From 198323aaf291965172ea5e4c8e76d56d1d716f3f Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Fri, 20 Jun 2014 16:56:33 +0100 Subject: [PATCH 01/59] Optimized blockchain storage --- ReleaseNotes.txt | 5 + .../epee/include/net/abstract_tcp_server2.inl | 2 +- .../net/levin_protocol_handler_async.h | 4 +- src/crypto/slow-hash.c | 5 +- src/cryptonote_config.h | 4 +- src/cryptonote_core/SwappedMap.cpp | 1 + src/cryptonote_core/SwappedMap.h | 350 ++++ src/cryptonote_core/SwappedVector.cpp | 1 + src/cryptonote_core/SwappedVector.h | 284 +++ src/cryptonote_core/blockchain_storage.cpp | 1710 +++++++++-------- src/cryptonote_core/blockchain_storage.h | 307 ++- .../blockchain_storage_boost_serialization.h | 3 +- src/cryptonote_core/checkpoints_create.h | 1 + src/cryptonote_core/cryptonote_basic_impl.cpp | 29 +- src/cryptonote_core/cryptonote_basic_impl.h | 1 + src/cryptonote_core/cryptonote_core.cpp | 55 +- src/cryptonote_core/cryptonote_core.h | 7 +- .../cryptonote_format_utils.cpp | 22 +- src/cryptonote_core/cryptonote_format_utils.h | 3 +- src/cryptonote_core/miner.cpp | 5 +- src/p2p/net_node.inl | 2 +- src/rpc/core_rpc_server.cpp | 38 +- src/serialization/binary_utils.h | 2 + src/serialization/crypto.h | 2 + src/serialization/json_utils.h | 2 + src/serialization/vector.h | 7 + src/version.h.in | 2 +- src/wallet/wallet_errors.h | 39 +- 28 files changed, 1776 insertions(+), 1117 deletions(-) create mode 100755 src/cryptonote_core/SwappedMap.cpp create mode 100755 src/cryptonote_core/SwappedMap.h create mode 100755 src/cryptonote_core/SwappedVector.cpp create mode 100755 src/cryptonote_core/SwappedVector.h diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 959851e585..90c74c3b73 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,8 @@ +Release notes 0.8.10 + +- Optimized blockchain storage memory usage +- Various code improvements + Release notes 0.8.9 - JSON RPC v2.0 compatibility diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 403f5a3bd3..74f1e5c4a4 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -304,7 +304,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) { send_guard.unlock(); - LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); +// LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); close(); return false; } diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index e7fb32fe06..a286c4723c 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -512,7 +512,7 @@ class async_protocol_handler CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock); if(!m_pservice_endpoint->do_send(&head, sizeof(head))) { - LOG_ERROR_CC(m_connection_context, "Failed to do_send"); +// LOG_ERROR_CC(m_connection_context, "Failed to do_send"); err_code = LEVIN_ERROR_CONNECTION; break; } @@ -635,7 +635,7 @@ class async_protocol_handler CRITICAL_REGION_BEGIN(m_send_lock); if(!m_pservice_endpoint->do_send(&head, sizeof(head))) { - LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); +// LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); return -1; } diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 351314bfec..468ba644be 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -150,7 +150,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash) uint8_t a[AES_BLOCK_SIZE]; uint8_t b[AES_BLOCK_SIZE]; uint8_t d[AES_BLOCK_SIZE]; - uint8_t aes_key[AES_KEY_SIZE]; RDATA_ALIGN16 uint8_t expandedKey[256]; union cn_slow_hash_state state; @@ -201,8 +200,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash) for(i = 0; i < ITER / 2; i++) { - #define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE) - #define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) + #define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE) + #define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) // Iteration 1 p = &long_state[state_index(a)]; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6b3db89aef..958639a0ec 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -19,6 +19,7 @@ // MONEY_SUPPLY - total number coins to be generated #define MONEY_SUPPLY ((uint64_t)(-1)) +#define EMISSION_SPEED_FACTOR (18) #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size @@ -29,9 +30,6 @@ #define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) -#define ORPHANED_BLOCKS_MAX_COUNT 100 - - #define DIFFICULTY_TARGET 120 // seconds #define DIFFICULTY_WINDOW 720 // blocks #define DIFFICULTY_LAG 15 // !!! diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/cryptonote_core/SwappedMap.cpp new file mode 100755 index 0000000000..44ed6ef26d --- /dev/null +++ b/src/cryptonote_core/SwappedMap.cpp @@ -0,0 +1 @@ +#include "SwappedMap.h" diff --git a/src/cryptonote_core/SwappedMap.h b/src/cryptonote_core/SwappedMap.h new file mode 100755 index 0000000000..f03bf5c949 --- /dev/null +++ b/src/cryptonote_core/SwappedMap.h @@ -0,0 +1,350 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template class SwappedMap { +private: + struct Descriptor { + uint64_t offset; + uint64_t index; + }; + +public: + typedef typename std::pair value_type; + + class const_iterator { + public: + //typedef ptrdiff_t difference_type; + //typedef std::bidirectional_iterator_tag iterator_category; + //typedef std::pair* pointer; + //typedef std::pair& reference; + //typedef std::pair value_type; + + const_iterator(SwappedMap* swappedMap, typename std::unordered_map::const_iterator descriptorsIterator) : m_swappedMap(swappedMap), m_descriptorsIterator(descriptorsIterator) { + } + + const_iterator& operator++() { + ++m_descriptorsIterator; + return *this; + } + + bool operator !=(const_iterator other) const { + return m_descriptorsIterator != other.m_descriptorsIterator; + } + + bool operator ==(const_iterator other) const { + return m_descriptorsIterator == other.m_descriptorsIterator; + } + + const std::pair& operator*() const { + return *m_swappedMap->load(m_descriptorsIterator->first, m_descriptorsIterator->second.offset); + } + + const std::pair* operator->() const { + return m_swappedMap->load(m_descriptorsIterator->first, m_descriptorsIterator->second.offset); + } + + typename std::unordered_map::const_iterator innerIterator() const { + return m_descriptorsIterator; + } + + private: + SwappedMap* m_swappedMap; + typename std::unordered_map::const_iterator m_descriptorsIterator; + }; + + typedef const_iterator iterator; + + SwappedMap(); + //SwappedMap(const SwappedMap&) = delete; + ~SwappedMap(); + //SwappedMap& operator=(const SwappedMap&) = delete; + + bool open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize); + void close(); + + uint64_t size() const; + const_iterator begin(); + const_iterator end(); + size_t count(const Key& key) const; + const_iterator find(const Key& key); + + void clear(); + void erase(const_iterator iterator); + std::pair insert(const std::pair& value); + +private: + std::fstream m_itemsFile; + std::fstream m_indexesFile; + size_t m_poolSize; + std::unordered_map m_descriptors; + uint64_t m_itemsFileSize; + std::unordered_map m_items; + std::list m_cache; + std::unordered_map::iterator> m_cacheIterators; + uint64_t m_cacheHits; + uint64_t m_cacheMisses; + + std::pair* prepare(const Key& key); + const std::pair* load(const Key& key, uint64_t offset); +}; + +template SwappedMap::SwappedMap() { +} + +template SwappedMap::~SwappedMap() { + close(); +} + +template bool SwappedMap::open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize) { + if (poolSize == 0) { + return false; + } + + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + if (m_itemsFile && m_indexesFile) { + uint64_t count; + m_indexesFile.read(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + std::unordered_map descriptors; + uint64_t itemsFileSize = 0; + for (uint64_t i = 0; i < count; ++i) { + bool valid; + m_indexesFile.read(reinterpret_cast(&valid), sizeof valid); + if (!m_indexesFile) { + return false; + } + + Key key; + m_indexesFile.read(reinterpret_cast(&key), sizeof key); + if (!m_indexesFile) { + return false; + } + + uint32_t itemSize; + m_indexesFile.read(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + return false; + } + + if (valid) { + Descriptor descriptor = { itemsFileSize, i }; + descriptors.insert(std::make_pair(key, descriptor)); + } + + itemsFileSize += itemSize; + } + + m_descriptors.swap(descriptors); + m_itemsFileSize = itemsFileSize; + } else { + m_itemsFile.open(itemFileName, std::ios::out | std::ios::binary); + m_itemsFile.close(); + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::out | std::ios::binary); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + m_indexesFile.close(); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + m_descriptors.clear(); + m_itemsFileSize = 0; + } + + m_poolSize = poolSize; + m_items.clear(); + m_cache.clear(); + m_cacheIterators.clear(); + m_cacheHits = 0; + m_cacheMisses = 0; + return true; +} + +template void SwappedMap::close() { + std::cout << "SwappedMap cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl; +} + +template uint64_t SwappedMap::size() const { + return m_descriptors.size(); +} + +template typename SwappedMap::const_iterator SwappedMap::begin() { + return const_iterator(this, m_descriptors.cbegin()); +} + +template typename SwappedMap::const_iterator SwappedMap::end() { + return const_iterator(this, m_descriptors.cend()); +} + +template size_t SwappedMap::count(const Key& key) const { + return m_descriptors.count(key); +} + +template typename SwappedMap::const_iterator SwappedMap::find(const Key& key) { + return const_iterator(this, m_descriptors.find(key)); +} + +template void SwappedMap::clear() { + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::clear"); + } + + m_indexesFile.seekp(0); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::clear"); + } + + m_descriptors.clear(); + m_itemsFileSize = 0; + m_items.clear(); + m_cache.clear(); + m_cacheIterators.clear(); +} + +template void SwappedMap::erase(const_iterator iterator) { + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::erase"); + } + + typename std::unordered_map::const_iterator descriptorsIterator = iterator.innerIterator(); + m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * descriptorsIterator->second.index); + bool valid = false; + m_indexesFile.write(reinterpret_cast(&valid), sizeof valid); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::erase"); + } + + m_descriptors.erase(descriptorsIterator); + auto cacheIteratorsIterator = m_cacheIterators.find(descriptorsIterator->first); + if (cacheIteratorsIterator != m_cacheIterators.end()) { + m_items.erase(descriptorsIterator->first); + m_cache.erase(cacheIteratorsIterator->second); + m_cacheIterators.erase(cacheIteratorsIterator); + } +} + +template std::pair::const_iterator, bool> SwappedMap::insert(const std::pair& value) { + uint64_t itemsFileSize; + + { + if (!m_itemsFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_itemsFile.seekp(m_itemsFileSize); + try { + boost::archive::binary_oarchive archive(m_itemsFile); + archive & value.second; + } catch (std::exception&) { + throw std::runtime_error("SwappedMap::insert"); + } + + itemsFileSize = m_itemsFile.tellp(); + } + + { + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * m_descriptors.size()); + bool valid = true; + m_indexesFile.write(reinterpret_cast(&valid), sizeof valid); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_indexesFile.write(reinterpret_cast(&value.first), sizeof value.first); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + uint32_t itemSize = static_cast(itemsFileSize - m_itemsFileSize); + m_indexesFile.write(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + + m_indexesFile.seekp(0); + uint64_t count = m_descriptors.size() + 1; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedMap::insert"); + } + } + + Descriptor descriptor = { m_itemsFileSize, m_descriptors.size() }; + auto descriptorsInsert = m_descriptors.insert(std::make_pair(value.first, descriptor)); + m_itemsFileSize = itemsFileSize; + + T* newItem = &prepare(value.first)->second; + *newItem = value.second; + return std::make_pair(const_iterator(this, descriptorsInsert.first), true); +} + +template std::pair* SwappedMap::prepare(const Key& key) { + if (m_items.size() == m_poolSize) { + typename std::list::iterator cacheIter = m_cache.begin(); + m_items.erase(*cacheIter); + m_cacheIterators.erase(*cacheIter); + m_cache.erase(cacheIter); + } + + std::pair::iterator, bool> itemInsert = m_items.insert(std::make_pair(key, T())); + typename std::list::iterator cacheIter = m_cache.insert(m_cache.end(), key); + m_cacheIterators.insert(std::make_pair(key, cacheIter)); + return &*itemInsert.first; +} + +template const std::pair* SwappedMap::load(const Key& key, uint64_t offset) { + auto itemIterator = m_items.find(key); + if (itemIterator != m_items.end()) { + auto cacheIteratorsIterator = m_cacheIterators.find(key); + if (cacheIteratorsIterator->second != --m_cache.end()) { + m_cache.splice(m_cache.end(), m_cache, cacheIteratorsIterator->second); + } + + ++m_cacheHits; + return &*itemIterator; + } + + typename std::unordered_map::iterator descriptorsIterator = m_descriptors.find(key); + if (descriptorsIterator == m_descriptors.end()) { + throw std::runtime_error("SwappedMap::load"); + } + + if (!m_itemsFile) { + throw std::runtime_error("SwappedMap::load"); + } + + m_itemsFile.seekg(descriptorsIterator->second.offset); + T tempItem; + try { + boost::archive::binary_iarchive archive(m_itemsFile); + archive & tempItem; + } catch (std::exception&) { + throw std::runtime_error("SwappedMap::load"); + } + + std::pair* item = prepare(key); + std::swap(tempItem, item->second); + ++m_cacheMisses; + return item; +} diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/cryptonote_core/SwappedVector.cpp new file mode 100755 index 0000000000..df4e51568e --- /dev/null +++ b/src/cryptonote_core/SwappedVector.cpp @@ -0,0 +1 @@ +#include "SwappedVector.h" diff --git a/src/cryptonote_core/SwappedVector.h b/src/cryptonote_core/SwappedVector.h new file mode 100755 index 0000000000..7285c7fe82 --- /dev/null +++ b/src/cryptonote_core/SwappedVector.h @@ -0,0 +1,284 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include "serialization/binary_archive.h" + +template class SwappedVector { +public: + SwappedVector(); + //SwappedVector(const SwappedVector&) = delete; + ~SwappedVector(); + //SwappedVector& operator=(const SwappedVector&) = delete; + + bool open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize); + void close(); + + bool empty() const; + uint64_t size() const; + const T& operator[](uint64_t index); + const T& front(); + const T& back(); + void clear(); + void pop_back(); + void push_back(const T& item); + +private: + struct ItemEntry; + struct CacheEntry; + + struct ItemEntry { + public: + T item; + typename std::list::iterator cacheIter; + }; + + struct CacheEntry { + public: + typename std::map::iterator itemIter; + }; + + std::fstream m_itemsFile; + std::fstream m_indexesFile; + size_t m_poolSize; + std::vector m_offsets; + uint64_t m_itemsFileSize; + std::map m_items; + std::list m_cache; + uint64_t m_cacheHits; + uint64_t m_cacheMisses; + + T* prepare(uint64_t index); +}; + +template SwappedVector::SwappedVector() { +} + +template SwappedVector::~SwappedVector() { + close(); +} + +template bool SwappedVector::open(const std::string& itemFileName, const std::string& indexFileName, size_t poolSize) { + if (poolSize == 0) { + return false; + } + + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + if (m_itemsFile && m_indexesFile) { + uint64_t count; + m_indexesFile.read(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + std::vector offsets; + uint64_t itemsFileSize = 0; + for (uint64_t i = 0; i < count; ++i) { + uint32_t itemSize; + m_indexesFile.read(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + return false; + } + + offsets.emplace_back(itemsFileSize); + itemsFileSize += itemSize; + } + + m_offsets.swap(offsets); + m_itemsFileSize = itemsFileSize; + } else { + m_itemsFile.open(itemFileName, std::ios::out | std::ios::binary); + m_itemsFile.close(); + m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); + m_indexesFile.open(indexFileName, std::ios::out | std::ios::binary); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + return false; + } + + m_indexesFile.close(); + m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); + m_offsets.clear(); + m_itemsFileSize = 0; + } + + m_poolSize = poolSize; + m_items.clear(); + m_cache.clear(); + m_cacheHits = 0; + m_cacheMisses = 0; + return true; +} + +template void SwappedVector::close() { + std::cout << "SwappedVector cache hits: " << m_cacheHits << ", misses: " << m_cacheMisses << " (" << std::fixed << std::setprecision(2) << static_cast(m_cacheMisses) / (m_cacheHits + m_cacheMisses) * 100 << "%)" << std::endl; +} + +template bool SwappedVector::empty() const { + return m_offsets.empty(); +} + +template uint64_t SwappedVector::size() const { + return m_offsets.size(); +} + +template const T& SwappedVector::operator[](uint64_t index) { + auto itemIter = m_items.find(index); + if (itemIter != m_items.end()) { + if (itemIter->second.cacheIter != --m_cache.end()) { + m_cache.splice(m_cache.end(), m_cache, itemIter->second.cacheIter); + } + + ++m_cacheHits; + return itemIter->second.item; + } + + if (index >= m_offsets.size()) { + throw std::runtime_error("SwappedVector::operator[]"); + } + + if (!m_itemsFile) { + throw std::runtime_error("SwappedVector::operator[]"); + } + + m_itemsFile.seekg(m_offsets[index]); + T tempItem; + //try { + //boost::archive::binary_iarchive archive(m_itemsFile); + //archive & tempItem; + //} catch (std::exception&) { + // throw std::runtime_error("SwappedVector::operator[]"); + //} + + binary_archive archive(m_itemsFile); + if (!do_serialize(archive, tempItem)) { + throw std::runtime_error("SwappedVector::operator[]"); + } + + T* item = prepare(index); + std::swap(tempItem, *item); + ++m_cacheMisses; + return *item; +} + +template const T& SwappedVector::front() { + return operator[](0); +} + +template const T& SwappedVector::back() { + return operator[](m_offsets.size() - 1); +} + +template void SwappedVector::clear() { + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::clear"); + } + + m_indexesFile.seekp(0); + uint64_t count = 0; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::clear"); + } + + m_offsets.clear(); + m_itemsFileSize = 0; + m_items.clear(); + m_cache.clear(); +} + +template void SwappedVector::pop_back() { + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::pop_back"); + } + + m_indexesFile.seekp(0); + uint64_t count = m_offsets.size() - 1; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::pop_back"); + } + + m_itemsFileSize = m_offsets.back(); + m_offsets.pop_back(); + auto itemIter = m_items.find(m_offsets.size()); + if (itemIter != m_items.end()) { + m_cache.erase(itemIter->second.cacheIter); + m_items.erase(itemIter); + } +} + +template void SwappedVector::push_back(const T& item) { + uint64_t itemsFileSize; + + { + if (!m_itemsFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + + m_itemsFile.seekp(m_itemsFileSize); + //try { + // boost::archive::binary_oarchive archive(m_itemsFile); + // archive & item; + //} catch (std::exception&) { + // throw std::runtime_error("SwappedVector::push_back"); + //} + + binary_archive archive(m_itemsFile); + if (!do_serialize(archive, *const_cast(&item))) { + throw std::runtime_error("SwappedVector::push_back"); + } + + itemsFileSize = m_itemsFile.tellp(); + } + + { + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + + m_indexesFile.seekp(sizeof(uint64_t) + sizeof(uint32_t) * m_offsets.size()); + uint32_t itemSize = static_cast(itemsFileSize - m_itemsFileSize); + m_indexesFile.write(reinterpret_cast(&itemSize), sizeof itemSize); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + + m_indexesFile.seekp(0); + uint64_t count = m_offsets.size() + 1; + m_indexesFile.write(reinterpret_cast(&count), sizeof count); + if (!m_indexesFile) { + throw std::runtime_error("SwappedVector::push_back"); + } + } + + m_offsets.push_back(m_itemsFileSize); + m_itemsFileSize = itemsFileSize; + + T* newItem = prepare(m_offsets.size() - 1); + *newItem = item; +} + +template T* SwappedVector::prepare(uint64_t index) { + if (m_items.size() == m_poolSize) { + auto cacheIter = m_cache.begin(); + m_items.erase(cacheIter->itemIter); + m_cache.erase(cacheIter); + } + + auto itemIter = m_items.insert(std::make_pair(index, ItemEntry())); + CacheEntry cacheEntry = { itemIter.first }; + auto cacheIter = m_cache.insert(m_cache.end(), cacheEntry); + itemIter.first->second.cacheIter = cacheIter; + return &itemIter.first->second.item; +} diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 1787ec0a91..f569e5062c 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -2,79 +2,269 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include "blockchain_storage.h" + #include #include #include #include -#include "include_base_utils.h" -#include "cryptonote_basic_impl.h" -#include "blockchain_storage.h" #include "cryptonote_format_utils.h" #include "cryptonote_boost_serialization.h" -#include "blockchain_storage_boost_serialization.h" -#include "cryptonote_config.h" -#include "miner.h" -#include "misc_language.h" + #include "profile_tools.h" #include "file_io_utils.h" #include "common/boost_serialization_helper.h" -#include "warnings.h" -#include "crypto/hash.h" -//#include "serialization/json_archive.h" -using namespace std; -using namespace epee; +//namespace { +// std::string hashHex(const crypto::hash& hash) { +// std::string result; +// for (size_t i = 0; i < crypto::HASH_SIZE; ++i) { +// result += "0123456789ABCDEF"[static_cast(hash.data[i]) >> 4]; +// result += "0123456789ABCDEF"[static_cast(hash.data[i]) & 15]; +// } +// +// return result; +// } +//} + +namespace { + std::string appendPath(const std::string& path, const std::string& fileName) { + std::string result = path; + if (!result.empty()) { + result += '/'; + } + + result += fileName; + return result; + } +} + +namespace std { + bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) { + return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0; + } + + bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) { + return memcmp(&keyImage1, &keyImage2, 32) < 0; + } +} + using namespace cryptonote; DISABLE_VS_WARNINGS(4267) -//------------------------------------------------------------------ -bool blockchain_storage::have_tx(const crypto::hash &id) -{ +namespace cryptonote { + struct transaction_chain_entry { + transaction tx; + uint64_t m_keeper_block_height; + size_t m_blob_size; + std::vector m_global_output_indexes; + + template void serialize(archive_t & ar, unsigned int version); + }; + + struct block_extended_info { + block bl; + uint64_t height; + size_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + + template void serialize(archive_t & ar, unsigned int version); + }; + + template void transaction_chain_entry::serialize(archive_t & ar, unsigned int version) { + ar & tx; + ar & m_keeper_block_height; + ar & m_blob_size; + ar & m_global_output_indexes; + } + + template void block_extended_info::serialize(archive_t & ar, unsigned int version) { + ar & bl; + ar & height; + ar & cumulative_difficulty; + ar & block_cumulative_size; + ar & already_generated_coins; + } +} + +template void cryptonote::blockchain_storage::Transaction::serialize(Archive& archive, unsigned int version) { + archive & tx; +} + +template void cryptonote::blockchain_storage::Block::serialize(Archive& archive, unsigned int version) { + archive & bl; + archive & height; + archive & block_cumulative_size; + archive & cumulative_difficulty; + archive & already_generated_coins; + archive & transactions; +} + +template void cryptonote::blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { + archive & block; + archive & transaction; +} + +namespace cryptonote { +#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 13 + + template void blockchain_storage::serialize(archive_t & ar, const unsigned int version) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (version < 12) { + LOG_PRINT_L0("Detected blockchain of unsupported version, migration is not possible."); + return; + } + + LOG_PRINT_L0("Blockchain of previous version detected, migrating. This may take several minutes, please be patient..."); + + std::vector blocks; + ar & blocks; + + { + std::unordered_map blocks_index; + ar & blocks_index; + } + + std::unordered_map transactions; + ar & transactions; + + { + std::unordered_set spent_keys; + ar & spent_keys; + } + + { + std::unordered_map alternative_chains; + ar & alternative_chains; + } + + { + std::map>> outputs; + ar & outputs; + } + + { + std::unordered_map invalid_blocks; + ar & invalid_blocks; + } + + size_t current_block_cumul_sz_limit; + ar & current_block_cumul_sz_limit; + LOG_PRINT_L0("Old blockchain storage:" << ENDL << + "blocks: " << blocks.size() << ENDL << + "transactions: " << transactions.size() << ENDL << + "current_block_cumul_sz_limit: " << current_block_cumul_sz_limit); + + Block block; + Transaction transaction; + for (uint32_t b = 0; b < blocks.size(); ++b) { + block.bl = blocks[b].bl; + block.height = b; + block.block_cumulative_size = blocks[b].block_cumulative_size; + block.cumulative_difficulty = blocks[b].cumulative_difficulty; + block.already_generated_coins = blocks[b].already_generated_coins; + block.transactions.resize(1 + blocks[b].bl.tx_hashes.size()); + block.transactions[0].tx = blocks[b].bl.miner_tx; + TransactionIndex transactionIndex = { b, 0 }; + pushTransaction(block, get_transaction_hash(blocks[b].bl.miner_tx), transactionIndex); + for (uint32_t t = 0; t < blocks[b].bl.tx_hashes.size(); ++t) { + block.transactions[1 + t].tx = transactions[blocks[b].bl.tx_hashes[t]].tx; + transactionIndex.transaction = 1 + t; + pushTransaction(block, blocks[b].bl.tx_hashes[t], transactionIndex); + } + + pushBlock(block); + } + + update_next_comulative_size_limit(); + if (m_current_block_cumul_sz_limit != current_block_cumul_sz_limit) { + LOG_ERROR("Migration was unsuccessful."); + } + } +} + +BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) + + +bool blockchain_storage::have_tx(const crypto::hash &id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_transactions.find(id) != m_transactions.end(); + return m_transactionMap.find(id) != m_transactionMap.end(); } -//------------------------------------------------------------------ -bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) -{ + +bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_spent_keys.find(key_im) != m_spent_keys.end(); } -//------------------------------------------------------------------ -transaction *blockchain_storage::get_tx(const crypto::hash &id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_transactions.find(id); - if (it == m_transactions.end()) - return NULL; - return &it->second.tx; -} -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_current_blockchain_height() -{ +uint64_t blockchain_storage::get_current_blockchain_height() { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_blocks.size(); } -//------------------------------------------------------------------ -bool blockchain_storage::init(const std::string& config_folder) -{ + +bool blockchain_storage::init(const std::string& config_folder) { CRITICAL_REGION_LOCAL(m_blockchain_lock); m_config_folder = config_folder; LOG_PRINT_L0("Loading blockchain..."); - const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - if(!tools::unserialize_obj_from_file(*this, filename)) - { - LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block."); - block bl = boost::value_initialized(); - block_verification_context bvc = boost::value_initialized(); - generate_genesis_block(bl); - add_new_block(bl, bvc); - CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain"); + if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) { + return false; } - if(!m_blocks.size()) - { + + if (m_blocks.empty()) { + const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME); + if (!tools::unserialize_obj_from_file(*this, filename)) { + LOG_PRINT_L0("Can't load blockchain storage from file."); + } + } else { + bool rebuild = true; + try { + std::ifstream file(appendPath(config_folder, "blockscache.dat"), std::ios::binary); + boost::archive::binary_iarchive archive(file); + crypto::hash lastBlockHash; + archive & lastBlockHash; + if (lastBlockHash == get_block_hash(m_blocks.back().bl)) { + archive & m_blockMap; + archive & m_transactionMap; + archive & m_spent_keys; + archive & m_outputs; + rebuild = false; + } + } catch (std::exception&) { + } + + if (rebuild) { + LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures..."); + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + const Block& block = m_blocks[b]; + crypto::hash blockHash = get_block_hash(block.bl); + m_blockMap.insert(std::make_pair(blockHash, b)); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const Transaction& transaction = block.transactions[t]; + crypto::hash transactionHash = get_transaction_hash(transaction.tx); + TransactionIndex transactionIndex = { b, t }; + m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + for (auto& i : transaction.tx.vin) { + if (i.type() == typeid(txin_to_key)) { + m_spent_keys.insert(::boost::get(i).k_image); + } + } + + for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { + m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o)); + } + } + } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count()); + } + } + + if (m_blocks.empty()) { LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); block bl = boost::value_initialized(); block_verification_context bvc = boost::value_initialized(); @@ -82,78 +272,107 @@ bool blockchain_storage::init(const std::string& config_folder) add_new_block(bl, bvc); CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); } + uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; - if(!m_blocks.back().bl.timestamp) + if (!m_blocks.back().bl.timestamp) timestamp_diff = time(NULL) - 1341378000; - LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size()-1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); + LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::store_blockchain() -{ - m_is_blockchain_storing = true; - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){m_is_blockchain_storing=false;}); - LOG_PRINT_L0("Storing blockchain..."); - if (!tools::create_directories_if_necessary(m_config_folder)) - { - LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); - return false; +bool blockchain_storage::store_blockchain() { + try { + std::ofstream file(appendPath(m_config_folder, "blockscache.dat"), std::ios::binary); + boost::archive::binary_oarchive archive(file); + crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); + archive & lastBlockHash; + archive & m_blockMap; + archive & m_transactionMap; + archive & m_spent_keys; + archive & m_outputs; + } catch (std::exception& e) { + LOG_ERROR("Failed to save blockchain, " << e.what()); + } + + //{ + // std::ofstream file(appendPath(m_config_folder, "blockscache2.dat"), std::ios::binary); + + // crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); + // file.write(reinterpret_cast(&lastBlockHash), sizeof(lastBlockHash)); + + // uint32_t blockMapSize = m_blockMap.size(); + // file.write(reinterpret_cast(&blockMapSize), sizeof(blockMapSize)); + // for (auto& i : m_blockMap) { + // crypto::hash blockHash = i.first; + // file.write(reinterpret_cast(&blockHash), sizeof(blockHash)); + + // uint32_t blockIndex = i.second; + // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); + // } + + // uint32_t transactionMapSize = m_transactionMap.size(); + // file.write(reinterpret_cast(&transactionMapSize), sizeof(transactionMapSize)); + // for (auto& i : m_transactionMap) { + // crypto::hash transactionHash = i.first; + // file.write(reinterpret_cast(&transactionHash), sizeof(transactionHash)); + + // uint32_t blockIndex = i.second.block; + // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); + + // uint32_t transactionIndex = i.second.transaction; + // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); + // } + + // uint32_t spentKeysSize = m_spent_keys.size(); + // file.write(reinterpret_cast(&spentKeysSize), sizeof(spentKeysSize)); + // for (auto& i : m_spent_keys) { + // crypto::key_image key = i; + // file.write(reinterpret_cast(&key), sizeof(key)); + // } + + // uint32_t outputsSize = m_outputs.size(); + // file.write(reinterpret_cast(&outputsSize), sizeof(outputsSize)); + // for (auto& i : m_outputs) { + // uint32_t indexesSize = i.second.size(); + // file.write(reinterpret_cast(&indexesSize), sizeof(indexesSize)); + // for (auto& j : i.second) { + // uint32_t blockIndex = j.first.block; + // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); + + // uint32_t transactionIndex = j.first.transaction; + // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); + + // uint32_t outputIndex = j.second; + // file.write(reinterpret_cast(&outputIndex), sizeof(outputIndex)); + // } + // } + //} + + { + //std::ofstream file(appendPath(m_config_folder, "blockscache3.dat"), std::ios::binary); + //binary_archive archive(file); + //crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); + //do_serialize(archive, lastBlockHash); + //do_serialize(archive, m_blockMap); + //do_serialize(archive, m_transactionMap); + //do_serialize(archive, m_spent_keys); + //do_serialize(archive, m_outputs); } - const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; - // There is a chance that temp_filename and filename are hardlinks to the same file - std::remove(temp_filename.c_str()); - if(!tools::serialize_obj_to_file(*this, temp_filename)) - { - //achtung! - LOG_ERROR("Failed to save blockchain data to file: " << temp_filename); - return false; - } - const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; - std::error_code ec = tools::replace_file(temp_filename, filename); - if (ec) - { - LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); - return false; - } - LOG_PRINT_L0("Blockchain stored OK."); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::deinit() -{ + +bool blockchain_storage::deinit() { return store_blockchain(); } -//------------------------------------------------------------------ -bool blockchain_storage::pop_block_from_blockchain() -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size()); - size_t h = m_blocks.size()-1; - block_extended_info& bei = m_blocks[h]; - //crypto::hash id = get_block_hash(bei.bl); - bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size()); - CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h); - - //remove from index - auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl)); - CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index"); - m_blocks_index.erase(bl_ind); - //pop block from core - m_blocks.pop_back(); - m_tx_pool.on_blockchain_dec(m_blocks.size()-1, get_tail_id()); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::reset_and_set_genesis_block(const block& b) -{ +bool blockchain_storage::reset_and_set_genesis_block(const block& b) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - m_transactions.clear(); - m_spent_keys.clear(); m_blocks.clear(); - m_blocks_index.clear(); + m_blockMap.clear(); + m_transactionMap.clear(); + + m_spent_keys.clear(); m_alternative_chains.clear(); m_outputs.clear(); @@ -161,219 +380,116 @@ bool blockchain_storage::reset_and_set_genesis_block(const block& b) add_new_block(b, bvc); return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; } -//------------------------------------------------------------------ -bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - struct purge_transaction_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - bool m_strict_check; - purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){} - - bool operator()(const txin_to_key& inp) const - { - //const crypto::key_image& ki = inp.k_image; - auto r = m_spent_keys.find(inp.k_image); - if(r != m_spent_keys.end()) - { - m_spent_keys.erase(r); - }else - { - CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found"); - } - return true; - } - bool operator()(const txin_gen& inp) const - { - return true; - } - bool operator()(const txin_to_script& tx) const - { - return false; - } - - bool operator()(const txin_to_scripthash& tx) const - { - return false; - } - }; - - BOOST_FOREACH(const txin_v& in, tx.vin) - { - bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in); - CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor"); - } - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto tx_index_it = m_transactions.find(tx_id); - CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!"); - transaction& tx = tx_index_it->second.tx; - - purge_transaction_keyimages_from_blockchain(tx, true); - - if(!is_coinbase(tx)) - { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool r = m_tx_pool.add_tx(tx, tvc, true); - CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool"); - } - - bool res = pop_transaction_from_global_index(tx, tx_id); - m_transactions.erase(tx_index_it); - LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL); - return res; -} -//------------------------------------------------------------------ -bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - bool res = true; - CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain"); - for(size_t count = 0; count != processed_tx_count; count++) - { - res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res; - } - res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res; - - return res; -} -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_tail_id(uint64_t& height) -{ +crypto::hash blockchain_storage::get_tail_id(uint64_t& height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - height = get_current_blockchain_height()-1; + height = get_current_blockchain_height() - 1; return get_tail_id(); } -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_tail_id() -{ + +crypto::hash blockchain_storage::get_tail_id() { CRITICAL_REGION_LOCAL(m_blockchain_lock); crypto::hash id = null_hash; - if(m_blocks.size()) - { + if (m_blocks.size()) { get_block_hash(m_blocks.back().bl, id); } + return id; } -//------------------------------------------------------------------ -bool blockchain_storage::get_short_chain_history(std::list& ids) -{ + +bool blockchain_storage::get_short_chain_history(std::list& ids) { CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t i = 0; size_t current_multiplier = 1; size_t sz = m_blocks.size(); - if(!sz) + if (!sz) return true; size_t current_back_offset = 1; bool genesis_included = false; - while(current_back_offset < sz) + while (current_back_offset < sz) { - ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl)); - if(sz-current_back_offset == 0) + ids.push_back(get_block_hash(m_blocks[sz - current_back_offset].bl)); + if (sz - current_back_offset == 0) genesis_included = true; - if(i < 10) + if (i < 10) { ++current_back_offset; - }else + } else { current_back_offset += current_multiplier *= 2; } ++i; } - if(!genesis_included) + if (!genesis_included) ids.push_back(get_block_hash(m_blocks[0].bl)); return true; } -//------------------------------------------------------------------ -crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) -{ + +crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(height >= m_blocks.size()) + if (height >= m_blocks.size()) return null_hash; return get_block_hash(m_blocks[height].bl); } -//------------------------------------------------------------------ -bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - // try to find block in main chain - blocks_by_id_index::const_iterator it = m_blocks_index.find(h); - if (m_blocks_index.end() != it) { - blk = m_blocks[it->second].bl; +bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, block& b) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto blockIndexByHashIterator = m_blockMap.find(blockHash); + if (blockIndexByHashIterator != m_blockMap.end()) { + b = m_blocks[blockIndexByHashIterator->second].bl; return true; } - // try to find block in alternative chain - blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h); - if (m_alternative_chains.end() != it_alt) { - blk = it_alt->second.bl; + auto blockByHashIterator = m_alternative_chains.find(blockHash); + if (blockByHashIterator != m_alternative_chains.end()) { + b = blockByHashIterator->second.bl; return true; } return false; } -//------------------------------------------------------------------ -void blockchain_storage::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - - BOOST_FOREACH(blocks_by_id_index::value_type &v, m_blocks_index) - main.push_back(v.first); - - BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_alternative_chains) - alt.push_back(v.first); - BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_invalid_blocks) - invalid.push_back(v.first); -} -//------------------------------------------------------------------ -difficulty_type blockchain_storage::get_difficulty_for_next_block() -{ +difficulty_type blockchain_storage::get_difficulty_for_next_block() { CRITICAL_REGION_LOCAL(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; - size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); - if(!offset) - ++offset;//skip genesis block - for(; offset < m_blocks.size(); offset++) - { + size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); + if (offset == 0) { + ++offset; + } + + for (; offset < m_blocks.size(); offset++) { timestamps.push_back(m_blocks[offset].bl.timestamp); commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); } + return next_difficulty(timestamps, commulative_difficulties); } -//------------------------------------------------------------------ -bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) -{ + +bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); //remove failed subchain - for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) + for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) { - bool r = pop_block_from_blockchain(); - CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); + popBlock(get_block_hash(m_blocks.back().bl)); + //bool r = pop_block_from_blockchain(); + //CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); } //return back original chain BOOST_FOREACH(auto& bl, original_chain) { block_verification_context bvc = boost::value_initialized(); - bool r = handle_block_to_main_chain(bl, bvc); + bool r = pushBlock(bl, bvc); CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC!!! failed to add (again) block while chain switching during the rollback!"); } LOG_PRINT_L0("Rollback success."); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) -{ + +bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); @@ -382,47 +498,41 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list disconnected_chain; - for(size_t i = m_blocks.size()-1; i >=split_height; i--) - { + for (size_t i = m_blocks.size() - 1; i >= split_height; i--) { block b = m_blocks[i].bl; - bool r = pop_block_from_blockchain(); - CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); + popBlock(get_block_hash(b)); + //CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); disconnected_chain.push_front(b); } //connecting new alternative chain - for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) - { + for (auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) { auto ch_ent = *alt_ch_iter; block_verification_context bvc = boost::value_initialized(); - bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc); - if(!r || !bvc.m_added_to_main_chain) - { + bool r = pushBlock(ch_ent->second.bl, bvc); + if (!r || !bvc.m_added_to_main_chain) { LOG_PRINT_L0("Failed to switch to alternative blockchain"); rollback_blockchain_switching(disconnected_chain, split_height); - add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); + //add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); LOG_PRINT_L0("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); m_alternative_chains.erase(ch_ent); - for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) - { + for (auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) { //block_verification_context bvc = boost::value_initialized(); - add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); + //add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); m_alternative_chains.erase(*alt_ch_to_orph_iter); } + return false; } } - if(!discard_disconnected_chain) - { + if (!discard_disconnected_chain) { //pushing old chain as alternative chain - BOOST_FOREACH(auto& old_ch_ent, disconnected_chain) - { + for (auto& old_ch_ent : disconnected_chain) { block_verification_context bvc = boost::value_initialized(); bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); - if(!r) - { + if (!r) { LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); rollback_blockchain_switching(disconnected_chain, split_height); return false; @@ -431,138 +541,127 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, block_extended_info& bei) -{ + +difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, Block& bei) { std::vector timestamps; std::vector commulative_difficulties; - if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT) - { + if (alt_chain.size() < DIFFICULTY_BLOCKS_COUNT) { CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); main_chain_count = std::min(main_chain_count, main_chain_stop_offset); size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; - if(!main_chain_start_offset) + if (!main_chain_start_offset) ++main_chain_start_offset; //skip genesis block - for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) - { + for (; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) { timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp); commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); } - CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size() - << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT ); - BOOST_FOREACH(auto it, alt_chain) - { + CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() + << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT); + for (auto it : alt_chain) { timestamps.push_back(it->second.bl.timestamp); commulative_difficulties.push_back(it->second.cumulative_difficulty); } - }else - { + } else { timestamps.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); commulative_difficulties.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); size_t count = 0; - size_t max_i = timestamps.size()-1; - BOOST_REVERSE_FOREACH(auto it, alt_chain) - { + size_t max_i = timestamps.size() - 1; + BOOST_REVERSE_FOREACH(auto it, alt_chain) { timestamps[max_i - count] = it->second.bl.timestamp; commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; count++; - if(count >= DIFFICULTY_BLOCKS_COUNT) + if (count >= DIFFICULTY_BLOCKS_COUNT) { break; + } } } + return next_difficulty(timestamps, commulative_difficulties); } -//------------------------------------------------------------------ -bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) -{ + +bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) { CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); - if(boost::get(b.miner_tx.vin[0]).height != height) - { + if (boost::get(b.miner_tx.vin[0]).height != height) { LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << boost::get(b.miner_tx.vin[0]).height << ", expected: " << height); return false; } + CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, - false, - "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + false, + "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); - //check outs overflow - if(!check_outs_overflow(b.miner_tx)) - { + if (!check_outs_overflow(b.miner_tx)) { LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); return false; } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) -{ - //validate reward + +bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) { uint64_t money_in_use = 0; - BOOST_FOREACH(auto& o, b.miner_tx.vout) + for (auto& o : b.miner_tx.vout) { money_in_use += o.amount; + } std::vector last_blocks_sizes; get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if(!get_block_reward(misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward)) - { + if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward)) { LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); return false; } - if(base_reward + fee < money_in_use) - { + + if (base_reward + fee < money_in_use) { LOG_ERROR("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); return false; } - if(base_reward + fee != money_in_use) - { + + if (base_reward + fee != money_in_use) { LOG_ERROR("coinbase transaction doesn't use full amount of block reward: spent: " - << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); return false; } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) -{ + +bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size()); - - size_t start_offset = (from_height+1) - std::min((from_height+1), count); - for(size_t i = start_offset; i != from_height+1; i++) + size_t start_offset = (from_height + 1) - std::min((from_height + 1), count); + for (size_t i = start_offset; i != from_height + 1; i++) { sz.push_back(m_blocks[i].block_cumulative_size); + } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) -{ + +bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!m_blocks.size()) + if (!m_blocks.size()) { return true; - return get_backward_blocks_sizes(m_blocks.size() -1, sz, count); + } + + return get_backward_blocks_sizes(m_blocks.size() - 1, sz, count); } -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_current_comulative_blocksize_limit() -{ + +uint64_t blockchain_storage::get_current_comulative_blocksize_limit() { return m_current_block_cumul_sz_limit; } -//------------------------------------------------------------------ -bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) -{ + +bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) { size_t median_size; uint64_t already_generated_coins; @@ -585,6 +684,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) { return false; } + #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) size_t real_txs_size = 0; uint64_t real_fee = 0; @@ -625,7 +725,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad /* 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 - */ + */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); @@ -662,7 +762,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) { //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size - LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2); + LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1, LOG_LEVEL_2); cumulative_size += delta - 1; continue; } @@ -676,43 +776,40 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad #endif return true; } + LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); return false; } -//------------------------------------------------------------------ -bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) -{ - if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) +bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { + if (timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) return true; CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); - size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0; + size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; do { timestamps.push_back(m_blocks[start_top_height].bl.timestamp); - if(start_top_height == 0) + if (start_top_height == 0) break; --start_top_height; - }while(start_top_height != stop_offset); + } while (start_top_height != stop_offset); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) -{ + +bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) { CRITICAL_REGION_LOCAL(m_blockchain_lock); uint64_t block_height = get_block_height(b); - if(0 == block_height) - { - LOG_ERROR("Block with id: " << string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction"); + if (block_height == 0) { + LOG_ERROR("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction"); bvc.m_verifivation_failed = true; return false; } - if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) - { + + if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " can't be accepted for alternative chain, block height: " << block_height << ENDL << " blockchain height: " << get_current_blockchain_height()); @@ -722,39 +819,35 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: //block is not related with head of main chain //first of all - look in alternative chains container - auto it_main_prev = m_blocks_index.find(b.prev_id); + auto it_main_prev = m_blockMap.find(b.prev_id); auto it_prev = m_alternative_chains.find(b.prev_id); - if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end()) - { + if (it_prev != m_alternative_chains.end() || it_main_prev != m_blockMap.end()) { //we have new block in alternative chain //build alternative subchain, front -> mainchain, back -> alternative head blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find() std::list alt_chain; std::vector timestamps; - while(alt_it != m_alternative_chains.end()) - { + while (alt_it != m_alternative_chains.end()) { alt_chain.push_front(alt_it); timestamps.push_back(alt_it->second.bl.timestamp); alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); } - if(alt_chain.size()) - { + if (alt_chain.size()) { //make sure that it has right connection to main chain CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); crypto::hash h = null_hash; get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain have wrong connection to main chain"); complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); - }else - { - CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); + } else { + CHECK_AND_ASSERT_MES(it_main_prev != m_blockMap.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); complete_timestamps_vector(it_main_prev->second, timestamps); } + //check timestamp correct - if(!check_block_timestamp(timestamps, b)) - { + if (!check_block_timestamp(timestamps, b)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp); //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed @@ -762,13 +855,12 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } - block_extended_info bei = boost::value_initialized(); + Block bei = boost::value_initialized(); bei.bl = b; bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1; bool is_a_checkpoint; - if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) - { + if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) { LOG_ERROR("CHECKPOINT VALIDATION FAILED"); bvc.m_verifivation_failed = true; return false; @@ -780,8 +872,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; get_block_longhash(bei.bl, proof_of_work, bei.height); - if(!check_hash(proof_of_work, current_diff)) - { + if (!check_hash(proof_of_work, current_diff)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work << ENDL << " expected difficulty: " << current_diff); @@ -789,54 +880,49 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } - if(!prevalidate_miner_transaction(b, bei.height)) - { - LOG_PRINT_RED_L0("Block with id: " << string_tools::pod_to_hex(id) - << " (as alternative) have wrong miner transaction."); + if (!prevalidate_miner_transaction(b, bei.height)) { + LOG_PRINT_RED_L0("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction."); bvc.m_verifivation_failed = true; return false; - } - bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty; + bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty : m_blocks[it_main_prev->second].cumulative_difficulty; bei.cumulative_difficulty += current_diff; #ifdef _DEBUG auto i_dres = m_alternative_chains.find(id); CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist"); #endif + auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); alt_chain.push_back(i_res.first); - if(is_a_checkpoint) - { + if (is_a_checkpoint) { //do reorganize! LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0); bool r = switch_to_alternative_blockchain(alt_chain, true); - if(r) bvc.m_added_to_main_chain = true; + if (r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; return r; - }else if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain + } else if (m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain { //do reorganize! LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0); bool r = switch_to_alternative_blockchain(alt_chain, false); - if(r) bvc.m_added_to_main_chain = true; + if (r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; return r; - }else - { + } else { LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height << ENDL << "id:\t" << id << ENDL << "PoW:\t" << proof_of_work << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); return true; } - }else - { + } else { //block orphaned bvc.m_marked_as_orphaned = true; LOG_PRINT_RED_L0("Block recognized as orphaned and rejected, id = " << id); @@ -844,13 +930,12 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) -{ + +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_blocks.size()) + if (start_offset >= m_blocks.size()) return false; - for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); std::list missed_ids; @@ -860,81 +945,75 @@ bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::li return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) -{ + +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_offset >= m_blocks.size()) + if (start_offset >= m_blocks.size()) { return false; + } - for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); + } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) -{ + +bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { CRITICAL_REGION_LOCAL(m_blockchain_lock); rsp.current_blockchain_height = get_current_blockchain_height(); std::list blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); - BOOST_FOREACH(const auto& bl, blocks) - { + for (const auto& bl : blocks) { std::list missed_tx_id; std::list txs; get_transactions(bl.tx_hashes, txs, rsp.missed_ids); - CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() - << ENDL << "for block id = " << get_block_hash(bl)); - rsp.blocks.push_back(block_complete_entry()); - block_complete_entry& e = rsp.blocks.back(); - //pack block - e.block = t_serializable_object_to_blob(bl); - //pack transactions - BOOST_FOREACH(transaction& tx, txs) - e.txs.push_back(t_serializable_object_to_blob(tx)); - + CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl)); + rsp.blocks.push_back(block_complete_entry()); + block_complete_entry& e = rsp.blocks.back(); + //pack block + e.block = t_serializable_object_to_blob(bl); + //pack transactions + for (transaction& tx : txs) { + e.txs.push_back(t_serializable_object_to_blob(tx)); + } } + //get another transactions, if need std::list txs; get_transactions(arg.txs, txs, rsp.missed_ids); //pack aside transactions - BOOST_FOREACH(const auto& tx, txs) + for (const auto& tx : txs) { rsp.txs.push_back(t_serializable_object_to_blob(tx)); + } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::get_alternative_blocks(std::list& blocks) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const auto& alt_bl, m_alternative_chains) - { +bool blockchain_storage::get_alternative_blocks(std::list& blocks) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + for (auto& alt_bl : m_alternative_chains) { blocks.push_back(alt_bl.second.bl); } + return true; } -//------------------------------------------------------------------ -size_t blockchain_storage::get_alternative_blocks_count() -{ + +size_t blockchain_storage::get_alternative_blocks_count() { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_alternative_chains.size(); } -//------------------------------------------------------------------ -bool blockchain_storage::add_out_to_get_random_outs(std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) -{ + +bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - transactions_container::iterator tx_it = m_transactions.find(amount_outs[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL << - ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" - << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first); - transaction& tx = tx_it->second.tx; + const transaction& tx = transactionByIndex(amount_outs[i].first).tx; + CHECK_AND_ASSERT_MES(tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" + << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx)); CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type"); //check if transaction is unlocked - if(!is_tx_spendtime_unlocked(tx.unlock_time)) + if (!is_tx_spendtime_unlocked(tx.unlock_time)) return false; 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()); @@ -942,78 +1021,74 @@ bool blockchain_storage::add_out_to_get_random_outs(std::vector(tx.vout[amount_outs[i].second].target).key; return true; } -//------------------------------------------------------------------ -size_t blockchain_storage::find_end_of_allowed_index(const std::vector >& amount_outs) -{ + +size_t blockchain_storage::find_end_of_allowed_index(const std::vector>& amount_outs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!amount_outs.size()) + if (amount_outs.empty()) { return 0; + } + size_t i = amount_outs.size(); - do - { + do { --i; - transactions_container::iterator it = m_transactions.find(amount_outs[i].first); - CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first); - if(it->second.m_keeper_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height() ) - return i+1; + if (amount_outs[i].first.block + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height()) { + return i + 1; + } } while (i != 0); + return 0; } -//------------------------------------------------------------------ -bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) -{ + +bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { srand(static_cast(time(NULL))); CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(uint64_t amount, req.amounts) - { + for (uint64_t amount : req.amounts) { 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; auto it = m_outputs.find(amount); - if(it == m_outputs.end()) - { + if (it == m_outputs.end()) { LOG_ERROR("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist"); continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist } - std::vector >& amount_outs = it->second; + + std::vector>& amount_outs = it->second; //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split //lets find upper bound of not fresh outs size_t up_index_limit = find_end_of_allowed_index(amount_outs); CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); - if(amount_outs.size() > req.outs_count) - { + if (amount_outs.size() > req.outs_count) { std::set used; size_t try_count = 0; - for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) - { - size_t i = rand()%up_index_limit; - if(used.count(i)) + for (uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) { + size_t i = rand() % up_index_limit; + if (used.count(i)) continue; bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i); used.insert(i); - if(added) + if (added) ++j; ++try_count; } - }else - { - for(size_t i = 0; i != up_index_limit; i++) + } else { + for (size_t i = 0; i != up_index_limit; i++) { add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + } } } return true; } -//------------------------------------------------------------------ + bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!qblock_ids.size() /*|| !req.m_total_height*/) + if (!qblock_ids.size() /*|| !req.m_total_height*/) { LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); return false; } //check genesis match - if(qblock_ids.back() != get_block_hash(m_blocks[0].bl)) + if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) { LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) @@ -1024,21 +1099,21 @@ bool blockchain_storage::find_blockchain_supplement(const std::listsecond; return true; } -//------------------------------------------------------------------ + uint64_t blockchain_storage::block_difficulty(size_t i) { CRITICAL_REGION_LOCAL(m_blockchain_lock); CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"); - if(i == 0) + if (i == 0) return m_blocks[i].cumulative_difficulty; - return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty; + return m_blocks[i].cumulative_difficulty - m_blocks[i - 1].cumulative_difficulty; } -//------------------------------------------------------------------ + void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(start_index >=m_blocks.size()) + if (start_index >= m_blocks.size()) { - LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1); + LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size() - 1); return; } - for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++) + for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) { ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size - << "\nid\t\t" << get_block_hash(m_blocks[i].bl) + << "\nid\t\t" << get_block_hash(m_blocks[i].bl) << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; } LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str()); LOG_PRINT_L0("Blockchain printed with log level 1"); } -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain_index() -{ + +void blockchain_storage::print_blockchain_index() { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index) - ss << "id\t\t" << v.first << " height" << v.second << ENDL << ""; + for (auto& i : m_blockMap) { + ss << "id\t\t" << i.first << " height" << i.second << ENDL << ""; + } LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); } -//------------------------------------------------------------------ -void blockchain_storage::print_blockchain_outs(const std::string& file) -{ + +void blockchain_storage::print_blockchain_outs(const std::string& file) { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const outputs_container::value_type& v, m_outputs) - { - const std::vector >& vals = v.second; - if(vals.size()) - { - ss << "amount: " << v.first << ENDL; - for(size_t i = 0; i != vals.size(); i++) - ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL; + for (const outputs_container::value_type& v : m_outputs) { + const std::vector>& vals = v.second; + if (!vals.empty()) { + ss << "amount: " << v.first << ENDL; + for (size_t i = 0; i != vals.size(); i++) { + ss << "\t" << get_transaction_hash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL; + } } } - if(file_io_utils::save_string_to_file(file, ss.str())) - { + + if (epee::file_io_utils::save_string_to_file(file, ss.str())) { LOG_PRINT_L0("Current outputs index writen to file: " << file); - }else - { + } else { LOG_PRINT_L0("Failed to write current outputs index to file: " << file); } } -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) -{ + +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!find_blockchain_supplement(qblock_ids, resp.start_height)) + if (!find_blockchain_supplement(qblock_ids, resp.start_height)) return false; resp.total_height = get_current_blockchain_height(); size_t count = 0; - for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) - resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl)); + for (size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) { + crypto::hash h; + if (!get_block_hash(m_blocks[i].bl, h)) { + return false; + } + + resp.m_block_ids.push_back(h); + } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) -{ + +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(!find_blockchain_supplement(qblock_ids, start_height)) + if (!find_blockchain_supplement(qblock_ids, start_height)) { return false; + } total_height = get_current_blockchain_height(); size_t count = 0; - for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) - { - blocks.resize(blocks.size()+1); + for (size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) { + blocks.resize(blocks.size() + 1); blocks.back().first = m_blocks[i].bl; std::list mis; get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h) -{ - block_extended_info bei = AUTO_VAL_INIT(bei); - bei.bl = bl; - return add_block_as_invalid(bei, h); -} -//------------------------------------------------------------------ -bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto i_res = m_invalid_blocks.insert(std::map::value_type(h, bei)); - CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed"); - LOG_PRINT_L0("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size()); - return true; -} -//------------------------------------------------------------------ + bool blockchain_storage::have_block(const crypto::hash& id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(m_blocks_index.count(id)) + if (m_blockMap.count(id)) return true; - if(m_alternative_chains.count(id)) - return true; - /*if(m_orphaned_blocks.get().count(id)) - return true;*/ - /*if(m_orphaned_by_tx.count(id)) - return true;*/ - if(m_invalid_blocks.count(id)) + if (m_alternative_chains.count(id)) return true; return false; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc) -{ - crypto::hash id = get_block_hash(bl); - return handle_block_to_main_chain(bl, id, bvc); -} -//------------------------------------------------------------------ -bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = 0; - BOOST_FOREACH(const auto& ot, tx.vout) - { - outputs_container::mapped_type& amount_index = m_outputs[ot.amount]; - amount_index.push_back(std::pair(tx_id, i)); - global_indexes.push_back(amount_index.size()-1); - ++i; - } - return true; -} -//------------------------------------------------------------------ -size_t blockchain_storage::get_total_transactions() -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - return m_transactions.size(); -} -//------------------------------------------------------------------ -bool blockchain_storage::get_outs(uint64_t amount, std::list& pkeys) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_outputs.find(amount); - if(it == m_outputs.end()) - return true; - BOOST_FOREACH(const auto& out_entry, it->second) - { - auto tx_it = m_transactions.find(out_entry.first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size"); - CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size"); - pkeys.push_back(boost::get(tx_it->second.tx.vout[out_entry.second].target).key); - } - - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id) -{ +size_t blockchain_storage::get_total_transactions() { CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = tx.vout.size()-1; - BOOST_REVERSE_FOREACH(const auto& ot, tx.vout) - { - auto it = m_outputs.find(ot.amount); - CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken"); - CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount); - CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch"); - CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch"); - it->second.pop_back(); - --i; - } - return true; + return m_transactionMap.size(); } -//------------------------------------------------------------------ -bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); - struct add_transaction_input_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - const crypto::hash& m_tx_id; - const crypto::hash& m_bl_id; - add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id) - {} - bool operator()(const txin_to_key& in) const - { - const crypto::key_image& ki = in.k_image; - auto r = m_spent_keys.insert(ki); - if(!r.second) - { - //double spend detected - LOG_PRINT_L0("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " have input marked as spent with key image: " << ki << ", block declined"); - return false; - } - return true; - } - - bool operator()(const txin_gen& tx) const{return true;} - bool operator()(const txin_to_script& tx) const{return false;} - bool operator()(const txin_to_scripthash& tx) const{return false;} - }; - BOOST_FOREACH(const txin_v& in, tx.vin) - { - if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in)) - { - LOG_ERROR("critical internal error: add_transaction_input_visitor failed. but here key_images should be shecked"); - purge_transaction_keyimages_from_blockchain(tx, false); - return false; - } - } - transaction_chain_entry ch_e; - ch_e.m_keeper_block_height = bl_height; - ch_e.tx = tx; - auto i_r = m_transactions.insert(std::pair(tx_id, ch_e)); - if(!i_r.second) - { - LOG_PRINT_L0("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain"); - return false; - } - bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes); - CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id); - LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL - << "tx_id: " << tx_id << ENDL - << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")"); - return true; -} -//------------------------------------------------------------------ -bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) -{ +bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto it = m_transactions.find(tx_id); - if(it == m_transactions.end()) - { + auto it = m_transactionMap.find(tx_id); + if (it == m_transactionMap.end()) { LOG_PRINT_RED_L0("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); return false; } - CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); - indexs = it->second.m_global_output_indexes; + const Transaction& tx = transactionByIndex(it->second); + CHECK_AND_ASSERT_MES(tx.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); + indexs.resize(tx.m_global_output_indexes.size()); + for (size_t i = 0; i < tx.m_global_output_indexes.size(); ++i) { + indexs[i] = tx.m_global_output_indexes[i]; + } + return true; } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) -{ + +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); bool res = check_tx_inputs(tx, &max_used_block_height); - if(!res) return false; - CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); + if (!res) return false; + CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) -{ - BOOST_FOREACH(const txin_v& in, tx.vin) - { + +bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) { + for(const txin_v& in : tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true); - if(have_tx_keyimg_as_spent(in_to_key.k_image)) + if (have_tx_keyimg_as_spent(in_to_key.k_image)) { return true; + } } + return false; } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) -{ + +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) { crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) -{ + +bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) { size_t sig_index = 0; - if(pmax_used_block_height) + if (pmax_used_block_height) { *pmax_used_block_height = 0; + } - BOOST_FOREACH(const auto& txin, tx.vin) - { + for (const auto& txin : tx.vin) { CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs"); const txin_to_key& in_to_key = boost::get(txin); CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); - if(have_tx_keyimg_as_spent(in_to_key.k_image)) + if (have_tx_keyimg_as_spent(in_to_key.k_image)) { - LOG_PRINT_L1("Key image already spent in blockchain: " << string_tools::pod_to_hex(in_to_key.k_image)); + LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image)); return false; } CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); - if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) - { + if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) { LOG_PRINT_L0("Failed to check ring signature for tx " << get_transaction_hash(tx)); return false; } @@ -1368,48 +1312,43 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha return true; } -//------------------------------------------------------------------ -bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) -{ - if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) - { + +bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { + if (unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) { //interpret as block index - if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + if (get_current_blockchain_height() - 1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) return true; else return false; - }else - { + } else { //interpret as time uint64_t current_time = static_cast(time(NULL)); - if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) + if (current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) return true; else return false; } + return false; } -//------------------------------------------------------------------ -bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) -{ + +bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); struct outputs_visitor { std::vector& m_results_collector; blockchain_storage& m_bch; - outputs_visitor(std::vector& results_collector, blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch) + outputs_visitor(std::vector& results_collector, blockchain_storage& bch) :m_results_collector(results_collector), m_bch(bch) {} - bool handle_output(const transaction& tx, const tx_out& out) - { + bool handle_output(const transaction& tx, const tx_out& out) { //check tx unlock time - if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) - { + if (!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) { LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << tx.unlock_time); return false; } - if(out.target.type() != typeid(txout_to_key)) + if (out.target.type() != typeid(txout_to_key)) { LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); return false; @@ -1423,255 +1362,340 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h //check ring signature std::vector output_keys; outputs_visitor vi(output_keys, *this); - if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) - { + if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) { LOG_PRINT_L0("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); return false; } - if(txin.key_offsets.size() != output_keys.size()) - { + if (txin.key_offsets.size() != output_keys.size()) { LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); return false; } + CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); - if(m_is_in_checkpoint_zone) + if (m_is_in_checkpoint_zone) { return true; + } + return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data()); } -//------------------------------------------------------------------ -uint64_t blockchain_storage::get_adjusted_time() -{ + +uint64_t blockchain_storage::get_adjusted_time() { //TODO: add collecting median time return time(NULL); } -//------------------------------------------------------------------ -bool blockchain_storage::check_block_timestamp_main(const block& b) -{ - if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) - { + +bool blockchain_storage::check_block_timestamp_main(const block& b) { + if (b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) { LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); return false; } std::vector timestamps; - size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; - for(;offset!= m_blocks.size(); ++offset) + size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0 : m_blocks.size() - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + for (; offset != m_blocks.size(); ++offset) { timestamps.push_back(m_blocks[offset].bl.timestamp); + } return check_block_timestamp(std::move(timestamps), b); } -//------------------------------------------------------------------ -bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) -{ - if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + +bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) { + if (timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) { return true; + } uint64_t median_ts = epee::misc_utils::median(timestamps); - if(b.timestamp < median_ts) - { + if (b.timestamp < median_ts) { LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); return false; } return true; } -//------------------------------------------------------------------ -bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc) -{ - TIME_MEASURE_START(block_processing_time); + +bool blockchain_storage::update_next_comulative_size_limit() { + std::vector sz; + get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + + uint64_t median = epee::misc_utils::median(sz); + if (median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + + m_current_block_cumul_sz_limit = median * 2; + return true; +} + +bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) { + //copy block here to let modify block.target + block bl = bl_; + crypto::hash id = get_block_hash(bl); + CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process + CRITICAL_REGION_LOCAL1(m_blockchain_lock); + if (have_block(id)) { + LOG_PRINT_L3("block with id = " << id << " already exists"); + bvc.m_already_exists = true; + return false; + } + + //check that block refers to chain tail + if (!(bl.prev_id == get_tail_id())) { + //chain switching or wrong block + bvc.m_added_to_main_chain = false; + return handle_alternative_block(bl, id, bvc); + //never relay alternative blocks + } + + return pushBlock(bl, bvc); +} + +const blockchain_storage::Transaction& blockchain_storage::transactionByIndex(TransactionIndex index) { + return m_blocks[index.block].transactions[index.transaction]; +} + +bool blockchain_storage::pushBlock(const block& blockData, block_verification_context& bvc) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if(bl.prev_id != get_tail_id()) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have wrong prev_id: " << bl.prev_id << ENDL - << "expected: " << get_tail_id()); + TIME_MEASURE_START(block_processing_time); + + crypto::hash blockHash = get_block_hash(blockData); + + if (m_blockMap.count(blockHash) != 0) { + LOG_ERROR("Block " << blockHash << " already exists in blockchain."); + bvc.m_verifivation_failed = true; return false; } - if(!check_block_timestamp_main(bl)) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have invalid timestamp: " << bl.timestamp); - //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed + if (blockData.prev_id != get_tail_id()) { + LOG_PRINT_L0("Block " << blockHash << " has wrong prev_id: " << blockData.prev_id << ", expected: " << get_tail_id()); + bvc.m_verifivation_failed = true; + return false; + } + + if (!check_block_timestamp_main(blockData)) { + LOG_PRINT_L0("Block " << blockHash << " has invalid timestamp: " << blockData.timestamp); bvc.m_verifivation_failed = true; return false; } - //check proof of work TIME_MEASURE_START(target_calculating_time); - difficulty_type current_diffic = get_difficulty_for_next_block(); - CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); + difficulty_type currentDifficulty = get_difficulty_for_next_block(); TIME_MEASURE_FINISH(target_calculating_time); + CHECK_AND_ASSERT_MES(currentDifficulty, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); + TIME_MEASURE_START(longhash_calculating_time); crypto::hash proof_of_work = null_hash; - if(!m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) - { - proof_of_work = get_block_longhash(bl, m_blocks.size()); - - if(!check_hash(proof_of_work, current_diffic)) - { - LOG_PRINT_L0("Block with id: " << id << ENDL - << "have not enough proof of work: " << proof_of_work << ENDL - << "nexpected difficulty: " << current_diffic ); + if (m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) { + if (!m_checkpoints.check_block(get_current_blockchain_height(), blockHash)) { + LOG_ERROR("CHECKPOINT VALIDATION FAILED"); bvc.m_verifivation_failed = true; return false; } - }else - { - if(!m_checkpoints.check_block(get_current_blockchain_height(), id)) - { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + } else { + proof_of_work = get_block_longhash(blockData, m_blocks.size()); + if (!check_hash(proof_of_work, currentDifficulty)) { + LOG_PRINT_L0("Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty); bvc.m_verifivation_failed = true; return false; } } + TIME_MEASURE_FINISH(longhash_calculating_time); - if(!prevalidate_miner_transaction(bl, m_blocks.size())) - { - LOG_PRINT_L0("Block with id: " << id - << " failed to pass prevalidation"); + if (!prevalidate_miner_transaction(blockData, m_blocks.size())) { + LOG_PRINT_L0("Block " << blockHash << " failed to pass prevalidation"); bvc.m_verifivation_failed = true; return false; } - size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); + + crypto::hash minerTransactionHash = get_transaction_hash(blockData.miner_tx); + + Block block; + block.bl = blockData; + block.transactions.resize(1); + block.transactions[0].tx = blockData.miner_tx; + TransactionIndex transactionIndex = { static_cast(m_blocks.size()), static_cast(0) }; + pushTransaction(block, minerTransactionHash, transactionIndex); + + size_t coinbase_blob_size = get_object_blobsize(blockData.miner_tx); size_t cumulative_block_size = coinbase_blob_size; - //process transactions - if(!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_height())) - { - LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); - bvc.m_verifivation_failed = true; - return false; - } - size_t tx_processed_count = 0; uint64_t fee_summary = 0; - BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes) - { - transaction tx; + for (const crypto::hash& tx_id : blockData.tx_hashes) { + block.transactions.resize(block.transactions.size() + 1); size_t blob_size = 0; uint64_t fee = 0; - if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee)) - { - LOG_PRINT_L0("Block with id: " << id << "have at least one unknown transaction with id: " << tx_id); - purge_block_data_from_blockchain(bl, tx_processed_count); - //add_block_as_invalid(bl, id); + if (!m_tx_pool.take_tx(tx_id, block.transactions.back().tx, blob_size, fee)) { + LOG_PRINT_L0("Block " << blockHash << " has at least one unknown transaction: " << tx_id); bvc.m_verifivation_failed = true; + tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + block.transactions.pop_back(); + popTransactions(block, minerTransactionHash); return false; } - if(!check_tx_inputs(tx)) - { - LOG_PRINT_L0("Block with id: " << id << "have at least one transaction (id: " << tx_id << ") with wrong inputs."); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool add_res = m_tx_pool.add_tx(tx, tvc, true); - CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); - purge_block_data_from_blockchain(bl, tx_processed_count); - add_block_as_invalid(bl, id); - LOG_PRINT_L0("Block with id " << id << " added as invalid becouse of wrong inputs in transactions"); + + if (!check_tx_inputs(block.transactions.back().tx)) { + LOG_PRINT_L0("Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id); bvc.m_verifivation_failed = true; + tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + if (!m_tx_pool.add_tx(block.transactions.back().tx, tvc, true)) { + LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); + } + + block.transactions.pop_back(); + popTransactions(block, minerTransactionHash); return false; } - if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height())) - { - LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - bool add_res = m_tx_pool.add_tx(tx, tvc, true); - CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; - } - fee_summary += fee; + ++transactionIndex.transaction; + pushTransaction(block, tx_id, transactionIndex); + cumulative_block_size += blob_size; - ++tx_processed_count; + fee_summary += fee; } + uint64_t base_reward = 0; - uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0; - if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) - { - LOG_PRINT_L0("Block with id: " << id - << " have wrong miner transaction"); - purge_block_data_from_blockchain(bl, tx_processed_count); + uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins : 0; + if (!validate_miner_transaction(blockData, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) { + LOG_PRINT_L0("Block " << blockHash << " has invalid miner transaction"); bvc.m_verifivation_failed = true; + popTransactions(block, minerTransactionHash); return false; } - - block_extended_info bei = boost::value_initialized(); - bei.bl = bl; - bei.block_cumulative_size = cumulative_block_size; - bei.cumulative_difficulty = current_diffic; - bei.already_generated_coins = already_generated_coins + base_reward; - if(m_blocks.size()) - bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty; - - bei.height = m_blocks.size(); - - auto ind_res = m_blocks_index.insert(std::pair(id, bei.height)); - if(!ind_res.second) - { - LOG_ERROR("block with id: " << id << " already in block indexes"); - purge_block_data_from_blockchain(bl, tx_processed_count); - bvc.m_verifivation_failed = true; - return false; + block.height = static_cast(m_blocks.size()); + block.block_cumulative_size = cumulative_block_size; + block.cumulative_difficulty = currentDifficulty; + block.already_generated_coins = already_generated_coins + base_reward; + if (m_blocks.size() > 0) { + block.cumulative_difficulty += m_blocks.back().cumulative_difficulty; } - m_blocks.push_back(bei); + pushBlock(block); update_next_comulative_size_limit(); TIME_MEASURE_FINISH(block_processing_time); - LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id + LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash << ENDL << "PoW:\t" << proof_of_work - << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic + << ENDL << "HEIGHT " << block.height << ", difficulty:\t" << currentDifficulty << 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"); + << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); bvc.m_added_to_main_chain = true; - /*if(!m_orphanes_reorganize_in_work) - review_orphaned_blocks_with_new_block_id(id, true);*/ - - m_tx_pool.on_blockchain_inc(bei.height, id); - //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl)); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::update_next_comulative_size_limit() -{ - std::vector sz; - get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - uint64_t median = misc_utils::median(sz); - if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) - median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; +bool blockchain_storage::pushBlock(Block& block) { + crypto::hash blockHash = get_block_hash(block.bl); + auto result = m_blockMap.insert(std::make_pair(blockHash, static_cast(m_blocks.size()))); + if (!result.second) { + LOG_ERROR("Duplicate block was pushed to blockchain."); + return false; + } - m_current_block_cumul_sz_limit = median*2; + m_blocks.push_back(block); return true; } -//------------------------------------------------------------------ -bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) -{ - //copy block here to let modify block.target - block bl = bl_; - crypto::hash id = get_block_hash(bl); - CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process - CRITICAL_REGION_LOCAL1(m_blockchain_lock); - if(have_block(id)) - { - LOG_PRINT_L3("block with id = " << id << " already exists"); - bvc.m_already_exists = true; + +void blockchain_storage::popBlock(const crypto::hash& blockHash) { + if (m_blocks.empty()) { + LOG_ERROR("Attempt to pop block from empty blockchain."); + return; + } + + popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.miner_tx)); + m_blocks.pop_back(); + size_t count = m_blockMap.erase(blockHash); + if (count != 1) { + LOG_ERROR("Blockchain consistency broken - cannot find block by hash."); + } +} + +bool blockchain_storage::pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { + auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + if (!result.second) { + LOG_ERROR("Duplicate transaction was pushed to blockchain."); return false; } - //check that block refers to chain tail - if(!(bl.prev_id == get_tail_id())) - { - //chain switching or wrong block - bvc.m_added_to_main_chain = false; - return handle_alternative_block(bl, id, bvc); - //never relay alternative blocks + Transaction& transaction = block.transactions[transactionIndex.transaction]; + for (size_t i = 0; i < transaction.tx.vin.size(); ++i) { + if (transaction.tx.vin[i].type() == typeid(txin_to_key)) { + auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).k_image); + if (!result.second) { + LOG_ERROR("Double spending transaction was pushed to blockchain."); + for (size_t j = 0; j < i; ++j) { + m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).k_image); + } + + m_transactionMap.erase(transactionHash); + return false; + } + } + } + + transaction.m_global_output_indexes.resize(transaction.tx.vout.size()); + for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) { + auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; + transaction.m_global_output_indexes[output] = amountOutputs.size(); + amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); + } + + return true; +} + +void blockchain_storage::popTransaction(const transaction& transaction, const crypto::hash& transactionHash) { + TransactionIndex transactionIndex = m_transactionMap.at(transactionHash); + for (size_t output = 0; output < transaction.vout.size(); ++output) { + auto amountOutputs = m_outputs.find(transaction.vout[transaction.vout.size() - 1 - output].amount); + if (amountOutputs == m_outputs.end()) { + LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); + continue; + } + + if (amountOutputs->second.empty()) { + LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); + continue; + } + + if (amountOutputs->second.back().first.block != transactionIndex.block || amountOutputs->second.back().first.transaction != transactionIndex.transaction) { + LOG_ERROR("Blockchain consistency broken - invalid transaction index."); + continue; + } + + if (amountOutputs->second.back().second != transaction.vout.size() - 1 - output) { + LOG_ERROR("Blockchain consistency broken - invalid output index."); + continue; + } + + amountOutputs->second.pop_back(); + if (amountOutputs->second.empty()) { + m_outputs.erase(amountOutputs); + } + } + + for (auto& input : transaction.vin) { + if (input.type() == typeid(txin_to_key)) { + size_t count = m_spent_keys.erase(::boost::get(input).k_image); + if (count != 1) { + LOG_ERROR("Blockchain consistency broken - cannot find spent key."); + } + } } - return handle_block_to_main_chain(bl, id, bvc); -} \ No newline at end of file + size_t count = m_transactionMap.erase(transactionHash); + if (count != 1) { + LOG_ERROR("Blockchain consistency broken - cannot find transaction by hash."); + } +} + +void blockchain_storage::popTransactions(const Block& block, const crypto::hash& minerTransactionHash) { + for (size_t i = 0; i < block.transactions.size() - 1; ++i) { + popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.tx_hashes[block.transactions.size() - 2 - i]); + tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) { + LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); + } + } + + popTransaction(block.bl.miner_tx, minerTransactionHash); +} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index b1fb5df41a..1cbb3dabdc 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -3,55 +3,17 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "syncobj.h" -#include "string_tools.h" + +#include "SwappedVector.h" +#include "cryptonote_format_utils.h" #include "tx_pool.h" -#include "cryptonote_basic.h" #include "common/util.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "difficulty.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "verification_context.h" -#include "crypto/hash.h" #include "checkpoints.h" -namespace cryptonote -{ - - /************************************************************************/ - /* */ - /************************************************************************/ - class blockchain_storage - { +namespace cryptonote { + class blockchain_storage { public: - struct transaction_chain_entry - { - transaction tx; - uint64_t m_keeper_block_height; - size_t m_blob_size; - std::vector m_global_output_indexes; - }; - - struct block_extended_info - { - block bl; - uint64_t height; - size_t block_cumulative_size; - difficulty_type cumulative_difficulty; - uint64_t already_generated_coins; - }; - blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false) {}; @@ -60,26 +22,17 @@ namespace cryptonote bool deinit(); void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } - - //bool push_new_block(); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); crypto::hash get_block_id_by_height(uint64_t height); bool get_block_by_hash(const crypto::hash &h, block &blk); - void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - template - void serialize(archive_t & ar, const unsigned int version); + template void serialize(archive_t & ar, const unsigned int version); bool have_tx(const crypto::hash &id); bool have_tx_keyimges_as_spent(const transaction &tx); - bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); - transaction *get_tx(const crypto::hash &id); - - template - bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); uint64_t get_current_blockchain_height(); crypto::hash get_tail_id(); @@ -90,231 +43,190 @@ namespace cryptonote bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); bool have_block(const crypto::hash& id); size_t get_total_transactions(); - bool get_outs(uint64_t amount, std::list& pkeys); bool get_short_chain_history(std::list& ids); bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + bool find_blockchain_supplement(const std::list& qblock_ids, std::list>>& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); - bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); 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); bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count); bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); bool store_blockchain(); - bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); - bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); - bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id); uint64_t get_current_comulative_blocksize_limit(); bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) - { + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const auto& bl_id, block_ids) - { - auto it = m_blocks_index.find(bl_id); - if(it == m_blocks_index.end()) + for (const auto& bl_id : block_ids) { + auto it = m_blockMap.find(bl_id); + if (it == m_blockMap.end()) { missed_bs.push_back(bl_id); - else - { + } else { CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) << " have index record with offset="<second<< ", bigger then m_blocks.size()=" << m_blocks.size()); blocks.push_back(m_blocks[it->second].bl); } } + return true; } template - bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) - { + bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - BOOST_FOREACH(const auto& tx_id, txs_ids) - { - auto it = m_transactions.find(tx_id); - if(it == m_transactions.end()) - { + for (const auto& tx_id : txs_ids) { + auto it = m_transactionMap.find(tx_id); + if (it == m_transactionMap.end()) { transaction tx; - if(!m_tx_pool.get_transaction(tx_id, tx)) + if (!m_tx_pool.get_transaction(tx_id, tx)) { missed_txs.push_back(tx_id); - else + } else { txs.push_back(tx); + } + } else { + txs.push_back(transactionByIndex(it->second).tx); } - else - txs.push_back(it->second.tx); } + return true; } + //debug functions void print_blockchain(uint64_t start_index, uint64_t end_index); void print_blockchain_index(); void print_blockchain_outs(const std::string& file); private: - typedef std::unordered_map blocks_by_id_index; - typedef std::unordered_map transactions_container; + struct Transaction { + transaction tx; + std::vector m_global_output_indexes; + + template void serialize(archive_t & ar, unsigned int version); + + BEGIN_SERIALIZE_OBJECT() + FIELD(tx) + FIELD(m_global_output_indexes) + END_SERIALIZE() + }; + + struct Block { + block bl; + uint32_t height; + uint64_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + std::vector transactions; + + template void serialize(Archive& archive, unsigned int version); + + BEGIN_SERIALIZE_OBJECT() + FIELD(bl) + VARINT_FIELD(height) + VARINT_FIELD(block_cumulative_size) + VARINT_FIELD(cumulative_difficulty) + VARINT_FIELD(already_generated_coins) + FIELD(transactions) + END_SERIALIZE() + }; + + struct TransactionIndex { + uint32_t block; + uint16_t transaction; + + template void serialize(Archive& archive, unsigned int version); + }; + typedef std::unordered_set key_images_container; - typedef std::vector blocks_container; - typedef std::unordered_map blocks_ext_by_hash; - typedef std::unordered_map blocks_by_hash; - typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction + typedef std::unordered_map blocks_ext_by_hash; + typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction tx_memory_pool& m_tx_pool; epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock - // main chain - blocks_container m_blocks; // height -> block_extended_info - blocks_by_id_index m_blocks_index; // crypto::hash -> height - transactions_container m_transactions; key_images_container m_spent_keys; size_t m_current_block_cumul_sz_limit; - - - // all alternative chains blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info - - // some invalid blocks - blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info outputs_container m_outputs; - std::string m_config_folder; checkpoints m_checkpoints; std::atomic m_is_in_checkpoint_zone; std::atomic m_is_blockchain_storing; - bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); - bool pop_block_from_blockchain(); - bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); - bool purge_transaction_from_blockchain(const crypto::hash& tx_id); - bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check); + typedef SwappedVector Blocks; + typedef std::unordered_map BlockMap; + typedef std::unordered_map TransactionMap; + + Blocks m_blocks; + BlockMap m_blockMap; + TransactionMap m_transactionMap; - bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc); - bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc); + template bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); + bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); - difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, block_extended_info& bei); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, Block& bei); bool prevalidate_miner_transaction(const block& b, uint64_t height); 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 validate_transaction(const block& b, uint64_t height, const transaction& tx); bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); - bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height); - bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); - bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); bool get_last_n_blocks_sizes(std::vector& sz, size_t count); - bool add_out_to_get_random_outs(std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); + bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); bool is_tx_spendtime_unlocked(uint64_t unlock_time); - bool add_block_as_invalid(const block& bl, const crypto::hash& h); - bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); - size_t find_end_of_allowed_index(const std::vector >& amount_outs); + size_t find_end_of_allowed_index(const std::vector>& amount_outs); bool check_block_timestamp_main(const block& b); bool check_block_timestamp(std::vector timestamps, const block& b); uint64_t get_adjusted_time(); bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); bool update_next_comulative_size_limit(); + bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); + bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); + bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); + bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); + const Transaction& transactionByIndex(TransactionIndex index); + bool pushBlock(const block& blockData, block_verification_context& bvc); + bool pushBlock(Block& block); + void popBlock(const crypto::hash& blockHash); + bool pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); + void popTransaction(const transaction& transaction, const crypto::hash& transactionHash); + void popTransactions(const Block& block, const crypto::hash& minerTransactionHash); }; - /************************************************************************/ - /* */ - /************************************************************************/ - - #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12 - - template - void blockchain_storage::serialize(archive_t & ar, const unsigned int version) - { - if(version < 11) - return; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - ar & m_blocks; - ar & m_blocks_index; - ar & m_transactions; - ar & m_spent_keys; - ar & m_alternative_chains; - ar & m_outputs; - ar & m_invalid_blocks; - ar & m_current_block_cumul_sz_limit; - /*serialization bug workaround*/ - if(version > 11) - { - uint64_t total_check_count = m_blocks.size() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit; - if(archive_t::is_saving::value) - { - ar & total_check_count; - }else - { - uint64_t total_check_count_loaded = 0; - ar & total_check_count_loaded; - if(total_check_count != total_check_count_loaded) - { - LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count); - - LOG_PRINT_L0("Blockchain storage:" << ENDL << - "m_blocks: " << m_blocks.size() << ENDL << - "m_blocks_index: " << m_blocks_index.size() << ENDL << - "m_transactions: " << m_transactions.size() << ENDL << - "m_spent_keys: " << m_spent_keys.size() << ENDL << - "m_alternative_chains: " << m_alternative_chains.size() << ENDL << - "m_outputs: " << m_outputs.size() << ENDL << - "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL << - "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); - - throw std::runtime_error("Blockchain data corruption"); - } - } - } - - - LOG_PRINT_L2("Blockchain storage:" << ENDL << - "m_blocks: " << m_blocks.size() << ENDL << - "m_blocks_index: " << m_blocks_index.size() << ENDL << - "m_transactions: " << m_transactions.size() << ENDL << - "m_spent_keys: " << m_spent_keys.size() << ENDL << - "m_alternative_chains: " << m_alternative_chains.size() << ENDL << - "m_outputs: " << m_outputs.size() << ENDL << - "m_invalid_blocks: " << m_invalid_blocks.size() << ENDL << - "m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit); - } - - //------------------------------------------------------------------ - template - bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) - { + template bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); auto it = m_outputs.find(tx_in_to_key.amount); - if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) + if (it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) return false; std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); - - - std::vector >& amount_outs_vec = it->second; + std::vector>& amount_outs_vec = it->second; size_t count = 0; - BOOST_FOREACH(uint64_t i, absolute_offsets) - { - if(i >= amount_outs_vec.size() ) - { + for (uint64_t i : absolute_offsets) { + if(i >= amount_outs_vec.size() ) { LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1); return false; } - transactions_container::iterator tx_it = m_transactions.find(amount_outs_vec[i].first); - CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); - CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx_it->second.tx.vout.size(), false, - "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size()); - if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second])) - { + + //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); + //CHECK_AND_ASSERT_MES(tx_it != m_transactionMap.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); + + const Transaction& tx = transactionByIndex(amount_outs_vec[i].first); + CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx.tx.vout.size(), false, + "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx.tx.vout.size()); + if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second])) { LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); return false; } - if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) - { - if(*pmax_related_block_height < tx_it->second.m_keeper_block_height) - *pmax_related_block_height = tx_it->second.m_keeper_block_height; + + if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) { + if (*pmax_related_block_height < amount_outs_vec[i].first.block) { + *pmax_related_block_height = amount_outs_vec[i].first.block; + } } } @@ -322,6 +234,5 @@ namespace cryptonote } } - - -BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) +#include "cryptonote_boost_serialization.h" +#include "blockchain_storage_boost_serialization.h" diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 6aa06e87f5..3f0c94b390 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once - +/* namespace boost { namespace serialization @@ -31,3 +31,4 @@ namespace boost } } +*/ diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index f4f3878eed..60e20e24be 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -26,6 +26,7 @@ namespace cryptonote { ADD_CHECKPOINT(468000, "251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1"); ADD_CHECKPOINT(480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"); ADD_CHECKPOINT(484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"); + ADD_CHECKPOINT(506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"); return true; } diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index 24b6b59d04..348384c8fc 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -34,7 +34,7 @@ namespace cryptonote { } //----------------------------------------------------------------------------------------------- bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) { - uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> 18; + uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR; //make it soft if (median_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) { @@ -94,24 +94,17 @@ namespace cryptonote { return true; } //----------------------------------------------------------------------- - bool get_account_address_from_str(account_public_address& adr, const std::string& str) + bool get_account_address_from_str(uint64_t& prefix, account_public_address& adr, const std::string& str) { if (2 * sizeof(public_address_outer_blob) != str.size()) { blobdata data; - uint64_t prefix; if (!tools::base58::decode_addr(str, prefix, data)) { LOG_PRINT_L1("Invalid address format"); return false; } - if (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) - { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); - return false; - } - if (!::serialization::parse_binary(data, adr)) { LOG_PRINT_L1("Account public address keys can't be parsed"); @@ -127,6 +120,8 @@ namespace cryptonote { else { // Old address format + prefix = CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; + std::string buff; if(!string_tools::parse_hexstr_to_binbuff(str, buff)) return false; @@ -158,6 +153,22 @@ namespace cryptonote { return true; } + //----------------------------------------------------------------------- + bool get_account_address_from_str(account_public_address& adr, const std::string& str) + { + uint64_t prefix; + if(!get_account_address_from_str(prefix, adr, str)) + return false; + + if(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) + { + LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + return false; + } + + return true; + } + //----------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index cb6fb7692d..27251ef54e 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -41,6 +41,7 @@ namespace cryptonote { bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); std::string get_account_address_as_str(const account_public_address& adr); + bool get_account_address_from_str(uint64_t& prefix, account_public_address& adr, const std::string& str); bool get_account_address_from_str(account_public_address& adr, const std::string& str); bool is_coinbase(const transaction& tx); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index b6bfa09c8b..2e46c99bd8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -283,10 +283,10 @@ namespace cryptonote return m_blockchain_storage.get_total_transactions(); } //----------------------------------------------------------------------------------------------- - bool core::get_outs(uint64_t amount, std::list& pkeys) - { - return m_blockchain_storage.get_outs(amount, pkeys); - } + //bool core::get_outs(uint64_t amount, std::list& pkeys) + //{ + // return m_blockchain_storage.get_outs(amount, pkeys); + //} //----------------------------------------------------------------------------------------------- bool core::add_new_tx(const 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) { @@ -397,15 +397,10 @@ namespace cryptonote { m_miner.on_synchronized(); } - bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count) - { - return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); - } - //----------------------------------------------------------------------------------------------- - bool core::add_new_block(const block& b, block_verification_context& bvc) - { - return m_blockchain_storage.add_new_block(b, bvc); - } + //bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count) + //{ + // return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); + //} //----------------------------------------------------------------------------------------------- bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) { @@ -417,7 +412,6 @@ namespace cryptonote return false; } - block b = AUTO_VAL_INIT(b); if(!parse_and_validate_block_from_blob(block_blob, b)) { @@ -425,9 +419,30 @@ namespace cryptonote bvc.m_verifivation_failed = true; return false; } - add_new_block(b, bvc); - if(update_miner_blocktemplate && bvc.m_added_to_main_chain) - update_miner_block_template(); + + m_blockchain_storage.add_new_block(b, bvc); + if (bvc.m_added_to_main_chain) { + cryptonote_connection_context exclude_context = boost::value_initialized(); + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + if (missed_txs.size() > 0 && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { + LOG_PRINT_L0("Block found but reorganize happened after that, block will not be relayed"); + } else { + CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); + block_to_blob(b, arg.b.block); + BOOST_FOREACH(auto& tx, txs) arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + m_pprotocol->relay_block(arg, exclude_context); + } + + if (update_miner_blocktemplate) { + update_miner_block_template(); + } + } + return true; } //----------------------------------------------------------------------------------------------- @@ -480,9 +495,9 @@ namespace cryptonote return m_blockchain_storage.get_block_by_hash(h, blk); } //----------------------------------------------------------------------------------------------- - void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { - m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); - } + //void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { + // m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); + //} //----------------------------------------------------------------------------------------------- std::string core::print_pool(bool short_format) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index cde52d5a21..056aa0743c 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -59,7 +59,7 @@ namespace cryptonote bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); bool get_transaction(const crypto::hash &h, transaction &tx); bool get_block_by_hash(const crypto::hash &h, block &blk); - void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); @@ -70,13 +70,13 @@ namespace cryptonote bool get_pool_transactions(std::list& txs); size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); - bool get_outs(uint64_t amount, std::list& pkeys); + //bool get_outs(uint64_t amount, std::list& pkeys); bool have_block(const crypto::hash& id); bool get_short_chain_history(std::list& ids); bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool get_stat_info(core_stat_info& st_inf); - bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); + //bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); crypto::hash get_tail_id(); 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); @@ -93,7 +93,6 @@ namespace cryptonote private: bool add_new_tx(const 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 add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); - bool add_new_block(const block& b, block_verification_context& bvc); bool load_state_data(); bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 049583932f..5b6fadf25d 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -591,18 +591,23 @@ namespace cryptonote return get_object_hash(t, res, blob_size); } //--------------------------------------------------------------- - blobdata get_block_hashing_blob(const block& b) + bool get_block_hashing_blob(const block& b, blobdata& blob) { - blobdata blob = t_serializable_object_to_blob(static_cast(b)); + if(!t_serializable_object_to_blob(static_cast(b), blob)) + return false; crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash )); - blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); - return blob; + blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); + blob.append(tools::get_varint_data(b.tx_hashes.size() + 1)); + + return true; } //--------------------------------------------------------------- bool get_block_hash(const block& b, crypto::hash& res) { - return get_object_hash(get_block_hashing_blob(b), res); + blobdata blob; + if (!get_block_hashing_blob(b, blob)) + return false; + return get_object_hash(blob, res); } //--------------------------------------------------------------- crypto::hash get_block_hash(const block& b) @@ -641,8 +646,9 @@ namespace cryptonote //--------------------------------------------------------------- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { - block b_local = b; //workaround to avoid const errors with do_serialize - blobdata bd = get_block_hashing_blob(b); + blobdata bd; + if(!get_block_hashing_blob(b, bd)) + return false; crypto::cn_slow_hash(bd.data(), bd.size(), res); return true; } diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 138fb52244..662e160ea3 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -74,7 +74,7 @@ namespace cryptonote crypto::hash get_transaction_hash(const transaction& t); bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); - blobdata get_block_hashing_blob(const block& b); + bool get_block_hashing_blob(const block& b, blobdata& blob); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); @@ -85,7 +85,6 @@ namespace cryptonote uint64_t get_outs_money_amount(const transaction& tx); bool check_inputs_types_supported(const transaction& tx); bool check_outs_valid(const transaction& tx); - blobdata get_block_hashing_blob(const block& b); bool parse_amount(uint64_t& amount, const std::string& str_amount); bool check_money_overflow(const transaction& tx); diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 56b459d6e4..fdc1545746 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -31,7 +31,7 @@ namespace cryptonote { const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; - const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; } @@ -340,7 +340,7 @@ namespace cryptonote crypto::hash h; get_block_longhash(b, h, height); - if(check_hash(h, local_diff)) + if(!m_stop && check_hash(h, local_diff)) { //we lucky! ++m_config.current_extra_message_index; @@ -354,6 +354,7 @@ namespace cryptonote epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); } } + nonce+=m_threads_total; ++m_hashes; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 916f10c3b3..5c60be0af6 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -130,7 +130,7 @@ namespace nodetool if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) return false; } - else if (command_line::has_arg(vm, arg_p2p_add_priority_node)) + if (command_line::has_arg(vm, arg_p2p_add_priority_node)) { if (!parse_peers_and_add_to_container(vm, arg_p2p_add_priority_node, m_priority_peers)) return false; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index f9e04bb89b..07f4a1cb9d 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -331,7 +331,7 @@ namespace cryptonote return false; } - if(req.reserve_size > 255) + if(req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE; error_resp.message = "To big reserved size, maximum 255"; @@ -357,31 +357,41 @@ namespace cryptonote LOG_ERROR("Failed to create block template"); return false; } + blobdata block_blob = t_serializable_object_to_blob(b); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); if(tx_pub_key == null_pkey) { 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 find 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)); - if(!res.reserved_offset) + + if(0 < req.reserve_size) { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to find tx pub key in blockblob"); - return false; + res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if(!res.reserved_offset) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to find tx pub key in blockblob"); + return false; + } + res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if(res.reserved_offset + req.reserve_size > block_blob.size()) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to calculate offset for reserved bytes"); + return false; + } } - res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - if(res.reserved_offset + req.reserve_size > block_blob.size()) + else { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to calculate offset for "); - return false; + res.reserved_offset = 0; } + res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); res.status = CORE_RPC_STATUS_OK; return true; diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index d06e8a22a5..00bb1741d6 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + #include #include "binary_archive.h" diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index b7763ffebe..6e683e6298 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + #include #include "serialization.h" diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 35bcc03356..24f5c11a81 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -2,6 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#pragma once + #include #include "json_archive.h" diff --git a/src/serialization/vector.h b/src/serialization/vector.h index 9a0c0ee565..d074723525 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -16,6 +16,13 @@ namespace serialization return ::do_serialize(ar, e); } + template + bool serialize_vector_element(Archive& ar, uint32_t& e) + { + ar.serialize_varint(e); + return true; + } + template bool serialize_vector_element(Archive& ar, uint64_t& e) { diff --git a/src/version.h.in b/src/version.h.in index 2d65ea8bd4..e412253e8d 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.9" +#define PROJECT_VERSION "0.8.10" #define PROJECT_VERSION_BUILD_NO "65" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 0d42dbaf44..7189ca9f28 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -55,6 +55,9 @@ namespace tools template struct wallet_error_base : public Base { + // This is necessary to compile with g++ 4.7.3, because of ~std::string() (m_loc) can throw an exception + ~wallet_error_base() throw() { } + const std::string& location() const { return m_loc; } std::string to_string() const @@ -96,6 +99,8 @@ namespace tools { } + ~failed_rpc_request() throw() { } + const std::string& status() const { return m_status; } std::string to_string() const @@ -128,6 +133,8 @@ namespace tools { } + ~unexpected_txin_type() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } std::string to_string() const @@ -165,6 +172,8 @@ namespace tools { } + ~file_error_base() throw() { } + const std::string& file() const { return m_file; } std::string to_string() const { return wallet_logic_error::to_string(); } @@ -192,7 +201,7 @@ namespace tools struct refresh_error : public wallet_logic_error { protected: - refresh_error(std::string&& loc, const std::string& message) + explicit refresh_error(std::string&& loc, const std::string& message) : wallet_logic_error(std::move(loc), message) { } @@ -209,6 +218,8 @@ namespace tools { } + ~acc_outs_lookup_error() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } const crypto::public_key& tx_pub_key() const { return m_tx_pub_key; } const cryptonote::account_keys& acc_keys() const { return m_acc_keys; } @@ -235,6 +246,8 @@ namespace tools { } + ~block_parse_error() throw() { } + const cryptonote::blobdata& block_blob() const { return m_block_blob; } std::string to_string() const { return refresh_error::to_string(); } @@ -255,6 +268,8 @@ namespace tools { } + ~tx_parse_error() throw() { } + const cryptonote::blobdata& tx_blob() const { return m_tx_blob; } std::string to_string() const { return refresh_error::to_string(); } @@ -266,7 +281,7 @@ namespace tools struct transfer_error : public wallet_logic_error { protected: - transfer_error(std::string&& loc, const std::string& message) + explicit transfer_error(std::string&& loc, const std::string& message) : wallet_logic_error(std::move(loc), message) { } @@ -276,7 +291,7 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct not_enough_money : public transfer_error { - not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) + explicit not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) : transfer_error(std::move(loc), "not enough money") , m_available(availbable) , m_tx_amount(tx_amount) @@ -315,6 +330,8 @@ namespace tools { } + ~not_enough_outs_to_mix() throw() { } + const scanty_outs_t& scanty_outs() const { return m_scanty_outs; } size_t mixin_count() const { return m_mixin_count; } @@ -347,6 +364,8 @@ namespace tools { } + ~tx_not_constructed() throw() { } + const sources_t& sources() const { return m_sources; } const destinations_t& destinations() const { return m_destinations; } uint64_t unlock_time() const { return m_unlock_time; } @@ -401,6 +420,8 @@ namespace tools { } + ~tx_rejected() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } const std::string& status() const { return m_status; } @@ -420,13 +441,15 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_sum_overflow : public transfer_error { - tx_sum_overflow(std::string&& loc, const std::vector& destinations, uint64_t fee) + explicit tx_sum_overflow(std::string&& loc, const std::vector& destinations, uint64_t fee) : transfer_error(std::move(loc), "transaction sum + fee exceeds " + cryptonote::print_money(std::numeric_limits::max())) , m_destinations(destinations) , m_fee(fee) { } + ~tx_sum_overflow() throw() { } + const std::vector& destinations() const { return m_destinations; } uint64_t fee() const { return m_fee; } @@ -457,6 +480,8 @@ namespace tools { } + ~tx_too_big() throw() { } + const cryptonote::transaction& tx() const { return m_tx; } uint64_t tx_size_limit() const { return m_tx_size_limit; } @@ -486,6 +511,8 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct wallet_rpc_error : public wallet_logic_error { + ~wallet_rpc_error() throw() { } + const std::string& request() const { return m_request; } std::string to_string() const @@ -496,7 +523,7 @@ namespace tools } protected: - wallet_rpc_error(std::string&& loc, const std::string& message, const std::string& request) + explicit wallet_rpc_error(std::string&& loc, const std::string& message, const std::string& request) : wallet_logic_error(std::move(loc), message) , m_request(request) { @@ -529,6 +556,8 @@ namespace tools { } + ~wallet_files_doesnt_correspond() throw() { } + const std::string& keys_file() const { return m_keys_file; } const std::string& wallet_file() const { return m_wallet_file; } From e3999735508132f0e3e36887622237c2d0c01d81 Mon Sep 17 00:00:00 2001 From: Albert Werner Date: Sat, 21 Jun 2014 17:41:43 +0400 Subject: [PATCH 02/59] Initial commit --- src/CMakeLists.txt | 4 +- src/common/base58.cpp | 2 +- src/common/base58.h | 2 +- src/common/boost_serialization_helper.h | 2 +- src/common/command_line.cpp | 2 +- src/common/command_line.h | 2 +- src/common/int-util.h | 2 +- src/common/pod-class.h | 2 +- ...unordered_containers_boost_serialization.h | 2 +- src/common/util.cpp | 2 +- src/common/util.h | 2 +- src/common/varint.h | 2 +- src/connectivity_tool/conn_tool.cpp | 2 +- src/crypto/chacha8.h | 2 +- src/crypto/crypto-ops-data.c | 2 +- src/crypto/crypto-ops.c | 2 +- src/crypto/crypto-ops.h | 2 +- src/crypto/crypto.cpp | 2 +- src/crypto/crypto.h | 2 +- src/crypto/generic-ops.h | 2 +- src/crypto/hash-extra-blake.c | 2 +- src/crypto/hash-extra-groestl.c | 2 +- src/crypto/hash-extra-jh.c | 2 +- src/crypto/hash-extra-skein.c | 2 +- src/crypto/hash-ops.h | 2 +- src/crypto/hash.c | 2 +- src/crypto/hash.h | 2 +- src/crypto/initializer.h | 2 +- src/crypto/slow-hash.c | 2 +- src/cryptonote_config.h | 43 ++-- src/cryptonote_core/SwappedMap.cpp | 4 + src/cryptonote_core/SwappedMap.h | 4 + src/cryptonote_core/SwappedVector.cpp | 4 + src/cryptonote_core/SwappedVector.h | 4 + src/cryptonote_core/account.cpp | 2 +- src/cryptonote_core/account.h | 2 +- .../account_boost_serialization.h | 2 +- src/cryptonote_core/blockchain_storage.cpp | 206 +----------------- src/cryptonote_core/blockchain_storage.h | 6 +- .../blockchain_storage_boost_serialization.h | 2 +- src/cryptonote_core/checkpoints.cpp | 2 +- src/cryptonote_core/checkpoints.h | 2 +- src/cryptonote_core/checkpoints_create.h | 18 +- src/cryptonote_core/connection_context.h | 2 +- src/cryptonote_core/cryptonote_basic.h | 2 +- src/cryptonote_core/cryptonote_basic_impl.cpp | 2 +- src/cryptonote_core/cryptonote_basic_impl.h | 2 +- .../cryptonote_boost_serialization.h | 2 +- src/cryptonote_core/cryptonote_core.cpp | 2 +- src/cryptonote_core/cryptonote_core.h | 2 +- .../cryptonote_format_utils.cpp | 11 +- src/cryptonote_core/cryptonote_format_utils.h | 2 +- src/cryptonote_core/cryptonote_stat_info.h | 2 +- src/cryptonote_core/difficulty.cpp | 2 +- src/cryptonote_core/difficulty.h | 2 +- src/cryptonote_core/miner.cpp | 2 +- src/cryptonote_core/miner.h | 2 +- src/cryptonote_core/tx_extra.h | 2 +- src/cryptonote_core/tx_pool.cpp | 2 +- src/cryptonote_core/tx_pool.h | 2 +- src/cryptonote_core/verification_context.h | 2 +- src/cryptonote_protocol/blobdatatype.h | 2 +- .../cryptonote_protocol_defs.h | 2 +- .../cryptonote_protocol_handler.h | 2 +- .../cryptonote_protocol_handler.inl | 2 +- .../cryptonote_protocol_handler_common.h | 2 +- src/daemon/daemon.cpp | 2 +- src/daemon/daemon_commands_handler.h | 2 +- src/miner/simpleminer.cpp | 2 +- src/miner/simpleminer.h | 2 +- src/miner/simpleminer_protocol_defs.h | 2 +- src/miner/target_helper.h | 2 +- src/p2p/net_node.h | 2 +- src/p2p/net_node.inl | 20 +- src/p2p/net_node_common.h | 2 +- src/p2p/net_peerlist.h | 2 +- src/p2p/net_peerlist_boost_serialization.h | 2 +- src/p2p/p2p_networks.h | 4 +- src/p2p/p2p_protocol_defs.h | 2 +- src/p2p/stdafx.h | 2 +- src/rpc/core_rpc_server.cpp | 2 +- src/rpc/core_rpc_server.h | 2 +- src/rpc/core_rpc_server_commands_defs.h | 2 +- src/rpc/core_rpc_server_error_codes.h | 2 +- src/serialization/binary_archive.h | 2 +- src/serialization/binary_utils.h | 2 +- src/serialization/crypto.h | 2 +- src/serialization/debug_archive.h | 2 +- src/serialization/json_archive.h | 2 +- src/serialization/json_utils.h | 2 +- src/serialization/serialization.h | 2 +- src/serialization/string.h | 2 +- src/serialization/variant.h | 2 +- src/serialization/vector.h | 2 +- src/simplewallet/password_container.cpp | 2 +- src/simplewallet/password_container.h | 2 +- src/simplewallet/simplewallet.cpp | 2 +- src/simplewallet/simplewallet.h | 2 +- src/wallet/wallet2.cpp | 2 +- src/wallet/wallet2.h | 2 +- src/wallet/wallet_errors.h | 2 +- src/wallet/wallet_rpc_server.cpp | 2 +- src/wallet/wallet_rpc_server.h | 2 +- src/wallet/wallet_rpc_server_commans_defs.h | 2 +- src/wallet/wallet_rpc_server_error_codes.h | 2 +- tests/core_proxy/core_proxy.cpp | 2 +- tests/core_proxy/core_proxy.h | 2 +- tests/core_tests/block_reward.cpp | 2 +- tests/core_tests/block_reward.h | 2 +- tests/core_tests/block_validation.cpp | 2 +- tests/core_tests/block_validation.h | 2 +- tests/core_tests/chain_split_1.cpp | 2 +- tests/core_tests/chain_split_1.h | 2 +- tests/core_tests/chain_switch_1.cpp | 2 +- tests/core_tests/chain_switch_1.h | 2 +- tests/core_tests/chaingen.cpp | 2 +- tests/core_tests/chaingen.h | 2 +- tests/core_tests/chaingen001.cpp | 2 +- tests/core_tests/chaingen_main.cpp | 2 +- tests/core_tests/chaingen_tests_list.h | 2 +- tests/core_tests/double_spend.cpp | 2 +- tests/core_tests/double_spend.h | 2 +- tests/core_tests/integer_overflow.cpp | 2 +- tests/core_tests/integer_overflow.h | 2 +- tests/core_tests/ring_signature_1.cpp | 2 +- tests/core_tests/ring_signature_1.h | 2 +- tests/core_tests/transaction_tests.cpp | 2 +- tests/core_tests/transaction_tests.h | 2 +- tests/core_tests/tx_validation.cpp | 2 +- tests/core_tests/tx_validation.h | 2 +- tests/crypto/crypto-ops-data.c | 2 +- tests/crypto/crypto-ops.c | 2 +- tests/crypto/crypto-tests.h | 2 +- tests/crypto/crypto.cpp | 2 +- tests/crypto/hash.c | 2 +- tests/crypto/main.cpp | 2 +- tests/crypto/random.c | 2 +- tests/daemon_tests/transfers.cpp | 2 +- tests/difficulty/difficulty.cpp | 2 +- tests/functional_tests/main.cpp | 2 +- .../transactions_flow_test.cpp | 2 +- .../functional_tests/transactions_flow_test.h | 2 +- ...ransactions_generation_from_blockchain.cpp | 2 +- .../transactions_generation_from_blockchain.h | 2 +- tests/hash-target.cpp | 2 +- tests/hash/main.cpp | 2 +- tests/io.h | 2 +- tests/net_load_tests/clt.cpp | 2 +- tests/net_load_tests/net_load_tests.h | 2 +- tests/net_load_tests/srv.cpp | 2 +- .../performance_tests/check_ring_signature.h | 2 +- tests/performance_tests/cn_slow_hash.h | 2 +- tests/performance_tests/construct_tx.h | 2 +- tests/performance_tests/derive_public_key.h | 2 +- tests/performance_tests/derive_secret_key.h | 2 +- .../generate_key_derivation.h | 2 +- tests/performance_tests/generate_key_image.h | 2 +- .../generate_key_image_helper.h | 2 +- tests/performance_tests/is_out_to_acc.h | 2 +- tests/performance_tests/main.cpp | 2 +- tests/performance_tests/multi_tx_test_base.h | 2 +- tests/performance_tests/performance_tests.h | 2 +- tests/performance_tests/performance_utils.h | 2 +- tests/performance_tests/single_tx_test_base.h | 2 +- tests/unit_tests/base58.cpp | 2 +- tests/unit_tests/block_reward.cpp | 2 +- tests/unit_tests/chacha8.cpp | 2 +- tests/unit_tests/checkpoints.cpp | 2 +- .../decompose_amount_into_digits.cpp | 2 +- tests/unit_tests/epee_boosted_tcp_server.cpp | 2 +- .../epee_levin_protocol_handler_async.cpp | 2 +- tests/unit_tests/get_xtype_from_string.cpp | 2 +- tests/unit_tests/main.cpp | 2 +- tests/unit_tests/mul_div.cpp | 2 +- tests/unit_tests/parse_amount.cpp | 2 +- tests/unit_tests/serialization.cpp | 2 +- tests/unit_tests/test_format_utils.cpp | 2 +- tests/unit_tests/test_peerlist.cpp | 2 +- tests/unit_tests/test_protocol_pack.cpp | 2 +- tests/unit_tests/unit_tests_utils.h | 2 +- 180 files changed, 238 insertions(+), 426 deletions(-) mode change 100644 => 100755 src/cryptonote_core/blockchain_storage.cpp mode change 100644 => 100755 src/cryptonote_core/blockchain_storage.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f890fcda91..04ff230e43 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,4 +43,6 @@ add_dependencies(simplewallet version) set_property(TARGET common crypto cryptonote_core rpc wallet PROPERTY FOLDER "libs") set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog") -set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind") +#TODO Specify the name of daemon for your currency +#set_property(TARGET daemon PROPERTY OUTPUT_NAME "cryptonoted") + diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 454c0db676..fecca43249 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/base58.h b/src/common/base58.h index 4055f62baf..4436550dd3 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 0bf9248028..cdd5ceaec7 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 0b90345d95..5be588d897 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/command_line.h b/src/common/command_line.h index 860653772f..bd3726e65b 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/int-util.h b/src/common/int-util.h index db9e9bea7d..15cd1e9906 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/pod-class.h b/src/common/pod-class.h index c07edb2089..e1989356e7 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index 84fa73b924..45101a60af 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/util.cpp b/src/common/util.cpp index c9c470851c..5a1f50cb85 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/util.h b/src/common/util.h index 8a1f4b0414..0af40e2808 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/common/varint.h b/src/common/varint.h index e62470fdfa..a607d83205 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp index 6743b4ae81..3d37f3117b 100644 --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/connectivity_tool/conn_tool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index e4fe467992..7d1e39f375 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 48bfe21a22..a3ded0c827 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 97e7df50e1..919eb983bf 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 9d07fc8b02..4639b517d6 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f5f525700c..e25c966d66 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 61641fbcfc..253ab98b9c 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 8cade72a87..378500c247 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 2eeb520208..50df08aa27 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 7918cfc530..9d7b8b4306 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 15c271b2a2..9bbe77c52d 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index 92361e6db4..60fbcccdca 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 9e6c821efb..7a2a3d29a0 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash.c b/src/crypto/hash.c index a3989d88c3..c86418d70e 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/hash.h b/src/crypto/hash.h index fb65494b67..a5903cd035 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 8c84621bf3..611ae16c13 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 468ba644be..06ef46d53a 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 958639a0ec..10ed771328 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,8 +8,10 @@ #define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! #define CRYPTONOTE_MAX_TX_SIZE 1000000000 #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 -#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 6 // addresses start with "2" -#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 10 +//TODO Define the first letter of your currency address +#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX +//TODO Choose maturity period for your currency +#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW #define CURRENT_TRANSACTION_VERSION 1 #define CURRENT_BLOCK_MAJOR_VERSION 1 #define CURRENT_BLOCK_MINOR_VERSION 0 @@ -17,22 +19,26 @@ #define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60 -// MONEY_SUPPLY - total number coins to be generated -#define MONEY_SUPPLY ((uint64_t)(-1)) +//TODO Specify total number of available coins +//TODO ((uint64_t)(-1)) equals to 18446744073709551616 coins +//TODO or you can define number explicitly UINT64_C(858986905600000000) +#define MONEY_SUPPLY #define EMISSION_SPEED_FACTOR (18) #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 -// COIN - number of smallest units in one coin -#define COIN ((uint64_t)100000000) // pow(10, 8) -#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) - +//TODO Define number of smallest units in one coin +#define COIN +//TODO Define default fee for transactions +#define DEFAULT_FEE +//TODO There are options to tune CryptoNote's difficulty retargeting function. +//TODO We recommend not to change it. #define DIFFICULTY_TARGET 120 // seconds #define DIFFICULTY_WINDOW 720 // blocks -#define DIFFICULTY_LAG 15 // !!! +#define DIFFICULTY_LAG 15 #define DIFFICULTY_CUT 60 // timestamps to cut after sorting #define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG @@ -41,16 +47,17 @@ #define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1 -#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN DIFFICULTY_TARGET //just alias +#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN DIFFICULTY_TARGET #define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing #define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading #define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block - -#define P2P_DEFAULT_PORT 8080 -#define RPC_DEFAULT_PORT 8081 +//TODO This port will be used by the daemon to establish connections with p2p network +#define P2P_DEFAULT_PORT +//TODO This port will be used by the daemon to interact with simlewallet +#define RPC_DEFAULT_PORT #define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 #define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 @@ -69,10 +76,12 @@ #define ALLOW_DEBUG_COMMANDS -#define CRYPTONOTE_NAME "bytecoin" +//TODO Put here the name of your currency +#define CRYPTONOTE_NAME +#define CRYPTONOTE_BLOCKS_FILENAME "blocks.dat" +#define CRYPTONOTE_BLOCKINDEXES_FILENAME "blockindexes.dat" +#define CRYPTONOTE_BLOCKSCACHE_FILENAME "blockscache.dat" #define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" -#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "blockchain.bin" -#define CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME "blockchain.bin.tmp" #define P2P_NET_DATA_FILENAME "p2pstate.bin" #define MINER_CONFIG_FILE_NAME "miner_conf.json" diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/cryptonote_core/SwappedMap.cpp index 44ed6ef26d..d0b7aca109 100755 --- a/src/cryptonote_core/SwappedMap.cpp +++ b/src/cryptonote_core/SwappedMap.cpp @@ -1 +1,5 @@ +// Copyright (c) 2011-2014 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "SwappedMap.h" diff --git a/src/cryptonote_core/SwappedMap.h b/src/cryptonote_core/SwappedMap.h index f03bf5c949..d1faf60714 100755 --- a/src/cryptonote_core/SwappedMap.h +++ b/src/cryptonote_core/SwappedMap.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2014 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once #include diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/cryptonote_core/SwappedVector.cpp index df4e51568e..5c59a20220 100755 --- a/src/cryptonote_core/SwappedVector.cpp +++ b/src/cryptonote_core/SwappedVector.cpp @@ -1 +1,5 @@ +// Copyright (c) 2011-2014 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "SwappedVector.h" diff --git a/src/cryptonote_core/SwappedVector.h b/src/cryptonote_core/SwappedVector.h index 7285c7fe82..027fad58f8 100755 --- a/src/cryptonote_core/SwappedVector.h +++ b/src/cryptonote_core/SwappedVector.h @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2014 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once #include diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index ba39b9b775..aa2efe80d0 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index 8b525da978..1e4dcddd40 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_core/account_boost_serialization.h index 9cc36d14a6..261cea512d 100644 --- a/src/cryptonote_core/account_boost_serialization.h +++ b/src/cryptonote_core/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp old mode 100644 new mode 100755 index f569e5062c..214d77331e --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -17,18 +17,6 @@ #include "file_io_utils.h" #include "common/boost_serialization_helper.h" -//namespace { -// std::string hashHex(const crypto::hash& hash) { -// std::string result; -// for (size_t i = 0; i < crypto::HASH_SIZE; ++i) { -// result += "0123456789ABCDEF"[static_cast(hash.data[i]) >> 4]; -// result += "0123456789ABCDEF"[static_cast(hash.data[i]) & 15]; -// } -// -// return result; -// } -//} - namespace { std::string appendPath(const std::string& path, const std::string& fileName) { std::string result = path; @@ -61,8 +49,6 @@ namespace cryptonote { uint64_t m_keeper_block_height; size_t m_blob_size; std::vector m_global_output_indexes; - - template void serialize(archive_t & ar, unsigned int version); }; struct block_extended_info { @@ -71,37 +57,7 @@ namespace cryptonote { size_t block_cumulative_size; difficulty_type cumulative_difficulty; uint64_t already_generated_coins; - - template void serialize(archive_t & ar, unsigned int version); }; - - template void transaction_chain_entry::serialize(archive_t & ar, unsigned int version) { - ar & tx; - ar & m_keeper_block_height; - ar & m_blob_size; - ar & m_global_output_indexes; - } - - template void block_extended_info::serialize(archive_t & ar, unsigned int version) { - ar & bl; - ar & height; - ar & cumulative_difficulty; - ar & block_cumulative_size; - ar & already_generated_coins; - } -} - -template void cryptonote::blockchain_storage::Transaction::serialize(Archive& archive, unsigned int version) { - archive & tx; -} - -template void cryptonote::blockchain_storage::Block::serialize(Archive& archive, unsigned int version) { - archive & bl; - archive & height; - archive & block_cumulative_size; - archive & cumulative_difficulty; - archive & already_generated_coins; - archive & transactions; } template void cryptonote::blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { @@ -109,87 +65,6 @@ template void cryptonote::blockchain_storage::TransactionIndex::s archive & transaction; } -namespace cryptonote { -#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 13 - - template void blockchain_storage::serialize(archive_t & ar, const unsigned int version) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if (version < 12) { - LOG_PRINT_L0("Detected blockchain of unsupported version, migration is not possible."); - return; - } - - LOG_PRINT_L0("Blockchain of previous version detected, migrating. This may take several minutes, please be patient..."); - - std::vector blocks; - ar & blocks; - - { - std::unordered_map blocks_index; - ar & blocks_index; - } - - std::unordered_map transactions; - ar & transactions; - - { - std::unordered_set spent_keys; - ar & spent_keys; - } - - { - std::unordered_map alternative_chains; - ar & alternative_chains; - } - - { - std::map>> outputs; - ar & outputs; - } - - { - std::unordered_map invalid_blocks; - ar & invalid_blocks; - } - - size_t current_block_cumul_sz_limit; - ar & current_block_cumul_sz_limit; - LOG_PRINT_L0("Old blockchain storage:" << ENDL << - "blocks: " << blocks.size() << ENDL << - "transactions: " << transactions.size() << ENDL << - "current_block_cumul_sz_limit: " << current_block_cumul_sz_limit); - - Block block; - Transaction transaction; - for (uint32_t b = 0; b < blocks.size(); ++b) { - block.bl = blocks[b].bl; - block.height = b; - block.block_cumulative_size = blocks[b].block_cumulative_size; - block.cumulative_difficulty = blocks[b].cumulative_difficulty; - block.already_generated_coins = blocks[b].already_generated_coins; - block.transactions.resize(1 + blocks[b].bl.tx_hashes.size()); - block.transactions[0].tx = blocks[b].bl.miner_tx; - TransactionIndex transactionIndex = { b, 0 }; - pushTransaction(block, get_transaction_hash(blocks[b].bl.miner_tx), transactionIndex); - for (uint32_t t = 0; t < blocks[b].bl.tx_hashes.size(); ++t) { - block.transactions[1 + t].tx = transactions[blocks[b].bl.tx_hashes[t]].tx; - transactionIndex.transaction = 1 + t; - pushTransaction(block, blocks[b].bl.tx_hashes[t], transactionIndex); - } - - pushBlock(block); - } - - update_next_comulative_size_limit(); - if (m_current_block_cumul_sz_limit != current_block_cumul_sz_limit) { - LOG_ERROR("Migration was unsuccessful."); - } - } -} - -BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) - - bool blockchain_storage::have_tx(const crypto::hash &id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_transactionMap.find(id) != m_transactionMap.end(); @@ -209,19 +84,16 @@ bool blockchain_storage::init(const std::string& config_folder) { CRITICAL_REGION_LOCAL(m_blockchain_lock); m_config_folder = config_folder; LOG_PRINT_L0("Loading blockchain..."); - if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) { + if (!m_blocks.open(appendPath(config_folder, CRYPTONOTE_BLOCKS_FILENAME), appendPath(config_folder, CRYPTONOTE_BLOCKINDEXES_FILENAME), 1024)) { return false; } if (m_blocks.empty()) { - const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME); - if (!tools::unserialize_obj_from_file(*this, filename)) { - LOG_PRINT_L0("Can't load blockchain storage from file."); - } + LOG_PRINT_L0("Can't load blockchain storage from file."); } else { bool rebuild = true; try { - std::ifstream file(appendPath(config_folder, "blockscache.dat"), std::ios::binary); + std::ifstream file(appendPath(config_folder, CRYPTONOTE_BLOCKSCACHE_FILENAME), std::ios::binary); boost::archive::binary_iarchive archive(file); crypto::hash lastBlockHash; archive & lastBlockHash; @@ -282,7 +154,7 @@ bool blockchain_storage::init(const std::string& config_folder) { bool blockchain_storage::store_blockchain() { try { - std::ofstream file(appendPath(m_config_folder, "blockscache.dat"), std::ios::binary); + std::ofstream file(appendPath(m_config_folder, CRYPTONOTE_BLOCKSCACHE_FILENAME), std::ios::binary); boost::archive::binary_oarchive archive(file); crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); archive & lastBlockHash; @@ -290,73 +162,9 @@ bool blockchain_storage::store_blockchain() { archive & m_transactionMap; archive & m_spent_keys; archive & m_outputs; + LOG_PRINT_L0("Saved blockchain cache."); } catch (std::exception& e) { - LOG_ERROR("Failed to save blockchain, " << e.what()); - } - - //{ - // std::ofstream file(appendPath(m_config_folder, "blockscache2.dat"), std::ios::binary); - - // crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); - // file.write(reinterpret_cast(&lastBlockHash), sizeof(lastBlockHash)); - - // uint32_t blockMapSize = m_blockMap.size(); - // file.write(reinterpret_cast(&blockMapSize), sizeof(blockMapSize)); - // for (auto& i : m_blockMap) { - // crypto::hash blockHash = i.first; - // file.write(reinterpret_cast(&blockHash), sizeof(blockHash)); - - // uint32_t blockIndex = i.second; - // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); - // } - - // uint32_t transactionMapSize = m_transactionMap.size(); - // file.write(reinterpret_cast(&transactionMapSize), sizeof(transactionMapSize)); - // for (auto& i : m_transactionMap) { - // crypto::hash transactionHash = i.first; - // file.write(reinterpret_cast(&transactionHash), sizeof(transactionHash)); - - // uint32_t blockIndex = i.second.block; - // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); - - // uint32_t transactionIndex = i.second.transaction; - // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); - // } - - // uint32_t spentKeysSize = m_spent_keys.size(); - // file.write(reinterpret_cast(&spentKeysSize), sizeof(spentKeysSize)); - // for (auto& i : m_spent_keys) { - // crypto::key_image key = i; - // file.write(reinterpret_cast(&key), sizeof(key)); - // } - - // uint32_t outputsSize = m_outputs.size(); - // file.write(reinterpret_cast(&outputsSize), sizeof(outputsSize)); - // for (auto& i : m_outputs) { - // uint32_t indexesSize = i.second.size(); - // file.write(reinterpret_cast(&indexesSize), sizeof(indexesSize)); - // for (auto& j : i.second) { - // uint32_t blockIndex = j.first.block; - // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); - - // uint32_t transactionIndex = j.first.transaction; - // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); - - // uint32_t outputIndex = j.second; - // file.write(reinterpret_cast(&outputIndex), sizeof(outputIndex)); - // } - // } - //} - - { - //std::ofstream file(appendPath(m_config_folder, "blockscache3.dat"), std::ios::binary); - //binary_archive archive(file); - //crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); - //do_serialize(archive, lastBlockHash); - //do_serialize(archive, m_blockMap); - //do_serialize(archive, m_transactionMap); - //do_serialize(archive, m_spent_keys); - //do_serialize(archive, m_outputs); + LOG_ERROR("Failed to save blockchain cache, " << e.what()); } return true; diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h old mode 100644 new mode 100755 index 1cbb3dabdc..4890c65659 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -105,8 +105,6 @@ namespace cryptonote { transaction tx; std::vector m_global_output_indexes; - template void serialize(archive_t & ar, unsigned int version); - BEGIN_SERIALIZE_OBJECT() FIELD(tx) FIELD(m_global_output_indexes) @@ -121,8 +119,6 @@ namespace cryptonote { uint64_t already_generated_coins; std::vector transactions; - template void serialize(Archive& archive, unsigned int version); - BEGIN_SERIALIZE_OBJECT() FIELD(bl) VARINT_FIELD(height) diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 3f0c94b390..77e4c7261c 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index 33a2d29864..532e23cfeb 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h index 1bc055d913..8db10bf937 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_core/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 60e20e24be..854375808e 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -12,22 +12,6 @@ namespace cryptonote { inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) { - ADD_CHECKPOINT(79000, "cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02"); - ADD_CHECKPOINT(140000, "993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772"); - ADD_CHECKPOINT(200000, "a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073"); - ADD_CHECKPOINT(230580, "32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103"); - ADD_CHECKPOINT(260000, "f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947"); - ADD_CHECKPOINT(300000, "8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff"); - ADD_CHECKPOINT(390285, "e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7"); - ADD_CHECKPOINT(417000, "2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f"); - ADD_CHECKPOINT(427193, "00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311"); - ADD_CHECKPOINT(453537, "d17de6916c5aa6ffcae575309c80b0f8fdcd0a84b5fa8e41a841897d4b5a4e97"); - ADD_CHECKPOINT(462250, "13468d210a5ec884cf839f0259f247ccf3efef0414ac45172033d32c739beb3e"); - ADD_CHECKPOINT(468000, "251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1"); - ADD_CHECKPOINT(480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"); - ADD_CHECKPOINT(484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"); - ADD_CHECKPOINT(506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"); - return true; } } diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_core/connection_context.h index 53cac992d0..f334b14cc7 100644 --- a/src/cryptonote_core/connection_context.h +++ b/src/cryptonote_core/connection_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 007e62bbdf..8546329cbc 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index 348384c8fc..0fef9a895f 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index 27251ef54e..270eff6511 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 80c4978444..a0a9cd9155 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2e46c99bd8..a5a06fa097 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 056aa0743c..e4a2764dbf 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 5b6fadf25d..8592fed488 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -622,15 +622,20 @@ namespace cryptonote //genesis block bl = boost::value_initialized(); - + //TODO Uncomment this code block on teh first network lounch. It will generate and print you genesis block's hash. + //TODO Then you must copy it and put to genesis_coinbase_tx_hex variable + /* account_public_address ac = boost::value_initialized(); std::vector sz; construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis blobdata txb = tx_to_blob(bl.miner_tx); std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + std::cout << "Genesis block hex: " << hex_to_represent << endl; + */ //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same - std::string genesis_coinbase_tx_hex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + //TODO After you obtain hash of the genesis block put it here and recompile sources! + std::string genesis_coinbase_tx_hex = ""; blobdata tx_bl; string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 662e160ea3..01939536c5 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_core/cryptonote_stat_info.h index 9d406748cd..0cdee0f7b4 100644 --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/cryptonote_core/cryptonote_stat_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp index 3dde6ad6c1..2774c50582 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_core/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h index aad1e27ca1..d38580e3f0 100644 --- a/src/cryptonote_core/difficulty.h +++ b/src/cryptonote_core/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index fdc1545746..8d06c90d85 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index 61c063ddf8..3d38fb4b4a 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h index 37a04a41e7..bf96482361 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_core/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 24e5752ad6..d88faf0c5c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 26d273aa70..8f3deb791b 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index 210cc2d5b1..09f40aecfb 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h index 23111f0487..80c6d4c2fd 100644 --- a/src/cryptonote_protocol/blobdatatype.h +++ b/src/cryptonote_protocol/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index d646a7f6fe..f1f25d4b52 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 80538677cd..8129ab0b80 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2584f10971..793a9cfbe7 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index f1ced5050d..d2b71247cd 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 329f44e7fe..22871b1565 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index 81d34b473c..7b701e7e5a 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp index a1c1ad5c68..40ca514a07 100644 --- a/src/miner/simpleminer.cpp +++ b/src/miner/simpleminer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/miner/simpleminer.h b/src/miner/simpleminer.h index 803ea12fba..43e1596d26 100644 --- a/src/miner/simpleminer.h +++ b/src/miner/simpleminer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/miner/simpleminer_protocol_defs.h b/src/miner/simpleminer_protocol_defs.h index 06b6a90535..bc48f52e97 100644 --- a/src/miner/simpleminer_protocol_defs.h +++ b/src/miner/simpleminer_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/miner/target_helper.h b/src/miner/target_helper.h index 5ac6eed143..08827e135a 100644 --- a/src/miner/target_helper.h +++ b/src/miner/target_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 5a03b049f1..74b9490f10 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 5c60be0af6..89ae6ddd1c 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -193,16 +193,8 @@ namespace nodetool template bool node_server::init(const boost::program_options::variables_map& vm) { - ADD_HARDCODED_SEED_NODE("seed.bytecoin.org:8080"); - ADD_HARDCODED_SEED_NODE("85.25.201.95:8080"); - ADD_HARDCODED_SEED_NODE("85.25.196.145:8080"); - ADD_HARDCODED_SEED_NODE("85.25.196.146:8080"); - ADD_HARDCODED_SEED_NODE("85.25.196.144:8080"); - ADD_HARDCODED_SEED_NODE("5.199.168.138:8080"); - ADD_HARDCODED_SEED_NODE("62.75.236.152:8080"); - ADD_HARDCODED_SEED_NODE("85.25.194.245:8080"); - ADD_HARDCODED_SEED_NODE("95.211.224.160:8080"); - ADD_HARDCODED_SEED_NODE("144.76.200.44:8080"); + //TODO add seed for your network + //ADD_HARDCODED_SEED_NODE("your_seed_ip.com:8080"); bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); @@ -376,7 +368,7 @@ namespace nodetool return; } - if(rsp.node_data.network_id != BYTECOIN_NETWORK) + if(rsp.node_data.network_id != CRYPTONOTE_NETWORK) { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); return; @@ -784,7 +776,7 @@ namespace nodetool node_data.my_port = m_external_port ? m_external_port : m_listenning_port; else node_data.my_port = 0; - node_data.network_id = BYTECOIN_NETWORK; + node_data.network_id = CRYPTONOTE_NETWORK; return true; } //----------------------------------------------------------------------------------- @@ -1004,7 +996,7 @@ namespace nodetool template int node_server::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { - if(arg.node_data.network_id != BYTECOIN_NETWORK) + if(arg.node_data.network_id != CRYPTONOTE_NETWORK) { LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 17ae20cbe8..24e7e47e73 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index ea541fcbc2..dcd6fed5c9 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 23a253f252..b8e3e7da92 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/p2p/p2p_networks.h b/src/p2p/p2p_networks.h index 3fa4090066..1d7bcbf78c 100644 --- a/src/p2p/p2p_networks.h +++ b/src/p2p/p2p_networks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -6,5 +6,5 @@ namespace nodetool { - const static boost::uuids::uuid BYTECOIN_NETWORK = { { 0x11 ,0x10, 0x01, 0x11 , 0x11, 0x00 , 0x01, 0x01, 0x10, 0x11, 0x00, 0x12, 0x10, 0x11, 0x01, 0x10} }; //Bender's nightmare + const static boost::uuids::uuid CRYPTONOTE_NETWORK = { { 0x11 ,0x10, 0x01, 0x11 , 0x11, 0x00 , 0x01, 0x01, 0x10, 0x11, 0x00, 0x12, 0x10, 0x11, 0x01, 0x10} }; } diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index fdf784f495..89387ba1fb 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index 1cc72f811c..35684fcd5e 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 07f4a1cb9d..86540fe2e7 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 7b14e741a8..65655a8a95 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index d67ebfdd7a..ed671d6f83 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 10785f8aba..c732603538 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index f28e45c0c1..72c2e5ad97 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index 00bb1741d6..3e2eae6d49 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 6e683e6298..db587b37b0 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index 08baee0169..0c8fde6ba8 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 49ad74d412..902c582129 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 24f5c11a81..1ce0a8199c 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 7024fdc03f..92175a3324 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/string.h b/src/serialization/string.h index 437cf1c778..780ab7fcd6 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 3b92fde202..b8235ac295 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/serialization/vector.h b/src/serialization/vector.h index d074723525..8363be4638 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/simplewallet/password_container.cpp b/src/simplewallet/password_container.cpp index 0b9dc1cdff..66db560cde 100644 --- a/src/simplewallet/password_container.cpp +++ b/src/simplewallet/password_container.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/simplewallet/password_container.h b/src/simplewallet/password_container.h index 2e99d9a62f..5c7133aef5 100644 --- a/src/simplewallet/password_container.h +++ b/src/simplewallet/password_container.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4c6a736025..e6dceb378b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2500553f09..0d2b4d5cac 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 111b761171..83ab6e7c87 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d2016f6961..ee053891d7 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 7189ca9f28..747005828d 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f1766c3b44..502dabd212 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index db49df574c..f7ed43de89 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index b99d92ca28..92846722ed 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 7fa536dac9..11ec50c3ff 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index caf4bf8377..f0a6eb2e15 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index d5be53f1da..aaf8abee18 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 7950738a67..204fcafcde 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index 506d7466c6..b0b69e0546 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 57e1471330..91c4b5afdf 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index fe5b859a7e..df9f99c358 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index bd598852e6..e820bb120c 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h index df7efea82b..8289bc306d 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 8e9a2c1dbf..abfd848800 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 05e84e6cff..449d526e20 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 124800db78..222c6d6ea0 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 3d47edeef6..09aedb78f0 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index f79420e691..cee347723f 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index e6f287d74f..8eff20f628 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index d56f78121e..383ea768f6 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 9a007ce261..9852ae1427 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index e43cc2ed34..1f1c79310a 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 5c619e97c9..319cabb317 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index 0e00ada323..f093cbfc65 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index bfdaf38f00..d62b95a053 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h index 1d74f68026..7f1c8bcd5a 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 635c41503e..56c7caab1e 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index 460192802d..3e3dbac2ec 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 93cb68c95d..98e8bcefc7 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index e1939c0492..13748d3120 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 5622acd03f..5b11f5390f 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index b21e2e03a4..cbd517a1b1 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index 80f658228c..26acb75448 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index cbc5879ca1..ffb11210c4 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index 6981f2be95..b952890632 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 5cb5c9ceba..9fb48c326c 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/crypto/random.c b/tests/crypto/random.c index 05d7cf5ff7..d14dfeddf7 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp index 23b8b8335a..d87cf540f8 100644 --- a/tests/daemon_tests/transfers.cpp +++ b/tests/daemon_tests/transfers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 2af2f79769..bc30607d0e 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index 330e7ef2fe..e1cbac3e6d 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 4c6d5b07f4..b96f4fc0c2 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h index 8f9061b92e..a8786db9f3 100644 --- a/tests/functional_tests/transactions_flow_test.h +++ b/tests/functional_tests/transactions_flow_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp index de2ef541f2..de88d5c9f4 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h index 64438488cc..0cc2224aef 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.h +++ b/tests/functional_tests/transactions_generation_from_blockchain.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index 208bb04a80..0234695cc2 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 6c898c9fc7..e730cfee56 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/io.h b/tests/io.h index 64f2bae77d..c59e7ef971 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 9c153a2999..bf5a506c15 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 3d95698354..4a29b0f73c 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 52895c9dd3..6444062e11 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/check_ring_signature.h b/tests/performance_tests/check_ring_signature.h index dafa172e37..e9ef3d0abb 100644 --- a/tests/performance_tests/check_ring_signature.h +++ b/tests/performance_tests/check_ring_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index ec001326e3..16691154a8 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 1e70059410..ceb344148e 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index ec9d64e092..dfa0118975 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index bd915846da..4315a7d5bd 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h index c2fce687d5..dd7f7b63dc 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index f6a00ffe35..f8d26038ef 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index 1c072f4430..cc41838c6c 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 4a33c25fbe..cde341f6ff 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 72ee2ca6ca..b6a0a195dc 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index c919bdf2d2..3105fa8805 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index c7efe93f13..5fdfd2bd76 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index 45ca5b27b1..28a3408b50 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 1c1fe655ba..c26091299f 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 87afd5d0e3..f2d73a3b85 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 2381d36861..1d9c9c7150 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/chacha8.cpp b/tests/unit_tests/chacha8.cpp index e3eeae5cfc..20889443d5 100644 --- a/tests/unit_tests/chacha8.cpp +++ b/tests/unit_tests/chacha8.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index 0d7bd4de99..06cabb741d 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp index 319b39e2f0..447a265d57 100644 --- a/tests/unit_tests/decompose_amount_into_digits.cpp +++ b/tests/unit_tests/decompose_amount_into_digits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index 1e606163d2..27578f6a0a 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index da22d9d2d4..b08c9da4c2 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index dd4d2e4190..cf6ccc7f4b 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 65bf247d8b..e9976b9945 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 291e4f191e..713bfe5a1c 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index 7d0b640880..039f3e8602 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 616509d82c..3f73d8fdcd 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index 4dd8775914..9041604a0a 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index bd58ca7537..2d1bf5c283 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index 18c7bc78b2..0389e1e2e4 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index 9eebb7ed99..b9c858c9ea 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. From 69a770823ef788f9f989e7d1bc137178b9efaef9 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Sat, 21 Jun 2014 15:09:13 +0100 Subject: [PATCH 03/59] Fix bytecoin folder creation on startup --- src/cryptonote_core/blockchain_storage.cpp | 5 +++++ src/version.h.in | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index f569e5062c..5fb23fa6d7 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -207,6 +207,11 @@ uint64_t blockchain_storage::get_current_blockchain_height() { bool blockchain_storage::init(const std::string& config_folder) { CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (!tools::create_directories_if_necessary(config_folder)) { + LOG_ERROR("Failed to create data directory: " << m_config_folder); + return false; + } + m_config_folder = config_folder; LOG_PRINT_L0("Loading blockchain..."); if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) { diff --git a/src/version.h.in b/src/version.h.in index e412253e8d..052704167d 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" #define PROJECT_VERSION "0.8.10" -#define PROJECT_VERSION_BUILD_NO "65" +#define PROJECT_VERSION_BUILD_NO "71" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" From 2f44547e0dc4a5ffd3da14caa6b605d422d62573 Mon Sep 17 00:00:00 2001 From: Albert Werner Date: Tue, 24 Jun 2014 23:30:02 +0400 Subject: [PATCH 04/59] Minor fixies in comments --- src/cryptonote_config.h | 7 +++++-- src/cryptonote_core/blockchain_storage.cpp | 5 +++++ src/cryptonote_core/cryptonote_format_utils.cpp | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 10ed771328..25fd1caad9 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -25,18 +25,21 @@ #define MONEY_SUPPLY #define EMISSION_SPEED_FACTOR (18) +//TODO Define number of blocks for block size median calculation #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 -#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 +//TODO Define number of digits +#define CRYPTONOTE_DISPLAY_DECIMAL_POINT //TODO Define number of smallest units in one coin #define COIN //TODO Define default fee for transactions #define DEFAULT_FEE +//TODO Define preferred block's target time +#define DIFFICULTY_TARGET 120 // seconds //TODO There are options to tune CryptoNote's difficulty retargeting function. //TODO We recommend not to change it. -#define DIFFICULTY_TARGET 120 // seconds #define DIFFICULTY_WINDOW 720 // blocks #define DIFFICULTY_LAG 15 #define DIFFICULTY_CUT 60 // timestamps to cut after sorting diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 214d77331e..b8d86f7f3c 100755 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -82,6 +82,11 @@ uint64_t blockchain_storage::get_current_blockchain_height() { bool blockchain_storage::init(const std::string& config_folder) { CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { + LOG_ERROR("Failed to create data directory: " << m_config_folder); + return false; + } + m_config_folder = config_folder; LOG_PRINT_L0("Loading blockchain..."); if (!m_blocks.open(appendPath(config_folder, CRYPTONOTE_BLOCKS_FILENAME), appendPath(config_folder, CRYPTONOTE_BLOCKINDEXES_FILENAME), 1024)) { diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 8592fed488..4456b9033f 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -622,7 +622,7 @@ namespace cryptonote //genesis block bl = boost::value_initialized(); - //TODO Uncomment this code block on teh first network lounch. It will generate and print you genesis block's hash. + //TODO Uncomment this code block on the first network launch. It will generate and print you genesis block's hash. //TODO Then you must copy it and put to genesis_coinbase_tx_hex variable /* account_public_address ac = boost::value_initialized(); @@ -630,10 +630,10 @@ namespace cryptonote construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis blobdata txb = tx_to_blob(bl.miner_tx); std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - std::cout << "Genesis block hex: " << hex_to_represent << endl; + std::cout << "Genesis coinbase tx hex: " << hex_to_represent << std::endl; */ - //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same + //hard code coinbase tx in genesis block, because "true" generating tx use random, but genesis should be always the same //TODO After you obtain hash of the genesis block put it here and recompile sources! std::string genesis_coinbase_tx_hex = ""; From 9a0a44a816b091132e9e49fa2674f1fd1193ad1d Mon Sep 17 00:00:00 2001 From: Albert Werner Date: Tue, 24 Jun 2014 23:35:25 +0400 Subject: [PATCH 05/59] Update README, add forking guide --- README | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/README b/README index 87a89c5871..051cbea0be 100644 --- a/README +++ b/README @@ -1,4 +1,163 @@ --= Building CryptoNote =- +== Preparation == + +1. Create an account on github.com + +2. Fork https://github.com/cryptonotefoundation/cryptonote repository + +3. Buy one or two Ubuntu-based dedicated servers (at least 8Gb of RAM) for seed nodes. + + +== First step. Give a name to your coin == + +Good name must be unique. Check uniqueness with google and mapofcoins.com or any other similar service. + +Name must be specified twice: + +- in file src/cryptonote_config.h - CRYPTONOTE_NAME macro + +Example: +#define CRYPTONOTE_NAME "furiouscoin" + +- in CMakeList.txt file - set_property(TARGET daemon PROPERTY OUTPUT_NAME "YOURCOINNAMEd") + +Example: +set_property(TARGET daemon PROPERTY OUTPUT_NAME "furiouscoind") + +Note: You should also change a repository name. + + +== Second step. Emission logic == + +1. Total money supply (src/cryptonote_config.h) + +Total amount of coins to be emitted. Most of CryptoNote based coins use (uint64_t)(-1) (equals to 18446744073709551616). You can define number explicitly (for example UINT64_C(858986905600000000)). + +Example: +#define MONEY_SUPPLY ((uint64_t)(-1)) + +2. Emission curve (src/cryptonote_config.h) + +Be default CryptoNote provides emission formula with slight decrease of block reward with each block. This is different from Bitcoin where block reward halves every 4 years. + +EMISSION_SPEED_FACTOR macro defines emission curve slope. This parameter is required to calulate block reward. + +Example: +#define EMISSION_SPEED_FACTOR (18) + +3. Difficulty target (src/cryptonote_config.h) + +Difficulty target is an ideal time period between blocks. In case an average time between blocks becomes less than difficulty target, the difficulty increases. Difficulty target is measured in seconds. + +Difficulty target directly influences several aspects of coin's behavior: + +- transaction confirmation speed: the longer the time between the blocks is, the slower transaction confirmation is +- emission speed: the longer the time between the blocks is the slower the emission process is +- orphan rate: chains with very fast blocks have greater orphan rate + +For most coins difficulty target is 60 or 120 seconds. + +Example: +#define DIFFICULTY_TARGET 120 + +4. Block reward formula + +In case you are not satisfied with CryptoNote default implementation of block reward logic you can also change it. The implementation is in src/cryptonote_core/cryptonote_basic_impl.cpp: + +bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) + +This function has two parts: + +- basic block reward calculation + +uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR; + +- big block penalty calculation + +This is the way CryptoNote protects the block chain from transaction flooding attacks and preserves opportunities for organic network growth at the same time. + +Only the first part of this function is directly related to the emission logic. You can change it the way you want. See MonetaVerde and DuckNote as the examples where this function is modified. + + +== Third step. Networking == + +1. Default ports for P2P and RPC networking (src/cryptonote_config.h) + +P2P port is used by daemons to talk to each other through P2P protocol. +RPC port is used by wallet and other programs to talk to daemon. + +It's better to choose ports that aren't used by other software or coins. See known TCP ports lists: + +- http://www.speedguide.net/ports.php +- http://www.networksorcery.com/enp/protocol/ip/ports00000.htm +- http://keir.net/portlist.html + +Example: + +#define P2P_DEFAULT_PORT 17236 +#define RPC_DEFAULT_PORT 18236 + + +2. Network identifier (src/p2p/p2p_networks.h) + +This identifier is used in network packages in order not to mix two different cryptocoin networks. Change all the bytes to random values for your network: + +const static boost::uuids::uuid CRYPTONOTE_NETWORK = { { 0xA1 ,0x1A, 0xA1, 0x1A , 0xA1, 0x0A , 0xA1, 0x0A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA1, 0x1A} }; + + +3. Seed nodes (src/p2p/net_node.inl) + +Add ip addresses of your seed nodes to the beginning of "node_server::init(const boost::program_options::variables_map& vm)" function. + +Example: +ADD_HARDCODED_SEED_NODE("111.11.11.11:17236"); +ADD_HARDCODED_SEED_NODE("222.22.22.22:17236"); + + +== Fourth step. Transaction fee and related parameters == + +1. Default transaction fee (src/cryptonote_config.h) + +Zero default fee can lead to transaction flooding. Transactions cheaper than the default transaction fee wouldn't be accepted by daemons. 100000 value for DEFAULT_FEE is usually enough. + +Example: +#define DEFAULT_FEE 100000 + + +2. Penalty free block size (src/cryptonote_config.h) + +CryptoNote protects chain from tx flooding by reducing block reward for blocks larger than the median block size. However, this rule applies for blocks larger than CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE bytes. + +Example: +#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 20000 + + +== Fifth step. Genesis block == + +Now your coin code is almost ready. You need to generate the coinbase transaction for your genesis block. Uncomment the following code in "generate_genesis_block(block& bl)" function in src/cryptonote_core/cryptonote_format_utils.cpp: + + /* + account_public_address ac = boost::value_initialized(); + std::vector sz; + construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis + blobdata txb = tx_to_blob(bl.miner_tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + std::cout << "Genesis block hex: " << hex_tx_represent << std::endl; + */ + +Compile and run the daemon. As soon as it prints line starting with "Genesis coinbase tx hex: " copy the hex representation of the coinbase tx and paste it into generate_genesis_block function: + +Genesis coinbase tx hex: 013c01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121012cfb466857c5762cdd97e242b08b7e239ab35807f5024d4785a33d9ebdba68b0 + +-> + +std::string genesis_coinbase_tx_hex = "013c01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121012cfb466857c5762cdd97e242b08b7e239ab35807f5024d4785a33d9ebdba68b0"; + +Comment the coinbase tx generation code back and recompile everything again. + +You coin code is ready now. Make an announcement for the potential users and enjoy! + + +== Building CryptoNote == On *nix: From 76bb193b0556aaaa3ffa5d48db7bd28eceb36300 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Wed, 25 Jun 2014 18:21:42 +0100 Subject: [PATCH 06/59] Minimum fix increase, high level api --- CMakeLists.txt | 4 +- ReleaseNotes.txt | 8 + include/INode.h | 45 +- include/IWallet.h | 8 +- src/CMakeLists.txt | 6 +- src/common/ObserverManager.h | 116 +++ src/common/base58.cpp | 2 +- src/crypto/chacha8.h | 12 +- src/crypto/hash-ops.h | 6 +- src/crypto/hash.h | 22 +- src/crypto/slow-hash.c | 352 ++++----- src/crypto/slow-hash.cpp | 60 ++ src/crypto/slow-hash.inl | 184 +++++ src/cryptonote_config.h | 5 +- src/cryptonote_core/blockchain_storage.cpp | 100 +-- src/cryptonote_core/blockchain_storage.h | 16 +- src/cryptonote_core/cryptonote_core.cpp | 89 +-- src/cryptonote_core/cryptonote_core.h | 8 +- .../cryptonote_format_utils.cpp | 12 +- src/cryptonote_core/cryptonote_format_utils.h | 4 +- src/cryptonote_core/miner.cpp | 21 +- src/cryptonote_core/miner.h | 12 +- src/cryptonote_core/tx_pool.cpp | 277 +++---- src/cryptonote_core/tx_pool.h | 113 +-- src/daemon/daemon.cpp | 2 +- src/miner/simpleminer.cpp | 5 +- src/node_rpc_proxy/InitState.h | 83 ++ src/node_rpc_proxy/NodeErrors.cpp | 13 + src/node_rpc_proxy/NodeErrors.h | 56 ++ src/node_rpc_proxy/NodeRpcProxy.cpp | 255 ++++++ src/node_rpc_proxy/NodeRpcProxy.h | 76 ++ src/rpc/core_rpc_server.cpp | 16 +- src/rpc/core_rpc_server_commands_defs.h | 10 +- src/simplewallet/simplewallet.cpp | 23 +- src/version.h.in | 4 +- src/wallet/Wallet.cpp | 491 ++++++++++++ src/wallet/Wallet.h | 124 +++ src/wallet/WalletAsyncContextCounter.cpp | 29 + src/wallet/WalletAsyncContextCounter.h | 30 + src/wallet/WalletErrors.cpp | 13 + src/wallet/WalletErrors.h | 73 ++ src/wallet/WalletEvent.h | 113 +++ src/wallet/WalletRequest.h | 95 +++ src/wallet/WalletSendTransactionContext.h | 40 + src/wallet/WalletSerialization.h | 80 ++ src/wallet/WalletSynchronizationContext.h | 42 + src/wallet/WalletSynchronizer.cpp | 475 +++++++++++ src/wallet/WalletSynchronizer.h | 83 ++ src/wallet/WalletTransactionSender.cpp | 299 +++++++ src/wallet/WalletTransactionSender.h | 55 ++ src/wallet/WalletTransferDetails.cpp | 169 ++++ src/wallet/WalletTransferDetails.h | 82 ++ src/wallet/WalletTxSendingState.cpp | 29 + src/wallet/WalletTxSendingState.h | 33 + src/wallet/WalletUnconfirmedTransactions.cpp | 45 ++ src/wallet/WalletUnconfirmedTransactions.h | 59 ++ src/wallet/WalletUserTransactionsCache.cpp | 166 ++++ src/wallet/WalletUserTransactionsCache.h | 77 ++ src/wallet/WalletUtils.h | 19 + src/wallet/wallet2.cpp | 23 +- src/wallet/wallet2.h | 1 + src/wallet/wallet_rpc_server.cpp | 45 +- src/wallet/wallet_rpc_server_commans_defs.h | 6 +- tests/CMakeLists.txt | 18 +- tests/core_proxy/core_proxy.cpp | 14 +- tests/core_proxy/core_proxy.h | 4 +- tests/core_tests/chain_switch_1.cpp | 6 +- tests/core_tests/chaingen.cpp | 6 +- tests/core_tests/chaingen.h | 4 +- tests/core_tests/double_spend.h | 2 +- .../transactions_flow_test.cpp | 6 +- tests/hash/main.cpp | 16 +- tests/io.h | 2 +- .../node_rpc_proxy_test.cpp | 132 ++++ tests/performance_tests/cn_slow_hash.h | 3 +- tests/performance_tests/main.cpp | 1 + tests/unit_tests/INodeStubs.cpp | 128 +++ tests/unit_tests/INodeStubs.h | 59 ++ tests/unit_tests/TestBlockchainGenerator.cpp | 113 +++ tests/unit_tests/TestBlockchainGenerator.h | 32 + tests/unit_tests/test_format_utils.cpp | 4 +- tests/unit_tests/test_wallet.cpp | 744 ++++++++++++++++++ 82 files changed, 5407 insertions(+), 708 deletions(-) create mode 100644 src/common/ObserverManager.h create mode 100644 src/crypto/slow-hash.cpp create mode 100644 src/crypto/slow-hash.inl create mode 100644 src/node_rpc_proxy/InitState.h create mode 100644 src/node_rpc_proxy/NodeErrors.cpp create mode 100644 src/node_rpc_proxy/NodeErrors.h create mode 100644 src/node_rpc_proxy/NodeRpcProxy.cpp create mode 100644 src/node_rpc_proxy/NodeRpcProxy.h create mode 100644 src/wallet/Wallet.cpp create mode 100644 src/wallet/Wallet.h create mode 100644 src/wallet/WalletAsyncContextCounter.cpp create mode 100644 src/wallet/WalletAsyncContextCounter.h create mode 100644 src/wallet/WalletErrors.cpp create mode 100644 src/wallet/WalletErrors.h create mode 100644 src/wallet/WalletEvent.h create mode 100644 src/wallet/WalletRequest.h create mode 100644 src/wallet/WalletSendTransactionContext.h create mode 100644 src/wallet/WalletSerialization.h create mode 100644 src/wallet/WalletSynchronizationContext.h create mode 100644 src/wallet/WalletSynchronizer.cpp create mode 100644 src/wallet/WalletSynchronizer.h create mode 100644 src/wallet/WalletTransactionSender.cpp create mode 100644 src/wallet/WalletTransactionSender.h create mode 100644 src/wallet/WalletTransferDetails.cpp create mode 100644 src/wallet/WalletTransferDetails.h create mode 100644 src/wallet/WalletTxSendingState.cpp create mode 100644 src/wallet/WalletTxSendingState.h create mode 100644 src/wallet/WalletUnconfirmedTransactions.cpp create mode 100644 src/wallet/WalletUnconfirmedTransactions.h create mode 100644 src/wallet/WalletUserTransactionsCache.cpp create mode 100644 src/wallet/WalletUserTransactionsCache.h create mode 100644 src/wallet/WalletUtils.h create mode 100644 tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp create mode 100644 tests/unit_tests/INodeStubs.cpp create mode 100644 tests/unit_tests/INodeStubs.h create mode 100644 tests/unit_tests/TestBlockchainGenerator.cpp create mode 100644 tests/unit_tests/TestBlockchainGenerator.h create mode 100644 tests/unit_tests/test_wallet.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 26c78e84ca..6dd3638aa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CMAKE_CONFIGURATION_TYPES "Debug;Release") enable_testing() -include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") +include_directories(include src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") if(APPLE) include_directories(SYSTEM /usr/include/malloc) endif() @@ -15,7 +15,7 @@ endif() set(STATIC ${MSVC} CACHE BOOL "Link libraries statically") 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__") + 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 /D_VARIADIC_MAX=8 /FIinline_c.h /D__SSE4_1__") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760") if(STATIC) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 90c74c3b73..69836befed 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,11 @@ +Release notes 0.8.11 + +- Increased minimum transaction fee +- Transaction pool optimizations +- High level API implementation +- CryptoNight hash function optimization +- Improvements for wallet JSON RPC API + Release notes 0.8.10 - Optimized blockchain storage memory usage diff --git a/include/INode.h b/include/INode.h index 624d4ac4c0..4ee6ce3b17 100644 --- a/include/INode.h +++ b/include/INode.h @@ -5,33 +5,54 @@ #pragma once #include +#include #include +#include + +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/core_rpc_server_commands_defs.h" namespace CryptoNote { class INodeObserver { public: - virtual void initCompleted(std::error_code result) {} - + virtual ~INodeObserver() {} virtual void peerCountUpdated(size_t count) {} - virtual void lastLocalBlockHeightUpdated(uint64_t height) {} + virtual void localBlockchainUpdated(uint64_t height) {} virtual void lastKnownBlockHeightUpdated(uint64_t height) {} +}; - virtual void blockchainReorganized(uint64_t height) {} +struct OutEntry { + uint64_t outGlobalIndex; + crypto::public_key outKey; +}; + +struct OutsForAmount { + uint64_t amount; + std::vector outs; }; class INode { public: - virtual ~INode() = 0; - virtual void addObserver(INodeObserver* observer) = 0; - virtual void removeObserver(INodeObserver* observer) = 0; + typedef std::function Callback; + + virtual ~INode() {} + virtual bool addObserver(INodeObserver* observer) = 0; + virtual bool removeObserver(INodeObserver* observer) = 0; + + virtual void init(const Callback& callback) = 0; + virtual bool shutdown() = 0; - virtual void init() = 0; - virtual void shutdown() = 0; + virtual size_t getPeerCount() const = 0; + virtual uint64_t getLastLocalBlockHeight() const = 0; + virtual uint64_t getLastKnownBlockHeight() const = 0; - virtual size_t getPeerCount() = 0; - virtual uint64_t getLastLocalBlockHeight() = 0; - virtual uint64_t getLastKnownBlockHeight() = 0; + virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) = 0; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; }; } diff --git a/include/IWallet.h b/include/IWallet.h index 32d5cad121..a89538d8bc 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -17,7 +17,7 @@ namespace CryptoNote { typedef size_t TransactionId; typedef size_t TransferId; -typedef std::array TransacitonHash; +typedef std::array TransactionHash; struct Transfer { std::string address; @@ -33,7 +33,7 @@ struct Transaction { size_t transferCount; int64_t totalAmount; uint64_t fee; - TransacitonHash hash; + TransactionHash hash; bool isCoinbase; uint64_t blockHeight; uint64_t timestamp; @@ -44,7 +44,7 @@ class IWalletObserver { public: virtual void initCompleted(std::error_code result) {} virtual void saveCompleted(std::error_code result) {} - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) {} virtual void actualBalanceUpdated(uint64_t actualBalance) {} virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} virtual void externalTransactionCreated(TransactionId transactionId) {} @@ -54,7 +54,7 @@ class IWalletObserver { class IWallet { public: - virtual ~IWallet() = 0; + virtual ~IWallet() {} ; virtual void addObserver(IWalletObserver* observer) = 0; virtual void removeObserver(IWalletObserver* observer) = 0; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f890fcda91..0a44632079 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ file(GLOB_RECURSE SIMPLEWALLET simplewallet/*) file(GLOB_RECURSE CONN_TOOL connectivity_tool/*) file(GLOB_RECURSE WALLET wallet/*) file(GLOB_RECURSE MINER miner/*) +file(GLOB_RECURSE NODE_RPC_PROXY node_rpc_proxy/*) source_group(common FILES ${COMMON}) source_group(crypto FILES ${CRYPTO}) @@ -23,6 +24,7 @@ source_group(simplewallet FILES ${SIMPLEWALLET}) source_group(connectivity-tool FILES ${CONN_TOOL}) source_group(wallet FILES ${WALLET}) source_group(simpleminer FILES ${MINER}) +source_group(node_rpc_proxy FILES ${NODE_RPC_PROXY}) add_library(common ${COMMON}) add_library(crypto ${CRYPTO}) @@ -37,10 +39,12 @@ add_library(rpc ${RPC}) add_library(wallet ${WALLET}) add_executable(simplewallet ${SIMPLEWALLET} ) target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) +add_library(node_rpc_proxy ${NODE_RPC_PROXY}) + add_dependencies(daemon version) add_dependencies(rpc version) add_dependencies(simplewallet version) -set_property(TARGET common crypto cryptonote_core rpc wallet PROPERTY FOLDER "libs") +set_property(TARGET common crypto cryptonote_core rpc wallet node_rpc_proxy PROPERTY FOLDER "libs") set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog") set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind") diff --git a/src/common/ObserverManager.h b/src/common/ObserverManager.h new file mode 100644 index 0000000000..02b59d9f11 --- /dev/null +++ b/src/common/ObserverManager.h @@ -0,0 +1,116 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +namespace tools { + +template +class ObserverManager { +public: + bool add(T* observer) { + std::unique_lock lock(m_observersMutex); + auto it = std::find(m_observers.begin(), m_observers.end(), observer); + if (m_observers.end() == it) { + m_observers.push_back(observer); + return true; + } else { + return false; + } + } + + bool remove(T* observer) { + std::unique_lock lock(m_observersMutex); + auto it = std::find(m_observers.begin(), m_observers.end(), observer); + if (m_observers.end() == it) { + return false; + } else { + m_observers.erase(it); + return true; + } + } + + void clear() { + std::unique_lock lock(m_observersMutex); + m_observers.clear(); + } + +#if defined(_MSC_VER) + template + void notify(F notification) { + std::vector observersCopy; + { + std::unique_lock lock(m_observersMutex); + observersCopy = m_observers; + } + + for (T* observer : observersCopy) { + (observer->*notification)(); + } + } + + template + void notify(F notification, const Arg0& arg0) { + std::vector observersCopy; + { + std::unique_lock lock(m_observersMutex); + observersCopy = m_observers; + } + + for (T* observer : observersCopy) { + (observer->*notification)(arg0); + } + } + + template + void notify(F notification, const Arg0& arg0, const Arg1& arg1) { + std::vector observersCopy; + { + std::unique_lock lock(m_observersMutex); + observersCopy = m_observers; + } + + for (T* observer : observersCopy) { + (observer->*notification)(arg0, arg1); + } + } + + template + void notify(F notification, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2) { + std::vector observersCopy; + { + std::unique_lock lock(m_observersMutex); + observersCopy = m_observers; + } + + for (T* observer : observersCopy) { + (observer->*notification)(arg0, arg1, arg2); + } + } + +#else + + template + void notify(F notification, Args... args) { + std::vector observersCopy; + { + std::unique_lock lock(m_observersMutex); + observersCopy = m_observers; + } + + for (T* observer : observersCopy) { + (observer->*notification)(args...); + } + } +#endif + +private: + std::vector m_observers; + std::mutex m_observersMutex; +}; + +} diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 454c0db676..575279aa55 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -110,7 +110,7 @@ namespace tools void encode_block(const char* block, size_t size, char* res) { - assert(1 <= size && size <= sizeof(full_block_size)); + assert(1 <= size && size <= full_block_size); uint64_t num = uint_8be_to_64(reinterpret_cast(block), size); int i = static_cast(encoded_block_sizes[size]) - 1; diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index e4fe467992..72f84e6274 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -44,13 +44,13 @@ namespace crypto { chacha8(data, length, reinterpret_cast(&key), reinterpret_cast(&iv), cipher); } - inline void generate_chacha8_key(std::string password, chacha8_key& key) { + inline void generate_chacha8_key(crypto::cn_context &context, std::string password, chacha8_key& key) { static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); - char pwd_hash[HASH_SIZE]; - crypto::cn_slow_hash(password.data(), password.size(), pwd_hash); - memcpy(&key, pwd_hash, sizeof(key)); - memset(pwd_hash, 0, sizeof(pwd_hash)); + crypto::hash pwd_hash; + crypto::cn_slow_hash(context, password.data(), password.size(), pwd_hash); + memcpy(&key, &pwd_hash, sizeof(key)); + memset(&pwd_hash, 0, sizeof(pwd_hash)); } } -#endif +#endif \ No newline at end of file diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 9e6c821efb..c3aacb60b4 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -49,11 +49,13 @@ void hash_process(union hash_state *state, const uint8_t *buf, size_t count); enum { HASH_SIZE = 32, - HASH_DATA_AREA = 136 + HASH_DATA_AREA = 136, + SLOW_HASH_CONTEXT_SIZE = 2097552 }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash); + +void cn_slow_hash_f(void *, const void *, size_t, void *); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index fb65494b67..bac2d266cd 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -37,8 +37,24 @@ namespace crypto { return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) { - cn_slow_hash(data, length, reinterpret_cast(&hash)); + class cn_context { + public: + + cn_context(); + ~cn_context(); +#if !defined(_MSC_VER) || _MSC_VER >= 1800 + cn_context(const cn_context &) = delete; + void operator=(const cn_context &) = delete; +#endif + + private: + + void *data; + friend inline void cn_slow_hash(cn_context &, const void *, std::size_t, hash &); + }; + + inline void cn_slow_hash(cn_context &context, const void *data, std::size_t length, hash &hash) { + (*cn_slow_hash_f)(context.data, data, length, reinterpret_cast(&hash)); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { @@ -47,4 +63,4 @@ namespace crypto { } -CRYPTO_MAKE_HASHABLE(hash) +CRYPTO_MAKE_HASHABLE(hash) \ No newline at end of file diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 468ba644be..44c6a1f0a7 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -2,257 +2,179 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include #include #include +#include +#include + +#if defined(_MSC_VER) +#include +#else +#include +#endif + #include "aesb.h" +#include "initializer.h" #include "common/int-util.h" #include "hash-ops.h" #include "oaes_lib.h" -#include +void (*cn_slow_hash_fp)(void *, const void *, size_t, void *); -#if defined(_MSC_VER) || defined(__INTEL_COMPILER) -#include -#define STATIC -#define INLINE __inline -#if !defined(RDATA_ALIGN16) -#define RDATA_ALIGN16 __declspec(align(16)) -#endif +void cn_slow_hash_f(void * a, const void * b, size_t c, void * d){ +(*cn_slow_hash_fp)(a, b, c, d); +} + +#if defined(__GNUC__) +#define likely(x) (__builtin_expect(!!(x), 1)) +#define unlikely(x) (__builtin_expect(!!(x), 0)) #else -#include -#define STATIC static -#define INLINE inline -#if !defined(RDATA_ALIGN16) -#define RDATA_ALIGN16 __attribute__ ((aligned(16))) +#define likely(x) (x) +#define unlikely(x) (x) +#define __attribute__(x) #endif + +#if defined(_MSC_VER) +#define restrict #endif -#define MEMORY (1 << 21) // 2MB scratchpad +#define MEMORY (1 << 21) /* 2 MiB */ #define ITER (1 << 20) #define AES_BLOCK_SIZE 16 -#define AES_KEY_SIZE 32 +#define AES_KEY_SIZE 32 /*16*/ #define INIT_SIZE_BLK 8 -#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) - -#define U64(x) ((uint64_t *) (x)) -#define R128(x) ((__m128i *) (x)) +#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) // 128 #pragma pack(push, 1) -union cn_slow_hash_state -{ - union hash_state hs; - struct - { - uint8_t k[64]; - uint8_t init[INIT_SIZE_BYTE]; - }; +union cn_slow_hash_state { + union hash_state hs; + struct { + uint8_t k[64]; + uint8_t init[INIT_SIZE_BYTE]; + }; }; #pragma pack(pop) -#if defined(_MSC_VER) || defined(__INTEL_COMPILER) -#define cpuid(info,x) __cpuidex(info,x,0) -#else -void cpuid(int CPUInfo[4], int InfoType) -{ - __asm__ __volatile__ - ( - "cpuid": - "=a" (CPUInfo[0]), - "=b" (CPUInfo[1]), - "=c" (CPUInfo[2]), - "=d" (CPUInfo[3]) : - "a" (InfoType), "c" (0) - ); -} +#if defined(_MSC_VER) +#define ALIGNED_DATA(x) __declspec(align(x)) +#define ALIGNED_DECL(t, x) ALIGNED_DATA(x) t +#elif defined(__GNUC__) +#define ALIGNED_DATA(x) __attribute__((aligned(x))) +#define ALIGNED_DECL(t, x) t ALIGNED_DATA(x) #endif -STATIC INLINE void mul(const uint8_t *a, const uint8_t *b, uint8_t *res) -{ - uint64_t a0, b0; - uint64_t hi, lo; - - a0 = U64(a)[0]; - b0 = U64(b)[0]; - lo = mul128(a0, b0, &hi); - U64(res)[0] = hi; - U64(res)[1] = lo; -} - -STATIC INLINE void sum_half_blocks(uint8_t *a, const uint8_t *b) -{ - uint64_t a0, a1, b0, b1; - a0 = U64(a)[0]; - a1 = U64(a)[1]; - b0 = U64(b)[0]; - b1 = U64(b)[1]; - a0 += b0; - a1 += b1; - U64(a)[0] = a0; - U64(a)[1] = a1; -} +struct cn_ctx { + ALIGNED_DECL(uint8_t long_state[MEMORY], 16); + ALIGNED_DECL(union cn_slow_hash_state state, 16); + ALIGNED_DECL(uint8_t text[INIT_SIZE_BYTE], 16); + ALIGNED_DECL(uint64_t a[AES_BLOCK_SIZE >> 3], 16); + ALIGNED_DECL(uint64_t b[AES_BLOCK_SIZE >> 3], 16); + ALIGNED_DECL(uint8_t c[AES_BLOCK_SIZE], 16); + oaes_ctx* aes_ctx; +}; -STATIC INLINE void swap_blocks(uint8_t *a, uint8_t *b) -{ - uint64_t t[2]; - U64(t)[0] = U64(a)[0]; - U64(t)[1] = U64(a)[1]; - U64(a)[0] = U64(b)[0]; - U64(a)[1] = U64(b)[1]; - U64(b)[0] = U64(t)[0]; - U64(b)[1] = U64(t)[1]; -} +static_assert(sizeof(struct cn_ctx) == SLOW_HASH_CONTEXT_SIZE, "Invalid structure size"); -STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b) +static inline void ExpandAESKey256_sub1(__m128i *tmp1, __m128i *tmp2) { - U64(a)[0] ^= U64(b)[0]; - U64(a)[1] ^= U64(b)[1]; + __m128i tmp4; + *tmp2 = _mm_shuffle_epi32(*tmp2, 0xFF); + tmp4 = _mm_slli_si128(*tmp1, 0x04); + *tmp1 = _mm_xor_si128(*tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp1 = _mm_xor_si128(*tmp1, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp1 = _mm_xor_si128(*tmp1, tmp4); + *tmp1 = _mm_xor_si128(*tmp1, *tmp2); } -STATIC INLINE int check_aes_hw(void) +static inline void ExpandAESKey256_sub2(__m128i *tmp1, __m128i *tmp3) { - int cpuid_results[4]; - static int supported = -1; - - if(supported >= 0) - return supported; - - cpuid(cpuid_results,1); - return supported = cpuid_results[2] & (1 << 25); + __m128i tmp2, tmp4; + + tmp4 = _mm_aeskeygenassist_si128(*tmp1, 0x00); + tmp2 = _mm_shuffle_epi32(tmp4, 0xAA); + tmp4 = _mm_slli_si128(*tmp3, 0x04); + *tmp3 = _mm_xor_si128(*tmp3, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp3 = _mm_xor_si128(*tmp3, tmp4); + tmp4 = _mm_slli_si128(tmp4, 0x04); + *tmp3 = _mm_xor_si128(*tmp3, tmp4); + *tmp3 = _mm_xor_si128(*tmp3, tmp2); } -STATIC INLINE void aesni_pseudo_round(const uint8_t *in, uint8_t *out, - const uint8_t *expandedKey) +// Special thanks to Intel for helping me +// with ExpandAESKey256() and its subroutines +static inline void ExpandAESKey256(uint8_t *keybuf) { - __m128i *k = R128(expandedKey); - __m128i d; - - d = _mm_loadu_si128(R128(in)); - d = _mm_aesenc_si128(d, *R128(&k[0])); - d = _mm_aesenc_si128(d, *R128(&k[1])); - d = _mm_aesenc_si128(d, *R128(&k[2])); - d = _mm_aesenc_si128(d, *R128(&k[3])); - d = _mm_aesenc_si128(d, *R128(&k[4])); - d = _mm_aesenc_si128(d, *R128(&k[5])); - d = _mm_aesenc_si128(d, *R128(&k[6])); - d = _mm_aesenc_si128(d, *R128(&k[7])); - d = _mm_aesenc_si128(d, *R128(&k[8])); - d = _mm_aesenc_si128(d, *R128(&k[9])); - _mm_storeu_si128((R128(out)), d); + __m128i tmp1, tmp2, tmp3, *keys; + + keys = (__m128i *)keybuf; + + tmp1 = _mm_load_si128((__m128i *)keybuf); + tmp3 = _mm_load_si128((__m128i *)(keybuf+0x10)); + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x01); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[2] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[3] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x02); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[4] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[5] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x04); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[6] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[7] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x08); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[8] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[9] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x10); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[10] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[11] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x20); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[12] = tmp1; + ExpandAESKey256_sub2(&tmp1, &tmp3); + keys[13] = tmp3; + + tmp2 = _mm_aeskeygenassist_si128(tmp3, 0x40); + ExpandAESKey256_sub1(&tmp1, &tmp2); + keys[14] = tmp1; } -void cn_slow_hash(const void *data, size_t length, char *hash) +static void (*const extra_hashes[4])(const void *, size_t, char *) = { - uint8_t long_state[MEMORY]; - uint8_t text[INIT_SIZE_BYTE]; - uint8_t a[AES_BLOCK_SIZE]; - uint8_t b[AES_BLOCK_SIZE]; - uint8_t d[AES_BLOCK_SIZE]; - RDATA_ALIGN16 uint8_t expandedKey[256]; - - union cn_slow_hash_state state; - - size_t i, j; - uint8_t *p = NULL; - oaes_ctx *aes_ctx; - - int useAes = check_aes_hw(); - static void (*const extra_hashes[4])(const void *, size_t, char *) = - { - hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein - }; - - hash_process(&state.hs, data, length); - memcpy(text, state.init, INIT_SIZE_BYTE); - - aes_ctx = (oaes_ctx *) oaes_alloc(); - oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE); - - // use aligned data - memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); - - if(useAes) - { - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) - { - for(j = 0; j < INIT_SIZE_BLK; j++) - aesni_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey); - memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); - } - } - else - { - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) - { - for(j = 0; j < INIT_SIZE_BLK; j++) - aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey); - - memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); - } - } - - U64(a)[0] = U64(&state.k[0])[0] ^ U64(&state.k[32])[0]; - U64(a)[1] = U64(&state.k[0])[1] ^ U64(&state.k[32])[1]; - U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0]; - U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1]; - - for(i = 0; i < ITER / 2; i++) - { - #define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE) - #define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) - - // Iteration 1 - p = &long_state[state_index(a)]; - - if(useAes) - _mm_storeu_si128(R128(p), _mm_aesenc_si128(_mm_loadu_si128(R128(p)), _mm_loadu_si128(R128(a)))); - else - aesb_single_round(p, p, a); - - xor_blocks(b, p); - swap_blocks(b, p); - swap_blocks(a, b); - - // 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); - } + hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein +}; - memcpy(text, state.init, INIT_SIZE_BYTE); - oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE); - memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); - if(useAes) - { - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) - { - for(j = 0; j < INIT_SIZE_BLK; j++) - { - xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); - aesni_pseudo_round(&text[j * AES_BLOCK_SIZE], &text[j * AES_BLOCK_SIZE], expandedKey); - } - } - } - else - { - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) - { - for(j = 0; j < INIT_SIZE_BLK; j++) - { - xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); - aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey); - } - } - } +#include "slow-hash.inl" +#define AESNI +#include "slow-hash.inl" - oaes_free((OAES_CTX **) &aes_ctx); - memcpy(state.init, text, INIT_SIZE_BYTE); - hash_permutation(&state.hs); - extra_hashes[state.hs.b[0] & 3](&state, 200, hash); +INITIALIZER(detect_aes) { + int ecx; +#if defined(_MSC_VER) + int cpuinfo[4]; + __cpuid(cpuinfo, 1); + ecx = cpuinfo[2]; +#else + int a, b, d; + __cpuid(1, a, b, ecx, d); +#endif + cn_slow_hash_fp = (ecx & (1 << 25)) ? &cn_slow_hash_aesni : &cn_slow_hash_noaesni; } diff --git a/src/crypto/slow-hash.cpp b/src/crypto/slow-hash.cpp new file mode 100644 index 0000000000..7141359ad4 --- /dev/null +++ b/src/crypto/slow-hash.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "hash.h" + +#if defined(WIN32) +#include +#else +#include +#endif + +using std::bad_alloc; + +namespace crypto { + + enum { + MAP_SIZE = SLOW_HASH_CONTEXT_SIZE + ((-SLOW_HASH_CONTEXT_SIZE) & 0xfff) + }; + +#if defined(WIN32) + + cn_context::cn_context() { + data = VirtualAlloc(nullptr, MAP_SIZE, MEM_COMMIT, PAGE_READWRITE); + if (data == nullptr) { + throw bad_alloc(); + } + } + + cn_context::~cn_context() { + if (!VirtualFree(data, 0, MEM_RELEASE)) { + throw bad_alloc(); + } + } + +#else + + cn_context::cn_context() { +#if !defined(__APPLE__) + data = mmap(nullptr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0); +#else + data = mmap(nullptr, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +#endif + if (data == MAP_FAILED) { + throw bad_alloc(); + } + mlock(data, MAP_SIZE); + } + + cn_context::~cn_context() { + if (munmap(data, MAP_SIZE) != 0) { + throw bad_alloc(); + } + } + +#endif + +} diff --git a/src/crypto/slow-hash.inl b/src/crypto/slow-hash.inl new file mode 100644 index 0000000000..0d9c3573b4 --- /dev/null +++ b/src/crypto/slow-hash.inl @@ -0,0 +1,184 @@ +static void +#if defined(AESNI) +cn_slow_hash_aesni +#else +cn_slow_hash_noaesni +#endif +(void *restrict context, const void *restrict data, size_t length, void *restrict hash) +{ +#define ctx ((struct cn_ctx *) context) + uint8_t ExpandedKey[256]; + size_t i, j; + __m128i *longoutput, *expkey, *xmminput, b_x; + ALIGNED_DECL(uint64_t a[2], 16); + hash_process(&ctx->state.hs, (const uint8_t*) data, length); + + memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); +#if defined(AESNI) + memcpy(ExpandedKey, ctx->state.hs.b, AES_KEY_SIZE); + ExpandAESKey256(ExpandedKey); +#else + ctx->aes_ctx = oaes_alloc(); + oaes_key_import_data(ctx->aes_ctx, ctx->state.hs.b, AES_KEY_SIZE); + memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len); +#endif + + longoutput = (__m128i *) ctx->long_state; + expkey = (__m128i *) ExpandedKey; + xmminput = (__m128i *) ctx->text; + + //for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) + // aesni_parallel_noxor(&ctx->long_state[i], ctx->text, ExpandedKey); + + for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) + { +#if defined(AESNI) + for(j = 0; j < 10; j++) + { + xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); + xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); + xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]); + xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]); + xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]); + xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]); + xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]); + xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]); + } +#else + aesb_pseudo_round((uint8_t *) &xmminput[0], (uint8_t *) &xmminput[0], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[1], (uint8_t *) &xmminput[1], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[2], (uint8_t *) &xmminput[2], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[3], (uint8_t *) &xmminput[3], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[4], (uint8_t *) &xmminput[4], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[5], (uint8_t *) &xmminput[5], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[6], (uint8_t *) &xmminput[6], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[7], (uint8_t *) &xmminput[7], (uint8_t *) expkey); +#endif + _mm_store_si128(&(longoutput[(i >> 4)]), xmminput[0]); + _mm_store_si128(&(longoutput[(i >> 4) + 1]), xmminput[1]); + _mm_store_si128(&(longoutput[(i >> 4) + 2]), xmminput[2]); + _mm_store_si128(&(longoutput[(i >> 4) + 3]), xmminput[3]); + _mm_store_si128(&(longoutput[(i >> 4) + 4]), xmminput[4]); + _mm_store_si128(&(longoutput[(i >> 4) + 5]), xmminput[5]); + _mm_store_si128(&(longoutput[(i >> 4) + 6]), xmminput[6]); + _mm_store_si128(&(longoutput[(i >> 4) + 7]), xmminput[7]); + } + + for (i = 0; i < 2; i++) + { + ctx->a[i] = ((uint64_t *)ctx->state.k)[i] ^ ((uint64_t *)ctx->state.k)[i+4]; + ctx->b[i] = ((uint64_t *)ctx->state.k)[i+2] ^ ((uint64_t *)ctx->state.k)[i+6]; + } + + b_x = _mm_load_si128((__m128i *)ctx->b); + a[0] = ctx->a[0]; + a[1] = ctx->a[1]; + + for(i = 0; likely(i < 0x80000); i++) + { + __m128i c_x = _mm_load_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0]); + __m128i a_x = _mm_load_si128((__m128i *)a); + ALIGNED_DECL(uint64_t c[2], 16); + ALIGNED_DECL(uint64_t b[2], 16); + uint64_t *nextblock, *dst; + +#if defined(AESNI) + c_x = _mm_aesenc_si128(c_x, a_x); +#else + aesb_single_round((uint8_t *) &c_x, (uint8_t *) &c_x, (uint8_t *) &a_x); +#endif + + _mm_store_si128((__m128i *)c, c_x); + //__builtin_prefetch(&ctx->long_state[c[0] & 0x1FFFF0], 0, 1); + + b_x = _mm_xor_si128(b_x, c_x); + _mm_store_si128((__m128i *)&ctx->long_state[a[0] & 0x1FFFF0], b_x); + + nextblock = (uint64_t *)&ctx->long_state[c[0] & 0x1FFFF0]; + b[0] = nextblock[0]; + b[1] = nextblock[1]; + + { + uint64_t hi, lo; + // hi,lo = 64bit x 64bit multiply of c[0] and b[0] + +#if defined(__GNUC__) && defined(__x86_64__) + __asm__("mulq %3\n\t" + : "=d" (hi), + "=a" (lo) + : "%a" (c[0]), + "rm" (b[0]) + : "cc" ); +#else + lo = mul128(c[0], b[0], &hi); +#endif + + a[0] += hi; + a[1] += lo; + } + dst = (uint64_t *) &ctx->long_state[c[0] & 0x1FFFF0]; + dst[0] = a[0]; + dst[1] = a[1]; + + a[0] ^= b[0]; + a[1] ^= b[1]; + b_x = c_x; + //__builtin_prefetch(&ctx->long_state[a[0] & 0x1FFFF0], 0, 3); + } + + memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE); +#if defined(AESNI) + memcpy(ExpandedKey, &ctx->state.hs.b[32], AES_KEY_SIZE); + ExpandAESKey256(ExpandedKey); +#else + oaes_key_import_data(ctx->aes_ctx, &ctx->state.hs.b[32], AES_KEY_SIZE); + memcpy(ExpandedKey, ctx->aes_ctx->key->exp_data, ctx->aes_ctx->key->exp_data_len); +#endif + + //for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) + // aesni_parallel_xor(&ctx->text, ExpandedKey, &ctx->long_state[i]); + + for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) + { + xmminput[0] = _mm_xor_si128(longoutput[(i >> 4)], xmminput[0]); + xmminput[1] = _mm_xor_si128(longoutput[(i >> 4) + 1], xmminput[1]); + xmminput[2] = _mm_xor_si128(longoutput[(i >> 4) + 2], xmminput[2]); + xmminput[3] = _mm_xor_si128(longoutput[(i >> 4) + 3], xmminput[3]); + xmminput[4] = _mm_xor_si128(longoutput[(i >> 4) + 4], xmminput[4]); + xmminput[5] = _mm_xor_si128(longoutput[(i >> 4) + 5], xmminput[5]); + xmminput[6] = _mm_xor_si128(longoutput[(i >> 4) + 6], xmminput[6]); + xmminput[7] = _mm_xor_si128(longoutput[(i >> 4) + 7], xmminput[7]); + +#if defined(AESNI) + for(j = 0; j < 10; j++) + { + xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); + xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); + xmminput[2] = _mm_aesenc_si128(xmminput[2], expkey[j]); + xmminput[3] = _mm_aesenc_si128(xmminput[3], expkey[j]); + xmminput[4] = _mm_aesenc_si128(xmminput[4], expkey[j]); + xmminput[5] = _mm_aesenc_si128(xmminput[5], expkey[j]); + xmminput[6] = _mm_aesenc_si128(xmminput[6], expkey[j]); + xmminput[7] = _mm_aesenc_si128(xmminput[7], expkey[j]); + } +#else + aesb_pseudo_round((uint8_t *) &xmminput[0], (uint8_t *) &xmminput[0], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[1], (uint8_t *) &xmminput[1], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[2], (uint8_t *) &xmminput[2], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[3], (uint8_t *) &xmminput[3], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[4], (uint8_t *) &xmminput[4], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[5], (uint8_t *) &xmminput[5], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[6], (uint8_t *) &xmminput[6], (uint8_t *) expkey); + aesb_pseudo_round((uint8_t *) &xmminput[7], (uint8_t *) &xmminput[7], (uint8_t *) expkey); +#endif + + } + +#if !defined(AESNI) + oaes_free((OAES_CTX **) &ctx->aes_ctx); +#endif + + memcpy(ctx->state.init, ctx->text, INIT_SIZE_BYTE); + hash_permutation(&ctx->state.hs); + extra_hashes[ctx->state.hs.b[0] & 3](&ctx->state, 200, hash); +} diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 958639a0ec..224b06cd17 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -26,8 +26,9 @@ #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 // COIN - number of smallest units in one coin -#define COIN ((uint64_t)100000000) // pow(10, 8) -#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) +#define COIN ((uint64_t)100000000) // pow(10, 8) +#define MINIMUM_FEE ((uint64_t)1000000000) // pow(10, 9) +#define DEFAULT_DUST_THRESHOLD ((uint64_t)1000000) // pow(10, 6) #define DIFFICULTY_TARGET 120 // seconds diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 5fb23fa6d7..c1cd4ad074 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -205,73 +205,79 @@ uint64_t blockchain_storage::get_current_blockchain_height() { return m_blocks.size(); } -bool blockchain_storage::init(const std::string& config_folder) { +bool blockchain_storage::init(const std::string& config_folder, bool load_existing) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if (!tools::create_directories_if_necessary(config_folder)) { + if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { LOG_ERROR("Failed to create data directory: " << m_config_folder); return false; } m_config_folder = config_folder; - LOG_PRINT_L0("Loading blockchain..."); + if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) { return false; } - if (m_blocks.empty()) { - const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME); - if (!tools::unserialize_obj_from_file(*this, filename)) { - LOG_PRINT_L0("Can't load blockchain storage from file."); - } - } else { - bool rebuild = true; - try { - std::ifstream file(appendPath(config_folder, "blockscache.dat"), std::ios::binary); - boost::archive::binary_iarchive archive(file); - crypto::hash lastBlockHash; - archive & lastBlockHash; - if (lastBlockHash == get_block_hash(m_blocks.back().bl)) { - archive & m_blockMap; - archive & m_transactionMap; - archive & m_spent_keys; - archive & m_outputs; - rebuild = false; + if (load_existing) { + LOG_PRINT_L0("Loading blockchain..."); + + if (m_blocks.empty()) { + const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME); + if (!tools::unserialize_obj_from_file(*this, filename)) { + LOG_PRINT_L0("Can't load blockchain storage from file."); + } + } else { + bool rebuild = true; + try { + std::ifstream file(appendPath(config_folder, "blockscache.dat"), std::ios::binary); + boost::archive::binary_iarchive archive(file); + crypto::hash lastBlockHash; + archive & lastBlockHash; + if (lastBlockHash == get_block_hash(m_blocks.back().bl)) { + archive & m_blockMap; + archive & m_transactionMap; + archive & m_spent_keys; + archive & m_outputs; + rebuild = false; + } + } catch (std::exception&) { } - } catch (std::exception&) { - } - if (rebuild) { - LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures..."); - std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); - for (uint32_t b = 0; b < m_blocks.size(); ++b) { - const Block& block = m_blocks[b]; - crypto::hash blockHash = get_block_hash(block.bl); - m_blockMap.insert(std::make_pair(blockHash, b)); - for (uint16_t t = 0; t < block.transactions.size(); ++t) { - const Transaction& transaction = block.transactions[t]; - crypto::hash transactionHash = get_transaction_hash(transaction.tx); - TransactionIndex transactionIndex = { b, t }; - m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); - for (auto& i : transaction.tx.vin) { - if (i.type() == typeid(txin_to_key)) { - m_spent_keys.insert(::boost::get(i).k_image); + if (rebuild) { + LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures..."); + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + const Block& block = m_blocks[b]; + crypto::hash blockHash = get_block_hash(block.bl); + m_blockMap.insert(std::make_pair(blockHash, b)); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const Transaction& transaction = block.transactions[t]; + crypto::hash transactionHash = get_transaction_hash(transaction.tx); + TransactionIndex transactionIndex = { b, t }; + m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + for (auto& i : transaction.tx.vin) { + if (i.type() == typeid(txin_to_key)) { + m_spent_keys.insert(::boost::get(i).k_image); + } } - } - for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { - m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o)); + for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { + m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o)); + } } } - } - std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; - LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count()); + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count()); + } } + } else { + m_blocks.clear(); } if (m_blocks.empty()) { LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); - block bl = boost::value_initialized(); + block bl = ::boost::value_initialized(); block_verification_context bvc = boost::value_initialized(); generate_genesis_block(bl); add_new_block(bl, bvc); @@ -876,7 +882,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; - get_block_longhash(bei.bl, proof_of_work, bei.height); + get_block_longhash(m_cn_context, bei.bl, proof_of_work, bei.height); if (!check_hash(proof_of_work, current_diff)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work @@ -1497,7 +1503,7 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co return false; } } else { - proof_of_work = get_block_longhash(blockData, m_blocks.size()); + proof_of_work = get_block_longhash(m_cn_context, blockData, m_blocks.size()); if (!check_hash(proof_of_work, currentDifficulty)) { LOG_PRINT_L0("Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty); bvc.m_verifivation_failed = true; diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 1cbb3dabdc..a64779ff7f 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -17,8 +17,8 @@ namespace cryptonote { blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false) {}; - bool init() { return init(tools::get_default_data_dir()); } - bool init(const std::string& config_folder); + bool init() { return init(tools::get_default_data_dir(), true); } + bool init(const std::string& config_folder, bool load_existing); bool deinit(); void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } @@ -75,24 +75,17 @@ namespace cryptonote { } template - bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { + void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); for (const auto& tx_id : txs_ids) { auto it = m_transactionMap.find(tx_id); if (it == m_transactionMap.end()) { - transaction tx; - if (!m_tx_pool.get_transaction(tx_id, tx)) { - missed_txs.push_back(tx_id); - } else { - txs.push_back(tx); - } + missed_txs.push_back(tx_id); } else { txs.push_back(transactionByIndex(it->second).tx); } } - - return true; } //debug functions @@ -146,6 +139,7 @@ namespace cryptonote { tx_memory_pool& m_tx_pool; epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock + crypto::cn_context m_cn_context; key_images_container m_spent_keys; size_t m_current_block_cumul_sz_limit; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 2e46c99bd8..b2dd7ddcef 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -75,27 +75,9 @@ namespace cryptonote { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) + void core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) { - return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); - } - //----------------------------------------------------------------------------------------------- - bool core::get_transaction(const crypto::hash &h, transaction &tx) - { - std::vector ids; - ids.push_back(h); - std::list ltx; - std::list missing; - if (m_blockchain_storage.get_transactions(ids, ltx, missing)) - { - if (ltx.size() > 0) - { - tx = *ltx.begin(); - return true; - } - } - - return false; + m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- bool core::get_alternative_blocks(std::list& blocks) @@ -108,14 +90,14 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm) + bool core::init(const boost::program_options::variables_map& vm, bool load_existing) { bool r = handle_command_line(vm); r = m_mempool.init(m_config_folder); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); - r = m_blockchain_storage.init(m_config_folder); + r = m_blockchain_storage.init(m_config_folder, load_existing); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); r = m_miner.init(vm); @@ -288,17 +270,17 @@ namespace cryptonote // return m_blockchain_storage.get_outs(amount, pkeys); //} //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(const 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) - { - if(m_mempool.have_tx(tx_hash)) - { - LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); + bool core::add_new_tx(const 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) { + if (m_blockchain_storage.have_tx(tx_hash)) { + LOG_PRINT_L2("tx " << tx_hash << " is already in blockchain"); return true; } - if(m_blockchain_storage.have_tx(tx_hash)) - { - LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain"); + // It's not very good to lock on m_mempool here, because it's very hard to understand the order of locking + // tx_memory_pool::m_transactions_lock, blockchain_storage::m_blockchain_lock, and core::m_incoming_tx_lock + CRITICAL_REGION_LOCAL(m_mempool); + if (m_mempool.have_tx(tx_hash)) { + LOG_PRINT_L2("tx " << tx_hash << " is already in transaction pool"); return true; } @@ -421,30 +403,33 @@ namespace cryptonote } m_blockchain_storage.add_new_block(b, bvc); - if (bvc.m_added_to_main_chain) { - cryptonote_connection_context exclude_context = boost::value_initialized(); - NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); - if (missed_txs.size() > 0 && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { - LOG_PRINT_L0("Block found but reorganize happened after that, block will not be relayed"); - } else { - CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); - block_to_blob(b, arg.b.block); - BOOST_FOREACH(auto& tx, txs) arg.b.txs.push_back(t_serializable_object_to_blob(tx)); - m_pprotocol->relay_block(arg, exclude_context); - } - - if (update_miner_blocktemplate) { - update_miner_block_template(); - } + if (bvc.m_added_to_main_chain && update_miner_blocktemplate) { + update_miner_block_template(); } return true; } + + void core::notify_new_block(const block& b) { + cryptonote_connection_context exclude_context = boost::value_initialized(); + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + if (missed_txs.size() > 0 && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { + LOG_PRINT_L0("Block found but reorganize happened after that, block will not be relayed"); + } else { + if (txs.size() != b.tx_hashes.size() || missed_txs.size()) { + LOG_ERROR("cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); + return; + } + block_to_blob(b, arg.b.block); + BOOST_FOREACH(auto& tx, txs) arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + m_pprotocol->relay_block(arg, exclude_context); + } + } //----------------------------------------------------------------------------------------------- crypto::hash core::get_tail_id() { @@ -471,9 +456,9 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_pool_transactions(std::list& txs) + void core::get_pool_transactions(std::list& txs) { - return m_mempool.get_transactions(txs); + m_mempool.get_transactions(txs); } //----------------------------------------------------------------------------------------------- bool core::get_short_chain_history(std::list& ids) diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 056aa0743c..c74bb027ed 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -43,7 +43,7 @@ namespace cryptonote miner& get_miner(){return m_miner;} static void init_options(boost::program_options::options_description& desc); - bool init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map& vm, bool load_existing); bool set_genesis_block(const block& b); bool deinit(); uint64_t get_current_blockchain_height(); @@ -56,8 +56,7 @@ namespace cryptonote return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); } crypto::hash get_block_id_by_height(uint64_t height); - bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); - bool get_transaction(const crypto::hash &h, transaction &tx); + void get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); bool get_block_by_hash(const crypto::hash &h, block &blk); //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); @@ -67,7 +66,7 @@ namespace cryptonote void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_checkpoints(checkpoints&& chk_pts); - bool get_pool_transactions(std::list& txs); + void get_pool_transactions(std::list& txs); size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); //bool get_outs(uint64_t amount, std::list& pkeys); @@ -89,6 +88,7 @@ namespace cryptonote std::string print_pool(bool short_format); void print_blockchain_outs(const std::string& file); void on_synchronized(); + void notify_new_block(const block& b); private: bool add_new_tx(const 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); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 5b6fadf25d..da0e501e20 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -81,7 +81,7 @@ namespace cryptonote block_reward += fee; std::vector out_amounts; - decompose_amount_into_digits(block_reward, DEFAULT_FEE, + decompose_amount_into_digits(block_reward, DEFAULT_DUST_THRESHOLD, [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); @@ -640,16 +640,16 @@ namespace cryptonote bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; bl.timestamp = 0; bl.nonce = 70; - miner::find_nonce_for_given_block(bl, 1, 0); + //miner::find_nonce_for_given_block(bl, 1, 0); return true; } //--------------------------------------------------------------- - bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) + bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height) { blobdata bd; if(!get_block_hashing_blob(b, bd)) return false; - crypto::cn_slow_hash(bd.data(), bd.size(), res); + crypto::cn_slow_hash(context, bd.data(), bd.size(), res); return true; } //--------------------------------------------------------------- @@ -673,10 +673,10 @@ namespace cryptonote return res; } //--------------------------------------------------------------- - crypto::hash get_block_longhash(const block& b, uint64_t height) + crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height) { crypto::hash p = null_hash; - get_block_longhash(b, p, height); + get_block_longhash(context, b, p, height); return p; } //--------------------------------------------------------------- diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 662e160ea3..6153a0460f 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -77,8 +77,8 @@ namespace cryptonote bool get_block_hashing_blob(const block& b, blobdata& blob); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); - bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); - crypto::hash get_block_longhash(const block& b, uint64_t height); + bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height); + crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height); bool generate_genesis_block(block& bl); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index fdc1545746..88dad337a3 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -15,6 +15,8 @@ #include "cryptonote_format_utils.h" #include "file_io_utils.h" #include "common/command_line.h" +#include "crypto/hash.h" +#include "crypto/random.h" #include "string_coding.h" #include "storages/portable_storage_template_helper.h" @@ -42,9 +44,9 @@ namespace cryptonote m_thread_index(0), m_phandler(phandler), m_height(0), - m_pausers_count(0), + m_pausers_count(0), m_threads_total(0), - m_starter_nonce(0), + m_starter_nonce(0), m_last_hr_merge_time(0), m_hashes(0), m_do_print_hashrate(false), @@ -83,7 +85,7 @@ namespace cryptonote block bl = AUTO_VAL_INIT(bl); difficulty_type di = AUTO_VAL_INIT(di); uint64_t height = AUTO_VAL_INIT(height); - cryptonote::blobdata extra_nonce; + cryptonote::blobdata extra_nonce; if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) { extra_nonce = m_extra_messages[m_config.current_extra_message_index]; @@ -109,7 +111,7 @@ namespace cryptonote merge_hr(); return true; }); - + return true; } //----------------------------------------------------------------------------------------------------- @@ -192,7 +194,7 @@ namespace cryptonote { return !m_stop; } - //----------------------------------------------------------------------------------------------------- + //----------------------------------------------------------------------------------------------------- bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs) { m_mine_address = adr; @@ -252,12 +254,12 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) + bool miner::find_nonce_for_given_block(crypto::cn_context &context, block& bl, const difficulty_type& diffic, uint64_t height) { for(; bl.nonce != std::numeric_limits::max(); bl.nonce++) { crypto::hash h; - get_block_longhash(bl, h, height); + get_block_longhash(context, bl, h, height); if(check_hash(h, diffic)) { @@ -308,6 +310,7 @@ namespace cryptonote uint64_t height = 0; difficulty_type local_diff = 0; uint32_t local_template_ver = 0; + crypto::cn_context context; block b; while(!m_stop) { @@ -319,7 +322,7 @@ namespace cryptonote if(local_template_ver != m_template_no) { - + CRITICAL_REGION_BEGIN(m_template_lock); b = m_template; local_diff = m_diffic; @@ -338,7 +341,7 @@ namespace cryptonote b.nonce = nonce; crypto::hash h; - get_block_longhash(b, h, height); + get_block_longhash(context, b, h, height); if(!m_stop && check_hash(h, local_diff)) { diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index 61c063ddf8..28121e6654 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -2,7 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#pragma once +#pragma once #include #include @@ -27,7 +27,7 @@ namespace cryptonote /************************************************************************/ class miner { - public: + public: miner(i_miner_handler* phandler); ~miner(); bool init(const boost::program_options::variables_map& vm); @@ -42,7 +42,7 @@ namespace cryptonote bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height); + static bool find_nonce_for_given_block(crypto::cn_context &context, block& bl, const difficulty_type& diffic, uint64_t height); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -51,7 +51,7 @@ namespace cryptonote bool worker_thread(); bool request_block_template(); void merge_hr(); - + struct miner_config { uint64_t current_extra_message_index; @@ -69,7 +69,7 @@ namespace cryptonote std::atomic m_starter_nonce; difficulty_type m_diffic; uint64_t m_height; - volatile uint32_t m_thread_index; + volatile uint32_t m_thread_index; volatile uint32_t m_threads_total; std::atomic m_pausers_count; epee::critical_section m_miners_count_lock; @@ -82,7 +82,7 @@ namespace cryptonote epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; std::vector m_extra_messages; miner_config m_config; - std::string m_config_folder_path; + std::string m_config_folder_path; std::atomic m_last_hr_merge_time; std::atomic m_hashes; std::atomic m_current_hash_rate; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 24e5752ad6..7fab0d2e30 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -20,60 +20,53 @@ DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated -namespace cryptonote -{ +namespace cryptonote { //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) - { - + tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) - { - - - if(!check_inputs_types_supported(tx)) - { + bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) { + if (!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; } uint64_t inputs_amount = 0; - if(!get_inputs_money_amount(tx, inputs_amount)) - { + if (!get_inputs_money_amount(tx, inputs_amount)) { tvc.m_verifivation_failed = true; return false; } uint64_t outputs_amount = get_outs_money_amount(tx); - if(outputs_amount >= inputs_amount) - { - LOG_PRINT_L0("transaction use more money then it has: use " << outputs_amount << ", have " << inputs_amount); + if (outputs_amount >= inputs_amount) { + LOG_PRINT_L0("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount)); + tvc.m_verifivation_failed = true; + return false; + } + + uint64_t fee = inputs_amount - outputs_amount; + if (!kept_by_block && fee < MINIMUM_FEE) { + LOG_ERROR("transaction fee is not enought: " << print_money(fee) << ", minumim fee: " << print_money(MINIMUM_FEE)); tvc.m_verifivation_failed = true; return false; } //check key images for transaction if it is not kept by block - if(!kept_by_block) - { - if(have_tx_keyimges_as_spent(tx)) - { + if (!kept_by_block) { + if (have_tx_keyimges_as_spent(tx)) { LOG_ERROR("Transaction with id= "<< id << " used already spent key images"); tvc.m_verifivation_failed = true; return false; } } - crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); CRITICAL_REGION_LOCAL(m_transactions_lock); - if(!ch_inp_res) - { - if(kept_by_block) - { + if (!ch_inp_res) { + if (kept_by_block) { //anyway add this transaction to pool, because it related to block auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); @@ -85,14 +78,12 @@ namespace cryptonote txd_p.first->second.kept_by_block = kept_by_block; tvc.m_verifivation_impossible = true; tvc.m_added_to_pool = true; - }else - { + } else { LOG_PRINT_L0("tx used wrong inputs, rejected"); tvc.m_verifivation_failed = true; return false; } - }else - { + } else { //update transactions container auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); @@ -106,14 +97,14 @@ namespace cryptonote txd_p.first->second.last_failed_id = null_hash; tvc.m_added_to_pool = true; - if(txd_p.first->second.fee > 0) + if (txd_p.first->second.fee > 0) { tvc.m_should_be_relayed = true; + } } tvc.m_verifivation_failed = true; //update image_keys container, here should everything goes ok. - BOOST_FOREACH(const auto& in, tx.vin) - { + for (const auto& in : tx.vin) { 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: keeped_by_block=" << kept_by_block @@ -128,47 +119,43 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) - { + bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) { crypto::hash h = null_hash; size_t blob_size = 0; get_transaction_hash(tx, h, blob_size); return add_tx(tx, h, blob_size, tvc, keeped_by_block); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) - { + bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) { CRITICAL_REGION_LOCAL(m_transactions_lock); - BOOST_FOREACH(const txin_v& vi, tx.vin) - { + crypto::hash tx_id = 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)); - 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 = " << get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << std::endl + << "transaction id = " << tx_id); + std::unordered_set& key_image_set = it->second; + CHECK_AND_ASSERT_MES(!key_image_set.empty(), false, "empty key_image set, img=" << txin.k_image << std::endl + << "transaction id = " << tx_id); - auto it_in_set = key_image_set.find(get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(key_image_set.size(), false, "transaction id not found in key_image set, img=" << txin.k_image << ENDL - << "transaction id = " << get_transaction_hash(tx)); + auto it_in_set = key_image_set.find(tx_id); + CHECK_AND_ASSERT_MES(it_in_set != key_image_set.end(), false, "transaction id not found in key_image set, img=" << txin.k_image << std::endl + << "transaction id = " << tx_id); key_image_set.erase(it_in_set); - if(!key_image_set.size()) - { + if (key_image_set.empty()) { //it is now empty hash container for this key_image m_spent_key_images.erase(it); } - } return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee) - { + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee) { CRITICAL_REGION_LOCAL(m_transactions_lock); auto it = m_transactions.find(id); - if(it == m_transactions.end()) + if (it == m_transactions.end()) { return false; + } tx = it->second.tx; blob_size = it->second.blob_size; @@ -178,134 +165,111 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - size_t tx_memory_pool::get_transactions_count() - { + size_t tx_memory_pool::get_transactions_count() const { CRITICAL_REGION_LOCAL(m_transactions_lock); return m_transactions.size(); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::get_transactions(std::list& txs) - { + void tx_memory_pool::get_transactions(std::list& txs) const { CRITICAL_REGION_LOCAL(m_transactions_lock); - BOOST_FOREACH(const auto& tx_vt, m_transactions) + for (const auto& tx_vt : m_transactions) { txs.push_back(tx_vt.second.tx); - - return true; - } - //--------------------------------------------------------------------------------- - bool tx_memory_pool::get_transaction(const crypto::hash& id, transaction& tx) - { - CRITICAL_REGION_LOCAL(m_transactions_lock); - auto it = m_transactions.find(id); - if(it == m_transactions.end()) - return false; - tx = it->second.tx; - return true; + } } //--------------------------------------------------------------------------------- - bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) - { + bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) { return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) - { + bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) { return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx(const crypto::hash &id) - { + bool tx_memory_pool::have_tx(const crypto::hash &id) const { CRITICAL_REGION_LOCAL(m_transactions_lock); - if(m_transactions.count(id)) + if (m_transactions.count(id)) { return true; + } return false; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) - { + bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) const { CRITICAL_REGION_LOCAL(m_transactions_lock); - BOOST_FOREACH(const auto& in, tx.vin) - { + for (const auto& in : tx.vin) { CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail - if(have_tx_keyimg_as_spent(tokey_in.k_image)) - return true; + if (have_tx_keyimg_as_spent(tokey_in.k_image)) { + return true; + } } return false; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) - { + bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) const { CRITICAL_REGION_LOCAL(m_transactions_lock); return m_spent_key_images.end() != m_spent_key_images.find(key_im); } //--------------------------------------------------------------------------------- - void tx_memory_pool::lock() - { + void tx_memory_pool::lock() const { m_transactions_lock.lock(); } //--------------------------------------------------------------------------------- - void tx_memory_pool::unlock() - { + void tx_memory_pool::unlock() const { m_transactions_lock.unlock(); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd) - { + bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd) const { //not the best implementation at this time, sorry :( //check is ring_signature already checked ? - if(txd.max_used_block_id == null_hash) - {//not checked, lets try to check - - if(txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) + if (txd.max_used_block_id == null_hash) { + //not checked, lets try to check + if (txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) { return false;//we already sure that this tx is broken for this height + } - if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) - { + if (!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) { 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); return false; } - }else - { - if(txd.max_used_block_height >= m_blockchain.get_current_blockchain_height()) + } else { + if (txd.max_used_block_height >= m_blockchain.get_current_blockchain_height()) { return false; - if(m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id) - { + } + if (m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id) { //if we already failed on this height and id, skip actual ring signature check - if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) + if (txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) { return false; + } //check ring signature again, it is possible (with very small chance) that this transaction become again valid - if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) - { + if (!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) { 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); return false; } } } + //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(txd.tx)) + if (m_blockchain.have_tx_keyimges_as_spent(txd.tx)) { return false; + } //transaction is ok. return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_key_images(const std::unordered_set& k_images, const transaction& tx) - { - for(size_t i = 0; i!= tx.vin.size(); i++) - { + bool tx_memory_pool::have_key_images(const std::unordered_set& k_images, const transaction& tx) { + for (size_t i = 0; i!= tx.vin.size(); i++) { CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); - if(k_images.count(itk.k_image)) + if (k_images.count(itk.k_image)) { return true; + } } return false; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::append_key_images(std::unordered_set& k_images, const transaction& tx) - { - for(size_t i = 0; i!= tx.vin.size(); i++) - { + bool tx_memory_pool::append_key_images(std::unordered_set& k_images, const transaction& tx) { + for (size_t i = 0; i!= tx.vin.size(); i++) { CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); auto i_res = k_images.insert(itk.k_image); CHECK_AND_ASSERT_MES(i_res.second, false, "internal error: key images pool cache - inserted duplicate image in set: " << itk.k_image); @@ -313,57 +277,43 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - std::string tx_memory_pool::print_pool(bool short_format) - { + std::string tx_memory_pool::print_pool(bool short_format) const { std::stringstream ss; CRITICAL_REGION_LOCAL(m_transactions_lock); - BOOST_FOREACH(transactions_container::value_type& txe, m_transactions) - { - if(short_format) - { - tx_details& txd = txe.second; - ss << "id: " << txe.first << ENDL - << "blob_size: " << txd.blob_size << ENDL - << "fee: " << txd.fee << ENDL - << "kept_by_block: " << txd.kept_by_block << ENDL - << "max_used_block_height: " << txd.max_used_block_height << ENDL - << "max_used_block_id: " << txd.max_used_block_id << ENDL - << "last_failed_height: " << txd.last_failed_height << ENDL - << "last_failed_id: " << txd.last_failed_id << ENDL; - }else - { - tx_details& txd = txe.second; - ss << "id: " << txe.first << ENDL - << obj_to_json_str(txd.tx) << ENDL - << "blob_size: " << txd.blob_size << ENDL - << "fee: " << txd.fee << ENDL - << "kept_by_block: " << txd.kept_by_block << ENDL - << "max_used_block_height: " << txd.max_used_block_height << ENDL - << "max_used_block_id: " << txd.max_used_block_id << ENDL - << "last_failed_height: " << txd.last_failed_height << ENDL - << "last_failed_id: " << txd.last_failed_id << ENDL; + for (const transactions_container::value_type& txe : m_transactions) { + const tx_details& txd = txe.second; + ss << "id: " << txe.first << std::endl; + if (!short_format) { + ss << obj_to_json_str(*const_cast(&txd.tx)) << std::endl; } - + ss << "blob_size: " << txd.blob_size << std::endl + << "fee: " << print_money(txd.fee) << std::endl + << "kept_by_block: " << (txd.kept_by_block ? 'T' : 'F') << std::endl + << "max_used_block_height: " << txd.max_used_block_height << std::endl + << "max_used_block_id: " << txd.max_used_block_id << std::endl + << "last_failed_height: " << txd.last_failed_height << std::endl + << "last_failed_id: " << txd.last_failed_id << std::endl; } + return ss.str(); } //--------------------------------------------------------------------------------- - 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) - { + 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) { CRITICAL_REGION_LOCAL(m_transactions_lock); total_size = 0; fee = 0; - size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + size_t max_total_size = (125 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; std::unordered_set k_images; - BOOST_FOREACH(transactions_container::value_type& tx, m_transactions) - { - if (max_total_size < total_size + tx.second.blob_size) + for (transactions_container::value_type& tx : m_transactions) { + if (max_total_size < total_size + tx.second.blob_size) { continue; + } - if (!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx)) + if (!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx)) { continue; + } bl.tx_hashes.push_back(tx.first); total_size += tx.second.blob_size; @@ -374,34 +324,35 @@ namespace cryptonote return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::init(const std::string& config_folder) - { + bool tx_memory_pool::init(const std::string& config_folder) { + CRITICAL_REGION_LOCAL(m_transactions_lock); + m_config_folder = config_folder; std::string state_file_path = config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; boost::system::error_code ec; - if(!boost::filesystem::exists(state_file_path, ec)) + if (!boost::filesystem::exists(state_file_path, ec)) { return true; + } bool res = tools::unserialize_obj_from_file(*this, state_file_path); - if(!res) - { - LOG_PRINT_L0("Failed to load memory pool from file " << state_file_path); + if (!res) { + LOG_ERROR("Failed to load memory pool from file " << state_file_path); + + m_transactions.clear(); + m_spent_key_images.clear(); } - return res; + // Ignore deserialization error + return true; } - //--------------------------------------------------------------------------------- - bool tx_memory_pool::deinit() - { - if (!tools::create_directories_if_necessary(m_config_folder)) - { + bool tx_memory_pool::deinit() { + if (!tools::create_directories_if_necessary(m_config_folder)) { LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); return false; } std::string state_file_path = m_config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; bool res = tools::serialize_obj_to_file(*this, state_file_path); - if(!res) - { + if (!res) { LOG_PRINT_L0("Failed to serialize memory pool to file " << state_file_path); } return true; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 26d273aa70..30b9061f31 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -18,61 +18,52 @@ #include "crypto/hash.h" -namespace cryptonote -{ +namespace cryptonote { class blockchain_storage; + /************************************************************************/ /* */ /************************************************************************/ - - class tx_memory_pool: boost::noncopyable - { + class tx_memory_pool: boost::noncopyable { public: tx_memory_pool(blockchain_storage& bchs); + + // load/store operations + bool init(const std::string& config_folder); + bool deinit(); + + bool have_tx(const crypto::hash &id) const; bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block); //gets tx and remove it from pool bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee); - bool have_tx(const crypto::hash &id); - bool have_tx_keyimg_as_spent(const crypto::key_image& key_im); - bool have_tx_keyimges_as_spent(const transaction& tx); - bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); - void lock(); - void unlock(); + void lock() const; + void unlock() const; - // load/store operations - bool init(const std::string& config_folder); - bool deinit(); bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); - bool get_transactions(std::list& txs); - bool get_transaction(const crypto::hash& h, transaction& tx); - size_t get_transactions_count(); - bool remove_transaction_keyimages(const transaction& tx); - bool have_key_images(const std::unordered_set& kic, const transaction& tx); - bool append_key_images(std::unordered_set& kic, const transaction& tx); - std::string print_pool(bool short_format); - /*bool flush_pool(const std::strig& folder); - bool inflate_pool(const std::strig& folder);*/ + void get_transactions(std::list& txs) const; + size_t get_transactions_count() const; + std::string print_pool(bool short_format) const; #define CURRENT_MEMPOOL_ARCHIVE_VER 7 template - void serialize(archive_t & a, const unsigned int version) - { - if(version < CURRENT_MEMPOOL_ARCHIVE_VER ) + void serialize(archive_t & a, const unsigned int version) { + if (version < CURRENT_MEMPOOL_ARCHIVE_VER) { return; + } + CRITICAL_REGION_LOCAL(m_transactions_lock); a & m_transactions; a & m_spent_key_images; } - struct tx_details - { + struct tx_details { transaction tx; size_t blob_size; uint64_t fee; @@ -85,58 +76,40 @@ namespace cryptonote }; private: - bool is_transaction_ready_to_go(tx_details& txd); + bool have_tx_keyimg_as_spent(const crypto::key_image& key_im) const; + bool have_tx_keyimges_as_spent(const transaction& tx) const; + bool remove_transaction_keyimages(const transaction& tx); + static bool have_key_images(const std::unordered_set& kic, const transaction& tx); + static bool append_key_images(std::unordered_set& kic, const transaction& tx); + + bool is_transaction_ready_to_go(tx_details& txd) const; + typedef std::unordered_map transactions_container; typedef std::unordered_map > key_images_container; - epee::critical_section m_transactions_lock; + mutable epee::critical_section m_transactions_lock; transactions_container m_transactions; key_images_container m_spent_key_images; - //transactions_container m_alternative_transactions; - std::string m_config_folder; blockchain_storage& m_blockchain; + /************************************************************************/ /* */ /************************************************************************/ - /*class inputs_visitor: public boost::static_visitor - { - key_images_container& m_spent_keys; - public: - inputs_visitor(key_images_container& spent_keys): m_spent_keys(spent_keys) - {} - bool operator()(const txin_to_key& tx) const - { - auto pr = m_spent_keys.insert(tx.k_image); - CHECK_AND_ASSERT_MES(pr.second, false, "Tried to insert transaction with input seems already spent, input: " << epee::string_tools::pod_to_hex(tx.k_image)); - return true; - } - bool operator()(const txin_gen& tx) const - { - CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); - return false; - } - bool operator()(const txin_to_script& tx) const {return false;} - bool operator()(const txin_to_scripthash& tx) const {return false;} - }; */ - /************************************************************************/ - /* */ - /************************************************************************/ - class amount_visitor: public boost::static_visitor - { + class amount_visitor: public boost::static_visitor { public: - uint64_t operator()(const txin_to_key& tx) const - { + uint64_t operator()(const txin_to_key& tx) const { return tx.amount; } - uint64_t operator()(const txin_gen& tx) const - { + + uint64_t operator()(const txin_gen& tx) const { CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); return 0; } - uint64_t operator()(const txin_to_script& tx) const {return 0;} - uint64_t operator()(const txin_to_scripthash& tx) const {return 0;} + + uint64_t operator()(const txin_to_script& tx) const { return 0; } + uint64_t operator()(const txin_to_scripthash& tx) const { return 0; } }; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) @@ -145,13 +118,10 @@ namespace cryptonote }; } -namespace boost -{ - namespace serialization - { +namespace boost { + namespace serialization { template - void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version) - { + void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version) { ar & td.blob_size; ar & td.fee; ar & td.tx; @@ -159,11 +129,8 @@ namespace boost ar & td.max_used_block_id; ar & td.last_failed_height; ar & td.last_failed_id; - } } } -BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) - - +BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 329f44e7fe..f1f9171c0f 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -160,7 +160,7 @@ int main(int argc, char* argv[]) //initialize core here LOG_PRINT_L0("Initializing core..."); - res = ccore.init(vm); + res = ccore.init(vm, true); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); LOG_PRINT_L0("Core initialized OK"); diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp index a1c1ad5c68..e03f608533 100644 --- a/src/miner/simpleminer.cpp +++ b/src/miner/simpleminer.cpp @@ -101,6 +101,7 @@ namespace mining std::string pool_session_id; simpleminer::job_details_native job = AUTO_VAL_INIT(job); uint64_t last_job_ticks = 0; + crypto::cn_context context; while(true) { @@ -158,11 +159,11 @@ namespace mining //uint32_t c = (*((uint32_t*)&job.blob.data()[39])); ++(*((uint32_t*)&job.blob.data()[39])); crypto::hash h = cryptonote::null_hash; - crypto::cn_slow_hash(job.blob.data(), job.blob.size(), h); + crypto::cn_slow_hash(context, job.blob.data(), job.blob.size(), h); if( ((uint32_t*)&h)[7] < job.target ) { //found! - + COMMAND_RPC_SUBMITSHARE::request submit_request = AUTO_VAL_INIT(submit_request); COMMAND_RPC_SUBMITSHARE::response submit_response = AUTO_VAL_INIT(submit_response); submit_request.id = pool_session_id; diff --git a/src/node_rpc_proxy/InitState.h b/src/node_rpc_proxy/InitState.h new file mode 100644 index 0000000000..13c8aad6de --- /dev/null +++ b/src/node_rpc_proxy/InitState.h @@ -0,0 +1,83 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +#include "include_base_utils.h" + +namespace tools { + +class InitState { +public: + InitState() : m_state(STATE_NOT_INITIALIZED) { + } + + bool initialized() const volatile { + return STATE_INITIALIZED == m_state.load(std::memory_order_acquire); + } + + bool beginInit() volatile { + State state = STATE_NOT_INITIALIZED; + if (!m_state.compare_exchange_strong(state, STATE_INITIALIZING, std::memory_order_seq_cst)) { + LOG_ERROR("object has been already initialized"); + return false; + } + return true; + } + + bool endInit() volatile { + State expectedState = STATE_INITIALIZING; + if (!m_state.compare_exchange_strong(expectedState, STATE_INITIALIZED, std::memory_order_seq_cst)) { + LOG_ERROR("Unexpected state: " << expectedState); + return false; + } + return true; + } + + bool beginShutdown() volatile { + while (true) { + State state = m_state.load(std::memory_order_relaxed); + if (STATE_NOT_INITIALIZED == state) { + return true; + } else if (STATE_INITIALIZING == state) { + LOG_ERROR("Object is being initialized"); + return false; + } else if (STATE_INITIALIZED == state) { + if (m_state.compare_exchange_strong(state, STATE_SHUTTING_DOWN, std::memory_order_seq_cst)) { + return true; + } + } else if (STATE_SHUTTING_DOWN == state) { + LOG_ERROR("Object is being shutting down"); + return false; + } else { + LOG_ERROR("Unknown state " << state); + return false; + } + } + } + + bool endShutdown() volatile { + State expectedState = STATE_SHUTTING_DOWN; + if (!m_state.compare_exchange_strong(expectedState, STATE_NOT_INITIALIZED, std::memory_order_seq_cst)) { + LOG_ERROR("Unexpected state: " << expectedState); + return false; + } + return true; + } + +private: + enum State { + STATE_NOT_INITIALIZED, + STATE_INITIALIZING, + STATE_INITIALIZED, + STATE_SHUTTING_DOWN + }; + +private: + std::atomic m_state; +}; + +} diff --git a/src/node_rpc_proxy/NodeErrors.cpp b/src/node_rpc_proxy/NodeErrors.cpp new file mode 100644 index 0000000000..7558f62a63 --- /dev/null +++ b/src/node_rpc_proxy/NodeErrors.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "NodeErrors.h" + +namespace cryptonote { +namespace error { + +NodeErrorCategory NodeErrorCategory::INSTANCE; + +} +} diff --git a/src/node_rpc_proxy/NodeErrors.h b/src/node_rpc_proxy/NodeErrors.h new file mode 100644 index 0000000000..0f21f6114d --- /dev/null +++ b/src/node_rpc_proxy/NodeErrors.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +namespace cryptonote { +namespace error { + +// custom error conditions enum type: +enum NodeErrorCodes { + NOT_INITIALIZED = 1, + ALREADY_INITIALIZED, + NETWORK_ERROR, + NODE_BUSY, + INTERNAL_NODE_ERROR, +}; + +// custom category: +class NodeErrorCategory : public std::error_category { +public: + static NodeErrorCategory INSTANCE; + + virtual const char* name() const throw() { + return "NodeErrorCategory"; + } + + virtual std::error_condition default_error_condition(int ev) const throw() { + return std::error_condition(ev, *this); + } + + virtual std::string message(int ev) const { + switch (ev) { + case NOT_INITIALIZED: return "Object was not initialized"; + case ALREADY_INITIALIZED: return "Object has been already initialized"; + case NETWORK_ERROR: return "Network error"; + case NODE_BUSY: return "Node is busy"; + case INTERNAL_NODE_ERROR: return "Internal node error"; + default: return "Unknown error"; + } + } + +private: + NodeErrorCategory() { + } +}; + +} +} + +inline std::error_code make_error_code(cryptonote::error::NodeErrorCodes e) { + return std::error_code(static_cast(e), cryptonote::error::NodeErrorCategory::INSTANCE); +} diff --git a/src/node_rpc_proxy/NodeRpcProxy.cpp b/src/node_rpc_proxy/NodeRpcProxy.cpp new file mode 100644 index 0000000000..3effca9da6 --- /dev/null +++ b/src/node_rpc_proxy/NodeRpcProxy.cpp @@ -0,0 +1,255 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "NodeRpcProxy.h" + +#include +#include +#include + +#include "cryptonote_core/cryptonote_format_utils.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "storages/http_abstract_invoke.h" +#include "NodeErrors.h" + +namespace cryptonote { + +using namespace CryptoNote; + +namespace { + std::error_code interpretJsonRpcResponse(bool ok, const std::string& status) { + if (!ok) { + return make_error_code(error::NETWORK_ERROR); + } else if (CORE_RPC_STATUS_BUSY == status) { + return make_error_code(error::NODE_BUSY); + } else if (CORE_RPC_STATUS_OK != status) { + return make_error_code(error::INTERNAL_NODE_ERROR); + } + return std::error_code(); + } +} + +NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort) + : m_nodeAddress("http://" + nodeHost + ":" + std::to_string(nodePort)) + , m_rpcTimeout(10000) + , m_pullTimer(m_ioService) + , m_pullInterval(10000) { + resetInternalState(); +} + +NodeRpcProxy::~NodeRpcProxy() { + shutdown(); +} + +void NodeRpcProxy::resetInternalState() { + m_ioService.reset(); + m_observerManager.clear(); + + m_peerCount = 0; + m_nodeHeight = 0; + m_networkHeight = 0; + m_lastKnowHash = cryptonote::null_hash; +} + +void NodeRpcProxy::init(const INode::Callback& callback) { + if (!m_initState.beginInit()) { + callback(make_error_code(error::ALREADY_INITIALIZED)); + return; + } + + resetInternalState(); + m_workerThread = std::thread(std::bind(&NodeRpcProxy::workerThread, this, callback)); +} + +bool NodeRpcProxy::shutdown() { + if (!m_initState.beginShutdown()) { + return false; + } + + boost::system::error_code ignored_ec; + m_pullTimer.cancel(ignored_ec); + m_ioService.stop(); + m_workerThread.join(); + + m_initState.endShutdown(); + return true; +} + +void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) { + if (!m_initState.endInit()) { + return; + } + + initialized_callback(std::error_code()); + + pullNodeStatusAndScheduleTheNext(); + + while (!m_ioService.stopped()) { + m_ioService.run_one(); + } +} + +void NodeRpcProxy::pullNodeStatusAndScheduleTheNext() { + updateNodeStatus(); + + m_pullTimer.expires_from_now(boost::posix_time::milliseconds(m_pullInterval)); + m_pullTimer.async_wait([=](const boost::system::error_code& ec) { + if (ec != boost::asio::error::operation_aborted) { + pullNodeStatusAndScheduleTheNext(); + } + }); +} + +void NodeRpcProxy::updateNodeStatus() { + cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp = AUTO_VAL_INIT(rsp); + bool r = epee::net_utils::invoke_http_json_rpc(m_nodeAddress + "/json_rpc", "getlastblockheader", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + if (!ec) { + crypto::hash blockHash; + if (!parse_hash256(rsp.block_header.hash, blockHash)) { + LOG_ERROR("Invalid block hash format: " << rsp.block_header.hash); + return; + } + + if (blockHash != m_lastKnowHash) { + m_lastKnowHash = blockHash; + m_nodeHeight = rsp.block_header.height; + // TODO request and update network height + m_networkHeight = m_nodeHeight; + m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); + //if (m_networkHeight != rsp.block_header.network_height) { + // m_networkHeight = rsp.block_header.network_height; + // m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); + //} + m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight); + } + } else { + LOG_PRINT_L2("Failed to invoke getlastblockheader: " << ec.message() << ':' << ec.value()); + } + + updatePeerCount(); +} + +void NodeRpcProxy::updatePeerCount() { + cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp); + bool r = epee::net_utils::invoke_http_json_remote_command2(m_nodeAddress + "/getinfo", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + if (!ec) { + size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count; + if (peerCount != m_peerCount) { + m_peerCount = peerCount; + m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount); + } + } else { + LOG_PRINT_L2("Failed to invoke getinfo: " << ec.message() << ':' << ec.value()); + } +} + +bool NodeRpcProxy::addObserver(INodeObserver* observer) { + return m_observerManager.add(observer); +} + +bool NodeRpcProxy::removeObserver(INodeObserver* observer) { + return m_observerManager.remove(observer); +} + +size_t NodeRpcProxy::getPeerCount() const { + return m_peerCount; +} + +uint64_t NodeRpcProxy::getLastLocalBlockHeight() const { + return m_nodeHeight; +} + +uint64_t NodeRpcProxy::getLastKnownBlockHeight() const { + return m_networkHeight; +} + +void NodeRpcProxy::relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) { + if (!m_initState.initialized()) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO: m_ioService.stop() won't inkove callback(aborted). Fix it + m_ioService.post(std::bind(&NodeRpcProxy::doRelayTransaction, this, transaction, callback)); +} + +void NodeRpcProxy::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& outs, const Callback& callback) { + if (!m_initState.initialized()) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + m_ioService.post(std::bind(&NodeRpcProxy::doGetRandomOutsByAmounts, this, std::move(amounts), outsCount, std::ref(outs), callback)); +} + +void NodeRpcProxy::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + if (!m_initState.initialized()) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + m_ioService.post(std::bind(&NodeRpcProxy::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), std::ref(startHeight), callback)); +} + +void NodeRpcProxy::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { + if (!m_initState.initialized()) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + m_ioService.post(std::bind(&NodeRpcProxy::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback)); +} + +void NodeRpcProxy::doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback) { + COMMAND_RPC_SEND_RAW_TX::request req; + COMMAND_RPC_SEND_RAW_TX::response rsp; + req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(transaction)); + bool r = epee::net_utils::invoke_http_json_remote_command2(m_nodeAddress + "/sendrawtransaction", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + callback(ec); +} + +void NodeRpcProxy::doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, std::vector& outs, const Callback& callback) { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response rsp = AUTO_VAL_INIT(rsp); + req.amounts = std::move(amounts); + req.outs_count = outsCount; + bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/getrandom_outs.bin", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + if (!ec) { + outs = std::move(rsp.outs); + } + callback(ec); +} + +void NodeRpcProxy::doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response rsp = AUTO_VAL_INIT(rsp); + req.block_ids = std::move(knownBlockIds); + bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/getblocks.bin", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + if (!ec) { + newBlocks = std::move(rsp.blocks); + startHeight = rsp.start_height; + } + callback(ec); +} + +void NodeRpcProxy::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { + cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response rsp = AUTO_VAL_INIT(rsp); + req.txid = transactionHash; + bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/get_o_indexes.bin", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + if (!ec) { + outsGlobalIndices = std::move(rsp.o_indexes); + } + callback(ec); +} + +} diff --git a/src/node_rpc_proxy/NodeRpcProxy.h b/src/node_rpc_proxy/NodeRpcProxy.h new file mode 100644 index 0000000000..adf1f30fe3 --- /dev/null +++ b/src/node_rpc_proxy/NodeRpcProxy.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include + +#include "common/ObserverManager.h" +#include "include_base_utils.h" +#include "net/http_client.h" +#include "InitState.h" +#include "INode.h" + +namespace cryptonote { + +class NodeRpcProxy : public CryptoNote::INode { +public: + NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort); + virtual ~NodeRpcProxy(); + + virtual bool addObserver(CryptoNote::INodeObserver* observer); + virtual bool removeObserver(CryptoNote::INodeObserver* observer); + + virtual void init(const Callback& callback); + virtual bool shutdown(); + + virtual size_t getPeerCount() const; + virtual uint64_t getLastLocalBlockHeight() const; + virtual uint64_t getLastKnownBlockHeight() const; + + virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + + unsigned int rpcTimeout() const { return m_rpcTimeout; } + void rpcTimeout(unsigned int val) { m_rpcTimeout = val; } + +private: + void resetInternalState(); + void workerThread(const Callback& initialized_callback); + + void pullNodeStatusAndScheduleTheNext(); + void updateNodeStatus(); + void updatePeerCount(); + + void doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + void doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + void doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + +private: + tools::InitState m_initState; + std::thread m_workerThread; + boost::asio::io_service m_ioService; + tools::ObserverManager m_observerManager; + + std::string m_nodeAddress; + unsigned int m_rpcTimeout; + epee::net_utils::http::http_simple_client m_httpClient; + + boost::asio::deadline_timer m_pullTimer; + uint64_t m_pullInterval; + + // Internal state + size_t m_peerCount; + uint64_t m_nodeHeight; + uint64_t m_networkHeight; + crypto::hash m_lastKnowHash; +}; + +} diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 07f4a1cb9d..4815af2a53 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -175,12 +175,7 @@ namespace cryptonote } std::list missed_txs; std::list txs; - bool r = m_core.get_transactions(vh, txs, missed_txs); - if(!r) - { - res.status = "Failed"; - return true; - } + m_core.get_transactions(vh, txs, missed_txs); BOOST_FOREACH(auto& tx, txs) { @@ -415,12 +410,17 @@ namespace cryptonote } cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); m_core.handle_incoming_block(blockblob, bvc); - if(!bvc.m_added_to_main_chain) - { + if (bvc.m_added_to_main_chain){ + block b = AUTO_VAL_INIT(b); + parse_and_validate_block_from_blob(blockblob, b); + + m_core.notify_new_block(b); + } else { error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED; error_resp.message = "Block not accepted"; return false; } + res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index d67ebfdd7a..7a95731988 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -115,8 +115,8 @@ namespace cryptonote { struct request { - std::list amounts; - uint64_t outs_count; + std::vector amounts; + uint64_t outs_count; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(amounts) KV_SERIALIZE(outs_count) @@ -363,7 +363,11 @@ namespace cryptonote struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { - typedef std::list request; + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; struct response { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4c6a736025..51e80bdd03 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -64,19 +64,6 @@ namespace return err; } - bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) - { - blobdata payment_id_data; - if(!string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) - return false; - - if(sizeof(crypto::hash) != payment_id_data.size()) - return false; - - payment_id = *reinterpret_cast(payment_id_data.data()); - return true; - } - class message_writer { public: @@ -448,9 +435,9 @@ bool simple_wallet::start_mining(const std::vector& args) } else if (1 == args.size()) { - uint16_t num; + uint16_t num = 1; ok = string_tools::get_xtype_from_string(num, args[0]); - ok &= (1 <= num && num <= max_mining_threads_count); + ok = ok && (1 <= num && num <= max_mining_threads_count); req.threads_count = num; } else @@ -663,7 +650,7 @@ bool simple_wallet::show_payments(const std::vector &args) for(std::string arg : args) { crypto::hash payment_id; - if(parse_payment_id(arg, payment_id)) + if (tools::wallet2::parse_payment_id(arg, payment_id)) { std::list payments; m_wallet->get_payments(payment_id, payments); @@ -746,7 +733,7 @@ bool simple_wallet::transfer(const std::vector &args_) local_args.pop_back(); crypto::hash payment_id; - bool r = parse_payment_id(payment_id_str, payment_id); + bool r = tools::wallet2::parse_payment_id(payment_id_str, payment_id); if(r) { std::string extra_nonce; @@ -785,7 +772,7 @@ bool simple_wallet::transfer(const std::vector &args_) try { cryptonote::transaction tx; - m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, extra, tx); + m_wallet->transfer(dsts, fake_outs_count, 0, MINIMUM_FEE, extra, tx); success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); } catch (const tools::error::daemon_busy&) diff --git a/src/version.h.in b/src/version.h.in index 052704167d..62316da3fd 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.10" -#define PROJECT_VERSION_BUILD_NO "71" +#define PROJECT_VERSION "0.8.11" +#define PROJECT_VERSION_BUILD_NO "65" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp new file mode 100644 index 0000000000..a327e1a2b4 --- /dev/null +++ b/src/wallet/Wallet.cpp @@ -0,0 +1,491 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "Wallet.h" +#include "wallet_errors.h" +#include "string_tools.h" +#include "serialization/binary_utils.h" +#include "storages/portable_storage_template_helper.h" +#include "WalletUtils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "WalletSerialization.h" +#include +#include + +namespace { + +void throwNotDefined() { + throw std::runtime_error("The behavior is not defined!"); +} + +bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { + crypto::public_key pub; + bool r = crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void throwIfKeysMissmatch(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { + if (!verifyKeys(sec, expected_pub)) + throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); +} + +class ContextCounterHolder +{ +public: + ContextCounterHolder(CryptoNote::WalletAsyncContextCounter& shutdowner) : m_shutdowner(shutdowner) {} + ~ContextCounterHolder() { m_shutdowner.delAsyncContext(); } + +private: + CryptoNote::WalletAsyncContextCounter& m_shutdowner; +}; + +template +void runAtomic(std::mutex& mutex, F f) { + std::unique_lock lock(mutex); + f(); +} +} //namespace + +namespace CryptoNote { + +Wallet::Wallet(INode& node) : + m_state(NOT_INITIALIZED), + m_node(node), + m_isSynchronizing(false), + m_isStopping(false), + m_transferDetails(m_blockchain), + m_transactionsCache(m_sendingTxsStates), + m_synchronizer(m_account, m_node, m_blockchain, m_transferDetails, m_unconfirmedTransactions, m_transactionsCache), + m_sender(m_transactionsCache, m_sendingTxsStates, m_transferDetails, m_unconfirmedTransactions) { + m_autoRefresher.reset(new WalletNodeObserver(this)); +} + +void Wallet::addObserver(IWalletObserver* observer) { + m_observerManager.add(observer); +} + +void Wallet::removeObserver(IWalletObserver* observer) { + m_observerManager.remove(observer); +} + +void Wallet::initAndGenerate(const std::string& password) { + { + std::unique_lock stateLock(m_cacheMutex); + + if (m_state != NOT_INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + m_node.addObserver(m_autoRefresher.get()); + + m_account.generate(); + m_password = password; + + m_sender.init(m_account.get_keys()); + + storeGenesisBlock(); + + m_state = INITIALIZED; + } + + m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + refresh(); +} + +void Wallet::storeGenesisBlock() { + cryptonote::block b; + cryptonote::generate_genesis_block(b); + m_blockchain.push_back(get_block_hash(b)); +} + +void Wallet::initAndLoad(std::istream& source, const std::string& password) { + std::unique_lock stateLock(m_cacheMutex); + + if (m_state != NOT_INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + m_node.addObserver(m_autoRefresher.get()); + + m_password = password; + m_state = LOADING; + + std::thread loader(&Wallet::doLoad, this, std::ref(source)); + loader.detach(); +} + +void Wallet::doLoad(std::istream& source) { + try + { + std::unique_lock lock(m_cacheMutex); + + boost::archive::binary_iarchive ar(source); + + crypto::chacha8_iv iv; + std::string chacha_str;; + ar >> chacha_str; + + ::serialization::parse_binary(chacha_str, iv); + + std::string cipher; + ar >> cipher; + + std::string plain; + decrypt(cipher, plain, iv, m_password); + + std::stringstream restore(plain); + + try + { + //boost archive ctor throws an exception if password is wrong (i.e. there's garbage in a stream) + boost::archive::binary_iarchive dataArchive(restore); + + dataArchive >> m_account; + + throwIfKeysMissmatch(m_account.get_keys().m_view_secret_key, m_account.get_keys().m_account_address.m_view_public_key); + throwIfKeysMissmatch(m_account.get_keys().m_spend_secret_key, m_account.get_keys().m_account_address.m_spend_public_key); + + dataArchive >> m_blockchain; + + m_transferDetails.load(dataArchive); + m_unconfirmedTransactions.load(dataArchive); + m_transactionsCache.load(dataArchive); + } + catch (std::exception&) { + throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); + } + + m_sender.init(m_account.get_keys()); + } + catch (std::system_error& e) { + runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); + m_observerManager.notify(&IWalletObserver::initCompleted, e.code()); + return; + } + catch (std::exception&) { + runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); + m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + return; + } + + runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); + + m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + + refresh(); +} + +void Wallet::decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password) { + crypto::chacha8_key key; + crypto::cn_context context; + crypto::generate_chacha8_key(context, password, key); + + plain.resize(cipher.size()); + + crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]); +} + +void Wallet::shutdown() { + { + std::unique_lock lock(m_cacheMutex); + + if (m_isStopping) + throwNotDefined(); + + m_isStopping = true; + + if (m_state == NOT_INITIALIZED) + throwNotDefined(); + + m_sender.stop(); + m_synchronizer.stop(); + } + + m_asyncContextCounter.waitAsyncContextsFinish(); + m_node.removeObserver(m_autoRefresher.get()); +} + +void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { + if(m_isStopping) { + m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::OPERATION_CANCELLED)); + return; + } + + { + std::unique_lock lock(m_cacheMutex); + + throwIf(m_state != INITIALIZED, cryptonote::error::WRONG_STATE); + + m_state = SAVING; + } + + m_asyncContextCounter.addAsyncContext(); + std::thread saver(&Wallet::doSave, this, std::ref(destination), saveDetailed, saveCache); + saver.detach(); +} + +void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache) { + ContextCounterHolder counterHolder(m_asyncContextCounter); + + try { + //TODO: exception safety: leave destination stream empty in case of errors + boost::archive::binary_oarchive ar(destination); + + std::stringstream original; + std::unique_lock lock(m_cacheMutex); + + boost::archive::binary_oarchive archive(original); + + archive << m_account; + + const BlockchainContainer& blockchain = saveCache ? m_blockchain : BlockchainContainer(); + + archive << blockchain; + + m_transferDetails.save(archive, saveCache); + m_unconfirmedTransactions.save(archive, saveCache); + m_transactionsCache.save(archive, saveDetailed, saveCache); + + std::string plain = original.str(); + std::string cipher; + + crypto::chacha8_iv iv = encrypt(plain, cipher); + + std::string chacha_str; + ::serialization::dump_binary(iv, chacha_str); + ar << chacha_str; + ar << cipher; + + m_state = INITIALIZED; + } + catch (std::system_error& e) { + runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); + m_observerManager.notify(&IWalletObserver::saveCompleted, e.code()); + return; + } + catch (std::exception&) { + runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); + m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + return; + } + + m_observerManager.notify(&IWalletObserver::saveCompleted, std::error_code()); +} + +crypto::chacha8_iv Wallet::encrypt(const std::string& plain, std::string& cipher) { + crypto::chacha8_key key; + crypto::cn_context context; + crypto::generate_chacha8_key(context, m_password, key); + + cipher.resize(plain.size()); + + crypto::chacha8_iv iv = crypto::rand(); + crypto::chacha8(plain.data(), plain.size(), key, iv, &cipher[0]); + + return iv; +} + +std::error_code Wallet::changePassword(const std::string& oldPassword, const std::string& newPassword) { + std::unique_lock passLock(m_cacheMutex); + + throwIfNotInitialised(); + + if (m_password.compare(oldPassword)) + return make_error_code(cryptonote::error::WRONG_PASSWORD); + + //we don't let the user to change the password while saving + m_password = newPassword; + + return std::error_code(); +} + +std::string Wallet::getAddress() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_account.get_public_address_str(); +} + +uint64_t Wallet::actualBalance() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transferDetails.countActualBalance(); +} + +uint64_t Wallet::pendingBalance() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return doPendingBalance(); +} + +uint64_t Wallet::doPendingBalance() { + uint64_t amount = 0; + amount = m_transferDetails.countPendingBalance(); + amount += m_unconfirmedTransactions.countPendingBalance(); + + return amount; +} + +size_t Wallet::getTransactionCount() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.getTransactionCount(); +} + +size_t Wallet::getTransferCount() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.getTransferCount(); +} + +TransactionId Wallet::findTransactionByTransferId(TransferId transferId) { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.findTransactionByTransferId(transferId); +} + +bool Wallet::getTransaction(TransactionId transactionId, Transaction& transaction) { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.getTransaction(transactionId, transaction); +} + +bool Wallet::getTransfer(TransferId transferId, Transfer& transfer) { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.getTransfer(transferId, transfer); +} + +TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { + std::vector transfers; + transfers.push_back(transfer); + + return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp); +} + +TransactionId Wallet::sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { + TransactionId txId = 0; + std::shared_ptr request; + std::deque > events; + + { + std::unique_lock lock(m_cacheMutex); + request = m_sender.makeSendRequest(txId, events, transfers, fee, extra, mixIn, unlockTimestamp); + } + + notifyClients(events); + + if (request) { + m_asyncContextCounter.addAsyncContext(); + request->perform(m_node, std::bind(&Wallet::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); + } + + return txId; +} + +void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) { + ContextCounterHolder counterHolder(m_asyncContextCounter); + std::deque > events; + + boost::optional > nextRequest; + { + std::unique_lock lock(m_cacheMutex); + callback(events, nextRequest, ec); + } + + notifyClients(events); + + if (nextRequest) { + m_asyncContextCounter.addAsyncContext(); + (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + } + +} + +void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { + ContextCounterHolder counterHolder(m_asyncContextCounter); + + std::deque > events; + boost::optional > nextRequest; + { + std::unique_lock lock(m_cacheMutex); + callback(events, nextRequest, ec); + + if (!nextRequest) + m_isSynchronizing = false; + } + + notifyClients(events); + + if (nextRequest) { + m_asyncContextCounter.addAsyncContext(); + (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + } +} + +std::error_code Wallet::cancelTransaction(size_t transactionId) { + return make_error_code(cryptonote::error::TX_CANCEL_IMPOSSIBLE); +} + +void Wallet::throwIfNotInitialised() { + if (m_state == NOT_INITIALIZED || m_state == LOADING) + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); +} + +void Wallet::startRefresh() { + refresh(); +} + +void Wallet::refresh() { + if (m_isStopping) + return; + + std::shared_ptr req; + { + std::unique_lock lock(m_cacheMutex); + + if (m_state != INITIALIZED) { + return; + } + + if (m_isSynchronizing) { + return; + } + + m_isSynchronizing = true; + + req = m_synchronizer.makeStartRefreshRequest(); + } + + m_asyncContextCounter.addAsyncContext(); + req->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); +} + +void Wallet::notifyClients(std::deque >& events) { + while (!events.empty()) { + std::shared_ptr event = events.front(); + event->notify(m_observerManager); + events.pop_front(); + } +} + +} //namespace CryptoNote diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h new file mode 100644 index 0000000000..6ca795d326 --- /dev/null +++ b/src/wallet/Wallet.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include + +#include "IWallet.h" +#include "INode.h" +#include "WalletErrors.h" +#include "WalletAsyncContextCounter.h" +#include "WalletTxSendingState.h" +#include "common/ObserverManager.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/tx_extra.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "WalletTransferDetails.h" +#include "WalletUserTransactionsCache.h" +#include "WalletUnconfirmedTransactions.h" +#include "WalletSynchronizer.h" +#include "WalletTransactionSender.h" +#include "WalletRequest.h" + +namespace CryptoNote { + +class Wallet : public IWallet { +public: + Wallet(INode& node); + ~Wallet() {}; + + virtual void addObserver(IWalletObserver* observer); + virtual void removeObserver(IWalletObserver* observer); + + virtual void initAndGenerate(const std::string& password); + virtual void initAndLoad(std::istream& source, const std::string& password); + virtual void shutdown(); + + virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true); + + virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword); + + virtual std::string getAddress(); + + virtual uint64_t actualBalance(); + virtual uint64_t pendingBalance(); + + virtual size_t getTransactionCount(); + virtual size_t getTransferCount(); + + virtual TransactionId findTransactionByTransferId(TransferId transferId); + + virtual bool getTransaction(TransactionId transactionId, Transaction& transaction); + virtual bool getTransfer(TransferId transferId, Transfer& transfer); + + virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + virtual std::error_code cancelTransaction(size_t transactionId); + + void startRefresh(); + +private: + void throwIfNotInitialised(); + void refresh(); + uint64_t doPendingBalance(); + + void doSave(std::ostream& destination, bool saveDetailed, bool saveCache); + void doLoad(std::istream& source); + + crypto::chacha8_iv encrypt(const std::string& plain, std::string& cipher); + void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password); + + void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec); + void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec); + void notifyClients(std::deque >& events); + + void storeGenesisBlock(); + + enum WalletState + { + NOT_INITIALIZED = 0, + INITIALIZED, + LOADING, + SAVING + }; + + WalletState m_state; + std::mutex m_cacheMutex; + cryptonote::account_base m_account; + std::string m_password; + INode& m_node; + bool m_isSynchronizing; + bool m_isStopping; + + typedef std::vector BlockchainContainer; + + BlockchainContainer m_blockchain; + WalletTransferDetails m_transferDetails; + WalletUnconfirmedTransactions m_unconfirmedTransactions; + tools::ObserverManager m_observerManager; + + WalletTxSendingState m_sendingTxsStates; + WalletUserTransactionsCache m_transactionsCache; + + struct WalletNodeObserver: public INodeObserver + { + WalletNodeObserver(Wallet* wallet) : m_wallet(wallet) {} + virtual void lastKnownBlockHeightUpdated(uint64_t height) { m_wallet->startRefresh(); } + + Wallet* m_wallet; + }; + + std::unique_ptr m_autoRefresher; + WalletAsyncContextCounter m_asyncContextCounter; + + WalletSynchronizer m_synchronizer; + WalletTransactionSender m_sender; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/wallet/WalletAsyncContextCounter.cpp new file mode 100644 index 0000000000..752933b6be --- /dev/null +++ b/src/wallet/WalletAsyncContextCounter.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "WalletAsyncContextCounter.h" + +namespace CryptoNote { + +void WalletAsyncContextCounter::addAsyncContext() { + std::unique_lock lock(m_mutex); + m_asyncContexts++; +} + +void WalletAsyncContextCounter::delAsyncContext() { + std::unique_lock lock(m_mutex); + m_asyncContexts--; + + if (!m_asyncContexts) m_cv.notify_one(); +} + +void WalletAsyncContextCounter::waitAsyncContextsFinish() { + std::unique_lock lock(m_mutex); + while (m_asyncContexts) + m_cv.wait(lock); +} + +} //namespace CryptoNote + + diff --git a/src/wallet/WalletAsyncContextCounter.h b/src/wallet/WalletAsyncContextCounter.h new file mode 100644 index 0000000000..c7acd6626d --- /dev/null +++ b/src/wallet/WalletAsyncContextCounter.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include + +namespace CryptoNote { + +class WalletAsyncContextCounter +{ +public: + WalletAsyncContextCounter() : m_asyncContexts(0) {} + + void addAsyncContext(); + void delAsyncContext(); + + //returns true if contexts are finished before timeout + void waitAsyncContextsFinish(); + +private: + uint32_t m_asyncContexts; + std::condition_variable m_cv; + std::mutex m_mutex; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletErrors.cpp b/src/wallet/WalletErrors.cpp new file mode 100644 index 0000000000..792c046de6 --- /dev/null +++ b/src/wallet/WalletErrors.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "WalletErrors.h" + +namespace cryptonote { +namespace error { + +WalletErrorCategory WalletErrorCategory::INSTANCE; + +} +} diff --git a/src/wallet/WalletErrors.h b/src/wallet/WalletErrors.h new file mode 100644 index 0000000000..e62617f2b8 --- /dev/null +++ b/src/wallet/WalletErrors.h @@ -0,0 +1,73 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +namespace cryptonote { +namespace error { + +// custom error conditions enum type: +enum WalletErrorCodes { + NOT_INITIALIZED = 1, + ALREADY_INITIALIZED, + WRONG_STATE, + WRONG_PASSWORD, + INTERNAL_WALLET_ERROR, + MIXIN_COUNT_TOO_BIG, + BAD_ADDRESS, + TRANSACTION_SIZE_TOO_BIG, + WRONG_AMOUNT, + SUM_OVERFLOW, + ZERO_DESTINATION, + TX_CANCEL_IMPOSSIBLE, + TX_CANCELLED, + OPERATION_CANCELLED +}; + +// custom category: +class WalletErrorCategory : public std::error_category { +public: + static WalletErrorCategory INSTANCE; + + virtual const char* name() const throw() { + return "WalletErrorCategory"; + } + + virtual std::error_condition default_error_condition(int ev) const throw() { + return std::error_condition(ev, *this); + } + + virtual std::string message(int ev) const { + switch (ev) { + case NOT_INITIALIZED: return "Object was not initialized"; + case WRONG_PASSWORD: return "The password is wrong"; + case ALREADY_INITIALIZED: return "The object is already initialized"; + case INTERNAL_WALLET_ERROR: return "Internal error occured"; + case MIXIN_COUNT_TOO_BIG: return "MixIn count is too big"; + case BAD_ADDRESS: return "Bad address"; + case TRANSACTION_SIZE_TOO_BIG: return "Transaction size is too big"; + case WRONG_AMOUNT: return "Wrong amount"; + case SUM_OVERFLOW: return "Sum overflow"; + case ZERO_DESTINATION: return "The destination is empty"; + case TX_CANCEL_IMPOSSIBLE: return "Impossible to cancel transaction"; + case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later"; + case OPERATION_CANCELLED: return "The operation you've requested has been cancelled"; + default: return "Unknown error"; + } + } + +private: + WalletErrorCategory() { + } +}; + +} +} + +inline std::error_code make_error_code(cryptonote::error::WalletErrorCodes e) { + return std::error_code(static_cast(e), cryptonote::error::WalletErrorCategory::INSTANCE); +} diff --git a/src/wallet/WalletEvent.h b/src/wallet/WalletEvent.h new file mode 100644 index 0000000000..10bc210a42 --- /dev/null +++ b/src/wallet/WalletEvent.h @@ -0,0 +1,113 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "IWallet.h" +#include "common/ObserverManager.h" + +namespace CryptoNote +{ + +class WalletEvent +{ +public: + virtual ~WalletEvent() {}; + + virtual void notify(tools::ObserverManager& observer) = 0; +}; + +class WalletTransactionUpdatedEvent : public WalletEvent +{ +public: + WalletTransactionUpdatedEvent(TransactionId transactionId) : m_id(transactionId) {}; + virtual ~WalletTransactionUpdatedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::transactionUpdated, m_id); + } + +private: + TransactionId m_id; +}; + +class WalletSendTransactionCompletedEvent : public WalletEvent +{ +public: + WalletSendTransactionCompletedEvent(TransactionId transactionId, std::error_code result) : m_id(transactionId), m_error(result) {}; + virtual ~WalletSendTransactionCompletedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::sendTransactionCompleted, m_id, m_error); + } + +private: + TransactionId m_id; + std::error_code m_error; +}; + +class WalletExternalTransactionCreatedEvent : public WalletEvent +{ +public: + WalletExternalTransactionCreatedEvent(TransactionId transactionId) : m_id(transactionId) {}; + virtual ~WalletExternalTransactionCreatedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::externalTransactionCreated, m_id); + } +private: + TransactionId m_id; +}; + +class WalletSynchronizationProgressUpdatedEvent : public WalletEvent +{ +public: + WalletSynchronizationProgressUpdatedEvent(uint64_t current, uint64_t total, std::error_code result) : m_current(current), m_total(total), m_ec(result) {}; + virtual ~WalletSynchronizationProgressUpdatedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::synchronizationProgressUpdated, m_current, m_total, m_ec); + } + +private: + uint64_t m_current; + uint64_t m_total; + std::error_code m_ec; +}; + + +class WalletActualBalanceUpdatedEvent : public WalletEvent +{ +public: + WalletActualBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {}; + virtual ~WalletActualBalanceUpdatedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::actualBalanceUpdated, m_balance); + } +private: + uint64_t m_balance; +}; + +class WalletPendingBalanceUpdatedEvent : public WalletEvent +{ +public: + WalletPendingBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {}; + virtual ~WalletPendingBalanceUpdatedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::pendingBalanceUpdated, m_balance); + } +private: + uint64_t m_balance; +}; + +} /* namespace CryptoNote */ + diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h new file mode 100644 index 0000000000..d3b43df77a --- /dev/null +++ b/src/wallet/WalletRequest.h @@ -0,0 +1,95 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "INode.h" + +#include "WalletSynchronizationContext.h" +#include "WalletSendTransactionContext.h" +#include "WalletEvent.h" + +namespace CryptoNote { + +class WalletRequest +{ +public: + typedef std::function >& events, boost::optional >& nextRequest, std::error_code ec)> Callback; + + virtual ~WalletRequest() {}; + + virtual void perform(INode& node, std::function cb) = 0; +}; + +class WalletGetNewBlocksRequest: public WalletRequest +{ +public: + WalletGetNewBlocksRequest(const std::list& knownBlockIds, std::shared_ptr context, Callback cb) : m_ids(knownBlockIds), m_context(context), m_cb(cb) {}; + virtual ~WalletGetNewBlocksRequest() {}; + + virtual void perform(INode& node, std::function cb) + { + node.getNewBlocks(std::move(m_ids), std::ref(m_context->newBlocks), std::ref(m_context->startHeight), std::bind(cb, m_cb, std::placeholders::_1)); + }; + +private: + std::shared_ptr m_context; + std::list m_ids; + Callback m_cb; +}; + +class WalletGetTransactionOutsGlobalIndicesRequest: public WalletRequest +{ +public: + WalletGetTransactionOutsGlobalIndicesRequest(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, Callback cb) : m_hash(transactionHash), m_outs(outsGlobalIndices), m_cb(cb) {}; + virtual ~WalletGetTransactionOutsGlobalIndicesRequest() {}; + + virtual void perform(INode& node, std::function cb) + { + node.getTransactionOutsGlobalIndices(m_hash, std::ref(m_outs), std::bind(cb, m_cb, std::placeholders::_1)); + }; + +private: + crypto::hash m_hash; + std::vector& m_outs; + Callback m_cb; +}; + +class WalletGetRandomOutsByAmountsRequest: public WalletRequest +{ +public: + WalletGetRandomOutsByAmountsRequest(const std::vector& amounts, uint64_t outsCount, std::shared_ptr context, Callback cb) : + m_amounts(amounts), m_outsCount(outsCount), m_context(context), m_cb(cb) {}; + + virtual ~WalletGetRandomOutsByAmountsRequest() {}; + + virtual void perform(INode& node, std::function cb) + { + node.getRandomOutsByAmounts(std::move(m_amounts), m_outsCount, std::ref(m_context->outs), std::bind(cb, m_cb, std::placeholders::_1)); + }; + +private: + std::vector m_amounts; + uint64_t m_outsCount; + std::shared_ptr m_context; + Callback m_cb; +}; + +class WalletRelayTransactionRequest: public WalletRequest +{ +public: + WalletRelayTransactionRequest(const cryptonote::transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {}; + virtual ~WalletRelayTransactionRequest() {}; + + virtual void perform(INode& node, std::function cb) + { + node.relayTransaction(m_tx, std::bind(cb, m_cb, std::placeholders::_1)); + } + +private: + cryptonote::transaction m_tx; + Callback m_cb; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h new file mode 100644 index 0000000000..796d5573a5 --- /dev/null +++ b/src/wallet/WalletSendTransactionContext.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include "cryptonote_core/cryptonote_basic.h" +#include "IWallet.h" + +namespace CryptoNote { + +struct TxDustPolicy +{ + uint64_t dustThreshold; + bool addToFee; + cryptonote::account_public_address addrForDust; + + TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address()) + : dustThreshold(a_dust_threshold) + , addToFee(an_add_to_fee) + , addrForDust(an_addr_for_dust) + { + } +}; + +struct SendTransactionContext +{ + TransactionId transactionId; + std::vector outs; + uint64_t foundMoney; + std::list selectedTransfers; + uint64_t unlockTimestamp; + TxDustPolicy dustPolicy; + uint64_t mixIn; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h new file mode 100644 index 0000000000..5d8e2a1a7f --- /dev/null +++ b/src/wallet/WalletSerialization.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include + +#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "common/unordered_containers_boost_serialization.h" +#include "storages/portable_storage_template_helper.h" + +BOOST_SERIALIZATION_SPLIT_FREE(cryptonote::account_base); + +namespace boost { +namespace serialization { + +template +inline void load(Archive & ar, cryptonote::account_base& account, const unsigned int version) +{ + std::string data; + ar >> data; + epee::serialization::load_t_from_binary(account, data); +} + +template +inline void save(Archive & ar, const cryptonote::account_base& account, const unsigned int version) +{ + std::string data; + epee::serialization::store_t_to_binary(account, data); + ar << data; +} + +template +inline void serialize(Archive & ar, CryptoNote::Transaction& tx, const unsigned int version) +{ + ar & tx.firstTransferId; + ar & tx.transferCount; + ar & tx.totalAmount; + ar & tx.fee; + ar & make_array(tx.hash.data(), tx.hash.size()); + ar & tx.isCoinbase; + ar & tx.blockHeight; + ar & tx.timestamp; + ar & tx.extra; +} + +template +inline void serialize(Archive & ar, CryptoNote::Transfer& tr, const unsigned int version) +{ + ar & tr.address; + ar & tr.amount; +} + +template +inline void serialize(Archive & ar, CryptoNote::TransferDetails& details, const unsigned int version) +{ + ar & details.blockHeight; + ar & details.tx; + ar & details.internalOutputIndex; + ar & details.globalOutputIndex; + ar & details.spent; + ar & details.keyImage; +} + +template +inline void serialize(Archive & ar, CryptoNote::UnconfirmedTransferDetails& details, const unsigned int version) +{ + ar & details.tx; + ar & details.change; + ar & details.sentTime; + ar & details.transactionId; +} + +} // namespace serialization +} // namespace boost diff --git a/src/wallet/WalletSynchronizationContext.h b/src/wallet/WalletSynchronizationContext.h new file mode 100644 index 0000000000..423da4944c --- /dev/null +++ b/src/wallet/WalletSynchronizationContext.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include + +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_basic.h" + +namespace CryptoNote { + +struct TransactionContextInfo +{ + std::vector requestedOuts; + std::vector globalIndices; + cryptonote::transaction transaction; + crypto::public_key transactionPubKey; +}; + +struct SynchronizationState +{ + SynchronizationState() : blockIdx(0), transactionIdx(0), minersTxProcessed(false) {} + size_t blockIdx; //block index within context->new_blocks array to be processed + size_t transactionIdx; //tx index within the block to be processed + bool minersTxProcessed; //is miner's tx in the block processed +}; + +struct SynchronizationContext +{ + std::list newBlocks; + uint64_t startHeight; + std::unordered_map transactionContext; + SynchronizationState progress; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletSynchronizer.cpp b/src/wallet/WalletSynchronizer.cpp new file mode 100644 index 0000000000..9b94815663 --- /dev/null +++ b/src/wallet/WalletSynchronizer.cpp @@ -0,0 +1,475 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "WalletSynchronizer.h" + +#include + +#include "WalletErrors.h" +#include "WalletUtils.h" + +namespace { + +void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) { + if (expr) + throw std::system_error(make_error_code(ec)); +} + +bool getTxPubKey(const cryptonote::transaction& tx, crypto::public_key& key) { + std::vector extraFields; + cryptonote::parse_tx_extra(tx.extra, extraFields); + + cryptonote::tx_extra_pub_key pubKeyField; + if(!cryptonote::find_tx_extra_field_by_type(extraFields, pubKeyField)) { + //Public key wasn't found in the transaction extra. Skipping transaction + return false; + } + + key = pubKeyField.pub_key; + return true; +} + +void findMyOuts(const cryptonote::account_keys& acc, const cryptonote::transaction& tx, const crypto::public_key& txPubKey, std::vector& outs, uint64_t& moneyTransfered) { + bool r = cryptonote::lookup_acc_outs(acc, tx, txPubKey, outs, moneyTransfered); + throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); +} + +uint64_t countOverallTxOutputs(const cryptonote::transaction& tx) { + uint64_t amount = 0; + for (const cryptonote::tx_out& o: tx.vout) { + amount += o.amount; + } + + return amount; +} + +uint64_t countOverallTxInputs(const cryptonote::transaction& tx) { + uint64_t amount = 0; + for (auto& in: tx.vin) { + if(in.type() != typeid(cryptonote::txin_to_key)) + continue; + + amount += boost::get(in).amount; + } + + return amount; +} + +void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::TransactionHash& hash) { + crypto::hash h = cryptonote::get_transaction_hash(tx); + memcpy(hash.data(), reinterpret_cast(&h), hash.size()); +} + +} + +namespace CryptoNote +{ + +WalletSynchronizer::WalletSynchronizer(const cryptonote::account_base& account, INode& node, std::vector& blockchain, + WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions, + WalletUserTransactionsCache& transactionsCache) : + m_account(account), + m_node(node), + m_blockchain(blockchain), + m_transferDetails(transferDetails), + m_unconfirmedTransactions(unconfirmedTransactions), + m_transactionsCache(transactionsCache), + m_actualBalance(0), + m_pendingBalance(0), + m_isStoping(false) { +} + +void WalletSynchronizer::stop() { + m_isStoping = true; +} + +std::shared_ptr WalletSynchronizer::makeStartRefreshRequest() { + std::shared_ptr context = std::make_shared(); + std::shared_ptr request = makeGetNewBlocksRequest(context); + + return request; +} + +void WalletSynchronizer::postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector& outsGlobalIndices, uint64_t height) { + parameters.nextRequest = std::make_shared(hash, outsGlobalIndices, + std::bind(&WalletSynchronizer::handleTransactionOutGlobalIndicesResponse, this, parameters.context, hash, height, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); +} + +std::shared_ptr WalletSynchronizer::makeGetNewBlocksRequest(std::shared_ptr context) { + context->newBlocks.clear(); + context->startHeight = 0; + context->progress = SynchronizationState(); + + std::list ids; + getShortChainHistory(ids); + + std::shared_ptrreq = std::make_shared(ids, context, std::bind(&WalletSynchronizer::handleNewBlocksPortion, this, + context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + + return req; +} + +void WalletSynchronizer::getShortChainHistory(std::list& ids) { + size_t i = 0; + size_t current_multiplier = 1; + size_t sz = m_blockchain.size(); + + if(!sz) + return; + + size_t current_back_offset = 1; + bool genesis_included = false; + + while(current_back_offset < sz) { + ids.push_back(m_blockchain[sz-current_back_offset]); + if(sz-current_back_offset == 0) + genesis_included = true; + if(i < 10) { + ++current_back_offset; + }else + { + current_back_offset += current_multiplier *= 2; + } + ++i; + } + + if(!genesis_included) + ids.push_back(m_blockchain[0]); +} + +void WalletSynchronizer::handleNewBlocksPortion(std::shared_ptr context, std::deque >& events, + boost::optional >& nextRequest, std::error_code ec) { + if (m_isStoping) { + return; + } + + if (ec) { + events.push_back(std::make_shared(context->startHeight, m_node.getLastLocalBlockHeight(), ec)); + return; + } + + ProcessParameters parameters; + parameters.context = context; + + try + { + bool fillRequest = processNewBlocks(parameters); + + if (fillRequest) { + parameters.nextRequest = makeGetNewBlocksRequest(context); + } + } + catch (std::system_error& e) { + parameters.nextRequest = boost::none; + parameters.events.push_back(std::make_shared(context->startHeight, m_node.getLastLocalBlockHeight(), e.code())); + } + catch (std::exception&) { + parameters.nextRequest = boost::none; + parameters.events.push_back(std::make_shared(context->startHeight, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + } + + refreshBalance(events); + + std::copy(parameters.events.begin(), parameters.events.end(), std::back_inserter(events)); + nextRequest = parameters.nextRequest; +} + +//returns true if new request should be performed +bool WalletSynchronizer::processNewBlocks(ProcessParameters& parameters) { + bool fillRequest = false; + std::shared_ptr context = parameters.context; + + size_t currentIndex = context->startHeight + context->progress.blockIdx; + + try + { + auto blocksIt = context->newBlocks.begin(); + std::advance(blocksIt, context->progress.blockIdx); + + for (; blocksIt != context->newBlocks.end(); ++blocksIt) { + if (m_isStoping) return false; + + auto& blockEntry = *blocksIt; + + NextBlockAction action = handleNewBlockchainEntry(parameters, blockEntry, currentIndex); + + if (action == INTERRUPT) + return false; + else if (action == CONTINUE) + fillRequest = true; + + ++context->progress.blockIdx; + ++currentIndex; + } + } + catch (std::exception& e) { + context->startHeight = currentIndex; + throw e; + } + + return fillRequest; +} + +WalletSynchronizer::NextBlockAction WalletSynchronizer::handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height) { + cryptonote::block b; + bool r = cryptonote::parse_and_validate_block_from_blob(blockEntry.block, b); + throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); + + crypto::hash blockId = get_block_hash(b); + if (height >= m_blockchain.size()) { + r = processNewBlockchainEntry(parameters, blockEntry, b, blockId, height); + + if (r) { + parameters.events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), std::error_code())); + return CONTINUE; + } + return INTERRUPT; + } + + if(blockId != m_blockchain[height]) { + //split detected here !!! + //Wrong daemon response + throwIf(height == parameters.context->startHeight, cryptonote::error::INTERNAL_WALLET_ERROR); + + detachBlockchain(height); + + r = processNewBlockchainEntry(parameters, blockEntry, b, blockId, height); + + if (r) { + parameters.events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), std::error_code())); + return CONTINUE; + } + return INTERRUPT; + } + + //we already have this block. + return SKIP; +} + +bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::block& b, crypto::hash& blockId, uint64_t height) { + throwIf(height != m_blockchain.size(), cryptonote::error::INTERNAL_WALLET_ERROR); + + if(b.timestamp + 60*60*24 > m_account.get_createtime()) { + if (!processMinersTx(parameters, b.miner_tx, height, b.timestamp)) + return false; + + auto txIt = blockEntry.txs.begin(); + std::advance(txIt, parameters.context->progress.transactionIdx); + + for (; txIt != blockEntry.txs.end(); ++txIt) { + auto& txblob = *txIt; + cryptonote::transaction tx; + + bool r = parse_and_validate_tx_from_blob(txblob, tx); + throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); + + r = processNewTransaction(parameters, tx, height, false, b.timestamp); + parameters.context->progress.transactionIdx++; + + if (!r) return false; + } + } + + parameters.context->progress.transactionIdx = 0; + + m_blockchain.push_back(blockId); + return true; +} + +bool WalletSynchronizer::processMinersTx(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp) { + bool r = true; + + if (!parameters.context->progress.minersTxProcessed) { + r = processNewTransaction(parameters, tx, height, true, timestamp); + parameters.context->progress.minersTxProcessed = true; + } + + return r; +} + +bool WalletSynchronizer::processNewTransaction(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp) { + bool res = true; + + processUnconfirmed(parameters, tx, height, timestamp); + std::vector outs; + uint64_t moneyInMyOuts = 0; + + crypto::public_key publicKey; + if (!getTxPubKey(tx, publicKey)) + return true; //Public key wasn't found in the transaction extra. Skipping transaction + + findMyOuts(m_account.get_keys(), tx, publicKey, outs, moneyInMyOuts); + + if(!outs.empty() && moneyInMyOuts) { + fillGetTransactionOutsGlobalIndicesRequest(parameters, tx, outs, publicKey, height); + res = false; + } + + uint64_t moneyInMyInputs = processMyInputs(tx); + + if (!moneyInMyOuts && !moneyInMyInputs) + return res; //There's nothing related to our account, skip it + + updateTransactionsCache(parameters, tx, moneyInMyOuts, moneyInMyInputs, height, isCoinbase, timestamp); + + return res; +} + +uint64_t WalletSynchronizer::processMyInputs(const cryptonote::transaction& tx) { + uint64_t money = 0; + // check all outputs for spending (compare key images) + for (auto& in: tx.vin) { + if(in.type() != typeid(cryptonote::txin_to_key)) + continue; + + size_t idx; + if (!m_transferDetails.getTransferDetailsIdxByKeyImage(boost::get(in).k_image, idx)) + continue; + + money += boost::get(in).amount; + + TransferDetails& td = m_transferDetails.getTransferDetails(idx); + td.spent = true; + } + + return money; +} + +void WalletSynchronizer::fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::transaction& tx, + const std::vector& outs, const crypto::public_key& publicKey, uint64_t height) { + crypto::hash txid = cryptonote::get_transaction_hash(tx); + + TransactionContextInfo tx_context; + tx_context.requestedOuts = outs; + tx_context.transaction = tx; + tx_context.transactionPubKey = publicKey; + + auto insert_result = parameters.context->transactionContext.emplace(txid, std::move(tx_context)); + + postGetTransactionOutsGlobalIndicesRequest(parameters, txid, insert_result.first->second.globalIndices, height); +} + +void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, bool isCoinbase, uint64_t timestamp) { + + uint64_t allOuts = countOverallTxOutputs(tx); + uint64_t allInputs = countOverallTxInputs(tx); + + TransactionId foundTx = m_transactionsCache.findTransactionByHash(cryptonote::get_transaction_hash(tx)); + if (foundTx == INVALID_TRANSACTION_ID) { + Transaction transaction; + transaction.firstTransferId = INVALID_TRANSFER_ID; + transaction.transferCount = 0; + transaction.totalAmount = myOuts - myInputs; + transaction.fee = isCoinbase ? 0 : allInputs - allOuts; + fillTransactionHash(tx, transaction.hash); + transaction.blockHeight = height; + transaction.isCoinbase = isCoinbase; + transaction.timestamp = timestamp; + + TransactionId newId = m_transactionsCache.insertTransaction(std::move(transaction)); + + parameters.events.push_back(std::make_shared(newId)); + } + else + { + Transaction& transaction = m_transactionsCache.getTransaction(foundTx); + transaction.blockHeight = height; + transaction.timestamp = timestamp; + transaction.isCoinbase = isCoinbase; + + parameters.events.push_back(std::make_shared(foundTx)); + } +} + +void WalletSynchronizer::processUnconfirmed(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp) { + TransactionId id; + crypto::hash hash = get_transaction_hash(tx); + + if (!m_unconfirmedTransactions.findTransactionId(hash, id)) + return; + + Transaction& tr = m_transactionsCache.getTransaction(id); + tr.blockHeight = height; + tr.timestamp = timestamp; + + m_unconfirmedTransactions.erase(hash); + + parameters.events.push_back(std::make_shared(id)); +} + +void WalletSynchronizer::handleTransactionOutGlobalIndicesResponse(std::shared_ptr context, crypto::hash txid, uint64_t height, + std::deque >& events, boost::optional >& nextRequest, std::error_code ec) { + if (m_isStoping) { + return; + } + + if (ec) { + events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), ec)); + return; + } + + try + { + auto it = context->transactionContext.find(txid); + if (it == context->transactionContext.end()) { + events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + return; + } + + cryptonote::transaction& tx = it->second.transaction; + std::vector& outs = it->second.requestedOuts; + crypto::public_key& tx_pub_key = it->second.transactionPubKey; + std::vector& global_indices = it->second.globalIndices; + + for (size_t o: outs) { + throwIf(tx.vout.size() <= o, cryptonote::error::INTERNAL_WALLET_ERROR); + + TransferDetails td; + td.blockHeight = height; + td.internalOutputIndex = o; + td.globalOutputIndex = global_indices[o]; + td.tx = tx; + td.spent = false; + cryptonote::keypair in_ephemeral; + cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.keyImage); + throwIf(in_ephemeral.pub != boost::get(tx.vout[o].target).key, cryptonote::error::INTERNAL_WALLET_ERROR); + + m_transferDetails.addTransferDetails(td); + } + + context->transactionContext.erase(it); + } + catch (std::exception&) { + events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + return; + } + + handleNewBlocksPortion(context, events, nextRequest, ec); +} + +void WalletSynchronizer::detachBlockchain(uint64_t height) { + m_transferDetails.detachTransferDetails(height); + + m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); + + m_transactionsCache.detachTransactions(height); +} + +void WalletSynchronizer::refreshBalance(std::deque >& events) { + uint64_t actualBalance = m_transferDetails.countActualBalance(); + uint64_t pendingBalance = m_unconfirmedTransactions.countPendingBalance(); + pendingBalance += m_transferDetails.countPendingBalance(); + + if (actualBalance != m_actualBalance) { + events.push_back(std::make_shared(actualBalance)); + m_actualBalance = actualBalance; + } + + if (pendingBalance != m_pendingBalance) { + events.push_back(std::make_shared(pendingBalance)); + m_pendingBalance = pendingBalance; + } +} + +} /* namespace CryptoNote */ diff --git a/src/wallet/WalletSynchronizer.h b/src/wallet/WalletSynchronizer.h new file mode 100644 index 0000000000..c86ee1310d --- /dev/null +++ b/src/wallet/WalletSynchronizer.h @@ -0,0 +1,83 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include + +#include "INode.h" +#include "crypto/hash.h" +#include "WalletTransferDetails.h" +#include "WalletUnconfirmedTransactions.h" +#include "WalletUserTransactionsCache.h" +#include "WalletEvent.h" +#include "WalletSynchronizationContext.h" +#include "WalletRequest.h" + +namespace CryptoNote { + +class WalletSynchronizer +{ +public: + WalletSynchronizer(const cryptonote::account_base& account, INode& node, std::vector& blockchain, WalletTransferDetails& transferDetails, + WalletUnconfirmedTransactions& unconfirmedTransactions, WalletUserTransactionsCache& transactionsCache); + + std::shared_ptr makeStartRefreshRequest(); + + void stop(); + +private: + + struct ProcessParameters { + std::shared_ptr context; + std::vector > events; + boost::optional > nextRequest; + }; + + enum NextBlockAction { + INTERRUPT = 0, + CONTINUE, + SKIP + }; + + void handleNewBlocksPortion(std::shared_ptr context, std::deque >& events, + boost::optional >& nextRequest, std::error_code ec); + void handleTransactionOutGlobalIndicesResponse(std::shared_ptr context, crypto::hash txid, uint64_t height, + std::deque >& events, boost::optional >& nextRequest, std::error_code ec); + + void getShortChainHistory(std::list& ids); + + bool processNewBlocks(ProcessParameters& parameters); + NextBlockAction handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height); + bool processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::block& b, crypto::hash& blockId, uint64_t height); + bool processNewTransaction(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp); + bool processMinersTx(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp); + void processUnconfirmed(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp); + uint64_t processMyInputs(const cryptonote::transaction& tx); + void updateTransactionsCache(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, + bool isCoinbase, uint64_t timestamp); + void detachBlockchain(uint64_t height); + void refreshBalance(std::deque >& events); + + void fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::transaction& tx, + const std::vector& outs, const crypto::public_key& publicKey, uint64_t height); + void postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector& outsGlobalIndices, uint64_t height); + std::shared_ptr makeGetNewBlocksRequest(std::shared_ptr context); + + const cryptonote::account_base& m_account; + INode& m_node; + std::vector& m_blockchain; + WalletTransferDetails& m_transferDetails; + WalletUnconfirmedTransactions& m_unconfirmedTransactions; + WalletUserTransactionsCache& m_transactionsCache; + + uint64_t m_actualBalance; + uint64_t m_pendingBalance; + + bool m_isStoping; +}; + +} /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp new file mode 100644 index 0000000000..737a769cc8 --- /dev/null +++ b/src/wallet/WalletTransactionSender.cpp @@ -0,0 +1,299 @@ +/* + * WalletTransactionSender.cpp + * + * Created on: 18 Ð¸ÑŽÐ½Ñ 2014 г. + * Author: milo + */ + +#include "WalletTransactionSender.h" +#include "WalletUtils.h" + +namespace { + +uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { + uint64_t needed_money = fee; + for (auto& transfer: transfers) { + CryptoNote::throwIf(transfer.amount == 0, cryptonote::error::ZERO_DESTINATION); + CryptoNote::throwIf(transfer.amount < 0, cryptonote::error::WRONG_AMOUNT); + + needed_money += transfer.amount; + CryptoNote::throwIf(static_cast(needed_money) < transfer.amount, cryptonote::error::SUM_OVERFLOW); + } + + return needed_money; +} + +void createChangeDestinations(const cryptonote::account_public_address& address, uint64_t neededMoney, uint64_t foundMoney, cryptonote::tx_destination_entry& changeDts) { + if (neededMoney < foundMoney) { + changeDts.addr = address; + changeDts.amount = foundMoney - neededMoney; + } +} + +void constructTx(const cryptonote::account_keys keys, const std::vector& sources, const std::vector& splittedDests, + const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, cryptonote::transaction& tx) { + std::vector extraVec; + extraVec.reserve(extra.size()); + std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);}); + + bool r = cryptonote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp); + CryptoNote::throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); + CryptoNote::throwIf(cryptonote::get_object_blobsize(tx) >= sizeLimit, cryptonote::error::TRANSACTION_SIZE_TOO_BIG); +} + +void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::TransactionHash& hash) { + crypto::hash h = cryptonote::get_transaction_hash(tx); + memcpy(hash.data(), reinterpret_cast(&h), hash.size()); +} + +} //namespace + +namespace CryptoNote { + +WalletTransactionSender::WalletTransactionSender(WalletUserTransactionsCache& transactionsCache, WalletTxSendingState& sendingTxsStates, + WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions): + m_transactionsCache(transactionsCache), + m_sendingTxsStates(sendingTxsStates), + m_transferDetails(transferDetails), + m_unconfirmedTransactions(unconfirmedTransactions), + m_isInitialized(false), + m_isStoping(false) { + m_upperTransactionSizeLimit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; +} + +void WalletTransactionSender::init(cryptonote::account_keys keys) { + if (m_isInitialized) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + m_keys = keys; + m_isInitialized = true; +} + +void WalletTransactionSender::stop() { + m_isStoping = true; +} + +std::shared_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, + const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { + if (!m_isInitialized) + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + + using namespace cryptonote; + + std::shared_ptr context = std::make_shared(); + throwIf(transfers.empty(), cryptonote::error::ZERO_DESTINATION); + + TransferId firstTransferId = m_transactionsCache.insertTransfers(transfers); + + uint64_t neededMoney = countNeededMoney(fee, transfers); + + Transaction transaction; + transaction.firstTransferId = firstTransferId; + transaction.transferCount = transfers.size(); + transaction.totalAmount = neededMoney; + transaction.fee = fee; + transaction.isCoinbase = false; + transaction.timestamp = 0; + transaction.extra = extra; + transaction.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + + TransactionId txId = m_transactionsCache.insertTransaction(std::move(transaction)); + transactionId = txId; + m_sendingTxsStates.sending(txId); + + context->transactionId = txId; + context->unlockTimestamp = unlockTimestamp; + context->mixIn = mixIn; + + context->foundMoney = m_transferDetails.selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); + throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); + + if(context->mixIn) { + std::shared_ptr request = makeGetRandomOutsRequest(context); + return request; + } + + return doSendTransaction(context, events); +} + +std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest(std::shared_ptr context) { + uint64_t outsCount = context->mixIn + 1;// add one to make possible (if need) to skip real output key + std::vector amounts; + + for (auto idx: context->selectedTransfers) { + const TransferDetails& td = m_transferDetails.getTransferDetails(idx); + throwIf(td.tx.vout.size() <= td.internalOutputIndex, cryptonote::error::INTERNAL_WALLET_ERROR); + amounts.push_back(td.amount()); + } + + return std::make_shared(amounts, outsCount, context, std::bind(&WalletTransactionSender::sendTransactionRandomOutsByAmount, + this, context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); +} + +void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, + boost::optional >& nextRequest, std::error_code ec) { + if (m_isStoping) { + events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); + return; + } + + if (ec) { + events.push_back(std::make_shared(context->transactionId, ec)); + return; + } + + auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), [&] (cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); + + if (scanty_it != context->outs.end()) { + events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::MIXIN_COUNT_TOO_BIG))); + return; + } + + std::shared_ptr req = doSendTransaction(context, events); + if (req) + nextRequest = req; +} + +std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque >& events) { + if (m_isStoping) { + events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); + return std::shared_ptr(); + } + + try + { + Transaction& transaction = m_transactionsCache.getTransaction(context->transactionId); + + std::vector sources; + prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); + + cryptonote::tx_destination_entry changeDts = AUTO_VAL_INIT(changeDts); + createChangeDestinations(m_keys.m_account_address, transaction.totalAmount, context->foundMoney, changeDts); + + std::vector splittedDests; + splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); + + cryptonote::transaction tx; + constructTx(m_keys, sources, splittedDests, transaction.extra, context->unlockTimestamp, m_upperTransactionSizeLimit, tx); + + fillTransactionHash(tx, transaction.hash); + + m_unconfirmedTransactions.add(tx, context->transactionId, changeDts.amount); + notifyBalanceChanged(events); + + return std::make_shared(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context->transactionId, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + } + catch(std::system_error& ec) { + events.push_back(std::make_shared(context->transactionId, ec.code())); + } + catch(std::exception&) { + events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + } + + return std::shared_ptr(); +} + +void WalletTransactionSender::relayTransactionCallback(TransactionId txId, std::deque >& events, + boost::optional >& nextRequest, std::error_code ec) { + if (m_isStoping) return; + + if (ec) { + m_sendingTxsStates.error(txId); + } else { + m_sendingTxsStates.sent(txId); + } + + events.push_back(std::make_shared(txId, ec)); +} + + +void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests) { + uint64_t dust = 0; + + digitSplitStrategy(firstTransferId, transfersCount, changeDts, dustPolicy.dustThreshold, splittedDests, dust); + + throwIf(dustPolicy.dustThreshold < dust, cryptonote::error::INTERNAL_WALLET_ERROR); + if (0 != dust && !dustPolicy.addToFee) { + splittedDests.push_back(cryptonote::tx_destination_entry(dust, dustPolicy.addrForDust)); + } +} + + +void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, + const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust) { + splitted_dsts.clear(); + dust = 0; + + for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) { + Transfer& de = m_transactionsCache.getTransfer(idx); + + cryptonote::account_public_address addr; + if (!cryptonote::get_account_address_from_str(addr, de.address)) { + throw std::system_error(make_error_code(cryptonote::error::BAD_ADDRESS)); + } + + cryptonote::decompose_amount_into_digits(de.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, addr)); }, + [&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, addr)); } ); + } + + cryptonote::decompose_amount_into_digits(change_dst.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr)); }, + [&](uint64_t a_dust) { dust = a_dust; } ); +} + + +void WalletTransactionSender::prepareInputs(const std::list& selectedTransfers, std::vector& outs, + std::vector& sources, uint64_t mixIn) { + size_t i = 0; + for (size_t idx: selectedTransfers) { + sources.resize(sources.size()+1); + cryptonote::tx_source_entry& src = sources.back(); + TransferDetails& td = m_transferDetails.getTransferDetails(idx); + src.amount = td.amount(); + + //paste mixin transaction + if(outs.size()) { + outs[i].outs.sort([](const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); + for (auto& daemon_oe: outs[i].outs) { + if(td.globalOutputIndex == daemon_oe.global_amount_index) + continue; + cryptonote::tx_source_entry::output_entry oe; + oe.first = daemon_oe.global_amount_index; + oe.second = daemon_oe.out_key; + src.outputs.push_back(oe); + if(src.outputs.size() >= mixIn) + break; + } + } + + //paste real transaction to the random index + auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; }); + + cryptonote::tx_source_entry::output_entry real_oe; + real_oe.first = td.globalOutputIndex; + real_oe.second = boost::get(td.tx.vout[td.internalOutputIndex].target).key; + + auto interted_it = src.outputs.insert(it_to_insert, real_oe); + + src.real_out_tx_key = get_tx_pub_key_from_extra(td.tx); + src.real_output = interted_it - src.outputs.begin(); + src.real_output_in_tx_index = td.internalOutputIndex; + ++i; + } +} + +void WalletTransactionSender::notifyBalanceChanged(std::deque >& events) { + uint64_t actualBalance = m_transferDetails.countActualBalance(); + uint64_t pendingBalance = m_unconfirmedTransactions.countPendingBalance(); + pendingBalance += m_transferDetails.countPendingBalance(); + + events.push_back(std::make_shared(actualBalance)); + events.push_back(std::make_shared(pendingBalance)); +} + +} /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h new file mode 100644 index 0000000000..9a51190c2a --- /dev/null +++ b/src/wallet/WalletTransactionSender.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "INode.h" +#include "WalletSendTransactionContext.h" +#include "WalletUserTransactionsCache.h" +#include "WalletTransferDetails.h" +#include "WalletUnconfirmedTransactions.h" +#include "WalletRequest.h" + +namespace CryptoNote { + +class WalletTransactionSender +{ +public: + WalletTransactionSender(WalletUserTransactionsCache& transactionsCache, WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails, + WalletUnconfirmedTransactions& unconfirmedTransactions); + + void init(cryptonote::account_keys keys); + void stop(); + + std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, + uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + +private: + std::shared_ptr makeGetRandomOutsRequest(std::shared_ptr context); + std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque >& events); + void prepareInputs(const std::list& selectedTransfers, std::vector& outs, + std::vector& sources, uint64_t mixIn); + void splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests); + void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust); + void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, + boost::optional >& nextRequest, std::error_code ec); + void relayTransactionCallback(TransactionId txId, std::deque >& events, + boost::optional >& nextRequest, std::error_code ec); + void notifyBalanceChanged(std::deque >& events); + + cryptonote::account_keys m_keys; + WalletUserTransactionsCache& m_transactionsCache; + WalletTxSendingState& m_sendingTxsStates; + WalletTransferDetails& m_transferDetails; + WalletUnconfirmedTransactions& m_unconfirmedTransactions; + uint64_t m_upperTransactionSizeLimit; + + bool m_isInitialized; + bool m_isStoping; +}; + +} /* namespace CryptoNote */ + diff --git a/src/wallet/WalletTransferDetails.cpp b/src/wallet/WalletTransferDetails.cpp new file mode 100644 index 0000000000..8c05ce8b28 --- /dev/null +++ b/src/wallet/WalletTransferDetails.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "WalletTransferDetails.h" + +#include +#include +#include + +#include + +#include "WalletErrors.h" + +#define DEFAULT_TX_SPENDABLE_AGE 10 + +namespace { + +template +T popRandomValue(URNG& randomGenerator, std::vector& vec) { + CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty"); + + std::uniform_int_distribution distribution(0, vec.size() - 1); + size_t idx = distribution(randomGenerator); + + T res = vec[idx]; + if (idx + 1 != vec.size()) { + vec[idx] = vec.back(); + } + vec.resize(vec.size() - 1); + + return res; +} + +} + +namespace CryptoNote +{ + +WalletTransferDetails::WalletTransferDetails(const std::vector& blockchain) : m_blockchain(blockchain) { +} + +WalletTransferDetails::~WalletTransferDetails() { +} + +TransferDetails& WalletTransferDetails::getTransferDetails(size_t idx) { + return m_transfers.at(idx); +} + +void WalletTransferDetails::addTransferDetails(const TransferDetails& details) { + m_transfers.push_back(details); + size_t idx = m_transfers.size() - 1; + + m_keyImages.insert(std::make_pair(details.keyImage, idx)); +} + +bool WalletTransferDetails::getTransferDetailsIdxByKeyImage(const crypto::key_image& image, size_t& idx) { + auto it = m_keyImages.find(image); + if (it == m_keyImages.end()) + return false; + + idx = it->second; + return true; +} + +bool WalletTransferDetails::isTxSpendtimeUnlocked(uint64_t unlockTime) const +{ + if(unlockTime < CRYPTONOTE_MAX_BLOCK_NUMBER) { + //interpret as block index + if(m_blockchain.size()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlockTime) + return true; + else + return false; + } + else + { + //interpret as time + uint64_t current_time = static_cast(time(NULL)); + if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlockTime) + return true; + else + return false; + } + return false; +} + +bool WalletTransferDetails::isTransferUnlocked(const TransferDetails& td) const +{ + if(!isTxSpendtimeUnlocked(td.tx.unlock_time)) + return false; + + if(td.blockHeight + DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size()) + return false; + + return true; +} + +uint64_t WalletTransferDetails::countActualBalance() const +{ + uint64_t amount = 0; + for (auto& transfer: m_transfers) { + if(!transfer.spent && isTransferUnlocked(transfer)) + amount += transfer.amount(); + } + + return amount; +} + +uint64_t WalletTransferDetails::countPendingBalance() const +{ + uint64_t amount = 0; + for (auto& td: m_transfers) { + if (!td.spent) + amount += td.amount(); + } + + return amount; +} + +uint64_t WalletTransferDetails::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers) { + std::vector unusedTransfers; + std::vector unusedDust; + + for (size_t i = 0; i < m_transfers.size(); ++i) { + const TransferDetails& td = m_transfers[i]; + if (!td.spent && isTransferUnlocked(td)) { + if (dust < td.amount()) + unusedTransfers.push_back(i); + else + unusedDust.push_back(i); + } + } + + std::default_random_engine randomGenerator(crypto::rand()); + bool selectOneDust = addDust && !unusedDust.empty(); + uint64_t foundMoney = 0; + while (foundMoney < neededMoney && (!unusedTransfers.empty() || !unusedDust.empty())) { + size_t idx; + if (selectOneDust) { + idx = popRandomValue(randomGenerator, unusedDust); + selectOneDust = false; + } + else + { + idx = !unusedTransfers.empty() ? popRandomValue(randomGenerator, unusedTransfers) : popRandomValue(randomGenerator, unusedDust); + } + + selectedTransfers.push_back(idx); + foundMoney += m_transfers[idx].amount(); + } + + return foundMoney; +} + +void WalletTransferDetails::detachTransferDetails(size_t height) { + auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const TransferDetails& td){return td.blockHeight >= height;}); + size_t start = it - m_transfers.begin(); + + for(size_t i = start; i!= m_transfers.size();i++) { + auto ki = m_keyImages.find(m_transfers[i].keyImage); + if(ki == m_keyImages.end()) throw std::system_error(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + + m_keyImages.erase(ki); + } + m_transfers.erase(it, m_transfers.end()); +} + +} /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransferDetails.h b/src/wallet/WalletTransferDetails.h new file mode 100644 index 0000000000..7dbdae12a4 --- /dev/null +++ b/src/wallet/WalletTransferDetails.h @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +#include "cryptonote_core/cryptonote_format_utils.h" +#include "IWallet.h" + +namespace CryptoNote { + +struct TransferDetails +{ + uint64_t blockHeight; + cryptonote::transaction tx; + size_t internalOutputIndex; + uint64_t globalOutputIndex; + bool spent; + crypto::key_image keyImage; + + uint64_t amount() const + { + return tx.vout[internalOutputIndex].amount; + } +}; + +class WalletTransferDetails +{ +public: + WalletTransferDetails(const std::vector& blockchain); + ~WalletTransferDetails(); + + TransferDetails& getTransferDetails(size_t idx); + void addTransferDetails(const TransferDetails& details); + bool getTransferDetailsIdxByKeyImage(const crypto::key_image& image, size_t& idx); + + uint64_t countActualBalance() const; + uint64_t countPendingBalance() const; + bool isTransferUnlocked(const TransferDetails& td) const; + + uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); + + void detachTransferDetails(size_t height); + + template + void save(Archive& ar, bool saveCache) const; + + template + void load(Archive& ar); + +private: + bool isTxSpendtimeUnlocked(uint64_t unlock_time) const; + + typedef std::vector TransferContainer; + TransferContainer m_transfers; + + typedef std::unordered_map KeyImagesContainer; + KeyImagesContainer m_keyImages; + + const std::vector& m_blockchain; +}; + +template +void WalletTransferDetails::save(Archive& ar, bool saveCache) const +{ + const TransferContainer& transfers = saveCache ? m_transfers : TransferContainer(); + const KeyImagesContainer& keyImages = saveCache ? m_keyImages : KeyImagesContainer(); + + ar << transfers; + ar << keyImages; +} + +template +void WalletTransferDetails::load(Archive& ar) +{ + ar >> m_transfers; + ar >> m_keyImages; +} + +} //namespace CryptoNote diff --git a/src/wallet/WalletTxSendingState.cpp b/src/wallet/WalletTxSendingState.cpp new file mode 100644 index 0000000000..fe1fda6c1d --- /dev/null +++ b/src/wallet/WalletTxSendingState.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "WalletTxSendingState.h" + +namespace CryptoNote { + +void WalletTxSendingState::sending(TransactionId id) { + m_states[id] = SENDING; +} + +void WalletTxSendingState::sent(TransactionId id) { + m_states.erase(id); +} + +void WalletTxSendingState::error(TransactionId id) { + m_states[id] = ERRORED; +} + +WalletTxSendingState::State WalletTxSendingState::state(TransactionId id) { + auto it = m_states.find(id); + + if (it == m_states.end()) + return NOT_FOUND; + + return it->second; +} +} //namespace CryptoNote diff --git a/src/wallet/WalletTxSendingState.h b/src/wallet/WalletTxSendingState.h new file mode 100644 index 0000000000..65ba7d30b9 --- /dev/null +++ b/src/wallet/WalletTxSendingState.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "IWallet.h" + +#include +#include + +namespace CryptoNote { + +class WalletTxSendingState +{ +public: + enum State + { + SENDING, + ERRORED, + NOT_FOUND + }; + + void sending(TransactionId id); + void sent(TransactionId id); + void error(TransactionId id); + State state(TransactionId id); + +private: + std::map m_states; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp new file mode 100644 index 0000000000..53a632cb36 --- /dev/null +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "WalletUnconfirmedTransactions.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +namespace CryptoNote { + +bool WalletUnconfirmedTransactions::findTransactionId(const crypto::hash& hash, TransactionId& id) { + auto it = m_unconfirmedTxs.find(hash); + + if(it == m_unconfirmedTxs.end()) + return false; + + id = it->second.transactionId; + + return true; +} + +void WalletUnconfirmedTransactions::erase(const crypto::hash& hash) { + m_unconfirmedTxs.erase(hash); +} + +void WalletUnconfirmedTransactions::add(const cryptonote::transaction& tx, + TransactionId transactionId, uint64_t change_amount) { + UnconfirmedTransferDetails& utd = m_unconfirmedTxs[cryptonote::get_transaction_hash(tx)]; + + utd.change = change_amount; + utd.sentTime = time(NULL); + utd.tx = tx; + utd.transactionId = transactionId; +} + +uint64_t WalletUnconfirmedTransactions::countPendingBalance() const +{ + uint64_t amount = 0; + + for (auto& utx: m_unconfirmedTxs) + amount+= utx.second.change; + + return amount; +} + +} /* namespace CryptoNote */ diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h new file mode 100644 index 0000000000..bfd99634de --- /dev/null +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +#include + +#include "IWallet.h" +#include "crypto/hash.h" +#include "cryptonote_core/cryptonote_basic.h" + +namespace CryptoNote { + +struct UnconfirmedTransferDetails +{ + cryptonote::transaction tx; + uint64_t change; + time_t sentTime; + TransactionId transactionId; +}; + +class WalletUnconfirmedTransactions +{ +public: + template + void save(Archive& ar, bool saveCache) const; + + template + void load(Archive& ar); + + bool findTransactionId(const crypto::hash& hash, TransactionId& id); + void erase(const crypto::hash& hash); + void add(const cryptonote::transaction& tx, TransactionId transactionId, uint64_t change_amount); + + uint64_t countPendingBalance() const; + +private: + typedef std::unordered_map UnconfirmedTxsContainer; + UnconfirmedTxsContainer m_unconfirmedTxs; +}; + +template +void WalletUnconfirmedTransactions::save(Archive& ar, bool saveCache) const +{ + const UnconfirmedTxsContainer& unconfirmedTxs = saveCache ? m_unconfirmedTxs : UnconfirmedTxsContainer(); + ar << unconfirmedTxs; +} + +template +void WalletUnconfirmedTransactions::load(Archive& ar) +{ + ar >> m_unconfirmedTxs; +} + +} // namespace CryptoNote + diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp new file mode 100644 index 0000000000..f0adbc98c4 --- /dev/null +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -0,0 +1,166 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "WalletUserTransactionsCache.h" + +#include + +namespace { +bool hashesEqual(const CryptoNote::TransactionHash& h1, const crypto::hash& h2) { + return !memcmp(static_cast(h1.data()), static_cast(&h2), h1.size()); +} +} + +namespace CryptoNote { + +size_t WalletUserTransactionsCache::getTransactionCount() const +{ + return m_transactions.size(); +} + +size_t WalletUserTransactionsCache::getTransferCount() const +{ + return m_transfers.size(); +} + +TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferId transferId) const +{ + TransactionId id; + for (id = 0; id < m_transactions.size(); ++id) { + const Transaction& tx = m_transactions[id]; + + if (tx.firstTransferId == INVALID_TRANSFER_ID || tx.transferCount == 0) + continue; + + if (transferId >= tx.firstTransferId && transferId < (tx.firstTransferId + tx.transferCount)) + break; + } + + if (id == m_transactions.size()) + return INVALID_TRANSACTION_ID; + + return id; +} + +bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, Transaction& transaction) const +{ + if (transactionId >= m_transactions.size()) + return false; + + transaction = m_transactions[transactionId]; + + return true; +} + +bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& transfer) const +{ + if (transferId >= m_transfers.size()) + return false; + + transfer = m_transfers[transferId]; + + return true; +} + +TransactionId WalletUserTransactionsCache::insertTransaction(Transaction&& transaction) { + m_transactions.emplace_back(transaction); + return m_transactions.size() - 1; +} + +TransactionId WalletUserTransactionsCache::findTransactionByHash(const crypto::hash& hash) { + auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const Transaction& tx) { return hashesEqual(tx.hash, hash); }); + + if (it == m_transactions.end()) + return CryptoNote::INVALID_TRANSACTION_ID; + + return std::distance(m_transactions.begin(), it); +} + +void WalletUserTransactionsCache::detachTransactions(uint64_t height) { + for (size_t id = 0; id < m_transactions.size(); ++id) { + Transaction& tx = m_transactions[id]; + if (tx.blockHeight >= height) { + tx.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + tx.timestamp = 0; + } + } +} + +Transaction& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { + return m_transactions.at(transactionId); +} + +void WalletUserTransactionsCache::getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) { + size_t offset = 0; + + for (size_t txId = 0; txId < m_transactions.size(); ++txId) { + WalletTxSendingState::State state = m_sendingTxsStates.state(txId); + bool isGood = state != WalletTxSendingState::ERRORED; + + if (isGood) { + getGoodTransaction(txId, offset, saveDetailed, transactions, transfers); + } + else + { + const Transaction& t = m_transactions[txId]; + if (t.firstTransferId != INVALID_TRANSFER_ID) + offset += t.transferCount; + } + } +} + +void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) { + transactions.push_back(m_transactions[txId]); + Transaction& tx = transactions.back(); + + if (!saveDetailed) { + tx.firstTransferId = INVALID_TRANSFER_ID; + tx.transferCount = 0; + + return; + } + + if (tx.firstTransferId == INVALID_TRANSFER_ID) { + return; + } + + UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId; + UserTransfers::const_iterator last = first + tx.transferCount; + + tx.firstTransferId -= offset; + + std::copy(first, last, std::back_inserter(transfers)); +} + +void WalletUserTransactionsCache::getGoodTransfers(UserTransfers& transfers) { + for (size_t txId = 0; txId < m_transactions.size(); ++txId) { + WalletTxSendingState::State state = m_sendingTxsStates.state(txId); + + if (state != WalletTxSendingState::ERRORED) { + getTransfersByTx(txId, transfers); + } + } +} + +void WalletUserTransactionsCache::getTransfersByTx(TransactionId id, UserTransfers& transfers) { + const Transaction& tx = m_transactions[id]; + + if (tx.firstTransferId != INVALID_TRANSFER_ID) { + UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId; + UserTransfers::const_iterator last = first + tx.transferCount; + std::copy(first, last, std::back_inserter(transfers)); + } +} + +TransferId WalletUserTransactionsCache::insertTransfers(const std::vector& transfers) { + std::copy(transfers.begin(), transfers.end(), std::back_inserter(m_transfers)); + return m_transfers.size() - transfers.size(); +} + +Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { + return m_transfers.at(transferId); +} + +} //namespace CryptoNote + diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h new file mode 100644 index 0000000000..48971bfdde --- /dev/null +++ b/src/wallet/WalletUserTransactionsCache.h @@ -0,0 +1,77 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "crypto/hash.h" +#include "IWallet.h" +#include "WalletTxSendingState.h" + +namespace CryptoNote { + +class WalletUserTransactionsCache +{ +public: + WalletUserTransactionsCache(WalletTxSendingState& states) : m_sendingTxsStates(states) {} + + template + void save(Archive& ar, bool saveDetailed, bool saveCache); + + template + void load(Archive& ar); + + size_t getTransactionCount() const; + size_t getTransferCount() const; + + TransactionId findTransactionByTransferId(TransferId transferId) const; + + bool getTransaction(TransactionId transactionId, Transaction& transaction) const; + Transaction& getTransaction(TransactionId transactionId); + bool getTransfer(TransferId transferId, Transfer& transfer) const; + Transfer& getTransfer(TransferId transferId); + + TransactionId insertTransaction(Transaction&& transaction); + TransferId insertTransfers(const std::vector& transfers); + + TransactionId findTransactionByHash(const crypto::hash& hash); + void detachTransactions(uint64_t height); + +private: + typedef std::vector UserTransfers; + typedef std::vector UserTransactions; + + void getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers); + void getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers); + + void getGoodTransfers(UserTransfers& transfers); + void getTransfersByTx(TransactionId id, UserTransfers& transfers); + + UserTransactions m_transactions; + UserTransfers m_transfers; + + WalletTxSendingState& m_sendingTxsStates; +}; + +template +void WalletUserTransactionsCache::load(Archive& ar) { + ar >> m_transactions; + ar >> m_transfers; +} + +template +void WalletUserTransactionsCache::save(Archive& ar, bool saveDetailed, bool saveCache) { + UserTransactions txsToSave; + UserTransfers transfersToSave; + + if (saveCache) { + getGoodItems(saveDetailed, txsToSave, transfersToSave); + } else { + if (saveDetailed) getGoodTransfers(transfersToSave); + } + + ar << txsToSave; + ar << transfersToSave; +} + +} //namespace CryptoNote diff --git a/src/wallet/WalletUtils.h b/src/wallet/WalletUtils.h new file mode 100644 index 0000000000..46d57793ca --- /dev/null +++ b/src/wallet/WalletUtils.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include "WalletErrors.h" + +namespace CryptoNote { + +inline void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) +{ + if (expr) + throw std::system_error(make_error_code(ec)); +} + +} //namespace CryptoNote + diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 111b761171..7e57d601ec 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -12,6 +12,7 @@ using namespace epee; #include "wallet2.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_protocol/blobdatatype.h" #include "rpc/core_rpc_server_commands_defs.h" #include "misc_language.h" #include "cryptonote_core/cryptonote_basic_impl.h" @@ -387,7 +388,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p wallet2::keys_file_data keys_file_data = boost::value_initialized(); crypto::chacha8_key key; - crypto::generate_chacha8_key(password, key); + crypto::cn_context cn_context; + crypto::generate_chacha8_key(cn_context, password, key); std::string cipher; cipher.resize(account_data.size()); keys_file_data.iv = crypto::rand(); @@ -422,7 +424,8 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); crypto::chacha8_key key; - crypto::generate_chacha8_key(password, key); + crypto::cn_context cn_context; + crypto::generate_chacha8_key(cn_context, password, key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -465,6 +468,20 @@ void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists wallet_file_exists = boost::filesystem::exists(wallet_file, ignore); } //---------------------------------------------------------------------------------------------------- +bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) { + cryptonote::blobdata payment_id_data; + if (!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) { + return false; + } + + if (sizeof(crypto::hash) != payment_id_data.size()) { + return false; + } + + payment_id = *reinterpret_cast(payment_id_data.data()); + return true; +} +//---------------------------------------------------------------------------------------------------- bool wallet2::prepare_file_names(const std::string& file_path) { do_prepare_file_names(file_path, m_keys_file, m_wallet_file); @@ -663,7 +680,7 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t cha void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx) { - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(fee), tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx); } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d2016f6961..a2c961e83a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -145,6 +145,7 @@ namespace tools } static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists); + static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); private: bool store_keys(const std::string& keys_file_name, const std::string& password); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f1766c3b44..7c4c7e3d8c 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -73,13 +73,10 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) { - std::vector dsts; - for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) - { + for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { cryptonote::tx_destination_entry de; - if(!get_account_address_from_str(de.addr, it->address)) - { + if (!get_account_address_from_str(de.addr, it->address)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; return false; @@ -87,27 +84,41 @@ namespace tools de.amount = it->amount; dsts.push_back(de); } - try - { + + std::vector extra; + if (!req.payment_id.empty()) { + std::string payment_id_str = req.payment_id; + + crypto::hash payment_id; + if (!wallet2::parse_payment_id(payment_id_str, payment_id)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"; + return false; + } + + std::string extra_nonce; + cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"; + return false; + } + } + + try { cryptonote::transaction tx; - m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, std::vector(), tx); + m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx); res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(tx)); return true; - } - catch (const tools::error::daemon_busy& e) - { + } catch (const tools::error::daemon_busy& e) { er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; er.message = e.what(); return false; - } - catch (const std::exception& e) - { + } catch (const std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; er.message = e.what(); return false; - } - catch (...) - { + } catch (...) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; return false; diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index b99d92ca28..217d7a2121 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -24,8 +24,8 @@ namespace wallet_rpc struct response { - uint64_t balance; - uint64_t unlocked_balance; + uint64_t balance; + uint64_t unlocked_balance; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(balance) @@ -52,12 +52,14 @@ namespace wallet_rpc uint64_t fee; uint64_t mixin; uint64_t unlock_time; + std::string payment_id; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) KV_SERIALIZE(fee) KV_SERIALIZE(mixin) KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) END_KV_SERIALIZE_MAP() }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 855b4d8088..35beea1426 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,7 @@ add_definitions(-DSTATICLIB) add_subdirectory(gtest) -include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) +include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR} ../version) file(GLOB_RECURSE CORE_TESTS core_tests/*) file(GLOB_RECURSE CRYPTO_TESTS crypto/*) @@ -19,6 +19,8 @@ source_group(unit_tests FILES ${UNIT_TESTS}) # add_subdirectory(daemon_tests) +add_library(coretests_lib ${CORE_TESTS}) + add_executable(coretests ${CORE_TESTS}) add_executable(crypto-tests ${CRYPTO_TESTS}) add_executable(difficulty-tests difficulty/difficulty.cpp) @@ -38,16 +40,24 @@ target_link_libraries(functional_tests cryptonote_core wallet common crypto ${Bo target_link_libraries(hash-tests crypto) target_link_libraries(hash-target-tests crypto cryptonote_core) target_link_libraries(performance_tests cryptonote_core common crypto ${Boost_LIBRARIES}) -target_link_libraries(unit_tests cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) +target_link_libraries(unit_tests cryptonote_core common crypto wallet coretests_lib gtest_main ${Boost_LIBRARIES}) target_link_libraries(net_load_tests_clt cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) target_link_libraries(net_load_tests_srv cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) + + +file(GLOB_RECURSE NODE_RPC_PROXY_TEST node_rpc_proxy_test/*) +source_group(node_rpc_proxy_test FILES ${NODE_RPC_PROXY_TEST}) +add_executable(node_rpc_proxy_test ${NODE_RPC_PROXY_TEST}) +target_link_libraries(node_rpc_proxy_test node_rpc_proxy cryptonote_core common crypto ${Boost_LIBRARIES}) + + if(NOT MSVC) set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") endif() -add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests) -set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv PROPERTY FOLDER "tests") +add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests node_rpc_proxy_test) +set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test PROPERTY FOLDER "tests") add_test(coretests coretests --generate_and_play_test_data) add_test(crypto crypto-tests ${CMAKE_CURRENT_SOURCE_DIR}/crypto/tests.txt) diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index caf4bf8377..d618040703 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -41,7 +41,7 @@ int main(int argc, char* argv[]) #ifdef WIN32 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif +#endif TRY_ENTRY(); @@ -51,8 +51,8 @@ int main(int argc, char* argv[]) //set up logging options log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); //log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), log_space::log_singletone::get_default_log_folder().c_str()); @@ -98,14 +98,14 @@ int main(int argc, char* argv[]) //initialize core here LOG_PRINT_L0("Initializing proxy core..."); res = pr_core.init(vm); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); LOG_PRINT_L0("Core initialized OK"); LOG_PRINT_L0("Starting p2p net loop..."); p2psrv.run(); LOG_PRINT_L0("p2p net loop stopped"); - //deinitialize components + //deinitialize components LOG_PRINT_L0("Deinitializing core..."); pr_core.deinit(); LOG_PRINT_L0("Deinitializing cryptonote_protocol..."); @@ -172,7 +172,7 @@ bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_ crypto::hash lh; cout << "BLOCK" << endl << endl; cout << (h = get_block_hash(b)) << endl; - cout << (lh = get_block_longhash(b, 0)) << endl; + cout << (lh = get_block_longhash(m_cn_context, b, 0)) << endl; cout << get_transaction_hash(b.miner_tx) << endl; cout << ::get_object_blobsize(b.miner_tx) << endl; //cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl; @@ -200,7 +200,7 @@ bool tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_i bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) { generate_genesis_block(m_genesis); crypto::hash h = get_block_hash(m_genesis); - add_block(h, get_block_longhash(m_genesis, 0), m_genesis, block_to_blob(m_genesis)); + add_block(h, get_block_longhash(m_cn_context, m_genesis, 0), m_genesis, block_to_blob(m_genesis)); return true; } diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index d5be53f1da..0d70708995 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -34,9 +34,11 @@ namespace tests crypto::hash m_lastblk; std::list txes; + crypto::cn_context m_cn_context; + bool add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob); void build_short_history(std::list &m_history, const crypto::hash &m_start); - + public: void on_synchronized(){} diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 8e9a2c1dbf..a08cabeb34 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -120,8 +120,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev CHECK_EQ(MK_COINS(3), get_balance(m_recipient_account_4, chain, mtx)); std::list tx_pool; - r = c.get_pool_transactions(tx_pool); - CHECK_TEST_CONDITION(r); + c.get_pool_transactions(tx_pool); CHECK_EQ(1, tx_pool.size()); std::vector tx_outs; @@ -170,8 +169,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind CHECK_EQ(MK_COINS(16), get_balance(m_recipient_account_4, chain, mtx)); std::list tx_pool; - r = c.get_pool_transactions(tx_pool); - CHECK_TEST_CONDITION(r); + c.get_pool_transactions(tx_pool); CHECK_EQ(1, tx_pool.size()); CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front())); diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 124800db78..c7ae28b9a0 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -153,7 +153,8 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co // Nonce search... blk.nonce = 0; - while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height)) + crypto::cn_context context; + while (!miner::find_nonce_for_given_block(context, blk, get_test_difficulty(), height)) blk.timestamp++; add_block(blk, txs_size, block_sizes, already_generated_coins); @@ -479,7 +480,8 @@ void fill_tx_sources_and_destinations(const std::vector& event void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) { blk.nonce = 0; - while (!miner::find_nonce_for_given_block(blk, diffic, height)) + crypto::cn_context context; + while (!miner::find_nonce_for_given_block(context, blk, diffic, height)) blk.timestamp++; } diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 3d47edeef6..f17ae2896f 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -461,7 +461,7 @@ inline bool do_replay_events(std::vector& events) cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects cryptonote::core c(&pr); - if (!c.init(vm)) + if (!c.init(vm, false)) { std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl; return false; @@ -643,4 +643,4 @@ inline bool do_replay_file(const std::string& filename) #define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2) #define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2) #define MK_COINS(amount) (UINT64_C(amount) * COIN) -#define TESTS_DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) +#define TESTS_DEFAULT_FEE (MINIMUM_FEE) diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index e43cc2ed34..8242ba56b2 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -99,7 +99,7 @@ struct gen_double_spend_in_alt_chain_in_different_blocks : public gen_double_spe class gen_double_spend_in_different_chains : public test_chain_unit_base { public: - static const uint64_t send_amount = MK_COINS(17); + static const uint64_t send_amount = MK_COINS(31); static const size_t expected_blockchain_height = 4 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; gen_double_spend_in_different_chains(); diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 4c6d5b07f4..fe26d04be3 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -52,7 +52,7 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, try { - w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx); + w1.transfer(dsts, mix_in_factor, 0, MINIMUM_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx); return true; } catch (const std::exception&) @@ -157,7 +157,7 @@ bool transactions_flow_test(std::string& working_folder, BOOST_FOREACH(tools::wallet2::transfer_details& td, incoming_transfers) { cryptonote::transaction tx_s; - bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - DEFAULT_FEE, tx_s, 50); + bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - MINIMUM_FEE, tx_s, 50); CHECK_AND_ASSERT_MES(r, false, "Failed to send starter tx " << get_transaction_hash(tx_s)); LOG_PRINT_GREEN("Starter transaction sent " << get_transaction_hash(tx_s), LOG_LEVEL_0); if(++count >= FIRST_N_TRANSFERS) @@ -185,7 +185,7 @@ bool transactions_flow_test(std::string& working_folder, for(i = 0; i != transactions_count; i++) { uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money); - while(w1.unlocked_balance() < amount_to_tx + DEFAULT_FEE) + while(w1.unlocked_balance() < amount_to_tx + MINIMUM_FEE) { misc_utils::sleep_no_w(1000); LOG_PRINT_L0("not enough money, waiting for cashback or mining"); diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 6c898c9fc7..da5a2e229c 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -16,23 +16,30 @@ using namespace std; using namespace crypto; typedef crypto::hash chash; +cn_context *context; + +extern "C" { + PUSH_WARNINGS DISABLE_VS_WARNINGS(4297) -extern "C" { static void hash_tree(const void *data, size_t length, char *hash) { if ((length & 31) != 0) { throw ios_base::failure("Invalid input length for tree_hash"); } tree_hash((const char (*)[32]) data, length >> 5, hash); } -} POP_WARNINGS + static void slow_hash(const void *data, size_t length, char *hash) { + cn_slow_hash(*context, data, length, *reinterpret_cast(hash)); + } +} + extern "C" typedef void hash_f(const void *, size_t, char *); struct hash_func { const string name; hash_f &f; -} hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash}, {"tree", hash_tree}, +} hashes[] = {{"fast", cn_fast_hash}, {"slow", slow_hash}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}}; @@ -58,6 +65,9 @@ int main(int argc, char *argv[]) { break; } } + if (f == slow_hash) { + context = new cn_context(); + } input.open(argv[2], ios_base::in); for (;;) { ++test; diff --git a/tests/io.h b/tests/io.h index 64f2bae77d..e3efa62ca2 100644 --- a/tests/io.h +++ b/tests/io.h @@ -80,7 +80,7 @@ inline void get(std::istream &input, std::vector &res) { } } -#if !defined(_MSC_VER) +#if !defined(_MSC_VER) || _MSC_VER >= 1800 template typename std::enable_if<(sizeof...(TT) > 0), void>::type diff --git a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp new file mode 100644 index 0000000000..e3aa703784 --- /dev/null +++ b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp @@ -0,0 +1,132 @@ +#include +#include + +#include "include_base_utils.h" + +#include "node_rpc_proxy/NodeRpcProxy.h" + +using namespace cryptonote; +using namespace CryptoNote; + + +class NodeObserver : public INodeObserver { +public: + NodeObserver(const std::string& name, NodeRpcProxy& nodeProxy) + : m_name(name) + , m_nodeProxy(nodeProxy) { + } + + virtual ~NodeObserver() { + } + + virtual void peerCountUpdated(size_t count) { + LOG_PRINT_L0('[' << m_name << "] peerCountUpdated " << count << " = " << m_nodeProxy.getPeerCount()); + } + + virtual void localBlockchainUpdated(uint64_t height) { + LOG_PRINT_L0('[' << m_name << "] localBlockchainUpdated " << height << " = " << m_nodeProxy.getLastLocalBlockHeight()); + + std::vector amounts; + amounts.push_back(100000000); + auto outs = std::make_shared>(); + m_nodeProxy.getRandomOutsByAmounts(std::move(amounts), 10, *outs.get(), [outs](std::error_code ec) { + if (!ec) { + if (1 == outs->size() && 10 == (*outs)[0].outs.size()) { + LOG_PRINT_L0("getRandomOutsByAmounts called successfully"); + } else { + LOG_PRINT_RED_L0("getRandomOutsByAmounts returned invalid result"); + } + } else { + LOG_PRINT_RED_L0("failed to call getRandomOutsByAmounts: " << ec.message() << ':' << ec.value()); + } + }); + } + + virtual void lastKnownBlockHeightUpdated(uint64_t height) { + LOG_PRINT_L0('[' << m_name << "] lastKnownBlockHeightUpdated " << height << " = " << m_nodeProxy.getLastKnownBlockHeight()); + } + +private: + std::string m_name; + NodeRpcProxy& m_nodeProxy; +}; + +int main(int argc, const char** argv) { + //set up logging options + epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + + NodeRpcProxy nodeProxy("127.0.0.1", 18081); + + NodeObserver observer1("obs1", nodeProxy); + NodeObserver observer2("obs2", nodeProxy); + + nodeProxy.addObserver(&observer1); + nodeProxy.addObserver(&observer2); + + nodeProxy.init([](std::error_code ec) { + if (ec) { + LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + } else { + LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + } + }); + + //nodeProxy.init([](std::error_code ec) { + // if (ec) { + // LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + // } else { + // LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + // } + //}); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + if (nodeProxy.shutdown()) { + LOG_PRINT_GREEN("shutdown", LOG_LEVEL_0); + } else { + LOG_PRINT_RED_L0("shutdown error"); + } + + nodeProxy.init([](std::error_code ec) { + if (ec) { + LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + } else { + LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + if (nodeProxy.shutdown()) { + LOG_PRINT_GREEN("shutdown", LOG_LEVEL_0); + } else { + LOG_PRINT_RED_L0("shutdown error"); + } + + cryptonote::transaction tx; + nodeProxy.relayTransaction(tx, [](std::error_code ec) { + if (!ec) { + LOG_PRINT_L0("relayTransaction called successfully"); + } else { + LOG_PRINT_RED_L0("failed to call relayTransaction: " << ec.message() << ':' << ec.value()); + } + }); + + nodeProxy.init([](std::error_code ec) { + if (ec) { + LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + } else { + LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(5)); + nodeProxy.relayTransaction(tx, [](std::error_code ec) { + if (!ec) { + LOG_PRINT_L0("relayTransaction called successfully"); + } else { + LOG_PRINT_RED_L0("failed to call relayTransaction: " << ec.message() << ':' << ec.value()); + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(60)); +} diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index ec001326e3..14c4a9ba1b 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -35,11 +35,12 @@ class test_cn_slow_hash bool test() { crypto::hash hash; - crypto::cn_slow_hash(&m_data, sizeof(m_data), hash); + crypto::cn_slow_hash(m_context, &m_data, sizeof(m_data), hash); return hash == m_expected_hash; } private: data_t m_data; crypto::hash m_expected_hash; + crypto::cn_context m_context; }; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 72ee2ca6ca..2c65f1c2ca 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -4,6 +4,7 @@ #include "performance_tests.h" #include "performance_utils.h" +#include "crypto/hash.h" // tests #include "construct_tx.h" diff --git a/tests/unit_tests/INodeStubs.cpp b/tests/unit_tests/INodeStubs.cpp new file mode 100644 index 0000000000..fbc135c7e5 --- /dev/null +++ b/tests/unit_tests/INodeStubs.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "INodeStubs.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "wallet/WalletErrors.h" + +#include +#include +#include +#include + +#include "crypto/crypto.h" + +void INodeTrivialRefreshStub::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) +{ + std::thread task(&INodeTrivialRefreshStub::doGetNewBlocks, this, knownBlockIds, std::ref(newBlocks), std::ref(startHeight), callback); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) +{ + auto& blockchain = m_blockchainGenerator.getBlockchain(); + + startHeight = m_lastHeight; + + for (; m_lastHeight < blockchain.size(); ++m_lastHeight) + { + cryptonote::block_complete_entry e; + e.block = cryptonote::t_serializable_object_to_blob(blockchain[m_lastHeight]); + + for (auto hash: blockchain[m_lastHeight].tx_hashes) + { + cryptonote::transaction tx; + if (!m_blockchainGenerator.getTransactionByHash(hash, tx)) + continue; + + e.txs.push_back(t_serializable_object_to_blob(tx)); + } + + newBlocks.push_back(e); + } + + m_lastHeight = blockchain.size() - 1; + + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) +{ + std::thread task(&INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) +{ + outsGlobalIndices.resize(20); //random + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) +{ + std::thread task(&INodeTrivialRefreshStub::doRelayTransaction, this, transaction, callback); + task.detach(); +} + +void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback) +{ + if (m_nextTxError) + { + callback(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + m_nextTxError = false; + return; + } + m_blockchainGenerator.addTxToBlockchain(transaction); + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) +{ + std::thread task(&INodeTrivialRefreshStub::doGetRandomOutsByAmounts, this, amounts, outsCount, std::ref(result), callback); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback) +{ + for (uint64_t amount: amounts) + { + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount out; + out.amount = amount; + + for (uint64_t i = 0; i < outsCount; ++i) + { + crypto::public_key key; + crypto::secret_key sk; + generate_keys(key, sk); + + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry e; + e.global_amount_index = i; + e.out_key = key; + + out.outs.push_back(e); + } + } + + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::startAlternativeChain(uint64_t height) +{ + std::vector& blockchain = m_blockchainGenerator.getBlockchain(); + + assert(height < blockchain.size()); + assert(height > m_lastHeight); + + auto it = blockchain.begin(); + std::advance(it, height); + + blockchain.erase(it, blockchain.end()); + + m_lastHeight = height; +} + +void INodeTrivialRefreshStub::setNextTransactionError() +{ + m_nextTxError = true; +} diff --git a/tests/unit_tests/INodeStubs.h b/tests/unit_tests/INodeStubs.h new file mode 100644 index 0000000000..28ffa4fb56 --- /dev/null +++ b/tests/unit_tests/INodeStubs.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +#include "INode.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "TestBlockchainGenerator.h" + +class INodeDummyStub : public CryptoNote::INode +{ +public: + virtual bool addObserver(CryptoNote::INodeObserver* observer) { return true; }; + virtual bool removeObserver(CryptoNote::INodeObserver* observer) { return true; }; + + virtual void init(const CryptoNote::INode::Callback& callback) {callback(std::error_code());}; + virtual bool shutdown() { return true; }; + + virtual size_t getPeerCount() const { return 0; }; + virtual uint64_t getLastLocalBlockHeight() const { return 0; }; + virtual uint64_t getLastKnownBlockHeight() const { return 0; }; + + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) {callback(std::error_code());}; + + virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) {callback(std::error_code());}; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) {callback(std::error_code());}; + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); }; +}; + +class INodeTrivialRefreshStub : public INodeDummyStub +{ +public: + INodeTrivialRefreshStub(TestBlockchainGenerator& generator) : m_lastHeight(1), m_blockchainGenerator(generator), m_nextTxError(false) {}; + + virtual uint64_t getLastLocalBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; }; + virtual uint64_t getLastKnownBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; }; + + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + + virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + + virtual void startAlternativeChain(uint64_t height); + virtual void setNextTransactionError(); + +private: + void doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + void doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + void doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + + uint64_t m_lastHeight; + TestBlockchainGenerator& m_blockchainGenerator; + bool m_nextTxError; +}; diff --git a/tests/unit_tests/TestBlockchainGenerator.cpp b/tests/unit_tests/TestBlockchainGenerator.cpp new file mode 100644 index 0000000000..78f5ccd86d --- /dev/null +++ b/tests/unit_tests/TestBlockchainGenerator.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include "TestBlockchainGenerator.h" + +#include "../performance_tests/multi_tx_test_base.h" + +class TransactionForAddressCreator : public multi_tx_test_base<5> +{ + typedef multi_tx_test_base<5> base_class; +public: + TransactionForAddressCreator() {} + + bool init() + { + return base_class::init(); + } + + void generate(const cryptonote::account_public_address& address, cryptonote::transaction& tx) + { + cryptonote::tx_destination_entry destination(this->m_source_amount, address); + std::vector destinations; + destinations.push_back(destination); + + cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0); + } +}; + + +TestBlockchainGenerator::TestBlockchainGenerator() +{ + miner_acc.generate(); + addGenesisBlock(); +} + +std::vector& TestBlockchainGenerator::getBlockchain() +{ + return m_blockchain; +} + +bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cryptonote::transaction& tx) +{ + auto it = m_txs.find(hash); + if (it == m_txs.end()) + return false; + + tx = it->second; + return true; +} + +void TestBlockchainGenerator::addGenesisBlock() +{ + cryptonote::block genesis; + uint64_t timestamp = time(NULL); + + generator.construct_block(genesis, miner_acc, timestamp); + m_blockchain.push_back(genesis); +} + +void TestBlockchainGenerator::generateEmptyBlocks(size_t count) +{ + addGenesisBlock(); + + for (size_t i = 0; i < count; ++i) + { + cryptonote::block& prev_block = m_blockchain.back(); + cryptonote::block block; + generator.construct_block(block, prev_block, miner_acc); + m_blockchain.push_back(block); + } +} + +void TestBlockchainGenerator::addTxToBlockchain(const cryptonote::transaction& transaction) +{ + crypto::hash txHash = cryptonote::get_transaction_hash(transaction); + m_txs[txHash] = transaction; + + std::list txs; + txs.push_back(transaction); + + cryptonote::block& prev_block = m_blockchain.back(); + cryptonote::block block; + + generator.construct_block(block, prev_block, miner_acc, txs); + m_blockchain.push_back(block); +} + +bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::account_public_address& address) +{ + TransactionForAddressCreator creator; + if (!creator.init()) + return false; + + cryptonote::transaction tx; + creator.generate(address, tx); + + crypto::hash txHash = cryptonote::get_transaction_hash(tx); + m_txs[txHash] = tx; + + std::list txs; + txs.push_back(tx); + + cryptonote::block& prev_block = m_blockchain.back(); + cryptonote::block block; + + generator.construct_block(block, prev_block, miner_acc, txs); + m_blockchain.push_back(block); + + return true; +} + diff --git a/tests/unit_tests/TestBlockchainGenerator.h b/tests/unit_tests/TestBlockchainGenerator.h new file mode 100644 index 0000000000..0c41a509bd --- /dev/null +++ b/tests/unit_tests/TestBlockchainGenerator.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "../core_tests/chaingen.h" +#include +#include + +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash.h" + +class TestBlockchainGenerator +{ +public: + TestBlockchainGenerator(); + + std::vector& getBlockchain(); + void addGenesisBlock(); + void generateEmptyBlocks(size_t count); + bool getBlockRewardForAddress(const cryptonote::account_public_address& address); + void addTxToBlockchain(const cryptonote::transaction& transaction); + bool getTransactionByHash(const crypto::hash& hash, cryptonote::transaction& tx); + +private: + test_generator generator; + cryptonote::account_base miner_acc; + std::vector m_blockchain; + std::unordered_map m_txs; +}; diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index 4dd8775914..980582ab39 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -109,7 +109,7 @@ TEST(parse_and_validate_tx_extra, is_valid_tx_extra_parsed) cryptonote::account_base acc; acc.generate(); cryptonote::blobdata b = "dsdsdfsdfsf"; - ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1)); + ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, MINIMUM_FEE, acc.get_keys().m_account_address, tx, b, 1)); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx); ASSERT_NE(tx_pub_key, cryptonote::null_pkey); } @@ -119,7 +119,7 @@ TEST(parse_and_validate_tx_extra, fails_on_big_extra_nonce) cryptonote::account_base acc; acc.generate(); cryptonote::blobdata b(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); - ASSERT_FALSE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1)); + ASSERT_FALSE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, MINIMUM_FEE, acc.get_keys().m_account_address, tx, b, 1)); } TEST(parse_and_validate_tx_extra, fails_on_wrong_size_in_extra_nonce) { diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp new file mode 100644 index 0000000000..8bbd838b8c --- /dev/null +++ b/tests/unit_tests/test_wallet.cpp @@ -0,0 +1,744 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "gtest/gtest.h" + +#include +#include +#include + +#include "INode.h" +#include "wallet/Wallet.h" +#include "cryptonote_core/account.h" + +#include "INodeStubs.h" +#include "TestBlockchainGenerator.h" + +class TrivialWalletObserver : public CryptoNote::IWalletObserver +{ +public: + TrivialWalletObserver() {} + + bool waitForSyncEnd() { + auto future = syncPromise.get_future(); + return future.wait_for(std::chrono::seconds(3)) == std::future_status::ready; + } + + bool waitForSendEnd(std::error_code& ec) { + auto future = sendPromise.get_future(); + + if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { + return false; + } + + ec = future.get(); + return true; + + } + + bool waitForSaveEnd(std::error_code& ec) { + auto future = savePromise.get_future(); + + if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { + return false; + } + + ec = future.get(); + return true; + } + + bool waitForLoadEnd(std::error_code& ec) { + auto future = loadPromise.get_future(); + + if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { + return false; + } + + ec = future.get(); + return true; + } + + void reset() { + syncPromise = std::promise(); + sendPromise = std::promise(); + savePromise = std::promise(); + loadPromise = std::promise(); + } + + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) { + if (result) { + syncPromise.set_value(); + return; + } + + if (current == total) { + syncPromise.set_value(); + } + } + + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) { + sendPromise.set_value(result); + } + + virtual void saveCompleted(std::error_code result) { + savePromise.set_value(result); + } + + virtual void initCompleted(std::error_code result) { + loadPromise.set_value(result); + } + + virtual void actualBalanceUpdated(uint64_t actualBalance) { +// std::cout << "actual balance: " << actualBalance << std::endl; + } + virtual void pendingBalanceUpdated(uint64_t pendingBalance) { +// std::cout << "pending balance: " << pendingBalance << std::endl; + } + + std::promise syncPromise; + std::promise sendPromise; + std::promise savePromise; + std::promise loadPromise; +}; + +static const uint64_t TEST_BLOCK_REWARD = 70368744177663; + +CryptoNote::TransactionId TransferMoney(CryptoNote::Wallet& from, CryptoNote::Wallet& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "") { + CryptoNote::Transfer transfer; + transfer.amount = amount; + transfer.address = to.getAddress(); + + return from.sendTransaction(transfer, fee, extra, mixIn); +} + +void WaitWalletSync(TrivialWalletObserver* observer) { + observer->reset(); + ASSERT_TRUE(observer->waitForSyncEnd()); +} + +void WaitWalletSend(TrivialWalletObserver* observer) { + std::error_code ec; + observer->reset(); + ASSERT_TRUE(observer->waitForSendEnd(ec)); +} + +void WaitWalletSend(TrivialWalletObserver* observer, std::error_code& ec) { + observer->reset(); + ASSERT_TRUE(observer->waitForSendEnd(ec)); +} + +void WaitWalletSave(TrivialWalletObserver* observer) { + observer->reset(); + std::error_code ec; + + ASSERT_TRUE(observer->waitForSaveEnd(ec)); + EXPECT_FALSE(ec); +} + +class WalletApi : public ::testing::Test +{ +public: + void SetUp(); + +protected: + void prepareAliceWallet(); + void prepareBobWallet(); + void prepareCarolWallet(); + + void GetOneBlockReward(CryptoNote::Wallet& wallet); + + void TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = ""); + void performTransferWithErrorTx(const std::array& amounts, uint64_t fee); + + TestBlockchainGenerator generator; + + std::shared_ptr aliceWalletObserver; + std::shared_ptr aliceNode; + std::shared_ptr alice; + + std::shared_ptr bobWalletObserver; + std::shared_ptr bobNode; + std::shared_ptr bob; + + std::shared_ptr carolWalletObserver; + std::shared_ptr carolNode; + std::shared_ptr carol; +}; + +void WalletApi::SetUp() { + prepareAliceWallet(); + generator.generateEmptyBlocks(3); +} + +void WalletApi::prepareAliceWallet() { + aliceNode.reset(new INodeTrivialRefreshStub(generator)); + aliceWalletObserver.reset(new TrivialWalletObserver()); + + alice.reset(new CryptoNote::Wallet(*aliceNode)); + alice->addObserver(aliceWalletObserver.get()); +} + +void WalletApi::prepareBobWallet() { + bobNode.reset(new INodeTrivialRefreshStub(generator)); + bobWalletObserver.reset(new TrivialWalletObserver()); + + bob.reset(new CryptoNote::Wallet(*bobNode)); + bob->addObserver(bobWalletObserver.get()); +} + +void WalletApi::prepareCarolWallet() { + carolNode.reset(new INodeTrivialRefreshStub(generator)); + carolWalletObserver.reset(new TrivialWalletObserver()); + + carol.reset(new CryptoNote::Wallet(*carolNode)); + carol->addObserver(carolWalletObserver.get()); +} + +void WalletApi::GetOneBlockReward(CryptoNote::Wallet& wallet) { + cryptonote::account_public_address address; + ASSERT_TRUE(cryptonote::get_account_address_from_str(address, wallet.getAddress())); + generator.getBlockRewardForAddress(address); +} + +void WalletApi::performTransferWithErrorTx(const std::array& amounts, uint64_t fee) { + std::vector trs; + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = amounts[0]; + trs.push_back(tr); + + tr.address = bob->getAddress(); + tr.amount = amounts[1]; + trs.push_back(tr); + + tr.address = carol->getAddress(); + tr.amount = amounts[2]; + trs.push_back(tr); + + aliceNode->setNextTransactionError(); + alice->sendTransaction(trs, fee); + + std::error_code result; + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), result)); + ASSERT_NE(result.value(), 0); + + trs.clear(); + + tr.address = bob->getAddress(); + tr.amount = amounts[3]; + trs.push_back(tr); + + tr.address = carol->getAddress(); + tr.amount = amounts[4]; + trs.push_back(tr); + + alice->sendTransaction(trs, fee); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), result)); + ASSERT_EQ(result.value(), 0); +} + +void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn, const std::string& extra) { + prepareBobWallet(); + prepareCarolWallet(); + + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + + //unblock Alice's money + generator.generateEmptyBlocks(10); + uint64_t expectedBalance = TEST_BLOCK_REWARD; + + alice->startRefresh(); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(alice->pendingBalance(), expectedBalance); + EXPECT_EQ(alice->actualBalance(), expectedBalance); + + bob->initAndGenerate("pass2"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, 0, "")); + + generator.generateEmptyBlocks(10); + + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + bob->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + EXPECT_EQ(bob->pendingBalance(), transferAmount); + EXPECT_EQ(bob->actualBalance(), transferAmount); + + EXPECT_EQ(alice->pendingBalance(), expectedBalance - transferAmount - fee); + EXPECT_EQ(alice->actualBalance(), expectedBalance - transferAmount - fee); + + alice->shutdown(); + bob->shutdown(); +} + +void WaitWalletLoad(TrivialWalletObserver* observer, std::error_code& ec) { + observer->reset(); + + ASSERT_TRUE(observer->waitForLoadEnd(ec)); +} + +TEST_F(WalletApi, refreshWithMoney) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(alice->actualBalance(), 0); + ASSERT_EQ(alice->pendingBalance(), 0); + + cryptonote::account_public_address address; + ASSERT_TRUE(cryptonote::get_account_address_from_str(address, alice->getAddress())); + generator.getBlockRewardForAddress(address); + + alice->startRefresh(); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(alice->actualBalance(), 0); + EXPECT_EQ(alice->pendingBalance(), TEST_BLOCK_REWARD); + + alice->shutdown(); +} + +TEST_F(WalletApi, TransactionsAndTransfersAfterSend) { + prepareBobWallet(); + prepareCarolWallet(); + + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(alice->getTransactionCount(), 0); + EXPECT_EQ(alice->getTransferCount(), 0); + + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + + //unblock Alice's money + generator.generateEmptyBlocks(10); + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + bob->initAndGenerate("pass2"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + uint64_t fee = 100000; + int64_t amount1 = 1230000; + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount1, fee, 0)); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + int64_t amount2 = 1234500; + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount2, fee, 0)); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + int64_t amount3 = 1234567; + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount3, fee, 0)); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + carol->initAndGenerate("pass3"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get())); + + int64_t amount4 = 1020304; + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *carol, amount4, fee, 0)); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + EXPECT_EQ(alice->getTransactionCount(), 5); + + CryptoNote::Transaction tx; + + //Transaction with id = 0 is tested in getTransactionSuccess + ASSERT_TRUE(alice->getTransaction(1, tx)); + EXPECT_EQ(tx.totalAmount, amount1 + fee); + EXPECT_EQ(tx.fee, fee); + EXPECT_EQ(tx.isCoinbase, false); + EXPECT_EQ(tx.firstTransferId, 0); + EXPECT_EQ(tx.transferCount, 1); + + ASSERT_TRUE(alice->getTransaction(2, tx)); + EXPECT_EQ(tx.totalAmount, amount2 + fee); + EXPECT_EQ(tx.fee, fee); + EXPECT_EQ(tx.isCoinbase, false); + EXPECT_EQ(tx.firstTransferId, 1); + EXPECT_EQ(tx.transferCount, 1); + + ASSERT_TRUE(alice->getTransaction(3, tx)); + EXPECT_EQ(tx.totalAmount, amount3 + fee); + EXPECT_EQ(tx.fee, fee); + EXPECT_EQ(tx.isCoinbase, false); + EXPECT_EQ(tx.firstTransferId, 2); + EXPECT_EQ(tx.transferCount, 1); + + ASSERT_TRUE(alice->getTransaction(4, tx)); + EXPECT_EQ(tx.totalAmount, amount4 + fee); + EXPECT_EQ(tx.fee, fee); + EXPECT_EQ(tx.isCoinbase, false); + EXPECT_EQ(tx.firstTransferId, 3); + EXPECT_EQ(tx.transferCount, 1); + + //Now checking transfers + CryptoNote::Transfer tr; + ASSERT_TRUE(alice->getTransfer(0, tr)); + EXPECT_EQ(tr.amount, amount1); + EXPECT_EQ(tr.address, bob->getAddress()); + + ASSERT_TRUE(alice->getTransfer(1, tr)); + EXPECT_EQ(tr.amount, amount2); + EXPECT_EQ(tr.address, bob->getAddress()); + + ASSERT_TRUE(alice->getTransfer(2, tr)); + EXPECT_EQ(tr.amount, amount3); + EXPECT_EQ(tr.address, bob->getAddress()); + + ASSERT_TRUE(alice->getTransfer(3, tr)); + EXPECT_EQ(tr.amount, amount4); + EXPECT_EQ(tr.address, carol->getAddress()); + + EXPECT_EQ(alice->findTransactionByTransferId(0), 1); + EXPECT_EQ(alice->findTransactionByTransferId(1), 2); + EXPECT_EQ(alice->findTransactionByTransferId(2), 3); + EXPECT_EQ(alice->findTransactionByTransferId(3), 4); + + alice->shutdown(); +} + +TEST_F(WalletApi, saveAndLoadCacheDetails) { + prepareBobWallet(); + prepareCarolWallet(); + + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + + //unblock Alice's money + generator.generateEmptyBlocks(10); + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + bob->initAndGenerate("pass2"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + carol->initAndGenerate("pass3"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get())); + + uint64_t fee = 1000000; + int64_t amount1 = 1234567; + int64_t amount2 = 1020304; + int64_t amount3 = 2030405; + + std::vector trs; + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = amount1; + trs.push_back(tr); + + tr.address = bob->getAddress(); + tr.amount = amount2; + trs.push_back(tr); + + alice->sendTransaction(trs, fee, "", 0, 0); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + trs.clear(); + tr.address = carol->getAddress(); + tr.amount = amount3; + trs.push_back(tr); + + alice->sendTransaction(trs, fee, "", 0, 0); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + std::stringstream archive; + alice->save(archive, true, true); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + alice->shutdown(); + + prepareAliceWallet(); + + alice->initAndLoad(archive, "pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(alice->getTransactionCount(), 3); + ASSERT_EQ(alice->getTransferCount(), 3); + + CryptoNote::Transaction tx; + ASSERT_TRUE(alice->getTransaction(1, tx)); + EXPECT_EQ(tx.totalAmount, amount1 + amount2 + fee); + EXPECT_EQ(tx.fee, fee); + EXPECT_EQ(tx.firstTransferId, 0); + EXPECT_EQ(tx.transferCount, 2); + + ASSERT_TRUE(alice->getTransaction(2, tx)); + EXPECT_EQ(tx.totalAmount, amount3 + fee); + EXPECT_EQ(tx.fee, fee); + EXPECT_EQ(tx.firstTransferId, 2); + EXPECT_EQ(tx.transferCount, 1); + + ASSERT_TRUE(alice->getTransfer(0, tr)); + EXPECT_EQ(tr.address, bob->getAddress()); + EXPECT_EQ(tr.amount, amount1); + + ASSERT_TRUE(alice->getTransfer(1, tr)); + EXPECT_EQ(tr.address, bob->getAddress()); + EXPECT_EQ(tr.amount, amount2); + + ASSERT_TRUE(alice->getTransfer(2, tr)); + EXPECT_EQ(tr.address, carol->getAddress()); + EXPECT_EQ(tr.amount, amount3); + + EXPECT_EQ(alice->findTransactionByTransferId(0), 1); + EXPECT_EQ(alice->findTransactionByTransferId(1), 1); + EXPECT_EQ(alice->findTransactionByTransferId(2), 2); + + alice->shutdown(); + carol->shutdown(); + bob->shutdown(); +} + +TEST_F(WalletApi, sendMoneySuccessNoMixin) { + ASSERT_NO_FATAL_FAILURE(TestSendMoney(10000000, 1000000, 0)); +} + +TEST_F(WalletApi, sendMoneySuccessWithMixin) { + ASSERT_NO_FATAL_FAILURE(TestSendMoney(10000000, 1000000, 3)); +} + +TEST_F(WalletApi, getTransactionSuccess) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::Transaction tx; + + ASSERT_EQ(alice->getTransactionCount(), 1); + ASSERT_TRUE(alice->getTransaction(0, tx)); + + EXPECT_EQ(tx.firstTransferId, CryptoNote::INVALID_TRANSFER_ID); + EXPECT_EQ(tx.transferCount, 0); + EXPECT_EQ(tx.totalAmount, TEST_BLOCK_REWARD); + EXPECT_EQ(tx.fee, 0); + EXPECT_EQ(tx.isCoinbase, false); + + alice->shutdown(); +} + +TEST_F(WalletApi, getTransactionFailure) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::Transaction tx; + + ASSERT_EQ(alice->getTransactionCount(), 0); + ASSERT_FALSE(alice->getTransaction(0, tx)); + + alice->shutdown(); +} + +TEST_F(WalletApi, useNotInitializedObject) { + EXPECT_THROW(alice->pendingBalance(), std::system_error); + EXPECT_THROW(alice->actualBalance(), std::system_error); + EXPECT_THROW(alice->getTransactionCount(), std::system_error); + EXPECT_THROW(alice->getTransferCount(), std::system_error); + EXPECT_THROW(alice->getAddress(), std::system_error); + + std::stringstream archive; + EXPECT_THROW(alice->save(archive, true, true), std::system_error); + + EXPECT_THROW(alice->findTransactionByTransferId(1), std::system_error); + + CryptoNote::Transaction tx; + CryptoNote::Transfer tr; + EXPECT_THROW(alice->getTransaction(1, tx), std::system_error); + EXPECT_THROW(alice->getTransfer(2, tr), std::system_error); + + tr.address = "lslslslslslsls"; + tr.amount = 1000000; + EXPECT_THROW(alice->sendTransaction(tr, 300201), std::system_error); + + std::vector trs; + trs.push_back(tr); + EXPECT_THROW(alice->sendTransaction(trs, 329293), std::system_error); +} + +TEST_F(WalletApi, sendWrongAmount) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::Transfer tr; + tr.address = "1234567890qwertasdfgzxcvbyuiophjklnm"; + tr.amount = 1; + + EXPECT_THROW(alice->sendTransaction(tr, 1), std::system_error); + + alice->shutdown(); +} + +TEST_F(WalletApi, wrongPassword) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + std::stringstream archive; + alice->save(archive, true, false); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + alice->shutdown(); + + prepareAliceWallet(); + alice->initAndLoad(archive, "wrongpass"); + + std::error_code result; + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); + EXPECT_EQ(result.value(), cryptonote::error::WRONG_PASSWORD); +} + +TEST_F(WalletApi, detachBlockchain) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + + generator.generateEmptyBlocks(10); + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + aliceNode->startAlternativeChain(3); + generator.generateEmptyBlocks(10); + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(alice->actualBalance(), 0); + EXPECT_EQ(alice->pendingBalance(), 0); + + alice->shutdown(); +} + +TEST_F(WalletApi, saveAndLoadErroneousTxsCacheDetails) { + prepareBobWallet(); + prepareCarolWallet(); + + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + generator.generateEmptyBlocks(10); + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + carol->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get())); + + std::array amounts; + amounts[0] = 1234567; + amounts[1] = 1345678; + amounts[2] = 1456789; + amounts[3] = 1567890; + amounts[4] = 1678901; + uint64_t fee = 10000; + + ASSERT_NO_FATAL_FAILURE(performTransferWithErrorTx(amounts, fee)); + + std::stringstream archive; + alice->save(archive); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + prepareAliceWallet(); + alice->initAndLoad(archive, "pass"); + + std::error_code result; + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); + ASSERT_EQ(result.value(), 0); + + EXPECT_EQ(alice->getTransactionCount(), 2); + EXPECT_EQ(alice->getTransferCount(), 2); + + CryptoNote::Transaction tx; + ASSERT_TRUE(alice->getTransaction(1, tx)); + EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee); + EXPECT_EQ(tx.firstTransferId, 0); + EXPECT_EQ(tx.transferCount, 2); + + CryptoNote::Transfer tr; + ASSERT_TRUE(alice->getTransfer(0, tr)); + EXPECT_EQ(tr.amount, amounts[3]); + EXPECT_EQ(tr.address, bob->getAddress()); + + ASSERT_TRUE(alice->getTransfer(1, tr)); + EXPECT_EQ(tr.amount, amounts[4]); + EXPECT_EQ(tr.address, carol->getAddress()); + + alice->shutdown(); +} + +TEST_F(WalletApi, saveAndLoadErroneousTxsCacheNoDetails) { + prepareBobWallet(); + prepareCarolWallet(); + + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); + generator.generateEmptyBlocks(10); + alice->startRefresh(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + carol->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(carolWalletObserver.get())); + + std::array amounts; + amounts[0] = 1234567; + amounts[1] = 1345678; + amounts[2] = 1456789; + amounts[3] = 1567890; + amounts[4] = 1678901; + uint64_t fee = 10000; + + ASSERT_NO_FATAL_FAILURE(performTransferWithErrorTx(amounts, fee)); + + std::stringstream archive; + alice->save(archive, false, true); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + prepareAliceWallet(); + alice->initAndLoad(archive, "pass"); + + std::error_code result; + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); + ASSERT_EQ(result.value(), 0); + + EXPECT_EQ(alice->getTransactionCount(), 2); + EXPECT_EQ(alice->getTransferCount(), 0); + + CryptoNote::Transaction tx; + ASSERT_TRUE(alice->getTransaction(1, tx)); + EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee); + EXPECT_EQ(tx.firstTransferId, CryptoNote::INVALID_TRANSFER_ID); + EXPECT_EQ(tx.transferCount, 0); + + alice->shutdown(); +} From 209e2356f54c5f93b8f6d6cf7810693e60b45a9e Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Sun, 29 Jun 2014 03:39:24 +0400 Subject: [PATCH 07/59] Add proper big transaction handling --- src/cryptonote_core/tx_pool.cpp | 18 ++++++++++++++++++ src/wallet/wallet2.h | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 7fab0d2e30..4f209ec401 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -20,6 +20,8 @@ DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated +#define TRANSACTION_SIZE_LIMIT (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) + namespace cryptonote { //--------------------------------------------------------------------------------- tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) { @@ -52,6 +54,12 @@ namespace cryptonote { return false; } + if (!kept_by_block && blob_size >= TRANSACTION_SIZE_LIMIT) { + LOG_ERROR("transaction is too big: " << blob_size << " bytes, maximum size: " << TRANSACTION_SIZE_LIMIT); + tvc.m_verifivation_failed = true; + return false; + } + //check key images for transaction if it is not kept by block if (!kept_by_block) { if (have_tx_keyimges_as_spent(tx)) { @@ -340,6 +348,16 @@ namespace cryptonote { m_transactions.clear(); m_spent_key_images.clear(); } + + for (auto it = m_transactions.begin(); it != m_transactions.end(); ) { + auto it2 = it++; + if (it2->second.blob_size >= TRANSACTION_SIZE_LIMIT) { + LOG_PRINT_L0("Transaction " << get_transaction_hash(it2->second.tx) << " is too big (" << it2->second.blob_size << " bytes), removing it from pool"); + remove_transaction_keyimages(it2->second.tx); + m_transactions.erase(it2); + } + } + // Ignore deserialization error return true; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a2c961e83a..6342e8c7de 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -102,7 +102,7 @@ namespace tools void store(); cryptonote::account_base& get_account(){return m_account;} - void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); bool deinit(); void stop() { m_run.store(false, std::memory_order_relaxed); } From f4769d87ad606b63484ef76ef57f50b5ac117d0a Mon Sep 17 00:00:00 2001 From: Albert Werner Date: Thu, 10 Jul 2014 20:47:56 +0400 Subject: [PATCH 08/59] Added testnet flag in daemon --- src/cryptonote_config.h | 2 +- src/cryptonote_core/blockchain_storage.cpp | 40 ++++++++++++++++--- src/cryptonote_core/blockchain_storage.h | 3 +- src/cryptonote_core/cryptonote_core.cpp | 4 +- src/cryptonote_core/cryptonote_core.h | 2 +- .../cryptonote_format_utils.cpp | 14 ++++++- src/cryptonote_core/cryptonote_format_utils.h | 3 +- src/daemon/daemon.cpp | 17 ++++++-- src/p2p/net_node.h | 5 ++- src/p2p/net_node.inl | 20 ++++++---- src/simplewallet/simplewallet.cpp | 21 ++++++---- src/simplewallet/simplewallet.h | 4 +- src/version.h.in | 4 +- src/wallet/Wallet.cpp | 2 +- src/wallet/wallet2.cpp | 35 ++++++++++++---- src/wallet/wallet2.h | 7 +++- tests/core_proxy/core_proxy.cpp | 4 +- tests/core_tests/chaingen.h | 2 +- 18 files changed, 135 insertions(+), 54 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 25fd1caad9..6035288c9a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -8,7 +8,7 @@ #define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! #define CRYPTONOTE_MAX_TX_SIZE 1000000000 #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 -//TODO Define the first letter of your currency address +//TODO Currency-specific address prefix #define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX //TODO Choose maturity period for your currency #define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index fb0663f78e..87d53c02a4 100755 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -80,7 +80,7 @@ uint64_t blockchain_storage::get_current_blockchain_height() { return m_blocks.size(); } -bool blockchain_storage::init(const std::string& config_folder, bool load_existing) { +bool blockchain_storage::init(const std::string& config_folder, bool load_existing, bool testnet) { CRITICAL_REGION_LOCAL(m_blockchain_lock); if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { LOG_ERROR("Failed to create data directory: " << m_config_folder); @@ -149,11 +149,24 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi if (m_blocks.empty()) { LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); - block bl = ::boost::value_initialized(); - block_verification_context bvc = boost::value_initialized(); - generate_genesis_block(bl); - add_new_block(bl, bvc); - CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); + + if (!storeGenesisBlock(testnet)) { + return false; + } + } else { + cryptonote::block b; + if (testnet) { + generateTestnetGenesisBlock(b); + } else { + generateGenesisBlock(b); + } + + crypto::hash genesis_hash = get_block_hash(m_blocks[0].bl); + crypto::hash testnet_genesis_hash = get_block_hash(b); + if (genesis_hash != testnet_genesis_hash) { + LOG_ERROR("Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network."); + return false; + } } uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; @@ -163,6 +176,21 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi return true; } +bool blockchain_storage::storeGenesisBlock(bool testnet) { + block bl = ::boost::value_initialized(); + block_verification_context bvc = boost::value_initialized(); + + if (testnet) { + generateTestnetGenesisBlock(bl); + } else { + generateGenesisBlock(bl); + } + + add_new_block(bl, bvc); + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); + return true; +} + bool blockchain_storage::store_blockchain() { try { std::ofstream file(appendPath(m_config_folder, CRYPTONOTE_BLOCKSCACHE_FILENAME), std::ios::binary); diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index a67cf52032..e8946865cc 100755 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -18,7 +18,7 @@ namespace cryptonote { {}; bool init() { return init(tools::get_default_data_dir(), true); } - bool init(const std::string& config_folder, bool load_existing); + bool init(const std::string& config_folder, bool load_existing, bool testnet = false); bool deinit(); void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } @@ -182,6 +182,7 @@ namespace cryptonote { bool pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); void popTransaction(const transaction& transaction, const crypto::hash& transactionHash); void popTransactions(const Block& block, const crypto::hash& minerTransactionHash); + bool storeGenesisBlock(bool testnet); }; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 5c456d41ce..6065eafd52 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -90,14 +90,14 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, bool load_existing) + bool core::init(const boost::program_options::variables_map& vm, bool load_existing, bool testnet) { bool r = handle_command_line(vm); r = m_mempool.init(m_config_folder); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); - r = m_blockchain_storage.init(m_config_folder, load_existing); + r = m_blockchain_storage.init(m_config_folder, load_existing, testnet); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); r = m_miner.init(vm); diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index b573438dbd..921f83f2dd 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -43,7 +43,7 @@ namespace cryptonote miner& get_miner(){return m_miner;} static void init_options(boost::program_options::options_description& desc); - bool init(const boost::program_options::variables_map& vm, bool load_existing); + bool init(const boost::program_options::variables_map& vm, bool load_existing, bool testnet); bool set_genesis_block(const block& b); bool deinit(); uint64_t get_current_blockchain_height(); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index afd5b9fdab..a44b060d84 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -617,7 +617,7 @@ namespace cryptonote return p; } //--------------------------------------------------------------- - bool generate_genesis_block(block& bl) + bool generateGenesisBlock(block& bl) { //genesis block bl = boost::value_initialized(); @@ -630,7 +630,7 @@ namespace cryptonote construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis blobdata txb = tx_to_blob(bl.miner_tx); std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - std::cout << "Genesis coinbase tx hex: " << hex_to_represent << std::endl; + std::cout << "Genesis coinbase tx hex: " << hex_tx_represent << std::endl; */ //hard code coinbase tx in genesis block, because "true" generating tx use random, but genesis should be always the same @@ -648,6 +648,16 @@ namespace cryptonote //miner::find_nonce_for_given_block(bl, 1, 0); return true; } + + bool generateTestnetGenesisBlock(cryptonote::block& b) { + if (!generateGenesisBlock(b)) { + return false; + } + + b.nonce += 1; + return true; + } + //--------------------------------------------------------------- bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height) { diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index d3c089800c..9477e1a642 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -79,7 +79,8 @@ namespace cryptonote crypto::hash get_block_hash(const block& b); bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height); crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height); - bool generate_genesis_block(block& bl); + bool generateGenesisBlock(block& bl); + bool generateTestnetGenesisBlock(block& bl); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); uint64_t get_outs_money_amount(const transaction& tx); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index b86edd1b2e..660cb7844b 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -36,6 +36,8 @@ namespace const command_line::arg_descriptor arg_log_file = {"log-file", "", ""}; const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; const command_line::arg_descriptor arg_console = {"no-console", "Disable daemon console commands"}; + const command_line::arg_descriptor arg_testnet_on = {"testnet", "Used to deploy test nets. Checkpoints and hardcoded seeds are ignored, " + "network id is changed. Use it with --data-dir flag. The wallet must be launched with --testnet flag.", false}; } bool command_line_preprocessor(const boost::program_options::variables_map& vm); @@ -66,7 +68,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_log_file); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_console); - + command_line::add_arg(desc_cmd_sett, arg_testnet_on); cryptonote::core::init_options(desc_cmd_sett); cryptonote::core_rpc_server::init_options(desc_cmd_sett); @@ -134,7 +136,14 @@ int main(int argc, char* argv[]) //create objects and link them cryptonote::core ccore(NULL); - ccore.set_checkpoints(std::move(checkpoints)); + + bool testnet_mode = command_line::get_arg(vm, arg_testnet_on); + if (testnet_mode) { + LOG_PRINT_L0("Starting in testnet mode!"); + } else { + ccore.set_checkpoints(std::move(checkpoints)); + } + cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); nodetool::node_server > p2psrv(cprotocol); cryptonote::core_rpc_server rpc_server(ccore, p2psrv); @@ -144,7 +153,7 @@ int main(int argc, char* argv[]) //initialize objects LOG_PRINT_L0("Initializing p2p server..."); - res = p2psrv.init(vm); + res = p2psrv.init(vm, testnet_mode); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); @@ -160,7 +169,7 @@ int main(int argc, char* argv[]) //initialize core here LOG_PRINT_L0("Initializing core..."); - res = ccore.init(vm, true); + res = ccore.init(vm, true, testnet_mode); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); LOG_PRINT_L0("Core initialized OK"); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 74b9490f10..05fe1b0430 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -53,13 +53,13 @@ namespace nodetool public: typedef t_payload_net_handler payload_net_handler; // Some code - node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false) + node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false), m_network_id(CRYPTONOTE_NETWORK) {} static void init_options(boost::program_options::options_description& desc); bool run(); - bool init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map& vm, bool testnet); bool deinit(); bool send_stop_signal(); uint32_t get_this_peer_port(){return m_listenning_port;} @@ -203,6 +203,7 @@ namespace nodetool uint64_t m_peer_livetime; //keep connections to initiate some interactions net_server m_net_server; + boost::uuids::uuid m_network_id; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 89ae6ddd1c..00f9852447 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -50,7 +50,8 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_add_priority_node); command_line::add_arg(desc, arg_p2p_add_exclusive_node); command_line::add_arg(desc, arg_p2p_seed_node); - command_line::add_arg(desc, arg_p2p_hide_my_port); } + command_line::add_arg(desc, arg_p2p_hide_my_port); + } //----------------------------------------------------------------------------------- template bool node_server::init_config() @@ -191,10 +192,14 @@ namespace nodetool #define ADD_HARDCODED_SEED_NODE(addr) append_net_address(m_seed_nodes, addr); //----------------------------------------------------------------------------------- template - bool node_server::init(const boost::program_options::variables_map& vm) + bool node_server::init(const boost::program_options::variables_map& vm, bool testnet) { - //TODO add seed for your network - //ADD_HARDCODED_SEED_NODE("your_seed_ip.com:8080"); + if (!testnet) { + //TODO add seed for your network + //ADD_HARDCODED_SEED_NODE("your_seed_ip.com:8080"); + } else { + m_network_id.data[0] += 1; + } bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); @@ -368,7 +373,7 @@ namespace nodetool return; } - if(rsp.node_data.network_id != CRYPTONOTE_NETWORK) + if(rsp.node_data.network_id != m_network_id) { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); return; @@ -776,7 +781,7 @@ namespace nodetool node_data.my_port = m_external_port ? m_external_port : m_listenning_port; else node_data.my_port = 0; - node_data.network_id = CRYPTONOTE_NETWORK; + node_data.network_id = m_network_id; return true; } //----------------------------------------------------------------------------------- @@ -996,9 +1001,8 @@ namespace nodetool template int node_server::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { - if(arg.node_data.network_id != CRYPTONOTE_NETWORK) + if(arg.node_data.network_id != m_network_id) { - LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); drop_connection(context); return 1; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index b01c8d2d90..f9c5985130 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -40,6 +40,7 @@ namespace const command_line::arg_descriptor arg_password = {"password", "Wallet password", "", true}; const command_line::arg_descriptor arg_daemon_port = {"daemon-port", "Use daemon instance at port instead of 8081", 0}; const command_line::arg_descriptor arg_log_level = {"set_log", "", 0, true}; + const command_line::arg_descriptor arg_testnet = {"testnet", "Used to deploy test nets. The daemon must be launched with --testnet flag", false}; const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; @@ -266,6 +267,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_daemon_address.empty()) m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port); + bool testnet = command_line::get_arg(vm, arg_testnet); + tools::password_container pwd_container; if (command_line::has_arg(vm, arg_password)) { @@ -283,12 +286,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!m_generate_new.empty()) { - bool r = new_wallet(m_generate_new, pwd_container.password()); + bool r = new_wallet(m_generate_new, pwd_container.password(), testnet); CHECK_AND_ASSERT_MES(r, false, "account creation failed"); } else { - bool r = open_wallet(m_wallet_file, pwd_container.password()); + bool r = open_wallet(m_wallet_file, pwd_container.password(), testnet); CHECK_AND_ASSERT_MES(r, false, "could not open account"); } @@ -324,11 +327,11 @@ bool simple_wallet::try_connect_to_daemon() return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password) +bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password, bool testnet) { m_wallet_file = wallet_file; - m_wallet.reset(new tools::wallet2()); + m_wallet.reset(new tools::wallet2(testnet)); m_wallet->callback(this); try { @@ -355,10 +358,10 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::open_wallet(const string &wallet_file, const std::string& password) +bool simple_wallet::open_wallet(const string &wallet_file, const std::string& password, bool testnet) { m_wallet_file = wallet_file; - m_wallet.reset(new tools::wallet2()); + m_wallet.reset(new tools::wallet2(testnet)); m_wallet->callback(this); try @@ -899,6 +902,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_daemon_port); command_line::add_arg(desc_params, arg_command); command_line::add_arg(desc_params, arg_log_level); + command_line::add_arg(desc_params, arg_testnet); tools::wallet_rpc_server::init_options(desc_params); po::positional_options_description positional_options; @@ -914,7 +918,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - success_msg_writer() << "bytecoin wallet v" << PROJECT_VERSION_LONG; + success_msg_writer() << CRYPTONOTE_NAME " wallet v" << PROJECT_VERSION_LONG; success_msg_writer() << "Usage: simplewallet [--wallet-file=|--generate-new-wallet=] [--daemon-address=:] []"; success_msg_writer() << desc_all << '\n' << w.get_commands_str(); return false; @@ -968,6 +972,7 @@ int main(int argc, char* argv[]) return 1; } + bool testnet = command_line::get_arg(vm, arg_testnet); std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); std::string wallet_password = command_line::get_arg(vm, arg_password); std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); @@ -980,7 +985,7 @@ int main(int argc, char* argv[]) if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - tools::wallet2 wal; + tools::wallet2 wal(testnet); try { LOG_PRINT_L0("Loading wallet..."); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 0d2b4d5cac..95fdecfa77 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -39,8 +39,8 @@ namespace cryptonote bool run_console_handler(); - bool new_wallet(const std::string &wallet_file, const std::string& password); - bool open_wallet(const std::string &wallet_file, const std::string& password); + bool new_wallet(const std::string &wallet_file, const std::string& password, bool testnet); + bool open_wallet(const std::string &wallet_file, const std::string& password, bool testnet); bool close_wallet(); bool help(const std::vector &args = std::vector()); diff --git a/src/version.h.in b/src/version.h.in index 62316da3fd..7812a19d6f 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.11" -#define PROJECT_VERSION_BUILD_NO "65" +#define PROJECT_VERSION "1.0.0" +#define PROJECT_VERSION_BUILD_NO "1" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index a327e1a2b4..15e790cd57 100644 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -106,7 +106,7 @@ void Wallet::initAndGenerate(const std::string& password) { void Wallet::storeGenesisBlock() { cryptonote::block b; - cryptonote::generate_genesis_block(b); + cryptonote::generateGenesisBlock(b); m_blockchain.push_back(get_block_hash(b)); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5cfe5d7243..4f323eb822 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -373,9 +373,6 @@ bool wallet2::clear() { m_blockchain.clear(); m_transfers.clear(); - cryptonote::block b; - cryptonote::generate_genesis_block(b); - m_blockchain.push_back(get_block_hash(b)); m_local_bc_height = 1; return true; } @@ -455,6 +452,10 @@ void wallet2::generate(const std::string& wallet_, const std::string& password) r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str()); if(!r) LOG_PRINT_RED_L0("String with address text not saved"); + cryptonote::block b; + generateGenesis(b); + m_blockchain.push_back(get_block_hash(b)); + store(); } //---------------------------------------------------------------------------------------------------- @@ -527,15 +528,25 @@ void wallet2::load(const std::string& wallet_, const std::string& password) m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - if(m_blockchain.empty()) - { - cryptonote::block b; - cryptonote::generate_genesis_block(b); - m_blockchain.push_back(get_block_hash(b)); + cryptonote::block genesis; + generateGenesis(genesis); + crypto::hash genesisHash = get_block_hash(genesis); + + if (m_blockchain.empty()) { + m_blockchain.push_back(genesisHash); + } else { + checkGenesis(genesisHash); } + m_local_bc_height = m_blockchain.size(); } //---------------------------------------------------------------------------------------------------- +void wallet2::checkGenesis(const crypto::hash& genesisHash) { + std::string what("Genesis block missmatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); + + THROW_WALLET_EXCEPTION_IF(genesisHash != m_blockchain[0], error::wallet_internal_error, what); +} +//---------------------------------------------------------------------------------------------------- void wallet2::store() { bool r = tools::serialize_obj_to_file(*this, m_wallet_file); @@ -690,4 +701,12 @@ void wallet2::transfer(const std::vector& dsts transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); } //---------------------------------------------------------------------------------------------------- +void wallet2::generateGenesis(cryptonote::block& b) { + if (m_testnet) { + cryptonote::generateTestnetGenesisBlock(b); + } else { + cryptonote::generateGenesisBlock(b); + } +} +//---------------------------------------------------------------------------------------------------- } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 778e1766fc..e1d4dd10ca 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -53,9 +53,9 @@ namespace tools class wallet2 { - wallet2(const wallet2&) : m_run(true), m_callback(0) {}; + wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false) {}; public: - wallet2() : m_run(true), m_callback(0) {}; + wallet2(bool testnet = false) : m_run(true), m_callback(0), m_testnet(testnet) {}; struct transfer_details { uint64_t m_block_height; @@ -162,6 +162,8 @@ namespace tools bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const cryptonote::transaction& tx); void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount); + void generateGenesis(cryptonote::block& b); + void checkGenesis(const crypto::hash& genesis_hash); //throws cryptonote::account_base m_account; std::string m_daemon_address; @@ -181,6 +183,7 @@ namespace tools std::atomic m_run; i_wallet2_callback* m_callback; + bool m_testnet; }; } BOOST_CLASS_VERSION(tools::wallet2, 7) diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index d3ff7444b4..25b6648604 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -86,7 +86,7 @@ int main(int argc, char* argv[]) //initialize objects LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(vm); + bool res = p2psrv.init(vm, false); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); @@ -198,7 +198,7 @@ bool tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_i } bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) { - generate_genesis_block(m_genesis); + generateGenesisBlock(m_genesis); crypto::hash h = get_block_hash(m_genesis); add_block(h, get_block_longhash(m_cn_context, m_genesis, 0), m_genesis, block_to_blob(m_genesis)); return true; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 2350c9311e..773b11c027 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -461,7 +461,7 @@ inline bool do_replay_events(std::vector& events) cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects cryptonote::core c(&pr); - if (!c.init(vm, false)) + if (!c.init(vm, false, false)) { std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl; return false; From 7f23949066a7812cbd7729208c0cc6818e432650 Mon Sep 17 00:00:00 2001 From: Albert Werner Date: Fri, 18 Jul 2014 14:49:01 +0400 Subject: [PATCH 09/59] Add print-genesis-tx option. --- src/common/ObserverManager.h | 3 +- src/cryptonote_config.h | 2 ++ .../cryptonote_format_utils.cpp | 33 +++++++++++-------- src/cryptonote_core/cryptonote_format_utils.h | 1 + src/daemon/daemon.cpp | 16 +++++++++ src/version.h.in | 2 +- src/wallet/Wallet.cpp | 2 +- src/wallet/Wallet.h | 2 +- src/wallet/WalletAsyncContextCounter.cpp | 2 +- src/wallet/WalletAsyncContextCounter.h | 2 +- src/wallet/WalletErrors.cpp | 2 +- src/wallet/WalletErrors.h | 2 +- src/wallet/WalletEvent.h | 2 +- src/wallet/WalletRequest.h | 2 +- src/wallet/WalletSendTransactionContext.h | 2 +- src/wallet/WalletSerialization.h | 2 +- src/wallet/WalletSynchronizationContext.h | 2 +- src/wallet/WalletSynchronizer.cpp | 2 +- src/wallet/WalletSynchronizer.h | 2 +- src/wallet/WalletTransactionSender.cpp | 10 +++--- src/wallet/WalletTransactionSender.h | 2 +- src/wallet/WalletTransferDetails.cpp | 2 +- src/wallet/WalletTransferDetails.h | 2 +- src/wallet/WalletTxSendingState.cpp | 2 +- src/wallet/WalletTxSendingState.h | 2 +- src/wallet/WalletUnconfirmedTransactions.cpp | 2 +- src/wallet/WalletUnconfirmedTransactions.h | 2 +- src/wallet/WalletUserTransactionsCache.cpp | 2 +- src/wallet/WalletUserTransactionsCache.h | 2 +- src/wallet/WalletUtils.h | 2 +- 30 files changed, 68 insertions(+), 45 deletions(-) diff --git a/src/common/ObserverManager.h b/src/common/ObserverManager.h index 02b59d9f11..68ed0e9104 100644 --- a/src/common/ObserverManager.h +++ b/src/common/ObserverManager.h @@ -1,9 +1,10 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #pragma once +#include #include #include diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6035288c9a..405ef6727b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -90,3 +90,5 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 +#define GENESIS_COINBASE_TX_HEX "" + diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index a44b060d84..e50d7b23c9 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -617,25 +617,30 @@ namespace cryptonote return p; } //--------------------------------------------------------------- + void generate_genesis_tx(transaction& tx) { + account_public_address ac = boost::value_initialized(); + std::vector sz; + construct_miner_tx(0, 0, 0, 0, 0, ac, tx); // zero fee in genesis + blobdata txb = tx_to_blob(tx); + } + + std::string get_genesis_tx_hex() { + transaction tx; + + generate_genesis_tx(tx); + blobdata txb = tx_to_blob(tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + return hex_tx_represent; + } + bool generateGenesisBlock(block& bl) { //genesis block bl = boost::value_initialized(); - //TODO Uncomment this code block on the first network launch. It will generate and print you genesis block's hash. - //TODO Then you must copy it and put to genesis_coinbase_tx_hex variable - /* - account_public_address ac = boost::value_initialized(); - std::vector sz; - construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - std::cout << "Genesis coinbase tx hex: " << hex_tx_represent << std::endl; - */ - - //hard code coinbase tx in genesis block, because "true" generating tx use random, but genesis should be always the same - //TODO After you obtain hash of the genesis block put it here and recompile sources! - std::string genesis_coinbase_tx_hex = ""; + //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same + std::string genesis_coinbase_tx_hex = GENESIS_COINBASE_TX_HEX; blobdata tx_bl; string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 9477e1a642..c70d920d4d 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -81,6 +81,7 @@ namespace cryptonote crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height); bool generateGenesisBlock(block& bl); bool generateTestnetGenesisBlock(block& bl); + std::string get_genesis_tx_hex(); bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); bool get_inputs_money_amount(const transaction& tx, uint64_t& money); uint64_t get_outs_money_amount(const transaction& tx); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 660cb7844b..d731ada940 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -38,10 +38,20 @@ namespace const command_line::arg_descriptor arg_console = {"no-console", "Disable daemon console commands"}; const command_line::arg_descriptor arg_testnet_on = {"testnet", "Used to deploy test nets. Checkpoints and hardcoded seeds are ignored, " "network id is changed. Use it with --data-dir flag. The wallet must be launched with --testnet flag.", false}; + const command_line::arg_descriptor arg_print_genesis_tx = {"print-genesis-tx", "Prints genesis' block tx hex to insert it to config and exits"}; } bool command_line_preprocessor(const boost::program_options::variables_map& vm); +void print_genesis_tx_hex() { + std::string tx_hex = cryptonote::get_genesis_tx_hex(); + + std::cout << "Insert this line into your coin configuration file as is: " << std::endl; + std::cout << "#define GENESIS_COINBASE_TX_HEX \"" << tx_hex << "\"" << std::endl; + + return; +} + int main(int argc, char* argv[]) { @@ -69,6 +79,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_console); command_line::add_arg(desc_cmd_sett, arg_testnet_on); + command_line::add_arg(desc_cmd_sett, arg_print_genesis_tx); cryptonote::core::init_options(desc_cmd_sett); cryptonote::core_rpc_server::init_options(desc_cmd_sett); @@ -90,6 +101,11 @@ int main(int argc, char* argv[]) return false; } + if (command_line::get_arg(vm, arg_print_genesis_tx)) { + print_genesis_tx_hex(); + return false; + } + std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir); std::string config = command_line::get_arg(vm, arg_config_file); diff --git a/src/version.h.in b/src/version.h.in index 7812a19d6f..ae6accfa7c 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.0" +#define PROJECT_VERSION "1.0.1" #define PROJECT_VERSION_BUILD_NO "1" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index 15e790cd57..984d8156fe 100644 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index 6ca795d326..cf3e6bf397 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/wallet/WalletAsyncContextCounter.cpp index 752933b6be..8ec39b4519 100644 --- a/src/wallet/WalletAsyncContextCounter.cpp +++ b/src/wallet/WalletAsyncContextCounter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletAsyncContextCounter.h b/src/wallet/WalletAsyncContextCounter.h index c7acd6626d..322551b317 100644 --- a/src/wallet/WalletAsyncContextCounter.h +++ b/src/wallet/WalletAsyncContextCounter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletErrors.cpp b/src/wallet/WalletErrors.cpp index 792c046de6..6e3f375665 100644 --- a/src/wallet/WalletErrors.cpp +++ b/src/wallet/WalletErrors.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletErrors.h b/src/wallet/WalletErrors.h index e62617f2b8..2d79f92856 100644 --- a/src/wallet/WalletErrors.h +++ b/src/wallet/WalletErrors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletEvent.h b/src/wallet/WalletEvent.h index 10bc210a42..3e7d51aa08 100644 --- a/src/wallet/WalletEvent.h +++ b/src/wallet/WalletEvent.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h index d3b43df77a..c5ae6bffdf 100644 --- a/src/wallet/WalletRequest.h +++ b/src/wallet/WalletRequest.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h index 796d5573a5..ff117d6c1e 100644 --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/wallet/WalletSendTransactionContext.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h index 5d8e2a1a7f..0e970f2e3f 100644 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletSynchronizationContext.h b/src/wallet/WalletSynchronizationContext.h index 423da4944c..de4abe850e 100644 --- a/src/wallet/WalletSynchronizationContext.h +++ b/src/wallet/WalletSynchronizationContext.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletSynchronizer.cpp b/src/wallet/WalletSynchronizer.cpp index 9b94815663..04be48c6af 100644 --- a/src/wallet/WalletSynchronizer.cpp +++ b/src/wallet/WalletSynchronizer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletSynchronizer.h b/src/wallet/WalletSynchronizer.h index c86ee1310d..8e1e61ddba 100644 --- a/src/wallet/WalletSynchronizer.h +++ b/src/wallet/WalletSynchronizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 737a769cc8..53d3df4fc8 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -1,9 +1,7 @@ -/* - * WalletTransactionSender.cpp - * - * Created on: 18 Ð¸ÑŽÐ½Ñ 2014 г. - * Author: milo - */ +// Copyright (c) 2011-2014 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "WalletTransactionSender.h" #include "WalletUtils.h" diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h index 9a51190c2a..0a51d89210 100644 --- a/src/wallet/WalletTransactionSender.h +++ b/src/wallet/WalletTransactionSender.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletTransferDetails.cpp b/src/wallet/WalletTransferDetails.cpp index 8c05ce8b28..36350f9c2b 100644 --- a/src/wallet/WalletTransferDetails.cpp +++ b/src/wallet/WalletTransferDetails.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletTransferDetails.h b/src/wallet/WalletTransferDetails.h index 7dbdae12a4..5658f1ee8d 100644 --- a/src/wallet/WalletTransferDetails.h +++ b/src/wallet/WalletTransferDetails.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletTxSendingState.cpp b/src/wallet/WalletTxSendingState.cpp index fe1fda6c1d..d371b07013 100644 --- a/src/wallet/WalletTxSendingState.cpp +++ b/src/wallet/WalletTxSendingState.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletTxSendingState.h b/src/wallet/WalletTxSendingState.h index 65ba7d30b9..0ea36264ee 100644 --- a/src/wallet/WalletTxSendingState.h +++ b/src/wallet/WalletTxSendingState.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index 53a632cb36..7fb0d5785d 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index bfd99634de..7341688f4e 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index f0adbc98c4..989f1c85f6 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index 48971bfdde..a21cc86917 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/wallet/WalletUtils.h b/src/wallet/WalletUtils.h index 46d57793ca..4074f609cc 100644 --- a/src/wallet/WalletUtils.h +++ b/src/wallet/WalletUtils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2013 The Cryptonote developers +// Copyright (c) 2011-2014 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. From eefb4111cdee0aa127e2dd186efffadd9d25b070 Mon Sep 17 00:00:00 2001 From: Albert Werner Date: Fri, 18 Jul 2014 16:35:40 +0400 Subject: [PATCH 10/59] Update README --- README | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/README b/README index 051cbea0be..b33aada9d3 100644 --- a/README +++ b/README @@ -133,28 +133,33 @@ Example: == Fifth step. Genesis block == -Now your coin code is almost ready. You need to generate the coinbase transaction for your genesis block. Uncomment the following code in "generate_genesis_block(block& bl)" function in src/cryptonote_core/cryptonote_format_utils.cpp: +1. Build the binaries with blank genesis tx hex (src/cryptonote_config.h) - /* - account_public_address ac = boost::value_initialized(); - std::vector sz; - construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - std::cout << "Genesis block hex: " << hex_tx_represent << std::endl; - */ +You should leave #define GENESIS_COINBASE_TX_HEX blank and compile the binaries without it. -Compile and run the daemon. As soon as it prints line starting with "Genesis coinbase tx hex: " copy the hex representation of the coinbase tx and paste it into generate_genesis_block function: +Example: +#define GENESIS_COINBASE_TX_HEX "" + + +2. Start the daemon to print out the genesis block -Genesis coinbase tx hex: 013c01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121012cfb466857c5762cdd97e242b08b7e239ab35807f5024d4785a33d9ebdba68b0 +Run your daemon with --print-genesis-tx argument. It will print out the genesis block coinbase transaction hash. + +Example: +cryptonotecoind --print-genesis-tx --> -std::string genesis_coinbase_tx_hex = "013c01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121012cfb466857c5762cdd97e242b08b7e239ab35807f5024d4785a33d9ebdba68b0"; +3. Copy the printed transaction hash — (src/cryptonote_config.h) + +Copy the tx hash that has been printed by the daemon to GENESIS_COINBASE_TX_HEX in /src/cryptonote_config.h + +Example: +#define GENESIS_COINBASE_TX_HEX "013c01ff0001ffff...785a33d9ebdba68b0" + -Comment the coinbase tx generation code back and recompile everything again. +4. Recompile the binaries -You coin code is ready now. Make an announcement for the potential users and enjoy! +Recompile everything again. Your coin code is ready now. Make an announcement for the potential users and enjoy! == Building CryptoNote == @@ -182,7 +187,7 @@ http://www.microsoft.com/ http://www.cmake.org/ http://www.boost.org/ -To build, change to a directory where this file is located, and run this commands: +To build, change to a directory where this file is located, and run theas commands: mkdir build cd build cmake -G "Visual Studio 11 Win64" .. From ce6a3be6469c98b0a16f593baddfd31394d4a5d8 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Wed, 13 Aug 2014 11:38:35 +0100 Subject: [PATCH 11/59] Update license from MIT to LGPL --- include/INode.h | 19 ++++++++++++--- include/IWallet.h | 19 ++++++++++++--- src/common/ObserverManager.h | 19 ++++++++++++--- src/common/base58.cpp | 20 ++++++++++++---- src/common/base58.h | 19 ++++++++++++--- src/common/boost_serialization_helper.h | 19 ++++++++++++--- src/common/command_line.cpp | 19 ++++++++++++--- src/common/command_line.h | 19 ++++++++++++--- src/common/int-util.h | 19 ++++++++++++--- src/common/pod-class.h | 19 ++++++++++++--- ...unordered_containers_boost_serialization.h | 19 ++++++++++++--- src/common/util.cpp | 19 ++++++++++++--- src/common/util.h | 19 ++++++++++++--- src/common/varint.h | 19 ++++++++++++--- src/connectivity_tool/conn_tool.cpp | 21 +++++++++++++---- src/crypto/chacha8.h | 21 +++++++++++++---- src/crypto/crypto-ops-data.c | 19 ++++++++++++--- src/crypto/crypto-ops.c | 19 ++++++++++++--- src/crypto/crypto-ops.h | 19 ++++++++++++--- src/crypto/crypto.cpp | 19 ++++++++++++--- src/crypto/crypto.h | 19 ++++++++++++--- src/crypto/generic-ops.h | 19 ++++++++++++--- src/crypto/hash-extra-blake.c | 19 ++++++++++++--- src/crypto/hash-extra-groestl.c | 19 ++++++++++++--- src/crypto/hash-extra-jh.c | 19 ++++++++++++--- src/crypto/hash-extra-skein.c | 19 ++++++++++++--- src/crypto/hash-ops.h | 19 ++++++++++++--- src/crypto/hash.c | 19 ++++++++++++--- src/crypto/hash.h | 21 +++++++++++++---- src/crypto/initializer.h | 19 ++++++++++++--- src/crypto/random.c | 19 ++++++++++++--- src/crypto/random.h | 19 ++++++++++++--- src/crypto/slow-hash.c | 19 ++++++++++++--- src/crypto/slow-hash.cpp | 19 ++++++++++++--- src/crypto/slow-hash.inl | 17 ++++++++++++++ src/crypto/tree-hash.c | 19 ++++++++++++--- src/cryptonote_config.h | 20 ++++++++++++---- src/cryptonote_core/SwappedMap.cpp | 17 ++++++++++++++ src/cryptonote_core/SwappedMap.h | 17 ++++++++++++++ src/cryptonote_core/SwappedVector.cpp | 17 ++++++++++++++ src/cryptonote_core/SwappedVector.h | 17 ++++++++++++++ src/cryptonote_core/account.cpp | 19 ++++++++++++--- src/cryptonote_core/account.h | 19 ++++++++++++--- .../account_boost_serialization.h | 19 ++++++++++++--- src/cryptonote_core/blockchain_storage.cpp | 19 ++++++++++++--- src/cryptonote_core/blockchain_storage.h | 19 ++++++++++++--- .../blockchain_storage_boost_serialization.h | 19 ++++++++++++--- src/cryptonote_core/checkpoints.cpp | 19 ++++++++++++--- src/cryptonote_core/checkpoints.h | 19 ++++++++++++--- src/cryptonote_core/checkpoints_create.h | 19 ++++++++++++--- src/cryptonote_core/connection_context.h | 19 ++++++++++++--- src/cryptonote_core/cryptonote_basic.h | 19 ++++++++++++--- src/cryptonote_core/cryptonote_basic_impl.cpp | 20 ++++++++++++---- src/cryptonote_core/cryptonote_basic_impl.h | 19 ++++++++++++--- .../cryptonote_boost_serialization.h | 19 ++++++++++++--- src/cryptonote_core/cryptonote_core.cpp | 20 ++++++++++++---- src/cryptonote_core/cryptonote_core.h | 19 ++++++++++++--- .../cryptonote_format_utils.cpp | 19 ++++++++++++--- src/cryptonote_core/cryptonote_format_utils.h | 19 ++++++++++++--- src/cryptonote_core/cryptonote_stat_info.h | 19 ++++++++++++--- src/cryptonote_core/difficulty.cpp | 19 ++++++++++++--- src/cryptonote_core/difficulty.h | 19 ++++++++++++--- src/cryptonote_core/miner.cpp | 21 +++++++++++++---- src/cryptonote_core/miner.h | 22 +++++++++++++----- src/cryptonote_core/tx_extra.h | 19 ++++++++++++--- src/cryptonote_core/tx_pool.cpp | 19 ++++++++++++--- src/cryptonote_core/tx_pool.h | 19 ++++++++++++--- src/cryptonote_core/verification_context.h | 20 ++++++++++++---- src/cryptonote_protocol/blobdatatype.h | 19 ++++++++++++--- .../cryptonote_protocol_defs.h | 19 ++++++++++++--- .../cryptonote_protocol_handler.h | 19 ++++++++++++--- .../cryptonote_protocol_handler.inl | 19 ++++++++++++--- .../cryptonote_protocol_handler_common.h | 19 ++++++++++++--- src/daemon/daemon.cpp | 19 ++++++++++++--- src/daemon/daemon_commands_handler.h | 19 ++++++++++++--- src/miner/simpleminer.cpp | 20 ++++++++++++---- src/miner/simpleminer.h | 19 ++++++++++++--- src/miner/simpleminer_protocol_defs.h | 20 ++++++++++++---- src/miner/target_helper.h | 19 ++++++++++++--- src/node_rpc_proxy/InitState.h | 19 ++++++++++++--- src/node_rpc_proxy/NodeErrors.cpp | 19 ++++++++++++--- src/node_rpc_proxy/NodeErrors.h | 19 ++++++++++++--- src/node_rpc_proxy/NodeRpcProxy.cpp | 19 ++++++++++++--- src/node_rpc_proxy/NodeRpcProxy.h | 19 ++++++++++++--- src/p2p/net_node.h | 19 ++++++++++++--- src/p2p/net_node.inl | 19 ++++++++++++--- src/p2p/net_node_common.h | 19 ++++++++++++--- src/p2p/net_peerlist.h | 19 ++++++++++++--- src/p2p/net_peerlist_boost_serialization.h | 19 ++++++++++++--- src/p2p/p2p_networks.h | 19 ++++++++++++--- src/p2p/p2p_protocol_defs.h | 22 +++++++++++++----- src/p2p/stdafx.h | 23 +++++++++++++------ src/platform/mingw/alloca.h | 19 ++++++++++++--- src/platform/msc/alloca.h | 19 ++++++++++++--- src/platform/msc/inline_c.h | 19 ++++++++++++--- src/platform/msc/stdbool.h | 19 ++++++++++++--- src/platform/msc/sys/param.h | 19 ++++++++++++--- src/rpc/core_rpc_server.cpp | 19 ++++++++++++--- src/rpc/core_rpc_server.h | 19 ++++++++++++--- src/rpc/core_rpc_server_commands_defs.h | 20 ++++++++++++---- src/rpc/core_rpc_server_error_codes.h | 23 +++++++++++++------ src/serialization/binary_archive.h | 19 ++++++++++++--- src/serialization/binary_utils.h | 19 ++++++++++++--- src/serialization/crypto.h | 19 ++++++++++++--- src/serialization/debug_archive.h | 19 ++++++++++++--- src/serialization/json_archive.h | 19 ++++++++++++--- src/serialization/json_utils.h | 19 ++++++++++++--- src/serialization/serialization.h | 19 ++++++++++++--- src/serialization/string.h | 19 ++++++++++++--- src/serialization/variant.h | 19 ++++++++++++--- src/serialization/vector.h | 19 ++++++++++++--- src/simplewallet/password_container.cpp | 19 ++++++++++++--- src/simplewallet/password_container.h | 20 ++++++++++++---- src/simplewallet/simplewallet.cpp | 19 ++++++++++++--- src/simplewallet/simplewallet.h | 19 ++++++++++++--- src/wallet/Wallet.cpp | 19 ++++++++++++--- src/wallet/Wallet.h | 19 ++++++++++++--- src/wallet/WalletAsyncContextCounter.cpp | 21 +++++++++++++---- src/wallet/WalletAsyncContextCounter.h | 19 ++++++++++++--- src/wallet/WalletErrors.cpp | 19 ++++++++++++--- src/wallet/WalletErrors.h | 19 ++++++++++++--- src/wallet/WalletEvent.h | 20 ++++++++++++---- src/wallet/WalletRequest.h | 19 ++++++++++++--- src/wallet/WalletSendTransactionContext.h | 19 ++++++++++++--- src/wallet/WalletSerialization.h | 19 ++++++++++++--- src/wallet/WalletSynchronizationContext.h | 19 ++++++++++++--- src/wallet/WalletSynchronizer.cpp | 19 ++++++++++++--- src/wallet/WalletSynchronizer.h | 19 ++++++++++++--- src/wallet/WalletTransactionSender.cpp | 22 +++++++++++++----- src/wallet/WalletTransactionSender.h | 20 ++++++++++++---- src/wallet/WalletTransferDetails.cpp | 20 ++++++++++++---- src/wallet/WalletTransferDetails.h | 19 ++++++++++++--- src/wallet/WalletTxSendingState.cpp | 19 ++++++++++++--- src/wallet/WalletTxSendingState.h | 19 ++++++++++++--- src/wallet/WalletUnconfirmedTransactions.cpp | 19 ++++++++++++--- src/wallet/WalletUnconfirmedTransactions.h | 20 ++++++++++++---- src/wallet/WalletUserTransactionsCache.cpp | 20 ++++++++++++---- src/wallet/WalletUserTransactionsCache.h | 19 ++++++++++++--- src/wallet/WalletUtils.h | 20 ++++++++++++---- src/wallet/wallet2.cpp | 20 ++++++++++++---- src/wallet/wallet2.h | 19 ++++++++++++--- src/wallet/wallet_errors.h | 19 ++++++++++++--- src/wallet/wallet_rpc_server.cpp | 20 ++++++++++++---- src/wallet/wallet_rpc_server.h | 19 ++++++++++++--- src/wallet/wallet_rpc_server_commans_defs.h | 20 ++++++++++++---- src/wallet/wallet_rpc_server_error_codes.h | 19 ++++++++++++--- tests/core_proxy/core_proxy.cpp | 19 ++++++++++++--- tests/core_proxy/core_proxy.h | 19 ++++++++++++--- tests/core_tests/block_reward.cpp | 19 ++++++++++++--- tests/core_tests/block_reward.h | 19 ++++++++++++--- tests/core_tests/block_validation.cpp | 19 ++++++++++++--- tests/core_tests/block_validation.h | 19 ++++++++++++--- tests/core_tests/chain_split_1.cpp | 22 +++++++++++++----- tests/core_tests/chain_split_1.h | 19 ++++++++++++--- tests/core_tests/chain_switch_1.cpp | 19 ++++++++++++--- tests/core_tests/chain_switch_1.h | 19 ++++++++++++--- tests/core_tests/chaingen.cpp | 19 ++++++++++++--- tests/core_tests/chaingen.h | 19 ++++++++++++--- tests/core_tests/chaingen001.cpp | 19 ++++++++++++--- tests/core_tests/chaingen_main.cpp | 19 ++++++++++++--- tests/core_tests/chaingen_tests_list.h | 19 ++++++++++++--- tests/core_tests/double_spend.cpp | 19 ++++++++++++--- tests/core_tests/double_spend.h | 19 ++++++++++++--- tests/core_tests/double_spend.inl | 17 ++++++++++++++ tests/core_tests/integer_overflow.cpp | 19 ++++++++++++--- tests/core_tests/integer_overflow.h | 19 ++++++++++++--- tests/core_tests/ring_signature_1.cpp | 19 ++++++++++++--- tests/core_tests/ring_signature_1.h | 19 ++++++++++++--- tests/core_tests/transaction_tests.cpp | 20 ++++++++++++---- tests/core_tests/transaction_tests.h | 19 ++++++++++++--- tests/core_tests/tx_validation.cpp | 19 ++++++++++++--- tests/core_tests/tx_validation.h | 19 ++++++++++++--- tests/crypto/crypto-ops-data.c | 19 ++++++++++++--- tests/crypto/crypto-ops.c | 19 ++++++++++++--- tests/crypto/crypto-tests.h | 19 ++++++++++++--- tests/crypto/crypto.cpp | 19 ++++++++++++--- tests/crypto/hash.c | 19 ++++++++++++--- tests/crypto/main.cpp | 19 ++++++++++++--- tests/crypto/random.c | 19 ++++++++++++--- tests/daemon_tests/transfers.cpp | 19 ++++++++++++--- tests/difficulty/difficulty.cpp | 19 ++++++++++++--- tests/functional_tests/main.cpp | 19 ++++++++++++--- .../transactions_flow_test.cpp | 20 ++++++++++++---- .../functional_tests/transactions_flow_test.h | 20 ++++++++++++---- ...ransactions_generation_from_blockchain.cpp | 19 ++++++++++++--- .../transactions_generation_from_blockchain.h | 20 ++++++++++++---- tests/hash-target.cpp | 19 ++++++++++++--- tests/hash/main.cpp | 19 ++++++++++++--- tests/io.h | 19 ++++++++++++--- tests/net_load_tests/clt.cpp | 19 ++++++++++++--- tests/net_load_tests/net_load_tests.h | 19 ++++++++++++--- tests/net_load_tests/srv.cpp | 19 ++++++++++++--- .../node_rpc_proxy_test.cpp | 17 ++++++++++++++ .../performance_tests/check_ring_signature.h | 19 ++++++++++++--- tests/performance_tests/cn_slow_hash.h | 19 ++++++++++++--- tests/performance_tests/construct_tx.h | 19 ++++++++++++--- tests/performance_tests/derive_public_key.h | 19 ++++++++++++--- tests/performance_tests/derive_secret_key.h | 19 ++++++++++++--- .../generate_key_derivation.h | 19 ++++++++++++--- tests/performance_tests/generate_key_image.h | 19 ++++++++++++--- .../generate_key_image_helper.h | 19 ++++++++++++--- tests/performance_tests/is_out_to_acc.h | 19 ++++++++++++--- tests/performance_tests/main.cpp | 19 ++++++++++++--- tests/performance_tests/multi_tx_test_base.h | 19 ++++++++++++--- tests/performance_tests/performance_tests.h | 19 ++++++++++++--- tests/performance_tests/performance_utils.h | 19 ++++++++++++--- tests/performance_tests/single_tx_test_base.h | 19 ++++++++++++--- tests/unit_tests/INodeStubs.cpp | 19 ++++++++++++--- tests/unit_tests/INodeStubs.h | 19 ++++++++++++--- tests/unit_tests/TestBlockchainGenerator.cpp | 20 ++++++++++++---- tests/unit_tests/TestBlockchainGenerator.h | 19 ++++++++++++--- tests/unit_tests/base58.cpp | 19 ++++++++++++--- tests/unit_tests/block_reward.cpp | 19 ++++++++++++--- tests/unit_tests/chacha8.cpp | 19 ++++++++++++--- tests/unit_tests/checkpoints.cpp | 19 ++++++++++++--- .../decompose_amount_into_digits.cpp | 19 ++++++++++++--- tests/unit_tests/epee_boosted_tcp_server.cpp | 19 ++++++++++++--- .../epee_levin_protocol_handler_async.cpp | 19 ++++++++++++--- tests/unit_tests/get_xtype_from_string.cpp | 19 ++++++++++++--- tests/unit_tests/main.cpp | 19 ++++++++++++--- tests/unit_tests/mul_div.cpp | 19 ++++++++++++--- tests/unit_tests/parse_amount.cpp | 19 ++++++++++++--- tests/unit_tests/serialization.cpp | 19 ++++++++++++--- tests/unit_tests/test_format_utils.cpp | 19 ++++++++++++--- tests/unit_tests/test_peerlist.cpp | 19 ++++++++++++--- tests/unit_tests/test_protocol_pack.cpp | 19 ++++++++++++--- tests/unit_tests/test_wallet.cpp | 19 ++++++++++++--- tests/unit_tests/unit_tests_utils.h | 19 ++++++++++++--- utils/test-static-assert.c | 17 ++++++++++++++ 229 files changed, 3674 insertions(+), 714 deletions(-) diff --git a/include/INode.h b/include/INode.h index 4ee6ce3b17..9602611972 100644 --- a/include/INode.h +++ b/include/INode.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/include/IWallet.h b/include/IWallet.h index a89538d8bc..8d6c5308e8 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/ObserverManager.h b/src/common/ObserverManager.h index 02b59d9f11..772f8ae209 100644 --- a/src/common/ObserverManager.h +++ b/src/common/ObserverManager.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/base58.cpp b/src/common/base58.cpp index 575279aa55..ea71004a1c 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "base58.h" diff --git a/src/common/base58.h b/src/common/base58.h index 4055f62baf..ee947fc65d 100644 --- a/src/common/base58.h +++ b/src/common/base58.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 0bf9248028..5d08ee4b6a 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp index 0b90345d95..92208953f3 100644 --- a/src/common/command_line.cpp +++ b/src/common/command_line.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "command_line.h" diff --git a/src/common/command_line.h b/src/common/command_line.h index 860653772f..db0c45c961 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/int-util.h b/src/common/int-util.h index db9e9bea7d..50e59a6946 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/pod-class.h b/src/common/pod-class.h index c07edb2089..6badc12c8a 100644 --- a/src/common/pod-class.h +++ b/src/common/pod-class.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index 84fa73b924..20a36fd69e 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/util.cpp b/src/common/util.cpp index c9c470851c..98f0e807e2 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include diff --git a/src/common/util.h b/src/common/util.h index 8a1f4b0414..cac14bc2ff 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/common/varint.h b/src/common/varint.h index e62470fdfa..c1e178ece6 100644 --- a/src/common/varint.h +++ b/src/common/varint.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp index 6743b4ae81..a0e2ef0e71 100644 --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/connectivity_tool/conn_tool.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" @@ -350,4 +362,3 @@ int main(int argc, char* argv[]) return 1; } - diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 72f84e6274..9319375c80 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -53,4 +66,4 @@ namespace crypto { } } -#endif \ No newline at end of file +#endif diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 48bfe21a22..d25a400c89 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 97e7df50e1..9b0b185842 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 9d07fc8b02..fbd5bbe4e9 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index f5f525700c..dbd9e42a9c 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 61641fbcfc..bb598cffe8 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 8cade72a87..5e9e6da66c 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 2eeb520208..37e74f51d6 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index 7918cfc530..ab00dd63ab 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 15c271b2a2..2b012c24b0 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index 92361e6db4..c0327fbc97 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index c3aacb60b4..1a56787704 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/crypto/hash.c b/src/crypto/hash.c index a3989d88c3..17031b6b77 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/hash.h b/src/crypto/hash.h index bac2d266cd..4eaee990f3 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -63,4 +76,4 @@ namespace crypto { } -CRYPTO_MAKE_HASHABLE(hash) \ No newline at end of file +CRYPTO_MAKE_HASHABLE(hash) diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 8c84621bf3..9afa713783 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/crypto/random.c b/src/crypto/random.c index a17cb47d2f..08604f25c2 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/random.h b/src/crypto/random.h index afc8f3b2d4..5b68e81b26 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 44c6a1f0a7..1454c14e41 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/crypto/slow-hash.cpp b/src/crypto/slow-hash.cpp index 7141359ad4..7934203c0c 100644 --- a/src/crypto/slow-hash.cpp +++ b/src/crypto/slow-hash.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include diff --git a/src/crypto/slow-hash.inl b/src/crypto/slow-hash.inl index 0d9c3573b4..d6b07554ac 100644 --- a/src/crypto/slow-hash.inl +++ b/src/crypto/slow-hash.inl @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + static void #if defined(AESNI) cn_slow_hash_aesni diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index a2b0eeaa5b..1d0f684e73 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 224b06cd17..e7ce4bd334 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -78,4 +91,3 @@ #define MINER_CONFIG_FILE_NAME "miner_conf.json" #define THREAD_STACK_SIZE 5 * 1024 * 1024 - diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/cryptonote_core/SwappedMap.cpp index 44ed6ef26d..2a1775be98 100755 --- a/src/cryptonote_core/SwappedMap.cpp +++ b/src/cryptonote_core/SwappedMap.cpp @@ -1 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include "SwappedMap.h" diff --git a/src/cryptonote_core/SwappedMap.h b/src/cryptonote_core/SwappedMap.h index f03bf5c949..ca45b59ea4 100755 --- a/src/cryptonote_core/SwappedMap.h +++ b/src/cryptonote_core/SwappedMap.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #include diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/cryptonote_core/SwappedVector.cpp index df4e51568e..57d3eb7d87 100755 --- a/src/cryptonote_core/SwappedVector.cpp +++ b/src/cryptonote_core/SwappedVector.cpp @@ -1 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include "SwappedVector.h" diff --git a/src/cryptonote_core/SwappedVector.h b/src/cryptonote_core/SwappedVector.h index 7285c7fe82..4523b3c117 100755 --- a/src/cryptonote_core/SwappedVector.h +++ b/src/cryptonote_core/SwappedVector.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #include diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index ba39b9b775..3524c43b8c 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index 8b525da978..4574af5839 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_core/account_boost_serialization.h index 9cc36d14a6..ee21d71883 100644 --- a/src/cryptonote_core/account_boost_serialization.h +++ b/src/cryptonote_core/account_boost_serialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index c1cd4ad074..5ea4d09822 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include "blockchain_storage.h" diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index a64779ff7f..6b6b119cc3 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 3f0c94b390..5036e82118 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once /* diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index 33a2d29864..2ba058a53d 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" using namespace epee; diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h index 1bc055d913..1121711500 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_core/checkpoints.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h index 60e20e24be..5254332a4d 100644 --- a/src/cryptonote_core/checkpoints_create.h +++ b/src/cryptonote_core/checkpoints_create.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_core/connection_context.h index 53cac992d0..0fed9b0c45 100644 --- a/src/cryptonote_core/connection_context.h +++ b/src/cryptonote_core/connection_context.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 007e62bbdf..0f3c7aaba6 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index 348384c8fc..5053ea30e8 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" using namespace epee; diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index 27251ef54e..ae3dd747cf 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 80c4978444..53847498c7 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index b2dd7ddcef..004df14a85 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" using namespace epee; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index c74bb027ed..5c79f6da3a 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index da0e501e20..9de9e1412e 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" using namespace epee; diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 6153a0460f..3ff408a7dd 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_core/cryptonote_stat_info.h index 9d406748cd..6129dd3a82 100644 --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/cryptonote_core/cryptonote_stat_info.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "serialization/keyvalue_serialization.h" diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp index 3dde6ad6c1..7100da4e5e 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_core/difficulty.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h index aad1e27ca1..5a4c803c57 100644 --- a/src/cryptonote_core/difficulty.h +++ b/src/cryptonote_core/difficulty.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 88dad337a3..3fad129a7c 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include @@ -366,4 +378,3 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------------- } - diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index 28121e6654..1b0c19ab77 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -93,6 +106,3 @@ namespace cryptonote }; } - - - diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h index 37a04a41e7..1c73e6ec40 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_core/tx_extra.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 4f209ec401..ff80b60dcd 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 30b9061f31..3d0c3336e6 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "include_base_utils.h" diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index 210cc2d5b1..44bcc151bc 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once namespace cryptonote diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h index 23111f0487..a47e9d34f3 100644 --- a/src/cryptonote_protocol/blobdatatype.h +++ b/src/cryptonote_protocol/blobdatatype.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index d646a7f6fe..117bd67dd5 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 80538677cd..d7b467fe9f 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 2584f10971..8b53121d15 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include "cryptonote_core/cryptonote_format_utils.h" diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index f1ced5050d..dcd7dad3e8 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index f1f9171c0f..d3465b49f8 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . // node.cpp : Defines the entry point for the console application. // diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index 81d34b473c..7a6625aeb0 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp index e03f608533..260c2f5184 100644 --- a/src/miner/simpleminer.cpp +++ b/src/miner/simpleminer.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "common/command_line.h" #include "misc_log_ex.h" diff --git a/src/miner/simpleminer.h b/src/miner/simpleminer.h index 803ea12fba..70c7c40a94 100644 --- a/src/miner/simpleminer.h +++ b/src/miner/simpleminer.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "net/http_client.h" diff --git a/src/miner/simpleminer_protocol_defs.h b/src/miner/simpleminer_protocol_defs.h index 06b6a90535..5db5d91058 100644 --- a/src/miner/simpleminer_protocol_defs.h +++ b/src/miner/simpleminer_protocol_defs.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" @@ -105,4 +118,3 @@ namespace mining }; }; } - diff --git a/src/miner/target_helper.h b/src/miner/target_helper.h index 5ac6eed143..b3d0a2c135 100644 --- a/src/miner/target_helper.h +++ b/src/miner/target_helper.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "cryptonote_core/difficulty.h" diff --git a/src/node_rpc_proxy/InitState.h b/src/node_rpc_proxy/InitState.h index 13c8aad6de..40d166c944 100644 --- a/src/node_rpc_proxy/InitState.h +++ b/src/node_rpc_proxy/InitState.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/node_rpc_proxy/NodeErrors.cpp b/src/node_rpc_proxy/NodeErrors.cpp index 7558f62a63..a36696822c 100644 --- a/src/node_rpc_proxy/NodeErrors.cpp +++ b/src/node_rpc_proxy/NodeErrors.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "NodeErrors.h" diff --git a/src/node_rpc_proxy/NodeErrors.h b/src/node_rpc_proxy/NodeErrors.h index 0f21f6114d..f2ff10591b 100644 --- a/src/node_rpc_proxy/NodeErrors.h +++ b/src/node_rpc_proxy/NodeErrors.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/node_rpc_proxy/NodeRpcProxy.cpp b/src/node_rpc_proxy/NodeRpcProxy.cpp index 3effca9da6..c024a8b7da 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.cpp +++ b/src/node_rpc_proxy/NodeRpcProxy.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "NodeRpcProxy.h" diff --git a/src/node_rpc_proxy/NodeRpcProxy.h b/src/node_rpc_proxy/NodeRpcProxy.h index adf1f30fe3..db5faaa5c6 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.h +++ b/src/node_rpc_proxy/NodeRpcProxy.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 5a03b049f1..e844b42989 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 5c60be0af6..d56a83519d 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 17ae20cbe8..d1380f8961 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index ea541fcbc2..a57a1c0e5b 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index 23a253f252..cc6961da5c 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/p2p/p2p_networks.h b/src/p2p/p2p_networks.h index 3fa4090066..884c13051b 100644 --- a/src/p2p/p2p_networks.h +++ b/src/p2p/p2p_networks.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index fdf784f495..c0f6efeef4 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -310,6 +323,3 @@ namespace nodetool } - - - diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h index 1cc72f811c..4ec27541c2 100644 --- a/src/p2p/stdafx.h +++ b/src/p2p/stdafx.h @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -23,6 +35,3 @@ #define ENABLE_RELEASE_LOGGING #include "log_opt_defs.h" #include "misc_log_ex.h" - - - diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h index 9e18ae9e23..9e05e581e0 100644 --- a/src/platform/mingw/alloca.h +++ b/src/platform/mingw/alloca.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h index 4e8c89ea37..3c05f1c764 100644 --- a/src/platform/msc/alloca.h +++ b/src/platform/msc/alloca.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h index c2ba06b611..b202526b2e 100644 --- a/src/platform/msc/inline_c.h +++ b/src/platform/msc/inline_c.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h index 2e1a06732e..f98edb352c 100644 --- a/src/platform/msc/stdbool.h +++ b/src/platform/msc/stdbool.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h index b44de84b47..9bf3a26f40 100644 --- a/src/platform/msc/sys/param.h +++ b/src/platform/msc/sys/param.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 4815af2a53..da65ebc932 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include "include_base_utils.h" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 7b14e741a8..135733d34c 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 7a95731988..1b9c5d9f9f 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" @@ -431,4 +444,3 @@ namespace cryptonote }; } - diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index 10785f8aba..edb619b656 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -13,7 +26,3 @@ #define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6 #define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 #define CORE_RPC_ERROR_CODE_CORE_BUSY -9 - - - - diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index f28e45c0c1..02474c0578 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . /* binary_archive.h * diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index 00bb1741d6..d5ef26939d 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 6e683e6298..2ac4fd0a51 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h index 08baee0169..7bb4f39058 100644 --- a/src/serialization/debug_archive.h +++ b/src/serialization/debug_archive.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 49ad74d412..2107f49b63 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . /* json_archive.h * diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 24f5c11a81..9f129a9ff3 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index 7024fdc03f..d9c7f1c83e 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . /* serialization.h * diff --git a/src/serialization/string.h b/src/serialization/string.h index 437cf1c778..044de8c522 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 3b92fde202..1471c30b92 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/serialization/vector.h b/src/serialization/vector.h index d074723525..a0a03b4c30 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/simplewallet/password_container.cpp b/src/simplewallet/password_container.cpp index 0b9dc1cdff..9f345aaf25 100644 --- a/src/simplewallet/password_container.cpp +++ b/src/simplewallet/password_container.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "password_container.h" diff --git a/src/simplewallet/password_container.h b/src/simplewallet/password_container.h index 2e99d9a62f..0157b9c677 100644 --- a/src/simplewallet/password_container.h +++ b/src/simplewallet/password_container.h @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 51e80bdd03..785a630ad7 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2500553f09..f462cefe98 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index a327e1a2b4..18e3dd6f9c 100644 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "Wallet.h" #include "wallet_errors.h" diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index 6ca795d326..e7a25b6d8b 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/wallet/WalletAsyncContextCounter.cpp index 752933b6be..0359f5f9ba 100644 --- a/src/wallet/WalletAsyncContextCounter.cpp +++ b/src/wallet/WalletAsyncContextCounter.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletAsyncContextCounter.h" @@ -25,5 +38,3 @@ void WalletAsyncContextCounter::waitAsyncContextsFinish() { } } //namespace CryptoNote - - diff --git a/src/wallet/WalletAsyncContextCounter.h b/src/wallet/WalletAsyncContextCounter.h index c7acd6626d..5bf34d9661 100644 --- a/src/wallet/WalletAsyncContextCounter.h +++ b/src/wallet/WalletAsyncContextCounter.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletErrors.cpp b/src/wallet/WalletErrors.cpp index 792c046de6..926eca98de 100644 --- a/src/wallet/WalletErrors.cpp +++ b/src/wallet/WalletErrors.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletErrors.h" diff --git a/src/wallet/WalletErrors.h b/src/wallet/WalletErrors.h index e62617f2b8..477212e474 100644 --- a/src/wallet/WalletErrors.h +++ b/src/wallet/WalletErrors.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletEvent.h b/src/wallet/WalletEvent.h index 10bc210a42..611d2d65f0 100644 --- a/src/wallet/WalletEvent.h +++ b/src/wallet/WalletEvent.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -110,4 +123,3 @@ class WalletPendingBalanceUpdatedEvent : public WalletEvent }; } /* namespace CryptoNote */ - diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h index d3b43df77a..1b59c861f6 100644 --- a/src/wallet/WalletRequest.h +++ b/src/wallet/WalletRequest.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h index 796d5573a5..6e6c363dc1 100644 --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/wallet/WalletSendTransactionContext.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h index 5d8e2a1a7f..378b6d2f81 100644 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletSynchronizationContext.h b/src/wallet/WalletSynchronizationContext.h index 423da4944c..3682dcd340 100644 --- a/src/wallet/WalletSynchronizationContext.h +++ b/src/wallet/WalletSynchronizationContext.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletSynchronizer.cpp b/src/wallet/WalletSynchronizer.cpp index 9b94815663..d85cab88fa 100644 --- a/src/wallet/WalletSynchronizer.cpp +++ b/src/wallet/WalletSynchronizer.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletSynchronizer.h" diff --git a/src/wallet/WalletSynchronizer.h b/src/wallet/WalletSynchronizer.h index c86ee1310d..64886d481b 100644 --- a/src/wallet/WalletSynchronizer.h +++ b/src/wallet/WalletSynchronizer.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 737a769cc8..afdf5aac52 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -1,9 +1,19 @@ -/* - * WalletTransactionSender.cpp - * - * Created on: 18 Ð¸ÑŽÐ½Ñ 2014 г. - * Author: milo - */ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletTransactionSender.h" #include "WalletUtils.h" diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h index 9a51190c2a..87fd20d7a1 100644 --- a/src/wallet/WalletTransactionSender.h +++ b/src/wallet/WalletTransactionSender.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -52,4 +65,3 @@ class WalletTransactionSender }; } /* namespace CryptoNote */ - diff --git a/src/wallet/WalletTransferDetails.cpp b/src/wallet/WalletTransferDetails.cpp index 8c05ce8b28..9e3b596e68 100644 --- a/src/wallet/WalletTransferDetails.cpp +++ b/src/wallet/WalletTransferDetails.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletTransferDetails.h" diff --git a/src/wallet/WalletTransferDetails.h b/src/wallet/WalletTransferDetails.h index 7dbdae12a4..911b4d498f 100644 --- a/src/wallet/WalletTransferDetails.h +++ b/src/wallet/WalletTransferDetails.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletTxSendingState.cpp b/src/wallet/WalletTxSendingState.cpp index fe1fda6c1d..f142330880 100644 --- a/src/wallet/WalletTxSendingState.cpp +++ b/src/wallet/WalletTxSendingState.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletTxSendingState.h" diff --git a/src/wallet/WalletTxSendingState.h b/src/wallet/WalletTxSendingState.h index 65ba7d30b9..e697301654 100644 --- a/src/wallet/WalletTxSendingState.h +++ b/src/wallet/WalletTxSendingState.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index 53a632cb36..0ac37f63bd 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletUnconfirmedTransactions.h" #include "cryptonote_core/cryptonote_format_utils.h" diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index bfd99634de..addcfffa29 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -56,4 +69,3 @@ void WalletUnconfirmedTransactions::load(Archive& ar) } } // namespace CryptoNote - diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index f0adbc98c4..f14cf9a88a 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "WalletUserTransactionsCache.h" @@ -163,4 +176,3 @@ Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { } } //namespace CryptoNote - diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index 48971bfdde..dea8516747 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/WalletUtils.h b/src/wallet/WalletUtils.h index 46d57793ca..3bf3dcc074 100644 --- a/src/wallet/WalletUtils.h +++ b/src/wallet/WalletUtils.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once @@ -16,4 +29,3 @@ inline void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) } } //namespace CryptoNote - diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7e57d601ec..f737289de2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 6342e8c7de..1447be5db4 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 7189ca9f28..4ae8c9bb81 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 7c4c7e3d8c..ea446a4e5e 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" using namespace epee; diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index db49df574c..09f0988941 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index 217d7a2121..7649332f08 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "cryptonote_protocol/cryptonote_protocol_defs.h" @@ -125,4 +138,3 @@ namespace wallet_rpc }; } } - diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 7fa536dac9..0f2f9ab27e 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index d618040703..8a179a942c 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . // node.cpp : Defines the entry point for the console application. // diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 0d70708995..19abcafd84 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 7950738a67..918a2336eb 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index 506d7466c6..e23205ab5a 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 57e1471330..be58059a5e 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index fe5b859a7e..ef12d4b6d9 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index bd598852e6..082968b642 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" @@ -288,6 +301,3 @@ bool gen_simple_chain_split_1::check_orphaned_chain_41(cryptonote::core& c, size return true; }*/ //----------------------------------------------------------------------------------------------------- - - - diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h index df7efea82b..5d128ab1e3 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index a08cabeb34..15e12dd803 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 05e84e6cff..cf3a39d49c 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index c7ae28b9a0..1671713599 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index f17ae2896f..fca74ff5dc 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index f79420e691..0d59f88719 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index e6f287d74f..94b14823bc 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen_tests_list.h index d56f78121e..f08d741765 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen_tests_list.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 9a007ce261..423f3ab595 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 8242ba56b2..2614298968 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 2d75d994a5..f0b895a056 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once //====================================================================================================================== diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 5c619e97c9..3a9edcb7d1 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index 0e00ada323..39ef73d32b 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index bfdaf38f00..09698cc982 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h index 1d74f68026..b7f4c38a93 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 635c41503e..dbf41fa236 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" #include "cryptonote_core/cryptonote_basic_impl.h" diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index 460192802d..f46c316c70 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 93cb68c95d..1dbfa9d812 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "chaingen.h" #include "chaingen_tests_list.h" diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index e1939c0492..4ddd7c4db1 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once #include "chaingen.h" diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 5622acd03f..4180a134d9 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,5 +1,18 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "crypto/crypto-ops-data.c" diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index b21e2e03a4..d965a29497 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,5 +1,18 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "crypto/crypto-ops.c" diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index 80f658228c..5d85f65f7b 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index cbc5879ca1..3a3b04d3d4 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "crypto/crypto.cpp" diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index 6981f2be95..17d751abac 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "crypto/hash.c" #include "crypto/keccak.c" diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 5cb5c9ceba..fb85169a70 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/crypto/random.c b/tests/crypto/random.c index 05d7cf5ff7..b6968c73c4 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "crypto/random.c" diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp index 23b8b8335a..b558c9f4ee 100644 --- a/tests/daemon_tests/transfers.cpp +++ b/tests/daemon_tests/transfers.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" #include diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 2af2f79769..1de712b1f4 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index 330e7ef2fe..5708d8905d 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index fe26d04be3..f46df5681a 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include @@ -268,4 +281,3 @@ bool transactions_flow_test(std::string& working_folder, return true; } - diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h index 8f9061b92e..c9680a3d65 100644 --- a/tests/functional_tests/transactions_flow_test.h +++ b/tests/functional_tests/transactions_flow_test.h @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . bool transactions_flow_test(std::string& working_folder, diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp index de2ef541f2..d9ca1ed032 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "include_base_utils.h" using namespace epee; diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/tests/functional_tests/transactions_generation_from_blockchain.h index 64438488cc..aee92ba369 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.h +++ b/tests/functional_tests/transactions_generation_from_blockchain.h @@ -1,7 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . bool transactions_generation_from_blockchain(std::string& blockchain_path); diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index 208bb04a80..8379323390 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index da5a2e229c..ed3d6044a3 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/io.h b/tests/io.h index e3efa62ca2..bbec48535e 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 9c153a2999..9c503e1888 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index 3d95698354..d7c566081f 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 52895c9dd3..9c884d2c68 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp index e3aa703784..11f84cb038 100644 --- a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp +++ b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include #include diff --git a/tests/performance_tests/check_ring_signature.h b/tests/performance_tests/check_ring_signature.h index dafa172e37..9cfedf2d16 100644 --- a/tests/performance_tests/check_ring_signature.h +++ b/tests/performance_tests/check_ring_signature.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index 14c4a9ba1b..fc6d95f245 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index 1e70059410..ec91eeb064 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index ec9d64e092..937bc6475c 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index bd915846da..51226f09a1 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h index c2fce687d5..a645dd52f0 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index f6a00ffe35..32a8709e1a 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index 1c072f4430..c2ba87bdc3 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 4a33c25fbe..f2031e11b9 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 2c65f1c2ca..05d48ee06e 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "performance_tests.h" #include "performance_utils.h" diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index c919bdf2d2..f6d81da903 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index c7efe93f13..8ff767ffeb 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index 45ca5b27b1..2ae780032f 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 1c1fe655ba..bf499a01e1 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/unit_tests/INodeStubs.cpp b/tests/unit_tests/INodeStubs.cpp index fbc135c7e5..6f0c8dc784 100644 --- a/tests/unit_tests/INodeStubs.cpp +++ b/tests/unit_tests/INodeStubs.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "INodeStubs.h" #include "cryptonote_core/cryptonote_format_utils.h" diff --git a/tests/unit_tests/INodeStubs.h b/tests/unit_tests/INodeStubs.h index 28ffa4fb56..b25e2a5814 100644 --- a/tests/unit_tests/INodeStubs.h +++ b/tests/unit_tests/INodeStubs.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/unit_tests/TestBlockchainGenerator.cpp b/tests/unit_tests/TestBlockchainGenerator.cpp index 78f5ccd86d..41ac13d70b 100644 --- a/tests/unit_tests/TestBlockchainGenerator.cpp +++ b/tests/unit_tests/TestBlockchainGenerator.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include "TestBlockchainGenerator.h" @@ -110,4 +123,3 @@ bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::account return true; } - diff --git a/tests/unit_tests/TestBlockchainGenerator.h b/tests/unit_tests/TestBlockchainGenerator.h index 0c41a509bd..80fd5432c5 100644 --- a/tests/unit_tests/TestBlockchainGenerator.h +++ b/tests/unit_tests/TestBlockchainGenerator.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 87afd5d0e3..ecf127d789 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 2381d36861..39f9f68f6e 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/chacha8.cpp b/tests/unit_tests/chacha8.cpp index e3eeae5cfc..86654e6f11 100644 --- a/tests/unit_tests/chacha8.cpp +++ b/tests/unit_tests/chacha8.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index 0d7bd4de99..86d42d8f86 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp index 319b39e2f0..23d9d329cb 100644 --- a/tests/unit_tests/decompose_amount_into_digits.cpp +++ b/tests/unit_tests/decompose_amount_into_digits.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp index 1e606163d2..4c74bba9e7 100644 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ b/tests/unit_tests/epee_boosted_tcp_server.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp index da22d9d2d4..8eb20eac6f 100644 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ b/tests/unit_tests/epee_levin_protocol_handler_async.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index dd4d2e4190..a2c345bfa9 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 65bf247d8b..c6865d04b9 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 291e4f191e..129e33f29b 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index 7d0b640880..6b90051174 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 616509d82c..8f1f058050 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include #include diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index 980582ab39..37c37ca471 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index bd58ca7537..1ef556af9d 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index 18c7bc78b2..c5c7a9f21b 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index 8bbd838b8c..48efd03cec 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #include "gtest/gtest.h" diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index 9eebb7ed99..ee95ed881e 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,6 +1,19 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . #pragma once diff --git a/utils/test-static-assert.c b/utils/test-static-assert.c index fcac11ce77..ad2bca80d9 100644 --- a/utils/test-static-assert.c +++ b/utils/test-static-assert.c @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include static_assert(1, "FAIL"); From 4363a9f1001893c80ee2435399836cfe43b3014e Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Wed, 13 Aug 2014 11:51:37 +0100 Subject: [PATCH 12/59] Multi-signatures added --- CMakeLists.txt | 3 +- ReleaseNotes.txt | 12 + contrib/CMakeLists.txt | 7 + contrib/epee/demo/demo_levin_server/stdafx.h | 3 - contrib/epee/include/console_handler.h | 59 +- contrib/epee/include/file_io_utils.h | 13 +- contrib/epee/include/math_helper.h | 1 + contrib/epee/include/misc_log_ex.cpp | 1029 +++++++++++ contrib/epee/include/misc_log_ex.h | 1173 ++---------- contrib/epee/include/misc_os_dependent.cpp | 97 + contrib/epee/include/misc_os_dependent.h | 84 +- .../epee/include/net/abstract_tcp_server2.inl | 4 +- .../epee/include/net/http_protocol_handler.h | 1 + .../include/net/http_protocol_handler.inl | 2 + .../include/net/http_server_handlers_map2.h | 9 +- .../net/levin_protocol_handler_async.h | 1 + contrib/epee/include/net/net_parse_helpers.h | 1 + contrib/epee/include/net/net_utils_base.h | 5 + contrib/epee/include/profile_tools.h | 2 + .../keyvalue_serialization_overloads.h | 5 + contrib/epee/include/static_initializer.h | 27 - .../epee/include/storages/portable_storage.h | 7 +- .../storages/portable_storage_from_json.h | 4 + .../storages/portable_storage_to_bin.h | 1 + .../portable_storage_val_converters.h | 4 +- contrib/epee/include/string_tools.cpp | 487 +++++ contrib/epee/include/string_tools.h | 543 +----- contrib/epee/include/syncobj.h | 7 +- external/google/dense_hash_map | 333 ++++ external/google/dense_hash_set | 308 ++++ external/google/sparse_hash_map | 310 ++++ external/google/sparse_hash_set | 285 +++ external/google/sparsehash/densehashtable.h | 1268 +++++++++++++ external/google/sparsehash/hashtable-common.h | 178 ++ .../sparsehash/libc_allocator_with_realloc.h | 121 ++ external/google/sparsehash/os_config.h | 37 + external/google/sparsehash/sparseconfig.h | 39 + external/google/sparsehash/sparseconfig_win.h | 37 + external/google/sparsehash/sparsehashtable.h | 1190 ++++++++++++ external/google/sparsetable | 1598 +++++++++++++++++ external/google/type_traits.h | 336 ++++ include/INode.h | 2 +- include/IWallet.h | 4 +- src/CMakeLists.txt | 11 +- src/common/BlockingQueue.cpp | 18 + src/common/BlockingQueue.h | 126 ++ src/common/ObserverManager.h | 1 + src/common/SignalHandler.cpp | 54 + src/common/SignalHandler.h | 59 + src/common/base58.cpp | 1 - src/common/boost_serialization_helper.h | 9 + ...unordered_containers_boost_serialization.h | 67 + src/common/util.cpp | 22 +- src/common/util.h | 76 +- src/connectivity_tool/conn_tool.cpp | 23 +- src/crypto/hash-ops.h | 3 + src/crypto/hash.h | 8 + src/crypto/tree-hash.c | 72 + src/cryptonote_config.h | 192 +- src/cryptonote_core/AccountKVSerialization.h | 113 ++ src/cryptonote_core/BlockIndex.cpp | 87 + src/cryptonote_core/BlockIndex.h | 90 + src/cryptonote_core/Currency.cpp | 435 +++++ src/cryptonote_core/Currency.h | 238 +++ src/cryptonote_core/ITimeProvider.cpp | 18 + src/cryptonote_core/ITimeProvider.h | 35 + src/cryptonote_core/ITransactionValidator.h | 51 + src/cryptonote_core/SwappedVector.h | 135 +- src/cryptonote_core/UpgradeDetector.cpp | 18 + src/cryptonote_core/UpgradeDetector.h | 193 ++ src/cryptonote_core/account.cpp | 19 +- src/cryptonote_core/account.h | 31 +- .../account_boost_serialization.h | 6 +- src/cryptonote_core/blockchain_storage.cpp | 1095 +++++++---- src/cryptonote_core/blockchain_storage.h | 185 +- src/cryptonote_core/checkpoints_create.h | 46 - src/cryptonote_core/connection_context.h | 7 +- src/cryptonote_core/cryptonote_basic.h | 521 +++--- src/cryptonote_core/cryptonote_basic_impl.cpp | 164 +- src/cryptonote_core/cryptonote_basic_impl.h | 32 +- .../cryptonote_boost_serialization.h | 72 +- src/cryptonote_core/cryptonote_core.cpp | 244 +-- src/cryptonote_core/cryptonote_core.h | 79 +- .../cryptonote_format_utils.cpp | 525 +++--- src/cryptonote_core/cryptonote_format_utils.h | 120 +- src/cryptonote_core/difficulty.cpp | 46 - src/cryptonote_core/difficulty.h | 2 - src/cryptonote_core/i_miner_handler.h | 31 + src/cryptonote_core/miner.cpp | 136 +- src/cryptonote_core/miner.h | 43 +- src/cryptonote_core/tx_extra.h | 6 + src/cryptonote_core/tx_pool.cpp | 499 ++--- src/cryptonote_core/tx_pool.h | 188 +- src/cryptonote_core/verification_context.h | 1 + .../cryptonote_protocol_defs.h | 124 +- .../cryptonote_protocol_handler.inl | 70 +- .../cryptonote_protocol_handler_common.h | 23 +- src/daemon/daemon.cpp | 53 +- src/daemon/daemon_commands_handler.h | 36 +- src/node_rpc_proxy/NodeRpcProxy.cpp | 4 +- src/node_rpc_proxy/NodeRpcProxy.h | 4 +- src/p2p/net_node.h | 7 +- src/p2p/net_node.inl | 64 +- src/p2p/net_node_common.h | 2 +- src/p2p/net_peerlist.h | 6 +- src/p2p/p2p_protocol_defs.h | 4 + src/rpc/core_rpc_server.cpp | 183 +- src/rpc/core_rpc_server.h | 9 +- src/rpc/core_rpc_server_commands_defs.h | 135 +- src/simplewallet/simplewallet.cpp | 279 ++- src/simplewallet/simplewallet.h | 20 +- src/version.h.in | 4 +- src/wallet/Wallet.cpp | 46 +- src/wallet/Wallet.h | 16 +- src/wallet/WalletRequest.h | 8 +- src/wallet/WalletSendTransactionContext.h | 4 +- src/wallet/WalletSerialization.h | 9 +- src/wallet/WalletSynchronizationContext.h | 2 +- src/wallet/WalletSynchronizer.cpp | 57 +- src/wallet/WalletSynchronizer.h | 14 +- src/wallet/WalletTransactionSender.cpp | 56 +- src/wallet/WalletTransactionSender.h | 10 +- src/wallet/WalletTransferDetails.cpp | 28 +- src/wallet/WalletTransferDetails.h | 6 +- src/wallet/WalletUnconfirmedTransactions.cpp | 2 +- src/wallet/WalletUnconfirmedTransactions.h | 4 +- src/wallet/WalletUserTransactionsCache.cpp | 20 +- src/wallet/WalletUserTransactionsCache.h | 8 +- src/wallet/wallet2.cpp | 449 +++-- src/wallet/wallet2.h | 129 +- src/wallet/wallet_errors.h | 68 +- src/wallet/wallet_rpc_server.cpp | 10 +- tests/CMakeLists.txt | 33 +- tests/TestGenerator/TestGenerator.cpp | 367 ++++ tests/TestGenerator/TestGenerator.h | 115 ++ tests/core_proxy/core_proxy.cpp | 80 +- tests/core_proxy/core_proxy.h | 37 +- tests/core_tests/TestGenerator.h | 119 ++ tests/core_tests/TransactionBuilder.cpp | 186 ++ tests/core_tests/TransactionBuilder.h | 75 + tests/core_tests/block_reward.cpp | 151 +- tests/core_tests/block_reward.h | 2 +- tests/core_tests/block_validation.cpp | 474 +++-- tests/core_tests/block_validation.h | 269 ++- tests/core_tests/chain_split_1.cpp | 15 +- tests/core_tests/chain_switch_1.cpp | 29 +- tests/core_tests/chain_switch_1.h | 4 +- tests/core_tests/chaingen.cpp | 384 +--- tests/core_tests/chaingen.h | 263 ++- tests/core_tests/chaingen001.cpp | 27 +- .../{chaingen_tests_list.h => chaingen001.h} | 15 +- tests/core_tests/chaingen_main.cpp | 113 +- tests/core_tests/double_spend.cpp | 319 +++- tests/core_tests/double_spend.h | 108 +- tests/core_tests/double_spend.inl | 40 +- tests/core_tests/integer_overflow.cpp | 65 +- tests/core_tests/integer_overflow.h | 4 +- tests/core_tests/ring_signature_1.cpp | 67 +- tests/core_tests/transaction_tests.cpp | 70 +- tests/core_tests/transaction_tests.h | 2 - tests/core_tests/tx_validation.cpp | 413 +++-- tests/core_tests/tx_validation.h | 47 +- tests/core_tests/upgrade.cpp | 251 +++ tests/core_tests/upgrade.h | 49 + tests/difficulty/difficulty.cpp | 21 +- tests/functional_tests/main.cpp | 1 + .../transactions_flow_test.cpp | 40 +- ...ransactions_generation_from_blockchain.cpp | 6 +- tests/net_load_tests/clt.cpp | 2 + tests/net_load_tests/net_load_tests.h | 1 + tests/net_load_tests/srv.cpp | 3 + .../node_rpc_proxy_test.cpp | 2 +- .../performance_tests/check_ring_signature.h | 6 +- tests/performance_tests/construct_tx.h | 2 +- tests/performance_tests/derive_public_key.h | 4 +- tests/performance_tests/derive_secret_key.h | 2 +- tests/performance_tests/generate_key_image.h | 4 +- .../generate_key_image_helper.h | 3 +- tests/performance_tests/is_out_to_acc.h | 3 +- tests/performance_tests/multi_tx_test_base.h | 9 +- tests/performance_tests/single_tx_test_base.h | 5 +- tests/unit_tests/BlockingQueue.cpp | 211 +++ tests/unit_tests/INodeStubs.cpp | 10 +- tests/unit_tests/INodeStubs.h | 6 +- tests/unit_tests/TestBlockchainGenerator.cpp | 42 +- tests/unit_tests/TestBlockchainGenerator.h | 19 +- tests/unit_tests/TestUpgradeDetector.cpp | 309 ++++ tests/unit_tests/base58.cpp | 62 +- tests/unit_tests/block_reward.cpp | 474 +++-- tests/unit_tests/get_xtype_from_string.cpp | 3 + tests/unit_tests/main.cpp | 4 + tests/unit_tests/parse_amount.cpp | 9 +- tests/unit_tests/serialization.cpp | 24 +- tests/unit_tests/test_format_utils.cpp | 40 +- tests/unit_tests/test_protocol_pack.cpp | 2 + tests/unit_tests/test_wallet.cpp | 82 +- tests/unit_tests/tx_pool.cpp | 397 ++++ 197 files changed, 17943 insertions(+), 5921 deletions(-) create mode 100644 contrib/CMakeLists.txt create mode 100644 contrib/epee/include/misc_log_ex.cpp create mode 100644 contrib/epee/include/misc_os_dependent.cpp create mode 100644 contrib/epee/include/string_tools.cpp create mode 100644 external/google/dense_hash_map create mode 100644 external/google/dense_hash_set create mode 100644 external/google/sparse_hash_map create mode 100644 external/google/sparse_hash_set create mode 100644 external/google/sparsehash/densehashtable.h create mode 100644 external/google/sparsehash/hashtable-common.h create mode 100644 external/google/sparsehash/libc_allocator_with_realloc.h create mode 100644 external/google/sparsehash/os_config.h create mode 100644 external/google/sparsehash/sparseconfig.h create mode 100644 external/google/sparsehash/sparseconfig_win.h create mode 100644 external/google/sparsehash/sparsehashtable.h create mode 100644 external/google/sparsetable create mode 100644 external/google/type_traits.h create mode 100644 src/common/BlockingQueue.cpp create mode 100644 src/common/BlockingQueue.h create mode 100644 src/common/SignalHandler.cpp create mode 100644 src/common/SignalHandler.h create mode 100644 src/cryptonote_core/AccountKVSerialization.h create mode 100644 src/cryptonote_core/BlockIndex.cpp create mode 100644 src/cryptonote_core/BlockIndex.h create mode 100644 src/cryptonote_core/Currency.cpp create mode 100644 src/cryptonote_core/Currency.h create mode 100644 src/cryptonote_core/ITimeProvider.cpp create mode 100644 src/cryptonote_core/ITimeProvider.h create mode 100644 src/cryptonote_core/ITransactionValidator.h create mode 100644 src/cryptonote_core/UpgradeDetector.cpp create mode 100644 src/cryptonote_core/UpgradeDetector.h delete mode 100644 src/cryptonote_core/checkpoints_create.h create mode 100644 src/cryptonote_core/i_miner_handler.h create mode 100644 tests/TestGenerator/TestGenerator.cpp create mode 100644 tests/TestGenerator/TestGenerator.h create mode 100644 tests/core_tests/TestGenerator.h create mode 100644 tests/core_tests/TransactionBuilder.cpp create mode 100644 tests/core_tests/TransactionBuilder.h rename tests/core_tests/{chaingen_tests_list.h => chaingen001.h} (75%) create mode 100644 tests/core_tests/upgrade.cpp create mode 100644 tests/core_tests/upgrade.h create mode 100644 tests/unit_tests/BlockingQueue.cpp create mode 100644 tests/unit_tests/TestUpgradeDetector.cpp create mode 100644 tests/unit_tests/tx_pool.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd3638aa4..243001d1d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ if(MSVC) # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760") if(STATIC) - foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE) + foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) string(REPLACE "/MD" "/MT" ${VAR} "${${VAR}}") endforeach() endif() @@ -122,6 +122,7 @@ else() endif() endif() +add_subdirectory(contrib) add_subdirectory(external) add_subdirectory(src) add_subdirectory(tests) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 69836befed..c97cbba0a3 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,15 @@ +Release notes 1.0.0 + +- Multi-signatures +- Updated block reward scheme +- Further optimization in daemon RAM consumption +- Faster wallet refresh +- Transaction priority based on tx fee +- Transactions are returned from tx pools after 24 hours +- Dynamic maximum block size limit +- Reduced default transaction fee +- Various network health updates + Release notes 0.8.11 - Increased minimum transaction fee diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt new file mode 100644 index 0000000000..7222cce5ee --- /dev/null +++ b/contrib/CMakeLists.txt @@ -0,0 +1,7 @@ +file(GLOB_RECURSE EPEE epee/include/*) + +source_group(epee FILES ${EPEE}) + +add_library(epee ${EPEE}) + +set_property(TARGET epee PROPERTY FOLDER "external") diff --git a/contrib/epee/demo/demo_levin_server/stdafx.h b/contrib/epee/demo/demo_levin_server/stdafx.h index f69d5922b1..cc4558434d 100644 --- a/contrib/epee/demo/demo_levin_server/stdafx.h +++ b/contrib/epee/demo/demo_levin_server/stdafx.h @@ -35,7 +35,4 @@ #define BOOST_FILESYSTEM_VERSION 3 #define ENABLE_RELEASE_LOGGING -#include "log_opt_defs.h" #include "misc_log_ex.h" - - diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index fcab35aaac..a1df78395f 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -32,6 +32,11 @@ #include #include +#include +#include + +#include "string_tools.h" + namespace epee { class async_stdin_reader @@ -294,7 +299,7 @@ namespace epee bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") { std::shared_ptr console_handler = std::make_shared(); - boost::thread([=](){console_handler->run(ptsrv, handlr, prompt, usage);}).detach(); + std::thread([=](){console_handler->run(ptsrv, handlr, prompt, usage);}).detach(); return true; } @@ -314,46 +319,24 @@ namespace epee bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") { async_console_handler console_handler; - return console_handler.run(ptsrv, boost::bind(no_srv_param_adapter, _1, _2, handlr), prompt, usage); + return console_handler.run(ptsrv, std::bind(no_srv_param_adapter, std::placeholders::_1, std::placeholders::_2, handlr), prompt, usage); } template bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") { - boost::thread( boost::bind(run_default_console_handler_no_srv_param, ptsrv, handlr, prompt, usage) ); + std::thread( std::bind(run_default_console_handler_no_srv_param, ptsrv, handlr, prompt, usage) ); return true; } - /*template - bool f(int i, a l) - { - return true; - }*/ - /* - template - bool default_console_handler2(chain_handler ch_handler, const std::string usage) - */ - - - /*template - bool start_default_console2(t_handler handlr, const std::string& usage = "") - { - //std::string usage_local = usage; - boost::thread( boost::bind(default_console_handler2, handlr, usage) ); - //boost::function p__ = boost::bind(f, 1, handlr); - //boost::function p__ = boost::bind(default_console_handler2, handlr, usage); - //boost::thread tr(p__); - return true; - }*/ - /************************************************************************/ /* */ /************************************************************************/ class console_handlers_binder { - typedef boost::function &)> console_command_handler; + typedef std::function &)> console_command_handler; typedef std::map > command_handlers_map; - std::unique_ptr m_console_thread; + std::unique_ptr m_console_thread; command_handlers_map m_command_handlers; async_console_handler m_console_handler; public: @@ -396,16 +379,9 @@ namespace epee return process_command_vec(cmd_v); } - /*template - bool start_handling(t_srv& srv, const std::string& usage_string = "") - { - start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1)); - return true; - }*/ - bool start_handling(const std::string& prompt, const std::string& usage_string = "") { - m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, prompt, usage_string))); + m_console_thread.reset(new std::thread(std::bind(&console_handlers_binder::run_handling, this, prompt, usage_string))); m_console_thread->detach(); return true; } @@ -417,14 +393,8 @@ namespace epee bool run_handling(const std::string& prompt, const std::string& usage_string) { - return m_console_handler.run(boost::bind(&console_handlers_binder::process_command_str, this, _1), prompt, usage_string); + return m_console_handler.run(std::bind(&console_handlers_binder::process_command_str, this, std::placeholders::_1), prompt, usage_string); } - - /*template - bool run_handling(t_srv& srv, const std::string& usage_string) - { - return run_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string); - }*/ }; /* work around because of broken boost bind */ @@ -438,13 +408,14 @@ namespace epee public: bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "") { - boost::thread(boost::bind(&srv_console_handlers_binder::run_handling, this, psrv, prompt, usage_string)).detach(); + std::thread(std::bind(&srv_console_handlers_binder::run_handling, this, psrv, prompt, usage_string)).detach(); return true; } bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string) { - return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder::process_command_str, this, _1, _2), prompt, usage_string); + return m_console_handler.run(psrv, std::bind(&srv_console_handlers_binder::process_command_str, this, + std::placeholders::_1, std::placeholders::_2), prompt, usage_string); } void stop_handling() diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h index 7e8521838c..01d70db3ef 100644 --- a/contrib/epee/include/file_io_utils.h +++ b/contrib/epee/include/file_io_utils.h @@ -28,10 +28,7 @@ #ifndef _FILE_IO_UTILS_H_ #define _FILE_IO_UTILS_H_ - -//#include -//#include - +#include #include #include @@ -75,7 +72,7 @@ namespace file_io_utils #ifdef BOOST_LEXICAL_CAST_INCLUDED inline - bool get_not_used_filename(const std::string& folder, OUT std::string& result_name) + bool get_not_used_filename(const std::string& folder, std::string& result_name) { DWORD folder_attr = ::GetFileAttributesA(folder.c_str()); if(folder_attr == INVALID_FILE_ATTRIBUTES) @@ -302,7 +299,7 @@ namespace file_io_utils } */ inline - bool get_file_time(const std::string& path_to_file, OUT time_t& ft) + bool get_file_time(const std::string& path_to_file, time_t& ft) { boost::system::error_code ec; ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec); @@ -408,7 +405,7 @@ namespace file_io_utils } */ #ifdef WINDOWS_PLATFORM - inline bool get_folder_content(const std::string& path, std::list& OUT target_list) + inline bool get_folder_content(const std::string& path, std::list& target_list) { WIN32_FIND_DATAA find_data = {0}; HANDLE hfind = ::FindFirstFileA((path + "\\*.*").c_str(), &find_data); @@ -426,7 +423,7 @@ namespace file_io_utils return true; } #endif - inline bool get_folder_content(const std::string& path, std::list& OUT target_list, bool only_files = false) + inline bool get_folder_content(const std::string& path, std::list& target_list, bool only_files = false) { try { diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h index 349d6d822b..11faa9762f 100644 --- a/contrib/epee/include/math_helper.h +++ b/contrib/epee/include/math_helper.h @@ -37,6 +37,7 @@ #include #include "misc_os_dependent.h" +#include "pragma_comp_defs.h" namespace epee { diff --git a/contrib/epee/include/misc_log_ex.cpp b/contrib/epee/include/misc_log_ex.cpp new file mode 100644 index 0000000000..0c0b441b4f --- /dev/null +++ b/contrib/epee/include/misc_log_ex.cpp @@ -0,0 +1,1029 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_log_ex.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(WIN32) +#include +#else +#include +#endif + +#include "static_initializer.h" +#include "string_tools.h" +#include "time_helper.h" +#include "misc_os_dependent.h" + +#include "syncobj.h" + + +#define LOG_LEVEL_SILENT -1 +#define LOG_LEVEL_0 0 +#define LOG_LEVEL_1 1 +#define LOG_LEVEL_2 2 +#define LOG_LEVEL_3 3 +#define LOG_LEVEL_4 4 +#define LOG_LEVEL_MIN LOG_LEVEL_SILENT +#define LOG_LEVEL_MAX LOG_LEVEL_4 + + +#define LOGGER_NULL 0 +#define LOGGER_FILE 1 +#define LOGGER_DEBUGGER 2 +#define LOGGER_CONSOLE 3 +#define LOGGER_DUMP 4 + + +#ifndef LOCAL_ASSERT +#include +#if (defined _MSC_VER) +#define LOCAL_ASSERT(expr) {if(epee::debug::get_set_enable_assert()){_ASSERTE(expr);}} +#else +#define LOCAL_ASSERT(expr) +#endif + +#endif + +namespace epee { +namespace log_space { + //---------------------------------------------------------------------------- + bool is_stdout_a_tty() + { + static std::atomic initialized(false); + static std::atomic is_a_tty(false); + + if (!initialized.load(std::memory_order_acquire)) + { +#if defined(WIN32) + is_a_tty.store(0 != _isatty(_fileno(stdout)), std::memory_order_relaxed); +#else + is_a_tty.store(0 != isatty(fileno(stdout)), std::memory_order_relaxed); +#endif + initialized.store(true, std::memory_order_release); + } + + return is_a_tty.load(std::memory_order_relaxed); + } + //---------------------------------------------------------------------------- + void set_console_color(int color, bool bright) + { + if (!is_stdout_a_tty()) + return; + + switch(color) + { + case console_color_default: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;37m"; + else + std::cout << "\033[0m"; +#endif + } + break; + case console_color_white: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;37m"; + else + std::cout << "\033[0;37m"; +#endif + } + break; + case console_color_red: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;31m"; + else + std::cout << "\033[0;31m"; +#endif + } + break; + case console_color_green: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;32m"; + else + std::cout << "\033[0;32m"; +#endif + } + break; + + case console_color_blue: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;34m"; + else + std::cout << "\033[0;34m"; +#endif + } + break; + + case console_color_cyan: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;36m"; + else + std::cout << "\033[0;36m"; +#endif + } + break; + + case console_color_magenta: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;35m"; + else + std::cout << "\033[0;35m"; +#endif + } + break; + + case console_color_yellow: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;33m"; + else + std::cout << "\033[0;33m"; +#endif + } + break; + + } + } + //---------------------------------------------------------------------------- + void reset_console_color() { + if (!is_stdout_a_tty()) + return; + +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); +#else + std::cout << "\033[0m"; + std::cout.flush(); +#endif + } + //---------------------------------------------------------------------------- + bool rotate_log_file(const char* pfile_path) + { +#ifdef _MSC_VER + if(!pfile_path) + return false; + + std::string file_path = pfile_path; + std::string::size_type a = file_path .rfind('.'); + if ( a != std::string::npos ) + file_path .erase( a, file_path .size()); + + ::DeleteFileA( (file_path + ".0").c_str() ); + ::MoveFileA( (file_path + ".log").c_str(), (file_path + ".0").c_str() ); +#else + return false;//not implemented yet +#endif + return true; + } + //---------------------------------------------------------------------------- +#ifdef _MSC_VER + bool debug_output_stream::out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name/* = NULL*/) + { + for ( int i = 0; i < buffer_len; i = i + max_dbg_str_len ) + { + std::string s( buffer + i, buffer_len- i < max_dbg_str_len ? + buffer_len - i : max_dbg_str_len ); + + ::OutputDebugStringA( s.c_str() ); + } + return true; + } +#endif + //---------------------------------------------------------------------------- + console_output_stream::console_output_stream() + { +#ifdef _MSC_VER + + if(!::GetStdHandle(STD_OUTPUT_HANDLE)) + m_have_to_kill_console = true; + else + m_have_to_kill_console = false; + + ::AllocConsole(); +#endif + } + //---------------------------------------------------------------------------- + console_output_stream::~console_output_stream() + { +#ifdef _MSC_VER + if(m_have_to_kill_console) + ::FreeConsole(); +#endif + } + //---------------------------------------------------------------------------- + bool console_output_stream::out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name/* = NULL*/) + { + if(plog_name) + return true; //skip alternative logs from console + + set_console_color(color, log_level < 1); + +#ifdef _MSC_VER + const char* ptarget_buf = NULL; + char* pallocated_buf = NULL; + + // + int i = 0; + for(; i < buffer_len; i++) + if(buffer[i] == '\a') break; + if(i == buffer_len) + ptarget_buf = buffer; + else + { + pallocated_buf = new char[buffer_len]; + ptarget_buf = pallocated_buf; + for(i = 0; i < buffer_len; i++) + { + if(buffer[i] == '\a') + pallocated_buf[i] = '^'; + else + pallocated_buf[i] = buffer[i]; + } + } + + //uint32_t b = 0; + //::WriteConsoleA(::GetStdHandle(STD_OUTPUT_HANDLE), ptarget_buf, buffer_len, (DWORD*)&b, 0); + std::cout << ptarget_buf; + if(pallocated_buf) delete [] pallocated_buf; +#else + std::string buf(buffer, buffer_len); + for(size_t i = 0; i!= buf.size(); i++) + { + if(buf[i] == 7 || buf[i] == -107) + buf[i] = '^'; + } + + std::cout << buf; +#endif + reset_console_color(); + return true; + } + //---------------------------------------------------------------------------- + file_output_stream::file_output_stream(std::string default_log_file_name, std::string log_path) + { + m_default_log_filename = default_log_file_name; + m_max_logfile_size = 0; + m_default_log_path = log_path; + m_pdefault_file_stream = add_new_stream_and_open(default_log_file_name.c_str()); + } + //---------------------------------------------------------------------------- + file_output_stream::~file_output_stream() + { + for(named_log_streams::iterator it = m_log_file_names.begin(); it!=m_log_file_names.end(); it++) + { + if ( it->second->is_open() ) + { + it->second->flush(); + it->second->close(); + } + delete it->second; + } + } + //---------------------------------------------------------------------------- + std::ofstream* file_output_stream::add_new_stream_and_open(const char* pstream_name) + { + //log_space::rotate_log_file((m_default_log_path + "\\" + pstream_name).c_str()); + + std::ofstream* pstream = (m_log_file_names[pstream_name] = new std::ofstream); + std::string target_path = m_default_log_path + "/" + pstream_name; + pstream->open( target_path.c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); + if(pstream->fail()) + return NULL; + return pstream; + } + //---------------------------------------------------------------------------- + bool file_output_stream::set_max_logfile_size(uint64_t max_size) + { + m_max_logfile_size = max_size; + return true; + } + //---------------------------------------------------------------------------- + bool file_output_stream::set_log_rotate_cmd(const std::string& cmd) + { + m_log_rotate_cmd = cmd; + return true; + } + //---------------------------------------------------------------------------- + bool file_output_stream::out_buffer(const char* buffer, int buffer_len, int log_level, int color, const char* plog_name/* = NULL*/) + { + std::ofstream* m_target_file_stream = m_pdefault_file_stream; + if(plog_name) + { //find named stream + named_log_streams::iterator it = m_log_file_names.find(plog_name); + if(it == m_log_file_names.end()) + m_target_file_stream = add_new_stream_and_open(plog_name); + else + m_target_file_stream = it->second; + } + if(!m_target_file_stream || !m_target_file_stream->is_open()) + return false;//TODO: add assert here + + m_target_file_stream->write(buffer, buffer_len ); + m_target_file_stream->flush(); + + if(m_max_logfile_size) + { + std::ofstream::pos_type pt = m_target_file_stream->tellp(); + uint64_t current_sz = pt; + if(current_sz > m_max_logfile_size) + { + std::cout << "current_sz= " << current_sz << " m_max_logfile_size= " << m_max_logfile_size << std::endl; + std::string log_file_name; + if(!plog_name) + log_file_name = m_default_log_filename; + else + log_file_name = plog_name; + + m_target_file_stream->close(); + std::string new_log_file_name = log_file_name; + + time_t tm = 0; + time(&tm); + + int err_count = 0; + boost::system::error_code ec; + do + { + new_log_file_name = string_tools::cut_off_extension(log_file_name); + if(err_count) + new_log_file_name += misc_utils::get_time_str_v2(tm) + "(" + boost::lexical_cast(err_count) + ")" + ".log"; + else + new_log_file_name += misc_utils::get_time_str_v2(tm) + ".log"; + + err_count++; + }while(boost::filesystem::exists(m_default_log_path + "/" + new_log_file_name, ec)); + + std::string new_log_file_path = m_default_log_path + "/" + new_log_file_name; + boost::filesystem::rename(m_default_log_path + "/" + log_file_name, new_log_file_path, ec); + if(ec) + { + std::cout << "Filed to rename, ec = " << ec.message() << std::endl; + } + + if(m_log_rotate_cmd.size()) + { + + std::string m_log_rotate_cmd_local_copy = m_log_rotate_cmd; + //boost::replace_all(m_log_rotate_cmd, "[*SOURCE*]", new_log_file_path); + boost::replace_all(m_log_rotate_cmd_local_copy, "[*TARGET*]", new_log_file_path); + + misc_utils::call_sys_cmd(m_log_rotate_cmd_local_copy); + } + + m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); + if(m_target_file_stream->fail()) + return false; + } + } + + return true; + } + //---------------------------------------------------------------------------- + log_stream_splitter::~log_stream_splitter() + { + //free pointers + std::for_each(m_log_streams.begin(), m_log_streams.end(), delete_ptr()); + } + //---------------------------------------------------------------------------- + bool log_stream_splitter::set_max_logfile_size(uint64_t max_size) + { + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + it->first->set_max_logfile_size(max_size); + return true; + } + //---------------------------------------------------------------------------- + bool log_stream_splitter::set_log_rotate_cmd(const std::string& cmd) + { + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + it->first->set_log_rotate_cmd(cmd); + return true; + } + //---------------------------------------------------------------------------- + bool log_stream_splitter::do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name/* = NULL*/) + { + std::string str_mess = rlog_mes; + size_t str_len = str_mess.size(); + const char* pstr = str_mess.c_str(); + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + if(it->second >= log_level) + it->first->out_buffer(pstr, (int)str_len, log_level, color, plog_name); + return true; + } + //---------------------------------------------------------------------------- + bool log_stream_splitter::add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit/* = LOG_LEVEL_4*/) + { + ibase_log_stream* ls = NULL; + + switch( type ) + { + case LOGGER_FILE: + ls = new file_output_stream( pdefault_file_name, pdefault_log_folder ); + break; + + case LOGGER_DEBUGGER: +#ifdef _MSC_VER + ls = new debug_output_stream( ); +#else + return false;//not implemented yet +#endif + break; + case LOGGER_CONSOLE: + ls = new console_output_stream( ); + break; + } + + if ( ls ) { + m_log_streams.push_back(streams_container::value_type(ls, log_level_limit)); + return true; + } + return ls ? true:false; + } + //---------------------------------------------------------------------------- + bool log_stream_splitter::add_logger(ibase_log_stream* pstream, int log_level_limit/* = LOG_LEVEL_4*/) + { + m_log_streams.push_back(streams_container::value_type(pstream, log_level_limit) ); + return true; + } + //---------------------------------------------------------------------------- + bool log_stream_splitter::remove_logger(int type) + { + streams_container::iterator it = m_log_streams.begin(); + for(;it!=m_log_streams.end(); it++) + { + if(it->first->get_type() == type) + { + delete it->first; + m_log_streams.erase(it); + return true; + } + } + return false; + } + //---------------------------------------------------------------------------- + std::string get_daytime_string2() + { + boost::posix_time::ptime p = boost::posix_time::microsec_clock::local_time(); + return misc_utils::get_time_str_v3(p); + } + //---------------------------------------------------------------------------- + std::string get_day_time_string() + { + return get_daytime_string2(); + //time_t tm = 0; + //time(&tm); + //return misc_utils::get_time_str(tm); + } + //---------------------------------------------------------------------------- + std::string get_time_string() + { + return get_daytime_string2(); + } + //---------------------------------------------------------------------------- +#ifdef _MSC_VER + std::string get_time_string_adv(SYSTEMTIME* pst/* = NULL*/) + { + SYSTEMTIME st = {0}; + if(!pst) + { + pst = &st; + GetSystemTime(&st); + } + std::stringstream str_str; + str_str.fill('0'); + str_str << std::setw(2) << pst->wHour << "_" + << std::setw(2) << pst->wMinute << "_" + << std::setw(2) << pst->wSecond << "_" + << std::setw(3) << pst->wMilliseconds; + return str_str.str(); + } +#endif + //---------------------------------------------------------------------------- + logger::logger() + { + CRITICAL_REGION_BEGIN(m_critical_sec); + init(); + CRITICAL_REGION_END(); + } + //---------------------------------------------------------------------------- + bool logger::set_max_logfile_size(uint64_t max_size) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.set_max_logfile_size(max_size); + CRITICAL_REGION_END(); + return true; + } + //---------------------------------------------------------------------------- + bool logger::set_log_rotate_cmd(const std::string& cmd) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.set_log_rotate_cmd(cmd); + CRITICAL_REGION_END(); + return true; + } + //---------------------------------------------------------------------------- + bool logger::take_away_journal(std::list& journal) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_journal.swap(journal); + CRITICAL_REGION_END(); + return true; + } + //---------------------------------------------------------------------------- + bool logger::do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal/* = false*/, const char* plog_name/* = NULL*/) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.do_log_message(rlog_mes, log_level, color, plog_name); + if(add_to_journal) + m_journal.push_back(rlog_mes); + + return true; + CRITICAL_REGION_END(); + } + //---------------------------------------------------------------------------- + bool logger::add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit/* = LOG_LEVEL_4*/) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.add_logger( type, pdefault_file_name, pdefault_log_folder, log_level_limit); + CRITICAL_REGION_END(); + } + //---------------------------------------------------------------------------- + bool logger::add_logger( ibase_log_stream* pstream, int log_level_limit/* = LOG_LEVEL_4*/) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.add_logger(pstream, log_level_limit); + CRITICAL_REGION_END(); + } + //---------------------------------------------------------------------------- + bool logger::remove_logger(int type) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.remove_logger(type); + CRITICAL_REGION_END(); + } + //---------------------------------------------------------------------------- + bool logger::set_thread_prefix(const std::string& prefix) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_thr_prefix_strings[misc_utils::get_thread_string_id()] = prefix; + CRITICAL_REGION_END(); + return true; + } + //---------------------------------------------------------------------------- + bool logger::init() + { + m_process_name = string_tools::get_current_module_name(); + + init_log_path_by_default(); + + //init default set of loggers + init_default_loggers(); + + std::stringstream ss; + ss << get_time_string() << " Init logging. Level=" << get_set_log_detalisation_level() + << " Log path=" << m_default_log_folder << std::endl; + this->do_log_message(ss.str(), console_color_white, LOG_LEVEL_0); + return true; + } + //---------------------------------------------------------------------------- + bool logger::init_default_loggers() + { + return true; + } + //---------------------------------------------------------------------------- + bool logger::init_log_path_by_default() + { + //load process name + m_default_log_folder = string_tools::get_current_module_folder(); + + m_default_log_file = m_process_name; + std::string::size_type a = m_default_log_file.rfind('.'); + if ( a != std::string::npos ) + m_default_log_file.erase( a, m_default_log_file.size()); + m_default_log_file += ".log"; + + return true; + } + //---------------------------------------------------------------------------- + int log_singletone::get_log_detalisation_level() + { + get_or_create_instance();//to initialize logger, if it not initialized + return get_set_log_detalisation_level(); + } + //---------------------------------------------------------------------------- + bool log_singletone::is_filter_error(int error_code) + { + return false; + } + //---------------------------------------------------------------------------- + bool log_singletone::do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name/* = NULL*/) + { + logger* plogger = get_or_create_instance(); + bool res = false; + if(plogger) + res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); + else + { //globally uninitialized, create new logger for each call of do_log_message() and then delete it + plogger = new logger(); + //TODO: some extra initialization + res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); + delete plogger; + plogger = NULL; + } + return res; + } + //---------------------------------------------------------------------------- + bool log_singletone::take_away_journal(std::list& journal) + { + logger* plogger = get_or_create_instance(); + bool res = false; + if(plogger) + res = plogger->take_away_journal(journal); + + return res; + } + //---------------------------------------------------------------------------- + bool log_singletone::set_max_logfile_size(uint64_t file_size) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_max_logfile_size(file_size); + } + //---------------------------------------------------------------------------- + bool log_singletone::set_log_rotate_cmd(const std::string& cmd) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_log_rotate_cmd(cmd); + } + //---------------------------------------------------------------------------- + bool log_singletone::add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit/* = LOG_LEVEL_4*/) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->add_logger(type, pdefault_file_name, pdefault_log_folder, log_level_limit); + } + //---------------------------------------------------------------------------- + std::string log_singletone::get_default_log_file() + { + logger* plogger = get_or_create_instance(); + if(plogger) + return plogger->get_default_log_file(); + + return ""; + } + //---------------------------------------------------------------------------- + std::string log_singletone::get_default_log_folder() + { + logger* plogger = get_or_create_instance(); + if(plogger) + return plogger->get_default_log_folder(); + + return ""; + } + //---------------------------------------------------------------------------- + bool log_singletone::add_logger(ibase_log_stream* pstream, int log_level_limit/* = LOG_LEVEL_4*/) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->add_logger(pstream, log_level_limit); + } + //---------------------------------------------------------------------------- + bool log_singletone::remove_logger(int type) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->remove_logger(type); + } + //---------------------------------------------------------------------------- +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + int log_singletone::get_set_log_detalisation_level(bool is_need_set/* = false*/, int log_level_to_set/* = LOG_LEVEL_1*/) + { + static int log_detalisation_level = LOG_LEVEL_1; + if(is_need_set) + log_detalisation_level = log_level_to_set; + return log_detalisation_level; + } +POP_WARNINGS + //---------------------------------------------------------------------------- + int log_singletone::get_set_time_level(bool is_need_set/* = false*/, int time_log_level/* = LOG_LEVEL_0*/) + { + static int val_time_log_level = LOG_LEVEL_0; + if(is_need_set) + val_time_log_level = time_log_level; + + return val_time_log_level; + } + //---------------------------------------------------------------------------- + int log_singletone::get_set_process_level(bool is_need_set/* = false*/, int process_log_level/* = LOG_LEVEL_0*/) + { + static int val_process_log_level = LOG_LEVEL_0; + if(is_need_set) + val_process_log_level = process_log_level; + + return val_process_log_level; + } + //---------------------------------------------------------------------------- + bool log_singletone::get_set_need_thread_id(bool is_need_set/* = false*/, bool is_need_val/* = false*/) + { + static bool is_need = false; + if(is_need_set) + is_need = is_need_val; + + return is_need; + } + //---------------------------------------------------------------------------- + bool log_singletone::get_set_need_proc_name(bool is_need_set/* = false*/, bool is_need_val/* = false*/) + { + static bool is_need = true; + if(is_need_set) + is_need = is_need_val; + + return is_need; + } + //---------------------------------------------------------------------------- + uint64_t log_singletone::get_set_err_count(bool is_need_set/* = false*/, uint64_t err_val/* = false*/) + { + static uint64_t err_count = 0; + if(is_need_set) + err_count = err_val; + + return err_count; + } + //---------------------------------------------------------------------------- +#ifdef _MSC_VER + void log_singletone::SetThreadName( DWORD dwThreadID, const char* threadName) + { +#define MS_VC_EXCEPTION 0x406D1388 + +#pragma pack(push,8) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; +#pragma pack(pop) + + Sleep(10); + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = (char*)threadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + } +#endif + //---------------------------------------------------------------------------- + bool log_singletone::set_thread_log_prefix(const std::string& prefix) + { +#ifdef _MSC_VER + SetThreadName(-1, prefix.c_str()); +#endif + + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_thread_prefix(prefix); + } + //---------------------------------------------------------------------------- + std::string log_singletone::get_prefix_entry() + { + std::stringstream str_prefix; + //write time entry + if ( get_set_time_level() <= get_set_log_detalisation_level() ) + str_prefix << get_day_time_string() << " "; + + //write process info + logger* plogger = get_or_create_instance(); + //bool res = false; + if(!plogger) + { //globally uninitialized, create new logger for each call of get_prefix_entry() and then delete it + plogger = new logger(); + } + + //if ( get_set_need_proc_name() && get_set_process_level() <= get_set_log_detalisation_level() ) + // str_prefix << "[" << plogger->m_process_name << " (id=" << GetCurrentProcessId() << ")] "; +//#ifdef _MSC_VER_EX + if ( get_set_need_thread_id() /*&& get_set_tid_level() <= get_set_log_detalisation_level()*/ ) + str_prefix << "tid:" << misc_utils::get_thread_string_id() << " "; +//#endif + + if(plogger->m_thr_prefix_strings.size()) + { + CRITICAL_REGION_LOCAL(plogger->m_critical_sec); + std::string thr_str = misc_utils::get_thread_string_id(); + std::map::iterator it = plogger->m_thr_prefix_strings.find(thr_str); + if(it!=plogger->m_thr_prefix_strings.end()) + { + str_prefix << it->second; + } + } + + if(get_set_is_uninitialized()) + delete plogger; + + return str_prefix.str(); + } + //---------------------------------------------------------------------------- + bool log_singletone::init() + { + return true;/*do nothing here*/ + } + //---------------------------------------------------------------------------- + bool log_singletone::un_init() + { + //delete object + logger* plogger = get_set_instance_internal(); + if(plogger) delete plogger; + //set uninitialized + get_set_is_uninitialized(true, true); + get_set_instance_internal(true, NULL); + return true; + } + //---------------------------------------------------------------------------- + logger* log_singletone::get_or_create_instance() + { + logger* plogger = get_set_instance_internal(); + if(!plogger) + if(!get_set_is_uninitialized()) + get_set_instance_internal(true, plogger = new logger); + + return plogger; + } + //---------------------------------------------------------------------------- + logger* log_singletone::get_set_instance_internal(bool is_need_set/* = false*/, logger* pnew_logger_val/* = NULL*/) + { + static logger* val_plogger = NULL; + + if(is_need_set) + val_plogger = pnew_logger_val; + + return val_plogger; + } + //---------------------------------------------------------------------------- + bool log_singletone::get_set_is_uninitialized(bool is_need_set/* = false*/, bool is_uninitialized/* = false*/) + { + static bool val_is_uninitialized = false; + + if(is_need_set) + val_is_uninitialized = is_uninitialized; + + return val_is_uninitialized; + } + //---------------------------------------------------------------------------- + log_frame::log_frame(const std::string& name, int dlevel/* = LOG_LEVEL_2*/, const char* plog_name/* = NULL*/) + { +#ifdef _MSC_VER + int lasterr=::GetLastError(); +#endif + m_plog_name = plog_name; + if ( dlevel <= log_singletone::get_log_detalisation_level() ) + { + m_name = name; + std::stringstream ss; + ss << log_space::log_singletone::get_prefix_entry() << "-->>" << m_name << std::endl; + log_singletone::do_log_message(ss.str(), dlevel, console_color_default, false, m_plog_name); + } + m_level = dlevel; +#ifdef _MSC_VER + ::SetLastError(lasterr); +#endif + } + //---------------------------------------------------------------------------- + log_frame::~log_frame() + { +#ifdef _MSC_VER + int lasterr=::GetLastError(); +#endif + + if (m_level <= log_singletone::get_log_detalisation_level() ) + { + std::stringstream ss; + ss << log_space::log_singletone::get_prefix_entry() << "<<--" << m_name << std::endl; + log_singletone::do_log_message(ss.str(), m_level, console_color_default, false,m_plog_name); + } +#ifdef _MSC_VER + ::SetLastError(lasterr); +#endif + } + //---------------------------------------------------------------------------- + std::string get_win32_err_descr(int err_no) + { +#ifdef _MSC_VER + LPVOID lpMsgBuf; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err_no, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*) &lpMsgBuf, + 0, NULL ); + + std::string fix_sys_message = "(null)"; + if(lpMsgBuf) fix_sys_message = (char*)lpMsgBuf; + std::string::size_type a; + if ( (a = fix_sys_message.rfind( '\n' )) != std::string::npos ) + fix_sys_message.erase(a); + if ( (a = fix_sys_message.rfind( '\r' )) != std::string::npos ) + fix_sys_message.erase(a); + + LocalFree(lpMsgBuf); + return fix_sys_message; +#else + return "Not implemented yet"; +#endif + } + //---------------------------------------------------------------------------- + bool getwin32_err_text(std::stringstream& ref_message, int error_no) + { + ref_message << "win32 error:" << get_win32_err_descr(error_no); + return true; + } +} +} diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 44f50afa26..918bfcf2d2 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -24,38 +24,27 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#pragma once #ifndef _MISC_LOG_EX_H_ #define _MISC_LOG_EX_H_ -//#include -#include -#include +#if defined(WIN32) +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#endif + #include -#include -#include -#include -#include #include #include -#include -#include -#include -#include -#include - -#if defined(WIN32) -#include -#else -#include -#endif +#include +#include -#include "static_initializer.h" -#include "string_tools.h" -#include "time_helper.h" #include "misc_os_dependent.h" - +#include "static_initializer.h" #include "syncobj.h" +#include "warnings.h" #define LOG_LEVEL_SILENT -1 @@ -121,50 +110,15 @@ namespace log_space struct ibase_log_stream { - ibase_log_stream(){} - virtual ~ibase_log_stream(){} - virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL)=0; - virtual int get_type(){return 0;} + ibase_log_stream() {} + virtual ~ibase_log_stream() {} + virtual bool out_buffer(const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL)=0; + virtual int get_type() const { return 0; } - virtual bool set_max_logfile_size(uint64_t max_size){return true;}; - virtual bool set_log_rotate_cmd(const std::string& cmd){return true;}; + virtual bool set_max_logfile_size(uint64_t max_size) { return true; } + virtual bool set_log_rotate_cmd(const std::string& cmd) { return true; } }; - /************************************************************************/ - /* */ - /************************************************************************/ - /*struct ibase_log_value - { - public: - virtual void debug_out( std::stringstream* p_stream)const = 0; - };*/ - - /************************************************************************/ - /* */ - /************************************************************************/ - /*class log_message: public std::stringstream - { - public: - log_message(const log_message& lm): std::stringstream(), std::stringstream::basic_ios() - {} - log_message(){} - - template - log_message& operator<< (T t) - { - std::stringstream* pstrstr = this; - (*pstrstr) << t; - - return *this; - } - }; - inline - log_space::log_message& operator<<(log_space::log_message& sstream, const ibase_log_value& log_val) - { - log_val.debug_out(&sstream); - return sstream; - } - */ /************************************************************************/ /* */ /************************************************************************/ @@ -180,176 +134,21 @@ namespace log_space /************************************************************************/ /* */ /************************************************************************/ + + bool is_stdout_a_tty(); + void set_console_color(int color, bool bright); + void reset_console_color(); + bool rotate_log_file(const char* pfile_path); + //------------------------------------------------------------------------ #define max_dbg_str_len 80 #ifdef _MSC_VER class debug_output_stream: public ibase_log_stream { - virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) - { - for ( int i = 0; i < buffer_len; i = i + max_dbg_str_len ) - { - std::string s( buffer + i, buffer_len- i < max_dbg_str_len ? - buffer_len - i : max_dbg_str_len ); - - ::OutputDebugStringA( s.c_str() ); - } - return true; - } - + virtual bool out_buffer(const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) override; }; #endif - inline bool is_stdout_a_tty() - { - static std::atomic initialized(false); - static std::atomic is_a_tty(false); - - if (!initialized.load(std::memory_order_acquire)) - { -#if defined(WIN32) - is_a_tty.store(0 != _isatty(_fileno(stdout)), std::memory_order_relaxed); -#else - is_a_tty.store(0 != isatty(fileno(stdout)), std::memory_order_relaxed); -#endif - initialized.store(true, std::memory_order_release); - } - - return is_a_tty.load(std::memory_order_relaxed); - } - - inline void set_console_color(int color, bool bright) - { - if (!is_stdout_a_tty()) - return; - - switch(color) - { - case console_color_default: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;37m"; - else - std::cout << "\033[0m"; -#endif - } - break; - case console_color_white: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;37m"; - else - std::cout << "\033[0;37m"; -#endif - } - break; - case console_color_red: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;31m"; - else - std::cout << "\033[0;31m"; -#endif - } - break; - case console_color_green: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;32m"; - else - std::cout << "\033[0;32m"; -#endif - } - break; - - case console_color_blue: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;34m"; - else - std::cout << "\033[0;34m"; -#endif - } - break; - - case console_color_cyan: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;36m"; - else - std::cout << "\033[0;36m"; -#endif - } - break; - - case console_color_magenta: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;35m"; - else - std::cout << "\033[0;35m"; -#endif - } - break; - - case console_color_yellow: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;33m"; - else - std::cout << "\033[0;33m"; -#endif - } - break; - - } - } - - inline void reset_console_color() { - if (!is_stdout_a_tty()) - return; - -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); -#else - std::cout << "\033[0m"; - std::cout.flush(); -#endif - } - class console_output_stream: public ibase_log_stream { #ifdef _MSC_VER @@ -357,129 +156,22 @@ namespace log_space #endif public: - console_output_stream() - { -#ifdef _MSC_VER - - if(!::GetStdHandle(STD_OUTPUT_HANDLE)) - m_have_to_kill_console = true; - else - m_have_to_kill_console = false; - - ::AllocConsole(); -#endif - } - - ~console_output_stream() - { -#ifdef _MSC_VER - if(m_have_to_kill_console) - ::FreeConsole(); -#endif - } - int get_type(){return LOGGER_CONSOLE;} - - - - virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) - { - if(plog_name) - return true; //skip alternative logs from console - - set_console_color(color, log_level < 1); - -#ifdef _MSC_VER - const char* ptarget_buf = NULL; - char* pallocated_buf = NULL; - - // - int i = 0; - for(; i < buffer_len; i++) - if(buffer[i] == '\a') break; - if(i == buffer_len) - ptarget_buf = buffer; - else - { - pallocated_buf = new char[buffer_len]; - ptarget_buf = pallocated_buf; - for(i = 0; i < buffer_len; i++) - { - if(buffer[i] == '\a') - pallocated_buf[i] = '^'; - else - pallocated_buf[i] = buffer[i]; - } - } - - //uint32_t b = 0; - //::WriteConsoleA(::GetStdHandle(STD_OUTPUT_HANDLE), ptarget_buf, buffer_len, (DWORD*)&b, 0); - std::cout << ptarget_buf; - if(pallocated_buf) delete [] pallocated_buf; -#else - std::string buf(buffer, buffer_len); - for(size_t i = 0; i!= buf.size(); i++) - { - if(buf[i] == 7 || buf[i] == -107) - buf[i] = '^'; - } - - std::cout << buf; -#endif - reset_console_color(); - return true; - } - + console_output_stream(); + virtual ~console_output_stream(); + virtual int get_type() const override { return LOGGER_CONSOLE; } + virtual bool out_buffer(const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) override; }; - inline bool rotate_log_file(const char* pfile_path) - { -#ifdef _MSC_VER - if(!pfile_path) - return false; - - std::string file_path = pfile_path; - std::string::size_type a = file_path .rfind('.'); - if ( a != std::string::npos ) - file_path .erase( a, file_path .size()); - - ::DeleteFileA( (file_path + ".0").c_str() ); - ::MoveFileA( (file_path + ".log").c_str(), (file_path + ".0").c_str() ); -#else - return false;//not implemented yet -#endif - return true; - } - - - - //--------------------------------------------------------------------------// class file_output_stream : public ibase_log_stream { public: typedef std::map named_log_streams; - file_output_stream( std::string default_log_file_name, std::string log_path ) - { - m_default_log_filename = default_log_file_name; - m_max_logfile_size = 0; - m_default_log_path = log_path; - m_pdefault_file_stream = add_new_stream_and_open(default_log_file_name.c_str()); - } + file_output_stream(std::string default_log_file_name, std::string log_path); + ~file_output_stream(); - ~file_output_stream() - { - for(named_log_streams::iterator it = m_log_file_names.begin(); it!=m_log_file_names.end(); it++) - { - if ( it->second->is_open() ) - { - it->second->flush(); - it->second->close(); - } - delete it->second; - } - } private: named_log_streams m_log_file_names; std::string m_default_log_path; @@ -488,109 +180,14 @@ namespace log_space std::string m_default_log_filename; uint64_t m_max_logfile_size; + virtual int get_type() const override { return LOGGER_FILE; } + virtual bool out_buffer(const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL) override; + virtual bool set_max_logfile_size(uint64_t max_size) override; + virtual bool set_log_rotate_cmd(const std::string& cmd) override; - std::ofstream* add_new_stream_and_open(const char* pstream_name) - { - //log_space::rotate_log_file((m_default_log_path + "\\" + pstream_name).c_str()); - - std::ofstream* pstream = (m_log_file_names[pstream_name] = new std::ofstream); - std::string target_path = m_default_log_path + "/" + pstream_name; - pstream->open( target_path.c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); - if(pstream->fail()) - return NULL; - return pstream; - } - - bool set_max_logfile_size(uint64_t max_size) - { - m_max_logfile_size = max_size; - return true; - } - - bool set_log_rotate_cmd(const std::string& cmd) - { - m_log_rotate_cmd = cmd; - return true; - } - - - - virtual bool out_buffer( const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL ) - { - std::ofstream* m_target_file_stream = m_pdefault_file_stream; - if(plog_name) - { //find named stream - named_log_streams::iterator it = m_log_file_names.find(plog_name); - if(it == m_log_file_names.end()) - m_target_file_stream = add_new_stream_and_open(plog_name); - else - m_target_file_stream = it->second; - } - if(!m_target_file_stream || !m_target_file_stream->is_open()) - return false;//TODO: add assert here - - m_target_file_stream->write(buffer, buffer_len ); - m_target_file_stream->flush(); - - if(m_max_logfile_size) - { - std::ofstream::pos_type pt = m_target_file_stream->tellp(); - uint64_t current_sz = pt; - if(current_sz > m_max_logfile_size) - { - std::cout << "current_sz= " << current_sz << " m_max_logfile_size= " << m_max_logfile_size << std::endl; - std::string log_file_name; - if(!plog_name) - log_file_name = m_default_log_filename; - else - log_file_name = plog_name; - - m_target_file_stream->close(); - std::string new_log_file_name = log_file_name; - - time_t tm = 0; - time(&tm); - - int err_count = 0; - boost::system::error_code ec; - do - { - new_log_file_name = string_tools::cut_off_extension(log_file_name); - if(err_count) - new_log_file_name += misc_utils::get_time_str_v2(tm) + "(" + boost::lexical_cast(err_count) + ")" + ".log"; - else - new_log_file_name += misc_utils::get_time_str_v2(tm) + ".log"; - - err_count++; - }while(boost::filesystem::exists(m_default_log_path + "/" + new_log_file_name, ec)); - - std::string new_log_file_path = m_default_log_path + "/" + new_log_file_name; - boost::filesystem::rename(m_default_log_path + "/" + log_file_name, new_log_file_path, ec); - if(ec) - { - std::cout << "Filed to rename, ec = " << ec.message() << std::endl; - } - - if(m_log_rotate_cmd.size()) - { - - std::string m_log_rotate_cmd_local_copy = m_log_rotate_cmd; - //boost::replace_all(m_log_rotate_cmd, "[*SOURCE*]", new_log_file_path); - boost::replace_all(m_log_rotate_cmd_local_copy, "[*TARGET*]", new_log_file_path); - - misc_utils::call_sys_cmd(m_log_rotate_cmd_local_copy); - } - - m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); - if(m_target_file_stream->fail()) - return false; - } - } - - return true; - } - int get_type(){return LOGGER_FILE;} + std::ofstream* add_new_stream_and_open(const char* pstream_name); }; + /************************************************************************/ /* */ /************************************************************************/ @@ -599,272 +196,59 @@ namespace log_space public: typedef std::list > streams_container; - log_stream_splitter(){} - ~log_stream_splitter() - { - //free pointers - std::for_each(m_log_streams.begin(), m_log_streams.end(), delete_ptr()); - } + log_stream_splitter() { } + ~log_stream_splitter(); - bool set_max_logfile_size(uint64_t max_size) - { - for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) - it->first->set_max_logfile_size(max_size); - return true; - } - - bool set_log_rotate_cmd(const std::string& cmd) - { - for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) - it->first->set_log_rotate_cmd(cmd); - return true; - } - - bool do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name = NULL) - { - std::string str_mess = rlog_mes; - size_t str_len = str_mess.size(); - const char* pstr = str_mess.c_str(); - for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) - if(it->second >= log_level) - it->first->out_buffer(pstr, (int)str_len, log_level, color, plog_name); - return true; - } - - bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4 ) - { - ibase_log_stream* ls = NULL; - - switch( type ) - { - case LOGGER_FILE: - ls = new file_output_stream( pdefault_file_name, pdefault_log_folder ); - break; - - case LOGGER_DEBUGGER: -#ifdef _MSC_VER - ls = new debug_output_stream( ); -#else - return false;//not implemented yet -#endif - break; - case LOGGER_CONSOLE: - ls = new console_output_stream( ); - break; - } - - if ( ls ) { - m_log_streams.push_back(streams_container::value_type(ls, log_level_limit)); - return true; - } - return ls ? true:false; - } - bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 ) - { - m_log_streams.push_back(streams_container::value_type(pstream, log_level_limit) ); - return true; - } - - bool remove_logger(int type) - { - streams_container::iterator it = m_log_streams.begin(); - for(;it!=m_log_streams.end(); it++) - { - if(it->first->get_type() == type) - { - delete it->first; - m_log_streams.erase(it); - return true; - } - } - return false; - - } + bool set_max_logfile_size(uint64_t max_size); + bool set_log_rotate_cmd(const std::string& cmd); + bool do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name = NULL); + bool add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4); + bool add_logger(ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4); + bool remove_logger(int type); - protected: private: - - streams_container m_log_streams; + streams_container m_log_streams; }; /************************************************************************/ /* */ /************************************************************************/ - inline int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); - inline int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); - inline bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); - inline bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); + int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); + int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); + bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); + bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); + std::string get_daytime_string2(); + std::string get_day_time_string(); + std::string get_time_string(); - inline std::string get_daytime_string2() - { - boost::posix_time::ptime p = boost::posix_time::microsec_clock::local_time(); - return misc_utils::get_time_str_v3(p); - } - inline std::string get_day_time_string() - { - return get_daytime_string2(); - //time_t tm = 0; - //time(&tm); - //return misc_utils::get_time_str(tm); - } - - inline std::string get_time_string() - { - return get_daytime_string2(); - - } #ifdef _MSC_VER - inline std::string get_time_string_adv(SYSTEMTIME* pst = NULL) - { - SYSTEMTIME st = {0}; - if(!pst) - { - pst = &st; - GetSystemTime(&st); - } - std::stringstream str_str; - str_str.fill('0'); - str_str << std::setw(2) << pst->wHour << "_" - << std::setw(2) << pst->wMinute << "_" - << std::setw(2) << pst->wSecond << "_" - << std::setw(3) << pst->wMilliseconds; - return str_str.str(); - } + inline std::string get_time_string_adv(SYSTEMTIME* pst = NULL); #endif - - - - - class logger + class logger { public: friend class log_singletone; - logger() - { - CRITICAL_REGION_BEGIN(m_critical_sec); - init(); - CRITICAL_REGION_END(); - } - ~logger() - { - } - - bool set_max_logfile_size(uint64_t max_size) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_log_target.set_max_logfile_size(max_size); - CRITICAL_REGION_END(); - return true; - } - - bool set_log_rotate_cmd(const std::string& cmd) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_log_target.set_log_rotate_cmd(cmd); - CRITICAL_REGION_END(); - return true; - } - - bool take_away_journal(std::list& journal) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_journal.swap(journal); - CRITICAL_REGION_END(); - return true; - } - - bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal = false, const char* plog_name = NULL) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_log_target.do_log_message(rlog_mes, log_level, color, plog_name); - if(add_to_journal) - m_journal.push_back(rlog_mes); - - return true; - CRITICAL_REGION_END(); - } - - bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder , int log_level_limit = LOG_LEVEL_4) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - return m_log_target.add_logger( type, pdefault_file_name, pdefault_log_folder, log_level_limit); - CRITICAL_REGION_END(); - } - bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - return m_log_target.add_logger(pstream, log_level_limit); - CRITICAL_REGION_END(); - } - - bool remove_logger(int type) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - return m_log_target.remove_logger(type); - CRITICAL_REGION_END(); - } + logger(); + ~logger() { } + bool set_max_logfile_size(uint64_t max_size); + bool set_log_rotate_cmd(const std::string& cmd); + bool take_away_journal(std::list& journal); + bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal = false, const char* plog_name = NULL); + bool add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder , int log_level_limit = LOG_LEVEL_4); + bool add_logger(ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4); + bool remove_logger(int type); + bool set_thread_prefix(const std::string& prefix); + std::string get_default_log_file() { return m_default_log_file; } + std::string get_default_log_folder() { return m_default_log_folder; } - bool set_thread_prefix(const std::string& prefix) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_thr_prefix_strings[misc_utils::get_thread_string_id()] = prefix; - CRITICAL_REGION_END(); - return true; - } - - - std::string get_default_log_file() - { - return m_default_log_file; - } - - std::string get_default_log_folder() - { - return m_default_log_folder; - } - - protected: private: - bool init() - { - // - - m_process_name = string_tools::get_current_module_name(); - - init_log_path_by_default(); - - //init default set of loggers - init_default_loggers(); - - std::stringstream ss; - ss << get_time_string() << " Init logging. Level=" << get_set_log_detalisation_level() - << " Log path=" << m_default_log_folder << std::endl; - this->do_log_message(ss.str(), console_color_white, LOG_LEVEL_0); - return true; - } - bool init_default_loggers() - { - //TODO: - return true; - } - - bool init_log_path_by_default() - { - //load process name - m_default_log_folder = string_tools::get_current_module_folder(); - - m_default_log_file = m_process_name; - std::string::size_type a = m_default_log_file.rfind('.'); - if ( a != std::string::npos ) - m_default_log_file.erase( a, m_default_log_file.size()); - m_default_log_file += ".log"; - - return true; - } + bool init(); + bool init_default_loggers(); + bool init_log_path_by_default(); log_stream_splitter m_log_target; @@ -875,6 +259,7 @@ namespace log_space std::list m_journal; critical_section m_critical_sec; }; + /************************************************************************/ /* */ /************************************************************************/ @@ -883,372 +268,63 @@ namespace log_space public: friend class initializer; friend class logger; - static int get_log_detalisation_level() - { - get_or_create_instance();//to initialize logger, if it not initialized - return get_set_log_detalisation_level(); - } - - static bool is_filter_error(int error_code) - { - return false; - } - - - static bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name = NULL) - { - logger* plogger = get_or_create_instance(); - bool res = false; - if(plogger) - res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); - else - { //globally uninitialized, create new logger for each call of do_log_message() and then delete it - plogger = new logger(); - //TODO: some extra initialization - res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); - delete plogger; - plogger = NULL; - } - return res; - } - - static bool take_away_journal(std::list& journal) - { - logger* plogger = get_or_create_instance(); - bool res = false; - if(plogger) - res = plogger->take_away_journal(journal); - - return res; - } - - static bool set_max_logfile_size(uint64_t file_size) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->set_max_logfile_size(file_size); - } - - - static bool set_log_rotate_cmd(const std::string& cmd) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->set_log_rotate_cmd(cmd); - } - - - static bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->add_logger(type, pdefault_file_name, pdefault_log_folder, log_level_limit); - } - static std::string get_default_log_file() - { - logger* plogger = get_or_create_instance(); - if(plogger) - return plogger->get_default_log_file(); - - return ""; - } - - static std::string get_default_log_folder() - { - logger* plogger = get_or_create_instance(); - if(plogger) - return plogger->get_default_log_folder(); + static int get_log_detalisation_level(); + static bool is_filter_error(int error_code); + static bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name = NULL); + static bool take_away_journal(std::list& journal); + static bool set_max_logfile_size(uint64_t file_size); + static bool set_log_rotate_cmd(const std::string& cmd); + static bool add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4); + static std::string get_default_log_file(); + static std::string get_default_log_folder(); + static bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4); + static bool remove_logger(int type); - return ""; - } - - static bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 ) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->add_logger(pstream, log_level_limit); - } - - - static bool remove_logger( int type ) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->remove_logger(type); - } PUSH_WARNINGS DISABLE_GCC_WARNING(maybe-uninitialized) - static int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1) - { - static int log_detalisation_level = LOG_LEVEL_1; - if(is_need_set) - log_detalisation_level = log_level_to_set; - return log_detalisation_level; - } + static int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); POP_WARNINGS - static int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0) - { - static int val_time_log_level = LOG_LEVEL_0; - if(is_need_set) - val_time_log_level = time_log_level; - - return val_time_log_level; - } - - static int get_set_process_level(bool is_need_set = false, int process_log_level = LOG_LEVEL_0) - { - static int val_process_log_level = LOG_LEVEL_0; - if(is_need_set) - val_process_log_level = process_log_level; - - return val_process_log_level; - } - - /*static int get_set_tid_level(bool is_need_set = false, int tid_log_level = LOG_LEVEL_0) - { - static int val_tid_log_level = LOG_LEVEL_0; - if(is_need_set) - val_tid_log_level = tid_log_level; - - return val_tid_log_level; - }*/ - - static bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false) - { - static bool is_need = false; - if(is_need_set) - is_need = is_need_val; - - return is_need; - } - - static bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false) - { - static bool is_need = true; - if(is_need_set) - is_need = is_need_val; - - return is_need; - } - static uint64_t get_set_err_count(bool is_need_set = false, uint64_t err_val = false) - { - static uint64_t err_count = 0; - if(is_need_set) - err_count = err_val; - - return err_count; - } + static int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); + static int get_set_process_level(bool is_need_set = false, int process_log_level = LOG_LEVEL_0); + static bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); + static bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); + static uint64_t get_set_err_count(bool is_need_set = false, uint64_t err_val = false); #ifdef _MSC_VER - - - static void SetThreadName( DWORD dwThreadID, const char* threadName) - { -#define MS_VC_EXCEPTION 0x406D1388 - -#pragma pack(push,8) - typedef struct tagTHREADNAME_INFO - { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - } THREADNAME_INFO; -#pragma pack(pop) - - - - Sleep(10); - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = (char*)threadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } - } + static void SetThreadName( DWORD dwThreadID, const char* threadName); #endif - static bool set_thread_log_prefix(const std::string& prefix) - { -#ifdef _MSC_VER - SetThreadName(-1, prefix.c_str()); -#endif - - - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->set_thread_prefix(prefix); - } - - - static std::string get_prefix_entry() - { - std::stringstream str_prefix; - //write time entry - if ( get_set_time_level() <= get_set_log_detalisation_level() ) - str_prefix << get_day_time_string() << " "; - - //write process info - logger* plogger = get_or_create_instance(); - //bool res = false; - if(!plogger) - { //globally uninitialized, create new logger for each call of get_prefix_entry() and then delete it - plogger = new logger(); - } - - //if ( get_set_need_proc_name() && get_set_process_level() <= get_set_log_detalisation_level() ) - // str_prefix << "[" << plogger->m_process_name << " (id=" << GetCurrentProcessId() << ")] "; -//#ifdef _MSC_VER_EX - if ( get_set_need_thread_id() /*&& get_set_tid_level() <= get_set_log_detalisation_level()*/ ) - str_prefix << "tid:" << misc_utils::get_thread_string_id() << " "; -//#endif - - if(plogger->m_thr_prefix_strings.size()) - { - CRITICAL_REGION_LOCAL(plogger->m_critical_sec); - std::string thr_str = misc_utils::get_thread_string_id(); - std::map::iterator it = plogger->m_thr_prefix_strings.find(thr_str); - if(it!=plogger->m_thr_prefix_strings.end()) - { - str_prefix << it->second; - } - } - - - if(get_set_is_uninitialized()) - delete plogger; - - return str_prefix.str(); - } + static bool set_thread_log_prefix(const std::string& prefix); + static std::string get_prefix_entry(); private: - log_singletone(){}//restric to create an instance + log_singletone() { } //restric to create an instance //static initializer m_log_initializer;//must be in one .cpp file (for example main.cpp) via DEFINE_LOGGING macro - static bool init() - { - return true;/*do nothing here*/ - } - static bool un_init() - { - //delete object - logger* plogger = get_set_instance_internal(); - if(plogger) delete plogger; - //set uninitialized - get_set_is_uninitialized(true, true); - get_set_instance_internal(true, NULL); - return true; - } - - static logger* get_or_create_instance() - { - logger* plogger = get_set_instance_internal(); - if(!plogger) - if(!get_set_is_uninitialized()) - get_set_instance_internal(true, plogger = new logger); - - return plogger; - } + static bool init(); + static bool un_init(); - static logger* get_set_instance_internal(bool is_need_set = false, logger* pnew_logger_val = NULL) - { - static logger* val_plogger = NULL; - - if(is_need_set) - val_plogger = pnew_logger_val; - - return val_plogger; - } - - static bool get_set_is_uninitialized(bool is_need_set = false, bool is_uninitialized = false) - { - static bool val_is_uninitialized = false; - - if(is_need_set) - val_is_uninitialized = is_uninitialized; - - return val_is_uninitialized; - } - //static int get_set_error_filter(bool is_need_set = false) + static logger* get_or_create_instance(); + static logger* get_set_instance_internal(bool is_need_set = false, logger* pnew_logger_val = NULL); + static bool get_set_is_uninitialized(bool is_need_set = false, bool is_uninitialized = false); }; const static initializer log_initializer; - /************************************************************************/ - /* */ -// /************************************************************************/ -// class log_array_value -// { -// int num; -// log_message& m_ref_log_mes; -// -// public: -// -// log_array_value( log_message& log_mes ) : num(0), m_ref_log_mes(log_mes) {} -// -// void operator ( )( ibase_log_value *val ) { -// m_ref_log_mes << "\n[" << num++ << "] "/* << val*/; } -// -// -// template -// void operator ()(T &value ) -// { -// m_ref_log_mes << "\n[" << num++ << "] " << value; -// } -// }; class log_frame { - std::string m_name; - int m_level; - const char* m_plog_name; - public: - - log_frame(const std::string& name, int dlevel = LOG_LEVEL_2 , const char* plog_name = NULL) - { -#ifdef _MSC_VER - int lasterr=::GetLastError(); -#endif - m_plog_name = plog_name; - if ( dlevel <= log_singletone::get_log_detalisation_level() ) - { - m_name = name; - std::stringstream ss; - ss << log_space::log_singletone::get_prefix_entry() << "-->>" << m_name << std::endl; - log_singletone::do_log_message(ss.str(), dlevel, console_color_default, false, m_plog_name); - } - m_level = dlevel; -#ifdef _MSC_VER - ::SetLastError(lasterr); -#endif - } - ~log_frame() - { -#ifdef _MSC_VER - int lasterr=::GetLastError(); -#endif + std::string m_name; + int m_level; + const char* m_plog_name; - if (m_level <= log_singletone::get_log_detalisation_level() ) - { - std::stringstream ss; - ss << log_space::log_singletone::get_prefix_entry() << "<<--" << m_name << std::endl; - log_singletone::do_log_message(ss.str(), m_level, console_color_default, false,m_plog_name); - } -#ifdef _MSC_VER - ::SetLastError(lasterr); -#endif - } + public: + log_frame(const std::string& name, int dlevel = LOG_LEVEL_2 , const char* plog_name = NULL); + ~log_frame(); }; - inline int get_set_time_level(bool is_need_set, int time_log_level) + inline int get_set_time_level(bool is_need_set, int time_log_level) { return log_singletone::get_set_time_level(is_need_set, time_log_level); } @@ -1269,41 +345,10 @@ POP_WARNINGS return log_singletone::get_set_need_proc_name(is_need_set, is_need_val); } - inline std::string get_win32_err_descr(int err_no) - { -#ifdef _MSC_VER - LPVOID lpMsgBuf; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - err_no, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (char*) &lpMsgBuf, - 0, NULL ); - - std::string fix_sys_message = "(null)"; - if(lpMsgBuf) fix_sys_message = (char*)lpMsgBuf; - std::string::size_type a; - if ( (a = fix_sys_message.rfind( '\n' )) != std::string::npos ) - fix_sys_message.erase(a); - if ( (a = fix_sys_message.rfind( '\r' )) != std::string::npos ) - fix_sys_message.erase(a); - - LocalFree(lpMsgBuf); - return fix_sys_message; -#else - return "Not implemented yet"; -#endif - } - - inline bool getwin32_err_text(std::stringstream& ref_message, int error_no) - { - ref_message << "win32 error:" << get_win32_err_descr(error_no); - return true; - } + inline std::string get_win32_err_descr(int err_no); + inline bool getwin32_err_text(std::stringstream& ref_message, int error_no); } + #if defined(_DEBUG) || defined(__GNUC__) #define ENABLE_LOGGING_INTERNAL #endif @@ -1342,6 +387,9 @@ POP_WARNINGS #define LOG_FRAME2(log_name, x, y) epee::log_space::log_frame frame(x, y, log_name) +#define LOG_WARNING2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "WARNING " << __FILE__ << ":" << __LINE__ << " " << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_red, true, log_name);LOCAL_ASSERT(0); epee::log_space::log_singletone::get_set_err_count(true, epee::log_space::log_singletone::get_set_err_count()+1);}} + #else @@ -1362,6 +410,8 @@ POP_WARNINGS #define LOG_FRAME2(log_name, x, y) +#define LOG_WARNING2(log_name, x, level) + #endif @@ -1400,6 +450,7 @@ POP_WARNINGS //#define LOGWIN_PLATFORM_ERROR(err_no) LOGWINDWOS_PLATFORM_ERROR2(LOG_DEFAULT_TARGET, err_no) #define LOG_SOCKET_ERROR(err_no) LOG_SOCKET_ERROR2(LOG_DEFAULT_TARGET, err_no) //#define LOGWIN_PLATFORM_ERROR_UNCRITICAL(mess) LOGWINDWOS_PLATFORM_ERROR_UNCRITICAL2(LOG_DEFAULT_TARGET, mess) +#define LOG_WARNING(mess, level) LOG_WARNING2(LOG_DEFAULT_TARGET, mess, level) #define ENDL std::endl diff --git a/contrib/epee/include/misc_os_dependent.cpp b/contrib/epee/include/misc_os_dependent.cpp new file mode 100644 index 0000000000..3bff65853c --- /dev/null +++ b/contrib/epee/include/misc_os_dependent.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_os_dependent.h" + +#include + +#include + +#ifdef __MACH__ +#include +#include +#endif + +namespace epee +{ +namespace misc_utils +{ + + uint64_t get_tick_count() + { +#if defined(_MSC_VER) + return ::GetTickCount64(); +#elif defined(__MACH__) + clock_serv_t cclock; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + + return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000); +#else + struct timespec ts; + if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000); +#endif + } + + int call_sys_cmd(const std::string& cmd) + { + std::cout << "# " << cmd << std::endl; + + FILE * fp ; + //char tstCommand[] ="ls *"; + char path[1000] = {0}; +#if !defined(__GNUC__) + fp = _popen(cmd.c_str(), "r"); +#else + fp = popen(cmd.c_str(), "r"); +#endif + while ( fgets( path, 1000, fp ) != NULL ) + std::cout << path; + +#if !defined(__GNUC__) + _pclose(fp); +#else + pclose(fp); +#endif + return 0; + } + + std::string get_thread_string_id() + { +#if defined(_MSC_VER) + return boost::lexical_cast(GetCurrentThreadId()); +#elif defined(__GNUC__) + return boost::lexical_cast(pthread_self()); +#endif + } +} +} diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index 4d9c991e4a..eb4a3a8a75 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -23,86 +23,30 @@ // (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 + #ifdef WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif - //#ifdef _WIN32_WINNT - // #undef _WIN32_WINNT - // #define _WIN32_WINNT 0x0600 - //#endif - - -#include -#endif - -#ifdef __MACH__ -#include -#include + #if !defined(NOMINMAX) + #define NOMINMAX 1 + #endif // !defined(NOMINMAX) + + #include #endif -#pragma once namespace epee { namespace misc_utils { - - inline uint64_t get_tick_count() - { -#if defined(_MSC_VER) - return ::GetTickCount64(); -#elif defined(__MACH__) - clock_serv_t cclock; - mach_timespec_t mts; - - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - - return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000); -#else - struct timespec ts; - if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000); -#endif - } - - - inline int call_sys_cmd(const std::string& cmd) - { - std::cout << "# " << cmd << std::endl; - - FILE * fp ; - //char tstCommand[] ="ls *"; - char path[1000] = {0}; -#if !defined(__GNUC__) - fp = _popen(cmd.c_str(), "r"); -#else - fp = popen(cmd.c_str(), "r"); -#endif - while ( fgets( path, 1000, fp ) != NULL ) - std::cout << path; - -#if !defined(__GNUC__) - _pclose(fp); -#else - pclose(fp); -#endif - return 0; - - } - - - inline std::string get_thread_string_id() - { -#if defined(_MSC_VER) - return boost::lexical_cast(GetCurrentThreadId()); -#elif defined(__GNUC__) - return boost::lexical_cast(pthread_self()); -#endif - } + uint64_t get_tick_count(); + int call_sys_cmd(const std::string& cmd); + std::string get_thread_string_id(); } } diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 74f1e5c4a4..0265d57ee9 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -30,10 +30,12 @@ #include #include #include +#include #include #include #include #include +#include "include_base_utils.h" #include "misc_language.h" #include "pragma_comp_defs.h" @@ -304,7 +306,7 @@ PRAGMA_WARNING_DISABLE_VS(4355) if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) { send_guard.unlock(); -// LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); + LOG_WARNING("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection", LOG_LEVEL_2); close(); return false; } diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index aed909778a..72b4b57569 100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -31,6 +31,7 @@ #define _HTTP_SERVER_H_ #include +#include "include_base_utils.h" #include "net_utils_base.h" #include "to_nonconst_iterator.h" #include "http_base.h" diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index 78b46427e6..fc091a2126 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -28,8 +28,10 @@ #include #include #include "http_protocol_handler.h" +#include "include_base_utils.h" #include "reg_exp_definer.h" #include "string_tools.h" +#include "time_helper.h" #include "file_io_utils.h" #include "net_parse_helpers.h" diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 2014601302..12ad9d9731 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -28,6 +28,7 @@ #pragma once #include "http_base.h" #include "jsonrpc_structs.h" +#include "misc_os_dependent.h" #include "storages/portable_storage.h" #include "storages/portable_storage_template_helper.h" @@ -59,7 +60,7 @@ else if(query_info.m_URI == s_pattern) \ { \ handled = true; \ - uint64_t ticks = misc_utils::get_tick_count(); \ + uint64_t ticks = epee::misc_utils::get_tick_count(); \ boost::value_initialized req; \ bool parse_res = epee::serialization::load_t_from_json(static_cast(req), query_info.m_body); \ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \ @@ -84,11 +85,11 @@ else if(query_info.m_URI == s_pattern) \ { \ handled = true; \ - uint64_t ticks = misc_utils::get_tick_count(); \ + uint64_t ticks = epee::misc_utils::get_tick_count(); \ boost::value_initialized req; \ bool parse_res = epee::serialization::load_t_from_binary(static_cast(req), query_info.m_body); \ 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(); \ + uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ boost::value_initialized resp;\ if(!callback_f(static_cast(req), static_cast(resp), m_conn_context)) \ { \ @@ -97,7 +98,7 @@ response_info.m_response_comment = "Internal Server Error"; \ return true; \ } \ - uint64_t ticks2 = misc_utils::get_tick_count(); \ + uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ epee::serialization::store_t_to_binary(static_cast(resp), response_info.m_body); \ uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ response_info.m_mime_tipe = " application/octet-stream"; \ diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h index a286c4723c..406d92b285 100644 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -25,6 +25,7 @@ // #pragma once +#include #include #include #include diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h index 586dac98dc..40c3d935d2 100644 --- a/contrib/epee/include/net/net_parse_helpers.h +++ b/contrib/epee/include/net/net_parse_helpers.h @@ -29,6 +29,7 @@ #pragma once #include "http_base.h" +#include "include_base_utils.h" #include "reg_exp_definer.h" diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index b5619bab3a..b0e44f5dd0 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -36,6 +36,11 @@ #define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) #endif +namespace boost { + namespace asio { + class io_service; + } +} namespace epee { diff --git a/contrib/epee/include/profile_tools.h b/contrib/epee/include/profile_tools.h index 0e1646f60b..49180c6a3c 100644 --- a/contrib/epee/include/profile_tools.h +++ b/contrib/epee/include/profile_tools.h @@ -28,6 +28,8 @@ #ifndef _PROFILE_TOOLS_H_ #define _PROFILE_TOOLS_H_ +#include + namespace epee { diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index 33486d9ecf..d52d09c050 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -26,6 +26,11 @@ #pragma once +#include + +#include +#include + namespace epee { namespace serialization diff --git a/contrib/epee/include/static_initializer.h b/contrib/epee/include/static_initializer.h index 3463a56078..1510805c25 100644 --- a/contrib/epee/include/static_initializer.h +++ b/contrib/epee/include/static_initializer.h @@ -44,38 +44,11 @@ class initializer initializer() { to_initialize::init(); - //get_set_is_initialized(true, true); } ~initializer() { to_initialize::un_init(); - //get_set_is_uninitialized(true, true); } - - /*static inline bool is_initialized() - { - return get_set_is_initialized(); - } - static inline bool is_uninitialized() - { - return get_set_is_uninitialized(); - } - -private: - static inline bool get_set_is_initialized(bool need_to_set = false, bool val_to_set= false) - { - static bool val_is_initialized = false; - if(need_to_set) - val_is_initialized = val_to_set; - return val_is_initialized; - } - static inline bool get_set_is_uninitialized(bool need_to_set = false, bool val_to_set = false) - { - static bool val_is_uninitialized = false; - if(need_to_set) - val_is_uninitialized = val_to_set; - return val_is_uninitialized; - }*/ }; } diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index bbfe5f85cb..87c6edb2b9 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -150,7 +150,8 @@ namespace epee m_root.m_entries.clear(); if(source.size() < sizeof(storage_block_header)) { - LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header)); + LOG_WARNING("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" + << sizeof(storage_block_header), LOG_LEVEL_2) return false; } storage_block_header* pbuff = (storage_block_header*)source.data(); @@ -158,12 +159,12 @@ namespace epee pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB ) { - LOG_ERROR("portable_storage: wrong binary format - signature missmatch"); + LOG_WARNING("portable_storage: wrong binary format - signature missmatch", LOG_LEVEL_2); return false; } if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER) { - LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver); + LOG_WARNING("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver, LOG_LEVEL_2); return false; } TRY_ENTRY(); diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h index 4e74fb7a5f..d3d84cc64f 100644 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -25,6 +25,9 @@ // #pragma once + +#include + #include "parserse_base_utils.h" #include "file_io_utils.h" @@ -365,6 +368,7 @@ namespace epee } catch(const std::exception& ex) { + (void)(ex); LOG_PRINT_RED_L0("Failed to parse json, what: " << ex.what()); return false; } diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h index baf90290ac..6743a60879 100644 --- a/contrib/epee/include/storages/portable_storage_to_bin.h +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -30,6 +30,7 @@ #include "misc_language.h" #include "portable_storage_base.h" +#include "pragma_comp_defs.h" namespace epee { diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h index 6ea505886a..73d339f5d0 100644 --- a/contrib/epee/include/storages/portable_storage_val_converters.h +++ b/contrib/epee/include/storages/portable_storage_val_converters.h @@ -24,10 +24,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - - #pragma once +#include + #include "misc_language.h" #include "portable_storage_base.h" #include "warnings.h" diff --git a/contrib/epee/include/string_tools.cpp b/contrib/epee/include/string_tools.cpp new file mode 100644 index 0000000000..8181bc1021 --- /dev/null +++ b/contrib/epee/include/string_tools.cpp @@ -0,0 +1,487 @@ +// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "string_tools.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#ifdef WINDOWS_PLATFORM +#pragma comment (lib, "Rpcrt4.lib") +#endif + +namespace epee +{ +namespace string_tools +{ + std::wstring get_str_from_guid(const boost::uuids::uuid& rid) + { + return boost::lexical_cast(rid); + } + //---------------------------------------------------------------------------- + std::string get_str_from_guid_a(const boost::uuids::uuid& rid) + { + return boost::lexical_cast(rid); + } + //---------------------------------------------------------------------------- + bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id) + { + if(str_id.size() < 36) + return false; + + if('{' == *str_id.begin()) + str_id.erase(0, 1); + + if('}' == *(--str_id.end())) + str_id.erase(--str_id.end()); + + try + { + inetifer = boost::lexical_cast(str_id); + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id) + { + std::string local_str_id = str_id; + if(local_str_id.size() < 36) + return false; + + if('{' == *local_str_id.begin()) + local_str_id.erase(0, 1); + + if('}' == *(--local_str_id.end())) + local_str_id.erase(--local_str_id.end()); + + try + { + inetifer = boost::lexical_cast(local_str_id); + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- +//#ifdef _WINSOCK2API_ + std::string get_ip_string_from_int32(uint32_t ip) + { + in_addr adr; + adr.s_addr = ip; + const char* pbuf = inet_ntoa(adr); + if(pbuf) + return pbuf; + else + return "[failed]"; + } + //---------------------------------------------------------------------------- + bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str) + { + ip = inet_addr(ip_str.c_str()); + if(INADDR_NONE == ip) + return false; + + return true; + } + //---------------------------------------------------------------------------- + bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) + { + //parse ip and address + std::string::size_type p = addres.find(':'); + if(p == std::string::npos) + { + return false; + } + std::string ip_str = addres.substr(0, p); + std::string port_str = addres.substr(p+1, addres.size()); + + if(!get_ip_int32_from_string(ip, ip_str)) + { + return false; + } + + if(!get_xtype_from_string(port, port_str)) + { + return false; + } + return true; + } + + //---------------------------------------------------------------------------- + std::string num_to_string_fast(int64_t val) + { + /* + char buff[30] = {0}; + i64toa_s(val, buff, sizeof(buff)-1, 10); + return buff;*/ + return boost::lexical_cast(val); + } + //---------------------------------------------------------------------------- + bool string_to_num_fast(const std::string& buff, int64_t& val) + { + //return get_xtype_from_string(val, buff); +#if (defined _MSC_VER) + val = _atoi64(buff.c_str()); +#else + val = atoll(buff.c_str()); +#endif + /* + * val = atoi64(buff.c_str()); + */ + if(buff != "0" && val == 0) + return false; + return true; + } + //---------------------------------------------------------------------------- + bool string_to_num_fast(const std::string& buff, int& val) + { + val = atoi(buff.c_str()); + if(buff != "0" && val == 0) + return false; + + return true; + } + //---------------------------------------------------------------------------- +#ifdef WINDOWS_PLATFORM + std::string system_time_to_string(const SYSTEMTIME& st) + { + + /* + TIME_ZONE_INFORMATION tzi; + GetTimeZoneInformation(&tzi); + SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal); + */ + + char szTime[25], szDate[25]; + ::GetTimeFormatA( + LOCALE_USER_DEFAULT, // locale + TIME_FORCE24HOURFORMAT, // options + &st, // time + NULL, // time format string + szTime, // formatted string buffer + 25 // size of string buffer + ); + + ::GetDateFormatA( + LOCALE_USER_DEFAULT, // locale + NULL, // options + &st, // date + NULL, // date format + szDate, // formatted string buffer + 25 // size of buffer + ); + szTime[24] = szDate[24] = 0; //be happy :) + + std::string res = szDate; + (res += " " )+= szTime; + return res; + + } +#endif + //---------------------------------------------------------------------------- + + bool compare_no_case(const std::string& str1, const std::string& str2) + { + + return !boost::iequals(str1, str2); + } + //---------------------------------------------------------------------------- + bool compare_no_case(const std::wstring& str1, const std::wstring& str2) + { + return !boost::iequals(str1, str2); + } + //---------------------------------------------------------------------------- + bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix) + { + if(prefix.size()>str1.size()) + return false; + + if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) + return true; + else + return false; + } + //---------------------------------------------------------------------------- + bool is_match_prefix(const std::string& str1, const std::string& prefix) + { + if(prefix.size()>str1.size()) + return false; + + if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) + return true; + else + return false; + } + //---------------------------------------------------------------------------- + std::string& get_current_module_name() + { + static std::string module_name; + return module_name; + } + //---------------------------------------------------------------------------- + std::string& get_current_module_folder() + { + static std::string module_folder; + return module_folder; + } + //---------------------------------------------------------------------------- +#ifdef _WIN32 + std::string get_current_module_path() + { + char pname [5000] = {0}; + GetModuleFileNameA( NULL, pname, sizeof(pname)); + pname[sizeof(pname)-1] = 0; //be happy ;) + return pname; + } +#endif + //---------------------------------------------------------------------------- + bool set_module_name_and_folder(const std::string& path_to_process_) + { + std::string path_to_process = path_to_process_; +#ifdef _WIN32 + path_to_process = get_current_module_path(); +#endif + std::string::size_type a = path_to_process.rfind( '\\' ); + if(a == std::string::npos ) + { + a = path_to_process.rfind( '/' ); + } + if ( a != std::string::npos ) + { + get_current_module_name() = path_to_process.substr(a+1, path_to_process.size()); + get_current_module_folder() = path_to_process.substr(0, a); + return true; + }else + return false; + + } + + //---------------------------------------------------------------------------- + bool trim_left(std::string& str) + { + for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast(*it));) + str.erase(str.begin()); + + return true; + } + //---------------------------------------------------------------------------- + bool trim_right(std::string& str) + { + + for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast(*it));) + str.erase( --((it++).base())); + + return true; + } + //---------------------------------------------------------------------------- + std::string& trim(std::string& str) + { + + trim_left(str); + trim_right(str); + return str; + } + //---------------------------------------------------------------------------- + std::string trim(const std::string& str_) + { + std::string str = str_; + trim_left(str); + trim_right(str); + return str; + } + //---------------------------------------------------------------------------- + std::string get_extension(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('.'); + if(std::string::npos == pos) + return res; + + res = str.substr(pos+1, str.size()-pos); + return res; + } + //---------------------------------------------------------------------------- + std::string get_filename_from_path(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('\\'); + if(std::string::npos == pos) + return str; + + res = str.substr(pos+1, str.size()-pos); + return res; + } + //---------------------------------------------------------------------------- + + + + std::string cut_off_extension(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('.'); + if(std::string::npos == pos) + return str; + + res = str.substr(0, pos); + return res; + } + + //---------------------------------------------------------------------------- +#ifdef _WININET_ + std::string get_string_from_systemtime(const SYSTEMTIME& sys_time) + { + std::string result_string; + + char buff[100] = {0}; + BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99); + if(!res) + { + LOG_ERROR("Failed to load SytemTime to string"); + } + + result_string = buff; + return result_string; + + } + //------------------------------------------------------------------------------------- + SYSTEMTIME get_systemtime_from_string(const std::string& buff) + { + SYSTEMTIME result_time = {0}; + + BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL); + if(!res) + { + LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes"); + } + + return result_time; + } +#endif + +#ifdef WINDOWS_PLATFORM + const wchar_t* get_pc_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = INFO_BUFFER_SIZE; + static bool init = false; + + if (!init) { + if (!GetComputerNameW( info, &bufCharCount )) + info[0] = 0; + else + init = true; + } + + return info; + } + + const wchar_t* get_user_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = INFO_BUFFER_SIZE; + static bool init = false; + + if (!init) { + if (!GetUserNameW( info, &bufCharCount )) + info[0] = 0; + else + init = true; + } + + return info; + } +#endif + +#ifdef _LM_ + const wchar_t* get_domain_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = 0; + static bool init = false; + + if (!init) { + LPWSTR domain( NULL ); + NETSETUP_JOIN_STATUS status; + info[0] = 0; + + if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status )) + { + LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result)); + } else + { + StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain ); + NetApiBufferFree((void*)domain); + init = true; + } + } + + return info; + } +#endif +#ifdef WINDOWS_PLATFORM + inline + std::string load_resource_string_a(int id, const char* pmodule_name = NULL) + { + //slow realization + HMODULE h = ::GetModuleHandleA( pmodule_name ); + + char buff[2000] = {0}; + + ::LoadStringA( h, id, buff, sizeof(buff)); + buff[sizeof(buff)-1] = 0; //be happy :) + return buff; + } + inline + std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL) + { + //slow realization + HMODULE h = ::GetModuleHandleA( pmodule_name ); + + wchar_t buff[2000] = {0}; + + ::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) ); + buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :) + return buff; + } +#endif +} +} diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 8289ee0bac..281fbd9404 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -24,24 +24,21 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // - +#pragma once #ifndef _STRING_TOOLS_H_ #define _STRING_TOOLS_H_ -//#include -#include -#include +#include +#include #include -//#include -#include -#include -#include -#include -#include -#include -#include "warnings.h" +#include +#include +#include +#include +#include +#include "warnings.h" #ifndef OUT #define OUT @@ -51,71 +48,32 @@ #pragma comment (lib, "Rpcrt4.lib") #endif +// Don't include lexical_cast.hpp, to reduce compilation time +//#include + +namespace boost { + namespace uuids { + struct uuid; + } + + template + inline Target lexical_cast(const Source &arg); +} + namespace epee { namespace string_tools { - inline std::wstring get_str_from_guid(const boost::uuids::uuid& rid) - { - return boost::lexical_cast(rid); - } - //---------------------------------------------------------------------------- - inline std::string get_str_from_guid_a(const boost::uuids::uuid& rid) - { - return boost::lexical_cast(rid); - } - //---------------------------------------------------------------------------- - inline bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id) - { - if(str_id.size() < 36) - return false; - - if('{' == *str_id.begin()) - str_id.erase(0, 1); - - if('}' == *(--str_id.end())) - str_id.erase(--str_id.end()); - - try - { - inetifer = boost::lexical_cast(str_id); - return true; - } - catch(...) - { - return false; - } - } - //---------------------------------------------------------------------------- - inline bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id) - { - std::string local_str_id = str_id; - if(local_str_id.size() < 36) - return false; - - if('{' == *local_str_id.begin()) - local_str_id.erase(0, 1); - - if('}' == *(--local_str_id.end())) - local_str_id.erase(--local_str_id.end()); - - try - { - inetifer = boost::lexical_cast(local_str_id); - return true; - } - catch(...) - { - return false; - } - } - //---------------------------------------------------------------------------- + std::wstring get_str_from_guid(const boost::uuids::uuid& rid); + std::string get_str_from_guid_a(const boost::uuids::uuid& rid); + bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id); + bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id); + //---------------------------------------------------------------------------- template std::basic_string buff_to_hex(const std::basic_string& s) { - using namespace std; - basic_stringstream hexStream; - hexStream << hex << noshowbase << setw(2); + std::basic_stringstream hexStream; + hexStream << std::hex << std::noshowbase << std::setw(2); for(typename std::basic_string::const_iterator it = s.begin(); it != s.end(); it++) { @@ -127,13 +85,12 @@ namespace string_tools template std::basic_string buff_to_hex_nodelimer(const std::basic_string& s) { - using namespace std; - basic_stringstream hexStream; - hexStream << hex << noshowbase; + std::basic_stringstream hexStream; + hexStream << std::hex << std::noshowbase; for(typename std::basic_string::const_iterator it = s.begin(); it != s.end(); it++) { - hexStream << setw(2) << setfill('0') << static_cast(static_cast(*it)); + hexStream << std::setw(2) << std::setfill('0') << static_cast(static_cast(*it)); } return hexStream.str(); } @@ -273,13 +230,6 @@ POP_WARNINGS return true; } -/* template - bool get_xparam_from_command_line(const std::map& res, const std::basic_string & key, t_type& val) - { - - } - */ - template bool get_xparam_from_command_line(const std::map& res, const t_string & key, t_type& val) { @@ -295,22 +245,22 @@ POP_WARNINGS return true; } - template - t_type get_xparam_from_command_line(const std::map& res, const t_string & key, const t_type& default_value) - { - typename std::map::const_iterator it = res.find(key); - if(it == res.end()) - return default_value; + template + t_type get_xparam_from_command_line(const std::map& res, const t_string & key, const t_type& default_value) + { + typename std::map::const_iterator it = res.find(key); + if(it == res.end()) + return default_value; - if(it->second.size()) - { - t_type s; - get_xtype_from_string(s, it->second); - return s; - } + if(it->second.size()) + { + t_type s; + get_xtype_from_string(s, it->second); + return s; + } - return default_value; - } + return default_value; + } template bool have_in_command_line(const std::map& res, const std::basic_string& key) @@ -322,243 +272,40 @@ POP_WARNINGS return true; } - //---------------------------------------------------------------------------- -//#ifdef _WINSOCK2API_ - inline std::string get_ip_string_from_int32(uint32_t ip) - { - in_addr adr; - adr.s_addr = ip; - const char* pbuf = inet_ntoa(adr); - if(pbuf) - return pbuf; - else - return "[failed]"; - } - //---------------------------------------------------------------------------- - inline bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str) - { - ip = inet_addr(ip_str.c_str()); - if(INADDR_NONE == ip) - return false; - - return true; - } //---------------------------------------------------------------------------- - inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) - { - //parse ip and address - std::string::size_type p = addres.find(':'); - if(p == std::string::npos) - { - return false; - } - std::string ip_str = addres.substr(0, p); - std::string port_str = addres.substr(p+1, addres.size()); - - if(!get_ip_int32_from_string(ip, ip_str)) - { - return false; - } - - if(!get_xtype_from_string(port, port_str)) - { - return false; - } - return true; - } - -//#endif - //---------------------------------------------------------------------------- + std::string get_ip_string_from_int32(uint32_t ip); + bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str); + bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres); + //---------------------------------------------------------------------------- template - inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8) + inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8) { std::stringstream ss; ss << std::setfill ('0') << std::setw (w) << std::hex << std::noshowbase; ss << v; return ss.str(); } - - inline std::string num_to_string_fast(int64_t val) - { - /* - char buff[30] = {0}; - i64toa_s(val, buff, sizeof(buff)-1, 10); - return buff;*/ - return boost::lexical_cast(val); - } - //---------------------------------------------------------------------------- - inline bool string_to_num_fast(const std::string& buff, int64_t& val) - { - //return get_xtype_from_string(val, buff); -#if (defined _MSC_VER) - val = _atoi64(buff.c_str()); -#else - val = atoll(buff.c_str()); -#endif - /* - * val = atoi64(buff.c_str()); - */ - if(buff != "0" && val == 0) - return false; - return true; - } - //---------------------------------------------------------------------------- - inline bool string_to_num_fast(const std::string& buff, int& val) - { - val = atoi(buff.c_str()); - if(buff != "0" && val == 0) - return false; - - return true; - } - //---------------------------------------------------------------------------- + //---------------------------------------------------------------------------- + std::string num_to_string_fast(int64_t val); + bool string_to_num_fast(const std::string& buff, int64_t& val); + bool string_to_num_fast(const std::string& buff, int& val); #ifdef WINDOWS_PLATFORM - inline std::string system_time_to_string(const SYSTEMTIME& st) - { - - /* - TIME_ZONE_INFORMATION tzi; - GetTimeZoneInformation(&tzi); - SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal); - */ - - char szTime[25], szDate[25]; - ::GetTimeFormatA( - LOCALE_USER_DEFAULT, // locale - TIME_FORCE24HOURFORMAT, // options - &st, // time - NULL, // time format string - szTime, // formatted string buffer - 25 // size of string buffer - ); - - ::GetDateFormatA( - LOCALE_USER_DEFAULT, // locale - NULL, // options - &st, // date - NULL, // date format - szDate, // formatted string buffer - 25 // size of buffer - ); - szTime[24] = szDate[24] = 0; //be happy :) - - std::string res = szDate; - (res += " " )+= szTime; - return res; - - } + std::string system_time_to_string(const SYSTEMTIME& st); #endif - //---------------------------------------------------------------------------- - - inline bool compare_no_case(const std::string& str1, const std::string& str2) - { - - return !boost::iequals(str1, str2); - } - //---------------------------------------------------------------------------- - inline bool compare_no_case(const std::wstring& str1, const std::wstring& str2) - { - return !boost::iequals(str1, str2); - } - //---------------------------------------------------------------------------- - inline bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix) - { - if(prefix.size()>str1.size()) - return false; - - if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) - return true; - else - return false; - } - //---------------------------------------------------------------------------- - inline bool is_match_prefix(const std::string& str1, const std::string& prefix) - { - if(prefix.size()>str1.size()) - return false; - - if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) - return true; - else - return false; - } - //---------------------------------------------------------------------------- - inline std::string& get_current_module_name() - { - static std::string module_name; - return module_name; - } - //---------------------------------------------------------------------------- - inline std::string& get_current_module_folder() - { - static std::string module_folder; - return module_folder; - } - //---------------------------------------------------------------------------- + bool compare_no_case(const std::string& str1, const std::string& str2); + bool compare_no_case(const std::wstring& str1, const std::wstring& str2); + bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix); + bool is_match_prefix(const std::string& str1, const std::string& prefix); + std::string& get_current_module_name(); + std::string& get_current_module_folder(); #ifdef _WIN32 - inline std::string get_current_module_path() - { - char pname [5000] = {0}; - GetModuleFileNameA( NULL, pname, sizeof(pname)); - pname[sizeof(pname)-1] = 0; //be happy ;) - return pname; - } + std::string get_current_module_path(); #endif - //---------------------------------------------------------------------------- - inline bool set_module_name_and_folder(const std::string& path_to_process_) - { - std::string path_to_process = path_to_process_; -#ifdef _WIN32 - path_to_process = get_current_module_path(); -#endif - std::string::size_type a = path_to_process.rfind( '\\' ); - if(a == std::string::npos ) - { - a = path_to_process.rfind( '/' ); - } - if ( a != std::string::npos ) - { - get_current_module_name() = path_to_process.substr(a+1, path_to_process.size()); - get_current_module_folder() = path_to_process.substr(0, a); - return true; - }else - return false; - - } - - //---------------------------------------------------------------------------- - inline bool trim_left(std::string& str) - { - for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast(*it));) - str.erase(str.begin()); - - return true; - } - //---------------------------------------------------------------------------- - inline bool trim_right(std::string& str) - { - - for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast(*it));) - str.erase( --((it++).base())); - - return true; - } - //---------------------------------------------------------------------------- - inline std::string& trim(std::string& str) - { - - trim_left(str); - trim_right(str); - return str; - } - //---------------------------------------------------------------------------- - inline std::string trim(const std::string& str_) - { - std::string str = str_; - trim_left(str); - trim_right(str); - return str; - } + bool set_module_name_and_folder(const std::string& path_to_process_); + bool trim_left(std::string& str); + bool trim_right(std::string& str); + std::string& trim(std::string& str); + std::string trim(const std::string& str_); //---------------------------------------------------------------------------- template std::string pod_to_hex(const t_pod_type& s) @@ -584,161 +331,27 @@ POP_WARNINGS return true; } //---------------------------------------------------------------------------- - inline std::string get_extension(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('.'); - if(std::string::npos == pos) - return res; - - res = str.substr(pos+1, str.size()-pos); - return res; - } - //---------------------------------------------------------------------------- - inline std::string get_filename_from_path(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('\\'); - if(std::string::npos == pos) - return str; - - res = str.substr(pos+1, str.size()-pos); - return res; - } - //---------------------------------------------------------------------------- - - - - inline std::string cut_off_extension(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('.'); - if(std::string::npos == pos) - return str; - - res = str.substr(0, pos); - return res; - } - - //---------------------------------------------------------------------------- + std::string get_extension(const std::string& str); + std::string get_filename_from_path(const std::string& str); + std::string cut_off_extension(const std::string& str); #ifdef _WININET_ - inline std::string get_string_from_systemtime(const SYSTEMTIME& sys_time) - { - std::string result_string; - - char buff[100] = {0}; - BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99); - if(!res) - { - LOG_ERROR("Failed to load SytemTime to string"); - } - - result_string = buff; - return result_string; - - } - //------------------------------------------------------------------------------------- - inline SYSTEMTIME get_systemtime_from_string(const std::string& buff) - { - SYSTEMTIME result_time = {0}; - - BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL); - if(!res) - { - LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes"); - } - - return result_time; - } + std::string get_string_from_systemtime(const SYSTEMTIME& sys_time); + SYSTEMTIME get_systemtime_from_string(const std::string& buff); #endif #ifdef WINDOWS_PLATFORM - static const DWORD INFO_BUFFER_SIZE = 10000; + const DWORD INFO_BUFFER_SIZE = 10000; - static const wchar_t* get_pc_name() - { - static wchar_t info[INFO_BUFFER_SIZE]; - static DWORD bufCharCount = INFO_BUFFER_SIZE; - static bool init = false; - - if (!init) { - if (!GetComputerNameW( info, &bufCharCount )) - info[0] = 0; - else - init = true; - } - - return info; - } - - static const wchar_t* get_user_name() - { - static wchar_t info[INFO_BUFFER_SIZE]; - static DWORD bufCharCount = INFO_BUFFER_SIZE; - static bool init = false; - - if (!init) { - if (!GetUserNameW( info, &bufCharCount )) - info[0] = 0; - else - init = true; - } - - return info; - } + const wchar_t* get_pc_name(); + const wchar_t* get_user_name(); #endif #ifdef _LM_ - static const wchar_t* get_domain_name() - { - static wchar_t info[INFO_BUFFER_SIZE]; - static DWORD bufCharCount = 0; - static bool init = false; - - if (!init) { - LPWSTR domain( NULL ); - NETSETUP_JOIN_STATUS status; - info[0] = 0; - - if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status )) - { - LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result)); - } else - { - StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain ); - NetApiBufferFree((void*)domain); - init = true; - } - } - - return info; - } + const wchar_t* get_domain_name(); #endif #ifdef WINDOWS_PLATFORM - inline - std::string load_resource_string_a(int id, const char* pmodule_name = NULL) - { - //slow realization - HMODULE h = ::GetModuleHandleA( pmodule_name ); - - char buff[2000] = {0}; - - ::LoadStringA( h, id, buff, sizeof(buff)); - buff[sizeof(buff)-1] = 0; //be happy :) - return buff; - } - inline - std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL) - { - //slow realization - HMODULE h = ::GetModuleHandleA( pmodule_name ); - - wchar_t buff[2000] = {0}; - - ::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) ); - buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :) - return buff; - } + std::string load_resource_string_a(int id, const char* pmodule_name = NULL); + std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL); #endif } } diff --git a/contrib/epee/include/syncobj.h b/contrib/epee/include/syncobj.h index b7273da8e3..52cb70e609 100644 --- a/contrib/epee/include/syncobj.h +++ b/contrib/epee/include/syncobj.h @@ -32,13 +32,9 @@ #include #include -#include -#include -#include namespace epee { - struct simple_event { simple_event() : m_rised(false) @@ -70,7 +66,7 @@ namespace epee class critical_section { - boost::recursive_mutex m_section; + std::recursive_mutex m_section; public: //to make copy fake! @@ -89,7 +85,6 @@ namespace epee void lock() { m_section.lock(); - //EnterCriticalSection( &m_section ); } void unlock() diff --git a/external/google/dense_hash_map b/external/google/dense_hash_map new file mode 100644 index 0000000000..74c796bab6 --- /dev/null +++ b/external/google/dense_hash_map @@ -0,0 +1,333 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ---- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over densehashtable.h, just +// like sgi stl's stl_hash_map is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// NOTE: this is exactly like sparse_hash_map.h, with the word +// "sparse" replaced by "dense", except for the addition of +// set_empty_key(). +// +// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. +// +// Otherwise your program will die in mysterious ways. (Note if you +// use the constructor that takes an InputIterator range, you pass in +// the empty key in the constructor, rather than after. As a result, +// this constructor differs from the standard STL version.) +// +// In other respects, we adhere mostly to the STL semantics for +// hash-map. One important exception is that insert() may invalidate +// iterators entirely -- STL semantics are that insert() may reorder +// iterators, but they all still refer to something valid in the +// hashtable. Not so for us. Likewise, insert() may invalidate +// pointers into the hashtable. (Whether insert invalidates iterators +// and pointers depends on whether it results in a hashtable resize). +// On the plus side, delete() doesn't invalidate iterators or pointers +// at all, or even change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// If you want to use erase() you *must* call set_deleted_key(), +// in addition to set_empty_key(), after construction. +// The deleted and empty keys must differ. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This allows you to iterate over a hashtable, +// and call erase(), without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// For tr1 compatibility, this can also be called as rehash(0). +// +// 3) min_load_factor(0.0) +// Setting the minimum load factor to 0.0 guarantees that +// the hash table will never shrink. +// +// Roughly speaking: +// (1) dense_hash_map: fastest, uses the most memory unless entries are small +// (2) sparse_hash_map: slowest, uses the least memory +// (3) hash_map / unordered_map (STL): in the middle +// +// Typically I use sparse_hash_map when I care about space and/or when +// I need to save the hashtable on disk. I use hash_map otherwise. I +// don't personally use dense_hash_set ever; some people use it for +// small sets with lots of lookups. +// +// - dense_hash_map has, typically, about 78% memory overhead (if your +// data takes up X bytes, the hash_map uses .78X more bytes in overhead). +// - sparse_hash_map has about 4 bits overhead per entry. +// - sparse_hash_map can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-*/dense_hash_map.html +// for information about how to use this class. + +#ifndef _DENSE_HASH_MAP_H_ +#define _DENSE_HASH_MAP_H_ + +#include +#include // for FILE * in read()/write() +#include // for the default template args +#include // for equal_to +#include // for alloc<> +#include // for pair<> +#include HASH_FUN_H // defined in config.h +#include +#include + + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template , // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to, + class Alloc = libc_allocator_with_realloc > > +class dense_hash_map { + private: + // Apparently select1st is not stl-standard, so we define our own + struct SelectKey { + typedef const Key& result_type; + const Key& operator()(const pair& p) const { + return p.first; + } + }; + struct SetKey { + void operator()(pair* value, const Key& new_key) const { + *const_cast(&value->first) = new_key; + // It would be nice to clear the rest of value here as well, in + // case it's taking up a lot of memory. We do this by clearing + // the value. This assumes T has a zero-arg constructor! + value->second = T(); + } + }; + // For operator[]. + struct DefaultValue { + STL_NAMESPACE::pair operator()(const Key& key) { + return STL_NAMESPACE::make_pair(key, T()); + } + }; + + // The actual data + typedef dense_hashtable, Key, HashFcn, SelectKey, + SetKey, EqualKey, Alloc> ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef T data_type; + typedef T mapped_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + typedef Alloc allocator_type; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::iterator iterator; + typedef typename ht::const_iterator const_iterator; + typedef typename ht::local_iterator local_iterator; + typedef typename ht::const_local_iterator const_local_iterator; + + // Iterator functions + iterator begin() { return rep.begin(); } + iterator end() { return rep.end(); } + const_iterator begin() const { return rep.begin(); } + const_iterator end() const { return rep.end(); } + + + // These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements. + local_iterator begin(size_type i) { return rep.begin(i); } + local_iterator end(size_type i) { return rep.end(i); } + const_local_iterator begin(size_type i) const { return rep.begin(i); } + const_local_iterator end(size_type i) const { return rep.end(i); } + + // Accessor functions + allocator_type get_allocator() const { return rep.get_allocator(); } + hasher hash_funct() const { return rep.hash_funct(); } + hasher hash_function() const { return hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit dense_hash_map(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { + } + + template + dense_hash_map(InputIterator f, InputIterator l, + const key_type& empty_key_val, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { + set_empty_key(empty_key_val); + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + // This clears the hash map without resizing it down to the minimum + // bucket count, but rather keeps the number of buckets constant + void clear_no_resize() { rep.clear_no_resize(); } + void swap(dense_hash_map& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + // These are tr1 methods. bucket() is the bucket the key is or would be in. + size_type bucket_size(size_type i) const { return rep.bucket_size(i); } + size_type bucket(const key_type& key) const { return rep.bucket(key); } + float load_factor() const { + return size() * 1.0f / bucket_count(); + } + float max_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return grow; + } + void max_load_factor(float new_grow) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(shrink, new_grow); + } + // These aren't tr1 methods but perhaps ought to be. + float min_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return shrink; + } + void min_load_factor(float new_shrink) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(new_shrink, grow); + } + // Deprecated; use min_load_factor() or max_load_factor() instead. + void set_resizing_parameters(float shrink, float grow) { + rep.set_resizing_parameters(shrink, grow); + } + + void resize(size_type hint) { rep.resize(hint); } + void rehash(size_type hint) { resize(hint); } // the tr1 name + + // Lookup routines + iterator find(const key_type& key) { return rep.find(key); } + const_iterator find(const key_type& key) const { return rep.find(key); } + + data_type& operator[](const key_type& key) { // This is our value-add! + // If key is in the hashtable, returns find(key)->second, + // otherwise returns insert(value_type(key, T()).first->second. + // Note it does not create an empty T unless the find fails. + return rep.template find_or_insert(key).second; + } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair equal_range(const key_type& key) { + return rep.equal_range(key); + } + pair equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair insert(const value_type& obj) { return rep.insert(obj); } + template + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion and empty routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted and empty buckets. You can change the + // deleted key as time goes on, or get rid of it entirely to be insert-only. + void set_empty_key(const key_type& key) { // YOU MUST CALL THIS! + rep.set_empty_key(value_type(key, data_type())); // rep wants a value + } + key_type empty_key() const { + return rep.empty_key().first; // rep returns a value + } + + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + key_type deleted_key() const { return rep.deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const dense_hash_map& hs) const { return rep == hs.rep; } + bool operator!=(const dense_hash_map& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +// We need a global swap as well +template +inline void swap(dense_hash_map& hm1, + dense_hash_map& hm2) { + hm1.swap(hm2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _DENSE_HASH_MAP_H_ */ diff --git a/external/google/dense_hash_set b/external/google/dense_hash_set new file mode 100644 index 0000000000..fcf5db844c --- /dev/null +++ b/external/google/dense_hash_set @@ -0,0 +1,308 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over densehashtable.h, just +// like sgi stl's stl_hash_set is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// This is more different from dense_hash_map than you might think, +// because all iterators for sets are const (you obviously can't +// change the key, and for sets there is no value). +// +// NOTE: this is exactly like sparse_hash_set.h, with the word +// "sparse" replaced by "dense", except for the addition of +// set_empty_key(). +// +// YOU MUST CALL SET_EMPTY_KEY() IMMEDIATELY AFTER CONSTRUCTION. +// +// Otherwise your program will die in mysterious ways. (Note if you +// use the constructor that takes an InputIterator range, you pass in +// the empty key in the constructor, rather than after. As a result, +// this constructor differs from the standard STL version.) +// +// In other respects, we adhere mostly to the STL semantics for +// hash-map. One important exception is that insert() may invalidate +// iterators entirely -- STL semantics are that insert() may reorder +// iterators, but they all still refer to something valid in the +// hashtable. Not so for us. Likewise, insert() may invalidate +// pointers into the hashtable. (Whether insert invalidates iterators +// and pointers depends on whether it results in a hashtable resize). +// On the plus side, delete() doesn't invalidate iterators or pointers +// at all, or even change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// If you want to use erase() you must call set_deleted_key(), +// in addition to set_empty_key(), after construction. +// The deleted and empty keys must differ. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This allows you to iterate over a hashtable, +// and call erase(), without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// For tr1 compatibility, this can also be called as rehash(0). +// +// 3) min_load_factor(0.0) +// Setting the minimum load factor to 0.0 guarantees that +// the hash table will never shrink. +// +// Roughly speaking: +// (1) dense_hash_set: fastest, uses the most memory unless entries are small +// (2) sparse_hash_set: slowest, uses the least memory +// (3) hash_set / unordered_set (STL): in the middle +// +// Typically I use sparse_hash_set when I care about space and/or when +// I need to save the hashtable on disk. I use hash_set otherwise. I +// don't personally use dense_hash_set ever; some people use it for +// small sets with lots of lookups. +// +// - dense_hash_set has, typically, about 78% memory overhead (if your +// data takes up X bytes, the hash_set uses .78X more bytes in overhead). +// - sparse_hash_set has about 4 bits overhead per entry. +// - sparse_hash_set can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-*/dense_hash_set.html +// for information about how to use this class. + +#ifndef _DENSE_HASH_SET_H_ +#define _DENSE_HASH_SET_H_ + +#include +#include // for FILE * in read()/write() +#include // for the default template args +#include // for equal_to +#include // for alloc<> +#include // for pair<> +#include HASH_FUN_H // defined in config.h +#include +#include + + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template , // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to, + class Alloc = libc_allocator_with_realloc > +class dense_hash_set { + private: + // Apparently identity is not stl-standard, so we define our own + struct Identity { + typedef const Value& result_type; + const Value& operator()(const Value& v) const { return v; } + }; + struct SetKey { + void operator()(Value* value, const Value& new_key) const { + *value = new_key; + } + }; + + // The actual data + typedef dense_hashtable ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + typedef Alloc allocator_type; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::const_pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::const_reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::const_iterator iterator; + typedef typename ht::const_iterator const_iterator; + typedef typename ht::const_local_iterator local_iterator; + typedef typename ht::const_local_iterator const_local_iterator; + + + // Iterator functions -- recall all iterators are const + iterator begin() const { return rep.begin(); } + iterator end() const { return rep.end(); } + + // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements. + local_iterator begin(size_type i) const { return rep.begin(i); } + local_iterator end(size_type i) const { return rep.end(i); } + + + // Accessor functions + allocator_type get_allocator() const { return rep.get_allocator(); } + hasher hash_funct() const { return rep.hash_funct(); } + hasher hash_function() const { return hash_funct(); } // tr1 name + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit dense_hash_set(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { + } + + template + dense_hash_set(InputIterator f, InputIterator l, + const key_type& empty_key_val, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { + set_empty_key(empty_key_val); + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + // This clears the hash set without resizing it down to the minimum + // bucket count, but rather keeps the number of buckets constant + void clear_no_resize() { rep.clear_no_resize(); } + void swap(dense_hash_set& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + // These are tr1 methods. bucket() is the bucket the key is or would be in. + size_type bucket_size(size_type i) const { return rep.bucket_size(i); } + size_type bucket(const key_type& key) const { return rep.bucket(key); } + float load_factor() const { + return size() * 1.0f / bucket_count(); + } + float max_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return grow; + } + void max_load_factor(float new_grow) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(shrink, new_grow); + } + // These aren't tr1 methods but perhaps ought to be. + float min_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return shrink; + } + void min_load_factor(float new_shrink) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(new_shrink, grow); + } + // Deprecated; use min_load_factor() or max_load_factor() instead. + void set_resizing_parameters(float shrink, float grow) { + rep.set_resizing_parameters(shrink, grow); + } + + void resize(size_type hint) { rep.resize(hint); } + void rehash(size_type hint) { resize(hint); } // the tr1 name + + // Lookup routines + iterator find(const key_type& key) const { return rep.find(key); } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + + // Insertion routines + pair insert(const value_type& obj) { + pair p = rep.insert(obj); + return pair(p.first, p.second); // const to non-const + } + template + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion and empty routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted and empty buckets. You can change the + // deleted key as time goes on, or get rid of it entirely to be insert-only. + void set_empty_key(const key_type& key) { rep.set_empty_key(key); } + key_type empty_key() const { return rep.empty_key(); } + + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + key_type deleted_key() const { return rep.deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const dense_hash_set& hs) const { return rep == hs.rep; } + bool operator!=(const dense_hash_set& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +template +inline void swap(dense_hash_set& hs1, + dense_hash_set& hs2) { + hs1.swap(hs2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _DENSE_HASH_SET_H_ */ diff --git a/external/google/sparse_hash_map b/external/google/sparse_hash_map new file mode 100644 index 0000000000..07e52d8b40 --- /dev/null +++ b/external/google/sparse_hash_map @@ -0,0 +1,310 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over sparsehashtable.h, just +// like sgi stl's stl_hash_map is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// We adhere mostly to the STL semantics for hash-map. One important +// exception is that insert() may invalidate iterators entirely -- STL +// semantics are that insert() may reorder iterators, but they all +// still refer to something valid in the hashtable. Not so for us. +// Likewise, insert() may invalidate pointers into the hashtable. +// (Whether insert invalidates iterators and pointers depends on +// whether it results in a hashtable resize). On the plus side, +// delete() doesn't invalidate iterators or pointers at all, or even +// change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// Unlike STL's hash_map, if you want to use erase() you +// *must* call set_deleted_key() after construction. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This is what allows you to iterate over a hashtable +// and call erase() without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// For tr1 compatibility, this can also be called as rehash(0). +// +// 3) min_load_factor(0.0) +// Setting the minimum load factor to 0.0 guarantees that +// the hash table will never shrink. +// +// Roughly speaking: +// (1) dense_hash_map: fastest, uses the most memory unless entries are small +// (2) sparse_hash_map: slowest, uses the least memory +// (3) hash_map / unordered_map (STL): in the middle +// +// Typically I use sparse_hash_map when I care about space and/or when +// I need to save the hashtable on disk. I use hash_map otherwise. I +// don't personally use dense_hash_map ever; some people use it for +// small maps with lots of lookups. +// +// - dense_hash_map has, typically, about 78% memory overhead (if your +// data takes up X bytes, the hash_map uses .78X more bytes in overhead). +// - sparse_hash_map has about 4 bits overhead per entry. +// - sparse_hash_map can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-*/sparse_hash_map.html +// for information about how to use this class. + +#ifndef _SPARSE_HASH_MAP_H_ +#define _SPARSE_HASH_MAP_H_ + +#include +#include // for FILE * in read()/write() +#include // for the default template args +#include // for equal_to +#include // for alloc<> +#include // for pair<> +#include HASH_FUN_H // defined in config.h +#include +#include + + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template , // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to, + class Alloc = libc_allocator_with_realloc > > +class sparse_hash_map { + private: + // Apparently select1st is not stl-standard, so we define our own + struct SelectKey { + typedef const Key& result_type; + const Key& operator()(const pair& p) const { + return p.first; + } + }; + struct SetKey { + void operator()(pair* value, const Key& new_key) const { + *const_cast(&value->first) = new_key; + // It would be nice to clear the rest of value here as well, in + // case it's taking up a lot of memory. We do this by clearing + // the value. This assumes T has a zero-arg constructor! + value->second = T(); + } + }; + // For operator[]. + struct DefaultValue { + STL_NAMESPACE::pair operator()(const Key& key) { + return STL_NAMESPACE::make_pair(key, T()); + } + }; + + // The actual data + typedef sparse_hashtable, Key, HashFcn, SelectKey, + SetKey, EqualKey, Alloc> ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef T data_type; + typedef T mapped_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + typedef Alloc allocator_type; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::iterator iterator; + typedef typename ht::const_iterator const_iterator; + typedef typename ht::local_iterator local_iterator; + typedef typename ht::const_local_iterator const_local_iterator; + + // Iterator functions + iterator begin() { return rep.begin(); } + iterator end() { return rep.end(); } + const_iterator begin() const { return rep.begin(); } + const_iterator end() const { return rep.end(); } + + // These come from tr1's unordered_map. For us, a bucket has 0 or 1 elements. + local_iterator begin(size_type i) { return rep.begin(i); } + local_iterator end(size_type i) { return rep.end(i); } + const_local_iterator begin(size_type i) const { return rep.begin(i); } + const_local_iterator end(size_type i) const { return rep.end(i); } + + // Accessor functions + allocator_type get_allocator() const { return rep.get_allocator(); } + hasher hash_funct() const { return rep.hash_funct(); } + hasher hash_function() const { return hash_funct(); } + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit sparse_hash_map(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { + } + + template + sparse_hash_map(InputIterator f, InputIterator l, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, SelectKey(), SetKey(), alloc) { + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + void swap(sparse_hash_map& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + // These are tr1 methods. bucket() is the bucket the key is or would be in. + size_type bucket_size(size_type i) const { return rep.bucket_size(i); } + size_type bucket(const key_type& key) const { return rep.bucket(key); } + float load_factor() const { + return size() * 1.0f / bucket_count(); + } + float max_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return grow; + } + void max_load_factor(float new_grow) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(shrink, new_grow); + } + // These aren't tr1 methods but perhaps ought to be. + float min_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return shrink; + } + void min_load_factor(float new_shrink) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(new_shrink, grow); + } + // Deprecated; use min_load_factor() or max_load_factor() instead. + void set_resizing_parameters(float shrink, float grow) { + rep.set_resizing_parameters(shrink, grow); + } + + void resize(size_type hint) { rep.resize(hint); } + void rehash(size_type hint) { resize(hint); } // the tr1 name + + // Lookup routines + iterator find(const key_type& key) { return rep.find(key); } + const_iterator find(const key_type& key) const { return rep.find(key); } + + data_type& operator[](const key_type& key) { // This is our value-add! + // If key is in the hashtable, returns find(key)->second, + // otherwise returns insert(value_type(key, T()).first->second. + // Note it does not create an empty T unless the find fails. + return rep.template find_or_insert(key).second; + } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair equal_range(const key_type& key) { + return rep.equal_range(key); + } + pair equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair insert(const value_type& obj) { return rep.insert(obj); } + template + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted buckets. You can change the key as + // time goes on, or get rid of it entirely to be insert-only. + void set_deleted_key(const key_type& key) { + rep.set_deleted_key(key); + } + void clear_deleted_key() { rep.clear_deleted_key(); } + key_type deleted_key() const { return rep.deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const sparse_hash_map& hs) const { return rep == hs.rep; } + bool operator!=(const sparse_hash_map& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +// We need a global swap as well +template +inline void swap(sparse_hash_map& hm1, + sparse_hash_map& hm2) { + hm1.swap(hm2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _SPARSE_HASH_MAP_H_ */ diff --git a/external/google/sparse_hash_set b/external/google/sparse_hash_set new file mode 100644 index 0000000000..6be16e9a62 --- /dev/null +++ b/external/google/sparse_hash_set @@ -0,0 +1,285 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// This is just a very thin wrapper over sparsehashtable.h, just +// like sgi stl's stl_hash_set is a very thin wrapper over +// stl_hashtable. The major thing we define is operator[], because +// we have a concept of a data_type which stl_hashtable doesn't +// (it only has a key and a value). +// +// This is more different from sparse_hash_map than you might think, +// because all iterators for sets are const (you obviously can't +// change the key, and for sets there is no value). +// +// We adhere mostly to the STL semantics for hash-map. One important +// exception is that insert() may invalidate iterators entirely -- STL +// semantics are that insert() may reorder iterators, but they all +// still refer to something valid in the hashtable. Not so for us. +// Likewise, insert() may invalidate pointers into the hashtable. +// (Whether insert invalidates iterators and pointers depends on +// whether it results in a hashtable resize). On the plus side, +// delete() doesn't invalidate iterators or pointers at all, or even +// change the ordering of elements. +// +// Here are a few "power user" tips: +// +// 1) set_deleted_key(): +// Unlike STL's hash_map, if you want to use erase() you +// *must* call set_deleted_key() after construction. +// +// 2) resize(0): +// When an item is deleted, its memory isn't freed right +// away. This allows you to iterate over a hashtable, +// and call erase(), without invalidating the iterator. +// To force the memory to be freed, call resize(0). +// For tr1 compatibility, this can also be called as rehash(0). +// +// 3) min_load_factor(0.0) +// Setting the minimum load factor to 0.0 guarantees that +// the hash table will never shrink. +// +// Roughly speaking: +// (1) dense_hash_set: fastest, uses the most memory unless entries are small +// (2) sparse_hash_set: slowest, uses the least memory +// (3) hash_set / unordered_set (STL): in the middle +// +// Typically I use sparse_hash_set when I care about space and/or when +// I need to save the hashtable on disk. I use hash_set otherwise. I +// don't personally use dense_hash_set ever; some people use it for +// small sets with lots of lookups. +// +// - dense_hash_set has, typically, about 78% memory overhead (if your +// data takes up X bytes, the hash_set uses .78X more bytes in overhead). +// - sparse_hash_set has about 4 bits overhead per entry. +// - sparse_hash_set can be 3-7 times slower than the others for lookup and, +// especially, inserts. See time_hash_map.cc for details. +// +// See /usr/(local/)?doc/sparsehash-*/sparse_hash_set.html +// for information about how to use this class. + +#ifndef _SPARSE_HASH_SET_H_ +#define _SPARSE_HASH_SET_H_ + +#include +#include // for FILE * in read()/write() +#include // for the default template args +#include // for equal_to +#include // for alloc<> +#include // for pair<> +#include HASH_FUN_H // defined in config.h +#include +#include + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +template , // defined in sparseconfig.h + class EqualKey = STL_NAMESPACE::equal_to, + class Alloc = libc_allocator_with_realloc > +class sparse_hash_set { + private: + // Apparently identity is not stl-standard, so we define our own + struct Identity { + typedef const Value& result_type; + const Value& operator()(const Value& v) const { return v; } + }; + struct SetKey { + void operator()(Value* value, const Value& new_key) const { + *value = new_key; + } + }; + + typedef sparse_hashtable ht; + ht rep; + + public: + typedef typename ht::key_type key_type; + typedef typename ht::value_type value_type; + typedef typename ht::hasher hasher; + typedef typename ht::key_equal key_equal; + typedef Alloc allocator_type; + + typedef typename ht::size_type size_type; + typedef typename ht::difference_type difference_type; + typedef typename ht::const_pointer pointer; + typedef typename ht::const_pointer const_pointer; + typedef typename ht::const_reference reference; + typedef typename ht::const_reference const_reference; + + typedef typename ht::const_iterator iterator; + typedef typename ht::const_iterator const_iterator; + typedef typename ht::const_local_iterator local_iterator; + typedef typename ht::const_local_iterator const_local_iterator; + + + // Iterator functions -- recall all iterators are const + iterator begin() const { return rep.begin(); } + iterator end() const { return rep.end(); } + + // These come from tr1's unordered_set. For us, a bucket has 0 or 1 elements. + local_iterator begin(size_type i) const { return rep.begin(i); } + local_iterator end(size_type i) const { return rep.end(i); } + + + // Accessor functions + allocator_type get_allocator() const { return rep.get_allocator(); } + hasher hash_funct() const { return rep.hash_funct(); } + hasher hash_function() const { return hash_funct(); } // tr1 name + key_equal key_eq() const { return rep.key_eq(); } + + + // Constructors + explicit sparse_hash_set(size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { + } + + template + sparse_hash_set(InputIterator f, InputIterator l, + size_type expected_max_items_in_table = 0, + const hasher& hf = hasher(), + const key_equal& eql = key_equal(), + const allocator_type& alloc = allocator_type()) + : rep(expected_max_items_in_table, hf, eql, Identity(), SetKey(), alloc) { + rep.insert(f, l); + } + // We use the default copy constructor + // We use the default operator=() + // We use the default destructor + + void clear() { rep.clear(); } + void swap(sparse_hash_set& hs) { rep.swap(hs.rep); } + + + // Functions concerning size + size_type size() const { return rep.size(); } + size_type max_size() const { return rep.max_size(); } + bool empty() const { return rep.empty(); } + size_type bucket_count() const { return rep.bucket_count(); } + size_type max_bucket_count() const { return rep.max_bucket_count(); } + + // These are tr1 methods. bucket() is the bucket the key is or would be in. + size_type bucket_size(size_type i) const { return rep.bucket_size(i); } + size_type bucket(const key_type& key) const { return rep.bucket(key); } + float load_factor() const { + return size() * 1.0f / bucket_count(); + } + float max_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return grow; + } + void max_load_factor(float new_grow) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(shrink, new_grow); + } + // These aren't tr1 methods but perhaps ought to be. + float min_load_factor() const { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + return shrink; + } + void min_load_factor(float new_shrink) { + float shrink, grow; + rep.get_resizing_parameters(&shrink, &grow); + rep.set_resizing_parameters(new_shrink, grow); + } + // Deprecated; use min_load_factor() or max_load_factor() instead. + void set_resizing_parameters(float shrink, float grow) { + rep.set_resizing_parameters(shrink, grow); + } + + void resize(size_type hint) { rep.resize(hint); } + void rehash(size_type hint) { resize(hint); } // the tr1 name + + // Lookup routines + iterator find(const key_type& key) const { return rep.find(key); } + + size_type count(const key_type& key) const { return rep.count(key); } + + pair equal_range(const key_type& key) const { + return rep.equal_range(key); + } + + // Insertion routines + pair insert(const value_type& obj) { + pair p = rep.insert(obj); + return pair(p.first, p.second); // const to non-const + } + template + void insert(InputIterator f, InputIterator l) { rep.insert(f, l); } + void insert(const_iterator f, const_iterator l) { rep.insert(f, l); } + // required for std::insert_iterator; the passed-in iterator is ignored + iterator insert(iterator, const value_type& obj) { return insert(obj).first; } + + + // Deletion routines + // THESE ARE NON-STANDARD! I make you specify an "impossible" key + // value to identify deleted buckets. You can change the key as + // time goes on, or get rid of it entirely to be insert-only. + void set_deleted_key(const key_type& key) { rep.set_deleted_key(key); } + void clear_deleted_key() { rep.clear_deleted_key(); } + key_type deleted_key() const { return rep.deleted_key(); } + + // These are standard + size_type erase(const key_type& key) { return rep.erase(key); } + void erase(iterator it) { rep.erase(it); } + void erase(iterator f, iterator l) { rep.erase(f, l); } + + + // Comparison + bool operator==(const sparse_hash_set& hs) const { return rep == hs.rep; } + bool operator!=(const sparse_hash_set& hs) const { return rep != hs.rep; } + + + // I/O -- this is an add-on for writing metainformation to disk + bool write_metadata(FILE *fp) { return rep.write_metadata(fp); } + bool read_metadata(FILE *fp) { return rep.read_metadata(fp); } + bool write_nopointer_data(FILE *fp) { return rep.write_nopointer_data(fp); } + bool read_nopointer_data(FILE *fp) { return rep.read_nopointer_data(fp); } +}; + +template +inline void swap(sparse_hash_set& hs1, + sparse_hash_set& hs2) { + hs1.swap(hs2); +} + +_END_GOOGLE_NAMESPACE_ + +#endif /* _SPARSE_HASH_SET_H_ */ diff --git a/external/google/sparsehash/densehashtable.h b/external/google/sparsehash/densehashtable.h new file mode 100644 index 0000000000..8a3126e3bd --- /dev/null +++ b/external/google/sparsehash/densehashtable.h @@ -0,0 +1,1268 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// A dense hashtable is a particular implementation of +// a hashtable: one that is meant to minimize memory allocation. +// It does this by using an array to store all the data. We +// steal a value from the key space to indicate "empty" array +// elements (ie indices where no item lives) and another to indicate +// "deleted" elements. +// +// (Note it is possible to change the value of the delete key +// on the fly; you can even remove it, though after that point +// the hashtable is insert_only until you set it again. The empty +// value however can't be changed.) +// +// To minimize allocation and pointer overhead, we use internal +// probing, in which the hashtable is a single table, and collisions +// are resolved by trying to insert again in another bucket. The +// most cache-efficient internal probing schemes are linear probing +// (which suffers, alas, from clumping) and quadratic probing, which +// is what we implement by default. +// +// Type requirements: value_type is required to be Copy Constructible +// and Default Constructible. It is not required to be (and commonly +// isn't) Assignable. +// +// You probably shouldn't use this code directly. Use +// or instead. + +// You can change the following below: +// HT_OCCUPANCY_PCT -- how full before we double size +// HT_EMPTY_PCT -- how empty before we halve size +// HT_MIN_BUCKETS -- default smallest bucket size +// +// You can also change enlarge_factor (which defaults to +// HT_OCCUPANCY_PCT), and shrink_factor (which defaults to +// HT_EMPTY_PCT) with set_resizing_parameters(). +// +// How to decide what values to use? +// shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good. +// HT_MIN_BUCKETS is probably unnecessary since you can specify +// (indirectly) the starting number of buckets at construct-time. +// For enlarge_factor, you can use this chart to try to trade-off +// expected lookup time to the space taken up. By default, this +// code uses quadratic probing, though you can change it to linear +// via _JUMP below if you really want to. +// +// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html +// NUMBER OF PROBES / LOOKUP Successful Unsuccessful +// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) +// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 +// +// -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 +// QUADRATIC COLLISION RES. +// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 +// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 +// LINEAR COLLISION RES. +// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 +// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 + +#ifndef _DENSEHASHTABLE_H_ +#define _DENSEHASHTABLE_H_ + +// The probing method +// Linear probing +// #define JUMP_(key, num_probes) ( 1 ) +// Quadratic probing +#define JUMP_(key, num_probes) ( num_probes ) + + +#include +#include +#include +#include // for abort() +#include // For swap(), eg +#include // For length_error +#include // For cerr +#include // For uninitialized_fill, uninitialized_copy +#include // for pair<> +#include // for facts about iterator tags +#include // for numeric_limits<> +#include +#include +#include // for true_type, integral_constant, etc. + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +// Hashtable class, used to implement the hashed associative containers +// hash_set and hash_map. + +// Value: what is stored in the table (each bucket is a Value). +// Key: something in a 1-to-1 correspondence to a Value, that can be used +// to search for a Value in the table (find() takes a Key). +// HashFcn: Takes a Key and returns an integer, the more unique the better. +// ExtractKey: given a Value, returns the unique Key associated with it. +// Must inherit from unary_function, or at least have a +// result_type enum indicating the return type of operator(). +// SetKey: given a Value* and a Key, modifies the value such that +// ExtractKey(value) == key. We guarantee this is only called +// with key == deleted_key or key == empty_key. +// EqualKey: Given two Keys, says whether they are the same (that is, +// if they are both associated with the same Value). +// Alloc: STL allocator to use to allocate memory. + +template +class dense_hashtable; + +template +struct dense_hashtable_iterator; + +template +struct dense_hashtable_const_iterator; + +// We're just an array, but we need to skip over empty and deleted elements +template +struct dense_hashtable_iterator { + private: + typedef typename A::template rebind::other value_alloc_type; + + public: + typedef dense_hashtable_iterator iterator; + typedef dense_hashtable_const_iterator const_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::pointer pointer; + + // "Real" constructor and default constructor + dense_hashtable_iterator(const dense_hashtable *h, + pointer it, pointer it_end, bool advance) + : ht(h), pos(it), end(it_end) { + if (advance) advance_past_empty_and_deleted(); + } + dense_hashtable_iterator() { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on an empty or marked-deleted array element + void advance_past_empty_and_deleted() { + while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) + ++pos; + } + iterator& operator++() { + assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const iterator& it) const { return pos == it.pos; } + bool operator!=(const iterator& it) const { return pos != it.pos; } + + + // The actual data + const dense_hashtable *ht; + pointer pos, end; +}; + + +// Now do it all again, but with const-ness! +template +struct dense_hashtable_const_iterator { + private: + typedef typename A::template rebind::other value_alloc_type; + + public: + typedef dense_hashtable_iterator iterator; + typedef dense_hashtable_const_iterator const_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::const_reference reference; + typedef typename value_alloc_type::const_pointer pointer; + + // "Real" constructor and default constructor + dense_hashtable_const_iterator( + const dense_hashtable *h, + pointer it, pointer it_end, bool advance) + : ht(h), pos(it), end(it_end) { + if (advance) advance_past_empty_and_deleted(); + } + dense_hashtable_const_iterator() + : ht(NULL), pos(pointer()), end(pointer()) { } + // This lets us convert regular iterators to const iterators + dense_hashtable_const_iterator(const iterator &it) + : ht(it.ht), pos(it.pos), end(it.end) { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on an empty or marked-deleted array element + void advance_past_empty_and_deleted() { + while ( pos != end && (ht->test_empty(*this) || ht->test_deleted(*this)) ) + ++pos; + } + const_iterator& operator++() { + assert(pos != end); ++pos; advance_past_empty_and_deleted(); return *this; + } + const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const const_iterator& it) const { return pos == it.pos; } + bool operator!=(const const_iterator& it) const { return pos != it.pos; } + + + // The actual data + const dense_hashtable *ht; + pointer pos, end; +}; + +template +class dense_hashtable { + private: + typedef typename Alloc::template rebind::other value_alloc_type; + + public: + typedef Key key_type; + typedef Value value_type; + typedef HashFcn hasher; + typedef EqualKey key_equal; + typedef Alloc allocator_type; + + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::const_reference const_reference; + typedef typename value_alloc_type::pointer pointer; + typedef typename value_alloc_type::const_pointer const_pointer; + typedef dense_hashtable_iterator + iterator; + + typedef dense_hashtable_const_iterator + const_iterator; + + // These come from tr1. For us they're the same as regular iterators. + typedef iterator local_iterator; + typedef const_iterator const_local_iterator; + + // How full we let the table get before we resize, by default. + // Knuth says .8 is good -- higher causes us to probe too much, + // though it saves memory. + static const int HT_OCCUPANCY_PCT; // = 50 (out of 100) + + // How empty we let the table get before we resize lower, by default. + // (0.0 means never resize lower.) + // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing + static const int HT_EMPTY_PCT; // = 0.4 * HT_OCCUPANCY_PCT; + + // Minimum size we're willing to let hashtables be. + // Must be a power of two, and at least 4. + // Note, however, that for a given hashtable, the initial size is a + // function of the first constructor arg, and may be >HT_MIN_BUCKETS. + static const size_type HT_MIN_BUCKETS = 4; + + // By default, if you don't specify a hashtable size at + // construction-time, we use this size. Must be a power of two, and + // at least HT_MIN_BUCKETS. + static const size_type HT_DEFAULT_STARTING_BUCKETS = 32; + + // ITERATOR FUNCTIONS + iterator begin() { return iterator(this, table, + table + num_buckets, true); } + iterator end() { return iterator(this, table + num_buckets, + table + num_buckets, true); } + const_iterator begin() const { return const_iterator(this, table, + table+num_buckets,true);} + const_iterator end() const { return const_iterator(this, table + num_buckets, + table+num_buckets,true);} + + // These come from tr1 unordered_map. They iterate over 'bucket' n. + // We'll just consider bucket n to be the n-th element of the table. + local_iterator begin(size_type i) { + return local_iterator(this, table + i, table + i+1, false); + } + local_iterator end(size_type i) { + local_iterator it = begin(i); + if (!test_empty(i) && !test_deleted(i)) + ++it; + return it; + } + const_local_iterator begin(size_type i) const { + return const_local_iterator(this, table + i, table + i+1, false); + } + const_local_iterator end(size_type i) const { + const_local_iterator it = begin(i); + if (!test_empty(i) && !test_deleted(i)) + ++it; + return it; + } + + // ACCESSOR FUNCTIONS for the things we templatize on, basically + hasher hash_funct() const { return settings; } + key_equal key_eq() const { return key_info; } + allocator_type get_allocator() const { + return allocator_type(val_info); + } + + // Accessor function for statistics gathering. + int num_table_copies() const { return settings.num_ht_copies(); } + + private: + // Annoyingly, we can't copy values around, because they might have + // const components (they're probably pair). We use + // explicit destructor invocation and placement new to get around + // this. Arg. + void set_value(pointer dst, const_reference src) { + dst->~value_type(); // delete the old value, if any + new(dst) value_type(src); + } + + void destroy_buckets(size_type first, size_type last) { + for ( ; first != last; ++first) + table[first].~value_type(); + } + + // DELETE HELPER FUNCTIONS + // This lets the user describe a key that will indicate deleted + // table entries. This key should be an "impossible" entry -- + // if you try to insert it for real, you won't be able to retrieve it! + // (NB: while you pass in an entire value, only the key part is looked + // at. This is just because I don't know how to assign just a key.) + private: + void squash_deleted() { // gets rid of any deleted entries we have + if ( num_deleted ) { // get rid of deleted before writing + dense_hashtable tmp(*this); // copying will get rid of deleted + swap(tmp); // now we are tmp + } + assert(num_deleted == 0); + } + + bool test_deleted_key(const key_type& key) const { + // The num_deleted test is crucial for read(): after read(), the ht values + // are garbage, and we don't want to think some of them are deleted. + // Invariant: !use_deleted implies num_deleted is 0. + assert(settings.use_deleted() || num_deleted == 0); + return num_deleted > 0 && equals(key_info.delkey, key); + } + + public: + void set_deleted_key(const key_type &key) { + // the empty indicator (if specified) and the deleted indicator + // must be different + assert((!settings.use_empty() || !equals(key, get_key(val_info.emptyval))) + && "Passed the empty-key to set_deleted_key"); + // It's only safe to change what "deleted" means if we purge deleted guys + squash_deleted(); + settings.set_use_deleted(true); + key_info.delkey = key; + } + void clear_deleted_key() { + squash_deleted(); + settings.set_use_deleted(false); + } + key_type deleted_key() const { + assert(settings.use_deleted() + && "Must set deleted key before calling deleted_key"); + return key_info.delkey; + } + + // These are public so the iterators can use them + // True if the item at position bucknum is "deleted" marker + bool test_deleted(size_type bucknum) const { + return test_deleted_key(get_key(table[bucknum])); + } + bool test_deleted(const iterator &it) const { + return test_deleted_key(get_key(*it)); + } + bool test_deleted(const const_iterator &it) const { + return test_deleted_key(get_key(*it)); + } + + private: + // Set it so test_deleted is true. true if object didn't used to be deleted. + bool set_deleted(iterator &it) { + assert(settings.use_deleted()); + bool retval = !test_deleted(it); + // &* converts from iterator to value-type. + set_key(&(*it), key_info.delkey); + return retval; + } + // Set it so test_deleted is false. true if object used to be deleted. + bool clear_deleted(iterator &it) { + assert(settings.use_deleted()); + // Happens automatically when we assign something else in its place. + return test_deleted(it); + } + + // We also allow to set/clear the deleted bit on a const iterator. + // We allow a const_iterator for the same reason you can delete a + // const pointer: it's convenient, and semantically you can't use + // 'it' after it's been deleted anyway, so its const-ness doesn't + // really matter. + bool set_deleted(const_iterator &it) { + assert(settings.use_deleted()); + bool retval = !test_deleted(it); + set_key(const_cast(&(*it)), key_info.delkey); + return retval; + } + // Set it so test_deleted is false. true if object used to be deleted. + bool clear_deleted(const_iterator &it) { + assert(settings.use_deleted()); + return test_deleted(it); + } + + // EMPTY HELPER FUNCTIONS + // This lets the user describe a key that will indicate empty (unused) + // table entries. This key should be an "impossible" entry -- + // if you try to insert it for real, you won't be able to retrieve it! + // (NB: while you pass in an entire value, only the key part is looked + // at. This is just because I don't know how to assign just a key.) + public: + // These are public so the iterators can use them + // True if the item at position bucknum is "empty" marker + bool test_empty(size_type bucknum) const { + assert(settings.use_empty()); // we always need to know what's empty! + return equals(get_key(val_info.emptyval), get_key(table[bucknum])); + } + bool test_empty(const iterator &it) const { + assert(settings.use_empty()); // we always need to know what's empty! + return equals(get_key(val_info.emptyval), get_key(*it)); + } + bool test_empty(const const_iterator &it) const { + assert(settings.use_empty()); // we always need to know what's empty! + return equals(get_key(val_info.emptyval), get_key(*it)); + } + + private: + void fill_range_with_empty(pointer table_start, pointer table_end) { + STL_NAMESPACE::uninitialized_fill(table_start, table_end, val_info.emptyval); + } + + public: + // TODO(csilvers): change all callers of this to pass in a key instead, + // and take a const key_type instead of const value_type. + void set_empty_key(const_reference val) { + // Once you set the empty key, you can't change it + assert(!settings.use_empty() && "Calling set_empty_key multiple times"); + // The deleted indicator (if specified) and the empty indicator + // must be different. + assert((!settings.use_deleted() || !equals(get_key(val), key_info.delkey)) + && "Setting the empty key the same as the deleted key"); + settings.set_use_empty(true); + set_value(&val_info.emptyval, val); + + assert(!table); // must set before first use + // num_buckets was set in constructor even though table was NULL + table = val_info.allocate(num_buckets); + assert(table); + fill_range_with_empty(table, table + num_buckets); + } + // TODO(sjackman): return a key_type rather than a value_type + value_type empty_key() const { + assert(settings.use_empty()); + return val_info.emptyval; + } + + // FUNCTIONS CONCERNING SIZE + public: + size_type size() const { return num_elements - num_deleted; } + size_type max_size() const { return val_info.max_size(); } + bool empty() const { return size() == 0; } + size_type bucket_count() const { return num_buckets; } + size_type max_bucket_count() const { return max_size(); } + size_type nonempty_bucket_count() const { return num_elements; } + // These are tr1 methods. Their idea of 'bucket' doesn't map well to + // what we do. We just say every bucket has 0 or 1 items in it. + size_type bucket_size(size_type i) const { + return begin(i) == end(i) ? 0 : 1; + } + + private: + // Because of the above, size_type(-1) is never legal; use it for errors + static const size_type ILLEGAL_BUCKET = size_type(-1); + + // Used after a string of deletes. Returns true if we actually shrunk. + // TODO(csilvers): take a delta so we can take into account inserts + // done after shrinking. Maybe make part of the Settings class? + bool maybe_shrink() { + assert(num_elements >= num_deleted); + assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two + assert(bucket_count() >= HT_MIN_BUCKETS); + bool retval = false; + + // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, + // we'll never shrink until you get relatively big, and we'll never + // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something + // like "dense_hash_set x; x.insert(4); x.erase(4);" will + // shrink us down to HT_MIN_BUCKETS buckets, which is too small. + const size_type num_remain = num_elements - num_deleted; + const size_type shrink_threshold = settings.shrink_threshold(); + if (shrink_threshold > 0 && num_remain < shrink_threshold && + bucket_count() > HT_DEFAULT_STARTING_BUCKETS) { + const float shrink_factor = settings.shrink_factor(); + size_type sz = bucket_count() / 2; // find how much we should shrink + while (sz > HT_DEFAULT_STARTING_BUCKETS && + num_remain < sz * shrink_factor) { + sz /= 2; // stay a power of 2 + } + dense_hashtable tmp(*this, sz); // Do the actual resizing + swap(tmp); // now we are tmp + retval = true; + } + settings.set_consider_shrink(false); // because we just considered it + return retval; + } + + // We'll let you resize a hashtable -- though this makes us copy all! + // When you resize, you say, "make it big enough for this many more elements" + // Returns true if we actually resized, false if size was already ok. + bool resize_delta(size_type delta) { + bool did_resize = false; + if ( settings.consider_shrink() ) { // see if lots of deletes happened + if ( maybe_shrink() ) + did_resize = true; + } + if (num_elements >= (STL_NAMESPACE::numeric_limits::max)() - delta) + throw std::length_error("resize overflow"); + if ( bucket_count() >= HT_MIN_BUCKETS && + (num_elements + delta) <= settings.enlarge_threshold() ) + return did_resize; // we're ok as we are + + // Sometimes, we need to resize just to get rid of all the + // "deleted" buckets that are clogging up the hashtable. So when + // deciding whether to resize, count the deleted buckets (which + // are currently taking up room). But later, when we decide what + // size to resize to, *don't* count deleted buckets, since they + // get discarded during the resize. + const size_type needed_size = settings.min_buckets(num_elements + delta, 0); + if ( needed_size <= bucket_count() ) // we have enough buckets + return did_resize; + + size_type resize_to = + settings.min_buckets(num_elements - num_deleted + delta, bucket_count()); + + if (resize_to < needed_size && // may double resize_to + resize_to < (STL_NAMESPACE::numeric_limits::max)() / 2) { + // This situation means that we have enough deleted elements, + // that once we purge them, we won't actually have needed to + // grow. But we may want to grow anyway: if we just purge one + // element, say, we'll have to grow anyway next time we + // insert. Might as well grow now, since we're already going + // through the trouble of copying (in order to purge the + // deleted elements). + const size_type target = + static_cast(settings.shrink_size(resize_to*2)); + if (num_elements - num_deleted + delta >= target) { + // Good, we won't be below the shrink threshhold even if we double. + resize_to *= 2; + } + } + dense_hashtable tmp(*this, resize_to); + swap(tmp); // now we are tmp + return true; + } + + // We require table be not-NULL and empty before calling this. + void resize_table(size_type /*old_size*/, size_type new_size, + true_type) { + table = val_info.realloc_or_die(table, new_size); + } + + void resize_table(size_type old_size, size_type new_size, false_type) { + val_info.deallocate(table, old_size); + table = val_info.allocate(new_size); + } + + // Used to actually do the rehashing when we grow/shrink a hashtable + void copy_from(const dense_hashtable &ht, size_type min_buckets_wanted) { + clear_to_size(settings.min_buckets(ht.size(), min_buckets_wanted)); + + // We use a normal iterator to get non-deleted bcks from ht + // We could use insert() here, but since we know there are + // no duplicates and no deleted items, we can be more efficient + assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two + for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + const size_type bucket_count_minus_one = bucket_count() - 1; + for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; + !test_empty(bucknum); // not empty + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { + ++num_probes; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + set_value(&table[bucknum], *it); // copies the value to here + num_elements++; + } + settings.inc_num_ht_copies(); + } + + // Required by the spec for hashed associative container + public: + // Though the docs say this should be num_buckets, I think it's much + // more useful as num_elements. As a special feature, calling with + // req_elements==0 will cause us to shrink if we can, saving space. + void resize(size_type req_elements) { // resize to this or larger + if ( settings.consider_shrink() || req_elements == 0 ) + maybe_shrink(); + if ( req_elements > num_elements ) + resize_delta(req_elements - num_elements); + } + + // Get and change the value of shrink_factor and enlarge_factor. The + // description at the beginning of this file explains how to choose + // the values. Setting the shrink parameter to 0.0 ensures that the + // table never shrinks. + void get_resizing_parameters(float* shrink, float* grow) const { + *shrink = settings.shrink_factor(); + *grow = settings.enlarge_factor(); + } + void set_resizing_parameters(float shrink, float grow) { + settings.set_resizing_parameters(shrink, grow); + settings.reset_thresholds(bucket_count()); + } + + // CONSTRUCTORS -- as required by the specs, we take a size, + // but also let you specify a hashfunction, key comparator, + // and key extractor. We also define a copy constructor and =. + // DESTRUCTOR -- needs to free the table + explicit dense_hashtable(size_type expected_max_items_in_table = 0, + const HashFcn& hf = HashFcn(), + const EqualKey& eql = EqualKey(), + const ExtractKey& ext = ExtractKey(), + const SetKey& set = SetKey(), + const Alloc& alloc = Alloc()) + : settings(hf), + key_info(ext, set, eql), + num_deleted(0), + num_elements(0), + num_buckets(expected_max_items_in_table == 0 + ? HT_DEFAULT_STARTING_BUCKETS + : settings.min_buckets(expected_max_items_in_table, 0)), + val_info(alloc_impl(alloc)), + table(NULL) { + // table is NULL until emptyval is set. However, we set num_buckets + // here so we know how much space to allocate once emptyval is set + settings.reset_thresholds(bucket_count()); + } + + // As a convenience for resize(), we allow an optional second argument + // which lets you make this new hashtable a different size than ht + dense_hashtable(const dense_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : settings(ht.settings), + key_info(ht.key_info), + num_deleted(0), + num_elements(0), + num_buckets(0), + val_info(ht.val_info), + table(NULL) { + if (!ht.settings.use_empty()) { + // If use_empty isn't set, copy_from will crash, so we do our own copying. + assert(ht.empty()); + num_buckets = settings.min_buckets(ht.size(), min_buckets_wanted); + settings.reset_thresholds(bucket_count()); + return; + } + settings.reset_thresholds(bucket_count()); + copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries + } + + dense_hashtable& operator= (const dense_hashtable& ht) { + if (&ht == this) return *this; // don't copy onto ourselves + if (!ht.settings.use_empty()) { + assert(ht.empty()); + dense_hashtable empty_table(ht); // empty table with ht's thresholds + this->swap(empty_table); + return *this; + } + settings = ht.settings; + key_info = ht.key_info; + set_value(&val_info.emptyval, ht.val_info.emptyval); + // copy_from() calls clear and sets num_deleted to 0 too + copy_from(ht, HT_MIN_BUCKETS); + // we purposefully don't copy the allocator, which may not be copyable + return *this; + } + + ~dense_hashtable() { + if (table) { + destroy_buckets(0, num_buckets); + val_info.deallocate(table, num_buckets); + } + } + + // Many STL algorithms use swap instead of copy constructors + void swap(dense_hashtable& ht) { + STL_NAMESPACE::swap(settings, ht.settings); + STL_NAMESPACE::swap(key_info, ht.key_info); + STL_NAMESPACE::swap(num_deleted, ht.num_deleted); + STL_NAMESPACE::swap(num_elements, ht.num_elements); + STL_NAMESPACE::swap(num_buckets, ht.num_buckets); + { value_type tmp; // for annoying reasons, swap() doesn't work + set_value(&tmp, val_info.emptyval); + set_value(&val_info.emptyval, ht.val_info.emptyval); + set_value(&ht.val_info.emptyval, tmp); + } + STL_NAMESPACE::swap(table, ht.table); + settings.reset_thresholds(bucket_count()); // this also resets consider_shrink + ht.settings.reset_thresholds(bucket_count()); + // we purposefully don't swap the allocator, which may not be swap-able + } + + private: + void clear_to_size(size_type new_num_buckets) { + if (!table) { + table = val_info.allocate(new_num_buckets); + } else { + destroy_buckets(0, num_buckets); + if (new_num_buckets != num_buckets) { // resize, if necessary + typedef integral_constant >::value> + realloc_ok; + resize_table(num_buckets, new_num_buckets, realloc_ok()); + } + } + assert(table); + fill_range_with_empty(table, table + new_num_buckets); + num_elements = 0; + num_deleted = 0; + num_buckets = new_num_buckets; // our new size + settings.reset_thresholds(bucket_count()); + } + + public: + // It's always nice to be able to clear a table without deallocating it + void clear() { + // If the table is already empty, and the number of buckets is + // already as we desire, there's nothing to do. + const size_type new_num_buckets = settings.min_buckets(0, 0); + if (num_elements == 0 && new_num_buckets == num_buckets) { + return; + } + clear_to_size(new_num_buckets); + } + + // Clear the table without resizing it. + // Mimicks the stl_hashtable's behaviour when clear()-ing in that it + // does not modify the bucket count + void clear_no_resize() { + if (num_elements > 0) { + assert(table); + destroy_buckets(0, num_buckets); + fill_range_with_empty(table, table + num_buckets); + } + // don't consider to shrink before another erase() + settings.reset_thresholds(bucket_count()); + num_elements = 0; + num_deleted = 0; + } + + // LOOKUP ROUTINES + private: + // Returns a pair of positions: 1st where the object is, 2nd where + // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET + // if object is not found; 2nd is ILLEGAL_BUCKET if it is. + // Note: because of deletions where-to-insert is not trivial: it's the + // first deleted bucket we see, as long as we don't find the key later + pair find_position(const key_type &key) const { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + size_type insert_pos = ILLEGAL_BUCKET; // where we would insert + while ( 1 ) { // probe until something happens + if ( test_empty(bucknum) ) { // bucket is empty + if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert + return pair(ILLEGAL_BUCKET, bucknum); + else + return pair(ILLEGAL_BUCKET, insert_pos); + + } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert + if ( insert_pos == ILLEGAL_BUCKET ) + insert_pos = bucknum; + + } else if ( equals(key, get_key(table[bucknum])) ) { + return pair(bucknum, ILLEGAL_BUCKET); + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + + public: + iterator find(const key_type& key) { + if ( size() == 0 ) return end(); + pair pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return iterator(this, table + pos.first, table + num_buckets, false); + } + + const_iterator find(const key_type& key) const { + if ( size() == 0 ) return end(); + pair pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return const_iterator(this, table + pos.first, table+num_buckets, false); + } + + // This is a tr1 method: the bucket a given key is in, or what bucket + // it would be put in, if it were to be inserted. Shrug. + size_type bucket(const key_type& key) const { + pair pos = find_position(key); + return pos.first == ILLEGAL_BUCKET ? pos.second : pos.first; + } + + // Counts how many elements have key key. For maps, it's either 0 or 1. + size_type count(const key_type &key) const { + pair pos = find_position(key); + return pos.first == ILLEGAL_BUCKET ? 0 : 1; + } + + // Likewise, equal_range doesn't really make sense for us. Oh well. + pair equal_range(const key_type& key) { + iterator pos = find(key); // either an iterator or end + if (pos == end()) { + return pair(pos, pos); + } else { + const iterator startpos = pos++; + return pair(startpos, pos); + } + } + pair equal_range(const key_type& key) const { + const_iterator pos = find(key); // either an iterator or end + if (pos == end()) { + return pair(pos, pos); + } else { + const const_iterator startpos = pos++; + return pair(startpos, pos); + } + } + + + // INSERTION ROUTINES + private: + // Private method used by insert_noresize and find_or_insert. + iterator insert_at(const_reference obj, size_type pos) { + if (size() >= max_size()) + throw std::length_error("insert overflow"); + if ( test_deleted(pos) ) { // just replace if it's been del. + // shrug: shouldn't need to be const. + const_iterator delpos(this, table + pos, table + num_buckets, false); + clear_deleted(delpos); + assert( num_deleted > 0); + --num_deleted; // used to be, now it isn't + } else { + ++num_elements; // replacing an empty bucket + } + set_value(&table[pos], obj); + return iterator(this, table + pos, table + num_buckets, false); + } + + // If you know *this is big enough to hold obj, use this routine + pair insert_noresize(const_reference obj) { + // First, double-check we're not inserting delkey or emptyval + assert((!settings.use_empty() || !equals(get_key(obj), + get_key(val_info.emptyval))) + && "Inserting the empty key"); + assert((!settings.use_deleted() || !equals(get_key(obj), key_info.delkey)) + && "Inserting the deleted key"); + const pair pos = find_position(get_key(obj)); + if ( pos.first != ILLEGAL_BUCKET) { // object was already there + return pair(iterator(this, table + pos.first, + table + num_buckets, false), + false); // false: we didn't insert + } else { // pos.second says where to put it + return pair(insert_at(obj, pos.second), true); + } + } + + // Specializations of insert(it, it) depending on the power of the iterator: + // (1) Iterator supports operator-, resize before inserting + template + void insert(ForwardIterator f, ForwardIterator l, STL_NAMESPACE::forward_iterator_tag) { + size_t dist = STL_NAMESPACE::distance(f, l); + if (dist >= (std::numeric_limits::max)()) + throw std::length_error("insert-range overflow"); + resize_delta(static_cast(dist)); + for ( ; dist > 0; --dist, ++f) { + insert_noresize(*f); + } + } + + // (2) Arbitrary iterator, can't tell how much to resize + template + void insert(InputIterator f, InputIterator l, STL_NAMESPACE::input_iterator_tag) { + for ( ; f != l; ++f) + insert(*f); + } + + public: + // This is the normal insert routine, used by the outside world + pair insert(const_reference obj) { + resize_delta(1); // adding an object, grow if need be + return insert_noresize(obj); + } + + // When inserting a lot at a time, we specialize on the type of iterator + template + void insert(InputIterator f, InputIterator l) { + // specializes on iterator type + insert(f, l, typename STL_NAMESPACE::iterator_traits::iterator_category()); + } + + // DefaultValue is a functor that takes a key and returns a value_type + // representing the default value to be inserted if none is found. + template + value_type& find_or_insert(const key_type& key) { + // First, double-check we're not inserting emptykey or delkey + assert((!settings.use_empty() || !equals(key, get_key(val_info.emptyval))) + && "Inserting the empty key"); + assert((!settings.use_deleted() || !equals(key, key_info.delkey)) + && "Inserting the deleted key"); + const pair pos = find_position(key); + DefaultValue default_value; + if ( pos.first != ILLEGAL_BUCKET) { // object was already there + return table[pos.first]; + } else if (resize_delta(1)) { // needed to rehash to make room + // Since we resized, we can't use pos, so recalculate where to insert. + return *insert_noresize(default_value(key)).first; + } else { // no need to rehash, insert right here + return *insert_at(default_value(key), pos.second); + } + } + + // DELETION ROUTINES + size_type erase(const key_type& key) { + // First, double-check we're not trying to erase delkey or emptyval. + assert((!settings.use_empty() || !equals(key, get_key(val_info.emptyval))) + && "Erasing the empty key"); + assert((!settings.use_deleted() || !equals(key, key_info.delkey)) + && "Erasing the deleted key"); + const_iterator pos = find(key); // shrug: shouldn't need to be const + if ( pos != end() ) { + assert(!test_deleted(pos)); // or find() shouldn't have returned it + set_deleted(pos); + ++num_deleted; + settings.set_consider_shrink(true); // will think about shrink after next insert + return 1; // because we deleted one thing + } else { + return 0; // because we deleted nothing + } + } + + // We return the iterator past the deleted item. + void erase(iterator pos) { + if ( pos == end() ) return; // sanity check + if ( set_deleted(pos) ) { // true if object has been newly deleted + ++num_deleted; + settings.set_consider_shrink(true); // will think about shrink after next insert + } + } + + void erase(iterator f, iterator l) { + for ( ; f != l; ++f) { + if ( set_deleted(f) ) // should always be true + ++num_deleted; + } + settings.set_consider_shrink(true); // will think about shrink after next insert + } + + // We allow you to erase a const_iterator just like we allow you to + // erase an iterator. This is in parallel to 'delete': you can delete + // a const pointer just like a non-const pointer. The logic is that + // you can't use the object after it's erased anyway, so it doesn't matter + // if it's const or not. + void erase(const_iterator pos) { + if ( pos == end() ) return; // sanity check + if ( set_deleted(pos) ) { // true if object has been newly deleted + ++num_deleted; + settings.set_consider_shrink(true); // will think about shrink after next insert + } + } + void erase(const_iterator f, const_iterator l) { + for ( ; f != l; ++f) { + if ( set_deleted(f) ) // should always be true + ++num_deleted; + } + settings.set_consider_shrink(true); // will think about shrink after next insert + } + + + // COMPARISON + bool operator==(const dense_hashtable& ht) const { + if (size() != ht.size()) { + return false; + } else if (this == &ht) { + return true; + } else { + // Iterate through the elements in "this" and see if the + // corresponding element is in ht + for ( const_iterator it = begin(); it != end(); ++it ) { + const_iterator it2 = ht.find(get_key(*it)); + if ((it2 == ht.end()) || (*it != *it2)) { + return false; + } + } + return true; + } + } + bool operator!=(const dense_hashtable& ht) const { + return !(*this == ht); + } + + + // I/O + // We support reading and writing hashtables to disk. Alas, since + // I don't know how to write a hasher or key_equal, you have to make + // sure everything but the table is the same. We compact before writing + // + // NOTE: These functions are currently TODO. They've not been implemented. + bool write_metadata(FILE * /*fp*/) { + squash_deleted(); // so we don't have to worry about delkey + return false; // TODO + } + + bool read_metadata(FILE* /*fp*/) { + num_deleted = 0; // since we got rid before writing + assert(settings.use_empty() && "empty_key not set for read_metadata"); + if (table) val_info.deallocate(table, num_buckets); // we'll make our own + // TODO: read magic number + // TODO: read num_buckets + settings.reset_thresholds(bucket_count()); + table = val_info.allocate(num_buckets); + assert(table); + fill_range_with_empty(table, table + num_buckets); + // TODO: read num_elements + for ( size_type i = 0; i < num_elements; ++i ) { + // TODO: read bucket_num + // TODO: set with non-empty, non-deleted value + } + return false; // TODO + } + + // If your keys and values are simple enough, we can write them to + // disk for you. "simple enough" means value_type is a POD type + // that contains no pointers. However, we don't try to normalize + // endianness + bool write_nopointer_data(FILE *fp) const { + for ( const_iterator it = begin(); it != end(); ++it ) { + // TODO: skip empty/deleted values + if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; + } + return false; + } + + // When reading, we have to override the potential const-ness of *it + bool read_nopointer_data(FILE *fp) { + for ( iterator it = begin(); it != end(); ++it ) { + // TODO: skip empty/deleted values + if ( !fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp) ) + return false; + } + return false; + } + + private: + template + class alloc_impl : public A { + public: + typedef typename A::pointer pointer; + typedef typename A::size_type size_type; + + // Convert a normal allocator to one that has realloc_or_die() + alloc_impl(const A& a) : A(a) { } + + // realloc_or_die should only be used when using the default + // allocator (libc_allocator_with_realloc). + pointer realloc_or_die(pointer /*ptr*/, size_type /*n*/) { + fprintf(stderr, "realloc_or_die is only supported for " + "libc_allocator_with_realloc"); + exit(1); + return NULL; + } + }; + + // A template specialization of alloc_impl for + // libc_allocator_with_realloc that can handle realloc_or_die. + template + class alloc_impl > + : public libc_allocator_with_realloc { + public: + typedef typename libc_allocator_with_realloc::pointer pointer; + typedef typename libc_allocator_with_realloc::size_type size_type; + + alloc_impl(const libc_allocator_with_realloc& a) + : libc_allocator_with_realloc(a) { } + + pointer realloc_or_die(pointer ptr, size_type n) { + pointer retval = this->reallocate(ptr, n); + if (retval == NULL) { + // We really should use PRIuS here, but I don't want to have to add + // a whole new configure option, with concomitant macro namespace + // pollution, just to print this (unlikely) error message. So I cast. + fprintf(stderr, "sparsehash: FATAL ERROR: failed to reallocate " + "%lu elements for ptr %p", + static_cast(n), ptr); + exit(1); + } + return retval; + } + }; + + // Package allocator with emptyval to eliminate memory needed for + // the zero-size allocator. + // If new fields are added to this class, we should add them to + // operator= and swap. + class ValInfo : public alloc_impl { + public: + typedef typename alloc_impl::value_type value_type; + + ValInfo(const alloc_impl& a) + : alloc_impl(a), emptyval() { } + ValInfo(const ValInfo& v) + : alloc_impl(v), emptyval(v.emptyval) { } + + value_type emptyval; // which key marks unused entries + }; + + + // Package functors with another class to eliminate memory needed for + // zero-size functors. Since ExtractKey and hasher's operator() might + // have the same function signature, they must be packaged in + // different classes. + struct Settings : + sh_hashtable_settings { + explicit Settings(const hasher& hf) + : sh_hashtable_settings( + hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {} + }; + + // Packages ExtractKey and SetKey functors. + class KeyInfo : public ExtractKey, public SetKey, public key_equal { + public: + KeyInfo(const ExtractKey& ek, const SetKey& sk, const key_equal& eq) + : ExtractKey(ek), + SetKey(sk), + key_equal(eq) { + } + + // We want to return the exact same type as ExtractKey: Key or const Key& + typename ExtractKey::result_type get_key(const_reference v) const { + return ExtractKey::operator()(v); + } + void set_key(pointer v, const key_type& k) const { + SetKey::operator()(v, k); + } + bool equals(const key_type& a, const key_type& b) const { + return key_equal::operator()(a, b); + } + + // Which key marks deleted entries. + // TODO(csilvers): make a pointer, and get rid of use_deleted (benchmark!) + typename remove_const::type delkey; + }; + + // Utility functions to access the templated operators + size_type hash(const key_type& v) const { + return settings.hash(v); + } + bool equals(const key_type& a, const key_type& b) const { + return key_info.equals(a, b); + } + typename ExtractKey::result_type get_key(const_reference v) const { + return key_info.get_key(v); + } + void set_key(pointer v, const key_type& k) const { + key_info.set_key(v, k); + } + + private: + // Actual data + Settings settings; + KeyInfo key_info; + + size_type num_deleted; // how many occupied buckets are marked deleted + size_type num_elements; + size_type num_buckets; + ValInfo val_info; // holds emptyval, and also the allocator + pointer table; +}; + + +// We need a global swap as well +template +inline void swap(dense_hashtable &x, + dense_hashtable &y) { + x.swap(y); +} + +#undef JUMP_ + +template +const typename dense_hashtable::size_type + dense_hashtable::ILLEGAL_BUCKET; + +// How full we let the table get before we resize. Knuth says .8 is +// good -- higher causes us to probe too much, though saves memory. +// However, we go with .5, getting better performance at the cost of +// more space (a trade-off densehashtable explicitly chooses to make). +// Feel free to play around with different values, though. +template +const int dense_hashtable::HT_OCCUPANCY_PCT = 50; + +// How empty we let the table get before we resize lower. +// It should be less than OCCUPANCY_PCT / 2 or we thrash resizing +template +const int dense_hashtable::HT_EMPTY_PCT + = static_cast(0.4 * + dense_hashtable::HT_OCCUPANCY_PCT); + +_END_GOOGLE_NAMESPACE_ + +#endif /* _DENSEHASHTABLE_H_ */ diff --git a/external/google/sparsehash/hashtable-common.h b/external/google/sparsehash/hashtable-common.h new file mode 100644 index 0000000000..e823b12429 --- /dev/null +++ b/external/google/sparsehash/hashtable-common.h @@ -0,0 +1,178 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Giao Nguyen + +#ifndef UTIL_GTL_HASHTABLE_COMMON_H_ +#define UTIL_GTL_HASHTABLE_COMMON_H_ + +#include + +// Settings contains parameters for growing and shrinking the table. +// It also packages zero-size functor (ie. hasher). + +template +class sh_hashtable_settings : public HashFunc { + public: + typedef Key key_type; + typedef HashFunc hasher; + typedef SizeType size_type; + + public: + sh_hashtable_settings(const hasher& hf, + const float ht_occupancy_flt, + const float ht_empty_flt) + : hasher(hf), + enlarge_threshold_(0), + shrink_threshold_(0), + consider_shrink_(false), + use_empty_(false), + use_deleted_(false), + num_ht_copies_(0) { + set_enlarge_factor(ht_occupancy_flt); + set_shrink_factor(ht_empty_flt); + } + + size_type hash(const key_type& v) const { + return hasher::operator()(v); + } + + float enlarge_factor() const { + return enlarge_factor_; + } + void set_enlarge_factor(float f) { + enlarge_factor_ = f; + } + float shrink_factor() const { + return shrink_factor_; + } + void set_shrink_factor(float f) { + shrink_factor_ = f; + } + + size_type enlarge_threshold() const { + return enlarge_threshold_; + } + void set_enlarge_threshold(size_type t) { + enlarge_threshold_ = t; + } + size_type shrink_threshold() const { + return shrink_threshold_; + } + void set_shrink_threshold(size_type t) { + shrink_threshold_ = t; + } + + size_type enlarge_size(size_type x) const { + return static_cast(x * enlarge_factor_); + } + size_type shrink_size(size_type x) const { + return static_cast(x * shrink_factor_); + } + + bool consider_shrink() const { + return consider_shrink_; + } + void set_consider_shrink(bool t) { + consider_shrink_ = t; + } + + bool use_empty() const { + return use_empty_; + } + void set_use_empty(bool t) { + use_empty_ = t; + } + + bool use_deleted() const { + return use_deleted_; + } + void set_use_deleted(bool t) { + use_deleted_ = t; + } + + size_type num_ht_copies() const { + return static_cast(num_ht_copies_); + } + void inc_num_ht_copies() { + ++num_ht_copies_; + } + + // Reset the enlarge and shrink thresholds + void reset_thresholds(size_type num_buckets) { + set_enlarge_threshold(enlarge_size(num_buckets)); + set_shrink_threshold(shrink_size(num_buckets)); + // whatever caused us to reset already considered + set_consider_shrink(false); + } + + // Caller is resposible for calling reset_threshold right after + // set_resizing_parameters. + void set_resizing_parameters(float shrink, float grow) { + assert(shrink >= 0.0); + assert(grow <= 1.0); + if (shrink > grow/2.0f) + shrink = grow / 2.0f; // otherwise we thrash hashtable size + set_shrink_factor(shrink); + set_enlarge_factor(grow); + } + + // This is the smallest size a hashtable can be without being too crowded + // If you like, you can give a min #buckets as well as a min #elts + size_type min_buckets(size_type num_elts, size_type min_buckets_wanted) { + float enlarge = enlarge_factor(); + size_type sz = HT_MIN_BUCKETS; // min buckets allowed + while ( sz < min_buckets_wanted || + num_elts >= static_cast(sz * enlarge) ) { + // This just prevents overflowing size_type, since sz can exceed + // max_size() here. + if (static_cast(sz * 2) < sz) { + throw std::length_error("resize overflow"); // protect against overflow + } + sz *= 2; + } + return sz; + } + + private: + size_type enlarge_threshold_; // table.size() * enlarge_factor + size_type shrink_threshold_; // table.size() * shrink_factor + float enlarge_factor_; // how full before resize + float shrink_factor_; // how empty before resize + // consider_shrink=true if we should try to shrink before next insert + bool consider_shrink_; + bool use_empty_; // used only by densehashtable, not sparsehashtable + bool use_deleted_; // false until delkey has been set + // num_ht_copies is a counter incremented every Copy/Move + unsigned int num_ht_copies_; +}; + +#endif // UTIL_GTL_HASHTABLE_COMMON_H_ diff --git a/external/google/sparsehash/libc_allocator_with_realloc.h b/external/google/sparsehash/libc_allocator_with_realloc.h new file mode 100644 index 0000000000..4ba1db48b5 --- /dev/null +++ b/external/google/sparsehash/libc_allocator_with_realloc.h @@ -0,0 +1,121 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Guilin Chen + +#ifndef UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_ +#define UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_ + +#include + +#include // for malloc/realloc/free +#include // for ptrdiff_t + + +_START_GOOGLE_NAMESPACE_ + +template +class libc_allocator_with_realloc { + public: + typedef T value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + + libc_allocator_with_realloc() {} + libc_allocator_with_realloc(const libc_allocator_with_realloc&) {} + ~libc_allocator_with_realloc() {} + + pointer address(reference r) const { return &r; } + const_pointer address(const_reference r) const { return &r; } + + pointer allocate(size_type n, const_pointer = 0) { + return static_cast(malloc(n * sizeof(value_type))); + } + void deallocate(pointer p, size_type) { + free(p); + } + pointer reallocate(pointer p, size_type n) { + return static_cast(realloc(p, n * sizeof(value_type))); + } + + size_type max_size() const { + return static_cast(-1) / sizeof(value_type); + } + + void construct(pointer p, const value_type& val) { + new(p) value_type(val); + } + void destroy(pointer p) { p->~value_type(); } + + template + libc_allocator_with_realloc(const libc_allocator_with_realloc&) {} + + template + struct rebind { + typedef libc_allocator_with_realloc other; + }; +}; + +// libc_allocator_with_realloc specialization. +template<> +class libc_allocator_with_realloc { + public: + typedef void value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef void* pointer; + typedef const void* const_pointer; + + template + struct rebind { + typedef libc_allocator_with_realloc other; + }; +}; + +template +inline bool operator==(const libc_allocator_with_realloc&, + const libc_allocator_with_realloc&) { + return true; +} + +template +inline bool operator!=(const libc_allocator_with_realloc&, + const libc_allocator_with_realloc&) { + return false; +} + +_END_GOOGLE_NAMESPACE_ + +#endif // UTIL_GTL_LIBC_ALLOCATOR_WITH_REALLOC_H_ diff --git a/external/google/sparsehash/os_config.h b/external/google/sparsehash/os_config.h new file mode 100644 index 0000000000..0a397192d3 --- /dev/null +++ b/external/google/sparsehash/os_config.h @@ -0,0 +1,37 @@ +#ifndef _MSC_VER +//non-win version + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if the system has the type `uint16_t'. */ +#define HAVE_UINT16_T 1 + +/* Define to 1 if the system has the type `u_int16_t'. */ +#define HAVE_U_INT16_T 1 + +/* Define to 1 if the system has the type `__uint16'. */ +/* #undef HAVE___UINT16 */ + +#else +//win version + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if the system has the type `uint16_t'. */ +#undef HAVE_UINT16_T + +/* Define to 1 if the system has the type `u_int16_t'. */ +#undef HAVE_U_INT16_T + +/* Define to 1 if the system has the type `__uint16'. */ +#define HAVE___UINT16 1 + +#endif diff --git a/external/google/sparsehash/sparseconfig.h b/external/google/sparsehash/sparseconfig.h new file mode 100644 index 0000000000..fbfa328ae1 --- /dev/null +++ b/external/google/sparsehash/sparseconfig.h @@ -0,0 +1,39 @@ +/* + * NOTE: This file is for internal use only. + * Do not use these #defines in your own program! + */ + +/* Namespace for Google classes */ +#define GOOGLE_NAMESPACE ::google + +/* the location of the header defining hash functions */ +#define HASH_FUN_H + +/* the namespace of the hash<> function */ +#define HASH_NAMESPACE std + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* The system-provided hash function including the namespace. */ +#define SPARSEHASH_HASH HASH_NAMESPACE::hash + +/* The system-provided hash function, in namespace HASH_NAMESPACE. */ +#define SPARSEHASH_HASH_NO_NAMESPACE hash + +/* the namespace where STL code like vector<> is defined */ +#define STL_NAMESPACE std + +/* Stops putting the code inside the Google namespace */ +#define _END_GOOGLE_NAMESPACE_ } + +/* Puts following code inside the Google namespace */ +#define _START_GOOGLE_NAMESPACE_ namespace google { + +#include "os_config.h" diff --git a/external/google/sparsehash/sparseconfig_win.h b/external/google/sparsehash/sparseconfig_win.h new file mode 100644 index 0000000000..a4607139ee --- /dev/null +++ b/external/google/sparsehash/sparseconfig_win.h @@ -0,0 +1,37 @@ +/* + * NOTE: This file is for internal use only. + * Do not use these #defines in your own program! + */ + +/* Namespace for Google classes */ +#define GOOGLE_NAMESPACE ::google + +/* the location of the header defining hash functions */ +#define HASH_FUN_H + +/* the namespace of the hash<> function */ +#define HASH_NAMESPACE std + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* The system-provided hash function including the namespace. */ +#define SPARSEHASH_HASH HASH_NAMESPACE::hash + +/* The system-provided hash function, in namespace HASH_NAMESPACE. */ +#define SPARSEHASH_HASH_NO_NAMESPACE hash + +/* the namespace where STL code like vector<> is defined */ +#define STL_NAMESPACE std + +/* Stops putting the code inside the Google namespace */ +#define _END_GOOGLE_NAMESPACE_ } + +/* Puts following code inside the Google namespace */ +#define _START_GOOGLE_NAMESPACE_ namespace google { diff --git a/external/google/sparsehash/sparsehashtable.h b/external/google/sparsehash/sparsehashtable.h new file mode 100644 index 0000000000..9f2d1443dc --- /dev/null +++ b/external/google/sparsehash/sparsehashtable.h @@ -0,0 +1,1190 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// A sparse hashtable is a particular implementation of +// a hashtable: one that is meant to minimize memory use. +// It does this by using a *sparse table* (cf sparsetable.h), +// which uses between 1 and 2 bits to store empty buckets +// (we may need another bit for hashtables that support deletion). +// +// When empty buckets are so cheap, an appealing hashtable +// implementation is internal probing, in which the hashtable +// is a single table, and collisions are resolved by trying +// to insert again in another bucket. The most cache-efficient +// internal probing schemes are linear probing (which suffers, +// alas, from clumping) and quadratic probing, which is what +// we implement by default. +// +// Deleted buckets are a bit of a pain. We have to somehow mark +// deleted buckets (the probing must distinguish them from empty +// buckets). The most principled way is to have another bitmap, +// but that's annoying and takes up space. Instead we let the +// user specify an "impossible" key. We set deleted buckets +// to have the impossible key. +// +// Note it is possible to change the value of the delete key +// on the fly; you can even remove it, though after that point +// the hashtable is insert_only until you set it again. +// +// You probably shouldn't use this code directly. Use +// or instead. +// +// You can modify the following, below: +// HT_OCCUPANCY_PCT -- how full before we double size +// HT_EMPTY_PCT -- how empty before we halve size +// HT_MIN_BUCKETS -- smallest bucket size +// HT_DEFAULT_STARTING_BUCKETS -- default bucket size at construct-time +// +// You can also change enlarge_factor (which defaults to +// HT_OCCUPANCY_PCT), and shrink_factor (which defaults to +// HT_EMPTY_PCT) with set_resizing_parameters(). +// +// How to decide what values to use? +// shrink_factor's default of .4 * OCCUPANCY_PCT, is probably good. +// HT_MIN_BUCKETS is probably unnecessary since you can specify +// (indirectly) the starting number of buckets at construct-time. +// For enlarge_factor, you can use this chart to try to trade-off +// expected lookup time to the space taken up. By default, this +// code uses quadratic probing, though you can change it to linear +// via _JUMP below if you really want to. +// +// From http://www.augustana.ca/~mohrj/courses/1999.fall/csc210/lecture_notes/hashing.html +// NUMBER OF PROBES / LOOKUP Successful Unsuccessful +// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) +// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 +// +// -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 +// QUADRATIC COLLISION RES. +// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 +// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 +// LINEAR COLLISION RES. +// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 +// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 5000.0 +// +// The value type is required to be copy constructible and default +// constructible, but it need not be (and commonly isn't) assignable. + +#ifndef _SPARSEHASHTABLE_H_ +#define _SPARSEHASHTABLE_H_ + +#ifndef SPARSEHASH_STAT_UPDATE +#define SPARSEHASH_STAT_UPDATE(x) ((void) 0) +#endif + +// The probing method +// Linear probing +// #define JUMP_(key, num_probes) ( 1 ) +// Quadratic probing +#define JUMP_(key, num_probes) ( num_probes ) + +#include +#include +#include // For swap(), eg +#include // For length_error +#include // for facts about iterator tags +#include // for numeric_limits<> +#include // for pair<> +#include +#include // Since that's basically what we are + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::pair; + +// The smaller this is, the faster lookup is (because the group bitmap is +// smaller) and the faster insert is, because there's less to move. +// On the other hand, there are more groups. Since group::size_type is +// a short, this number should be of the form 32*x + 16 to avoid waste. +static const u_int16_t DEFAULT_GROUP_SIZE = 48; // fits in 1.5 words + +// Hashtable class, used to implement the hashed associative containers +// hash_set and hash_map. +// +// Value: what is stored in the table (each bucket is a Value). +// Key: something in a 1-to-1 correspondence to a Value, that can be used +// to search for a Value in the table (find() takes a Key). +// HashFcn: Takes a Key and returns an integer, the more unique the better. +// ExtractKey: given a Value, returns the unique Key associated with it. +// Must inherit from unary_function, or at least have a +// result_type enum indicating the return type of operator(). +// SetKey: given a Value* and a Key, modifies the value such that +// ExtractKey(value) == key. We guarantee this is only called +// with key == deleted_key. +// EqualKey: Given two Keys, says whether they are the same (that is, +// if they are both associated with the same Value). +// Alloc: STL allocator to use to allocate memory. + +template +class sparse_hashtable; + +template +struct sparse_hashtable_iterator; + +template +struct sparse_hashtable_const_iterator; + +// As far as iterating, we're basically just a sparsetable +// that skips over deleted elements. +template +struct sparse_hashtable_iterator { + private: + typedef typename A::template rebind::other value_alloc_type; + + public: + typedef sparse_hashtable_iterator iterator; + typedef sparse_hashtable_const_iterator const_iterator; + typedef typename sparsetable::nonempty_iterator + st_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::pointer pointer; + + // "Real" constructor and default constructor + sparse_hashtable_iterator(const sparse_hashtable *h, + st_iterator it, st_iterator it_end) + : ht(h), pos(it), end(it_end) { advance_past_deleted(); } + sparse_hashtable_iterator() { } // not ever used internally + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on a marked-deleted array element + void advance_past_deleted() { + while ( pos != end && ht->test_deleted(*this) ) + ++pos; + } + iterator& operator++() { + assert(pos != end); ++pos; advance_past_deleted(); return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const iterator& it) const { return pos == it.pos; } + bool operator!=(const iterator& it) const { return pos != it.pos; } + + + // The actual data + const sparse_hashtable *ht; + st_iterator pos, end; +}; + +// Now do it all again, but with const-ness! +template +struct sparse_hashtable_const_iterator { + private: + typedef typename A::template rebind::other value_alloc_type; + + public: + typedef sparse_hashtable_iterator iterator; + typedef sparse_hashtable_const_iterator const_iterator; + typedef typename sparsetable::const_nonempty_iterator + st_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::const_reference reference; + typedef typename value_alloc_type::const_pointer pointer; + + // "Real" constructor and default constructor + sparse_hashtable_const_iterator(const sparse_hashtable *h, + st_iterator it, st_iterator it_end) + : ht(h), pos(it), end(it_end) { advance_past_deleted(); } + // This lets us convert regular iterators to const iterators + sparse_hashtable_const_iterator() { } // never used internally + sparse_hashtable_const_iterator(const iterator &it) + : ht(it.ht), pos(it.pos), end(it.end) { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on a marked-deleted array element + void advance_past_deleted() { + while ( pos != end && ht->test_deleted(*this) ) + ++pos; + } + const_iterator& operator++() { + assert(pos != end); ++pos; advance_past_deleted(); return *this; + } + const_iterator operator++(int) { const_iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const const_iterator& it) const { return pos == it.pos; } + bool operator!=(const const_iterator& it) const { return pos != it.pos; } + + + // The actual data + const sparse_hashtable *ht; + st_iterator pos, end; +}; + +// And once again, but this time freeing up memory as we iterate +template +struct sparse_hashtable_destructive_iterator { + private: + typedef typename A::template rebind::other value_alloc_type; + + public: + typedef sparse_hashtable_destructive_iterator iterator; + typedef typename sparsetable::destructive_iterator + st_iterator; + + typedef STL_NAMESPACE::forward_iterator_tag iterator_category; + typedef V value_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::pointer pointer; + + // "Real" constructor and default constructor + sparse_hashtable_destructive_iterator(const + sparse_hashtable *h, + st_iterator it, st_iterator it_end) + : ht(h), pos(it), end(it_end) { advance_past_deleted(); } + sparse_hashtable_destructive_iterator() { } // never used internally + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *pos; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic. The only hard part is making sure that + // we're not on a marked-deleted array element + void advance_past_deleted() { + while ( pos != end && ht->test_deleted(*this) ) + ++pos; + } + iterator& operator++() { + assert(pos != end); ++pos; advance_past_deleted(); return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + // Comparison. + bool operator==(const iterator& it) const { return pos == it.pos; } + bool operator!=(const iterator& it) const { return pos != it.pos; } + + + // The actual data + const sparse_hashtable *ht; + st_iterator pos, end; +}; + + +template +class sparse_hashtable { + private: + typedef typename Alloc::template rebind::other value_alloc_type; + + public: + typedef Key key_type; + typedef Value value_type; + typedef HashFcn hasher; + typedef EqualKey key_equal; + typedef Alloc allocator_type; + + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::const_reference const_reference; + typedef typename value_alloc_type::pointer pointer; + typedef typename value_alloc_type::const_pointer const_pointer; + typedef sparse_hashtable_iterator + iterator; + + typedef sparse_hashtable_const_iterator + const_iterator; + + typedef sparse_hashtable_destructive_iterator + destructive_iterator; + + // These come from tr1. For us they're the same as regular iterators. + typedef iterator local_iterator; + typedef const_iterator const_local_iterator; + + // How full we let the table get before we resize, by default. + // Knuth says .8 is good -- higher causes us to probe too much, + // though it saves memory. + static const int HT_OCCUPANCY_PCT; // = 80 (out of 100); + + // How empty we let the table get before we resize lower, by default. + // (0.0 means never resize lower.) + // It should be less than OCCUPANCY_PCT / 2 or we thrash resizing + static const int HT_EMPTY_PCT; // = 0.4 * HT_OCCUPANCY_PCT; + + // Minimum size we're willing to let hashtables be. + // Must be a power of two, and at least 4. + // Note, however, that for a given hashtable, the initial size is a + // function of the first constructor arg, and may be >HT_MIN_BUCKETS. + static const size_type HT_MIN_BUCKETS = 4; + + // By default, if you don't specify a hashtable size at + // construction-time, we use this size. Must be a power of two, and + // at least HT_MIN_BUCKETS. + static const size_type HT_DEFAULT_STARTING_BUCKETS = 32; + + // ITERATOR FUNCTIONS + iterator begin() { return iterator(this, table.nonempty_begin(), + table.nonempty_end()); } + iterator end() { return iterator(this, table.nonempty_end(), + table.nonempty_end()); } + const_iterator begin() const { return const_iterator(this, + table.nonempty_begin(), + table.nonempty_end()); } + const_iterator end() const { return const_iterator(this, + table.nonempty_end(), + table.nonempty_end()); } + + // These come from tr1 unordered_map. They iterate over 'bucket' n. + // For sparsehashtable, we could consider each 'group' to be a bucket, + // I guess, but I don't really see the point. We'll just consider + // bucket n to be the n-th element of the sparsetable, if it's occupied, + // or some empty element, otherwise. + local_iterator begin(size_type i) { + if (table.test(i)) + return local_iterator(this, table.get_iter(i), table.nonempty_end()); + else + return local_iterator(this, table.nonempty_end(), table.nonempty_end()); + } + local_iterator end(size_type i) { + local_iterator it = begin(i); + if (table.test(i) && !test_deleted(i)) + ++it; + return it; + } + const_local_iterator begin(size_type i) const { + if (table.test(i)) + return const_local_iterator(this, table.get_iter(i), + table.nonempty_end()); + else + return const_local_iterator(this, table.nonempty_end(), + table.nonempty_end()); + } + const_local_iterator end(size_type i) const { + const_local_iterator it = begin(i); + if (table.test(i) && !test_deleted(i)) + ++it; + return it; + } + + // This is used when resizing + destructive_iterator destructive_begin() { + return destructive_iterator(this, table.destructive_begin(), + table.destructive_end()); + } + destructive_iterator destructive_end() { + return destructive_iterator(this, table.destructive_end(), + table.destructive_end()); + } + + + // ACCESSOR FUNCTIONS for the things we templatize on, basically + hasher hash_funct() const { return settings; } + key_equal key_eq() const { return key_info; } + allocator_type get_allocator() const { return table.get_allocator(); } + + // Accessor function for statistics gathering. + int num_table_copies() const { return settings.num_ht_copies(); } + + private: + // We need to copy values when we set the special marker for deleted + // elements, but, annoyingly, we can't just use the copy assignment + // operator because value_type might not be assignable (it's often + // pair). We use explicit destructor invocation and + // placement new to get around this. Arg. + void set_value(pointer dst, const_reference src) { + dst->~value_type(); // delete the old value, if any + new(dst) value_type(src); + } + + // This is used as a tag for the copy constructor, saying to destroy its + // arg We have two ways of destructively copying: with potentially growing + // the hashtable as we copy, and without. To make sure the outside world + // can't do a destructive copy, we make the typename private. + enum MoveDontCopyT {MoveDontCopy, MoveDontGrow}; + + // DELETE HELPER FUNCTIONS + // This lets the user describe a key that will indicate deleted + // table entries. This key should be an "impossible" entry -- + // if you try to insert it for real, you won't be able to retrieve it! + // (NB: while you pass in an entire value, only the key part is looked + // at. This is just because I don't know how to assign just a key.) + private: + void squash_deleted() { // gets rid of any deleted entries we have + if ( num_deleted ) { // get rid of deleted before writing + sparse_hashtable tmp(MoveDontGrow, *this); + swap(tmp); // now we are tmp + } + assert(num_deleted == 0); + } + + bool test_deleted_key(const key_type& key) const { + // The num_deleted test is crucial for read(): after read(), the ht values + // are garbage, and we don't want to think some of them are deleted. + // Invariant: !use_deleted implies num_deleted is 0. + assert(settings.use_deleted() || num_deleted == 0); + return num_deleted > 0 && equals(key_info.delkey, key); + } + + public: + void set_deleted_key(const key_type &key) { + // It's only safe to change what "deleted" means if we purge deleted guys + squash_deleted(); + settings.set_use_deleted(true); + key_info.delkey = key; + } + void clear_deleted_key() { + squash_deleted(); + settings.set_use_deleted(false); + } + key_type deleted_key() const { + assert(settings.use_deleted() + && "Must set deleted key before calling deleted_key"); + return key_info.delkey; + } + + // These are public so the iterators can use them + // True if the item at position bucknum is "deleted" marker + bool test_deleted(size_type bucknum) const { + if (num_deleted == 0 || !table.test(bucknum)) return false; + return test_deleted_key(get_key(table.unsafe_get(bucknum))); + } + bool test_deleted(const iterator &it) const { + if (!settings.use_deleted()) return false; + return test_deleted_key(get_key(*it)); + } + bool test_deleted(const const_iterator &it) const { + if (!settings.use_deleted()) return false; + return test_deleted_key(get_key(*it)); + } + bool test_deleted(const destructive_iterator &it) const { + if (!settings.use_deleted()) return false; + return test_deleted_key(get_key(*it)); + } + + private: + // Set it so test_deleted is true. true if object didn't used to be deleted. + // TODO(csilvers): make these private (also in densehashtable.h) + bool set_deleted(iterator &it) { + assert(settings.use_deleted()); + bool retval = !test_deleted(it); + // &* converts from iterator to value-type. + set_key(&(*it), key_info.delkey); + return retval; + } + // Set it so test_deleted is false. true if object used to be deleted. + bool clear_deleted(iterator &it) { + assert(settings.use_deleted()); + // Happens automatically when we assign something else in its place. + return test_deleted(it); + } + + // We also allow to set/clear the deleted bit on a const iterator. + // We allow a const_iterator for the same reason you can delete a + // const pointer: it's convenient, and semantically you can't use + // 'it' after it's been deleted anyway, so its const-ness doesn't + // really matter. + bool set_deleted(const_iterator &it) { + assert(settings.use_deleted()); // bad if set_deleted_key() wasn't called + bool retval = !test_deleted(it); + set_key(const_cast(&(*it)), key_info.delkey); + return retval; + } + // Set it so test_deleted is false. true if object used to be deleted. + bool clear_deleted(const_iterator &it) { + assert(settings.use_deleted()); // bad if set_deleted_key() wasn't called + return test_deleted(it); + } + + // FUNCTIONS CONCERNING SIZE + public: + size_type size() const { return table.num_nonempty() - num_deleted; } + size_type max_size() const { return table.max_size(); } + bool empty() const { return size() == 0; } + size_type bucket_count() const { return table.size(); } + size_type max_bucket_count() const { return max_size(); } + // These are tr1 methods. Their idea of 'bucket' doesn't map well to + // what we do. We just say every bucket has 0 or 1 items in it. + size_type bucket_size(size_type i) const { + return begin(i) == end(i) ? 0 : 1; + } + + private: + // Because of the above, size_type(-1) is never legal; use it for errors + static const size_type ILLEGAL_BUCKET = size_type(-1); + + // Used after a string of deletes. Returns true if we actually shrunk. + // TODO(csilvers): take a delta so we can take into account inserts + // done after shrinking. Maybe make part of the Settings class? + bool maybe_shrink() { + assert(table.num_nonempty() >= num_deleted); + assert((bucket_count() & (bucket_count()-1)) == 0); // is a power of two + assert(bucket_count() >= HT_MIN_BUCKETS); + bool retval = false; + + // If you construct a hashtable with < HT_DEFAULT_STARTING_BUCKETS, + // we'll never shrink until you get relatively big, and we'll never + // shrink below HT_DEFAULT_STARTING_BUCKETS. Otherwise, something + // like "dense_hash_set x; x.insert(4); x.erase(4);" will + // shrink us down to HT_MIN_BUCKETS buckets, which is too small. + const size_type num_remain = table.num_nonempty() - num_deleted; + const size_type shrink_threshold = settings.shrink_threshold(); + if (shrink_threshold > 0 && num_remain < shrink_threshold && + bucket_count() > HT_DEFAULT_STARTING_BUCKETS) { + const float shrink_factor = settings.shrink_factor(); + size_type sz = bucket_count() / 2; // find how much we should shrink + while (sz > HT_DEFAULT_STARTING_BUCKETS && + num_remain < static_cast(sz * shrink_factor)) { + sz /= 2; // stay a power of 2 + } + sparse_hashtable tmp(MoveDontCopy, *this, sz); + swap(tmp); // now we are tmp + retval = true; + } + settings.set_consider_shrink(false); // because we just considered it + return retval; + } + + // We'll let you resize a hashtable -- though this makes us copy all! + // When you resize, you say, "make it big enough for this many more elements" + // Returns true if we actually resized, false if size was already ok. + bool resize_delta(size_type delta) { + bool did_resize = false; + if ( settings.consider_shrink() ) { // see if lots of deletes happened + if ( maybe_shrink() ) + did_resize = true; + } + if (table.num_nonempty() >= + (STL_NAMESPACE::numeric_limits::max)() - delta) + throw std::length_error("resize overflow"); + if ( bucket_count() >= HT_MIN_BUCKETS && + (table.num_nonempty() + delta) <= settings.enlarge_threshold() ) + return did_resize; // we're ok as we are + + // Sometimes, we need to resize just to get rid of all the + // "deleted" buckets that are clogging up the hashtable. So when + // deciding whether to resize, count the deleted buckets (which + // are currently taking up room). But later, when we decide what + // size to resize to, *don't* count deleted buckets, since they + // get discarded during the resize. + const size_type needed_size = + settings.min_buckets(table.num_nonempty() + delta, 0); + if ( needed_size <= bucket_count() ) // we have enough buckets + return did_resize; + + size_type resize_to = + settings.min_buckets(table.num_nonempty() - num_deleted + delta, + bucket_count()); + if (resize_to < needed_size && // may double resize_to + resize_to < (STL_NAMESPACE::numeric_limits::max)() / 2) { + // This situation means that we have enough deleted elements, + // that once we purge them, we won't actually have needed to + // grow. But we may want to grow anyway: if we just purge one + // element, say, we'll have to grow anyway next time we + // insert. Might as well grow now, since we're already going + // through the trouble of copying (in order to purge the + // deleted elements). + const size_type target = + static_cast(settings.shrink_size(resize_to*2)); + if (table.num_nonempty() - num_deleted + delta >= target) { + // Good, we won't be below the shrink threshhold even if we double. + resize_to *= 2; + } + } + + sparse_hashtable tmp(MoveDontCopy, *this, resize_to); + swap(tmp); // now we are tmp + return true; + } + + // Used to actually do the rehashing when we grow/shrink a hashtable + void copy_from(const sparse_hashtable &ht, size_type min_buckets_wanted) { + clear(); // clear table, set num_deleted to 0 + + // If we need to change the size of our table, do it now + const size_type resize_to = + settings.min_buckets(ht.size(), min_buckets_wanted); + if ( resize_to > bucket_count() ) { // we don't have enough buckets + table.resize(resize_to); // sets the number of buckets + settings.reset_thresholds(bucket_count()); + } + + // We use a normal iterator to get non-deleted bcks from ht + // We could use insert() here, but since we know there are + // no duplicates and no deleted items, we can be more efficient + assert((bucket_count() & (bucket_count()-1)) == 0); // a power of two + for ( const_iterator it = ht.begin(); it != ht.end(); ++it ) { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + const size_type bucket_count_minus_one = bucket_count() - 1; + for (bucknum = hash(get_key(*it)) & bucket_count_minus_one; + table.test(bucknum); // not empty + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one) { + ++num_probes; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + table.set(bucknum, *it); // copies the value to here + } + settings.inc_num_ht_copies(); + } + + // Implementation is like copy_from, but it destroys the table of the + // "from" guy by freeing sparsetable memory as we iterate. This is + // useful in resizing, since we're throwing away the "from" guy anyway. + void move_from(MoveDontCopyT mover, sparse_hashtable &ht, + size_type min_buckets_wanted) { + clear(); // clear table, set num_deleted to 0 + + // If we need to change the size of our table, do it now + size_type resize_to; + if ( mover == MoveDontGrow ) + resize_to = ht.bucket_count(); // keep same size as old ht + else // MoveDontCopy + resize_to = settings.min_buckets(ht.size(), min_buckets_wanted); + if ( resize_to > bucket_count() ) { // we don't have enough buckets + table.resize(resize_to); // sets the number of buckets + settings.reset_thresholds(bucket_count()); + } + + // We use a normal iterator to get non-deleted bcks from ht + // We could use insert() here, but since we know there are + // no duplicates and no deleted items, we can be more efficient + assert( (bucket_count() & (bucket_count()-1)) == 0); // a power of two + // THIS IS THE MAJOR LINE THAT DIFFERS FROM COPY_FROM(): + for ( destructive_iterator it = ht.destructive_begin(); + it != ht.destructive_end(); ++it ) { + size_type num_probes = 0; // how many times we've probed + size_type bucknum; + for ( bucknum = hash(get_key(*it)) & (bucket_count()-1); // h % buck_cnt + table.test(bucknum); // not empty + bucknum = (bucknum + JUMP_(key, num_probes)) & (bucket_count()-1) ) { + ++num_probes; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + table.set(bucknum, *it); // copies the value to here + } + settings.inc_num_ht_copies(); + } + + + // Required by the spec for hashed associative container + public: + // Though the docs say this should be num_buckets, I think it's much + // more useful as num_elements. As a special feature, calling with + // req_elements==0 will cause us to shrink if we can, saving space. + void resize(size_type req_elements) { // resize to this or larger + if ( settings.consider_shrink() || req_elements == 0 ) + maybe_shrink(); + if ( req_elements > table.num_nonempty() ) // we only grow + resize_delta(req_elements - table.num_nonempty()); + } + + // Get and change the value of shrink_factor and enlarge_factor. The + // description at the beginning of this file explains how to choose + // the values. Setting the shrink parameter to 0.0 ensures that the + // table never shrinks. + void get_resizing_parameters(float* shrink, float* grow) const { + *shrink = settings.shrink_factor(); + *grow = settings.enlarge_factor(); + } + void set_resizing_parameters(float shrink, float grow) { + settings.set_resizing_parameters(shrink, grow); + settings.reset_thresholds(bucket_count()); + } + + // CONSTRUCTORS -- as required by the specs, we take a size, + // but also let you specify a hashfunction, key comparator, + // and key extractor. We also define a copy constructor and =. + // DESTRUCTOR -- the default is fine, surprisingly. + explicit sparse_hashtable(size_type expected_max_items_in_table = 0, + const HashFcn& hf = HashFcn(), + const EqualKey& eql = EqualKey(), + const ExtractKey& ext = ExtractKey(), + const SetKey& set = SetKey(), + const Alloc& alloc = Alloc()) + : settings(hf), + key_info(ext, set, eql), + num_deleted(0), + table((expected_max_items_in_table == 0 + ? HT_DEFAULT_STARTING_BUCKETS + : settings.min_buckets(expected_max_items_in_table, 0)), + alloc) { + settings.reset_thresholds(bucket_count()); + } + + // As a convenience for resize(), we allow an optional second argument + // which lets you make this new hashtable a different size than ht. + // We also provide a mechanism of saying you want to "move" the ht argument + // into us instead of copying. + sparse_hashtable(const sparse_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : settings(ht.settings), + key_info(ht.key_info), + num_deleted(0), + table(0, ht.get_allocator()) { + settings.reset_thresholds(bucket_count()); + copy_from(ht, min_buckets_wanted); // copy_from() ignores deleted entries + } + sparse_hashtable(MoveDontCopyT mover, sparse_hashtable& ht, + size_type min_buckets_wanted = HT_DEFAULT_STARTING_BUCKETS) + : settings(ht.settings), + key_info(ht.key_info), + num_deleted(0), + table(0, ht.get_allocator()) { + settings.reset_thresholds(bucket_count()); + move_from(mover, ht, min_buckets_wanted); // ignores deleted entries + } + + sparse_hashtable& operator= (const sparse_hashtable& ht) { + if (&ht == this) return *this; // don't copy onto ourselves + settings = ht.settings; + key_info = ht.key_info; + num_deleted = ht.num_deleted; + // copy_from() calls clear and sets num_deleted to 0 too + copy_from(ht, HT_MIN_BUCKETS); + // we purposefully don't copy the allocator, which may not be copyable + return *this; + } + + // Many STL algorithms use swap instead of copy constructors + void swap(sparse_hashtable& ht) { + STL_NAMESPACE::swap(settings, ht.settings); + STL_NAMESPACE::swap(key_info, ht.key_info); + STL_NAMESPACE::swap(num_deleted, ht.num_deleted); + table.swap(ht.table); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + if (!empty() || (num_deleted != 0)) { + table.clear(); + } + settings.reset_thresholds(bucket_count()); + num_deleted = 0; + } + + // LOOKUP ROUTINES + private: + // Returns a pair of positions: 1st where the object is, 2nd where + // it would go if you wanted to insert it. 1st is ILLEGAL_BUCKET + // if object is not found; 2nd is ILLEGAL_BUCKET if it is. + // Note: because of deletions where-to-insert is not trivial: it's the + // first deleted bucket we see, as long as we don't find the key later + pair find_position(const key_type &key) const { + size_type num_probes = 0; // how many times we've probed + const size_type bucket_count_minus_one = bucket_count() - 1; + size_type bucknum = hash(key) & bucket_count_minus_one; + size_type insert_pos = ILLEGAL_BUCKET; // where we would insert + SPARSEHASH_STAT_UPDATE(total_lookups += 1); + while ( 1 ) { // probe until something happens + if ( !table.test(bucknum) ) { // bucket is empty + SPARSEHASH_STAT_UPDATE(total_probes += num_probes); + if ( insert_pos == ILLEGAL_BUCKET ) // found no prior place to insert + return pair(ILLEGAL_BUCKET, bucknum); + else + return pair(ILLEGAL_BUCKET, insert_pos); + + } else if ( test_deleted(bucknum) ) {// keep searching, but mark to insert + if ( insert_pos == ILLEGAL_BUCKET ) + insert_pos = bucknum; + + } else if ( equals(key, get_key(table.unsafe_get(bucknum))) ) { + SPARSEHASH_STAT_UPDATE(total_probes += num_probes); + return pair(bucknum, ILLEGAL_BUCKET); + } + ++num_probes; // we're doing another probe + bucknum = (bucknum + JUMP_(key, num_probes)) & bucket_count_minus_one; + assert(num_probes < bucket_count() + && "Hashtable is full: an error in key_equal<> or hash<>"); + } + } + + public: + iterator find(const key_type& key) { + if ( size() == 0 ) return end(); + pair pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return iterator(this, table.get_iter(pos.first), table.nonempty_end()); + } + + const_iterator find(const key_type& key) const { + if ( size() == 0 ) return end(); + pair pos = find_position(key); + if ( pos.first == ILLEGAL_BUCKET ) // alas, not there + return end(); + else + return const_iterator(this, + table.get_iter(pos.first), table.nonempty_end()); + } + + // This is a tr1 method: the bucket a given key is in, or what bucket + // it would be put in, if it were to be inserted. Shrug. + size_type bucket(const key_type& key) const { + pair pos = find_position(key); + return pos.first == ILLEGAL_BUCKET ? pos.second : pos.first; + } + + // Counts how many elements have key key. For maps, it's either 0 or 1. + size_type count(const key_type &key) const { + pair pos = find_position(key); + return pos.first == ILLEGAL_BUCKET ? 0 : 1; + } + + // Likewise, equal_range doesn't really make sense for us. Oh well. + pair equal_range(const key_type& key) { + iterator pos = find(key); // either an iterator or end + if (pos == end()) { + return pair(pos, pos); + } else { + const iterator startpos = pos++; + return pair(startpos, pos); + } + } + pair equal_range(const key_type& key) const { + const_iterator pos = find(key); // either an iterator or end + if (pos == end()) { + return pair(pos, pos); + } else { + const const_iterator startpos = pos++; + return pair(startpos, pos); + } + } + + + // INSERTION ROUTINES + private: + // Private method used by insert_noresize and find_or_insert. + iterator insert_at(const_reference obj, size_type pos) { + if (size() >= max_size()) + throw std::length_error("insert overflow"); + if ( test_deleted(pos) ) { // just replace if it's been deleted + // The set() below will undelete this object. We just worry about stats + assert(num_deleted > 0); + --num_deleted; // used to be, now it isn't + } + table.set(pos, obj); + return iterator(this, table.get_iter(pos), table.nonempty_end()); + } + + // If you know *this is big enough to hold obj, use this routine + pair insert_noresize(const_reference obj) { + // First, double-check we're not inserting delkey + assert((!settings.use_deleted() || !equals(get_key(obj), key_info.delkey)) + && "Inserting the deleted key"); + const pair pos = find_position(get_key(obj)); + if ( pos.first != ILLEGAL_BUCKET) { // object was already there + return pair(iterator(this, table.get_iter(pos.first), + table.nonempty_end()), + false); // false: we didn't insert + } else { // pos.second says where to put it + return pair(insert_at(obj, pos.second), true); + } + } + + // Specializations of insert(it, it) depending on the power of the iterator: + // (1) Iterator supports operator-, resize before inserting + template + void insert(ForwardIterator f, ForwardIterator l, STL_NAMESPACE::forward_iterator_tag) { + size_t dist = STL_NAMESPACE::distance(f, l); + if (dist >= (std::numeric_limits::max)()) + throw std::length_error("insert-range overflow"); + resize_delta(static_cast(dist)); + for ( ; dist > 0; --dist, ++f) { + insert_noresize(*f); + } + } + + // (2) Arbitrary iterator, can't tell how much to resize + template + void insert(InputIterator f, InputIterator l, STL_NAMESPACE::input_iterator_tag) { + for ( ; f != l; ++f) + insert(*f); + } + + public: + // This is the normal insert routine, used by the outside world + pair insert(const_reference obj) { + resize_delta(1); // adding an object, grow if need be + return insert_noresize(obj); + } + + // When inserting a lot at a time, we specialize on the type of iterator + template + void insert(InputIterator f, InputIterator l) { + // specializes on iterator type + insert(f, l, typename STL_NAMESPACE::iterator_traits::iterator_category()); + } + + // DefaultValue is a functor that takes a key and returns a value_type + // representing the default value to be inserted if none is found. + template + value_type& find_or_insert(const key_type& key) { + // First, double-check we're not inserting delkey + assert((!settings.use_deleted() || !equals(key, key_info.delkey)) + && "Inserting the deleted key"); + const pair pos = find_position(key); + DefaultValue default_value; + if ( pos.first != ILLEGAL_BUCKET) { // object was already there + return *table.get_iter(pos.first); + } else if (resize_delta(1)) { // needed to rehash to make room + // Since we resized, we can't use pos, so recalculate where to insert. + return *insert_noresize(default_value(key)).first; + } else { // no need to rehash, insert right here + return *insert_at(default_value(key), pos.second); + } + } + + // DELETION ROUTINES + size_type erase(const key_type& key) { + // First, double-check we're not erasing delkey. + assert((!settings.use_deleted() || !equals(key, key_info.delkey)) + && "Erasing the deleted key"); + assert(!settings.use_deleted() || !equals(key, key_info.delkey)); + const_iterator pos = find(key); // shrug: shouldn't need to be const + if ( pos != end() ) { + assert(!test_deleted(pos)); // or find() shouldn't have returned it + set_deleted(pos); + ++num_deleted; + // will think about shrink after next insert + settings.set_consider_shrink(true); + return 1; // because we deleted one thing + } else { + return 0; // because we deleted nothing + } + } + + // We return the iterator past the deleted item. + void erase(iterator pos) { + if ( pos == end() ) return; // sanity check + if ( set_deleted(pos) ) { // true if object has been newly deleted + ++num_deleted; + // will think about shrink after next insert + settings.set_consider_shrink(true); + } + } + + void erase(iterator f, iterator l) { + for ( ; f != l; ++f) { + if ( set_deleted(f) ) // should always be true + ++num_deleted; + } + // will think about shrink after next insert + settings.set_consider_shrink(true); + } + + // We allow you to erase a const_iterator just like we allow you to + // erase an iterator. This is in parallel to 'delete': you can delete + // a const pointer just like a non-const pointer. The logic is that + // you can't use the object after it's erased anyway, so it doesn't matter + // if it's const or not. + void erase(const_iterator pos) { + if ( pos == end() ) return; // sanity check + if ( set_deleted(pos) ) { // true if object has been newly deleted + ++num_deleted; + // will think about shrink after next insert + settings.set_consider_shrink(true); + } + } + void erase(const_iterator f, const_iterator l) { + for ( ; f != l; ++f) { + if ( set_deleted(f) ) // should always be true + ++num_deleted; + } + // will think about shrink after next insert + settings.set_consider_shrink(true); + } + + + // COMPARISON + bool operator==(const sparse_hashtable& ht) const { + if (size() != ht.size()) { + return false; + } else if (this == &ht) { + return true; + } else { + // Iterate through the elements in "this" and see if the + // corresponding element is in ht + for ( const_iterator it = begin(); it != end(); ++it ) { + const_iterator it2 = ht.find(get_key(*it)); + if ((it2 == ht.end()) || (*it != *it2)) { + return false; + } + } + return true; + } + } + bool operator!=(const sparse_hashtable& ht) const { + return !(*this == ht); + } + + + // I/O + // We support reading and writing hashtables to disk. NOTE that + // this only stores the hashtable metadata, not the stuff you've + // actually put in the hashtable! Alas, since I don't know how to + // write a hasher or key_equal, you have to make sure everything + // but the table is the same. We compact before writing. + bool write_metadata(FILE *fp) { + squash_deleted(); // so we don't have to worry about delkey + return table.write_metadata(fp); + } + + bool read_metadata(FILE *fp) { + num_deleted = 0; // since we got rid before writing + bool result = table.read_metadata(fp); + settings.reset_thresholds(bucket_count()); + return result; + } + + // Only meaningful if value_type is a POD. + bool write_nopointer_data(FILE *fp) { + return table.write_nopointer_data(fp); + } + + // Only meaningful if value_type is a POD. + bool read_nopointer_data(FILE *fp) { + return table.read_nopointer_data(fp); + } + + private: + // Table is the main storage class. + typedef sparsetable Table; + + // Package templated functors with the other types to eliminate memory + // needed for storing these zero-size operators. Since ExtractKey and + // hasher's operator() might have the same function signature, they + // must be packaged in different classes. + struct Settings : + sh_hashtable_settings { + explicit Settings(const hasher& hf) + : sh_hashtable_settings( + hf, HT_OCCUPANCY_PCT / 100.0f, HT_EMPTY_PCT / 100.0f) {} + }; + + // KeyInfo stores delete key and packages zero-size functors: + // ExtractKey and SetKey. + class KeyInfo : public ExtractKey, public SetKey, public key_equal { + public: + KeyInfo(const ExtractKey& ek, const SetKey& sk, const key_equal& eq) + : ExtractKey(ek), + SetKey(sk), + key_equal(eq) { + } + // We want to return the exact same type as ExtractKey: Key or const Key& + typename ExtractKey::result_type get_key(const_reference v) const { + return ExtractKey::operator()(v); + } + void set_key(pointer v, const key_type& k) const { + SetKey::operator()(v, k); + } + bool equals(const key_type& a, const key_type& b) const { + return key_equal::operator()(a, b); + } + + // Which key marks deleted entries. + // TODO(csilvers): make a pointer, and get rid of use_deleted (benchmark!) + typename remove_const::type delkey; + }; + + // Utility functions to access the templated operators + size_type hash(const key_type& v) const { + return settings.hash(v); + } + bool equals(const key_type& a, const key_type& b) const { + return key_info.equals(a, b); + } + typename ExtractKey::result_type get_key(const_reference v) const { + return key_info.get_key(v); + } + void set_key(pointer v, const key_type& k) const { + key_info.set_key(v, k); + } + + private: + // Actual data + Settings settings; + KeyInfo key_info; + size_type num_deleted; // how many occupied buckets are marked deleted + Table table; // holds num_buckets and num_elements too +}; + + +// We need a global swap as well +template +inline void swap(sparse_hashtable &x, + sparse_hashtable &y) { + x.swap(y); +} + +#undef JUMP_ + +template +const typename sparse_hashtable::size_type + sparse_hashtable::ILLEGAL_BUCKET; + +// How full we let the table get before we resize. Knuth says .8 is +// good -- higher causes us to probe too much, though saves memory +template +const int sparse_hashtable::HT_OCCUPANCY_PCT = 80; + +// How empty we let the table get before we resize lower. +// It should be less than OCCUPANCY_PCT / 2 or we thrash resizing +template +const int sparse_hashtable::HT_EMPTY_PCT + = static_cast(0.4 * + sparse_hashtable::HT_OCCUPANCY_PCT); + +_END_GOOGLE_NAMESPACE_ + +#endif /* _SPARSEHASHTABLE_H_ */ diff --git a/external/google/sparsetable b/external/google/sparsetable new file mode 100644 index 0000000000..aa4ccab466 --- /dev/null +++ b/external/google/sparsetable @@ -0,0 +1,1598 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Craig Silverstein +// +// A sparsetable is a random container that implements a sparse array, +// that is, an array that uses very little memory to store unassigned +// indices (in this case, between 1-2 bits per unassigned index). For +// instance, if you allocate an array of size 5 and assign a[2] = , then a[2] will take up a lot of memory but a[0], a[1], +// a[3], and a[4] will not. Array elements that have a value are +// called "assigned". Array elements that have no value yet, or have +// had their value cleared using erase() or clear(), are called +// "unassigned". +// +// Unassigned values seem to have the default value of T (see below). +// Nevertheless, there is a difference between an unassigned index and +// one explicitly assigned the value of T(). The latter is considered +// assigned. +// +// Access to an array element is constant time, as is insertion and +// deletion. Insertion and deletion may be fairly slow, however: +// because of this container's memory economy, each insert and delete +// causes a memory reallocation. +// +// See doc/sparsetable.html for information about how to use this class. + +#ifndef _SPARSETABLE_H_ +#define _SPARSETABLE_H_ + +#include +#include // for malloc/free +#include // to read/write tables +#ifdef HAVE_STDINT_H +#include // the normal place uint16_t is defined +#endif +#ifdef HAVE_SYS_TYPES_H +#include // the normal place u_int16_t is defined +#endif +#ifdef HAVE_INTTYPES_H +#include // a third place for uint16_t or u_int16_t +#endif +#include // for bounds checking +#include // to define reverse_iterator for me +#include // equal, lexicographical_compare, swap,... +#include // uninitialized_copy +#include // a sparsetable is a vector of groups +#include +#include // for true_type, integral_constant, etc. + +#if STDC_HEADERS +#include // for memcpy +#else +#if !HAVE_MEMCPY +#define memcpy(d, s, n) bcopy ((s), (d), (n)) +#endif +#endif + +#ifndef HAVE_U_INT16_T +# if defined HAVE_UINT16_T + typedef uint16_t u_int16_t; // true on solaris, possibly other C99 libc's +# elif defined HAVE___UINT16 + typedef __int16 int16_t; // true on vc++7 + typedef unsigned __int16 u_int16_t; +# else + // Cannot find a 16-bit integer type. Hoping for the best with "short"... + typedef short int int16_t; + typedef unsigned short int u_int16_t; +# endif +#endif + +_START_GOOGLE_NAMESPACE_ + +using STL_NAMESPACE::vector; +using STL_NAMESPACE::uninitialized_copy; + +// The smaller this is, the faster lookup is (because the group bitmap is +// smaller) and the faster insert is, because there's less to move. +// On the other hand, there are more groups. Since group::size_type is +// a short, this number should be of the form 32*x + 16 to avoid waste. +static const u_int16_t DEFAULT_SPARSEGROUP_SIZE = 48; // fits in 1.5 words + + +// A NOTE ON ASSIGNING: +// A sparse table does not actually allocate memory for entries +// that are not filled. Because of this, it becomes complicated +// to have a non-const iterator: we don't know, if the iterator points +// to a not-filled bucket, whether you plan to fill it with something +// or whether you plan to read its value (in which case you'll get +// the default bucket value). Therefore, while we can define const +// operations in a pretty 'normal' way, for non-const operations, we +// define something that returns a helper object with operator= and +// operator& that allocate a bucket lazily. We use this for table[] +// and also for regular table iterators. + +template +class table_element_adaptor { + public: + typedef typename tabletype::value_type value_type; + typedef typename tabletype::size_type size_type; + typedef typename tabletype::reference reference; + typedef typename tabletype::pointer pointer; + + table_element_adaptor(tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + table_element_adaptor& operator= (const value_type &val) { + table->set(pos, val); + return *this; + } + operator value_type() { return table->get(pos); } // we look like a value + pointer operator& () { return &table->mutating_get(pos); } + + private: + tabletype* table; + size_type pos; +}; + +// Our iterator as simple as iterators can be: basically it's just +// the index into our table. Dereference, the only complicated +// thing, we punt to the table class. This just goes to show how +// much machinery STL requires to do even the most trivial tasks. +// +// By templatizing over tabletype, we have one iterator type which +// we can use for both sparsetables and sparsebins. In fact it +// works on any class that allows size() and operator[] (eg vector), +// as long as it does the standard STL typedefs too (eg value_type). + +template +class table_iterator { + public: + typedef table_iterator iterator; + + typedef STL_NAMESPACE::random_access_iterator_tag iterator_category; + typedef typename tabletype::value_type value_type; + typedef typename tabletype::difference_type difference_type; + typedef typename tabletype::size_type size_type; + typedef table_element_adaptor reference; + typedef table_element_adaptor* pointer; + + // The "real" constructor + table_iterator(tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + // The default constructor, used when I define vars of type table::iterator + table_iterator() : table(NULL), pos(0) { } + // The copy constructor, for when I say table::iterator foo = tbl.begin() + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // The main thing our iterator does is dereference. If the table entry + // we point to is empty, we return the default value type. + // This is the big different function from the const iterator. + reference operator*() { + return table_element_adaptor(table, pos); + } + pointer operator->() { return &(operator*()); } + + // Helper function to assert things are ok; eg pos is still in range + void check() const { + assert(table); + assert(pos <= table->size()); + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + iterator& operator+=(size_type t) { pos += t; check(); return *this; } + iterator& operator-=(size_type t) { pos -= t; check(); return *this; } + iterator& operator++() { ++pos; check(); return *this; } + iterator& operator--() { --pos; check(); return *this; } + iterator operator++(int) { iterator tmp(*this); // for x++ + ++pos; check(); return tmp; } + iterator operator--(int) { iterator tmp(*this); // for x-- + --pos; check(); return tmp; } + iterator operator+(difference_type i) const { iterator tmp(*this); + tmp += i; return tmp; } + iterator operator-(difference_type i) const { iterator tmp(*this); + tmp -= i; return tmp; } + difference_type operator-(iterator it) const { // for "x = it2 - it" + assert(table == it.table); + return pos - it.pos; + } + reference operator[](difference_type n) const { + return *(*this + n); // simple though not totally efficient + } + + // Comparisons. + bool operator==(const iterator& it) const { + return table == it.table && pos == it.pos; + } + bool operator<(const iterator& it) const { + assert(table == it.table); // life is bad bad bad otherwise + return pos < it.pos; + } + bool operator!=(const iterator& it) const { return !(*this == it); } + bool operator<=(const iterator& it) const { return !(it < *this); } + bool operator>(const iterator& it) const { return it < *this; } + bool operator>=(const iterator& it) const { return !(*this < it); } + + // Here's the info we actually need to be an iterator + tabletype *table; // so we can dereference and bounds-check + size_type pos; // index into the table +}; + +// support for "3 + iterator" has to be defined outside the class, alas +template +table_iterator operator+(typename table_iterator::difference_type i, + table_iterator it) { + return it + i; // so people can say it2 = 3 + it +} + +template +class const_table_iterator { + public: + typedef table_iterator iterator; + typedef const_table_iterator const_iterator; + + typedef STL_NAMESPACE::random_access_iterator_tag iterator_category; + typedef typename tabletype::value_type value_type; + typedef typename tabletype::difference_type difference_type; + typedef typename tabletype::size_type size_type; + typedef typename tabletype::const_reference reference; // we're const-only + typedef typename tabletype::const_pointer pointer; + + // The "real" constructor + const_table_iterator(const tabletype *tbl, size_type p) + : table(tbl), pos(p) { } + // The default constructor, used when I define vars of type table::iterator + const_table_iterator() : table(NULL), pos(0) { } + // The copy constructor, for when I say table::iterator foo = tbl.begin() + // Also converts normal iterators to const iterators + const_table_iterator(const iterator &from) + : table(from.table), pos(from.pos) { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // The main thing our iterator does is dereference. If the table entry + // we point to is empty, we return the default value type. + reference operator*() const { return (*table)[pos]; } + pointer operator->() const { return &(operator*()); } + + // Helper function to assert things are ok; eg pos is still in range + void check() const { + assert(table); + assert(pos <= table->size()); + } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + const_iterator& operator+=(size_type t) { pos += t; check(); return *this; } + const_iterator& operator-=(size_type t) { pos -= t; check(); return *this; } + const_iterator& operator++() { ++pos; check(); return *this; } + const_iterator& operator--() { --pos; check(); return *this; } + const_iterator operator++(int) { const_iterator tmp(*this); // for x++ + ++pos; check(); return tmp; } + const_iterator operator--(int) { const_iterator tmp(*this); // for x-- + --pos; check(); return tmp; } + const_iterator operator+(difference_type i) const { const_iterator tmp(*this); + tmp += i; return tmp; } + const_iterator operator-(difference_type i) const { const_iterator tmp(*this); + tmp -= i; return tmp; } + difference_type operator-(const_iterator it) const { // for "x = it2 - it" + assert(table == it.table); + return pos - it.pos; + } + reference operator[](difference_type n) const { + return *(*this + n); // simple though not totally efficient + } + + // Comparisons. + bool operator==(const const_iterator& it) const { + return table == it.table && pos == it.pos; + } + bool operator<(const const_iterator& it) const { + assert(table == it.table); // life is bad bad bad otherwise + return pos < it.pos; + } + bool operator!=(const const_iterator& it) const { return !(*this == it); } + bool operator<=(const const_iterator& it) const { return !(it < *this); } + bool operator>(const const_iterator& it) const { return it < *this; } + bool operator>=(const const_iterator& it) const { return !(*this < it); } + + // Here's the info we actually need to be an iterator + const tabletype *table; // so we can dereference and bounds-check + size_type pos; // index into the table +}; + +// support for "3 + iterator" has to be defined outside the class, alas +template +const_table_iterator operator+(typename + const_table_iterator::difference_type i, + const_table_iterator it) { + return it + i; // so people can say it2 = 3 + it +} + + +// --------------------------------------------------------------------------- + + +/* +// This is a 2-D iterator. You specify a begin and end over a list +// of *containers*. We iterate over each container by iterating over +// it. It's actually simple: +// VECTOR.begin() VECTOR[0].begin() --------> VECTOR[0].end() ---, +// | ________________________________________________/ +// | \_> VECTOR[1].begin() --------> VECTOR[1].end() -, +// | ___________________________________________________/ +// v \_> ...... +// VECTOR.end() +// +// It's impossible to do random access on one of these things in constant +// time, so it's just a bidirectional iterator. +// +// Unfortunately, because we need to use this for a non-empty iterator, +// we use nonempty_begin() and nonempty_end() instead of begin() and end() +// (though only going across, not down). +*/ + +#define TWOD_BEGIN_ nonempty_begin +#define TWOD_END_ nonempty_end +#define TWOD_ITER_ nonempty_iterator +#define TWOD_CONST_ITER_ const_nonempty_iterator + +template +class two_d_iterator { + public: + typedef two_d_iterator iterator; + + typedef STL_NAMESPACE::bidirectional_iterator_tag iterator_category; + // apparently some versions of VC++ have trouble with two ::'s in a typename + typedef typename containertype::value_type _tmp_vt; + typedef typename _tmp_vt::value_type value_type; + typedef typename _tmp_vt::difference_type difference_type; + typedef typename _tmp_vt::reference reference; + typedef typename _tmp_vt::pointer pointer; + + // The "real" constructor. begin and end specify how many rows we have + // (in the diagram above); we always iterate over each row completely. + two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr) + : row_begin(begin), row_end(end), row_current(curr), col_current() { + if ( row_current != row_end ) { + col_current = row_current->TWOD_BEGIN_(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + // If you want to start at an arbitrary place, you can, I guess + two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr, + typename containertype::value_type::TWOD_ITER_ col) + : row_begin(begin), row_end(end), row_current(curr), col_current(col) { + advance_past_end(); // in case cur->begin() == cur->end() + } + // The default constructor, used when I define vars of type table::iterator + two_d_iterator() : row_begin(), row_end(), row_current(), col_current() { } + // The default destructor is fine; we don't define one + // The default operator= is fine; we don't define one + + // Happy dereferencer + reference operator*() const { return *col_current; } + pointer operator->() const { return &(operator*()); } + + // Arithmetic: we just do arithmetic on pos. We don't even need to + // do bounds checking, since STL doesn't consider that its job. :-) + // NOTE: this is not amortized constant time! What do we do about it? + void advance_past_end() { // used when col_current points to end() + while ( col_current == row_current->TWOD_END_() ) { // end of current row + ++row_current; // go to beginning of next + if ( row_current != row_end ) // col is irrelevant at end + col_current = row_current->TWOD_BEGIN_(); + else + break; // don't go past row_end + } + } + + iterator& operator++() { + assert(row_current != row_end); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + iterator& operator--() { + while ( row_current == row_end || + col_current == row_current->TWOD_BEGIN_() ) { + assert(row_current != row_begin); + --row_current; + col_current = row_current->TWOD_END_(); // this is 1 too far + } + --col_current; + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } + + + // Comparisons. + bool operator==(const iterator& it) const { + return ( row_begin == it.row_begin && + row_end == it.row_end && + row_current == it.row_current && + (row_current == row_end || col_current == it.col_current) ); + } + bool operator!=(const iterator& it) const { return !(*this == it); } + + + // Here's the info we actually need to be an iterator + // These need to be public so we convert from iterator to const_iterator + typename containertype::iterator row_begin, row_end, row_current; + typename containertype::value_type::TWOD_ITER_ col_current; +}; + +// The same thing again, but this time const. :-( +template +class const_two_d_iterator { + public: + typedef const_two_d_iterator iterator; + + typedef STL_NAMESPACE::bidirectional_iterator_tag iterator_category; + // apparently some versions of VC++ have trouble with two ::'s in a typename + typedef typename containertype::value_type _tmp_vt; + typedef typename _tmp_vt::value_type value_type; + typedef typename _tmp_vt::difference_type difference_type; + typedef typename _tmp_vt::const_reference reference; + typedef typename _tmp_vt::const_pointer pointer; + + const_two_d_iterator(typename containertype::const_iterator begin, + typename containertype::const_iterator end, + typename containertype::const_iterator curr) + : row_begin(begin), row_end(end), row_current(curr), col_current() { + if ( curr != end ) { + col_current = curr->TWOD_BEGIN_(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + const_two_d_iterator(typename containertype::const_iterator begin, + typename containertype::const_iterator end, + typename containertype::const_iterator curr, + typename containertype::value_type::TWOD_CONST_ITER_ col) + : row_begin(begin), row_end(end), row_current(curr), col_current(col) { + advance_past_end(); // in case cur->begin() == cur->end() + } + const_two_d_iterator() + : row_begin(), row_end(), row_current(), col_current() { + } + // Need this explicitly so we can convert normal iterators to const iterators + const_two_d_iterator(const two_d_iterator& it) : + row_begin(it.row_begin), row_end(it.row_end), row_current(it.row_current), + col_current(it.col_current) { } + + typename containertype::const_iterator row_begin, row_end, row_current; + typename containertype::value_type::TWOD_CONST_ITER_ col_current; + + + // EVERYTHING FROM HERE DOWN IS THE SAME AS THE NON-CONST ITERATOR + reference operator*() const { return *col_current; } + pointer operator->() const { return &(operator*()); } + + void advance_past_end() { // used when col_current points to end() + while ( col_current == row_current->TWOD_END_() ) { // end of current row + ++row_current; // go to beginning of next + if ( row_current != row_end ) // col is irrelevant at end + col_current = row_current->TWOD_BEGIN_(); + else + break; // don't go past row_end + } + } + iterator& operator++() { + assert(row_current != row_end); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + iterator& operator--() { + while ( row_current == row_end || + col_current == row_current->TWOD_BEGIN_() ) { + assert(row_current != row_begin); + --row_current; + col_current = row_current->TWOD_END_(); // this is 1 too far + } + --col_current; + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + iterator operator--(int) { iterator tmp(*this); --*this; return tmp; } + + bool operator==(const iterator& it) const { + return ( row_begin == it.row_begin && + row_end == it.row_end && + row_current == it.row_current && + (row_current == row_end || col_current == it.col_current) ); + } + bool operator!=(const iterator& it) const { return !(*this == it); } +}; + +// We provide yet another version, to be as frugal with memory as +// possible. This one frees each block of memory as it finishes +// iterating over it. By the end, the entire table is freed. +// For understandable reasons, you can only iterate over it once, +// which is why it's an input iterator +template +class destructive_two_d_iterator { + public: + typedef destructive_two_d_iterator iterator; + + typedef STL_NAMESPACE::input_iterator_tag iterator_category; + // apparently some versions of VC++ have trouble with two ::'s in a typename + typedef typename containertype::value_type _tmp_vt; + typedef typename _tmp_vt::value_type value_type; + typedef typename _tmp_vt::difference_type difference_type; + typedef typename _tmp_vt::reference reference; + typedef typename _tmp_vt::pointer pointer; + + destructive_two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr) + : row_begin(begin), row_end(end), row_current(curr), col_current() { + if ( curr != end ) { + col_current = curr->TWOD_BEGIN_(); + advance_past_end(); // in case cur->begin() == cur->end() + } + } + destructive_two_d_iterator(typename containertype::iterator begin, + typename containertype::iterator end, + typename containertype::iterator curr, + typename containertype::value_type::TWOD_ITER_ col) + : row_begin(begin), row_end(end), row_current(curr), col_current(col) { + advance_past_end(); // in case cur->begin() == cur->end() + } + destructive_two_d_iterator() + : row_begin(), row_end(), row_current(), col_current() { + } + + typename containertype::iterator row_begin, row_end, row_current; + typename containertype::value_type::TWOD_ITER_ col_current; + + // This is the part that destroys + void advance_past_end() { // used when col_current points to end() + while ( col_current == row_current->TWOD_END_() ) { // end of current row + row_current->clear(); // the destructive part + // It would be nice if we could decrement sparsetable->num_buckets here + ++row_current; // go to beginning of next + if ( row_current != row_end ) // col is irrelevant at end + col_current = row_current->TWOD_BEGIN_(); + else + break; // don't go past row_end + } + } + + // EVERYTHING FROM HERE DOWN IS THE SAME AS THE REGULAR ITERATOR + reference operator*() const { return *col_current; } + pointer operator->() const { return &(operator*()); } + + iterator& operator++() { + assert(row_current != row_end); // how to ++ from there? + ++col_current; + advance_past_end(); // in case col_current is at end() + return *this; + } + iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; } + + bool operator==(const iterator& it) const { + return ( row_begin == it.row_begin && + row_end == it.row_end && + row_current == it.row_current && + (row_current == row_end || col_current == it.col_current) ); + } + bool operator!=(const iterator& it) const { return !(*this == it); } +}; + +#undef TWOD_BEGIN_ +#undef TWOD_END_ +#undef TWOD_ITER_ +#undef TWOD_CONST_ITER_ + + + + +// SPARSE-TABLE +// ------------ +// The idea is that a table with (logically) t buckets is divided +// into t/M *groups* of M buckets each. (M is a constant set in +// GROUP_SIZE for efficiency.) Each group is stored sparsely. +// Thus, inserting into the table causes some array to grow, which is +// slow but still constant time. Lookup involves doing a +// logical-position-to-sparse-position lookup, which is also slow but +// constant time. The larger M is, the slower these operations are +// but the less overhead (slightly). +// +// To store the sparse array, we store a bitmap B, where B[i] = 1 iff +// bucket i is non-empty. Then to look up bucket i we really look up +// array[# of 1s before i in B]. This is constant time for fixed M. +// +// Terminology: the position of an item in the overall table (from +// 1 .. t) is called its "location." The logical position in a group +// (from 1 .. M ) is called its "position." The actual location in +// the array (from 1 .. # of non-empty buckets in the group) is +// called its "offset." + +// The weird mod in the offset is entirely to quiet compiler warnings +// as is the cast to int after doing the "x mod 256" +#define PUT_(take_from, offset) do { \ + if (putc(static_cast(((take_from) >> ((offset) % (sizeof(take_from)*8)))\ + % 256), fp) \ + == EOF) \ + return false; \ +} while (0) + +#define GET_(add_to, offset) do { \ + if ((x=getc(fp)) == EOF) \ + return false; \ + else \ + add_to |= (static_cast(x) << ((offset) % (sizeof(add_to)*8))); \ +} while (0) + +template +class sparsegroup { + private: + typedef typename Alloc::template rebind::other value_alloc_type; + + public: + // Basic types + typedef T value_type; + typedef Alloc allocator_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::const_reference const_reference; + typedef typename value_alloc_type::pointer pointer; + typedef typename value_alloc_type::const_pointer const_pointer; + + typedef table_iterator > iterator; + typedef const_table_iterator > + const_iterator; + typedef table_element_adaptor > + element_adaptor; + typedef u_int16_t size_type; // max # of buckets + typedef int16_t difference_type; + typedef STL_NAMESPACE::reverse_iterator const_reverse_iterator; + typedef STL_NAMESPACE::reverse_iterator reverse_iterator; + + // These are our special iterators, that go over non-empty buckets in a + // group. These aren't const-only because you can change non-empty bcks. + typedef pointer nonempty_iterator; + typedef const_pointer const_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator reverse_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator const_reverse_nonempty_iterator; + + // Iterator functions + iterator begin() { return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + iterator end() { return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + // We'll have versions for our special non-empty iterator too + nonempty_iterator nonempty_begin() { return group; } + const_nonempty_iterator nonempty_begin() const { return group; } + nonempty_iterator nonempty_end() { + return group + settings.num_buckets; + } + const_nonempty_iterator nonempty_end() const { + return group + settings.num_buckets; + } + reverse_nonempty_iterator nonempty_rbegin() { + return reverse_nonempty_iterator(nonempty_end()); + } + const_reverse_nonempty_iterator nonempty_rbegin() const { + return const_reverse_nonempty_iterator(nonempty_end()); + } + reverse_nonempty_iterator nonempty_rend() { + return reverse_nonempty_iterator(nonempty_begin()); + } + const_reverse_nonempty_iterator nonempty_rend() const { + return const_reverse_nonempty_iterator(nonempty_begin()); + } + + + // This gives us the "default" value to return for an empty bucket. + // We just use the default constructor on T, the template type + const_reference default_value() const { + static value_type defaultval = value_type(); + return defaultval; + } + + + private: + // We need to do all this bit manipulation, of course. ick + static size_type charbit(size_type i) { return i >> 3; } + static size_type modbit(size_type i) { return 1 << (i&7); } + int bmtest(size_type i) const { return bitmap[charbit(i)] & modbit(i); } + void bmset(size_type i) { bitmap[charbit(i)] |= modbit(i); } + void bmclear(size_type i) { bitmap[charbit(i)] &= ~modbit(i); } + + pointer allocate_group(size_type n) { + pointer retval = settings.allocate(n); + if (retval == NULL) { + // We really should use PRIuS here, but I don't want to have to add + // a whole new configure option, with concomitant macro namespace + // pollution, just to print this (unlikely) error message. So I cast. + fprintf(stderr, "sparsehash: FATAL ERROR: " + "failed to allocate %lu groups\n", + static_cast(n)); + exit(1); + } + return retval; + } + + void free_group() { + if (!group) return; + pointer end_it = group + settings.num_buckets; + for (pointer p = group; p != end_it; ++p) + p->~value_type(); + settings.deallocate(group, settings.num_buckets); + group = NULL; + } + + public: // get_iter() in sparsetable needs it + // We need a small function that tells us how many set bits there are + // in positions 0..i-1 of the bitmap. It uses a big table. + // We make it static so templates don't allocate lots of these tables. + // There are lots of ways to do this calculation (called 'popcount'). + // The 8-bit table lookup is one of the fastest, though this + // implementation suffers from not doing any loop unrolling. See, eg, + // http://www.dalkescientific.com/writings/diary/archive/2008/07/03/hakmem_and_other_popcounts.html + // http://gurmeetsingh.wordpress.com/2008/08/05/fast-bit-counting-routines/ + static size_type pos_to_offset(const unsigned char *bm, size_type pos) { + // We could make these ints. The tradeoff is size (eg does it overwhelm + // the cache?) vs efficiency in referencing sub-word-sized array elements + static const char bits_in[256] = { // # of bits set in one char + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, + }; + size_type retval = 0; + + // [Note: condition pos > 8 is an optimization; convince yourself we + // give exactly the same result as if we had pos >= 8 here instead.] + for ( ; pos > 8; pos -= 8 ) // bm[0..pos/8-1] + retval += bits_in[*bm++]; // chars we want *all* bits in + return retval + bits_in[*bm & ((1 << pos)-1)]; // the char that includes pos + } + + size_type pos_to_offset(size_type pos) const { // not static but still const + return pos_to_offset(bitmap, pos); + } + + + public: + // Constructors -- default and copy -- and destructor + sparsegroup(allocator_type& a) : + group(0), settings(alloc_impl(a)) { + memset(bitmap, 0, sizeof(bitmap)); + } + sparsegroup(const sparsegroup& x) : group(0), settings(x.settings) { + if ( settings.num_buckets ) { + group = allocate_group(x.settings.num_buckets); + uninitialized_copy(x.group, x.group + x.settings.num_buckets, group); + } + memcpy(bitmap, x.bitmap, sizeof(bitmap)); + } + ~sparsegroup() { free_group(); } + + // Operator= is just like the copy constructor, I guess + // TODO(austern): Make this exception safe. Handle exceptions in value_type's + // copy constructor. + sparsegroup &operator=(const sparsegroup& x) { + if ( &x == this ) return *this; // x = x + if ( x.settings.num_buckets == 0 ) { + free_group(); + } else { + pointer p = allocate_group(x.settings.num_buckets); + uninitialized_copy(x.group, x.group + x.settings.num_buckets, p); + free_group(); + group = p; + } + memcpy(bitmap, x.bitmap, sizeof(bitmap)); + settings.num_buckets = x.settings.num_buckets; + return *this; + } + + // Many STL algorithms use swap instead of copy constructors + void swap(sparsegroup& x) { + STL_NAMESPACE::swap(group, x.group); + for ( int i = 0; i < sizeof(bitmap) / sizeof(*bitmap); ++i ) + STL_NAMESPACE::swap(bitmap[i], x.bitmap[i]); // swap not defined on arrays + STL_NAMESPACE::swap(settings.num_buckets, x.settings.num_buckets); + // we purposefully don't swap the allocator, which may not be swap-able + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + free_group(); + memset(bitmap, 0, sizeof(bitmap)); + settings.num_buckets = 0; + } + + // Functions that tell you about size. Alas, these aren't so useful + // because our table is always fixed size. + size_type size() const { return GROUP_SIZE; } + size_type max_size() const { return GROUP_SIZE; } + bool empty() const { return false; } + // We also may want to know how many *used* buckets there are + size_type num_nonempty() const { return settings.num_buckets; } + + + // get()/set() are explicitly const/non-const. You can use [] if + // you want something that can be either (potentially more expensive). + const_reference get(size_type i) const { + if ( bmtest(i) ) // bucket i is occupied + return group[pos_to_offset(bitmap, i)]; + else + return default_value(); // return the default reference + } + + // TODO(csilvers): make protected + friend + // This is used by sparse_hashtable to get an element from the table + // when we know it exists. + const_reference unsafe_get(size_type i) const { + assert(bmtest(i)); + return group[pos_to_offset(bitmap, i)]; + } + + // TODO(csilvers): make protected + friend + reference mutating_get(size_type i) { // fills bucket i before getting + if ( !bmtest(i) ) + set(i, default_value()); + return group[pos_to_offset(bitmap, i)]; + } + + // Syntactic sugar. It's easy to return a const reference. To + // return a non-const reference, we need to use the assigner adaptor. + const_reference operator[](size_type i) const { + return get(i); + } + + element_adaptor operator[](size_type i) { + return element_adaptor(this, i); + } + + private: + // Create space at group[offset], assuming value_type has trivial + // copy constructor and destructor, and the allocator_type is + // the default libc_allocator_with_alloc. (Really, we want it to have + // "trivial move", because that's what realloc and memmove both do. + // But there's no way to capture that using type_traits, so we + // pretend that move(x, y) is equivalent to "x.~T(); new(x) T(y);" + // which is pretty much correct, if a bit conservative.) + void set_aux(size_type offset, true_type) { + group = settings.realloc_or_die(group, settings.num_buckets+1); + // This is equivalent to memmove(), but faster on my Intel P4, + // at least with gcc4.1 -O2 / glibc 2.3.6. + for (size_type i = settings.num_buckets; i > offset; --i) + memcpy(group + i, group + i-1, sizeof(*group)); + } + + // Create space at group[offset], without special assumptions about value_type + // and allocator_type. + void set_aux(size_type offset, false_type) { + // This is valid because 0 <= offset <= num_buckets + pointer p = allocate_group(settings.num_buckets + 1); + uninitialized_copy(group, group + offset, p); + uninitialized_copy(group + offset, group + settings.num_buckets, + p + offset + 1); + free_group(); + group = p; + } + + public: + // This returns a reference to the inserted item (which is a copy of val). + // TODO(austern): Make this exception safe: handle exceptions from + // value_type's copy constructor. + reference set(size_type i, const_reference val) { + size_type offset = pos_to_offset(bitmap, i); // where we'll find (or insert) + if ( bmtest(i) ) { + // Delete the old value, which we're replacing with the new one + group[offset].~value_type(); + } else { + typedef integral_constant::value && + has_trivial_destructor::value && + is_same >::value)> + realloc_and_memmove_ok; // we pretend mv(x,y) == "x.~T(); new(x) T(y)" + set_aux(offset, realloc_and_memmove_ok()); + ++settings.num_buckets; + bmset(i); + } + // This does the actual inserting. Since we made the array using + // malloc, we use "placement new" to just call the constructor. + new(&group[offset]) value_type(val); + return group[offset]; + } + + // We let you see if a bucket is non-empty without retrieving it + bool test(size_type i) const { + return bmtest(i) != 0; + } + bool test(iterator pos) const { + return bmtest(pos.pos) != 0; + } + + private: + // Shrink the array, assuming value_type has trivial copy + // constructor and destructor, and the allocator_type is the default + // libc_allocator_with_alloc. (Really, we want it to have "trivial + // move", because that's what realloc and memmove both do. But + // there's no way to capture that using type_traits, so we pretend + // that move(x, y) is equivalent to ""x.~T(); new(x) T(y);" + // which is pretty much correct, if a bit conservative.) + void erase_aux(size_type offset, true_type) { + // This isn't technically necessary, since we know we have a + // trivial destructor, but is a cheap way to get a bit more safety. + group[offset].~value_type(); + // This is equivalent to memmove(), but faster on my Intel P4, + // at lesat with gcc4.1 -O2 / glibc 2.3.6. + assert(settings.num_buckets > 0); + for (size_type i = offset; i < settings.num_buckets-1; ++i) + memcpy(group + i, group + i+1, sizeof(*group)); // hopefully inlined! + group = settings.realloc_or_die(group, settings.num_buckets-1); + } + + // Shrink the array, without any special assumptions about value_type and + // allocator_type. + void erase_aux(size_type offset, false_type) { + // This is valid because 0 <= offset < num_buckets. Note the inequality. + pointer p = allocate_group(settings.num_buckets - 1); + uninitialized_copy(group, group + offset, p); + uninitialized_copy(group + offset + 1, group + settings.num_buckets, + p + offset); + free_group(); + group = p; + } + + public: + // This takes the specified elements out of the group. This is + // "undefining", rather than "clearing". + // TODO(austern): Make this exception safe: handle exceptions from + // value_type's copy constructor. + void erase(size_type i) { + if ( bmtest(i) ) { // trivial to erase empty bucket + size_type offset = pos_to_offset(bitmap,i); // where we'll find (or insert) + if ( settings.num_buckets == 1 ) { + free_group(); + group = NULL; + } else { + typedef integral_constant::value && + has_trivial_destructor::value && + is_same< + allocator_type, + libc_allocator_with_realloc >::value)> + realloc_and_memmove_ok; // pretend mv(x,y) == "x.~T(); new(x) T(y)" + erase_aux(offset, realloc_and_memmove_ok()); + } + --settings.num_buckets; + bmclear(i); + } + } + + void erase(iterator pos) { + erase(pos.pos); + } + + void erase(iterator start_it, iterator end_it) { + // This could be more efficient, but to do so we'd need to make + // bmclear() clear a range of indices. Doesn't seem worth it. + for ( ; start_it != end_it; ++start_it ) + erase(start_it); + } + + + // I/O + // We support reading and writing groups to disk. We don't store + // the actual array contents (which we don't know how to store), + // just the bitmap and size. Meant to be used with table I/O. + // Returns true if all was ok + bool write_metadata(FILE *fp) const { + // we explicitly set to u_int16_t + assert(sizeof(settings.num_buckets) == 2); + PUT_(settings.num_buckets, 8); + PUT_(settings.num_buckets, 0); + if ( !fwrite(bitmap, sizeof(bitmap), 1, fp) ) + return false; + return true; + } + + // Reading destroys the old group contents! Returns true if all was ok + bool read_metadata(FILE *fp) { + clear(); + + int x; // the GET_ macro requires an 'int x' to be defined + GET_(settings.num_buckets, 8); + GET_(settings.num_buckets, 0); + + if ( !fread(bitmap, sizeof(bitmap), 1, fp) ) return false; + + // We'll allocate the space, but we won't fill it: it will be + // left as uninitialized raw memory. + group = allocate_group(settings.num_buckets); + return true; + } + + // If your keys and values are simple enough, we can write them + // to disk for you. "simple enough" means POD and no pointers. + // However, we don't try to normalize endianness + bool write_nopointer_data(FILE *fp) const { + for ( const_nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; + } + return true; + } + + // When reading, we have to override the potential const-ness of *it. + // Again, only meaningful if value_type is a POD. + bool read_nopointer_data(FILE *fp) { + for ( nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp) ) + return false; + } + return true; + } + + // Comparisons. Note the comparisons are pretty arbitrary: we + // compare values of the first index that isn't equal (using default + // value for empty buckets). + bool operator==(const sparsegroup& x) const { + return ( settings.num_buckets == x.settings.num_buckets && + memcmp(bitmap, x.bitmap, sizeof(bitmap)) == 0 && + STL_NAMESPACE::equal(begin(), end(), x.begin()) ); // from algorithm + } + bool operator<(const sparsegroup& x) const { // also from algorithm + return STL_NAMESPACE::lexicographical_compare(begin(), end(), + x.begin(), x.end()); + } + bool operator!=(const sparsegroup& x) const { return !(*this == x); } + bool operator<=(const sparsegroup& x) const { return !(x < *this); } + bool operator>(const sparsegroup& x) const { return x < *this; } + bool operator>=(const sparsegroup& x) const { return !(*this < x); } + + private: + template + class alloc_impl : public A { + public: + typedef typename A::pointer pointer; + typedef typename A::size_type size_type; + + // Convert a normal allocator to one that has realloc_or_die() + alloc_impl(const A& a) : A(a) { } + + // realloc_or_die should only be used when using the default + // allocator (libc_allocator_with_realloc). + pointer realloc_or_die(pointer /*ptr*/, size_type /*n*/) { + fprintf(stderr, "realloc_or_die is only supported for " + "libc_allocator_with_realloc"); + exit(1); + return NULL; + } + }; + + // A template specialization of alloc_impl for + // libc_allocator_with_realloc that can handle realloc_or_die. + template + class alloc_impl > + : public libc_allocator_with_realloc { + public: + typedef typename libc_allocator_with_realloc::pointer pointer; + typedef typename libc_allocator_with_realloc::size_type size_type; + + alloc_impl(const libc_allocator_with_realloc& a) + : libc_allocator_with_realloc(a) { } + + pointer realloc_or_die(pointer ptr, size_type n) { + pointer retval = this->reallocate(ptr, n); + if (retval == NULL) { + // We really should use PRIuS here, but I don't want to have to add + // a whole new configure option, with concomitant macro namespace + // pollution, just to print this (unlikely) error message. So I cast. + fprintf(stderr, "sparsehash: FATAL ERROR: failed to reallocate " + "%lu elements for ptr %p", + static_cast(n), ptr); + exit(1); + } + return retval; + } + }; + + // Package allocator with num_buckets to eliminate memory needed for the + // zero-size allocator. + // If new fields are added to this class, we should add them to + // operator= and swap. + class Settings : public alloc_impl { + public: + Settings(const alloc_impl& a, u_int16_t n = 0) + : alloc_impl(a), num_buckets(n) { } + Settings(const Settings& s) + : alloc_impl(s), num_buckets(s.num_buckets) { } + + u_int16_t num_buckets; // limits GROUP_SIZE to 64K + }; + + // The actual data + pointer group; // (small) array of T's + Settings settings; // allocator and num_buckets + unsigned char bitmap[(GROUP_SIZE-1)/8 + 1]; // fancy math is so we round up +}; + +// We need a global swap as well +template +inline void swap(sparsegroup &x, + sparsegroup &y) { + x.swap(y); +} + +// --------------------------------------------------------------------------- + + +template > +class sparsetable { + private: + typedef typename Alloc::template rebind::other value_alloc_type; + typedef typename Alloc::template rebind< + sparsegroup >::other vector_alloc; + + public: + // Basic types + typedef T value_type; // stolen from stl_vector.h + typedef Alloc allocator_type; + typedef typename value_alloc_type::size_type size_type; + typedef typename value_alloc_type::difference_type difference_type; + typedef typename value_alloc_type::reference reference; + typedef typename value_alloc_type::const_reference const_reference; + typedef typename value_alloc_type::pointer pointer; + typedef typename value_alloc_type::const_pointer const_pointer; + typedef table_iterator > iterator; + typedef const_table_iterator > + const_iterator; + typedef table_element_adaptor > + element_adaptor; + typedef STL_NAMESPACE::reverse_iterator const_reverse_iterator; + typedef STL_NAMESPACE::reverse_iterator reverse_iterator; + + // These are our special iterators, that go over non-empty buckets in a + // table. These aren't const only because you can change non-empty bcks. + typedef two_d_iterator< vector< sparsegroup, + vector_alloc> > + nonempty_iterator; + typedef const_two_d_iterator< vector< sparsegroup, + vector_alloc> > + const_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator reverse_nonempty_iterator; + typedef STL_NAMESPACE::reverse_iterator const_reverse_nonempty_iterator; + // Another special iterator: it frees memory as it iterates (used to resize) + typedef destructive_two_d_iterator< vector< sparsegroup, + vector_alloc> > + destructive_iterator; + + // Iterator functions + iterator begin() { return iterator(this, 0); } + const_iterator begin() const { return const_iterator(this, 0); } + iterator end() { return iterator(this, size()); } + const_iterator end() const { return const_iterator(this, size()); } + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + // Versions for our special non-empty iterator + nonempty_iterator nonempty_begin() { + return nonempty_iterator(groups.begin(), groups.end(), groups.begin()); + } + const_nonempty_iterator nonempty_begin() const { + return const_nonempty_iterator(groups.begin(),groups.end(), groups.begin()); + } + nonempty_iterator nonempty_end() { + return nonempty_iterator(groups.begin(), groups.end(), groups.end()); + } + const_nonempty_iterator nonempty_end() const { + return const_nonempty_iterator(groups.begin(), groups.end(), groups.end()); + } + reverse_nonempty_iterator nonempty_rbegin() { + return reverse_nonempty_iterator(nonempty_end()); + } + const_reverse_nonempty_iterator nonempty_rbegin() const { + return const_reverse_nonempty_iterator(nonempty_end()); + } + reverse_nonempty_iterator nonempty_rend() { + return reverse_nonempty_iterator(nonempty_begin()); + } + const_reverse_nonempty_iterator nonempty_rend() const { + return const_reverse_nonempty_iterator(nonempty_begin()); + } + destructive_iterator destructive_begin() { + return destructive_iterator(groups.begin(), groups.end(), groups.begin()); + } + destructive_iterator destructive_end() { + return destructive_iterator(groups.begin(), groups.end(), groups.end()); + } + + typedef sparsegroup group_type; + typedef vector group_vector_type; + + typedef typename group_vector_type::reference GroupsReference; + typedef typename group_vector_type::const_reference GroupsConstReference; + typedef typename group_vector_type::iterator GroupsIterator; + typedef typename group_vector_type::const_iterator GroupsConstIterator; + + // How to deal with the proper group + static size_type num_groups(size_type num) { // how many to hold num buckets + return num == 0 ? 0 : ((num-1) / GROUP_SIZE) + 1; + } + + u_int16_t pos_in_group(size_type i) const { + return static_cast(i % GROUP_SIZE); + } + size_type group_num(size_type i) const { + return i / GROUP_SIZE; + } + GroupsReference which_group(size_type i) { + return groups[group_num(i)]; + } + GroupsConstReference which_group(size_type i) const { + return groups[group_num(i)]; + } + + public: + // Constructors -- default, normal (when you specify size), and copy + sparsetable(size_type sz = 0, Alloc alloc = Alloc()) + : groups(vector_alloc(alloc)), settings(alloc, sz) { + groups.resize(num_groups(sz), group_type(settings)); + } + // We can get away with using the default copy constructor, + // and default destructor, and hence the default operator=. Huzzah! + + // Many STL algorithms use swap instead of copy constructors + void swap(sparsetable& x) { + STL_NAMESPACE::swap(groups, x.groups); + STL_NAMESPACE::swap(settings.table_size, x.settings.table_size); + STL_NAMESPACE::swap(settings.num_buckets, x.settings.num_buckets); + } + + // It's always nice to be able to clear a table without deallocating it + void clear() { + GroupsIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) { + group->clear(); + } + settings.num_buckets = 0; + } + + // ACCESSOR FUNCTIONS for the things we templatize on, basically + allocator_type get_allocator() const { + return allocator_type(settings); + } + + + // Functions that tell you about size. + // NOTE: empty() is non-intuitive! It does not tell you the number + // of not-empty buckets (use num_nonempty() for that). Instead + // it says whether you've allocated any buckets or not. + size_type size() const { return settings.table_size; } + size_type max_size() const { return settings.max_size(); } + bool empty() const { return settings.table_size == 0; } + // We also may want to know how many *used* buckets there are + size_type num_nonempty() const { return settings.num_buckets; } + + // OK, we'll let you resize one of these puppies + void resize(size_type new_size) { + groups.resize(num_groups(new_size), group_type(settings)); + if ( new_size < settings.table_size) { + // lower num_buckets, clear last group + if ( pos_in_group(new_size) > 0 ) // need to clear inside last group + groups.back().erase(groups.back().begin() + pos_in_group(new_size), + groups.back().end()); + settings.num_buckets = 0; // refigure # of used buckets + GroupsConstIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) + settings.num_buckets += group->num_nonempty(); + } + settings.table_size = new_size; + } + + + // We let you see if a bucket is non-empty without retrieving it + bool test(size_type i) const { + return which_group(i).test(pos_in_group(i)); + } + bool test(iterator pos) const { + return which_group(pos.pos).test(pos_in_group(pos.pos)); + } + bool test(const_iterator pos) const { + return which_group(pos.pos).test(pos_in_group(pos.pos)); + } + + // We only return const_references because it's really hard to + // return something settable for empty buckets. Use set() instead. + const_reference get(size_type i) const { + assert(i < settings.table_size); + return which_group(i).get(pos_in_group(i)); + } + + // TODO(csilvers): make protected + friend + // This is used by sparse_hashtable to get an element from the table + // when we know it exists (because the caller has called test(i)). + const_reference unsafe_get(size_type i) const { + assert(i < settings.table_size); + assert(test(i)); + return which_group(i).unsafe_get(pos_in_group(i)); + } + + // TODO(csilvers): make protected + friend element_adaptor + reference mutating_get(size_type i) { // fills bucket i before getting + assert(i < settings.table_size); + size_type old_numbuckets = which_group(i).num_nonempty(); + reference retval = which_group(i).mutating_get(pos_in_group(i)); + settings.num_buckets += which_group(i).num_nonempty() - old_numbuckets; + return retval; + } + + // Syntactic sugar. As in sparsegroup, the non-const version is harder + const_reference operator[](size_type i) const { + return get(i); + } + + element_adaptor operator[](size_type i) { + return element_adaptor(this, i); + } + + // Needed for hashtables, gets as a nonempty_iterator. Crashes for empty bcks + const_nonempty_iterator get_iter(size_type i) const { + assert(test(i)); // how can a nonempty_iterator point to an empty bucket? + return const_nonempty_iterator( + groups.begin(), groups.end(), + groups.begin() + group_num(i), + (groups[group_num(i)].nonempty_begin() + + groups[group_num(i)].pos_to_offset(pos_in_group(i)))); + } + // For nonempty we can return a non-const version + nonempty_iterator get_iter(size_type i) { + assert(test(i)); // how can a nonempty_iterator point to an empty bucket? + return nonempty_iterator( + groups.begin(), groups.end(), + groups.begin() + group_num(i), + (groups[group_num(i)].nonempty_begin() + + groups[group_num(i)].pos_to_offset(pos_in_group(i)))); + } + + + // This returns a reference to the inserted item (which is a copy of val) + // The trick is to figure out whether we're replacing or inserting anew + reference set(size_type i, const_reference val) { + assert(i < settings.table_size); + size_type old_numbuckets = which_group(i).num_nonempty(); + reference retval = which_group(i).set(pos_in_group(i), val); + settings.num_buckets += which_group(i).num_nonempty() - old_numbuckets; + return retval; + } + + // This takes the specified elements out of the table. This is + // "undefining", rather than "clearing". + void erase(size_type i) { + assert(i < settings.table_size); + size_type old_numbuckets = which_group(i).num_nonempty(); + which_group(i).erase(pos_in_group(i)); + settings.num_buckets += which_group(i).num_nonempty() - old_numbuckets; + } + + void erase(iterator pos) { + erase(pos.pos); + } + + void erase(iterator start_it, iterator end_it) { + // This could be more efficient, but then we'd need to figure + // out if we spanned groups or not. Doesn't seem worth it. + for ( ; start_it != end_it; ++start_it ) + erase(start_it); + } + + + // We support reading and writing tables to disk. We don't store + // the actual array contents (which we don't know how to store), + // just the groups and sizes. Returns true if all went ok. + + private: + // Every time the disk format changes, this should probably change too + static const unsigned long MAGIC_NUMBER = 0x24687531; + + // Old versions of this code write all data in 32 bits. We need to + // support these files as well as having support for 64-bit systems. + // So we use the following encoding scheme: for values < 2^32-1, we + // store in 4 bytes in big-endian order. For values > 2^32, we + // store 0xFFFFFFF followed by 8 bytes in big-endian order. This + // causes us to mis-read old-version code that stores exactly + // 0xFFFFFFF, but I don't think that is likely to have happened for + // these particular values. + static bool write_32_or_64(FILE* fp, size_type value) { + if ( value < 0xFFFFFFFFULL ) { // fits in 4 bytes + PUT_(value, 24); + PUT_(value, 16); + PUT_(value, 8); + PUT_(value, 0); + } else if ( value == 0xFFFFFFFFUL ) { // special case in 32bit systems + PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); // marker + PUT_(0, 0); PUT_(0, 0); PUT_(0, 0); PUT_(0, 0); + PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); + } else { + PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); PUT_(0xFF, 0); // marker + PUT_(value, 56); + PUT_(value, 48); + PUT_(value, 40); + PUT_(value, 32); + PUT_(value, 24); + PUT_(value, 16); + PUT_(value, 8); + PUT_(value, 0); + } + return true; + } + + static bool read_32_or_64(FILE* fp, size_type *value) { // reads into value + size_type first4 = 0; + int x; + GET_(first4, 24); + GET_(first4, 16); + GET_(first4, 8); + GET_(first4, 0); + if ( first4 < 0xFFFFFFFFULL ) { + *value = first4; + } else { + GET_(*value, 56); + GET_(*value, 48); + GET_(*value, 40); + GET_(*value, 32); + GET_(*value, 24); + GET_(*value, 16); + GET_(*value, 8); + GET_(*value, 0); + } + return true; + } + + public: + bool write_metadata(FILE *fp) const { + if ( !write_32_or_64(fp, MAGIC_NUMBER) ) return false; + if ( !write_32_or_64(fp, settings.table_size) ) return false; + if ( !write_32_or_64(fp, settings.num_buckets) ) return false; + + GroupsConstIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) + if ( group->write_metadata(fp) == false ) return false; + return true; + } + + // Reading destroys the old table contents! Returns true if read ok. + bool read_metadata(FILE *fp) { + size_type magic_read = 0; + if ( !read_32_or_64(fp, &magic_read) ) return false; + if ( magic_read != MAGIC_NUMBER ) { + clear(); // just to be consistent + return false; + } + + if ( !read_32_or_64(fp, &settings.table_size) ) return false; + if ( !read_32_or_64(fp, &settings.num_buckets) ) return false; + + resize(settings.table_size); // so the vector's sized ok + GroupsIterator group; + for ( group = groups.begin(); group != groups.end(); ++group ) + if ( group->read_metadata(fp) == false ) return false; + return true; + } + + // This code is identical to that for SparseGroup + // If your keys and values are simple enough, we can write them + // to disk for you. "simple enough" means no pointers. + // However, we don't try to normalize endianness + bool write_nopointer_data(FILE *fp) const { + for ( const_nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fwrite(&*it, sizeof(*it), 1, fp) ) return false; + } + return true; + } + + // When reading, we have to override the potential const-ness of *it + bool read_nopointer_data(FILE *fp) { + for ( nonempty_iterator it = nonempty_begin(); + it != nonempty_end(); ++it ) { + if ( !fread(reinterpret_cast(&(*it)), sizeof(*it), 1, fp) ) + return false; + } + return true; + } + + // Comparisons. Note the comparisons are pretty arbitrary: we + // compare values of the first index that isn't equal (using default + // value for empty buckets). + bool operator==(const sparsetable& x) const { + return ( settings.table_size == x.settings.table_size && + settings.num_buckets == x.settings.num_buckets && + groups == x.groups ); + } + bool operator<(const sparsetable& x) const { // also from algobase.h + return STL_NAMESPACE::lexicographical_compare(begin(), end(), + x.begin(), x.end()); + } + bool operator!=(const sparsetable& x) const { return !(*this == x); } + bool operator<=(const sparsetable& x) const { return !(x < *this); } + bool operator>(const sparsetable& x) const { return x < *this; } + bool operator>=(const sparsetable& x) const { return !(*this < x); } + + + private: + // Package allocator with table_size and num_buckets to eliminate memory + // needed for the zero-size allocator. + // If new fields are added to this class, we should add them to + // operator= and swap. + class Settings : public allocator_type { + public: + typedef typename allocator_type::size_type size_type; + + Settings(const allocator_type& a, size_type sz = 0, size_type n = 0) + : allocator_type(a), table_size(sz), num_buckets(n) { } + + Settings(const Settings& s) + : allocator_type(s), + table_size(s.table_size), num_buckets(s.num_buckets) { } + + size_type table_size; // how many buckets they want + size_type num_buckets; // number of non-empty buckets + }; + + // The actual data + group_vector_type groups; // our list of groups + Settings settings; // allocator, table size, buckets +}; + +// We need a global swap as well +template +inline void swap(sparsetable &x, + sparsetable &y) { + x.swap(y); +} + +#undef GET_ +#undef PUT_ + +_END_GOOGLE_NAMESPACE_ + +#endif diff --git a/external/google/type_traits.h b/external/google/type_traits.h new file mode 100644 index 0000000000..87729d4053 --- /dev/null +++ b/external/google/type_traits.h @@ -0,0 +1,336 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// ---- +// Author: Matt Austern +// +// Define a small subset of tr1 type traits. The traits we define are: +// is_integral +// is_floating_point +// is_pointer +// is_enum +// is_reference +// is_pod +// has_trivial_constructor +// has_trivial_copy +// has_trivial_assign +// has_trivial_destructor +// remove_const +// remove_volatile +// remove_cv +// remove_reference +// add_reference +// remove_pointer +// is_same +// is_convertible +// We can add more type traits as required. + +#ifndef BASE_TYPE_TRAITS_H_ +#define BASE_TYPE_TRAITS_H_ + +#include +#include // For pair + +_START_GOOGLE_NAMESPACE_ + +// integral_constant, defined in tr1, is a wrapper for an integer +// value. We don't really need this generality; we could get away +// with hardcoding the integer type to bool. We use the fully +// general integer_constant for compatibility with tr1. + +template +struct integral_constant { + static const T value = v; + typedef T value_type; + typedef integral_constant type; +}; + +template const T integral_constant::value; + +// Abbreviations: true_type and false_type are structs that represent +// boolean true and false values. +typedef integral_constant true_type; +typedef integral_constant false_type; + +// Types small_ and big_ are guaranteed such that sizeof(small_) < +// sizeof(big_) +typedef char small_; + +struct big_ { + char dummy[2]; +}; + +template struct is_integral; +template struct is_floating_point; +template struct is_pointer; +// MSVC can't compile this correctly, and neither can gcc 3.3.5 (at least) +#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) +// is_enum uses is_convertible, which is not available on MSVC. +template struct is_enum; +#endif +template struct is_reference; +template struct is_pod; +template struct has_trivial_constructor; +template struct has_trivial_copy; +template struct has_trivial_assign; +template struct has_trivial_destructor; +template struct remove_const; +template struct remove_volatile; +template struct remove_cv; +template struct remove_reference; +template struct add_reference; +template struct remove_pointer; +template struct is_same; +#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) +template struct is_convertible; +#endif + +// is_integral is false except for the built-in integer types. +template struct is_integral : false_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +#if defined(_MSC_VER) +// wchar_t is not by default a distinct type from unsigned short in +// Microsoft C. +// See http://msdn2.microsoft.com/en-us/library/dh8che7s(VS.80).aspx +template<> struct is_integral<__wchar_t> : true_type { }; +#else +template<> struct is_integral : true_type { }; +#endif +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +#ifdef HAVE_LONG_LONG +template<> struct is_integral : true_type { }; +template<> struct is_integral : true_type { }; +#endif + + +// is_floating_point is false except for the built-in floating-point types. +template struct is_floating_point : false_type { }; +template<> struct is_floating_point : true_type { }; +template<> struct is_floating_point : true_type { }; +template<> struct is_floating_point : true_type { }; + + +// is_pointer is false except for pointer types. +template struct is_pointer : false_type { }; +template struct is_pointer : true_type { }; + +#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) + +namespace internal { + +template struct is_class_or_union { + template static small_ tester(void (U::*)()); + template static big_ tester(...); + static const bool value = sizeof(tester(0)) == sizeof(small_); +}; + +// is_convertible chokes if the first argument is an array. That's why +// we use add_reference here. +template struct is_enum_impl + : is_convertible::type, int> { }; + +template struct is_enum_impl : false_type { }; + +} // namespace internal + +// Specified by TR1 [4.5.1] primary type categories. + +// Implementation note: +// +// Each type is either void, integral, floating point, array, pointer, +// reference, member object pointer, member function pointer, enum, +// union or class. Out of these, only integral, floating point, reference, +// class and enum types are potentially convertible to int. Therefore, +// if a type is not a reference, integral, floating point or class and +// is convertible to int, it's a enum. +// +// Is-convertible-to-int check is done only if all other checks pass, +// because it can't be used with some types (e.g. void or classes with +// inaccessible conversion operators). +template struct is_enum + : internal::is_enum_impl< + is_same::value || + is_integral::value || + is_floating_point::value || + is_reference::value || + internal::is_class_or_union::value, + T> { }; + +template struct is_enum : is_enum { }; +template struct is_enum : is_enum { }; +template struct is_enum : is_enum { }; + +#endif + +// is_reference is false except for reference types. +template struct is_reference : false_type {}; +template struct is_reference : true_type {}; + + +// We can't get is_pod right without compiler help, so fail conservatively. +// We will assume it's false except for arithmetic types, enumerations, +// pointers and const versions thereof. Note that std::pair is not a POD. +template struct is_pod + : integral_constant::value || + is_floating_point::value || +#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) + // is_enum is not available on MSVC. + is_enum::value || +#endif + is_pointer::value)> { }; +template struct is_pod : is_pod { }; + + +// We can't get has_trivial_constructor right without compiler help, so +// fail conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial +// constructors. (3) array of a type with a trivial constructor. +// (4) const versions thereof. +template struct has_trivial_constructor : is_pod { }; +template struct has_trivial_constructor > + : integral_constant::value && + has_trivial_constructor::value)> { }; +template struct has_trivial_constructor + : has_trivial_constructor { }; +template struct has_trivial_constructor + : has_trivial_constructor { }; + +// We can't get has_trivial_copy right without compiler help, so fail +// conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial copy +// constructors. (3) array of a type with a trivial copy constructor. +// (4) const versions thereof. +template struct has_trivial_copy : is_pod { }; +template struct has_trivial_copy > + : integral_constant::value && + has_trivial_copy::value)> { }; +template struct has_trivial_copy + : has_trivial_copy { }; +template struct has_trivial_copy : has_trivial_copy { }; + +// We can't get has_trivial_assign right without compiler help, so fail +// conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial copy +// constructors. (3) array of a type with a trivial assign constructor. +template struct has_trivial_assign : is_pod { }; +template struct has_trivial_assign > + : integral_constant::value && + has_trivial_assign::value)> { }; +template struct has_trivial_assign + : has_trivial_assign { }; + +// We can't get has_trivial_destructor right without compiler help, so +// fail conservatively. We will assume it's false except for: (1) types +// for which is_pod is true. (2) std::pair of types with trivial +// destructors. (3) array of a type with a trivial destructor. +// (4) const versions thereof. +template struct has_trivial_destructor : is_pod { }; +template struct has_trivial_destructor > + : integral_constant::value && + has_trivial_destructor::value)> { }; +template struct has_trivial_destructor + : has_trivial_destructor { }; +template struct has_trivial_destructor + : has_trivial_destructor { }; + +// Specified by TR1 [4.7.1] +template struct remove_const { typedef T type; }; +template struct remove_const { typedef T type; }; +template struct remove_volatile { typedef T type; }; +template struct remove_volatile { typedef T type; }; +template struct remove_cv { + typedef typename remove_const::type>::type type; +}; + + +// Specified by TR1 [4.7.2] Reference modifications. +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +template struct add_reference { typedef T& type; }; +template struct add_reference { typedef T& type; }; + +// Specified by TR1 [4.7.4] Pointer modifications. +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { + typedef T type; }; + +// Specified by TR1 [4.6] Relationships between types +template struct is_same : public false_type { }; +template struct is_same : public true_type { }; + +// Specified by TR1 [4.6] Relationships between types +#if !defined(_MSC_VER) && !(defined(__GNUC__) && __GNUC__ <= 3) +namespace internal { + +// This class is an implementation detail for is_convertible, and you +// don't need to know how it works to use is_convertible. For those +// who care: we declare two different functions, one whose argument is +// of type To and one with a variadic argument list. We give them +// return types of different size, so we can use sizeof to trick the +// compiler into telling us which function it would have chosen if we +// had called it with an argument of type From. See Alexandrescu's +// _Modern C++ Design_ for more details on this sort of trick. + +template +struct ConvertHelper { + static small_ Test(To); + static big_ Test(...); + static From Create(); +}; +} // namespace internal + +// Inherits from true_type if From is convertible to To, false_type otherwise. +template +struct is_convertible + : integral_constant::Test( + internal::ConvertHelper::Create())) + == sizeof(small_)> { +}; +#endif + +_END_GOOGLE_NAMESPACE_ + +#endif // BASE_TYPE_TRAITS_H_ diff --git a/include/INode.h b/include/INode.h index 9602611972..f6a013a76d 100644 --- a/include/INode.h +++ b/include/INode.h @@ -62,7 +62,7 @@ class INode { virtual uint64_t getLastLocalBlockHeight() const = 0; virtual uint64_t getLastKnownBlockHeight() const = 0; - virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) = 0; + virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) = 0; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; diff --git a/include/IWallet.h b/include/IWallet.h index 8d6c5308e8..26a2dcab34 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -41,7 +41,7 @@ const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits::max(); const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); -struct Transaction { +struct TransactionInfo { TransferId firstTransferId; size_t transferCount; int64_t totalAmount; @@ -89,7 +89,7 @@ class IWallet { virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; - virtual bool getTransaction(TransactionId transactionId, Transaction& transaction) = 0; + virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) = 0; virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0; virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a44632079..9f18c3d548 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ add_definitions(-DSTATICLIB) file(GLOB_RECURSE COMMON common/*) file(GLOB_RECURSE CRYPTO crypto/*) -file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/*) +file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/* cryptonote_config.h) file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*) file(GLOB_RECURSE DAEMON daemon/*) file(GLOB_RECURSE P2P p2p/*) @@ -32,15 +32,16 @@ add_library(cryptonote_core ${CRYPTONOTE_CORE}) add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL}) add_executable(connectivity_tool ${CONN_TOOL}) add_executable(simpleminer ${MINER}) -target_link_libraries(daemon rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(connectivity_tool cryptonote_core crypto common ${Boost_LIBRARIES}) -target_link_libraries(simpleminer cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(connectivity_tool epee cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(simpleminer epee cryptonote_core crypto common ${Boost_LIBRARIES}) add_library(rpc ${RPC}) add_library(wallet ${WALLET}) add_executable(simplewallet ${SIMPLEWALLET} ) -target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(simplewallet epee wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) add_library(node_rpc_proxy ${NODE_RPC_PROXY}) +add_dependencies(connectivity_tool version) add_dependencies(daemon version) add_dependencies(rpc version) add_dependencies(simplewallet version) diff --git a/src/common/BlockingQueue.cpp b/src/common/BlockingQueue.cpp new file mode 100644 index 0000000000..3778bec4ac --- /dev/null +++ b/src/common/BlockingQueue.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockingQueue.h" diff --git a/src/common/BlockingQueue.h b/src/common/BlockingQueue.h new file mode 100644 index 0000000000..bdae78ca4f --- /dev/null +++ b/src/common/BlockingQueue.h @@ -0,0 +1,126 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +template < typename T, typename Container = std::deque > +class BlockingQueue { +public: + + typedef BlockingQueue ThisType; + + BlockingQueue(size_t maxSize = 1) : + m_maxSize(maxSize), m_closed(false) {} + + template + bool push(TT&& v) { + std::unique_lock lk(m_mutex); + + while (!m_closed && m_queue.size() >= m_maxSize) { + m_haveSpace.wait(lk); + } + + if (m_closed) { + return false; + } + + m_queue.push_back(std::forward(v)); + m_haveData.notify_one(); + return true; + } + + bool pop(T& v) { + std::unique_lock lk(m_mutex); + + while (m_queue.empty()) { + if (m_closed) { + // all data has been processed, queue is closed + return false; + } + m_haveData.wait(lk); + } + + v = std::move(m_queue.front()); + m_queue.pop_front(); + + // we can have several waiting threads to unblock + if (m_closed && m_queue.empty()) + m_haveSpace.notify_all(); + else + m_haveSpace.notify_one(); + + return true; + } + + void close(bool wait = false) { + std::unique_lock lk(m_mutex); + m_closed = true; + m_haveData.notify_all(); // wake up threads in pop() + + if (wait) { + while (!m_queue.empty()) { + m_haveSpace.wait(lk); + } + } + } + + size_t size() { + std::unique_lock lk(m_mutex); + return m_queue.size(); + } + + size_t capacity() const { + return m_maxSize; + } + +private: + + const size_t m_maxSize; + Container m_queue; + bool m_closed; + + std::mutex m_mutex; + std::condition_variable m_haveData; + std::condition_variable m_haveSpace; +}; + +template +class GroupClose { +public: + + GroupClose(QueueT& queue, size_t groupSize) + : m_queue(queue), m_count(groupSize) {} + + void close() { + if (m_count == 0) + return; + if (m_count.fetch_sub(1) == 1) + m_queue.close(); + } + +private: + + std::atomic m_count; + QueueT& m_queue; + +}; diff --git a/src/common/ObserverManager.h b/src/common/ObserverManager.h index 772f8ae209..4387b21c9a 100644 --- a/src/common/ObserverManager.h +++ b/src/common/ObserverManager.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include diff --git a/src/common/SignalHandler.cpp b/src/common/SignalHandler.cpp new file mode 100644 index 0000000000..da63a53729 --- /dev/null +++ b/src/common/SignalHandler.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "SignalHandler.h" + +#include +#include + +// epee +#include "include_base_utils.h" + + +namespace tools { + std::function SignalHandler::m_handler; + +#if defined(WIN32) + BOOL WINAPI SignalHandler::winHandler(DWORD type) { + if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) { + handleSignal(); + return TRUE; + } else { + LOG_PRINT_RED_L0("Got control signal " << type << ". Exiting without saving..."); + return FALSE; + } + return TRUE; + } + +#else + + void SignalHandler::posixHandler(int /*type*/) { + handleSignal(); + } +#endif + + void SignalHandler::handleSignal() { + static std::mutex m_mutex; + std::unique_lock lock(m_mutex); + m_handler(); + } +} diff --git a/src/common/SignalHandler.h b/src/common/SignalHandler.h new file mode 100644 index 0000000000..bfab0b89b6 --- /dev/null +++ b/src/common/SignalHandler.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "misc_os_dependent.h" + +namespace tools { + class SignalHandler + { + public: + template + static bool install(T t) + { +#if defined(WIN32) + bool r = TRUE == ::SetConsoleCtrlHandler(&winHandler, TRUE); + if (r) + { + m_handler = t; + } + return r; +#else + signal(SIGINT, posixHandler); + signal(SIGTERM, posixHandler); + m_handler = t; + return true; +#endif + } + + private: +#if defined(WIN32) + static BOOL WINAPI winHandler(DWORD type); +#else + static void posixHandler(int /*type*/); +#endif + + static void handleSignal(); + + private: + static std::function m_handler; + }; +} diff --git a/src/common/base58.cpp b/src/common/base58.cpp index ea71004a1c..926a56b4ba 100644 --- a/src/common/base58.cpp +++ b/src/common/base58.cpp @@ -23,7 +23,6 @@ #include "crypto/hash.h" #include "int-util.h" -#include "util.h" #include "varint.h" namespace tools diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h index 5d08ee4b6a..7bfac1f8af 100644 --- a/src/common/boost_serialization_helper.h +++ b/src/common/boost_serialization_helper.h @@ -17,9 +17,18 @@ #pragma once +#if defined(WIN32) +#include +#endif + +#include + #include #include +// epee +#include "include_base_utils.h" +#include "misc_os_dependent.h" namespace tools { diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index 20a36fd69e..f8409c5e20 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -21,6 +21,9 @@ #include #include +#include "google/sparse_hash_set" +#include "google/sparse_hash_map" + namespace boost { namespace serialization @@ -108,6 +111,70 @@ namespace boost } } + template + inline void save(Archive &a, const ::google::sparse_hash_set &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v; + } + } + + template + inline void load(Archive &a, ::google::sparse_hash_set &x, const boost::serialization::version_type ver) + { + x.clear(); + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + hval v; + a >> v; + x.insert(v); + } + } + + template + inline void serialize(Archive &a, ::google::sparse_hash_set &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } + + template + inline void save(Archive &a, const ::google::sparse_hash_map &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v.first; + a << v.second; + } + } + + template + inline void load(Archive &a, ::google::sparse_hash_map &x, const boost::serialization::version_type ver) + { + x.clear(); + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + h_key k; + hval v; + a >> k; + a >> v; + x.insert(std::pair(k, v)); + } + } + + template + inline void serialize(Archive &a, ::google::sparse_hash_map &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } template inline void serialize(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) diff --git a/src/common/util.cpp b/src/common/util.cpp index 98f0e807e2..16786d58bf 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -15,12 +15,16 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "util.h" + #include +#include + #include "include_base_utils.h" using namespace epee; -#include "util.h" +#include "p2p/p2p_protocol_defs.h" #include "cryptonote_config.h" #ifdef WIN32 @@ -34,8 +38,6 @@ using namespace epee; namespace tools { - std::function signal_handler::m_handler; - #ifdef WIN32 std::string get_windows_version_display_string() { @@ -311,7 +313,7 @@ std::string get_nix_version_display_string() std::string config_folder; #ifdef WIN32 // Windows - config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CRYPTONOTE_NAME; + config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + cryptonote::CRYPTONOTE_NAME; #else std::string pathRet; char* pszHome = getenv("HOME"); @@ -322,10 +324,10 @@ std::string get_nix_version_display_string() #ifdef MAC_OSX // Mac pathRet /= "Library/Application Support"; - config_folder = (pathRet + "/" + CRYPTONOTE_NAME); + config_folder = (pathRet + "/" + cryptonote::CRYPTONOTE_NAME); #else // Unix - config_folder = (pathRet + "/." + CRYPTONOTE_NAME); + config_folder = (pathRet + "/." + cryptonote::CRYPTONOTE_NAME); #endif #endif @@ -374,4 +376,12 @@ std::string get_nix_version_display_string() #endif return std::error_code(code, std::system_category()); } + + crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) + { + std::string s; + s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); + s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); + return crypto::cn_fast_hash(s.data(), s.size()); + } } diff --git a/src/common/util.h b/src/common/util.h index cac14bc2ff..514bd90f33 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -17,14 +17,15 @@ #pragma once -#include +#include #include -#include -#include "crypto/crypto.h" #include "crypto/hash.h" -#include "misc_language.h" -#include "p2p/p2p_protocol_defs.h" + + +namespace nodetool { + struct proof_of_trust; +} namespace tools { @@ -32,68 +33,5 @@ namespace tools std::string get_os_version_string(); bool create_directories_if_necessary(const std::string& path); std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); - - inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) - { - std::string s; - s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); - s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); - } - - - class signal_handler - { - public: - template - static bool install(T t) - { -#if defined(WIN32) - bool r = TRUE == ::SetConsoleCtrlHandler(&win_handler, TRUE); - if (r) - { - m_handler = t; - } - return r; -#else - signal(SIGINT, posix_handler); - signal(SIGTERM, posix_handler); - m_handler = t; - return true; -#endif - } - - private: -#if defined(WIN32) - static BOOL WINAPI win_handler(DWORD type) - { - if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) - { - handle_signal(); - return TRUE; - } - else - { - LOG_PRINT_RED_L0("Got control signal " << type << ". Exiting without saving..."); - return FALSE; - } - return TRUE; - } -#else - static void posix_handler(int /*type*/) - { - handle_signal(); - } -#endif - - static void handle_signal() - { - static std::mutex m_mutex; - std::unique_lock lock(m_mutex); - m_handler(); - } - - private: - static std::function m_handler; - }; + crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot); } diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp index a0e2ef0e71..68fe82195b 100644 --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/connectivity_tool/conn_tool.cpp @@ -15,26 +15,27 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include +// epee #include "include_base_utils.h" -#include "version.h" - -using namespace epee; -#include -#include "p2p/p2p_protocol_defs.h" -#include "common/command_line.h" -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "net/http_client.h" #include "net/levin_client.h" +#include "storages/http_abstract_invoke.h" #include "storages/levin_abstract_invoke2.h" -#include "cryptonote_core/cryptonote_core.h" #include "storages/portable_storage_template_helper.h" + +#include "common/command_line.h" #include "crypto/crypto.h" -#include "storages/http_abstract_invoke.h" -#include "net/http_client.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/p2p_protocol_defs.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "version.h" namespace po = boost::program_options; using namespace cryptonote; +using namespace epee; using namespace nodetool; namespace diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 1a56787704..18d7147fb7 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -76,3 +76,6 @@ void hash_extra_jh(const void *data, size_t length, char *hash); void hash_extra_skein(const void *data, size_t length, char *hash); void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash); +size_t tree_depth(size_t count); +void tree_branch(const char (*hashes)[HASH_SIZE], size_t count, char (*branch)[HASH_SIZE]); +void tree_hash_from_branch(const char (*branch)[HASH_SIZE], size_t depth, const char *leaf, const void *path, char *root_hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 4eaee990f3..5115e0f95b 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -74,6 +74,14 @@ namespace crypto { tree_hash(reinterpret_cast(hashes), count, reinterpret_cast(&root_hash)); } + inline void tree_branch(const hash *hashes, std::size_t count, hash *branch) { + tree_branch(reinterpret_cast(hashes), count, reinterpret_cast(branch)); + } + + inline void tree_hash_from_branch(const hash *branch, std::size_t depth, const hash &leaf, const void *path, hash &root_hash) { + tree_hash_from_branch(reinterpret_cast(branch), depth, reinterpret_cast(&leaf), path, reinterpret_cast(&root_hash)); + } + } CRYPTO_MAKE_HASHABLE(hash) diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 1d0f684e73..feee98dd6a 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -51,3 +51,75 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { cn_fast_hash(ints[0], 64, root_hash); } } + +size_t tree_depth(size_t count) { + size_t i; + size_t depth = 0; + assert(count > 0); + for (i = sizeof(size_t) << 2; i > 0; i >>= 1) { + if (count >> i > 0) { + count >>= i; + depth += i; + } + } + return depth; +} + +void tree_branch(const char (*hashes)[HASH_SIZE], size_t count, char (*branch)[HASH_SIZE]) { + size_t i, j; + size_t cnt = 1; + size_t depth = 0; + char (*ints)[HASH_SIZE]; + assert(count > 0); + for (i = sizeof(size_t) << 2; i > 0; i >>= 1) { + if (cnt << i <= count) { + cnt <<= i; + depth += i; + } + } + assert(cnt == 1ULL << depth); + assert(depth == tree_depth(count)); + ints = alloca((cnt - 1) * HASH_SIZE); + memcpy(ints, hashes + 1, (2 * cnt - count - 1) * HASH_SIZE); + for (i = 2 * cnt - count, j = 2 * cnt - count - 1; j < cnt - 1; i += 2, ++j) { + cn_fast_hash(hashes[i], 2 * HASH_SIZE, ints[j]); + } + assert(i == count); + while (depth > 0) { + assert(cnt == 1ULL << depth); + cnt >>= 1; + --depth; + memcpy(branch[depth], ints[0], HASH_SIZE); + for (i = 1, j = 0; j < cnt - 1; i += 2, ++j) { + cn_fast_hash(ints[i], 2 * HASH_SIZE, ints[j]); + } + } +} + +void tree_hash_from_branch(const char (*branch)[HASH_SIZE], size_t depth, const char *leaf, const void *path, char *root_hash) { + if (depth == 0) { + memcpy(root_hash, leaf, HASH_SIZE); + } else { + char buffer[2][HASH_SIZE]; + int from_leaf = 1; + char *leaf_path, *branch_path; + while (depth > 0) { + --depth; + if (path && (((const char *) path)[depth >> 3] & (1 << (depth & 7))) != 0) { + leaf_path = buffer[1]; + branch_path = buffer[0]; + } else { + leaf_path = buffer[0]; + branch_path = buffer[1]; + } + if (from_leaf) { + memcpy(leaf_path, leaf, HASH_SIZE); + from_leaf = 0; + } else { + cn_fast_hash(buffer, 2 * HASH_SIZE, leaf_path); + } + memcpy(branch_path, branch[depth], HASH_SIZE); + } + cn_fast_hash(buffer, 2 * HASH_SIZE, root_hash); + } +} diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e7ce4bd334..1b6292feea 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -17,77 +17,133 @@ #pragma once -#define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000 -#define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! -#define CRYPTONOTE_MAX_TX_SIZE 1000000000 -#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 -#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 6 // addresses start with "2" -#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 10 -#define CURRENT_TRANSACTION_VERSION 1 -#define CURRENT_BLOCK_MAJOR_VERSION 1 -#define CURRENT_BLOCK_MINOR_VERSION 0 -#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 - -#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60 - -// MONEY_SUPPLY - total number coins to be generated -#define MONEY_SUPPLY ((uint64_t)(-1)) -#define EMISSION_SPEED_FACTOR (18) - -#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 -#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size -#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 -#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 -// COIN - number of smallest units in one coin -#define COIN ((uint64_t)100000000) // pow(10, 8) -#define MINIMUM_FEE ((uint64_t)1000000000) // pow(10, 9) -#define DEFAULT_DUST_THRESHOLD ((uint64_t)1000000) // pow(10, 6) - - -#define DIFFICULTY_TARGET 120 // seconds -#define DIFFICULTY_WINDOW 720 // blocks -#define DIFFICULTY_LAG 15 // !!! -#define DIFFICULTY_CUT 60 // timestamps to cut after sorting -#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG - - -#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS -#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1 +#include +namespace cryptonote { +namespace parameters { -#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN DIFFICULTY_TARGET //just alias +const uint64_t CRYPTONOTE_MAX_BLOCK_NUMBER = 500000000; +const size_t CRYPTONOTE_MAX_BLOCK_BLOB_SIZE = 500000000; +const size_t CRYPTONOTE_MAX_TX_SIZE = 1000000000; +const uint64_t CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 6; // addresses start with "2" +const size_t CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW = 10; +const uint64_t CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT = 60 * 60 * 2; +const size_t BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW = 60; -#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing -#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading -#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block - - -#define P2P_DEFAULT_PORT 8080 -#define RPC_DEFAULT_PORT 8081 -#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 - -#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 -#define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 - -#define P2P_DEFAULT_CONNECTIONS_COUNT 8 -#define P2P_DEFAULT_HANDSHAKE_INTERVAL 60 //secondes -#define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size -#define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250 -#define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds -#define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds -#define P2P_DEFAULT_INVOKE_TIMEOUT 60*2*1000 //2 minutes -#define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds -#define P2P_STAT_TRUSTED_PUB_KEY "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115" -#define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 +// MONEY_SUPPLY - total number coins to be generated +const uint64_t MONEY_SUPPLY = static_cast(-1); +const unsigned EMISSION_SPEED_FACTOR = 18; +static_assert(EMISSION_SPEED_FACTOR <= 8 * sizeof(uint64_t), "Bad EMISSION_SPEED_FACTOR"); + +const size_t CRYPTONOTE_REWARD_BLOCKS_WINDOW = 100; +const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = 10000; //size of block (bytes) after which reward for block calculated using block size +const size_t CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE = 600; +const size_t CRYPTONOTE_DISPLAY_DECIMAL_POINT = 8; +// COIN - number of smallest units in one coin +const uint64_t COIN = UINT64_C(100000000); // pow(10, 8) +const uint64_t MINIMUM_FEE = UINT64_C(1000000); // pow(10, 6) +const uint64_t DEFAULT_DUST_THRESHOLD = UINT64_C(1000000); // pow(10, 6) + +const uint64_t DIFFICULTY_TARGET = 120; // seconds +const uint64_t EXPECTED_NUMBER_OF_BLOCKS_PER_DAY = 24 * 60 * 60 / DIFFICULTY_TARGET; +const size_t DIFFICULTY_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks +const size_t DIFFICULTY_CUT = 60; // timestamps to cut after sorting +const size_t DIFFICULTY_LAG = 15; // !!! +static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Bad DIFFICULTY_WINDOW or DIFFICULTY_CUT"); + +const size_t MAX_BLOCK_SIZE_INITIAL = 20 * 1024; +const uint64_t MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR = 100 * 1024; +const uint64_t MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR = 365 * 24 * 60 * 60 / DIFFICULTY_TARGET; + +const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS = 1; +const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS = DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS; + +const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24; //seconds, one day +const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week + +const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent +const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks +const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks +static_assert(0 < UPGRADE_VOTING_THRESHOLD && UPGRADE_VOTING_THRESHOLD <= 100, "Bad UPGRADE_VOTING_THRESHOLD"); +static_assert(UPGRADE_VOTING_WINDOW > 1, "Bad UPGRADE_VOTING_WINDOW"); + +const char CRYPTONOTE_BLOCKCHAINDATA_FILENAME[] = "blockchain.bin"; // Obsolete blockchain format +const char CRYPTONOTE_BLOCKS_FILENAME[] = "blocks.dat"; +const char CRYPTONOTE_BLOCKINDEXES_FILENAME[] = "blockindexes.dat"; +const char CRYPTONOTE_BLOCKSCACHE_FILENAME[] = "blockscache.dat"; +const char CRYPTONOTE_POOLDATA_FILENAME[] = "poolstate.bin"; +const char P2P_NET_DATA_FILENAME[] = "p2pstate.bin"; +const char MINER_CONFIG_FILE_NAME[] = "miner_conf.json"; +} // parameters + +const char CRYPTONOTE_NAME[] = "bytecoin"; + +const uint8_t CURRENT_TRANSACTION_VERSION = 1; +const uint8_t BLOCK_MAJOR_VERSION_1 = 1; +const uint8_t BLOCK_MAJOR_VERSION_2 = 2; +const uint8_t BLOCK_MINOR_VERSION_0 = 0; +const uint8_t BLOCK_MINOR_VERSION_1 = 1; + +const size_t BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT = 10000; //by default, blocks ids count in synchronizing +const size_t BLOCKS_SYNCHRONIZING_DEFAULT_COUNT = 200; //by default, blocks count in blocks downloading +const size_t COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT = 1000; + +const int P2P_DEFAULT_PORT = 8080; +const int RPC_DEFAULT_PORT = 8081; + +const size_t P2P_LOCAL_WHITE_PEERLIST_LIMIT = 1000; +const size_t P2P_LOCAL_GRAY_PEERLIST_LIMIT = 5000; + +const uint32_t P2P_DEFAULT_CONNECTIONS_COUNT = 8; +const uint32_t P2P_DEFAULT_HANDSHAKE_INTERVAL = 60; // seconds +const uint32_t P2P_DEFAULT_PACKET_MAX_SIZE = 50000000; // 50000000 bytes maximum packet size +const uint32_t P2P_DEFAULT_PEERS_IN_HANDSHAKE = 250; +const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // 5 seconds +const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds +const uint64_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes +const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds +const char P2P_STAT_TRUSTED_PUB_KEY[] = "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115"; +const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; + +const unsigned THREAD_STACK_SIZE = 5 * 1024 * 1024; + +const char* const SEED_NODES[] = { + "seed.bytecoin.org:8080", + "85.25.201.95:8080", + "85.25.196.145:8080", + "85.25.196.146:8080", + "85.25.196.144:8080", + "5.199.168.138:8080", + "62.75.236.152:8080", + "85.25.194.245:8080", + "95.211.224.160:8080", + "144.76.200.44:8080" +}; + +struct CheckpointData { + uint64_t height; + const char* blockId; +}; + +const CheckpointData CHECKPOINTS[] = { + {79000, "cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02"}, + {140000, "993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772"}, + {200000, "a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073"}, + {230580, "32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103"}, + {260000, "f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947"}, + {300000, "8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff"}, + {390285, "e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7"}, + {417000, "2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f"}, + {427193, "00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311"}, + {453537, "d17de6916c5aa6ffcae575309c80b0f8fdcd0a84b5fa8e41a841897d4b5a4e97"}, + {462250, "13468d210a5ec884cf839f0259f247ccf3efef0414ac45172033d32c739beb3e"}, + {468000, "251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1"}, + {480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"}, + {484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"}, + {506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"}, + {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"} +}; +} // cryptonote #define ALLOW_DEBUG_COMMANDS - -#define CRYPTONOTE_NAME "bytecoin" -#define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" -#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "blockchain.bin" -#define CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME "blockchain.bin.tmp" -#define P2P_NET_DATA_FILENAME "p2pstate.bin" -#define MINER_CONFIG_FILE_NAME "miner_conf.json" - -#define THREAD_STACK_SIZE 5 * 1024 * 1024 diff --git a/src/cryptonote_core/AccountKVSerialization.h b/src/cryptonote_core/AccountKVSerialization.h new file mode 100644 index 0000000000..31d65f76e8 --- /dev/null +++ b/src/cryptonote_core/AccountKVSerialization.h @@ -0,0 +1,113 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/account.h" + +// epee +#include "serialization/keyvalue_serialization.h" + +namespace cryptonote { + template struct AccountPublicAddressSerializer; + template struct AccountKeysSerializer; + template struct AccountBaseSerializer; + + template<> + struct AccountPublicAddressSerializer { + const AccountPublicAddress& m_account_address; + + AccountPublicAddressSerializer(const AccountPublicAddress& account_address) : m_account_address(account_address) { + } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_spendPublicKey, "m_spend_public_key") + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_viewPublicKey, "m_view_public_key") + END_KV_SERIALIZE_MAP() + }; + + template<> + struct AccountPublicAddressSerializer { + AccountPublicAddress& m_account_address; + + AccountPublicAddressSerializer(AccountPublicAddress& account_address) : m_account_address(account_address) { + } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_spendPublicKey, "m_spend_public_key") + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_viewPublicKey, "m_view_public_key") + END_KV_SERIALIZE_MAP() + }; + + template<> + struct AccountKeysSerializer { + const account_keys& m_keys; + + AccountKeysSerializer(const account_keys& keys) : m_keys(keys) { + } + + BEGIN_KV_SERIALIZE_MAP() + AccountPublicAddressSerializer addressSerializer(this_ref.m_keys.m_account_address); + epee::serialization::selector::serialize(addressSerializer, stg, hparent_section, "m_account_address"); + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_spend_secret_key, "m_spend_secret_key") + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_view_secret_key, "m_view_secret_key") + END_KV_SERIALIZE_MAP() + }; + + template<> + struct AccountKeysSerializer { + account_keys& m_keys; + + AccountKeysSerializer(account_keys& keys) : m_keys(keys) { + } + + BEGIN_KV_SERIALIZE_MAP() + AccountPublicAddressSerializer addressSerializer(this_ref.m_keys.m_account_address); + epee::serialization::selector::serialize(addressSerializer, stg, hparent_section, "m_account_address"); + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_spend_secret_key, "m_spend_secret_key") + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_view_secret_key, "m_view_secret_key") + END_KV_SERIALIZE_MAP() + }; + + template<> + struct AccountBaseSerializer { + const account_base& m_account; + + AccountBaseSerializer(const account_base& account) : m_account(account) { + } + + BEGIN_KV_SERIALIZE_MAP() + AccountKeysSerializer keysSerializer(this_ref.m_account.m_keys); + epee::serialization::selector::serialize(keysSerializer, stg, hparent_section, "m_keys"); + KV_SERIALIZE_N(m_account.m_creation_timestamp, "m_creation_timestamp") + END_KV_SERIALIZE_MAP() + }; + + template<> + struct AccountBaseSerializer { + account_base& m_account; + + AccountBaseSerializer(account_base& account) : m_account(account) { + } + + BEGIN_KV_SERIALIZE_MAP() + AccountKeysSerializer keysSerializer(this_ref.m_account.m_keys); + epee::serialization::selector::serialize(keysSerializer, stg, hparent_section, "m_keys"); + KV_SERIALIZE_N(m_account.m_creation_timestamp, "m_creation_timestamp") + END_KV_SERIALIZE_MAP() + }; +} diff --git a/src/cryptonote_core/BlockIndex.cpp b/src/cryptonote_core/BlockIndex.cpp new file mode 100644 index 0000000000..984a8bb689 --- /dev/null +++ b/src/cryptonote_core/BlockIndex.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockIndex.h" +#include + +namespace CryptoNote +{ + crypto::hash BlockIndex::getBlockId(uint64_t height) const { + if (height >= m_container.size()) + return boost::value_initialized(); + return m_container[static_cast(height)]; + } + + bool BlockIndex::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) const { + if (startHeight >= m_container.size()) + return false; + + for (size_t i = startHeight; i < (startHeight + maxCount) && i < m_container.size(); ++i) { + items.push_back(m_container[i]); + } + + return true; + } + + + bool BlockIndex::findSupplement(const std::list& ids, uint64_t& offset) const { + + for (const auto& id : ids) { + if (getBlockHeight(id, offset)) + return true; + } + + return false; + } + + bool BlockIndex::getShortChainHistory(std::list& ids) const { + size_t i = 0; + size_t current_multiplier = 1; + size_t sz = size(); + + if (!sz) + return true; + + size_t current_back_offset = 1; + bool genesis_included = false; + + while (current_back_offset < sz) { + ids.push_back(m_container[sz - current_back_offset]); + if (sz - current_back_offset == 0) + genesis_included = true; + if (i < 10) { + ++current_back_offset; + } else { + current_back_offset += current_multiplier *= 2; + } + ++i; + } + + if (!genesis_included) + ids.push_back(m_container[0]); + + return true; + } + + crypto::hash BlockIndex::getTailId() const { + if (m_container.empty()) + return boost::value_initialized(); + return m_container.back(); + } + + +} diff --git a/src/cryptonote_core/BlockIndex.h b/src/cryptonote_core/BlockIndex.h new file mode 100644 index 0000000000..382e6e01c6 --- /dev/null +++ b/src/cryptonote_core/BlockIndex.h @@ -0,0 +1,90 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// multi index +#include +#include +#include + +#include "crypto/hash.h" +#include + +namespace CryptoNote +{ + class BlockIndex { + + public: + + BlockIndex() : + m_index(m_container.get<1>()) {} + + void pop() { + m_container.pop_back(); + } + + // returns true if new element was inserted, false if already exists + bool push(const crypto::hash& h) { + auto result = m_container.push_back(h); + return result.second; + } + + bool hasBlock(const crypto::hash& h) const { + return m_index.find(h) != m_index.end(); + } + + bool getBlockHeight(const crypto::hash& h, uint64_t& height) const { + auto hi = m_index.find(h); + if (hi == m_index.end()) + return false; + + height = std::distance(m_container.begin(), m_container.project<0>(hi)); + return true; + } + + size_t size() const { + return m_container.size(); + } + + void clear() { + m_container.clear(); + } + + crypto::hash getBlockId(uint64_t height) const; + bool getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) const; + bool findSupplement(const std::list& ids, uint64_t& offset) const; + bool getShortChainHistory(std::list& ids) const; + crypto::hash getTailId() const; + + template void serialize(Archive& ar, const unsigned int version) { + ar & m_container; + } + + private: + + typedef boost::multi_index_container < + crypto::hash, + boost::multi_index::indexed_by< + boost::multi_index::random_access<>, + boost::multi_index::hashed_unique> + > + > ContainerT; + + ContainerT m_container; + ContainerT::nth_index<1>::type& m_index; + + }; +} diff --git a/src/cryptonote_core/Currency.cpp b/src/cryptonote_core/Currency.cpp new file mode 100644 index 0000000000..aac4338b50 --- /dev/null +++ b/src/cryptonote_core/Currency.cpp @@ -0,0 +1,435 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Currency.h" + +#include +#include + +// epee +#include "include_base_utils.h" +#include "string_tools.h" + +#include "common/base58.h" +#include "common/int-util.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/UpgradeDetector.h" + +namespace cryptonote { + bool Currency::init() { + bool r; + r = generateGenesisBlock(); + CHECK_AND_ASSERT_MES(r, false, "Failed to generate genesis block"); + + r = get_block_hash(m_genesisBlock, m_genesisBlockHash); + CHECK_AND_ASSERT_MES(r, false, "Failed to get genesis block hash"); + + if (isTestnet()) { + m_blocksFileName = "testnet_" + m_blocksFileName; + m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; + m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; + m_txPoolFileName = "testnet_" + m_txPoolFileName; + } + + return true; + } + + bool Currency::generateGenesisBlock() { + m_genesisBlock = boost::value_initialized(); + + //account_public_address ac = boost::value_initialized(); + //std::vector sz; + //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.minerTx); // zero fee in genesis + //blobdata txb = tx_to_blob(m_genesisBlock.minerTx); + //std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + // Hard code coinbase tx in genesis block, because through generating tx use random, but genesis should be always the same + std::string genesisCoinbaseTxHex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + + blobdata minerTxBlob; + epee::string_tools::parse_hexstr_to_binbuff(genesisCoinbaseTxHex, minerTxBlob); + bool r = parse_and_validate_tx_from_blob(minerTxBlob, m_genesisBlock.minerTx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + m_genesisBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + m_genesisBlock.minorVersion = BLOCK_MINOR_VERSION_0; + m_genesisBlock.timestamp = 0; + m_genesisBlock.nonce = 70; + if (m_testnet) { + ++m_genesisBlock.nonce; + } + //miner::find_nonce_for_given_block(bl, 1, 0); + + return true; + } + + bool Currency::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, + uint64_t fee, bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const { + assert(alreadyGeneratedCoins <= m_moneySupply); + assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t)); + + uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor; + + medianSize = std::max(medianSize, m_blockGrantedFullRewardZone); + if (currentBlockSize > UINT64_C(2) * medianSize) { + LOG_PRINT_L4("Block cumulative size is too big: " << currentBlockSize << ", expected less than " << 2 * medianSize); + return false; + } + + uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize); + uint64_t penalizedFee = penalizeFee ? getPenalizedAmount(fee, medianSize, currentBlockSize) : fee; + + emissionChange = penalizedBaseReward - (fee - penalizedFee); + reward = penalizedBaseReward + penalizedFee; + + return true; + } + + size_t Currency::maxBlockCumulativeSize(uint64_t height) const { + assert(height <= std::numeric_limits::max() / m_maxBlockSizeGrowthSpeedNumerator); + size_t maxSize = static_cast(m_maxBlockSizeInitial + + (height * m_maxBlockSizeGrowthSpeedNumerator) / m_maxBlockSizeGrowthSpeedDenominator); + assert(maxSize >= m_maxBlockSizeInitial); + return maxSize; + } + + bool Currency::constructMinerTx(size_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, + uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, + const blobdata& extraNonce/* = blobdata()*/, size_t maxOuts/* = 1*/, + bool penalizeFee/* = false*/) const { + tx.vin.clear(); + tx.vout.clear(); + tx.extra.clear(); + + KeyPair txkey = KeyPair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + if (!extraNonce.empty()) { + if (!add_extra_nonce_to_tx_extra(tx.extra, extraNonce)) { + return false; + } + } + + TransactionInputGenerate in; + in.height = height; + + uint64_t blockReward; + int64_t emissionChange; + if (!getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, blockReward, emissionChange)) { + LOG_PRINT_L0("Block is too big"); + return false; + } +#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) + LOG_PRINT_L1("Creating block template: reward " << blockReward << ", fee " << fee); +#endif + + std::vector outAmounts; + decompose_amount_into_digits(blockReward, m_defaultDustThreshold, + [&outAmounts](uint64_t a_chunk) { outAmounts.push_back(a_chunk); }, + [&outAmounts](uint64_t a_dust) { outAmounts.push_back(a_dust); }); + + CHECK_AND_ASSERT_MES(1 <= maxOuts, false, "max_out must be non-zero"); + while (maxOuts < outAmounts.size()) { + outAmounts[outAmounts.size() - 2] += outAmounts.back(); + outAmounts.resize(outAmounts.size() - 1); + } + + uint64_t summaryAmounts = 0; + for (size_t no = 0; no < outAmounts.size(); no++) { + crypto::key_derivation derivation = boost::value_initialized(); + crypto::public_key outEphemeralPubKey = boost::value_initialized(); + bool r = crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << + minerAddress.m_viewPublicKey << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, no, minerAddress.m_spendPublicKey, outEphemeralPubKey); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << + no << ", "<< minerAddress.m_spendPublicKey << ")"); + + TransactionOutputToKey tk; + tk.key = outEphemeralPubKey; + + TransactionOutput out; + summaryAmounts += out.amount = outAmounts[no]; + out.target = tk; + tx.vout.push_back(out); + } + + CHECK_AND_ASSERT_MES(summaryAmounts == blockReward, false, + "Failed to construct miner tx, summaryAmounts = " << summaryAmounts << " not equal blockReward = " << blockReward); + + tx.version = CURRENT_TRANSACTION_VERSION; + //lock + tx.unlockTime = height + m_minedMoneyUnlockWindow; + tx.vin.push_back(in); + return true; + } + + std::string Currency::accountAddressAsString(const account_base& account) const { + return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.get_keys().m_account_address); + } + + bool Currency::parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const { + uint64_t prefix; + if (!cryptonote::parseAccountAddressString(prefix, addr, str)) { + return false; + } + + if (prefix != m_publicAddressBase58Prefix) { + LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << m_publicAddressBase58Prefix); + return false; + } + + return true; + } + + std::string Currency::formatAmount(uint64_t amount) const { + std::string s = std::to_string(amount); + if (s.size() < m_numberOfDecimalPlaces + 1) { + s.insert(0, m_numberOfDecimalPlaces + 1 - s.size(), '0'); + } + s.insert(s.size() - m_numberOfDecimalPlaces, "."); + return s; + } + + bool Currency::parseAmount(const std::string& str, uint64_t& amount) const { + std::string strAmount = str; + boost::algorithm::trim(strAmount); + + size_t pointIndex = strAmount.find_first_of('.'); + size_t fractionSize; + if (std::string::npos != pointIndex) { + fractionSize = strAmount.size() - pointIndex - 1; + while (m_numberOfDecimalPlaces < fractionSize && '0' == strAmount.back()) { + strAmount.erase(strAmount.size() - 1, 1); + --fractionSize; + } + if (m_numberOfDecimalPlaces < fractionSize) { + return false; + } + strAmount.erase(pointIndex, 1); + } else { + fractionSize = 0; + } + + if (strAmount.empty()) { + return false; + } + + if (fractionSize < m_numberOfDecimalPlaces) { + strAmount.append(m_numberOfDecimalPlaces - fractionSize, '0'); + } + + return epee::string_tools::get_xtype_from_string(amount, strAmount); + } + + difficulty_type Currency::nextDifficulty(std::vector timestamps, + std::vector cumulativeDifficulties) const { + assert(m_difficultyWindow >= 2); + + if (timestamps.size() > m_difficultyWindow) { + timestamps.resize(m_difficultyWindow); + cumulativeDifficulties.resize(m_difficultyWindow); + } + + size_t length = timestamps.size(); + assert(length == cumulativeDifficulties.size()); + assert(length <= m_difficultyWindow); + if (length <= 1) { + return 1; + } + + sort(timestamps.begin(), timestamps.end()); + + size_t cutBegin, cutEnd; + assert(2 * m_difficultyCut <= m_difficultyWindow - 2); + if (length <= m_difficultyWindow - 2 * m_difficultyCut) { + cutBegin = 0; + cutEnd = length; + } else { + cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2; + cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut); + } + assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length); + uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin]; + if (timeSpan == 0) { + timeSpan = 1; + } + + difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin]; + assert(totalWork > 0); + + uint64_t low, high; + low = mul128(totalWork, m_difficultyTarget, &high); + if (high != 0 || low + timeSpan - 1 < low) { + return 0; + } + + return (low + timeSpan - 1) / timeSpan; + } + + bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + crypto::hash& proofOfWork) const { + if (BLOCK_MAJOR_VERSION_1 != block.majorVersion) { + return false; + } + + if (!get_block_longhash(context, block, proofOfWork)) { + return false; + } + + return check_hash(proofOfWork, currentDiffic); + } + + bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + crypto::hash& proofOfWork) const { + if (BLOCK_MAJOR_VERSION_2 != block.majorVersion) { + return false; + } + + if (!get_block_longhash(context, block, proofOfWork)) { + return false; + } + + if (!check_hash(proofOfWork, currentDiffic)) { + return false; + } + + tx_extra_merge_mining_tag mmTag; + if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mmTag)) { + LOG_ERROR("merge mining tag wasn't found in extra of the parent block miner transaction"); + return false; + } + + if (8 * sizeof(m_genesisBlockHash) < block.parentBlock.blockchainBranch.size()) { + return false; + } + + crypto::hash auxBlockHeaderHash; + if (!get_aux_block_header_hash(block, auxBlockHeaderHash)) { + return false; + } + + crypto::hash auxBlocksMerkleRoot; + crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), + auxBlockHeaderHash, &m_genesisBlockHash, auxBlocksMerkleRoot); + CHECK_AND_NO_ASSERT_MES(auxBlocksMerkleRoot == mmTag.merkle_root, false, "Aux block hash wasn't found in merkle tree"); + + return true; + } + + bool Currency::checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const { + switch (block.majorVersion) { + case BLOCK_MAJOR_VERSION_1: return checkProofOfWorkV1(context, block, currentDiffic, proofOfWork); + case BLOCK_MAJOR_VERSION_2: return checkProofOfWorkV2(context, block, currentDiffic, proofOfWork); + } + + CHECK_AND_ASSERT_MES(false, false, "Unknown block major version: " << block.majorVersion << "." << block.minorVersion); + } + + CurrencyBuilder::CurrencyBuilder() { + maxBlockNumber(parameters::CRYPTONOTE_MAX_BLOCK_NUMBER); + maxBlockBlobSize(parameters::CRYPTONOTE_MAX_BLOCK_BLOB_SIZE); + maxTxSize(parameters::CRYPTONOTE_MAX_TX_SIZE); + publicAddressBase58Prefix(parameters::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + minedMoneyUnlockWindow(parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + + timestampCheckWindow(parameters::BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW); + blockFutureTimeLimit(parameters::CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT); + + moneySupply(parameters::MONEY_SUPPLY); + emissionSpeedFactor(parameters::EMISSION_SPEED_FACTOR); + + rewardBlocksWindow(parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW); + blockGrantedFullRewardZone(parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); + minerTxBlobReservedSize(parameters::CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + + numberOfDecimalPlaces(parameters::CRYPTONOTE_DISPLAY_DECIMAL_POINT); + + mininumFee(parameters::MINIMUM_FEE); + defaultDustThreshold(parameters::DEFAULT_DUST_THRESHOLD); + + difficultyTarget(parameters::DIFFICULTY_TARGET); + difficultyWindow(parameters::DIFFICULTY_WINDOW); + difficultyLag(parameters::DIFFICULTY_LAG); + difficultyCut(parameters::DIFFICULTY_CUT); + + maxBlockSizeInitial(parameters::MAX_BLOCK_SIZE_INITIAL); + maxBlockSizeGrowthSpeedNumerator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR); + maxBlockSizeGrowthSpeedDenominator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR); + + lockedTxAllowedDeltaSeconds(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS); + lockedTxAllowedDeltaBlocks(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS); + + mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME); + mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); + + upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT); + upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); + upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); + upgradeWindow(parameters::UPGRADE_WINDOW); + + blocksFileName(parameters::CRYPTONOTE_BLOCKS_FILENAME); + blocksCacheFileName(parameters::CRYPTONOTE_BLOCKSCACHE_FILENAME); + blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME); + txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME); + + testnet(false); + } + + CurrencyBuilder& CurrencyBuilder::emissionSpeedFactor(unsigned int val) { + if (val <= 0 || val > 8 * sizeof(uint64_t)) { + throw std::invalid_argument("val at emissionSpeedFactor()"); + } + + m_currency.m_emissionSpeedFactor = val; + return *this; + } + + CurrencyBuilder& CurrencyBuilder::numberOfDecimalPlaces(size_t val) { + m_currency.m_numberOfDecimalPlaces = val; + m_currency.m_coin = 1; + for (size_t i = 0; i < m_currency.m_numberOfDecimalPlaces; ++i) { + m_currency.m_coin *= 10; + } + + return *this; + } + + CurrencyBuilder& CurrencyBuilder::difficultyWindow(size_t val) { + if (val < 2) { + throw std::invalid_argument("val at difficultyWindow()"); + } + m_currency.m_difficultyWindow = val; + return *this; + } + + CurrencyBuilder& CurrencyBuilder::upgradeVotingThreshold(unsigned int val) { + if (val <= 0 || val > 100) { + throw std::invalid_argument("val at upgradeVotingThreshold()"); + } + m_currency.m_upgradeVotingThreshold = val; + return *this; + } + + CurrencyBuilder& CurrencyBuilder::upgradeWindow(size_t val) { + if (val <= 0) { + throw std::invalid_argument("val at upgradeWindow()"); + } + m_currency.m_upgradeWindow = val; + return *this; + } +} diff --git a/src/cryptonote_core/Currency.h b/src/cryptonote_core/Currency.h new file mode 100644 index 0000000000..bbdffd3980 --- /dev/null +++ b/src/cryptonote_core/Currency.h @@ -0,0 +1,238 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include + +#include "crypto/hash.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/difficulty.h" +#include "cryptonote_config.h" +#include "cryptonote_protocol/blobdatatype.h" + +namespace cryptonote +{ + class Currency { + public: + uint64_t maxBlockHeight() const { return m_maxBlockHeight; } + size_t maxBlockBlobSize() const { return m_maxBlockBlobSize; } + size_t maxTxSize() const { return m_maxTxSize; } + uint64_t publicAddressBase58Prefix() const { return m_publicAddressBase58Prefix; } + size_t minedMoneyUnlockWindow() const { return m_minedMoneyUnlockWindow; } + + size_t timestampCheckWindow() const { return m_timestampCheckWindow; } + uint64_t blockFutureTimeLimit() const { return m_blockFutureTimeLimit; } + + uint64_t moneySupply() const { return m_moneySupply; } + unsigned int emissionSpeedFactor() const { return m_emissionSpeedFactor; } + + size_t rewardBlocksWindow() const { return m_rewardBlocksWindow; } + size_t blockGrantedFullRewardZone() const { return m_blockGrantedFullRewardZone; } + size_t minerTxBlobReservedSize() const { return m_minerTxBlobReservedSize; } + + size_t numberOfDecimalPlaces() const { return m_numberOfDecimalPlaces; } + uint64_t coin() const { return m_coin; } + + uint64_t minimumFee() const { return m_mininumFee; } + uint64_t defaultDustThreshold() const { return m_defaultDustThreshold; } + + uint64_t difficultyTarget() const { return m_difficultyTarget; } + size_t difficultyWindow() const { return m_difficultyWindow; } + size_t difficultyLag() const { return m_difficultyLag; } + size_t difficultyCut() const { return m_difficultyCut; } + size_t difficultyBlocksCount() const { return m_difficultyWindow + m_difficultyLag; } + + size_t maxBlockSizeInitial() const { return m_maxBlockSizeInitial; } + uint64_t maxBlockSizeGrowthSpeedNumerator() const { return m_maxBlockSizeGrowthSpeedNumerator; } + uint64_t maxBlockSizeGrowthSpeedDenominator() const { return m_maxBlockSizeGrowthSpeedDenominator; } + + uint64_t lockedTxAllowedDeltaSeconds() const { return m_lockedTxAllowedDeltaSeconds; } + size_t lockedTxAllowedDeltaBlocks() const { return m_lockedTxAllowedDeltaBlocks; } + + uint64_t mempoolTxLiveTime() const { return m_mempoolTxLiveTime; } + uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; } + + uint64_t upgradeHeight() const { return m_upgradeHeight; } + unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; } + size_t upgradeVotingWindow() const { return m_upgradeVotingWindow; } + size_t upgradeWindow() const { return m_upgradeWindow; } + size_t minNumberVotingBlocks() const { return (m_upgradeVotingWindow * m_upgradeVotingThreshold + 99) / 100; } + uint64_t maxUpgradeDistance() const { return static_cast(m_upgradeWindow); } + uint64_t calculateUpgradeHeight(uint64_t voteCompleteHeight) const { return voteCompleteHeight + m_upgradeWindow; } + + const std::string& blocksFileName() const { return m_blocksFileName; } + const std::string& blocksCacheFileName() const { return m_blocksCacheFileName; } + const std::string& blockIndexesFileName() const { return m_blockIndexesFileName; } + const std::string& txPoolFileName() const { return m_txPoolFileName; } + + bool isTestnet() const { return m_testnet; } + + const Block& genesisBlock() const { return m_genesisBlock; } + const crypto::hash& genesisBlockHash() const { return m_genesisBlockHash; } + + bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const; + size_t maxBlockCumulativeSize(uint64_t height) const; + + bool constructMinerTx(size_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, + uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, + const blobdata& extraNonce = blobdata(), size_t maxOuts = 1, bool penalizeFee = false) const; + + std::string accountAddressAsString(const account_base& account) const; + bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; + + std::string formatAmount(uint64_t amount) const; + bool parseAmount(const std::string& str, uint64_t& amount) const; + + difficulty_type nextDifficulty(std::vector timestamps, std::vector cumulativeDifficulties) const; + + bool checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + + private: + Currency() { + } + + bool init(); + + bool generateGenesisBlock(); + + private: + uint64_t m_maxBlockHeight; + size_t m_maxBlockBlobSize; + size_t m_maxTxSize; + uint64_t m_publicAddressBase58Prefix; + size_t m_minedMoneyUnlockWindow; + + size_t m_timestampCheckWindow; + uint64_t m_blockFutureTimeLimit; + + uint64_t m_moneySupply; + unsigned int m_emissionSpeedFactor; + + size_t m_rewardBlocksWindow; + size_t m_blockGrantedFullRewardZone; + size_t m_minerTxBlobReservedSize; + + size_t m_numberOfDecimalPlaces; + uint64_t m_coin; + + uint64_t m_mininumFee; + uint64_t m_defaultDustThreshold; + + uint64_t m_difficultyTarget; + size_t m_difficultyWindow; + size_t m_difficultyLag; + size_t m_difficultyCut; + + size_t m_maxBlockSizeInitial; + uint64_t m_maxBlockSizeGrowthSpeedNumerator; + uint64_t m_maxBlockSizeGrowthSpeedDenominator; + + uint64_t m_lockedTxAllowedDeltaSeconds; + size_t m_lockedTxAllowedDeltaBlocks; + + uint64_t m_mempoolTxLiveTime; + uint64_t m_mempoolTxFromAltBlockLiveTime; + + uint64_t m_upgradeHeight; + unsigned int m_upgradeVotingThreshold; + size_t m_upgradeVotingWindow; + size_t m_upgradeWindow; + + std::string m_blocksFileName; + std::string m_blocksCacheFileName; + std::string m_blockIndexesFileName; + std::string m_txPoolFileName; + + bool m_testnet; + + Block m_genesisBlock; + crypto::hash m_genesisBlockHash; + + friend class CurrencyBuilder; + }; + + class CurrencyBuilder : boost::noncopyable { + public: + CurrencyBuilder(); + + Currency currency() { + if (!m_currency.init()) { + throw std::runtime_error("Failed to initialize currency object"); + } + return m_currency; + } + + CurrencyBuilder& maxBlockNumber(uint64_t val) { m_currency.m_maxBlockHeight = val; return *this; } + CurrencyBuilder& maxBlockBlobSize(size_t val) { m_currency.m_maxBlockBlobSize = val; return *this; } + CurrencyBuilder& maxTxSize(size_t val) { m_currency.m_maxTxSize = val; return *this; } + CurrencyBuilder& publicAddressBase58Prefix(uint64_t val) { m_currency.m_publicAddressBase58Prefix = val; return *this; } + CurrencyBuilder& minedMoneyUnlockWindow(size_t val) { m_currency.m_minedMoneyUnlockWindow = val; return *this; } + + CurrencyBuilder& timestampCheckWindow(size_t val) { m_currency.m_timestampCheckWindow = val; return *this; } + CurrencyBuilder& blockFutureTimeLimit(uint64_t val) { m_currency.m_blockFutureTimeLimit = val; return *this; } + + CurrencyBuilder& moneySupply(uint64_t val) { m_currency.m_moneySupply = val; return *this; } + CurrencyBuilder& emissionSpeedFactor(unsigned int val); + + CurrencyBuilder& rewardBlocksWindow(size_t val) { m_currency.m_rewardBlocksWindow = val; return *this; } + CurrencyBuilder& blockGrantedFullRewardZone(size_t val) { m_currency.m_blockGrantedFullRewardZone = val; return *this; } + CurrencyBuilder& minerTxBlobReservedSize(size_t val) { m_currency.m_minerTxBlobReservedSize = val; return *this; } + + CurrencyBuilder& numberOfDecimalPlaces(size_t val); + + CurrencyBuilder& mininumFee(uint64_t val) { m_currency.m_mininumFee = val; return *this; } + CurrencyBuilder& defaultDustThreshold(uint64_t val) { m_currency.m_defaultDustThreshold = val; return *this; } + + CurrencyBuilder& difficultyTarget(uint64_t val) { m_currency.m_difficultyTarget = val; return *this; } + CurrencyBuilder& difficultyWindow(size_t val); + CurrencyBuilder& difficultyLag(size_t val) { m_currency.m_difficultyLag = val; return *this; } + CurrencyBuilder& difficultyCut(size_t val) { m_currency.m_difficultyCut = val; return *this; } + + CurrencyBuilder& maxBlockSizeInitial(size_t val) { m_currency.m_maxBlockSizeInitial = val; return *this; } + CurrencyBuilder& maxBlockSizeGrowthSpeedNumerator(uint64_t val) { m_currency.m_maxBlockSizeGrowthSpeedNumerator = val; return *this; } + CurrencyBuilder& maxBlockSizeGrowthSpeedDenominator(uint64_t val) { m_currency.m_maxBlockSizeGrowthSpeedDenominator = val; return *this; } + + CurrencyBuilder& lockedTxAllowedDeltaSeconds(uint64_t val) { m_currency.m_lockedTxAllowedDeltaSeconds = val; return *this; } + CurrencyBuilder& lockedTxAllowedDeltaBlocks(size_t val) { m_currency.m_lockedTxAllowedDeltaBlocks = val; return *this; } + + CurrencyBuilder& mempoolTxLiveTime(uint64_t val) { m_currency.m_mempoolTxLiveTime = val; return *this; } + CurrencyBuilder& mempoolTxFromAltBlockLiveTime(uint64_t val) { m_currency.m_mempoolTxFromAltBlockLiveTime = val; return *this; } + + CurrencyBuilder& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; } + CurrencyBuilder& upgradeVotingThreshold(unsigned int val); + CurrencyBuilder& upgradeVotingWindow(size_t val) { m_currency.m_upgradeVotingWindow = val; return *this; } + CurrencyBuilder& upgradeWindow(size_t val); + + CurrencyBuilder& blocksFileName(const std::string& val) { m_currency.m_blocksFileName = val; return *this; } + CurrencyBuilder& blocksCacheFileName(const std::string& val) { m_currency.m_blocksCacheFileName = val; return *this; } + CurrencyBuilder& blockIndexesFileName(const std::string& val) { m_currency.m_blockIndexesFileName = val; return *this; } + CurrencyBuilder& txPoolFileName(const std::string& val) { m_currency.m_txPoolFileName = val; return *this; } + + CurrencyBuilder& testnet(bool val) { m_currency.m_testnet = val; return *this; } + + private: + Currency m_currency; + }; +} diff --git a/src/cryptonote_core/ITimeProvider.cpp b/src/cryptonote_core/ITimeProvider.cpp new file mode 100644 index 0000000000..52d0481a56 --- /dev/null +++ b/src/cryptonote_core/ITimeProvider.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ITimeProvider.h" diff --git a/src/cryptonote_core/ITimeProvider.h b/src/cryptonote_core/ITimeProvider.h new file mode 100644 index 0000000000..fa650796c5 --- /dev/null +++ b/src/cryptonote_core/ITimeProvider.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace CryptoNote { + + struct ITimeProvider { + virtual time_t now() = 0; + virtual ~ITimeProvider() {} + }; + + struct RealTimeProvider : public ITimeProvider { + virtual time_t now() { + return time(nullptr); + } + }; + +} diff --git a/src/cryptonote_core/ITransactionValidator.h b/src/cryptonote_core/ITransactionValidator.h new file mode 100644 index 0000000000..e0b974f5a3 --- /dev/null +++ b/src/cryptonote_core/ITransactionValidator.h @@ -0,0 +1,51 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/cryptonote_basic.h" + +namespace CryptoNote { + + struct BlockInfo { + uint64_t height; + crypto::hash id; + + BlockInfo() { + clear(); + } + + void clear() { + height = 0; + id = cryptonote::null_hash; + } + + bool empty() const { + return id == cryptonote::null_hash; + } + }; + + class ITransactionValidator { + public: + virtual ~ITransactionValidator() {} + + virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) = 0; + virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) = 0; + virtual bool haveSpentKeyImages(const cryptonote::Transaction& tx) = 0; + }; + +} diff --git a/src/cryptonote_core/SwappedVector.h b/src/cryptonote_core/SwappedVector.h index 4523b3c117..c5fcc6ad4b 100755 --- a/src/cryptonote_core/SwappedVector.h +++ b/src/cryptonote_core/SwappedVector.h @@ -25,12 +25,119 @@ #include #include #include -//#include -//#include #include "serialization/binary_archive.h" template class SwappedVector { public: + typedef T value_type; + + class const_iterator { + public: + typedef ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef const T* pointer; + typedef const T& reference; + typedef T value_type; + + const_iterator() { + } + + const_iterator(SwappedVector* swappedVector, std::size_t index) : m_swappedVector(swappedVector), m_index(index) { + } + + bool operator!=(const const_iterator& other) const { + return m_index != other.m_index; + } + + bool operator<(const const_iterator& other) const { + return m_index < other.m_index; + } + + bool operator<=(const const_iterator& other) const { + return m_index <= other.m_index; + } + + bool operator==(const const_iterator& other) const { + return m_index == other.m_index; + } + + bool operator>(const const_iterator& other) const { + return m_index > other.m_index; + } + + bool operator>=(const const_iterator& other) const { + return m_index >= other.m_index; + } + + const_iterator& operator++() { + ++m_index; + return *this; + } + + const_iterator operator++(int) { + const_iterator i = *this; + ++m_index; + return i; + } + + const_iterator& operator--() { + --m_index; + return *this; + } + + const_iterator operator--(int) { + const_iterator i = *this; + --m_index; + return i; + } + + const_iterator& operator+=(difference_type n) { + m_index += n; + return *this; + } + + const_iterator& operator-=(difference_type n) { + m_index -= n; + return *this; + } + + const_iterator operator+(difference_type n) const { + return const_iterator(m_swappedVector, m_index + n); + } + + friend const_iterator operator+(difference_type n, const const_iterator& i) { + return const_iterator(i.m_swappedVector, n + i.m_index); + } + + difference_type operator-(const const_iterator& other) const { + return m_index - other.m_index; + } + + const_iterator& operator-(difference_type n) const { + return const_iterator(m_swappedVector, m_index - n); + } + + const T& operator*() const { + return (*m_swappedVector)[m_index]; + } + + const T* operator->() const { + return &(*m_swappedVector)[m_index]; + } + + const T& operator[](difference_type offset) const { + return (*m_swappedVector)[m_index + offset]; + } + + std::size_t index() const { + return m_index; + } + + private: + SwappedVector* m_swappedVector; + std::size_t m_index; + }; + SwappedVector(); //SwappedVector(const SwappedVector&) = delete; ~SwappedVector(); @@ -41,6 +148,8 @@ template class SwappedVector { bool empty() const; uint64_t size() const; + const_iterator begin(); + const_iterator end(); const T& operator[](uint64_t index); const T& front(); const T& back(); @@ -149,6 +258,14 @@ template uint64_t SwappedVector::size() const { return m_offsets.size(); } +template typename SwappedVector::const_iterator SwappedVector::begin() { + return const_iterator(this, 0); +} + +template typename SwappedVector::const_iterator SwappedVector::end() { + return const_iterator(this, m_offsets.size()); +} + template const T& SwappedVector::operator[](uint64_t index) { auto itemIter = m_items.find(index); if (itemIter != m_items.end()) { @@ -170,13 +287,6 @@ template const T& SwappedVector::operator[](uint64_t index) { m_itemsFile.seekg(m_offsets[index]); T tempItem; - //try { - //boost::archive::binary_iarchive archive(m_itemsFile); - //archive & tempItem; - //} catch (std::exception&) { - // throw std::runtime_error("SwappedVector::operator[]"); - //} - binary_archive archive(m_itemsFile); if (!do_serialize(archive, tempItem)) { throw std::runtime_error("SwappedVector::operator[]"); @@ -244,13 +354,6 @@ template void SwappedVector::push_back(const T& item) { } m_itemsFile.seekp(m_itemsFileSize); - //try { - // boost::archive::binary_oarchive archive(m_itemsFile); - // archive & item; - //} catch (std::exception&) { - // throw std::runtime_error("SwappedVector::push_back"); - //} - binary_archive archive(m_itemsFile); if (!do_serialize(archive, *const_cast(&item))) { throw std::runtime_error("SwappedVector::push_back"); diff --git a/src/cryptonote_core/UpgradeDetector.cpp b/src/cryptonote_core/UpgradeDetector.cpp new file mode 100644 index 0000000000..c42dbd42a9 --- /dev/null +++ b/src/cryptonote_core/UpgradeDetector.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "UpgradeDetector.h" diff --git a/src/cryptonote_core/UpgradeDetector.h b/src/cryptonote_core/UpgradeDetector.h new file mode 100644 index 0000000000..060fdcf0af --- /dev/null +++ b/src/cryptonote_core/UpgradeDetector.h @@ -0,0 +1,193 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +// epee +#include "include_base_utils.h" + +#include "cryptonote_core/Currency.h" +#include "cryptonote_config.h" + +namespace cryptonote { + class UpgradeDetectorBase { + public: + enum : uint64_t { + UNDEF_HEIGHT = static_cast(-1), + }; + }; + + static_assert(cryptonote::UpgradeDetectorBase::UNDEF_HEIGHT == UINT64_C(0xFFFFFFFFFFFFFFFF), "UpgradeDetectorBase::UNDEF_HEIGHT has invalid value"); + + template + class BasicUpgradeDetector : public UpgradeDetectorBase { + public: + BasicUpgradeDetector(const Currency& currency, BC& blockchain, uint8_t targetVersion) : + m_currency(currency), + m_blockchain(blockchain), + m_targetVersion(targetVersion), + m_votingCompleteHeight(UNDEF_HEIGHT) { + } + + bool init() { + if (m_currency.upgradeHeight() == UNDEF_HEIGHT) { + if (m_blockchain.empty()) { + m_votingCompleteHeight = UNDEF_HEIGHT; + + } else if (m_targetVersion - 1 == m_blockchain.back().bl.majorVersion) { + m_votingCompleteHeight = findVotingCompleteHeight(m_blockchain.size() - 1); + + } else if (m_targetVersion <= m_blockchain.back().bl.majorVersion) { + auto it = std::lower_bound(m_blockchain.begin(), m_blockchain.end(), m_targetVersion, + [](const typename BC::value_type& b, uint8_t v) { return b.bl.majorVersion < v; }); + CHECK_AND_ASSERT_MES(it != m_blockchain.end() && it->bl.majorVersion == m_targetVersion, false, + "Internal error: upgrade height isn't found"); + uint64_t upgradeHeight = it - m_blockchain.begin(); + m_votingCompleteHeight = findVotingCompleteHeight(upgradeHeight); + CHECK_AND_ASSERT_MES(m_votingCompleteHeight != UNDEF_HEIGHT, false, + "Internal error: voting complete height isn't found, upgrade height = " << upgradeHeight); + + } else { + m_votingCompleteHeight = UNDEF_HEIGHT; + } + } else if (!m_blockchain.empty()) { + if (m_blockchain.size() <= m_currency.upgradeHeight() + 1) { + CHECK_AND_ASSERT_MES(m_blockchain.back().bl.majorVersion == m_targetVersion - 1, false, + "Internal error: block at height " << (m_blockchain.size() - 1) << " has invalid version " << + static_cast(m_blockchain.back().bl.majorVersion) << ", expected " << static_cast(m_targetVersion)); + } else { + int blockVersionAtUpgradeHeight = m_blockchain[m_currency.upgradeHeight()].bl.majorVersion; + CHECK_AND_ASSERT_MES(blockVersionAtUpgradeHeight == m_targetVersion - 1, false, + "Internal error: block at height " << m_currency.upgradeHeight() << " has invalid version " << + blockVersionAtUpgradeHeight << ", expected " << static_cast(m_targetVersion - 1)); + + int blockVersionAfterUpgradeHeight = m_blockchain[m_currency.upgradeHeight() + 1].bl.majorVersion; + CHECK_AND_ASSERT_MES(blockVersionAfterUpgradeHeight == m_targetVersion, false, + "Internal error: block at height " << (m_currency.upgradeHeight() + 1) << " has invalid version " << + blockVersionAfterUpgradeHeight << ", expected " << static_cast(m_targetVersion)); + } + } + + return true; + } + + uint8_t targetVersion() const { return m_targetVersion; } + uint64_t votingCompleteHeight() const { return m_votingCompleteHeight; } + + uint64_t upgradeHeight() const { + if (m_currency.upgradeHeight() == UNDEF_HEIGHT) { + return m_votingCompleteHeight == UNDEF_HEIGHT ? UNDEF_HEIGHT : m_currency.calculateUpgradeHeight(m_votingCompleteHeight); + } else { + return m_currency.upgradeHeight(); + } + } + + void blockPushed() { + assert(!m_blockchain.empty()); + + if (m_currency.upgradeHeight() != UNDEF_HEIGHT) { + if (m_blockchain.size() <= m_currency.upgradeHeight() + 1) { + assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); + } else { + assert(m_blockchain.back().bl.majorVersion == m_targetVersion); + } + + } else if (m_votingCompleteHeight != UNDEF_HEIGHT) { + assert(m_blockchain.size() > m_votingCompleteHeight); + + if (m_blockchain.size() <= upgradeHeight()) { + assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); + + if (m_blockchain.size() % (60 * 60 / m_currency.difficultyTarget()) == 0) { + LOG_PRINT_GREEN("###### UPGRADE is going to happen after height " << upgradeHeight() << "!", LOG_LEVEL_2); + } + } else if (m_blockchain.size() == upgradeHeight() + 1) { + assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); + + LOG_PRINT_GREEN("###### UPGRADE has happened! Starting from height " << (upgradeHeight() + 1) << + " blocks with major version below " << static_cast(m_targetVersion) << " will be rejected!", LOG_LEVEL_2); + } else { + assert(m_blockchain.back().bl.majorVersion == m_targetVersion); + } + + } else { + uint64_t lastBlockHeight = m_blockchain.size() - 1; + if (isVotingComplete(lastBlockHeight)) { + m_votingCompleteHeight = lastBlockHeight; + LOG_PRINT_GREEN("###### UPGRADE voting complete at height " << m_votingCompleteHeight << + "! UPGRADE is going to happen after height " << upgradeHeight() << "!", LOG_LEVEL_2); + } + } + } + + void blockPopped() { + if (m_votingCompleteHeight != UNDEF_HEIGHT) { + assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); + + if (m_blockchain.size() == m_votingCompleteHeight) { + LOG_PRINT_YELLOW("###### UPGRADE after height " << upgradeHeight() << " has been cancelled!", LOG_LEVEL_2); + m_votingCompleteHeight = UNDEF_HEIGHT; + } else { + assert(m_blockchain.size() > m_votingCompleteHeight); + } + } + } + + private: + uint64_t findVotingCompleteHeight(uint64_t probableUpgradeHeight) { + assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); + + uint64_t probableVotingCompleteHeight = probableUpgradeHeight > m_currency.maxUpgradeDistance() ? + probableUpgradeHeight - m_currency.maxUpgradeDistance() : 0; + for (size_t i = probableVotingCompleteHeight; i <= probableUpgradeHeight; ++i) { + if (isVotingComplete(i)) { + return i; + } + } + + return UNDEF_HEIGHT; + } + + bool isVotingComplete(uint64_t height) { + assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); + assert(m_currency.upgradeVotingWindow() > 1); + assert(m_currency.upgradeVotingThreshold() > 0 && m_currency.upgradeVotingThreshold() <= 100); + + if (height < static_cast(m_currency.upgradeVotingWindow()) - 1) { + return false; + } + + unsigned int voteCounter = 0; + for (size_t i = height + 1 - m_currency.upgradeVotingWindow(); i <= height; ++i) { + const auto& b = m_blockchain[i].bl; + voteCounter += (b.majorVersion == m_targetVersion - 1) && (b.minorVersion == BLOCK_MINOR_VERSION_1) ? 1 : 0; + } + + return m_currency.upgradeVotingThreshold() * m_currency.upgradeVotingWindow() <= 100 * voteCounter; + } + + private: + const Currency& m_currency; + BC& m_blockchain; + uint8_t m_targetVersion; + uint64_t m_votingCompleteHeight; + }; +} diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index 3524c43b8c..c7ba60451b 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -15,21 +15,18 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "account.h" + #include #include #include #include "include_base_utils.h" -#include "account.h" #include "warnings.h" -#include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" -using namespace std; DISABLE_VS_WARNINGS(4244 4345) - namespace cryptonote +namespace cryptonote { //----------------------------------------------------------------- account_base::account_base() @@ -44,8 +41,8 @@ DISABLE_VS_WARNINGS(4244 4345) //----------------------------------------------------------------- void account_base::generate() { - generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key); - generate_keys(m_keys.m_account_address.m_view_public_key, m_keys.m_view_secret_key); + crypto::generate_keys(m_keys.m_account_address.m_spendPublicKey, m_keys.m_spend_secret_key); + crypto::generate_keys(m_keys.m_account_address.m_viewPublicKey, m_keys.m_view_secret_key); m_creation_timestamp = time(NULL); } //----------------------------------------------------------------- @@ -54,10 +51,4 @@ DISABLE_VS_WARNINGS(4244 4345) return m_keys; } //----------------------------------------------------------------- - std::string account_base::get_public_address_str() - { - //TODO: change this code into base 58 - return get_account_address_as_str(m_keys.m_account_address); - } - //----------------------------------------------------------------- } diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index 4574af5839..a53ebfa346 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -19,34 +19,24 @@ #include "cryptonote_core/cryptonote_basic.h" #include "crypto/crypto.h" -#include "serialization/keyvalue_serialization.h" -namespace cryptonote -{ +namespace cryptonote { + template struct AccountBaseSerializer; - struct account_keys - { - account_public_address m_account_address; + struct account_keys { + AccountPublicAddress m_account_address; crypto::secret_key m_spend_secret_key; crypto::secret_key m_view_secret_key; - - 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) - END_KV_SERIALIZE_MAP() }; /************************************************************************/ /* */ /************************************************************************/ - class account_base - { + class account_base { public: account_base(); void generate(); const account_keys& get_keys() const; - std::string get_public_address_str(); uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } @@ -55,20 +45,17 @@ namespace cryptonote bool store(const std::string& file_path); template - inline void serialize(t_archive &a, const unsigned int /*ver*/) - { + inline void serialize(t_archive &a, const unsigned int /*ver*/) { a & m_keys; a & m_creation_timestamp; } - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_keys) - KV_SERIALIZE(m_creation_timestamp) - END_KV_SERIALIZE_MAP() - private: void set_null(); account_keys m_keys; uint64_t m_creation_timestamp; + + friend struct AccountBaseSerializer; + friend struct AccountBaseSerializer; }; } diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_core/account_boost_serialization.h index ee21d71883..e391b39b6c 100644 --- a/src/cryptonote_core/account_boost_serialization.h +++ b/src/cryptonote_core/account_boost_serialization.h @@ -34,10 +34,10 @@ namespace boost } template - inline void serialize(Archive &a, cryptonote::account_public_address &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::AccountPublicAddress &x, const boost::serialization::version_type ver) { - a & x.m_spend_public_key; - a & x.m_view_public_key; + a & x.m_spendPublicKey; + a & x.m_viewPublicKey; } } diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 5ea4d09822..9b578959f7 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -20,15 +20,21 @@ #include #include + #include #include +// epee +#include "file_io_utils.h" +#include "misc_language.h" +#include "profile_tools.h" +#include "time_helper.h" + +#include "common/boost_serialization_helper.h" #include "cryptonote_format_utils.h" #include "cryptonote_boost_serialization.h" +#include "rpc/core_rpc_server_commands_defs.h" -#include "profile_tools.h" -#include "file_io_utils.h" -#include "common/boost_serialization_helper.h" //namespace { // std::string hashHex(const crypto::hash& hash) { @@ -70,7 +76,7 @@ DISABLE_VS_WARNINGS(4267) namespace cryptonote { struct transaction_chain_entry { - transaction tx; + Transaction tx; uint64_t m_keeper_block_height; size_t m_blob_size; std::vector m_global_output_indexes; @@ -79,7 +85,7 @@ namespace cryptonote { }; struct block_extended_info { - block bl; + Block bl; uint64_t height; size_t block_cumulative_size; difficulty_type cumulative_difficulty; @@ -104,11 +110,11 @@ namespace cryptonote { } } -template void cryptonote::blockchain_storage::Transaction::serialize(Archive& archive, unsigned int version) { +template void cryptonote::blockchain_storage::TransactionEntry::serialize(Archive& archive, unsigned int version) { archive & tx; } -template void cryptonote::blockchain_storage::Block::serialize(Archive& archive, unsigned int version) { +template void cryptonote::blockchain_storage::BlockEntry::serialize(Archive& archive, unsigned int version) { archive & bl; archive & height; archive & block_cumulative_size; @@ -122,6 +128,12 @@ template void cryptonote::blockchain_storage::TransactionIndex::s archive & transaction; } +template void cryptonote::blockchain_storage::MultisignatureOutputUsage::serialize(Archive& archive, unsigned int version) { + archive & transactionIndex; + archive & outputIndex; + archive & isUsed; +} + namespace cryptonote { #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 13 @@ -172,22 +184,22 @@ namespace cryptonote { "transactions: " << transactions.size() << ENDL << "current_block_cumul_sz_limit: " << current_block_cumul_sz_limit); - Block block; - Transaction transaction; + BlockEntry block; + TransactionEntry transaction; for (uint32_t b = 0; b < blocks.size(); ++b) { block.bl = blocks[b].bl; block.height = b; block.block_cumulative_size = blocks[b].block_cumulative_size; block.cumulative_difficulty = blocks[b].cumulative_difficulty; block.already_generated_coins = blocks[b].already_generated_coins; - block.transactions.resize(1 + blocks[b].bl.tx_hashes.size()); - block.transactions[0].tx = blocks[b].bl.miner_tx; + block.transactions.resize(1 + blocks[b].bl.txHashes.size()); + block.transactions[0].tx = blocks[b].bl.minerTx; TransactionIndex transactionIndex = { b, 0 }; - pushTransaction(block, get_transaction_hash(blocks[b].bl.miner_tx), transactionIndex); - for (uint32_t t = 0; t < blocks[b].bl.tx_hashes.size(); ++t) { - block.transactions[1 + t].tx = transactions[blocks[b].bl.tx_hashes[t]].tx; + pushTransaction(block, get_transaction_hash(blocks[b].bl.minerTx), transactionIndex); + for (uint32_t t = 0; t < blocks[b].bl.txHashes.size(); ++t) { + block.transactions[1 + t].tx = transactions[blocks[b].bl.txHashes[t]].tx; transactionIndex.transaction = 1 + t; - pushTransaction(block, blocks[b].bl.tx_hashes[t], transactionIndex); + pushTransaction(block, blocks[b].bl.txHashes[t], transactionIndex); } pushBlock(block); @@ -202,6 +214,119 @@ namespace cryptonote { BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) +namespace cryptonote +{ + +#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1 + + class BlockCacheSerializer { + + public: + BlockCacheSerializer(blockchain_storage& bs, const crypto::hash lastBlockHash) : + m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false) {} + + template void serialize(Archive& ar, unsigned int version) { + + // ignore old versions, do rebuild + if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) + return; + + if (Archive::is_loading::value) { + + crypto::hash blockHash; + ar & blockHash; + + if (blockHash != m_lastBlockHash) { + return; + } + + } else { + ar & m_lastBlockHash; + } + + ar & m_bs.m_blockIndex; + ar & m_bs.m_transactionMap; + ar & m_bs.m_spent_keys; + ar & m_bs.m_outputs; + ar & m_bs.m_multisignatureOutputs; + + m_loaded = true; + } + + bool loaded() const { + return m_loaded; + } + + private: + + bool m_loaded; + blockchain_storage& m_bs; + crypto::hash m_lastBlockHash; + }; +} + +BOOST_CLASS_VERSION(cryptonote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) + + +blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool): + m_currency(currency), + m_tx_pool(tx_pool), + m_current_block_cumul_sz_limit(currency.blockGrantedFullRewardZone() * 2), + m_is_in_checkpoint_zone(false), + m_is_blockchain_storing(false), + m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2) { + m_outputs.set_deleted_key(0); + + crypto::key_image nullImage = AUTO_VAL_INIT(nullImage); + m_spent_keys.set_deleted_key(nullImage); +} + +bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) { + return check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id); +} + +bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { + + BlockInfo tail; + + //not the best implementation at this time, sorry :( + //check is ring_signature already checked ? + if (maxUsedBlock.empty()) { + //not checked, lets try to check + if (!lastFailed.empty() && get_current_blockchain_height() > lastFailed.height && get_block_id_by_height(lastFailed.height) == lastFailed.id) { + return false; //we already sure that this tx is broken for this height + } + + if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { + lastFailed = tail; + return false; + } + } + else { + if (maxUsedBlock.height >= get_current_blockchain_height()) { + return false; + } + + if (get_block_id_by_height(maxUsedBlock.height) != maxUsedBlock.id) { + //if we already failed on this height and id, skip actual ring signature check + if (lastFailed.id == get_block_id_by_height(lastFailed.height)) { + return false; + } + + //check ring signature again, it is possible (with very small chance) that this transaction become again valid + if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { + lastFailed = tail; + return false; + } + } + } + + return true; +} + +bool blockchain_storage::haveSpentKeyImages(const cryptonote::Transaction& tx) { + return this->have_tx_keyimges_as_spent(tx); +} bool blockchain_storage::have_tx(const crypto::hash &id) { CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -227,7 +352,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi m_config_folder = config_folder; - if (!m_blocks.open(appendPath(config_folder, "blocks.dat"), appendPath(config_folder, "blockindexes.dat"), 1024)) { + if (!m_blocks.open(appendPath(config_folder, m_currency.blocksFileName()), appendPath(config_folder, m_currency.blockIndexesFileName()), 1024)) { return false; } @@ -235,42 +360,34 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi LOG_PRINT_L0("Loading blockchain..."); if (m_blocks.empty()) { - const std::string filename = appendPath(m_config_folder, CRYPTONOTE_BLOCKCHAINDATA_FILENAME); + const std::string filename = appendPath(m_config_folder, cryptonote::parameters::CRYPTONOTE_BLOCKCHAINDATA_FILENAME); if (!tools::unserialize_obj_from_file(*this, filename)) { LOG_PRINT_L0("Can't load blockchain storage from file."); } } else { - bool rebuild = true; - try { - std::ifstream file(appendPath(config_folder, "blockscache.dat"), std::ios::binary); - boost::archive::binary_iarchive archive(file); - crypto::hash lastBlockHash; - archive & lastBlockHash; - if (lastBlockHash == get_block_hash(m_blocks.back().bl)) { - archive & m_blockMap; - archive & m_transactionMap; - archive & m_spent_keys; - archive & m_outputs; - rebuild = false; - } - } catch (std::exception&) { - } + BlockCacheSerializer loader(*this, get_block_hash(m_blocks.back().bl)); + tools::unserialize_obj_from_file(loader, appendPath(config_folder, m_currency.blocksCacheFileName())); - if (rebuild) { + if (!loader.loaded()) { LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures..."); std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + m_blockIndex.clear(); + m_transactionMap.clear(); + m_spent_keys.clear(); + m_outputs.clear(); + m_multisignatureOutputs.clear(); for (uint32_t b = 0; b < m_blocks.size(); ++b) { - const Block& block = m_blocks[b]; + const BlockEntry& block = m_blocks[b]; crypto::hash blockHash = get_block_hash(block.bl); - m_blockMap.insert(std::make_pair(blockHash, b)); + m_blockIndex.push(blockHash); for (uint16_t t = 0; t < block.transactions.size(); ++t) { - const Transaction& transaction = block.transactions[t]; + const TransactionEntry& transaction = block.transactions[t]; crypto::hash transactionHash = get_transaction_hash(transaction.tx); TransactionIndex transactionIndex = { b, t }; m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); for (auto& i : transaction.tx.vin) { - if (i.type() == typeid(txin_to_key)) { - m_spent_keys.insert(::boost::get(i).k_image); + if (i.type() == typeid(TransactionInputToKey)) { + m_spent_keys.insert(::boost::get(i).keyImage); } } @@ -290,110 +407,50 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi if (m_blocks.empty()) { LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); - block bl = ::boost::value_initialized(); block_verification_context bvc = boost::value_initialized(); - generate_genesis_block(bl); - add_new_block(bl, bvc); + add_new_block(m_currency.genesisBlock(), bvc); CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); + } else { + crypto::hash firstBlockHash = get_block_hash(m_blocks[0].bl); + CHECK_AND_ASSERT_MES(firstBlockHash == m_currency.genesisBlockHash(), false, + "Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network."); + } + + if (!m_upgradeDetector.init()) { + LOG_ERROR("Failed to initialize upgrade detector"); + return false; } uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; - if (!m_blocks.back().bl.timestamp) + if (!m_blocks.back().bl.timestamp) { timestamp_diff = time(NULL) - 1341378000; + } + LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); return true; } -bool blockchain_storage::store_blockchain() { - try { - std::ofstream file(appendPath(m_config_folder, "blockscache.dat"), std::ios::binary); - boost::archive::binary_oarchive archive(file); - crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); - archive & lastBlockHash; - archive & m_blockMap; - archive & m_transactionMap; - archive & m_spent_keys; - archive & m_outputs; - } catch (std::exception& e) { - LOG_ERROR("Failed to save blockchain, " << e.what()); - } - - //{ - // std::ofstream file(appendPath(m_config_folder, "blockscache2.dat"), std::ios::binary); - - // crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); - // file.write(reinterpret_cast(&lastBlockHash), sizeof(lastBlockHash)); - - // uint32_t blockMapSize = m_blockMap.size(); - // file.write(reinterpret_cast(&blockMapSize), sizeof(blockMapSize)); - // for (auto& i : m_blockMap) { - // crypto::hash blockHash = i.first; - // file.write(reinterpret_cast(&blockHash), sizeof(blockHash)); - - // uint32_t blockIndex = i.second; - // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); - // } - - // uint32_t transactionMapSize = m_transactionMap.size(); - // file.write(reinterpret_cast(&transactionMapSize), sizeof(transactionMapSize)); - // for (auto& i : m_transactionMap) { - // crypto::hash transactionHash = i.first; - // file.write(reinterpret_cast(&transactionHash), sizeof(transactionHash)); - - // uint32_t blockIndex = i.second.block; - // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); - - // uint32_t transactionIndex = i.second.transaction; - // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); - // } - - // uint32_t spentKeysSize = m_spent_keys.size(); - // file.write(reinterpret_cast(&spentKeysSize), sizeof(spentKeysSize)); - // for (auto& i : m_spent_keys) { - // crypto::key_image key = i; - // file.write(reinterpret_cast(&key), sizeof(key)); - // } - - // uint32_t outputsSize = m_outputs.size(); - // file.write(reinterpret_cast(&outputsSize), sizeof(outputsSize)); - // for (auto& i : m_outputs) { - // uint32_t indexesSize = i.second.size(); - // file.write(reinterpret_cast(&indexesSize), sizeof(indexesSize)); - // for (auto& j : i.second) { - // uint32_t blockIndex = j.first.block; - // file.write(reinterpret_cast(&blockIndex), sizeof(blockIndex)); - - // uint32_t transactionIndex = j.first.transaction; - // file.write(reinterpret_cast(&transactionIndex), sizeof(transactionIndex)); - - // uint32_t outputIndex = j.second; - // file.write(reinterpret_cast(&outputIndex), sizeof(outputIndex)); - // } - // } - //} +bool blockchain_storage::storeCache() { + CRITICAL_REGION_LOCAL(m_blockchain_lock); - { - //std::ofstream file(appendPath(m_config_folder, "blockscache3.dat"), std::ios::binary); - //binary_archive archive(file); - //crypto::hash lastBlockHash = get_block_hash(m_blocks.back().bl); - //do_serialize(archive, lastBlockHash); - //do_serialize(archive, m_blockMap); - //do_serialize(archive, m_transactionMap); - //do_serialize(archive, m_spent_keys); - //do_serialize(archive, m_outputs); + BlockCacheSerializer ser(*this, get_tail_id()); + if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { + LOG_ERROR("Failed to save blockchain cache"); + return false; } return true; } bool blockchain_storage::deinit() { - return store_blockchain(); + storeCache(); + return true; } -bool blockchain_storage::reset_and_set_genesis_block(const block& b) { +bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { CRITICAL_REGION_LOCAL(m_blockchain_lock); m_blocks.clear(); - m_blockMap.clear(); + m_blockIndex.clear(); m_transactionMap.clear(); m_spent_keys.clear(); @@ -413,56 +470,26 @@ crypto::hash blockchain_storage::get_tail_id(uint64_t& height) { crypto::hash blockchain_storage::get_tail_id() { CRITICAL_REGION_LOCAL(m_blockchain_lock); - crypto::hash id = null_hash; - if (m_blocks.size()) { - get_block_hash(m_blocks.back().bl, id); - } - - return id; + return m_blockIndex.getTailId(); } bool blockchain_storage::get_short_chain_history(std::list& ids) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = m_blocks.size(); - if (!sz) - return true; - size_t current_back_offset = 1; - bool genesis_included = false; - while (current_back_offset < sz) - { - ids.push_back(get_block_hash(m_blocks[sz - current_back_offset].bl)); - if (sz - current_back_offset == 0) - genesis_included = true; - if (i < 10) - { - ++current_back_offset; - } else - { - current_back_offset += current_multiplier *= 2; - } - ++i; - } - if (!genesis_included) - ids.push_back(get_block_hash(m_blocks[0].bl)); - - return true; + return m_blockIndex.getShortChainHistory(ids); } crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - if (height >= m_blocks.size()) - return null_hash; - - return get_block_hash(m_blocks[height].bl); + return m_blockIndex.getBlockId(height); } -bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, block& b) { +bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& b) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - auto blockIndexByHashIterator = m_blockMap.find(blockHash); - if (blockIndexByHashIterator != m_blockMap.end()) { - b = m_blocks[blockIndexByHashIterator->second].bl; + + uint64_t height = 0; + + if (m_blockIndex.getBlockHeight(blockHash, height)) { + b = m_blocks[height].bl; return true; } @@ -479,7 +506,7 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block() { CRITICAL_REGION_LOCAL(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; - size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); + size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(m_currency.difficultyBlocksCount())); if (offset == 0) { ++offset; } @@ -489,10 +516,23 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block() { commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); } - return next_difficulty(timestamps, commulative_difficulties); + return m_currency.nextDifficulty(timestamps, commulative_difficulties); +} + +uint64_t blockchain_storage::getCoinsInCirculation() { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (m_blocks.empty()) { + return 0; + } else { + return m_blocks.back().already_generated_coins; + } +} + +uint8_t blockchain_storage::get_block_major_version_for_height(uint64_t height) const { + return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1; } -bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) { +bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); //remove failed subchain for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) @@ -521,9 +561,9 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height"); //disconnecting old chain - std::list disconnected_chain; + std::list disconnected_chain; for (size_t i = m_blocks.size() - 1; i >= split_height; i--) { - block b = m_blocks[i].bl; + Block b = m_blocks[i].bl; popBlock(get_block_hash(b)); //CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); disconnected_chain.push_front(b); @@ -573,13 +613,13 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, Block& bei) { +difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei) { std::vector timestamps; std::vector commulative_difficulties; - if (alt_chain.size() < DIFFICULTY_BLOCKS_COUNT) { + if (alt_chain.size() < m_currency.difficultyBlocksCount()) { CRITICAL_REGION_LOCAL(m_blockchain_lock); size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; - size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); + size_t main_chain_count = m_currency.difficultyBlocksCount() - std::min(m_currency.difficultyBlocksCount(), alt_chain.size()); main_chain_count = std::min(main_chain_count, main_chain_stop_offset); size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; @@ -590,43 +630,46 @@ difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(co commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); } - CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() - << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT); + CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= m_currency.difficultyBlocksCount(), false, + "Internal error, alt_chain.size()[" << alt_chain.size() << "] + timestamps.size()[" << timestamps.size() << + "] NOT <= m_currency.difficultyBlocksCount()[" << m_currency.difficultyBlocksCount() << ']'); for (auto it : alt_chain) { timestamps.push_back(it->second.bl.timestamp); commulative_difficulties.push_back(it->second.cumulative_difficulty); } } else { - timestamps.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); - commulative_difficulties.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); + timestamps.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount())); + commulative_difficulties.resize(std::min(alt_chain.size(), m_currency.difficultyBlocksCount())); size_t count = 0; size_t max_i = timestamps.size() - 1; BOOST_REVERSE_FOREACH(auto it, alt_chain) { timestamps[max_i - count] = it->second.bl.timestamp; commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; count++; - if (count >= DIFFICULTY_BLOCKS_COUNT) { + if (count >= m_currency.difficultyBlocksCount()) { break; } } } - return next_difficulty(timestamps, commulative_difficulties); + return m_currency.nextDifficulty(timestamps, commulative_difficulties); } -bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) { - CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); - CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); - if (boost::get(b.miner_tx.vin[0]).height != height) { - LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << boost::get(b.miner_tx.vin[0]).height << ", expected: " << height); +bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t height) { + CHECK_AND_ASSERT_MES(b.minerTx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); + CHECK_AND_ASSERT_MES(b.minerTx.vin[0].type() == typeid(TransactionInputGenerate), false, + "coinbase transaction in the block has the wrong type"); + if (boost::get(b.minerTx.vin[0]).height != height) { + LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << + boost::get(b.minerTx.vin[0]).height << ", expected: " << height); return false; } - CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, + CHECK_AND_ASSERT_MES(b.minerTx.unlockTime == height + m_currency.minedMoneyUnlockWindow(), false, - "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + "coinbase transaction transaction have wrong unlock time=" << b.minerTx.unlockTime << ", expected " << height + m_currency.minedMoneyUnlockWindow()); - if (!check_outs_overflow(b.miner_tx)) { + if (!check_outs_overflow(b.minerTx)) { LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); return false; } @@ -634,27 +677,31 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t return true; } -bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) { - uint64_t money_in_use = 0; - for (auto& o : b.miner_tx.vout) { - money_in_use += o.amount; +bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, + uint64_t alreadyGeneratedCoins, uint64_t fee, + uint64_t& reward, int64_t& emissionChange) { + uint64_t minerReward = 0; + for (auto& o : b.minerTx.vout) { + minerReward += o.amount; } - 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)) { - LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); + std::vector lastBlocksSizes; + get_last_n_blocks_sizes(lastBlocksSizes, m_currency.rewardBlocksWindow()); + size_t blocksSizeMedian = epee::misc_utils::median(lastBlocksSizes); + + bool penalizeFee = get_block_major_version_for_height(height) > BLOCK_MAJOR_VERSION_1; + if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange)) { + LOG_PRINT_L0("block size " << cumulativeBlockSize << " is bigger than allowed for this blockchain"); return false; } - if (base_reward + fee < money_in_use) { - LOG_ERROR("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + if (minerReward > reward) { + LOG_ERROR("Coinbase transaction spend too much money: " << m_currency.formatAmount(minerReward) << + ", block reward is " << m_currency.formatAmount(reward)); return false; - } - - if (base_reward + fee != money_in_use) { - LOG_ERROR("coinbase transaction doesn't use full amount of block reward: spent: " - << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + } else if (minerReward < reward) { + LOG_ERROR("Coinbase transaction doesn't use full amount of block reward: spent " << + m_currency.formatAmount(minerReward) << ", block reward is " << m_currency.formatAmount(reward)); return false; } @@ -685,18 +732,33 @@ uint64_t blockchain_storage::get_current_comulative_blocksize_limit() { return m_current_block_cumul_sz_limit; } -bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) { +bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) { size_t median_size; uint64_t already_generated_coins; CRITICAL_REGION_BEGIN(m_blockchain_lock); - b.major_version = CURRENT_BLOCK_MAJOR_VERSION; - b.minor_version = CURRENT_BLOCK_MINOR_VERSION; - b.prev_id = get_tail_id(); - b.timestamp = time(NULL); height = m_blocks.size(); diffic = get_difficulty_for_next_block(); - CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead."); + CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); + + b = boost::value_initialized(); + b.majorVersion = get_block_major_version_for_height(height); + + if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_1; + } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_0; + + b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; + b.parentBlock.numberOfTransactions = 1; + tx_extra_merge_mining_tag mm_tag = AUTO_VAL_INIT(mm_tag); + bool r = append_mm_tag_to_extra(b.parentBlock.minerTx.extra, mm_tag); + CHECK_AND_ASSERT_MES(r, false, "Failed to append merge mining tag to extra of the parent block miner transaction"); + } + + b.prevId = get_tail_id(); + b.timestamp = time(NULL); median_size = m_current_block_cumul_sz_limit / 2; already_generated_coins = m_blocks.back().already_generated_coins; @@ -705,7 +767,8 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad size_t txs_size; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) { + if (!m_tx_pool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, + txs_size, fee)) { return false; } @@ -713,7 +776,7 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad size_t real_txs_size = 0; uint64_t real_fee = 0; CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); - BOOST_FOREACH(crypto::hash &cur_hash, b.tx_hashes) { + for (crypto::hash &cur_hash : b.txHashes) { auto cur_res = m_tx_pool.m_transactions.find(cur_hash); if (cur_res == m_tx_pool.m_transactions.end()) { LOG_ERROR("Creating block template: error: transaction not found"); @@ -751,18 +814,19 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size - bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11); + bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; + bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); - size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); + size_t cumulative_size = txs_size + get_object_blobsize(b.minerTx); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) << + LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.minerTx) << ", cumulative size " << cumulative_size); #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, 11); + r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); - size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); + size_t coinbase_blob_size = get_object_blobsize(b.minerTx); if (coinbase_blob_size > cumulative_size - txs_size) { cumulative_size = txs_size + coinbase_blob_size; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) @@ -779,21 +843,21 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad ", cumulative size " << txs_size + coinbase_blob_size << " is less then before, adding " << delta << " zero bytes"); #endif - b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); + b.minerTx.extra.insert(b.minerTx.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)) { - 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)); - b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); - if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) { + if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) { + CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.minerTx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx)); + b.minerTx.extra.resize(b.minerTx.extra.size() - 1); + if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) { //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1, LOG_LEVEL_2); cumulative_size += delta - 1; continue; } - LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); + LOG_PRINT_GREEN("Setting extra for block: " << b.minerTx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); } } - 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_size == txs_size + get_object_blobsize(b.minerTx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx)); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << ", cumulative size " << cumulative_size << " is now good"); @@ -806,11 +870,11 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad } bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { - if (timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + if (timestamps.size() >= m_currency.timestampCheckWindow()) return true; CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); + size_t need_elements = m_currency.timestampCheckWindow() - timestamps.size(); CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; do @@ -823,7 +887,7 @@ bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, s return true; } -bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) { +bool blockchain_storage::handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc) { CRITICAL_REGION_LOCAL(m_blockchain_lock); uint64_t block_height = get_block_height(b); @@ -841,11 +905,33 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } + if (!checkBlockVersion(b, id)) { + bvc.m_verifivation_failed = true; + return false; + } + + if (!checkParentBlockSize(b, id)) { + bvc.m_verifivation_failed = true; + return false; + } + + size_t cumulativeSize; + if (!getBlockCumulativeSize(b, cumulativeSize)) { + LOG_PRINT_L2("Block with id: " << id << " has at least one unknown transaction. Cumulative size is calculated imprecisely"); + } + + if (!checkCumulativeBlockSize(id, cumulativeSize, block_height)) { + bvc.m_verifivation_failed = true; + return false; + } + //block is not related with head of main chain //first of all - look in alternative chains container - auto it_main_prev = m_blockMap.find(b.prev_id); - auto it_prev = m_alternative_chains.find(b.prev_id); - if (it_prev != m_alternative_chains.end() || it_main_prev != m_blockMap.end()) { + uint64_t mainPrevHeight = 0; + const bool mainPrev = m_blockIndex.getBlockHeight(b.prevId, mainPrevHeight); + const auto it_prev = m_alternative_chains.find(b.prevId); + + if (it_prev != m_alternative_chains.end() || mainPrev) { //we have new block in alternative chain //build alternative subchain, front -> mainchain, back -> alternative head @@ -855,7 +941,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: while (alt_it != m_alternative_chains.end()) { alt_chain.push_front(alt_it); timestamps.push_back(alt_it->second.bl.timestamp); - alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); + alt_it = m_alternative_chains.find(alt_it->second.bl.prevId); } if (alt_chain.size()) { @@ -863,11 +949,11 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); crypto::hash h = null_hash; get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); - CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain have wrong connection to main chain"); + CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prevId, false, "alternative chain have wrong connection to main chain"); complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); } else { - CHECK_AND_ASSERT_MES(it_main_prev != m_blockMap.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); - complete_timestamps_vector(it_main_prev->second, timestamps); + CHECK_AND_ASSERT_MES(mainPrev, false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); + complete_timestamps_vector(mainPrevHeight, timestamps); } //check timestamp correct @@ -879,9 +965,9 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } - Block bei = boost::value_initialized(); + BlockEntry bei = boost::value_initialized(); bei.bl = b; - bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1; + bei.height = static_cast(alt_chain.size() ? it_prev->second.height + 1 : mainPrevHeight + 1); bool is_a_checkpoint; if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) { @@ -895,8 +981,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; - get_block_longhash(m_cn_context, bei.bl, proof_of_work, bei.height); - if (!check_hash(proof_of_work, current_diff)) { + if (!m_currency.checkProofOfWork(m_cn_context, bei.bl, current_diff, proof_of_work)) { LOG_PRINT_RED_L0("Block with id: " << id << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work << ENDL << " expected difficulty: " << current_diff); @@ -910,7 +995,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return false; } - bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty : m_blocks[it_main_prev->second].cumulative_difficulty; + bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty : m_blocks[mainPrevHeight].cumulative_difficulty; bei.cumulative_difficulty += current_diff; #ifdef _DEBUG @@ -955,7 +1040,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto:: return true; } -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { CRITICAL_REGION_LOCAL(m_blockchain_lock); if (start_offset >= m_blocks.size()) return false; @@ -963,14 +1048,14 @@ bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::li { blocks.push_back(m_blocks[i].bl); std::list missed_ids; - get_transactions(m_blocks[i].bl.tx_hashes, txs, missed_ids); + get_transactions(m_blocks[i].bl.txHashes, txs, missed_ids); CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "have missed transactions in own block in main blockchain"); } return true; } -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { CRITICAL_REGION_LOCAL(m_blockchain_lock); if (start_offset >= m_blocks.size()) { return false; @@ -986,26 +1071,26 @@ bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::li bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { CRITICAL_REGION_LOCAL(m_blockchain_lock); rsp.current_blockchain_height = get_current_blockchain_height(); - std::list blocks; + std::list blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl : blocks) { std::list missed_tx_id; - std::list txs; - get_transactions(bl.tx_hashes, txs, rsp.missed_ids); + std::list txs; + get_transactions(bl.txHashes, txs, rsp.missed_ids); CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl)); rsp.blocks.push_back(block_complete_entry()); block_complete_entry& e = rsp.blocks.back(); //pack block e.block = t_serializable_object_to_blob(bl); //pack transactions - for (transaction& tx : txs) { + for (Transaction& tx : txs) { e.txs.push_back(t_serializable_object_to_blob(tx)); } } //get another transactions, if need - std::list txs; + std::list txs; get_transactions(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx : txs) { @@ -1015,7 +1100,7 @@ bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& return true; } -bool blockchain_storage::get_alternative_blocks(std::list& blocks) { +bool blockchain_storage::get_alternative_blocks(std::list& blocks) { CRITICAL_REGION_LOCAL(m_blockchain_lock); for (auto& alt_bl : m_alternative_chains) { blocks.push_back(alt_bl.second.bl); @@ -1031,18 +1116,18 @@ size_t blockchain_storage::get_alternative_blocks_count() { bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { CRITICAL_REGION_LOCAL(m_blockchain_lock); - const transaction& tx = transactionByIndex(amount_outs[i].first).tx; + const Transaction& tx = transactionByIndex(amount_outs[i].first).tx; CHECK_AND_ASSERT_MES(tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type"); + CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(TransactionOutputToKey), false, "unknown tx out type"); //check if transaction is unlocked - if (!is_tx_spendtime_unlocked(tx.unlock_time)) + if (!is_tx_spendtime_unlocked(tx.unlockTime)) return false; 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; - oen.out_key = boost::get(tx.vout[amount_outs[i].second].target).key; + oen.out_key = boost::get(tx.vout[amount_outs[i].second].target).key; return true; } @@ -1055,7 +1140,7 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vectorsecond; - return true; + //this should NEVER happen, but, dose of paranoia in such cases is not too bad + LOG_ERROR("Internal error handling connection, can't find split point"); + return false; } uint64_t blockchain_storage::block_difficulty(size_t i) @@ -1173,7 +1239,7 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind { ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size << "\nid\t\t" << get_block_hash(m_blocks[i].bl) - << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; + << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.txHashes.size() << ENDL; } LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str()); LOG_PRINT_L0("Blockchain printed with log level 1"); @@ -1182,11 +1248,17 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind void blockchain_storage::print_blockchain_index() { std::stringstream ss; CRITICAL_REGION_LOCAL(m_blockchain_lock); - for (auto& i : m_blockMap) { - ss << "id\t\t" << i.first << " height" << i.second << ENDL << ""; + + std::list blockIds; + m_blockIndex.getBlockIds(0, std::numeric_limits::max(), blockIds); + + LOG_PRINT_L0("Current blockchain index:" << ENDL); + + size_t height = 0; + for (auto i = blockIds.begin(); i != blockIds.end(); ++i, ++height) { + LOG_PRINT_L0("id\t\t" << *i << " height" << height); } - LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); } void blockchain_storage::print_blockchain_outs(const std::string& file) { @@ -1216,19 +1288,11 @@ bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); if (!find_blockchain_supplement(qblock_ids, start_height)) { return false; @@ -1240,7 +1304,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list mis; - get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis); + get_transactions(m_blocks[i].bl.txHashes, blocks.back().second, mis); CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); } @@ -1250,7 +1314,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::listsecond); + const TransactionEntry& tx = transactionByIndex(it->second); CHECK_AND_ASSERT_MES(tx.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); indexs.resize(tx.m_global_output_indexes.size()); for (size_t i = 0; i < tx.m_global_output_indexes.size(); ++i) { @@ -1282,8 +1346,12 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std:: return true; } -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) { +bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail) { CRITICAL_REGION_LOCAL(m_blockchain_lock); + + if (tail) + tail->id = get_tail_id(tail->height); + bool res = check_tx_inputs(tx, &max_used_block_height); if (!res) return false; CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); @@ -1291,63 +1359,73 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_us return true; } -bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) { - for(const txin_v& in : tx.vin) { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true); - if (have_tx_keyimg_as_spent(in_to_key.k_image)) { - return true; +bool blockchain_storage::have_tx_keyimges_as_spent(const Transaction &tx) { + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + if (have_tx_keyimg_as_spent(boost::get(in).keyImage)) { + return true; + } } } return false; } -bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) { +bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height) { crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); } -bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) { - size_t sig_index = 0; +bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) { + size_t inputIndex = 0; if (pmax_used_block_height) { *pmax_used_block_height = 0; } + crypto::hash transactionHash = get_transaction_hash(tx); for (const auto& txin : tx.vin) { - CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs"); - const txin_to_key& in_to_key = boost::get(txin); + assert(inputIndex < tx.signatures.size()); + if (txin.type() == typeid(TransactionInputToKey)) { + const TransactionInputToKey& in_to_key = boost::get(txin); + CHECK_AND_ASSERT_MES(!in_to_key.keyOffsets.empty(), false, "empty in_to_key.keyOffsets in transaction with id " << get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); + if (have_tx_keyimg_as_spent(in_to_key.keyImage)) { + LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.keyImage)); + return false; + } - if (have_tx_keyimg_as_spent(in_to_key.k_image)) - { - LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image)); - return false; - } + if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[inputIndex], pmax_used_block_height)) { + LOG_PRINT_L0("Failed to check ring signature for tx " << transactionHash); + return false; + } - CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); - if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) { - LOG_PRINT_L0("Failed to check ring signature for tx " << get_transaction_hash(tx)); + ++inputIndex; + } else if (txin.type() == typeid(TransactionInputMultisignature)) { + if (!validateInput(::boost::get(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) { + return false; + } + + ++inputIndex; + } else { + LOG_PRINT_L0("Transaction << " << transactionHash << " contains input of unsupported type."); return false; } - - sig_index++; } return true; } bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { - if (unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) { + if (unlock_time < m_currency.maxBlockHeight()) { //interpret as block index - if (get_current_blockchain_height() - 1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + if (get_current_blockchain_height() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time) return true; else return false; } else { //interpret as time uint64_t current_time = static_cast(time(NULL)); - if (current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) + if (current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlock_time) return true; else return false; @@ -1356,7 +1434,7 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { return false; } -bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { +bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); struct outputs_visitor @@ -1365,20 +1443,20 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h blockchain_storage& m_bch; outputs_visitor(std::vector& results_collector, blockchain_storage& bch) :m_results_collector(results_collector), m_bch(bch) {} - bool handle_output(const transaction& tx, const tx_out& out) { + bool handle_output(const Transaction& tx, const TransactionOutput& out) { //check tx unlock time - if (!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) { - LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << tx.unlock_time); + if (!m_bch.is_tx_spendtime_unlocked(tx.unlockTime)) { + LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlockTime = " << tx.unlockTime); return false; } - if (out.target.type() != typeid(txout_to_key)) + if (out.target.type() != typeid(TransactionOutputToKey)) { LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); return false; } - m_results_collector.push_back(&boost::get(out.target).key); + m_results_collector.push_back(&boost::get(out.target).key); return true; } }; @@ -1387,12 +1465,13 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h std::vector output_keys; outputs_visitor vi(output_keys, *this); if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) { - LOG_PRINT_L0("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); + LOG_PRINT_L0("Failed to get output keys for tx with amount = " << m_currency.formatAmount(txin.amount) << + " and count indexes " << txin.keyOffsets.size()); return false; } - if (txin.key_offsets.size() != output_keys.size()) { - LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); + if (txin.keyOffsets.size() != output_keys.size()) { + LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.keyOffsets.size() << " returned wrong keys count " << output_keys.size()); return false; } @@ -1401,7 +1480,7 @@ bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::h return true; } - return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data()); + return crypto::check_ring_signature(tx_prefix_hash, txin.keyImage, output_keys, sig.data()); } uint64_t blockchain_storage::get_adjusted_time() { @@ -1409,14 +1488,14 @@ uint64_t blockchain_storage::get_adjusted_time() { return time(NULL); } -bool blockchain_storage::check_block_timestamp_main(const block& b) { - if (b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) { +bool blockchain_storage::check_block_timestamp_main(const Block& b) { + if (b.timestamp > get_adjusted_time() + m_currency.blockFutureTimeLimit()) { LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); return false; } std::vector timestamps; - size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0 : m_blocks.size() - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + size_t offset = m_blocks.size() <= m_currency.timestampCheckWindow() ? 0 : m_blocks.size() - m_currency.timestampCheckWindow(); for (; offset != m_blocks.size(); ++offset) { timestamps.push_back(m_blocks[offset].bl.timestamp); } @@ -1424,37 +1503,100 @@ bool blockchain_storage::check_block_timestamp_main(const block& b) { return check_block_timestamp(std::move(timestamps), b); } -bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) { - if (timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) { +bool blockchain_storage::check_block_timestamp(std::vector timestamps, const Block& b) { + if (timestamps.size() < m_currency.timestampCheckWindow()) { return true; } uint64_t median_ts = epee::misc_utils::median(timestamps); if (b.timestamp < median_ts) { - LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); + LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << + ", less than median of last " << m_currency.timestampCheckWindow() << " blocks, " << median_ts); + return false; + } + + return true; +} + +bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& blockHash) { + uint64_t height = get_block_height(b); + const uint8_t expectedBlockVersion = get_block_major_version_for_height(height); + if (b.majorVersion != expectedBlockVersion) { + LOG_PRINT_L0("Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << + ", at height " << height << " expected version is " << static_cast(expectedBlockVersion)); + return false; + } + + return true; +} + +bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash& blockHash) { + if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { + auto serializer = makeParentBlockSerializer(b, false, false); + size_t parentBlockSize; + if (!get_object_blobsize(serializer, parentBlockSize)) { + LOG_ERROR("Block " << blockHash << ": failed to determine parent block size"); + return false; + } + + if (parentBlockSize > 2 * 1024) { + LOG_PRINT_L0("Block " << blockHash << " contains too big parent block: " << parentBlockSize << + " bytes, expected no more than " << 2 * 1024 << " bytes"); + return false; + } + } + + return true; +} + +bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height) { + size_t maxBlockCumulativeSize = m_currency.maxBlockCumulativeSize(height); + if (cumulativeBlockSize > maxBlockCumulativeSize) { + LOG_PRINT_L0("Block " << blockId << " is too big: " << cumulativeBlockSize << " bytes, " << + "exptected no more than " << maxBlockCumulativeSize << " bytes"); return false; } return true; } +// Returns true, if cumulativeSize is calculated precisely, else returns false. +bool blockchain_storage::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) { + std::vector blockTxs; + std::vector missedTxs; + get_transactions(block.txHashes, blockTxs, missedTxs, true); + + cumulativeSize = get_object_blobsize(block.minerTx); + for (const Transaction& tx : blockTxs) { + cumulativeSize += get_object_blobsize(tx); + } + + return missedTxs.empty(); +} + bool blockchain_storage::update_next_comulative_size_limit() { std::vector sz; - get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + get_last_n_blocks_sizes(sz, m_currency.rewardBlocksWindow()); uint64_t median = epee::misc_utils::median(sz); - if (median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) - median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + if (median <= m_currency.blockGrantedFullRewardZone()) + median = m_currency.blockGrantedFullRewardZone(); m_current_block_cumul_sz_limit = median * 2; return true; } -bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) { +bool blockchain_storage::add_new_block(const Block& bl_, block_verification_context& bvc) { //copy block here to let modify block.target - block bl = bl_; - crypto::hash id = get_block_hash(bl); + Block bl = bl_; + crypto::hash id; + if (!get_block_hash(bl, id)) { + LOG_ERROR("Failed to get block hash, possible block has invalid format"); + bvc.m_verifivation_failed = true; + return false; + } + CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process CRITICAL_REGION_LOCAL1(m_blockchain_lock); if (have_block(id)) { @@ -1464,7 +1606,7 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont } //check that block refers to chain tail - if (!(bl.prev_id == get_tail_id())) { + if (!(bl.prevId == get_tail_id())) { //chain switching or wrong block bvc.m_added_to_main_chain = false; return handle_alternative_block(bl, id, bvc); @@ -1474,24 +1616,34 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont return pushBlock(bl, bvc); } -const blockchain_storage::Transaction& blockchain_storage::transactionByIndex(TransactionIndex index) { +const blockchain_storage::TransactionEntry& blockchain_storage::transactionByIndex(TransactionIndex index) { return m_blocks[index.block].transactions[index.transaction]; } -bool blockchain_storage::pushBlock(const block& blockData, block_verification_context& bvc) { +bool blockchain_storage::pushBlock(const Block& blockData, block_verification_context& bvc) { CRITICAL_REGION_LOCAL(m_blockchain_lock); TIME_MEASURE_START(block_processing_time); crypto::hash blockHash = get_block_hash(blockData); - if (m_blockMap.count(blockHash) != 0) { + if (m_blockIndex.hasBlock(blockHash)) { LOG_ERROR("Block " << blockHash << " already exists in blockchain."); bvc.m_verifivation_failed = true; return false; } - if (blockData.prev_id != get_tail_id()) { - LOG_PRINT_L0("Block " << blockHash << " has wrong prev_id: " << blockData.prev_id << ", expected: " << get_tail_id()); + if (!checkBlockVersion(blockData, blockHash)) { + bvc.m_verifivation_failed = true; + return false; + } + + if (!checkParentBlockSize(blockData, blockHash)) { + bvc.m_verifivation_failed = true; + return false; + } + + if (blockData.prevId != get_tail_id()) { + LOG_PRINT_L0("Block " << blockHash << " has wrong prevId: " << blockData.prevId << ", expected: " << get_tail_id()); bvc.m_verifivation_failed = true; return false; } @@ -1516,8 +1668,7 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co return false; } } else { - proof_of_work = get_block_longhash(m_cn_context, blockData, m_blocks.size()); - if (!check_hash(proof_of_work, currentDifficulty)) { + if (!m_currency.checkProofOfWork(m_cn_context, blockData, currentDifficulty, proof_of_work)) { LOG_PRINT_L0("Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty); bvc.m_verifivation_failed = true; return false; @@ -1532,19 +1683,19 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co return false; } - crypto::hash minerTransactionHash = get_transaction_hash(blockData.miner_tx); + crypto::hash minerTransactionHash = get_transaction_hash(blockData.minerTx); - Block block; + BlockEntry block; block.bl = blockData; block.transactions.resize(1); - block.transactions[0].tx = blockData.miner_tx; + block.transactions[0].tx = blockData.minerTx; TransactionIndex transactionIndex = { static_cast(m_blocks.size()), static_cast(0) }; pushTransaction(block, minerTransactionHash, transactionIndex); - size_t coinbase_blob_size = get_object_blobsize(blockData.miner_tx); + size_t coinbase_blob_size = get_object_blobsize(blockData.minerTx); size_t cumulative_block_size = coinbase_blob_size; uint64_t fee_summary = 0; - for (const crypto::hash& tx_id : blockData.tx_hashes) { + for (const crypto::hash& tx_id : blockData.txHashes) { block.transactions.resize(block.transactions.size() + 1); size_t blob_size = 0; uint64_t fee = 0; @@ -1577,9 +1728,15 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co fee_summary += fee; } - uint64_t base_reward = 0; - uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins : 0; - if (!validate_miner_transaction(blockData, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) { + if (!checkCumulativeBlockSize(blockHash, cumulative_block_size, m_blocks.size())) { + bvc.m_verifivation_failed = true; + return false; + } + + int64_t emissionChange = 0; + uint64_t reward = 0; + uint64_t already_generated_coins = m_blocks.empty() ? 0 : m_blocks.back().already_generated_coins; + if (!validate_miner_transaction(blockData, m_blocks.size(), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) { LOG_PRINT_L0("Block " << blockHash << " has invalid miner transaction"); bvc.m_verifivation_failed = true; popTransactions(block, minerTransactionHash); @@ -1589,7 +1746,7 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co block.height = static_cast(m_blocks.size()); block.block_cumulative_size = cumulative_block_size; block.cumulative_difficulty = currentDifficulty; - block.already_generated_coins = already_generated_coins + base_reward; + block.already_generated_coins = already_generated_coins + emissionChange; if (m_blocks.size() > 0) { block.cumulative_difficulty += m_blocks.back().cumulative_difficulty; } @@ -1600,23 +1757,25 @@ bool blockchain_storage::pushBlock(const block& blockData, block_verification_co LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash << ENDL << "PoW:\t" << proof_of_work << ENDL << "HEIGHT " << block.height << ", difficulty:\t" << currentDifficulty - << 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 + << ENDL << "block reward: " << m_currency.formatAmount(reward) << ", fee = " << m_currency.formatAmount(fee_summary) + << ", coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); bvc.m_added_to_main_chain = true; + + m_upgradeDetector.blockPushed(); + return true; } -bool blockchain_storage::pushBlock(Block& block) { +bool blockchain_storage::pushBlock(BlockEntry& block) { crypto::hash blockHash = get_block_hash(block.bl); - auto result = m_blockMap.insert(std::make_pair(blockHash, static_cast(m_blocks.size()))); - if (!result.second) { - LOG_ERROR("Duplicate block was pushed to blockchain."); - return false; - } m_blocks.push_back(block); + m_blockIndex.push(blockHash); + + assert(m_blockIndex.size() == m_blocks.size()); + return true; } @@ -1626,29 +1785,37 @@ void blockchain_storage::popBlock(const crypto::hash& blockHash) { return; } - popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.miner_tx)); + popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.minerTx)); m_blocks.pop_back(); - size_t count = m_blockMap.erase(blockHash); - if (count != 1) { - LOG_ERROR("Blockchain consistency broken - cannot find block by hash."); - } + m_blockIndex.pop(); + + assert(m_blockIndex.size() == m_blocks.size()); + + m_upgradeDetector.blockPopped(); } -bool blockchain_storage::pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { +bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); if (!result.second) { LOG_ERROR("Duplicate transaction was pushed to blockchain."); return false; } - Transaction& transaction = block.transactions[transactionIndex.transaction]; + TransactionEntry& transaction = block.transactions[transactionIndex.transaction]; + + if (!checkMultisignatureInputsDiff(transaction.tx)) { + LOG_ERROR("Double spending transaction was pushed to blockchain."); + m_transactionMap.erase(transactionHash); + return false; + } + for (size_t i = 0; i < transaction.tx.vin.size(); ++i) { - if (transaction.tx.vin[i].type() == typeid(txin_to_key)) { - auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).k_image); + if (transaction.tx.vin[i].type() == typeid(TransactionInputToKey)) { + auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).keyImage); if (!result.second) { LOG_ERROR("Double spending transaction was pushed to blockchain."); for (size_t j = 0; j < i; ++j) { - m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).k_image); + m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).keyImage); } m_transactionMap.erase(transactionHash); @@ -1657,52 +1824,108 @@ bool blockchain_storage::pushTransaction(Block& block, const crypto::hash& trans } } + for (const auto& inv : transaction.tx.vin) { + if (inv.type() == typeid(TransactionInputMultisignature)) { + const TransactionInputMultisignature& in = ::boost::get(inv); + auto& amountOutputs = m_multisignatureOutputs[in.amount]; + amountOutputs[in.outputIndex].isUsed = true; + } + } + transaction.m_global_output_indexes.resize(transaction.tx.vout.size()); for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) { - auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; - transaction.m_global_output_indexes[output] = amountOutputs.size(); - amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); + if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputToKey)) { + auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; + transaction.m_global_output_indexes[output] = amountOutputs.size(); + amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); + } else if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputMultisignature)) { + auto& amountOutputs = m_multisignatureOutputs[transaction.tx.vout[output].amount]; + MultisignatureOutputUsage outputUsage = {transactionIndex, output, false}; + amountOutputs.push_back(outputUsage); + } } return true; } -void blockchain_storage::popTransaction(const transaction& transaction, const crypto::hash& transactionHash) { +void blockchain_storage::popTransaction(const Transaction& transaction, const crypto::hash& transactionHash) { TransactionIndex transactionIndex = m_transactionMap.at(transactionHash); - for (size_t output = 0; output < transaction.vout.size(); ++output) { - auto amountOutputs = m_outputs.find(transaction.vout[transaction.vout.size() - 1 - output].amount); - if (amountOutputs == m_outputs.end()) { - LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); - continue; - } + for (size_t outputIndex = 0; outputIndex < transaction.vout.size(); ++outputIndex) { + const TransactionOutput& output = transaction.vout[transaction.vout.size() - 1 - outputIndex]; + if (output.target.type() == typeid(TransactionOutputToKey)) { + auto amountOutputs = m_outputs.find(output.amount); + if (amountOutputs == m_outputs.end()) { + LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); + continue; + } - if (amountOutputs->second.empty()) { - LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); - continue; - } + if (amountOutputs->second.empty()) { + LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); + continue; + } - if (amountOutputs->second.back().first.block != transactionIndex.block || amountOutputs->second.back().first.transaction != transactionIndex.transaction) { - LOG_ERROR("Blockchain consistency broken - invalid transaction index."); - continue; - } + if (amountOutputs->second.back().first.block != transactionIndex.block || amountOutputs->second.back().first.transaction != transactionIndex.transaction) { + LOG_ERROR("Blockchain consistency broken - invalid transaction index."); + continue; + } - if (amountOutputs->second.back().second != transaction.vout.size() - 1 - output) { - LOG_ERROR("Blockchain consistency broken - invalid output index."); - continue; - } + if (amountOutputs->second.back().second != transaction.vout.size() - 1 - outputIndex) { + LOG_ERROR("Blockchain consistency broken - invalid output index."); + continue; + } + + amountOutputs->second.pop_back(); + if (amountOutputs->second.empty()) { + m_outputs.erase(amountOutputs); + } + } else if (output.target.type() == typeid(TransactionOutputMultisignature)) { + auto amountOutputs = m_multisignatureOutputs.find(output.amount); + if (amountOutputs == m_multisignatureOutputs.end()) { + LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); + continue; + } + + if (amountOutputs->second.empty()) { + LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); + continue; + } + + if (amountOutputs->second.back().isUsed) { + LOG_ERROR("Blockchain consistency broken - attempting to remove used output."); + continue; + } + + if (amountOutputs->second.back().transactionIndex.block != transactionIndex.block || amountOutputs->second.back().transactionIndex.transaction != transactionIndex.transaction) { + LOG_ERROR("Blockchain consistency broken - invalid transaction index."); + continue; + } + + if (amountOutputs->second.back().outputIndex != transaction.vout.size() - 1 - outputIndex) { + LOG_ERROR("Blockchain consistency broken - invalid output index."); + continue; + } - amountOutputs->second.pop_back(); - if (amountOutputs->second.empty()) { - m_outputs.erase(amountOutputs); + amountOutputs->second.pop_back(); + if (amountOutputs->second.empty()) { + m_multisignatureOutputs.erase(amountOutputs); + } } } for (auto& input : transaction.vin) { - if (input.type() == typeid(txin_to_key)) { - size_t count = m_spent_keys.erase(::boost::get(input).k_image); + if (input.type() == typeid(TransactionInputToKey)) { + size_t count = m_spent_keys.erase(::boost::get(input).keyImage); if (count != 1) { LOG_ERROR("Blockchain consistency broken - cannot find spent key."); } + } else if (input.type() == typeid(TransactionInputMultisignature)) { + const TransactionInputMultisignature& in = ::boost::get(input); + auto& amountOutputs = m_multisignatureOutputs[in.amount]; + if (!amountOutputs[in.outputIndex].isUsed) { + LOG_ERROR("Blockchain consistency broken - multisignature output not marked as used."); + } + + amountOutputs[in.outputIndex].isUsed = false; } } @@ -1712,14 +1935,88 @@ void blockchain_storage::popTransaction(const transaction& transaction, const cr } } -void blockchain_storage::popTransactions(const Block& block, const crypto::hash& minerTransactionHash) { +void blockchain_storage::popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash) { for (size_t i = 0; i < block.transactions.size() - 1; ++i) { - popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.tx_hashes[block.transactions.size() - 2 - i]); + popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.txHashes[block.transactions.size() - 2 - i]); tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) { LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); } } - popTransaction(block.bl.miner_tx, minerTransactionHash); + popTransaction(block.bl.minerTx, minerTransactionHash); +} + +bool blockchain_storage::validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector& transactionSignatures) { + assert(input.signatures == transactionSignatures.size()); + MultisignatureOutputsContainer::const_iterator amountOutputs = m_multisignatureOutputs.find(input.amount); + if (amountOutputs == m_multisignatureOutputs.end()) { + LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid amount."); + return false; + } + + if (input.outputIndex >= amountOutputs->second.size()) { + LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid outputIndex."); + return false; + } + + const MultisignatureOutputUsage& outputIndex = amountOutputs->second[input.outputIndex]; + if (outputIndex.isUsed) { + LOG_PRINT_L1("Transaction << " << transactionHash << " contains double spending multisignature input."); + return false; + } + + const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx; + if (!is_tx_spendtime_unlocked(outputTransaction.unlockTime)) { + LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input which points to a locked transaction."); + return false; + } + + assert(outputTransaction.vout[outputIndex.outputIndex].amount == input.amount); + assert(outputTransaction.vout[outputIndex.outputIndex].target.type() == typeid(TransactionOutputMultisignature)); + const TransactionOutputMultisignature& output = ::boost::get(outputTransaction.vout[outputIndex.outputIndex].target); + if (input.signatures != output.requiredSignatures) { + LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid signature count."); + return false; + } + + std::size_t inputSignatureIndex = 0; + std::size_t outputKeyIndex = 0; + while (inputSignatureIndex < input.signatures) { + if (outputKeyIndex == output.keys.size()) { + LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid signatures."); + return false; + } + + if (crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) { + ++inputSignatureIndex; + } + + ++outputKeyIndex; + } + + return true; +} + +bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + if (startOffset >= m_blocks.size()) { + return false; + } + + auto bound = std::lower_bound(m_blocks.begin() + startOffset, m_blocks.end(), timestamp - m_currency.blockFutureTimeLimit(), + [](const BlockEntry& b, uint64_t timestamp) { return b.bl.timestamp < timestamp; }); + + if (bound == m_blocks.end()) { + return false; + } + + height = std::distance(m_blocks.begin(), bound); + return true; +} + +bool blockchain_storage::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_blockIndex.getBlockIds(startHeight, maxCount, items); } diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 6b6b119cc3..65e6a43272 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -17,54 +17,80 @@ #pragma once +#include + +#include "Currency.h" #include "SwappedVector.h" +#include "UpgradeDetector.h" #include "cryptonote_format_utils.h" #include "tx_pool.h" #include "common/util.h" -#include "rpc/core_rpc_server_commands_defs.h" #include "checkpoints.h" +#include "google/sparse_hash_set" +#include "google/sparse_hash_map" + +#include "ITransactionValidator.h" +#include "BlockIndex.h" + namespace cryptonote { - class blockchain_storage { + struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; + struct NOTIFY_REQUEST_GET_OBJECTS_request; + struct NOTIFY_RESPONSE_GET_OBJECTS_request; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount; + + using CryptoNote::BlockInfo; + class blockchain_storage : public CryptoNote::ITransactionValidator { public: - blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false) - {}; + blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool); + + // ITransactionValidator + virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock); + virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed); + virtual bool haveSpentKeyImages(const cryptonote::Transaction& tx); bool init() { return init(tools::get_default_data_dir(), true); } bool init(const std::string& config_folder, bool load_existing); bool deinit(); + bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height); + bool getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items); + void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); - bool get_alternative_blocks(std::list& blocks); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); + bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); crypto::hash get_block_id_by_height(uint64_t height); - bool get_block_by_hash(const crypto::hash &h, block &blk); + bool get_block_by_hash(const crypto::hash &h, Block &blk); template void serialize(archive_t & ar, const unsigned int version); bool have_tx(const crypto::hash &id); - bool have_tx_keyimges_as_spent(const transaction &tx); + bool have_tx_keyimges_as_spent(const Transaction &tx); uint64_t get_current_blockchain_height(); crypto::hash get_tail_id(); crypto::hash get_tail_id(uint64_t& height); difficulty_type get_difficulty_for_next_block(); - bool add_new_block(const block& bl_, block_verification_context& bvc); - bool reset_and_set_genesis_block(const block& b); - bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + uint64_t getCoinsInCirculation(); + uint8_t get_block_major_version_for_height(uint64_t height) const; + bool add_new_block(const Block& bl_, block_verification_context& bvc); + bool reset_and_set_genesis_block(const Block& b); + bool create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); bool have_block(const crypto::hash& id); size_t get_total_transactions(); bool get_short_chain_history(std::list& ids); - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list>>& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); - 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); + bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); // !!!! + bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); + bool find_blockchain_supplement(const std::list& qblock_ids, std::list>>& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp); + 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); bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count); bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - bool store_blockchain(); - bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id); + bool check_tx_inputs(const Transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail = 0); uint64_t get_current_comulative_blocksize_limit(); bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); @@ -74,13 +100,13 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_blockchain_lock); for (const auto& bl_id : block_ids) { - auto it = m_blockMap.find(bl_id); - if (it == m_blockMap.end()) { + uint64_t height = 0; + if (!m_blockIndex.getBlockHeight(bl_id, height)) { missed_bs.push_back(bl_id); } else { - CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) - << " have index record with offset="<second<< ", bigger then m_blocks.size()=" << m_blocks.size()); - blocks.push_back(m_blocks[it->second].bl); + CHECK_AND_ASSERT_MES(height < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) + << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size()); + blocks.push_back(m_blocks[height].bl); } } @@ -88,7 +114,7 @@ namespace cryptonote { } template - void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { + void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { CRITICAL_REGION_LOCAL(m_blockchain_lock); for (const auto& tx_id : txs_ids) { @@ -99,6 +125,12 @@ namespace cryptonote { txs.push_back(transactionByIndex(it->second).tx); } } + + if (checkTxPool) { + auto poolTxIds = std::move(missed_txs); + missed_txs.clear(); + m_tx_pool.getTransactions(poolTxIds, txs, missed_txs); + } } //debug functions @@ -107,8 +139,8 @@ namespace cryptonote { void print_blockchain_outs(const std::string& file); private: - struct Transaction { - transaction tx; + struct TransactionEntry { + Transaction tx; std::vector m_global_output_indexes; template void serialize(archive_t & ar, unsigned int version); @@ -119,13 +151,13 @@ namespace cryptonote { END_SERIALIZE() }; - struct Block { - block bl; + struct BlockEntry { + Block bl; uint32_t height; uint64_t block_cumulative_size; difficulty_type cumulative_difficulty; uint64_t already_generated_coins; - std::vector transactions; + std::vector transactions; template void serialize(Archive& archive, unsigned int version); @@ -146,10 +178,20 @@ namespace cryptonote { template void serialize(Archive& archive, unsigned int version); }; - typedef std::unordered_set key_images_container; - typedef std::unordered_map blocks_ext_by_hash; - typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction + struct MultisignatureOutputUsage { + TransactionIndex transactionIndex; + uint16_t outputIndex; + bool isUsed; + + template void serialize(Archive& archive, unsigned int version); + }; + + typedef google::sparse_hash_set key_images_container; + typedef std::unordered_map blocks_ext_by_hash; + typedef google::sparse_hash_map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction + typedef std::map> MultisignatureOutputsContainer; + const Currency& m_currency; tx_memory_pool& m_tx_pool; epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock crypto::cn_context m_cn_context; @@ -164,53 +206,80 @@ namespace cryptonote { std::atomic m_is_in_checkpoint_zone; std::atomic m_is_blockchain_storing; - typedef SwappedVector Blocks; + typedef SwappedVector Blocks; typedef std::unordered_map BlockMap; typedef std::unordered_map TransactionMap; + typedef BasicUpgradeDetector UpgradeDetector; + + friend class BlockCacheSerializer; Blocks m_blocks; - BlockMap m_blockMap; + CryptoNote::BlockIndex m_blockIndex; TransactionMap m_transactionMap; + MultisignatureOutputsContainer m_multisignatureOutputs; + UpgradeDetector m_upgradeDetector; - template bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); + bool storeCache(); + template bool scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); - bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); - difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, Block& bei); - bool prevalidate_miner_transaction(const block& b, uint64_t height); - 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 validate_transaction(const block& b, uint64_t height, const transaction& tx); - bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); + bool handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); + bool prevalidate_miner_transaction(const Block& b, uint64_t height); + bool validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange); + bool validate_transaction(const Block& b, uint64_t height, const Transaction& tx); + bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); bool get_last_n_blocks_sizes(std::vector& sz, size_t count); - bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); + bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount& result_outs, uint64_t amount, size_t i); bool is_tx_spendtime_unlocked(uint64_t unlock_time); size_t find_end_of_allowed_index(const std::vector>& amount_outs); - bool check_block_timestamp_main(const block& b); - bool check_block_timestamp(std::vector timestamps, const block& b); + bool check_block_timestamp_main(const Block& b); + bool check_block_timestamp(std::vector timestamps, const Block& b); uint64_t get_adjusted_time(); bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); + bool checkBlockVersion(const Block& b, const crypto::hash& blockHash); + bool checkParentBlockSize(const Block& b, const crypto::hash& blockHash); + bool checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height); + bool getBlockCumulativeSize(const Block& block, size_t& cumulativeSize); bool update_next_comulative_size_limit(); - bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); - bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); - bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); - bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); + bool check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); + bool check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height = NULL); bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); - const Transaction& transactionByIndex(TransactionIndex index); - bool pushBlock(const block& blockData, block_verification_context& bvc); - bool pushBlock(Block& block); + const TransactionEntry& transactionByIndex(TransactionIndex index); + bool pushBlock(const Block& blockData, block_verification_context& bvc); + bool pushBlock(BlockEntry& block); void popBlock(const crypto::hash& blockHash); - bool pushTransaction(Block& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); - void popTransaction(const transaction& transaction, const crypto::hash& transactionHash); - void popTransactions(const Block& block, const crypto::hash& minerTransactionHash); + bool pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); + void popTransaction(const Transaction& transaction, const crypto::hash& transactionHash); + void popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash); + bool validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector& transactionSignatures); + + friend class LockedBlockchainStorage; }; + class LockedBlockchainStorage: boost::noncopyable { + public: + + LockedBlockchainStorage(blockchain_storage& bc) + : m_bc(bc), m_lock(bc.m_blockchain_lock) {} + + blockchain_storage* operator -> () { + return &m_bc; + } + + private: + + blockchain_storage& m_bc; + epee::critical_region_t m_lock; + }; - template bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { + template bool blockchain_storage::scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); auto it = m_outputs.find(tx_in_to_key.amount); - if (it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) + if (it == m_outputs.end() || !tx_in_to_key.keyOffsets.size()) return false; - std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); + std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.keyOffsets); std::vector>& amount_outs_vec = it->second; size_t count = 0; for (uint64_t i : absolute_offsets) { @@ -222,7 +291,7 @@ namespace cryptonote { //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); //CHECK_AND_ASSERT_MES(tx_it != m_transactionMap.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); - const Transaction& tx = transactionByIndex(amount_outs_vec[i].first); + const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first); CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx.tx.vout.size(), false, "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx.tx.vout.size()); if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second])) { diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h deleted file mode 100644 index 5254332a4d..0000000000 --- a/src/cryptonote_core/checkpoints_create.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "checkpoints.h" -#include "misc_log_ex.h" - -#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(checkpoints.add_checkpoint(h, hash), false); - -namespace cryptonote { - inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) - { - ADD_CHECKPOINT(79000, "cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02"); - ADD_CHECKPOINT(140000, "993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772"); - ADD_CHECKPOINT(200000, "a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073"); - ADD_CHECKPOINT(230580, "32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103"); - ADD_CHECKPOINT(260000, "f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947"); - ADD_CHECKPOINT(300000, "8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff"); - ADD_CHECKPOINT(390285, "e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7"); - ADD_CHECKPOINT(417000, "2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f"); - ADD_CHECKPOINT(427193, "00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311"); - ADD_CHECKPOINT(453537, "d17de6916c5aa6ffcae575309c80b0f8fdcd0a84b5fa8e41a841897d4b5a4e97"); - ADD_CHECKPOINT(462250, "13468d210a5ec884cf839f0259f247ccf3efef0414ac45172033d32c739beb3e"); - ADD_CHECKPOINT(468000, "251bcbd398b1f593193a7210934a3d87f692b2cb0c45206150f59683dd7e9ba1"); - ADD_CHECKPOINT(480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"); - ADD_CHECKPOINT(484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"); - ADD_CHECKPOINT(506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"); - - return true; - } -} diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_core/connection_context.h index 0fed9b0c45..f18272af18 100644 --- a/src/cryptonote_core/connection_context.h +++ b/src/cryptonote_core/connection_context.h @@ -16,11 +16,16 @@ // along with Bytecoin. If not, see . #pragma once -#include + #include +#include +#include + #include "net/net_utils_base.h" #include "copyable_atomic.h" +#include "crypto/hash.h" + namespace cryptonote { diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 0f3c7aaba6..5a02a73cf0 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -17,344 +17,421 @@ #pragma once -#include -#include +#include #include -#include // memcmp -#include -#include "serialization/serialization.h" -#include "serialization/variant.h" -#include "serialization/vector.h" + +#include +#include + +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "cryptonote_core/tx_extra.h" #include "serialization/binary_archive.h" -#include "serialization/json_archive.h" -#include "serialization/debug_archive.h" #include "serialization/crypto.h" -#include "serialization/keyvalue_serialization.h" // eepe named serialization -#include "string_tools.h" +#include "serialization/debug_archive.h" +#include "serialization/json_archive.h" +#include "serialization/serialization.h" +#include "serialization/variant.h" #include "cryptonote_config.h" -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "misc_language.h" -#include "tx_extra.h" +namespace cryptonote { + class account_base; + struct account_keys; + struct Block; + struct Transaction; + struct tx_extra_merge_mining_tag; -namespace cryptonote -{ + // Implemented in cryptonote_format_utils.cpp + bool get_transaction_hash(const Transaction& t, crypto::hash& res); + bool get_mm_tag_from_extra(const std::vector& tx, tx_extra_merge_mining_tag& mm_tag); - const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); - const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey); + const static crypto::hash null_hash = boost::value_initialized(); + const static crypto::public_key null_pkey = boost::value_initialized(); - typedef std::vector ring_signature; + /* inputs */ + struct TransactionInputGenerate { + size_t height; - /* outputs */ + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(height); + END_SERIALIZE() + }; - struct txout_to_script - { - std::vector keys; - std::vector script; + struct TransactionInputToKey { + uint64_t amount; + std::vector keyOffsets; + crypto::key_image keyImage; // double spending protection BEGIN_SERIALIZE_OBJECT() - FIELD(keys) - FIELD(script) + VARINT_FIELD(amount); + FIELD(keyOffsets); + FIELD(keyImage); END_SERIALIZE() }; - struct txout_to_scripthash - { - crypto::hash hash; - }; + struct TransactionInputMultisignature { + uint64_t amount; + uint32_t signatures; + uint64_t outputIndex; - struct txout_to_key - { - txout_to_key() { } - txout_to_key(const crypto::public_key &_key) : key(_key) { } - crypto::public_key key; + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount); + VARINT_FIELD(signatures); + VARINT_FIELD(outputIndex); + END_SERIALIZE() }; + /* outputs */ - /* inputs */ + struct TransactionOutputToKey { + TransactionOutputToKey() { } + TransactionOutputToKey(const crypto::public_key &_key) : key(_key) { } + crypto::public_key key; + }; - struct txin_gen - { - size_t height; + struct TransactionOutputMultisignature { + std::vector keys; + uint32_t requiredSignatures; BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(height) + FIELD(keys); + VARINT_FIELD(requiredSignatures); END_SERIALIZE() }; - struct txin_to_script - { - crypto::hash prev; - size_t prevout; - std::vector sigset; - + struct TransactionInputToScript { BEGIN_SERIALIZE_OBJECT() - FIELD(prev) - VARINT_FIELD(prevout) - FIELD(sigset) END_SERIALIZE() }; - struct txin_to_scripthash - { - crypto::hash prev; - size_t prevout; - txout_to_script script; - std::vector sigset; - + struct TransactionInputToScriptHash { BEGIN_SERIALIZE_OBJECT() - FIELD(prev) - VARINT_FIELD(prevout) - FIELD(script) - FIELD(sigset) END_SERIALIZE() }; - struct txin_to_key - { - uint64_t amount; - std::vector key_offsets; - crypto::key_image k_image; // double spending protection - + struct TransactionOutputToScript { BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount) - FIELD(key_offsets) - FIELD(k_image) END_SERIALIZE() }; + struct TransactionOutputToScriptHash { + BEGIN_SERIALIZE_OBJECT() + END_SERIALIZE() + }; - typedef boost::variant txin_v; + typedef boost::variant< + TransactionInputGenerate, + TransactionInputToScript, + TransactionInputToScriptHash, + TransactionInputToKey, + TransactionInputMultisignature> TransactionInput; - typedef boost::variant txout_target_v; + typedef boost::variant< + TransactionOutputToScript, + TransactionOutputToScriptHash, + TransactionOutputToKey, + TransactionOutputMultisignature> TransactionOutputTarget; - //typedef std::pair out_t; - struct tx_out - { + struct TransactionOutput { uint64_t amount; - txout_target_v target; + TransactionOutputTarget target; BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount) - FIELD(target) + VARINT_FIELD(amount); + FIELD(target); END_SERIALIZE() - - }; - class transaction_prefix - { - - public: + struct TransactionPrefix { // tx information size_t version; - uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time + uint64_t unlockTime; //number of block (or time), used as a limitation like: spend this tx not early then block/time - std::vector vin; - std::vector vout; + std::vector vin; + std::vector vout; //extra std::vector extra; BEGIN_SERIALIZE() - VARINT_FIELD(version) - if(CURRENT_TRANSACTION_VERSION < version) return false; - VARINT_FIELD(unlock_time) - FIELD(vin) - FIELD(vout) - FIELD(extra) + VARINT_FIELD(version); + if(CURRENT_TRANSACTION_VERSION < version) { + return false; + } + VARINT_FIELD(unlockTime); + FIELD(vin); + FIELD(vout); + FIELD(extra); END_SERIALIZE() - protected: - transaction_prefix(){} + TransactionPrefix() {} }; - class transaction: public transaction_prefix - { - public: + struct Transaction: public TransactionPrefix { std::vector > signatures; //count signatures always the same as inputs count - transaction(); - virtual ~transaction(); - void set_null(); + Transaction() { + clear(); + } + + void clear() { + version = 0; + unlockTime = 0; + vin.clear(); + vout.clear(); + extra.clear(); + signatures.clear(); + } BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)) + FIELDS(*static_cast(this)) ar.tag("signatures"); ar.begin_array(); PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); bool signatures_not_expected = signatures.empty(); - if (!signatures_not_expected && vin.size() != signatures.size()) + if (!signatures_not_expected && vin.size() != signatures.size()) { return false; + } - for (size_t i = 0; i < vin.size(); ++i) - { - size_t signature_size = get_signature_size(vin[i]); - if (signatures_not_expected) - { - if (0 == signature_size) + for (size_t i = 0; i < vin.size(); ++i) { + size_t signatureSize = getSignatureSize(vin[i]); + if (signatures_not_expected) { + if (0 == signatureSize) { continue; - else + } else { return false; + } } - PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); - if (signature_size != signatures[i].size()) + PREPARE_CUSTOM_VECTOR_SERIALIZATION(signatureSize, signatures[i]); + if (signatureSize != signatures[i].size()) { return false; + } FIELDS(signatures[i]); - if (vin.size() - i > 1) + if (vin.size() - i > 1) { ar.delimit_array(); + } } ar.end_array(); END_SERIALIZE() private: - static size_t get_signature_size(const txin_v& tx_in); + static size_t getSignatureSize(const TransactionInput& input) { + struct txin_signature_size_visitor : public boost::static_visitor { + size_t operator()(const TransactionInputGenerate& txin) const { return 0; } + size_t operator()(const TransactionInputToScript& txin) const { assert(false); return 0; } + size_t operator()(const TransactionInputToScriptHash& txin) const { assert(false); return 0; } + size_t operator()(const TransactionInputToKey& txin) const { return txin.keyOffsets.size();} + size_t operator()(const TransactionInputMultisignature& txin) const { return txin.signatures; } + }; + + return boost::apply_visitor(txin_signature_size_visitor(), input); + } }; + struct ParentBlock { + uint8_t majorVersion; + uint8_t minorVersion; + crypto::hash prevId; + size_t numberOfTransactions; + std::vector minerTxBranch; + Transaction minerTx; + std::vector blockchainBranch; + }; - inline - transaction::transaction() - { - set_null(); - } + struct ParentBlockSerializer { + ParentBlockSerializer(ParentBlock& parentBlock, uint64_t& timestamp, uint32_t& nonce, bool hashingSerialization, bool headerOnly) : + m_parentBlock(parentBlock), m_timestamp(timestamp), m_nonce(nonce), m_hashingSerialization(hashingSerialization), m_headerOnly(headerOnly) { + } - inline - transaction::~transaction() - { - //set_null(); - } + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD_N("majorVersion", m_parentBlock.majorVersion); + if (BLOCK_MAJOR_VERSION_1 < m_parentBlock.majorVersion) { + return false; + } + VARINT_FIELD_N("minorVersion", m_parentBlock.minorVersion); + VARINT_FIELD_N("timestamp", m_timestamp); + FIELD_N("prevId", m_parentBlock.prevId); + FIELD_N("nonce", m_nonce); + + if (m_hashingSerialization) { + crypto::hash minerTxHash; + if (!get_transaction_hash(m_parentBlock.minerTx, minerTxHash)) { + return false; + } - inline - void transaction::set_null() - { - version = 0; - unlock_time = 0; - vin.clear(); - vout.clear(); - extra.clear(); - signatures.clear(); - } + crypto::hash merkleRoot; + crypto::tree_hash_from_branch(m_parentBlock.minerTxBranch.data(), m_parentBlock.minerTxBranch.size(), minerTxHash, 0, merkleRoot); - inline - size_t transaction::get_signature_size(const txin_v& tx_in) - { - struct txin_signature_size_visitor : public boost::static_visitor - { - size_t operator()(const txin_gen& txin) const{return 0;} - size_t operator()(const txin_to_script& txin) const{return 0;} - size_t operator()(const txin_to_scripthash& txin) const{return 0;} - size_t operator()(const txin_to_key& txin) const {return txin.key_offsets.size();} - }; - - return boost::apply_visitor(txin_signature_size_visitor(), tx_in); - } + FIELD(merkleRoot); + } + + VARINT_FIELD_N("numberOfTransactions", m_parentBlock.numberOfTransactions); + if (m_parentBlock.numberOfTransactions < 1) { + return false; + } + if (!m_headerOnly) { + ar.tag("minerTxBranch"); + ar.begin_array(); + size_t branchSize = crypto::tree_depth(m_parentBlock.numberOfTransactions); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(branchSize, const_cast(m_parentBlock).minerTxBranch); + if (m_parentBlock.minerTxBranch.size() != branchSize) { + return false; + } + for (size_t i = 0; i < branchSize; ++i) { + FIELDS(m_parentBlock.minerTxBranch[i]); + if (i + 1 < branchSize) { + ar.delimit_array(); + } + } + ar.end_array(); + FIELD(m_parentBlock.minerTx); - /************************************************************************/ - /* */ - /************************************************************************/ - struct block_header - { - uint8_t major_version; - uint8_t minor_version; - uint64_t timestamp; - crypto::hash prev_id; + tx_extra_merge_mining_tag mmTag; + if (!get_mm_tag_from_extra(m_parentBlock.minerTx.extra, mmTag)) { + return false; + } + + if (mmTag.depth > 8 * sizeof(crypto::hash)) { + return false; + } + + ar.tag("blockchainBranch"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(mmTag.depth, const_cast(m_parentBlock).blockchainBranch); + if (mmTag.depth != m_parentBlock.blockchainBranch.size()) { + return false; + } + for (size_t i = 0; i < mmTag.depth; ++i) { + FIELDS(m_parentBlock.blockchainBranch[i]); + if (i + 1 < mmTag.depth) { + ar.delimit_array(); + } + } + ar.end_array(); + } + END_SERIALIZE() + + private: + ParentBlock& m_parentBlock; + uint64_t& m_timestamp; + uint32_t& m_nonce; + bool m_hashingSerialization; + bool m_headerOnly; + }; + + // Implemented below + inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly); + + struct BlockHeader { + uint8_t majorVersion; + uint8_t minorVersion; uint32_t nonce; + uint64_t timestamp; + crypto::hash prevId; BEGIN_SERIALIZE() - VARINT_FIELD(major_version) - if(major_version > CURRENT_BLOCK_MAJOR_VERSION) return false; - VARINT_FIELD(minor_version) - VARINT_FIELD(timestamp) - FIELD(prev_id) - FIELD(nonce) + VARINT_FIELD(majorVersion) + if (majorVersion > BLOCK_MAJOR_VERSION_2) { + return false; + } + VARINT_FIELD(minorVersion) + if (majorVersion == BLOCK_MAJOR_VERSION_1) { + VARINT_FIELD(timestamp); + FIELD(prevId); + FIELD(nonce); + } else if (majorVersion == BLOCK_MAJOR_VERSION_2) { + FIELD(prevId); + } else { + return false; + } END_SERIALIZE() }; - struct block: public block_header - { - transaction miner_tx; - std::vector tx_hashes; + struct Block: public BlockHeader { + ParentBlock parentBlock; + + Transaction minerTx; + std::vector txHashes; BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)) - FIELD(miner_tx) - FIELD(tx_hashes) + FIELDS(*static_cast(this)); + if (majorVersion == BLOCK_MAJOR_VERSION_2) { + auto serializer = makeParentBlockSerializer(*this, false, false); + FIELD_N("parentBlock", serializer); + } + FIELD(minerTx); + FIELD(txHashes); END_SERIALIZE() }; + inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly) { + Block& blockRef = const_cast(b); + return ParentBlockSerializer(blockRef.parentBlock, blockRef.timestamp, blockRef.nonce, hashingSerialization, headerOnly); + } - /************************************************************************/ - /* */ - /************************************************************************/ - struct account_public_address - { - crypto::public_key m_spend_public_key; - crypto::public_key m_view_public_key; + struct AccountPublicAddress { + crypto::public_key m_spendPublicKey; + crypto::public_key m_viewPublicKey; BEGIN_SERIALIZE_OBJECT() - FIELD(m_spend_public_key) - FIELD(m_view_public_key) + FIELD(m_spendPublicKey); + FIELD(m_viewPublicKey); END_SERIALIZE() - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key) - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key) - END_KV_SERIALIZE_MAP() }; - struct keypair - { + struct KeyPair { crypto::public_key pub; crypto::secret_key sec; - static inline keypair generate() - { - keypair k; + static KeyPair generate() { + KeyPair k; generate_keys(k.pub, k.sec); return k; } }; - //--------------------------------------------------------------- - } -BLOB_SERIALIZER(cryptonote::txout_to_key); -BLOB_SERIALIZER(cryptonote::txout_to_scripthash); - -VARIANT_TAG(binary_archive, cryptonote::txin_gen, 0xff); -VARIANT_TAG(binary_archive, cryptonote::txin_to_script, 0x0); -VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1); -VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2); -VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0); -VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1); -VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2); -VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); -VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); - -VARIANT_TAG(json_archive, cryptonote::txin_gen, "gen"); -VARIANT_TAG(json_archive, cryptonote::txin_to_script, "script"); -VARIANT_TAG(json_archive, cryptonote::txin_to_scripthash, "scripthash"); -VARIANT_TAG(json_archive, cryptonote::txin_to_key, "key"); -VARIANT_TAG(json_archive, cryptonote::txout_to_script, "script"); -VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash"); -VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key"); -VARIANT_TAG(json_archive, cryptonote::transaction, "tx"); -VARIANT_TAG(json_archive, cryptonote::block, "block"); - -VARIANT_TAG(debug_archive, cryptonote::txin_gen, "gen"); -VARIANT_TAG(debug_archive, cryptonote::txin_to_script, "script"); -VARIANT_TAG(debug_archive, cryptonote::txin_to_scripthash, "scripthash"); -VARIANT_TAG(debug_archive, cryptonote::txin_to_key, "key"); -VARIANT_TAG(debug_archive, cryptonote::txout_to_script, "script"); -VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash"); -VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key"); -VARIANT_TAG(debug_archive, cryptonote::transaction, "tx"); -VARIANT_TAG(debug_archive, cryptonote::block, "block"); +BLOB_SERIALIZER(cryptonote::TransactionOutputToKey); + +VARIANT_TAG(binary_archive, cryptonote::TransactionInputGenerate, 0xff); +VARIANT_TAG(binary_archive, cryptonote::TransactionInputToScript, 0x0); +VARIANT_TAG(binary_archive, cryptonote::TransactionInputToScriptHash, 0x1); +VARIANT_TAG(binary_archive, cryptonote::TransactionInputToKey, 0x2); +VARIANT_TAG(binary_archive, cryptonote::TransactionInputMultisignature, 0x3); +VARIANT_TAG(binary_archive, cryptonote::TransactionOutputToScript, 0x0); +VARIANT_TAG(binary_archive, cryptonote::TransactionOutputToScriptHash, 0x1); +VARIANT_TAG(binary_archive, cryptonote::TransactionOutputToKey, 0x2); +VARIANT_TAG(binary_archive, cryptonote::TransactionOutputMultisignature, 0x3); +VARIANT_TAG(binary_archive, cryptonote::Transaction, 0xcc); +VARIANT_TAG(binary_archive, cryptonote::Block, 0xbb); + +VARIANT_TAG(json_archive, cryptonote::TransactionInputGenerate, "generate"); +VARIANT_TAG(json_archive, cryptonote::TransactionInputToScript, "script"); +VARIANT_TAG(json_archive, cryptonote::TransactionInputToScriptHash, "scripthash"); +VARIANT_TAG(json_archive, cryptonote::TransactionInputToKey, "key"); +VARIANT_TAG(json_archive, cryptonote::TransactionInputMultisignature, "multisignature"); +VARIANT_TAG(json_archive, cryptonote::TransactionOutputToScript, "script"); +VARIANT_TAG(json_archive, cryptonote::TransactionOutputToScriptHash, "scripthash"); +VARIANT_TAG(json_archive, cryptonote::TransactionOutputToKey, "key"); +VARIANT_TAG(json_archive, cryptonote::TransactionOutputMultisignature, "multisignature"); +VARIANT_TAG(json_archive, cryptonote::Transaction, "Transaction"); +VARIANT_TAG(json_archive, cryptonote::Block, "Block"); + +VARIANT_TAG(debug_archive, cryptonote::TransactionInputGenerate, "generate"); +VARIANT_TAG(debug_archive, cryptonote::TransactionInputToScript, "script"); +VARIANT_TAG(debug_archive, cryptonote::TransactionInputToScriptHash, "scripthash"); +VARIANT_TAG(debug_archive, cryptonote::TransactionInputToKey, "key"); +VARIANT_TAG(debug_archive, cryptonote::TransactionInputMultisignature, "multisignature"); +VARIANT_TAG(debug_archive, cryptonote::TransactionOutputToScript, "script"); +VARIANT_TAG(debug_archive, cryptonote::TransactionOutputToScriptHash, "scripthash"); +VARIANT_TAG(debug_archive, cryptonote::TransactionOutputToKey, "key"); +VARIANT_TAG(debug_archive, cryptonote::TransactionOutputMultisignature, "multisignature"); +VARIANT_TAG(debug_archive, cryptonote::Transaction, "Transaction"); +VARIANT_TAG(debug_archive, cryptonote::Block, "Block"); diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index 5053ea30e8..b2952a560c 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -35,158 +35,78 @@ namespace cryptonote { /* Cryptonote helper functions */ /************************************************************************/ //----------------------------------------------------------------------------------------------- - size_t get_max_block_size() - { - return CRYPTONOTE_MAX_BLOCK_SIZE; - } - //----------------------------------------------------------------------------------------------- - size_t get_max_tx_size() - { - 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) { - uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR; - - //make it soft - if (median_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) { - median_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + uint64_t getPenalizedAmount(uint64_t amount, size_t medianSize, size_t currentBlockSize) { + static_assert(sizeof(size_t) >= sizeof(uint32_t), "size_t is too small"); + assert(currentBlockSize <= 2 * medianSize); + assert(medianSize <= std::numeric_limits::max()); + assert(currentBlockSize <= std::numeric_limits::max()); + + if (amount == 0) { + return 0; } - if (current_block_size <= median_size) { - reward = base_reward; - return true; + if (currentBlockSize <= medianSize) { + return amount; } - if(current_block_size > 2 * median_size) { - LOG_PRINT_L4("Block cumulative size is too big: " << current_block_size << ", expected less than " << 2 * median_size); - return false; - } - - assert(median_size < std::numeric_limits::max()); - assert(current_block_size < std::numeric_limits::max()); + uint64_t productHi; + uint64_t productLo = mul128(amount, currentBlockSize * (UINT64_C(2) * medianSize - currentBlockSize), &productHi); - uint64_t product_hi; - uint64_t product_lo = mul128(base_reward, current_block_size * (2 * median_size - current_block_size), &product_hi); + uint64_t penalizedAmountHi; + uint64_t penalizedAmountLo; + div128_32(productHi, productLo, static_cast(medianSize), &penalizedAmountHi, &penalizedAmountLo); + div128_32(penalizedAmountHi, penalizedAmountLo, static_cast(medianSize), &penalizedAmountHi, &penalizedAmountLo); - 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); - assert(0 == reward_hi); - assert(reward_lo < base_reward); + assert(0 == penalizedAmountHi); + assert(penalizedAmountLo < amount); - reward = reward_lo; - return true; - } - //------------------------------------------------------------------------------------ - uint8_t get_account_address_checksum(const public_address_outer_blob& bl) - { - const unsigned char* pbuf = reinterpret_cast(&bl); - uint8_t summ = 0; - for(size_t i = 0; i!= sizeof(public_address_outer_blob)-1; i++) - summ += pbuf[i]; - - return summ; + return penalizedAmountLo; } //----------------------------------------------------------------------- - std::string get_account_address_as_str(const account_public_address& adr) - { - return tools::base58::encode_addr(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(adr)); + std::string getAccountAddressAsStr(uint64_t prefix, const AccountPublicAddress& adr) { + blobdata blob; + bool r = t_serializable_object_to_blob(adr, blob); + assert(r); + return tools::base58::encode_addr(prefix, blob); } //----------------------------------------------------------------------- - bool is_coinbase(const transaction& tx) - { - if(tx.vin.size() != 1) + bool is_coinbase(const Transaction& tx) { + if(tx.vin.size() != 1) { return false; + } - if(tx.vin[0].type() != typeid(txin_gen)) + if(tx.vin[0].type() != typeid(TransactionInputGenerate)) { return false; + } return true; } //----------------------------------------------------------------------- - bool get_account_address_from_str(uint64_t& prefix, account_public_address& adr, const std::string& str) - { - if (2 * sizeof(public_address_outer_blob) != str.size()) - { - blobdata data; - if (!tools::base58::decode_addr(str, prefix, data)) - { - LOG_PRINT_L1("Invalid address format"); - return false; - } - - if (!::serialization::parse_binary(data, adr)) - { - LOG_PRINT_L1("Account public address keys can't be parsed"); - return false; - } - - if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) - { - LOG_PRINT_L1("Failed to validate address keys"); - return false; - } - } - else - { - // Old address format - prefix = CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX; - - std::string buff; - if(!string_tools::parse_hexstr_to_binbuff(str, buff)) - return false; - - if(buff.size()!=sizeof(public_address_outer_blob)) - { - LOG_PRINT_L1("Wrong public address size: " << buff.size() << ", expected size: " << sizeof(public_address_outer_blob)); - return false; - } - - public_address_outer_blob blob = *reinterpret_cast(buff.data()); - - - if(blob.m_ver > CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER) - { - LOG_PRINT_L1("Unknown version of public address: " << blob.m_ver << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER); - return false; - } - - if(blob.check_sum != get_account_address_checksum(blob)) - { - LOG_PRINT_L1("Wrong public address checksum"); - return false; - } - - //we success - adr = blob.m_address; + bool parseAccountAddressString(uint64_t& prefix, AccountPublicAddress& adr, const std::string& str) { + blobdata data; + if (!tools::base58::decode_addr(str, prefix, data)) { + LOG_PRINT_L1("Invalid address format"); + return false; } - return true; - } - //----------------------------------------------------------------------- - bool get_account_address_from_str(account_public_address& adr, const std::string& str) - { - uint64_t prefix; - if(!get_account_address_from_str(prefix, adr, str)) + if (!::serialization::parse_binary(data, adr)) { + LOG_PRINT_L1("Account public address keys can't be parsed"); return false; + } - if(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) - { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + if (!crypto::check_key(adr.m_spendPublicKey) || !crypto::check_key(adr.m_viewPublicKey)) { + LOG_PRINT_L1("Failed to validate address keys"); return false; } return true; } //----------------------------------------------------------------------- - - bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { + bool operator ==(const cryptonote::Transaction& a, const cryptonote::Transaction& b) { return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); } - - bool operator ==(const cryptonote::block& a, const cryptonote::block& b) { + //----------------------------------------------------------------------- + bool operator ==(const cryptonote::Block& a, const cryptonote::Block& b) { return cryptonote::get_block_hash(a) == cryptonote::get_block_hash(b); } } diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index ae3dd747cf..793df885b4 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -17,9 +17,12 @@ #pragma once -#include "cryptonote_basic.h" +//epee +#include "string_tools.h" + #include "crypto/crypto.h" #include "crypto/hash.h" +#include "cryptonote_core/cryptonote_basic.h" namespace cryptonote { @@ -35,31 +38,16 @@ namespace cryptonote { } }; - -#pragma pack(push, 1) - struct public_address_outer_blob - { - uint8_t m_ver; - account_public_address m_address; - uint8_t check_sum; - }; -#pragma pack (pop) - - /************************************************************************/ /* Cryptonote helper functions */ /************************************************************************/ - 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 get_account_address_checksum(const public_address_outer_blob& bl); - std::string get_account_address_as_str(const account_public_address& adr); - bool get_account_address_from_str(uint64_t& prefix, account_public_address& adr, const std::string& str); - bool get_account_address_from_str(account_public_address& adr, const std::string& str); - bool is_coinbase(const transaction& tx); + uint64_t getPenalizedAmount(uint64_t amount, size_t medianSize, size_t currentBlockSize); + std::string getAccountAddressAsStr(uint64_t prefix, const AccountPublicAddress& adr); + bool parseAccountAddressString(uint64_t& prefix, AccountPublicAddress& adr, const std::string& str); + bool is_coinbase(const Transaction& tx); - bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b); - bool operator ==(const cryptonote::block& a, const cryptonote::block& b); + bool operator ==(const cryptonote::Transaction& a, const cryptonote::Transaction& b); + bool operator ==(const cryptonote::Block& a, const cryptonote::Block& b); } template diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 53847498c7..d1a7b5d75f 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -67,59 +67,55 @@ namespace boost a & reinterpret_cast(x); } - template - inline void serialize(Archive &a, cryptonote::txout_to_script &x, const boost::serialization::version_type ver) - { - a & x.keys; - a & x.script; + template void serialize(Archive& archive, cryptonote::TransactionInputToScript&, unsigned int version) { + assert(false); } + template void serialize(Archive& archive, cryptonote::TransactionInputToScriptHash&, unsigned int version) { + assert(false); + } - template - inline void serialize(Archive &a, cryptonote::txout_to_key &x, const boost::serialization::version_type ver) - { - a & x.key; + template void serialize(Archive& archive, cryptonote::TransactionOutputToScript&, unsigned int version) { + assert(false); } - template - inline void serialize(Archive &a, cryptonote::txout_to_scripthash &x, const boost::serialization::version_type ver) - { - a & x.hash; + template void serialize(Archive& archive, cryptonote::TransactionOutputToScriptHash&, unsigned int version) { + assert(false); } - template - inline void serialize(Archive &a, cryptonote::txin_gen &x, const boost::serialization::version_type ver) - { - a & x.height; + template void serialize(Archive& archive, cryptonote::TransactionInputMultisignature &output, unsigned int version) { + archive & output.amount; + archive & output.signatures; + archive & output.outputIndex; + } + + template void serialize(Archive& archive, cryptonote::TransactionOutputMultisignature &output, unsigned int version) { + archive & output.keys; + archive & output.requiredSignatures; } template - inline void serialize(Archive &a, cryptonote::txin_to_script &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::TransactionOutputToKey &x, const boost::serialization::version_type ver) { - a & x.prev; - a & x.prevout; - a & x.sigset; + a & x.key; } template - inline void serialize(Archive &a, cryptonote::txin_to_scripthash &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::TransactionInputGenerate &x, const boost::serialization::version_type ver) { - a & x.prev; - a & x.prevout; - a & x.script; - a & x.sigset; + a & x.height; } template - inline void serialize(Archive &a, cryptonote::txin_to_key &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::TransactionInputToKey &x, const boost::serialization::version_type ver) { a & x.amount; - a & x.key_offsets; - a & x.k_image; + a & x.keyOffsets; + a & x.keyImage; } template - inline void serialize(Archive &a, cryptonote::tx_out &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::TransactionOutput &x, const boost::serialization::version_type ver) { a & x.amount; a & x.target; @@ -127,10 +123,10 @@ namespace boost template - inline void serialize(Archive &a, cryptonote::transaction &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::Transaction &x, const boost::serialization::version_type ver) { a & x.version; - a & x.unlock_time; + a & x.unlockTime; a & x.vin; a & x.vout; a & x.extra; @@ -139,16 +135,16 @@ namespace boost template - inline void serialize(Archive &a, cryptonote::block &b, const boost::serialization::version_type ver) + inline void serialize(Archive &a, cryptonote::Block &b, const boost::serialization::version_type ver) { - a & b.major_version; - a & b.minor_version; + a & b.majorVersion; + a & b.minorVersion; a & b.timestamp; - a & b.prev_id; + a & b.prevId; a & b.nonce; //------------------ - a & b.miner_tx; - a & b.tx_hashes; + a & b.minerTx; + a & b.txHashes; } } } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 004df14a85..ca4f1f9670 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -15,19 +15,29 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "include_base_utils.h" -using namespace epee; +#include "cryptonote_core.h" -#include +#include #include -#include "cryptonote_core.h" + +#include "storages/portable_storage_template_helper.h" +#include "include_base_utils.h" +#include "misc_log_ex.h" +#include "misc_language.h" +#include "warnings.h" + #include "common/command_line.h" #include "common/util.h" -#include "warnings.h" #include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_stat_info.h" +#include "cryptonote_core/miner.h" #include "cryptonote_config.h" -#include "cryptonote_format_utils.h" -#include "misc_language.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/core_rpc_server_commands_defs.h" + + +using namespace epee; DISABLE_VS_WARNINGS(4355) @@ -35,15 +45,19 @@ namespace cryptonote { //----------------------------------------------------------------------------------------------- - core::core(i_cryptonote_protocol* pprotocol): - m_mempool(m_blockchain_storage), - m_blockchain_storage(m_mempool), - m_miner(this), - m_miner_address(boost::value_initialized()), + core::core(const Currency& currency, i_cryptonote_protocol* pprotocol): + m_currency(currency), + m_mempool(currency, m_blockchain_storage, m_timeProvider), + m_blockchain_storage(currency, m_mempool), + m_miner(new miner(currency, this)), m_starter_message_showed(false) { set_cryptonote_protocol(pprotocol); } + //----------------------------------------------------------------------------------------------- + core::~core() { + } + //----------------------------------------------------------------------------------------------- void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { if(pprotocol) @@ -78,21 +92,21 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) + bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { 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) + bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } //----------------------------------------------------------------------------------------------- - void core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) + void core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) { m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); } //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list& blocks) + bool core::get_alternative_blocks(std::list& blocks) { return m_blockchain_storage.get_alternative_blocks(blocks); } @@ -112,13 +126,13 @@ namespace cryptonote r = m_blockchain_storage.init(m_config_folder, load_existing); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - r = m_miner.init(vm); + r = m_miner->init(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); return load_state_data(); } //----------------------------------------------------------------------------------------------- - bool core::set_genesis_block(const block& b) + bool core::set_genesis_block(const Block& b) { return m_blockchain_storage.reset_and_set_genesis_block(b); } @@ -131,7 +145,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::deinit() { - m_miner.stop(); + m_miner->stop(); m_mempool.deinit(); m_blockchain_storage.deinit(); return true; @@ -143,7 +157,7 @@ namespace cryptonote //want to process all transactions sequentially CRITICAL_REGION_LOCAL(m_incoming_tx_lock); - if(tx_blob.size() > get_max_tx_size()) + if(tx_blob.size() > m_currency.maxTxSize()) { LOG_PRINT_L0("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"); tvc.m_verifivation_failed = true; @@ -152,7 +166,7 @@ namespace cryptonote crypto::hash tx_hash = null_hash; crypto::hash tx_prefixt_hash = null_hash; - transaction tx; + Transaction tx; if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { @@ -177,19 +191,26 @@ namespace cryptonote } bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); - if(tvc.m_verifivation_failed) - {LOG_PRINT_RED_L0("Transaction verification failed: " << tx_hash);} - else if(tvc.m_verifivation_impossible) - {LOG_PRINT_RED_L0("Transaction verification impossible: " << tx_hash);} + if(tvc.m_verifivation_failed) { + if (!tvc.m_tx_fee_too_small) { + LOG_PRINT_RED_L0("Transaction verification failed: " << tx_hash); + } else { + LOG_PRINT_L0("Transaction verification failed: " << tx_hash); + } + } else if(tvc.m_verifivation_impossible) { + LOG_PRINT_RED_L0("Transaction verification impossible: " << tx_hash); + } - if(tvc.m_added_to_pool) + if (tvc.m_added_to_pool) { LOG_PRINT_L1("tx added: " << tx_hash); + } + return r; } //----------------------------------------------------------------------------------------------- bool core::get_stat_info(core_stat_info& st_inf) { - st_inf.mining_speed = m_miner.get_speed(); + st_inf.mining_speed = m_miner->get_speed(); st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); st_inf.tx_pool_size = m_mempool.get_transactions_count(); @@ -198,7 +219,7 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------- - bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) + bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { if(!tx.vin.size()) { @@ -234,36 +255,41 @@ namespace cryptonote return false; } - if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) + if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { - LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << + (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize())); return false; } //check if tx use different key images if(!check_tx_inputs_keyimages_diff(tx)) { - LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + LOG_PRINT_RED_L0("tx has a few inputs with identical keyimages"); return false; } + if (!checkMultisignatureInputsDiff(tx)) { + LOG_PRINT_RED_L0("tx has a few multisignature inputs with identical output indexes"); + return false; + } return true; } //----------------------------------------------------------------------------------------------- - bool core::check_tx_inputs_keyimages_diff(const transaction& tx) + bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { std::unordered_set ki; - BOOST_FOREACH(const auto& in, tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); - if(!ki.insert(tokey_in.k_image).second) - return false; + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } } return true; } //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block) + bool core::add_new_tx(const Transaction& tx, tx_verification_context& tvc, bool keeped_by_block) { crypto::hash tx_hash = get_transaction_hash(tx); crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); @@ -282,7 +308,7 @@ namespace cryptonote // return m_blockchain_storage.get_outs(amount, pkeys); //} //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(const 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 core::add_new_tx(const 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) { if (m_blockchain_storage.have_tx(tx_hash)) { LOG_PRINT_L2("tx " << tx_hash << " is already in blockchain"); return true; @@ -299,7 +325,7 @@ namespace cryptonote return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); } //----------------------------------------------------------------------------------------------- - bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) + bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) { return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); } @@ -309,7 +335,7 @@ namespace cryptonote return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); } //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) + bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); } @@ -339,108 +365,84 @@ namespace cryptonote return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); } //----------------------------------------------------------------------------------------------- - void core::pause_mine() - { - m_miner.pause(); + void core::pause_mining() { + m_miner->pause(); } //----------------------------------------------------------------------------------------------- - void core::resume_mine() - { - m_miner.resume(); + void core::update_block_template_and_resume_mining() { + update_miner_block_template(); + m_miner->resume(); } //----------------------------------------------------------------------------------------------- - bool core::handle_block_found(block& b) - { + bool core::handle_block_found(Block& b) { block_verification_context bvc = boost::value_initialized(); - m_miner.pause(); - m_blockchain_storage.add_new_block(b, bvc); - //anyway - update miner template - update_miner_block_template(); - m_miner.resume(); - - - CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "mined block failed verification"); - if(bvc.m_added_to_main_chain) - { - cryptonote_connection_context exclude_context = boost::value_initialized(); - NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(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)) - { - LOG_PRINT_L0("Block found but, seems that reorganize just happened after that, do not relay this block"); - return true; - } - CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() - << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); + handle_incoming_block(b, bvc, true, true); - block_to_blob(b, arg.b.block); - //pack transactions - BOOST_FOREACH(auto& tx, txs) - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); - - m_pprotocol->relay_block(arg, exclude_context); + if (bvc.m_verifivation_failed) { + LOG_ERROR("mined block failed verification"); } + return bvc.m_added_to_main_chain; } //----------------------------------------------------------------------------------------------- void core::on_synchronized() { - m_miner.on_synchronized(); + m_miner->on_synchronized(); } - //bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count) - //{ - // return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); - //} //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) - { - bvc = boost::value_initialized(); - if(block_blob.size() > get_max_block_size()) - { + bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (block_blob.size() > m_currency.maxBlockBlobSize()) { LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); bvc.m_verifivation_failed = true; return false; } - block b = AUTO_VAL_INIT(b); - if(!parse_and_validate_block_from_blob(block_blob, b)) - { + Block b = AUTO_VAL_INIT(b); + if (!parse_and_validate_block_from_blob(block_blob, b)) { LOG_PRINT_L0("Failed to parse and validate new block"); bvc.m_verifivation_failed = true; return false; } - m_blockchain_storage.add_new_block(b, bvc); - if (bvc.m_added_to_main_chain && update_miner_blocktemplate) { - update_miner_block_template(); + return handle_incoming_block(b, bvc, control_miner, relay_block); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (control_miner) { + pause_mining(); } - return true; - } + m_blockchain_storage.add_new_block(b, bvc); + + if (control_miner) { + update_block_template_and_resume_mining(); + } - void core::notify_new_block(const block& b) { - cryptonote_connection_context exclude_context = boost::value_initialized(); - NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); - if (missed_txs.size() > 0 && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { - LOG_PRINT_L0("Block found but reorganize happened after that, block will not be relayed"); - } else { - if (txs.size() != b.tx_hashes.size() || missed_txs.size()) { - LOG_ERROR("cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); - return; + if (relay_block && bvc.m_added_to_main_chain) { + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(b.txHashes, txs, missed_txs); + if (!missed_txs.empty() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { + LOG_PRINT_L0("Block added, but it seems that reorganize just happened after that, do not relay this block"); + } else { + CHECK_AND_ASSERT_MES(txs.size() == b.txHashes.size() && missed_txs.empty(), false, "can't find some transactions in found block:" << + get_block_hash(b) << " txs.size()=" << txs.size() << ", b.txHashes.size()=" << b.txHashes.size() << ", missed_txs.size()" << missed_txs.size()); + + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + bool r = block_to_blob(b, arg.b.block); + CHECK_AND_ASSERT_MES(r, false, "failed to serialize block"); + for (auto& tx : txs) { + arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + } + + cryptonote_connection_context exclude_context = boost::value_initialized(); + m_pprotocol->relay_block(arg, exclude_context); } - block_to_blob(b, arg.b.block); - BOOST_FOREACH(auto& tx, txs) arg.b.txs.push_back(t_serializable_object_to_blob(tx)); - m_pprotocol->relay_block(arg, exclude_context); } + + return true; } //----------------------------------------------------------------------------------------------- crypto::hash core::get_tail_id() @@ -458,17 +460,17 @@ namespace cryptonote return m_blockchain_storage.have_block(id); } //----------------------------------------------------------------------------------------------- - bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) + bool core::parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) { return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); } //----------------------------------------------------------------------------------------------- - bool core::check_tx_syntax(const transaction& tx) + bool core::check_tx_syntax(const Transaction& tx) { return true; } //----------------------------------------------------------------------------------------------- - void core::get_pool_transactions(std::list& txs) + void core::get_pool_transactions(std::list& txs) { m_mempool.get_transactions(txs); } @@ -488,7 +490,7 @@ namespace cryptonote return m_blockchain_storage.get_block_id_by_height(height); } //----------------------------------------------------------------------------------------------- - bool core::get_block_by_hash(const crypto::hash &h, block &blk) { + bool core::get_block_by_hash(const crypto::hash &h, Block &blk) { return m_blockchain_storage.get_block_by_hash(h, blk); } //----------------------------------------------------------------------------------------------- @@ -503,7 +505,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::update_miner_block_template() { - m_miner.on_block_chain_update(); + m_miner->on_block_chain_update(); return true; } //----------------------------------------------------------------------------------------------- @@ -523,8 +525,8 @@ namespace cryptonote m_starter_message_showed = true; } - m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage)); - m_miner.on_idle(); + m_miner->on_idle(); + m_mempool.on_idle(); return true; } //----------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 5c79f6da3a..2596908699 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -22,78 +22,77 @@ #include "p2p/net_node_common.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" -#include "storages/portable_storage_template_helper.h" +#include "Currency.h" #include "tx_pool.h" #include "blockchain_storage.h" -#include "miner.h" +#include "cryptonote_core/i_miner_handler.h" #include "connection_context.h" -#include "cryptonote_core/cryptonote_stat_info.h" #include "warnings.h" #include "crypto/hash.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) -namespace cryptonote -{ - /************************************************************************/ - /* */ - /************************************************************************/ - class core: public i_miner_handler - { +namespace cryptonote { + struct core_stat_info; + class miner; + + class core: public i_miner_handler { public: - core(i_cryptonote_protocol* pprotocol); - bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context); + core(const Currency& currency, i_cryptonote_protocol* pprotocol); + ~core(); + bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp, cryptonote_connection_context& context); bool on_idle(); bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); - bool handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate = true); + bool handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); + const Currency& currency() const { return m_currency; } i_cryptonote_protocol* get_protocol(){return m_pprotocol;} //-------------------- i_miner_handler ----------------------- - virtual bool handle_block_found( block& b); - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + virtual bool handle_block_found(Block& b); + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); - miner& get_miner(){return m_miner;} + miner& get_miner() { return *m_miner; } static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm, bool load_existing); - bool set_genesis_block(const block& b); + bool set_genesis_block(const Block& b); bool deinit(); uint64_t get_current_blockchain_height(); bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); template bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); } crypto::hash get_block_id_by_height(uint64_t height); - void get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); - bool get_block_by_hash(const crypto::hash &h, block &blk); + void get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); + bool get_block_by_hash(const crypto::hash &h, Block &blk); //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - bool get_alternative_blocks(std::list& blocks); + bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_checkpoints(checkpoints&& chk_pts); - void get_pool_transactions(std::list& txs); + void get_pool_transactions(std::list& txs); size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); //bool get_outs(uint64_t amount, std::list& pkeys); bool have_block(const crypto::hash& id); bool get_short_chain_history(std::list& ids); - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); + bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool get_stat_info(core_stat_info& st_inf); //bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); crypto::hash get_tail_id(); - 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); - void pause_mine(); - void resume_mine(); + 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); + void pause_mining(); + void update_block_template_and_resume_mining(); blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} //debug functions void print_blockchain(uint64_t start_index, uint64_t end_index); @@ -101,39 +100,37 @@ namespace cryptonote std::string print_pool(bool short_format); void print_blockchain_outs(const std::string& file); void on_synchronized(); - void notify_new_block(const block& b); private: - bool add_new_tx(const 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 add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); + bool add_new_tx(const 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 add_new_tx(const Transaction& tx, tx_verification_context& tvc, bool keeped_by_block); bool load_state_data(); - bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); + bool parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); + bool handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block); - bool check_tx_syntax(const transaction& tx); + bool check_tx_syntax(const Transaction& tx); //check correct values, amounts and all lightweight checks not related with database - bool check_tx_semantic(const transaction& tx, bool keeped_by_block); + bool check_tx_semantic(const Transaction& tx, bool keeped_by_block); //check if tx already in memory pool or in main blockchain bool is_key_image_spent(const crypto::key_image& key_im); - bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); + bool check_tx_ring_signature(const TransactionInputToKey& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); bool is_tx_spendtime_unlocked(uint64_t unlock_time); bool update_miner_block_template(); bool handle_command_line(const boost::program_options::variables_map& vm); bool on_update_blocktemplate_interval(); - bool check_tx_inputs_keyimages_diff(const transaction& tx); - + bool check_tx_inputs_keyimages_diff(const Transaction& tx); + const Currency& m_currency; + CryptoNote::RealTimeProvider m_timeProvider; tx_memory_pool m_mempool; blockchain_storage m_blockchain_storage; i_cryptonote_protocol* m_pprotocol; epee::critical_section m_incoming_tx_lock; - //m_miner and m_miner_addres are probably temporary here - miner m_miner; - account_public_address m_miner_address; + std::unique_ptr m_miner; std::string m_config_folder; cryptonote_protocol_stub m_protocol_stub; - epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; friend class tx_validate_inputs; std::atomic m_starter_message_showed; }; diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 9de9e1412e..16bcdb7ef9 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -15,35 +15,40 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include + +// epee #include "include_base_utils.h" -using namespace epee; +#include "misc_language.h" -#include "cryptonote_format_utils.h" -#include -#include "cryptonote_config.h" -#include "miner.h" #include "crypto/crypto.h" #include "crypto/hash.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "serialization/binary_utils.h" +#include "cryptonote_config.h" + +using namespace epee; namespace cryptonote { //--------------------------------------------------------------- - void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) + void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h) { std::ostringstream s; binary_archive a(s); - ::serialization::serialize(a, const_cast(tx)); + ::serialization::serialize(a, const_cast(tx)); crypto::cn_fast_hash(s.str().data(), s.str().size(), h); } //--------------------------------------------------------------- - crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx) + crypto::hash get_transaction_prefix_hash(const TransactionPrefix& tx) { crypto::hash h = null_hash; get_transaction_prefix_hash(tx, h); return h; } //--------------------------------------------------------------- - bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx) + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx) { std::stringstream ss; ss << tx_blob; @@ -53,7 +58,7 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - 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, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) { std::stringstream ss; ss << tx_blob; @@ -67,83 +72,14 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - 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) { - tx.vin.clear(); - tx.vout.clear(); - tx.extra.clear(); - - keypair txkey = keypair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - if(!extra_nonce.empty()) - if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce)) - return false; - - txin_gen in; - in.height = height; - - uint64_t block_reward; - if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward)) - { - LOG_PRINT_L0("Block is too big"); - return false; - } -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: reward " << block_reward << - ", fee " << fee) -#endif - block_reward += fee; - - std::vector out_amounts; - decompose_amount_into_digits(block_reward, DEFAULT_DUST_THRESHOLD, - [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, - [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); - - CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); - while (max_outs < out_amounts.size()) - { - out_amounts[out_amounts.size() - 2] += out_amounts.back(); - out_amounts.resize(out_amounts.size() - 1); - } - - uint64_t summary_amounts = 0; - for (size_t no = 0; no < out_amounts.size(); no++) - { - crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; - crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); - bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); - - r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); - - txout_to_key tk; - tk.key = out_eph_public_key; - - tx_out out; - summary_amounts += out.amount = out_amounts[no]; - out.target = tk; - tx.vout.push_back(out); - } - - CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); - - tx.version = CURRENT_TRANSACTION_VERSION; - //lock - tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; - tx.vin.push_back(in); - //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) - // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); - return true; - } - //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki) { crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); bool r = crypto::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 << ")"); - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spendPublicKey, in_ephemeral.pub); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spendPublicKey << ")"); crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); @@ -161,59 +97,29 @@ namespace cryptonote return total; } //--------------------------------------------------------------- - bool parse_amount(uint64_t& amount, const std::string& str_amount_) + bool get_tx_fee(const Transaction& tx, uint64_t & fee) { - std::string str_amount = str_amount_; - boost::algorithm::trim(str_amount); + uint64_t amount_in = 0; + uint64_t amount_out = 0; - size_t point_index = str_amount.find_first_of('.'); - size_t fraction_size; - if (std::string::npos != point_index) - { - fraction_size = str_amount.size() - point_index - 1; - while (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back()) - { - str_amount.erase(str_amount.size() - 1, 1); - --fraction_size; + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + amount_in += boost::get(in).amount; + } else if (in.type() == typeid(TransactionInputMultisignature)) { + amount_in += boost::get(in).amount; } - if (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size) - return false; - str_amount.erase(point_index, 1); - } - else - { - fraction_size = 0; } - if (str_amount.empty()) - return false; - - if (fraction_size < CRYPTONOTE_DISPLAY_DECIMAL_POINT) - { - str_amount.append(CRYPTONOTE_DISPLAY_DECIMAL_POINT - fraction_size, '0'); - } - - return string_tools::get_xtype_from_string(amount, str_amount); - } - //--------------------------------------------------------------- - bool get_tx_fee(const transaction& tx, uint64_t & fee) - { - uint64_t amount_in = 0; - uint64_t amount_out = 0; - BOOST_FOREACH(auto& in, tx.vin) - { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); - amount_in += boost::get(in).amount; - } - BOOST_FOREACH(auto& o, tx.vout) + for (const auto& o : tx.vout) { amount_out += o.amount; + } CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" < ar(iss); bool eof = false; - while (!eof) - { + while (!eof) { tx_extra_field field; bool r = ::do_serialize(ar, field); - CHECK_AND_NO_ASSERT_MES(r, false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + if (!r) { + LOG_PRINT_L4("failed to deserialize extra field. extra = " << + string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + return false; + } tx_extra_fields.push_back(field); std::ios_base::iostate state = iss.rdstate(); eof = (EOF == iss.peek()); iss.clear(state); } - CHECK_AND_NO_ASSERT_MES(::serialization::check_stream_state(ar), false, "failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + + if (!::serialization::check_stream_state(ar)) { + LOG_PRINT_L4("failed to deserialize extra field. extra = " << + string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); + return false; + } return true; } @@ -261,12 +175,12 @@ namespace cryptonote return pub_key_field.pub_key; } //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const transaction& tx) + crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx) { return get_tx_pub_key_from_extra(tx.extra); } //--------------------------------------------------------------- - bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) + bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key) { tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; @@ -290,6 +204,24 @@ namespace cryptonote return true; } //--------------------------------------------------------------- + bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) { + blobdata blob; + if (!t_serializable_object_to_blob(mm_tag, blob)) { + return false; + } + + tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); + std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); + return true; + } + //--------------------------------------------------------------- + bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) { + std::vector tx_extra_fields; + parse_tx_extra(tx_extra, tx_extra_fields); + + return find_tx_extra_field_by_type(tx_extra_fields, mm_tag); + } + //--------------------------------------------------------------- void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) { extra_nonce.clear(); @@ -308,29 +240,29 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time) + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, Transaction& tx, uint64_t unlock_time) { tx.vin.clear(); tx.vout.clear(); tx.signatures.clear(); tx.version = CURRENT_TRANSACTION_VERSION; - tx.unlock_time = unlock_time; + tx.unlockTime = unlock_time; tx.extra = extra; - keypair txkey = keypair::generate(); + KeyPair txkey = KeyPair::generate(); add_tx_pub_key_to_extra(tx, txkey.pub); struct input_generation_context_data { - keypair in_ephemeral; + KeyPair in_ephemeral; }; std::vector in_contexts; uint64_t summary_inputs_money = 0; //fill inputs - BOOST_FOREACH(const tx_source_entry& src_entr, sources) + for (const tx_source_entry& src_entr : sources) { if(src_entr.real_output >= src_entr.outputs.size()) { @@ -341,7 +273,7 @@ namespace cryptonote //key_derivation recv_derivation; in_contexts.push_back(input_generation_context_data()); - keypair& in_ephemeral = in_contexts.back().in_ephemeral; + KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; crypto::key_image img; if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) return false; @@ -356,15 +288,16 @@ namespace cryptonote } //put key image into tx input - txin_to_key input_to_key; + TransactionInputToKey input_to_key; input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; + input_to_key.keyImage = img; //fill outputs array and use relative offsets - BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs) - input_to_key.key_offsets.push_back(out_entry.first); + for (const tx_source_entry::output_entry& out_entry : src_entr.outputs) { + input_to_key.keyOffsets.push_back(out_entry.first); + } - input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); tx.vin.push_back(input_to_key); } @@ -375,20 +308,19 @@ namespace cryptonote uint64_t summary_outs_money = 0; //fill outputs size_t output_index = 0; - BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts) - { + for (const tx_destination_entry& dst_entr : shuffled_dsts) { CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); crypto::key_derivation derivation; crypto::public_key out_eph_public_key; - bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")"); + bool r = crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_viewPublicKey << ", " << txkey.sec << ")"); - r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spendPublicKey, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spendPublicKey << ")"); - tx_out out; + TransactionOutput out; out.amount = dst_entr.amount; - txout_to_key tk; + TransactionOutputToKey tk; tk.key = out_eph_public_key; out.target = tk; tx.vout.push_back(out); @@ -410,12 +342,10 @@ namespace cryptonote std::stringstream ss_ring_s; size_t i = 0; - BOOST_FOREACH(const tx_source_entry& src_entr, sources) - { + for (const tx_source_entry& src_entr : sources) { ss_ring_s << "pub_keys:" << ENDL; std::vector keys_ptrs; - BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) - { + for (const tx_source_entry::output_entry& o : src_entr.outputs) { keys_ptrs.push_back(&o.second); ss_ring_s << o.second << ENDL; } @@ -423,10 +353,12 @@ namespace cryptonote tx.signatures.push_back(std::vector()); std::vector& sigs = tx.signatures.back(); sigs.resize(src_entr.outputs.size()); - crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).keyImage, keys_ptrs, + in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); ss_ring_s << "signatures:" << ENDL; std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); - ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output; + ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << + ENDL << "real_output: " << src_entr.real_output; i++; } @@ -435,75 +367,118 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - bool get_inputs_money_amount(const transaction& tx, uint64_t& money) + bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { money = 0; - BOOST_FOREACH(const auto& in, tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); - money += tokey_in.amount; + + for (const auto& in : tx.vin) { + uint64_t amount = 0; + + if (in.type() == typeid(TransactionInputToKey)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(TransactionInputMultisignature)) { + amount = boost::get(in).amount; + } + + money += amount; } return true; } //--------------------------------------------------------------- - uint64_t get_block_height(const block& b) + uint64_t get_block_height(const Block& b) { - CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1"); - CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0); + CHECK_AND_ASSERT_MES(b.minerTx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.minerTx.vin.size() != 1"); + CHECKED_GET_SPECIFIC_VARIANT(b.minerTx.vin[0], const TransactionInputGenerate, coinbase_in, 0); return coinbase_in.height; } //--------------------------------------------------------------- - bool check_inputs_types_supported(const transaction& tx) - { - BOOST_FOREACH(const auto& in, tx.vin) - { - CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: " - << in.type().name() << ", expected " << typeid(txin_to_key).name() - << ", in transaction id=" << get_transaction_hash(tx)); - + bool check_inputs_types_supported(const Transaction& tx) { + for (const auto& in : tx.vin) { + if (in.type() != typeid(TransactionInputToKey) && in.type() != typeid(TransactionInputMultisignature)) { + LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains inputs with invalid type."); + return false; + } } + return true; } //----------------------------------------------------------------------------------------------- - bool check_outs_valid(const transaction& tx) - { - BOOST_FOREACH(const tx_out& out, tx.vout) - { - CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: " - << out.target.type().name() << ", expected " << typeid(txout_to_key).name() - << ", in transaction id=" << get_transaction_hash(tx)); + bool check_outs_valid(const Transaction& tx) { + for (const TransactionOutput& out : tx.vout) { + //assert(out.target.type() == typeid(TransactionOutputToKey) || out.target.type() == typeid(TransactionOutputMultisignature)); + if (out.target.type() == typeid(TransactionOutputToKey)) { + CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx)); + + if (!check_key(boost::get(out.target).key)) { + return false; + } + } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { + const TransactionOutputMultisignature& multisignatureOutput = ::boost::get(out.target); + if (multisignatureOutput.requiredSignatures > multisignatureOutput.keys.size()) { + LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains multisignature output with invalid required signature count."); + return false; + } + + for (const crypto::public_key& key : multisignatureOutput.keys) { + if (!check_key(key)) { + LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains multisignature output with invalid public keys."); + return false; + } + } + } else { + LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains outputs with invalid type."); + return false; + } + } - CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx)); + return true; + } - if(!check_key(boost::get(out.target).key)) - return false; + //----------------------------------------------------------------------------------------------- + bool checkMultisignatureInputsDiff(const Transaction& tx) { + std::set> inputsUsage; + for (const auto& inv : tx.vin) { + if (inv.type() == typeid(TransactionInputMultisignature)) { + const TransactionInputMultisignature& in = ::boost::get(inv); + if (!inputsUsage.insert(std::make_pair(in.amount, static_cast(in.outputIndex))).second) { + return false; + } + } } return true; } + //----------------------------------------------------------------------------------------------- - bool check_money_overflow(const transaction& tx) + bool check_money_overflow(const Transaction& tx) { return check_inputs_overflow(tx) && check_outs_overflow(tx); } //--------------------------------------------------------------- - bool check_inputs_overflow(const transaction& tx) + bool check_inputs_overflow(const Transaction& tx) { uint64_t money = 0; - BOOST_FOREACH(const auto& in, tx.vin) - { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); - if(money > tokey_in.amount + money) + + for (const auto& in : tx.vin) { + uint64_t amount = 0; + + if (in.type() == typeid(TransactionInputToKey)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(TransactionInputMultisignature)) { + amount = boost::get(in).amount; + } + + if (money > amount + money) return false; - money += tokey_in.amount; + + money += amount; } return true; } //--------------------------------------------------------------- - bool check_outs_overflow(const transaction& tx) + bool check_outs_overflow(const Transaction& tx) { uint64_t money = 0; - BOOST_FOREACH(const auto& o, tx.vout) - { + for (const auto& o : tx.vout) { if(money > o.amount + money) return false; money += o.amount; @@ -511,11 +486,12 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - uint64_t get_outs_money_amount(const transaction& tx) + uint64_t get_outs_money_amount(const Transaction& tx) { uint64_t outputs_amount = 0; - BOOST_FOREACH(const auto& o, tx.vout) + for (const auto& o : tx.vout) { outputs_amount += o.amount; + } return outputs_amount; } //--------------------------------------------------------------- @@ -527,17 +503,25 @@ namespace cryptonote res.insert(8, "...."); return res; } + //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) + bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex) { - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); crypto::public_key pk; - derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + derive_public_key(derivation, keyIndex, acc.m_account_address.m_spendPublicKey, pk); return pk == out_key.key; } + + //--------------------------------------------------------------- + bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex) + { + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + return is_out_to_acc(acc, out_key, derivation, keyIndex); + } + //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered) + bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) { crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); if(null_pkey == tx_pub_key) @@ -545,19 +529,29 @@ namespace cryptonote return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); } //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) + bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) { money_transfered = 0; - size_t i = 0; - BOOST_FOREACH(const tx_out& o, tx.vout) - { - CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); - if(is_out_to_acc(acc, boost::get(o.target), tx_pub_key, i)) - { - outs.push_back(i); - money_transfered += o.amount; + size_t keyIndex = 0; + size_t outputIndex = 0; + + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + + for (const TransactionOutput& o : tx.vout) { + assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); + if (o.target.type() == typeid(TransactionOutputToKey)) { + if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { + outs.push_back(outputIndex); + money_transfered += o.amount; + } + + ++keyIndex; + } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { + keyIndex += boost::get(o.target).keys.size(); } - i++; + + ++outputIndex; } return true; } @@ -567,17 +561,6 @@ namespace cryptonote cn_fast_hash(blob.data(), blob.size(), res); } //--------------------------------------------------------------- - std::string print_money(uint64_t amount) - { - std::string s = std::to_string(amount); - if(s.size() < CRYPTONOTE_DISPLAY_DECIMAL_POINT+1) - { - s.insert(0, CRYPTONOTE_DISPLAY_DECIMAL_POINT+1 - s.size(), '0'); - } - s.insert(s.size() - CRYPTONOTE_DISPLAY_DECIMAL_POINT, "."); - return s; - } - //--------------------------------------------------------------- crypto::hash get_blob_hash(const blobdata& blob) { crypto::hash h = null_hash; @@ -585,7 +568,7 @@ namespace cryptonote return h; } //--------------------------------------------------------------- - crypto::hash get_transaction_hash(const transaction& t) + crypto::hash get_transaction_hash(const Transaction& t) { crypto::hash h = null_hash; size_t blob_size = 0; @@ -593,75 +576,79 @@ namespace cryptonote return h; } //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res) + bool get_transaction_hash(const Transaction& t, crypto::hash& res) { size_t blob_size = 0; return get_object_hash(t, res, blob_size); } //--------------------------------------------------------------- - bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) + bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size) { return get_object_hash(t, res, blob_size); } //--------------------------------------------------------------- - bool get_block_hashing_blob(const block& b, blobdata& blob) - { - if(!t_serializable_object_to_blob(static_cast(b), blob)) + bool get_block_hashing_blob(const Block& b, blobdata& blob) { + if (!t_serializable_object_to_blob(static_cast(b), blob)) { return false; + } crypto::hash tree_root_hash = get_tx_tree_hash(b); blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); - blob.append(tools::get_varint_data(b.tx_hashes.size() + 1)); + blob.append(tools::get_varint_data(b.txHashes.size() + 1)); return true; } //--------------------------------------------------------------- - bool get_block_hash(const block& b, crypto::hash& res) - { + bool get_parent_block_hashing_blob(const Block& b, blobdata& blob) { + auto serializer = makeParentBlockSerializer(b, true, true); + return t_serializable_object_to_blob(serializer, blob); + } + //--------------------------------------------------------------- + bool get_block_hash(const Block& b, crypto::hash& res) { blobdata blob; - if (!get_block_hashing_blob(b, blob)) + if (!get_block_hashing_blob(b, blob)) { return false; + } + + if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { + blobdata parent_blob; + auto serializer = makeParentBlockSerializer(b, true, false); + if (!t_serializable_object_to_blob(serializer, parent_blob)) + return false; + + blob.append(parent_blob); + } + return get_object_hash(blob, res); } //--------------------------------------------------------------- - crypto::hash get_block_hash(const block& b) - { + crypto::hash get_block_hash(const Block& b) { crypto::hash p = null_hash; get_block_hash(b, p); return p; } //--------------------------------------------------------------- - bool generate_genesis_block(block& bl) - { - //genesis block - bl = boost::value_initialized(); - - - account_public_address ac = boost::value_initialized(); - std::vector sz; - construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - - //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same - std::string genesis_coinbase_tx_hex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + bool get_aux_block_header_hash(const Block& b, crypto::hash& res) { + blobdata blob; + if (!get_block_hashing_blob(b, blob)) { + return false; + } - blobdata tx_bl; - string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); - bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); - CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; - bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; - bl.timestamp = 0; - bl.nonce = 70; - //miner::find_nonce_for_given_block(bl, 1, 0); - return true; + return get_object_hash(blob, res); } //--------------------------------------------------------------- - bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height) - { + bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res) { blobdata bd; - if(!get_block_hashing_blob(b, bd)) + if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { + if (!get_block_hashing_blob(b, bd)) { + return false; + } + } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { + if (!get_parent_block_hashing_blob(b, bd)) { + return false; + } + } else { return false; + } crypto::cn_slow_hash(context, bd.data(), bd.size(), res); return true; } @@ -686,14 +673,7 @@ namespace cryptonote return res; } //--------------------------------------------------------------- - crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height) - { - crypto::hash p = null_hash; - get_block_longhash(context, b, p, height); - return p; - } - //--------------------------------------------------------------- - bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) + bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b) { std::stringstream ss; ss << b_blob; @@ -703,22 +683,22 @@ namespace cryptonote return true; } //--------------------------------------------------------------- - blobdata block_to_blob(const block& b) + blobdata block_to_blob(const Block& b) { return t_serializable_object_to_blob(b); } //--------------------------------------------------------------- - bool block_to_blob(const block& b, blobdata& b_blob) + bool block_to_blob(const Block& b, blobdata& b_blob) { return t_serializable_object_to_blob(b, b_blob); } //--------------------------------------------------------------- - blobdata tx_to_blob(const transaction& tx) + blobdata tx_to_blob(const Transaction& tx) { return t_serializable_object_to_blob(tx); } //--------------------------------------------------------------- - bool tx_to_blob(const transaction& tx, blobdata& b_blob) + bool tx_to_blob(const Transaction& tx, blobdata& b_blob) { return t_serializable_object_to_blob(tx, b_blob); } @@ -735,15 +715,16 @@ namespace cryptonote return h; } //--------------------------------------------------------------- - crypto::hash get_tx_tree_hash(const block& b) + crypto::hash get_tx_tree_hash(const Block& b) { std::vector txs_ids; crypto::hash h = null_hash; size_t bl_sz = 0; - get_transaction_hash(b.miner_tx, h, bl_sz); + get_transaction_hash(b.minerTx, h, bl_sz); txs_ids.push_back(h); - BOOST_FOREACH(auto& th, b.tx_hashes) + for (auto& th : b.txHashes) { txs_ids.push_back(th); + } return get_tx_tree_hash(txs_ids); } //--------------------------------------------------------------- diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 3ff408a7dd..af588bf85c 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -16,22 +16,27 @@ // along with Bytecoin. If not, see . #pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "account.h" + +#include + +#include + #include "include_base_utils.h" + #include "crypto/crypto.h" #include "crypto/hash.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/difficulty.h" +#include "cryptonote_protocol/blobdatatype.h" 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_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 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 = 1); + void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h); + crypto::hash get_transaction_prefix_hash(const TransactionPrefix& 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); struct tx_source_entry { @@ -47,14 +52,14 @@ namespace cryptonote struct tx_destination_entry { uint64_t amount; //money - account_public_address addr; //destination address + AccountPublicAddress addr; //destination address - tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { } - tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } + tx_destination_entry() : amount(0), addr(boost::value_initialized()) { } + tx_destination_entry(uint64_t a, const AccountPublicAddress &ad) : amount(a), addr(ad) { } }; //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, transaction& tx, uint64_t unlock_time); + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, Transaction& tx, uint64_t unlock_time); template bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) @@ -69,44 +74,46 @@ namespace cryptonote bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra); - crypto::public_key get_tx_pub_key_from_extra(const transaction& tx); - bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); + crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx); + bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key); bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); - bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index); - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); - bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered); - bool get_tx_fee(const transaction& tx, uint64_t & fee); - uint64_t get_tx_fee(const transaction& tx); - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); + bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag); + bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag); + bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex); + bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex); + bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); + bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); + bool get_tx_fee(const Transaction& tx, uint64_t & fee); + uint64_t get_tx_fee(const Transaction& tx); + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki); void get_blob_hash(const blobdata& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); std::string short_hash_str(const crypto::hash& h); - crypto::hash get_transaction_hash(const transaction& t); - bool get_transaction_hash(const transaction& t, crypto::hash& res); - bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); - bool get_block_hashing_blob(const block& b, blobdata& blob); - bool get_block_hash(const block& b, crypto::hash& res); - crypto::hash get_block_hash(const block& b); - bool get_block_longhash(crypto::cn_context &context, const block& b, crypto::hash& res, uint64_t height); - crypto::hash get_block_longhash(crypto::cn_context &context, const block& b, uint64_t height); - bool generate_genesis_block(block& bl); - bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); - bool get_inputs_money_amount(const transaction& tx, uint64_t& money); - uint64_t get_outs_money_amount(const transaction& tx); - 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); - - bool check_money_overflow(const transaction& tx); - bool check_outs_overflow(const transaction& tx); - bool check_inputs_overflow(const transaction& tx); - uint64_t get_block_height(const block& b); + crypto::hash get_transaction_hash(const Transaction& t); + bool get_transaction_hash(const Transaction& t, crypto::hash& res); + bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size); + bool get_block_hashing_blob(const Block& b, blobdata& blob); + bool get_parent_block_hashing_blob(const Block& b, blobdata& blob); + bool get_aux_block_header_hash(const Block& b, crypto::hash& res); + bool get_block_hash(const Block& b, crypto::hash& res); + crypto::hash get_block_hash(const Block& b); + bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res); + bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b); + bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); + uint64_t get_outs_money_amount(const Transaction& tx); + bool check_inputs_types_supported(const Transaction& tx); + bool check_outs_valid(const Transaction& tx); + bool checkMultisignatureInputsDiff(const Transaction& tx); + + bool check_money_overflow(const Transaction& tx); + bool check_outs_overflow(const Transaction& tx); + bool check_inputs_overflow(const Transaction& tx); + uint64_t get_block_height(const Block& b); std::vector relative_output_offsets_to_absolute(const std::vector& off); std::vector absolute_output_offsets_to_relative(const std::vector& off); - std::string print_money(uint64_t amount); //--------------------------------------------------------------- template bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) @@ -134,10 +141,22 @@ namespace cryptonote } //--------------------------------------------------------------- template + bool get_object_blobsize(const t_object& o, size_t& size) { + blobdata blob; + if (!t_serializable_object_to_blob(o, blob)) { + size = (std::numeric_limits::max)(); + return false; + } + size = blob.size(); + return true; + } + //--------------------------------------------------------------- + template size_t get_object_blobsize(const t_object& o) { - blobdata b = t_serializable_object_to_blob(o); - return b.size(); + size_t size; + get_object_blobsize(o, size); + return size; } //--------------------------------------------------------------- template @@ -150,11 +169,10 @@ namespace cryptonote } //--------------------------------------------------------------- template - std::string obj_to_json_str(T& obj) - { + std::string obj_to_json_str(const T& obj) { std::stringstream ss; json_archive ar(ss, true); - bool r = ::serialization::serialize(ar, obj); + bool r = ::serialization::serialize(ar, *const_cast(&obj)); CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false"); return ss.str(); } @@ -201,13 +219,13 @@ namespace cryptonote } } //--------------------------------------------------------------- - blobdata block_to_blob(const block& b); - bool block_to_blob(const block& b, blobdata& b_blob); - blobdata tx_to_blob(const transaction& b); - bool tx_to_blob(const transaction& b, blobdata& b_blob); + blobdata block_to_blob(const Block& b); + bool block_to_blob(const Block& b, blobdata& b_blob); + blobdata tx_to_blob(const Transaction& b); + bool tx_to_blob(const Transaction& b, blobdata& b_blob); void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h); crypto::hash get_tx_tree_hash(const std::vector& tx_hashes); - crypto::hash get_tx_tree_hash(const block& b); + crypto::hash get_tx_tree_hash(const Block& b); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp index 7100da4e5e..eed5e00489 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_core/difficulty.cpp @@ -75,50 +75,4 @@ namespace cryptonote { carry = cadc(high, top, carry); return !carry; } - - difficulty_type next_difficulty(vector timestamps, vector cumulative_difficulties, size_t target_seconds) { - //cutoff DIFFICULTY_LAG - if(timestamps.size() > DIFFICULTY_WINDOW) - { - timestamps.resize(DIFFICULTY_WINDOW); - cumulative_difficulties.resize(DIFFICULTY_WINDOW); - } - - - size_t length = timestamps.size(); - assert(length == cumulative_difficulties.size()); - if (length <= 1) { - return 1; - } - static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); - assert(length <= DIFFICULTY_WINDOW); - sort(timestamps.begin(), timestamps.end()); - size_t cut_begin, cut_end; - static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large"); - if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { - cut_begin = 0; - cut_end = length; - } else { - cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; - cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); - } - assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); - uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; - if (time_span == 0) { - time_span = 1; - } - difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; - assert(total_work > 0); - uint64_t low, high; - mul(total_work, target_seconds, low, high); - if (high != 0 || low + time_span - 1 < low) { - return 0; - } - return (low + time_span - 1) / time_span; - } - - difficulty_type next_difficulty(vector timestamps, vector cumulative_difficulties) - { - return next_difficulty(std::move(timestamps), std::move(cumulative_difficulties), DIFFICULTY_TARGET); - } } diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h index 5a4c803c57..f10937c060 100644 --- a/src/cryptonote_core/difficulty.h +++ b/src/cryptonote_core/difficulty.h @@ -27,6 +27,4 @@ namespace cryptonote typedef std::uint64_t difficulty_type; bool check_hash(const crypto::hash &hash, difficulty_type difficulty); - difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties); - difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); } diff --git a/src/cryptonote_core/i_miner_handler.h b/src/cryptonote_core/i_miner_handler.h new file mode 100644 index 0000000000..ff3d20fb84 --- /dev/null +++ b/src/cryptonote_core/i_miner_handler.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/difficulty.h" + +namespace cryptonote { + struct i_miner_handler { + virtual bool handle_block_found(Block& b) = 0; + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + + protected: + ~i_miner_handler(){}; + }; +} diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 3fad129a7c..fc64a01acf 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -17,10 +17,13 @@ #include #include -#include + +#include +#include #include #include -#include +#include + #include "misc_language.h" #include "include_base_utils.h" #include "cryptonote_basic_impl.h" @@ -35,8 +38,8 @@ using namespace epee; #include "miner.h" - - +#include +#include namespace cryptonote { @@ -49,13 +52,14 @@ namespace cryptonote } - miner::miner(i_miner_handler* phandler):m_stop(1), - m_template(boost::value_initialized()), + miner::miner(const Currency& currency, i_miner_handler* phandler): + m_currency(currency), + m_stop(1), + m_template(boost::value_initialized()), m_template_no(0), m_diffic(0), m_thread_index(0), m_phandler(phandler), - m_height(0), m_pausers_count(0), m_threads_total(0), m_starter_nonce(0), @@ -65,38 +69,47 @@ namespace cryptonote m_do_mining(false), m_current_hash_rate(0) { - } //----------------------------------------------------------------------------------------------------- - miner::~miner() - { + miner::~miner() { stop(); } //----------------------------------------------------------------------------------------------------- - bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) - { + bool miner::set_block_template(const Block& bl, const difficulty_type& di) { CRITICAL_REGION_LOCAL(m_template_lock); m_template = bl; + + if (BLOCK_MAJOR_VERSION_2 == m_template.majorVersion) { + cryptonote::tx_extra_merge_mining_tag mm_tag; + mm_tag.depth = 0; + if (!cryptonote::get_aux_block_header_hash(m_template, mm_tag.merkle_root)) { + return false; + } + + m_template.parentBlock.minerTx.extra.clear(); + if (!cryptonote::append_mm_tag_to_extra(m_template.parentBlock.minerTx.extra, mm_tag)) { + return false; + } + } + m_diffic = di; - m_height = height; ++m_template_no; m_starter_nonce = crypto::rand(); return true; } //----------------------------------------------------------------------------------------------------- - bool miner::on_block_chain_update() - { - if(!is_mining()) + bool miner::on_block_chain_update() { + if (!is_mining()) { return true; + } return request_block_template(); } //----------------------------------------------------------------------------------------------------- - bool miner::request_block_template() - { - block bl = AUTO_VAL_INIT(bl); + bool miner::request_block_template() { + Block bl = AUTO_VAL_INIT(bl); difficulty_type di = AUTO_VAL_INIT(di); - uint64_t height = AUTO_VAL_INIT(height); + uint64_t height; cryptonote::blobdata extra_nonce; if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) { @@ -108,7 +121,7 @@ namespace cryptonote LOG_ERROR("Failed to get_block_template(), stopping mining"); return false; } - set_block_template(bl, di, height); + set_block_template(bl, di); return true; } //----------------------------------------------------------------------------------------------------- @@ -180,14 +193,13 @@ namespace cryptonote } m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); m_config = AUTO_VAL_INIT(m_config); - epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + cryptonote::parameters::MINER_CONFIG_FILE_NAME); LOG_PRINT_L0("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); } if(command_line::has_arg(vm, arg_start_mining)) { - if(!cryptonote::get_account_address_from_str(m_mine_address, command_line::get_arg(vm, arg_start_mining))) - { + if (!m_currency.parseAccountAddressString(command_line::get_arg(vm, arg_start_mining), m_mine_address)) { LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); return false; } @@ -207,7 +219,7 @@ namespace cryptonote return !m_stop; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs) + bool miner::start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs) { m_mine_address = adr; m_threads_total = static_cast(threads_count); @@ -266,18 +278,61 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(crypto::cn_context &context, block& bl, const difficulty_type& diffic, uint64_t height) - { - for(; bl.nonce != std::numeric_limits::max(); bl.nonce++) - { - crypto::hash h; - get_block_longhash(context, bl, h, height); + bool miner::find_nonce_for_given_block(crypto::cn_context &context, Block& bl, const difficulty_type& diffic) { - if(check_hash(h, diffic)) - { - return true; + unsigned nthreads = std::thread::hardware_concurrency(); + + if (nthreads > 0 && diffic > 5) { + std::vector> threads(nthreads); + std::atomic foundNonce; + std::atomic found(false); + uint32_t startNonce = crypto::rand(); + + for (unsigned i = 0; i < nthreads; ++i) { + threads[i] = std::async(std::launch::async, [&, i]() { + crypto::cn_context localctx; + crypto::hash h; + + Block lb(bl); // copy to local block + + for (uint32_t nonce = startNonce + i; !found; nonce += nthreads) { + lb.nonce = nonce; + + if (!get_block_longhash(localctx, lb, h)) { + return; + } + + if (check_hash(h, diffic)) { + foundNonce = nonce; + found = true; + return; + } + } + }); + } + + for (auto& t : threads) { + t.wait(); + } + + if (found) { + bl.nonce = foundNonce.load(); + } + + return found; + } else { + for (; bl.nonce != std::numeric_limits::max(); bl.nonce++) { + crypto::hash h; + if (!get_block_longhash(context, bl, h)) { + return false; + } + + if (check_hash(h, diffic)) { + return true; + } } } + return false; } //----------------------------------------------------------------------------------------------------- @@ -319,11 +374,10 @@ namespace cryptonote LOG_PRINT_L0("Miner thread was started ["<< th_local_index << "]"); log_space::log_singletone::set_thread_log_prefix(std::string("[miner ") + std::to_string(th_local_index) + "]"); uint32_t nonce = m_starter_nonce + th_local_index; - uint64_t height = 0; difficulty_type local_diff = 0; uint32_t local_template_ver = 0; crypto::cn_context context; - block b; + Block b; while(!m_stop) { if(m_pausers_count)//anti split workaround @@ -338,7 +392,6 @@ namespace cryptonote CRITICAL_REGION_BEGIN(m_template_lock); b = m_template; local_diff = m_diffic; - height = m_height; CRITICAL_REGION_END(); local_template_ver = m_template_no; nonce = m_starter_nonce + th_local_index; @@ -353,9 +406,12 @@ namespace cryptonote b.nonce = nonce; crypto::hash h; - get_block_longhash(context, b, h, height); + if (!m_stop && !get_block_longhash(context, b, h)) { + LOG_ERROR("Failed to get block long hash"); + m_stop = true; + } - if(!m_stop && check_hash(h, local_diff)) + if (!m_stop && check_hash(h, local_diff)) { //we lucky! ++m_config.current_extra_message_index; @@ -366,7 +422,7 @@ namespace cryptonote }else { //success update, lets update config - epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + cryptonote::parameters::MINER_CONFIG_FILE_NAME); } } diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index 1b0c19ab77..1f492761a5 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -17,37 +17,29 @@ #pragma once -#include #include -#include "cryptonote_basic.h" -#include "difficulty.h" -#include "math_helper.h" +#include -namespace cryptonote -{ +// epee +#include "serialization/keyvalue_serialization.h" +#include "math_helper.h" - struct i_miner_handler - { - virtual bool handle_block_found(block& b) = 0; - virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; - protected: - ~i_miner_handler(){}; - }; +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/difficulty.h" +#include "cryptonote_core/i_miner_handler.h" - /************************************************************************/ - /* */ - /************************************************************************/ - class miner - { +namespace cryptonote { + class miner { public: - miner(i_miner_handler* phandler); + miner(const Currency& currency, i_miner_handler* phandler); ~miner(); bool init(const boost::program_options::variables_map& vm); static void init_options(boost::program_options::options_description& desc); - bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); + bool set_block_template(const Block& bl, const difficulty_type& diffic); bool on_block_chain_update(); - bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs); + bool start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs); uint64_t get_speed(); void send_stop_signal(); bool stop(); @@ -55,7 +47,7 @@ namespace cryptonote bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(crypto::cn_context &context, block& bl, const difficulty_type& diffic, uint64_t height); + static bool find_nonce_for_given_block(crypto::cn_context &context, Block& bl, const difficulty_type& diffic); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -75,13 +67,13 @@ namespace cryptonote }; + const Currency& m_currency; volatile uint32_t m_stop; epee::critical_section m_template_lock; - block m_template; + Block m_template; std::atomic m_template_no; std::atomic m_starter_nonce; difficulty_type m_diffic; - uint64_t m_height; volatile uint32_t m_thread_index; volatile uint32_t m_threads_total; std::atomic m_pausers_count; @@ -90,7 +82,7 @@ namespace cryptonote std::list m_threads; epee::critical_section m_threads_lock; i_miner_handler* m_phandler; - account_public_address m_mine_address; + AccountPublicAddress m_mine_address; epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; std::vector m_extra_messages; @@ -103,6 +95,5 @@ namespace cryptonote std::list m_last_hash_rates; bool m_do_print_hashrate; bool m_do_mining; - }; } diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h index 1c73e6ec40..325bc2a884 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_core/tx_extra.h @@ -17,6 +17,12 @@ #pragma once +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "serialization/binary_archive.h" +#include "serialization/crypto.h" +#include "serialization/serialization.h" +#include "serialization/variant.h" #define TX_EXTRA_PADDING_MAX_COUNT 255 #define TX_EXTRA_NONCE_MAX_COUNT 255 diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index ff80b60dcd..27beaa38d4 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -15,32 +15,100 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "tx_pool.h" + #include -#include -#include +#include #include +#include -#include "tx_pool.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" -#include "cryptonote_config.h" -#include "blockchain_storage.h" -#include "common/boost_serialization_helper.h" -#include "common/int-util.h" +#include + +// epee #include "misc_language.h" +#include "misc_log_ex.h" #include "warnings.h" + +#include "common/boost_serialization_helper.h" +#include "common/int-util.h" +#include "common/util.h" #include "crypto/hash.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_config.h" -DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated -#define TRANSACTION_SIZE_LIMIT (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) +DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated namespace cryptonote { + //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) { + // BlockTemplate + //--------------------------------------------------------------------------------- + class BlockTemplate { + public: + + bool addTransaction(const crypto::hash& txid, const Transaction& tx) { + if (!canAdd(tx)) + return false; + + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + auto r = m_keyImages.insert(boost::get(in).keyImage); + (void)r; //just to make compiler to shut up + assert(r.second); + } else if (in.type() == typeid(TransactionInputMultisignature)) { + const auto& msig = boost::get(in); + auto r = m_usedOutputs.insert(std::make_pair(msig.amount, msig.outputIndex)); + (void)r; //just to make compiler to shut up + assert(r.second); + } + } + + m_txHashes.push_back(txid); + return true; + } + + const std::vector& getTransactions() const { + return m_txHashes; + } + + private: + + bool canAdd(const Transaction& tx) { + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + if (m_keyImages.count(boost::get(in).keyImage)) { + return false; + } + } else if (in.type() == typeid(TransactionInputMultisignature)) { + const auto& msig = boost::get(in); + if (m_usedOutputs.count(std::make_pair(msig.amount, msig.outputIndex))) { + return false; + } + } + } + return true; + } + + std::unordered_set m_keyImages; + std::set> m_usedOutputs; + std::vector m_txHashes; + }; + + using CryptoNote::BlockInfo; + + //--------------------------------------------------------------------------------- + tx_memory_pool::tx_memory_pool(const cryptonote::Currency& currency, CryptoNote::ITransactionValidator& validator, CryptoNote::ITimeProvider& timeProvider) : + m_currency(currency), + m_validator(validator), + m_timeProvider(timeProvider), + m_txCheckInterval(60, timeProvider), + m_fee_index(boost::get<1>(m_transactions)) { } + //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(const 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 tx_memory_pool::add_tx(const Transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { if (!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; @@ -55,134 +123,103 @@ namespace cryptonote { uint64_t outputs_amount = get_outs_money_amount(tx); if (outputs_amount >= inputs_amount) { - LOG_PRINT_L0("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount)); + LOG_PRINT_L0("transaction use more money then it has: use " << m_currency.formatAmount(outputs_amount) << + ", have " << m_currency.formatAmount(inputs_amount)); tvc.m_verifivation_failed = true; return false; } - uint64_t fee = inputs_amount - outputs_amount; - if (!kept_by_block && fee < MINIMUM_FEE) { - LOG_ERROR("transaction fee is not enought: " << print_money(fee) << ", minumim fee: " << print_money(MINIMUM_FEE)); - tvc.m_verifivation_failed = true; - return false; - } - - if (!kept_by_block && blob_size >= TRANSACTION_SIZE_LIMIT) { - LOG_ERROR("transaction is too big: " << blob_size << " bytes, maximum size: " << TRANSACTION_SIZE_LIMIT); + const uint64_t fee = inputs_amount - outputs_amount; + if (!keptByBlock && fee < m_currency.minimumFee()) { + LOG_PRINT_L0("transaction fee is not enought: " << m_currency.formatAmount(fee) << + ", minumim fee: " << m_currency.formatAmount(m_currency.minimumFee())); tvc.m_verifivation_failed = true; + tvc.m_tx_fee_too_small = true; return false; } //check key images for transaction if it is not kept by block - if (!kept_by_block) { - if (have_tx_keyimges_as_spent(tx)) { - LOG_ERROR("Transaction with id= "<< id << " used already spent key images"); + if (!keptByBlock) { + CRITICAL_REGION_LOCAL(m_transactions_lock); + if (haveSpentInputs(tx)) { + LOG_PRINT_L0("Transaction with id= " << id << " used already spent inputs"); tvc.m_verifivation_failed = true; return false; } } - crypto::hash max_used_block_id = null_hash; - uint64_t max_used_block_height = 0; - bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); - CRITICAL_REGION_LOCAL(m_transactions_lock); - if (!ch_inp_res) { - if (kept_by_block) { - //anyway add this transaction to pool, because it related to block - auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); - CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); - txd_p.first->second.blob_size = blob_size; - txd_p.first->second.tx = tx; - txd_p.first->second.fee = inputs_amount - outputs_amount; - txd_p.first->second.max_used_block_id = null_hash; - txd_p.first->second.max_used_block_height = 0; - txd_p.first->second.kept_by_block = kept_by_block; - tvc.m_verifivation_impossible = true; - tvc.m_added_to_pool = true; - } else { + BlockInfo maxUsedBlock; + + // check inputs + bool inputsValid = m_validator.checkTransactionInputs(tx, maxUsedBlock); + + if (!inputsValid) { + if (!keptByBlock) { LOG_PRINT_L0("tx used wrong inputs, rejected"); tvc.m_verifivation_failed = true; return false; } - } else { - //update transactions container - auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); - CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); - txd_p.first->second.blob_size = blob_size; - txd_p.first->second.tx = tx; - txd_p.first->second.kept_by_block = kept_by_block; - txd_p.first->second.fee = inputs_amount - outputs_amount; - txd_p.first->second.max_used_block_id = max_used_block_id; - txd_p.first->second.max_used_block_height = max_used_block_height; - txd_p.first->second.last_failed_height = 0; - txd_p.first->second.last_failed_id = null_hash; - tvc.m_added_to_pool = true; - - if (txd_p.first->second.fee > 0) { - tvc.m_should_be_relayed = true; - } + + maxUsedBlock.clear(); + tvc.m_verifivation_impossible = true; } - tvc.m_verifivation_failed = true; - //update image_keys container, here should everything goes ok. - for (const auto& in : tx.vin) { - 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: keeped_by_block=" << kept_by_block - << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL - << "tx_id=" << id ); - 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"); + CRITICAL_REGION_LOCAL(m_transactions_lock); + + // add to pool + { + TransactionDetails txd; + + txd.id = id; + txd.blobSize = blobSize; + txd.tx = tx; + txd.fee = fee; + txd.keptByBlock = keptByBlock; + txd.receiveTime = m_timeProvider.now(); + + txd.maxUsedBlock = maxUsedBlock; + txd.lastFailedBlock.clear(); + + auto txd_p = m_transactions.insert(std::move(txd)); + CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); } + tvc.m_added_to_pool = true; + + if (inputsValid && fee > 0) + tvc.m_should_be_relayed = true; + + tvc.m_verifivation_failed = true; + + if (!addTransactionInputs(id, tx, keptByBlock)) + return false; + tvc.m_verifivation_failed = false; //succeed return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) { + bool tx_memory_pool::add_tx(const Transaction &tx, tx_verification_context& tvc, bool keeped_by_block) { crypto::hash h = null_hash; - size_t blob_size = 0; - get_transaction_hash(tx, h, blob_size); - return add_tx(tx, h, blob_size, tvc, keeped_by_block); - } - //--------------------------------------------------------------------------------- - bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) { - CRITICAL_REGION_LOCAL(m_transactions_lock); - crypto::hash tx_id = 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 << std::endl - << "transaction id = " << tx_id); - std::unordered_set& key_image_set = it->second; - CHECK_AND_ASSERT_MES(!key_image_set.empty(), false, "empty key_image set, img=" << txin.k_image << std::endl - << "transaction id = " << tx_id); - - auto it_in_set = key_image_set.find(tx_id); - CHECK_AND_ASSERT_MES(it_in_set != key_image_set.end(), false, "transaction id not found in key_image set, img=" << txin.k_image << std::endl - << "transaction id = " << tx_id); - key_image_set.erase(it_in_set); - if (key_image_set.empty()) { - //it is now empty hash container for this key_image - m_spent_key_images.erase(it); - } - } - return true; + size_t blobSize = 0; + get_transaction_hash(tx, h, blobSize); + return add_tx(tx, h, blobSize, tvc, keeped_by_block); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee) { + bool tx_memory_pool::take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee) { CRITICAL_REGION_LOCAL(m_transactions_lock); auto it = m_transactions.find(id); if (it == m_transactions.end()) { return false; } - tx = it->second.tx; - blob_size = it->second.blob_size; - fee = it->second.fee; - remove_transaction_keyimages(it->second.tx); - m_transactions.erase(it); + auto& txd = *it; + + tx = txd.tx; + blobSize = txd.blobSize; + fee = txd.fee; + + removeTransaction(it); return true; } //--------------------------------------------------------------------------------- @@ -191,10 +228,10 @@ namespace cryptonote { return m_transactions.size(); } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_transactions(std::list& txs) const { + void tx_memory_pool::get_transactions(std::list& txs) const { CRITICAL_REGION_LOCAL(m_transactions_lock); for (const auto& tx_vt : m_transactions) { - txs.push_back(tx_vt.second.tx); + txs.push_back(tx_vt.tx); } } //--------------------------------------------------------------------------------- @@ -214,22 +251,6 @@ namespace cryptonote { return false; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) const { - CRITICAL_REGION_LOCAL(m_transactions_lock); - for (const auto& in : tx.vin) { - CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail - if (have_tx_keyimg_as_spent(tokey_in.k_image)) { - return true; - } - } - return false; - } - //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) const { - CRITICAL_REGION_LOCAL(m_transactions_lock); - return m_spent_key_images.end() != m_spent_key_images.find(key_im); - } - //--------------------------------------------------------------------------------- void tx_memory_pool::lock() const { m_transactions_lock.lock(); } @@ -238,110 +259,74 @@ namespace cryptonote { m_transactions_lock.unlock(); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd) const { - //not the best implementation at this time, sorry :( - //check is ring_signature already checked ? - if (txd.max_used_block_id == null_hash) { - //not checked, lets try to check - if (txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) { - return false;//we already sure that this tx is broken for this height - } + bool tx_memory_pool::is_transaction_ready_to_go(const Transaction& tx, TransactionCheckInfo& txd) const { - if (!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) { - 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); - return false; - } - } else { - if (txd.max_used_block_height >= m_blockchain.get_current_blockchain_height()) { - return false; - } - if (m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id) { - //if we already failed on this height and id, skip actual ring signature check - if (txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) { - return false; - } - //check ring signature again, it is possible (with very small chance) that this transaction become again valid - if (!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) { - 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); - return false; - } - } - } + if (!m_validator.checkTransactionInputs(tx, txd.maxUsedBlock, txd.lastFailedBlock)) + return false; //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(txd.tx)) { + if (m_validator.haveSpentKeyImages(tx)) return false; - } //transaction is ok. return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_key_images(const std::unordered_set& k_images, const transaction& tx) { - for (size_t i = 0; i!= tx.vin.size(); i++) { - CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); - if (k_images.count(itk.k_image)) { - return true; - } - } - return false; - } - //--------------------------------------------------------------------------------- - bool tx_memory_pool::append_key_images(std::unordered_set& k_images, const transaction& tx) { - for (size_t i = 0; i!= tx.vin.size(); i++) { - CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); - auto i_res = k_images.insert(itk.k_image); - CHECK_AND_ASSERT_MES(i_res.second, false, "internal error: key images pool cache - inserted duplicate image in set: " << itk.k_image); - } - return true; - } - //--------------------------------------------------------------------------------- std::string tx_memory_pool::print_pool(bool short_format) const { std::stringstream ss; CRITICAL_REGION_LOCAL(m_transactions_lock); - for (const transactions_container::value_type& txe : m_transactions) { - const tx_details& txd = txe.second; - ss << "id: " << txe.first << std::endl; + for (const auto& txd : m_fee_index) { + ss << "id: " << txd.id << std::endl; if (!short_format) { - ss << obj_to_json_str(*const_cast(&txd.tx)) << std::endl; + ss << obj_to_json_str(txd.tx) << std::endl; } - ss << "blob_size: " << txd.blob_size << std::endl - << "fee: " << print_money(txd.fee) << std::endl - << "kept_by_block: " << (txd.kept_by_block ? 'T' : 'F') << std::endl - << "max_used_block_height: " << txd.max_used_block_height << std::endl - << "max_used_block_id: " << txd.max_used_block_id << std::endl - << "last_failed_height: " << txd.last_failed_height << std::endl - << "last_failed_id: " << txd.last_failed_id << std::endl; + ss << "blobSize: " << txd.blobSize << std::endl + << "fee: " << m_currency.formatAmount(txd.fee) << std::endl + << "keptByBlock: " << (txd.keptByBlock ? 'T' : 'F') << std::endl + << "max_used_block_height: " << txd.maxUsedBlock.height << std::endl + << "max_used_block_id: " << txd.maxUsedBlock.id << std::endl + << "last_failed_height: " << txd.lastFailedBlock.height << std::endl + << "last_failed_id: " << txd.lastFailedBlock.id << std::endl + << "recieved: " << std::ctime(&txd.receiveTime) << std::endl; } return ss.str(); } //--------------------------------------------------------------------------------- - 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) { + bool tx_memory_pool::fill_block_template(Block& bl, size_t median_size, size_t maxCumulativeSize, + uint64_t already_generated_coins, size_t& total_size, uint64_t& fee) { CRITICAL_REGION_LOCAL(m_transactions_lock); total_size = 0; fee = 0; - size_t max_total_size = (125 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; - std::unordered_set k_images; - for (transactions_container::value_type& tx : m_transactions) { - if (max_total_size < total_size + tx.second.blob_size) { - continue; - } + size_t max_total_size = (125 * median_size) / 100 - m_currency.minerTxBlobReservedSize(); + max_total_size = std::min(max_total_size, maxCumulativeSize); + + BlockTemplate blockTemplate; + + for (auto i = m_fee_index.begin(); i != m_fee_index.end(); ++i) { + const auto& txd = *i; - if (!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx)) { + if (max_total_size < total_size + txd.blobSize) { continue; } - bl.tx_hashes.push_back(tx.first); - total_size += tx.second.blob_size; - fee += tx.second.fee; - append_key_images(k_images, tx.second.tx); + TransactionCheckInfo checkInfo(txd); + bool ready = is_transaction_ready_to_go(txd.tx, checkInfo); + + // update item state + m_fee_index.modify(i, [&checkInfo](TransactionCheckInfo& item) { + item = checkInfo; + }); + + if (ready && blockTemplate.addTransaction(txd.id, txd.tx)) { + total_size += txd.blobSize; + fee += txd.fee; + } } + bl.txHashes = blockTemplate.getTransactions(); return true; } //--------------------------------------------------------------------------------- @@ -349,7 +334,7 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); m_config_folder = config_folder; - std::string state_file_path = config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; + std::string state_file_path = config_folder + "/" + m_currency.txPoolFileName(); boost::system::error_code ec; if (!boost::filesystem::exists(state_file_path, ec)) { return true; @@ -360,17 +345,8 @@ namespace cryptonote { m_transactions.clear(); m_spent_key_images.clear(); + m_spentOutputs.clear(); } - - for (auto it = m_transactions.begin(); it != m_transactions.end(); ) { - auto it2 = it++; - if (it2->second.blob_size >= TRANSACTION_SIZE_LIMIT) { - LOG_PRINT_L0("Transaction " << get_transaction_hash(it2->second.tx) << " is too big (" << it2->second.blob_size << " bytes), removing it from pool"); - remove_transaction_keyimages(it2->second.tx); - m_transactions.erase(it2); - } - } - // Ignore deserialization error return true; } @@ -381,11 +357,116 @@ namespace cryptonote { return false; } - std::string state_file_path = m_config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; + std::string state_file_path = m_config_folder + "/" + m_currency.txPoolFileName(); bool res = tools::serialize_obj_to_file(*this, state_file_path); if (!res) { LOG_PRINT_L0("Failed to serialize memory pool to file " << state_file_path); } return true; } + + //--------------------------------------------------------------------------------- + void tx_memory_pool::on_idle() { + m_txCheckInterval.call([this](){ return removeExpiredTransactions(); }); + } + + //--------------------------------------------------------------------------------- + bool tx_memory_pool::removeExpiredTransactions() { + CRITICAL_REGION_LOCAL(m_transactions_lock); + + auto now = m_timeProvider.now(); + + for (auto it = m_transactions.begin(); it != m_transactions.end();) { + uint64_t txAge = now - it->receiveTime; + bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); + + if (remove) { + LOG_PRINT_L2("Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge); + it = removeTransaction(it); + } else { + ++it; + } + } + return true; + } + + tx_memory_pool::tx_container_t::iterator tx_memory_pool::removeTransaction(tx_memory_pool::tx_container_t::iterator i) { + removeTransactionInputs(i->id, i->tx, i->keptByBlock); + return m_transactions.erase(i); + } + + bool tx_memory_pool::removeTransactionInputs(const crypto::hash& tx_id, const Transaction& tx, bool keptByBlock) { + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + const auto& txin = boost::get(in); + auto it = m_spent_key_images.find(txin.keyImage); + CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.keyImage << std::endl + << "transaction id = " << tx_id); + std::unordered_set& key_image_set = it->second; + CHECK_AND_ASSERT_MES(!key_image_set.empty(), false, "empty key_image set, img=" << txin.keyImage << std::endl + << "transaction id = " << tx_id); + + auto it_in_set = key_image_set.find(tx_id); + CHECK_AND_ASSERT_MES(it_in_set != key_image_set.end(), false, "transaction id not found in key_image set, img=" << txin.keyImage << std::endl + << "transaction id = " << tx_id); + key_image_set.erase(it_in_set); + if (key_image_set.empty()) { + //it is now empty hash container for this key_image + m_spent_key_images.erase(it); + } + } else if (in.type() == typeid(TransactionInputMultisignature)) { + if (!keptByBlock) { + const auto& msig = boost::get(in); + auto output = GlobalOutput(msig.amount, msig.outputIndex); + assert(m_spentOutputs.count(output)); + m_spentOutputs.erase(output); + } + } + } + + return true; + } + + //--------------------------------------------------------------------------------- + bool tx_memory_pool::addTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock) { + // should not fail + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + const auto& txin = boost::get(in); + std::unordered_set& kei_image_set = m_spent_key_images[txin.keyImage]; + CHECK_AND_ASSERT_MES(keptByBlock || kei_image_set.size() == 0, false, "internal error: keptByBlock=" << keptByBlock + << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.keyImage=" << txin.keyImage << ENDL + << "tx_id=" << id); + 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"); + } else if (in.type() == typeid(TransactionInputMultisignature)) { + if (!keptByBlock) { + const auto& msig = boost::get(in); + auto r = m_spentOutputs.insert(GlobalOutput(msig.amount, msig.outputIndex)); + (void)r; + assert(r.second); + } + } + } + + return true; + } + + //--------------------------------------------------------------------------------- + bool tx_memory_pool::haveSpentInputs(const Transaction& tx) const { + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + const auto& tokey_in = boost::get(in); + if (m_spent_key_images.count(tokey_in.keyImage)) { + return true; + } + } else if (in.type() == typeid(TransactionInputMultisignature)) { + const auto& msig = boost::get(in); + if (m_spentOutputs.count(GlobalOutput(msig.amount, msig.outputIndex))) { + return true; + } + } + } + return false; + } } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 3d0c3336e6..91db2ca7ea 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -21,35 +21,80 @@ #include #include #include + #include #include +// multi index +#include +#include +#include +#include + +// epee +#include "math_helper.h" #include "string_tools.h" #include "syncobj.h" -#include "cryptonote_basic_impl.h" -#include "verification_context.h" + +#include "common/util.h" +#include "common/int-util.h" #include "crypto/hash.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/ITimeProvider.h" +#include "cryptonote_core/ITransactionValidator.h" +#include "cryptonote_core/verification_context.h" namespace cryptonote { - class blockchain_storage; + + + class OnceInTimeInterval { + public: + OnceInTimeInterval(unsigned interval, CryptoNote::ITimeProvider& timeProvider) + : m_interval(interval), m_timeProvider(timeProvider) { + m_lastWorkedTime = 0; + } + + template + bool call(functor_t functr) { + time_t now = m_timeProvider.now(); + + if (now - m_lastWorkedTime > m_interval) { + bool res = functr(); + m_lastWorkedTime = m_timeProvider.now(); + return res; + } + + return true; + } + + private: + time_t m_lastWorkedTime; + unsigned m_interval; + CryptoNote::ITimeProvider& m_timeProvider; + }; + + using CryptoNote::BlockInfo; + using namespace boost::multi_index; /************************************************************************/ /* */ /************************************************************************/ class tx_memory_pool: boost::noncopyable { public: - tx_memory_pool(blockchain_storage& bchs); + tx_memory_pool(const cryptonote::Currency& currency, CryptoNote::ITransactionValidator& validator, + CryptoNote::ITimeProvider& timeProvider); // load/store operations bool init(const std::string& config_folder); bool deinit(); bool have_tx(const crypto::hash &id) const; - bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); - bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block); + bool add_tx(const Transaction &tx, const crypto::hash &id, size_t blobSize, tx_verification_context& tvc, bool keeped_by_block); + bool add_tx(const Transaction &tx, tx_verification_context& tvc, bool keeped_by_block); //gets tx and remove it from pool - bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee); + bool take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee); bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); @@ -57,13 +102,28 @@ namespace cryptonote { void lock() const; void unlock() const; - bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); + bool fill_block_template(Block &bl, size_t median_size, size_t maxCumulativeSize, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); - void get_transactions(std::list& txs) const; + void get_transactions(std::list& txs) const; size_t get_transactions_count() const; std::string print_pool(bool short_format) const; + void on_idle(); + + template + void getTransactions(const t_ids_container& txsIds, t_tx_container& txs, t_missed_container& missedTxs) { + CRITICAL_REGION_LOCAL(m_transactions_lock); -#define CURRENT_MEMPOOL_ARCHIVE_VER 7 + for (const auto& id : txsIds) { + auto it = m_transactions.find(id); + if (it == m_transactions.end()) { + missedTxs.push_back(id); + } else { + txs.push_back(it->tx); + } + } + } + +#define CURRENT_MEMPOOL_ARCHIVE_VER 10 template void serialize(archive_t & a, const unsigned int version) { @@ -74,56 +134,79 @@ namespace cryptonote { CRITICAL_REGION_LOCAL(m_transactions_lock); a & m_transactions; a & m_spent_key_images; + a & m_spentOutputs; } - struct tx_details { - transaction tx; - size_t blob_size; + struct TransactionCheckInfo { + BlockInfo maxUsedBlock; + BlockInfo lastFailedBlock; + }; + + struct TransactionDetails : public TransactionCheckInfo { + crypto::hash id; + Transaction tx; + size_t blobSize; uint64_t fee; - crypto::hash max_used_block_id; - uint64_t max_used_block_height; - bool kept_by_block; - // - uint64_t last_failed_height; - crypto::hash last_failed_id; + bool keptByBlock; + time_t receiveTime; }; private: - bool have_tx_keyimg_as_spent(const crypto::key_image& key_im) const; - bool have_tx_keyimges_as_spent(const transaction& tx) const; - bool remove_transaction_keyimages(const transaction& tx); - static bool have_key_images(const std::unordered_set& kic, const transaction& tx); - static bool append_key_images(std::unordered_set& kic, const transaction& tx); - bool is_transaction_ready_to_go(tx_details& txd) const; + struct TransactionPriorityComparator { + // lhs > hrs + bool operator()(const TransactionDetails& lhs, const TransactionDetails& rhs) const { + // price(lhs) = lhs.fee / lhs.blobSize + // price(lhs) > price(rhs) --> + // lhs.fee / lhs.blobSize > rhs.fee / rhs.blobSize --> + // lhs.fee * rhs.blobSize > rhs.fee * lhs.blobSize + uint64_t lhs_hi, lhs_lo = mul128(lhs.fee, rhs.blobSize, &lhs_hi); + uint64_t rhs_hi, rhs_lo = mul128(rhs.fee, lhs.blobSize, &rhs_hi); + + return + // prefer more profitable transactions + (lhs_hi > rhs_hi) || + (lhs_hi == rhs_hi && lhs_lo > rhs_lo) || + // prefer smaller + (lhs_hi == rhs_hi && lhs_lo == rhs_lo && lhs.blobSize < rhs.blobSize) || + // prefer older + (lhs_hi == rhs_hi && lhs_lo == rhs_lo && lhs.blobSize == rhs.blobSize && lhs.receiveTime < rhs.receiveTime); + } + }; + + typedef hashed_unique main_index_t; + typedef ordered_non_unique, TransactionPriorityComparator> fee_index_t; - typedef std::unordered_map transactions_container; + typedef multi_index_container + > tx_container_t; + + typedef std::pair GlobalOutput; + typedef std::set GlobalOutputsContainer; typedef std::unordered_map > key_images_container; + + // double spending checking + bool addTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock); + bool haveSpentInputs(const Transaction& tx) const; + bool removeTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock); + + tx_container_t::iterator removeTransaction(tx_container_t::iterator i); + bool removeExpiredTransactions(); + bool is_transaction_ready_to_go(const Transaction& tx, TransactionCheckInfo& txd) const; + + const cryptonote::Currency& m_currency; + OnceInTimeInterval m_txCheckInterval; mutable epee::critical_section m_transactions_lock; - transactions_container m_transactions; key_images_container m_spent_key_images; + GlobalOutputsContainer m_spentOutputs; std::string m_config_folder; - blockchain_storage& m_blockchain; - - /************************************************************************/ - /* */ - /************************************************************************/ - class amount_visitor: public boost::static_visitor { - public: - uint64_t operator()(const txin_to_key& tx) const { - return tx.amount; - } - - uint64_t operator()(const txin_gen& tx) const { - CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); - return 0; - } + CryptoNote::ITransactionValidator& m_validator; + CryptoNote::ITimeProvider& m_timeProvider; - uint64_t operator()(const txin_to_script& tx) const { return 0; } - uint64_t operator()(const txin_to_scripthash& tx) const { return 0; } - }; + tx_container_t m_transactions; + tx_container_t::nth_index<1>::type& m_fee_index; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) friend class blockchain_storage; @@ -134,14 +217,17 @@ namespace cryptonote { namespace boost { namespace serialization { template - void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version) { - ar & td.blob_size; + void serialize(archive_t & ar, cryptonote::tx_memory_pool::TransactionDetails& td, const unsigned int version) { + ar & td.id; + ar & td.blobSize; ar & td.fee; ar & td.tx; - ar & td.max_used_block_height; - ar & td.max_used_block_id; - ar & td.last_failed_height; - ar & td.last_failed_id; + ar & td.maxUsedBlock.height; + ar & td.maxUsedBlock.id; + ar & td.lastFailedBlock.height; + ar & td.lastFailedBlock.id; + ar & td.keptByBlock; + ar & td.receiveTime; } } } diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index 44bcc151bc..52fd1cc118 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -27,6 +27,7 @@ namespace cryptonote bool m_verifivation_failed; //bad tx, should drop connection bool m_verifivation_impossible; //the transaction is related with an alternative blockchain bool m_added_to_pool; + bool m_tx_fee_too_small; }; struct block_verification_context diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 117bd67dd5..7e28e89daa 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -45,77 +45,82 @@ namespace cryptonote /************************************************************************/ /* */ /************************************************************************/ - struct NOTIFY_NEW_BLOCK + struct NOTIFY_NEW_BLOCK_request { - const static int ID = BC_COMMANDS_POOL_BASE + 1; + block_complete_entry b; + uint64_t current_blockchain_height; + uint32_t hop; - struct request - { - block_complete_entry b; - uint64_t current_blockchain_height; - uint32_t hop; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(b) + KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE(hop) + END_KV_SERIALIZE_MAP() + }; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(b) - KV_SERIALIZE(current_blockchain_height) - KV_SERIALIZE(hop) - END_KV_SERIALIZE_MAP() - }; + struct NOTIFY_NEW_BLOCK + { + const static int ID = BC_COMMANDS_POOL_BASE + 1; + typedef NOTIFY_NEW_BLOCK_request request; }; /************************************************************************/ /* */ /************************************************************************/ - struct NOTIFY_NEW_TRANSACTIONS + struct NOTIFY_NEW_TRANSACTIONS_request { - const static int ID = BC_COMMANDS_POOL_BASE + 2; + std::list txs; - struct request - { - std::list txs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + }; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs) - END_KV_SERIALIZE_MAP() - }; + struct NOTIFY_NEW_TRANSACTIONS + { + const static int ID = BC_COMMANDS_POOL_BASE + 2; + typedef NOTIFY_NEW_TRANSACTIONS_request request; }; + /************************************************************************/ /* */ /************************************************************************/ + struct NOTIFY_REQUEST_GET_OBJECTS_request + { + std::list txs; + std::list blocks; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks) + END_KV_SERIALIZE_MAP() + }; + struct NOTIFY_REQUEST_GET_OBJECTS { const static int ID = BC_COMMANDS_POOL_BASE + 3; + typedef NOTIFY_REQUEST_GET_OBJECTS_request request; + }; - struct request - { - std::list txs; - std::list blocks; + struct NOTIFY_RESPONSE_GET_OBJECTS_request + { + std::list txs; + std::list blocks; + std::list missed_ids; + uint64_t current_blockchain_height; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks) - END_KV_SERIALIZE_MAP() - }; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + KV_SERIALIZE(blocks) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids) + KV_SERIALIZE(current_blockchain_height) + END_KV_SERIALIZE_MAP() }; struct NOTIFY_RESPONSE_GET_OBJECTS { const static int ID = BC_COMMANDS_POOL_BASE + 4; - - struct request - { - std::list txs; - std::list blocks; - std::list missed_ids; - uint64_t current_blockchain_height; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs) - KV_SERIALIZE(blocks) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids) - KV_SERIALIZE(current_blockchain_height) - END_KV_SERIALIZE_MAP() - }; + typedef NOTIFY_RESPONSE_GET_OBJECTS_request request; }; @@ -144,22 +149,23 @@ namespace cryptonote }; }; - struct NOTIFY_RESPONSE_CHAIN_ENTRY + struct NOTIFY_RESPONSE_CHAIN_ENTRY_request { - const static int ID = BC_COMMANDS_POOL_BASE + 7; + uint64_t start_height; + uint64_t total_height; + std::list m_block_ids; - struct request - { - uint64_t start_height; - uint64_t total_height; - std::list m_block_ids; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(start_height) + KV_SERIALIZE(total_height) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) + END_KV_SERIALIZE_MAP() + }; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(start_height) - KV_SERIALIZE(total_height) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) - END_KV_SERIALIZE_MAP() - }; + struct NOTIFY_RESPONSE_CHAIN_ENTRY + { + const static int ID = BC_COMMANDS_POOL_BASE + 7; + typedef NOTIFY_RESPONSE_CHAIN_ENTRY_request request; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 8b53121d15..bcbcf968e6 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -123,7 +123,7 @@ namespace cryptonote int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height - << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / DIFFICULTY_TARGET) << " days) " + << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " << (0 <= diff ? std::string("behind") : std::string("ahead")) << "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1)); LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); @@ -153,50 +153,42 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) - { + template + int t_cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"); - if(context.m_state != cryptonote_connection_context::state_normal) + if (context.m_state != cryptonote_connection_context::state_normal) { return 1; + } - for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) - { + for (auto tx_blob_it = arg.b.txs.begin(); tx_blob_it != arg.b.txs.end(); tx_blob_it++) { cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); m_core.handle_incoming_tx(*tx_blob_it, tvc, true); - if(tvc.m_verifivation_failed) - { + if (tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L0("Block verification failed: transaction verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } } - block_verification_context bvc = boost::value_initialized(); - m_core.pause_mine(); - m_core.handle_incoming_block(arg.b.block, bvc); - m_core.resume_mine(); - if(bvc.m_verifivation_failed) - { + m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); + if (bvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } - if(bvc.m_added_to_main_chain) - { + if (bvc.m_added_to_main_chain) { ++arg.hop; //TODO: Add here announce protocol usage relay_block(arg, context); - }else if(bvc.m_marked_as_orphaned) - { + } else if (bvc.m_marked_as_orphaned) { context.m_state = cryptonote_connection_context::state_synchronizing; NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); m_core.get_short_chain_history(r.block_ids); - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size()); post_notify(r, context); } - + return 1; } //------------------------------------------------------------------------------------------------------------------------ @@ -266,14 +258,14 @@ namespace cryptonote BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) { ++count; - block b; + Block b; if(!parse_and_validate_block_from_blob(block_entry.block, b)) { LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); m_p2p->drop_connection(context); return 1; - } + } //to avoid concurrency in core between connections, suspend connections which delivered block later then first one if(count == 2) { @@ -286,7 +278,7 @@ namespace cryptonote return 1; } } - + auto req_it = context.m_requested_objects.find(get_block_hash(b)); if(req_it == context.m_requested_objects.end()) { @@ -295,10 +287,10 @@ namespace cryptonote m_p2p->drop_connection(context); return 1; } - if(b.tx_hashes.size() != block_entry.txs.size()) + if (b.txHashes.size() != block_entry.txs.size()) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) - << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); + << ", txHashes.size()=" << b.txHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); m_p2p->drop_connection(context); return 1; } @@ -315,20 +307,17 @@ namespace cryptonote } { - m_core.pause_mine(); + m_core.pause_mining(); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( - boost::bind(&t_core::resume_mine, &m_core)); + boost::bind(&t_core::update_block_template_and_resume_mining, &m_core)); - BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) - { + for (const block_complete_entry& block_entry : arg.blocks) { //process transactions TIME_MEASURE_START(transactions_process_time); - BOOST_FOREACH(auto& tx_blob, block_entry.txs) - { + for (auto& tx_blob : block_entry.txs) { tx_verification_context tvc = AUTO_VAL_INIT(tvc); m_core.handle_incoming_tx(tx_blob, tvc, true); - if(tvc.m_verifivation_failed) - { + if (tvc.m_verifivation_failed) { LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); m_p2p->drop_connection(context); @@ -340,24 +329,21 @@ namespace cryptonote //process block TIME_MEASURE_START(block_process_time); block_verification_context bvc = boost::value_initialized(); + m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); - m_core.handle_incoming_block(block_entry.block, bvc, false); - - if(bvc.m_verifivation_failed) - { + if (bvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; - } - if(bvc.m_marked_as_orphaned) - { + } else if (bvc.m_marked_as_orphaned) { LOG_PRINT_CCONTEXT_L0("Block received at sync phase was marked as orphaned, dropping connection"); m_p2p->drop_connection(context); return 1; } TIME_MEASURE_FINISH(block_process_time); - LOG_PRINT_CCONTEXT_L2("Block process time: " << block_process_time + transactions_process_time << "(" << transactions_process_time << "/" << block_process_time << ")ms"); + LOG_PRINT_CCONTEXT_L2("Block process time: " << block_process_time + transactions_process_time << + " (" << transactions_process_time << " / " << block_process_time << ") ms"); } } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index dcd7dad3e8..f85d512f04 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -18,33 +18,32 @@ #pragma once #include "p2p/net_node_common.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_core/connection_context.h" + namespace cryptonote { + struct NOTIFY_NEW_BLOCK_request; + struct NOTIFY_NEW_TRANSACTIONS_request; + /************************************************************************/ /* */ /************************************************************************/ - struct i_cryptonote_protocol - { - virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0; - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0; + struct i_cryptonote_protocol { + virtual bool relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context)=0; + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context)=0; //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; }; /************************************************************************/ /* */ /************************************************************************/ - struct cryptonote_protocol_stub: public i_cryptonote_protocol - { - virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) - { + struct cryptonote_protocol_stub: public i_cryptonote_protocol { + virtual bool relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context) { return false; } - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) - { + + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context) { return false; } - }; } diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index d3465b49f8..3881b64cf8 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -26,14 +26,17 @@ using namespace epee; #include -#include "crypto/hash.h" +// epee #include "console_handler.h" -#include "p2p/net_node.h" -#include "cryptonote_core/checkpoints_create.h" + +#include "common/SignalHandler.h" +#include "crypto/hash.h" #include "cryptonote_core/cryptonote_core.h" -#include "rpc/core_rpc_server.h" +#include "cryptonote_core/Currency.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "daemon_commands_handler.h" +#include "daemon/daemon_commands_handler.h" +#include "p2p/net_node.h" +#include "rpc/core_rpc_server.h" #include "version.h" #if defined(WIN32) @@ -44,11 +47,13 @@ namespace po = boost::program_options; namespace { - const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(CRYPTONOTE_NAME ".conf")}; + const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(cryptonote::CRYPTONOTE_NAME) + ".conf"}; const command_line::arg_descriptor arg_os_version = {"os-version", ""}; const command_line::arg_descriptor arg_log_file = {"log-file", "", ""}; const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; const command_line::arg_descriptor arg_console = {"no-console", "Disable daemon console commands"}; + const command_line::arg_descriptor arg_testnet_on = {"testnet", "Used to deploy test nets. Checkpoints and hardcoded seeds are ignored, " + "network id is changed. Use it with --data-dir flag. The wallet must be launched with --testnet flag.", false}; } bool command_line_preprocessor(const boost::program_options::variables_map& vm); @@ -79,7 +84,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_log_file); command_line::add_arg(desc_cmd_sett, arg_log_level); command_line::add_arg(desc_cmd_sett, arg_console); - + command_line::add_arg(desc_cmd_sett, arg_testnet_on); cryptonote::core::init_options(desc_cmd_sett); cryptonote::core_rpc_server::init_options(desc_cmd_sett); @@ -96,7 +101,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; + std::cout << cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; std::cout << desc_options << std::endl; return false; } @@ -131,7 +136,7 @@ int main(int argc, char* argv[]) log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder(); log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str()); - LOG_PRINT_L0(CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG); + LOG_PRINT_L0(cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG); if (command_line_preprocessor(vm)) { @@ -140,14 +145,26 @@ int main(int argc, char* argv[]) LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0); - bool res = true; - cryptonote::checkpoints checkpoints; - res = cryptonote::create_checkpoints(checkpoints); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize checkpoints"); + bool testnet_mode = command_line::get_arg(vm, arg_testnet_on); + if (testnet_mode) { + LOG_PRINT_L0("Starting in testnet mode!"); + } //create objects and link them - cryptonote::core ccore(NULL); - ccore.set_checkpoints(std::move(checkpoints)); + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.testnet(testnet_mode); + cryptonote::Currency currency = currencyBuilder.currency(); + cryptonote::core ccore(currency, NULL); + + cryptonote::checkpoints checkpoints; + for (const auto& cp : cryptonote::CHECKPOINTS) { + checkpoints.add_checkpoint(cp.height, cp.blockId); + } + + if (!testnet_mode) { + ccore.set_checkpoints(std::move(checkpoints)); + } + cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); nodetool::node_server > p2psrv(cprotocol); cryptonote::core_rpc_server rpc_server(ccore, p2psrv); @@ -157,7 +174,7 @@ int main(int argc, char* argv[]) //initialize objects LOG_PRINT_L0("Initializing p2p server..."); - res = p2psrv.init(vm); + bool res = p2psrv.init(vm, testnet_mode); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); @@ -188,7 +205,7 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); LOG_PRINT_L0("Core rpc server started ok"); - tools::signal_handler::install([&dch, &p2psrv] { + tools::SignalHandler::install([&dch, &p2psrv] { dch.stop_handling(); p2psrv.send_stop_signal(); }); @@ -227,7 +244,7 @@ bool command_line_preprocessor(const boost::program_options::variables_map& vm) bool exit = false; if (command_line::get_arg(vm, command_line::arg_version)) { - std::cout << CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; + std::cout << cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; exit = true; } if (command_line::get_arg(vm, arg_os_version)) diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index 7a6625aeb0..837839aafe 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -21,6 +21,7 @@ #include "console_handler.h" #include "p2p/net_node.h" +#include "cryptonote_core/miner.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "common/util.h" #include "crypto/hash.h" @@ -47,7 +48,6 @@ class daemon_cmmands_handler m_cmd_binder.set_handler("print_pool_sh", boost::bind(&daemon_cmmands_handler::print_pool_sh, this, _1), "Print transaction pool (short format)"); m_cmd_binder.set_handler("show_hr", boost::bind(&daemon_cmmands_handler::show_hr, this, _1), "Start showing hash rate"); m_cmd_binder.set_handler("hide_hr", boost::bind(&daemon_cmmands_handler::hide_hr, this, _1), "Stop showing hash rate"); - m_cmd_binder.set_handler("save", boost::bind(&daemon_cmmands_handler::save, this, _1), "Save blockchain"); m_cmd_binder.set_handler("set_log", boost::bind(&daemon_cmmands_handler::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); } @@ -69,7 +69,7 @@ class daemon_cmmands_handler std::string get_commands_str() { std::stringstream ss; - ss << CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; + ss << cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; ss << "Commands: " << ENDL; std::string usage = m_cmd_binder.get_usage(); boost::replace_all(usage, "\n", "\n "); @@ -90,12 +90,6 @@ class daemon_cmmands_handler return true; } //-------------------------------------------------------------------------------- - bool save(const std::vector& args) - { - m_srv.get_payload_object().get_core().get_blockchain_storage().store_blockchain(); - return true; - } - //-------------------------------------------------------------------------------- bool show_hr(const std::vector& args) { if(!m_srv.get_payload_object().get_core().get_miner().is_mining()) @@ -204,22 +198,20 @@ class daemon_cmmands_handler //-------------------------------------------------------------------------------- template - static bool print_as_json(T& obj) - { + static bool print_as_json(const T& obj) { std::cout << cryptonote::obj_to_json_str(obj) << ENDL; return true; } //-------------------------------------------------------------------------------- bool print_block_by_height(uint64_t height) { - std::list blocks; + std::list blocks; m_srv.get_payload_object().get_core().get_blocks(height, 1, blocks); if (1 == blocks.size()) { - cryptonote::block& block = blocks.front(); - std::cout << "block_id: " << get_block_hash(block) << ENDL; - print_as_json(block); + std::cout << "block_id: " << get_block_hash(blocks.front()) << ENDL; + print_as_json(blocks.front()); } else { @@ -243,14 +235,13 @@ class daemon_cmmands_handler std::list block_ids; block_ids.push_back(block_hash); - std::list blocks; + std::list blocks; std::list missed_ids; m_srv.get_payload_object().get_core().get_blocks(block_ids, blocks, missed_ids); if (1 == blocks.size()) { - cryptonote::block block = blocks.front(); - print_as_json(block); + print_as_json(blocks.front()); } else { @@ -300,14 +291,13 @@ class daemon_cmmands_handler std::vector tx_ids; tx_ids.push_back(tx_hash); - std::list txs; + std::list txs; std::list missed_ids; m_srv.get_payload_object().get_core().get_transactions(tx_ids, txs, missed_ids); if (1 == txs.size()) { - cryptonote::transaction tx = txs.front(); - print_as_json(tx); + print_as_json(txs.front()); } else { @@ -336,8 +326,8 @@ class daemon_cmmands_handler return true; } - cryptonote::account_public_address adr; - if(!cryptonote::get_account_address_from_str(adr, args.front())) + cryptonote::AccountPublicAddress adr; + if(!m_srv.get_payload_object().get_core().currency().parseAccountAddressString(args.front(), adr)) { std::cout << "target account address has wrong format" << std::endl; return true; @@ -350,7 +340,7 @@ class daemon_cmmands_handler } boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); + attrs.set_stack_size(cryptonote::THREAD_STACK_SIZE); m_srv.get_payload_object().get_core().get_miner().start(adr, threads_count, attrs); return true; diff --git a/src/node_rpc_proxy/NodeRpcProxy.cpp b/src/node_rpc_proxy/NodeRpcProxy.cpp index c024a8b7da..1e434d9d23 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.cpp +++ b/src/node_rpc_proxy/NodeRpcProxy.cpp @@ -181,7 +181,7 @@ uint64_t NodeRpcProxy::getLastKnownBlockHeight() const { return m_networkHeight; } -void NodeRpcProxy::relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) { +void NodeRpcProxy::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { if (!m_initState.initialized()) { callback(make_error_code(error::NOT_INITIALIZED)); return; @@ -218,7 +218,7 @@ void NodeRpcProxy::getTransactionOutsGlobalIndices(const crypto::hash& transacti m_ioService.post(std::bind(&NodeRpcProxy::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback)); } -void NodeRpcProxy::doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback) { +void NodeRpcProxy::doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { COMMAND_RPC_SEND_RAW_TX::request req; COMMAND_RPC_SEND_RAW_TX::response rsp; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(transaction)); diff --git a/src/node_rpc_proxy/NodeRpcProxy.h b/src/node_rpc_proxy/NodeRpcProxy.h index db5faaa5c6..158fdf1e98 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.h +++ b/src/node_rpc_proxy/NodeRpcProxy.h @@ -45,7 +45,7 @@ class NodeRpcProxy : public CryptoNote::INode { virtual uint64_t getLastLocalBlockHeight() const; virtual uint64_t getLastKnownBlockHeight() const; - virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); @@ -61,7 +61,7 @@ class NodeRpcProxy : public CryptoNote::INode { void updateNodeStatus(); void updatePeerCount(); - void doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + void doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); void doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); void doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index e844b42989..a8356eafe7 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -66,13 +66,13 @@ namespace nodetool public: typedef t_payload_net_handler payload_net_handler; // Some code - node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false) + node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false), m_network_id(BYTECOIN_NETWORK) {} static void init_options(boost::program_options::options_description& desc); bool run(); - bool init(const boost::program_options::variables_map& vm); + bool init(const boost::program_options::variables_map& vm, bool testnet); bool deinit(); bool send_stop_signal(); uint32_t get_this_peer_port(){return m_listenning_port;} @@ -200,7 +200,7 @@ namespace nodetool t_payload_net_handler& m_payload_handler; peerlist_manager m_peerlist; - epee::math_helper::once_a_time_seconds m_peer_handshake_idle_maker_interval; + epee::math_helper::once_a_time_seconds m_peer_handshake_idle_maker_interval; 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; @@ -216,6 +216,7 @@ namespace nodetool uint64_t m_peer_livetime; //keep connections to initiate some interactions net_server m_net_server; + boost::uuids::uuid m_network_id; }; } diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index d56a83519d..87346fdbdf 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -40,7 +40,7 @@ namespace nodetool namespace { const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; - const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(cryptonote::P2P_DEFAULT_PORT)}; const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; @@ -63,14 +63,15 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_add_priority_node); command_line::add_arg(desc, arg_p2p_add_exclusive_node); command_line::add_arg(desc, arg_p2p_seed_node); - command_line::add_arg(desc, arg_p2p_hide_my_port); } + command_line::add_arg(desc, arg_p2p_hide_my_port); + } //----------------------------------------------------------------------------------- template bool node_server::init_config() { // TRY_ENTRY(); - std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; + std::string state_file_path = m_config_folder + "/" + cryptonote::parameters::P2P_NET_DATA_FILENAME; std::ifstream p2p_data; p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::in); if(!p2p_data.fail()) @@ -83,13 +84,13 @@ namespace nodetool } //at this moment we have hardcoded config - m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL; - m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; - m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit + m_config.m_net_config.handshake_interval = cryptonote::P2P_DEFAULT_HANDSHAKE_INTERVAL; + m_config.m_net_config.connections_count = cryptonote::P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.packet_max_size = cryptonote::P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit m_config.m_net_config.config_id = 0; // initial config - m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT; - m_config.m_net_config.ping_connection_timeout = P2P_DEFAULT_PING_CONNECTION_TIMEOUT; - m_config.m_net_config.send_peerlist_sz = P2P_DEFAULT_PEERS_IN_HANDSHAKE; + m_config.m_net_config.connection_timeout = cryptonote::P2P_DEFAULT_CONNECTION_TIMEOUT; + m_config.m_net_config.ping_connection_timeout = cryptonote::P2P_DEFAULT_PING_CONNECTION_TIMEOUT; + m_config.m_net_config.send_peerlist_sz = cryptonote::P2P_DEFAULT_PEERS_IN_HANDSHAKE; m_first_connection_maker_call = true; CATCH_ENTRY_L0("node_server::init_config", false); @@ -201,21 +202,16 @@ namespace nodetool } } - #define ADD_HARDCODED_SEED_NODE(addr) append_net_address(m_seed_nodes, addr); //----------------------------------------------------------------------------------- template - bool node_server::init(const boost::program_options::variables_map& vm) - { - ADD_HARDCODED_SEED_NODE("seed.bytecoin.org:8080"); - ADD_HARDCODED_SEED_NODE("85.25.201.95:8080"); - ADD_HARDCODED_SEED_NODE("85.25.196.145:8080"); - ADD_HARDCODED_SEED_NODE("85.25.196.146:8080"); - ADD_HARDCODED_SEED_NODE("85.25.196.144:8080"); - ADD_HARDCODED_SEED_NODE("5.199.168.138:8080"); - ADD_HARDCODED_SEED_NODE("62.75.236.152:8080"); - ADD_HARDCODED_SEED_NODE("85.25.194.245:8080"); - ADD_HARDCODED_SEED_NODE("95.211.224.160:8080"); - ADD_HARDCODED_SEED_NODE("144.76.200.44:8080"); + bool node_server::init(const boost::program_options::variables_map& vm, bool testnet) { + if (!testnet) { + for (auto seed : cryptonote::SEED_NODES) { + append_net_address(m_seed_nodes, seed); + } + } else { + m_network_id.data[0] += 1; + } bool res = handle_command_line(vm); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); @@ -239,7 +235,7 @@ namespace nodetool //configure self m_net_server.set_threads_prefix("P2P"); m_net_server.get_config_object().m_pcommands_handler = this; - m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + m_net_server.get_config_object().m_invoke_timeout = cryptonote::P2P_DEFAULT_INVOKE_TIMEOUT; //try to bind LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); @@ -264,7 +260,8 @@ namespace nodetool if (result == 1) { std::ostringstream portString; portString << m_listenning_port; - if (UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0") != 0) { + if (UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), + portString.str().c_str(), lanAddress, cryptonote::CRYPTONOTE_NAME, "TCP", 0, "0") != 0) { LOG_ERROR("UPNP_AddPortMapping failed."); } else { LOG_PRINT_GREEN("Added IGD port mapping.", LOG_LEVEL_0); @@ -301,7 +298,7 @@ namespace nodetool m_net_server.add_idle_handler(boost::bind(&t_payload_net_handler::on_idle, &m_payload_handler), 1000); boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); + attrs.set_stack_size(cryptonote::THREAD_STACK_SIZE); //go to loop LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); @@ -340,7 +337,7 @@ namespace nodetool return false; } - std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; + std::string state_file_path = m_config_folder + "/" + cryptonote::parameters::P2P_NET_DATA_FILENAME; std::ofstream p2p_data; p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); if(p2p_data.fail()) @@ -353,8 +350,6 @@ namespace nodetool a << *this; return true; CATCH_ENTRY_L0("blockchain_storage::save", false); - - return true; } //----------------------------------------------------------------------------------- template @@ -389,7 +384,7 @@ namespace nodetool return; } - if(rsp.node_data.network_id != BYTECOIN_NETWORK) + if(rsp.node_data.network_id != m_network_id) { LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); return; @@ -424,7 +419,7 @@ namespace nodetool { LOG_PRINT_CCONTEXT_L1(" COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); } - }, P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); + }, cryptonote::P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); if(r) { @@ -667,7 +662,7 @@ namespace nodetool if (!connect_to_peerlist(m_priority_peers)) return false; - size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + size_t expected_white_connections = (m_config.m_net_config.connections_count * cryptonote::P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT) / 100; size_t conn_count = get_outgoing_connections_count(); if(conn_count < m_config.m_net_config.connections_count) @@ -797,7 +792,7 @@ namespace nodetool node_data.my_port = m_external_port ? m_external_port : m_listenning_port; else node_data.my_port = 0; - node_data.network_id = BYTECOIN_NETWORK; + node_data.network_id = m_network_id; return true; } //----------------------------------------------------------------------------------- @@ -823,7 +818,7 @@ namespace nodetool return false; } crypto::public_key pk = AUTO_VAL_INIT(pk); - epee::string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pk); + epee::string_tools::hex_to_pod(cryptonote::P2P_STAT_TRUSTED_PUB_KEY, pk); crypto::hash h = tools::get_proof_of_trust_hash(tr); if(!crypto::check_signature(h, pk, tr.sign)) { @@ -1017,9 +1012,8 @@ namespace nodetool template int node_server::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { - if(arg.node_data.network_id != BYTECOIN_NETWORK) + if(arg.node_data.network_id != m_network_id) { - LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); drop_connection(context); return 1; diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index d1380f8961..d86c3609e5 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -19,13 +19,13 @@ #include #include "net/net_utils_base.h" -#include "p2p_protocol_defs.h" namespace nodetool { typedef boost::uuids::uuid uuid; typedef boost::uuids::uuid net_connection_id; + typedef uint64_t peerid_type; template struct i_p2p_endpoint diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index a57a1c0e5b..ba4bc0c134 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -56,7 +56,7 @@ namespace nodetool 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_head(std::list& bs_head, uint32_t depth = cryptonote::P2P_DEFAULT_PEERS_IN_HANDSHAKE); bool get_peerlist_full(std::list& pl_gray, std::list& 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); @@ -193,7 +193,7 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- inline void peerlist_manager::trim_white_peerlist() { - while(m_peers_gray.size() > P2P_LOCAL_GRAY_PEERLIST_LIMIT) + while(m_peers_gray.size() > cryptonote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) { peers_indexed::index::type& sorted_index=m_peers_gray.get(); sorted_index.erase(sorted_index.begin()); @@ -202,7 +202,7 @@ namespace nodetool //-------------------------------------------------------------------------------------------------- inline void peerlist_manager::trim_gray_peerlist() { - while(m_peers_white.size() > P2P_LOCAL_WHITE_PEERLIST_LIMIT) + while(m_peers_white.size() > cryptonote::P2P_LOCAL_WHITE_PEERLIST_LIMIT) { peers_indexed::index::type& sorted_index=m_peers_white.get(); sorted_index.erase(sorted_index.begin()); diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index c0f6efeef4..46213dfb5b 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -18,8 +18,12 @@ #pragma once #include + #include "serialization/keyvalue_serialization.h" #include "misc_language.h" +#include "string_tools.h" +#include "time_helper.h" + #include "cryptonote_config.h" #include "crypto/crypto.h" diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index da65ebc932..641fda8fa8 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -15,18 +15,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include +#include "core_rpc_server.h" + #include "include_base_utils.h" -using namespace epee; +#include "misc_language.h" -#include "core_rpc_server.h" #include "common/command_line.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "misc_language.h" #include "crypto/hash.h" -#include "core_rpc_server_error_codes.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/miner.h" +#include "rpc/core_rpc_server_error_codes.h" + +using namespace epee; namespace cryptonote { @@ -104,19 +105,17 @@ namespace cryptonote bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx) { CHECK_CORE_READY(); - std::list > > bs; + std::list > > bs; if(!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { res.status = "Failed"; return false; } - BOOST_FOREACH(auto& b, bs) - { + for (auto& b : bs) { res.blocks.resize(res.blocks.size()+1); res.blocks.back().block = block_to_blob(b.first); - BOOST_FOREACH(auto& t, b.second) - { + for (auto& t : b.second) { res.blocks.back().txs.push_back(tx_to_blob(t)); } } @@ -124,6 +123,81 @@ namespace cryptonote res.status = CORE_RPC_STATUS_OK; return true; } + + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res, connection_context& cntx) + { + CHECK_CORE_READY(); + + typedef COMMAND_RPC_QUERY_BLOCKS::response_item ResponseItem; + + LockedBlockchainStorage lbs(m_core.get_blockchain_storage()); + + uint64_t currentHeight = lbs->get_current_blockchain_height(); + uint64_t startOffset = 0; + + if (!lbs->find_blockchain_supplement(req.block_ids, startOffset)) { + res.status = "Failed to find blockchain supplement"; + return false; + } + + uint64_t startFullOffset = 0; + + if (!lbs->getLowerBound(req.timestamp, startOffset, startFullOffset)) + startFullOffset = startOffset; + + res.full_offset = startFullOffset; + + if (startOffset != startFullOffset) { + std::list blockIds; + if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { + res.status = "Failed to get block ids"; + return false; + } + + for (const auto& id : blockIds) { + res.items.push_back(ResponseItem()); + res.items.back().block_id = id; + } + } + + auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - res.items.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); + + if (blocksLeft) { + std::list blocks; + lbs->get_blocks(startFullOffset, blocksLeft, blocks); + + for (auto& b : blocks) { + + ResponseItem item; + + item.block_id = get_block_hash(b); + + if (b.timestamp >= req.timestamp) { + // query transactions + std::list txs; + std::list missedTxs; + lbs->get_transactions(b.txHashes, txs, missedTxs); + + // fill data + block_complete_entry& completeEntry = item; + completeEntry.block = block_to_blob(b); + for (auto& tx : txs) { + completeEntry.txs.push_back(tx_to_blob(tx)); + } + } + + res.items.push_back(std::move(item)); + } + } + + res.current_height = currentHeight; + res.start_height = startOffset; + + res.status = CORE_RPC_STATUS_OK; + 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, connection_context& cntx) { @@ -172,8 +246,7 @@ namespace cryptonote { CHECK_CORE_READY(); std::vector vh; - BOOST_FOREACH(const auto& tx_hex_str, req.txs_hashes) - { + for (const auto& tx_hex_str : req.txs_hashes) { blobdata b; if(!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) { @@ -187,17 +260,15 @@ namespace cryptonote vh.push_back(*reinterpret_cast(b.data())); } std::list missed_txs; - std::list txs; + std::list txs; m_core.get_transactions(vh, txs, missed_txs); - BOOST_FOREACH(auto& tx, txs) - { + for (auto& tx : txs) { blobdata blob = t_serializable_object_to_blob(tx); res.txs_as_hex.push_back(string_tools::buff_to_hex_nodelimer(blob)); } - BOOST_FOREACH(const auto& miss_tx, missed_txs) - { + for (const auto& miss_tx : missed_txs) { res.missed_tx.push_back(string_tools::pod_to_hex(miss_tx)); } @@ -252,8 +323,8 @@ namespace cryptonote bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx) { CHECK_CORE_READY(); - account_public_address adr; - if(!get_account_address_from_str(adr, req.miner_address)) + AccountPublicAddress adr; + if(!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { res.status = "Failed, wrong address"; return true; @@ -346,16 +417,16 @@ namespace cryptonote return false; } - cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + cryptonote::AccountPublicAddress acc = AUTO_VAL_INIT(acc); - if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(acc, req.wallet_address)) + if(!req.wallet_address.size() || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS; error_resp.message = "Failed to parse wallet address"; return false; } - block b = AUTO_VAL_INIT(b); + Block b = AUTO_VAL_INIT(b); cryptonote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) @@ -367,7 +438,7 @@ namespace cryptonote } blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.minerTx); if(tx_pub_key == null_pkey) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -402,33 +473,37 @@ namespace cryptonote res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); res.status = CORE_RPC_STATUS_OK; + return true; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) + bool core_rpc_server::on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& /*req*/, COMMAND_RPC_GET_CURRENCY_ID::response& res, epee::json_rpc::error& error_resp, connection_context& /*cntx*/) { + crypto::hash currencyId = m_core.currency().genesisBlockHash(); + blobdata blob = t_serializable_object_to_blob(currencyId); + res.currency_id_blob = string_tools::buff_to_hex_nodelimer(blob); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { CHECK_CORE_READY(); - if(req.size()!=1) - { + if (req.size() != 1) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; error_resp.message = "Wrong param"; return false; } + blobdata blockblob; - if(!string_tools::parse_hexstr_to_binbuff(req[0], blockblob)) - { + if (!string_tools::parse_hexstr_to_binbuff(req[0], blockblob)) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; error_resp.message = "Wrong block blob"; return false; } - cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_core.handle_incoming_block(blockblob, bvc); - if (bvc.m_added_to_main_chain){ - block b = AUTO_VAL_INIT(b); - parse_and_validate_block_from_blob(blockblob, b); - m_core.notify_new_block(b); - } else { + cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); + m_core.handle_incoming_block_blob(blockblob, bvc, true, true); + if (!bvc.m_added_to_main_chain) { error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED; error_resp.message = "Block not accepted"; return false; @@ -438,22 +513,22 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ - uint64_t core_rpc_server::get_block_reward(const block& blk) - { - uint64_t reward = 0; - BOOST_FOREACH(const tx_out& out, blk.miner_tx.vout) - { - reward += out.amount; + namespace { + uint64_t get_block_reward(const Block& blk) { + uint64_t reward = 0; + for (const TransactionOutput& out : blk.minerTx.vout) { + reward += out.amount; + } + return reward; } - return reward; } //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::fill_block_header_responce(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce) + bool core_rpc_server::fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce) { - responce.major_version = blk.major_version; - responce.minor_version = blk.minor_version; + responce.major_version = blk.majorVersion; + responce.minor_version = blk.minorVersion; responce.timestamp = blk.timestamp; - responce.prev_hash = string_tools::pod_to_hex(blk.prev_id); + responce.prev_hash = string_tools::pod_to_hex(blk.prevId); responce.nonce = blk.nonce; responce.orphan_status = orphan_status; responce.height = height; @@ -481,7 +556,7 @@ namespace cryptonote error_resp.message = "Internal error: can't get last block hash."; return false; } - block last_block; + Block last_block; bool have_last_block = m_core.get_block_by_hash(last_block_hash, last_block); if (!have_last_block) { @@ -515,7 +590,7 @@ namespace cryptonote error_resp.message = "Failed to parse hex representation of block hash. Hex = " + req.hash + '.'; return false; } - block blk; + Block blk; bool have_block = m_core.get_block_by_hash(block_hash, blk); if (!have_block) { @@ -523,13 +598,13 @@ namespace cryptonote error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.'; return false; } - if (blk.miner_tx.vin.front().type() != typeid(txin_gen)) + if (blk.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; error_resp.message = "Internal error: coinbase transaction in the block has the wrong type"; return false; } - uint64_t block_height = boost::get(blk.miner_tx.vin.front()).height; + uint64_t block_height = boost::get(blk.minerTx.vin.front()).height; bool responce_filled = fill_block_header_responce(blk, false, block_height, block_hash, res.block_header); if (!responce_filled) { @@ -555,7 +630,7 @@ namespace cryptonote return false; } crypto::hash block_hash = m_core.get_block_id_by_height(req.height); - block blk; + Block blk; bool have_block = m_core.get_block_by_hash(block_hash, blk); if (!have_block) { diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 135733d34c..bdb9dbe70f 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -47,7 +47,8 @@ namespace cryptonote BEGIN_URI_MAP2() MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT) MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST) - MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) + MAP_URI_AUTO_BIN2("/queryblocks.bin", on_query_blocks, COMMAND_RPC_QUERY_BLOCKS) + MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) @@ -58,6 +59,7 @@ namespace cryptonote MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) + MAP_JON_RPC_WE("getcurrencyid", on_get_currency_id, COMMAND_RPC_GET_CURRENCY_ID) MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK) MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER) MAP_JON_RPC_WE("getblockheaderbyhash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH) @@ -67,6 +69,7 @@ namespace cryptonote bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx); bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx); + bool on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res, connection_context& cntx); bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx); bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx); bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx); @@ -79,6 +82,7 @@ namespace cryptonote bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx); bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); + bool on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& req, COMMAND_RPC_GET_CURRENCY_ID::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); 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, connection_context& cntx); 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, connection_context& cntx); @@ -88,8 +92,7 @@ namespace cryptonote bool check_core_ready(); //utils - uint64_t get_block_reward(const block& blk); - bool fill_block_header_responce(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce); + bool fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce); core& m_core; nodetool::node_server >& m_p2p; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 1b9c5d9f9f..a9b8e912d1 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -124,46 +124,52 @@ namespace cryptonote }; }; //----------------------------------------------- - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request { - struct request - { - std::vector amounts; - uint64_t outs_count; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amounts) - KV_SERIALIZE(outs_count) - END_KV_SERIALIZE_MAP() - }; + 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; - }; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_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; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_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() - }; + 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; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(outs) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response + { + std::vector outs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS + { + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request request; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response response; + + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry out_entry; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount outs_for_amount; }; //----------------------------------------------- struct COMMAND_RPC_SEND_RAW_TX @@ -173,7 +179,7 @@ namespace cryptonote std::string tx_as_hex; request() {} - explicit request(const transaction &); + explicit request(const Transaction &); BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_as_hex) @@ -331,6 +337,24 @@ namespace cryptonote }; }; + struct COMMAND_RPC_GET_CURRENCY_ID + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string currency_id_blob; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(currency_id_blob) + END_KV_SERIALIZE_MAP() + }; + }; + struct COMMAND_RPC_SUBMITBLOCK { typedef std::vector request; @@ -443,4 +467,47 @@ namespace cryptonote }; + struct COMMAND_RPC_QUERY_BLOCKS + { + struct request + { + 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 timestamp; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + KV_SERIALIZE(timestamp) + END_KV_SERIALIZE_MAP() + }; + + struct response_item : public block_complete_entry + { + crypto::hash block_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB(block_id) + KV_SERIALIZE(block) + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + + }; + + struct response + { + std::string status; + uint64_t start_height; + uint64_t current_height; + uint64_t full_offset; + + std::list items; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(start_height) + KV_SERIALIZE(current_height) + KV_SERIALIZE(full_offset) + KV_SERIALIZE(items) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 785a630ad7..51f93450ba 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -19,15 +19,19 @@ #include #include #include + +// epee #include "include_base_utils.h" +#include "storages/http_abstract_invoke.h" + #include "common/command_line.h" +#include "common/SignalHandler.h" #include "common/util.h" -#include "p2p/net_node.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "simplewallet.h" #include "cryptonote_core/cryptonote_format_utils.h" -#include "storages/http_abstract_invoke.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "simplewallet.h" #include "wallet/wallet_rpc_server.h" #include "version.h" @@ -53,6 +57,7 @@ namespace const command_line::arg_descriptor arg_password = {"password", "Wallet password", "", true}; const command_line::arg_descriptor arg_daemon_port = {"daemon-port", "Use daemon instance at port instead of 8081", 0}; const command_line::arg_descriptor arg_log_level = {"set_log", "", 0, true}; + const command_line::arg_descriptor arg_testnet = {"testnet", "Used to deploy test nets. The daemon must be launched with --testnet flag", false}; const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; @@ -155,6 +160,123 @@ namespace { return message_writer(epee::log_space::console_color_red, true, "Error: ", LOG_LEVEL_0); } + + + template + class ArgumentReader { + public: + + ArgumentReader(IterT begin, IterT end) : + m_begin(begin), m_end(end), m_cur(begin) {} + + bool eof() const { + return m_cur == m_end; + } + + ValueT next() { + if (eof()) { + throw std::runtime_error("unexpected end of arguments"); + } + + return *m_cur++; + } + + private: + + IterT m_cur; + IterT m_begin; + IterT m_end; + }; + + struct TransferCommand { + const cryptonote::Currency& m_currency; + size_t fake_outs_count; + vector dsts; + std::vector extra; + uint64_t fee; + + TransferCommand(const cryptonote::Currency& currency) : + m_currency(currency), fake_outs_count(0), fee(currency.minimumFee()) { + } + + bool parseArguments(const std::vector &args) { + + ArgumentReader::const_iterator> ar(args.begin(), args.end()); + + try { + + auto mixin_str = ar.next(); + + if (!epee::string_tools::get_xtype_from_string(fake_outs_count, mixin_str)) { + fail_msg_writer() << "mixin_count should be non-negative integer, got " << mixin_str; + return false; + } + + while (!ar.eof()) { + + auto arg = ar.next(); + + if (arg.size() && arg[0] == '-') { + + const auto& value = ar.next(); + + if (arg == "-p") { + crypto::hash payment_id; + bool r = tools::wallet2::parse_payment_id(value, payment_id); + if (r) { + 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); + } + + if (!r) { + fail_msg_writer() << "payment id has invalid format: \"" << value << "\", expected 64-character string"; + return false; + } + } else if (arg == "-f") { + bool ok = m_currency.parseAmount(value, fee); + if (!ok) { + fail_msg_writer() << "Fee value is invalid: " << value; + return false; + } + + if (fee < m_currency.minimumFee()) { + fail_msg_writer() << "Fee value is less than minimum: " << m_currency.minimumFee(); + return false; + } + } + } else { + cryptonote::tx_destination_entry de; + + if (!m_currency.parseAccountAddressString(arg, de.addr)) { + fail_msg_writer() << "wrong address: " << arg; + return false; + } + + auto value = ar.next(); + bool ok = m_currency.parseAmount(value, de.amount); + if (!ok || 0 == de.amount) { + fail_msg_writer() << "amount is wrong: " << arg << ' ' << value << + ", expected number from 0 to " << m_currency.formatAmount(std::numeric_limits::max()); + return false; + } + + dsts.push_back(de); + } + } + + if (dsts.empty()) { + fail_msg_writer() << "At least one destination address is required"; + return false; + } + } catch (const std::exception& e) { + fail_msg_writer() << e.what(); + return false; + } + + return true; + } + }; } @@ -175,8 +297,9 @@ bool simple_wallet::help(const std::vector &args/* = std::vector] - Start mining in daemon"); @@ -186,7 +309,10 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [ ... ] [payment_id] - Transfer ,... to ,... , respectively. is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), + "transfer [ ... ] [-p payment_id] [-f fee]" + " - Transfer ,... to ,... , respectively. " + " is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); @@ -341,12 +467,14 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas { m_wallet_file = wallet_file; - m_wallet.reset(new tools::wallet2()); + m_wallet.reset(new tools::wallet2(m_currency)); m_wallet->callback(this); try { m_wallet->generate(wallet_file, password); - message_writer(epee::log_space::console_color_white, true) << "Generated new wallet: " << m_wallet->get_account().get_public_address_str() << std::endl << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + message_writer(epee::log_space::console_color_white, true) << + "Generated new wallet: " << m_currency.accountAddressAsString(m_wallet->get_account()) << std::endl << + "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); } catch (const std::exception& e) { @@ -371,13 +499,14 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas bool simple_wallet::open_wallet(const string &wallet_file, const std::string& password) { m_wallet_file = wallet_file; - m_wallet.reset(new tools::wallet2()); + m_wallet.reset(new tools::wallet2(m_currency)); m_wallet->callback(this); try { m_wallet->load(m_wallet_file, password); - message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << m_wallet->get_account().get_public_address_str(); + message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << + m_currency.accountAddressAsString(m_wallet->get_account()); } catch (const std::exception& e) { @@ -438,7 +567,7 @@ bool simple_wallet::start_mining(const std::vector& args) return true; COMMAND_RPC_START_MINING::request req; - req.miner_address = m_wallet->get_account().get_public_address_str(); + req.miner_address = m_currency.accountAddressAsString(m_wallet->get_account()); bool ok = true; size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast(2)); @@ -491,30 +620,30 @@ bool simple_wallet::stop_mining(const std::vector& args) return true; } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block) +void simple_wallet::on_new_block(uint64_t height) { m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) +void simple_wallet::on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index) { message_writer(epee::log_space::console_color_green, false) << "Height " << height << ", transaction " << get_transaction_hash(tx) << - ", received " << print_money(tx.vout[out_index].amount); + ", received " << m_currency.formatAmount(tx.vout[out_index].amount); m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) +void simple_wallet::on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx) { message_writer(epee::log_space::console_color_magenta, false) << "Height " << height << ", transaction " << get_transaction_hash(spend_tx) << - ", spent " << print_money(in_tx.vout[out_index].amount); + ", spent " << m_currency.formatAmount(in_tx.vout[out_index].amount); m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) +void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx) { message_writer(epee::log_space::console_color_red, true) << "Height " << height << @@ -585,7 +714,8 @@ bool simple_wallet::refresh(const std::vector& args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) { - success_msg_writer() << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance()); + success_msg_writer() << "balance: " << m_currency.formatAmount(m_wallet->balance()) << + ", unlocked balance: " << m_currency.formatAmount(m_wallet->unlocked_balance()); return true; } //---------------------------------------------------------------------------------------------------- @@ -621,7 +751,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args transfers_found = true; } message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) << - std::setw(21) << print_money(td.amount()) << '\t' << + std::setw(21) << m_currency.formatAmount(td.amount()) << '\t' << std::setw(3) << (td.m_spent ? 'T' : 'F') << " \t" << std::setw(12) << td.m_global_output_index << '\t' << get_transaction_hash(td.m_tx); @@ -683,7 +813,7 @@ bool simple_wallet::show_payments(const std::vector &args) payment_id << '\t' << pd.m_tx_hash << '\t' << std::setw(8) << pd.m_block_height << '\t' << - std::setw(21) << print_money(pd.m_amount) << '\t' << + std::setw(21) << m_currency.formatAmount(pd.m_amount) << '\t' << pd.m_unlock_time; } } @@ -719,73 +849,20 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::transfer(const std::vector &args_) +bool simple_wallet::transfer(const std::vector &args) { if (!try_connect_to_daemon()) return true; - std::vector local_args = args_; - if(local_args.size() < 3) - { - fail_msg_writer() << "wrong number of arguments, expected at least 3, got " << local_args.size(); - return true; - } - - size_t fake_outs_count; - if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) - { - fail_msg_writer() << "mixin_count should be non-negative integer, got " << local_args[0]; - return true; - } - local_args.erase(local_args.begin()); - - std::vector extra; - if (1 == local_args.size() % 2) - { - std::string payment_id_str = local_args.back(); - local_args.pop_back(); - - crypto::hash payment_id; - bool r = tools::wallet2::parse_payment_id(payment_id_str, payment_id); - if(r) - { - 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); - } - - if(!r) - { - fail_msg_writer() << "payment id has invalid format: \"" << payment_id_str << "\", expected 64-character string"; - return true; - } - } - - vector dsts; - for (size_t i = 0; i < local_args.size(); i += 2) + try { - cryptonote::tx_destination_entry de; - if(!get_account_address_from_str(de.addr, local_args[i])) - { - fail_msg_writer() << "wrong address: " << local_args[i]; - return true; - } + TransferCommand cmd(m_currency); - bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]); - if(!ok || 0 == de.amount) - { - fail_msg_writer() << "amount is wrong: " << local_args[i] << ' ' << local_args[i + 1] << - ", expected number from 0 to " << print_money(std::numeric_limits::max()); + if (!cmd.parseArguments(args)) return true; - } - - dsts.push_back(de); - } - try - { - cryptonote::transaction tx; - m_wallet->transfer(dsts, fake_outs_count, 0, MINIMUM_FEE, extra, tx); + cryptonote::Transaction tx; + m_wallet->transfer(cmd.dsts, cmd.fake_outs_count, 0, cmd.fee, cmd.extra, tx); success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); } catch (const tools::error::daemon_busy&) @@ -807,9 +884,9 @@ bool simple_wallet::transfer(const std::vector &args_) } catch (const tools::error::not_enough_money& e) { - fail_msg_writer() << "not enough money to transfer, available only " << print_money(e.available()) << - ", transaction amount " << print_money(e.tx_amount() + e.fee()) << " = " << print_money(e.tx_amount()) << - " + " << print_money(e.fee()) << " (fee)"; + fail_msg_writer() << "not enough money to transfer, available only " << m_currency.formatAmount(e.available()) << + ", transaction amount " << m_currency.formatAmount(e.tx_amount() + e.fee()) << " = " << + m_currency.formatAmount(e.tx_amount()) << " + " << m_currency.formatAmount(e.fee()) << " (fee)"; } catch (const tools::error::not_enough_outs_to_mix& e) { @@ -817,7 +894,8 @@ bool simple_wallet::transfer(const std::vector &args_) writer << "not enough outputs for specified mixin_count = " << e.mixin_count() << ":"; for (const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& outs_for_amount : e.scanty_outs()) { - writer << "\noutput amount = " << print_money(outs_for_amount.amount) << ", fount outputs to mix = " << outs_for_amount.outs.size(); + writer << "\noutput amount = " << m_currency.formatAmount(outs_for_amount.amount) << + ", fount outputs to mix = " << outs_for_amount.outs.size(); } } catch (const tools::error::tx_not_constructed&) @@ -834,7 +912,7 @@ bool simple_wallet::transfer(const std::vector &args_) } catch (const tools::error::tx_too_big& e) { - cryptonote::transaction tx = e.tx(); + cryptonote::Transaction tx = e.tx(); fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " << get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes"; } @@ -868,7 +946,7 @@ bool simple_wallet::transfer(const std::vector &args_) //---------------------------------------------------------------------------------------------------- bool simple_wallet::run() { - std::string addr_start = m_wallet->get_account().get_public_address_str().substr(0, 6); + std::string addr_start = m_currency.accountAddressAsString(m_wallet->get_account()).substr(0, 6); return m_cmd_binder.run_handling("[wallet " + addr_start + "]: ", ""); } //---------------------------------------------------------------------------------------------------- @@ -880,7 +958,7 @@ void simple_wallet::stop() //---------------------------------------------------------------------------------------------------- bool simple_wallet::print_address(const std::vector &args/* = std::vector()*/) { - success_msg_writer() << m_wallet->get_account().get_public_address_str(); + success_msg_writer() << m_currency.accountAddressAsString(m_wallet->get_account()); return true; } //---------------------------------------------------------------------------------------------------- @@ -912,6 +990,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_daemon_port); command_line::add_arg(desc_params, arg_command); command_line::add_arg(desc_params, arg_log_level); + command_line::add_arg(desc_params, arg_testnet); tools::wallet_rpc_server::init_options(desc_params); po::positional_options_description positional_options; @@ -919,7 +998,8 @@ int main(int argc, char* argv[]) po::options_description desc_all; desc_all.add(desc_general).add(desc_params); - cryptonote::simple_wallet w; + cryptonote::Currency tmp_currency = cryptonote::CurrencyBuilder().currency(); + cryptonote::simple_wallet tmp_wallet(tmp_currency); po::variables_map vm; bool r = command_line::handle_error_helper(desc_all, [&]() { @@ -927,9 +1007,9 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - success_msg_writer() << "bytecoin wallet v" << PROJECT_VERSION_LONG; + success_msg_writer() << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; success_msg_writer() << "Usage: simplewallet [--wallet-file=|--generate-new-wallet=] [--daemon-address=:] []"; - success_msg_writer() << desc_all << '\n' << w.get_commands_str(); + success_msg_writer() << desc_all << '\n' << tmp_wallet.get_commands_str(); return false; } else if (command_line::get_arg(vm, command_line::arg_version)) @@ -961,6 +1041,10 @@ int main(int argc, char* argv[]) log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); } + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.testnet(command_line::get_arg(vm, arg_testnet)); + cryptonote::Currency currency = currencyBuilder.currency(); + if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); @@ -993,7 +1077,7 @@ int main(int argc, char* argv[]) if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - tools::wallet2 wal; + tools::wallet2 wal(currency); try { LOG_PRINT_L0("Loading wallet..."); @@ -1011,7 +1095,7 @@ int main(int argc, char* argv[]) bool r = wrpc.init(vm); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server"); - tools::signal_handler::install([&wrpc, &wal] { + tools::SignalHandler::install([&wrpc, &wal] { wrpc.send_stop_signal(); wal.store(); }); @@ -1032,19 +1116,20 @@ int main(int argc, char* argv[]) }else { //runs wallet with console interface - r = w.init(vm); + cryptonote::simple_wallet wal(currency); + r = wal.init(vm); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet"); std::vector command = command_line::get_arg(vm, arg_command); if (!command.empty()) - w.process_command(command); + wal.process_command(command); - tools::signal_handler::install([&w] { - w.stop(); + tools::SignalHandler::install([&wal] { + wal.stop(); }); - w.run(); + wal.run(); - w.deinit(); + wal.deinit(); } return 1; //CATCH_ENTRY_L0("main", 1); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index f462cefe98..529b9d0a51 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -21,8 +21,8 @@ #include -#include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/Currency.h" #include "wallet/wallet2.h" #include "console_handler.h" #include "password_container.h" @@ -38,7 +38,7 @@ namespace cryptonote public: typedef std::vector command_type; - simple_wallet(); + simple_wallet(const cryptonote::Currency& currency); bool init(const boost::program_options::variables_map& vm); bool deinit(); bool run(); @@ -47,6 +47,9 @@ namespace cryptonote //wallet *create_wallet(); bool process_command(const std::vector &args); std::string get_commands_str(); + + const cryptonote::Currency& currency() const { return m_currency; } + private: void handle_command_line(const boost::program_options::variables_map& vm); @@ -74,10 +77,10 @@ namespace cryptonote bool ask_wallet_create_if_needed(); //----------------- i_wallet2_callback --------------------- - virtual void on_new_block(uint64_t height, const cryptonote::block& block); - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index); - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx); - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx); + virtual void on_new_block(uint64_t height); + virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index); + virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx); + virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx); //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -96,8 +99,8 @@ namespace cryptonote void update(uint64_t height, bool force = false) { auto current_time = std::chrono::system_clock::now(); - if (std::chrono::seconds(DIFFICULTY_TARGET / 2) < current_time - m_blockchain_height_update_time || m_blockchain_height <= height) - { + if (std::chrono::seconds(m_simple_wallet.currency().difficultyTarget() / 2) < current_time - m_blockchain_height_update_time || + m_blockchain_height <= height) { update_blockchain_height(); m_blockchain_height = (std::max)(m_blockchain_height, height); } @@ -143,6 +146,7 @@ namespace cryptonote epee::console_handlers_binder m_cmd_binder; + const cryptonote::Currency& m_currency; std::unique_ptr m_wallet; epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; diff --git a/src/version.h.in b/src/version.h.in index 62316da3fd..073f9e1708 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "0.8.11" -#define PROJECT_VERSION_BUILD_NO "65" +#define PROJECT_VERSION "1.0.0" +#define PROJECT_VERSION_BUILD_NO "312" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index 18e3dd6f9c..ae2c8e8f51 100644 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -36,6 +36,7 @@ #include "WalletSerialization.h" #include #include +#include namespace { @@ -73,15 +74,34 @@ void runAtomic(std::mutex& mutex, F f) { namespace CryptoNote { -Wallet::Wallet(INode& node) : +void Wallet::WalletNodeObserver::postponeRefresh() { + std::unique_lock lock(postponeMutex); + postponed = true; +} + +void Wallet::WalletNodeObserver::saveCompleted(std::error_code result) { + bool startRefresh = false; + { + std::unique_lock lock(postponeMutex); + startRefresh = postponed; + postponed = false; + } + + if (startRefresh) { + m_wallet->startRefresh(); + } +} + +Wallet::Wallet(const cryptonote::Currency& currency, INode& node) : m_state(NOT_INITIALIZED), + m_currency(currency), m_node(node), m_isSynchronizing(false), m_isStopping(false), - m_transferDetails(m_blockchain), + m_transferDetails(currency, m_blockchain), m_transactionsCache(m_sendingTxsStates), m_synchronizer(m_account, m_node, m_blockchain, m_transferDetails, m_unconfirmedTransactions, m_transactionsCache), - m_sender(m_transactionsCache, m_sendingTxsStates, m_transferDetails, m_unconfirmedTransactions) { + m_sender(currency, m_transactionsCache, m_sendingTxsStates, m_transferDetails, m_unconfirmedTransactions) { m_autoRefresher.reset(new WalletNodeObserver(this)); } @@ -102,6 +122,7 @@ void Wallet::initAndGenerate(const std::string& password) { } m_node.addObserver(m_autoRefresher.get()); + addObserver(m_autoRefresher.get()); m_account.generate(); m_password = password; @@ -118,9 +139,7 @@ void Wallet::initAndGenerate(const std::string& password) { } void Wallet::storeGenesisBlock() { - cryptonote::block b; - cryptonote::generate_genesis_block(b); - m_blockchain.push_back(get_block_hash(b)); + m_blockchain.push_back(m_currency.genesisBlockHash()); } void Wallet::initAndLoad(std::istream& source, const std::string& password) { @@ -131,6 +150,7 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { } m_node.addObserver(m_autoRefresher.get()); + addObserver(m_autoRefresher.get()); m_password = password; m_state = LOADING; @@ -167,8 +187,8 @@ void Wallet::doLoad(std::istream& source) { dataArchive >> m_account; - throwIfKeysMissmatch(m_account.get_keys().m_view_secret_key, m_account.get_keys().m_account_address.m_view_public_key); - throwIfKeysMissmatch(m_account.get_keys().m_spend_secret_key, m_account.get_keys().m_account_address.m_spend_public_key); + throwIfKeysMissmatch(m_account.get_keys().m_view_secret_key, m_account.get_keys().m_account_address.m_viewPublicKey); + throwIfKeysMissmatch(m_account.get_keys().m_spend_secret_key, m_account.get_keys().m_account_address.m_spendPublicKey); dataArchive >> m_blockchain; @@ -228,6 +248,7 @@ void Wallet::shutdown() { m_asyncContextCounter.waitAsyncContextsFinish(); m_node.removeObserver(m_autoRefresher.get()); + removeObserver(m_autoRefresher.get()); } void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { @@ -328,7 +349,7 @@ std::string Wallet::getAddress() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); - return m_account.get_public_address_str(); + return m_currency.accountAddressAsString(m_account); } uint64_t Wallet::actualBalance() { @@ -374,7 +395,7 @@ TransactionId Wallet::findTransactionByTransferId(TransferId transferId) { return m_transactionsCache.findTransactionByTransferId(transferId); } -bool Wallet::getTransaction(TransactionId transactionId, Transaction& transaction) { +bool Wallet::getTransaction(TransactionId transactionId, TransactionInfo& transaction) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); @@ -476,6 +497,11 @@ void Wallet::refresh() { { std::unique_lock lock(m_cacheMutex); + if (m_state == SAVING) { + m_autoRefresher->postponeRefresh(); + return; + } + if (m_state != INITIALIZED) { return; } diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index e7a25b6d8b..a363995049 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -29,9 +29,9 @@ #include "WalletAsyncContextCounter.h" #include "WalletTxSendingState.h" #include "common/ObserverManager.h" -#include "cryptonote_core/account.h" #include "cryptonote_core/tx_extra.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" #include "WalletTransferDetails.h" #include "WalletUserTransactionsCache.h" #include "WalletUnconfirmedTransactions.h" @@ -43,7 +43,7 @@ namespace CryptoNote { class Wallet : public IWallet { public: - Wallet(INode& node); + Wallet(const cryptonote::Currency& currency, INode& node); ~Wallet() {}; virtual void addObserver(IWalletObserver* observer); @@ -67,7 +67,7 @@ class Wallet : public IWallet { virtual TransactionId findTransactionByTransferId(TransferId transferId); - virtual bool getTransaction(TransactionId transactionId, Transaction& transaction); + virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction); virtual bool getTransfer(TransferId transferId, Transfer& transfer); virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); @@ -105,6 +105,7 @@ class Wallet : public IWallet { std::mutex m_cacheMutex; cryptonote::account_base m_account; std::string m_password; + const cryptonote::Currency& m_currency; INode& m_node; bool m_isSynchronizing; bool m_isStopping; @@ -119,12 +120,17 @@ class Wallet : public IWallet { WalletTxSendingState m_sendingTxsStates; WalletUserTransactionsCache m_transactionsCache; - struct WalletNodeObserver: public INodeObserver + struct WalletNodeObserver: public INodeObserver, public IWalletObserver { - WalletNodeObserver(Wallet* wallet) : m_wallet(wallet) {} + WalletNodeObserver(Wallet* wallet) : m_wallet(wallet), postponed(false) {} virtual void lastKnownBlockHeightUpdated(uint64_t height) { m_wallet->startRefresh(); } + virtual void saveCompleted(std::error_code result); + void postponeRefresh(); Wallet* m_wallet; + + std::mutex postponeMutex; + bool postponed; }; std::unique_ptr m_autoRefresher; diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h index 1b59c861f6..45f89ccae5 100644 --- a/src/wallet/WalletRequest.h +++ b/src/wallet/WalletRequest.h @@ -17,6 +17,10 @@ #pragma once +#include +#include +#include + #include "INode.h" #include "WalletSynchronizationContext.h" @@ -92,7 +96,7 @@ class WalletGetRandomOutsByAmountsRequest: public WalletRequest class WalletRelayTransactionRequest: public WalletRequest { public: - WalletRelayTransactionRequest(const cryptonote::transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {}; + WalletRelayTransactionRequest(const cryptonote::Transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {}; virtual ~WalletRelayTransactionRequest() {}; virtual void perform(INode& node, std::function cb) @@ -101,7 +105,7 @@ class WalletRelayTransactionRequest: public WalletRequest } private: - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; Callback m_cb; }; diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h index 6e6c363dc1..c27815d07a 100644 --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/wallet/WalletSendTransactionContext.h @@ -29,9 +29,9 @@ struct TxDustPolicy { uint64_t dustThreshold; bool addToFee; - cryptonote::account_public_address addrForDust; + cryptonote::AccountPublicAddress addrForDust; - TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address()) + TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::AccountPublicAddress an_addr_for_dust = cryptonote::AccountPublicAddress()) : dustThreshold(a_dust_threshold) , addToFee(an_add_to_fee) , addrForDust(an_addr_for_dust) diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h index 378b6d2f81..2126575b53 100644 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -23,6 +23,7 @@ #include #include +#include "cryptonote_core/AccountKVSerialization.h" #include "cryptonote_core/cryptonote_boost_serialization.h" #include "common/unordered_containers_boost_serialization.h" #include "storages/portable_storage_template_helper.h" @@ -37,19 +38,21 @@ inline void load(Archive & ar, cryptonote::account_base& account, const unsigned { std::string data; ar >> data; - epee::serialization::load_t_from_binary(account, data); + cryptonote::AccountBaseSerializer accountSerializer(account); + epee::serialization::load_t_from_binary(accountSerializer, data); } template inline void save(Archive & ar, const cryptonote::account_base& account, const unsigned int version) { std::string data; - epee::serialization::store_t_to_binary(account, data); + cryptonote::AccountBaseSerializer accountSerializer(account); + epee::serialization::store_t_to_binary(accountSerializer, data); ar << data; } template -inline void serialize(Archive & ar, CryptoNote::Transaction& tx, const unsigned int version) +inline void serialize(Archive & ar, CryptoNote::TransactionInfo& tx, const unsigned int version) { ar & tx.firstTransferId; ar & tx.transferCount; diff --git a/src/wallet/WalletSynchronizationContext.h b/src/wallet/WalletSynchronizationContext.h index 3682dcd340..5e42da6b67 100644 --- a/src/wallet/WalletSynchronizationContext.h +++ b/src/wallet/WalletSynchronizationContext.h @@ -32,7 +32,7 @@ struct TransactionContextInfo { std::vector requestedOuts; std::vector globalIndices; - cryptonote::transaction transaction; + cryptonote::Transaction transaction; crypto::public_key transactionPubKey; }; diff --git a/src/wallet/WalletSynchronizer.cpp b/src/wallet/WalletSynchronizer.cpp index d85cab88fa..b96f83140e 100644 --- a/src/wallet/WalletSynchronizer.cpp +++ b/src/wallet/WalletSynchronizer.cpp @@ -19,6 +19,8 @@ #include +#include "cryptonote_core/account.h" + #include "WalletErrors.h" #include "WalletUtils.h" @@ -29,7 +31,7 @@ void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) { throw std::system_error(make_error_code(ec)); } -bool getTxPubKey(const cryptonote::transaction& tx, crypto::public_key& key) { +bool getTxPubKey(const cryptonote::Transaction& tx, crypto::public_key& key) { std::vector extraFields; cryptonote::parse_tx_extra(tx.extra, extraFields); @@ -43,33 +45,33 @@ bool getTxPubKey(const cryptonote::transaction& tx, crypto::public_key& key) { return true; } -void findMyOuts(const cryptonote::account_keys& acc, const cryptonote::transaction& tx, const crypto::public_key& txPubKey, std::vector& outs, uint64_t& moneyTransfered) { +void findMyOuts(const cryptonote::account_keys& acc, const cryptonote::Transaction& tx, const crypto::public_key& txPubKey, std::vector& outs, uint64_t& moneyTransfered) { bool r = cryptonote::lookup_acc_outs(acc, tx, txPubKey, outs, moneyTransfered); throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); } -uint64_t countOverallTxOutputs(const cryptonote::transaction& tx) { +uint64_t countOverallTxOutputs(const cryptonote::Transaction& tx) { uint64_t amount = 0; - for (const cryptonote::tx_out& o: tx.vout) { + for (const cryptonote::TransactionOutput& o: tx.vout) { amount += o.amount; } return amount; } -uint64_t countOverallTxInputs(const cryptonote::transaction& tx) { +uint64_t countOverallTxInputs(const cryptonote::Transaction& tx) { uint64_t amount = 0; for (auto& in: tx.vin) { - if(in.type() != typeid(cryptonote::txin_to_key)) + if(in.type() != typeid(cryptonote::TransactionInputToKey)) continue; - amount += boost::get(in).amount; + amount += boost::get(in).amount; } return amount; } -void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::TransactionHash& hash) { +void fillTransactionHash(const cryptonote::Transaction& tx, CryptoNote::TransactionHash& hash) { crypto::hash h = cryptonote::get_transaction_hash(tx); memcpy(hash.data(), reinterpret_cast(&h), hash.size()); } @@ -213,6 +215,7 @@ bool WalletSynchronizer::processNewBlocks(ProcessParameters& parameters) { fillRequest = true; ++context->progress.blockIdx; + context->progress.minersTxProcessed = false; ++currentIndex; } } @@ -225,7 +228,7 @@ bool WalletSynchronizer::processNewBlocks(ProcessParameters& parameters) { } WalletSynchronizer::NextBlockAction WalletSynchronizer::handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height) { - cryptonote::block b; + cryptonote::Block b; bool r = cryptonote::parse_and_validate_block_from_blob(blockEntry.block, b); throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); @@ -260,11 +263,11 @@ WalletSynchronizer::NextBlockAction WalletSynchronizer::handleNewBlockchainEntry return SKIP; } -bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::block& b, crypto::hash& blockId, uint64_t height) { +bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::Block& b, crypto::hash& blockId, uint64_t height) { throwIf(height != m_blockchain.size(), cryptonote::error::INTERNAL_WALLET_ERROR); if(b.timestamp + 60*60*24 > m_account.get_createtime()) { - if (!processMinersTx(parameters, b.miner_tx, height, b.timestamp)) + if (!processMinersTx(parameters, b.minerTx, height, b.timestamp)) return false; auto txIt = blockEntry.txs.begin(); @@ -272,7 +275,7 @@ bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters for (; txIt != blockEntry.txs.end(); ++txIt) { auto& txblob = *txIt; - cryptonote::transaction tx; + cryptonote::Transaction tx; bool r = parse_and_validate_tx_from_blob(txblob, tx); throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); @@ -290,7 +293,7 @@ bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters return true; } -bool WalletSynchronizer::processMinersTx(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp) { +bool WalletSynchronizer::processMinersTx(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp) { bool r = true; if (!parameters.context->progress.minersTxProcessed) { @@ -301,7 +304,7 @@ bool WalletSynchronizer::processMinersTx(ProcessParameters& parameters, const cr return r; } -bool WalletSynchronizer::processNewTransaction(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp) { +bool WalletSynchronizer::processNewTransaction(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp) { bool res = true; processUnconfirmed(parameters, tx, height, timestamp); @@ -329,18 +332,18 @@ bool WalletSynchronizer::processNewTransaction(ProcessParameters& parameters, co return res; } -uint64_t WalletSynchronizer::processMyInputs(const cryptonote::transaction& tx) { +uint64_t WalletSynchronizer::processMyInputs(const cryptonote::Transaction& tx) { uint64_t money = 0; // check all outputs for spending (compare key images) for (auto& in: tx.vin) { - if(in.type() != typeid(cryptonote::txin_to_key)) + if(in.type() != typeid(cryptonote::TransactionInputToKey)) continue; size_t idx; - if (!m_transferDetails.getTransferDetailsIdxByKeyImage(boost::get(in).k_image, idx)) + if (!m_transferDetails.getTransferDetailsIdxByKeyImage(boost::get(in).keyImage, idx)) continue; - money += boost::get(in).amount; + money += boost::get(in).amount; TransferDetails& td = m_transferDetails.getTransferDetails(idx); td.spent = true; @@ -349,7 +352,7 @@ uint64_t WalletSynchronizer::processMyInputs(const cryptonote::transaction& tx) return money; } -void WalletSynchronizer::fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::transaction& tx, +void WalletSynchronizer::fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::Transaction& tx, const std::vector& outs, const crypto::public_key& publicKey, uint64_t height) { crypto::hash txid = cryptonote::get_transaction_hash(tx); @@ -363,14 +366,14 @@ void WalletSynchronizer::fillGetTransactionOutsGlobalIndicesRequest(ProcessParam postGetTransactionOutsGlobalIndicesRequest(parameters, txid, insert_result.first->second.globalIndices, height); } -void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, bool isCoinbase, uint64_t timestamp) { +void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, bool isCoinbase, uint64_t timestamp) { uint64_t allOuts = countOverallTxOutputs(tx); uint64_t allInputs = countOverallTxInputs(tx); TransactionId foundTx = m_transactionsCache.findTransactionByHash(cryptonote::get_transaction_hash(tx)); if (foundTx == INVALID_TRANSACTION_ID) { - Transaction transaction; + TransactionInfo transaction; transaction.firstTransferId = INVALID_TRANSFER_ID; transaction.transferCount = 0; transaction.totalAmount = myOuts - myInputs; @@ -386,7 +389,7 @@ void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, } else { - Transaction& transaction = m_transactionsCache.getTransaction(foundTx); + TransactionInfo& transaction = m_transactionsCache.getTransaction(foundTx); transaction.blockHeight = height; transaction.timestamp = timestamp; transaction.isCoinbase = isCoinbase; @@ -395,14 +398,14 @@ void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, } } -void WalletSynchronizer::processUnconfirmed(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp) { +void WalletSynchronizer::processUnconfirmed(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp) { TransactionId id; crypto::hash hash = get_transaction_hash(tx); if (!m_unconfirmedTransactions.findTransactionId(hash, id)) return; - Transaction& tr = m_transactionsCache.getTransaction(id); + TransactionInfo& tr = m_transactionsCache.getTransaction(id); tr.blockHeight = height; tr.timestamp = timestamp; @@ -430,7 +433,7 @@ void WalletSynchronizer::handleTransactionOutGlobalIndicesResponse(std::shared_p return; } - cryptonote::transaction& tx = it->second.transaction; + cryptonote::Transaction& tx = it->second.transaction; std::vector& outs = it->second.requestedOuts; crypto::public_key& tx_pub_key = it->second.transactionPubKey; std::vector& global_indices = it->second.globalIndices; @@ -444,9 +447,9 @@ void WalletSynchronizer::handleTransactionOutGlobalIndicesResponse(std::shared_p td.globalOutputIndex = global_indices[o]; td.tx = tx; td.spent = false; - cryptonote::keypair in_ephemeral; + cryptonote::KeyPair in_ephemeral; cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.keyImage); - throwIf(in_ephemeral.pub != boost::get(tx.vout[o].target).key, cryptonote::error::INTERNAL_WALLET_ERROR); + throwIf(in_ephemeral.pub != boost::get(tx.vout[o].target).key, cryptonote::error::INTERNAL_WALLET_ERROR); m_transferDetails.addTransferDetails(td); } diff --git a/src/wallet/WalletSynchronizer.h b/src/wallet/WalletSynchronizer.h index 64886d481b..9b69cb6c93 100644 --- a/src/wallet/WalletSynchronizer.h +++ b/src/wallet/WalletSynchronizer.h @@ -65,17 +65,17 @@ class WalletSynchronizer bool processNewBlocks(ProcessParameters& parameters); NextBlockAction handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height); - bool processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::block& b, crypto::hash& blockId, uint64_t height); - bool processNewTransaction(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp); - bool processMinersTx(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp); - void processUnconfirmed(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t height, uint64_t timestamp); - uint64_t processMyInputs(const cryptonote::transaction& tx); - void updateTransactionsCache(ProcessParameters& parameters, const cryptonote::transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, + bool processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::Block& b, crypto::hash& blockId, uint64_t height); + bool processNewTransaction(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp); + bool processMinersTx(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp); + void processUnconfirmed(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp); + uint64_t processMyInputs(const cryptonote::Transaction& tx); + void updateTransactionsCache(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, bool isCoinbase, uint64_t timestamp); void detachBlockchain(uint64_t height); void refreshBalance(std::deque >& events); - void fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::transaction& tx, + void fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::Transaction& tx, const std::vector& outs, const crypto::public_key& publicKey, uint64_t height); void postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector& outsGlobalIndices, uint64_t height); std::shared_ptr makeGetNewBlocksRequest(std::shared_ptr context); diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index afdf5aac52..31fc187277 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -15,6 +15,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +// epee +#include "misc_language.h" + +#include "cryptonote_core/account.h" + #include "WalletTransactionSender.h" #include "WalletUtils.h" @@ -33,7 +38,7 @@ uint64_t countNeededMoney(uint64_t fee, const std::vector& return needed_money; } -void createChangeDestinations(const cryptonote::account_public_address& address, uint64_t neededMoney, uint64_t foundMoney, cryptonote::tx_destination_entry& changeDts) { +void createChangeDestinations(const cryptonote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, cryptonote::tx_destination_entry& changeDts) { if (neededMoney < foundMoney) { changeDts.addr = address; changeDts.amount = foundMoney - neededMoney; @@ -41,7 +46,7 @@ void createChangeDestinations(const cryptonote::account_public_address& address, } void constructTx(const cryptonote::account_keys keys, const std::vector& sources, const std::vector& splittedDests, - const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, cryptonote::transaction& tx) { + const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, cryptonote::Transaction& tx) { std::vector extraVec; extraVec.reserve(extra.size()); std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);}); @@ -51,7 +56,7 @@ void constructTx(const cryptonote::account_keys keys, const std::vector= sizeLimit, cryptonote::error::TRANSACTION_SIZE_TOO_BIG); } -void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::TransactionHash& hash) { +void fillTransactionHash(const cryptonote::Transaction& tx, CryptoNote::TransactionHash& hash) { crypto::hash h = cryptonote::get_transaction_hash(tx); memcpy(hash.data(), reinterpret_cast(&h), hash.size()); } @@ -60,15 +65,16 @@ void fillTransactionHash(const cryptonote::transaction& tx, CryptoNote::Transact namespace CryptoNote { -WalletTransactionSender::WalletTransactionSender(WalletUserTransactionsCache& transactionsCache, WalletTxSendingState& sendingTxsStates, - WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions): +WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, + WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions): + m_currency(currency), m_transactionsCache(transactionsCache), m_sendingTxsStates(sendingTxsStates), m_transferDetails(transferDetails), m_unconfirmedTransactions(unconfirmedTransactions), m_isInitialized(false), m_isStoping(false) { - m_upperTransactionSizeLimit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + m_upperTransactionSizeLimit = (m_currency.blockGrantedFullRewardZone() * 125) / 100 - m_currency.minerTxBlobReservedSize(); } void WalletTransactionSender::init(cryptonote::account_keys keys) { @@ -84,6 +90,19 @@ void WalletTransactionSender::stop() { m_isStoping = true; } +bool WalletTransactionSender::validateDestinationAddress(const std::string& address) { + cryptonote::AccountPublicAddress ignore; + return m_currency.parseAccountAddressString(address, ignore); +} + +void WalletTransactionSender::validateTransfersAddresses(const std::vector& transfers) { + for (const Transfer& tr: transfers) { + if (!validateDestinationAddress(tr.address)) { + throw std::system_error(make_error_code(cryptonote::error::BAD_ADDRESS)); + } + } +} + std::shared_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { if (!m_isInitialized) @@ -91,14 +110,18 @@ std::shared_ptr WalletTransactionSender::makeSendRequest(Transact using namespace cryptonote; - std::shared_ptr context = std::make_shared(); throwIf(transfers.empty(), cryptonote::error::ZERO_DESTINATION); + validateTransfersAddresses(transfers); + uint64_t neededMoney = countNeededMoney(fee, transfers); - TransferId firstTransferId = m_transactionsCache.insertTransfers(transfers); + std::shared_ptr context = std::make_shared(); - uint64_t neededMoney = countNeededMoney(fee, transfers); + context->foundMoney = m_transferDetails.selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); + throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); - Transaction transaction; + TransferId firstTransferId = m_transactionsCache.insertTransfers(transfers); + + TransactionInfo transaction; transaction.firstTransferId = firstTransferId; transaction.transferCount = transfers.size(); transaction.totalAmount = neededMoney; @@ -116,9 +139,6 @@ std::shared_ptr WalletTransactionSender::makeSendRequest(Transact context->unlockTimestamp = unlockTimestamp; context->mixIn = mixIn; - context->foundMoney = m_transferDetails.selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); - throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); - if(context->mixIn) { std::shared_ptr request = makeGetRandomOutsRequest(context); return request; @@ -173,7 +193,7 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s try { - Transaction& transaction = m_transactionsCache.getTransaction(context->transactionId); + TransactionInfo& transaction = m_transactionsCache.getTransaction(context->transactionId); std::vector sources; prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); @@ -184,7 +204,7 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s std::vector splittedDests; splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); - cryptonote::transaction tx; + cryptonote::Transaction tx; constructTx(m_keys, sources, splittedDests, transaction.extra, context->unlockTimestamp, m_upperTransactionSizeLimit, tx); fillTransactionHash(tx, transaction.hash); @@ -241,8 +261,8 @@ void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, siz for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) { Transfer& de = m_transactionsCache.getTransfer(idx); - cryptonote::account_public_address addr; - if (!cryptonote::get_account_address_from_str(addr, de.address)) { + cryptonote::AccountPublicAddress addr; + if (!m_currency.parseAccountAddressString(de.address, addr)) { throw std::system_error(make_error_code(cryptonote::error::BAD_ADDRESS)); } @@ -286,7 +306,7 @@ void WalletTransactionSender::prepareInputs(const std::list& selectedTra cryptonote::tx_source_entry::output_entry real_oe; real_oe.first = td.globalOutputIndex; - real_oe.second = boost::get(td.tx.vout[td.internalOutputIndex].target).key; + real_oe.second = boost::get(td.tx.vout[td.internalOutputIndex].target).key; auto interted_it = src.outputs.insert(it_to_insert, real_oe); diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h index 87fd20d7a1..e40626626a 100644 --- a/src/wallet/WalletTransactionSender.h +++ b/src/wallet/WalletTransactionSender.h @@ -17,6 +17,9 @@ #pragma once +#include "cryptonote_core/account.h" +#include "cryptonote_core/Currency.h" + #include "INode.h" #include "WalletSendTransactionContext.h" #include "WalletUserTransactionsCache.h" @@ -29,8 +32,8 @@ namespace CryptoNote { class WalletTransactionSender { public: - WalletTransactionSender(WalletUserTransactionsCache& transactionsCache, WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails, - WalletUnconfirmedTransactions& unconfirmedTransactions); + WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, + WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions); void init(cryptonote::account_keys keys); void stop(); @@ -52,7 +55,10 @@ class WalletTransactionSender void relayTransactionCallback(TransactionId txId, std::deque >& events, boost::optional >& nextRequest, std::error_code ec); void notifyBalanceChanged(std::deque >& events); + void validateTransfersAddresses(const std::vector& transfers); + bool validateDestinationAddress(const std::string& address); + const cryptonote::Currency& m_currency; cryptonote::account_keys m_keys; WalletUserTransactionsCache& m_transactionsCache; WalletTxSendingState& m_sendingTxsStates; diff --git a/src/wallet/WalletTransferDetails.cpp b/src/wallet/WalletTransferDetails.cpp index 9e3b596e68..0d3ea404a4 100644 --- a/src/wallet/WalletTransferDetails.cpp +++ b/src/wallet/WalletTransferDetails.cpp @@ -50,7 +50,8 @@ T popRandomValue(URNG& randomGenerator, std::vector& vec) { namespace CryptoNote { -WalletTransferDetails::WalletTransferDetails(const std::vector& blockchain) : m_blockchain(blockchain) { +WalletTransferDetails::WalletTransferDetails(const cryptonote::Currency& currency, const std::vector& blockchain) : + m_currency(currency), m_blockchain(blockchain) { } WalletTransferDetails::~WalletTransferDetails() { @@ -76,30 +77,21 @@ bool WalletTransferDetails::getTransferDetailsIdxByKeyImage(const crypto::key_im return true; } -bool WalletTransferDetails::isTxSpendtimeUnlocked(uint64_t unlockTime) const -{ - if(unlockTime < CRYPTONOTE_MAX_BLOCK_NUMBER) { - //interpret as block index - if(m_blockchain.size()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlockTime) - return true; - else - return false; - } - else - { - //interpret as time +bool WalletTransferDetails::isTxSpendtimeUnlocked(uint64_t unlockTime) const { + if (unlockTime < m_currency.maxBlockHeight()) { + // interpret as block index + return m_blockchain.size()-1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlockTime; + } else { + // interpret as time uint64_t current_time = static_cast(time(NULL)); - if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlockTime) - return true; - else - return false; + return current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlockTime; } return false; } bool WalletTransferDetails::isTransferUnlocked(const TransferDetails& td) const { - if(!isTxSpendtimeUnlocked(td.tx.unlock_time)) + if(!isTxSpendtimeUnlocked(td.tx.unlockTime)) return false; if(td.blockHeight + DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size()) diff --git a/src/wallet/WalletTransferDetails.h b/src/wallet/WalletTransferDetails.h index 911b4d498f..1f9ce4e956 100644 --- a/src/wallet/WalletTransferDetails.h +++ b/src/wallet/WalletTransferDetails.h @@ -20,6 +20,7 @@ #include #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" #include "IWallet.h" namespace CryptoNote { @@ -27,7 +28,7 @@ namespace CryptoNote { struct TransferDetails { uint64_t blockHeight; - cryptonote::transaction tx; + cryptonote::Transaction tx; size_t internalOutputIndex; uint64_t globalOutputIndex; bool spent; @@ -42,7 +43,7 @@ struct TransferDetails class WalletTransferDetails { public: - WalletTransferDetails(const std::vector& blockchain); + WalletTransferDetails(const cryptonote::Currency& currency, const std::vector& blockchain); ~WalletTransferDetails(); TransferDetails& getTransferDetails(size_t idx); @@ -72,6 +73,7 @@ class WalletTransferDetails typedef std::unordered_map KeyImagesContainer; KeyImagesContainer m_keyImages; + const cryptonote::Currency& m_currency; const std::vector& m_blockchain; }; diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index 0ac37f63bd..b0ed23dfac 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -35,7 +35,7 @@ void WalletUnconfirmedTransactions::erase(const crypto::hash& hash) { m_unconfirmedTxs.erase(hash); } -void WalletUnconfirmedTransactions::add(const cryptonote::transaction& tx, +void WalletUnconfirmedTransactions::add(const cryptonote::Transaction& tx, TransactionId transactionId, uint64_t change_amount) { UnconfirmedTransferDetails& utd = m_unconfirmedTxs[cryptonote::get_transaction_hash(tx)]; diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index addcfffa29..67275dece0 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -29,7 +29,7 @@ namespace CryptoNote { struct UnconfirmedTransferDetails { - cryptonote::transaction tx; + cryptonote::Transaction tx; uint64_t change; time_t sentTime; TransactionId transactionId; @@ -46,7 +46,7 @@ class WalletUnconfirmedTransactions bool findTransactionId(const crypto::hash& hash, TransactionId& id); void erase(const crypto::hash& hash); - void add(const cryptonote::transaction& tx, TransactionId transactionId, uint64_t change_amount); + void add(const cryptonote::Transaction& tx, TransactionId transactionId, uint64_t change_amount); uint64_t countPendingBalance() const; diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index f14cf9a88a..cb46c0c64a 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -41,7 +41,7 @@ TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferI { TransactionId id; for (id = 0; id < m_transactions.size(); ++id) { - const Transaction& tx = m_transactions[id]; + const TransactionInfo& tx = m_transactions[id]; if (tx.firstTransferId == INVALID_TRANSFER_ID || tx.transferCount == 0) continue; @@ -56,7 +56,7 @@ TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferI return id; } -bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, Transaction& transaction) const +bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, TransactionInfo& transaction) const { if (transactionId >= m_transactions.size()) return false; @@ -76,13 +76,13 @@ bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& t return true; } -TransactionId WalletUserTransactionsCache::insertTransaction(Transaction&& transaction) { - m_transactions.emplace_back(transaction); +TransactionId WalletUserTransactionsCache::insertTransaction(TransactionInfo&& Transaction) { + m_transactions.emplace_back(Transaction); return m_transactions.size() - 1; } TransactionId WalletUserTransactionsCache::findTransactionByHash(const crypto::hash& hash) { - auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const Transaction& tx) { return hashesEqual(tx.hash, hash); }); + auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const TransactionInfo& tx) { return hashesEqual(tx.hash, hash); }); if (it == m_transactions.end()) return CryptoNote::INVALID_TRANSACTION_ID; @@ -92,7 +92,7 @@ TransactionId WalletUserTransactionsCache::findTransactionByHash(const crypto::h void WalletUserTransactionsCache::detachTransactions(uint64_t height) { for (size_t id = 0; id < m_transactions.size(); ++id) { - Transaction& tx = m_transactions[id]; + TransactionInfo& tx = m_transactions[id]; if (tx.blockHeight >= height) { tx.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; tx.timestamp = 0; @@ -100,7 +100,7 @@ void WalletUserTransactionsCache::detachTransactions(uint64_t height) { } } -Transaction& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { +TransactionInfo& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { return m_transactions.at(transactionId); } @@ -116,7 +116,7 @@ void WalletUserTransactionsCache::getGoodItems(bool saveDetailed, UserTransactio } else { - const Transaction& t = m_transactions[txId]; + const TransactionInfo& t = m_transactions[txId]; if (t.firstTransferId != INVALID_TRANSFER_ID) offset += t.transferCount; } @@ -125,7 +125,7 @@ void WalletUserTransactionsCache::getGoodItems(bool saveDetailed, UserTransactio void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) { transactions.push_back(m_transactions[txId]); - Transaction& tx = transactions.back(); + TransactionInfo& tx = transactions.back(); if (!saveDetailed) { tx.firstTransferId = INVALID_TRANSFER_ID; @@ -157,7 +157,7 @@ void WalletUserTransactionsCache::getGoodTransfers(UserTransfers& transfers) { } void WalletUserTransactionsCache::getTransfersByTx(TransactionId id, UserTransfers& transfers) { - const Transaction& tx = m_transactions[id]; + const TransactionInfo& tx = m_transactions[id]; if (tx.firstTransferId != INVALID_TRANSFER_ID) { UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId; diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index dea8516747..11644c8df0 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -39,12 +39,12 @@ class WalletUserTransactionsCache TransactionId findTransactionByTransferId(TransferId transferId) const; - bool getTransaction(TransactionId transactionId, Transaction& transaction) const; - Transaction& getTransaction(TransactionId transactionId); + bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) const; + TransactionInfo& getTransaction(TransactionId transactionId); bool getTransfer(TransferId transferId, Transfer& transfer) const; Transfer& getTransfer(TransferId transferId); - TransactionId insertTransaction(Transaction&& transaction); + TransactionId insertTransaction(TransactionInfo&& Transaction); TransferId insertTransfers(const std::vector& transfers); TransactionId findTransactionByHash(const crypto::hash& hash); @@ -52,7 +52,7 @@ class WalletUserTransactionsCache private: typedef std::vector UserTransfers; - typedef std::vector UserTransactions; + typedef std::vector UserTransactions; void getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers); void getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f737289de2..f7dc057816 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -15,25 +15,30 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include -#include +#include "wallet2.h" +#include + +#include +#include #include + +// epee #include "include_base_utils.h" -using namespace epee; +#include "misc_language.h" +#include "profile_tools.h" -#include "wallet2.h" +#include "common/boost_serialization_helper.h" +#include "crypto/crypto.h" +#include "cryptonote_core/AccountKVSerialization.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_protocol/blobdatatype.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "misc_language.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "common/boost_serialization_helper.h" -#include "profile_tools.h" -#include "crypto/crypto.h" #include "serialization/binary_utils.h" using namespace cryptonote; +using namespace epee; namespace { @@ -55,17 +60,13 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file, namespace tools { //---------------------------------------------------------------------------------------------------- -void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) +void wallet2::init(const std::string& daemon_address) { - m_upper_transaction_size_limit = upper_transaction_size_limit; m_daemon_address = daemon_address; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height) -{ +bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id) { process_unconfirmed(tx); - std::vector outs; - uint64_t tx_money_got_in_outs = 0; std::vector tx_extra_fields; if(!parse_tx_extra(tx.extra, tx_extra_fields)) @@ -80,14 +81,26 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << get_transaction_hash(tx)); if(0 != m_callback) m_callback->on_skip_transaction(height, tx); - return; + return false; } - crypto::public_key tx_pub_key = pub_key_field.pub_key; - bool r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs); - THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); + TxItem txItem = { tx, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) }; + queue.push(std::unique_ptr(new TxItem(std::move(txItem)))); + return true; +} + +//---------------------------------------------------------------------------------------------------- +void wallet2::processCheckedTransaction(const TxItem& item) { + + const cryptonote::Transaction& tx = item.tx; + const std::vector& outs = item.outs; + const std::vector& tx_extra_fields = item.txExtraFields; + + uint64_t tx_money_got_in_outs = item.txMoneyGotInOuts; + uint64_t height = item.height; + - if(!outs.empty() && tx_money_got_in_outs) + if (!outs.empty() && tx_money_got_in_outs) { //good news - got money! take care about it //usually we have only one transfer for user in transaction @@ -102,8 +115,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ "transactions outputs size=" + std::to_string(tx.vout.size()) + " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size())); - BOOST_FOREACH(size_t o, outs) - { + for (size_t o : outs) { THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" + std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size())); @@ -114,15 +126,16 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ td.m_global_output_index = res.o_indexes[o]; td.m_tx = tx; td.m_spent = false; - cryptonote::keypair in_ephemeral; - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.m_key_image); - THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(tx.vout[o].target).key, + cryptonote::KeyPair in_ephemeral; + cryptonote::generate_key_image_helper(m_account.get_keys(), item.txPubKey, o, in_ephemeral, td.m_key_image); + THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(tx.vout[o].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - m_key_images[td.m_key_image] = m_transfers.size()-1; - LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx)); - if (0 != m_callback) + m_key_images[td.m_key_image] = m_transfers.size() - 1; + LOG_PRINT_L0("Received money: " << m_currency.formatAmount(td.amount()) << ", with tx: " << get_transaction_hash(tx)); + if (0 != m_callback) { m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); + } } } @@ -130,17 +143,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ // check all outputs for spending (compare key images) BOOST_FOREACH(auto& in, tx.vin) { - if(in.type() != typeid(cryptonote::txin_to_key)) - continue; - auto it = m_key_images.find(boost::get(in).k_image); - if(it != m_key_images.end()) - { - LOG_PRINT_L0("Spent money: " << print_money(boost::get(in).amount) << ", with tx: " << get_transaction_hash(tx)); - tx_money_spent_in_ins += boost::get(in).amount; - transfer_details& td = m_transfers[it->second]; - td.m_spent = true; - if (0 != m_callback) - m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx); + if (in.type() == typeid(cryptonote::TransactionInputToKey)) { + auto it = m_key_images.find(boost::get(in).keyImage); + if (it != m_key_images.end()) { + LOG_PRINT_L0("Spent money: " << m_currency.formatAmount(boost::get(in).amount) << + ", with tx: " << get_transaction_hash(tx)); + tx_money_spent_in_ins += boost::get(in).amount; + transfer_details& td = m_transfers[it->second]; + td.m_spent = true; + if (0 != m_callback) + m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx); + } } } @@ -148,65 +161,104 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { crypto::hash payment_id; - if(get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) { uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; if (0 < received && null_hash != payment_id) { payment_details payment; - payment.m_tx_hash = cryptonote::get_transaction_hash(tx); - payment.m_amount = received; + payment.m_tx_hash = cryptonote::get_transaction_hash(tx); + payment.m_amount = received; payment.m_block_height = height; - payment.m_unlock_time = tx.unlock_time; + payment.m_unlock_time = tx.unlockTime; m_payments.emplace(payment_id, payment); LOG_PRINT_L2("Payment found: " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); } } } + } + //---------------------------------------------------------------------------------------------------- -void wallet2::process_unconfirmed(const cryptonote::transaction& tx) +void wallet2::process_unconfirmed(const cryptonote::Transaction& tx) { auto unconf_it = m_unconfirmed_txs.find(get_transaction_hash(tx)); if(unconf_it != m_unconfirmed_txs.end()) m_unconfirmed_txs.erase(unconf_it); } + //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height) +bool wallet2::addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t current_index) { - //handle transactions from new block - THROW_WALLET_EXCEPTION_IF(height != m_blockchain.size(), error::wallet_internal_error, - "current_index=" + std::to_string(height) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size())); + if (current_index < m_blockchain.size()) { + 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); + } else { + LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id)); + return false; + } + } + + THROW_WALLET_EXCEPTION_IF(current_index != m_blockchain.size(), error::wallet_internal_error, + "current_index=" + std::to_string(current_index) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size())); - //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup - if(b.timestamp + 60*60*24 > m_account.get_createtime()) + m_blockchain.push_back(bl_id); + ++m_local_bc_height; + + if (0 != m_callback) { + m_callback->on_new_block(current_index); + } + + return true; +} + +size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height) +{ + size_t processedTransactions = 0; + + if (!bche.block.empty()) { - TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(b.miner_tx, height); - TIME_MEASURE_FINISH(miner_tx_handle_time); + cryptonote::Block b; + bool r = cryptonote::parse_and_validate_block_from_blob(bche.block, b); + THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bche.block); + + //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup + if (b.timestamp + 60 * 60 * 24 > m_account.get_createtime()) + { + TIME_MEASURE_START(miner_tx_handle_time); + if(processNewTransaction(queue, b.minerTx, height, bl_id)) + ++processedTransactions; + TIME_MEASURE_FINISH(miner_tx_handle_time); - TIME_MEASURE_START(txs_handle_time); - BOOST_FOREACH(auto& txblob, bche.txs) + TIME_MEASURE_START(txs_handle_time); + BOOST_FOREACH(auto& txblob, bche.txs) + { + cryptonote::Transaction tx; + bool r = parse_and_validate_tx_from_blob(txblob, tx); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); + if(processNewTransaction(queue, tx, height, bl_id)) + ++processedTransactions; + } + 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"); + } else { - cryptonote::transaction tx; - bool r = parse_and_validate_tx_from_blob(txblob, tx); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(tx, height); + LOG_PRINT_L2("Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime()); } - 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"); - }else - { - 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); - ++m_local_bc_height; - if (0 != m_callback) - m_callback->on_new_block(height, b); + return processedTransactions; } + + //---------------------------------------------------------------------------------------------------- -void wallet2::get_short_chain_history(std::list& ids) +void wallet2::get_short_chain_history(std::list& ids) const { size_t i = 0; size_t current_multiplier = 1; @@ -232,53 +284,109 @@ void wallet2::get_short_chain_history(std::list& ids) if(!genesis_included) ids.push_back(m_blockchain[0]); } + //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(size_t& blocks_added) +size_t wallet2::updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res) { - blocks_added = 0; - cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); - get_short_chain_history(req.block_ids); - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); - THROW_WALLET_EXCEPTION_IF(m_blockchain.size() <= res.start_height, error::wallet_internal_error, - "wrong daemon response: m_start_height=" + std::to_string(res.start_height) + - " not less than local blockchain size=" + std::to_string(m_blockchain.size())); - + size_t blocks_added = 0; size_t current_index = res.start_height; - BOOST_FOREACH(auto& bl_entry, res.blocks) - { - cryptonote::block bl; - r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); - THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block); - 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); + // update local blockchain + for (const auto& item : res.items) { + if (addNewBlockchainEntry(item.block_id, res.start_height, current_index)) ++blocks_added; - } - else if(bl_id != m_blockchain[current_index]) - { - //split detected here !!! - THROW_WALLET_EXCEPTION_IF(current_index == res.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(res.start_height) + "), local block id at this height: " + - string_tools::pod_to_hex(m_blockchain[current_index])); + ++current_index; + } - detach_blockchain(current_index); - process_new_blockchain_entry(bl, bl_entry, bl_id, current_index); - } - else - { - LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id)); + return blocks_added; +} + +//---------------------------------------------------------------------------------------------------- +void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res) +{ + size_t checkingThreads = std::thread::hardware_concurrency(); + + if (checkingThreads == 0) + checkingThreads = 4; + + std::vector> futures; + + TxQueue incomingQueue(checkingThreads * 2); + TxQueue checkedQueue(checkingThreads * 2); + + std::atomic inputTx(0); + std::atomic checkedTx(0); + + futures.push_back(std::async(std::launch::async, [&] { + try { + size_t current_index = res.start_height; + for (const auto& item : res.items) { + inputTx += processNewBlockchainEntry(incomingQueue, item, item.block_id, current_index); + ++current_index; + } + incomingQueue.close(); + } catch (...) { + LOG_ERROR("Exception in pushing thread!"); + incomingQueue.close(); + throw; } + })); - ++current_index; + GroupClose queueClose(checkedQueue, checkingThreads); + + for (size_t i = 0; i < checkingThreads; ++i) { + futures.push_back(std::async(std::launch::async, [&] { + TxQueueItem item; + while (incomingQueue.pop(item)) { + ++checkedTx; + lookup_acc_outs(m_account.get_keys(), item->tx, item->txPubKey, item->outs, item->txMoneyGotInOuts); + checkedQueue.push(std::move(item)); + } + queueClose.close(); + })); + } + + size_t txCount = 0; + + TxQueueItem item; + while (checkedQueue.pop(item)) { + processCheckedTransaction(*item); + ++txCount; + } + + for (auto& f : futures) { + f.get(); } + + if (checkedQueue.size() > 0 || incomingQueue.size() > 0) { + LOG_ERROR("ERROR! Queues not empty. Incoming: " << incomingQueue.size() << " Checked: " << checkedQueue.size()); + } + + if (inputTx != txCount) { + LOG_ERROR("Failed to process some transactions. Incoming: " << inputTx << " Processed: " << txCount); + } +} + +//---------------------------------------------------------------------------------------------------- +cryptonote::COMMAND_RPC_QUERY_BLOCKS::response wallet2::queryBlocks(epee::net_utils::http::http_simple_client& client) +{ + cryptonote::COMMAND_RPC_QUERY_BLOCKS::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_QUERY_BLOCKS::response res = AUTO_VAL_INIT(res); + + get_short_chain_history(req.block_ids); + req.timestamp = m_account.get_createtime() - 60 * 60 * 24; // get full blocks starting from wallet creation time minus 1 day + + bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/queryblocks.bin", req, res, client, WALLET_RCP_CONNECTION_TIMEOUT); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "queryblocks.bin"); + THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "queryblocks.bin"); + THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); + THROW_WALLET_EXCEPTION_IF(m_blockchain.size() <= res.start_height, error::wallet_internal_error, + "wrong daemon response: m_start_height=" + std::to_string(res.start_height) + + " not less than local blockchain size=" + std::to_string(m_blockchain.size())); + + return res; } + //---------------------------------------------------------------------------------------------------- void wallet2::refresh() { @@ -300,14 +408,38 @@ void wallet2::refresh(size_t & blocks_fetched, bool& received_money) size_t try_count = 0; crypto::hash last_tx_hash_id = m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash; + std::future processingTask; + + epee::net_utils::http::http_simple_client queryClient; + + auto r = connectClient(queryClient); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "refresh"); + + auto startTime = std::chrono::high_resolution_clock::now(); + while(m_run.load(std::memory_order_relaxed)) { try { - pull_blocks(added_blocks); - blocks_fetched += added_blocks; - if(!added_blocks) + auto res = std::make_shared(queryBlocks(queryClient)); + + if (processingTask.valid()) + processingTask.get(); // sync with transaction processing + + added_blocks = updateBlockchain(*res); + + if (!added_blocks) { break; + } + + blocks_fetched += added_blocks; + + bool hasFullBlocks = std::any_of(res->items.begin(), res->items.end(), + [](const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response_item& ri) { return !ri.block.empty(); }); + + if (hasFullBlocks) { + processingTask = std::async(std::launch::async, [res, this] { processTransactions(*res); }); + } } catch (const std::exception&) { @@ -324,10 +456,19 @@ void wallet2::refresh(size_t & blocks_fetched, bool& received_money) } } } + + if (processingTask.valid()) + processingTask.get(); + + auto duration = std::chrono::high_resolution_clock::now() - startTime; + if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash)) received_money = true; - LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance: " << print_money(balance()) << ", unlocked: " << print_money(unlocked_balance())); + LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << + ", balance: " << m_currency.formatAmount(balance()) << + ", unlocked: " << m_currency.formatAmount(unlocked_balance())); + LOG_PRINT_L1("Time: " << std::chrono::duration(duration).count() << "s"); } //---------------------------------------------------------------------------------------------------- bool wallet2::refresh(size_t & blocks_fetched, bool& received_money, bool& ok) @@ -385,9 +526,7 @@ bool wallet2::clear() { m_blockchain.clear(); m_transfers.clear(); - cryptonote::block b; - cryptonote::generate_genesis_block(b); - m_blockchain.push_back(get_block_hash(b)); + m_blockchain.push_back(m_currency.genesisBlockHash()); m_local_bc_height = 1; return true; } @@ -395,7 +534,8 @@ bool wallet2::clear() bool wallet2::store_keys(const std::string& keys_file_name, const std::string& password) { std::string account_data; - bool r = epee::serialization::store_t_to_binary(m_account, account_data); + AccountBaseSerializer accountSerializer(m_account); + bool r = epee::serialization::store_t_to_binary(accountSerializer, account_data); CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys"); wallet2::keys_file_data keys_file_data = boost::value_initialized(); @@ -443,9 +583,10 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); const cryptonote::account_keys& keys = m_account.get_keys(); - r = epee::serialization::load_t_from_binary(m_account, account_data); - r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); + AccountBaseSerializer accountSerializer(m_account); + r = epee::serialization::load_t_from_binary(accountSerializer, account_data); + r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey); + r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); } //---------------------------------------------------------------------------------------------------- @@ -464,9 +605,11 @@ void wallet2::generate(const std::string& wallet_, const std::string& password) bool r = store_keys(m_keys_file, password); 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()); + r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_currency.accountAddressAsString(m_account)); if(!r) LOG_PRINT_RED_L0("String with address text not saved"); + m_blockchain.push_back(m_currency.genesisBlockHash()); + store(); } //---------------------------------------------------------------------------------------------------- @@ -504,13 +647,18 @@ bool wallet2::check_connection() { if(m_http_client.is_connected()) return true; + return connectClient(m_http_client); +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::connectClient(epee::net_utils::http::http_simple_client& client) { net_utils::http::url_content u; net_utils::parse_url(m_daemon_address, u); - if(!u.port) + if (!u.port) u.port = RPC_DEFAULT_PORT; - return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); + return client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); } + //---------------------------------------------------------------------------------------------------- void wallet2::load(const std::string& wallet_, const std::string& password) { @@ -522,7 +670,7 @@ void wallet2::load(const std::string& wallet_, const std::string& password) THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file); load_keys(m_keys_file, password); - LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str()); + LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_currency.accountAddressAsString(m_account)); //keys loaded ok! //try to load wallet file. but even if we failed, it is not big problem @@ -530,21 +678,22 @@ void wallet2::load(const std::string& wallet_, const std::string& password) { LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain"); m_account_public_address = m_account.get_keys().m_account_address; - return; + } else { + bool r = tools::unserialize_obj_from_file(*this, m_wallet_file); + THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); + THROW_WALLET_EXCEPTION_IF( + m_account_public_address.m_spendPublicKey != m_account.get_keys().m_account_address.m_spendPublicKey || + m_account_public_address.m_viewPublicKey != m_account.get_keys().m_account_address.m_viewPublicKey, + error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); } - bool r = tools::unserialize_obj_from_file(*this, m_wallet_file); - THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); - THROW_WALLET_EXCEPTION_IF( - m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key || - m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key, - error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - - if(m_blockchain.empty()) - { - cryptonote::block b; - cryptonote::generate_genesis_block(b); - m_blockchain.push_back(get_block_hash(b)); + + if (m_blockchain.empty()) { + m_blockchain.push_back(m_currency.genesisBlockHash()); + } else { + THROW_WALLET_EXCEPTION_IF(m_blockchain[0] != m_currency.genesisBlockHash(), error::wallet_internal_error, + "Genesis block missmatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); } + m_local_bc_height = m_blockchain.size(); } //---------------------------------------------------------------------------------------------------- @@ -593,7 +742,7 @@ void wallet2::get_payments(const crypto::hash& payment_id, std::list m_blockchain.size()) @@ -602,23 +751,14 @@ bool wallet2::is_transfer_unlocked(const transfer_details& td) const return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time) const -{ - if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) - { - //interpret as block index - if(m_blockchain.size()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) - return true; - else - return false; - }else - { +bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time) const { + if (unlock_time < m_currency.maxBlockHeight()) { + // interpret as block index + return m_blockchain.size() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time; + } else { //interpret as time uint64_t current_time = static_cast(time(NULL)); - if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) - return true; - else - return false; + return current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlock_time; } return false; } @@ -681,7 +821,7 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_ return found_money; } //---------------------------------------------------------------------------------------------------- -void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount) +void wallet2::add_unconfirmed_tx(const cryptonote::Transaction& tx, uint64_t change_amount) { unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)]; utd.m_change = change_amount; @@ -690,15 +830,16 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t cha } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::Transaction& tx) { - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx); + transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, + tx_dust_policy(m_currency.defaultDustThreshold()), tx); } //---------------------------------------------------------------------------------------------------- void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra) { - cryptonote::transaction tx; + cryptonote::Transaction tx; transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 1447be5db4..dd911b3b4c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -23,9 +23,9 @@ #include #include "include_base_utils.h" -#include "cryptonote_core/account.h" #include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/Currency.h" #include "net/http_client.h" #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -33,6 +33,7 @@ #include "common/unordered_containers_boost_serialization.h" #include "crypto/chacha8.h" #include "crypto/hash.h" +#include "common/BlockingQueue.h" #include "wallet_errors.h" @@ -44,19 +45,20 @@ namespace tools class i_wallet2_callback { public: - virtual void on_new_block(uint64_t height, const cryptonote::block& block) {} - virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) {} - virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) {} - virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {} + virtual void on_new_block(uint64_t height) {} + virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index) {} + virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx) {} + virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx) {} }; struct tx_dust_policy { uint64_t dust_threshold; bool add_to_fee; - cryptonote::account_public_address addr_for_dust; + cryptonote::AccountPublicAddress addr_for_dust; - tx_dust_policy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address()) + tx_dust_policy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, + cryptonote::AccountPublicAddress an_addr_for_dust = cryptonote::AccountPublicAddress()) : dust_threshold(a_dust_threshold) , add_to_fee(an_add_to_fee) , addr_for_dust(an_addr_for_dust) @@ -64,15 +66,24 @@ namespace tools } }; - class wallet2 - { - wallet2(const wallet2&) : m_run(true), m_callback(0) {}; + class wallet2 { + wallet2(const wallet2& rhs) : + m_currency(rhs.m_currency), + m_run(true), + m_callback(0), + m_upper_transaction_size_limit(rhs.m_upper_transaction_size_limit) { + }; + public: - wallet2() : m_run(true), m_callback(0) {}; + wallet2(const cryptonote::Currency& currency) : + m_currency(currency), m_run(true), m_callback(0) { + m_upper_transaction_size_limit = (m_currency.blockGrantedFullRewardZone() * 125) / 100 - m_currency.minerTxBlobReservedSize(); + } + struct transfer_details { uint64_t m_block_height; - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; size_t m_internal_output_index; uint64_t m_global_output_index; bool m_spent; @@ -91,7 +102,7 @@ namespace tools struct unconfirmed_transfer_details { - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; uint64_t m_change; time_t m_sent_time; }; @@ -115,11 +126,13 @@ namespace tools void store(); cryptonote::account_base& get_account(){return m_account;} - void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + void init(const std::string& daemon_address = "http://localhost:8080"); bool deinit(); void stop() { m_run.store(false, std::memory_order_relaxed); } + const cryptonote::Currency currency() const { return m_currency; } + i_wallet2_callback* callback() const { return m_callback; } void callback(i_wallet2_callback* callback) { m_callback = callback; } @@ -133,10 +146,11 @@ namespace tools template void transfer(const std::vector& dsts, size_t fake_outputs_count, 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, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx); + void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::Transaction &tx); void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra); - void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx); + void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::Transaction& tx); bool check_connection(); + bool connectClient(epee::net_utils::http::http_simple_client& client); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments) const; uint64_t get_blockchain_current_height() const { return m_local_bc_height; } @@ -163,19 +177,57 @@ namespace tools private: bool store_keys(const std::string& keys_file_name, const std::string& password); void load_keys(const std::string& keys_file_name, const std::string& password); - void process_new_transaction(const cryptonote::transaction& tx, uint64_t height); - void process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height); + + struct TxItem { + cryptonote::Transaction tx; + uint64_t height; + crypto::hash blockId; + crypto::public_key txPubKey; + std::vector txExtraFields; + + // resolved + std::vector outs; + uint64_t txMoneyGotInOuts; + }; + + typedef std::unique_ptr TxQueueItem; + typedef BlockingQueue TxQueue; + + bool addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t height); + + size_t processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height); + bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id); + void processCheckedTransaction(const TxItem& item); + + // returns number of blocks added + size_t updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res); + void processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res); + cryptonote::COMMAND_RPC_QUERY_BLOCKS::response queryBlocks(epee::net_utils::http::http_simple_client& client); + void detach_blockchain(uint64_t height); - void get_short_chain_history(std::list& ids); + void get_short_chain_history(std::list& ids) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; bool is_transfer_unlocked(const transfer_details& td) const; bool clear(); - void pull_blocks(size_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list& selected_transfers); bool prepare_file_names(const std::string& file_path); - void process_unconfirmed(const cryptonote::transaction& tx); - void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount); + void process_unconfirmed(const cryptonote::Transaction& tx); + void add_unconfirmed_tx(const cryptonote::Transaction& tx, uint64_t change_amount); + void checkGenesis(const crypto::hash& genesis_hash); //throws + + inline void print_source_entry(const cryptonote::tx_source_entry& src) { + std::string indexes; + std::for_each(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& s_e) { + indexes += std::to_string(s_e.first) + " "; + }); + LOG_PRINT_L0("amount=" << m_currency.formatAmount(src.amount) << + ", real_output=" << src.real_output << + ", real_output_in_tx_index=" << src.real_output_in_tx_index << + ", indexes: " << indexes); + } + private: + const cryptonote::Currency& m_currency; cryptonote::account_base m_account; std::string m_daemon_address; std::string m_wallet_file; @@ -188,7 +240,7 @@ namespace tools transfer_container m_transfers; payment_container m_payments; std::unordered_map m_key_images; - cryptonote::account_public_address m_account_public_address; + cryptonote::AccountPublicAddress m_account_public_address; uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value std::atomic m_run; @@ -288,26 +340,19 @@ namespace tools } } //---------------------------------------------------------------------------------------------------- - inline void print_source_entry(const cryptonote::tx_source_entry& src) - { - std::string indexes; - std::for_each(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& s_e) { indexes += boost::to_string(s_e.first) + " "; }); - LOG_PRINT_L0("amount=" << cryptonote::print_money(src.amount) << ", real_output=" < void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy) { - cryptonote::transaction tx; + cryptonote::Transaction tx; transfer(dsts, fake_outputs_count, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx); } template void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) + uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::Transaction &tx) { using namespace cryptonote; THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); @@ -317,7 +362,7 @@ namespace tools { 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); + THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, m_currency.publicAddressBase58Prefix(), dsts, fee); } std::list selected_transfers; @@ -393,12 +438,12 @@ namespace tools //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 = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; + real_oe.second = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; 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; - detail::print_source_entry(src); + print_source_entry(src); ++i; } @@ -420,14 +465,14 @@ namespace tools } bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time); + THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, m_currency.publicAddressBase58Prefix(), sources, splitted_dsts, unlock_time); THROW_WALLET_EXCEPTION_IF(m_upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_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 + bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const TransactionInput& s_e) -> bool { - CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false); - key_images += boost::to_string(in.k_image) + " "; + CHECKED_GET_SPECIFIC_VARIANT(s_e, const TransactionInputToKey, in, false); + key_images += boost::to_string(in.keyImage) + " "; return true; }); THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx); @@ -448,9 +493,9 @@ namespace tools it->m_spent = true; LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL - << "Commission: " << print_money(fee+dust) << " (dust: " << print_money(dust) << ")" << ENDL - << "Balance: " << print_money(balance()) << ENDL - << "Unlocked: " << print_money(unlocked_balance()) << ENDL + << "Commission: " << m_currency.formatAmount(fee + dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL + << "Balance: " << m_currency.formatAmount(balance()) << ENDL + << "Unlocked: " << m_currency.formatAmount(unlocked_balance()) << ENDL << "Please, wait for confirmation for your balance to be unlocked."); } } diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 4ae8c9bb81..47b1a5812c 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -140,7 +140,7 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct unexpected_txin_type : public wallet_internal_error { - explicit unexpected_txin_type(std::string&& loc, const cryptonote::transaction& tx) + explicit unexpected_txin_type(std::string&& loc, const cryptonote::Transaction& tx) : wallet_internal_error(std::move(loc), "one of tx inputs has unexpected type") , m_tx(tx) { @@ -148,18 +148,17 @@ namespace tools ~unexpected_txin_type() throw() { } - const cryptonote::transaction& tx() const { return m_tx; } + const cryptonote::Transaction& tx() const { return m_tx; } std::string to_string() const { std::ostringstream ss; - cryptonote::transaction tx = m_tx; - ss << wallet_internal_error::to_string() << ", tx:\n" << cryptonote::obj_to_json_str(tx); + ss << wallet_internal_error::to_string() << ", tx:\n" << cryptonote::obj_to_json_str(m_tx); return ss.str(); } private: - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; }; //---------------------------------------------------------------------------------------------------- const char* const file_error_messages[] = { @@ -222,7 +221,7 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct acc_outs_lookup_error : public refresh_error { - explicit acc_outs_lookup_error(std::string&& loc, const cryptonote::transaction& tx, + explicit acc_outs_lookup_error(std::string&& loc, const cryptonote::Transaction& tx, const crypto::public_key& tx_pub_key, const cryptonote::account_keys& acc_keys) : refresh_error(std::move(loc), "account outs lookup error") , m_tx(tx) @@ -233,20 +232,19 @@ namespace tools ~acc_outs_lookup_error() throw() { } - const cryptonote::transaction& tx() const { return m_tx; } + const cryptonote::Transaction& tx() const { return m_tx; } const crypto::public_key& tx_pub_key() const { return m_tx_pub_key; } const cryptonote::account_keys& acc_keys() const { return m_acc_keys; } std::string to_string() const { std::ostringstream ss; - cryptonote::transaction tx = m_tx; - ss << refresh_error::to_string() << ", tx: " << cryptonote::obj_to_json_str(tx); + ss << refresh_error::to_string() << ", tx: " << cryptonote::obj_to_json_str(m_tx); return ss.str(); } private: - const cryptonote::transaction m_tx; + const cryptonote::Transaction m_tx; const crypto::public_key m_tx_pub_key; const cryptonote::account_keys m_acc_keys; }; @@ -320,9 +318,9 @@ namespace tools { std::ostringstream ss; ss << transfer_error::to_string() << - ", available = " << cryptonote::print_money(m_available) << - ", tx_amount = " << cryptonote::print_money(m_tx_amount) << - ", fee = " << cryptonote::print_money(m_fee); + ", available = " << m_available << + ", tx_amount = " << m_tx_amount << + ", fee = " << m_fee; return ss.str(); } @@ -354,7 +352,7 @@ namespace tools ss << transfer_error::to_string() << ", mixin_count = " << m_mixin_count << ", scanty_outs:"; for (const auto& outs_for_amount : m_scanty_outs) { - ss << '\n' << cryptonote::print_money(outs_for_amount.amount) << " - " << outs_for_amount.outs.size(); + ss << '\n' << outs_for_amount.amount << " - " << outs_for_amount.outs.size(); } return ss.str(); } @@ -369,8 +367,10 @@ namespace tools typedef std::vector sources_t; typedef std::vector destinations_t; - explicit tx_not_constructed(std::string&& loc, const sources_t& sources, const destinations_t& destinations, uint64_t unlock_time) + explicit tx_not_constructed(std::string&& loc, uint64_t addressPrefix, const sources_t& sources, + const destinations_t& destinations, uint64_t unlock_time) : transfer_error(std::move(loc), "transaction was not constructed") + , m_addressPrefix(addressPrefix) , m_sources(sources) , m_destinations(destinations) , m_unlock_time(unlock_time) @@ -392,7 +392,7 @@ namespace tools { const cryptonote::tx_source_entry& src = m_sources[i]; ss << "\n source " << i << ":"; - ss << "\n amount: " << cryptonote::print_money(src.amount); + ss << "\n amount: " << src.amount; // It's not good, if logs will contain such much data //ss << "\n real_output: " << src.real_output; //ss << "\n real_output_in_tx_index: " << src.real_output_in_tx_index; @@ -409,8 +409,7 @@ namespace tools for (size_t i = 0; i < m_destinations.size(); ++i) { const cryptonote::tx_destination_entry& dst = m_destinations[i]; - ss << "\n " << i << ": " << cryptonote::get_account_address_as_str(dst.addr) << " " << - cryptonote::print_money(dst.amount); + ss << "\n " << i << ": " << getAccountAddressAsStr(m_addressPrefix, dst.addr) << ' ' << dst.amount; } ss << "\nunlock_time: " << m_unlock_time; @@ -419,6 +418,7 @@ namespace tools } private: + uint64_t m_addressPrefix; sources_t m_sources; destinations_t m_destinations; uint64_t m_unlock_time; @@ -426,7 +426,7 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_rejected : public transfer_error { - explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, const std::string& status) + explicit tx_rejected(std::string&& loc, const cryptonote::Transaction& tx, const std::string& status) : transfer_error(std::move(loc), "transaction was rejected by daemon") , m_tx(tx) , m_status(status) @@ -435,27 +435,29 @@ namespace tools ~tx_rejected() throw() { } - const cryptonote::transaction& tx() const { return m_tx; } + const cryptonote::Transaction& tx() const { return m_tx; } const std::string& status() const { return m_status; } std::string to_string() const { std::ostringstream ss; ss << transfer_error::to_string() << ", status = " << m_status << ", tx:\n"; - cryptonote::transaction tx = m_tx; - ss << cryptonote::obj_to_json_str(tx); + ss << cryptonote::obj_to_json_str(m_tx); return ss.str(); } private: - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; std::string m_status; }; //---------------------------------------------------------------------------------------------------- struct tx_sum_overflow : public transfer_error { - explicit tx_sum_overflow(std::string&& loc, const std::vector& destinations, uint64_t fee) - : transfer_error(std::move(loc), "transaction sum + fee exceeds " + cryptonote::print_money(std::numeric_limits::max())) + typedef std::vector destinations_t; + + explicit tx_sum_overflow(std::string&& loc, uint64_t addressPrefix, const destinations_t& destinations, uint64_t fee) + : transfer_error(std::move(loc), "transaction sum + fee exceeds " + std::to_string(std::numeric_limits::max())) + , m_addressPrefix(addressPrefix) , m_destinations(destinations) , m_fee(fee) { @@ -470,23 +472,24 @@ namespace tools { std::ostringstream ss; ss << transfer_error::to_string() << - ", fee = " << cryptonote::print_money(m_fee) << + ", fee = " << m_fee << ", destinations:"; for (const auto& dst : m_destinations) { - ss << '\n' << cryptonote::print_money(dst.amount) << " -> " << cryptonote::get_account_address_as_str(dst.addr); + ss << '\n' << dst.amount << " -> " << getAccountAddressAsStr(m_addressPrefix, dst.addr); } return ss.str(); } private: - std::vector m_destinations; + uint64_t m_addressPrefix; + destinations_t m_destinations; uint64_t m_fee; }; //---------------------------------------------------------------------------------------------------- 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_size_limit) : transfer_error(std::move(loc), "transaction is too big") , m_tx(tx) , m_tx_size_limit(tx_size_limit) @@ -495,22 +498,21 @@ namespace tools ~tx_too_big() throw() { } - const cryptonote::transaction& tx() const { return m_tx; } + const cryptonote::Transaction& tx() const { return m_tx; } uint64_t tx_size_limit() const { return m_tx_size_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:\n" << cryptonote::obj_to_json_str(tx); + ", tx:\n" << cryptonote::obj_to_json_str(m_tx); return ss.str(); } private: - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; uint64_t m_tx_size_limit; }; //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ea446a4e5e..de783cbb7f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -44,7 +44,11 @@ namespace tools bool wallet_rpc_server::run() { m_net_server.add_idle_handler([this](){ - m_wallet.refresh(); + try { + m_wallet.refresh(); + } catch (const std::exception& ex) { + LOG_ERROR("Exception at while refreshing, what=" << ex.what()); + } return true; }, 20000); @@ -88,7 +92,7 @@ namespace tools std::vector dsts; for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { cryptonote::tx_destination_entry de; - if (!get_account_address_from_str(de.addr, it->address)) { + if (!m_wallet.currency().parseAccountAddressString(it->address, de.addr)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; return false; @@ -118,7 +122,7 @@ namespace tools } try { - cryptonote::transaction tx; + cryptonote::Transaction tx; m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx); res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(tx)); return true; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 35beea1426..de7ae5ff58 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ file(GLOB_RECURSE CRYPTO_TESTS crypto/*) file(GLOB_RECURSE FUNC_TESTS functional_tests/*) file(GLOB_RECURSE PERFORMANCE_TESTS performance_tests/*) file(GLOB_RECURSE CORE_PROXY core_proxy/*) +file(GLOB_RECURSE TEST_GENERATOR TestGenerator/*) file(GLOB_RECURSE UNIT_TESTS unit_tests/*) source_group(core_tests FILES ${CORE_TESTS}) @@ -15,11 +16,12 @@ source_group(crypto_tests FILES ${CRYPTO_TESTS}) source_group(functional_tests FILES ${FUNC_TESTS}) source_group(performance_tests FILES ${PERFORMANCE_TESTS}) source_group(core_proxy FILES ${CORE_PROXY}) +source_group(TestGenerator FILES ${TEST_GENERATOR}) source_group(unit_tests FILES ${UNIT_TESTS}) # add_subdirectory(daemon_tests) -add_library(coretests_lib ${CORE_TESTS}) +add_library(TestGenerator ${TEST_GENERATOR}) add_executable(coretests ${CORE_TESTS}) add_executable(crypto-tests ${CRYPTO_TESTS}) @@ -33,31 +35,30 @@ add_executable(unit_tests ${UNIT_TESTS}) add_executable(net_load_tests_clt net_load_tests/clt.cpp) add_executable(net_load_tests_srv net_load_tests/srv.cpp) -target_link_libraries(core_proxy cryptonote_core common crypto upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(coretests cryptonote_core common crypto ${Boost_LIBRARIES}) -target_link_libraries(difficulty-tests cryptonote_core) -target_link_libraries(functional_tests cryptonote_core wallet common crypto ${Boost_LIBRARIES}) +target_link_libraries(core_proxy epee cryptonote_core common crypto upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(coretests epee cryptonote_core common crypto TestGenerator ${Boost_LIBRARIES}) +target_link_libraries(difficulty-tests epee common crypto cryptonote_core ${Boost_LIBRARIES}) +target_link_libraries(functional_tests epee cryptonote_core wallet common crypto ${Boost_LIBRARIES}) target_link_libraries(hash-tests crypto) -target_link_libraries(hash-target-tests crypto cryptonote_core) -target_link_libraries(performance_tests cryptonote_core common crypto ${Boost_LIBRARIES}) -target_link_libraries(unit_tests cryptonote_core common crypto wallet coretests_lib gtest_main ${Boost_LIBRARIES}) -target_link_libraries(net_load_tests_clt cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) -target_link_libraries(net_load_tests_srv cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) - - +target_link_libraries(hash-target-tests epee crypto cryptonote_core) +target_link_libraries(performance_tests epee cryptonote_core common crypto ${Boost_LIBRARIES}) +target_link_libraries(unit_tests epee wallet TestGenerator cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) +target_link_libraries(net_load_tests_clt epee cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) +target_link_libraries(net_load_tests_srv epee cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) file(GLOB_RECURSE NODE_RPC_PROXY_TEST node_rpc_proxy_test/*) source_group(node_rpc_proxy_test FILES ${NODE_RPC_PROXY_TEST}) add_executable(node_rpc_proxy_test ${NODE_RPC_PROXY_TEST}) -target_link_libraries(node_rpc_proxy_test node_rpc_proxy cryptonote_core common crypto ${Boost_LIBRARIES}) - +target_link_libraries(node_rpc_proxy_test epee node_rpc_proxy cryptonote_core common crypto ${Boost_LIBRARIES}) if(NOT MSVC) - set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") + set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv TestGenerator APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") endif() add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests node_rpc_proxy_test) -set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test PROPERTY FOLDER "tests") +set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test TestGenerator PROPERTY FOLDER "tests") + +add_dependencies(core_proxy version) add_test(coretests coretests --generate_and_play_test_data) add_test(crypto crypto-tests ${CMAKE_CURRENT_SOURCE_DIR}/crypto/tests.txt) diff --git a/tests/TestGenerator/TestGenerator.cpp b/tests/TestGenerator/TestGenerator.cpp new file mode 100644 index 0000000000..2fd6818ef7 --- /dev/null +++ b/tests/TestGenerator/TestGenerator.cpp @@ -0,0 +1,367 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TestGenerator.h" + +// epee +#include "misc_language.h" + +#include "cryptonote_core/account.h" +#include "cryptonote_core/miner.h" + +using namespace std; + +using namespace epee; +using namespace cryptonote; + + +void test_generator::getBlockchain(std::vector& blockchain, const crypto::hash& head, size_t n) const { + crypto::hash curr = head; + while (null_hash != curr && blockchain.size() < n) { + auto it = m_blocksInfo.find(curr); + if (m_blocksInfo.end() == it) { + throw std::runtime_error("block hash wasn't found"); + } + + blockchain.push_back(it->second); + curr = it->second.prevId; + } + + std::reverse(blockchain.begin(), blockchain.end()); +} + +void test_generator::getLastNBlockSizes(std::vector& blockSizes, const crypto::hash& head, size_t n) const { + std::vector blockchain; + getBlockchain(blockchain, head, n); + for (auto& bi : blockchain) { + blockSizes.push_back(bi.blockSize); + } +} + +uint64_t test_generator::getAlreadyGeneratedCoins(const crypto::hash& blockId) const { + auto it = m_blocksInfo.find(blockId); + if (it == m_blocksInfo.end()) { + throw std::runtime_error("block hash wasn't found"); + } + + return it->second.alreadyGeneratedCoins; +} + +uint64_t test_generator::getAlreadyGeneratedCoins(const cryptonote::Block& blk) const { + crypto::hash blkHash; + get_block_hash(blk, blkHash); + return getAlreadyGeneratedCoins(blkHash); +} + +void test_generator::addBlock(const cryptonote::Block& blk, size_t tsxSize, uint64_t fee, + std::vector& blockSizes, uint64_t alreadyGeneratedCoins) { + const size_t blockSize = tsxSize + get_object_blobsize(blk.minerTx); + int64_t emissionChange; + uint64_t blockReward; + bool penalizeFee = blk.majorVersion > BLOCK_MAJOR_VERSION_1; + m_currency.getBlockReward(misc_utils::median(blockSizes), blockSize, alreadyGeneratedCoins, fee, penalizeFee, + blockReward, emissionChange); + m_blocksInfo[get_block_hash(blk)] = BlockInfo(blk.prevId, alreadyGeneratedCoins + emissionChange, blockSize); +} + +bool test_generator::constructBlock(cryptonote::Block& blk, uint64_t height, const crypto::hash& prevId, + const cryptonote::account_base& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins, + std::vector& blockSizes, const std::list& txList) { + blk.majorVersion = defaultMajorVersion; + blk.minorVersion = defaultMinorVersion; + blk.timestamp = timestamp; + blk.prevId = prevId; + + blk.txHashes.reserve(txList.size()); + for (const Transaction &tx : txList) { + crypto::hash tx_hash; + get_transaction_hash(tx, tx_hash); + blk.txHashes.push_back(tx_hash); + } + + uint64_t totalFee = 0; + size_t txsSize = 0; + for (auto& tx : txList) { + uint64_t fee = 0; + bool r = get_tx_fee(tx, fee); + CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block"); + totalFee += fee; + txsSize += get_object_blobsize(tx); + } + + blk.minerTx = AUTO_VAL_INIT(blk.minerTx); + size_t targetBlockSize = txsSize + get_object_blobsize(blk.minerTx); + while (true) { + if (!m_currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, targetBlockSize, + totalFee, minerAcc.get_keys().m_account_address, blk.minerTx, blobdata(), 10)) { + return false; + } + + size_t actualBlockSize = txsSize + get_object_blobsize(blk.minerTx); + if (targetBlockSize < actualBlockSize) { + targetBlockSize = actualBlockSize; + } else if (actualBlockSize < targetBlockSize) { + size_t delta = targetBlockSize - actualBlockSize; + blk.minerTx.extra.resize(blk.minerTx.extra.size() + delta, 0); + actualBlockSize = txsSize + get_object_blobsize(blk.minerTx); + if (actualBlockSize == targetBlockSize) { + break; + } else { + CHECK_AND_ASSERT_MES(targetBlockSize < actualBlockSize, false, "Unexpected block size"); + delta = actualBlockSize - targetBlockSize; + blk.minerTx.extra.resize(blk.minerTx.extra.size() - delta); + actualBlockSize = txsSize + get_object_blobsize(blk.minerTx); + if (actualBlockSize == targetBlockSize) { + break; + } else { + CHECK_AND_ASSERT_MES(actualBlockSize < targetBlockSize, false, "Unexpected block size"); + blk.minerTx.extra.resize(blk.minerTx.extra.size() + delta, 0); + targetBlockSize = txsSize + get_object_blobsize(blk.minerTx); + } + } + } else { + break; + } + } + + if (blk.majorVersion >= BLOCK_MAJOR_VERSION_2) { + blk.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + blk.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; + blk.parentBlock.numberOfTransactions = 1; + + cryptonote::tx_extra_merge_mining_tag mmTag; + mmTag.depth = 0; + if (!cryptonote::get_aux_block_header_hash(blk, mmTag.merkle_root)) { + return false; + } + + blk.parentBlock.minerTx.extra.clear(); + if (!cryptonote::append_mm_tag_to_extra(blk.parentBlock.minerTx.extra, mmTag)) { + return false; + } + } + + // Nonce search... + blk.nonce = 0; + crypto::cn_context context; + while (!miner::find_nonce_for_given_block(context, blk, getTestDifficulty())) { + blk.timestamp++; + } + + addBlock(blk, txsSize, totalFee, blockSizes, alreadyGeneratedCoins); + + return true; +} + +bool test_generator::constructBlock(cryptonote::Block& blk, const cryptonote::account_base& minerAcc, uint64_t timestamp) { + std::vector blockSizes; + std::list txList; + return constructBlock(blk, 0, null_hash, minerAcc, timestamp, 0, blockSizes, txList); +} + +bool test_generator::constructBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, + const cryptonote::account_base& minerAcc, + const std::list& txList/* = std::list()*/) { + uint64_t height = boost::get(blkPrev.minerTx.vin.front()).height + 1; + crypto::hash prevId = get_block_hash(blkPrev); + // Keep difficulty unchanged + uint64_t timestamp = blkPrev.timestamp + m_currency.difficultyTarget(); + uint64_t alreadyGeneratedCoins = getAlreadyGeneratedCoins(prevId); + std::vector blockSizes; + getLastNBlockSizes(blockSizes, prevId, m_currency.rewardBlocksWindow()); + + return constructBlock(blk, height, prevId, minerAcc, timestamp, alreadyGeneratedCoins, blockSizes, txList); +} + +bool test_generator::constructBlockManually(Block& blk, const Block& prevBlock, const account_base& minerAcc, + int actualParams/* = bf_none*/, uint8_t majorVer/* = 0*/, + uint8_t minorVer/* = 0*/, uint64_t timestamp/* = 0*/, + const crypto::hash& prevId/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/, + const Transaction& minerTx/* = transaction()*/, + const std::vector& txHashes/* = std::vector()*/, + size_t txsSizes/* = 0*/, uint64_t fee/* = 0*/) { + blk.majorVersion = actualParams & bf_major_ver ? majorVer : defaultMajorVersion; + blk.minorVersion = actualParams & bf_minor_ver ? minorVer : defaultMinorVersion; + blk.timestamp = actualParams & bf_timestamp ? timestamp : prevBlock.timestamp + m_currency.difficultyTarget(); // Keep difficulty unchanged + blk.prevId = actualParams & bf_prev_id ? prevId : get_block_hash(prevBlock); + blk.txHashes = actualParams & bf_tx_hashes ? txHashes : std::vector(); + + size_t height = get_block_height(prevBlock) + 1; + uint64_t alreadyGeneratedCoins = getAlreadyGeneratedCoins(prevBlock); + std::vector blockSizes; + getLastNBlockSizes(blockSizes, get_block_hash(prevBlock), m_currency.rewardBlocksWindow()); + if (actualParams & bf_miner_tx) { + blk.minerTx = minerTx; + } else { + size_t currentBlockSize = txsSizes + get_object_blobsize(blk.minerTx); + // TODO: This will work, until size of constructed block is less then m_currency.blockGrantedFullRewardZone() + if (!m_currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, currentBlockSize, 0, + minerAcc.get_keys().m_account_address, blk.minerTx, blobdata(), 1, blk.majorVersion > BLOCK_MAJOR_VERSION_1)) { + return false; + } + } + + if (blk.majorVersion >= BLOCK_MAJOR_VERSION_2) { + blk.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + blk.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; + blk.parentBlock.numberOfTransactions = 1; + + cryptonote::tx_extra_merge_mining_tag mmTag; + mmTag.depth = 0; + if (!cryptonote::get_aux_block_header_hash(blk, mmTag.merkle_root)) { + return false; + } + + blk.parentBlock.minerTx.extra.clear(); + if (!cryptonote::append_mm_tag_to_extra(blk.parentBlock.minerTx.extra, mmTag)) { + return false; + } + } + + difficulty_type aDiffic = actualParams & bf_diffic ? diffic : getTestDifficulty(); + if (1 < aDiffic) { + fillNonce(blk, aDiffic); + } + + addBlock(blk, txsSizes, fee, blockSizes, alreadyGeneratedCoins); + + return true; +} + +bool test_generator::constructBlockManuallyTx(cryptonote::Block& blk, const cryptonote::Block& prevBlock, + const cryptonote::account_base& minerAcc, + const std::vector& txHashes, size_t txsSize) { + return constructBlockManually(blk, prevBlock, minerAcc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, Transaction(), + txHashes, txsSize); +} + +bool test_generator::constructMaxSizeBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, + const cryptonote::account_base& minerAccount, + size_t medianBlockCount/* = 0*/, + const std::list& txList/* = std::list()*/) { + std::vector blockSizes; + medianBlockCount = medianBlockCount == 0 ? m_currency.rewardBlocksWindow() : medianBlockCount; + getLastNBlockSizes(blockSizes, get_block_hash(blkPrev), medianBlockCount); + + size_t median = misc_utils::median(blockSizes); + median = std::max(median, m_currency.blockGrantedFullRewardZone()); + + uint64_t totalFee = 0; + size_t txsSize = 0; + std::vector txHashes; + for (auto& tx : txList) { + uint64_t fee = 0; + bool r = get_tx_fee(tx, fee); + CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_max_size_block"); + totalFee += fee; + txsSize += get_object_blobsize(tx); + txHashes.push_back(get_transaction_hash(tx)); + } + + Transaction minerTx; + bool r = constructMinerTxBySize(m_currency, minerTx, get_block_height(blkPrev) + 1, + getAlreadyGeneratedCoins(blkPrev), minerAccount.get_keys().m_account_address, blockSizes, + 2 * median - txsSize, 2 * median, totalFee, defaultMajorVersion > BLOCK_MAJOR_VERSION_1); + if (!r) { + return false; + } + + return constructBlockManually(blk, blkPrev, minerAccount, test_generator::bf_miner_tx | test_generator::bf_tx_hashes, + 0, 0, 0, crypto::hash(), 0, minerTx, txHashes, txsSize, totalFee); +} + +void fillNonce(cryptonote::Block& blk, const difficulty_type& diffic) { + blk.nonce = 0; + crypto::cn_context context; + while (!miner::find_nonce_for_given_block(context, blk, diffic)) { + blk.timestamp++; + } +} + +bool constructMinerTxManually(const cryptonote::Currency& currency, size_t height, uint64_t alreadyGeneratedCoins, + const AccountPublicAddress& minerAddress, Transaction& tx, uint64_t fee, + KeyPair* pTxKey/* = 0*/) { + KeyPair txkey; + txkey = KeyPair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + + if (0 != pTxKey) { + *pTxKey = txkey; + } + + TransactionInputGenerate in; + in.height = height; + tx.vin.push_back(in); + + // This will work, until size of constructed block is less then currency.blockGrantedFullRewardZone() + int64_t emissionChange; + uint64_t blockReward; + if (!currency.getBlockReward(0, 0, alreadyGeneratedCoins, fee, false, blockReward, emissionChange)) { + LOG_PRINT_L0("Block is too big"); + return false; + } + + crypto::key_derivation derivation; + crypto::public_key outEphPublicKey; + crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); + crypto::derive_public_key(derivation, 0, minerAddress.m_spendPublicKey, outEphPublicKey); + + TransactionOutput out; + out.amount = blockReward; + out.target = TransactionOutputToKey(outEphPublicKey); + tx.vout.push_back(out); + + tx.version = CURRENT_TRANSACTION_VERSION; + tx.unlockTime = height + currency.minedMoneyUnlockWindow(); + + return true; +} + +bool constructMinerTxBySize(const cryptonote::Currency& currency, cryptonote::Transaction& minerTx, uint64_t height, + uint64_t alreadyGeneratedCoins, const cryptonote::AccountPublicAddress& minerAddress, + std::vector& blockSizes, size_t targetTxSize, size_t targetBlockSize, + uint64_t fee/* = 0*/, bool penalizeFee/* = false*/) { + if (!currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, targetBlockSize, + fee, minerAddress, minerTx, cryptonote::blobdata(), 1, penalizeFee)) { + return false; + } + + size_t currentSize = get_object_blobsize(minerTx); + size_t tryCount = 0; + while (targetTxSize != currentSize) { + ++tryCount; + if (10 < tryCount) { + return false; + } + + if (targetTxSize < currentSize) { + size_t diff = currentSize - targetTxSize; + if (diff <= minerTx.extra.size()) { + minerTx.extra.resize(minerTx.extra.size() - diff); + } else { + return false; + } + } else { + size_t diff = targetTxSize - currentSize; + minerTx.extra.resize(minerTx.extra.size() + diff); + } + + currentSize = get_object_blobsize(minerTx); + } + + return true; +} diff --git a/tests/TestGenerator/TestGenerator.h b/tests/TestGenerator/TestGenerator.h new file mode 100644 index 0000000000..e7b4658504 --- /dev/null +++ b/tests/TestGenerator/TestGenerator.h @@ -0,0 +1,115 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include + +#include "crypto/hash.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/difficulty.h" + + +class test_generator +{ +public: + struct BlockInfo { + BlockInfo() + : prevId() + , alreadyGeneratedCoins(0) + , blockSize(0) { + } + + BlockInfo(crypto::hash aPrevId, uint64_t anAlreadyGeneratedCoins, size_t aBlockSize) + : prevId(aPrevId) + , alreadyGeneratedCoins(anAlreadyGeneratedCoins) + , blockSize(aBlockSize) { + } + + crypto::hash prevId; + uint64_t alreadyGeneratedCoins; + size_t blockSize; + }; + + enum BlockFields { + bf_none = 0, + bf_major_ver = 1 << 0, + bf_minor_ver = 1 << 1, + bf_timestamp = 1 << 2, + bf_prev_id = 1 << 3, + bf_miner_tx = 1 << 4, + bf_tx_hashes = 1 << 5, + bf_diffic = 1 << 6 + }; + + test_generator(const cryptonote::Currency& currency, uint8_t majorVersion = cryptonote::BLOCK_MAJOR_VERSION_1, + uint8_t minorVersion = cryptonote::BLOCK_MINOR_VERSION_0) + : m_currency(currency), defaultMajorVersion(majorVersion), defaultMinorVersion(minorVersion) { + } + + + uint8_t defaultMajorVersion; + uint8_t defaultMinorVersion; + + const cryptonote::Currency& currency() const { return m_currency; } + + void getBlockchain(std::vector& blockchain, const crypto::hash& head, size_t n) const; + void getLastNBlockSizes(std::vector& blockSizes, const crypto::hash& head, size_t n) const; + uint64_t getAlreadyGeneratedCoins(const crypto::hash& blockId) const; + uint64_t getAlreadyGeneratedCoins(const cryptonote::Block& blk) const; + + void addBlock(const cryptonote::Block& blk, size_t tsxSize, uint64_t fee, std::vector& blockSizes, + uint64_t alreadyGeneratedCoins); + bool constructBlock(cryptonote::Block& blk, uint64_t height, const crypto::hash& prevId, + const cryptonote::account_base& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins, + std::vector& blockSizes, const std::list& txList); + bool constructBlock(cryptonote::Block& blk, const cryptonote::account_base& minerAcc, uint64_t timestamp); + bool constructBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, const cryptonote::account_base& minerAcc, + const std::list& txList = std::list()); + + bool constructBlockManually(cryptonote::Block& blk, const cryptonote::Block& prevBlock, + const cryptonote::account_base& minerAcc, int actualParams = bf_none, uint8_t majorVer = 0, + uint8_t minorVer = 0, uint64_t timestamp = 0, const crypto::hash& prevId = crypto::hash(), + const cryptonote::difficulty_type& diffic = 1, const cryptonote::Transaction& minerTx = cryptonote::Transaction(), + const std::vector& txHashes = std::vector(), size_t txsSizes = 0, uint64_t fee = 0); + bool constructBlockManuallyTx(cryptonote::Block& blk, const cryptonote::Block& prevBlock, + const cryptonote::account_base& minerAcc, const std::vector& txHashes, size_t txsSize); + bool constructMaxSizeBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, + const cryptonote::account_base& minerAccount, size_t medianBlockCount = 0, + const std::list& txList = std::list()); + +private: + const cryptonote::Currency& m_currency; + std::unordered_map m_blocksInfo; +}; + +inline cryptonote::difficulty_type getTestDifficulty() { return 1; } +void fillNonce(cryptonote::Block& blk, const cryptonote::difficulty_type& diffic); + +bool constructMinerTxManually(const cryptonote::Currency& currency, size_t height, uint64_t alreadyGeneratedCoins, + const cryptonote::AccountPublicAddress& minerAddress, cryptonote::Transaction& tx, uint64_t fee, + cryptonote::KeyPair* pTxKey = 0); +bool constructMinerTxBySize(const cryptonote::Currency& currency, cryptonote::Transaction& minerTx, uint64_t height, + uint64_t alreadyGeneratedCoins, const cryptonote::AccountPublicAddress& minerAddress, + std::vector& blockSizes, size_t targetTxSize, size_t targetBlockSize, uint64_t fee = 0, + bool penalizeFee = false); diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index 8a179a942c..a2db0ca719 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -33,7 +33,6 @@ using namespace std; #include "common/command_line.h" #include "console_handler.h" #include "p2p/net_node.h" -//#include "cryptonote_core/cryptonote_core.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "core_proxy.h" #include "version.h" @@ -89,7 +88,8 @@ int main(int argc, char* argv[]) //create objects and link them - tests::proxy_core pr_core; + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + tests::proxy_core pr_core(currency); cryptonote::t_cryptonote_protocol_handler cprotocol(pr_core, NULL); nodetool::node_server > p2psrv(cprotocol); cprotocol.set_p2p_endpoint(&p2psrv); @@ -99,7 +99,7 @@ int main(int argc, char* argv[]) //initialize objects LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(vm); + bool res = p2psrv.init(vm, false); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); @@ -143,7 +143,7 @@ string tx2str(const cryptonote::transaction& tx, const cryptonote::hash256& tx_h ss << "{" << endl; ss << "\tversion:" << tx.version << endl; - ss << "\tunlock_time:" << tx.unlock_time << endl; + ss << "\tunlock_time:" << tx.unlockTime << endl; ss << "\t" return ss.str(); @@ -155,7 +155,7 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob, crypto::hash tx_hash = null_hash; crypto::hash tx_prefix_hash = null_hash; - transaction tx; + Transaction tx; if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash)) { cerr << "WRONG TRANSACTION BLOB, Failed to parse, rejected" << endl; @@ -173,30 +173,30 @@ bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob, return true; } -bool tests::proxy_core::handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate) { - block b = AUTO_VAL_INIT(b); - - if(!parse_and_validate_block_from_blob(block_blob, b)) { - cerr << "Failed to parse and validate new block" << endl; - return false; - } - - crypto::hash h; - crypto::hash lh; - cout << "BLOCK" << endl << endl; - cout << (h = get_block_hash(b)) << endl; - cout << (lh = get_block_longhash(m_cn_context, b, 0)) << endl; - cout << get_transaction_hash(b.miner_tx) << endl; - cout << ::get_object_blobsize(b.miner_tx) << endl; - //cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl; - cout << obj_to_json_str(b) << endl; - - cout << endl << "ENDBLOCK" << endl << endl; - - if (!add_block(h, lh, b, block_blob)) - return false; - - return true; +bool tests::proxy_core::handle_incoming_block_blob(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool control_miner, bool relay_block) { + Block b = AUTO_VAL_INIT(b); + + if (!parse_and_validate_block_from_blob(block_blob, b)) { + cerr << "Failed to parse and validate new block" << endl; + return false; + } + + crypto::hash h; + crypto::hash lh; + if (!get_block_longhash(m_cn_context, b, lh)) { + return false; + } + + cout << "BLOCK" << endl << endl; + cout << (h = get_block_hash(b)) << endl; + cout << lh << endl; + cout << get_transaction_hash(b.minerTx) << endl; + cout << ::get_object_blobsize(b.minerTx) << endl; + //cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl; + cout << obj_to_json_str(b) << endl; + cout << endl << "ENDBLOCK" << endl << endl; + + return add_block(h, lh, b, block_to_blob(b)); } bool tests::proxy_core::get_short_chain_history(std::list& ids) { @@ -211,9 +211,13 @@ bool tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_i } bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) { - generate_genesis_block(m_genesis); - crypto::hash h = get_block_hash(m_genesis); - add_block(h, get_block_longhash(m_cn_context, m_genesis, 0), m_genesis, block_to_blob(m_genesis)); + m_genesis = m_currency.genesisBlock(); + crypto::hash h = m_currency.genesisBlockHash(); + crypto::hash lh; + if (!get_block_longhash(m_cn_context, m_genesis, lh)) { + return false; + } + add_block(h, lh, m_genesis, block_to_blob(m_genesis)); return true; } @@ -231,20 +235,20 @@ void tests::proxy_core::build_short_history(std::list &m_history, m_history.push_front(cit->first); size_t n = 1 << m_history.size(); - while (m_hash2blkidx.end() != cit && cryptonote::null_hash != cit->second.blk.prev_id && n > 0) { + while (m_hash2blkidx.end() != cit && cryptonote::null_hash != cit->second.blk.prevId && n > 0) { n--; - cit = m_hash2blkidx.find(cit->second.blk.prev_id); + cit = m_hash2blkidx.find(cit->second.blk.prevId); } } while (m_hash2blkidx.end() != cit && get_block_hash(cit->second.blk) != cit->first);*/ } -bool tests::proxy_core::add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob) { +bool tests::proxy_core::add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::Block &_blk, const cryptonote::blobdata &_blob) { size_t height = 0; - if (cryptonote::null_hash != _blk.prev_id) { - std::unordered_map::const_iterator cit = m_hash2blkidx.find(_blk.prev_id); + if (cryptonote::null_hash != _blk.prevId) { + std::unordered_map::const_iterator cit = m_hash2blkidx.find(_blk.prevId); if (m_hash2blkidx.end() == cit) { - cerr << "ERROR: can't find previous block with id \"" << _blk.prev_id << "\"" << endl; + cerr << "ERROR: can't find previous block with id \"" << _blk.prevId << "\"" << endl; return false; } diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 19abcafd84..785b2e40d0 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -17,11 +17,13 @@ #pragma once +#include + #include #include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/Currency.h" #include "cryptonote_core/verification_context.h" -#include namespace tests { @@ -29,33 +31,38 @@ namespace tests size_t height; crypto::hash id; crypto::hash longhash; - cryptonote::block blk; + cryptonote::Block blk; cryptonote::blobdata blob; - std::list txes; + std::list txes; block_index() : height(0), id(cryptonote::null_hash), longhash(cryptonote::null_hash) { } - block_index(size_t _height, const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob, const std::list &_txes) + block_index(size_t _height, const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::Block &_blk, const cryptonote::blobdata &_blob, const std::list &_txes) : height(_height), id(_id), longhash(_longhash), blk(_blk), blob(_blob), txes(_txes) { } }; class proxy_core { - cryptonote::block m_genesis; - std::list m_known_block_list; - std::unordered_map m_hash2blkidx; + const cryptonote::Currency& m_currency; + cryptonote::Block m_genesis; + std::list m_known_block_list; + std::unordered_map m_hash2blkidx; - crypto::hash m_lastblk; - std::list txes; + crypto::hash m_lastblk; + std::list txes; - crypto::cn_context m_cn_context; + crypto::cn_context m_cn_context; - bool add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::block &_blk, const cryptonote::blobdata &_blob); - void build_short_history(std::list &m_history, const crypto::hash &m_start); + bool add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::Block &_blk, const cryptonote::blobdata &_blob); + void build_short_history(std::list &m_history, const crypto::hash &m_start); public: + proxy_core(const cryptonote::Currency& currency) : m_currency(currency) { + } + void on_synchronized(){} uint64_t get_current_blockchain_height(){return 1;} + const cryptonote::Currency& currency() const { return m_currency; } bool init(const boost::program_options::variables_map& vm); bool deinit(){return true;} bool get_short_chain_history(std::list& ids); @@ -63,9 +70,9 @@ namespace tests bool have_block(const crypto::hash& id); bool 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 handle_incoming_block(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool update_miner_blocktemplate = true); - void pause_mine(){} - void resume_mine(){} + bool handle_incoming_block_blob(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool control_miner, bool relay_block); + void pause_mining(){} + void update_block_template_and_resume_mining(){} bool on_idle(){return true;} bool find_blockchain_supplement(const std::list& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;} bool handle_get_objects(cryptonote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote::cryptonote_connection_context& context){return true;} diff --git a/tests/core_tests/TestGenerator.h b/tests/core_tests/TestGenerator.h new file mode 100644 index 0000000000..ee6274268e --- /dev/null +++ b/tests/core_tests/TestGenerator.h @@ -0,0 +1,119 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "chaingen.h" + +#include "cryptonote_core/Currency.h" +#include "TransactionBuilder.h" + +class TestGenerator { +public: + TestGenerator(const cryptonote::Currency& currency, std::vector& eventsRef) : + generator(currency), + events(eventsRef) { + minerAccount.generate(); + generator.constructBlock(genesisBlock, minerAccount, 1338224400); + events.push_back(genesisBlock); + lastBlock = genesisBlock; + } + + const cryptonote::Currency& currency() const { return generator.currency(); } + + void makeNextBlock(const std::list& txs = std::list()) { + cryptonote::Block block; + generator.constructBlock(block, lastBlock, minerAccount, txs); + events.push_back(block); + lastBlock = block; + } + + void makeNextBlock(const cryptonote::Transaction& tx) { + std::list txs; + txs.push_back(tx); + makeNextBlock(txs); + } + + void generateBlocks() { + generateBlocks(currency().minedMoneyUnlockWindow()); + } + + void generateBlocks(size_t count, uint8_t majorVersion = cryptonote::BLOCK_MAJOR_VERSION_1) { + while (count--) { + cryptonote::Block next; + generator.constructBlockManually(next, lastBlock, minerAccount, test_generator::bf_major_ver, majorVersion); + lastBlock = next; + events.push_back(next); + } + } + + TransactionBuilder createTxBuilder(const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee) { + + std::vector sources; + std::vector destinations; + + fillTxSourcesAndDestinations(sources, destinations, from, to, amount, fee); + + TransactionBuilder builder(generator.currency()); + + builder.setInput(sources, from.get_keys()); + builder.setOutput(destinations); + + return builder; + } + + void fillTxSourcesAndDestinations( + std::vector& sources, + std::vector& destinations, + const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix = 0) { + fill_tx_sources_and_destinations(events, lastBlock, from, to, amount, fee, nmix, sources, destinations); + } + + void constructTxToKey( + cryptonote::Transaction& tx, + const cryptonote::account_base& from, + const cryptonote::account_base& to, + uint64_t amount, + uint64_t fee, + size_t nmix = 0) { + construct_tx_to_key(events, tx, lastBlock, from, to, amount, fee, nmix); + } + + void addEvent(const test_event_entry& e) { + events.push_back(e); + } + + void addCallback(const std::string& name) { + callback_entry cb; + cb.callback_name = name; + events.push_back(cb); + } + + void addCheckAccepted() { + addCallback("check_block_accepted"); + } + + void addCheckPurged() { + addCallback("check_block_purged"); + } + + test_generator generator; + cryptonote::Block genesisBlock; + cryptonote::Block lastBlock; + cryptonote::account_base minerAccount; + std::vector& events; +}; diff --git a/tests/core_tests/TransactionBuilder.cpp b/tests/core_tests/TransactionBuilder.cpp new file mode 100644 index 0000000000..37ab1b5a12 --- /dev/null +++ b/tests/core_tests/TransactionBuilder.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransactionBuilder.h" + +using namespace cryptonote; + + +TransactionBuilder::TransactionBuilder(const cryptonote::Currency& currency, uint64_t unlockTime) + : m_currency(currency), m_version(cryptonote::CURRENT_TRANSACTION_VERSION), m_unlockTime(unlockTime), m_txKey(KeyPair::generate()) {} + +TransactionBuilder& TransactionBuilder::newTxKeys() { + m_txKey = KeyPair::generate(); + return *this; +} + + +TransactionBuilder& TransactionBuilder::setInput(const std::vector& sources, const cryptonote::account_keys& senderKeys) { + m_sources = sources; + m_senderKeys = senderKeys; + return *this; +} + +TransactionBuilder& TransactionBuilder::addMultisignatureInput(const cryptonote::TransactionInputMultisignature& input, const TransactionBuilder::KeysVector& keys) { + + MultisignatureSource src; + src.input = input; + src.keys = keys; + m_msigSources.push_back(src); + + return *this; +} + +TransactionBuilder& TransactionBuilder::setOutput(const std::vector& destinations) { + m_destinations = destinations; + return *this; +} + +TransactionBuilder& TransactionBuilder::addOutput(const cryptonote::tx_destination_entry& dest) { + m_destinations.push_back(dest); + return *this; +} + +TransactionBuilder& TransactionBuilder::addMultisignatureOut(uint64_t amount, const KeysVector& keys, uint32_t required) { + + cryptonote::TransactionOutputMultisignature target; + + for (const auto& key : keys) { + target.keys.push_back(key.m_account_address.m_spendPublicKey); + } + + target.requiredSignatures = required; + + MultisignatureDestination dst; + + dst.amount = amount; + dst.output = target; + + m_msigDestinations.push_back(dst); + + return *this; +} + +Transaction TransactionBuilder::build() const { + crypto::hash prefixHash; + + Transaction tx; + add_tx_pub_key_to_extra(tx, m_txKey.pub); + + tx.version = m_version; + tx.unlockTime = m_unlockTime; + + std::vector contexts; + + fillInputs(tx, contexts); + fillOutputs(tx); + + get_transaction_prefix_hash(tx, prefixHash); + + signSources(prefixHash, contexts, tx); + + return tx; +} + +void TransactionBuilder::fillInputs(Transaction& tx, std::vector& contexts) const { + for (const tx_source_entry& src_entr : m_sources) { + contexts.push_back(KeyPair()); + KeyPair& in_ephemeral = contexts.back(); + crypto::key_image img; + generate_key_image_helper(m_senderKeys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img); + + // put key image into tx input + TransactionInputToKey input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.keyImage = img; + + // fill outputs array and use relative offsets + for (const tx_source_entry::output_entry& out_entry : src_entr.outputs) { + input_to_key.keyOffsets.push_back(out_entry.first); + } + + input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); + tx.vin.push_back(input_to_key); + } + + for (const auto& msrc : m_msigSources) { + tx.vin.push_back(msrc.input); + } +} + +void TransactionBuilder::fillOutputs(Transaction& tx) const { + size_t output_index = 0; + + for(const auto& dst_entr : m_destinations) { + crypto::key_derivation derivation; + crypto::public_key out_eph_public_key; + crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, m_txKey.sec, derivation); + crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spendPublicKey, out_eph_public_key); + + TransactionOutput out; + out.amount = dst_entr.amount; + TransactionOutputToKey tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.vout.push_back(out); + output_index++; + } + + for (const auto& mdst : m_msigDestinations) { + TransactionOutput out; + out.amount = mdst.amount; + out.target = mdst.output; + tx.vout.push_back(out); + } +} + + +void TransactionBuilder::signSources(const crypto::hash& prefixHash, const std::vector& contexts, Transaction& tx) const { + + tx.signatures.clear(); + + size_t i = 0; + + // sign TransactionInputToKey sources + for (const auto& src_entr : m_sources) { + std::vector keys_ptrs; + + for (const auto& o : src_entr.outputs) { + keys_ptrs.push_back(&o.second); + } + + tx.signatures.push_back(std::vector()); + std::vector& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + generate_ring_signature(prefixHash, boost::get(tx.vin[i]).keyImage, keys_ptrs, contexts[i].sec, src_entr.real_output, sigs.data()); + i++; + } + + // sign multisignature source + for (const auto& msrc : m_msigSources) { + tx.signatures.resize(tx.signatures.size() + 1); + auto& outsigs = tx.signatures.back(); + + for (const auto& key : msrc.keys) { + const auto& pk = key.m_account_address.m_spendPublicKey; + const auto& sk = key.m_spend_secret_key; + crypto::signature sig; + crypto::generate_signature(prefixHash, pk, sk, sig); + outsigs.push_back(sig); + } + } +} diff --git a/tests/core_tests/TransactionBuilder.h b/tests/core_tests/TransactionBuilder.h new file mode 100644 index 0000000000..6211325812 --- /dev/null +++ b/tests/core_tests/TransactionBuilder.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" + +class TransactionBuilder { +public: + + typedef std::vector KeysVector; + typedef std::vector SignatureVector; + typedef std::vector SignatureMultivector; + + TransactionBuilder(const cryptonote::Currency& currency, uint64_t unlockTime = 0); + + // regenerate transaction keys + TransactionBuilder& newTxKeys(); + + // inputs + TransactionBuilder& setInput(const std::vector& sources, const cryptonote::account_keys& senderKeys); + TransactionBuilder& addMultisignatureInput(const cryptonote::TransactionInputMultisignature& input, const KeysVector& keys); + + // outputs + TransactionBuilder& setOutput(const std::vector& destinations); + TransactionBuilder& addOutput(const cryptonote::tx_destination_entry& dest); + TransactionBuilder& addMultisignatureOut(uint64_t amount, const KeysVector& keys, uint32_t required); + + cryptonote::Transaction build() const; + + std::vector m_sources; + std::vector m_destinations; + +private: + + void fillInputs(cryptonote::Transaction& tx, std::vector& contexts) const; + void fillOutputs(cryptonote::Transaction& tx) const; + void signSources(const crypto::hash& prefixHash, const std::vector& contexts, cryptonote::Transaction& tx) const; + + struct MultisignatureSource { + cryptonote::TransactionInputMultisignature input; + KeysVector keys; + }; + + struct MultisignatureDestination { + uint64_t amount; + cryptonote::TransactionOutputMultisignature output; + }; + + cryptonote::account_keys m_senderKeys; + + std::vector m_msigSources; + std::vector m_msigDestinations; + + size_t m_version; + uint64_t m_unlockTime; + cryptonote::KeyPair m_txKey; + const cryptonote::Currency& m_currency; +}; diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 918a2336eb..99a370adde 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -15,78 +15,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" - #include "block_reward.h" +// epee +#include "misc_language.h" + using namespace epee; 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) - { - if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, fee, miner_address, miner_tx)) - return false; - - size_t current_size = get_object_blobsize(miner_tx); - size_t try_count = 0; - while (target_tx_size != current_size) - { - ++try_count; - if (10 < try_count) - return false; - - if (target_tx_size < current_size) - { - size_t diff = current_size - target_tx_size; - if (diff <= miner_tx.extra.size()) - miner_tx.extra.resize(miner_tx.extra.size() - diff); - else - return false; - } - else - { - size_t diff = target_tx_size - current_size; - miner_tx.extra.resize(miner_tx.extra.size() + diff); - } - - current_size = get_object_blobsize(miner_tx); - } - - return true; - } - - bool construct_max_size_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); - - size_t median = misc_utils::median(block_sizes); - median = std::max(median, static_cast(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE)); - - 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); - if (!r) - return false; - - return generator.construct_block_manually(blk, blk_prev, miner_account, test_generator::bf_miner_tx, 0, 0, 0, - crypto::hash(), 0, miner_tx); - } - - bool rewind_blocks(std::vector& events, test_generator& generator, block& blk, const block& blk_prev, + bool rewind_blocks(std::vector& events, test_generator& generator, Block& blk, const Block& blk_prev, const account_base& miner_account, size_t block_count) { blk = blk_prev; for (size_t i = 0; i < block_count; ++i) { - block blk_i; - if (!construct_max_size_block(generator, blk_i, blk, miner_account)) + Block blk_i; + if (!generator.constructMaxSizeBlock(blk_i, blk, miner_account)) return false; events.push_back(blk_i); @@ -96,7 +42,7 @@ namespace return true; } - uint64_t get_tx_out_amount(const transaction& tx) + uint64_t get_tx_out_amount(const Transaction& tx) { uint64_t amount = 0; BOOST_FOREACH(auto& o, tx.vout) @@ -106,8 +52,11 @@ namespace } gen_block_reward::gen_block_reward() - : m_invalid_block_index(0) -{ + : m_invalid_block_index(0) { + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); + m_currency = currencyBuilder.currency(); + REGISTER_CALLBACK_METHOD(gen_block_reward, mark_invalid_block); REGISTER_CALLBACK_METHOD(gen_block_reward, mark_checked_block); REGISTER_CALLBACK_METHOD(gen_block_reward, check_block_rewards); @@ -122,28 +71,34 @@ bool gen_block_reward::generate(std::vector& events) const DO_CALLBACK(events, "mark_checked_block"); MAKE_ACCOUNT(events, bob_account); - // Test: miner transactions without outputs (block reward == 0) - block blk_0r; - if (!rewind_blocks(events, generator, blk_0r, blk_0, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW)) + // Test case 1: miner transactions without outputs (block reward == 0) + Block blk_0r; + if (!rewind_blocks(events, generator, blk_0r, blk_0, miner_account, m_currency.rewardBlocksWindow())) { return false; + } - // Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks + // Test: block reward is calculated using median of the latest m_currency.rewardBlocksWindow() 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)) + Block blk_1_bad_1; + if (!generator.constructMaxSizeBlock(blk_1_bad_1, blk_0r, miner_account, m_currency.rewardBlocksWindow() + 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)) + Block blk_1_bad_2; + if (!generator.constructMaxSizeBlock(blk_1_bad_2, blk_0r, miner_account, m_currency.rewardBlocksWindow() - 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)) + // Test 1.2: miner transactions without outputs (block reward == 0) + Block blk_1; + if (!generator.constructMaxSizeBlock(blk_1, blk_0r, miner_account)) { return false; + } events.push_back(blk_1); + // End of Test case 1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -156,35 +111,35 @@ bool gen_block_reward::generate(std::vector& events) const MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_account); DO_CALLBACK(events, "mark_checked_block"); - block blk_5r; - if (!rewind_blocks(events, generator, blk_5r, blk_5, miner_account, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)) + Block blk_5r; + if (!rewind_blocks(events, generator, blk_5r, blk_5, miner_account, m_currency.minedMoneyUnlockWindow())) return false; // Test: fee increases block reward - transaction tx_0(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * TESTS_DEFAULT_FEE)); + Transaction tx_0(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * m_currency.minimumFee())); MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_account, tx_0); DO_CALLBACK(events, "mark_checked_block"); // Test: fee from all block transactions increase block reward - std::list txs_0; - txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * TESTS_DEFAULT_FEE)); - txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * TESTS_DEFAULT_FEE)); + std::list txs_0; + txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * m_currency.minimumFee())); + txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * m_currency.minimumFee())); MAKE_NEXT_BLOCK_TX_LIST(events, blk_7, blk_6, miner_account, txs_0); DO_CALLBACK(events, "mark_checked_block"); // Test: block reward == transactions fee { - 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); + Transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * m_currency.minimumFee()); + Transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * m_currency.minimumFee()); size_t txs_1_size = get_object_blobsize(tx_1) + get_object_blobsize(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); + generator.getLastNBlockSizes(block_sizes, get_block_hash(blk_7), m_currency.rewardBlocksWindow()); size_t median = misc_utils::median(block_sizes); - 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), + Transaction miner_tx; + bool r = constructMinerTxBySize(m_currency, miner_tx, get_block_height(blk_7) + 1, generator.getAlreadyGeneratedCoins(blk_7), miner_account.get_keys().m_account_address, block_sizes, 2 * median - txs_1_size, 2 * median, txs_fee); if (!r) return false; @@ -193,9 +148,9 @@ bool gen_block_reward::generate(std::vector& events) const txs_1_hashes.push_back(get_transaction_hash(tx_1)); txs_1_hashes.push_back(get_transaction_hash(tx_2)); - 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); + Block blk_8; + generator.constructBlockManually(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, txs_fee); events.push_back(blk_8); DO_CALLBACK(events, "mark_checked_block"); @@ -206,7 +161,7 @@ bool gen_block_reward::generate(std::vector& events) const return true; } -bool gen_block_reward::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/) +bool gen_block_reward::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/) { if (m_invalid_block_index == event_idx) { @@ -236,28 +191,28 @@ bool gen_block_reward::check_block_rewards(cryptonote::core& /*c*/, size_t /*ev_ DEFINE_TESTS_ERROR_CONTEXT("gen_block_reward_without_txs::check_block_rewards"); std::array blk_rewards; - blk_rewards[0] = MONEY_SUPPLY >> 18; + blk_rewards[0] = m_currency.moneySupply() >> m_currency.emissionSpeedFactor(); uint64_t cumulative_reward = blk_rewards[0]; for (size_t i = 1; i < blk_rewards.size(); ++i) { - blk_rewards[i] = (MONEY_SUPPLY - cumulative_reward) >> 18; + blk_rewards[i] = (m_currency.moneySupply() - cumulative_reward) >> m_currency.emissionSpeedFactor(); cumulative_reward += blk_rewards[i]; } for (size_t i = 0; i < 5; ++i) { - block blk_i = boost::get(events[m_checked_blocks_indices[i]]); - CHECK_EQ(blk_rewards[i], get_tx_out_amount(blk_i.miner_tx)); + Block blk_i = boost::get(events[m_checked_blocks_indices[i]]); + CHECK_EQ(blk_rewards[i], get_tx_out_amount(blk_i.minerTx)); } - block blk_n1 = boost::get(events[m_checked_blocks_indices[5]]); - CHECK_EQ(blk_rewards[5] + 3 * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n1.miner_tx)); + Block blk_n1 = boost::get(events[m_checked_blocks_indices[5]]); + CHECK_EQ(blk_rewards[5] + 3 * m_currency.minimumFee(), get_tx_out_amount(blk_n1.minerTx)); - block blk_n2 = boost::get(events[m_checked_blocks_indices[6]]); - CHECK_EQ(blk_rewards[6] + (5 + 7) * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n2.miner_tx)); + Block blk_n2 = boost::get(events[m_checked_blocks_indices[6]]); + CHECK_EQ(blk_rewards[6] + (5 + 7) * m_currency.minimumFee(), get_tx_out_amount(blk_n2.minerTx)); - block blk_n3 = boost::get(events[m_checked_blocks_indices[7]]); - CHECK_EQ((11 + 13) * TESTS_DEFAULT_FEE, get_tx_out_amount(blk_n3.miner_tx)); + Block blk_n3 = boost::get(events[m_checked_blocks_indices[7]]); + CHECK_EQ((11 + 13) * m_currency.minimumFee(), get_tx_out_amount(blk_n3.minerTx)); return true; } diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index e23205ab5a..564fbd6144 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -24,7 +24,7 @@ struct gen_block_reward : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& blk); + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& blk); bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); bool mark_checked_block(cryptonote::core& c, size_t ev_index, const std::vector& events); diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index be58059a5e..c01551ff49 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -15,31 +15,35 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "block_validation.h" +#include "TestGenerator.h" using namespace epee; using namespace cryptonote; -namespace -{ - bool lift_up_difficulty(std::vector& events, std::vector& timestamps, - std::vector& cummulative_difficulties, test_generator& generator, - size_t new_block_count, const block blk_last, const account_base& miner_account) - { - difficulty_type commulative_diffic = cummulative_difficulties.empty() ? 0 : cummulative_difficulties.back(); - block blk_prev = blk_last; - for (size_t i = 0; i < new_block_count; ++i) - { - block blk_next; - difficulty_type diffic = next_difficulty(timestamps, cummulative_difficulties); - if (!generator.construct_block_manually(blk_next, blk_prev, miner_account, - test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic)) +#define BLOCK_VALIDATION_INIT_GENERATE() \ + GENERATE_ACCOUNT(miner_account); \ + MAKE_GENESIS_BLOCK(events, blk_0, miner_account, 1338224400); + +namespace { + bool lift_up_difficulty(const cryptonote::Currency& currency, std::vector& events, + std::vector& timestamps, + std::vector& cummulative_difficulties, test_generator& generator, + size_t new_block_count, const cryptonote::Block blk_last, + const cryptonote::account_base& miner_account, uint8_t block_major_version) { + cryptonote::difficulty_type commulative_diffic = cummulative_difficulties.empty() ? 0 : cummulative_difficulties.back(); + cryptonote::Block blk_prev = blk_last; + for (size_t i = 0; i < new_block_count; ++i) { + cryptonote::Block blk_next; + cryptonote::difficulty_type diffic = currency.nextDifficulty(timestamps, cummulative_difficulties); + if (!generator.constructBlockManually(blk_next, blk_prev, miner_account, + test_generator::bf_major_ver | test_generator::bf_timestamp | test_generator::bf_diffic, + block_major_version, 0, blk_prev.timestamp, crypto::hash(), diffic)) { return false; + } commulative_diffic += diffic; - if (timestamps.size() == DIFFICULTY_WINDOW) - { + if (timestamps.size() == currency.difficultyWindow()) { timestamps.erase(timestamps.begin()); cummulative_difficulties.erase(cummulative_difficulties.begin()); } @@ -52,34 +56,84 @@ namespace return true; } -} -#define BLOCK_VALIDATION_INIT_GENERATE() \ - GENERATE_ACCOUNT(miner_account); \ - MAKE_GENESIS_BLOCK(events, blk_0, miner_account, 1338224400); + bool getParentBlockSize(const cryptonote::Block& block, size_t& size) { + auto serializer = cryptonote::makeParentBlockSerializer(block, false, false); + if (!cryptonote::get_object_blobsize(serializer, size)) { + LOG_ERROR("Failed to get size of parent block"); + return false; + } + return true; + } -//---------------------------------------------------------------------------------------------------------------------- -// Tests + bool adjustParentBlockSize(cryptonote::Block& block, size_t targetSize) { + size_t parentBlockSize; + if (!getParentBlockSize(block, parentBlockSize)) { + return false; + } -bool gen_block_big_major_version::generate(std::vector& events) const -{ - BLOCK_VALIDATION_INIT_GENERATE(); + if (parentBlockSize > targetSize) { + LOG_ERROR("Parent block size is " << parentBlockSize << " bytes that is already greater than target size " << targetSize << " bytes"); + return false; + } - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, CURRENT_BLOCK_MAJOR_VERSION + 1); - events.push_back(blk_1); + block.parentBlock.minerTx.extra.resize(block.parentBlock.minerTx.extra.size() + (targetSize - parentBlockSize)); + + if (!getParentBlockSize(block, parentBlockSize)) { + return false; + } + + if (parentBlockSize > targetSize) { + if (block.parentBlock.minerTx.extra.size() < parentBlockSize - targetSize) { + LOG_ERROR("Failed to adjust parent block size to " << targetSize); + return false; + } + + block.parentBlock.minerTx.extra.resize(block.parentBlock.minerTx.extra.size() - (parentBlockSize - targetSize)); + + if (!getParentBlockSize(block, parentBlockSize)) { + return false; + } + + if (parentBlockSize + 1 == targetSize) { + block.timestamp = std::max(block.timestamp, UINT64_C(1)) << 7; + if (!getParentBlockSize(block, parentBlockSize)) { + return false; + } + } + } + + if (parentBlockSize != targetSize) { + LOG_ERROR("Failed to adjust parent block size to " << targetSize); + return false; + } + + return true; + } +} - DO_CALLBACK(events, "check_block_purged"); +bool TestBlockMajorVersionAccepted::generate(std::vector& events) const { + TestGenerator bg(m_currency, events); + bg.generateBlocks(1, m_blockMajorVersion); + DO_CALLBACK(events, "check_block_accepted"); return true; } -bool gen_block_big_minor_version::generate(std::vector& events) const -{ +bool TestBlockMajorVersionRejected::generate(std::vector& events) const { + TestGenerator bg(m_currency, events); + bg.generateBlocks(1, m_blockGeneratedVersion); + DO_CALLBACK(events, "check_block_purged"); + return true; +} + +bool TestBlockBigMinorVersion::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_minor_ver, 0, CURRENT_BLOCK_MINOR_VERSION + 1); + cryptonote::Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_minor_ver, m_blockMajorVersion, BLOCK_MINOR_VERSION_0 + 1); + events.push_back(blk_1); DO_CALLBACK(events, "check_block_accepted"); @@ -90,10 +144,14 @@ bool gen_block_big_minor_version::generate(std::vector& events bool gen_block_ts_not_checked::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 2); - block blk_1; - generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, blk_0.timestamp - 60 * 60); + generator.defaultMajorVersion = m_blockMajorVersion; + + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, m_currency.timestampCheckWindow() - 2); + + Block blk_1; + generator.constructBlockManually(blk_1, blk_0r, miner_account, + test_generator::bf_major_ver | test_generator::bf_timestamp, m_blockMajorVersion, 0, blk_0.timestamp - 60 * 60); events.push_back(blk_1); DO_CALLBACK(events, "check_block_accepted"); @@ -104,11 +162,15 @@ bool gen_block_ts_not_checked::generate(std::vector& events) c bool gen_block_ts_in_past::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - 1); - uint64_t ts_below_median = boost::get(events[BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW / 2 - 1]).timestamp; - block blk_1; - generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_timestamp, 0, 0, ts_below_median); + generator.defaultMajorVersion = m_blockMajorVersion; + + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_account, m_currency.timestampCheckWindow() - 1); + + uint64_t ts_below_median = boost::get(events[m_currency.timestampCheckWindow() / 2 - 1]).timestamp; + Block blk_1; + generator.constructBlockManually(blk_1, blk_0r, miner_account, + test_generator::bf_major_ver | test_generator::bf_timestamp, m_blockMajorVersion, 0, ts_below_median); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -116,12 +178,13 @@ bool gen_block_ts_in_past::generate(std::vector& events) const return true; } -bool gen_block_ts_in_future::generate(std::vector& events) const +bool gen_block_ts_in_future_rejected::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_timestamp, 0, 0, time(NULL) + 60*60 + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver | test_generator::bf_timestamp, + m_blockMajorVersion, 0, time(NULL) + 60 * 60 + m_currency.blockFutureTimeLimit()); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -129,14 +192,30 @@ bool gen_block_ts_in_future::generate(std::vector& events) con return true; } +bool gen_block_ts_in_future_accepted::generate(std::vector& events) const +{ + BLOCK_VALIDATION_INIT_GENERATE(); + + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver | test_generator::bf_timestamp, + m_blockMajorVersion, 0, time(NULL) - 60 + m_currency.blockFutureTimeLimit()); + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_accepted"); + + return true; +} + + bool gen_block_invalid_prev_id::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - block blk_1; + Block blk_1; crypto::hash prev_id = get_block_hash(blk_0); reinterpret_cast(prev_id) ^= 1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_prev_id, 0, 0, 0, prev_id); + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_prev_id, m_blockMajorVersion, 0, 0, prev_id); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -144,7 +223,7 @@ bool gen_block_invalid_prev_id::generate(std::vector& events) return true; } -bool gen_block_invalid_prev_id::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/) +bool gen_block_invalid_prev_id::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/) { if (1 == event_idx) return bvc.m_marked_as_orphaned && !bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; @@ -158,21 +237,23 @@ bool gen_block_invalid_nonce::generate(std::vector& events) co std::vector timestamps; std::vector commulative_difficulties; - if (!lift_up_difficulty(events, timestamps, commulative_difficulties, generator, 2, blk_0, miner_account)) + if (!lift_up_difficulty(m_currency, events, timestamps, commulative_difficulties, generator, 2, blk_0, miner_account, + m_blockMajorVersion)) { return false; + } // Create invalid nonce - difficulty_type diffic = next_difficulty(timestamps, commulative_difficulties); + difficulty_type diffic = m_currency.nextDifficulty(timestamps, commulative_difficulties); assert(1 < diffic); - const block& blk_last = boost::get(events.back()); + const Block& blk_last = boost::get(events.back()); uint64_t timestamp = blk_last.timestamp; - block blk_3; + Block blk_3; do { ++timestamp; - blk_3.miner_tx.set_null(); - if (!generator.construct_block_manually(blk_3, blk_last, miner_account, - test_generator::bf_diffic | test_generator::bf_timestamp, 0, 0, timestamp, crypto::hash(), diffic)) + blk_3.minerTx.clear(); + if (!generator.constructBlockManually(blk_3, blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_diffic | test_generator::bf_timestamp, m_blockMajorVersion, 0, timestamp, crypto::hash(), diffic)) return false; } while (0 == blk_3.nonce); @@ -186,11 +267,12 @@ bool gen_block_no_miner_tx::generate(std::vector& events) cons { BLOCK_VALIDATION_INIT_GENERATE(); - transaction miner_tx; - miner_tx.set_null(); + Transaction miner_tx; + miner_tx.clear(); - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -203,10 +285,11 @@ bool gen_block_unlock_time_is_low::generate(std::vector& event BLOCK_VALIDATION_INIT_GENERATE(); MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - --miner_tx.unlock_time; + --miner_tx.unlockTime; - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -219,10 +302,11 @@ bool gen_block_unlock_time_is_high::generate(std::vector& even BLOCK_VALIDATION_INIT_GENERATE(); MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - ++miner_tx.unlock_time; + ++miner_tx.unlockTime; - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -235,10 +319,11 @@ bool gen_block_unlock_time_is_timestamp_in_past::generate(std::vector& events) co BLOCK_VALIDATION_INIT_GENERATE(); MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - boost::get(miner_tx.vin[0]).height--; + boost::get(miner_tx.vin[0]).height--; - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -283,10 +370,11 @@ bool gen_block_height_is_high::generate(std::vector& events) c BLOCK_VALIDATION_INIT_GENERATE(); MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - boost::get(miner_tx.vin[0]).height++; + boost::get(miner_tx.vin[0]).height++; - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -300,12 +388,13 @@ bool gen_block_miner_tx_has_2_tx_gen_in::generate(std::vector& MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - txin_gen in; + TransactionInputGenerate in; in.height = get_block_height(blk_0) + 1; miner_tx.vin.push_back(in); - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -316,15 +405,18 @@ bool gen_block_miner_tx_has_2_tx_gen_in::generate(std::vector& bool gen_block_miner_tx_has_2_in::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); + + generator.defaultMajorVersion = m_blockMajorVersion; + REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); GENERATE_ACCOUNT(alice); tx_source_entry se; - se.amount = blk_0.miner_tx.vout[0].amount; - se.outputs.push_back(std::make_pair(0, boost::get(blk_0.miner_tx.vout[0].target).key)); + se.amount = blk_0.minerTx.vout[0].amount; + se.outputs.push_back(std::make_pair(0, boost::get(blk_0.minerTx.vout[0].target).key)); se.real_output = 0; - se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx); + se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.minerTx); se.real_output_in_tx_index = 0; std::vector sources; sources.push_back(se); @@ -335,15 +427,16 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector& events std::vector destinations; destinations.push_back(de); - transaction tmp_tx; + Transaction tmp_tx; if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tmp_tx, 0)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); miner_tx.vin.push_back(tmp_tx.vin[0]); - block blk_1; - generator.construct_block_manually(blk_1, blk_0r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0r, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -355,18 +448,20 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector { BLOCK_VALIDATION_INIT_GENERATE(); + generator.defaultMajorVersion = m_blockMajorVersion; + // This block has only one output - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_none); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_none); events.push_back(blk_1); REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); tx_source_entry se; - se.amount = blk_1.miner_tx.vout[0].amount; - se.outputs.push_back(std::make_pair(0, boost::get(blk_1.miner_tx.vout[0].target).key)); + se.amount = blk_1.minerTx.vout[0].amount; + se.outputs.push_back(std::make_pair(0, boost::get(blk_1.minerTx.vout[0].target).key)); se.real_output = 0; - se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx); + se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.minerTx); se.real_output_in_tx_index = 0; std::vector sources; sources.push_back(se); @@ -377,15 +472,16 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector std::vector destinations; destinations.push_back(de); - transaction tmp_tx; + Transaction tmp_tx; if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tmp_tx, 0)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_1); miner_tx.vin[0] = tmp_tx.vin[0]; - block blk_2; - generator.construct_block_manually(blk_2, blk_1r, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_2; + generator.constructBlockManually(blk_2, blk_1r, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_2); DO_CALLBACK(events, "check_block_purged"); @@ -400,8 +496,9 @@ bool gen_block_miner_tx_out_is_small::generate(std::vector& ev MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); miner_tx.vout[0].amount /= 2; - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -416,8 +513,9 @@ bool gen_block_miner_tx_out_is_big::generate(std::vector& even MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); miner_tx.vout[0].amount *= 2; - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -432,8 +530,9 @@ bool gen_block_miner_tx_has_no_out::generate(std::vector& even MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); miner_tx.vout.clear(); - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -447,22 +546,23 @@ bool gen_block_miner_tx_has_out_to_alice::generate(std::vector GENERATE_ACCOUNT(alice); - keypair txkey; + KeyPair txkey; MAKE_MINER_TX_AND_KEY_MANUALLY(miner_tx, blk_0, &txkey); crypto::key_derivation derivation; crypto::public_key out_eph_public_key; - crypto::generate_key_derivation(alice.get_keys().m_account_address.m_view_public_key, txkey.sec, derivation); - crypto::derive_public_key(derivation, 1, alice.get_keys().m_account_address.m_spend_public_key, out_eph_public_key); + crypto::generate_key_derivation(alice.get_keys().m_account_address.m_viewPublicKey, txkey.sec, derivation); + crypto::derive_public_key(derivation, 1, alice.get_keys().m_account_address.m_spendPublicKey, out_eph_public_key); - tx_out out_to_alice; + TransactionOutput out_to_alice; out_to_alice.amount = miner_tx.vout[0].amount / 2; miner_tx.vout[0].amount -= out_to_alice.amount; - out_to_alice.target = txout_to_key(out_eph_public_key); + out_to_alice.target = TransactionOutputToKey(out_eph_public_key); miner_tx.vout.push_back(out_to_alice); - block blk_1; - generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx); + Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx); events.push_back(blk_1); DO_CALLBACK(events, "check_block_accepted"); @@ -474,11 +574,13 @@ bool gen_block_has_invalid_tx::generate(std::vector& events) c { BLOCK_VALIDATION_INIT_GENERATE(); + generator.defaultMajorVersion = m_blockMajorVersion; + std::vector tx_hashes; tx_hashes.push_back(crypto::hash()); - block blk_1; - generator.construct_block_manually_tx(blk_1, blk_0, miner_account, tx_hashes, 0); + Block blk_1; + generator.constructBlockManuallyTx(blk_1, blk_0, miner_account, tx_hashes, 0); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); @@ -492,22 +594,22 @@ bool gen_block_is_too_big::generate(std::vector& events) const // Creating a huge miner_tx, it will have a lot of outs MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - static const size_t tx_out_count = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE / 2; + static const size_t tx_out_count = m_currency.blockGrantedFullRewardZone() / 2; uint64_t amount = get_outs_money_amount(miner_tx); uint64_t portion = amount / tx_out_count; uint64_t remainder = amount % tx_out_count; - txout_target_v target = miner_tx.vout[0].target; + TransactionOutputTarget target = miner_tx.vout[0].target; miner_tx.vout.clear(); for (size_t i = 0; i < tx_out_count; ++i) { - tx_out o; + TransactionOutput o; o.amount = portion; o.target = target; miner_tx.vout.push_back(o); } if (0 < remainder) { - tx_out o; + TransactionOutput o; o.amount = remainder; o.target = target; miner_tx.vout.push_back(o); @@ -515,8 +617,9 @@ bool gen_block_is_too_big::generate(std::vector& events) const // Block reward will be incorrect, as it must be reduced if cumulative block size is very big, // but in this test it doesn't matter - block blk_1; - if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx)) + Block blk_1; + if (!generator.constructBlockManually(blk_1, blk_0, miner_account, + test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx)) return false; events.push_back(blk_1); @@ -526,9 +629,39 @@ bool gen_block_is_too_big::generate(std::vector& events) const return true; } -gen_block_invalid_binary_format::gen_block_invalid_binary_format() - : m_corrupt_blocks_begin_idx(0) -{ +bool TestBlockCumulativeSizeExceedsLimit::generate(std::vector& events) const { + BLOCK_VALIDATION_INIT_GENERATE(); + + generator.defaultMajorVersion = m_blockMajorVersion; + + Block prevBlock = blk_0; + for (size_t height = 1; height < 1000; ++height) { + Block block; + if (!generator.constructMaxSizeBlock(block, prevBlock, miner_account)) { + return false; + } + + prevBlock = block; + + if (get_object_blobsize(block.minerTx) <= m_currency.maxBlockCumulativeSize(height)) { + events.push_back(block); + } else { + DO_CALLBACK(events, "markInvalidBlock"); + events.push_back(block); + return true; + } + } + + return false; +} + +gen_block_invalid_binary_format::gen_block_invalid_binary_format(uint8_t blockMajorVersion) : + m_corrupt_blocks_begin_idx(0), + m_blockMajorVersion(blockMajorVersion) { + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : 0); + m_currency = currencyBuilder.currency(); + REGISTER_CALLBACK("check_all_blocks_purged", gen_block_invalid_binary_format::check_all_blocks_purged); REGISTER_CALLBACK("corrupt_blocks_boundary", gen_block_invalid_binary_format::corrupt_blocks_boundary); } @@ -537,14 +670,16 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev { BLOCK_VALIDATION_INIT_GENERATE(); + generator.defaultMajorVersion = m_blockMajorVersion; + std::vector timestamps; std::vector cummulative_difficulties; difficulty_type cummulative_diff = 1; // Unlock blk_0 outputs - block blk_last = blk_0; - assert(CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW < DIFFICULTY_WINDOW); - for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) + Block blk_last = blk_0; + assert(m_currency.minedMoneyUnlockWindow() < m_currency.difficultyWindow()); + for (size_t i = 0; i < m_currency.minedMoneyUnlockWindow(); ++i) { MAKE_NEXT_BLOCK(events, blk_curr, blk_last, miner_account); timestamps.push_back(blk_curr.timestamp); @@ -556,26 +691,28 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev difficulty_type diffic; do { - blk_last = boost::get(events.back()); - diffic = next_difficulty(timestamps, cummulative_difficulties); - if (!lift_up_difficulty(events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account)) + blk_last = boost::get(events.back()); + diffic = m_currency.nextDifficulty(timestamps, cummulative_difficulties); + if (!lift_up_difficulty(m_currency, events, timestamps, cummulative_difficulties, generator, 1, blk_last, + miner_account, m_blockMajorVersion)) { return false; + } std::cout << "Block #" << events.size() << ", difficulty: " << diffic << std::endl; } while (diffic < 1500); - blk_last = boost::get(events.back()); - MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(120), boost::get(events[1])); + blk_last = boost::get(events.back()); + MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(120), boost::get(events[1])); DO_CALLBACK(events, "corrupt_blocks_boundary"); - block blk_test; + 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); - diffic = next_difficulty(timestamps, cummulative_difficulties); - 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)) + diffic = m_currency.nextDifficulty(timestamps, cummulative_difficulties); + if (!generator.constructBlockManually(blk_test, blk_last, miner_account, + test_generator::bf_major_ver | test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, + m_blockMajorVersion, 0, blk_last.timestamp, crypto::hash(), diffic, Transaction(), tx_hashes, txs_size)) return false; blobdata blob = t_serializable_object_to_blob(blk_test); @@ -597,7 +734,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev } bool gen_block_invalid_binary_format::check_block_verification_context(const cryptonote::block_verification_context& bvc, - size_t event_idx, const cryptonote::block& blk) + size_t event_idx, const cryptonote::Block& blk) { if (0 == m_corrupt_blocks_begin_idx || event_idx < m_corrupt_blocks_begin_idx) { @@ -624,3 +761,76 @@ bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core& return true; } + +bool TestMaxSizeOfParentBlock::generate(std::vector& events) const { + BLOCK_VALIDATION_INIT_GENERATE(); + + cryptonote::Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, BLOCK_MAJOR_VERSION_2); + if (!adjustParentBlockSize(blk_1, 2 * 1024)) { + return false; + } + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_accepted"); + + return true; +} + +bool TestBigParentBlock::generate(std::vector& events) const { + BLOCK_VALIDATION_INIT_GENERATE(); + + cryptonote::Block blk_1; + generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, BLOCK_MAJOR_VERSION_2); + if (!adjustParentBlockSize(blk_1, 2 * 1024 + 1)) { + return false; + } + events.push_back(blk_1); + + DO_CALLBACK(events, "check_block_purged"); + + return true; +} + + +namespace +{ + template + bool GenerateAndMutateBlockV2(const cryptonote::Currency& currency, std::vector& events, const std::string& callback, MutateFunc mf) { + TestGenerator bg(currency, events); + + cryptonote::Block blk_1; + bg.generator.constructBlockManually( + blk_1, bg.lastBlock, bg.minerAccount, test_generator::bf_major_ver, BLOCK_MAJOR_VERSION_2); + + mf(blk_1); + + events.push_back(blk_1); + bg.addCallback(callback); + + return true; + } +} + +bool TestBlock2ExtraEmpty::generate(std::vector& events) const { + return GenerateAndMutateBlockV2(m_currency, events, "check_block_purged", [](cryptonote::Block& blk) { + blk.parentBlock.minerTx.extra.clear(); + }); +} + +bool TestBlock2ExtraWithoutMMTag::generate(std::vector& events) const { + return GenerateAndMutateBlockV2(m_currency, events, "check_block_purged", [](cryptonote::Block& blk) { + blk.parentBlock.minerTx.extra.clear(); + cryptonote::add_extra_nonce_to_tx_extra(blk.parentBlock.minerTx.extra, "0xdeadbeef"); + }); +} + +bool TestBlock2ExtraWithGarbage::generate(std::vector& events) const { + return GenerateAndMutateBlockV2(m_currency, events, "check_block_accepted", [](cryptonote::Block& blk) { + cryptonote::add_extra_nonce_to_tx_extra(blk.parentBlock.minerTx.extra, "0xdeadbeef"); + blk.parentBlock.minerTx.extra.push_back(0xde); + blk.parentBlock.minerTx.extra.push_back(0xad); + blk.parentBlock.minerTx.extra.push_back(0xbe); + blk.parentBlock.minerTx.extra.push_back(0xef); + }); +} diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index ef12d4b6d9..351b30b0c5 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -16,180 +16,345 @@ // along with Bytecoin. If not, see . #pragma once + #include "chaingen.h" -template -class gen_block_verification_base : public test_chain_unit_base -{ +const uint64_t UNDEF_HEIGHT = static_cast(cryptonote::UpgradeDetectorBase::UNDEF_HEIGHT); + +class CheckBlockPurged : public test_chain_unit_base { public: - gen_block_verification_base() - { - REGISTER_CALLBACK("check_block_purged", gen_block_verification_base::check_block_purged); + CheckBlockPurged(size_t invalidBlockIdx, uint8_t blockMajorVersion) : + m_invalidBlockIdx(invalidBlockIdx), + m_blockMajorVersion(blockMajorVersion) { + assert(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_2); + + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + m_currency = currencyBuilder.currency(); + + REGISTER_CALLBACK("check_block_purged", CheckBlockPurged::check_block_purged); + REGISTER_CALLBACK("markInvalidBlock", CheckBlockPurged::markInvalidBlock); } - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/) - { - if (invalid_block_idx == event_idx) + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t eventIdx, const cryptonote::Block& /*blk*/) { + if (m_invalidBlockIdx == eventIdx) { return bvc.m_verifivation_failed; - else + } else { return !bvc.m_verifivation_failed; + } } - bool check_block_purged(cryptonote::core& c, size_t ev_index, const std::vector& events) - { - DEFINE_TESTS_ERROR_CONTEXT("gen_block_verification_base::check_block_purged"); + bool check_block_purged(cryptonote::core& c, size_t eventIdx, const std::vector& events) { + DEFINE_TESTS_ERROR_CONTEXT("CheckBlockPurged::check_block_purged"); - CHECK_TEST_CONDITION(invalid_block_idx < ev_index); + CHECK_TEST_CONDITION(m_invalidBlockIdx < eventIdx); CHECK_EQ(0, c.get_pool_transactions_count()); - CHECK_EQ(invalid_block_idx, c.get_current_blockchain_height()); + CHECK_EQ(m_invalidBlockIdx, c.get_current_blockchain_height()); return true; } + + bool markInvalidBlock(cryptonote::core& c, size_t eventIdx, const std::vector& events) { + m_invalidBlockIdx = eventIdx + 1; + return true; + } + +protected: + size_t m_invalidBlockIdx; + const uint8_t m_blockMajorVersion; }; -template -struct gen_block_accepted_base : public test_chain_unit_base -{ - gen_block_accepted_base() - { - REGISTER_CALLBACK("check_block_accepted", gen_block_accepted_base::check_block_accepted); + +struct CheckBlockAccepted : public test_chain_unit_base { + CheckBlockAccepted(size_t expectedBlockchainHeight, uint8_t blockMajorVersion) : + m_expectedBlockchainHeight(expectedBlockchainHeight), + m_blockMajorVersion(blockMajorVersion) { + assert(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_2); + + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + m_currency = currencyBuilder.currency(); + + REGISTER_CALLBACK("check_block_accepted", CheckBlockAccepted::check_block_accepted); } - bool check_block_accepted(cryptonote::core& c, size_t /*ev_index*/, const std::vector& /*events*/) - { - DEFINE_TESTS_ERROR_CONTEXT("gen_block_accepted_base::check_block_accepted"); + bool check_block_accepted(cryptonote::core& c, size_t /*eventIdx*/, const std::vector& /*events*/) { + DEFINE_TESTS_ERROR_CONTEXT("CheckBlockAccepted::check_block_accepted"); CHECK_EQ(0, c.get_pool_transactions_count()); - CHECK_EQ(expected_blockchain_height, c.get_current_blockchain_height()); + CHECK_EQ(m_expectedBlockchainHeight, c.get_current_blockchain_height()); return true; } + +protected: + size_t m_expectedBlockchainHeight; + const uint8_t m_blockMajorVersion; }; -struct gen_block_big_major_version : public gen_block_verification_base<1> -{ + +struct TestBlockMajorVersionAccepted : public CheckBlockAccepted { + TestBlockMajorVersionAccepted(uint8_t blockMajorVersion) : + CheckBlockAccepted(2, blockMajorVersion) {} + + bool generate(std::vector& events) const; +}; + +struct TestBlockMajorVersionRejected : public CheckBlockPurged { + TestBlockMajorVersionRejected(uint8_t blockAcceptedVersion, uint8_t blockGeneratedVersion) : + CheckBlockPurged(1, blockAcceptedVersion), m_blockGeneratedVersion(blockGeneratedVersion) {} + + const uint8_t m_blockGeneratedVersion; + + bool generate(std::vector& events) const; +}; + +struct TestBlockBigMinorVersion : public CheckBlockAccepted { + + TestBlockBigMinorVersion(uint8_t blockMajorVersion) + : CheckBlockAccepted(2, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_big_minor_version : public gen_block_accepted_base<2> +struct gen_block_ts_not_checked : public CheckBlockAccepted { + gen_block_ts_not_checked(uint8_t blockMajorVersion) + : CheckBlockAccepted(0, blockMajorVersion) { + m_expectedBlockchainHeight = m_currency.timestampCheckWindow(); + } + bool generate(std::vector& events) const; }; -struct gen_block_ts_not_checked : public gen_block_accepted_base +struct gen_block_ts_in_past : public CheckBlockPurged { + gen_block_ts_in_past(uint8_t blockMajorVersion) + : CheckBlockPurged(0, blockMajorVersion) { + m_invalidBlockIdx = m_currency.timestampCheckWindow(); + } + bool generate(std::vector& events) const; }; -struct gen_block_ts_in_past : public gen_block_verification_base +struct gen_block_ts_in_future_rejected : public CheckBlockPurged { + gen_block_ts_in_future_rejected(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_ts_in_future : public gen_block_verification_base<1> +struct gen_block_ts_in_future_accepted : public CheckBlockAccepted { + gen_block_ts_in_future_accepted(uint8_t blockMajorVersion) + : CheckBlockAccepted(2, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_invalid_prev_id : public gen_block_verification_base<1> +struct gen_block_invalid_prev_id : public CheckBlockPurged { + gen_block_invalid_prev_id(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/); + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/); }; -struct gen_block_invalid_nonce : public gen_block_verification_base<3> +struct gen_block_invalid_nonce : public CheckBlockPurged { + gen_block_invalid_nonce(uint8_t blockMajorVersion) + : CheckBlockPurged(3, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_no_miner_tx : public gen_block_verification_base<1> +struct gen_block_no_miner_tx : public CheckBlockPurged { + gen_block_no_miner_tx(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_unlock_time_is_low : public gen_block_verification_base<1> +struct gen_block_unlock_time_is_low : public CheckBlockPurged { + gen_block_unlock_time_is_low(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_unlock_time_is_high : public gen_block_verification_base<1> +struct gen_block_unlock_time_is_high : public CheckBlockPurged { + gen_block_unlock_time_is_high(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_unlock_time_is_timestamp_in_past : public gen_block_verification_base<1> +struct gen_block_unlock_time_is_timestamp_in_past : public CheckBlockPurged { + gen_block_unlock_time_is_timestamp_in_past(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_unlock_time_is_timestamp_in_future : public gen_block_verification_base<1> +struct gen_block_unlock_time_is_timestamp_in_future : public CheckBlockPurged { + gen_block_unlock_time_is_timestamp_in_future(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_height_is_low : public gen_block_verification_base<1> +struct gen_block_height_is_low : public CheckBlockPurged { + gen_block_height_is_low(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_height_is_high : public gen_block_verification_base<1> +struct gen_block_height_is_high : public CheckBlockPurged { + gen_block_height_is_high(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_has_2_tx_gen_in : public gen_block_verification_base<1> +struct gen_block_miner_tx_has_2_tx_gen_in : public CheckBlockPurged { + gen_block_miner_tx_has_2_tx_gen_in(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_has_2_in : public gen_block_verification_base +struct gen_block_miner_tx_has_2_in : public CheckBlockPurged { + gen_block_miner_tx_has_2_in(uint8_t blockMajorVersion) + : CheckBlockPurged(0, blockMajorVersion) { + m_invalidBlockIdx = m_currency.minedMoneyUnlockWindow() + 1; + } + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_with_txin_to_key : public gen_block_verification_base +struct gen_block_miner_tx_with_txin_to_key : public CheckBlockPurged { + gen_block_miner_tx_with_txin_to_key(uint8_t blockMajorVersion) + : CheckBlockPurged(0, blockMajorVersion) { + m_invalidBlockIdx = m_currency.minedMoneyUnlockWindow() + 2; + } + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_out_is_small : public gen_block_verification_base<1> +struct gen_block_miner_tx_out_is_small : public CheckBlockPurged { + gen_block_miner_tx_out_is_small(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_out_is_big : public gen_block_verification_base<1> +struct gen_block_miner_tx_out_is_big : public CheckBlockPurged { + gen_block_miner_tx_out_is_big(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_has_no_out : public gen_block_verification_base<1> +struct gen_block_miner_tx_has_no_out : public CheckBlockPurged { + gen_block_miner_tx_has_no_out(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_miner_tx_has_out_to_alice : public gen_block_accepted_base<2> +struct gen_block_miner_tx_has_out_to_alice : public CheckBlockAccepted { + gen_block_miner_tx_has_out_to_alice(uint8_t blockMajorVersion) + : CheckBlockAccepted(2, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_has_invalid_tx : public gen_block_verification_base<1> +struct gen_block_has_invalid_tx : public CheckBlockPurged { + gen_block_has_invalid_tx(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + bool generate(std::vector& events) const; }; -struct gen_block_is_too_big : public gen_block_verification_base<1> +struct gen_block_is_too_big : public CheckBlockPurged { + gen_block_is_too_big(uint8_t blockMajorVersion) + : CheckBlockPurged(1, blockMajorVersion) {} + + bool generate(std::vector& events) const; +}; + +struct TestBlockCumulativeSizeExceedsLimit : public CheckBlockPurged { + TestBlockCumulativeSizeExceedsLimit(uint8_t blockMajorVersion) + : CheckBlockPurged(std::numeric_limits::max(), blockMajorVersion) { + } + bool generate(std::vector& events) const; }; struct gen_block_invalid_binary_format : public test_chain_unit_base { - gen_block_invalid_binary_format(); + gen_block_invalid_binary_format(uint8_t blockMajorVersion); + bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*blk*/); + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/); bool check_all_blocks_purged(cryptonote::core& c, size_t ev_index, const std::vector& events); bool corrupt_blocks_boundary(cryptonote::core& c, size_t ev_index, const std::vector& events); private: + const uint8_t m_blockMajorVersion; size_t m_corrupt_blocks_begin_idx; }; + +struct TestMaxSizeOfParentBlock : public CheckBlockAccepted { + TestMaxSizeOfParentBlock() : CheckBlockAccepted(2, cryptonote::BLOCK_MAJOR_VERSION_2) { + } + + bool generate(std::vector& events) const; +}; + +struct TestBigParentBlock : public CheckBlockPurged { + TestBigParentBlock() : CheckBlockPurged(1, cryptonote::BLOCK_MAJOR_VERSION_2) { + } + + bool generate(std::vector& events) const; +}; + +struct TestBlock2ExtraEmpty : public CheckBlockPurged { + + TestBlock2ExtraEmpty() : CheckBlockPurged(1, cryptonote::BLOCK_MAJOR_VERSION_2) {} + + bool generate(std::vector& events) const; +}; + +struct TestBlock2ExtraWithoutMMTag : public CheckBlockPurged { + + TestBlock2ExtraWithoutMMTag() : CheckBlockPurged(1, cryptonote::BLOCK_MAJOR_VERSION_2) {} + + bool generate(std::vector& events) const; +}; + +struct TestBlock2ExtraWithGarbage : public CheckBlockAccepted { + + TestBlock2ExtraWithGarbage() : CheckBlockAccepted(2, cryptonote::BLOCK_MAJOR_VERSION_2) {} + + bool generate(std::vector& events) const; +}; diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index 082968b642..28719f42a1 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -15,8 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "chain_split_1.h" using namespace std; @@ -167,7 +166,7 @@ bool gen_simple_chain_split_1::check_split_not_switched(cryptonote::core& c, siz //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 2); return true; } @@ -178,7 +177,7 @@ bool gen_simple_chain_split_1::check_split_not_switched2(cryptonote::core& c, si //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3); return true; } @@ -190,7 +189,7 @@ bool gen_simple_chain_split_1::check_split_switched(cryptonote::core& c, size_t //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 10); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 10); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[14]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[14]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3); return true; } @@ -201,7 +200,7 @@ bool gen_simple_chain_split_1::check_split_not_switched_back(cryptonote::core& c //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 14); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 14); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[19]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[19]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8); return true; @@ -214,7 +213,7 @@ bool gen_simple_chain_split_1::check_split_switched_back_1(cryptonote::core& c, //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 15); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 15); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[26]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[26]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8); return true; @@ -226,7 +225,7 @@ bool gen_simple_chain_split_1::check_split_switched_back_2(cryptonote::core& c, //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 16); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 16); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[28]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[28]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8); return true; } diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 15e12dd803..c82b865441 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -15,8 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "chain_switch_1.h" using namespace epee; using namespace cryptonote; @@ -68,7 +67,7 @@ bool gen_chain_switch_1::generate(std::vector& events) const MAKE_TX_LIST_START(events, txs_blk_3, miner_account, recipient_account_2, MK_COINS(7), blk_2); // 8 + 2N MAKE_TX_LIST_START(events, txs_blk_4, miner_account, recipient_account_3, MK_COINS(11), blk_2); // 9 + 2N MAKE_TX_LIST_START(events, txs_blk_5, miner_account, recipient_account_4, MK_COINS(13), blk_2); // 10 + 2N - std::list txs_blk_6; + std::list txs_blk_6; txs_blk_6.push_back(txs_blk_4.front()); // Transactions, that has different order in alt block chains @@ -115,15 +114,15 @@ 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::list blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); - CHECK_EQ(5 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); - CHECK_TEST_CONDITION(blocks.back() == boost::get(events[20 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW])); // blk_4 + CHECK_EQ(5 + 2 * m_currency.minedMoneyUnlockWindow(), blocks.size()); + CHECK_TEST_CONDITION(blocks.back() == boost::get(events[20 + 2 * m_currency.minedMoneyUnlockWindow()])); // blk_4 CHECK_EQ(2, c.get_alternative_blocks_count()); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -132,7 +131,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::list tx_pool; c.get_pool_transactions(tx_pool); CHECK_EQ(1, tx_pool.size()); @@ -152,27 +151,27 @@ 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::list blocks; bool r = c.get_blocks(0, 10000, blocks); CHECK_TEST_CONDITION(r); - CHECK_EQ(6 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks.size()); + CHECK_EQ(6 + 2 * m_currency.minedMoneyUnlockWindow(), blocks.size()); auto it = blocks.end(); --it; --it; --it; 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 + CHECK_TEST_CONDITION(blocks.back() == boost::get(events[24 + 2 * m_currency.minedMoneyUnlockWindow()])); // blk_7 - std::list alt_blocks; + std::list alt_blocks; r = c.get_alternative_blocks(alt_blocks); CHECK_TEST_CONDITION(r); CHECK_EQ(2, c.get_alternative_blocks_count()); // Some blocks that were in main chain are in alt chain now - BOOST_FOREACH(block b, alt_blocks) + BOOST_FOREACH(Block b, alt_blocks) { CHECK_TEST_CONDITION(m_chain_1.end() != std::find(m_chain_1.begin(), m_chain_1.end(), b)); } - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -181,7 +180,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::list tx_pool; c.get_pool_transactions(tx_pool); CHECK_EQ(1, tx_pool.size()); CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front())); diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index cf3a39d49c..6a46754396 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -32,12 +32,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::list 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::list m_tx_pool; }; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 1671713599..58ba58b6e3 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -15,21 +15,28 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "chaingen.h" + #include #include -#include +#include -#include "include_base_utils.h" +#include +#include +#include -#include "console_handler.h" +#include "include_base_utils.h" +#include "misc_language.h" -#include "p2p/net_node.h" +#include "common/command_line.h" +#include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/miner.h" - -#include "chaingen.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/cryptonote_boost_serialization.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/UpgradeDetector.h" using namespace std; @@ -37,226 +44,18 @@ using namespace epee; using namespace cryptonote; -void test_generator::get_block_chain(std::vector& blockchain, const crypto::hash& head, size_t n) const -{ - crypto::hash curr = head; - while (null_hash != curr && blockchain.size() < n) - { - auto it = m_blocks_info.find(curr); - if (m_blocks_info.end() == it) - { - throw std::runtime_error("block hash wasn't found"); - } - - blockchain.push_back(it->second); - curr = it->second.prev_id; - } - - 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 -{ - std::vector blockchain; - get_block_chain(blockchain, head, n); - BOOST_FOREACH(auto& bi, blockchain) - { - block_sizes.push_back(bi.block_size); - } -} - -uint64_t test_generator::get_already_generated_coins(const crypto::hash& blk_id) const -{ - auto it = m_blocks_info.find(blk_id); - if (it == m_blocks_info.end()) - throw std::runtime_error("block hash wasn't found"); - - return it->second.already_generated_coins; -} - -uint64_t test_generator::get_already_generated_coins(const cryptonote::block& blk) const -{ - crypto::hash blk_hash; - get_block_hash(blk, blk_hash); - 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) -{ - const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); - uint64_t block_reward; - get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward); - m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); -} - -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) -{ - blk.major_version = CURRENT_BLOCK_MAJOR_VERSION; - blk.minor_version = CURRENT_BLOCK_MINOR_VERSION; - blk.timestamp = timestamp; - blk.prev_id = prev_id; - - blk.tx_hashes.reserve(tx_list.size()); - BOOST_FOREACH(const transaction &tx, tx_list) - { - crypto::hash tx_hash; - get_transaction_hash(tx, tx_hash); - blk.tx_hashes.push_back(tx_hash); - } - - uint64_t total_fee = 0; - size_t txs_size = 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); - } - - blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx); - size_t target_block_size = txs_size + get_object_blobsize(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)) - return false; - - size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); - if (target_block_size < actual_block_size) - { - target_block_size = actual_block_size; - } - else if (actual_block_size < target_block_size) - { - size_t delta = target_block_size - actual_block_size; - 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) - { - break; - } - else - { - CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size"); - delta = actual_block_size - target_block_size; - 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) - { - break; - } - else - { - CHECK_AND_ASSERT_MES(actual_block_size < target_block_size, 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); - } - } - } - else - { - break; - } - } - - //blk.tree_root_hash = get_tx_tree_hash(blk); - - // Nonce search... - blk.nonce = 0; - crypto::cn_context context; - while (!miner::find_nonce_for_given_block(context, blk, get_test_difficulty(), height)) - blk.timestamp++; - - add_block(blk, txs_size, block_sizes, 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::list tx_list; - return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_sizes, tx_list); -} - -bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, - const cryptonote::account_base& miner_acc, - const std::list& tx_list/* = std::list()*/) -{ - uint64_t height = boost::get(blk_prev.miner_tx.vin.front()).height + 1; - crypto::hash prev_id = get_block_hash(blk_prev); - // 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); - - return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list); -} - -bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc, - int actual_params/* = bf_none*/, uint8_t major_ver/* = 0*/, - uint8_t minor_ver/* = 0*/, uint64_t timestamp/* = 0*/, - 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*/) -{ - 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; - blk.timestamp = actual_params & bf_timestamp ? timestamp : prev_block.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; // Keep difficulty unchanged - blk.prev_id = actual_params & bf_prev_id ? prev_id : get_block_hash(prev_block); - blk.tx_hashes = actual_params & bf_tx_hashes ? tx_hashes : std::vector(); - - 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); - 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); - // 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(), 1)) - return false; - } - - //blk.tree_root_hash = get_tx_tree_hash(blk); - - 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); - - 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) -{ - return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size); -} - - struct output_index { - const cryptonote::txout_target_v out; + const cryptonote::TransactionOutputTarget out; uint64_t amount; size_t blk_height; // block height size_t tx_no; // index of transaction in block size_t out_no; // index of out in transaction size_t idx; bool spent; - const cryptonote::block *p_blk; - const cryptonote::transaction *p_tx; + const cryptonote::Block *p_blk; + const cryptonote::Transaction *p_tx; - output_index(const cryptonote::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::block *_pb, const cryptonote::transaction *_pt) + output_index(const cryptonote::TransactionOutputTarget &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::Block *_pb, const cryptonote::Transaction *_pt) : out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { } output_index(const output_index &other) @@ -301,13 +100,13 @@ namespace } } -bool init_output_indices(map_output_idx_t& outs, std::map >& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) { +bool init_output_indices(map_output_idx_t& outs, std::map >& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) { - BOOST_FOREACH (const block& blk, blockchain) { - vector vtx; - vtx.push_back(&blk.miner_tx); + BOOST_FOREACH (const Block& blk, blockchain) { + vector vtx; + vtx.push_back(&blk.minerTx); - BOOST_FOREACH(const crypto::hash &h, blk.tx_hashes) { + for (const crypto::hash& h : blk.txHashes) { const map_hash2tx_t::const_iterator cit = mtx.find(h); if (mtx.end() == cit) throw std::runtime_error("block contains an unknown tx hash"); @@ -318,22 +117,25 @@ bool init_output_indices(map_output_idx_t& outs, std::map(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]); - - if (2 == out.target.which()) { // out_to_key - outs[out.amount].push_back(oi); - size_t tx_global_idx = outs[out.amount].size() - 1; - outs[out.amount][tx_global_idx].idx = tx_global_idx; - // Is out to me? - if (is_out_to_acc(from.get_keys(), boost::get(out.target), get_tx_pub_key_from_extra(tx), j)) { - outs_mine[out.amount].push_back(tx_global_idx); - } + const TransactionOutput &out = tx.vout[j]; + if (out.target.type() == typeid(TransactionOutputToKey)) { + output_index oi(out.target, out.amount, boost::get(*blk.minerTx.vin.begin()).height, i, j, &blk, vtx[i]); + outs[out.amount].push_back(oi); + size_t tx_global_idx = outs[out.amount].size() - 1; + outs[out.amount][tx_global_idx].idx = tx_global_idx; + // Is out to me? + if (is_out_to_acc(from.get_keys(), boost::get(out.target), get_tx_pub_key_from_extra(tx), keyIndex)) { + outs_mine[out.amount].push_back(tx_global_idx); } + + ++keyIndex; + } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { + keyIndex += boost::get(out.target).keys.size(); + } } } } @@ -341,24 +143,24 @@ bool init_output_indices(map_output_idx_t& outs, std::map& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) { +bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) { - BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) { + for (const map_output_t::value_type& o: outs_mine) { for (size_t i = 0; i < o.second.size(); ++i) { output_index &oi = outs[o.first][o.second[i]]; // construct key image for this output crypto::key_image img; - keypair in_ephemeral; + KeyPair in_ephemeral; generate_key_image_helper(from.get_keys(), get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img); // lookup for this key image in the events vector - BOOST_FOREACH(auto& tx_pair, mtx) { - const transaction& tx = *tx_pair.second; - BOOST_FOREACH(const txin_v &in, tx.vin) { - if (typeid(txin_to_key) == in.type()) { - const txin_to_key &itk = boost::get(in); - if (itk.k_image == img) { + for (auto& tx_pair : mtx) { + const Transaction& tx = *tx_pair.second; + for (const auto& in : tx.vin) { + if (typeid(TransactionInputToKey) == in.type()) { + const TransactionInputToKey &itk = boost::get(in); + if (itk.keyImage == img) { oi.spent = true; } } @@ -398,7 +200,7 @@ bool fill_output_entries(std::vector& out_indices, size_t sender_o if (append) { - const txout_to_key& otk = boost::get(oi.out); + const TransactionOutputToKey& otk = boost::get(oi.out); output_entries.push_back(tx_source_entry::output_entry(oi.idx, otk.key)); } } @@ -407,12 +209,12 @@ bool fill_output_entries(std::vector& out_indices, size_t sender_o } bool fill_tx_sources(std::vector& sources, const std::vector& events, - const block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix) + const Block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix) { map_output_idx_t outs; map_output_t outs_mine; - std::vector blockchain; + std::vector blockchain; map_hash2tx_t mtx; if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head))) return false; @@ -464,7 +266,7 @@ bool fill_tx_destination(tx_destination_entry &de, const cryptonote::account_bas return true; } -void fill_tx_sources_and_destinations(const std::vector& events, const block& blk_head, +void fill_tx_sources_and_destinations(const std::vector& events, const Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix, std::vector& sources, std::vector& destinations) @@ -490,55 +292,7 @@ void fill_tx_sources_and_destinations(const std::vector& event } } -void fill_nonce(cryptonote::block& blk, const difficulty_type& diffic, uint64_t height) -{ - blk.nonce = 0; - crypto::cn_context context; - while (!miner::find_nonce_for_given_block(context, blk, diffic, height)) - blk.timestamp++; -} - -bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins, - const account_public_address& miner_address, transaction& tx, uint64_t fee, - keypair* p_txkey/* = 0*/) -{ - keypair txkey; - txkey = keypair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - - if (0 != p_txkey) - *p_txkey = txkey; - - txin_gen in; - in.height = height; - tx.vin.push_back(in); - - // This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - uint64_t block_reward; - if (!get_block_reward(0, 0, already_generated_coins, block_reward)) - { - LOG_PRINT_L0("Block is too big"); - return false; - } - block_reward += fee; - - crypto::key_derivation derivation; - crypto::public_key out_eph_public_key; - crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); - crypto::derive_public_key(derivation, 0, miner_address.m_spend_public_key, out_eph_public_key); - - tx_out out; - out.amount = block_reward; - out.target = txout_to_key(out_eph_public_key); - tx.vout.push_back(out); - - tx.version = CURRENT_TRANSACTION_VERSION; - tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; - - return true; -} - -bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, const block& blk_head, +bool construct_tx_to_key(const std::vector& events, cryptonote::Transaction& tx, const Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix) { @@ -549,16 +303,16 @@ bool construct_tx_to_key(const std::vector& events, cryptonote return construct_tx(from.get_keys(), sources, destinations, std::vector(), tx, 0); } -transaction construct_tx_with_fee(std::vector& events, const block& blk_head, +Transaction construct_tx_with_fee(std::vector& events, const Block& blk_head, const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee) { - transaction tx; + Transaction tx; construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 0); events.push_back(tx); return tx; } -uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx) { +uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx) { uint64_t res = 0; std::map > outs; std::map > outs_mine; @@ -584,12 +338,12 @@ uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs) +void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs) { std::unordered_set confirmed_hashes; - BOOST_FOREACH(const block& blk, blockchain) + for (const Block& blk : blockchain) { - BOOST_FOREACH(const crypto::hash& tx_hash, blk.tx_hashes) + for (const crypto::hash& tx_hash : blk.txHashes) { confirmed_hashes.insert(tx_hash); } @@ -604,18 +358,18 @@ void get_confirmed_txs(const std::vector& blockchain, const m } } -bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { - std::unordered_map block_index; +bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { + std::unordered_map block_index; BOOST_FOREACH(const test_event_entry& ev, events) { - if (typeid(block) == ev.type()) + if (typeid(Block) == ev.type()) { - const block* blk = &boost::get(ev); + const Block* blk = &boost::get(ev); block_index[get_block_hash(*blk)] = blk; } - else if (typeid(transaction) == ev.type()) + else if (typeid(Transaction) == ev.type()) { - const transaction& tx = boost::get(ev); + const Transaction& tx = boost::get(ev); mtx[get_transaction_hash(tx)] = &tx; } } @@ -625,7 +379,7 @@ bool find_block_chain(const std::vector& events, std::vectorsecond); - id = it->second->prev_id; + id = it->second->prevId; if (null_hash == id) { b_success = true; @@ -638,10 +392,16 @@ bool find_block_chain(const std::vector& events, std::vector &events) { auto cb_it = m_callbacks.find(cb_name); diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index fca74ff5dc..4fedf5eee6 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -17,28 +17,16 @@ #pragma once -#include -#include -#include - -#include -#include #include -#include #include -#include "include_base_utils.h" #include "common/boost_serialization_helper.h" #include "common/command_line.h" - #include "cryptonote_core/account_boost_serialization.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" -#include "misc_language.h" +#include "../TestGenerator/TestGenerator.h" namespace concolor { @@ -122,8 +110,8 @@ struct serialized_object } }; -typedef serialized_object serialized_block; -typedef serialized_object serialized_transaction; +typedef serialized_object serialized_block; +typedef serialized_object serialized_transaction; struct event_visitor_settings { @@ -158,115 +146,57 @@ 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 std::unordered_map map_hash2tx_t; +typedef boost::variant test_event_entry; +typedef std::unordered_map map_hash2tx_t; -class test_chain_unit_base +class test_chain_unit_base: boost::noncopyable { public: - typedef boost::function &events)> verify_callback; + test_chain_unit_base() : + m_currency(cryptonote::CurrencyBuilder().currency()) { + } + + typedef std::function &events)> verify_callback; typedef std::map callbacks_map; + const cryptonote::Currency& currency() const; void register_callback(const std::string& cb_name, verify_callback cb); bool verify(const std::string& cb_name, cryptonote::core& c, size_t ev_index, const std::vector &events); -private: - callbacks_map m_callbacks; -}; - -class test_generator -{ -public: - struct block_info - { - block_info() - : prev_id() - , already_generated_coins(0) - , block_size(0) - { - } - - block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_size) - : prev_id(a_prev_id) - , already_generated_coins(an_already_generated_coins) - , block_size(a_block_size) - { - } - - crypto::hash prev_id; - uint64_t already_generated_coins; - size_t block_size; - }; - - enum block_fields - { - bf_none = 0, - bf_major_ver = 1 << 0, - bf_minor_ver = 1 << 1, - bf_timestamp = 1 << 2, - bf_prev_id = 1 << 3, - bf_miner_tx = 1 << 4, - bf_tx_hashes = 1 << 5, - bf_diffic = 1 << 6 - }; - - 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; - 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); - 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); - 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()); - - bool construct_block_manually(cryptonote::block& blk, const cryptonote::block& prev_block, - const cryptonote::account_base& miner_acc, int actual_params = bf_none, uint8_t major_ver = 0, - uint8_t minor_ver = 0, uint64_t timestamp = 0, const crypto::hash& prev_id = crypto::hash(), - const cryptonote::difficulty_type& diffic = 1, const cryptonote::transaction& miner_tx = cryptonote::transaction(), - const std::vector& tx_hashes = std::vector(), size_t txs_sizes = 0); - bool 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); +protected: + cryptonote::Currency m_currency; private: - std::unordered_map m_blocks_info; + callbacks_map m_callbacks; }; -inline cryptonote::difficulty_type get_test_difficulty() {return 1;} -void fill_nonce(cryptonote::block& blk, const cryptonote::difficulty_type& diffic, uint64_t height); -bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins, - const cryptonote::account_public_address& miner_address, cryptonote::transaction& tx, - uint64_t fee, cryptonote::keypair* p_txkey = 0); -bool construct_tx_to_key(const std::vector& events, cryptonote::transaction& tx, - const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, +bool construct_tx_to_key(const std::vector& events, cryptonote::Transaction& tx, + const cryptonote::Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix); -cryptonote::transaction construct_tx_with_fee(std::vector& events, const cryptonote::block& blk_head, +cryptonote::Transaction construct_tx_with_fee(std::vector& events, const cryptonote::Block& blk_head, const cryptonote::account_base& acc_from, const cryptonote::account_base& acc_to, uint64_t amount, uint64_t fee); -void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); -bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); -void fill_tx_sources_and_destinations(const std::vector& events, const cryptonote::block& blk_head, +void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); +bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); +void fill_tx_sources_and_destinations(const std::vector& events, const cryptonote::Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix, std::vector& sources, std::vector& destinations); -uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx); +uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx); //-------------------------------------------------------------------------- template -auto do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator, int) +auto do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::Transaction& tx, t_test_class& validator, int) -> decltype(validator.check_tx_verification_context(tvc, tx_added, event_index, tx)) { return validator.check_tx_verification_context(tvc, tx_added, event_index, tx); } //-------------------------------------------------------------------------- template -bool do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t /*event_index*/, const cryptonote::transaction& /*tx*/, t_test_class&, long) +bool do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t /*event_index*/, const cryptonote::Transaction& /*tx*/, t_test_class&, long) { // Default block verification context check if (tvc.m_verifivation_failed) @@ -275,21 +205,21 @@ bool do_check_tx_verification_context(const cryptonote::tx_verification_context& } //-------------------------------------------------------------------------- template -bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::transaction& tx, t_test_class& validator) +bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::Transaction& tx, t_test_class& validator) { // SFINAE in action return do_check_tx_verification_context(tvc, tx_added, event_index, tx, 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) +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)) { return validator.check_block_verification_context(bvc, event_index, blk); } //-------------------------------------------------------------------------- template -bool do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t /*event_index*/, const cryptonote::block& /*blk*/, t_test_class&, long) +bool do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t /*event_index*/, const cryptonote::Block& /*blk*/, t_test_class&, long) { // Default block verification context check if (bvc.m_verifivation_failed) @@ -298,7 +228,7 @@ bool do_check_block_verification_context(const cryptonote::block_verification_co } //-------------------------------------------------------------------------- template -bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::block& blk, t_test_class& validator) +bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::Block& blk, t_test_class& validator) { // SFINAE in action return do_check_block_verification_context(bvc, event_index, blk, validator, 0); @@ -345,11 +275,11 @@ struct push_core_event_visitor: public boost::static_visitor return true; } - bool operator()(const cryptonote::transaction& tx) const + bool operator()(const cryptonote::Transaction& tx) const { - log_event("cryptonote::transaction"); + log_event("cryptonote::Transaction"); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + cryptonote::tx_verification_context tvc = boost::value_initialized(); size_t pool_size = m_c.get_pool_transactions_count(); m_c.handle_incoming_tx(t_serializable_object_to_blob(tx), tvc, m_txs_keeped_by_block); bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count(); @@ -358,12 +288,12 @@ struct push_core_event_visitor: public boost::static_visitor return true; } - bool operator()(const cryptonote::block& b) const + bool operator()(const cryptonote::Block& b) const { - log_event("cryptonote::block"); + log_event("cryptonote::Block"); - cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_c.handle_incoming_block(t_serializable_object_to_blob(b), bvc); + cryptonote::block_verification_context bvc = boost::value_initialized(); + m_c.handle_incoming_block_blob(t_serializable_object_to_blob(b), bvc, false, false); bool r = check_block_verification_context(bvc, m_ev_index, b, m_validator); CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed"); return r; @@ -385,17 +315,17 @@ struct push_core_event_visitor: public boost::static_visitor { log_event("serialized_block"); - cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_c.handle_incoming_block(sr_block.data, bvc); + cryptonote::block_verification_context bvc = boost::value_initialized(); + m_c.handle_incoming_block_blob(sr_block.data, bvc, false, false); - cryptonote::block blk; + cryptonote::Block blk; std::stringstream ss; ss << sr_block.data; binary_archive ba(ss); ::serialization::serialize(ba, blk); if (!ss.good()) { - blk = cryptonote::block(); + blk = cryptonote::Block(); } bool r = check_block_verification_context(bvc, m_ev_index, blk, m_validator); CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed"); @@ -406,19 +336,19 @@ struct push_core_event_visitor: public boost::static_visitor { log_event("serialized_transaction"); - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + cryptonote::tx_verification_context tvc = boost::value_initialized();; size_t pool_size = m_c.get_pool_transactions_count(); m_c.handle_incoming_tx(sr_tx.data, tvc, m_txs_keeped_by_block); bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count(); - cryptonote::transaction tx; + cryptonote::Transaction tx; std::stringstream ss; ss << sr_tx.data; binary_archive ba(ss); ::serialization::serialize(ba, tx); if (!ss.good()) { - tx = cryptonote::transaction(); + tx = cryptonote::Transaction(); } bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator); @@ -440,8 +370,8 @@ inline bool replay_events_through_core(cryptonote::core& cr, const std::vector(events[0])); + CHECK_AND_ASSERT_MES(typeid(cryptonote::Block) == events[0].type(), false, "First event must be genesis block creation"); + cr.set_genesis_block(boost::get(events[0])); bool r = true; push_core_event_visitor visitor(cr, events, validator); @@ -457,7 +387,7 @@ inline bool replay_events_through_core(cryptonote::core& cr, const std::vector -inline bool do_replay_events(std::vector& events) +inline bool do_replay_events(std::vector& events, t_test_class& validator) { boost::program_options::options_description desc("Allowed options"); cryptonote::core::init_options(desc); @@ -473,13 +403,13 @@ inline bool do_replay_events(std::vector& events) return false; cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects - cryptonote::core c(&pr); + cryptonote::core c(validator.currency(), &pr); if (!c.init(vm, false)) { std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl; return false; } - t_test_class validator; + return replay_events_through_core(c, events, validator); } //-------------------------------------------------------------------------- @@ -492,7 +422,8 @@ inline bool do_replay_file(const std::string& filename) std::cout << concolor::magenta << "Failed to deserialize data from file: " << filename << concolor::normal << std::endl; return false; } - return do_replay_events(events); + t_test_class validator; + return do_replay_events(events, validator); } //-------------------------------------------------------------------------- #define GENERATE_ACCOUNT(account) \ @@ -512,40 +443,40 @@ inline bool do_replay_file(const std::string& filename) } #define REGISTER_CALLBACK(CB_NAME, CLBACK) \ - register_callback(CB_NAME, boost::bind(&CLBACK, this, _1, _2, _3)); + register_callback(CB_NAME, std::bind(&CLBACK, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); #define REGISTER_CALLBACK_METHOD(CLASS, METHOD) \ - register_callback(#METHOD, boost::bind(&CLASS::METHOD, this, _1, _2, _3)); + register_callback(#METHOD, std::bind(&CLASS::METHOD, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); #define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \ - test_generator generator; \ - cryptonote::block BLK_NAME; \ - generator.construct_block(BLK_NAME, MINER_ACC, TS); \ + test_generator generator(this->m_currency); \ + cryptonote::Block BLK_NAME; \ + generator.constructBlock(BLK_NAME, MINER_ACC, TS); \ VEC_EVENTS.push_back(BLK_NAME); #define MAKE_NEXT_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \ - cryptonote::block BLK_NAME; \ - generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC); \ + cryptonote::Block BLK_NAME; \ + generator.constructBlock(BLK_NAME, PREV_BLOCK, MINER_ACC); \ VEC_EVENTS.push_back(BLK_NAME); #define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \ - cryptonote::block BLK_NAME; \ + cryptonote::Block BLK_NAME; \ { \ - std::list tx_list; \ + std::list tx_list; \ tx_list.push_back(TX1); \ - generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, tx_list); \ + generator.constructBlock(BLK_NAME, PREV_BLOCK, MINER_ACC, tx_list); \ } \ VEC_EVENTS.push_back(BLK_NAME); #define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \ - cryptonote::block BLK_NAME; \ - generator.construct_block(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \ + cryptonote::Block BLK_NAME; \ + generator.constructBlock(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \ VEC_EVENTS.push_back(BLK_NAME); #define REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT) \ - cryptonote::block BLK_NAME; \ + cryptonote::Block BLK_NAME; \ { \ - cryptonote::block blk_last = PREV_BLOCK; \ + cryptonote::Block blk_last = PREV_BLOCK; \ for (size_t i = 0; i < COUNT; ++i) \ { \ MAKE_NEXT_BLOCK(VEC_EVENTS, blk, blk_last, MINER_ACC); \ @@ -554,33 +485,34 @@ inline bool do_replay_file(const std::string& filename) BLK_NAME = blk_last; \ } -#define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW) +#define REWIND_BLOCKS(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \ + REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, this->m_currency.minedMoneyUnlockWindow()) -#define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ - cryptonote::transaction TX_NAME; \ - construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \ +#define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + cryptonote::Transaction TX_NAME; \ + construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, this->m_currency.minimumFee(), NMIX); \ VEC_EVENTS.push_back(TX_NAME); #define MAKE_TX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, 0, HEAD) -#define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ - { \ - cryptonote::transaction t; \ - construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX); \ - SET_NAME.push_back(t); \ - VEC_EVENTS.push_back(t); \ +#define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ + { \ + cryptonote::Transaction t; \ + construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, this->m_currency.minimumFee(), NMIX); \ + SET_NAME.push_back(t); \ + VEC_EVENTS.push_back(t); \ } #define MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, 0, HEAD) #define MAKE_TX_LIST_START(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) \ - std::list SET_NAME; \ + std::list SET_NAME; \ MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD); -#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \ - transaction TX; \ - if (!construct_miner_tx_manually(get_block_height(BLK) + 1, generator.get_already_generated_coins(BLK), \ - miner_account.get_keys().m_account_address, TX, 0, KEY)) \ +#define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \ + Transaction TX; \ + if (!constructMinerTxManually(this->m_currency, get_block_height(BLK) + 1, generator.getAlreadyGeneratedCoins(BLK), \ + miner_account.get_keys().m_account_address, TX, 0, KEY)) \ return false; #define MAKE_MINER_TX_MANUALLY(TX, BLK) MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, 0) @@ -625,7 +557,8 @@ inline bool do_replay_file(const std::string& filename) { \ LOG_PRINT(#genclass << " generation failed: generic exception", 0); \ } \ - if (generated && do_replay_events< genclass >(events)) \ + genclass validator; \ + if (generated && do_replay_events< genclass >(events, validator)) \ { \ std::cout << concolor::green << "#TEST# Succeeded " << #genclass << concolor::normal << '\n'; \ } \ @@ -637,6 +570,35 @@ inline bool do_replay_file(const std::string& filename) std::cout << std::endl; \ } + +template +bool GenerateAndPlay(const char* testname, GenClassT&& g) { + std::vector events; + bool generated = false; + + try { + generated = g.generate(events); + } catch (const std::exception& ex) { + LOG_PRINT(testname << " generation failed: what=" << ex.what(), 0); + } catch (...) { + LOG_PRINT(testname << " generation failed: generic exception", 0); + } + + bool succeeded = generated && do_replay_events(events, g); + + if (succeeded) { + std::cout << concolor::green << "#TEST# Succeeded " << testname << concolor::normal << '\n'; + } else { + std::cout << concolor::magenta << "#TEST# Failed " << testname << concolor::normal << '\n'; + } + + std::cout << std::endl; + return succeeded; +} + +#define GENERATE_AND_PLAY_EX(genclass) { ++tests_count; if (!GenerateAndPlay(#genclass, genclass)) failed_tests.push_back(#genclass); } + + #define CALL_TEST(test_name, function) \ { \ if(!function()) \ @@ -655,5 +617,4 @@ inline bool do_replay_file(const std::string& filename) #define CHECK_TEST_CONDITION(cond) CHECK_AND_ASSERT_MES(cond, false, "[" << perr_context << "] failed: \"" << QUOTEME(cond) << "\"") #define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2) #define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2) -#define MK_COINS(amount) (UINT64_C(amount) * COIN) -#define TESTS_DEFAULT_FEE (MINIMUM_FEE) +#define MK_COINS(amount) (UINT64_C(amount) * cryptonote::parameters::COIN) diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index 0d59f88719..d32a01acb1 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -15,18 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include -#include - -#include "include_base_utils.h" - -#include "console_handler.h" - -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/cryptonote_format_utils.h" - -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "chaingen001.h" using namespace std; @@ -59,13 +48,13 @@ bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const std::vector alice = boost::get(events[1]); // check balances - //std::vector chain; + //std::vector chain; //map_hash2tx_t mtx; - //CHECK_TEST_CONDITION(find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[1])))); + //CHECK_TEST_CONDITION(find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[1])))); //CHECK_TEST_CONDITION(get_block_reward(0) == get_balance(alice, events, chain, mtx)); // check height - std::list blocks; + std::list blocks; std::list outs; bool r = c.get_blocks(0, 100, blocks); //c.get_outs(100, outs); @@ -73,7 +62,7 @@ bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const std::vector CHECK_TEST_CONDITION(blocks.size() == 1); //CHECK_TEST_CONDITION(outs.size() == blocks.size()); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 1); - CHECK_TEST_CONDITION(blocks.back() == boost::get(events[0])); + CHECK_TEST_CONDITION(blocks.back() == boost::get(events[0])); return true; } @@ -101,9 +90,9 @@ bool gen_simple_chain_001::generate(std::vector &events) MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner); //MAKE_TX(events, tx_0, first_miner_account, alice, 151, blk_2); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; - /*bool r = */find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[3]))); + /*bool r = */find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[3]))); std::cout << "BALANCE = " << get_balance(miner, chain, mtx) << std::endl; REWIND_BLOCKS(events, blk_2r, blk_2, miner); @@ -127,7 +116,7 @@ bool gen_simple_chain_001::generate(std::vector &events) //MAKE_BLOCK_TX1(events, blk_3, 3, get_block_hash(blk_0), get_test_target(), first_miner_account, ts_start + 10, tx_0); //DO_CALLBACK(events, "verify_callback_2"); -/* std::vector chain; +/* std::vector chain; map_hash2tx_t mtx; if (!find_block_chain(events, chain, mtx, get_block_hash(blk_6))) throw; diff --git a/tests/core_tests/chaingen_tests_list.h b/tests/core_tests/chaingen001.h similarity index 75% rename from tests/core_tests/chaingen_tests_list.h rename to tests/core_tests/chaingen001.h index f08d741765..63de1d74f2 100644 --- a/tests/core_tests/chaingen_tests_list.h +++ b/tests/core_tests/chaingen001.h @@ -15,20 +15,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#pragma once - +#pragma once #include "chaingen.h" -#include "block_reward.h" -#include "block_validation.h" -#include "chain_split_1.h" -#include "chain_switch_1.h" -#include "double_spend.h" -#include "integer_overflow.h" -#include "ring_signature_1.h" -#include "tx_validation.h" -/************************************************************************/ -/* */ -/************************************************************************/ + class gen_simple_chain_001: public test_chain_unit_base { public: diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 94b14823bc..134f4ece06 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -16,9 +16,20 @@ // along with Bytecoin. If not, see . #include "chaingen.h" -#include "chaingen_tests_list.h" + #include "common/command_line.h" + +#include "block_reward.h" +#include "block_validation.h" +#include "chain_split_1.h" +#include "chain_switch_1.h" +#include "chaingen001.h" +#include "double_spend.h" +#include "integer_overflow.h" +#include "ring_signature_1.h" #include "transaction_tests.h" +#include "tx_validation.h" +#include "upgrade.h" namespace po = boost::program_options; @@ -81,6 +92,10 @@ int main(int argc, char* argv[]) } else if (command_line::get_arg(vm, arg_generate_and_play_test_data)) { +#define GENERATE_AND_PLAY_EX_2VER(TestCase) \ + GENERATE_AND_PLAY_EX(TestCase(cryptonote::BLOCK_MAJOR_VERSION_1)) \ + GENERATE_AND_PLAY_EX(TestCase(cryptonote::BLOCK_MAJOR_VERSION_2)) + GENERATE_AND_PLAY(gen_simple_chain_001); GENERATE_AND_PLAY(gen_simple_chain_split_1); GENERATE_AND_PLAY(one_block); @@ -90,40 +105,50 @@ int main(int argc, char* argv[]) //GENERATE_AND_PLAY(gen_ring_signature_big); // Takes up to XXX hours (if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10) // Block verification tests - GENERATE_AND_PLAY(gen_block_big_major_version); - GENERATE_AND_PLAY(gen_block_big_minor_version); - GENERATE_AND_PLAY(gen_block_ts_not_checked); - GENERATE_AND_PLAY(gen_block_ts_in_past); - GENERATE_AND_PLAY(gen_block_ts_in_future); - GENERATE_AND_PLAY(gen_block_invalid_prev_id); - GENERATE_AND_PLAY(gen_block_invalid_nonce); - GENERATE_AND_PLAY(gen_block_no_miner_tx); - GENERATE_AND_PLAY(gen_block_unlock_time_is_low); - GENERATE_AND_PLAY(gen_block_unlock_time_is_high); - GENERATE_AND_PLAY(gen_block_unlock_time_is_timestamp_in_past); - GENERATE_AND_PLAY(gen_block_unlock_time_is_timestamp_in_future); - GENERATE_AND_PLAY(gen_block_height_is_low); - GENERATE_AND_PLAY(gen_block_height_is_high); - GENERATE_AND_PLAY(gen_block_miner_tx_has_2_tx_gen_in); - GENERATE_AND_PLAY(gen_block_miner_tx_has_2_in); - GENERATE_AND_PLAY(gen_block_miner_tx_with_txin_to_key); - GENERATE_AND_PLAY(gen_block_miner_tx_out_is_small); - GENERATE_AND_PLAY(gen_block_miner_tx_out_is_big); - GENERATE_AND_PLAY(gen_block_miner_tx_has_no_out); - GENERATE_AND_PLAY(gen_block_miner_tx_has_out_to_alice); - GENERATE_AND_PLAY(gen_block_has_invalid_tx); - GENERATE_AND_PLAY(gen_block_is_too_big); - GENERATE_AND_PLAY(gen_block_invalid_binary_format); // Takes up to 3 hours, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 500, up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10 + GENERATE_AND_PLAY_EX_2VER(TestBlockMajorVersionAccepted); + GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(cryptonote::BLOCK_MAJOR_VERSION_1, cryptonote::BLOCK_MAJOR_VERSION_2)); + GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(cryptonote::BLOCK_MAJOR_VERSION_2, cryptonote::BLOCK_MAJOR_VERSION_1)); + GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(cryptonote::BLOCK_MAJOR_VERSION_2, cryptonote::BLOCK_MAJOR_VERSION_2 + 1)); + GENERATE_AND_PLAY_EX_2VER(TestBlockBigMinorVersion); + GENERATE_AND_PLAY_EX_2VER(gen_block_ts_not_checked); + GENERATE_AND_PLAY_EX_2VER(gen_block_ts_in_past); + GENERATE_AND_PLAY_EX_2VER(gen_block_ts_in_future_rejected); + GENERATE_AND_PLAY_EX_2VER(gen_block_ts_in_future_accepted); + GENERATE_AND_PLAY_EX_2VER(gen_block_invalid_prev_id); + GENERATE_AND_PLAY_EX_2VER(gen_block_invalid_nonce); + GENERATE_AND_PLAY_EX_2VER(gen_block_no_miner_tx); + GENERATE_AND_PLAY_EX_2VER(gen_block_unlock_time_is_low); + GENERATE_AND_PLAY_EX_2VER(gen_block_unlock_time_is_high); + GENERATE_AND_PLAY_EX_2VER(gen_block_unlock_time_is_timestamp_in_past); + GENERATE_AND_PLAY_EX_2VER(gen_block_unlock_time_is_timestamp_in_future); + GENERATE_AND_PLAY_EX_2VER(gen_block_height_is_low); + GENERATE_AND_PLAY_EX_2VER(gen_block_height_is_high); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_has_2_tx_gen_in); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_has_2_in); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_with_txin_to_key); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_out_is_small); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_out_is_big); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_has_no_out); + GENERATE_AND_PLAY_EX_2VER(gen_block_miner_tx_has_out_to_alice); + GENERATE_AND_PLAY_EX_2VER(gen_block_has_invalid_tx); + GENERATE_AND_PLAY_EX_2VER(gen_block_is_too_big); + GENERATE_AND_PLAY_EX_2VER(TestBlockCumulativeSizeExceedsLimit); + GENERATE_AND_PLAY_EX_2VER(gen_block_invalid_binary_format); // Takes up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10 + + GENERATE_AND_PLAY(TestMaxSizeOfParentBlock); + GENERATE_AND_PLAY(TestBigParentBlock); + GENERATE_AND_PLAY(TestBlock2ExtraEmpty); + GENERATE_AND_PLAY(TestBlock2ExtraWithoutMMTag); + GENERATE_AND_PLAY(TestBlock2ExtraWithGarbage); // Transaction verification tests GENERATE_AND_PLAY(gen_tx_big_version); GENERATE_AND_PLAY(gen_tx_unlock_time); - GENERATE_AND_PLAY(gen_tx_input_is_not_txin_to_key); GENERATE_AND_PLAY(gen_tx_no_inputs_no_outputs); GENERATE_AND_PLAY(gen_tx_no_inputs_has_outputs); GENERATE_AND_PLAY(gen_tx_has_inputs_no_outputs); GENERATE_AND_PLAY(gen_tx_invalid_input_amount); - GENERATE_AND_PLAY(gen_tx_input_wo_key_offsets); + GENERATE_AND_PLAY(gen_tx_in_to_key_wo_key_offsets); GENERATE_AND_PLAY(gen_tx_sender_key_offest_not_exist); GENERATE_AND_PLAY(gen_tx_key_offest_points_to_foreign_key); GENERATE_AND_PLAY(gen_tx_mixed_key_offest_not_exist); @@ -132,9 +157,28 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_tx_check_input_unlock_time); GENERATE_AND_PLAY(gen_tx_txout_to_key_has_invalid_key); GENERATE_AND_PLAY(gen_tx_output_with_zero_amount); - GENERATE_AND_PLAY(gen_tx_output_is_not_txout_to_key); GENERATE_AND_PLAY(gen_tx_signatures_are_invalid); + // multisignature output + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 1, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(2, 2, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(3, 2, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(0, 0, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 0, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(0, 1, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 2, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(2, 3, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_InvalidOutputSignature()); + + // multisignature input + GENERATE_AND_PLAY_EX(MultiSigTx_Input(1, 1, 1, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_Input(2, 1, 1, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_Input(3, 2, 2, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_Input(1, 1, 0, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_Input(2, 2, 1, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_Input(3, 2, 1, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_BadInputSignature()); + // Double spend GENERATE_AND_PLAY(gen_double_spend_in_tx); GENERATE_AND_PLAY(gen_double_spend_in_tx); @@ -148,10 +192,23 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_double_spend_in_alt_chain_in_different_blocks); GENERATE_AND_PLAY(gen_double_spend_in_alt_chain_in_different_blocks); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendInTx(false)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendInTx(true)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendSameBlock(false)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendSameBlock(true)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendDifferentBlocks(false)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendDifferentBlocks(true)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainSameBlock(false)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainSameBlock(true)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainDifferentBlocks(false)); + GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainDifferentBlocks(true)); + + GENERATE_AND_PLAY(gen_uint_overflow_1); GENERATE_AND_PLAY(gen_uint_overflow_2); GENERATE_AND_PLAY(gen_block_reward); + GENERATE_AND_PLAY(gen_upgrade); std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta); std::cout << "\nREPORT:\n"; diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 423f3ab595..1e5004ac99 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -15,17 +15,18 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "double_spend.h" +#include "TestGenerator.h" using namespace epee; using namespace cryptonote; - //====================================================================================================================== gen_double_spend_in_different_chains::gen_double_spend_in_different_chains() { + expected_blockchain_height = 4 + 2 * m_currency.minedMoneyUnlockWindow(); + REGISTER_CALLBACK_METHOD(gen_double_spend_in_different_chains, check_double_spend); } @@ -34,9 +35,9 @@ bool gen_double_spend_in_different_chains::generate(std::vector block_list; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); + std::list block_list; + bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), block_list); CHECK_TEST_CONDITION(r); - std::vector blocks(block_list.begin(), block_list.end()); + std::vector blocks(block_list.begin(), block_list.end()); CHECK_EQ(expected_blockchain_height, blocks.size()); CHECK_EQ(1, c.get_pool_transactions_count()); @@ -72,12 +73,310 @@ bool gen_double_spend_in_different_chains::check_double_spend(cryptonote::core& cryptonote::account_base bob_account = boost::get(events[1]); cryptonote::account_base alice_account = boost::get(events[2]); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); CHECK_EQ(0, get_balance(bob_account, blocks, mtx)); - CHECK_EQ(send_amount - TESTS_DEFAULT_FEE, get_balance(alice_account, blocks, mtx)); + CHECK_EQ(send_amount - m_currency.minimumFee(), get_balance(alice_account, blocks, mtx)); + + return true; +} + +//====================================================================================================================== +// DoubleSpendBase +//====================================================================================================================== +DoubleSpendBase::DoubleSpendBase() : + m_invalid_tx_index(invalid_index_value), + m_invalid_block_index(invalid_index_value), + send_amount(MK_COINS(17)), + has_invalid_tx(false) +{ + m_bob_account.generate(); + m_alice_account.generate(); + + REGISTER_CALLBACK_METHOD(DoubleSpendBase, mark_last_valid_block); + REGISTER_CALLBACK_METHOD(DoubleSpendBase, mark_invalid_tx); + REGISTER_CALLBACK_METHOD(DoubleSpendBase, mark_invalid_block); + REGISTER_CALLBACK_METHOD(DoubleSpendBase, check_double_spend); +} + +bool DoubleSpendBase::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 DoubleSpendBase::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 DoubleSpendBase::mark_last_valid_block(cryptonote::core& c, size_t /*ev_index*/, const std::vector& /*events*/) +{ + m_last_valid_block = c.get_blockchain_storage().get_tail_id(); + return true; +} + +bool DoubleSpendBase::mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +{ + m_invalid_tx_index = ev_index + 1; + return true; +} + +bool DoubleSpendBase::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +{ + m_invalid_block_index = ev_index + 1; + return true; +} + +bool DoubleSpendBase::check_double_spend(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events) +{ + DEFINE_TESTS_ERROR_CONTEXT("DoubleSpendBase::check_double_spend"); + CHECK_EQ(m_last_valid_block, c.get_blockchain_storage().get_tail_id()); + return true; +} + +TestGenerator DoubleSpendBase::prepare(std::vector& events) const { + + TestGenerator generator(m_currency, events); + + // unlock + generator.generateBlocks(); + + auto builder = generator.createTxBuilder(generator.minerAccount, m_bob_account, send_amount, m_currency.minimumFee()); + + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(m_bob_account.get_keys()); + + builder.addMultisignatureOut(send_amount, kv, 1); + + // move money + auto tx = builder.build(); + + generator.addEvent(tx); + generator.makeNextBlock(tx); + + // unlock + generator.generateBlocks(); + + return generator; +} + +TransactionBuilder DoubleSpendBase::createBobToAliceTx() const { + TransactionBuilder builder(m_currency); + + TransactionInputMultisignature msigInput; + msigInput.amount = send_amount; + msigInput.outputIndex = 0; + msigInput.signatures = 1; + + TransactionBuilder::KeysVector kv; + kv.push_back(m_bob_account.get_keys()); + + builder. + addMultisignatureInput(msigInput, kv). + addOutput(tx_destination_entry(send_amount - m_currency.minimumFee(), m_alice_account.get_keys().m_account_address)); + + return builder; +} + +//====================================================================================================================== +// MultiSigTx_DoubleSpendInTx +//====================================================================================================================== + +MultiSigTx_DoubleSpendInTx::MultiSigTx_DoubleSpendInTx(bool txsKeepedByBlock) + : m_txsKeepedByBlock(txsKeepedByBlock) +{ + has_invalid_tx = true; +} + +bool MultiSigTx_DoubleSpendInTx::generate(std::vector& events) const { + TestGenerator generator(prepare(events)); + + generator.addCallback("mark_last_valid_block"); + + TransactionInputMultisignature msigInput; + msigInput.amount = send_amount; + msigInput.outputIndex = 0; + msigInput.signatures = 1; + + TransactionBuilder::KeysVector kv; + kv.push_back(m_bob_account.get_keys()); + + TransactionBuilder builder(generator.currency()); + + auto tx = builder. + addMultisignatureInput(msigInput, kv). + addMultisignatureInput(msigInput, kv). + addOutput(tx_destination_entry(send_amount*2 - m_currency.minimumFee(), m_alice_account.get_keys().m_account_address)). + build(); + + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, m_txsKeepedByBlock); + + generator.addCallback("mark_invalid_tx"); // should be rejected by the core + generator.addEvent(tx); + generator.addCallback("mark_invalid_block"); + generator.makeNextBlock(tx); + generator.addCallback("check_double_spend"); + + return true; +} + +//====================================================================================================================== +// MultiSigTx_DoubleSpendSameBlock +//====================================================================================================================== +MultiSigTx_DoubleSpendSameBlock::MultiSigTx_DoubleSpendSameBlock(bool txsKeepedByBlock) + : m_txsKeepedByBlock(txsKeepedByBlock) { + has_invalid_tx = !txsKeepedByBlock; +} + +bool MultiSigTx_DoubleSpendSameBlock::generate(std::vector& events) const { + TestGenerator generator(prepare(events)); + + generator.addCallback("mark_last_valid_block"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, m_txsKeepedByBlock); + + std::list txs; + + auto builder = createBobToAliceTx(); + + auto tx1 = builder.newTxKeys().build(); + auto tx2 = builder.newTxKeys().build(); + + generator.addEvent(tx1); + + if (has_invalid_tx) { + generator.addCallback("mark_invalid_tx"); + } + + generator.addEvent(tx2); + + txs.push_back(tx1); + txs.push_back(tx2); + + generator.addCallback("mark_invalid_block"); + generator.makeNextBlock(txs); + generator.addCallback("check_double_spend"); + + return true; +} + +//====================================================================================================================== +// MultiSigTx_DoubleSpendDifferentBlocks +//====================================================================================================================== +MultiSigTx_DoubleSpendDifferentBlocks::MultiSigTx_DoubleSpendDifferentBlocks(bool txsKeepedByBlock) + : m_txsKeepedByBlock(txsKeepedByBlock) { + has_invalid_tx = !txsKeepedByBlock; +} + +bool MultiSigTx_DoubleSpendDifferentBlocks::generate(std::vector& events) const { + TestGenerator generator(prepare(events)); + + generator.addCallback("mark_last_valid_block"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, m_txsKeepedByBlock); + + auto builder = createBobToAliceTx(); + + auto tx1 = builder.build(); + + generator.addEvent(tx1); + generator.makeNextBlock(tx1); + generator.addCallback("mark_last_valid_block"); + + auto tx2 = builder.newTxKeys().build(); // same transaction, but different tx key + + if (has_invalid_tx) { + generator.addCallback("mark_invalid_tx"); + } + + generator.addEvent(tx2); + generator.addCallback("mark_invalid_block"); + generator.makeNextBlock(tx2); + generator.addCallback("check_double_spend"); + + return true; +} + +//====================================================================================================================== +// MultiSigTx_DoubleSpendAltChainSameBlock +//====================================================================================================================== + +MultiSigTx_DoubleSpendAltChainSameBlock::MultiSigTx_DoubleSpendAltChainSameBlock(bool txsKeepedByBlock) + : m_txsKeepedByBlock(txsKeepedByBlock) { + has_invalid_tx = !txsKeepedByBlock; +} + +bool MultiSigTx_DoubleSpendAltChainSameBlock::generate(std::vector& events) const { + TestGenerator mainChain(prepare(events)); + TestGenerator altChain(mainChain); + + mainChain.makeNextBlock(); // main chain + mainChain.addCallback("mark_last_valid_block"); + + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, m_txsKeepedByBlock); + + auto builder = createBobToAliceTx(); + + std::list txs; + auto tx1 = builder.build(); + auto tx2 = builder.newTxKeys().build(); + txs.push_back(tx1); + txs.push_back(tx2); + + altChain.addEvent(tx1); + altChain.addEvent(tx2); + altChain.makeNextBlock(txs); + altChain.generateBlocks(); // force switch to alt chain + + mainChain.addCallback("check_double_spend"); + return true; +} + +//====================================================================================================================== +// MultiSigTx_DoubleSpendAltChainDifferentBlocks +//====================================================================================================================== + +MultiSigTx_DoubleSpendAltChainDifferentBlocks::MultiSigTx_DoubleSpendAltChainDifferentBlocks(bool txsKeepedByBlock) + : m_txsKeepedByBlock(txsKeepedByBlock) { + has_invalid_tx = !txsKeepedByBlock; +} + +bool MultiSigTx_DoubleSpendAltChainDifferentBlocks::generate(std::vector& events) const { + TestGenerator mainChain(prepare(events)); + TestGenerator altChain(mainChain); + + mainChain.makeNextBlock(); // main chain + + mainChain.addCallback("mark_last_valid_block"); + SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, m_txsKeepedByBlock); + + auto builder = createBobToAliceTx(); + + auto tx1 = builder.build(); + + altChain.addEvent(tx1); + altChain.makeNextBlock(tx1); + altChain.addCallback("mark_last_valid_block"); + + auto tx2 = builder.newTxKeys().build(); + + if (has_invalid_tx) { + altChain.addCallback("mark_invalid_tx"); + } + + altChain.addEvent(tx2); + altChain.addCallback("mark_invalid_block"); + altChain.makeNextBlock(tx2); + + mainChain.addCallback("check_double_spend"); return true; } diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 2614298968..ebbf67870f 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -17,6 +17,7 @@ #pragma once #include "chaingen.h" +#include "TransactionBuilder.h" const size_t invalid_index_value = std::numeric_limits::max(); @@ -29,8 +30,8 @@ class gen_double_spend_base : public test_chain_unit_base gen_double_spend_base(); - bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& tx); - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& block); + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); bool mark_invalid_tx(cryptonote::core& c, size_t ev_index, const std::vector& events); @@ -38,7 +39,7 @@ class gen_double_spend_base : public test_chain_unit_base bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector& events); private: - cryptonote::block m_last_valid_block; + cryptonote::Block m_last_valid_block; size_t m_invalid_tx_index; size_t m_invalid_block_index; }; @@ -77,11 +78,19 @@ struct gen_double_spend_in_different_blocks : public gen_double_spend_base< gen_ static const bool has_invalid_tx = !txs_keeped_by_block; static const size_t expected_pool_txs_count = has_invalid_tx ? 0 : 1; static const uint64_t expected_bob_balance = 0; - static const uint64_t expected_alice_balance = send_amount - TESTS_DEFAULT_FEE; + static uint64_t expected_alice_balance; + + gen_double_spend_in_different_blocks() : + gen_double_spend_base< gen_double_spend_in_different_blocks >() { + expected_alice_balance = send_amount - this->m_currency.minimumFee(); + } bool generate(std::vector& events) const; }; +template +uint64_t gen_double_spend_in_different_blocks::expected_alice_balance; + template struct gen_double_spend_in_alt_chain_in_the_same_block : public gen_double_spend_base< gen_double_spend_in_alt_chain_in_the_same_block > @@ -113,7 +122,7 @@ class gen_double_spend_in_different_chains : public test_chain_unit_base { public: static const uint64_t send_amount = MK_COINS(31); - static const size_t expected_blockchain_height = 4 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + size_t expected_blockchain_height; gen_double_spend_in_different_chains(); @@ -123,6 +132,95 @@ class gen_double_spend_in_different_chains : public test_chain_unit_base }; +class TestGenerator; + +class DoubleSpendBase : public test_chain_unit_base +{ +public: + + // parameters to be checked + uint64_t send_amount; + bool has_invalid_tx; + + DoubleSpendBase(); + + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); + + bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_tx(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector& events); + + TestGenerator prepare(std::vector& events) const; + TransactionBuilder createBobToAliceTx() const; + +protected: + + cryptonote::account_base m_bob_account; + cryptonote::account_base m_alice_account; + +private: + + crypto::hash m_last_valid_block; + size_t m_invalid_tx_index; + size_t m_invalid_block_index; +}; + + +struct MultiSigTx_DoubleSpendInTx : public DoubleSpendBase +{ + const bool m_txsKeepedByBlock; + + MultiSigTx_DoubleSpendInTx(bool txsKeepedByBlock); + + bool generate(std::vector& events) const; +}; + +struct MultiSigTx_DoubleSpendSameBlock : public DoubleSpendBase +{ + const bool m_txsKeepedByBlock; + + MultiSigTx_DoubleSpendSameBlock(bool txsKeepedByBlock); + + bool generate(std::vector& events) const; +}; + + +struct MultiSigTx_DoubleSpendDifferentBlocks : public DoubleSpendBase +{ + const bool m_txsKeepedByBlock; + + MultiSigTx_DoubleSpendDifferentBlocks(bool txsKeepedByBlock); + + bool generate(std::vector& events) const; +}; + +struct MultiSigTx_DoubleSpendAltChainSameBlock : public DoubleSpendBase +{ + const bool m_txsKeepedByBlock; + + MultiSigTx_DoubleSpendAltChainSameBlock(bool txsKeepedByBlock); + + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx) { + return true; + } + + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block) { + return true; + } + + bool generate(std::vector& events) const; +}; + +struct MultiSigTx_DoubleSpendAltChainDifferentBlocks : public DoubleSpendBase +{ + const bool m_txsKeepedByBlock; + MultiSigTx_DoubleSpendAltChainDifferentBlocks(bool txsKeepedByBlock); + bool generate(std::vector& events) const; +}; + + #define INIT_DOUBLE_SPEND_TEST() \ uint64_t ts_start = 1338224400; \ GENERATE_ACCOUNT(miner_account); \ diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index f0b895a056..aa9c2320fe 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -31,7 +31,7 @@ gen_double_spend_base::gen_double_spend_base() } template -bool gen_double_spend_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/) +bool gen_double_spend_base::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; @@ -40,7 +40,7 @@ bool gen_double_spend_base::check_tx_verification_context(const c } template -bool gen_double_spend_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/) +bool gen_double_spend_base::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; @@ -51,7 +51,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::list 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(); @@ -83,8 +83,8 @@ bool gen_double_spend_base::check_double_spend(cryptonote::core& } CHECK_NOT_EQ(invalid_index_value, m_invalid_block_index); - std::list block_list; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, block_list); + std::list block_list; + bool r = c.get_blocks(0, 100 + 2 * this->m_currency.minedMoneyUnlockWindow(), block_list); CHECK_TEST_CONDITION(r); CHECK_TEST_CONDITION(m_last_valid_block == block_list.back()); @@ -93,9 +93,9 @@ bool gen_double_spend_base::check_double_spend(cryptonote::core& cryptonote::account_base bob_account = boost::get(events[1]); cryptonote::account_base alice_account = boost::get(events[2]); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; - std::vector blocks(block_list.begin(), block_list.end()); + std::vector blocks(block_list.begin(), block_list.end()); r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); CHECK_EQ(concrete_test::expected_bob_balance, get_balance(bob_account, blocks, mtx)); @@ -115,7 +115,7 @@ bool gen_double_spend_in_tx::generate(std::vector sources; cryptonote::tx_source_entry se; se.amount = tx_0.vout[0].amount; - se.outputs.push_back(std::make_pair(0, boost::get(tx_0.vout[0].target).key)); + se.outputs.push_back(std::make_pair(0, boost::get(tx_0.vout[0].target).key)); se.real_output = 0; se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0); se.real_output_in_tx_index = 0; @@ -125,11 +125,11 @@ bool gen_double_spend_in_tx::generate(std::vectorm_currency.minimumFee(); std::vector destinations; destinations.push_back(de); - cryptonote::transaction tx_1; + cryptonote::Transaction tx_1; if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector(), tx_1, 0)) return false; @@ -151,8 +151,8 @@ bool gen_double_spend_in_the_same_block::generate(std::vect DO_CALLBACK(events, "mark_last_valid_block"); SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); - MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); - cryptonote::transaction tx_1 = txs_1.front(); + MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); + cryptonote::Transaction tx_1 = txs_1.front(); auto tx_1_idx = events.size() - 1; // Remove tx_1, it is being inserted back a little later events.pop_back(); @@ -161,7 +161,7 @@ bool gen_double_spend_in_the_same_block::generate(std::vect { DO_CALLBACK(events, "mark_invalid_tx"); } - MAKE_TX_LIST(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); + MAKE_TX_LIST(events, txs_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); events.insert(events.begin() + tx_1_idx, tx_1); DO_CALLBACK(events, "mark_invalid_block"); MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_1); @@ -179,9 +179,9 @@ bool gen_double_spend_in_different_blocks::generate(std::ve SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); // Create two identical transactions, but don't push it to events list - MAKE_TX(events, tx_blk_2, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); + MAKE_TX(events, tx_blk_2, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); events.pop_back(); - MAKE_TX(events, tx_blk_3, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); + MAKE_TX(events, tx_blk_3, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); events.pop_back(); events.push_back(tx_blk_2); @@ -213,8 +213,8 @@ bool gen_double_spend_in_alt_chain_in_the_same_block::gener DO_CALLBACK(events, "mark_last_valid_block"); // Alt chain - MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); - cryptonote::transaction tx_1 = txs_1.front(); + MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); + cryptonote::Transaction tx_1 = txs_1.front(); auto tx_1_idx = events.size() - 1; // Remove tx_1, it is being inserted back a little later events.pop_back(); @@ -223,7 +223,7 @@ bool gen_double_spend_in_alt_chain_in_the_same_block::gener { DO_CALLBACK(events, "mark_invalid_tx"); } - MAKE_TX_LIST(events, txs_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); + MAKE_TX_LIST(events, txs_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); events.insert(events.begin() + tx_1_idx, tx_1); MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_1r, miner_account, txs_1); @@ -248,9 +248,9 @@ bool gen_double_spend_in_alt_chain_in_different_blocks::gen DO_CALLBACK(events, "mark_last_valid_block"); // Alternative chain - MAKE_TX(events, tx_1, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); + MAKE_TX(events, tx_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); events.pop_back(); - MAKE_TX(events, tx_2, bob_account, alice_account, send_amount - TESTS_DEFAULT_FEE, blk_1); + MAKE_TX(events, tx_2, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); events.pop_back(); events.push_back(tx_1); diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 3a9edcb7d1..e1ddeb6f48 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -15,9 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" - #include "integer_overflow.h" using namespace epee; @@ -25,30 +22,30 @@ using namespace cryptonote; namespace { - void split_miner_tx_outs(transaction& miner_tx, uint64_t amount_1) + void split_miner_tx_outs(Transaction& miner_tx, uint64_t amount_1) { uint64_t total_amount = get_outs_money_amount(miner_tx); uint64_t amount_2 = total_amount - amount_1; - txout_target_v target = miner_tx.vout[0].target; + TransactionOutputTarget target = miner_tx.vout[0].target; miner_tx.vout.clear(); - tx_out out1; + TransactionOutput out1; out1.amount = amount_1; out1.target = target; miner_tx.vout.push_back(out1); - tx_out out2; + TransactionOutput out2; out2.amount = amount_2; out2.target = target; miner_tx.vout.push_back(out2); } - void append_tx_source_entry(std::vector& sources, const transaction& tx, size_t out_idx) + void append_tx_source_entry(std::vector& sources, const Transaction& tx, size_t out_idx) { cryptonote::tx_source_entry se; se.amount = tx.vout[out_idx].amount; - se.outputs.push_back(std::make_pair(0, boost::get(tx.vout[out_idx].target).key)); + se.outputs.push_back(std::make_pair(0, boost::get(tx.vout[out_idx].target).key)); se.real_output = 0; se.real_out_tx_key = get_tx_pub_key_from_extra(tx); se.real_output_in_tx_index = out_idx; @@ -65,12 +62,12 @@ gen_uint_overflow_base::gen_uint_overflow_base() REGISTER_CALLBACK_METHOD(gen_uint_overflow_1, mark_last_valid_block); } -bool gen_uint_overflow_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& /*tx*/) +bool gen_uint_overflow_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& /*tx*/) { return m_last_valid_block_event_idx < event_idx ? !tx_added && tvc.m_verifivation_failed : tx_added && !tvc.m_verifivation_failed; } -bool gen_uint_overflow_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/) +bool gen_uint_overflow_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*block*/) { return m_last_valid_block_event_idx < event_idx ? bvc.m_verifivation_failed | bvc.m_marked_as_orphaned : !bvc.m_verifivation_failed; } @@ -95,31 +92,31 @@ bool gen_uint_overflow_1::generate(std::vector& events) const // Problem 1. Miner tx output overflow MAKE_MINER_TX_MANUALLY(miner_tx_0, blk_0); - split_miner_tx_outs(miner_tx_0, MONEY_SUPPLY); - block blk_1; - if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_0)) + split_miner_tx_outs(miner_tx_0, m_currency.moneySupply()); + Block blk_1; + if (!generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_0)) return false; events.push_back(blk_1); // Problem 1. Miner tx outputs overflow MAKE_MINER_TX_MANUALLY(miner_tx_1, blk_1); - split_miner_tx_outs(miner_tx_1, MONEY_SUPPLY); - block blk_2; - if (!generator.construct_block_manually(blk_2, blk_1, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_1)) + split_miner_tx_outs(miner_tx_1, m_currency.moneySupply()); + Block blk_2; + if (!generator.constructBlockManually(blk_2, blk_1, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_1)) return false; events.push_back(blk_2); REWIND_BLOCKS(events, blk_2r, blk_2, miner_account); - MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2); - MAKE_TX_LIST(events, txs_0, miner_account, bob_account, MONEY_SUPPLY, blk_2); + MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, m_currency.moneySupply(), blk_2); + MAKE_TX_LIST(events, txs_0, miner_account, bob_account, m_currency.moneySupply(), blk_2); MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_0); REWIND_BLOCKS(events, blk_3r, blk_3, miner_account); // Problem 2. total_fee overflow, block_reward overflow - std::list txs_1; + std::list txs_1; // Create txs with huge fee - txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), MONEY_SUPPLY - MK_COINS(1))); - txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), MONEY_SUPPLY - MK_COINS(1))); + txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), m_currency.moneySupply() - MK_COINS(1))); + txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), m_currency.moneySupply() - MK_COINS(1))); MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_1); return true; @@ -140,11 +137,11 @@ bool gen_uint_overflow_2::generate(std::vector& events) const // Problem 1. Regular tx outputs overflow std::vector sources; - for (size_t i = 0; i < blk_0.miner_tx.vout.size(); ++i) + for (size_t i = 0; i < blk_0.minerTx.vout.size(); ++i) { - if (TESTS_DEFAULT_FEE < blk_0.miner_tx.vout[i].amount) + if (m_currency.minimumFee() < blk_0.minerTx.vout[i].amount) { - append_tx_source_entry(sources, blk_0.miner_tx, i); + append_tx_source_entry(sources, blk_0.minerTx, i); break; } } @@ -154,13 +151,13 @@ bool gen_uint_overflow_2::generate(std::vector& events) const } std::vector destinations; - const account_public_address& bob_addr = bob_account.get_keys().m_account_address; - destinations.push_back(tx_destination_entry(MONEY_SUPPLY, bob_addr)); - destinations.push_back(tx_destination_entry(MONEY_SUPPLY - 1, bob_addr)); - // sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + TESTS_DEFAULT_FEE - destinations.push_back(tx_destination_entry(sources.front().amount - MONEY_SUPPLY - MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr)); + const AccountPublicAddress& bob_addr = bob_account.get_keys().m_account_address; + destinations.push_back(tx_destination_entry(m_currency.moneySupply(), bob_addr)); + destinations.push_back(tx_destination_entry(m_currency.moneySupply() - 1, bob_addr)); + // sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + m_currency.minimumFee() + destinations.push_back(tx_destination_entry(sources.front().amount - m_currency.moneySupply() - m_currency.moneySupply() + 1 - m_currency.minimumFee(), bob_addr)); - cryptonote::transaction tx_1; + cryptonote::Transaction tx_1; if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tx_1, 0)) return false; events.push_back(tx_1); @@ -173,7 +170,7 @@ bool gen_uint_overflow_2::generate(std::vector& events) const for (size_t i = 0; i < tx_1.vout.size(); ++i) { auto& tx_1_out = tx_1.vout[i]; - if (tx_1_out.amount < MONEY_SUPPLY - 1) + if (tx_1_out.amount < m_currency.moneySupply() - 1) continue; append_tx_source_entry(sources, tx_1, i); @@ -182,11 +179,11 @@ bool gen_uint_overflow_2::generate(std::vector& events) const destinations.clear(); cryptonote::tx_destination_entry de; de.addr = alice_account.get_keys().m_account_address; - de.amount = MONEY_SUPPLY - TESTS_DEFAULT_FEE; + de.amount = m_currency.moneySupply() - m_currency.minimumFee(); destinations.push_back(de); destinations.push_back(de); - cryptonote::transaction tx_2; + cryptonote::Transaction tx_2; if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector(), tx_2, 0)) return false; events.push_back(tx_2); diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index 39ef73d32b..368ac4de18 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -22,8 +22,8 @@ struct gen_uint_overflow_base : public test_chain_unit_base { gen_uint_overflow_base(); - bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::transaction& tx); - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& block); + bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index 09698cc982..b30598ff52 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -15,8 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "ring_signature_1.h" using namespace epee; using namespace cryptonote; @@ -74,7 +73,7 @@ bool gen_ring_signature_1::generate(std::vector& events) const DO_CALLBACK(events, "check_balances_1"); // 23 + 2N REWIND_BLOCKS(events, blk_6r, blk_6, miner_account); // // 129 = 11 + 11 + 20 + 29 + 29 + 29 - MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - TESTS_DEFAULT_FEE, 2, blk_6); // 24 + 3N + MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - m_currency.minimumFee(), 2, blk_6); // 24 + 3N MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6r, miner_account, tx_0); // 25 + 3N DO_CALLBACK(events, "check_balances_2"); // 26 + 3N @@ -88,11 +87,11 @@ 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; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + std::list blocks; + bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -106,16 +105,16 @@ 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; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + std::list blocks; + bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); CHECK_EQ(MK_COINS(1), get_balance(m_bob_account, chain, mtx)); - CHECK_EQ(MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - TESTS_DEFAULT_FEE, get_balance(m_alice_account, chain, mtx)); + CHECK_EQ(MK_COINS(129) + 2 * rnd_11 + rnd_20 + 3 * rnd_29 - m_currency.minimumFee(), get_balance(m_alice_account, chain, mtx)); return true; } @@ -155,7 +154,7 @@ bool gen_ring_signature_2::generate(std::vector& events) const MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_blk_4); // 10 + N DO_CALLBACK(events, "check_balances_1"); // 11 + N REWIND_BLOCKS(events, blk_4r, blk_4, miner_account); // - MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(244) - TESTS_DEFAULT_FEE, 3, blk_4); // 12 + 2N + MAKE_TX_MIX(events, tx_0, bob_account, alice_account, MK_COINS(244) - m_currency.minimumFee(), 3, blk_4); // 12 + 2N MAKE_NEXT_BLOCK_TX1(events, blk_5, blk_4r, miner_account, tx_0); // 13 + 2N DO_CALLBACK(events, "check_balances_2"); // 14 + 2N @@ -169,11 +168,11 @@ 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; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + std::list blocks; + bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -187,16 +186,16 @@ 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; - bool r = c.get_blocks(0, 100 + 2 * CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + std::list blocks; + bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); CHECK_EQ(0, get_balance(m_bob_account, chain, mtx)); - CHECK_EQ(MK_COINS(244) - TESTS_DEFAULT_FEE, get_balance(m_alice_account, chain, mtx)); + CHECK_EQ(MK_COINS(244) - m_currency.minimumFee(), get_balance(m_alice_account, chain, mtx)); return true; } @@ -223,8 +222,8 @@ gen_ring_signature_big::gen_ring_signature_big() bool gen_ring_signature_big::generate(std::vector& events) const { std::vector accounts(m_test_size); - std::vector blocks; - blocks.reserve(m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + std::vector blocks; + blocks.reserve(m_test_size + m_currency.minedMoneyUnlockWindow()); uint64_t ts_start = 1338224400; GENERATE_ACCOUNT(miner_account); @@ -243,21 +242,21 @@ bool gen_ring_signature_big::generate(std::vector& events) con blocks.push_back(blk_0); for (size_t i = blk_0r_idx; i < events.size(); ++i) { - blocks.push_back(boost::get(events[i])); + blocks.push_back(boost::get(events[i])); } for (size_t i = 0; i < m_test_size; ++i) { - block blk_with_unlocked_out = blocks[blocks.size() - 1 - CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; + Block blk_with_unlocked_out = blocks[blocks.size() - 1 - m_currency.minedMoneyUnlockWindow()]; MAKE_TX_LIST_START(events, txs_blk_i, miner_account, accounts[i], m_tx_amount, blk_with_unlocked_out); for (size_t j = 0; j <= i; ++j) { - MAKE_TX_LIST(events, txs_blk_i, miner_account, accounts[i], TESTS_DEFAULT_FEE, blk_with_unlocked_out); + MAKE_TX_LIST(events, txs_blk_i, miner_account, accounts[i], m_currency.minimumFee(), blk_with_unlocked_out); } MAKE_NEXT_BLOCK_TX_LIST(events, blk_i, blocks.back(), miner_account, txs_blk_i); blocks.push_back(blk_i); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; bool r = find_block_chain(events, chain, mtx, get_block_hash(blk_i)); CHECK_AND_NO_ASSERT_MES(r, false, "failed to call find_block_chain"); @@ -279,21 +278,21 @@ 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; - bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + std::list blocks; + bool r = c.get_blocks(0, 2 * m_test_size + m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); - CHECK_EQ(m_tx_amount + TESTS_DEFAULT_FEE, get_balance(m_bob_account, chain, mtx)); + CHECK_EQ(m_tx_amount + m_currency.minimumFee(), get_balance(m_bob_account, chain, mtx)); CHECK_EQ(0, get_balance(m_alice_account, chain, mtx)); for (size_t i = 2; i < 1 + m_test_size; ++i) { const account_base& an_account = boost::get(events[i]); - uint64_t balance = m_tx_amount + TESTS_DEFAULT_FEE * i; + uint64_t balance = m_tx_amount + m_currency.minimumFee() * i; CHECK_EQ(balance, get_balance(an_account, chain, mtx)); } @@ -304,11 +303,11 @@ 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; - bool r = c.get_blocks(0, 2 * m_test_size + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, blocks); + std::list blocks; + bool r = c.get_blocks(0, 2 * m_test_size + m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -318,13 +317,13 @@ bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_ind for (size_t i = 2; i < 1 + m_test_size; ++i) { const account_base& an_account = boost::get(events[i]); - uint64_t balance = m_tx_amount + TESTS_DEFAULT_FEE * i; + uint64_t balance = m_tx_amount + m_currency.minimumFee() * i; CHECK_EQ(balance, get_balance(an_account, chain, mtx)); } std::vector tx_outs; uint64_t transfered; - lookup_acc_outs(m_alice_account.get_keys(), boost::get(events[events.size() - 3]), get_tx_pub_key_from_extra(boost::get(events[events.size() - 3])), tx_outs, transfered); + lookup_acc_outs(m_alice_account.get_keys(), boost::get(events[events.size() - 3]), get_tx_pub_key_from_extra(boost::get(events[events.size() - 3])), tx_outs, transfered); CHECK_EQ(m_tx_amount, transfered); return true; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index dbf41fa236..5085aa6f6a 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -19,14 +19,14 @@ #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" #include "misc_language.h" using namespace cryptonote; - - bool test_transaction_generation_and_ring_signature() { + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); account_base miner_acc1; miner_acc1.generate(); @@ -41,25 +41,24 @@ bool test_transaction_generation_and_ring_signature() account_base miner_acc6; miner_acc6.generate(); - std::string add_str = miner_acc3.get_public_address_str(); - + std::string add_str = currency.accountAddressAsString(miner_acc3); account_base rv_acc; rv_acc.generate(); account_base rv_acc2; rv_acc2.generate(); - transaction tx_mine_1; - construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_mine_1); - transaction tx_mine_2; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc2.get_keys().m_account_address, tx_mine_2); - transaction tx_mine_3; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc3.get_keys().m_account_address, tx_mine_3); - transaction tx_mine_4; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc4.get_keys().m_account_address, tx_mine_4); - transaction tx_mine_5; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc5.get_keys().m_account_address, tx_mine_5); - transaction tx_mine_6; - construct_miner_tx(0, 0, 0, 0, 0, miner_acc6.get_keys().m_account_address, tx_mine_6); + Transaction tx_mine_1; + currency.constructMinerTx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_mine_1); + Transaction tx_mine_2; + currency.constructMinerTx(0, 0, 0, 0, 0, miner_acc2.get_keys().m_account_address, tx_mine_2); + Transaction tx_mine_3; + currency.constructMinerTx(0, 0, 0, 0, 0, miner_acc3.get_keys().m_account_address, tx_mine_3); + Transaction tx_mine_4; + currency.constructMinerTx(0, 0, 0, 0, 0, miner_acc4.get_keys().m_account_address, tx_mine_4); + Transaction tx_mine_5; + currency.constructMinerTx(0, 0, 0, 0, 0, miner_acc5.get_keys().m_account_address, tx_mine_5); + Transaction tx_mine_6; + currency.constructMinerTx(0, 0, 0, 0, 0, miner_acc6.get_keys().m_account_address, tx_mine_6); //fill inputs entry typedef tx_source_entry::output_entry tx_output_entry; @@ -70,27 +69,27 @@ bool test_transaction_generation_and_ring_signature() { tx_output_entry oe; oe.first = 0; - oe.second = boost::get(tx_mine_1.vout[0].target).key; + oe.second = boost::get(tx_mine_1.vout[0].target).key; src.outputs.push_back(oe); oe.first = 1; - oe.second = boost::get(tx_mine_2.vout[0].target).key; + oe.second = boost::get(tx_mine_2.vout[0].target).key; src.outputs.push_back(oe); oe.first = 2; - oe.second = boost::get(tx_mine_3.vout[0].target).key; + oe.second = boost::get(tx_mine_3.vout[0].target).key; src.outputs.push_back(oe); oe.first = 3; - oe.second = boost::get(tx_mine_4.vout[0].target).key; + oe.second = boost::get(tx_mine_4.vout[0].target).key; src.outputs.push_back(oe); oe.first = 4; - oe.second = boost::get(tx_mine_5.vout[0].target).key; + oe.second = boost::get(tx_mine_5.vout[0].target).key; src.outputs.push_back(oe); oe.first = 5; - oe.second = boost::get(tx_mine_6.vout[0].target).key; + oe.second = boost::get(tx_mine_6.vout[0].target).key; src.outputs.push_back(oe); src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2); @@ -104,19 +103,20 @@ bool test_transaction_generation_and_ring_signature() std::vector destinations; destinations.push_back(td); - transaction tx_rc1; + Transaction tx_rc1; bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, std::vector(), tx_rc1, 0); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); crypto::hash pref_hash = get_transaction_prefix_hash(tx_rc1); std::vector output_keys; - output_keys.push_back(&boost::get(tx_mine_1.vout[0].target).key); - output_keys.push_back(&boost::get(tx_mine_2.vout[0].target).key); - output_keys.push_back(&boost::get(tx_mine_3.vout[0].target).key); - output_keys.push_back(&boost::get(tx_mine_4.vout[0].target).key); - output_keys.push_back(&boost::get(tx_mine_5.vout[0].target).key); - output_keys.push_back(&boost::get(tx_mine_6.vout[0].target).key); - r = crypto::check_ring_signature(pref_hash, boost::get(tx_rc1.vin[0]).k_image, output_keys, &tx_rc1.signatures[0][0]); + output_keys.push_back(&boost::get(tx_mine_1.vout[0].target).key); + output_keys.push_back(&boost::get(tx_mine_2.vout[0].target).key); + output_keys.push_back(&boost::get(tx_mine_3.vout[0].target).key); + output_keys.push_back(&boost::get(tx_mine_4.vout[0].target).key); + output_keys.push_back(&boost::get(tx_mine_5.vout[0].target).key); + output_keys.push_back(&boost::get(tx_mine_6.vout[0].target).key); + r = crypto::check_ring_signature(pref_hash, boost::get(tx_rc1.vin[0]).keyImage, + output_keys, &tx_rc1.signatures[0][0]); CHECK_AND_ASSERT_MES(r, false, "failed to check ring signature"); std::vector outs; @@ -136,11 +136,13 @@ bool test_block_creation() { uint64_t vszs[] = {80,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,9391,476,476,475,475,474,475,8819,8301,475,472,4302,5316,14347,16620,19583,19403,19728,19442,19852,19015,19000,19016,19795,19749,18087,19787,19704,19750,19267,19006,19050,19445,19407,19522,19546,19788,19369,19486,19329,19370,18853,19600,19110,19320,19746,19474,19474,19743,19494,19755,19715,19769,19620,19368,19839,19532,23424,28287,30707}; std::vector szs(&vszs[0], &vszs[90]); - account_public_address adr; - bool r = get_account_address_from_str(adr, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2"); + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + + AccountPublicAddress adr; + bool r = currency.parseAccountAddressString("272xWzbWsP4cfNFfxY5ETN5moU8x81PKfWPwynrrqsNGDBQGLmD1kCkKCvPeDUXu5XfmZkCrQ53wsWmdfvHBGLNjGcRiDcK", adr); CHECK_AND_ASSERT_MES(r, false, "failed to import"); - block b; - r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, b.miner_tx, blobdata(), 11); + Block b; + r = currency.constructMinerTx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, b.minerTx, blobdata(), 11); return r; } diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index f46c316c70..40303a3f9a 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -17,7 +17,5 @@ #pragma once - - bool test_transactions(); bool test_block_creation(); diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 1dbfa9d812..38cc5c14d8 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -15,8 +15,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "chaingen.h" -#include "chaingen_tests_list.h" +#include "tx_validation.h" +#include "TestGenerator.h" using namespace epee; using namespace crypto; @@ -33,9 +33,9 @@ namespace m_tx.signatures.clear(); m_tx.version = version; - m_tx.unlock_time = unlock_time; + m_tx.unlockTime = unlock_time; - m_tx_key = keypair::generate(); + m_tx_key = KeyPair::generate(); add_tx_pub_key_to_extra(m_tx, m_tx_key.pub); } @@ -43,21 +43,21 @@ namespace { BOOST_FOREACH(const tx_source_entry& src_entr, sources) { - m_in_contexts.push_back(keypair()); - keypair& in_ephemeral = m_in_contexts.back(); + m_in_contexts.push_back(KeyPair()); + KeyPair& in_ephemeral = m_in_contexts.back(); crypto::key_image img; generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img); // put key image into tx input - txin_to_key input_to_key; + TransactionInputToKey input_to_key; input_to_key.amount = src_entr.amount; - input_to_key.k_image = img; + input_to_key.keyImage = img; // fill outputs array and use relative offsets BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs) - input_to_key.key_offsets.push_back(out_entry.first); + input_to_key.keyOffsets.push_back(out_entry.first); - input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); m_tx.vin.push_back(input_to_key); } } @@ -69,12 +69,12 @@ namespace { crypto::key_derivation derivation; crypto::public_key out_eph_public_key; - crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, m_tx_key.sec, derivation); - crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, m_tx_key.sec, derivation); + crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spendPublicKey, out_eph_public_key); - tx_out out; + TransactionOutput out; out.amount = dst_entr.amount; - txout_to_key tk; + TransactionOutputToKey tk; tk.key = out_eph_public_key; out.target = tk; m_tx.vout.push_back(out); @@ -103,24 +103,25 @@ namespace m_tx.signatures.push_back(std::vector()); std::vector& sigs = m_tx.signatures.back(); sigs.resize(src_entr.outputs.size()); - generate_ring_signature(m_tx_prefix_hash, boost::get(m_tx.vin[i]).k_image, keys_ptrs, m_in_contexts[i].sec, src_entr.real_output, sigs.data()); + generate_ring_signature(m_tx_prefix_hash, boost::get(m_tx.vin[i]).keyImage, + keys_ptrs, m_in_contexts[i].sec, src_entr.real_output, sigs.data()); i++; } } - transaction m_tx; - keypair m_tx_key; - std::vector m_in_contexts; + Transaction m_tx; + KeyPair m_tx_key; + std::vector m_in_contexts; crypto::hash m_tx_prefix_hash; }; - transaction make_simple_tx_with_unlock_time(const std::vector& events, - const cryptonote::block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, - uint64_t amount, uint64_t unlock_time) + Transaction make_simple_tx_with_unlock_time(const std::vector& events, + const cryptonote::Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, + uint64_t amount, uint64_t fee, uint64_t unlock_time) { std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, 0, sources, destinations); tx_builder builder; builder.step1_init(CURRENT_TRANSACTION_VERSION, unlock_time); @@ -161,7 +162,7 @@ bool gen_tx_big_version::generate(std::vector& events) const std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(CURRENT_TRANSACTION_VERSION + 1, 0); @@ -185,12 +186,13 @@ bool gen_tx_unlock_time::generate(std::vector& events) const REWIND_BLOCKS_N(events, blk_1, blk_0, miner_account, 10); REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); - auto make_tx_with_unlock_time = [&](uint64_t unlock_time) -> transaction + auto make_tx_with_unlock_time = [&](uint64_t unlock_time) -> Transaction { - return make_simple_tx_with_unlock_time(events, blk_1, miner_account, miner_account, MK_COINS(1), unlock_time); + return make_simple_tx_with_unlock_time(events, blk_1, miner_account, miner_account, MK_COINS(1), + m_currency.minimumFee(), unlock_time); }; - std::list txs_0; + std::list txs_0; txs_0.push_back(make_tx_with_unlock_time(0)); events.push_back(txs_0.back()); @@ -218,42 +220,6 @@ bool gen_tx_unlock_time::generate(std::vector& events) const return true; } -bool gen_tx_input_is_not_txin_to_key::generate(std::vector& events) const -{ - uint64_t ts_start = 1338224400; - - GENERATE_ACCOUNT(miner_account); - MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); - REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); - - MAKE_NEXT_BLOCK(events, blk_tmp, blk_0r, miner_account); - events.pop_back(); - - DO_CALLBACK(events, "mark_invalid_tx"); - events.push_back(blk_tmp.miner_tx); - - auto make_tx_with_input = [&](const txin_v& tx_input) -> transaction - { - std::vector sources; - std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); - - tx_builder builder; - builder.step1_init(); - builder.m_tx.vin.push_back(tx_input); - builder.step3_fill_outputs(destinations); - return builder.m_tx; - }; - - DO_CALLBACK(events, "mark_invalid_tx"); - events.push_back(make_tx_with_input(txin_to_script())); - - DO_CALLBACK(events, "mark_invalid_tx"); - events.push_back(make_tx_with_input(txin_to_scripthash())); - - return true; -} - bool gen_tx_no_inputs_no_outputs::generate(std::vector& events) const { uint64_t ts_start = 1338224400; @@ -279,7 +245,7 @@ bool gen_tx_no_inputs_has_outputs::generate(std::vector& event std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -301,7 +267,7 @@ bool gen_tx_has_inputs_no_outputs::generate(std::vector& event std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); destinations.clear(); tx_builder builder; @@ -327,7 +293,7 @@ bool gen_tx_invalid_input_amount::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); sources.front().amount++; tx_builder builder; @@ -343,7 +309,7 @@ bool gen_tx_invalid_input_amount::generate(std::vector& events return true; } -bool gen_tx_input_wo_key_offsets::generate(std::vector& events) const +bool gen_tx_in_to_key_wo_key_offsets::generate(std::vector& events) const { uint64_t ts_start = 1338224400; @@ -353,20 +319,20 @@ bool gen_tx_input_wo_key_offsets::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); builder.step2_fill_inputs(miner_account.get_keys(), sources); builder.step3_fill_outputs(destinations); - txin_to_key& in_to_key = boost::get(builder.m_tx.vin.front()); - uint64_t key_offset = in_to_key.key_offsets.front(); - in_to_key.key_offsets.pop_back(); - CHECK_AND_ASSERT_MES(in_to_key.key_offsets.empty(), false, "txin contained more than one key_offset"); + TransactionInputToKey& in_to_key = boost::get(builder.m_tx.vin.front()); + uint64_t key_offset = in_to_key.keyOffsets.front(); + in_to_key.keyOffsets.pop_back(); + CHECK_AND_ASSERT_MES(in_to_key.keyOffsets.empty(), false, "txin contained more than one key_offset"); builder.step4_calc_hash(); - in_to_key.key_offsets.push_back(key_offset); + in_to_key.keyOffsets.push_back(key_offset); builder.step5_sign(sources); - in_to_key.key_offsets.pop_back(); + in_to_key.keyOffsets.pop_back(); DO_CALLBACK(events, "mark_invalid_tx"); events.push_back(builder.m_tx); @@ -390,17 +356,17 @@ bool gen_tx_key_offest_points_to_foreign_key::generate(std::vector sources_bob; std::vector destinations_bob; - fill_tx_sources_and_destinations(events, blk_2, bob_account, miner_account, MK_COINS(60) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, 0, sources_bob, destinations_bob); + fill_tx_sources_and_destinations(events, blk_2, bob_account, miner_account, MK_COINS(60) + 1 - m_currency.minimumFee(), m_currency.minimumFee(), 0, sources_bob, destinations_bob); std::vector sources_alice; std::vector destinations_alice; - fill_tx_sources_and_destinations(events, blk_2, alice_account, miner_account, MK_COINS(60) + 1 - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, 0, sources_alice, destinations_alice); + fill_tx_sources_and_destinations(events, blk_2, alice_account, miner_account, MK_COINS(60) + 1 - m_currency.minimumFee(), m_currency.minimumFee(), 0, sources_alice, destinations_alice); tx_builder builder; builder.step1_init(); builder.step2_fill_inputs(bob_account.get_keys(), sources_bob); - txin_to_key& in_to_key = boost::get(builder.m_tx.vin.front()); - in_to_key.key_offsets.front() = sources_alice.front().outputs.front().first; + TransactionInputToKey& in_to_key = boost::get(builder.m_tx.vin.front()); + in_to_key.keyOffsets.front() = sources_alice.front().outputs.front().first; builder.step3_fill_outputs(destinations_bob); builder.step4_calc_hash(); builder.step5_sign(sources_bob); @@ -421,13 +387,13 @@ bool gen_tx_sender_key_offest_not_exist::generate(std::vector& std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); builder.step2_fill_inputs(miner_account.get_keys(), sources); - txin_to_key& in_to_key = boost::get(builder.m_tx.vin.front()); - in_to_key.key_offsets.front() = std::numeric_limits::max(); + TransactionInputToKey& in_to_key = boost::get(builder.m_tx.vin.front()); + in_to_key.keyOffsets.front() = std::numeric_limits::max(); builder.step3_fill_outputs(destinations); builder.step4_calc_hash(); builder.step5_sign(sources); @@ -448,13 +414,13 @@ bool gen_tx_mixed_key_offest_not_exist::generate(std::vector& REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); MAKE_ACCOUNT(events, alice_account); MAKE_ACCOUNT(events, bob_account); - MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); - MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); + MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + m_currency.minimumFee(), blk_1); + MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + m_currency.minimumFee(), blk_1); MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0); std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_2, bob_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 1, sources, destinations); + fill_tx_sources_and_destinations(events, blk_2, bob_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 1, sources, destinations); sources.front().outputs[(sources.front().real_output + 1) % 2].first = std::numeric_limits::max(); @@ -481,17 +447,17 @@ bool gen_tx_key_image_not_derive_from_tx_key::generate(std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); builder.step2_fill_inputs(miner_account.get_keys(), sources); - txin_to_key& in_to_key = boost::get(builder.m_tx.vin.front()); - keypair kp = keypair::generate(); + TransactionInputToKey& in_to_key = boost::get(builder.m_tx.vin.front()); + KeyPair kp = KeyPair::generate(); key_image another_ki; crypto::generate_key_image(kp.pub, kp.sec, another_ki); - in_to_key.k_image = another_ki; + in_to_key.keyImage = another_ki; builder.step3_fill_outputs(destinations); builder.step4_calc_hash(); @@ -517,15 +483,15 @@ bool gen_tx_key_image_is_invalid::generate(std::vector& events std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); builder.step2_fill_inputs(miner_account.get_keys(), sources); - txin_to_key& in_to_key = boost::get(builder.m_tx.vin.front()); + TransactionInputToKey& in_to_key = boost::get(builder.m_tx.vin.front()); crypto::public_key pub = generate_invalid_pub_key(); - memcpy(&in_to_key.k_image, &pub, sizeof(crypto::ec_point)); + memcpy(&in_to_key.keyImage, &pub, sizeof(crypto::ec_point)); builder.step3_fill_outputs(destinations); builder.step4_calc_hash(); @@ -559,11 +525,11 @@ bool gen_tx_check_input_unlock_time::generate(std::vector& eve accounts[i] = acc; } - std::list txs_0; + std::list txs_0; auto make_tx_to_acc = [&](size_t acc_idx, uint64_t unlock_time) { txs_0.push_back(make_simple_tx_with_unlock_time(events, blk_1, miner_account, accounts[acc_idx], - MK_COINS(1) + TESTS_DEFAULT_FEE, unlock_time)); + MK_COINS(1) + m_currency.minimumFee(), m_currency.minimumFee(), unlock_time)); events.push_back(txs_0.back()); }; @@ -576,10 +542,11 @@ bool gen_tx_check_input_unlock_time::generate(std::vector& eve make_tx_to_acc(5, time(0) + 60 * 60); MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0); - std::list txs_1; + std::list txs_1; auto make_tx_from_acc = [&](size_t acc_idx, bool invalid) { - transaction tx = make_simple_tx_with_unlock_time(events, blk_2, accounts[acc_idx], miner_account, MK_COINS(1), 0); + Transaction tx = make_simple_tx_with_unlock_time(events, blk_2, accounts[acc_idx], miner_account, MK_COINS(1), + m_currency.minimumFee(), 0); if (invalid) { DO_CALLBACK(events, "mark_invalid_tx"); @@ -612,14 +579,14 @@ bool gen_tx_txout_to_key_has_invalid_key::generate(std::vector std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); builder.step2_fill_inputs(miner_account.get_keys(), sources); builder.step3_fill_outputs(destinations); - txout_to_key& out_to_key = boost::get(builder.m_tx.vout.front().target); + TransactionOutputToKey& out_to_key = boost::get(builder.m_tx.vout.front().target); out_to_key.key = generate_invalid_pub_key(); builder.step4_calc_hash(); @@ -641,7 +608,7 @@ bool gen_tx_output_with_zero_amount::generate(std::vector& eve std::vector sources; std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); tx_builder builder; builder.step1_init(); @@ -659,48 +626,6 @@ bool gen_tx_output_with_zero_amount::generate(std::vector& eve return true; } -bool gen_tx_output_is_not_txout_to_key::generate(std::vector& events) const -{ - uint64_t ts_start = 1338224400; - - GENERATE_ACCOUNT(miner_account); - MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); - REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); - - std::vector sources; - std::vector destinations; - fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations); - - tx_builder builder; - builder.step1_init(); - builder.step2_fill_inputs(miner_account.get_keys(), sources); - - builder.m_tx.vout.push_back(tx_out()); - builder.m_tx.vout.back().amount = 1; - builder.m_tx.vout.back().target = txout_to_script(); - - builder.step4_calc_hash(); - builder.step5_sign(sources); - - DO_CALLBACK(events, "mark_invalid_tx"); - events.push_back(builder.m_tx); - - builder.step1_init(); - builder.step2_fill_inputs(miner_account.get_keys(), sources); - - builder.m_tx.vout.push_back(tx_out()); - builder.m_tx.vout.back().amount = 1; - builder.m_tx.vout.back().target = txout_to_scripthash(); - - builder.step4_calc_hash(); - builder.step5_sign(sources); - - DO_CALLBACK(events, "mark_invalid_tx"); - events.push_back(builder.m_tx); - - return true; -} - bool gen_tx_signatures_are_invalid::generate(std::vector& events) const { uint64_t ts_start = 1338224400; @@ -711,8 +636,8 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even REWIND_BLOCKS(events, blk_1r, blk_1, miner_account); MAKE_ACCOUNT(events, alice_account); MAKE_ACCOUNT(events, bob_account); - MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); - MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + TESTS_DEFAULT_FEE, blk_1); + MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, MK_COINS(1) + m_currency.minimumFee(), blk_1); + MAKE_TX_LIST(events, txs_0, miner_account, alice_account, MK_COINS(1) + m_currency.minimumFee(), blk_1); MAKE_NEXT_BLOCK_TX_LIST(events, blk_2, blk_1r, miner_account, txs_0); MAKE_TX(events, tx_0, miner_account, miner_account, MK_COINS(60), blk_2); @@ -723,7 +648,7 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even // Tx with nmix = 0 without signatures DO_CALLBACK(events, "mark_invalid_tx"); - blobdata sr_tx = t_serializable_object_to_blob(static_cast(tx_0)); + blobdata sr_tx = t_serializable_object_to_blob(static_cast(tx_0)); events.push_back(serialized_transaction(sr_tx)); // Tx with nmix = 0 have a few inputs, and not enough signatures @@ -740,7 +665,7 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even // Tx with nmix = 1 without signatures DO_CALLBACK(events, "mark_invalid_tx"); - sr_tx = t_serializable_object_to_blob(static_cast(tx_1)); + sr_tx = t_serializable_object_to_blob(static_cast(tx_1)); events.push_back(serialized_transaction(sr_tx)); // Tx with nmix = 1 have not enough signatures @@ -757,3 +682,205 @@ bool gen_tx_signatures_are_invalid::generate(std::vector& even return true; } + +MultiSigTx_OutputSignatures::MultiSigTx_OutputSignatures(size_t givenKeys, uint32_t requiredSignatures, bool shouldSucceed) : + m_givenKeys(givenKeys), m_requiredSignatures(requiredSignatures), m_shouldSucceed(shouldSucceed) { + + for (size_t i = 0; i < m_givenKeys; ++i) { + account_base acc; + acc.generate(); + m_outputAccounts.push_back(acc); + } +} + + +bool MultiSigTx_OutputSignatures::generate(std::vector& events) const { + TestGenerator generator(m_currency, events); + return generate(generator); +} + +bool MultiSigTx_OutputSignatures::generate(TestGenerator& generator) const { + + generator.generateBlocks(m_currency.minedMoneyUnlockWindow()); + + std::vector sources; + std::vector destinations; + fill_tx_sources_and_destinations(generator.events, generator.lastBlock, generator.minerAccount, generator.minerAccount, + MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); + + tx_builder builder; + builder.step1_init(); + builder.step2_fill_inputs(generator.minerAccount.get_keys(), sources); + + TransactionOutputMultisignature target; + + for (const auto& acc : m_outputAccounts) { + target.keys.push_back(acc.get_keys().m_account_address.m_spendPublicKey); + } + target.requiredSignatures = m_requiredSignatures; + TransactionOutput txOut = { MK_COINS(1), target }; + builder.m_tx.vout.push_back(txOut); + + builder.step4_calc_hash(); + builder.step5_sign(sources); + + if (!m_shouldSucceed) { + generator.addCallback("mark_invalid_tx"); + } + + generator.addEvent(builder.m_tx); + + if (!m_shouldSucceed) { + generator.addCallback("mark_invalid_block"); + } + + generator.makeNextBlock(builder.m_tx); + + return true; + +} + +bool MultiSigTx_InvalidOutputSignature::generate(std::vector& events) const { + uint64_t ts_start = 1338224400; + + GENERATE_ACCOUNT(miner_account); + MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); + REWIND_BLOCKS(events, blk_0r, blk_0, miner_account); + + std::vector sources; + std::vector destinations; + fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), m_currency.minimumFee(), 0, sources, destinations); + + tx_builder builder; + builder.step1_init(); + builder.step2_fill_inputs(miner_account.get_keys(), sources); + + TransactionOutputMultisignature target; + + crypto::public_key pk; + crypto::secret_key sk; + crypto::generate_keys(pk, sk); + + // fill with 1 valid key + target.keys.push_back(pk); + // and 1 invalid + target.keys.push_back(generate_invalid_pub_key()); + + target.requiredSignatures = 2; + + TransactionOutput txOut = { MK_COINS(1), target }; + builder.m_tx.vout.push_back(txOut); + + builder.step4_calc_hash(); + builder.step5_sign(sources); + + DO_CALLBACK(events, "mark_invalid_tx"); + events.push_back(builder.m_tx); + + return true; +} + +namespace +{ + void fillMultisignatureInput(TestGenerator& generator, tx_builder& builder, uint64_t inputAmount, uint32_t givenSignatures) { + + builder.step1_init(); + + // create input + TransactionInputMultisignature input; + input.amount = inputAmount; + input.signatures = givenSignatures; + input.outputIndex = 0; + builder.m_tx.vin.push_back(input); + + // create output + std::vector destinations; + destinations.emplace_back(inputAmount - generator.currency().minimumFee(), generator.minerAccount.get_keys().m_account_address); + builder.step3_fill_outputs(destinations); + + // calc hash + builder.step4_calc_hash(); + + } +} + + +MultiSigTx_Input::MultiSigTx_Input( + size_t givenKeys, uint32_t requiredSignatures, uint32_t givenSignatures, bool inputShouldSucceed) : + MultiSigTx_OutputSignatures(givenKeys, requiredSignatures, true), + m_givenSignatures(givenSignatures), + m_inputShouldSucceed(inputShouldSucceed) {} + +bool MultiSigTx_Input::generate(std::vector& events) const { + + TestGenerator generator(m_currency, events); + + // create outputs + MultiSigTx_OutputSignatures::generate(generator); + + tx_builder builder; + fillMultisignatureInput(generator, builder, MK_COINS(1), m_givenSignatures); + + // calc signatures + builder.m_tx.signatures.resize(builder.m_tx.signatures.size() + 1); + auto& outsigs = builder.m_tx.signatures.back(); + + for (size_t i = 0; i < m_givenSignatures; ++i) { + const auto& pk = m_outputAccounts[i].get_keys().m_account_address.m_spendPublicKey; + const auto& sk = m_outputAccounts[i].get_keys().m_spend_secret_key; + + crypto::signature sig; + crypto::generate_signature(builder.m_tx_prefix_hash, pk, sk, sig); + outsigs.push_back(sig); + } + + if (!m_inputShouldSucceed) { + generator.addCallback("mark_invalid_tx"); + } + + generator.addEvent(builder.m_tx); + return true; +} + + +MultiSigTx_BadInputSignature::MultiSigTx_BadInputSignature() : + MultiSigTx_OutputSignatures(1, 1, true) { +} + + +bool MultiSigTx_BadInputSignature::generate(std::vector& events) const { + + TestGenerator generator(m_currency, events); + + // create outputs + MultiSigTx_OutputSignatures::generate(generator); + + tx_builder builder; + fillMultisignatureInput(generator, builder, MK_COINS(1), 1); + + // calc signatures + builder.m_tx.signatures.resize(builder.m_tx.signatures.size() + 1); + auto& outsigs = builder.m_tx.signatures.back(); + + const auto& pk = m_outputAccounts[0].get_keys().m_account_address.m_spendPublicKey; + const auto& sk = m_outputAccounts[0].get_keys().m_spend_secret_key; + + // modify the transaction prefix hash + crypto::hash badHash = builder.m_tx_prefix_hash; + *reinterpret_cast(&badHash) = 0xdead; + + // sign the hash + crypto::signature sig; + crypto::generate_signature(badHash, pk, sk, sig); + outsigs.push_back(sig); + + // transaction with bad signature should be rejected + generator.addCallback("mark_invalid_tx"); + generator.addEvent(builder.m_tx); + + // blocks with transaction with bad signature should be rejected + generator.addCallback("mark_invalid_block"); + generator.makeNextBlock(builder.m_tx); + + return true; +} diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 4ddd7c4db1..50b29aba28 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -28,7 +28,7 @@ struct get_tx_validation_base : public test_chain_unit_base REGISTER_CALLBACK_METHOD(get_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*/) + 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; @@ -36,7 +36,7 @@ struct get_tx_validation_base : public test_chain_unit_base return !tvc.m_verifivation_failed && tx_added; } - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::block& /*block*/) + 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; @@ -71,11 +71,6 @@ struct gen_tx_unlock_time : public get_tx_validation_base bool generate(std::vector& events) const; }; -struct gen_tx_input_is_not_txin_to_key : public get_tx_validation_base -{ - bool generate(std::vector& events) const; -}; - struct gen_tx_no_inputs_no_outputs : public get_tx_validation_base { bool generate(std::vector& events) const; @@ -96,7 +91,7 @@ struct gen_tx_invalid_input_amount : public get_tx_validation_base bool generate(std::vector& events) const; }; -struct gen_tx_input_wo_key_offsets : public get_tx_validation_base +struct gen_tx_in_to_key_wo_key_offsets : public get_tx_validation_base { bool generate(std::vector& events) const; }; @@ -141,12 +136,42 @@ struct gen_tx_output_with_zero_amount : public get_tx_validation_base bool generate(std::vector& events) const; }; -struct gen_tx_output_is_not_txout_to_key : public get_tx_validation_base +struct gen_tx_signatures_are_invalid : public get_tx_validation_base { bool generate(std::vector& events) const; }; -struct gen_tx_signatures_are_invalid : public get_tx_validation_base -{ +// MultiSignature + +class TestGenerator; + +struct MultiSigTx_OutputSignatures : public get_tx_validation_base { + MultiSigTx_OutputSignatures(size_t givenKeys, uint32_t requiredSignatures, bool shouldSucceed); + + bool generate(std::vector& events) const; + bool generate(TestGenerator& generator) const; + + const size_t m_givenKeys; + const uint32_t m_requiredSignatures; + const bool m_shouldSucceed; + std::vector m_outputAccounts; +}; + +struct MultiSigTx_InvalidOutputSignature : public get_tx_validation_base { + bool generate(std::vector& events) const; +}; + + +struct MultiSigTx_Input : public MultiSigTx_OutputSignatures { + MultiSigTx_Input(size_t givenKeys, uint32_t requiredSignatures, uint32_t givenSignatures, bool shouldSucceed); + bool generate(std::vector& events) const; + + const bool m_inputShouldSucceed; + const uint32_t m_givenSignatures; +}; + + +struct MultiSigTx_BadInputSignature : public MultiSigTx_OutputSignatures { + MultiSigTx_BadInputSignature(); bool generate(std::vector& events) const; }; diff --git a/tests/core_tests/upgrade.cpp b/tests/core_tests/upgrade.cpp new file mode 100644 index 0000000000..3ccb33d1e1 --- /dev/null +++ b/tests/core_tests/upgrade.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "upgrade.h" + +using namespace epee; +using namespace cryptonote; + +namespace { + bool makeBlocks(std::vector& events, test_generator& generator, Block& lastBlock, + const Block& parentBlock, const cryptonote::account_base& minerAcc, size_t count, + uint8_t majorVersion, uint8_t minorVersion) { + cryptonote::Block prevBlock = parentBlock; + for (size_t i = 0; i < count; ++i) { + cryptonote::Block b; + bool r = generator.constructBlockManually(b, prevBlock, minerAcc, test_generator::bf_major_ver | test_generator::bf_minor_ver, + majorVersion, minorVersion); + if (!r) { + return false; + } else { + prevBlock = b; + events.push_back(b); + } + } + + lastBlock = prevBlock; + + return true; + } +} + +gen_upgrade::gen_upgrade() : m_invalidBlockIndex(0), m_checkBlockTemplateVersionCallCounter(0), + m_coinsInCirculationBeforeUpgrade(0), m_coinsInCirculationAfterUpgrade(0) { + REGISTER_CALLBACK_METHOD(gen_upgrade, markInvalidBlock); + REGISTER_CALLBACK_METHOD(gen_upgrade, checkBlockTemplateVersionIsV1); + REGISTER_CALLBACK_METHOD(gen_upgrade, checkBlockTemplateVersionIsV2); + REGISTER_CALLBACK_METHOD(gen_upgrade, checkBlockRewardEqFee); + REGISTER_CALLBACK_METHOD(gen_upgrade, checkBlockRewardIsZero); + REGISTER_CALLBACK_METHOD(gen_upgrade, rememberCoinsInCirculationBeforeUpgrade); + REGISTER_CALLBACK_METHOD(gen_upgrade, rememberCoinsInCirculationAfterUpgrade); +} + +bool gen_upgrade::generate(std::vector& events) const { + const uint64_t tsStart = 1338224400; + + GENERATE_ACCOUNT(minerAccount); + MAKE_GENESIS_BLOCK(events, blk0, minerAccount, tsStart); + + // Vote for upgrade + Block blk1; + if (!makeBlocks(events, generator, blk1, blk0, minerAccount, m_currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1)) { + return false; + } + + if (!checkBeforeUpgrade(events, generator, blk1, minerAccount, true)) { + return false; + } + + // Fill m_currency.upgradeVotingWindow() + Block blk2; + if (!makeBlocks(events, generator, blk2, blk1, minerAccount, m_currency.upgradeVotingWindow() - m_currency.minNumberVotingBlocks() - 1, + BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0)) { + return false; + } + + // Upgrade voting complete! + uint64_t votingCompleteHeight = get_block_height(blk2); + uint64_t upgradeHeight = m_currency.calculateUpgradeHeight(votingCompleteHeight); + + if (!checkBeforeUpgrade(events, generator, blk2, minerAccount, true)) { + return false; + } + + // Create blocks up to upgradeHeight + Block blk3; + if (!makeBlocks(events, generator, blk3, blk2, minerAccount, upgradeHeight - votingCompleteHeight - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0)) { + return false; + } + + if (!checkBeforeUpgrade(events, generator, blk3, minerAccount, false)) { + return false; + } + + // Create last block with version 1.x + Block blk4; + if (!makeBlocks(events, generator, blk4, blk3, minerAccount, 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0)) { + return false; + } + + generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.defaultMinorVersion = BLOCK_MINOR_VERSION_0; + + if (!checkAfterUpgrade(events, generator, blk4, minerAccount)) { + return false; + } + + // Create a few blocks with version 2.0 + Block blk5; + if (!makeBlocks(events, generator, blk5, blk4, minerAccount, 3, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0)) { + return false; + } + + if (!checkAfterUpgrade(events, generator, blk5, minerAccount)) { + return false; + } + + return true; +} + +bool gen_upgrade::checkBeforeUpgrade(std::vector& events, test_generator& generator, + const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc, + bool checkReward) const { + // Checking 1: get_block_templare returns block with major version 1 + DO_CALLBACK(events, "checkBlockTemplateVersionIsV1"); + + // Checking 2: penalty doesn't apply to transactions fee + if (checkReward) { + // Add block to the blockchain, later it become an alternative + DO_CALLBACK(events, "rememberCoinsInCirculationBeforeUpgrade"); + MAKE_TX_LIST_START(events, txs, minerAcc, minerAcc, MK_COINS(1), parentBlock); + Block alternativeBlk; + if (!generator.constructMaxSizeBlock(alternativeBlk, parentBlock, minerAcc, m_currency.rewardBlocksWindow(), txs)) { + return false; + } + events.push_back(alternativeBlk); + DO_CALLBACK(events, "checkBlockRewardEqFee"); + } + + // Checking 3: block with version 2.0 doesn't accepted + Block badBlock; + DO_CALLBACK(events, "markInvalidBlock"); + return makeBlocks(events, generator, badBlock, parentBlock, minerAcc, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); +} + +bool gen_upgrade::checkAfterUpgrade(std::vector& events, test_generator& generator, + const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc) const { + // Checking 1: get_block_templare returns block with major version 2 + DO_CALLBACK(events, "checkBlockTemplateVersionIsV2"); + + // Checking 2: penalty applies to transactions fee + // Add block to the blockchain, later it become an alternative + DO_CALLBACK(events, "rememberCoinsInCirculationAfterUpgrade"); + MAKE_TX_LIST_START(events, txs, minerAcc, minerAcc, MK_COINS(1), parentBlock); + Block alternativeBlk; + if (!generator.constructMaxSizeBlock(alternativeBlk, parentBlock, minerAcc, m_currency.rewardBlocksWindow(), txs)) { + return false; + } + events.push_back(alternativeBlk); + DO_CALLBACK(events, "checkBlockRewardIsZero"); + + // Checking 3: block with version 1.0 doesn't accepted + Block badBlock; + DO_CALLBACK(events, "markInvalidBlock"); + if (!makeBlocks(events, generator, badBlock, parentBlock, minerAcc, 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0)) { + return false; + } + + // Checking 2: block with version 1.1 doesn't accepted + DO_CALLBACK(events, "markInvalidBlock"); + return makeBlocks(events, generator, badBlock, parentBlock, minerAcc, 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); +} + +bool gen_upgrade::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t eventIdx, const cryptonote::Block& /*blk*/) { + if (m_invalidBlockIndex == eventIdx) { + m_invalidBlockIndex = 0; + return bvc.m_verifivation_failed; + } else { + return !bvc.m_verifivation_failed; + } +} + +bool gen_upgrade::markInvalidBlock(cryptonote::core& /*c*/, size_t evIndex, const std::vector& /*events*/) { + m_invalidBlockIndex = evIndex + 1; + return true; +} + +bool gen_upgrade::checkBlockTemplateVersionIsV1(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { + DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockTemplateVersionIsV1"); + CHECK_TEST_CONDITION(checkBlockTemplateVersion(c, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1)); + return true; +} + +bool gen_upgrade::checkBlockTemplateVersionIsV2(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { + DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockTemplateVersionIsV2"); + CHECK_TEST_CONDITION(checkBlockTemplateVersion(c, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0)); + return true; +} + +bool gen_upgrade::checkBlockTemplateVersion(cryptonote::core& c, uint8_t expectedMajorVersion, uint8_t expectedMinorVersion) { + DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockTemplateVersion"); + + account_base account; + account.generate(); + + Block b; + difficulty_type diff; + uint64_t height; + CHECK_TEST_CONDITION(c.get_block_template(b, account.get_keys().m_account_address, diff, height, blobdata())); + CHECK_EQ(b.majorVersion, expectedMajorVersion); + CHECK_EQ(b.minorVersion, expectedMinorVersion); + + return true; +} + +bool gen_upgrade::checkBlockRewardEqFee(cryptonote::core& c, size_t evIndex, const std::vector& events) { + DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockRewardEqFee"); + + Block blk = boost::get(events[evIndex - 1]); + uint64_t blockReward = get_outs_money_amount(blk.minerTx); + CHECK_EQ(blockReward, m_currency.minimumFee()); + + CHECK_EQ(m_coinsInCirculationBeforeUpgrade, c.get_blockchain_storage().getCoinsInCirculation()); + + return true; +} + +bool gen_upgrade::checkBlockRewardIsZero(cryptonote::core& c, size_t evIndex, const std::vector& events) { + DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockRewardIsZero"); + + Block blk = boost::get(events[evIndex - 1]); + uint64_t blockReward = get_outs_money_amount(blk.minerTx); + CHECK_EQ(blockReward, 0); + + CHECK_EQ(m_coinsInCirculationAfterUpgrade - m_currency.minimumFee(), c.get_blockchain_storage().getCoinsInCirculation()); + + return true; +} + +bool gen_upgrade::rememberCoinsInCirculationBeforeUpgrade(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { + m_coinsInCirculationBeforeUpgrade = c.get_blockchain_storage().getCoinsInCirculation(); + return true; +} + +bool gen_upgrade::rememberCoinsInCirculationAfterUpgrade(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { + m_coinsInCirculationAfterUpgrade = c.get_blockchain_storage().getCoinsInCirculation(); + return true; +} diff --git a/tests/core_tests/upgrade.h b/tests/core_tests/upgrade.h new file mode 100644 index 0000000000..1d5f5af27e --- /dev/null +++ b/tests/core_tests/upgrade.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once +#include "chaingen.h" + +struct gen_upgrade : public test_chain_unit_base +{ + gen_upgrade(); + + bool generate(std::vector& events) const; + + bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t eventIdx, const cryptonote::Block& blk); + + bool markInvalidBlock(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockTemplateVersionIsV1(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockTemplateVersionIsV2(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockRewardEqFee(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockRewardIsZero(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool rememberCoinsInCirculationBeforeUpgrade(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool rememberCoinsInCirculationAfterUpgrade(cryptonote::core& c, size_t evIndex, const std::vector& events); + +private: + bool checkBeforeUpgrade(std::vector& events, test_generator& generator, + const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc, bool checkReward) const; + bool checkAfterUpgrade(std::vector& events, test_generator& generator, + const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc) const; + bool checkBlockTemplateVersion(cryptonote::core& c, uint8_t expectedMajorVersion, uint8_t expectedMinorVersion); + +private: + size_t m_invalidBlockIndex; + size_t m_checkBlockTemplateVersionCallCounter; + uint64_t m_coinsInCirculationBeforeUpgrade; + uint64_t m_coinsInCirculationAfterUpgrade; +}; diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index 1de712b1f4..dba4fc85d5 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -24,16 +24,21 @@ #include "cryptonote_config.h" #include "cryptonote_core/difficulty.h" +#include "cryptonote_core/Currency.h" using namespace std; -#define DEFAULT_TEST_DIFFICULTY_TARGET 120 - int main(int argc, char *argv[]) { if (argc != 2) { cerr << "Wrong arguments" << endl; return 1; } + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.difficultyTarget(120); + currencyBuilder.difficultyWindow(720); + currencyBuilder.difficultyCut(60); + currencyBuilder.difficultyLag(15); + cryptonote::Currency currency = currencyBuilder.currency(); vector timestamps, cumulative_difficulties; fstream data(argv[1], fstream::in); data.exceptions(fstream::badbit); @@ -42,16 +47,16 @@ int main(int argc, char *argv[]) { size_t n = 0; while (data >> timestamp >> difficulty) { size_t begin, end; - if (n < DIFFICULTY_WINDOW + DIFFICULTY_LAG) { + if (n < currency.difficultyWindow() + currency.difficultyLag()) { begin = 0; - end = min(n, (size_t) DIFFICULTY_WINDOW); + end = min(n, currency.difficultyWindow()); } else { - end = n - DIFFICULTY_LAG; - begin = end - DIFFICULTY_WINDOW; + end = n - currency.difficultyLag(); + begin = end - currency.difficultyWindow(); } - uint64_t res = cryptonote::next_difficulty( + uint64_t res = currency.nextDifficulty( vector(timestamps.begin() + begin, timestamps.begin() + end), - vector(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET); + vector(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end)); if (res != difficulty) { cerr << "Wrong difficulty for block " << n << endl << "Expected: " << difficulty << endl diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp index 5708d8905d..87eac61b61 100644 --- a/tests/functional_tests/main.cpp +++ b/tests/functional_tests/main.cpp @@ -18,6 +18,7 @@ #include #include "include_base_utils.h" +#include "string_tools.h" using namespace epee; #include "common/command_line.h" diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index f46df5681a..b97c51d666 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -16,11 +16,14 @@ // along with Bytecoin. If not, see . #include +#include #include #include #include "include_base_utils.h" using namespace epee; + +#include "cryptonote_core/Currency.h" #include "wallet/wallet2.h" using namespace cryptonote; @@ -38,7 +41,7 @@ inline uint64_t random(const uint64_t max_value) { (uint64_t(rand())<<48)) % max_value; } -bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, uint64_t amount_to_transfer, transaction& tx, size_t parts=1) +bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, uint64_t amount_to_transfer, Transaction& tx, size_t parts=1) { CHECK_AND_ASSERT_MES(parts > 0, false, "parts must be > 0"); @@ -65,7 +68,8 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, try { - w1.transfer(dsts, mix_in_factor, 0, MINIMUM_FEE, std::vector(), tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_DUST_THRESHOLD), tx); + w1.transfer(dsts, mix_in_factor, 0, w1.currency().minimumFee(), std::vector(), + tools::detail::null_split_strategy, tools::tx_dust_policy(w1.currency().defaultDustThreshold()), tx); return true; } catch (const std::exception&) @@ -97,7 +101,8 @@ bool transactions_flow_test(std::string& working_folder, uint64_t amount_to_transfer, size_t mix_in_factor, size_t transactions_count, size_t transactions_per_second) { LOG_PRINT_L0("-----------------------STARTING TRANSACTIONS FLOW TEST-----------------------"); - tools::wallet2 w1, w2; + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + tools::wallet2 w1(currency), w2(currency); if(path_source_wallet.empty()) path_source_wallet = generate_random_wallet_name(); @@ -130,8 +135,8 @@ bool transactions_flow_test(std::string& working_folder, w2.init(daemon_addr_b); LOG_PRINT_GREEN("Using wallets: " << ENDL - << "Source: " << w1.get_account().get_public_address_str() << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL - << "Target: " << w2.get_account().get_public_address_str() << ENDL << "Path: " << working_folder + "/" + path_terget_wallet, LOG_LEVEL_1); + << "Source: " << currency.accountAddressAsString(w1.get_account()) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL + << "Target: " << currency.accountAddressAsString(w2.get_account()) << ENDL << "Path: " << working_folder + "/" + path_terget_wallet, LOG_LEVEL_1); //lets do some money epee::net_utils::http::http_simple_client http_client; @@ -142,7 +147,7 @@ bool transactions_flow_test(std::string& working_folder, COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); COMMAND_RPC_START_MINING::response daemon_rsp = AUTO_VAL_INIT(daemon_rsp); - daemon_req.miner_address = w1.get_account().get_public_address_str(); + daemon_req.miner_address = currency.accountAddressAsString(w1.get_account()); daemon_req.threads_count = 9; r = net_utils::invoke_http_json_remote_command2(daemon_addr_a + "/start_mining", daemon_req, daemon_rsp, http_client, 10000); CHECK_AND_ASSERT_MES(r, false, "failed to get getrandom_outs"); @@ -169,8 +174,8 @@ bool transactions_flow_test(std::string& working_folder, size_t count = 0; BOOST_FOREACH(tools::wallet2::transfer_details& td, incoming_transfers) { - cryptonote::transaction tx_s; - bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - MINIMUM_FEE, tx_s, 50); + cryptonote::Transaction tx_s; + bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - currency.minimumFee(), tx_s, 50); CHECK_AND_ASSERT_MES(r, false, "Failed to send starter tx " << get_transaction_hash(tx_s)); LOG_PRINT_GREEN("Starter transaction sent " << get_transaction_hash(tx_s), LOG_LEVEL_0); if(++count >= FIRST_N_TRANSFERS) @@ -189,7 +194,7 @@ bool transactions_flow_test(std::string& working_folder, size_t i = 0; struct tx_test_entry { - transaction tx; + Transaction tx; size_t m_received_count; uint64_t amount_transfered; }; @@ -198,14 +203,14 @@ bool transactions_flow_test(std::string& working_folder, for(i = 0; i != transactions_count; i++) { uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money); - while(w1.unlocked_balance() < amount_to_tx + MINIMUM_FEE) + while(w1.unlocked_balance() < amount_to_tx + currency.minimumFee()) { misc_utils::sleep_no_w(1000); LOG_PRINT_L0("not enough money, waiting for cashback or mining"); w1.refresh(blocks_fetched, received_money, ok); } - transaction tx; + Transaction tx; /*size_t n_attempts = 0; while (!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx)) { n_attempts++; @@ -225,7 +230,7 @@ bool transactions_flow_test(std::string& working_folder, return false; } } - lst_sent_ki = boost::get(tx.vin[0]).k_image; + lst_sent_ki = boost::get(tx.vin[0]).keyImage; transfered_money += amount_to_tx; @@ -239,19 +244,21 @@ bool transactions_flow_test(std::string& working_folder, LOG_PRINT_L0( "waiting some new blocks..."); - misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*20*1000);//wait two blocks before sync on another wallet on another daemon + //wait two blocks before sync on another wallet on another daemon + misc_utils::sleep_no_w(static_cast(currency.difficultyTarget() * 20 * 1000)); LOG_PRINT_L0( "refreshing..."); bool recvd_money = false; while(w2.refresh(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 + //wait two blocks before sync on another wallet on another daemon + misc_utils::sleep_no_w(static_cast(currency.difficultyTarget() * 1000)); } uint64_t money_2 = w2.balance(); if(money_2 == transfered_money) { LOG_PRINT_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------", LOG_LEVEL_0); - LOG_PRINT_GREEN("transferred " << print_money(transfered_money) << " via " << i << " transactions" , LOG_LEVEL_0); + LOG_PRINT_GREEN("transferred " << currency.formatAmount(transfered_money) << " via " << i << " transactions" , LOG_LEVEL_0); return true; }else { @@ -274,7 +281,8 @@ bool transactions_flow_test(std::string& working_folder, } LOG_PRINT_RED_L0("-----------------------FINISHING TRANSACTIONS FLOW TEST FAILED-----------------------" ); - LOG_PRINT_RED_L0("income " << print_money(money_2) << " via " << i << " transactions, expected money = " << print_money(transfered_money) ); + LOG_PRINT_RED_L0("income " << currency.formatAmount(money_2) << " via " << i << + " transactions, expected money = " << currency.formatAmount(transfered_money) ); LOCAL_ASSERT(false); return false; } diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp index d9ca1ed032..14f659f635 100644 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ b/tests/functional_tests/transactions_generation_from_blockchain.cpp @@ -32,7 +32,7 @@ bool transactions_generation_from_blockchain(std::string& blockchain_folder_path CHECK_AND_ASSERT_MES(r, false, "failed to load blockchain"); //amount = 3000000000000 - //key_offsets = 1,2,3,4,5,10,12,27,31,33,34 + //keyOffsets = 1,2,3,4,5,10,12,27,31,33,34 // } @@ -40,7 +40,7 @@ tx_source_entry::output_entry make_outptu_entr_for_gindex(size_t i, std::map(vout[v[i].second].target).key; + oe.second = txs[v[i].first].boost::get(vout[v[i].second].target).key; return oe; } @@ -96,7 +96,7 @@ bool make_tx(blockchain_storage& bch) //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 = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; + real_oe.second = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; auto interted_it = src.outputs.insert(it_to_insert, real_oe); src.real_out_tx_key = td.m_tx.tx_pub_key; src.real_output = interted_it - src.outputs.begin(); diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp index 9c503e1888..c7a76da098 100644 --- a/tests/net_load_tests/clt.cpp +++ b/tests/net_load_tests/clt.cpp @@ -22,6 +22,8 @@ #include #include +#include + #include "gtest/gtest.h" #include "include_base_utils.h" diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h index d7c566081f..dcb0091956 100644 --- a/tests/net_load_tests/net_load_tests.h +++ b/tests/net_load_tests/net_load_tests.h @@ -20,6 +20,7 @@ #include #include +#include #include "include_base_utils.h" #include "string_tools.h" diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp index 9c884d2c68..8fb1364010 100644 --- a/tests/net_load_tests/srv.cpp +++ b/tests/net_load_tests/srv.cpp @@ -15,9 +15,12 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include #include #include +#include + #include "include_base_utils.h" #include "misc_log_ex.h" #include "storages/levin_abstract_invoke2.h" diff --git a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp index 11f84cb038..3ff889c3bc 100644 --- a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp +++ b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp @@ -119,7 +119,7 @@ int main(int argc, const char** argv) { LOG_PRINT_RED_L0("shutdown error"); } - cryptonote::transaction tx; + cryptonote::Transaction tx; nodeProxy.relayTransaction(tx, [](std::error_code ec) { if (!ec) { LOG_PRINT_L0("relayTransaction called successfully"); diff --git a/tests/performance_tests/check_ring_signature.h b/tests/performance_tests/check_ring_signature.h index 9cfedf2d16..b50726478b 100644 --- a/tests/performance_tests/check_ring_signature.h +++ b/tests/performance_tests/check_ring_signature.h @@ -59,12 +59,12 @@ class test_check_ring_signature : private multi_tx_test_base bool test() { - const cryptonote::txin_to_key& txin = boost::get(m_tx.vin[0]); - return crypto::check_ring_signature(m_tx_prefix_hash, txin.k_image, this->m_public_key_ptrs, ring_size, m_tx.signatures[0].data()); + const cryptonote::TransactionInputToKey& txin = boost::get(m_tx.vin[0]); + return crypto::check_ring_signature(m_tx_prefix_hash, txin.keyImage, this->m_public_key_ptrs, ring_size, m_tx.signatures[0].data()); } private: cryptonote::account_base m_alice; - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; crypto::hash m_tx_prefix_hash; }; diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index ec91eeb064..ac4f146382 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -61,5 +61,5 @@ class test_construct_tx : private multi_tx_test_base private: cryptonote::account_base m_alice; std::vector m_destinations; - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; }; diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index 937bc6475c..d456d4fad8 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -33,14 +33,14 @@ class test_derive_public_key : public single_tx_test_base return false; crypto::generate_key_derivation(m_tx_pub_key, m_bob.get_keys().m_view_secret_key, m_key_derivation); - m_spend_public_key = m_bob.get_keys().m_account_address.m_spend_public_key; + m_spend_public_key = m_bob.get_keys().m_account_address.m_spendPublicKey; return true; } bool test() { - cryptonote::keypair in_ephemeral; + cryptonote::KeyPair in_ephemeral; crypto::derive_public_key(m_key_derivation, 0, m_spend_public_key, in_ephemeral.pub); return true; } diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index 51226f09a1..cde62a0a2e 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -40,7 +40,7 @@ class test_derive_secret_key : public single_tx_test_base bool test() { - cryptonote::keypair in_ephemeral; + cryptonote::KeyPair in_ephemeral; crypto::derive_secret_key(m_key_derivation, 0, m_spend_secret_key, in_ephemeral.sec); return true; } diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index 32a8709e1a..29106b4b39 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -39,7 +39,7 @@ class test_generate_key_image : public single_tx_test_base crypto::key_derivation recv_derivation; crypto::generate_key_derivation(m_tx_pub_key, bob_keys.m_view_secret_key, recv_derivation); - crypto::derive_public_key(recv_derivation, 0, bob_keys.m_account_address.m_spend_public_key, m_in_ephemeral.pub); + crypto::derive_public_key(recv_derivation, 0, bob_keys.m_account_address.m_spendPublicKey, m_in_ephemeral.pub); crypto::derive_secret_key(recv_derivation, 0, bob_keys.m_spend_secret_key, m_in_ephemeral.sec); return true; @@ -53,5 +53,5 @@ class test_generate_key_image : public single_tx_test_base } private: - cryptonote::keypair m_in_ephemeral; + cryptonote::KeyPair m_in_ephemeral; }; diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index c2ba87bdc3..121f20b0cb 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -17,7 +17,6 @@ #pragma once -#include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_format_utils.h" @@ -30,7 +29,7 @@ class test_generate_key_image_helper : public single_tx_test_base bool test() { - cryptonote::keypair in_ephemeral; + cryptonote::KeyPair in_ephemeral; crypto::key_image ki; return cryptonote::generate_key_image_helper(m_bob.get_keys(), m_tx_pub_key, 0, in_ephemeral, ki); } diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index f2031e11b9..8ea73941fc 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -17,7 +17,6 @@ #pragma once -#include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_format_utils.h" @@ -30,7 +29,7 @@ class test_is_out_to_acc : public single_tx_test_base bool test() { - const cryptonote::txout_to_key& tx_out = boost::get(m_tx.vout[0].target); + const cryptonote::TransactionOutputToKey& tx_out = boost::get(m_tx.vout[0].target); return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, 0); } }; diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index f6d81da903..ef3e912f99 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -22,6 +22,7 @@ #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" #include "crypto/crypto.h" template @@ -37,15 +38,17 @@ class multi_tx_test_base { using namespace cryptonote; + Currency currency = CurrencyBuilder().currency(); + std::vector output_entries; for (size_t i = 0; i < ring_size; ++i) { m_miners[i].generate(); - if (!construct_miner_tx(0, 0, 0, 2, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i])) + if (!currency.constructMinerTx(0, 0, 0, 2, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i])) return false; - txout_to_key tx_out = boost::get(m_miner_txs[i].vout[0].target); + TransactionOutputToKey tx_out = boost::get(m_miner_txs[i].vout[0].target); output_entries.push_back(std::make_pair(i, tx_out.key)); m_public_keys[i] = tx_out.key; m_public_key_ptrs[i] = &m_public_keys[i]; @@ -67,7 +70,7 @@ class multi_tx_test_base protected: cryptonote::account_base m_miners[ring_size]; - cryptonote::transaction m_miner_txs[ring_size]; + cryptonote::Transaction m_miner_txs[ring_size]; uint64_t m_source_amount; std::vector m_sources; diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index bf499a01e1..7a670e0a50 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -28,9 +28,10 @@ class single_tx_test_base { using namespace cryptonote; + Currency currency = CurrencyBuilder().currency(); m_bob.generate(); - if (!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().m_account_address, m_tx)) + if (!currency.constructMinerTx(0, 0, 0, 2, 0, m_bob.get_keys().m_account_address, m_tx)) return false; m_tx_pub_key = get_tx_pub_key_from_extra(m_tx); @@ -39,6 +40,6 @@ class single_tx_test_base protected: cryptonote::account_base m_bob; - cryptonote::transaction m_tx; + cryptonote::Transaction m_tx; crypto::public_key m_tx_pub_key; }; diff --git a/tests/unit_tests/BlockingQueue.cpp b/tests/unit_tests/BlockingQueue.cpp new file mode 100644 index 0000000000..df673387fe --- /dev/null +++ b/tests/unit_tests/BlockingQueue.cpp @@ -0,0 +1,211 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include "common/BlockingQueue.h" + +#include +#include +#include +#include + +class ParallelProcessor { +public: + + ParallelProcessor(size_t threads) + : m_threads(threads) {} + + template + void spawn(F f) { + for (auto& t : m_threads) { + t = std::thread(f); + } + } + + void join() { + for (auto& t : m_threads) { + t.join(); + } + } + +private: + + std::vector m_threads; + +}; + +// single producer, many consumers +void TestQueue_SPMC(unsigned iterations, unsigned threadCount, unsigned queueSize) { + + BlockingQueue bq(queueSize); + + ParallelProcessor processor(threadCount); + std::atomic result(0); + + processor.spawn([&bq, &result]{ + int v = 0; + int64_t sum = 0; + + while (bq.pop(v)) { + sum += v; + } + + result += sum; + // std::cout << "Sum: " << sum << std::endl; + }); + + int64_t expectedSum = 0; + + for (unsigned i = 0; i < iterations; ++i) { + expectedSum += i; + ASSERT_TRUE(bq.push(i)); + } + + bq.close(); + processor.join(); + + ASSERT_EQ(expectedSum, result.load()); +} + +void TestQueue_MPSC(unsigned iterations, unsigned threadCount, unsigned queueSize) { + + BlockingQueue bq(queueSize); + + ParallelProcessor processor(threadCount); + std::atomic counter(0); + std::atomic pushed(0); + + processor.spawn([&]{ + int v = 0; + int64_t sum = 0; + + for(;;) { + unsigned value = counter.fetch_add(1); + if (value >= iterations) + break; + + bq.push(value); + sum += value; + } + + pushed += sum; + // std::cout << "Sum: " << sum << std::endl; + }); + + int64_t expectedSum = 0; + + for (unsigned i = 0; i < iterations; ++i) { + int value; + ASSERT_TRUE(bq.pop(value)); + expectedSum += i; + } + + ASSERT_EQ(0, bq.size()); + + processor.join(); + + ASSERT_EQ(expectedSum, pushed); +} + + +TEST(BlockingQueue, SPMC) +{ + TestQueue_SPMC(10000, 1, 1); + TestQueue_SPMC(10000, 4, 1); + TestQueue_SPMC(10000, 16, 16); + TestQueue_SPMC(10000, 16, 100); +} + +TEST(BlockingQueue, MPSC) +{ + TestQueue_MPSC(10000, 1, 1); + TestQueue_MPSC(10000, 4, 1); + TestQueue_MPSC(10000, 16, 16); + TestQueue_MPSC(10000, 16, 100); +} + + +TEST(BlockingQueue, PerfTest) +{ + // TestQueue_SPMC(1000000, 32, 1); +} + +TEST(BlockingQueue, Close) +{ + BlockingQueue bq(4); + ParallelProcessor p(4); + + p.spawn([&bq] { + int v; + while (bq.pop(v)) + ; + }); + + bq.push(10); // enqueue 1 item + + bq.close(); // all threads should unblock and finish + p.join(); +} + +TEST(BlockingQueue, CloseAndWait) +{ + size_t queueSize = 100; + BlockingQueue bq(queueSize); + ParallelProcessor p(4); + + std::atomic itemsPopped(0); + + // fill the queue + for (int i = 0; i < queueSize; ++i) + bq.push(i); + + p.spawn([&bq, &itemsPopped] { + int v; + while (bq.pop(v)) { + itemsPopped += 1; + // some delay to make close() really wait + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + }); + + + // check with multiple closing + auto f1 = std::async(std::launch::async, [&] { bq.close(true); }); + auto f2 = std::async(std::launch::async, [&] { bq.close(true); }); + + bq.close(true); + + f1.get(); + f2.get(); + + ASSERT_EQ(queueSize, itemsPopped.load()); + + p.join(); +} + +TEST(BlockingQueue, AllowsMoveOnly) +{ + BlockingQueue> bq(1); + + std::unique_ptr v(new int(100)); + ASSERT_TRUE(bq.push(std::move(v))); + + std::unique_ptr popval; + bq.pop(popval); + + ASSERT_EQ(*popval, 100); +} diff --git a/tests/unit_tests/INodeStubs.cpp b/tests/unit_tests/INodeStubs.cpp index 6f0c8dc784..6f83aeb1fb 100644 --- a/tests/unit_tests/INodeStubs.cpp +++ b/tests/unit_tests/INodeStubs.cpp @@ -43,9 +43,9 @@ void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockI cryptonote::block_complete_entry e; e.block = cryptonote::t_serializable_object_to_blob(blockchain[m_lastHeight]); - for (auto hash: blockchain[m_lastHeight].tx_hashes) + for (auto hash : blockchain[m_lastHeight].txHashes) { - cryptonote::transaction tx; + cryptonote::Transaction tx; if (!m_blockchainGenerator.getTransactionByHash(hash, tx)) continue; @@ -72,13 +72,13 @@ void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const crypto::ha callback(std::error_code()); } -void INodeTrivialRefreshStub::relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) +void INodeTrivialRefreshStub::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { std::thread task(&INodeTrivialRefreshStub::doRelayTransaction, this, transaction, callback); task.detach(); } -void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback) +void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { if (m_nextTxError) { @@ -122,7 +122,7 @@ void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amo void INodeTrivialRefreshStub::startAlternativeChain(uint64_t height) { - std::vector& blockchain = m_blockchainGenerator.getBlockchain(); + std::vector& blockchain = m_blockchainGenerator.getBlockchain(); assert(height < blockchain.size()); assert(height > m_lastHeight); diff --git a/tests/unit_tests/INodeStubs.h b/tests/unit_tests/INodeStubs.h index b25e2a5814..60ce379e98 100644 --- a/tests/unit_tests/INodeStubs.h +++ b/tests/unit_tests/INodeStubs.h @@ -38,7 +38,7 @@ class INodeDummyStub : public CryptoNote::INode virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) {callback(std::error_code());}; - virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback) {callback(std::error_code());}; + virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) {callback(std::error_code());}; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) {callback(std::error_code());}; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); }; }; @@ -53,7 +53,7 @@ class INodeTrivialRefreshStub : public INodeDummyStub virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); - virtual void relayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); @@ -63,7 +63,7 @@ class INodeTrivialRefreshStub : public INodeDummyStub private: void doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); - void doRelayTransaction(const cryptonote::transaction& transaction, const Callback& callback); + void doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); void doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback); uint64_t m_lastHeight; diff --git a/tests/unit_tests/TestBlockchainGenerator.cpp b/tests/unit_tests/TestBlockchainGenerator.cpp index 41ac13d70b..92d5e61909 100644 --- a/tests/unit_tests/TestBlockchainGenerator.cpp +++ b/tests/unit_tests/TestBlockchainGenerator.cpp @@ -31,7 +31,7 @@ class TransactionForAddressCreator : public multi_tx_test_base<5> return base_class::init(); } - void generate(const cryptonote::account_public_address& address, cryptonote::transaction& tx) + void generate(const cryptonote::AccountPublicAddress& address, cryptonote::Transaction& tx) { cryptonote::tx_destination_entry destination(this->m_source_amount, address); std::vector destinations; @@ -42,18 +42,20 @@ class TransactionForAddressCreator : public multi_tx_test_base<5> }; -TestBlockchainGenerator::TestBlockchainGenerator() +TestBlockchainGenerator::TestBlockchainGenerator(const cryptonote::Currency& currency) : + m_currency(currency), + generator(currency) { miner_acc.generate(); addGenesisBlock(); } -std::vector& TestBlockchainGenerator::getBlockchain() +std::vector& TestBlockchainGenerator::getBlockchain() { return m_blockchain; } -bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cryptonote::transaction& tx) +bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cryptonote::Transaction& tx) { auto it = m_txs.find(hash); if (it == m_txs.end()) @@ -65,10 +67,10 @@ bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cry void TestBlockchainGenerator::addGenesisBlock() { - cryptonote::block genesis; + cryptonote::Block genesis; uint64_t timestamp = time(NULL); - generator.construct_block(genesis, miner_acc, timestamp); + generator.constructBlock(genesis, miner_acc, timestamp); m_blockchain.push_back(genesis); } @@ -78,47 +80,47 @@ void TestBlockchainGenerator::generateEmptyBlocks(size_t count) for (size_t i = 0; i < count; ++i) { - cryptonote::block& prev_block = m_blockchain.back(); - cryptonote::block block; - generator.construct_block(block, prev_block, miner_acc); + cryptonote::Block& prev_block = m_blockchain.back(); + cryptonote::Block block; + generator.constructBlock(block, prev_block, miner_acc); m_blockchain.push_back(block); } } -void TestBlockchainGenerator::addTxToBlockchain(const cryptonote::transaction& transaction) +void TestBlockchainGenerator::addTxToBlockchain(const cryptonote::Transaction& transaction) { crypto::hash txHash = cryptonote::get_transaction_hash(transaction); m_txs[txHash] = transaction; - std::list txs; + std::list txs; txs.push_back(transaction); - cryptonote::block& prev_block = m_blockchain.back(); - cryptonote::block block; + cryptonote::Block& prev_block = m_blockchain.back(); + cryptonote::Block block; - generator.construct_block(block, prev_block, miner_acc, txs); + generator.constructBlock(block, prev_block, miner_acc, txs); m_blockchain.push_back(block); } -bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::account_public_address& address) +bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::AccountPublicAddress& address) { TransactionForAddressCreator creator; if (!creator.init()) return false; - cryptonote::transaction tx; + cryptonote::Transaction tx; creator.generate(address, tx); crypto::hash txHash = cryptonote::get_transaction_hash(tx); m_txs[txHash] = tx; - std::list txs; + std::list txs; txs.push_back(tx); - cryptonote::block& prev_block = m_blockchain.back(); - cryptonote::block block; + cryptonote::Block& prev_block = m_blockchain.back(); + cryptonote::Block block; - generator.construct_block(block, prev_block, miner_acc, txs); + generator.constructBlock(block, prev_block, miner_acc, txs); m_blockchain.push_back(block); return true; diff --git a/tests/unit_tests/TestBlockchainGenerator.h b/tests/unit_tests/TestBlockchainGenerator.h index 80fd5432c5..6863d3b8fc 100644 --- a/tests/unit_tests/TestBlockchainGenerator.h +++ b/tests/unit_tests/TestBlockchainGenerator.h @@ -17,29 +17,32 @@ #pragma once -#include "../core_tests/chaingen.h" #include #include #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/Currency.h" #include "crypto/hash.h" +#include "../TestGenerator/TestGenerator.h" + class TestBlockchainGenerator { public: - TestBlockchainGenerator(); + TestBlockchainGenerator(const cryptonote::Currency& currency); - std::vector& getBlockchain(); + std::vector& getBlockchain(); void addGenesisBlock(); void generateEmptyBlocks(size_t count); - bool getBlockRewardForAddress(const cryptonote::account_public_address& address); - void addTxToBlockchain(const cryptonote::transaction& transaction); - bool getTransactionByHash(const crypto::hash& hash, cryptonote::transaction& tx); + bool getBlockRewardForAddress(const cryptonote::AccountPublicAddress& address); + void addTxToBlockchain(const cryptonote::Transaction& transaction); + bool getTransactionByHash(const crypto::hash& hash, cryptonote::Transaction& tx); private: + const cryptonote::Currency& m_currency; test_generator generator; cryptonote::account_base miner_acc; - std::vector m_blockchain; - std::unordered_map m_txs; + std::vector m_blockchain; + std::unordered_map m_txs; }; diff --git a/tests/unit_tests/TestUpgradeDetector.cpp b/tests/unit_tests/TestUpgradeDetector.cpp new file mode 100644 index 0000000000..a8146a64a9 --- /dev/null +++ b/tests/unit_tests/TestUpgradeDetector.cpp @@ -0,0 +1,309 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include + +#include "gtest/gtest.h" + +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/UpgradeDetector.h" + +namespace { + using cryptonote::BLOCK_MAJOR_VERSION_1; + using cryptonote::BLOCK_MAJOR_VERSION_2; + using cryptonote::BLOCK_MINOR_VERSION_0; + using cryptonote::BLOCK_MINOR_VERSION_1; + + struct BlockEx { + cryptonote::Block bl; + }; + + typedef std::vector BlockVector; + typedef cryptonote::BasicUpgradeDetector UpgradeDetector; + + cryptonote::Currency createCurrency(uint64_t upgradeHeight = UpgradeDetector::UNDEF_HEIGHT) { + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.upgradeVotingThreshold(90); + currencyBuilder.upgradeVotingWindow(720); + currencyBuilder.upgradeWindow(720); + currencyBuilder.upgradeHeight(upgradeHeight); + return currencyBuilder.currency(); + } + + void createBlocks(BlockVector& blockchain, size_t count, uint8_t majorVersion, uint8_t minorVersion) { + for (size_t i = 0; i < count; ++i) { + BlockEx b; + b.bl.majorVersion = majorVersion; + b.bl.minorVersion = minorVersion; + b.bl.timestamp = 0; + blockchain.push_back(b); + } + } + + void createBlocks(BlockVector& blockchain, UpgradeDetector& upgradeDetector, size_t count, uint8_t majorVersion, uint8_t minorVersion) { + for (size_t i = 0; i < count; ++i) { + BlockEx b; + b.bl.majorVersion = majorVersion; + b.bl.minorVersion = minorVersion; + b.bl.timestamp = 0; + blockchain.push_back(b); + upgradeDetector.blockPushed(); + } + } + + void popBlocks(BlockVector& blockchain, UpgradeDetector& upgradeDetector, size_t count) { + for (size_t i = 0; i < count; ++i) { + blockchain.pop_back(); + upgradeDetector.blockPopped(); + } + } + + + TEST(UpgradeDetector_voting_init, handlesEmptyBlockchain) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_voting_init, votingIsNotCompleteDueShortBlockchain) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + createBlocks(blocks, currency.upgradeVotingWindow() - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfBlocks) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), currency.upgradeVotingWindow() - 1); + } + + TEST(UpgradeDetector_voting_init, votingIsNotCompleteDueLackOfVoices) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, currency.minNumberVotingBlocks() - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfVoices) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1); + } + + TEST(UpgradeDetector_voting_init, handlesOneCompleteUpgrade) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + uint64_t upgradeHeight = currency.calculateUpgradeHeight(blocks.size() - 1); + createBlocks(blocks, upgradeHeight - blocks.size(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + // Upgrade is here + createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), currency.upgradeVotingWindow() - 1); + ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); + } + + TEST(UpgradeDetector_voting_init, handlesAFewCompleteUpgrades) { + cryptonote::Currency currency = createCurrency(); + const uint8_t BLOCK_V3 = BLOCK_MAJOR_VERSION_2 + 1; + const uint8_t BLOCK_V4 = BLOCK_MAJOR_VERSION_2 + 2; + + BlockVector blocks; + + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + uint64_t votingCompleteHeigntV2 = blocks.size() - 1; + uint64_t upgradeHeightV2 = currency.calculateUpgradeHeight(votingCompleteHeigntV2); + createBlocks(blocks, upgradeHeightV2 - blocks.size(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + // Upgrade to v2 is here + createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); + + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_1); + uint64_t votingCompleteHeigntV3 = blocks.size() - 1; + uint64_t upgradeHeightV3 = currency.calculateUpgradeHeight(votingCompleteHeigntV3); + createBlocks(blocks, upgradeHeightV3 - blocks.size(), BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); + // Upgrade to v3 is here + createBlocks(blocks, 1, BLOCK_V3, BLOCK_MINOR_VERSION_0); + + createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_V3, BLOCK_MINOR_VERSION_1); + uint64_t votingCompleteHeigntV4 = blocks.size() - 1; + uint64_t upgradeHeightV4 = currency.calculateUpgradeHeight(votingCompleteHeigntV4); + createBlocks(blocks, upgradeHeightV4 - blocks.size(), BLOCK_V3, BLOCK_MINOR_VERSION_0); + // Upgrade to v4 is here + createBlocks(blocks, 1, BLOCK_V4, BLOCK_MINOR_VERSION_0); + + UpgradeDetector upgradeDetectorV2(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetectorV2.init()); + ASSERT_EQ(upgradeDetectorV2.votingCompleteHeight(), votingCompleteHeigntV2); + ASSERT_EQ(upgradeDetectorV2.upgradeHeight(), upgradeHeightV2); + + UpgradeDetector upgradeDetectorV3(currency, blocks, BLOCK_V3); + ASSERT_TRUE(upgradeDetectorV3.init()); + ASSERT_EQ(upgradeDetectorV3.votingCompleteHeight(), votingCompleteHeigntV3); + ASSERT_EQ(upgradeDetectorV3.upgradeHeight(), upgradeHeightV3); + + UpgradeDetector upgradeDetectorV4(currency, blocks, BLOCK_V4); + ASSERT_TRUE(upgradeDetectorV4.init()); + ASSERT_EQ(upgradeDetectorV4.votingCompleteHeight(), votingCompleteHeigntV4); + ASSERT_EQ(upgradeDetectorV4.upgradeHeight(), upgradeHeightV4); + } + + TEST(UpgradeDetector_upgradeHeight_init, handlesEmptyBlockchain) { + const uint64_t upgradeHeight = 17; + cryptonote::Currency currency = createCurrency(upgradeHeight); + BlockVector blocks; + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_upgradeHeight_init, handlesBlockchainBeforeUpgrade) { + const uint64_t upgradeHeight = 17; + cryptonote::Currency currency = createCurrency(upgradeHeight); + BlockVector blocks; + createBlocks(blocks, upgradeHeight, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_upgradeHeight_init, handlesBlockchainAtUpgrade) { + const uint64_t upgradeHeight = 17; + cryptonote::Currency currency = createCurrency(upgradeHeight); + BlockVector blocks; + createBlocks(blocks, upgradeHeight + 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_upgradeHeight_init, handlesBlockchainAfterUpgrade) { + const uint64_t upgradeHeight = 17; + cryptonote::Currency currency = createCurrency(upgradeHeight); + BlockVector blocks; + createBlocks(blocks, upgradeHeight + 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); + + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_voting, handlesVotingCompleteStartingEmptyBlockchain) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + + createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1); + } + + TEST(UpgradeDetector_voting, handlesVotingCompleteStartingNonEmptyBlockchain) { + cryptonote::Currency currency = createCurrency(); + assert(currency.minNumberVotingBlocks() >= 2); + const uint64_t portion = currency.minNumberVotingBlocks() - currency.minNumberVotingBlocks() / 2; + + BlockVector blocks; + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + + createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks() - portion, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + + ASSERT_TRUE(upgradeDetector.init()); + createBlocks(blocks, upgradeDetector, portion, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1); + } + + TEST(UpgradeDetector_voting, handlesVotingCancelling) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + + createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + uint64_t votingCompleteHeight = blocks.size() - 1; + uint64_t hadrforkHeight = currency.calculateUpgradeHeight(votingCompleteHeight); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), votingCompleteHeight); + + createBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), votingCompleteHeight); + + // Cancel voting + popBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight - 1); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), votingCompleteHeight); + popBlocks(blocks, upgradeDetector, 1); + ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); + } + + TEST(UpgradeDetector_voting, handlesVotingAndUpgradeCancelling) { + cryptonote::Currency currency = createCurrency(); + BlockVector blocks; + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + ASSERT_TRUE(upgradeDetector.init()); + + createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); + uint64_t votingCompleteHeight = blocks.size() - 1; + uint64_t hadrforkHeight = currency.calculateUpgradeHeight(votingCompleteHeight); + ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight()); + + createBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); + createBlocks(blocks, upgradeDetector, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); + ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight()); + + // Cancel upgrade (pop block v2) + popBlocks(blocks, upgradeDetector, 1); + ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight()); + + // Pop blocks after voting + popBlocks(blocks, upgradeDetector, hadrforkHeight - votingCompleteHeight); + ASSERT_EQ(votingCompleteHeight, upgradeDetector.votingCompleteHeight()); + + // Cancel voting + popBlocks(blocks, upgradeDetector, 1); + ASSERT_EQ(UpgradeDetector::UNDEF_HEIGHT, upgradeDetector.votingCompleteHeight()); + } +} diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index ecf127d789..20452ba6c5 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -454,73 +454,75 @@ namespace "\x22\x09\x39\x68\x9e\xdf\x1a\xbd\x5b\xc1\xd0\x31\xf7\x3e\xcd\x6c" "\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d"); std::string test_keys_addr_str = "2AaF4qEmER6dNeM6dfiBFL7kqund3HYGvMBF3ttsNd9SfzgYB6L7ep1Yg1osYJzLdaKAYSLVh6e6jKnAuzj3bw1oGyd1x7Z"; + const uint64_t TEST_PUBLIC_ADDRESS_BASE58_PREFIX = 6; } -TEST(get_account_address_as_str, works_correctly) +TEST(getAccountAddressAsStr, works_correctly) { - cryptonote::account_public_address addr; + cryptonote::AccountPublicAddress addr; ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr)); - std::string addr_str = cryptonote::get_account_address_as_str(addr); + std::string addr_str = cryptonote::getAccountAddressAsStr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, addr); ASSERT_EQ(addr_str, test_keys_addr_str); } -TEST(get_account_address_from_str, handles_valid_address) +TEST(parseAccountAddressString, handles_valid_address) { - cryptonote::account_public_address addr; - ASSERT_TRUE(cryptonote::get_account_address_from_str(addr, test_keys_addr_str)); + uint64_t prefix; + cryptonote::AccountPublicAddress addr; + ASSERT_TRUE(cryptonote::parseAccountAddressString(prefix, addr, test_keys_addr_str)); + ASSERT_EQ(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, prefix); std::string blob; ASSERT_TRUE(serialization::dump_binary(addr, blob)); ASSERT_EQ(blob, test_serialized_keys); } -TEST(get_account_address_from_str, fails_on_invalid_address_format) +TEST(parseAccountAddressString, fails_on_invalid_address_format) { - cryptonote::account_public_address addr; std::string addr_str = test_keys_addr_str; addr_str[0] = '0'; - ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, addr_str)); + uint64_t prefix; + cryptonote::AccountPublicAddress addr; + ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); } -TEST(get_account_address_from_str, fails_on_invalid_address_prefix) +TEST(parseAccountAddressString, fails_on_invalid_address_prefix) { std::string addr_str = base58::encode_addr(0, test_serialized_keys); - cryptonote::account_public_address addr; - ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, addr_str)); + uint64_t prefix; + cryptonote::AccountPublicAddress addr; + ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); } -TEST(get_account_address_from_str, fails_on_invalid_address_content) +TEST(parseAccountAddressString, fails_on_invalid_address_content) { - std::string addr_str = base58::encode_addr(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, test_serialized_keys.substr(1)); + std::string addr_str = base58::encode_addr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, test_serialized_keys.substr(1)); - cryptonote::account_public_address addr; - ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, addr_str)); + uint64_t prefix; + cryptonote::AccountPublicAddress addr; + ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); } -TEST(get_account_address_from_str, fails_on_invalid_address_spend_key) +TEST(parseAccountAddressString, fails_on_invalid_address_spend_key) { std::string serialized_keys_copy = test_serialized_keys; serialized_keys_copy[0] = '\0'; - std::string addr_str = base58::encode_addr(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); + std::string addr_str = base58::encode_addr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); - cryptonote::account_public_address addr; - ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, addr_str)); + uint64_t prefix; + cryptonote::AccountPublicAddress addr; + ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); } -TEST(get_account_address_from_str, fails_on_invalid_address_view_key) +TEST(parseAccountAddressString, fails_on_invalid_address_view_key) { std::string serialized_keys_copy = test_serialized_keys; serialized_keys_copy.back() = '\x01'; - std::string addr_str = base58::encode_addr(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); + std::string addr_str = base58::encode_addr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); - cryptonote::account_public_address addr; - ASSERT_FALSE(cryptonote::get_account_address_from_str(addr, addr_str)); -} - -TEST(get_account_address_from_str, parses_old_address_format) -{ - cryptonote::account_public_address addr; - ASSERT_TRUE(cryptonote::get_account_address_from_str(addr, "002391bbbb24dea6fd95232e97594a27769d0153d053d2102b789c498f57a2b00b69cd6f2f5c529c1660f2f4a2b50178d6640c20ce71fe26373041af97c5b10236fc")); + uint64_t prefix; + cryptonote::AccountPublicAddress addr; + ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); } diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 39f9f68f6e..261e73addb 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -18,218 +18,400 @@ #include "gtest/gtest.h" #include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/Currency.h" using namespace cryptonote; namespace { + const uint64_t TEST_GRANTED_FULL_REWARD_ZONE = 10000; + const uint64_t TEST_MONEY_SUPPLY = static_cast(-1); + const uint64_t TEST_EMISSION_SPEED_FACTOR = 18; + //-------------------------------------------------------------------------------------------------------------------- - class block_reward_and_already_generated_coins : public ::testing::Test - { + class getBlockReward_and_already_generated_coins : public ::testing::Test { + public: + getBlockReward_and_already_generated_coins() : + ::testing::Test(), + m_currency(cryptonote::CurrencyBuilder(). + blockGrantedFullRewardZone(TEST_GRANTED_FULL_REWARD_ZONE). + moneySupply(TEST_MONEY_SUPPLY). + emissionSpeedFactor(TEST_EMISSION_SPEED_FACTOR). + currency()) { + } + protected: - static const size_t current_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE / 2; + static const size_t currentBlockSize = TEST_GRANTED_FULL_REWARD_ZONE / 2; - bool m_block_not_too_big; - uint64_t m_block_reward; + cryptonote::Currency m_currency; + bool m_blockTooBig; + int64_t m_emissionChange; + uint64_t m_blockReward; }; - #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); \ - ASSERT_TRUE(m_block_not_too_big); \ - ASSERT_EQ(m_block_reward, UINT64_C(expected_reward)); + #define TEST_ALREADY_GENERATED_COINS(alreadyGeneratedCoins, expectedReward) \ + m_blockTooBig = !m_currency.getBlockReward(0, currentBlockSize, alreadyGeneratedCoins, 0, false, \ + m_blockReward, m_emissionChange); \ + ASSERT_FALSE(m_blockTooBig); \ + ASSERT_EQ(UINT64_C(expectedReward), m_blockReward); \ + ASSERT_EQ(UINT64_C(expectedReward), m_emissionChange); - TEST_F(block_reward_and_already_generated_coins, handles_first_values) - { + TEST_F(getBlockReward_and_already_generated_coins, handles_first_values) { TEST_ALREADY_GENERATED_COINS(0, 70368744177663); - TEST_ALREADY_GENERATED_COINS(m_block_reward, 70368475742208); + TEST_ALREADY_GENERATED_COINS(m_blockReward, 70368475742208); TEST_ALREADY_GENERATED_COINS(UINT64_C(2756434948434199641), 59853779316998); } - TEST_F(block_reward_and_already_generated_coins, correctly_steps_from_2_to_1) - { - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((2 << 18) + 1), 2); - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - (2 << 18) , 2); - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((2 << 18) - 1), 1); + TEST_F(getBlockReward_and_already_generated_coins, correctly_steps_from_reward_2_to_1) { + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - ((UINT64_C(2) << m_currency.emissionSpeedFactor()) + 1), 2); + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - (UINT64_C(2) << m_currency.emissionSpeedFactor()) , 2); + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - ((UINT64_C(2) << m_currency.emissionSpeedFactor()) - 1), 1); } - TEST_F(block_reward_and_already_generated_coins, handles_max) - { - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((1 << 18) + 1), 1); - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - (1 << 18) , 1); - TEST_ALREADY_GENERATED_COINS(MONEY_SUPPLY - ((1 << 18) - 1), 0); + TEST_F(getBlockReward_and_already_generated_coins, handles_max_already_generaged_coins) { + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - ((UINT64_C(1) << m_currency.emissionSpeedFactor()) + 1), 1); + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - (UINT64_C(1) << m_currency.emissionSpeedFactor()) , 1); + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - ((UINT64_C(1) << m_currency.emissionSpeedFactor()) - 1), 0); + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply() - 1, 0); + TEST_ALREADY_GENERATED_COINS(m_currency.moneySupply(), 0); } //-------------------------------------------------------------------------------------------------------------------- - class block_reward_and_current_block_size : public ::testing::Test - { - protected: - virtual void SetUp() - { - m_block_not_too_big = get_block_reward(0, 0, already_generated_coins, m_standard_block_reward); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward); + class getBlockReward_and_median_and_blockSize : public ::testing::Test { + public: + getBlockReward_and_median_and_blockSize() : + ::testing::Test(), + m_currency(cryptonote::CurrencyBuilder(). + blockGrantedFullRewardZone(TEST_GRANTED_FULL_REWARD_ZONE). + moneySupply(TEST_MONEY_SUPPLY). + emissionSpeedFactor(TEST_EMISSION_SPEED_FACTOR). + currency()) { } - void do_test(size_t median_block_size, size_t current_block_size) - { - m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward); + protected: + static const uint64_t alreadyGeneratedCoins = 0; + + virtual void SetUp() { + m_blockTooBig = !m_currency.getBlockReward(0, 0, alreadyGeneratedCoins, 0, false, + m_standardBlockReward, m_emissionChange); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(UINT64_C(70368744177663), m_standardBlockReward); } - static const uint64_t already_generated_coins = 0; + void do_test(size_t medianBlockSize, size_t currentBlockSize) { + m_blockTooBig = !m_currency.getBlockReward(medianBlockSize, currentBlockSize, alreadyGeneratedCoins, 0, false, + m_blockReward, m_emissionChange); + } - bool m_block_not_too_big; - uint64_t m_block_reward; - uint64_t m_standard_block_reward; + cryptonote::Currency m_currency; + bool m_blockTooBig; + int64_t m_emissionChange; + uint64_t m_blockReward; + uint64_t m_standardBlockReward; }; - TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level) - { - do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward); + TEST_F(getBlockReward_and_median_and_blockSize, handles_zero_median) { + do_test(0, TEST_GRANTED_FULL_REWARD_ZONE); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level) - { - do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward); + TEST_F(getBlockReward_and_median_and_blockSize, handles_median_lt_relevance_level) { + do_test(TEST_GRANTED_FULL_REWARD_ZONE - 1, TEST_GRANTED_FULL_REWARD_ZONE); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level) - { - do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_LT(m_block_reward, m_standard_block_reward); + TEST_F(getBlockReward_and_median_and_blockSize, handles_median_eq_relevance_level) { + do_test(TEST_GRANTED_FULL_REWARD_ZONE, TEST_GRANTED_FULL_REWARD_ZONE - 1); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_less_2_relevance_level) - { - do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 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(getBlockReward_and_median_and_blockSize, handles_median_gt_relevance_level) { + do_test(TEST_GRANTED_FULL_REWARD_ZONE + 1, TEST_GRANTED_FULL_REWARD_ZONE); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level) - { - do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(0, m_block_reward); + TEST_F(getBlockReward_and_median_and_blockSize, handles_big_median) { + size_t blockSize = 1; + size_t medianSize = std::numeric_limits::max(); + + do_test(medianSize, blockSize); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_current_block_size, handles_block_size_gt_2_relevance_level) - { - do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1); - ASSERT_FALSE(m_block_not_too_big); + TEST_F(getBlockReward_and_median_and_blockSize, handles_big_block_size) { + size_t blockSize = std::numeric_limits::max() - 1; // even + size_t medianSize = blockSize / 2; // 2 * medianSize == blockSize + + do_test(medianSize, blockSize); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(0, m_blockReward); } - TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size) - { -#if !defined(NDEBUG) - size_t huge_size = std::numeric_limits::max() + UINT64_C(2); - ASSERT_DEATH(do_test(huge_size, huge_size + 1), ""); -#endif + TEST_F(getBlockReward_and_median_and_blockSize, handles_big_block_size_fail) { + size_t blockSize = std::numeric_limits::max(); + size_t medianSize = blockSize / 2 - 1; + + do_test(medianSize, blockSize); + ASSERT_TRUE(m_blockTooBig); } - TEST_F(block_reward_and_current_block_size, fails_on_huge_block_size) - { -#if !defined(NDEBUG) - size_t huge_size = std::numeric_limits::max() + UINT64_C(2); - ASSERT_DEATH(do_test(huge_size - 2, huge_size), ""); -#endif + TEST_F(getBlockReward_and_median_and_blockSize, handles_big_median_and_block_size) { + // blockSize should be greater medianSize + size_t blockSize = std::numeric_limits::max(); + size_t medianSize = std::numeric_limits::max() - 1; + + do_test(medianSize, blockSize); + ASSERT_FALSE(m_blockTooBig); + ASSERT_LT(m_blockReward, m_standardBlockReward); } //-------------------------------------------------------------------------------------------------------------------- - class block_reward_and_last_block_sizes : public ::testing::Test - { - protected: - virtual void SetUp() - { - m_last_block_sizes.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - m_last_block_sizes.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - m_last_block_sizes.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - m_last_block_sizes.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - m_last_block_sizes.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - - m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; - - m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward); + class getBlockReward_and_currentBlockSize : public ::testing::Test { + public: + getBlockReward_and_currentBlockSize() : + ::testing::Test(), + m_currency(cryptonote::CurrencyBuilder(). + blockGrantedFullRewardZone(TEST_GRANTED_FULL_REWARD_ZONE). + moneySupply(TEST_MONEY_SUPPLY). + emissionSpeedFactor(TEST_EMISSION_SPEED_FACTOR). + currency()) { } - void do_test(size_t current_block_size) - { - 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); + protected: + static const size_t testMedian = 7 * TEST_GRANTED_FULL_REWARD_ZONE; + static const uint64_t alreadyGeneratedCoins = 0; + + virtual void SetUp() { + m_blockTooBig = !m_currency.getBlockReward(testMedian, 0, alreadyGeneratedCoins, 0, false, + m_standardBlockReward, m_emissionChange); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(UINT64_C(70368744177663), m_standardBlockReward); } - static const uint64_t already_generated_coins = 0; + void do_test(size_t currentBlockSize) { + m_blockTooBig = !m_currency.getBlockReward(testMedian, currentBlockSize, alreadyGeneratedCoins, 0, false, + m_blockReward, m_emissionChange); + } - std::vector m_last_block_sizes; - uint64_t m_last_block_sizes_median; - bool m_block_not_too_big; - uint64_t m_block_reward; - uint64_t m_standard_block_reward; + cryptonote::Currency m_currency; + bool m_blockTooBig; + int64_t m_emissionChange; + uint64_t m_blockReward; + uint64_t m_standardBlockReward; }; - TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median) - { - do_test(m_last_block_sizes_median - 1); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward); + TEST_F(getBlockReward_and_currentBlockSize, handles_zero_block_size) { + do_test(0); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); + } + + TEST_F(getBlockReward_and_currentBlockSize, handles_block_size_less_median) { + do_test(testMedian - 1); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_median) - { - do_test(m_last_block_sizes_median); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward); + TEST_F(getBlockReward_and_currentBlockSize, handles_block_size_eq_median) { + do_test(testMedian); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward, m_blockReward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median) - { - do_test(m_last_block_sizes_median + 1); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_LT(m_block_reward, m_standard_block_reward); + TEST_F(getBlockReward_and_currentBlockSize, handles_block_size_gt_median) { + do_test(testMedian + 1); + ASSERT_FALSE(m_blockTooBig); + ASSERT_LT(m_blockReward, m_standardBlockReward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians) - { - do_test(2 * m_last_block_sizes_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(getBlockReward_and_currentBlockSize, handles_block_size_less_2_medians) { + do_test(2 * testMedian - 1); + ASSERT_FALSE(m_blockTooBig); + ASSERT_LT(m_blockReward, m_standardBlockReward); + ASSERT_GT(m_blockReward, 0); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians) - { - do_test(2 * m_last_block_sizes_median); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(0, m_block_reward); + TEST_F(getBlockReward_and_currentBlockSize, handles_block_size_eq_2_medians) { + do_test(2 * testMedian); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(0, m_blockReward); } - TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians) - { - do_test(2 * m_last_block_sizes_median + 1); - ASSERT_FALSE(m_block_not_too_big); + TEST_F(getBlockReward_and_currentBlockSize, handles_block_size_gt_2_medians) { + do_test(2 * testMedian + 1); + ASSERT_TRUE(m_blockTooBig); } - TEST_F(block_reward_and_last_block_sizes, calculates_correctly) - { - ASSERT_EQ(0, m_last_block_sizes_median % 8); + TEST_F(getBlockReward_and_currentBlockSize, calculates_correctly) { + ASSERT_EQ(0, testMedian % 8); - do_test(m_last_block_sizes_median * 9 / 8); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64); + // reward = 1 - (k - 1)^2 + // k = 9/8 => reward = 63/64 + do_test(testMedian * 9 / 8); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward * 63 / 64, m_blockReward); // 3/2 = 12/8 - do_test(m_last_block_sizes_median * 3 / 2); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4); + do_test(testMedian * 3 / 2); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward * 3 / 4, m_blockReward); + + do_test(testMedian * 15 / 8); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(m_standardBlockReward * 15 / 64, m_blockReward); + } + //-------------------------------------------------------------------------------------------------------------------- + const unsigned int testEmissionSpeedFactor = 4; + const size_t testGrantedFullRewardZone = 1000; + const size_t testMedian = testGrantedFullRewardZone; + const size_t testBlockSize = testMedian + testMedian * 8 / 10; // expected penalty 0.64 * reward + const uint64_t testPenalty = 64; // percentage + const uint64_t testMoneySupply = UINT64_C(1000000000); + const uint64_t expectedBaseReward = 62500000; // testMoneySupply >> testEmissionSpeedFactor + const uint64_t expectedBlockReward = 22500000; // expectedBaseReward - expectedBaseReward * testPenalty / 100 + //-------------------------------------------------------------------------------------------------------------------- + class getBlockReward_fee_and_penalizeFee_test : public ::testing::Test { + public: + getBlockReward_fee_and_penalizeFee_test() : + ::testing::Test(), + m_currency(cryptonote::CurrencyBuilder(). + blockGrantedFullRewardZone(testGrantedFullRewardZone). + moneySupply(testMoneySupply). + emissionSpeedFactor(testEmissionSpeedFactor). + currency()) { + } + + protected: + virtual void SetUp() { + uint64_t blockReward; + int64_t emissionChange; + + m_blockTooBig = !m_currency.getBlockReward(testMedian, testBlockSize, 0, 0, false, blockReward, emissionChange); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward, blockReward); + ASSERT_EQ(expectedBlockReward, emissionChange); + } + + void do_test(uint64_t alreadyGeneratedCoins, uint64_t fee, bool penalizeFee) { + m_blockTooBig = !m_currency.getBlockReward(testMedian, testBlockSize, alreadyGeneratedCoins, fee, penalizeFee, + m_blockReward, m_emissionChange); + } + + cryptonote::Currency m_currency; + bool m_blockTooBig; + int64_t m_emissionChange; + uint64_t m_blockReward; + }; + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_zero_fee_and_no_penalize_fee) { + do_test(0, 0, false); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward, m_blockReward); + ASSERT_EQ(expectedBlockReward, m_emissionChange); + ASSERT_GT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_zero_fee_and_penalize_fee) { + do_test(0, 0, true); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward, m_blockReward); + ASSERT_EQ(expectedBlockReward, m_emissionChange); + ASSERT_GT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_lt_block_reward_and_no_penalize_fee) { + uint64_t fee = expectedBlockReward / 2; + do_test(0, fee, false); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee, m_blockReward); + ASSERT_EQ(expectedBlockReward, m_emissionChange); + ASSERT_GT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_lt_block_reward_and_penalize_fee) { + uint64_t fee = expectedBlockReward / 2; + do_test(0, fee, true); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee - fee * testPenalty / 100, m_blockReward); + ASSERT_EQ(expectedBlockReward - fee * testPenalty / 100, m_emissionChange); + ASSERT_GT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_eq_block_reward_and_no_penalize_fee) { + uint64_t fee = expectedBlockReward; + do_test(0, fee, false); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee, m_blockReward); + ASSERT_EQ(expectedBlockReward, m_emissionChange); + ASSERT_GT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_eq_block_reward_and_penalize_fee) { + uint64_t fee = expectedBlockReward; + do_test(0, fee, true); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee - fee * testPenalty / 100, m_blockReward); + ASSERT_EQ(expectedBlockReward - fee * testPenalty / 100, m_emissionChange); + ASSERT_GT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_gt_block_reward_and_no_penalize_fee) { + uint64_t fee = 2 * expectedBlockReward; + do_test(0, fee, false); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee, m_blockReward); + ASSERT_EQ(expectedBlockReward, m_emissionChange); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_gt_block_reward_and_penalize_fee) { + uint64_t fee = 2 * expectedBlockReward; + do_test(0, fee, true); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee - fee * testPenalty / 100, m_blockReward); + ASSERT_EQ(expectedBlockReward - fee * testPenalty / 100, m_emissionChange); + ASSERT_LT(m_emissionChange, 0); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_emission_change_eq_zero) { + uint64_t fee = expectedBlockReward * 100 / testPenalty; + do_test(0, fee, true); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(expectedBlockReward + fee - fee * testPenalty / 100, m_blockReward); + ASSERT_EQ(0, m_emissionChange); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_if_block_reward_is_zero_and_no_penalize_fee) { + uint64_t fee = UINT64_C(100); + do_test(m_currency.moneySupply(), fee, false); + + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(fee, m_blockReward); + ASSERT_EQ(0, m_emissionChange); + } + + TEST_F(getBlockReward_fee_and_penalizeFee_test, handles_fee_if_block_reward_is_zero_and_penalize_fee) { + uint64_t fee = UINT64_C(100); + do_test(m_currency.moneySupply(), fee, true); - do_test(m_last_block_sizes_median * 15 / 8); - ASSERT_TRUE(m_block_not_too_big); - ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64); + ASSERT_FALSE(m_blockTooBig); + ASSERT_EQ(fee - fee * testPenalty / 100, m_blockReward); + ASSERT_EQ(-static_cast(fee * testPenalty / 100), m_emissionChange); } } diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index a2c345bfa9..28d0c5dab2 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -17,6 +17,9 @@ #include "gtest/gtest.h" +#include + +// epee #include using namespace epee::string_tools; diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index c6865d04b9..28ac4ab337 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -23,6 +23,10 @@ int main(int argc, char** argv) { epee::debug::get_set_enable_assert(true, false); + //set up logging options + epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index 6b90051174..e1516b612c 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -18,28 +18,33 @@ #include "gtest/gtest.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" using namespace cryptonote; namespace { + const size_t TEST_NUMBER_OF_DECIMAL_PLACES = 8; + void do_pos_test(uint64_t expected, const std::string& str) { + cryptonote::Currency currency = cryptonote::CurrencyBuilder().numberOfDecimalPlaces(TEST_NUMBER_OF_DECIMAL_PLACES).currency(); uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end()); - ASSERT_TRUE(parse_amount(val, number_str)); + ASSERT_TRUE(currency.parseAmount(number_str, val)); ASSERT_EQ(expected, val); } void do_neg_test(const std::string& str) { + cryptonote::Currency currency = cryptonote::CurrencyBuilder().numberOfDecimalPlaces(TEST_NUMBER_OF_DECIMAL_PLACES).currency(); uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end()); - ASSERT_FALSE(parse_amount(val, number_str)); + ASSERT_FALSE(currency.parseAmount(number_str, val)); } } diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 8f1f058050..bc239a8575 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -290,12 +290,12 @@ TEST(Serialization, serializes_transacion_signatures_correctly) { using namespace cryptonote; - transaction tx; - transaction tx1; + Transaction tx; + Transaction tx1; string blob; // Empty tx - tx.set_null(); + tx.clear(); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_EQ(5, blob.size()); // 5 bytes + 0 bytes extra + 0 bytes signatures ASSERT_TRUE(serialization::parse_binary(blob, tx1)); @@ -303,9 +303,9 @@ TEST(Serialization, serializes_transacion_signatures_correctly) ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); // Miner tx without signatures - txin_gen txin_gen1; + TransactionInputGenerate txin_gen1; txin_gen1.height = 0; - tx.set_null(); + tx.clear(); tx.vin.push_back(txin_gen1); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_EQ(7, blob.size()); // 5 bytes + 2 bytes vin[0] + 0 bytes extra + 0 bytes signatures @@ -336,7 +336,7 @@ TEST(Serialization, serializes_transacion_signatures_correctly) tx.signatures[1].resize(1); ASSERT_FALSE(serialization::dump_binary(tx, blob)); - // Two txin_gen, no signatures + // Two TransactionInputGenerate, no signatures tx.vin.push_back(txin_gen1); tx.signatures.resize(0); ASSERT_TRUE(serialization::dump_binary(tx, blob)); @@ -345,11 +345,11 @@ TEST(Serialization, serializes_transacion_signatures_correctly) ASSERT_EQ(tx, tx1); ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); - // Two txin_gen, signatures vector contains only one empty element + // Two TransactionInputGenerate, signatures vector contains only one empty element tx.signatures.resize(1); ASSERT_FALSE(serialization::dump_binary(tx, blob)); - // Two txin_gen, signatures vector contains two empty elements + // Two TransactionInputGenerate, signatures vector contains two empty elements tx.signatures.resize(2); ASSERT_TRUE(serialization::dump_binary(tx, blob)); ASSERT_EQ(9, blob.size()); // 5 bytes + 2 * 2 bytes vins + 0 bytes extra + 0 bytes signatures @@ -357,11 +357,11 @@ TEST(Serialization, serializes_transacion_signatures_correctly) ASSERT_EQ(tx, tx1); ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); - // Two txin_gen, signatures vector contains three empty elements + // Two TransactionInputGenerate, signatures vector contains three empty elements tx.signatures.resize(3); ASSERT_FALSE(serialization::dump_binary(tx, blob)); - // Two txin_gen, signatures vector contains two non empty elements + // Two TransactionInputGenerate, signatures vector contains two non empty elements tx.signatures.resize(2); tx.signatures[0].resize(1); tx.signatures[1].resize(1); @@ -380,8 +380,8 @@ TEST(Serialization, serializes_transacion_signatures_correctly) ASSERT_FALSE(serialization::parse_binary(blob, tx1)); // Not enough signature vectors for all inputs - txin_to_key txin_to_key1; - txin_to_key1.key_offsets.resize(2); + TransactionInputToKey txin_to_key1; + txin_to_key1.keyOffsets.resize(2); tx.vin.clear(); tx.vin.push_back(txin_to_key1); tx.vin.push_back(txin_to_key1); diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index 37c37ca471..f00e153021 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -19,8 +19,13 @@ #include +// epee +#include "misc_language.h" + #include "common/util.h" +#include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" TEST(parse_tx_extra, handles_empty_extra) @@ -118,25 +123,27 @@ TEST(parse_tx_extra, handles_pub_key_and_padding) TEST(parse_and_validate_tx_extra, is_valid_tx_extra_parsed) { - cryptonote::transaction tx = AUTO_VAL_INIT(tx); + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + cryptonote::Transaction tx = AUTO_VAL_INIT(tx); cryptonote::account_base acc; acc.generate(); cryptonote::blobdata b = "dsdsdfsdfsf"; - ASSERT_TRUE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, MINIMUM_FEE, acc.get_keys().m_account_address, tx, b, 1)); + ASSERT_TRUE(currency.constructMinerTx(0, 0, 10000000000000, 1000, currency.minimumFee(), acc.get_keys().m_account_address, tx, b, 1)); crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx); ASSERT_NE(tx_pub_key, cryptonote::null_pkey); } TEST(parse_and_validate_tx_extra, fails_on_big_extra_nonce) { - cryptonote::transaction tx = AUTO_VAL_INIT(tx); + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + cryptonote::Transaction tx = AUTO_VAL_INIT(tx); cryptonote::account_base acc; acc.generate(); cryptonote::blobdata b(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); - ASSERT_FALSE(cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, MINIMUM_FEE, acc.get_keys().m_account_address, tx, b, 1)); + ASSERT_FALSE(currency.constructMinerTx(0, 0, 10000000000000, 1000, currency.minimumFee(), acc.get_keys().m_account_address, tx, b, 1)); } TEST(parse_and_validate_tx_extra, fails_on_wrong_size_in_extra_nonce) { - cryptonote::transaction tx = AUTO_VAL_INIT(tx); + cryptonote::Transaction tx = AUTO_VAL_INIT(tx); tx.extra.resize(20, 0); tx.extra[0] = TX_EXTRA_NONCE; tx.extra[1] = 255; @@ -145,44 +152,45 @@ TEST(parse_and_validate_tx_extra, fails_on_wrong_size_in_extra_nonce) } TEST(validate_parse_amount_case, validate_parse_amount) { + cryptonote::Currency currency = cryptonote::CurrencyBuilder().numberOfDecimalPlaces(8).currency(); uint64_t res = 0; - bool r = cryptonote::parse_amount(res, "0.0001"); + bool r = currency.parseAmount("0.0001", res); ASSERT_TRUE(r); ASSERT_EQ(res, 10000); - r = cryptonote::parse_amount(res, "100.0001"); + r = currency.parseAmount("100.0001", res); ASSERT_TRUE(r); ASSERT_EQ(res, 10000010000); - r = cryptonote::parse_amount(res, "000.0000"); + r = currency.parseAmount("000.0000", res); ASSERT_TRUE(r); ASSERT_EQ(res, 0); - r = cryptonote::parse_amount(res, "0"); + r = currency.parseAmount("0", res); ASSERT_TRUE(r); ASSERT_EQ(res, 0); - r = cryptonote::parse_amount(res, " 100.0001 "); + r = currency.parseAmount(" 100.0001 ", res); ASSERT_TRUE(r); ASSERT_EQ(res, 10000010000); - r = cryptonote::parse_amount(res, " 100.0000 "); + r = currency.parseAmount(" 100.0000 ", res); ASSERT_TRUE(r); ASSERT_EQ(res, 10000000000); - r = cryptonote::parse_amount(res, " 100. 0000 "); + r = currency.parseAmount(" 100. 0000 ", res); ASSERT_FALSE(r); - r = cryptonote::parse_amount(res, "100. 0000"); + r = currency.parseAmount("100. 0000", res); ASSERT_FALSE(r); - r = cryptonote::parse_amount(res, "100 . 0000"); + r = currency.parseAmount("100 . 0000", res); ASSERT_FALSE(r); - r = cryptonote::parse_amount(res, "100.00 00"); + r = currency.parseAmount("100.00 00", res); ASSERT_FALSE(r); - r = cryptonote::parse_amount(res, "1 00.00 00"); + r = currency.parseAmount("1 00.00 00", res); ASSERT_FALSE(r); } diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index c5c7a9f21b..7ca59dcf73 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -15,6 +15,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include + #include "gtest/gtest.h" #include "include_base_utils.h" diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index 48efd03cec..f64f8315cc 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -24,6 +24,7 @@ #include "INode.h" #include "wallet/Wallet.h" #include "cryptonote_core/account.h" +#include "cryptonote_core/Currency.h" #include "INodeStubs.h" #include "TestBlockchainGenerator.h" @@ -115,6 +116,18 @@ class TrivialWalletObserver : public CryptoNote::IWalletObserver std::promise loadPromise; }; +struct SaveOnInitWalletObserver: public CryptoNote::IWalletObserver { + SaveOnInitWalletObserver(CryptoNote::Wallet* wallet) : wallet(wallet) {}; + virtual ~SaveOnInitWalletObserver() {} + + virtual void initCompleted(std::error_code result) { + wallet->save(stream, true, true); + } + + CryptoNote::Wallet* wallet; + std::stringstream stream; +}; + static const uint64_t TEST_BLOCK_REWARD = 70368744177663; CryptoNote::TransactionId TransferMoney(CryptoNote::Wallet& from, CryptoNote::Wallet& to, int64_t amount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "") { @@ -152,6 +165,9 @@ void WaitWalletSave(TrivialWalletObserver* observer) { class WalletApi : public ::testing::Test { public: + WalletApi() : m_currency(cryptonote::CurrencyBuilder().currency()), generator(m_currency) { + } + void SetUp(); protected: @@ -164,6 +180,8 @@ class WalletApi : public ::testing::Test void TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = ""); void performTransferWithErrorTx(const std::array& amounts, uint64_t fee); + cryptonote::Currency m_currency; + TestBlockchainGenerator generator; std::shared_ptr aliceWalletObserver; @@ -188,7 +206,7 @@ void WalletApi::prepareAliceWallet() { aliceNode.reset(new INodeTrivialRefreshStub(generator)); aliceWalletObserver.reset(new TrivialWalletObserver()); - alice.reset(new CryptoNote::Wallet(*aliceNode)); + alice.reset(new CryptoNote::Wallet(m_currency, *aliceNode)); alice->addObserver(aliceWalletObserver.get()); } @@ -196,7 +214,7 @@ void WalletApi::prepareBobWallet() { bobNode.reset(new INodeTrivialRefreshStub(generator)); bobWalletObserver.reset(new TrivialWalletObserver()); - bob.reset(new CryptoNote::Wallet(*bobNode)); + bob.reset(new CryptoNote::Wallet(m_currency, *bobNode)); bob->addObserver(bobWalletObserver.get()); } @@ -204,13 +222,13 @@ void WalletApi::prepareCarolWallet() { carolNode.reset(new INodeTrivialRefreshStub(generator)); carolWalletObserver.reset(new TrivialWalletObserver()); - carol.reset(new CryptoNote::Wallet(*carolNode)); + carol.reset(new CryptoNote::Wallet(m_currency, *carolNode)); carol->addObserver(carolWalletObserver.get()); } void WalletApi::GetOneBlockReward(CryptoNote::Wallet& wallet) { - cryptonote::account_public_address address; - ASSERT_TRUE(cryptonote::get_account_address_from_str(address, wallet.getAddress())); + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(wallet.getAddress(), address)); generator.getBlockRewardForAddress(address); } @@ -301,6 +319,14 @@ void WaitWalletLoad(TrivialWalletObserver* observer, std::error_code& ec) { ASSERT_TRUE(observer->waitForLoadEnd(ec)); } +TEST_F(WalletApi, initAndSave) { + SaveOnInitWalletObserver saveOnInit(alice.get()); + alice->addObserver(&saveOnInit); + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + alice->shutdown(); +} + TEST_F(WalletApi, refreshWithMoney) { alice->initAndGenerate("pass"); @@ -309,8 +335,8 @@ TEST_F(WalletApi, refreshWithMoney) { ASSERT_EQ(alice->actualBalance(), 0); ASSERT_EQ(alice->pendingBalance(), 0); - cryptonote::account_public_address address; - ASSERT_TRUE(cryptonote::get_account_address_from_str(address, alice->getAddress())); + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getBlockRewardForAddress(address); alice->startRefresh(); @@ -323,6 +349,34 @@ TEST_F(WalletApi, refreshWithMoney) { alice->shutdown(); } +TEST_F(WalletApi, initWithMoney) { + std::stringstream archive; + + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + alice->save(archive, true, true); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + ASSERT_EQ(alice->actualBalance(), 0); + ASSERT_EQ(alice->pendingBalance(), 0); + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + + alice->shutdown(); + + generator.getBlockRewardForAddress(address); + + prepareAliceWallet(); + alice->initAndLoad(archive, "pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(alice->actualBalance(), 0); + EXPECT_EQ(alice->pendingBalance(), TEST_BLOCK_REWARD); + + alice->shutdown(); +} + TEST_F(WalletApi, TransactionsAndTransfersAfterSend) { prepareBobWallet(); prepareCarolWallet(); @@ -365,7 +419,7 @@ TEST_F(WalletApi, TransactionsAndTransfersAfterSend) { EXPECT_EQ(alice->getTransactionCount(), 5); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; //Transaction with id = 0 is tested in getTransactionSuccess ASSERT_TRUE(alice->getTransaction(1, tx)); @@ -482,7 +536,7 @@ TEST_F(WalletApi, saveAndLoadCacheDetails) { ASSERT_EQ(alice->getTransactionCount(), 3); ASSERT_EQ(alice->getTransferCount(), 3); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; ASSERT_TRUE(alice->getTransaction(1, tx)); EXPECT_EQ(tx.totalAmount, amount1 + amount2 + fee); EXPECT_EQ(tx.fee, fee); @@ -533,7 +587,7 @@ TEST_F(WalletApi, getTransactionSuccess) { alice->startRefresh(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; ASSERT_EQ(alice->getTransactionCount(), 1); ASSERT_TRUE(alice->getTransaction(0, tx)); @@ -552,7 +606,7 @@ TEST_F(WalletApi, getTransactionFailure) { ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; ASSERT_EQ(alice->getTransactionCount(), 0); ASSERT_FALSE(alice->getTransaction(0, tx)); @@ -572,7 +626,7 @@ TEST_F(WalletApi, useNotInitializedObject) { EXPECT_THROW(alice->findTransactionByTransferId(1), std::system_error); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; CryptoNote::Transfer tr; EXPECT_THROW(alice->getTransaction(1, tx), std::system_error); EXPECT_THROW(alice->getTransfer(2, tr), std::system_error); @@ -685,7 +739,7 @@ TEST_F(WalletApi, saveAndLoadErroneousTxsCacheDetails) { EXPECT_EQ(alice->getTransactionCount(), 2); EXPECT_EQ(alice->getTransferCount(), 2); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; ASSERT_TRUE(alice->getTransaction(1, tx)); EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee); EXPECT_EQ(tx.firstTransferId, 0); @@ -747,7 +801,7 @@ TEST_F(WalletApi, saveAndLoadErroneousTxsCacheNoDetails) { EXPECT_EQ(alice->getTransactionCount(), 2); EXPECT_EQ(alice->getTransferCount(), 0); - CryptoNote::Transaction tx; + CryptoNote::TransactionInfo tx; ASSERT_TRUE(alice->getTransaction(1, tx)); EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee); EXPECT_EQ(tx.firstTransferId, CryptoNote::INVALID_TRANSFER_ID); diff --git a/tests/unit_tests/tx_pool.cpp b/tests/unit_tests/tx_pool.cpp new file mode 100644 index 0000000000..d506492d6a --- /dev/null +++ b/tests/unit_tests/tx_pool.cpp @@ -0,0 +1,397 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include + +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/tx_pool.h" + +using namespace cryptonote; +using namespace CryptoNote; + +class TransactionValidator : public CryptoNote::ITransactionValidator { + virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) { + return true; + } + + virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { + return true; + } + + virtual bool haveSpentKeyImages(const cryptonote::Transaction& tx) { + return false; + } +}; + +class FakeTimeProvider : public ITimeProvider { +public: + FakeTimeProvider(time_t currentTime = time(nullptr)) + : timeNow(currentTime) {} + + time_t timeNow; + virtual time_t now() { return timeNow; } +}; + + +class TestTransactionGenerator { + +public: + + TestTransactionGenerator(const cryptonote::Currency& currency, size_t ringSize) : + m_currency(currency), + m_ringSize(ringSize), + m_miners(ringSize), + m_miner_txs(ringSize), + m_public_keys(ringSize), + m_public_key_ptrs(ringSize) + { + rv_acc.generate(); + } + + bool createSources() { + + size_t real_source_idx = m_ringSize / 2; + + std::vector output_entries; + for (size_t i = 0; i < m_ringSize; ++i) + { + m_miners[i].generate(); + + if (!m_currency.constructMinerTx(0, 0, 0, 2, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i])) { + return false; + } + + TransactionOutputToKey tx_out = boost::get(m_miner_txs[i].vout[0].target); + output_entries.push_back(std::make_pair(i, tx_out.key)); + m_public_keys[i] = tx_out.key; + m_public_key_ptrs[i] = &m_public_keys[i]; + } + + m_source_amount = m_miner_txs[0].vout[0].amount; + + tx_source_entry source_entry; + source_entry.amount = m_source_amount; + source_entry.real_out_tx_key = get_tx_pub_key_from_extra(m_miner_txs[real_source_idx]); + source_entry.real_output_in_tx_index = 0; + source_entry.outputs.swap(output_entries); + source_entry.real_output = real_source_idx; + + m_sources.push_back(source_entry); + + m_realSenderKeys = m_miners[real_source_idx].get_keys(); + + return true; + } + + void construct(uint64_t amount, uint64_t fee, size_t outputs, Transaction& tx) { + + std::vector destinations; + uint64_t amountPerOut = (amount - fee) / outputs; + + for (size_t i = 0; i < outputs; ++i) { + destinations.push_back(tx_destination_entry(amountPerOut, rv_acc.get_keys().m_account_address)); + } + + construct_tx(m_realSenderKeys, m_sources, destinations, std::vector(), tx, 0); + } + + std::vector m_miners; + std::vector m_miner_txs; + std::vector m_sources; + std::vector m_public_keys; + std::vector m_public_key_ptrs; + + const cryptonote::Currency& m_currency; + const size_t m_ringSize; + account_keys m_realSenderKeys; + uint64_t m_source_amount; + account_base rv_acc; +}; + + + +namespace +{ + static const size_t textMaxCumulativeSize = std::numeric_limits::max(); + + void GenerateTransaction(const cryptonote::Currency& currency, Transaction& tx, uint64_t fee, size_t outputs) { + TestTransactionGenerator txGenerator(currency, 1); + txGenerator.createSources(); + txGenerator.construct(txGenerator.m_source_amount, fee, outputs, tx); + } + + template + class TestPool : public tx_memory_pool { + public: + + Validator validator; + TimeProvider timeProvider; + + TestPool(const cryptonote::Currency& m_currency) : + tx_memory_pool(m_currency, validator, timeProvider) {} + }; + + class TxTestBase { + public: + TxTestBase(size_t ringSize) : + m_currency(cryptonote::CurrencyBuilder().currency()), + txGenerator(m_currency, ringSize), + pool(m_currency, validator, m_time) + { + txGenerator.createSources(); + } + + void construct(uint64_t fee, size_t outputs, Transaction& tx) { + txGenerator.construct(txGenerator.m_source_amount, fee, outputs, tx); + } + + cryptonote::Currency m_currency; + CryptoNote::RealTimeProvider m_time; + TestTransactionGenerator txGenerator; + TransactionValidator validator; + tx_memory_pool pool; + }; + + void InitBlock(Block& bl, uint8_t majorVersion = BLOCK_MAJOR_VERSION_1) { + bl.majorVersion = majorVersion; + bl.minorVersion = 0; + bl.nonce = 0; + bl.timestamp = time(0); + bl.prevId = null_hash; + } + +} + +TEST(tx_pool, add_one_tx) +{ + TxTestBase test(1); + Transaction tx; + + test.construct(test.m_currency.minimumFee(), 1, tx); + + tx_verification_context tvc = boost::value_initialized(); + + ASSERT_TRUE(test.pool.add_tx(tx, tvc, false)); + ASSERT_FALSE(tvc.m_verifivation_failed); +}; + +TEST(tx_pool, take_tx) +{ + TxTestBase test(1); + Transaction tx; + + test.construct(test.m_currency.minimumFee(), 1, tx); + + auto txhash = get_transaction_hash(tx); + + tx_verification_context tvc = boost::value_initialized(); + + ASSERT_TRUE(test.pool.add_tx(tx, tvc, false)); + ASSERT_FALSE(tvc.m_verifivation_failed); + + Transaction txOut; + size_t blobSize; + uint64_t fee = 0; + + ASSERT_TRUE(test.pool.take_tx(txhash, txOut, blobSize, fee)); + ASSERT_EQ(fee, test.m_currency.minimumFee()); + ASSERT_EQ(tx, txOut); +}; + + +TEST(tx_pool, double_spend_tx) +{ + TxTestBase test(1); + Transaction tx, tx_double; + + test.construct(test.m_currency.minimumFee(), 1, tx); + + tx_verification_context tvc = boost::value_initialized(); + + ASSERT_TRUE(test.pool.add_tx(tx, tvc, false)); + ASSERT_FALSE(tvc.m_verifivation_failed); + + test.txGenerator.rv_acc.generate(); // generate new receiver address + test.construct(test.m_currency.minimumFee(), 1, tx_double); + + ASSERT_FALSE(test.pool.add_tx(tx_double, tvc, false)); + ASSERT_TRUE(tvc.m_verifivation_failed); +} + + +TEST(tx_pool, fillblock_same_fee) +{ + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + TestPool pool(currency); + uint64_t fee = currency.minimumFee(); + + std::unordered_map> transactions; + + // generate transactions + for (int i = 1; i <= 50; ++i) { + TestTransactionGenerator txGenerator(currency, 1); + txGenerator.createSources(); + + std::unique_ptr txptr(new Transaction); + Transaction& tx = *txptr; + + txGenerator.construct(txGenerator.m_source_amount, fee, i, tx); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + transactions[get_transaction_hash(tx)] = std::move(txptr); + } + + Block bl; + + InitBlock(bl); + + size_t totalSize = 0; + uint64_t txFee = 0; + uint64_t median = 5000; + + ASSERT_TRUE(pool.fill_block_template(bl, median, textMaxCumulativeSize, 0, totalSize, txFee)); + ASSERT_TRUE(totalSize*100 < median*125); + + // now, check that the block is opimally filled + // if fee is fixed, transactions with smaller number of outputs should be included + + size_t maxOuts = 0; + + for (auto& th : bl.txHashes) { + auto iter = transactions.find(th); + ASSERT_TRUE(iter != transactions.end()); + + size_t txouts = iter->second->vout.size(); + + if (txouts > maxOuts) + maxOuts = txouts; + } + + ASSERT_TRUE(maxOuts <= bl.txHashes.size()); +} + + +TEST(tx_pool, fillblock_same_size) +{ + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + TestPool pool(currency); + + const uint64_t fee = currency.minimumFee(); + const size_t totalTransactions = 50; + + std::unordered_map> transactions; + + + // generate transactions + for (int i = 0; i <= totalTransactions; ++i) { + + TestTransactionGenerator txGenerator(currency, 1); + txGenerator.createSources(); + + std::unique_ptr txptr(new Transaction); + Transaction& tx = *txptr; + + // interleave fee and fee*2 + txGenerator.construct(txGenerator.m_source_amount, fee + (fee * (i&1)), 1, tx); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + transactions[get_transaction_hash(tx)] = std::move(txptr); + } + + + Block bl; + + InitBlock(bl); + + size_t totalSize = 0; + uint64_t txFee = 0; + uint64_t median = 5000; + + ASSERT_TRUE(pool.fill_block_template(bl, median, textMaxCumulativeSize, 0, totalSize, txFee)); + ASSERT_TRUE(totalSize * 100 < median * 125); + + // check that fill_block_template prefers transactions with double fee + + size_t doubleFee = 0; + + for (auto& th : bl.txHashes) { + + auto iter = transactions.find(th); + ASSERT_TRUE(iter != transactions.end()); + + if (get_tx_fee(*iter->second) > fee) + ++doubleFee; + } + + ASSERT_TRUE(doubleFee == std::min(bl.txHashes.size(), totalTransactions / 2)); + +} + + +TEST(tx_pool, cleanup_stale_tx) +{ + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + TestPool pool(currency); + const uint64_t fee = currency.minimumFee(); + + time_t startTime = pool.timeProvider.now(); + + for (int i = 0; i < 3; ++i) { + Transaction tx; + GenerateTransaction(currency, tx, fee, 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain + ASSERT_TRUE(tvc.m_added_to_pool); + + pool.timeProvider.timeNow += 60 * 60 * 2; // add 2 hours + } + + for (int i = 0; i < 5; ++i) { + Transaction tx; + GenerateTransaction(currency, tx, fee, 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, true)); // alternative chain + ASSERT_TRUE(tvc.m_added_to_pool); + + pool.timeProvider.timeNow += 60 * 60 * 2; // add 2 hours + } + + + ASSERT_EQ(8, pool.get_transactions_count()); + + pool.timeProvider.timeNow = startTime + currency.mempoolTxLiveTime() + 3*60*60; + pool.on_idle(); // 2 transactions should be removed + + ASSERT_EQ(6, pool.get_transactions_count()); + + pool.timeProvider.timeNow = startTime + currency.mempoolTxFromAltBlockLiveTime() + (3*2+3) * 60 * 60; + pool.on_idle(); // all transactions from main chain and 2 transactions from altchain should be removed + + ASSERT_EQ(3, pool.get_transactions_count()); +} From c3b1a00085c674e91af25f4c3659b3c15529233e Mon Sep 17 00:00:00 2001 From: jezal Date: Thu, 14 Aug 2014 16:41:44 +0100 Subject: [PATCH 13/59] Fix transfers in simplewallet --- LICENSE | 165 +++++++++++++++++++++ ReleaseNotes.txt | 4 + src/common/BlockingQueue.h | 1 + src/cryptonote_config.h | 3 +- src/cryptonote_core/Currency.cpp | 5 +- src/cryptonote_core/blockchain_storage.cpp | 17 ++- src/simplewallet/simplewallet.cpp | 1 - src/version.h.in | 4 +- src/wallet/WalletTransactionSender.cpp | 2 +- src/wallet/wallet2.cpp | 121 ++++++++------- src/wallet/wallet2.h | 8 +- tests/TestGenerator/TestGenerator.cpp | 5 +- tests/core_tests/chaingen_main.cpp | 4 +- tests/core_tests/upgrade.cpp | 6 +- 14 files changed, 272 insertions(+), 74 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index c97cbba0a3..7d3de6eb5b 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,7 @@ +Release notes 1.0.1 + +- Fix transfers in simplewallet + Release notes 1.0.0 - Multi-signatures diff --git a/src/common/BlockingQueue.h b/src/common/BlockingQueue.h index bdae78ca4f..97f55b3776 100644 --- a/src/common/BlockingQueue.h +++ b/src/common/BlockingQueue.h @@ -76,6 +76,7 @@ class BlockingQueue { std::unique_lock lk(m_mutex); m_closed = true; m_haveData.notify_all(); // wake up threads in pop() + m_haveSpace.notify_all(); if (wait) { while (!m_queue.empty()) { diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 1b6292feea..55d9103eaf 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -37,7 +37,8 @@ const unsigned EMISSION_SPEED_FACTOR = 18; static_assert(EMISSION_SPEED_FACTOR <= 8 * sizeof(uint64_t), "Bad EMISSION_SPEED_FACTOR"); const size_t CRYPTONOTE_REWARD_BLOCKS_WINDOW = 100; -const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = 10000; //size of block (bytes) after which reward for block calculated using block size +const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = 20000; //size of block (bytes) after which reward for block calculated using block size +const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 = 10000; const size_t CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE = 600; const size_t CRYPTONOTE_DISPLAY_DECIMAL_POINT = 8; // COIN - number of smallest units in one coin diff --git a/src/cryptonote_core/Currency.cpp b/src/cryptonote_core/Currency.cpp index aac4338b50..1c14c243c3 100644 --- a/src/cryptonote_core/Currency.cpp +++ b/src/cryptonote_core/Currency.cpp @@ -84,7 +84,10 @@ namespace cryptonote { uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor; - medianSize = std::max(medianSize, m_blockGrantedFullRewardZone); + size_t blockGrantedFullRewardZone = penalizeFee ? + m_blockGrantedFullRewardZone : + cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + medianSize = std::max(medianSize, blockGrantedFullRewardZone); if (currentBlockSize > UINT64_C(2) * medianSize) { LOG_PRINT_L4("Block cumulative size is too big: " << currentBlockSize << ", expected less than " << 2 * medianSize); return false; diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 9b578959f7..18c74f2956 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -271,7 +271,7 @@ BOOST_CLASS_VERSION(cryptonote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool): m_currency(currency), m_tx_pool(tx_pool), - m_current_block_cumul_sz_limit(currency.blockGrantedFullRewardZone() * 2), + m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), m_is_blockchain_storing(false), m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2) { @@ -421,6 +421,8 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi return false; } + update_next_comulative_size_limit(); + uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; if (!m_blocks.back().bl.timestamp) { timestamp_diff = time(NULL) - 1341378000; @@ -1575,13 +1577,20 @@ bool blockchain_storage::getBlockCumulativeSize(const Block& block, size_t& cumu return missedTxs.empty(); } +// Precondition: m_blockchain_lock is locked. bool blockchain_storage::update_next_comulative_size_limit() { + uint8_t nextBlockMajorVersion = get_block_major_version_for_height(m_blocks.size()); + size_t nextBlockGrantedFullRewardZone = nextBlockMajorVersion == BLOCK_MAJOR_VERSION_1 ? + parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : + m_currency.blockGrantedFullRewardZone(); + std::vector sz; get_last_n_blocks_sizes(sz, m_currency.rewardBlocksWindow()); uint64_t median = epee::misc_utils::median(sz); - if (median <= m_currency.blockGrantedFullRewardZone()) - median = m_currency.blockGrantedFullRewardZone(); + if (median <= nextBlockGrantedFullRewardZone) { + median = nextBlockGrantedFullRewardZone; + } m_current_block_cumul_sz_limit = median * 2; return true; @@ -1752,7 +1761,6 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co } pushBlock(block); - update_next_comulative_size_limit(); TIME_MEASURE_FINISH(block_processing_time); LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash << ENDL << "PoW:\t" << proof_of_work @@ -1764,6 +1772,7 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co bvc.m_added_to_main_chain = true; m_upgradeDetector.blockPushed(); + update_next_comulative_size_limit(); return true; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 51f93450ba..8f5123852e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1097,7 +1097,6 @@ int main(int argc, char* argv[]) tools::SignalHandler::install([&wrpc, &wal] { wrpc.send_stop_signal(); - wal.store(); }); LOG_PRINT_L0("Starting wallet rpc server"); wrpc.run(); diff --git a/src/version.h.in b/src/version.h.in index 073f9e1708..d04bb6276f 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.0" -#define PROJECT_VERSION_BUILD_NO "312" +#define PROJECT_VERSION "1.0.1" +#define PROJECT_VERSION_BUILD_NO "316" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 31fc187277..54b55e67e3 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -74,7 +74,7 @@ WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& cur m_unconfirmedTransactions(unconfirmedTransactions), m_isInitialized(false), m_isStoping(false) { - m_upperTransactionSizeLimit = (m_currency.blockGrantedFullRewardZone() * 125) / 100 - m_currency.minerTxBlobReservedSize(); + m_upperTransactionSizeLimit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize(); } void WalletTransactionSender::init(cryptonote::account_keys keys) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f7dc057816..6792bf2389 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -131,7 +131,9 @@ void wallet2::processCheckedTransaction(const TxItem& item) { THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(tx.vout[o].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - m_key_images[td.m_key_image] = m_transfers.size() - 1; + auto insertResult = m_key_images.insert(std::make_pair(td.m_key_image, m_transfers.size() - 1)); + THROW_WALLET_EXCEPTION_IF(!insertResult.second, error::wallet_internal_error, "Key image already exists"); + LOG_PRINT_L0("Received money: " << m_currency.formatAmount(td.amount()) << ", with tx: " << get_transaction_hash(tx)); if (0 != m_callback) { m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); @@ -286,15 +288,18 @@ void wallet2::get_short_chain_history(std::list& ids) const } //---------------------------------------------------------------------------------------------------- -size_t wallet2::updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res) -{ +size_t wallet2::updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, std::unordered_set& newBlocks) { size_t blocks_added = 0; size_t current_index = res.start_height; // update local blockchain for (const auto& item : res.items) { - if (addNewBlockchainEntry(item.block_id, res.start_height, current_index)) + if (addNewBlockchainEntry(item.block_id, res.start_height, current_index)) { + if (!item.block.empty()) { + newBlocks.insert(item.block_id); + } ++blocks_added; + } ++current_index; } @@ -302,7 +307,7 @@ size_t wallet2::updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::res } //---------------------------------------------------------------------------------------------------- -void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res) +void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, const std::unordered_set& newBlocks) { size_t checkingThreads = std::thread::hardware_concurrency(); @@ -315,13 +320,14 @@ void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::re TxQueue checkedQueue(checkingThreads * 2); std::atomic inputTx(0); - std::atomic checkedTx(0); futures.push_back(std::async(std::launch::async, [&] { try { size_t current_index = res.start_height; for (const auto& item : res.items) { - inputTx += processNewBlockchainEntry(incomingQueue, item, item.block_id, current_index); + if (newBlocks.count(item.block_id)) { + inputTx += processNewBlockchainEntry(incomingQueue, item, item.block_id, current_index); + } ++current_index; } incomingQueue.close(); @@ -338,7 +344,6 @@ void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::re futures.push_back(std::async(std::launch::async, [&] { TxQueueItem item; while (incomingQueue.pop(item)) { - ++checkedTx; lookup_acc_outs(m_account.get_keys(), item->tx, item->txPubKey, item->outs, item->txMoneyGotInOuts); checkedQueue.push(std::move(item)); } @@ -348,10 +353,18 @@ void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::re size_t txCount = 0; - TxQueueItem item; - while (checkedQueue.pop(item)) { - processCheckedTransaction(*item); - ++txCount; + try { + TxQueueItem item; + while (checkedQueue.pop(item)) { + processCheckedTransaction(*item); + ++txCount; + } + } catch (...) { + checkedQueue.close(); + for (auto& f : futures) { + f.wait(); + } + throw; } for (auto& f : futures) { @@ -404,12 +417,9 @@ void wallet2::refresh(size_t & blocks_fetched, bool& received_money) { received_money = false; blocks_fetched = 0; - size_t added_blocks = 0; size_t try_count = 0; crypto::hash last_tx_hash_id = m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash; - std::future processingTask; - epee::net_utils::http::http_simple_client queryClient; auto r = connectClient(queryClient); @@ -417,49 +427,46 @@ void wallet2::refresh(size_t & blocks_fetched, bool& received_money) auto startTime = std::chrono::high_resolution_clock::now(); - while(m_run.load(std::memory_order_relaxed)) - { - try - { - auto res = std::make_shared(queryBlocks(queryClient)); - - if (processingTask.valid()) - processingTask.get(); // sync with transaction processing - - added_blocks = updateBlockchain(*res); - - if (!added_blocks) { - break; - } + cryptonote::COMMAND_RPC_QUERY_BLOCKS::response res; + std::unordered_set newBlocks; + + size_t lastHeight = m_blockchain.size(); + size_t added_blocks = 0; - blocks_fetched += added_blocks; + while (m_run.load(std::memory_order_relaxed)) { + try { + std::future processingTask; + if (!newBlocks.empty()) { + processingTask = std::async(std::launch::async, [&res, &newBlocks, this] { processTransactions(res, newBlocks); }); + } - bool hasFullBlocks = std::any_of(res->items.begin(), res->items.end(), - [](const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response_item& ri) { return !ri.block.empty(); }); + cryptonote::COMMAND_RPC_QUERY_BLOCKS::response tempRes = queryBlocks(queryClient); + if (!newBlocks.empty()) { + processingTask.get(); + lastHeight = m_blockchain.size(); + newBlocks.clear(); + } - if (hasFullBlocks) { - processingTask = std::async(std::launch::async, [res, this] { processTransactions(*res); }); + added_blocks = updateBlockchain(tempRes, newBlocks); + if (added_blocks == 0) { + break; } - } - catch (const std::exception&) - { + + res = std::move(tempRes); blocks_fetched += added_blocks; - if(try_count < 3) - { + } catch (const std::exception&) { + newBlocks.clear(); + blocks_fetched -= detach_blockchain(lastHeight); + if (try_count < 3) { LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); ++try_count; - } - else - { + } else { LOG_ERROR("pull_blocks failed, try_count=" << try_count); throw; } } } - if (processingTask.valid()) - processingTask.get(); - auto duration = std::chrono::high_resolution_clock::now() - startTime; if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash)) @@ -485,22 +492,23 @@ bool wallet2::refresh(size_t & blocks_fetched, bool& received_money, bool& ok) return ok; } //---------------------------------------------------------------------------------------------------- -void wallet2::detach_blockchain(uint64_t height) +size_t wallet2::detach_blockchain(uint64_t height) { LOG_PRINT_L0("Detaching blockchain on height " << height); size_t transfers_detached = 0; - auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const transfer_details& td){return td.m_block_height >= height;}); - size_t i_start = it - m_transfers.begin(); - - for(size_t i = i_start; i!= m_transfers.size();i++) - { - auto it_ki = m_key_images.find(m_transfers[i].m_key_image); - THROW_WALLET_EXCEPTION_IF(it_ki == m_key_images.end(), error::wallet_internal_error, "key image not found"); - m_key_images.erase(it_ki); - ++transfers_detached; + // do not rely on ordering by height in transfers + for (auto it = m_transfers.begin(); it != m_transfers.end();) { + if (it->m_block_height >= height) { + auto it_ki = m_key_images.find(it->m_key_image); + THROW_WALLET_EXCEPTION_IF(it_ki == m_key_images.end(), error::wallet_internal_error, "key image not found"); + m_key_images.erase(it_ki); + it = m_transfers.erase(it); + ++transfers_detached; + } else { + ++it; + } } - m_transfers.erase(it, m_transfers.end()); size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height); m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); @@ -515,6 +523,7 @@ void wallet2::detach_blockchain(uint64_t height) } LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); + return blocks_detached; } //---------------------------------------------------------------------------------------------------- bool wallet2::deinit() diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index dd911b3b4c..b7d452baff 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -77,7 +77,7 @@ namespace tools public: wallet2(const cryptonote::Currency& currency) : m_currency(currency), m_run(true), m_callback(0) { - m_upper_transaction_size_limit = (m_currency.blockGrantedFullRewardZone() * 125) / 100 - m_currency.minerTxBlobReservedSize(); + m_upper_transaction_size_limit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize(); } struct transfer_details @@ -200,11 +200,11 @@ namespace tools void processCheckedTransaction(const TxItem& item); // returns number of blocks added - size_t updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res); - void processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res); + size_t updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, std::unordered_set& newBlocks); + void processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, const std::unordered_set& newBlocks); cryptonote::COMMAND_RPC_QUERY_BLOCKS::response queryBlocks(epee::net_utils::http::http_simple_client& client); - void detach_blockchain(uint64_t height); + size_t detach_blockchain(uint64_t height); void get_short_chain_history(std::list& ids) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; bool is_transfer_unlocked(const transfer_details& td) const; diff --git a/tests/TestGenerator/TestGenerator.cpp b/tests/TestGenerator/TestGenerator.cpp index 2fd6818ef7..87711a2234 100644 --- a/tests/TestGenerator/TestGenerator.cpp +++ b/tests/TestGenerator/TestGenerator.cpp @@ -258,7 +258,10 @@ bool test_generator::constructMaxSizeBlock(cryptonote::Block& blk, const crypton getLastNBlockSizes(blockSizes, get_block_hash(blkPrev), medianBlockCount); size_t median = misc_utils::median(blockSizes); - median = std::max(median, m_currency.blockGrantedFullRewardZone()); + size_t blockGrantedFullRewardZone = defaultMajorVersion <= BLOCK_MAJOR_VERSION_1 ? + cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : + m_currency.blockGrantedFullRewardZone(); + median = std::max(median, blockGrantedFullRewardZone); uint64_t totalFee = 0; size_t txsSize = 0; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 134f4ece06..4c749f1dcd 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -163,8 +163,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 1, true)); GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(2, 2, true)); GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(3, 2, true)); - GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(0, 0, false)); - GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 0, false)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(0, 0, true)); + GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 0, true)); GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(0, 1, false)); GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 2, false)); GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(2, 3, false)); diff --git a/tests/core_tests/upgrade.cpp b/tests/core_tests/upgrade.cpp index 3ccb33d1e1..0d4196c33f 100644 --- a/tests/core_tests/upgrade.cpp +++ b/tests/core_tests/upgrade.cpp @@ -44,7 +44,11 @@ namespace { } gen_upgrade::gen_upgrade() : m_invalidBlockIndex(0), m_checkBlockTemplateVersionCallCounter(0), - m_coinsInCirculationBeforeUpgrade(0), m_coinsInCirculationAfterUpgrade(0) { + m_coinsInCirculationBeforeUpgrade(0), m_coinsInCirculationAfterUpgrade(0) { + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); + m_currency = currencyBuilder.currency(); + REGISTER_CALLBACK_METHOD(gen_upgrade, markInvalidBlock); REGISTER_CALLBACK_METHOD(gen_upgrade, checkBlockTemplateVersionIsV1); REGISTER_CALLBACK_METHOD(gen_upgrade, checkBlockTemplateVersionIsV2); From 9df3a81801af2a8d02add6ef27b5af73df71ef16 Mon Sep 17 00:00:00 2001 From: jezal Date: Mon, 25 Aug 2014 15:35:07 +0100 Subject: [PATCH 14/59] Transaction history and 'reset' command for simplewallet --- ReleaseNotes.txt | 6 ++ src/cryptonote_config.h | 6 +- src/cryptonote_core/Currency.cpp | 3 +- src/cryptonote_core/blockchain_storage.cpp | 25 ++++- src/cryptonote_core/cryptonote_core.cpp | 2 +- .../cryptonote_protocol_handler.inl | 10 +- src/simplewallet/simplewallet.cpp | 59 ++++++++-- src/simplewallet/simplewallet.h | 4 +- src/version.h.in | 4 +- src/wallet/WalletTransactionSender.cpp | 2 +- src/wallet/wallet2.cpp | 101 ++++++++++++++---- src/wallet/wallet2.h | 86 ++++++++++++--- src/wallet/wallet_rpc_server.cpp | 42 +++++++- src/wallet/wallet_rpc_server.h | 14 ++- src/wallet/wallet_rpc_server_commans_defs.h | 66 ++++++++++++ tests/core_tests/block_validation.cpp | 31 +----- tests/core_tests/block_validation.h | 7 +- 17 files changed, 373 insertions(+), 95 deletions(-) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 7d3de6eb5b..cc0f8a41d3 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,9 @@ +Release notes 1.0.2 + +- Transaction history for simplewallet +- Reset command for simplewallet +- Various simplewallet improvements + Release notes 1.0.1 - Fix transfers in simplewallet diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 55d9103eaf..fc52139f41 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -63,6 +63,7 @@ const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS = DIFFICULTY_TARGET const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24; //seconds, one day const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week +const uint64_t UPGRADE_HEIGHT = 546602; const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks @@ -104,7 +105,7 @@ const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds const uint64_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds -const char P2P_STAT_TRUSTED_PUB_KEY[] = "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115"; +const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4"; const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; const unsigned THREAD_STACK_SIZE = 5 * 1024 * 1024; @@ -143,7 +144,8 @@ const CheckpointData CHECKPOINTS[] = { {480200, "363544ac9920c778b815c2fdbcbca70a0d79b21f662913a42da9b49e859f0e5b"}, {484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"}, {506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"}, - {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"} + {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"}, + {553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"} }; } // cryptonote diff --git a/src/cryptonote_core/Currency.cpp b/src/cryptonote_core/Currency.cpp index 1c14c243c3..fd7a95ae91 100644 --- a/src/cryptonote_core/Currency.cpp +++ b/src/cryptonote_core/Currency.cpp @@ -40,6 +40,7 @@ namespace cryptonote { CHECK_AND_ASSERT_MES(r, false, "Failed to get genesis block hash"); if (isTestnet()) { + m_upgradeHeight = 0; m_blocksFileName = "testnet_" + m_blocksFileName; m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; @@ -380,7 +381,7 @@ namespace cryptonote { mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME); mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); - upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT); + upgradeHeight(parameters::UPGRADE_HEIGHT); upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); upgradeWindow(parameters::UPGRADE_WINDOW); diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 18c74f2956..4dfd8733c9 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -231,8 +231,9 @@ namespace cryptonote if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) return; + std::string operation; if (Archive::is_loading::value) { - + operation = "- loading "; crypto::hash blockHash; ar & blockHash; @@ -241,13 +242,23 @@ namespace cryptonote } } else { + operation = "- saving "; ar & m_lastBlockHash; } + LOG_PRINT_L0(operation << "block index..."); ar & m_bs.m_blockIndex; + + LOG_PRINT_L0(operation << "transaction map..."); ar & m_bs.m_transactionMap; + + LOG_PRINT_L0(operation << "spend keys..."); ar & m_bs.m_spent_keys; + + LOG_PRINT_L0(operation << "outputs..."); ar & m_bs.m_outputs; + + LOG_PRINT_L0(operation << "multi-signature outputs..."); ar & m_bs.m_multisignatureOutputs; m_loaded = true; @@ -377,6 +388,9 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi m_outputs.clear(); m_multisignatureOutputs.clear(); for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + std::cout << "Height " << b << " of " << m_blocks.size() << '\r'; + } const BlockEntry& block = m_blocks[b]; crypto::hash blockHash = get_block_hash(block.bl); m_blockIndex.push(blockHash); @@ -435,6 +449,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi bool blockchain_storage::storeCache() { CRITICAL_REGION_LOCAL(m_blockchain_lock); + LOG_PRINT_L0("Saving blockchain..."); BlockCacheSerializer ser(*this, get_tail_id()); if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { LOG_ERROR("Failed to save blockchain cache"); @@ -900,9 +915,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: } if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { - LOG_PRINT_RED_L0("Block with id: " << id - << ENDL << " can't be accepted for alternative chain, block height: " << block_height - << ENDL << " blockchain height: " << get_current_blockchain_height()); + LOG_PRINT_L2("Block with id: " << id << std::endl << + " can't be accepted for alternative chain, block height: " << block_height << std::endl << + " blockchain height: " << get_current_blockchain_height()); bvc.m_verifivation_failed = true; return false; } @@ -1525,7 +1540,7 @@ bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& b uint64_t height = get_block_height(b); const uint8_t expectedBlockVersion = get_block_major_version_for_height(height); if (b.majorVersion != expectedBlockVersion) { - LOG_PRINT_L0("Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << + LOG_PRINT_L2("Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << ", at height " << height << " expected version is " << static_cast(expectedBlockVersion)); return false; } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index ca4f1f9670..ce12c8191d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -257,7 +257,7 @@ namespace cryptonote if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { - LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << + LOG_PRINT_RED_L0("transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize())); return false; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index bcbcf968e6..47427ac9b1 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -123,9 +123,9 @@ namespace cryptonote int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height - << " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " - << (0 <= diff ? std::string("behind") : std::string("ahead")) - << "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1)); + << " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " + << (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl << + "SYNCHRONIZATION started", (diff >= 0 ? (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1) : LOG_LEVEL_2)); LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; context.m_remote_blockchain_height = hshd.current_height; @@ -173,7 +173,7 @@ namespace cryptonote block_verification_context bvc = boost::value_initialized(); m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); if (bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } @@ -332,7 +332,7 @@ namespace cryptonote m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); if (bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); m_p2p->drop_connection(context); return 1; } else if (bvc.m_marked_as_orphaned) { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8f5123852e..80e71d30c8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -230,7 +230,7 @@ namespace } if (!r) { - fail_msg_writer() << "payment id has invalid format: \"" << value << "\", expected 64-character string"; + fail_msg_writer() << "payment ID has invalid format: \"" << value << "\", expected 64-character string"; return false; } } else if (arg == "-f") { @@ -249,7 +249,13 @@ namespace cryptonote::tx_destination_entry de; if (!m_currency.parseAccountAddressString(arg, de.addr)) { - fail_msg_writer() << "wrong address: " << arg; + crypto::hash paymentId; + if (tools::wallet2::parse_payment_id(arg, paymentId)) { + fail_msg_writer() << "Invalid payment ID usage. Please, use -p . See help for details."; + } else { + fail_msg_writer() << "Wrong address: " << arg; + } + return false; } @@ -307,6 +313,7 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); + m_cmd_binder.set_handler("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers"); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), @@ -316,6 +323,7 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); + m_cmd_binder.set_handler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start"); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help"); } //---------------------------------------------------------------------------------------------------- @@ -560,7 +568,14 @@ bool simple_wallet::save(const std::vector &args) return true; } -//---------------------------------------------------------------------------------------------------- + +bool simple_wallet::reset(const std::vector &args) { + m_wallet->reset(); + success_msg_writer(true) << "Reset is complete successfully"; + refresh(); + return true; +} + bool simple_wallet::start_mining(const std::vector& args) { if (!try_connect_to_daemon()) @@ -652,7 +667,7 @@ void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::Trans m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::refresh(const std::vector& args) +bool simple_wallet::refresh(const std::vector& args/* = std::vector()*/) { if (!try_connect_to_daemon()) return true; @@ -776,12 +791,35 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args return true; } -//---------------------------------------------------------------------------------------------------- + +bool simple_wallet::listTransfers(const std::vector& args) { + const std::vector& transfers = m_wallet->getTransfers(); + for (const tools::wallet2::Transfer& transfer : transfers) { + std::string address = "UNKNOWN"; + if (transfer.hasAddress) { + address = getAccountAddressAsStr(m_currency.publicAddressBase58Prefix(), transfer.address); + } + + message_writer(transfer.output ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) + << transfer.time + << ", " << (transfer.output ? "OUTPUT" : "INPUT") + << ", " << transfer.transactionHash + << ", " << m_currency.formatAmount(transfer.amount) + << ", " << m_currency.formatAmount(transfer.fee) + << ", " << transfer.paymentId + << ", " << address + << ", " << transfer.blockIndex + << ", " << transfer.unlockTime; + } + + return true; +} + bool simple_wallet::show_payments(const std::vector &args) { if(args.empty()) { - fail_msg_writer() << "expected at least one payment_id"; + fail_msg_writer() << "expected at least one payment ID"; return true; } @@ -819,7 +857,7 @@ bool simple_wallet::show_payments(const std::vector &args) } else { - fail_msg_writer() << "payment id has invalid format: \"" << arg << "\", expected 64-character string"; + fail_msg_writer() << "payment ID has invalid format: \"" << arg << "\", expected 64-character string"; } } @@ -864,6 +902,13 @@ bool simple_wallet::transfer(const std::vector &args) cryptonote::Transaction tx; m_wallet->transfer(cmd.dsts, cmd.fake_outs_count, 0, cmd.fee, cmd.extra, tx); success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); + + try { + m_wallet->store(); + } catch (const std::exception& e) { + fail_msg_writer() << e.what(); + return false; + } } catch (const tools::error::daemon_busy&) { diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 529b9d0a51..5256ce7e74 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -62,14 +62,16 @@ namespace cryptonote bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); - bool refresh(const std::vector &args); + bool refresh(const std::vector &args = std::vector()); bool show_balance(const std::vector &args = std::vector()); bool show_incoming_transfers(const std::vector &args); bool show_payments(const std::vector &args); bool show_blockchain_height(const std::vector &args); + bool listTransfers(const std::vector &args); bool transfer(const std::vector &args); bool print_address(const std::vector &args = std::vector()); bool save(const std::vector &args); + bool reset(const std::vector &args); bool set_log(const std::vector &args); uint64_t get_daemon_blockchain_height(std::string& err); diff --git a/src/version.h.in b/src/version.h.in index d04bb6276f..497e2ae5cf 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.1" -#define PROJECT_VERSION_BUILD_NO "316" +#define PROJECT_VERSION "1.0.2" +#define PROJECT_VERSION_BUILD_NO "336" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 54b55e67e3..e831b7fe7c 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -74,7 +74,7 @@ WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& cur m_unconfirmedTransactions(unconfirmedTransactions), m_isInitialized(false), m_isStoping(false) { - m_upperTransactionSizeLimit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize(); + m_upperTransactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); } void WalletTransactionSender::init(cryptonote::account_keys keys) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6792bf2389..7e9e00d61b 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -65,7 +65,7 @@ void wallet2::init(const std::string& daemon_address) m_daemon_address = daemon_address; } //---------------------------------------------------------------------------------------------------- -bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id) { +bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id) { process_unconfirmed(tx); std::vector tx_extra_fields; @@ -84,7 +84,7 @@ bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transactio return false; } - TxItem txItem = { tx, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) }; + TxItem txItem = { tx, time, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) }; queue.push(std::unique_ptr(new TxItem(std::move(txItem)))); return true; } @@ -159,26 +159,59 @@ void wallet2::processCheckedTransaction(const TxItem& item) { } } - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) - { - crypto::hash payment_id; - if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) - { - uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0; - if (0 < received && null_hash != payment_id) - { + crypto::hash transactionHash = get_transaction_hash(tx); + + bool ownTransfer = false; + for (Transfer& transfer : transfers) { + if (transfer.transactionHash == transactionHash) { + transfer.blockIndex = height; + ownTransfer = true; + } + } + + if (!ownTransfer) { + crypto::hash paymentId = null_hash; + tx_extra_nonce extraNonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extraNonce)) { + get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); + } + + if (tx_money_spent_in_ins < tx_money_got_in_outs) { + if (paymentId != null_hash) { payment_details payment; - payment.m_tx_hash = cryptonote::get_transaction_hash(tx); - payment.m_amount = received; + payment.m_tx_hash = transactionHash; + payment.m_amount = tx_money_got_in_outs - tx_money_spent_in_ins; payment.m_block_height = height; payment.m_unlock_time = tx.unlockTime; - m_payments.emplace(payment_id, payment); - LOG_PRINT_L2("Payment found: " << payment_id << " / " << payment.m_tx_hash << " / " << payment.m_amount); + m_payments.emplace(paymentId, payment); + LOG_PRINT_L2("Payment found: " << paymentId << " / " << payment.m_tx_hash << " / " << payment.m_amount); } + + Transfer transfer; + transfer.time = item.time; + transfer.output = false; + transfer.transactionHash = transactionHash; + transfer.amount = tx_money_got_in_outs - tx_money_spent_in_ins; + transfer.fee = 0; + transfer.paymentId = paymentId; + transfer.hasAddress = false; + transfer.blockIndex = height; + transfer.unlockTime = tx.unlockTime; + transfers.push_back(transfer); + } else if (tx_money_got_in_outs < tx_money_spent_in_ins) { + Transfer transfer; + transfer.time = item.time; + transfer.output = true; + transfer.transactionHash = transactionHash; + transfer.amount = tx_money_spent_in_ins - tx_money_got_in_outs; + transfer.fee = 0; + transfer.paymentId = paymentId; + transfer.hasAddress = false; + transfer.blockIndex = height; + transfer.unlockTime = tx.unlockTime; + transfers.push_back(transfer); } } - } //---------------------------------------------------------------------------------------------------- @@ -211,7 +244,6 @@ bool wallet2::addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_he "current_index=" + std::to_string(current_index) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size())); m_blockchain.push_back(bl_id); - ++m_local_bc_height; if (0 != m_callback) { m_callback->on_new_block(current_index); @@ -234,7 +266,7 @@ size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::bloc if (b.timestamp + 60 * 60 * 24 > m_account.get_createtime()) { TIME_MEASURE_START(miner_tx_handle_time); - if(processNewTransaction(queue, b.minerTx, height, bl_id)) + if(processNewTransaction(queue, b.minerTx, height, b.timestamp, bl_id)) ++processedTransactions; TIME_MEASURE_FINISH(miner_tx_handle_time); @@ -244,7 +276,7 @@ size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::bloc cryptonote::Transaction tx; bool r = parse_and_validate_tx_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - if(processNewTransaction(queue, tx, height, bl_id)) + if(processNewTransaction(queue, tx, height, b.timestamp, bl_id)) ++processedTransactions; } TIME_MEASURE_FINISH(txs_handle_time); @@ -512,7 +544,6 @@ size_t wallet2::detach_blockchain(uint64_t height) size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height); m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); - m_local_bc_height -= blocks_detached; for (auto it = m_payments.begin(); it != m_payments.end(); ) { @@ -522,6 +553,14 @@ size_t wallet2::detach_blockchain(uint64_t height) ++it; } + for (std::size_t transferIndex = 0; transferIndex < transfers.size();) { + if (transfers[transferIndex].blockIndex != 0 && transfers[transferIndex].blockIndex >= height) { + transfers.erase(transfers.begin() + transferIndex); + } else { + ++transferIndex; + } + } + LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); return blocks_detached; } @@ -536,7 +575,6 @@ bool wallet2::clear() m_blockchain.clear(); m_transfers.clear(); m_blockchain.push_back(m_currency.genesisBlockHash()); - m_local_bc_height = 1; return true; } //---------------------------------------------------------------------------------------------------- @@ -702,8 +740,6 @@ void wallet2::load(const std::string& wallet_, const std::string& password) THROW_WALLET_EXCEPTION_IF(m_blockchain[0] != m_currency.genesisBlockHash(), error::wallet_internal_error, "Genesis block missmatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); } - - m_local_bc_height = m_blockchain.size(); } //---------------------------------------------------------------------------------------------------- void wallet2::store() @@ -852,4 +888,23 @@ void wallet2::transfer(const std::vector& dsts transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); } //---------------------------------------------------------------------------------------------------- +const std::vector& wallet2::getTransfers() { + return transfers; +} + +void wallet2::reset() { + clear(); + m_unconfirmed_txs.clear(); + m_payments.clear(); + m_key_images.clear(); + for (std::size_t transferIndex = 0; transferIndex < transfers.size();) { + if (transfers[transferIndex].hasAddress) { + transfers[transferIndex].blockIndex = 0; + ++transferIndex; + } else { + transfers.erase(transfers.begin() + transferIndex); + } + } +} + } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b7d452baff..f2ff6ed921 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -70,14 +70,12 @@ namespace tools wallet2(const wallet2& rhs) : m_currency(rhs.m_currency), m_run(true), - m_callback(0), - m_upper_transaction_size_limit(rhs.m_upper_transaction_size_limit) { + m_callback(0) { }; public: wallet2(const cryptonote::Currency& currency) : m_currency(currency), m_run(true), m_callback(0) { - m_upper_transaction_size_limit = (cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100 - m_currency.minerTxBlobReservedSize(); } struct transfer_details @@ -121,6 +119,37 @@ namespace tools END_SERIALIZE() }; + struct Transfer { + uint64_t time; + bool output; + crypto::hash transactionHash; + uint64_t amount; + uint64_t fee; + crypto::hash paymentId; + bool hasAddress; + cryptonote::AccountPublicAddress address; + uint64_t blockIndex; + uint64_t unlockTime; + + template void serialize(Archive& archive, unsigned int version) { + archive & time; + archive & output; + archive & transactionHash; + archive & amount; + archive & fee; + archive & paymentId; + archive & hasAddress; + if (hasAddress) { + archive & address; + } + + archive & blockIndex; + archive & unlockTime; + } + }; + + typedef std::vector Transfers; + void generate(const std::string& wallet, const std::string& password); void load(const std::string& wallet, const std::string& password); void store(); @@ -153,7 +182,10 @@ namespace tools bool connectClient(epee::net_utils::http::http_simple_client& client); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments) const; - uint64_t get_blockchain_current_height() const { return m_local_bc_height; } + uint64_t get_blockchain_current_height() const { return m_blockchain.size(); } + const std::vector& getTransfers(); + void reset(); + template inline void serialize(t_archive &a, const unsigned int ver) { @@ -169,6 +201,9 @@ namespace tools if(ver < 7) return; a & m_payments; + if (ver >= 8) { + a & transfers; + } } static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists); @@ -180,6 +215,7 @@ namespace tools struct TxItem { cryptonote::Transaction tx; + uint64_t time; uint64_t height; crypto::hash blockId; crypto::public_key txPubKey; @@ -196,7 +232,7 @@ namespace tools bool addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t height); size_t processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height); - bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, const crypto::hash& bl_id); + bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id); void processCheckedTransaction(const TxItem& item); // returns number of blocks added @@ -234,21 +270,21 @@ namespace tools std::string m_keys_file; epee::net_utils::http::http_simple_client m_http_client; std::vector m_blockchain; - std::atomic m_local_bc_height; //temporary workaround std::unordered_map m_unconfirmed_txs; transfer_container m_transfers; payment_container m_payments; std::unordered_map m_key_images; cryptonote::AccountPublicAddress m_account_public_address; - uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value std::atomic m_run; i_wallet2_callback* m_callback; + + Transfers transfers; }; } -BOOST_CLASS_VERSION(tools::wallet2, 7) +BOOST_CLASS_VERSION(tools::wallet2, 8) namespace boost { @@ -466,7 +502,8 @@ namespace tools bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, m_currency.publicAddressBase58Prefix(), sources, splitted_dsts, unlock_time); - THROW_WALLET_EXCEPTION_IF(m_upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_limit); + uint64_t transactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); + THROW_WALLET_EXCEPTION_IF(get_object_blobsize(tx) > transactionSizeLimit, error::tx_too_big, tx, transactionSizeLimit); std::string key_images; bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const TransactionInput& s_e) -> bool @@ -487,13 +524,38 @@ namespace tools add_unconfirmed_tx(tx, change_dts.amount); - LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]"); + crypto::hash transactionHash = get_transaction_hash(tx); + LOG_PRINT_L2("transaction " << transactionHash << " generated ok and sent to daemon, key_images: [" << key_images << "]"); BOOST_FOREACH(transfer_container::iterator it, selected_transfers) it->m_spent = true; - LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(tx) << ">" << ENDL - << "Commission: " << m_currency.formatAmount(fee + dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL + crypto::hash paymentId = null_hash; + std::vector transactionExtras; + if (parse_tx_extra(tx.extra, transactionExtras)) { + tx_extra_nonce extraNonce; + if (find_tx_extra_field_by_type(transactionExtras, extraNonce)) { + get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); + } + } + + for (auto& destination : dsts) { + Transfer transfer; + transfer.time = static_cast(time(NULL)); + transfer.output = true; + transfer.transactionHash = transactionHash; + transfer.amount = destination.amount; + transfer.fee = fee; + transfer.paymentId = paymentId; + transfer.hasAddress = true; + transfer.address = destination.addr; + transfer.blockIndex = 0; + transfer.unlockTime = unlock_time; + transfers.push_back(transfer); + } + + LOG_PRINT_L0("Transaction successfully sent. <" << transactionHash << ">" << ENDL + << "Commission: " << m_currency.formatAmount(fee+dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL << "Balance: " << m_currency.formatAmount(balance()) << ENDL << "Unlocked: " << m_currency.formatAmount(unlocked_balance()) << ENDL << "Please, wait for confirmation for your balance to be unlocked."); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index de783cbb7f..ccd2ad4b34 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -46,9 +46,16 @@ namespace tools m_net_server.add_idle_handler([this](){ try { m_wallet.refresh(); - } catch (const std::exception& ex) { - LOG_ERROR("Exception at while refreshing, what=" << ex.what()); + } catch (const std::exception& e) { + LOG_ERROR("Exception while refreshing, what=" << e.what()); } + + try { + m_wallet.store(); + } catch (const std::exception& e) { + LOG_ERROR("Exception while storing, what=" << e.what()); + } + return true; }, 20000); @@ -192,5 +199,34 @@ namespace tools return true; } - //------------------------------------------------------------------------------------------------------------------------------ + + 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, connection_context& cntx) { + res.transfers.clear(); + const std::vector& transfers = m_wallet.getTransfers(); + for (const tools::wallet2::Transfer& transfer : transfers) { + wallet_rpc::Transfer transfer2; + transfer2.time = transfer.time; + transfer2.output = transfer.output; + transfer2.transactionHash = epee::string_tools::pod_to_hex(transfer.transactionHash); + transfer2.amount = transfer.amount; + transfer2.fee = transfer.fee; + transfer2.paymentId = transfer.paymentId == cryptonote::null_hash ? "" : epee::string_tools::pod_to_hex(transfer.paymentId); + transfer2.address = transfer.hasAddress ? getAccountAddressAsStr(m_wallet.currency().publicAddressBase58Prefix(), transfer.address) : ""; + transfer2.blockIndex = transfer.blockIndex; + transfer2.unlockTime = transfer.unlockTime; + res.transfers.push_back(transfer2); + } + + return true; + } + + bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx) { + res.height = m_wallet.get_blockchain_current_height(); + return true; + } + + bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) { + m_wallet.reset(); + return true; + } } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 09f0988941..680324f28b 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -48,10 +48,13 @@ namespace tools BEGIN_URI_MAP2() BEGIN_JSON_RPC_MAP("/json_rpc") - MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) - MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) - MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) - MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) + MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) + MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) + MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) + MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) + MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS) + MAP_JON_RPC_WE("get_height", on_get_height, wallet_rpc::COMMAND_RPC_GET_HEIGHT) + MAP_JON_RPC_WE("reset", on_reset, wallet_rpc::COMMAND_RPC_RESET) END_JSON_RPC_MAP() END_URI_MAP2() @@ -60,6 +63,9 @@ 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, connection_context& cntx); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool 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, connection_context& cntx); + bool on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx); bool handle_command_line(const boost::program_options::variables_map& vm); diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index 7649332f08..8ef30557c7 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -136,5 +136,71 @@ namespace wallet_rpc END_KV_SERIALIZE_MAP() }; }; + + struct Transfer { + uint64_t time; + bool output; + std::string transactionHash; + uint64_t amount; + uint64_t fee; + std::string paymentId; + std::string address; + uint64_t blockIndex; + uint64_t unlockTime; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(time) + KV_SERIALIZE(output) + KV_SERIALIZE(transactionHash) + KV_SERIALIZE(amount) + KV_SERIALIZE(fee) + KV_SERIALIZE(paymentId) + KV_SERIALIZE(address) + KV_SERIALIZE(blockIndex) + KV_SERIALIZE(unlockTime) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_TRANSFERS { + struct request { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response { + std::list transfers; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(transfers) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_HEIGHT { + struct request { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response { + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_RESET { + struct request { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; } } diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index c01551ff49..d87dffe30c 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -591,37 +591,14 @@ bool gen_block_has_invalid_tx::generate(std::vector& events) c bool gen_block_is_too_big::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); + generator.defaultMajorVersion = m_blockMajorVersion; - // Creating a huge miner_tx, it will have a lot of outs - MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); - static const size_t tx_out_count = m_currency.blockGrantedFullRewardZone() / 2; - uint64_t amount = get_outs_money_amount(miner_tx); - uint64_t portion = amount / tx_out_count; - uint64_t remainder = amount % tx_out_count; - TransactionOutputTarget target = miner_tx.vout[0].target; - miner_tx.vout.clear(); - for (size_t i = 0; i < tx_out_count; ++i) - { - TransactionOutput o; - o.amount = portion; - o.target = target; - miner_tx.vout.push_back(o); - } - if (0 < remainder) - { - TransactionOutput o; - o.amount = remainder; - o.target = target; - miner_tx.vout.push_back(o); - } - - // Block reward will be incorrect, as it must be reduced if cumulative block size is very big, - // but in this test it doesn't matter Block blk_1; - if (!generator.constructBlockManually(blk_1, blk_0, miner_account, - test_generator::bf_major_ver | test_generator::bf_miner_tx, m_blockMajorVersion, 0, 0, crypto::hash(), 0, miner_tx)) + if (!generator.constructMaxSizeBlock(blk_1, blk_0, miner_account)) { return false; + } + blk_1.minerTx.extra.resize(blk_1.minerTx.extra.size() + 1); events.push_back(blk_1); DO_CALLBACK(events, "check_block_purged"); diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index 351b30b0c5..eed583b5e3 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -297,7 +297,12 @@ struct gen_block_has_invalid_tx : public CheckBlockPurged struct gen_block_is_too_big : public CheckBlockPurged { gen_block_is_too_big(uint8_t blockMajorVersion) - : CheckBlockPurged(1, blockMajorVersion) {} + : CheckBlockPurged(1, blockMajorVersion) { + cryptonote::CurrencyBuilder currencyBuilder; + currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); + m_currency = currencyBuilder.currency(); + } bool generate(std::vector& events) const; }; From 6b1858d965ad6c976abd173f8b9eb1f3b22ea411 Mon Sep 17 00:00:00 2001 From: jezal Date: Thu, 4 Sep 2014 17:37:24 +0100 Subject: [PATCH 15/59] CryptoC-3: fix for Merkle tree root issue --- src/crypto/tree-hash.c | 2 +- src/version.h.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index feee98dd6a..7ab087dde3 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -32,7 +32,7 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { size_t i, j; size_t cnt = count - 1; char (*ints)[HASH_SIZE]; - for (i = 1; i < sizeof(size_t); i <<= 1) { + for (i = 1; i < 8 * sizeof(size_t); i <<= 1) { cnt |= cnt >> i; } cnt &= ~(cnt >> 1); diff --git a/src/version.h.in b/src/version.h.in index 497e2ae5cf..c26fa936fe 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" #define PROJECT_VERSION "1.0.2" -#define PROJECT_VERSION_BUILD_NO "336" +#define PROJECT_VERSION_BUILD_NO "358" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" From 6be8153a8bddf7be43aca1efb829ba719409787a Mon Sep 17 00:00:00 2001 From: cryptonotefoundation Date: Fri, 5 Sep 2014 13:46:10 +0000 Subject: [PATCH 16/59] CryptoC-3: fix for Merkle tree root issue --- src/crypto/tree-hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index a2b0eeaa5b..87423fb82a 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -19,7 +19,7 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { size_t i, j; size_t cnt = count - 1; char (*ints)[HASH_SIZE]; - for (i = 1; i < sizeof(size_t); i <<= 1) { + for (i = 1; i < 8 * sizeof(size_t); i <<= 1) { cnt |= cnt >> i; } cnt &= ~(cnt >> 1); From 6d741947cb41776bfacc4a2cb9c45083837196c4 Mon Sep 17 00:00:00 2001 From: jezal Date: Mon, 15 Sep 2014 14:48:45 +0400 Subject: [PATCH 17/59] New TCP server --- CMakeLists.txt | 2 +- src/System/Event.cpp | 98 ++++++++++++++++++++++++++ src/System/Event.h | 43 +++++++++++ src/System/System.cpp | 133 +++++++++++++++++++++++++++++++++++ src/System/System.h | 46 ++++++++++++ src/System/TcpConnection.cpp | 110 +++++++++++++++++++++++++++++ src/System/TcpConnection.h | 47 +++++++++++++ src/System/TcpConnector.cpp | 67 ++++++++++++++++++ src/System/TcpConnector.h | 41 +++++++++++ src/System/TcpListener.cpp | 88 +++++++++++++++++++++++ src/System/TcpListener.h | 43 +++++++++++ src/System/TcpStream.cpp | 82 +++++++++++++++++++++ src/System/TcpStream.h | 43 +++++++++++ src/System/Timer.cpp | 70 ++++++++++++++++++ src/System/Timer.h | 38 ++++++++++ 15 files changed, 950 insertions(+), 1 deletion(-) create mode 100644 src/System/Event.cpp create mode 100644 src/System/Event.h create mode 100644 src/System/System.cpp create mode 100644 src/System/System.h create mode 100644 src/System/TcpConnection.cpp create mode 100644 src/System/TcpConnection.h create mode 100644 src/System/TcpConnector.cpp create mode 100644 src/System/TcpConnector.h create mode 100644 src/System/TcpListener.cpp create mode 100644 src/System/TcpListener.h create mode 100644 src/System/TcpStream.cpp create mode 100644 src/System/TcpStream.h create mode 100644 src/System/Timer.cpp create mode 100644 src/System/Timer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 243001d1d2..09493a7336 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,7 @@ if(STATIC) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) endif() -find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options) +find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options coroutine context) if((${Boost_MAJOR_VERSION} EQUAL 1) AND (${Boost_MINOR_VERSION} EQUAL 54)) message(SEND_ERROR "Boost version 1.54 is unsupported, more details are available here http://goo.gl/RrCFmA") endif() diff --git a/src/System/Event.cpp b/src/System/Event.cpp new file mode 100644 index 0000000000..ddc2a507f2 --- /dev/null +++ b/src/System/Event.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Event.h" +#include +#include "System.h" + +struct Event::Waiter { + Event::Waiter* next; + void* context; +}; + +Event::Event() : system(nullptr) { +} + +Event::Event(System& system) : system(&system), first(nullptr), state(false) { +} + +Event::Event(Event&& other) : system(other.system) { + if (other.system != nullptr) { + first = other.first; + if (other.first != nullptr) { + last = other.last; + } + + state = other.state; + other.system = nullptr; + } +} + +Event::~Event() { + assert(first == nullptr); +} + +Event& Event::operator=(Event&& other) { + assert(first == nullptr); + system = other.system; + if (other.system != nullptr) { + first = other.first; + if (other.first != nullptr) { + last = other.last; + } + + state = other.state; + other.system = nullptr; + } + + return *this; +} + +bool Event::get() const { + assert(system != nullptr); + return state; +} + +void Event::clear() { + assert(system != nullptr); + state = false; +} + +void Event::set() { + assert(system != nullptr); + state = true; + for (Waiter* waiter = first; waiter != nullptr; waiter = waiter->next) { + system->pushContext(waiter->context); + } + + first = nullptr; +} + +void Event::wait() { + assert(system != nullptr); + Waiter waiter = {nullptr, system->getCurrentContext()}; + if (first != nullptr) { + last->next = &waiter; + } else { + first = &waiter; + } + + last = &waiter; + while (!state) { + system->yield(); + } +} diff --git a/src/System/Event.h b/src/System/Event.h new file mode 100644 index 0000000000..50125024d9 --- /dev/null +++ b/src/System/Event.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +class System; + +class Event { +public: + Event(); + explicit Event(System& system); + Event(const Event&) = delete; + Event(Event&& other); + ~Event(); + Event& operator=(const Event&) = delete; + Event& operator=(Event&& other); + bool get() const; + void clear(); + void set(); + void wait(); + +private: + struct Waiter; + + System* system; + Waiter* first; + Waiter* last; + bool state; +}; diff --git a/src/System/System.cpp b/src/System/System.cpp new file mode 100644 index 0000000000..fd12d33c0b --- /dev/null +++ b/src/System/System.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "System.h" +#include +#include +#include + +namespace { +void contextProcedureStatic(intptr_t context) { + reinterpret_cast(context)->contextProcedure(); +} +} + +System::System() { + ioService = new boost::asio::io_service; + work = new boost::asio::io_service::work(*static_cast(ioService)); + currentContext = new boost::context::fcontext_t; +} + +System::~System() { + assert(procedures.empty()); + assert(resumingContexts.empty()); + while (!contexts.empty()) { + + delete static_cast(contexts.top()); + + contexts.pop(); + } + delete static_cast(work); + if (!static_cast(ioService)->stopped()) { + static_cast(ioService)->stop(); + } + delete static_cast(ioService); +} + +void* System::getCurrentContext() const { + + return currentContext; +} + +void* System::getIoService() { + return ioService; +} + +void System::pushContext(void* context) { + resumingContexts.push(context); +} + +void System::spawn(std::function&& procedure) { + procedures.emplace(std::move(procedure)); +} + +void System::wake() { + static_cast(ioService)->post([] {}); +} + +void System::yield() { + if (procedures.empty()) { + void* context; + for (;;) { + if (resumingContexts.empty()) { + boost::system::error_code errorCode; + static_cast(ioService)->run_one(errorCode); + if (errorCode) { + std::cerr << "boost::asio::io_service::run_onw failed, result=" << errorCode << '.' << std::endl; + throw std::runtime_error("System::yield"); + } + } else { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } + } + + if (context != currentContext) { + boost::context::fcontext_t* oldContext = static_cast(currentContext); + currentContext = context; +#if (BOOST_VERSION >= 105600) + boost::context::jump_fcontext(oldContext, *static_cast(context), reinterpret_cast(this), false); +#else + boost::context::jump_fcontext(oldContext, static_cast(context), reinterpret_cast(this), false); +#endif + } + } else { + void* context; + if (contexts.empty()) { +#if (BOOST_VERSION >= 105600) + context = new boost::context::fcontext_t(boost::context::make_fcontext(new uint8_t[65536] + 65536, 65536, contextProcedureStatic)); +#else + context = new boost::context::fcontext_t(*boost::context::make_fcontext(new uint8_t[65536] + 65536, 65536, contextProcedureStatic)); +#endif + } else { + context = contexts.top(); + contexts.pop(); + } + + + boost::context::fcontext_t* oldContext = static_cast(currentContext); + currentContext = context; +#if (BOOST_VERSION >= 105600) + boost::context::jump_fcontext(oldContext, *static_cast(context), reinterpret_cast(this), false); +#else + boost::context::jump_fcontext(oldContext, static_cast(context), reinterpret_cast(this), false); +#endif + } +} + +void System::contextProcedure() { + void* context = currentContext; + for (;;) { + assert(!procedures.empty()); + std::function procedure = std::move(procedures.front()); + procedures.pop(); + procedure(); + contexts.push(context); + yield(); + } +} diff --git a/src/System/System.h b/src/System/System.h new file mode 100644 index 0000000000..712de34899 --- /dev/null +++ b/src/System/System.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +class System { +public: + System(); + System(const System&) = delete; + ~System(); + System& operator=(const System&) = delete; + void* getCurrentContext() const; + void* getIoService(); + void pushContext(void* context); + void spawn(std::function&& procedure); + void yield(); + void wake(); + + void contextProcedure(); + +private: + void* ioService; + void* work; + std::stack contexts; + std::queue> procedures; + std::queue resumingContexts; + void* currentContext; +}; diff --git a/src/System/TcpConnection.cpp b/src/System/TcpConnection.cpp new file mode 100644 index 0000000000..13a5a41be0 --- /dev/null +++ b/src/System/TcpConnection.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnection.h" +#include +#include +#include "System.h" + +TcpConnection::TcpConnection() : system(nullptr) { +} + +TcpConnection::TcpConnection(System& system, void* socket) : system(&system), socket(socket), stopped(false) { +} + +TcpConnection::TcpConnection(TcpConnection&& other) : system(other.system) { + if (other.system != nullptr) { + socket = other.socket; + stopped = other.stopped; + other.system = nullptr; + } +} + +TcpConnection::~TcpConnection() { + if (system != nullptr) { + delete static_cast(socket); + } +} + +TcpConnection& TcpConnection::operator=(TcpConnection&& other) { + if (system != nullptr) { + delete static_cast(socket); + } + + system = other.system; + if (other.system != nullptr) { + socket = other.socket; + stopped = other.stopped; + other.system = nullptr; + } + + return *this; +} + +void TcpConnection::start() { + stopped = false; +} + +void TcpConnection::stop() { + stopped = true; +} + +size_t TcpConnection::read(uint8_t* data, size_t size) { + assert(system != nullptr); + if (stopped) { + throw std::runtime_error("Stopped"); + } + + void* context = system->getCurrentContext(); + boost::system::error_code errorCode; + std::size_t transferred; + static_cast(socket)->async_read_some(boost::asio::buffer(data, size), [&](const boost::system::error_code& callbackErrorCode, std::size_t callbackTransferred) { + errorCode = callbackErrorCode; + transferred = callbackTransferred; + system->pushContext(context); + }); + + system->yield(); + if (errorCode) { + throw boost::system::system_error(errorCode); + } + + return transferred; +} + +void TcpConnection::write(const uint8_t* data, size_t size) { + assert(system != nullptr); + if (stopped) { + throw std::runtime_error("Stopped"); + } + + void* context = system->getCurrentContext(); + boost::system::error_code errorCode; + std::size_t transferred; + boost::asio::async_write(*static_cast(socket), boost::asio::buffer(data, size), [&](const boost::system::error_code& callbackErrorCode, std::size_t callbackTransferred) { + errorCode = callbackErrorCode; + transferred = callbackTransferred; + system->pushContext(context); + }); + + system->yield(); + if (errorCode) { + throw boost::system::system_error(errorCode); + } + + assert(transferred == size); +} diff --git a/src/System/TcpConnection.h b/src/System/TcpConnection.h new file mode 100644 index 0000000000..ce2829e984 --- /dev/null +++ b/src/System/TcpConnection.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +class System; + +class TcpConnection { +public: + TcpConnection(); + TcpConnection(const TcpConnection&) = delete; + TcpConnection(TcpConnection&& other); + ~TcpConnection(); + TcpConnection& operator=(const TcpConnection&) = delete; + TcpConnection& operator=(TcpConnection&& other); + void start(); + void stop(); + std::size_t read(uint8_t* data, std::size_t size); + void write(const uint8_t* data, std::size_t size); + +private: + friend class TcpConnector; + friend class TcpListener; + + explicit TcpConnection(System& system, void* socket); + + System* system; + void* socket; + bool stopped; +}; diff --git a/src/System/TcpConnector.cpp b/src/System/TcpConnector.cpp new file mode 100644 index 0000000000..703a1f8d1a --- /dev/null +++ b/src/System/TcpConnector.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnector.h" +#include +#include "System.h" +#include "TcpConnection.h" + +TcpConnector::TcpConnector() : system(nullptr) { +} + +TcpConnector::TcpConnector(System& system, const std::string& address, uint16_t port) : system(&system), m_address(address), m_port(port) { } + +TcpConnector::TcpConnector(TcpConnector&& other) : system(other.system) { + if (other.system != nullptr) { + m_address = other.m_address; + m_port = other.m_port; + other.system = nullptr; + } +} + +TcpConnector::~TcpConnector() { +} + +TcpConnector& TcpConnector::operator=(TcpConnector&& other) { + system = other.system; + if (other.system != nullptr) { + m_address = other.m_address; + m_port = other.m_port; + other.system = nullptr; + } + + return *this; +} + +TcpConnection TcpConnector::connect() { + assert(system != nullptr); + void* context = system->getCurrentContext(); + boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*static_cast(system->getIoService())); + boost::system::error_code errorCode; + socket->async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_address), m_port), [&](const boost::system::error_code& callbackErrorCode) { + errorCode = callbackErrorCode; + system->pushContext(context); + }); + + system->yield(); + if (errorCode) { + delete socket; + throw boost::system::system_error(errorCode); + } + + return TcpConnection(*system, socket); +} diff --git a/src/System/TcpConnector.h b/src/System/TcpConnector.h new file mode 100644 index 0000000000..960d1f1f07 --- /dev/null +++ b/src/System/TcpConnector.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +class System; +class TcpConnection; + +class TcpConnector { +public: + TcpConnector(); + TcpConnector(System& system, const std::string& address, uint16_t port); + TcpConnector(const TcpConnector&) = delete; + TcpConnector(TcpConnector&& other); + ~TcpConnector(); + TcpConnector& operator=(const TcpConnector&) = delete; + TcpConnector& operator=(TcpConnector&& other); + TcpConnection connect(); + +private: + System* system; + std::string m_address; + uint16_t m_port; +}; diff --git a/src/System/TcpListener.cpp b/src/System/TcpListener.cpp new file mode 100644 index 0000000000..75071eac28 --- /dev/null +++ b/src/System/TcpListener.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpListener.h" +#include +#include "System.h" +#include "TcpConnection.h" + +TcpListener::TcpListener() : system(nullptr) { +} + +TcpListener::TcpListener(System& system, const std::string& address, uint16_t port) : system(&system), stopped(false) { + listener = new boost::asio::ip::tcp::acceptor(*static_cast(system.getIoService()), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port), true); +} + +TcpListener::TcpListener(TcpListener&& other) : system(other.system) { + if (other.system != nullptr) { + listener = other.listener; + stopped = other.stopped; + other.system = nullptr; + } +} + +TcpListener::~TcpListener() { + if (system != nullptr) { + delete static_cast(listener); + } +} + +TcpListener& TcpListener::operator=(TcpListener&& other) { + if (system != nullptr) { + delete static_cast(listener); + } + + system = other.system; + if (other.system != nullptr) { + listener = other.listener; + stopped = other.stopped; + other.system = nullptr; + } + + return *this; +} + +void TcpListener::start() { + stopped = false; +} + +void TcpListener::stop() { + stopped = true; +} + +TcpConnection TcpListener::accept() { + assert(system != nullptr); + if (stopped) { + throw std::runtime_error("Stopped"); + } + + void* context = system->getCurrentContext(); + boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*static_cast(system->getIoService())); + boost::system::error_code errorCode; + static_cast(listener)->async_accept(*socket, [&](const boost::system::error_code& callbackErrorCode) { + errorCode = callbackErrorCode; + system->pushContext(context); + }); + + system->yield(); + if (errorCode) { + delete socket; + throw boost::system::system_error(errorCode); + } + + return TcpConnection(*system, socket); +} diff --git a/src/System/TcpListener.h b/src/System/TcpListener.h new file mode 100644 index 0000000000..6147257c59 --- /dev/null +++ b/src/System/TcpListener.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +class System; +class TcpConnection; + +class TcpListener { +public: + TcpListener(); + TcpListener(System& system, const std::string& address, uint16_t port); + TcpListener(const TcpListener&) = delete; + TcpListener(TcpListener&& other); + ~TcpListener(); + TcpListener& operator=(const TcpListener&) = delete; + TcpListener& operator=(TcpListener&& other); + void start(); + void stop(); + TcpConnection accept(); + +private: + System* system; + void* listener; + bool stopped; +}; diff --git a/src/System/TcpStream.cpp b/src/System/TcpStream.cpp new file mode 100644 index 0000000000..da058f4bb9 --- /dev/null +++ b/src/System/TcpStream.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpStream.h" + +#include + +TcpStreambuf::TcpStreambuf(TcpConnection& connection) : connection(connection) { + setg(&readBuf.front(), &readBuf.front(), &readBuf.front()); + setp(reinterpret_cast(&writeBuf.front()), reinterpret_cast(&writeBuf.front() + writeBuf.max_size())); +} + +TcpStreambuf::~TcpStreambuf() { + dumpBuffer(); +} + +std::streambuf::int_type TcpStreambuf::underflow() { + if (gptr() < egptr()) + return traits_type::to_int_type(*gptr()); + + size_t bytesRead; + try { + bytesRead = connection.read(reinterpret_cast(&readBuf.front()), readBuf.max_size()); + } catch (std::exception& ex) { + return traits_type::eof(); + } + + if (bytesRead == 0) { + return traits_type::eof(); + } + + setg(&readBuf.front(), &readBuf.front(), &readBuf.front() + bytesRead); + + return traits_type::to_int_type(*gptr()); +} + +int TcpStreambuf::sync() { + return dumpBuffer() ? 0 : -1; +} + +bool TcpStreambuf::dumpBuffer() { + try { + size_t count = pptr() - pbase(); + connection.write(&writeBuf.front(), count); + pbump(-count); + } catch (std::exception&) { + return false; + } + + return true; +} + +std::streambuf::int_type TcpStreambuf::overflow(std::streambuf::int_type ch) { + if (ch == traits_type::eof()) { + return traits_type::eof(); + } + + if (pptr() == epptr()) { + if (!dumpBuffer()) { + return traits_type::eof(); + } + } + + *pptr() = ch; + pbump(1); + + return ch; +} diff --git a/src/System/TcpStream.h b/src/System/TcpStream.h new file mode 100644 index 0000000000..55c28d5538 --- /dev/null +++ b/src/System/TcpStream.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "TcpConnection.h" + +class TcpStreambuf : public std::streambuf { +public: + TcpStreambuf(TcpConnection& connection); + TcpStreambuf(const TcpStreambuf&) = delete; + + virtual ~TcpStreambuf(); + +private: + std::streambuf::int_type underflow() override; + std::streambuf::int_type overflow(std::streambuf::int_type ch) override; + int sync() override; + + bool dumpBuffer(); + + TcpConnection& connection; + + std::array readBuf; + std::array writeBuf; +}; diff --git a/src/System/Timer.cpp b/src/System/Timer.cpp new file mode 100644 index 0000000000..786d44853a --- /dev/null +++ b/src/System/Timer.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Timer.h" +#include +#include "System.h" + +Timer::Timer() : system(nullptr) { +} + +Timer::Timer(System& system) : system(&system) { + timer = new boost::asio::steady_timer(*static_cast(system.getIoService())); +} + +Timer::Timer(Timer&& other) : system(other.system) { + if (other.system != nullptr) { + timer = other.timer; + other.system = nullptr; + } +} + +Timer::~Timer() { + if (system != nullptr) { + delete static_cast(timer); + } +} + +Timer& Timer::operator=(Timer&& other) { + if (system != nullptr) { + delete static_cast(timer); + } + + system = other.system; + if (other.system != nullptr) { + timer = other.timer; + other.system = nullptr; + } + + return *this; +} + +void Timer::sleep(std::chrono::milliseconds time) { + assert(system != nullptr); + static_cast(timer)->expires_from_now(time); + void* context = system->getCurrentContext(); + boost::system::error_code errorCode; + static_cast(timer)->async_wait([&](const boost::system::error_code& callbackErrorCode) { + errorCode = callbackErrorCode; + system->pushContext(context); + }); + + system->yield(); + if (errorCode) { + throw boost::system::system_error(errorCode); + } +} diff --git a/src/System/Timer.h b/src/System/Timer.h new file mode 100644 index 0000000000..317f87ebb1 --- /dev/null +++ b/src/System/Timer.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +class System; + +class Timer { +public: + Timer(); + explicit Timer(System& system); + Timer(const Timer&) = delete; + Timer(Timer&& other); + ~Timer(); + Timer& operator=(const Timer&) = delete; + Timer& operator=(Timer&& other); + void sleep(std::chrono::milliseconds time); + +private: + System* system; + void* timer; +}; From 640efb3e158a51737c4038cdc16d8c93bd355bd9 Mon Sep 17 00:00:00 2001 From: jezal Date: Mon, 15 Sep 2014 16:46:31 +0400 Subject: [PATCH 18/59] New HTTP server --- src/System/HttpParser.cpp | 215 ++++++++++++++++++++++++++++++++++++ src/System/HttpParser.h | 47 ++++++++ src/System/HttpRequest.cpp | 73 ++++++++++++ src/System/HttpRequest.h | 53 +++++++++ src/System/HttpResponse.cpp | 83 ++++++++++++++ src/System/HttpResponse.h | 57 ++++++++++ 6 files changed, 528 insertions(+) create mode 100644 src/System/HttpParser.cpp create mode 100644 src/System/HttpParser.h create mode 100644 src/System/HttpRequest.cpp create mode 100644 src/System/HttpRequest.h create mode 100644 src/System/HttpResponse.cpp create mode 100644 src/System/HttpResponse.h diff --git a/src/System/HttpParser.cpp b/src/System/HttpParser.cpp new file mode 100644 index 0000000000..3e4efd2dfc --- /dev/null +++ b/src/System/HttpParser.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpParser.h" + +#include + +namespace cryptonote { + +HttpResponse::HTTP_STATUS HttpParser::parseResponseStatusFromString(const std::string& status) { + if (status == "200 OK" || status == "200 Ok") return cryptonote::HttpResponse::STATUS_200; + else if (status == "404 Not Found") return cryptonote::HttpResponse::STATUS_404; + else if (status == "500 Internal Server Error") return cryptonote::HttpResponse::STATUS_500; + else throw std::runtime_error("Unknown HTTP status code is given"); + + return cryptonote::HttpResponse::STATUS_200; //unaccessible +} + + +void HttpParser::receiveRequest(std::istream& stream, HttpRequest& request) { + readWord(stream, request.method); + readWord(stream, request.url); + + std::string httpVersion; + readWord(stream, httpVersion); + + readHeaders(stream, request.headers); + + std::string body; + size_t bodyLen = getBodyLen(request.headers); + if (bodyLen) { + readBody(stream, request.body, bodyLen); + } +} + + +void HttpParser::receiveResponse(std::istream& stream, HttpResponse& response) { + std::string httpVersion; + readWord(stream, httpVersion); + + std::string status; + char c; + + stream.get(c); + while (stream.good() && c != '\r') { //Till the end + status += c; + stream.get(c); + } + + if (!stream.good()) { + throw std::runtime_error("Parser error: stream is not good"); + } + + if (c == '\r') { + stream.get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + } + + response.setStatus(parseResponseStatusFromString(status)); + + std::string name; + std::string value; + + while (readHeader(stream, name, value)) { + response.addHeader(name, value); + name.clear(); + value.clear(); + } + + response.addHeader(name, value); + auto headers = response.getHeaders(); + size_t length = 0; + auto it = headers.find("Content-Length"); + if (it != headers.end()) { + length = std::stoul(it->second); + } + + std::string body; + if (length) { + readBody(stream, body, length); + } + + response.setBody(body); +} + + +void HttpParser::readWord(std::istream& stream, std::string& word) { + char c; + + stream.get(c); + while (stream.good() && c != ' ' && c != '\r') { + word += c; + stream.get(c); + } + + if (!stream.good()) { + throw std::runtime_error("Parser error: stream is not good"); + } + + if (c == '\r') { + stream.get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + } +} + +void HttpParser::readHeaders(std::istream& stream, HttpRequest::Headers& headers) { + std::string name; + std::string value; + + while (readHeader(stream, name, value)) { + headers[name] = value; //use insert + name.clear(); + value.clear(); + } + + headers[name] = value; //use insert +} + +bool HttpParser::readHeader(std::istream& stream, std::string& name, std::string& value) { + char c; + bool isName = true; + + stream.get(c); + while (stream.good() && c != '\r') { + if (c == ':') { + if (stream.peek() == ' ') { + stream.get(c); + } + + if (name.empty()) { + throw std::runtime_error("Header name must be not empty"); + } + + if (isName) { + isName = false; + stream.get(c); + continue; + } + } + + if (isName) { + name += c; + stream.get(c); + } else { + value += c; + stream.get(c); + } + } + + if (!stream.good()) { + throw std::runtime_error("Parser error: stream is not good"); + } + + stream.get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + + c = stream.peek(); + if (c == '\r') { + stream.get(c).get(c); + if (c != '\n') { + throw std::runtime_error("Parser error: '\\n' symbol is expected"); + } + + return false; //no more headers + } + + return true; +} + +size_t HttpParser::getBodyLen(const HttpRequest::Headers& headers) { + auto it = headers.find("Content-Length"); + if (it != headers.end()) { + size_t bytes = std::stoul(it->second); + return bytes; + } + + return 0; +} + +void HttpParser::readBody(std::istream& stream, std::string& body, const size_t bodyLen) { + size_t read = 0; + + while (stream.good() && read < bodyLen) { + body += stream.get(); + ++read; + } + + if (!stream.good()) { + throw std::runtime_error("stream is not good"); + } +} + +} + + diff --git a/src/System/HttpParser.h b/src/System/HttpParser.h new file mode 100644 index 0000000000..759917813b --- /dev/null +++ b/src/System/HttpParser.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#ifndef HTTPPARSER_H_ +#define HTTPPARSER_H_ + +#include +#include +#include +#include "HttpRequest.h" +#include "HttpResponse.h" + +namespace cryptonote { + +//Blocking HttpParser +class HttpParser { +public: + HttpParser() {}; + + void receiveRequest(std::istream& stream, HttpRequest& request); + void receiveResponse(std::istream& stream, HttpResponse& response); + static HttpResponse::HTTP_STATUS parseResponseStatusFromString(const std::string& status); +private: + void readWord(std::istream& stream, std::string& word); + void readHeaders(std::istream& stream, HttpRequest::Headers &headers); + bool readHeader(std::istream& stream, std::string& name, std::string& value); + size_t getBodyLen(const HttpRequest::Headers& headers); + void readBody(std::istream& stream, std::string& body, const size_t bodyLen); +}; + +} //namespace cryptonote + +#endif /* HTTPPARSER_H_ */ diff --git a/src/System/HttpRequest.cpp b/src/System/HttpRequest.cpp new file mode 100644 index 0000000000..861a918be0 --- /dev/null +++ b/src/System/HttpRequest.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpRequest.h" + +namespace cryptonote { + + const std::string& HttpRequest::getMethod() const { + return method; + } + + const std::string& HttpRequest::getUrl() const { + return url; + } + + const HttpRequest::Headers& HttpRequest::getHeaders() const { + return headers; + } + + const std::string& HttpRequest::getBody() const { + return body; + } + + void HttpRequest::addHeader(const std::string& name, const std::string& value) { + headers[name] = value; + } + void HttpRequest::setBody(const std::string& b) { + body = b; + if (!body.empty()) { + headers["Content-Length"] = std::to_string(body.size()); + } + else { + headers.erase("Content-Length"); + } + } + + void HttpRequest::setUrl(const std::string& u) { + url = u; + } + + std::ostream& HttpRequest::printHttpRequest(std::ostream& os) const { + os << "POST " << url << " HTTP/1.1\r\n"; + auto host = headers.find("Host"); + if (host == headers.end()) { + os << "Host: " << "127.0.0.1" << "\r\n"; + } + + for (auto pair : headers) { + os << pair.first << ": " << pair.second << "\r\n"; + } + + os << "\r\n"; + if (!body.empty()) { + os << body; + } + + return os; + } +} \ No newline at end of file diff --git a/src/System/HttpRequest.h b/src/System/HttpRequest.h new file mode 100644 index 0000000000..2acb8c2e57 --- /dev/null +++ b/src/System/HttpRequest.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace cryptonote { + class HttpRequest { + public: + typedef std::map Headers; + + const std::string& getMethod() const; + const std::string& getUrl() const; + const Headers& getHeaders() const; + const std::string& getBody() const; + + void addHeader(const std::string& name, const std::string& value); + void setBody(const std::string& b); + void setUrl(const std::string& uri); + + private: + friend class HttpParser; + + std::string method; + std::string url; + Headers headers; + std::string body; + + friend std::ostream& operator<<(std::ostream& os, const HttpRequest& resp); + std::ostream& printHttpRequest(std::ostream& os) const; + }; + + inline std::ostream& operator<<(std::ostream& os, const HttpRequest& resp) { + return resp.printHttpRequest(os); + } +} \ No newline at end of file diff --git a/src/System/HttpResponse.cpp b/src/System/HttpResponse.cpp new file mode 100644 index 0000000000..bb6ec3db5c --- /dev/null +++ b/src/System/HttpResponse.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpResponse.h" + +#include + +namespace { + +const char* getStatusString(cryptonote::HttpResponse::HTTP_STATUS status) { + switch (status) { + case cryptonote::HttpResponse::STATUS_200: + return "200 OK"; + case cryptonote::HttpResponse::STATUS_404: + return "404 Not Found"; + case cryptonote::HttpResponse::STATUS_500: + return "500 Internal Server Error"; + default: + throw std::runtime_error("Unknown HTTP status code is given"); + } + + return ""; //unaccessible +} + + +} //namespace + +namespace cryptonote { + +HttpResponse::HttpResponse() { + status = STATUS_200; + headers["Server"] = "Cryptonote-based HTTP server"; +} + +void HttpResponse::setStatus(HTTP_STATUS s) { + status = s; +} + +void HttpResponse::addHeader(const std::string& name, const std::string& value) { + headers[name] = value; +} + +void HttpResponse::setBody(const std::string& b) { + body = b; + if (!body.empty()) { + headers["Content-Length"] = std::to_string(body.size()); + } else { + headers.erase("Content-Length"); + } +} + +std::ostream& HttpResponse::printHttpResponse(std::ostream& os) const { + os << "HTTP/1.1 " << getStatusString(status) << "\r\n"; + + for (auto pair: headers) { + os << pair.first << ": " << pair.second << "\r\n"; + } + os << "\r\n"; + + if (!body.empty()) { + os << body; + } + + return os; +} + +} //namespace cryptonote + + diff --git a/src/System/HttpResponse.h b/src/System/HttpResponse.h new file mode 100644 index 0000000000..3fffd2adea --- /dev/null +++ b/src/System/HttpResponse.h @@ -0,0 +1,57 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace cryptonote { + + class HttpResponse { + public: + enum HTTP_STATUS { + STATUS_200, + STATUS_404, + STATUS_500 + }; + + HttpResponse(); + + void setStatus(HTTP_STATUS s); + void addHeader(const std::string& name, const std::string& value); + void setBody(const std::string& b); + + const std::map& getHeaders() const { return headers; } + HTTP_STATUS getStatus() const { return status; } + const std::string& getBody() const { return body; } + + private: + friend std::ostream& operator<<(std::ostream& os, const HttpResponse& resp); + std::ostream& printHttpResponse(std::ostream& os) const; + + HTTP_STATUS status; + std::map headers; + std::string body; + }; + + inline std::ostream& operator<<(std::ostream& os, const HttpResponse& resp) { + return resp.printHttpResponse(os); + } + +} //namespace cryptonote From 257a2bf339a77cd410fa5d84d16db405cbf35eb9 Mon Sep 17 00:00:00 2001 From: jezal Date: Mon, 15 Sep 2014 21:33:47 +0400 Subject: [PATCH 19/59] New JSON serialization --- CMakeLists.txt | 4 +- .../BinaryInputStreamSerializer.cpp | 242 +++++++ .../BinaryInputStreamSerializer.h | 66 ++ .../BinaryOutputStreamSerializer.cpp | 185 ++++++ .../BinaryOutputStreamSerializer.h | 65 ++ src/serialization/ISerializer.h | 135 ++++ .../JsonInputStreamSerializer.cpp | 33 + src/serialization/JsonInputStreamSerializer.h | 40 ++ .../JsonInputValueSerializer.cpp | 216 +++++++ src/serialization/JsonInputValueSerializer.h | 67 ++ .../JsonOutputStreamSerializer.cpp | 189 ++++++ .../JsonOutputStreamSerializer.h | 69 ++ .../JsonSerializationDispatcher.h | 65 ++ src/serialization/JsonValue.cpp | 612 ++++++++++++++++++ src/serialization/JsonValue.h | 103 +++ src/serialization/SerializationOverloads.cpp | 81 +++ src/serialization/SerializationOverloads.h | 78 +++ 17 files changed, 2248 insertions(+), 2 deletions(-) create mode 100644 src/serialization/BinaryInputStreamSerializer.cpp create mode 100644 src/serialization/BinaryInputStreamSerializer.h create mode 100644 src/serialization/BinaryOutputStreamSerializer.cpp create mode 100644 src/serialization/BinaryOutputStreamSerializer.h create mode 100644 src/serialization/ISerializer.h create mode 100644 src/serialization/JsonInputStreamSerializer.cpp create mode 100644 src/serialization/JsonInputStreamSerializer.h create mode 100644 src/serialization/JsonInputValueSerializer.cpp create mode 100644 src/serialization/JsonInputValueSerializer.h create mode 100644 src/serialization/JsonOutputStreamSerializer.cpp create mode 100644 src/serialization/JsonOutputStreamSerializer.h create mode 100644 src/serialization/JsonSerializationDispatcher.h create mode 100644 src/serialization/JsonValue.cpp create mode 100644 src/serialization/JsonValue.h create mode 100644 src/serialization/SerializationOverloads.cpp create mode 100644 src/serialization/SerializationOverloads.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 09493a7336..c2794e7bb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,9 +31,9 @@ else() else() set(ARCH_FLAG "-march=${ARCH}") endif() - set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized") + set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized -Wno-error=unused-result") if(CMAKE_C_COMPILER_ID STREQUAL "Clang") - set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration") + set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function") else() set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized") endif() diff --git a/src/serialization/BinaryInputStreamSerializer.cpp b/src/serialization/BinaryInputStreamSerializer.cpp new file mode 100644 index 0000000000..31a85b3264 --- /dev/null +++ b/src/serialization/BinaryInputStreamSerializer.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BinaryInputStreamSerializer.h" +#include "SerializationOverloads.h" + +#include +#include +#include + +namespace { + +void deserialize(std::istream& stream, uint8_t& v) { + char c; + stream.get(c); + v = static_cast(c); +} + +void deserialize(std::istream& stream, int8_t& v) { + uint8_t val; + deserialize(stream, val); + v = val; +} + +void deserialize(std::istream& stream, bool& v) { + uint8_t val; + deserialize(stream, val); + + v = val; +} + +void deserialize(std::istream& stream, uint32_t& v) { + char c; + + stream.get(c); + v = static_cast(c); + + stream.get(c); + v += static_cast(c) << 8; + + stream.get(c); + v += static_cast(c) << 16; + + stream.get(c); + v += static_cast(c) << 24; +} + +void deserialize(std::istream& stream, int32_t& v) { + uint32_t val; + deserialize(stream, val); + v = val; +} + +void deserialize(std::istream& stream, uint64_t& v) { + char c; + uint64_t uc; + + stream.get(c); + uc = static_cast(c); + v = uc; + + stream.get(c); + uc = static_cast(c); + v += (uc << 8); + + stream.get(c); + uc = static_cast(c); + v += (uc << 16); + + stream.get(c); + uc = static_cast(c); + v += (uc << 24); + + stream.get(c); + uc = static_cast(c); + v += (uc << 32); + + stream.get(c); + uc = static_cast(c); + v += (uc << 40); + + stream.get(c); + uc = static_cast(c); + v += (uc << 48); + + stream.get(c); + uc = static_cast(c); + v += (uc << 56); +} + +void deserialize(std::istream& stream, int64_t& v) { + uint64_t val; + deserialize(stream, val); + v = val; +} + +void deserialize(std::istream& stream, char* buf, size_t len) { + const size_t chunk = 1000; + +// stream.read(buf, len); + +// looks redundant, but i had a bug with it + while (len && stream) { + size_t toRead = std::min(len, chunk); + stream.read(buf, toRead); + len -= toRead; + buf += toRead; + } +} + +} + +namespace cryptonote { + +ISerializer::SerializerType BinaryInputStreamSerializer::type() const { + return ISerializer::INPUT; +} + +ISerializer& BinaryInputStreamSerializer::beginObject(const std::string& name) { + return *this; +} + +ISerializer& BinaryInputStreamSerializer::endObject() { + return *this; +} + +ISerializer& BinaryInputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { + uint64_t val; + serializeVarint(val, name, *this); + size = val; + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::endArray() { + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(uint8_t& value, const std::string& name) { + deserialize(stream, value); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(uint32_t& value, const std::string& name) { + deserialize(stream, value); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(int32_t& value, const std::string& name) { + uint32_t v; + operator()(v, name); + value = v; + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(int64_t& value, const std::string& name) { + deserialize(stream, value); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(uint64_t& value, const std::string& name) { + deserialize(stream, value); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(bool& value, const std::string& name) { + deserialize(stream, value); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(std::string& value, const std::string& name) { + uint64_t size; + serializeVarint(size, name, *this); + + std::vector temp; + temp.resize(size); + + deserialize(stream, &temp[0], size); + + value.reserve(size); + value.assign(&temp[0], size); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::operator()(char* value, std::size_t size, const std::string& name) { + stream.read(value, size); + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::tag(const std::string& name) { + return *this; +} + +ISerializer& BinaryInputStreamSerializer::untagged(uint8_t& value) { + char v; + stream.get(v); + value = v; + + return *this; +} + +ISerializer& BinaryInputStreamSerializer::endTag() { + return *this; +} + +bool BinaryInputStreamSerializer::hasObject(const std::string& name) { + assert(false); //the method is not supported for this type of serialization + throw std::runtime_error("hasObject method is not supported in BinaryInputStreamSerializer"); + + return false; +} + +ISerializer& BinaryInputStreamSerializer::operator()(double& value, const std::string& name) { + assert(false); //the method is not supported for this type of serialization + throw std::runtime_error("double serialization is not supported in BinaryInputStreamSerializer"); + + return *this; +} + +} diff --git a/src/serialization/BinaryInputStreamSerializer.h b/src/serialization/BinaryInputStreamSerializer.h new file mode 100644 index 0000000000..50ee18e831 --- /dev/null +++ b/src/serialization/BinaryInputStreamSerializer.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" +#include "SerializationOverloads.h" + +#include + +namespace cryptonote { + +class BinaryInputStreamSerializer : public ISerializer { +public: + BinaryInputStreamSerializer(std::istream& strm) : stream(strm) {} + virtual ~BinaryInputStreamSerializer() {} + + virtual ISerializer::SerializerType type() const; + + virtual ISerializer& beginObject(const std::string& name) override; + virtual ISerializer& endObject() override; + + virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; + virtual ISerializer& endArray() override; + + virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; + virtual ISerializer& operator()(int32_t& value, const std::string& name) override; + + virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; + virtual ISerializer& operator()(int64_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; + virtual ISerializer& operator()(double& value, const std::string& name) override; + virtual ISerializer& operator()(bool& value, const std::string& name) override; + virtual ISerializer& operator()(std::string& value, const std::string& name) override; + virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name); + + virtual ISerializer& tag(const std::string& name) override; + virtual ISerializer& untagged(uint8_t& value) override; + virtual ISerializer& endTag() override; + + virtual bool hasObject(const std::string& name) override; + + template + ISerializer& operator()(T& value, const std::string& name) { + return ISerializer::operator()(value, name); + } + +private: + std::istream& stream; +}; + +} diff --git a/src/serialization/BinaryOutputStreamSerializer.cpp b/src/serialization/BinaryOutputStreamSerializer.cpp new file mode 100644 index 0000000000..eba6bba375 --- /dev/null +++ b/src/serialization/BinaryOutputStreamSerializer.cpp @@ -0,0 +1,185 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BinaryOutputStreamSerializer.h" + +#include +#include + +namespace { + +void serializeInteger(std::ostream& stream, uint8_t v) { + stream.put(static_cast(v)); +} + +void serializeBool(std::ostream& stream, bool v) { + serializeInteger(stream, static_cast(v)); +} + +void serializeInteger(std::ostream& stream, uint32_t v) { + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); +} + +void serializeInteger(std::ostream& stream, uint64_t v) { + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); + v >>= 8; + + stream.put(static_cast(v & 0xff)); +} + +void serializeInteger(std::ostream& stream, int64_t v) { + serializeInteger(stream, static_cast(v)); +} + +void serializeData(std::ostream& stream, const char* buf, size_t len) { + stream.write(buf, len); +} + +} + +namespace cryptonote { + +ISerializer::SerializerType BinaryOutputStreamSerializer::type() const { + return ISerializer::OUTPUT; +} + +ISerializer& BinaryOutputStreamSerializer::beginObject(const std::string& name) { + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::endObject() { + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { + uint64_t size64 = size; + serializeVarint(size64, name, *this); + size = size64; + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::endArray() { + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { + serializeInteger(stream, value); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { + serializeInteger(stream, value); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { + uint32_t v = value; + operator()(v, name); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { + serializeInteger(stream, value); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { + serializeInteger(stream, value); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(bool& value, const std::string& name) { + serializeBool(stream, value); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(std::string& value, const std::string& name) { + uint64_t size = value.size(); + serializeVarint(size, name, *this); + serializeData(stream, value.c_str(), value.size()); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(char* value, std::size_t size, const std::string& name) { + serializeData(stream, value, size); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::tag(const std::string& name) { + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::untagged(uint8_t& value) { + stream.put(value); + + return *this; +} + +ISerializer& BinaryOutputStreamSerializer::endTag() { + return *this; +} + +bool BinaryOutputStreamSerializer::hasObject(const std::string& name) { + assert(false); //the method is not supported for this type of serialization + throw std::runtime_error("hasObject method is not supported in BinaryOutputStreamSerializer"); + + return false; +} + +ISerializer& BinaryOutputStreamSerializer::operator()(double& value, const std::string& name) { + assert(false); //the method is not supported for this type of serialization + throw std::runtime_error("double serialization is not supported in BinaryOutputStreamSerializer"); + + return *this; +} + +} diff --git a/src/serialization/BinaryOutputStreamSerializer.h b/src/serialization/BinaryOutputStreamSerializer.h new file mode 100644 index 0000000000..dc4bf80fcc --- /dev/null +++ b/src/serialization/BinaryOutputStreamSerializer.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" +#include "SerializationOverloads.h" + +#include + +namespace cryptonote { + +class BinaryOutputStreamSerializer : public ISerializer { +public: + BinaryOutputStreamSerializer(std::ostream& strm) : stream(strm) {} + virtual ~BinaryOutputStreamSerializer() {} + + virtual ISerializer::SerializerType type() const; + + virtual ISerializer& beginObject(const std::string& name) override; + virtual ISerializer& endObject() override; + + virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; + virtual ISerializer& endArray() override; + + virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; + virtual ISerializer& operator()(int32_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; + virtual ISerializer& operator()(int64_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; + virtual ISerializer& operator()(double& value, const std::string& name) override; + virtual ISerializer& operator()(bool& value, const std::string& name) override; + virtual ISerializer& operator()(std::string& value, const std::string& name) override; + virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) override; + + virtual ISerializer& tag(const std::string& name) override; + virtual ISerializer& untagged(uint8_t& value) override; + virtual ISerializer& endTag() override; + + virtual bool hasObject(const std::string& name) override; + + template + ISerializer& operator()(T& value, const std::string& name) { + return ISerializer::operator()(value, name); + } + +private: + std::ostream& stream; +}; + +} diff --git a/src/serialization/ISerializer.h b/src/serialization/ISerializer.h new file mode 100644 index 0000000000..fe83f7b065 --- /dev/null +++ b/src/serialization/ISerializer.h @@ -0,0 +1,135 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace cryptonote { + +class ISerializer { +public: + + enum SerializerType { + INPUT, + OUTPUT + }; + + virtual ~ISerializer() {} + + virtual SerializerType type() const = 0; + + virtual ISerializer& beginObject(const std::string& name) = 0; + virtual ISerializer& endObject() = 0; + virtual ISerializer& beginArray(std::size_t& size, const std::string& name) = 0; + virtual ISerializer& endArray() = 0; + + virtual ISerializer& operator()(uint8_t& value, const std::string& name) = 0; + virtual ISerializer& operator()(int32_t& value, const std::string& name) = 0; + virtual ISerializer& operator()(uint32_t& value, const std::string& name) = 0; + virtual ISerializer& operator()(int64_t& value, const std::string& name) = 0; + virtual ISerializer& operator()(uint64_t& value, const std::string& name) = 0; + virtual ISerializer& operator()(double& value, const std::string& name) = 0; + virtual ISerializer& operator()(bool& value, const std::string& name) = 0; + virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) = 0; + virtual ISerializer& operator()(std::string& value, const std::string& name) = 0; + + virtual ISerializer& tag(const std::string& name) = 0; + virtual ISerializer& untagged(uint8_t& value) = 0; + virtual ISerializer& endTag() = 0; + + virtual bool hasObject(const std::string& name) = 0; + + template + ISerializer& operator()(T& value, const std::string& name); +}; + +template +ISerializer& ISerializer::operator()(T& value, const std::string& name) { + serialize(value, name, *this); + return *this; +} + +template +void serialize(T& value, const std::string& name, ISerializer& serializer) { + value.serialize(serializer, name); + return; +} + +/* +template +void serialize(std::vector& value, const std::string& name); + +template +void serialize(std::unordered_map& value, const std::string& name); + + + +template +void ISerializer::serialize(std::vector& value, const std::string& name) { + std::size_t size = value.size(); + beginArray(size, name); + value.resize(size); + + for (size_t i = 0; i < size; ++i) { + serialize(value[i], ""); + } + + endArray(); + + return *this; +} + +template +void ISerializer::serialize(std::unordered_map& value, const std::string& name) { + std::size_t size; + size = value.size(); + + beginArray(size, name); + + if (type() == INPUT) { + value.reserve(size); + + for (size_t i = 0; i < size; ++i) { + K key; + V v; + beginObject(""); + serialize(key, ""); + serialize(v, ""); + endObject(); + + value[key] = v; + } + } else { + for (auto kv: value) { + K key; + key = kv.first; + beginObject(""); + serialize(key, ""); + serialize(kv.second, ""); + endObject(); + } + } + + endArray(); + + return *this; +} +*/ + +} diff --git a/src/serialization/JsonInputStreamSerializer.cpp b/src/serialization/JsonInputStreamSerializer.cpp new file mode 100644 index 0000000000..124b172a4c --- /dev/null +++ b/src/serialization/JsonInputStreamSerializer.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "serialization/JsonInputStreamSerializer.h" + +#include +#include + +namespace cryptonote { + +JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) { + stream >> root; + JsonInputValueSerializer::setJsonValue(&root); +} + +JsonInputStreamSerializer::~JsonInputStreamSerializer() { +} + +} //namespace cryptonote diff --git a/src/serialization/JsonInputStreamSerializer.h b/src/serialization/JsonInputStreamSerializer.h new file mode 100644 index 0000000000..ff67f95163 --- /dev/null +++ b/src/serialization/JsonInputStreamSerializer.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +//#include "serialization/Enumerator.h" +#include "serialization/JsonInputValueSerializer.h" +#include "serialization/JsonValue.h" + +namespace cryptonote { + +//deserialization +class JsonInputStreamSerializer : public JsonInputValueSerializer { +public: + JsonInputStreamSerializer(std::istream& stream); + virtual ~JsonInputStreamSerializer(); + +private: + JsonValue root; +}; + +} diff --git a/src/serialization/JsonInputValueSerializer.cpp b/src/serialization/JsonInputValueSerializer.cpp new file mode 100644 index 0000000000..7a1d385a48 --- /dev/null +++ b/src/serialization/JsonInputValueSerializer.cpp @@ -0,0 +1,216 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "serialization/JsonInputValueSerializer.h" + +#include +#include + +namespace cryptonote { + +JsonInputValueSerializer::JsonInputValueSerializer() : root(nullptr) { +} + +JsonInputValueSerializer::~JsonInputValueSerializer() { +} + +void JsonInputValueSerializer::setJsonValue(const JsonValue* value) { + root = value; +} + +ISerializer::SerializerType JsonInputValueSerializer::type() const { + return ISerializer::INPUT; +} + +ISerializer& JsonInputValueSerializer::beginObject(const std::string& name) { + assert(root); + + if (chain.size() == 0) { + chain.push_back(root); + return *this; + } + + const JsonValue* parent = chain.back(); + if (parent->isArray()) { + const JsonValue& v = (*parent)[idxs.back()++]; + chain.push_back(&v); + } else { + const JsonValue& v = (*parent)(name); + chain.push_back(&v); + } + + return *this; +} + +ISerializer& JsonInputValueSerializer::endObject() { + assert(root); + + chain.pop_back(); + return *this; +} + +ISerializer& JsonInputValueSerializer::beginArray(std::size_t& size, const std::string& name) { + assert(root); + + const JsonValue* parent = chain.back(); + + const JsonValue& arr = (*parent)(name); + size = arr.size(); + + chain.push_back(&arr); + idxs.push_back(0); + return *this; +} + +ISerializer& JsonInputValueSerializer::endArray() { + assert(root); + + chain.pop_back(); + idxs.pop_back(); + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(uint32_t& value, const std::string& name) { + assert(root); + + int64_t v = static_cast(value); + operator()(v, name); + value = static_cast(v); + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(int32_t& value, const std::string& name) { + assert(root); + + int64_t v = static_cast(value); + operator()(v, name); + value = static_cast(v); + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(int64_t& value, const std::string& name) { + assert(root); + + const JsonValue* val = chain.back(); + + if (val->isArray()) { + const JsonValue& v = (*val)[idxs.back()++]; + value = v.getNumber(); + } else { + const JsonValue& v = (*val)(name); + value = v.getNumber(); + } + + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(uint64_t& value, const std::string& name) { + assert(root); + + int64_t v = static_cast(value); + operator()(v, name); + value = static_cast(v); + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(double& value, const std::string& name) { + assert(root); + + const JsonValue* val = chain.back(); + + if (val->isArray()) { + value = (*val)[idxs.back()++].getDouble(); + } else { + value = (*val)(name).getDouble(); + } + + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(std::string& value, const std::string& name) { + assert(root); + + const JsonValue* val = chain.back(); + + if (val->isArray()) { + value = (*val)[idxs.back()++].getString(); + } else { + value = (*val)(name).getString(); + } + + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(uint8_t& value, const std::string& name) { + assert(root); + + uint64_t v = static_cast(value); + operator()(v, name); + value = static_cast(value); + + return *this; +} + +ISerializer& JsonInputValueSerializer::operator()(bool& value, const std::string& name) { + assert(root); + + const JsonValue* val = chain.back(); + + if (val->isArray()) { + value = (*val)[idxs.back()++].getBool(); + } else { + value = (*val)(name).getBool(); + } + + return *this; +} + +bool JsonInputValueSerializer::hasObject(const std::string& name) { + const JsonValue* val = chain.back(); + + return val->count(name) != 0; +} + +ISerializer& JsonInputValueSerializer::operator()(char* value, std::size_t size, const std::string& name) { + assert(false); + throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); + + return *this; +} + +ISerializer& JsonInputValueSerializer::tag(const std::string& name) { + assert(false); + throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); + + return *this; +} + +ISerializer& JsonInputValueSerializer::untagged(uint8_t& value) { + assert(false); + throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); + + return *this; +} + +ISerializer& JsonInputValueSerializer::endTag() { + assert(false); + throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); + + return *this; +} + +} diff --git a/src/serialization/JsonInputValueSerializer.h b/src/serialization/JsonInputValueSerializer.h new file mode 100644 index 0000000000..04647fddb5 --- /dev/null +++ b/src/serialization/JsonInputValueSerializer.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "serialization/ISerializer.h" +#include "serialization/JsonValue.h" + +namespace cryptonote { + +//deserialization +class JsonInputValueSerializer : public ISerializer { +public: + JsonInputValueSerializer(); + virtual ~JsonInputValueSerializer(); + + void setJsonValue(const JsonValue* value); + SerializerType type() const; + + virtual ISerializer& beginObject(const std::string& name) override; + virtual ISerializer& endObject() override; + + virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; + virtual ISerializer& endArray() override; + + virtual ISerializer& operator()(int32_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; + virtual ISerializer& operator()(int64_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; + virtual ISerializer& operator()(double& value, const std::string& name) override; + virtual ISerializer& operator()(std::string& value, const std::string& name) override; + virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; + virtual ISerializer& operator()(bool& value, const std::string& name) override; + virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) override; + + virtual ISerializer& tag(const std::string& name) override; + virtual ISerializer& untagged(uint8_t& value) override; + virtual ISerializer& endTag() override; + + virtual bool hasObject(const std::string& name) override; + + template + ISerializer& operator()(T& value, const std::string& name) { + return ISerializer::operator()(value, name); + } + +private: + const JsonValue* root; + std::vector chain; + std::vector idxs; +}; + +} diff --git a/src/serialization/JsonOutputStreamSerializer.cpp b/src/serialization/JsonOutputStreamSerializer.cpp new file mode 100644 index 0000000000..bb47029525 --- /dev/null +++ b/src/serialization/JsonOutputStreamSerializer.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "serialization/JsonOutputStreamSerializer.h" + +#include +#include + +namespace cryptonote { + +JsonOutputStreamSerializer::JsonOutputStreamSerializer() : root(JsonValue::OBJECT) { +} + +JsonOutputStreamSerializer::~JsonOutputStreamSerializer() { +} + +std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& enumerator) { + out << enumerator.root; + return out; +} + +JsonValue JsonOutputStreamSerializer::getJsonValue() const { + return root; +} + +ISerializer::SerializerType JsonOutputStreamSerializer::type() const { + return ISerializer::OUTPUT; +} + +ISerializer& JsonOutputStreamSerializer::beginObject(const std::string& name) { + if (chain.size() == 0) { + chain.push_back(&root); + return *this; + } + + JsonValue* parent = chain.back(); + JsonValue obj(JsonValue::OBJECT); + + if (parent->isObject()) { + JsonValue& res = parent->insert(name, obj); + chain.push_back(&res); + } else { + JsonValue& res = parent->pushBack(obj); + chain.push_back(&res); + } + + return *this; +} + +ISerializer& JsonOutputStreamSerializer::endObject() { + chain.pop_back(); + return *this; +} + +ISerializer& JsonOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { + JsonValue val(JsonValue::ARRAY); + JsonValue& res = chain.back()->insert(name, val); + chain.push_back(&res); + return *this; +} + +ISerializer& JsonOutputStreamSerializer::endArray() { + chain.pop_back(); + return *this; +} + +ISerializer& JsonOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { + int64_t v = static_cast(value); + return operator()(v, name); +} + +ISerializer& JsonOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { + uint64_t v = static_cast(value); + return operator()(v, name); +} + +ISerializer& JsonOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { + int64_t v = static_cast(value); + return operator()(v, name); +} + +ISerializer& JsonOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { + JsonValue* val = chain.back(); + JsonValue v; + v = static_cast(value); + if (val->isArray()) { + val->pushBack(v); + } else { + val->insert(name, v); + } + return *this; +} + +ISerializer& JsonOutputStreamSerializer::operator()(double& value, const std::string& name) { + JsonValue* val = chain.back(); + JsonValue v; + v = static_cast(value); + + if (val->isArray()) { + val->pushBack(v); + } else { + val->insert(name, v); + } + return *this; +} + +ISerializer& JsonOutputStreamSerializer::operator()(std::string& value, const std::string& name) { + JsonValue* val = chain.back(); + JsonValue v; + v = value; + + if (val->isArray()) { + val->pushBack(v); + } else { + val->insert(name, v); + } + return *this; +} + +ISerializer& JsonOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { + uint64_t v = static_cast(value); + return operator()(v, name); +} + +ISerializer& JsonOutputStreamSerializer::operator()(bool& value, const std::string& name) { + JsonValue* val = chain.back(); + JsonValue v; + v = static_cast(value); + if (val->isArray()) { + val->pushBack(v); + } else { + val->insert(name, v); + } + + return *this; +} + +ISerializer& JsonOutputStreamSerializer::operator()(char* value, std::size_t size, const std::string& name) { + assert(false); + throw std::runtime_error("JsonOutputStreamSerializer doesn't support \"char *\" type of serialization"); + + return *this; +} + +ISerializer& JsonOutputStreamSerializer::tag(const std::string& name) { + assert(false); + throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); + + return *this; +} + +ISerializer& JsonOutputStreamSerializer::untagged(uint8_t& value) { + assert(false); + throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); + + return *this; +} + +ISerializer& JsonOutputStreamSerializer::endTag() { + assert(false); + throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); + + return *this; +} + +bool JsonOutputStreamSerializer::hasObject(const std::string& name) { + assert(false); + throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); + + return false; +} + +} + + diff --git a/src/serialization/JsonOutputStreamSerializer.h b/src/serialization/JsonOutputStreamSerializer.h new file mode 100644 index 0000000000..fd6ebff7ca --- /dev/null +++ b/src/serialization/JsonOutputStreamSerializer.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "serialization/ISerializer.h" +#include "serialization/JsonValue.h" + +#include + +namespace cryptonote { + +class JsonOutputStreamSerializer : public ISerializer { +public: + JsonOutputStreamSerializer(); + virtual ~JsonOutputStreamSerializer(); + + JsonValue getJsonValue() const; + SerializerType type() const; + + virtual ISerializer& beginObject(const std::string& name) override; + virtual ISerializer& endObject() override; + + virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; + virtual ISerializer& endArray() override; + + virtual ISerializer& operator()(int32_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; + virtual ISerializer& operator()(int64_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; + virtual ISerializer& operator()(double& value, const std::string& name) override; + virtual ISerializer& operator()(std::string& value, const std::string& name) override; + virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; + virtual ISerializer& operator()(bool& value, const std::string& name) override; + virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) override; + + virtual ISerializer& tag(const std::string& name) override; + virtual ISerializer& untagged(uint8_t& value) override; + virtual ISerializer& endTag() override; + + virtual bool hasObject(const std::string& name) override; + + template + ISerializer& operator()(T& value, const std::string& name) { + return ISerializer::operator()(value, name); + } + + friend std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& enumerator); + +private: + JsonValue root; + std::vector chain; +}; + +} // namespace cryptonote diff --git a/src/serialization/JsonSerializationDispatcher.h b/src/serialization/JsonSerializationDispatcher.h new file mode 100644 index 0000000000..94f26e2ed2 --- /dev/null +++ b/src/serialization/JsonSerializationDispatcher.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include "serialization/JsonOutputStreamSerializer.h" +#include "serialization/JsonInputStreamSerializer.h" +#include "storages/portable_storage_template_helper.h" + +namespace { +BOOST_TTI_HAS_MEMBER_FUNCTION(serialize) +} //namespace + +namespace cryptonote { + +template +inline typename std::enable_if::value, void>::type SerializeToJson(T& obj, std::string& jsonBuff) { + std::stringstream stream; + JsonOutputStreamSerializer serializer; + + obj.serialize(serializer, ""); + + stream << serializer; + jsonBuff = stream.str(); +} + +template +inline typename std::enable_if::value, void>::type LoadFromJson(T& obj, const std::string& jsonBuff) { + std::stringstream stream(jsonBuff); + JsonInputStreamSerializer serializer(stream); + + obj.serialize(serializer, ""); +} + +//old epee serialization + +template +inline typename std::enable_if::value, void>::type SerializeToJson(T& obj, std::string& jsonBuff) { + epee::serialization::store_t_to_json(obj, jsonBuff); +} + +template +inline typename std::enable_if::value, void>::type LoadFromJson(T& obj, const std::string& jsonBuff) { + epee::serialization::load_t_from_json(obj, jsonBuff); +} + +} //namespace cryptonote diff --git a/src/serialization/JsonValue.cpp b/src/serialization/JsonValue.cpp new file mode 100644 index 0000000000..c9ef17baf5 --- /dev/null +++ b/src/serialization/JsonValue.cpp @@ -0,0 +1,612 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonValue.h" +#include +#include +#include + +namespace cryptonote { + +JsonValue::JsonValue() : d_type(NIL) { +} + +JsonValue::JsonValue(JsonValue::Type type) { + switch(type) { + case OBJECT: + new(d_valueObject)JsonValue::Object(); + break; + case ARRAY: + new(d_valueArray)JsonValue::Array(); + break; + default: + throw std::runtime_error("Wrong JsonValue type. Object or Array are possible only"); + } + + d_type = type; +} + +JsonValue::JsonValue(const JsonValue& other) : d_type(other.d_type) { + switch (d_type) { + case ARRAY: + new(d_valueArray)JsonValue::Array(*reinterpret_cast(other.d_valueArray)); + break; + case BOOL: + d_valueBool = other.d_valueBool; + break; + case INT64: + d_valueInt64 = other.d_valueInt64; + break; + case NIL: + break; + case OBJECT: + new(d_valueObject)JsonValue::Object(*reinterpret_cast(other.d_valueObject)); + break; + case DOUBLE: + d_valueDouble = other.d_valueDouble; + break; + case STRING: + new(d_valueString)std::string(*reinterpret_cast(other.d_valueString)); + break; + default: + throw(std::runtime_error("Invalid type")); + } +} + +JsonValue::~JsonValue() { + destructValue(); +} + +bool JsonValue::isArray() const { + return d_type == ARRAY; +} + +bool JsonValue::isBool() const { + return d_type == BOOL; +} + +bool JsonValue::isInt64() const { + return d_type == INT64; +} + +bool JsonValue::isNil() const { + return d_type == NIL; +} + +bool JsonValue::isObject() const { + return d_type == OBJECT; +} + +bool JsonValue::isDouble() const { + return d_type == DOUBLE; +} + +bool JsonValue::isString() const { + return d_type == STRING; +} + +bool JsonValue::getBool() const { + assert(d_type == BOOL); + if (d_type != BOOL) { + throw(std::runtime_error("Value type is not BOOL")); + } + + return d_valueBool; +} + +int64_t JsonValue::getNumber() const { + assert(d_type == INT64); + if (d_type != INT64) { + throw(std::runtime_error("Value type is not INT64")); + } + + return d_valueInt64; +} + +const JsonValue::Object& JsonValue::getObject() const { + assert(d_type == OBJECT); + if (d_type != OBJECT) { + throw(std::runtime_error("Value type is not OBJECT")); + } + + return *reinterpret_cast(d_valueObject); +} + +double JsonValue::getDouble() const { + assert(d_type == DOUBLE); + if (d_type != DOUBLE) { + throw(std::runtime_error("Value type is not DOUBLE")); + } + + return d_valueDouble; +} + +std::string JsonValue::getString() const { + assert(d_type == STRING); + if (d_type != STRING) { + throw(std::runtime_error("Value type is not STRING")); + } + + return *reinterpret_cast(d_valueString); +} + +const JsonValue& JsonValue::operator()(const std::string& name) const { + assert(d_type == OBJECT); + assert(reinterpret_cast(d_valueObject)->count(name) > 0); + if (d_type != OBJECT) { + throw(std::runtime_error("Value type is not OBJECT")); + } + + return reinterpret_cast(d_valueObject)->at(name); +} + +size_t JsonValue::count(const std::string& name) const { + assert(d_type == OBJECT); + if (d_type != OBJECT) { + throw(std::runtime_error("Value type is not OBJECT")); + } + + return reinterpret_cast(d_valueObject)->count(name); +} + +const JsonValue& JsonValue::operator[](size_t index) const { + assert(d_type == ARRAY); + if (d_type != ARRAY) { + throw(std::runtime_error("Value type is not ARRAY")); + } + + return reinterpret_cast(d_valueArray)->at(index); +} + +size_t JsonValue::size() const { + assert(d_type == ARRAY || d_type == OBJECT); + switch (d_type) { + case OBJECT: + return reinterpret_cast(d_valueString)->size(); + case ARRAY: + return reinterpret_cast(d_valueString)->size(); + default: + throw(std::runtime_error("Value type is not ARRAY or OBJECT")); + } +} + +JsonValue::Array::const_iterator JsonValue::begin() const { + assert(d_type == ARRAY); + if (d_type != ARRAY) { + throw(std::runtime_error("Value type is not ARRAY")); + } + + return reinterpret_cast(d_valueArray)->begin(); +} + +JsonValue::Array::const_iterator JsonValue::end() const { + assert(d_type == ARRAY); + if (d_type != ARRAY) { + throw(std::runtime_error("Value type is not ARRAY")); + } + + return reinterpret_cast(d_valueArray)->end(); +} + +void JsonValue::readArray(std::istream& in) { + char c; + JsonValue::Array value; + + c = in.peek(); + while (true) { + if (!isspace(in.peek())) break; + in.read(&c, 1); + } + + if (c != ']') { + for (;;) { + value.resize(value.size() + 1); + in >> value.back(); + in >> c; + while (isspace(c)) in >> c; + if (c == ']') { + break; + } + + if (c != ',') { + throw(std::runtime_error("Unable to parse")); + } + } + } + + if (d_type != JsonValue::ARRAY) { + destructValue(); + d_type = JsonValue::NIL; + new(d_valueArray)JsonValue::Array; + d_type = JsonValue::ARRAY; + } + + reinterpret_cast(d_valueArray)->swap(value); +} + +void JsonValue::readTrue(std::istream& in) { + char data[3]; + in.read(data, 3); + if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') { + throw(std::runtime_error("Unable to parse")); + } + + if (d_type != JsonValue::BOOL) { + destructValue(); + d_type = JsonValue::BOOL; + } + + d_valueBool = true; +} + +void JsonValue::readFalse(std::istream& in) { + char data[4]; + in.read(data, 4); + if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') { + throw(std::runtime_error("Unable to parse")); + } + + if (d_type != JsonValue::BOOL) { + destructValue(); + d_type = JsonValue::BOOL; + } + + d_valueBool = false; +} + +void JsonValue::readNumber(std::istream& in, char c) { + std::string text; + text += c; + size_t dots = 0; + for (;;) { + int i = in.peek(); + if (i >= '0' && i <= '9') { + in.read(&c, 1); + text += c; + } else if (i == '.') { + in.read(&c, 1); + text += '.'; + ++dots; + } else { + break; + } + } + + if (dots > 0) { + if (dots > 1) { + throw(std::runtime_error("Unable to parse")); + } + + int i = in.peek(); + if (in.peek() == 'e') { + in.read(&c, 1); + text += c; + i = in.peek(); + if (i == '+') { + in.read(&c, 1); + text += c; + i = in.peek(); + } else if (i == '-') { + in.read(&c, 1); + text += c; + i = in.peek(); + } + + if (i < '0' || i > '9') { + throw(std::runtime_error("Unable to parse")); + } + + do { + in.read(&c, 1); + text += c; + i = in.peek(); + } while (i >= '0' && i <= '9'); + } + + double value; + std::istringstream(text) >> value; + if (d_type != JsonValue::DOUBLE) { + destructValue(); + d_type = JsonValue::DOUBLE; + } + + d_valueDouble = value; + } else { + if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) { + throw(std::runtime_error("Unable to parse")); + } + + int64_t value; + std::istringstream(text) >> value; + if (d_type != JsonValue::INT64) { + destructValue(); + d_type = JsonValue::INT64; + } + + d_valueInt64 = value; + } +} + +void JsonValue::readNull(std::istream& in) { + char data[3]; + in.read(data, 3); + if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') { + throw(std::runtime_error("Unable to parse")); + } + + if (d_type != JsonValue::NIL) { + destructValue(); + d_type = JsonValue::NIL; + } +} + +void JsonValue::readObject(std::istream& in) { + char c; + JsonValue::Object value; + in >> c; + while (isspace(c)) in >> c; + + if (c != '}') { + std::string name; + for (;;) { + if (c != '"') { + throw(std::runtime_error("Unable to parse")); + } + + name.clear(); + for (;;) { + in >> c; + if (c == '"') { + break; + } + + if (c == '\\') { + name += c; + in >> c; + } + + name += c; + } + + in >> c; + while (isspace(c)) in >> c; + if (c != ':') { + throw(std::runtime_error("Unable to parse")); + } + + in >> value[name]; + in >> c; + while (isspace(c)) in >> c; + if (c == '}') { + break; + } + + if (c != ',') { + throw(std::runtime_error("Unable to parse")); + } + in >> c; + while (isspace(c)) in >> c; + } + } + + if (d_type != JsonValue::OBJECT) { + destructValue(); + d_type = JsonValue::NIL; + new(d_valueObject)JsonValue::Object; + d_type = JsonValue::OBJECT; + } + + reinterpret_cast(d_valueObject)->swap(value); +} + +void JsonValue::readString(std::istream& in) { + char c; + std::string value; + + for (;;) { + in.read(&c, 1); + if (c == '"') { + break; + } + + if (c == '\\') { + value += c; + in >> c; + } + + value += c; + } + + if (d_type != JsonValue::STRING) { + destructValue(); + d_type = JsonValue::NIL; + new(d_valueString)std::string; + d_type = JsonValue::STRING; + } + + reinterpret_cast(d_valueString)->swap(value); +} + +std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { + char c; + in >> c; + while (isspace(c)) in >> c; + if (c == '[') { + jsonValue.readArray(in); + } else if (c == 't') { + jsonValue.readTrue(in); + } else if (c == 'f') { + jsonValue.readFalse(in); + } else if ((c == '-') || (c >= '0' && c <= '9')) { + jsonValue.readNumber(in, c); + } else if (c == 'n') { + jsonValue.readNull(in); + } else if (c == '{') { + jsonValue.readObject(in); + } else if (c == '"') { + jsonValue.readString(in); + } else { + throw(std::runtime_error("Unable to parse")); + } + + return in; +} + +std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { + if (jsonValue.d_type == JsonValue::ARRAY) { + const JsonValue::Array& array = *reinterpret_cast(jsonValue.d_valueArray); + out << '['; + if (array.size() > 0) { + out << array[0]; + for (size_t i = 1; i < array.size(); ++i) { + out << ',' << array[i]; + } + } + + out << ']'; + } else if (jsonValue.d_type == JsonValue::BOOL) { + out << (jsonValue.d_valueBool ? "true" : "false"); + } else if (jsonValue.d_type == JsonValue::INT64) { + out << jsonValue.d_valueInt64; + } else if (jsonValue.d_type == JsonValue::NIL) { + out << "null"; + } else if (jsonValue.d_type == JsonValue::OBJECT) { + const JsonValue::Object& object = *reinterpret_cast(jsonValue.d_valueObject); + out << '{'; + auto iter = object.begin(); + if (iter != object.end()) { + out << '"' << iter->first << "\":" << iter->second; + ++iter; + for (; iter != object.end(); ++iter) { + out << ",\"" << iter->first << "\":" << iter->second; + } + } + + out << '}'; + } else if (jsonValue.d_type == JsonValue::DOUBLE) { + std::ostringstream stream; + stream << std::fixed << std::setprecision(11) << jsonValue.d_valueDouble; + std::string value = stream.str(); + while (value.size() > 1 && value[value.size() - 2] != '.' && value[value.size() - 1] == '0') { + value.resize(value.size() - 1); + } + + out << value; + } else if (jsonValue.d_type == JsonValue::STRING) { + out << '"' << *reinterpret_cast(jsonValue.d_valueString) << '"'; + } else { + throw(std::runtime_error("Invalid type")); + } + + return out; +} + +void JsonValue::destructValue() { + switch (d_type) { + case ARRAY: + reinterpret_cast(d_valueArray)->~Array(); + break; + case OBJECT: + reinterpret_cast(d_valueObject)->~Object(); + break; + case STRING: + reinterpret_cast(d_valueString)->~basic_string(); + break; + default: + break; + } +} + +JsonValue& JsonValue::pushBack(const JsonValue& val) { + if (d_type != ARRAY) { + throw std::runtime_error("JsonValue error. pushBack is only possible for arrays"); + } + + Array* array = reinterpret_cast(d_valueArray); + array->push_back(val); + + return array->back(); +} + +JsonValue& JsonValue::insert(const std::string key, const JsonValue& value) { + if (d_type != OBJECT) { + throw std::runtime_error("JsonValue error. insert is only possible for objects"); + } + + Object* obj = reinterpret_cast(d_valueObject); + + auto res = obj->insert(std::make_pair(key, value)); + return res.first->second; +} + +JsonValue& JsonValue::operator=(bool value) { + if (d_type != BOOL) { + destructValue(); + d_type = BOOL; + } + + d_valueBool = value; + + return *this; +} + +JsonValue& JsonValue::operator=(int64_t value) { + if (d_type != INT64) { + destructValue(); + d_type = INT64; + } + + d_valueInt64 = value; + + return *this; +} + +//JsonValue& JsonValue::operator=(NilType value) { +// if (d_type != NIL) { +// destructValue(); +// d_type = NIL; +// } +//} + +JsonValue& JsonValue::operator=(double value) { + if (d_type != DOUBLE) { + destructValue(); + d_type = DOUBLE; + } + + d_valueDouble = value; + + return *this; +} + +JsonValue& JsonValue::operator=(const std::string& value) { + if (d_type != STRING) { + destructValue(); + new(d_valueString)std::string; + d_type = STRING; + } + + reinterpret_cast(d_valueString)->assign(value.data(), value.size()); + + return *this; +} + +JsonValue& JsonValue::operator=(const char* value) { + return operator=(std::string(value)); +} + +} //namespace cryptonote diff --git a/src/serialization/JsonValue.h b/src/serialization/JsonValue.h new file mode 100644 index 0000000000..53a82f820a --- /dev/null +++ b/src/serialization/JsonValue.h @@ -0,0 +1,103 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include + +namespace cryptonote { + +class JsonValue { +public: + typedef std::vector Array; + typedef std::map Object; + + enum Type { + ARRAY, + BOOL, + INT64, + NIL, + OBJECT, + DOUBLE, + STRING + }; + + JsonValue(); + JsonValue(Type type); + JsonValue(const JsonValue& other); + ~JsonValue(); + JsonValue& operator=(const JsonValue& other) = delete; + bool isArray() const; + bool isBool() const; + bool isInt64() const; + bool isNil() const; + bool isObject() const; + bool isDouble() const; + bool isString() const; + bool getBool() const; + int64_t getNumber() const; + const Object& getObject() const; + double getDouble() const; + std::string getString() const; + const JsonValue& operator()(const std::string& name) const; + size_t count(const std::string& name) const; + const JsonValue& operator[](size_t index) const; + size_t size() const; + Array::const_iterator begin() const; + Array::const_iterator end() const; + + JsonValue& pushBack(const JsonValue& val); + JsonValue& insert(const std::string key, const JsonValue& value); + + JsonValue& operator=(bool value); + JsonValue& operator=(int64_t value); +// JsonValue& operator=(NilType value); + JsonValue& operator=(double value); + JsonValue& operator=(const std::string& value); + JsonValue& operator=(const char* value); + + + friend std::istream& operator>>(std::istream& in, JsonValue& jsonValue); + friend std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue); + +private: + size_t d_type; + union { + uint8_t d_valueArray[sizeof(Array)]; + bool d_valueBool; + int64_t d_valueInt64; + uint8_t d_valueObject[sizeof(Object)]; + double d_valueDouble; + uint8_t d_valueString[sizeof(std::string)]; + }; + + void destructValue(); + + void readArray(std::istream& in); + void readTrue(std::istream& in); + void readFalse(std::istream& in); + void readNumber(std::istream& in, char c); + void readNull(std::istream& in); + void readObject(std::istream& in); + void readString(std::istream& in); +}; + +} //namespace cryptonote diff --git a/src/serialization/SerializationOverloads.cpp b/src/serialization/SerializationOverloads.cpp new file mode 100644 index 0000000000..ffd5e29689 --- /dev/null +++ b/src/serialization/SerializationOverloads.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "SerializationOverloads.h" + +#include + +namespace cryptonote { + +void readVarint(uint64_t& value, cryptonote::ISerializer& serializer) { + const int bits = std::numeric_limits::digits; + + uint64_t v = 0; + for (int shift = 0;; shift += 7) { + uint8_t b; + serializer.untagged(b); + + if (shift + 7 >= bits && b >= 1 << (bits - shift)) { + throw std::runtime_error("Varint overflow"); + } + + if (b == 0 && shift != 0) { + throw std::runtime_error("Non-canonical varint representation"); + } + + v |= static_cast(b & 0x7f) << shift; + if ((b & 0x80) == 0) { + break; + } + } + + value = v; +} + +void writeVarint(uint64_t& value, cryptonote::ISerializer& serializer) { + uint64_t v = value; + + while (v >= 0x80) { + uint8_t b = (static_cast(v) & 0x7f) | 0x80; + serializer.untagged(b); + v >>= 7; + } + + uint8_t b = static_cast(v); + serializer.untagged(b); +} + + +void serializeVarint(uint64_t& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.tag(name); + + if (serializer.type() == cryptonote::ISerializer::INPUT) { + readVarint(value, serializer); + } else { + writeVarint(value, serializer); + } + + serializer.endTag(); +} + +void serializeVarint(uint32_t& value, const std::string& name, cryptonote::ISerializer& serializer) { + uint64_t v = value; + serializeVarint(v, name, serializer); + value = static_cast(v); +} + +} diff --git a/src/serialization/SerializationOverloads.h b/src/serialization/SerializationOverloads.h new file mode 100644 index 0000000000..a4a58384e6 --- /dev/null +++ b/src/serialization/SerializationOverloads.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" + +#include +#include +#include + +namespace cryptonote { + +void serializeVarint(uint64_t& value, const std::string& name, cryptonote::ISerializer& serializer); +void serializeVarint(uint32_t& value, const std::string& name, cryptonote::ISerializer& serializer); + +template +void serialize(std::vector& value, const std::string& name, cryptonote::ISerializer& serializer) { + std::size_t size = value.size(); + serializer.beginArray(size, name); + value.resize(size); + + for (size_t i = 0; i < size; ++i) { + serializer(value[i], ""); + } + + serializer.endArray(); +} + +template +void serialize(std::unordered_map& value, const std::string& name, cryptonote::ISerializer& serializer) { + std::size_t size; + size = value.size(); + + serializer.beginArray(size, name); + + if (serializer.type() == cryptonote::ISerializer::INPUT) { + value.reserve(size); + + for (size_t i = 0; i < size; ++i) { + K key; + V v; + serializer.beginObject(""); + serializer(key, ""); + serializer(v, ""); + serializer.endObject(); + + value[key] = v; + } + } else { + for (auto kv: value) { + K key; + key = kv.first; + serializer.beginObject(""); + serializer(key, ""); + serializer(kv.second, ""); + serializer.endObject(); + } + } + + serializer.endArray(); +} + +} From b00a96c2661deb9a40b7e0760a8519fe5d140989 Mon Sep 17 00:00:00 2001 From: cryptonotefoundation Date: Tue, 17 Feb 2015 18:13:48 +0300 Subject: [PATCH 20/59] Update README --- README => README.md | 146 ++++++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 58 deletions(-) rename README => README.md (58%) diff --git a/README b/README.md similarity index 58% rename from README rename to README.md index b33aada9d3..a31442cb98 100644 --- a/README +++ b/README.md @@ -1,50 +1,64 @@ -== Preparation == +This is the reference code for [CryptoNote](https://cryptonote.org) cryptocurrency protocol. +* Launch your own CryptoNote currency: [CryptoNote Starter](https://cryptonotestarter.org/) +* CryptoNote reference implementation: [CryptoNoteCoin](https://cryptonote-coin.org) +* Discussion board and support: [CryptoNote Forum](https://forum.cryptonote.org) -1. Create an account on github.com +## CryptoNote forking how-to -2. Fork https://github.com/cryptonotefoundation/cryptonote repository +### Preparation -3. Buy one or two Ubuntu-based dedicated servers (at least 8Gb of RAM) for seed nodes. +1. Create an account on [GitHub.com](github.com) +2. Fork [CryptoNote repository](https://github.com/cryptonotefoundation/cryptonote) +3. Buy one or two Ubuntu-based dedicated servers (at least 2Gb of RAM) for seed nodes. -== First step. Give a name to your coin == -Good name must be unique. Check uniqueness with google and mapofcoins.com or any other similar service. +### First step. Give a name to your coin + +**Good name must be unique.*** Check uniqueness with [google](http://google.com) and [Map of Coins](mapofcoins.com) or any other similar service. Name must be specified twice: -- in file src/cryptonote_config.h - CRYPTONOTE_NAME macro +**1. in file src/cryptonote_config.h** - CRYPTONOTE_NAME macro -Example: +Example: +``` #define CRYPTONOTE_NAME "furiouscoin" +``` -- in CMakeList.txt file - set_property(TARGET daemon PROPERTY OUTPUT_NAME "YOURCOINNAMEd") +**2. in CMakeList.txt file** - set_property(TARGET daemon PROPERTY OUTPUT_NAME "YOURCOINNAMEd") -Example: +Example: +``` set_property(TARGET daemon PROPERTY OUTPUT_NAME "furiouscoind") +``` -Note: You should also change a repository name. +**Note:** You should also change a repository name. -== Second step. Emission logic == +### Second step. Emission logic -1. Total money supply (src/cryptonote_config.h) +**1. Total money supply** (src/cryptonote_config.h) -Total amount of coins to be emitted. Most of CryptoNote based coins use (uint64_t)(-1) (equals to 18446744073709551616). You can define number explicitly (for example UINT64_C(858986905600000000)). +Total amount of coins to be emitted. Most of CryptoNote based coins use `(uint64_t)(-1)` (equals to 18446744073709551616). You can define number explicitly (for example UINT64_C(858986905600000000)). Example: +``` #define MONEY_SUPPLY ((uint64_t)(-1)) +``` -2. Emission curve (src/cryptonote_config.h) +**2. Emission curve** (src/cryptonote_config.h) Be default CryptoNote provides emission formula with slight decrease of block reward with each block. This is different from Bitcoin where block reward halves every 4 years. EMISSION_SPEED_FACTOR macro defines emission curve slope. This parameter is required to calulate block reward. Example: +``` #define EMISSION_SPEED_FACTOR (18) +``` -3. Difficulty target (src/cryptonote_config.h) +**3. Difficulty target** (src/cryptonote_config.h) Difficulty target is an ideal time period between blocks. In case an average time between blocks becomes less than difficulty target, the difficulty increases. Difficulty target is measured in seconds. @@ -57,140 +71,156 @@ Difficulty target directly influences several aspects of coin's behavior: For most coins difficulty target is 60 or 120 seconds. Example: +``` #define DIFFICULTY_TARGET 120 +``` -4. Block reward formula +**4. Block reward formula** In case you are not satisfied with CryptoNote default implementation of block reward logic you can also change it. The implementation is in src/cryptonote_core/cryptonote_basic_impl.cpp: - +``` bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) +``` This function has two parts: -- basic block reward calculation - -uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR; - -- big block penalty calculation - -This is the way CryptoNote protects the block chain from transaction flooding attacks and preserves opportunities for organic network growth at the same time. +- basic block reward calculation: `uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> EMISSION_SPEED_FACTOR;` +- big block penalty calculation: this is the way CryptoNote protects the block chain from transaction flooding attacks and preserves opportunities for organic network growth at the same time. Only the first part of this function is directly related to the emission logic. You can change it the way you want. See MonetaVerde and DuckNote as the examples where this function is modified. -== Third step. Networking == +### Third step. Networking -1. Default ports for P2P and RPC networking (src/cryptonote_config.h) +**1. Default ports for P2P and RPC networking** (src/cryptonote_config.h) P2P port is used by daemons to talk to each other through P2P protocol. RPC port is used by wallet and other programs to talk to daemon. It's better to choose ports that aren't used by other software or coins. See known TCP ports lists: -- http://www.speedguide.net/ports.php -- http://www.networksorcery.com/enp/protocol/ip/ports00000.htm -- http://keir.net/portlist.html +* http://www.speedguide.net/ports.php +* http://www.networksorcery.com/enp/protocol/ip/ports00000.htm +* http://keir.net/portlist.html Example: - +``` #define P2P_DEFAULT_PORT 17236 #define RPC_DEFAULT_PORT 18236 +``` -2. Network identifier (src/p2p/p2p_networks.h) +**2. Network identifier** (src/p2p/p2p_networks.h) This identifier is used in network packages in order not to mix two different cryptocoin networks. Change all the bytes to random values for your network: - +``` const static boost::uuids::uuid CRYPTONOTE_NETWORK = { { 0xA1 ,0x1A, 0xA1, 0x1A , 0xA1, 0x0A , 0xA1, 0x0A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA0, 0x1A, 0xA1, 0x1A} }; +``` -3. Seed nodes (src/p2p/net_node.inl) +**3. Seed nodes** (src/p2p/net_node.inl) -Add ip addresses of your seed nodes to the beginning of "node_server::init(const boost::program_options::variables_map& vm)" function. +Add ip addresses of your seed nodes to the beginning of `node_server::init(const boost::program_options::variables_map& vm)` function. Example: +``` ADD_HARDCODED_SEED_NODE("111.11.11.11:17236"); ADD_HARDCODED_SEED_NODE("222.22.22.22:17236"); +``` -== Fourth step. Transaction fee and related parameters == +### Fourth step. Transaction fee and related parameters -1. Default transaction fee (src/cryptonote_config.h) +**1. Default transaction fee** (src/cryptonote_config.h) Zero default fee can lead to transaction flooding. Transactions cheaper than the default transaction fee wouldn't be accepted by daemons. 100000 value for DEFAULT_FEE is usually enough. Example: +``` #define DEFAULT_FEE 100000 +``` -2. Penalty free block size (src/cryptonote_config.h) +**2. Penalty free block size** (src/cryptonote_config.h) CryptoNote protects chain from tx flooding by reducing block reward for blocks larger than the median block size. However, this rule applies for blocks larger than CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE bytes. Example: +``` #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 20000 +``` -== Fifth step. Genesis block == +### Fifth step. Genesis block -1. Build the binaries with blank genesis tx hex (src/cryptonote_config.h) +**1. Build the binaries with blank genesis tx hex** (src/cryptonote_config.h) You should leave #define GENESIS_COINBASE_TX_HEX blank and compile the binaries without it. Example: +``` #define GENESIS_COINBASE_TX_HEX "" +``` -2. Start the daemon to print out the genesis block +**2. Start the daemon to print out the genesis block** Run your daemon with --print-genesis-tx argument. It will print out the genesis block coinbase transaction hash. Example: +``` cryptonotecoind --print-genesis-tx +``` -3. Copy the printed transaction hash — (src/cryptonote_config.h) +**3. Copy the printed transaction hash** (src/cryptonote_config.h) Copy the tx hash that has been printed by the daemon to GENESIS_COINBASE_TX_HEX in /src/cryptonote_config.h Example: +``` #define GENESIS_COINBASE_TX_HEX "013c01ff0001ffff...785a33d9ebdba68b0" +``` -4. Recompile the binaries +**4. Recompile the binaries** Recompile everything again. Your coin code is ready now. Make an announcement for the potential users and enjoy! -== Building CryptoNote == +## Building CryptoNote + +### On *nix -On *nix: +Dependencies: GCC 4.7.3 or later, CMake 2.8.6 or later, and Boost 1.53 or later (except 1.54, more details here: http://goo.gl/RrCFmA). -Dependencies: GCC 4.7.3 or later, CMake 2.8.6 or later, and Boost 1.53 or later (except 1.54, more details here: http://goo.gl/RrCFmA). You may download them from: -http://gcc.gnu.org/ -http://www.cmake.org/ -http://www.boost.org/ +* http://gcc.gnu.org/ +* http://www.cmake.org/ +* http://www.boost.org/ +* Alternatively, it may be possible to install them using a package manager. To build, change to a directory where this file is located, and run `make'. The resulting executables can be found in build/release/src. -Advanced options: -Parallel build: run `make -j' instead of `make'. -Debug build: run `make build-debug'. -Test suite: run `make test-release' to run tests in addition to building. Running `make test-debug' will do the same to the debug version. -Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++' before running `make'. +**Advanced options:** +* Parallel build: run `make -j` instead of `make`. +* Debug build: run `make build-debug`. +* Test suite: run `make test-release` to run tests in addition to building. Running `make test-debug` will do the same to the debug version. +* Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++` before running `make`. -On Windows: +### On Windows Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.53 or later. You may download them from: -http://www.microsoft.com/ -http://www.cmake.org/ -http://www.boost.org/ +* http://www.microsoft.com/ +* http://www.cmake.org/ +* http://www.boost.org/ To build, change to a directory where this file is located, and run theas commands: +``` mkdir build cd build cmake -G "Visual Studio 11 Win64" .. +``` And then do Build. Good luck! \ No newline at end of file From 1743402759f7b94da1f4248cc5a8efff9e7088fc Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Mon, 6 Apr 2015 17:13:07 +0100 Subject: [PATCH 21/59] Multisignature API, Low level and High level APIs --- CMakeLists.txt | 22 +- ReleaseNotes.txt | 8 + .../keyvalue_serialization_overloads.h | 1 + .../include/storages/levin_abstract_invoke2.h | 2 + .../storages/portable_storage_from_json.h | 1 + contrib/epee/include/string_tools.h | 2 + include/INode.h | 10 + include/IObservable.h | 29 + include/IStreamSerializable.h | 30 + include/ITransaction.h | 180 + include/ITransfersContainer.h | 105 + include/ITransfersSynchronizer.h | 75 + include/IWallet.h | 49 +- src/CMakeLists.txt | 35 +- src/{System => HTTP}/HttpParser.cpp | 0 src/{System => HTTP}/HttpParser.h | 0 src/{System => HTTP}/HttpRequest.cpp | 0 src/{System => HTTP}/HttpRequest.h | 0 src/{System => HTTP}/HttpResponse.cpp | 0 src/{System => HTTP}/HttpResponse.h | 0 src/Platform/Linux/System/Dispatcher.cpp | 160 + src/Platform/Linux/System/Dispatcher.h | 64 + src/{ => Platform/Linux}/System/Event.cpp | 60 +- src/{ => Platform/Linux}/System/Event.h | 16 +- .../Linux/System/InterruptedException.cpp | 3 +- .../Linux/System/InterruptedException.h | 10 +- src/Platform/Linux/System/TcpConnection.cpp | 297 + .../Linux}/System/TcpConnection.h | 14 +- src/Platform/Linux/System/TcpConnector.cpp | 213 + src/Platform/Linux/System/TcpConnector.h | 50 + src/Platform/Linux/System/TcpListener.cpp | 207 + src/Platform/Linux/System/TcpListener.h | 49 + src/Platform/Linux/System/Timer.cpp | 160 + src/Platform/Linux/System/Timer.h | 46 + src/Platform/OSX/System/Dispatcher.cpp | 169 + src/Platform/OSX/System/Dispatcher.h | 66 + src/Platform/OSX/System/Event.cpp | 106 + src/Platform/OSX/System/Event.h | 45 + .../OSX/System/InterruptedException.cpp | 18 + .../OSX/System/InterruptedException.h | 27 + src/Platform/OSX/System/TcpConnection.cpp | 247 + src/Platform/OSX/System/TcpConnection.h | 53 + src/Platform/OSX/System/TcpConnector.cpp | 213 + src/{ => Platform/OSX}/System/TcpConnector.h | 18 +- src/Platform/OSX/System/TcpListener.cpp | 202 + src/{ => Platform/OSX}/System/TcpListener.h | 13 +- src/Platform/OSX/System/Timer.cpp | 137 + src/Platform/OSX/System/Timer.h | 46 + src/Platform/Windows/System/Dispatcher.cpp | 186 + .../Windows/System/Dispatcher.h} | 46 +- src/Platform/Windows/System/Event.cpp | 107 + src/Platform/Windows/System/Event.h | 45 + .../Windows/System/InterruptedException.cpp | 18 + .../Windows/System/InterruptedException.h | 27 + src/Platform/Windows/System/TcpConnection.cpp | 240 + src/Platform/Windows/System/TcpConnection.h | 52 + src/Platform/Windows/System/TcpConnector.cpp | 204 + src/Platform/Windows/System/TcpConnector.h | 49 + src/Platform/Windows/System/TcpListener.cpp | 226 + src/Platform/Windows/System/TcpListener.h | 48 + src/Platform/Windows/System/Timer.cpp | 146 + src/{ => Platform/Windows}/System/Timer.h | 16 +- src/System/System.cpp | 133 - src/System/TcpConnection.cpp | 110 - src/System/TcpConnector.cpp | 67 - src/System/TcpListener.cpp | 88 - src/System/TcpStream.cpp | 2 + src/System/TcpStream.h | 6 +- src/System/Timer.cpp | 70 - src/common/ObserverManager.h | 13 + src/common/ShuffleGenerator.h | 70 + src/common/int-util.h | 2 + src/common/static_assert.h | 9 + ...unordered_containers_boost_serialization.h | 14 +- src/crypto/crypto-ops.c | 3 +- src/crypto/crypto-ops.h | 4 + src/crypto/crypto.cpp | 23 +- src/crypto/crypto.h | 47 +- src/crypto/hash-ops.h | 1 + src/crypto/random.h | 2 + src/crypto/tree-hash.c | 2 +- src/cryptonote_config.h | 9 +- src/cryptonote_core/CoreConfig.cpp | 36 + src/cryptonote_core/CoreConfig.h | 36 + .../IBlockchainStorageObserver.h | 26 + src/cryptonote_core/ICore.h | 61 + src/cryptonote_core/ICoreObserver.h | 29 + src/cryptonote_core/ITxPoolObserver.h | 26 + src/cryptonote_core/MinerConfig.cpp | 55 + src/cryptonote_core/MinerConfig.h | 39 + src/cryptonote_core/Transaction.cpp | 608 ++ .../TransactionApi.h} | 33 +- src/cryptonote_core/TransactionExtra.h | 94 + src/cryptonote_core/account.cpp | 4 + src/cryptonote_core/account.h | 2 + src/cryptonote_core/blockchain_storage.cpp | 83 +- src/cryptonote_core/blockchain_storage.h | 28 +- src/cryptonote_core/cryptonote_basic.h | 3 +- src/cryptonote_core/cryptonote_core.cpp | 109 +- src/cryptonote_core/cryptonote_core.h | 34 +- .../cryptonote_format_utils.cpp | 52 +- src/cryptonote_core/cryptonote_format_utils.h | 4 + .../cryptonote_serialization.cpp | 501 ++ .../cryptonote_serialization.h | 62 + src/cryptonote_core/miner.cpp | 44 +- src/cryptonote_core/miner.h | 5 +- src/cryptonote_core/tx_pool.cpp | 67 +- src/cryptonote_core/tx_pool.h | 8 + .../ICryptonoteProtocolObserver.h | 32 + .../ICryptonoteProtocolQuery.h | 34 + .../cryptonote_protocol_defs.h | 10 + .../cryptonote_protocol_handler.h | 104 +- .../cryptonote_protocol_handler.inl | 216 +- .../cryptonote_protocol_handler_common.h | 10 +- src/daemon/daemon.cpp | 23 +- src/inprocess_node/InProcessNode.cpp | 498 ++ src/inprocess_node/InProcessNode.h | 118 + src/inprocess_node/InProcessNodeErrors.cpp | 27 + src/inprocess_node/InProcessNodeErrors.h | 70 + src/logger/CommonLogger.cpp | 35 + src/logger/CommonLogger.h | 23 + src/logger/ConsoleLogger.cpp | 162 + src/logger/ConsoleLogger.h | 19 + src/logger/ILogger.cpp | 30 + src/logger/ILogger.h | 48 + src/logger/LoggerGroup.cpp | 29 + src/logger/LoggerGroup.h | 20 + src/logger/LoggerMessage.cpp | 94 + src/logger/LoggerMessage.h | 28 + src/logger/LoggerRef.cpp | 20 + src/logger/LoggerRef.h | 21 + src/logger/StreamLogger.cpp | 28 + src/logger/StreamLogger.h | 20 + src/node_rpc_proxy/NodeErrors.h | 2 + src/node_rpc_proxy/NodeRpcProxy.cpp | 57 +- src/node_rpc_proxy/NodeRpcProxy.h | 5 + src/p2p/NetNodeConfig.cpp | 126 + src/p2p/NetNodeConfig.h | 47 + src/p2p/net_node.h | 23 +- src/p2p/net_node.inl | 193 +- src/p2p/net_node_common.h | 7 +- src/p2p/net_peerlist.h | 12 - src/rpc/core_rpc_server.cpp | 76 +- src/rpc/core_rpc_server.h | 2 + src/rpc/core_rpc_server_commands_defs.h | 32 +- .../BinaryInputStreamSerializer.cpp | 197 +- .../BinaryInputStreamSerializer.h | 9 +- .../BinaryOutputStreamSerializer.cpp | 119 +- .../BinaryOutputStreamSerializer.h | 7 +- src/serialization/ISerializer.h | 70 +- src/serialization/IStream.h | 35 + .../JsonInputValueSerializer.cpp | 98 +- src/serialization/JsonInputValueSerializer.h | 12 +- .../JsonOutputStreamSerializer.cpp | 35 +- .../JsonOutputStreamSerializer.h | 8 +- src/serialization/JsonValue.cpp | 1226 ++-- src/serialization/JsonValue.h | 206 +- src/serialization/KVBinaryCommon.h | 66 + .../KVBinaryInputStreamSerializer.cpp | 200 + .../KVBinaryInputStreamSerializer.h | 52 + .../KVBinaryOutputStreamSerializer.cpp | 252 + .../KVBinaryOutputStreamSerializer.h | 103 + .../MemoryStream.cpp} | 5 +- src/serialization/MemoryStream.h | 82 + src/serialization/SerializationOverloads.cpp | 112 +- src/serialization/SerializationOverloads.h | 38 +- src/simplewallet/simplewallet.cpp | 1225 ++-- src/simplewallet/simplewallet.h | 49 +- src/transfers/BlockchainSynchronizer.cpp | 552 ++ src/transfers/BlockchainSynchronizer.h | 144 + .../CommonTypes.h} | 35 +- src/transfers/IBlockchainSynchronizer.h | 63 + src/transfers/IObservableImpl.h | 40 + src/transfers/SerializationHelpers.h | 48 + src/transfers/SynchronizationState.cpp | 120 + src/transfers/SynchronizationState.h | 63 + src/transfers/TransfersConsumer.cpp | 464 ++ src/transfers/TransfersConsumer.h | 88 + src/transfers/TransfersContainer.cpp | 811 +++ src/transfers/TransfersContainer.h | 286 + src/transfers/TransfersSubscription.cpp | 81 + src/transfers/TransfersSubscription.h | 53 + src/transfers/TransfersSynchronizer.cpp | 236 + src/transfers/TransfersSynchronizer.h | 63 + src/transfers/TypeHelpers.h | 50 + src/version.h.in | 4 +- src/wallet/KeysStorage.cpp | 41 + src/wallet/KeysStorage.h | 40 + src/wallet/LegacyKeysImporter.cpp | 97 + src/wallet/LegacyKeysImporter.h | 27 + src/wallet/Wallet.cpp | 440 +- src/wallet/Wallet.h | 67 +- src/wallet/WalletAsyncContextCounter.cpp | 2 +- src/wallet/WalletErrors.h | 4 +- src/wallet/WalletEvent.h | 17 +- src/wallet/WalletHelper.cpp | 20 + src/wallet/WalletHelper.h | 35 + src/wallet/WalletRequest.h | 47 +- src/wallet/WalletSendTransactionContext.h | 10 +- src/wallet/WalletSerialization.cpp | 72 + src/wallet/WalletSerialization.h | 81 +- src/wallet/WalletSerializer.cpp | 179 + src/wallet/WalletSerializer.h | 55 + src/wallet/WalletSynchronizationContext.h | 55 - src/wallet/WalletSynchronizer.cpp | 491 -- src/wallet/WalletSynchronizer.h | 96 - src/wallet/WalletTransactionSender.cpp | 201 +- src/wallet/WalletTransactionSender.h | 20 +- src/wallet/WalletTransferDetails.cpp | 173 - src/wallet/WalletTransferDetails.h | 97 - src/wallet/WalletUnconfirmedTransactions.cpp | 97 +- src/wallet/WalletUnconfirmedTransactions.h | 62 +- src/wallet/WalletUserTransactionsCache.cpp | 211 +- src/wallet/WalletUserTransactionsCache.h | 65 +- src/wallet/WalletUtils.h | 17 + src/wallet/wallet2.cpp | 910 --- src/wallet/wallet2.h | 563 -- src/wallet/wallet_rpc_server.cpp | 382 +- src/wallet/wallet_rpc_server.h | 17 +- tests/CMakeLists.txt | 37 +- tests/core_proxy/core_proxy.cpp | 13 +- tests/core_tests/TransactionBuilder.cpp | 52 +- tests/core_tests/TransactionBuilder.h | 18 +- tests/core_tests/chaingen.h | 9 +- tests/core_tests/chaingen_main.cpp | 3 +- tests/core_tests/double_spend.cpp | 42 +- tests/core_tests/double_spend.h | 2 + tests/core_tests/random_outs.cpp | 111 + tests/core_tests/random_outs.h | 40 + tests/crypto/main.cpp | 14 + tests/crypto/tests.txt | 6366 +++++++++-------- tests/functional_tests/main.cpp | 126 - .../transactions_flow_test.cpp | 291 - .../functional_tests/transactions_flow_test.h | 24 - ...ransactions_generation_from_blockchain.cpp | 141 - .../BaseFunctionalTest.cpp | 345 + .../integration_test_lib/BaseFunctionalTest.h | 160 + .../CoreRpcSerialization.cpp | 57 + .../CoreRpcSerialization.h | 36 + tests/integration_test_lib/Logger.cpp | 31 + tests/integration_test_lib/Logger.h | 56 + tests/integration_test_lib/RPCTestNode.cpp | 208 + tests/integration_test_lib/RPCTestNode.h | 48 + tests/integration_test_lib/TestNode.h | 37 + tests/integration_tests/main.cpp | 826 +++ tests/transfers_tests/globals.h | 36 + tests/transfers_tests/main.cpp | 48 + tests/transfers_tests/tests.cpp | 520 ++ tests/unit_tests/EventWaiter.cpp | 37 + tests/unit_tests/EventWaiter.h | 35 + tests/unit_tests/ICoreStub.cpp | 94 + tests/unit_tests/ICoreStub.h | 63 + .../ICryptonoteProtocolQueryStub.cpp | 42 + .../unit_tests/ICryptonoteProtocolQueryStub.h | 40 + tests/unit_tests/INodeStubs.cpp | 157 +- tests/unit_tests/INodeStubs.h | 38 +- tests/unit_tests/TestBlockchainGenerator.cpp | 116 +- tests/unit_tests/TestBlockchainGenerator.h | 16 +- tests/unit_tests/TransactionApi.cpp | 317 + tests/unit_tests/TransactionApiHelpers.h | 102 + tests/unit_tests/TransfersObserver.h | 47 + tests/unit_tests/base58.cpp | 7 +- .../binary_serialization_compatibility.cpp | 544 ++ tests/unit_tests/block_reward.cpp | 2 +- tests/unit_tests/serialization_kv.cpp | 240 + .../serialization_structs_comparators.h | 138 + tests/unit_tests/shuffle.cpp | 78 + tests/unit_tests/test_BcS.cpp | 1434 ++++ tests/unit_tests/test_TransfersConsumer.cpp | 904 +++ tests/unit_tests/test_TransfersContainer.cpp | 1209 ++++ .../test_TransfersContainerKeyImage.cpp | 732 ++ .../unit_tests/test_TransfersSubscription.cpp | 156 + tests/unit_tests/test_inprocess_node.cpp | 287 + tests/unit_tests/test_transfers.cpp | 429 ++ tests/unit_tests/test_tx_pool_detach.cpp | 443 ++ tests/unit_tests/test_wallet.cpp | 712 +- tests/unit_tests/tx_pool.cpp | 28 + 277 files changed, 30175 insertions(+), 9760 deletions(-) create mode 100644 include/IObservable.h create mode 100644 include/IStreamSerializable.h create mode 100644 include/ITransaction.h create mode 100644 include/ITransfersContainer.h create mode 100644 include/ITransfersSynchronizer.h mode change 100644 => 100755 src/CMakeLists.txt rename src/{System => HTTP}/HttpParser.cpp (100%) mode change 100644 => 100755 rename src/{System => HTTP}/HttpParser.h (100%) mode change 100644 => 100755 rename src/{System => HTTP}/HttpRequest.cpp (100%) mode change 100644 => 100755 rename src/{System => HTTP}/HttpRequest.h (100%) mode change 100644 => 100755 rename src/{System => HTTP}/HttpResponse.cpp (100%) mode change 100644 => 100755 rename src/{System => HTTP}/HttpResponse.h (100%) mode change 100644 => 100755 create mode 100755 src/Platform/Linux/System/Dispatcher.cpp create mode 100755 src/Platform/Linux/System/Dispatcher.h rename src/{ => Platform/Linux}/System/Event.cpp (57%) mode change 100644 => 100755 rename src/{ => Platform/Linux}/System/Event.h (88%) mode change 100644 => 100755 rename tests/functional_tests/transactions_generation_from_blockchain.h => src/Platform/Linux/System/InterruptedException.cpp (90%) mode change 100644 => 100755 rename utils/test-static-assert.c => src/Platform/Linux/System/InterruptedException.h (89%) mode change 100644 => 100755 create mode 100755 src/Platform/Linux/System/TcpConnection.cpp rename src/{ => Platform/Linux}/System/TcpConnection.h (87%) mode change 100644 => 100755 create mode 100755 src/Platform/Linux/System/TcpConnector.cpp create mode 100755 src/Platform/Linux/System/TcpConnector.h create mode 100755 src/Platform/Linux/System/TcpListener.cpp create mode 100755 src/Platform/Linux/System/TcpListener.h create mode 100755 src/Platform/Linux/System/Timer.cpp create mode 100755 src/Platform/Linux/System/Timer.h create mode 100755 src/Platform/OSX/System/Dispatcher.cpp create mode 100755 src/Platform/OSX/System/Dispatcher.h create mode 100755 src/Platform/OSX/System/Event.cpp create mode 100755 src/Platform/OSX/System/Event.h create mode 100755 src/Platform/OSX/System/InterruptedException.cpp create mode 100755 src/Platform/OSX/System/InterruptedException.h create mode 100755 src/Platform/OSX/System/TcpConnection.cpp create mode 100755 src/Platform/OSX/System/TcpConnection.h create mode 100755 src/Platform/OSX/System/TcpConnector.cpp rename src/{ => Platform/OSX}/System/TcpConnector.h (81%) mode change 100644 => 100755 create mode 100755 src/Platform/OSX/System/TcpListener.cpp rename src/{ => Platform/OSX}/System/TcpListener.h (86%) mode change 100644 => 100755 create mode 100755 src/Platform/OSX/System/Timer.cpp create mode 100755 src/Platform/OSX/System/Timer.h create mode 100755 src/Platform/Windows/System/Dispatcher.cpp rename src/{System/System.h => Platform/Windows/System/Dispatcher.h} (60%) mode change 100644 => 100755 create mode 100755 src/Platform/Windows/System/Event.cpp create mode 100755 src/Platform/Windows/System/Event.h create mode 100755 src/Platform/Windows/System/InterruptedException.cpp create mode 100755 src/Platform/Windows/System/InterruptedException.h create mode 100755 src/Platform/Windows/System/TcpConnection.cpp create mode 100755 src/Platform/Windows/System/TcpConnection.h create mode 100755 src/Platform/Windows/System/TcpConnector.cpp create mode 100755 src/Platform/Windows/System/TcpConnector.h create mode 100755 src/Platform/Windows/System/TcpListener.cpp create mode 100755 src/Platform/Windows/System/TcpListener.h create mode 100755 src/Platform/Windows/System/Timer.cpp rename src/{ => Platform/Windows}/System/Timer.h (82%) mode change 100644 => 100755 delete mode 100644 src/System/System.cpp delete mode 100644 src/System/TcpConnection.cpp delete mode 100644 src/System/TcpConnector.cpp delete mode 100644 src/System/TcpListener.cpp mode change 100644 => 100755 src/System/TcpStream.cpp mode change 100644 => 100755 src/System/TcpStream.h delete mode 100644 src/System/Timer.cpp create mode 100644 src/common/ShuffleGenerator.h create mode 100755 src/common/static_assert.h create mode 100644 src/cryptonote_core/CoreConfig.cpp create mode 100644 src/cryptonote_core/CoreConfig.h create mode 100644 src/cryptonote_core/IBlockchainStorageObserver.h create mode 100755 src/cryptonote_core/ICore.h create mode 100644 src/cryptonote_core/ICoreObserver.h create mode 100755 src/cryptonote_core/ITxPoolObserver.h create mode 100644 src/cryptonote_core/MinerConfig.cpp create mode 100644 src/cryptonote_core/MinerConfig.h create mode 100644 src/cryptonote_core/Transaction.cpp rename src/{wallet/WalletTxSendingState.h => cryptonote_core/TransactionApi.h} (68%) create mode 100644 src/cryptonote_core/TransactionExtra.h mode change 100644 => 100755 src/cryptonote_core/cryptonote_core.cpp create mode 100644 src/cryptonote_core/cryptonote_serialization.cpp create mode 100644 src/cryptonote_core/cryptonote_serialization.h create mode 100644 src/cryptonote_protocol/ICryptonoteProtocolObserver.h create mode 100644 src/cryptonote_protocol/ICryptonoteProtocolQuery.h create mode 100644 src/inprocess_node/InProcessNode.cpp create mode 100644 src/inprocess_node/InProcessNode.h create mode 100644 src/inprocess_node/InProcessNodeErrors.cpp create mode 100644 src/inprocess_node/InProcessNodeErrors.h create mode 100755 src/logger/CommonLogger.cpp create mode 100755 src/logger/CommonLogger.h create mode 100755 src/logger/ConsoleLogger.cpp create mode 100755 src/logger/ConsoleLogger.h create mode 100755 src/logger/ILogger.cpp create mode 100755 src/logger/ILogger.h create mode 100755 src/logger/LoggerGroup.cpp create mode 100755 src/logger/LoggerGroup.h create mode 100755 src/logger/LoggerMessage.cpp create mode 100755 src/logger/LoggerMessage.h create mode 100755 src/logger/LoggerRef.cpp create mode 100755 src/logger/LoggerRef.h create mode 100755 src/logger/StreamLogger.cpp create mode 100755 src/logger/StreamLogger.h create mode 100644 src/p2p/NetNodeConfig.cpp create mode 100644 src/p2p/NetNodeConfig.h create mode 100644 src/serialization/IStream.h create mode 100644 src/serialization/KVBinaryCommon.h create mode 100644 src/serialization/KVBinaryInputStreamSerializer.cpp create mode 100644 src/serialization/KVBinaryInputStreamSerializer.h create mode 100644 src/serialization/KVBinaryOutputStreamSerializer.cpp create mode 100644 src/serialization/KVBinaryOutputStreamSerializer.h rename src/{platform/msc/inline_c.h => serialization/MemoryStream.cpp} (92%) create mode 100644 src/serialization/MemoryStream.h create mode 100644 src/transfers/BlockchainSynchronizer.cpp create mode 100644 src/transfers/BlockchainSynchronizer.h rename src/{wallet/WalletTxSendingState.cpp => transfers/CommonTypes.h} (62%) create mode 100644 src/transfers/IBlockchainSynchronizer.h create mode 100644 src/transfers/IObservableImpl.h create mode 100644 src/transfers/SerializationHelpers.h create mode 100644 src/transfers/SynchronizationState.cpp create mode 100644 src/transfers/SynchronizationState.h create mode 100644 src/transfers/TransfersConsumer.cpp create mode 100644 src/transfers/TransfersConsumer.h create mode 100644 src/transfers/TransfersContainer.cpp create mode 100644 src/transfers/TransfersContainer.h create mode 100755 src/transfers/TransfersSubscription.cpp create mode 100644 src/transfers/TransfersSubscription.h create mode 100644 src/transfers/TransfersSynchronizer.cpp create mode 100644 src/transfers/TransfersSynchronizer.h create mode 100644 src/transfers/TypeHelpers.h create mode 100644 src/wallet/KeysStorage.cpp create mode 100644 src/wallet/KeysStorage.h create mode 100755 src/wallet/LegacyKeysImporter.cpp create mode 100755 src/wallet/LegacyKeysImporter.h mode change 100644 => 100755 src/wallet/Wallet.cpp create mode 100755 src/wallet/WalletHelper.cpp create mode 100755 src/wallet/WalletHelper.h create mode 100644 src/wallet/WalletSerialization.cpp mode change 100644 => 100755 src/wallet/WalletSerialization.h create mode 100644 src/wallet/WalletSerializer.cpp create mode 100644 src/wallet/WalletSerializer.h delete mode 100644 src/wallet/WalletSynchronizationContext.h delete mode 100644 src/wallet/WalletSynchronizer.cpp delete mode 100644 src/wallet/WalletSynchronizer.h delete mode 100644 src/wallet/WalletTransferDetails.cpp delete mode 100644 src/wallet/WalletTransferDetails.h delete mode 100644 src/wallet/wallet2.cpp delete mode 100644 src/wallet/wallet2.h mode change 100644 => 100755 tests/CMakeLists.txt create mode 100644 tests/core_tests/random_outs.cpp create mode 100644 tests/core_tests/random_outs.h delete mode 100644 tests/functional_tests/main.cpp delete mode 100644 tests/functional_tests/transactions_flow_test.cpp delete mode 100644 tests/functional_tests/transactions_flow_test.h delete mode 100644 tests/functional_tests/transactions_generation_from_blockchain.cpp create mode 100755 tests/integration_test_lib/BaseFunctionalTest.cpp create mode 100755 tests/integration_test_lib/BaseFunctionalTest.h create mode 100755 tests/integration_test_lib/CoreRpcSerialization.cpp create mode 100755 tests/integration_test_lib/CoreRpcSerialization.h create mode 100755 tests/integration_test_lib/Logger.cpp create mode 100755 tests/integration_test_lib/Logger.h create mode 100755 tests/integration_test_lib/RPCTestNode.cpp create mode 100755 tests/integration_test_lib/RPCTestNode.h create mode 100755 tests/integration_test_lib/TestNode.h create mode 100755 tests/integration_tests/main.cpp create mode 100644 tests/transfers_tests/globals.h create mode 100644 tests/transfers_tests/main.cpp create mode 100644 tests/transfers_tests/tests.cpp create mode 100644 tests/unit_tests/EventWaiter.cpp create mode 100644 tests/unit_tests/EventWaiter.h create mode 100755 tests/unit_tests/ICoreStub.cpp create mode 100755 tests/unit_tests/ICoreStub.h create mode 100644 tests/unit_tests/ICryptonoteProtocolQueryStub.cpp create mode 100644 tests/unit_tests/ICryptonoteProtocolQueryStub.h create mode 100644 tests/unit_tests/TransactionApi.cpp create mode 100644 tests/unit_tests/TransactionApiHelpers.h create mode 100644 tests/unit_tests/TransfersObserver.h create mode 100644 tests/unit_tests/binary_serialization_compatibility.cpp create mode 100644 tests/unit_tests/serialization_kv.cpp create mode 100644 tests/unit_tests/serialization_structs_comparators.h create mode 100644 tests/unit_tests/shuffle.cpp create mode 100755 tests/unit_tests/test_BcS.cpp create mode 100644 tests/unit_tests/test_TransfersConsumer.cpp create mode 100644 tests/unit_tests/test_TransfersContainer.cpp create mode 100644 tests/unit_tests/test_TransfersContainerKeyImage.cpp create mode 100644 tests/unit_tests/test_TransfersSubscription.cpp create mode 100644 tests/unit_tests/test_inprocess_node.cpp create mode 100644 tests/unit_tests/test_transfers.cpp create mode 100755 tests/unit_tests/test_tx_pool_detach.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c2794e7bb0..102399c4cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,11 +12,19 @@ if(APPLE) include_directories(SYSTEM /usr/include/malloc) endif() +if(MSVC) +include_directories(src/Platform/Windows) +elseif(APPLE) +include_directories(src/Platform/OSX) +else() +include_directories(src/Platform/Linux) +endif() + + set(STATIC ${MSVC} CACHE BOOL "Link libraries statically") 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 /D_VARIADIC_MAX=8 /FIinline_c.h /D__SSE4_1__") - # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline") + 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 /D_VARIADIC_MAX=8 /D__SSE4_1__") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760") if(STATIC) foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) @@ -35,7 +43,7 @@ else() if(CMAKE_C_COMPILER_ID STREQUAL "Clang") set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function") else() - set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized") + set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=clobbered -Wno-error=unused-but-set-variable") endif() if(MINGW) set(WARNINGS "${WARNINGS} -Wno-error=unused-value") @@ -46,13 +54,7 @@ else() endif() set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes") set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers") - try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c11") - if(STATIC_ASSERT_RES) - set(STATIC_ASSERT_FLAG "") - else() - set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert") - endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes") if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0") diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index cc0f8a41d3..d00ac9aaf4 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,11 @@ +Release notes 1.0.3 + +- Multisignature API +- Low level API +- High level API improvements +- Instant transaction pool notifications +- Fully refactored simplewallet + Release notes 1.0.2 - Transaction history for simplewallet diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h index d52d09c050..d53f167fb0 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -27,6 +27,7 @@ #pragma once #include +#include #include #include diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h index 1b32c51d13..da12c10607 100644 --- a/contrib/epee/include/storages/levin_abstract_invoke2.h +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -114,6 +114,7 @@ namespace epee const_cast(out_struct).store(stg);//TODO: add true const support to searilzation std::string buff_to_send, buff_to_recv; stg.store_to_binary(buff_to_send); + int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool { t_result result_struct = AUTO_VAL_INIT(result_struct); @@ -131,6 +132,7 @@ namespace epee return false; } result_struct.load(stg_ret); + cb(code, result_struct, context); return true; }, inv_timeout); diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h index d3d84cc64f..3ca61c02de 100644 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -27,6 +27,7 @@ #pragma once #include +#include #include "parserse_base_utils.h" #include "file_io_utils.h" diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 281fbd9404..c5d09421b5 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -38,6 +38,8 @@ #include #include +#include + #include "warnings.h" #ifndef OUT diff --git a/include/INode.h b/include/INode.h index f6a013a76d..364a5d4da3 100644 --- a/include/INode.h +++ b/include/INode.h @@ -35,6 +35,7 @@ class INodeObserver { virtual void peerCountUpdated(size_t count) {} virtual void localBlockchainUpdated(uint64_t height) {} virtual void lastKnownBlockHeightUpdated(uint64_t height) {} + virtual void poolChanged() {} }; struct OutEntry { @@ -47,6 +48,12 @@ struct OutsForAmount { std::vector outs; }; +struct BlockCompleteEntry { + crypto::hash blockHash; + cryptonote::blobdata block; + std::list txs; +}; + class INode { public: typedef std::function Callback; @@ -61,11 +68,14 @@ class INode { virtual size_t getPeerCount() const = 0; virtual uint64_t getLastLocalBlockHeight() const = 0; virtual uint64_t getLastKnownBlockHeight() const = 0; + virtual uint64_t getLastLocalBlockTimestamp() const = 0; virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) = 0; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) = 0; }; } diff --git a/include/IObservable.h b/include/IObservable.h new file mode 100644 index 0000000000..6694420511 --- /dev/null +++ b/include/IObservable.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace CryptoNote { + +template +class IObservable { +public: + virtual void addObserver(T* observer) = 0; + virtual void removeObserver(T* observer) = 0; +}; + +} diff --git a/include/IStreamSerializable.h b/include/IStreamSerializable.h new file mode 100644 index 0000000000..0472ec2ab2 --- /dev/null +++ b/include/IStreamSerializable.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace CryptoNote { + +class IStreamSerializable { +public: + virtual void save(std::ostream& os) = 0; + virtual void load(std::istream& in) = 0; +}; + +} diff --git a/include/ITransaction.h b/include/ITransaction.h new file mode 100644 index 0000000000..bed5de3ab0 --- /dev/null +++ b/include/ITransaction.h @@ -0,0 +1,180 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace CryptoNote { + +typedef std::array PublicKey; +typedef std::array SecretKey; +typedef std::array KeyImage; +typedef std::array Hash; +typedef std::vector Blob; + +struct AccountAddress { + PublicKey spendPublicKey; + PublicKey viewPublicKey; +}; + +struct AccountKeys { + AccountAddress address; + SecretKey spendSecretKey; + SecretKey viewSecretKey; +}; + +struct KeyPair { + PublicKey publicKey; + SecretKey secretKey; +}; + +namespace TransactionTypes { + + enum class InputType : uint8_t { Invalid, Key, Multisignature, Generating }; + enum class OutputType : uint8_t { Invalid, Key, Multisignature }; + + struct InputKey { + uint64_t amount; + std::vector keyOffsets; + KeyImage keyImage; // double spending protection + }; + + struct InputMultisignature { + uint64_t amount; + uint32_t signatures; + uint64_t outputIndex; + }; + + struct OutputKey { + uint64_t amount; + PublicKey key; + }; + + struct OutputMultisignature { + uint64_t amount; + std::vector keys; + uint32_t requiredSignatures; + }; + + struct GlobalOutput { + PublicKey targetKey; + uint64_t outputIndex; + }; + + typedef std::vector GlobalOutputsContainer; + + struct OutputKeyInfo { + PublicKey transactionPublicKey; + size_t transactionIndex; + size_t outputInTransaction; + }; + + struct InputKeyInfo { + uint64_t amount; + GlobalOutputsContainer outputs; + OutputKeyInfo realOutput; + }; + +} + +// +// ITransactionReader +// +class ITransactionReader { +public: + virtual ~ITransactionReader() { } + + virtual Hash getTransactionHash() const = 0; + virtual Hash getTransactionPrefixHash() const = 0; + virtual PublicKey getTransactionPublicKey() const = 0; + virtual uint64_t getUnlockTime() const = 0; + + // extra + virtual bool getPaymentId(Hash& paymentId) const = 0; + virtual bool getExtraNonce(std::string& nonce) const = 0; + + // inputs + virtual size_t getInputCount() const = 0; + virtual uint64_t getInputTotalAmount() const = 0; + virtual TransactionTypes::InputType getInputType(size_t index) const = 0; + virtual void getInput(size_t index, TransactionTypes::InputKey& input) const = 0; + virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const = 0; + + // outputs + virtual size_t getOutputCount() const = 0; + virtual uint64_t getOutputTotalAmount() const = 0; + virtual TransactionTypes::OutputType getOutputType(size_t index) const = 0; + virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const = 0; + virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const = 0; + + // signatures + virtual size_t getRequiredSignaturesCount(size_t inputIndex) const = 0; + virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const = 0; + + // various checks + virtual bool validateInputs() const = 0; + virtual bool validateOutputs() const = 0; + virtual bool validateSignatures() const = 0; + + // serialized transaction + virtual Blob getTransactionData() const = 0; +}; + +// +// ITransactionWriter +// +class ITransactionWriter { +public: + + virtual ~ITransactionWriter() { } + + // transaction parameters + virtual void setUnlockTime(uint64_t unlockTime) = 0; + + // extra + virtual void setPaymentId(const Hash& paymentId) = 0; + virtual void setExtraNonce(const std::string& nonce) = 0; + + // Inputs/Outputs + virtual size_t addInput(const TransactionTypes::InputKey& input) = 0; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0; + virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0; + + virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0; + virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) = 0; + + // transaction info + virtual bool getTransactionSecretKey(SecretKey& key) const = 0; + virtual void setTransactionSecretKey(const SecretKey& key) = 0; + + // signing + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0; + virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; +}; + +class ITransaction : + public ITransactionReader, + public ITransactionWriter { +public: + virtual ~ITransaction() { } + +}; + +} diff --git a/include/ITransfersContainer.h b/include/ITransfersContainer.h new file mode 100644 index 0000000000..d6c8408031 --- /dev/null +++ b/include/ITransfersContainer.h @@ -0,0 +1,105 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include "crypto/hash.h" +#include "ITransaction.h" +#include "IObservable.h" +#include "IStreamSerializable.h" + +namespace CryptoNote { + +const uint64_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits::max(); + +struct TransactionInformation { + // transaction info + Hash transactionHash; + PublicKey publicKey; + uint64_t blockHeight; + uint64_t timestamp; + uint64_t unlockTime; + uint64_t totalAmountIn; + uint64_t totalAmountOut; + std::vector extra; + Hash paymentId; +}; + + +struct TransactionOutputInformation { + // output info + TransactionTypes::OutputType type; + uint64_t amount; + uint64_t globalOutputIndex; + uint32_t outputInTransaction; + + // transaction info + Hash transactionHash; + PublicKey transactionPublicKey; + + union { + PublicKey outputKey; // Type: Key + uint32_t requiredSignatures; // Type: Multisignature + }; +}; + +struct TransactionSpentOutputInformation: public TransactionOutputInformation { + uint64_t spendingBlockHeight; + uint64_t timestamp; + Hash spendingTransactionHash; + KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key + uint32_t inputInTransaction; +}; + +class ITransfersContainer : public IStreamSerializable { +public: + enum Flags : uint32_t { + // state + IncludeStateUnlocked = 0x01, + IncludeStateLocked = 0x02, + IncludeStateSoftLocked = 0x04, + // output type + IncludeTypeKey = 0x100, + IncludeTypeMultisignature = 0x200, + // combinations + IncludeStateAll = 0xff, + IncludeTypeAll = 0xff00, + + IncludeKeyUnlocked = IncludeTypeKey | IncludeStateUnlocked, + IncludeKeyNotUnlocked = IncludeTypeKey | IncludeStateLocked | IncludeStateSoftLocked, + + IncludeAllLocked = IncludeTypeAll | IncludeStateLocked | IncludeStateSoftLocked, + IncludeAllUnlocked = IncludeTypeAll | IncludeStateUnlocked, + IncludeAll = IncludeTypeAll | IncludeStateAll, + + IncludeDefault = IncludeKeyUnlocked + }; + + virtual size_t transfersCount() = 0; + virtual size_t transactionsCount() = 0; + virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0; + virtual void getOutputs(std::vector& transfers, uint32_t flags = IncludeDefault) = 0; + virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; + virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; + virtual void getUnconfirmedTransactions(std::vector& transactions) = 0; + virtual std::vector getSpentOutputs() = 0; +}; + +} diff --git a/include/ITransfersSynchronizer.h b/include/ITransfersSynchronizer.h new file mode 100644 index 0000000000..c9734c4d07 --- /dev/null +++ b/include/ITransfersSynchronizer.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "ITransaction.h" +#include "ITransfersContainer.h" +#include "IStreamSerializable.h" + +namespace CryptoNote { + +struct SynchronizationStart { + uint64_t timestamp; + uint64_t height; +}; + +struct AccountSubscription { + AccountKeys keys; + SynchronizationStart syncStart; + size_t transactionSpendableAge; +}; + +class ITransfersSubscription; + +class ITransfersObserver { +public: + virtual void onError(ITransfersSubscription* object, + uint64_t height, std::error_code ec) {} + + virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) {} + + /** + * \note The sender must guarantee that onTransactionDeleted() is called only after onTransactionUpdated() is called + * for the same \a transactionHash. + */ + virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { } +}; + +class ITransfersSubscription : public IObservable < ITransfersObserver > { +public: + virtual ~ITransfersSubscription() {} + + virtual AccountAddress getAddress() = 0; + virtual ITransfersContainer& getContainer() = 0; +}; + +class ITransfersSynchronizer : public IStreamSerializable { +public: + virtual ~ITransfersSynchronizer() {} + + virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) = 0; + virtual bool removeSubscription(const AccountAddress& acc) = 0; + virtual void getSubscriptions(std::vector& subscriptions) = 0; + // returns nullptr if address is not found + virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) = 0; +}; + +} diff --git a/include/IWallet.h b/include/IWallet.h index 26a2dcab34..b15a627671 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -41,23 +41,48 @@ const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits::max(); const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); +enum class TransactionState : uint8_t { + Active, // --> {Deleted} + Deleted, // --> {Active} + + Sending, // --> {Active, Cancelled, Failed} + Cancelled, // --> {} + Failed // --> {} +}; + struct TransactionInfo { - TransferId firstTransferId; - size_t transferCount; - int64_t totalAmount; - uint64_t fee; - TransactionHash hash; - bool isCoinbase; - uint64_t blockHeight; - uint64_t timestamp; - std::string extra; + TransferId firstTransferId; + size_t transferCount; + int64_t totalAmount; + uint64_t fee; + uint64_t sentTime; + uint64_t unlockTime; + TransactionHash hash; + bool isCoinbase; + uint64_t blockHeight; + uint64_t timestamp; + std::string extra; + TransactionState state; +}; + +typedef std::array WalletPublicKey; +typedef std::array WalletSecretKey; + +struct WalletAccountKeys { + WalletPublicKey viewPublicKey; + WalletSecretKey viewSecretKey; + WalletPublicKey spendPublicKey; + WalletSecretKey spendSecretKey; }; class IWalletObserver { public: + virtual ~IWalletObserver() {} + virtual void initCompleted(std::error_code result) {} virtual void saveCompleted(std::error_code result) {} - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) {} + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} + virtual void synchronizationCompleted(std::error_code result) {} virtual void actualBalanceUpdated(uint64_t actualBalance) {} virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} virtual void externalTransactionCreated(TransactionId transactionId) {} @@ -73,7 +98,9 @@ class IWallet { virtual void initAndGenerate(const std::string& password) = 0; virtual void initAndLoad(std::istream& source, const std::string& password) = 0; + virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) = 0; virtual void shutdown() = 0; + virtual void reset() = 0; virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0; @@ -95,6 +122,8 @@ class IWallet { virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; virtual std::error_code cancelTransaction(size_t transferId) = 0; + + virtual void getAccountKeys(WalletAccountKeys& keys) = 0; }; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt old mode 100644 new mode 100755 index 9f18c3d548..9015679249 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,6 +12,19 @@ file(GLOB_RECURSE CONN_TOOL connectivity_tool/*) file(GLOB_RECURSE WALLET wallet/*) file(GLOB_RECURSE MINER miner/*) file(GLOB_RECURSE NODE_RPC_PROXY node_rpc_proxy/*) +file(GLOB_RECURSE TRANSFERS transfers/*) +if(MSVC) +file(GLOB_RECURSE SYSTEM Platform/Windows/System/*) +elseif(APPLE) +file(GLOB_RECURSE SYSTEM Platform/OSX/System/*) +else() +file(GLOB_RECURSE SYSTEM Platform/Linux/System/*) +endif() +file(GLOB_RECURSE SERIALIZATION serialization/*) +file(GLOB_RECURSE LOGGER logger/*) +file(GLOB_RECURSE INPROCESS_NODE inprocess_node/*) +file(GLOB_RECURSE HTTP HTTP/*) + source_group(common FILES ${COMMON}) source_group(crypto FILES ${CRYPTO}) @@ -20,32 +33,42 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) source_group(daemon FILES ${DAEMON}) source_group(p2p FILES ${P2P}) source_group(rpc FILES ${RPC}) +source_group(System FILES ${SYSTEM} ${HTTP}) source_group(simplewallet FILES ${SIMPLEWALLET}) source_group(connectivity-tool FILES ${CONN_TOOL}) source_group(wallet FILES ${WALLET}) source_group(simpleminer FILES ${MINER}) source_group(node_rpc_proxy FILES ${NODE_RPC_PROXY}) +source_group(transfers FILES ${TRANSFERS}) +source_group(logger FILES ${LOGGER}) +source_group(inprocess_node FILES ${INPROCESS_NODE}) add_library(common ${COMMON}) add_library(crypto ${CRYPTO}) +add_library(serialization ${SERIALIZATION}) add_library(cryptonote_core ${CRYPTONOTE_CORE}) +add_library(node_rpc_proxy ${NODE_RPC_PROXY}) +add_library(inprocess_node ${INPROCESS_NODE}) add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL}) add_executable(connectivity_tool ${CONN_TOOL}) add_executable(simpleminer ${MINER}) -target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(connectivity_tool epee cryptonote_core crypto common ${Boost_LIBRARIES}) -target_link_libraries(simpleminer epee cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static serialization ${Boost_LIBRARIES}) +target_link_libraries(connectivity_tool epee rpc cryptonote_core crypto common serialization ${Boost_LIBRARIES}) +target_link_libraries(simpleminer epee cryptonote_core crypto common serialization ${Boost_LIBRARIES}) add_library(rpc ${RPC}) +add_library(System ${SYSTEM} ${HTTP} System/TcpStream.cpp System/TcpStream.h) add_library(wallet ${WALLET}) add_executable(simplewallet ${SIMPLEWALLET} ) -target_link_libraries(simplewallet epee wallet rpc cryptonote_core crypto common upnpc-static ${Boost_LIBRARIES}) -add_library(node_rpc_proxy ${NODE_RPC_PROXY}) +target_link_libraries(simplewallet epee wallet transfers rpc cryptonote_core crypto common upnpc-static node_rpc_proxy serialization ${Boost_LIBRARIES}) +add_library(logger ${LOGGER}) +add_library(transfers ${TRANSFERS}) + add_dependencies(connectivity_tool version) add_dependencies(daemon version) add_dependencies(rpc version) add_dependencies(simplewallet version) -set_property(TARGET common crypto cryptonote_core rpc wallet node_rpc_proxy PROPERTY FOLDER "libs") +set_property(TARGET common crypto cryptonote_core rpc System wallet node_rpc_proxy serialization logger transfers inprocess_node PROPERTY FOLDER "libs") set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog") set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind") diff --git a/src/System/HttpParser.cpp b/src/HTTP/HttpParser.cpp old mode 100644 new mode 100755 similarity index 100% rename from src/System/HttpParser.cpp rename to src/HTTP/HttpParser.cpp diff --git a/src/System/HttpParser.h b/src/HTTP/HttpParser.h old mode 100644 new mode 100755 similarity index 100% rename from src/System/HttpParser.h rename to src/HTTP/HttpParser.h diff --git a/src/System/HttpRequest.cpp b/src/HTTP/HttpRequest.cpp old mode 100644 new mode 100755 similarity index 100% rename from src/System/HttpRequest.cpp rename to src/HTTP/HttpRequest.cpp diff --git a/src/System/HttpRequest.h b/src/HTTP/HttpRequest.h old mode 100644 new mode 100755 similarity index 100% rename from src/System/HttpRequest.h rename to src/HTTP/HttpRequest.h diff --git a/src/System/HttpResponse.cpp b/src/HTTP/HttpResponse.cpp old mode 100644 new mode 100755 similarity index 100% rename from src/System/HttpResponse.cpp rename to src/HTTP/HttpResponse.cpp diff --git a/src/System/HttpResponse.h b/src/HTTP/HttpResponse.h old mode 100644 new mode 100755 similarity index 100% rename from src/System/HttpResponse.h rename to src/HTTP/HttpResponse.h diff --git a/src/Platform/Linux/System/Dispatcher.cpp b/src/Platform/Linux/System/Dispatcher.cpp new file mode 100755 index 0000000000..753d612910 --- /dev/null +++ b/src/Platform/Linux/System/Dispatcher.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Dispatcher.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace System; + +void Dispatcher::contextProcedureStatic(void *context) { + reinterpret_cast(context)->contextProcedure(); +} + +Dispatcher::Dispatcher() { + epoll = ::epoll_create1(0); + if (epoll == -1) { + std::cerr << "kqueue() fail errno=" << errno << std::endl; + } else { + currentContext = new ucontext_t; + if (getcontext(reinterpret_cast(currentContext)) == -1) { + std::cerr << "getcontext() fail errno=" << errno << std::endl; + } else { + contextCount = 0; + return; + } + } + throw std::runtime_error("Dispatcher::Dispatcher"); +} + +Dispatcher::~Dispatcher() { + assert(resumingContexts.empty()); + assert(reusableContexts.size() == contextCount); + assert(spawningProcedures.empty()); + assert(reusableContexts.size() == allocatedStacks.size()); + while (!reusableContexts.empty()) { + delete[] allocatedStacks.top(); + allocatedStacks.pop(); + delete static_cast(reusableContexts.top()); + reusableContexts.pop(); + } + + while (!timers.empty()) { + timers.pop(); + } + + if (-1 == close(epoll)) { + std::cerr << "close() fail errno=" << errno << std::endl; + } +} + +void* Dispatcher::getCurrentContext() const { + return currentContext; +} + +int Dispatcher::getEpoll() const { + return epoll; +} + +void Dispatcher::pushContext(void* context) { + resumingContexts.push(context); +} + +void Dispatcher::spawn(std::function&& procedure) { + ucontext_t *context; + if (reusableContexts.empty()) { + context = new ucontext_t; + if (getcontext(context) == -1) { //makecontext precondition + std::cerr << "getcontext() fail errno=" << errno << std::endl; + throw std::runtime_error("Dispatcher::spawn()"); + } + auto stackPointer = new uint8_t[64 * 1024]; + context->uc_stack.ss_sp = stackPointer; + allocatedStacks.push(stackPointer); + context->uc_stack.ss_size = 64 * 1024; + makecontext(context, (void(*)())contextProcedureStatic, 1, reinterpret_cast(this)); + ++contextCount; + } else { + context = static_cast(reusableContexts.top()); + reusableContexts.pop(); + } + + resumingContexts.push(context); + spawningProcedures.emplace(std::move(procedure)); +} + +void Dispatcher::clear() { +//TODO +} + +void Dispatcher::yield() { + void* context; + for (;;) { + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + assert(context); + break; + } + + epoll_event event; + int count = epoll_wait(epoll, &event, 1, -1); + + if (count == 1) { + if ((event.events & EPOLLOUT) != 0) { + context = static_cast(event.data.ptr)->writeContext; + } else { + context = static_cast(event.data.ptr)->context; + } + assert(context); + break; + } + + if (errno != EINTR) { + std::cerr << "epoll_wait() failed, errno=" << errno << std::endl; + throw std::runtime_error("Dispatcher::yield()"); + } + } + + if (context != currentContext) { + ucontext_t* oldContext = static_cast(currentContext); + currentContext = context; + if (-1 == swapcontext(oldContext, static_cast(context))) { + std::cerr << "swapcontext() failed, errno=" << errno << std::endl; + throw std::runtime_error("Dispatcher::yield()"); + } + } +} + +void Dispatcher::contextProcedure() { + void* context = currentContext; + for (;;) { + assert(!spawningProcedures.empty()); + std::function procedure = std::move(spawningProcedures.front()); + spawningProcedures.pop(); + procedure(); + reusableContexts.push(context); + yield(); + } +} diff --git a/src/Platform/Linux/System/Dispatcher.h b/src/Platform/Linux/System/Dispatcher.h new file mode 100755 index 0000000000..c62a0ff494 --- /dev/null +++ b/src/Platform/Linux/System/Dispatcher.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +class Dispatcher { +public: + Dispatcher(); + Dispatcher(const Dispatcher&) = delete; + ~Dispatcher(); + Dispatcher& operator=(const Dispatcher&) = delete; + void spawn(std::function&& procedure); + void yield(); + void clear(); + + struct ContextExt { + void *context; + void *writeContext; //required workaround + }; +private: + friend class Event; + friend class DispatcherAccessor; + friend class TcpConnection; + friend class TcpConnector; + friend class TcpListener; + friend class Timer; + int epoll; + void* currentContext; + std::size_t contextCount; + std::queue resumingContexts; + std::stack reusableContexts; + std::stack allocatedStacks; + std::queue> spawningProcedures; + std::stack timers; + + int getEpoll() const; + void pushContext(void* context); + void* getCurrentContext() const; + + void contextProcedure(); + static void contextProcedureStatic(void* context); +}; + +} diff --git a/src/System/Event.cpp b/src/Platform/Linux/System/Event.cpp old mode 100644 new mode 100755 similarity index 57% rename from src/System/Event.cpp rename to src/Platform/Linux/System/Event.cpp index ddc2a507f2..008bb332b1 --- a/src/System/Event.cpp +++ b/src/Platform/Linux/System/Event.cpp @@ -17,28 +17,35 @@ #include "Event.h" #include -#include "System.h" +#include +#include "Dispatcher.h" -struct Event::Waiter { - Event::Waiter* next; +using namespace System; + +namespace { + +struct Waiter { + Waiter* next; void* context; }; -Event::Event() : system(nullptr) { } -Event::Event(System& system) : system(&system), first(nullptr), state(false) { +Event::Event() : dispatcher(nullptr) { +} + +Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { } -Event::Event(Event&& other) : system(other.system) { - if (other.system != nullptr) { +Event::Event(Event&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { first = other.first; if (other.first != nullptr) { last = other.last; } state = other.state; - other.system = nullptr; + other.dispatcher = nullptr; } } @@ -48,51 +55,52 @@ Event::~Event() { Event& Event::operator=(Event&& other) { assert(first == nullptr); - system = other.system; - if (other.system != nullptr) { + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { first = other.first; if (other.first != nullptr) { last = other.last; } state = other.state; - other.system = nullptr; + other.dispatcher = nullptr; } return *this; } bool Event::get() const { - assert(system != nullptr); + assert(dispatcher != nullptr); return state; } void Event::clear() { - assert(system != nullptr); + assert(dispatcher != nullptr); state = false; } void Event::set() { - assert(system != nullptr); + assert(dispatcher != nullptr); state = true; - for (Waiter* waiter = first; waiter != nullptr; waiter = waiter->next) { - system->pushContext(waiter->context); + for (Waiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + dispatcher->pushContext(waiter->context); } first = nullptr; } void Event::wait() { - assert(system != nullptr); - Waiter waiter = {nullptr, system->getCurrentContext()}; - if (first != nullptr) { - last->next = &waiter; - } else { - first = &waiter; - } + assert(dispatcher != nullptr); + if (!state) { + Waiter waiter = { nullptr, dispatcher->getCurrentContext() }; + if (first != nullptr) { + static_cast(last)->next = &waiter; + } else { + first = &waiter; + } - last = &waiter; - while (!state) { - system->yield(); + last = &waiter; + dispatcher->yield(); + assert(dispatcher != nullptr); } } diff --git a/src/System/Event.h b/src/Platform/Linux/System/Event.h old mode 100644 new mode 100755 similarity index 88% rename from src/System/Event.h rename to src/Platform/Linux/System/Event.h index 50125024d9..aab4d1a4f2 --- a/src/System/Event.h +++ b/src/Platform/Linux/System/Event.h @@ -17,12 +17,14 @@ #pragma once -class System; +namespace System { + +class Dispatcher; class Event { public: Event(); - explicit Event(System& system); + explicit Event(Dispatcher& dispatcher); Event(const Event&) = delete; Event(Event&& other); ~Event(); @@ -34,10 +36,10 @@ class Event { void wait(); private: - struct Waiter; - - System* system; - Waiter* first; - Waiter* last; + Dispatcher* dispatcher; + void* first; + void* last; bool state; }; + +} diff --git a/tests/functional_tests/transactions_generation_from_blockchain.h b/src/Platform/Linux/System/InterruptedException.cpp old mode 100644 new mode 100755 similarity index 90% rename from tests/functional_tests/transactions_generation_from_blockchain.h rename to src/Platform/Linux/System/InterruptedException.cpp index aee92ba369..0e268f4f6f --- a/tests/functional_tests/transactions_generation_from_blockchain.h +++ b/src/Platform/Linux/System/InterruptedException.cpp @@ -15,5 +15,4 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . - -bool transactions_generation_from_blockchain(std::string& blockchain_path); +#include "InterruptedException.h" diff --git a/utils/test-static-assert.c b/src/Platform/Linux/System/InterruptedException.h old mode 100644 new mode 100755 similarity index 89% rename from utils/test-static-assert.c rename to src/Platform/Linux/System/InterruptedException.h index ad2bca80d9..67762ed304 --- a/utils/test-static-assert.c +++ b/src/Platform/Linux/System/InterruptedException.h @@ -15,9 +15,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include +#pragma once -static_assert(1, "FAIL"); -int main(int argc, char *argv[]) { - return 0; -} \ No newline at end of file +#include + +class InterruptedException : public std::exception { +}; diff --git a/src/Platform/Linux/System/TcpConnection.cpp b/src/Platform/Linux/System/TcpConnection.cpp new file mode 100755 index 0000000000..7ef7cb038e --- /dev/null +++ b/src/Platform/Linux/System/TcpConnection.cpp @@ -0,0 +1,297 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnection.h" +#include +#include +#include +#include +#include +#include +#include +#include "Dispatcher.h" +#include "InterruptedException.h" + +using namespace System; + +namespace { + +struct ConnectionContext : public Dispatcher::ContextExt { + bool interrupted; +}; + +} + +TcpConnection::TcpConnection() : dispatcher(nullptr) { +} + +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), context(nullptr) { + epoll_event connectionEvent; + connectionEvent.data.fd = connection; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) { + std::cerr << errno << std::endl; + throw std::runtime_error("epoll_ctl() fail"); + } +} + +TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + connection = other.connection; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpConnection::~TcpConnection() { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(connection) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + } + } +} + +TcpConnection& TcpConnection::operator=(TcpConnection&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(connection) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnection::operator="); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + connection = other.connection; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpConnection::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void TcpConnection::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + ConnectionContext *context2 = static_cast(context); + if (!context2->interrupted) { + + epoll_event connectionEvent; + connectionEvent.data.fd = connection; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + std::cerr << errno << std::endl; + throw std::runtime_error("epoll_ctl() fail"); + } + + context2->interrupted = true; + + if (context2->context != nullptr) { + dispatcher->pushContext(context2->context); + } + + if (context2->writeContext != nullptr) { + dispatcher->pushContext(context2->writeContext); + } + } + } + + stopped = true; +} + +size_t TcpConnection::read(uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(context == nullptr || static_cast(context)->context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + ssize_t transferred = ::recv(connection, (void *)data, size, 0); + if (transferred == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + std::cerr << "recv failed, result=" << errno << '.' << std::endl; + } else { + epoll_event connectionEvent; + connectionEvent.data.fd = connection; + + ConnectionContext context2; + if (context == nullptr) { + context2.writeContext = nullptr; + context2.interrupted = false; + context2.context = dispatcher->getCurrentContext(); + context = &context2; + connectionEvent.events = EPOLLIN | EPOLLONESHOT; + } else { + assert(static_cast(context)->writeContext != nullptr); + connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; + } + + connectionEvent.data.ptr = context; + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + } else { + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + if (static_cast(context)->interrupted) { + context = nullptr; + throw InterruptedException(); + } + + assert(static_cast(context)->context == context2.context); + if (static_cast(context)->writeContext != nullptr) { //write is presented, rearm + static_cast(context)->context = nullptr; + + epoll_event connectionEvent; + connectionEvent.data.fd = connection; + connectionEvent.events = EPOLLOUT | EPOLLONESHOT; + connectionEvent.data.ptr = context; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnection::read"); + } + } else { + context = nullptr; + } + + ssize_t transferred = ::recv(connection, (void *)data, size, 0); + if (transferred == -1) { + std::cerr << "recv failed, errno=" << errno << '.' << std::endl; + } else { + if (transferred == 0) { + std::cerr << "recv return after yield with 0 bytes" << std::endl; + + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; + } else { + std::cerr << "recv getsockopt retval = " << retval << std::endl; + } + } + + assert(transferred <= size); + return transferred; + } + } + } + + throw std::runtime_error("TcpConnection::read"); + } + + assert(transferred <= size); + return transferred; +} + +void TcpConnection::write(const uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(context == nullptr || static_cast(context)->writeContext == nullptr); + if (stopped) { + throw InterruptedException(); + } + + if (size == 0) { + if (shutdown(connection, SHUT_WR) == -1) { + std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnection::write"); + } + + return; + } + + ssize_t transferred = ::send(connection, (void *)data, size, 0); + if (transferred == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + std::cerr << "send failed, result=" << errno << '.' << std::endl; + } else { + epoll_event connectionEvent; + connectionEvent.data.fd = connection; + + ConnectionContext context2; + if (context == nullptr) { + context2.context = nullptr; + context2.interrupted = false; + context2.writeContext = dispatcher->getCurrentContext(); + context = &context2; + connectionEvent.events = EPOLLOUT | EPOLLONESHOT; + } else { + assert(static_cast(context)->context != nullptr); + connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; + } + + connectionEvent.data.ptr = context; + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + } else { + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.writeContext == dispatcher->getCurrentContext()); + if (static_cast(context)->interrupted) { + context = nullptr; + throw InterruptedException(); + } + + assert(static_cast(context)->writeContext == context2.writeContext); + if (static_cast(context)->context != nullptr) { //read is presented, rearm + static_cast(context)->writeContext = nullptr; + + epoll_event connectionEvent; + connectionEvent.data.fd = connection; + connectionEvent.events = EPOLLIN | EPOLLONESHOT; + connectionEvent.data.ptr = context; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnection::write"); + } + } else { + context = nullptr; + } + + ssize_t transferred = ::send(connection, (void *)data, size, 0); + if (transferred == -1) { + std::cerr << "send failed, errno=" << errno << '.' << std::endl; + } else { + if (transferred == 0) { + throw std::runtime_error("send transferred 0 bytes."); + } + + assert(transferred == size); + return; + } + } + } + + throw std::runtime_error("TcpConnection::write"); + } +} diff --git a/src/System/TcpConnection.h b/src/Platform/Linux/System/TcpConnection.h old mode 100644 new mode 100755 similarity index 87% rename from src/System/TcpConnection.h rename to src/Platform/Linux/System/TcpConnection.h index ce2829e984..9c4993fd26 --- a/src/System/TcpConnection.h +++ b/src/Platform/Linux/System/TcpConnection.h @@ -19,8 +19,11 @@ #include #include +#include -class System; +namespace System { + +class Dispatcher; class TcpConnection { public: @@ -39,9 +42,12 @@ class TcpConnection { friend class TcpConnector; friend class TcpListener; - explicit TcpConnection(System& system, void* socket); + explicit TcpConnection(Dispatcher& dispatcher, int socket); - System* system; - void* socket; + Dispatcher* dispatcher; + int connection; bool stopped; + void* context; }; + +} diff --git a/src/Platform/Linux/System/TcpConnector.cpp b/src/Platform/Linux/System/TcpConnector.cpp new file mode 100755 index 0000000000..85542efc1d --- /dev/null +++ b/src/Platform/Linux/System/TcpConnector.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnector.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Dispatcher.h" +#include "TcpConnection.h" +#include "InterruptedException.h" + +using namespace System; + +namespace { + +struct ConnectorContext : public Dispatcher::ContextExt { + int connection; + bool interrupted; +}; + +} + +TcpConnector::TcpConnector() : dispatcher(nullptr) { +} + +TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { +} + +TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + address = other.address; + port = other.port; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpConnector::~TcpConnector() { +} + +TcpConnector& TcpConnector::operator=(TcpConnector&& other) { + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + address = other.address; + port = other.port; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpConnector::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +TcpConnection TcpConnector::connect() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + std::ostringstream portStream; + portStream << port; + addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; + addrinfo *addressInfos; + int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); + if (result == -1) { + std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl; + } else { + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + std::uniform_int_distribution distribution(0, count - 1); + std::size_t index = distribution(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + sockaddr_in addressData = *reinterpret_cast(addressInfo->ai_addr); + freeaddrinfo(addressInfo); + int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == -1) { + std::cerr << "socket failed, errno=" << errno << '.' << std::endl; + } else { + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + std::cerr << "bind failed, errno=" << errno << '.' << std::endl; + } else { + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + std::cerr << "fcntl() failed errno=" << errno << std::endl; + } else { + int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); + if (result == -1) { + if (errno == EINPROGRESS) { + ConnectorContext context2; + context2.writeContext = dispatcher->getCurrentContext(); + context2.context = nullptr; + context2.interrupted = false; + context2.connection = connection; + context = &context2; + + epoll_event connectEvent; + connectEvent.data.fd = connection; + connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT; + connectEvent.data.ptr = context; + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + } else { + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.writeContext == dispatcher->getCurrentContext()); + assert(context == &context2); + context = nullptr; + context2.writeContext = nullptr; + if (context2.interrupted) { + if (close(connection) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + + throw InterruptedException(); + } + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + } else { + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; + } else { + if (retval != 0) { + std::cerr << "connect failed; getsockopt retval = " << retval << std::endl; + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + } + } + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + + if (close(connection) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + } + } + + throw std::runtime_error("TcpConnector::connect"); +} + +void TcpConnector::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + ConnectorContext* context2 = static_cast(context); + if (!context2->interrupted) { + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, context2->connection, NULL) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnector::stop"); + } + + dispatcher->pushContext(context2->writeContext); + context2->interrupted = true; + } + } + + stopped = true; +} diff --git a/src/Platform/Linux/System/TcpConnector.h b/src/Platform/Linux/System/TcpConnector.h new file mode 100755 index 0000000000..2add7e3dd8 --- /dev/null +++ b/src/Platform/Linux/System/TcpConnector.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +class Dispatcher; +class TcpConnection; + +class TcpConnector { +public: + TcpConnector(); + TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpConnector(const TcpConnector&) = delete; + TcpConnector(TcpConnector&& other); + ~TcpConnector(); + TcpConnector& operator=(const TcpConnector&) = delete; + TcpConnector& operator=(TcpConnector&& other); + void start(); + void stop(); + TcpConnection connect(); + +private: + Dispatcher* dispatcher; + std::string address; + uint16_t port; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/Linux/System/TcpListener.cpp b/src/Platform/Linux/System/TcpListener.cpp new file mode 100755 index 0000000000..221163d8b6 --- /dev/null +++ b/src/Platform/Linux/System/TcpListener.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpListener.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Dispatcher.h" +#include "TcpConnection.h" +#include "InterruptedException.h" + +using namespace System; + +namespace { + +struct ListenerContext : public Dispatcher::ContextExt { + bool interrupted; +}; + +} + +TcpListener::TcpListener() : dispatcher(nullptr) { +} + +TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + listener = other.listener; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpListener::~TcpListener() { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(listener) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + } + } +} + +TcpListener& TcpListener::operator=(TcpListener&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(listener) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpListener::operator="); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + listener = other.listener; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpListener::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == -1) { + std::cerr << "socket failed, errno=" << errno << std::endl; + } else { + int flags = fcntl(listener, F_GETFL, 0); + if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) { + std::cerr << "fcntl() failed errno=" << errno << std::endl; + } else { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = INADDR_ANY; + if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { + std::cerr << "bind failed, errno=" << errno << std::endl; + } else if (listen(listener, SOMAXCONN) != 0) { + std::cerr << "listen failed, errno=" << errno << std::endl; + } else { + epoll_event listenEvent; + listenEvent.data.fd = listener; + listenEvent.events = 0; + listenEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + } else { + stopped = false; + context = nullptr; + return; + } + } + } + + if (close(listener) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + } + + throw std::runtime_error("TcpListener::TcpListener"); +} + +TcpConnection TcpListener::accept() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + ListenerContext context2; + context2.context = dispatcher->getCurrentContext(); + context2.writeContext = nullptr; + context2.interrupted = false; + + epoll_event listenEvent; + listenEvent.data.fd = listener; + listenEvent.events = EPOLLIN | EPOLLONESHOT; + listenEvent.data.ptr = &context2; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + } else { + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(context2.writeContext == nullptr); + assert(context == &context2); + context = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + if (close(listener) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + throw InterruptedException(); + } + + sockaddr inAddr; + socklen_t inLen = sizeof(inAddr); + int connection = ::accept(listener, &inAddr, &inLen); + if (connection == -1) { + std::cerr << "accept() failed, errno=" << errno << '.' << std::endl; + } else { + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + std::cerr << "fcntl() failed errno=" << errno << std::endl; + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + + throw std::runtime_error("TcpListener::accept"); +} + +void TcpListener::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + ListenerContext* context2 = static_cast(context); + if (!context2->interrupted) { + context2->interrupted = true; + + epoll_event listenEvent; + listenEvent.data.fd = listener; + listenEvent.events = 0; + listenEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpListener::stop"); + } + + dispatcher->pushContext(context2->context); + } + } + + stopped = true; +} diff --git a/src/Platform/Linux/System/TcpListener.h b/src/Platform/Linux/System/TcpListener.h new file mode 100755 index 0000000000..6f2b51ec46 --- /dev/null +++ b/src/Platform/Linux/System/TcpListener.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +class Dispatcher; +class TcpConnection; + +class TcpListener { +public: + TcpListener(); + TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpListener(const TcpListener&) = delete; + TcpListener(TcpListener&& other); + ~TcpListener(); + TcpListener& operator=(const TcpListener&) = delete; + TcpListener& operator=(TcpListener&& other); + void start(); + void stop(); + TcpConnection accept(); + +private: + Dispatcher* dispatcher; + int listener; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/Linux/System/Timer.cpp b/src/Platform/Linux/System/Timer.cpp new file mode 100755 index 0000000000..b773ea81b8 --- /dev/null +++ b/src/Platform/Linux/System/Timer.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Timer.h" +#include +#include +#include +#include +#include +#include +#include +#include "Dispatcher.h" +#include "InterruptedException.h" + +using namespace System; + +namespace { + +struct TimerContext : public Dispatcher::ContextExt { + Dispatcher* dispatcher; + bool interrupted; +}; + +} + +Timer::Timer() : dispatcher(nullptr) { +} + +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { + timer = timerfd_create(CLOCK_MONOTONIC, 0); + epoll_event timerEvent; + timerEvent.data.fd = timer; + timerEvent.events = 0; + timerEvent.data.ptr = nullptr; + + if (epoll_ctl(this->dispatcher->getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("Timer::Timer"); + } +} + +Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + timer = other.timer; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +Timer::~Timer() { + if (dispatcher != nullptr) { + close(timer); + } +} + +Timer& Timer::operator=(Timer&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + close(timer); + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + timer = other.timer; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void Timer::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void Timer::sleep(std::chrono::milliseconds duration) { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + auto seconds = std::chrono::duration_cast(duration); + + itimerspec expires; + expires.it_interval.tv_nsec = expires.it_interval.tv_sec = 0; + expires.it_value.tv_sec = seconds.count(); + expires.it_value.tv_nsec = std::chrono::duration_cast(duration - seconds).count(); + timerfd_settime(timer, 0, &expires, NULL); + + TimerContext context2; + context2.dispatcher = dispatcher; + context2.context = dispatcher->getCurrentContext(); + context2.writeContext = nullptr; + context2.interrupted = false; + + epoll_event timerEvent; + timerEvent.data.fd = timer; + timerEvent.events = EPOLLIN | EPOLLONESHOT; + timerEvent.data.ptr = &context2; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("Timer::sleep"); + } + + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(context2.writeContext == nullptr); + assert(context == &context2); + context = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + throw InterruptedException(); + } +} + +void Timer::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + TimerContext* context2 = reinterpret_cast(context); + if (context2->context != nullptr) { + epoll_event timerEvent; + timerEvent.data.fd = timer; + timerEvent.events = 0; + timerEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { + std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("Timer::sleep"); + } + + dispatcher->pushContext(context2->context); + context2->interrupted = true; + } + } + + stopped = true; +} diff --git a/src/Platform/Linux/System/Timer.h b/src/Platform/Linux/System/Timer.h new file mode 100755 index 0000000000..1269281c45 --- /dev/null +++ b/src/Platform/Linux/System/Timer.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; + +class Timer { +public: + Timer(); + explicit Timer(Dispatcher& dispatcher); + Timer(const Timer&) = delete; + Timer(Timer&& other); + ~Timer(); + Timer& operator=(const Timer&) = delete; + Timer& operator=(Timer&& other); + void start(); + void stop(); + void sleep(std::chrono::milliseconds duration); + +private: + Dispatcher* dispatcher; + int timer; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/OSX/System/Dispatcher.cpp b/src/Platform/OSX/System/Dispatcher.cpp new file mode 100755 index 0000000000..b731b3c24d --- /dev/null +++ b/src/Platform/OSX/System/Dispatcher.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Dispatcher.h" +#include +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include +#include + +using namespace System; + +void Dispatcher::contextProcedureStatic(void *context) { + reinterpret_cast(context)->contextProcedure(); +} + +Dispatcher::Dispatcher() : lastCreatedTimer(0) { + kqueue = ::kqueue(); + if (kqueue == -1) { + std::cerr << "kqueue() fail errno=" << errno << std::endl; + } else { + currentContext = new ucontext_t; + if (getcontext(reinterpret_cast(currentContext)) == -1) { + std::cerr << "getcontext() fail errno=" << errno << std::endl; + } else { + contextCount = 0; + return; + } + } + throw std::runtime_error("Dispatcher::Dispatcher"); +} + +Dispatcher::~Dispatcher() { + assert(resumingContexts.empty()); + assert(reusableContexts.size() == contextCount); + assert(spawningProcedures.empty()); + assert(reusableContexts.size() == allocatedStacks.size()); + while (!reusableContexts.empty()) { + delete[] allocatedStacks.top(); + allocatedStacks.pop(); + delete static_cast(reusableContexts.top()); + reusableContexts.pop(); + } + + while (!timers.empty()) { + timers.pop(); + } + + if (-1 == close(kqueue)) { + std::cerr << "close() fail errno=" << errno << std::endl; + } +} + +void* Dispatcher::getCurrentContext() const { + return currentContext; +} + +int Dispatcher::getKqueue() const { + return kqueue; +} + +void Dispatcher::pushContext(void* context) { + resumingContexts.push(context); +} + +void Dispatcher::spawn(std::function&& procedure) { + void* context; + if (reusableContexts.empty()) { + context = new ucontext_t; + if (-1 == getcontext(reinterpret_cast(context))) { //makecontext precondition + std::cerr << "getcontext() fail errno=" << errno << std::endl; + throw std::runtime_error("Dispatcher::spawn()"); + } + auto stackPointer = new uint8_t[64 * 1024]; + reinterpret_cast(context)->uc_stack.ss_sp = stackPointer; + allocatedStacks.push(stackPointer); + reinterpret_cast(context)->uc_stack.ss_size = 64 * 1024; + makecontext(reinterpret_cast(context), (void(*)())contextProcedureStatic, 1, reinterpret_cast(this)); + ++contextCount; + } else { + context = reusableContexts.top(); + reusableContexts.pop(); + } + + resumingContexts.push(context); + spawningProcedures.emplace(std::move(procedure)); +} + +int Dispatcher::getTimer() { + int timer; + if (timers.empty()) { + timer = ++lastCreatedTimer; + } else { + timer = timers.top(); + timers.pop(); + } + + return timer; +} + +void Dispatcher::pushTimer(int timer) { + timers.push(timer); +} + +void Dispatcher::clear() { +//TODO +} + +void Dispatcher::yield() { + void* context; + for (;;) { + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } + + struct kevent event; + int count = kevent(kqueue, NULL, 0, &event, 1, NULL); + + if (count == 1) { + context = static_cast(event.udata)->context; + break; + } + + if (errno != EINTR) { + std::cerr << "kevent() failed, errno=" << errno << std::endl; + throw std::runtime_error("Dispatcher::yield()"); + } + } + + if (context != currentContext) { + ucontext_t* oldContext = static_cast(currentContext); + currentContext = context; + if (-1 == swapcontext(oldContext, static_cast(context))) { + std::cerr << "setcontext() failed, errno=" << errno << std::endl; + throw std::runtime_error("Dispatcher::yield()"); + } + } +} + +void Dispatcher::contextProcedure() { + void* context = currentContext; + for (;;) { + assert(!spawningProcedures.empty()); + std::function procedure = std::move(spawningProcedures.front()); + spawningProcedures.pop(); + procedure(); + reusableContexts.push(context); + yield(); + } +} diff --git a/src/Platform/OSX/System/Dispatcher.h b/src/Platform/OSX/System/Dispatcher.h new file mode 100755 index 0000000000..37690d53b4 --- /dev/null +++ b/src/Platform/OSX/System/Dispatcher.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +class Dispatcher { +public: + Dispatcher(); + Dispatcher(const Dispatcher&) = delete; + ~Dispatcher(); + Dispatcher& operator=(const Dispatcher&) = delete; + void spawn(std::function&& procedure); + void yield(); + void clear(); + + struct ContextExt { + void *context; + }; +private: + friend class Event; + friend class DispatcherAccessor; + friend class TcpConnection; + friend class TcpConnector; + friend class TcpListener; + friend class Timer; + int kqueue; + void* currentContext; + int lastCreatedTimer; + std::size_t contextCount; + std::queue resumingContexts; + std::stack reusableContexts; + std::stack allocatedStacks; + std::queue> spawningProcedures; + std::stack timers; + + int getKqueue() const; + int getTimer(); + void pushTimer(int timer); + void pushContext(void* context); + void* getCurrentContext() const; + + void contextProcedure(); + static void contextProcedureStatic(void* context); +}; + +} diff --git a/src/Platform/OSX/System/Event.cpp b/src/Platform/OSX/System/Event.cpp new file mode 100755 index 0000000000..008bb332b1 --- /dev/null +++ b/src/Platform/OSX/System/Event.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Event.h" +#include +#include +#include "Dispatcher.h" + +using namespace System; + +namespace { + +struct Waiter { + Waiter* next; + void* context; +}; + +} + +Event::Event() : dispatcher(nullptr) { +} + +Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { +} + +Event::Event(Event&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + first = other.first; + if (other.first != nullptr) { + last = other.last; + } + + state = other.state; + other.dispatcher = nullptr; + } +} + +Event::~Event() { + assert(first == nullptr); +} + +Event& Event::operator=(Event&& other) { + assert(first == nullptr); + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + first = other.first; + if (other.first != nullptr) { + last = other.last; + } + + state = other.state; + other.dispatcher = nullptr; + } + + return *this; +} + +bool Event::get() const { + assert(dispatcher != nullptr); + return state; +} + +void Event::clear() { + assert(dispatcher != nullptr); + state = false; +} + +void Event::set() { + assert(dispatcher != nullptr); + state = true; + for (Waiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + dispatcher->pushContext(waiter->context); + } + + first = nullptr; +} + +void Event::wait() { + assert(dispatcher != nullptr); + if (!state) { + Waiter waiter = { nullptr, dispatcher->getCurrentContext() }; + if (first != nullptr) { + static_cast(last)->next = &waiter; + } else { + first = &waiter; + } + + last = &waiter; + dispatcher->yield(); + assert(dispatcher != nullptr); + } +} diff --git a/src/Platform/OSX/System/Event.h b/src/Platform/OSX/System/Event.h new file mode 100755 index 0000000000..aab4d1a4f2 --- /dev/null +++ b/src/Platform/OSX/System/Event.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace System { + +class Dispatcher; + +class Event { +public: + Event(); + explicit Event(Dispatcher& dispatcher); + Event(const Event&) = delete; + Event(Event&& other); + ~Event(); + Event& operator=(const Event&) = delete; + Event& operator=(Event&& other); + bool get() const; + void clear(); + void set(); + void wait(); + +private: + Dispatcher* dispatcher; + void* first; + void* last; + bool state; +}; + +} diff --git a/src/Platform/OSX/System/InterruptedException.cpp b/src/Platform/OSX/System/InterruptedException.cpp new file mode 100755 index 0000000000..0e268f4f6f --- /dev/null +++ b/src/Platform/OSX/System/InterruptedException.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "InterruptedException.h" diff --git a/src/Platform/OSX/System/InterruptedException.h b/src/Platform/OSX/System/InterruptedException.h new file mode 100755 index 0000000000..aa85c8bd03 --- /dev/null +++ b/src/Platform/OSX/System/InterruptedException.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class InterruptedException : public std::exception { +}; + +} diff --git a/src/Platform/OSX/System/TcpConnection.cpp b/src/Platform/OSX/System/TcpConnection.cpp new file mode 100755 index 0000000000..6410ceb150 --- /dev/null +++ b/src/Platform/OSX/System/TcpConnection.cpp @@ -0,0 +1,247 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnection.h" +#include +#include +#include +#include +#include +#include +#include "Dispatcher.h" +#include "InterruptedException.h" + +using namespace System; + +namespace { + +struct ConnectionContext : public Dispatcher::ContextExt { + bool interrupted; +}; + +} + +TcpConnection::TcpConnection() : dispatcher(nullptr) { +} + +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { +} + +TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + connection = other.connection; + stopped = other.stopped; + readContext = other.readContext; + writeContext = other.writeContext; + other.dispatcher = nullptr; + } +} + +TcpConnection::~TcpConnection() { + if (dispatcher != nullptr) { + assert(readContext == nullptr); + assert(writeContext == nullptr); + if (close(connection) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + } + } +} + +TcpConnection& TcpConnection::operator=(TcpConnection&& other) { + if (dispatcher != nullptr) { + assert(readContext == nullptr); + assert(writeContext == nullptr); + if (close(connection) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnection::operator="); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + connection = other.connection; + stopped = other.stopped; + readContext = other.readContext; + writeContext = other.writeContext; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpConnection::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +size_t TcpConnection::read(uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(readContext == nullptr); + if (stopped) { + throw InterruptedException(); + } + + ssize_t transferred = ::recv(connection, (void *)data, size, 0); + if (transferred == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + std::cerr << "recv failed, result=" << errno << '.' << std::endl; + } else { + ConnectionContext context2; + context2.context = dispatcher->getCurrentContext(); + context2.interrupted = false; + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context2); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + readContext = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(readContext == &context2); + readContext = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + throw InterruptedException(); + } + + ssize_t transferred = ::recv(connection, (void *)data, size, 0); + if (transferred == -1) { + std::cerr << "recv failed, errno=" << errno << '.' << std::endl; + } else { + if (transferred == 0) { + std::cerr << "recv return after yield with 0 bytes" << std::endl; + + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; + } else { + std::cerr << "recv getsockopt retval = " << retval << std::endl; + } + } + + assert(transferred <= size); + return transferred; + } + } + } + + throw std::runtime_error("TcpConnection::read"); + } + + assert(transferred <= size); + return transferred; +} + +void TcpConnection::write(const uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(writeContext == nullptr); + if (stopped) { + throw InterruptedException(); + } + + if (size == 0) { + if (shutdown(connection, SHUT_WR) == -1) { + std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnection::write"); + } + + return; + } + + ssize_t transferred = ::send(connection, (void *)data, size, 0); + if (transferred == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + std::cerr << "send failed, result=" << errno << '.' << std::endl; + } else { + ConnectionContext context2; + context2.context = dispatcher->getCurrentContext(); + context2.interrupted = false; + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context2); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + writeContext = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(writeContext == &context2); + writeContext = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + throw InterruptedException(); + } + + ssize_t transferred = ::send(connection, (void *)data, size, 0); + if (transferred == -1) { + std::cerr << "recv failed, errno=" << errno << '.' << std::endl; + } else { + if (transferred == 0) { + throw std::runtime_error("send transferred 0 bytes."); + } + + assert(transferred == size); + return; + } + } + } + + throw std::runtime_error("TcpConnection::write"); + } +} + +void TcpConnection::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (writeContext != nullptr && static_cast(writeContext)->context != nullptr) { + ConnectionContext* context2 = static_cast(writeContext); + if (!context2->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpListener::stop"); + } + + context2->interrupted = true; + dispatcher->pushContext(context2->context); + } + } + + if (readContext != nullptr && static_cast(readContext)->context != nullptr) { + ConnectionContext* context2 = static_cast(readContext); + if (!context2->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpListener::stop"); + } + + context2->interrupted = true; + dispatcher->pushContext(context2->context); + } + } + + stopped = true; +} diff --git a/src/Platform/OSX/System/TcpConnection.h b/src/Platform/OSX/System/TcpConnection.h new file mode 100755 index 0000000000..74b099d58f --- /dev/null +++ b/src/Platform/OSX/System/TcpConnection.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Dispatcher; + +class TcpConnection { +public: + TcpConnection(); + TcpConnection(const TcpConnection&) = delete; + TcpConnection(TcpConnection&& other); + ~TcpConnection(); + TcpConnection& operator=(const TcpConnection&) = delete; + TcpConnection& operator=(TcpConnection&& other); + void start(); + void stop(); + std::size_t read(uint8_t* data, std::size_t size); + void write(const uint8_t* data, std::size_t size); + +private: + friend class TcpConnector; + friend class TcpListener; + + explicit TcpConnection(Dispatcher& dispatcher, int socket); + + Dispatcher* dispatcher; + int connection; + bool stopped; + void* readContext; + void* writeContext; +}; + +} diff --git a/src/Platform/OSX/System/TcpConnector.cpp b/src/Platform/OSX/System/TcpConnector.cpp new file mode 100755 index 0000000000..a5110bc3e0 --- /dev/null +++ b/src/Platform/OSX/System/TcpConnector.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see + +#include "TcpConnector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "InterruptedException.h" +#include "Dispatcher.h" +#include "TcpConnection.h" + +using namespace System; + +namespace { + +struct ConnectorContext : public Dispatcher::ContextExt { + int connection; + bool interrupted; +}; + +} + +TcpConnector::TcpConnector() : dispatcher(nullptr) { +} + +TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { +} + +TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + address = other.address; + port = other.port; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpConnector::~TcpConnector() { +} + +TcpConnector& TcpConnector::operator=(TcpConnector&& other) { + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + address = other.address; + port = other.port; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpConnector::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void TcpConnector::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + ConnectorContext* context2 = static_cast(context); + if (!context2->interrupted) { + struct kevent event; + EV_SET(&event, context2->connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpConnector::stop"); + } + + dispatcher->pushContext(context2->context); + context2->interrupted = true; + } + } + + stopped = true; +} + +TcpConnection TcpConnector::connect() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + std::ostringstream portStream; + portStream << port; + addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; + addrinfo *addressInfos; + int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); + if (result == -1) { + std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl; + } else { + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + std::uniform_int_distribution distribution(0, count - 1); + std::size_t index = distribution(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + sockaddr_in addressData = *reinterpret_cast(addressInfo->ai_addr); + freeaddrinfo(addressInfo); + int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == -1) { + std::cerr << "socket failed, errno=" << errno << '.' << std::endl; + } else { + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + std::cerr << "bind failed, errno=" << errno << '.' << std::endl; + } else { + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) { + std::cerr << "fcntl() failed errno=" << errno << std::endl; + } else { + int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); + if (result == -1) { + if (errno == EINPROGRESS) { + + ConnectorContext context2; + context2.context = dispatcher->getCurrentContext(); + context2.interrupted = false; + context2.connection = connection; + + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &context2); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(context == &context2); + context = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + if (close(connection) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + + throw InterruptedException(); + } + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; + } else { + if (retval != 0) { + std::cerr << "connect failed; getsockopt retval = " << retval << std::endl; + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + } + } + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + + if (close(connection) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + } + } + + throw std::runtime_error("TcpConnector::connect"); +} diff --git a/src/System/TcpConnector.h b/src/Platform/OSX/System/TcpConnector.h old mode 100644 new mode 100755 similarity index 81% rename from src/System/TcpConnector.h rename to src/Platform/OSX/System/TcpConnector.h index 960d1f1f07..1f64b2d264 --- a/src/System/TcpConnector.h +++ b/src/Platform/OSX/System/TcpConnector.h @@ -20,22 +20,30 @@ #include #include -class System; +namespace System { + +class Dispatcher; class TcpConnection; class TcpConnector { public: TcpConnector(); - TcpConnector(System& system, const std::string& address, uint16_t port); + TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); TcpConnector(const TcpConnector&) = delete; TcpConnector(TcpConnector&& other); ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); + void start(); + void stop(); TcpConnection connect(); private: - System* system; - std::string m_address; - uint16_t m_port; + Dispatcher* dispatcher; + std::string address; + uint16_t port; + bool stopped; + void* context; }; + +} diff --git a/src/Platform/OSX/System/TcpListener.cpp b/src/Platform/OSX/System/TcpListener.cpp new file mode 100755 index 0000000000..63a87462ac --- /dev/null +++ b/src/Platform/OSX/System/TcpListener.cpp @@ -0,0 +1,202 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpListener.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "InterruptedException.h" +#include "Dispatcher.h" +#include "TcpConnection.h" + +using namespace System; + +namespace { + +struct ListenerContext : public Dispatcher::ContextExt { + bool interrupted; +}; + +} + +TcpListener::TcpListener() : dispatcher(nullptr) { +} + +TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + listener = other.listener; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpListener::~TcpListener() { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(listener) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + } + } +} + +TcpListener& TcpListener::operator=(TcpListener&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (close(listener) == -1) { + std::cerr << "close() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpListener::operator="); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + listener = other.listener; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpListener::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == -1) { + std::cerr << "socket failed, errno=" << errno << std::endl; + } else { + int flags = fcntl(listener, F_GETFL, 0); + if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) { + std::cerr << "fcntl() failed errno=" << errno << std::endl; + } else { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = INADDR_ANY; + if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { + std::cerr << "bind failed, errno=" << errno << std::endl; + } else if (listen(listener, SOMAXCONN) != 0) { + std::cerr << "listen failed, errno=" << errno << std::endl; + } else { + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL); + + if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + stopped = false; + context = nullptr; + return; + } + } + } + + if (close(listener) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + } + + throw std::runtime_error("TcpListener::TcpListener"); +} + +TcpConnection TcpListener::accept() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + ListenerContext context2; + context2.context = dispatcher->getCurrentContext(); + context2.interrupted = false; + + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &context2); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(context == &context2); + context = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + if (close(listener) == -1) { + std::cerr << "close failed, errno=" << errno << std::endl; + } + throw InterruptedException(); + } + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + } else { + sockaddr inAddr; + socklen_t inLen = sizeof(inAddr); + int connection = ::accept(listener, &inAddr, &inLen); + if (connection == -1) { + std::cerr << "accept() failed, errno=" << errno << '.' << std::endl; + } else { + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) { + std::cerr << "fcntl() failed errno=" << errno << std::endl; + } else { + return TcpConnection(*dispatcher, connection); + } + } + } + } + + throw std::runtime_error("TcpListener::accept"); +} + +void TcpListener::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + ListenerContext* context2 = static_cast(context); + if (!context2->interrupted) { + context2->interrupted = true; + + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("TcpListener::stop"); + } + + dispatcher->pushContext(context2->context); + } + } + + stopped = true; +} diff --git a/src/System/TcpListener.h b/src/Platform/OSX/System/TcpListener.h old mode 100644 new mode 100755 similarity index 86% rename from src/System/TcpListener.h rename to src/Platform/OSX/System/TcpListener.h index 6147257c59..2fcd6f337f --- a/src/System/TcpListener.h +++ b/src/Platform/OSX/System/TcpListener.h @@ -20,13 +20,15 @@ #include #include -class System; +namespace System { + +class Dispatcher; class TcpConnection; class TcpListener { public: TcpListener(); - TcpListener(System& system, const std::string& address, uint16_t port); + TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); TcpListener(const TcpListener&) = delete; TcpListener(TcpListener&& other); ~TcpListener(); @@ -37,7 +39,10 @@ class TcpListener { TcpConnection accept(); private: - System* system; - void* listener; + Dispatcher* dispatcher; + int listener; bool stopped; + void* context; }; + +} diff --git a/src/Platform/OSX/System/Timer.cpp b/src/Platform/OSX/System/Timer.cpp new file mode 100755 index 0000000000..63f1941a1e --- /dev/null +++ b/src/Platform/OSX/System/Timer.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Timer.h" +#include +#include +#include +#include +#include +#include +#include "Dispatcher.h" +#include "InterruptedException.h" + +using namespace System; + +namespace { + +struct TimerContext : public Dispatcher::ContextExt { + Dispatcher* dispatcher; + bool interrupted; +}; + +} + +Timer::Timer() : dispatcher(nullptr) { +} + +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { + timer = dispatcher.getTimer(); +} + +Timer::~Timer() { + if (dispatcher != nullptr) { + assert(context == nullptr); + dispatcher->pushTimer(timer); + } +} + +Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + timer = other.timer; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +Timer& Timer::operator=(Timer&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + dispatcher->pushTimer(timer); + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + timer = other.timer; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void Timer::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void Timer::sleep(std::chrono::milliseconds duration) { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + TimerContext context2; + context2.dispatcher = dispatcher; + context2.context = dispatcher->getCurrentContext(); + context2.interrupted = false; + + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count(), &context2); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("Timer::sleep"); + } + + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == dispatcher->getCurrentContext()); + assert(context == &context2); + context = nullptr; + context2.context = nullptr; + if (context2.interrupted) { + throw InterruptedException(); + } +} + +void Timer::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + TimerContext* context2 = reinterpret_cast(context); + if (context2->context != nullptr) { + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + throw std::runtime_error("Timer::stop"); + } + + dispatcher->pushContext(context2->context); + context2->interrupted = true; + } + } + + stopped = true; +} diff --git a/src/Platform/OSX/System/Timer.h b/src/Platform/OSX/System/Timer.h new file mode 100755 index 0000000000..1269281c45 --- /dev/null +++ b/src/Platform/OSX/System/Timer.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; + +class Timer { +public: + Timer(); + explicit Timer(Dispatcher& dispatcher); + Timer(const Timer&) = delete; + Timer(Timer&& other); + ~Timer(); + Timer& operator=(const Timer&) = delete; + Timer& operator=(Timer&& other); + void start(); + void stop(); + void sleep(std::chrono::milliseconds duration); + +private: + Dispatcher* dispatcher; + int timer; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/Windows/System/Dispatcher.cpp b/src/Platform/Windows/System/Dispatcher.cpp new file mode 100755 index 0000000000..54943a3596 --- /dev/null +++ b/src/Platform/Windows/System/Dispatcher.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Dispatcher.h" +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include + +using namespace System; + +namespace { + +struct OverlappedExt : public OVERLAPPED { + void* context; +}; + +} + +Dispatcher::Dispatcher() { + completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (completionPort == NULL) { + std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + } else { + if (ConvertThreadToFiberEx(NULL, 0) == NULL) { + std::cerr << "ConvertThreadToFiberEx failed, result=" << GetLastError() << '.' << std::endl; + } else { + WSADATA wsaData; + int result = WSAStartup(0x0202, &wsaData); + if (result != 0) { + std::cerr << "WSAStartup failed, result=" << result << '.' << std::endl; + } else { + contextCount = 0; + return; + } + + if (ConvertFiberToThread() != TRUE) { + std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl; + } + } + + if (CloseHandle(completionPort) != TRUE) { + std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl; + } + } + + throw std::runtime_error("Dispatcher::Dispatcher"); +} + +Dispatcher::~Dispatcher() { + assert(resumingContexts.empty()); + assert(reusableContexts.size() == contextCount); + assert(spawningProcedures.empty()); + while (!reusableContexts.empty()) { + DeleteFiber(reusableContexts.top()); + reusableContexts.pop(); + } + + while (!timers.empty()) { + if (CloseHandle(timers.top()) != TRUE) { + std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl; + } + + timers.pop(); + } + + if (WSACleanup() != 0) { + std::cerr << "WSACleanup failed, result=" << WSAGetLastError() << '.' << std::endl; + } + + if (ConvertFiberToThread() != TRUE) { + std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl; + } + + if (CloseHandle(completionPort) != TRUE) { + std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl; + } +} + +void* Dispatcher::getCompletionPort() const { + return completionPort; +} + +void* Dispatcher::getTimer() { + void* timer; + if (timers.empty()) { + timer = CreateWaitableTimer(NULL, FALSE, NULL); + if (timer == NULL) { + std::cerr << "CreateWaitableTimer failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("Dispatcher::getTimer"); + } + } else { + timer = timers.top(); + timers.pop(); + } + + return timer; +} + +void Dispatcher::pushTimer(void* timer) { + timers.push(timer); +} + +void Dispatcher::pushContext(void* context) { + resumingContexts.push(context); +} + +void Dispatcher::spawn(std::function&& procedure) { + void* context; + if (reusableContexts.empty()) { + context = CreateFiberEx(16384, 65536, 0, contextProcedureStatic, this); + if (context == NULL) { + std::cerr << "CreateFiberEx failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("Dispatcher::spawn"); + } + ++contextCount; + } else { + context = reusableContexts.top(); + reusableContexts.pop(); + } + + resumingContexts.push(context); + spawningProcedures.emplace(std::move(procedure)); +} + +void Dispatcher::clear() { +//TODO +} + +void Dispatcher::yield() { + void* context; + for (;;) { + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } + + OVERLAPPED_ENTRY entry; + ULONG actual = 0; + if (GetQueuedCompletionStatusEx(completionPort, &entry, 1, &actual, INFINITE, TRUE) == TRUE) { + context = reinterpret_cast(entry.lpOverlapped)->context; + break; + } + + DWORD lastError = GetLastError(); + if (lastError != WAIT_IO_COMPLETION) { + std::cerr << "GetQueuedCompletionStatusEx failed, result=" << lastError << '.' << std::endl; + throw std::runtime_error("Dispatcher::yield"); + } + } + + if (context != GetCurrentFiber()) { + SwitchToFiber(context); + } +} + +void Dispatcher::contextProcedure() { + for (;;) { + assert(!spawningProcedures.empty()); + std::function procedure = std::move(spawningProcedures.front()); + spawningProcedures.pop(); + procedure(); + reusableContexts.push(GetCurrentFiber()); + yield(); + } +} + +void __stdcall Dispatcher::contextProcedureStatic(void* context) { + static_cast(context)->contextProcedure(); +} diff --git a/src/System/System.h b/src/Platform/Windows/System/Dispatcher.h old mode 100644 new mode 100755 similarity index 60% rename from src/System/System.h rename to src/Platform/Windows/System/Dispatcher.h index 712de34899..8df6cd9e0e --- a/src/System/System.h +++ b/src/Platform/Windows/System/Dispatcher.h @@ -21,26 +21,40 @@ #include #include -class System { +namespace System { + +class Dispatcher { public: - System(); - System(const System&) = delete; - ~System(); - System& operator=(const System&) = delete; - void* getCurrentContext() const; - void* getIoService(); - void pushContext(void* context); + Dispatcher(); + Dispatcher(const Dispatcher&) = delete; + ~Dispatcher(); + Dispatcher& operator=(const Dispatcher&) = delete; void spawn(std::function&& procedure); void yield(); - void wake(); - - void contextProcedure(); + void clear(); private: - void* ioService; - void* work; - std::stack contexts; - std::queue> procedures; + friend class Event; + friend class DispatcherAccessor; + friend class TcpConnection; + friend class TcpConnector; + friend class TcpListener; + friend class Timer; + + void* completionPort; + std::size_t contextCount; std::queue resumingContexts; - void* currentContext; + std::stack reusableContexts; + std::queue> spawningProcedures; + std::stack timers; + + void* getCompletionPort() const; + void* getTimer(); + void pushTimer(void* timer); + void pushContext(void* context); + + void contextProcedure(); + static void __stdcall contextProcedureStatic(void* context); }; + +} diff --git a/src/Platform/Windows/System/Event.cpp b/src/Platform/Windows/System/Event.cpp new file mode 100755 index 0000000000..6187378cc8 --- /dev/null +++ b/src/Platform/Windows/System/Event.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Event.h" +#include +#define WIN32_LEAN_AND_MEAN +#include +#include "Dispatcher.h" + +using namespace System; + +namespace { + +struct Waiter { + Waiter* next; + void* context; +}; + +} + +Event::Event() : dispatcher(nullptr) { +} + +Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { +} + +Event::Event(Event&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + first = other.first; + if (other.first != nullptr) { + last = other.last; + } + + state = other.state; + other.dispatcher = nullptr; + } +} + +Event::~Event() { + assert(first == nullptr); +} + +Event& Event::operator=(Event&& other) { + assert(first == nullptr); + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + first = other.first; + if (other.first != nullptr) { + last = other.last; + } + + state = other.state; + other.dispatcher = nullptr; + } + + return *this; +} + +bool Event::get() const { + assert(dispatcher != nullptr); + return state; +} + +void Event::clear() { + assert(dispatcher != nullptr); + state = false; +} + +void Event::set() { + assert(dispatcher != nullptr); + state = true; + for (Waiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + dispatcher->pushContext(waiter->context); + } + + first = nullptr; +} + +void Event::wait() { + assert(dispatcher != nullptr); + if (!state) { + Waiter waiter = {nullptr, GetCurrentFiber()}; + if (first != nullptr) { + static_cast(last)->next = &waiter; + } else { + first = &waiter; + } + + last = &waiter; + dispatcher->yield(); + assert(dispatcher != nullptr); + } +} diff --git a/src/Platform/Windows/System/Event.h b/src/Platform/Windows/System/Event.h new file mode 100755 index 0000000000..aab4d1a4f2 --- /dev/null +++ b/src/Platform/Windows/System/Event.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace System { + +class Dispatcher; + +class Event { +public: + Event(); + explicit Event(Dispatcher& dispatcher); + Event(const Event&) = delete; + Event(Event&& other); + ~Event(); + Event& operator=(const Event&) = delete; + Event& operator=(Event&& other); + bool get() const; + void clear(); + void set(); + void wait(); + +private: + Dispatcher* dispatcher; + void* first; + void* last; + bool state; +}; + +} diff --git a/src/Platform/Windows/System/InterruptedException.cpp b/src/Platform/Windows/System/InterruptedException.cpp new file mode 100755 index 0000000000..0e268f4f6f --- /dev/null +++ b/src/Platform/Windows/System/InterruptedException.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "InterruptedException.h" diff --git a/src/Platform/Windows/System/InterruptedException.h b/src/Platform/Windows/System/InterruptedException.h new file mode 100755 index 0000000000..aa85c8bd03 --- /dev/null +++ b/src/Platform/Windows/System/InterruptedException.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class InterruptedException : public std::exception { +}; + +} diff --git a/src/Platform/Windows/System/TcpConnection.cpp b/src/Platform/Windows/System/TcpConnection.cpp new file mode 100755 index 0000000000..0f2ea49a5c --- /dev/null +++ b/src/Platform/Windows/System/TcpConnection.cpp @@ -0,0 +1,240 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnection.h" +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include "InterruptedException.h" +#include "Dispatcher.h" + +using namespace System; + +namespace { + +struct OverlappedExt : public OVERLAPPED { + void* context; + bool interrupted; +}; + +struct Context { + Dispatcher* dispatcher; + OverlappedExt* read; + OverlappedExt* write; +}; + +} + +TcpConnection::TcpConnection() : dispatcher(nullptr) { +} + +TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), context(nullptr) { +} + + +TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + connection = other.connection; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpConnection::~TcpConnection() { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (closesocket(connection) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + } +} + +TcpConnection& TcpConnection::operator=(TcpConnection&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (closesocket(connection) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + throw std::runtime_error("TcpConnection::operator="); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + connection = other.connection; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpConnection::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void TcpConnection::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + Context* context2 = static_cast(context); + if (context2->read != nullptr && !context2->read->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context2->read) != TRUE) { + std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("TcpConnection::stop"); + } + + context2->read->interrupted = true; + } + + if (context2->write != nullptr && !context2->write->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context2->write) != TRUE) { + std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("TcpConnection::stop"); + } + + context2->write->interrupted = true; + } + } + + stopped = true; +} + +size_t TcpConnection::read(uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(context == nullptr || static_cast(context)->read == nullptr); + if (stopped) { + throw InterruptedException(); + } + + WSABUF buf{static_cast(size), reinterpret_cast(data)}; + DWORD flags = 0; + OverlappedExt overlapped; + overlapped.hEvent = NULL; + if (WSARecv(connection, &buf, 1, NULL, &flags, &overlapped, NULL) != 0) { + int lastError = WSAGetLastError(); + if (lastError != WSA_IO_PENDING) { + std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl; + throw std::runtime_error("TcpConnection::read"); + } + } + + assert(flags == 0); + Context context2; + if (context == nullptr) { + context2.dispatcher = dispatcher; + context2.write = nullptr; + context = &context2; + } + + overlapped.context = GetCurrentFiber(); + overlapped.interrupted = false; + static_cast(context)->read = &overlapped; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(overlapped.context == GetCurrentFiber()); + assert(static_cast(context)->read == &overlapped); + if (static_cast(context)->write != nullptr) { + static_cast(context)->read = nullptr; + } else { + context = nullptr; + } + + DWORD transferred; + if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) { + int lastError = WSAGetLastError(); + if (lastError == ERROR_OPERATION_ABORTED) { + assert(overlapped.interrupted); + throw InterruptedException(); + } + + std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl; + throw std::runtime_error("TcpConnection::read"); + } + + assert(transferred <= size); + assert(flags == 0); + return transferred; +} + +void TcpConnection::write(const uint8_t* data, size_t size) { + assert(dispatcher != nullptr); + assert(context == nullptr || static_cast(context)->write == nullptr); + if (stopped) { + throw InterruptedException(); + } + + if (size == 0) { + if (shutdown(connection, SD_SEND) != 0) { + std::cerr << "shutdown failed, result=" << WSAGetLastError() << '.' << std::endl; + throw std::runtime_error("TcpConnection::write"); + } + + return; + } + + WSABUF buf{static_cast(size), reinterpret_cast(const_cast(data))}; + OverlappedExt overlapped; + overlapped.hEvent = NULL; + if (WSASend(connection, &buf, 1, NULL, 0, &overlapped, NULL) != 0) { + int lastError = WSAGetLastError(); + if (lastError != WSA_IO_PENDING) { + std::cerr << "WSASend failed, result=" << lastError << '.' << std::endl; + throw std::runtime_error("TcpConnection::write"); + } + } + + Context context2; + if (context == nullptr) { + context2.dispatcher = dispatcher; + context2.read = nullptr; + context = &context2; + } + + overlapped.context = GetCurrentFiber(); + overlapped.interrupted = false; + static_cast(context)->write = &overlapped; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(overlapped.context == GetCurrentFiber()); + assert(static_cast(context)->write == &overlapped); + if (static_cast(context)->read != nullptr) { + static_cast(context)->write = nullptr; + } else { + context = nullptr; + } + + DWORD transferred; + DWORD flags; + if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) { + int lastError = WSAGetLastError(); + if (lastError == ERROR_OPERATION_ABORTED) { + assert(overlapped.interrupted); + throw InterruptedException(); + } + + std::cerr << "WSSend failed, result=" << lastError << '.' << std::endl; + throw std::runtime_error("TcpConnection::write"); + } + + assert(transferred == size); + assert(flags == 0); +} diff --git a/src/Platform/Windows/System/TcpConnection.h b/src/Platform/Windows/System/TcpConnection.h new file mode 100755 index 0000000000..1cf24221ae --- /dev/null +++ b/src/Platform/Windows/System/TcpConnection.h @@ -0,0 +1,52 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Dispatcher; + +class TcpConnection { +public: + TcpConnection(); + TcpConnection(const TcpConnection&) = delete; + TcpConnection(TcpConnection&& other); + ~TcpConnection(); + TcpConnection& operator=(const TcpConnection&) = delete; + TcpConnection& operator=(TcpConnection&& other); + void start(); + void stop(); + std::size_t read(uint8_t* data, std::size_t size); + void write(const uint8_t* data, std::size_t size); + +private: + friend class TcpConnector; + friend class TcpListener; + + explicit TcpConnection(Dispatcher& dispatcher, std::size_t connection); + + Dispatcher* dispatcher; + std::size_t connection; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/Windows/System/TcpConnector.cpp b/src/Platform/Windows/System/TcpConnector.cpp new file mode 100755 index 0000000000..fb698341e9 --- /dev/null +++ b/src/Platform/Windows/System/TcpConnector.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpConnector.h" +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "InterruptedException.h" +#include "Dispatcher.h" +#include "TcpConnection.h" + +using namespace System; + +namespace { + +struct Context : public OVERLAPPED { + void* context; + std::size_t connection; + bool interrupted; +}; + +LPFN_CONNECTEX connectEx = nullptr; + +} + +TcpConnector::TcpConnector() : dispatcher(nullptr) { +} + +TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { +} + +TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + address = other.address; + port = other.port; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpConnector::~TcpConnector() { +} + +TcpConnector& TcpConnector::operator=(TcpConnector&& other) { + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + address = other.address; + port = other.port; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpConnector::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void TcpConnector::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + Context* context2 = static_cast(context); + if (!context2->interrupted) { + if (CancelIoEx(reinterpret_cast(context2->connection), context2) != TRUE) { + std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("TcpConnector::stop"); + } + + context2->interrupted = true; + } + } + + stopped = true; +} + +TcpConnection TcpConnector::connect() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + std::ostringstream portStream; + portStream << port; + addrinfo hints = {0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; + addrinfo* addressInfos; + int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); + if (result != 0) { + std::cerr << "getaddrinfo failed, result=" << result << '.' << std::endl; + } else { + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + std::uniform_int_distribution distribution(0, count - 1); + std::size_t index = distribution(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + sockaddr_in addressData = *reinterpret_cast(addressInfo->ai_addr); + freeaddrinfo(addressInfo); + SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == INVALID_SOCKET) { + std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + GUID guidConnectEx = WSAID_CONNECTEX; + DWORD read = sizeof connectEx; + if (connectEx == nullptr && WSAIoctl(connection, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof guidConnectEx, &connectEx, sizeof connectEx, &read, NULL, NULL) != 0) { + std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + assert(read == sizeof connectEx); + if (CreateIoCompletionPort(reinterpret_cast(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) { + std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + } else { + addressData.sin_port = htons(port); + Context context2; + context2.hEvent = NULL; + if (connectEx(connection, reinterpret_cast(&addressData), sizeof addressData, NULL, 0, NULL, &context2) == TRUE) { + std::cerr << "ConnectEx returned immediately, which is not supported." << std::endl; + } else { + int lastError = WSAGetLastError(); + if (lastError != WSA_IO_PENDING) { + std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl; + } else { + context2.context = GetCurrentFiber(); + context2.connection = connection; + context2.interrupted = false; + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == GetCurrentFiber()); + assert(context2.connection == connection); + assert(context == &context2); + context = nullptr; + DWORD transferred; + DWORD flags; + if (WSAGetOverlappedResult(connection, &context2, &transferred, FALSE, &flags) != TRUE) { + lastError = WSAGetLastError(); + if (lastError == ERROR_OPERATION_ABORTED) { + assert(context2.interrupted); + if (closesocket(connection) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + + throw InterruptedException(); + } + + std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl; + } else { + assert(transferred == 0); + assert(flags == 0); + return TcpConnection(*dispatcher, connection); + } + } + } + } + } + } + + if (closesocket(connection) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + } + } + + throw std::runtime_error("TcpConnector::connect"); +} diff --git a/src/Platform/Windows/System/TcpConnector.h b/src/Platform/Windows/System/TcpConnector.h new file mode 100755 index 0000000000..1f64b2d264 --- /dev/null +++ b/src/Platform/Windows/System/TcpConnector.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Dispatcher; +class TcpConnection; + +class TcpConnector { +public: + TcpConnector(); + TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpConnector(const TcpConnector&) = delete; + TcpConnector(TcpConnector&& other); + ~TcpConnector(); + TcpConnector& operator=(const TcpConnector&) = delete; + TcpConnector& operator=(TcpConnector&& other); + void start(); + void stop(); + TcpConnection connect(); + +private: + Dispatcher* dispatcher; + std::string address; + uint16_t port; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/Windows/System/TcpListener.cpp b/src/Platform/Windows/System/TcpListener.cpp new file mode 100755 index 0000000000..020c34a460 --- /dev/null +++ b/src/Platform/Windows/System/TcpListener.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TcpListener.h" +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "InterruptedException.h" +#include "Dispatcher.h" +#include "TcpConnection.h" + +using namespace System; + +namespace { + +struct Context : public OVERLAPPED { + void* context; + bool interrupted; +}; + +LPFN_ACCEPTEX acceptEx = nullptr; +LPFN_GETACCEPTEXSOCKADDRS getAcceptExSockaddrs = nullptr; + +} + +TcpListener::TcpListener() : dispatcher(nullptr) { +} + +TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == INVALID_SOCKET) { + std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = INADDR_ANY; + if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { + std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl; + } else if (listen(listener, SOMAXCONN) != 0) { + std::cerr << "listen failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + GUID guidAcceptEx = WSAID_ACCEPTEX; + DWORD read = sizeof acceptEx; + if (acceptEx == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof guidAcceptEx, &acceptEx, sizeof acceptEx, &read, NULL, NULL) != 0) { + std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + assert(read == sizeof acceptEx); + GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; + read = sizeof getAcceptExSockaddrs; + if (getAcceptExSockaddrs == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidGetAcceptExSockaddrs, sizeof guidGetAcceptExSockaddrs, &getAcceptExSockaddrs, sizeof getAcceptExSockaddrs, &read, NULL, NULL) != 0) { + std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + assert(read == sizeof getAcceptExSockaddrs); + if (CreateIoCompletionPort(reinterpret_cast(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) { + std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + } else { + stopped = false; + context = nullptr; + return; + } + } + } + } + + if (closesocket(listener) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + } + + throw std::runtime_error("TcpListener::TcpListener"); +} + +TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + listener = other.listener; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +TcpListener::~TcpListener() { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (closesocket(listener) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + } +} + +TcpListener& TcpListener::operator=(TcpListener&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + if (closesocket(listener) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + throw std::runtime_error("TcpListener::operator="); + } + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + listener = other.listener; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void TcpListener::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void TcpListener::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + Context* context2 = static_cast(context); + if (!context2->interrupted) { + if (CancelIoEx(reinterpret_cast(listener), context2) != TRUE) { + std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("TcpListener::stop"); + } + + context2->interrupted = true; + } + } + + stopped = true; +} + +TcpConnection TcpListener::accept() { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == INVALID_SOCKET) { + std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + uint8_t addresses[sizeof sockaddr_in * 2 + 32]; + DWORD received; + Context context2; + context2.hEvent = NULL; + if (acceptEx(listener, connection, addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &context2) == TRUE) { + std::cerr << "AcceptEx returned immediately, which is not supported." << std::endl; + } else { + int lastError = WSAGetLastError(); + if (lastError != WSA_IO_PENDING) { + std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl; + } else { + context2.context = GetCurrentFiber(); + context2.interrupted = false; + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == GetCurrentFiber()); + assert(context == &context2); + context = nullptr; + DWORD transferred; + DWORD flags; + if (WSAGetOverlappedResult(listener, &context2, &transferred, FALSE, &flags) != TRUE) { + lastError = WSAGetLastError(); + if (lastError == ERROR_OPERATION_ABORTED) { + assert(context2.interrupted); + if (closesocket(connection) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + + throw InterruptedException(); + } + + std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl; + } else { + assert(transferred == 0); + assert(flags == 0); + if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast(&listener), sizeof listener) != 0) { + std::cerr << "setsockopt failed, result=" << WSAGetLastError() << '.' << std::endl; + } else { + if (CreateIoCompletionPort(reinterpret_cast(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) { + std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + } else { + //sockaddr_in* local; + //int localSize; + //sockaddr_in* remote; + //int remoteSize; + //static_cast(getAcceptExSockaddrs)(addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, reinterpret_cast(&local), &localSize, reinterpret_cast(&remote), &remoteSize); + //assert(localSize == sizeof sockaddr_in); + //assert(remoteSize == sizeof sockaddr_in); + //std::cout << "Client connected from " << static_cast(remote->sin_addr.S_un.S_un_b.s_b1) << '.' << static_cast(remote->sin_addr.S_un.S_un_b.s_b2) << '.' << static_cast(remote->sin_addr.S_un.S_un_b.s_b3) << '.' << static_cast(remote->sin_addr.S_un.S_un_b.s_b4) << ':' << remote->sin_port << std::endl; + return TcpConnection(*dispatcher, connection); + } + } + } + } + } + + if (closesocket(connection) != 0) { + std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + } + } + + throw std::runtime_error("TcpListener::accept"); +} diff --git a/src/Platform/Windows/System/TcpListener.h b/src/Platform/Windows/System/TcpListener.h new file mode 100755 index 0000000000..e48af44424 --- /dev/null +++ b/src/Platform/Windows/System/TcpListener.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Dispatcher; +class TcpConnection; + +class TcpListener { +public: + TcpListener(); + TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpListener(const TcpListener&) = delete; + TcpListener(TcpListener&& other); + ~TcpListener(); + TcpListener& operator=(const TcpListener&) = delete; + TcpListener& operator=(TcpListener&& other); + void start(); + void stop(); + TcpConnection accept(); + +private: + Dispatcher* dispatcher; + std::size_t listener; + bool stopped; + void* context; +}; + +} diff --git a/src/Platform/Windows/System/Timer.cpp b/src/Platform/Windows/System/Timer.cpp new file mode 100755 index 0000000000..f8fbed2add --- /dev/null +++ b/src/Platform/Windows/System/Timer.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Timer.h" +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include "InterruptedException.h" +#include "Dispatcher.h" + +using namespace System; + +namespace System { + +class DispatcherAccessor { +public: + DispatcherAccessor(Dispatcher* dispatcher, void* context) { + dispatcher->pushContext(context); + } +}; + +} + +namespace { + +struct Context { + Dispatcher* dispatcher; + void* context; + bool interrupted; +}; + +void __stdcall callbackProcedure(void* lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { + Context* context = static_cast(lpArgToCompletionRoutine); + assert(context->context != nullptr); + DispatcherAccessor(context->dispatcher, context->context); + context->context = nullptr; +} + +} + +Timer::Timer() : dispatcher(nullptr) { +} + +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { + timer = dispatcher.getTimer(); +} + +Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { + if (other.dispatcher != nullptr) { + timer = other.timer; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } +} + +Timer::~Timer() { + if (dispatcher != nullptr) { + assert(context == nullptr); + dispatcher->pushTimer(timer); + } +} + +Timer& Timer::operator=(Timer&& other) { + if (dispatcher != nullptr) { + assert(context == nullptr); + dispatcher->pushTimer(timer); + } + + dispatcher = other.dispatcher; + if (other.dispatcher != nullptr) { + timer = other.timer; + stopped = other.stopped; + context = other.context; + other.dispatcher = nullptr; + } + + return *this; +} + +void Timer::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void Timer::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + Context* context2 = static_cast(context); + if (context2->context != nullptr) { + if (CancelWaitableTimer(timer) != TRUE) { + std::cerr << "CancelWaitableTimer failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("Timer::stop"); + } + + dispatcher->pushContext(context2->context); + context2->context = nullptr; + context2->interrupted = true; + } + } + + stopped = true; +} + +void Timer::sleep(std::chrono::nanoseconds duration) { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + LARGE_INTEGER duration2; + duration2.QuadPart = static_cast(duration.count() / -100); + Context context2 = {dispatcher, GetCurrentFiber(), false}; + if (SetWaitableTimer(timer, &duration2, 0, callbackProcedure, &context2, FALSE) != TRUE) { + std::cerr << "SetWaitableTimer failed, result=" << GetLastError() << '.' << std::endl; + throw std::runtime_error("Timer::sleep"); + } + + context = &context2; + dispatcher->yield(); + assert(dispatcher != nullptr); + assert(context2.context == nullptr); + assert(context == &context2); + context = nullptr; + if (context2.interrupted) { + throw InterruptedException(); + } +} diff --git a/src/System/Timer.h b/src/Platform/Windows/System/Timer.h old mode 100644 new mode 100755 similarity index 82% rename from src/System/Timer.h rename to src/Platform/Windows/System/Timer.h index 317f87ebb1..78af395f34 --- a/src/System/Timer.h +++ b/src/Platform/Windows/System/Timer.h @@ -19,20 +19,28 @@ #include -class System; +namespace System { + +class Dispatcher; class Timer { public: Timer(); - explicit Timer(System& system); + explicit Timer(Dispatcher& dispatcher); Timer(const Timer&) = delete; Timer(Timer&& other); ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void sleep(std::chrono::milliseconds time); + void start(); + void stop(); + void sleep(std::chrono::nanoseconds duration); private: - System* system; + Dispatcher* dispatcher; void* timer; + bool stopped; + void* context; }; + +} diff --git a/src/System/System.cpp b/src/System/System.cpp deleted file mode 100644 index fd12d33c0b..0000000000 --- a/src/System/System.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "System.h" -#include -#include -#include - -namespace { -void contextProcedureStatic(intptr_t context) { - reinterpret_cast(context)->contextProcedure(); -} -} - -System::System() { - ioService = new boost::asio::io_service; - work = new boost::asio::io_service::work(*static_cast(ioService)); - currentContext = new boost::context::fcontext_t; -} - -System::~System() { - assert(procedures.empty()); - assert(resumingContexts.empty()); - while (!contexts.empty()) { - - delete static_cast(contexts.top()); - - contexts.pop(); - } - delete static_cast(work); - if (!static_cast(ioService)->stopped()) { - static_cast(ioService)->stop(); - } - delete static_cast(ioService); -} - -void* System::getCurrentContext() const { - - return currentContext; -} - -void* System::getIoService() { - return ioService; -} - -void System::pushContext(void* context) { - resumingContexts.push(context); -} - -void System::spawn(std::function&& procedure) { - procedures.emplace(std::move(procedure)); -} - -void System::wake() { - static_cast(ioService)->post([] {}); -} - -void System::yield() { - if (procedures.empty()) { - void* context; - for (;;) { - if (resumingContexts.empty()) { - boost::system::error_code errorCode; - static_cast(ioService)->run_one(errorCode); - if (errorCode) { - std::cerr << "boost::asio::io_service::run_onw failed, result=" << errorCode << '.' << std::endl; - throw std::runtime_error("System::yield"); - } - } else { - context = resumingContexts.front(); - resumingContexts.pop(); - break; - } - } - - if (context != currentContext) { - boost::context::fcontext_t* oldContext = static_cast(currentContext); - currentContext = context; -#if (BOOST_VERSION >= 105600) - boost::context::jump_fcontext(oldContext, *static_cast(context), reinterpret_cast(this), false); -#else - boost::context::jump_fcontext(oldContext, static_cast(context), reinterpret_cast(this), false); -#endif - } - } else { - void* context; - if (contexts.empty()) { -#if (BOOST_VERSION >= 105600) - context = new boost::context::fcontext_t(boost::context::make_fcontext(new uint8_t[65536] + 65536, 65536, contextProcedureStatic)); -#else - context = new boost::context::fcontext_t(*boost::context::make_fcontext(new uint8_t[65536] + 65536, 65536, contextProcedureStatic)); -#endif - } else { - context = contexts.top(); - contexts.pop(); - } - - - boost::context::fcontext_t* oldContext = static_cast(currentContext); - currentContext = context; -#if (BOOST_VERSION >= 105600) - boost::context::jump_fcontext(oldContext, *static_cast(context), reinterpret_cast(this), false); -#else - boost::context::jump_fcontext(oldContext, static_cast(context), reinterpret_cast(this), false); -#endif - } -} - -void System::contextProcedure() { - void* context = currentContext; - for (;;) { - assert(!procedures.empty()); - std::function procedure = std::move(procedures.front()); - procedures.pop(); - procedure(); - contexts.push(context); - yield(); - } -} diff --git a/src/System/TcpConnection.cpp b/src/System/TcpConnection.cpp deleted file mode 100644 index 13a5a41be0..0000000000 --- a/src/System/TcpConnection.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "TcpConnection.h" -#include -#include -#include "System.h" - -TcpConnection::TcpConnection() : system(nullptr) { -} - -TcpConnection::TcpConnection(System& system, void* socket) : system(&system), socket(socket), stopped(false) { -} - -TcpConnection::TcpConnection(TcpConnection&& other) : system(other.system) { - if (other.system != nullptr) { - socket = other.socket; - stopped = other.stopped; - other.system = nullptr; - } -} - -TcpConnection::~TcpConnection() { - if (system != nullptr) { - delete static_cast(socket); - } -} - -TcpConnection& TcpConnection::operator=(TcpConnection&& other) { - if (system != nullptr) { - delete static_cast(socket); - } - - system = other.system; - if (other.system != nullptr) { - socket = other.socket; - stopped = other.stopped; - other.system = nullptr; - } - - return *this; -} - -void TcpConnection::start() { - stopped = false; -} - -void TcpConnection::stop() { - stopped = true; -} - -size_t TcpConnection::read(uint8_t* data, size_t size) { - assert(system != nullptr); - if (stopped) { - throw std::runtime_error("Stopped"); - } - - void* context = system->getCurrentContext(); - boost::system::error_code errorCode; - std::size_t transferred; - static_cast(socket)->async_read_some(boost::asio::buffer(data, size), [&](const boost::system::error_code& callbackErrorCode, std::size_t callbackTransferred) { - errorCode = callbackErrorCode; - transferred = callbackTransferred; - system->pushContext(context); - }); - - system->yield(); - if (errorCode) { - throw boost::system::system_error(errorCode); - } - - return transferred; -} - -void TcpConnection::write(const uint8_t* data, size_t size) { - assert(system != nullptr); - if (stopped) { - throw std::runtime_error("Stopped"); - } - - void* context = system->getCurrentContext(); - boost::system::error_code errorCode; - std::size_t transferred; - boost::asio::async_write(*static_cast(socket), boost::asio::buffer(data, size), [&](const boost::system::error_code& callbackErrorCode, std::size_t callbackTransferred) { - errorCode = callbackErrorCode; - transferred = callbackTransferred; - system->pushContext(context); - }); - - system->yield(); - if (errorCode) { - throw boost::system::system_error(errorCode); - } - - assert(transferred == size); -} diff --git a/src/System/TcpConnector.cpp b/src/System/TcpConnector.cpp deleted file mode 100644 index 703a1f8d1a..0000000000 --- a/src/System/TcpConnector.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "TcpConnector.h" -#include -#include "System.h" -#include "TcpConnection.h" - -TcpConnector::TcpConnector() : system(nullptr) { -} - -TcpConnector::TcpConnector(System& system, const std::string& address, uint16_t port) : system(&system), m_address(address), m_port(port) { } - -TcpConnector::TcpConnector(TcpConnector&& other) : system(other.system) { - if (other.system != nullptr) { - m_address = other.m_address; - m_port = other.m_port; - other.system = nullptr; - } -} - -TcpConnector::~TcpConnector() { -} - -TcpConnector& TcpConnector::operator=(TcpConnector&& other) { - system = other.system; - if (other.system != nullptr) { - m_address = other.m_address; - m_port = other.m_port; - other.system = nullptr; - } - - return *this; -} - -TcpConnection TcpConnector::connect() { - assert(system != nullptr); - void* context = system->getCurrentContext(); - boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*static_cast(system->getIoService())); - boost::system::error_code errorCode; - socket->async_connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(m_address), m_port), [&](const boost::system::error_code& callbackErrorCode) { - errorCode = callbackErrorCode; - system->pushContext(context); - }); - - system->yield(); - if (errorCode) { - delete socket; - throw boost::system::system_error(errorCode); - } - - return TcpConnection(*system, socket); -} diff --git a/src/System/TcpListener.cpp b/src/System/TcpListener.cpp deleted file mode 100644 index 75071eac28..0000000000 --- a/src/System/TcpListener.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "TcpListener.h" -#include -#include "System.h" -#include "TcpConnection.h" - -TcpListener::TcpListener() : system(nullptr) { -} - -TcpListener::TcpListener(System& system, const std::string& address, uint16_t port) : system(&system), stopped(false) { - listener = new boost::asio::ip::tcp::acceptor(*static_cast(system.getIoService()), boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), port), true); -} - -TcpListener::TcpListener(TcpListener&& other) : system(other.system) { - if (other.system != nullptr) { - listener = other.listener; - stopped = other.stopped; - other.system = nullptr; - } -} - -TcpListener::~TcpListener() { - if (system != nullptr) { - delete static_cast(listener); - } -} - -TcpListener& TcpListener::operator=(TcpListener&& other) { - if (system != nullptr) { - delete static_cast(listener); - } - - system = other.system; - if (other.system != nullptr) { - listener = other.listener; - stopped = other.stopped; - other.system = nullptr; - } - - return *this; -} - -void TcpListener::start() { - stopped = false; -} - -void TcpListener::stop() { - stopped = true; -} - -TcpConnection TcpListener::accept() { - assert(system != nullptr); - if (stopped) { - throw std::runtime_error("Stopped"); - } - - void* context = system->getCurrentContext(); - boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(*static_cast(system->getIoService())); - boost::system::error_code errorCode; - static_cast(listener)->async_accept(*socket, [&](const boost::system::error_code& callbackErrorCode) { - errorCode = callbackErrorCode; - system->pushContext(context); - }); - - system->yield(); - if (errorCode) { - delete socket; - throw boost::system::system_error(errorCode); - } - - return TcpConnection(*system, socket); -} diff --git a/src/System/TcpStream.cpp b/src/System/TcpStream.cpp old mode 100644 new mode 100755 index da058f4bb9..4502fdf2ba --- a/src/System/TcpStream.cpp +++ b/src/System/TcpStream.cpp @@ -19,6 +19,8 @@ #include +using namespace System; + TcpStreambuf::TcpStreambuf(TcpConnection& connection) : connection(connection) { setg(&readBuf.front(), &readBuf.front(), &readBuf.front()); setp(reinterpret_cast(&writeBuf.front()), reinterpret_cast(&writeBuf.front() + writeBuf.max_size())); diff --git a/src/System/TcpStream.h b/src/System/TcpStream.h old mode 100644 new mode 100755 index 55c28d5538..359673b3e5 --- a/src/System/TcpStream.h +++ b/src/System/TcpStream.h @@ -20,7 +20,9 @@ #include #include -#include "TcpConnection.h" +#include + +namespace System { class TcpStreambuf : public std::streambuf { public: @@ -41,3 +43,5 @@ class TcpStreambuf : public std::streambuf { std::array readBuf; std::array writeBuf; }; + +} diff --git a/src/System/Timer.cpp b/src/System/Timer.cpp deleted file mode 100644 index 786d44853a..0000000000 --- a/src/System/Timer.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Timer.h" -#include -#include "System.h" - -Timer::Timer() : system(nullptr) { -} - -Timer::Timer(System& system) : system(&system) { - timer = new boost::asio::steady_timer(*static_cast(system.getIoService())); -} - -Timer::Timer(Timer&& other) : system(other.system) { - if (other.system != nullptr) { - timer = other.timer; - other.system = nullptr; - } -} - -Timer::~Timer() { - if (system != nullptr) { - delete static_cast(timer); - } -} - -Timer& Timer::operator=(Timer&& other) { - if (system != nullptr) { - delete static_cast(timer); - } - - system = other.system; - if (other.system != nullptr) { - timer = other.timer; - other.system = nullptr; - } - - return *this; -} - -void Timer::sleep(std::chrono::milliseconds time) { - assert(system != nullptr); - static_cast(timer)->expires_from_now(time); - void* context = system->getCurrentContext(); - boost::system::error_code errorCode; - static_cast(timer)->async_wait([&](const boost::system::error_code& callbackErrorCode) { - errorCode = callbackErrorCode; - system->pushContext(context); - }); - - system->yield(); - if (errorCode) { - throw boost::system::system_error(errorCode); - } -} diff --git a/src/common/ObserverManager.h b/src/common/ObserverManager.h index 4387b21c9a..b1e1e17c49 100644 --- a/src/common/ObserverManager.h +++ b/src/common/ObserverManager.h @@ -106,6 +106,19 @@ class ObserverManager { } } + template + void notify(F notification, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) { + std::vector observersCopy; + { + std::unique_lock lock(m_observersMutex); + observersCopy = m_observers; + } + + for (T* observer : observersCopy) { + (observer->*notification)(arg0, arg1, arg2, arg3); + } + } + #else template diff --git a/src/common/ShuffleGenerator.h b/src/common/ShuffleGenerator.h new file mode 100644 index 0000000000..367f47900a --- /dev/null +++ b/src/common/ShuffleGenerator.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +template +class ShuffleGenerator { +public: + + ShuffleGenerator(T n, const Gen& gen = Gen()) : + N(n), generator(gen), count(n) {} + + T operator()() { + + if (count == 0) { + throw std::runtime_error("shuffle sequence ended"); + } + + typedef typename std::uniform_int_distribution distr_t; + typedef typename distr_t::param_type param_t; + + distr_t distr; + + T value = distr(generator, param_t(0, --count)); + + auto rvalIt = selected.find(count); + auto rval = rvalIt != selected.end() ? rvalIt->second : count; + + auto lvalIt = selected.find(value); + + if (lvalIt != selected.end()) { + value = lvalIt->second; + lvalIt->second = rval; + } else { + selected[value] = rval; + } + + return value; + } + + void reset() { + count = N; + selected.clear(); + } + +private: + + std::unordered_map selected; + T count; + const T N; + Gen generator; +}; + diff --git a/src/common/int-util.h b/src/common/int-util.h index 50e59a6946..0ac962ac05 100644 --- a/src/common/int-util.h +++ b/src/common/int-util.h @@ -26,6 +26,8 @@ #if defined(_MSC_VER) #include +#define inline __inline + static inline uint32_t rol32(uint32_t x, int r) { static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers"); return _rotl(x, r); diff --git a/src/common/static_assert.h b/src/common/static_assert.h new file mode 100755 index 0000000000..1ebe4c1c84 --- /dev/null +++ b/src/common/static_assert.h @@ -0,0 +1,9 @@ +#pragma once + +#ifndef __cplusplus +#ifdef __clang__ + +#define static_assert _Static_assert + +#endif +#endif diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h index f8409c5e20..45974dacdd 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/common/unordered_containers_boost_serialization.h @@ -28,8 +28,8 @@ namespace boost { namespace serialization { - template - inline void save(Archive &a, const std::unordered_map &x, const boost::serialization::version_type ver) + template + inline void save(Archive &a, const std::unordered_map &x, const boost::serialization::version_type ver) { size_t s = x.size(); a << s; @@ -40,8 +40,8 @@ namespace boost } } - template - inline void load(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) + template + inline void load(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) { x.clear(); size_t s = 0; @@ -128,6 +128,7 @@ namespace boost x.clear(); size_t s = 0; a >> s; + x.resize(s); for(size_t i = 0; i != s; i++) { hval v; @@ -160,6 +161,7 @@ namespace boost x.clear(); size_t s = 0; a >> s; + x.resize(s); for(size_t i = 0; i != s; i++) { h_key k; @@ -176,8 +178,8 @@ namespace boost split_free(a, x, ver); } - template - inline void serialize(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) + template + inline void serialize(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) { split_free(a, x, ver); } diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 9b0b185842..0267643854 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -32,7 +32,6 @@ static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *); static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *); static void ge_p2_0(ge_p2 *); static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *); -static void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *); static void fe_divpowm1(fe, const fe, const fe); /* Common functions */ @@ -1578,7 +1577,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { r = p - q */ -static void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { +void ge_sub(ge_p1p1 *r, const ge_p3 *p, const ge_cached *q) { fe t0; fe_add(r->X, p->Y, p->X); fe_sub(r->Y, p->Y, p->X); diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index fbd5bbe4e9..912404e9a4 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -103,6 +103,10 @@ void ge_p3_tobytes(unsigned char *, const ge_p3 *); extern const ge_precomp ge_base[32][8]; void ge_scalarmult_base(ge_p3 *, const unsigned char *); +/* From ge_sub.c */ + +void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *); + /* From ge_tobytes.c */ void ge_tobytes(unsigned char *, const ge_p2 *); diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index dbd9e42a9c..2664c30a97 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -33,12 +33,9 @@ namespace crypto { using std::abort; using std::int32_t; - using std::int64_t; using std::lock_guard; using std::mutex; using std::size_t; - using std::uint32_t; - using std::uint64_t; extern "C" { #include "crypto-ops.h" @@ -153,6 +150,26 @@ namespace crypto { sc_add(&derived_key, &base, &scalar); } + bool crypto_ops::underive_public_key(const key_derivation &derivation, size_t output_index, + const public_key &derived_key, public_key &base) { + ec_scalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, &derived_key) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, scalar); + ge_scalarmult_base(&point2, &scalar); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(&base, &point5); + return true; + } + struct s_comm { hash h; ec_point key; diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index bb598cffe8..21757e07a4 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -27,6 +28,8 @@ namespace crypto { + using std::size_t; + extern "C" { #include "random.h" } @@ -87,6 +90,8 @@ namespace crypto { friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); + static bool underive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); + friend bool underive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); static bool check_signature(const hash &, const public_key &, const signature &); @@ -113,6 +118,35 @@ namespace crypto { return res; } + /* Random number engine based on crypto::rand() + */ + template + class random_engine { + public: + typedef T result_type; + +#ifdef __clang__ + constexpr static T min() { + return (std::numeric_limits::min)(); + } + + constexpr static T max() { + return (std::numeric_limits::max)(); + } +#else + static T(min)() { + return (std::numeric_limits::min)(); + } + + static T(max)() { + return (std::numeric_limits::max)(); + } +#endif + typename std::enable_if::value, T>::type operator()() { + return rand(); + } + }; + /* Generate a new key pair */ inline void generate_keys(public_key &pub, secret_key &sec) { @@ -133,8 +167,8 @@ namespace crypto { /* To generate an ephemeral key used to send money to: * * The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in "extra" field. - * * Both the sender and the receiver generate key derivation from the transaction key, the receivers' "view" key and the output index. - * * The sender uses key derivation and the receivers' "spend" key to derive an ephemeral public key. + * * Both the sender and the receiver generate key derivation from the transaction key and the receivers' "view" key. + * * The sender uses key derivation, the output index, and the receivers' "spend" key to derive an ephemeral public key. * * The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key (to spend the money). */ inline bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { @@ -149,6 +183,13 @@ namespace crypto { crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); } + /* Inverse function of derive_public_key. It can be used by the receiver to find which "spend" key was used to generate a transaction. This may be useful if the receiver used multiple addresses which only differ in "spend" key. + */ + inline bool underive_public_key(const key_derivation &derivation, std::size_t output_index, + const public_key &derived_key, public_key &base) { + return crypto_ops::underive_public_key(derivation, output_index, derived_key, base); + } + /* Generation and checking of a standard signature. */ inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { @@ -194,6 +235,6 @@ namespace crypto { } } -CRYPTO_MAKE_COMPARABLE(public_key) +CRYPTO_MAKE_HASHABLE(public_key) CRYPTO_MAKE_HASHABLE(key_image) CRYPTO_MAKE_COMPARABLE(signature) diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 18d7147fb7..9a8f154745 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -24,6 +24,7 @@ #include #include +#include "../common/static_assert.h" #include "common/int-util.h" #include "warnings.h" diff --git a/src/crypto/random.h b/src/crypto/random.h index 5b68e81b26..c8eab97fb8 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -17,6 +17,8 @@ #pragma once +#if !defined(__cplusplus) #include +#endif void generate_random_bytes(size_t n, void *result); diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index 7ab087dde3..feee98dd6a 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -32,7 +32,7 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { size_t i, j; size_t cnt = count - 1; char (*ints)[HASH_SIZE]; - for (i = 1; i < 8 * sizeof(size_t); i <<= 1) { + for (i = 1; i < sizeof(size_t); i <<= 1) { cnt |= cnt >> i; } cnt &= ~(cnt >> 1); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index fc52139f41..a1110c7a66 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -145,7 +145,14 @@ const CheckpointData CHECKPOINTS[] = { {484500, "5cdf2101a0a62a0ab2a1ca0c15a6212b21f6dbdc42a0b7c0bcf65ca40b7a14fb"}, {506000, "3d54c1132f503d98d3f0d78bb46a4503c1a19447cb348361a2232e241cb45a3c"}, {544000, "f69dc61b6a63217f32fa64d5d0f9bd920873f57dfd79ebe1d7d6fb1345b56fe0"}, - {553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"} + {553300, "f7a5076b887ce5f4bb95b2729c0edb6f077a463f04f1bffe7f5cb0b16bb8aa5f"}, + {580000, "93aea06936fa4dc0a84c9109c9d5f0e1b0815f96898171e42fd2973d262ed9ac"}, + {602000, "a05fd2fccbb5f567ece940ebb62a82fdb1517ff5696551ae704e5f0ef8edb979"}, + {623000, "7c92dd374efd0221065c7d98fce0568a1a1c130b5da28bb3f338cdc367b93d0b"}, + {645000, "1eeba944c0dd6b9a1228a425a74076fbdbeaf9b657ba7ef02547d99f971de70d"}, + {667000, "a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995"}, + {689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"}, + {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"} }; } // cryptonote diff --git a/src/cryptonote_core/CoreConfig.cpp b/src/cryptonote_core/CoreConfig.cpp new file mode 100644 index 0000000000..e3a50772e5 --- /dev/null +++ b/src/cryptonote_core/CoreConfig.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CoreConfig.h" + +#include "common/util.h" +#include "common/command_line.h" + +namespace cryptonote { + +CoreConfig::CoreConfig() { + configFolder = tools::get_default_data_dir(); +} + +void CoreConfig::init(const boost::program_options::variables_map& options) { + configFolder = command_line::get_arg(options, command_line::arg_data_dir); +} + +void CoreConfig::initOptions(boost::program_options::options_description& desc) { +} +} //namespace cryptonote + diff --git a/src/cryptonote_core/CoreConfig.h b/src/cryptonote_core/CoreConfig.h new file mode 100644 index 0000000000..655e15e33d --- /dev/null +++ b/src/cryptonote_core/CoreConfig.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include + +namespace cryptonote { + +class CoreConfig { +public: + CoreConfig(); + + static void initOptions(boost::program_options::options_description& desc); + void init(const boost::program_options::variables_map& options); + + std::string configFolder; +}; + +} //namespace cryptonote diff --git a/src/cryptonote_core/IBlockchainStorageObserver.h b/src/cryptonote_core/IBlockchainStorageObserver.h new file mode 100644 index 0000000000..9136732a21 --- /dev/null +++ b/src/cryptonote_core/IBlockchainStorageObserver.h @@ -0,0 +1,26 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +namespace cryptonote { + class IBlockchainStorageObserver { + public: + virtual ~IBlockchainStorageObserver() { + } + + virtual void blockchainUpdated() = 0; + }; +} diff --git a/src/cryptonote_core/ICore.h b/src/cryptonote_core/ICore.h new file mode 100755 index 0000000000..38bddcadff --- /dev/null +++ b/src/cryptonote_core/ICore.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include + +#include "crypto/hash.h" +#include "cryptonote_protocol/blobdatatype.h" + +namespace cryptonote { +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; +struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; +struct Block; +struct Transaction; +struct i_cryptonote_protocol; +struct tx_verification_context; +struct BlockFullInfo; +class ICoreObserver; + +class ICore { +public: + virtual ~ICore() {} + + virtual bool addObserver(ICoreObserver* observer) = 0; + virtual bool removeObserver(ICoreObserver* observer) = 0; + + virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) = 0; + virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, + uint64_t& total_height, uint64_t& start_height, size_t max_count) = 0; + virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp) = 0; + virtual 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) = 0; + virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) = 0; + virtual i_cryptonote_protocol* get_protocol() = 0; + virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) = 0; + virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) = 0; + virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, + uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) = 0; + + virtual bool getBlockByHash(const crypto::hash &h, Block &blk) = 0; +}; + +} //namespace cryptonote diff --git a/src/cryptonote_core/ICoreObserver.h b/src/cryptonote_core/ICoreObserver.h new file mode 100644 index 0000000000..9e8a2b3531 --- /dev/null +++ b/src/cryptonote_core/ICoreObserver.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace cryptonote { + +class ICoreObserver { +public: + virtual ~ICoreObserver() {}; + virtual void blockchainUpdated() {}; + virtual void poolUpdated() {}; +}; + +} //namespace cryptonote diff --git a/src/cryptonote_core/ITxPoolObserver.h b/src/cryptonote_core/ITxPoolObserver.h new file mode 100755 index 0000000000..2cdf2caf77 --- /dev/null +++ b/src/cryptonote_core/ITxPoolObserver.h @@ -0,0 +1,26 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +namespace cryptonote { +class ITxPoolObserver { +public: + virtual ~ITxPoolObserver() { + } + + virtual void txDeletedFromPool() = 0; +}; +} diff --git a/src/cryptonote_core/MinerConfig.cpp b/src/cryptonote_core/MinerConfig.cpp new file mode 100644 index 0000000000..a524cf8864 --- /dev/null +++ b/src/cryptonote_core/MinerConfig.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "MinerConfig.h" + +#include "common/command_line.h" + +namespace cryptonote { + +namespace { +const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; +const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; +const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; +} + +MinerConfig::MinerConfig() { + miningThreads = 0; +} + +void MinerConfig::initOptions(boost::program_options::options_description& desc) { + command_line::add_arg(desc, arg_extra_messages); + command_line::add_arg(desc, arg_start_mining); + command_line::add_arg(desc, arg_mining_threads); +} + +void MinerConfig::init(const boost::program_options::variables_map& options) { + if(command_line::has_arg(options, arg_extra_messages)) { + extraMessages = command_line::get_arg(options, arg_extra_messages); + } + + if (command_line::has_arg(options, arg_start_mining)) { + startMining = command_line::get_arg(options, arg_start_mining); + } + + if (command_line::has_arg(options, arg_mining_threads)) { + miningThreads = command_line::get_arg(options, arg_mining_threads); + } +} + +} //namespace cryptonote + diff --git a/src/cryptonote_core/MinerConfig.h b/src/cryptonote_core/MinerConfig.h new file mode 100644 index 0000000000..ae9675d424 --- /dev/null +++ b/src/cryptonote_core/MinerConfig.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include + +namespace cryptonote { + +class MinerConfig { +public: + MinerConfig(); + + static void initOptions(boost::program_options::options_description& desc); + void init(const boost::program_options::variables_map& options); + + std::string extraMessages; + std::string startMining; + uint32_t miningThreads; +}; + +} //namespace cryptonote diff --git a/src/cryptonote_core/Transaction.cpp b/src/cryptonote_core/Transaction.cpp new file mode 100644 index 0000000000..ad15b0f3ca --- /dev/null +++ b/src/cryptonote_core/Transaction.cpp @@ -0,0 +1,608 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ITransaction.h" +#include "TransactionExtra.h" + +#include "cryptonote_format_utils.h" +#include "account.h" + +#include +#include +#include + +namespace { + + using namespace cryptonote; + using namespace CryptoNote; + + void derivePublicKey(const AccountAddress& to, const crypto::secret_key& txKey, size_t outputIndex, crypto::public_key& ephemeralKey) { + crypto::key_derivation derivation; + crypto::generate_key_derivation(*reinterpret_cast(&to.viewPublicKey), txKey, derivation); + crypto::derive_public_key(derivation, outputIndex, *reinterpret_cast(&to.spendPublicKey), ephemeralKey); + } + + bool checkInputsKeyimagesDiff(const cryptonote::Transaction& tx) { + std::unordered_set ki; + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } + } + return true; + } + + + // TransactionInput helper functions + + size_t getRequiredSignaturesCount(const TransactionInput& in) { + if (in.type() == typeid(TransactionInputToKey)) { + return boost::get(in).keyOffsets.size(); + } + if (in.type() == typeid(TransactionInputMultisignature)) { + return boost::get(in).signatures; + } + return 0; + } + + uint64_t getTransactionInputAmount(const TransactionInput& in) { + if (in.type() == typeid(TransactionInputToKey)) { + return boost::get(in).amount; + } + if (in.type() == typeid(TransactionInputMultisignature)) { + return boost::get(in).amount; + } + return 0; + } + + TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { + if (in.type() == typeid(TransactionInputToKey)) { + return TransactionTypes::InputType::Key; + } + if (in.type() == typeid(TransactionInputMultisignature)) { + return TransactionTypes::InputType::Multisignature; + } + if (in.type() == typeid(TransactionInputGenerate)) { + return TransactionTypes::InputType::Generating; + } + return TransactionTypes::InputType::Invalid; + } + + const TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index) { + if (transaction.vin.size() <= index) { + throw std::runtime_error("Transaction input index out of range"); + } + return transaction.vin[index]; + } + + const TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { + const auto& input = getInputChecked(transaction, index); + if (getTransactionInputType(input) != type) { + throw std::runtime_error("Unexpected transaction input type"); + } + return input; + } + + // TransactionOutput helper functions + + TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { + if (out.type() == typeid(TransactionOutputToKey)) { + return TransactionTypes::OutputType::Key; + } + if (out.type() == typeid(TransactionOutputMultisignature)) { + return TransactionTypes::OutputType::Multisignature; + } + return TransactionTypes::OutputType::Invalid; + } + + const TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index) { + if (transaction.vout.size() <= index) { + throw std::runtime_error("Transaction output index out of range"); + } + return transaction.vout[index]; + } + + const TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { + const auto& output = getOutputChecked(transaction, index); + if (getTransactionOutputType(output.target) != type) { + throw std::runtime_error("Unexpected transaction output target type"); + } + return output; + } +} + + +namespace CryptoNote { + + using namespace TransactionTypes; + + //////////////////////////////////////////////////////////////////////// + // class Transaction declaration + //////////////////////////////////////////////////////////////////////// + + class Transaction : public ITransaction { + public: + Transaction(); + Transaction(const Blob& txblob); + Transaction(const cryptonote::Transaction& tx); + + // ITransactionReader + virtual Hash getTransactionHash() const override; + virtual Hash getTransactionPrefixHash() const override; + virtual PublicKey getTransactionPublicKey() const override; + virtual uint64_t getUnlockTime() const override; + virtual bool getPaymentId(Hash& hash) const override; + virtual bool getExtraNonce(std::string& nonce) const override; + + // inputs + virtual size_t getInputCount() const override; + virtual uint64_t getInputTotalAmount() const override; + virtual TransactionTypes::InputType getInputType(size_t index) const override; + virtual void getInput(size_t index, TransactionTypes::InputKey& input) const override; + virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const override; + + // outputs + virtual size_t getOutputCount() const override; + virtual uint64_t getOutputTotalAmount() const override; + virtual TransactionTypes::OutputType getOutputType(size_t index) const override; + virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const override; + virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const override; + + virtual size_t getRequiredSignaturesCount(size_t index) const override; + virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; + + // various checks + virtual bool validateInputs() const override; + virtual bool validateOutputs() const override; + virtual bool validateSignatures() const override; + + // get serialized transaction + virtual Blob getTransactionData() const override; + + // ITransactionWriter + + virtual void setUnlockTime(uint64_t unlockTime) override; + virtual void setPaymentId(const Hash& hash) override; + virtual void setExtraNonce(const std::string& nonce) override; + + // Inputs/Outputs + virtual size_t addInput(const TransactionTypes::InputKey& input) override; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) override; + virtual size_t addInput(const TransactionTypes::InputMultisignature& input) override; + + virtual size_t addOutput(uint64_t amount, const AccountAddress& to) override; + virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) override; + + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) override; + virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) override; + + // secret key + virtual bool getTransactionSecretKey(SecretKey& key) const override; + virtual void setTransactionSecretKey(const SecretKey& key) override; + + private: + + std::vector& getSignatures(size_t input); + + const crypto::secret_key& txSecretKey() const { + if (!secretKey) { + throw std::runtime_error("Operation requires transaction secret key"); + } + return *secretKey; + } + + cryptonote::Transaction constructFinalTransaction() const { + cryptonote::Transaction tx(transaction); + tx.extra = extra.serialize(); + return tx; + } + + void checkIfSigning() const { + if (!transaction.signatures.empty()) { + throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); + } + } + + cryptonote::Transaction transaction; + boost::optional secretKey; + TransactionExtra extra; + }; + + + //////////////////////////////////////////////////////////////////////// + // class Transaction implementation + //////////////////////////////////////////////////////////////////////// + + std::unique_ptr createTransaction() { + return std::unique_ptr(new Transaction()); + } + + std::unique_ptr createTransaction(const Blob& transactionBlob) { + return std::unique_ptr(new Transaction(transactionBlob)); + } + + std::unique_ptr createTransaction(const cryptonote::Transaction& tx) { + return std::unique_ptr(new Transaction(tx)); + } + + Transaction::Transaction() { + cryptonote::KeyPair txKeys(cryptonote::KeyPair::generate()); + + transaction.version = CURRENT_TRANSACTION_VERSION; + transaction.unlockTime = 0; + + tx_extra_pub_key pk = { txKeys.pub }; + extra.set(pk); + + secretKey = txKeys.sec; + } + + Transaction::Transaction(const Blob& data) { + cryptonote::blobdata blob(reinterpret_cast(data.data()), data.size()); + if (!cryptonote::parse_and_validate_tx_from_blob(blob, transaction)) { + throw std::runtime_error("Invalid transaction data"); + } + + extra.parse(transaction.extra); + } + + Transaction::Transaction(const cryptonote::Transaction& tx) : transaction(tx) { + extra.parse(transaction.extra); + } + + Hash Transaction::getTransactionHash() const { + auto hash = get_transaction_hash(constructFinalTransaction()); + return reinterpret_cast(hash); + } + + Hash Transaction::getTransactionPrefixHash() const { + auto hash = get_transaction_prefix_hash(constructFinalTransaction()); + return reinterpret_cast(hash); + } + + PublicKey Transaction::getTransactionPublicKey() const { + crypto::public_key pk(null_pkey); + extra.getPublicKey(pk); + return reinterpret_cast(pk); + } + + uint64_t Transaction::getUnlockTime() const { + return transaction.unlockTime; + } + + void Transaction::setUnlockTime(uint64_t unlockTime) { + checkIfSigning(); + transaction.unlockTime = unlockTime; + } + + bool Transaction::getTransactionSecretKey(SecretKey& key) const { + if (!secretKey) { + return false; + } + key = reinterpret_cast(secretKey.get()); + return true; + } + + void Transaction::setTransactionSecretKey(const SecretKey& key) { + const auto& sk = reinterpret_cast(key); + crypto::public_key pk; + crypto::public_key txPubKey; + + crypto::secret_key_to_public_key(sk, pk); + extra.getPublicKey(txPubKey); + + if (txPubKey != pk) { + throw std::runtime_error("Secret transaction key does not match public key"); + } + + secretKey = reinterpret_cast(key); + } + + size_t Transaction::addInput(const InputKey& input) { + checkIfSigning(); + TransactionInputToKey inKey = { input.amount, input.keyOffsets, *reinterpret_cast(&input.keyImage) }; + transaction.vin.emplace_back(inKey); + return transaction.vin.size() - 1; + } + + size_t Transaction::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) { + checkIfSigning(); + InputKey input; + input.amount = info.amount; + + generate_key_image_helper( + reinterpret_cast(senderKeys), + reinterpret_cast(info.realOutput.transactionPublicKey), + info.realOutput.outputInTransaction, + reinterpret_cast(ephKeys), + reinterpret_cast(input.keyImage)); + + // fill outputs array and use relative offsets + for (const auto& out : info.outputs) { + input.keyOffsets.push_back(out.outputIndex); + } + input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); + + return addInput(input); + } + + size_t Transaction::addInput(const InputMultisignature& input) { + checkIfSigning(); + TransactionInputMultisignature inMsig; + inMsig.amount = input.amount; + inMsig.outputIndex = input.outputIndex; + inMsig.signatures = input.signatures; + transaction.vin.push_back(inMsig); + return transaction.vin.size() - 1; + } + + size_t Transaction::addOutput(uint64_t amount, const AccountAddress& to) { + checkIfSigning(); + TransactionOutputToKey outKey; + derivePublicKey(to, txSecretKey(), transaction.vout.size(), outKey.key); + TransactionOutput out = { amount, outKey }; + transaction.vout.emplace_back(out); + return transaction.vout.size() - 1; + } + + size_t Transaction::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { + checkIfSigning(); + const auto& txKey = txSecretKey(); + size_t outputIndex = transaction.vout.size(); + TransactionOutputMultisignature outMsig; + outMsig.requiredSignatures = requiredSignatures; + outMsig.keys.resize(to.size()); + for (int i = 0; i < to.size(); ++i) { + derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); + } + TransactionOutput out = { amount, outMsig }; + transaction.vout.emplace_back(out); + return outputIndex; + } + + void Transaction::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) { + const auto& input = boost::get(getInputChecked(transaction, index, InputType::Key)); + Hash prefixHash = getTransactionPrefixHash(); + + std::vector signatures; + std::vector keysPtrs; + + for (const auto& o : info.outputs) { + keysPtrs.push_back(reinterpret_cast(&o.targetKey)); + } + + signatures.resize(keysPtrs.size()); + + generate_ring_signature( + reinterpret_cast(prefixHash), + reinterpret_cast(input.keyImage), + keysPtrs, + reinterpret_cast(ephKeys.secretKey), + info.realOutput.transactionIndex, + signatures.data()); + + getSignatures(index) = signatures; + } + + void Transaction::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { + crypto::key_derivation derivation; + crypto::public_key ephemeralPublicKey; + crypto::secret_key ephemeralSecretKey; + + crypto::generate_key_derivation( + reinterpret_cast(sourceTransactionKey), + reinterpret_cast(accountKeys.viewSecretKey), + derivation); + + crypto::derive_public_key(derivation, outputIndex, + reinterpret_cast(accountKeys.address.spendPublicKey), ephemeralPublicKey); + crypto::derive_secret_key(derivation, outputIndex, + reinterpret_cast(accountKeys.spendSecretKey), ephemeralSecretKey); + + crypto::signature signature; + auto txPrefixHash = getTransactionPrefixHash(); + + crypto::generate_signature(reinterpret_cast(txPrefixHash), + ephemeralPublicKey, ephemeralSecretKey, signature); + + getSignatures(index).push_back(signature); + } + + std::vector& Transaction::getSignatures(size_t input) { + // update signatures container size if needed + if (transaction.signatures.size() < transaction.vin.size()) { + transaction.signatures.resize(transaction.vin.size()); + } + // check range + if (input >= transaction.signatures.size()) { + throw std::runtime_error("Invalid input index"); + } + + return transaction.signatures[input]; + } + + std::vector Transaction::getTransactionData() const { + return stringToVector(t_serializable_object_to_blob(constructFinalTransaction())); + } + + void Transaction::setPaymentId(const Hash& hash) { + checkIfSigning(); + blobdata paymentIdBlob; + set_payment_id_to_tx_extra_nonce(paymentIdBlob, reinterpret_cast(hash)); + setExtraNonce(paymentIdBlob); + } + + bool Transaction::getPaymentId(Hash& hash) const { + blobdata nonce; + if (getExtraNonce(nonce)) { + crypto::hash paymentId; + if (get_payment_id_from_tx_extra_nonce(nonce, paymentId)) { + hash = reinterpret_cast(paymentId); + return true; + } + } + return false; + } + + void Transaction::setExtraNonce(const std::string& nonce) { + checkIfSigning(); + tx_extra_nonce extraNonce = { nonce }; + extra.set(extraNonce); + } + + bool Transaction::getExtraNonce(std::string& nonce) const { + tx_extra_nonce extraNonce; + if (extra.get(extraNonce)) { + nonce = extraNonce.nonce; + return true; + } + return false; + } + + size_t Transaction::getInputCount() const { + return transaction.vin.size(); + } + + uint64_t Transaction::getInputTotalAmount() const { + return std::accumulate(transaction.vin.begin(), transaction.vin.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { + return val + getTransactionInputAmount(in); }); + } + + TransactionTypes::InputType Transaction::getInputType(size_t index) const { + return getTransactionInputType(getInputChecked(transaction, index)); + } + + void Transaction::getInput(size_t index, InputKey& input) const { + const auto& k = boost::get(getInputChecked(transaction, index, InputType::Key)); + input.amount = k.amount; + input.keyImage = reinterpret_cast(k.keyImage); + input.keyOffsets = k.keyOffsets; + } + + void Transaction::getInput(size_t index, InputMultisignature& input) const { + const auto& m = boost::get(getInputChecked(transaction, index, InputType::Multisignature)); + input.amount = m.amount; + input.outputIndex = m.outputIndex; + input.signatures = m.signatures; + } + + size_t Transaction::getOutputCount() const { + return transaction.vout.size(); + } + + uint64_t Transaction::getOutputTotalAmount() const { + return std::accumulate(transaction.vout.begin(), transaction.vout.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { + return val + out.amount; }); + } + + TransactionTypes::OutputType Transaction::getOutputType(size_t index) const { + return getTransactionOutputType(getOutputChecked(transaction, index).target); + } + + void Transaction::getOutput(size_t index, OutputKey& output) const { + const auto& out = getOutputChecked(transaction, index, OutputType::Key); + const auto& k = boost::get(out.target); + output.amount = out.amount; + output.key = reinterpret_cast(k.key); + } + + void Transaction::getOutput(size_t index, OutputMultisignature& output) const { + const auto& out = getOutputChecked(transaction, index, OutputType::Multisignature); + const auto& m = boost::get(out.target); + output.amount = out.amount; + output.keys = reinterpret_cast&>(m.keys); + output.requiredSignatures = m.requiredSignatures; + } + + bool isOutToKey(const crypto::public_key& spendPublicKey, const crypto::public_key& outKey, const crypto::key_derivation& derivation, size_t keyIndex) { + crypto::public_key pk; + derive_public_key(derivation, keyIndex, spendPublicKey, pk); + return pk == outKey; + } + + bool Transaction::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { + account_keys keys; + keys.m_account_address = reinterpret_cast(addr); + // only view secret key is used, spend key is not needed + keys.m_view_secret_key = reinterpret_cast(viewSecretKey); + + auto pk = getTransactionPublicKey(); + crypto::public_key txPubKey = reinterpret_cast(pk); + + amount = 0; + size_t keyIndex = 0; + uint32_t outputIndex = 0; + + crypto::key_derivation derivation; + generate_key_derivation(txPubKey, keys.m_view_secret_key, derivation); + + for (const TransactionOutput& o : transaction.vout) { + assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); + if (o.target.type() == typeid(TransactionOutputToKey)) { + if (is_out_to_acc(keys, boost::get(o.target), derivation, keyIndex)) { + out.push_back(outputIndex); + amount += o.amount; + } + ++keyIndex; + } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { + const auto& target = boost::get(o.target); + for (const auto& key : target.keys) { + if (isOutToKey(keys.m_account_address.m_spendPublicKey, key, derivation, static_cast(outputIndex))) { + out.push_back(outputIndex); + } + ++keyIndex; + } + } + ++outputIndex; + } + + return true; + } + + size_t Transaction::getRequiredSignaturesCount(size_t index) const { + return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); + } + + bool Transaction::validateInputs() const { + return + check_inputs_types_supported(transaction) && + check_inputs_overflow(transaction) && + checkInputsKeyimagesDiff(transaction) && + checkMultisignatureInputsDiff(transaction); + } + + bool Transaction::validateOutputs() const { + return + check_outs_valid(transaction) && + check_outs_overflow(transaction); + } + + bool Transaction::validateSignatures() const { + if (transaction.signatures.size() < transaction.vin.size()) { + return false; + } + + for (size_t i = 0; i < transaction.vin.size(); ++i) { + if (getRequiredSignaturesCount(i) > transaction.signatures[i].size()) { + return false; + } + } + + return true; + } +} diff --git a/src/wallet/WalletTxSendingState.h b/src/cryptonote_core/TransactionApi.h similarity index 68% rename from src/wallet/WalletTxSendingState.h rename to src/cryptonote_core/TransactionApi.h index e697301654..7af07943d9 100644 --- a/src/wallet/WalletTxSendingState.h +++ b/src/cryptonote_core/TransactionApi.h @@ -17,30 +17,15 @@ #pragma once -#include "IWallet.h" +#include +#include "ITransaction.h" -#include -#include +namespace cryptonote { + struct Transaction; +} namespace CryptoNote { - -class WalletTxSendingState -{ -public: - enum State - { - SENDING, - ERRORED, - NOT_FOUND - }; - - void sending(TransactionId id); - void sent(TransactionId id); - void error(TransactionId id); - State state(TransactionId id); - -private: - std::map m_states; -}; - -} //namespace CryptoNote + std::unique_ptr createTransaction(); + std::unique_ptr createTransaction(const Blob& transactionBlob); + std::unique_ptr createTransaction(const cryptonote::Transaction& tx); +} diff --git a/src/cryptonote_core/TransactionExtra.h b/src/cryptonote_core/TransactionExtra.h new file mode 100644 index 0000000000..34fc7e0eeb --- /dev/null +++ b/src/cryptonote_core/TransactionExtra.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_format_utils.h" + +namespace CryptoNote { + + inline std::vector stringToVector(const std::string& s) { + std::vector vec( + reinterpret_cast(s.data()), + reinterpret_cast(s.data()) + s.size()); + return vec; + } + + class TransactionExtra { + public: + TransactionExtra() {} + TransactionExtra(const std::vector& extra) { + parse(extra); + } + + bool parse(const std::vector& extra) { + fields.clear(); + return cryptonote::parse_tx_extra(extra, fields); + } + + template + bool get(T& value) const { + auto it = find(typeid(T)); + if (it == fields.end()) { + return false; + } + value = boost::get(*it); + return true; + } + + template + void set(const T& value) { + auto it = find(typeid(T)); + if (it != fields.end()) { + *it = value; + } else { + fields.push_back(value); + } + } + + bool getPublicKey(crypto::public_key& pk) const { + cryptonote::tx_extra_pub_key extraPk; + if (!get(extraPk)) { + return false; + } + pk = extraPk.pub_key; + return true; + } + + std::vector serialize() const { + std::ostringstream out; + binary_archive ar(out); + for (const auto& f : fields) { + ::do_serialize(ar, const_cast(f)); + } + return stringToVector(out.str()); + } + + private: + + std::vector::const_iterator find(const std::type_info& t) const { + return std::find_if(fields.begin(), fields.end(), [&t](const cryptonote::tx_extra_field& f) { return t == f.type(); }); + } + + std::vector::iterator find(const std::type_info& t) { + return std::find_if(fields.begin(), fields.end(), [&t](const cryptonote::tx_extra_field& f) { return t == f.type(); }); + } + + std::vector fields; + }; + +} \ No newline at end of file diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index c7ba60451b..c4ada894e5 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -50,5 +50,9 @@ namespace cryptonote { return m_keys; } + + void account_base::set_keys(const account_keys& keys) { + m_keys = keys; + } //----------------------------------------------------------------- } diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index a53ebfa346..62594eb4ea 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -36,7 +36,9 @@ namespace cryptonote { public: account_base(); void generate(); + const account_keys& get_keys() const; + void set_keys(const account_keys& keys); uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index 4dfd8733c9..fee5dcbf2a 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -31,6 +31,7 @@ #include "time_helper.h" #include "common/boost_serialization_helper.h" +#include "common/ShuffleGenerator.h" #include "cryptonote_format_utils.h" #include "cryptonote_boost_serialization.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -292,6 +293,14 @@ blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& m_spent_keys.set_deleted_key(nullImage); } +bool blockchain_storage::addObserver(IBlockchainStorageObserver* observer) { + return m_observerManager.add(observer); +} + +bool blockchain_storage::removeObserver(IBlockchainStorageObserver* observer) { + return m_observerManager.remove(observer); +} + bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) { return check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id); } @@ -399,14 +408,26 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi crypto::hash transactionHash = get_transaction_hash(transaction.tx); TransactionIndex transactionIndex = { b, t }; m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + + // process inputs for (auto& i : transaction.tx.vin) { if (i.type() == typeid(TransactionInputToKey)) { m_spent_keys.insert(::boost::get(i).keyImage); + } else if (i.type() == typeid(TransactionInputMultisignature)) { + auto out = ::boost::get(i); + m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; } } + // process outputs for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { - m_outputs[transaction.tx.vout[o].amount].push_back(std::make_pair<>(transactionIndex, o)); + const auto& out = transaction.tx.vout[o]; + if(out.target.type() == typeid(TransactionOutputToKey)) { + m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); + } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { + MultisignatureOutputUsage usage = { transactionIndex, o, false }; + m_multisignatureOutputs[out.amount].push_back(usage); + } } } } @@ -490,6 +511,22 @@ crypto::hash blockchain_storage::get_tail_id() { return m_blockIndex.getTailId(); } +bool blockchain_storage::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector& new_txs, std::vector& deleted_tx_ids) { + CRITICAL_REGION_LOCAL1(m_tx_pool); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if (known_block_id != get_tail_id()) { + return false; + } + + std::vector new_tx_ids; + m_tx_pool.get_difference(known_pool_tx_ids, new_tx_ids, deleted_tx_ids); + + std::vector misses; + get_transactions(new_tx_ids, new_txs, misses, true); + assert(misses.empty()); + return true; +} + bool blockchain_storage::get_short_chain_history(std::list& ids) { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_blockIndex.getShortChainHistory(ids); @@ -1166,8 +1203,8 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector(time(NULL))); CRITICAL_REGION_LOCAL(m_blockchain_lock); + for (uint64_t amount : req.amounts) { 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; @@ -1182,22 +1219,11 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO //lets find upper bound of not fresh outs size_t up_index_limit = find_end_of_allowed_index(amount_outs); CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); - if (amount_outs.size() > req.outs_count) { - std::set used; - size_t try_count = 0; - for (uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) { - size_t i = rand() % up_index_limit; - if (used.count(i)) - continue; - bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i); - used.insert(i); - if (added) - ++j; - ++try_count; - } - } else { - for (size_t i = 0; i != up_index_limit; i++) { - add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + + if (up_index_limit > 0) { + ShuffleGenerator> generator(up_index_limit); + for (uint64_t j = 0; j < up_index_limit && result_outs.outs.size() < req.outs_count; ++j) { + add_out_to_get_random_outs(amount_outs, result_outs, amount, generator()); } } } @@ -1621,8 +1647,9 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont return false; } - CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process - CRITICAL_REGION_LOCAL1(m_blockchain_lock); + bool add_result; + CRITICAL_REGION_BEGIN(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process + CRITICAL_REGION_BEGIN1(m_blockchain_lock); if (have_block(id)) { LOG_PRINT_L3("block with id = " << id << " already exists"); bvc.m_already_exists = true; @@ -1633,11 +1660,18 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont if (!(bl.prevId == get_tail_id())) { //chain switching or wrong block bvc.m_added_to_main_chain = false; - return handle_alternative_block(bl, id, bvc); - //never relay alternative blocks + add_result = handle_alternative_block(bl, id, bvc); + } else { + add_result = pushBlock(bl, bvc); + } + CRITICAL_REGION_END(); + CRITICAL_REGION_END(); + + if (add_result && bvc.m_added_to_main_chain) { + m_observerManager.notify(&IBlockchainStorageObserver::blockchainUpdated); } - return pushBlock(bl, bvc); + return add_result; } const blockchain_storage::TransactionEntry& blockchain_storage::transactionByIndex(TransactionIndex index) { @@ -1864,7 +1898,8 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); } else if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputMultisignature)) { auto& amountOutputs = m_multisignatureOutputs[transaction.tx.vout[output].amount]; - MultisignatureOutputUsage outputUsage = {transactionIndex, output, false}; + transaction.m_global_output_indexes[output] = amountOutputs.size(); + MultisignatureOutputUsage outputUsage = { transactionIndex, output, false }; amountOutputs.push_back(outputUsage); } } diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 65e6a43272..29d38e977a 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -19,19 +19,21 @@ #include -#include "Currency.h" -#include "SwappedVector.h" -#include "UpgradeDetector.h" -#include "cryptonote_format_utils.h" -#include "tx_pool.h" -#include "common/util.h" -#include "checkpoints.h" - #include "google/sparse_hash_set" #include "google/sparse_hash_map" -#include "ITransactionValidator.h" -#include "BlockIndex.h" +#include "common/ObserverManager.h" +#include "common/util.h" +#include "cryptonote_core/BlockIndex.h" +#include "cryptonote_core/checkpoints.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/IBlockchainStorageObserver.h" +#include "cryptonote_core/ITransactionValidator.h" +#include "cryptonote_core/SwappedVector.h" +#include "cryptonote_core/UpgradeDetector.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/tx_pool.h" + namespace cryptonote { struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; @@ -46,6 +48,9 @@ namespace cryptonote { public: blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool); + bool addObserver(IBlockchainStorageObserver* observer); + bool removeObserver(IBlockchainStorageObserver* observer); + // ITransactionValidator virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock); virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed); @@ -94,6 +99,8 @@ namespace cryptonote { uint64_t get_current_comulative_blocksize_limit(); bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); + bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector& new_txs, std::vector& deleted_tx_ids); + template bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { @@ -195,6 +202,7 @@ namespace cryptonote { tx_memory_pool& m_tx_pool; epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock crypto::cn_context m_cn_context; + tools::ObserverManager m_observerManager; key_images_container m_spent_keys; size_t m_current_block_cumul_sz_limit; diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 5a02a73cf0..9e804c0b80 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -28,6 +28,7 @@ #include "cryptonote_core/tx_extra.h" #include "serialization/binary_archive.h" #include "serialization/crypto.h" +#include "serialization/keyvalue_serialization.h" // eepe named serialization #include "serialization/debug_archive.h" #include "serialization/json_archive.h" #include "serialization/serialization.h" @@ -219,7 +220,6 @@ namespace cryptonote { ar.end_array(); END_SERIALIZE() - private: static size_t getSignatureSize(const TransactionInput& input) { struct txin_signature_size_visitor : public boost::static_visitor { size_t operator()(const TransactionInputGenerate& txin) const { return 0; } @@ -318,7 +318,6 @@ namespace cryptonote { } END_SERIALIZE() - private: ParentBlock& m_parentBlock; uint64_t& m_timestamp; uint32_t& m_nonce; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp old mode 100644 new mode 100755 index ce12c8191d..58917762be --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -32,6 +32,7 @@ #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_stat_info.h" #include "cryptonote_core/miner.h" +#include "cryptonote_core/CoreConfig.h" #include "cryptonote_config.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" @@ -53,9 +54,12 @@ namespace cryptonote m_starter_message_showed(false) { set_cryptonote_protocol(pprotocol); + m_blockchain_storage.addObserver(this); + m_mempool.addObserver(this); } //----------------------------------------------------------------------------------------------- core::~core() { + m_blockchain_storage.removeObserver(this); } //----------------------------------------------------------------------------------------------- void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) @@ -116,17 +120,15 @@ namespace cryptonote return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::init(const boost::program_options::variables_map& vm, bool load_existing) - { - bool r = handle_command_line(vm); - - r = m_mempool.init(m_config_folder); + bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { + m_config_folder = config.configFolder; + bool r = m_mempool.init(m_config_folder); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); r = m_blockchain_storage.init(m_config_folder, load_existing); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); - r = m_miner->init(vm); + r = m_miner->init(minerConfig); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); return load_state_data(); @@ -203,6 +205,7 @@ namespace cryptonote if (tvc.m_added_to_pool) { LOG_PRINT_L1("tx added: " << tx_hash); + poolUpdated(); } return r; @@ -390,6 +393,11 @@ namespace cryptonote m_miner->on_synchronized(); } //----------------------------------------------------------------------------------------------- + bool core::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { + isBcActual = m_blockchain_storage.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, new_txs, deleted_tx_ids); + return true; + } + //----------------------------------------------------------------------------------------------- bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { if (block_blob.size() > m_currency.maxBlockBlobSize()) { LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); @@ -485,6 +493,10 @@ namespace cryptonote return m_blockchain_storage.handle_get_objects(arg, rsp); } //----------------------------------------------------------------------------------------------- + bool core::getBlockByHash(const crypto::hash &h, Block &blk) { + return core::get_block_by_hash(h, blk); + } + crypto::hash core::get_block_id_by_height(uint64_t height) { return m_blockchain_storage.get_block_id_by_height(height); @@ -530,4 +542,89 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::addObserver(ICoreObserver* observer) { + return m_observerManager.add(observer); + } + + bool core::removeObserver(ICoreObserver* observer) { + return m_observerManager.remove(observer); + } + + void core::blockchainUpdated() { + m_observerManager.notify(&ICoreObserver::blockchainUpdated); + } + + void core::txDeletedFromPool() { + poolUpdated(); + } + + void core::poolUpdated() { + m_observerManager.notify(&ICoreObserver::poolUpdated); + } + + bool core::queryBlocks(const std::list& knownBlockIds, uint64_t timestamp, + uint64_t& resStartHeight, uint64_t& resCurrentHeight, uint64_t& resFullOffset, std::list& entries) { + + LockedBlockchainStorage lbs(m_blockchain_storage); + + uint64_t currentHeight = lbs->get_current_blockchain_height(); + uint64_t startOffset = 0; + + if (!lbs->find_blockchain_supplement(knownBlockIds, startOffset)) { + return false; + } + + uint64_t startFullOffset = 0; + + if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) + startFullOffset = startOffset; + + resFullOffset = startFullOffset; + + if (startOffset != startFullOffset) { + std::list blockIds; + if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { + return false; + } + + for (const auto& id : blockIds) { + entries.push_back(BlockFullInfo()); + entries.back().block_id = id; + } + } + + auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); + + if (blocksLeft) { + std::list blocks; + lbs->get_blocks(startFullOffset, blocksLeft, blocks); + + for (auto& b : blocks) { + BlockFullInfo item; + + item.block_id = get_block_hash(b); + + if (b.timestamp >= timestamp) { + // query transactions + std::list txs; + std::list missedTxs; + lbs->get_transactions(b.txHashes, txs, missedTxs); + + // fill data + block_complete_entry& completeEntry = item; + completeEntry.block = block_to_blob(b); + for (auto& tx : txs) { + completeEntry.txs.push_back(tx_to_blob(tx)); + } + } + + entries.push_back(std::move(item)); + } + } + + resCurrentHeight = currentHeight; + resStartHeight = startOffset; + + return true; + } } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 2596908699..8704350117 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -26,9 +26,13 @@ #include "tx_pool.h" #include "blockchain_storage.h" #include "cryptonote_core/i_miner_handler.h" +#include "cryptonote_core/MinerConfig.h" #include "connection_context.h" #include "warnings.h" #include "crypto/hash.h" +#include "ICore.h" +#include "ICoreObserver.h" +#include "common/ObserverManager.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -36,30 +40,33 @@ DISABLE_VS_WARNINGS(4355) namespace cryptonote { struct core_stat_info; class miner; + class CoreConfig; - class core: public i_miner_handler { + class core : public ICore, public i_miner_handler, public IBlockchainStorageObserver, public ITxPoolObserver { public: core(const Currency& currency, i_cryptonote_protocol* pprotocol); ~core(); bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp, cryptonote_connection_context& context); bool on_idle(); - bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); + virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); bool handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); const Currency& currency() const { return m_currency; } - i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} //-------------------- i_miner_handler ----------------------- virtual bool handle_block_found(Block& b); virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + bool addObserver(ICoreObserver* observer); + bool removeObserver(ICoreObserver* observer); miner& get_miner() { return *m_miner; } static void init_options(boost::program_options::options_description& desc); - bool init(const boost::program_options::variables_map& vm, bool load_existing); + bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing); bool set_genesis_block(const Block& b); bool deinit(); uint64_t get_current_blockchain_height(); - bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); + virtual bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); template @@ -67,11 +74,15 @@ namespace cryptonote { { return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); } + virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, + uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); crypto::hash get_block_id_by_height(uint64_t height); void get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); bool get_block_by_hash(const crypto::hash &h, Block &blk); //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + virtual bool getBlockByHash(const crypto::hash &h, Block &blk) override; + bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); @@ -84,13 +95,13 @@ namespace cryptonote { //bool get_outs(uint64_t amount, std::list& pkeys); bool have_block(const crypto::hash& id); bool get_short_chain_history(std::list& ids); - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); + virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool get_stat_info(core_stat_info& st_inf); //bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); - bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); + virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); crypto::hash get_tail_id(); - 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); + virtual 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); void pause_mining(); void update_block_template_and_resume_mining(); blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} @@ -100,6 +111,7 @@ namespace cryptonote { std::string print_pool(bool short_format); void print_blockchain_outs(const std::string& file); void on_synchronized(); + virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; private: bool add_new_tx(const 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); @@ -121,6 +133,9 @@ namespace cryptonote { bool handle_command_line(const boost::program_options::variables_map& vm); bool on_update_blocktemplate_interval(); bool check_tx_inputs_keyimages_diff(const Transaction& tx); + virtual void blockchainUpdated() override; + virtual void txDeletedFromPool() override; + void poolUpdated(); const Currency& m_currency; CryptoNote::RealTimeProvider m_timeProvider; @@ -133,6 +148,7 @@ namespace cryptonote { cryptonote_protocol_stub m_protocol_stub; friend class tx_validate_inputs; std::atomic m_starter_message_showed; + tools::ObserverManager m_observerManager; }; } diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 16bcdb7ef9..ac20cf8286 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -239,7 +239,57 @@ namespace cryptonote payment_id = *reinterpret_cast(extra_nonce.data() + 1); return true; } - //--------------------------------------------------------------- + + bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId) { + cryptonote::blobdata binData; + if (!epee::string_tools::parse_hexstr_to_binbuff(paymentIdString, binData)) { + return false; + } + + if (sizeof(crypto::hash) != binData.size()) { + return false; + } + + paymentId = *reinterpret_cast(binData.data()); + return true; + } + + + bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { + crypto::hash paymentIdBin; + + if (!parsePaymentId(paymentIdString, paymentIdBin)) { + return false; + } + + std::string extraNonce; + cryptonote::set_payment_id_to_tx_extra_nonce(extraNonce, paymentIdBin); + + if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extraNonce)) { + return false; + } + + return true; + } + + bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId) { + std::vector tx_extra_fields; + if(!parse_tx_extra(extra, tx_extra_fields)) { + return false; + } + + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { + if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentId)) { + return false; + } + } else { + return false; + } + + return true; + } + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, Transaction& tx, uint64_t unlock_time) { tx.vin.clear(); diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index af588bf85c..0296fb4cad 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -91,6 +91,10 @@ namespace cryptonote void get_blob_hash(const blobdata& blob, crypto::hash& res); crypto::hash get_blob_hash(const blobdata& blob); std::string short_hash_str(const crypto::hash& h); + bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); + //returns false if payment id is not found or parse error + bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId); + bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId); crypto::hash get_transaction_hash(const Transaction& t); bool get_transaction_hash(const Transaction& t, crypto::hash& res); diff --git a/src/cryptonote_core/cryptonote_serialization.cpp b/src/cryptonote_core/cryptonote_serialization.cpp new file mode 100644 index 0000000000..8a4b530e38 --- /dev/null +++ b/src/cryptonote_core/cryptonote_serialization.cpp @@ -0,0 +1,501 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "cryptonote_serialization.h" + +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" +#include "serialization/BinaryInputStreamSerializer.h" +#include "serialization/BinaryOutputStreamSerializer.h" +#include "crypto/crypto.h" +#include "cryptonote_config.h" + +#include +#include + +#include +#include + +namespace { + +struct BinaryVariantTagGetter: boost::static_visitor { + uint8_t operator()(const cryptonote::TransactionInputGenerate) { return 0xff; } + uint8_t operator()(const cryptonote::TransactionInputToScript) { return 0x0; } + uint8_t operator()(const cryptonote::TransactionInputToScriptHash) { return 0x1; } + uint8_t operator()(const cryptonote::TransactionInputToKey) { return 0x2; } + uint8_t operator()(const cryptonote::TransactionInputMultisignature) { return 0x3; } + uint8_t operator()(const cryptonote::TransactionOutputToScript) { return 0x0; } + uint8_t operator()(const cryptonote::TransactionOutputToScriptHash) { return 0x1; } + uint8_t operator()(const cryptonote::TransactionOutputToKey) { return 0x2; } + uint8_t operator()(const cryptonote::TransactionOutputMultisignature) { return 0x3; } + uint8_t operator()(const cryptonote::Transaction) { return 0xcc; } + uint8_t operator()(const cryptonote::Block) { return 0xbb; } +}; + +struct VariantSerializer : boost::static_visitor<> { + VariantSerializer(cryptonote::ISerializer& serializer, const std::string& name) : s(serializer), name(name) {} + + void operator() (cryptonote::TransactionInputGenerate& param) { s(param, name); } + void operator() (cryptonote::TransactionInputToScript& param) { s(param, name); } + void operator() (cryptonote::TransactionInputToScriptHash& param) { s(param, name); } + void operator() (cryptonote::TransactionInputToKey& param) { s(param, name); } + void operator() (cryptonote::TransactionInputMultisignature& param) { s(param, name); } + void operator() (cryptonote::TransactionOutputToScript& param) { s(param, name); } + void operator() (cryptonote::TransactionOutputToScriptHash& param) { s(param, name); } + void operator() (cryptonote::TransactionOutputToKey& param) { s(param, name); } + void operator() (cryptonote::TransactionOutputMultisignature& param) { s(param, name); } + + cryptonote::ISerializer& s; + const std::string& name; +}; + +void getVariantValue(cryptonote::ISerializer& serializer, uint8_t tag, cryptonote::TransactionInput& in) { + switch(tag) { + case 0xff: { + cryptonote::TransactionInputGenerate v; + serializer(v, "data"); + in = v; + break; + } + case 0x0: { + cryptonote::TransactionInputToScript v; + serializer(v, "data"); + in = v; + break; + } + case 0x1: { + cryptonote::TransactionInputToScriptHash v; + serializer(v, "data"); + in = v; + break; + } + case 0x2: { + cryptonote::TransactionInputToKey v; + serializer(v, "data"); + in = v; + break; + } + case 0x3: { + cryptonote::TransactionInputMultisignature v; + serializer(v, "data"); + in = v; + break; + } + default: + throw std::runtime_error("Unknown variant tag"); + } +} + +void getVariantValue(cryptonote::ISerializer& serializer, uint8_t tag, cryptonote::TransactionOutputTarget& out) { + switch(tag) { + case 0x0: { + cryptonote::TransactionOutputToScript v; + serializer(v, "data"); + out = v; + break; + } + case 0x1: { + cryptonote::TransactionOutputToScriptHash v; + serializer(v, "data"); + out = v; + break; + } + case 0x2: { + cryptonote::TransactionOutputToKey v; + serializer(v, "data"); + out = v; + break; + } + case 0x3: { + cryptonote::TransactionOutputMultisignature v; + serializer(v, "data"); + out = v; + break; + } + default: + throw std::runtime_error("Unknown variant tag"); + } +} + +template +void serializePod(T& v, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.binary(&v, sizeof(v), name); +} + +void serializeVarintVector(std::vector& vector, cryptonote::ISerializer& serializer, const std::string& name) { + std::size_t size = vector.size(); + serializer.beginArray(size, name); + vector.resize(size); + + for (size_t i = 0; i < size; ++i) { + serializer(vector[i], ""); + } + + serializer.endArray(); +} + +} + +namespace crypto { + +void serialize(public_key& pubKey, const std::string& name, cryptonote::ISerializer& serializer) { + serializePod(pubKey, name, serializer); +} + +void serialize(secret_key& secKey, const std::string& name, cryptonote::ISerializer& serializer) { + serializePod(secKey, name, serializer); +} + +void serialize(hash& h, const std::string& name, cryptonote::ISerializer& serializer) { + serializePod(h, name, serializer); +} + +void serialize(key_image& keyImage, const std::string& name, cryptonote::ISerializer& serializer) { + serializePod(keyImage, name, serializer); +} + +void serialize(chacha8_iv& chacha, const std::string& name, cryptonote::ISerializer& serializer) { + serializePod(chacha, name, serializer); +} + +} + +namespace cryptonote { + +void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + uint64_t version = static_cast(txP.version); + serializer(version, "version"); + txP.version = static_cast(version); + serializer(txP.unlockTime, "unlock_time"); + serializer(txP.vin, "vin"); + serializer(txP.vout, "vout"); + serializeAsBinary(txP.extra, "extra", serializer); + serializer.endObject(); +} + +void serialize(Transaction& tx, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + uint64_t version = static_cast(tx.version); + serializer(version, "version"); + tx.version = static_cast(version); + //TODO: make version. check version here + serializer(tx.unlockTime, "unlock_time"); + serializer(tx.vin, "vin"); + serializer(tx.vout, "vout"); + serializeAsBinary(tx.extra, "extra", serializer); + + std::size_t sigSize = tx.vin.size(); + //TODO: make arrays without sizes +// serializer.beginArray(sigSize, "signatures"); + tx.signatures.resize(sigSize); + + bool signaturesNotExpected = tx.signatures.empty(); + if (!signaturesNotExpected && tx.vin.size() != tx.signatures.size()) { + throw std::runtime_error("Serialization error: unexpected signatures size"); + } + + for (size_t i = 0; i < tx.vin.size(); ++i) { + size_t signatureSize = Transaction::getSignatureSize(tx.vin[i]); + if (signaturesNotExpected) { + if (signatureSize == 0) { + continue; + } else { + throw std::runtime_error("Serialization error: signatures are not expected"); + } + } + + if (serializer.type() == ISerializer::OUTPUT) { + if (signatureSize != tx.signatures[i].size()) { + throw std::runtime_error("Serialization error: unexpected signatures size"); + } + } else { + tx.signatures[i].resize(signatureSize); + } + + for (crypto::signature& sig: tx.signatures[i]) { + serializePod(sig, "", serializer); + } + } +// serializer.endArray(); + + serializer.endObject(); +} + +void serialize(TransactionInput& in, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + if (serializer.type() == ISerializer::OUTPUT) { + BinaryVariantTagGetter tagGetter; + uint8_t tag = boost::apply_visitor(tagGetter, in); + serializer.binary(&tag, sizeof(tag), "type"); + + VariantSerializer visitor(serializer, "value"); + boost::apply_visitor(visitor, in); + } else { + uint8_t tag; + serializer.binary(&tag, sizeof(tag), "type"); + + getVariantValue(serializer, tag, in); + } + + serializer.endObject(); +} + +void serialize(TransactionInputGenerate& gen, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + uint64_t height = static_cast(gen.height); + serializer(height, "height"); + gen.height = static_cast(height); + serializer.endObject(); +} + +void serialize(TransactionInputToScript& script, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer.endObject(); +} + +void serialize(TransactionInputToScriptHash& scripthash, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer.endObject(); +} + +void serialize(TransactionInputToKey& key, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer(key.amount, "amount"); + serializeVarintVector(key.keyOffsets, serializer, "key_offsets"); + serializer(key.keyImage, "k_image"); + serializer.endObject(); +} + +void serialize(TransactionInputMultisignature& multisignature, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer(multisignature.amount, "amount"); + serializer(multisignature.signatures, "signatures"); + serializer(multisignature.outputIndex, "outputIndex"); + serializer.endObject(); +} + +void serialize(TransactionOutput& output, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer(output.amount, "amount"); + serializer(output.target, "target"); + serializer.endObject(); +} + +void serialize(TransactionOutputTarget& output, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + if (serializer.type() == ISerializer::OUTPUT) { + BinaryVariantTagGetter tagGetter; + uint8_t tag = boost::apply_visitor(tagGetter, output); + serializer.binary(&tag, sizeof(tag), "type"); + + VariantSerializer visitor(serializer, "data"); + boost::apply_visitor(visitor, output); + } else { + uint8_t tag; + serializer.binary(&tag, sizeof(tag), "type"); + + getVariantValue(serializer, tag, output); + } + + serializer.endObject(); +} + +void serialize(TransactionOutputToScript& script, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer.endObject(); +} + +void serialize(TransactionOutputToScriptHash& scripthash, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer.endObject(); +} + +void serialize(TransactionOutputToKey& key, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer(key.key, "key"); + serializer.endObject(); +} + +void serialize(TransactionOutputMultisignature& multisignature, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializer(multisignature.keys, "keys"); + serializer(multisignature.requiredSignatures, "required_signatures"); + serializer.endObject(); +} + +void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + serializer(pbs.m_parentBlock.majorVersion, "majorVersion"); + + if (BLOCK_MAJOR_VERSION_1 < pbs.m_parentBlock.majorVersion) { + throw std::runtime_error("Wrong parent block major version"); + } + + serializer(pbs.m_parentBlock.minorVersion, "minorVersion"); + serializer(pbs.m_timestamp, "timestamp"); + serializer(pbs.m_parentBlock.prevId, "prevId"); + serializer.binary(&pbs.m_nonce, sizeof(pbs.m_nonce), "nonce"); + + if (pbs.m_hashingSerialization) { + crypto::hash minerTxHash; + if (!get_transaction_hash(pbs.m_parentBlock.minerTx, minerTxHash)) { + throw std::runtime_error("Get transaction hash error"); + } + + crypto::hash merkleRoot; + crypto::tree_hash_from_branch(pbs.m_parentBlock.minerTxBranch.data(), pbs.m_parentBlock.minerTxBranch.size(), minerTxHash, 0, merkleRoot); + + serializer(merkleRoot, "merkleRoot"); + } + + uint64_t txNum = static_cast(pbs.m_parentBlock.numberOfTransactions); + serializer(txNum, "numberOfTransactions"); + pbs.m_parentBlock.numberOfTransactions = static_cast(txNum); + if (pbs.m_parentBlock.numberOfTransactions < 1) { + throw std::runtime_error("Wrong transactions number"); + } + + if (pbs.m_headerOnly) { + return; + } + + size_t branchSize = crypto::tree_depth(pbs.m_parentBlock.numberOfTransactions); + if (serializer.type() == ISerializer::OUTPUT) { + if (pbs.m_parentBlock.minerTxBranch.size() != branchSize) { + throw std::runtime_error("Wrong miner transaction branch size"); + } + } else { + pbs.m_parentBlock.minerTxBranch.resize(branchSize); + } + +// serializer(m_parentBlock.minerTxBranch, "minerTxBranch"); + //TODO: Make arrays with computable size! This code won't work with json serialization! + for (crypto::hash& hash: pbs.m_parentBlock.minerTxBranch) { + serializer(hash, ""); + } + + serializer(pbs.m_parentBlock.minerTx, "minerTx"); + + tx_extra_merge_mining_tag mmTag; + if (!get_mm_tag_from_extra(pbs.m_parentBlock.minerTx.extra, mmTag)) { + throw std::runtime_error("Can't get extra merge mining tag"); + } + + if (mmTag.depth > 8 * sizeof(crypto::hash)) { + throw std::runtime_error("Wrong merge mining tag depth"); + } + + if (serializer.type() == ISerializer::OUTPUT) { + if (mmTag.depth != pbs.m_parentBlock.blockchainBranch.size()) { + throw std::runtime_error("Blockchain branch size must be equal to merge mining tag depth"); + } + } else { + pbs.m_parentBlock.blockchainBranch.resize(mmTag.depth); + } + +// serializer(m_parentBlock.blockchainBranch, "blockchainBranch"); + //TODO: Make arrays with computable size! This code won't work with json serialization! + for (crypto::hash& hash: pbs.m_parentBlock.blockchainBranch) { + serializer(hash, ""); + } + + serializer.endObject(); +} + +void serializeBlockHeader(BlockHeader& header, ISerializer& serializer) { + serializer(header.majorVersion, "major_version"); + if (header.majorVersion > BLOCK_MAJOR_VERSION_2) { + throw std::runtime_error("Wrong major version"); + } + + serializer(header.minorVersion, "minor_version"); + if (header.majorVersion == BLOCK_MAJOR_VERSION_1) { + serializer(header.timestamp, "timestamp"); + serializer(header.prevId, "prev_id"); + serializer.binary(&header.nonce, sizeof(header.nonce), "nonce"); + } else if (header.majorVersion == BLOCK_MAJOR_VERSION_2) { + serializer(header.prevId, "prev_id"); + } else { + throw std::runtime_error("Wrong major version"); + } +} + +void serialize(BlockHeader& header, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + serializeBlockHeader(header, serializer); + serializer.endObject(); +} + +void serialize(Block& block, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + serializeBlockHeader(block, serializer); + + if (block.majorVersion == BLOCK_MAJOR_VERSION_2) { + auto parentBlockSerializer = makeParentBlockSerializer(block, false, false); + serializer(parentBlockSerializer, "parent_block"); + } + + serializer(block.minerTx, "miner_tx"); + serializer(block.txHashes, "tx_hashes"); + + serializer.endObject(); +} + +void serialize(AccountPublicAddress& address, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + serializer(address.m_spendPublicKey, "spend_public_key"); + serializer(address.m_viewPublicKey, "view_public_key"); + + serializer.endObject(); +} + +void doSerialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer) { + uint64_t depth = static_cast(tag.depth); + serializer(depth, "depth"); + tag.depth = static_cast(depth); + serializer(tag.merkle_root, "merkle_root"); +} + +void serialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer) { + serializer.beginObject(name); + + if (serializer.type() == ISerializer::OUTPUT) { + std::stringstream stream; + BinaryOutputStreamSerializer output(stream); + doSerialize(tag, "", output); + std::string field = stream.str(); + serializer(field, ""); + } else { + std::string field; + serializer(field, ""); + + std::stringstream stream(field); + BinaryInputStreamSerializer input(stream); + doSerialize(tag, "", input); + } + + serializer.endObject(); +} + +} //namespace cryptonote diff --git a/src/cryptonote_core/cryptonote_serialization.h b/src/cryptonote_core/cryptonote_serialization.h new file mode 100644 index 0000000000..16e569273d --- /dev/null +++ b/src/cryptonote_core/cryptonote_serialization.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_basic.h" + +namespace cryptonote { +class ISerializer; +} + +namespace crypto { + +void serialize(public_key& pubKey, const std::string& name, cryptonote::ISerializer& enumerator); +void serialize(secret_key& secKey, const std::string& name, cryptonote::ISerializer& enumerator); +void serialize(hash& h, const std::string& name, cryptonote::ISerializer& enumerator); +void serialize(chacha8_iv& chacha, const std::string& name, cryptonote::ISerializer& enumerator); +void serialize(key_image& keyImage, const std::string& name, cryptonote::ISerializer& enumerator); + +} //namespace crypto + +namespace cryptonote { +void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& serializer); +void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& serializer); +void serialize(Transaction& tx, const std::string& name, ISerializer& serializer); +void serialize(TransactionInput& in, const std::string& name, ISerializer& serializer); +void serialize(TransactionOutput& in, const std::string& name, ISerializer& serializer); + +void serialize(TransactionInputGenerate& gen, const std::string& name, ISerializer& serializer); +void serialize(TransactionInputToScript& script, const std::string& name, ISerializer& serializer); +void serialize(TransactionInputToScriptHash& scripthash, const std::string& name, ISerializer& serializer); +void serialize(TransactionInputToKey& key, const std::string& name, ISerializer& serializer); +void serialize(TransactionInputMultisignature& multisignature, const std::string& name, ISerializer& serializer); + +void serialize(TransactionOutput& output, const std::string& name, ISerializer& serializer); + +void serialize(TransactionOutputTarget& output, const std::string& name, ISerializer& serializer); + +void serialize(TransactionOutputToScript& script, const std::string& name, ISerializer& serializer); +void serialize(TransactionOutputToScriptHash& scripthash, const std::string& name, ISerializer& serializer); +void serialize(TransactionOutputToKey& key, const std::string& name, ISerializer& serializer); +void serialize(TransactionOutputMultisignature& multisignature, const std::string& name, ISerializer& serializer); +void serialize(BlockHeader& header, const std::string& name, ISerializer& serializer); +void serialize(Block& block, const std::string& name, ISerializer& serializer); +void serialize(AccountPublicAddress& address, const std::string& name, ISerializer& serializer); +void serialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer); + +} //namespace cryptonote diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index fc64a01acf..71a142a207 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -44,14 +44,6 @@ using namespace epee; namespace cryptonote { - namespace - { - const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; - const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; - const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; - } - - miner::miner(const Currency& currency, i_miner_handler* phandler): m_currency(currency), m_stop(1), @@ -164,26 +156,16 @@ namespace cryptonote m_last_hr_merge_time = misc_utils::get_tick_count(); m_hashes = 0; } - //----------------------------------------------------------------------------------------------------- - void miner::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_extra_messages); - command_line::add_arg(desc, arg_start_mining); - command_line::add_arg(desc, arg_mining_threads); - } - //----------------------------------------------------------------------------------------------------- - bool miner::init(const boost::program_options::variables_map& vm) - { - if(command_line::has_arg(vm, arg_extra_messages)) - { + + bool miner::init(const MinerConfig& config) { + if (!config.extraMessages.empty()) { std::string buff; - bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); - CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); + bool r = file_io_utils::load_file_to_string(config.extraMessages, buff); + CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << config.extraMessages); std::vector extra_vec; boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); m_extra_messages.resize(extra_vec.size()); - for(size_t i = 0; i != extra_vec.size(); i++) - { + for(size_t i = 0; i != extra_vec.size(); i++) { string_tools::trim(extra_vec[i]); if(!extra_vec[i].size()) continue; @@ -191,23 +173,21 @@ namespace cryptonote if(buff != "0") m_extra_messages[i] = buff; } - m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); + m_config_folder_path = boost::filesystem::path(config.extraMessages).parent_path().string(); m_config = AUTO_VAL_INIT(m_config); epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + cryptonote::parameters::MINER_CONFIG_FILE_NAME); LOG_PRINT_L0("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); } - if(command_line::has_arg(vm, arg_start_mining)) - { - if (!m_currency.parseAccountAddressString(command_line::get_arg(vm, arg_start_mining), m_mine_address)) { - LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); + if(!config.startMining.empty()) { + if (!m_currency.parseAccountAddressString(config.startMining, m_mine_address)) { + LOG_ERROR("Target account address " << config.startMining << " has wrong format, starting daemon canceled"); return false; } m_threads_total = 1; m_do_mining = true; - if(command_line::has_arg(vm, arg_mining_threads)) - { - m_threads_total = command_line::get_arg(vm, arg_mining_threads); + if(config.miningThreads > 0) { + m_threads_total = config.miningThreads; } } diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index 1f492761a5..f5de9e701e 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -29,14 +29,15 @@ #include "cryptonote_core/Currency.h" #include "cryptonote_core/difficulty.h" #include "cryptonote_core/i_miner_handler.h" +#include "cryptonote_core/MinerConfig.h" namespace cryptonote { class miner { public: miner(const Currency& currency, i_miner_handler* phandler); ~miner(); - bool init(const boost::program_options::variables_map& vm); - static void init_options(boost::program_options::options_description& desc); + + bool init(const MinerConfig& config); bool set_block_template(const Block& bl, const difficulty_type& diffic); bool on_block_chain_update(); bool start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 27beaa38d4..0f5a3897f0 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -235,6 +235,32 @@ namespace cryptonote { } } //--------------------------------------------------------------------------------- + void tx_memory_pool::get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const { + CRITICAL_REGION_LOCAL(m_transactions_lock); + std::unordered_set ready_tx_ids; + for (const auto& tx : m_transactions) { + TransactionCheckInfo checkInfo(tx); + if (is_transaction_ready_to_go(tx.tx, checkInfo)) { + ready_tx_ids.insert(tx.id); + } + } + + std::unordered_set known_set(known_tx_ids.begin(), known_tx_ids.end()); + for (auto it = ready_tx_ids.begin(), e = ready_tx_ids.end(); it != e;) { + auto known_it = known_set.find(*it); + if (known_it != known_set.end()) { + known_set.erase(known_it); + it = ready_tx_ids.erase(it); + } + else { + ++it; + } + } + + new_tx_ids.assign(ready_tx_ids.begin(), ready_tx_ids.end()); + deleted_tx_ids.assign(known_set.begin(), known_set.end()); + } + //--------------------------------------------------------------------------------- bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) { return true; } @@ -287,7 +313,7 @@ namespace cryptonote { << "max_used_block_id: " << txd.maxUsedBlock.id << std::endl << "last_failed_height: " << txd.lastFailedBlock.height << std::endl << "last_failed_id: " << txd.lastFailedBlock.id << std::endl - << "recieved: " << std::ctime(&txd.receiveTime) << std::endl; + << "received: " << std::ctime(&txd.receiveTime) << std::endl; } return ss.str(); @@ -372,21 +398,30 @@ namespace cryptonote { //--------------------------------------------------------------------------------- bool tx_memory_pool::removeExpiredTransactions() { - CRITICAL_REGION_LOCAL(m_transactions_lock); - - auto now = m_timeProvider.now(); + bool somethingRemoved = false; + { + CRITICAL_REGION_LOCAL(m_transactions_lock); - for (auto it = m_transactions.begin(); it != m_transactions.end();) { - uint64_t txAge = now - it->receiveTime; - bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); + auto now = m_timeProvider.now(); - if (remove) { - LOG_PRINT_L2("Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge); - it = removeTransaction(it); - } else { - ++it; + for (auto it = m_transactions.begin(); it != m_transactions.end();) { + uint64_t txAge = now - it->receiveTime; + bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); + + if (remove) { + LOG_PRINT_L2("Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge); + it = removeTransaction(it); + somethingRemoved = true; + } else { + ++it; + } } } + + if (somethingRemoved) { + m_observerManager.notify(&ITxPoolObserver::txDeletedFromPool); + } + return true; } @@ -469,4 +504,12 @@ namespace cryptonote { } return false; } + + bool tx_memory_pool::addObserver(ITxPoolObserver* observer) { + return m_observerManager.add(observer); + } + + bool tx_memory_pool::removeObserver(ITxPoolObserver* observer) { + return m_observerManager.remove(observer); + } } diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 91db2ca7ea..78a22f92ca 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -38,11 +38,13 @@ #include "common/util.h" #include "common/int-util.h" +#include "common/ObserverManager.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/Currency.h" #include "cryptonote_core/ITimeProvider.h" #include "cryptonote_core/ITransactionValidator.h" +#include "cryptonote_core/ITxPoolObserver.h" #include "cryptonote_core/verification_context.h" @@ -86,6 +88,9 @@ namespace cryptonote { tx_memory_pool(const cryptonote::Currency& currency, CryptoNote::ITransactionValidator& validator, CryptoNote::ITimeProvider& timeProvider); + bool addObserver(ITxPoolObserver* observer); + bool removeObserver(ITxPoolObserver* observer); + // load/store operations bool init(const std::string& config_folder); bool deinit(); @@ -105,6 +110,7 @@ namespace cryptonote { bool fill_block_template(Block &bl, size_t median_size, size_t maxCumulativeSize, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); void get_transactions(std::list& txs) const; + void get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const; size_t get_transactions_count() const; std::string print_pool(bool short_format) const; void on_idle(); @@ -195,6 +201,8 @@ namespace cryptonote { bool removeExpiredTransactions(); bool is_transaction_ready_to_go(const Transaction& tx, TransactionCheckInfo& txd) const; + tools::ObserverManager m_observerManager; + const cryptonote::Currency& m_currency; OnceInTimeInterval m_txCheckInterval; mutable epee::critical_section m_transactions_lock; diff --git a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h b/src/cryptonote_protocol/ICryptonoteProtocolObserver.h new file mode 100644 index 0000000000..a573773cb4 --- /dev/null +++ b/src/cryptonote_protocol/ICryptonoteProtocolObserver.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace cryptonote { + +class ICryptonoteProtocolObserver { +public: + virtual void peerCountUpdated(size_t count) {} + virtual void lastKnownBlockHeightUpdated(uint64_t height) {} +}; + +} //namespace cryptonote diff --git a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h b/src/cryptonote_protocol/ICryptonoteProtocolQuery.h new file mode 100644 index 0000000000..9d853c9bc5 --- /dev/null +++ b/src/cryptonote_protocol/ICryptonoteProtocolQuery.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace cryptonote { +class ICryptonoteProtocolObserver; + +class ICryptonoteProtocolQuery { +public: + virtual bool addObserver(ICryptonoteProtocolObserver* observer) = 0; + virtual bool removeObserver(ICryptonoteProtocolObserver* observer) = 0; + + virtual uint64_t getObservedHeight() const = 0; + virtual size_t getPeerCount() const = 0; +}; + +} //namespace cryptonote diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 7e28e89daa..b52e7d4ffc 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -41,6 +41,16 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; + struct BlockFullInfo : public block_complete_entry + { + crypto::hash block_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB(block_id) + KV_SERIALIZE(block) + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + }; /************************************************************************/ /* */ diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index d7b467fe9f..fc5e75ec1d 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -17,25 +17,31 @@ #pragma once +#include + #include +#include +// epee #include "storages/levin_abstract_invoke2.h" #include "warnings.h" -#include "cryptonote_protocol_defs.h" -#include "cryptonote_protocol_handler_common.h" + #include "cryptonote_core/connection_context.h" #include "cryptonote_core/cryptonote_stat_info.h" #include "cryptonote_core/verification_context.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" +#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) -namespace cryptonote -{ +namespace cryptonote { template - class t_cryptonote_protocol_handler: public i_cryptonote_protocol - { + class t_cryptonote_protocol_handler : public i_cryptonote_protocol, public ICryptonoteProtocolQuery + { public: typedef cryptonote_connection_context connection_context; typedef core_stat_info stat_info; @@ -53,19 +59,29 @@ namespace cryptonote HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) END_INVOKE_MAP2() - bool on_idle(); - bool init(const boost::program_options::variables_map& vm); + bool init(); bool deinit(); + + virtual bool addObserver(ICryptonoteProtocolObserver* observer); + virtual bool removeObserver(ICryptonoteProtocolObserver* observer); + void set_p2p_endpoint(nodetool::i_p2p_endpoint* p2p); - //bool process_handshake_data(const blobdata& data, cryptonote_connection_context& context); - bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); - bool get_payload_sync_data(blobdata& data); - bool get_payload_sync_data(CORE_SYNC_DATA& hshd); - bool get_stat_info(core_stat_info& stat_inf); - bool on_callback(cryptonote_connection_context& context); - t_core& get_core(){return m_core;} - bool is_synchronized(){return m_synchronized;} + t_core& get_core() { return m_core; } + bool is_synchronized() const { return m_synchronized; } void log_connections(); + + // Interface t_payload_net_handler, where t_payload_net_handler is template argument of nodetool::node_server + void stop(); + bool on_callback(cryptonote_connection_context& context); + bool on_idle(); + void onConnectionOpened(cryptonote_connection_context& context); + void onConnectionClosed(cryptonote_connection_context& context); + bool get_stat_info(core_stat_info& stat_inf); + bool get_payload_sync_data(CORE_SYNC_DATA& hshd); + bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); + virtual size_t getPeerCount() const; + virtual uint64_t getObservedHeight() const; + private: //----------------- commands handlers ---------------------------------------------- int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context); @@ -75,43 +91,51 @@ namespace cryptonote int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); - - //----------------- i_bc_protocol_layout --------------------------------------- - virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context); + //----------------- i_cryptonote_protocol ---------------------------------- + virtual void relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) override; + virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) override; //---------------------------------------------------------------------------------- - //bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context); + bool request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks); size_t get_synchronizing_connections_count(); bool on_connection_synchronized(); + void updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context); + void recalculateMaxObservedHeight(const cryptonote_connection_context& context); + + template + bool post_notify(typename t_parametr::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); + std::string blob; + epee::serialization::store_t_to_binary(arg, blob); + return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); + } + + template + void relay_post_notify(typename t_parametr::request& arg, cryptonote_connection_context& exlude_context) + { + LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exlude_context) << "] post relay " << typeid(t_parametr).name() << " -->"); + std::string arg_buff; + epee::serialization::store_t_to_binary(arg, arg_buff); + m_p2p->relay_notify_to_all(t_parametr::ID, arg_buff, exlude_context); + } + + private: t_core& m_core; nodetool::p2p_endpoint_stub m_p2p_stub; nodetool::i_p2p_endpoint* m_p2p; - std::atomic m_syncronized_connections_count; std::atomic m_synchronized; + std::atomic m_stop; - template - bool post_notify(typename t_parametr::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); - std::string blob; - epee::serialization::store_t_to_binary(arg, blob); - return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); - } - - template - bool relay_post_notify(typename t_parametr::request& arg, cryptonote_connection_context& exlude_context) - { - LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exlude_context) << "] post relay " << typeid(t_parametr).name() << " -->"); - std::string arg_buff; - epee::serialization::store_t_to_binary(arg, arg_buff); - return m_p2p->relay_notify_to_all(t_parametr::ID, arg_buff, exlude_context); - } + mutable std::mutex m_observedHeightMutex; + uint64_t m_observedHeight; + + std::atomic m_peersCount; + tools::ObserverManager m_observerManager; }; } - #include "cryptonote_protocol_handler.inl" POP_WARNINGS diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 47427ac9b1..4326f03279 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -15,37 +15,42 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include -#include "cryptonote_core/cryptonote_format_utils.h" +// epee #include "profile_tools.h" + +#include "cryptonote_core/cryptonote_format_utils.h" + namespace cryptonote { - //----------------------------------------------------------------------------------------------------------------------- template - t_cryptonote_protocol_handler::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint* p_net_layout):m_core(rcore), - m_p2p(p_net_layout), - m_syncronized_connections_count(0), - m_synchronized(false) - - { - if(!m_p2p) + t_cryptonote_protocol_handler::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint* p_net_layout) : + m_core(rcore), + m_p2p(p_net_layout), + m_synchronized(false), + m_stop(false), + m_observedHeight(0) { + if (!m_p2p) { m_p2p = &m_p2p_stub; + } } - //----------------------------------------------------------------------------------------------------------------------- - template - bool t_cryptonote_protocol_handler::init(const boost::program_options::variables_map& vm) - { + + template + bool t_cryptonote_protocol_handler::init() { + m_peersCount = 0; return true; } - //------------------------------------------------------------------------------------------------------------------------ + //------------------------------------------------------------------------------------------------------------------------ template bool t_cryptonote_protocol_handler::deinit() { - - return true; } + + template + size_t t_cryptonote_protocol_handler::getPeerCount() const { + return m_peersCount; + } //------------------------------------------------------------------------------------------------------------------------ template void t_cryptonote_protocol_handler::set_p2p_endpoint(nodetool::i_p2p_endpoint* p2p) @@ -56,7 +61,41 @@ namespace cryptonote m_p2p = &m_p2p_stub; } //------------------------------------------------------------------------------------------------------------------------ - template + template + void t_cryptonote_protocol_handler::onConnectionOpened(cryptonote_connection_context& context) { + } + //------------------------------------------------------------------------------------------------------------------------ + template + void t_cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_context& context) { + bool updated = false; + { + std::lock_guard lock(m_observedHeightMutex); + uint64_t prevHeight = m_observedHeight; + recalculateMaxObservedHeight(context); + if (prevHeight != m_observedHeight) { + updated = true; + } + } + + if (updated) { + LOG_PRINT_L2("Observed height updated: " << m_observedHeight); + m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + } + + if (context.m_state != cryptonote_connection_context::state_befor_handshake) { + m_peersCount--; + m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + } + } + + //------------------------------------------------------------------------------------------------------------------------ + template + void t_cryptonote_protocol_handler::stop() { + m_stop = true; + } + + //------------------------------------------------------------------------------------------------------------------------ + template bool t_cryptonote_protocol_handler::on_callback(cryptonote_connection_context& context) { LOG_PRINT_CCONTEXT_L2("callback fired"); @@ -110,29 +149,34 @@ namespace cryptonote if(context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) return true; - if(context.m_state == cryptonote_connection_context::state_synchronizing) - return true; - - if(m_core.have_block(hshd.top_id)) - { + if(context.m_state == cryptonote_connection_context::state_synchronizing) { + } else if(m_core.have_block(hshd.top_id)) { context.m_state = cryptonote_connection_context::state_normal; if(is_inital) on_connection_synchronized(); - return true; + } else { + int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); + LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height + << " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " + << (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl << + "SYNCHRONIZATION started", (diff >= 0 ? (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1) : LOG_LEVEL_2)); + LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); + context.m_state = cryptonote_connection_context::state_synchronizing; + + //let the socket to send response to handshake, but request callback, to let send request data after response + LOG_PRINT_CCONTEXT_L2("requesting callback"); + ++context.m_callback_request_count; + m_p2p->request_callback(context); } - int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); - LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height - << " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " - << (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl << - "SYNCHRONIZATION started", (diff >= 0 ? (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1) : LOG_LEVEL_2)); - LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); - context.m_state = cryptonote_connection_context::state_synchronizing; + updateObservedHeight(hshd.current_height, context); context.m_remote_blockchain_height = hshd.current_height; - //let the socket to send response to handshake, but request callback, to let send request data after response - LOG_PRINT_CCONTEXT_L2("requesting callback"); - ++context.m_callback_request_count; - m_p2p->request_callback(context); + + if (is_inital) { + m_peersCount++; + m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + } + return true; } //------------------------------------------------------------------------------------------------------------------------ @@ -143,19 +187,15 @@ namespace cryptonote hshd.current_height +=1; return true; } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::get_payload_sync_data(blobdata& data) - { - CORE_SYNC_DATA hsd = boost::value_initialized(); - get_payload_sync_data(hsd); - epee::serialization::store_t_to_binary(hsd, data); - return true; - } //------------------------------------------------------------------------------------------------------------------------ template int t_cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"); + + updateObservedHeight(arg.current_blockchain_height, context); + + context.m_remote_blockchain_height = arg.current_blockchain_height; + if (context.m_state != cryptonote_connection_context::state_normal) { return 1; } @@ -252,10 +292,12 @@ namespace cryptonote return 1; } + updateObservedHeight(arg.current_blockchain_height, context); + context.m_remote_blockchain_height = arg.current_blockchain_height; size_t count = 0; - BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) + for (const block_complete_entry& block_entry : arg.blocks) { ++count; Block b; @@ -309,9 +351,13 @@ namespace cryptonote { m_core.pause_mining(); epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( - boost::bind(&t_core::update_block_template_and_resume_mining, &m_core)); + std::bind(&t_core::update_block_template_and_resume_mining, &m_core)); for (const block_complete_entry& block_entry : arg.blocks) { + if (m_stop) { + break; + } + //process transactions TIME_MEASURE_START(transactions_process_time); for (auto& tx_blob : block_entry.txs) { @@ -347,7 +393,10 @@ namespace cryptonote } } - request_missing_objects(context, true); + if (!m_stop) { + request_missing_objects(context, true); + } + return 1; } //------------------------------------------------------------------------------------------------------------------------ @@ -481,8 +530,7 @@ namespace cryptonote m_p2p->drop_connection(context); } - BOOST_FOREACH(auto& bl_id, arg.m_block_ids) - { + for (auto& bl_id : arg.m_block_ids) { if(!m_core.have_block(bl_id)) context.m_needed_objects.push_back(bl_id); } @@ -492,14 +540,74 @@ namespace cryptonote } //------------------------------------------------------------------------------------------------------------------------ template - bool t_cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) + void t_cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) { - return relay_post_notify(arg, exclude_context); + relay_post_notify(arg, exclude_context); } //------------------------------------------------------------------------------------------------------------------------ template - bool t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + void t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) { - return relay_post_notify(arg, exclude_context); + relay_post_notify(arg, exclude_context); + } + //------------------------------------------------------------------------------------------------------------------------ + template + void t_cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context) { + bool updated = false; + { + std::lock_guard lock(m_observedHeightMutex); + + uint64_t height = m_observedHeight; + if (peerHeight > context.m_remote_blockchain_height) { + m_observedHeight = std::max(m_observedHeight, peerHeight); + if (m_observedHeight != height) { + updated = true; + } + } else if (context.m_remote_blockchain_height == m_observedHeight) { + //the client switched to alternative chain and had maximum observed height. need to recalculate max height + recalculateMaxObservedHeight(context); + if (m_observedHeight != height) { + updated = true; + } + } + } + + if (updated) { + LOG_PRINT_L2("Observed height updated: " << m_observedHeight); + m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + } + } + + template + void t_cryptonote_protocol_handler::recalculateMaxObservedHeight(const cryptonote_connection_context& context) { + //should be locked outside + uint64_t peerHeight = 0; + m_p2p->for_each_connection([&peerHeight, &context](const cryptonote_connection_context& ctx, nodetool::peerid_type peerId) { + if (ctx.m_connection_id != context.m_connection_id) { + peerHeight = std::max(peerHeight, ctx.m_remote_blockchain_height); + } + return true; + }); + + uint64_t localHeight = 0; + crypto::hash ignore; + m_core.get_blockchain_top(localHeight, ignore); + m_observedHeight = std::max(peerHeight, localHeight); + } + + template + uint64_t t_cryptonote_protocol_handler::getObservedHeight() const { + std::lock_guard lock(m_observedHeightMutex); + return m_observedHeight; + }; + + template + bool t_cryptonote_protocol_handler::addObserver(ICryptonoteProtocolObserver* observer) { + return m_observerManager.add(observer); + } + + template + bool t_cryptonote_protocol_handler::removeObserver(ICryptonoteProtocolObserver* observer) { + return m_observerManager.remove(observer); } -} +}; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index f85d512f04..7e24444ea6 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -29,8 +29,8 @@ namespace cryptonote /* */ /************************************************************************/ struct i_cryptonote_protocol { - virtual bool relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context)=0; - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context)=0; + virtual void relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context)=0; + virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context)=0; //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; }; @@ -38,12 +38,10 @@ namespace cryptonote /* */ /************************************************************************/ struct cryptonote_protocol_stub: public i_cryptonote_protocol { - virtual bool relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context) { - return false; + virtual void relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context) override { } - virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context) { - return false; + virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context) override { } }; } diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 3881b64cf8..52f765db82 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -32,10 +32,13 @@ using namespace epee; #include "common/SignalHandler.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/CoreConfig.h" #include "cryptonote_core/Currency.h" +#include "cryptonote_core/MinerConfig.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "daemon/daemon_commands_handler.h" #include "p2p/net_node.h" +#include "p2p/NetNodeConfig.h" #include "rpc/core_rpc_server.h" #include "version.h" @@ -86,10 +89,11 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_console); command_line::add_arg(desc_cmd_sett, arg_testnet_on); - cryptonote::core::init_options(desc_cmd_sett); cryptonote::core_rpc_server::init_options(desc_cmd_sett); - nodetool::node_server >::init_options(desc_cmd_sett); - cryptonote::miner::init_options(desc_cmd_sett); + + cryptonote::CoreConfig::initOptions(desc_cmd_sett); + nodetool::NetNodeConfig::initOptions(desc_cmd_sett); + cryptonote::MinerConfig::initOptions(desc_cmd_sett); po::options_description desc_options("Allowed options"); desc_options.add(desc_cmd_only).add(desc_cmd_sett); @@ -165,6 +169,13 @@ int main(int argc, char* argv[]) ccore.set_checkpoints(std::move(checkpoints)); } + cryptonote::CoreConfig coreConfig; + coreConfig.init(vm); + nodetool::NetNodeConfig netNodeConfig; + netNodeConfig.init(vm); + cryptonote::MinerConfig minerConfig; + minerConfig.init(vm); + cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); nodetool::node_server > p2psrv(cprotocol); cryptonote::core_rpc_server rpc_server(ccore, p2psrv); @@ -174,12 +185,12 @@ int main(int argc, char* argv[]) //initialize objects LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(vm, testnet_mode); + bool res = p2psrv.init(netNodeConfig, testnet_mode); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); LOG_PRINT_L0("Initializing cryptonote protocol..."); - res = cprotocol.init(vm); + res = cprotocol.init(); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol."); LOG_PRINT_L0("Cryptonote protocol initialized OK"); @@ -190,7 +201,7 @@ int main(int argc, char* argv[]) //initialize core here LOG_PRINT_L0("Initializing core..."); - res = ccore.init(vm, true); + res = ccore.init(coreConfig, minerConfig, true); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); LOG_PRINT_L0("Core initialized OK"); diff --git a/src/inprocess_node/InProcessNode.cpp b/src/inprocess_node/InProcessNode.cpp new file mode 100644 index 0000000000..a92257c0fd --- /dev/null +++ b/src/inprocess_node/InProcessNode.cpp @@ -0,0 +1,498 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "InProcessNode.h" + +#include +#include + +#include "cryptonote_core/connection_context.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/verification_context.h" +#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "InProcessNodeErrors.h" + +namespace CryptoNote { + +InProcessNode::InProcessNode(cryptonote::ICore& core, cryptonote::ICryptonoteProtocolQuery& protocol) : + state(NOT_INITIALIZED), + core(core), + protocol(protocol) +{ +} + +InProcessNode::~InProcessNode() { + shutdown(); +} + +bool InProcessNode::addObserver(INodeObserver* observer) { + return observerManager.add(observer); +} + +bool InProcessNode::removeObserver(INodeObserver* observer) { + return observerManager.remove(observer); +} + +void InProcessNode::init(const Callback& callback) { + std::unique_lock lock(mutex); + std::error_code ec; + + if (state != NOT_INITIALIZED) { + ec = make_error_code(cryptonote::error::ALREADY_INITIALIZED); + } else { + protocol.addObserver(this); + core.addObserver(this); + + work.reset(new boost::asio::io_service::work(ioService)); + workerThread.reset(new std::thread(&InProcessNode::workerFunc, this)); + + state = INITIALIZED; + } + + ioService.post(std::bind(callback, ec)); +} + +bool InProcessNode::shutdown() { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + return false; + } + + protocol.removeObserver(this); + core.removeObserver(this); + state = NOT_INITIALIZED; + + work.reset(); + ioService.stop(); + workerThread->join(); + ioService.reset(); + return true; +} + +void InProcessNode::workerFunc() { + ioService.run(); +} + +void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, + uint64_t& startHeight, const Callback& callback) +{ + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind(&InProcessNode::getNewBlocksAsync, + this, + std::move(knownBlockIds), + std::ref(newBlocks), + std::ref(startHeight), + callback + ) + ); +} + +void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, + uint64_t& startHeight, const Callback& callback) +{ + std::error_code ec; + { + std::unique_lock lock(mutex); + ec = doGetNewBlocks(std::move(knownBlockIds), newBlocks, startHeight); + } + + callback(ec); +} + +//it's always protected with mutex +std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight) { + if (state != INITIALIZED) { + return make_error_code(cryptonote::error::NOT_INITIALIZED); + } + + try { + uint64_t totalHeight; + std::list > > bs; + if (!core.find_blockchain_supplement(knownBlockIds, bs, totalHeight, startHeight, 1000)) { + return make_error_code(cryptonote::error::REQUEST_ERROR); + } + + for (auto& b : bs) { + cryptonote::block_complete_entry be; + be.block = cryptonote::block_to_blob(b.first); + + for (auto& t : b.second) { + be.txs.push_back(cryptonote::tx_to_blob(t)); + } + + newBlocks.push_back(std::move(be)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + } + + return std::error_code(); +} + +void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, + const Callback& callback) +{ + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind(&InProcessNode::getTransactionOutsGlobalIndicesAsync, + this, + std::cref(transactionHash), + std::ref(outsGlobalIndices), + callback + ) + ); +} + +void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, + const Callback& callback) +{ + std::error_code ec; + { + std::unique_lock lock(mutex); + ec = doGetTransactionOutsGlobalIndices(transactionHash, outsGlobalIndices); + } + + callback(ec); +} + +//it's always protected with mutex +std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { + if (state != INITIALIZED) { + return make_error_code(cryptonote::error::NOT_INITIALIZED); + } + + try { + bool r = core.get_tx_outputs_gindexs(transactionHash, outsGlobalIndices); + if(!r) { + return make_error_code(cryptonote::error::REQUEST_ERROR); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + } + + return std::error_code(); +} + +void InProcessNode::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, + std::vector& result, const Callback& callback) +{ + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind(&InProcessNode::getRandomOutsByAmountsAsync, + this, + std::move(amounts), + outsCount, + std::ref(result), + callback + ) + ); +} + +void InProcessNode::getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, + std::vector& result, const Callback& callback) +{ + std::error_code ec; + { + std::unique_lock lock(mutex); + ec = doGetRandomOutsByAmounts(std::move(amounts), outsCount, result); + } + + callback(ec); +} + +//it's always protected with mutex +std::error_code InProcessNode::doGetRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result) { + if (state != INITIALIZED) { + return make_error_code(cryptonote::error::NOT_INITIALIZED); + } + + try { + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res; + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req; + req.amounts = amounts; + req.outs_count = outsCount; + + if(!core.get_random_outs_for_amounts(req, res)) { + return make_error_code(cryptonote::error::REQUEST_ERROR); + } + + result = std::move(res.outs); + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + } + + return std::error_code(); +} + + +void InProcessNode::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) +{ + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind(&InProcessNode::relayTransactionAsync, + this, + transaction, + callback + ) + ); +} + +void InProcessNode::relayTransactionAsync(const cryptonote::Transaction& transaction, const Callback& callback) { + std::error_code ec; + { + std::unique_lock lock(mutex); + ec = doRelayTransaction(transaction); + } + + callback(ec); +} + +//it's always protected with mutex +std::error_code InProcessNode::doRelayTransaction(const cryptonote::Transaction& transaction) { + if (state != INITIALIZED) { + return make_error_code(cryptonote::error::NOT_INITIALIZED); + } + + try { + cryptonote::blobdata txBlob = cryptonote::tx_to_blob(transaction); + cryptonote::tx_verification_context tvc = boost::value_initialized(); + + if(!core.handle_incoming_tx(txBlob, tvc, false)) { + return make_error_code(cryptonote::error::REQUEST_ERROR); + } + + if(tvc.m_verifivation_failed) { + return make_error_code(cryptonote::error::REQUEST_ERROR); + } + + if(!tvc.m_should_be_relayed) { + return make_error_code(cryptonote::error::REQUEST_ERROR); + } + + cryptonote::cryptonote_connection_context fake_context = boost::value_initialized(); + cryptonote::NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(txBlob); + core.get_protocol()->relay_transactions(r, fake_context); + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + } + + return std::error_code(); +} + +size_t InProcessNode::getPeerCount() const { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + return protocol.getPeerCount(); +} + +uint64_t InProcessNode::getLastLocalBlockHeight() const { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + uint64_t height; + crypto::hash ignore; + + if (!core.get_blockchain_top(height, ignore)) { + throw std::system_error(make_error_code(cryptonote::error::INTERNAL_NODE_ERROR)); + } + + return height; +} + +uint64_t InProcessNode::getLastKnownBlockHeight() const { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + return protocol.getObservedHeight(); +} + +uint64_t InProcessNode::getLastLocalBlockTimestamp() const { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + lock.unlock(); + + uint64_t ignore; + crypto::hash hash; + + if (!core.get_blockchain_top(ignore, hash)) { + throw std::system_error(make_error_code(cryptonote::error::INTERNAL_NODE_ERROR)); + } + + cryptonote::Block block; + if (!core.getBlockByHash(hash, block)) { + throw std::system_error(make_error_code(cryptonote::error::INTERNAL_NODE_ERROR)); + } + + return block.timestamp; +} + +void InProcessNode::peerCountUpdated(size_t count) { + observerManager.notify(&INodeObserver::peerCountUpdated, count); +} + +void InProcessNode::lastKnownBlockHeightUpdated(uint64_t height) { + observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); +} + +void InProcessNode::blockchainUpdated() { + uint64_t height; + crypto::hash ignore; + + core.get_blockchain_top(height, ignore); + observerManager.notify(&INodeObserver::localBlockchainUpdated, height); +} + +void InProcessNode::poolUpdated() { + observerManager.notify(&INodeObserver::poolChanged); +} + +void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, + std::list& newBlocks, uint64_t& startHeight, const InProcessNode::Callback& callback) +{ + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind(&InProcessNode::queryBlocksAsync, + this, + std::move(knownBlockIds), + timestamp, + std::ref(newBlocks), + std::ref(startHeight), + callback + ) + ); +} + +void InProcessNode::queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, + std::list& newBlocks, uint64_t& startHeight, const Callback& callback) +{ + std::error_code ec; + { + std::unique_lock lock(mutex); + ec = doQueryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight); + } + + callback(ec); +} + +std::error_code InProcessNode::doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, + std::list& newBlocks, uint64_t& startHeight) { + uint64_t currentHeight, fullOffset; + std::list entries; + + if (!core.queryBlocks(knownBlockIds, timestamp, startHeight, currentHeight, fullOffset, entries)) { + return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + } + + for (const auto& entry: entries) { + BlockCompleteEntry bce; + bce.blockHash = entry.block_id; + bce.block = entry.block; + std::copy(entry.txs.begin(), entry.txs.end(), std::back_inserter(bce.txs)); + + newBlocks.push_back(std::move(bce)); + } + + return std::error_code(); +} + +void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, crypto::hash knownBlockId, bool& isBcActual, std::vector& newTxs, + std::vector& deletedTxIds, const Callback& callback) { + + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind(&InProcessNode::getPoolSymmetricDifferenceAsync, + this, + std::move(knownPoolTxIds), + knownBlockId, + std::ref(isBcActual), + std::ref(newTxs), + std::ref(deletedTxIds), + callback + ) + ); +} + +void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, + std::vector& deleted_tx_ids, const Callback& callback) { + std::error_code ec = std::error_code(); + + std::unique_lock lock(mutex); + if (!core.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, is_bc_actual, new_txs, deleted_tx_ids)) { + ec = make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + } + + lock.unlock(); + callback(ec); +} + +} //namespace CryptoNote + diff --git a/src/inprocess_node/InProcessNode.h b/src/inprocess_node/InProcessNode.h new file mode 100644 index 0000000000..1010443b69 --- /dev/null +++ b/src/inprocess_node/InProcessNode.h @@ -0,0 +1,118 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "INode.h" +#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" +#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" +#include "cryptonote_core/ICore.h" +#include "cryptonote_core/ICoreObserver.h" +#include "common/ObserverManager.h" + +#include +#include + +namespace cryptonote { +class core; +} + +namespace CryptoNote { + +class InProcessNode : public INode, public cryptonote::ICryptonoteProtocolObserver, public cryptonote::ICoreObserver { +public: + InProcessNode(cryptonote::ICore& core, cryptonote::ICryptonoteProtocolQuery& protocol); + + InProcessNode(const InProcessNode&) = delete; + InProcessNode(InProcessNode&&) = delete; + + InProcessNode& operator=(const InProcessNode&) = delete; + InProcessNode& operator=(InProcessNode&&) = delete; + + virtual ~InProcessNode(); + + virtual void init(const Callback& callback) override; + virtual bool shutdown() override; + + virtual bool addObserver(INodeObserver* observer) override; + virtual bool removeObserver(INodeObserver* observer) override; + + virtual size_t getPeerCount() const; + virtual uint64_t getLastLocalBlockHeight() const; + virtual uint64_t getLastKnownBlockHeight() const; + virtual uint64_t getLastLocalBlockTimestamp() const override; + + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, + std::vector& result, const Callback& callback) override; + virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) override; + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, + const Callback& callback) override; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, + std::vector& deleted_tx_ids, const Callback& callback) override; + +private: + virtual void peerCountUpdated(size_t count) override; + virtual void lastKnownBlockHeightUpdated(uint64_t height) override; + virtual void blockchainUpdated() override; + virtual void poolUpdated() override; + + void getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + std::error_code doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight); + + void getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + std::error_code doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); + + void getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, + std::vector& result, const Callback& callback); + std::error_code doGetRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, + std::vector& result); + + void relayTransactionAsync(const cryptonote::Transaction& transaction, const Callback& callback); + std::error_code doRelayTransaction(const cryptonote::Transaction& transaction); + + void queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, + const Callback& callback); + std::error_code doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight); + + void getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, + std::vector& deleted_tx_ids, const Callback& callback); + + void workerFunc(); + + enum State { + NOT_INITIALIZED, + INITIALIZED + }; + + State state; + cryptonote::ICore& core; + cryptonote::ICryptonoteProtocolQuery& protocol; + tools::ObserverManager observerManager; + + boost::asio::io_service ioService; + std::unique_ptr workerThread; + std::unique_ptr work; + + mutable std::mutex mutex; +}; + +} //namespace CryptoNote + + + diff --git a/src/inprocess_node/InProcessNodeErrors.cpp b/src/inprocess_node/InProcessNodeErrors.cpp new file mode 100644 index 0000000000..9f63125d12 --- /dev/null +++ b/src/inprocess_node/InProcessNodeErrors.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "InProcessNodeErrors.h" + +namespace cryptonote { +namespace error { + +InProcessNodeErrorCategory InProcessNodeErrorCategory::INSTANCE; + +} //namespace error +} //namespace cryptonote + diff --git a/src/inprocess_node/InProcessNodeErrors.h b/src/inprocess_node/InProcessNodeErrors.h new file mode 100644 index 0000000000..4fdf82bcc3 --- /dev/null +++ b/src/inprocess_node/InProcessNodeErrors.h @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace cryptonote { +namespace error { + +enum InProcessNodeErrorCodes { + NOT_INITIALIZED = 1, + ALREADY_INITIALIZED, + NETWORK_ERROR, + NODE_BUSY, + INTERNAL_NODE_ERROR, + REQUEST_ERROR +}; + +class InProcessNodeErrorCategory : public std::error_category { +public: + static InProcessNodeErrorCategory INSTANCE; + + virtual const char* name() const throw() { + return "InProcessNodeErrorCategory"; + } + + virtual std::error_condition default_error_condition(int ev) const throw() { + return std::error_condition(ev, *this); + } + + virtual std::string message(int ev) const { + switch (ev) { + case NOT_INITIALIZED: return "Object was not initialized"; + case ALREADY_INITIALIZED: return "Object has been already initialized"; + case NETWORK_ERROR: return "Network error"; + case NODE_BUSY: return "Node is busy"; + case INTERNAL_NODE_ERROR: return "Internal node error"; + case REQUEST_ERROR: return "Error in request parameters"; + default: return "Unknown error"; + } + } + +private: + InProcessNodeErrorCategory() { + } +}; + +} //namespace error +} //namespace cryptonote + +inline std::error_code make_error_code(cryptonote::error::InProcessNodeErrorCodes e) { + return std::error_code(static_cast(e), cryptonote::error::InProcessNodeErrorCategory::INSTANCE); +} + diff --git a/src/logger/CommonLogger.cpp b/src/logger/CommonLogger.cpp new file mode 100755 index 0000000000..eb1eb9b622 --- /dev/null +++ b/src/logger/CommonLogger.cpp @@ -0,0 +1,35 @@ +#include "CommonLogger.h" + +#include + +using namespace Log; + +CommonLogger::CommonLogger(ILogger::Level level) : logLevel(level) { +} + +void CommonLogger::doLogString(Level level, boost::posix_time::ptime time, const std::string& message) { +} + +void CommonLogger::enableCategory(const std::string& category) { + disabledCategories.erase(category); +} + +void CommonLogger::disableCategory(const std::string& category) { + disabledCategories.insert(category); +} + +void CommonLogger::setMaxLevel(Level level) { + logLevel = level; +} + +void CommonLogger::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { + if (level > logLevel) { + return; + } + + if (disabledCategories.count(category) != 0) { + return; + } + + doLogString(level, time, body); +} diff --git a/src/logger/CommonLogger.h b/src/logger/CommonLogger.h new file mode 100755 index 0000000000..2643c29805 --- /dev/null +++ b/src/logger/CommonLogger.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "ILogger.h" + +namespace Log { + +class CommonLogger : public ILogger { +public: + virtual void enableCategory(const std::string& category) override; + virtual void disableCategory(const std::string& category) override; + virtual void setMaxLevel(Level level) override; + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; + +protected: + std::set disabledCategories; + Level logLevel; + + CommonLogger(ILogger::Level level); + virtual void doLogString(Level level, boost::posix_time::ptime time, const std::string& message); +}; + +} diff --git a/src/logger/ConsoleLogger.cpp b/src/logger/ConsoleLogger.cpp new file mode 100755 index 0000000000..d8e82b8ab4 --- /dev/null +++ b/src/logger/ConsoleLogger.cpp @@ -0,0 +1,162 @@ +#include "ConsoleLogger.h" +#include +#include +#include +#if defined(_WIN32) +#include +#include +#else +#include +#endif + + +using namespace Log; + +ConsoleLogger::ConsoleLogger(ILogger::Level level) : CommonLogger(level) { +} + +void ConsoleLogger::doLogString(Level level, boost::posix_time::ptime time, const std::string& message) { + std::vector > coloredStrings; + { + std::stringstream ss(message); + char c; + std::string color = ""; + std::string text = ""; + ss.read(&c, 1); + while (!ss.eof()) { + if (c == ILogger::COLOR_DELIMETER) { + coloredStrings.push_back(std::make_pair(color, text)); + color.clear(); + text.clear(); + color += COLOR_DELIMETER; + ss.read(&c, 1); + while (c != ILogger::COLOR_DELIMETER) { + color += c; + ss.read(&c, 1); + } + color += COLOR_DELIMETER; + } else { + text += c; + } + ss.read(&c, 1); + } + coloredStrings.push_back(std::make_pair(color, text)); + coloredStrings[0].first = coloredStrings[1].first; + coloredStrings[0].second = boost::posix_time::to_simple_string(time) + ILogger::LEVEL_NAMES[level]; + } + + std::lock_guard lock(mutex); + for (size_t stringNumber = 0 ; stringNumber < coloredStrings.size(); ++stringNumber) { + if (coloredStrings[stringNumber].second.empty()) continue; + std::string color = coloredStrings[stringNumber].first; + + if (color == BLUE) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE); +#else + std::cout << "\033[0;34m"; +#endif + } else if (color == GREEN) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN); +#else + std::cout << "\033[0;32m"; +#endif + } else if (color == RED) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED); +#else + std::cout << "\033[0;31m"; +#endif + } else if (color == YELLOW) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN); +#else + std::cout << "\033[0;33m"; +#endif + } else if (color == WHITE) { +#ifdef _WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); +#else + std::cout << "\033[0;37m"; +#endif + } else if (color == CYAN) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE); +#else + std::cout << "\033[0;36m"; +#endif + } else if (color == MAGENTA) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_BLUE); +#else + std::cout << "\033[0;35m"; +#endif + } else if (color == BRIGHT_BLUE) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;34m"; +#endif + } else if (color == BRIGHT_GREEN) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;32m"; +#endif + } else if (color == BRIGHT_RED) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;31m"; +#endif + } else if (color == BRIGHT_YELLOW) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;33m"; +#endif + } else if (color == BRIGHT_WHITE) { +#ifdef _WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;37m"; +#endif + } else if (color == BRIGHT_CYAN) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;36m"; +#endif + } else if (color == BRIGHT_MAGENTA) { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); +#else + std::cout << "\033[1;35m"; +#endif + } else { +#ifdef _WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); +#else + std::cout << "\033[0m"; +#endif + } + + std::cout << coloredStrings[stringNumber].second; + } +} diff --git a/src/logger/ConsoleLogger.h b/src/logger/ConsoleLogger.h new file mode 100755 index 0000000000..bfa672eceb --- /dev/null +++ b/src/logger/ConsoleLogger.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "CommonLogger.h" + +namespace Log { + +class ConsoleLogger : public CommonLogger { +public: + ConsoleLogger(ILogger::Level level = ILogger::DEBUGGING); + +protected: + virtual void doLogString(Level level, boost::posix_time::ptime time, const std::string& message) override; + +private: + std::mutex mutex; +}; + +} diff --git a/src/logger/ILogger.cpp b/src/logger/ILogger.cpp new file mode 100755 index 0000000000..47cc3a052c --- /dev/null +++ b/src/logger/ILogger.cpp @@ -0,0 +1,30 @@ +#include "ILogger.h" + +using namespace Log; + +const std::string ILogger::BLUE = "\x1F""BLUE\x1F"; +const std::string ILogger::GREEN = "\x1F""GREEN\x1F"; +const std::string ILogger::RED = "\x1F""RED\x1F"; +const std::string ILogger::YELLOW = "\x1F""YELLOW\x1F"; +const std::string ILogger::WHITE = "\x1F""WHITE\x1F"; +const std::string ILogger::CYAN = "\x1F""CYAN\x1F"; +const std::string ILogger::MAGENTA = "\x1F""MAGENTA\x1F"; +const std::string ILogger::BRIGHT_BLUE = "\x1F""BRIGHT_BLUE\x1F"; +const std::string ILogger::BRIGHT_GREEN = "\x1F""BRIGHT_GREEN\x1F"; +const std::string ILogger::BRIGHT_RED = "\x1F""BRIGHT_RED\x1F"; +const std::string ILogger::BRIGHT_YELLOW = "\x1F""BRIGHT_YELLOW\x1F"; +const std::string ILogger::BRIGHT_WHITE = "\x1F""BRIGHT_WHITE\x1F"; +const std::string ILogger::BRIGHT_CYAN = "\x1F""BRIGHT_CYAN\x1F"; +const std::string ILogger::BRIGHT_MAGENTA = "\x1F""BRIGHT_MAGENTA\x1F"; +const std::string ILogger::DEFAULT = "\x1F""DEFAULT\x1F"; + +const char ILogger::COLOR_DELIMETER = '\x1F'; + +const std::vector ILogger::LEVEL_NAMES = { + " [FATAL] ", + " [ERROR] ", + " [WARNING] ", + " [INFO] ", + " [DEBUG] ", + " [TRACE] " +}; diff --git a/src/logger/ILogger.h b/src/logger/ILogger.h new file mode 100755 index 0000000000..649e2de094 --- /dev/null +++ b/src/logger/ILogger.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +#undef ERROR + +namespace Log { + +class ILogger { +public: + typedef std::size_t Level; + + const static Level FATAL = 0; + const static Level ERROR = 1; + const static Level WARNING = 2; + const static Level INFO = 3; + const static Level DEBUGGING = 4; + const static Level TRACE = 5; + + const static std::string BLUE; + const static std::string GREEN; + const static std::string RED; + const static std::string YELLOW; + const static std::string WHITE; + const static std::string CYAN; + const static std::string MAGENTA; + const static std::string BRIGHT_BLUE; + const static std::string BRIGHT_GREEN; + const static std::string BRIGHT_RED; + const static std::string BRIGHT_YELLOW; + const static std::string BRIGHT_WHITE; + const static std::string BRIGHT_CYAN; + const static std::string BRIGHT_MAGENTA; + const static std::string DEFAULT; + + const static char COLOR_DELIMETER; + + const static std::vector LEVEL_NAMES; + + virtual void enableCategory(const std::string& category) = 0; + virtual void disableCategory(const std::string& category) = 0; + virtual void setMaxLevel(Level level) = 0; + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) = 0; +}; + +} diff --git a/src/logger/LoggerGroup.cpp b/src/logger/LoggerGroup.cpp new file mode 100755 index 0000000000..f89926cbd2 --- /dev/null +++ b/src/logger/LoggerGroup.cpp @@ -0,0 +1,29 @@ +#include "LoggerGroup.h" +#include + +using namespace Log; + +LoggerGroup::LoggerGroup(ILogger::Level level) : CommonLogger(level) { +} + +void LoggerGroup::addLogger(ILogger& logger) { + loggers.push_back(&logger); +} + +void LoggerGroup::removeLogger(ILogger& logger) { + loggers.erase(std::remove(loggers.begin(), loggers.end(), &logger), loggers.end()); +} + +void LoggerGroup::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { + if (level > logLevel) { + return; + } + + if (disabledCategories.count(category) != 0) { + return; + } + + for (auto& logger: loggers) { + (*logger)(category, level, time, body); + } +} diff --git a/src/logger/LoggerGroup.h b/src/logger/LoggerGroup.h new file mode 100755 index 0000000000..7cb4e3319f --- /dev/null +++ b/src/logger/LoggerGroup.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "CommonLogger.h" + +namespace Log { + +class LoggerGroup : public CommonLogger { +public: + LoggerGroup(ILogger::Level level = DEBUGGING); + + void addLogger(ILogger& logger); + void removeLogger(ILogger& logger); + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; + +protected: + std::vector loggers; +}; + +} diff --git a/src/logger/LoggerMessage.cpp b/src/logger/LoggerMessage.cpp new file mode 100755 index 0000000000..5b62d2f1f6 --- /dev/null +++ b/src/logger/LoggerMessage.cpp @@ -0,0 +1,94 @@ +#include "LoggerMessage.h" + +using namespace Log; + +LoggerMessage::LoggerMessage(ILogger& logger, const std::string& category, ILogger::Level level, const std::string& color) + : std::ostream(this) + , std::streambuf() + , logger(logger) + , category(category) + , logLevel(level) + , message(color) + , timestamp(boost::posix_time::microsec_clock::local_time()) + , gotText(false) { +} + +LoggerMessage::~LoggerMessage() { + if (gotText) { + (*this) << std::endl; + } +} + +#ifndef __linux__ +LoggerMessage::LoggerMessage(LoggerMessage&& other) + : std::ostream(std::move(other)) + , std::streambuf(std::move(other)) + , category(other.category) + , logLevel(other.logLevel) + , logger(other.logger) + , message(other.message) + , timestamp(boost::posix_time::microsec_clock::local_time()) + , gotText(false) { + this->set_rdbuf(this); +} +#else +LoggerMessage::LoggerMessage(LoggerMessage&& other) + : std::ostream(nullptr) + , std::streambuf() + , category(other.category) + , logLevel(other.logLevel) + , logger(other.logger) + , message(other.message) + , timestamp(boost::posix_time::microsec_clock::local_time()) + , gotText(false) { + if (this != &other) { + _M_tie = nullptr; + _M_streambuf = nullptr; + + //ios_base swap + std::swap(_M_streambuf_state, other._M_streambuf_state); + std::swap(_M_exception, other._M_exception); + std::swap(_M_flags, other._M_flags); + std::swap(_M_precision, other._M_precision); + std::swap(_M_width, other._M_width); + + std::swap(_M_callbacks, other._M_callbacks); + std::swap(_M_ios_locale, other._M_ios_locale); + //ios_base swap + + //streambuf swap + char *_Pfirst = pbase(); + char *_Pnext = pptr(); + char *_Pend = epptr(); + char *_Gfirst = eback(); + char *_Gnext = gptr(); + char *_Gend = egptr(); + + setp(other.pbase(), other.epptr()); + other.setp(_Pfirst, _Pend); + + setg(other.eback(), other.gptr(), other.egptr()); + other.setg(_Gfirst, _Gnext, _Gend); + + std::swap(_M_buf_locale, other._M_buf_locale); + //streambuf swap + + std::swap(_M_fill, other._M_fill); + std::swap(_M_tie, other._M_tie); + } + _M_streambuf = this; +} +#endif + +int LoggerMessage::sync() { + logger(category, logLevel, timestamp, message); + gotText = false; + message = ILogger::DEFAULT; + return 0; +} + +int LoggerMessage::overflow(int c) { + gotText = true; + message += static_cast(c); + return 0; +} diff --git a/src/logger/LoggerMessage.h b/src/logger/LoggerMessage.h new file mode 100755 index 0000000000..08879d50d3 --- /dev/null +++ b/src/logger/LoggerMessage.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include "ILogger.h" + +namespace Log { + +class LoggerMessage : public std::ostream, std::streambuf { +public: + LoggerMessage(ILogger& logger, const std::string& category, ILogger::Level level, const std::string& color); + ~LoggerMessage(); + LoggerMessage(const LoggerMessage&) = delete; + LoggerMessage& operator=(const LoggerMessage&) = delete; + LoggerMessage(LoggerMessage&& other); + +private: + int sync() override; + int overflow(int c) override; + + std::string message; + const std::string category; + ILogger::Level logLevel; + ILogger& logger; + boost::posix_time::ptime timestamp; + bool gotText; +}; + +} diff --git a/src/logger/LoggerRef.cpp b/src/logger/LoggerRef.cpp new file mode 100755 index 0000000000..29fc035d5a --- /dev/null +++ b/src/logger/LoggerRef.cpp @@ -0,0 +1,20 @@ +#include "LoggerRef.h" + +using namespace Log; + +LoggerRef::LoggerRef(ILogger& logger, const std::string& category) : logger(logger), category(category) { +} + +LoggerRef::LoggerRef(const LoggerRef& other) : logger(other.logger), category(other.category) { +} + +LoggerRef::LoggerRef(const LoggerRef& other, const std::string& category) : logger(other.logger), category(category) { +} + +LoggerMessage LoggerRef::operator()(const std::string& category, ILogger::Level level, const std::string& color) { + return LoggerMessage(logger, category, level, color); +} + +LoggerMessage LoggerRef::operator()(ILogger::Level level, const std::string& color) { + return LoggerMessage(logger, category, level, color); +} diff --git a/src/logger/LoggerRef.h b/src/logger/LoggerRef.h new file mode 100755 index 0000000000..39099e026e --- /dev/null +++ b/src/logger/LoggerRef.h @@ -0,0 +1,21 @@ +#pragma once + +#include "ILogger.h" +#include "LoggerMessage.h" + +namespace Log { + +class LoggerRef { +public: + LoggerRef(const LoggerRef& other); + LoggerRef(const LoggerRef& other, const std::string& category); + LoggerRef(ILogger& logger, const std::string& category); + LoggerMessage operator()(const std::string& category, ILogger::Level level, const std::string& color = ILogger::DEFAULT); + LoggerMessage operator()(ILogger::Level level = ILogger::INFO, const std::string& color = ILogger::DEFAULT); + +private: + ILogger& logger; + std::string category; +}; + +} diff --git a/src/logger/StreamLogger.cpp b/src/logger/StreamLogger.cpp new file mode 100755 index 0000000000..344e499fe7 --- /dev/null +++ b/src/logger/StreamLogger.cpp @@ -0,0 +1,28 @@ +#include "StreamLogger.h" +#include +#include + +using namespace Log; + +StreamLogger::StreamLogger(std::ostream& stream, ILogger::Level level) : CommonLogger(level), stream(stream) { +} + +void StreamLogger::doLogString(Level level, boost::posix_time::ptime time, const std::string& message) { + std::string result; + std::stringstream ss(message); + char c; + bool readingText = true; + while (ss.read(&c, 1)) { + if (c == ILogger::COLOR_DELIMETER) { + readingText = !readingText; + continue; + } + + if (readingText) { + result += c; + } + } + + std::lock_guard lock(mutex); + stream << boost::posix_time::to_iso_extended_string(time) << ILogger::LEVEL_NAMES[level] << result << std::flush; +} diff --git a/src/logger/StreamLogger.h b/src/logger/StreamLogger.h new file mode 100755 index 0000000000..72c7fddcae --- /dev/null +++ b/src/logger/StreamLogger.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "CommonLogger.h" + +namespace Log { + +class StreamLogger : public CommonLogger { +public: + StreamLogger(std::ostream& stream, ILogger::Level level = ILogger::DEBUGGING); + +protected: + virtual void doLogString(Level level, boost::posix_time::ptime time, const std::string& message) override; + +private: + std::mutex mutex; + std::ostream& stream; +}; + +} diff --git a/src/node_rpc_proxy/NodeErrors.h b/src/node_rpc_proxy/NodeErrors.h index f2ff10591b..e1c4bda4f4 100644 --- a/src/node_rpc_proxy/NodeErrors.h +++ b/src/node_rpc_proxy/NodeErrors.h @@ -30,6 +30,7 @@ enum NodeErrorCodes { NETWORK_ERROR, NODE_BUSY, INTERNAL_NODE_ERROR, + REQUEST_ERROR }; // custom category: @@ -52,6 +53,7 @@ class NodeErrorCategory : public std::error_category { case NETWORK_ERROR: return "Network error"; case NODE_BUSY: return "Node is busy"; case INTERNAL_NODE_ERROR: return "Internal node error"; + case REQUEST_ERROR: return "Error in request parameters"; default: return "Unknown error"; } } diff --git a/src/node_rpc_proxy/NodeRpcProxy.cpp b/src/node_rpc_proxy/NodeRpcProxy.cpp index 1e434d9d23..82605b0269 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.cpp +++ b/src/node_rpc_proxy/NodeRpcProxy.cpp @@ -47,7 +47,8 @@ NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort) : m_nodeAddress("http://" + nodeHost + ":" + std::to_string(nodePort)) , m_rpcTimeout(10000) , m_pullTimer(m_ioService) - , m_pullInterval(10000) { + , m_pullInterval(10000) + , m_lastLocalBlockTimestamp(0) { resetInternalState(); } @@ -57,8 +58,6 @@ NodeRpcProxy::~NodeRpcProxy() { void NodeRpcProxy::resetInternalState() { m_ioService.reset(); - m_observerManager.clear(); - m_peerCount = 0; m_nodeHeight = 0; m_networkHeight = 0; @@ -83,9 +82,11 @@ bool NodeRpcProxy::shutdown() { boost::system::error_code ignored_ec; m_pullTimer.cancel(ignored_ec); m_ioService.stop(); - m_workerThread.join(); + if (m_workerThread.joinable()) { + m_workerThread.join(); + m_initState.endShutdown(); + } - m_initState.endShutdown(); return true; } @@ -129,6 +130,7 @@ void NodeRpcProxy::updateNodeStatus() { if (blockHash != m_lastKnowHash) { m_lastKnowHash = blockHash; m_nodeHeight = rsp.block_header.height; + m_lastLocalBlockTimestamp = rsp.block_header.timestamp; // TODO request and update network height m_networkHeight = m_nodeHeight; m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); @@ -181,6 +183,10 @@ uint64_t NodeRpcProxy::getLastKnownBlockHeight() const { return m_networkHeight; } +uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const { + return m_lastLocalBlockTimestamp; +} + void NodeRpcProxy::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { if (!m_initState.initialized()) { callback(make_error_code(error::NOT_INITIALIZED)); @@ -218,6 +224,15 @@ void NodeRpcProxy::getTransactionOutsGlobalIndices(const crypto::hash& transacti m_ioService.post(std::bind(&NodeRpcProxy::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback)); } +void NodeRpcProxy::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + if (!m_initState.initialized()) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + m_ioService.post(std::bind(&NodeRpcProxy::doQueryBlocks, this, std::move(knownBlockIds), timestamp, std::ref(newBlocks), std::ref(startHeight), callback)); +} + void NodeRpcProxy::doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { COMMAND_RPC_SEND_RAW_TX::request req; COMMAND_RPC_SEND_RAW_TX::response rsp; @@ -265,4 +280,36 @@ void NodeRpcProxy::doGetTransactionOutsGlobalIndices(const crypto::hash& transac callback(ec); } +void NodeRpcProxy::doQueryBlocks(const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + cryptonote::COMMAND_RPC_QUERY_BLOCKS::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_QUERY_BLOCKS::response rsp = AUTO_VAL_INIT(rsp); + + req.block_ids = knownBlockIds; + req.timestamp = timestamp; + + bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/queryblocks.bin", req, rsp, m_httpClient, m_rpcTimeout); + + std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + + if (!ec) { + for (auto& item : rsp.items) { + BlockCompleteEntry entry; + + entry.blockHash = item.block_id; + entry.block = std::move(item.block); + entry.txs = std::move(item.txs); + + newBlocks.push_back(std::move(entry)); + } + + startHeight = rsp.start_height; + } + callback(ec); +} + +void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { + is_bc_actual = true; + callback(std::error_code()); +}; + } diff --git a/src/node_rpc_proxy/NodeRpcProxy.h b/src/node_rpc_proxy/NodeRpcProxy.h index 158fdf1e98..b255de4134 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.h +++ b/src/node_rpc_proxy/NodeRpcProxy.h @@ -44,11 +44,14 @@ class NodeRpcProxy : public CryptoNote::INode { virtual size_t getPeerCount() const; virtual uint64_t getLastLocalBlockHeight() const; virtual uint64_t getLastKnownBlockHeight() const; + virtual uint64_t getLastLocalBlockTimestamp() const override; virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; unsigned int rpcTimeout() const { return m_rpcTimeout; } void rpcTimeout(unsigned int val) { m_rpcTimeout = val; } @@ -65,6 +68,7 @@ class NodeRpcProxy : public CryptoNote::INode { void doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); void doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + void doQueryBlocks(const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); private: tools::InitState m_initState; @@ -84,6 +88,7 @@ class NodeRpcProxy : public CryptoNote::INode { uint64_t m_nodeHeight; uint64_t m_networkHeight; crypto::hash m_lastKnowHash; + uint64_t m_lastLocalBlockTimestamp; }; } diff --git a/src/p2p/NetNodeConfig.cpp b/src/p2p/NetNodeConfig.cpp new file mode 100644 index 0000000000..11fbd51544 --- /dev/null +++ b/src/p2p/NetNodeConfig.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include "NetNodeConfig.h" + +#include "cryptonote_config.h" +#include "common/command_line.h" + +namespace nodetool { +namespace { + +const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; +const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(cryptonote::P2P_DEFAULT_PORT)}; +const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; +const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; +const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; +const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; +const command_line::arg_descriptor > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." + " If this option is given the options add-priority-node and seed-node are ignored"}; +const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; +const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + +bool parsePeerFromString(nodetool::net_address& pe, const std::string& node_addr) { + return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); +} + +bool parsePeersAndAddToContainer(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor>& arg, + std::vector& container) +{ + std::vector peers = command_line::get_arg(vm, arg); + + for(const std::string& str: peers) { + nodetool::net_address na = boost::value_initialized(); + if (!parsePeerFromString(na, str)) { + return false; + } + container.push_back(na); + } + + return true; +} + +} //namespace + +void NetNodeConfig::initOptions(boost::program_options::options_description& desc) { + command_line::add_arg(desc, arg_p2p_bind_ip); + command_line::add_arg(desc, arg_p2p_bind_port); + command_line::add_arg(desc, arg_p2p_external_port); + command_line::add_arg(desc, arg_p2p_allow_local_ip); + command_line::add_arg(desc, arg_p2p_add_peer); + command_line::add_arg(desc, arg_p2p_add_priority_node); + command_line::add_arg(desc, arg_p2p_add_exclusive_node); + command_line::add_arg(desc, arg_p2p_seed_node); + command_line::add_arg(desc, arg_p2p_hide_my_port); +} + +NetNodeConfig::NetNodeConfig() { + bindIp = "0.0.0.0"; + bindPort = std::to_string(cryptonote::P2P_DEFAULT_PORT); + externalPort = 0; + allowLocalIp = false; + hideMyPort = false; + configFolder = tools::get_default_data_dir(); +} + +bool NetNodeConfig::init(const boost::program_options::variables_map& vm) +{ + bindIp = command_line::get_arg(vm, arg_p2p_bind_ip); + bindPort = command_line::get_arg(vm, arg_p2p_bind_port); + externalPort = command_line::get_arg(vm, arg_p2p_external_port); + allowLocalIp = command_line::get_arg(vm, arg_p2p_allow_local_ip); + configFolder = command_line::get_arg(vm, command_line::arg_data_dir); + + if (command_line::has_arg(vm, arg_p2p_add_peer)) + { + std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); + for(const std::string& pr_str: perrs) + { + nodetool::peerlist_entry pe = boost::value_initialized(); + pe.id = crypto::rand(); + if (!parsePeerFromString(pe.adr, pr_str)) { + return false; + } + peers.push_back(pe); + } + } + + if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) { + if (!parsePeersAndAddToContainer(vm, arg_p2p_add_exclusive_node, exclusiveNodes)) + return false; + } + + if (command_line::has_arg(vm, arg_p2p_add_priority_node)) { + if (!parsePeersAndAddToContainer(vm, arg_p2p_add_priority_node, priorityNodes)) + return false; + } + + if (command_line::has_arg(vm, arg_p2p_seed_node)) { + if (!parsePeersAndAddToContainer(vm, arg_p2p_seed_node, seedNodes)) + return false; + } + + if(command_line::has_arg(vm, arg_p2p_hide_my_port)) + hideMyPort = true; + + return true; +} + +} //namespace nodetool + diff --git a/src/p2p/NetNodeConfig.h b/src/p2p/NetNodeConfig.h new file mode 100644 index 0000000000..d2a846b4fe --- /dev/null +++ b/src/p2p/NetNodeConfig.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include +#include "p2p_protocol_defs.h" + +namespace nodetool { + +class NetNodeConfig { +public: + NetNodeConfig(); + static void initOptions(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + + std::string bindIp; + std::string bindPort; + uint32_t externalPort; + bool allowLocalIp; + std::vector peers; + std::vector priorityNodes; + std::vector exclusiveNodes; + std::vector seedNodes; + bool hideMyPort; + std::string configFolder; +}; + +} //namespace nodetool diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index a8356eafe7..d54c71bcae 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -38,6 +38,7 @@ #include "math_helper.h" #include "net_node_common.h" #include "common/command_line.h" +#include "NetNodeConfig.h" PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) @@ -72,7 +73,7 @@ namespace nodetool static void init_options(boost::program_options::options_description& desc); bool run(); - bool init(const boost::program_options::variables_map& vm, bool testnet); + bool init(const NetNodeConfig& config, bool testnet); bool deinit(); bool send_stop_signal(); uint32_t get_this_peer_port(){return m_listenning_port;} @@ -121,22 +122,24 @@ namespace nodetool bool make_default_config(); bool store_config(); bool check_trust(const proof_of_trust& tr); + void initUpnp(); //----------------- levin_commands_handler ------------------------------------------------------------- - virtual void on_connection_new(p2p_connection_context& context); - virtual void on_connection_close(p2p_connection_context& context); - virtual void callback(p2p_connection_context& context); + virtual void on_connection_new(p2p_connection_context& context) override; + virtual void on_connection_close(p2p_connection_context& context) override; + virtual void callback(p2p_connection_context& context) override; //----------------- i_p2p_endpoint ------------------------------------------------------------- - virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context); - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context); - virtual bool drop_connection(const epee::net_utils::connection_context_base& context); - virtual void request_callback(const epee::net_utils::connection_context_base& context); - virtual void for_each_connection(std::function f); + virtual void relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) override; + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) override; + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) override; + virtual bool drop_connection(const epee::net_utils::connection_context_base& context) override; + virtual void request_callback(const epee::net_utils::connection_context_base& context) override; + virtual void for_each_connection(std::function f) override; //----------------------------------------------------------------------------------------------- bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line(const boost::program_options::variables_map& vm); + bool handleConfig(const NetNodeConfig& config); bool idle_worker(); bool handle_remote_peerlist(const std::list& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context); bool get_local_node_data(basic_node_data& node_data); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 87346fdbdf..f2347cd669 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -37,35 +37,6 @@ namespace nodetool { - namespace - { - const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; - const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(cryptonote::P2P_DEFAULT_PORT)}; - const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; - const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; - const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; - const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; - const command_line::arg_descriptor > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." - " If this option is given the options add-priority-node and seed-node are ignored"}; - const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; - const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; - } - - //----------------------------------------------------------------------------------- - template - void node_server::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_p2p_bind_ip); - command_line::add_arg(desc, arg_p2p_bind_port); - command_line::add_arg(desc, arg_p2p_external_port); - command_line::add_arg(desc, arg_p2p_allow_local_ip); - command_line::add_arg(desc, arg_p2p_add_peer); - command_line::add_arg(desc, arg_p2p_add_priority_node); - command_line::add_arg(desc, arg_p2p_add_exclusive_node); - command_line::add_arg(desc, arg_p2p_seed_node); - command_line::add_arg(desc, arg_p2p_hide_my_port); - } - //----------------------------------------------------------------------------------- template bool node_server::init_config() { @@ -112,55 +83,6 @@ namespace nodetool return true; } //----------------------------------------------------------------------------------- - template - bool node_server::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) - { - return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); - } - //----------------------------------------------------------------------------------- - template - bool node_server::handle_command_line(const boost::program_options::variables_map& vm) - { - m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); - m_port = command_line::get_arg(vm, arg_p2p_bind_port); - m_external_port = command_line::get_arg(vm, arg_p2p_external_port); - m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); - - if (command_line::has_arg(vm, arg_p2p_add_peer)) - { - std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); - for(const std::string& pr_str: perrs) - { - nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe); - pe.id = crypto::rand(); - bool r = parse_peer_from_string(pe.adr, pr_str); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - m_command_line_peers.push_back(pe); - } - } - - if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) - { - if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) - return false; - } - if (command_line::has_arg(vm, arg_p2p_add_priority_node)) - { - if (!parse_peers_and_add_to_container(vm, arg_p2p_add_priority_node, m_priority_peers)) - return false; - } - if (command_line::has_arg(vm, arg_p2p_seed_node)) - { - if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, m_seed_nodes)) - return false; - } - - if(command_line::has_arg(vm, arg_p2p_hide_my_port)) - m_hide_my_port = true; - - return true; - } - //----------------------------------------------------------------------------------- namespace { template @@ -202,9 +124,59 @@ namespace nodetool } } - //----------------------------------------------------------------------------------- template - bool node_server::init(const boost::program_options::variables_map& vm, bool testnet) { + void node_server::initUpnp() { + // Add UPnP port mapping + LOG_PRINT_L0("Attempting to add IGD port mapping."); + int result; + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); + UPNPUrls urls; + IGDdatas igdData; + char lanAddress[64]; + result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); + freeUPNPDevlist(deviceList); + if (result != 0) { + if (result == 1) { + std::ostringstream portString; + portString << m_listenning_port; + if (UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), + portString.str().c_str(), lanAddress, cryptonote::CRYPTONOTE_NAME, "TCP", 0, "0") != 0) { + LOG_ERROR("UPNP_AddPortMapping failed."); + } else { + LOG_PRINT_GREEN("Added IGD port mapping.", LOG_LEVEL_0); + } + } else if (result == 2) { + LOG_PRINT_L0("IGD was found but reported as not connected."); + } else if (result == 3) { + LOG_PRINT_L0("UPnP device was found but not recoginzed as IGD."); + } else { + LOG_ERROR("UPNP_GetValidIGD returned an unknown result code."); + } + + FreeUPNPUrls(&urls); + } else { + LOG_PRINT_L0("No IGD was found."); + } + } + + template + bool node_server::handleConfig(const NetNodeConfig& config) { + m_bind_ip = config.bindIp; + m_port = config.bindPort; + m_external_port = config.externalPort; + m_allow_local_ip = config.allowLocalIp; + + std::copy(config.peers.begin(), config.peers.end(), std::back_inserter(m_command_line_peers)); + std::copy(config.exclusiveNodes.begin(), config.exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); + std::copy(config.priorityNodes.begin(), config.priorityNodes.end(), std::back_inserter(m_priority_peers)); + std::copy(config.seedNodes.begin(), config.seedNodes.end(), std::back_inserter(m_seed_nodes)); + + m_hide_my_port = config.hideMyPort; + return true; + } + + template + bool node_server::init(const NetNodeConfig& config, bool testnet) { if (!testnet) { for (auto seed : cryptonote::SEED_NODES) { append_net_address(m_seed_nodes, seed); @@ -213,9 +185,10 @@ namespace nodetool m_network_id.data[0] += 1; } - bool res = handle_command_line(vm); + bool res = handleConfig(config); CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + + m_config_folder = config.configFolder; res = init_config(); CHECK_AND_ASSERT_MES(res, false, "Failed to init config."); @@ -247,37 +220,7 @@ namespace nodetool if(m_external_port) LOG_PRINT_L0("External port defined as " << m_external_port); - // Add UPnP port mapping - LOG_PRINT_L0("Attempting to add IGD port mapping."); - int result; - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); - UPNPUrls urls; - IGDdatas igdData; - char lanAddress[64]; - result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); - freeUPNPDevlist(deviceList); - if (result != 0) { - if (result == 1) { - std::ostringstream portString; - portString << m_listenning_port; - if (UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), - portString.str().c_str(), lanAddress, cryptonote::CRYPTONOTE_NAME, "TCP", 0, "0") != 0) { - LOG_ERROR("UPNP_AddPortMapping failed."); - } else { - LOG_PRINT_GREEN("Added IGD port mapping.", LOG_LEVEL_0); - } - } else if (result == 2) { - LOG_PRINT_L0("IGD was found but reported as not connected."); - } else if (result == 3) { - LOG_PRINT_L0("UPnP device was found but not recoginzed as IGD."); - } else { - LOG_ERROR("UPNP_GetValidIGD returned an unknown result code."); - } - - FreeUPNPUrls(&urls); - } else { - LOG_PRINT_L0("No IGD was found."); - } + initUpnp(); return res; } @@ -356,11 +299,10 @@ namespace nodetool bool node_server::send_stop_signal() { m_net_server.send_stop_signal(); + m_payload_handler.stop(); LOG_PRINT_L0("[node] Stop signal sent"); return true; } - //----------------------------------------------------------------------------------- - template bool node_server::do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context_, bool just_take_peerlist) @@ -886,7 +828,7 @@ namespace nodetool } //----------------------------------------------------------------------------------- template - bool node_server::relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + void node_server::relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) { std::list connections; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) @@ -900,7 +842,6 @@ namespace nodetool { m_net_server.get_config_object().notify(command, data_buff, c_id); } - return true; } //----------------------------------------------------------------------------------- template @@ -1115,12 +1056,14 @@ namespace nodetool void node_server::on_connection_new(p2p_connection_context& context) { LOG_PRINT_L2("["<< epee::net_utils::print_connection_context(context) << "] NEW CONNECTION"); + m_payload_handler.onConnectionOpened(context); } //----------------------------------------------------------------------------------- template void node_server::on_connection_close(p2p_connection_context& context) { LOG_PRINT_L2("["<< epee::net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); + m_payload_handler.onConnectionClosed(context); } template @@ -1145,20 +1088,4 @@ namespace nodetool return true; } - - template template - bool node_server::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor > & arg, Container& container) - { - std::vector perrs = command_line::get_arg(vm, arg); - - for(const std::string& pr_str: perrs) - { - nodetool::net_address na = AUTO_VAL_INIT(na); - bool r = parse_peer_from_string(na, pr_str); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); - container.push_back(na); - } - - return true; - } } diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index d86c3609e5..9aa4d05fc8 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -30,7 +30,7 @@ namespace nodetool template struct i_p2p_endpoint { - virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context)=0; + virtual void relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context)=0; virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; @@ -42,9 +42,8 @@ namespace nodetool template struct p2p_endpoint_stub: public i_p2p_endpoint { - virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + virtual void relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) { - return false; } virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) { @@ -60,11 +59,9 @@ namespace nodetool } virtual void request_callback(const epee::net_utils::connection_context_base& context) { - } virtual void for_each_connection(std::function f) { - } virtual uint64_t get_connections_count() diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index ba4bc0c134..8388e70af2 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -97,18 +97,6 @@ namespace nodetool const peerlist_entry& m_ple; }; - struct modify_last_seen - { - modify_last_seen(time_t last_seen):m_last_seen(last_seen){} - void operator()(peerlist_entry& e) - { - e.last_seen = m_last_seen; - } - private: - time_t m_last_seen; - }; - - typedef boost::multi_index_container< peerlist_entry, boost::multi_index::indexed_by< diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 641fda8fa8..3f3de41315 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -129,71 +129,11 @@ namespace cryptonote { CHECK_CORE_READY(); - typedef COMMAND_RPC_QUERY_BLOCKS::response_item ResponseItem; - - LockedBlockchainStorage lbs(m_core.get_blockchain_storage()); - - uint64_t currentHeight = lbs->get_current_blockchain_height(); - uint64_t startOffset = 0; - - if (!lbs->find_blockchain_supplement(req.block_ids, startOffset)) { - res.status = "Failed to find blockchain supplement"; + if (!m_core.queryBlocks(req.block_ids, req.timestamp, res.start_height, res.current_height, res.full_offset, res.items)) { + res.status = "Failed to perform query"; return false; } - uint64_t startFullOffset = 0; - - if (!lbs->getLowerBound(req.timestamp, startOffset, startFullOffset)) - startFullOffset = startOffset; - - res.full_offset = startFullOffset; - - if (startOffset != startFullOffset) { - std::list blockIds; - if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { - res.status = "Failed to get block ids"; - return false; - } - - for (const auto& id : blockIds) { - res.items.push_back(ResponseItem()); - res.items.back().block_id = id; - } - } - - auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - res.items.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); - - if (blocksLeft) { - std::list blocks; - lbs->get_blocks(startFullOffset, blocksLeft, blocks); - - for (auto& b : blocks) { - - ResponseItem item; - - item.block_id = get_block_hash(b); - - if (b.timestamp >= req.timestamp) { - // query transactions - std::list txs; - std::list missedTxs; - lbs->get_transactions(b.txHashes, txs, missedTxs); - - // fill data - block_complete_entry& completeEntry = item; - completeEntry.block = block_to_blob(b); - for (auto& tx : txs) { - completeEntry.txs.push_back(tx_to_blob(tx)); - } - } - - res.items.push_back(std::move(item)); - } - } - - res.current_height = currentHeight; - res.start_height = startOffset; - res.status = CORE_RPC_STATUS_OK; return true; } @@ -354,6 +294,18 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, connection_context& cntx) { + CHECK_CORE_READY(); + if (m_core.currency().isTestnet()) { + m_p2p.send_stop_signal(); + res.status = CORE_RPC_STATUS_OK; + } else { + res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx) { CHECK_CORE_READY(); diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index bdb9dbe70f..3f2765356f 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -54,6 +54,7 @@ namespace cryptonote MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) MAP_URI_AUTO_JON2("/start_mining", on_start_mining, COMMAND_RPC_START_MINING) MAP_URI_AUTO_JON2("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING) + MAP_URI_AUTO_JON2("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) @@ -75,6 +76,7 @@ namespace cryptonote bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx); bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx); bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx); + bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, connection_context& cntx); bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx); bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index a9b8e912d1..2c6afa77e9 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -279,6 +279,24 @@ namespace cryptonote }; }; + //----------------------------------------------- + struct COMMAND_RPC_STOP_DAEMON { + struct request { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + + struct response { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + // struct COMMAND_RPC_GETBLOCKCOUNT @@ -480,18 +498,6 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; - struct response_item : public block_complete_entry - { - crypto::hash block_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB(block_id) - KV_SERIALIZE(block) - KV_SERIALIZE(txs) - END_KV_SERIALIZE_MAP() - - }; - struct response { std::string status; @@ -499,7 +505,7 @@ namespace cryptonote uint64_t current_height; uint64_t full_offset; - std::list items; + std::list items; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) diff --git a/src/serialization/BinaryInputStreamSerializer.cpp b/src/serialization/BinaryInputStreamSerializer.cpp index 31a85b3264..5b981cfaf3 100644 --- a/src/serialization/BinaryInputStreamSerializer.cpp +++ b/src/serialization/BinaryInputStreamSerializer.cpp @@ -24,103 +24,41 @@ namespace { -void deserialize(std::istream& stream, uint8_t& v) { - char c; - stream.get(c); - v = static_cast(c); -} - -void deserialize(std::istream& stream, int8_t& v) { - uint8_t val; - deserialize(stream, val); - v = val; -} - -void deserialize(std::istream& stream, bool& v) { - uint8_t val; - deserialize(stream, val); - - v = val; -} - -void deserialize(std::istream& stream, uint32_t& v) { - char c; - - stream.get(c); - v = static_cast(c); - - stream.get(c); - v += static_cast(c) << 8; - - stream.get(c); - v += static_cast(c) << 16; - - stream.get(c); - v += static_cast(c) << 24; -} - -void deserialize(std::istream& stream, int32_t& v) { - uint32_t val; - deserialize(stream, val); - v = val; -} - -void deserialize(std::istream& stream, uint64_t& v) { - char c; - uint64_t uc; - - stream.get(c); - uc = static_cast(c); - v = uc; - - stream.get(c); - uc = static_cast(c); - v += (uc << 8); - - stream.get(c); - uc = static_cast(c); - v += (uc << 16); - - stream.get(c); - uc = static_cast(c); - v += (uc << 24); - - stream.get(c); - uc = static_cast(c); - v += (uc << 32); - - stream.get(c); - uc = static_cast(c); - v += (uc << 40); - - stream.get(c); - uc = static_cast(c); - v += (uc << 48); - - stream.get(c); - uc = static_cast(c); - v += (uc << 56); +template::digits> +typename std::enable_if::value && std::is_unsigned::value, size_t>::type +readVarint(std::istream& s, T &i) { + size_t read = 0; + i = 0; + for (int shift = 0;; shift += 7) { + if (s.eof()) { + return read; + } + uint8_t byte = s.get(); + if (!s) { + throw std::runtime_error("Stream read error"); + } + ++read; + if (shift + 7 >= bits && byte >= 1 << (bits - shift)) { + throw std::runtime_error("Varint overflow"); + } + if (byte == 0 && shift != 0) { + throw std::runtime_error("Non-canonical varint representation"); + } + i |= static_cast(byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + break; + } + } + return read; } -void deserialize(std::istream& stream, int64_t& v) { - uint64_t val; - deserialize(stream, val); - v = val; +template +void readVarintAs(std::istream& s, T &i) { + StorageType v; + readVarint(s, v); + i = static_cast(v); } -void deserialize(std::istream& stream, char* buf, size_t len) { - const size_t chunk = 1000; - -// stream.read(buf, len); - -// looks redundant, but i had a bug with it - while (len && stream) { - size_t toRead = std::min(len, chunk); - stream.read(buf, toRead); - len -= toRead; - buf += toRead; - } -} } @@ -139,10 +77,7 @@ ISerializer& BinaryInputStreamSerializer::endObject() { } ISerializer& BinaryInputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { - uint64_t val; - serializeVarint(val, name, *this); - size = val; - + readVarintAs(stream, size); return *this; } @@ -151,79 +86,61 @@ ISerializer& BinaryInputStreamSerializer::endArray() { } ISerializer& BinaryInputStreamSerializer::operator()(uint8_t& value, const std::string& name) { - deserialize(stream, value); - + readVarint(stream, value); return *this; } ISerializer& BinaryInputStreamSerializer::operator()(uint32_t& value, const std::string& name) { - deserialize(stream, value); - + readVarint(stream, value); return *this; } ISerializer& BinaryInputStreamSerializer::operator()(int32_t& value, const std::string& name) { - uint32_t v; - operator()(v, name); - value = v; - + readVarintAs(stream, value); return *this; } ISerializer& BinaryInputStreamSerializer::operator()(int64_t& value, const std::string& name) { - deserialize(stream, value); - + readVarintAs(stream, value); return *this; } ISerializer& BinaryInputStreamSerializer::operator()(uint64_t& value, const std::string& name) { - deserialize(stream, value); - + readVarint(stream, value); return *this; } ISerializer& BinaryInputStreamSerializer::operator()(bool& value, const std::string& name) { - deserialize(stream, value); - + value = static_cast(stream.get()); return *this; } ISerializer& BinaryInputStreamSerializer::operator()(std::string& value, const std::string& name) { uint64_t size; - serializeVarint(size, name, *this); - - std::vector temp; - temp.resize(size); - - deserialize(stream, &temp[0], size); - - value.reserve(size); - value.assign(&temp[0], size); - - return *this; -} - -ISerializer& BinaryInputStreamSerializer::operator()(char* value, std::size_t size, const std::string& name) { - stream.read(value, size); + readVarint(stream, size); + + if (size > 0) { + std::vector temp; + temp.resize(size); + checkedRead(&temp[0], size); + value.reserve(size); + value.assign(&temp[0], size); + } else { + value.clear(); + } return *this; } -ISerializer& BinaryInputStreamSerializer::tag(const std::string& name) { +ISerializer& BinaryInputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { + stream.read(static_cast(value), size); return *this; } -ISerializer& BinaryInputStreamSerializer::untagged(uint8_t& value) { - char v; - stream.get(v); - value = v; - - return *this; +ISerializer& BinaryInputStreamSerializer::binary(std::string& value, const std::string& name) { + return (*this)(value, name); } -ISerializer& BinaryInputStreamSerializer::endTag() { - return *this; -} bool BinaryInputStreamSerializer::hasObject(const std::string& name) { assert(false); //the method is not supported for this type of serialization @@ -239,4 +156,12 @@ ISerializer& BinaryInputStreamSerializer::operator()(double& value, const std::s return *this; } +void BinaryInputStreamSerializer::checkedRead(char* buf, size_t size) { + stream.read(buf, size); + if (!stream) { + throw std::runtime_error("Stream read error"); + } +} + + } diff --git a/src/serialization/BinaryInputStreamSerializer.h b/src/serialization/BinaryInputStreamSerializer.h index 50ee18e831..46707df8da 100644 --- a/src/serialization/BinaryInputStreamSerializer.h +++ b/src/serialization/BinaryInputStreamSerializer.h @@ -46,11 +46,9 @@ class BinaryInputStreamSerializer : public ISerializer { virtual ISerializer& operator()(double& value, const std::string& name) override; virtual ISerializer& operator()(bool& value, const std::string& name) override; virtual ISerializer& operator()(std::string& value, const std::string& name) override; - virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name); - virtual ISerializer& tag(const std::string& name) override; - virtual ISerializer& untagged(uint8_t& value) override; - virtual ISerializer& endTag() override; + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; + virtual ISerializer& binary(std::string& value, const std::string& name) override; virtual bool hasObject(const std::string& name) override; @@ -60,6 +58,9 @@ class BinaryInputStreamSerializer : public ISerializer { } private: + + void checkedRead(char* buf, size_t size); + std::istream& stream; }; diff --git a/src/serialization/BinaryOutputStreamSerializer.cpp b/src/serialization/BinaryOutputStreamSerializer.cpp index eba6bba375..5365c01dc2 100644 --- a/src/serialization/BinaryOutputStreamSerializer.cpp +++ b/src/serialization/BinaryOutputStreamSerializer.cpp @@ -22,58 +22,18 @@ namespace { -void serializeInteger(std::ostream& stream, uint8_t v) { - stream.put(static_cast(v)); -} - -void serializeBool(std::ostream& stream, bool v) { - serializeInteger(stream, static_cast(v)); -} - -void serializeInteger(std::ostream& stream, uint32_t v) { - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); -} - -void serializeInteger(std::ostream& stream, uint64_t v) { - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); - v >>= 8; - - stream.put(static_cast(v & 0xff)); -} - -void serializeInteger(std::ostream& stream, int64_t v) { - serializeInteger(stream, static_cast(v)); -} +template +typename std::enable_if::value && std::is_unsigned::value, void>::type +writeVarint(std::ostream& s, T i) { + while (i >= 0x80) { + s.put((static_cast(i)& 0x7f) | 0x80); + i >>= 7; + } + s.put(static_cast(i)); -void serializeData(std::ostream& stream, const char* buf, size_t len) { - stream.write(buf, len); + if (!s) { + throw std::runtime_error("Stream write error"); + } } } @@ -93,9 +53,7 @@ ISerializer& BinaryOutputStreamSerializer::endObject() { } ISerializer& BinaryOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { - uint64_t size64 = size; - serializeVarint(size64, name, *this); - size = size64; + writeVarint(stream, size); return *this; } @@ -104,68 +62,50 @@ ISerializer& BinaryOutputStreamSerializer::endArray() { } ISerializer& BinaryOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { - serializeInteger(stream, value); - + writeVarint(stream, value); return *this; } ISerializer& BinaryOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { - serializeInteger(stream, value); - + writeVarint(stream, value); return *this; } ISerializer& BinaryOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { - uint32_t v = value; - operator()(v, name); - + writeVarint(stream, static_cast(value)); return *this; } ISerializer& BinaryOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { - serializeInteger(stream, value); - + writeVarint(stream, static_cast(value)); return *this; } ISerializer& BinaryOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { - serializeInteger(stream, value); - + writeVarint(stream, value); return *this; } ISerializer& BinaryOutputStreamSerializer::operator()(bool& value, const std::string& name) { - serializeBool(stream, value); - + char boolVal = value; + checkedWrite(&boolVal, 1); return *this; } ISerializer& BinaryOutputStreamSerializer::operator()(std::string& value, const std::string& name) { - uint64_t size = value.size(); - serializeVarint(size, name, *this); - serializeData(stream, value.c_str(), value.size()); - - return *this; -} - -ISerializer& BinaryOutputStreamSerializer::operator()(char* value, std::size_t size, const std::string& name) { - serializeData(stream, value, size); - + writeVarint(stream, value.size()); + checkedWrite(value.data(), value.size()); return *this; } -ISerializer& BinaryOutputStreamSerializer::tag(const std::string& name) { +ISerializer& BinaryOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { + checkedWrite(static_cast(value), size); return *this; } -ISerializer& BinaryOutputStreamSerializer::untagged(uint8_t& value) { - stream.put(value); - - return *this; -} - -ISerializer& BinaryOutputStreamSerializer::endTag() { - return *this; +ISerializer& BinaryOutputStreamSerializer::binary(std::string& value, const std::string& name) { + // write as string (with size prefix) + return (*this)(value, name); } bool BinaryOutputStreamSerializer::hasObject(const std::string& name) { @@ -182,4 +122,11 @@ ISerializer& BinaryOutputStreamSerializer::operator()(double& value, const std:: return *this; } +void BinaryOutputStreamSerializer::checkedWrite(const char* buf, size_t size) { + stream.write(buf, size); + if (!stream) { + throw std::runtime_error("Stream write error"); + } +} + } diff --git a/src/serialization/BinaryOutputStreamSerializer.h b/src/serialization/BinaryOutputStreamSerializer.h index dc4bf80fcc..cd70464db7 100644 --- a/src/serialization/BinaryOutputStreamSerializer.h +++ b/src/serialization/BinaryOutputStreamSerializer.h @@ -45,11 +45,9 @@ class BinaryOutputStreamSerializer : public ISerializer { virtual ISerializer& operator()(double& value, const std::string& name) override; virtual ISerializer& operator()(bool& value, const std::string& name) override; virtual ISerializer& operator()(std::string& value, const std::string& name) override; - virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) override; - virtual ISerializer& tag(const std::string& name) override; - virtual ISerializer& untagged(uint8_t& value) override; - virtual ISerializer& endTag() override; + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; + virtual ISerializer& binary(std::string& value, const std::string& name) override; virtual bool hasObject(const std::string& name) override; @@ -59,6 +57,7 @@ class BinaryOutputStreamSerializer : public ISerializer { } private: + void checkedWrite(const char* buf, size_t size); std::ostream& stream; }; diff --git a/src/serialization/ISerializer.h b/src/serialization/ISerializer.h index fe83f7b065..a30a7f903c 100644 --- a/src/serialization/ISerializer.h +++ b/src/serialization/ISerializer.h @@ -46,12 +46,11 @@ class ISerializer { virtual ISerializer& operator()(uint64_t& value, const std::string& name) = 0; virtual ISerializer& operator()(double& value, const std::string& name) = 0; virtual ISerializer& operator()(bool& value, const std::string& name) = 0; - virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) = 0; virtual ISerializer& operator()(std::string& value, const std::string& name) = 0; - - virtual ISerializer& tag(const std::string& name) = 0; - virtual ISerializer& untagged(uint8_t& value) = 0; - virtual ISerializer& endTag() = 0; + + // read/write binary block + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) = 0; + virtual ISerializer& binary(std::string& value, const std::string& name) = 0; virtual bool hasObject(const std::string& name) = 0; @@ -71,65 +70,4 @@ void serialize(T& value, const std::string& name, ISerializer& serializer) { return; } -/* -template -void serialize(std::vector& value, const std::string& name); - -template -void serialize(std::unordered_map& value, const std::string& name); - - - -template -void ISerializer::serialize(std::vector& value, const std::string& name) { - std::size_t size = value.size(); - beginArray(size, name); - value.resize(size); - - for (size_t i = 0; i < size; ++i) { - serialize(value[i], ""); - } - - endArray(); - - return *this; -} - -template -void ISerializer::serialize(std::unordered_map& value, const std::string& name) { - std::size_t size; - size = value.size(); - - beginArray(size, name); - - if (type() == INPUT) { - value.reserve(size); - - for (size_t i = 0; i < size; ++i) { - K key; - V v; - beginObject(""); - serialize(key, ""); - serialize(v, ""); - endObject(); - - value[key] = v; - } - } else { - for (auto kv: value) { - K key; - key = kv.first; - beginObject(""); - serialize(key, ""); - serialize(kv.second, ""); - endObject(); - } - } - - endArray(); - - return *this; -} -*/ - } diff --git a/src/serialization/IStream.h b/src/serialization/IStream.h new file mode 100644 index 0000000000..6269c6e302 --- /dev/null +++ b/src/serialization/IStream.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace cryptonote { + +class IInputStream { +public: + virtual std::size_t read(char* data, std::size_t size) = 0; +}; + +class IOutputStream { +public: + virtual void write(const char* data, std::size_t size) = 0; +}; + +} diff --git a/src/serialization/JsonInputValueSerializer.cpp b/src/serialization/JsonInputValueSerializer.cpp index 7a1d385a48..d8f841024b 100644 --- a/src/serialization/JsonInputValueSerializer.cpp +++ b/src/serialization/JsonInputValueSerializer.cpp @@ -68,10 +68,15 @@ ISerializer& JsonInputValueSerializer::beginArray(std::size_t& size, const std:: const JsonValue* parent = chain.back(); - const JsonValue& arr = (*parent)(name); - size = arr.size(); + if (parent->count(name)) { + const JsonValue& arr = (*parent)(name); + size = arr.size(); + chain.push_back(&arr); + } else { + size = 0; + chain.push_back(0); + } - chain.push_back(&arr); idxs.push_back(0); return *this; } @@ -86,131 +91,78 @@ ISerializer& JsonInputValueSerializer::endArray() { ISerializer& JsonInputValueSerializer::operator()(uint32_t& value, const std::string& name) { assert(root); - - int64_t v = static_cast(value); - operator()(v, name); - value = static_cast(v); + value = static_cast(getNumber(name)); return *this; } ISerializer& JsonInputValueSerializer::operator()(int32_t& value, const std::string& name) { assert(root); - - int64_t v = static_cast(value); - operator()(v, name); - value = static_cast(v); + value = static_cast(getNumber(name)); return *this; } ISerializer& JsonInputValueSerializer::operator()(int64_t& value, const std::string& name) { assert(root); - - const JsonValue* val = chain.back(); - - if (val->isArray()) { - const JsonValue& v = (*val)[idxs.back()++]; - value = v.getNumber(); - } else { - const JsonValue& v = (*val)(name); - value = v.getNumber(); - } - + value = getNumber(name); return *this; } ISerializer& JsonInputValueSerializer::operator()(uint64_t& value, const std::string& name) { assert(root); - - int64_t v = static_cast(value); - operator()(v, name); - value = static_cast(v); + value = static_cast(getNumber(name)); return *this; } ISerializer& JsonInputValueSerializer::operator()(double& value, const std::string& name) { assert(root); - - const JsonValue* val = chain.back(); - - if (val->isArray()) { - value = (*val)[idxs.back()++].getDouble(); - } else { - value = (*val)(name).getDouble(); - } - + value = getValue(name).getDouble(); return *this; } ISerializer& JsonInputValueSerializer::operator()(std::string& value, const std::string& name) { assert(root); - - const JsonValue* val = chain.back(); - - if (val->isArray()) { - value = (*val)[idxs.back()++].getString(); - } else { - value = (*val)(name).getString(); - } - + value = getValue(name).getString(); return *this; } ISerializer& JsonInputValueSerializer::operator()(uint8_t& value, const std::string& name) { assert(root); - - uint64_t v = static_cast(value); - operator()(v, name); - value = static_cast(value); - + value = static_cast(getNumber(name)); return *this; } ISerializer& JsonInputValueSerializer::operator()(bool& value, const std::string& name) { assert(root); - - const JsonValue* val = chain.back(); - - if (val->isArray()) { - value = (*val)[idxs.back()++].getBool(); - } else { - value = (*val)(name).getBool(); - } - + value = getValue(name).getBool(); return *this; } bool JsonInputValueSerializer::hasObject(const std::string& name) { - const JsonValue* val = chain.back(); - - return val->count(name) != 0; + return chain.back()->count(name) != 0; } -ISerializer& JsonInputValueSerializer::operator()(char* value, std::size_t size, const std::string& name) { +ISerializer& JsonInputValueSerializer::binary(void* value, std::size_t size, const std::string& name) { assert(false); throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); return *this; } -ISerializer& JsonInputValueSerializer::tag(const std::string& name) { +ISerializer& JsonInputValueSerializer::binary(std::string& value, const std::string& name) { assert(false); throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); return *this; } -ISerializer& JsonInputValueSerializer::untagged(uint8_t& value) { - assert(false); - throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); - - return *this; +JsonValue JsonInputValueSerializer::getValue(const std::string& name) { + const JsonValue& val = *chain.back(); + return val.isArray() ? val[idxs.back()++] : val(name); } -ISerializer& JsonInputValueSerializer::endTag() { - assert(false); - throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); - - return *this; +int64_t JsonInputValueSerializer::getNumber(const std::string& name) { + return getValue(name).getNumber(); } + } diff --git a/src/serialization/JsonInputValueSerializer.h b/src/serialization/JsonInputValueSerializer.h index 04647fddb5..8bf269ce71 100644 --- a/src/serialization/JsonInputValueSerializer.h +++ b/src/serialization/JsonInputValueSerializer.h @@ -45,11 +45,9 @@ class JsonInputValueSerializer : public ISerializer { virtual ISerializer& operator()(std::string& value, const std::string& name) override; virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; virtual ISerializer& operator()(bool& value, const std::string& name) override; - virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) override; - - virtual ISerializer& tag(const std::string& name) override; - virtual ISerializer& untagged(uint8_t& value) override; - virtual ISerializer& endTag() override; + + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; + virtual ISerializer& binary(std::string& value, const std::string& name) override; virtual bool hasObject(const std::string& name) override; @@ -59,6 +57,10 @@ class JsonInputValueSerializer : public ISerializer { } private: + + JsonValue getValue(const std::string& name); + int64_t getNumber(const std::string& name); + const JsonValue* root; std::vector chain; std::vector idxs; diff --git a/src/serialization/JsonOutputStreamSerializer.cpp b/src/serialization/JsonOutputStreamSerializer.cpp index bb47029525..dfd62cd7e2 100644 --- a/src/serialization/JsonOutputStreamSerializer.cpp +++ b/src/serialization/JsonOutputStreamSerializer.cpp @@ -17,6 +17,8 @@ #include "serialization/JsonOutputStreamSerializer.h" +#include "string_tools.h" + #include #include @@ -149,32 +151,15 @@ ISerializer& JsonOutputStreamSerializer::operator()(bool& value, const std::stri return *this; } -ISerializer& JsonOutputStreamSerializer::operator()(char* value, std::size_t size, const std::string& name) { - assert(false); - throw std::runtime_error("JsonOutputStreamSerializer doesn't support \"char *\" type of serialization"); - - return *this; -} - -ISerializer& JsonOutputStreamSerializer::tag(const std::string& name) { - assert(false); - throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); - - return *this; -} - -ISerializer& JsonOutputStreamSerializer::untagged(uint8_t& value) { - assert(false); - throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); - - return *this; +ISerializer& JsonOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { + auto str = static_cast(value); + std::string tmpbuf(str, str + size); + return binary(tmpbuf, name); } -ISerializer& JsonOutputStreamSerializer::endTag() { - assert(false); - throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); - - return *this; +ISerializer& JsonOutputStreamSerializer::binary(std::string& value, const std::string& name) { + std::string hex = epee::string_tools::buff_to_hex(value); + return (*this)(hex, name); } bool JsonOutputStreamSerializer::hasObject(const std::string& name) { @@ -185,5 +170,3 @@ bool JsonOutputStreamSerializer::hasObject(const std::string& name) { } } - - diff --git a/src/serialization/JsonOutputStreamSerializer.h b/src/serialization/JsonOutputStreamSerializer.h index fd6ebff7ca..26d444f860 100644 --- a/src/serialization/JsonOutputStreamSerializer.h +++ b/src/serialization/JsonOutputStreamSerializer.h @@ -46,11 +46,9 @@ class JsonOutputStreamSerializer : public ISerializer { virtual ISerializer& operator()(std::string& value, const std::string& name) override; virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; virtual ISerializer& operator()(bool& value, const std::string& name) override; - virtual ISerializer& operator()(char* value, std::size_t size, const std::string& name) override; - - virtual ISerializer& tag(const std::string& name) override; - virtual ISerializer& untagged(uint8_t& value) override; - virtual ISerializer& endTag() override; + + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; + virtual ISerializer& binary(std::string& value, const std::string& name) override; virtual bool hasObject(const std::string& name) override; diff --git a/src/serialization/JsonValue.cpp b/src/serialization/JsonValue.cpp index c9ef17baf5..bd517679bd 100644 --- a/src/serialization/JsonValue.cpp +++ b/src/serialization/JsonValue.cpp @@ -1,612 +1,614 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "JsonValue.h" -#include -#include -#include - -namespace cryptonote { - -JsonValue::JsonValue() : d_type(NIL) { -} - -JsonValue::JsonValue(JsonValue::Type type) { - switch(type) { - case OBJECT: - new(d_valueObject)JsonValue::Object(); - break; - case ARRAY: - new(d_valueArray)JsonValue::Array(); - break; - default: - throw std::runtime_error("Wrong JsonValue type. Object or Array are possible only"); - } - - d_type = type; -} - -JsonValue::JsonValue(const JsonValue& other) : d_type(other.d_type) { - switch (d_type) { - case ARRAY: - new(d_valueArray)JsonValue::Array(*reinterpret_cast(other.d_valueArray)); - break; - case BOOL: - d_valueBool = other.d_valueBool; - break; - case INT64: - d_valueInt64 = other.d_valueInt64; - break; - case NIL: - break; - case OBJECT: - new(d_valueObject)JsonValue::Object(*reinterpret_cast(other.d_valueObject)); - break; - case DOUBLE: - d_valueDouble = other.d_valueDouble; - break; - case STRING: - new(d_valueString)std::string(*reinterpret_cast(other.d_valueString)); - break; - default: - throw(std::runtime_error("Invalid type")); - } -} - -JsonValue::~JsonValue() { - destructValue(); -} - -bool JsonValue::isArray() const { - return d_type == ARRAY; -} - -bool JsonValue::isBool() const { - return d_type == BOOL; -} - -bool JsonValue::isInt64() const { - return d_type == INT64; -} - -bool JsonValue::isNil() const { - return d_type == NIL; -} - -bool JsonValue::isObject() const { - return d_type == OBJECT; -} - -bool JsonValue::isDouble() const { - return d_type == DOUBLE; -} - -bool JsonValue::isString() const { - return d_type == STRING; -} - -bool JsonValue::getBool() const { - assert(d_type == BOOL); - if (d_type != BOOL) { - throw(std::runtime_error("Value type is not BOOL")); - } - - return d_valueBool; -} - -int64_t JsonValue::getNumber() const { - assert(d_type == INT64); - if (d_type != INT64) { - throw(std::runtime_error("Value type is not INT64")); - } - - return d_valueInt64; -} - -const JsonValue::Object& JsonValue::getObject() const { - assert(d_type == OBJECT); - if (d_type != OBJECT) { - throw(std::runtime_error("Value type is not OBJECT")); - } - - return *reinterpret_cast(d_valueObject); -} - -double JsonValue::getDouble() const { - assert(d_type == DOUBLE); - if (d_type != DOUBLE) { - throw(std::runtime_error("Value type is not DOUBLE")); - } - - return d_valueDouble; -} - -std::string JsonValue::getString() const { - assert(d_type == STRING); - if (d_type != STRING) { - throw(std::runtime_error("Value type is not STRING")); - } - - return *reinterpret_cast(d_valueString); -} - -const JsonValue& JsonValue::operator()(const std::string& name) const { - assert(d_type == OBJECT); - assert(reinterpret_cast(d_valueObject)->count(name) > 0); - if (d_type != OBJECT) { - throw(std::runtime_error("Value type is not OBJECT")); - } - - return reinterpret_cast(d_valueObject)->at(name); -} - -size_t JsonValue::count(const std::string& name) const { - assert(d_type == OBJECT); - if (d_type != OBJECT) { - throw(std::runtime_error("Value type is not OBJECT")); - } - - return reinterpret_cast(d_valueObject)->count(name); -} - -const JsonValue& JsonValue::operator[](size_t index) const { - assert(d_type == ARRAY); - if (d_type != ARRAY) { - throw(std::runtime_error("Value type is not ARRAY")); - } - - return reinterpret_cast(d_valueArray)->at(index); -} - -size_t JsonValue::size() const { - assert(d_type == ARRAY || d_type == OBJECT); - switch (d_type) { - case OBJECT: - return reinterpret_cast(d_valueString)->size(); - case ARRAY: - return reinterpret_cast(d_valueString)->size(); - default: - throw(std::runtime_error("Value type is not ARRAY or OBJECT")); - } -} - -JsonValue::Array::const_iterator JsonValue::begin() const { - assert(d_type == ARRAY); - if (d_type != ARRAY) { - throw(std::runtime_error("Value type is not ARRAY")); - } - - return reinterpret_cast(d_valueArray)->begin(); -} - -JsonValue::Array::const_iterator JsonValue::end() const { - assert(d_type == ARRAY); - if (d_type != ARRAY) { - throw(std::runtime_error("Value type is not ARRAY")); - } - - return reinterpret_cast(d_valueArray)->end(); -} - -void JsonValue::readArray(std::istream& in) { - char c; - JsonValue::Array value; - - c = in.peek(); - while (true) { - if (!isspace(in.peek())) break; - in.read(&c, 1); - } - - if (c != ']') { - for (;;) { - value.resize(value.size() + 1); - in >> value.back(); - in >> c; - while (isspace(c)) in >> c; - if (c == ']') { - break; - } - - if (c != ',') { - throw(std::runtime_error("Unable to parse")); - } - } - } - - if (d_type != JsonValue::ARRAY) { - destructValue(); - d_type = JsonValue::NIL; - new(d_valueArray)JsonValue::Array; - d_type = JsonValue::ARRAY; - } - - reinterpret_cast(d_valueArray)->swap(value); -} - -void JsonValue::readTrue(std::istream& in) { - char data[3]; - in.read(data, 3); - if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') { - throw(std::runtime_error("Unable to parse")); - } - - if (d_type != JsonValue::BOOL) { - destructValue(); - d_type = JsonValue::BOOL; - } - - d_valueBool = true; -} - -void JsonValue::readFalse(std::istream& in) { - char data[4]; - in.read(data, 4); - if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') { - throw(std::runtime_error("Unable to parse")); - } - - if (d_type != JsonValue::BOOL) { - destructValue(); - d_type = JsonValue::BOOL; - } - - d_valueBool = false; -} - -void JsonValue::readNumber(std::istream& in, char c) { - std::string text; - text += c; - size_t dots = 0; - for (;;) { - int i = in.peek(); - if (i >= '0' && i <= '9') { - in.read(&c, 1); - text += c; - } else if (i == '.') { - in.read(&c, 1); - text += '.'; - ++dots; - } else { - break; - } - } - - if (dots > 0) { - if (dots > 1) { - throw(std::runtime_error("Unable to parse")); - } - - int i = in.peek(); - if (in.peek() == 'e') { - in.read(&c, 1); - text += c; - i = in.peek(); - if (i == '+') { - in.read(&c, 1); - text += c; - i = in.peek(); - } else if (i == '-') { - in.read(&c, 1); - text += c; - i = in.peek(); - } - - if (i < '0' || i > '9') { - throw(std::runtime_error("Unable to parse")); - } - - do { - in.read(&c, 1); - text += c; - i = in.peek(); - } while (i >= '0' && i <= '9'); - } - - double value; - std::istringstream(text) >> value; - if (d_type != JsonValue::DOUBLE) { - destructValue(); - d_type = JsonValue::DOUBLE; - } - - d_valueDouble = value; - } else { - if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) { - throw(std::runtime_error("Unable to parse")); - } - - int64_t value; - std::istringstream(text) >> value; - if (d_type != JsonValue::INT64) { - destructValue(); - d_type = JsonValue::INT64; - } - - d_valueInt64 = value; - } -} - -void JsonValue::readNull(std::istream& in) { - char data[3]; - in.read(data, 3); - if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') { - throw(std::runtime_error("Unable to parse")); - } - - if (d_type != JsonValue::NIL) { - destructValue(); - d_type = JsonValue::NIL; - } -} - -void JsonValue::readObject(std::istream& in) { - char c; - JsonValue::Object value; - in >> c; - while (isspace(c)) in >> c; - - if (c != '}') { - std::string name; - for (;;) { - if (c != '"') { - throw(std::runtime_error("Unable to parse")); - } - - name.clear(); - for (;;) { - in >> c; - if (c == '"') { - break; - } - - if (c == '\\') { - name += c; - in >> c; - } - - name += c; - } - - in >> c; - while (isspace(c)) in >> c; - if (c != ':') { - throw(std::runtime_error("Unable to parse")); - } - - in >> value[name]; - in >> c; - while (isspace(c)) in >> c; - if (c == '}') { - break; - } - - if (c != ',') { - throw(std::runtime_error("Unable to parse")); - } - in >> c; - while (isspace(c)) in >> c; - } - } - - if (d_type != JsonValue::OBJECT) { - destructValue(); - d_type = JsonValue::NIL; - new(d_valueObject)JsonValue::Object; - d_type = JsonValue::OBJECT; - } - - reinterpret_cast(d_valueObject)->swap(value); -} - -void JsonValue::readString(std::istream& in) { - char c; - std::string value; - - for (;;) { - in.read(&c, 1); - if (c == '"') { - break; - } - - if (c == '\\') { - value += c; - in >> c; - } - - value += c; - } - - if (d_type != JsonValue::STRING) { - destructValue(); - d_type = JsonValue::NIL; - new(d_valueString)std::string; - d_type = JsonValue::STRING; - } - - reinterpret_cast(d_valueString)->swap(value); -} - -std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { - char c; - in >> c; - while (isspace(c)) in >> c; - if (c == '[') { - jsonValue.readArray(in); - } else if (c == 't') { - jsonValue.readTrue(in); - } else if (c == 'f') { - jsonValue.readFalse(in); - } else if ((c == '-') || (c >= '0' && c <= '9')) { - jsonValue.readNumber(in, c); - } else if (c == 'n') { - jsonValue.readNull(in); - } else if (c == '{') { - jsonValue.readObject(in); - } else if (c == '"') { - jsonValue.readString(in); - } else { - throw(std::runtime_error("Unable to parse")); - } - - return in; -} - -std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { - if (jsonValue.d_type == JsonValue::ARRAY) { - const JsonValue::Array& array = *reinterpret_cast(jsonValue.d_valueArray); - out << '['; - if (array.size() > 0) { - out << array[0]; - for (size_t i = 1; i < array.size(); ++i) { - out << ',' << array[i]; - } - } - - out << ']'; - } else if (jsonValue.d_type == JsonValue::BOOL) { - out << (jsonValue.d_valueBool ? "true" : "false"); - } else if (jsonValue.d_type == JsonValue::INT64) { - out << jsonValue.d_valueInt64; - } else if (jsonValue.d_type == JsonValue::NIL) { - out << "null"; - } else if (jsonValue.d_type == JsonValue::OBJECT) { - const JsonValue::Object& object = *reinterpret_cast(jsonValue.d_valueObject); - out << '{'; - auto iter = object.begin(); - if (iter != object.end()) { - out << '"' << iter->first << "\":" << iter->second; - ++iter; - for (; iter != object.end(); ++iter) { - out << ",\"" << iter->first << "\":" << iter->second; - } - } - - out << '}'; - } else if (jsonValue.d_type == JsonValue::DOUBLE) { - std::ostringstream stream; - stream << std::fixed << std::setprecision(11) << jsonValue.d_valueDouble; - std::string value = stream.str(); - while (value.size() > 1 && value[value.size() - 2] != '.' && value[value.size() - 1] == '0') { - value.resize(value.size() - 1); - } - - out << value; - } else if (jsonValue.d_type == JsonValue::STRING) { - out << '"' << *reinterpret_cast(jsonValue.d_valueString) << '"'; - } else { - throw(std::runtime_error("Invalid type")); - } - - return out; -} - -void JsonValue::destructValue() { - switch (d_type) { - case ARRAY: - reinterpret_cast(d_valueArray)->~Array(); - break; - case OBJECT: - reinterpret_cast(d_valueObject)->~Object(); - break; - case STRING: - reinterpret_cast(d_valueString)->~basic_string(); - break; - default: - break; - } -} - -JsonValue& JsonValue::pushBack(const JsonValue& val) { - if (d_type != ARRAY) { - throw std::runtime_error("JsonValue error. pushBack is only possible for arrays"); - } - - Array* array = reinterpret_cast(d_valueArray); - array->push_back(val); - - return array->back(); -} - -JsonValue& JsonValue::insert(const std::string key, const JsonValue& value) { - if (d_type != OBJECT) { - throw std::runtime_error("JsonValue error. insert is only possible for objects"); - } - - Object* obj = reinterpret_cast(d_valueObject); - - auto res = obj->insert(std::make_pair(key, value)); - return res.first->second; -} - -JsonValue& JsonValue::operator=(bool value) { - if (d_type != BOOL) { - destructValue(); - d_type = BOOL; - } - - d_valueBool = value; - - return *this; -} - -JsonValue& JsonValue::operator=(int64_t value) { - if (d_type != INT64) { - destructValue(); - d_type = INT64; - } - - d_valueInt64 = value; - - return *this; -} - -//JsonValue& JsonValue::operator=(NilType value) { -// if (d_type != NIL) { -// destructValue(); -// d_type = NIL; -// } -//} - -JsonValue& JsonValue::operator=(double value) { - if (d_type != DOUBLE) { - destructValue(); - d_type = DOUBLE; - } - - d_valueDouble = value; - - return *this; -} - -JsonValue& JsonValue::operator=(const std::string& value) { - if (d_type != STRING) { - destructValue(); - new(d_valueString)std::string; - d_type = STRING; - } - - reinterpret_cast(d_valueString)->assign(value.data(), value.size()); - - return *this; -} - -JsonValue& JsonValue::operator=(const char* value) { - return operator=(std::string(value)); -} - -} //namespace cryptonote +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonValue.h" +#include +#include +#include + +namespace cryptonote { + +JsonValue::JsonValue() : d_type(NIL) { +} + +JsonValue::JsonValue(JsonValue::Type type) { + switch(type) { + case OBJECT: + new(d_valueObject)JsonValue::Object(); + break; + case ARRAY: + new(d_valueArray)JsonValue::Array(); + break; + default: + throw std::runtime_error("Wrong JsonValue type. Object or Array are possible only"); + } + + d_type = type; +} + +JsonValue::JsonValue(const JsonValue& other) : d_type(other.d_type) { + switch (d_type) { + case ARRAY: + new(d_valueArray)JsonValue::Array(*reinterpret_cast(other.d_valueArray)); + break; + case BOOL: + d_valueBool = other.d_valueBool; + break; + case INT64: + d_valueInt64 = other.d_valueInt64; + break; + case NIL: + break; + case OBJECT: + new(d_valueObject)JsonValue::Object(*reinterpret_cast(other.d_valueObject)); + break; + case DOUBLE: + d_valueDouble = other.d_valueDouble; + break; + case STRING: + new(d_valueString)std::string(*reinterpret_cast(other.d_valueString)); + break; + default: + throw(std::runtime_error("Invalid type")); + } +} + +JsonValue::~JsonValue() { + destructValue(); +} + +bool JsonValue::isArray() const { + return d_type == ARRAY; +} + +bool JsonValue::isBool() const { + return d_type == BOOL; +} + +bool JsonValue::isInt64() const { + return d_type == INT64; +} + +bool JsonValue::isNil() const { + return d_type == NIL; +} + +bool JsonValue::isObject() const { + return d_type == OBJECT; +} + +bool JsonValue::isDouble() const { + return d_type == DOUBLE; +} + +bool JsonValue::isString() const { + return d_type == STRING; +} + +bool JsonValue::getBool() const { + assert(d_type == BOOL); + if (d_type != BOOL) { + throw(std::runtime_error("Value type is not BOOL")); + } + + return d_valueBool; +} + +int64_t JsonValue::getNumber() const { + assert(d_type == INT64); + if (d_type != INT64) { + throw(std::runtime_error("Value type is not INT64")); + } + + return d_valueInt64; +} + +const JsonValue::Object& JsonValue::getObject() const { + assert(d_type == OBJECT); + if (d_type != OBJECT) { + throw(std::runtime_error("Value type is not OBJECT")); + } + + return *reinterpret_cast(d_valueObject); +} + +double JsonValue::getDouble() const { + assert(d_type == DOUBLE); + if (d_type != DOUBLE) { + throw(std::runtime_error("Value type is not DOUBLE")); + } + + return d_valueDouble; +} + +std::string JsonValue::getString() const { + assert(d_type == STRING); + if (d_type != STRING) { + throw(std::runtime_error("Value type is not STRING")); + } + + return *reinterpret_cast(d_valueString); +} + +const JsonValue& JsonValue::operator()(const std::string& name) const { + assert(d_type == OBJECT); + assert(reinterpret_cast(d_valueObject)->count(name) > 0); + if (d_type != OBJECT) { + throw(std::runtime_error("Value type is not OBJECT")); + } + + return reinterpret_cast(d_valueObject)->at(name); +} + +size_t JsonValue::count(const std::string& name) const { + assert(d_type == OBJECT); + if (d_type != OBJECT) { + throw(std::runtime_error("Value type is not OBJECT")); + } + + return reinterpret_cast(d_valueObject)->count(name); +} + +const JsonValue& JsonValue::operator[](size_t index) const { + assert(d_type == ARRAY); + if (d_type != ARRAY) { + throw(std::runtime_error("Value type is not ARRAY")); + } + + return reinterpret_cast(d_valueArray)->at(index); +} + +size_t JsonValue::size() const { + assert(d_type == ARRAY || d_type == OBJECT); + switch (d_type) { + case OBJECT: + return reinterpret_cast(d_valueString)->size(); + case ARRAY: + return reinterpret_cast(d_valueString)->size(); + default: + throw(std::runtime_error("Value type is not ARRAY or OBJECT")); + } +} + +JsonValue::Array::const_iterator JsonValue::begin() const { + assert(d_type == ARRAY); + if (d_type != ARRAY) { + throw(std::runtime_error("Value type is not ARRAY")); + } + + return reinterpret_cast(d_valueArray)->begin(); +} + +JsonValue::Array::const_iterator JsonValue::end() const { + assert(d_type == ARRAY); + if (d_type != ARRAY) { + throw(std::runtime_error("Value type is not ARRAY")); + } + + return reinterpret_cast(d_valueArray)->end(); +} + +void JsonValue::readArray(std::istream& in) { + char c; + JsonValue::Array value; + + c = in.peek(); + while (true) { + if (!isspace(in.peek())) break; + in.read(&c, 1); + } + + if (in.peek() != ']') { + for (;;) { + value.resize(value.size() + 1); + in >> value.back(); + in >> c; + while (isspace(c)) in >> c; + if (c == ']') { + break; + } + + if (c != ',') { + throw(std::runtime_error("Unable to parse")); + } + } + } else { + in.read(&c, 1); + } + + if (d_type != JsonValue::ARRAY) { + destructValue(); + d_type = JsonValue::NIL; + new(d_valueArray)JsonValue::Array; + d_type = JsonValue::ARRAY; + } + + reinterpret_cast(d_valueArray)->swap(value); +} + +void JsonValue::readTrue(std::istream& in) { + char data[3]; + in.read(data, 3); + if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') { + throw(std::runtime_error("Unable to parse")); + } + + if (d_type != JsonValue::BOOL) { + destructValue(); + d_type = JsonValue::BOOL; + } + + d_valueBool = true; +} + +void JsonValue::readFalse(std::istream& in) { + char data[4]; + in.read(data, 4); + if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') { + throw(std::runtime_error("Unable to parse")); + } + + if (d_type != JsonValue::BOOL) { + destructValue(); + d_type = JsonValue::BOOL; + } + + d_valueBool = false; +} + +void JsonValue::readNumber(std::istream& in, char c) { + std::string text; + text += c; + size_t dots = 0; + for (;;) { + int i = in.peek(); + if (i >= '0' && i <= '9') { + in.read(&c, 1); + text += c; + } else if (i == '.') { + in.read(&c, 1); + text += '.'; + ++dots; + } else { + break; + } + } + + if (dots > 0) { + if (dots > 1) { + throw(std::runtime_error("Unable to parse")); + } + + int i = in.peek(); + if (in.peek() == 'e') { + in.read(&c, 1); + text += c; + i = in.peek(); + if (i == '+') { + in.read(&c, 1); + text += c; + i = in.peek(); + } else if (i == '-') { + in.read(&c, 1); + text += c; + i = in.peek(); + } + + if (i < '0' || i > '9') { + throw(std::runtime_error("Unable to parse")); + } + + do { + in.read(&c, 1); + text += c; + i = in.peek(); + } while (i >= '0' && i <= '9'); + } + + double value; + std::istringstream(text) >> value; + if (d_type != JsonValue::DOUBLE) { + destructValue(); + d_type = JsonValue::DOUBLE; + } + + d_valueDouble = value; + } else { + if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) { + throw(std::runtime_error("Unable to parse")); + } + + int64_t value; + std::istringstream(text) >> value; + if (d_type != JsonValue::INT64) { + destructValue(); + d_type = JsonValue::INT64; + } + + d_valueInt64 = value; + } +} + +void JsonValue::readNull(std::istream& in) { + char data[3]; + in.read(data, 3); + if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') { + throw(std::runtime_error("Unable to parse")); + } + + if (d_type != JsonValue::NIL) { + destructValue(); + d_type = JsonValue::NIL; + } +} + +void JsonValue::readObject(std::istream& in) { + char c; + JsonValue::Object value; + in >> c; + while (isspace(c)) in >> c; + + if (c != '}') { + std::string name; + for (;;) { + if (c != '"') { + throw(std::runtime_error("Unable to parse")); + } + + name.clear(); + for (;;) { + in >> c; + if (c == '"') { + break; + } + + if (c == '\\') { + name += c; + in >> c; + } + + name += c; + } + + in >> c; + while (isspace(c)) in >> c; + if (c != ':') { + throw(std::runtime_error("Unable to parse")); + } + + in >> value[name]; + in >> c; + while (isspace(c)) in >> c; + if (c == '}') { + break; + } + + if (c != ',') { + throw(std::runtime_error("Unable to parse")); + } + in >> c; + while (isspace(c)) in >> c; + } + } + + if (d_type != JsonValue::OBJECT) { + destructValue(); + d_type = JsonValue::NIL; + new(d_valueObject)JsonValue::Object; + d_type = JsonValue::OBJECT; + } + + reinterpret_cast(d_valueObject)->swap(value); +} + +void JsonValue::readString(std::istream& in) { + char c; + std::string value; + + for (;;) { + in.read(&c, 1); + if (c == '"') { + break; + } + + if (c == '\\') { + value += c; + in >> c; + } + + value += c; + } + + if (d_type != JsonValue::STRING) { + destructValue(); + d_type = JsonValue::NIL; + new(d_valueString)std::string; + d_type = JsonValue::STRING; + } + + reinterpret_cast(d_valueString)->swap(value); +} + +std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { + char c; + in >> c; + while (isspace(c)) in >> c; + if (c == '[') { + jsonValue.readArray(in); + } else if (c == 't') { + jsonValue.readTrue(in); + } else if (c == 'f') { + jsonValue.readFalse(in); + } else if ((c == '-') || (c >= '0' && c <= '9')) { + jsonValue.readNumber(in, c); + } else if (c == 'n') { + jsonValue.readNull(in); + } else if (c == '{') { + jsonValue.readObject(in); + } else if (c == '"') { + jsonValue.readString(in); + } else { + throw(std::runtime_error("Unable to parse")); + } + + return in; +} + +std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { + if (jsonValue.d_type == JsonValue::ARRAY) { + const JsonValue::Array& array = *reinterpret_cast(jsonValue.d_valueArray); + out << '['; + if (array.size() > 0) { + out << array[0]; + for (size_t i = 1; i < array.size(); ++i) { + out << ',' << array[i]; + } + } + + out << ']'; + } else if (jsonValue.d_type == JsonValue::BOOL) { + out << (jsonValue.d_valueBool ? "true" : "false"); + } else if (jsonValue.d_type == JsonValue::INT64) { + out << jsonValue.d_valueInt64; + } else if (jsonValue.d_type == JsonValue::NIL) { + out << "null"; + } else if (jsonValue.d_type == JsonValue::OBJECT) { + const JsonValue::Object& object = *reinterpret_cast(jsonValue.d_valueObject); + out << '{'; + auto iter = object.begin(); + if (iter != object.end()) { + out << '"' << iter->first << "\":" << iter->second; + ++iter; + for (; iter != object.end(); ++iter) { + out << ",\"" << iter->first << "\":" << iter->second; + } + } + + out << '}'; + } else if (jsonValue.d_type == JsonValue::DOUBLE) { + std::ostringstream stream; + stream << std::fixed << std::setprecision(11) << jsonValue.d_valueDouble; + std::string value = stream.str(); + while (value.size() > 1 && value[value.size() - 2] != '.' && value[value.size() - 1] == '0') { + value.resize(value.size() - 1); + } + + out << value; + } else if (jsonValue.d_type == JsonValue::STRING) { + out << '"' << *reinterpret_cast(jsonValue.d_valueString) << '"'; + } else { + throw(std::runtime_error("Invalid type")); + } + + return out; +} + +void JsonValue::destructValue() { + switch (d_type) { + case ARRAY: + reinterpret_cast(d_valueArray)->~Array(); + break; + case OBJECT: + reinterpret_cast(d_valueObject)->~Object(); + break; + case STRING: + reinterpret_cast(d_valueString)->~basic_string(); + break; + default: + break; + } +} + +JsonValue& JsonValue::pushBack(const JsonValue& val) { + if (d_type != ARRAY) { + throw std::runtime_error("JsonValue error. pushBack is only possible for arrays"); + } + + Array* array = reinterpret_cast(d_valueArray); + array->push_back(val); + + return array->back(); +} + +JsonValue& JsonValue::insert(const std::string& key, const JsonValue& value) { + if (d_type != OBJECT) { + throw std::runtime_error("JsonValue error. insert is only possible for objects"); + } + + Object* obj = reinterpret_cast(d_valueObject); + + auto res = obj->insert(std::make_pair(key, value)); + return res.first->second; +} + +JsonValue& JsonValue::operator=(bool value) { + if (d_type != BOOL) { + destructValue(); + d_type = BOOL; + } + + d_valueBool = value; + + return *this; +} + +JsonValue& JsonValue::operator=(int64_t value) { + if (d_type != INT64) { + destructValue(); + d_type = INT64; + } + + d_valueInt64 = value; + + return *this; +} + +//JsonValue& JsonValue::operator=(NilType value) { +// if (d_type != NIL) { +// destructValue(); +// d_type = NIL; +// } +//} + +JsonValue& JsonValue::operator=(double value) { + if (d_type != DOUBLE) { + destructValue(); + d_type = DOUBLE; + } + + d_valueDouble = value; + + return *this; +} + +JsonValue& JsonValue::operator=(const std::string& value) { + if (d_type != STRING) { + destructValue(); + new(d_valueString)std::string; + d_type = STRING; + } + + reinterpret_cast(d_valueString)->assign(value.data(), value.size()); + + return *this; +} + +JsonValue& JsonValue::operator=(const char* value) { + return operator=(std::string(value)); +} + +} //namespace cryptonote diff --git a/src/serialization/JsonValue.h b/src/serialization/JsonValue.h index 53a82f820a..b44c86a62a 100644 --- a/src/serialization/JsonValue.h +++ b/src/serialization/JsonValue.h @@ -1,103 +1,103 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include - -#include - -namespace cryptonote { - -class JsonValue { -public: - typedef std::vector Array; - typedef std::map Object; - - enum Type { - ARRAY, - BOOL, - INT64, - NIL, - OBJECT, - DOUBLE, - STRING - }; - - JsonValue(); - JsonValue(Type type); - JsonValue(const JsonValue& other); - ~JsonValue(); - JsonValue& operator=(const JsonValue& other) = delete; - bool isArray() const; - bool isBool() const; - bool isInt64() const; - bool isNil() const; - bool isObject() const; - bool isDouble() const; - bool isString() const; - bool getBool() const; - int64_t getNumber() const; - const Object& getObject() const; - double getDouble() const; - std::string getString() const; - const JsonValue& operator()(const std::string& name) const; - size_t count(const std::string& name) const; - const JsonValue& operator[](size_t index) const; - size_t size() const; - Array::const_iterator begin() const; - Array::const_iterator end() const; - - JsonValue& pushBack(const JsonValue& val); - JsonValue& insert(const std::string key, const JsonValue& value); - - JsonValue& operator=(bool value); - JsonValue& operator=(int64_t value); -// JsonValue& operator=(NilType value); - JsonValue& operator=(double value); - JsonValue& operator=(const std::string& value); - JsonValue& operator=(const char* value); - - - friend std::istream& operator>>(std::istream& in, JsonValue& jsonValue); - friend std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue); - -private: - size_t d_type; - union { - uint8_t d_valueArray[sizeof(Array)]; - bool d_valueBool; - int64_t d_valueInt64; - uint8_t d_valueObject[sizeof(Object)]; - double d_valueDouble; - uint8_t d_valueString[sizeof(std::string)]; - }; - - void destructValue(); - - void readArray(std::istream& in); - void readTrue(std::istream& in); - void readFalse(std::istream& in); - void readNumber(std::istream& in, char c); - void readNull(std::istream& in); - void readObject(std::istream& in); - void readString(std::istream& in); -}; - -} //namespace cryptonote +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include + +namespace cryptonote { + +class JsonValue { +public: + typedef std::vector Array; + typedef std::map Object; + + enum Type { + ARRAY, + BOOL, + INT64, + NIL, + OBJECT, + DOUBLE, + STRING + }; + + JsonValue(); + JsonValue(Type type); + JsonValue(const JsonValue& other); + ~JsonValue(); + JsonValue& operator=(const JsonValue& other) = delete; + bool isArray() const; + bool isBool() const; + bool isInt64() const; + bool isNil() const; + bool isObject() const; + bool isDouble() const; + bool isString() const; + bool getBool() const; + int64_t getNumber() const; + const Object& getObject() const; + double getDouble() const; + std::string getString() const; + const JsonValue& operator()(const std::string& name) const; + size_t count(const std::string& name) const; + const JsonValue& operator[](size_t index) const; + size_t size() const; + Array::const_iterator begin() const; + Array::const_iterator end() const; + + JsonValue& pushBack(const JsonValue& val); + JsonValue& insert(const std::string& key, const JsonValue& value); + + JsonValue& operator=(bool value); + JsonValue& operator=(int64_t value); +// JsonValue& operator=(NilType value); + JsonValue& operator=(double value); + JsonValue& operator=(const std::string& value); + JsonValue& operator=(const char* value); + + + friend std::istream& operator>>(std::istream& in, JsonValue& jsonValue); + friend std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue); + +private: + size_t d_type; + union { + uint8_t d_valueArray[sizeof(Array)]; + bool d_valueBool; + int64_t d_valueInt64; + uint8_t d_valueObject[sizeof(Object)]; + double d_valueDouble; + uint8_t d_valueString[sizeof(std::string)]; + }; + + void destructValue(); + + void readArray(std::istream& in); + void readTrue(std::istream& in); + void readFalse(std::istream& in); + void readNumber(std::istream& in, char c); + void readNull(std::istream& in); + void readObject(std::istream& in); + void readString(std::istream& in); +}; + +} //namespace cryptonote diff --git a/src/serialization/KVBinaryCommon.h b/src/serialization/KVBinaryCommon.h new file mode 100644 index 0000000000..21b1bcbb05 --- /dev/null +++ b/src/serialization/KVBinaryCommon.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace CryptoNote { + +const uint32_t PORTABLE_STORAGE_SIGNATUREA = 0x01011101; +const uint32_t PORTABLE_STORAGE_SIGNATUREB = 0x01020101; // bender's nightmare +const uint8_t PORTABLE_STORAGE_FORMAT_VER = 1; + +const uint8_t PORTABLE_RAW_SIZE_MARK_MASK = 0x03; +const uint8_t PORTABLE_RAW_SIZE_MARK_BYTE = 0; +const uint8_t PORTABLE_RAW_SIZE_MARK_WORD = 1; +const uint8_t PORTABLE_RAW_SIZE_MARK_DWORD = 2; +const uint8_t PORTABLE_RAW_SIZE_MARK_INT64 = 3; + +#ifndef MAX_STRING_LEN_POSSIBLE +#define MAX_STRING_LEN_POSSIBLE 2000000000 //do not let string be so big +#endif + +//data types + +const uint8_t BIN_KV_SERIALIZE_TYPE_INT64 = 1; +const uint8_t BIN_KV_SERIALIZE_TYPE_INT32 = 2; +const uint8_t BIN_KV_SERIALIZE_TYPE_INT16 = 3; +const uint8_t BIN_KV_SERIALIZE_TYPE_INT8 = 4; +const uint8_t BIN_KV_SERIALIZE_TYPE_UINT64 = 5; +const uint8_t BIN_KV_SERIALIZE_TYPE_UINT32 = 6; +const uint8_t BIN_KV_SERIALIZE_TYPE_UINT16 = 7; +const uint8_t BIN_KV_SERIALIZE_TYPE_UINT8 = 8; +const uint8_t BIN_KV_SERIALIZE_TYPE_DOUBLE = 9; +const uint8_t BIN_KV_SERIALIZE_TYPE_STRING = 10; +const uint8_t BIN_KV_SERIALIZE_TYPE_BOOL = 11; +const uint8_t BIN_KV_SERIALIZE_TYPE_OBJECT = 12; +const uint8_t BIN_KV_SERIALIZE_TYPE_ARRAY = 13; +const uint8_t BIN_KV_SERIALIZE_FLAG_ARRAY = 0x80; + +#pragma pack(push) +#pragma pack(1) +struct KVBinaryStorageBlockHeader +{ + uint32_t m_signature_a; + uint32_t m_signature_b; + uint8_t m_ver; +}; +#pragma pack(pop) + + +} diff --git a/src/serialization/KVBinaryInputStreamSerializer.cpp b/src/serialization/KVBinaryInputStreamSerializer.cpp new file mode 100644 index 0000000000..6dd24ce1ea --- /dev/null +++ b/src/serialization/KVBinaryInputStreamSerializer.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "KVBinaryInputStreamSerializer.h" +#include "KVBinaryCommon.h" + +#include "JsonValue.h" + +#include +#include +#include +#include + +using namespace CryptoNote; +using namespace cryptonote; + +namespace { + +template +T readPod(std::istream& s) { + T v; + s.read(reinterpret_cast(&v), sizeof(T)); + return v; +} + +template +cryptonote::JsonValue readPodJson(std::istream& s) { + T v; + s.read(reinterpret_cast(&v), sizeof(T)); + cryptonote::JsonValue jv; + jv = static_cast(v); + return jv; +} + +template +cryptonote::JsonValue readIntegerJson(std::istream& s) { + return readPodJson(s); +} + +size_t readVarint(std::istream& s) { + size_t v = 0; + uint8_t size_mask = uint8_t(s.peek()) & PORTABLE_RAW_SIZE_MARK_MASK; + + switch (size_mask) { + case PORTABLE_RAW_SIZE_MARK_BYTE: + v = readPod(s); + break; + case PORTABLE_RAW_SIZE_MARK_WORD: + v = readPod(s); + break; + case PORTABLE_RAW_SIZE_MARK_DWORD: + v = readPod(s); + break; + case PORTABLE_RAW_SIZE_MARK_INT64: + v = readPod(s); + break; + default: + throw std::runtime_error("unknown varint size_mask"); + } + + v >>= 2; + return v; +} + +std::string readString(std::istream& s) { + auto size = readVarint(s); + std::string str; + str.resize(size); + s.read(&str[0], size); + return str; +} + +JsonValue readStringJson(std::istream& s) { + JsonValue js; + js = readString(s); + return js; +} + +void readName(std::istream& s, std::string& name) { + uint8_t len = readPod(s); + name.resize(len); + s.read(&name[0], len); +} + +} + + +namespace cryptonote { + +void KVBinaryInputStreamSerializer::parse() { + auto hdr = readPod(stream); + + if ( + hdr.m_signature_a != PORTABLE_STORAGE_SIGNATUREA || + hdr.m_signature_b != PORTABLE_STORAGE_SIGNATUREB) { + throw std::runtime_error("Invalid binary storage signature"); + } + + if (hdr.m_ver != PORTABLE_STORAGE_FORMAT_VER) { + throw std::runtime_error("Unknown binary storage format version"); + } + + root.reset(new JsonValue(loadSection())); + setJsonValue(root.get()); +} + +ISerializer& KVBinaryInputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { + std::string str; + + (*this)(str, name); + + if (str.size() != size) { + throw std::runtime_error("Binary block size mismatch"); + } + + memcpy(value, str.data(), size); + return *this; +} + +ISerializer& KVBinaryInputStreamSerializer::binary(std::string& value, const std::string& name) { + if (!hasObject(name)) { + value.clear(); + return *this; + } + + return (*this)(value, name); // load as string +} + +JsonValue KVBinaryInputStreamSerializer::loadSection() { + JsonValue sec(JsonValue::OBJECT); + size_t count = readVarint(stream); + std::string name; + + while (count--) { + readName(stream, name); + sec.insert(name, loadEntry()); + } + + return sec; +} + +JsonValue KVBinaryInputStreamSerializer::loadValue(uint8_t type) { + switch (type) { + case BIN_KV_SERIALIZE_TYPE_INT64: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_INT32: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_INT16: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_INT8: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_UINT64: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_UINT32: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_UINT16: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_UINT8: return readIntegerJson(stream); + case BIN_KV_SERIALIZE_TYPE_DOUBLE: return readPodJson(stream); + case BIN_KV_SERIALIZE_TYPE_BOOL: return readPodJson(stream); + case BIN_KV_SERIALIZE_TYPE_STRING: return readStringJson(stream); + case BIN_KV_SERIALIZE_TYPE_OBJECT: return loadSection(); + case BIN_KV_SERIALIZE_TYPE_ARRAY: return loadArray(type); + default: + throw std::runtime_error("Unknown data type"); + break; + } +} + +JsonValue KVBinaryInputStreamSerializer::loadEntry() { + uint8_t type = readPod(stream); + + if (type & BIN_KV_SERIALIZE_FLAG_ARRAY) { + type &= ~BIN_KV_SERIALIZE_FLAG_ARRAY; + return loadArray(type); + } + + return loadValue(type); +} + +JsonValue KVBinaryInputStreamSerializer::loadArray(uint8_t itemType) { + JsonValue arr(JsonValue::ARRAY); + size_t count = readVarint(stream); + + while (count--) { + arr.pushBack(loadValue(itemType)); + } + + return arr; +} + + +} diff --git a/src/serialization/KVBinaryInputStreamSerializer.h b/src/serialization/KVBinaryInputStreamSerializer.h new file mode 100644 index 0000000000..41d83c34c5 --- /dev/null +++ b/src/serialization/KVBinaryInputStreamSerializer.h @@ -0,0 +1,52 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" +#include "SerializationOverloads.h" + +#include "JsonValue.h" +#include "JsonInputValueSerializer.h" + +#include +#include + +namespace cryptonote { + +class KVBinaryInputStreamSerializer : public JsonInputValueSerializer { +public: + KVBinaryInputStreamSerializer(std::istream& strm) : stream(strm) {} + virtual ~KVBinaryInputStreamSerializer() {} + + void parse(); + + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; + virtual ISerializer& binary(std::string& value, const std::string& name) override; + +private: + + JsonValue loadSection(); + JsonValue loadEntry(); + JsonValue loadValue(uint8_t type); + JsonValue loadArray(uint8_t itemType); + + std::unique_ptr root; + std::istream& stream; +}; + +} diff --git a/src/serialization/KVBinaryOutputStreamSerializer.cpp b/src/serialization/KVBinaryOutputStreamSerializer.cpp new file mode 100644 index 0000000000..c275661b4d --- /dev/null +++ b/src/serialization/KVBinaryOutputStreamSerializer.cpp @@ -0,0 +1,252 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "KVBinaryOutputStreamSerializer.h" +#include "KVBinaryCommon.h" + +#include +#include + +using namespace CryptoNote; +using namespace cryptonote; + +namespace { + +template +void writePod(IOutputStream& s, const T& value) { + s.write((const char*)&value, sizeof(T)); +} + +template +size_t packVarint(IOutputStream& s, uint8_t type_or, size_t pv) { + T v = static_cast(pv << 2); + v |= type_or; + s.write((const char*)&v, sizeof(T)); + return sizeof(T); +} + +void writeElementName(IOutputStream& s, const std::string& name) { + if (name.size() > std::numeric_limits::max()) { + throw std::runtime_error("Element name is too long"); + } + uint8_t len = static_cast(name.size()); + s.write((const char*)&len, sizeof(len)); + s.write(name.data(), len); +} + +size_t writeArraySize(IOutputStream& s, size_t val) { + if (val <= 63) { + return packVarint(s, PORTABLE_RAW_SIZE_MARK_BYTE, val); + } else if (val <= 16383) { + return packVarint(s, PORTABLE_RAW_SIZE_MARK_WORD, val); + } else if (val <= 1073741823) { + return packVarint(s, PORTABLE_RAW_SIZE_MARK_DWORD, val); + } else { + if (val > 4611686018427387903) { + throw std::runtime_error("failed to pack varint - too big amount"); + } + return packVarint(s, PORTABLE_RAW_SIZE_MARK_INT64, val); + } +} + +} + +namespace cryptonote { + +using namespace CryptoNote; + + + + +KVBinaryOutputStreamSerializer::KVBinaryOutputStreamSerializer() { + beginObject(std::string()); +} + +void KVBinaryOutputStreamSerializer::write(std::ostream& target) { + + assert(m_objectsStack.size() == 1); + assert(m_stack.size() == 1); + + KVBinaryStorageBlockHeader hdr; + hdr.m_signature_a = PORTABLE_STORAGE_SIGNATUREA; + hdr.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; + hdr.m_ver = PORTABLE_STORAGE_FORMAT_VER; + + target.write(reinterpret_cast(&hdr), sizeof(hdr)); + target.write(stream().data(), stream().size()); +} + +ISerializer::SerializerType KVBinaryOutputStreamSerializer::type() const { + return ISerializer::OUTPUT; +} + +ISerializer& KVBinaryOutputStreamSerializer::beginObject(const std::string& name) { + checkArrayPreamble(BIN_KV_SERIALIZE_TYPE_OBJECT); + + m_stack.push_back(Level(name)); + m_objectsStack.push_back(MemoryStream()); + + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::endObject() { + assert(m_objectsStack.size()); + + auto level = std::move(m_stack.back()); + m_stack.pop_back(); + + auto objStream = std::move(m_objectsStack.back()); + m_objectsStack.pop_back(); + + auto& out = stream(); + + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_OBJECT, level.name); + + writeArraySize(out, level.count); + out.write(objStream.data(), objStream.size()); + + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { + m_stack.push_back(Level(name, size)); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::endArray() { + bool validArray = m_stack.back().state == State::Array; + m_stack.pop_back(); + + if (m_stack.back().state == State::Object && validArray) { + ++m_stack.back().count; + } + + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT8, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT32, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_INT32, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_INT64, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT64, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(bool& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_BOOL, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(double& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_DOUBLE, name); + writePod(stream(), value); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::operator()(std::string& value, const std::string& name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_STRING, name); + + auto& out = stream(); + writeArraySize(out, value.size()); + out.write(value.data(), value.size()); + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { + if (size > 0) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_STRING, name); + auto& out = stream(); + writeArraySize(out, size); + out.write(static_cast(value), size); + } + return *this; +} + +ISerializer& KVBinaryOutputStreamSerializer::binary(std::string& value, const std::string& name) { + return binary(const_cast(value.data()), value.size(), name); +} + +bool KVBinaryOutputStreamSerializer::hasObject(const std::string& name) { + assert(false); //the method is not supported for this type of serialization + throw std::runtime_error("hasObject method is not supported in KVBinaryOutputStreamSerializer"); + + return false; +} + +void KVBinaryOutputStreamSerializer::writeElementPrefix(uint8_t type, const std::string& name) { + assert(m_stack.size()); + + checkArrayPreamble(type); + Level& level = m_stack.back(); + + if (level.state != State::Array) { + if (!name.empty()) { + auto& s = stream(); + writeElementName(s, name); + s.write((const char*)&type, 1); + } + ++level.count; + } +} + +void KVBinaryOutputStreamSerializer::checkArrayPreamble(uint8_t type) { + if (m_stack.empty()) { + return; + } + + Level& level = m_stack.back(); + + if (level.state == State::ArrayPrefix) { + auto& s = stream(); + writeElementName(s, level.name); + char c = BIN_KV_SERIALIZE_FLAG_ARRAY | type; + s.write(&c, 1); + writeArraySize(s, level.count); + level.state = State::Array; + } +} + + +MemoryStream& KVBinaryOutputStreamSerializer::stream() { + assert(m_objectsStack.size()); + return m_objectsStack.back(); +} + +} diff --git a/src/serialization/KVBinaryOutputStreamSerializer.h b/src/serialization/KVBinaryOutputStreamSerializer.h new file mode 100644 index 0000000000..553c375074 --- /dev/null +++ b/src/serialization/KVBinaryOutputStreamSerializer.h @@ -0,0 +1,103 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" +#include "SerializationOverloads.h" +#include "MemoryStream.h" + +#include +#include +#include +#include + +namespace cryptonote { + +class KVBinaryOutputStreamSerializer : public ISerializer { +public: + + KVBinaryOutputStreamSerializer(); + virtual ~KVBinaryOutputStreamSerializer() {} + + void write(std::ostream& target); + + virtual ISerializer::SerializerType type() const; + + virtual ISerializer& beginObject(const std::string& name) override; + virtual ISerializer& endObject() override; + + virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; + virtual ISerializer& endArray() override; + + virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; + virtual ISerializer& operator()(int32_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; + virtual ISerializer& operator()(int64_t& value, const std::string& name) override; + virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; + virtual ISerializer& operator()(double& value, const std::string& name) override; + virtual ISerializer& operator()(bool& value, const std::string& name) override; + virtual ISerializer& operator()(std::string& value, const std::string& name) override; + + virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; + virtual ISerializer& binary(std::string& value, const std::string& name) override; + + virtual bool hasObject(const std::string& name) override; + + template + ISerializer& operator()(T& value, const std::string& name) { + return ISerializer::operator()(value, name); + } + +private: + + void writeElementPrefix(uint8_t type, const std::string& name); + void checkArrayPreamble(uint8_t type); + void updateState(uint8_t type); + MemoryStream& stream(); + + enum class State { + Root, + Object, + ArrayPrefix, + Array + }; + + struct Level { + State state; + std::string name; + size_t count; + + Level(const std::string& nm) : + name(nm), state(State::Object), count(0) {} + + Level(const std::string& nm, size_t arraySize) : + name(nm), state(State::ArrayPrefix), count(arraySize) {} + + Level(Level&& rv) { + state = rv.state; + name = std::move(rv.name); + count = rv.count; + } + + }; + + std::vector m_objectsStack; + std::vector m_stack; +}; + +} diff --git a/src/platform/msc/inline_c.h b/src/serialization/MemoryStream.cpp similarity index 92% rename from src/platform/msc/inline_c.h rename to src/serialization/MemoryStream.cpp index b202526b2e..99bc74ad05 100644 --- a/src/platform/msc/inline_c.h +++ b/src/serialization/MemoryStream.cpp @@ -15,8 +15,5 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#pragma once +#include "MemoryStream.h" -#ifndef __cplusplus -#define inline __inline -#endif diff --git a/src/serialization/MemoryStream.h b/src/serialization/MemoryStream.h new file mode 100644 index 0000000000..52f87e3280 --- /dev/null +++ b/src/serialization/MemoryStream.h @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + + +#include "IStream.h" +#include +#include +#include // memcpy + +namespace cryptonote { + +class MemoryStream: + public IInputStream, + public IOutputStream { +public: + + MemoryStream() : + m_readPos(0), m_writePos(0) {} + + virtual void write(const char* data, std::size_t size) override { + if (size == 0) { + return; + } + + if (m_writePos + size > m_buffer.size()) { + m_buffer.resize(m_writePos + size); + } + + memcpy(&m_buffer[m_writePos], data, size); + m_writePos += size; + } + + virtual std::size_t read(char* data, std::size_t size) override { + size_t readSize = std::min(size, m_buffer.size() - m_readPos); + + if (readSize > 0) { + memcpy(data, &m_buffer[m_readPos], readSize); + m_readPos += readSize; + } + + return readSize; + } + + size_t size() { + return m_buffer.size(); + } + + const char* data() { + return m_buffer.data(); + } + + void clear() { + m_readPos = 0; + m_writePos = 0; + m_buffer.resize(0); + } + +private: + + size_t m_readPos; + size_t m_writePos; + std::vector m_buffer; +}; + +} + diff --git a/src/serialization/SerializationOverloads.cpp b/src/serialization/SerializationOverloads.cpp index ffd5e29689..d5a7e16adb 100644 --- a/src/serialization/SerializationOverloads.cpp +++ b/src/serialization/SerializationOverloads.cpp @@ -21,61 +21,61 @@ namespace cryptonote { -void readVarint(uint64_t& value, cryptonote::ISerializer& serializer) { - const int bits = std::numeric_limits::digits; - - uint64_t v = 0; - for (int shift = 0;; shift += 7) { - uint8_t b; - serializer.untagged(b); - - if (shift + 7 >= bits && b >= 1 << (bits - shift)) { - throw std::runtime_error("Varint overflow"); - } - - if (b == 0 && shift != 0) { - throw std::runtime_error("Non-canonical varint representation"); - } - - v |= static_cast(b & 0x7f) << shift; - if ((b & 0x80) == 0) { - break; - } - } - - value = v; -} - -void writeVarint(uint64_t& value, cryptonote::ISerializer& serializer) { - uint64_t v = value; - - while (v >= 0x80) { - uint8_t b = (static_cast(v) & 0x7f) | 0x80; - serializer.untagged(b); - v >>= 7; - } - - uint8_t b = static_cast(v); - serializer.untagged(b); -} - - -void serializeVarint(uint64_t& value, const std::string& name, cryptonote::ISerializer& serializer) { - serializer.tag(name); - - if (serializer.type() == cryptonote::ISerializer::INPUT) { - readVarint(value, serializer); - } else { - writeVarint(value, serializer); - } - - serializer.endTag(); -} - -void serializeVarint(uint32_t& value, const std::string& name, cryptonote::ISerializer& serializer) { - uint64_t v = value; - serializeVarint(v, name, serializer); - value = static_cast(v); -} +//void readVarint(uint64_t& value, cryptonote::ISerializer& serializer) { +// const int bits = std::numeric_limits::digits; +// +// uint64_t v = 0; +// for (int shift = 0;; shift += 7) { +// uint8_t b; +// serializer.untagged(b); +// +// if (shift + 7 >= bits && b >= 1 << (bits - shift)) { +// throw std::runtime_error("Varint overflow"); +// } +// +// if (b == 0 && shift != 0) { +// throw std::runtime_error("Non-canonical varint representation"); +// } +// +// v |= static_cast(b & 0x7f) << shift; +// if ((b & 0x80) == 0) { +// break; +// } +// } +// +// value = v; +//} +// +//void writeVarint(uint64_t& value, cryptonote::ISerializer& serializer) { +// uint64_t v = value; +// +// while (v >= 0x80) { +// uint8_t b = (static_cast(v) & 0x7f) | 0x80; +// serializer.untagged(b); +// v >>= 7; +// } +// +// uint8_t b = static_cast(v); +// serializer.untagged(b); +//} +// +// +//void serializeVarint(uint64_t& value, const std::string& name, cryptonote::ISerializer& serializer) { +// serializer.tag(name); +// +// if (serializer.type() == cryptonote::ISerializer::INPUT) { +// readVarint(value, serializer); +// } else { +// writeVarint(value, serializer); +// } +// +// serializer.endTag(); +//} +// +//void serializeVarint(uint32_t& value, const std::string& name, cryptonote::ISerializer& serializer) { +// uint64_t v = value; +// serializeVarint(v, name, serializer); +// value = static_cast(v); +//} } diff --git a/src/serialization/SerializationOverloads.h b/src/serialization/SerializationOverloads.h index a4a58384e6..4c98a8456c 100644 --- a/src/serialization/SerializationOverloads.h +++ b/src/serialization/SerializationOverloads.h @@ -22,11 +22,28 @@ #include #include #include +#include +#include namespace cryptonote { -void serializeVarint(uint64_t& value, const std::string& name, cryptonote::ISerializer& serializer); -void serializeVarint(uint32_t& value, const std::string& name, cryptonote::ISerializer& serializer); +template +typename std::enable_if::value>::type +serializeAsBinary(std::vector& value, const std::string& name, cryptonote::ISerializer& serializer) { + std::string blob; + if (serializer.type() == ISerializer::INPUT) { + serializer.binary(blob, name); + value.resize(blob.size() / sizeof(T)); + if (blob.size()) { + memcpy(&value[0], blob.data(), blob.size()); + } + } else { + if (!value.empty()) { + blob.assign(reinterpret_cast(&value[0]), value.size() * sizeof(T)); + } + serializer.binary(blob, name); + } +} template void serialize(std::vector& value, const std::string& name, cryptonote::ISerializer& serializer) { @@ -41,8 +58,8 @@ void serialize(std::vector& value, const std::string& name, cryptonote::ISeri serializer.endArray(); } -template -void serialize(std::unordered_map& value, const std::string& name, cryptonote::ISerializer& serializer) { +template +void serialize(std::unordered_map& value, const std::string& name, cryptonote::ISerializer& serializer) { std::size_t size; size = value.size(); @@ -55,8 +72,8 @@ void serialize(std::unordered_map& value, const std::string& name, crypton K key; V v; serializer.beginObject(""); - serializer(key, ""); - serializer(v, ""); + serializer(key, "key"); + serializer(v, "value"); serializer.endObject(); value[key] = v; @@ -66,8 +83,8 @@ void serialize(std::unordered_map& value, const std::string& name, crypton K key; key = kv.first; serializer.beginObject(""); - serializer(key, ""); - serializer(kv.second, ""); + serializer(key, "key"); + serializer(kv.second, "value"); serializer.endObject(); } } @@ -75,4 +92,9 @@ void serialize(std::unordered_map& value, const std::string& name, crypton serializer.endArray(); } +template +void serialize(std::array& value, const std::string& name, cryptonote::ISerializer& s) { + s.binary(value.data(), value.size(), name); +} + } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 80e71d30c8..ae5ab7dfdf 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -16,7 +16,11 @@ // along with Bytecoin. If not, see . #include +#include +#include +#include #include +#include #include #include @@ -34,6 +38,11 @@ #include "simplewallet.h" #include "wallet/wallet_rpc_server.h" #include "version.h" +#include "wallet/WalletHelper.h" +#include "wallet/Wallet.h" +#include "wallet/wallet_errors.h" +#include "node_rpc_proxy/NodeRpcProxy.h" +#include "wallet/LegacyKeysImporter.h" #if defined(WIN32) #include @@ -42,247 +51,330 @@ using namespace std; using namespace epee; using namespace cryptonote; +using namespace CryptoNote; using boost::lexical_cast; namespace po = boost::program_options; #define EXTENDED_LOGS_FILE "wallet_details.log" -namespace -{ - const command_line::arg_descriptor arg_wallet_file = {"wallet-file", "Use wallet ", ""}; - const command_line::arg_descriptor arg_generate_new_wallet = {"generate-new-wallet", "Generate new wallet and save it to or
.wallet by default", ""}; - const command_line::arg_descriptor arg_daemon_address = {"daemon-address", "Use daemon instance at :", ""}; - const command_line::arg_descriptor arg_daemon_host = {"daemon-host", "Use daemon instance at host instead of localhost", ""}; - const command_line::arg_descriptor arg_password = {"password", "Wallet password", "", true}; - const command_line::arg_descriptor arg_daemon_port = {"daemon-port", "Use daemon instance at port instead of 8081", 0}; - const command_line::arg_descriptor arg_log_level = {"set_log", "", 0, true}; - const command_line::arg_descriptor arg_testnet = {"testnet", "Used to deploy test nets. The daemon must be launched with --testnet flag", false}; - - const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; - - inline std::string interpret_rpc_response(bool ok, const std::string& status) - { - std::string err; - if (ok) - { - if (status == CORE_RPC_STATUS_BUSY) - { - err = "daemon is busy. Please try later"; - } - else if (status != CORE_RPC_STATUS_OK) - { - err = status; - } - } - else - { - err = "possible lost connection to daemon"; +namespace { + +const command_line::arg_descriptor arg_wallet_file = { "wallet-file", "Use wallet ", "" }; +const command_line::arg_descriptor arg_generate_new_wallet = { "generate-new-wallet", "Generate new wallet and save it to ", "" }; +const command_line::arg_descriptor arg_daemon_address = { "daemon-address", "Use daemon instance at :", "" }; +const command_line::arg_descriptor arg_daemon_host = { "daemon-host", "Use daemon instance at host instead of localhost", "" }; +const command_line::arg_descriptor arg_password = { "password", "Wallet password", "", true }; +const command_line::arg_descriptor arg_daemon_port = { "daemon-port", "Use daemon instance at port instead of 8081", 0 }; +const command_line::arg_descriptor arg_log_level = { "set_log", "", 0, true }; +const command_line::arg_descriptor arg_testnet = { "testnet", "Used to deploy test nets. The daemon must be launched with --testnet flag", false }; + +const command_line::arg_descriptor< std::vector > arg_command = { "command", "" }; + +inline std::string interpret_rpc_response(bool ok, const std::string& status) { + std::string err; + if (ok) { + if (status == CORE_RPC_STATUS_BUSY) { + err = "daemon is busy. Please try later"; + } else if (status != CORE_RPC_STATUS_OK) { + err = status; } - return err; + } else { + err = "possible lost connection to daemon"; } + return err; +} - class message_writer - { - public: - message_writer(epee::log_space::console_colors color = epee::log_space::console_color_default, bool bright = false, - std::string&& prefix = std::string(), int log_level = LOG_LEVEL_2) - : m_flush(true) - , m_color(color) - , m_bright(bright) - , m_log_level(log_level) - { - m_oss << prefix; - } +class message_writer { +public: + message_writer(epee::log_space::console_colors color = epee::log_space::console_color_default, bool bright = false, + std::string&& prefix = std::string(), int log_level = LOG_LEVEL_2) + : m_flush(true) + , m_color(color) + , m_bright(bright) + , m_log_level(log_level) { + m_oss << prefix; + } - message_writer(message_writer&& rhs) - : m_flush(std::move(rhs.m_flush)) + message_writer(message_writer&& rhs) + : m_flush(std::move(rhs.m_flush)) #if defined(_MSC_VER) - , m_oss(std::move(rhs.m_oss)) + , m_oss(std::move(rhs.m_oss)) #else - // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 - , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate) + // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 + , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate) #endif - , m_color(std::move(rhs.m_color)) - , m_log_level(std::move(rhs.m_log_level)) - { - rhs.m_flush = false; - } + , m_color(std::move(rhs.m_color)) + , m_log_level(std::move(rhs.m_log_level)) { + rhs.m_flush = false; + } - template - std::ostream& operator<<(const T& val) - { - m_oss << val; - return m_oss; - } + template + std::ostream& operator<<(const T& val) { + m_oss << val; + return m_oss; + } - ~message_writer() - { - if (m_flush) - { - m_flush = false; + ~message_writer() { + if (m_flush) { + m_flush = false; - LOG_PRINT(m_oss.str(), m_log_level) + LOG_PRINT(m_oss.str(), m_log_level) - if (epee::log_space::console_color_default == m_color) - { - std::cout << m_oss.str(); + if (epee::log_space::console_color_default == m_color) { + std::cout << m_oss.str(); + } else { + epee::log_space::set_console_color(m_color, m_bright); + std::cout << m_oss.str(); + epee::log_space::reset_console_color(); } - else - { - epee::log_space::set_console_color(m_color, m_bright); - std::cout << m_oss.str(); - epee::log_space::reset_console_color(); - } - std::cout << std::endl; - } + std::cout << std::endl; } + } - private: - message_writer(message_writer& rhs); - message_writer& operator=(message_writer& rhs); - message_writer& operator=(message_writer&& rhs); +private: + message_writer(message_writer& rhs); + message_writer& operator=(message_writer& rhs); + message_writer& operator=(message_writer&& rhs); + +private: + bool m_flush; + std::stringstream m_oss; + epee::log_space::console_colors m_color; + bool m_bright; + int m_log_level; +}; + +message_writer success_msg_writer(bool color = false) { + return message_writer(color ? epee::log_space::console_color_green : epee::log_space::console_color_default, false, std::string(), LOG_LEVEL_2); +} - private: - bool m_flush; - std::stringstream m_oss; - epee::log_space::console_colors m_color; - bool m_bright; - int m_log_level; - }; +message_writer fail_msg_writer() { + return message_writer(epee::log_space::console_color_red, true, std::string("Error: "), LOG_LEVEL_0); +} - message_writer success_msg_writer(bool color = false) - { - return message_writer(color ? epee::log_space::console_color_green : epee::log_space::console_color_default, false, std::string(), LOG_LEVEL_2); + +template +class ArgumentReader { +public: + + ArgumentReader(IterT begin, IterT end) : + m_begin(begin), m_end(end), m_cur(begin) { } - message_writer fail_msg_writer() - { - return message_writer(epee::log_space::console_color_red, true, "Error: ", LOG_LEVEL_0); + bool eof() const { + return m_cur == m_end; } + ValueT next() { + if (eof()) { + throw std::runtime_error("unexpected end of arguments"); + } - template - class ArgumentReader { - public: + return *m_cur++; + } - ArgumentReader(IterT begin, IterT end) : - m_begin(begin), m_end(end), m_cur(begin) {} +private: - bool eof() const { - return m_cur == m_end; - } + IterT m_cur; + IterT m_begin; + IterT m_end; +}; - ValueT next() { - if (eof()) { - throw std::runtime_error("unexpected end of arguments"); - } +struct TransferCommand { + const cryptonote::Currency& m_currency; + size_t fake_outs_count; + vector dsts; + std::vector extra; + uint64_t fee; - return *m_cur++; - } + TransferCommand(const cryptonote::Currency& currency) : + m_currency(currency), fake_outs_count(0), fee(currency.minimumFee()) { + } - private: + bool parseArguments(const std::vector &args) { - IterT m_cur; - IterT m_begin; - IterT m_end; - }; + ArgumentReader::const_iterator> ar(args.begin(), args.end()); - struct TransferCommand { - const cryptonote::Currency& m_currency; - size_t fake_outs_count; - vector dsts; - std::vector extra; - uint64_t fee; + try { - TransferCommand(const cryptonote::Currency& currency) : - m_currency(currency), fake_outs_count(0), fee(currency.minimumFee()) { - } + auto mixin_str = ar.next(); - bool parseArguments(const std::vector &args) { + if (!epee::string_tools::get_xtype_from_string(fake_outs_count, mixin_str)) { + fail_msg_writer() << "mixin_count should be non-negative integer, got " << mixin_str; + return false; + } - ArgumentReader::const_iterator> ar(args.begin(), args.end()); + while (!ar.eof()) { - try { + auto arg = ar.next(); - auto mixin_str = ar.next(); + if (arg.size() && arg[0] == '-') { - if (!epee::string_tools::get_xtype_from_string(fake_outs_count, mixin_str)) { - fail_msg_writer() << "mixin_count should be non-negative integer, got " << mixin_str; - return false; - } + const auto& value = ar.next(); - while (!ar.eof()) { - - auto arg = ar.next(); - - if (arg.size() && arg[0] == '-') { - - const auto& value = ar.next(); - - if (arg == "-p") { - crypto::hash payment_id; - bool r = tools::wallet2::parse_payment_id(value, payment_id); - if (r) { - 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); - } - - if (!r) { - fail_msg_writer() << "payment ID has invalid format: \"" << value << "\", expected 64-character string"; - return false; - } - } else if (arg == "-f") { - bool ok = m_currency.parseAmount(value, fee); - if (!ok) { - fail_msg_writer() << "Fee value is invalid: " << value; - return false; - } - - if (fee < m_currency.minimumFee()) { - fail_msg_writer() << "Fee value is less than minimum: " << m_currency.minimumFee(); - return false; - } + if (arg == "-p") { + if (!createTxExtraWithPaymentId(value, extra)) { + fail_msg_writer() << "payment ID has invalid format: \"" << value << "\", expected 64-character string"; + return false; } - } else { - cryptonote::tx_destination_entry de; - - if (!m_currency.parseAccountAddressString(arg, de.addr)) { - crypto::hash paymentId; - if (tools::wallet2::parse_payment_id(arg, paymentId)) { - fail_msg_writer() << "Invalid payment ID usage. Please, use -p . See help for details."; - } else { - fail_msg_writer() << "Wrong address: " << arg; - } - + } else if (arg == "-f") { + bool ok = m_currency.parseAmount(value, fee); + if (!ok) { + fail_msg_writer() << "Fee value is invalid: " << value; return false; } - auto value = ar.next(); - bool ok = m_currency.parseAmount(value, de.amount); - if (!ok || 0 == de.amount) { - fail_msg_writer() << "amount is wrong: " << arg << ' ' << value << - ", expected number from 0 to " << m_currency.formatAmount(std::numeric_limits::max()); + if (fee < m_currency.minimumFee()) { + fail_msg_writer() << "Fee value is less than minimum: " << m_currency.minimumFee(); return false; } + } + } else { + Transfer destination; + cryptonote::tx_destination_entry de; + + if (!m_currency.parseAccountAddressString(arg, de.addr)) { + crypto::hash paymentId; + if (cryptonote::parsePaymentId(arg, paymentId)) { + fail_msg_writer() << "Invalid payment ID usage. Please, use -p . See help for details."; + } else { + fail_msg_writer() << "Wrong address: " << arg; + } - dsts.push_back(de); + return false; } - } - if (dsts.empty()) { - fail_msg_writer() << "At least one destination address is required"; - return false; + auto value = ar.next(); + bool ok = m_currency.parseAmount(value, de.amount); + if (!ok || 0 == de.amount) { + fail_msg_writer() << "amount is wrong: " << arg << ' ' << value << + ", expected number from 0 to " << m_currency.formatAmount(std::numeric_limits::max()); + return false; + } + destination.address = arg; + destination.amount = de.amount; + + dsts.push_back(destination); } - } catch (const std::exception& e) { - fail_msg_writer() << e.what(); + } + + if (dsts.empty()) { + fail_msg_writer() << "At least one destination address is required"; return false; } + } catch (const std::exception& e) { + fail_msg_writer() << e.what(); + return false; + } - return true; + return true; + } +}; + + +std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { + std::string keys_file, walletFileName; + WalletHelper::prepareFileNames(walletFile, keys_file, walletFileName); + + boost::system::error_code ignore; + bool keysExists = boost::filesystem::exists(keys_file, ignore); + bool walletExists = boost::filesystem::exists(walletFileName, ignore); + + if (walletExists) { + LOG_PRINT_L0("Loading wallet..."); + std::ifstream walletFile; + walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::in); + if (walletFile.fail()) + throw std::runtime_error("error opening walletfile"); + + WalletHelper::InitWalletResultObserver initObserver; + std::future f_initError = initObserver.initResult.get_future(); + wallet->addObserver(&initObserver); + wallet->initAndLoad(walletFile, password); + auto initError = f_initError.get(); + wallet->removeObserver(&initObserver); + walletFile.close(); + if (initError) { //bad password, or legacy format + if (keysExists) { + std::stringstream ss; + cryptonote::importLegacyKeys(keys_file, password, ss); + boost::filesystem::rename(keys_file, keys_file + ".back"); + boost::filesystem::rename(walletFileName, walletFileName + ".back"); + + f_initError = initObserver.initResult.get_future(); + wallet->addObserver(&initObserver); + wallet->initAndLoad(ss, password); + auto initError = f_initError.get(); + wallet->removeObserver(&initObserver); + if (initError) { + throw std::runtime_error("failed to load wallet: " + initError.message()); + } + + LOG_PRINT_L0("Storing wallet..."); + std::ofstream walletFile; + walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + throw std::runtime_error("error saving walletfile"); + WalletHelper::SaveWalletResultObserver saveObserver; + std::future f_saveError = saveObserver.saveResult.get_future(); + wallet->addObserver(&saveObserver); + wallet->save(walletFile, false, false); + auto saveError = f_saveError.get(); + wallet->removeObserver(&saveObserver); + if (saveError) { + fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + throw std::runtime_error("error saving walletfile"); + } + + LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); + return walletFileName; + } else { // no keys, wallet error loading + throw std::runtime_error("can't load walletfile, check password"); + } + } else { //new wallet ok + return walletFileName; + } + } else { + if (keysExists) { //wallet not exists but keys presented + std::stringstream ss; + cryptonote::importLegacyKeys(keys_file, password, ss); + boost::filesystem::rename(keys_file, keys_file + ".back"); + + WalletHelper::InitWalletResultObserver initObserver; + std::future f_initError = initObserver.initResult.get_future(); + wallet->addObserver(&initObserver); + wallet->initAndLoad(ss, password); + auto initError = f_initError.get(); + wallet->removeObserver(&initObserver); + if (initError) { + throw std::runtime_error("failed to load wallet: " + initError.message()); + } + + LOG_PRINT_L0("Storing wallet..."); + std::ofstream walletFile; + walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + throw std::runtime_error("error saving walletfile"); + WalletHelper::SaveWalletResultObserver saveObserver; + std::future f_saveError = saveObserver.saveResult.get_future(); + wallet->addObserver(&saveObserver); + wallet->save(walletFile, false, false); + auto saveError = f_saveError.get(); + wallet->removeObserver(&saveObserver); + if (saveError) { + fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + throw std::runtime_error("error saving walletfile"); + } + + LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); + return walletFileName; + } else { //no wallet no keys + throw std::runtime_error("walletfile not found"); } - }; + } +} + } @@ -307,16 +399,18 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) : m_daemon_port(0) , m_currency(currency) , m_refresh_progress_reporter(*this) + , m_saveResultPromise(nullptr) + , m_initResultPromise(nullptr) { m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); - m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); + //m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); - m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability"); + m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers"); m_cmd_binder.set_handler("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers"); m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), + m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [ ... ] [-p payment_id] [-f fee]" " - Transfer ,... to ,... , respectively. " " is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); @@ -329,18 +423,18 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) //---------------------------------------------------------------------------------------------------- bool simple_wallet::set_log(const std::vector &args) { - if(args.size() != 1) + if (args.size() != 1) { fail_msg_writer() << "use: set_log "; return true; } uint16_t l = 0; - if(!epee::string_tools::get_xtype_from_string(l, args[0])) + if (!epee::string_tools::get_xtype_from_string(l, args[0])) { fail_msg_writer() << "wrong number format, use: set_log "; return true; } - if(LOG_LEVEL_4 < l) + if (LOG_LEVEL_4 < l) { fail_msg_writer() << "wrong number range, use: set_log "; return true; @@ -350,60 +444,65 @@ bool simple_wallet::set_log(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::ask_wallet_create_if_needed() +bool simple_wallet::init(const boost::program_options::variables_map& vm) { - std::string wallet_path; - - std::cout << "Specify wallet file name (e.g., wallet.bin). If the wallet doesn't exist, it will be created.\n"; - std::cout << "Wallet file name: "; + handle_command_line(vm); - std::getline(std::cin, wallet_path); + if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port) + { + fail_msg_writer() << "you can't specify daemon host or port several times"; + return false; + } - wallet_path = string_tools::trim(wallet_path); + if (m_generate_new.empty() && m_wallet_file_arg.empty()) { + std::cout << "Nor 'generate-new-wallet' neither 'wallet-file' argument was specified.\nWhat do you want to do?\n[O]pen existing wallet, [G]enerate new wallet file or [E]xit.\n"; + char c; + do { + std::string answer; + std::getline(std::cin, answer); + c = answer[0]; + if (!(c == 'O' || c == 'G' || c == 'E' || c == 'o' || c == 'g' || c == 'e')) { + std::cout << "Unknown command: " << c<m_node.reset(new NodeRpcProxy(m_daemon_host, m_daemon_port)); + + std::promise errorPromise; + std::future f_error = errorPromise.get_future(); + auto callback = [&errorPromise](std::error_code e) {errorPromise.set_value(e); }; + m_node->init(callback); + auto error = f_error.get(); + if (error) { + fail_msg_writer() << "failed to init NodeRPCProxy: " << error.message(); + return false; + } + if (!m_generate_new.empty()) { - bool r = new_wallet(m_generate_new, pwd_container.password()); + bool r = new_wallet(walletFileName, pwd_container.password()); CHECK_AND_ASSERT_MES(r, false, "account creation failed"); } else { - bool r = open_wallet(m_wallet_file, pwd_container.password()); - CHECK_AND_ASSERT_MES(r, false, "could not open account"); + m_wallet.reset(new Wallet(m_currency, *m_node)); + + try { + m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(m_wallet, m_wallet_file_arg, pwd_container.password()); + } catch (const std::exception& e) { + fail_msg_writer() << "failed to load wallet: " << e.what(); + return false; + } + + m_wallet->addObserver(this); + m_node->addObserver(this); + + message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << m_wallet->getAddress(); + + + success_msg_writer() << + "**********************************************************************\n" << + "Use \"help\" command to see the list of available commands.\n" << + "**********************************************************************"; } return true; @@ -452,37 +580,51 @@ bool simple_wallet::deinit() //---------------------------------------------------------------------------------------------------- void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm) { - m_wallet_file = command_line::get_arg(vm, arg_wallet_file); - m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); + m_wallet_file_arg = command_line::get_arg(vm, arg_wallet_file); + m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); m_daemon_address = command_line::get_arg(vm, arg_daemon_address); - m_daemon_host = command_line::get_arg(vm, arg_daemon_host); - m_daemon_port = command_line::get_arg(vm, arg_daemon_port); -} -//---------------------------------------------------------------------------------------------------- -bool simple_wallet::try_connect_to_daemon() -{ - if (!m_wallet->check_connection()) - { - fail_msg_writer() << "wallet failed to connect to daemon (" << m_daemon_address << "). " << - "Daemon either is not started or passed wrong port. " << - "Please, make sure that daemon is running or restart the wallet with correct daemon address."; - return false; - } - return true; + m_daemon_host = command_line::get_arg(vm, arg_daemon_host); + m_daemon_port = command_line::get_arg(vm, arg_daemon_port); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password) { m_wallet_file = wallet_file; - m_wallet.reset(new tools::wallet2(m_currency)); - m_wallet->callback(this); + m_wallet.reset(new Wallet(m_currency, *m_node.get())); + m_node->addObserver(this); + m_wallet->addObserver(this); try { - m_wallet->generate(wallet_file, password); + m_initResultPromise.reset(new std::promise()); + std::future f_initError = m_initResultPromise->get_future(); + m_wallet->initAndGenerate(password); + auto initError = f_initError.get(); + m_initResultPromise.reset(nullptr); + if (initError) { + fail_msg_writer() << "failed to generate new wallet: " << initError.message(); + return false; + } + std::ofstream walletFile; + walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + return false; + m_saveResultPromise.reset(new std::promise()); + std::future f_saveError = m_saveResultPromise->get_future(); + m_wallet->save(walletFile); + auto saveError = f_saveError.get(); + m_saveResultPromise.reset(nullptr); + if (saveError) { + fail_msg_writer() << "failed to save new wallet: " << saveError.message(); + return false; + } + + WalletAccountKeys keys; + m_wallet->getAccountKeys(keys); + message_writer(epee::log_space::console_color_white, true) << - "Generated new wallet: " << m_currency.accountAddressAsString(m_wallet->get_account()) << std::endl << - "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + "Generated new wallet: " << m_wallet->getAddress() << std::endl << + "view key: " << epee::string_tools::pod_to_hex(keys.viewSecretKey); } catch (const std::exception& e) { @@ -490,12 +632,9 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas return false; } - m_wallet->init(m_daemon_address); - success_msg_writer() << "**********************************************************************\n" << "Your wallet has been generated.\n" << - "To start synchronizing with the daemon use \"refresh\" command.\n" << "Use \"help\" command to see the list of available commands.\n" << "Always use \"exit\" command when closing simplewallet to save\n" << "current session's state. Otherwise, you will possibly need to synchronize \n" << @@ -504,53 +643,30 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::open_wallet(const string &wallet_file, const std::string& password) -{ - m_wallet_file = wallet_file; - m_wallet.reset(new tools::wallet2(m_currency)); - m_wallet->callback(this); - - try - { - m_wallet->load(m_wallet_file, password); - message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << - m_currency.accountAddressAsString(m_wallet->get_account()); - } - catch (const std::exception& e) - { - fail_msg_writer() << "failed to load wallet: " << e.what(); - return false; - } - - m_wallet->init(m_daemon_address); - - refresh(std::vector()); - success_msg_writer() << - "**********************************************************************\n" << - "Use \"help\" command to see the list of available commands.\n" << - "**********************************************************************"; - return true; -} -//---------------------------------------------------------------------------------------------------- bool simple_wallet::close_wallet() { - bool r = m_wallet->deinit(); - if (!r) - { - fail_msg_writer() << "failed to deinit wallet"; - return false; - } - try { - m_wallet->store(); + std::ofstream walletFile; + walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + return false; + m_saveResultPromise.reset(new std::promise()); + std::future f_saveError = m_saveResultPromise->get_future(); + m_wallet->save(walletFile); + auto saveError = f_saveError.get(); + m_saveResultPromise.reset(nullptr); + if (saveError) { + fail_msg_writer() << saveError.message(); + return false; + } } catch (const std::exception& e) { fail_msg_writer() << e.what(); return false; } - + m_wallet->removeObserver(this); return true; } //---------------------------------------------------------------------------------------------------- @@ -558,7 +674,19 @@ bool simple_wallet::save(const std::vector &args) { try { - m_wallet->store(); + std::ofstream walletFile; + walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + return false; + m_saveResultPromise.reset(new std::promise()); + std::future f_saveError = m_saveResultPromise->get_future(); + m_wallet->save(walletFile); + auto saveError = f_saveError.get(); + m_saveResultPromise.reset(nullptr); + if (saveError) { + fail_msg_writer() << saveError.message(); + return false; + } success_msg_writer() << "Wallet data saved"; } catch (const std::exception& e) @@ -572,17 +700,13 @@ bool simple_wallet::save(const std::vector &args) bool simple_wallet::reset(const std::vector &args) { m_wallet->reset(); success_msg_writer(true) << "Reset is complete successfully"; - refresh(); return true; } bool simple_wallet::start_mining(const std::vector& args) { - if (!try_connect_to_daemon()) - return true; - COMMAND_RPC_START_MINING::request req; - req.miner_address = m_currency.accountAddressAsString(m_wallet->get_account()); + req.miner_address = m_wallet->getAddress(); bool ok = true; size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast(2)); @@ -621,8 +745,8 @@ bool simple_wallet::start_mining(const std::vector& args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::stop_mining(const std::vector& args) { - if (!try_connect_to_daemon()) - return true; + /* if (!try_connect_to_daemon()) + return true;*/ COMMAND_RPC_STOP_MINING::request req; COMMAND_RPC_STOP_MINING::response res; @@ -635,189 +759,106 @@ bool simple_wallet::stop_mining(const std::vector& args) return true; } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_new_block(uint64_t height) -{ - m_refresh_progress_reporter.update(height, false); -} -//---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index) -{ - message_writer(epee::log_space::console_color_green, false) << - "Height " << height << - ", transaction " << get_transaction_hash(tx) << - ", received " << m_currency.formatAmount(tx.vout[out_index].amount); - m_refresh_progress_reporter.update(height, true); +void simple_wallet::initCompleted(std::error_code result) { + if (m_initResultPromise.get() != nullptr) { + m_initResultPromise->set_value(result); + } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx) -{ - message_writer(epee::log_space::console_color_magenta, false) << - "Height " << height << - ", transaction " << get_transaction_hash(spend_tx) << - ", spent " << m_currency.formatAmount(in_tx.vout[out_index].amount); - m_refresh_progress_reporter.update(height, true); +void simple_wallet::saveCompleted(std::error_code result) { + if (m_saveResultPromise.get() != nullptr) { + m_saveResultPromise->set_value(result); + } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx) +void simple_wallet::localBlockchainUpdated(uint64_t height) { - message_writer(epee::log_space::console_color_red, true) << - "Height " << height << - ", transaction " << get_transaction_hash(tx) << - ", unsupported transaction format"; - m_refresh_progress_reporter.update(height, true); + m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::refresh(const std::vector& args/* = std::vector()*/) +void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transactionId) { - if (!try_connect_to_daemon()) - return true; - - message_writer() << "Starting refresh..."; - size_t fetched_blocks = 0; - bool ok = false; - std::ostringstream ss; - try - { - m_wallet->refresh(fetched_blocks); - ok = true; - // Clear line "Height xxx of xxx" - std::cout << "\r \r"; - success_msg_writer(true) << "Refresh done, blocks received: " << fetched_blocks; - show_balance(); - } - catch (const tools::error::daemon_busy&) - { - ss << "daemon is busy. Please try later"; - } - catch (const tools::error::no_connection_to_daemon&) - { - ss << "no connection to daemon. Please, make sure daemon is running"; - } - catch (const tools::error::wallet_rpc_error& e) - { - LOG_ERROR("Unknown RPC error: " << e.to_string()); - ss << "RPC error \"" << e.what() << '"'; - } - catch (const tools::error::refresh_error& e) - { - LOG_ERROR("refresh error: " << e.to_string()); - ss << e.what(); - } - catch (const tools::error::wallet_internal_error& e) - { - LOG_ERROR("internal error: " << e.to_string()); - ss << "internal error: " << e.what(); - } - catch (const std::exception& e) - { - LOG_ERROR("unexpected error: " << e.what()); - ss << "unexpected error: " << e.what(); - } - catch (...) - { - LOG_ERROR("Unknown error"); - ss << "unknown error"; - } - - if (!ok) - { - fail_msg_writer() << "refresh failed: " << ss.str() << ". Blocks received: " << fetched_blocks; - } + TransactionInfo txInfo; + m_wallet->getTransaction(transactionId, txInfo); - return true; + message_writer(epee::log_space::console_color_green, false) << + "Height " << txInfo.blockHeight << + ", transaction " << epee::string_tools::pod_to_hex(txInfo.hash) << + ", received " << m_currency.formatAmount(txInfo.totalAmount); + m_refresh_progress_reporter.update(txInfo.blockHeight, true); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) { - success_msg_writer() << "balance: " << m_currency.formatAmount(m_wallet->balance()) << - ", unlocked balance: " << m_currency.formatAmount(m_wallet->unlocked_balance()); + success_msg_writer() << "balance: " << m_currency.formatAmount(m_wallet->pendingBalance()) << + ", unlocked balance: " << m_currency.formatAmount(m_wallet->actualBalance()); return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_incoming_transfers(const std::vector& args) { - bool filter = false; - bool available = false; - if (!args.empty()) - { - if (args[0] == "available") - { - filter = true; - available = true; - } - else if (args[0] == "unavailable") - { - filter = true; - available = false; - } - } - - tools::wallet2::transfer_container transfers; - m_wallet->get_transfers(transfers); - - bool transfers_found = false; - for (const auto& td : transfers) - { - if (!filter || available != td.m_spent) - { - if (!transfers_found) - { - message_writer() << " amount \tspent\tglobal index\t tx id"; - transfers_found = true; - } - message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) << - std::setw(21) << m_currency.formatAmount(td.amount()) << '\t' << - std::setw(3) << (td.m_spent ? 'T' : 'F') << " \t" << - std::setw(12) << td.m_global_output_index << '\t' << - get_transaction_hash(td.m_tx); - } - } - - if (!transfers_found) - { - if (!filter) - { - success_msg_writer() << "No incoming transfers"; - } - else if (available) - { - success_msg_writer() << "No incoming available transfers"; - } - else - { - success_msg_writer() << "No incoming unavailable transfers"; - } + bool hasTransfers = false; + size_t transactionsCount = m_wallet->getTransactionCount(); + for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { + TransactionInfo txInfo; + m_wallet->getTransaction(trantransactionNumber, txInfo); + if (txInfo.totalAmount < 0) continue; + hasTransfers = true; + message_writer() << " amount \t tx id"; + message_writer( epee::log_space::console_color_green, false) << // spent magenta + std::setw(21) << m_currency.formatAmount(txInfo.totalAmount) << '\t' << epee::string_tools::pod_to_hex(txInfo.hash); } + if (!hasTransfers) success_msg_writer() << "No incoming transfers"; return true; } bool simple_wallet::listTransfers(const std::vector& args) { - const std::vector& transfers = m_wallet->getTransfers(); - for (const tools::wallet2::Transfer& transfer : transfers) { - std::string address = "UNKNOWN"; - if (transfer.hasAddress) { - address = getAccountAddressAsStr(m_currency.publicAddressBase58Prefix(), transfer.address); + size_t transactionsCount = m_wallet->getTransactionCount(); + for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { + TransactionInfo txInfo; + m_wallet->getTransaction(trantransactionNumber, txInfo); + if (txInfo.state != TransactionState::Active) { + continue; + } + + std::string paymentIdStr = ""; + std::vector extraVec; + extraVec.reserve(txInfo.extra.size()); + std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); + + crypto::hash paymentId; + paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? epee::string_tools::pod_to_hex(paymentId) : ""); + + std::string address = ""; + if (txInfo.totalAmount < 0) { + if (txInfo.transferCount > 0) + { + Transfer tr; + m_wallet->getTransfer(txInfo.firstTransferId, tr); + address = tr.address; + } } - message_writer(transfer.output ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) - << transfer.time - << ", " << (transfer.output ? "OUTPUT" : "INPUT") - << ", " << transfer.transactionHash - << ", " << m_currency.formatAmount(transfer.amount) - << ", " << m_currency.formatAmount(transfer.fee) - << ", " << transfer.paymentId + message_writer(txInfo.totalAmount < 0 ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) + << txInfo.timestamp + << ", " << (txInfo.totalAmount < 0 ? "OUTPUT" : "INPUT") + << ", " << epee::string_tools::pod_to_hex(txInfo.hash) + << ", " << (txInfo.totalAmount < 0 ? "-" : "") << m_currency.formatAmount(abs(txInfo.totalAmount)) + << ", " << m_currency.formatAmount(txInfo.fee) + << ", " << paymentIdStr << ", " << address - << ", " << transfer.blockIndex - << ", " << transfer.unlockTime; + << ", " << txInfo.blockHeight + << ", " << txInfo.unlockTime; } + if (transactionsCount == 0) success_msg_writer() << "No transfers"; return true; } bool simple_wallet::show_payments(const std::vector &args) { - if(args.empty()) + if (args.empty()) { fail_msg_writer() << "expected at least one payment ID"; return true; @@ -825,34 +866,39 @@ bool simple_wallet::show_payments(const std::vector &args) message_writer() << " payment \t" << " transaction \t" << - " height\t amount \tunlock time"; + " height\t amount "; bool payments_found = false; - for(std::string arg : args) + for (const std::string& arg: args) { - crypto::hash payment_id; - if (tools::wallet2::parse_payment_id(arg, payment_id)) + crypto::hash expectedPaymentId; + if (cryptonote::parsePaymentId(arg, expectedPaymentId)) { - std::list payments; - m_wallet->get_payments(payment_id, payments); - if(payments.empty()) - { - success_msg_writer() << "No payments with id " << payment_id; - continue; - } - for (const tools::wallet2::payment_details& pd : payments) - { - if(!payments_found) - { + size_t transactionsCount = m_wallet->getTransactionCount(); + for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { + TransactionInfo txInfo; + m_wallet->getTransaction(trantransactionNumber, txInfo); + if (txInfo.totalAmount < 0) continue; + std::vector extraVec; + extraVec.reserve(txInfo.extra.size()); + std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); + + crypto::hash paymentId; + if (cryptonote::getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { payments_found = true; + success_msg_writer(true) << + paymentId << "\t\t" << + epee::string_tools::pod_to_hex(txInfo.hash) << + std::setw(8) << txInfo.blockHeight << '\t' << + std::setw(21) << m_currency.formatAmount(txInfo.totalAmount);// << '\t' << } - success_msg_writer(true) << - payment_id << '\t' << - pd.m_tx_hash << '\t' << - std::setw(8) << pd.m_block_height << '\t' << - std::setw(21) << m_currency.formatAmount(pd.m_amount) << '\t' << - pd.m_unlock_time; + } + + if (!payments_found) + { + success_msg_writer() << "No payments with id " << expectedPaymentId; + continue; } } else @@ -864,125 +910,82 @@ bool simple_wallet::show_payments(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- -uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) -{ - COMMAND_RPC_GET_HEIGHT::request req; - COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized(); - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client); - err = interpret_rpc_response(r, res.status); - return res.height; -} -//---------------------------------------------------------------------------------------------------- bool simple_wallet::show_blockchain_height(const std::vector& args) { - if (!try_connect_to_daemon()) - return true; - - std::string err; - uint64_t bc_height = get_daemon_blockchain_height(err); - if (err.empty()) + try { + uint64_t bc_height = m_node->getLastLocalBlockHeight(); success_msg_writer() << bc_height; - else - fail_msg_writer() << "failed to get blockchain height: " << err; + } catch (std::exception &e) { + fail_msg_writer() << "failed to get blockchain height: " << e.what(); + } + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector &args) { - if (!try_connect_to_daemon()) - return true; - try { TransferCommand cmd(m_currency); if (!cmd.parseArguments(args)) + return false; + cryptonote::WalletHelper::SendCompleteResultObserver sent; + std::promise txId; + sent.expectedTxID = txId.get_future(); + std::future f_sendError = sent.sendResult.get_future(); + std::string extraString; + std::copy(cmd.extra.begin(), cmd.extra.end(), std::back_inserter(extraString)); + + m_wallet->addObserver(&sent); + CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0); + if (tx == INVALID_TRANSACTION_ID) { + fail_msg_writer() << "Can't send money"; + return true; + } + txId.set_value(tx); + std::error_code sendError = f_sendError.get(); + m_wallet->removeObserver(&sent); + if (sendError) { + fail_msg_writer() << sendError.message(); return true; + } - cryptonote::Transaction tx; - m_wallet->transfer(cmd.dsts, cmd.fake_outs_count, 0, cmd.fee, cmd.extra, tx); - success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx); + CryptoNote::TransactionInfo txInfo; + m_wallet->getTransaction(tx, txInfo); + success_msg_writer(true) << "Money successfully sent, transaction " << epee::string_tools::pod_to_hex(txInfo.hash); try { - m_wallet->store(); + std::ofstream walletFile; + walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) { + fail_msg_writer() << "cant open " << m_wallet_file << " for save"; + return true; + } + m_saveResultPromise.reset(new std::promise()); + std::future f_saveError = m_saveResultPromise->get_future(); + m_wallet->save(walletFile); + auto saveError = f_saveError.get(); + m_saveResultPromise.reset(nullptr); + if (saveError) { + fail_msg_writer() << saveError.message(); + return true; + } } catch (const std::exception& e) { fail_msg_writer() << e.what(); - return false; - } - } - catch (const tools::error::daemon_busy&) - { - fail_msg_writer() << "daemon is busy. Please try later"; - } - catch (const tools::error::no_connection_to_daemon&) - { - fail_msg_writer() << "no connection to daemon. Please, make sure daemon is running."; - } - catch (const tools::error::wallet_rpc_error& e) - { - LOG_ERROR("Unknown RPC error: " << e.to_string()); - fail_msg_writer() << "RPC error \"" << e.what() << '"'; - } - catch (const tools::error::get_random_outs_error&) - { - fail_msg_writer() << "failed to get random outputs to mix"; - } - catch (const tools::error::not_enough_money& e) - { - fail_msg_writer() << "not enough money to transfer, available only " << m_currency.formatAmount(e.available()) << - ", transaction amount " << m_currency.formatAmount(e.tx_amount() + e.fee()) << " = " << - m_currency.formatAmount(e.tx_amount()) << " + " << m_currency.formatAmount(e.fee()) << " (fee)"; - } - catch (const tools::error::not_enough_outs_to_mix& e) - { - auto writer = fail_msg_writer(); - writer << "not enough outputs for specified mixin_count = " << e.mixin_count() << ":"; - for (const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& outs_for_amount : e.scanty_outs()) - { - writer << "\noutput amount = " << m_currency.formatAmount(outs_for_amount.amount) << - ", fount outputs to mix = " << outs_for_amount.outs.size(); + return true; } } - catch (const tools::error::tx_not_constructed&) - { - fail_msg_writer() << "transaction was not constructed"; - } - catch (const tools::error::tx_rejected& e) - { - fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " was rejected by daemon with status \"" << e.status() << '"'; - } - catch (const tools::error::tx_sum_overflow& e) - { - fail_msg_writer() << e.what(); - } - catch (const tools::error::tx_too_big& e) - { - cryptonote::Transaction tx = e.tx(); - fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " << - get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes"; - } - catch (const tools::error::zero_destination&) + catch (const std::system_error& e) { - fail_msg_writer() << "one of destinations is zero"; - } - catch (const tools::error::transfer_error& e) - { - LOG_ERROR("unknown transfer error: " << e.to_string()); - fail_msg_writer() << "unknown transfer error: " << e.what(); - } - catch (const tools::error::wallet_internal_error& e) - { - LOG_ERROR("internal error: " << e.to_string()); - fail_msg_writer() << "internal error: " << e.what(); + fail_msg_writer() << "unexpected error: " << e.what(); } catch (const std::exception& e) { - LOG_ERROR("unexpected error: " << e.what()); fail_msg_writer() << "unexpected error: " << e.what(); } catch (...) { - LOG_ERROR("Unknown error"); fail_msg_writer() << "unknown error"; } @@ -991,19 +994,19 @@ bool simple_wallet::transfer(const std::vector &args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::run() { - std::string addr_start = m_currency.accountAddressAsString(m_wallet->get_account()).substr(0, 6); + std::string addr_start = m_wallet->getAddress().substr(0, 6); return m_cmd_binder.run_handling("[wallet " + addr_start + "]: ", ""); } //---------------------------------------------------------------------------------------------------- void simple_wallet::stop() { m_cmd_binder.stop_handling(); - m_wallet->stop(); + m_wallet->shutdown(); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::print_address(const std::vector &args/* = std::vector()*/) { - success_msg_writer() << m_currency.accountAddressAsString(m_wallet->get_account()); + success_msg_writer() << m_wallet->getAddress(); return true; } //---------------------------------------------------------------------------------------------------- @@ -1012,14 +1015,13 @@ bool simple_wallet::process_command(const std::vector &args) return m_cmd_binder.process_command_vec(args); } //---------------------------------------------------------------------------------------------------- + int main(int argc, char* argv[]) { #ifdef WIN32 - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif - //TRY_ENTRY(); - string_tools::set_module_name_and_folder(argv[0]); po::options_description desc_general("General options"); @@ -1080,7 +1082,7 @@ int main(int argc, char* argv[]) message_writer(epee::log_space::console_color_white, true) << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; - if(command_line::has_arg(vm, arg_log_level)) + if (command_line::has_arg(vm, arg_log_level)) { LOG_PRINT_L0("Setting log level = " << command_line::get_arg(vm, arg_log_level)); log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); @@ -1090,29 +1092,29 @@ int main(int argc, char* argv[]) currencyBuilder.testnet(command_line::get_arg(vm, arg_testnet)); cryptonote::Currency currency = currencyBuilder.currency(); - if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) + if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); //runs wallet with rpc interface - if(!command_line::has_arg(vm, arg_wallet_file) ) + if (!command_line::has_arg(vm, arg_wallet_file)) { - LOG_ERROR("Wallet file not set."); + fail_msg_writer() << "Wallet file not set."; return 1; } - if(!command_line::has_arg(vm, arg_daemon_address) ) + if (!command_line::has_arg(vm, arg_daemon_address)) { - LOG_ERROR("Daemon address not set."); + fail_msg_writer() << "Daemon address not set."; return 1; } - if(!command_line::has_arg(vm, arg_password) ) + if (!command_line::has_arg(vm, arg_password)) { - LOG_ERROR("Wallet password not set."); + fail_msg_writer() << "Wallet password not set."; return 1; } - std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); + std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); std::string wallet_password = command_line::get_arg(vm, arg_password); - std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); + std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); int daemon_port = command_line::get_arg(vm, arg_daemon_port); if (daemon_host.empty()) @@ -1122,25 +1124,42 @@ int main(int argc, char* argv[]) if (daemon_address.empty()) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); - tools::wallet2 wal(currency); + + std::unique_ptr node; + + node.reset(new NodeRpcProxy(daemon_host, daemon_port)); + + std::promise errorPromise; + std::future error = errorPromise.get_future(); + auto callback = [&errorPromise](std::error_code e) {errorPromise.set_value(e); }; + node->init(callback); + if (error.get()) { + fail_msg_writer() << ("failed to init NodeRPCProxy"); + return 1; + } + + std::unique_ptr wallet; + + wallet.reset(new Wallet(currency, *node.get())); + std::string walletFileName; try { - LOG_PRINT_L0("Loading wallet..."); - wal.load(wallet_file, wallet_password); - wal.init(daemon_address); - wal.refresh(); + walletFileName = ::tryToOpenWalletOrLoadKeysOrThrow(wallet, wallet_file, wallet_password); + LOG_PRINT_L1("balance: " << currency.formatAmount(wallet->pendingBalance()) << + ", unlocked balance: " << currency.formatAmount(wallet->actualBalance())); LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0); } catch (const std::exception& e) { - LOG_ERROR("Wallet initialize failed: " << e.what()); + fail_msg_writer() << "Wallet initialize failed: " << e.what(); return 1; } - tools::wallet_rpc_server wrpc(wal); - bool r = wrpc.init(vm); + + tools::wallet_rpc_server wrpc(*wallet, *node, currency, walletFileName); + wrpc.init(vm); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server"); - tools::SignalHandler::install([&wrpc, &wal] { + tools::SignalHandler::install([&wrpc, &wallet] { wrpc.send_stop_signal(); }); LOG_PRINT_L0("Starting wallet rpc server"); @@ -1149,15 +1168,29 @@ int main(int argc, char* argv[]) try { LOG_PRINT_L0("Storing wallet..."); - wal.store(); + std::ofstream walletFile; + walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + return false; + WalletHelper::SaveWalletResultObserver saveObserver; + std::future f_saveError = saveObserver.saveResult.get_future(); + wallet->addObserver(&saveObserver); + wallet->save(walletFile); + auto saveError = f_saveError.get(); + wallet->removeObserver(&saveObserver); + if (saveError) { + fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + return 1; + } LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); } catch (const std::exception& e) { - LOG_ERROR("Failed to store wallet: " << e.what()); + fail_msg_writer() << "Failed to store wallet: " << e.what(); return 1; } - }else + } + else { //runs wallet with console interface cryptonote::simple_wallet wal(currency); @@ -1173,7 +1206,9 @@ int main(int argc, char* argv[]) }); wal.run(); - wal.deinit(); + if (!wal.deinit()) { + fail_msg_writer() << "Failed to close wallet"; + } } return 1; //CATCH_ENTRY_L0("main", 1); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 5256ce7e74..ff61848014 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -18,22 +18,25 @@ #pragma once #include +#include #include #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/Currency.h" -#include "wallet/wallet2.h" #include "console_handler.h" #include "password_container.h" - +#include "IWallet.h" +#include "INode.h" +#include "wallet/WalletHelper.h" +#include "net/http_client.h" namespace cryptonote { /************************************************************************/ /* */ /************************************************************************/ - class simple_wallet : public tools::i_wallet2_callback + class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletObserver { public: typedef std::vector command_type; @@ -62,7 +65,7 @@ namespace cryptonote bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); - bool refresh(const std::vector &args = std::vector()); + //bool refresh(const std::vector &args = std::vector()); bool show_balance(const std::vector &args = std::vector()); bool show_incoming_transfers(const std::vector &args); bool show_payments(const std::vector &args); @@ -74,15 +77,24 @@ namespace cryptonote bool reset(const std::vector &args); bool set_log(const std::vector &args); - uint64_t get_daemon_blockchain_height(std::string& err); - bool try_connect_to_daemon(); + //uint64_t get_daemon_blockchain_height(std::string& err); + //bool try_connect_to_daemon(); bool ask_wallet_create_if_needed(); - //----------------- i_wallet2_callback --------------------- - virtual void on_new_block(uint64_t height); - virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index); - virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx); - virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx); + ////----------------- i_wallet2_callback --------------------- + //virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index); + //virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx); + //virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx); + ////---------------------------------------------------------- + + //---------------- IWalletObserver ------------------------- + virtual void initCompleted(std::error_code result) override; + virtual void saveCompleted(std::error_code result) override; + virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override; + //---------------------------------------------------------- + + //----------------- INodeObserver -------------------------- + virtual void localBlockchainUpdated(uint64_t height) override; //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -109,7 +121,7 @@ namespace cryptonote if (std::chrono::milliseconds(1) < current_time - m_print_time || force) { - std::cout << "Height " << height << " of " << m_blockchain_height << '\r'; + LOG_PRINT_L0("Height " << height << " of " << m_blockchain_height << '\r'); m_print_time = current_time; } } @@ -118,7 +130,7 @@ namespace cryptonote void update_blockchain_height() { std::string err; - uint64_t blockchain_height = m_simple_wallet.get_daemon_blockchain_height(err); + uint64_t blockchain_height = m_simple_wallet.m_node->getLastLocalBlockHeight(); if (err.empty()) { m_blockchain_height = blockchain_height; @@ -138,7 +150,7 @@ namespace cryptonote }; private: - std::string m_wallet_file; + std::string m_wallet_file_arg; std::string m_generate_new; std::string m_import_path; @@ -146,10 +158,17 @@ namespace cryptonote std::string m_daemon_host; int m_daemon_port; + std::string m_wallet_file; + + std::unique_ptr> m_initResultPromise; + std::unique_ptr> m_saveResultPromise; + epee::console_handlers_binder m_cmd_binder; const cryptonote::Currency& m_currency; - std::unique_ptr m_wallet; + + std::unique_ptr m_node; + std::unique_ptr m_wallet; epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; }; diff --git a/src/transfers/BlockchainSynchronizer.cpp b/src/transfers/BlockchainSynchronizer.cpp new file mode 100644 index 0000000000..19c7c524a2 --- /dev/null +++ b/src/transfers/BlockchainSynchronizer.cpp @@ -0,0 +1,552 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockchainSynchronizer.h" +#include "cryptonote_core/TransactionApi.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include +#include +#include + + +namespace { + +inline std::vector stringToVector(const std::string& s) { + std::vector vec( + reinterpret_cast(s.data()), + reinterpret_cast(s.data()) + s.size()); + return vec; +} + +} + +namespace CryptoNote { + +BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const crypto::hash& genesisBlockHash) : +m_node(node), m_genesisBlockHash(genesisBlockHash), m_currentState(State::stopped), m_futureState(State::stopped), shouldSyncConsumersPool(true) { +} + +BlockchainSynchronizer::~BlockchainSynchronizer() { + stop(); +} + +void BlockchainSynchronizer::addConsumer(IBlockchainConsumer* consumer) { + assert(consumer != nullptr); + assert(m_consumers.count(consumer) == 0); + + if (!(checkIfStopped() && checkIfShouldStop())) { + throw std::runtime_error("Can't add consumer, because BlockchainSynchronizer isn't stopped"); + } + + m_consumers.insert(std::make_pair(consumer, std::make_shared(m_genesisBlockHash))); + shouldSyncConsumersPool = true; +} + +bool BlockchainSynchronizer::removeConsumer(IBlockchainConsumer* consumer) { + assert(consumer != nullptr); + + if (!(checkIfStopped() && checkIfShouldStop())) { + throw std::runtime_error("Can't remove consumer, because BlockchainSynchronizer isn't stopped"); + } + + return m_consumers.erase(consumer) > 0; +} + +IStreamSerializable* BlockchainSynchronizer::getConsumerState(IBlockchainConsumer* consumer) { + assert(consumer != nullptr); + + if (!(checkIfStopped() && checkIfShouldStop())) { + throw std::runtime_error("Can't get consumer state, because BlockchainSynchronizer isn't stopped"); + } + + std::unique_lock lk(m_consumersMutex); + + auto it = m_consumers.find(consumer); + if (it == m_consumers.end()) { + return nullptr; + } + + return it->second.get(); +} + + +void BlockchainSynchronizer::save(std::ostream& os) { + os.write(reinterpret_cast(&m_genesisBlockHash), sizeof(m_genesisBlockHash)); +} + +void BlockchainSynchronizer::load(std::istream& in) { + crypto::hash genesisBlockHash; + in.read(reinterpret_cast(&genesisBlockHash), sizeof(genesisBlockHash)); + if (genesisBlockHash != m_genesisBlockHash) { + throw std::runtime_error("Genesis block hash does not match stored state"); + } +} + +//--------------------------- FSM ------------------------------------ + +bool BlockchainSynchronizer::setFutureState(State s) { + return setFutureStateIf(s, std::bind( + [](State futureState, State s) -> bool { + return s > futureState; + }, std::ref(m_futureState), s)); +} + +bool BlockchainSynchronizer::setFutureStateIf(State s, std::function&& pred) { + std::unique_lock lk(m_stateMutex); + if (pred()) { + m_futureState = s; + return true; + } + + return false; +} + +void BlockchainSynchronizer::actualizeFutureState() { + std::unique_lock lk(m_stateMutex); + if (m_currentState == State::stopped && m_futureState == State::blockchainSync) { // start(), immideately attach observer + m_node.addObserver(this); + } + + if (m_futureState == State::stopped && m_currentState != State::stopped) { // stop(), immideately detach observer + m_node.removeObserver(this); + } + + m_currentState = m_futureState; + switch (m_futureState) { + case State::stopped: + m_futureState = State::stopped; + break; + case State::blockchainSync: + m_futureState = State::poolSync; + lk.unlock(); + startBlockchainSync(); + break; + case State::poolSync: + m_futureState = State::idle; + lk.unlock(); + startPoolSync(); + break; + case State::idle: + m_futureState = State::idle; + lk.unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + break; + default: + break; + } +} + +bool BlockchainSynchronizer::checkIfShouldStop() { + std::unique_lock lk(m_stateMutex); + return m_futureState == State::stopped; +} + +bool BlockchainSynchronizer::checkIfStopped() { + std::unique_lock lk(m_stateMutex); + return m_currentState == State::stopped; +} + + +void BlockchainSynchronizer::workingProcedure() { + while (!checkIfShouldStop()) { + actualizeFutureState(); + } + + actualizeFutureState(); +} + +void BlockchainSynchronizer::start() { + if (m_consumers.empty()) { + throw std::runtime_error("Can't start, because BlockchainSynchronizer has no consumers"); + } + + if (!setFutureStateIf(State::blockchainSync, std::bind( + [](State currentState, State futureState) -> bool { + return currentState == State::stopped && futureState == State::stopped; + }, std::ref(m_currentState), std::ref(m_futureState))) ) { + throw std::runtime_error("BlockchainSynchronizer already started"); + } + + shouldSyncConsumersPool = true; + + workingThread.reset(new std::thread([this] {this->workingProcedure(); })); +} + +void BlockchainSynchronizer::stop() { + setFutureState(State::stopped); + + // wait for previous processing to end + if (workingThread.get() != nullptr && workingThread->joinable()) { + workingThread->join(); + } + + workingThread.release(); +} + +void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint64_t height) { + setFutureState(State::blockchainSync); +} + +void BlockchainSynchronizer::poolChanged() { + setFutureState(State::poolSync); +} +//--------------------------- FSM END ------------------------------------ + +BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getUnionPoolHistory() { + GetPoolRequest request; + std::unordered_set unionHistory; + { + std::unique_lock lk(m_consumersMutex); + for (auto& consumer : m_consumers) { + std::vector consumerKnownIds; + consumer.first->getKnownPoolTxIds(consumerKnownIds); + for (auto& txId : consumerKnownIds) { + unionHistory.insert(txId); + } + } + } + + for (auto& id : unionHistory) { + request.knownTxIds.push_back(id); + } + + request.lastKnownBlock = lastBlockId; + return request; +} + +BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getIntersectedPoolHistory() { + GetPoolRequest request; + { + std::unique_lock lk(m_consumersMutex); + auto it = m_consumers.begin(); + + it->first->getKnownPoolTxIds(request.knownTxIds); + ++it; + + for (; it != m_consumers.end(); ++it) { //iterate over consumers + std::vector consumerKnownIds; + it->first->getKnownPoolTxIds(consumerKnownIds); + for (auto itReq = request.knownTxIds.begin(); itReq != request.knownTxIds.end(); ) { //iterate over intersection + if (std::count(consumerKnownIds.begin(), consumerKnownIds.end(), *itReq) == 0) { //consumer doesn't contain id from intersection, so delete this id from intersection + itReq = request.knownTxIds.erase(itReq); + } else { + ++itReq; + } + } + } + } + + request.lastKnownBlock = lastBlockId; + return request; +} + +BlockchainSynchronizer::GetBlocksRequest BlockchainSynchronizer::getCommonHistory() { + GetBlocksRequest request; + std::unique_lock lk(m_consumersMutex); + if (m_consumers.empty()) { + return request; + } + + auto shortest = m_consumers.begin(); + auto syncStart = shortest->first->getSyncStart(); + auto it = shortest; + ++it; + for (; it != m_consumers.end(); ++it) { + if (it->second->getHeight() < shortest->second->getHeight()) { + shortest = it; + } + + auto consumerStart = it->first->getSyncStart(); + syncStart.timestamp = std::min(syncStart.timestamp, consumerStart.timestamp); + syncStart.height = std::min(syncStart.height, consumerStart.height); + } + + request.knownBlocks = shortest->second->getShortHistory(); + request.syncStart = syncStart; + return request; +} + +void BlockchainSynchronizer::startBlockchainSync() { + GetBlocksResponse response; + GetBlocksRequest req = getCommonHistory(); + + try { + if (!req.knownBlocks.empty()) { + asyncOperationCompleted = std::promise(); + asyncOperationWaitFuture = asyncOperationCompleted.get_future(); + + m_node.queryBlocks(std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight, + std::bind(&BlockchainSynchronizer::onGetBlocksCompleted, this, std::placeholders::_1)); + + std::error_code ec = asyncOperationWaitFuture.get(); + + if (ec) { + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + ec); + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); + } else { + processBlocks(response); + } + } + } catch (std::exception& e) { + std::cout << e.what()<< std::endl; + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + std::make_error_code(std::errc::invalid_argument)); + } +} + +void BlockchainSynchronizer::onGetBlocksCompleted(std::error_code ec) { + decltype(asyncOperationCompleted) detachedPromise = std::move(asyncOperationCompleted); + detachedPromise.set_value(ec); +} + +void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { + auto newHeight = response.startHeight + response.newBlocks.size(); + BlockchainInterval interval; + interval.startHeight = response.startHeight; + std::vector blocks; + + // parse blocks + for (const auto& block : response.newBlocks) { + if (checkIfShouldStop()) { + break; + } + CompleteBlock completeBlock; + completeBlock.blockHash = block.blockHash; + interval.blocks.push_back(completeBlock.blockHash); + if (!block.block.empty()) { + cryptonote::Block parsedBlock; + if (!cryptonote::parse_and_validate_block_from_blob(block.block, parsedBlock)) { + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + std::make_error_code(std::errc::invalid_argument)); + return; + } + + completeBlock.block = std::move(parsedBlock); + + completeBlock.transactions.push_back(createTransaction(completeBlock.block->minerTx)); + + try { + for (const auto& txblob : block.txs) { + completeBlock.transactions.push_back(createTransaction(stringToVector(txblob))); + } + } catch (std::exception &) { + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + std::make_error_code(std::errc::invalid_argument)); + return; + } + } + + blocks.push_back(std::move(completeBlock)); + } + + if (!checkIfShouldStop()) { + response.newBlocks.clear(); + std::unique_lock lk(m_consumersMutex); + auto result = updateConsumers(interval, blocks); + lk.unlock(); + + switch (result) { + case UpdateConsumersResult::errorOccured: + if (setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState)))) { + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + std::make_error_code(std::errc::invalid_argument)); + } + + break; + case UpdateConsumersResult::nothingChanged: + if (m_node.getLastKnownBlockHeight() > newHeight) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } else { + break; + } + case UpdateConsumersResult::addedNewBlocks: + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationProgressUpdated, + newHeight, + m_node.getLastKnownBlockHeight()); + setFutureState(State::blockchainSync); + break; + } + + if (!blocks.empty()) { + lastBlockId = blocks.back().blockHash; + } + } + + if (checkIfShouldStop()) { //Sic! + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + std::make_error_code(std::errc::interrupted)); + } +} + +BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateConsumers(const BlockchainInterval& interval, const std::vector& blocks) { + bool smthChanged = false; + + for (auto& kv : m_consumers) { + auto result = kv.second->checkInterval(interval); + + if (result.detachRequired) { + kv.first->onBlockchainDetach(result.detachHeight); + kv.second->detach(result.detachHeight); + } + + if (result.hasNewBlocks) { + size_t startOffset = result.newBlockHeight - interval.startHeight; + // update consumer + if (kv.first->onNewBlocks( + blocks.data() + startOffset, + result.newBlockHeight, + blocks.size() - startOffset)) { + // update state if consumer succeeded + kv.second->addBlocks( + interval.blocks.data() + startOffset, + result.newBlockHeight, + interval.blocks.size() - startOffset); + smthChanged = true; + } else { + return UpdateConsumersResult::errorOccured; + } + } + } + + return smthChanged ? UpdateConsumersResult::addedNewBlocks : UpdateConsumersResult::nothingChanged; +} + +void BlockchainSynchronizer::startPoolSync() { + GetPoolResponse unionResponse; + GetPoolRequest unionRequest = getUnionPoolHistory(); + + asyncOperationCompleted = std::promise(); + asyncOperationWaitFuture = asyncOperationCompleted.get_future(); + + unionResponse.isLastKnownBlockActual = false; + + m_node.getPoolSymmetricDifference(std::move(unionRequest.knownTxIds), std::move(unionRequest.lastKnownBlock), unionResponse.isLastKnownBlockActual, + unionResponse.newTxs, unionResponse.deletedTxIds, std::bind(&BlockchainSynchronizer::onGetPoolChanges, this, std::placeholders::_1)); + + std::error_code ec = asyncOperationWaitFuture.get(); + + if (ec) { + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + ec); + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); + } else { //get union ok + if (!unionResponse.isLastKnownBlockActual) { //bc outdated + setFutureState(State::blockchainSync); + } else { + if (!shouldSyncConsumersPool) { //usual case, start pool processing + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + processPoolTxs(unionResponse)); + } else {// first launch, we should sync consumers' pools, so let's ask for intersection + GetPoolResponse intersectionResponse; + GetPoolRequest intersectionRequest = getIntersectedPoolHistory(); + + asyncOperationCompleted = std::promise(); + asyncOperationWaitFuture = asyncOperationCompleted.get_future(); + + intersectionResponse.isLastKnownBlockActual = false; + + m_node.getPoolSymmetricDifference(std::move(intersectionRequest.knownTxIds), std::move(intersectionRequest.lastKnownBlock), intersectionResponse.isLastKnownBlockActual, + intersectionResponse.newTxs, intersectionResponse.deletedTxIds, std::bind(&BlockchainSynchronizer::onGetPoolChanges, this, std::placeholders::_1)); + + std::error_code ec2 = asyncOperationWaitFuture.get(); + + if (ec2) { + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + ec2); + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); + } else { //get intersection ok + if (!intersectionResponse.isLastKnownBlockActual) { //bc outdated + setFutureState(State::blockchainSync); + } else { + intersectionResponse.deletedTxIds.assign(unionResponse.deletedTxIds.begin(), unionResponse.deletedTxIds.end()); + std::error_code ec3 = processPoolTxs(intersectionResponse); + + //notify about error, or success + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + ec3); + + if (!ec3) { + shouldSyncConsumersPool = false; + } + } + } + } + } + } +} + +void BlockchainSynchronizer::onGetPoolChanges(std::error_code ec) { + decltype(asyncOperationCompleted) detachedPromise = std::move(asyncOperationCompleted); + detachedPromise.set_value(ec); +} + +std::error_code BlockchainSynchronizer::processPoolTxs(GetPoolResponse& response) { + std::error_code error; + { + std::unique_lock lk(m_consumersMutex); + for (auto& consumer : m_consumers) { + if (checkIfShouldStop()) { //if stop, return immediately, without notification + return std::make_error_code(std::errc::interrupted); + } + + error = consumer.first->onPoolUpdated(response.newTxs, response.deletedTxIds); + if (error) { + break; + } + } + } + + return error; +} + +} diff --git a/src/transfers/BlockchainSynchronizer.h b/src/transfers/BlockchainSynchronizer.h new file mode 100644 index 0000000000..ab8af4f57f --- /dev/null +++ b/src/transfers/BlockchainSynchronizer.h @@ -0,0 +1,144 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "INode.h" +#include "SynchronizationState.h" +#include "IBlockchainSynchronizer.h" +#include "IObservableImpl.h" +#include "IStreamSerializable.h" + +#include +#include +#include +#include + +namespace CryptoNote { + +class BlockchainSynchronizer : + public IObservableImpl, + public INodeObserver { +public: + + BlockchainSynchronizer(INode& node, const crypto::hash& genesisBlockHash); + ~BlockchainSynchronizer(); + + // IBlockchainSynchronizer + virtual void addConsumer(IBlockchainConsumer* consumer) override; + virtual bool removeConsumer(IBlockchainConsumer* consumer) override; + virtual IStreamSerializable* getConsumerState(IBlockchainConsumer* consumer) override; + + virtual void start() override; + virtual void stop() override; + + // IStreamSerializable + virtual void save(std::ostream& os) override; + virtual void load(std::istream& in) override; + + // INodeObserver + virtual void lastKnownBlockHeightUpdated(uint64_t height) override; + virtual void poolChanged() override; + +private: + + struct GetBlocksResponse { + uint64_t startHeight; + std::list newBlocks; + }; + + struct GetBlocksRequest { + GetBlocksRequest() { + syncStart.timestamp = 0; + syncStart.height = 0; + } + SynchronizationStart syncStart; + std::list knownBlocks; + }; + + struct GetPoolResponse { + bool isLastKnownBlockActual; + std::vector newTxs; + std::vector deletedTxIds; + }; + + struct GetPoolRequest { + std::vector knownTxIds; + crypto::hash lastKnownBlock; + }; + + + enum class State { //prioritized finite states + idle = 0, //DO + poolSync = 1, //NOT + blockchainSync = 2, //REORDER + stopped = 3 //!!! + }; + + enum class UpdateConsumersResult { + nothingChanged = 0, + addedNewBlocks = 1, + errorOccured = 2 + }; + + //void startSync(); + void startPoolSync(); + void startBlockchainSync(); + + void onGetBlocksCompleted(std::error_code ec); + void processBlocks(GetBlocksResponse& response); + UpdateConsumersResult updateConsumers(const BlockchainInterval& interval, const std::vector& blocks); + void onGetPoolChanges(std::error_code ec); + std::error_code processPoolTxs(GetPoolResponse& response); + + ///second parameter is used only in case of errors returned into callback from INode, such as aborted or connection lost + bool setFutureState(State s); + bool setFutureStateIf(State s, std::function&& pred); + + void actualizeFutureState(); + bool checkIfShouldStop(); + bool checkIfStopped(); + + void workingProcedure(); + + GetBlocksRequest getCommonHistory(); + GetPoolRequest getUnionPoolHistory(); + GetPoolRequest getIntersectedPoolHistory(); + + typedef std::map> ConsumersMap; + + ConsumersMap m_consumers; + INode& m_node; + const crypto::hash m_genesisBlockHash; + + std::vector knownTxIds; + crypto::hash lastBlockId; + + State m_currentState; + State m_futureState; + std::unique_ptr workingThread; + + std::future asyncOperationWaitFuture; + std::promise asyncOperationCompleted; + + std::mutex m_consumersMutex; + std::mutex m_stateMutex; + + bool shouldSyncConsumersPool; +}; + +} diff --git a/src/wallet/WalletTxSendingState.cpp b/src/transfers/CommonTypes.h similarity index 62% rename from src/wallet/WalletTxSendingState.cpp rename to src/transfers/CommonTypes.h index f142330880..e96aef14ba 100644 --- a/src/wallet/WalletTxSendingState.cpp +++ b/src/transfers/CommonTypes.h @@ -15,28 +15,29 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletTxSendingState.h" +#pragma once -namespace CryptoNote { +#include +#include +#include -void WalletTxSendingState::sending(TransactionId id) { - m_states[id] = SENDING; -} +#include -void WalletTxSendingState::sent(TransactionId id) { - m_states.erase(id); -} +#include "INode.h" +#include "ITransaction.h" -void WalletTxSendingState::error(TransactionId id) { - m_states[id] = ERRORED; -} +namespace CryptoNote { -WalletTxSendingState::State WalletTxSendingState::state(TransactionId id) { - auto it = m_states.find(id); +struct BlockchainInterval { + uint64_t startHeight; + std::vector blocks; +}; - if (it == m_states.end()) - return NOT_FOUND; +struct CompleteBlock { + crypto::hash blockHash; + boost::optional block; + // first transaction is always coinbase + std::list> transactions; +}; - return it->second; } -} //namespace CryptoNote diff --git a/src/transfers/IBlockchainSynchronizer.h b/src/transfers/IBlockchainSynchronizer.h new file mode 100644 index 0000000000..92ba1f3297 --- /dev/null +++ b/src/transfers/IBlockchainSynchronizer.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_basic.h" + +#include "IObservable.h" +#include "IStreamSerializable.h" +#include "ITransfersSynchronizer.h" + +namespace CryptoNote { + +struct CompleteBlock; + +class IBlockchainSynchronizerObserver { +public: + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} + virtual void synchronizationCompleted(std::error_code result) {} +}; + +class IBlockchainConsumer { +public: + + virtual SynchronizationStart getSyncStart() = 0; + virtual void getKnownPoolTxIds(std::vector& ids) = 0; + virtual void onBlockchainDetach(uint64_t height) = 0; + virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) = 0; + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) = 0; +}; + + +class IBlockchainSynchronizer : + public IObservable, + public IStreamSerializable { +public: + virtual void addConsumer(IBlockchainConsumer* consumer) = 0; + virtual bool removeConsumer(IBlockchainConsumer* consumer) = 0; + virtual IStreamSerializable* getConsumerState(IBlockchainConsumer* consumer) = 0; + + virtual void start() = 0; + virtual void stop() = 0; +}; + +} diff --git a/src/transfers/IObservableImpl.h b/src/transfers/IObservableImpl.h new file mode 100644 index 0000000000..13f048b057 --- /dev/null +++ b/src/transfers/IObservableImpl.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "common/ObserverManager.h" + +namespace CryptoNote { + +template +class IObservableImpl : public Base { +public: + + virtual void addObserver(Observer* observer) override { + m_observerManager.add(observer); + } + + virtual void removeObserver(Observer* observer) override { + m_observerManager.remove(observer); + } + +protected: + tools::ObserverManager m_observerManager; +}; + +} diff --git a/src/transfers/SerializationHelpers.h b/src/transfers/SerializationHelpers.h new file mode 100644 index 0000000000..c44b1efbca --- /dev/null +++ b/src/transfers/SerializationHelpers.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "serialization/ISerializer.h" + +namespace cryptonote { + +template +void writeSequence(Iterator begin, Iterator end, const std::string& name, ISerializer& s) { + size_t size = std::distance(begin, end); + s.beginArray(size, name); + for (Iterator i = begin; i != end; ++i) { + s(const_cast(*i), ""); + } + s.endArray(); +} + +template +void readSequence(Iterator outputIterator, const std::string& name, ISerializer& s) { + size_t size = 0; + s.beginArray(size, name); + + while (size--) { + Element e; + s(e, ""); + *outputIterator++ = e; + } + + s.endArray(); +} + +} diff --git a/src/transfers/SynchronizationState.cpp b/src/transfers/SynchronizationState.cpp new file mode 100644 index 0000000000..0f48286d8b --- /dev/null +++ b/src/transfers/SynchronizationState.cpp @@ -0,0 +1,120 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "SynchronizationState.h" + +#include "serialization/BinaryInputStreamSerializer.h" +#include "serialization/BinaryOutputStreamSerializer.h" +#include "cryptonote_core/cryptonote_serialization.h" + +namespace CryptoNote { + +SynchronizationState::ShortHistory SynchronizationState::getShortHistory() const { + + ShortHistory history; + size_t i = 0; + size_t current_multiplier = 1; + size_t sz = m_blockchain.size(); + + if (!sz) + return history; + + size_t current_back_offset = 1; + bool genesis_included = false; + + while (current_back_offset < sz) { + history.push_back(m_blockchain[sz - current_back_offset]); + if (sz - current_back_offset == 0) + genesis_included = true; + if (i < 10) { + ++current_back_offset; + } else { + current_back_offset += current_multiplier *= 2; + } + ++i; + } + + if (!genesis_included) + history.push_back(m_blockchain[0]); + + return history; +} + +SynchronizationState::CheckResult SynchronizationState::checkInterval(const BlockchainInterval& interval) const { + assert(interval.startHeight <= m_blockchain.size()); + + CheckResult result = { false, 0, false, 0 }; + + size_t intervalEnd = interval.startHeight + interval.blocks.size(); + size_t iterationEnd = std::min(m_blockchain.size(), intervalEnd); + + for (size_t i = interval.startHeight; i < iterationEnd; ++i) { + if (m_blockchain[i] != interval.blocks[i - interval.startHeight]) { + result.detachRequired = true; + result.detachHeight = i; + break; + } + } + + if (result.detachRequired) { + result.hasNewBlocks = true; + result.newBlockHeight = result.detachHeight; + return result; + } + + if (intervalEnd > m_blockchain.size()) { + result.hasNewBlocks = true; + result.newBlockHeight = m_blockchain.size(); + } + + return result; +} + +void SynchronizationState::detach(uint64_t height) { + assert(height < m_blockchain.size()); + m_blockchain.resize(height); +} + +void SynchronizationState::addBlocks(const crypto::hash* blockHashes, uint64_t height, size_t count) { + assert(blockHashes); + auto size = m_blockchain.size(); + assert( size == height); + m_blockchain.insert(m_blockchain.end(), blockHashes, blockHashes + count); +} + +uint64_t SynchronizationState::getHeight() const { + return m_blockchain.size(); +} + +void SynchronizationState::save(std::ostream& os) { + cryptonote::BinaryOutputStreamSerializer s(os); + serialize(s, "state"); +} + +void SynchronizationState::load(std::istream& in) { + cryptonote::BinaryInputStreamSerializer s(in); + serialize(s, "state"); +} + +cryptonote::ISerializer& SynchronizationState::serialize(cryptonote::ISerializer& s, const std::string& name) { + s.beginObject(name); + s(m_blockchain, "blockchain"); + s.endObject(); + return s; +} + +} diff --git a/src/transfers/SynchronizationState.h b/src/transfers/SynchronizationState.h new file mode 100644 index 0000000000..9cfc8ad4ec --- /dev/null +++ b/src/transfers/SynchronizationState.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "CommonTypes.h" +#include "IStreamSerializable.h" +#include "serialization/ISerializer.h" +#include +#include + +namespace CryptoNote { + +class SynchronizationState : public IStreamSerializable { +public: + + struct CheckResult { + bool detachRequired; + uint64_t detachHeight; + bool hasNewBlocks; + uint64_t newBlockHeight; + }; + + typedef std::list ShortHistory; + + explicit SynchronizationState(const crypto::hash& genesisBlockHash) { + m_blockchain.push_back(genesisBlockHash); + } + + ShortHistory getShortHistory() const; + CheckResult checkInterval(const BlockchainInterval& interval) const; + + void detach(uint64_t height); + void addBlocks(const crypto::hash* blockHashes, uint64_t height, size_t count); + uint64_t getHeight() const; + + // IStreamSerializable + virtual void save(std::ostream& os) override; + virtual void load(std::istream& in) override; + + // serialization + cryptonote::ISerializer& serialize(cryptonote::ISerializer& s, const std::string& name); + +private: + + std::vector m_blockchain; +}; + +} diff --git a/src/transfers/TransfersConsumer.cpp b/src/transfers/TransfersConsumer.cpp new file mode 100644 index 0000000000..bc12d64af4 --- /dev/null +++ b/src/transfers/TransfersConsumer.cpp @@ -0,0 +1,464 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransfersConsumer.h" +#include "CommonTypes.h" + +#include "common/BlockingQueue.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/TransactionApi.h" + +#include "IWallet.h" +#include "INode.h" +#include + +namespace { + +using namespace CryptoNote; + +void checkOutputKey( + const crypto::key_derivation& derivation, + const PublicKey& key, + size_t keyIndex, + size_t outputIndex, + const std::unordered_set& spendKeys, + std::unordered_map>& outputs) { + + PublicKey spendKey; + crypto::underive_public_key(derivation, keyIndex, + reinterpret_cast(key), + reinterpret_cast(spendKey)); + + if (spendKeys.find(spendKey) != spendKeys.end()) { + outputs[spendKey].push_back(static_cast(outputIndex)); + } + +} + +void findMyOutputs( + const ITransactionReader& tx, + const SecretKey& viewSecretKey, + const std::unordered_set& spendKeys, + std::unordered_map>& outputs) { + + auto txPublicKey = tx.getTransactionPublicKey(); + crypto::key_derivation derivation; + + if (!crypto::generate_key_derivation( + reinterpret_cast(txPublicKey), + reinterpret_cast(viewSecretKey), + derivation)) { + return; + } + + size_t keyIndex = 0; + size_t outputCount = tx.getOutputCount(); + + for (size_t idx = 0; idx < outputCount; ++idx) { + + auto outType = tx.getOutputType(size_t(idx)); + + if (outType == TransactionTypes::OutputType::Key) { + + TransactionTypes::OutputKey out; + tx.getOutput(idx, out); + checkOutputKey(derivation, out.key, keyIndex, idx, spendKeys, outputs); + ++keyIndex; + + } else if (outType == TransactionTypes::OutputType::Multisignature) { + + TransactionTypes::OutputMultisignature out; + tx.getOutput(idx, out); + for (const auto& key : out.keys) { + checkOutputKey(derivation, key, idx, idx, spendKeys, outputs); + ++keyIndex; + } + } + } +} + +} + +namespace CryptoNote { + +TransfersConsumer::TransfersConsumer(const cryptonote::Currency& currency, INode& node, const SecretKey& viewSecret) : + m_node(node), m_viewSecret(viewSecret), m_currency(currency) { + updateSyncStart(); +} + +ITransfersSubscription& TransfersConsumer::addSubscription(const AccountSubscription& subscription) { + if (subscription.keys.viewSecretKey != m_viewSecret) { + throw std::runtime_error("TransfersConsumer: view secret key mismatch"); + } + + auto& res = m_subscriptions[subscription.keys.address.spendPublicKey]; + + if (res.get() == nullptr) { + res.reset(new TransfersSubscription(m_currency, subscription)); + m_spendKeys.insert(subscription.keys.address.spendPublicKey); + updateSyncStart(); + } + + return *res; +} + +bool TransfersConsumer::removeSubscription(const AccountAddress& address) { + m_subscriptions.erase(address.spendPublicKey); + m_spendKeys.erase(address.spendPublicKey); + updateSyncStart(); + return m_subscriptions.empty(); +} + +ITransfersSubscription* TransfersConsumer::getSubscription(const AccountAddress& acc) { + auto it = m_subscriptions.find(acc.spendPublicKey); + return it == m_subscriptions.end() ? nullptr : it->second.get(); +} + +void TransfersConsumer::getSubscriptions(std::vector& subscriptions) { + for (const auto& kv : m_subscriptions) { + subscriptions.push_back(kv.second->getAddress()); + } +} + +void TransfersConsumer::updateSyncStart() { + SynchronizationStart start; + + start.height = std::numeric_limits::max(); + start.timestamp = std::numeric_limits::max(); + + for (const auto& kv : m_subscriptions) { + auto subStart = kv.second->getSyncStart(); + start.height = std::min(start.height, subStart.height); + start.timestamp = std::min(start.timestamp, subStart.timestamp); + } + + m_syncStart = start; +} + +SynchronizationStart TransfersConsumer::getSyncStart() { + return m_syncStart; +} + +void TransfersConsumer::onBlockchainDetach(uint64_t height) { + for (const auto& kv : m_subscriptions) { + kv.second->onBlockchainDetach(height); + } +} + +bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) { + assert(blocks); + + struct Tx { + BlockInfo blockInfo; + const ITransactionReader* tx; + }; + + struct PreprocessedTx : Tx, PreprocessInfo {}; + + std::vector preprocessedTransactions; + std::mutex preprocessedTransactionsMutex; + + size_t workers = std::thread::hardware_concurrency(); + if (workers == 0) { + workers = 2; + } + + BlockingQueue inputQueue(workers * 2); + + std::atomic stopProcessing(false); + + auto pushingThread = std::async(std::launch::async, [&] { + for (size_t i = 0; i < count && !stopProcessing; ++i) { + const auto& block = blocks[i].block; + + if (!block.is_initialized()) { + continue; + } + + // filter by syncStartTimestamp + if (m_syncStart.timestamp && block->timestamp < m_syncStart.timestamp) { + continue; + } + + BlockInfo blockInfo; + blockInfo.height = startHeight + i; + blockInfo.timestamp = block->timestamp; + blockInfo.transactionIndex = 0; // position in block + + for (const auto& tx : blocks[i].transactions) { + auto pubKey = tx->getTransactionPublicKey(); + if (*reinterpret_cast(&pubKey) == cryptonote::null_pkey) { + continue; + } + + Tx item = { blockInfo, tx.get() }; + inputQueue.push(item); + ++blockInfo.transactionIndex; + } + } + + inputQueue.close(); + }); + + auto processingFunction = [&] { + Tx item; + std::error_code ec; + while (!stopProcessing && inputQueue.pop(item)) { + PreprocessedTx output; + static_cast(output) = item; + + ec = preprocessOutputs(item.blockInfo, *item.tx, output); + if (ec) { + stopProcessing = true; + break; + } + + std::lock_guard lk(preprocessedTransactionsMutex); + preprocessedTransactions.push_back(std::move(output)); + } + return ec; + }; + + std::vector> processingThreads; + for (size_t i = 0; i < workers; ++i) { + processingThreads.push_back(std::async(std::launch::async, processingFunction)); + } + + std::error_code processingError; + for (auto& f : processingThreads) { + try { + std::error_code ec = f.get(); + if (!processingError && ec) { + processingError = ec; + } + } catch (const std::system_error& e) { + processingError = e.code(); + } catch (const std::exception& e) { + processingError = std::make_error_code(std::errc::operation_canceled); + } + } + + if (!processingError) { + // sort by block height and transaction index in block + std::sort(preprocessedTransactions.begin(), preprocessedTransactions.end(), [](const PreprocessedTx& a, const PreprocessedTx& b) { + return std::tie(a.blockInfo.height, a.blockInfo.transactionIndex) < std::tie(b.blockInfo.height, b.blockInfo.transactionIndex); + }); + + for (const auto& tx : preprocessedTransactions) { + processingError = processTransaction(tx.blockInfo, *tx.tx, tx); + if (processingError) { + break; + } + } + } + + if (processingError) { + forEachSubscription([&](TransfersSubscription& sub) { + sub.onError(processingError, startHeight); + }); + return false; + } + + auto newHeight = startHeight + count; + forEachSubscription([newHeight](TransfersSubscription& sub) { + sub.advanceHeight(newHeight); + }); + + return true; +} + +std::error_code TransfersConsumer::onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) { + BlockInfo unconfirmedBlockInfo; + unconfirmedBlockInfo.timestamp = 0; + unconfirmedBlockInfo.height = UNCONFIRMED_TRANSACTION_HEIGHT; + std::error_code processingError; + for (auto& cryptonoteTransaction : addedTransactions) { + auto transaction = CryptoNote::createTransaction(cryptonoteTransaction); + processingError = processTransaction(unconfirmedBlockInfo, *transaction.get()); + if (processingError) { + break; + } + } + + if (processingError) { + for (auto& sub : m_subscriptions) { + sub.second->onError(processingError, UNCONFIRMED_TRANSACTION_HEIGHT); + } + + return processingError; + } + + for (auto& deletedTxHash : deletedTransactions) { + for (auto& sub: m_subscriptions) { + sub.second->deleteUnconfirmedTransaction(*reinterpret_cast(&deletedTxHash)); + } + } + return std::error_code(); +} + +void TransfersConsumer::getKnownPoolTxIds(std::vector& ids) { + ids.clear(); + std::unordered_set knownIds; + for (auto& sub : m_subscriptions) { + std::vector subscriptionUnconfirmedTxIds; + sub.second->getContainer().getUnconfirmedTransactions(subscriptionUnconfirmedTxIds); + knownIds.insert(subscriptionUnconfirmedTxIds.begin(), subscriptionUnconfirmedTxIds.end()); + } + + ids.assign(knownIds.begin(), knownIds.end()); +} + +std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info) { + findMyOutputs(tx, m_viewSecret, m_spendKeys, info.outputs); + + std::error_code errorCode; + if (!info.outputs.empty()) { + auto txHash = tx.getTransactionHash(); + if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); + if (errorCode) { + return errorCode; + } + } + } + + return std::error_code(); +} + +std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx) { + PreprocessInfo info; + auto ec = preprocessOutputs(blockInfo, tx, info); + if (ec) { + return ec; + } + + return processTransaction(blockInfo, tx, info); +} + + +std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) { + std::error_code errorCode; + static std::vector emptyOutputs; + for (auto& kv : m_subscriptions) { + auto it = info.outputs.find(kv.first); + auto& subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it->second; + errorCode = processOutputs(blockInfo, *kv.second, tx, subscriptionOutputs, info.globalIdxs); + if (errorCode) { + return errorCode; + } + } + + return std::error_code(); +} + +std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, + const ITransactionReader& tx, const std::vector& outputs, const std::vector& globalIdxs) { + + if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + TransactionInformation subscribtionTxInfo; + int64_t txBalance; + if (sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo, txBalance)) { + if (subscribtionTxInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + // pool->blockchain + sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); + return std::error_code(); + } else { + // - Subscription already has this transaction as confirmed, so why are we here? + // - Because, for instance, some another subscription doesn't have this transactions, so it is given to us again. + return std::error_code(); + } + } + } + + std::vector transfers; + + auto txPubKey = tx.getTransactionPublicKey(); + + for (auto idx : outputs) { + + if (idx >= tx.getOutputCount()) { + return std::make_error_code(std::errc::argument_out_of_domain); + } + + auto outType = tx.getOutputType(size_t(idx)); + + if ( + outType != TransactionTypes::OutputType::Key && + outType != TransactionTypes::OutputType::Multisignature) { + continue; + } + + TransactionOutputInformationIn info; + + info.type = outType; + info.transactionPublicKey = txPubKey; + info.outputInTransaction = idx; + info.globalOutputIndex = + (blockInfo.height == UNCONFIRMED_TRANSACTION_HEIGHT) ? + UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : + globalIdxs[idx]; + + if (outType == TransactionTypes::OutputType::Key) { + TransactionTypes::OutputKey out; + tx.getOutput(idx, out); + + cryptonote::KeyPair in_ephemeral; + cryptonote::generate_key_image_helper( + reinterpret_cast(sub.getKeys()), + reinterpret_cast(txPubKey), + idx, + in_ephemeral, + reinterpret_cast(info.keyImage)); + + assert(out.key == reinterpret_cast(in_ephemeral.pub)); + + info.amount = out.amount; + info.outputKey = out.key; + + } else if (outType == TransactionTypes::OutputType::Multisignature) { + TransactionTypes::OutputMultisignature out; + tx.getOutput(idx, out); + + info.amount = out.amount; + info.requiredSignatures = out.requiredSignatures; + } + + transfers.push_back(info); + } + + sub.addTransaction(blockInfo, tx, transfers); + + return std::error_code(); +} + + +std::error_code TransfersConsumer::getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { + std::promise prom; + std::future f = prom.get_future(); + + INode::Callback cb = [&prom](std::error_code ec) { + std::promise p(std::move(prom)); + p.set_value(std::move(ec)); + }; + + outsGlobalIndices.clear(); + m_node.getTransactionOutsGlobalIndices(transactionHash, outsGlobalIndices, cb); + + return f.get(); +} + +} diff --git a/src/transfers/TransfersConsumer.h b/src/transfers/TransfersConsumer.h new file mode 100644 index 0000000000..638994d376 --- /dev/null +++ b/src/transfers/TransfersConsumer.h @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IBlockchainSynchronizer.h" +#include "ITransfersSynchronizer.h" +#include "TransfersSubscription.h" +#include "TypeHelpers.h" + +#include "crypto/crypto.h" + +#include "IObservableImpl.h" + +#include + +namespace CryptoNote { + +class INode; + +class TransfersConsumer : public IBlockchainConsumer { + +public: + + TransfersConsumer(const cryptonote::Currency& currency, INode& node, const SecretKey& viewSecret); + + ITransfersSubscription& addSubscription(const AccountSubscription& subscription); + // returns true if no subscribers left + bool removeSubscription(const AccountAddress& address); + ITransfersSubscription* getSubscription(const AccountAddress& acc); + void getSubscriptions(std::vector& subscriptions); + + // IBlockchainConsumer + virtual SynchronizationStart getSyncStart() override; + virtual void onBlockchainDetach(uint64_t height) override; + virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override; + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override; + virtual void getKnownPoolTxIds(std::vector& ids) override; + +private: + + template + void forEachSubscription(F action) { + for (const auto& kv : m_subscriptions) { + action(*kv.second); + } + } + + struct PreprocessInfo { + std::unordered_map> outputs; + std::vector globalIdxs; + }; + + std::error_code preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info); + std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx); + std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info); + std::error_code processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, + const std::vector& outputs, const std::vector& globalIdxs); + + std::error_code getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); + + void updateSyncStart(); + + SynchronizationStart m_syncStart; + const SecretKey m_viewSecret; + // map { spend public key -> subscription } + std::unordered_map> m_subscriptions; + std::unordered_set m_spendKeys; + + INode& m_node; + const cryptonote::Currency& m_currency; +}; + +} diff --git a/src/transfers/TransfersContainer.cpp b/src/transfers/TransfersContainer.cpp new file mode 100644 index 0000000000..e36ad2464e --- /dev/null +++ b/src/transfers/TransfersContainer.cpp @@ -0,0 +1,811 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransfersContainer.h" +#include "IWallet.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +#include "serialization/BinaryInputStreamSerializer.h" +#include "serialization/BinaryOutputStreamSerializer.h" + +namespace CryptoNote { + +void serialize(TransactionInformation& ti, const std::string& name, cryptonote::ISerializer& s) { + s(ti.transactionHash, ""); + s(ti.publicKey, ""); + s(ti.blockHeight, ""); + s(ti.timestamp, ""); + s(ti.unlockTime, ""); + s(ti.totalAmountIn, ""); + s(ti.totalAmountOut, ""); + s(ti.extra, ""); + s(ti.paymentId, ""); +} + +const uint32_t TRANSFERS_CONTAINER_STORAGE_VERSION = 0; + +namespace { + template + class TransferIteratorList { + public: + TransferIteratorList(const TIterator& begin, const TIterator& end) : m_end(end) { + for (auto it = begin; it != end; ++it) { + m_list.emplace_back(it); + } + } + + TransferIteratorList(TransferIteratorList&& other) { + m_list = std::move(other.m_list); + m_end = std::move(other.m_end); + } + + TransferIteratorList& operator=(TransferIteratorList&& other) { + m_list = std::move(other.m_list); + m_end = std::move(other.m_end); + return *this; + } + + void sort() { + std::sort(m_list.begin(), m_list.end(), &TransferIteratorList::lessTIterator); + } + + TIterator findFirstByAmount(uint64_t amount) const { + auto listIt = std::find_if(m_list.begin(), m_list.end(), [amount](const TIterator& it) { + return it->amount == amount; + }); + return listIt == m_list.end() ? m_end : *listIt; + } + + TIterator minElement() const { + auto listIt = std::min_element(m_list.begin(), m_list.end(), &TransferIteratorList::lessTIterator); + return listIt == m_list.end() ? m_end : *listIt; + } + + private: + static bool lessTIterator(const TIterator& it1, const TIterator& it2) { + return + (it1->blockHeight < it2->blockHeight) || + (it1->blockHeight == it2->blockHeight && it1->transactionIndex < it2->transactionIndex); + } + + private: + std::vector m_list; + TIterator m_end; + }; + + template + TransferIteratorList createTransferIteratorList(const std::pair& itPair) { + return TransferIteratorList(itPair.first, itPair.second); + } +} + + +SpentOutputDescriptor::SpentOutputDescriptor() : + m_type(TransactionTypes::OutputType::Invalid) { +} + +SpentOutputDescriptor::SpentOutputDescriptor(const TransactionOutputInformationIn& transactionInfo) : + m_type(transactionInfo.type) { + if (m_type == TransactionTypes::OutputType::Key) { + m_keyImage = &transactionInfo.keyImage; + } else if (m_type == TransactionTypes::OutputType::Multisignature) { + m_amount = transactionInfo.amount; + m_globalOutputIndex = transactionInfo.globalOutputIndex; + } else { + assert(false); + } +} + +SpentOutputDescriptor::SpentOutputDescriptor(const KeyImage* keyImage) { + assign(keyImage); +} + +SpentOutputDescriptor::SpentOutputDescriptor(uint64_t amount, uint64_t globalOutputIndex) { + assign(amount, globalOutputIndex); +} + +void SpentOutputDescriptor::assign(const KeyImage* keyImage) { + m_type = TransactionTypes::OutputType::Key; + m_keyImage = keyImage; +} + +void SpentOutputDescriptor::assign(uint64_t amount, uint64_t globalOutputIndex) { + m_type = TransactionTypes::OutputType::Multisignature; + m_amount = amount; + m_globalOutputIndex = globalOutputIndex; +} + +bool SpentOutputDescriptor::isValid() const { + return m_type != TransactionTypes::OutputType::Invalid; +} + +bool SpentOutputDescriptor::operator==(const SpentOutputDescriptor& other) const { + if (m_type == TransactionTypes::OutputType::Key) { + return other.m_type == m_type && *other.m_keyImage == *m_keyImage; + } else if (m_type == TransactionTypes::OutputType::Multisignature) { + return other.m_type == m_type && other.m_amount == m_amount && other.m_globalOutputIndex == m_globalOutputIndex; + } else { + assert(false); + return false; + } +} + +size_t SpentOutputDescriptor::hash() const { + if (m_type == TransactionTypes::OutputType::Key) { + static_assert(sizeof(size_t) < sizeof(*m_keyImage), "sizeof(size_t) < sizeof(*m_keyImage)"); + return *reinterpret_cast(m_keyImage->data()); + } else if (m_type == TransactionTypes::OutputType::Multisignature) { + size_t hashValue = boost::hash_value(m_amount); + boost::hash_combine(hashValue, m_globalOutputIndex); + return hashValue; + } else { + assert(false); + return 0; + } +} + + +TransfersContainer::TransfersContainer(const cryptonote::Currency& currency, size_t transactionSpendableAge) : + m_currentHeight(0), + m_currency(currency), + m_transactionSpendableAge(transactionSpendableAge) { +} + +bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransactionReader& tx, + const std::vector& transfers) { + std::unique_lock lock(m_mutex); + + if (block.height < m_currentHeight) { + throw std::invalid_argument("Cannot add transaction from block < m_currentHeight"); + } + + if (m_transactions.count(tx.getTransactionHash()) > 0) { + throw std::invalid_argument("Transaction is already added"); + } + + bool added = addTransactionOutputs(block, tx, transfers); + added |= addTransactionInputs(block, tx); + + if (added) { + addTransaction(block, tx); + } + + if (block.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + m_currentHeight = block.height; + } + + return added; +} + +/** + * \pre m_mutex is locked. + */ +void TransfersContainer::addTransaction(const BlockInfo& block, const ITransactionReader& tx) { + auto txHash = tx.getTransactionHash(); + + TransactionInformation txInfo; + txInfo.blockHeight = block.height; + txInfo.timestamp = block.timestamp; + txInfo.transactionHash = txHash; + txInfo.unlockTime = tx.getUnlockTime(); + txInfo.publicKey = tx.getTransactionPublicKey(); + txInfo.totalAmountIn = tx.getInputTotalAmount(); + txInfo.totalAmountOut = tx.getOutputTotalAmount(); + + if (!tx.getPaymentId(txInfo.paymentId)) { + txInfo.paymentId.fill(0); + } + + auto result = m_transactions.emplace(std::move(txInfo)); + assert(result.second); +} + +/** + * \pre m_mutex is locked. + */ +bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, + const std::vector& transfers) { + bool outputsAdded = false; + + auto txHash = tx.getTransactionHash(); + bool transactionIsUnconfimed = (block.height == UNCONFIRMED_TRANSACTION_HEIGHT); + for (const auto& transfer : transfers) { + assert(transfer.outputInTransaction < tx.getOutputCount()); + assert(transfer.type == tx.getOutputType(transfer.outputInTransaction)); + assert(transfer.amount > 0); + + bool transferIsUnconfirmed = (transfer.globalOutputIndex == UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + if (transactionIsUnconfimed != transferIsUnconfirmed) { + throw std::invalid_argument("Bad transfer's globalOutputIndex"); + } + + TransactionOutputInformationEx info; + static_cast(info) = transfer; + info.blockHeight = block.height; + info.transactionIndex = block.transactionIndex; + info.unlockTime = tx.getUnlockTime(); + info.transactionHash = txHash; + info.visible = true; + + if (transferIsUnconfirmed) { + auto result = m_unconfirmedTransfers.emplace(std::move(info)); + assert(result.second); + } else { + if (info.type == TransactionTypes::OutputType::Multisignature) { + SpentOutputDescriptor descriptor(transfer); + if (m_availableTransfers.get().count(descriptor) > 0 || + m_spentTransfers.get().count(descriptor) > 0) { + throw std::runtime_error("Transfer already exists"); + } + } + + auto result = m_availableTransfers.emplace(std::move(info)); + assert(result.second); + } + + if (info.type == TransactionTypes::OutputType::Key) { + updateTransfersVisibility(info.keyImage); + } + + outputsAdded = true; + } + + return outputsAdded; +} + +/** + * \pre m_mutex is locked. + */ +bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITransactionReader& tx) { + bool inputsAdded = false; + + for (size_t i = 0; i < tx.getInputCount(); ++i) { + auto inputType = tx.getInputType(i); + + if (inputType == TransactionTypes::InputType::Key) { + TransactionTypes::InputKey input; + tx.getInput(i, input); + + SpentOutputDescriptor descriptor(&input.keyImage); + auto spentRange = m_spentTransfers.get().equal_range(descriptor); + if (std::distance(spentRange.first, spentRange.second) > 0) { + throw std::runtime_error("Spending already spent transfer"); + } + + auto availableRange = m_availableTransfers.get().equal_range(descriptor); + auto unconfirmedRange = m_unconfirmedTransfers.get().equal_range(descriptor); + size_t availableCount = std::distance(availableRange.first, availableRange.second); + size_t unconfirmedCount = std::distance(unconfirmedRange.first, unconfirmedRange.second); + + if (availableCount == 0) { + if (unconfirmedCount > 0) { + throw std::runtime_error("Spending unconfirmed transfer"); + } else { + // This input doesn't spend any transfer from this container + continue; + } + } + + auto& outputDescriptorIndex = m_availableTransfers.get(); + auto availableOutputsRange = outputDescriptorIndex.equal_range(SpentOutputDescriptor(&input.keyImage)); + + auto iteratorList = createTransferIteratorList(availableOutputsRange); + iteratorList.sort(); + auto spendingTransferIt = iteratorList.findFirstByAmount(input.amount); + + if (spendingTransferIt == availableOutputsRange.second) { + throw std::runtime_error("Input has invalid amount, corresponding output isn't found"); + } + + assert(spendingTransferIt->keyImage == input.keyImage); + copyToSpent(block, tx, i, *spendingTransferIt); + // erase from available outputs + outputDescriptorIndex.erase(spendingTransferIt); + updateTransfersVisibility(input.keyImage); + + inputsAdded = true; + } else if (inputType == TransactionTypes::InputType::Multisignature) { + TransactionTypes::InputMultisignature input; + tx.getInput(i, input); + + auto& outputDescriptorIndex = m_availableTransfers.get(); + auto availableOutputIt = outputDescriptorIndex.find(SpentOutputDescriptor(input.amount, input.outputIndex)); + if (availableOutputIt != outputDescriptorIndex.end()) { + copyToSpent(block, tx, i, *availableOutputIt); + // erase from available outputs + outputDescriptorIndex.erase(availableOutputIt); + + inputsAdded = true; + } + } else { + assert(inputType == TransactionTypes::InputType::Generating); + } + } + + return inputsAdded; +} + +bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHash) { + std::unique_lock lock(m_mutex); + + auto it = m_transactions.find(transactionHash); + if (it == m_transactions.end()) { + return false; + } else if (it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT) { + return false; + } else { + deleteTransactionTransfers(it->transactionHash); + m_transactions.erase(it); + return true; + } +} + +bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, + const std::vector& globalIndices) { + if (block.height == UNCONFIRMED_TRANSACTION_HEIGHT) { + throw std::invalid_argument("Block height equals UNCONFIRMED_TRANSACTION_HEIGHT"); + } + + std::unique_lock lock(m_mutex); + + auto transactionIt = m_transactions.find(transactionHash); + if (transactionIt == m_transactions.end()) { + return false; + } + + if (transactionIt->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT) { + return false; + } + + auto txInfo = *transactionIt; + txInfo.blockHeight = block.height; + txInfo.timestamp = block.timestamp; + m_transactions.replace(transactionIt, txInfo); + + auto availableRange = m_unconfirmedTransfers.get().equal_range(transactionHash); + for (auto transferIt = availableRange.first; transferIt != availableRange.second; ) { + auto transfer = *transferIt; + assert(transfer.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT); + assert(transfer.globalOutputIndex == UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + if (transfer.outputInTransaction >= globalIndices.size()) { + throw std::invalid_argument("Not enough elements in globalIndices"); + } + + transfer.blockHeight = block.height; + transfer.transactionIndex = block.transactionIndex; + transfer.globalOutputIndex = globalIndices[transfer.outputInTransaction]; + + if (transfer.type == TransactionTypes::OutputType::Multisignature) { + SpentOutputDescriptor descriptor(transfer); + if (m_availableTransfers.get().count(descriptor) > 0 || + m_spentTransfers.get().count(descriptor) > 0) { + // This exception breaks TransfersContainer consistency + throw std::runtime_error("Transfer already exists"); + } + } + + auto result = m_availableTransfers.emplace(std::move(transfer)); + assert(result.second); + + transferIt = m_unconfirmedTransfers.get().erase(transferIt); + + if (transfer.type == TransactionTypes::OutputType::Key) { + updateTransfersVisibility(transfer.keyImage); + } + } + + auto& spendingTransactionIndex = m_spentTransfers.get(); + auto spentRange = spendingTransactionIndex.equal_range(transactionHash); + for (auto transferIt = spentRange.first; transferIt != spentRange.second; ++transferIt) { + auto transfer = *transferIt; + assert(transfer.spendingBlock.height == UNCONFIRMED_TRANSACTION_HEIGHT); + + transfer.spendingBlock = block; + spendingTransactionIndex.replace(transferIt, transfer); + } + + return true; +} + +/** + * \pre m_mutex is locked. + */ +void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) { + auto& spendingTransactionIndex = m_spentTransfers.get(); + auto spentTransfersRange = spendingTransactionIndex.equal_range(transactionHash); + for (auto it = spentTransfersRange.first; it != spentTransfersRange.second;) { + assert(it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); + assert(it->globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + + auto result = m_availableTransfers.emplace(static_cast(*it)); + assert(result.second); + it = spendingTransactionIndex.erase(it); + + if (result.first->type == TransactionTypes::OutputType::Key) { + updateTransfersVisibility(result.first->keyImage); + } + } + + auto unconfirmedTransfersRange = m_unconfirmedTransfers.get().equal_range(transactionHash); + for (auto it = unconfirmedTransfersRange.first; it != unconfirmedTransfersRange.second;) { + if (it->type == TransactionTypes::OutputType::Key) { + KeyImage keyImage = it->keyImage; + it = m_unconfirmedTransfers.get().erase(it); + updateTransfersVisibility(keyImage); + } else { + it = m_unconfirmedTransfers.get().erase(it); + } + } + + auto& transactionTransfersIndex = m_availableTransfers.get(); + auto transactionTransfersRange = transactionTransfersIndex.equal_range(transactionHash); + for (auto it = transactionTransfersRange.first; it != transactionTransfersRange.second;) { + if (it->type == TransactionTypes::OutputType::Key) { + KeyImage keyImage = it->keyImage; + it = transactionTransfersIndex.erase(it); + updateTransfersVisibility(keyImage); + } else { + it = transactionTransfersIndex.erase(it); + } + } +} + +/** + * \pre m_mutex is locked. + */ +void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, + const TransactionOutputInformationEx& output) { + assert(output.blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); + assert(output.globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + + SpentTransactionOutput spentOutput; + static_cast(spentOutput) = output; + spentOutput.spendingBlock = block; + spentOutput.spendingTransactionHash = tx.getTransactionHash(); + spentOutput.inputInTransaction = static_cast(inputIndex); + auto result = m_spentTransfers.emplace(std::move(spentOutput)); + assert(result.second); +} + +std::vector TransfersContainer::detach(uint64_t height) { + // This method expects that UNCONFIRMED_TRANSACTION_HEIGHT is a big positive number + assert(height < UNCONFIRMED_TRANSACTION_HEIGHT); + + std::lock_guard lk(m_mutex); + + std::vector deletedTransactions; + auto& spendingTransactionIndex = m_spentTransfers.get(); + auto& blockHeightIndex = m_transactions.get<1>(); + auto it = blockHeightIndex.end(); + while (it != blockHeightIndex.begin()) { + --it; + + bool doDelete = false; + if (it->blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + auto range = spendingTransactionIndex.equal_range(it->transactionHash); + for (auto spentTransferIt = range.first; spentTransferIt != range.second; ++spentTransferIt) { + if (spentTransferIt->blockHeight >= height) { + doDelete = true; + break; + } + } + } else if (it->blockHeight >= height) { + doDelete = true; + } else { + break; + } + + if (doDelete) { + deleteTransactionTransfers(it->transactionHash); + deletedTransactions.emplace_back(it->transactionHash); + it = blockHeightIndex.erase(it); + } + } + + // TODO: notification on detach + m_currentHeight = height == 0 ? 0 : height - 1; + + return deletedTransactions; +} + +namespace { + template + void updateVisibility(C& collection, const T& range, bool visible) { + for (auto it = range.first; it != range.second; ++it) { + auto updated = *it; + updated.visible = visible; + collection.replace(it, updated); + } + } +} + +/** + * \pre m_mutex is locked. + */ +void TransfersContainer::updateTransfersVisibility(const KeyImage& keyImage) { + auto& unconfirmedIndex = m_unconfirmedTransfers.get(); + auto& availableIndex = m_availableTransfers.get(); + auto& spentIndex = m_spentTransfers.get(); + + SpentOutputDescriptor descriptor(&keyImage); + auto unconfirmedRange = unconfirmedIndex.equal_range(descriptor); + auto availableRange = availableIndex.equal_range(descriptor); + auto spentRange = spentIndex.equal_range(descriptor); + + size_t unconfirmedCount = std::distance(unconfirmedRange.first, unconfirmedRange.second); + size_t availableCount = std::distance(availableRange.first, availableRange.second); + size_t spentCount = std::distance(spentRange.first, spentRange.second); + assert(spentCount == 0 || spentCount == 1); + + if (spentCount > 0) { + updateVisibility(unconfirmedIndex, unconfirmedRange, false); + updateVisibility(availableIndex, availableRange, false); + updateVisibility(spentIndex, spentRange, true); + } else if (availableCount > 0) { + updateVisibility(unconfirmedIndex, unconfirmedRange, false); + updateVisibility(availableIndex, availableRange, false); + + auto iteratorList = createTransferIteratorList(availableRange); + auto earliestTransferIt = iteratorList.minElement(); + assert(earliestTransferIt != availableRange.second); + + auto earliestTransfer = *earliestTransferIt; + earliestTransfer.visible = true; + availableIndex.replace(earliestTransferIt, earliestTransfer); + } else { + updateVisibility(unconfirmedIndex, unconfirmedRange, unconfirmedCount == 1); + } +} + +bool TransfersContainer::advanceHeight(uint64_t height) { + std::lock_guard lk(m_mutex); + + if (m_currentHeight <= height) { + m_currentHeight = height; + return true; + } + + return false; +} + +size_t TransfersContainer::transfersCount() { + std::lock_guard lk(m_mutex); + return m_unconfirmedTransfers.size() + m_availableTransfers.size() + m_spentTransfers.size(); +} + +size_t TransfersContainer::transactionsCount() { + std::lock_guard lk(m_mutex); + return m_transactions.size(); +} + +uint64_t TransfersContainer::balance(uint32_t flags) { + std::lock_guard lk(m_mutex); + uint64_t amount = 0; + + for (const auto& t : m_availableTransfers) { + if (t.visible && isIncluded(t, flags)) { + amount += t.amount; + } + } + + if ((flags & IncludeStateLocked) != 0) { + for (const auto& t : m_unconfirmedTransfers) { + if (t.visible && isIncluded(t.type, IncludeStateLocked, flags)) { + amount += t.amount; + } + } + } + + return amount; +} + +void TransfersContainer::getOutputs(std::vector& transfers, uint32_t flags) { + std::lock_guard lk(m_mutex); + for (const auto& t : m_availableTransfers) { + if (t.visible && isIncluded(t, flags)) { + transfers.push_back(t); + } + } + + if ((flags & IncludeStateLocked) != 0) { + for (const auto& t : m_unconfirmedTransfers) { + if (t.visible && isIncluded(t.type, IncludeStateLocked, flags)) { + transfers.push_back(t); + } + } + } +} + +bool TransfersContainer::getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) { + std::lock_guard lk(m_mutex); + auto it = m_transactions.find(transactionHash); + if (it == m_transactions.end()) { + return false; + } + + info = *it; + + int64_t amountOut = 0; + if (info.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + auto unconfirmedOutputsRange = m_unconfirmedTransfers.get().equal_range(transactionHash); + for (auto it = unconfirmedOutputsRange.first; it != unconfirmedOutputsRange.second; ++it) { + amountOut += static_cast(it->amount); + } + } else { + auto availableOutputsRange = m_availableTransfers.get().equal_range(transactionHash); + for (auto it = availableOutputsRange.first; it != availableOutputsRange.second; ++it) { + amountOut += static_cast(it->amount); + } + + auto spentOutputsRange = m_spentTransfers.get().equal_range(transactionHash); + for (auto it = spentOutputsRange.first; it != spentOutputsRange.second; ++it) { + amountOut += static_cast(it->amount); + } + } + + int64_t amountIn = 0; + auto rangeInputs = m_spentTransfers.get().equal_range(transactionHash); + for (auto it = rangeInputs.first; it != rangeInputs.second; ++it) { + amountIn += static_cast(it->amount); + } + + txBalance = amountOut - amountIn; + + return true; +} + +std::vector TransfersContainer::getTransactionOutputs(const Hash& transactionHash, + uint32_t flags) { + std::lock_guard lk(m_mutex); + + std::vector result; + + auto availableRange = m_availableTransfers.get().equal_range(transactionHash); + for (auto i = availableRange.first; i != availableRange.second; ++i) { + const auto& t = *i; + if (isIncluded(t, flags)) { + result.push_back(t); + } + } + + if ((flags & IncludeStateLocked) != 0) { + auto unconfirmedRange = m_unconfirmedTransfers.get().equal_range(transactionHash); + for (auto i = unconfirmedRange.first; i != unconfirmedRange.second; ++i) { + if (isIncluded(i->type, IncludeStateLocked, flags)) { + result.push_back(*i); + } + } + } + + return result; +} + +void TransfersContainer::getUnconfirmedTransactions(std::vector& transactions) { + std::lock_guard lk(m_mutex); + transactions.clear(); + for (auto& element : m_transactions) { + if (element.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + transactions.push_back(*reinterpret_cast(&element.transactionHash)); + } + } +} + +std::vector TransfersContainer::getSpentOutputs() { + std::lock_guard lk(m_mutex); + + std::vector spentOutputs; + + spentOutputs.reserve(m_spentTransfers.size()); + + for (const auto& o : m_spentTransfers) { + TransactionSpentOutputInformation spentOutput; + static_cast(spentOutput) = o; + + spentOutput.spendingBlockHeight = o.spendingBlock.height; + spentOutput.timestamp = o.spendingBlock.timestamp; + spentOutput.spendingTransactionHash = o.spendingTransactionHash; + spentOutput.keyImage = o.keyImage; + spentOutput.inputInTransaction = o.inputInTransaction; + + spentOutputs.push_back(spentOutput); + } + + return spentOutputs; +} + +void TransfersContainer::save(std::ostream& os) { + std::lock_guard lk(m_mutex); + cryptonote::BinaryOutputStreamSerializer s(os); + + s(const_cast(TRANSFERS_CONTAINER_STORAGE_VERSION), "version"); + + s(m_currentHeight, "height"); + cryptonote::writeSequence(m_transactions.begin(), m_transactions.end(), "transactions", s); + cryptonote::writeSequence(m_unconfirmedTransfers.begin(), m_unconfirmedTransfers.end(), "unconfirmedTransfers", s); + cryptonote::writeSequence(m_availableTransfers.begin(), m_availableTransfers.end(), "availableTransfers", s); + cryptonote::writeSequence(m_spentTransfers.begin(), m_spentTransfers.end(), "spentTransfers", s); +} + +void TransfersContainer::load(std::istream& in) { + std::lock_guard lk(m_mutex); + cryptonote::BinaryInputStreamSerializer s(in); + + uint32_t version = 0; + s(version, "version"); + + if (version > TRANSFERS_CONTAINER_STORAGE_VERSION) { + throw std::runtime_error("Unsupported transfers storage version"); + } + + uint64_t currentHeight = 0; + TransactionMultiIndex transactions; + UnconfirmedTransfersMultiIndex unconfirmedTransfers; + AvailableTransfersMultiIndex availableTransfers; + SpentTransfersMultiIndex spentTransfers; + + s(currentHeight, "height"); + cryptonote::readSequence(std::inserter(transactions, transactions.end()), "transactions", s); + cryptonote::readSequence(std::inserter(unconfirmedTransfers, unconfirmedTransfers.end()), "unconfirmedTransfers", s); + cryptonote::readSequence(std::inserter(availableTransfers, availableTransfers.end()), "availableTransfers", s); + cryptonote::readSequence(std::inserter(spentTransfers, spentTransfers.end()), "spentTransfers", s); + + m_currentHeight = currentHeight; + m_transactions = std::move(transactions); + m_unconfirmedTransfers = std::move(unconfirmedTransfers); + m_availableTransfers = std::move(availableTransfers); + m_spentTransfers = std::move(spentTransfers); +} + +bool TransfersContainer::isSpendTimeUnlocked(uint64_t unlockTime) const { + if (unlockTime < m_currency.maxBlockHeight()) { + // interpret as block index + return m_currentHeight - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlockTime; + } else { + //interpret as time + uint64_t current_time = static_cast(time(NULL)); + return current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlockTime; + } + + return false; +} + +bool TransfersContainer::isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const { + uint32_t state; + if (info.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT || !isSpendTimeUnlocked(info.unlockTime)) { + state = IncludeStateLocked; + } else if (m_currentHeight < info.blockHeight + m_transactionSpendableAge) { + state = IncludeStateSoftLocked; + } else { + state = IncludeStateUnlocked; + } + + return isIncluded(info.type, state, flags); +} + +bool TransfersContainer::isIncluded(TransactionTypes::OutputType type, uint32_t state, uint32_t flags) { + return + // filter by type + ( + ((flags & IncludeTypeKey) != 0 && type == TransactionTypes::OutputType::Key) || + ((flags & IncludeTypeMultisignature) != 0 && type == TransactionTypes::OutputType::Multisignature) + ) + && + // filter by state + ((flags & state) != 0); +} + +} diff --git a/src/transfers/TransfersContainer.h b/src/transfers/TransfersContainer.h new file mode 100644 index 0000000000..da7cd97c3e --- /dev/null +++ b/src/transfers/TransfersContainer.h @@ -0,0 +1,286 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/Currency.h" +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" + +#include "ITransaction.h" +#include "ITransfersContainer.h" +#include "SerializationHelpers.h" + +namespace CryptoNote { + +struct TransactionOutputInformationIn; + +class SpentOutputDescriptor { +public: + SpentOutputDescriptor(); + SpentOutputDescriptor(const TransactionOutputInformationIn& transactionInfo); + SpentOutputDescriptor(const KeyImage* keyImage); + SpentOutputDescriptor(uint64_t amount, uint64_t globalOutputIndex); + + void assign(const KeyImage* keyImage); + void assign(uint64_t amount, uint64_t globalOutputIndex); + + bool isValid() const; + + bool operator==(const SpentOutputDescriptor& other) const; + size_t hash() const; + +private: + TransactionTypes::OutputType m_type; + union { + const KeyImage* m_keyImage; +struct { + uint64_t m_amount; + uint64_t m_globalOutputIndex; + }; + }; +}; + +struct SpentOutputDescriptorHasher { + size_t operator()(const SpentOutputDescriptor& descriptor) const { + return descriptor.hash(); + } +}; + +struct TransactionOutputInformationIn : public TransactionOutputInformation { + KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key +}; + +struct TransactionOutputInformationEx : public TransactionOutputInformationIn { + uint64_t unlockTime; + uint64_t blockHeight; + uint32_t transactionIndex; + bool visible; + + SpentOutputDescriptor getSpentOutputDescriptor() const { return SpentOutputDescriptor(*this); } + const Hash& getTransactionHash() const { return transactionHash; } + + void serialize(cryptonote::ISerializer& s, const std::string& name) { + s(reinterpret_cast(type), "type"); + s(amount, ""); + s(globalOutputIndex, ""); + s(outputInTransaction, ""); + s(transactionPublicKey, ""); + s(keyImage, ""); + s(unlockTime, ""); + s(blockHeight, ""); + s(transactionIndex, ""); + s(transactionHash, ""); + s(visible, ""); + + if (type == TransactionTypes::OutputType::Key) + s(outputKey, ""); + else if (type == TransactionTypes::OutputType::Multisignature) + s(requiredSignatures, ""); + } + +}; + +struct BlockInfo { + uint64_t height; + uint64_t timestamp; + uint32_t transactionIndex; + + void serialize(cryptonote::ISerializer& s, const std::string& name) { + s(height, "height"); + s(timestamp, "timestamp"); + s(transactionIndex, "transactionIndex"); + } +}; + +struct SpentTransactionOutput : TransactionOutputInformationEx { + BlockInfo spendingBlock; + Hash spendingTransactionHash; + uint32_t inputInTransaction; + + const Hash& getSpendingTransactionHash() const { + return spendingTransactionHash; + } + + void serialize(cryptonote::ISerializer& s, const std::string& name) { + TransactionOutputInformationEx::serialize(s, name); + s(spendingBlock, "spendingBlock"); + s(spendingTransactionHash, "spendingTransactionHash"); + s(inputInTransaction, "inputInTransaction"); + } +}; + +enum class KeyImageState { + Unconfirmed, + Confirmed, + Spent +}; + +struct KeyOutputInfo { + KeyImageState state; + size_t count; +}; + +class TransfersContainer : public ITransfersContainer { + +public: + + TransfersContainer(const cryptonote::Currency& currency, size_t transactionSpendableAge); + + bool addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers); + bool deleteUnconfirmedTransaction(const Hash& transactionHash); + bool markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); + + std::vector detach(uint64_t height); + bool advanceHeight(uint64_t height); + + // ITransfersContainer + virtual size_t transfersCount() override; + virtual size_t transactionsCount() override; + virtual uint64_t balance(uint32_t flags) override; + virtual void getOutputs(std::vector& transfers, uint32_t flags) override; + virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) override; + virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags) override; + virtual void getUnconfirmedTransactions(std::vector& transactions) override; + virtual std::vector getSpentOutputs() override; + + // IStreamSerializable + virtual void save(std::ostream& os) override; + virtual void load(std::istream& in) override; + +private: + struct ContainingTransactionIndex { }; + struct SpendingTransactionIndex { }; + struct SpentOutputDescriptorIndex { }; + + typedef boost::multi_index_container< + TransactionInformation, + boost::multi_index::indexed_by< + boost::multi_index::hashed_unique, + boost::multi_index::ordered_non_unique + > + > TransactionMultiIndex; + + typedef boost::multi_index_container< + TransactionOutputInformationEx, + boost::multi_index::indexed_by< + boost::multi_index::hashed_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun< + TransactionOutputInformationEx, + SpentOutputDescriptor, + &TransactionOutputInformationEx::getSpentOutputDescriptor>, + SpentOutputDescriptorHasher + >, + boost::multi_index::hashed_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun< + TransactionOutputInformationEx, + const Hash&, + &TransactionOutputInformationEx::getTransactionHash> + > + > + > UnconfirmedTransfersMultiIndex; + + typedef boost::multi_index_container< + TransactionOutputInformationEx, + boost::multi_index::indexed_by< + boost::multi_index::hashed_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun< + TransactionOutputInformationEx, + SpentOutputDescriptor, + &TransactionOutputInformationEx::getSpentOutputDescriptor>, + SpentOutputDescriptorHasher + >, + boost::multi_index::hashed_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun< + TransactionOutputInformationEx, + const Hash&, + &TransactionOutputInformationEx::getTransactionHash> + > + > + > AvailableTransfersMultiIndex; + + typedef boost::multi_index_container< + SpentTransactionOutput, + boost::multi_index::indexed_by< + boost::multi_index::hashed_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun< + TransactionOutputInformationEx, + SpentOutputDescriptor, + &TransactionOutputInformationEx::getSpentOutputDescriptor>, + SpentOutputDescriptorHasher + >, + boost::multi_index::hashed_non_unique< + boost::multi_index::tag, + boost::multi_index::const_mem_fun< + TransactionOutputInformationEx, + const Hash&, + &SpentTransactionOutput::getTransactionHash> + >, + boost::multi_index::hashed_non_unique < + boost::multi_index::tag, + boost::multi_index::const_mem_fun < + SpentTransactionOutput, + const Hash&, + &SpentTransactionOutput::getSpendingTransactionHash> + > + > + > SpentTransfersMultiIndex; + +private: + void addTransaction(const BlockInfo& block, const ITransactionReader& tx); + bool addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, + const std::vector& transfers); + bool addTransactionInputs(const BlockInfo& block, const ITransactionReader& tx); + void deleteTransactionTransfers(const Hash& transactionHash); + bool isSpendTimeUnlocked(uint64_t unlockTime) const; + bool isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const; + static bool isIncluded(TransactionTypes::OutputType type, uint32_t state, uint32_t flags); + void updateTransfersVisibility(const KeyImage& keyImage); + + void copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output); + +private: + TransactionMultiIndex m_transactions; + UnconfirmedTransfersMultiIndex m_unconfirmedTransfers; + AvailableTransfersMultiIndex m_availableTransfers; + SpentTransfersMultiIndex m_spentTransfers; + //std::unordered_map> m_keyImages; + + uint64_t m_currentHeight; // current height is needed to check if a transfer is unlocked + size_t m_transactionSpendableAge; + const cryptonote::Currency& m_currency; + std::mutex m_mutex; +}; + +} diff --git a/src/transfers/TransfersSubscription.cpp b/src/transfers/TransfersSubscription.cpp new file mode 100755 index 0000000000..8ea2216d78 --- /dev/null +++ b/src/transfers/TransfersSubscription.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransfersSubscription.h" +#include "IWallet.h" + +namespace CryptoNote { + +TransfersSubscription::TransfersSubscription(const cryptonote::Currency& currency, const AccountSubscription& sub) + : m_currency(currency), m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} + + +SynchronizationStart TransfersSubscription::getSyncStart() { + return m_subscription.syncStart; +} + +void TransfersSubscription::onBlockchainDetach(uint64_t height) { + std::vector deletedTransactions = m_transfers.detach(height); + for (auto& hash : deletedTransactions) { + m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, hash); + } +} + +void TransfersSubscription::onError(const std::error_code& ec, uint64_t height) { + if (height != UNCONFIRMED_TRANSACTION_HEIGHT) { + m_transfers.detach(height); + } + m_observerManager.notify(&ITransfersObserver::onError, this, height, ec); +} + +bool TransfersSubscription::advanceHeight(uint64_t height) { + return m_transfers.advanceHeight(height); +} + +const AccountKeys& TransfersSubscription::getKeys() const { + return m_subscription.keys; +} + +void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, + const std::vector& transfers) { + + bool added = m_transfers.addTransaction(blockInfo, tx, transfers); + if (added) { + m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); + } +} + +AccountAddress TransfersSubscription::getAddress() { + return m_subscription.keys.address; +} + +ITransfersContainer& TransfersSubscription::getContainer() { + return m_transfers; +} + +void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) { + m_transfers.deleteUnconfirmedTransaction(transactionHash); + m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); +} + +void TransfersSubscription::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, + const std::vector& globalIndices) { + m_transfers.markTransactionConfirmed(block, transactionHash, globalIndices); + m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash); +} + +} diff --git a/src/transfers/TransfersSubscription.h b/src/transfers/TransfersSubscription.h new file mode 100644 index 0000000000..98520f9245 --- /dev/null +++ b/src/transfers/TransfersSubscription.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ITransfersSynchronizer.h" +#include "TransfersContainer.h" +#include "IObservableImpl.h" + +namespace CryptoNote { + +class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITransfersSubscription > { +public: + + TransfersSubscription(const cryptonote::Currency& currency, const AccountSubscription& sub); + + SynchronizationStart getSyncStart(); + void onBlockchainDetach(uint64_t height); + void onError(const std::error_code& ec, uint64_t height); + bool advanceHeight(uint64_t height); + const AccountKeys& getKeys() const; + void addTransaction(const BlockInfo& blockInfo, + const ITransactionReader& tx, const std::vector& transfers); + + void deleteUnconfirmedTransaction(const Hash& transactionHash); + void markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); + + // ITransfersSubscription + virtual AccountAddress getAddress() override; + virtual ITransfersContainer& getContainer() override; + +private: + + TransfersContainer m_transfers; + AccountSubscription m_subscription; + const cryptonote::Currency& m_currency; +}; + +} diff --git a/src/transfers/TransfersSynchronizer.cpp b/src/transfers/TransfersSynchronizer.cpp new file mode 100644 index 0000000000..be9927887f --- /dev/null +++ b/src/transfers/TransfersSynchronizer.cpp @@ -0,0 +1,236 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransfersSynchronizer.h" +#include "TransfersConsumer.h" + +#include "serialization/BinaryInputStreamSerializer.h" +#include "serialization/BinaryOutputStreamSerializer.h" + +namespace CryptoNote { + +void serialize(AccountAddress& acc, const std::string& name, cryptonote::ISerializer& s) { + s.beginObject(name); + s(acc.spendPublicKey, "spendKey"); + s(acc.viewPublicKey, "viewKey"); + s.endObject(); +} + +const uint32_t TRANSFERS_STORAGE_ARCHIVE_VERSION = 0; + +TransfersSyncronizer::TransfersSyncronizer(const cryptonote::Currency& currency, IBlockchainSynchronizer& sync, INode& node) : + m_currency(currency), m_sync(sync), m_node(node) { +} + +TransfersSyncronizer::~TransfersSyncronizer() { + m_sync.stop(); + for (const auto& kv : m_consumers) { + m_sync.removeConsumer(kv.second.get()); + } +} + +ITransfersSubscription& TransfersSyncronizer::addSubscription(const AccountSubscription& acc) { + auto it = m_consumers.find(acc.keys.address.viewPublicKey); + + if (it == m_consumers.end()) { + std::unique_ptr consumer( + new TransfersConsumer(m_currency, m_node, acc.keys.viewSecretKey)); + m_sync.addConsumer(consumer.get()); + it = m_consumers.insert(std::make_pair(acc.keys.address.viewPublicKey, std::move(consumer))).first; + } + + return it->second->addSubscription(acc); +} + +bool TransfersSyncronizer::removeSubscription(const AccountAddress& acc) { + auto it = m_consumers.find(acc.viewPublicKey); + if (it == m_consumers.end()) + return false; + + if (it->second->removeSubscription(acc)) { + m_sync.removeConsumer(it->second.get()); + m_consumers.erase(it); + } + + return true; +} + +void TransfersSyncronizer::getSubscriptions(std::vector& subscriptions) { + for (const auto& kv : m_consumers) { + kv.second->getSubscriptions(subscriptions); + } +} + +ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountAddress& acc) { + auto it = m_consumers.find(acc.viewPublicKey); + return (it == m_consumers.end()) ? 0 : it->second->getSubscription(acc); +} + +void TransfersSyncronizer::save(std::ostream& os) { + m_sync.save(os); + + cryptonote::BinaryOutputStreamSerializer s(os); + s(const_cast(TRANSFERS_STORAGE_ARCHIVE_VERSION), "version"); + + size_t subscriptionCount = m_consumers.size(); + + s.beginArray(subscriptionCount, "consumers"); + + for (const auto& consumer : m_consumers) { + s.beginObject(""); + s(const_cast(consumer.first), "view_key"); + + std::stringstream consumerState; + // synchronization state + m_sync.getConsumerState(consumer.second.get())->save(consumerState); + + std::string blob = consumerState.str(); + s(blob, "state"); + + std::vector subscriptions; + consumer.second->getSubscriptions(subscriptions); + size_t subCount = subscriptions.size(); + + s.beginArray(subCount, "subscriptions"); + + for (auto& addr : subscriptions) { + auto sub = consumer.second->getSubscription(addr); + if (sub != nullptr) { + s.beginObject(""); + + std::stringstream subState; + assert(sub); + sub->getContainer().save(subState); + // store data block + std::string blob = subState.str(); + s(addr, "address"); + s(blob, "state"); + + s.endObject(); + } + } + + s.endArray(); + s.endObject(); + } +} + +namespace { +std::string getObjectState(IStreamSerializable& obj) { + std::stringstream stream; + obj.save(stream); + return stream.str(); +} + +void setObjectState(IStreamSerializable& obj, const std::string& state) { + std::stringstream stream(state); + obj.load(stream); +} + +} + +void TransfersSyncronizer::load(std::istream& is) { + m_sync.load(is); + + cryptonote::BinaryInputStreamSerializer s(is); + uint32_t version = 0; + + s(version, "version"); + + if (version > TRANSFERS_STORAGE_ARCHIVE_VERSION) { + throw std::runtime_error("TransfersSyncronizer version mismatch"); + } + + + struct ConsumerState { + PublicKey viewKey; + std::string state; + std::vector> subscriptionStates; + }; + + std::vector updatedStates; + + try { + size_t subscriptionCount = 0; + s.beginArray(subscriptionCount, "consumers"); + + while (subscriptionCount--) { + s.beginObject(""); + PublicKey viewKey; + s(viewKey, "view_key"); + + std::string blob; + s(blob, "state"); + + auto subIter = m_consumers.find(viewKey); + if (subIter != m_consumers.end()) { + auto consumerState = m_sync.getConsumerState(subIter->second.get()); + assert(consumerState); + + { + // store previous state + auto prevConsumerState = getObjectState(*consumerState); + // load consumer state + setObjectState(*consumerState, blob); + updatedStates.push_back(ConsumerState{ viewKey, std::move(prevConsumerState) }); + } + + // load subscriptions + size_t subCount = 0; + s.beginArray(subCount, "subscriptions"); + + while (subCount--) { + s.beginObject(""); + + AccountAddress acc; + std::string state; + + s(acc, "address"); + s(state, "state"); + + auto sub = subIter->second->getSubscription(acc); + + if (sub != nullptr) { + auto prevState = getObjectState(sub->getContainer()); + setObjectState(sub->getContainer(), state); + updatedStates.back().subscriptionStates.push_back(std::make_pair(acc, prevState)); + } + + s.endObject(); + } + s.endArray(); + } + } + + s.endObject(); + s.endArray(); + + } catch (...) { + // rollback state + for (const auto& consumerState : updatedStates) { + auto consumer = m_consumers.find(consumerState.viewKey)->second.get(); + setObjectState(*m_sync.getConsumerState(consumer), consumerState.state); + for (const auto& sub : consumerState.subscriptionStates) { + setObjectState(consumer->getSubscription(sub.first)->getContainer(), sub.second); + } + } + throw; + } + +} + +} diff --git a/src/transfers/TransfersSynchronizer.h b/src/transfers/TransfersSynchronizer.h new file mode 100644 index 0000000000..14c95b5138 --- /dev/null +++ b/src/transfers/TransfersSynchronizer.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . +#pragma once + +#include "ITransfersSynchronizer.h" +#include "IBlockchainSynchronizer.h" +#include "TypeHelpers.h" + +#include +#include +#include + +namespace cryptonote { +class Currency; +} + +namespace CryptoNote { + +class TransfersConsumer; +class INode; + +class TransfersSyncronizer : public ITransfersSynchronizer { +public: + + TransfersSyncronizer(const cryptonote::Currency& currency, IBlockchainSynchronizer& sync, INode& node); + ~TransfersSyncronizer(); + + // ITransfersSynchronizer + virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) override; + virtual bool removeSubscription(const AccountAddress& acc) override; + virtual void getSubscriptions(std::vector& subscriptions) override; + virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) override; + + // IStreamSerializable + virtual void save(std::ostream& os) override; + virtual void load(std::istream& in) override; + +private: + + // map { view public key -> consumer } + std::unordered_map> m_consumers; + + // std::unordered_map> m_subscriptions; + IBlockchainSynchronizer& m_sync; + INode& m_node; + const cryptonote::Currency& m_currency; +}; + +} diff --git a/src/transfers/TypeHelpers.h b/src/transfers/TypeHelpers.h new file mode 100644 index 0000000000..dd7afdb0cc --- /dev/null +++ b/src/transfers/TypeHelpers.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ITransaction.h" +#include +#include + +namespace CryptoNote { + +inline bool operator==(const AccountAddress &_v1, const AccountAddress &_v2) { + return memcmp(&_v1, &_v2, sizeof(AccountAddress)) == 0; +} + +} + +namespace std { + +template<> +struct hash < CryptoNote::AccountAddress > { + std::size_t operator()(const CryptoNote::AccountAddress& val) const { + size_t spend = *(reinterpret_cast(&val.spendPublicKey)); + size_t view = *(reinterpret_cast(&val.viewPublicKey)); + return spend ^ view; + } +}; + +template<> +struct hash < CryptoNote::PublicKey > { + std::size_t operator()(const CryptoNote::PublicKey& val) const { + return *reinterpret_cast(&val); + } +}; + +} diff --git a/src/version.h.in b/src/version.h.in index c26fa936fe..cec6af9b10 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.2" -#define PROJECT_VERSION_BUILD_NO "358" +#define PROJECT_VERSION "1.0.3" +#define PROJECT_VERSION_BUILD_NO "387" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/KeysStorage.cpp b/src/wallet/KeysStorage.cpp new file mode 100644 index 0000000000..0fb7eafb9a --- /dev/null +++ b/src/wallet/KeysStorage.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "KeysStorage.h" + +#include "WalletSerialization.h" +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" +#include "cryptonote_core/cryptonote_serialization.h" + +namespace cryptonote { + +void KeysStorage::serialize(ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + + serializer(creationTimestamp, "creation_timestamp"); + + serializer(spendPublicKey, "spend_public_key"); + serializer(spendSecretKey, "spend_secret_key"); + + serializer(viewPublicKey, "view_public_key"); + serializer(viewSecretKey, "view_secret_key"); + + serializer.endObject(); +} + +} diff --git a/src/wallet/KeysStorage.h b/src/wallet/KeysStorage.h new file mode 100644 index 0000000000..423e506c32 --- /dev/null +++ b/src/wallet/KeysStorage.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "crypto/crypto.h" + +#include + +namespace cryptonote { + +class ISerializer; + +struct KeysStorage { + uint64_t creationTimestamp; + + crypto::public_key spendPublicKey; + crypto::secret_key spendSecretKey; + + crypto::public_key viewPublicKey; + crypto::secret_key viewSecretKey; + + void serialize(ISerializer& serializer, const std::string& name); +}; + +} //namespace cryptonote diff --git a/src/wallet/LegacyKeysImporter.cpp b/src/wallet/LegacyKeysImporter.cpp new file mode 100755 index 0000000000..8cdc22d29a --- /dev/null +++ b/src/wallet/LegacyKeysImporter.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LegacyKeysImporter.h" + +#include +#include + +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/AccountKVSerialization.h" +#include "file_io_utils.h" + +#include "serialization/binary_utils.h" +#include "storages/portable_storage.h" +#include "storages/portable_storage_template_helper.h" + +#include "wallet/wallet_errors.h" +#include "wallet/WalletSerializer.h" +#include "wallet/WalletUserTransactionsCache.h" +#include "wallet/WalletErrors.h" + +namespace { + +struct keys_file_data { + crypto::chacha8_iv iv; + std::string account_data; + + BEGIN_SERIALIZE_OBJECT() + FIELD(iv) + FIELD(account_data) + END_SERIALIZE() +}; + +bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { + crypto::public_key pub; + bool r = crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void loadKeysFromFile(const std::string& filename, const std::string& password, cryptonote::account_base& account) { + keys_file_data keys_file_data; + std::string buf; + bool r = epee::file_io_utils::load_file_to_string(filename, buf); + THROW_WALLET_EXCEPTION_IF(!r, tools::error::file_read_error, filename); + r = ::serialization::parse_binary(buf, keys_file_data); + THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, "internal error: failed to deserialize \"" + filename + '\"'); + + crypto::chacha8_key key; + crypto::cn_context cn_context; + crypto::generate_chacha8_key(cn_context, password, key); + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + + const ::cryptonote::account_keys& keys = account.get_keys(); + cryptonote::AccountBaseSerializer accountSerializer(account); + r = epee::serialization::load_t_from_binary(accountSerializer, account_data); + r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey); + r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey); + THROW_WALLET_EXCEPTION_IF(!r, tools::error::invalid_password); +} + +} + +namespace cryptonote { + +void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination) { + cryptonote::account_base account; + + try { + loadKeysFromFile(legacyKeysFilename, password, account); + } catch (tools::error::invalid_password&) { + throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); + } + + CryptoNote::WalletUserTransactionsCache transactionsCache; + std::string cache; + CryptoNote::WalletSerializer importer(account, transactionsCache); + importer.serialize(destination, password, false, cache); +} + +} //namespace cryptonote diff --git a/src/wallet/LegacyKeysImporter.h b/src/wallet/LegacyKeysImporter.h new file mode 100755 index 0000000000..d201397d5b --- /dev/null +++ b/src/wallet/LegacyKeysImporter.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace cryptonote { + +void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination); + +} //namespace cryptonote diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp old mode 100644 new mode 100755 index ae2c8e8f51..b864cb842b --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -21,22 +21,12 @@ #include "serialization/binary_utils.h" #include "storages/portable_storage_template_helper.h" #include "WalletUtils.h" - -#include -#include -#include -#include -#include -#include -#include +#include "WalletSerializer.h" #include #include #include "WalletSerialization.h" -#include -#include -#include namespace { @@ -70,39 +60,88 @@ void runAtomic(std::mutex& mutex, F f) { std::unique_lock lock(mutex); f(); } + +class InitWaiter : public CryptoNote::IWalletObserver { +public: + InitWaiter() : future(promise.get_future()) {} + + virtual void initCompleted(std::error_code result) override { + promise.set_value(result); + } + + std::error_code waitInit() { + return future.get(); + } +private: + std::promise promise; + std::future future; +}; + + +class SaveWaiter : public CryptoNote::IWalletObserver { +public: + SaveWaiter() : future(promise.get_future()) {} + + virtual void saveCompleted(std::error_code result) override { + promise.set_value(result); + } + + std::error_code waitSave() { + return future.get(); + } + +private: + std::promise promise; + std::future future; +}; } //namespace namespace CryptoNote { -void Wallet::WalletNodeObserver::postponeRefresh() { - std::unique_lock lock(postponeMutex); - postponed = true; -} +class SyncStarter : public CryptoNote::IWalletObserver { +public: + SyncStarter(BlockchainSynchronizer& sync) : m_sync(sync) {} + virtual ~SyncStarter() {} -void Wallet::WalletNodeObserver::saveCompleted(std::error_code result) { - bool startRefresh = false; - { - std::unique_lock lock(postponeMutex); - startRefresh = postponed; - postponed = false; + virtual void initCompleted(std::error_code result) { + if (!result) { + m_sync.start(); + } } - if (startRefresh) { - m_wallet->startRefresh(); - } -} + BlockchainSynchronizer& m_sync; +}; Wallet::Wallet(const cryptonote::Currency& currency, INode& node) : - m_state(NOT_INITIALIZED), - m_currency(currency), - m_node(node), - m_isSynchronizing(false), - m_isStopping(false), - m_transferDetails(currency, m_blockchain), - m_transactionsCache(m_sendingTxsStates), - m_synchronizer(m_account, m_node, m_blockchain, m_transferDetails, m_unconfirmedTransactions, m_transactionsCache), - m_sender(currency, m_transactionsCache, m_sendingTxsStates, m_transferDetails, m_unconfirmedTransactions) { - m_autoRefresher.reset(new WalletNodeObserver(this)); + m_state(NOT_INITIALIZED), + m_currency(currency), + m_node(node), + m_isStopping(false), + m_blockchainSync(node, currency.genesisBlockHash()), + m_transfersSync(currency, m_blockchainSync, node), + m_transferDetails(nullptr), + m_sender(nullptr), + m_onInitSyncStarter(new SyncStarter(m_blockchainSync)) +{ + addObserver(m_onInitSyncStarter.get()); + m_blockchainSync.addObserver(this); +} + +Wallet::~Wallet() { + removeObserver(m_onInitSyncStarter.get()); + + { + std::unique_lock lock(m_cacheMutex); + if (m_state != NOT_INITIALIZED) { + m_sender->stop(); + m_isStopping = true; + } + } + + m_blockchainSync.removeObserver(this); + m_blockchainSync.stop(); + m_asyncContextCounter.waitAsyncContextsFinish(); + m_sender.release(); } void Wallet::addObserver(IWalletObserver* observer) { @@ -118,89 +157,103 @@ void Wallet::initAndGenerate(const std::string& password) { std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(cryptonote::error::ALREADY_INITIALIZED)); } - m_node.addObserver(m_autoRefresher.get()); - addObserver(m_autoRefresher.get()); - m_account.generate(); m_password = password; - m_sender.init(m_account.get_keys()); - - storeGenesisBlock(); - - m_state = INITIALIZED; + initSync(); } m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); - refresh(); } -void Wallet::storeGenesisBlock() { - m_blockchain.push_back(m_currency.genesisBlockHash()); +void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) { + { + std::unique_lock stateLock(m_cacheMutex); + + if (m_state != NOT_INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::ALREADY_INITIALIZED)); + } + + cryptonote::account_keys keys; + + std::copy(accountKeys.spendPublicKey.begin(), + accountKeys.spendPublicKey.end(), + reinterpret_cast(&keys.m_account_address.m_spendPublicKey)); + + std::copy(accountKeys.viewPublicKey.begin(), + accountKeys.viewPublicKey.end(), + reinterpret_cast(&keys.m_account_address.m_viewPublicKey)); + + std::copy(accountKeys.spendSecretKey.begin(), + accountKeys.spendSecretKey.end(), + reinterpret_cast(&keys.m_spend_secret_key)); + + std::copy(accountKeys.viewSecretKey.begin(), + accountKeys.viewSecretKey.end(), + reinterpret_cast(&keys.m_view_secret_key)); + + m_account.set_keys(keys); + m_account.set_createtime(0); + m_password = password; + + initSync(); + } + + m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); } void Wallet::initAndLoad(std::istream& source, const std::string& password) { std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(cryptonote::error::ALREADY_INITIALIZED)); } - m_node.addObserver(m_autoRefresher.get()); - addObserver(m_autoRefresher.get()); - m_password = password; m_state = LOADING; + m_asyncContextCounter.addAsyncContext(); std::thread loader(&Wallet::doLoad, this, std::ref(source)); loader.detach(); } -void Wallet::doLoad(std::istream& source) { - try - { - std::unique_lock lock(m_cacheMutex); - - boost::archive::binary_iarchive ar(source); - - crypto::chacha8_iv iv; - std::string chacha_str;; - ar >> chacha_str; - - ::serialization::parse_binary(chacha_str, iv); - - std::string cipher; - ar >> cipher; - - std::string plain; - decrypt(cipher, plain, iv, m_password); +void Wallet::initSync() { + AccountSubscription sub; + sub.keys = reinterpret_cast(m_account.get_keys()); + sub.transactionSpendableAge = 10; + sub.syncStart.height = 0; + sub.syncStart.timestamp = m_account.get_createtime() - (60 * 60 * 24); + + auto& subObject = m_transfersSync.addSubscription(sub); + m_transferDetails = &subObject.getContainer(); + subObject.addObserver(this); - std::stringstream restore(plain); - - try - { - //boost archive ctor throws an exception if password is wrong (i.e. there's garbage in a stream) - boost::archive::binary_iarchive dataArchive(restore); - - dataArchive >> m_account; - - throwIfKeysMissmatch(m_account.get_keys().m_view_secret_key, m_account.get_keys().m_account_address.m_viewPublicKey); - throwIfKeysMissmatch(m_account.get_keys().m_spend_secret_key, m_account.get_keys().m_account_address.m_spendPublicKey); - - dataArchive >> m_blockchain; + m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.get_keys(), *m_transferDetails)); + m_state = INITIALIZED; +} - m_transferDetails.load(dataArchive); - m_unconfirmedTransactions.load(dataArchive); - m_transactionsCache.load(dataArchive); - } - catch (std::exception&) { - throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); +void Wallet::doLoad(std::istream& source) { + ContextCounterHolder counterHolder(m_asyncContextCounter); + try { + std::unique_lock lock(m_cacheMutex); + + std::string cache; + WalletSerializer serializer(m_account, m_transactionsCache); + serializer.deserialize(source, m_password, cache); + + initSync(); + + try { + if (!cache.empty()) { + std::stringstream stream(cache); + m_transfersSync.load(stream); + } + } catch (const std::exception&) { + // ignore cache loading errors } - - m_sender.init(m_account.get_keys()); } catch (std::system_error& e) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); @@ -213,11 +266,7 @@ void Wallet::doLoad(std::istream& source) { return; } - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); - - refresh(); } void Wallet::decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password) { @@ -239,16 +288,47 @@ void Wallet::shutdown() { m_isStopping = true; - if (m_state == NOT_INITIALIZED) + if (m_state != INITIALIZED) throwNotDefined(); - m_sender.stop(); - m_synchronizer.stop(); + m_sender->stop(); } + m_blockchainSync.removeObserver(this); + m_blockchainSync.stop(); m_asyncContextCounter.waitAsyncContextsFinish(); - m_node.removeObserver(m_autoRefresher.get()); - removeObserver(m_autoRefresher.get()); + + m_sender.release(); + + { + std::unique_lock lock(m_cacheMutex); + m_isStopping = false; + m_state = NOT_INITIALIZED; + } +} + +void Wallet::reset() { + InitWaiter initWaiter; + SaveWaiter saveWaiter; + + addObserver(&initWaiter); + addObserver(&saveWaiter); + + std::stringstream ss; + try { + save(ss, false, false); + + auto saveError = saveWaiter.waitSave(); + if (!saveError) { + shutdown(); + initAndLoad(ss, m_password); + initWaiter.waitInit(); + } + } catch (std::exception&) { + } + + removeObserver(&saveWaiter); + removeObserver(&initWaiter); } void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { @@ -274,35 +354,22 @@ void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache ContextCounterHolder counterHolder(m_asyncContextCounter); try { - //TODO: exception safety: leave destination stream empty in case of errors - boost::archive::binary_oarchive ar(destination); - - std::stringstream original; + m_blockchainSync.stop(); std::unique_lock lock(m_cacheMutex); + + WalletSerializer serializer(m_account, m_transactionsCache); + std::string cache; + + if (saveCache) { + std::stringstream stream; + m_transfersSync.save(stream); + cache = stream.str(); + } - boost::archive::binary_oarchive archive(original); - - archive << m_account; - - const BlockchainContainer& blockchain = saveCache ? m_blockchain : BlockchainContainer(); - - archive << blockchain; - - m_transferDetails.save(archive, saveCache); - m_unconfirmedTransactions.save(archive, saveCache); - m_transactionsCache.save(archive, saveDetailed, saveCache); - - std::string plain = original.str(); - std::string cipher; - - crypto::chacha8_iv iv = encrypt(plain, cipher); - - std::string chacha_str; - ::serialization::dump_binary(iv, chacha_str); - ar << chacha_str; - ar << cipher; + serializer.serialize(destination, m_password, saveDetailed, cache); m_state = INITIALIZED; + m_blockchainSync.start(); //XXX: start can throw. what to do in this case? } catch (std::system_error& e) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); @@ -356,22 +423,16 @@ uint64_t Wallet::actualBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); - return m_transferDetails.countActualBalance(); + return m_transferDetails->balance(ITransfersContainer::IncludeKeyUnlocked) - + m_transactionsCache.unconfrimedOutsAmount(); } uint64_t Wallet::pendingBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); - return doPendingBalance(); -} - -uint64_t Wallet::doPendingBalance() { - uint64_t amount = 0; - amount = m_transferDetails.countPendingBalance(); - amount += m_unconfirmedTransactions.countPendingBalance(); - - return amount; + uint64_t change = m_transactionsCache.unconfrimedOutsAmount() - m_transactionsCache.unconfirmedTransactionsAmount(); + return m_transferDetails->balance(ITransfersContainer::IncludeKeyNotUnlocked) + change; } size_t Wallet::getTransactionCount() { @@ -412,6 +473,7 @@ bool Wallet::getTransfer(TransferId transferId, Transfer& transfer) { TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { std::vector transfers; transfers.push_back(transfer); + throwIfNotInitialised(); return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp); } @@ -420,10 +482,11 @@ TransactionId Wallet::sendTransaction(const std::vector& transfers, ui TransactionId txId = 0; std::shared_ptr request; std::deque > events; + throwIfNotInitialised(); { std::unique_lock lock(m_cacheMutex); - request = m_sender.makeSendRequest(txId, events, transfers, fee, extra, mixIn, unlockTimestamp); + request = m_sender->makeSendRequest(txId, events, transfers, fee, extra, mixIn, unlockTimestamp); } notifyClients(events); @@ -452,7 +515,6 @@ void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::erro m_asyncContextCounter.addAsyncContext(); (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } - } void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { @@ -463,9 +525,6 @@ void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::erro { std::unique_lock lock(m_cacheMutex); callback(events, nextRequest, ec); - - if (!nextRequest) - m_isSynchronizing = false; } notifyClients(events); @@ -480,43 +539,56 @@ std::error_code Wallet::cancelTransaction(size_t transactionId) { return make_error_code(cryptonote::error::TX_CANCEL_IMPOSSIBLE); } -void Wallet::throwIfNotInitialised() { - if (m_state == NOT_INITIALIZED || m_state == LOADING) - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); +void Wallet::synchronizationProgressUpdated(uint64_t current, uint64_t total) { + // forward notification + m_observerManager.notify(&IWalletObserver::synchronizationProgressUpdated, current, total); + + // check if balance has changed and notify client + notifyIfBalanceChanged(); } -void Wallet::startRefresh() { - refresh(); +void Wallet::synchronizationCompleted(std::error_code result) { + if (result != std::make_error_code(std::errc::interrupted)) { + m_observerManager.notify(&IWalletObserver::synchronizationCompleted, result); + } + + if (!result) { + notifyIfBalanceChanged(); + } } -void Wallet::refresh() { - if (m_isStopping) - return; +void Wallet::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + std::shared_ptr event; - std::shared_ptr req; - { + TransactionInformation txInfo; + int64_t txBalance; + if (m_transferDetails->getTransactionInformation(transactionHash, txInfo, txBalance)) { std::unique_lock lock(m_cacheMutex); + event = m_transactionsCache.onTransactionUpdated(txInfo, txBalance); + } - if (m_state == SAVING) { - m_autoRefresher->postponeRefresh(); - return; - } - - if (m_state != INITIALIZED) { - return; - } + if (event.get()) { + event->notify(m_observerManager); + } +} - if (m_isSynchronizing) { - return; - } +void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + std::shared_ptr event; - m_isSynchronizing = true; + { + std::unique_lock lock(m_cacheMutex); + event = m_transactionsCache.onTransactionDeleted(transactionHash); + } - req = m_synchronizer.makeStartRefreshRequest(); + if (event.get()) { + event->notify(m_observerManager); } +} - m_asyncContextCounter.addAsyncContext(); - req->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); +void Wallet::throwIfNotInitialised() { + if (m_state == NOT_INITIALIZED || m_state == LOADING) + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + assert(m_transferDetails); } void Wallet::notifyClients(std::deque >& events) { @@ -527,4 +599,44 @@ void Wallet::notifyClients(std::deque >& events) { } } +void Wallet::notifyIfBalanceChanged() { + auto actual = actualBalance(); + auto prevActual = m_lastNotifiedActualBalance.exchange(actual); + + if (prevActual != actual) { + m_observerManager.notify(&IWalletObserver::actualBalanceUpdated, actual); + } + + auto pending = pendingBalance(); + auto prevPending = m_lastNotifiedPendingBalance.exchange(pending); + + if (prevPending != pending) { + m_observerManager.notify(&IWalletObserver::pendingBalanceUpdated, pending); + } + +} + +void Wallet::getAccountKeys(WalletAccountKeys& keys) { + if (m_state == NOT_INITIALIZED) { + throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + } + + const cryptonote::account_keys& accountKeys = m_account.get_keys(); + std::copy(reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey), + reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey) + sizeof(crypto::public_key), + keys.spendPublicKey.begin()); + + std::copy(reinterpret_cast(&accountKeys.m_spend_secret_key), + reinterpret_cast(&accountKeys.m_spend_secret_key) + sizeof(crypto::secret_key), + keys.spendSecretKey.begin()); + + std::copy(reinterpret_cast(&accountKeys.m_account_address.m_viewPublicKey), + reinterpret_cast(&accountKeys.m_account_address.m_viewPublicKey) + sizeof(crypto::public_key), + keys.viewPublicKey.begin()); + + std::copy(reinterpret_cast(&accountKeys.m_view_secret_key), + reinterpret_cast(&accountKeys.m_view_secret_key) + sizeof(crypto::secret_key), + keys.viewSecretKey.begin()); +} + } //namespace CryptoNote diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index a363995049..c6c27fac56 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -27,31 +27,40 @@ #include "INode.h" #include "WalletErrors.h" #include "WalletAsyncContextCounter.h" -#include "WalletTxSendingState.h" #include "common/ObserverManager.h" #include "cryptonote_core/tx_extra.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/Currency.h" -#include "WalletTransferDetails.h" #include "WalletUserTransactionsCache.h" #include "WalletUnconfirmedTransactions.h" -#include "WalletSynchronizer.h" + #include "WalletTransactionSender.h" #include "WalletRequest.h" +#include "transfers/BlockchainSynchronizer.h" +#include "transfers/TransfersSynchronizer.h" + namespace CryptoNote { -class Wallet : public IWallet { +class SyncStarter; + +class Wallet : + public IWallet, + IBlockchainSynchronizerObserver, + ITransfersObserver { + public: Wallet(const cryptonote::Currency& currency, INode& node); - ~Wallet() {}; + virtual ~Wallet(); virtual void addObserver(IWalletObserver* observer); virtual void removeObserver(IWalletObserver* observer); virtual void initAndGenerate(const std::string& password); virtual void initAndLoad(std::istream& source, const std::string& password); + virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password); virtual void shutdown(); + virtual void reset(); virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true); @@ -74,12 +83,20 @@ class Wallet : public IWallet { virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); virtual std::error_code cancelTransaction(size_t transactionId); - void startRefresh(); + virtual void getAccountKeys(WalletAccountKeys& keys); private: + + // IBlockchainSynchronizerObserver + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override; + virtual void synchronizationCompleted(std::error_code result) override; + + // ITransfersObserver + virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override; + virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) override; + + void initSync(); void throwIfNotInitialised(); - void refresh(); - uint64_t doPendingBalance(); void doSave(std::ostream& destination, bool saveDetailed, bool saveCache); void doLoad(std::istream& source); @@ -90,8 +107,7 @@ class Wallet : public IWallet { void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec); void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec); void notifyClients(std::deque >& events); - - void storeGenesisBlock(); + void notifyIfBalanceChanged(); enum WalletState { @@ -107,37 +123,22 @@ class Wallet : public IWallet { std::string m_password; const cryptonote::Currency& m_currency; INode& m_node; - bool m_isSynchronizing; bool m_isStopping; - typedef std::vector BlockchainContainer; + std::atomic m_lastNotifiedActualBalance; + std::atomic m_lastNotifiedPendingBalance; - BlockchainContainer m_blockchain; - WalletTransferDetails m_transferDetails; - WalletUnconfirmedTransactions m_unconfirmedTransactions; - tools::ObserverManager m_observerManager; + BlockchainSynchronizer m_blockchainSync; + TransfersSyncronizer m_transfersSync; + ITransfersContainer* m_transferDetails; - WalletTxSendingState m_sendingTxsStates; WalletUserTransactionsCache m_transactionsCache; + std::unique_ptr m_sender; - struct WalletNodeObserver: public INodeObserver, public IWalletObserver - { - WalletNodeObserver(Wallet* wallet) : m_wallet(wallet), postponed(false) {} - virtual void lastKnownBlockHeightUpdated(uint64_t height) { m_wallet->startRefresh(); } - virtual void saveCompleted(std::error_code result); - void postponeRefresh(); - - Wallet* m_wallet; - - std::mutex postponeMutex; - bool postponed; - }; - - std::unique_ptr m_autoRefresher; WalletAsyncContextCounter m_asyncContextCounter; + tools::ObserverManager m_observerManager; - WalletSynchronizer m_synchronizer; - WalletTransactionSender m_sender; + std::unique_ptr m_onInitSyncStarter; }; } //namespace CryptoNote diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/wallet/WalletAsyncContextCounter.cpp index 0359f5f9ba..75d3514855 100644 --- a/src/wallet/WalletAsyncContextCounter.cpp +++ b/src/wallet/WalletAsyncContextCounter.cpp @@ -33,7 +33,7 @@ void WalletAsyncContextCounter::delAsyncContext() { void WalletAsyncContextCounter::waitAsyncContextsFinish() { std::unique_lock lock(m_mutex); - while (m_asyncContexts) + while (m_asyncContexts > 0) m_cv.wait(lock); } diff --git a/src/wallet/WalletErrors.h b/src/wallet/WalletErrors.h index 477212e474..baf33464e8 100644 --- a/src/wallet/WalletErrors.h +++ b/src/wallet/WalletErrors.h @@ -38,7 +38,8 @@ enum WalletErrorCodes { ZERO_DESTINATION, TX_CANCEL_IMPOSSIBLE, TX_CANCELLED, - OPERATION_CANCELLED + OPERATION_CANCELLED, + TX_TRANSFER_IMPOSSIBLE }; // custom category: @@ -69,6 +70,7 @@ class WalletErrorCategory : public std::error_category { case TX_CANCEL_IMPOSSIBLE: return "Impossible to cancel transaction"; case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later"; case OPERATION_CANCELLED: return "The operation you've requested has been cancelled"; + case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible"; default: return "Unknown error"; } } diff --git a/src/wallet/WalletEvent.h b/src/wallet/WalletEvent.h index 611d2d65f0..3c21fbb807 100644 --- a/src/wallet/WalletEvent.h +++ b/src/wallet/WalletEvent.h @@ -79,20 +79,31 @@ class WalletExternalTransactionCreatedEvent : public WalletEvent class WalletSynchronizationProgressUpdatedEvent : public WalletEvent { public: - WalletSynchronizationProgressUpdatedEvent(uint64_t current, uint64_t total, std::error_code result) : m_current(current), m_total(total), m_ec(result) {}; + WalletSynchronizationProgressUpdatedEvent(uint64_t current, uint64_t total) : m_current(current), m_total(total) {}; virtual ~WalletSynchronizationProgressUpdatedEvent() {}; virtual void notify(tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::synchronizationProgressUpdated, m_current, m_total, m_ec); + observer.notify(&IWalletObserver::synchronizationProgressUpdated, m_current, m_total); } private: uint64_t m_current; uint64_t m_total; - std::error_code m_ec; }; +class WalletSynchronizationCompletedEvent : public WalletEvent { +public: + WalletSynchronizationCompletedEvent(uint64_t current, uint64_t total, std::error_code result) : m_ec(result) {}; + virtual ~WalletSynchronizationCompletedEvent() {}; + + virtual void notify(tools::ObserverManager& observer) { + observer.notify(&IWalletObserver::synchronizationCompleted, m_ec); + } + +private: + std::error_code m_ec; +}; class WalletActualBalanceUpdatedEvent : public WalletEvent { diff --git a/src/wallet/WalletHelper.cpp b/src/wallet/WalletHelper.cpp new file mode 100755 index 0000000000..806982357c --- /dev/null +++ b/src/wallet/WalletHelper.cpp @@ -0,0 +1,20 @@ +#include "WalletHelper.h" +#include + +#include "string_tools.h" +#include "cryptonote_protocol/blobdatatype.h" + +using namespace cryptonote; +using namespace epee; + + +void WalletHelper::prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { + keys_file = file_path; + wallet_file = file_path; + boost::system::error_code e; + if (string_tools::get_extension(keys_file) == "keys") {//provided keys file name + wallet_file = string_tools::cut_off_extension(wallet_file); + } else {//provided wallet file name + keys_file += ".keys"; + } +} diff --git a/src/wallet/WalletHelper.h b/src/wallet/WalletHelper.h new file mode 100755 index 0000000000..ac1cc9c61a --- /dev/null +++ b/src/wallet/WalletHelper.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "crypto/hash.h" +#include "IWallet.h" + + +namespace cryptonote { +namespace WalletHelper { + +class SaveWalletResultObserver : public CryptoNote::IWalletObserver { +public: + std::promise saveResult; + virtual void saveCompleted(std::error_code result) override { saveResult.set_value(result); } +}; + +class InitWalletResultObserver : public CryptoNote::IWalletObserver { +public: + std::promise initResult; + virtual void initCompleted(std::error_code result) override { initResult.set_value(result); } +}; + +class SendCompleteResultObserver : public CryptoNote::IWalletObserver { +public: + std::future expectedTxID; + std::promise sendResult; + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { + if (transactionId == expectedTxID.get()) sendResult.set_value(result); + } +}; + +void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file); + +} } diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h index 45f89ccae5..2ec27d4c80 100644 --- a/src/wallet/WalletRequest.h +++ b/src/wallet/WalletRequest.h @@ -17,16 +17,17 @@ #pragma once -#include -#include -#include - #include "INode.h" - -#include "WalletSynchronizationContext.h" +// #include "WalletSynchronizationContext.h" #include "WalletSendTransactionContext.h" #include "WalletEvent.h" +#include + +#include +#include +#include + namespace CryptoNote { class WalletRequest @@ -39,40 +40,6 @@ class WalletRequest virtual void perform(INode& node, std::function cb) = 0; }; -class WalletGetNewBlocksRequest: public WalletRequest -{ -public: - WalletGetNewBlocksRequest(const std::list& knownBlockIds, std::shared_ptr context, Callback cb) : m_ids(knownBlockIds), m_context(context), m_cb(cb) {}; - virtual ~WalletGetNewBlocksRequest() {}; - - virtual void perform(INode& node, std::function cb) - { - node.getNewBlocks(std::move(m_ids), std::ref(m_context->newBlocks), std::ref(m_context->startHeight), std::bind(cb, m_cb, std::placeholders::_1)); - }; - -private: - std::shared_ptr m_context; - std::list m_ids; - Callback m_cb; -}; - -class WalletGetTransactionOutsGlobalIndicesRequest: public WalletRequest -{ -public: - WalletGetTransactionOutsGlobalIndicesRequest(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, Callback cb) : m_hash(transactionHash), m_outs(outsGlobalIndices), m_cb(cb) {}; - virtual ~WalletGetTransactionOutsGlobalIndicesRequest() {}; - - virtual void perform(INode& node, std::function cb) - { - node.getTransactionOutsGlobalIndices(m_hash, std::ref(m_outs), std::bind(cb, m_cb, std::placeholders::_1)); - }; - -private: - crypto::hash m_hash; - std::vector& m_outs; - Callback m_cb; -}; - class WalletGetRandomOutsByAmountsRequest: public WalletRequest { public: diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h index c27815d07a..db40d86474 100644 --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/wallet/WalletSendTransactionContext.h @@ -22,6 +22,7 @@ #include "cryptonote_core/cryptonote_basic.h" #include "IWallet.h" +#include "ITransfersContainer.h" namespace CryptoNote { @@ -32,11 +33,7 @@ struct TxDustPolicy cryptonote::AccountPublicAddress addrForDust; TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::AccountPublicAddress an_addr_for_dust = cryptonote::AccountPublicAddress()) - : dustThreshold(a_dust_threshold) - , addToFee(an_add_to_fee) - , addrForDust(an_addr_for_dust) - { - } + : dustThreshold(a_dust_threshold), addToFee(an_add_to_fee), addrForDust(an_addr_for_dust) {} }; struct SendTransactionContext @@ -44,8 +41,7 @@ struct SendTransactionContext TransactionId transactionId; std::vector outs; uint64_t foundMoney; - std::list selectedTransfers; - uint64_t unlockTimestamp; + std::list selectedTransfers; TxDustPolicy dustPolicy; uint64_t mixIn; }; diff --git a/src/wallet/WalletSerialization.cpp b/src/wallet/WalletSerialization.cpp new file mode 100644 index 0000000000..c207eb741d --- /dev/null +++ b/src/wallet/WalletSerialization.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletSerialization.h" +#include "WalletUnconfirmedTransactions.h" +#include "IWallet.h" + +#include "cryptonote_core/cryptonote_serialization.h" +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" + +namespace CryptoNote { + +void serialize(UnconfirmedTransferDetails& utd, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(utd.tx, "transaction"); + serializer(utd.amount, "amount"); + serializer(utd.outsAmount, "outs_amount"); + uint64_t time = static_cast(utd.sentTime); + serializer(time, "sent_time"); + utd.sentTime = static_cast(time); + uint64_t txId = static_cast(utd.transactionId); + serializer(txId, "transaction_id"); + utd.transactionId = static_cast(txId); + serializer.endObject(); +} + +void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + + uint64_t trId = static_cast(txi.firstTransferId); + serializer(trId, "first_transfer_id"); + txi.firstTransferId = static_cast(trId); + + uint64_t trCount = static_cast(txi.transferCount); + serializer(trCount, "transfer_count"); + txi.transferCount = static_cast(trCount); + + serializer(txi.totalAmount, "total_amount"); + + serializer(txi.fee, "fee"); + serializer(txi.hash, "hash"); + serializer(txi.isCoinbase, "is_coinbase"); + serializer(txi.blockHeight, "block_height"); + serializer(txi.timestamp, "timestamp"); + serializer(txi.unlockTime, "unlock_time"); + serializer(txi.extra, "extra"); + serializer.endObject(); +} + +void serialize(Transfer& tr, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(tr.address, "address"); + serializer(tr.amount, "amount"); + serializer.endObject(); +} + +} //namespace CryptoNote diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h old mode 100644 new mode 100755 index 2126575b53..613ac73659 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -17,80 +17,25 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include -#include "cryptonote_core/AccountKVSerialization.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" -#include "common/unordered_containers_boost_serialization.h" -#include "storages/portable_storage_template_helper.h" +#include "IWallet.h" -BOOST_SERIALIZATION_SPLIT_FREE(cryptonote::account_base); - -namespace boost { -namespace serialization { - -template -inline void load(Archive & ar, cryptonote::account_base& account, const unsigned int version) -{ - std::string data; - ar >> data; - cryptonote::AccountBaseSerializer accountSerializer(account); - epee::serialization::load_t_from_binary(accountSerializer, data); +namespace cryptonote { +class ISerializer; } -template -inline void save(Archive & ar, const cryptonote::account_base& account, const unsigned int version) -{ - std::string data; - cryptonote::AccountBaseSerializer accountSerializer(account); - epee::serialization::store_t_to_binary(accountSerializer, data); - ar << data; -} +namespace CryptoNote { -template -inline void serialize(Archive & ar, CryptoNote::TransactionInfo& tx, const unsigned int version) -{ - ar & tx.firstTransferId; - ar & tx.transferCount; - ar & tx.totalAmount; - ar & tx.fee; - ar & make_array(tx.hash.data(), tx.hash.size()); - ar & tx.isCoinbase; - ar & tx.blockHeight; - ar & tx.timestamp; - ar & tx.extra; -} +struct UnconfirmedTransferDetails; +struct TransactionInfo; +struct Transfer; -template -inline void serialize(Archive & ar, CryptoNote::Transfer& tr, const unsigned int version) -{ - ar & tr.address; - ar & tr.amount; -} - -template -inline void serialize(Archive & ar, CryptoNote::TransferDetails& details, const unsigned int version) -{ - ar & details.blockHeight; - ar & details.tx; - ar & details.internalOutputIndex; - ar & details.globalOutputIndex; - ar & details.spent; - ar & details.keyImage; -} +void serialize(UnconfirmedTransferDetails& utd, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(Transfer& tr, const std::string& name, cryptonote::ISerializer& serializer); -template -inline void serialize(Archive & ar, CryptoNote::UnconfirmedTransferDetails& details, const unsigned int version) -{ - ar & details.tx; - ar & details.change; - ar & details.sentTime; - ar & details.transactionId; } -} // namespace serialization -} // namespace boost diff --git a/src/wallet/WalletSerializer.cpp b/src/wallet/WalletSerializer.cpp new file mode 100644 index 0000000000..d8559716d3 --- /dev/null +++ b/src/wallet/WalletSerializer.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletSerializer.h" + +#include + +#include "serialization/BinaryOutputStreamSerializer.h" +#include "serialization/BinaryInputStreamSerializer.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_serialization.h" +#include "WalletUserTransactionsCache.h" +#include "WalletErrors.h" +#include "KeysStorage.h" + +namespace { + +bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { + crypto::public_key pub; + bool r = crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void throwIfKeysMissmatch(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { + if (!verifyKeys(sec, expected_pub)) + throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); +} + +} + +namespace CryptoNote { + +WalletSerializer::WalletSerializer(cryptonote::account_base& account, WalletUserTransactionsCache& transactionsCache) : + account(account), + transactionsCache(transactionsCache), + walletSerializationVersion(1) +{ +} + +void WalletSerializer::serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache) { + std::stringstream plainArchive; + cryptonote::BinaryOutputStreamSerializer serializer(plainArchive); + saveKeys(serializer); + + serializer(saveDetailed, "has_details"); + + if (saveDetailed) { + serializer(transactionsCache, "details"); + } + + serializer.binary(const_cast(cache), "cache"); + + std::string plain = plainArchive.str(); + std::string cipher; + + crypto::chacha8_iv iv = encrypt(plain, password, cipher); + + uint32_t version = walletSerializationVersion; + cryptonote::BinaryOutputStreamSerializer s(stream); + s.beginObject("wallet"); + s(version, "version"); + s(iv, "iv"); + s(cipher, "data"); + s.endObject(); +} + +void WalletSerializer::saveKeys(cryptonote::ISerializer& serializer) { + cryptonote::KeysStorage keys; + cryptonote::account_keys acc = account.get_keys(); + + keys.creationTimestamp = account.get_createtime(); + keys.spendPublicKey = acc.m_account_address.m_spendPublicKey; + keys.spendSecretKey = acc.m_spend_secret_key; + keys.viewPublicKey = acc.m_account_address.m_viewPublicKey; + keys.viewSecretKey = acc.m_view_secret_key; + + keys.serialize(serializer, "keys"); +} + +crypto::chacha8_iv WalletSerializer::encrypt(const std::string& plain, const std::string& password, std::string& cipher) { + crypto::chacha8_key key; + crypto::cn_context context; + crypto::generate_chacha8_key(context, password, key); + + cipher.resize(plain.size()); + + crypto::chacha8_iv iv = crypto::rand(); + crypto::chacha8(plain.data(), plain.size(), key, iv, &cipher[0]); + + return iv; +} + + +void WalletSerializer::deserialize(std::istream& stream, const std::string& password, std::string& cache) { + cryptonote::BinaryInputStreamSerializer serializerEncrypted(stream); + + serializerEncrypted.beginObject("wallet"); + + uint32_t version; + serializerEncrypted(version, "version"); + + crypto::chacha8_iv iv; + serializerEncrypted(iv, "iv"); + + std::string cipher; + serializerEncrypted(cipher, "data"); + + serializerEncrypted.endObject(); + + std::string plain; + decrypt(cipher, plain, iv, password); + + std::stringstream decryptedStream(plain); + + cryptonote::BinaryInputStreamSerializer serializer(decryptedStream); + + try + { + loadKeys(serializer); + throwIfKeysMissmatch(account.get_keys().m_view_secret_key, account.get_keys().m_account_address.m_viewPublicKey); + throwIfKeysMissmatch(account.get_keys().m_spend_secret_key, account.get_keys().m_account_address.m_spendPublicKey); + } + catch (std::exception&) { + throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); + } + + bool detailsSaved; + + serializer(detailsSaved, "has_details"); + + if (detailsSaved) { + serializer(transactionsCache, "details"); + } + + serializer.binary(cache, "cache"); +} + +void WalletSerializer::decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password) { + crypto::chacha8_key key; + crypto::cn_context context; + crypto::generate_chacha8_key(context, password, key); + + plain.resize(cipher.size()); + + crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]); +} + +void WalletSerializer::loadKeys(cryptonote::ISerializer& serializer) { + cryptonote::KeysStorage keys; + + keys.serialize(serializer, "keys"); + + cryptonote::account_keys acc; + acc.m_account_address.m_spendPublicKey = keys.spendPublicKey; + acc.m_spend_secret_key = keys.spendSecretKey; + acc.m_account_address.m_viewPublicKey = keys.viewPublicKey; + acc.m_view_secret_key = keys.viewSecretKey; + + account.set_keys(acc); + account.set_createtime(keys.creationTimestamp); +} + +} + + diff --git a/src/wallet/WalletSerializer.h b/src/wallet/WalletSerializer.h new file mode 100644 index 0000000000..2fe1137310 --- /dev/null +++ b/src/wallet/WalletSerializer.h @@ -0,0 +1,55 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include "crypto/hash.h" +#include "crypto/chacha8.h" + +namespace cryptonote { +class account_base; +class ISerializer; +} + +namespace CryptoNote { + +class WalletUserTransactionsCache; + +class WalletSerializer { +public: + WalletSerializer(cryptonote::account_base& account, WalletUserTransactionsCache& transactionsCache); + + void serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache); + void deserialize(std::istream& stream, const std::string& password, std::string& cache); + +private: + void saveKeys(cryptonote::ISerializer& serializer); + void loadKeys(cryptonote::ISerializer& serializer); + + crypto::chacha8_iv encrypt(const std::string& plain, const std::string& password, std::string& cipher); + void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password); + + cryptonote::account_base& account; + WalletUserTransactionsCache& transactionsCache; + const uint32_t walletSerializationVersion; +}; + +} //namespace CryptoNote diff --git a/src/wallet/WalletSynchronizationContext.h b/src/wallet/WalletSynchronizationContext.h deleted file mode 100644 index 5e42da6b67..0000000000 --- a/src/wallet/WalletSynchronizationContext.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include -#include -#include - -#include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" - -namespace CryptoNote { - -struct TransactionContextInfo -{ - std::vector requestedOuts; - std::vector globalIndices; - cryptonote::Transaction transaction; - crypto::public_key transactionPubKey; -}; - -struct SynchronizationState -{ - SynchronizationState() : blockIdx(0), transactionIdx(0), minersTxProcessed(false) {} - size_t blockIdx; //block index within context->new_blocks array to be processed - size_t transactionIdx; //tx index within the block to be processed - bool minersTxProcessed; //is miner's tx in the block processed -}; - -struct SynchronizationContext -{ - std::list newBlocks; - uint64_t startHeight; - std::unordered_map transactionContext; - SynchronizationState progress; -}; - -} //namespace CryptoNote diff --git a/src/wallet/WalletSynchronizer.cpp b/src/wallet/WalletSynchronizer.cpp deleted file mode 100644 index b96f83140e..0000000000 --- a/src/wallet/WalletSynchronizer.cpp +++ /dev/null @@ -1,491 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "WalletSynchronizer.h" - -#include - -#include "cryptonote_core/account.h" - -#include "WalletErrors.h" -#include "WalletUtils.h" - -namespace { - -void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) { - if (expr) - throw std::system_error(make_error_code(ec)); -} - -bool getTxPubKey(const cryptonote::Transaction& tx, crypto::public_key& key) { - std::vector extraFields; - cryptonote::parse_tx_extra(tx.extra, extraFields); - - cryptonote::tx_extra_pub_key pubKeyField; - if(!cryptonote::find_tx_extra_field_by_type(extraFields, pubKeyField)) { - //Public key wasn't found in the transaction extra. Skipping transaction - return false; - } - - key = pubKeyField.pub_key; - return true; -} - -void findMyOuts(const cryptonote::account_keys& acc, const cryptonote::Transaction& tx, const crypto::public_key& txPubKey, std::vector& outs, uint64_t& moneyTransfered) { - bool r = cryptonote::lookup_acc_outs(acc, tx, txPubKey, outs, moneyTransfered); - throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); -} - -uint64_t countOverallTxOutputs(const cryptonote::Transaction& tx) { - uint64_t amount = 0; - for (const cryptonote::TransactionOutput& o: tx.vout) { - amount += o.amount; - } - - return amount; -} - -uint64_t countOverallTxInputs(const cryptonote::Transaction& tx) { - uint64_t amount = 0; - for (auto& in: tx.vin) { - if(in.type() != typeid(cryptonote::TransactionInputToKey)) - continue; - - amount += boost::get(in).amount; - } - - return amount; -} - -void fillTransactionHash(const cryptonote::Transaction& tx, CryptoNote::TransactionHash& hash) { - crypto::hash h = cryptonote::get_transaction_hash(tx); - memcpy(hash.data(), reinterpret_cast(&h), hash.size()); -} - -} - -namespace CryptoNote -{ - -WalletSynchronizer::WalletSynchronizer(const cryptonote::account_base& account, INode& node, std::vector& blockchain, - WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions, - WalletUserTransactionsCache& transactionsCache) : - m_account(account), - m_node(node), - m_blockchain(blockchain), - m_transferDetails(transferDetails), - m_unconfirmedTransactions(unconfirmedTransactions), - m_transactionsCache(transactionsCache), - m_actualBalance(0), - m_pendingBalance(0), - m_isStoping(false) { -} - -void WalletSynchronizer::stop() { - m_isStoping = true; -} - -std::shared_ptr WalletSynchronizer::makeStartRefreshRequest() { - std::shared_ptr context = std::make_shared(); - std::shared_ptr request = makeGetNewBlocksRequest(context); - - return request; -} - -void WalletSynchronizer::postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector& outsGlobalIndices, uint64_t height) { - parameters.nextRequest = std::make_shared(hash, outsGlobalIndices, - std::bind(&WalletSynchronizer::handleTransactionOutGlobalIndicesResponse, this, parameters.context, hash, height, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); -} - -std::shared_ptr WalletSynchronizer::makeGetNewBlocksRequest(std::shared_ptr context) { - context->newBlocks.clear(); - context->startHeight = 0; - context->progress = SynchronizationState(); - - std::list ids; - getShortChainHistory(ids); - - std::shared_ptrreq = std::make_shared(ids, context, std::bind(&WalletSynchronizer::handleNewBlocksPortion, this, - context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - - return req; -} - -void WalletSynchronizer::getShortChainHistory(std::list& ids) { - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = m_blockchain.size(); - - if(!sz) - return; - - size_t current_back_offset = 1; - bool genesis_included = false; - - while(current_back_offset < sz) { - ids.push_back(m_blockchain[sz-current_back_offset]); - if(sz-current_back_offset == 0) - genesis_included = true; - if(i < 10) { - ++current_back_offset; - }else - { - current_back_offset += current_multiplier *= 2; - } - ++i; - } - - if(!genesis_included) - ids.push_back(m_blockchain[0]); -} - -void WalletSynchronizer::handleNewBlocksPortion(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec) { - if (m_isStoping) { - return; - } - - if (ec) { - events.push_back(std::make_shared(context->startHeight, m_node.getLastLocalBlockHeight(), ec)); - return; - } - - ProcessParameters parameters; - parameters.context = context; - - try - { - bool fillRequest = processNewBlocks(parameters); - - if (fillRequest) { - parameters.nextRequest = makeGetNewBlocksRequest(context); - } - } - catch (std::system_error& e) { - parameters.nextRequest = boost::none; - parameters.events.push_back(std::make_shared(context->startHeight, m_node.getLastLocalBlockHeight(), e.code())); - } - catch (std::exception&) { - parameters.nextRequest = boost::none; - parameters.events.push_back(std::make_shared(context->startHeight, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); - } - - refreshBalance(events); - - std::copy(parameters.events.begin(), parameters.events.end(), std::back_inserter(events)); - nextRequest = parameters.nextRequest; -} - -//returns true if new request should be performed -bool WalletSynchronizer::processNewBlocks(ProcessParameters& parameters) { - bool fillRequest = false; - std::shared_ptr context = parameters.context; - - size_t currentIndex = context->startHeight + context->progress.blockIdx; - - try - { - auto blocksIt = context->newBlocks.begin(); - std::advance(blocksIt, context->progress.blockIdx); - - for (; blocksIt != context->newBlocks.end(); ++blocksIt) { - if (m_isStoping) return false; - - auto& blockEntry = *blocksIt; - - NextBlockAction action = handleNewBlockchainEntry(parameters, blockEntry, currentIndex); - - if (action == INTERRUPT) - return false; - else if (action == CONTINUE) - fillRequest = true; - - ++context->progress.blockIdx; - context->progress.minersTxProcessed = false; - ++currentIndex; - } - } - catch (std::exception& e) { - context->startHeight = currentIndex; - throw e; - } - - return fillRequest; -} - -WalletSynchronizer::NextBlockAction WalletSynchronizer::handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height) { - cryptonote::Block b; - bool r = cryptonote::parse_and_validate_block_from_blob(blockEntry.block, b); - throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); - - crypto::hash blockId = get_block_hash(b); - if (height >= m_blockchain.size()) { - r = processNewBlockchainEntry(parameters, blockEntry, b, blockId, height); - - if (r) { - parameters.events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), std::error_code())); - return CONTINUE; - } - return INTERRUPT; - } - - if(blockId != m_blockchain[height]) { - //split detected here !!! - //Wrong daemon response - throwIf(height == parameters.context->startHeight, cryptonote::error::INTERNAL_WALLET_ERROR); - - detachBlockchain(height); - - r = processNewBlockchainEntry(parameters, blockEntry, b, blockId, height); - - if (r) { - parameters.events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), std::error_code())); - return CONTINUE; - } - return INTERRUPT; - } - - //we already have this block. - return SKIP; -} - -bool WalletSynchronizer::processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::Block& b, crypto::hash& blockId, uint64_t height) { - throwIf(height != m_blockchain.size(), cryptonote::error::INTERNAL_WALLET_ERROR); - - if(b.timestamp + 60*60*24 > m_account.get_createtime()) { - if (!processMinersTx(parameters, b.minerTx, height, b.timestamp)) - return false; - - auto txIt = blockEntry.txs.begin(); - std::advance(txIt, parameters.context->progress.transactionIdx); - - for (; txIt != blockEntry.txs.end(); ++txIt) { - auto& txblob = *txIt; - cryptonote::Transaction tx; - - bool r = parse_and_validate_tx_from_blob(txblob, tx); - throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); - - r = processNewTransaction(parameters, tx, height, false, b.timestamp); - parameters.context->progress.transactionIdx++; - - if (!r) return false; - } - } - - parameters.context->progress.transactionIdx = 0; - - m_blockchain.push_back(blockId); - return true; -} - -bool WalletSynchronizer::processMinersTx(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp) { - bool r = true; - - if (!parameters.context->progress.minersTxProcessed) { - r = processNewTransaction(parameters, tx, height, true, timestamp); - parameters.context->progress.minersTxProcessed = true; - } - - return r; -} - -bool WalletSynchronizer::processNewTransaction(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp) { - bool res = true; - - processUnconfirmed(parameters, tx, height, timestamp); - std::vector outs; - uint64_t moneyInMyOuts = 0; - - crypto::public_key publicKey; - if (!getTxPubKey(tx, publicKey)) - return true; //Public key wasn't found in the transaction extra. Skipping transaction - - findMyOuts(m_account.get_keys(), tx, publicKey, outs, moneyInMyOuts); - - if(!outs.empty() && moneyInMyOuts) { - fillGetTransactionOutsGlobalIndicesRequest(parameters, tx, outs, publicKey, height); - res = false; - } - - uint64_t moneyInMyInputs = processMyInputs(tx); - - if (!moneyInMyOuts && !moneyInMyInputs) - return res; //There's nothing related to our account, skip it - - updateTransactionsCache(parameters, tx, moneyInMyOuts, moneyInMyInputs, height, isCoinbase, timestamp); - - return res; -} - -uint64_t WalletSynchronizer::processMyInputs(const cryptonote::Transaction& tx) { - uint64_t money = 0; - // check all outputs for spending (compare key images) - for (auto& in: tx.vin) { - if(in.type() != typeid(cryptonote::TransactionInputToKey)) - continue; - - size_t idx; - if (!m_transferDetails.getTransferDetailsIdxByKeyImage(boost::get(in).keyImage, idx)) - continue; - - money += boost::get(in).amount; - - TransferDetails& td = m_transferDetails.getTransferDetails(idx); - td.spent = true; - } - - return money; -} - -void WalletSynchronizer::fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::Transaction& tx, - const std::vector& outs, const crypto::public_key& publicKey, uint64_t height) { - crypto::hash txid = cryptonote::get_transaction_hash(tx); - - TransactionContextInfo tx_context; - tx_context.requestedOuts = outs; - tx_context.transaction = tx; - tx_context.transactionPubKey = publicKey; - - auto insert_result = parameters.context->transactionContext.emplace(txid, std::move(tx_context)); - - postGetTransactionOutsGlobalIndicesRequest(parameters, txid, insert_result.first->second.globalIndices, height); -} - -void WalletSynchronizer::updateTransactionsCache(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, bool isCoinbase, uint64_t timestamp) { - - uint64_t allOuts = countOverallTxOutputs(tx); - uint64_t allInputs = countOverallTxInputs(tx); - - TransactionId foundTx = m_transactionsCache.findTransactionByHash(cryptonote::get_transaction_hash(tx)); - if (foundTx == INVALID_TRANSACTION_ID) { - TransactionInfo transaction; - transaction.firstTransferId = INVALID_TRANSFER_ID; - transaction.transferCount = 0; - transaction.totalAmount = myOuts - myInputs; - transaction.fee = isCoinbase ? 0 : allInputs - allOuts; - fillTransactionHash(tx, transaction.hash); - transaction.blockHeight = height; - transaction.isCoinbase = isCoinbase; - transaction.timestamp = timestamp; - - TransactionId newId = m_transactionsCache.insertTransaction(std::move(transaction)); - - parameters.events.push_back(std::make_shared(newId)); - } - else - { - TransactionInfo& transaction = m_transactionsCache.getTransaction(foundTx); - transaction.blockHeight = height; - transaction.timestamp = timestamp; - transaction.isCoinbase = isCoinbase; - - parameters.events.push_back(std::make_shared(foundTx)); - } -} - -void WalletSynchronizer::processUnconfirmed(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp) { - TransactionId id; - crypto::hash hash = get_transaction_hash(tx); - - if (!m_unconfirmedTransactions.findTransactionId(hash, id)) - return; - - TransactionInfo& tr = m_transactionsCache.getTransaction(id); - tr.blockHeight = height; - tr.timestamp = timestamp; - - m_unconfirmedTransactions.erase(hash); - - parameters.events.push_back(std::make_shared(id)); -} - -void WalletSynchronizer::handleTransactionOutGlobalIndicesResponse(std::shared_ptr context, crypto::hash txid, uint64_t height, - std::deque >& events, boost::optional >& nextRequest, std::error_code ec) { - if (m_isStoping) { - return; - } - - if (ec) { - events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), ec)); - return; - } - - try - { - auto it = context->transactionContext.find(txid); - if (it == context->transactionContext.end()) { - events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); - return; - } - - cryptonote::Transaction& tx = it->second.transaction; - std::vector& outs = it->second.requestedOuts; - crypto::public_key& tx_pub_key = it->second.transactionPubKey; - std::vector& global_indices = it->second.globalIndices; - - for (size_t o: outs) { - throwIf(tx.vout.size() <= o, cryptonote::error::INTERNAL_WALLET_ERROR); - - TransferDetails td; - td.blockHeight = height; - td.internalOutputIndex = o; - td.globalOutputIndex = global_indices[o]; - td.tx = tx; - td.spent = false; - cryptonote::KeyPair in_ephemeral; - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.keyImage); - throwIf(in_ephemeral.pub != boost::get(tx.vout[o].target).key, cryptonote::error::INTERNAL_WALLET_ERROR); - - m_transferDetails.addTransferDetails(td); - } - - context->transactionContext.erase(it); - } - catch (std::exception&) { - events.push_back(std::make_shared(height, m_node.getLastLocalBlockHeight(), make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); - return; - } - - handleNewBlocksPortion(context, events, nextRequest, ec); -} - -void WalletSynchronizer::detachBlockchain(uint64_t height) { - m_transferDetails.detachTransferDetails(height); - - m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); - - m_transactionsCache.detachTransactions(height); -} - -void WalletSynchronizer::refreshBalance(std::deque >& events) { - uint64_t actualBalance = m_transferDetails.countActualBalance(); - uint64_t pendingBalance = m_unconfirmedTransactions.countPendingBalance(); - pendingBalance += m_transferDetails.countPendingBalance(); - - if (actualBalance != m_actualBalance) { - events.push_back(std::make_shared(actualBalance)); - m_actualBalance = actualBalance; - } - - if (pendingBalance != m_pendingBalance) { - events.push_back(std::make_shared(pendingBalance)); - m_pendingBalance = pendingBalance; - } -} - -} /* namespace CryptoNote */ diff --git a/src/wallet/WalletSynchronizer.h b/src/wallet/WalletSynchronizer.h deleted file mode 100644 index 9b69cb6c93..0000000000 --- a/src/wallet/WalletSynchronizer.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include - -#include "INode.h" -#include "crypto/hash.h" -#include "WalletTransferDetails.h" -#include "WalletUnconfirmedTransactions.h" -#include "WalletUserTransactionsCache.h" -#include "WalletEvent.h" -#include "WalletSynchronizationContext.h" -#include "WalletRequest.h" - -namespace CryptoNote { - -class WalletSynchronizer -{ -public: - WalletSynchronizer(const cryptonote::account_base& account, INode& node, std::vector& blockchain, WalletTransferDetails& transferDetails, - WalletUnconfirmedTransactions& unconfirmedTransactions, WalletUserTransactionsCache& transactionsCache); - - std::shared_ptr makeStartRefreshRequest(); - - void stop(); - -private: - - struct ProcessParameters { - std::shared_ptr context; - std::vector > events; - boost::optional > nextRequest; - }; - - enum NextBlockAction { - INTERRUPT = 0, - CONTINUE, - SKIP - }; - - void handleNewBlocksPortion(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec); - void handleTransactionOutGlobalIndicesResponse(std::shared_ptr context, crypto::hash txid, uint64_t height, - std::deque >& events, boost::optional >& nextRequest, std::error_code ec); - - void getShortChainHistory(std::list& ids); - - bool processNewBlocks(ProcessParameters& parameters); - NextBlockAction handleNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, uint64_t height); - bool processNewBlockchainEntry(ProcessParameters& parameters, cryptonote::block_complete_entry& blockEntry, const cryptonote::Block& b, crypto::hash& blockId, uint64_t height); - bool processNewTransaction(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, bool isCoinbase, uint64_t timestamp); - bool processMinersTx(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp); - void processUnconfirmed(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t height, uint64_t timestamp); - uint64_t processMyInputs(const cryptonote::Transaction& tx); - void updateTransactionsCache(ProcessParameters& parameters, const cryptonote::Transaction& tx, uint64_t myOuts, uint64_t myInputs, uint64_t height, - bool isCoinbase, uint64_t timestamp); - void detachBlockchain(uint64_t height); - void refreshBalance(std::deque >& events); - - void fillGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const cryptonote::Transaction& tx, - const std::vector& outs, const crypto::public_key& publicKey, uint64_t height); - void postGetTransactionOutsGlobalIndicesRequest(ProcessParameters& parameters, const crypto::hash& hash, std::vector& outsGlobalIndices, uint64_t height); - std::shared_ptr makeGetNewBlocksRequest(std::shared_ptr context); - - const cryptonote::account_base& m_account; - INode& m_node; - std::vector& m_blockchain; - WalletTransferDetails& m_transferDetails; - WalletUnconfirmedTransactions& m_unconfirmedTransactions; - WalletUserTransactionsCache& m_transactionsCache; - - uint64_t m_actualBalance; - uint64_t m_pendingBalance; - - bool m_isStoping; -}; - -} /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index e831b7fe7c..bfc61d3d34 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -19,12 +19,19 @@ #include "misc_language.h" #include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" #include "WalletTransactionSender.h" #include "WalletUtils.h" +#include "cryptonote_core/cryptonote_basic_impl.h" + +#include + namespace { +using namespace CryptoNote; + uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { uint64_t needed_money = fee; for (auto& transfer: transfers) { @@ -61,30 +68,22 @@ void fillTransactionHash(const cryptonote::Transaction& tx, CryptoNote::Transact memcpy(hash.data(), reinterpret_cast(&h), hash.size()); } +std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { + transactionCache.updateTransactionSendingState(transactionId, ec); + return std::make_shared(transactionId, ec); +} + } //namespace namespace CryptoNote { -WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, - WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions): - m_currency(currency), - m_transactionsCache(transactionsCache), - m_sendingTxsStates(sendingTxsStates), - m_transferDetails(transferDetails), - m_unconfirmedTransactions(unconfirmedTransactions), - m_isInitialized(false), - m_isStoping(false) { - m_upperTransactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); -} - -void WalletTransactionSender::init(cryptonote::account_keys keys) { - if (m_isInitialized) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); - } - - m_keys = keys; - m_isInitialized = true; -} +WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, cryptonote::account_keys keys, ITransfersContainer& transfersContainer) : + m_currency(currency), + m_transactionsCache(transactionsCache), + m_isStoping(false), + m_keys(keys), + m_transferDetails(transfersContainer), + m_upperTransactionSizeLimit(m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize()) {} void WalletTransactionSender::stop() { m_isStoping = true; @@ -105,8 +104,6 @@ void WalletTransactionSender::validateTransfersAddresses(const std::vector WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { - if (!m_isInitialized) - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); using namespace cryptonote; @@ -116,27 +113,11 @@ std::shared_ptr WalletTransactionSender::makeSendRequest(Transact std::shared_ptr context = std::make_shared(); - context->foundMoney = m_transferDetails.selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); + context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); - TransferId firstTransferId = m_transactionsCache.insertTransfers(transfers); - - TransactionInfo transaction; - transaction.firstTransferId = firstTransferId; - transaction.transferCount = transfers.size(); - transaction.totalAmount = neededMoney; - transaction.fee = fee; - transaction.isCoinbase = false; - transaction.timestamp = 0; - transaction.extra = extra; - transaction.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; - - TransactionId txId = m_transactionsCache.insertTransaction(std::move(transaction)); - transactionId = txId; - m_sendingTxsStates.sending(txId); - - context->transactionId = txId; - context->unlockTimestamp = unlockTimestamp; + transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, extra, transfers, unlockTimestamp); + context->transactionId = transactionId; context->mixIn = mixIn; if(context->mixIn) { @@ -151,10 +132,8 @@ std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest uint64_t outsCount = context->mixIn + 1;// add one to make possible (if need) to skip real output key std::vector amounts; - for (auto idx: context->selectedTransfers) { - const TransferDetails& td = m_transferDetails.getTransferDetails(idx); - throwIf(td.tx.vout.size() <= td.internalOutputIndex, cryptonote::error::INTERNAL_WALLET_ERROR); - amounts.push_back(td.amount()); + for (const auto& td : context->selectedTransfers) { + amounts.push_back(td.amount); } return std::make_shared(amounts, outsCount, context, std::bind(&WalletTransactionSender::sendTransactionRandomOutsByAmount, @@ -163,20 +142,21 @@ std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, boost::optional >& nextRequest, std::error_code ec) { + if (m_isStoping) { - events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); - return; + ec = make_error_code(cryptonote::error::TX_CANCELLED); } if (ec) { - events.push_back(std::make_shared(context->transactionId, ec)); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec)); return; } - auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), [&] (cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); + auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), + [&] (cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); if (scanty_it != context->outs.end()) { - events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::MIXIN_COUNT_TOO_BIG))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::MIXIN_COUNT_TOO_BIG))); return; } @@ -187,7 +167,7 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque >& events) { if (m_isStoping) { - events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); return std::shared_ptr(); } @@ -199,43 +179,41 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); cryptonote::tx_destination_entry changeDts = AUTO_VAL_INIT(changeDts); - createChangeDestinations(m_keys.m_account_address, transaction.totalAmount, context->foundMoney, changeDts); + uint64_t totalAmount = -transaction.totalAmount; + createChangeDestinations(m_keys.m_account_address, totalAmount, context->foundMoney, changeDts); std::vector splittedDests; splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); cryptonote::Transaction tx; - constructTx(m_keys, sources, splittedDests, transaction.extra, context->unlockTimestamp, m_upperTransactionSizeLimit, tx); + constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx); fillTransactionHash(tx, transaction.hash); - m_unconfirmedTransactions.add(tx, context->transactionId, changeDts.amount); - notifyBalanceChanged(events); + m_transactionsCache.updateTransaction(context->transactionId, tx, totalAmount, context->selectedTransfers); - return std::make_shared(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context->transactionId, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + notifyBalanceChanged(events); + + return std::make_shared(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } catch(std::system_error& ec) { - events.push_back(std::make_shared(context->transactionId, ec.code())); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); } catch(std::exception&) { - events.push_back(std::make_shared(context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); } return std::shared_ptr(); } -void WalletTransactionSender::relayTransactionCallback(TransactionId txId, std::deque >& events, +void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque >& events, boost::optional >& nextRequest, std::error_code ec) { - if (m_isStoping) return; - - if (ec) { - m_sendingTxsStates.error(txId); - } else { - m_sendingTxsStates.sent(txId); + if (m_isStoping) { + return; } - events.push_back(std::make_shared(txId, ec)); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec)); } @@ -277,14 +255,18 @@ void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, siz } -void WalletTransactionSender::prepareInputs(const std::list& selectedTransfers, std::vector& outs, - std::vector& sources, uint64_t mixIn) { +void WalletTransactionSender::prepareInputs( + const std::list& selectedTransfers, + std::vector& outs, + std::vector& sources, uint64_t mixIn) { + size_t i = 0; - for (size_t idx: selectedTransfers) { + + for (const auto& td: selectedTransfers) { sources.resize(sources.size()+1); cryptonote::tx_source_entry& src = sources.back(); - TransferDetails& td = m_transferDetails.getTransferDetails(idx); - src.amount = td.amount(); + + src.amount = td.amount; //paste mixin transaction if(outs.size()) { @@ -306,24 +288,87 @@ void WalletTransactionSender::prepareInputs(const std::list& selectedTra cryptonote::tx_source_entry::output_entry real_oe; real_oe.first = td.globalOutputIndex; - real_oe.second = boost::get(td.tx.vout[td.internalOutputIndex].target).key; + real_oe.second = reinterpret_cast(td.outputKey); auto interted_it = src.outputs.insert(it_to_insert, real_oe); - src.real_out_tx_key = get_tx_pub_key_from_extra(td.tx); + src.real_out_tx_key = reinterpret_cast(td.transactionPublicKey); src.real_output = interted_it - src.outputs.begin(); - src.real_output_in_tx_index = td.internalOutputIndex; + src.real_output_in_tx_index = td.outputInTransaction; ++i; } } void WalletTransactionSender::notifyBalanceChanged(std::deque >& events) { - uint64_t actualBalance = m_transferDetails.countActualBalance(); - uint64_t pendingBalance = m_unconfirmedTransactions.countPendingBalance(); - pendingBalance += m_transferDetails.countPendingBalance(); + uint64_t unconfirmedOutsAmount = m_transactionsCache.unconfrimedOutsAmount(); + uint64_t change = unconfirmedOutsAmount - m_transactionsCache.unconfirmedTransactionsAmount(); + + uint64_t actualBalance = m_transferDetails.balance(ITransfersContainer::IncludeKeyUnlocked) - unconfirmedOutsAmount; + uint64_t pendingBalance = m_transferDetails.balance(ITransfersContainer::IncludeKeyNotUnlocked) + change; events.push_back(std::make_shared(actualBalance)); events.push_back(std::make_shared(pendingBalance)); } +namespace { + +template +T popRandomValue(URNG& randomGenerator, std::vector& vec) { + CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty"); + + std::uniform_int_distribution distribution(0, vec.size() - 1); + size_t idx = distribution(randomGenerator); + + T res = vec[idx]; + if (idx + 1 != vec.size()) { + vec[idx] = vec.back(); + } + vec.resize(vec.size() - 1); + + return res; +} + +} + + +uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers) { + + std::vector unusedTransfers; + std::vector unusedDust; + + std::vector outputs; + m_transferDetails.getOutputs(outputs, ITransfersContainer::IncludeKeyUnlocked); + + for (size_t i = 0; i < outputs.size(); ++i) { + const auto& out = outputs[i]; + if (!m_transactionsCache.isUsed(out)) { + if (dust < out.amount) + unusedTransfers.push_back(i); + else + unusedDust.push_back(i); + } + } + + std::default_random_engine randomGenerator(crypto::rand()); + bool selectOneDust = addDust && !unusedDust.empty(); + uint64_t foundMoney = 0; + + while (foundMoney < neededMoney && (!unusedTransfers.empty() || !unusedDust.empty())) { + size_t idx; + if (selectOneDust) { + idx = popRandomValue(randomGenerator, unusedDust); + selectOneDust = false; + } else { + idx = !unusedTransfers.empty() ? popRandomValue(randomGenerator, unusedTransfers) : popRandomValue(randomGenerator, unusedDust); + } + + selectedTransfers.push_back(outputs[idx]); + foundMoney += outputs[idx].amount; + } + + return foundMoney; + +} + + } /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h index e40626626a..187ef65b22 100644 --- a/src/wallet/WalletTransactionSender.h +++ b/src/wallet/WalletTransactionSender.h @@ -23,19 +23,19 @@ #include "INode.h" #include "WalletSendTransactionContext.h" #include "WalletUserTransactionsCache.h" -#include "WalletTransferDetails.h" #include "WalletUnconfirmedTransactions.h" #include "WalletRequest.h" +#include "ITransfersContainer.h" + namespace CryptoNote { class WalletTransactionSender { public: - WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, - WalletTxSendingState& sendingTxsStates, WalletTransferDetails& transferDetails, WalletUnconfirmedTransactions& unconfirmedTransactions); + WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, cryptonote::account_keys keys, ITransfersContainer& transfersContainer); - void init(cryptonote::account_keys keys); + void init(cryptonote::account_keys keys, ITransfersContainer& transfersContainer); void stop(); std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, @@ -44,7 +44,7 @@ class WalletTransactionSender private: std::shared_ptr makeGetRandomOutsRequest(std::shared_ptr context); std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque >& events); - void prepareInputs(const std::list& selectedTransfers, std::vector& outs, + void prepareInputs(const std::list& selectedTransfers, std::vector& outs, std::vector& sources, uint64_t mixIn); void splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts, const TxDustPolicy& dustPolicy, std::vector& splittedDests); @@ -52,22 +52,22 @@ class WalletTransactionSender std::vector& splitted_dsts, uint64_t& dust); void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, boost::optional >& nextRequest, std::error_code ec); - void relayTransactionCallback(TransactionId txId, std::deque >& events, + void relayTransactionCallback(std::shared_ptr context, std::deque >& events, boost::optional >& nextRequest, std::error_code ec); void notifyBalanceChanged(std::deque >& events); + void validateTransfersAddresses(const std::vector& transfers); bool validateDestinationAddress(const std::string& address); + uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); + const cryptonote::Currency& m_currency; cryptonote::account_keys m_keys; WalletUserTransactionsCache& m_transactionsCache; - WalletTxSendingState& m_sendingTxsStates; - WalletTransferDetails& m_transferDetails; - WalletUnconfirmedTransactions& m_unconfirmedTransactions; uint64_t m_upperTransactionSizeLimit; - bool m_isInitialized; bool m_isStoping; + ITransfersContainer& m_transferDetails; }; } /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransferDetails.cpp b/src/wallet/WalletTransferDetails.cpp deleted file mode 100644 index 0d3ea404a4..0000000000 --- a/src/wallet/WalletTransferDetails.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "WalletTransferDetails.h" - -#include -#include -#include - -#include - -#include "WalletErrors.h" - -#define DEFAULT_TX_SPENDABLE_AGE 10 - -namespace { - -template -T popRandomValue(URNG& randomGenerator, std::vector& vec) { - CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty"); - - std::uniform_int_distribution distribution(0, vec.size() - 1); - size_t idx = distribution(randomGenerator); - - T res = vec[idx]; - if (idx + 1 != vec.size()) { - vec[idx] = vec.back(); - } - vec.resize(vec.size() - 1); - - return res; -} - -} - -namespace CryptoNote -{ - -WalletTransferDetails::WalletTransferDetails(const cryptonote::Currency& currency, const std::vector& blockchain) : - m_currency(currency), m_blockchain(blockchain) { -} - -WalletTransferDetails::~WalletTransferDetails() { -} - -TransferDetails& WalletTransferDetails::getTransferDetails(size_t idx) { - return m_transfers.at(idx); -} - -void WalletTransferDetails::addTransferDetails(const TransferDetails& details) { - m_transfers.push_back(details); - size_t idx = m_transfers.size() - 1; - - m_keyImages.insert(std::make_pair(details.keyImage, idx)); -} - -bool WalletTransferDetails::getTransferDetailsIdxByKeyImage(const crypto::key_image& image, size_t& idx) { - auto it = m_keyImages.find(image); - if (it == m_keyImages.end()) - return false; - - idx = it->second; - return true; -} - -bool WalletTransferDetails::isTxSpendtimeUnlocked(uint64_t unlockTime) const { - if (unlockTime < m_currency.maxBlockHeight()) { - // interpret as block index - return m_blockchain.size()-1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlockTime; - } else { - // interpret as time - uint64_t current_time = static_cast(time(NULL)); - return current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlockTime; - } - return false; -} - -bool WalletTransferDetails::isTransferUnlocked(const TransferDetails& td) const -{ - if(!isTxSpendtimeUnlocked(td.tx.unlockTime)) - return false; - - if(td.blockHeight + DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size()) - return false; - - return true; -} - -uint64_t WalletTransferDetails::countActualBalance() const -{ - uint64_t amount = 0; - for (auto& transfer: m_transfers) { - if(!transfer.spent && isTransferUnlocked(transfer)) - amount += transfer.amount(); - } - - return amount; -} - -uint64_t WalletTransferDetails::countPendingBalance() const -{ - uint64_t amount = 0; - for (auto& td: m_transfers) { - if (!td.spent) - amount += td.amount(); - } - - return amount; -} - -uint64_t WalletTransferDetails::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers) { - std::vector unusedTransfers; - std::vector unusedDust; - - for (size_t i = 0; i < m_transfers.size(); ++i) { - const TransferDetails& td = m_transfers[i]; - if (!td.spent && isTransferUnlocked(td)) { - if (dust < td.amount()) - unusedTransfers.push_back(i); - else - unusedDust.push_back(i); - } - } - - std::default_random_engine randomGenerator(crypto::rand()); - bool selectOneDust = addDust && !unusedDust.empty(); - uint64_t foundMoney = 0; - while (foundMoney < neededMoney && (!unusedTransfers.empty() || !unusedDust.empty())) { - size_t idx; - if (selectOneDust) { - idx = popRandomValue(randomGenerator, unusedDust); - selectOneDust = false; - } - else - { - idx = !unusedTransfers.empty() ? popRandomValue(randomGenerator, unusedTransfers) : popRandomValue(randomGenerator, unusedDust); - } - - selectedTransfers.push_back(idx); - foundMoney += m_transfers[idx].amount(); - } - - return foundMoney; -} - -void WalletTransferDetails::detachTransferDetails(size_t height) { - auto it = std::find_if(m_transfers.begin(), m_transfers.end(), [&](const TransferDetails& td){return td.blockHeight >= height;}); - size_t start = it - m_transfers.begin(); - - for(size_t i = start; i!= m_transfers.size();i++) { - auto ki = m_keyImages.find(m_transfers[i].keyImage); - if(ki == m_keyImages.end()) throw std::system_error(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); - - m_keyImages.erase(ki); - } - m_transfers.erase(it, m_transfers.end()); -} - -} /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransferDetails.h b/src/wallet/WalletTransferDetails.h deleted file mode 100644 index 1f9ce4e956..0000000000 --- a/src/wallet/WalletTransferDetails.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/Currency.h" -#include "IWallet.h" - -namespace CryptoNote { - -struct TransferDetails -{ - uint64_t blockHeight; - cryptonote::Transaction tx; - size_t internalOutputIndex; - uint64_t globalOutputIndex; - bool spent; - crypto::key_image keyImage; - - uint64_t amount() const - { - return tx.vout[internalOutputIndex].amount; - } -}; - -class WalletTransferDetails -{ -public: - WalletTransferDetails(const cryptonote::Currency& currency, const std::vector& blockchain); - ~WalletTransferDetails(); - - TransferDetails& getTransferDetails(size_t idx); - void addTransferDetails(const TransferDetails& details); - bool getTransferDetailsIdxByKeyImage(const crypto::key_image& image, size_t& idx); - - uint64_t countActualBalance() const; - uint64_t countPendingBalance() const; - bool isTransferUnlocked(const TransferDetails& td) const; - - uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); - - void detachTransferDetails(size_t height); - - template - void save(Archive& ar, bool saveCache) const; - - template - void load(Archive& ar); - -private: - bool isTxSpendtimeUnlocked(uint64_t unlock_time) const; - - typedef std::vector TransferContainer; - TransferContainer m_transfers; - - typedef std::unordered_map KeyImagesContainer; - KeyImagesContainer m_keyImages; - - const cryptonote::Currency& m_currency; - const std::vector& m_blockchain; -}; - -template -void WalletTransferDetails::save(Archive& ar, bool saveCache) const -{ - const TransferContainer& transfers = saveCache ? m_transfers : TransferContainer(); - const KeyImagesContainer& keyImages = saveCache ? m_keyImages : KeyImagesContainer(); - - ar << transfers; - ar << keyImages; -} - -template -void WalletTransferDetails::load(Archive& ar) -{ - ar >> m_transfers; - ar >> m_keyImages; -} - -} //namespace CryptoNote diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index b0ed23dfac..f322ffee0e 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -16,43 +16,112 @@ // along with Bytecoin. If not, see . #include "WalletUnconfirmedTransactions.h" +#include "WalletSerialization.h" + #include "cryptonote_core/cryptonote_format_utils.h" +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" namespace CryptoNote { -bool WalletUnconfirmedTransactions::findTransactionId(const crypto::hash& hash, TransactionId& id) { - auto it = m_unconfirmedTxs.find(hash); +inline TransactionOutputId getOutputId(const TransactionOutputInformation& out) { + return std::make_pair(out.transactionPublicKey, out.outputInTransaction); +} + +void WalletUnconfirmedTransactions::serialize(cryptonote::ISerializer& s, const std::string& name) { + s.beginObject(name); + s(m_unconfirmedTxs, "transactions"); + s.endObject(); - if(it == m_unconfirmedTxs.end()) + if (s.type() == cryptonote::ISerializer::INPUT) { + collectUsedOutputs(); + } +} + +bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& hash, TransactionId& id) { + auto it = m_unconfirmedTxs.find(hash); + if (it == m_unconfirmedTxs.end()) { return false; + } id = it->second.transactionId; - return true; } -void WalletUnconfirmedTransactions::erase(const crypto::hash& hash) { - m_unconfirmedTxs.erase(hash); +void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { + auto it = m_unconfirmedTxs.find(hash); + if (it == m_unconfirmedTxs.end()) { + return; + } + + for (const auto& o : it->second.usedOutputs) { + m_usedOutputs.erase(o); + } + m_unconfirmedTxs.erase(it); } -void WalletUnconfirmedTransactions::add(const cryptonote::Transaction& tx, - TransactionId transactionId, uint64_t change_amount) { - UnconfirmedTransferDetails& utd = m_unconfirmedTxs[cryptonote::get_transaction_hash(tx)]; +void WalletUnconfirmedTransactions::add(const cryptonote::Transaction& tx, TransactionId transactionId, + uint64_t amount, const std::list& usedOutputs) { + + auto cryptoHash = cryptonote::get_transaction_hash(tx); + TransactionHash hash = reinterpret_cast(cryptoHash); + + UnconfirmedTransferDetails& utd = m_unconfirmedTxs[hash]; - utd.change = change_amount; - utd.sentTime = time(NULL); + utd.amount = amount; + utd.sentTime = time(nullptr); utd.tx = tx; utd.transactionId = transactionId; + + uint64_t outsAmount = 0; + // process used outputs + utd.usedOutputs.reserve(usedOutputs.size()); + for (const auto& out : usedOutputs) { + auto id = getOutputId(out); + utd.usedOutputs.push_back(id); + m_usedOutputs.insert(id); + outsAmount += out.amount; + } + + utd.outsAmount = outsAmount; } -uint64_t WalletUnconfirmedTransactions::countPendingBalance() const -{ +void WalletUnconfirmedTransactions::updateTransactionId(const TransactionHash& hash, TransactionId id) { + auto it = m_unconfirmedTxs.find(hash); + if (it != m_unconfirmedTxs.end()) { + it->second.transactionId = id; + } +} + +uint64_t WalletUnconfirmedTransactions::countUnconfirmedOutsAmount() const { + uint64_t amount = 0; + + for (auto& utx: m_unconfirmedTxs) + amount+= utx.second.outsAmount; + + return amount; +} + +uint64_t WalletUnconfirmedTransactions::countUnconfirmedTransactionsAmount() const { uint64_t amount = 0; for (auto& utx: m_unconfirmedTxs) - amount+= utx.second.change; + amount+= utx.second.amount; return amount; } +bool WalletUnconfirmedTransactions::isUsed(const TransactionOutputInformation& out) const { + return m_usedOutputs.find(getOutputId(out)) != m_usedOutputs.end(); +} + +void WalletUnconfirmedTransactions::collectUsedOutputs() { + UsedOutputsContainer used; + for (const auto& kv : m_unconfirmedTxs) { + used.insert(kv.second.usedOutputs.begin(), kv.second.usedOutputs.end()); + } + m_usedOutputs = std::move(used); +} + + } /* namespace CryptoNote */ diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index 67275dece0..f21f86b28c 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -17,55 +17,63 @@ #pragma once -#include +#include "IWallet.h" +#include "ITransfersContainer.h" +#include +#include #include +#include -#include "IWallet.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_basic.h" +namespace cryptonote { +class ISerializer; +} + namespace CryptoNote { -struct UnconfirmedTransferDetails -{ +typedef std::pair TransactionOutputId; + +struct UnconfirmedTransferDetails { + + UnconfirmedTransferDetails() : + amount(0), sentTime(0), transactionId(INVALID_TRANSACTION_ID) {} + cryptonote::Transaction tx; - uint64_t change; + uint64_t amount; + uint64_t outsAmount; time_t sentTime; TransactionId transactionId; + std::vector usedOutputs; }; class WalletUnconfirmedTransactions { public: - template - void save(Archive& ar, bool saveCache) const; - template - void load(Archive& ar); + void serialize(cryptonote::ISerializer& s, const std::string& name); - bool findTransactionId(const crypto::hash& hash, TransactionId& id); - void erase(const crypto::hash& hash); - void add(const cryptonote::Transaction& tx, TransactionId transactionId, uint64_t change_amount); + bool findTransactionId(const TransactionHash& hash, TransactionId& id); + void erase(const TransactionHash& hash); + void add(const cryptonote::Transaction& tx, TransactionId transactionId, + uint64_t amount, const std::list& usedOutputs); + void updateTransactionId(const TransactionHash& hash, TransactionId id); - uint64_t countPendingBalance() const; + uint64_t countUnconfirmedOutsAmount() const; + uint64_t countUnconfirmedTransactionsAmount() const; + bool isUsed(const TransactionOutputInformation& out) const; private: - typedef std::unordered_map UnconfirmedTxsContainer; - UnconfirmedTxsContainer m_unconfirmedTxs; -}; -template -void WalletUnconfirmedTransactions::save(Archive& ar, bool saveCache) const -{ - const UnconfirmedTxsContainer& unconfirmedTxs = saveCache ? m_unconfirmedTxs : UnconfirmedTxsContainer(); - ar << unconfirmedTxs; -} + void collectUsedOutputs(); -template -void WalletUnconfirmedTransactions::load(Archive& ar) -{ - ar >> m_unconfirmedTxs; -} + typedef std::unordered_map> UnconfirmedTxsContainer; + typedef std::set UsedOutputsContainer; + + UnconfirmedTxsContainer m_unconfirmedTxs; + UsedOutputsContainer m_usedOutputs; +}; } // namespace CryptoNote diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index cb46c0c64a..b958c4f045 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -15,28 +15,164 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +// epee +#include "misc_log_ex.h" + +#include "WalletErrors.h" #include "WalletUserTransactionsCache.h" +#include "WalletSerialization.h" +#include "WalletUtils.h" +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" #include -namespace { -bool hashesEqual(const CryptoNote::TransactionHash& h1, const crypto::hash& h2) { - return !memcmp(static_cast(h1.data()), static_cast(&h2), h1.size()); +namespace CryptoNote { + + +void WalletUserTransactionsCache::serialize(cryptonote::ISerializer& s, const std::string& name) { + s.beginObject(name); + + if (s.type() == cryptonote::ISerializer::INPUT) { + s(m_transactions, "transactions"); + s(m_transfers, "transfers"); + s(m_unconfirmedTransactions, "unconfirmed"); + updateUnconfirmedTransactions(); + } else { + UserTransactions txsToSave; + UserTransfers transfersToSave; + + getGoodItems(txsToSave, transfersToSave); + s(txsToSave, "transactions"); + s(transfersToSave, "transfers"); + s(m_unconfirmedTransactions, "unconfirmed"); + } + + s.endObject(); } + +uint64_t WalletUserTransactionsCache::unconfirmedTransactionsAmount() const { + return m_unconfirmedTransactions.countUnconfirmedTransactionsAmount(); } -namespace CryptoNote { +uint64_t WalletUserTransactionsCache::unconfrimedOutsAmount() const { + return m_unconfirmedTransactions.countUnconfirmedOutsAmount(); +} -size_t WalletUserTransactionsCache::getTransactionCount() const -{ +size_t WalletUserTransactionsCache::getTransactionCount() const { return m_transactions.size(); } -size_t WalletUserTransactionsCache::getTransferCount() const -{ +size_t WalletUserTransactionsCache::getTransferCount() const { return m_transfers.size(); } +TransactionId WalletUserTransactionsCache::addNewTransaction( + uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime) { + + TransactionInfo transaction; + + transaction.firstTransferId = insertTransfers(transfers); + transaction.transferCount = transfers.size(); + transaction.totalAmount = -static_cast(amount); + transaction.fee = fee; + transaction.sentTime = time(nullptr); + transaction.isCoinbase = false; + transaction.timestamp = 0; + transaction.extra = extra; + transaction.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + transaction.state = TransactionState::Sending; + transaction.unlockTime = unlockTime; + + return insertTransaction(std::move(transaction)); +} + +void WalletUserTransactionsCache::updateTransaction( + TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::list& usedOutputs) { + m_unconfirmedTransactions.add(tx, transactionId, amount, usedOutputs); +} + +void WalletUserTransactionsCache::updateTransactionSendingState(TransactionId transactionId, std::error_code ec) { + auto& txInfo = m_transactions.at(transactionId); + if (ec) { + txInfo.state = ec.value() == cryptonote::error::TX_CANCELLED ? TransactionState::Cancelled : TransactionState::Failed; + m_unconfirmedTransactions.erase(txInfo.hash); + } else { + txInfo.sentTime = time(nullptr); // update sending time + txInfo.state = TransactionState::Active; + } +} + +std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, + int64_t txBalance) { + std::shared_ptr event; + + TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; + + if (!m_unconfirmedTransactions.findTransactionId(txInfo.transactionHash, id)) { + id = findTransactionByHash(txInfo.transactionHash); + } else { + m_unconfirmedTransactions.erase(txInfo.transactionHash); + } + + bool isCoinbase = txInfo.totalAmountIn == 0; + + if (id == CryptoNote::INVALID_TRANSACTION_ID) { + TransactionInfo transaction; + transaction.firstTransferId = INVALID_TRANSFER_ID; + transaction.transferCount = 0; + transaction.totalAmount = txBalance; + transaction.fee = isCoinbase ? 0 : txInfo.totalAmountIn - txInfo.totalAmountOut; + transaction.sentTime = 0; + transaction.hash = txInfo.transactionHash; + transaction.blockHeight = txInfo.blockHeight; + transaction.isCoinbase = isCoinbase; + transaction.timestamp = txInfo.timestamp; + transaction.extra.assign(txInfo.extra.begin(), txInfo.extra.end()); + transaction.state = TransactionState::Active; + transaction.unlockTime = txInfo.unlockTime; + + id = insertTransaction(std::move(transaction)); + // notification event + event = std::make_shared(id); + } else { + TransactionInfo& tr = getTransaction(id); + tr.blockHeight = txInfo.blockHeight; + tr.timestamp = txInfo.timestamp; + tr.state = TransactionState::Active; + // notification event + event = std::make_shared(id); + } + + return event; +} + +std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const TransactionHash& transactionHash) { + TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; + if (m_unconfirmedTransactions.findTransactionId(transactionHash, id)) { + m_unconfirmedTransactions.erase(transactionHash); + LOG_ERROR("Unconfirmed transaction is deleted: id = " << id << ", hash = " << transactionHash); + assert(false); + } else { + id = findTransactionByHash(transactionHash); + } + + std::shared_ptr event; + if (id != CryptoNote::INVALID_TRANSACTION_ID) { + TransactionInfo& tr = getTransaction(id); + tr.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + tr.timestamp = 0; + tr.state = TransactionState::Deleted; + + event = std::make_shared(id); + } else { + LOG_ERROR("Transaction wasn't found: " << transactionHash); + assert(false); + } + + return event; +} + TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferId transferId) const { TransactionId id; @@ -77,12 +213,12 @@ bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& t } TransactionId WalletUserTransactionsCache::insertTransaction(TransactionInfo&& Transaction) { - m_transactions.emplace_back(Transaction); + m_transactions.emplace_back(std::move(Transaction)); return m_transactions.size() - 1; } -TransactionId WalletUserTransactionsCache::findTransactionByHash(const crypto::hash& hash) { - auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const TransactionInfo& tx) { return hashesEqual(tx.hash, hash); }); +TransactionId WalletUserTransactionsCache::findTransactionByHash(const TransactionHash& hash) { + auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const TransactionInfo& tx) { return tx.hash == hash; }); if (it == m_transactions.end()) return CryptoNote::INVALID_TRANSACTION_ID; @@ -90,50 +226,35 @@ TransactionId WalletUserTransactionsCache::findTransactionByHash(const crypto::h return std::distance(m_transactions.begin(), it); } -void WalletUserTransactionsCache::detachTransactions(uint64_t height) { - for (size_t id = 0; id < m_transactions.size(); ++id) { - TransactionInfo& tx = m_transactions[id]; - if (tx.blockHeight >= height) { - tx.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; - tx.timestamp = 0; - } - } +bool WalletUserTransactionsCache::isUsed(const TransactionOutputInformation& out) const { + return m_unconfirmedTransactions.isUsed(out); } TransactionInfo& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { return m_transactions.at(transactionId); } -void WalletUserTransactionsCache::getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) { +void WalletUserTransactionsCache::getGoodItems(UserTransactions& transactions, UserTransfers& transfers) { size_t offset = 0; for (size_t txId = 0; txId < m_transactions.size(); ++txId) { - WalletTxSendingState::State state = m_sendingTxsStates.state(txId); - bool isGood = state != WalletTxSendingState::ERRORED; + bool isGood = + m_transactions[txId].state != TransactionState::Cancelled && + m_transactions[txId].state != TransactionState::Failed; if (isGood) { - getGoodTransaction(txId, offset, saveDetailed, transactions, transfers); - } - else - { + getGoodTransaction(txId, offset, transactions, transfers); + } else { const TransactionInfo& t = m_transactions[txId]; - if (t.firstTransferId != INVALID_TRANSFER_ID) - offset += t.transferCount; + offset += t.firstTransferId != INVALID_TRANSFER_ID ? t.transferCount : 0; } } } -void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers) { +void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, UserTransactions& transactions, UserTransfers& transfers) { transactions.push_back(m_transactions[txId]); TransactionInfo& tx = transactions.back(); - if (!saveDetailed) { - tx.firstTransferId = INVALID_TRANSFER_ID; - tx.transferCount = 0; - - return; - } - if (tx.firstTransferId == INVALID_TRANSFER_ID) { return; } @@ -146,16 +267,6 @@ void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t std::copy(first, last, std::back_inserter(transfers)); } -void WalletUserTransactionsCache::getGoodTransfers(UserTransfers& transfers) { - for (size_t txId = 0; txId < m_transactions.size(); ++txId) { - WalletTxSendingState::State state = m_sendingTxsStates.state(txId); - - if (state != WalletTxSendingState::ERRORED) { - getTransfersByTx(txId, transfers); - } - } -} - void WalletUserTransactionsCache::getTransfersByTx(TransactionId id, UserTransfers& transfers) { const TransactionInfo& tx = m_transactions[id]; @@ -171,6 +282,14 @@ TransferId WalletUserTransactionsCache::insertTransfers(const std::vector - void save(Archive& ar, bool saveDetailed, bool saveCache); - - template - void load(Archive& ar); + void serialize(cryptonote::ISerializer& serializer, const std::string& name); + uint64_t unconfirmedTransactionsAmount() const; + uint64_t unconfrimedOutsAmount() const; size_t getTransactionCount() const; size_t getTransferCount() const; + TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime); + void updateTransaction(TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::list& usedOutputs); + void updateTransactionSendingState(TransactionId transactionId, std::error_code ec); + + std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); + std::shared_ptr onTransactionDeleted(const TransactionHash& transactionHash); + TransactionId findTransactionByTransferId(TransferId transferId) const; bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) const; @@ -44,47 +56,26 @@ class WalletUserTransactionsCache bool getTransfer(TransferId transferId, Transfer& transfer) const; Transfer& getTransfer(TransferId transferId); + bool isUsed(const TransactionOutputInformation& out) const; + +private: + + TransactionId findTransactionByHash(const TransactionHash& hash); TransactionId insertTransaction(TransactionInfo&& Transaction); TransferId insertTransfers(const std::vector& transfers); + void updateUnconfirmedTransactions(); - TransactionId findTransactionByHash(const crypto::hash& hash); - void detachTransactions(uint64_t height); - -private: typedef std::vector UserTransfers; typedef std::vector UserTransactions; - void getGoodItems(bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers); - void getGoodTransaction(TransactionId txId, size_t offset, bool saveDetailed, UserTransactions& transactions, UserTransfers& transfers); + void getGoodItems(UserTransactions& transactions, UserTransfers& transfers); + void getGoodTransaction(TransactionId txId, size_t offset, UserTransactions& transactions, UserTransfers& transfers); - void getGoodTransfers(UserTransfers& transfers); void getTransfersByTx(TransactionId id, UserTransfers& transfers); UserTransactions m_transactions; UserTransfers m_transfers; - - WalletTxSendingState& m_sendingTxsStates; + WalletUnconfirmedTransactions m_unconfirmedTransactions; }; -template -void WalletUserTransactionsCache::load(Archive& ar) { - ar >> m_transactions; - ar >> m_transfers; -} - -template -void WalletUserTransactionsCache::save(Archive& ar, bool saveDetailed, bool saveCache) { - UserTransactions txsToSave; - UserTransfers transfersToSave; - - if (saveCache) { - getGoodItems(saveDetailed, txsToSave, transfersToSave); - } else { - if (saveDetailed) getGoodTransfers(transfersToSave); - } - - ar << txsToSave; - ar << transfersToSave; -} - } //namespace CryptoNote diff --git a/src/wallet/WalletUtils.h b/src/wallet/WalletUtils.h index 3bf3dcc074..5705c9958a 100644 --- a/src/wallet/WalletUtils.h +++ b/src/wallet/WalletUtils.h @@ -18,6 +18,10 @@ #pragma once #include +#include +#include + +#include "IWallet.h" #include "WalletErrors.h" namespace CryptoNote { @@ -28,4 +32,17 @@ inline void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) throw std::system_error(make_error_code(ec)); } +inline std::ostream& operator <<(std::ostream& ostr, const TransactionHash& hash) { + std::ios_base::fmtflags flags = ostr.setf(std::ios_base::hex, std::ios_base::basefield); + char fill = ostr.fill('0'); + + for (auto b : hash) { + ostr << std::setw(2) << static_cast(b); + } + + ostr.fill(fill); + ostr.setf(flags); + return ostr; +} + } //namespace CryptoNote diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp deleted file mode 100644 index 7e9e00d61b..0000000000 --- a/src/wallet/wallet2.cpp +++ /dev/null @@ -1,910 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "wallet2.h" - -#include - -#include -#include -#include - -// epee -#include "include_base_utils.h" -#include "misc_language.h" -#include "profile_tools.h" - -#include "common/boost_serialization_helper.h" -#include "crypto/crypto.h" -#include "cryptonote_core/AccountKVSerialization.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_protocol/blobdatatype.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "serialization/binary_utils.h" - -using namespace cryptonote; -using namespace epee; - -namespace -{ -void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) -{ - keys_file = file_path; - wallet_file = file_path; - boost::system::error_code e; - if(string_tools::get_extension(keys_file) == "keys") - {//provided keys file name - wallet_file = string_tools::cut_off_extension(wallet_file); - }else - {//provided wallet file name - keys_file += ".keys"; - } -} -} //namespace - -namespace tools -{ -//---------------------------------------------------------------------------------------------------- -void wallet2::init(const std::string& daemon_address) -{ - m_daemon_address = daemon_address; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id) { - process_unconfirmed(tx); - - std::vector tx_extra_fields; - if(!parse_tx_extra(tx.extra, 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: " << get_transaction_hash(tx)); - } - - tx_extra_pub_key pub_key_field; - if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) - { - LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << get_transaction_hash(tx)); - if(0 != m_callback) - m_callback->on_skip_transaction(height, tx); - return false; - } - - TxItem txItem = { tx, time, height, bl_id, pub_key_field.pub_key, std::move(tx_extra_fields) }; - queue.push(std::unique_ptr(new TxItem(std::move(txItem)))); - return true; -} - -//---------------------------------------------------------------------------------------------------- -void wallet2::processCheckedTransaction(const TxItem& item) { - - const cryptonote::Transaction& tx = item.tx; - const std::vector& outs = item.outs; - const std::vector& tx_extra_fields = item.txExtraFields; - - uint64_t tx_money_got_in_outs = item.txMoneyGotInOuts; - uint64_t height = item.height; - - - if (!outs.empty() && tx_money_got_in_outs) - { - //good news - got money! take care about it - //usually we have only one transfer for user in transaction - cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); - req.txid = get_transaction_hash(tx); - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status); - THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size())); - - for (size_t o : outs) { - THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" + - std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size())); - - m_transfers.push_back(boost::value_initialized()); - transfer_details& td = m_transfers.back(); - td.m_block_height = height; - td.m_internal_output_index = o; - td.m_global_output_index = res.o_indexes[o]; - td.m_tx = tx; - td.m_spent = false; - cryptonote::KeyPair in_ephemeral; - cryptonote::generate_key_image_helper(m_account.get_keys(), item.txPubKey, o, in_ephemeral, td.m_key_image); - THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(tx.vout[o].target).key, - error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - - auto insertResult = m_key_images.insert(std::make_pair(td.m_key_image, m_transfers.size() - 1)); - THROW_WALLET_EXCEPTION_IF(!insertResult.second, error::wallet_internal_error, "Key image already exists"); - - LOG_PRINT_L0("Received money: " << m_currency.formatAmount(td.amount()) << ", with tx: " << get_transaction_hash(tx)); - if (0 != m_callback) { - m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); - } - } - } - - uint64_t tx_money_spent_in_ins = 0; - // check all outputs for spending (compare key images) - BOOST_FOREACH(auto& in, tx.vin) - { - if (in.type() == typeid(cryptonote::TransactionInputToKey)) { - auto it = m_key_images.find(boost::get(in).keyImage); - if (it != m_key_images.end()) { - LOG_PRINT_L0("Spent money: " << m_currency.formatAmount(boost::get(in).amount) << - ", with tx: " << get_transaction_hash(tx)); - tx_money_spent_in_ins += boost::get(in).amount; - transfer_details& td = m_transfers[it->second]; - td.m_spent = true; - if (0 != m_callback) - m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx); - } - } - } - - crypto::hash transactionHash = get_transaction_hash(tx); - - bool ownTransfer = false; - for (Transfer& transfer : transfers) { - if (transfer.transactionHash == transactionHash) { - transfer.blockIndex = height; - ownTransfer = true; - } - } - - if (!ownTransfer) { - crypto::hash paymentId = null_hash; - tx_extra_nonce extraNonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extraNonce)) { - get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); - } - - if (tx_money_spent_in_ins < tx_money_got_in_outs) { - if (paymentId != null_hash) { - payment_details payment; - payment.m_tx_hash = transactionHash; - payment.m_amount = tx_money_got_in_outs - tx_money_spent_in_ins; - payment.m_block_height = height; - payment.m_unlock_time = tx.unlockTime; - m_payments.emplace(paymentId, payment); - LOG_PRINT_L2("Payment found: " << paymentId << " / " << payment.m_tx_hash << " / " << payment.m_amount); - } - - Transfer transfer; - transfer.time = item.time; - transfer.output = false; - transfer.transactionHash = transactionHash; - transfer.amount = tx_money_got_in_outs - tx_money_spent_in_ins; - transfer.fee = 0; - transfer.paymentId = paymentId; - transfer.hasAddress = false; - transfer.blockIndex = height; - transfer.unlockTime = tx.unlockTime; - transfers.push_back(transfer); - } else if (tx_money_got_in_outs < tx_money_spent_in_ins) { - Transfer transfer; - transfer.time = item.time; - transfer.output = true; - transfer.transactionHash = transactionHash; - transfer.amount = tx_money_spent_in_ins - tx_money_got_in_outs; - transfer.fee = 0; - transfer.paymentId = paymentId; - transfer.hasAddress = false; - transfer.blockIndex = height; - transfer.unlockTime = tx.unlockTime; - transfers.push_back(transfer); - } - } -} - -//---------------------------------------------------------------------------------------------------- -void wallet2::process_unconfirmed(const cryptonote::Transaction& tx) -{ - auto unconf_it = m_unconfirmed_txs.find(get_transaction_hash(tx)); - if(unconf_it != m_unconfirmed_txs.end()) - m_unconfirmed_txs.erase(unconf_it); -} - -//---------------------------------------------------------------------------------------------------- -bool wallet2::addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t current_index) -{ - if (current_index < m_blockchain.size()) { - 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); - } else { - LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id)); - return false; - } - } - - THROW_WALLET_EXCEPTION_IF(current_index != m_blockchain.size(), error::wallet_internal_error, - "current_index=" + std::to_string(current_index) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size())); - - m_blockchain.push_back(bl_id); - - if (0 != m_callback) { - m_callback->on_new_block(current_index); - } - - return true; -} - -size_t wallet2::processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height) -{ - size_t processedTransactions = 0; - - if (!bche.block.empty()) - { - cryptonote::Block b; - bool r = cryptonote::parse_and_validate_block_from_blob(bche.block, b); - THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bche.block); - - //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup - if (b.timestamp + 60 * 60 * 24 > m_account.get_createtime()) - { - TIME_MEASURE_START(miner_tx_handle_time); - if(processNewTransaction(queue, b.minerTx, height, b.timestamp, bl_id)) - ++processedTransactions; - TIME_MEASURE_FINISH(miner_tx_handle_time); - - TIME_MEASURE_START(txs_handle_time); - BOOST_FOREACH(auto& txblob, bche.txs) - { - cryptonote::Transaction tx; - bool r = parse_and_validate_tx_from_blob(txblob, tx); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - if(processNewTransaction(queue, tx, height, b.timestamp, bl_id)) - ++processedTransactions; - } - 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"); - } else - { - LOG_PRINT_L2("Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime()); - } - } - - return processedTransactions; -} - - -//---------------------------------------------------------------------------------------------------- -void wallet2::get_short_chain_history(std::list& ids) const -{ - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = m_blockchain.size(); - if(!sz) - return; - size_t current_back_offset = 1; - bool genesis_included = false; - while(current_back_offset < sz) - { - ids.push_back(m_blockchain[sz-current_back_offset]); - if(sz-current_back_offset == 0) - genesis_included = true; - if(i < 10) - { - ++current_back_offset; - }else - { - current_back_offset += current_multiplier *= 2; - } - ++i; - } - if(!genesis_included) - ids.push_back(m_blockchain[0]); -} - -//---------------------------------------------------------------------------------------------------- -size_t wallet2::updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, std::unordered_set& newBlocks) { - size_t blocks_added = 0; - size_t current_index = res.start_height; - - // update local blockchain - for (const auto& item : res.items) { - if (addNewBlockchainEntry(item.block_id, res.start_height, current_index)) { - if (!item.block.empty()) { - newBlocks.insert(item.block_id); - } - ++blocks_added; - } - ++current_index; - } - - return blocks_added; -} - -//---------------------------------------------------------------------------------------------------- -void wallet2::processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, const std::unordered_set& newBlocks) -{ - size_t checkingThreads = std::thread::hardware_concurrency(); - - if (checkingThreads == 0) - checkingThreads = 4; - - std::vector> futures; - - TxQueue incomingQueue(checkingThreads * 2); - TxQueue checkedQueue(checkingThreads * 2); - - std::atomic inputTx(0); - - futures.push_back(std::async(std::launch::async, [&] { - try { - size_t current_index = res.start_height; - for (const auto& item : res.items) { - if (newBlocks.count(item.block_id)) { - inputTx += processNewBlockchainEntry(incomingQueue, item, item.block_id, current_index); - } - ++current_index; - } - incomingQueue.close(); - } catch (...) { - LOG_ERROR("Exception in pushing thread!"); - incomingQueue.close(); - throw; - } - })); - - GroupClose queueClose(checkedQueue, checkingThreads); - - for (size_t i = 0; i < checkingThreads; ++i) { - futures.push_back(std::async(std::launch::async, [&] { - TxQueueItem item; - while (incomingQueue.pop(item)) { - lookup_acc_outs(m_account.get_keys(), item->tx, item->txPubKey, item->outs, item->txMoneyGotInOuts); - checkedQueue.push(std::move(item)); - } - queueClose.close(); - })); - } - - size_t txCount = 0; - - try { - TxQueueItem item; - while (checkedQueue.pop(item)) { - processCheckedTransaction(*item); - ++txCount; - } - } catch (...) { - checkedQueue.close(); - for (auto& f : futures) { - f.wait(); - } - throw; - } - - for (auto& f : futures) { - f.get(); - } - - if (checkedQueue.size() > 0 || incomingQueue.size() > 0) { - LOG_ERROR("ERROR! Queues not empty. Incoming: " << incomingQueue.size() << " Checked: " << checkedQueue.size()); - } - - if (inputTx != txCount) { - LOG_ERROR("Failed to process some transactions. Incoming: " << inputTx << " Processed: " << txCount); - } -} - -//---------------------------------------------------------------------------------------------------- -cryptonote::COMMAND_RPC_QUERY_BLOCKS::response wallet2::queryBlocks(epee::net_utils::http::http_simple_client& client) -{ - cryptonote::COMMAND_RPC_QUERY_BLOCKS::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_QUERY_BLOCKS::response res = AUTO_VAL_INIT(res); - - get_short_chain_history(req.block_ids); - req.timestamp = m_account.get_createtime() - 60 * 60 * 24; // get full blocks starting from wallet creation time minus 1 day - - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/queryblocks.bin", req, res, client, WALLET_RCP_CONNECTION_TIMEOUT); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "queryblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "queryblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); - THROW_WALLET_EXCEPTION_IF(m_blockchain.size() <= res.start_height, error::wallet_internal_error, - "wrong daemon response: m_start_height=" + std::to_string(res.start_height) + - " not less than local blockchain size=" + std::to_string(m_blockchain.size())); - - return res; -} - -//---------------------------------------------------------------------------------------------------- -void wallet2::refresh() -{ - size_t blocks_fetched = 0; - refresh(blocks_fetched); -} -//---------------------------------------------------------------------------------------------------- -void wallet2::refresh(size_t & blocks_fetched) -{ - bool received_money = false; - refresh(blocks_fetched, received_money); -} -//---------------------------------------------------------------------------------------------------- -void wallet2::refresh(size_t & blocks_fetched, bool& received_money) -{ - received_money = false; - blocks_fetched = 0; - size_t try_count = 0; - crypto::hash last_tx_hash_id = m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash; - - epee::net_utils::http::http_simple_client queryClient; - - auto r = connectClient(queryClient); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "refresh"); - - auto startTime = std::chrono::high_resolution_clock::now(); - - cryptonote::COMMAND_RPC_QUERY_BLOCKS::response res; - std::unordered_set newBlocks; - - size_t lastHeight = m_blockchain.size(); - size_t added_blocks = 0; - - while (m_run.load(std::memory_order_relaxed)) { - try { - std::future processingTask; - if (!newBlocks.empty()) { - processingTask = std::async(std::launch::async, [&res, &newBlocks, this] { processTransactions(res, newBlocks); }); - } - - cryptonote::COMMAND_RPC_QUERY_BLOCKS::response tempRes = queryBlocks(queryClient); - if (!newBlocks.empty()) { - processingTask.get(); - lastHeight = m_blockchain.size(); - newBlocks.clear(); - } - - added_blocks = updateBlockchain(tempRes, newBlocks); - if (added_blocks == 0) { - break; - } - - res = std::move(tempRes); - blocks_fetched += added_blocks; - } catch (const std::exception&) { - newBlocks.clear(); - blocks_fetched -= detach_blockchain(lastHeight); - if (try_count < 3) { - LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); - ++try_count; - } else { - LOG_ERROR("pull_blocks failed, try_count=" << try_count); - throw; - } - } - } - - auto duration = std::chrono::high_resolution_clock::now() - startTime; - - if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash)) - received_money = true; - - LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << - ", balance: " << m_currency.formatAmount(balance()) << - ", unlocked: " << m_currency.formatAmount(unlocked_balance())); - LOG_PRINT_L1("Time: " << std::chrono::duration(duration).count() << "s"); -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::refresh(size_t & blocks_fetched, bool& received_money, bool& ok) -{ - try - { - refresh(blocks_fetched, received_money); - ok = true; - } - catch (...) - { - ok = false; - } - return ok; -} -//---------------------------------------------------------------------------------------------------- -size_t wallet2::detach_blockchain(uint64_t height) -{ - LOG_PRINT_L0("Detaching blockchain on height " << height); - size_t transfers_detached = 0; - - // do not rely on ordering by height in transfers - for (auto it = m_transfers.begin(); it != m_transfers.end();) { - if (it->m_block_height >= height) { - auto it_ki = m_key_images.find(it->m_key_image); - THROW_WALLET_EXCEPTION_IF(it_ki == m_key_images.end(), error::wallet_internal_error, "key image not found"); - m_key_images.erase(it_ki); - it = m_transfers.erase(it); - ++transfers_detached; - } else { - ++it; - } - } - - size_t blocks_detached = m_blockchain.end() - (m_blockchain.begin()+height); - m_blockchain.erase(m_blockchain.begin()+height, m_blockchain.end()); - - for (auto it = m_payments.begin(); it != m_payments.end(); ) - { - if(height <= it->second.m_block_height) - it = m_payments.erase(it); - else - ++it; - } - - for (std::size_t transferIndex = 0; transferIndex < transfers.size();) { - if (transfers[transferIndex].blockIndex != 0 && transfers[transferIndex].blockIndex >= height) { - transfers.erase(transfers.begin() + transferIndex); - } else { - ++transferIndex; - } - } - - LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); - return blocks_detached; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::deinit() -{ - return true; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::clear() -{ - m_blockchain.clear(); - m_transfers.clear(); - m_blockchain.push_back(m_currency.genesisBlockHash()); - return true; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::store_keys(const std::string& keys_file_name, const std::string& password) -{ - std::string account_data; - AccountBaseSerializer accountSerializer(m_account); - bool r = epee::serialization::store_t_to_binary(accountSerializer, account_data); - CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys"); - wallet2::keys_file_data keys_file_data = boost::value_initialized(); - - crypto::chacha8_key key; - crypto::cn_context cn_context; - crypto::generate_chacha8_key(cn_context, password, key); - std::string cipher; - cipher.resize(account_data.size()); - keys_file_data.iv = crypto::rand(); - crypto::chacha8(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); - keys_file_data.account_data = cipher; - - 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); - - return true; -} -//---------------------------------------------------------------------------------------------------- -namespace -{ - bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) - { - crypto::public_key pub; - bool r = crypto::secret_key_to_public_key(sec, pub); - return r && expected_pub == pub; - } -} -//---------------------------------------------------------------------------------------------------- -void wallet2::load_keys(const std::string& keys_file_name, const std::string& password) -{ - 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); - 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::chacha8_key key; - crypto::cn_context cn_context; - crypto::generate_chacha8_key(cn_context, password, key); - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - - const cryptonote::account_keys& keys = m_account.get_keys(); - AccountBaseSerializer accountSerializer(m_account); - r = epee::serialization::load_t_from_binary(accountSerializer, account_data); - r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey); - r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey); - THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); -} -//---------------------------------------------------------------------------------------------------- -void wallet2::generate(const std::string& wallet_, const std::string& password) -{ - clear(); - prepare_file_names(wallet_); - - boost::system::error_code ignored_ec; - 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_account.generate(); - m_account_public_address = m_account.get_keys().m_account_address; - - bool r = store_keys(m_keys_file, password); - 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_currency.accountAddressAsString(m_account)); - if(!r) LOG_PRINT_RED_L0("String with address text not saved"); - - m_blockchain.push_back(m_currency.genesisBlockHash()); - - store(); -} -//---------------------------------------------------------------------------------------------------- -void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists) -{ - std::string keys_file, wallet_file; - do_prepare_file_names(file_path, keys_file, wallet_file); - - boost::system::error_code ignore; - keys_file_exists = boost::filesystem::exists(keys_file, ignore); - wallet_file_exists = boost::filesystem::exists(wallet_file, ignore); -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id) { - cryptonote::blobdata payment_id_data; - if (!epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id_data)) { - return false; - } - - if (sizeof(crypto::hash) != payment_id_data.size()) { - return false; - } - - payment_id = *reinterpret_cast(payment_id_data.data()); - return true; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::prepare_file_names(const std::string& file_path) -{ - do_prepare_file_names(file_path, m_keys_file, m_wallet_file); - return true; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::check_connection() -{ - if(m_http_client.is_connected()) - return true; - return connectClient(m_http_client); -} - -//---------------------------------------------------------------------------------------------------- -bool wallet2::connectClient(epee::net_utils::http::http_simple_client& client) { - net_utils::http::url_content u; - net_utils::parse_url(m_daemon_address, u); - if (!u.port) - u.port = RPC_DEFAULT_PORT; - return client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); -} - -//---------------------------------------------------------------------------------------------------- -void wallet2::load(const std::string& wallet_, const std::string& password) -{ - clear(); - prepare_file_names(wallet_); - - 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); - - load_keys(m_keys_file, password); - LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_currency.accountAddressAsString(m_account)); - - //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) - { - LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain"); - m_account_public_address = m_account.get_keys().m_account_address; - } else { - bool r = tools::unserialize_obj_from_file(*this, m_wallet_file); - THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, m_wallet_file); - THROW_WALLET_EXCEPTION_IF( - m_account_public_address.m_spendPublicKey != m_account.get_keys().m_account_address.m_spendPublicKey || - m_account_public_address.m_viewPublicKey != m_account.get_keys().m_account_address.m_viewPublicKey, - error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file); - } - - if (m_blockchain.empty()) { - m_blockchain.push_back(m_currency.genesisBlockHash()); - } else { - THROW_WALLET_EXCEPTION_IF(m_blockchain[0] != m_currency.genesisBlockHash(), error::wallet_internal_error, - "Genesis block missmatch. You probably use wallet without testnet flag with blockchain from test network or vice versa"); - } -} -//---------------------------------------------------------------------------------------------------- -void wallet2::store() -{ - bool r = tools::serialize_obj_to_file(*this, m_wallet_file); - THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file); -} -//---------------------------------------------------------------------------------------------------- -uint64_t wallet2::unlocked_balance() -{ - uint64_t amount = 0; - BOOST_FOREACH(transfer_details& td, m_transfers) - if(!td.m_spent && is_transfer_unlocked(td)) - amount += td.amount(); - - return amount; -} -//---------------------------------------------------------------------------------------------------- -uint64_t wallet2::balance() -{ - uint64_t amount = 0; - BOOST_FOREACH(auto& td, m_transfers) - if(!td.m_spent) - amount += td.amount(); - - - BOOST_FOREACH(auto& utx, m_unconfirmed_txs) - amount+= utx.second.m_change; - - return amount; -} -//---------------------------------------------------------------------------------------------------- -void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) const -{ - incoming_transfers = m_transfers; -} -//---------------------------------------------------------------------------------------------------- -void wallet2::get_payments(const crypto::hash& payment_id, std::list& payments) const -{ - auto range = m_payments.equal_range(payment_id); - std::for_each(range.first, range.second, [&payments](const payment_container::value_type& x) { - payments.push_back(x.second); - }); -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::is_transfer_unlocked(const transfer_details& td) const -{ - if(!is_tx_spendtime_unlocked(td.m_tx.unlockTime)) - return false; - - if(td.m_block_height + DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size()) - return false; - - return true; -} -//---------------------------------------------------------------------------------------------------- -bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time) const { - if (unlock_time < m_currency.maxBlockHeight()) { - // interpret as block index - return m_blockchain.size() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time; - } else { - //interpret as time - uint64_t current_time = static_cast(time(NULL)); - return current_time + m_currency.lockedTxAllowedDeltaSeconds() >= unlock_time; - } - return false; -} -//---------------------------------------------------------------------------------------------------- -namespace -{ - template - T pop_random_value(std::vector& vec) - { - CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty"); - - size_t idx = crypto::rand() % vec.size(); - T res = vec[idx]; - if (idx + 1 != vec.size()) - { - vec[idx] = vec.back(); - } - vec.resize(vec.size() - 1); - - return res; - } -} -//---------------------------------------------------------------------------------------------------- -uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list& selected_transfers) -{ - std::vector unused_transfers_indices; - std::vector unused_dust_indices; - for (size_t i = 0; i < m_transfers.size(); ++i) - { - const transfer_details& td = m_transfers[i]; - if (!td.m_spent && is_transfer_unlocked(td)) - { - if (dust < td.amount()) - unused_transfers_indices.push_back(i); - else - unused_dust_indices.push_back(i); - } - } - - bool select_one_dust = add_dust && !unused_dust_indices.empty(); - uint64_t found_money = 0; - while (found_money < needed_money && (!unused_transfers_indices.empty() || !unused_dust_indices.empty())) - { - size_t idx; - if (select_one_dust) - { - idx = pop_random_value(unused_dust_indices); - select_one_dust = false; - } - else - { - idx = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_indices); - } - - transfer_container::iterator it = m_transfers.begin() + idx; - selected_transfers.push_back(it); - found_money += it->amount(); - } - - return found_money; -} -//---------------------------------------------------------------------------------------------------- -void wallet2::add_unconfirmed_tx(const cryptonote::Transaction& tx, uint64_t change_amount) -{ - unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)]; - utd.m_change = change_amount; - utd.m_sent_time = time(NULL); - utd.m_tx = tx; -} -//---------------------------------------------------------------------------------------------------- -void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::Transaction& tx) -{ - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, - tx_dust_policy(m_currency.defaultDustThreshold()), tx); -} -//---------------------------------------------------------------------------------------------------- -void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra) -{ - cryptonote::Transaction tx; - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx); -} -//---------------------------------------------------------------------------------------------------- -const std::vector& wallet2::getTransfers() { - return transfers; -} - -void wallet2::reset() { - clear(); - m_unconfirmed_txs.clear(); - m_payments.clear(); - m_key_images.clear(); - for (std::size_t transferIndex = 0; transferIndex < transfers.size();) { - if (transfers[transferIndex].hasAddress) { - transfers[transferIndex].blockIndex = 0; - ++transferIndex; - } else { - transfers.erase(transfers.begin() + transferIndex); - } - } -} - -} diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h deleted file mode 100644 index f2ff6ed921..0000000000 --- a/src/wallet/wallet2.h +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include -#include - -#include "include_base_utils.h" -#include "cryptonote_core/account_boost_serialization.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "net/http_client.h" -#include "storages/http_abstract_invoke.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "common/unordered_containers_boost_serialization.h" -#include "crypto/chacha8.h" -#include "crypto/hash.h" -#include "common/BlockingQueue.h" - -#include "wallet_errors.h" - -#define DEFAULT_TX_SPENDABLE_AGE 10 -#define WALLET_RCP_CONNECTION_TIMEOUT 200000 - -namespace tools -{ - class i_wallet2_callback - { - public: - virtual void on_new_block(uint64_t height) {} - virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index) {} - virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx) {} - virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx) {} - }; - - struct tx_dust_policy - { - uint64_t dust_threshold; - bool add_to_fee; - cryptonote::AccountPublicAddress addr_for_dust; - - tx_dust_policy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, - cryptonote::AccountPublicAddress an_addr_for_dust = cryptonote::AccountPublicAddress()) - : dust_threshold(a_dust_threshold) - , add_to_fee(an_add_to_fee) - , addr_for_dust(an_addr_for_dust) - { - } - }; - - class wallet2 { - wallet2(const wallet2& rhs) : - m_currency(rhs.m_currency), - m_run(true), - m_callback(0) { - }; - - public: - wallet2(const cryptonote::Currency& currency) : - m_currency(currency), m_run(true), m_callback(0) { - } - - struct transfer_details - { - uint64_t m_block_height; - cryptonote::Transaction m_tx; - size_t m_internal_output_index; - uint64_t m_global_output_index; - bool m_spent; - crypto::key_image m_key_image; //TODO: key_image stored twice :( - - uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; } - }; - - struct payment_details - { - crypto::hash m_tx_hash; - uint64_t m_amount; - uint64_t m_block_height; - uint64_t m_unlock_time; - }; - - struct unconfirmed_transfer_details - { - cryptonote::Transaction m_tx; - uint64_t m_change; - time_t m_sent_time; - }; - - typedef std::vector transfer_container; - typedef std::unordered_multimap payment_container; - - struct keys_file_data - { - crypto::chacha8_iv iv; - std::string account_data; - - BEGIN_SERIALIZE_OBJECT() - FIELD(iv) - FIELD(account_data) - END_SERIALIZE() - }; - - struct Transfer { - uint64_t time; - bool output; - crypto::hash transactionHash; - uint64_t amount; - uint64_t fee; - crypto::hash paymentId; - bool hasAddress; - cryptonote::AccountPublicAddress address; - uint64_t blockIndex; - uint64_t unlockTime; - - template void serialize(Archive& archive, unsigned int version) { - archive & time; - archive & output; - archive & transactionHash; - archive & amount; - archive & fee; - archive & paymentId; - archive & hasAddress; - if (hasAddress) { - archive & address; - } - - archive & blockIndex; - archive & unlockTime; - } - }; - - typedef std::vector Transfers; - - void generate(const std::string& wallet, const std::string& password); - void load(const std::string& wallet, const std::string& password); - void store(); - cryptonote::account_base& get_account(){return m_account;} - - void init(const std::string& daemon_address = "http://localhost:8080"); - bool deinit(); - - void stop() { m_run.store(false, std::memory_order_relaxed); } - - const cryptonote::Currency currency() const { return m_currency; } - - i_wallet2_callback* callback() const { return m_callback; } - void callback(i_wallet2_callback* callback) { m_callback = callback; } - - void refresh(); - void refresh(size_t & blocks_fetched); - void refresh(size_t & blocks_fetched, bool& received_money); - bool refresh(size_t & blocks_fetched, bool& received_money, bool& ok); - - uint64_t balance(); - uint64_t unlocked_balance(); - template - void transfer(const std::vector& dsts, size_t fake_outputs_count, 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, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::Transaction &tx); - void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra); - void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::Transaction& tx); - bool check_connection(); - bool connectClient(epee::net_utils::http::http_simple_client& client); - void get_transfers(wallet2::transfer_container& incoming_transfers) const; - void get_payments(const crypto::hash& payment_id, std::list& payments) const; - uint64_t get_blockchain_current_height() const { return m_blockchain.size(); } - const std::vector& getTransfers(); - void reset(); - - template - inline void serialize(t_archive &a, const unsigned int ver) - { - if(ver < 5) - return; - a & m_blockchain; - a & m_transfers; - a & m_account_public_address; - a & m_key_images; - if(ver < 6) - return; - a & m_unconfirmed_txs; - if(ver < 7) - return; - a & m_payments; - if (ver >= 8) { - a & transfers; - } - } - - static void wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists); - static bool parse_payment_id(const std::string& payment_id_str, crypto::hash& payment_id); - - private: - bool store_keys(const std::string& keys_file_name, const std::string& password); - void load_keys(const std::string& keys_file_name, const std::string& password); - - struct TxItem { - cryptonote::Transaction tx; - uint64_t time; - uint64_t height; - crypto::hash blockId; - crypto::public_key txPubKey; - std::vector txExtraFields; - - // resolved - std::vector outs; - uint64_t txMoneyGotInOuts; - }; - - typedef std::unique_ptr TxQueueItem; - typedef BlockingQueue TxQueue; - - bool addNewBlockchainEntry(const crypto::hash& bl_id, uint64_t start_height, uint64_t height); - - size_t processNewBlockchainEntry(TxQueue& queue, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height); - bool processNewTransaction(TxQueue& queue, const cryptonote::Transaction& tx, uint64_t height, uint64_t time, const crypto::hash& bl_id); - void processCheckedTransaction(const TxItem& item); - - // returns number of blocks added - size_t updateBlockchain(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, std::unordered_set& newBlocks); - void processTransactions(const cryptonote::COMMAND_RPC_QUERY_BLOCKS::response& res, const std::unordered_set& newBlocks); - cryptonote::COMMAND_RPC_QUERY_BLOCKS::response queryBlocks(epee::net_utils::http::http_simple_client& client); - - size_t detach_blockchain(uint64_t height); - void get_short_chain_history(std::list& ids) const; - bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; - bool is_transfer_unlocked(const transfer_details& td) const; - bool clear(); - uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list& selected_transfers); - bool prepare_file_names(const std::string& file_path); - void process_unconfirmed(const cryptonote::Transaction& tx); - void add_unconfirmed_tx(const cryptonote::Transaction& tx, uint64_t change_amount); - void checkGenesis(const crypto::hash& genesis_hash); //throws - - inline void print_source_entry(const cryptonote::tx_source_entry& src) { - std::string indexes; - std::for_each(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& s_e) { - indexes += std::to_string(s_e.first) + " "; - }); - LOG_PRINT_L0("amount=" << m_currency.formatAmount(src.amount) << - ", real_output=" << src.real_output << - ", real_output_in_tx_index=" << src.real_output_in_tx_index << - ", indexes: " << indexes); - } - - private: - const cryptonote::Currency& m_currency; - cryptonote::account_base m_account; - std::string m_daemon_address; - std::string m_wallet_file; - std::string m_keys_file; - epee::net_utils::http::http_simple_client m_http_client; - std::vector m_blockchain; - std::unordered_map m_unconfirmed_txs; - - transfer_container m_transfers; - payment_container m_payments; - std::unordered_map m_key_images; - cryptonote::AccountPublicAddress m_account_public_address; - - std::atomic m_run; - - i_wallet2_callback* m_callback; - - Transfers transfers; - }; -} -BOOST_CLASS_VERSION(tools::wallet2, 8) - -namespace boost -{ - namespace serialization - { - template - inline void serialize(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver) - { - a & x.m_block_height; - a & x.m_global_output_index; - a & x.m_internal_output_index; - a & x.m_tx; - a & x.m_spent; - a & x.m_key_image; - } - - template - inline void serialize(Archive &a, tools::wallet2::unconfirmed_transfer_details &x, const boost::serialization::version_type ver) - { - a & x.m_change; - a & x.m_sent_time; - a & x.m_tx; - } - - template - inline void serialize(Archive& a, tools::wallet2::payment_details& x, const boost::serialization::version_type ver) - { - a & x.m_tx_hash; - a & x.m_amount; - a & x.m_block_height; - a & x.m_unlock_time; - } - } -} - -namespace tools -{ - namespace detail - { - //---------------------------------------------------------------------------------------------------- - inline void digit_split_strategy(const std::vector& dsts, - const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust) - { - splitted_dsts.clear(); - dust = 0; - - BOOST_FOREACH(auto& de, dsts) - { - cryptonote::decompose_amount_into_digits(de.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, de.addr)); }, - [&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, de.addr)); } ); - } - - cryptonote::decompose_amount_into_digits(change_dst.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr)); }, - [&](uint64_t a_dust) { dust = a_dust; } ); - } - //---------------------------------------------------------------------------------------------------- - inline void null_split_strategy(const std::vector& dsts, - const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust) - { - splitted_dsts = dsts; - - dust = 0; - uint64_t change = change_dst.amount; - if (0 < dust_threshold) - { - for (uint64_t order = 10; order <= 10 * dust_threshold; order *= 10) - { - uint64_t dust_candidate = change_dst.amount % order; - uint64_t change_candidate = (change_dst.amount / order) * order; - if (dust_candidate <= dust_threshold) - { - dust = dust_candidate; - change = change_candidate; - } - else - { - break; - } - } - } - - if (0 != change) - { - splitted_dsts.push_back(cryptonote::tx_destination_entry(change, change_dst.addr)); - } - } - //---------------------------------------------------------------------------------------------------- - } - //---------------------------------------------------------------------------------------------------- - template - void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy) - { - cryptonote::Transaction tx; - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, destination_split_strategy, dust_policy, tx); - } - - template - void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::Transaction &tx) - { - using namespace cryptonote; - THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); - - uint64_t needed_money = fee; - BOOST_FOREACH(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, m_currency.publicAddressBase58Prefix(), dsts, fee); - } - - std::list selected_transfers; - uint64_t found_money = select_transfers(needed_money, 0 == fake_outputs_count, dust_policy.dust_threshold, selected_transfers); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); - - 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 - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - { - 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()); - } - - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); - 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::vector scanty_outs; - BOOST_FOREACH(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.push_back(amount_outs); - } - } - 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; - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - { - sources.resize(sources.size()+1); - cryptonote::tx_source_entry& src = sources.back(); - transfer_details& td = *it; - src.amount = td.amount(); - //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;}); - BOOST_FOREACH(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 = daemon_oe.out_key; - 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 = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; - 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; - print_source_entry(src); - ++i; - } - - cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts); - if (needed_money < found_money) - { - change_dts.addr = m_account.get_keys().m_account_address; - change_dts.amount = found_money - needed_money; - } - - uint64_t dust = 0; - std::vector splitted_dsts; - destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust); - THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < dust, error::wallet_internal_error, "invalid dust value: dust = " + - std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold)); - if (0 != dust && !dust_policy.add_to_fee) - { - splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust)); - } - - bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time); - THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, m_currency.publicAddressBase58Prefix(), sources, splitted_dsts, unlock_time); - uint64_t transactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); - THROW_WALLET_EXCEPTION_IF(get_object_blobsize(tx) > transactionSizeLimit, error::tx_too_big, tx, transactionSizeLimit); - - std::string key_images; - bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const TransactionInput& s_e) -> bool - { - CHECKED_GET_SPECIFIC_VARIANT(s_e, const TransactionInputToKey, in, false); - key_images += boost::to_string(in.keyImage) + " "; - return true; - }); - THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx); - - COMMAND_RPC_SEND_RAW_TX::request req; - req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx)); - COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; - r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, tx, daemon_send_resp.status); - - add_unconfirmed_tx(tx, change_dts.amount); - - crypto::hash transactionHash = get_transaction_hash(tx); - LOG_PRINT_L2("transaction " << transactionHash << " generated ok and sent to daemon, key_images: [" << key_images << "]"); - - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - it->m_spent = true; - - crypto::hash paymentId = null_hash; - std::vector transactionExtras; - if (parse_tx_extra(tx.extra, transactionExtras)) { - tx_extra_nonce extraNonce; - if (find_tx_extra_field_by_type(transactionExtras, extraNonce)) { - get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); - } - } - - for (auto& destination : dsts) { - Transfer transfer; - transfer.time = static_cast(time(NULL)); - transfer.output = true; - transfer.transactionHash = transactionHash; - transfer.amount = destination.amount; - transfer.fee = fee; - transfer.paymentId = paymentId; - transfer.hasAddress = true; - transfer.address = destination.addr; - transfer.blockIndex = 0; - transfer.unlockTime = unlock_time; - transfers.push_back(transfer); - } - - LOG_PRINT_L0("Transaction successfully sent. <" << transactionHash << ">" << ENDL - << "Commission: " << m_currency.formatAmount(fee+dust) << " (dust: " << m_currency.formatAmount(dust) << ")" << ENDL - << "Balance: " << m_currency.formatAmount(balance()) << ENDL - << "Unlocked: " << m_currency.formatAmount(unlocked_balance()) << ENDL - << "Please, wait for confirmation for your balance to be unlocked."); - } -} diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ccd2ad4b34..3af0b8ad45 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -25,208 +25,252 @@ using namespace epee; #include "misc_language.h" #include "string_tools.h" #include "crypto/hash.h" +#include "WalletHelper.h" +#include "wallet_errors.h" -namespace tools -{ - //----------------------------------------------------------------------------------- - const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = {"rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", "", true}; - const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"}; - void wallet_rpc_server::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_rpc_bind_ip); - command_line::add_arg(desc, arg_rpc_bind_port); - } - //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w) - {} - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::run() - { - m_net_server.add_idle_handler([this](){ - try { - m_wallet.refresh(); - } catch (const std::exception& e) { - LOG_ERROR("Exception while refreshing, what=" << e.what()); - } - - try { - m_wallet.store(); - } catch (const std::exception& e) { - LOG_ERROR("Exception while storing, what=" << e.what()); - } - - return true; - }, 20000); +using namespace CryptoNote; +using namespace cryptonote; +namespace tools { +//----------------------------------------------------------------------------------- +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", "", true }; +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = { "rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1" }; - //DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING - return epee::http_server_impl_base::run(1, true); +void wallet_rpc_server::init_options(boost::program_options::options_description& desc) { + command_line::add_arg(desc, arg_rpc_bind_ip); + command_line::add_arg(desc, arg_rpc_bind_port); +} +//------------------------------------------------------------------------------------------------------------------------------ +wallet_rpc_server::wallet_rpc_server(CryptoNote::IWallet&w, CryptoNote::INode& n, cryptonote::Currency& currency, const std::string& walletFile) :m_wallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile), m_saveResultPromise(nullptr) { +} +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::run() { + //DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING + return epee::http_server_impl_base::run(1, true); +} +//---------------------------------------------------------------------------------------------------- +void wallet_rpc_server::saveCompleted(std::error_code result) { + if (m_saveResultPromise.get() != nullptr) { + m_saveResultPromise->set_value(result); } - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) - { - m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); - m_port = command_line::get_arg(vm, arg_rpc_bind_port); - return true; +} +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) { + m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); + m_port = command_line::get_arg(vm, arg_rpc_bind_port); + return true; +} +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) { + m_net_server.set_threads_prefix("RPC"); + bool r = handle_command_line(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); + return epee::http_server_impl_base::init(m_port, m_bind_ip); +} +//------------------------------------------------------------------------------------------------------------------------------ +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, connection_context& cntx) { + try { + res.balance = m_wallet.pendingBalance(); + res.unlocked_balance = m_wallet.pendingBalance(); + } catch (std::exception& e) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what(); + return false; } - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) - { - m_net_server.set_threads_prefix("RPC"); - bool r = handle_command_line(vm); - CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); - return epee::http_server_impl_base::init(m_port, m_bind_ip); + return true; +} +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) { + std::vector transfers; + for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { + CryptoNote::Transfer transfer; + transfer.address = it->address; + transfer.amount = it->amount; + transfers.push_back(transfer); } - //------------------------------------------------------------------------------------------------------------------------------ - 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, connection_context& cntx) - { - try - { - res.balance = m_wallet.balance(); - res.unlocked_balance = m_wallet.unlocked_balance(); + + std::vector extra; + if (!req.payment_id.empty()) { + std::string payment_id_str = req.payment_id; + + crypto::hash payment_id; + if (!cryptonote::parsePaymentId(payment_id_str, payment_id)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"; + return false; } - catch (std::exception& e) - { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + + std::string extra_nonce; + cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"; return false; } - return true; } - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) - { - std::vector dsts; - for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { - cryptonote::tx_destination_entry de; - if (!m_wallet.currency().parseAccountAddressString(it->address, de.addr)) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; - er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; - return false; - } - de.amount = it->amount; - dsts.push_back(de); - } - std::vector extra; - if (!req.payment_id.empty()) { - std::string payment_id_str = req.payment_id; - - crypto::hash payment_id; - if (!wallet2::parse_payment_id(payment_id_str, payment_id)) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"; - return false; - } + std::string extraString; + std::copy(extra.begin(), extra.end(), std::back_inserter(extraString)); + try { + cryptonote::WalletHelper::SendCompleteResultObserver sent; + std::promise txId; + sent.expectedTxID = txId.get_future(); + std::future f_sendError = sent.sendResult.get_future(); - std::string extra_nonce; - cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"; - return false; - } - } - - try { - cryptonote::Transaction tx; - m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx); - res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(tx)); - return true; - } catch (const tools::error::daemon_busy& e) { - er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; - er.message = e.what(); - return false; - } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; - er.message = e.what(); - return false; - } catch (...) { + m_wallet.addObserver(&sent); + CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time); + txId.set_value(tx); + std::error_code sendError = f_sendError.get(); + m_wallet.removeObserver(&sent); + if (sendError) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + er.message = sendError.message(); return false; } + + CryptoNote::TransactionInfo txInfo; + m_wallet.getTransaction(tx, txInfo); + + std::string hexHash; + std::copy(txInfo.hash.begin(), txInfo.hash.end(), std::back_inserter(hexHash)); + res.tx_hash = epee::string_tools::buff_to_hex_nodelimer(hexHash); return true; + } catch (const tools::error::daemon_busy& e) { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + return false; + } catch (const std::exception& e) { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = e.what(); + return false; + } catch (...) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; } - //------------------------------------------------------------------------------------------------------------------------------ - 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, connection_context& cntx) - { - try - { - m_wallet.store(); - } - catch (std::exception& e) - { + return true; +} +//------------------------------------------------------------------------------------------------------------------------------ +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, connection_context& cntx) { + try { + std::ofstream walletFile; + walletFile.open(m_walletFilename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) + return false; + m_wallet.addObserver(this); + m_saveResultPromise.reset(new std::promise()); + std::future f_saveError = m_saveResultPromise->get_future(); + m_wallet.save(walletFile); + auto saveError = f_saveError.get(); + m_saveResultPromise.reset(nullptr); + if (saveError) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + er.message = saveError.message(); return false; } - return true; + m_wallet.removeObserver(this); + } catch (std::exception& e) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = e.what(); + return false; + } + return true; +} +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) { + crypto::hash expectedPaymentId; + cryptonote::blobdata payment_id_blob; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob)) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment ID has invald format"; + return false; } - //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) - { - crypto::hash payment_id; - cryptonote::blobdata payment_id_blob; - if(!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob)) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment ID has invald format"; - return false; - } - if(sizeof(payment_id) != payment_id_blob.size()) - { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment ID has invalid size"; - return false; + if (sizeof(expectedPaymentId) != payment_id_blob.size()) { + er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; + er.message = "Payment ID has invalid size"; + return false; + } + + expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); + size_t transactionsCount = m_wallet.getTransactionCount(); + for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { + TransactionInfo txInfo; + m_wallet.getTransaction(trantransactionNumber, txInfo); + if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + continue; } - payment_id = *reinterpret_cast(payment_id_blob.data()); + if (txInfo.totalAmount < 0) continue; + std::vector extraVec; + extraVec.reserve(txInfo.extra.size()); + std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - res.payments.clear(); - std::list payment_list; - m_wallet.get_payments(payment_id, payment_list); - for (auto payment : payment_list) - { + crypto::hash paymentId; + if (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { wallet_rpc::payment_details rpc_payment; - rpc_payment.tx_hash = epee::string_tools::pod_to_hex(payment.m_tx_hash); - rpc_payment.amount = payment.m_amount; - rpc_payment.block_height = payment.m_block_height; - rpc_payment.unlock_time = payment.m_unlock_time; + rpc_payment.tx_hash = epee::string_tools::pod_to_hex(txInfo.hash); + rpc_payment.amount = txInfo.totalAmount; + rpc_payment.block_height = txInfo.totalAmount; + rpc_payment.unlock_time = txInfo.unlockTime; res.payments.push_back(rpc_payment); } - - return true; } - 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, connection_context& cntx) { - res.transfers.clear(); - const std::vector& transfers = m_wallet.getTransfers(); - for (const tools::wallet2::Transfer& transfer : transfers) { - wallet_rpc::Transfer transfer2; - transfer2.time = transfer.time; - transfer2.output = transfer.output; - transfer2.transactionHash = epee::string_tools::pod_to_hex(transfer.transactionHash); - transfer2.amount = transfer.amount; - transfer2.fee = transfer.fee; - transfer2.paymentId = transfer.paymentId == cryptonote::null_hash ? "" : epee::string_tools::pod_to_hex(transfer.paymentId); - transfer2.address = transfer.hasAddress ? getAccountAddressAsStr(m_wallet.currency().publicAddressBase58Prefix(), transfer.address) : ""; - transfer2.blockIndex = transfer.blockIndex; - transfer2.unlockTime = transfer.unlockTime; - res.transfers.push_back(transfer2); + return true; +} + +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, connection_context& cntx) { + res.transfers.clear(); + size_t transactionsCount = m_wallet.getTransactionCount(); + for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { + TransactionInfo txInfo; + m_wallet.getTransaction(trantransactionNumber, txInfo); + if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + continue; } - return true; - } + std::string address = ""; + if (txInfo.totalAmount < 0) { + if (txInfo.transferCount > 0) { + Transfer tr; + m_wallet.getTransfer(txInfo.firstTransferId, tr); + address = tr.address; + } + } - bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx) { - res.height = m_wallet.get_blockchain_current_height(); - return true; - } + wallet_rpc::Transfer transfer; + transfer.time = txInfo.timestamp; + transfer.output = txInfo.totalAmount < 0; + transfer.transactionHash = epee::string_tools::pod_to_hex(txInfo.hash); + transfer.amount = txInfo.totalAmount; + transfer.fee = txInfo.fee; + transfer.address = address; + transfer.blockIndex = txInfo.blockHeight; + transfer.unlockTime = txInfo.unlockTime; + transfer.paymentId = ""; - bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) { - m_wallet.reset(); - return true; + std::vector extraVec; + extraVec.reserve(txInfo.extra.size()); + std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); + + crypto::hash paymentId; + transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? epee::string_tools::pod_to_hex(paymentId) : ""); + + res.transfers.push_back(transfer); } + + return true; +} + +bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx) { + res.height = m_node.getLastLocalBlockHeight(); + return true; +} + +bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) { + m_wallet.reset(); + return true; +} + } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 680324f28b..3011732677 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -17,27 +17,31 @@ #pragma once +#include #include #include #include "net/http_server_impl_base.h" #include "wallet_rpc_server_commans_defs.h" -#include "wallet2.h" +#include "Wallet.h" #include "common/command_line.h" namespace tools { /************************************************************************/ /* */ /************************************************************************/ - class wallet_rpc_server: public epee::http_server_impl_base + class wallet_rpc_server: public epee::http_server_impl_base, public CryptoNote::IWalletObserver { public: typedef epee::net_utils::connection_context_base connection_context; - wallet_rpc_server(wallet2& cr); + wallet_rpc_server(CryptoNote::IWallet &w, CryptoNote::INode &n, cryptonote::Currency& currency, const std::string& walletFilename); const static command_line::arg_descriptor arg_rpc_bind_port; const static command_line::arg_descriptor arg_rpc_bind_ip; + //---------------- IWalletObserver ------------------------- + virtual void saveCompleted(std::error_code result) override; + //---------------------------------------------------------- static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); @@ -69,8 +73,13 @@ namespace tools bool handle_command_line(const boost::program_options::variables_map& vm); - wallet2& m_wallet; + CryptoNote::IWallet& m_wallet; + CryptoNote::INode& m_node; std::string m_port; std::string m_bind_ip; + cryptonote::Currency& m_currency; + const std::string m_walletFilename; + + std::unique_ptr> m_saveResultPromise; }; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt old mode 100644 new mode 100755 index de7ae5ff58..fb56e3ba3e --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,58 +5,73 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR} ../version) file(GLOB_RECURSE CORE_TESTS core_tests/*) file(GLOB_RECURSE CRYPTO_TESTS crypto/*) -file(GLOB_RECURSE FUNC_TESTS functional_tests/*) file(GLOB_RECURSE PERFORMANCE_TESTS performance_tests/*) file(GLOB_RECURSE CORE_PROXY core_proxy/*) file(GLOB_RECURSE TEST_GENERATOR TestGenerator/*) file(GLOB_RECURSE UNIT_TESTS unit_tests/*) +file(GLOB_RECURSE INTEGRATION_TEST_LIB integration_test_lib/*) +file(GLOB_RECURSE INTEGRATION_TESTS integration_tests/*) +file(GLOB_RECURSE TRANSFERS_TESTS transfers_tests/*) + + source_group(core_tests FILES ${CORE_TESTS}) source_group(crypto_tests FILES ${CRYPTO_TESTS}) -source_group(functional_tests FILES ${FUNC_TESTS}) source_group(performance_tests FILES ${PERFORMANCE_TESTS}) source_group(core_proxy FILES ${CORE_PROXY}) source_group(TestGenerator FILES ${TEST_GENERATOR}) source_group(unit_tests FILES ${UNIT_TESTS}) +source_group(integration_test_lib FILES ${INTEGRATION_TEST_LIB}) +source_group(integration_tests FILES ${INTEGRATION_TESTS}) +source_group(transfers_tests FILES ${TRANSFERS_TESTS}) + # add_subdirectory(daemon_tests) add_library(TestGenerator ${TEST_GENERATOR}) +add_library(integration_test_lib ${INTEGRATION_TEST_LIB}) + add_executable(coretests ${CORE_TESTS}) add_executable(crypto-tests ${CRYPTO_TESTS}) add_executable(difficulty-tests difficulty/difficulty.cpp) add_executable(hash-tests hash/main.cpp) add_executable(hash-target-tests hash-target.cpp) -add_executable(functional_tests ${FUNC_TESTS}) add_executable(performance_tests ${PERFORMANCE_TESTS}) -add_executable(core_proxy ${CORE_PROXY}) +add_executable(core_proxy ${CORE_PROXY} ../src/p2p/NetNodeConfig.cpp) add_executable(unit_tests ${UNIT_TESTS}) add_executable(net_load_tests_clt net_load_tests/clt.cpp) add_executable(net_load_tests_srv net_load_tests/srv.cpp) +add_executable(integration_tests ${INTEGRATION_TESTS} ../src/p2p/NetNodeConfig.cpp) +add_executable(transfers_tests ${TRANSFERS_TESTS} ../src/p2p/NetNodeConfig.cpp ../src/cryptonote_core/MinerConfig.cpp ../src/cryptonote_core/CoreConfig.cpp) + target_link_libraries(core_proxy epee cryptonote_core common crypto upnpc-static ${Boost_LIBRARIES}) target_link_libraries(coretests epee cryptonote_core common crypto TestGenerator ${Boost_LIBRARIES}) -target_link_libraries(difficulty-tests epee common crypto cryptonote_core ${Boost_LIBRARIES}) -target_link_libraries(functional_tests epee cryptonote_core wallet common crypto ${Boost_LIBRARIES}) +target_link_libraries(difficulty-tests epee cryptonote_core common crypto ${Boost_LIBRARIES}) target_link_libraries(hash-tests crypto) target_link_libraries(hash-target-tests epee crypto cryptonote_core) target_link_libraries(performance_tests epee cryptonote_core common crypto ${Boost_LIBRARIES}) -target_link_libraries(unit_tests epee wallet TestGenerator cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) +target_link_libraries(unit_tests epee wallet TestGenerator cryptonote_core common crypto gtest_main transfers serialization inprocess_node ${Boost_LIBRARIES}) target_link_libraries(net_load_tests_clt epee cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) target_link_libraries(net_load_tests_srv epee cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) +target_link_libraries(integration_tests integration_test_lib epee wallet node_rpc_proxy rpc transfers cryptonote_core crypto common upnpc-static serialization System inprocess_node ${Boost_LIBRARIES}) +target_link_libraries(transfers_tests integration_test_lib epee node_rpc_proxy rpc upnpc-static transfers System gtest_main inprocess_node wallet serialization cryptonote_core crypto common ${Boost_LIBRARIES}) + + file(GLOB_RECURSE NODE_RPC_PROXY_TEST node_rpc_proxy_test/*) source_group(node_rpc_proxy_test FILES ${NODE_RPC_PROXY_TEST}) add_executable(node_rpc_proxy_test ${NODE_RPC_PROXY_TEST}) -target_link_libraries(node_rpc_proxy_test epee node_rpc_proxy cryptonote_core common crypto ${Boost_LIBRARIES}) +target_link_libraries(node_rpc_proxy_test epee rpc node_rpc_proxy cryptonote_core common crypto serialization ${Boost_LIBRARIES}) if(NOT MSVC) - set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv TestGenerator APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") + set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv TestGenerator integration_test_lib integration_tests APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") endif() -add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests node_rpc_proxy_test) -set_property(TARGET coretests crypto-tests functional_tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test TestGenerator PROPERTY FOLDER "tests") +add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests node_rpc_proxy_test integration_tests transfers_tests) +set_property(TARGET coretests crypto-tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test TestGenerator integration_test_lib integration_tests PROPERTY FOLDER "tests") +set_property(TARGET transfers_tests PROPERTY FOLDER "tests") add_dependencies(core_proxy version) diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp index a2db0ca719..55bd3acf7f 100644 --- a/tests/core_proxy/core_proxy.cpp +++ b/tests/core_proxy/core_proxy.cpp @@ -29,6 +29,7 @@ using namespace epee; using namespace std; #include +#include "cryptonote_core/CoreConfig.h" #include "common/command_line.h" #include "console_handler.h" @@ -71,7 +72,9 @@ int main(int argc, char* argv[]) po::options_description desc("Allowed options"); // tools::get_default_data_dir() can't be called during static initialization command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir()); - nodetool::node_server >::init_options(desc); + + cryptonote::CoreConfig::initOptions(desc); + nodetool::NetNodeConfig::initOptions(desc); po::variables_map vm; bool r = command_line::handle_error_helper(desc, [&]() @@ -97,14 +100,18 @@ int main(int argc, char* argv[]) //daemon_cmmands_handler dch(p2psrv); //initialize objects + cryptonote::CoreConfig coreConfig; + coreConfig.init(vm); + nodetool::NetNodeConfig netNodeConfig; + netNodeConfig.init(vm); LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(vm, false); + bool res = p2psrv.init(netNodeConfig, false); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); LOG_PRINT_L0("P2p server initialized OK"); LOG_PRINT_L0("Initializing cryptonote protocol..."); - res = cprotocol.init(vm); + res = cprotocol.init(); CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol."); LOG_PRINT_L0("Cryptonote protocol initialized OK"); diff --git a/tests/core_tests/TransactionBuilder.cpp b/tests/core_tests/TransactionBuilder.cpp index 37ab1b5a12..6565b47750 100644 --- a/tests/core_tests/TransactionBuilder.cpp +++ b/tests/core_tests/TransactionBuilder.cpp @@ -28,6 +28,10 @@ TransactionBuilder& TransactionBuilder::newTxKeys() { return *this; } +TransactionBuilder& TransactionBuilder::setTxKeys(const cryptonote::KeyPair& txKeys) { + m_txKey = txKeys; + return *this; +} TransactionBuilder& TransactionBuilder::setInput(const std::vector& sources, const cryptonote::account_keys& senderKeys) { m_sources = sources; @@ -35,13 +39,8 @@ TransactionBuilder& TransactionBuilder::setInput(const std::vector SignatureVector; typedef std::vector SignatureMultivector; + struct MultisignatureSource { + cryptonote::TransactionInputMultisignature input; + KeysVector keys; + crypto::public_key srcTxPubKey; + size_t srcOutputIndex; + }; + TransactionBuilder(const cryptonote::Currency& currency, uint64_t unlockTime = 0); // regenerate transaction keys TransactionBuilder& newTxKeys(); + TransactionBuilder& setTxKeys(const cryptonote::KeyPair& txKeys); // inputs TransactionBuilder& setInput(const std::vector& sources, const cryptonote::account_keys& senderKeys); - TransactionBuilder& addMultisignatureInput(const cryptonote::TransactionInputMultisignature& input, const KeysVector& keys); + TransactionBuilder& addMultisignatureInput(const MultisignatureSource& source); // outputs TransactionBuilder& setOutput(const std::vector& destinations); @@ -53,14 +61,10 @@ class TransactionBuilder { void fillOutputs(cryptonote::Transaction& tx) const; void signSources(const crypto::hash& prefixHash, const std::vector& contexts, cryptonote::Transaction& tx) const; - struct MultisignatureSource { - cryptonote::TransactionInputMultisignature input; - KeysVector keys; - }; - struct MultisignatureDestination { uint64_t amount; - cryptonote::TransactionOutputMultisignature output; + uint32_t requiredSignatures; + KeysVector keys; }; cryptonote::account_keys m_senderKeys; diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 4fedf5eee6..8744efd95e 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -19,6 +19,7 @@ #include #include +#include "cryptonote_core/CoreConfig.h" #include "common/boost_serialization_helper.h" #include "common/command_line.h" @@ -390,7 +391,7 @@ template inline bool do_replay_events(std::vector& events, t_test_class& validator) { boost::program_options::options_description desc("Allowed options"); - cryptonote::core::init_options(desc); + cryptonote::CoreConfig::initOptions(desc); command_line::add_arg(desc, command_line::arg_data_dir); boost::program_options::variables_map vm; bool r = command_line::handle_error_helper(desc, [&]() @@ -402,9 +403,13 @@ inline bool do_replay_events(std::vector& events, t_test_class if (!r) return false; + cryptonote::CoreConfig coreConfig; + coreConfig.init(vm); + cryptonote::MinerConfig emptyMinerConfig; + cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects cryptonote::core c(validator.currency(), &pr); - if (!c.init(vm, false)) + if (!c.init(coreConfig, emptyMinerConfig, false)) { std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl; return false; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 4c749f1dcd..49ff96f8f1 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -30,6 +30,7 @@ #include "transaction_tests.h" #include "tx_validation.h" #include "upgrade.h" +#include "random_outs.h" namespace po = boost::program_options; @@ -203,12 +204,12 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainDifferentBlocks(false)); GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainDifferentBlocks(true)); - GENERATE_AND_PLAY(gen_uint_overflow_1); GENERATE_AND_PLAY(gen_uint_overflow_2); GENERATE_AND_PLAY(gen_block_reward); GENERATE_AND_PLAY(gen_upgrade); + GENERATE_AND_PLAY(GetRandomOutputs); std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta); std::cout << "\nREPORT:\n"; diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 1e5004ac99..400925a0a2 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -92,6 +92,7 @@ DoubleSpendBase::DoubleSpendBase() : send_amount(MK_COINS(17)), has_invalid_tx(false) { + m_outputTxKey = KeyPair::generate(); m_bob_account.generate(); m_alice_account.generate(); @@ -151,6 +152,7 @@ TestGenerator DoubleSpendBase::prepare(std::vector& events) co auto builder = generator.createTxBuilder(generator.minerAccount, m_bob_account, send_amount, m_currency.minimumFee()); + builder.setTxKeys(m_outputTxKey); builder.m_destinations.clear(); TransactionBuilder::KeysVector kv; @@ -160,7 +162,7 @@ TestGenerator DoubleSpendBase::prepare(std::vector& events) co // move money auto tx = builder.build(); - + generator.addEvent(tx); generator.makeNextBlock(tx); @@ -170,19 +172,27 @@ TestGenerator DoubleSpendBase::prepare(std::vector& events) co return generator; } -TransactionBuilder DoubleSpendBase::createBobToAliceTx() const { - TransactionBuilder builder(m_currency); - TransactionInputMultisignature msigInput; - msigInput.amount = send_amount; - msigInput.outputIndex = 0; - msigInput.signatures = 1; +TransactionBuilder::MultisignatureSource DoubleSpendBase::createSource() const { - TransactionBuilder::KeysVector kv; - kv.push_back(m_bob_account.get_keys()); + TransactionBuilder::MultisignatureSource src; + + src.input.amount = send_amount; + src.input.outputIndex = 0; + src.input.signatures = 1; + + src.keys.push_back(m_bob_account.get_keys()); + src.srcTxPubKey = m_outputTxKey.pub; + src.srcOutputIndex = 0; + + return src; +} + +TransactionBuilder DoubleSpendBase::createBobToAliceTx() const { + TransactionBuilder builder(m_currency); builder. - addMultisignatureInput(msigInput, kv). + addMultisignatureInput(createSource()). addOutput(tx_destination_entry(send_amount - m_currency.minimumFee(), m_alice_account.get_keys().m_account_address)); return builder; @@ -203,19 +213,11 @@ bool MultiSigTx_DoubleSpendInTx::generate(std::vector& events) generator.addCallback("mark_last_valid_block"); - TransactionInputMultisignature msigInput; - msigInput.amount = send_amount; - msigInput.outputIndex = 0; - msigInput.signatures = 1; - - TransactionBuilder::KeysVector kv; - kv.push_back(m_bob_account.get_keys()); - TransactionBuilder builder(generator.currency()); auto tx = builder. - addMultisignatureInput(msigInput, kv). - addMultisignatureInput(msigInput, kv). + addMultisignatureInput(createSource()). + addMultisignatureInput(createSource()). addOutput(tx_destination_entry(send_amount*2 - m_currency.minimumFee(), m_alice_account.get_keys().m_account_address)). build(); diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index ebbf67870f..7a87beba86 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -154,11 +154,13 @@ class DoubleSpendBase : public test_chain_unit_base TestGenerator prepare(std::vector& events) const; TransactionBuilder createBobToAliceTx() const; + TransactionBuilder::MultisignatureSource createSource() const; protected: cryptonote::account_base m_bob_account; cryptonote::account_base m_alice_account; + cryptonote::KeyPair m_outputTxKey; private: diff --git a/tests/core_tests/random_outs.cpp b/tests/core_tests/random_outs.cpp new file mode 100644 index 0000000000..55a61330da --- /dev/null +++ b/tests/core_tests/random_outs.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "random_outs.h" +#include "TestGenerator.h" +#include "rpc/core_rpc_server_commands_defs.h" + +GetRandomOutputs::GetRandomOutputs() { + REGISTER_CALLBACK_METHOD(GetRandomOutputs, checkHalfUnlocked); + REGISTER_CALLBACK_METHOD(GetRandomOutputs, checkFullyUnlocked); +} + +bool GetRandomOutputs::generate(std::vector& events) const { + TestGenerator generator(m_currency, events); + + generator.generateBlocks(); + + uint64_t sendAmount = MK_COINS(1); + + auto builder = generator.createTxBuilder( + generator.minerAccount, generator.minerAccount, sendAmount, m_currency.minimumFee()); + + for (int i = 0; i < 10; ++i) { + auto builder = generator.createTxBuilder( + generator.minerAccount, generator.minerAccount, sendAmount, m_currency.minimumFee()); + + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + // unlock half of the money + generator.generateBlocks(m_currency.minedMoneyUnlockWindow() / 2); + generator.addCallback("checkHalfUnlocked"); + + // unlock the remaining part + generator.generateBlocks(m_currency.minedMoneyUnlockWindow() / 2); + generator.addCallback("checkFullyUnlocked"); + + return true; +} + +bool GetRandomOutputs::request(cryptonote::core& c, uint64_t amount, size_t mixin, cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp) { + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request req; + + req.amounts.push_back(amount); + req.outs_count = mixin; + + resp = boost::value_initialized(); + + return c.get_random_outs_for_amounts(req, resp); +} + +#define CHECK(cond) if((cond) == false) { LOG_ERROR("Condition "#cond" failed"); return false; } + +bool GetRandomOutputs::checkHalfUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events) { + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response resp; + + auto amount = MK_COINS(1); + auto unlocked = m_currency.minedMoneyUnlockWindow() / 2 + 1; + + CHECK(request(c, amount, 0, resp)); + CHECK(resp.outs.size() == 1); + CHECK(resp.outs[0].amount == amount); + CHECK(resp.outs[0].outs.size() == 0); + + CHECK(request(c, amount, unlocked, resp)); + CHECK(resp.outs.size() == 1); + CHECK(resp.outs[0].amount == amount); + CHECK(resp.outs[0].outs.size() == unlocked); + + CHECK(request(c, amount, unlocked * 2, resp)); + CHECK(resp.outs.size() == 1); + CHECK(resp.outs[0].amount == amount); + CHECK(resp.outs[0].outs.size() == unlocked); + + return true; +} + +bool GetRandomOutputs::checkFullyUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events) { + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response resp; + + auto amount = MK_COINS(1); + auto unlocked = m_currency.minedMoneyUnlockWindow() + 1; + + CHECK(request(c, amount, unlocked, resp)); + CHECK(resp.outs.size() == 1); + CHECK(resp.outs[0].amount == amount); + CHECK(resp.outs[0].outs.size() == unlocked); + + CHECK(request(c, amount, unlocked * 2, resp)); + CHECK(resp.outs.size() == 1); + CHECK(resp.outs[0].amount == amount); + CHECK(resp.outs[0].outs.size() == unlocked); + + return true; +} diff --git a/tests/core_tests/random_outs.h b/tests/core_tests/random_outs.h new file mode 100644 index 0000000000..08ae2e3b8a --- /dev/null +++ b/tests/core_tests/random_outs.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "chaingen.h" + +struct GetRandomOutputs : public test_chain_unit_base +{ + GetRandomOutputs(); + + // bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); + // bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); + // bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); + + bool generate(std::vector& events) const; + + +private: + + bool checkHalfUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool checkFullyUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events); + + bool request(cryptonote::core& c, uint64_t amount, size_t mixin, cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp); + +}; diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index fb85169a70..4f4e12ad59 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -155,6 +155,20 @@ int main(int argc, char *argv[]) { if (expected != actual) { goto error; } + } else if (cmd == "underive_public_key") { + key_derivation derivation; + size_t output_index; + public_key derived_key; + bool expected1, actual1; + public_key expected2, actual2; + get(input, derivation, output_index, derived_key, expected1); + if (expected1) { + get(input, expected2); + } + actual1 = underive_public_key(derivation, output_index, derived_key, actual2); + if (expected1 != actual1 || (expected1 && expected2 != actual2)) { + goto error; + } } else if (cmd == "generate_signature") { chash prefix_hash; public_key pub; diff --git a/tests/crypto/tests.txt b/tests/crypto/tests.txt index a02e71ee92..eb8fb8e014 100644 --- a/tests/crypto/tests.txt +++ b/tests/crypto/tests.txt @@ -2291,221 +2291,229 @@ derive_public_key 5ea95a51ab11b80c7d09d0c8f8952b70c67e81d0fd421bbed43ab77c1b7b30 derive_public_key 2c312ef971def53361274c37a90bfde86f959d877a636ea641a9c976ee80c7e3 121 b611ebd2bcfefc81cb772e35e3dd0204575cb0da644f68d4f9828a2683861e6c false derive_public_key 6fa161dd958022caf185faf873dd9adbc5578352cda505e84fff7cc99a8762a7 333934910 c2b56e207862958751d49643f23079009092c32bf82179a1295e3b85a385c1c3 false derive_public_key d85725562544e1984048391413a6112eb221bd217db3baaa9843bee331000e8e 21880446 f3148c3041e634d829e5df463ba3b1d64df282d620d63485e1f10024e003b939 false -derive_secret_key 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 66 5619c62aa4ad787274b1071598b6ecacf4f9dacca2fd11b0c80741b744400500 55297d64b0c0556d5583ce0e30c2024ccce90c93d16bdeb4e40fce7afff87803 -derive_secret_key fea25a8d0184526c85c16c032c7678c7a1e3ace773b31566d159dc8a3cb81ae1 755 265685f284fe213678cad94e337196428237ac55edb5871c1f0209769ba9a803 e83934c766427920055d77755b7205156e1bffc37f68135182f0974fe008470c -derive_secret_key df2c15b6f3ee51445f9097f5488158a8021dd15be1e6dbe676087bda1f2d9760 62075 04a4ca22d78a0e746c9e58e785da9635664cfdccf4b1e87537b359f656dff403 6bad669f91c2df065ee93b446b2db9d3582960ff804096ef76be64febda5450e -derive_secret_key 04fcd66c3c3551d8c9cfe47a2dda3bee9af6690790415f15f3c85fcbeae5eb1a 42055609 de68a85fdadf027981b4acf455a2b112d33f70937f6b4df24234144a5e189704 0fa11e23bc5fcf7fda3ceb2e07ba62adae3c696ab3d315ec51358f9a4267ee01 -derive_secret_key 73579e1e4fd06d78616b1a4f939275a701f0c0b1f7e73d5bcda522c070e50b43 2053410 599f97fd820e35f32d3ffd2507ba2b3617d2de7f7142cceb4a80967bbca9f109 61c8c0bba43f62a70f658bfe7e5af09869ee4b132c4d86b010977bfed6bbf907 -derive_secret_key ed118911b278428ed1a26f8c5ec923a8dfce29a7c0c3bfb353be9f158a5c277a 1896804 d9640af78bbc5dc1f1d3c1bf2e85cb8b67808e40fe49042a92e61fddb1fe720c b7d6fc96e28b99449be056109f0df445422115bd682fdce88a1d0331d44f7208 -derive_secret_key c13b8ba1710c77e7aedbe59c6f2d2160e65056488549d471eca67f1d44918ffe 43 e8058ef0f3bb3bf3a6f15acc04c947de745017c8ed1b5b5280e5dde62d24960d c707dbbb0e4cd0c4863faab771842682eef327d979d497e3f0e7fab9cc2dbb0d -derive_secret_key e4a94a7c8fc7546d2300a00b3079a6f92d27892ecac7dba505953bf2844cfb2d 24046 020e3c34c1a1b208e51ff7248b8c3aec2dec969be1aa1cebf685125c7b7e8d0a 06428933d5d9e51112d00130b799f60b486e3b76cb55ab45de6f380fcb250f09 -derive_secret_key 9d62651ca1bc7946844ec22996b6bbf64893d0ed68463e7936d5e4776cee554f 30361 423d553e1b154208941a35beade2ccff69f06646752b3eb1b83d0b9539827b0f 54bdbb0115432a2d5aca4760823712678fe80d1fe9c926ce15391939434e500c -derive_secret_key 1ca55257ffdb20e93cddecfbf30f43141f70572ce66860b580f4024f40a31cbe 9387777 abd00394451d70415e1c9f71705d169b71406ce8e7fc8aa29a776073693f4208 6ff182e0a4e9ff2dd471c7086f7f9d17cbf139e6f0b72e68746f1969cd4ac90c -derive_secret_key 4f37834b9c2aff4d8eab709cf1ccb8bc874a469da0475bca4505d5ea066dcbc8 50 d38e8123f0d08ba2eaf508c0904b28101d2f46e55740b1ccb0a1aefaa080a102 a4640319c41d047829c9231d603d3f18ee4ece19e950dd7d3d1a4dbc47e6810c -derive_secret_key 5e2cf56e73c5b2b011056660bc8a3ff4c62bac0d6643e87270faf22410504659 25261806 b3b270d07dad67fce47fe15b574bdf6d8caf2695d7536f0ae594a66d55c1790a 6c5d21e56125436886c959fa73b95fd9176639f26643480e531d274273de8007 -derive_secret_key 9c5958c354f35798d19c120cbba1d6128034f2a4d064e7ada247a652445fb916 10731622 b11fe0134adabb72ceb21aef7a1408694905112074eb1d3b3d32fbfc52e99c05 2ab7fff5c38cbc6a74bad41d55d1a67fb44b9313552e07db8f27924fdf615804 -derive_secret_key bb707742202a4eb7178e83dc96b904cd4886348dc69608de24f5baa44826012a 21 613458301d8e5244af8b09f7119f5f81b0d576260b0db8885c78b1a7f2afc201 34754f176801dc4462bd6d782f6c92feea6b1e885e384f61a800eac8b462970b -derive_secret_key 75b29302bd5526e9c3bb053ba3ecd21ef3e149d2d4abf0aeb825e072281343b8 188 9f1ef66ea1944d8329fef9123f65a6322fbbe9523d2e81be7822924ff598380a f4941c7b365447ace6ae804837d4c4373607a7b06903b9209bb126117a79560c -derive_secret_key 64c689c90f928a450d704a41471d56b0d19e96b9fbd318d709ed574ab4964653 491063975 0169a0d809edf69930f063460a56ed74b244c7f7af90bac2149167091ffd1e09 055083b8f78f7a51fbdfb4ceff27b60ff473f50f777ba6a64da63b9df26b960b -derive_secret_key cf487971d4e2bf0436b0dc49e0ac7920ec1e0861165ffe25a8f28fbe35519486 11690663 67bdd3badbd933d5255e658e1cdf5bc1e71a361e1e8048faee6ab2b4c8cd4b0d 90ac0b0a7d9629de252cd1799f9f8f1e59c739698121c24773fe6bce80a17f02 -derive_secret_key ae8c74af310a89480d17486479f1d369e0a56be44b32dee53ba77c451a02acdb 1993 05c0762a267036e29e7e5016ef7589b11abc14b5cce5abe521cecd0b50947308 8a749ef2337c46cf30a57eea0bdf68b6c70c25849c88edc5ed34f2d19d472404 -derive_secret_key 663f212146502b787d877a4c9787acc7f66d38014eb5397f8654289f3b95e40d 27 6b3bf5d4fd4c03129fb771b838e2ada7c9eaed65760d01dcb1c3619d55bd8a04 b36e7a64463fdfa2e3e56b5b225aa3c6651a5b2d0cc31acfb8f902f189524e00 -derive_secret_key f7c762f903e87885bfbbed90f84f8f64c708bafabf79f33f2adc33e62c5885ba 0 1f57e640825942acd833e7f5362da1eca91fa4f5b1acdaed1b62e968e98f500a 35eaf7d9e022fea5972381cd40501647f7048961f4f2d351227da0edfc4a670a -derive_secret_key 7b0a2aacd1781a76a7e057cdd947f0938f91d10a481ea4a0259f7a5d31bec654 893448691 0510223554f92a91cc67c6ae6d7d659c75d3cb3c48c9d55deaeb79d9af85ba0b 68d93428af739ad6d963c0fd8c6854d24eefd16b13952f50ab0457e511e19608 -derive_secret_key 4e70ab479377c3c8700ba453553105ace212b7ae8292be8e247095733a390b8e 87813051 58395bdb7e4b58d4d1707e7856b6535a796dba08d181d22014d9f0b607c99d0d 9456e1647ac37fce2c3ac73d2bcea41fed2dfefa3dd7fcf20922ce0af86a650a -derive_secret_key 466fbf1157905fbfa326bc0d9df5ec735516a62a19054fd807955ef892590bed 29 13debff897ef9476c388ef64b2ce3757cabc98d41f12221913c9f2a373e0210a d2404954049035091f3744875cdc0adc92e67b1befaddc02a3482565b7cc680b -derive_secret_key f822a8288071e8fbd5964ce4755c9e3314d01a934986e2ddf77952614db14458 3728162 e03b1cefd8b140bdd44fb2afb6dff23d9c684d3f15c4b489a92dd9551b8aa502 fb553ab907e14c88007d4b32fa8075c32e896bcb55bd3c624aa4949d09a27102 -derive_secret_key d4734ca92a189dae8dc61836bfa6be8d838e624ce1242155b4200c2b5130bd74 204972 6eae086f3a0fd49102861d260ab2dbd2a7f6e0d18695c12638c4da52842e8401 064c83ebfac71ec945fb6a6b478c1f151faa49d39d750c29cfc72be27a69a808 -derive_secret_key 3faede36e9f844af42c2119ef3d325f1c6dc1359a6a5f2969af1124e9ee05e9f 458 73f8953246e7e4df3b44f0667ca4d131c73654cd4b98a0d71eb2667236769a00 6a263a15f24176f8c56b090d895bb7b5a4c7ecc5e197184cabc04ac1d452520f -derive_secret_key cc1d64027ab703d6d5a8afe5e68ac23e801fdc94309f7bb88d8c533b288943f3 0 cd7889bb3f741345fcc1f09bb08c7ce920d19ce746339d5c963d50026b7a490b 5a8327cb1e6985315576e07fd746b829dc686e6e298569d46b880d8186283f04 -derive_secret_key f7a1d063f494ad003ad1e18b0c14ed428d29fbb929d0889e093c781b1bbf4828 4 385dc517c446cad30604c993b13d871149469cfdf998ce8a428265a127c47702 5d05e45db98a31417a6536e792b75f41ab855b87da3bb37bce36f1d2626bf104 -derive_secret_key 87b95ade027f2a7285677c1779da47fd49414a678a6bd61b4e4321acf4f2b217 13014 2a49a5699c511609c54edaa97fdcaadd64549bcb5d6e711c7a709322b7f62503 360666f1ba8f25d62892384af7a559f9a7bdee2a16f159ea725eb56e2e023705 -derive_secret_key 1dc7c96bbdff1574be5c40de5880546655a12637d162b7327b7f7ed40fdbb16d 1 d3bd3b2cac1efa21c9af02300ae018c1c1045ec1ad55aa392a9261e9ba63d10a cc8e1bd2a5095c6e8dfeb8badf48174223546917b0bd3b75ee83d716a2122503 -derive_secret_key 3494ae5b57abab789992fdd790771ba0bfc2eb54b88828376e582d5015a047bc 1343810 5e931d0ef581e985be1a8cd9f4f6778bcc454c63e09168fd038c4ba4413b0203 4caa7b4493a08f444d2f1e82c45c64633b7b5ef6b4a2c1b3c82156be93cfc30d -derive_secret_key 8d1b33d704b585a1c1721e3c633ee9981881e58fcfd1835ae338aec76b98b82e 70 bf1fc4f944689a14819b97e621ffe73ed46c6e03376a024334823734979db004 e4fc9c1a09259011458efa598d51d0189e91c2e5ca115b30b0695d1af5fde70c -derive_secret_key f72bd8fe05efa8377196977f3381ef20539528c3f308257648b2c583cef47855 187441355 3b86d5c1f1fbf381e63b1ee9bf8b41c9091c8cdb4ee4cb9b518d90102ef6c906 ea72b1f3e853d0ed281d1b119dc0fa155e71b28242d54e237314c44234e5ba0d -derive_secret_key 0c0b90d03dd5e422500d7540475143ae0f03ece418e2fb8efc21ed4a4f5bc184 783 28fa627399989a992464148ed83be2d79ad88569b845e024bae587e39ea3870a d550da0a1169274d86c771485a8872c1c5638538d4cfe2eb4bd9afc8c8324508 -derive_secret_key 2116610f3ebb0b730cd32308e3981c46d29d81800fa0bba216499adf6565bfea 2363 0c7e6a0cb45c3abea314d573a671b3a052c2275b2c990a66808be1273d2cde08 760b29c95cb3a351e1bfd76e5aa08cb29e66eb0eacae5d9df90447bf9d9eed05 -derive_secret_key c410321be072c6700f180f2672c38b8269edb188ad29a87579196652e242c564 744 3f73df9876a9a6139986954e48d1c5e9a8d76fc6385a96a67a8e15bd76529907 ed3ee5477faf56f75ebce759740ae043d267e49b46f7b962055c71664293770f -derive_secret_key 2a97d5181dcc3fdbd8145319e1bff891b3c4434ee81708af6a4e6dccd2aec701 21296 76b083462227d175143c84df722c199b123b3a57bad37cca02fee5f1defc6803 35620f73278cb728fd09eb98fb903a84666dfa6046b0029a5e0704d0bc194e0f -derive_secret_key 040d9c07357308f7f8def27ddb0bda18255dfff5a05333faae9c6b5b1a93b627 2681 e1e5a31aa3c3e108bf29ecf861d341fde8c19c106104e0faf84af795a9085700 8cf0d71ac651ee5b33cbea23a61aa899f5d5f94b8b0ea4e8bdc7e90fb0b10e0b -derive_secret_key 4fb81d88a888c1121dceddddc9cb6d48086953e2284b3c91f3ce18e25a0b3a72 26592686 bed6a3dbd324da12f2ebcc3c569f3a2b4d5586a9f943cd9c03a19853abba6309 f228f9735751a4c27517a95a3e2b2a73e4315b49d37453e1dcdb174612b60505 -derive_secret_key ad35f1fed55bb8770c290ddeaf15d584464702290a978135b29f7ebaa5bf25b1 806472 5dfef53fe2ca9de2ad87865c5c4322792712deb2b9b2de2f3b8fbd7c86fec008 5cfc77a531824a404191e155438acda4db45cf206d13a76e1300c49eb42cf007 -derive_secret_key db5eebda4e2d34297d2cb5c38f7f0bd759f06fbefbd01448fd9d5fbf900a8084 36 be6fa90e5969c0de8c0939ec69444d6f240e391339b427d920b4268fc44c3606 bb8fffe300074f4d89e13280731ed1edac2cc7adc35370bca7b0c18ceabf6407 -derive_secret_key 13140753ff934a699a8397308c8b207f06886c2730ee64f5e3407f96b621b530 1895 b3904221257affeaa23f163c81b5dc1b58602df586ff5f9f43e4142c7e146d01 e43ff3dbdb4927f3b2a50b538da8cfb916283c903fdd39be1430f04d238d3707 -derive_secret_key 695f20d615e5798600b73802d584521ac0189afb406c3f60b20bb743f4ea1b9e 31552 fd9ea801f0dfc910f67912ac915963eef5dc31aaf68d1fe5043784fc6a440508 5b9419f475463ed257ba1d2de0394a8e79ab62e46415a3b4904226ff39478301 -derive_secret_key 18b48028cb1a064ac4271196b298f3cab677a6b13602a5c6748a139e00cdd40a 2255 ff34d6e62f026d5d715e08a4418aee7fbaa1b8e3c08d7d740e81dfc4c404250a 142979916654e40bff815703f3450687a062bc128cd3f582a264d7615a944600 -derive_secret_key 438ccb519550946116dc4ba68d670f46eb65d84086e478a35f350db5735c4b07 374 55faba03fc8be604db066cbb0a5179821339521d1a84bf35651fa329ea36f507 8243ee979c696935a7774aa5402a614f4c1b756560c77c7dd1b4680fbebe0106 -derive_secret_key 98a4710dc316fbb8a76ab2308efe453f8e43be1e7df840004e7467118c9988b4 500 e8663dc916a65fccf71467fb21ff78952226740c833e06e32af0215ca1caaa0e 5fa6cb4ef788555938b88411a21514349b6a12c2fad188c19462b3476d23440e -derive_secret_key 9f2d2522fb0add4962177f96567a8e7685f03d2e266b5c2f33488291078ccf67 222 4de40a704b36b1f659c86cff07f8bca5ef0a9a0f428a3f9054ba3d766dc1e401 91c79a73f947cd5f48f669a68c9a4a7a7e4e0bb25533a67e86591b0793d36405 -derive_secret_key 3f1f7184a40f32f364eb7fe3acfa3dbf2678480566873cdd58cd0a973f3adfa2 17971 337ea5c7ae4ff14851eb0736874370940689911c5c91d46a62535789cfa7bb00 0fe3c65f1afda2fe445f64bdba259dba0de489ea0a4897a423dcd9d6d922610a -derive_secret_key a841f02184b21a2457831722044304ed5f2d85855bb4b9ef880409ad29cbcf57 34192731 e9c21788d0e839e409f9a21f609b5c02c382fda77f185b56af017b7bd5309d06 1500eb623318550625e04a5ebe896dbd4ca88fa1d1677869f18027cbe1a8bd00 -derive_secret_key aacf674706d70c05e0105a99a5a6978db2808cc97ac67212dd5399de5eb50671 3 986fea1092254d08ab56e83951e0e608ad9511ace7ff8e1aa9da1ac6e5f47c0d c99f711bbfa0b7303f9fb4ebb28ccb8a7e17c07e766ec994ba71461ebd549e02 -derive_secret_key 2724c233bbddf2b2289cd5d5a8f4d788ee5b836c7668327b4f5e71a490b23db4 3 867670c91684adb4c71a067d3e60bea19863530f18c7421c03c08717d0e7f702 68b3cad47026d214d07825a0cfc4a2a7a15fc773d58d962a5d10089cdce2280b -derive_secret_key 035a428055ada1abdd7e28d4d38d64abac169a37d7835dd260fada61c7e3d51e 1599700 8c28fb3157e21c046d60521dfb7c1839e48b7499b64ab80b3ca439a9c7eaf300 c26d68bc26aa732bc2e126fccbe927bd7f92b96fd49d0f2de02bc1e4e6b55e08 -derive_secret_key 0aaf8b67ed99040fa384a0dd544b327cd7fe9f3ecb7853a22ead9a54a9be6830 14797185 0ce65680b9a280751ffe5a85bba1c64856b939c66439faf908d71a7500322b05 eefaa4e3f44c6ba3d7bf81557fa81349573c067155c99c92e49f3938df1c240e -derive_secret_key dace393f1080e97eeb44a4a93a489cdf7bd652f009faa4a2e47dbf78d5502ab2 978686464 f6f95534d934fa3a8626ca8f2b339274477d167ff7a4a0f25c88846559ede10e 43f77007f354e76bb9a9d2e13ced7cd1ff1e42d32ec5f37471d63f010aefdc07 -derive_secret_key 2b30fa5e506b86bdc7e41125ef1fbd84352d76ef3e58015cc773bf7332de1991 0 cca53c64e9c5591c9cc5afc6513855c879d533c2c0d536af8e6c24b1b1f0dc0f 79da0ccd588161ce2abd93fd4cb17ba4e49ebc21aa48b536d8ea93e52b053204 -derive_secret_key b301c9b23ab883089c465e5e4ad096461e485850d90e87a5b1c75f089a464a2d 74209 c6b6c3e21818a12d7276995f223de8aefa16afb2c1f53833ae61f954a3daad06 1213aeca73f8ea63bd9a2e45cef0a85ddc4fa379f61572178d23b57dabf69d03 -derive_secret_key 4916fe07b030505cbe741973edc23e2ff2a2466ceccaee8a668818ea9c88c969 7112712 db13650830e6bfc0b6e6b6288eb660e2a1f0fdc7b7e6056e3daa63f185f1ac0b 586419b0d1e1d24757d63fd44e6d1d71e1cb0307df0f7d905a7af7a16642a80a -derive_secret_key 5d65b50504fdbf74a499b5bfcafe8360e38c46ae0c234cd04585847cee899362 49539803 86426073f3e87fb2385d179c772e4a82d650bccded32ac1c4258a6ad5063df0b 08d348b02d786e3adc05afd813afb3e71aef2845b88e0c8d22ec0dcce4acd902 -derive_secret_key 9f6f9aadbe4b7364190b3ea764f785c8ca2ec33fd7480c74872299af22998aae 2433898 9aa11a7dfce88ccf578ccad1d5d9c25bfaa1e9c1222271e8f7d9ddd7013c540b 38e6ce353f8fec8f12819f29b93d7a4ac5f5e99527dd608c74cfd13ed7f56b07 -derive_secret_key 4c303ff19317002fa202fe579ebcf790d6042092f748876f7a365239e1e52a80 0 a6efcd692853fc43ead8c82c17a4f9598652dba23568274ccffaea6e737bff00 02d7bcc95eafd1b14a9f57503b19944edd70c9b43fee6a1f71da3054a616630a -derive_secret_key 6aa64af464e21cb77b0bb644ccd6b66c9c6519790a8a6339f1a3fc05ca1d3770 3 07ea6b523a83ee0d3766155f0f565a4668c5cc65c8722bc60fe7742255e73b02 2e9d8e19b753736f5dfa71b643a6688f1b636f0a8bdad3efb6ca2df52c5eae06 -derive_secret_key 56ee4d4350072ebf3637430daf901e0e99d0347c8f39c776d7b404249a857fbf 1034283032 e68de07a27393d2bc19c60f95c9ccbaecfdefd0d0c44d506126096a92705010f 503af66e8a440d6e7b4a1aefcb84ce00417d708f5893cb5aaa775021ddc7a40f -derive_secret_key 7b3559c73ae51b256aa76c23ebec239bfb8ccd03a31610a7618eaddab1ef3ca5 0 4b5fa5df913a8979c249bc538d84dd875557e4611b5d76388fd0451c88e5ab05 87cf71f8ac9c08b6926c76a8c71cc36c456b6fbcda02f0fad9d95b0865303a0d -derive_secret_key 9f71e579044ab493ff0301c80411dff04337024318bf19af8f30c55fa826efad 44607553 8abda1a925d0035dd16398c87c9e5c7027e13b787fc2d5f8dc248363256e5008 1ed7b1e498ab6b201a8c25ac680d667939b60448e79340fd6ab269b6f82d360e -derive_secret_key d391054b248a0f524e3676729d0a4c255efaa5800e9179818c66259472b2c602 25240 19f255cb3af8db1de9227dc0f12109390a676535b282b1a4b07b0d8bfb61db05 518d9dccb7601f6d662fd7a7ad1a62e8ea0e8a0aefc8b8a3b7fe86c1b0ed7102 -derive_secret_key 509a4d7821e347f14981b6dc30b992746eb32f81ef0a12b2893211bb0d0a6f1a 382699 dea7f9f6613438c884acab71396e3a36cf4a0518ed0c7492b9c5357a64f6eb04 b9d737beab4b9dfb206001775390f4429d55da38ddbaf60630c044c466f1cc07 -derive_secret_key 9a11459b5b20f8e19a65f1b3a405080601aa4e8f38f15be7bd04d49d008b7779 605905 5ce92e3ab14df668336d216a76a24e3376bc0301b6454577169a7654ba56b106 3259dfa4d27a00aba238806299c60dbd0fc46974cab51e2534f10a684856a50b -derive_secret_key a118293834f4dd23564a6cfd8e03652f25513e3755f530db48df18f7b2b598c8 38088925 2c35d46223c6325bc232edc79d3d0bde56c7f9fe6a6934006f409ea1bf8db608 c8d4cbd65907d5bc1104dab2a61250dec12509fa218c0bc2e363026111efa200 -derive_secret_key 14addabde641bc75e5702c3e533c9defc46e0814158309922ed3d254b54bafd0 372637353 769938138b788670c038c39a789cf4ae01de05041de8dac6b8f6e20f145c5704 c061f89689689f8f709d8629cb1ac2aa5df0a5eaa2b3c137c3a7a06a47bd650c -derive_secret_key f0e04f48a64c141ec983c1b92a69ee8b181763ca5bd92ca06ed1973ca7fe0099 123877 99d452b9ed46aef84037e91d04c639145a0ef513464401a98757b82ea4fe1704 20818ff54ce09a01c60a85617f2d6c27f14117f79a7656e763de77e038b94006 -derive_secret_key c37fbbbc5e45bee29d31841d7e838d285f0ee86e686bf054fa035da90a8de0f3 11095 329897dbf40b0adfc2d6d7c30c3e62f6eda0dad311f18c01482cbdfb3e8ba30a da6ba063ecf81c574ce54509022b3f012f31090d37da1e1b553e79f54d276207 -derive_secret_key ba73e8d07ea30be78bbe3fefcc40f37b746fca7006ba99b3472d5dc9c6951819 0 19b19392bc32130f1e7def49454f2a5e8aca64e6e18e21d149f29af16db1ab02 5ee13322db943d084c2237b94295d19c8813ba097450f77da2cc00703711d30e -derive_secret_key 4a35a89b3dd409ce0cb7fc53b2484953cfe04fb40d0a1f9c9129ecff5f364373 46246322 fba0b8fcd3ddfd5a62eaa9dce1e3b74594db686e0f420be3070b829bb631a409 4c4414daa95c6935b785010c0dc0d0b76eeb18ca636359e73e4c251f1254e206 -derive_secret_key caff5e44e4b6285e39e860c4afed0786c7781f7ab32cdf9e0beeef70dc3dac80 11434230 8057fac8d5b9c66bc9cf390e783941ef02c0a3464aebf2d677fc4a61ddad4402 e11d28560f66b467f3b436cf0ab0886bdbd827a03b41752d66603cb4aa376a0c -derive_secret_key fbd5dea27f019304995af878363cd6f1dbf864a4f530c4af3b826264ccaac3d7 70132260 37d2ffc72417a62d5b521f22969d723ec7bfe5e0ad147fce8d9c87c3906fb30b 7a962c434dad167a553fd9cea06d826969c9cc9960be5c865469a11e8df1ef00 -derive_secret_key 1dbad3792e84d3904646ba39181a6604f442e6c3f54cddaeca5db85a8428e2a9 67765551 fb93fecaa4291e2055490ae5f47a2626c7296284d4730dd5da12ca3dfc290301 103541312be4057652dfa16f8dbda65b5fd7a21fbbf4939befc79db668c1ef09 -derive_secret_key 8875ad1eb19f1ae7e2c1896d95d56bd82ad2afadef88d5bdb7e6401febe31268 9583 c79c19a73a96d910cb01bfa364f9d60da0fbec268adaf895b3628aa5766db507 9453c9ee74e71934edb221a94ba05d9fc9c1d449f2530bf3ceb161b14739290d -derive_secret_key 5ecae1dc27f7cbfb963314c0232db121154705018e9f77eed5428105b6218234 25069 b8c7bb2560045fea6d75b2eb0ebdab910f03a2f974b12458ccbf8bb3c41ea10f c20f0286f937219d26a02eb58091506d99a706e9521ee4afc23e3dcdd61a4204 -derive_secret_key 75883f73434b7435543b3a4dfbbc550c63e64c0c4c9a834c6082dc89cb19aaf3 681 9031381b273f9280b8e69865d766855b2a6345e9d7c0e18992882b301caff80c 01e9aab52568faf879955151b022d11f35442e7dbcfcd4f173cdbd5bedeb5300 -derive_secret_key 7270a51592cf98d252f73c10b6c624a49ef819afe9c7b2cfddc2aa87202f7054 205129 2ac73de61fb3f0cf2e4cf1168fbdb182e866b1384e21823b02d13dc7e4848503 96c742b9309b91aeeeb03c093659dcf1fdbeb538b6247f0f741631ec75a6850c -derive_secret_key 9891198266f968256a6583ed30c8101f63ef3a6b8d1447b82daf6d1e72b08250 6573 1036b648cd81d1f997756ea166a3d10044332bbf6b0d3e43ae1aa2913e3cdf06 5db242d019a350015128cacbd73d8d58f0385363afae21b4a3b8d9d72d76340e -derive_secret_key 57f8c6b2ec27cfbc705812def2d1bb1f8375ef6b5622b71777ca8399d929a432 12 a09cfd081dbfd7f6f6e36f86476b785d8c40eb4f120ed49cf5b486aedbb52507 3bcc80b99ffd9117b97f72fa9aab8015cdbb2f45ba4cf7c443e0b61e0b3c500e -derive_secret_key 504249986363fc1267095df37f7d9112d87bef56a9c5362a4fce5825e0302777 41052246 0e0290f1c7f937840e53eab3c68d8eb03949f0d98b566e1268c52bf1a7f90b07 e102d9b539344d784abb2a8047c6ca6fef9134325a5d8a812b6bd869aa397900 -derive_secret_key c68c10078d886d8f74dacc24611f8519ab077409efe20a2aeb09e99c5080173d 13547 d2ed75a871d9afc215cf5c538938b6de0694f764b7606b1e8b565832d697f106 be39a03fb1a2490f4ad5e236f0b77c667f5bc12e94185aa47ff6b05a5ac2d302 -derive_secret_key d21c4455334f6e74c9fd398292387195482d2c01393a9f74682209c00e573c45 27416980 df62d785e9069b4620f8749375979616411aea7fcf36957b661cf85103dba309 423569dea958d83d6a3f85a0a785a40c9c0291d1a36f8dbb41d5e894689c5a0d -derive_secret_key 21959238c6fc2d5ce951aaa308847010bd8cc66210f20ce244e25a8b5ebca5af 226463581 d30e52e5e7c5d9e1a7b12da0f01e4f44e3a080a62150e4a030d4ea653859fb0d 1321cef82fae3c7b6df0a46aee34412697916d3baaa4ec49ba7208c7aa67a907 -derive_secret_key 4310cc6d0db9213655faa72f98fcef718e0faba76ef36cf0dcebde1004284a09 421444140 821c71d929ce0d2c6d7d8475f972bef6753e39db49c5f4b497a52b42f7929b0a 03b61063331303698a7b1bcfffe18bb8c94f9f9c90a2f1649db55712ef721f0a -derive_secret_key aab75b0fec457f28c46a4df5024e48157531bc8105d6a29eafe3e241d4968932 235684 7c3d53c69b95c735b7cfb918ca4963cbb50eaed8d9640550b4a653548f67650e a158fc4e6fefa4d28745b014985cd1f09a52dec6d36e6e7c3aada6f60d85cd03 -derive_secret_key 302f2de2394d17be18ef90f5a9dc363ad48f72c216aebd73d6420e7344b3491a 1308 95f4193b873a844cb2c2f1f3278bc2faef256afa43584f86e94d20cd24361109 6c5a08f32bebe891517b3fb2fda33caed68f3c201591acc95d940e82c7349a06 -derive_secret_key d6fdbab05daf3bce40e48fc237dc829c094f37f6d9d156ea48a282913be5df3a 255696 446e1a00c085d65cef24416904207ccdb170b47c160320a6fd0af6a5cb1bef0d 8f6ea1663373d112c4946c3dc4f9589ca7f8753d7720198213f1bd98b76c2906 -derive_secret_key 1ae1d19560fac6a318a2612e406763e7ce4249526b5e19bfe0abbdb1d89bd201 51972 205c18e14cc4eab6762177ec71e665aa8c5945a5bb8913f92eb6479e56c8bc03 5240166c2e14073d39737619d8ee949cc0eb72302da002aa94a390ae8d70b203 -derive_secret_key ebf4af354e6380f6e218b32fefed598411866162f22f5fe72fbad8b38f3ee674 13257786 d428802dc6fe698a31cfcc35befc0a434e7b032ed6e9c4c00fd29b769703e605 29af2c65aec91c88b09bf0245e0531d99b58faf9c8f5937c0517a7becb18670c -derive_secret_key e1c56a524b698234695976298e90ca414d633db51cb75c6d10054a22244c8b7e 4791 b63714d9d96b3ff9c468921b7d7a1e4f7853a386a4196ac2a57a74f904b46406 4b22d0dce1d109c7b54687bee941ba20077f41529f08d5bb7c56617bf3504205 -derive_secret_key e472599451ad37cec21e0f4e8ee00c58648c872e8ec828b6a653d5ecb408fd14 1 94a797866b99d0b74c5b5fe6e865984007770fde86f6cccc7212b525dbaea80d a7d65d1a1ae9bc9f0d73aa05f224d8951261f7d83e3ae9a952a0e05e2d13100c -derive_secret_key 3888620a82b463587ada366b50e30aa27ea4c5f50a581672bbcaabc34ac314c8 3218185 8b6078e315d9c023a8d230119244bdab4f31f7715748cf2f00959e50a7fe1b00 d34b832b6df66ea39ebcbb1004de4a0470bb8bad3fb57d3856f6053d3a897e0a -derive_secret_key 08931015d7e8144eff587aa93a9cdbfef5ff73cacb8dd25bae609bd8b0a8b842 14481 85c4544e75852b4e2a0579a57aa08f7a24250f7ed75b571008a3eb4290c0660f 2e8cadc27335706997955602a5773131a07f74200ea3a1a7b908fd03c160810f -derive_secret_key 52cb514ddda46a63610185d906c9a3bee730f12dd76b2ddc5ea7298b260299ea 18 d7fe59df24040d47b9a11680a7e4499319ee9cad6e089c8b1031280d0f302207 e13c7ec3abb6b24b9d423ff25a116f39cf1342e5c2a7d4639d4d2a43a8e84d04 -derive_secret_key 9e68f3f173c4eb86c216c023f5e404643d78e695026ccb48a1709ef7f8b356e4 756 75be6ef2f301dcdf188e45167d0c6470abe408a1651e7e8d4a07b7dfa1eace07 6d306ad9c43913ddf1e64f61f75e582573609654fcd7f7ffbecaa59fab332b0a -derive_secret_key 9d9f16c31e49463b712bd15ad4a53adc5e261fe3b0b21c229455c5d11044c781 11036962 5eaf9f0fba3d39afa3e6dbd3bc7e9b7f3ea017e93f36f60def67a27c9261d202 1bb9d4ed4997ef939210bf125b57f0132ac3822cacc51af7408fd2b6c8322905 -derive_secret_key 6438a0ebf2930fa290a4dd656375ddf7339f4540d4a44f312e1a634d7abe5ded 3007 eb7ef26449e74205863c88ead838d8c9a3f80fd0d782cacb4e5e87070309f900 f7e40167a1f2fda0e78b48aa494f2d460cca0044d2dbd5134f62ceab40e34204 -derive_secret_key 1c565a8f73d66a4c5134bfbb761dc5656d85b92005909e56bb3bc2dfbd2ed7af 1057212 91d30cbb3d9123d5ce68558d0359d99ff039aba7a29f4148f9595334f0b00f05 6749bb7c3fa2b8e571d9b5ab7a0ac4609a49bd5b9b08bd8233796025a4f40a09 -derive_secret_key ae6245000cd9700859cee95ff800f11350cd2090e86359c168ee78819d98aec3 33993145 7869f6d056981b095085211afa4c8ae7e6053d2fd2d90c75eaa5574cbc3bab0f bf6f687f821c39bfdcf6941e015f548dd831ea725b675cd77e25e6879c7d730f -derive_secret_key 64b9d15226d032bb8b9b887d60496597ae4d5433299a8a9f0ed702120eb3902d 1 d00c7913e685edf9c50c0b90ee64b22fd1da1da746faf6b4e5daecd6c1767b0d 8df41126c700c2a840e9dc01404f6a5379417614dea5634ca5b3bc86e76bf40f -derive_secret_key 1983fb00ec3cc628472ec8d91b99bed137e4a98f33390cf9d460185e682d988d 5188 b416236242e5ae69e2920d14d6a1baabfb80c0d53b95d33f7f24747ea3fe3902 225501aff1117ffdfa49aa5ce966ffcaf1918f912dd25f8bb6d164266135b500 -derive_secret_key 95e5d133b41551fe5615f5b690baa6935d3374d040ae57c2e4e5896239e125b0 23 25640598faa20c9e1bb201ba776ae252b3227d9b8efc682d84ad028e79055f0f 61a80d245ae3e4fbea21402d9876ad97116ec49cfba4b97fe442fd7fdf882e05 -derive_secret_key e5360736d6233700439ac27fcda902b6b3071886c9fe195ab55acdbf528dc50f 6150 613d73dbc6298d59c970ff2c2542020fee62b336ff5e6bd6aff713b16f0deb04 b366f64450a1f8c82aea16e94617576b61cae423361789573972a324d529c405 -derive_secret_key 83725aaebfd246f1ba04030e5f50af5a979946df6f6b873a9494cb80ec7a23b8 31922 12334c8f649db9229efa02913750d4b7565d500b4325872816e0a0a72e2d7c08 76a84d0de31ec91b2882cd0dcdfc35f751e3440d9c77a47d4f80350a3071bf08 -derive_secret_key ea9ec7488b7a8db8c76cb21b246739136225546b437ae454a5b66a907c0ea86b 15877373 e2ad6b8513e3fe69541b3ab208e71d79309de63fee29f970b53e4b6799212309 d8f5f77aea03fd1d5091024c6c4a182153114397229dfda23f58022a1c22dd0d -derive_secret_key a0d1fdbf4a1c774fbd95e5df13b83564efa45dfa7ad5cdbbdbfd28c2737fc345 99 a7d60ca5b812e995998c51847b416e16462e48297925957a795b6d4388cd090e aee51964eafd09f13bb668bac5b99a23964d13673ad0427de1491e1e9fb57c0c -derive_secret_key 5c4d9311da5d627ce2f3564641907dc45fb967255ee57405df0ecb3065823f89 2 6eb3a15978ef3a6477a19dc72fad772d5304400fdc4ef09f0951c41d40e57108 ef1abfca8cad1557c55fc74b52895b72a928abcd5ee7416aab63a6bb6c569f0a -derive_secret_key e99227148be2ebfda334232633148f2d1f222ee7c990c2d5e0dffe2281e352db 858 f2a4776abb56939ac587d8357cb7ffffaa07d96c1eb623a7e08ebba298879a03 cd5ab0e1cd863ebe1caabc4082baa741d79f8c28a8933f86eb622c8936ed1700 -derive_secret_key e6d85728dcbe8e18ca5f98fc57851ce38f95d6c52a0a3ee17b52ce5505262394 100 ccfdbb4c86d57f9aea1c52284d17cc286c196f0f93cb08931dedb4a10fc4ce0a 5d0aa23e053c249a0569b543cd192f4af11e925fd7eea84f36ec8ea9465cbf0e -derive_secret_key 9eec934e5b4d74db6b0694465d972a9effe8ea9a199c1c9be13be76b80a3f092 1 82c0e19ccb8ffa2c998a1a37ce929f13af0200ba21582d3b7eef3759887ef10d 82228a6ff30a9007c88802d74f7ed26458e64a19fc30f730b9aef2addbe79304 -derive_secret_key 0f43c1efaf302a47a664dc8453f88375acf082b26b3e52145b05b3794b738cf1 191 063a3a6064ce5160fe3ad65fc740886a251a6085ac7ae30ca850dd178e380e0c 53bdfcc7e0bec08def70ffc41c9f5dd363c834785b4091d6212793c5339d8605 -derive_secret_key 5c342d02342f62998e7e8646bb15ed193a0a129086ee56d675aa45780a86f00e 174 d3c98ad7c9768fae843a1e56b06bced14bd47fde6ab6e63fd20b7763c2a4b80e 3c8483ff4aa7fb9be8b2c06cd1ace6512bea9284c27ee89a772b8896ba0dbb07 -derive_secret_key 2ee448befb8073111b822cec330f8e4ceba4f9932ca1334105c0d39fc3ca2e7c 257 fe40b1cd43c754d421c15ee688be94130498323c03f8a6fc1f4028a026d40f05 c4d147e9471f1cd7c7ecf3c898a2bd673817e5aacc0a43326f41d29d354f0e00 -derive_secret_key 8154637273ee4d724444d5bed75ae0d5d6510e6cd4d6049ed52ea72942ea09cb 654751 fcecd8a37cc86bbbb8f5e59995de110cf2bf63f8ce83bf82197b0d23a0aa7a04 538f6c8d7995e7a30ef4f3c3e8177c82ec8715d74215c3e75ef5a631add0290a -derive_secret_key 3614a3a00b0126671d4131f460f81be0ffe9560220400a82fa3771eb89478e1c 377 ab854c3cdf3a8bc97df696bb62a382aa8e94fb12666273f0a0e85e3987c31a0a a10ccc6625e3db60bdad66c7bd3707a308b849b19dc2066610927d005f8db50d -derive_secret_key 20841a2d83df80666d52b2bfd7c8b91528cdd7a3013639b673c7a6c5eba0b20f 29567 6158bbba9b8227654bca316bf2bc18d13b5f27cc2779927b08ad3ffa3cdc1808 942d2840393580979a26e7d787b354ed5810d5f07615e984dc01468fdcbcdd0c -derive_secret_key 29cb7dcb017030f2b5deaab21b2e276bf12599014d2989c174140705643f0fc9 234647 75d34b8640e6a9eb478d0c3a57cd6cfcd9ad5c1d13c19d99c0d9d20f41139200 11c9ba77326466ea88f5533c57e3678d42141c2300ce35db2aedeb56862b3c04 -derive_secret_key 87081ac4eb79c97439d23e7afe327b955d67d9ed9bd101cd2cc15b1e99fc91a3 15232 54ce5f3b6c498e78e610efa7f684985c2073ad83d8c0f76c924873df81bd4002 a3959707f8b8bb9a8b831c3b8cec045624e9eea0891211a66f2108c194561006 -derive_secret_key 0307cc704d6cecdfe652f7a4f23132278d6cc10c27a553b2c325d3d66527eb99 9202 7aca51abce812ff9737258264f7ef2c1a2eabdc78524df1b79a28dd4d1c1f505 83567397a8451df4cd2a5cd53b038f7b188682e0a8763af87366e308773d7d03 -derive_secret_key 4fb4c853a6bcb2cb66abbcf961d4df4aa0c07fef9d7d8e648c12733793b850c4 11 b2f70782f7bac1daf71a2caf35708943776919de0cbc5c7ea8ec755b27344506 817fe81b169820a11c13937db36c1def72d6b395e01a3d97197b99e9c8e1f303 -derive_secret_key 2a7f1a0dbfa0b3b794a678d586a66c25e14b6f95e5b8d3068c7792add1961d46 109 ce57c2da6e12f032105b4113cb9ccbd5c1484868e74dafd7ad0b021195986e0b 2b3f2f5ec228c7800668f8a1e23b3ece41dfa471a0298ee03fb5784c6d718b0d -derive_secret_key 1164c712d5db8ad0fe44ff3ce402fb6b18e3e3942db125392e323714a0a8365c 106 ff3faa4967abd1bb54f83510c7de724f6fbb6f23b7efe72b2b32ff70003a1509 1c54368cf55c62f8b5180aecdcccc9aab86937a9290ac5a795513b4c29f2970d -derive_secret_key 3587d416bd1f37aa7e75d8ef7a078f5f3f5ca089eae7aab01ef013ddbb8f5e46 2840 a8c3c079034779aaf359bfaa6246428233046ab713bccae08a2a7500823a3c05 dcd3ccc5632ecde722d6084a353812cbd2a7c3ff534ff54a2196cc94db5fac0f -derive_secret_key 9e3170f6b808a5a959cfef85fba66fd752c5b1d6e8e6ae11c5027650bd7d9cdc 13 4cbdda6ad1883bc280016d7dd0bc04c3d6aa565c2e71dbaa6061fe56297db10d bc3f879a4d9bcd47a5106b8660df66f8ca83fc490f935192a1802c9aabf43e0e -derive_secret_key b5ba1cd4f1b6ff5fe0412f35fefc6e765135e3c0f722370024e2d9a6bc0759a5 11498 61966fd16192d59b1abf27cf4f39f4fc2a24f2bb093b591bd39e7da4eecd7f06 c1d4b6da11ffe929a7f51cfa46497fc1b1533dcd60740bed37e8f60fe68a870b -derive_secret_key ef3085c8ff7e9ddbd062fe94f09fe2e44cbb64467f2efb595364a79a023e7b2c 1628 179cab1ceae5934ab56bdd8480a3a57d553cf199aa9bd18774f202f8e1f5d50c 5bbbbb8f8d396b25bdd55e2823ff2d5cc6c28d6c89b40909f0eb1375b7693605 -derive_secret_key 14ea888e65a172d87d0ca1ea3a28497f36643cadf7ef6f826a229010b592039c 27744371 2ae8330bb871ebc17a0ff80abfd04c4a4e036e36e72055beaa754dde50787004 7d0c659c4d9e2ee1f3e066950e9e0946c1493994681da65a82aa3a1dd0236105 -derive_secret_key fdac08f951bff89aee7d2cd631d6134e431583834b8008b8f41ecedbf9b48bd8 859 7bd5e8960a174ae6a2870e0ca6f29c482e12a0553f16d78f520b6f6574861c0a 39ac610a94da03355d32798f853dd298cb6a1023b114aa7f486b6a026f2a7603 -derive_secret_key 6ec75b621f87637eccdc37f977c7582495a3e350bf702cc8e1b8dc70466a000b 2010 00422014e6169e553dc2aa9ce61a84edd658aca18482f7fc53e339604899c409 868a841f8f5cd6d0362aceefb3d437aeed6f854c85608c87025e08c1e4b34404 -derive_secret_key d5c37da6e8afec6d26063cf4426350f7b085eb7c3effac51fcf85f2e49c2186c 58001999 ac67abd7f4ed50e6e73926936f3b9f5942132be6f027d93a4e91c54a2b232b03 176da8abef858a8c6688f9ff9e7ec48e21b874628d2d993a70efccca839d1a0d -derive_secret_key fb14cddb5ba7ef56a53bb0177fe857e85056a8783fe4099c32285c071290a24d 606 160250ddcf0796f02eb3f43a42b9d5686d5d2fac2b5b7fc7c55536432d9d1d00 4d19cfc6bf62c74601d49aec9745b9850d12cafcb4c031758ca8cba342e89507 -derive_secret_key 4756c9b63c4779537dae5eecfe1c0a5b4b70c638048ff9f0c897f6e2641fe6f4 31039275 f1c94d89524dd7cf67fe3ebe809c8787e3c4ade0da6a6e08616685b2c335790a f2455efaee4a97e28caba0012c62e826e309ae85f9de51c174407cfa483e7200 -derive_secret_key f0928a8e68c23ff264ef029bd6dbd2734a45a0555d011059ac30ddb55b7e32f5 29 5675b446ae1e09a276a9ab542ae970af092f08480ff0a1abac243a8fec29bc03 2140a4f4eb433c3814295fcdcea544bbf84d639d10f5e27c1ba1d4de9e56b205 -derive_secret_key 6bb1d17879ceaf9217058081abe574cfe8da00ded1b755ee4cb16f232c9f6b74 2594 3b6f7cd68f5ecb7054f8e4549ed93a0fe42300915a43fc0ed762e5ca84aab705 20d3c954abb4e384233160976a473a96f65fac555c9bfee39067781aab443805 -derive_secret_key 8da44d1c1415e0b22a8cecf9d9d8a648e1a6827d299294bdca939881f397baa2 148312995 76d59f901950fa438b7e502a946d9c933ab65204df3b6ee3b159a9b3f727da0f d6967b1851c0de164803a83a8cfdd55cea1c83d18a776c00bf38ef972b238700 -derive_secret_key 14a2403a907afc490e1d4e0846119010428b9b3c9035395ba351d9e0e954a825 67194 2ae97465c5f9f14d6b4f52444b140f45d078b051ef935717564e8aebe46b3f0c 325e6eee33bbcbc1a75ea2ba28e26c82ccb14d377af737801680b17de479420c -derive_secret_key d38b0fc241636d5f198c0a9addbc9c63b7f45dd1fec17ecaa8ead0d204c3a39f 2827 989d6e0c7ef414f4b11faa416c0e5e20c7afae519daaf9dece5882316608660a 81d4f43c4fd8402ceeeac09e1b927652cdf810488650db9932da04cfad698909 -derive_secret_key 71b2737f006a9b4ed600326a12750394901d968352b8e233bbfc25fd20599222 382081 38a39a9ad935285beef3e145db85e1517b1fcd34a9e1ae80866886199b6d1d05 8ebb1f017ec00a3281b27a28c2afc401caccdadadebfc056664627c809c9a106 -derive_secret_key 332febf041d1db3b422298a56e50ed8fec636b42460e07e3f0d3e492f286066c 1112591730 9797ab098015dde707b468a38ba01c82f3245ac7c5acc24caeb6fc04dbc63004 98e181e10c7a94cd1d1cb815fdd80f8603819e610befe89210888595a94c9606 -derive_secret_key 45c738734841a73609f6c18770fa99a766376fc75a720996eb901d205be589af 1641286675 cac9abfac3a4c26dea454deab89e609d2f99adfb1c55e1bb668ebea611fce90e a22bb27e6d39354b5dce33b8bf8af717c2ce11ecb8c233be4abc0e738c0dbe0b -derive_secret_key 1f7443d6453cb01d65710e5c29fb408f8f16e342170ebf3c564471a7211aad32 1249448 8a7d9dc5d8f4b5bb070bacde5bf1bd74fd6f4fd5d11f6086d99ad0064ed8dd03 2a5cf911cb12c840bd8c7f7d5be053995877914b8473fc10bdf72ff41ab1c202 -derive_secret_key c0c8626f416ff32f4a11e8cf98282bc5f0623d25ac100885eb8c3e49ad0fed43 29361056 039257d76b72e96e18063d5f13b7f72ff9a5971f27102237a2f66a5c5844810b 8440745187799413e77c0830db746c8e2b77c76add6775e94687381bfb737401 -derive_secret_key 7905cb46bee84365799f97a6b274c5ac1ef679cf829d54d53871db2379cc24df 5614 7466b387d218b0f4eef29b3d32a4522add103a99d889e43d144a0a7531c96204 161c983afc6b7af137b7c6448ef28145ec21d8475a826c466be0fc9ebad7a002 -derive_secret_key 198730c73e485c3194cc2ef1bb51437fb75a031b899d980ce72467ab7e75855c 124047 9e69a9892f50717b8a11bf3f674ee29aef664520f04ad6894d3da1470faf9704 6296da9faca3737b0a2116e141c7cc2c6c25b71fd3498c9bdabd399e4adc5001 -derive_secret_key 794cebdd017cd0f5662d5461b492d1b6bfd24a84cacb823c7f1ee7b2ea62787c 60731075 c9154c56761a16eff14fc9ee404f1e356827b5e6e1e839a23c7f4bd759608b01 cc4d2a3e411c9bbf2b883fc631ef0a0155aedabebb250f1b60d070b4774dd50f -derive_secret_key 1986509c9af8469928bf4c0998be207faa728a58f558f4e58472ae311ccb746f 17556461 b23e146d6ab19b152f28013b86c473e76e8c3f9da7253a442bfc63a106ddd70b bfe77a0e41dc7a476bb776b782befa30b0897e22a11bf8f421ca7adae6cb8407 -derive_secret_key ab05abdd70b412ca2655a6e66afb090dbb0f88fcb29a5222e299dbe87a727e48 29015599 c3aa8d7a23c05058231a4ac5a9bf91129775ed75a7f9315171c7a99a48ac6201 3085643d28854bc39ac11853931b6d7d63708891b7648f7b70e4a9539cbeec0c -derive_secret_key 16326c0760a1a8fbd2feb5ce8218b011871e75c45f6eaa2e21ccc6d4ca378ec6 722601 331e71e4334aeaf99d4ce38371b1ba167121969fb60bf189782e4fb4e87f3908 11ecdc63032b1ac4a013219205437e487fc9a770137389597eac140305174d0e -derive_secret_key 15d3d876b90b230ee360e7ae3c1aed3e142cb5b55e5f5a7cbbbbbcf070c616a7 22720 cb453fc5b59c2701174ff8e720ad14f347c53777871c2b1999474ad74038ef04 d862c17b9f4e175b58fe53bf0ad57cbafdaf71926558238610a806851bec6306 -derive_secret_key 8428e52ce38b48f0a2ad2b34b6c0b2c6b36aaaf18873741c83e985f9e06a446e 478 e5d24c080792b73ee47eacee4bb734b0331275a8bc35170eebf463790d3e410e 156383809b178badb4dc94ad3ad0d95ada61af178f1fc6d6e74738622ea03502 -derive_secret_key c505505a0dfe7abaf8adfaf94214b117cc621488e5a0db972bbbc32ef3fa7dcb 0 c06a635f95d7576210535489cec2cc509c5b90439234ce3c1c5289b2a6a97502 c193e198664c6cec0bcfc6333dd19632e22a77b681ee0c3a548ed4d43dcaa506 -derive_secret_key cd6f0d46e8b0019397d5b406171edd1f249578909c04748bb9895ace6fe68755 978776904 c17194748fe08cf92b8f4ab42ae3490a4007f5462db7dde7420f36e5db346307 f45e7366711db4537b56ab6f5a07dbec964f1526a89504d22e47a1571252a10f -derive_secret_key 5979492130d554de66a4e377116be6813d1beb6f5e534ceb86908ffdc2660929 30 29590db2f45523959dd85eb1de5b8d63e3972a6270fa8a024515a6cf4110980a b28c959962c2ac45665e50d02805527417a99573ca2fcf71e9fa5328c4640d06 -derive_secret_key 838c825cdef495d93898af2a41bac50afca56848a1e8a5574713776bc90b78cf 190 d8bfeadb37cf0be64fb8117eb394dddad1e0dfb3e0d149e657e3230cf2367f06 757b0815911e25bc80cdbe6733f4d7258eba954da799c21a517fa8db9f77dc09 -derive_secret_key 48f967652e827ab54320bada68369a465c3a35190b73f8fddd2c2f6bacaf03dd 8653569 f687bd794da5a46fe97897e46eff49dcb5395e059664c0ff87ccdc71da7b2f0d 7a4be6c3ae43ba81f22c8b4fabd3979f7aec8691473b314c35ad267ac8196307 -derive_secret_key 3607cf27f98f1757b8e76a7a478d7d58516acb0b2c53968902daca7b1462609c 1386669 300db752fd55037c29a0c33de0c20676cf0f28ae56e25f06d811a39bbf5aa303 25fcaa0f5c89278d2fe0364d43ea52e0f4e3f15bd3d55e893c7e11e8b464f80c -derive_secret_key 359098f43d98b96afabc8f0e5f5e597d610b8b0856a5b5d11de86756f37483c9 15600 b46ac186ba172626a377cdb1eb318762da42e2e31abf6308a3a2bf1c0d37c40b cab9a9bb3149e1e06932fc26e5cb6f6d3b5dc15ce6ae70e2266363d692ee3d01 -derive_secret_key cc7ce6a58a04852c3cca56c960f1566a0e2dc5a62f1e208d3df9aca90e2c97c8 163468 2de948d907c87d5b9282f5a296be60ea1588e492b8ee5168c4524d285533f208 c89613b6a9603756b8055322710a4fd8b5f96d5da349a9dab25364e37cfbde08 -derive_secret_key 29ed24119a7aaeb63fedfe913147a14bd1c35f5571d622efef733af4f3b6af33 456593136 29ac9e43e1b48cb0d035d68721c2720f449f056f7f1950c0fb52b262a7619a0b 6eeed90d9d97fe62dea411cb67cb893439ae99b9ce77c88c11f381211611d707 -derive_secret_key 0be916e8a0a05bcbf1bece510cc42224e8a2d430935c3d1b766c271c31df7767 525502927 f9a0c412851afec8181964c0ab95e617ca3bb64b6384a8515c9ce3428ea26800 38ed0c0313a4c7b9f838ef605bbf91aaedd172c1554e97b1f6847ddfb4f43700 -derive_secret_key 0fb08f2e7f679f898ec1b4e6eeb4ed7c3aab345eb302b41fcfc274be62de45e8 16261445 740b0a697abac819f141a01fd37738f46f9ac6e2cc1e21a8997196f0fbdd5e0c 2659a7f16df29af0c3f4e74656e73c842d64fde3ff5b62fa40e035bf3456990a -derive_secret_key 42ad2da26ec698b38e799bb4cb2fc5f5b266d3377194471931a57691d3d53bda 4 69e12374056d28048a06ba68a73519afd6c6381c69fdec9468e6b1979f4c9507 b9b0b9488793f8f711f0f4207f5fb27036fb69cb88fc0bcf3ee490219c4d2704 -derive_secret_key 02509ef1509cd972e77cc910775930f4437541419ce86a40b422a8ae2389ec17 59340 1f120a335179f002557a38fdcc19bd622b6d73c038abaf93213cb90d5351970a fa0ca67d406fc850451b72041bb1c05fcbf2e22042c7489afba0ae8d56539709 -derive_secret_key 6768644488df1f1d1a54d51fd200e2fdac0d0a354d55990aaa09f0ccf6a3682c 2942756 fb85fa4e50aa1d10dcee27828e31c35e4a0348d8350ef13a6e350d853508e105 d7796ec4d0d28832b1786c2cbdff0fd3d2047d3e795c3d7bdc9b16eb70b7f209 -derive_secret_key 02d56f16d2a72fae1a57feab8ac4d918be932a4e4cedb284fc1fc8a3172ba8f1 922547965 dfe3d3af3298621c10874dc95ebaa591e1fe3a81f768e6b70e940d51d96d290b 763e168424f661bd0777c3d1d130e67ef9285b44d6cd2bb103e9233fae06bb02 -derive_secret_key 19360614fc8b298c78a6d8b46e714733775df2931fbc9bd6fd997904e68f2aa0 3731785 001063053a4a3208bfd7a2bbaccaac4dd1c3b779fa9ac94acf51927ba0e5830f f0d3c80b5627128a20c50c22c566c8a559c26631225511d34b522f00570de209 -derive_secret_key 306e7cd81b00431d1ad6b77f612bb525e4047a189fba6304b179d60fdbf3c1bb 183554664 3fd791aa9fc235a0d5f2717abc2f6f833649dbe0f0be2fb06e6205c618e8af03 1b101544a40afee59ee6a77b39f36c9640665cc749fe7276175672e11f73fa07 -derive_secret_key 0cdc862d27d2eefc8e5158a45a319e2c086998a255be5cc0b55b6b7eb2d42318 160 abf12a0a075584db0a646fdccbded1d8ca2dd85f74c007a28a2a0bdc02153704 246b33fdd28da78bffee260811806966ceba033781125385e30e2ed5ed763809 -derive_secret_key db78e939e7116b70ad82a308fb7de3c1c1be4acc5eb5e0af5058fa15750d51e9 5215950 3bdd7724c8974729415fd8234349b7b8f96467e897786e9334dd83f6ac5e490e abd57d43e55c0ffc465a247d8aacdaba0de87d8f6224aaddc62663762a0ce608 -derive_secret_key 24b0a862c37e05d166032ab1e0125c2792e999c041be8fc669529877f5e5b12a 204430773 f7b0db4cd783e9adfc1811434a02205631baa82db23ae1c22f836e022c04d901 03be8c2186f5efe9a19e7ad84d40f9e86d834c00839924b7911814ffb3da6f09 -derive_secret_key 8f29aab6d864f4048defe9351306c08b896f69b11bd7261778d8668f57e1e095 1674077 a78cb46c5d5621e513e50ef4d53bec236277d67c8bf2360f6ba9a5a7664d3d08 2ce7b86c547c5191916547b859c6209e4a60e0f620f8c63ae6c72c5fdfa1f80f -derive_secret_key 761db74b5f78bc312396cb3ca842727fba5b8790ebec9f2ac437261a45d0be19 148 8f6ec651ddbf6b9e533d8cbed9ff338da0d3519064c90a0e2f9f357712ea4c04 e04ee261aa91715d93efd764000b0201079765207234c6674fcb014e0d074108 -derive_secret_key dfd456b0defa69a1cfbf89f30489ed8439971f84480c67a00902c17e90ce6da3 241 56c4adcca2ca16e74ed882f60a997af56c1ae971dfff46c090a2939697187100 bc163368f37882b3369fc38cb8b5c54d92eaf8afec384f6fb739a5249d121500 -derive_secret_key daaf8ae4e34ba1350811f127801a9dadb7878fd8c673e3eaf40f124b47fa0da9 3107523 7300ccdd1ed991b9a8d1ef0d07b0fddef256359fac8c72aab2691e0dbeff8d02 aec9c1eea25d506a3bcd01d7983e157d1992c92d698846a343958a6d4808b70a -derive_secret_key a1a99a34560ce85c7a1ec80699f4515af79bb08bc22a10766b1d38b9131ea871 469112332 0042327b9cc50098b178659fe5b74f8fba26a27687f2403998e3b2331b64ab0c a84bc17b2f10a1bf4e95718bd082cbe95ed953f1fec83191a1a46c824986180e -derive_secret_key 67bd79e8e2551e69e4a647f8ae7f6eb8651dc183bc56a9dffa5a297e6147f380 248698130 626a8a276ea0447cacffbfd8c956b0a057847f3bbd77cca7135564ef35999107 d5ae5d27d15cea4fbeb283d7e8579f9ecda5927b9169470db17ac0275eb9f901 -derive_secret_key 8c5725820299b8fafb6c9ee96782ec3336ed47b224f9d92871fe293d740052b2 34152884 0dfe7c0c67beaf17d2a40f548645c42e325653d2027195806c552f627649da0c dd05c0087efbd0726f28ff7748e3fc809a3b50b71a40d0f0cd5ed29800cef705 -derive_secret_key f1c27278404a28811bd8249891b403eeffc97e4715955a40aaaa068b1a943007 495619 66fa71a7149f4aaf838ac5ce9fa91e07939128d8bb1db1df5d4a7204c565a504 b60bae360ff2dc7225bf97ef72a2a07b93870d17700429826e17f1134b06de06 -derive_secret_key bc9a8f57ef00dc02c43b462e67afa17271afbd25677a0eeb542f2aa2a58b7fed 872 7f2a890dcf31a3507b87d009a6957aba6aafdcbdcad5d64519e33b92ba405100 f6faf81091949df2a1924a0556de9c1fc688af86a6f26bc1aac8b7497423ca06 -derive_secret_key f9d7e1fe2e3f8399ccf14b788c3e262cf5e4a93346a999458698a55f536f4de9 220 e6d9c448bc86514a47f9f7f5b6854813fd2cfc9b039abdf08657051f8429b50b 93edc72e8a7dc6e202dbf2411c4f6a61ce05304583a26afe0914b3cb2289c100 -derive_secret_key a1ae2ecb955130240b79914289429f33d9d29c559d9b5f5852041aa7ec383daa 316730087 99cc976a3cb982ffdf34fb47ded388350205c4870dcb3669df6c14e34eb3e105 65c3177eff91261d0fb2cd35711b761648eed74054db09b2f8dc5a2ef0ab3503 -derive_secret_key 129c6960e4e6a0fb4e025f6133cf43abafa4df588940ce85863e85aabfc2e3af 6212708 7a2e2a9e9b185302432785b9bcbabbe5bf3cd436076f39fe3910033da885f808 abeed0110cf9e6acb0cfc467ec0d30e08193429c649442497e3a62745214ee02 -derive_secret_key a2395b3f9d230e98411e0d38e331f797a7678fafabd4544af9584d757cd36fd0 14455628 265f3859eb5f58578274ada9381daa379735644ce8b7190f11d3676d409f0205 4cc32b89db3dfdb8311edf044204402c5f9d35c8dc029c7c02547f1fdc982803 -derive_secret_key 5616a6a49767eee9c4b9030dbf21f41b016c2755d5518357986a62df789df0ee 53609 6e498951f795944f8f5a91538ff3da93dea91bf7274cdd9092e38fcc0914bf03 7a62c09a0b8f38d2654feb9c6a7ab03216550edec16abd91bfbbea5aecf6ab0a -derive_secret_key 7d2575f5477f589efcf9d53ee4ba9a13beb8391726cdd7e40f8ae02499b3c78a 10 d85e9f89ace5bf04cded4e70b346e1543d714599ce0b94b79fb2b4bb75a2a60c a9bfdff03e9b971262ee36a02b00ab6e708d93656568219f0e5a8c3a9eb86e03 -derive_secret_key 4da4e4ee43819abdd267c3f8e310b667113e7a3d132fdf6b4bb70ff371904808 66359210 938650bae4fdb4db4c5eb69c1c04a334352a86fe1a19197292faa73c505ebc00 04dfb1bf514e34d7012b499701f1c0c9eaed559afb8a9944a25c6f9d2b5eb902 -derive_secret_key c0e2516b9f857f3c190189faabcbff2c5dbc9fa87ca24ce34f5c5100dc852980 350182271 3c41348d2ec119967d26a6719bce2af8e70226c22856a84085c97776414ca40c f2dab7c03dfd959bceb2dc2ede2790433da4d622c52f441dc820ecdb59873503 -derive_secret_key 9a97f37c8dc4793ea942f6f5a311a77d480325b6743d5874ba1603999f16c3b2 1 777c0cedde7fd4fb95b8542d15e643801820aa87bf694a26b683909dad76000b 50e9d74519caa24387e3da8acc3d989fd77d714c30f4000fbd921939135f9c09 -derive_secret_key f2fcb37acc736806c52467c80204dbb82763d8ee3d930f5a1d3cb589f600fec3 396853 4f612ce9b8da13d45dcfed589f22b3864dbb758d5e7054628a787cf39a18a101 a27007b9409e24a1dd7b5e45e43eacabfb1ab5b59cf9bb3c5cb5abb94e92cc09 -derive_secret_key 4398db3e682d2257f81b86929119cf503ac3e211818ed6d24209563509eada84 1609603441 7c629cc844f0a5a954f5f1ff5d69fcc1c3b5849c6946634e889ce2e1b0ff220f 2c8319f1edb04242c4be2bbe59f5186e4ea5396f3a4e33e8226c17cc5edc750b -derive_secret_key 0b5116e8c70c7abe3934cd094f069bc6a9f4a8ac7887a7aae73bdfa81db40f9a 13 4586329fb2cb8a6863473bee7cc74c20bd11b5825041d2f832b81b8c9b00f904 acf033027a4b25b646ab93499a13e35c62533ff13df132419da8b80f26fe2203 -derive_secret_key 25d41bbcbed3d91afe1fa5e56b99dc035ab331da753b278f4a11dbd645ab86ca 1209 d194c2b90472b36d53ae61ce5b2347ed70124fc2cc5a8476f1bf6fa9e7161005 c50756735d44fdcf3006d26e50c4647c4fe5271afb147511b6fd18297e2b2d00 -derive_secret_key db7b5233f0d913f25292cb5957709b2b20cf2c4fb08ac7facce1879ca2a54865 1326463158 844c9e691a7ba328a95d2f4d11603d03e7cb589985ce3a83c76624e259076306 b2841211b99e42a5a662eb793f2ece6f360f2dca234ddc0aa97936452cbf9200 -derive_secret_key 1b9031b9872e48d85763f4b64be4dfd27c60077d1bc0fdaccb1d16830c306bd1 5170464 434e6f35320faaa9a2db608d5a14f8307ccd2e66e4325fad7de38fbe15d6d006 5e16867998db638eaf2528b958a0ff710471595f64f22a4c91e9c93604c3d70d -derive_secret_key 39bf63190641c50cdc41230703057c042bd97a3b4095c08e436ce9588363c668 216571446 fb04ae0c803ed5fdd9c8b4e083da8b9f8f396f8c840a93f8aa67bc7e66cbef02 e76d929d82542c2f7359a44ec861989acc6030482eaa6fddb47c5037cbc4f40b -derive_secret_key 32d317ebd2e3aa2dcb987d27e8356e1f49bcad6335f8e0929f4bf930e095cc28 368430761 cd17d4ce004aa76035f58a5dcd9ef422febba18e3e4c8e43ab9fb69f2bc64602 4a88954252739e6bbca1d527f8d60fbb0b576f977219f11c199d5759b821c809 -derive_secret_key 042522b87fe1b5ca3fe0dff43cd3c7c2dc3b126c68e8d15dfa3d5de59931e523 137110771 e7a668f42e404dbe6c798e85faf73b9084863ea39039dbd5a34dffafb9bbb50a 1049e80fc8c4ce42fee8bc65c64681846394312bf6f77dbe0136917bcde05702 -derive_secret_key 790ad0038a4d7e22d08f74816125cdf77fcceadf18e3af06708f9c3f6ec4d792 2 d3d0c3db4a57b32d2208ca4345e43d70a4b80826568f81c20153884028cda00d e400e6705c08c7383df6efa2c91aa011f4cb0af45966c92bed6d81829e38a005 -derive_secret_key 53fc7f59b34550ef4db0c3da379779e154c9914664cce9acab7238bba01efaf8 10521695 a80ad147f893ab56e1c60aca60151feedbb6f61dc56733b9b124d2d59aa3660a 9bee1ccffa4fb8c45eed3b932edda6184c4a1beacb0cf59dc4a44161620a4602 -derive_secret_key 5cfa3c6e2efd58bc1b3046ee8f3fbe3a53a53e6249e95543f0887a2671b91b68 9466861 44d23a83d89b5ad01ea241cc392c727b4604d353bfdcf6148163578cdf88f90b 7b12d4bcdd81974150128dee355f7ae01aa15242410b593ef1051c68220f3d08 -derive_secret_key 0ec78ba7de8138641c00442f509f7069fe2713e90e79e2daee520487a67b2c4a 34 dcc4c66feecf8b0381c43ba609290de28632c61a9d5d1d2b6fe296d423cce20d f230f89c2fe6f5925bebc1c226968c8dac2ee91f65aa9286c2a44d2efea5ea02 -derive_secret_key 108c56cfc260d1d9058bbed6003a76693f02e38bdbb20082435096fe92dd7240 172173 672d109450fe1384a752bb92d36d69e0aabef375a3f16536214833fa1c249e0d 7f8dc2dda3b587a7682e1fa8a5a67e1148f26d36ddca870d34a6d89aec9ed90e -derive_secret_key adaa9002d39c25eefd4721428d96aca99849686bfb44a6e7a54d1e448dd1ca8d 1375394 6d534ce833ac1b7671a342a3319cfee50e995c331005515438d32f0c29e5c40d c2edcd8fb7fd1545ba9d94dc205e356294bdb0bd87d363ed019747efa0d7e80d -derive_secret_key ce2f9e3ed8a5b24510669003b32fcb5bebe8fdb1ba17ebb08aaa4c7cbcbe4fa9 51425 683f18ac08e276c42bf7eb4b2e7b57535b37535c8bde5161fe9bf2dc1dc7fd08 ffcff61e36762c6249c641f695c85ac6b7c3315d93b42d4f32c70788b2029a02 -derive_secret_key c1a7ad060e86bc6d2f6470b0b39effb345a7309d738dabb45a18d56065e3663e 8476688 c0f9bd1efe3a96a2a6c447cf26d20d8c86cc8f692c4bed1045bb651e4fb60602 d0c920a2df75b6e0b0f4f91a873df3ba6adc86317911ab4ffeaa79929bf0f304 -derive_secret_key 45d3031dcf8942fd77ce550e0a560794bf08387ece09d441a83ce40e1b73d96a 1053 1f5afc0a5e4f3ba5f35567d6bc48d3613a08f7ecb9234c908055c0100ca10d00 22b17d37b529fb6247fc501010f72799379da29704c9214f22c9ac9ee2cc7703 -derive_secret_key 9361b5df0b09cdc5c9e0925c841c760ead7ce9884daf29f1e7c0c2d0de7c49f6 6483 18d23c328720c797905055f9e773d332fa41aa973316b0825058afe717bbfb04 40cbe32ec5527aabb0231b4945b34641ee5eaed54d9be1256232b1fba0a91a01 -derive_secret_key 947812508f072877b41b17a7a32eb38423815627e0244b499d6a08e3b7a3a439 997 c715efd181e95ef1ed5b41b2dd4d5dd201342b83d443f4692198b6fcb5d92b08 b3b88c31304e161bd0899c6dc2a8390782c8e75ed336cf029d375527bfe01f05 -derive_secret_key 2fe73495b78de676735d0ee13b8e80292caa43e60c05cf29d608797037c72206 143599409 842752a32e576c057efc3913410ba4adbd108748cd8415240064020dc8c8df0b b66fb5ca76693ff5bedaf750f3ff1cfb33cb0d6770a7c28cafdb3caf1ddc4b06 -derive_secret_key f539784167b49c4b6d169b2678979cdc1ad6dc01509841810d4c72e1d601ad4d 1322896 68d3e7c4a8d7957532cce042d76af86ea5276cf27f340f1dcd346872fd2fcf0f 1f6b7daff864fd5496eb522d77b76e8d1863ee438d0f1c48efdc1b6ab389e800 -derive_secret_key cdc501f495fe11d3596b265a926f067048fcdb409fbf7470381543062e954d6f 12907569 deb872909e2d3b30a62685d8211422aaf3651d0f280c5ae83c7094861114ff0b 88334360ed2dd127c4b90bcdd60a489a69b1d9ee1eb213d2fdc6b8f6a3d33704 -derive_secret_key 4f3687468d9d7f33bb0786a2185c9dfab790b65cb76dd45525599e9ab9ca36f6 57645 dd02b663e47ab73e826920278b8207769a9688749d8422552b1e660d81aca80e 54e552ab7e72c01efa74f0ee2bf5661f2dce1cd5dc163528e240c6c3b3c0ae00 +derive_public_key 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 66 e2de6c372b7fd2326263dca8863a0e5946e52b0422deba7dc8ecf840b4931096 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 5619c62aa4ad787274b1071598b6ecacf4f9dacca2fd11b0c80741b7444005c0 13020497 43000c8e763d2f7938ca8f3b58b7e660c16d01ff7365ddf8b6fe76dfcee8fb6e true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 84526c85c16c032c7678c7a1e3ace773b31566d159dc8a3cb81ae13be705f087 26 13019a84009f0dee4518206f249cb9a9b6915e8a5fccb474b0fe7d0f429b7056 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 5c6223731282644a78322a36c11bd6aaf6da438e0f8104bbcd54d4d1374b85ed 559677 9f8effe2eec72038a174ab9828723db6fb9ab3c6e85f0ca47d0ffc1a655615dd true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 5f9097f5488158a8021dd15be1e6dbe676087bda1f2d9760ea3d7910902a8b5c 237684 f096b83579693d35f6c8da752dc703eb5813bf9c13bcd06cfa6d35e48e5d6f68 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 364facf3426dcb1a33a67e66fa58f4ba9bd92c7bab6ffa41c06fcdc65313859d 7067430 9f8aaf243da44065f422b6ad17c32f8fb4bad058802561f4664a88056611b6b8 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key d1de71d7b44f3b800cfaaa9847fe5a562f5fd7c0cbbd0d146f34d42fed6f81bc 0 2a5429d5cdad9716b4a022b70883d0f316ab561fd7a4d9f7f04b35334fd81d33 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 6959e9ab446325a67fe026ffd69ae485682894bc302ec9dc95a7c713741b5ed8 43208474 a7c81eaf3ce7d563d1d52a5a366902cf5945c3b20ca8e2ebba04b4c7d8f93e8f true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 64dd69003c70ecfd79cf5673a908305cf9c2d05aa4ea678e066f6fa9b346b5f1 7218015 1dfcb7efab4b573879004579259183c44b67e5479dfbb97bdec0be0795fc1aed true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 4dffba90f6fe8b13625e5702b4dce34d8d4fb7472446c8e20939468bbe317a25 31362 9c2378526fc06eb1da949c4f43e40f79515eae0e51a5796b4b83cbe5e658406e true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key db39e51478f87776caf7b34291eb44cf228be7f2fe1e43ffc997a4ea1953ead6 2045 5c60f3cf84fd2ffc15b481bb77022bb0c81547044d99c3f22cd99c5650ad82b6 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 150dd011c83f894045d2fca33bd65f8efdc13b8ba1710c77e7aedbe59c6f2d21 559498137 a7ed51454096ef7dca60d295455b81bd1ad815e689cd82b3e76aa8a58ccb2ac6 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 152651c7b19bfe7510453efacface8058ef0f3bb3bf3a6f15acc04c947de7450 7321376 35bb21800ddf1466a8e53ac8b495c377a838ff0dc6f229e5cbe7b567ba6f8a26 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key d892022cef366f21b16ce4a94a7c8fc7546d2300a00b3079a6f92d27892ecac7 106174103 398e7187706333c1a35f485d45d08b786bfdee753bd465fb8eae2857d2b2baa5 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key 1d7942a6fd16c8bd4bc0818726385416a1fce39e64915187bd85dd72335c9563 235487995 34485d8265590fdb57b6b081802a9e73bd094e7f377a4da66368840fc4240758 true 0100000000000000000000000000000000000000000000000000000000000000 +derive_public_key edf935aa4eb1328e50de3c234227e1144bdb5d7ba449e87634239f3c9b6af23b 240375 03baaa481dc892dc6454a14ca9a87cfa35bdbdfcb0aba2a8eb5f570e09cfa68e true 0100000000000000000000000000000000000000000000000000000000000000 +derive_secret_key 299d69c7510e84ffb46d860b444542b066ad98a2066f6654b7e2138bdbb35099 0 f75e1ca55257ffdb20e93cddecfbf30f43141f70572ce66860b580f4024f4003 3393dac098f6440f45e0575482a74ec8635d58de64e34a9530435815aef41c0b +derive_secret_key e5f07507fc3cc241360e1e7ff38e224b661b42c581c2900362433fe75714d5bc 4 cda5fd08a17dba195ce254f96f725c85e38c67c6e53d5432ea043dda522e2a08 4616f7c4def34364608ef8f9fe2562a1dc793dc30beb80ff88df958de8708507 +derive_secret_key b5ba41db3232499e760c1c81875e1455af4700865c4281e878312abf028a6586 5588 dc393b33131f3198bf85864c4adbe859be78157fe88d57581bcc86d0e5e0f405 ff112cc3374a89160c537dd6abfc51e883b2f5cd3ebec82e1033bf160ae86706 +derive_secret_key 0201659495b9db059e958583ee6b3de327ff0bdfba5afa6e637c35a9bc9e7a53 10574 f90ebf2608ff74d588124639c8250f00c1b01b6a2d0148234f0a4d76de2a7a04 f2b50497592c0f3c071dab427e905f9b10fe5354bcf1c7517d6a0cfa5245a106 +derive_secret_key 548ae82bd7a265063c1afb013ea1a4bd2be72cabf1ae4781909654100142b7de 2674280359 e4ac4eada4bf966355178274007d5ebab6cc25684632a46934b644f026a9d705 e45d4b4df249cd601c172f113f405e0b0f37395915fcf5f15531512c85389900 +derive_secret_key 8a6412a0a2b7b2af6c78ed820353d5e25c2327e965fac356db992c34e02272e1 27 53f957e1f075b29302bd5526e9c3bb053ba3ecd21ef3e149d2d4abf0aeb82500 c02097955a4cab5c622863ea8c0dfce9960503b739bd3f5a2fcb9b684d22bc02 +derive_secret_key 97439918c24578178b5cce9ff3ddc997d563723b20f0d797dd74a91e97405f3c 40740 108a851a5e530276920e3bd077cc6bdb1c7558c1467b5ae6ee4f635c27b45f09 d9adc3c17ee1c9dcbd8914cc13e6a4f076a2e2f12a3b2c9ae4e9850587322602 +derive_secret_key a2b5349a7a9f2a14f5803450ec8476fb4c18f8312305ab763a59a2e3fb57485d 0 8ac8b3848f7e8ff48c9417472dfe4b6003cb9d04ce9a07c2ee811066f1e55f02 4112321ac3db357142c5f85640781981a955a15e7b4278f89a4750fd1b48410c +derive_secret_key 54f947df9a284ac31ea762b29cf54eeb6e67cf5497789539727c6f059f6bd878 199827968 08040a22e36dea15c5907c374686883a0817486479f1d369e0a56be44b32de05 d39ec39c4b3b87d75fc96f02049e6c217b00ce8f12a6eca7e8c561a67cab0c0d +derive_secret_key 4ef98a340458b799e42b00b6533181b311f7f483b278af4b8cd5e0a5a8652e5f 14475806 9e599eb8d9d2d8d4d7de5167230260c0315d1eb21edbb7e10438d5e6fc195201 bc1e1e65f615fd5e9d54336810f7519b0fad8316e7ca108add4dea7a7e177801 +derive_secret_key f9dca9246f3036d138b8a3cc048af0523c987f7df55a74b27a7b995d430077ec 1588252336 8ae4fb63b1fc0174bcc2dfdd7648fca747b263045dfddfbcf91f15ee1973160c e1615a93320054a0a2ff00e797616a4cb6a53ac81a12bdcf7e3a0365ec87b302 +derive_secret_key 15eaca82ef1fad6ffb71c655d866bd430ff265a1fa415a1fcbaaddbe21968e96 10562 de5350618dc6d3b03b05bf6ace3e829f7c8c8c5640f220052df9d4eb8af13506 0dab63f596c7e044e289dd697426c6750600efd2756d35e01ff383b3fe26510b +derive_secret_key 55ee7c3c50ed7ddbf08ef8fb1494175a4baf1237625c37bdcc83945cdda5be9e 2190326 ddb513dcead1e4dd3032dc02e954554c01abb8c4adaba0a4af23095ce59c4e0e c6eec5b02ad83ed8c76923129bf8c7aa1aec7bd4474d6935c934baf52631070d +derive_secret_key 161c5fbbeb3b8595b3b5edb784451d0de78767653ba595d7a68b101d280d4291 3516 344beace0220d6f43de079e47752cfc471d6b3cf555998aa64143c611f547a01 8ff195cc0988288fb4c9b5707c4d48cf943780613a5648aef69dab7521cb5f05 +derive_secret_key 326ba1bde83debd8edcac88b1c9e3edf976c9195cccc8b49fd21219231912c3f 3 e021ea135f041505300e7dbfda9289bc8ecb7386025a6332c950bcfb3e4f2a0c 2e307b96a64c23467161660faa6f5f51d8fc78ed8ff81909e50428f4676e9701 +derive_secret_key 4db144582c9171bc3b7e90da8057d61698185a3f370d7bc689d6f453414c9b98 47543883 a1582aa29e634a55c1e8746d34c6b0f935f56d1c7414630a2709a9a205615809 b2ab71568f4b472b2176cce848d3b8e10388b7f1cf4ddb03beb9d04206826304 +derive_secret_key 144c2f9d2d2bc858911ee1aa5eb5d86e06eac30d57c33daa3d78b46165b0090e 390211949 c2f0fcb87adba4e313bd0a094778ce4f97c41b734f649996ca5b6ac64b387902 d2b6fc5311753a5a698bd4031ffb6a1395635b1cb26e2f0b3835215d4c932b01 +derive_secret_key dceb735ef91c7ea58cd139f9f70e11bc191f6974ccb10d55f31226e8b587ac99 10122806 983bc804f46e07acab515fcbcd15857d003fb829613ef7701b19a77650128706 aeb0d9af62881d6163aba2a4421d802626c78b6b2703db063ac047bc6e573b0a +derive_secret_key 1f9882d4d5d1e9e0d4a3ee7f87b701ea4c8444739e1bcd747259f64009ace925 4360159 44fbbbb2d009655eb22ab1225a768eafcf4d8146f44ce0c1dbd8f84542111307 18b20239cbaad3a639b70bb8d18c126f342be4579c5e5ad48baa4b86b0a5960e +derive_secret_key 57f105b191f2b40141f2646ccf61449211677f3ea6b3a2906059e809f19d38cc 23499 7f2a7285677c1779da47fd49414a678a6bd61b4e4321acf4f2b217286bd90502 7e67c65845cb9256f47f382757c715c5a6a1f999ddc5a141e4a07cc15002020d +derive_secret_key daa50e2961c6f2fee9deed2db95619d5e672975b1c871edca4c8ad7dc9988ee3 107 7aff2be87cb980bcb100a9ccaa424d6ea2c56e65f6fefca81fb663db84ac6507 de1aeb4e1612a9ab7527bb0263821487126d453ab9c517b4eb8fd55b45f81b05 +derive_secret_key 310727f790ae4b8faf3b9eb6560f26f00a6ead52cd51910c4bd71d8b56d350ba 90004154 789992fdd790771ba0bfc2eb54b88828376e582d5015a047bc6ba140ea35d901 abf067ec24522aa6fa29e6e47d3058b1068d25a0f05aef47782eaa9c0f19000d +derive_secret_key 87fac0f4425f0dc66c7afbbb45e622a631f048b4fe01c625d2a01d81d1b83173 3393 3dde1a11452a6849f82973bed84b801d0f6a8de3b81eaf61e2ba2846fc7e1007 f9e521f16f7dcbbed42e3d9d7408dfadfbe64371c42a0c5097e1f99474feb40e +derive_secret_key 2742d3a408dcbc340ff93ff7a166731bb8511318a211bca1b9ec8425f72bd8fe 28 1df526cef2f26f26f01d64aa1265781ea1c40e49b678d0991eafeabd0cc2b206 137f15f2c9e2eb2cb156cda9e5470b30419f0010610b9e26e2d1d78bfefe5c08 +derive_secret_key f5b4e0ba30c076595bbf07d521c0030507e3b613f9f266542324848b7db2f943 22 3a7219ba53d5e95568d99b8a91d9b515ff75720cf17d47fe9076a5a7ad604205 7fdf7e25e663392f95c107b1c2f934e1805e82be627f4d7ade2f5ba6b013d605 +derive_secret_key fb730c166c8df0ba5031c6e9fbe080522776611a6e1138896ef9e1b8e7e8a1fa 708 84fe1d861dde582037d562bc9186ea5f1908f800ba2b6a91a4f95d56f6abde0c 0b1d91749d438c8f25dc0f9bdf8a6dcfec96f8d9c521b99752a33372fc7ef004 +derive_secret_key 12839f1a032d978eef2845f59c69dc2ca894f0c9164ba68219e062f8490f8b37 0 43c86c80cb19c33d603c98c80d2f0aa6b5c722b6a6a0d6e5659849890b159309 184d467653b3a0fb284c852018ea677a5c4ca981816686d18343d10b17aa620a +derive_secret_key 41f767ee1bd32ed57422d3b0d20929ba381df5facd1847cbd254cfb1a2d74e2a 168 82ae77bafb3848b3a612763e42ef89a4120f39a15f20bcaa39b5314bbb1e0700 cd9614681984fa77e4ee7dd482735d2ae53af1b031ba78267bef434d117a2c0e +derive_secret_key c34ced60078d444ea2eb287808bfe5583236257674ae74a7f99405fccbe3bdf9 66587 3dd9acbe5bb6da12c3376d8402be317073fdd7834ecde8bb72ae6d694cda9e0c 1de54c45a614e3fc071b01b3ae5c023a25ef090ff1bf005f860bf6be7894be01 +derive_secret_key 79aadbf8487d9d6890bb36b6edf371fc6f921ecc09114600ae8faf745f998a70 24 c5c84f561c4b5e758d62cc75d6a31d6710d2a6c451967822e79d31c4b5167404 9c3f2c03ca1804558727fe5e07e6cec003d1b2a4a346b447a52d1bb3122c1803 +derive_secret_key b95c8b2bf3b51edd9e26d196905f67e6b1fad45969aa324ccd1f6ae61c08c59c 19691349 81c3f64377f14b3f6c182b76a237ecdf8c8e0452142e036b643ffd744b7f4b02 0b78a2b488a31c38837e520c049b8169ebd255b5cc3cc67a492796848255b500 +derive_secret_key da213971977ffd8fb872a778eba12117d79048de8984b76caeacf7cbce632f9f 0 7f60b4edb5aeedd44293d2c7523bfcf8b7709d05ffe6bb0f4d81d4dff9f50b09 03e1acf59930b15e8ed61394d8171c4bd59eba768e19ff934e1ef9dcca6ff802 +derive_secret_key 054042bd24dfb75487ac34606fc6841cf634a2a63712879c891cda936c105a93 9 3f6da0f889bb37ee21e80463cf53279b1641fe0c10d94e60dcc9eac781fe2c0d f73ed9923869b7ffc65d13dae64383763bd187663984c707c258b97c95f6ae07 +derive_secret_key 21b530cdd99985140a29d1fb5717fdb1e009ace5dec0026ba937fcfffa1c22a7 765628357 05ff4234bffe2f5702c3bd511872ee27018ca1b90fc4f60326bb703b44afbe01 44c170004059c3dc046686d9f3dc1397314f76301bfd72dbafabc40248ed8f01 +derive_secret_key 8c80f6f81a8d8bb0a5e570d786ce4adf814a897bee1855fbc68f72821b427e35 1 76f0037367f1eac52aa3546c51d55a489c57b6bd338db5112835a6539cf00408 9bc323753ae9764a364ee0e34848422f68d9f17d4efc49bd2261c054612c5d0b +derive_secret_key 532b34cfc82f1ff183b6b9b9a8c84b36bad18090ee86e28e0337f6d139047e13 151628 cfcf9cb704e4edd9859293350cc133c3c168bd0c1bc8901c6ff4aba6a1768e0b 1d8816a590cbb0db3d60eca5182792bd9ced2c7331be096e2a1d6c907255e80b +derive_secret_key d24162f7226ada0b23b5507b08b415804c74c33a9123d5a141f85b53f6319aa2 26017446 49e31a862df6714fd564611cfd8b7e1c877c3dfaf081009ce8ce22183311690d a86bbdfe3e1d0f2804c74fb86c755c09f107d3106b899f211cc149c4b4215109 +derive_secret_key a20fddac27d9c2f48bf99ee26c3fe41faf52c4848e61d0c7605c053e842b5459 1235447 ba4c33e81b41561ef87fdf22e511d1637b5c4cd6b85e669004230f189fcf120f cf96505a54c7cfd28040ff0e1fed6e68c980bfe22974f64453f60847e3ff4900 +derive_secret_key b4c757c81b2a7b2cc1758f4e23ea9e0cafa0f920a4f80349a5db63d7164c1eee 1225318974 a2e8ad6d2639f7b628b60af003506c063be4e9c66a56b8fcd1f916a5668c000e ed1a337fc94ddf194bc3c186d2049408b3b6303c2259bbabaaf8d47430e67105 +derive_secret_key 8756cf24fa957585597df0eba11a719018c9c11549ad26367595f87cba0b1635 248 404bc5f0091cf369f2bf130951b749b729dca2cd7d472448684d597ebecad90a d51f2ccf8a34ae438f2724e3aa47b3dfd96abb300a8cc7c299689b2e698a8204 +derive_secret_key 8210561b102a78f5a2b495912ceb78d4601916ec3ffdc3d8b27a0dd8dbab86e9 683 cf674706d70c05e0105a99a5a6978db2808cc97ac67212dd5399de5eb5067102 c3f28dd51dcfe6d5b1733010f2bd67926f1f9785c647d9a9d015f58b3b8a0e05 +derive_secret_key 4ece5a7d60f0763162076c171277410c5b2b2358cfff1d3552b5358ccbe9f9da 322708034 dd6e795914ceea6a54fa6b44f7ad41363b3499bd27af385248d91eda6185db0c cb639374373424ae673144504582b874de7a9607e43fbf240012975aace35f0e +derive_secret_key ee69ad378a9b8fa79dec76f03d32c7a61e308e853806800f2fa0cfefc50c6809 0 7f2d2f00a627639290a359700d77e34f733dd8250da6af1d763c5eed5151a301 10b3110145f19035d4b77b795f6d31b68aee12fd5c7c2c56ddf13b0ab543640a +derive_secret_key f097ed08b9c9a3ba849edb00e544a9ab7c912ed356097781873427f5587d1e5c 700 8b67ed99040fa384a0dd544b327cd7fe9f3ecb7853a22ead9a54a9be68303801 4d4571b33ef00e7184c6f0c2efdcaf30f7a7f283f5de6030c418b778c9550a03 +derive_secret_key 4c0e67de28a4e38e565ac27ca3b48c57492867959b634c96a39f8f70ad510720 82 a4ed9cf3030198eeb74e449aaa83c4f9bd672d059fa04f2a4adef78b570da502 2fd8048bfc76dcf18140a6e0088d9eef2115198e1686bdaefeb5cde3f61ba80c +derive_secret_key d9188064954e873c4e5202dd5ff8d1a5cac3ea1ec644eab3f8bb270595e74224 2020758 cf4642ed318404e4ad44f162f1a6c0dad04b58d362f7ee8315c0753cf73b2703 358ea4135c0de3b9cb2c4e671e5965eb3c82e73edb301f3eb9a06e73b5e80204 +derive_secret_key 3322e3611ed73cad98ad76aa2c1d583f714376bdea1961e06a9b57473692d858 114128878 a6bc6d63a77bdf9d76f53fdfaa5765e5232ca86c87c3d2d8e32f044d23a5160a 130a807e3f57f89c90304961810fc5e8d114efdddf592394fd62a8cee6c6e205 +derive_secret_key 7848dca61afcc441517f150592a4a7f38ce97d8b57d9e07a9c19d7b07caa51ed 2397133 8606458292fdedad6e6ca51b8b63f574a2466ceccaee8a668818ea9c88c9690c 5de9d37e14e1ce132b1436e4a9e63e2c6882d4bdff0ef5b15cb83be65adb6203 +derive_secret_key 02227b7ba20c01c6fc17d8d6dc16c5d1164c3c14befff8d6bcc0ad47752cbe30 374724054 270e41cf21223689af12fd2c4c85a90f46ae0c234cd04585847cee899362ee0d 1f3f47900b084499199ff99d75bb366b02fef249c4b9ebf24a67f19f071ada02 +derive_secret_key fabc0c85c0e6e6d1ff6471ba2e38ef5c9404ada1789bdb65583984b04c5ba1c6 501 7cd36cf55d9a23cb58f03925bb2f44567619feb94662a03b14c97c15c9547405 e01f70eeec3e5134230a18260f533b72ef2ce1f89072ee529b7636440741270d +derive_secret_key 4b6da4d40cd5e8e347677cbe62548eaece16ded20f4d0f16118943bfcfeebe0e 3241241223 fcc44f5e00bc880af85f79f2de435a138048de231dbee9d948e58497ab00060c f2934f2220b1eba30d59fd5d3c0aa3bafebb88e0362d9c3733d67bb75247ea0f +derive_secret_key c59c4e3ff02396083acd452eec1acc8852dba23568274ccffaea6e737bffe047 1363 d1938973dcee2dd812315bdbb2719665e429288ee5c48ff2172877dcc0097f00 5ba01239f5c92f0231a2c702a571fa80c14f6ef4faf455d06e407eb892e94d0e +derive_secret_key 769fadbabf7bbfe77f22f10030aaa515339721cbad183f9cd389549def88d7ca 7668788 975f9b9ba18657480f874c681abec79c63bb6b5a0212cdc2bf5f0f03bdb4370f c91349ba2906259dd9e5ea23f18a71074e9c71e4db7fc598a9a237267ee7120a +derive_secret_key 23b8de494ecf4a3027583e17e7b2ebb3777f030351b581049865ea4941c0dbab 235 ac53be51a276ca36b2ce3eb2b9cfd83c306a01711ae6d8aa1dfbce533a02db0a f403ada3586be4ce066fda16de91eccbf7d1d3550c6190864a077bf68ae48505 +derive_secret_key 680d308df789c8f68925c55f17055657e4611b5d76388fd0451c88e5ab653fe3 975 3680e594c949df6f660a8d7d52e94ae260fc66bc3ec2147fa19abcb77241a808 08191720ead94230b217a8b54f87f7e73aef3974dac653641286061bc5318903 +derive_secret_key c5a825bb1e16916bbb8630d3a4cdb36c3f09dfc1fb13aec6e726191c2b718342 29 64c11289e28394938d9d5ca7025389977e29a043645e20a35909a59cacb1400d 45eb501af67822a10f050d5d34edd7a4b27603bdbdb278389b6d602c2c3e5406 +derive_secret_key 31c519f255cb3af8db1de9227dc0f12109390a676535b282b1a4b07b0d8bfb61 114108439 2f877842f4770420eae13611d5daafcc25f05d414236512662b741e14d43bd0e d2dae1cb32a135b14ad1bcb2f6870327fe1e49090e48a5486e1fef5e081ca80d +derive_secret_key badea7f9f6613438c884acab71396e3a36cf4a0518ed0c7492b9c5357a64f6eb 13 8c28dadc02c10fd72c8b9f252d40300850757ac489df3aef25a0ee0458bccb03 18c26d537207afe20ae0b845ec1080f349d692a000a28727db09cfed089d200b +derive_secret_key ff689f040a10f44924f73e871d80756a9409f0ea8e7720c0b6a8e8ce42d38e4a 7559339 0ac548c1a1a1ef1eb15262eb771c287b2989f2b9a9aa87d946fac6b897adc504 2a4acd0ecdb53bbd7dbdf6c3914d86772aec64dde6ff7f5c5f944ec9fa33d000 +derive_secret_key 326e374c915c964bc8608cd035e46fddcc6a8ca2adabe37c7fb5341a803720cf 7039 6dd151b46af79a07f1d695c3b1f84cf174be13bb2150540c2648ba4c4b53d50e 564e2979598736df0e95141e1aca815be84524881fe8e069a9eb592115c5e802 +derive_secret_key e915ba4bf5afb10d4615857ffc30288fb989b0d4c8645ac0bb80a0035ddb18d7 772409329 aec8030ffe8464ca44e1913c189cab92e6be887131a6bc95cd02ea167dc9730a 285dbfb75bb2196a86c5abaa2ff6ad2b89b445b76687410d37501c4f9dbce302 +derive_secret_key 7f804c9bf2f132a9a572db8d5cf1816ed23b088c7328b41cea278c8802520faf 1610256570 20c37fbbbc5e45bee29d31841d7e838d285f0ee86e686bf054fa035da90a8d00 666b600c33c045f3e2623f36f92429d92812acae80600479d7a9dd1ba3108004 +derive_secret_key 9fafb95a65302fb7e91714be85adaf87197cc4ecdb41b5a723e2190390587af7 927638411 9840e5c46e45c2db1106008783ffbd4a6fca7006ba99b3472d5dc9c695181901 5a5979f3772ac3ecf3856bfcb26eba3a718a7ba6064616670225dc74f4fe680c +derive_secret_key 19b19392bc32130f1e7def49454f2a5e8aca64e6e18e21d149f29af16db1ab42 59917 0a4611dc2fba48ec4d057b771bf4a6d2b30d0a1f9c9129ecff5f3643739b6c0a 9230df14179820744cec9c9b9cb6ea1f658229899272eca7a27cbb9322428907 +derive_secret_key 5822e69e8a1c0cdb1adae59d6bc66fbc30e5369adb83d0c2f8c182e0a66d0c69 12451733 5d4ce860da2d1ab5041d692619f4a489ce9b65f9f65c707f87e3ee61056cd903 dcb57e8293eb75c4e183a1567539bd2e87e607dcfc88f4b7a7cb8c277c28ab02 +derive_secret_key 7578eab59f63a8e17be63b007fa5d8631b001e35525a97b7bee3570aeb6e2512 3009887 0fb8d46a0ce0f18df1d39f2f3f4380ef683d0cf1eb8ea01819b3eaf0f5472202 ff75f7ca397c206c067e67b79c3233429754d4e15924305d838966cf843aae02 +derive_secret_key 8580d05b0fccea56ee3e6074409b8a84413bfe2d076fa5f8736ee43c1c867c9b 490447325 3c17c269482323dd1c0c0d33027a21f3e17aa66e57e52e5c2d4214f1d45e2f05 6d3675a0bde0fafa141611ef4ea98884016505165d755004b99383e466bb790d +derive_secret_key 50a0fd497f65d2140f90aa2485727a3d1393e3143142eab9866a6d09e51efe94 0 01b1aed523f653e35c3c38b1adb27a0d5b45fab5f51db1baf7d61ce8637d5c02 16f7b896dde379be4d63212224eba483325e03f2168f9988cfe79fd63aa19c05 +derive_secret_key 68cf5b094a5410f6c6e50f7cb7e39a1ed24e7903bacf6ea2a88d5f392ba6586a 5893467 977238f7c9fdf2bee50c05f0484b6c48c5514180e3e79d7bb55060816d88200d beac978481860f54633908d2268c01ace2c6aad5a58d781496359989616de905 +derive_secret_key 52ed61b8c7bb2560045fea6d75b2eb0ebdab910f03a2f974b12458ccbf8bb3c4 70958724 242522739c0f2c3d3cfbfdad993dce6f333130690e32810972272e67a8ce2b09 6a8766d53f7ecff1eb8a717eff5a7096abc8cddcaeca739dca2cf3f63dc4100a +derive_secret_key 858cc1d938f99104c435c72cbb362bdc52192b4abf060e4f94445c81e178c567 0 d40c5738e58c353af2cdd290db9016ea3ee335fd58f6b95b58f510e4058eca06 3fca10bf2f3e3152b5520c203f920e63b164220536d0c15ec7b4c6d845ab6607 +derive_secret_key 52c8ae2f00856fc117384dbe2fba31e6fa3fba592c8e5388e08e4074cf313961 1 18ccc80c41b37cb412b5b2c1761864888fb1779db5468a23dc96d7360f395801 9a7a8f5b5d98cab8eca663d3c475e2400c67d101d191d06f9fc153cec7575d02 +derive_secret_key 4241ad19f5ee50010124fb54a2d7aef391cb4715a29995dfb5069f21570dd148 1464712824 f08d65d94f9e79e1b024bce5a3773f06ebded7ac446e2fee940733b353486508 788140d193847379dbb134514a0649cb067a1f7638d635b494db2ee80c312c02 +derive_secret_key 62e4cc9b87a34af6f1eb9dfe1b5d856a6023d0fa93840335673dada1eb766dc9 0 c7dca6769200d47521d9caa0420765fbaff7dead528b6d549e9cb14ac0614e0e 34e11dc8fd66cc2a1d5bb4295cb16538bf7019297e9e177bcda14c309ad4a20b +derive_secret_key 695668729ef9521f664ca6941400b885fe68c82d3a49f0d98b566e1268c52bf1 2035 e18c3123c4412362db239d363349d84761c6ea015dc2bbb882ca7a423a271400 24fe2326a90e25fb708a6d8eb3f295434696d6c2c07b0e690e7414a7b435ca01 +derive_secret_key 2f7a50eb74263cef15278a9d74163be7b1153f1740a0bc27bb055bf358b4c292 242162045 690ea2aa992737bae4fe1c41499cb84aa41696801c9d4f3a34910460872b9e02 c557c41c254df293440340378bb219199ad1c45c535c2dcdeebd5c91c32dd70e +derive_secret_key 659459a2bfc5ae0bd30d368d40f0e926eb2e2d2d8234d4ff9e6d2af7cc38f0a3 24 1dc11eef5ffe0c36c3a75b49952987f68383e8653416839067102712d75af402 c407157c601d78fc4ddb3bd5fa38e87538ee583ce1caf90703dc04faf4a1e102 +derive_secret_key 5bfaea753dfe35d30e52e5e7c5d9e1a7b12da0f01e4f44e3a080a62150e4a030 104363 4ed67e1b82606e6bc80db1a9d23f7dc1e47f8f737c583d759b6783e75ef78600 7045acffa28a9d08f0696ca5477f5ba932cba9a167bc854784afc3e8e4dd090e +derive_secret_key 018a5242c7a2eb91ffa6cfc5e81c086895c7d74e6eef4bb0e793b39d544c4f7b 30444265 1a5068f0000d9009f18a4166117c49dbf2024e48157531bc8105d6a29eafe302 8be2941f0adf98b6d06a3b20b7172a768ef45ea2e667befc279d0083ab3c490e +derive_secret_key a2b64c94994831c7d73365bc59795c73fb9c8ba19c34b65cebe08a9d4d560045 690804 8f67650ee6a5453ca7e9c217e31db23e95db4687fa514ed8c2b577ce5ac8610e 34d0345eb3b5b733be30edb25ea6325bf8b494f2eae9324954b6c07158216b0b +derive_secret_key 44b3491a0b472be933760e7508996485e3e74f1685f5df4bd4f487b09e0cd39b 0 6926b18948d6fdbab05daf3bce40e48fc237dc829c094f37f6d9d156ea48a202 ff02d3fa59bbcd8297476337fa21adf0ff3c8e248bbaf382b492651c06bd0000 +derive_secret_key 8cdc29ffd691a0cd2772d300002eb4e67a27094a2300e16b8e85a3e5b3180031 6250671 e114062500fc7e7b14bb9e017450053380cec6ce9d8592a4d6bc327ec1577b03 82d9b992906e3bc338a3980ecd6344466b5f744d4b40919a45628303ca6afc02 +derive_secret_key 626f4a074404cbfa03049b818a0f67235b66322fda23d48c5945a5bb8913f92e 433735 9d6c1ba49caafd7ccd6bd2eb00ebb24e7b7b166184619898fccbd7f98b2ef60c 800a44e557b7294abc68ff542456799aed9c2950cbeb66ae908678d7f1fd9e0e +derive_secret_key d1c79c0e57874919d428802dc6fe698a31cfcc35befc0a434e7b032ed6e9c4c0 28488 b4bb1c302fe1c56a524b698234695976298e90ca414d633db51cb75c6d10050a 50f8a360763d0b63c2c108bd8f986e90550f97b62116c39d24f7464c5ef62505 +derive_secret_key 1221615af47b6e25d1cbb5019e6f6bc27205583b6d151633ded4a82169869a70 47677 1e5dc8aa3102c1027c696b29df917ff49b1cc119b0c8180f5d1c91516c4da70a 44af6a4f0ca933129bb4e15376a57b1e1ee1ebd5d28efde9797804b682fb0202 +derive_secret_key b3d322f45304294f2f0dd732a16f99b6beccd1cb30810eee1ebc0ded9999e524 224406 d500224d1c861ce0c9d25b091e734f405c41d48fb4b85e01cb426e5779755809 714a82aad17e2d0c14befadeac01722fd37f46d968dbc143be8948dda8670c0f +derive_secret_key 3005f295b011932190752a7d964f5331805c3de1ec553fc5dcc75d213dbf0054 33 3af5df00234c42545ca35338fd63e9a5ea706efbd7ffcf292f374a6fb9826d02 190a6c971f942a5c8eaf866775bf5ccd8112d4949f1be8a987e17dcda12e2606 +derive_secret_key 1b161557e889c415125339d515ae38a914e495ea813eea91943cf85d6f5d4120 3770 4290c0664f6a39aaa99b546d2c2ca030db2079d4f71c26bee57aad85dbeb3405 e6ff9bea451340fbfa6889b58c34f619f18b68ae99c3a641fdedf0e22189bc04 +derive_secret_key 8b260299ea85ccec391a86adf6ae7e9dc8a3be7f072134dc395bdd1038172162 30825 801139eb89361f3f47bc6e286c013c524f4e40d683675e29c0b68c140ae7790f ba0cc026dbdd1028814f98c01ff808495512a8f7a1b532d1f119d2a699562208 +derive_secret_key fc592bf2865e9cccb458519001208b8f69b87400443457c91142cb3cfc1a950e 61507454 f43fe395569b6a74f663c94fab392c291d6e2f938f7158590e11caaae2680802 7de13639b3c6e10849583c8d4aa21486b91492872bb543eaa72457af7d471b06 +derive_secret_key c78199481a2aafd7cf07dd9e9cd751f3ed695ebfcd3f1fd08bf41f1bfb86f733 75001 61d2820c07745d7ef2411492b4bb6cacaefb7ee6b308889af429c64563ac490f d0cafcbc07385edb3803434ab0e705c7c14522904cc2b6dd5da4af236fd4870a +derive_secret_key be5dedccef8a4dbc3dfc5acf6a65ecee602c592ce747f11fa0af0595979dbc0e 18456 4777e0a7702f14b7008d25ff35c8adad515cd656980b5200e969b5bb23fcdd0b e4bc9ca1b85ac2415d7a45aed7af29ee17d59c602e879c4de20444ddb0d2d007 +derive_secret_key e5fab5e20d8191d30cbb3d9123d5ce68558d0359d99ff039aba7a29f4148f959 245969 d887e22a5604c0900d8790e59cfe850f103f01d50c02893e96158ce68e17d809 6b0de51a16c5dc571308de1de5846e0f7980a53bdcea2c4fa9d2c1ab3b87ff04 +derive_secret_key 4cd7e12d37d6407869f6d056981b095085211afa4c8ae7e6053d2fd2d90c75ea 175 987877561f596eb49409b4cceee226621f5852d9a56b13d54c8aa6e2a7c3b500 c9b20957fddcd775a1bb4440d8c7c3d757a52db229325576551c91f47f33fe0b +derive_secret_key 241c66215b8ca04a4bd6acc124a12a67a45fbd8a059715addd716aa46f4f5bae 5563 6e02a0ca7e9490bda16d5f735b9c612abb91e91b7d439efa3893c3904f0d8601 1cd936d3e2e16d0bec1cc0841ad331b60570374ed313079bdb2870388f203a02 +derive_secret_key b416ccc6dd44f4e8ebc071b73a9df1c8cc9f35598957bd0f085cbd5339fdf347 1 e737ea9f23aa2c8f9ea1ad88f2b7aaa8b785d4359dec9aa1830672bd12262f0f 4ac5eb466ed75b1b49b07362e731bfba63e6ffcd5343e6336e993b82ca72700e +derive_secret_key a2584e7809ecf8fe17e2a35ed2629c90d7e1ff69bc40f96645fa361df9d15a08 63729685 055fef729b031beb911b80214de1bfe65401dbd9030cc364ff0cad5aade65f09 8b3b05b10e6862722ee17690fd9f4cfe7d23ac4adad30f87f61fee05ddca1301 +derive_secret_key 35163fa00cb04eb957e5fe7b6c829daabb9c780d308ebbd8accdbfd79af5ebfd 1 fbd6b04e1e94d372fd95368ad7251870f8827ad5bacc34fa7e5b3bd4a1a45c06 0e047faf891f7dbe27c0eb98f697cf58a523c0d18cd474520c0b77bb4797890c +derive_secret_key 20bbde08ee23cbb76bdf24658e794b2bd1c85bd30f49865b75412d0c951ca258 0 434ce9eb5049803ecd52c5621a95bda226bb41729633215642b536a4474e550a 648a3ca27c2e52c2b528e2377acd3b95e9d0373db84cb78ad36255246f53690d +derive_secret_key 560d92cf0175addd4f24cf5b75f583943aa2115095825facbd2d0ad369fee39e 89150 6508349e5b259043afe074c6e51cac2f6e65f9f7046e0dd93b6997be5e75f30e 16ed62db833f6739821c7ceeb2b2aefb5540a8c2c9d6ee7fb87584f0d794d705 +derive_secret_key ee47119efb1b2e4ac69c5a3394e24aa45766324611ee05b95918b920a5e49554 187339 6d4388cd098eab693242bb4b8c4f7cdeca2808b28ff82bf7acc4ab9caee0db01 18985944349384f75f7cc752b4f15eb627bb1198333966d090ca5104f69b6502 +derive_secret_key cb3065823f8983dc6643b3f0de75c8ee423b8f5f5aef5aa608801eb89de03f13 1 837f3d67294f06b90ddd771fbe23181d1193198ac7960f1197f36448e16af00f 7e911f6efd573caf51761a3dee305b6062c97f91c7789537f3f3d9f979ffbe02 +derive_secret_key bf48e0b8d4f60ead414e1dccacd480d5651ad3035ac99cbed5836c360fdb9153 413318030 44c2a27792f81d314a3cd50a41cf26962ae4187facb42e5651f009df9372ae0a 48fb542fd0a32a389f09af1fddf7b817827bdd61d9e7e516e8cd13561667a60e +derive_secret_key 603242899059f57e7a2428eeaaf0c91ffa84f205a66c196f0f93cb08931dedb4 0 1f889dd579b24f3a6d35d16daf19501a755daa78fea3ab6b6670726c86ef9c0f a693ea43083146143f23344516d2a62fb14cc3ac868f058f945e2afb923eb002 +derive_secret_key 0d70145ed2e7c0037857b1d58121066f3e9b98a35756ae00806e0856cb8edffb 2137 4238fbcf2771e2dff57ebef8594edfb9e014fe601d2bbca0ec9a8f14c556c10c 35a432a55673cc2f13fae1ce2987f4ce9f3c82b7c4a00080073446e70c8db200 +derive_secret_key 6f698e31deb7df32a6bf512004188e007d34e67f43f8bc4403ac90556f9c0115 230903 88e3c0868ba64580e6452cd3d1cfd068b7a23d43474102d2d0ddcaba4eb5080f a3661c1e29fd1fbf9690e1a8612c27288561629c4a8e227e0b489af3024d340a +derive_secret_key 0a86f00e25d7e964c5eb64bb4757421d0f2bd835e7e825ea3f6f355bf31fe985 4248 b93a4f5d3d5ebacd138099c7832238700747a675d2fc4996d099a002e0e9cf01 f0fe8df4248f6b8f4e79f4ac2b4106835b637ea739756a0aab632bcccc718a00 +derive_secret_key ca2e7ce5806c0128ab45c667022432f6ff69605b688029c333806fcaff018402 129668 4f87959a2eac4e3ea13855fe17cb1931e0d5d6510e6cd4d6049ed52ea729420a 9edb8989988a8a672c4471fb199e31d74e6a6ce2028660a306b30e2064d77f0e +derive_secret_key 1396d79ffd694d49dc15eb08b956f652fd2d25fd5c23ff3b86ef3cf82b98b1d7 257250308 920dc528e84280c95947500c3d18fe06f87fba9500089082a0fe4ddc7ae29103 8a341ed9c36f44fb46dd39e6d9196ae293dcc0065ca64689a61399f63c528908 +derive_secret_key 3992bcab854c3cdf3a8bc97df696bb62a382aa8e94fb12666273f0a0e85e3987 3 0d0542a8d132f80d68d62625fb7b8d9c5b81d27c3d1a6093633b776c5abc0e0a 7210dd1a0048018f8ca11c8bcef482ca32bcc83fae6f40d8ff40033f7ad9c301 +derive_secret_key d987f4df9c30ac5ddd4dc193b225e59835795e8ce89daf13e6933cc93d84d61f 168179471 09eefbb762e5a9e854c28707d6bb5938f892cc80a694c4603a8a8302b29f8704 2e44c92f63eecffbc4b6c6750857a684c1ee71b8c6fccf75f5f6552109ff580a +derive_secret_key cd25e5002c3bd07ae360f037e9682f3ca29ae669b772754c04776602674b3f04 2337 0668257a5311cfe23c39b5373709a8cc2abbceb2db37a3039a5982b73c32f903 83c9935ad27c2a820c5c04c77c8b534c7f0d1ee34a8003b1f18e38b126c56f0a +derive_secret_key 8ea200772ae7af1db624473c7388f7537b424c2e90b9d6416ce07b3649a4b9ef 0 5f0e4fd56879023d663f12151b1c6b753132278d6cc10c27a553b2c325d3d605 ffae02600c4f27807023757ec03f8facf0e2101fdd910be1ef3154fc3688fd07 +derive_secret_key 93f5cc1af9514f396ad539f0257f4e0ecbe4c94f3e5854bdf7b890e47b234fb4 59626 707d79a2459e32e5955d365be5cd0fa3fe560205fe7befec7324639498bb9904 dc7951e27b9d64fc2f11de7a90122c96f67079b73cbc37c13c9308c4ae32d80b +derive_secret_key 2e14f178c9de1f08deeb066bdf6bb0bcd6c0250edda5657833f072f9a1b2d76d 342119234 f2a7d1f00b3a7b4b698a576d68ca5612bef456598e3b6dc07827d91a6dd96104 49cce10b7265de5322f7473636cb9fb6e4075270715a0942f06a0fde1f082f08 +derive_secret_key 7d6dd4ff56ca516c8a715e4a982c44c8c4ff602424b4f3a6d7ebd60581884a4c 5833813 6389ea6d45687fa27f1e7281fd358cf171ca96d8921c17991b0a50541b2e440d e8ee5dc824ba2b678833c3ab4a43a1dd16e721a7c0e1313eb55a1d28c9149d0d +derive_secret_key ff3faa4967abd1bb54f83510c7de724f6fbb6f23b7efe72b2b32ff70003a15a9 1313 932910b86e2433d7507588aa3b0206dc99a87eae0aeb013fd1bdfbe865c4600c 736d2c8f748397f2f1352539556e3dbfbfc6c92d801d42232885469f4b57da04 +derive_secret_key 6677f03434f35c37dfa0914f3384ef7f8640ed768257195c51a50e405087a79c 6168 d81f5a6e180756ce82a24837fea26c64b54637778d2816b083eaede3e4361a06 b6f0331714bad8fd14e3ab3c85dd137a495517bdecf519251462eb2390f1e10b +derive_secret_key bdda6ad1883bc280016d7dd0bc04c3d6aa565c2e71dbaa6061fe56297db1ad56 7924238 4d07bd53696a66e8ed0e0c749db1c9618bdc009088679bf21e64953aea6c9805 439f7a66d880fc667b723c8c64d1ea5db143af2fa6b79934d66a923181e3e809 +derive_secret_key 2d3a4cb27a53e3f7e4f929879e5f85447e3761276b63dab38fd4bdf9cfbcc314 2 25548ab40ec5d4a315469b4d76f9d4effcb9ec674d919d6a0af8ecb12c5cbe00 867d80750c94f85fad799024bf8b00158598f3e3744618d7dfc3978cc4afa90a +derive_secret_key b9caa15e3ea954bbd64d08385ada57c5139fa9ba197d48272f801f5e5fcd28d4 76916 30edd7342f423027950f0b6e64efab937f7f1354138184a8951ce0cce6b04e0b 7ea7ac212621260a113688536ba698176a760e53c70612d76a0797c24db1d903 +derive_secret_key 419f59c08d5b0fd67bc057f8856652721a70b33907a9f255ad6bf286c28323fd 2355729 5a209ca51f3d572ac0cdf497708736f308100197dec3793b9f76119b66ebbd0a 039ac9bc1a4d430f8ae2d6edc99d4e77e47af4ceaea780a4351be6081b952f0a +derive_secret_key baa5c28592b9e8a10383a93c27928b0468d58fc5f5a3d4c25b199d2187723bde 58348 1cf363e6bec9bf3bc622a91c1d87fa8563410ec7e5863352035858b43fe68c07 4c65e237db037a4ef037af7e8898945cc245caa4cb1211fa3501bdc830da7902 +derive_secret_key 88308dc16ce80dcfcdc3be57ed1a8b359450f09e7f6a3c070c2993385df5709f 22516 f63613031e7aa131a87bd8c2753e9f7fd6287efc2f9724610c369c2785ba590f b591063166dd2aded4018f8d4fa32ba477ef827d761a8abcc0bf24599b561009 +derive_secret_key b67a4ddf0e657e9e6332f9b6f3992534b1620e7f92ade31459acb432b232f629 20635358 f0bf67afe36a8a33b995b9e00aeac1a44ee09441e13a9080146d52bcb410800a 0856735f1865d4c546cd3c3a24e0fca41317d3cab5fccd67e911c5c61cd7bc02 +derive_secret_key 3f1f58c2bbccd2eb08e556a3b575bdb0ae6cfd1d1757d90cb574768023ab645b 984019598 73f562f7e750d85a8233c62178cc8747beb41727fb30a7cf563eb397e372860e a4efa9b3d5e2342daeb1826ac8b4efdf32894355a57e15a37c01d8c5d6055208 +derive_secret_key 38c1ff4781e342e00359143b4edc0aaeade686106656283b5c93a78697547444 411640 93bd0b6c5a6f4bcf2915815675054064b1c274d76ef9c9d41f9d9ced8e8e290e 28f279557fc02fd62e02ca54c93c7e22afe4e70690103a5faed3783b7ffd6800 +derive_secret_key 77b27c56792662c4aa2c0a2f08480ff0a1abac243a8fec29bc63d662a3f1f29c 2669717 57c8c9e2b953386b13020b710daa6bcc6d84e5738d8e4e4477def8ac1fbd9601 cc31de20d20d2ad1eb9365c8ae14691b038518ec71fe37ba73d204054d66ac0c +derive_secret_key 51e193537966eb3c908f00446a0df13b5c8b952b13aade9646d2260e8a0a7059 1300760 9f8d6d8a146e2ad8972249d9ab3c8919387fa92b0a8e4e5c23d8557f42664009 d96abfaef015b445fcd3165f329e887a5a800e40240a565cbd25a345bede8301 +derive_secret_key 7f68d10f4a85b28d7352c7568ae07bc76d3c362b75f6fe44fb518802e940eaf1 2162 0e2704a3084808a1c54d1ec89a9cadd1a86cf0742ad4121e7a06a9a4d3951507 8cda0b47e1e27df53d7399b113c414a341b8068772d44fe4da424f0733fd6202 +derive_secret_key 3fbe69ed498a6889e2a1081a0f36ea7df2eac2ca49719d7ced874d2f3e08078d 25981 6054d0ece6e51cbba5ef8af60ff6534655879626181efdac5f5880d9e9c6e007 36ed10cf92def256d9b02c9d709ad6fe14afa5549ec33c7de038d4b83582b901 +derive_secret_key 7a0afad80fd52036072f90e357d7a84ed57c6f672cc11833043315273bf707a0 353513 0164d424ea0628213b2c07a570c56776f94bfa41b22445a840eac219d5d4cc0e e169a87376ec9c139e571419fc79c9d53c66ef95df3f91f913fd04ea2cd72e0c +derive_secret_key 0dca96fb7c78d1766178d4de47334d6ab82ba0219a61c6665b47997959870f8a 71845755 a3531a733ccd6ab7d0d7be4ad6eaa72ac925e50d0dd83eb96628e1e5e56a0200 4aec792f51b5b0c9ee27d1ec4c7bccabd0db7f20aa051856d57486d5d4b5be01 +derive_secret_key a2fbfc80166d74119443709e44ebb8985598c9d5969f60db1886141de3cc2105 526422890 e04338fdcc53b39bb7632db904cb75c80e90adf2c4577082813a0c837540d400 9db40115daa00de23cc0801036d22088ef7c14e761b7ab24de0889ce0ec17701 +derive_secret_key 82d86734fb7aba1c49321826b3759fa32a7cd7ccd1d734823fdd1d1f7443d645 167640 23414480f62a32b741dea3cfd3ab25a12aa2b8d3108d56990a15622af6751603 5e1d621b50cd75fa02483d87fbc05cc720abe9efb3d60f8862dd643f2d6d4909 +derive_secret_key e96b770f1658bdb7e27be9fadf9eaaa33fc00cb335a10d9cb0bb0730b2d85bd0 21639155 02fd191365a5185eaca7841502a1709dd127a9f5a17da8031d000e039257d70b 09697abc34a3cb9903b867fe7e883f608d3831a01aae6e483a44405b5d23500b +derive_secret_key 934b77c330e8f99ab8bd7fc92fbdfc388110b911b557e3c2220a5c7905cb46be 19079 f153d17a60df06b64877e03559f8e680c689db1ec96326f9f6dfbdc2c9e13308 0926bbfe80c168d79db4066fac30d41f012a893ba33a6be43b5a2135596fc70a +derive_secret_key c13735e926cbe2e0fb25045537844e267622790f8592425d4cb21829e310e6d8 36 e3cd8d041d72813607501099d590eaf861329c939cadfad51572558fe4610903 d8be13c09deeaa875ff8ecd1aa296a389956946a9299948bc325472565b4a705 +derive_secret_key dd368b7e2f42fa32467ca67060c09b1581c02b592736f5841e3dbc5e92c9635a 487047197 7ab316aa305ac968db5f692542e565419e3f8f735975313c3ebc6157cf49ae00 b3c2812ab25d1a98444d7f11c19336eb3d5ee7aa66eef5f31fe5840153b7720c +derive_secret_key 93959d86c57bfc53b23bd093470dda49ad79387a8e28cfdfd27516d862c83084 1 35f18d32517e9912307d41fe54e514b1eab1e8cb09e55c633896e9deb4f6f105 9143fed2776359278cde5e897e756fd748f23d4b050a978deb4daf45da093202 +derive_secret_key 6434ff37f9bc032ede0e83071cc28d897863fce93c2dd1215ae11f0b35e8be5e 15554605 339f3fe7378c40ba017db08af307c39e2fab2925229eb98dae27e78794bdf80a b8609058ff44c750d0e4d4d2780612b9dc172a60906fee3c51fc4250e910d905 +derive_secret_key 3dacdaa837020c8535a2a1549cfa1b297159d75e779a1f1315779caa89c42a16 1945800 00168aba2fed5feb2c88011b71e85147fce5a6ea12c26c4cad7ce3686ca51a0c 7587815d8ddde7b1ec9e6db5c763b1ed3eac25295ca25ad2db918cabaa05e804 +derive_secret_key 99f188239f5152cfef641a1f8c8bd5b5880bb1fcb45d884fc47379a245ffcb41 1794892 2e7761c4611cecdc9547a3dd8782a5b6d6eb4b8b6f7797171eced8e2340d8c05 2e4c1f6be03e8bf55be168ef6fbed16d980f2aa85e925c07871beacde56fc806 +derive_secret_key cb453fc5b59c2701174ff8e720ad14f347c53777871c2b1999474ad74038ef84 29332 96f1452478d1d6151a5b6059e35935d578c4393a8ec1f4c27c703522b7ddde01 07dfe82e3b37af3f8d8c3b179e2eb07a46211b03bd75390a291be848dbcd8409 +derive_secret_key 9cf2834d2979c993fa8a1ef0a0a45b0b8d441d2a6fcd85c33afd585e834f90fb 5 27ec6ac98ad809cefe44cfca87f8478918056239e8f6e5caeeb0cbbc7edf3200 cb70659301bce7a312f7b7da675ef4337f0da9f67efc2455925b7db1a8836906 +derive_secret_key 0654d3190c65cd249b7bcbedb3f9d3756e410e49d238f3704825ca9aa6d689af 0 fcb89854e66954a9a031819b140a84288907c94940b7989ba8e5fc667e58f501 1c76ffb631690207d38d77204f20305f587e100464896cc5eeb460b85bdd1403 +derive_secret_key 9ab7d205c751d23d8233e6af3c2ad1aa8c2729001dd41bb5dc769f0b3dd8946f 161331 565e52084c3595b719e9f85dc49a7960cfc6fa9bd714d3ba21e463bfb059420a 1d1316db615baf7b7ba6e8bce55db6abb336d3c65921b2e7ee7deb2bdd5d010f +derive_secret_key c63e25ab4196be6aa4b213db2bd67bab716cfc52450c4e5f51a0a8c2f4390802 51253 94bafcb6a795f3b4d4f4202fa010604d120d2914bdf4ea68e26e2d7901efb904 4ea687430e9cbfbaa955191d26aeabc087bdc3c5706a06c79ae33d291ec91906 +derive_secret_key ec5ff5ed9be705f327dc08bf59ca6eed68f0ef59f0e824f3abf11106799b3f83 612862 b908ead50e81e86aa3d9681a71e9d4642ccce1f777b3bcacb1be0e7463010b04 0b41cd24c168909390fa4b5eb85dd60c772e7f80df165c827d778b3ecd5ac102 +derive_secret_key 0cff004fd7ef2500df4bca6c4db507b36c73bc0a2cc980ff0f99b9e3b4f75eda 8188019 4a570629d630389d426fc2c96d56af4f8165ca3251405b798f420c8cd36a4509 199c0a1b2af8afaaf76675f0053fae08a26ed8f1238e61edf11c5b3a9a3ccc0e +derive_secret_key 11521434b7608226f119a79386361c4de80714572bf12f03ec88d1cd5fadd1f1 624899 fa1ecc5c357dde47872fafacbeb0854504abd2dae80ef433ab79bac164343c0f 225a8f2753955f33b1eb3e09a91c21296896165ee5d6cf3a87ed6c231b92af0b +derive_secret_key 478956a0f76e25eba758de7bd49222466d21f1718ddf318451d15f8e861be215 31 e6a58a04852c3cca56c960f1566a0e2dc5a62f1e208d3df9aca90e2c97c81403 9240d68bb52aedd35f632e657fccb2c10eae59b98195957b3d61a67f05b10f02 +derive_secret_key fdc4055f3830d5605fc37c19a673ced43cb3402497c4758f4223966a42a99a91 125 c2e286f495a2ee5c792d30522bb3081aa287bfaae2ac45dedfe774e8e76d5f07 166631711015c607c23d054c802104e542de1ad69bf19a40035c4ff88258ef08 +derive_secret_key 39f00e375b0aabe750382d232c748df56188b0dc03d167c1db5f0614f0be94ac 10117543 6b21dd021d14746b39ded7398a815884045d941a6692ab67c38eed8423e6fb0e 89ac53b9c7f01dee2f86441dd4c1366f9277f451930523225a4a594641fe0d0f +derive_secret_key 67f0e744a94f3e28b144a1863f32460619f06aa5f985f28eedd218216a1417e7 36217744 030fb08f2e7f679f898ec1b4e6eeb4ed7c3aab345eb302b41fcfc274be62de05 5551490105f209419c14eea54efbbd280dfe2320539bdecfed7a58b27daeaf07 +derive_secret_key 426f1585e0d32d28a4e9e92267c407817e4cdfe1d0bf691a8b337b84a066c659 3 ef777b31a1d616513763cc59c7bc4ddae597e27a59b3e99b38caa38c9852bb08 934816de09d2d8f9af94092d1b29016ba2b010e856fa21262256cc6c79d2bd0d +derive_secret_key 4e57ef681394163e4257d08642a068a08b765a93f16a6d8cc391d6cf4e89661e 25852 ce4a1d8644001fc1b991aead684f1e3cc1d00fd5050571a2ab01d18aa0ba8e04 0764b602253f44c48e0a9d4651172c05a6eb9a97a22adcafa2104c22daacfe03 +derive_secret_key 64bf48319ffb39f6059a0546d5dc368f05799837754bdb1c30ceeaeb64084f6e 3 7b2cbac0087dfe7295c20fa313adecd4900610ef676d50a869aaca54504d8007 a02f71f6710d20dd6e661a74798b83f6edf90fb6438618037b0d544145a8e300 +derive_secret_key 6c3f8ac6c2263967a95bcc110ae1848011512e90974a0211950690b06b1ce275 203975083 1a84f0b240f59b85f4e98babc695ffaa22713686efa48a1353bb2c21ff07f208 aeb1a2f17da103405b587b5f14c1452ef75ce18ff9441be12ecf5b2dd265630f +derive_secret_key 2f5650e3c17e7b7e1beee26fda99d721332f30cf0387ebd86eb8bf4ee03d9af9 8833543 bbf49f6642cab0c6318f2b8d199a78f0d0bac51dcddc75c94f7ef06e5af76706 4349782b1ea1f6e7ca39f98d29e4ccd250a6a7b6faaaa1c1e535cae0796be20d +derive_secret_key c0fc5105f43e291e47fbd6e9d70de20a772d423240cd57ed2e7af8364f5f3359 38003 720fb47cf005e3c687bd0130d4a1617dfb17b6525b424ea087f1a93b46109b07 1ddffd7caa5227170734e1c9582e6a40936f8a6ec5ea29ff486295f2aef50a02 +derive_secret_key eb87edf9e05d189a34bcf2731da9fa295c035a2d1fa7c7fbf2366893b40d0eef 30081 4dae74d24c3816680bd46b08e7e93bf4a2b048b5623c5910d23045ab7cb9800b ed170dce1fc647d21b548491d154124d753bd389e9b573d95b28edd0e9266505 +derive_secret_key 6eadf9c9528f6024a0849916c43b1ba98bb79d5e2289d28f02cb2dd85f74c007 1 4595056e818a1bf2365e7ace79c41a5cabe028c27edf7870b0af12b3572df80b d01a526a0ef0472d5f3279066a2b1e1c9071db3fdfb4f1bf6d36acb4a74f060a +derive_secret_key a1b0f42bea1aa2d2ef9c2d9f76baef48902f8f5282beb04786926e71f3c9ced0 38648290 4df7a03dab5792238145151bf62b88361b50890597e03a914ccf040ef27d340e bb13368eac849fc8f213a81a7682bf6e13200dc32af0ab6fb1df92d313081c01 +derive_secret_key 9a14e65d7d79ac0aacb6eb85b987dd66ba1e4c6fe5c78818521200b18ad1456d 10070 2efc32e826c042901d1e53546db1c9e8091adfd36b260c801713dfd26237ae0d 4756c1a570532e86bb2157a8b726acf7faa1ac43c3d9e6970e2eda4ec8ce0309 +derive_secret_key 7281876df678150e5ec9ee5acc9c32d2b2755985944f943bd057efb08f88dd59 45 08c0b62aaddace24653d74a4e5db11e277bc312396cb3ca842727fba5b87900b 1aa0e72745e410ced7a64e88fb80b2cbff7b532ce24e083d2a88adc50c60b709 +derive_secret_key f64f15e21b138d2268df0c85924737e3a8eedf35cfa91e46dfecff9946d0e928 201 4a834c024810b899fbf9a129ad379a45fdb4d0e7dfc47982c476c29ccb0f4204 4a1c6c064ec4fc425a863b623d6175483ffed827ee3af26a7ff2f610facb5a08 +derive_secret_key 0c67a00902c17e90ce6da3fae35911b7328b2a5b9c3b610bda2b64ead5b369a4 119 5e886032aa7edbba45df6acec5cf0ec1bd145a8310117f02a8d1d97a7bf8880d 22f77677d14f36a03aa02d4c140df2c39e3b42c0268636dc2177948aec612f04 +derive_secret_key 9e1b57a77f90583ad26f48650dabbd7300ccdd1ed991b9a8d1ef0d07b0fddef2 3308757 ca28a72a9be6d1e0fbdf2842533569ac18d0b9f43c900d32e9a3b4ee37611705 3243a7080ffced7b38cabf28bc38105e8bda4633cfe99f66f856690fd6e9ef09 +derive_secret_key ac0261b7d681933be1811af766b0b0df0042327b9cc50098b178659fe5b74f8f 3 92116c8ccbe40c150059421dac8009c7cc4317aff24823373dc277fd73c32d0b af07b9e9568cd8a98003c52b963ade977fb09774809ad15c00f70a419765750b +derive_secret_key c183bc56a9dffa5a297e6147f3809c44b5b48ba9299eb88112f1b1feff62275b 1 05bd22fcdbe9bd633e9da8227bafc98c3c8c5725820299b8fafb6c9ee967820c d825f70ef8397b241599d0ba3de7291399f3e8eb5f2a0cb67c61630ac6bf8408 +derive_secret_key 9fb1693f9225c9cf4689f34fe9a1039092d56843129c97a2196f12521ffb79fb 2588 4299405f766344d10b115170ed237391246d76bcb01c1e90124ae00636096604 f03b64478d1683dbdfe39d63e9a5e31865ed37f5b7c8635f31057eb3ec17ba06 +derive_secret_key da01f7ff64bfa38a4a2d20555583450d4a98033e039087997edc29c5a7d2eba0 2 ecfc99ea7130198982bddb11fbdda52447505c564a78351fafde01b80588770c 2ca65667e375b86dfd83ad3ecc3460bc26c39c8e3ef9c7f54bb900561e6ebf04 +derive_secret_key e472f61a2a17f7da5b72a6e7b04ef5a2225abaf8d7aea0fda792d8f01c330ab5 6331656 daaddb803cd9089f6c08a87f8e1d55272e5014c8bf0ef777f919cc648e5fc203 0c8cbce55b0f565da77e1b56d4e7334f588426207d2c4ec6cfae9f96df666f0b +derive_secret_key fa98b0d493a7ce18a566161962967e4dbd35a597b8cdb38991780da3948ef2ef 136923 587c8b178b3002c9045dfc6dd8745205b42bd4d565b9320a8664212f52285108 6e07f3cd4846f4f7e1271644090f7cc9c1beb5b283f0a3c8ebd05f905533a002 +derive_secret_key ce644b7356756e7e614911689cb2e3f4a8becfd5c1651c4b006c252184c6442e 358 1f427b53df49fa857fa1b537f65dce11cd86975f82330d8cdc1c74df49e02b0c 16e2f3754629f1b15b30b8e1391a3eb84b3ceeeb26bec5134a72d60e41631707 +derive_secret_key ccf3d0ea2be93756229073a1a14fa1eaaff0f8ab46c6ec8560d2be9cb3162c91 5 bdb2b8f57751de35f57c4a320efe115818e8412dc447239ab3f5d339e2801904 bec22dadadca68dbe871c41dd392c869e673ada7c6578a0a20e7c31627c53c06 +derive_secret_key a301673ce6fef2f4ecf175959a4a291faba98e6ffa0dbac534c9dd5aed1b26ca 35161724 ecedc1e18fd35ed69031a1df663c444c9fb5017d0a94b1b23025bd3c734f270e 0eeeb9507864b6df755ba5deb989c2a1304ba4fdc8f65d38e4c6e3b44fdfe80d +derive_secret_key ee40c36f08fd4600db495575d4e015a69ad8375e27bcfb94160d523cdd020b57 4857343 98c9748ed9eb9dba717fc2d40d2939fec89c40f13bea2ba9ab3ffac3f2e4cf0f 7461ee0f3db6fb5354433f48a5267dfa769bc9a2ae7db3b4b17053841377e600 +derive_secret_key b50fb9aee6842f6ece8549f335f983223849e6ecb1e248b1bd3e1359cb7f099a 94077559 3a7335d518c0c8a3a50c321468fbab891235654da4e4ee43819abdd267c3f803 8d76a5db31e669badbe813efbcef0fb34f1737e85ee855ea0849fcca5e87c608 +derive_secret_key c2f62cc247af67e2e57b6de9f6613e0e1209c1a5fa487f717d7cee6b72b7acfd 105901 34364f71e4c45c0b3a274bd6667b94f9c48b177c028b47ad7d16fef16404240a f155b014729df9717250c9660cebe798f8245a0efa60a0636c9eb88beda8a507 +derive_secret_key 5dfe67e9e2fd44e513651a7fe28a02e02e4c01ecfeb6bee922fde368ecd0c748 16 55de6209a387a00b9808a358a1021526dfd9053191b2342fe7f91a89f37c5205 77700bc34100cfc65d24b746aacf6725ab70253f613d766f003f1522cf01c40a +derive_secret_key 645f3f1a71da873450624bd78345a76b3190f969312c6b04777c0cedde7fd4fb 1397474 a5c27c08100344f5f0374dc9c47610b2b3d50e60c9f3cfea31cfa11914939c01 0be6c7b03c03a96b3efdaeca9dff0b0c2ede8b3148b9fe21f838befa81a48300 +derive_secret_key 5980601bf7640cdbbd67f241ab83a736d11ec07f585de3e0a73096745ced09ea 56159695 a7c8ac61d36e5da3171c9598221edfbc26466818c2dcf6416b11b9c2df30940c c0b7ca34a6b28fe3e339e8079e8f2cfba15684da31fc56b0c6bfba863dd7cd07 +derive_secret_key 64c63394ceb07844a0a3b5b45082554d82ba3621189709ff25687dd98b0b51e8 392466 b65f4fab5ebe68026cf94bbb0146380d87c3fe8bbc5b88b2403f66d0f3cda109 13856bd684314244bec3ba59142d13c73cb33b4d7534f8683f9a8d6165220b00 +derive_secret_key 273c196c1aa7d2a3b2e21d9eaa9eef7ca376d03e6812fd70f1c83a8f7cc58008 476805 7615947a236a05a182a4f1657037183701f2c925d41bbcbed3d91afe1fa5e50b 5135cea95ec13afa56e27cf8f9378a86b6186877b706d654d48f70fc9ba82800 +derive_secret_key cbe41ed09a8dd1aedb3979548ad8b62e5a35545e72194d299c4b2037db36e51a 889 8d1cb5c3493c09336b11dac5ffbea59e5b4094ed3da919f8ec097929c9e5ac0b df5d92cffe80c65e950c994634fdd233d19d72b005c2f72565644f9cd42e510c +derive_secret_key c16dae803cb33cc12a1eeb33871f728a962295c16d71209e0451d8256e7344ce 1015429 48dab9033c5fc6ca2c74d6193c362311cf3a1833b70119937be882847d35460f 69b60edcf62afa979a937bf3a886b33d734a5fc1c01d9f66fdd07cf51360c401 +derive_secret_key db25f26f693eb083be0de07ed6e50e8b410698b5e85f41ca9d0e7bad77b36ae7 1054031 4d5f20d86af135bb9891cb7cb5f68d3ffa5658439b737ec7320c828a19b88306 ff91e148cc7828cdb52aa0b34108f5bee7c67a984d4085102cba64596e95f70f +derive_secret_key 723050c047b092adb7035409ec38c4968e3538668cc6d978a2b39a35d3989640 2102325 916af8e25f3674431a0fb9eacbbbbc9ceb67b6fc2e9699be58971e576d59c60c b798c65fc08c94792f3b5ebb456207b813cc1698ab1aa5aac0e6bcec57b85a04 +derive_secret_key 77825ee3f691c4db3a56830f2ef9b9940f035ec98cd23bd5b9be365f503b0328 257119754 99169c118e7ac98def13f0485bd4388bb2fd5c313612042522b87fe1b5ca3f00 3ab04afc9f52b851f0dd141bc9ac66a1477bfe14c087a2d5d5b4c915b330e902 +derive_secret_key a6e7993e16e6de916043438feed2efe92acf8c291fc19b276141b01395eb9881 229841262 b77cbb9e9f4b68e8330a99b35d3ddaf4ff9abb5babce53801e506cf213817e04 5767fd4f7acf0ea2191c617c0c55439089aaaffd549d97873aed66509f99ce0e +derive_secret_key 5d605849f3fd1fb3fa37c6f8ab01dc23e78f1bf1b5a45856bcd7cabf0eec6ee7 2953122674 14c0a628eed1338d59d01706640ee30a83b6a7f8ffb2668ba0de9b6087b56f0e 4fc7e90095c23e1a6355890cc16955f691189ab6a7db516db6d52d8666b2f30f +derive_secret_key e6855325471a9131a7b3aecae1ec827ae8e3f3ff620485aa107d843fb96a156e 2890049 9d46198acf9acd79c9b9a49111c971b846cd14973e8f9b4b3f16ef068c91fb03 612ce6b8c52a0e86e7134349ae725ea6ced209acd123c47e95cb3bc05f4b370e +derive_secret_key c75767aad4472c29bd6a081e51cf242e77030d6b9f832492d619c4ded482f610 6536 392c727b4604d353bfdcf6148163578cdf88f9cbe178f1d43b10878c0380e805 9315578ab963bbf2bb0b6ba2443522b8d1adefa0fb66b465f2c70674caf6dd0f +derive_secret_key 509f7069fe2713e90e79e2daee520487a67b2c4a89088d37b19351447fe2302e 0 65b898b4973a20ddececf4726df512a99f1e61166f07c168f52c0c169d5db008 5c59f14ef2673be46e5f15972064591e3a2d70d52e4084b7e82a6ae7adc89100 +derive_secret_key 1a40c72eed47607c715b16407008cad25fb25b0ee8e74650d1934c07bb430952 140286 b285a4d1ae55dff9bad1f8329b10a4197d0e12cfb6adaa9002d39c25eefd4701 b052a09199c33f8f645bd0c6c76e7e1e7dac3f52b37acf0067cec599460cb301 +derive_secret_key 116ab4644dc54c425bdb27323d2f6df2206a8c566eac44f9a9aff46021ed9f68 122750215 697738630f995c331005515438d32f0c29e5c4ed9d5f3c7db04b658b20cc2007 eee381401d20d504685b45b1186ff8ad9b45589804804afff22baf3665483b0b +derive_secret_key 30fbb2bcb58ede1fab7bb10eaba8cac4c7ebfb940a8523f736dbd9a634e5d431 51 ed30b38a84deba99e25af48e0af3df94e7ee38ee47177cda6ae060c8dbf64206 7029294e161f768c6976c752d72492d79caf8c6f04cb35886f736ecb000b4303 +derive_secret_key 38d859cfffd9a25398ceb9c6555a2d8c6ab0b271331f0c022b708a0764e26c24 11416982 6b006c311dfcfc42b7e0db0c7e50134599c793ad81b8a2e9818ee744a1fe3b07 92261ab6bc036645235e93756a41b64d8d9eb5c8488279a6b7ccaf2f49fbb805 +derive_secret_key 550e0a560794bf08387ece09d441a83ce40e1b73d96ae60e7e68f12b783ded94 13663 58c372eab46af8d3cebcedcb68540201600886d006301956fbbd90d05c9c0c0e 3b4b4fa24ec6152cfccc926b8e5fc466191e145939fe5718c165b59e02d42a08 +derive_secret_key 928b90c3cea1952f1db1e93525fe1c5818da9b2fc9fe99ca6048f3c81c821c5f 1 7b5dd251a0e4be9219f7ac672b26f18a84f57a7eb1bb4f28f124a01e0f50ee08 38046dac3fa2d62adecd94f1daa29032c7b249fc16d5cb10da294e76b07f6508 +derive_secret_key 1b17a7a32eb38423815627e0244b499d6a08e3b7a3a4394cf91c57bc4707a67b 31 13b455f8a887382255fa3b3d175036402198b6fcb5d92be8e59ca6f2b6d1dc0e d898f66647b0798e541890b65b63e5a5d22ac76a436912c0b76d53d0efb06700 +derive_secret_key 5c9743f88e23600a8bea903943c1738a35421edccdb188811773f288109e488d 14 3ab395654a082020562f7ce9062e94096105090099400332f2f7aacfc10b3a03 60bf0af7c88d526af7abf706b40c3c925813ec287b80e1f9a4c62269d2f5970a +derive_secret_key cbb9d466b1698277c9c9ad61cd1d00851914d8c024176e1dd0da6441be5068d3 4489 7a5d5927c30c2e74ad86ef567ac226ff47f3d0d14c8326d7fff2fc9a8b03e80b fb4a51d5db06f85227417de64c3d4f5fe029fa2622919dd6613416671fa0250d +derive_secret_key e91f319db566a225f9660087c4bf0df4f94b0787533164e052d9f496c5d013e3 2 79294dcf7f814b81d1797c1f804fcf97753ca03068a1f3c0511a4650fcaf9e0c 86ab97c17bd8e50debcfd35cd38a34ecfbc9d2f011a361d87c2028306cb04909 +derive_secret_key 7368d4d8f937b37b60288ac1d5a97f0b69cb75db465d5592e5a999ab6c632fb5 14 44369d0da85b09db1deb38a07a4d3ee3a6698948d7492852b5e261d610c88a0a 3a00cfc8c9baaf2eb6d44bed54f6fbe7aad0a2389a5aa599ec528f206a28980a derive_secret_key 72cbee826e17d5f9f05632a44c806c1482293d60bde2bff6b5ca15afa76ff400 19 1a558d41cfb8bf22116652eb7cbaac3f5918dfa7191ff80bb8bf6dd5f8226303 4f32b5d18f9193c011e1f439862a84b72f2ffc49fb659a0f9230061f488b2a0f derive_secret_key 4d85acfc3024345fa9243f20318052faaea6aa03d3c84e86cab26452bc357b75 302385092 b791136227554c2410049d0d1cc9698736d3e31908bf5fc83724452f56666706 be398b1db0e0e80a90b89998dd182776927aa944b8b844e16e4bd2037e218302 derive_secret_key 35c496baf9648a894b2c08195afd11ee6d918383969db2e79eda9ceeec3f81c0 0 d44a6c9f9b3ec3eb171de07d7d96302c31204525827cdcf8991907c07f914f0a ce8fa37e0e83be985f7e9eda81b64ad757eec99f60ad16437f01a94f7f5ab101 @@ -2547,1030 +2555,1326 @@ 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 -check_signature 57fd3427123988a99aae02ce20312b61a88a39692f3462769947467c6e4c3961 a5e61831eb296ad2b18e4b4b00ec0ff160e30b2834f8d1eda4f28d9656a2ec75 cd89c4cbb1697ebc641e77fdcd843ff9b2feaf37cfeee078045ef1bb8f0efe0bb5fd0131fbc314121d9c19e046aea55140165441941906a757e574b8b775c008 true -check_signature 92c1259cddde43602eeac1ab825dc12ffc915c9cfe57abcca04c8405df338359 9fa6c7fd338517c7d45b3693fbc91d4a28cd8cc226c4217f3e2694ae89a6f3dc b027582f0d05bacb3ebe4e5f12a8a9d65e987cc1e99b759dca3fee84289efa5124ad37550b985ed4f2db0ab6f44d2ebbc195a7123fd39441d3a57e0f70ecf608 false -check_signature f8628174b471912e7b51aceecd9373d22824065cee93ff899968819213d338c3 8a7d608934a96ae5f1f141f8aa45a2f0ba5819ad668b22d6a12ad6e366bbc467 d7e827fbc168a81b401be58c919b7bcf2d7934fe10da6082970a1eb9d98ca609c660855ae5617aeed466c5fd832daa405ee83aef69f0c2661bfa7edf91ca6201 true -check_signature ec9deeaca9ce8f248337213e1411276b9c41e8d4369fc60981b0385653c0f170 df7f028022cb1b960f2bd740d13c9e44d25c344e57f8978459ffa3c384cd541c 2c2c8e7c83b662b58e561871f4de4287576946f4e26545ba40e78354c6d0b36f69ea44892f39a46cf3fd5c2813cbc1c525dac199ada6fd5ca8e1e04cff947700 false -check_signature 114e8fffb137c2ce87dd59eff7f4b8e6cc167fdd28c3ea77d345d2c8c00989a1 d257f46216be34be5589e0b12094e643d1b31bc3c50e006d044d1ea885b5007d 9579b6e8dc108633ac8b67004699921aef479b6e7ee9590073fbe1404ee4b3d533dec29fd35540f13ac531c3ae49abb62cbc11d36b0cc3353db77a294d8d3d92 false -check_signature ce03e1fa5476167c3ebce1a400ca1d2d375176b5cb9ed180913efa1a688ddc97 a05a3a6776f85c5d04c42fa2c6a731831c3d3a4e3a12f967f9ba0b1ecd1aee98 4992de4fec265113710ec3a211e86784581f96241f0305d069a1e4629b504d03b3a1561fd9e73597db89ba00beeb60d2107c1f835176949bd354e8a173d46705 true -check_signature 7db838c96a3e1fb14156986aef37b70f932ee79d3cbc8233cdd76997eaa0c0c2 306593abefdbe99beec4752ebb135131a93e8361fc35f60a1c56fc4501c6782f 5bd47b285d25ede033bc5c2049edf3feb06fe29091e2c90ba25128c6c1a050713f28db1b9106013d22d5e0ba05bbaca43c4d30b6f0bbad8768e6cb89b205c20c false -check_signature 2d96536cad13a409d5a46a6bde1f5cf1d9255e741d5a17309248dd910e02d1c3 c2257e5386cdef44b989ce395532b8e03dde166ba26c18759e1c440738242fe4 2f5e7a5c690c0d3bb2974e47eaa159d0bb2205a636b8cd09736add9fe8d75bee4249b30f8e1b99c1dea45999842f5709d2ee1d8e450807319723625074c69605 false -check_signature 87625d63fcb5653575416de00c82ab4d893d6d5b64bb79eec564f61fe6cbe4de 8d4c678d19b93fe24e90e1e78ba439e3ab8c3d6fc8c2eb89bd343b92040de1b3 02f424dc88a9fa42b262707cb874d1e480f74a366bca7ae96bade60cd0c9e5515e993b892609b1d8dd2835177568911df548717b0396f9ecafa6d977dded2303 false -check_signature 83824c2a42fd2d1f9f74106ce128ea3402261eb67a2cbcc74603b8fc52c694ed f5834a5bffe7f8fd69ae6626e2d95902f470b309968c65834cefae251c408425 0e652aa0db24b0346fb2923b9c16d94f7038687a66e491c2dba3aa62a5ce27a3f15127deb15af6f02190486e65f4e48946a2dcaa85c54ba0273ddd84815e630f false -check_signature bce37b33f7ee148282c3ccc12565da4f7c53af9646df616b17d95631ef209d14 13f4ba800ef35daefed6b8e54e865113b1d523a8719a5d22f596fc12203298a1 40cd174bf8b7a7564ed09b8f5c15d1b2134e5b8a36ba907dcffa5d76eb201c842eb63d40eefa4fb71a7e6f7fdadb96229daf56a121e64484e12e2455a12760bb false -check_signature 1d7a83ae9338726c1b6776f5fafdd78a79df32b1f8bd2702727d9c17f8771caa 42f746e23416bb1ca24b486424c3462291d9d6fa7436da6e66e59bae0ab43162 9417c063b682475fdc01a623b1a88043f176b1df0965b28b54de2712193a740e66f56b22815a05bcecbbaccd5494d29f7bbce32456201a8a21f0cca1892187cb false -check_signature b921adf724750c6305c33cf98dc866dab70ac24ac2c6aba47b9cfb621aa0b691 53fd594636a7e10be480b78eb51a8188ea610be58fcf98b16202d2481dee3662 f624b6b8bda7df029d85e5c9a4a2a206fa84354ac703eeedc44a1f5c9d33e306bca16faef19fb8af6f7ff8aba005092ffa1781592ea0c193f5edbe1ed2bcb11c false -check_signature 06f6ea8e3a4cc5e6da4a5c300b8056eab7d9a260bdd8d445146a906a46377003 d4f328c185fa9ef3398acc0e62a47b738f9f799e6649dccc0ade5f6b0b5388bb c26cc73e7891b1d1c4c7e8166c835198317fa6c024e245693ccb8a9a456c9b04edf11f00b64eb317d1617be12d1d49606f25195017666db6242a58e98f839789 false -check_signature 9409e7a481efcc779b8ee25b012ecb057cba3967296ff2e540c4c0f4dda0e82e 2c7814a245fc1a24ea25ce395415bfb715a9c84832d49f2e560d108afac578dd 567acbabea38bfffae0a7c2b745c574197471e12b239aa410fa77f497a372ea941711917c6018502e340860930494c112cd107d0f6bf48dea68c18ed6b8bbce7 false -check_signature ef83026149b0a2f22f2e086c8d4b8b237b25b82acef3f90214fc0b2dd8434516 6ea89899209ea552694676dd68debaedae8188a6bf0ec95ef64ff8ccfbe79a68 a00ac22a9ef1cd65832759ec6d5f868c38ec719ea6831090e8d93799929eaf72fd476f9257eebde6bc449d942cf20ed71d14bdc1852521942f2ddc4690a21bd7 false -check_signature 1b246206e60db2d7d2f54356a2973ff6dfc8921229923f57ecd2096f087952d8 d7b213889dec8fbef903f80df2b7e96881eb8faf8267fe0df3041359230aef90 b81181559d0ec72984bb9fba9e1ef5eb3c3904add1d57197d19a04f0b3d5d5578f4ad39becb2d1d12dfbb0a9294df4e0ff5eadcc29a7a9d6d52920c7cd5ac70c false -check_signature 6d18e81cf4dcd5dfea5b12c2287ef3317089aa5a5eeb813d4156ea08958db8a3 6e3649ed3894b5423adecdab1d1782be4640a92ed310aa2199c5861cb3405e96 e99b6acc2c6169e1635adcfa55777c2c8b3023af17fb4fbcb2ed44435ac6da10afa8743f402cea715f4b59323ca6a3d74df2dfd955194f8c1574e4234ac66700 false -check_signature 9a42386a99a81416028a9fa66cc49d8aa66af6982bf709e8d5c46d60f5eb57f7 55290789bf61a9d623dc8453275660fa69f753a8dd25836dfa1a55a836c08659 f67d12a33595183573e2708e02df73e301d988cb75bad371d59ea92a4304ea8ea879a5bdfec3a68b9ce0d53bfd72808567ab897ff8758b18401ed3cd688459aa false -check_signature b49f3b74d8da9917c016e5ac06f8437b671463b8a81b2671243df2b2b8052fb2 aa248b65faf63026d2bd3e159c7273a69af4d1e2ed917d4cedd0f24eb0d61991 ef92065a1f1e51568ae40238a4be93f811fc06885193c60d85b76dd3fe68b6d63eead361ad0090e2e270d5d2f199c6b95d19f9942c21cfc20fdf313e43f39b03 false -check_signature fafbfe9233c1be9051ca8e2ac1b331b8341842d91de0f802817a5c30c234f391 416f5246a8a8a3402e137a8befc05d6f02db534e67461397023fbe0e29b77aa2 542e9b73109b1febc0c9cfa6384e43cdc0f0c170c746849ffccc626232355860c717f1c4d11ee6fe90a75b0294fae2dfe025462ba44704f193ef54092e9d5808 false -check_signature 61154b91c6610d2d001a1e6f6f2652faecfdfffec237dfa844ac799b393de4f5 14c3d2ad74ba443296102139cf94b421a72ff62f2b8727e303e53d6a22ac7faa 9e4bfd899e87b3b88b7d39f4546605ae892675315b0ed83176edf9947924ab069750696702856e7ecc1561cefa65d149540223b15578b378926f6196d8572f0d false -check_signature 40e0758cd9c9f8f8f7d0fbf351084863973a3622c92dab501ffdee610278f86a f79812b95048683d47eb5435bdd97c5a39532c6693dc8b965af76d7f3ab88e92 a9ba1cb8bf2898e21c12bfd23788994fe20d45ef6f775c197ab157d7c2721100f2123c19395f13ff79941e4fc9ac33b2f70077a79c552b4ebc97a4321ae66e09 true -check_signature 3b49a4ba1b62db697c7826a66b3a24f5c00054ba8c212ddf6094654059ce973e aaa6eebac75c052fdf2abbe18e4718c3b388ff919bf4a514ab61bcac661b4409 5d156005ee2588edcf470dc653a0635dbf3afc393eb2d89a75054a93b271ee02e46d532ac2d65d7f661113093a68d2ce6516a5abf08231104d0fdcbe6649e80f true -check_signature 088144dfd67a5cd013db2318eadf14a2ec30f7b11f75e687c3e76aac5b0ea584 21e031f6f3d406f53e14e57741c9861ca785cc9bc6fe041aa8c2f0282e8db52f 44c354bb8980cb86ff17758d9a834cd1f1c6b82b9de7a2d074f0ab8dd3507d221bbd53afe01180305555444a085d0b73b4353a26fa980d594ae33fa4600c120f false -check_signature b1930f6d59fe22b002a0a822e0ca2f3168f792e02f8be89e5d565de7a2eca4d4 43e773d2046d30e002cc231c85057f834d508bae0ac5eae3ec581da291736efe e5c0205493e59f6a644ecd4ff30b2892b266a26f17ba9a00f604fc62f1c40d0313ce08d0c303a8696bc638ee0bf6835562cfdff9086bd221c236f7441357470b false -check_signature 916ceda80ef84b8538e06c5fec7f04810a960d878f31c390705c6bba01814a93 86fef19663f5fca7d6c425944140a2941c2f2c131cbe881868bebd1e733bc432 68fb16d0429f61bbd63ff328b4defbc7ff03b5d3d3b83618da159fe02a808f0a22c86ea632e4b7b32a662e70bb7bbd8fb381fe10950c340d82ae5c0931b07a06 true -check_signature 9f2ca44de5b87bb7d90cf57dedf6c5e6a232372b28e9542be6cd6943e69984ca 9078fa24f1b212816e0c9f483db8004d2a6f6d267b441292218392c5d033b01a 6bd88d6d4b2a2e16e823241fea53e2d50625506b89b1067b9aa038f8d55f480ff018813c937ad2a7a09162b7538e179785b9c40ed6f4367b30cf7d76d076960e false -check_signature c16ea334df0501f4fa8e6d31fc733cd9728128759d77a94ce662f343fd2b4b45 30c16767c8353a7e7dd0a3ca1449a0cd3756135970333f0c96e7e6b151e00f54 548167935d72bbd001a9d8ffb805d3ef661453e4d39fece6b131acbfa3c1bb0df10c0232da00ddbeb9181b55d174f3d38c423d17227e9b6265f23e33389a05b2 false -check_signature 7a94605c0d43ec0f35eac8963231dc1c08b972045007d3d6c3905e142b3e2829 5fd4c73fd1f146f41e16276ea18919b7babecb5928a06acee7e24c006cc2417f 1d6c46b9de850979c7a7bfb5304072c3c778027801a5dccf19d78bbd5741ff02b743f1d88f7bc3e9b32a054f6e5c3334d456a69b5a943bb9e52f046748e1d0b5 false -check_signature 9343ac2dd894b368e76537fb7fa61672b6f2d00d7393cd4ab725218f59dbebeb b1e869915645764bd9a15ad6674cf11be4371903cb6a63c412c1379384938045 c0d2b432ab43a41c03d923227f7e5fa6367cad41bb34c3408d42bfe2a6018407c3bb22aff17b69dc4cbb95fc0743bd774ca7a889f72b911418c38e8a9d9bef0e true -check_signature 70cbcab50667a8840352d1a38e9871a517d142da5174e84423e55e0dbc7ab634 076daae7755177728b0e21499d3520b86777e53113547091ccda04528a4e8cc9 9943b01dd217dcf746ceb09647807001de56542936169240d12700451e309e0ff1193f04ce3848b92bbca3d306f9ca9c84a0bd4dd7342aa68bb94712a87c2b0a true -check_signature c620fdeda0c01518fbf903bfdab96296b6d68a6e54f77349de9dd0a89b8f2dab f45f889e87b9cdd172424336880a16fc99e8a72e5a68b19d503416d276aedeac 973aa8034a1995ffa58819a4856dc851f4625bfe1b583c1e06fb6295f1d36001ee5d974a06316274e3af8db48b79ad03adc6f822ffb695436fa7ac274c2c268d false -check_signature 883f1ae53940841369bc477089d27c369b2771200c36afc80a1e862be23218c5 1e5ddaa318b8f3bb6e1d3211f3fdf3844deb3433e7a58ca81c4c494dfde87745 42be7f838c60b965abfc3ba5b5fa849d5a73fbc9d907d440431265a07ac00d74c89b3d4dc3a5d1330f87a894df74a0bb084200ebe8fb78a5443938d7e3b0b70b false -check_signature 086b76c066b5d34b4062a2058591a193680f8269a68b2f75611d9420002c0659 e331c38217bec0115ad936c96efb5228d0e26c8e2104b4695f6e566f81e5fec7 e073048df4f0eecff9a5c9219c1eb64557ca16bc0e45038d08b94e7fbc185a1263842d2665209ec99fd948799d45e1ad569637a88a0bc0eed31829393ee73713 false -check_signature 32738c9f04e480f9a8f3d1a37c1733558cfa23c833247030b74f1a9126d6cbfa 933b2ea675a4fc4cea830daad6b25053f2e766fb12bf4d16a22146405b0cf7c8 3d47f7d43de5cda1b762ba108687885cc4c19510bc924aa6180a3d33d570e2edc430d9afda75fd07bd1a3325698a36b3cac3bf85b61ac5c3667b5c9e5ee8a708 false -check_signature afda9dd111f115f0358a17fbdadd526398ae14d845e3670db7537bd06e1b4f04 7c67e16e80f007bc9723135cc3d0283920943a1faee59f84c19e10f82c799426 3df1783d5419fd49279a99128959a7490cada9403aab910a51d5e9deb4ede202f04881770a6deb7e01ef223d9358ff0b4cd519f0b7ed2549643945c95af06cec false -check_signature d247d2c85ed257022ec9b0c152d4c2c40e80f2637667ee93245b417fc3f65332 ea47eb53b525f087b830ba9a6f053f223f97cd914d711d1d8987592997db5dd9 ea792a023be8c1884ef85aa47212cd92cd2aec696b910e039095a845a7941f0b1f19c23ec412fb6b003578beb4a18560619bdfed401762fa4473a6fa61658503 true -check_signature f4d765776aeae815d6f079033c69b1b7e65376471b8e530825295f6f1cbd4a32 110e566bbad531298eb9da49615a5ec08acaa5779f7ee71c39f04cc2ecedc7b7 65b3c6794184a3d826d3ac155a9d87d648a5de48b7a31246007dc226b04d210e03c02d590af27d03975a796119ec87e9b9a9cb7128df16eff175816fed2ce030 false -check_signature 08f24fe3b0dab6cddf18ea6c763c5c667d57733f32081b7cce88933c6dbd90ac d2fcd90cb246e55a86ef47a0bcc49ff902c0e45a2909cdb33065b4ba21e33014 2b76f7b81cc72fb56bf780002d48fb4a1a7005fe5b5ac26d2fd82a444b59fe05b2d25d914550e6b9dbd0e4a5b57f54ffc22405907318f6f5f2f84a0f00b0ab0b false -check_signature 6826599e0ad3e2c795cbea6dd30114f909b7c6b417a609439f4e1c4805ca8720 4e8f90cf5671b17d97b479c5ee29cd6f2f0fadbb22f8316b0d0064401bb7857e c6e6d48948fc3c87847ce50b3a7686dcd73b76b12e854d38ea08bb87e2c7750d2372837517b621559e0a687398f67e0943f53a1c35e3b085e27bbdb178369102 false -check_signature 40b6eae8a27a184186427fbfeed196c6db18a096343d1c9bcc11484b8f461e6d c55021f810e1de1627dc5cdd7163a464cba3d585af7b9d47da7a1fecc151a605 40803be66914719aa8a39845d6d07d8df5c8fc437882ed9d5ac377e6d1ad97043163b642df0e0436e3ff547ef00f98235d60a93b667a3068c2b3af3b98428e03 false -check_signature 56a9b87d7262a77a1da9d9c5ed82062aecc2811998058ae13f071e25cc228be4 858ce9107c427e7071b1dcf85cf2229f6ab0b91232d9e261628bf912c4c7f7d2 1243c0509ab2992c4aed96e6d566fad176bb2214c072510c2ee3e4e019f811b5b66384fba19f599d62b5a5e7c72ea49f4a179d693c4193f677e13ea901592d8f false -check_signature 281a31e5a31c962cbe6d6d5a910f1235c52af09e6448fc80a835187aa57e2671 cbcf7456ec1c2595711dc0d0bcfdd94959485c232e6d6ec7925d648a0275ac7e 9856c1e6beda57d148745ed56ca15e0791666a440b0558756b0c7a92dd0d7c032613c49ecc4ad9c0046d75558140b79c5a1829085612ff352edabd44624ffd00 true -check_signature 74bead005976d8eec2d56256dc4b3165b238864aaaf1ca72fab57ae423d2a503 8a083a5dca2868f66d4bfceeb9f68c69bbaafd511239a145a7e2be22e0a6b586 7bbf3f55491463e3171d07ee48b25ccf41ba5d52da5edeb5bc523746ead49d51705c734842f56050c61af47d8f7f7cc2eb1e4df0304968b819c7aaa1efad1ed7 false -check_signature 3e2ff76a26b0a579ff026bb251579e3d584710ab0203186ae4ff1062387633a9 36346e83b0a875eee90d74950db9a86300afbb60424167a43bdb68550f0fe42d ce0f314c9b5d62d1fd8b695ef7e721037c10b7256de02fc140fbcb0509a1596edcdd83bd4cb1fa8361508f3da83e14e3c82bb5e149b831de3314c4e4eedc1c0d false -check_signature 1ffd81dec35e40928ebde4b2142a823350d603247be751e64802ebcc2041c7d5 57134229a89e7bf2d080bda2fb5ed6615a40c78bd7d3aac0ae3a9f1b58446518 b5661d6b8b417d1340fb57fc264258745dcf3b21e592d16716cc85502dc1c360c591c4e6a48eeb51eb11727b4f7fecb4facd113d4da844b4fea3de7602dbd60f false -check_signature cf4ee342c3da26abff8c76ff01b4e429ae83c6bf32afdab122e651f8ec3c7989 b7d6c18f7eaf34345597608af769bc49aad7e67ded62584a17b8ddda9029468d 1b40a6415c9f069fa8ad8ce0a2dd790a2e4fe4986be657e6ca3b3f1451dd1c0dc1a671042979c0100e751af063287310ffbe725e69f208b10716efa0c285790e true -check_signature 4afea1bc4b6e1d40ab5f391d2faceec9c62578d4947389dfb38fb4c8d32f7709 0d5c80f00872a774812269660551b34b9a902ad8ec0c1a4a5cdcf439d8f19e9d 4c4addd045170d9b2c56e8b91af8b83dc1d9438829d66247292e2d27cfbb9103c3dc72755ed0c55bc332eb5c6204dfc1b6b7908405108113fa5bd7ef1cbfe90f false -check_signature c23af8d9cd0e8629b726eb64c6b204c7b7346630f43eb73f2197eccf7d9ef999 8ddfabb7d342d66afb185df46728ad6fa10a90b06560e4200aa73322be129900 1549701f964c5a52dda3231ea573141188608ed3f8b6ac0a8191dd3ea278e20e63db6dedeccc7a9f1fb3c23265ff9b0982a0458dde7306ac30fb7ed09ef3ba06 false -check_signature 5e95efd029ec296baca1eab533f39aefccb2ea052bfc93bbcaf3e14218f984f9 a6144c6a5ea2687764724f774f4f92e9ddf77e39c90f9276e8b72524cd516fca a7307bb846e2d499a7cdb9e87626ddbfd9f23299a4a3de5eb5e018e5ce8d0e950943b5c46b3a0e222a2789183d213b95e6d5eec0781f791d305e8b8d52ef2e8c false -check_signature c1ab449ffbc78e51e40e44cc63622552c7b0f11e4a3b77c06e10769e012751c9 1c9f0a92744c32396d2eb0f63a5654910083a1adc1bc923c0d9a5242966702a2 b8653e2e1ecd8ccf08c6c900160516e8e477dd8829a837bdd235899f9d64ee0fc717394747b96df54015e8e6fc3b59cab28c5df93d892a8ec6e2b4cec5754605 true -check_signature 2cc3692f9efffaa6788f2b528d92ef42e975a6f38cc98a074d59aaa18a9198d4 e645637bb3b54b018a91c37c54b376237a2b082266d63013d9cd16bca7cbc6eb 38b6243e5b7a82cea8d8000931cc29a5822c567324aeb10942d0336ca1a4ba7637a1077434c7ede24c382f3e8329d49a985fc402efca3ffaf92ced9defb90c00 false -check_signature 43dd81e71e8c60dccd75f0ffeab0283d77d1ab410a638620511863ff62323922 b27e57d32cb84f7e0964e7d8ce96eda465260678e646d58ca533e6c06dd8865a db9e6c7883507239a712389f0658bf5bbbc4445e6c8b2628d2fd4d2fc70fa9af1f1f100c2e21a4807282d54bcfab802cf95c7b6b7c63449f27715938df637d0f false -check_signature 2f867226a4fe1f33e7d5214b5f218b261d1de56177e5a31c367a64cf517abbf8 50f3d002d7ce36b58133ec2a37bdc10fb76ad56c70a6d61341962145bb2c4cc9 83ba8733a67dc0e84aafe1b7238995e3aa4a2cbabe01f40c77721fed32895d0886a04ea2f56ad012d287639b64b68d76ccf68928c75790b60106cc6850e7b4be false -check_signature e8c43cc4c5986754b8aeb9794efafad928133f2da2beaf1741a9418553f0d537 fdce6b784bca0ead88124c7980ce4582df5cf070d7b03e69d2feb55a3a1c5aaa cbe7bf4c1c37fff696ae2bb9fb7bc03cc35df02507ec67bbba81fe5fd662460ac9af3f78762dbdb70e9388c8b85fa12b85d70c21670ffc0c10c67bd353971307 false -check_signature 11d57ebd8e2dc2f14c11132df95c4507853fd92ad79273def8e53002e0908499 d3296891ee567b8c1a088c1771b2666d34301bb2234a0b3b3ed5cc7f64a6fe35 e2405f1028ca98ff520ac48d65dee1d3ad13b89694414a4dbeedb71510df2106e8bf26b791cf2256239f0701a209eb26a55754202790252f87beb87fd8b8fde7 false -check_signature 58c2e8e070001ce13608156804d4c168c9dcb6f38e29e16e44d39944483856cf 93382fa0283401709b50a2ac2521fa060886772b0f5ae94221c4cdc32f553aec 803b39d6d157f692d691d5d9aa824600c349266eef9fac584065230d9490d4024260073ea408a305be55eeed5cd5012b3d1371607d61c512f52677dfb50dfa07 false -check_signature 10e0dbd68b496dca34bb19016b7e0b09d594ca9892ddd38f8e1758c4cea809cb 7ce2c12cb01981e969f1f327d4a20cb9c9856b093d803474c188724ac5dadc78 188462c2b3d9c4f9e81be0eb0cc81d7498d0d35c003dffde88f91b087365bf068a8654dda6ecb1ca18977dd1704c7eb0a86b1e98424f368bc17b5d31dff3890b true -check_signature 775cd0daa8318155efe697bb847aef3b3a80aa5e9786326f471fd57d0047106d b0434ee73f06b41cd38dc4c014be4c3c2d5fda9f81b52a58b6ff05db9dd3da08 87999be925f2eab3bfa6669c18f8a8df3d904d85df5b863f8207acae0c4bc207805b876cbfe7d1fc26a8ad62d86fb1ec228b88919e224c494746c5725a1a7201 true -check_signature 0dc42c91ac6352b00b933c541174cb1886a9de7ff68b227c52955c61856ae7c3 aafa9833db9de79d5d86d2cadd345e43fd4ba54ecbea7847110db0e3b40ce807 6c017af0d049afcb8888e74c624a17e6b3b487fde4783479b3c8c9445a70b00aa7da4b2e4899b04d7f8a55572631ff535836f11780e3ba1b296975a0edd34f07 false -check_signature 83d5250dcdca4ffb2912654004f3c58ec8eadde7e418ca500ac379dd81233338 5f97075339d3f98e488ded3eae74cbaf6518a17c310d0d2f2542cc44831a727c 09b2d497bbd1c56bf2ae47548b99ca29e0d546c26a6ff3a75ec3a5b304899f26ba10c245c8e5e58dcc56c4b8e3f1acb55eb1963fb7643bfc5898c5f163a3d002 false -check_signature b8e692984d3d5407cde2ca717651150c45cb150f0cd96cd7abd9911eb09f4e34 ebc25d1d03b62bb0030a234fecdb11b2636e82bc587732d3ecb63010f6e9676d 11f2335b9db1357c250fcbec7ac60b719f0e0243852ad8d395c3c5a777783709a5ce535b54d7395bbba4c998204693e47d67675e601f4288b8cc2d58c7d82706 true -check_signature 4693124d4b8cf9918d8d59cda481625e7270c3862702379206ddd09b09f5c8b8 857fc99c43e85dbca69a6c6d1fe38fc7bcc26611e7541a4a8b343c17b0a64c3a 470aff28ff7ee773fde10b136838d746974f0909ceeb31025c6d9a50d7a8430fdce44afadbe596682291e2c4ecc46b67727f1848a8bf8ae839c0991f56b9131d false -check_signature 98b4e15b0bbcbeaa3c72f4b64c70f609093a70269bf51acda6048d068e7ae29f d094f07c2f34299737978d6aa4b237588b50bb711c071232efac65fee1be0461 efdded2fc25f12b6254114030b840ac0271b8fc516dc5d58b421ee7e0df7b1056b36fc255ca7941a90a156829f5666457dd3412c369bf08a1c5c13917a1efb05 true -check_signature eeb95ad412d1d251ca9dc88455d4212d3fbb8cd05cfb0662596fce1541d8ede7 58b217f19d482caa2adf6063953083a27788e011d692275683e1ee447781357d 8db860206e838d77031fcce79738e997aa3d8dc9f9650ca19d39961de639f447d80fc3d7a29a1767890be1ce218700f0c6b0f08515b6db6e0da17aede484310c false -check_signature 58c76eea169fa0120d1c3986070084cbfd352ed4056d723fff061bcfaa3c9e42 9caf478bc7b5d47706c0eeda67ff713fac8fa77b651b0635c1c36330e7e883e6 ca5a6be6d3f52acf8ce6be78eb60f337f7b35d16949b2dd66ed67a32f02b95717e36f034b76c93e9485714215ff1196209c2870a901377da9538e7a60eba6900 false -check_signature 294c96ff5234da8129f124e32354f33aff3c8509ee14ca57ab9a76e67542ba4f f57a59019f6c4fe32e9315db10cb62f55b1cbca743863b4f37e12ba8c81ba139 626843a1118cfc830b16c9c864d6efbde55210b29a11194877fcdf66b8439f08ccfb65df5da5e1ecf4fd3b6137f13ae7e27764c1ac08f07a3666357519083c01 false -check_signature 663f580b14170b54c410cdf171daa0eab7956af24fc475f9a22541492116ce4d b25c0cc0be565d3a359ecdcb0ceecd27f7a0aa820132754f8bf18ed49c830a15 da2d3ec4fd921d27695ff43d68e6fdec1009553f372cea5f76af0b68716a2309e15f5d58087e8c11fc8cd3f4fdac9f509b502519dc778785050985768147000e false -check_signature 5276f367e077c50435ba5c3caf320464245a9c5b8e7fac645d4c73c386a56b85 e675290a402fd21e68d03e4298bada5673f062e3876147f42360d174929b292b 76dbd4648038daefa122d7a223c11ee94f526870cfce1ecce27797e6b69de807aca55903bc848f21f5e3f25ff86f6fd6b92aa25832b7e92e1b841f9130c52f85 false -check_signature f561d4d2cd927dc56c1176488ed4015df4f7900faeb9751c5f4aa06492874aae 5f1d19e16aee76f3f335f371f9b03aed7e9a7a1fba24c1a698e507d4fac6da5c cfc5c35072758b0dd017021d66f610fcda69bf3a8211c9baabdc5c9d7c4d160d055ee4d953ddee9e2ee20ebf722920aece4d2365664ee19b5a214166ef9e050f true -check_signature 112010cd2d26efc84e1703ea0d0b20151048838fd1f48523f5fc62f634c49453 d22372e1cf8ebd4d3e41449d1bf08f278e651df4fec30e69ab4e1eccc2d66bdf 72791ee1b9624d0684cd3cf6fc061c7fccbbb81b5681e21ef05319b16acb9007c0092e0f1b372a724c12fd8b1dc3fa9b7ef11c5feb29aedde85da2036305e40f false -check_signature e597851a19740e20e7411b03ebe7e603ca26e464d9e1be24ae94327246a44867 24970881935ef1d4ba35b98802a4ed2e0b68e7d41df751d13c95a60fe61ee70c 93fa2bb7a0abc374c6e4d72b225cdf1a2bd53e338465f6b39b17c23dc9c14704f721a30ee1946df2c8b5b905ee0f09d2fd7c054e50aa6dba3e28e4a7be2507e2 false -check_signature ee96ec311b1db35cd73e65179951a4af2b688309812db1621126f099494e35cf aec23a395c827c345b19bfb38e5fce179cfd8569564e19970f1c834a199299eb b490f66d83d5ed11a9f55a2386b015a6825485a94f36a7390490e342fbab8406399f49143ae48f908b4c6d859aca035d00ecd889be7f1eb737f46c0873921203 true -check_signature 83077be87ac523ed2da94c2ffcfc1ca5027f2d1f43859802d40df2f96ee95fe2 8e6d4ee77b075cd599d8ceaeb055469cddd26f15652a91ae2a61794e8353eb96 1da09787bcad5415a1ffce2d215b35db86d50053f66fe23c05cdf6336f12030c9650a3e170ee8dd233aa1d7128a871033daaac9b74c8c566d27434520ae64f00 true -check_signature 1b7f6552df011a198cd47cbcb508b1375ca9f5dc95c6fff462071ed095351035 68d056552ec6d05622545275b032cf37e0944998eca2613e2c189f834774a5dc 50ce34393cfb325b4c5ea8c34948b7f757df9e4034e04e2e06259348d528ee5145a3a074d3016a15f47d830141d0d34106f16573afb7646bb419f0cd1cecd509 false -check_signature 9c39b23fe721113aa3837ae1b545a82600698e8ccc68498ed4afb29d25f859e9 694c58fdb1934aecb985a23cea2e6a5cc2ba84c0b2a685bbf2e924095b1658c9 53c34bd0adee4383756a1133ee62f8700fe4f88b075585a0fec1e053aabfc0014e814f0b42271883eaf33c0a3171d76e8ff45ba8157d45c7ec7137dbc1c2450c true -check_signature 5289c8ba8c4ab9629282c8ee0152fa90e83d562a57444a312e7a1a5c6ed3b8a2 be8aa61f54163dcd0d7a941506a65573a92061eb793d62394a6c6d091d66fc55 6310c55e19f83c498d39377dd23e6583d8fbc8d16d84d70f1261d8dd577a0a0fdf225bfd8437ce8b67c3b907314762d0f07c766ae5233fc06bdbfd0705428133 false -check_signature aac6bb27f647224afa05dc1cfaa8e604ab93999ef927fdb0201d8c80851d4b44 c584b5535f7144ab5774a8650dbc6cd38325cfdb1a00286973a79846dd3b92e7 ece1d5775cf0d31ee07e5b9a57fd3ab0c2bbe7faeb0f6110f3f50d67f3c71c8118c8e0e4da79c2eb231a7e18710c70d9c1c0b147054c60c70c7f28fbe6a59d07 false -check_signature 79c6c499813307cc56dffa76ea55d9edf2aca6c8d1bc1db08b816756962a1bf5 8cb322916efe196c940a9c365a0465c5b1ba3de989f6a950ef7f02d6c03b3900 a1a989c1d68fcdce744e0b18c0c87396f5c3f2292edf8d1097bd926a93cd1506a588bb73856e1b7890b5fe95e01b86b8c0335f49e8888a170162a3a891b19303 true -check_signature 7a7622a439ab4745e4c30ae2efbde8545138e43cd749d784b6163ac01d0c479a 62d1fc422a1025dd024321d5291594ac5377642da284b0aae86063f36bb36d80 c3ede96ef8202295a19e2aa53f90a315080350790006df9be6255ac2049446f7a1e057392a2f8edcf7f6258507888a39026a052ed9ab220d573b211c403a0900 false -check_signature c91b287e7c77d0fd2823769aa432f9e416476df2a3691b061567ba2d65258ffc 06d381d1d128143b278df358b30d4fc173f426558bd65eba65fe887a7d125581 5aaed95719080e387bc5d41abcb8d9ab6514f50b646e4f95f97e854534ba240ec3623fbfe287c0feec53915f77a513595165a2a885b3cc087b693553d6ad295c false -check_signature 91815e269a16a6c412e8a604ee1dba93f00b13416eef384621a1934c7b8a2973 a80776a89afd4ff9c6f6c93231b1f283d7184a3f736d69b5849184f11dfa4c6e 3672113ab56b852ec9d184cbadb5b5ef0a5ea0b64409361fbcb4557530b2d50b2e1a946474095f10ab078fef28c9e60b94c81a892ce8e8d1ad3a329f5eaec700 true -check_signature d69bd93ad43d38593998fd89dc7032d5d49b1cc181203f90d033bb7c23413e20 a3d675846b63e50375413973b874ec289adc9243efd97d7f3b1b7710cf7ba4ec 0f6a621ec81182672471a2d5a0551e4b599853306fbea1225f4e6c15d57e92e88acc68135cd17d136ac52482504011368f2e47eecd690a151c394011a4ad2b05 false -check_signature 68ee7b58658cd8a128167f98d27321a512efae8cb98f4a84d59bef61112bd1a1 35e783d8f464c8ae1aedfc3477c45a4cebf8a29804a01dcc263fb3427813a413 e28a9c91a169d38197302c1fcc1306e2ae15a228d4f13ab3922c5c27f8b141121020c077a792ba15704343f84748906c03c3f8f329096986a23dac5452ba9f23 false -check_signature 8cff2675af738ef5ed35dd58dd88bae1ed59213e9015191b6ca75b894413684a 76d57a0bbe75bdd37ed5d8436121aba5e653241901e42ad1831fe5a1290f1c35 7d55045a98626eaa8b9c9a4e38e8b3da0e03cb546296362511b6e172d3e458080faf98918dfb86342d96c20d3d51892a0f9839db064c4c38bfc46a481a5ce5d9 false -check_signature 33ea920d499ab8c2ec0cc23d59bd655fb68692600de1ca21a9cc3707f84bfa83 da3b411d2a358eb416ff3e5130dbb4a528d41e8c25afc34630120dd13512d5a5 89cd78f4991de3c805cf3ea8624b818fa559c643fb80af1199cf001974994c00b5d45e09c6aa25aeee7c559f375b1a101c5fed724a020a3926e1c3f925b89f02 true -check_signature f9e80ec779af0221c6a7c306993c558bf24e20eb7adb51043f872ec46986039d a3f1b017a4bb3a048b73d7927009deee71b889c2d1df39adbf01ffb5c4757b01 ee7d05d8ff30c3b0e6870b3902e6f9fb54b0c8cf7d405e5743a15958e1aae90e1c34768be63184021ede0f3b70e3073ce5e2ae1c4a02c743a0690a40e468927d false -check_signature 1471ad938be9b0048d65b03228515df19dc52486273e14bd72f47e57a4a4a2bf bf47d31ab587702ae4cf7a581ca5a009c5b930881eac1458acbd807972ab0e13 20b400422dcd802a0925e8b0fedb0aedcccdaa9e692ad7c71388098b2e2e10b3600975333d1f5efbe1de00470ae0b1a5c94187f9c99a54b1cb36eedb33d23d89 false -check_signature 8853e0d1fb7ce1cf269483b890f81b864fb70edffb8a7d923a34e59810a7cd7a fc99cf0b70a5fabb0ceb0f4bbd2ca458c12c64931c9c75a78398ca16753c3d30 438ac939d615284d5fa73da7b8055ac381fc6bb10ebe2a72d02557a0f6f0db0249b660678839e2854a8a4a7945093555106f53958faf41609ed6e96e65c8348f false -check_signature 89c15dda393624f879903e457d51e67845d1078678c2e9c27ee72a3f95923f16 da42a95c8e2e0833a531e9a98fa1502407eb4002cb6aeb1ffbcb57627d3ff263 8a4a7cb92eb20fc22060e7183d2ca536af5e628cbf44a99f6fdaca438e42f007226bab5d40037b4c77081d8d40a9150cc97491ec7ddd5c61c803d15569be0b02 true -check_signature e9f6baa7573f5571d026ce41bd31f1d19de7916031ddc80f3fc69443bf323808 4527c1f245df325ffeb629036af474d328903c8a8b2cb3c5ae32f25d310f74c9 fec4e7810c39d711a10c3ba205429c5dd0b29e69bbdc414b61c0b66773c8590d19e01f0b8898575fcd55a136b847f32c3664a485c517ce07b1510f19a8ee1f0e false -check_signature 4681c0ef3610694a7c22120918b88e2090d3325c570a41adf68cb7f53835c6a6 8865406cedf341c8738fe4e43d99bd51a1828468d3900baf08016f5a54a62be9 734e2a51b26c5116bddd62bceaec85828fe2609514d4fc1b3b80d2fe24a3a409b9d7a3d69210227eed51a7b52e4395987e06311a3e5d2d0502e006f4339fa604 false -check_signature b68b7c4090be0fb3a135c2b861fde3ddb6a95bbb023ca5a09731638553f10da3 a39519e99c22eeb12a394bf31d21e0666351f75eb5c0a6ce327a66f974412173 e524bd25f3f1897d6fccd5cf398151c7525b9265cd5882efae8aa585c10eba008e21812c2fa9c8781626b0bcf06520b2837dd1c659f3fe14225fcf26e2513005 true -check_signature f73cbe759f22b6ef78f1928c09778c85d1d8d122a56764155d320640fe3f9496 04d1ed9b76bdb9127631761e977c322515595123527f368e42430f84d1fed660 42339acc914a5289cd4b8d06784d4df8845590e29de0a50af15216448e1e350aefc25e89ad4e38bee92c8a11b3be31798fd870382a46f5f2ae73ca1708d4950e true -check_signature b6844a8f8f7ffd61ebffcebc22ac74cac0101572fd41f20443fde142a52253e4 14d36e1c54eb6fcb72f6c991e3869491289d14136e706d19c4b4d285345a7333 c4062ed36264842043b2eb474e78850bff463ac37e064b720a769db6edb59d05cb151605268003abe0ff8836a4fac1021749186fc07cfc859ae36d39597a84b7 false -check_signature f9eb03bd39feb77c7d71cd838f1abe46838d0ff33a2713f051726db8e7b7921d 2203438db1ef94e97be21d6f98bf36fefe2e6d7b890220dd93935de6b37e0821 775f10d3d318839f6d85d5940ccd19a4b1c6673b6263c8477e7c488c8e9a6d0cc658a3322223b3de79293af90e9cad416b84fda35ba0d0fe80498e22b753a4af false -check_signature 8e62031457f2cfb2ec9340634a8d7e89d6169499b4253f974df2fafe06110842 ce0a6a5f6efedbf2a1d55d0fac6a3b7e6c085e35dc563bdf14697f7814c6ee4e a66cefef4bd9afad98d4aa9d660fb441a8724813d1fdee18188b988fd28b6b0397df91f13878231d69ae47d489515a275d40f32b5e8f68ce7c113be866ec2bc4 false -check_signature b5d396d7e13718beaa21f6421765b4705628105b0dfcee6509329b0ef6500d32 8c863cfac6ca0f65f3d55a0b212536c2825a5fd8f0389079d160d1745245f340 62eb711b9be737e02e61427f56c9dcc03b833274ed8de283593260249e390997c08da5ed7389bdff372237afc0e762e43a62d0a8c56a75a33ea55e67b3e74309 false -check_signature aa67d9296eca5e2ee7252f5c63a0dbe253c149027d7740ab7a733bb6f75ba903 572a4265fe89d80ffa65c884ac394e05ce4bc00e52412a4e70d9f6dd1202b38e 064c67a5d85238c7b8c3b587cf8e8b6e4131f928927aa8b5e0af534bd2edd0bb0f6734098e91bad3bbfaaa6239e53b396e37da5fa31c89c10bb0d0837a596b0a false -check_signature c694bf63f3d6704dd46804618a38646b92e62754b6f1854ae5d2f33dc7234d0f 2cc93a9e8958e39c3b0fd9929ee25bbf2234dd9208918644153a97c85f2b444a 5c62537e1db42a8266c642e124ac81876f1c9bad93db3754927ba44d869f4b06d86a73727fc2bae8aae3536807b495a8a1626a2a8e76965734d2e218749b9200 false -check_signature 39838d46879f1b38d8e818a85b43a3d13bfdf0c46b4d1fdb57c173f3e7ed9e96 52049c468fd115e86487deb205f45ae9c575c1c7d00e983c4c88b0e79c64019c 930bce099993010259c022c83c13a9d937b195400a72c87815476566bb1e43049ec3e55c6c97221950e4a88c0d2f68e969c0b4c005ec74458132e2c37626bd18 false -check_signature 3e7e7ba58c80795fb87eef7d6396c95d4e160a20ff15785ade288b2b0dcb3c35 78ff6121c71e779af5f29ee2217fe3749582777574606f1a94deb8e9111dcf1a 20e6fa4d3ab61188e213220a397e7235b3a7f8f31176a4229d27f93f3d923bf31ffd0df871d4d26a7c26b2f1f2f7e894818ec032e25b4cc2c9c4303d4144160c false -check_signature ecd6e5f611ccb506b036c22c170db42bb6908da13742b0b30cdd2d4d49a5ab67 1965a1c7f90b9ead3db44ff5bfc7c6217a138e55ea4e39eced32e1db299e8b81 25c2ab224239e3b264fa295803e7475202a1cc886bb012e67f64e335c144510d39906aaeb9bad97d13db47fc7895d94283db892342ae57cbbc77b469ac1df336 false -check_signature a45217a4521ac67382da57ca5dd8986647e6df1e053957525c82796aa90d6626 657bce22fc58099ab9bd7ffcae4b1f541762d71d91cb4f13e844bc24ac98e9a7 a590eeb65cea9510fbcb470baaf99e3449c221120607f615a24f063295d3be048c1216a6f08dcfb6d7ccb2255f9deb2593f064dd2ea2caef8bcd925d3aa8f7a2 false -check_signature c141f54775f806b5957b103a43a36de99c535adc687fb0acaad44cecc53cfc89 ee095d3f1096666e66a8e4a5b7b27ef5f60d6b963d6899f8694432d07d2ed193 5254acb327dcb3cf50eaad4619157acb26e28da13e653ca52a084fe5e513c8afcc599130cd8c9d9d274fcf29069866d41323394088b2703e6b84e92c38d952de false -check_signature 4a5b9c68c971b371583cdee9c579ea3a0ad50033b05053f8bbd26d249c8181da b9e4d9f479df12b2b2f04ef2e3496561909d4f16e24a43f1622d103b0e429283 421413e755e888e15704f2b0b56b2d16fec2d60e8459a3a88c3567eaba55eb0e26773a4e97a379013e899205c207c40469614efaa6f6b76bfcf6b50ac8937792 false -check_signature 2c07d4024b0db36d82be67a1da0629c7329c1bb80ba6e305b6f40d3bdb023a73 037a5e7eb75523ad4f27908a9e6c89739f16ff9b7fde02e8ac76da454f83ce7c ec586b279240aca17f80dc32289a7e829f4056bc78555e9d5ad2c430845642e327f67c3c2a74b0c18afb5d3796735dcf41810bfe9b1b875706d8309036a27a78 false -check_signature ed83078fe330839132feea5eb771a0935e3cef9189fdd115dbf4e5242ab55f99 d398707e1d304fc13b89a8ced334852dbe3271e5ffcb8ee68984d866af053e20 a6d60915069ae635e3944fbbe5e9b8067eb514a7b1479f7f8726b6ca597c7c082bd02a3b716e3af6495838c3a3dfa7a99ae3b6d65f077d3e751f3a98c96b4be4 false -check_signature fbcb2bfd6843d73dc5fd0f61bdc629296f13434efe1a2e8143b251a8a6e99631 57bb10b73b283074cc8d9b7e048ce576a84381cf762f2f7bd808b16431c2dc9a 9ce59a18c8262e0e30db0cb0663c4b31bcb9569aa556d17b7168126d96649102ca591e17542b85660825c716aae116b084a609e7efee5f615417a09330e9d142 false -check_signature 2bd972333015b6f4302a8f88bfddcf269c70484edba2007f0e9aa895701229fd 525785835e001a33f08da0c35e6a402223feb575d4e6192be0325d10eb4d0c0b e871d67360cd49382580064311734e4cbeaae6ad6de545e84f0f136c04743f002cbb5c46cfb0b0990a50160d8538b87df729c66bcfea30af5a667cc2c8458b71 false -check_signature dc9dbb2b2e4ee2bc16da69399fbdaf02817c087a5e78bfc969cc86085ba77d00 27d93829ccf678a18a03e5e7974f8e6b266181e943ad30fbbc28e89246dbc705 7f931e9ecc49b950f3580ddef57a3036e9f13ab426f091d2e36a3ac1a0973501f5824206ba88c401fc92abda460e7c18c6dc1a1849fd63865321cd7794ed650e false -check_signature bf42110e3c49a6a4bef339b6b764c6d8d56107ad6d227ce357fc31d12498816a 3c5292c30e221823b0961c9939385071851a205c2a67f03f4ad856645bdcc504 97ea98bfb64c1cedcc5fe00fc088049f43a1daec5e9ebaf96e9c2f335179de04121262814f138133097ef5ae461d4b0b78cd168a7807e03c1befcc7031c5b805 true -check_signature 2963486419e8dd4041689a563d60456eba48e707e071d10e1b461be735ac15a1 1186bd7259e6940b8bc4608b7a58abdf0f205275d7eacd8b4819b2a6ef22ffdd 56d0b1d8cbc0ee64dfd59a37163154eba2a13c4b2a1bd63a02e37ad1ced19c062185130265bc1a215d42efee3e065dfecb7a764de518f152cb693d1173f7b888 false -check_signature 00b33a4f8f5486362712da0b914b2165d04c652de575c040a615360c6b8d02b5 270ac06119dfb4415016e53ca8b83b98254c832d2928ff3a71206959136cc709 0547b3f4011fd9695b4cf3557d9ea0b69f488c84ee0baa080a5a9a4dcfa989004451577f7f23b22b7281eaac7789d1cd174609fb092c3386274454e6f94e5a04 false -check_signature 5ca112f1e71036e5ef0e2e0d2704365914c4198862a5b28a4c0aee2963ca35e1 b1098a6043edff1c248fa4a5ad78c79a267dae854baf2b74fafa6f4fea831f89 a6ee1bfaf75e3ff0f901150ce78e3838929a91116858a1dd236be6d126c7b15b753c55c4d6f4da1e0ac3bf11a770b494356155b0446a35ed6171dc7320c5880a false -check_signature 62a84185d3f5f015bcd36a56097902048ddc74811bf33466d6638068e5805dca 8ded0de0e2f763d60b3581bb6960971f0819d45200dd970eb005f60f59c48ac0 f11e4ee4e540b27f0b2a589d443f93b9e36c699ea61163ffcff048d3a3c0530d0b3a2c65aed5cbe5b06be077033a559b746f7a87dcb6ed71018a9bf73abe989b false -check_signature d83a7ac6702dac9c2937f32102738a28d92e9fcd705b8b1ccf7e8e0a2d8786ed 8ae05fa6dfcc7819d12dfc690675d27b84b661138d2a85c502ca88be2990d381 a1d80995e33a95465870d4cc3069c8386f737f19f09606b64789d61cb83e3a0d6ea68a020f3d5c40f340b612e45cfa94dfc6e45f41b574cd712305c8e7dbd40f false -check_signature 3c0dd6f388d92f7d3f7b1c162e3aeef45cb56c57ce4056e425702ea0a5317567 96f209da0c24f59adb4d5670bbb967fc93821674a1336ef9078edf7eda643ddf d5ba6351c675907129e3e17f4363d7ddf486578fa08f45b3b0bfa724b4afcb096a194dba7a0f4e60956691f870a87b9d8e8719aa50ccf4172dc296e9db925401 true -check_signature b9b96a0f85d5d22748d65bf69c33c6db5c29d6be7acd1e664e9d5dff338e7e96 276805510f4a90ff77e0dbc0e75c6fecbf3b68bc34b406aa171152f234bf8279 4fa18baf88fe6f79aee576af1393ea3ed559d3c7824bf15b4057fa41a071b209672daf2af2b20237967ea57ba59f72dd33c3558b526ac9bdfe5b0b9e021d1392 false -check_signature 9fd40d6e9b68b57192bcf4e1439d50705171614feb79f7ea629d7640ddaf4a89 60df412680e83d4f96243314699f4dffa6f4ea6da18506dbb5d8b4ca0957870c c8f3e8124de59335144b2a6b96e9c6c9a18006894a99cb50eea3e5a407e5023cdaa0e20c315221e051c7c8351c423ab83cf8f4bf78f20e32a52a8c18f5d09604 false -check_signature c6eb4b2625113bf8756f4038abd758aac244cf6d95e55a0fa73faefcd372bbf9 f4e7b2745e72ca8324f1a4a234c33252ead9d6c2e94f008b3b9baf85afa6cb0f a7f348cea08b366bf3e840987589d7ec10a672f9345ce137e80674af3d570f04d513f3440195c3641a83db0a54a01345552abfb52d2241ad8cf94fbbb255c107 true -check_signature fe79b0358042df0517efd6ab205a76040aece8175d3c83b8529fb9d118c18d42 dd01c920cff48826825a42f57571b5dc32b8dfa1efdfbdac575ab282f9fdcfab 6e6b4290134ed882531f8f7f42d54fe724574df5d0b9b1a0e00f69f84131e40901ff9ad2f7bc7087b02fd189c2e6ffe008c832909384148ebd461d9c9797c20e false -check_signature b47b84ce92513774e976fbda647c42dbca87fae9daad6ac0cb97d9262f16aaa9 cff341c78ae9d220b9da4b0d6b254c16be4ab137a09535f30f1bda2520d3f405 185aaa6be7c1edaeb4668c4115aa5fb222aec3fcc8bcd422bc27c72e226b3e0b6e5f388c07f672ef5356d2845546d5f5e4540bcab3776c78b0d736313a8a006c false -check_signature 7af6085843ed724d872e71f468c0eefb0bf80a13c0bbfca9cfd2ac9a09aedb78 e13c987b86f36cb6d81d7f40f01c251ba2e91ac95f9bad99563510b18785f248 59b9e73da44e1def33713d555c886ab093817913b5ecf3188b0e684504a4650f4d378abd8a29ed19b1d9c79cf6ea8329ef000764c97a5c023e2dcd3c8ca7b306 true -check_signature 46e702b3e2a1b0227ee5f94a2a803a047b0f33c764d601c0cef625a849b0e0da 2d0adb3b12acc804d3751af10da125b4f60c6bbcbfdabbabb0a1a2c3424f00e4 0d2b5ff890a89ecd6e7b26838a72adbdfc98820684f1a3b85e3a45964f16d303c42f8357b3bbea8567494ed74a96b4fcaf12787eb5c3b75781166902cb8a695c false -check_signature 579b07ec03b7bf919fcde7165bb3d2d6533cca311ede7182d0244889c381de2e 9ab9013bca2f4b58aae445fed95dca9cdbe0be178444cc1648884ae7ab611938 aec8b842880aa7a8bd980cf1315e8fc3464aa3af66fc17c1fee31f6403f5d406ff3c44c08b48170b73de8d18a1370a5f9691cda265c07df2c16ab7b166dc4501 true -check_signature 81827e41b96a670a8c517307893fec64ec23a4126c6631761faeda38109b038e adb3a9b2612a340466283f8f1ab99aa6c59be8beb9ec466801403220cf76fe1f 002b14ba33ecd0d4370ac7b38e3cbffa46b3982b52f272257a30159c332e170ac219f4dd8483bf8480677b873e672c03af79338b1352536e462cb395a4dc4a0d false -check_signature 92542ec480fd737b84c673448399c9dacd02a8458d2b1f41a01a744ea93ac1c9 4bdd395610edecccd1d572a2f8c33670260e4bbc3581b866142143308a282c79 30b0720164074b10ffe05fc24221bf3e50c5ae1be2032f2792e2a4dc692960ef3f60756cdc81585e711093e5ec60721a363f5e206ab51fecfb9ea754b3098382 false -check_signature 41eadfbad65bf007bf1988697e4e9680ed1a9b2e0886b409f6f75bc52fc14533 69a89e1e5b285fc2371b0f28e5dcb73a4e820f30815fa2665afdba90665716d8 adb064f99c9037084180796fd65a6f53671b342ca94a968970e6e96bf7f9ad48ed487e2b7db4f2dc92e25ef9188889ba6c8cb4ac574c1821116251f426bcac0d false -check_signature d0fab8a303a5cc053efaf9c72d2da6e89905f2a9110641f8f133b64a7bc31cdf 3fc058f2a5f1b41d7eaf97e7aa31ea7a59e72608cbc5283bf24b78d61a6e43b7 236a93c6a46334cdef7728f1cf1ec18dbe7787e10d457afb27d17a9dd611030809441abf1f8295e5ad9649cbbb65d6252a7e805d8a0d489768040f00f468a50f true -check_signature 6999dbac641e837c702d0cc0932d8208319baedca7c1c7df3cac69ae9d34871b f047bd0600dbe2e72f0bcc1394dc4cc8a3736493878928043318acd4ece1c963 32e136cb4e1aaa6728972a5b4300c7d2eb5f7699c781d8ebfe2f01fb0cacf30165ec7d29fc898b7048257dd5ae6604a3464222f04dc4a2a563088a37bfe86b03 false -check_signature a78623e59321b7c3d79cd251773188e3c8979fae39e53060a4259eb8ce4d92c9 f6bb5ff7ed06d96c34f792f80fde79d31da9c946253209dcc1f54ea9ea8c9096 389068f00d433f216a68b83dc62dc70200b46153121ba360dd0a2e4533106c0fe51e015ee43146ff40db7ab45d7bb4e842e8828d9aed9240c206c50e8af11205 true -check_signature f16bdc6fc2b40a367bba4e1658d276b8dfdb5abdd515f15f21e204e17fed3e82 c9a76fd884d2673a13b1ee080759ea59376a5168447370a90f7a19409f25d994 29c57be8898ed3f20c8565a7879525d97f024abb78ed6dd78a54be970d5c830a5e7b496d527d133fb48e67e8d2e16a12f7302b0d7a663222e2e5546975eb8c94 false -check_signature ad9738312df13165a6f225d20e5cf2bb84f1b27339eecb414e60c20f3180492a 6e02a2c218c752ce33e96f806f00768df077c89b1ae939818ad2533b90a3a14c 9a4177c069270cb6dc5b9c0648efeb2cc1b0703642310ddfcf17769a2df0440d74a1b97d1df2401c5a19479003da7d02694ea422479924005562414661bdff00 false -check_signature 78bb8e2d868e9c873064132bf40a237cc863d686c0549ba0031014a92baac117 a47251eb39610103f5e594c2ed7cd52ed680325670c1c86c6c0da6f808cfc88c f1a87fa0b3a9c9bd1640d6555ee1c15bf32048352e1429057f7eb38361024d0d8f5dcc175f367961a287c9685609404c5a016602acdf8db76042a7f8211bb902 true -check_signature 9914cac3887a748abe4ec36c2df93001afe143a9af45644920a40ab3551832dd 0ed9e7f0db8ce8b36bb47726f1bbbd84edcfd2eaf954bca770cde3ed93ac3aa4 f8c36b3a9b37b3cf74a602595cc533e1e6250b66d136098942d7b3d2afeeab0fb86d0549736ffbb7ae0d85111b8cd56748d167790f51430dcf2dee5fbb814d04 false -check_signature 7fef9df7e1740c9632f94cebd7100a8434be39738cd62ffad45d8d8ee4d6ee85 a32ec09383511e0af09d7efab8205c39237a8f6d9b04112b819a2e478d04629e 213047fe9a320dfa1a27ab1782a5e4f04f641319d7e1489a50d167bd016d21c78845258de1d94f1cfd0f5306b92c005751c53616e3791d8e6e5941b463fb1800 false -check_signature e6cf2b7ac1b3aa9973870669a9058f7412e5e198fc1df6faeb873d035c35cb06 884c446be8cb8d77c0ef70746b430c3cda3affc570b0edd2fd4dbe3cdb7fbb29 387da8c2c9ea47a0ee0a912cc7c0810fbdc1ddd21344f772c724355098471d67b24ca0e12435214a906141c97a07f81da687c0586c9eb551579c3488fac88123 false -check_signature 4289ad0c2b2e8d913476ea0ebc04e6daf784fa6e7d6a2bf5d49ed4ab29719115 7ae7de4f85ba90d52c7eb8e9f4e123e0425a4fc4777dfd2840c9f623c76edae9 e3aa4abb74d754d4a5e41d12cb48a0b6242d45634f1773e46d224f8ed5bf9f0ccfad67f9faa00425f3cc2a2d39bd56a37a7f093a42991729458776944dd8e304 false -check_signature 51ba03884a418674898fd590bc4db6e0f626e48fda522610b4c6bedf68c4107b fc5aaced954553bee646e754798c418a4e78282194082851dea71a00f4ce9690 aae68f442609e79ebc6e949f21d324c204b507fbb43648b3cfa8dd016123bd0f92e36b06e3c0c3e08c5b43c0474b4e88eb7e8d9377daa2a118ffb15342bed40a true -check_signature de086255d48fc091cc16fabf6bc2c947db8a4c6d96c025c6f50f0f9a5309a8b0 13c6d495b1a6e5259c5e23a34a8d5260e2fb5d0663d23fa21a63bd3fa7ee3eba 1435b65c1262d0e465d2086fb200b468fc99de557ab6b751e6165417befda20449804d9dbc4ee823f8044bfec9f3666c5847d3f34b5bb69d19477e5576b8a00b false -check_signature dd4785940682581386fcc09a03d94cd09bc5ce031244c364e73bc0502052c356 b09b92c1ce10a2c044c3beb5ea471bbb01394298ca2364e70978acece6fac5ee 34fbd83cb47f8794a0eca6ed986ccc026e4fb85f487d9281cfe10f4b9713888a1fe15b0198e5862648df35d3aec0ef12d958dc333ded4c80f10187a3645cc009 false -check_signature 1824f53a7d32bf8b1470c8024b042525f067de60833974c7e5e159a7555af00d 7c528887e98668763d283833de11d49d7dd39e1dbea10649161fdb7c82ad87cd 442e08a8aa7db2862decc23da399b6f02f92590b1235ab8c741f706a392fcd0acfdab62c865e6a7b1f511ccc93c8cfce05650031cc99b5ebeda427c4e84e8f0e true -check_signature 56040e2b6d0d8e00bfac2931a60f6de87c651c88c930b4b4805a4e7352abae6b 2969c1f26daf14774ad8a2846df0805fbc22a005c0bc535df9d98cfb5096c97e 0105d4c962bbef88bb8a65e199808e0e1bba2942d2724d9b9afaca68df973801008a1d921fa5118d108d900f5a013ed224b0031130fc5c4dd813b6ed1593deac false -check_signature 36cace8013115df307c956838964560caf4b081a8ae6f578f9edc5ddbd731c0a b2669528949f346612c37f2eb9e31dd6afef65ec3fc1d528668cee4f1bf54107 fc0e61bf27040f12a7a1354cb0f0972cdfbea298ddf19635d99415edbee1e90379df1b6482b8f3ba9397b00ec074c783e1a6f8dfcad1be2c9fc7172a56170506 true -check_signature 4a34a2ed10f87606e03025fe74187523eed7ab9f1e91e725590653501946e298 c77462674fa807bb22883e554c4f5771907d4f5d42d74aac8fb9ee336a1db136 5097d337780c540f315fe8d6086116519ea85ba8fb4b99e4a9e42a4cae22780dfecf16a51449e6227742a226b78094a45af2066910b47b19f783da7f1ed7c500 false -check_signature e9d12566ca2a43237d74765a653ec04012c69582635706f4676d6f1e59fee9e4 38f85e5ae12e87826b1203a41d547c5ae3225c768bfa368ed9555f20c8c68d40 d04f3f9029d87559cc683986a628a46681cdb44f37d95c3589ae883e0a49d50f21bf8bd2be96895dad33129f91ff1d5425cfea7eeb5edca218089d2e27c5c6b8 false -check_signature e675b337b5fbe05ec83fe04ce8db7d01c01a470ed5351db1dfa843aa05ec5717 4ef0cdd62734a9207fa1c5a7585dd2576c7be28c88b1037e7e44858dbb50632a 396e0224d505b309ea77028cdf8914b0080893f8c1a79473552d69a261e00e02dfd53008a2b6423256f47fbff420f48f0bb55023d57774d75110f1d869b2e970 false -check_signature da7326debb77296e413d530042565af1a932cd736185d1285299a98510d593b0 a048577c42c5d0f22b88747645f2fa84c8ed63be30daaafb450bdcccfb487463 9f07118413b84d09fe5b18381f2954be564bac4ccfa8a55a76fcba7203ee2509571f8295f7159b2349b15e9270319f8a3583fcf01c2e2d93257897e42fc3be05 false -check_signature c73e637c79bf76cfad8e5fd2882bf6d1e94b442b71c3e7ecc5964419d8105aa8 395081f51fb3d34a35c2c9d9fbc7a033e9abdec3000e8d083f0747b6514edbe9 29ef3323a8fc514623e6b7acebf7954ba43afe058bc8eb8f29342a0156e6bd03b5daf875ef3c528f283be00dc752fceeacd03b282b04d23b7e2dfd8e8183fe48 false -check_signature 63d5217306dcc827b0562c9f03ba33c95f8722e411ae0ee8ef3bdb4d61383d77 a9d3cf6e96f66886508a1222c74e6ac5972e50e151e6ef9c8da86e888d756094 7087682ab21b6a350ecbb50394d336aee71457bee7aba1e7d69319fba5fbdd021c20802ca5085739598b1970f59cca55e73680878241e675de1bf2ab9149a41c false -check_signature 8002778d4c3d43dbbebd1783026fb606dea0554ef4ed541d40c29fa876eb932d c16c9d540430c8d3b2a2ff1c01650657908dcd20bb5b5b508c6a415d08c38376 9985378ab87976de89f3bdfe0e075f15ede02919ac412a1b5c6f46a62a809f02bcd55f26d26c4f0e882c14856d440b79ae78f2d2182762d26a4b4cb5c0696803 true -check_signature b6c88a0a27ee19dc3b845b78d4a1819d5adf6bccb8e2168ff500e4f7b786ebbd f920370ec3837942716e8811cf9346f0ae5bf354eb2c7bc8cd90c5a43707a8eb 2125f7d000f02f1328ac414a29c9ec9911bb6b4a1851a38929e4bc6b1459109f4e4f24b48d8c1776909bf7d0598786fe6e1b50fc46028a2949515be8f1e1780c false -check_signature 8dde417c2aca4b8b573dcd82ac2c14bca1125bfd87f9dd2d182f261edde81035 76b2e55036404fb5f62af0e4fed54bdf8c0247596ce0ab8451f8e67dab634b82 020aacb373ebbf56e2be81568c6979f8aa3653067578f636466c8411f7da9d01051fa84f3d8e5eace15d2d876f53c1a5d4b5345c35e66a0e8df63d2df3e74504 true -check_signature 400a113de0c780d48ea589053a0cbd723bef14301189edb3537ebc44a29253c3 b32f48235aa3a4ac68ccaceff40be811cdf899c22d414b54bb92cf9a9449d42c 8ef6326e14ce5e00b21a92072d5bb4af81ec39ed718288978b2e6e36300ec50fdb96db2fcfdabf14e90b53ddfc0221fcec5c803f09d507930771d342d45a1333 false -check_signature c8004459d6f717f2f2b8cd9a2ca143fe2a853d920a175c8c876c5174e594364e 4b0c02414e688e0e516012fe02f296c228b76e4ce213b778f6672554db582686 a6b37a0ee56bdbbc9998ede740c4b4a112b63487328818f795a1c1bd373885077d7ebfcf774762e1c4abd85555956e5221b26f8335d5070ee259aa73205314b0 false -check_signature 961b3544a613953d9d00e6f4c1a325733db5a73ba530a406f286272da1bb4c2a 99fe405e4013e555afb11c67b2cf86e6368d59041a1a5d98d5a0d5d0425d6957 071e7faf64c856eac9afef439896eedd63259d5e165f479cd087e7e74226a09c4abb981a2936483031bc5a919c5a6bbc199ad4a35c57f25dd38200d1633b4d57 false -check_signature 066609a9872c4aa76856c5701c8ca810ad57c0d842fce39f5490222eb8f960be 69fae9a1247ac7d3357f09484ba58aeb09dfe6009d2c4bbe963b6da7fbcc75ef 8ec040a6f8816e5a3b99768de710a10c34fe149367a765cb3d781ad803d83800e55e89233f71cecbdf06f40abb75bead1f4b8f8cb904993627f9890a2582700d true -check_signature c366fe2758bcc40bd72b0b96cb365c19957f4c76a15ae65b7a2658c75cbbdee4 85f659a78131979131e76ee62597ddee2e77aa19e3cdb0970d11b47c4478345c ef0774f8da5f3f2aee579a598ceab814e66cee7f81e30ee64557b657688a8a01bc6ce72c69afe82932a63a557cc5a19f346fff1c7c2eba50494929e643b7da2a false -check_signature ef3d20fec4f5690fb824c816bcf1400d5aa75e06195eb314d6e09ce85fd03341 ce3ba26b2588c745a92df46db32e3bfdd8da09ce1bf123c92129ea5aa8f825fa 1662bb9e233d286aa038efbe4d332a12b60b33a75780b9888e4217e82ad75218b014d405b0e1705ce479bb57aeda9610d03593daad1f21192631836087d9a40f false -check_signature baa2d86da340bc7a3dd7a71418cf32cd6057f5ae5129cb62255a78ecccaeca33 8fa4fd810b88570b8e15331a9029688b1a2235b01e5d9bee91e08b73b9abfc35 08d9d3552badbbed89aaea26e008bf1d9d53c84020ac1394bc6f81aee510d906a776e674763d79f89cb5d4d8c29d381dce1ddea2a82dc95f4b7d7452a1386a0b false -check_signature 6b2229cb3f1745cd613c6a5b39829770df0dc6bd5cdd64f4f3cdeaf04093a6a5 c958fbe8dc3b12cefe2953de6a7fe9658509cf06d4a7c59c120f63d70b438319 81f5fa25aa5867df88ba6d67b04767484a0807f272444a83db4488271e7de907795c4ea473c3a71bb495bf4104bc6a33a4a7a13d59c144de42e9d4e9a10d440b false -check_signature 73962970b6254757d2a698100777e940156128455d15c6ac9b5e86ad1e25f5d9 9ef844797cfab48b058fa056d85c7d6070759dccbab72790ecda95ed590adfba c93e809a159f05bb4ae9cd2dfe6a2a504b1683e29f24b8395b2f28b3d4ba588c1882384a9b2eeacde86129692f90cdbc33f113d40e77b224db13ab42aab17575 false -check_signature dd312e4ea445eca2d5a4c45ecee65041e4e22af71ea8839ae1abf5f8e826873c 0759cdf6b2093c29f05edbe748fc484c86b337dc35eb4c2f05c3b6d5fc02c20e 95e9280deb11c3eba7bd9d12e58a46ca82453a6e2bd825a2d051be651e18d4cf101c7787290b58914ea4453404c81a3cf9e39c122d751aa0d4b42beadb6d7e14 false -check_signature f6dfaafe3101e353c6ae2a07e3c5453679ac47f4c7d9c425b24ad3560e8556c8 c95e08462d1bf35bedee7622c042330f6d114d1c31e139fb4d13456e3d573915 8c5e7c07ba79aa1aa1123c6d4a9bd5b2bfafc3fcea5011bee01df7b04c16340e30540928e78c09e7a06aa8740f952c60269e22106582e30d37284fa95f0ab907 false -check_signature dfdc085cdc54d39990e25f35c336d717cc35d52087424ae09edffed02ccbf963 fd0962c58aa6a20c6af5b92bbc9062767ba43bbc1cab1d2de12961f49ac10171 aab5718f35c9267f51b8c616868acbcfb45ba0d502c5aa6889564befb149db020079428d42f75a652d4f68b1b1ff0fb9ccce9e3e89be942713ed886cb1eb7c04 true -check_signature f214e9b5b3dc31a242b2dd438c71123cd3cd67b0802abd3765bcbc31695cbffe bb6449612241d9a7b85f502056fd2f0613ad1fe1d863b721e4d1f2c9adb4304c 9c84d657823c50a9e04241a570d364efcd15771b3202a0730724bc99bd598b02c15cc408e05d4a14f3ff9f66e03ff68ab7d72c40897a32c3183b1b7bc8911402 true -check_signature dc14c0bc3e0c4d38a7462e09897e78c4a04b119cc165f3cab323b1e44a38be42 3960cee7c101f5833532252a24372f9c20b8e764ffec4c7cc75a7cf02110403e 781e8349ab367e7b5af882fa37d0c95ab2c1ee5c066b9bfa1fefeedbd63787c38952d7ad715514fe6a5ab76db5e24311ca8b75d93fa806b9e86f5c8481fc600c false -check_signature 455eafb5a909a45404d1c9dddae48795592d0937fdf53cf48cbda7621fabeaa5 3f9bf3e4e1545225153010ae6caae1f165776fc8e46cac9ce37d5c39656cac6a 8814e1c17dd7d74fc6a0742b0b4fce898d69852acf5f30ef52ff228bf89fec0cc4aa142ae6725c6371db04c7c3a54342dc18bd2455f2c0cfd570c280efdabe7b false -check_signature 11b6a1a3349f9211a428abe20d0cc9b159ebc0aef215ab977bc80e5b8b26ce03 0237ce6382e83da4a2e81451c53b3dd9de32c0f9bc624d29ba16f41e8cf47638 ad43985ae2e6a6649757e779bc4a740a2bdbb5f79463ee6114a6b0e6fb066b04039cb50d1e10849ec77e698caac8545c7d36d9ab8690f075ed302ba2e5b66532 false -check_signature f86909eeeb4cc145aacf68048c804510139fd8dfca8a7d1a0600b049965b2130 f05788b62e8f13ee0b634c5529f424f56a1b40efb0075919267f542de2ccec25 d9a80b9fd8a6c8d7b935d15f259d72d30d69257e0a49beeff5fdc228043331094e6e93c23b19e96b6eae49f2aa275a23a895585fd3b79793407cb253b59c7d0e true -check_signature 62ce58411cefb9a510f605afa36280cd02e47a6cb855a1573198319858cefd48 39c8f4ca71f17b65c0ac7f6a78dd79335b64a1d7564b6cf49c12813e3cd60773 24e0e68f14327e46363d1a9f1ed3d7b858a2dcb0542fc3931b7d52bdd0439f09d5bcaa03a30fdf0b43911e1cdc5b6bc5892c991af4252a3c7c63588959b90649 false -check_signature ad2a540c905e9e81aabb92e7aab4f8066bde0fc8389f586f37caeece6fb34162 917f46fd55f285f4769a220d6b92f8cb1023c2bdfa5a1d4b4f6e1c2d8c99fb75 f3d4ee42c6f48781a478d65be3cc236073120a373fc53c7462dd029938fff70fd19beaa775c2883a218e3c6c4c3d81fad6d20b4422b7b805de9ae10fdb862c09 true -check_signature 5aa5d16f66c341b27be95ac01a64d7eff70cf1fe22ab3ca86fbfd4b9cdec8032 63abcdd26494e0ebd86c965f6aed0101032cdea2373a4586719ec3afb60f725f 2d1bfce52196fe280a55d1f5b69ca137abdd34545c8abf9bc3709eb68341a206b33c83bb4ef419ec1ff04e7d3357c053ef7d2ced5b81744c724d7ea66ffe97f5 false -check_signature 3d18b5d958507781426a19acf14d1b718d795ac2b3cd9bc782fa8b85b6c533c1 429c646dc090656d803f0bd25e036d829b89f90bf0ab2b6e7b4c239cf604c152 fdbcc0c7900ea7419884d1690f0ab44c5f106d4ef6756e751b250cd20b4fe30706fe1368230c74668e66db1866e18136d3f9b401ca3dbf114dc0ae786c9aed0d true -check_signature 142257a143335f83b3eaefc4da31fcba0daa9b0ef8065c15ff8457ed95c03e7d d7d8ad230bffa21c28d42e5d666395242060b5e8cc48204ff89b6ad32f0f14a8 67e6b042c0bcb8c5612b3b283167dd2ce58a65411b758b5e79eeeb45702d3dc62255f448305e7356117f3adf3382ee429b37f19f40992a46cee12a614a5d16ce false -check_signature 72e94202f8886c664b79ae8c4c9fb943c5ebf289711c094846d177dd621c3e89 04a5e418f99760b40a19bf76aee25f480abc94975ddedb4d470fa2025b3d4111 75c156eda240e03cc14af66ba107e078d6b16b235a2636be768f0baad82279bbe7ce57d8d29357e311ec39fa29a323ce551cc17d18db75959a8709da1c0c170f false -check_signature a3242a6310ff98096083719d0f087ca8ffcaab0b6db412c52186d0b04ea11688 de774d3c0a02d802d8fd4778769ebda508b096e9e8a59ba5dd19d68a706e66df 73fc786ae0a545da76343d0efedce350edc353634503f26e5dcc006ce4ba8b00144e1468f873f7d26691e682fb40bdda2a68704916cafb7c2fc33e7f1df511f0 false -check_signature 287a5d4d9179b877f2dbc0c9386f22392e1c3b04623e39083db7f9a1152df7b5 249ee0ecbe1cdcf3d1cd337c531bf7b1795b036453733bef11492743b382dd00 09adac33a8f571fee6ac056b5d70498ffeff42077837de5a4927c277dd5db05efdbdc624ba093b9fe3be3777d10bf480c2c6952bafbd95b455117d7fa7fec384 false -check_signature 275b430e53d9901ffcff2871fd4b74595075021e236f5e93a3cf45470090b101 cb1bcde08998440c794874157bc43414a3ed2c24419fdbcc80d43f960c571fd0 2ff8a1bc1ec89687554027a8996535cda9b9dff53d200534564d1161ecb14e51ef6f61a2c570dde307b37aae21e87f7991af9d45052fdb6c906ce111d3b88b0e false -check_signature 27b0dc208dd1e8bd48b085a69c5d9c243b0a2945cfe46cc9d8ba8bc427a51db8 035223ecbb37a217c81ffdeb58fc9a2a989da9d0492524dea30c3393040f1012 73df9cf590dc4fefd698bb7b4bea95e544c5bae51fbf3ffa148a0bee3cbc2f0a7d7e4d733dc59afad63cbc402077801049f4fa90d7dfc16820b880bd12f5db08 true -check_signature c6897cd4532c52c2f1a96ec26a6dcd7ec7a2c2854c30c518eac725ba1903ab1c d96220ba9f60387ee39ecffaf6d58c6a2c694da5538430c329200fb1f7f6ff02 d70314c618c04d0c2c14cc9f2464cd49a624cca663fd73e4f6782141b67fca05e32afe860af10f239660603bf6ca7442005ea08894a26ba7dfc5efb9121e2702 false -check_signature 75a7a78d093fc3fbec0a5276e1dce164eb625a77e7c891db5faf6a1d8b80eaf0 e0faeb46a65e256c7359a6cd460056cfc243683db3d11cc960586bf2dd1c3076 acde6873a4283409eedfaa818afcde1956901f1858539e946452a948284be40bc5c0c970bb410dc7293ca41fcdb2190a534d08bec6b1c293ba6cf9e2db38e501 false -check_signature ab35d4f6c3871993292497bbf3a79af1f826cbf4b63068b3a194510ed829335e c51a2a8b1a3ba60c84d4e00383d93f1fc9025fe2f2668edfaa69f30c7c0125ad 80082f9b5bc91434e89ba78c1fcaf81acbeb16e749675f5493e154da0fc2ab079b5ef2713c88c8687b318a9fc17890f0675edb82105f5f5836953c6de75b7a09 true -check_signature fb06a1854e64c9740a112f59c0e2e2a5342fd9d0181643477a919142908c54b1 1e1b9db92d586f378ae5cead1c76ce3f098ab2bd14886f9ac51ae849737fd0d9 b57928882eb70e372e4dee35406cd283d1f536a4514d4cbc0ae5aa246715810f8976f0cc8d74dc17f50584db9b744810de340f5b19299f5fe82b30b656fbaa03 true -check_signature abd4f193f9c2a8f4d54e79e60a79f20859db957a2077494459a264078f106ada 69dd6f6edc0bcedc1ec4a92349acd86fa2e30faca7a86e5bce94a1c5b7bb791f f9f4c10728b2e3ab3bdceb8cfe90f10bb0020fcbfcaca9245cb0f3e032af92cbafe953861ce040d5eb8d419bed6873a29c14cd997dc9200aff01f4cd4be95c04 false -check_signature 3c80f5daefa2c87ed3574e38ea8b64dc4e53c3b22976bcf29caed356f22a7729 1f79c7cc0001311ce4c9cd2ee69712a88eb7b43367c2f12511334f003f71869c f6033cde3895476670a11f6c5f0083a163e4c68e9528e9d6d117de84d5935f3a1caf355c2d9336caf63cad75627c8d209eeab3a9e7f2ffd23ca946b5d3aaf305 false -check_signature e6637b6e76973ec0fa2b3ef9f1f86042ba0b0d91fd3743bf0f9ce38767dd401b 9c809bd6bdb167eaced1d79e8121df10fe5e25c864a27957faeee57b2ac38eee 7e35ad9facfcab913a07f3562a912237f0a9b6b874a4ec0b9f7c659de45c050dab5d78aaf8926dfd69ad45f0b60308c249b50e6250c102846a82cb5b0dfc6e01 false -check_signature a24f176d58b4a8db3cc4639726eb99a7627a0c1a2fb8d8f348e3eee086ef362c 3591dd7c73dc9e3a7f7eb12945c362a2b33c9552bee4cdf57f76b82841adf2be 87db653745a3c1343ebbf93a04a683217bd920cbd1d593ba46ecda49b68d720fe8e9d91cf3155ce5baac92099a94f19d658edc84697faa7402ef16e0ed59820a true -check_signature 973a0d5a5e9a9e9203223a3842c87af2a0fe2b49d733b3e4305f8bf04b1becb9 b3c1eb655fbba40cb7d76b23f6bc7854a2e8eb75ced22c8e8bd8d0b1135887e9 a53aced45832ce5d2474812ac6b3679a17e636766175655b0c26d46cb61cda05021cf04fc5989804c69e2f8911b97392d7ae51e322a6667721a6e8da5d91c00e true -check_signature a1576ad6adb41882e555c326602b2553ab7602c59b14e2db54a6c5114b8e8557 8252337f80000ab41b19b82f383f70d237030f800a4348f258daed62f35ad2d9 98f6559f75a9d71aa9209671ef8051fda28bb26c3cef4578c1b8e6649a51a70b3a77b5091f1897d8ae00a4e140f88767aa5d58bc3bf0bd61afe32709b666c2bd false -check_signature 249ffdaa75440bfc5f0f5c4c47dda52cbd384f37b2e0560cc9880c1c6a3e1049 4156757cda03103488b0bddd37cc4bb91064157b590c4e4715db9f9c63068e0b c8c75cf862e685f5e3dc698ead789ef791f2bfcfa1638a610115f2f634d457ae0684f8185ebe7c885ec14c8906f15c4717949cd69a0b4745913c0efc3af93f01 false -check_signature 85d0f5423d4c378ddfe1313a6bfb859b0b2ad5eb8a551bde184a15b47453a5da 504f7c74a57ccf0e1b068af49883bcbe6657991de61beafc778d317cb4d7bc74 118aa6e49b1d87515aedb0ffb0c712e9ea5539413b56f7b528849e62fcda1204694b87e5c9b740f88e82476272c00b49db09c9e621644a6eb3c5a44779be0005 false -check_signature 37776c30d074cedf16531e7b21d52682b5b871cc2122a4f3409c97a9e1a28a84 1e18c5770ed58733b4e821040882c4def2695d5e205ce460ad67dbdd4b0565d5 76a232684595e4eabf5e6161cc04943f0fcfc9ed0dfd616d28f81eabe2b9a84dae2660ac6771c86f9409113568be287100bd6801f04d079bd0b21c4916283eea false -check_signature 5c0ab75d16eeb4db165d9608901ae08a9ff6a009b57c782b98f44bc705a435ba 8e05ec8a1df90974abc6245e6d2bef715ceb4f8dbcc7de495013f8693f574de5 b89b0e2ebf57f96dc70e60ded7d876c0616fb30595d02320acf3f78ff1f532fe4b62e6d6e087938d50389573bb13e5ccf57acc2cced6e22ec391b1dcf19ebb02 false -check_signature 1f48cfe066bd58b15568a41d01081416ce846659c20c98f1feed23d7e89de013 4ad514874f9664d1f2bc797eedb589e393e9679c7ccf05e6cb3053df220de503 51a10d5e9b230c2486a51b838bfe2731732d3129bf11142bebc94c415f0b35087029c49ad53ce8fb866154f9a87d362256051367417905dc52409e0fccff5b42 false -check_signature e98fed9856b3367a1427d927ba553942f8dfa78c27cae550d60f34f9d9c797f6 e067842c7de0ecce72e5614911dbab4cc6f22fd60b9c46d88d04cf6da21526bc 79c181c96170508bfd4cbc146a300f9728dc875cb1f6f14167e47c493f8c1d08093eeef45a1f55558ef395c162a486e92cee2f03d3453617dd076113310dcdb0 false -check_signature 23617f4d106215d445cd414d6e3d659770559752e7fb33e4b4a1c7fb66f6d7ec e21a6dfbccf6ea06111f83721421f136ca4374fd0f8f6641dff5a5c7747634dd 3b529f83d992389da2578b3932eb6d31d5b30df4fdc5c943f126a0d2d11e7008317da1e667056aee21b55f95160fc50b93b496c440409518e3677291c3277e8f false -check_signature 4e63b42481cb1b5530cb2c6fc3d085e998b4294da532d6858ac76922a6abfd38 15521bc7f662d459fcd2e6e12236befa0a65ce36a04a8b729ab593538ac8f82b 01b2b15e0e30334bba7bbba8227bfe08c6810700830e30b35018525f094932391d8b5d28519bf26a2304ea71833799801e6c4bb7f0aa9727045d999ed170f400 false -check_signature 3adb7fdc1c1988c5ac334aaff227c87260cd37fbeaab4e6e9abd2461d23d5553 5d22b0f9c9ad2e9abf423ae1026bed06387f5f21dd9b52e4d59b627f37cba458 611f3b8967f107cdbae82b58d077eef1bb178cb08ee4f7567ce83a4790bfb40a22d947232222a34515dc02e078ac64139def013646c6b4ca633c3735a09ef80a false -check_signature bb86845fc42363fc45be68389c5b5183e798564bb27a8016723e9258d0b20ecf d4ea920169adf19f4c47a15b22f926f697cd38eab8e067603c42ba0fed226f1b d18b5e2875c399518bb0c848c6805b33354a4c1d64880f3dd9f07c823a10d8e7796b38b2f306f2a615e493ead006ee43e0ae78f4e084d79b42b909a245429a05 false -check_signature 083df6b7a5374b4a4328a26049d73d1906ac9ab975c43475236cd6421668630b 618da2329fbd8db4fac0e593d3bf4bba361c5f22f3d246d421c96ff38c508fb7 7b13c63d8f423dc82e4d75a7822c8b41a0f398614da8f6965ee71cc239c9974e0ac47a30d9ec910c878d0772fa0650ad555a738c6c1dcb5d7b2eebb1cdfd1705 false -check_signature bb569084e6269afdf7570ef95680fb386b4b97bd26757651a2a989c684fa72f4 f56af32b4c53e4fda4a1baaa7abc1a8b36592a7db3f5c48acb5081e0d4596a29 11508f73a76068b806361d27e50a68da34d7c352a0566ff3022e9696d4076d05442aa9ef4cce1c9dc54832d7de74e1fba88b26104cab4d5850e111da98e0bc05 false -check_signature 99e44c0891daa895338a47a0b8fe1be14600fb924d90b5a11d1b6a4e189e0226 090e12af5046eb76f742726b9dd28dce651351e81cbddbe856ffb8ccdc653869 add0db98d04f3dfbc74051a6f870327b693bb6a73263df2fb7ae45cdec070e0ad2b1a881d572edfb41287868d366bd8a938fd8374979e0298fb707ac2bdd1109 false -check_signature b4a3b72e9a697dcd178fdb7e9cbe7dad1afb0f6769e8bfb9120611c84bf23aca 613a2be21f964bec1d9cd9dfa298757671291808e5838a7c194cee4b5e5a7465 e497a9d0236fc9482f6e4da472b08537c178c2318bffdcb6f0eff2022d0d4b0be6803322bfdd91d923c8e40288e70e92a31c9fdd73b1d8fb665ab15615943706 false -check_signature 849566caf8a7be9959890f8521c984b76eb1b5756c1fb0fdee1e34f709e67e95 d01be676cbb3ef03157f7b8af19830a67d87ddfa2174b93810548a68dbdd23a6 70feceba3f56446b6c70d989fe44e78662a16212fddf5f674daa3cf809824e6172294d3fe25ffcac8f1e5e07b92cca477d6b5d9ff5b64af42cd318cb8b8d5504 false -check_signature 826aaf2996a45e6d70a0d90591302e06ac152c27809ce75cf28e1e7d43179a7f bf1b5b66e90e77981362bd8080951b68d668f110bde5eaef1cd475a15f101d79 672822cbcf05773f1f0833a96fc7a8906976c94f21fd9a5da3db2a90bfe85c02e2a6a5de5a7bfb794907c019361475c4296464c7e7150c9f1b65a07f1246920d true -check_signature 87f33ca5b3729506adee9e45e75c6f50d352fc7c152067bc6d52fe881ac91b0c 5d4c77152b65bef82f61aea7f41edf1a178b7836df3e7c5c4bfe740c4d55c469 25abd6e6b52fa1c6d4eb1927c2bd7cba0f739a3aea91a4b494780e420dd4121a9235a6f939056e9e857c02447c3a3939d0589cc966f15b27b90b428b86a2fe09 false -check_signature d41b537a0dae20b2bf62d58a7c5d6f2de64ed8f61d40dc4ea2b10fce41711c4d 44009b3074bcf9531c0ce6251b05fac5fc8dd42aadfead9efea23d142d484f8f b944b8181f3c194d7be0c97527e457d5d64e59270ad5cafb4480e26581c69604705ab4223fa54d9877a85c4b2772fe9176e283c5476e1a096939e84cdf601d04 false -check_signature 74173a74e838ceba4384d6fb1c799a8da0c9d3a36f0e55e2d1586ef6bc7f50e8 99a5de8c8fea4507448c29c0fb7796322c1016705b6b9ce30064b244dce37863 4b1e0a1263d10717cff5f60292491fd8e1f4286379966d1281b1ff1faf7b75063ab5851da21a8962e5b63f32e1ceb2d6bf6e4eb9b43984f085653c55f7f0290b true -check_signature c5eb266cef9fd6b1489a99663cce2a4174c23ead23170cb458129983b64d8ebc 5f5b3c5ba2f1d7221efc4c935a1ba47c9c1914e9b519544c9948eeb25432c249 ed196c43a62f87b5f4a11344286ddfad932f9201db234183852b43ceb33af95a9613203a67635da058067bcea1f4448df09992dd583f5e6a556644a86837b706 false -check_signature d293d4fce4deb52cd5a2e5c03c840a124f388b0a637596857977131553188e43 ecc2fcd003ff3c403278f9e79f33f229a0e1a761a1c115c7e5e7390eaf1037c4 4e746d12eb4542fc9ce5b165f7e52d2ad08f2d3197c258f8b200389d0704930150a1ed1c6e7b967134a6a0dc765030dad9d58b6f314213d0bae70c332f45a7c5 false -check_signature 2247b7806d80d380ad20dc6fa543c5fefd31aaead98b81644c4ae2700eab2627 e85fa500394947dbda1a73d1e0891c0196b4de2277ecf056e4274270f5ffa0fa a4e214769727848edcd201458c3587a988b56c95afc3ce6d727796aa7eb68400f4e1ec062e1174b52dabf31f9b3e73881da877a733c008f2d125c37ed112afe6 false -check_signature 548c9e7f81792480262b60386793ffda30b0b3282cd9775416faa8d94095e0f0 8d695a23bbf5f3a17461808a6e4fb8ccc03c5ba72b5b11cc23b029a10a30cd66 b7209d6f6d0d9ffb282c1e98d4c1cf171f10d4536634326854843ab7b4569fc47b968c99ebf6a1cad9423935a5ac23eaddfba794e8584a3b59fa457398ec8706 false -check_signature e57d66f06302f62d79887e3886c5779115b89ffdaa678ac564636ba0f0ac8cfd b4eb4d81371bf8122287cc943a2caa49d6392531abe4c59889cd094b49db7d07 5278baa2574ef3985bc1fd802249f36c0b76f9bc89d434940db848e5003af9041f9198110fb457b98183a7e0084b88feb7a9e730b9d11c257d6007ca3726760f false -check_signature 591982842fb3b66ab2cf92f1ea048d76914b1d1b8dc925e634c2431fccd32012 98d2c139f0b4bc0c36e068d11d541290cc65a96857acd0b88dba9a12dcabe6b3 111717ea846395d8b2ce5422699f01dd27551c5c67de977ffa97cc50b8e9400b632c2705379b33e4e0d5f1e2287824b8c0af7d49c9d6240b04fa14fc63cb6f0a true -check_signature 9838ec858557d67d8ad6e2f7008fa819a2782f14c7af1126ef2c570958f1aa95 f73048fe44a11328a0ff24e11756c5f6eadf0af3419a2a5dcc97905d5149649e 1816b8f2fd86b71a14b79e9572afbcc75c6dd9f0628fbcfc17d73905cbd7e308c6a366ce1b3e6caf0e80b7dd5f5939bb073cd5fa8ca5ee2222fb818ef32208e8 false -check_signature 8d369aaa228a84497ac054bca5f47271a4585e4094a65175cc6c1328c4838e99 5905677ac6cd0c73f49288647e63a5abaf623e8cc52df57ccd90d6b608c376ba 9f4c983aa411401ea411e594426a564cc0905c059c7f787de80d2c85f757cdc9869546eae2189d89039eeb5ba81fcfc6bfcc221d380dd61e16788b87bebf8aa6 false -check_signature 935f59ae90b09a27dd8a84cd7d968b838cc9186f53e7af7516a8ac1d4a5abb67 f40a4200e2c84c58ebb28854211d2f7bb8e94364a931476644626cd2b09ae916 0d431a27e4920d3e6feae99366c896869c945634c34798ea64f352bb147dc30039d37aea80b2068f871e2ab6d5fef88268aaee1f9c9b6b344524c96118f06705 true -check_signature e97de8bfbc46a4dd66cf221313697e991d2233b737fbbea140c9f9e65b814e4d 56183e587c64a1797a048328c5d02b4c6446254d6851f4479e143e41635cd2b8 5130eec4fa5383949473ca462f4bada147e8c07083fbf4504dd80dedbbe7720fb0bdb70abfcf1cea2c72b160eae3c4b81b9d64b64f5bd4e3a3598f3b977a578a false -check_signature 0db667b3f847dc51f50e00df15051a92ee42ecb01a5c6729d8a7dce2766e482f 66c50afe35e67128afe73adc21e1e17d535c11272a282e0eb51938b72b0e0fc4 6bb89c9c853159fd0b10291c9e44926b6290375460b002b374a65109e387a30dfa0d9a93a681a3d26596cefb52099826e321ab63a78f616f97724d46642b8901 false -check_signature 262c96602e33f162e7918ede45f3ec5267ac6eab7c02af74c6b6378d0b3991f4 c972d66add2986e51ebb66976e26db6c32298d602f77c4e6fd55006e04ca384d 0f8dc5846d73eff2809cc4eb86fde0a0d2237309d2aae8d8816a1b6ca1e3c7e31acc2ac23c236d047d102cd60c3737c67ccca5e3d58e5cac982ce7d51abdcef8 false -check_signature 2792b65813705a407f3ba8d1d34d9a7eca8edb266ca27e3fe99b2ffaab6fe504 3f8a7079202c335eb0ef7e3880209a140f63460571f1af86a1dc57949a01d474 3786d706d819f88c2926bf0e29b36ac239338c8b44a32d0095ea9f1640560d0f0bb6de706f71057a15645d752b9fc7c48e25010db9eaa5d383a8d5ced6987c04 false -check_signature 70e6d385bcd64e89ce4eedb9c877baf4bddac34d0ddb9c09ab6ae35c9faa6a1d 623bcf696bdef2b6f1e34609794de0e36f62bf97530ccd8ee9948c9e16024957 6bd9f707b8ca583e6831f878cc708672c413a23de7276fa8d69286649a807c14be68fc623f81fcf0b75db6e1254c672fc62e02addaf27e2f74ac03dca0ac2902 false -check_signature b09a144eba1faac9c36715008f6f79e9894bab1b43a9f8a73a92c34472bd8a86 5f8d3403e2e62e09e1b8540079193019a9883cf2ef0f4ddcc8ab1bf067bef74b 8e480b962ba468117bdb70d03712549854c7c24475b351de86b999b97a333ebb00e6a0c1a7c081e9181ac58f9b2a72fb289c762c519df22d95c845032590eb0f false -check_signature d95f6291795426353efc33dad779d7a5cf05f7b9e3e28567e20115a10d695b09 3e6405cf103708e2e62366513dc282e713a71f9022b7e177b462f542536ca6d6 129ac8c4e65711337e6dffd60960f130f02204301985f116b718383dde900a201ea52112f8da9dc6903501d5d91917f592e7a5247dead513dfd1c891c9f12e02 false -check_signature f81916e4f815de7c1b1261e963d89bceca9cfbfabeb0d9c01d7c32cc351194e6 f7a5ca33dee5760a88a4565a6b30b34726948156c06e3d2d7be8a508603947c9 460b247f45262c98379b189714bbde759b051f0c5e5740e71f47103a0ebe3937a1b809b4bb70c00335a6d3d45fe304f96e55843079328d9130312c3213e6dd0d false -check_signature 31fcea77ad3a6d1a629c9f89eb06e9d4c6349091cb01b077a2c3332bc13f4b33 20f4f1c680756df0ea1a568fc0b28b29ce97bb118143f1683a4eb1059e46fe43 edd459cdb7a02f4d55e45d168fd2fd1a17a1c9ab4d66cec0ff7bc407bd0443d96360d94020bc01744ec3e14f8ce5534acb3f84662bf8201ab8e5c4d6bc3e7c64 false -check_signature 2051c80e920d57ae3059308279076dda7b43854b0f75cf20bb3460e227ef5a91 81f0ec7e209c43ca30aab742a2a4a702a7b836c693f6613b7bf895c6536f6379 1c6f1102de216086b69d5704ad9bc5de5c9787604c3141f4ee100667f5f06fa1f49439b2d092fd8d597cf0e092ca284052123771307c3392ff7acddfa48ecb09 false -check_signature fa7dbf3794624aacb97c9dbe5aaa55c0f4b3583e47843f2cbdddb43174f55d68 3e3cb6432d8aa8c21477d122d9bdc3c52d82dfaee7ef428bc4e798349de063fa f57ecef8a86cb17b3ad905e1dfec658d965559cfbc8a9b08cf0d290cda77c70c73d5e90b4a3e2ebc6fed090ebbdff05b8ce212bc0dd0df6f421e45729c52aa06 false -check_signature 08d48d56ac8b9a6115e1f42585e37195088e3a341ed850d566c720177470c6f1 c5ce8f67f90128797c99b03fc9804b1f81efb1c051d43e386f6649f647f5bc64 b849a8d21512126a161da26386dc3dd604a4d4b93357f5354414c14244ed8c0e0146e6579cda6dfc8e73efb3202e7a3601605128daf79df07977c1e0d0bd9d00 true -check_signature f65407ae3fd72d8fde9e69912dea997dc4c48e7b5a6357b35ae20eb631e0ee56 ff5ff2707c52dd6c89e3f58cdbbbfd53269c48e44339bc44306c96410751c3bb 38caf0d41ca7c10c4a3cb0e3fc49d984d3d6befc00b8952d03c4ced4ea2946b62ecf8add10d047b928c1650c62f47aa6844f57fcfe3e62f562601bbb072f1501 false -check_signature 03f04d6ac5203108bd72651be74e21aac97813e6c39f8cfce8737c0c394a196f bbd5b352d2974a86801326414dcee771475494d7388b492ade3d59ee5e05a7b2 17e7877a91d7d2a669393fcdcd5538f184a3804aaecc9c2eb1dea56fc4fdc2075f6e00147873ee16470f8686e4c4d1036819ed31dd5e96937d81fb83c4c21208 true -check_signature 165eb53ea0bc5b296e42ecfce0a8c9324a8c5dca8c352487bce83b77bc8b1b2f 863c274ccb81a5832382af2fcc6fb34a9520a9291b2ee2173cd9747cb58ff248 bb062279761d7af8e3f6aff32f0c6291fbacae481063fafb317e9b9f96f8a96549fde245ae5f64b35a92b9ec69ccd51cd069f0a208e2e01792dcc907b5df090d false -check_signature bc85a04d3c62c373b186a427a7b9880b41e9c68e188893fdfce5ae9ce6781375 7326d62d3e8dc69bd07c6d107aa1a7b8bdbb320bec644e5beed7e3adaa47cb96 a33b6fddf84b726a74c580b0f851acf421c4d5f49738b3de436f6dcae196820437ae4edab40a13a5f363e5f09bc007dc58f482a701df0ed95682926019f0ad0e true -check_signature 25f475d625437c965145487c1ad906ab771d89c7024951d1f73af64e3eb2e6fd 0cb748db38bc45b30569c413719ef7d7414b5e13b8d85443b976a8238af329e2 38f98344dbe8c2c18eab095ee879ca5955b7db573b966f9cfe51e5fdc33b0502f31af3008c27c9eeddf5086b102b7f1954eede2c8886ef0133d0b9bbe7054e06 true -check_signature 808b1c02c4dec1f5c4324344dd1e58e82b1a30f54163abaf7bf8e52c2c1f18f8 b2c496b525db2ff7ea47ea16db42ee5ec4765064b566041a3d617df51adb001d b2795776c82f053cdc8237052406c80cfa5a993dc0e13f1ebafc8245de95b5088ba2e45fa349dc42dec97e51b5eef020ed1010a0e2cd81ebf63f6ac050fe6d0d true -check_signature 33f64539439107c988f457034d3485d383ec69ee5f5e0e34cb9247f4e11025ce 5136a7230e4a6a40d757f279fa535dfdcc29d7b723b142741177be2c4fab511a 943f8dac5f3c5b91238e74961e1f80f00aa3bf5555748c6cf92cb79904f0ca45a6d16e5fe94b2515f68b7aefa71b6fa349e67182a915726f0ecfcdfe74d72b5f false -check_signature a4f46bf4701cc45a104b8561458304dfa11df1f1f22686a5e3d24b83ddbcf8a0 dae9d8cb54b6b0c7f395a4730106a4c9b21ab15d8ba45d956349f96ac88a2609 c3f61facbc88e4bee9fbf520124e7b3fe7b2a617e90a98b513abc13c0f98c0df6d6628d63af45651f43802b8eda426e2f9dd0ab86944ab567c58351b3aee62a0 false -check_signature 9c9a490b0a6520508038c71b77ebf9e44fefad691c27efe0184859342b21a57c eb0430d1473c1747239cf6f7282233770f1fb5d3c4da4f61d09db353975af501 b521c551621e159020536b7f40f2f9d5a2b5b7194889b9942ccaa57eb16d9605138111d36647ba482a5359ac1c011ff8d7002a363d5a7f20b7a0acf66079cd8b false -check_signature c91078fb8eeaf4570458a278fc5bf3c1db35c0465b9b61171fbe1f92fcf4e338 8ef35d70e6d171e47e471a04a06c3584609c47b8d3065e52fa77e66deecd4ae7 515cfc0c557dc8cedcd809196f38d1b9d261d5f40ca1e9dc752b0b005ecb720a6f8a7abbf248041a3e4e6bc09abfff6f93d2c85a523397569fbae47786707704 true -check_signature 599f807ca40bcef83c3f444a6c5ec35df3a6e54b37aacf6fe1a12332ee5cc0ed 473876b80539da86492706fcffd1c1a10660039aaddbd06862d4484dafa4a529 fcb31cf7f5d3a1e0ac3338d71004e3676037c622ed5c2813c013fbac6104a42b94130cbc127a59106583366687b1a90b83222eeeaa03fcb4923d93181457fb07 false -check_signature 8e2aa99babf04276d2ca1fbd64b3940af209b36aaf477bccdeab0ec3890b3a3c e1260f75182ad271ca7d330e4ac65b854145dca2ab6ba864e5c03622ea6985eb 736b582b3facc1c52a382730fc0d577322b64f0afbc9f1608d6f7b93e011d9020708c55cf86870f37a1c67c8149d0d4f107b0409611cb4af2c1d6eecff535600 true -check_signature fd869a5991d685b91f660dd535208353a818a1f0de82af32c9831142f1f8410c 56d6df00bd6aac54f9db936621625e7a71e737ef2504dd47ed17b2c6d0efb27b 7020d2da17688127d237903f6117c3d229ddb117c4e96ba0db78df7d50552909a2d689af2a98d5ce88cfcec74d46fb77e09d5efcc13c0efe162fbe854359050e false -check_signature c47b86f60803ae49411aca2225ee186c32f008af819f4a9b14d9909b5c892d1b b56b0fe592d099e12ab82868ebaa6798e9ee72bb0bc5e6def98885e9a1539af7 62f3769066a457c51068cc7dfb1dcaaa1cf25950d290d4139ee528c7248e8d025a9713f1307faa1699f2bca187e0a1d08490e4e99b247e64d34eecba51a1320d false -check_signature d0b0027bb57ae83bf4829a17d4467de0aeba4795794c532e4366df7e9ff113d6 610d38e5de582a52e45200c8838853598fcffe1a467ae2e98ce0646f78063bc3 b728ae60ade18c596508359369b1e8d7ef667a7a94d4f99f5102ed827c666d044451420cbb14ae326515963fd38d2654392b719213dff9fa00996878b5b73007 true -check_signature 71723bf9389c5935bf1640b7a5e6c17ccdfa392f422581ff8b88888ab698ac04 ba7187d32ea113e1e66c0a1d71dd1f8b41cdfb6fd88c2c6842ff0a653051e43a ac6af6745e6375abc6faa306f6b6e5594d7d8b0cafe13295dc64eafd9def7fa55495dc27900f3c22c17f5da9c974815ecda63451dfa5640ad096463096a91002 false -check_signature 07d798f3a7600c9d8836f2c9d912da6dae3f6ffa34019942289d817a1535c3c2 894e2ae63a95cd94bf03d1996ebf2d41fe1e010b4c5143468978b52c3219b3cd 89a522a511e75792b758051c713411e4fcabe9cbba44e88bbd8db940195e4e31070ccc2376067cc3b6b4063e2a4b0866884b7df23050b2fbd90bb4b68a35d80a false -check_signature 367f50cb7401833b2d2d4e61a705824a5efa4dbee7192c039780e28ab5f8ba00 e3f3373038a22a641d8c7aac290365b651501f11b5b82a73379baeb8a373ba6d 5e15063433eac3d5bdc461b5701439c10ea14da6e505c10c94ca7c4e281ef70f412d440e6e7612ff9fce760d44df17a7fc287fdf11cbed4a2d6374d9a95b0101 true -check_signature 426a7375df13d1512b44a87b3ba2c5ee3f999189bc79519b392456f3747f3902 28abe0c857966341f2de1a9852f63a94c11d61332da14960fb9037a1bd6e4a8c e9d878fbeb4792e421ceef4e31fabcfced9c625bde5f368431aee876769155056b04968704d202d51c775c2860ad3761a9c39e78c93b9143882799eae47d1505 false -check_signature d85a8edf2234196d6d6b23eb4df9be9b0cedf581840c491cb1d11be359440696 f5a23771598df225c0ff5d5f5d7593007734080f800dd27b35207cf4c1fd04de f8a4ca9ed8b7415e7be9c731f0d41a6f5cddf750879a5ad9bdbdc8f4fa3e330dfe41f883cd00da8ca2325f55840a3fea67d744f168c3ccc7b91efa9a761dbe0c false -check_signature cbefe589735724e743d7f8585c6889bdcafe5a6813e4b1f37c3cf0c68e567fd8 449f5adadeb587f2895a22eb486740903e48bfb751cb0caca3e850c65ec3aa6e de2d3b236a0b67d45db08e54e5e8bcbaccfec848b2261ddd5fca735f132ad97cebe3bc80679db5befac534de85c33714aeea96a85c1013a2e55d5833ba89b262 false -check_signature 850dd159ca4318b813a9984f1b5bc8cb0c3d940b5d45e7bd055a5210f434b941 fa45a67d67f2a892f934f8c802bc78875e7f7428f0395b16256112bc6a4aaacf 43768b5e0bb6ff2d3daeb25f6f6ed1bf63dec0867a6d2048fe2f0f5de1647d74886570d34fe813d660a31c500abe448ef31afe61600a6d082a4f7f2bab4ce944 false -check_signature 809e57902c168bf36f5cce16f5f724b77b6f93595e02709661bce292a4c8c5b2 723871c2914cb5f7805b038809b31280cdaa0984f2765d12c033657aa96831e1 fe11e6a9abad6b1c2838b75676eb70939a5a00f245803ac7605080f77b729403cbce1d3b0b7e63cca0ebad0ab03b83ab203ae61dd1920d5690a6c0017c9f7700 true -check_signature 3ea8625c64f6c8fe805386c5875d83bec85a3bd2b4a8174a6c6af2e9207fdd6a ff1f5d99ac6ba3283afdcee8a224bd43309871e1d8567ace6bc337633fcd4440 7481e0456d3bf36abeb77733d4d56e1c5aa5d17eebe721c7098f83fb2507c90e4046d54e509f357ba06fcf241afa1630a22ee05040f85db25f983d9a95cc2f07 false -check_signature 6a27d33e604d91993b215c9d7f45855e74472b0d7355791149d41261683e9b6e f6e0c428e51b0f2e79f53304de9815ff4eb206d327fd06aa67fc0d932d7ae0d5 a1f6efdb4759ae01bf099b689580c374aa7bc79d873ae436b64d995f649f1104e619ce29f01f1136c272a3207b0997a2252304a9c8fdf32b5b7cf35b628a3a0a true -check_signature c9240a21322dbab65666dae0f128b06eeba51924bcd98a15d15862228289fcad 32760dba8228d05e5f8b694d5645536c1c4e992d48485831ab492dcee77a27b0 0ceef5122967c192ff875453f6598f8d64483f5202dce2a2f9a4eb516fa1cc05abdaf3b2ed6f6fa4f589dcf951514721a12db7a4c92c0d4e3e9ce4c96f5c81db false -check_signature 4367f6a9db47d546f37d182f77f7208789fb911571997adfa74201a213aaa92b c74138a99e5429adb7cf11a06035d8a97ace835d1e5d56307615c492912ad70d 9be5f33a0ef9b2f3421c7a4e42ccf32a058b06f03a8337f968f1e0770810aa0bc5efa0e7eeecae0f8bb593f6a7251320bec6e4833da578ffa318b907a0548492 false -check_signature fd0bc7052fb5cfeebfe0b086077681c3af90ddaee4d705cf860c3450d9cac1dd 32e6c1e068d7bd9fa336cfd7b16d86a1dda4ad93f90e51710983a58d74139c98 57c0514588da3a11ad5a8d79ff537940eacadd0f10580a3c7ad23bcbe358e5f4853a52d2adf35a8cc43c24ec8f6aeb158ce7af495509cb4a9f6cf5e7add9f90d false -check_signature b34a9ec6c6070ad2e91cfe3d02aa5c3bb6764b75d14dd087a5f093833b7991a2 68aa1e14fea6d8bcb0099ef3373e4f8a9745ce5c0bce5eea34b7a14a38e6228a 2b70aa61c68995a2bd2f70d066482952bdf90d3cd0818eae1a2f99be07ba170dd31719486e8989ef49059cfbc43965bf48fa5f8ef78719b0849a5d8761968202 false -check_signature 4765970012908be2682edc180787b22fd9c45a837974a7d765d5ec2ab70c2b5c 43ea87ddeb61e4bb2d12ae8e9d213aec52cb80600db1821160c81836d6c2693b 9f2d54369e74219663dabeb52f017bc9f2faceb081a3781ba4e44c0e578a480edbb18726843b460479222c5e69b31f41ff8c5308b2459d57f10cf0c470afce03 true -check_signature 9e452b85deb1e0125247b86832c2aa01782e3a532f428227ccf9e41549bcdbb5 fcf9ebfb6b6667384564440e8989b76a10e86e359f3daeab9de85f32b7be9e7f 91e13a8cd9b3b96b2ce1fd209f4836b8e090809ab8a01b3c8f8e6f73255b5e05ba0d0213ba6ba09058160ecc89c5d91fb2f4336f14951d336bbb85eec79aca61 false -check_signature b047505a4b7c2ea1b1a00ae9c4001f6793f0c1ebd37f52de9fcce1cc3ab12b95 b0d16ab72120f1852272f648bc9bf5fa54693c0a9b596250af2f892dac52bc29 256bc1870a3e4871eea77dfba0db2b8f7a86aa97da077119066d19bd74375c0162fafa8f086f263edb1a69396bb44b27901fde9f51965578e69410e3b2c8472f false -check_signature 6c6b74c59a8123b5d00089a215ffe39bd9152a11819efb5f31d177d9c1b91e3e a2a95477cb90b8eb44b78b4a11fb0fd6ef37a82de784a771b03c75dbac43b387 c35f4fe6290ba3ba0dcebd00cce588fa69fadee7791126cc3edcb41a4b6f12053847adfb28ff7ba59f82cfce7a572256bce8a2064c75da059bd18195266ce6d0 false -check_signature afb49c2a6bbe214eff37ef910f25522e57caad2f5e72de48bd7c22a8ae757f0c af2cc5aaac84dd9e1831b17c51f10824d5395373f6138d7ec0f9a5d135f9fb4a dac03c9ea200c43c9f9b8e0d312ce5aeb5d9bd8abb1e4af681fae0a88b30931bf6fdf04c9a70b71c6a4187d715ab43b72bf013409aa0fa0ded5d58b0771fad0e false -check_signature 6056f884d2bac4929775d78ee792d9cf0b082ce8a90696f7225e63e213e65265 2870ee66d3a35dc0b80a3500a9bdb0fc6e6b9fc3bdafbb3a90ad5c4043327440 80cde10ed0078887a1b78bb5b153704fba3adec546c0182207d20f24584f3fd6a701398ae8b35d752d378f00e3aaa99f42e94a51ea0ef7daf83b44136056f00f false -check_signature 1f81a250872d10df1ce50d9e920aa1d9c6449386457c1393070255f2f392ecdb f75948c7b82f6f59b3b4e00faa53edbcb6f632b7a3475194c5eb089a7bb64f84 46820505dc172b4fe370c967c68bf90292c6195a22f7559294f33f0dcff15d0278235579097defcc3c32c7e3d6dbc45c8c797526c9859676c3e2c6789b5f7e0c false -check_signature fc097ac71e0a75b17d392284d6995fc1219e90f304e3c7bbd52572f5000d6d93 18bf263340fda3c2fb97ed3cc1cd8b8e44c965e24df4b1e6616198d30a0517d4 2d2327e66af3d2be7620f4fa7bb14797c5feabe180167fae68973016fb18e00992d6ad5ca9d0a2ac6cad8ac849c053735504c657e0be12be5c7cf5793a7b0b03 true -check_signature b23af6cfcf81ed8177a49a7e1485f03cea6d1b169170ddfd93456977fc7a5949 25fbb263b62901c4f93bba6acb0942e11cf656134c647440ce5a4396c11d36e6 c7a86e75de8de7d22bd26b8a4df21c5b1f62ae8077160a7b94c5be7b9093930c6e7dfde4aa9113311bd8e1fd8302ad095b578f841a95746217ddc1d0dfde5ac5 false -check_signature 3a91026ffc848f779d56121ede0447ac3ba6882123f1b598b3765b19145d1a8b 1e626a07b0eae9a020e762f441796e33a090017593ddbccd34beb1f198c88434 e7d3a11da75da69e0f5c38793c3c729433b8c6ad0c7f346b6cab454e920b7a007853fce160f422d3d87bbd11a538c9927b76334d6cfe3db55e5148e56606cca2 false -check_signature 4a6dea191f1a9d3ed0ea32680bfd394642bebaaf3c7c132ff1b35a1e58ac0ce8 248043f800e90d117a82e8a8aa37fc9f5de7d295e4f659fdc2d029b38d8bb3d2 3db4bef72d367f829fbb971c172c3b70368e802989e34ecf8e990148318cc00b5f6254f29cf6901e4e010f4587a6e0c4e9f48d172db31f6f1d0f0d2353d17cea false -check_signature cb641e240fb7ee2ba731baa41316f2cdeecd1ee10290ef33b22de03a5f28a91b 1ae6aa13bbd63ef99e435e94f6886ce7db34814726bef2a261c6dc31b7bd612a d23535b3145b73f005b3fd9856513400862144693aab09fed893d2205c992d0be818106e0edf655a7da614d1138ebe9ecf1bf28e75a3a579f51347f0cea37859 false -check_signature 4b0de81a59ae305ce0f7d297dd4df14032e88fbaf3629c1b243b25415b2f0a50 24a583ba75d8095219ed073fcfea8ad75d37f30c8735a10f284c49e53108fc34 d7f77b977dd9919cf00a220c12008267bec7c77fb4606a1e19b6b1470e6ecd9aff41785091da4d9052f6d92fea313ff0eefeb9c40ec4cfb3847233bd1df9f502 false -check_signature 72bfaebad3fd4ae4685dca80990fa5302a12eeb952f00ac90044cafddbf3667d 3f9369c2cd4ff820b120be672e5547ee257e7fc854e701f6189ec7102b926374 aa87ad51e780d1e378589406cf397d42cbb9b4c6ff5276aff24e36561890ae81c91783f78ccffeaef2701d9785d82f58076ee4b3be8fd65e8dafcacc86a28a07 false -check_signature f4171b718967da7d08a0d1b5d93d7f37c5b6594a26b7a0044321ab19df0c3df3 516648681eee10892498c4fee18edde7f2dbaf74daa2c74c6d9c9c3e9b71fb5f 40175122a7a227f6016328e595fc70591ca5ee71017ca907d5238513ece94909ae5a3606009668f426712ba86755e7a787c50d73df6a807194e17ff6725d0b08 true -check_signature ade5ddd17c520b3b0a04682152393d6b2ae9c2b5ae4e50de9dfd262a638904a3 1559329af03555b34784a988d461df601a02632c51223f22e64c7f023d12a91c 82f0d64b0781da72f39061e5a8d1e88f74343873404b6fa086c9497e3585350a8609ec79a3e9c186856cca63fef92129e31ca2fb37dd1e7c5686f4c58083a303 false -check_signature 0461e5982eacd439aba9735d20c62b7247d5b4c2b86050cdab7bbe4b534f4308 44eb6e57c319f5a56062c9716e9ff3a3f2ceed3f75bd355b117df75577a7c3f0 0c1c7b90e6cf176a32ef32ea4c1b868b02f4c5149297646250b637d6f3550e035c0f35265e36cf18503f93623bc1b0c35490a33c3e1184745791d6cea595180a true -check_signature a093876e55a8980f89a3b8e5911aa386bf9377bbd7a8a8084a03009073cde0bb 9cf09163055bb4e6b90460bda638f9b7dce64c31b5308701944b277cf98e7539 1d09975c9c9e478d5d920e544a605386574460f1de5fa4cda226efe0ed371ba086bbd9b7b2d76b8e0e76830ecafb787f81be4e179c2a60c7c04955fcfc4f1b82 false -check_signature 5646548d108b090d8602ec9b8eb9817964b6034a8d950f41f44bb99201aaead1 c69bc064dc758f9d23a3d6babccd2bc83dacae71041d370d3b43e2bfb11d2f66 bd52cee9b6735d6800d4faa2422feecf4af221ddf0b1ed5fb2f5210c3f03010b3a9993476fca2305a4bce0ce5fe66aa99e08cdaf7d6d2a0cd76c270d7cdcd907 false -check_signature 3b2d71cc4737f057be2ac75d60c9e618ccc57c834cea1335b80f56cdeb5d1fc9 d6eba224004e75c51ac6a31ab59118345f2410a1720422effb4d0d35766aedea d060a193f81f4fd70f4e171df463cbbd2fe739b90bbb73fba0ab01603929250d1564ad568136364b3f592c3ca8d8098fdc296155559dcfa10f323845b588770e true -check_signature cdd47290b848918463943d8382a7d44e9bd0fec69c5b78d9430f82c1a9f245b6 6fed40c669a76beffd9910793b661dfac5f4ff1852aae0193770ae62ab04929b 8e8917a79c518ce536d1b744c7f43fdb78a62d102da12e4f599c5f9f22defedc186327f60a0ba4c9317e29932b6f5bf1fbfe5cff4c1e76cb345d5067eb5f7ce2 false -check_signature 70405691c4cecc2926aea53aa1fbc370a46a4965f91ca66f7349aea4cea8070c 417c3b2b9c93f909b7bb16210ad2bd0cffe8600f87bcfd244163ada118bc8cd4 66c6711e1450877073c16a47b5060e76970091c4bd9c190807ffdb810e6afc0952b2db6a229efd69394f7c82912b379a09fa40ad867b6eabc848245317f8ca32 false -check_signature 27a1367a782a0655230028e88b0680234e135a235cbb1503a7d89a7138ea2d30 db592a54f2c9e5936274cac3294f11900e188531a56252083fa1685ceeba06ed 30d337fa46ef71e7c8e5b940707da0623911e275f5f1a8d058551a66deb94179303787d71b0ed40e36ccb2d8c05878c5bf435dfa885e7f20528d91b916926090 false -check_signature 7e0e7c5665d262d211a158da6eb852ee9621b0d5f0bfeed3b7e57eec37a8d14a 7f6678a789e72b8dcde9c2119564039fd75ce97b5e4e794e68c926c89394475f 63114a4f498b1e0605078f407a1270ea74dd3a31d6cd2ac4de854c88919b3508394fa83053dba2f4082a98e01791f4ceaa57e4c898b1f0c61f0cbaad9b313df4 false -check_signature 70cdd25054d77f6588ae413ccf4de5cae6e6b80bdc94e093df65eedb01f7724c 7d1127d9e37d6b3c8f38c43a0aaa245a9822e8e5c6b75f0e576628f06a66403b 9b1688ebecf161dd0e15f04f08e2116841c5578ade7bd994147fb70344bf0a0e3d6117942c9c1eba709ec55d57da321b68a3c704b1eb0c173aef512f51ab3f05 false -check_signature 99ecd33b20f17581763bd84588ebbad59992a69bf441dce1f3b434c68799d782 5293784a98ac037126d4060117b93f6130f91bf2fcd6a493e2369d3f98ac7bbc 64c857ff77599b516958cd3c02815620056c531b52895353329191bdd4ed634a6ee3ae634cd9d12560c3a83ec56f2c70aa95fb4cc9e02d2decf3fe02338d9f8a false -check_signature 57a7ba51dcc9929b01af66ab4d3fbb039036f547e1c82dac2ab8d2694df986d8 6d39b983b89af2b9cdf9c7605fef84b34565bdadce91e16ceb849a9cc34135dc 201d5c290773e8214c2fd290a5b160313b00545c1a7cfdeb4bdb1f366efce0053d28ea621d65221d7df07c732a02f2051c9a489f878f74cc5853072f818b8404 false -check_signature 2e6df0305579ef8c7aba10d6bf62d51c6b73329f5bb572aad6adfdc76ae0ed03 30793574c6e05800a0ffed391d6d102a227fb5ee7d5c05a509d51a2e42460438 ff4998a65e67becd55ca888fda3ea58c438f9939cdd386ccb53eb22c78fa88bef7b49b697901f8e4fa96dcf71ca4406912761dcebec9814018f08b2b73097d01 false -check_signature a3bfadffb340d7097faa0d602fa2d4271f082c2e47276507108c0059ad2bffb7 ad388335fd782ead4b2ad4e0eb56646d683a5a567dd1d384d75029cca15b3afe e11792c8817b7ef5f63896d15e077908f4291b86efcde08a877f6a1be876dd0479b83c3a6f01cc8ca08be7a5d6b20d954eb79bc7c596e5e5b0aadebfee827e01 false -check_signature bbe4698a8db8193c22a2d8723e20b9bf7111995a48dd7010a1e287cca77a3270 43145028613289deb10857f2e03ebd70bf9853e40e59234c491e049f7fef9291 fe739c4cd0c583e8ca2f638ef720b2b5623870d366ac8589c001cdd535c6cc08bc27a5995ab3d2a49ef0d3234a3768fe6ce4f8c54cfce638fa1adb11d343f90e false -check_signature d59f70c447f5e7bb183d800c3eff535721cfff0a3ffa327594d573b8e2b47def 7cf807de0db62a6e73f1ce6934b1bc55f40f9232cc58dc98d96461e84b55686e a68982c46410f69a8a8eaf79ba545ca3bd0bb8222de9bc7375ded46ce9be950365ba148356f2bd0290632b0690e9c8af4a3f351620e26239b9c8124d5e7ce502 true -check_signature 7487d873f23f6a3302b1f2aa537fdc57868df6acf713f469ed3e6a2e79b443b3 09c12b4d8dd1b938ad6473d069544d196612677e134409ef4d22726906536970 4c1eba3c0efad0b904d5b454a9c590563ab7581a569908af23dea1569fca880137cbb4e5b60a002af900cb2489f3c31844b90b91ab29e2957b584fd2ba7bcc08 false -check_signature d95e3dad0ee69201eedf4438c539e70a7e4fe22f93405b03af757ea4610dfbf6 67453c0c767cf842e48e0feadcff39aa5903177576afc03f2a169dc2bdf2147e db0fae5169039107fbbbfa04c1af0344f5e20f428cbf26e293c3648032034808cffafe8023d7895fde373aab63d085f1dfc444c261e503ef6ff8d89a5669e00f true -check_signature a66f08e2f79f9dae8412c12983b473dd6932a12159404c6a12bd6e87111ca369 1596be99811ea82807280f19fe45fcbb44233d86a1645b207a596b4d9850a047 242242946ec87506a7287efd70db1c8176bf5211d975bb48c0b67ecf588dd1064aa6cb7927d21f2914a6538aa65b8818ea426b979e9a6a038ccfa1651f05470b false -check_signature 1829276530f67d1bef72d3f40a8050307918b805284ccfc2c41f33bba37cf8d1 480702b1a59f5ba280fcd947ff0ae71400b47be80c4457ffb9871eaee914f6fa 55f6a6ea9b4615d995a4a06c186f29dedf8de0292984fcc5b75bb76e541c1407147982cfd057ea759493df9c38a9a0df9a0a14a03482821105f0d82369e5970c false -check_signature 11b82e7398469cabf6d6192a45232d2db602df65af8b4db3fd98a7b663c08628 21f4d729dc1ac5f2ab8d51d2a8c58c5cd395295bfa46a0df40ebcdc3bad556f9 b0326b27e31e604c22dcef65722453ddd0d5a09a063b25d9d769a11400df360c23bdeab50ce3540348c5a364b07d9856861633919378819c19e4766193105906 false -check_signature cd3635603beb581acff8bbf4dceef1605f1948739688b5db5a88919e7abaf01a 00f86f022d5ed991d9302783cb03f73e6ac908ea02e40cd6f18821fb4749495e f93b89bdf313d32bdd6ea6c7ea904f10916d584d0a6944ef55a8f3bfc430a404d435c4fafc65b3a5925923b12e8f745b403a818b4087c17e9c031cf3752e330a true -check_signature b0bedd910fde04f14d764eff12b61d83c458ae4612d5e68484bb85d742252028 f894ce528f2a8ea0aac7de422578195da9632f127ad04910b398b2b55ef3b02b f9fce0a4315ebc6e16fbd8c580206c25d5c7da80411f1d3584fa4497224e67028f89250da83c9518b518ee2552df8af627b4d00b5da21c0e2f737f580cb30308 true -check_signature e2d21de3fca7458721bc5659ac39177dc06cbbe7dd2f363c9149927fdb6b234c 9de78d5c7d3c5ae0d7e49c5f1c3d6c9bfaaaea82e4c3f1ede84b6b679a59ae72 6fee1d640568d0a8841f6dbcbf44facb3cef95c13565c31c46f150dfad73930eb000ed29eeec297b53b5d9ca445a0c735070ff706f2341dbbbbcebcef67e2d0f false -check_signature 0081ce3e98a86a393b0a5479fc965bfc3bd3d5bfc9d1f3a8967fd5e3515c68b0 f787aadbb51e924c5efc434ee71d7a294f9b5ec57be238a813ed60c475830153 b0c2c30149f79641c67b419219e310af0c274d034c5af28de46d4bd64180a309070f16938d5dc87b04143d492ed69bad4e265e073bdf321be7fb345f7f32090e false -check_signature 46a4e147069ce1d19c343bee22908eb5c4f493a37475acda3f26b31ec870de13 780d47c0dd019d6456734a9746504fbd53f2952a8425f88e47e0af47cddaf858 5f8d629478c6abd6a4b455024035c32ba3a83e5465247c51725acb92c24120028dc64fb3ae1427bc4e3473097b31bca78de17bae99f0e8aeb382d56a70a30da1 false -check_signature 0f46c33aee7a6016774e889be3074574c3c76200ec2dfe875ed3c77b3dbffcaa b8a654bdcaed016b9bf03ba7a667dd6bddaaddd2307590d8e5d95e1732ed9acb 1f35febff82c0310d28d119ce938c54f2fb848029d58d0268c3e531cb952440a757af6ed9ecc153a641775a052c77df958991efa7049ce25972bc2b274bf3b0f true -check_signature 6dc0c591ff0fa7b8bf6895d1869889a765260175546d753ad8db8d9ef0a1416a 70e14d7c8f244b26d574c7dd9ed3b30a752c2f7f81c5e4a87d8823b5391bc138 b03cd5be6e5899abe944e606d1c3fee7ec38286e65eb1f676427cef1f239a20d2989cbc7ff474967c16166a8c9490fda1a64032fccee2441670bb74f53e1a30e true -check_signature 2fa904d20dabacc79bac42763cc0277bf1241cab17cfb51e64a87fd757fa2391 7de903a2a71be2bfad06acfaf70c0d92011d0b58453fec29a34959b78976f1aa 6ab16e0658f73c495a3473c107329b81144fe93148bb419444bc7c0a6fb16e9d87984fbfc256591ff7007ecfb34933eb1d53750a23ea58d0f2acba63a2be6206 false -check_signature 6625012adb1e2c3430c116c494af5eaa45c88c4fd53b483cbcbe2866cd8b7f93 7558f467116039f7eb82b58f854941e1002546616325dcc5b403b14334c762c8 535737ab91bbc7cbe7980335f5f90cf577a8335b7dd9307a82e6d865a9fcf107501e33a134f511bacd111684f919baf3a00391f009f912e9d4c63401ff5b4510 false -check_signature 3f84a0d17cd68117b394389f515afb4fa178d60bf67653ae967c454c145304bf abfa2ae3048b5cb3351d5693fd815b0578a4856becbf5a95183dd278ced4908a 8c598eae838da4153167927b4e82dfdbe68ea9a1b536d35b47c259a13f811e195cbb4fad882da50738e97d1c7dce7e0648ca751abd8f61949b3604498520f805 false -check_signature fbd5dc2bb70c3e66475b30b8b1083f771b588be0526cb80f62bb066d8e62eeed 9cde8a890bf861774178b4e2f19e86b9d1f73d37b47f92060416d170d89a9e36 c4922504cc61b9f8e4ba8f6b1169c8d4613c5f22269ed2819de270cafdfa2704ca3e404349e1a83ea58a8d9612aa46be25bb486c667bfb50236b9c36cf91f30d false -check_signature ec25995b1c4ced84bba99e0fb49ebcecb08a003e2fbaf573e012733b049e00e8 987ef0ad9dd79051a10d84265d723dff1b84727c2e6190e523aac84f8b32cc74 24eb02d61cd15bb9073d9f3222bb6f0b5882e9126163d3f4fc42ff0372538503f586c7358127d7906660084a6c82b8ac75ad78b9fe110e1a12b64f71bcb2a301 true -check_signature 00ab3fe2c10745005ff367718651ae77d121d986d84fa44f8a701d25e7fd4855 ca7f36fa68c42ed0726149ad03cfacf8f39a7ee4886b0fbe898cd3012dbc4a7b a970e5ab34e6d10885f6f5cbb0cc18016cdfa4affbc2f9c0f68ca1b3f42cbc0bf328ba238a41b7607bf817c6a0b109e121c3ede258cb734bf523c49a888b0e0f false -check_signature e3b5ef651e536268bc3f5ad473403747937bc5bd07301c72979ebd01e87b452a e533d8f7823d890ab11003d8394df99642528b86dbb3ffdb62a9d3c62c06f7b3 7b2da3b9ef8b66277ef1e04c7647f125efba511c349956105d91c3222e8d3b0d4e271672e9f4bd97ff482715ac613b330450057d8246507b9d91badb5f19cc06 true -check_signature 7e8f1e1063feb0ec2a111a3f39e92d059705c2d98f0771eff5f8eb1f57521cbd b384e8cdfd794266ad52c2ae53f6021b636cfc1082e115437ac7655a0d8c40de b568da5a7629a38a00fa0890db7b7259724acd45ddf024a2125c48bb2abe4b03b22853583a5471a713d5d213681cb135f8137d59810d286561e2b45ae6cb2b0f true -check_signature d837fce281d75a8cc8a1ecfb1153beaef3e13c2c11c7f489a2c6d50469a7935f 95cc605ff63c4bee5a11395316f93ff393e557c79c474b4d1353716a63872880 2860f6b8a3a8fb24b99ec5ced6e25559e71656ddeb1c99ac13ebe3ad68604b0f23a1e361eb9b197c4376f2a0b3a8168a8fda17dbc144995e97549fa958c6ec06 false -check_signature 5f4f109a83a720cf7bde34542e0a290c32755cfd2b1da09cf7e1bb102ba8fc4d 2c7a810c61324ba6beaaad26a1c7f1931bb70381522db1e518f18c3a0bda434f 78ffda6ecc9efee049bfb602eed0fa85776d10ff689235cb3e3daadfb7ac9e0081fde45e0a54fb0e84ada828d3d1d2027f95623ed34a1d96bc956d635e7de00f true -check_signature efa066c7096d9e48dcb5c637983e0d1f19b7b3ed4fe3b3bcfab75f8aa18c88a3 4b040e45df4f86466ec972a1c56511fbf042139211150e490f9c5d549366967a 924be40bc32867e19d76411916af818a27e365ba74ca5de54d543eb82ae1880cf164eec4d4cd1199189e71713c49a01ad9cae2ab589ea4986bb61ad084e1350b true -check_signature 49ed62bffe9ae0a9563734ee6276f494d90b020966d17a5a2fd8b3d67ab1460c 027f34fc2f00f14473eb65fa374373c5b6b16af4abe0ac75242ff83146e7561d 79905d0f14aff95eddfb5a4025b3ffd7265a80febf26e8ed0e07045732746953828a52f492ef846544c8725048d1dd1616652d7e292492db7bf3855a3981540f false -check_signature bd9f4b011a38f06a1e42c2b8d32cf1b27f9432dbf9ab9acce304bcd2fd0ee535 fc385bcbfa89a4d01a2b50f0062561e1c10f3dad9fd5a5ebc648313b9ccbcbef 2943e5333e35ff3357597b1cb8f74b0c895cee1e9dadc7db2a992a769942dedbd5c2017607c8ec330cc5f05d9681f15eab48c8eb6c7a62add8280258ebf77702 false -check_signature 7d747782c73000b7a9f1be24bbd7346682f5675a9e481765e8df325ba3a8dfcd 69b1253ac313efb11306c741e9dd5b6cd261f859424f66d36caeb882710a0469 8a98636b11734303ac8e3797b422fb70a33489d8bf82629d0e309cc97a007cd1e59ccded13be8570ed4f77e779c59e37b8ed8852092b3a157bfe0886085d3f8b false -check_signature 0b0794ce3c3e9703f7595f13db306cbe1edbfe4eb6618ccedc00266ad1219eef 4a4ebc66ff40ba0249eda997f8040ca381b4ea90a3d61c4c3fb708c754449193 3cffc2ee8b62a81aed4e9e6e33236108133aa4a7a0f819277e917e44a7dae508480a28c914bc14795f81a9fca09540e925eb3cf92d99f022680f5b7ba4b9d306 false -check_signature beb774e1d379889945d8a1270458f726751d73c425221de43fa15c4c443532ea b8531c82da0658d698c44062a372a75e4fe791445df17240912ccfc9dcaa1820 4a050cb411118b91acd5cbce3d9853c02570e7864b8d8d41111db16ebb9bb7069d384b5410758fe13cf4a50b37991f2a51bedd87b7070d4bd47d8051f9bd8e08 false -check_signature 8baf11bbb8ab85675f46c9acf8cd77549464820f37206e4bcc2d6cacd75c865b 3620148b9873c15ed280d95e06af19258ed1daaac838da0b265dc842860c1eec e139c2b24a97870cf61d963e9d6d970bd0f33b40509332641ac2cd9375ba37092b7b84bf5e15e839a7cb309c7bb988299b5e4eedd61569bf45394f406eda2468 false -check_signature dede1dedc2b1c4588b19a9e43dcf49ad0b0005c44be01bd3ac8356af6024060e c693cf60d621195835623248acb9e467e39ef3866fd91dc053b7f7b07435fccf ec359fa9ce1c540b9d0daf88a3d72ac70e848a96eb55894b7a9f25d29d096b0cc0c5c4cfe8d75bc9be1e1d1f2f4bbd3e8440f59c8328a0cc156d5ab75e4a1c0b false -check_signature 7ae5328e7177e12fa0520bd53afa66158bad21bc90734079cdda08324b2b9d2a 5dff07ce5d3c22263e14f2ee23995aaab6cfab16586cc31b87b20159d90c9e3e f4a2ade50b9c449d8b007fe4b71fb14ac76f50b6f464e507e8f12d45398bd19dc552ed32ed9518dbd95149c92fcc6798470ba0c4c58321f38e725aa434c34601 false -check_signature 2b6dffc25f4d4e55fc7499c1f5e6fb4a998456a3734bf05bef320913c9c4d5fd fd73522dc36a2f1805e5fa2b7a3c9b34d1bfee416a9a6df09ed25002aa281aa9 ba6d1d95563e409d18b1cb8e11407957c939684f630a942b26ec05e2673aa90629d71200f67d6f3cca45eb524ea44320452051acdb2a64eee841fa1e0d247f08 false -check_signature 94bc969b03986d4fe94994101d55db4fbc82bac4849aeec4c9568bebe2e70de6 62e4355ef9c3a74b41a73201066ecd2617c53d3f8e6aca90dd2e29e7efb1dbeb b9e242d4c4061c8cd0dac913c82355d38019252a27a5b4dd730c867d8b754c01d91bf09782e78b33a019e16871b13f20884a5cac14058eb250a95489fde1ea4e false -check_signature 8c535cbb1da67dcd487a164f8c4750e34c779145359c027c6e3e0133a0093be4 2fe1330f2679a6c08ecc054c923b87e979b2cb7173d4b917b3564a3490d9d9a2 8336e1a63de604408a9df8587d78300363d3870f72c707156ed37460e79cb33a8e71ed08b3ab92cffd4eeb882cca0928625263421f8e22e27df743c2cdbe1d0a false -check_signature 1f5c5cdfb6c1726adbfc9a58728d9fbfcf2bdecf83cca405632e7eb5d0875ec5 f4b9f4cd90d7df838d5bda83e5136c96c10aa6e0119fbe82319cd1ad08328223 2362d621e89acbc4ce3f70a3fe7d4443b67a387e4fa54cdaa62e0a01564397025f9e6b7699f5b793ba017babca4a3b7bd671f2404fdad6d827b658bc96a16468 false -check_signature bebf03a4b6aca0b7d9be06e9f5e4027ac5ef36c8d4567555e04b8e1e1e4fd0e1 3e5402691953e5c5ce668e0e4aa6ea7b4e601f3e4d94be3444ee408d5bee6353 44fee21dc6b1673cb274161f1b6b0da8cfa46a771628f9bb0f7a6ed9031ea1732f8bdd2745b447ddc581b78c9872f68171185adc2405aaaa92e226573755660b false -check_signature 264c127ea46509e1334e9aec150f1ab79a2ddaa343c4a99b13d130375e365061 0ce5bd82b6237a1e81ef9c6c9572e95f807e8be0cfd6506b84a82ec8ecb55a7a d3a27eb4457d60e770f8b083a354ace5792458403c48abdca134878ff6258e079e41abb4f553d3b35db472526cce2b28913d2de62ea263726c2757be34b34c02 false -check_signature 712a77f1a93bc1cb39c7663c42dcb201dd40528c5018513742b8665db83ec908 b7b9bcd5ccc8350f5dc85d294b6e0a913257955191a29371e2191dc2ae52ecc4 ec16e87d39799484af52c28c3e27b38dbe6293d587d1400ccf128217b24c830a0006aa720c9636841a6f55294347d1050eab6f8e86aa7bc9e55061afa1829b0d true -check_signature a94a54c13b523c36a6ffb32c66c1c5fd3f8c7bb172f1231064afe0f3f2782c23 4c99fa7f80c84d323ba0191cfd11e327a818acf5d9bc72f085d69388b11a9cb5 bb9f8af07a031cc64ed9e9bf1369aead1ac9392dc85355227e18ec9304b53c97da57a2b95207a31b7d8c22bf73cae5eccdb082265fca02212f6d5e12c0f04606 false -check_signature 3db5a830746d521880050306bf153d7351cb85ca3d7164ff722e8bfad2c9778b f929ef7885fb0bb4307f6fd67a5ffa3baf11352b53b41f6ea8a817dcdbe54c3b 665a4fa15f16a750431edecdc7b780d7775d7b8685b95efef243893a8a0115434dbe87d31c3bcf3eb2f5845564434c2dadc455732738c5e16163a3ed5ebc590b false -check_signature d7b73640ecbcd34fe320c0767162d73574fc297f232e2afe68dabd76b8ef1e11 45755fce2b2ea1389c8e5650cc82811bad07fe3f5423205e9b575cef7c5bb7ea 66361ea647069c3045775b548d0bdc3a3726e16fd382547c92cca2d06fc759012c45a8f374b5e53737885f4935ce48081ccaf446e03375905d3114a75ea35d09 true -check_signature f082ff955d7565f8403e47c4bcd3b567bcaf3a54a8900c15974a64d1439a9d59 c7036de49877728001af254e32421a064df87ea032f98d7000eeadf026713f79 94474ca173e66053282a86dd2ddd9911fbc55b796dceea5c41722dcd472bec170dff940e0263578f462d05be6956299112400204cba70cb9c92d664f2b6e0106 false -check_signature da05c46b92fddcb555c54ef6e9842c02568125e7d01dfab246c52e4e2decb51e d8324f167354ce23a6beffe100646d8b96f699aa967313b4d54e11951f15a8f4 519237a94a1dea76171d963036611322520b70214f29afed89dd328c2ddcae02621c60bc9d6c55bb1b641ddb8ae1702a6071879794b2bcd23febcab5b7a39514 false -check_signature 98541b80d80680a7c0d9d3ea50bdc343216fbb085060ab2d8702be52d3b5fbc4 b09a3eefc7c31cd8f3f1d65e698604f3a7a5e486077610600fe352fcb5b692ce cf90c10f5057c481c85b87399eaab80794f6536f7b2cd92bbf0a10f76af47304a38b9db0fc7cba7953d92ee576330184a727d959742358947b847637e18ae292 false -check_signature e64095ffc25f57220fc65ab2bf4d8fb34806588791d27badbd709e7527e26b7f 030d4d2503d711cc2f9b9bc796b59b256758ea5d671576bb5197efa116e602f5 410d42be91d434df4ed7983983871cdb982313be1bf2ff4a026ffae7bff45b0baa31381193b0e596a18538584adfffff9cfc65c65b174c51fa04cf6f44088807 false -check_signature 9881c71280f07bb1f4e9164ef27255416830908b889c3915c738de637353e702 cef59d346979fe2a7ac062c8ad3cb648a52ef28ec2a9cc618b554923e21a13e4 e230769fff94d8bb091015d65cf0985d0983b87c3d565c02e869ccd95bf3e60c7bb728c38c933757f19bb40d7eeaa39775211dc515319bef6e3a79d1c54e6209 true -check_signature a8460322ba08f11bbcce2c6cc1a3aafd481cd25537d45cd3975c955e510db910 42fdd00c64bf3522e0892c606b0987f8340ad81f94d204b7606229dd2acc396f 69ab592ff7a216dfacdfcfc19c680c8145c652e05e11c57f06dd88573204cd09927405249898a838bfadad96c0106166aee22dd8300e934e3796389407c9a307 false -check_signature 93fe22d8b54969bc5e2166f0316becf3526232116758409bdd1abb0ee47a6e8a 1d09ec2b3e772071cc40748cbe417a6c6d0dba06ee7cea90e02eeed8003c0ef9 663ed175edd66ef639d2d01e7b3ac90f25e0cd5399e061f06c44ff47d961760415550b3aaca77601d310a2591493f26a433ea7103914a2c24a3274371ba0e10c false -check_signature 2f2bd022969f84705319cef2744b2de342ab8092ea2598d1cb4c91a5191f2aea 5af26bc27543ecfb3a0b42f13f4e75efe675dd280ae7e41fd8cb18cfa51bcfbe d29965abcae815a8c345454939d7ae7c1dce5c5cbe0aadf8cb30fc75d2f75c77aa1fea92f71b7dd7a9310bf16e3a06a5acc96228960efc3bef2dfebed2e1fb03 false -check_signature 0f3e5055ad62241af230337045ca32d7d216b4ff0c41164459eaaa4e1dc56f08 11310ffc0aec7c4a69be5b8289e655aeb6882ce3ade0e2c5a28425ae38c90c10 efa287c8131b69223fcff0c48ca395081d07b044bbe5a13464a875ca35fffa0f3ff0b2a7ca31f5c69689e00e4df2d7c89adfd783433c70e6ba6256028f716b01 false -check_signature 8b3771c51a40a1eecbb7b0ef7dfcd7fa5196dc6cfd65cfe3d18143c856f83efa 621e200cc4bcff2e775b7f8bc63ad84e4b5ae90379fc503695666e4b249a6715 9cf6e7cbedc54e39fe83bea33884755ce600ca1fdfad7b14cd166f91beb7b90e728b10bfbc1f162b020fd2c6de6db10e84a859c554bd509702e8b7d10969ad02 true -check_signature dd569a2a2ebcf8c2ee6019c7af0da3e53397d6261985096b45c6f22443956a84 1a4746b0c9deaacead07dab91c6842d0e4ddc2888ff4bbf85b1461bb0bb2fe5c 08fedb3afe87c14903b58daeac9bdaaf3b9f120f70e1e4a1192a349c0a33890459574606922269d8548a39cc3bdd8894b80991b3296288f4d8bb44f36a73390a true -check_signature 912d78f3e41dd0496f12f471f4f3e99615401af1bd129977dc24f3756e73ec80 4284d206087e9bf07315afff46619899be7fb77bd2ac646e785e6e3778db5edc b8f5931e8a3cf128ba43b42d86308d34596c761e4110993e0974a0d75c8f4b855f03388010752663999701052054defefca178acb465d094e71f8c33147e700a false -check_signature fe7120cc232dda6e5463f3d5234b811450c2cda86ec9b205238061c406f0e6b3 c238fafbc0afc63ff116e3874289a67297aa0ee02f009eac2d9d1386e004b704 74596398c6fea849143a43443453d098c841ff29c4981ae9e4d802a610a4340e296818dadfe4354d48d4d546b65cd3ac6e91955cbebacc2d9a28bfcbb35bd173 false -check_signature bb60590c074a2a1f29b5aa9e3bc9b08204a3fb77a9dbfde7f101ca6b205f3749 409850adb44b3915b8774b7f4598623eb768f28b6d7d18afe6b40fba81aafcb0 c5f532bddbb24d9499de8088573e40996ff6f46fd43f66255130dcf3a9449070f3db609b7329006c59e1e493442a5b4e2c95e23f20abdab6f118714ecb9edb58 false -check_signature 5887cb32298c712695e0ed018d28e1503c623b1aa118515f87755dad624b43f8 5b832ce25eae63f254c8a37cb9400efe74446221ea011703f4a4e56cb00fa26e 518abdd8d20a61e29563dbc2efb5f5bfe6d426c14cc4175e6a90573571957407710e0d509f50966efd223db0e698ba4cbc8942bf6470c50d574808497ca9440a false -check_signature 0c09a5724fcca8d0f7b1a3fbf97a1ea77f7c3f6eff91a0ba0017bee9092011f2 11863062ca14a2d79b196a992f9a64fca7f440540a94709199e4a86d5e3e4d80 377ce42f63a0276e14816a276aeca2c84c780c2d80a580d4b500431e2980570105be7eb031206c2c394fdac70159e1b1457799694a94025955135e296626cf8c false -check_signature 59f14dfcc0e130b80183e0473a7851a8e8fe024ea77336a0f297accdc2c3794a a8f3872366accc29a6349cd7eb1abcadddad20f1f200ac97759b6b7372a89602 d12621652e3eb001484ce05339e1288c30aba053926c2b1a1b61b92e244fff02f0879149c776b5ec760d47a689a89b38d8908521bd3bc13e6010eb6fc18f1b05 false -check_signature c07bf8b7870651b3b9a0f824c89f54ed25d3cfce69b6792afafc87c5e4ffd732 778c965cca10b717561672bfc060967b2fe384af287d6238dbe861a79edc44dc 26c016f4cc610ce493358f638005e40d037b0a19132e32fc94cba2519402c405901c7b4fcbe7cd3eb6316cd5719bc601777f4c557c986652527499e47076d908 true -check_signature b1ec71fd60a6692a0f1baa311c2ac4ae6c139d3ed9c64b973a68d2da6c86fb8f c2719858b5464e8a504ee41e04d7eee6acffa76cbb90a8ef0bed7e4d5f35a20a d0762e61eb5f5be223727c9f09921f6ae9565f5f1a7c27cbadb155b12c62be148baab1f92711d4ed27bc50833e5412b482bab273f5c8f58235a2cbb362389f02 false -check_signature 433a8e21cb8f8b57762af362c918c130631196f9c4183b3a91ce33e9d499959d 2897cc662ee8c9e417d76d48d89f4d8d215501e66a16b0ebc87943ca78e8677b 08b2dba84365e85da84725b3ddd8d5c300d4f8a5a34616a78020a1d72c5d0f007b582199ebb8df38490124bc25f28bf241194339f667012115843bf48b135688 false -check_signature 2756f06a788bedba6006dfd04379ebc3bd0d582d9f3f09a8b51d0351689a6ae8 ad0af87cdbb4d718ebba9b4222e3198570e6c62347ad2d93b07c8f1145118996 b9bf218bf38bb4244d2d28a3dfcb337db1c62e9ef8064a9bef764fbf78600c0463b24cff39140df080d55cae39fec3bd44bc211e555dd1c55f75bd6bf7b5bd0a false -check_signature f079705ddc4e09271f0e7323bf14e536923f12cacc68a5676cad5684525b476a cdb54738adb6fe9148099e9442e6d8d13d8ac3012686305f6ad95329c8e8e0ac cc440eb7fc3ebaf02bbe0b3436a0ee67590f6411ce61e1ae9e7cc7d3a405d30123c3270ad0918a33c6a57b0c7e55f0234d1ebe6640c8fc1fb819f96232a84f09 true -check_signature 3e20a050ce64fee38ed8979c2b0f690d235ce690f103813b40a3bc1f29ceedc1 f3167ea992005396e9931985381fbefc67e3d66b0ef73b699293a35f821ec02d 59f538549df866a496fd555f9e59f8202ef993643a00dddac6f6bd5a1b88c25fdeee5d699cb45ec3f93b3038fae325c9c6a575110145c43645edf9d80670c51d false -check_signature 6a00678f3f2a650bda1796bca464567925f91bde38d1e1887a5ca7f29dbba8f2 0d7449f33f5fd47918247327550330ca692b102ec0e628550812e247088da82b f2af140135471023c98b2dcbdf9daa1b84663263c13733789f3d1355b9111c0bef4064090628de91b9e95c3029c533a25c11a91f5fa83a8e218f38b43d56d660 false -check_signature 639363f39eb2b6883ebc926217b973259215931d911bd33e86c0ac53aafb22be 10f549f06297140b5681bf73eb44c42394ff75bc2e3b49173c18e79a92726af6 be4f92482d575cb5d9ab67fa10754c70345f70827a779b80a7dd5dbcc41f620c53d624e2b9705b569a6229be96e420c4d070d06d56c01b1766bddbaa2165fa07 true -check_signature 96aed7dd2793632d0ad9a54272a35fee3c4cb67d1ebedcacee878d08dbe223ec 94539f512a6b0a40a9c174056472c6475a4ca8f27b91432c39172c4aa524167e 85b21922acb2fddfdb71da041de52998ff8ee3e1361655f6a67005368c6e9606596f64c5798cc06c3d5c4c1351161de34a0d87cf16c2bf8f5f32d39f1acdd88b false -check_signature 6a037fbc19a5147fa911dc59a15662726d1ffe7a7e146540402709660f66fec3 6688a35ce1255d48a52d8a0cb2100a9317fcc0f60487c91195982c1f4e655413 620098cdad1b7a4dff4836965288214340b5d65ec34b19d66e04f2e9ce9c2da72ff2e9108561e15345924ee91f5bc2e208ed6c8d36a001d71ead424256bb2e56 false -check_signature 4a5ed9977a6320a55f4be9a4a60c4472815d278d8df1202721d506b9be3dc2af 5499803d6648f943a79d41b356ad77b61165572c1e4f549d443928c44317fdd1 34256ba0c61511c499d183bf60b2a642165cdc79c6e442348b2b629dbd4b5f0bb2adb2302be2e6d7d16e3253d0df388e9e33a4cbeec779d1b3f73a3d8d4a690a true -check_signature e19770d985d938aa858d896128930c6ee52deef4d5f5be45c49d6fd9e2c11ac6 fb98c7bb2a3e8d4a1e13bd122ef24dc01bdd45ff4a3ad855344c1b33343d751e ca272544b301911be7adc9f6bd9ed629b9be4fdbbeef086c05c13c98b588860dc61a171451dafe56ce8d38a853eb4f0ec93eebcd7f77463c8c2531391ad81a03 false -check_signature 3ed2ed5b3b45a4d0a59969c9187b06fbfe8316ed487feac4a72f855f079fc1c5 7046789264e8d4d928cfad5205e694fe5ad0ee20df1e55b3af06d065b39a7065 1567aacfaf81d7e4c0f4ae3ff8a7ecbbc89ee1ff9e365cd1bcda3ebc09f8d39cba251ea4cbaa9065fae3b55171db7e209e3f3ad2acaf305e93bb7769c117310f false -check_signature e43572c933af5343ffb324b49a87c8ea0a94268a646004e6d9885df10983f1a0 37f9d94703817d873a0d50b19ece6bc74e0f4863896be36a5b557391703b4daf ce0356e38f8f0b446dc026e59c5771d729c3d9a5bb6cea1a4649a94c34aba601d8b91922a0f59612ada34b90500e56a850dc6282abcc8b150f4e4830c44d5f00 false -check_signature bc1a581b1164d003423d8618bd1bfaf042a25f1f18253b485832f1e469aba442 ebc4601a73117c82e756501d07e0d0be6d92e9c746b060a47af10537ffdfae91 59aeda87c1c8a9454670a8cf459f8ed69842e131d8e6ca8520c69bfe2d14b30fd69da24b5fc8dd7db7829766fe9bd6a82d0e358c50f4011c867dc08b687df56f false -check_signature 7e7ba15ddbda300b12a46aa785cc339ef67002dfb34fe52e6019d60ac459c571 9cb14f8c7a5d5dc16c7de7e133d295806a65171d9ca5a5c3b4b5542b32a2773e 7b2417ae7caf5aaf2d49c9ed017c27e9c61d634205f652e652501f3adecf7f02e9b173b19120a5920b89f34be3ef2e7231965ef5234d0117ab2e5b9ea11cf200 false -check_signature a20acea3cbe398ead45b7b7ceea687726db18c3e6c8da9b87ec76f2a3c83e3bc 20a1ed7a1bb52f457e1cc42b7eecff1c7eeca3a17e5b44ce4967143d4a9931d1 7865137321585245c4197719c055db4969fa6662a5aa567ec85f62417b60ec01545eeb1195c6d8dca4a26b1936d3573a70100c6ce18a80f44ebd9e1fc3fd3fa1 false -check_signature 7406b11847d065ee1dba08124065227ddb17b147da2dc5d4aac1ecd2e5666d95 bd54b94d54a404ca3189d40eeaaf0be4718d7a0d62445c1817ca95f3cded7171 b97082fcc9e30fcdff2a0be2886cd01d436756be1fc7c059138a0bb97d91ff0f042c608e25047519178701d4e0d3506a638125c118eae8bc589a88111419d80f true -check_signature be3d39d27f0f15af686172316b393f791ecab3aab5b60ab71a42b8ac8b33c492 d7e99fe323c46f3748222f705eff1e9a460d858c731a755b759f541d87820723 4f54620e2d2894682d4055e1c58f096574215fec1d51be889c95fa0290d1f585d045a2e42f05ad80c6074637a2f263744f21c495239a2061b6c3795bfd51541c false -check_signature 28308c178019b1b14ee667db75f8c58aa93b357610884e1d452de4b2a1e4b29f 9b6bbea8cc319424aabd8d0b4f61eeab0db02c81f462a6d04b3b39bd101de3f1 ba8dc0995177c85aecd104ba9f83334cb59635a153d8969a9115454a82f525b1cb816d28e0b71f76af4f92c1ae7e9601ad654dfafebd98a6f756d36b6d2b7305 false -check_signature 74faf838d4def6fd4311dfd5c5fd5edbdce6ff276bb753ac264919e4179f4d7b e3f145e15296d9b7ae4fd026d28ff6f529ef3fd152d1a207f079e0693789e198 bcbcdfddfec8ee641a3227a2fae14962404761d1ff40cd803108bc3fce6ec982982890615bb39f8bbb1dd085670daff7abb903cf1258e5fd62ef2be907009b0e false -check_signature 95d3f921684095f4cb05efaf18d3cd97101f589df8283a6b8dae4983cd5da19b 8b8e44026120c9b58fe7d38f45d12c6e5a30bd83e0250617c1bb71b6b20c7cde 853d2c9371d9dd117798e6358ab009496dcdabae8ee449835a3064ecc86b2c32598b0c41670b101ed8db7ccdfee5f3fbe0abb7a8537d5db7e425e8d53292cf0c false -check_signature 8ad45ceb3c0b908dd7022ab95df9f8da2611cec5e45cda45885b3e8afe4496c3 f5a49fc28445bed8eb1653e8e32bfd7fcc1d3d72c658d03e595b9580eb22314c 0fd5f0fed3c0f005c2b5f2b26ad6338e2c98877c63578bc2adf06e7933312f074c4edd41ddee4f8a3fa485906ae0c696d29506b7f781cea26ceef76149013e09 true -check_signature 4eeef937cd12e45f720c025981ec5445e4932cc8a8d6dc29e4244cae4b6a1054 1a59fe64b9b48c8d801a8d7daac8b43272019f1481d0e80069ebc873a822f8e0 ba3daacc80beff4da1779560cc70b6528dca714f6101ddadfdef16b2d70d9f05a397747fad2fac978b728a9fdf741ca3aa91255e15c2c39321776032171a000e false -check_signature 0523eb1843ea83dd419088cd0f9f7fbb7f84582117faad32894692c545a5d9cd e6c39c733e49cabc970fcb00cb98e172d5f40c8c55c7b22a70b640daadcfc30d f6c34668fd657b34c33635514cb9b0bd520866be8a31b89d83f316a18f47fcf3d9888f28b58b99eb6d5c22b8493241e285092f1cc8057d8b7ba21769852d99d7 false -check_signature 7a524ecbc6c385cd2fc75f0044f52e3db712419037a1b177cd3d58bdb4fbb537 4f0aac80f921741c1d25fcb07cf76a19bc2ecd62ac1f8dd4e7db68f16162e925 42a00ab4a85615cc5b6b0372d240423f29515d7eecc1a7ec7d67e6372445a194c626ee99f2b51b25c0986fec3b4caeafd27781578067c3fbd73dd005b9c86507 false -check_signature 1ced369bbaaf6801538573c0deffd89971fcce79563bcd03aa9c6a7dcfc745ff adf3e158773def49256ed68688a1e2ccc68e32f9870bcdd62577a1d56b27581c 433a1bc923e8632baef870c6d8941644abf53bfb9e08167daa1edf44df766d0a0daf428a33c2ddad7620f5ee313217c3acee8b452c9329801629eeab0f2bab07 true -check_signature bbb98eca82f8536cdfc26d14706ac5e45928603c6a7b2cdfac6d9b41147a30a2 d66ad4670027917fa181ec2e8f56ce7ce28180e3b0873098a984353b849c32b4 66f9b514efc585ef9fe6aa88dfc9f2b905f02d8077b227d3f868ab0ba5112c0e667b615d57024230da36e90752ed3309c96ef3a955fd00ccfb45b19bb3489c0f false -check_signature ac2016c0bfc27f9d43bc85af4530002c329a6e95bec30e9f782051331621e833 88240ae12a598117f6a1ddbbd3948a0207332def11b9d3746fbca973386f9ab3 c68c0bfc3b106fe4299d6122025b5cba60e56291e3f33726c65f7041068b018bea5f4ba0909c41b6f3a9124d8eea7bebd86415c545d7a4ae999fb4cf3dbf3303 false -check_signature d40f448fc22db6c2615edab6289f83cc460867630971663cc914ec053f996204 888206fb94f8265b309376dbaa319c43c0c15cc6724c31a28daec7e090d899b3 2d2f6846f5b57d73734114082c1563a713499398ed0bb73feca5cb751c6ee97c98d2899a742101e44c7e0b4d1a087b55c5fcc600075ea5dd4d069b534846da0b false -check_signature 0844348a6ef1432c66bc1cbe17e704f0731df64fc1b6b9a18c70d66a50aacf34 7cb82aaa315e6c2ccef05857b3ac1b247c51fc9ba5912168a43b014977e6308d 84deab32195503c9437d6d8a14c3c6a4f827be2da0b48c20c1d3c305c491bf0e55d3c32b3310b8f1b6532fd6070870188c269c0bee466f8703214b3a7c29a108 true -check_signature c1a85051f34d30fa95e69da6be3d7ad6b16a1827aebd4947e2c86200e7f3f676 7d351cef3ff3dc6e74bf50cb27bb024fc9e8597077646148e571fa94e5c0717a c1f70a06d2fa41a29addc7cf2caef8b5be767665e5b2aa430028ea89751b7825d3c0abaa6ba2dc20782110cce11e1c11ef69485473b2a970c1484997c6884f0d false -check_signature 0021bddd9143ad3c2c56153067d546a16bf66805232d90967eca6ccf5d674046 51b3e601b8b7c50630c62afb30957fb8bc9166e41f520bc3122cc17b6fbac027 61e60a37897756554f1a045a70224c56e46a6c93bbc3e7c55ca9dc9931362305671ef02a5f9da85c7a0eefbc375aed0d87b8ff6c368f8c32f6698d6da2fba00e false -check_signature 7d3ca1d2f3084fa214f6408213b3db5aa80cfb9ae29e15477954d14286c94ae5 240260111fa149e10c83c9a26245a55967bf3f73b3aceb646fed504340eac25f 8fa6134c0d5a08bdbb696a312f3a90ce8daf1f21a07a7c43adfaaacf6288870ce67b51d210909025182d1e835a5961c7450a20eac847471a60ce5b6a56092701 true -check_signature 510b9ca705c991e49ece419ceeb1352351f3afe603cf069d64c5d1352ef9e6f3 dca77388a384eee03da84c393f3d3efa56452e7d43a143a33073f288bf31b79e 13d9eee35d231bea513b65f375d8154ff24519613b2bbc3049dc1c4fd7d59e0a2950823f9c87c52c5d97f134bb13b6bf698c960af76cf1b3c1afd167cc86e400 true -check_signature 9134660972dc2b9f0654f583f68c9ca537b483d6a3f66bc14e7564caf9912e91 36779c04c987749895bf28dc54b648430e4f731ea05d355325355b164ae114fd a305964b6d3d74f2982eac189e9c098e3f9f24e2fef6f2d4a8192491312ac4813298c2f92ee9932414f56f1b312bb9a8e3095c3e9355c32ad8d30abfe48c550a false -check_signature 87f9cd64a66c0f7583a923a69f5a29296c53b6d6a120a521775372e3c43bf2e8 77418b2de805a507e5fd4a6b92930b62a87c93b4410981da3840096acde4bdd0 1dbe47acbd9c7b2d39cf97d0f55931b5645320f2af81530716c84f9e79f40406ada5ba1a7333ab2e944819aab3c4f583b327e8ed6edbf0c4d0c62013ff231a0f true -check_signature 7caf9dfd123d3b7f35815733b8ad9412cb857d3344de307be5292246d5a0a83e 70997ca3b57fc2b9dfe2e03728b1a36bf82012f16870aa9ce7e8941aa0321238 7acffaefa96b5a11e199ebe7563953f669f71f75b52ba90fd8f630823ed01b039c8c2e1e44bdfa3950b50408ae56ef140ee17846e9554d5521962d0e84ebc103 true -check_signature 61ecd41232521b96f8608d1f79cd2f3717261d545679e109ba3329a6065b76f9 2d0b4d92e33e3376a917ad3e0fcb6afcd72bf913eecbf8188b5c40eae7a6c434 c283a32062ad92361d1a1d9ae0b0af7ba4b4ef14d5f58e507041925a4bad2c02782dec4765c8f63cec36eb54876928ede86646a0ff642c548bccc9d437be7308 true -check_signature 4113eeadfcae3ed76e5bd0745a6f1cd21e41a6b67af4076ac091accc61b868c8 bc18187850dfe341bb1a1bb57c2c5e15bfd69cb9d86ccf45e0ebf00ee055c6a8 d8d8e751cb116f51b8409bbd98a1701f30ad93ed72b25d855f3cf4d807741005c18335322fc0dffc1210c7a1b2abfd848dd9ec5176d6be6ad8cfa0b1b8d2a60d true -check_signature 9f97ee8124f7c1f9d7af26bd2a4efe0d88def5181e4e7d3b611b8666a08b4812 b169761070e57b71f0730282741107d1378d63707b4f397e1e145f6114c9c94d 76b7f7bfe52464c2e8be682e023f15ecb48c143f7253e8e1f9574be5085d3571f0a2379da778765da56719c163906c4b69efcecafa89816e4237ceb202cd1e01 false -check_signature 719123201745249e69c9f8e13c5718baada1ddcac892c4556f09bcb960659a85 4ff9f5c18f8d78d37c08fab9189720b7f23f6f4c54f470abaf358ff5b29b8212 a0c895db6a8566d5d6cf8f1d8f05b3aae278640f0b2640f034755679e8b77b09214736213fbc21f3433535f7686051522f3b6f4462471d5f6d05924370d57089 false -check_signature 6787e5801dfc90f5e55c9902d44d41608a5a3c12216e138997015da84d95f18a b982af583f4d730dc8679e5b1e3029b0ba030c2b592b731429116f1a0c639d62 0e2dbef41b760610b4a92ac3971c08afad4751103c63691881233baf0df6ce06bcc26df38ff0391a4ae6efa13dd6e256dfc6c796d867ed103a7e77dcda328404 true -check_signature 53b8e357051a9d0316fc7abcf5e599a0ad08420621bb597bc74a5fb02699ca50 772b61f9f0bcea161629583547b5e834c056f03a32318abc54240c2c763f6ab9 4fe6ed63dbe9a1476f31a3ccc345cd3286074c733f20c31462db29dcde75fe0e0af23addd19271458a650fb5f23c78d2bf81406d26796a64d603866ff8ddad07 false -check_signature c5f216bbe95c0fecf308350aaed530fdcd1672c04dd13fc2e774a5600bbf8f46 f8d94a71fcec722075e44d5af946c4630f216cd511d7585ad4d6346d91704746 a566aee3df443d00e5eb3514a3a346066ee02e8e6ec2ff87ab3cc8a14d7a909ffd586c142c5640704ba5dd9736fc9a955404d2f52236dcb81f5b052e089e2b53 false -check_signature 1ff0c83fd5989e1290076b8ae5d70d286ec2579b64f9a8e844b5e371a3509ed0 07bd56206c84666d3657d06685efdc2ad10573276a7665d24accd6b5abb80ce0 92f98ac7070ab9a4612ad3d4cc40dd0790d14a11cf349c9f56099f3d2e307c0f025503352b31c19695d391c448c4c87f4909aed4edac8708f21820d1cd1d0004 true -check_signature 5b2319a6bf1bbe6db7a643386f1da892778b7b83316e9a7752c81f82e619e771 436f5e10e2691488c966fdfbe8202ebe9d06e4862b9ca5da439cdc4f3abbddf4 3caf46141b04b4fd45b552971671edbb97a852d70ab4035f450604260196d80290270ad8792589393b99134c4e110925dabae3717e1add06ebe57468a069c50c true -check_signature 2aecd39a0e063f55d37917446369a253b5b935df269685030b8c65f790a9b986 74c1560dde2eed037a911439a56c9d82f5120fea8edcaed129f481f7a5d6071f 405b7cf09c8032f43f5c61e9c56d67371ba9988d991941a6bbad81448f2b394714a24acc6f8d1e0e8da23943ce29186593897387b671c2efd1c3d92ba1dd6bfb false -check_signature b92e994f80cd4994d2b717ff3608e6203501fffc2d6b89f41aa211db0d9945c0 51039f70b3d8127664f0bd069a3c6fdd80c96aa72431cf949b4b51f9bee648b5 292c9c15102eaae0bc20652bd703501783b42e385e8850a2e874303844045f09cf7d4f220ef49d05e38e3fae7ef83f41e32d62ef9ccc92323efcc2acdf9d0d0e false -check_signature d3d78fea6a242411d53171569f7346cf6ed8ee6460728933b87f21a2686b69fe 173424db7b62b7ceee310204b52d06a7431947504ff3804d4b7179c849c0a0f1 745eae4cdf34159aa3b2dcd28cd4975f326507f1ab92a7dd2983062e0002c40e64192a95e1eee6a861dca29ab8beb81aa081b4bc78b6a9fab1d6421e813a0804 false -check_signature aca1e90f0c64c9a87c8cac4ac1d19adcbd8786a6dc6f97e62cf99b5548c67393 c2045a337a2754c6020973c8d6528a57b813c3f81d9e019b6dfb18276ad2aa16 2575e061c1b37824a135c08705ed0046a8fcdd6e0baba3716d880f8992fdcc02f9553b201fc6e076c4a52ad934607dc4cf4846bb6562e2fc6c26c6771869e005 false -check_signature 20f9393d2eb3300995dda94bd17e626dee5d635d76f8da7964ad197dffeaf716 ea194e8c4364a7afc315ff8360b1b018cb05b05282cd89062111351ed28d90f6 f68e4247738f54699909ffba9be221015622e2c46c2f07f8deebe6c5549ea903b40ac1a7169e391a5598fbc9c61d962346248fcc4dac29c61fa8554970000705 false -check_signature 516090c021cd1507b6cdb91063125c8979a64c0d38ddd523709b19f6c1addba4 ff1838058698f74f0538815f9fc9ae190a0e0dc713e07d8a34e7a6b3736bfe0c 692ce34dc3bdd89fcaf4a0ddef55ce70307689ca66583b212b8eecf4fa38f900cbeb93aac6c636827a675c696386013adf1782465603964d65b0a6c2d6b0cfa9 false -check_signature 0bceffb5158e6ecb3c638929848c8174ff0642e0a1c260092ead2bd4fbb6202c 3f65527b7eaedc30c477b28a60b588083a2a010e05b1e02d61a40fd330b3b01b 5207b79cd2c8c2f8c14205c3fe3f0477bc1e46d4deea9e85eaeb9e6fd7fbbad0651173e5b3994caaecc8973825861a3c49920ce52d56a17d58553cbc8d9cf504 false -check_signature fc30ebbf82486e11c6fcd115b160b486d28386509534445485b58673616a5008 ffe115b07cd40d38d5381d88ccbefb475fc23d58816d4fdc8d9c6b436173d3b1 6dadcfb4ec53b630997c1e4e7a752b890fc4ae465677e919fe071f22128877023300617e75b5f60f18bb5c763e052619e088d28e3d84db3f2057e2ecd016d4b9 false -check_signature 0573e326dadf1e97a6f05c8c9dd2e13da3b3c9eac88a753edee29607e616a998 10b4bafb59416a754fa9b37d397897ab743109e0c3aeea942cfc01765e9bae08 b517727234fa41f442aceec15dd16813f03c36330358c625e475d5e1be80ac753221f2e25058ea1b8a7d2dad37c841a50c31fb0c15411abc1b8589f08941b409 false -check_signature 4a904a3417de52da1accf8cbe1c25945002e5e021f04eb9d156cb601ec850769 0e59c53365fecc338b319b42daad217f9c2771a985583cb44661e444906a36a2 46a50712b1e072cf125fda7a0faafafd1b573f797aa6b3e1db5bd9dd04df1301dadee712295dc46d076d9fdfd075b6dde8238f7b7925930030bb25f60bed6c06 true -check_signature 018b085ef6c660c2480ae8a9d4704714e327bb1bd4f0f90adc14d8ac7a55d6d4 9414d86e0a1045775f37df5f479996ada444b4fd1b77dfc55e62cf98bfa1a4e2 599e19a9ca9fb1a2c43f357cdffe2ecbfc2feb8e1f69a976c5e3cf94b740880bfae6d3f7c28db4fb3755a93593d7edc651ec68cf469ead07aba5ccd3aa7d2c00 true -check_signature 67118ef2e08f19c4d33016f4bf010214204fb51e09f414d3f7427fae240ccc26 62fbf65f08c8276069cb69b3fcb996963b648d64eacad5f0c3e59418d70dfa92 f053602afc18f3b0d5b417d4bf884d2fddcc9286455a953c41d4782f4b94a7057834d19970f72908de0a35798dc73730c68e3a3884985ef263367bb7ed752a0f false -check_signature 2b12bc854f97bd14a10bf8294442bd7f2e39966568843256cabbe82d25255d73 79e240ed801ed5bd2e022fe5c06514918f598665957eb8db17082162ab0afd8d c04cecce0a4995dd98039572ed4ac7c22396b5eee30c913950a8f8e03857af0d8f53f05843f5b72da42804ba0f483ac6c17fdce8956a40b93f5742132d6c4307 false -check_signature 32975f69a151fa4108aabc2e6beec4e03049ff44520b00c6d2b8ca437784fab4 ba0a4708d39f7a578423c449205e9d18ee8600e081ac4e8878efa2c1c1c22fb5 0ad080ff494929cf045bceee69556bcbe598b29aa537f95a9fdca9ec4782540202c945168bc9924b2a28d6f6f3ea24d76a591a2e9ede14c094f5f73ff662da07 false -check_signature ca62d4c926106fd83cf7f8bec37572a03ad594f1eca09df75357655824b1bd88 0737301e9163d3f1ebbda5353b4ea028801928fc5330087c103297d22419951c 0c6797afb37f5082cbe799d737f4f6f493f845585d76b0cb695a4d3bc6015975ad721fa41fbeebf10aa7fba8bfc0ebe27ee87dd26d92cc76010270d0311d6103 false -check_signature 36ea668736eb1d402314a348d43abd0d1b5fe88c06bf573c61175cd4da5e9477 db66dbe8ae8397638c679f1b40ec10885f2dfea67ed49f98dfc85516dfe0318a 342e16740a75758e52625a6398387c4d62a1987aac33349fce028166af14cfb104312f6d673e1b3e133696b0c70d579953f43350a134d797329087dab472cb05 false -check_signature 4e5573a386a9a2b2cce17462b8547ec708d0e57b5042e8ad69f3938babf9775a d792c8cafc5b202a6d2d94e96393f2c849a9b1378f7a4f789c18053cb07baf61 65f0c0584ec7a56a5c1ecb526689d9e4611d7daaed40385ca34b3805c2b2630f8f570384955ed986052bd1f44eba70b7fb5e54ca7e87cd579ce300d03c7fe102 true -check_signature fc8d823e668091a5657de32029145f5238f0c19ffe3c9c1a2da400bb437d2668 421f735cd674cf2b79e8935545e737d31b3b86854057c712d0906f84f9dca7f9 c670cdbb6abd8a0414594a55e9b11660c86a4cf6f01ab44f626ba3dda777f303cbf1d16203c08d372f012c50bb36488e6582516267533a0e2fb7b05e67213104 true -check_signature a347ddec6f8efa7120e0e44c3d587d53316736fd7a02764cbb3b10d8ec59d8fb 041914c8d6e7751275cc0ff90214a14328857a3c1ee86300e2221e17b968e733 6a4c277c4eabd27b98af3d0c33f091cac09cbeb4d372aa26ec401b177721150daf5b9c7f3bf63cad201d58c193bc3e7418f92b7c9e8f744f9f9a7a2e21858305 true -check_signature 7ca4246b253d931ac30556d22716650a5fe6e911a740cc62628373d841a84ebd 2facc0c761c7991605a07a9753b25152aa5659c58a2bc959ec4c0a87cc1347ee d87aa13897fa8c8b6fd8aa54c268f3626c333992b8a05373945822fd73530a0919602fba5d2e9d05b20d0267193a4d8aeddf4201be6dcbeb37eb09cfe32cf422 false -check_signature ac72908dcd8b31ff87655ea7627a7a09aacf926145696808b836b4f951cdf609 ed8e0fb70437aa0956c0736aa3ad017c0d43664841f4dcdd6490584652c4843a 5c5875447560d3d1bae622a1cc8e9df1907870e2d285b08ddafef37791d506e2873d51215754feef9584e905efadb61b9e0c479c39c986734ded4f6bc38d3a02 false -check_signature 2efed00675c631f7850ec6f7fec0dc5c82a88fa75e1327022f15bcaa5a6463bb a317c0afba9eaa59ba93b482115e807eea49ee909afe586bc9bce179181a6c2f 3d30bb18de461419dd828834ca3ab3abf01d5b243a4655a7b4c470ef039554c976d9dd2d341430352fbadb86e5e70746b7dff54f68c0cc1eb5b7d71e7a09a003 false -check_signature aabd4de484cc10ebb30f9c782e2c87207ee1a30c9fa4f06cdf0e1b39f536a521 200d95a2acf2220faf0725372889151e167c719b5ddb9fc8a1fdc3143158a48f 89331de995f103db60c211cd848a140507aeddbdf944fdaa61af6ad3f187ce081942b62637b7fba813771718825b0c05d4dbbe62d1628acc11673472cdd1670e true -check_signature dae573c0db0823eaec2e8656a366a798ed350b0eefe40c97ca6b879216edcd3f 855ee989b6a7f6dfc53ee0ececbf2bed0a87300d0352e027dbcb47f26972bfe7 c7573a12cfb6bdd5320208addede304034f3655ff8bf26e7ce39443a63cf2705d64cb32bb343f2fe5eac1dcce1415345c76bd5dba2ecb758c0cdd018f0669608 true -check_signature 703047959ccf6ca44bfa4b085d9398e3bcf22c9a11114f95cc3df86959962dbf 17c2924499b9c95139e5f97fb1d737f206230970a7a7878f6749c28caebe0a3f 54e1b263746d4189712b4f133110ad8d689768a4b6f93ea46647f6cabde9400cf4d4548cba89421680bf06c661da72a1ed083320cc0ffe323c673326a270500d false -check_signature e31740960594c975665951a195802c12b57c1ad1e68e8976af7bd347e8db009b 8f32d6bc976a555df48b6fa6a9c74ebc2d61bce2b8491d050e01aa30ac1963c5 647d28b262a8ec87ccf939fa7501b7e73d4444368eccbdf93e44ad7bf3901702542e55a1e88066207d4d8096c480f52c9f55cdd9be1b935adeab766b933d6a0a true -check_signature 1831e21a81fe9b64a54111b73969e0cc5684bad6e580a2b1f5543516130e04bf 4a826148f8464bfcb413e61332f780b7a6621ea3a5ab6978ad98b12ea5cfd9f3 cda193dbfafea942df343cd69a8a76d2d67c111d08e949e2dd0440594e47998502700a37c7fae108e98231b1e35d062cf3a3ccefc1fcd79f92deaca251301f0b false -check_signature 7a998dcead73bb08cf3e35c1ea25d8d1fe10e262cfb0b43b6157bb58483ce69d deb2bda07576d9dd5108d5936f88fb255499255f426cd03fec321ca8d8414429 f8b93ef5eefecae8bec85d73f0a960ef0deecbb0dca8ff475617d6f1dbf461e8c289b37f0e3ef1681f42b8f9f308ec21e7d1a709e5bd20b78e7ec67ece407f0f false -check_signature 6c2b79e65dc56b65aac8b40979929ce2ff4e11621d025b3ff998085b07718d1a 81a02f8d1b5e60bd0ab4e9c0f896ce9c6e106a71af442cb275489324e3a1fbd2 a9f4212df4c00e5aa2df986cc9fe2cc4e343bb84c70eca47c8559bbfb64ba40e9153b9ed2f59fd4362f9035c74faa8a51a5a15e0b0306fb067c15b1e4dacde0c true -check_signature 0716174c9571df2b5e8abdc4abd33cf249b2ded6478817ed5046e220634b5184 0c5d9a2efc2a8df3f85f3682a898f42be320685231478ab845ac89787a2f1447 0d7cc516d562b8d6f26acd85df32627902d168df8bd68c8237f2e5b441ef5f5dfdeebcc4114ee20bb337a074b38d2c40048a78a76786899c3cd00d76ada0bfa9 false -check_signature d9258d864d47b7e62d61f43a51906ca941d75e5fc2ea081a088fd6b58038f0dc 5acd4452e318a800a944b1a66d36485b31f883d1738919a240d753b5b5389750 f4e8a12ada40082a620e5863a133f09001a28d3f6f1e7ec30da4fecaef5f5f0d2ac68de86900910707af93d02ff2db86790e52a406a935cc752707c682e7087d false -check_signature 5b7bfeb2a11ab36820a129256fb38b892d7c9f93fe58aa56e2acdee6d307661d 136c1cb18286240e4628298c587444dc1a7073b05cce4a30fe6f52f38639fb52 bc9beee778e6b8d8f66d679e2e758fc1b0460903697c442368643fb574874f0cf465fbbab78b435cc948c44da100d24d8a378ebd05073a4d18c2d700e154488b false -check_signature 5f0e6ccfc3d42b25a2f7bbd1c980a22d93ff30f27e67954ddd36fa4f8d93600a a0522339cd67e6ec69a2cdeaede7120eb89e71a41d5e801472a99afdf4cc7f38 629467b56889f5ba36b59926f2a8f17ed9e315024fae3b66a89c25ddc958b30036ea09d5767a14947d331a5ea8bba4ef43781ec836355fb9f805def31fbbe905 true -check_signature 46e14766f33b8f5a139b1f2ddd25cd2eaeca79155f812bd24893dd3235493126 29bde47470d93fef83de4c533d0d63dea99f154a11188770e2b1682369e648c4 aa623ec054a48a5197e43a91ae2ba385f7b9197c6832fec4993954e9de23f45597a0fae456eb35faea9f6c81e2c47b44703a7bc9aa811c5afaa40a2a112c7fe7 false -check_signature aa528e584c692a1a096cd2821214fc264a362b91b807cd4da4be4d84af7e8b15 d53070a1c27c041b64bca96632a6b95a0f1e51462dc96df8eef8b25517775ee2 48933914d67a412c909410f4a4079195c627ccd9b7fb4099ef16c92a6a378e02e22b4c8586a2fe6f5559d4c275e567c3ce30fec03a7533713955c286d1a07e92 false -check_signature 31aaf4f393534312bd0516ff5c36f86209f942ac3cd11c34ba5f9bb00312bb63 eaae0888eec9227bab58e095da3a2b9ad6e6233cd053991dcf27d02581e04810 900a64437b9915c090e9f75f40b692a1d5907d998dbcb5440dd701163fcb3c08d843d6eeb26ea8037be47265352eff37282d5c820be7c22584a2fcd5fbbf460e false -check_signature 6e34b4ffb422f79d6c338e79a33e5350b5419432db7890174dc273b4f6533663 b6bff833bd56a8ad69d3cc357facb36e7be5b86654b9e4bd04660d89f84a4382 0765c7dcf0dc5f7998952d05637def4ad9e360e108d5601553551e5c0968cd094b05ec64d3241ea7d2409136ea04681ab6971cc3234d43045a04a67142a57c08 false -check_signature d46eec1d07477c9153ae0dcbd02df66aa1af9ac5e5f8c01f4ee6693b2a23cb83 b21e8af422090a68a07c852243348e2dc84a887b7b32a21b576dffa2b8407a07 c36be2466d40e67064b5539975dd23795b218ff7fdc5bb462bac29a70e2110021b2ad0dbd890f025800bafed093d9b7236dd4b1cd3bfc460f02960ac0ff6c30f true -check_signature 442fd8d7919424e2c783e0e768eaab9db91c53b6a0209023e60e829116db45ff 30c7a55d59ad174442c159068b14c61ad030ad6131e79a38597c6df369f25c8d ee9b7f92bce29cdb151eda4f5007e1a22bf2208fc89d1dae21e0b4924ab6a4e263298d7c74d586721ddb8d391ee97b93ce658113f4b4dfef434905e0356eebff false -check_signature 8445c00b42d7c748593ad8c1e165856744e9eea8e4082089b9dca2c8547f7b41 b82c10a811d3bfcf7d6b3dbb3e9eb7df476a91415264625c347c5d5bb5fe5a4c dc6fe8512f5aa05d0f77d8b94946344571fc9778c835342a8fdd305c3db62a0de7ba6fc8cd98c96db954206d2c3199adfea14f08a14a9d6be5a8657c892cf706 false -check_signature cfb52876897295009069db931172e543ebdc192993d3d54f36912799e73417a5 f97dd70ddd52c5b05576c01850081afd17f65499c5ed9adc3a313b0435449ff2 6e14e5455c1e8d7c3552e6c852b21ac6306a1fa0046e7c644134f8733359e3009382194bbeac02cecbc0aca2050640f097f0924a67dc309adfea3364d40e7b14 false -check_signature 07b95304778c1e03548fbed71b8395c7f8127327251dd66766fda9bf49aeacaa d17e4953f7e9c9be86274eb248103c0a54ffdd139add40fc0638ba651d87dda8 11c2d697c41cdfd4f9aba6c40b88483e72b4f1d5e2b9588a6c4b5942f6a4c03407ceb6a66254e67d9daad7a5f81f6bf99406e9c6a32dcf8ba58df81e96316838 false -check_signature 6e24959c30258fdad3b7a9f514f2a1ecae92030c3088c6b8759f714e85ffe445 6bc04499b62bfb06d3c3961f874b47d26aca919701b4f0108db59fe743f30071 dca27a6dc58f7212ff838ddce80f5e00705badee055914509a323100644ae40e7cc3e47366866625ccc44bf30d476584d8720317c8691be8607f0a9a236efc01 true -check_signature f39797202e133eee0a50e41d386e70bd66cb6af52c45b57542f6fc08e04c9969 6e6840e9593a5fcc448c26a497c5ccafbc7be03fc15b16452a4b10415644bcb1 e7be1872a651e36a73e3a12870d257ec8b7bf88705262bc38533c3be08fde80c910f17a07447742c12b26030b6fd7323a38cf6ad4a87caae940db8a612a92fd0 false -check_signature 92da4215c2caa7cc731c729a8a1f1a8d04c042e337aaf8e4799dcc5b67ed5ed2 2ebe6084ed33bcca9419882b9b749b01382c4608e5f3de62077885025a9046e5 5926d40d0d571f29a2d4b7f523f8a3216248e1bedd0e9d2f5e3f4ca00b631400c6b1f97a3a0b2bf101d7331898ee81dd47cbeb5363acff9eda019a80179f97c0 false -check_signature edf10c36f121a1bbb0e789ed78832cce26192e9d9ea47837ce8e4148be3e1e52 ad8c129daa918d3b990bb0c1eb95825c954c2d4d6ded5972b0875a4f3f43ad56 c95dacbfe1b1f4334a7966493ce2321d3ce5e2ef42f2494b99a69f4c350cef03b23df838a6945e3589402087f92e14178e10ecc61eaeb8b3aab5fa6b8bf9cd03 false -check_signature c9519eda2a98a0e959929542fe7f55d14566d0cfb8f977482e991abce18618dc fbb76ff0b8e25f970f4750e59b6d1ff36d5cbf8b8c334f9466433efe3ddfb850 06059c70db398e68cf2ee192d753b695e90e744953ccc70e98119c78bc23c03a93250b0c03ba84375c724305013e2db842c84749e0934febd588db840e467b04 false -check_signature eee7aa48f2d06f6db4a6e44ef82421f82873695f82443f39b530361dd850631c 46c1086c8ae73be3b8534295da535b7dab5c57c62feccc28792a0a593ad8adf5 b7b892fd5bb31a91ae26d9776ded2622489d55a5e1e68bf561243100b22392d5ea4029ba2fb2f96b65fb91e6cb374a0111ba2c4756b54ae2b00ab5f060a0f304 false -check_signature a32e30e3f7062c621e97aeb10ebee9e7bfb04b8af45e48f2391eb824bf4ad13e 5f90b0273c643370ce8e514adb804dbbd3d44ebbd0fbeec5dae34fcd739c8b42 e7128f717605c3f7a8d07ee72523fac23fab613dc92904253272a15b01598aa3795faebed7bf73636ec5a17a0fd22bda2d2bee1f0533b7c4e25dc48f2bf0df00 false -check_signature bd86885a2b1475a3266824b92599c2d8a2ceff81d83237d8f197f98be026f6f8 afd8a92ec4a6a4102c3b6dd98603565b2272b516c842028812515af8ceb349dc 86dfbcb27f5fb1f8796347336f6f0a54198053c8b859ca14de965bb2c302858a9b7367f6cec1545d11ee1f586ddd07bfb3fabfda051a6665aa6391dd5f9d668d false -check_signature a330d23ebc423abf20b2e18addfcbe9035f6bcfc1bc82e3158f0ca462780ce7b 1309ac5c8d08759e2ff88da76e904f9f657920623b0565d4ffa04fefc45cde1a 0806e345e61e87ab50c0549a9e61d3a7a470cd9c094eed4333549743a4479104da757ef71425fb5b49374c3028e43ec48861012537a8a4483844402f443fd407 true -check_signature f4fed1f787da34c0bcc968dfde865027a097037ac67d9c5b721c00fb211db10a 7a8cd988b3a0c8510175acb5e67a77a96d0e162972e7ea39a27033c6836852f1 7e12e3f564c7989b8f2ac2c2b7ab96e3dcbc3e31289ce9290747cfa1160cb0024effcde71c36e1fa06545bc46bbc8f6ee6e9a97186e39e8ae7636341eb7dbc03 false -check_signature 68c816939b24d1ad3a1d68609926ad21ba67dba9ec76fd0755713198dd331ba0 3f81db6c4202c1f284eed2ec32d978c6e7bcdaa1d8b0d5374aa6751a70477c4d 5be4c748df7b31d8a0c19fe25074d5934c58623db55f2caa1311ca0f8963290f4dc22778ee17ca235e9a27bfc798e4b99e9aeaa528691b512c29a6e79be36f68 false -check_signature 9e6ed3018b30ada3e60e52ddc9fcc2e1fb1a4b3f86ba0ffc87fc4cff1c993b14 3cda562b4fcd94dd0dc96d9a9e6668cd340a36461d4fbc7c64e1a73d0c9a8f3d 167ce086e95ef702bdbcdd1735f56a77eeac9523adb88c125979f5e48f40750692e19a38cef61d45959ea748ccc92c9d6f633710b73d1fc0cb958c6066512c0b true -check_signature db5300ed1f9662cd239ed1d6893fdde02e65e501d84d2801c87c404ede70c1e1 4b09badc8bea8c631c220bf1bfbd62073724f74351cfef7fe840e2ca328fae4d c3495a5839b26f06056e08f74db2702fa188164cfebd4210624d73cdc8dc2d390637d21b0ac59ddc9e140c95cfa43d5b7f7268b5ba3b3bac0739115da2732cda false -check_signature 9f160db33d08b02a29822aa9542db4afaaa1fb74def8f6994571ba20a17b2e71 bcdb4cd4180c2a519da7f647feca1f251985fe9c207ff56b59a77f5b08baccf5 0f149bde885ce100a45345f09cf56e56916bc086de7ffe5edadbe6e2bcf35a094fa9618b889a84252b8b07328c7fdae99ee6f422e677146ae2812ed023145e07 false -check_signature 94a2ac7eab19478c5159a14384c484e0bbb4eecb775940f6ac978166ba71f92a 6b936516032476a1c75af4a689a48d12f0c5a225d95103f6c934db78b5b3547a 8f604d23984172d29cda49301a66a6e2ed937c5b9b70ca18b60d93200c4df10900082e78272a1ed6769907620d6dd83b31a73370eafc383aa4eee139343a5003 false -check_signature 0b5525c578131ca8912dca3654e6e6e825fc94dd8d8da4f96f893500a7c33f6e 57062d0cb37b6f560829c2ae9a5ca605775e5d22a9c9d452ffd4a3f8536e7cf5 620b58665e733e85d8c7f56a25515255aa4e9dde412cce8262572c3bd8d00c0a88c077ac3a769d5429946ea45d2077f6ecd2fe1348e9d56d608494a093beab04 true -check_signature be1296671116ab5a79a000310a853882f4b03b6e5842329bf3757c3449cea112 37c297f070c2c1f2cca8642b1cbc24abc744bcd4d67aa9c13c77e7403302a400 0cde2c401598f7237300ad31b9eae6bc944d05bfa3b360fbcaa74dd768ea0d0e21fcaa7c9c0a4261323b6d7dccb7808835678a3dc74d82ab8aea59cda5e0290c true -check_signature 8c5ec044ee5320fa9ea15c5e52aab4dddd224854e65366f083747fb0a1bbb727 9fcb4041d0e6aec5bbc3be01cadb5a247a30e349d34d97a8622bbf5d0d26d950 d9fa68d9540d180588505fe73ee55ab4b822b65c302f5395855aec50963b7601d25cd997d1a0d3a657cd5769427dd97b19258eb6abccd2b5faaebfa4be2974d9 false -check_signature 976a5ccd4043e9e88b64c0eda357a5ebe5e3e512b6044c4d99dbc3799419be7a 761bf2b2370f964ad55e7e7d35d35b7d32b3d82aee4fee5ce14b79117f016d64 c789b4330e619fb999f29b1babcbce37e697d803b7c2961f6e8e8c92eba5bc06db5f66ae47c4f5c543391583f70019b33d5cac7679a8effe935be40802e25c0d true -check_signature 481aff81b2b83105da725372d389517d8b99b2aa57799ede98b2047a5ec17c2c 0de8c958a2a50daaf409ef50096e86da4eaa0deab34943fdce415b66381b4a90 40d05b877c191183b8bee3ece402665d6b8f9dcc983bf937cd93f4a99319a60ad81fd050addec13258b3fda03f6ac151b70089fced22bbceb5e630fb8d052c16 false -check_signature bdc2e9b8ce1a4c5073de355c55ea3109984894c338779cec73530c9de1cc9c2c 77c94aa21599c24029e2f817b95f7c9960d3a0727acc4885d8582dd2b4c57fed 8e21f25d6dcd44439a19b7f5d6dfc1bc115874823ace28754068593b48e506ab93548be7addbb69651e8487e96824588f41828081a5bbf11de0207c8ca319f62 false -check_signature 51748cf6fec82ff14face14bc36ad290af669b1cd8858041741cc33d8e009880 341cb549665bec4f8b71eab5d39d775ac45780689cdd1db264a36b20cb5e0f57 8e4a5623b21276ded58278a79a7ba17479d64f97b1586bb46d87529c4947c60faf4b954fc2faa41e9c32e97b85bbfb17908fbbe4b493e092c7893221fb29590a false -check_signature 8dbb10e2c198631e7016437d8f25f39d587d0df0e77ea4bb4a19165739404bc0 7a5fafe3f6237a6ef914890d38aa9cd585ff0b0ae190ba02823f3781a1f61ebb 187428caa114709beb653c7fffb7e9eb559c3bd61da346bac1209f38d584fe06fd25f1f3f76a538bea5000b565086b8b12cb6586202380488d40d9bf9a387b94 false -check_signature 43b8caf0ad1874f45bb154da508f0372cdc2c3cdc8e756899504fec7b0b9d05d a59f700fcf2987cc9c7767bcb474e0c7e5fff28a683d3f1f85273f4d4a2f4dcf 3ab24c6d97593664449733c413b823a1ad8e6e0bc71be00ac234d50f5e11182cb69bd82487ff43a3fc6b126763cb88ae23d0669ab7136db39a25e8978b210d07 false -check_signature 32fbde8c3759bf5b650409a9c33d6b3d9b35508db301c30d5d2ae3425d47c10c e7ed702e8ebcd0fe71c4ab766b080aadb61f2d4059a7596ed0762d3c562a2a60 d95b992e7b21025d0ff1f1958d32036d799601ccebf13ee057f4eec935e9a9e65068829d2781ac0dc3901c67b4e222e00576dc660e578e70bf3f477ef8864b55 false -check_signature 962c3bffd53677300d440a3b6556ff7778cebb35d825d3143111536e80aac60b e8d591bf5450110e7d3483dcf8497dfb54edaad2654e610c1b81de6fe6cfca05 388ebece67ff9adb4a67b88023487d05c3057a8cca0251d6339f501cca3eaaa41bb07202d88060e75d98e7cecfc39106acf07b88eda01c262e4b04ddeaf7d203 false -check_signature 6be8926e24c67d0917a2d1f53f42db18427e53493195d95fce014b01fad6c0be 16bd8cbbae8da957e0f3dfcc9cf31363e3ec779371298321604005385cf9f8a5 f557c7fd783210ea79c5661d458107ce6513400a2bbbb28dd564fd73e1c4ac2e3eec023422e4c980869d9d9ce58c2783a5e07ae6f4c031f1a1da95a401fde9b0 false -check_signature 46179465725d8a6c073b98e3d2213d01a1fc20b0b02b55cdf624bb9629b29e98 db680a925c4594cfde8f0bd983531f051482643cbbe49ae99eeb1d60ba2a08e6 6feb9458c34581428bbb47eb282068c7687ecbc48671a8d5149b741ab484d30d1cb47a02e92059b12533eb06425cae313bbaa14e00251c0278e58b5bc3be73e9 false -check_signature 84b4358aaef2eea767b6e2eb6f82b983958f379285499811b03afa96f0ff9f25 a6d5db99a459a3559516aa599a8ffba446b789dc1f5f10bbd7155f1405d6359b e3e01fb3ab074f884cc02c06e5eaed23c73a909f1d42a929c0adea9c1017890a6a87ef25c605f66fecc8750af54e730128d7d13cf7e2a82a0510e0b661165009 true -check_signature 9b9f99eff0fb466635b9b5428dc60f2e8f7ae1a0412e63436275be26967a426a e0fcf004e5a857a7c010ca8f1731e5df45ad5ccc1019a081e8182b7be3c1bcf0 48e74c1ac2fb2fb6c09944a0b82d8ea3e1337223a923b009daea9b4b0f8f380ff14133983882adfcbf9fb2de0efa641057104bd7dda3ba7d293e5e04f48ffe00 true -check_signature 3e0c26f5d318b5937e7ce64cc529817fba17bd90461a475fd3c9c5cf4cdb274c 8e87d01a50450ad9eed52b9d42ab6cc05e02eeb35b5e1195d4744fc568f9cc3a 1806223ec5a5e77d20aaae12fa9c50bd6b23152401a7cb3eab731abbd540773c4852a4b7576e98abd157945291d0fffd6863625e0edbfa7d24cb91e278834b0a false -check_signature c0bb3e5d2683f7ed130ed0b612aeeb5db960b29de9674bfed5add3c9e3c6168e 89dc49a9f0c1e51ceba38780a58a1903ef8d06309492bbafc2061ff452ff5bb8 3d0715ae398ad1bc3f4b5b2fd793a1b5136750d75e71bb5ef7abd7a8f3077b02b6bcdd6e19935dc6bfa04c5ec6bc0accb521e06c2ac32a7094fdc82aac2cfdb3 false -check_signature dfbfd2afb2a611d5f1400f7a52dba224029ae1f345f1e07dc640fa15eafc7307 4e1018481ad0de395c1c926b2236d281c3aa95d0ef396b52b7c6c61a40e6b7d6 37fdec353729648a07e240f0fdcc468df65a578c0f337fcebdf732768c0c4a0326ae06c0f078a43c8e935566798e039c32c71727fe20bd334c5dd3bcc747df0b false -check_signature 60c06b35d7fdcfdb228ff6f3e533d01b150d0939e8a1e6a13bd010bc0e047cca 85151cb17caeaa89caaff94a03408992c93134ba32eac64d7cfd89cad69fffef 512685cdfd298654dcd9c59ea9137a9bb52bb12efeb8af9bed7bab4f71d0bb030a0ebb9459cd2685155c3401f16b2d4479928a850ae2182ea357d9c9df4fa70a true -check_signature 5eb662f8076f6c3e3621ac0699593b8578eb181ffd8377b75d36f22036bcb3f4 0b423590e576f83486ba0d9ef99207d66074aad48e9791117c4cddc1673f9935 5ab3ac6284b47e60e6daf4bbc688fb80698e94379ba679d06a0389e30c888801c7430f5512dcdeee389fd86c24b7daca0dcefc628453a3d9a16dc6d295a68f6e false -check_signature 556572807a931c2128604cdb8fdd414b18ac3de8d65a6948b629df4c365ec571 595497eb52426eb087b9838204d2a5c1d47281beec602c60637e84196b053a3f 738f54815583cf89b799def50d85d2590e896826d68847f279b924f4f0d7590e80da50d3c89b1450d72f9329f0d2532e74042f1f87b4de3d5d06cb5a67ad700f false -check_signature 10a0df91796872d980789ea4078fec5cec9b0990cceb5f566ad8d59cfbecdf1b fcd0d9d5253c23917dacfc0895d1d870469815ffad14026e7e75481738e984f3 dbfeae65c7ada95e36406fa01cd0cf2f767dbc6c062a108c4026320bb334c130aa0fcb8af5d62f2ec0c3abd84d4706fa47c6148e8da633b5a13845eac1b78700 false -check_signature ce7a35f52f35795d6c1eb4e01c624c85b688a78d984c72849b358d60eba2d78e 0ed098d38a407652e60f24d6ce00e854c90aef88aec3d4c82f31c380237ebe70 41210e132b8287da697b5458b92383a27993c7add3086c01629d839994c550074329e83041ba9022aa71f3c6f65cafd1e7c4bd1b9797f821960cc813a21b375e false -check_signature 5088ef6f2153a4f1ce2ad274493830358343f3bdb4a54dc3718618ac1d7560fc 5a58fcb1ab2fa2e325d2d24901362d781507be9bdf0458addcd5f74e7aafbc4d 99e912c9513adda45e04036df6fb508d567e671cf49be3d032ab29b5804de70e67be571b84b24d154b1f50932e17ad23cac31c8640856b2076dcac590f3be31b false -check_signature 305b0d01519959e5fea54aa83117f59f5755187b2faea1d76f0d204bfbc692c0 f362fe9bf8eaa6b77b5ea46a145ba717d69438693b74b2aa8c53eda3ad6678ea 3f9f88c4104308d977f3e21f6844c2bf0fa65f2090a13efa2933158a693bf3e1c87ff69be84c86d867e43a50cfc46950a31fc7b3c8bf0d00ebc1d6160a76fb05 false -check_signature 4fe54d12e6c3cabc249415a10f5c1ef45eb8adab7dcd0215a1701d5cf362c9cc 01f14de28e674609225ff506c8f9fdbd408608c6d9ab8beadcc845cf5e4c8d7d a0406d540249d27c490c7ce15cf66feab416990fa0b81c6a8bbbf70b1b6c2e0bc67007fb581de7107487e1791aa7bff3cc4a79e609211e06214f1d05c9de5dde false -check_signature 51b32bc45478be6947c788d1b18f5b5d65494e27013afad1d288d94aa635ae12 47260522256f50dc07d3b0760b2c87b85364870db96f260154c016c6180917fe e1be5449429bec127df7fe4fa2db206cdf53be08014060d71b797516f2956206cdcdcf05181b8a9cc35c711b3634c5ba67ded6ebe971a24699506ec05dc9ea03 false -check_signature cf2dff8741a3a1ec23f0ab90958b0794010fa47e50ead239144db571c44482f7 2fe77e0e569b9efaf81a6bff4918842a8eb36d9d9b5a40f569573f8ca329a400 9e3da432cf3db477783375f0c6c4ff2aa558776cff47476619fdbe70dd479f0d1d039c4b005894c17e7900312d2572ce4e27c0ec0412832b547ce4dfce20b60e false -check_signature 68fbfc416fc1c81c4cc1afd2539eafba45a7047db760e43a003d254be508fe09 be1bf7420eea5bffd900da473b57dd6617d525af622beb3d58720325e2b3a0ac 04dacf4f3c8e3fd5c82859082067335d48d9378b93d9f6795f9482b0e60a914674b7a2721b6725672e2f94e0ac890d4653501262fdce200cac6dccccd271ec8f false -check_signature 07a13f9525a17f0b47f9aded518bba42e48cf5d2a3bde0f359534fab65659adb 9defb568ea53dbb7e3fe62aafad8caa4331c693e97891f8f17c56b1819f3532e 39526f6194387844493f7b8901feb25a02d9249d7821a71b4e7d66c27ca6507f5b24c4f2047650cea2f02ce464311884fea1ec3561aab3541efb75694fdafc94 false -check_signature aeb7d2b974574de4c0f3db1fc93d755f780c35d0764464162b30cdf1c8e466f5 33bcff428df9f9e6410c9202014824ec2a2289a97820a4348fc5e090d90b95fa d4158c2d6c413e9f2f2388303cc6e429cf17492b0383a33a75aae57b34cd1384fb51db0cf5a1a3606a56b7f603b9b89d978f0a2a10d63d10e6bdf51c50f4210e false -check_signature 9df6cc78ecd112da01166c2013afaa84d906570c44d0f6d571ad325754bea2f2 1d4086a13ed53d1547fa57e2574ba76bf7a4f5e9c7e833c72d79be92f63c9b42 aad7ecd3449f92eb32f4a19a01ff3ce5b968f2db45df84f96cadaa5914b4e5d9c7cb463a945d9c8e96c14c33fa1ee9e33a1a3d86c7440baa8636291f74b3300a false -check_signature 4e1443b22049a5bcbc62e0c20f8013825251664913125b6b591433c98afaa8f4 361159386bc69419268ae8173bb60a030f45292e4f43cf41dc7d949702040df8 ef5daffd6d1f8a9fdc2739e74790b92415e16c874fa5c49bc4e8510e0507c903be8ba340bf4396a61a13aed657fa218bd9835763976c24630bfdc2bf24611003 true -check_signature 4222fa87347badef06c877959e3a9b74794ce60c94474ae3c98de3fa6ca3d0c9 496a95b0ecc15c99e8430136285097f5829f7afa5a27f181d3d814e192292d95 a3143a82287b8cce81d2564a2d765d9b58d4fc3705c53e357a324d0029e0740f00f55f2fcb520be48be1d20b361e34d522ecd12fc10d7731c9ccad01ae149505 true -check_signature 2db4497de15d3ecb0d990c7ca310049bec73f288ae865a4254bcafcee7d08141 c980ce874f3f84b5b31190caf40b24d151ece78460dbe603d96c7727b1fdc490 cea430f572c7705f0e4a7fcd98db4f61fbb8db9c7a3656a831d8f1f9d2f463031b79b5d62d1aa602480881c50233f73ada40d396a20e72856ec3e8b4d9643a02 false -check_signature e7fdf00027f266bac366ba538e1f900d635f04ff95ab6f6e09d2875db7db42e9 2521921940246cb7e1f1aff9fb3db9965024d50eb833e4cb1cb9e381dd59f124 9f153e05ada1a28fc72b1d8cc570205b23665f4ff0737cac255c76a91593000770ba6d3320b8b4043afe1b77c0a3c6dd3d1f6cf855b748ff7c3cd0fc2c1622a4 false -check_signature 7a33b1f68d4c8d5334353b0d7c79a0454726cf9143d7ed48361163e1fa04c365 f0e5af64bea9c21ac669753d231649a6c2779662f12d08457f550028e287532b 18b67955a36af7890b502b4889fb6be31e69bd16e016ded3fdda14718dbf2204897d92fdd9c6e482da23149f0bab3ea50d75850678bf5edac33243dd072b960d true -check_signature a07a4e21253bf880c867ee40182194cda88350ff901e6f9cd7c4c62953709baf 4da13f551cef1069e0b1ce0eb3474d5ddc1e64c17042b6c578f88faafd95f95a cc04207bc2640c7d468f95e859ebf584f086d6ae06029092e65bddda804d6707cb01e475ee52d27c97eba424ee4240f2c2a0ae9ccc985bdc2d684b69d03afd03 false -check_signature 1952b0075742c2dc4c164aa3f4d0550d5ee748c27fba60bf9aa802c2b6bf9715 0c7ddb0b41296205a662a48097bba6d2f5992abb19cb7046561f6b749d441b67 bccf4cfe81d2037959e3ee11de599281038a08162eec90838a8403f062f92a79682e0317a9515bac75a810d9ddd9b1fca3132e186bd29c18f671d4ce8dfcb906 false -check_signature b2d7eb5d044c81c42f36696cdf411381fc4804182ddc55db2987ad4cada61c8d 8f9b4111bb7f5203ce78b5ee27e907b627a11a36f80f8ee1dc7939416a8a1e04 a431b46d5ca9ca41a6dcbefbc427ad3624b85c5b6ab88403be3b9efbe0d18d0320b1bab53daafdd7272c8494c5953441ef31907f11837125898dfc20f88a36fe false -check_signature 9c59340fcb9c9b37ddc5f18fa178df3548135e30b9147aa5efdd6fe6c2286ad7 f5be78453a0d5eac05cce4513b4a39d74ebf759f00043cdb759b0b732a01bec3 9a3172770772dca9ceb22987313c4f0b2a1d7dc8566f0041da3fa9d114308207c07e2aea608e04b2482f07212789f58012efef435eb1e739279fd291f6c6360f false -check_signature eb88c413548eb8924682cd7dfe1b66567a03f9c98d9f2eb272be1f1033c460e8 4dad20bd1b6771162262e4207060819add925f4b92825fae965118c6daac99a9 f52c7b612286b08c467d836581f0f08568c58c1f4c138b48937dcf107c039800936e9f583733e299bc07e8b69ef3c106c7d47f6ccea7d885fdf5e53bc3c52b43 false -check_signature 748fa65229e740c49868d1ffd72b0ecf2a38f6ded11ca3f8a8e636a26dbe2714 e6e4904cf87968010c9a4e3d9831c63b4c51badd0459f73ead063a220563d807 4cb64a56bbe2dde9e869506d34e3b3c3388c1f68540e1246b4563c722e86f8033ae66523851adf5ee50024954c5066fe67dbcc4b1aaa05793a6df7b31717f133 false -check_signature 24564f9558da8a47b74bdfb9f75386688386946e7d0752d68763985d8d62aa2b f8f93b439a2af5e7a1b3b22a10ba6ecba60a4f43193a3b3e12857ac14e89d996 eecc29671ae1f3936694a327c11f7d7ee4002250c2a735c767f9384705e41a082d0bb83e3800860f393de2924cbbfc40cf3f519120dae353571dd2e93286f231 false -check_signature 70edba484996d578afe80523b0ec15a07020e98899a4391146defb55551521bd e83ae828307340c439987f95445ca96dabf23cfd267c6e0a896935e7fbb4c9d0 28389cad85a826432be1a63d5b2ad367ed2b73488a3cfb3836127203c7726b09b9cd9775aa531a9586e11a836cd69c159fa1d3da7cce06443a4ebf90244d1902 false -check_signature c9ea2f365fa8d308e69d53419bba82c87e6e613bdc71ab60045fdf1d57f66c30 50934654502763c3874a8e456119274bf9ef0f8c21d2e78d033665c1e9fc13b2 f2b5b716e1a9ee7e0ed3e516c92f7f40afc92506f91baa9121923fc4f58fca0fba45944656a75423bf959497c3bb472dadf0b8b8f3cdfd34cc0c1d8e34ed510b true -check_signature ff19bee8705cf1a66ba5416bfb52f7269f0d015bfa2d5e1e8c81ef99f4174f7c 649477b801a5385cd7653834fc597a29507c7f2fde65de4ebae7edaab90dfee9 a813090183b3399a84afeeb8fcfb70e969891c4d3c2d91e45c0b1f27a3f38b58eec2ad32fcd408f6a96888595581f1ff381683f28f0120cde61d9b83c6e08503 false -check_signature be9c889bd9dd7e67512840992e205dc901e27f6d893b90d97d2abec372acc007 0e3b78b6c99cc2f6809869e07b11c814596fc6a42c66fa4c96758e713c09abad f6687db16d0fc8dfdf2991c44a34664a6c74f36e45f206eec007210bad745cb513db1e972bcaf27f5fdfbf2839bb1a4d279170d5476f0f4c8e05053112dae80d false -check_signature ca18a6ded48c9812698387c962e2ecb0294ef0f090b34281df94f50887abb50d 5d18b7f9d97e78bf7fc78dbf2c821bd6ebe7e471f2d37ab07d3795356ed98642 ecd9ecfd38388783d1f854ce7b039dc0925f9c7b3a5b31fe8bf83b8ddaf77d0403b8b967c5a74310626c3b5550c3ba10eb8f2fa50e938521d1ac9529b0cdcf02 true -check_signature 15a17382540b276d8d14235327a182e773ce2cacbf2420be6aa262a0823e3042 2bded0b6457b0a07671df903f30075ce545a0b32f7db4eed4f1f1aef874e8f71 c4c14248296c776da41eef54c79edd51326bbcf2f47cdcefd9a37db726aaea02fb4f331dec17f27501f1d34f02d3371000e902be65d83a140d3d61708bf6430d false -check_signature 37f1f0aed2c87bc73bf3c55eef293f14ea962f3b391d935f9b1f3e658d840166 4fb53c5aea9b35fe3ad4f01da473d389e2cd2fa4267dcf0d6ae3a96eac9e172c 1484664a1d652702c11aa7d0bc48ea2533ecf838ed4b051c9d1a6e878beb2a74c1316fdfec070b0a04ac5fd6f9c4323b2bc85ad018c14f925c270b93e1ad6a05 false -check_signature d081b11cf96d27799c2dcecdcfd57c7ea7ba3ec0e352d9eddffd3a429dd04b6c fe1e720038ebf0547f4f95f3e4cadcf9658d649a454fa9613344b00a87572a89 c3bde60ac4b18b35b953b5b7206b575af8bff02a34a6105baa1ded668f77d00560fcbddf7e1f00a1f26ade482025e55b0701d3b396c75bc9bee4c8912cbf6dd8 false -check_signature 6ce3c149c50318b940e14929940bf8e5bbf36d98b26f5d5a4be18954fbd9b529 41bc04037fad5296689ea05d32254ca4e9ba9f0010957ba89ef3b02cc08efae5 d276c625ebd3869898316ddceebb2deb5e83a3faa6f3dd865a5a25ee053eae009846797393ccfa759592e2af9815628f00ffbb16e5a0dbf7e76c5a3865ad0230 false -check_signature 510a7a19ed0b4605df1cb88b6cc36ca2dc926c58ee66bc21c32b281034ace642 6fd01a51372f8f70e3673e7cd7474f95dde138c46940caf5ca935b3c317a9c72 514ff38b0fbb1451f9a38c263df7f8a84f70540a2b24e44122a8bd3a8df0c1247c5f6aaeede7077ab643311bc57e8b29f9863ae8b6f920cb2826f9810a8eb301 false -check_signature 6b67fc75a5d30b54b7794482ee5f49d3abf95b1bdb322555034eec5c05b9069e 123ca7b6326fc2c6495dbd1bcde7a93306fdadb8f5b63a47d95bbdfabc594457 01c07283eb51d4cccfb84e71bab0b679be7a90ccb203f330d5720a70951cdcd4a595118a7d20613e29caf50e04646f49744165e894d49e3ac3524857022249b9 false -check_signature 66fa55ea31cce9fce2bbd6b412a0b457fcabcb01ac48dd8e38c96b1a7efdf6fc 603aab3508bb38d93b96dae2aebcf4d76beb78246f1d97e6f5343baeb7d5c513 c864b8d606410de83742074de871e0c73542e29c8d5244a52df5b9c5c0887d44263e01cad48c39175e4f9fadc6a5c3ff20916a39389826c8c623a3ec30e45103 false -check_signature a4b9dcb9b2adaab9f3290b12d16187cbc9f63e7287980b4c10724bd1ac096073 ed803b78c34956686dd3492fd414be5cd41b4026c08aa92a2ebc120cc1741a37 1a6484f1a961ca0b495f234f072ba69d49e543c12c924298e0ec3965823356d2437d0efe79903e60f069bc45c7ef02e5f8cfd73d21ffec24986c00cdf624a001 false -check_signature 0d56f36ad04fdd9092fc3c7bbbf44b79a08b78f0b6f8b54f2e3a9a1d32e5d179 6cdd24b6b94138c4dc28de51cdb29934a3113dd3b730a69769999f404bc568c4 ccf2ca4757e64a1b4837be8d6f56c7be6b9faeee5844b09a070223cb9b47b5ff79d6c18792a7dca908a16c253aed1c0c31db5d321ce8b5da85b5f87913d45608 false -hash_to_point 83efb774657700e37291f4b8dd10c839d1c739fd135c07a2fd7382334dafdd6a 2789ecbaf36e4fcb41c6157228001538b40ca379464b718d830c58caae7ea4ca -hash_to_point 5c380f98794ab7a9be7c2d3259b92772125ce93527be6a76210631fdd8001498 31a1feb4986d42e2137ae061ea031838d24fa523234954cf8860bcd42421ae94 -hash_to_point 4775d39f91a466262f0ccf21f5a7ee446f79a05448861e212be063a1063298f0 897b3589f29ea40e576a91506d9aeca4c05a494922a80de57276f4b40c0a98bc -hash_to_point e11135e56c57a95cf2e668183e91cfed3122e0bb80e833522d4dda335b57c8ff d52757c2bfdd30bf4137d66c087b07486643938c32d6aae0b88d20aa3c07c594 -hash_to_point 3f287e7e6cf6ef2ed9a8c7361e4ec96535f0df208ddee9a57ffb94d4afb94a93 e462eea6e7d404b0f1219076e3433c742a1641dbcc9146362c27d152c6175410 -hash_to_point a41c7e0b404c67b7cda969b102e539aebb7c55695ba1e4580621615460b85373 bb6efb51b4d847c5d1e03bfa68a624add0cf9923c57d78fd3c440845eb010007 -hash_to_point d7a3cb05a3846008cda1755b6d2f0d3b4ba3e2d1fddd098670d86ac58b4a6da4 bdfdf6e21eaaa082618d408e8c2da5485e5de7568dfb7908ff8399b056a8f709 -hash_to_point 21980724cff0a0aa58c720bf66e22c12fe6ca76b8abced48b51695d0a9929716 019bd0eb3215d19c8d69b9b1903418a9e38ad8a80b59eaa59621025fb5ab33b7 -hash_to_point 6ff28dc7e074be14c46f11f2cf19bae2ce82ba6e1b7152fd80ea1cd89cb11460 83b7abe8cda7a3716ae7170dd5d0e8e994574ad632ce71a7321dec93fc7d9748 -hash_to_point cbebf8589e79e38dc4b9da4986a24480d471fbe820f213d7059db91e94f06b4c bb926a656bb1cbba8ffb2e43cc22597496b342994c229d9a1f0ab7400a18ca47 -hash_to_point 8e88a4647a88d583e712c5de7725aaf99b667c35d71f9a155ddceb4535ba64b3 571fbb41ceb1caea8f43bd713fa9251019d8b70924ffa97cf98e8c795949405c -hash_to_point 5b1a391b8326adca88ea15f376a11bc801cd1a2cdc745aa5080951de5a532d2a 4cb9ff3e7055d42f1cf739fc22a57725d05f8e92aaf4f105a6f4419c9ccc0f54 -hash_to_point d71db4857bad8d104c940b44b3c0f701b77ac33bceb3430d5e8bf3d1855c5d8b 6d8e36e84bed370912adca091f25296201110a4745b5fe977c23d6f8bfdb5550 -hash_to_point f89a603f47360ee2b0db273f63eb012eaa8469b27ce12211c7dd3f6cf57f76d4 d33161d963bfe7934be5d38b880af91e9cea24b2ed287fd31c336e8f651a68ed -hash_to_point 2edfa0ae2c931f13ce8b9bb6ce07446ad95a52735dad1f3e2335f1aeedd6fdbe c2747f491527febd78065eb0dc440358074c20af18199d3443ca3862bad4a403 -hash_to_point 646022adcfd8365bd5d3394c622d5936678e1a335e7113a8a4a98d045ec0acac c653e3c8359b3f60c2d8c50f9869ea5de459908321696e228b2aefbf397445a2 -hash_to_point a780eb50f4ae405d09b4ecdada4a7a2235398ee9b8c17855ea14b42e410b3843 c85dde24690f374183fd8913dcac1cc7e1098246ad03a6678a97a09f73d730b9 -hash_to_point d50c5b9e90b00ad2a3d12f865937331eb8f1040ed035387eee4d5fa3d3f619c2 c8555cf0309eced0554a4fd6ea1298f19d6e4028198f82a7ce1d79db9892522a -hash_to_point 121d04463675f6eb9dba7aead37ef893ab63f9e31f4d57fa87edd9fae86928c5 d5051691bd5497ef82fa7904042e1191a28b4a37c63926ff35d07e900b9b8b0f -hash_to_point 036291b42946c45b627a83701184f7d41647779cf5475d39e029443be33acacc ccc370b8bd978dc2d096eede50271c16922994b97959a9bd0171aaf5d4eb981f -hash_to_point fff86285af4a9e8f777fb16723fea046207e0c5949934836acb43a36360ec7eb 98fc4d1c6077c21c2993bcd7abb0af6b4daaa4c2fdea13eb4cd5ad5f7ce0de6f -hash_to_point 7ed5a8182b8c79f553a101ef17df87dd45870821d53fbb00dd4d5b2a52f0effc 0ea13276d187cffa25955e3c49cf72244bbe8c1d3b72a1f5f0502139970f106e -hash_to_point 4c60069c56d10bb3edc4dfe98d73f39456d846d4139fd3adaffd198e5009bdda 2097cd5377111642d9d7b96980a316e5227a4ed0f989232d09f1268048282a6e -hash_to_point 176b82d6d68b0b906da1e992f5010f23b25d36112d8987d52e514ceeb8010e3d 3c8abee0fefa206c3631bf8c3208593afda93fa8a1ef75355ae05b3fe41c2749 -hash_to_point 72aa89d776c6bc7bd09385ac7e8112868f85025fa966bb5df65bbbd5c63a13c6 dfb5f593f9e78041c63f784e6db5f902d98ecfa3bd3668f8baa56062cc9e9e35 -hash_to_point abfb4aef4ed3277ddbcb0bf13fc54faa8e161ce9b58c625d4523fc050e67e991 746f5b282beda0f831a7cb453bc5727cfc70d01227a109fb26a62d06f09ade3e -hash_to_point 433ea849299a1e5f0ba7a47f2446104f892101945d4179e9048192bbc8f59af6 f647b5caf04c090bf1ed6261154ce7a50449e17fa1d547fe6c21b03b7eab73c5 -hash_to_point 1f295bde4a00978cdd63961c782c91dbd91729816ca9df28519b504c04893b1a 2c91d1f7dcc2d5574b4456b4e719da361d79e9643a95c35281a734250000b127 -hash_to_point 1a850011820eff7fc9a5644781ea03724455da1aea500aa6c87effe72de1034a 7f64606531bf1f011843793a89145f3157d6e8cc5dffd6d289f1b77b0ff38145 -hash_to_point 54bd98c746d28b7c9d1de0333ee07a263c4d9a3a0147da3c7b28dcf3e6a38bc2 438251faaec2c50a535011fafc8342924bfbc3d86896e5acbe2939fd3503fd6f -hash_to_point 61834c9ecb168cbd15817685e16b154916ddb7f89748f2155b023ac1159772d9 7b0b740a8fe9bbd523a9a3adf2c440b7d7eb6ab9e766cd666a42269f2c4d76fd -hash_to_point c9f2b10159f2c3dccf5f41a86f5ae3dbba2c98f61094652438181268a365c09c 54a8b18c11f206e24785c3bd484ca31a5f057f60be274192b0e65e8b4d312d19 -hash_to_point 178230e18c2a454c51bd69eab228a181e75d43d8be60039e35cd606e57d1a853 afeee169414e67181f57f4ad3bcddc6f7554ae38020fe2e11174a6bdc20412a6 -hash_to_point efe510ccc5f224a5b50c084c90029ca12eeb0ef01ef4698f247e1df771e9236d 82e11c8ab897d4bf0baef243c3f95d16d32c45bc9f13f358644c21e19d4cc0e2 -hash_to_point 1219cf4695194ace13c565b28368f15bbb56902d56eb895f586fc0eef910a5f6 412cd09d574f68e498741ada157a85336a60fe75fedd0892d111ce3cb6b2be4a -hash_to_point eda06e78625070db7323c2f2cb76054053a5e6dcde892b0fcfd19041872dc1ae 3d207eda1e309c4b8b8d5c273a29c864579ea2652da1fd2609aa14b6aefa3b23 -hash_to_point 9094e97036b69eaf668add6e179a06ccecb17036711a9c3bbb29beaf6d643ecb c10aad0c1e8c3706b79b5f82e75fc2280e02fc19603c8d1159961175e3e11a4c -hash_to_point 37b1800578f3ee0114136ef7e429102f664222df3003b549001948512b1cab3a 9e8254a67bdf36a4a20a3e6224a2c743b1736f5bcc8b821978ef1139d4354d31 -hash_to_point 4360a5b660eb2d326d6f69be216e351e34c497e9e44778e6ce359fbc98088522 4c1ae50ca34f1844239a9107ae4cd86ede2280a1b860e7f2f2234c2f2e4874ca -hash_to_point a75e82836e65682bd9810c7434c536501569565af8e9372ae1dbd734640dcc60 9c8f1dfb32d9cbfb35cb6651df2a280b86a06d2cd92fa7fa566a165fad1ec172 -hash_to_point 3cf2974d1d1d00e0be22a2030c53f350883bb637f6a8139b92a86c249fd0a966 62c2454cff6f4401240d7c2aa2ff4e0665c6d0918013ce2625034474995bbc09 -hash_to_point 088aa079f44a79dc490ae200732e4728471b9dde7b85535468c26b123c1ee340 1ecdd643b7b998e2b504c41ac6baf7df94dfcbd42e513bc388c54a8a92e6b390 -hash_to_point 5543787f328e9ebe1addf2d4ca9ba0709a7134205c0850fbff8f9c04a4b31677 5c41f42b2867ba4c14d0d5c61fd90226e147ab018f008dd83e0d768ef28042e5 -hash_to_point 823d901ac4cdc6cf3e46f946c08341544d7c18ffb2aae6d652a1a10af8694c7f fb0ac69c45a7e509a819eb5c01f80c3c86c347bb504648541fdab820bbb94842 -hash_to_point e4bfca0ffc308fc7c344654307a32ab3008bcf5070523133093d4387341ce4d9 94b09b83f1355e9740c22c8600ef5a278e8f98655b1be0b4bf7fa6ce7f844cdd -hash_to_point 3859f8109804e596a4bf45126d1c4a3033f4112e349ef38f157eaad5e9248fcd af103b02bb065669ee9d42043abf5b31c51e5ca815f29403c56258865768fb8a -hash_to_point 9a49662aeb249adbd4c093da3e875c2a8423c7e0123acf61b3a5d488055778b9 7195d8d965fa146b447da717deb4944622a7735997306d0c21a148602c95e823 -hash_to_point 5c49b709e88a273f56d775740e750749f859855b422040b0036da433165f4100 af7c51a68116b577d39663d3e5aca308b36e9758c0dcb5c5f7117b0486786841 -hash_to_point 1bde7580ee21543d6b47486ee4d2a55c568a751c7628a495648a8a0cd7ff77a6 d4c218507dfd43080e2e5ed941b9d05456cee990463f13dcbe4257da0d0dedf2 -hash_to_point c990a7af0a97ef27ea65e3166b0222f73302a02c5960ecf207b4a682aee0372f 6013ab247260392e0e512362dd3e416a6ed1132d0450b0d90d2bc8f00d160c7f -hash_to_point ac27a17d1189389cf1f70af8e568dbfd53f577793b386206d504ae0abce05a39 c0210069c656eb8a32f363c5e67e26b472918a9e0124f03b74b73a16ade77e0a -hash_to_point 31ebe0d6dd1662bd7cb451c1376b8aa92aa412af570743eb629cca489bd3fb9a 57f1b34587f7866361d5d504643ec7adf86d56c2b8c60a77aa2d186923ff5528 -hash_to_point 449ad773bff18c02f894538ebe9e93a54843a1e93dcb0f02aa0343664aabc9e2 da8b976517d8f32ad964ae735126549e1f8bea2c492621fd6a90cf4759a5582b -hash_to_point 4aee7ea3d49d96e15c77d4d20f14ebf121f99fb6b7e8081891e88198645acd84 97d6b0b72039c3614f8d46e8d37f605e2cdcff3c7fdcfaff1e962c9d1686cdc1 -hash_to_point 93fab226937b99d05fe498d3a00ff3da70a890f0a63cc2bcdf2eb7fcaced40e9 4562123a0bab149c5fe22522c3683bc9a3a682a42f30cb1cb418f549e7e5bc44 -hash_to_point 91f2c9c030d5845adf49db17748c8bb19b80fa1fb13c84d82834dcbb2016fe92 2f75f4f0b27f69b01ef7026694a66370b3999bfd01166ace59c3f794cfb28b22 -hash_to_point 1051476dc715c13516a2d9201249f68b0e27d1af16a835d7be851a10279417b6 10f935ea2e704b8936634edbb7e89154dd53c3ba74e95dd14881e9d4f56854b9 -hash_to_point 729eaeb491d5beedb4766cb89fb4416dd41a551e153a08f83343168105cc5765 f1fb9a530108752fde94a03ea2099ae17f6dcc72b23a52910ce4d8cb0706bd98 -hash_to_point e399b1f1aeab37496984c0d55e7d7e55cdb51c6e22fa3ec5d1df75fce5783259 c03ac1332cf0b9d02dc7bee0be7470880cc596e144cf5266e8b77c15b87e4f64 -hash_to_point 9ed052bde729224bccf780bcc3d677b7239c3503a3f3254d1bfa6bed256862e6 058f3c520ad62411c20b25bf78eea2ce834d12a12e3b8a47191027d9387f0791 -hash_to_point 6b7295099bf669ea795f3eed2bb0ade6e0a05d232c6d67da6dd583eb3cb6fdec 6f13fe162009ecef3c5a47e26bf34b6d016b35364eac48c1fc3775611886ef89 -hash_to_point 85c1cc949214ac8e0a3e205e0e1f1a66784ace42907cc9723e3b34acb3ca6b83 6dab031acab92664e04bc9807ee88fbe2e96f70e9069a38300ecd253bb8e0399 -hash_to_point 8b0d8685fb92e664861a18b6fe4cc83764da7ea829a52c8b0f21f798bdb6bc0b d7b18aa946e28211fc749507b08283d963d2635c9ad7e88cf5a1f7ab215a0543 -hash_to_point b85775a9963d737099280e74b2cb7fa8b433d6e2f40d2a1724d19b959e7fb1d9 65923fbefe0ae80eb947aa34d68af59f1fc104ac500c67a05f0f5fbd416967d9 -hash_to_point 5727cf2cc96713d998aa9aa0e1e8f1ed69e34fb54b9752015ec2bcbbdb1d4603 c1fc2513dd29f7f3fcc0d3ac587ef9aa6881c7c7f9cafff07047ae27f58ee942 -hash_to_point 43fa08067a97a96cd7f1473038c44ffe1c5da34a924e2cfc9a536f183090dfb1 527ccc412739ccb5bc0c46c63f35621fc2c15b3fd7c83252a1dad040aaa9fdef -hash_to_point 65e3d22f19312c1e9529b2200d7d3e4a88ed33a179bc2e6c818d944897037fa2 f6764f19dd66267116479a31ca474bd88b15b02a95c64c7e34d21e2b962cd9c1 -hash_to_point 56df6bc2fe426e30b5a72bd37b9605aaf75d4c90648a4f9c199c7ac5aeffe15c c5e978be4dc1b45abef387292aa4808dc5465a263697d86fef3b1aa6e1b28272 -hash_to_point 48518169ffb4d192ffdc6147376a83d82385951a95004cea28c7f7bbb9225de7 22927c67e44a353c7cb9bc303e18ec66fc99b75ebc2f541a3dd03f37eaf900e1 -hash_to_point 6ab3663a5b8332595e90ed1c9ef96096c29a6fb8ae4f4cff0e9d30e0f4448fd7 20853b151c9b7b053bb8f9d0e6ed6c43c6ae51d1fce6bd88fd90b2b8167f4187 -hash_to_point d9f3064efa8a74775aa8be7407461529d50a8c4fdac2164b643feaad02c13ab4 ab24e90595793d13b15520f42dae212c3c8def7a1527a961b76f0ef45c9ce0cd -hash_to_point f2ada1d4893c33abec4c6f8679d9fd0df32dbeea8ab7648fb21be3b084582fbe 11fefff917b119f6d6931fbbc5e69273e7d515b76bb82c46313f305f36ce481f -hash_to_point bde15c2c63ad02277633de0c55f20c1ec84deee59c95b73684c9b1d1d98519c2 58731e4bf1e704cc39d4047ba9fe35939b9203aeabb6c57c95e088711e77693c -hash_to_point cf8bb235afcc34e80dccfe3a1854f831dd06e82c299cac2aa514a6492fd9f8f7 05f0dd09a1f971665ed768be44e4c0a72cf350e056cad3fbafcd6ec5472b91b7 -hash_to_point 8594429dd064e33953a6eea4331d63caf734332b7ff64e22683a5242384d6911 e993c8d05980fe22b43253815749a8cb011c86d245d3531ead5f187f5b214a61 -hash_to_point bf1d265f0126c423cfbc7bf7be108bc82107941672bd364ada4a8455d2febda7 161ea254a17c312969b2b7fa9cf73def665a18337e7aabdbd834566ad7ae382e -hash_to_point b80f1c16a91c608da41a7cd2dba36d57c6b73b66da82c7a02429f86d9df9c3f3 5f01a6297a3a2908078593bf1fd6c8a2e77e0a0fea420fa2fe3bd5c54934da9b -hash_to_point db690f6293b0bc437397a4b8d0697a6b7d03b861e955ead4f37284598302e597 8707ff11d73168789da7c9bada742ca8e77a6220aeb4fc63616d429dffa9acd6 -hash_to_point a2a2e220871df683096e88319a576c9397597d53dc4321879779c00604b0c9b5 cd8b64b33f13ce7b736f54077d849d68e14968e6feb8270f677fcb457edf8a16 -hash_to_point 4e1c7cc557763f694c04e55d65fdc546d01431114fdc397326b6a90975d3bf6d c742ec33ac654884a783506af48f9065f14e2fdff29abec684099c417eb49fd4 -hash_to_point 7a6e256a82f9dbdf19e921cf8bb144e8749790bdbb2228a7e701796224f5c262 6ebc96e385b115fbd901161f13d51a88609ce7f6ad414921fa6f51f5ac649b49 -hash_to_point ff73c806a9906b75ee122011611134b5ac3d2780689f9c4bff993dbdd91d7cf8 bc18e3f89062b2a05e787f219a92de3272a66e9c9b04eb228fe54257711d9974 -hash_to_point 396e4c811505a894766a79244f3e14da8df8e6d82c17cfab9542dfa4df1b28a6 fa04a1371aafe952ce774b17a8613526fda55a6e4f7e59025f1e02c975753924 -hash_to_point 152f25cd6fe5c48545ff47257c75924176631bff77f3fbf94f0f0479a853fa7c 77c45cae7c16a629d6e4a0683e8e0fc39ab709d01e65da67d8c27488e8641530 -hash_to_point 867996419e3d0dce0b15e20343357131b498750b1e4886d4a2b38d64fcd1f62d 8491b1d7d4d2883a33950f3a92ca8ebf123f94805fb47d76dfda6c628562c183 -hash_to_point 108f76b5804e74c915444d4aff33bd731f04168b7662689ec754ab461ab5ecd6 0e6dc60f9f556a3b819fc1a47e9e0e834621157469b12c508a7d18d2358d27ec -hash_to_point 49c832f6f1c29c4324f82d0bb2bf8f4ecc36bb2566605c2c8e6d4d760d57e5b3 b0d0f394019c1b16c76e3920440f234252c5e7cbcec05ea7db8d1a1455c70ab7 -hash_to_point 8a43aaee739ccd0e6f192f682ba8918cfbd616b01ab70800c8199645a0a14eb7 846983e88c7d58a3401637f5688f405b0464fdd36d7aa058e0a3372bd4223698 -hash_to_point 5e4f543b7eb8c6c49d69b23d10901f80e2315085abbfa0087241034bf960ccd4 2ceece65baae61d4618d6659e23bfe60189a2b762166b4847fb778d3bb289f6d -hash_to_point 110af662b71713f9098af1932e6514bbb43d5724c80b5cc963fcf1d4f11062ac 3465a0e4b4402ca45ad95b686f4e6def9d8a9baa38ce59a6bf3443ba349db8ee -hash_to_point 38ac2bde8a6b060da1d934caef64e38a0e8d4d8fd7763851aba81200f8d12691 10395bbbacd12f40a498a43ed954be7e3e238aa887b040518b4cab198f59ecfb -hash_to_point 7a98a3e34b1ffe10835bae80ec34ba77397315704a1dc23ff4064273d25d4d15 3520fa6ba42d911cd53e7976f7001b856bcaa264b0f41dde8885ca9e5f667810 -hash_to_point 4b4ffb69e9a3af51aff5687c477023177562d94fc369b222cc86214098384739 439c0752992421d0e8c6dc7173e44729f52a6d143c235ebd1d756b30dcae2437 -hash_to_point 9fc49562b22e1ab399026a1d46e1c1e872e2e07f53714cde0255feb2819148b5 5224d6bbea8e2c33dd148f764852c72cfee9665d6c6ec5ef77031ac5ee861f47 -hash_to_point 892abf488775b2fca3fe4b0c3697f58e3165866631a30e9bcc4a98bbc9f42f60 02770005fd9b53c08804b9d4f366a8c2ba0554e1677affd700a3d18a3c64a0c2 -hash_to_point e6dfcb8132d456553bf26d27d9e83226ffe34ea76ffbc6eadeaf6b132058bf0c 22a758d187ff8695e1beef47e494e6f3a2283b5218733d83ee28eb5ebb67b5ee -hash_to_point f9c73146089f8749a8658859bc960a23f993b0597eeab25958de16058bb51195 8df5180c750dc6d9fab8afb426c77e41a39e2444533fa46ecf76984b26c028fa -hash_to_point a72f10ac6ecf70a1b17a7ca9e61e0215350caf0801e0a053633955095085db68 aa1d2fa895784e734cbd92d356aa2a94f23eaf4e16e524994cf9960cdc1c3471 -hash_to_point 8106ca0e8fb3b6139f204328c01acbe54c8ff38447933bd48f85f864ece23042 85ac37e18ccf19ed91071684ea730019d6469f8bbe3bf74ebbabce6d01980d06 -hash_to_point 62827a00208d6e48a1e4e5017d1a42eadc58d382dd0d291ac0b910e5cbff2945 d62da672f788bc989c776f7ad30b6fdd92a46c6d263ae6f79d26cb698f52a340 -hash_to_point 321d44dd0f9e5d02241f556195ec29df2ede40b197f6d400674d2e75765c3978 d28c2353fb61f321aebdc326b828724be400d103131c09c4095dd711029d7749 -hash_to_point 941d53f3132fdc8c14ba4b080bc09443baf311d967ca5016f62fd458726fe1cf 9be9b4b884aac7d8faff49be25fc9f4fbd9d828c07cfab53f263fc3c1e3a5ee2 -hash_to_point daad94b6004eed3f9f1a58c82c6cfcf71f74bf4f2e60cec60494d25f1a76b4e4 6ebc76f7196233ef999acf54098210f848864b912eb224d9e3ed2e602dcf765c -hash_to_point 27398f739e8821d1d5c85b045b343274c62476036b358b11aaa87d49445c5dce 1a4afa8b6b1fc48b17c0a9229b6f02d2d41942c03f6e5b1c849f229f436f3fd1 -hash_to_point f6639cf7f41619a8cc58f9265acb86576d7960c0b751afeb48ec379da86cd4ef c85ea3ba8c1b9fc7c4cdde051e8501a2d224732bcd0c8974f477b36633426134 -hash_to_point 7b9931b576f16df50ce0fb29a70bfa94d6fd81ef85741fbcc0f3d67cd1c4eff3 f42c2e891107d4deab425ba3095f4be09651a22a0dc76452520fd09d42d5f08d -hash_to_point 6bb9088964c82ab8e892cd061ab3058632fa15d98596b8a9de0854a959bcd8e8 4ff4dbf37cc62703707361a14c1aeaec9e35fc976d25ec3928be7e046a80be14 -hash_to_point 993bd39a1ce9b8d03d504563fd582246823cf9d17111ba9b7e684d47085784f7 b036cfec9e9cc0295031feb50358511e70b3f7e71a6d069017f0e428285301bb -hash_to_point e3594a225b48f86bb65537dff478f30d07140c8f286708612fa9fa1474088e39 d28e263777013b7649cd048c067b18b63161b8543392d9a85cad551c871d55c2 -hash_to_point d6064b1a3586bc0db1e60b08833ec91fb4709f05207585aa6d028251e3b6bce0 c360c5ca99f0ba309d20f649470271f5d74c1a794442d7b0eadfecf43c8484ee -hash_to_point 5275eb7124a2df3d37070d9d0225aa18363343fce22d9380f8bcb5f0c33dd677 39e8f1a8ae42d82bfa30b0792acf406a94824073713e7ee1b8e9f471704912d1 -hash_to_point 3e0d0431afc723933ae08b8a0d0ae504ebc95194a6ab0c2e0547bfd7bb340c64 250122a48610abb634eb86cd5e6ea626fe7e7ca79a1f7bec55f4db1cc518325e -hash_to_point e3915bce8800d31b7c33721fb02f9844de2fbc93e3da92081e747ae83a8e3d22 7658f36d6be4ca2bc8621d1f68a4a4dc9e7ea2601ad8a9ca42ef0561616a067c -hash_to_point c202bcb53044b52574d42108d07e1bc99e3437991ea55be36bdf880848c77733 d078eca7da46a5e6883356a5ed26e08e1ba7957d6b51c518cfa80b3d194a1480 -hash_to_point 017324ccd42d217f2c7f6de721229ef8c533cdb0f4f1433f6501196a73ef242f 8bb3516b35199b7f4922797e9683e9d72df8fd589450c11ef83949b08b64a8cb -hash_to_point a7075bcbedccf4592cf1983538e4c910a39e08045b070fbdd821a4cb9e9020c2 8b876e4d43c31888bc2642ce61c3ca3fe8c8da55b678cfdcc2787cc9cdb0cc12 -hash_to_point 3bf8ca02243adf1a2e34c8060e416cfa29be6e411727a0140fb0036bc712f53d 196dfcf22e7fc6b6d7758411f806f12a555d980520a1d69ee0eb74057bcb7feb -hash_to_point 29a87f1d88dc9873e143a0678d447af66ceca1b5822337c9e85684de160ea63c 4aa8dab44e3d957450c871bca013bbf40c60dc0737056927e11eb57a3ea4d72b -hash_to_point da0bab557e851e921186032a19ce21bdf47c767558007ca6de31ec250c00d25c 7899ead57f00dc0a956d69ba2d8b556a1be8f35a5ea3b3fa7179bd3d3eacbd51 -hash_to_point 2ce966eefb280ca6a5d629f33d97dba0a9f41d4ce71ae91ea162bfd20ade44f4 f0bfc699e61e362216311b22a535b70454b725efce9952c5ce22e48a573fb570 -hash_to_point c2bc98dd2a7517442a9b66a8fc07c3b87e75159aa9a81ef302e7a3d7aaaf18e9 6c042a5678639712c06001d579512ac0037870391e501225e5b95f0208bf5515 -hash_to_point f3a1ec4a867e2fb5e16f5617b47a677a0f1ebcdf4c18e935f4c94adc80782d24 fe7994e96d8ed503d978e128d80964a8de669b268eb71f5a2e3aea3ec392b3b2 -hash_to_point 307be4aff1d0d0752e857a88e7d4cdacbe9f6a1efc9904e020ed42f37c497ef9 f6f21058b5f3982c0809ca41c3b2429a1d4c7fd7396263f3ac65b452b85b4b48 -hash_to_point 15471a73a0e2f90b776a373b43fad5fa0146c310398d7f2804c513cd25aa6270 35ad033391d7c25d24605634e3db7069bf18961046b96b5a83c7f6fd67b349f1 -hash_to_point 3b4b3eef3c38f75fefea04483c2a69eb569774c79ded92bf73b9a55cfef2c5f0 4d013d88cb99b0614cb33b50a9ae05d7a5740f25c6b34bf9a7d5806279be89aa -hash_to_point 133a76addbb66d62422660ae61d1bd785d6b017bd84cb4f8163e69ac801abbd5 92a8c338b452959081f0a6595344a696777fbf077c0baab6c7cee2eaa79b1e50 -hash_to_point 6fbb4348466d9090d2f32b6bd7c6b54bd399f341772c06199d1d6f425096fec8 d604e5f8615b80fbea9662cc7eee326b4fd90bd03fc8b02de5d5d86bdabb20aa -hash_to_point 0adcdb15fb9e1ed61b87a5427d52b5ee0b53c0305a4fba760bec9c23831ae5e5 e3bb0e46f9d22dbcff73845aec18ac2c1d3a38e853b0d363dd334b1d2b3cadc5 -hash_to_point e088dbce19dd6bb48e0d2a3d6686b78a3141afa2e3fa271c501e3c712b3d8398 bcd58daf1b3b004ec0df892f208eccb74b872de2ead94fb66ec40143653bffb0 -hash_to_point 55957f638b190fbbec4388b0230307802443ec0db552a6e12831539cc4a9d11c a8016ab604ec66214af3946b5bffae7b9260c311da6f13c8593b09756032eb60 -hash_to_point a6fdf6bedaee413277b0ff736300a8469e2b3405ae2b1f5568deb99f69c56f07 646f12a0f6c5938dbd9c9369bdfb62aed84eb84abd8eec69f44cd32bcaeaa6d9 -hash_to_point 34cef7d5288e38092b6f1096b829b40deea00ab66afa03003d914c64070b4906 bff806e8c68b0ef5a88ae5e64bc67fa7ee1e42d5777a61be8e79edb992a71f1b -hash_to_point 34d06d7bb1b312e9740bb0940ba407a0581eee86ef3d7b20a69e6785a31b6255 a6d899713ba1222f0d62be8dec91b525d5291a2b69fdee885291102e27c80e5d -hash_to_point 041fbca587b82988700a9f573537fff15be3fcbe572dc3da650ad7e8c903244d d005005f18cc37aa09601a3a2e44ad2024515539e48ad914ee5e4323fd94f551 -hash_to_point d55e5640c3b7f0a424821078d7cd36c0662a7502facb75b373c91080925afb63 be39ad5a7061b46343b622ad97793bff9aa97ba2e31341a0495a616e9fde6223 -hash_to_point b965f1724da830fc6a892226e420d8d5b924229abb357f242c1e8881b0ee13c0 373057e3c45c120b419169f52f992d76a0b1de1f2f2c918dddb4ed08439c3163 -hash_to_point c6690c38e0551c87e129b81b03448a8bbcb8cd936f3a94193ffc3dfaec006ad7 652e8bec2e485851f74fb7ccb1ad6fa1a71fb21dd45341acf3985c86c04b3f30 -hash_to_point 1b8f07862ec7f800e61a221410dfe9e93a3b41a179301ca607f11afb57d9fd81 135615ba236c265729c2aaa69dc766be83780f00f047d504148323515ce91ba0 -hash_to_point fa7380693c844801971aef325f5f6707997311d1555624697d96655e5a7c1a78 a6356b23d48a7eff106c58dafa03483b0876e9ae27b636b501f7804bbfcfdef1 -hash_to_point 549543ba9f2f5997330c48a434d71b06dc01be1d422bb63260465a80b5c66c1f 25a0d041877566b15cd9fa2eff1891f8f3830e641668a853983d298e60b78c2a -hash_to_point bd7b986a669c07089a1ede05cc8b7884fbed840d19158235aa1f72c3780c6eb1 27cc4ed181593db1fcd2475f3e4498e97125717bdf3078196992670dae027246 -hash_to_point 8df8549222c7057ad60b1005bf59fe3a3be4a7057d3a707eb876baca3839abb6 98cbb6c5f417c39a38b0f283f2070e55450f993f2a65734ff5a5b84088f53318 -hash_to_point 36c0da4710cc1af1b9c191d268e8ce3f9dd55830ab2526c4891a1d76e58e3c3e 614a12c387fa1737b4d133bd8b237d0b58026b229058673b52b910a6d8d89848 -hash_to_point 3eb748926c59e91f5784422bc278d8de63c338f9d9dade152c4db021411b1833 08c940452a51916d4b74d578b04f405caf9a890105f4f842d7cde97af4f245b9 -hash_to_point cdf1c39919a44008542d9a7250447ed719b7855ac5b1579d2ea6dcf998e467bd 96aa0c87f8139c2666093d61f615963c87d8ef00f09180abe5da1c52fbeaeb31 -hash_to_point 5ff576c6cd73f2b6b19e814b47207b55849b7e1b7c5c48aa31241f481d5626c9 3ec2dc3df285b9b9bfcae34ae31c2fd6e0846b0c9fd36ca1bbe33cb6c4a03b83 -hash_to_point 7c9a7610234acb926d743ee9aebf46d1ba5bbf2ffd440bf207e638bea8d50441 c1ea504ee08263f119f693f5b05af7fe4fcc39e72deab086a2510d9b6d75f91a -hash_to_point 3bbb07ba1198511a456b5f239cd89475588ce9170353009332ade363b3716189 b72e0bce5933c4fb1771be3b302e43659fcffee0d9b1583cc3f6c4b48e81e248 -hash_to_point bbb7f73379243df51ad005440a88d6fbb04cc2c41534d4fe6e95c91c3161290a cb39c1b2779021b3fa618610a0b51bcf29a6c1601f162e25bcb95feffeb5d5ed -hash_to_point 048791f4d98fac912126423256f685e7f27a762533bf853ba45a21d9f1fe8a0f d2982c159beb89cfa6cfd393370aa4aadd04e2ffde8524aceac8797cf86b38ca -hash_to_point e27f4f8adf3479f2d8a3d0c5f9ce155824a5c209845574c54571521e8ccb374b 98c792f4a68a0b781fd823bbd70db88a380a6c66b697e01228f9fd749d1e840a -hash_to_point e3a1fea613b6cb0aaa74d6319a15f933821eaca8142e9db64263d7032902eb18 3c1d92ed50a5c0dfefe9cfaabbe8c5c3a2a477af9c700540b2be322b1553e864 -hash_to_point 79fd41bcbe51dbe34dc63e86596a19bbcc3b9044521ae0ac479fcbd2760ae131 8f333855cae1aa56028643ed8d25d3275ea53ede285f450d76dc57d2bc7bdb99 -hash_to_point 45848cac731031637fb676f68ef3836ce434ddd1da962001cba3660be6003697 1fce38c08f2e62515ce33d6caf64afcea80ad0451dfdb539cd3ee128232e9647 -hash_to_point a4be3ff8b2b34e3792f72a61d14c2bc75943892ca00c247977d51c93de0f4acb 7ea3ab561d6c0e86e8d018192c4c5484f3f4a60284ad76325d832ab7ec51c7f5 -hash_to_point 53092d805d173972deb7fa4f0e9d1c0da34a52b9db17b73cd0d3141c891c6565 b4da3e082510726a6254d1deb74c597892a1a80125330cdb062ed55b29c5c9f7 -hash_to_point 1e6ddd29275cf7bf71d721e9c5d38fbe3bf7a7da2b8052a4e04da15659f1488f 418ae60b1feb1a573031c0bdf27fba8c8a7925df06aed487a88823f510841883 -hash_to_point 84d9ce991d50c6597f8dd4afc440fd32ec8e2f1291e639d4a29c377ae138c4a5 ef333f5a3d834b809b9888313e515a525bd0533183f81446263ab090edd5fb80 -hash_to_point 06380d1eb9b7671d483362a76ff7be319f89e4c16f3a5c98331a5a271a570149 ba5a73c2157f9fc185d819d8788efca4dfe59e15e6dc55ae64aece934e8143cf -hash_to_point 96745d82591658f8f4844699c4231fab7ff9335d2dbef1246979c962253eee48 5469053551a2702141e98aff4b31c80ff97cbd21357f422a5eae48342b7700b4 -hash_to_point a2c7a37412fb1173c0e75e5a4e913858a2f2772e5f21be54cbd5391cd81f6fa6 d77daca7615cad68cc0801138534ae8d03945ff9c55b893a2858a96e53cf9afb -hash_to_point dc2689312e034807bbde334556b5938c83c7a4f205e72f83f696f96514760c7b 6318e7b245a7b68609cf3c3c08c364efbddde1d2fbd7bd8bbacc74b6f439a0f5 -hash_to_point 8470ed5937b200c207627e7d637281c03c01d6c0814d94389f10108c6bb56d8d 596141be8631d387949d87fd18ec7a5d890790d695806cdfd84131f9a92d9bdd -hash_to_point b7e12874f0ce763d46e04ad67e3b5a5586b11317e6e592bd4c4f2d627a662788 8aabeb61a29ac826ecc31d8e265be581eb1e4c959f918256d328286f36c57047 -hash_to_point 2e8a949f9f612a449d9bcda59891c9bcc68bc234e7437879c37cfbeb09e0da6e b73a45aeb5d849e24682f32d4e207cf587c9a7692a83abb2e77954c52e1f89da -hash_to_point c1abfb0cd3d4f1227dd2834fadd86871df4af2140147adb97a6325ab0bf456c7 2468f3374862cd9ee0716c9d13efdbdd740ffe9a0e152f8ee575c6eb63f3414e -hash_to_point be3abd7488aa7450a8816fa186790d09c9ddfde25a329258a648e1481eee07cc 3689aa3bb2039bf3d670e3fd30477a3e7ebd14984a5c4e197a0ce0bc7b811303 -hash_to_point 71b38c953ac5ccb4afcd8add9368f782825fb2307247ce228013cbaf1b99e09d ada5fd051ecd9725290a6660a69b5efafb12a36cb1083ba8d2a8bb779218a0d6 -hash_to_point ea0830e7b4248f3cf32a0efc1dfa812a1f22a1d9e0afaece898e09f0afbbaad2 1284d0f19e53a55c4f15ada781d5954e1b796a3e6195e0e2ad64013d931edeee -hash_to_point 0af5ecc45142f6186c5c8d2328b9378bae03f6c70121cbb7771eb4c8655b88b2 1cc14ef67a57c31e972b2e226ca3bb5034ffac3b967e14d33f1eb9087a4f59dc -hash_to_point de64c98a76e2443dc8b15ba6e02c08c1b6ff271a3ad8dd5fed42bf9773fd97f8 fe6534979d827cd291cd014ec2ad34942f804c8c6da5c9e2ae732d39c1500644 -hash_to_point b9e6988b85b1938f413838d16409c5b78141aabd2faf879f28992b90c143e885 329639298b058b75a44b6fc6f8dda5ba97133e063312919c85d85d3a97d11db9 -hash_to_point aa502b234a587b9c2828c1c2b62b0d606b86d893c4215b517c0a4b8b08259fec f7da37a8204ca4bcf442cd76f352ac1fd9d38ee76c694463a36663e355de21b5 -hash_to_point b519548dcca9077641a6d8d6e412b6615701507348f7fdca5d0b5fed8ca95455 0b253907f8da2f319b26f93c3d04ea8eb3018add20a94caf8ac0c661c0a1721b -hash_to_point a1b7e9dc65e2483a000c3bf23e5de8bfb5b3b443e5a690e69ab1c14a818afee1 2209a8b1ea25c95116d31ac3b0c3cc50a609acdf5d77d2bd1936afd836fc483f -hash_to_point 99619ab4a831481823fed7975dbece0b49b04f853a18f2843e996fe619e12d55 634c24ac8171c84c6eca1cb2b743e549147c265fec20d9cdde7d19d9fc438493 -hash_to_point f5300e7a436d683a286af2478ea7353194b00531f4faedf43a4beb4a9c4df181 37c3710b48e2293245ebea181411caed089e5a7a1b9252e506b83f871fc26b43 -hash_to_point f11edca9cd499e7ed4e91600d3d7fd078090bf096b6cda57c9018e38d7203966 df19986c8552253fe928c32860133fccd334dc83e761e2381bdb21b369df12e2 -hash_to_point 1ff730b951a28d6ecb85db38e983725c977613a68beaf98afe0d84fa52f14a72 e422e09f2058ca888b5c6a7c212977d69ad3c18f02cd148dd66fee6f36c61f92 -hash_to_point 86c8bd4f52a0da933f98a161b46097874e00aead37927430d61c8c0bbc0ef72e 6dea133e90805d075f8d3d5fd1827a7c7ef538ff9d80928cd460baf66293becd -hash_to_point 08e73deb3f2cc96ed888d4742431aa3bd7e906c0f6edd9972f7f7b4ae9ffbbab 7a529abf5b3d32323878744938c0593fbe1479144e462b9d3dedc7400357e8ca -hash_to_point 6a06747d9d728c2a3eee04d29ad24b1363971bb994f8a3a5388e937bd5b38148 56ee061b682c724de54bdbde14717ac13ea084d1b0037835b225b8f45fe48ee9 -hash_to_point 7b1b0609398c9ba7a4623b27b004f7f3992e535302ceeb3333c4ab5dd93da980 236c3ee6c70fe0e53acdf4a0e34bb30c63626317d072727ae0f7803dd3fc7dfb -hash_to_point 0d19d4ec0eeae6a2e3171b792bba1994ad26050693a80a3a8dcf43f9048036c1 869685a185a333b9a7fb166ab5a0165a61869f12a0678f3ca65ea2c8e40ccd8f -hash_to_point e2e86d3e75a9e11fe3b252baf89f66f78adea5fd593744ce133fab6abced51d1 fed9fd54e936ebfda5d7a0e53f4db777d8f9ae0c828cb57d2adc7d2a2b97c91a -hash_to_point 5f661d341a09433b56c5123f50b9448a9c65e0fa570e5439040b382f3ffbf6d9 8b2c208dd74235bcdc3dfc02cdbb0fb98667796539b2c4755dd8bb4d3d5030b0 -hash_to_point ca2a2390fc4e9a2456626dee20ac8cc04d26de1f9015f3f6d8187ef4ff3ac19b 1652ce33cc4a918c7348d1de00a528d3c2a32bc083eb7ec1ba0080b905d82d1a -hash_to_point 99247578832dde6397ef482c7a885abc3d315b91d8eb16e267048dec89eb0467 91c3d8c3b890e034d5e991a9ff70e4484465fe100a304516e5f21ccc96dfa3e3 -hash_to_point b5224510122dce5dfabe68b16ab7b1d4f133d3dbac396235965387b8ae5ca824 d1104d34549bc58dc42846ae012f7bd0c7013b036545cfb0a430382f7f47a01d -hash_to_point 3f00cb7a81f7b8fa79f0253a0ec1a3faabc0590c9c39c2617054d1b0377d8c2e f7a7ea514b9a90d422e45b25b0305ece6a1e75ac88485daff5be1d208738614e -hash_to_point 025cfdd3faa9d85b6bc06831c9f7b850cd82a730dd046c33e4a89637c2ddd260 071b21d17ed44a415d602b48421768de258f5b4f74f0c9b051427b11c3f88f2e -hash_to_point 58f48898581d2b262e7faa06dbb20eef8e36907597f4ac911ce7e816c84013a8 3cddccff41876e1afa7297060608d328a14839c4d90338cd630b7775c2f2cf4d -hash_to_point 806e8f05f671649e267e3193ec23b5a86eb79c957c8a00d5d7a4ad7fee8a12d7 ea2aa3321c2f4778574d6506dcb3386d291346d1dcf0921b5795a553c11750b6 -hash_to_point 5ccadf3b1c30b449201c220d295d38627c3bf381b6abff7975b8e2c099399ca8 d0ee1bec1471be7cabadd9bf22f7e1e18ccf2494109806a9f7f8d47b5c1f25ff -hash_to_point 86c2f1377354c0e02ba935ddf224b0b5b4eddce64987447d4398ba16a5fc8186 9bffc8ed0801765bda467c60acfc9dc9c1a4c4cfe461db77dd7a3f15b02a7a7f -hash_to_point 38dac5e3a649197493a75562b962514d40c9cf47c2e3ddc4ae481dd07c1f5c91 dd30a465c4b9754007ede53c294ff1bed78b5f2c677e24fc20e5f999ec342158 -hash_to_point 2d13f9ecf6b50af547739cb4df4c0d4dbdce8fe2dd052b94c64fbdb437a2d6db 02a4ebe5375bf6997199d6d292cad962decc3dd346eac5fbd2d9325f24692f03 -hash_to_point 139b28cc42975a8e7c4545e8ad4a960c20192b4726cf224d0bb6546a93d8677a 2406244a43c2b2b404c2c6787172009047db4b0c4145e865e2fa6e4054a1804b -hash_to_point fb2786195dd95878e866a9716bb2c7bf21b4abdaff41f3e75503f9710725c0ff fbb8efc6b57f635668730d83a8662de2bf71c6d915a8cccf1b1e323a134353a4 -hash_to_point 1d5579f8cb694627d0c7319be388718df49d86454debe9084ed47ba837a7b757 95122521b22c1a534bfe5a5f37b7794809a6ef80a2ffda2b08c5f8e7798279a7 -hash_to_point 706fa20166c83927871fb15c39c303c041dea57b3c5940de8fea2c14a759a201 6a11fb86857f012b708d619c33c5ab4e3176b0d31c955c509b0f3ef841b452e4 -hash_to_point 4c3b65e0618a5cca7d50e9a8a2d24e2376e8adb09611dea1624d422e329639b9 828a1b4b8855c4d0dd384580c43506d90228793d8fb851df81ca55cea3039436 -hash_to_point f57859914da087f548f3f064bf575fe47e9d42178f8e66d896e6f5fe45c656a2 b67246c1567fd16d8ef05e13d06415d8e14a89534971f9309a8cbe18a22f357e -hash_to_point bb680ced036dfb330171ed72972f186dacf3374483fa4e2af6780ba66b2b5574 752546e67b9f37e5f5725cef82ce513857a540199e0b2724a74f7678e41ff634 -hash_to_point c0dc99aafcaae855aa99b17c0d68ca841c03527b76aa2a11c38a561bcda37d0a bda5405bc8ba8d2cd4ed7eb1b42d91249b22733ddab5e6797805363ec61f4246 -hash_to_point ad156f4a7d2e431f23674f91a71ef65b3bd9077176996c4e02970539a23d2aeb 64df1d12fc2202db4efb6439c906c941dd4137b4b19509f9d9486b85d869b001 -hash_to_point fd112c3c63410551032648c025ad89734fbb4ba21bad30f87b91e694ae4c6c4d e2752480ac533acd196e959e58ec36ebe8309853e7cca5a4614ee13ee3f45d06 -hash_to_point 43fbb7440940e6ac095a3a3ed97161ca707269fa0af6014ae191f8586c36478f 8cd31b9c9abc94f6340b5948934eecf41ef2e3846c61ada8cb7bae7f7c0e4981 -hash_to_point 45087bf19e14cc93394ae54caa40d1072f0ae86f90d0101b8a78d63a3be43e8c a0d20a8b9060c1391ec551e3462dc13609780414b1fad278c76b78a7fb431262 -hash_to_point d0474f4413d5f903e6b50fc40ca0af19f89ca31906198c6d7e93722b8016b65f 215bcfe9a30472a94dcd6ab6de708b4c6c768c24c3abe7d22d743f58990333f4 -hash_to_point d649b45f4d10d8c5e216d7fa1e2a6465bf6d729c669e5febe198e6f35ce8c460 1ea5d8b384496d7ce7d707dc8806b967c8d15028b5c35dd9f7089453aab9a121 -hash_to_point adb4c42dbc358b4d0ddadd79446376fd84eda50844a7a34413303d9c94229a3f f80d8b2ef8861ac5266d17d7f8971e5002d55d0d40abbd2279b3a5392bfb701e -hash_to_point 616eaa295ddc0c0755d22046b24198b84ce7a66e06e8778855e399d769e67f0d 2437fa51f8be9a966034c5b0d41fcd628b89ced37eaff2da23d80324f844182f -hash_to_point 63c0c30e3c8a8717d97be9aba79831e6c55e57b0a591d0a4080648e83c6ee538 c5d35a4f975538b004b938d7c25867439934e19e97f17b389187e54a0e6df979 -hash_to_point 855174b59d2dd577d5ece9e22f396c6f7aad0857661f3a925a8aad64288886b4 f055c3cd008d66bdb40bd47fdba474ef59613cbcfe1dd05cb1f3a79ced021ff1 -hash_to_point fc8d2ca37efc34b13aacb89a9376dfcf9621201e8f5ed9876c45b3cf1d6b5ecb cbfaa1628bb1f9c62cb0b197b48110b9793dea19ff863d5ce94e26069f755657 -hash_to_point a1f81ff59073858b6e21ea3339bec8b3d3bc33573ca85026d94e5f898ceecede e3cb0034795557bfd8612e86d87036ba0172ad9688f0afd862709cacd27903e4 -hash_to_point 61841e1c08df81ca6c8e195b2aeb187e27767ac298bb62855b15fc185e901081 bf8f6f0a9c8374da26985d2bb042f8356420aa5de66c82fd5e0f720a50a69c53 -hash_to_point e77667c5b33b1113d47cba4a2766f673c1d5f63a57298a3dca92af92195b354d 6a7827ed9078f343d11a88385f6dcde2180a33a883f957edc22b38f75403bb00 -hash_to_point b3896d6fd82ef85c4e79f96f45ed7b4d66814c37cd2c794da868570adc8ead83 e2327dd5220844fea1bc16ef8ba3519939fd39096eebe2656c80f7ff3b1e809c -hash_to_point 7e5ef26e3e50a8287a1c7ba24d41e88f5f40946881ab0af5e30652105ede7c6c ca4a0c61706d2e1664fe45cec1af5bca9bc7fe165f8562daff10a36345e050be -hash_to_point bc16d30b3e5c8982449cd2a11a46434ac06ad5db8b7e01ba7300d46ab4e00e90 df90c53a700adf1c32eac8e82fda53cf7578400e8e128644ca599ab8bb5c1abb -hash_to_point 8b826af84a6adc9902da4bb9653209d3c6293e7d44234f5485a5cb465120c4ae 1d08f00042cfc49eb84ed6bcdf2110f55bc5b27cd0898a54105be4c95170bc53 -hash_to_point e158aa2285103eada1f176e94023514b9cbf0952c70bb256ee6eb63a8e90b81d 060fa005c6693fe84185b14a1228e88bfcdedd90028af9911783696d028cfa3c -hash_to_point 1f6f0a153dd0e6ff9aa7910f29980efefc966fb90f502e524d0aeb0fc345a2b2 09e9f8b8306bd5a0f415e07f65c44c1fc0ce222be23f71e6fdb81be4ecbcc4fa -hash_to_point 8b8cfbb58b830f93c4a16ccec26745e264ef747f8d17fd5a3ef282e8f41e0fc3 1d9c9dc63affb7306452b059aa340c9e84a2a7ebf489a4e8c20af0bc17c6143f -hash_to_point dd583289c05112300dac0de5a5cfd6b1bc67dd12dfbb2ea8b7f6e86388520cac ed0a334b518437b2872bddcce1d48973643d692e8e31717ff82e98c9ec01990e -hash_to_point 15c9305554d18480903da544cfb4dec3b1c663dfeacf3b27d6c88eb78c1f5497 9d50f07c41f18917e8746460ab424fb4eba43b619f05db16c5508d33e7497128 -hash_to_point fbb60a34f061f710ab53e390c268bc308a4715dbdfb74a3914f49d2aca198eeb a79e2f8fdb87d5a5b10cfbacbff4cc3ac8bbe3a6aea52730026b2b5259949b7b -hash_to_point 86a21f24a14ab7796660b1a0c71e13e2c8d087f187080c5551a239861cfa021b fc09351012854b64e62c8708c8541337b034608129e9bd781b0b049c4d0d557a -hash_to_point fa9b331bc4fe993e8c7ccd1a6709fa807c3ca8aa893b42148c5403c9c40fefe4 d30144dad8b951a515343d8349de574227d77f2bf4b7bbe4ebef90459f61e797 -hash_to_point 32d4115ba7970c88d4adf490112d3c72eee4b6c444471de154f92c0ee65384f0 e8d91507b1efd1d067b1155a9634f84e425da791a8ef413d06d537ef334a3150 -hash_to_point 7732f81d4982cbeb7b56b83222cb94b2926ad0a74973889f67b48c6e51bf46df 6dbcc684ac4dc9eba9e72e36059d910e6db1112f6a1588d2e23e6e2ee99ca0bd -hash_to_point 9e2f4135ee6b8743871b042bd7ee90628aa12a2f2a8bc4711799928fea49992d c9b2e79ca5230fa300a3909dfd125ece4114fb06abe3b1d4fb1511af9a46d721 -hash_to_point 973ea6c5b6078104d99c7af23dae9c0e40e8e69c31ccc3d5de492a29c9cc5cde 8e774b86661934cbcc9cd371d09cbc7c547f2225bfce22fcbc38a5d4e289a2ae -hash_to_point c34cff2dc26df0c38127c1e4642133303783aaa57245389791d9026f528c7511 8a363a642084d922a00fc62ffa296ca4913ea0049bdea4cb6ef21e9f51509c42 -hash_to_point 4cbcd8d2affc8aaa418aa4d035fde7beb844bc8267fb89a26f9e461bc558d94a 328f8efa6bea3953680694176211e6baf12e39766e89467a2bb93b3406a2383a -hash_to_point 3dc96daae7372d4266ca1711edab9a356c09e4cf4c8a71cea1e2ed21e063373b 221a5c7fe06f16c42e98b6e3b232058b6cf8be0d44559e76f37108e2d18fa213 -hash_to_point 6e1e6e21304349c0e3a85217602144c9fdc1708aa2259f96c509caf2c3942e44 e40e06de8c20d357b1668a5f025311b44b19ea49e10eae83395f6e96ffd11c34 -hash_to_point 167b7994b46e5711d81a3c970a30a0359c1943a97d1b331c75e31d299c95b21f 77439d6ff0cf2015fd7b74399343539821e37fc806c7eca57fde70ca73525d09 -hash_to_point 477e02c1daad984d3d9a8306c1bbbcdd856ec89286f4ee0e32b6d23228101f81 f1f2cd60dc5cd98477228630edf9bcf375562c2eaab6ec33bba457c3537147a5 -hash_to_point 672e1a4d0f819e81726a532884599c1c9ed2be6076474ab2bfff20634abf1227 3ec6046b788e85c0621e0a13e4e4692f3f360b0067937ad9e87be25e37cea4fc -hash_to_point 60dd975a846cb92fb3a3bddb26369c68aaf50677dd07124500b6f9a18867b1e6 89601d6bd2b35767c70fc84980e70cdb20481f76dd9b52d271e9540b6cb077ff -hash_to_point 0670076c445385726b5b1bbbda9b5b5c65565c965043e7f82bc36ea56d14df3a b6a9ef8e8c029a33d807c8e0b8331d6c7f29f6f6cf8f1d6ceb51027ce933d6f7 -hash_to_point 539a5bfc8932145a2471054fc842ee0475289a5b1ee1a1fa1e6d8dbfbd52ceb9 6773624f44c41ec018ba8c7073c676a58c1340f305132c6bdee587b3d08af6cd -hash_to_point 15ba1965a910317beb48703601bf0d22c8fffff82cae0a3d159d8a94cf8b3a31 2795b0e862a94f69a2ce481a20443e0b62ca8445b1dfd1cf1a4415bb431b17a8 -hash_to_point b46bde188ab538f4a518ca26cd118cf290e893c9f32afa00e35ba278c9fe06ea 452a4417a90a9d324bf537393616172f8b3c3f6d78671573351b076cba6561de -hash_to_point 7090b8b98b30cedd6029069aabe1240450ee7a1f9a35167956cf45414b6d8551 158b2b01f96a17dd66fd0a28d7645a4c695f1b3814f64e1528c0397863468b97 -hash_to_point 3d323d052f1036e7fdb3e9bb215e4de81f23e332b68193872be0b7370c52f934 f09a808c093dda7822829c906d3f85f5358ceae54e49414e1be66de60f44ed31 -hash_to_point 2d2af493d7c8ec8fa0fed011a072aadf27af24e1e56faa0ad4aa23a1d89c4f61 757b32796259aba55f1d3a8b14d0df1c6f8ca8e8b9899d8563dacdb5e89b143a -hash_to_point cb487a9ea905f078b657b453b80394a651150d0880c84f7addb219749c8c0843 f0e21b7197ad61f8de0fa039a4fb4732e005d8c4216c15c45dba4d066101584b -hash_to_point c4fab29801f27798a1e7319f6479c8cda258272aa82c283d613a7aed2c0edd06 deb6df999f31939ed0f7a0b85752b11e18f35a9948ed6b73f5f746a8b86838cd -hash_to_point 13903b9465a009c0677711569a1620745cfd2cb7c3c3cc321cd33d8b61efd70c 99113ad3309a0bbe6063608710d3bd0290e80ad3ca424c813f4eb5f9087a1209 -hash_to_point 7a0ffc90c1392da2fbf861b1584c93e5b93b55d3cc44e269c565fc911b94dcc7 949e5381451f5ca92ee3a7b561f8c50d3f8b42b82101178e26b2205a6aa4246b -hash_to_point a5ec39f6fadf1542beb4294a70a87347103b22a11a3090f04fb5b565e84d66e6 9ef36238a773886f9a4d60d808b7682d4053fcd1a5b4d8e165706aa645db8534 -hash_to_point 559dacec6876590e144ea7c7718b4af4fc8468f87f638aecc5cef086d819fb88 dd64dcb70f5a704e5dbb37b6e87df893ae902f429a444c1721f6c7e747cb8d19 +derive_secret_key f63c961bb5086f07773645716d9013a5169590fd7033a3bc9be571c7442c4c98 798 9db31a41cecb76c2890b198949290f5cd9c20a25b7077090659e0686a159330c 2d749669b193a8139520d84c4f1e289b884883ba2648a8e05543449a30db2302 +derive_secret_key 24b04305886e359cd3e9f2391ac94255e7548e26e3ca18dbc29133db55812ade 172580 359b8606e7754d544c4eb853d5b57f9c1b4343d64e0d8f8b2edcf9b532568609 fdb63f86238b5efdfd5d452df11d6ca2c03d0209b13f796df1858d3b7fd7a001 +derive_secret_key 1c81f110ee07810dc71f8a5d6e51cb616083564ea8dcd83c26c82bd5cb2023cd 18824895 b41355f9972c17d7994c931298caa0d05f05356a5313aa5724a407a04246160d a53f98c614cb11d1db8c3354f5583adb72d9928b47df142e008d5b59ac91f309 +derive_secret_key d90f44dd5575365112615244dd1ceba375bac921cbbf23c3d0eeac2dd34d0b96 333 0355683685b9e0dfe41815b7146fec1d29dc151288442c3327184bf84ce67600 410330f056161f379e1ce881807264bfce230a0572e19e7e5e7d9434afef6d02 +derive_secret_key 0423fe7499db98a08f830570eeb08eae13de23bfa901f1cdeba1aa20fc581418 1745807 bc6eec6eba9d5fd2821832630360962d8b0bdc80d8256ae9698cae8d9fa33f02 6ff7ef4e34ca977ea62ba52d8ce03f6fa596073464c263776d734b952e6a1002 +derive_secret_key 84fc6813164e882ebdebd16885be6117ce84645696f7008c4f894d7de5511a2a 0 fcc2b4e048f14a7634592b0a619174e8f772770f1dc10a5696f159b9bd27a607 2b79592c059ea253b04d0f7511aead15adba30c988fea5443a69bbf2959c210b +derive_secret_key b53d90e0dc67865b5541a8859d827c516fd23d54f21c80c46854f3ee127e2b9a 3463 25528262c2434079bc2832173cbec57a5d05ff381a091ce30fe86c2212b08c03 00e3740529be1c613719cbf9e357b83063d4f3a6f662ef72ff0636c02e732806 +derive_secret_key 8738c4a00195455d89d01eb7a7d49ccf6755f89a1aa17fa50aaa867be8947260 15 1c6c4bbf34d33dfffc173a8b7525d0d566a53b7f37fd9a77e1a20b3580f59708 ca18d37a649db60c7f6b3d86f648beeeca11b0ae316d1e0791b1e4e0846f3d0b +underive_public_key aac614b4d96328e7980b83af47be3d64d3a46b45e6130ce1de797157b86ad0c0 482 f56d5985ef4159d6e3550243a1cce0c4b9e108b54de8f833c17a6b7f001eb52c true c930e39fbbb08a51e207c4e81e9fb14074abe5df150c08f6d5c4ba87e7690714 +underive_public_key 0c0051dabf63e697775b4483398543b5483410a713390e05890ff569393032e5 56 ab14099cebe630c8dccfc2eb28e5ab9ea10aaf52dd2f13d51325703873a169dd true 19396b99e74cdb955c93e57e89ab36364b83c1f66cd34684a41fbab691a5cb68 +underive_public_key ba6454bb2602ff83bad30555cca5d3285acfaa306ee685c2da1792b421ded597 86047760 7e51ae564d369cfe59b3b1c718735bf4be01c9b7839694932a8c8a99dbb27790 true 5dfc472ecf9b05eb30a41eb95b232c4bc3339b3f38e3a50815a9387117d24c72 +underive_public_key ce4e6c360b79686208c392a757929c199c3f7a5756496e45f1f8d860abf21d6a 198147 5c784b021529b1fb88baba4d28535e91c30c646e743ed491ca465d84bb9a85f9 true b71c5cc372d524ffcd2dcba58c6a3e9ab4a4395d8215141c11e35df1c8c71d00 +underive_public_key 8956015ba3152b587223aebdb45cd024f7be653711e0c035d678069500ac64ec 10321726 146c9f950f92512001f0413f027a8259c3a1a0f2a3038a4004732ea3ab9384d0 true c17752d0e580cb493252b4ce449a4c9b568273781bba4a31a4bbc600cbbfa7b8 +underive_public_key 015f4c71bf7e0f9190d5e38af75e47dddcad530b44b84e0c17aedf53dc12c463 440 ceb87dec7a0e62f0f5ce9f17862d1f29985a5ff03053490e73507494bb9ef6b4 true 01a32a65389913fadff27b570d2874ca68bc2af0a636c820d304c7bda57cb84b +underive_public_key 4b15d9954642c6fa1620e1a61f3cc5c41322de032a1f3517c23315c99191f8e1 12249 4cecf1c487de6f401b7438496626d7b54876297a73b2e7179e7b6b424ae00816 true 87060f85b350646eccf0f3d93ddcee70ab33f0731d512113ead5753aaf1fee01 +underive_public_key 01241ba09a9b73548ed5de81902075f2d3028c5f028679fb1dad47db74fd8018 11322 6d204e075ab023dce3be9bd6f01039ce88819622abf7616fd072535f3f310d28 true fb0beb58e94951a016f7cd14f00a43f48e8a2d4a0bacb31e7965368bb7fb6027 +underive_public_key 2da945915a8ab5d288a2d1052803a09ba28862be1315cf6f79add04b7671bbd7 2 94c27204dc237860735f8c821290ecd0886ccc2468bab7b043011c15f5fa29f5 true dda993ebeb678523974fff2c1e9ac8a7f209fe3bac0609618b71f9221defeead +underive_public_key 6a12c2f8ffea1d5c4e363c8ed407a3a0081528a9b828f2629fcc262b909876a5 158 52fb9be59d1dcb73fa1795fac83ccb977fc7c50f4c2ce121fa75540b59003875 true b36e7575385fe91c30c5a0ed785e7f488d38cb65f6665128ceab43cf8c89381c +underive_public_key 4041ff2f1434b8a0c01c547f106140a1c35210e79223b2c349f5edfd6339cc4c 6841 d3589e5895e0570d1930f919365591db83e42343a638dba6fb5d78e1b9c11e23 true 43b2c83c1af6c03115e9f54ef20e16019e2e8c452da884c9265452cf8da94e58 +underive_public_key 6425b432c77deac3ad098b93ad73f14e21ef45eaa40c537783a95bb5d05591d8 6063677 b96b711d1a069f9472a3ef6335c9881d5ac8e3b3ea55feb4971e35de833b6e9c true b8cd372e8f8c0f3f710c6a140c5f72517cbc9a7a2b9dd9e64b78c50493fced4a +underive_public_key 1782be4d81041a09d0cf077590b777b0a252f2a7435ebde6256ee9b8f0622e64 23 640dc5c3c7ce723f3053358dee0bcfea557af568bdcf0b2ec112c01eebe38663 true 714f4e1434fe04c27bc3e58344036673d0a4142953ecc2823f92f62ef186898e +underive_public_key 90bfc110a7bb6bdfbf78fe78cc69a56b410ec0a64854876db7026aa5cbdf5a20 0 50193e2ec7d5ff41c478e830b8404d0a37693b0b0af77bc4267dcd29b9d33dc1 true 5a8d42a25cffbc4d13f9332f3636f573806ed4213a9b0e0b228214f4a385e40d +underive_public_key f3911eb8e173aea37855795290299410a0c5ff3782aba9c4741f1695b42091cd 9255554 3ed03a77dc1c80776738c2548e6dd77c1647813ce6ed531692c630abb47aacd8 true 5c6b9ecb28e069551a1755e6cc71820ae76392d2d577794a5d7c20995dd33d5c +underive_public_key e96a52d585f01f69178d816a4dd1f0a76c5cc030b70e7a51a6d15f60fa4d676b 0 dd196a4fadbd136b3f90003770d93d9fd1c2ef61f75ff30dfdc66706a9f259e4 true f2006f136aca6cc937534a90e3b1205772271260d0e5a43721a1be63fecf6258 +underive_public_key 63bbc7331358f6902452fd38b3c156eda0429935c0aa5b9bc6e09d6cfcba26c8 1763923 274faf39fe975d40e98baf18dd0857d68d2ebe7ba5dbcdee9906714f69322651 true 42e879a222a33d8f94c78ebc21d3f81e1df017e506421071d73f1640f284d74a +underive_public_key 7e8767f103f044829547e4905e3ed014ea897b2823e274f5eee176d5e808232c 2000808802 7076bd1a90450e80e77cb1aca6a5f08e2320128b704b663402759d01fcd8033b true 2fc44a1d0f3deeb2c35a82a8e691b44282ec613697003b79e6feef947beda071 +underive_public_key bdaae6d6a44734e5f5497fb35179400feda56c72f321867da84116e7f1c3dc92 25 63118d9552a93a4b81e84967f31fdce031afa0da3b65f812637950845e6cd217 true ade21ff1d7a72b50b7a83166271e1bc6adfadce86e584c71c4741678e51dff5b +underive_public_key 8b04f3c015e4c44ce61932196032729b1593ce0ee6f4c3a86923340d2fa9f8c1 1 f64a0f59dd9e7dc677e132561d95b7c0d159a3d19b4e6721b034d7a072cfe814 true 3b753cbfa4a14620e3d3a726b17b05ab0b4a2afe2d61c4a8e5a209794705ad4e +underive_public_key cffff8480c6a28bc7882f3fc0975fc5f432ed8859daf15bc2995ae24e188ff3f 434218310 ceb0187b29b15466d86497957d982e998797be1f4ce4c349e892bc83acadda0d true 5a3bfcd66c5c9cc8dd3a34c06fec9c6bf18a7bdea6a5dedccc014f3cb3072c96 +underive_public_key c199c2744fd900841968e29643e2ef46d41de87346a12940daecaa940ffef48a 173483364 bdb618a666ae229ff80e31b598b5e434a033d667a067dc7c156f227b2f920e9c true 878d6aea28e3a870b556674c051440a7d799f8a995298774d5034f69046a2a78 +underive_public_key 2466c670d041a63cbc50e1c0ac69cfe77c33ae236aeca5f27ea2b57b73ad206a 7229706 55fb39977d3f6bf69aeb779c0ffece06550465c3078a63a09fe508b81e426d40 true a48c1ff7bbf8a4daf9569cbf8b8cac1c917d3a41b01128653ff40be3f786744b +underive_public_key f520dcdc1d75e502d20a93ce29287aa3176c1c3ac0bc50e915dafec31cb4bb6f 1712085712 06881afdfebb2d416e246046aba47e086c5400dba3b38fbe39e01de4ce828bf4 true 35d119822bff90d52aa966dae27dae1063dcfcd063826420970d3dfdaf4b75e5 +underive_public_key 694291abeb20c559d983d7aba48bcdb71518fa4717868cec05b3aa66f759fe1b 19605 0fa8d50a043e57a40cfeb73f84697ce818d5522c62c55e49d6068be12b2ac190 true 27c210bf0b959ab0a8c3da6d3d14cb634865fadf297322596c8c2eaf2d579bbd +underive_public_key 9b7cdecad34280332adf408a7b909d0cece15254485dfb780e1c0857976d714b 55 959efa1c2b8fb94144b4798769569b9c567a384de2cf2eda7c7da6e811cdb432 true 0b56fd4ef8f9ac2430cee7c89edafa89b49114d467e0a17fc83961551345df37 +underive_public_key 9d8702bbed748e8b209ca7023d5fd639c4e57bf7f842be4357aa9c200ebe094a 78919 4fdf6991e1dde98644b6690c9bc7fed809f26b4b64e5fe738f7559c43ec48e80 true e6a2954c65c24226249e62c27d53a77e4103670bbc640bbad7a330db4c689eca +underive_public_key 3255d3e32efbce4dd950a7a93ebef0f945555a3f24ee6906d666ca8217151e14 32462918 af7e9207aca938d2b1f4daae808f1605e03ef4658b744a386a2defd4d0d0b60c true 64336a0399b116ae463c9959e3a6713d46906c574a32f5cdc64d5c58cb8416b4 +underive_public_key a6f0f90c87bd3e11e61edd1d4baa50d8e9b27baeef3230a77a63c589dde17b5d 101 66584d9323ae17789518a5a4f6a72352d25249173c3ba083eadabc2e08a0b6da true 9163f03d3ee51453c8183643a9ad5cd073cf00465fe209ff6c671e6e408a7bb6 +underive_public_key 2a280754d202962dd4776173b89cddc89b3f595b8cb5e2ae1c7bbb87ce3a08cf 13415668 911b0a2cc9d38fcf86c2159b11232d9632280d978e5c7f75ac1c1d1832d78136 true f206ec27e09e737e745f4e0d2ca890f73ee883a8275bd2fa91f3da5b74133d38 +underive_public_key ae9459b070982eb03e91fb4b686f17e60be644127d2faf9bbc7c11b6633301d4 252574329 593c5ef347e69e1e5645523b337e0181c87ad6c4cda6a61f83714acc3d893ee8 true 7b77e446a43659b3d72254dd58b124c9a70675d0efd3c17019933f0fb239e1bb +underive_public_key 23f1dd140f9fc682e2892aec852bf483d8e842aefc54efaa3fcdda59130d7923 76217442 d94e7cd314e4a6962a3e13f8f827037ee51cad4c01f6c5debde59c70fb3df24b true b8ccded2b6a1c967c6ed3bd167e7aab419732c0841bb6b60896a21dcf596922e +underive_public_key 2a0eae221fcc30a66ac10a5fdbb99b8ebb610791ce4afc7f79b81c760c710063 536860479 17794ecf123aa2c7a0fed0911ebc06b8ed642c8615c0770c0354f5967986983f true 60dc9285a8448f4901024b1d21f3b4e6b2ffdeae02beff3fbdc84a28978a76ae +underive_public_key bc48a3164bfd26b357b80447d32e86b2be34dc1e57bf379ba6e928364c2a081c 2137227414 2aef35c201438f485f42c9f17b722a101fb9445ae3f259165c3a7cf3cbe66022 true 7c9da1e6d93eb700dfa4bfc36ef836ef2354dacb70777e6cc6947f781c65a15b +underive_public_key 5754812f49b9610c65879c238f9e6ea6f8b4f1a6a53f1f6bb713aee616c5364d 875 4ef4a103fc7a7ddd1aca7f2ff57299f0aaff1aaf0d1982ae10e5805d3a471dc1 true b888517e1cd126b949256bb48ce4e0dc6bdf3319e111c620d4f94f11bbb0c9af +underive_public_key 9c7eb0092d945b0818072c71ad531b21146a288ca819b28995aa87b858f9c437 32 e23e08d371c2358d3c508ee1ae76524516086567dec928c3c1168646667cafd1 true a7d91d3ea2049300974f3164caa47eaab0b5b0426377af541df7e05d7efffa42 +underive_public_key 138ade5eb2d8557f41a2ed3ccc53bd36e2d40ce37c686a1800cc4a226d668cd7 53215 916f075ab5e1c37a43d92680bba9570db40b662ed6754916c8cf296fad950fee true e726f8b5d370259a2fa3186003e107720a546dd8b9ed1b59d3ca36f9355ae136 +underive_public_key ce6377786405d288ab6f38ff83a74e2108461fb3ac0de8bd5a7ec162fe1602bc 46951 49b4a73940d601f5ecd96fc6c687890ea7544defce0a9bb8d1d5c502ea92eec3 true af9827383cb5112b44b8cff89b437d90a7a87564151a28d1cb4383e3273e01db +underive_public_key c34e6b864433a4c8549680f138899c669fe514a9e6968f028ea6f366b8ae62f7 108177 48ad3899cf037c02af6c77c278c2bdf23742eb1cd3aa5b2796b04ccbd3ecb7ca true 253199b25c90b0eaa5a4e7c22dcca03b7b61b8c7288a09502145a14196bcadc0 +underive_public_key 3182d12bce035edcc049e6eb7226977e914c22b406939c83efd32e2d42359c9e 2480 7bc6c3f3f9e0ddb2a1fb41e0b348574dd9ff1a047dedc0fe911cdd5b2da34806 true 13daf94ae026d331699225695f9229015db4b04f465092382beecfdb8adfb568 +underive_public_key aced816cb7246c35bac9ecb989b5c404bf94fe60ac5568b5f32cbf6ea5ad5f13 123839 69cd9f17ae986c7bb07c0ee12efe2aca99e5e2b7ef49443c5cec721882cb0bf4 true 891c42eac0cfd2246a6534eb2218f8f32d7ea9bf1501aac66cea9dfc407d68cb +underive_public_key a3530ee2564f40ba1b39db64678a8d238a9c0159cdad8442bfe771e5a153aba3 15 27df566a9594a9f81189ebede42ec8b34c7babd15b2d22e7b17ac8118bb391c5 true d0f7496bb31b06093d7d0c41cf59616d261bd9b50e7fe00c78e4937ed96ef59c +underive_public_key f7ec0c7c43ab16553eb793d5488ff3170833ccd1e71d03cf19ab214685f348fa 158 a05b09aae61339333afb339f88aba505508a280cb18d74a38d49dfae2a1173f8 true 772a8ba9d42b3e2b72b3a23547e60892a502bbb0a7ec0122b4c681e2eed2a0f8 +underive_public_key 3b199bf2a20dc5bf53e33cdeb81bf9b7c2a79c61481f81bf2d4bb080cf1d185f 489 a2dbe4069a5438456462c26e07afe871380ac05bcc743a36a06a2acbd2f44e84 true 9611fd05572e60c04f49e92d10fc8c201ab10b0552fc53673521ec1633104e16 +underive_public_key a1c7067262088dd8312a37e8c9dbd3f98fd5f6f876475bb2ade8fc1c5b1fd9c7 0 0057ccdf7e6794e4581f83ecb923a8f47746f0cf1a16124605ab7a9cfef7fe6b true 861363372d65348f710287a79c71e5bff7ad551a0d15189ed0bb3a27bea7ffd0 +underive_public_key 8051431180a10308ba3a1946230fe94ec295d9c464c36a0447dbdff1aab0f7be 102055 544434f7b8855a9602f5faadbef9a3d791500ac764ddee99b36aaf6395641009 true ac5f13e98c4ecf9d542f5b4a1c8f2d6d8dd8ce252cb0898f89a3deb71568c4f9 +underive_public_key 5e7f60028bec5de7a61964fccfc46ff33c9bb186918dfacf99935088f0b412b5 19 5d86f5f6e3e3fc1419e47cf11606a523d41a371eb7a328c56d058595806c9dbd true c90ad149c04b62da67d978da190e7eacb70f70ef0230b1c9b94a45cc654dcdac +underive_public_key a8aacbef9409e11b45f06609fdc4da1e921d7eedcc35f5910844a1f1637a49ad 11251 1c31b74b13dd60b4ae30f8146fdc6a54eb8018493742413dfd267f18bf0d21b3 true dc787a1c0fb3d52ca297784e168db3c308bbde53e999e5940911d554767a8665 +underive_public_key 80dcba1893078d77d8b32733f165a25d2ba01370d30f36f0b1abb7314a38d1a3 14581052 6737c36eaa00e94484b079bbbbeb9649972a71ec37b2b8eacc50f4b65fb16faa true 506b79f188ccc21fc9905f36bd99c76836214693c3709c5ec65e0a3ea0c30d2b +underive_public_key 1cb048e60005af589ad9348573fd6e82568654d668432124687eefa445432136 27 f57216ecba1a219464cf16300f0dd3615e7e06fc85d76b59e33ad0d994b207d7 true 907878c9c40627179864db7dd87d5f94f32a72d7610bf1ded02fc93eb6f4f74c +underive_public_key 178dd0c2b44c11e10cb17ce4e96080a0510acaa5de55105ceb2db5087893147d 4 157d2e5b23849549681ebff7c210c0c9221c7c49318043e4c4c6eb7bc820cf80 true 07a8d76c3b62b5310ee696b17e7c33852826e39c77cfd49fb9a857c1e2b3c179 +underive_public_key 2d89bcd53c73189610960e0397572bd45e3f86cfff69870426fb9bb28f6d176d 52 c9863c51bf3421d4eff8724fdfa111ec61a29345a9dfb48b65660be4e0156932 true f1408856659356de31e7ce7b93428bcdda42acdc9260863fc57742126c7c9196 +underive_public_key 09ba1c5528680f55124f6b6facfa7b80cb71d8cf1362d08cf1170d32d9881229 251540 5012fcd15fb1dadd6e07dfb63b082cb6a9aae353941d8721f73543526b33ef74 true f5323a6fcd2a4ac086dab5f9361e1945ec283666b5f0ebcc9e9aa8b1a7265d13 +underive_public_key d43d4e6291bd26da50d592de0ee28aef5ca32aa990eae5eb4652a1b6a1f26783 120677261 ddc88eba9e472be31be6dcdbe2b12d9b778da426d428e6459fd9be05494365ac true 8da911a8eadcf14ffaad1a0c6126089310215f920c22a5c4d2878c882b311ef0 +underive_public_key e42cae0c3b87df52b5dfb9ed4f566c72ebf05565afc73a88fdebafaba87dd585 2 0cfea2ed43fff8ae63827898be2ca2769ecb16c3759f1f3a7f32f72015b7a616 true 1a4deafbcaa67802173296f7ae1ebcadf9a8a3ac7a9501984462101aec9aab37 +underive_public_key 0c191569e5866a7494c1ae8bd41cf5e65c5b8bcf2922629692dd26e4b1e0394d 386 97a72250c15d3c0561cb6ec1dc100f1febd22486f1d51a54757c83c44f443d84 true 27bde6cd807ffcbdb57270a074775a70f7e58f1b123a5e6a78ac35abcd556627 +underive_public_key 6707e06dcc13a22493db6d0b0c88775c62e8187f216da0c992208f3f7afde4e5 92389509 2d0119071163227af1912a566da879c254cb2cf93a5c33f9f0876862a101e83a true b75ddccd90ddf3c44cea5140bdafc58ca1a4516387408f70fbe863f6b53ae2eb +underive_public_key c5f7e978ecdde2fb7f6da26cd275f80235690ae2bdac0033395df15dd8e7a6fa 889255 39fe675be94c160bcec10e60d4ab1a158200b745e929cfd6b82e995ec9b51904 true 50ab79ab9e93e70d6f29d9598aa1fbafa04e4be594231f5b2640438cbdf40c85 +underive_public_key 199e7fa401aabc6fe084465153f88bdd99728bea8cc159802f115044dc19a054 431122 96c4e8812a88f4583f22e256d228dc83f40b2962fbe84593d206d921c2f2e485 true 028c073e6645e302e871a3ae028cbc6000c7d4e6b95e9c3380ced7b2a8e0bd78 +underive_public_key bd05761461fe8153cd6e9ea9eabc9d835faa687969fe7128ff00f7e2df7424ed 761 9359bb7b6c407ca7e6d5bb26bb269fb1d1006e8d948a36b460f89d8871df12c7 true 1067243c06fc7dc3b4a066fc60d2a4cc1f4da06ea6e1324c0c91ee4ebd7daf44 +underive_public_key f69a081664900d0c3b174bc24162ea0403742a932bf369f028c8c7ffd6281797 1 c5e5e381ddb9d1ae5990bd261f57d924bdd5adb58dbd67c50a36bdb848c3b479 true 84083d0cbf747a208b64d134e11bf1fd4c7cf4f7f4a64ea002af799c37c3fdaa +underive_public_key 97d2b486dd0355629b114abb82147e0143469e1052acf963b3da7be596da29ee 0 c6f9787d2415d5f649e191a7b9e5546fb597e9c5fe5a224c523c1b6951278da4 true 0a66929be0f82ae0b4f4695d08f22f56a7ce44cd29340dae42721c41b5b2925f +underive_public_key b7b0e158168553d512a694935e2c5a267c976eae01cae1ecd41e18215b5d58ee 430466 ed42ba330391c9090988da0deb6d9070d3799095a42e03c2037ac2f6b82d7e32 true 59b3bb14d63e638549b23ca524735a3fffdd97b2939a6ecf53a3af6043e4291b +underive_public_key 8fc6a1b0922b3f02ec4b6b890b6a7818d9a5aa5b9a5f56420b8185bfd41cafbd 271 d789f2c4caa42e5b05a61af3fa9c7de849da6695dfae3d2a21e808436a55821d true f65a1222defca378deb0545311074a2111c1c809d732df6eec1f6347578338f8 +underive_public_key 2ebeaa8f1bfd94a16e526f5726b56c81d4701036e3e3d62785459d081e5a8054 10357625 63e4510ae2f3fef2a209a758c699cd35fbd74d5805b161be0b8cd8ea57109067 true 63463ac82953e5484cbd8da3aa335a70841dbda454d1c03fb7cf1851c1c1c383 +underive_public_key 76f041d47b88efe6ef8b817cc54ea54105eb28b7ab8d08000ea69f843481ec37 15554120 e6dd8f267dad22314b01f69577a21412e27db7ffd65b54adbcb916f106037a62 true 93a6a08903d7f0e6fff203576d4af35eae894d2b34de366c972652125397bd9a +underive_public_key 8dbc274834fbf48303d3a91edab977786a1c0ec3fa2da25d67ec96099a4b64da 6403349 68bf4852e4c77ae85444c87c8d76522d57dd2fdca5499865da4b1b093c9f963b true 63bccf6a0324add80d4b5ac81202b7a6dda837aeb9bf0a95d071d49b512b9bec +underive_public_key 2728307c514cb6fb3f687154d0b3a52f9292ca90073fce3dc2301c0969364d3e 68483873 001400d53d5977e03e4dcad49b0e53e09108e663a3b033e5829e44c275364130 true 6d0beefe8aad5ec6fd7575896dfec555ed4aa497fc2ed90582a2ccc7e730ec40 +underive_public_key 674e1c758e22b7349bc8f6f53cb2075275d79305e77c0c5691298db113c42a9e 1646156677 b2eb10e15d1ebd518a736cb485cf66df4eead213c73c48c6b29efd75eded51c7 true 5ca5d57280e9a83450a47b9f284c895e9f28c37276e13063732d185e13fb2dae +underive_public_key 4f656a73b5577ddadfb533757093f257787cc9ca91dc9c8aaa2f5ffe1332bd6d 54 6d1bf2cf7fce82167deb85e5116cc1f6fc3c5798d6a6b850194b6255bb2ed892 true 251ee4bd305462f61944e955a71c71983fbb56ec9c329777adfc69f8a63216d0 +underive_public_key a704d78e3cb01ad42128cf83691d72b32e4fa89482b429aeed9acd7f3f500070 54900125 b6dffd89a715681f42d9bd7c2e826ab0def1c24cb7dc2305ca1588835bb1c1cf true c280b2710609f0289891c3829132a115fbc9dfa964dc1256c5e39dc18750b379 +underive_public_key f33823f627e3bf07d160c9bcbe620949165fbdc44bcb44ac9bb4d26c88bf5709 1560140 d802cc1de4dfd4ecdc37a24191593adab7004a756b96d615920a03164c5dec64 true 93eaa2ff6c227d9c8a501dce0862f15214efedc2feaf2ea063c839a0bece8da1 +underive_public_key 815f4e460c445489806d19779d93856de0daef2d76c251c5c77e758253957b21 491335 488c73300220c620d6b44c74fe16970584cfc4e8eee3a87a59ed379d2d30ef3e true 042e48dabe397ff96e46c3ffc67f1b350490a6ac3e55bfeaf68d7a2064be08a8 +underive_public_key 806f41475cb412fb3c030cf621ed5752ed0a6d8b51abf51a7746520cc2ecdcbc 24739 f55fc3397dc37fb9bea5fae9c7c22c5f02c741da62bbf7bb6030856631197b2d true 235f30378882a07f000868476f540a2f996040b38dd3aa7af23ee2cc46d7f1be +underive_public_key 6ce251adf81670999c89e2b346e535857fd22a07c1d4317b4b6c6be6f25c2ecd 252 7675b256c18a26d1798cf97b67acb04e3d8b877ce4c32882e5dc5ad830597d17 true 86405afc5d389d47be972253fbb03fbf679678d95e1bbe78d592032ca5ace3ce +underive_public_key 4d9e701a1025a80216d3630238f726a82631498a6ef2709df5b72fa5ecab2caa 68 ff03cb02b2ca32f3f0e7976b3887411ef55c80c9ed69a7c57c5dfa6308ba1d9e true 792903012f7c5d7531aeda46b6b43856b0eab5da4d3a2b56f435dcc0d5d24b04 +underive_public_key 5f0e5aa6e9dc799ed9afe93739e5288a97abaf307a4932ef8f15539d70e8721d 141 20215da76bd9531afcfd9bffe90f3956e68b4cdd6473ec2a0575fc5972adfe82 true 9d448d86f9fc51366a135eb224d151279e5fe85d7b132cbdb583f55988a14391 +underive_public_key 0483a76acdec46eb1ed6181174875debe9f7e3634e6357a7122b1ae8d517f4d5 2861 219843fb6f16194ba6236b4cda166dc80c49a91d44ca58b871726b04a096b4fc true f2b8fb6680c9ec207d2958f47ca4409fa9c3bbd26d7defb96e7904907ef32968 +underive_public_key 62e5bad372746ef2bb86105860814519674aac1b47f755091164937fbf91fa1e 12823549 46391ebcb33fa53f30885e068e84f73771d04f90fe7542ab7657f71c82bafff7 true e745bbcf06f0b1cfc5cbdb016fcbc0dae382dbcd2bed5e9aab010293535f31a4 +underive_public_key 3a4ea847b70da1538cfd177a3d76f8c6d9a06fbfa94ad95dffff000cbc6aaac9 45 3d78f75f8798d84805bf94c7302bb829229e48a568d842bb6e1f9e7826427b3b true 114b379f5c7e27417b4f3e23ccf6dc71fbc18429f6c89c54eec0578b2fcca4ce +underive_public_key a4cb99cd667a11952e5ffbfc8f3ae66419f7da9ed5284b940258cee9eab4f496 25 d7d34cf0e745d7f5103237107bc0865d49f6115fd7d162d4f9ee45e1d010c6ad true 203a7dd2360e785dbeeb00970842ca8099f787e83b1347b81a0f9a1dd796faf8 +underive_public_key 219433ecdd3c2b48a6d849fae1366bfda19c4cce78b0c9f452e2ed5f35f2bb50 2164230 480631c9a626a3cd8be5938043a417c82b1a22c349628bed904b53beadd89bdf true 31d144eca5a3c9d14487813f7002559973d50d6095aa93eba392729f4b3b2b04 +underive_public_key 3408b47189d2612719fe94cb174a4452ad0a1a6f365b08d2bccfcebdf26e133f 1104 e891ba27235e9c791ba230013631f38a006f7a6c9a304b8f14a0c0958d0e479d true 6f65e61807ea15371001751cb57308fe6560756ea53196fe06fd73c44b633008 +underive_public_key b97d63e2efe4fc1c25dcd3663ef7ef67125fd311ccd3750bfca892ef4637e4e9 253148 1a6638f22b0eeb6fe4efd6d84904ce74e70bea2a3f070c239c1906d9c424f153 true d2f78261f85d042215c09f535c69e83a690d12c8954c6de95abb9f1891195da2 +underive_public_key 8e123cd8e396a3e7cd958cb1d6349a4d4ea277f0dbd73b450b319f222f5d1d03 17 1100bbcc3ae12a412dc3523c172f1b5731105beb3590586a4109ce50fd3ce05e true e436e677a8646f753b54bab3c97428af2ee418f4e58a9ebafbd473320026b4c4 +underive_public_key 4dd180cccf0bc2f82e95ff82f5c618056b81b918ea8106a7c20677f1d3acdd6d 2438001 9f2f604d4752df7498497ac42483990e2919a820003979fc13fd14039b037dcf true 5a346a7d2cb7c579473c256a968377aae6c676ded415960ec1262f0d16dc24e0 +underive_public_key 08d06d9dd1fa5a9837660d61153533935cfdcb08f4cbb9dad6e5417a71eff6e5 5547 30b81d8e2b6c401e040e81588fd3e3052ca5f2cf66b4e277d00778e7e5e89fd5 true e8857694d3cb3d38beeb933571854fa16bb3dbba41fd0bc18a3f17e6b511872c +underive_public_key d33e727b58be3c8bcbaaf2e8c24919a66fd2aba7ed765ad2851a402012dfb978 2 db65398da8ad7c93081769825ade98e1486c5954ccaa2d28b6a47efb979b34f9 true 75e14e927509a27c26abd93a7289b7c9ba52e656f35154531305c4d089a6b15e +underive_public_key 373afd530d194d901b41e61d3d02a4b1c2fdf0ce16ba8c2990e9dd3c9c7fcb76 577724071 adbf07e3dd21a37f2b5f9c371e325d3433c38e8f2c98d6e2abed189812013603 true 77d2bae0deaa77a0a342e295b26d658b12f3500f75e89b26e01b2bde00741862 +underive_public_key a291c56a8cdbb578287ecb81eafb895ac9eb5c5ec3026aa9bb3876f0f8acbfd5 3365138 5f2195bb9ca88aa8332f57137c00740f305ee47800bd5741832c1d11cf0f4aa5 true 1324cc398197c4e068328685750fd2973be4c9e0fe2ef9fa9fbbb5cf451118ba +underive_public_key a9762d1dbdb3e29f44188f4e5a37ad6c80477b2bf0bd416d30eb05423b4dec85 5787892 6cbe0a9d62146d5bb8b265d5eff3b16be1812c227784ad828eaa0073135a3404 true 5d866f1de290151082efd7480a93cf2c6fc309200e2a48e7e4ba417f8058dda6 +underive_public_key 5b6789f4bd93243b550bb8c3b422816ad7710dc5fefe19987662fad6ed37af7b 7479574 36d76e26c1981a456ca30941268483a8a020b310bf5929ffa607543a1c3eb3d6 true d8aa028c92171a6db3cd9f02306db2d5b777beebccc4a30ec123c50717beaf99 +underive_public_key a470b6a0931cf4a715495be29c1cd117f8ade8c73cb8219d3f2151b1eea6a31e 1258917 b6aea56abdaa94f32f7e3c1d4869deef3837e97a3c34fabebb1f4e66f24bc95f true 3df1d45c5b03f8649df6087f055fed0d3d242266a9061cdedd7c5e1a0b883bfd +underive_public_key a843d2e840988be32b797ebd6d5b9ab5e388304f6dd4a9093c712bc35eef4d2a 573 26ea0dec7b343292fbe39cc7b8d0edf49b2d97fc7cdfbdd7a28239bc2174e333 true b54e6835c0be2967788d3d09cbab3704a5363207d1863ea27f5ea451b966258d +underive_public_key 1c24083e3b7d9b0037a0188d0a87e28dbf904053369e0ed755f4f76b0959b399 0 d92389e93e50a9401c903d2282de4883b77425a738b86670ee4dcd24dcd61fe2 true 5b29d7e146d7d191fdb8b0603a07543cab81ff5f28bc0a429a440516837e1684 +underive_public_key 222db80465c09b7eebb3a75dfa3d3a13861c01258be6d450df8f6d4c8bbaa154 868230 474660679cfa64169dcb0ff681d98b324889848ef8937b5e116b96bed9a71712 true 7fc4257c87de34de1635788f3cff1e65f1cac94e97f1b710a333e442faed1b39 +underive_public_key 4dd8439268d33cffc22d7dd976341143b4e373095a3b85c1cfb1da7381e4a30b 65175977 4ed93a357834153c0739553a490d64ddcbe4b26902e6ed837b1179d3e522629e true c74607ef82d4cb7c44d94fa6183ae3cdae6dba1f3e9736820158ce42eb7d1198 +underive_public_key ec86d2fa7cdc478ab0e95e8b2bb8de1901a0d4a6efe453b2fcc97e5a7840872b 544798145 7e49cfd4012656253899eaebfb1627fbd9e9834104ee8c5d103c35d737bfd5af true c4e2ab09c95ac42e5739ff9d463f27c511b51950499d3c7ac651761ece813b76 +underive_public_key 2b7295feeffa5b734f688a88012de84c82b730254894ac63554dfc075e0ad721 102694443 c2a02a8f79ea1b4cb934f059f1610603ea6dc3e1534a10ad30ccf67d7c317d76 true 05a7364abd44c61e75a19da5ef69caa0adb05108fe25979112beb326fa167d28 +underive_public_key 6035e11179cbd116760945b67cf878b1f245031eab58f5cd504a74ad9d1be67e 14647539 cb1a61b387d80215513daca214a8ad997f61fc2397cb41018eaa3f195423772b true 0b58a908f5d57759132343c96cd305d3eabae5d6f27488e1d111a553f7bf0d44 +underive_public_key b03c9b9e4514e81d4b16d45d6b3309a7adb4455c3e7eaf3520a4a67321898926 0 1adab9ac37228a79165ec0c95290d91fd67a93a1ed2521420a2d33c3e1ef8176 true 62181ae99529325186e96144c04a0ffe89eb0ad11878a1a9ff095257ad59e1d2 +underive_public_key dd03ac2154cdf46739912cfeb31be0fe2e1bc2fa3228a01b6101ca88ee9e2d5e 133 5448a2d18f95aa6720ea2b7f996bda65e1f6c6e35a45cf4f25c8a6324da5b0ff true 41767d364de089de49d267d6cf2aba43599f1963d06407d9e24db83fd6102608 +underive_public_key 79110dbef02d60bc8fda5c2ce8980d7223b57dd8d8122fa3bbcd42e03cc635df 552 1e05f690097551bd1784c4c499d144255f5cbc96f05d1f2f077891905ff21c86 true 2a6ce36a89b04523a5d1b293e4eb41d78651da4304139b0be4df5c80c752f5c6 +underive_public_key 806a5b6f092fddc586460f657739c6dce5f4a2a3c267e13cd1b01efd4e3fa387 1557336964 99daa125e6ec326437d10dbcbc3f2767496a05a4049a241de199a874f26697ce true 8f178844d99189bc1ae933657d846a3ea5bb0ee2771f902e264aa9a13948125d +underive_public_key ee68a48bce8668b6e8b1e072b75a656e5d18d31226bd73f9f5e9a90e956bd6cd 952 e8325511ceb9aa041f32aac98a5dc8cfb06a85c98853b25cdfc4705768666af9 true ddf6a8c08ee3d7d6458ab2f4eb063ba74edc5b13b9f3531ffaad617e0cdb539b +underive_public_key a628d5db825b7bce8114b2f3402ae6250d8ce7e2082bf07b54a3c9ada500b822 284042 b9c69ef149f4d459f67963c15e1692757cf9c6d8d791d06ba0609f6c05aa1768 true d125f0f0c115a7fb666f8d5cb80704b91d65bdd73c777a2dcd09fd93b7e5edf2 +underive_public_key 38703b0850ff701eea97c3a979a2c2c877cecfab56b98ff4eb32f53a100c2ed0 1878 51baee1fbdc30780d33602eeeb0e2cc985e0b1053258948729ee9efc47540adc true 5c3fc7006a89a87b783d04fcec1756baeb4d3d93a9aea525433dc6c7277b4f15 +underive_public_key bf5ec54f01a793827101c21b4e0c777e386470e03ffdec5eab34be74772066e6 0 9cb0b8a2748643c485ca6ae43a5f866bdf06612fbc9c1053f8edede402015dc3 true 541ae13f65bab7a1e7b07c39c4f54229e5cec2a13154e1f70e14bcbd1a41d3ec +underive_public_key 1b3d31e5c9632ac728a2f374c4ee3ea4a3969ddb76b74a3edfb88a7a0ebd45e2 788814 442deef9e99afb9153ee67bd43b61d1c6eb62bbe592d8c92636bd2f38e1aac2a true a8aa2ea61135c907f86f4de25eccf8a91d18d52a7ece5eea789c796c4d18fd49 +underive_public_key fcc583dae7edb8ca27f90a97b5c41057e582f11d1e9597db374d5c517629cf39 15642208 6e71d506346fdfce8c361d6f5d9ad2d4da8ff4558729e85a1cc60ca918d1c2fa true fbcf97e83f94a41dc7796c3e5d48b728b757ba19776057eccc071635677b098c +underive_public_key dcf3140721ba6e8ce8fea0db187e7310dcb8641193ef6720e43ece3e00550079 1 3d53bb333ac5afe09764a82fc433342ec8573fc7b9563ee63312ce7c876a78c4 true ed29a76c9d70718d3cc437b63ecb5b387b6538912925a144451194ce0bf95d0e +underive_public_key b6494fc8c398d55a94d72444185102a861aceb1defe7a0f28f3469f9f93e19a0 443889 8b4ea02c4c415623b7a21b5e5b15e42c71742e553b349ba1b299dda3fac543a4 true 7b3e84893bd52fdd36c4f733468f07689f14530c62f4434ab82cc86f648eb0ea +underive_public_key fc90e9d85ddc8fcd28f23353607ef766de50c8136b248679216ab09dfa8d4a32 3392807984 8fefedc1ac8a1e601cd6b8d3b60d35701d56576aa82feb453775a7ca43946b5f true 399551338c9bf408cb73e3b20f4434ba235cd30a4e07c2553c4cde39c7e94cd1 +underive_public_key c46caea97976d328b18f97d1a692b51e687f425c8c85c796f89439bcd709e588 369353034 7cef3d838bf4f8d729a6377d146e6fc63ee784f9531aefe0d770856c2df7d670 true f3b017cacfb7cb74624298816b81f51f041a65e92b0b6956b75551fcfd512e9b +underive_public_key 7cdf3cd77b9bbfd501b01ad96821443931c8e4f1006a6397dd77ea1e05a87d2b 95811 146a8c301f2fb6ec3f0cdba6ea2ae8192b6c1f948e3a5cb2ff3e06a5ebe991ef true 3dccf7d3984c5fc7cfbfc1c8ae93bd683761ba4f84bbfd04d7fd6eeaa98b8b1f +underive_public_key 561df4b6de126adc3f87d8935fcad2ae9494af19533fc7a06fe00c30fe951419 39090 4b7e2171714f3900b4b8c8d9ea790811a708459683472c9bf6574fbfd6bf4a25 true a7521d15716a7d2c4dd9a27df0644f51011a1f318885254e6891ec9195859a46 +underive_public_key eceee0d49b94f832ff6c026214ece0bb88ce544e6ab200e26a781531c491c3d9 155100 f781fba8efd8d9620c375d96b310f63be1648e316770db9adbbdc7d87df28965 true 0be7848d312fb3db9f2eb6c0ce4742ede6eb22958f5a090f0944c72445e2690b +underive_public_key a2e65e1c045c99d72429b9ac27b2473251c1e7a350068c9cba20d4dbbb159c5b 0 03b8c62a49f521dce51aedeea59500eaf4b2a00764cc174cef35e8be160bbb81 true 7a0c68ab1c006e7e6b12e180670f475aedc47211a2e8e9ceb8f6ebdaf6a2cb2b +underive_public_key 3efdfaae3ff40aea794470d42e43cfdf539002527a2b34d29611b1d5120212b1 436271 cfa22be85521a6516dd48862818df6b8e7bdc41c954bc50a16f818ef4af5f49a true 5e55da0cab6e5d052d40ee7df27053b9d744e0240ea38bd199eb00c79f7c2ef0 +underive_public_key b38389842e15ddfa15084db385f0adde1384d8677863daf0dcf468f0208d6dae 5154 66b3dcf9aa28be6a6fb9cb972f23f5514734931f06b77a9b8b9f3b05cf1fe7c1 true 219621e7a066c14f05ca47551612d7bce3ace1db757c2bba4081a51443994ffb +underive_public_key 8fcdfbf26682ed748f7d8fedc376719e318063cd664c685817281a0df0001118 3514594 1522b6bff2cc5fcd978854e02a218e8a97193b8a62d0035a77dd46a1cc7bfdd9 true a347b409dc7b5e9f5c77481c59e187c703be0337b2788af71d42535fea8cf38b +underive_public_key 896072fd49282494c6a59298692235ed4eb9f68cc05d1943cf4b33bfa4d399f4 22528018 06fbdea185f27db5dbecb41a7af978dc431ea1a4dc670c378870aea268c86129 true 0500ca1b50e9f0c3c08f87f6f2e60446d8593d663d38875807bf24310649622d +underive_public_key ac1eda03fde622c8fceecde1e6ab3ca920c1897b02cdddf3437b2fe5f41d98c2 4 a9ef7047ddc5870471086c36a9561201a76adafba8f6243dd75aa7ef60f55771 true 5afa16ee5cccd33759a7443bb0a020fb6f645da08af0dff6f7247fbe4498564e +underive_public_key 6ed04220e9ae89017a5495b9f777b45cd256eb87cd298ae9d346caef147cbbb9 2976612 3c4beb10e425305f0f23342f1ca9062b27a409dc20bc8862570cf458b4cdda04 true 49f7a75bbd1d57cb120b1ef63da896e0b128311a9a9e44c9bea1f17d59ea7fec +underive_public_key 42836ccde56d5e356997ec7cc83008ab4626099bc14ed50b80255c8031f10cb6 1572545723 c4711cc86248b84b7f5ef1f466fb89a4ceb0b0a32f20bdca3897606b37fa16eb true a275dd679b624a08de5749da63181c2e3559ba515dcbd368e5113e1a01a47b8f +underive_public_key d710f46be883a4cf9f60d0259c56a7288f4d9879698a2e1099af97439c396df9 0 4e791c68194656a17dddd873308499f0cd473744be2458cef4a6bac58a6c5d9b true 507ae4c0d88962f3abd54234fecf779a1f10b72f545922717fd7c6ecc2583135 +underive_public_key b5d06fa7bcb4780476540caa0c77c6e079d3295b2f9aef8cacc942223f6cd34e 20885 6d9144ef0d77db3d5c8872dc9e35e7fc06fd09e2eabce2765c06b8d420734130 true 0230c97f5b1ba80e0549787815906f5beaf972512659a1775fab52b32b44f24f +underive_public_key ea44d2199dff4c0724e626eb28eaa44bb5b5b2c54caa1b66a037ec07a2a4a7e1 2567 0001d9df0789d8f0d4fcb4c83020713008f9960a6718d5a5847c201b1f3d7bfc true 1609eaee61a5a4451f98fc5b8020af3e8ba717691f125a587825b695ef115a44 +underive_public_key 4357a49e1375972206b3bd4bda6ffcd3d99c9671e4c899e97d6cc7290f430748 1992201 dc3c187692e95689c13a2ab3ba5c6d983673b85506451c68a79c9934b52dbd2a true 8f85576f48bf249c5886b87d3bcd6532c3307e0440f295540f8434239029f7ee +underive_public_key 1c45deb8b15b1cc9502f06512ff18728fb67318a457094eb390e84d030e00781 784695945 c69a5ff16ba98a68b2f85e9b85d2ec4c648fdb991a22c716db616ac54171e13d true a6129d5f63064769052458158b1341e72a9c6c690d154f68b04454d89aedd67c +underive_public_key 00b8217c6e2ea386cf315c2421f7ae40b8fcc5f952bd794af7825b467699e3d8 35164855 d062d6764da6ee5fcae947e062c97b9c585a61cd190e23fd27203b054cba85f7 true 05c82c20f71ab01b76ed1bebcd5f58e937ead45ac5c0f66711c498992cac9633 +underive_public_key 8b7b4bee69cc221da0577f5c06193c3dc223db1932206ed43a8b9da02551966a 65620 6b75b7b22232c6af68c5e0e0a1d97e19628730750589a954c02102545c18f7bb true 4d175ece75f221d68f2e754517a35861d38df006823283af92e7af8bd1d2e672 +underive_public_key 05ef3e9eb8cbe3652341763d7035016b1477198123c53533192d0e8f353a2182 5 9755d184820f95176366522f785e0226137ba7118b5f87f863c351f4509e02da true 96064ed880b4bdc44e230f9bdcca95b0ab944b9c943c2511a76eb6595c55f01c +underive_public_key 9a7b1435668d5537e27738395b57b0a15cc6f2109b29e3ad3a677b683f92a1b5 25107 c80853e78be663d665e06c974b334e84a9e15bcd8db460fc0a03fbdb88a2b3e0 true 2d615081dd489d5143caf02e48734e5c23340b41a22c0b9a4701732d7422cbe1 +underive_public_key 69a345fcfe6a95553c92e6ee352832f25de8e04418dea1ef5ea8212ab868b543 714 c5438e7907a2baebf4c31a15fc8fe58b99a1c8b8c50c2d193bc8cdf318458e52 true fdc84f020a4a1a98ec1c4f377288f94046c8a3abeaaa27a2f76df2884dc188e9 +underive_public_key 9b9df466733d756f9aa815cbd8d1f96737eb7f919e0f9e09f37febadd11c65a3 14385 2c55c2624e7ea24bf3c4905371d4de28508a6da1011eac26feebb14e4a97564e true 5be29d5c60b7b9f3307ac2708767535917fda8d84ba1e085bec6ff2cf76dbb6b +underive_public_key 56dded7736af60d040da14db887dee01f0579479f46b4bb9a1abfc3374dd3016 3 ed95cca6cd483780e57b40d58f4ba634b3bb3d29a53ba6cbe8b89b712f0ab4f0 true b03c77e7bb1ea8ce0f72ac42fe40d1af74f1221e280b34fb5abf894260bdd22a +underive_public_key dbb7c76e672ffa52cf53b4de99445464dc7f09a1952869d33846c7f32e1f2b77 3637111 b7d8dafdb9f3d11cfc50e658fd1f2474f1c66b38af7096e03a9fa58ed4e1504e true 6ea9e15481542c8a7798b36dc5cceef92b5bfe065f13ffea9a028855d3c40509 +underive_public_key f316440e28729b922e61b635643b60d2e544e8bfbb0dd83769ad1646a2161013 240932 4fbf677512a897e759965f4b829970c1c4746d772ac2dc94b7bca86365b8e5f3 true 645c6ba5c7c31bea8ce106c4db588ea3033ac3286425e9a5101b41b1c4f0e6ea +underive_public_key 55706b5f7f2d2d88aacdbe82d2375d194093b1b0d40cd8bde91b640123c68ece 159 3475277bc5971d7f2e27d891d36198d2dc00aaa1d91f5ccbce2373cdefb3fc73 true f80074c170a9ec0746e27aff53ec373b44a72ad5b5779dfd121dff7f38d5a637 +underive_public_key dbe35ac38ccba1435cc7da72b6e797f3a327accb3b472496dbbba10e3a2178be 166993852 f599bf53089380fd36a0a87defdcd0c040c2ab44b0ae1885706ab73813de3a50 true f993085c3f9bfba94fe11795e867def6811ae29fb8128002fb0b4597018575e8 +underive_public_key d78633d7d18bbbf2bbdb08d09c2735ed2a35d4cd4d49145a128772f7a9bfbf26 360307 a290ad21874999ccb5fac661b5d990ee1010c3730765b8426f8ac67a8c406f2f true 4c7a4c00fe3068491489633aa22862c69cbd5ed83b7b92a822cac913eb959839 +underive_public_key e5a10870eef2fae5613b0f8953739ef760bfb089f081576ef619c98fa69e80bf 23639405 5af9dbac4c0e7660f53c7ec5eb3b2b945ac18fe9bac36e299bdb85f958b40f53 true 1220137cea6082cca1a6c0509c81d8323edcc4c51a33673ad04fd24794379043 +underive_public_key 7ce8cd3e5e3b322bd54ab519e53bec1ffd7603360d370ea25c74d99c4d0445ea 192 6f1d804bdf82de72a5ac4fa2c083c59a583ca87b9cde11e3c16c725b9a9e8e97 true d1835c18614119274a02f946c696dd24140c7c0130489d9ff9f58b60b28fe16b +underive_public_key 87f6210f505d04ea2a76945bc2505d37d1f2278533c5f7c1c2bb616fe7fb0895 12772 b324b7526295e2ad3a5b07893b0205b6cd6e123c6d2771392e6ee71eaa988e51 true b37ebd37534d82209ed5632f7f19e77a9abe2e6f68d1a0cd1c23390b5973f644 +underive_public_key 4ffaf1f41abbee87addf5da26d291c18a4b06db294ceb5bec5879ab919823d4a 7842256 e0797f66b36741d558bf5217b94ffdea55422a6aa31536b937059952ecbd1eb6 true 534157cd5f283662696b174475a3127b8723789411710041722c0d3f72ae862f +underive_public_key fcb6ca71536564fe67f7fa149cc1c3620e776776992591a8b5205144307f1ce9 43992 a3373e96c773017de1c90942f43785e249d4bc6c0afda3f6e165af6aecc7e3c7 true ca9a1130c530121860c511d595ccf613f0b61172acf09a8c5041cf229aa98d97 +underive_public_key 1879d0090ac4d3f031a16665368511f2cd35c60ec8fd10f2eb05a569b07e5b18 1 74aca140fef11c4c3cddea1d1beac9ec5c8524958f746923c935889c2102eab8 true 1120e52dbb06e946580a19727b2e27e5fb2d9ec4d55644792b7e82c316070e19 +underive_public_key e582501d7feca00ddc72797be144c3d8b6c40b425148e25588f7befdb2750e2b 108633 c43084f60b27ac1c9757dc446586f2b859980ef8f4d00eb9f3446c906c9c228d true 1dcf8d4e4a0762cffd30b06223bdb0c9261a3d03099dceceebb5eccaff393c3d +underive_public_key c4b7fb2bb31b695c1a05848d0f8b982e443299bfee862349e57ebbfc6bd639e1 758 50bc7c2243ca2220d0b5e75342a723166685683e1626c40b831d3094e8ffde0c true 0d67b7c244ff16a9ce85ae4bc6c79b7750e84a72c040577b6cf4b851c7963a90 +underive_public_key c0c0384194403b2ab7b6c96563d44bc208a2f2ba5d0834b4a3f3695d0e152a7a 15130899 31791089d6219ea1c84b71048c31ff4da2efe29457df19c566a8d65e3f0048fb true caaa3c8d2d8893ec6991e2daa13825ff236dcea8186f65ddbcf608f9903416bf +underive_public_key ff5ee6ff809e46ebf7064e8a2671f5497e8e03ebaa4aad65bd019c7f21f49633 1921 913501f5acf2596980439508086bba168f47c4c0851f7eb38a149eaa7dfeae99 true 17d31bee6f431457dee2b505f9b9dbdae24a8e8e077e8283282657a34a4f6bed +underive_public_key 7eebdf55119feefaec988f5c4ec3a911f6f38888442e9fb423575284da9e68d1 0 264c452f2912c8d22acec0eddb834f37a5a5bda99c4fce914d871deea3214364 true 930dafe0cae4328ce105911e1d2bc035ebae59b86320651698d40c7eae15f2c0 +underive_public_key 1d953f3f485333d0d23a68704eb0ac65d2aaec480c20c73e85945a525790fc0b 0 f680ec1763eb0fcecbdd9fd4d992ed0460d457913d4c7f11f535764c3f2e8a12 true 169e7c8c31b1be9510add24673cb7e476ecc8f512b8aa6f279c1a25c4e72aedd +underive_public_key 210a141901fa6e05f32d4e656e38ce71236fb83e7f2476d20e5eb623f2f3d562 225731 1bd24aa53868b5667b10b9755edcfd6975675b9276049a7ca52cd14533bfd1fa true f6c47a2c5ce3ae7907bdfd76104cb07631a847ac26a65701667ce006ede0944f +underive_public_key 848f5da4eb241df1f43e2f0ff6c73ed6931d47ce1b4149795b836d7cda4e6f4e 1917 54179f432f0aa61711727cafabad66ca93d3b08cba40d02e91713c273016e238 true 36bf38d0757822a47766c6a6e5fbc4f8d83e1feba6ba72e50fab98c501e336f7 +underive_public_key ec46effa76273779b560ddb772ca11ed36393150c898089d0cef7b55cdd35173 123 d2d627bdfd76a171942c41094445f6434f993904436912ac16f5ce4a2d28dc18 true 7743be506d97d40ffef2ce02135aaaff190d84dbf8f65321341546b6e324fcd7 +underive_public_key 26c4d59b87e7c4b356f869d8f5ca493add6de7fb5518693685fab6552825d2b1 58 75df6996cbe48b3e9469c38d8eebde85e818113da7d771a68c668a292b819675 true e96e2eccbe8796dc837fc574396eccfc455383311bf4cbb45721da4aa4ea252e +underive_public_key a87fdeb1c8ec40fb3ce1fddc2eb6475d91781b3822541eb50e4eafeef7efcf6e 79858 9d637c7c0daafa8415a2f873984ac5c570812e62add53474fdf0b236dc1a7561 true 3c7cc15791dae90831877efb8d5c1457bcd70c1ff4b3f25e6d12a3cf0d516924 +underive_public_key 268f069bb8ceb2e26eac08f7840c738d0031dc679782abc7e40351403d36271b 128013 902546423c042fe616714ca6b490c4c14bc78172d33703c2f1592ca6bb98dc62 true d19b50620764cf65fc7a7a7150b60d16b3a79c7731033af0116f8782de62bcee +underive_public_key 9d1146fbac13651af0f1cf501bfc22b3d22d8a804f84d1e2a64c86abd27e7004 100606 ef7aaf68a1ba5a294e651de1b43343a89cf7b9c8ea4e942cccc269fc978959f7 true 31749bdef7f4356fa7d230a57631adfb1cb12c08e71ad6a880778862c021a438 +underive_public_key 8acb406a9bebef25480c8f766e5f5ac72e455a08d93a7661e8ca929851de37cd 427252712 62027ecef14686a15f8a444bda46aacdd5ab9e82da816b2b1e254fcdfbb980fa true 14677efa7e2a7b347a2ca343f8a1052ae58bdf766b0127cea771344c09072b58 +underive_public_key 9f83c55abc86b8e958a7aaa23fd93104b23b036c473fc764e19a66bfe691044f 6168026 bda81d3d834dbd06f7abac64577fc4473169f2608edf7097b31b8337bbba5cb4 true 17ecdbe216670d8d6d43f32519af39ca314ca641cade776a8fb05853fbd2fe1a +underive_public_key 14486adb0a2a58a278203ca3fc225b3e8b39924f5a560511a1a0f64500efb66b 3 c5473ef702f69cac688e12d3b4cbe455d0105cc3f02ea752df6ba03b97ff9c71 true 04b0ca2bb7f47c053581c5eaeba3a07bb37e90930ef4729ba3194e6d0113c21a +underive_public_key 797cc9e81e24e90f939303395bd90d5af0511afa68487bdd8672cf202fed275c 28944976 93ec70d4c722afc2dda3e736a1519d18d1cd26ef1526e058b61ac73fb7813920 true 85849dda9cc817df0cef79bfc3ff326d07ea853f2e3b8d197a1844db99f5eb23 +underive_public_key d800559bd49f36d910e65e5a91d398068691671891a3fd4cde277837e852f07e 2871 4988457eda8ceb4671a8d0a5619b5e46693aa336bb9745eaacf2f9965e3b31ff true c091ab1b0227b9976d817e61f42fcb7ce5a28625df81146beabb7f8d50f0159a +underive_public_key 6c5eda25bb13b7bc9b80e37a6e8ffdf536b72d2cef97be52719dc1275eec3a7e 2086 e272993e7d21f9672d1d54f0536e2b9a5d625c98881816802c73ff3f6d67063a true 7f6c4c68a4daba1415e054e9530804fb7dadc8c7e329f4b29701fe181f14b7a4 +underive_public_key 8f8f6086863d4ba391ec7ad18f8e20e00d86958c214b9a1a853563d28a7d5733 2 2ac48a4a05f43c5c944f8d57212136dfdeda73fc55de27cdb48e938bdcf64f8a true f2b0386b8315240805a250b9bc1c61c411d94e61fdaaa483eeb963961ca41f3c +underive_public_key 75b99bac90a51c910c96ddf79622f0b9fc7465fa55fd10c6d83dc4dd0b096248 56990929 427954c8ca568869488f50f7f9346566cfa369a4e9de04575b067c6425a8ea78 true 02d50cd5f2dfc6531ca9bdd76902c2436a4eb33c9ecf0bb13ee9934a652ce0b8 +underive_public_key 6c1a018fd06dd6594eeee78d3ac12cfb1b62651de021d36edaf1d54aa63bca47 10016 ae5cb8e33d29222bf7d0ff2037c2b887a9b4901032d6d782b0e3749dcda2760f true 29759e4cbdf0569e26796e00d06525e28d80f1078715c7e9573153a2b0ba9105 +underive_public_key e8d8677fe51067a71e0f15dbddb0dd7de5d28b57fb6b2a26d0c4b9abc85cacaf 26291089 ecbe5c1aa5b18dd4b95f10e865f7345933a51bcfbaefc6b90e0e31b34702fca1 true 96595b47882272c927eb6d62666712d21e5f0015c05a1e1b67fcbd904814b8d3 +underive_public_key 37de059f8081197c6841c13f3845db95d3cdfc6617fdc318bdbb1f5ee643894c 1525 7b52a713a0b4715f18ffe258799fecaf50a9f57bb891297f7344ba61aa96fb90 true 70dc2375979bec49d1b018eaae75575e900807cfe1c8539b96b669044fba4687 +underive_public_key 7d89566f45feca7ce26373b078a3847762a1d3bb0a18b9a11c53ff36cc858a7b 55849 65c1297ec2659cc738626d3cc1069c873c60ed6efca44283acf6364c1d0486c6 true 011d525d00f830375839efd28e204b5dafc1210ed15f7aab2d9adbcb00fec71c +underive_public_key 5b2718bff769df9eee4f6d55f0c0457ed9bb81b039b74e48ba20140702f324ff 49542 7e16e01aa0bd43ab54ad8b9b1f2aca6f7596b33137d1d2ae12b808275bcf522f true 1fbf32f6d6136d2d107a850e7f66d2cddf2a2db7022fe6a020a7d21ee410fbc9 +underive_public_key 3328540c5f9de2e30ceeb279a1bc12a82ac78aa4806ce31157b086579c0599e6 0 3957eba83a2c97f369eb07ec31f276ebf5cc0f0fb84118f68d723da3f136055a true ad316dc1b252ca98fb0f4d6093dbd40e87f392c24af7b8ab07a4192272df6105 +underive_public_key e88d5cce3effbd2d3defa3028f1ecefb5eca2449e6182620559cabc2b7fe6c11 840492820 14dfb150cad61938e3414c53546158cc46d9dd45f36cf1db95aa1281156c8bad true fed2d82e8ee6e86e9a85eab2bcd7e64091d60e67d5f492779d092096c85d3611 +underive_public_key fc742c43146321ea3f02374200827ed18959d35a90a3d402bbff402ae75c5155 2565561 02f439b970251787b22b06e23f088125aef2f11cbeb4bb58c750e2567020ed82 true 5cafd3e4dda779d447330e9f2c3f616af8c2f184ded779395443b1a85e308c02 +underive_public_key e0d85567db3c401ebde4510d1f96968e8e177c8d636a8291c91323d3f8c55a62 409899 6bc753f8f198da15b5a738eca7ecb74afa3ca6007fedb4f6b3d900f6051ad6bf true c12f13659ddf38d300a476827d61ed7dcb3c4d26610d6d3b95c120a299186bd6 +underive_public_key f3380fc7c090f9729da21fceb86a010096ab43a8a3d1b4dfb4020ab70d203b67 199 e7f043934ba310f562d432443120cb8421e9e4da9d9f1d516d4c215d2a4e279c true bc97303952a2d5524c9f84a5ec364c649b4153d45856be7e981df2a7df76181a +underive_public_key 31136ad3ec0881c868c015d73522824577c3c355be40fd18a3ea4cdebfc5ae25 59899 3449756b01fd9f50021afaae2fdf4907cc57125ab4d958a35622e6c471213760 true 2365f7d08e3a7c66141b57b51d9478ed4414e8e5acb6f3d952dc129aee4faa42 +underive_public_key a1fcc985319f7babe659f9ae44c4942283e5b24d81b64dbcbf8a2199f7c3c451 4270413992 f91fb7efe9f606abc1c46eaf15f58d4f27775f40d1295bf5a10c31f1cd64c5c3 true 9fede3ecd472bec44ba5ba861123081b0af5d981788ebc0a9478bf8cfa128719 +underive_public_key 9e800fe673706d2963f6c021dcc03fc58eb3aa9ecef02aa098b54bdbe95d4f87 9 b8de61c15b15c4602c09024d7b3ffd95da1263d36d3acf802ff23d05ae90ee8d true f4b5a337b157685ce71289010744386470431989918901e19dcbe0966788bd9c +underive_public_key dabb5d000237eeaba2ee454a3d7a822cc54fab3de262fb236fbe52af3b24b774 3857 916279870017d6061ce97e9cc14e0610e9dfb9773975dca47784aa66afb33fc0 true 61b340e6ace434cb05864873d26b647f8a26972e46ac246c26e3cfdd6ab01f15 +underive_public_key becf67166ea7de3122ceb30e6b332780442ba846654fe61f59dd6cce06696eb2 268682683 612df7317babf581210c6594f0b65f6301bb351ccff8cd1f6dc5fb517ef421d0 true 55f84045d5971335c4ed0107154f36bf9aeea0f97cf0f697049ce294a7eb897d +underive_public_key da02786bb3c64d66d636fe83b89e71a2670f5270de9fb0851d7a6c98e28e3735 18 137e95c746f5843988bfdb07553ece3f9e3216189ccffd8d8952ee301c64f40b true 5cadbba5f33e084905d50e6cfd4c4d4b374c97a375cdd36111a30ff0dd4fcd21 +underive_public_key f5198b2f6b16bca2ae6cb334a634cf93cf90e350cdf294e39ae86c3cd7ab09fb 602590545 6bb94493e83e676da1ea8e7ac593cf7b0957a3fa433fb10390433e2dca6f17c7 true b1ec9895cc958e323501f70372d65bc1c857f05c376adb4f177060d0eb5f962f +underive_public_key ed62c2d4da3472a2da4862b545d6df4132403662f10b20991264d5ebff4718dd 990407290 3c8daed44e3dd2b180e4f789ed0ad37481f9230e2734441cc6695c7f747150c7 true 3a0ed4c64063d8f0514faff3c65438e078146a38469c8768012c38dd25661811 +underive_public_key 91617445306c5d173a12221013486b010827371788955ef141acefcb0cf88842 728 9fbcf8ce2691f7ffec1922ea94d4ab2fe6e673e66a07921743aac569aaeef157 true ce83a30af735babbe7372c32b9dc9284241a4af0ad0abcf18f7165647ba460a2 +underive_public_key 18b27c72f34b0875f1375bffe3e828335a63f65d47b79cf3621600773971f365 6514557 1ef35dafec200bf83036f6ec01babeda282dc50599f133aa68a3df6c8c8a3598 true cf8145267be3195475c50a240791e7b60580768f19cab1224166a710172612d4 +underive_public_key ed8de0bac82dc2dac36afdc1467b5c9d949a1cd6d172f5df77dc289dbb40efa7 2172 572123e3cc3566f07e9b0f95f0e8b43237b107c9840c70659f8749717b0478be true 5335206429b87ded0aaff37069090e692977422de7a1e4a902fdcf3db5a6a551 +underive_public_key 5488ce4c9d2dbe4f1e057a1c82b650b79e36d16507636ad92f6599f50949ef9f 482201563 1b7ed448e86eb34c493bb7cde32b654a2c3b94734b54e313b44705736194f9c1 true df51546ddcc5eb9e01a5abf75bced4bde35998f53a11ea0a797f82b7f8887fac +underive_public_key e9a89af23677682d17fb636d0fcaa8fe6fd80c0eedb939bea51a5290354ff919 82374543 88ae22957c25058dbc70d7f3bbd48ba284e4a280de4dca0dcd353d982bb444c2 true 916d9b1908c67709f57a0d9fe705578da293e5c234bced7cd8bda8586ecc283a +underive_public_key c4612f9e611bd0469587ec999d702adab89536ccb71cc367c057e3408ce98e0b 51770462 5a0d7e5dc512f5f4013f93e8cad51ba9346a57fb1fa32f55c48ca6a816a08a8c true 5d3c23a9b2cf67258a92ab072d21d5852a4bd4e7a5518229ffe82d26e820f202 +underive_public_key 0aedc4e0baf37901301079affc950fd05364398db6a5aa08c988b7c0a9a05eae 236229 6889846f2cb663d5e595be507943f8468e53110cb3d607cf3cdeaedf72bb5e75 true 8957dd20752bbe0e1885981bbf5c54d2d63cbb3417b09e2079be5f056ee62941 +underive_public_key 7beaaf33735c4e0a6c9d36e15c3bc44f46a9256908376edaad525e6486cc191e 415256496 20345e17c3c392fa76cd55b21e947bec02e985a49ad06984a1ba646e5792d430 true 82fe6e29b2ca2d3d2387be72d6e7a9f447648d0b72acee5b8372c441638e525d +underive_public_key 298ce9aaa953bd305d8051a2ebf5017e514395e526d9026c762e540c0e801a26 921 7e2c75f4d073d7632518f463daa1f50cd702a81a6d6e70568fd196d632201c19 true df39bc07af71418eda9e301296142ab0bf3b2807cac84750caaa82105250d532 +underive_public_key a035ff48bff823d6b7cf36e9ec6bd8ee97292a3092bd7a2c32ecdcd9524e6c1c 108477597 2b98b940df2543b6213f9ac078f0c05a09b9aa4db31a69d334d12beea3162441 true f4a3c34188e60044a34c47b4e75e88510b81f8ff4c8c3637e25bdbe3df316642 +underive_public_key 1dcfee46cb4a374a4467dab31cd0492c843fbcbb57a6d28157bdce5bd98bae0c 959 eb23ca70256a429d50e3ac49d2c04ed5734d85a0f7673f4f1930ed3539b2cbc0 true 459e63aaeec6d14cf53eb9631f82a6c962e3c045798a78c4731bbaff2c50877d +underive_public_key e79665be488d43e8a47eb3ae1fc062c4751eedef15f53d7e400a6c44d5cc0e1d 3868 30ee821c43cd5cfc675ca37d31f43416cbd1b2830968525ad74003669ecdbbab true 7ca447e498af741e303a37713857409f84fecad7cba3a03a81de7e0d59dad7b2 +underive_public_key a433b9a387ba47382c0bda75753b1e5040ed7ba9cbdc7d32e3cc586894b76d79 7155 68ac3de4eb385b5be2bf65516d589a014af5cebf9c61e1f12a8c9adf787b62e6 true 0a7d75e51024805617f0c446fb699e4ad888137ea5bc0272dc2e2760be813adb +underive_public_key 590ac6df06153c278b4296135ea4070aa3678b3d40291736dd389f20595fb462 3988198 84f152d62b0c82f12d732b75068118fc3062805c157c33b9f357410c480938cc true e5dc9ac43542e7ff84e7436973c5f766a30fa8190f6cfcfd133de98af5f3b7cb +underive_public_key 4816c8b48c38d3863a9e0de0df8509ff5a27a98dfafacd8f2d43b9df7c66092c 5867458 543039dc0a79e1ac960465d2f3f8acf72592c2a165c34ade0e2d5f4efcd399e4 true 4ac27fc58b3e1abfc5ac0821513e002cece8c433ae4e19e6660bc70bb0cb7ac5 +underive_public_key 0065084b61335175d3564ded22746294d544c4289f7e06da117369f2b109124b 3 7609535035d372146b4a4f1e1839326751991d1564a84f0c163424c78786ad50 true ad412eb34495b333ec019e43e622eecfb78f61d8ef7ab8393645e1632755d373 +underive_public_key 6e9e7a221ed338f6cf695967c556536e19e51235002d525331089e00e17f488f 1 6f49d08da0339af7c0fceb52922ff1d5b64cf214459d44d35896e81de40012b9 true 06e3ff4a6ace11b0a82f7736e86cf6684fc2bae6cf390068d05e738fa76cb9e0 +underive_public_key 4ce5cb3dfadd14ccc99dccda4bb567bddfb9ee77aeea66d96e008a4863bb8f00 1146 38b9556ab2b869f5c773d1f0bce69dd22def2ee36d3151bb89410d30d3c0e33f true 5ac170190bfd6b79919243e4bd6eebd3fbdf658cc0f72dcbb08bfc1889f7ca81 +underive_public_key 56e9a28d3c147b6a9c8fde1647f5a7f75cfa4c705b1508d593bb4703672cbda1 243 2e61aa11a92bb56bb0648b732f133a2c3a8f2b9584fc574250e699438c4460d2 true b8296b4261be0e1dd9abf26e4c64a2ee7665f7cfa7ac296a950a936ecb04651d +underive_public_key 4152bc1894d39bbd5e4be23b1575b3b6c0d4319d388b11bb97b109c958183c68 92464832 ca670094a667fd3397c7b7b22b375fb87eebf633c98e35c50f51bdfd962706d0 true 41dac82f7dc94e5d4493ef89adea59f10f2d5d86335423338643ba78cd587824 +underive_public_key 2157198d1a088440fa71ac22d261e530d4e48c3ef06c508a58d03b0e6500c1b8 4448 a2302cef70f3cb9888dbf82abb8abd070886efe0b66d90869a02f12b5097a050 true 86205218e05d2e709218df62af27c4fcd2b0df7e9eae77d6ee07a2b8ebdc8c36 +underive_public_key a822f95f60ee5d248017057da6e949377fc02d018209b558219565ae1bd1f15e 17018 08ad69ad216b17ee18b5f7199718180035da9f96a45d2276df2bd50c2ada2a45 true a4545dab13e67a0e1eb584594315157520f5f4615f36b4d72ecb5ba1cd29f6e4 +underive_public_key afb2feec7a5ce95ba599d33635e811b2f620764b1aebf07135ed751dbab958cf 45460 9ae82b33fdeed12661cced9e67730bd96810ab1ad23cb49ee98512f80c7ff98c true cabda5cb022bfa75bf41d5886958aaf6a1594588e038513b5128810441d4a1c2 +underive_public_key 582ebda61d77b2d1768cd08d2d8709e08fa2f2ba3271e1ac4601771ec508c6aa 960541 6d715d63d7eaa735c8fdfd643a425b1adf4f25738cc22f731e142c2ca5c03297 true 671635da4fb8e6f3a9309f6b86326350f853ec2ae6a04eb6a828b76809a0ca4a +underive_public_key 07ca6da126b0eb06225f063ab3410d34a34ac3d6e8d7b32d8e70c9c4d6cfb38a 8373346 bb0631c973c8168bb3830303f0c33a950607eb76ecfdd290079b4de8cbf4b46b true ba78d5c0dd9c09e934a06f050a1fc50c752018a6fe240e6b28b40f97ba8d9b90 +underive_public_key cfbd5bff20596dbf60c4b7d99c14726b95660126cb3a276b656e4bc15abc980f 143867 552f11fd741015789151324ef0ab5cdc6a2ef0c8cd3ac060bcd5d1cb5d5d9e9e true 410b8ef6ccec2dc2c815c8c696d3de6e65c51066a1bb68f1cb5a0782f4877b2e +underive_public_key a29ef3134c4f7b14487cdd0838bf655e1377c2ac52e77bf50c26ba3cee603423 240 717cc923d6e1614ed1071166080277f78fad318fc9cafb37f5a42d2e62c6eab8 true 420032e8f3127caedc05933b52bf5fb39f31d5a8e5f4e1b4107d7bc7bf6af5a4 +underive_public_key 34cc61471bd4eee65cdf4969d01ca737d5d2b4836208eb68ae6e2b7c1e92ab09 49330 dfd5ab7e312604e61cc6dcf3e6ac78fcb558712679ccceeaf0b0d625fb89eb2e true 3d83d94fb92ee2c8c47a3400ef0ef722bb812337483519e3cddc9f8e46d59ad1 +underive_public_key 05176ca13911fcd7f02ffede4240577a5f931d9a8e39ceb101a816a0ea1b56fe 14198114 bd3d8960c2743682543857298f6cec9a33b43d37eeacea973131997428be4961 true 5a2192459948c695a4338a16ec14a2a18ab4e3c5f39df14334a1ca0bc03db520 +underive_public_key 4ea9cb8d803b0b21750c52060a335a1e23b6e23286d923ddd70914ba172990f8 826108 1018fa373dbe9eba272dee179221c840d3648ddecf0bf6d9f535c0994a4598d5 true 4aa9ade7ac99b3f664ba43e03cba3e0030d40b65a7793145d357ee38174b94fe +underive_public_key 33e4c0a557344b6207fe6c01cc6130a3f51dd7a012e3a4b5e28edcbb478ee0ed 109880478 af4d8d40901f7caec08657f5273f8f7d659a3b85930c975e7a6cb8d7246aa07d true f8640b0f824df05b391be1086919bb6c43ea400572aed6e40a9e95c0bc70cc2c +underive_public_key 61e42980257e349f1c98ad556e03c15afdd6651d90ffaeb96bf7514969d9f8ae 26632066 012e6cb026ce572558524849b3f6e24a6eb06fab44bd169359bd8af01e55b832 true 4c7a44ff713cf2309c344a8ba41dc22136880132fc61c22d72db727507e9cc6c +underive_public_key e867e5e208996ce0b44d8fd0bf641c6294bc48c0602f8d3cc0e21ae72fa04842 232267206 159eb25faed43538d343dedae78379ba461c9018ab80a7584d744113bdaad528 true 28f26322273956777ec29e6b105dd4af960fb755d145c0599d818c898c02dc38 +underive_public_key 451620c26e53cb878ff802ccbf0a37d7af55bcf79e4f107a230f1d8b5caa29c1 24111441 0e70c065460415ade7c62dd37b35152ae423c2cb04aa1831cfaa746800eb9ea9 true 2a6384a26332d0c7d856d36aa08a83ebbd198eebc47d37c3a4bfa8ca1d590c88 +underive_public_key b7b7f37e0709f06132a5a873988bd563fac2917a2f412abd52a425739f017a43 798 d5d057aba753803f352e94c490f792ce35cf9058025658656f801e818d72ddf6 true bb11eac2e067b92f900f06b8618c71993d16bf08f46c845f369e42c540ec44e0 +underive_public_key 18947d2fc03737d5b944510898d3012257aa5ba8f51e8a17f526336c39cf5514 86230 ea0d472ed7de10cb0939ea4d8dab1ba29f17842214bdf16eed987b612d612580 true 2538f1a55527d90c382a406de86fecc1bc2ea4ec582b3e86542c813dc81413d8 +underive_public_key fc5d969e250b0d1bd1a0c83c3d19b6d89a5e1c462c74f1babd2ae4265dac51dc 134388465 8e7a56a25d56585092b740d60bd40b991990f6f6acb8ad0b21963574686ae930 true 5bde3d3cf0f90c3e7dc0f9e3ac9eb2e104d1c75fcd600d09c76b0a9f6caaf24a +underive_public_key e1cf7903048f35132c495b9a25e52d15dcfecef2b4b71cefb43fad24ac0f09ea 62803 0cdd5ea1bb39ca2b14d820add6cf006f8f9e10b7194ef6ad88fb5f4d6d2bd850 true 50413386438494323fbe044d116616c52fae05353a24a5300590f4b3088d0e74 +underive_public_key 0192a9b26faf1fc7c78e64f9a4439ff81a058fd7d4fc450ac86f312dce7a050b 34512 a05db4b267970ab3b116aaa522f95dc94bc3f3c1ab71cc1f637c014fc9a4c8a5 true 8251150be6726d153015a86ca8fe1665fd99c62a2da354f2b395c047a14d7bb1 +underive_public_key ba1479b9d78ad3b5bd2737b19389e1186c9fbd35f6fa22ad3becd0ebe4564f4c 153 8b2b4beae5000cda2f7b665c46b5b49373efb28859ecee4bc3aa6bfc9c401de1 true 6ab42946a1ad829d7ffd42f30f9ae3822d54aee520af77c451ebe180baaccd00 +underive_public_key c55df5373291cfc287b308e68a1afb065243cfa0a941c03a865c41a00f1ab89c 0 7de560a0333e932734c7ae8b7e48c7362b419ffa159c4d7b5650da7fefa34582 true 9a8457f98460cf176bdff38c1ba461270ed5ee88913b1fdfafbf7520598b3340 +underive_public_key b4ef9b87696c29b979bbfac5baadf9de0880647b595d49419c60d4f839876ab9 116101436 bbe994446ff08410a8662fb6960e66b16c7c1f3fd990a8a02c9d3a37f84c7bf1 true b401d522f4eb9d5ee4cad7f4266ee1c607f65afc5dab03d15472db45f64e3533 +underive_public_key 217a3b96761b47321c2092a04e41f2915227c51f177a934430ac3f8579b99cb1 304 41c3f7d7eec2cd4716c852748deecb137c2f424ee88b30ef369273d61d5e0eee true c1246fa9fda40fb2299affec544fea3a6e6811698a80fb500297c66566d95e70 +underive_public_key 0314f67db01ce58ef26bc0ed28d625a31be424f6e9820e0726fb9b34856e8ee0 27686437 ce84f067c0bbcc6199f14474c0f1dc9fb8e55a5dc6563d544d31372ebd8d7fb2 true 8e91eafbaccd8bde74ee8d12017be8900fecbbbe0da3ea5038dd2438e9f9ed12 +underive_public_key 6091ac42be96d6718eb7034e374b4bcea38af1213d0bc0f6b67ee20578d080a2 9413 89521b77e14420331e61b34e6bd70a36f0b23ed598091eeefca2ad92f85837c9 true 0d52b9fa479abce02087897e70ac43bc47faab611b2f136f4ca0944b3ac202b1 +underive_public_key bcf5f6ead71ee9a1577c854760e4ed3d5462c85cccf4afcae4304a96c6d110a6 72 1df856a0bbe50a3def436f9670bd55d207a978912115f0a89c237656982258a4 true cca3736785318ea07afdce311559071d9a6a8eb7dcf86cbdacf763b954ad962b +underive_public_key 798b388df10cb64a07413907b3c5c60b97a9ca46203d88ab69e94aa0c775e355 305240 ea7044817f17a7eb5204768119556632b7ae53c1637a82ea51fcfadf2f23f4fa true c50dd3b2273a45cbe0c642cb756e5daf97706f25753016b1bf954fd6917b9e22 +underive_public_key 9187b94a7b1e1bce1f5f121dbb263adee15e8b79c4e93d6e5adb96f13c024f14 4167115026 1fe234bfcc4c14cbd528a2748ce659714d6559aec46c35234d3ab2022a781079 true 60caeb0f68934805ac504f9d1fa31943105844d92999deb6390256facb12f5fb +underive_public_key 02df9b3e445575a39856460b744f4a07d4be5396bfe125fed9110bb1c3176df1 195195934 f789120f19753de09fc9265a67cb1811991e0948ae18baceaa9c2e065700f734 true 91ff1bee287673c3c4e36bfa07467f598042f45bed88c6ac8b528612852e1eba +underive_public_key a0fe8195d69b13d5061823a977971946a0d9ff124d9f352c588f73d09fb5147e 330870478 899fc7dc62babeeb5d1f487ed601da4ee495a8f6d5b8c8425c48913ca89b7b1c true 541af0e596614d1f8c84cad75fab96418ca4ce925bb1e52000f2b9764a778511 +underive_public_key a54b09292034bf9ae721191bbf9f0061ea4675aafe067ed7109e346b3f31a42d 112 a64d897db468f6e9473dd7e93a6afe97458b0da0f7c4f0fc3987e0b6350d7256 true 8ba0d6a0c93e8f61181ba27454e44c8491d9fe85732042b03e12ca2d4cc29366 +underive_public_key 31663ec265b74b9d22519c921b200f0291ab6050630317d476888ed10c1d3656 222989 1455fc0bdfaa201377c753c98bc1ba7d37800ac42fff839d0a209755579d9296 true 837ad9c035f1eb677d9b5f3b674561ebc8b8154e7c620a801d3c92ca52632de8 +underive_public_key 4fe0bd83cce0741367198883ccb110132e4e17e837303eb81311a585045b0d7e 4221092369 4dce2a5ccb91d6b7bd0e97ea4c69d026411bf9e9fdc599bd5438a29c0c9de34a true 414a59f94fc1a5e5c2406d3642310a90e12b0121158a9b39d6dce613de11544a +underive_public_key b4442c681c3334ac326681bec23dc0656d0ae411ad0f2693f1311b54b3be0843 2 0ed6eaaec4a713384acd11df0020089b515efa376867fbb2fb6239da9eb8262f true f05cf4fd94efa727a5722ebd3d6bfe98e2b5591df12fc5a7aa3e0821b5376fb1 +underive_public_key a1c8306b138d199dcbbb94b11f1e10d4f042d7d669ee88d7537b20243b0633d4 2541215672 ce86383ddd44bb944b6d21b34a2ff72b705c28b7f138619f4b6af9df4fc02827 true 6b3e527cc69695c2b4885040f09726e4dca92c761c6c1cfc0e273b33bf19dd12 +underive_public_key 9f4fe26fabb8a65003d2f8dbda56de1905d5427265744290a0d9cbb7d8ec8b94 869 f3c33adfb0241f30fb6224c21365542e06a159659dffbab53ab916edeeac5f46 true d02d42f69387c3e5dd27fe0ffa11a104d1770a62374936c0b53e9c47906ba4e8 +underive_public_key e983cc0f0cc8eeaac1aec3e58d86d873ce64f75a9587716c6cbe9724e3316ef2 1 23cd02291bbb01e4188b59a9cd3cd8563f49e78d2fa4f9ff6696a7f685089758 true c24eed9939f155cc7b556ec3146c7e41fb4cc507f237880a2d0a451cfced8be8 +underive_public_key a7216c13f5c461ddc5fc0af8fc800694ab051def5eaf16c86ee52b22d18bf056 772 e34589dc247ca3fe58fa60b0d6530ff4b834f55d10b7af54a83422202317f1cb true 54dfd88ebd7a6bf251647551c46ee563fcba9a3de963ec1d9d8a58f6057f4b9a +underive_public_key a89203d506c46c3fcc7d8e447db4f003b3dd4aa46537d8d4a76acc336291d69b 2638094 a10796a4a3a99289d137870090cb5b3fb53d796ca01b6e99e0be6a7e9b227ce9 true c8be28612bd11d3b91ca1724b29742cccb0451e6d4199031cad10e80c92be685 +underive_public_key 17f12e5e8fd422180a5c010a1031b6cbcaac68f92408fd6714679d6c4323d48d 43 9ac191e152acd4bbc3786f0fcfb4bf273327fb7780c9010a38e7490f43492f66 true f0a5ef4e4e89608ed58e8fd2ddc706aa93f4611c390d16ad79803159b5b6d957 +underive_public_key efd22dc9185452c89f2715ffcf4e5decf6ea5ad81886eb0024721fe149f765bc 2048397 92f52554b75e457148983037be1e0b40121b83b9aada24cd11f0b4c824d929a7 true e1ee9bb2533d6cf53aa69378a10cd755a507893320ee101a72173094968abf0b +underive_public_key 8e6149510569b101505ab2e3c71c888e19fab9b6562d71f9403f14072c8c1f69 344 36a4e3f0f45580f707785f42bb65c0571afe29088355a02b75c1b6ba5de29055 true 76a5691bbf1e1217374c75ec64557a4f3da3df1fc648a303d2b057c534f0e51e +underive_public_key 58e4fb73330598e32d47ec6bd45905cca3b87fd9611239b2ddd5a917fc8812cf 74563774 4c8405df338359965daa432471cefd693cf7c635441b187a427aabb241848565 true 09e658a0ffbff9173c301233748be3b9111d10b400a07232b1e168ce14b3f589 +underive_public_key b461c3e1acc0de3f22a133724aa5c4994ff1f6fb5b5c2c750367f353587448f2 125 40bd09108250051cef8df67fda4fd3a94735ee72f8b1469704cadf47bc9136be true 3d00ff76aa6425040e15899ff3a141dded14b2442cca1ffc6dae0d5dae297749 +underive_public_key 2c555249743726741664be7192c30428bc0156b67d0d78eaa10d2137560680bf 45916 4d6013f56f61ef74674d757e2419bc09f1a1883859e30c42a7b6f9344e0884c5 true 12fa7341ff717f33cf71ec40fd7eda183b9bf70f407fc3ffdb5a5f362d985e8e +underive_public_key b19a028e875ba6b033b0465004ff10fcc70b734ff8d9a2dfb5e66558ef221c71 47068272 6f94ba4d521f2387e3e35674d3a14e6496d9513be3201d31bec03c5ac2c31d11 true e6386086c45323064244ff60e7353ee256bfce770365c098a0916d7d4d14a37d +underive_public_key f3798612c6af27db1796dd3ab2159c812ba4d332fb5210a70cdcc59f32355a60 200 9f89707afc8fbd11763eecce7abfa7c73567b6f8eb461956bf9b2e9246064e48 true 63efd41f5b2786035bb9e9f2dbd6aaceab36a13ea06a0e8963c4f6dc34e46c0b +underive_public_key 0c358783add749febe79cd4593befdc620678a266375e18fc228f512d16be3f1 7335 fa8a5eccc297c6b72a113c1684d27c287a7663b8d801a08da0c903b5b016a0ef true fa553b2180513902405e25c149d860217820eda1737b5ed150545272cfcfbb13 +underive_public_key 9579b6e8dc108633ac8b67004699921aef479b6e7ee9590073fbe1404ee4b3d5 1945656262 741550bc4e71ccb06bd2aa2d0b6fc4f41ac3704dcfad5e4a53638fa4f340b83e true fc64f2adc55a577957ccf28b8a76443d6870e1f5e6966692c8c40c4eef67e2d9 +underive_public_key 959d059f0f6f3829807247cb4d945dedb2673460a48fbe065a23f72594b882f9 3292874 5d0e806b4dc1a7f91d1dde54b244400cb5ea75f221e933bcd767d61e7a7e1bcc false +underive_public_key ca814cec63d2236a1663fdcf799cb14c8862b080b6308a664de2b0ed3a3efa03 63009 718bf5bba9c9624074e5a14b8e5a1b6b1f9fd0f4edf325e65bad58eb3e7ba962 false +underive_public_key 136d83af514019e541b9996269cf7ecd45c55a394dfdc6edf36536eec0691239 3411637605 f48f79dc8cf39b06fba4316442d72397c503884578a64fda55c4a191a4d0190b false +underive_public_key 8e7c54ee756dcc0d33e397d8ebf11741e8b9a5d36239c5d63a419a504daab6e6 0 c291fef0183ea01a7fa79a4d45e2c97d52d5c392c328ef6c8959dd9bbd035ea5 false +underive_public_key d8b258d9c242c96b1b68328815bcca000db67f297b2d69dcf8129c83d194c0a7 17 401f1dfeeaea59576a32334a815c5c266680079a98285ca20188f6f79d1e7d7f false +underive_public_key 495b2937976ee4be89047d6e1cc42bf3e8a325d91955b07fa1308e95bfd0de16 1446362896 6217e93e40fe45c524c60434b05c7a017a126ec4547d215931383e5cba6872c0 false +underive_public_key 7b259b3565bdf4b5567306e8e4f22883824c2a42fd2d1f9f74106ce128ea3402 60 5b3d16de63a3015c7e2963caf6bc0b46118c4ed8f298a2a3e3150e97c61df819 false +underive_public_key bbf85aa22942a41d255f6dc963a7226c83f3133b0d0e21a6976ac5590129f094 5539811 daae658fcd8f18d9dd04533e0932b1e0056ac2d7670839e6da4f7ed45e981a7d false +underive_public_key 834371c43e21bf8a3a9276f6197b94903611b30bb285d9855874b45320f799a4 7124309 bbaa85ca8392466f6af25357da625b83684c659d08b737eedc6f1f10fb056cc2 false +underive_public_key 4a909b7ed99ffa41a5adff73fcfe34573313f1ec2c017ab8d9044bc6b241a677 14411 413a291a32261fb1416233aeb28b751fcf93317eb0a839caeb7a06d107726ef9 false +underive_public_key cc7dc5acd49e9d4714800707f3d64c9d25fe845e5cb142b5f046777a1ff25bca 215297566 d5ddbcade53aa29fbb01af79f923d9451757e5705f76728c858064a16134221c false +underive_public_key b61d7a83ae9338726c1b6776f5fafdd78a79df32b1f8bd2702727d9c17f8771c 23195 e06755f562e59b94d2de954b55c84bc0a84992c16a4cfc80207d2f9856685e16 false +underive_public_key 5e0fe211a72edeeb956fdf424b8769e86ca6d8605fa1dedcfc7027b17902ef5e 6 f415080f3734b92e6332f9d5acd2a1caf15edea9e51864d1a3a67bd034529b0a false +underive_public_key 037b75471da662736d252e9805402bf5db6c51b05e6cea220a354835a31bb881 63 0ae5d0501579b294a99293bfc9fd7fb370d694a45ae734b83dc1a5efcffc18dc false +underive_public_key 2dbbd502216e805cf1aeaf2d7cf84f16fd17da669047977bf802b7e069c1f36a 247358 606fc92fb0784b68b04883032355956c7a72176771488f2ae9a3b01b6f8fff00 false +underive_public_key b0759abd880edb0b6fe948027b2bc980ba306bb32551c14a7f1cbc4ca44c3827 7664 8ad8d6e47a9008f635c6902553cb0c547c766de8b1fa4aecc2c8a6257291e61b false +underive_public_key bcbf682a7da26caab7cfd2a3c3607eb91c6574cfa92c6ce797b8a6bd521dc843 216 2cc2a30b0d66a804248060b12ce44ffa769bc499d2e5a268e81c10a078a8d0de true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 6ad1c04a7c34bd43054aeb4f5de64a50176fc0968234c9c22ad50bfda9c6de5b 23 eb34e3b15d8c39d6a0c7e38939653be90f78ebb42e5a58f5980b44614a61d702 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 6548be0e5729f2b3a1bad82cee43cdaea73777cbae74030b1e856811bf06897a 462 33327eb2ea6f591fff748da6e6146f0be9125f013636c95cb95ae0192518ecce true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 1caa8adfdb8a54642419ea4f17ab060845fd62bceef441711917c6018502e340 38 cd5230384556f90a67adc1da6940efbf3f906774871480dede68c923311caa61 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 0093c414c1127d006dff8be46dca88d1beb6c87bfe3e281096042b2affe282c0 2282211 8b2ce986ed33b5ba15d8ad63129989a430ba70b81413f4f7ecdc8f95c8cad4e3 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 237b25b82acef3f90214fc0b2dd84345160a04521d81c2b73f1604df571d9840 459 67a984f15ced72c6105b39d7cf41033e3c6fbce101a22569caa210affe3cb5ac true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 0229c8f00fcfb9d6d064f0aa7d4218e37b7ea90a2d22bf46ac737d9a6acdfb98 229698 6fe099b61d3f31f2237ec46a536407718374c696ba04abe5c2c54c8689e5eb94 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 8ae2ee6e37c8088c04b390c10175119bb282024586cb47d9d42ddbd88df80537 1874720421 a46af19b2b8db8037e157e06f7c38746d7e0b6e066abea41d4c029ade193bc7e true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key ef319bc98b4f287d9559131c6b2514b9d94508f5ce19343a701422656d7e1e23 2789799309 ff02d6df1b58122e5259894e575a01f0091ccba12c4b3ddcf8e3a0e888e8120e true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 4ea60db8a162668278964aa519d975a379ebb6bb06229afe3a247bd93fe133ef 1440123310 205001bb379b34f88fa437b34c908a7e56b7b122e63db34832893100d55d0611 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 3748c40ccc1b64afa5eb87ac442f7fecbf91252552247faed8a513de10f2a4b0 815087 8369a6d58249f09dc2ef2117111ef8f201fe1b2aaa1b0d11f81602001f798938 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 4a1df369b8182fdd1d0b6dd4f20f1ba23a4c95694dd1690f8f3d957adf7ef58c 189982940 3e1964af923a69820273cb56b4410f5e85f9c991d247b15de04cf7594ada2494 true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key b807ef51dca31fbf602f87794cb35d9f4d97ce3df2b6fba90f0026bd94c05649 11722228 dc181f924d683b139756d8659241a081d07120da4fd95d43d5129ab14213811c true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 5b7b24f9fbb8225d4a50e5c61739f9b80a8c612fc1f9a732ef78e1f588d18b97 4 04ef306baff5ab5b6af21d1557031e68ac4ee739fb299e01ee12a4b9658c24bf true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 1d656b5ea25babebc241e8806f05ec9c3acd88ba0ada30d039e8b9abbfd5b724 6 648c3e2dfc4bc7f0bf498a0a2d828d10dc789ba652f9b2faab21f9b4478f251b true 0100000000000000000000000000000000000000000000000000000000000000 +underive_public_key 14bff918b844552daff5c09e202b7584ca46dcd1d347f140f0da32e265e05ddf 49659 3674e3f6396577a4f291c2afd3f3e16c4d7c7c12f4fa60c5ff1ce2a9f365a1a7 true 0100000000000000000000000000000000000000000000000000000000000000 +generate_signature fa3fb9873bad676e411ff299538ec02885e9ef27796d54af939500c84b1807d6 cea673c7815c7db84e9ef36fab71f04a901d7c18c6a8fee53e2a995ceebaa6ea 2945c1c21b1f8c2e6388ead3b5e73279d441e8c154c48cc08c640edb7473410d b3ff7b834fb88e019b640631192feff8ef157ef2de678a933b0340eb8c776b02c45436707c15db14112c1c5e2bfba0ff60062f0e05afa29800127243d435530e +generate_signature 2db735b711109824fd271fd8bd93b73bae764fdf546366094b0b1fd3e2d6afba 541dc48a0996d4210fc7d2456214c94c611e1dc7a1d89737c3618dd32a739eab 4e101ada7a1e219ff74ed15a8179336286afac7ce8d6a76a74d7f6e9864c890f dd37d47bde29c91f855a7673b730cf93140ee643411d9f884c3865c1a6e03b0042d38596456afe06bcfc5f8b78ecdd0cb73ec38807854dd88f9bdbf058007c0e +generate_signature ee543455b3c75cb94f40af266e03ab5fbfba3fc04f7c005665c6d45edffe6974 a18f715d6dbf7d914acda2704d52702b61a28ac0c1401c8e8866065646489487 ddb7607c7d47741813115082be398e82961d344187b836186bf9deedb5219207 852e3102c9bc88e2a56b213e725294b4f062e8f05a3501b59bf31efee84acf035a2e45fe99fc77923f5d88c3aae7c66bd47ccd4cba331312694721f714930a0c +generate_signature d5a43a7098f713be7f2f6703565149590cf9b1f3f13c63524763d1e6330036c6 8b389fb374262084fe46a3b7baa1e78110dbcf1948b09df0f5d3229bd7673e5d 7e6f78171fd6e515f5cac33c9309e6214280a787604942e7915d3de302ebc708 f5f88ce849e81c5fae48ec4b9ed096623de388358e5d33b4301a036d778f6103533f933b21676a742c69cf0f9947d3f708319406657cf6d6357973e3bbbce00d +generate_signature ee5c548f7796ea3b904b5db6b8f69603097f9844e28770055594b85944ca5cdc 6d5ec5947c673dd54e60b33d414805ca28ab877ddf4a485a68d43d551ec6fec5 8074381994b8c339a883dd07695cb28397d9bb1dca391a22ffbe14bb31093a0c 15c205fbfe532f3da3621220f8e63ed1408b76c161d8a1cbcf2a15f1f3c8ec059576b6777a0d03ef6b5c24fdcdffbabefa90277d2652c379b283d6697793a509 +generate_signature ddd17497d9fd46556d107c7559d9b8f75e969a4ec7e7ef92065a1f1e51568ae4 9b2aa37770423b62b9a12bb78552a1887c4e8166858469510defcf52a7bb93e5 632758e9debb9a229cda7acc1a1ff2aac0058c5529195fae32486c3150621c08 07c7479ee2022745a2b56afb4ada06e27c95685bd1da28155463bcfea2613206cccf76d6c0fe3024a34fce7cfa2dcb7b82123956b242b5e11a3b0eb7461fd104 +generate_signature 98525528d27e7f528169a7f5bced121719d4cc10d8338578f459bb36f58f11bf 5fec6a15f78d9e184421f4a838b1cd079785240f9b0265310a9080ded3af3841 d8e4e16046fdbf7ae309f2c26d3bdd8115875e46c238f1f2f7738080f8e9dd0e 8c4d0f34d8b4371ed5b9c4a3663d19c9c1c8114a65339ad601074626fe108f0857cdb0c78681a59ff96ff18e7184ece83640b8f35c85c1edcbf35c0be6117609 +generate_signature 1a22a5380393bbfe78f10e054da0177f0c88156ec6218ba772d99c83d8fc5807 d6ccabf76a925b58461c9fe1445cab30f73b82f17f0445fea7838fa4d6c74805 52a74c9c16a2a5c0c561dd55f3fa881eb5a0e65e7ff014e42c9d7d494ddf990a eb6a955d2525076597c0f2bf7a618f63255a71e6bbde9af194c75124e405df087875a292a137734ebc4b896da5c31e76c156ea709e0200e633f1cecc25f68208 +generate_signature 7dfb85fb5782a63a7023dd7c2f6ca7c7371fd9b87c51602314a3671a411115dc 65caa46224dca2640943c13f071f9d2b074548b2d99d62ff0e53ab2962bf7282 8a2a9ab81fee502a0a5ec8e4c66f6709636e9fa38ec5b9008e3266750535d108 fc678d7103cf160a3610772df74a0ae84cbfdba1d087d4674d640eb3d3e95e016219be5e8aeddb5c23bdbaec304db025e82ef0ec897c2d444461e6dad6dc7403 +generate_signature bd0266d9234b922bc689f31a76b676d8bc86dbfd0d3d2a7eb60489a512f49adb 893fa44d4d3d998d571fd15f3af0885269627d65d10695ef563799e99c29c683 cccf7c79b87680aefdfdbf2f7127d8df840c3dc1efd4fa3ead2f444d9ca80f09 eee21a995e6453ca469275815543e10734e91e6c2bdc32c24bce8c33efadd60e0e8d6b7dcb0e48ca36b8fd0820f5927b03ebfef2423c66cfbe2e123b76aec209 +generate_signature e15f43feb7b663d62250c1bb0d1c870545316f88e29a15d0c844b063a189d95e 27b13d6a14642215b2bd3355acf6747cb2188f05790b807a1299822c85011ea9 ee20f044bedd5dcf15a460a28236dd08d1e12cf1466171b4cdf7446b83ba150a f3ef1d31b40b8916e5fcfc28719463578c8933ecb2140b31bbcc32829f32e90f8bc40b548b3709e05d28aaac2d44a5fd565dcfbb73440ae3c4e0688a2894d60e +generate_signature c98376c03a61fd15aef915b0ad237bbe2cfa7e545ca7ee8a929b9d2452dd0db1 86230cbe97320a89b354477759034b6a1aa44d9d66f19fc1b57f72fd3efe02f6 073b22957a3828e3d229e7f7cc294ee629de51681d87b8e39bda5c9cbc513105 d0b90616d13580e094d46ec7f30a10a33949e80799e1310de45dc017a6aa7c0f734706f32b422274435cafbb8b80ff4d98fddd43a9ff85d0c96db6601968e807 +generate_signature 463fa492f0b7d9bf8e7039e7bdc0d95634ec770fbebf702c80085e213d8abf8a 12591c22002ab4265a2e1e8a84fe8a12d751d85553f022fd17d6ed08f6a1d7f0 9678a36e5833ca49779694df8cdf2a0deaa9d0dee2548bb325eddd4b4dc72c0e e34a853fc61bbd41d4ea3ad3f0d60345a53e90a7151888e9fb7f9aa0a9e7ce04cb263bb94acd72b6c67fe0f02bb9ab95fe278fa5d5649e3add53a29a17c76a0a +generate_signature 7a5cd013db2318eadf14a2ec30f7b11f75e687c3e76aac5b0ea584c028ab3208 5a8491d3a78040532a09145273528340bb61c8df30b3e330e65817ef5a459297 e20aff0217caf27eb1bf06c38be72651b2b3c292932de0ba99829e09925abf09 25b42045e29611eb92cc295df37bdf278b4ea51b7a494d2389b9b761819bcf00f5b4527c38abbb908dd854fb1e32de79112ec669e78e0332e5dc583bf7db0c06 +generate_signature b29795f5a821f7039cd37f892cda3c189a09e5ed1d310f3245def40f1190819d 683fd2ef7ad80bc4eca233f29abef84c8e2fb5ef1a4570369b7494f69b510be1 ee5634cae6f0aae225189d7198c2e1ee9bfcca41bcf0e09acd8a238f06a77b0e acd27709f25bbaf629b9f5af9ccda2948dfdd5f7a979d2782b5995f1643a800a617e9dc9f9325aaa5078eca3dfbed491a62aab99e932d37313f1a51014bf6704 +generate_signature 5b96bf08ac0028aa08b8f24b0cdabd24f8cb22ba679755d7b9283b29f5921a56 9f34b5e0ecc7ffd9100c7db569e62973c6aad7dcc83d67c2c9552e6d8192dc1e c94b5ef1dab84ec1414feb4ddd3403588b566d6bd11ad78da139333054aa8e00 51dfea74d9f1892f166dc7a2eb789d7be2bdb5d0aab384c07b9fe5bd372b94001f0a3445bccf1cb940250cb50149a83bd27291436f5f95e6907b094977915f02 +generate_signature 93a60939be6cfc3932d7b760c829dd09c74a4369f92d4871db86f49f06f84205 656e4941c9be8949ad940e16460eb716107934d7a414bf89c8b69637075c18fe cffab20cc4e31aca6bd7be891d13c2ba16a026f3a852c77269c9f7c9b4dc6602 922725040380a42d5337b0c3786c385e7a4907308e907a82686f871e073cde0ba522373baf2668c7efc637ba5ab4af7de71d8900d0f7b1fd03549cdbc45a2f08 +generate_signature 3baa03fe52210e38db17fb1f41a08265c3e163cc30241cd79a6e40a0d2e42bee 56720164f1222e5ff9654d2733d313c8af52209c047a3630990f05bb9904bfe4 cf0e5fb728e156c2c36c60d39d7bbeaa34ab71f05c2db571d1cbb8d55ca63a0b 6ac999a68327f85bb3dddab04bcc3250f583b313915bd6dd30de39ca0ccd6107b35fce2b7e4f96845eeb31eccc1d4c18f22dc318f456eef8940cc7f43c739c0e +generate_signature e091d28a2e49d9eea21551093ede5e8f3daff4bddfb2aa2d6306c6b461d1762c 6acd454a5140f4655475dce6bed1a0aa4518e167a2225d42dc2b52fbdea6ada5 5993f200bd72140d454831fca8664feb5690be562b33094bebeacbc890b73c01 7b72351906987ffc0b5ed224051eabffc55f7a7ef0d22c25256e475d43a05a0e19c79d7c365a4c65b43051e52b7c3edf5debaac691d4243d32fc1f091b528402 +generate_signature b190c113c7a3023ba9b2120f484f55204e6295df5bde2f90d3ad724e5ced0dc2 ef3b565dea7287e17aa79fce0394bc85756f50464c17ddbbacd2b8b720b7a0ce b89d252e5da33cd7f032e5ad07f2485e5b1e0d469d85889d046afbf547f3a906 a86cbbb59e0079c63188fc3ca13434b08c56ee4f8a203f667ee1fce136c9ce09cca3394c29e33c12bff3c7b07727d097c6dafba2d83e93095b27570114e5410b +generate_signature d1fb76b2dc1956bfe35891c9e020667dd4e11f92fe9cdf232a801264b0dcb286 b5653b5294d21cc6647d5fb5061e092eaca57186a99db1c57de79505e3417952 342afeb00c425825623129a4134e351017fd1c882540397c81d0f774835a900b 571b3b3dafc026da38bb863840f765ea426e2b383d0ae85f26af2e122bb4fe0fc9298755ccac1b110db82389b6668ea9b6972936a651cd637ef2f33976777807 +generate_signature 249e3e49bcac44a01bc327520f2e4093ca5b9bc91e918464c8a06431f40cacc6 077a678e9c84f6db264c569ddeaa66213120846c57a5781fb7c59cc3738f6d05 3ef3ecf142a69db223c8e6ed5422309daae050f9d77fa2fa87d03c0cc4abbe00 0c3012b91de0907004b3bb02b092a8ffd7426f7e598d447481bf549f80e73a09cc585169fb82f8376568b9b4f4d66d7c9801fccba1b271e3e110fe6f2956890e +generate_signature 67a0627e248c27374772dcd435b618f2178055f1670d84e3c201728cc977d93e f35c106fa7543dd4a3426f5f5b2150d166a11df584823856da40834f3977c82b 3c1892f13fc8c396ea902f2c91d69151d86c81cadb8399c875252656428edc0e de48399e9eadc8fe2897a0c2959c229becdb9ebe942796a17f6d2c9d8bbec6098c0fd7fbbd11f9721332de21e67eb2c7bdc0964e91899c781acbe43363162006 +generate_signature 3552cfb14df5cf0cfc85511f5ff7f911b09e50a7f67d0ea72bcc40a620678a0d c13c1ad6571241a5f02dd80f67ca7e2f0fdd64b40697e2af37f6bde022ec06e0 ec68be6de0dcf104e1c05612f93982f13ec5b9bcc1cc005bedf24b8480c1330b c36a1f75ea41b479c93b168ca7b4ba149b7055f725e70c21ef80b9289bbc28028680b2a14f701c1f2746dd7a4ab5b916ba9061cf1065b232de3a36e57851190c +generate_signature 49ab4709c6d530c4fe50a38e6c2913c3cd81902b470075306d3d0ce945b1e283 546197d0ca5a21b19b89946a860c1ba2f9f6140d142d4d99830499f89a2de192 ca971bf751d18bd63a1ea220fd929b8a537e3b503e5d3326e54cd96de8ecca02 8d5555f31246535e592bc1841eab9cb867e70ea586db759aa7348ac03cb70906c1ab8d8b2593a913e6b761dc9a4795a0c6df1788a72f3cfffbf72fc2c1e3a50b +generate_signature ae7ccb0735af0e04cee8d3b1967b682ec7a560c407bddcfb43e0475f7a41fd46 d4f38eb5ea8e1b7ed04762ab15b77a5c8be5e20152ee06ebf2768806970d0f79 e3498a84ba3daad29fff06015451cd123ea911baaea9a8dc566b48d8687f3503 04536aae9bf72603ce8574bb721394961f9b3478fd7300672831015ef028c002c766990e8f8282bff28a4299dad1eaede51488427ba26fab1f01b2c92af3930a +generate_signature 70e8dac921d6166cca59b4f3b29bfd3f530b395b79e886b9c966a5db9290c7ac f7ecec54fcc82e951a2d3376b5619cc8dbcda396f150889442c2d68a749cb5e9 b8a256eb15057a18c097c8765e5637a5cb4fbb015eb864c68b5438ba8214a20c 567421b7adbab7098d7ce5a78900ed693c0fac02f25f038aefdca77ae71b900d616e33a1ab39e05c5e7b08e50a40df0e56ff9a029a964ca1e08b5816e8f83407 +generate_signature 15f27228f3c5a57a7c3d85b6e7375fc4287e6a78b30876ee7268118fbf027abf aa20be5d943865f367c6be4518c56d5bc0e149fc5ed60f2336b801becc86485c c36aa4174aa6a34b70781cc29ace36155aa20b657d4bb51e54df0a607a71650f 9711e365050377e5f429495469f80b86fc684d85a30ef7a9d101c2d73c950f01ed13db330b0899e3971443493e183dc37ade74fff72e173aedfba5d1bade6c0c +generate_signature bc7ab6348d12ac587f301d1616dc36322fc682f1f0f09eea2a11e5432ad2b6dc c7e9d1530adfe4d1faf0fdefb864c59eed25918357405f62d90b744a90fc8c74 189b711171728f1b4a817988f076954a21c9d3bac4179a211b7d656b3d874f0d d37ddec167361c04322a8e5a9408bbbd0152e0825f54e334ed7d97ff1966690c05ce9509a9fa23571ad4691211700a820c2643fedf4ac2761074849cb40ea80a +generate_signature 9293432bf91750082875f17910f129641a2bfa1f8e6186bef86c1f3648f3e525 6ad08ed7fc82ca80b10a556c1fdf77276bdc3bf92e2f9e9becdd6bb00d306f5a 49c6daeb546452f50eab71723bdf3011e1cf87cb47e6b6c4e2e5166704f23801 9ff91c94e70495e99f8ac5eccde0cbc6d9dd8516f7cdff93c43612517d51b602d04a90ede402a104408b1e6a8b3e8263a218ee9d8f708797a0b4c635a890ca09 +generate_signature 9a1ee6dffac3230e3e4e1ee5f47fc6e592ffe5e48c25cbc37bc33489e9baafc1 8d4f6aa6be4b23007021ba15f8b5e876b551ccf66a49333c9521a1c5cd144bcf 2e850b4d320828abb8d94a0f59eeb2b6cc5db1ff86732f4965e0ecf4d5722a09 88228a877f3cba86d5794bd2c7b02bb35d60e39447224c57326d227af9b7280ef965e74d1ddcabc62f89d4a13c7d7842b6c45e86291d20ad5514bc9a2a6c000a +generate_signature 58e28f9221a5eede75a96410234637feda48bb98d73ad06a8c2ff26f5b39f476 d237fd40cb3815b4d3bafba997065d4aa9f1e3840bdd4090f5114373c2d494d7 c0ba2a58ebdea287ce6e21a2eba08402e4fee862a1de53b1b44928018d58030e b6087a6997a8f6b48331e79af8182c167ae14bca8523b3f2d2017406d532fb0255491bd89c1d4e941b8f86dcbc63b9aa6ac6df2939ed299a4c054c4f28e9d605 +generate_signature 928fbe133ebed7ad9f6156e19c7c9aeffc575c33b8de99c5287e82016e776de2 0b0744145264c241e8c439c4d961f6ccf15bb53ba337188d43a677b71cb7b284 495d6e8010bba630c5d694e43987f2f1b58048477aeb3d5523bbbd7416b7a005 4eda083d9b961e357b9e7404c182bbb2b26276175ffd1cb96e76363d055acb02b702478ad19babd13fdc76ea9facf97d0c1ebde21bff1172440b38961338bb07 +generate_signature 250eb4279162d1bd90efdf2023586ed92aff4e69ad3e61a7d6dc7e72f60135d0 8684e1e21267b8e2f23d2c0147f166303f1f943ef33d65f5bf8a91b0b2451d18 c7f27c46c9852d8ee91218ee60c111d01576c1f2f83a96712892dcc638258404 5b0a8954366f168978df36ef025e505c078175729f6ec10d16904001afee0206f0ed5bd3af298149eb4ef41bb87e214b7dcd6c4b8a1218df5de7137e0157c10c +generate_signature a3108d474af228bd9feec79463a4aec53e59c771abe5b47d58032180bb549959 33a453a35e55c67dba186594af25038d30bb4d3e082eb56e7c4acbf6e677c423 1340dc449704c563aee4de4cc8a4eae7f0fe70e0c59266262b5606e86ba0f40c 6c64eb76ffe55b78acac71da48fd9a03bb70754ed21584059a9aeb9109fb5a04b554a35573225636ef09f750f94984679826209abb18e50e05da1895658b050a +generate_signature b0ceea82b9b437584058b30336ab9d5e0212132d288c0c9d447b104c335d7ca9 63809d69d9c776c977a8dd2cb0f993ebdc84e10c83476aa4a2b05d3bd8d24a35 c1596711a14a1d0e3eba38858f3430c09abd5364de646cf331d99985ca885008 b7126a33c5ec6eba7ccef3ba171371bca9713c567d3a5931c8c8f1f9f426bb00370e697d37d0a12d6e132368353a015fc87b4200da84664b0b4d737e64bd6c0f +generate_signature 6cf3a018c314186bfedb70f5e958b53684e3b843ab5a62d2ac69db60c9a661ff caca492b3ba7f16a4e827c1534e2595a1fde46bcd9720cfa8c028f2d4a5de351 1e397c8e3de059c4a4e4491fa332d74b3c580f351ec51b890732f8abaa2c8301 60e1d2a9a581e60af20cdf792c742bf77b401e6433d321da9cbc2d87f92fc9025319d14d141a646601fc185be1ff367fd40c8898db914ac778c5e3e9f592eb03 +generate_signature 050fba651fbdcb18d4f1c7de73744fdd53de1c7a2ba60b617888c8451c5c09c1 f6cca896895a61f7f6ddd912680f4f6d3e25ac8547ee66e495ad76a37e8f18e4 79c3dd12d937e7990a7e0b659ef01fa2ebc3ccb3773c924c7f89024c7e61de04 5d2018e432df792ba7766f01499fd060d2b0b798188ab8c87a0f026ff4cd410dd57a54e981f4ca5bf41f7fca8df0a55eb51b3da315bdc3eed37fcf0891f3e50d +generate_signature 0c9b35fbb4efd6256b7d2c47fdbf714e1fe067f60b7afa9f49db3313a2bb95c8 1cb4038e369caaa6abb7cff7409a594faaeae4a44c2e7bd843d90f98a0cbde09 3c13899a69c73470a6bc0aeedd887cf8b99d09b97bd914ce38b56cb7f023730e a8e9b6c62fe7918298a93b163d439c05f4e0b32b746b6b293334108b7c1a2400c98765719e48f851b0da6dc83594e841c79f0519512ffc9c352a3c427219a808 +generate_signature 3425d36a2f0191bdbc9cee5c2e973e9246f692be127049860d96a21626760094 e12519363f668f05820aa0e7bd9502e8d403dd841828a0566dab1b35610775cb bcee59690a43c97882967f9111226e6409254df559133f434f73e3cd2def4b07 10e2b79a2fbc2b1d2ecc66253f242d1087c54680620ea4d8c8197932082789028164c6a4c567e9081510c15ce83c9c3d8f8b75cb56076c650de6693341954a0c +generate_signature f57623e53665c858c4085296cfb5c9b0caf435f973386459597730607e67a7a5 a399963cf3618a7e6f54e9919fb4fd3e12173c2a97b848eabec4cf1352e7411a e70c40af4d91731064241aeb8b9ab81c9c31569d0fa01460bd63b8d60277020e 1c43d8ffe2c79b4d4f8572804e6801aaa9d9f91f923da4ea277e6a649ad14608942df0fbc1e028d68b64a9b918f13a01caa677a4cda11e8623d662042197d60e +generate_signature 04be18f1ef7103d4bd3b2fcaa76643a992813b06054a591f3ed32dd1cc9d2915 3b61a36d74afc83c8b09c0c7fc44e290c610cdbab8f7c8168bb5618015518e89 a2cc17fbb1055c5c9d537bc09ad92ec57df9ad4a550d4005505fee8d5f8f6101 5e6edd4fac85cb64ca84a4ea54d72611fc772155a069dda16ce1356783f3a30e2cb861c3e02888cca6ad7536d5cf7ef85d7e584d74850d30a32feb0794536101 +generate_signature 0b47cebd30c25be0dc9416fc4281d15377075f5b21411955087b9ec6cee1ec9f 61cc430983d7888a123cf670cd275936e1144b57ce6f91173879ed8b8b25972f 1f7a1444733424d2e1ed1fc9ea75a246b670fce608f1f75ae4d0ff8550fb440d b23b8d0ea24f281fecef6f5771b8f8b7804166a1e7e93636df2aa6aae7e4c9030386ae16b2e4a23d7bf4e8033303cc5bbd070ebf8109d0f2401df1cb8a48c60a +generate_signature f55dcdfdc8206cf039234ef2b4f542b216d9d542973d00b2c936d0e7096fc381 c0c49552e7d4e48967f4209808ca8c39dd62da31148ab4ab7e7da47e8374879b f9e7907766d9a4e49a0d9210d8094fc709ba3315b50d8a0a908a63cb86237e05 0c69374088e57246ef2f94fff9f989392689a5b4606938f434cffb040d923806ea9f5e2568bbe7664c5ca6785783c24a43ecb939b073bf63d77c7412ca5ac503 +generate_signature fadd3694ddf776654eb964e290e600f6ec414ef19011b9845253ad335f154fa9 43623619ad149d56578956385e2977831cc9314e8baf2d71059fb90e4f4b4e22 65702ff240d14fb5885a0bef85633cd4b6c599a6c02382262884705b0fbf2006 450c8a9f2bcc894f1aff1f2ba05cf171b45d7885fc0e55c1abc8e8a645910f0546f8c6423ff181f5f3954833cbb185af860c0be3aa9ff34a5b57a797f4c02809 +generate_signature c84fb835a6bd304d18fa74e2402a503e04190b43a58312daa075145c51d361ec 70f5d1a1634316f318951fbd9a43fcd73afd73c59143ae4b620c57530e2557d4 a0c4aef1a7bcbd8306d53c03e743a6e5366be0e6a194fd7c2ea0d6916fe8380d 4696456329ddc061e659b2fa1d8a1aba1dbc2549e737c43cc77c2aa91997210e6714453bd99acfc620e37d2284ec846bb6bf059ccbffb0b6c0d206bcf1937c05 +generate_signature 41526567e43f93b8af1a169f501980378ea8cc540e1e186268c97f8dde137b49 ba2d31dbc007adc49a749a2461ae2c840feda87d867192ae6c96d2cae69e7756 fab4bc5aa6944eae2914c5039ccab3f6fe055cce814afe493eff4eb4efca2a09 87c805a4f389b672dffb64210129b5406d193546272d03e1a16c199b0cc79d0b670a8ffd7cc1f1a95574186c4104bf1caa6c00569a3a22d32b29dad35a9d4103 +generate_signature dad2781b03d492a6876393390269e9d1c8a32d6b0b98e53050ac5c49c210d5c0 bf490a7972b68eeff2a75872d364020c6b62e23f038b4f6012e6522967d42650 041108da975d77087a16cecc9645383ca7227793c5e09d7e3efb22e246c77506 77f9aae57d274345bb4ca74583f7fdace0498a4e7a98cd7535e4121e877df40071416cce86c14a630b00b108b616ffdefbd51f65823c5686451b29bdc8a21701 +generate_signature 7529b35f5dae5ea87c6601814aa7e141c022441e189b2f6614f5092a11e04f8d 08371b61e192f61b5c43c59362fa484f1c6dcd9a16ec9cd4d6fd3753b64d7739 45d96ce1ccabc68b802a6e2aff07ca28bc894836a2817dec49b85b680cb6d900 b518ecaef2a9742e2ba206bd48be5892b4a6b5e6b0314bfb6bb919a042f2b7094544aa8de4f76dd17c048065aaf3fe35947dbcbdbaf8bf62ff629429ca727707 +generate_signature d601aba62eb0d41d333d1834e1d9d71d4c21c7b1552a6e9f9cd8a95e476a7671 7713a615f8acf20e8da5662ac49f850d4ad0bc1e9e9aea8069553cfee3a4ea31 2f3ce910ad8baa5e2061406185871647fd6f656a2b3ec0a99e5a17034d681c00 4c38dd3eb958e03a684ebf4faeb6f08bbf21ef54157bd571ffc42ed43bb2c508912e2d635867d40d4b12737285163366251790dd8e568e34a03cfa6f972ba70a +generate_signature 063e8c3860646c4648d12e4e46d96099c794493c6cd8743b2fa0f3220a64cb27 5f8fbd3d98529e037a4477b3e67e1e350f5e6b431374ba628d221ea07e39535b f15712414c04b72d45987e17381abce6f15427c857d94278add0f8770b8ec608 5bce0be09677d7f01b07a664bace5969e2dd44cd5f408b13cf286cf300ed040a6d394c9e3d68b9d9c3b7237bdce0248295ee16bee1ed92bcc9d2768131569c0e +generate_signature e9f9b10be9a7d245671a4fd0a4fd5db84f6a4056cb238a464cf92887258b6f5b 8d09347e44c28df511e7cf53241b140f12ef729c14f1938f638c90de0bf5c977 a524a60d52966e7e6c82391cda0fac8e38b05771059ad2e45fe753156700a406 8f9e7ed37738301dfcbea06039dec4c33f0027f0714b2f85fefe63ea76a3f10e75819cee48fb2a13bbcd359548c5997fdd0a51dd1aba027e96762d39370d510d +generate_signature 956468098cc3142d6c33aab70f3aa2337685e470d125ef76adc6a21c8e448725 3ea3f7be647c52cef9e6072d059b57ccb9b272b34baee10184938e1c29a85030 642629a2eb8d7edf3bb225337c1ff95046752d47ea516a13b98c063f4e025f0c 5ac3e907400ca4c8f252b95496aaa025c857b07caa49dfbd5f6e938cc6cd800b95d85e1911c05e417f82776f2c89180ced13b1f278149b4e94e0cc8eadff260b +generate_signature 6a312beea59832591c4325d5786539fd5a3df211e9d281f0a95bd822311f95aa ff021af6b35b2fc59f4f08a1761f87808ada24af4cd7d430cdc0e1583a516db0 79f7a5bccb5d861429b90189a56d82af355ca78e42ce8740468f04d33ef41201 752ab3884fad17bbc27919b58d123efde76e146fd5d1c01c2f0c4433a4f5e3062ca44cc8bc54f61da87c28c3f543284804b117e7f59f55db3845ab4310e7700c +generate_signature a7afd351a4b602fff6095b6f5b4afa4a9e13250beebbcd8de81fc52d056db158 897f21a3f9b97fe200651a95817ead8d3b17a38da06d1916e590ecee60f47f5a 25ca23e6f48b0c0319fe10a4ac223cdbc4b11b002194cd1e8577860e94b96002 17e3957c88dba5bf61f51b85bf501cbdecfdcff4420ca1cb1d18564fd3d9710620efd26d59b15efdf16fe585a220ac8a8bec73d72f5b5607a2c36a87dfc8e606 +generate_signature 56b8eea583a9cfbd334edadeccbfe2e5ba264011b4f1e906cde6d69ad6ba014d 50aa92351de4a4584bd8313d78da479d4ccf93701d6313576f890ad6cf8693ea 07294c792ee31271be29371156656a1fda0129931d744a619fc5b82dbb1ddc03 ea5523743d004a57242ce015ef2806f87bf55f111d271f5f7ed7c167716cd606a56f195056eee265e96b02ca6f39ce793a6c9ad1916a98dc0c58288bd9477d0f +generate_signature ad24f4437348e4487e4258840ae60a6650756fddba391b6db847effc10c3b4d9 5429920e3c13ee7da1a04e8154a74e03168f90d2fdbb4ed185de978ae92f659e 556046bd36029c27b6278936d2a265221d720c532249ff7b5ca24d5119116906 e3c327465675a0bdba58d59b9414eae44401ab86b14a2025933a0b3a5c92b4000b56fe5cfb0dc6e76f80b570a98a67735af88fd3e7741b726c2bb2e899d23a0c +generate_signature 0f86c062369617a41bc427eb1567996f6c3404b1f9a94a1aec2c383112a3069a bd776f630e1807e7f350d5dbefde0f96cda41888c534e03560a31220571f2d64 0c71f2a3633e55a2c4526fd472932962f91e86924af42ea7636293869443c80a 9db8b89c86b96fa98449aea5536325750a0599ea7a4e740adbd78ad6afa97505b433705a8e9128aeac9816d6bf1a2138594207c07ff12ccdb96b3e44c132a407 +generate_signature 0e17f4713d396c983c3f0295c39fa682ce924e0e57079bfba0e1a2109266c63b 5ff7c0becb25bd895cdba55fdbc04715cc8fcf769f9db51cf5fa29580fee0ec9 45b72601ef3938f2e4d43aad234d3cb1c2d628659c5b82fe5db0362ad1f7cb02 65e3350f1caf7346132af1454321c0997a0af7d88cc22ac0d90bd07c525be40c9b4354dcb5425e2be796fd1a362f035effd3e664ea78fc6c7e42f05f4ba35e02 +generate_signature eab9270f0dd82bbaef651da60574bc783dad0aecaaf3b9814554869179761a17 038130f3fb26d87dd8fd66f6210a018248134ba6aa693eb1beda17ab4b4d1aa4 c002e957846aa9bbf00f28dc24c268a6c383857f033a7eecc544812fd19d5805 95dec45589c08516be25c718f7ecdaed48769f1289ebbed2a2ae823d6c49b107525175b9b387e620441b657f14cb6ed0a650155216884854d90078687a838302 +generate_signature 779c55583946ff83e64c2c52615638a7f8ab11f0248aee16eb9e42296a3d1e24 fa09a59efe46828de3119ca6bc9d6a0d63726e3ab0720521dbbf5ec484bbd1bf 71d6f89383d089b1f6509fc05a35443236fcbe22f0974c85b85d7271bf6f6006 03e6e84ba1254e1fbbecba8718a694eff7b8772358e37f893016e0cfab354a0b2063d7bba38667ff3d5f5f29a2f8512af7e061a894a18f201204cd0b23ac230e +generate_signature cabbe4d601b4fa95d3f1c2ea9e6c5c82474d3997f83dfb488b3cfd729780629c f956c281c4fe07ac8ba46d3dd22ab6bcd517dd0c4d5fba092aadacb8c9f7c6a9 8f3e2a0c0673d11955ef358a58be53e2f845ac4302cefe97a75f8aaef1ba7208 b357371c430fc4b70839fa572a52d88b279e51278117f69df89d5c88a580f90433fd4c0d5c670e63be6c2510dc235cc14c1970a5e0fccb505cc9138d2f15620f +generate_signature 89cb74d023296ac71ffd7cf203d6e0525a3795675ab4ab64cf270d4ac15e7b37 c140914153a46cada7260e37bb2930c5f395dbe4d34b4f105194c4204f4f1908 b4c97d77a478288dbd1134bec7c9f4bcbe4c991b0f7432311c1005223b583009 fd0e9822ca7cc2683e851c88213560351e1ccba4d4fe21f34c155531c4d0ba0d81e693b48a1d6f72e930509d04b73e94f13d30a0772ce981ecc3437bf107a20e +generate_signature 36d360a3ea0074a3bb3531d3caa29d59ad856c4d205463e64540e151f7462aad cb475a3dcd97c4307de91ae720f9c112d0ea570d43c95b0873df5fdcf93c77f3 0da745980245fe54ac4ea23195fa4952d5db5bae3c87e8f0638799ed56450d0a 7a15203cad36bf9e7010bf74f68ec396880ef8c4ec1588b145256b2f130ded0024ed556dcba1c7d2fb59802649a1bad94e3deb1c8e110776b1192a6ffc56bd0e +generate_signature bd3ebd64d4e1e4bef3ac124139d331cb00587dea763606101c5f8b9b319a73eb 937cc6b3624fc63ee6020f498f31650e738b33ddcb9475bc5f5972b8b2b71e0f 374e8d3774101ad0aa9fef0d00b6b93e83336d2c4e5413f4dc58c0ab392a090b 959c5ddbbc51c767d01248686c6506da21718117ea43a763c7682b389579ce09a3c5b68d31a6d33aad8302195039aa8925a16c86570f54783f6ff97617e62002 +generate_signature 20bbdcfa5b210a3ab56a8d0741a571be41c32f74a9d006ef22ab2526dfb315ce 6482f9b991491cd8efee24d98c9c7f71524c7ac7001e64b52d2ec2d97ca95b79 66393258298f55af28423cf5e7efdd8a9c7c240da55251ff628ec824e926890d 3105211e64ffe2ea3ee43fae3b1a7b3142e0dbca1292052d2051129f8d3ce70cdafcd113bdb150fa22bf768677b2a5c47b279d0d32dc2f766c58ff09000ecc0d +generate_signature bb236eddc6019ddbe80bde4bf6eb3e46a87d96a11a32b708b76ac3e42d52bd61 4196e072ae8f91cd2b642293c59acae05e5dc7485bb8cb41f6a258adc4c61306 d4d6d69c6e412a6ff028c0e36c215894424b74ed5340a5cbb553c0de4bb54f0a 0a6f0a40c7ea9d8ce470204f54a933de91915b5bb70aaafab5a0be1c9867ae01a35d083911dc486101c5e31a8388e7164c62d447b067d5aa4849debfad6faa03 +generate_signature f92a9f30544bbca6e320a2729288d113b253695eed0e8cf791d701e3b5d828f5 25d62242b0db38243942a186780bf2924c2a120317c6183ade656eab8b4ec19d 9cf84ded3c8e73533583da60b5a88466d6c837fe8aef810c4b8b705dc76f090d 699cf0613bdc7bc0971600b992ea1bab83a4bfaeb27deeb316f69979324cf2004c38f1a1a6983bfece78b58c24823a66b5bc6d46a86d99a5f542a93b54967a0d +generate_signature 117204070200aab919ab1a2f09800a2a4e70ca4744b8825faa8fdfb5960f4566 9e2264eafc4ecc842b8f55990d0ef49bf08860d6fad7928f715214f7bc59c6c7 3d9583be56b118a91aead2839686db1fbb76fe12e81dd0e8078a07a2b4a87d09 d31aaa9a32bc1f8eaf77d3b42bd9e6d77da16e3e84de8ea47ee60531330a0f02f23f9c9672abf1971dcff0bb5b6e86a95898dc83b5e56fd52699a42633427d0b +generate_signature e11b1e846538edc5f35fdf14ef7145aa51f25d28bdce749e3159f1a0294b3554 2cda0eaca8b7eef7a8cbaf76db6ca274a7bc1e756860337473a690b04808177c 3ad9d55895744c3599bbb8995d31d4dbe28476c8c49646553fd8378d37293309 2c7a28328bd5633b60813bbad1ce3af094dc772b74598d6da1c7f46554720c047164f053699aae6b6d0e338b91903c459d1ed4e15cd1e9949aaae95500b41208 +generate_signature 1971731580aabb8013108d639f2d7eb8f6dc1def7210b3bfc4a5d29308841b4d 250397833c28ea3e685cea4f0e1107c4bee99a45fc6a53fca9b6676a2bada93d 54e5c4fce46b113f207d0e7d2db8bf2b96aa59be2fc9e74e3f7ea65caa925c0b 02ae096078e58b10d9b4ed21e9492f180af392144d03c80449ddbb7c3f8b710894f1498d0e40a9112dc9ff544361ed8a3c03744255c6d27b1d0e1b7f91a7a001 +generate_signature fb1793c911291e0a48f197d4765670022fc260dbf3ad33677bdfcaf54b9c049b b9efe0f20b60d4584b0006c53b44204d737ee31c6a4699f76f77ad01a533ffa0 a818429bb9faf9ad0f2393830e459373090abd877509b2a34d15248d88aab407 c29af8272c8974cfa84d5426fb029624ea36391f98c0901a8107d91a6914b60ff85de55552baa80ffa808a6f6412d8468f5a28422238c9279c2fd6fa50f78e00 +generate_signature e50318bf682eb86eda1e91c716f6153d09e487192b6a1a04559dc87ccb2dfd82 09531596456dd1089bbc21ab57532723951956831a1c084ae755ce5c132aa337 ea50ed7001fba1ea790ddc65d61cb6e0edf03f7d794fc6ee61aefbac69deb103 d4ae598558640f4dbb7d23e21355839fc6a759b6483f1fc305c2bf2c2f83400d80da07ef38d06fb6572a6c20782c30308b038e6be4c1cede1b2e19e56f22d70d +generate_signature 1ed97394de2e3e396be18688ee450d8c76cc8a0129163de7a3b3955412cb4715 3be01c17990e292609479a486adb2f06f59c1044505b2d92da08b328251e0580 ec06b9e5ebaa15ea15e0feda295158d7c8144410976c2bf5a3fe6a35392ac60e b68e38e3ca15bafb3c57c5c2af409707f6d0bc7868da77aca55647761cce8508c60c957073c692643ab078688924d6b30473207ae13ab927cf2bc54f1aef660f +generate_signature 99bd3f98492129e6fbb98f390e57f3a874f8883a033a5fc46a4f7f383e418ac9 6efcc7bc1c68aef675a930bb2aa777fbacde41e14725edb0209aa1c1f42e3778 8ed3f9201af66db49c393f604edd378659dbe8b14e531484b3c11dba710c0a01 f7efb4600ec92068dc949dfe517ad362142311d8b0f42295b5b399399c9fc10b8625eead8f960acd1b6f6d0e1e47aebfbf7b5bcda7fbc17931533087e1782c03 +generate_signature 8252830aa7e0ab6fe4a0ea03aca1b52648ff87e028268e2ea4618202e28e211f 0cd3d53dc54800c05b664259dbe57cdf9c2bff6fe975a00fbc7bd9ece270f02b 3884657955116fbc3a6a9d7dbf0c01054561dd3bfc32ec5dd22df6a0ee567d0b 7756c3147ad1684b0be16b791c2d868d9ecdac633938156a7347102d6c0768000671e05a78b65e43eacc1a8f0bd641a577893c7c6dde2f723c2b4f9a32a1770e +generate_signature 77d17f7ba5e635b021f067249baf29f96a0316b0f48cdc83258a1a3f292e4660 f3909c4c5f1b70470df51d39fbb1154fc36dfdd28f54ce7046e2c433693471bf 8642ebec92ff6659b8b02876de216f68796487f783cbe747189c813a03c27f07 5d186e56c6d5db86ab375185f90a0cf8f6c42181325891f381fef1bf763c180bff99c9f06661379fc3f817e73d8368d5c5567f80cf24c03d3d8c4263d487020a +generate_signature 678030de9b9eba9c3888a8f6eb756c118e678a9868c9e72a3a28fcc956b9969c dc91f8694f457341eb76c3fe451ce748a0b6fe038d69c361ca0bf00a66adfacb 927eb9be8682dabd036b515218374e32bc053ce764bf0e38a69816c4de803805 3389c10c2bacf2943ccfda899e581dac1a5c58d81afbc183d92f22d9b926170da680153fe932c2a7292201ea61a313c83cb7337e59c1e0322981600be60fe509 +generate_signature 77c277d78d0a7ae05e79bf99e13dd19db299ac0fe195b5602432f9a724390d3a 55c344a46416ab0f8cf3d6489f2bcd2f1e5dc9576527b2bf67fdd1a5e24fc35e 37e1e254f69b062d3dcb23a8e9907c3d02df72dad334a6cddcaa6a1c865bc50f bf2f3b5e8d7be32125cc106148560606e7c20ec6bfdff2dffce2acdb2851b300ba7cba1db86090fa5cc57efac678bc5cc51f493e55b958195261ab8164407400 +generate_signature 64c9cba12fee1f366eff3996303a381c0047b80d42051a0175305a32b7edbc63 ea5f63d765091ae508398f31aa01d214d47587bcc1b17d1ca74f3f8076d54931 5feff437c8cd6cab5e09db49bd99d93dd9056f7a8b545ec8abb97f4474e64609 e9f28203e8504c88f356f95a984d9b1199afb2d91b7c2ab23042e62b708cd60d6dbd61c87d3de11f92711ce70921b3b83c0b905f0e495130a20e1c005effac0c +generate_signature 181a8223c0689326ac922118ffca8e0198a065881f7dd6f7949b1b90fbd4fb1c 686c26828a7bbf98b3fdbf7839dc70e71fb62c5a3f787595b6991d45c16da74d e0cd78f0d49b635e2631b0ffb90c3ad8bcd63455d49d667a1a87fcfd0bdecb05 707583621b9a2127e0e23a1cad1e2132e6c95d8a6d70bd3093699221b768e50e488ecda16bf634dd762cd727dee9169837ffdc11f4cbc2bf5219adf41c80d200 +generate_signature beb062897a93bbefda06fd3310e0dbd68b496dca34bb19016b7e0b09d594ca98 1355b5ba2d2a51c4092c45dfd9b1c6013f88c0d6d029135456dc617d618db60c 02b6674553673a813f3836d07316f2879c7c28f9b9d2675b70690a58c35d100a b44784f2fab1262af406b40555e1af8201a5652f9b55225d3b8f87df16afa50801c64f279ec35a0bc16f02d1e273285e84de925e0055bff33ab439c28cba1a0b +generate_signature 41abd216ac181ab4b5ab5e39e48a160037bd6a8e112aecbbfb28e4f8d088165a b82c1a6e240315f5a10a01e2c21461f19a12a99ce8189cf5f6b5f1d29c1bfa54 4525003025ece34ab218047b517029c1716c295d3787a77eb5dc527cdf606b08 44d5097e86411f1c7f529d6b1131b08c3bd0c7c74aae0b0dc74a5ceb95c9b9038a6769261472911425421ba75d7f71283ca4fb273f3a7bb93fd22e34cc7a930f +generate_signature bd2e0d65de8e3eaafb008e20da6447b5bc7264403437c4a54c08d2744ed318ee 3ebae3294c8fae475d4232f9399d055952d17c3360b882fa03595bea03729971 de6540579d614c5317abe6950b8a63a31f50db418f7d6dba387ea5f588cb8a02 8f789940c24ee763c69d9828861436ae1e2206281b708cfcaea770b46a90d101d262a70ee6ca104a7bff4925486d3dd25cd21825317268dee34717b0a94cd403 +generate_signature cd2ea6ceebb1eb858ce1dc300d3da1a967b48af8f02c7def98a419c2f115e57a 75edd7b610bfe4960e60a9f72fe0f0a30511fea824157752060ec164781ee0cf 5678faab109acf6faa11ab96edb708b6f062c1ae14ec4665657f74e1f6753e08 43c9183be1498a1dc8a86e2022dfcb3d91ecb6adef803313e20f3627c8f7cb0adc081aa0d3eca70f7e4abf982315a7a054d1f10054466bd77d21818584653a0d +generate_signature 5533e192202b2876a843472c84a672ad8fb8656e315e1960e8ab7abb79c59d2c b195c606a04722a36fa7eddeb1fc34a3c97c9e8a83abe948239b5fc352c6127a 30499c52311d98764984d7ccae60ebb8769ee53ced3b67e4eb12392b72a0e40e 8d5e22db1fb76e36bc562223a56f8048363682c1e3d8133d9d32d47bcc7b9b0b02610db13c6988c3656ea557b9413af0fabf11a898f33964cf9a51f2aca5b702 +generate_signature 7140f13e794023ef8992f4228bd1520c287883cef755ddbf424cd4394ed2db87 4b981a07fd6e8b2bd3bc19ad4376702c6e6704320bfd9bdd671c8613745ba9cc 886f8f3f56f1c045116688440cc6addc289319dc61fda860a876cdfc1370f407 e0b5732d629ffd36df56c3e7629919de9d940c44cf3f09e47ce9a91656cf1a06d5a36641fd77347cc8a08abe87ec15ed1090d4a4a9662f3195ef4e42e4883201 +generate_signature 250167d0b355eee2a7b28e9aa3b8c56a5a95578aef53a1f7be9be8eb0fcde8cc 4eb77ec86f63a73d5e72cad08d4654aba08c070d1e0b36f5c16f2c9f762aa594 656ddbaa9000985f78ccee51a6cebb03a59f1a7e2fe18cc5846e30219efcec0c 19498e3fea73a56dbff47d2970374b450b8f8ed200440cf14e8d02b537579a09b2c23ad9c43d6ed69f4299c44c382191c13d9513e476e7e770bfcefa9cc22401 +generate_signature 1be1015cc327418c02e342cc140be56c2670cd25319b7aa80e9ac595e3eca22a ae039a2ed447cd898f604fdeb4cfd172e624f1b0da3cf9ae98724fac92ab5c7f f357433c68256eab17b1b58d517ea364d2d688c79f6de17370b73633eff70f0c ad7ba150febc1312b37cc05fc339c3ee94f7f25759d71c02dd1fa66a3403240197aef2ef07b68c0a9d359309ff6ba2a85f52a0b4c8e2a524702b680b9e096607 +generate_signature f0facda962d9604d3d93e643364bc7a48d997a3d83c868f02120f30dad915a8e d861406f0f5cb83efaa6376ca3b0cf29f276f8f986d17c48be3fa3006b73e60d cbf68db94e86ce9ee220ce32ae1ceecd2218618fe3f9ac93f5f99ad05d911f05 c7ea786d6252a5b900c09da789fa9072b91f23f0c65c1316f8cdf3f960ea95073460b7309d76167f0f63e422c1f5f950ee746a154af725fb12a24fdd5c1e4307 +generate_signature 8a79c9c10d1b9e08dc481a74436f26d423e35e8806ab87a17f22bf2d3182c8ed 550a14bdffa7ddfc0c734f6b31d7f90cd6f8f2fd7183d32d8cc990bd0aa0798e 551206da98b909914b9befad9eb3741841673d81cbb6bf3c1160411aa2d3b10d fe2ff735afe0675316a26f503a341b53df74b69c175004803114295c13b7c708d4c19c687d265313888629eefd607ecbfd8c04d692bd0e01202955c8e61a2a05 +generate_signature 5f54781fa8654fc1bac4dbdbd28e5d5718b0aed9f9acba5a1b78d5caba395047 30babbb9aa1bde130c312a6d22959af359f23e8c0d5cf1a6eeee77fd57df4a8f e010aefca8925f858e6ce9c15e9f2d784b654dbe02c3f6835f5064d33da2a70c 829c2844fb42fbe2211c2d622d7cad27ad46c72a4cf9c995a8e2a1f3ee023e058af2ea2f90784f14237c618c0f7694061b88a68bdf8f9aedb7b389109aa82d03 +generate_signature 4ebc7626f7878184faab889e039cf961953bd181491bbeb5c0ebabca23476fcb 86344a72cfb90c1d6be168ff33edf9b5f0318792a21c9663d3b35e32d0430665 7adb248725a0f38f9e0724e52a54212691931fc972e1c8fe118c2f3f36bf2106 b5175b91e414abca1adef47df31fe9e16cd138771631159c81195f9f2b6f0f01541059d4f680bdd4b50b648e91b82a13a53efc02d152f5ba5ebc2ea07d623806 +generate_signature 47ca095a4508e65d40e2e983f456e499ae5565512bff18e298b1369ed643af3a 4d7e29cb931257d5daa7b92a4650a275c33e820dfc81e308f96ef2a775fc6561 fd1395cb2a4d672011703751de7326b09a0247efa524584ac4557400c07ce408 77ed87c2d7b9920a960b6b8c69f14e298c912472c1ad5f668e8340f1ac0fb60b1a506861886c4e9dce3b190634546188703c6e0b9a25163ca173e6eeb4242606 +generate_signature b08a3aa4e56797119a6bdf402cebcdb92208bbfdfc06965e32a15c51519ecd48 7b9190a2e9fc8251ecff9abf9498d81abbab5d5a20c198600283a917aa6d69e2 35e05e8daafaa0d1d13ec2c67c1fd7409bea5f28441047c58e38c0359fb68b0a 29b5f7155aec0f2f424d8682e9e030ac1815cd693671f3a941e3dab402ad5709fbabe42c538319f3c3a196bd87b9fa7fe8023bb218e5042ea439332235ce550c +generate_signature c6451426de9e3b2e91cc51141159bdc353f6a7a8a4ab2ac3d3afcfe3e4c98460 6bb011b5ade7e88c074b2cbd4fd86e5ec040d8a8cb1d64a95ee74a147b80bed6 bcf0f5512a5bf905e0e711cf855c43bdaa226dabad7bb30f681ef31f94481f08 d26f89cc5a8e23d8bfee13aba54a8cc7000563266810a6550f3b3842432a9c0511e8accd5ccdb53b67871e87c6cb268015a9c890d32e14180996dc35966abe08 +generate_signature 3986070084cbfd352ed4056d723fff061bcfaa3c9e42c412219e90a0f3ac4399 09e1c1252dbcc744890d5c7549055cd37c5611ebdc8b9b149f50e806ddded0d4 0e886ea900b0fb152c456052e20ee423c391539944b214328fa3e376050b6b02 cd02dd9b3a12dd7f885a246c9d5d689680d716c3b981a9f11d5940645b9517053b2f713a4395620280e237b7d4a704e901740d259d66a9ad7addb9b30bd93205 +generate_signature 09f9aa96450944c5e39a39ad83f22b5822891d60928823fa1194c6aac5628013 54eaa0dc1b249b46fb742afcdbd723f3d11d509c510a20e15e91f4650248daf7 503b75aab65aeee3bcb0ac3998c4dc252c59f38f26467b8014ef5387fd71310d 0b085f7201692d832511717a6541258b1a907b0065fbb5d24b068d34aefe000e6658608954b48e6dc3af956835902b261dd9bbf73bc0d1f1e75e7ef2cfb41603 +generate_signature 83b7cd78a98a52deed578c47be164c85507435c8beee190faab1dd894883c099 9969defaaf5b420ebc53893dae580af72d3cc2d598cd655171271f9640e306f7 cd3988186473236e0cfbe249e583a30f15f595870b0676c64e656f1b4a5b9f0e 9c41a9078ce7d345475fa42ef7a6e6f5630c471233feee062a1e034d01f2570e234fafad2181473880ae485e90a254a539e1705a91c9446f3515d7dc1d7ab301 +generate_signature f235e6fbf083c6535c225eb51b687236e53a82cd53cecee54059c6699f0a7f34 8f91f51794400cfcd52213be29440d74ea5639058d34d07f9c3ad33dd6feca89 6b366e032618366e998cfb6532c20d8e6622f7465c171a5a4c50e66ad296f508 563a3db02f21cd97b7cae61c779d8efd86a95a0e0ec7bf14b1513df235745d0bda301cfd0e815b605a95c78d4aa2e5bb03fe764b39c9f8fbe81ec7912180720e +generate_signature dc65b2621b6259ac7e8b83f774c870e7e9267c051579233407b31fac058a8b05 3833e47604978d39ff707b2c0b8f9ce7535df6e73700b887f2dbd81d10184819 4d502f6b880c5119c05c3e3dccfe3238852f926c9ffa6881ea29894818be9e07 1a412b2d0432a9b0177ea2e97b113f05cdf14fde59303e6418b76284d00380063c637e596efbf47f463ce9c52311b09636bbbc9aba9cdcdb22c3ed05ea44bf05 +generate_signature cd9fd995fa233db9e2a2392b730d5a05532a8c692db08a722d9444186c751889 8262c6ca62dcd387b780ca8d680ac3449f83275e404fc138ac4c821411b5e3fc 56b37427c84f58d725dd60f62427faa3df956501459337a8757ce36bab17c608 35de8e5ccbbb34fed60c32e1c16b5807d746a36cfd81d71a0289f3c34b46cd010eed02ccd44edf31a2a4260f54a0858eb67d922db1a6b701d68c10cafdca0c0b +generate_signature 3146f0334ed3f7b37e426d42956470df1d16162414da051e013829bbf933f0bb 4ce41f54579496e3caf783beb6d190ba18aa9580a8f3c7bdd67a48b2db39f4f9 9c4851e02fd002ee3582773b5828f4a3d2d9d443619529b58521199587f1cf07 19018bd8f4f6f364871f1663995fcc74305bc4f0c5ce224f30355f60c60a9b0948103e035f3bde0fd8f10e1e4181ae1c6ab3cca0be47fd7622b2d0823ad48900 +generate_signature 2121314a2b5679d188bd2556a1859664416580dfe94cb7b9ff30a84129a2b3e1 aca93ed6c8fe186d77aeac53800a9dddb8b458e6c186b334f66c4a5f8c1051b8 579f7f571fb930bce9b727b862345f7963cfb5171360809528f35523e8744006 e151d4559c4eb04828ed6622598f3c0b3d6014dff4fea4846efbf3df1348fe060adecca08594d1ee0e54f516a17a385c0b781ac36bc0c4b90af6e11c8b765602 +generate_signature 59b2af982dc20ec9913aa08bfe1ef2c135b78ee34b09944cf250c9158c7b0562 75fb03f855dee869ac12a5573bff1919eaa1e7d8a2c3f440beb9d09608395544 9778bbf88a2f63bf4984485a6611a5bacb963ff20578415608e57065388a8106 e750f72d2eeca08cfc65082fed516e06db0d533a2dd471d6d149ba42970344095891fc2430950321ed0cbb4c19e67e6e9af8997ee209a32c2c2633f27956e10a +generate_signature 6470cee1364f1a12046671b3a0e3e6c34eb0f0cbf84cec3177906be6a2cb2adf 2e151ab2141db362d0619de69336be4272e4f210345111313f81f6cf39c376b2 576a19a58314250abccd68a3f7e03ce868d099dc2665e2f5726c8cdf4341530b c7e79b9c7805f7b9cf2f77bf8551271dcead7211f4a7f7508630e2832f779909aead48730e63b0f5c19527234645d19d4576abf0194d6bb7c5968fd5144bef0e +generate_signature 8b918ac29e7fcaaeb535862c009c37420345ef40ab9a154850c318ef99802ee6 84568b77a10b5525ce70504b228aadfc61d9c1fa86e9bb753f115dba202660ee 37d22076a2431e927086e5129d2e59dce38ee5a1573a6b2d8d1a3ecbc3c91d09 6eae67a14dc4a886e7aa3f4a615e932fa13b7519a5f385a557792d4e4ab224074d85e483458ec19a9653516954c3b174dbda577f4c298b911862a10516b52c0f +generate_signature 6de4e3f35ce908657b888134a842e0bf07d83c44fb7e79894a5a9f979c436770 5b105e2f08a1302656c06cf1ec1adb95d99a10144055f7f6af04b68ba3b7bd80 bec083620ba91422b6d9ad44250d9c15a10a3926d485ac8669333e7132d3eb09 40442bf90ee19c54fcfed38b5c871447d17f5e8bcb86d7b918e83aff1538580dc88ba8d17fcd7cd0f16e24fb54b5132d22cd28e3512b51f3a4d3aa40b2854703 +generate_signature 428d0c3a0790f3a08d81f573f301651372b2ec705f12574a19392352a433e9f7 cf8b8fa1f56aa595710d513d06646df49bd8b35df7cb7b33c7d85411dc3bbbd2 866a67036089df01228fc89bacba4820f78f1d64649cc5a9ed2574e511f34c07 587fc8e79242274c3b0f52f61a0b9dbab5c20be6b897e79a3dffa8a8182aea0e39c8c33e6a8a09cac4bf6868a9cec4d7fcbd17c1e9033db1c8d75fb2c7367a0d +generate_signature 891f34ae06a2551cf5f1592b1e63c8d68bb0e29c21afd091aa7bb66d650f7f7e 2be48fee977901919728a8782df0dd5b149c51a76b8ad4023a141b8101b29867 5b77bbea2c0e497fbe94053f5720982a1d07e5ab6e06ed738a0f7c93b612740c 66a87728a9e7f36b95bbaa9033abb0861d716e7a977e575d883095b0ebdbbb05111104b2e1dfe648e233472e7219ffd183978149fd0c876b3ce0ae9ed19a6908 +generate_signature 6ec91eb3d131cb75ed5376911945faba82369810d8122b1661029f99e454f3bc caca892b7ae85ee023049945c04351cfa4fc0b6941c9b40c9454fac84287ee15 40fa7e85f431fcbdec1fb7f21c02c27346471b67bbc589050bac291841265303 7d32823e672291c955dc6a4aa0073b36fca7355982ec19fb0fa760f1ff7d480ae1e17f8a38cffe9325787303c98e1455eb1f9af8ab94ad80f2b11dfebb7cec0f +generate_signature 4e5a5da743f5ac99f8741c46548faab2d8c47d1d05e1717c3c5e09b19b21dd05 8a7a088a7fd4d7dc21ab874269a88b8da2facbd56564511030984bfac5dd75eb 01bc5f09cff86b98bef89295667cf1a86090011568c4b03162a99dc9493ca10e a4f1d2f88be98040bfb558501dd043de5877e2854fe64833611243535d86a60416abb01f1f826784ce8b2b9be65a60058c77b5daa511231181764e4c0fcfd107 +generate_signature 9cc42548bc6e6e34a19a057bea5db27a934c078a6d455ff8b9a883938a23b55c 9e630c49db0a23ef056bc1bdabafd8e68f7280beca5d951b726fed30aedecbee b29c707d31e9e8c51f81fd3cf068d5fcac4eab2b8b980e839cd55486b4ead602 511e427adbed91a3e7b7694845bac03f77aae43456a9e87b7fa5a486a54f4f0f3b91a42d116b1e9cb558aaf290c9f0d0485e72c3202685b23d3f28f4a3192601 +generate_signature 4c276a4a1f984a111ac26eeb4a8e65ff739324dc8ad15e56abd63a6204a06379 b5bf38a4b0a9234c4f98086d6c6cfed67859be06f67ce9ec379bc83e0ba40b84 919db5eace4f328af88b950f67fcf435a58e4d2dd00798bc431115ca3d3e2206 c594a848901963d97c0f93ce2a68ea2c343eaf0ccbfc3fe2bc9482e70bad8d0698373354d737faa0b31e4349b3551f194833bf32c4294ff75b62106c1d02e40b +generate_signature b534073350e2527eacc2f1064b0910d01cfaf4a5ab87d4d82fa14a45c94f5ca6 b7c7373b51e051ef2e9234d2e76bae6a6ad17160c9cac1f55f4665c3eed499c4 03716be3af04c61b7c26f1dad54283da440a25a5e18b803d97568d2737e20b0b 98899e4953f1de2e48263f0cea9bd899e2af4a68b6b1f8ee5c60c2bf41d0f202c6400a3d93b4b29021d8d80731f35c4f5ecdf06c7f376c94c83efefabf893d0c +generate_signature 1ea2981cca992687675f668bc90b753809e9f6feeadb138806dcc9c5a06412a9 626fae2fc95f46e2473dad6165e4193651a4fa4f642b4984aff91c39dc723188 89a112cfeae28543b6173ec04e9a79d217953ab8a1b064d9d657ab80693b3701 3519e5621fc02e31e92fa0a332b6f886aee286a0f3d317c9b4853775614fe908bf2638f95bacfcfcdf357a998aafa9d2f2ef7b866924da0d820b2174a14b4e07 +generate_signature 504d717ec221573c9e4ddf4d496e5debadbc1ce24c5b923aae4cd250780cadb8 90e2758536c08f8f990aa6f1f9ff74ff3de165a70b5cc68efd506a8b3fbcc886 8b8aa640cfda81cd5e9ec4ce519e615561fa5b9cebdcae51c6ef99fb58bf1803 797fdbbccc4fad425ebca9c10bd056c17d73e943df1ddbc66a279d1b9eacff024bb75efb4b7f55a955978b0f854c83e711d27280b421305c71ba98c1e369da0f +generate_signature bb87f19b04a944645d46a55c31494164f700297d48f41e2b952b22a518173d0d 4d2e3b4182633d1f814622c21e4cd7aee1cfcdb3e390d8918f644e3c1d9ae375 dc89bd151f1d13adb1d564b2429a60e83758403ec5af90f942b6a29860c41c0d 096b49ee4dc161724d9231c41bea6b54bf95e88528bb63a968f9d6aa34303803872d1906cd43229d2fb85e7141ab69086e54d990c25da7579eb9cd4ce9402904 +generate_signature c2840682cfaeb76d4daa848743d8af0e0f5dd83f210a8e527d26316c68d430da 459387d746c8dba28c6c108a30fada502b362f3e0d1345a4b0df077cde4c8285 7c4acd237ec081b8c8a9c3de26638fc43d224642404781ad06541c3456118f0f df1b4f4cd2fcbad5769054fe2892abe768a2306a7e238d77a86357493fc20404dc64383cbf38082cd695d380883568355adc27b06381dd928b996ccd05ed7a0d +generate_signature 8311b0b06389082fd0dcb45f5466d9bc0f7ff9f7fdf8b3b327c005548d79d362 eaae7d5fd70b6090b53eabf32ae7c43c9008dc41cd1e0005f8903b989f33c765 d79dfdaa686f93de2f11b8df5e42b54537bc0bfcdd9affc52024e842d6757c05 a959bf602929a28725fe4d49e79b08b6cb2d58a3876edfad038913f4d7db2f027f152a353369a1766e1371718ac9f0caf1e7adb874a118463fcd7268ac07010d +generate_signature 84ff717f1756dbc6ecc40b86d819a75203af6213ba20cf7f537efa5e65c9cc03 07bd8fe674cf8c78acecf2b9d308c64490efddba1cdac2ba0b1c724582130c30 ab13225a28539e69b62adecf4414c59f73203c3fd53fea4f2ed013ffd99d6003 cceed4edd8c0c39b06e0f14330167ba07c00d6c83b2ce6575fd5598e12a0730eba22af0ed7f91573b687b2aad8efe4b1189334c7d7b3f4f0e30e7848462c6b0c +generate_signature b74cb606d37ae863a89168c871287f56877b24da39850715fc4360b8d18f7aa1 5edb3eab8e691b84d7a225f9d6b8f8d7f7fd120e08d614ea53bc7b881a26ec4c 0103cfd229d54fd7d542ce737beec314f44a1582b950e2ba26722e44bda0b308 822b568f754450f1ff4ccea71f6ebed1f3dd4ed74a119f8c06dc6cab590060058081712162e1560688d950ba9acde159da0eca72fec6c640586103fdc1baed07 +generate_signature c7ed53d2a48994508ea36727429ab37a54443eac20fede8b4e158543ce739d74 58bb32f737cb1ea95e72a41b160e0ea266fd4392a60cd35a2811467c5bdc0bb2 ba7f76423499c1e1be19e20a43805e730c61aab2a70c2ef1903a88eb6524ae08 481231a011fb613da4122dacd2f0856e56146ae0532bb79ddd50921e008fae093ff6e831e3154dc0567c977f0326861d1b3bb0566ac518040e190153eb3a5502 +generate_signature 0fc334d324c6facd4217cf152ff28788085c56c312bd95dd55a16a02695a9d3b 3d66494c5d4ab4dd49adf8e7a8568dd83d63ef6db53be9f3795b65750dc3daa3 6ee142727aa4c0036e6d77805c51239cf888306abf15a73e8aac24ab6d97e704 25129ea2beeb8baa69fc2437e4de24e6ecefff2b72afd1fb647d68564ef1090e9b77a795cb88e2e0c9736fbf5ba1758ddf104fd6aafe5f71ebea59f446301901 +generate_signature a66d18549ce9b694953cf20bd6631d0c7a5a5b9258c528cae3a215324709ea33 0b78d05abebbedf136ab381ad3f3034cbd2121efa88cd8b83d99b93c5923c6af a6dd28b9ece76146fd8c841a3f873e902360313d9546cf5d93a7f00c0216d908 861fb736e015f4a783c94481518e1cd011bb6226ee7fab8d0b04aa9396e049055cddf216e13be277bb9bb4cdc9553e4c784ea622ae8cafc387cfe049bd23f20f +generate_signature b3811abdfbef45084d373790b164474e8b8d533a57b22072dea9f7f59abf1856 c3c37abf4f4a520ad9d8d56e9c3ef2bc07333e1ed4c90ea75fb5514a209c3b8a f09818d60daa9448138b132765a80b7ee6b209106d53f11034ffaf7f4bc02409 61b9b59cc3bef282c71df3cd2a9b857ad534b0c752a207be6750d3b937c28202dfd5e8dbfad3a54abaad30cf0aca8d354d4889737c099745c057180798b95501 +generate_signature 5c3c72bd5ac1bc5176400ae49c01256f671e3efb18f2cc31cc813db4a8683f22 d10cbb17bec45f6c7101a3fdf52ded3eaf5932deaf61b42ce5291ba120fb7855 5beb635e8153ef8b3daf2083838b185521d6ea5f0c48bda138c3a8fda504380c d99e4f6620c5725fb9ffc1d25b7c9a8b37e5111f796e3c4d509db12ac9a89c05f2b1940c60d683403004ab166d196ebf964b45f476aabd81ebbe6e6b08897d0f +generate_signature d7a0e232ca95508dfffeade3c933ac37b375a87b70b27230fb13b9e164aaa937 3338f2898062607a5641b30fd7c8a9319f0eee27e29f62eb81f776ce2c8e1240 274c8a6a627c78dbbb936a69e7ffbcbfd2ec55c01e3d1bb022f729a6f2160e0d e4f021f9084b55aa0c47f75d91c767fecc0449b58bc3cfc3d92c127bfd41d1077ee69c8637b8af7ae65787f62b0195b15008337ec86eae134ce6c46376aebe0c +generate_signature 8ba7111b26e3648abad63741792cb95236c6264b9c3c22f191c994f29eb6d6aa 106a04c1789cc3e76783f799fbec03c5065f84b1e4e9bf3c80ccadf3f87ed562 f5c111b34be8144b4836b45608e00490106d8771ee5d883b020361cbe1463907 d85ccba9c71587e39046066a290857e1918b8a9049cb4bf07ec86462897eb401280ace23fb2362d002275c78f2c2cefdf7f1cdcf6c4e9736ab50ba9785fe220a +generate_signature c29c8279f30d15f97262aba8f6934487e6be8755c6881d8a62f187293d17522a dbf46bb5d5758d90b019f12ff1370f86967829a99cbdeafe3f8a7acd1c47cbc2 9b79edce573af0ec876e6d7d8cebd86b53f727d239f102b41be6ce6a7c80b50a 47629dd5cdc1697fc4bbc92f53a2f367b83c711119414cc8c6b00f94a961670480ba60baa398cfe530859e008f1242ef4e4c550634c93850038146dcbccf1b07 +generate_signature e05fb593e797348736ddf8836cd941a22c94ce7f5f967bc23cee3ffd77201e8f 7965473f19ae14e303ebc33ef47289a5dfb198e3064b6f1d01be5cc80c94f5a3 24f6d687c24de2489b44bd42750d91d9792aaa82c026f777982890e7a130f506 b33f574b3d6f97f01a6af8f32b076b5d453f6474ef5fe1d614001e8b6d7d290822a045be001434a0205f4c3589558751887a014693b3bd426735be56524aa401 +generate_signature 5ac717c52400ed6036f99915c29b209db8b822676468da74e0250ccb07f38481 b62d880d1c0932842b37a6553d566364f75ae0c13b74ed8bca40a71879fb304d 6a3be311359ec3bc82b8d0cfe65165b30a92623f14b4946e917b515cafd84906 09569ded9ac99aa4c5509b135213dd623d886c507801f69785f189a6d03820086c57443d9ad8bf2bc6de40df031cac6c4956a49c755f83d77be1d4b6319bb50a +generate_signature c3dbb3427c202b3236d84eb7128926d094bef85a4b8fa87a588fddf3802c1fab a40fcd8972fe1f8f58450043197e9fd67b29068284ede9f66df37307c4e58e89 fe6fd999cb72babe36dae3c39da7bfdd017b8fc82bdac0a9df43eeacd0c45a0a 31e6799c414d569e524148c67175457a637607984202121e442163c5b1d2c30d27d91f0907efa0531d39ac28cb58a56724e4d253778a4c2a4d3c994a22860701 +generate_signature e73da37887cff1817fae7c4741733cd7e7834ba00dea2f500b5e93a26c581e56 43e7277ccb7a977a1a7b596dd9a8c2a349a5e474671fd653a411be11e00f4c85 a57d16c32055b1ecc355e83624436780efb637a584f54fd3b5ddfd3566136c0d b622ca194bae4efc4b050ae1eabb65fea68f7f80088c2bc8e14a2b7583ba5909cd24ed11d4f62912b17ef46af48674700400bcbb0621333f3d69a53af3ab2906 +generate_signature e8be4f01046ab95f2c451084b237ddfe3a6dc2969c69607e23737f90db2aa4bb ee5bd0efea632271d2d7206ad663d9e59e7e156b98bec152ddea2dbd043dae3f a73892c116f5f1f3825ed9cc5453b4dacb2ce6ca8d02189f093770d3477ede09 71ed47ecddf3daeea66660b3c3904c309cb8e9c285fe88914848ce920ff02708b45c0b5ae4997f86bab210718de1f0caff01497d0543c83dd86abb10d2e41e01 +generate_signature f37c2e442f40c0e701a7b61052f53aa88308b419f9e80ec779af0221c6a7c306 eb15993e2eabcd07c9f099ec698b0b9fd1a362b76d0dd7f6157f988afe59f66e 152373f115c96e7d8f4a375f06ef772eda0c955a9eb06bd884734c942106d409 ebe1697d5f8a498c6f7f9736561d6633eb442ca799de174330e93219cad14301f302d3a496995a964712636136a65601217896493f15d183967a1fd134b7bf06 +generate_signature 2aa9d48d8e94af5841f54f2757058c0b8d297eb686f8bff610cd8f76b310c355 feecbde00f05ef3398507c7ad352058a4aff93d71f76d1622edf6bb9d97a5743 bbf9ceafbd855e038b163b50559e3fe4f52741447c362e5ba794a9e08e495b00 858d8bc2f2f0056744d885d5530e511870f2c7972c73edf120d39ed387f2d90a7e92e813e89a3b102603498eb1b79bb371307d3c381f69f1d795ff1f7094940c +generate_signature d8811b3fe0291777e55012381e024d5300224793eca3886b9d5c4c8725682c83 081363794d031f2306db381cbab0d81199147e65a14e4ba27590dbed32705e58 acd5792a6da2c61941a20c6740c86a1d9e00947b5ef67cd62bed10b68498a10a a642aa8b60fec2244e68309c35111f29d85a147eac05618042347a609729b80ec9eb76d4754dba7f8109824e82cd3455c70a0ab4848419be57daff04ba3c720c +generate_signature 4d2f86ba853c80f9c8b344075240f1f65a942557d2d263d04023d1add7de11db 85d83ccfb3906964b966ccfd1640483f62362b33aed599b71e967cd3b51d9d3a df721a6b3e8f4bc07d31284fafa002b6194e2c391067f4e11f6bd0ad88f48603 30ad2f51fd6f5e642feb1cd7a2a9a26048d8c1dadccea1b119fa4a3306588002614730ba29690625d634499f5241b52debd261c5792f69b2316a9ad23cbc5a06 +generate_signature f7063852008f2d4d0e3acc4fd6a48a5db671df9e91ee49449c028fdee70b7f36 f84052f57beed384afe4e175761f029ffcffdc5bcbbfe08c42cede441545b6a7 c70f2bf24f5703fa17e0667655644dcc38b3fce4f981aab9832b4fb2df342d05 302a76be14e8404ac4969b93d3af8a6ac604927f073cb403a26990f0ce206d0b668422de7b40f8529f877c7952a39dba09333c93c55763811055d1c12bb3c80c +generate_signature 6ce1e641618e40f7937e12da26a53b2f31cdc26bb9eaf20d81fa904609f6e671 02727c0eefd403c3e219b206d3be0839b4f595697b366cde46d8d2d019ed39b1 b69515f720780b6479729d0ea33df63cd2331b51e5cbf2aeb88e016eef07d907 d03034284828f1f94b8f81f9d49916ecfac2d0bcd8dc16486e6ead9ae7c86e0d6ba5ef702f530c30a727524108b139b67ab14aec87cbd0ff187c327525cddc0f +generate_signature cb4cc77ff9f6368cea9cff0f1d6695b5cc3b7aa9eba56584f0673e2fc095eaef 4b03cd2baa5f4c0d65d7164188a3530000a812036084a9e98cefec7e411057d0 fd3aaa84c7cb6b807c45f463f4b4f066accf5bba330f488406c6ab9ccb1a8a07 f19fb2227869da79ea459f3d7f3bd1c396eaad07b26f533f889666a3c67b9b0a2ae91efbbfc140f94086613984b308bd83a544d535260e36851915967a445300 +generate_signature 0ecfa0630254ab0ec470f449384405468c9ccfa4143dbf388b7456e70982dd1c 63187efc37ecb61327c2bdc509b9c5f6c330ab7c17dcc427e14b61b078e40b4a 5841921bf5f940ea56d8b8e2bb973eaaa501e5e1e8adf4e863bccd76049d9b09 f68d3659e4e79d6ff1b2a3eb88f50febe24d8ffdb469340f1bba5b008ddc460244a7974d202c8a3233bbffd2891d00582cf7eb57f99fdcae28db5e15d0c8ae07 +generate_signature 702df13a628eb82742d486f26df833558404503899d3c36f51eea4dbeb9e5efd 2d10be4ee24fe5707396d02286c824cfaa81cbd5265cd2fc85b6a9e6b428d7cd dad65d46ef1ea1726918c3811b7f73774e43e51fac9220b093b32ef3067fc90b 2481e7afecb02dddce8e14ab63f401860172cb6d6a5e71f7849af39eb2077504fb10b983fafaf936fd8ed2b733d6a00cad7c064ff5c880cd175d443f63e63f05 +generate_signature d949405ae46e35e52e80e89164aba42de533247fb1c326f21ce3fccc99f5effb db491ab3a302b1633506559f045281b72d6dde7e362e66e3d300216f25db0c02 c60c8cd467c52c6daf52e7b287554c03e8ee041dec08bd19baf64fba002c0906 6ab6f5f416cee0a35a52abead9d8a134f59ff4d90d0e31a201e674639dfcdd096ff85c44d8df578156c158946a9063e8bcd9a0a0dbd393e1853a8bfe259e230c +generate_signature 90731d11cab0235a20c4d9052deb99b6cb1db414066c7b36879cd5180502bfdb 9499b11ed61d77120a9d5e319b7a9fe98b2c2b43a3f3c9424d04ec2e8d3720d4 18732d6e64d8d106882b6dfb482d150789627e765c4443a154f1fc614b32ec0a 7c47637b1ba36bc2c8df951ec188b6c0527cdd7c94aac5bcc21b2683f85498056a5b8b06808fb05326813e8bf89be042d8bb60d1e8dbb241358e7ca03caf0a01 +generate_signature 50125dbe49ae1d6b4ffe541b6ecb2cda9201f66bec53a1460444fcfa5c3220c4 4195fc42fe0a9b95c3a00759290a572907a370ba7681fcb7f80862268ab06ba2 39779d84c58f059cc84ea429b22f4a28664e4a273c4a7479953ff98707a6f505 d97cb34c72e42d38b093183a76316aed241c33f11add6a060db0448b4189d60822d8cffad3d8628bca134bf9707e5e513fa893473c5ad4905511d089f694be01 +generate_signature e981204e827d9a9df823401af94442c5596888b56a8ab4b79252dae010cb80d8 426d0fbd917b7eb50748becde6b07003837a039addb757e1e894d143a88a23e4 696794c4a2c9d67c3c2292b4eb0c10ad667df99c55e2aa83b7c5aeb5aefd050d 5c9ca021ebbd319a9fde24132222db6b446e5381938ebd66146c3344f3f9d50a92aed7f2ae09556ece28772d8e60ee2ad48a4ffd5d210169aaf9f41be03d2000 +generate_signature a394c44333e8f829901575cf8d08041310219a3bf79d77fec3a276ed38e3d7fd a1c6b325711a89cb44bb48fdffe91609d0735bd81c539a147d68977df635e8f8 561cb49b2ceec9bedf803ca5f04c0e2e16fd4bf51185b52dcc1f0783d0f40b0f fabf88c60173d6cdda1ad33728d628502ce3e7e4835a155274883beb3719e90a8e40d4c5c73e8c17125a6fde022c17b14711e83af9e8d480543b41ddb981d901 +generate_signature 81911223396ee5d724ca57bbacb843cbb8ec9b034bb4bfd335db80435073cfe3 a54bf5264161d56fb20b34f4026cfd9dd65e86c84a6f3e0b216bfd9a061b2231 d4a6dd9d9416fd09d539ce45dea2c3ae51ac79e80ffef74e0d2768f39db3c306 5040554003518d2d6bd2b182986a6aad6f449386eef9dc727bf66616f926420b4fcb68b34f3d327b4c0fcf1a7ec195b76045dfa75d306302c19d2d2df3536400 +generate_signature a96884c55f56f223e962ebd4acbe567cecec0dd3f0b8218a421a035a7a1ef7c2 acc2330cf888d4dd3735df869e623318af730bef8d4bff39e443b16efecadb26 7b9ae23bc26e2d1762b772a05cd53fbc6a963c0985f28bdedb009c90f4e37d09 02bea8ae1164822864d07cb5b196358a4abb452dc83ea63baf2ad5c558ea65094b86a5c42a537fdb85b49e56737c2468bfcfc27f93019c6c772efa9a0eb6c305 +generate_signature 50e9f1f1af3f6cfddf995784954e1918a242ae3f489e60a83f5ca854648abc94 e71c13574844d5047687242f6f7127cb45b34768673271c44b6a82084bf7ab57 bd30810d83a8e9cb6878f4eabee809e0b52ff8b77d6e44eeba663d1182711405 c5617c50da94046aeda48e71ba23616c3f7cf4db77a5e1b28eb0f4f10d57f50c2b9d09ef9628947de9a12612401da0a85adbcae7b73e3dbf529c1ef898de7e05 +generate_signature bde263b1b239b43e0f6b8aea30b8c860dc945d309b071c7fd9432f6044028c56 ed007643ff5a017efaf6edcf79e2f41fd9785f02b50328ea5329e669307f2645 52b688f56c135cc5bca50a4b4f39bf84014a9f142b3e9d884970f572879a7308 c88d8125da18b40ed7b922103318e678eee740f523d4d9eabef90ca19eb9060ca722b60f805280b7bbdf13b7d2b762d7e5aa2dcaa43a134cd669ef8577647809 +generate_signature 31edc641b5feb62c679f1c396e481989d24931e106d796414c2b5d48a33537a3 93806db1b69b0710f8771fd323868352bdeba169b1bd719c8683b338b60fc6be 55d12e566339f83da1397601b9ccc7dccf62fc36321e406b84a40f80e5bf3b05 956387ee080c796f5e747b0a0ba3de41d16a4c5216bd32ca65f7eb7fcbd8160bc059e8c1d527ed9e1714edf72e047d97ac0de6d3c12e4aba52dae62a3840630d +generate_signature 385a652f3545dcab05138ca2903c6314b2ef1e840ee6cbaa3c98844045f7f129 a7967055fcea5fd6b71168c5e78fea3b75d0aaa221932012f62612cd57ebe19e 90a75f4fbaa7a3818ae6bbfe2b2b43826f4ac5295ec4f0b4288720a6deea2a01 b9ca8a9e7e40c00300e3fc3e5cba3743b4fc818944723e886731c0fd49c78f098de98a16e4abce3ba0e8ef9c9afdede6796adfa29ebc237c6386cb73dbc4dc03 +generate_signature 85fcf581de1cff5bbebeb8e6c1470d5fa3c1c687799d9309f828b936dcf35bc9 70523af5a3e6e4482753755bd90b77ec518e3a2b891656263b0f8b9b64c3be6e c48b624d3e7bf21af9a7515b133188e10f63003adffb16a71572db81570a5106 8b900fb48e541bd21d602e47e9116c6ffb76ba6cf38c67f11dd92ea2e3042b0e266269af9cf6badcb91400dd26169927e835cc5c3dde7fa4e5f5c2d68d527608 +generate_signature c484d299589d96f6404f075538bc10c9f278a8fa1c3a22af223424d69dadaa65 6bfffef70fff450f94729b23c4ee9ac3760fde1c9606b01f0ed44231db80cdef e23db2b1b845376de1e14bcdbe37bcdeecbdc054557d41196884d308a731d107 3349636db0c42a6b097ea1fd6abb052c7b2db4f48409bd9196640dd44aabbd07cdb8a2d45ff60196686145419c6a519da5592f23345226105bc1c55196757703 +generate_signature ce30c1516c80e24afe59967d12684ca9d12fd1da823293b6e4e7b2495edfdf20 985611fef7860e78f6cdb7e3d7a3da93b53bee5d991b22800671affce6d21998 7283f05251bdcf003e5596d4a25229da37eb12891253dd8e24aa2528ddb4e804 399dc661afd8966f179670618863f9e73e75c5a809d1c3b4c31ed6248ab7530ecf7ba2603393bdf44e81e017e2e0541b6c05c035df7c3432f28ca5300f5dde05 +generate_signature 0fd52053d0b60a1afcffa939012ee6792a906def61871930571f149a8939d276 ddf745b9520511cc2f2990a585296b64bcef7f6507ab22ccc10931081cac2a55 12ed3507d22d4adee28729c340df1f0237920bcf8f95c1860e942efc4680a303 991999e11a197a70b88610386f977520f7c249730bff4e1fbf8c7954d286bb03b6a4b6d2e247fe08daf4f0aed05057a5d4800c8c1c5e72c0c1362f224f3e7c04 +generate_signature 772aba31e8644b5fd7da349c8316a6c4b0d4108d081eef6f1c6dc58f7bd4537a e4d85318548a8805ae6c7caf56b8895532969379e7ee2fb2a31e415705578a16 9fa11ea041ab8fcc4b4724c7aad6c11107710c6d4331d5fd0d63234b702da10d 2b42ff49a1649611d946d8473a6f2b7c7ecf611d3927d2511a8c0291b51468055cf9c3727968050ffbfa1f52aa02d5cbaba6241133e5b884e957f2b63af3e40a +generate_signature 8e5670764857059bb8b8bb632d62b4658d8dee2fd6427a0c015784044f02c810 0ebcaba414e59fb9a7ce593aac4689feb93948fa77dacb24a8a3b1c04e59070a 7559c89ff677a58defeba4fe099b7f4bcfda9a4b01dfef74a9586927f665b101 6484c1de1ffb4f7a3031bb830f6a4674c1ecbd05b9261f463647b373bc5348055b937fbc5cede63be413a9200c95e54e143eb12236ba6eccd5350a37d88ae30d +generate_signature 50383bb831ba6ec074568a2d85738c3b5c7bf8ecb8e81614938f22a9875a0bfe 6bc42b37a646263ca8da98d821f10e17f143d401853e9fda864accba33408c68 60627092f4f39625cd80c60552d93be0915af4539b6e80e69aae371b1affbf0b 78a7cab96a7b349e7cfa86a99b1c6b57ab4d915cabbfc94d3b01dbb5e485560076070c95095a587924371f6c417b6ffac45cd76213ce38e3643db3192b99c309 +generate_signature 0a62eecf9dc7a1018e1df0b7b22d4d2b09dfae49321c53ebe97774649f27c12d 7b890c2771b1662db5cead07b8477731e11446716045b8fa9337efb8a63ee680 55392dfca3a006db962ebc05b1266c80a3c5f86071897d6997f81d5b8f7e6702 b13d8ef92bd60d58a4c996a2c2d50405b8f75a39296e1cd61805dd98f6d24c05062a4765d4bc716d3508c2cea193b3b423b6d34a0e7b2315b60f87c3ea6d2000 +generate_signature 39143c0427a36f008f6e797afec3fffbd50531d178f6eb2fb3cb8f278214791a 1ac97fe79a9d108ce484e8a649c640ef137d2a18bb204e728b6505fed36d6a69 daa5ee4a0376ef1da36f379ed7256516a30c3739f79c77e74039707eb401b60f f0f864025dd9d1ee8c1a201100354add159099552901aba4fb5723fbbcea0903ac0e970be0e37427b43ed3bd6c0f09d93bfcd320493038b486cde3443d8f620b +generate_signature 6f176ec8377e7a21954528c5715e243f6cc10dcc18247a5510dbf2e8bd498895 ffe8c4e6f94fba34f3fde62345177a7993383380962d56df2335c6c0a4d7e3e5 7e40e3f35994b5ec09add44a5a1094d0536c40b9fd83843a87d9b641a0e37b01 cf57785049aaaf6d57768e28a47711022a20565f52412c6d9f065479343a1a0d9fe79a599b80382b6da7187c37fa5712ffde12d0eea6995948927f306f2c0509 +generate_signature 55f0dcfc79bba7a52c8ea158ca3a0b02f2a662d8462a9af532a40d806cd1a7d0 8621fade5d065c1513a4d13784bda4aebbda401f8f16018c62f1ace1fe9e1153 a3d160acad00cb2f70abd4f846cf63198a24bc8f71284c918dd7bb984618a10d 92477c0724d3805e2a62043507e13fd86e5c5f5370401397f5bede3faa0cf00ddb1727bfa20ae1d67c41eaa67ccfed4b682c39d594fbded1df74eb991572f003 +generate_signature ac2217fe9b82ef0663478c4ec3fab98713d1e199f33f64f38115b6e6703b1898 f94fe12138b647f0fdd0214e36ae211372fdf7d5fab13f198f725be474a6e2c0 9d6579f38323780b60f9ba93bcbbdefa548ad81638b6d0add934fc8c523d6008 4e3ae7df86615c5b01d969d3a7f025d47737ed25780a5926cf8d1b02121ff2094c5085c491e934c3f3d886fcb2342d45d8200649c7eae15d22278f67e592d50f +generate_signature 09a4c9e41dd32954cf6587123eff4a939caa3ec20a8a70678edd6723a18d50a8 6c626e4b9d3c7e2d8074bd6c5ef2cfc45daaad5ad97998a958eab39666063f87 6414848b5b32b3197c090955a52298bb8630456f614763fe934ecea5b422b30d a316c76cdad2b49ff91e1abf9aff7dbcdb9befc06b1efe63cc325f422d97f40d81978c7f9300caad147b20a261100f78d581d77cf701b66c9395ae1b89e4d802 +generate_signature 21b2c33be81b2a111f813b88b97e938e6d04a2f88488428e9f5ccdec29fe7c84 33a05a50dc5198ead700328e356a54fa547ef94243408f37824d2c30a7d67b7d cf438a34396cf8360b85efd183683e248e2dfb8e93c8cf4e6dd16911e1f3390e d63274b6d1e3e907435f18e6d00d48c8bda74befc36e738e66cb2f2359add906909305f98df17deae04aace295f76077ed7c67da3ead5f73916c3dd6cdc2c900 +generate_signature 42b0b30cdd2d4d49a5ab673b6c05979a9121edfb01c7a4915f389e41a4ef19f1 90f6f8aff1c712d1a50a7eefaeebc19cce0f59abc5662413689c51e62d86750a 982061c62116e339fda221df92cad3f311481f4ecb28c525b9bcdfd5ea6c890f 377e24d9e16f79cb3a41cfa168e837efe582eccadc823b3affcccb687a7e5207c7d2b2d3325532a1744937e583271646be01fba91fe616e32341ec80afa93702 +generate_signature faa494f48f62d242b9607ec200904a8d263b0d68d31a65e142264c81a9a75084 a664b769f845c5a148811de5939cb2abde14dd458821e507b3af707222a47233 c2213d8fb497226994db9fb65d5703ad3788b3fa2448cb3b522c63c6931b3002 4762b5d6786f1d76f5c313681f70b98d56af4ddfa659ef44db3a2e95f3777a0fd8aa1dcab9766c86b74f4a256a6c88e3d41309e9f54a1b2e887f631db6ba4607 +generate_signature 7b14e45c497109e6a9a5369899781ddf9990b5843d6348acc2862dca2139f3e0 f774b3c6236d43ab817e8d535661021be7564e1fcc291a6379d4ca5690ac302c a5e42346c2e6779e47f8dae593fce734cb8dcc72bac6be7dd0514520c2175007 fc3296718828b0e7c6fb536530e302da383771ac4a2487cc4d7f6bc29161d10929334e61ec74c33e94c092cf8bcc32a2090ce5735a679010508fa93f74c41703 +generate_signature 851920725035e4c9f85ef7540a5f2fdf2f4c6f0d0b502b3c95203f9aa90b24a8 03cd58925bc2f313888a1d613d8bc7ab17056d50313a507f86de295aa496b2fb 2f1710533c67294342571031173974f60102aa258bf7af0ffbd496e635be1302 47a09f57ac5df93f9b5ea5e608423a6c97d97118cc2592e1e03bdd3a120607052e650bb2bb50707136827f2f0f2b04ed2d2e1dc9f6a9b363928a6cb2ed652c0d +generate_signature 85301994625e5bd3491375d4496117266fbc577d419585fe4b67237dbd74d1e8 a19e9db9aab1f9dbb546801c0e542b4a7d89224e991a36bf497450fa4e9bb6cc 1826a8ca1d3cb5eb83fe2039a80140bd117ffee11b37879c1d4cfb33010d2e09 6a258e54ac1c00534d9292f91fe9593baa9599fb6b1d77220ac0cd8717689c0350adcc812898346a00db625ac5c9fbf4626aa5edb6fa5f0552fdb9e792d3580c +generate_signature f48b2b1616c97da2343fb62a0e39258178292ad6d913eed96728f556a38c0abd f7cb7cf3105089f40f883da20ed4bd1ff52c7cc1e1543731e89ac2048aac681d 3e08cc438a070d68191ad224b39de4f9301556689a0fd7a4afc42947c8678e01 4b27db034af973f5e820b3a53f39fbb51f2467cec130f1e25c6a0853f90f2f0f01b775c4e26d2a78239f04dd59ea103d3494d89de7d3083968bb476be1725a05 +generate_signature 03204c81ced567a1155d5a6835df7f6a1be4e68233e5a8a74ad03a55c4f47960 e8b93c8947503ccb7e892009bc78cd405410a388f33cbaeddff274811e27ce6b 2ba23a3e3ce6c2f0c3365a345320aa0ec79ba8e1170503d6a42f81ff75e74601 8b7b5cb17dfa7a5211eb7cce2714307f8229e8efed4f5d67ee4d4012d64b840f5fc4516a2dead8114e83ac3fe9138da747c3c5b9f1aca22a6a6988459578f80d +generate_signature 5fd4d50164f45e9c16ce3989e3008571059ea62cdc0adc5a0541baa7abebc137 1b9c16ac2f6e450bb9fb0a2c406d1f5c64c604492434b8affc3a765930af3edb b8d8c219539bd9766c43a962ae5e8b28edecdfdf3245aff03d0a6c111941f907 2b95c28d1c8b3ff979b63cd1e21013308bd466e0e85fea8c6053021a3d69630769962835c28e85506726f9f479d8d36eff754c0a552fec16ea518f156b722306 +generate_signature f22033754ba4e85dc23530ee614dc66e8e1af71e0ad9fa3bb2e2506961b38d6d 057b74db8073b52709eb9b2df641427c111d4bb65a3d2a96a1dc7fad0580ea42 85bc0b9bb168eeaa90103a169c7d67c52093e59e59af74e38d270d2952f9ec0d 1e71c0dc6c885141b3b2afb388e5425f9aa8cc6dc32318911cef3c61a9a37703607713cec1b9e9f830873015ff8d08f3966ae0899889637fa9d24e3a0af58409 +generate_signature 6d83946319ce0ddc05d3f1025bfa869d6d019db99f1d6bed44128835f40f905b 1cea92d0c4b63ff2d281dd6adab4fc03fa5915e38bf174a82cd74689620dfb13 9b05d2168e550b1aabd162ceb4ce729399eb7e5c0ae3540d8de7b81c143b8a0d 884e5207bd4996605f17426c8bb2c11d77c3c284fa731bf520d6c17202f9e3030b5883c7234512a220e04aaeda052d7c057a89ac88b60eb4ce67e10c22b33902 +generate_signature 129f77525518dde09ef25ab8604fb4afe4e14e20f78254b824a43f501bd5fec4 fc1f896cff241308922c662c7e444cf5620ac519e1f31d80c71f1a32ed62f30e f7724e295fd791a013f754b8c5ceb5564bc5a9719faba20d85ca832bc131e007 dc32cb406b66e9076aaeee16e1063447b7139dc99782d1b3ed4657773a04270ec24825bbf0c75a5bfca3f0e0c90e2ecfee5ce29cb67f7f35e2364059fd827406 +generate_signature 26276a8ade191addf57a64e95dea269f42a31e9af829cbf79757fad186ae7b8a b14772fbffe1e3af6ebb5fa58ed9a1406655a7ac5fdfeb3b121fcd347493db78 06ba0abb9351a0fa2e110a911619954c277cd229fe7754d7aed934a2023f3008 d67897f05575ca79cddd6fa91c3d10dc23a5b73556e30d3675dba1d1f2c46a0ef87b8dcff5d8bd6aa5daffc89e32306a71498a3556627bdacb1cee8f34d75508 +generate_signature 320e9dd8813611c9318c7a623347c4929be7e97e70d751bf281e5494d9a5dd44 ad1d645ad7f111776821632e780a078442292f229774f8785f2f45a4f4da60aa 945087edaf34455a17cec28004ba30a90ecf79e5f9855c968e1e80dbfb07100d b47bd965c218e70c4e9bbc3adb01ef7eb62a7ef58e68e73e631a8bb050addb0515faabae5d8e6df1b896b4a8655bb8f80286d1e24a3db5b80fca36c34a7d9202 +generate_signature 7a189547c4dfee67134e3824a76d51803f074dd44a388994fec628a0e8929691 f36b9e276e839fa30e6a12b6b40984a7326ce14238a5a6780bb152b6358bd30f 2ce73653be8047126c51db715ba29c356b013c943c5b4afaefd65449d92b4c03 b89963cc179eb98057d538fabfec529daff4af8ebf7b599233e0107a8adc120f14235255ddab1facb52d70bfef62b25356da2d663a726e893cdbe341820f7a07 +generate_signature 10c0d0153713abcab61087f2fe502c34a02d22019eca69e8a232fc8de25a10f9 0231d4c16dedc5f999769ce4818c5b50b8741c8022167c76a8e68790f4554df8 53a44a07381f2a3f30bc148f79e6227b79680b29e2ff7a6394a1ebc163512b0f fb77fed4b93fa2c96509327afbee50b73123f91941a7b8057fab727a59cf7f04cb6babf6c73563357390df0d532aa3092b897a24243c0604e812cabca998a00f +generate_signature 24ce6ba19d96f3d9fb2a10c887a0e785f79b9cc66c88b075da07709db4e23e49 036e6f48bb8d458c3a94bebbf3fc7fbaa63e6f63428243d4f68f6a432da03733 9b92591fd53964f7dcca218215ae4be7d3fda84b7b3883fc2fbf01c9ef9fc906 9b505543790ae852e08fb565f3945cfcbb50bd4af263bf9ed0918e0992b6e606ea33264a26754d00b95b5c36623ad5392e8bfc088a6fb47bc2d8155844337208 +generate_signature cce2811c2fba24ec40c8a4d4c5da90be47b60f7f7d093228e7c4d968196c8d59 762d69ec0e9d90f0a24e20be6d61c07636ec2327823bf6fff3ec479101f775b5 4769b9e831cfbb68bad0c3f6fd14cca9e0e2e816d6ecf335a0640fa768231305 c7bddf01308904bda87dc26077807ed7489719b810febeed15ef0de566a7900e050209e485ef60a423d19f17db0242e39c845996a9695e7d9528501d5d6da902 +generate_signature 4f9229a9ef7c8eed2d99317675d8416b9b08dff8157f4c340966a01ad9f9634b 1f3de3e39611aa2fb2c35ea6eebd48d7eb355ab1572312f672f8800ad24cfe5a 03ceb9ffc3051aab7986662b8fd6729bbec4cd9cfbfe57a4d7fef13bd9e6e00e 95102bc97e4cbd1b9067f2737b204ed191d1cb9c8ae380f2ed3c8b87aba5100e69bf016e1b67ae1028ff34f544e8779e72db9490fb2619d6a0a55640f3141507 +generate_signature 9d1b89fbeedbd493e0dbe49053c7e445f248e0bb5ad6a026cec3534fe18fc010 03b1e72c21e042287c88ce19279ffe9bd5b635b9955d73e40b75850cd96836c6 a803f823270d876cffb26d8d82bf07bebacf6601c3e995d211eedb3abab0d702 d56876d03412255a729410e64e42a557cfd9502c37318baeb5214743362524069e6ae9333d6936f3006c02965a072c04dfd85ca8bff87d3103958a5c6b843f0c +generate_signature eb3b8cece0aebc65f8750efef8869763b87253e7f6f2a2092ea183848e79a70a 30c6ab34f968a3513952ccbc21c873ad1109e7de585877d6e0b5d6e2b9b12aa6 f242d281fc3fe8b5a215b1536fad31d7c92f8adb4cea9d7a6c4873636f60710a d84ac90b8bdb6658e688a3cf1e92bd6da6f4838177d0c820ce5874d8499cc7050edc9609023cc358cd58ae131390b6a61e781140f97210877c26e9ea8fa7da07 +generate_signature 1526ed742358c3790aa8a761ce594fa1644da404c44bba1afb516fa567f6aca0 6362f4c79ae2ae232514fab877399a7996802e3a79719685650c0f57187065dc 1df2ee8ac08310920dc981f8cc928b8f4e55693a06ca6caf5c4ad63585a8690e ba3f9a62f3544ac888ecc44c664576a696e7803b656ad141d6fb62fbdf762e0d6b822b6f0b5a55f6dac4d3dab3df35e27b2683a3ab3485dcba9c1ac0842d0f03 +generate_signature b9c9f8987408a1c643b23d384b293310c4ee8d1e9145ce5a4c4e277fad53ea9c a2b3cf9490a04b0cee070e3a0b643a05b0304ace406564b2d78988531cd61f38 23d2fc4067939718e88f9cf980f777f3c26b9596170bcce3277d6d5431363403 f40b564d7cebcb719abf4e3f80979067dfffda19f91cabe925083ba7b92c8e0091c03204b0d7127c9c21b7c73cbb3cab5f754ed1891d78c43444dca82e02530d +generate_signature 8bcd3d04bacaaaffed0c074251f54537e0c16dd0a06b5958ed100627b6171e33 374ef697bef89cc06f6d562eb2bf692d3324a02834319bc6900ce9aacf751c1c 646361ad720733eb42718bdb222ee95ac0e1aecf4a0142abbbd5e81083ab7c04 437c827bb9351c55f13e1d09485c9fd5537ea3c6702f7df02416d812c139070107c85332a893ed309c31d8559d2d8adea6787e8963bafa35b7bcd406fcb16d0a +generate_signature 4c9b86a5066c55844b8b6f678089370c03f10df7006d51244f0a50ca5852393c 55a7b3b998b1b50e23c338735c8f3d9bb72a35edeeae57b6f1edb84852cea3a8 1f0d86f242026185315c9bfe8b1427e381f073d0d9f54b4e1d4a346b6a523306 cf4c059657e363391c4eefebbd80c5da721ea9204e2dfe85a3be7356a795dd08b95c0cbd8321952f545176b9ec41aa34388765bb4125ba1ac014c3922b96c100 +generate_signature ab9c4b451a0cc40643f210215131d761364aa9de470ae4aa51c8a7022e0c8c14 223e7b5eaf21e28bb35646afb36fa205670f88cce122f62a2321b7722e01ff2c 0eb7149ac290f18a6451d25be2fe828189ed69db7ad15547183819abe6416303 55b1396c9657aea66f1c74682b20073ac33bd5133206e3a4e0f1a33ca81f750655f82c286cf9038b900608b0d5973dda3e290ec9617020cc6e1ed925b0220b09 +generate_signature 524ae9d00665b81d1ed3881870af870eee60d5703b4c5540dce1955bf188ee24 fc9ec12b42c41be47f1770656bbbd4e12e91b462bda5b0cc95b890f3b6ab5fbd b03e262f5308572f2e4c48db3c2905fd7552123990326d4d663fc5e9ca827607 46112b3a811f7641997a15d5a92bc0de5320c877dd97fda2e88b605ac72afb0135b69e586b7ac3e8ce1da4443a33cb2281750cc6d4d63512bca5d5cc6c3d9104 +generate_signature 12b49466593ecab317f4c9c0ace8c6e64a7cf423e1bf64d23c78c52af536fb47 befa7be4735b1d457f40a7a88b93b31b59ec1ff9f41b782d60e14abccd6498e8 36ca9f5b488cb7f7ac361f8339b24178f190eaf4ce522eb8a50551d061173d09 3bdae341f3030b46fe05f7985d049ae4e4744b4c89e644da251e65d7db2ad20a288e38d3637e5de80d2b699a207faf80620ac5aae048a549873574e42537b90d +generate_signature 0e8137eafca086aca0fc9152c3ba362aee8968f21a2077f5e34cb12530fe37f4 9f50b4c8d944a408ec5bf46e47ab5ee8ce902ba55c86b465226b6ca41d58fd72 8e15445b911a2ddd67d9763e8cc71e7f383af49998f8ffa2b7b56df191274f0e 9ae84590dcc94cf34ce76649c9ff1c23bc104d9ae071ab56468871ef40d535065fe008a51f1ac6b48312ccefc28e2e8766e1077f7c7b37df8d115011f6d9e30d +generate_signature 7268d8fe856227548eeb541a61c15133c3a421e3bccdfd65c05b1ad81e255a73 4ebb8c4206bd0185c0174f49fca794a94448ea71010b5506c88b94a152de4bef 5dd21319bec6deca00c7b9f238f058f7c0ff8870ff6915e4d32c0d992e6f610f 33621700570562e844370af50baf779abd5ece861a80a4e7df39aead4303660b4c4a73ace578a99154c1fb56a10ce5819c5c0b48771a14baafb5f56176ad000c +generate_signature a5317567f68c9483e409ea6bb7ffc2fff6a3f6804a5531167a3ef620cd61cb1e 54ba81af0f6e2e3d9d23c14131387c575826eeb7a6e7a776128880ef15c17abe 693c52f9203a2fe80321c2bfc5c965f8f458a328e83462a5af3457bc6661dd02 2893861e391dd1a92564d4fbc83f3026d384ff127e3ab576e65250397172c007c9cdaf1732de00d644a3706762169587dbfa920bd8f7f01ee386340c59d20f09 +generate_signature 83eb818bdbeb8b33d10ccde23b640d1d0d9553be2d67eed0ce6d291c414fe470 39074efbfa4658ba167993c6deaf5327b6e559488253e722c6f9b7d1ee8dfd28 dabfaf0da318b10e0d5fe5585e030c69b77ad6c1c8f85ba03fba70dafd230d08 6cf386193c6c78744a795d8accabf4e6e7c9fb3a9635b0bcdd9f1dfe7d51f203612215432b048a425f47692af7026032dce233fdb680d80b2865b565c6cc6f07 +generate_signature 4e7f1117e1b9be40d3fa6d0eeaa578ed7ac6934ef21de63375bb917744cd082c c767e9fba01a1546080e8de17b66297b8df3e62542d43cfb352be4cf38a3616b 4a3f16e81c66c2ac26974349856205734fc74179f07b02509b1ea6d124818802 cabffc0a9f468cf95abd1ce87f0d373d12b45cd9c58148e0f7e81cb1f9176504542847fca1de44a8cc2d581070275279f0a4caaf2610cf16279827ce30f3ab0d +generate_signature a0e60f9c45bb7ed6f2aa222f2b7063e957ba57fa29d73d335cb528a596dcebbf 750b07f349b2fd667cbb2665bfc7d080060eebab4c532c49ba81c3bb8c6a4d70 211f507d192c4bda2eb53531fc185a5e9bcf43f4991a9b5c4dc86a9c1ea00002 f4bf46e941999e682283be58da1e62e6847aa2200c8c6de3ffe9890555ebef0f8d8949eb17e60cbea17eb15eec263caeb68bd1c1a779b82f681c710314cd8103 +generate_signature 90c3b897303789c3c83e3b6bbf3c9e9d234a9ddde538e6c16801302e5d263bca 58e3ecafdb34e2aba3c1f336fd4c644b4aa0d57611c4e39325f207fb10c67a3f 11f8fb150bd086df8b95dc834e4978bd5f079f8e0c16ccdf3bc3e267cc99bf0d 07691b2afed403cf90d5d4fadac4ab3aabae933d362db910ea1d6ae0f84728022dcef9060dd7079ed49c98a7413b4e9684ade6e725b092f1953e6f1989ab7808 +generate_signature 9581ad37cb69666f18af2f999444ece0d7bd01e1ac5e63a90a133db755966b3d 84a7c7215a0588f0cd72c68161d63b08f252aad72f85c6957cade84f560e0684 d58ea88bedc2c44f6f23734042c9a81cfcf6b3f177e7139a0d35f11ca5c2220f f4ecdad3ee0048aa23ffe9e8e4986abece19e4a774c404f40d26bb5108dab70f0984081fe72fc4de8cb5434af97316ad4d35bb6a3337dc0b9aa57c21358bad0f +generate_signature 398e7c3fa41a05b9bd3652e390353aab186356e2bf06eb33222bd64f52e2cd6a df30038ccd78926feeb6b425a11674d8a36d34fe834cd8203e5d56d76679c30e 4a99a385d252b7744aaa87d988805c708b045ab9da312fcc5122e6ac8424cb07 240158faf0b8ca5b73ba89d4394326485368b886032acb0ebc2facd8c2da320e3099cd0a160b10d302a45f6a27ec272b123b4843dfd37fa7338d9d48f4db610c +generate_signature c495facc8dc6086e1402ed5f60da498387b10c7cedafe04f52bdec49f5db561b 16a50956cc1f916a9d3cb0c168fe194a465697df3cb45f76a5e78c439d66515d c67c6f6c03f1e426c692f47f4d2ebe1c8e4f88d6b3a7b63b94ae8c140f395e08 d1f5e01d2615286834f7d913874e003d0e831939d9b43d18ed28a03dd6407404c8003836e4be78043c845d680503671587cce2228ac3ed7df9e7e644ef478e04 +generate_signature 4d2f64a0cae7c7a1387237f3a1addbe07fd13d332614443d205007425cac7a11 e17486cefb6abf4f9dc47d927ca52766a43426decb8d639bcd0ab5e234db0f95 b99a6c751671a29c56def02ddbeeb6e330cc8ef23ae2872ff9ebae4cf4317d06 ff93634eff78c33481586322b123469ff0edd33528a890413534659bd7b23b00c0c19a527a520a7d89b68f0f264b88d63b881a2a8935a6f7cdabdcf2a24f1000 +generate_signature 5bd580972fb34d5e2c545345a6d4883ebc5dc01bc0bd8d942800de1ba60529c9 332a5b3dcf79612b4f0397f52102267e22ab30cbd11e2a83eb70eeb8a63486a8 f0b936c45d3b62945806c6ab55aef24e3c10c72ee942ae341a93b31c25bf640d 9bc5c0472dc1b844016928df723eb288029636f44a9d376ce41803ef137cf20b16ca0551cc1d7c6ae127992572bf9d55cc33bbba35161cdc9a515afcf776ae0d +generate_signature a55bb7642debc46b710e53b1707de2b4399704f68c6550db025d16a4c60be3c4 8b2d545f29e50192d8856fe3d275c00a42e2f771d70cf44635ceac1380f79b01 ae1612c0262bcfed1d8dab20faf3ee9bce067c6ef8b1cef27b0e67363cecd808 2d6ea03d1353e316f49769a2d4f7f7837d9d32d566996743569c10db58c3ba06b8a44a1eb20a4022d18662caad609e2b30492cc7b3f477a2f02fa5291f47c00d +generate_signature 45d3a1ed5014c3837126dc70cff341c78ae9d220b9da4b0d6b254c16be4ab137 1ef94aaa7f30cb4927ea1507fd6625a28eda348ad25cb09e1400bfcf5955c4a0 e0a939f49099d170a8652a3d3ecbe2886e3ddb5fc6913f3c4e0eb9d300ab5a0a e794e7793763b49475c52eb9de05f8c45160d67f79c83326847c684de54ca209f9e32345823d2b598999d8beb3cdb12287429e628d179c7284e75ec62348520e +generate_signature 268077f9539fa55935135cb7f19e3dc9f6d0182ba7cd5c037da5c50ca4a2d8d8 9730b41780a30e8a10583c981af9a96bb912708ac5e84f87372b4d69956ffb1b 902c436411573bd9eb3e822b326062551db25b2b5ae8fe93ca5ef3416efa420e e27bd77c6f2557932179c08ada8fd808f0debd8503602deb9db60ac0088afb08189d0f0cc2b466944760b117e5d156addd9d6ebb06c27964c4e0f2b1e438750c +generate_signature 9a4ed9a2f32b3928f50e59089018fc109bc0573473b5901c94ad1e39f4d7907a efa3bb7a4e878c3e4ba34750c24c03618d7ea019030a3bc688e21c13264fc61c 640f6edfbe17a9f673ac05badd6ecb38e8dbe489e759ae9d963d032564eee402 d5d53f18c170872d097f257b1342a964e3a6eaf261a7778705aa5a41cecf38060de9b816f23b6d21a1fdd92a7c2e059003d2bd6467f6bff1c149c17482edcd00 +generate_signature a830e01f2d2ca3d6d87fe05fea71588e329221386507970f556d3c9d131e566e 7a82864f355d8f47b8dca503a419e9fe25cbde4ad3ca98519686fe12951fc655 16563a73457e494f535e0c6c7f3e6f5cacff6c58c817cdce83003b0eb8b8c601 5e0fd684b643286cd3bdfeace6a38367ab8ae340704eb9c380ac1cb77c2f2f038068f8d5780c612f39a602ccf52c4e71be0250dfa4b5c3589b04cc5a8b759b0d +generate_signature ec7176e063a4fd477a700c2f550aa49cf865f06a7657bdf02cc9e95ac99296ff d3976ec62cb19b1eefb2cfac88fdd13e8c114b6db0f9daafaa4266c3e1bac6c9 82a69e99071938575431587df3abfb1ea817c387a42029755db66c4827da6c01 ce5c77d5420283e23632c6458a28f8ebd7e4c5c6be2ca009091158cbcb66a80b8944d6eb70e51807932ddb1c227873ee0b46822d3097447bf413250cbc97ba0a +generate_signature 7bea033099bbb1cab0283e800ab62385add3ec8ec870ff3d5b2de52e0f99f9ad b6a0797cdb0ed3602670a362a317d196c203034711bb4ae34eea64e1c9121207 14390912bf018aa4cbad49aa11f5e3eabf4f6d67801c0796e39b082a049b5c0f 406c7d9b1ca8e398c5b4caab68f4f23159d31043f465a7798703dc1729bf2b052f897d7755efc61612a0d5024dc137f4f12c4f9db2f865a87bf9ab3d41b9d30d +generate_signature beb5d0da2939bdc50a6e71b875a29bb34fa0a05f50aeda990263d4dc41e20f3b 28934db7903d577158a4eab10c492bcca131ca8daeadab58f6c93d3632aa9847 6e86fb5d4f6b7392b14e78281cdbbced628e9eb006f05fa92c493119ac1e6d07 257a94df4af1300ed143c6f01e9b5dceddd8dc88d29450da006ffc70bf8421099913515b245616c285346e652162538e873fdcc07e3e59776ced4241cd995907 +generate_signature 513db851076fb28b0b7b1bbe4ea7de2a1612de172767dbe7021117da6c073c46 aec93462d7498de5094557fb62d17025381e819fd8a67e72f93d07dfe8936004 4ea007a61f623cc1040cec9ae4a19a0607f9524cb482a1b9afb828d9f8e3bf0f 6fdbf5fdafe08397294aadfc35def67a495864044b8beafeaf912c0fca287305569cdcb8ddeb8251bd82a999c08d92abffd5a87cabad8ad30dfa2708a501ad09 +generate_signature bffa46b3982b52f272257a30159c332e170a24a95c8801fbe7f6088de7880633 ab7462366a11d701d91738f490c475d4871b728e93d76f0506cfa1510c1c6da6 19655d6478b79904cd4cd74ac159cca92ee751495130d2cc8826fb75a77c7a08 6bf589fd104bed6c6eab5389123f1e45f2afb1e683b753c8ad4bc7889d1b370ae67c2ce5abbaeb36d8f4aa002d75bb8df257f9f64dbe1a96d432b46bdf5cc004 +generate_signature 7e42f0d30a898e27734cb3e54b193cd2c35e478b83424120d14511c188423cb5 0638294dd83e195282180e5e1dcc006453996e81ea3473d92b7dd5637671cc7a 896e31a68a87feaa0f644fd063320c9e02475b87119d1ddbade61bdb91162605 22312f9a1e0444d2f85d51e1ca1539ba85bcf1f6a5b52f91a5603ac9b12843098a5b8cd0aa78e206efa5cb2039cd8e075056a0fd32298ad7dcb1f099dcd22308 +generate_signature d9c1e4346c7ebc40d46a3fd8f73d4fa96613060583d4bf75adb7e00f7e3310d3 63a4bc6f2a5879c80a92b30698199ac44c364d864c06f1be62fa8c7b7c23992c 67e8e63e11623e75ad1b14ee7a25b6e4c24961354e07ff898e4901b171b2c008 601604dab95948ecc76526dd34674d351052167dce439db45b23471c6151a20f8f5ae87bc4e68aad19a13400b3edde77e1e088ee81571e487adeb00c781eec0d +generate_signature 19d054f19250d7125e7f7eb62eb02d723fc9fe9442d08cc8ab280f6e4db877b1 144ba0faf65e7a6900deb97b733f98004e817f7e293c13ab189f749289d8585e 78c7519c89acabf06e1df4227ba94dc679076559a29ff95de5a11981705de00d d9f3db50da704ad002e8f9d0ddf1620519780a2ef49c18ee04ab42d39119820375439231717e3814c08e8719fe895140e1eebd314f7f6b728efb0d95f5c81e0f +generate_signature fce3961653f4cc02f9d4088320fcf8195ba5bd618e6facce2b1e152dc94b1f7a 5ae607d3c9e6966ad25cc3d2992f513ff6a472673ff47611e26d3e8810ff92e8 4758d3f72fb9b4219a2d5f85913fd3bd20580141355fce49e3358b7ccc46aa0f 43d239a2c8ef2aa8e37e2b5ff8ca177c6e4079d8d410ca4d46080a98c9e75f059e0e54c48a54818262a540b7929f1ad3d76c58e6a7a05021fe82e7a32c669b0d +generate_signature e2d850573f3fe1a3ee9a6221529546de4e09096ea3e612f5270bb870687ea1de 057d8ca92817ba1ab0c3e899f186090b89f32304010f5ac854ba56af4efb5f5c f98dc15a059172372e7980467cf43edf878bf35777706844cc1210ded6c89300 3f538479bb0481f2e7d64ea2f4d06162cedffc0cecd5548130b523f2cb5bf40ee0d42b1f82795aac9ccc0fff004cff632e8a7efb2c4be1ce2549722f7e189c05 +generate_signature 851d907f8119758f9797665fbcd1c98c66e4f332d910633967de2758c89308c9 5b5c003be12f29f337256909c8b4391f527fd34de9b084edfb26b3d71df23ce2 990a18ee7657b7ca8f5f9245074773a23eec5ad13a74eef0e7c29457e33b7e07 f0361ee89ed3e588e885784dfad77e53d0ddad3c7f2531f62308539832fa690b32a0974d3aad67bc8b86bf0dcb8869955192b7b2201af4240aa31b64b3c7a30e +generate_signature fb6f10a901344d91e661535de7b4647dee64ce2dcb87a8a7c984db2c3b69a89e 9d274992c7dd94f41f2bef64e7d5454eb03b233ad8b373cfc1c11f9d3fd46f21 c53eea88e7f692b4f8c9d9909707c592f33033cc74868d0ec61cd0ea2b796b03 6d165d8b9aab33ae1db6186e70b0620dfdbf17e9e8516c166049723bacd4fe0208c8489029d4db2d80acd830b9fe3d09e21b4b9a2fa692033f5f221deea4860e +generate_signature af39a5a3ee6210c7912f3f5d73ca61c0484b3c719d9b249389b63c3b73bfb102 719aba5f25d08e9a12594e04613770f449e5049a2e0df7d18e06b812927cc3e0 a8f9fd262bc1fe090598ab4e9bf7d3e85e85fa0c1c0ad4c950b37786deb3400f ac669eec9507834cc89dee7ef0d88d4b2f6d113953ac10d8a4390e8ab894170862b734dae685d73f5d9c6d1e6783ea04c0465b08d7f8343153e461e475440204 +generate_signature 964c0c859da6aa099806f5433e5a47e31da9b9bbeb444bb3b439c122c7313f39 28ea8475f68c1f547a1c0ca7c90e353eb69c8ec04bcfe392ebfd6243081a3ca1 b7bb840a368d1235ad4dd69b3ffbaead1ce124787c8db4e679baecedab48d406 6160e8f9dcd9b89da91f3bc666e3a9d1c7394fecbadcb9629bfba5a1392ac30d3afbe0ea193e9e81c99612596cc7eaf9029abb050f9dcc03a115da0471435c0c +generate_signature 38a06c632e78e69cb5780e77ff7da0b020add44d3e79ebd39afc531e692164bb a0bfd8f1e2b82e17a4d97d7801e28dd43161ede215b83112684c6d15321e38dd abeccee267de379b80adfe60b8ac574a6abccbe272404673a3d799b374199a0a 50026814ca7f32be6cbd6da01cbd81cf2ad651c381bcd91426981afa9dd07b0620110fe0a22117a7b3b889e4297b7908f07645d3f4051ec3862a38b7cab2fe06 +generate_signature 916b81a38add8eeb1bca64b4d8d3c60b90886d9cb85d8856e690dd6b2fa94daa 6e5d33e7e8fd3ff9cfe247d9b5aea9e30dc3a83f78a42547d7e7c246adadc345 e2bd809fa23015f393722c3c041294230855b6d5530f42a338fde6a5e58ba505 16d1a588766537bc954915c0a974691400d43135eb8dae69af0a66fa2e04ac02a1600a3de4584a1b9a4c4c62645d196abba5422833624e20f35c9c144ebcf408 +generate_signature 6d5e5d92b1e3c002a2e30d86e259e7e0aecdce699beaad6fb290e7f4586721ce 6e745976b3d441e224f1d55f540799a31b2ac84984d44f08958398b6ea2940e8 1ae703c6f978030ce34b4cd0196dbe0828d96d1ed78cb4a82f9d5a15d5bff504 871cc51ae465fc91b4eab06affbcf75a61982e08fb80af4bc7e57b2fa0a48a0082e68a22655d311537327814c8e6499a490f072c154e5c57c7b1462142f68a08 +generate_signature 358d826ff50fbe086b6c8c1fbebcf55e099843f154146f71e105421a8c2e34b7 702cb6c78c066c704568bc8096435bd46965fbc63a70592686c9b3e4b594ab5b 524af81a3750daf8659656ce628287a17da6c101a2c1ebc1ef99b6b4877a6005 5f2f5bea15417d00c666280eb04727ab281f6121a2d997f4ca6b16b55855d002c95d48e133b634f0cec6eb7937269f2c8683328b27068cb25005ac8a99e7340a +generate_signature 141d23463071a73e6613f350b04f07a5093cccd97dbaa863c7dd22d886be097e 641ff2051ab20d0445e9c9516e71ce613be3e47762ff07357b4f8dc4c1f42e17 9de7a5c746aaffdb468b59335ead9ddae037a9030782c03b599cc7adab225909 c798b334e85de86222ffa64c31f4d7d384098f39b1de5156c83788ace0571c000c006a72ac4980f7c492b0f1272e20035f6f7939bd814189a05d545aa4bfd40a +generate_signature 975d28c46d7715f6ba1ea585f6424029250d9c47d39970f57cb61694e4c9a450 056e3bf5e45b03c37937ee620d908cf524313300ec1201d55e6a210390875b03 4f877f21ae60a7566c28f15a52f7f0a83988aceb1b55bd04c6a3d376488a300e 8c47a73d133a02e95449671f642f60d12b793ce23cd2bc5d9531b2359b0ceb0ab4c33a15184bfbc774758c825e0942adfedbe991dae1f63c4a438be41450280f +generate_signature 845d5853b809ee1ef926d49459ab39a3cb6d619c9d8ff5a4e36d182fe3361d3c fcb8cc5c195d21bf097fb017df4edd9faea78eb21ff1b40b08a1e572ab552ec5 b5e60d1b90278d6d7da305723d14d84a352797cdf4b8d00cc3df4cc04719b50d 3e528af0ed92af89ae042d81d3c78b4b914bd6facae6c6560b8a68e14ed1340bb74d221be0dd4ef115228a563aec58285d75bea1c5d41f0a01ea6e44ff9e860e +generate_signature 225e00b506046deb88ee0afb6f321229fa2cef216aa8e1b9c5fd6b37b089f4f7 ae60313eab3249d6e13926c9025081b5ba9d6de94c607f5c32064d969888ba96 fc99b1117755f84ea35202cb5345a9bdf63dbfbf1e2aa3d41b5062196366b706 c12d2285e3253d2d118b8a300d3d5ea2812ff408bc629bb530c7fb5bfcddfc0abaf0d2b5adedc4a4a055a29c0228a1b5cf619dbc56bdf15c9dbed5e85979e50b +generate_signature 1c589a60bad3ae3a0029d2cb2b64127bc5bc5abc943c5e0730453756a847b32d 94cf8698bb6ff9e01e332883fb7bedac450575767385a12a75f2058e2a8012db 34e627a965f757962ba2d91da9133086cd3ad9b27768ac4de6bc9cca9ba5730a 47e572e1b3ee7de9d952133de2bb6c788f6eebb0b8477971fe8687bb66857b0f327e479ba2a5046bbd88df1b938c06f1a4ec5261ecb05e99492442233d3e5909 +generate_signature 08cc91bfa64c83bec6c9ea85602939fc13d944c67538922654f4596f405bc871 f5c6179ba6cf6231ae35cdf1fa294674b21f49f25ccb82e77cf1612cf13c6a5c dd8eb2d07d00e3eb9c6f259624c17997cfbb6847852372d411a631cdfef23e0f 81182debe138ea3633f37338a7871ce555eebadc38ec4f4a65df953996d5620aaaa1b6dcc0766d2f37c6a2a35d170edaf067e928720a3a9998ef92528ece6c02 +generate_signature d9acb151328fcccbafe86444e21c85731515d1c8e0aef8a2f3e67170c9e5ba44 e1d39b6c470aea40ed96a1b309385e030ec6c383d2b68bfaab69f73066ebd013 7005f0953d9cf4f03c39bc6fd236eac3310cabd5b4eb25e2ab2f917f0cd89e0d bae4077044ddf4b9536529f8c682784b2b029f96f59749fd3d12f537f04ae1050bce60b09f116136b8e7dbcd1ba9b51c64317400fd7ec6e40bc7f40e21a2930a +generate_signature 95c912a1c456861597c6481a3b75075e0273ed7b427db73eb5957a6a4fead594 d83aa36b0eda1a40a5d7ccf7cb35075138af0bfec8fb0138c1ba67ecd33eb254 4780d869a4d6328f97cd999105bad4ab28e97acf26d589b6a6530fcd928e9006 1ebfb7627ade1cde4514391ead6419b8b9b3b3c5ab4c0c5c7883c00e96771406c0462ba7ca71dd62728bf57e1f35222881e82a9ce64afc652f770c1eae03cd02 +generate_signature ff967b2d1ae8533e549ce54bfe0b5b8f59383fd78d476f6aad156cb71649216b 32788c7daa953b92d6852eee6ffb386133ced0008f232fa55869039df4e65907 806710c29e906b98b0d0a630f89124bab94bd22fba271c67e5267258c9d27f0c ff765a886d72ea9c31f78118b6d9be768b8ad86a06ccf197d44f9ded998be7030c12bdc835971938f6680c22d3acbc25c496c1041d89660ab16de2451397cd0e +generate_signature ab7f3fd9a85553b32f6729d59f061a73f6b014cd7a7f093a4299172945877694 48c003f92a95e4fa152e239ce8d9b63c72b3c46e700da94ee2fe02f532620ee9 cfe1d3cb5556a32bfe77763f2acce83324e567a18477cfb87d8b64656358420d ea266ae6178646315d1e14a07e38429ad4511ce96556677ef317774628d0090a7e2bb4af753bf3874294cf0444ef39d308962f7ce4fe39f3a1b5289794130007 +generate_signature a9427c9b562348034045d6c4e3ce970aa4122510ce67fa68c72c65034fae4c89 21901fea722befaf3745863b80b35c70b2bdc0fa0a238405d33ec4581af0f8e7 512d84a942df61c501107492043adc721ec1173841879948dc39810964e23d0a f81acdad364ad51ed2ba919dd65959939b3ca9a18de8c3d76a75900279bd2500469dff1a82b696bb418c6fb5a79e1fdb2647bf666ac0bdc9f6d5f740ebf56d0a +generate_signature 492095832ade086255d48fc091cc16fabf6bc2c947db8a4c6d96c025c6f50f0f 0ace65dc7c16be46be9374c0945b0f565a2d2ddc0407b2d13b4014cfb466df01 85eb9c28869d565df9fc06248ff15feaacb7505c87a78fce0d3b5a1bb8655e09 e4c72997ea10c473e4b231e21e73017daa0ff18c38f95726ebf58457c7f9f60dd4c908cce01752c8b402fff0bd5969592a58417a7e82c5771075176b8382a70e +generate_signature 0c5a8dc366155b7a7b25516e0ea2b85ff742e4e9b1c075447714ff36c622ebcf 0fc98be6dc22e01057041635716b8f82b62c7ee3ed33e52ebf27aeea3cbdabbd 29178989b18db4fac57e54b689fb00af7392816b98eb194738a3f8bf7adf7a0f 409e542ca79fb1ea83c30578b8b427d9778e53e9b942e5a1f7e9ed179a685e017bc859cccbef2668059d2f498ba6786100db7214370f212c0a8a11e10aa20c02 +generate_signature 17ae01e83670ef8441c24da51cf2af17b7951e40341fe183ed0767ef663838a3 18d96f43e31793bc9ca52b96640126d3ce397a572f0bdcc66ff976f4ed3802f8 ce8ab3218464135398b53c0e1e5fe70428b2489b491824a78192c68413e7d404 952cd319c978661f4ac0134de063fc5f2d9e5cf255a32947064f956860e92f0774a5f0d160ce56a6b059a396fba91e77899d4866ce4e49301909edb7d0413308 +generate_signature 80a140a486adc81153011874d6cd39e4209b233e5d08cadcdcd1686b2d544539 fee2a85a1d8d86038d15146a3ed903f22924917aa776b3cad37ad131850596a6 b0ecb296b58d8d75e0c65ba2b09b22691f55d1e10fa5a9fef49abe1ba157ee04 419d265e03e3fc382f4128698ebc840ab5fbade285823a5f8fabdb6b98c53a0ce2f7f9baa08e62890e9bd26e89618da004c96eaf5e991be862394bb368a4c206 +generate_signature e329ecf89eab72b29f69047db90fad16397705539b8f6e8144f81babd3fe5c90 af47d6695f38b0e97fd472f6bc656a7f4e61faf7d50d9ead592a30dfdd291e73 8af2c84a4882959948958d776868c5e69f58821ede4a24b6fcfd81b24a707f0e f83f0588a6b8a91a24848aee8bd311cb5943b86f2626744478a9073b689d790079308fe215266caf8716e1c15a182bf5fc4faa40d551d277269ad67af8ac8f00 +generate_signature a7265e49d9c5cc8276eea12991cc05e4cdb9af655e31c8bfe4cd9863f4e73909 230ba0567fd19f089ae8019cd5139626774ee6b657420a1e90f4eda6a1d8de6f 3154234c4ab8fe016b014bbcc9ca5e412ed32fd4d78fc35096f239b162e8c206 6c068b8544703e53a2d1efa1d0e8f6e1a08aaf2e23c76c81b11be0d5ac0b6c01cbef0301a2f739bbc549c25803cfbc0abafa05b2ed3913226ce137a11cc62906 +generate_signature 1b9035bd9747d5fa3e56040e2b6d0d8e00bfac2931a60f6de87c651c88c930b4 135e6152057484860517e8e6994f69857c48924db418989d79d0730df14bcea8 d79ff410932b54b964c8392cf1c4789eedae43e503b53f0e845fd2a1040ba804 ec39c51e4d41a926ce46a54535a964c45bee59dde2e805e6087e2b9f26a86f0174a5a0d0910a6940c8927e8c368ea0c2a6a083223b8c1774eef8cbcf7385ff02 +generate_signature 9763bae26b83e47ad0bf525aa5e7073f7f014e824f07dac5f2b28d89bd03748c 5e0d3a51500fa6b48065a1cbcdc3585c43e170824754f8c2fdef39031932e213 5a612f91dba18d3783a5cec0dfebb2af98887a7e498238ac73809859a310320b 6e83994ac68866f48b064bcc108f85b7f1f61caf21faf6ea55ffa509dd8459007f2765561ca2519b320ca9fcf22de060198b70e18642e94a43e6ff2cc9dac80a +generate_signature a64d4d7d65b4ef4b9cf0a736cace8013115df307c956838964560caf4b081a8a 390a7ce2ef8ea99ffea752a94ba59e1501a8ccc91a37371017810db1abfb1db2 d0424a8f81b405f4e9dc602b89a91fb035a49369dca19f4a3cc7af8107590f02 6c4a78ee5cae7d9bf97450d8bbf4b42c8cfa475583536114095ac5844099770da8982c55ff4f2fade2af5887b25d3fa06aed9cd0b625e8c108653a7b57315c0b +generate_signature 8f6dd134b6abed5de418e03d0b53ac1cac95224885d58ce83da1a3ceb1c75e4e 10131e79d55ced17f02a9126ee0eee1fff54ff368dd886303ce8c6f0244d179d e7efffb51511727c482f85d2f3aec9be229c2811d65915628fedcc7f25af970a 8b9946fab444a5761298152a78d7b1d39a8d82a1da8bb6ccd211e1beda1d1e0f23388a05f5ce77fd8ff4f649b75bdbe5eb3d8e78b1951772d580fa33ffd4bb0d +generate_signature 3d22cf4bb20ca6a0328cc431a19199a01a271a91320710d81f2bcebcaf0976a1 70be7f5ab9707765f3a975b6b9e05d4646612d8656ed76baf60ec4d53e8b0aa9 aa57d362335b0d241c88fdf7d79cd887d3036a73635141f0e3e26493531ea60c 76911c459d32910410202e56c57227198ea60dfc2405a190c27e4a16d0faf60886457050f7f2808cdbd16a8b59b4c00731b359cf3ff578874e9654b5f954260d +generate_signature e27f3ae3431f3fe04dbf186543d32ab8138a1cf9e778e12c3bf94eb5cb618b43 d59043ea4ea274ec8b3b632c873860cc85d84ccd5bda2b82865bdda9b688d4f6 f00718fae93573305cda2ddfd935fc532ca6845863f7fa07dc0aba8d78abfe03 4656c592c099b5c4062c91d7df8a1978a9c2c43856e1f0a45532032e01ef170ca4442555fb485bf6290e0234fddd8d9464eb955784c54bfc3120c93955532908 +generate_signature 141cbb32a03f6b7bf3c8f24f27f7d0032edeec089b248825b63ae8672f7feb1d b9976cb49a3b91af37bd397f96086a1cfac2efd41ae696cd521f574d5a89b1b8 62554a69ae1ab4193f6832a7220505da447bb978e6409ed3e4b8152a17e9320b 2025d55fad2da93939a5b20cab01405764322ebbda122a8403bb6a3cdd4aa205adced4eefdd3c224fc3ecde1ae7d93fb045ce90b4a2747e4126fb448f1f9a708 +generate_signature 7c0a1051b510ef04ba72fd2909888a3dc9e75525e51e082a6d230b59cdf7b22e d17cbb7fc6dcdcec97a873fcd6416acbaa3744ba23936d4a7786b9f61e6b4213 aaec989cd7e54bed4c51d69dda19ec900daf35b8dbcf875c3a89340ad3518004 de34b9de5ecd4a47339a54d655562f95c787941cc16a86e0d33182f99f3d000c920cafe49f84f592bf94678b2789f82ad28530b44c8aa1ab587ba6c3a6d5ef01 +check_signature 39e894379d44ea5e8c3ee79bd84f8810f42cd72fe0c17a69620b019e943bf925 32e97b7cd2f0f60f1ff833986adb1f3f17598f8271463592a880e0d5ce3fece6 a81d286473509c47d40e80fb87b015359787f6498cdd28cfb38b4d099a60e10749913540e2810c5e7fefb9a87066ef5be003d2d14691f029b99b2ca17451d959 false +check_signature f695e708940db17769c59ee1d2c867f7337a2300b61a8491cec1dd4584f6e60d ab5c1c7fb5ccccc0f2f8c93c8037046286ef3ec82cf8b57bb34d45a4a10ba2e3 fbb0e3e49810ca89468e4fa0530f1c2c131a45da4dd82eab750e5de5bbc3d5ff57c1232a44500e260c5d5f66b37e5aa86c7c4525ba1cd433f74a52ff25ea249e false +check_signature a32c685729dabae612a6a91b93d551a8b92f8dfd727b9776c6e139763aa89fb0 81c8c3c0213d426d1afada8048a40a1b359bd49f700e91820ccfa14af3f51126 3bdfd68b2cc2db652b74b76dd94cd49c03dca2e944a8c8efd5c250d46c525f5c4354653d180c0d69075b383ed04e7f01e1fa032242316af10c526eced8c9a600 false +check_signature e7fdbf62ef8d4c5df8e8f66e0df4bcc0f1031dc22da0702dc347b6c047edff57 ef00b7edc266df3b7f0bfa071fb0b9c2c3262d88271f127985af3188348787c5 947f5b20c591a57b468d8e3a588fe967ca3dfdf380d3cefef775872a2b83a80c0605fb7cc42c52d6c9fbe2b55a57db176675fadc43ea624267f83047a3b08a07 true +check_signature 5fbd62834ddba1b5c4f60807c411dcac4efe4e8a8becb2bfeacd5a16c9143a08 887aa3df72ff89a2212c0014a13b5fb9a7d4e81dc4a7a2bcb478d5d32cc8ca42 0836842eaf1a0e52e93d303c81fd4393b3b0913852bd7774e4949ab9f2a404010d95d8ea3fccef6ec17831f1e84687a861cbef397cf4d3c5d1a42db3ae072b47 false +check_signature 783f6c92999ea1ca5063fb7c8242790add54f3f49e77b7ce75a49f3877796b63 d97afb18d9ece0d08933d0018c59826eb50309da262fcdd8ef5ecdde25e13ab7 027af8e299117c827e5e4a8123efb2956c391dc9ce389290136f6a81fed6d305d889bff4b831e7052d389b6a4a334c605744498f116cea3b7d13e213c2307d6e false +check_signature 85129d27f5af91f431f41fb6d8589dc4b6a7283ceb4030311b067604bf86c873 3d93fee7a6e8356ad8fd7b217ce0f8d5df137fb86d267e6d912c8b5efe952119 3a9c83d1c1c6d887797749f1dc6babafce87792c210e1adc552d2c2317c1b378c2d4edb9f45516888ebfaed2d2eba91f0f528559d7758660997d7c8f35cdbd07 false +check_signature 6ef43d62fb40cdc5e1fc5a3ee914bd8329a7a9350f81d38f8e9d1abaa2040f82 30c7ff9b52b6d28fdc39f10bd0b74a73fccf54be3414d091014d60026035299a 36f3ddf60ae302e69b489225f7ce0a2eb8345fdd7eda74595032dbba3ba28a01ae5dc719adbf38c393a18b90b4f321ea761fa704660ec31f028da9c37ea9fb0b true +check_signature 3096c47fa28ef5fe3ea784e74207ce56d4fb558ba00ba07d88e4152ecf891a72 9b2aeebff4abec609c05675881ce1d4e6fceb6ddeb20e45c9306293bf6558f5e bc235d27c20c69e631d8d49378169fe07e509f6133ff132c5ee285eb9505cb65805f83b78140ef97904cc9ccd1faf759ae37b9fd961d46eded858fed79573204 false +check_signature 983bbeb6d8be6dc583ce44aafd852a6361de422c545976dc3da74220c184eae7 c87d156ef0ea98172e3c52534e7cedfab38d36580aa5332509ecf056c293d14e 37778b8f0890e60fbb3dfb601da0fffab8d55679ef01f127ae4f7bc02541b60049e7a1b881cc41fd52584cb627ba4620aa1796b10db25485f1b2118469a8e5c8 false +check_signature 0335689d7a196478cd52588373a27f41cf0435add98b84bec4371e8bb9ea8515 a14518ab58937364ba86b248ccdbc742917976a9450a6efc0d9a0c422c4eff55 92a079ce422efc70c72ef33be6be990f2def0a5e098374593805a3715b94400759a619ecaade352a6559ac440b8f9dd9557926066e7f86744b6d9e50c93b087e false +check_signature e115bd602e90ea2acbd4046cbb862345b2c4eadef180359b24843d17bfe1f9b9 fb70ea0f5540520db59cf5b88ac68663d17a867e2231b575d4bf45c15588233b 670f734333c225729e7eaec128060241b6010d1f1a2007b1890bcf2037333d07a70dd782ae81a7505ced74e0b92e1d1be1d0a09ac1f07bf3015a7002f37b6d0c true +check_signature 7809f7dd60dccbd54d463fdfac0e0f34695a4a5c39a134507990b56fe7ce3459 62b3dd78651f815bcb0d8b94e95d0315f47ab95205c3e080b9364a88383ef6a6 4f122bfa0eb15473905a6e44437ba20b10aa93b1f6d1b977249cfd53a6bcd5feed39a38ac26aa53c21501156462f387dd1fe7d123e04b67f2ad716a4f1b0530f false +check_signature e9658509cf06d4a7c59c120f63d70b4383198139cb1438db92a32b69534c8883 56c5b625ecb144cd2d2eae714c774b3f92ddcab0eb8e7171222d6217ad2625f6 987337a4fdf883d9c4569db8b9985f59c2078e57fc6536c9422767411575b60d731658caa305de613b3f22a337293311779129298f5c39f05f3a730c7f8198e5 false +check_signature 6c0e15442eae72ef813aa819be5a8f8f6e72c873abc8d3fac8b4160a5b824792 b3ae46f6bc136d6c2fa702518bb20269fab796bcfcadbc0746d400f33fc8ebff 60bcb8c8268ff588fe389bb8445669dacaa1d00ab93ca727c58c5ce4968d0c9a65ca68b89fbb6dcea060237a77839b1129b9c2d1c73db03c37393945ebf10e08 false +check_signature 2abfdee0f79197d5ece5c3132d0e1236e0a0abfbffe874c5ba67056cb4570498 83a97b3ac44e87f264f1e467d8850800ed29c5c0b171e52a9237ade643351511 cb3b8d9f2b5802e189e11fd23f4f885ec3300c87eedf253c0f83093315167209510b374586de4e715b87ee3f4973853d79afa31e420388887e85fc5ecd2b6b7b false +check_signature 0f952c60269e22106582e30d37284fa95f0ab9e79b1b818b9b6a3a1352fcab66 c638099ee9e633584095de9b325ede9834ae5fffa6a38d8c215a893bec8ce851 4753e612e036bcd4dad46050e184c481600eb0fa095d4d4ce917858e46de05729564b5d57e38b2af38d615d200b348d92c90e3f9592edb272061fb1d6ad6f898 false +check_signature e8951017867c27bd1d7a7f5843eeb9d3d04bee6aea59dd4d6c7f8cb6fb4b7f28 80a2261e57854571ea4f0eee1c84360e6f80027985c45b3c581655c5ab043b72 3dbd8e8ec1fd411a14ce909c1c553ca44691761d35582656a77728008a69c70bc4cec1ebaad37d3f367bfa53992514f17ae197b84e19c7f18d8b20692518fe03 false +check_signature a4e35e8f9d4880c2fbd9eb52a0c62a4603b229c78abc5e6b531348a908a293bb 72033c8950c53679ed6f5e81ff5176d86629c0e3adc5d9329403d48c8fce20af e3679da6f3da9e5b552e2c3b9e2368e0318858de5d4d601b43ca044580c5e607f338d576a6059738c54075d7cd29c8a1cf3add9519c11e194ba894ba4ea7f806 true +check_signature 784e80b20b9235a32868d001a0b9fa5727559b6a5d846de828cda7640429caaa 1bbdf17c40a326d9668a356fdcfb56f8bffbea8b4aad03bf83be6739f10c5350 8508452bcf2a4eb5b845b14a83b93f302ab988066aad43985ae2e6a6649757077cee2765e370771aa2fde7ea87e297d6590b347585cdc0fe6ea20116ca336907 false +check_signature 1eaf129dc2ca76ed3de5987b188529acf9bec11af900676d830704a1e7b15f1a 8c1697ee1b3dce941204df6e3e9ca70153f632914b744e0cd81ccceed17fca26 83d87b25223cfbde897fc2642370f6209584c54cd4652690d3803d14b220addb41634bfa2c749f2be90f7f90d1e5ec062239b51f92a8fb8bdaf2ea9d7978254d false +check_signature 49016fdda26b94f000eeb517713fed5b97b426053167ac208ef7dc5208fb82d7 989244f60b1132999f9c2ade0794fa2c53ef2adae1d290402f0eb14ce66b1932 432d3ee0bfabbd57f8236449c6f1dac419c388948cc5b720f670810b2dd8b708591720d76865b5ca53ffa2fca4e1a71aee935006963efa76f1deca6278619b58 false +check_signature c6f49fd3e97bbf67376c1d69596246caadd0a8e5a2551afd66361c24bb97ae05 fadc4ba2bfd0fc46865986eed94074bd19a09bd08512f0222155dcae4cdcc83e b2d7f2cc700479e165007cc6a2458dc708233e21350b63551787faa698734bded05debbf0e43f1ee9acaaa2bf8f7a8bed15eef573543a715ac51dbb9f7a2b307 false +check_signature 905dbfdf33c4fb8bacf2a0befd52e736b303ca7051ee45d70e9577a712a80c8e d5e5ca71aac1532ff58d8e879ad02e16ac02c0d6f214767731816879cdbfdcb8 2930289cc6ce5c22c336e4e4d847c119e3b42cff0b476da55f9644ced9acaa0cbade2ec947ece373b50facf782412a757e1a8668228441527bff8196c32eec02 true +check_signature 73053cf5dec7d2be1548c724d7e467fae67f59df83519b8d05751728a496c11a 321178971d9149cabeff4077981a47046c4715837f64da38a86927c34bd0f4a5 b23678b20681b4f28217192ab920a3534a97c3412172153a34f33538abfe4e0cc7f0eb36a86e3ae01b7055fc135eb55702fbf44d7722578637372a7ee472f60b false +check_signature 247f065ca05b7892747c8440a56838d2d5bcd9ec0f71a17c70aa66bdc29be803 e81ba82050bd7f53d9db03d10b4cd77070b76dbc97559883e4a063673122f762 ef4cae811ff609ef6b8da23aa720bfad2dc6e1931899662c4d6a4a89dfd29d0ed39230a0b88e669ce4bd9cc770785624e9e679bcbbd34cc4365f758d0c1894c9 false +check_signature 943ce129e032e6130a45a9cef1187bad6d15f01e09d25807b8fee0affa4d4e1f c6c7fa0006ec09371c07308db8265ebc92a909b3fc6e7d5559889186fb2dc328 f4dc1ef4f3e5b9003d80fcb111d43ea9add47a3dba5a5b46b5b87aab6cfdfd72acbed1064f2594cbe9bf1f26ae0b48274eebe64de455d666dee54165665e7203 false +check_signature 4ec67913c971e1d82110f3c941e8b9cd0fad68b9af3d06d254b9f830c3e60a88 b18b1c12d6b076ceb0254094460940efa798b0f6bad49b5697daa331fdc4a83d bfc62a04ee8194b0c4d597c2e0a5fa8d1d4b3fb4d8a6a475a51cfcfc8186f20bebaccf6fdaecafb64c06292e9992036920cc1a7a34fd47009f1504eb03538807 false +check_signature bcdc0f4e62b8e3f8f818348a75ab0c67cb785ea55879add0e43e0c865971d85a 5b5206041569e4266dc3aac5addb773e2d8132649ff6bdac28b4eabacd4de9f2 34c62fe2ba4b01ea71cd38177a8326ae16422074ca15cee05f81d3e8e1f7dcf41e1424a176395c1dbb7e7048a8a7b1a90eb23c93dc45611354b30c9db7789903 false +check_signature 44fb2198dc854dae97f5c35ccfcc008018a945346b5ebfeedb1fc7f48518c291 edab05291c7e119b4ba5227f8655acfd30462101263196ac03c750db69e16d73 9866a4e70bc0849743c32cbf10a15a821519855c5e34ff2b6d81c183de66dd049afa30b700308d323b7df38cf55d3f8d5dd09e4f81e0f0d156c719f6d6fac40c false +check_signature 827014981b1df3ce7642c16cb9c96bf5cca1a37e0303458c71221ff5148b9470 5eb7072ccdfab108da5a482a276aeeec7a3a252666356d081b4e88ef51700d75 be05ba55e2f032874909c3bf0158919e6701b4249201b36209a05238eb6bbf066ceb8f01c1a194a21ff5751c26880f879938434b5abf6d171f1e8b3845d66107 true +check_signature 5bea04ca794da563eb08a6fac19a93e896e74ca482d899bbeb010a630ce02606 59885a475aff9c30024445382377503d720827c764ac38b6612bcf5bb2a3008c 28ced4e9d2a1e165089cd2b9ddca36fa8339cf9a4b695c775f3cf8a4c80ad500b082e84fca11765b33af0e956aea49c29708af42b73f67c43b97336730423f07 true +check_signature ef15d9140dabe94136fd126c5436c35778f4e6c9b2ad3956c7cb7d0c1659c0ad ce4936df476d3f5a416a911038118234021ba16fe42068f480a755c2f1e4a894 d7dda2e480ce35082f918f58ef0ed12846dcf3cdf34594ab4f0b7a52b9fd740876f80bac1d60de2acdcb97455f36eb9a53ee31a89c7dd578d5401a77a3760205 true +check_signature f6f1f36dbd966629bcb09577bedb6585fd6b4f7c2a504ab4856ac21e2753ab9a d79bb7c20eb128001ba9d15afa236732dc1bbe567538834079f6d66a9fa626a7 f557e8fb1ea3ac58c41746b562d7cdc5cccbb99a3a9c834c683730111a023585f25f0d773d4416c0be0fbc9173147eac8ca93052fed2ff54b0f41c5f411ac708 false +check_signature 3c9cb8d18320338e6eec4f99664ef2eae954dbb6d76a6493cc45e69e7e4758e8 d641a71ffd276484018bf664acd3820083de83912f0e4f034c0df9526fbda61c 9a3d8da177bf000a94c2c3c091ca8252bdb61129fc57dfad4c1e4e1d79409d49d6006e511ff74c65156cd7d4b1c862857966afb692e763de26ce6e2910da946e false +check_signature dcb6fa0ec113604b84441e29f0ad9aeb4b1d7a1d2fa9a642c78b53d6c8a6b841 9300bee10abf1572c04b4abba68892de0b95527fe2c2c93ba283bb4d30c0fd70 9de784d978f8a56f728dd13613e6fd34a6c8db9476a01a2b784f0ee2f65c81ad310035dd2089824a98fa22ceb0676de63cf6f8feea45e5d83ea1615b16bd9f24 false +check_signature 2fcc25a768633e782e43431b46a64d8a48503138892a7b16a134a0bd81a4d9c7 f60a053b62cf7bc5070d81c0a357f9d45b6edf56a428e727575a2f84e54650fb e72d01a00a711f0959dd194a7767410b5465f86a237a01a5a0145f212c4995082f5a8a65647ebe4a5e4181054da7ebf59a7de57665338c7f84ff04b5dec5cf00 true +check_signature 37f362f6249d647dbacb5b04bbdc9520aa95f31d374be386dc537c703002cb67 e29150a901ec41c1f138d9c557b4ad621140df0be9d073410ba916a36cc2e4c9 36536f3eba77ed019a1c16f5a974504c02842c937f68a2329cbda83c9cecf60047d883d735a9199aa9e402859c52759c5ed55064970bfda6efa22225844ece0a true +check_signature e76a975c38a135acd82f7ac23bce50dba775bfb9712f38737b10a78e24b162bf c1687ab784f8fcdae885b9cb2138b6ac2ad3b664ab10173bb07cbae2b156da6e f7d3b6f9821e959f928c9ce568bf523b45249b0a719a6d6ab9c811bcfaee010c809d60f0d9df2ea6344d0feaabce9929926c221caf22e4e73095eace07b89521 false +check_signature 1ef49217612fd93d66834ac08789f2a555bc41b80a663a8cab9e8b23cde8cb18 0116a705fe56a4344e7a0f6b89bf3b411649b1186bd3bbee77ea335003cf9851 b03a8f97163c8a021eb98ffe79adab9c333c9719622a611a340c042217b32eec1bac81b7b482b46791bfdbf40bc64cb09f04a24f176d58b4a8db3cc46397260b false +check_signature 334fc5f418345e70b1e791c6ddc10ddf6d58b4f940cca6b1d37fa95b5f38d4a2 a1c407ed8a1f1da89728aee961db261a82f76086b036ce893e7e2d47c77941f6 817f374aee4e1220dba096a266570f053d24937980c60caeadfdcfeec09b1a0368ed7ecfb93f4057601c94ecde055eb9d773d5118fbf090395a19aac32af0b0a false +check_signature 09e9027a1a4332f6bcb1f842311b2671c90a7dd67afdb3af5bee5452a493b4a4 e1207f18898586021357bed5ddedc5fcf94a92d3ec8f141e845ea15c4e83479d ba95956f88d19bbff29126eb38241dc104bcd5a45be89a77ddbff763084877041c0edb451fe96d40372b33bffbbb90298a84d8a482940409073d15166a3ece08 true +check_signature 366cac4f9e5fb4536fcdcfa39d431df936bd197f73b82364e99bc934478e4810 63471b1cf36edec2dd1ad9941029a41a37e9ac02e67170fac7c7dfb2a65e9745 a482f124db1156dd908896c3a91349ac9df2171aad9fc51067ba3d90297aeb0e2e65e9c579ba9105b762484664e050f38148baab47a750bd9af104687383b5da false +check_signature 5e4466a0f320a1d2de207f9ef91c873e63f6aa1e8b7ed5a9fd3dab24d98e462b b041ca06c0d12683fea5dcaf9ce46e4a1233ea05efde09d15d732efbd62c986c ab06c49c2c383560f8c49be207960d5a5fa371dcb4ef123ea4c4649d8c3369f7f8a348fc36991c4515314b78ddd83ad6a4da6dc15ba926cbdbc0ccc8efbeb30f false +check_signature f2fa9a5d6576986fa8f3df35c6f0d15ef3d27173c706034de7fc6d31e5b11752 631ff241aece1e07f38c979e3d801c9358369a11fd2c5910f60e93929cb8f2b5 c09248d47564116a9dc95875c5bf23a70e4261e5c791202254b2427166cc3e386c4774a6dea83a550833053b7daeec728efb434fdaa4af9017e624d264607049 false +check_signature b016378e394484741e88f332355c549150fa540f293ef7c3088b957b6b39b6d9 fccb33cc5a74f0db413848d577f34f5c0f365925310321f164219ef88abb7e99 94cf3bf1c2f929716c3eca0520f714d83306ce6273c95cb3f5bda98903d9ea0b180a2eabff30f10b1acb28bfba427e883d308aef1caa0f6768d14308100489bd false +check_signature e5d3babc40b8c8c15acfb6bb970acaaa6f272a83565449aefeeb1516c64c40f9 c416d62415ac32d41a59301e87232e68b6bd6e0a8ab1c75259bc07f427a1dedf 383b0d9ffd6bfc56f4bac32af7375780ab891e3d5f882d42801c9453b8a7af0e01a1a66d9c49aef4b13626ba9e9d0ab14110348bcbd18ab73ddae092a9af0840 false +check_signature dd788b8f83cc09e5801035127b90d1be9b1193b188ac4aa4aa5dfc0e2ab13fb6 e793afb26d2a1aa716d90763c37eeba30a5e8682a8865c922f7d30aa12e90fb6 d182be94bde55492e8624c8495bfacce56e9b953249d608fd987b7f24145fb0d81ac4b2314aa4e53207cc42b5fef1fad9fab70f8cace9d9cfa9bd524255947fd false +check_signature 67174e09e0b9b3ba8b00706e8fe023fb39ab06cf21788b5cfd2cb07f87ff441c ab1cd136373b0518025cbc8c80a31abb65a11ef9bad7fe4c9be70023b0cfc2c1 928c1843064f407c73bedfde067b4f665e9ee0e34eaff55155e5385f192c460a4ce0d687d9b37fef349cd14615189ef6c7fbef4b1f9437727a48304c73a5b90e false +check_signature a6b3b8bf0c4c17d95c741f844dc43434c38e84fd354188555017350735b9f594 75ce49e674e7f9dc7afaf0263bfd1760034c17e900723c032e23ffc8685de639 4dc1ef82bdd8257f6d62fa00634819e3e542a7315a92c0e58d2a986596b761089c2d7cbae91620f22719a2a57640f10542818192a088904bc4ef545a71c40b15 false +check_signature a663d2a63495ca58172a1ea78998aef6e3d0044332abdd81f7413236e13b55d4 24c8bff1fd43222bb419a3a8e30c24a216cb9433b6d0e9905731f46dafc771ef 2318071e000c3ac0cc4261487d2524c9e4d0d9fee3e6c8402c669d517a953f41b833d638ece0a157af9db41bdd7cfa3d59e5533ac4c62cd966c719098a67430e false +check_signature cb8135dfecabaf3ab969f6928449f7544d79ec525ecbfb5ab44cd0dcc7d59340 45a8b6e15dafa0d540e3a3314722d46d7c7cc3ea6dcbc63402292efc0420c3f8 988237b645980397967ab3bd1d32b02a2d1a202f1c801347478f39db7505889d155bc6788b002c2ec5761349593773013e78e2f99fc2c5e2b87145a720717f04 false +check_signature 1fb611147cb2fd62f46cf9dc61cea9e32afbe8452f94bae1cca84558642463c0 f346739aa073098720a1ea0822476530a2e0513f627cef584bd604ae6a96703d 2c0c8d3dc87bab816914b16bff181d64b536bd423cb051c8f43bcbcc33c298d064cc3b7c04f0f47885c7559fea1f43f50b286812e5bdc7ffce3bc47b2c61c50a false +check_signature a64689a9830cf1a1271b9e4f500702fb1c428ffd6de9cd92d2108a2858d2754f fb1f18d52a534cd8471ad3383bfc7f4195d129901b54360443883e345f8ccec0 998457bdcfa8ed1bac4dab1a8b1af43be2e0434e6c9e5a3ee43cd28b81a79309a11e25a675233d2fcd0e4a37be7be11a8c0c17334f2ba6b849b6a8033944d464 false +check_signature c4edc37491718d387bf4e2d6a26fc458b8587b07d53eb4d249851cb92e4e25f5 e80b7bb3a9031464fddd3161699cb6703de8932f48827bc212791c7eeb5ef02c df3201b11aeb4fd6b203793d9c3f8aa38091c5cf6d672bc7d21a99ef1f3b0f1dc7d10c3987a8ad9cd78a5fcaff3d2a2c1e05b562696e48342e2903a07cc3f80c false +check_signature 4fdd61af8c368e871fc7e7dbbe59d5ab5bac07299c9653e96fe5c6478a49ad30 1fb5d6643d25b236c723b3f3e5bdb97a1c2dc8a1524dfcbb488e081e8a617d14 048915ca539e8bd7b16daf00097bdf2b5759e94c77e267ff880df1fbe6472ee74d917c6d22748deeda6a48ca9647005a630aaf75ee03646d9a3ffc3196b13e06 false +check_signature 1df6e2e075d6d34ff9f870d19b8c77d9cc4ddcfc23397f38832184034420fc37 688b07d61edfb81c36cb47a9ca699988f2ab433dbdb2db29dac9aa07ad2d10ec 25d3263577fa420f5e84c7941ec611287b23aabe0660fb1896f74ba9b2058d083fff9229bc7603c08fbea4d277242bf311506c0d5774edf421ece1dc77b90b5e false +check_signature 1a0db14dbcc2d7613bb2151f5a40783461a5c14746d4cb7579216d2faad71947 76110cdbcf36b8d477c16fdf83467ad98bce2499122649fb971180bb1f094ed7 672f611929f8255ef1929f488a3ded6b480e2c8e8fe05cc4511f053f9137e908a819244e6c9b0cc13e60d319c71bc6ecbee6b8977f08089b59e7948ff7dfcc00 true +check_signature 5b5bc7f601dbefee41739f60ee5789b9095c58204255adadff772c46d499d9a7 47402d5a7c50abf0565d474dfa81b951f2d5c66f9e138c5c06c88805215e4746 b2fc5e2545767f6e7968b01a45d7bef9c0902ea1ddca015939f330944c30310eb6cd5592b339355040759583678bfc512114d0b2aab9dd85827427070d0d7305 true +check_signature 544ca2fffbebac4995073f41d0298ca0da6b8a25a9571b1c687641248c8b016b 02e64022781ee6d377cfb903ca0a38782cc826e1f8ab3c58ffe1cbd2f894702b cda36c7bb06590b0b60cfee479d1769bf31dadf0975637d21d4ea431423fee2a3e621821641bc87709fa434f19c6ae49dae922609856177e0b3615c0fd6af302 false +check_signature d2863bcd7c264b3badb61b4079510ee7794a67e52a0d5add3d8bceb9dea0a6a5 56304ee91eedcd0d3d91bdb9c43c7023f35f1c55b0872c4ec5a16ac65880ed24 d62e3a40e6fc995fdb6601249a12aac95d82e1a9166cd9f15e18e947615b6300e219e83df26762e17239dd3d777954e40b325bab011775718f896d94be4e0d22 false +check_signature 2f093522cdeca38453169ff3bc1593ac31cdcf2970f32ce41320e2d3c9c981c6 1b9ce855c2f4282369d052fdc099f89ae842be253bfdf2b47bacfe318f37085c 33462df6fb4a6426919de3a517bfdc83ede81d88f79cd1e6906e3d4120b05e0aab9fa6575a9ab75cbd420e56b741745c398f0414e5c5332aacc8d493d9856bfb false +check_signature 33a4bae97f06a62a6bea8590d32dc93e2a3d25e0d37fa291bb4b7e5df16fed94 47a52ab4182becc76238c1cda1f254dd340b4245608c2f402a230bd5387e924e 539a83ca06d2da12e1f2ef2f14558ceb0c237779796cc2f66fa4e7b04dee5a07a4cb5d1589843a7fa91296d413ad5990ec43291fcc19fb615d241b106e2a020d true +check_signature d54cafbf748a0448c41b464f11ff5485bde916bbf391511ba230da6a6e640b99 1206db640ac382a171116c662c45671f7522eff7121ffd0d18e89f3fa77e39ef 174c3ee5fbee3f7e57f9c59b90d7069ff48e17c8bfaf0282e3f4c6012b47e0055ceacf9424890560cfabc25810deaa5a4f769072e6ef951bff4e50f492824fd7 false +check_signature e7dd94f35ec2e3651f2d4f711b1315f492d2f21d4a2a05211c71d0785be3cc66 1f712b45745375335eada45e2f255d81a8c78ce08d4e0304410f9d526749d881 c345d47f81f93354cad75a50f8fcafa93b12eb383ed653431cdd275f4c7faa7503db8b4106a151516063ab17693e7e8ab93c4e0b4da75eb92c3ec2fab4bbed03 false +check_signature 23459ec0c9084b00c29520e0c29e5be4b9ef58eac1f6da7043dd21bd6cc6ae20 b53c320fe7cce9d92aba7aa56ea3d8dc6e2c6625f1d93babf05ed9fd63f2dbd3 0d5b368abe2d8ef1ac471afe488771c7a2477585493bf0ac82d9ddfee14eeb0fbcf5bb25d68e51c72a7a8bda3c66073c03b47c2c51eb23fd693b6ec2d9c02e25 false +check_signature 8c90174667a9233e086944ab66abed2044fdeb3e96071fd11143070f9396ee52 8e02305c734807d9e7f787a2f9df970f606d409f401694c23b9adb30f18e530c d527f80b89433c1c30e22c1c2d216f2fef9cf8431ac3d70d39007f4e0f56030d0e2a9a47e503681f79b5cba4a20ea81eac27c17dff5b2e9d3a1b29f42248a406 false +check_signature 5bf70c8fa932c55b3b823494b4a10111c3eaf85976652cb6e66b1f52d38bda89 b7e2bbe6034f203e7f260d578bea687f40d9f453e4618db5999a99c723e7ecdf 44c0581995ced7b49d62d3b0568ae87b1b326e0fa17ebeeccc0a452e01853e010b7648615fa05cf8d3581fb52fb0a01c1fe2aa56e84689901ce1478e14d9953c false +check_signature 0501395ab53de02430f6dbc634ad91ddfaf950ba304045b7275c66609eadd395 9d1f5062f67b08d3bf36644e80951b9883271a5380d538e42c138ae2c6633c57 99744adc3232d6af6e286c4b26ad54541e0a2187f6542a1fb6e73aea447a8f09d73490cbebdc6e9711aaa83aa24e40a8d8e4bd75299cff4004c968c9bd1f8409 true +check_signature aaa924be5bb9e42bd5a291c2bbe1eb4ed7a0b464691c92b7cfd236a0f06ee8f8 8d579f15161759be46a28b341968c18a9676d760bc59a1be94a35a9086e3fc86 ce3606522daaf247635c9f1885bab7f79588b9a2a1159a7398eb0b1eaf291c7959fb9019def0521f26eb5b59ed618f9542c4f4d6d7b1d829d61367f5da1079a7 false +check_signature d19a0a2be66a5d4029b5d85fb03c4adefc8545cc14b001acce3c4dde480031da 15178d3510ddc40f03fefb8348ef2ae4740061c852a86dfbf737aecb1e512844 1b9814bcda45e600697c6af3564cea7245b905e1b6d15fc0a25aa70d2b0800b7e548e3a12c4f200a5aae6e9185d0a427e32e71098b5784aa5cbf6185b9048bfc false +check_signature 367e0b9880141e4bc8e17bad370d191c078a6d9d38d6e36bbe17a2489363315e 63f14e372a94426642da6e4ae326c237fff2d88f5674ba4e4300b0c532a8169e 382113358655de6306ed667cd5fed841b8178404b03e228ab746d94b471c1908ef2c554a4b9f6ee315eedb4102f485da6ddd2192a72e62f552d8e3a7ba9d802f false +check_signature c00c040b38950bb116b3af1265ecff052699714ab1790f82cbf6fbeda42ad747 45584c8d3d6f27139452ba56b86162cb2f30db5cd8953244b970b9c34571a8c3 2f4328400505538287914b137dcd50cdeb609ab49ea55549747542d56ee84904340425ff6a55759b7a61038b6f26115cac0b32f5d8ccc399463e35f30d73cc33 false +check_signature 36533f0c1fc27f9db67ec5fb8085483206344e04e67f5e5fc1a53cb7559410e2 a18dae5f51288e33e1b87dee2d3f0732a60503923c6fff1d5cc6e1aba91b940d 4259afd05efe378fb054abd9c1c3fb18cc0ba394c31e8c372e6e19023796ce06bac554e1c1199caa6e3b2a6cf3582ee7a20aa051f121ddb2a6f156a34cb26b84 false +check_signature 474d8169aabfd2180d56183e587c64a1797a048328c5d02b4c6446254d6851f4 5c9a64f3527e02d78cfaf94ec4164322b21db83b5b6bd9ab8fc5839d9e5b9d47 dba2dfcb69ca7436907b9d07f37237951cb7e022116c8a7cdc3ab25ddd3baa08f76f9b09a16780bfd126b8522dbbad9bb50ff69947da11d307e9cb2881fda504 true +check_signature 70803762aac2c84b9cf8e9dbd28c06f2f79e48cd12edf9199d191ffe70f38ae6 162f75b0f15d2eeca62d795bb28f1358d8ced8781c182a68be272964b9663096 ae3f7ce42296dac5b8e8dde69969948b62a24b48d3a1b6faaf0407ffab938309d67d5921bf610bd27775f934a31256124787269797d061d69ab09d79fc51f005 false +check_signature 10e050bf8527dad46d330986cfd41d34d53c6c7bdc965c42a03c1fb593901c6a a6a198afa0be0ef248a4d7fdffdcfd397e8c6a69e55a340ffbb550b361a0c5de c16055afd267d7dcfed2a87afb21c8bcd1e496ff5f2a21227f0cf2a22d4329091ba6e9687e217f4cb7f6b1716c056f97876c308c752ecdde225facb8ffc66005 true +check_signature 25373b3dec2077a7dbc3afcb1e03b29e16c64d62e54cd3d5556399574f54e249 ebbe998669b0833d4cfe4adfbba80396ff8860a04c2925435246a37e40ca4522 d53c7de3059aabbbfc2c7de02e87d763cb23fe8e190fa3becd9f4c50f5e3290a21702f1d08459571ba513d9072d6e917b498decf0253ee105714a2c268243e0a true +check_signature 4774cf830858f7f4aa0abd9e965e5dfd99b7c4055e6679e0074878003cea2542 44b00a25027d9eb6456a97aa77b143ec9f1155bd844bab53db5137cd4cfc4586 eff149081189ecf51f0e4552882734ac32484110504caa0043913c247ecf8d0534eb063cb2d8b7aedd80f5e47b8c96369e3c9f475b29089d0ed002f3b6a02407 true +check_signature a92ff35c449cd13d599422125f7194ec085fbb618764d99119f0c7216b8d6935 e54b7544e837a90ee9a0c992db295b3450f1db849675e0afda07bf134f475175 27384d90331a6b2f43af2ce69360b855fc81b70742a10ea23e6e6a670971650c6500674e79f670c509478a8bae9693ead212116a3ea9b198f18e2e2ebb145c06 false +check_signature d221bfa5cebe67f93c50ab59503d7cc3832c8d80290dc5a934acfe89752a2967 c7897a4120a560cd376af12fe4e6ac80f3a4917d043c756fa75799f60142a4fa 2faa8004d7b026b963b1d9c9bf5ebdc2193b8933cadee350032d0afb96f2df0894ee9a0c501cfa9bc76c3f170490a178b840a62dc12972fcca882c078d086e83 false +check_signature beffb14792d7c2271498659ad4a2276399213d08f981db874658141d8bcfafd1 b08c1c94ebdc438da14a78bf0b7ada820f6fecb6cba4e58c249b3e9bbcf78eb8 d8b6c1ba4be710f9dc6e8cf5485c8db3473f3dbe3c0bbd39c30ff259e574100cd5c4742e87923f526571509f69f9deab2a013b2d5e78fc466ffab4e520992409 true +check_signature 3d77778e9555778d3038460752ba707483182c4633ebcf2f055e87e3873cb9f8 45cd2fb73ba6b9615574bdfe73153ba113e36dc583f456412d4abdafb436474d 0906ee36053df24c8d85c95b1d25ae40fff7d2eec48665f525d6b4efb1d203381737dcc03f242fa8c8b16825f887a900d8c62e102fbd3464ab499dec517b576d false +check_signature e60cfcbf477cd04b30949d8144213b48365cb9c264c108e61db469ef0d152e3d 55f20664c36f383dbcbd5d15e815629c01fe44cf82e9f548a863b4b4bbcee261 e840ded807e2277c2143988a497c1c7f5b90758067601b395ea194f975e9c0c58623964045e00b19d4c0ad9d704e021204ccd0b6f38aa075b3d89bebf2108c09 false +check_signature 4c10bd3b84c1593dfc5ba84a77918ce982f0b17093685e4447f789c95fa7fe1b a0c522f545440cf31ad6aa43021859c48951b459b4b767e2d287f8893dd32cbb 31901fb6d7c704f0176b8b6f595a66d99d431f444ccb98698da3a7774d860b0b35a792dad11c69d459c30f63627373a75cc42b954d0582ab3e7b1a6006507303 true +check_signature 2499703c3b8f3d1f3909e8f7fdde508a29b1e6f275fa6aa95601d3cf62f91c11 2e4b73b200fa51a22fdea5e625075b0ab4ec08fcb56ad50405ac00928a17bcad c34997f7eb15004de174e5577716d721e97006db11296eaa429fba4d74463b06ec6276aa8448a6a2e11c5f74b7fabebaa8134004a89aaa0bdfc3dce98ff72b0f true +check_signature 65dd020bd71073dd372e752482a8ffa5fecc53fe9a203a8dd80b6aaac978096d 3beb97d5936693bfa297f4dfa1744c11ae7e1b620ee6d70728b5e24501e93de5 19b781e22a3cc73728da41059d098fcb9fbfa5bb5545952eb708e595e451a10dc4e498cd2dbc9e9ecb91212de6f35edef7ad0abbea2a77d18399084df52384af false +check_signature 211a7f9288129cc3039dd4901d643b028c2e51a7cc5964e23695a7518ecdbb32 aa5cb78215091d4e4403ad915d163c9917b5c4411ff0b449528fefc21d55e5bc 7f3b3d98003a7c4c811217c7c84967dbdf7714ad5c5ea889b7808c74db25df035a66df9d591e91daa886391b986fbb85d380823c2770464a2781f0f2e3859a0c true +check_signature 4b92ad1221f8bd3f458415a9b4a73724599941be7d9276853ac386209e29dd69 279770f1c56849c99ae004b8f2a5240ce38ed28ae498ea4f8bef742b288d162b ffd5ce3e548d92b193eb50acf5b9ce05ab07c3ce36153d14e2a7f6dc071e300b5de95ec1d9b42ec8f75041e0c3cbf539b14d39b41a296c8e4dac54ac69159410 false +check_signature 8d1267106bfc70cc4264e24ccf82f17027e5f199363ec0087d9bbf89df3ad8f3 a9911175fcba38c9f3846de45ac8141b9fad1de6e3598c96934f4ccf7b2f7f54 b7f962d4dec0a5e44ac2aeb0c4c32bc9fd7cb3f62a66e3684a254c7579781f091abbee1f470a0fa8e7a408ba29e60d4613d797a5214691dda31e6cda5dc57a0f false +check_signature 2812d41654285cffdfe6e31fc0450e0162efe07a629921a26e0f2cf4150d98fa 9b80210956f9119a3d20c72caf5ac869d71acc27fbad96bb4cbb5caaf1d1b89d e5a00ab69d72f4e5add01272b1bdc98cacfabe2fcf2aeb74e2a6c53e0922420b8986f013525dd0eec8a21c3e064c1415429107c988f457034d3485d383ec690e false +check_signature cbcb816659f2883e1ca2c4d936428de919873304d5b0905c73193f7bb21e4143 9b16d9f80b61ef9df5be9f670eeb4e3b7851e6440bf85099f999d7c5653ad367 f9cea994fab036c5d491b0707f87394094da10143cb6bbdfc2416488d306060cd1f8725bb897e423b3d275b4f019e881a5d26ebc340866dcb422d42ad59e0605 true +check_signature 985a21f7e6f0dcec4f77bdf2454abf460fc741ac05b15418563448f01dda111f 0a1e6759819cc59cd269b98c5012658edceb3409f49f9f099123f747cd7ab279 9434e8bb9a8aa2b0168c40d13c6b01f6cbbbff7f04783d8bab6dbed10fcd50092ac8c7d8c4bd2bf7c5b5dba2228bf55ba52fde50d4b15c002754b244bb85fc05 true +check_signature 35f62aed83b6c40bbf949aa1d429a766d2824219081420cef1c6dd7a3ef9d37b bad8b1ba52c463de9c8f09822e5dcc2ae290878fb0e199004781f5fd7297e19e 6a5e68aab66d2d7919eb7d9dd5610b32e328c4e7b89e73aa90e203266b7e1a00badf7033773713e18ff870e8b685f23921733951fb034f7055a7b8b75f5d7604 true +check_signature bb433ea96995e0d5928b63607d1b3c599f807ca40bcef83c3f444a6c5ec35df3 3192b5a1da733ee8171b0599e8300189d8f129ae9009dfe5e82ef8e204e82566 ba3fe0c0c87b9e68181fedb70b237136a0119df64e02d011dc686e6193d36d01ad41bfda963621595c4f385a94e65b612668087715ccb1760c76e82d99f46b02 false +check_signature 2d5fba517d7e0b0f1d9171e7026e2f48a91c91bfb408c198cec66a37871ea07d 3f197b7d6d450e592146e7724249915f7d686d0872b5bc446b8abccedd9586f4 7b7436a16dceda73d58504315608cee1c7fae361daa7d934659642b80f7ffb062e689a2c07d6bdae1bf01a621229daa0df3d8f719fe5cad55fa6eb37a2b70503 true +check_signature 8ab8b8ab0ef0d34af64c62505ced1f4795d4cd5578213b69e58f5eb2594a05f9 39a555b306d412c5764b260df9cf92baeb3b6b00596fe311fac3b062c82e18ea 30eea1acd1504f9c7b23e3e33ce9152f56320373b857e3b9aae1d8cef61c0d097a9922e1ee166d46dd6aa3b97842d1fdc329497f7d4e769d8583afe498006a0d true +check_signature e02c1eccd7e655bf3e0af4ff8c29ec79d5bb4d061fac1c6b37fb7aa2052033f7 b3f1e174bdb43168a61ae8935a395e55d3d1eae0eeb7ff5da377086c5cac7fd2 30aaac80c71ab9e9386a1e74a5d06fa89915695d98fb61d6505d033238858a1182a1ad666dd7ce5afc34957b71089074221c7d5f9cc5328352935b56031c990f false +check_signature 85f7167c95491e8c108ac70f6228c47b86f60803ae49411aca2225ee186c32f0 3209d0bdea9a0cf0fd20438017ae11716257af09b2d874073997cc04c4748260 44c52d718e3e042c0647e3791e6983470ee592d099e12ab82868ebaa6798e90eda10bdfff72281e68346bc9813144b0094d8da24f3b05bdc837e31e0f509940e false +check_signature b7bb506cee9d8f58981e3aa5793fcfa698d2a61a5f5d6fe8ee306e042235877c 65685e60bd1397bed21d398a1965c9a94c312db63b755a8b930859e8098bb0b0 d67124472451998ef172eee32da69c1943dbb1e28567251a67e7b40a7d5fca0a8dc4aa91f19119006b0802f60312c4ce82d01d25bab5b4858f2f44c2bbe1cf0d true +check_signature c57804acad4b994f6342110a63aca16858fb89c34ddae5c1a19ea5a4d39fee56 d9b2ab43f58fc0e2854c9307986b3b029ce3b50b96c73a0b1ba540ec4f90727f b449a83beea3dab258aa447d8ec5fe05b4f036f7bf4f9edb75b40f7307b99901ef188ff25ec3caba740c8df52639edd2214d30682782a21eeaa847aba71f6c06 true +check_signature 50f6167fac8c4a31c58925969aae938c72466dc8857f82aa1169dcded3623831 247abe1303ee941c3375dfb85c6447feede40ff6e4b6552c6ff42e5bb50f56ce f9252c5e6dfc6fe3a6fef5c8a94055ed9dcc46c4be0b52b2d4f2e930640f0c0ef5385cb2d179bdaec3d9f329f51786d7c10f2f547bbfdd2925a8f76529df4f08 true +check_signature 0beb6e1a4c5f2f25a31cf4515743c0acc055442755a0333b4de62f73ab339ff6 e904b83ceb60e741e428c1144bc91e59f4d1740bb05f59a4a974649b084fe72d a0120e36d8e8e83586ec2d56b182f268296fb521e306d717ca07706313049200a38ea2fa55c81d5af80c1d5eba5db76f42468b144a662b37c3573fb680cd6792 false +check_signature 36fdd7ac27c4513b3b0bee919003d746d504bf8957640380129518997706ee0c 0e18ab7e439a1aa66392621b081985f59ce38d26a224aa93696629caee3da23a 5a039f41423b0951b4084c6a95e49bf6f6f779219709257456a7bb3b49fedf9ecd8177a1fe33ad27a2b91f7d122c822f0547beb21c0b92f7d6c094b2d7a10c0e false +check_signature 1136d3129a04b60f7913daeba6c408b696e3b7084d465bdbdac87a53beef2643 5707b8084cf85e22d9cb1d349ec1919e76af1e52fc1fccc54914c6f626be766e 0784d6c734c6c5d2fed168938f525c9ac916abc1cfcc0165e4f6ae15191b930b627fb4572d740ddfdeb173ae8b73cbe43e0bafe8b95a00df6da755301b25257a false +check_signature df431d6a6a65f7f622d3ebfbccb42cbf9727ce5d919c0f5de36371a125f62afb e9f1edd032548f2cc048119fd0d87b2496528b32dd38c09d372e4aa794402b61 8ce888ebb5af19b71758ed58f8faeeebe3bc80679db5befac534de85c337140e64df880f1a7f93bf72c85e007fc585564ba947f44b444a8118f34eda623a335e false +check_signature dd12950b6242b4bc0b6b46375156acb0213a4b790803772215f369630b7999a1 175682b17d2b3ce6b5c649463dc51d0752dd277a855c3bc164d107055f4ab413 7258d9deb9bee12adbe9079a572ee53e8b92de30ce2c1188305005eeef85c757fc209270e52da6b5ff9d32fd3ed341a7564b09b30c9e1ed4f33b022080a42867 false +check_signature c575f0b8a363341227181c5769bb2396c14d3fa14f58838d724029f81239ce6b 2e300affde7e6d4f709a3118619a37c0a759cab892692d61dba0ecc2eaf51955 fcb03fe0b1a66ada39ae5422770a81768f291d350d0d25c53535f4dc1f5e706ca0f5426d9c53ad2587c131b28a81a462d6230ccc2ca0305646b5f60f19882e0a false +check_signature 3463c29d583ec92a5cda92b104cd3f16b422188539782b80003d12f0a4edceda edbe8fe936ac6ee4dfa599e7776c965c3c659cc62ba849122870294a8e8a2367 35a5c8b8e54c69929e8999d0f714fe3cb0430168aa1048347ac5883d15f8e344aa0c9c57120c03648511123f6b88d65d66ac6f221d9f2facf1ca4744fe713288 false +check_signature 0f8589b6aef6b606151b6a157c50c5b8c8ec91fd01a70c8b0fbb067d91b576a4 a3f2ad16376c0ee69e6e03a1b82729bfa929cdd8dab5f1ef78121497ef42a3fe 2122d3140ac55e88e48c0f926c992c6ae8741d6f84bd1980283f233153d7208714605251d2fbc3858bbdd8c4764e1277fd67b30751269531ab7c349f53898bc8 false +check_signature 81aca741317d3a37e5dc4ff2b1d812b498454ada281cfe9ecafeb27b51fdc8cf 564a70978b89a0130873f5a301fb04971aef7b8a838569b7270b9cf18f39ce7d dbdc52f15eefc81e9690b4bc51343c6e830b0daa46f4c75a2139134955ff6d81cc6f9102c205b38a08ee3b27041b81813b4a68f29f032ff640e56905ccb3f107 false +check_signature fa3c47f81abc7b725ccd71beaa3d2fdbfef6465a9fc89d1f15751412da724b9a 6f2886dc506f0de26db23e00b8af813ad52f76c25c76f524faf460f6e53186a7 b806e01c819301feff6861a9bf2b12cf1dd6af4cc72b611d20f0bfb605ca7d05e3dcbe0a656700f77d041537f4aaa47e62acd381e6477ef8fa5b8a8ae7f21b98 false +check_signature 21b4a46550c381cf0e52fc848248fcf01c11892051fe4dc84f81f7e7f4acd373 dbe71b7b8588a636a051a4e1a8425b14dc532e62c1aa5c553f36bf8421e09d68 242211df6ffaefdf93b790d2f0f7df8d1947c18192d9209bbc6a637af3eb8545850499e41b1b729274aae4c5ead8ef53316ed241e78abd08c33cc786aefd3001 false +check_signature 7f37234f59caae084ae90cb84e89ff1a65644534f60ce294d67fc00ee882e04e a2e212064b1be92638317bb9078628a75729e3f035e84b0d83db0d72f9064187 87ee682f6f997a16244d5a47a488f7892c4ad92dad9a008308b988a5c00678039ab3d7c67206858c9b61b67b7eb7a80325551a9ec4bbd279f8ceae3eebd97d0d false +check_signature 4167b8a8a119d29dbdb15c8dedc9a52b2ed2adad1bf3b16b72caf6e6b4beaef1 f09fdc4f6b0d966b36780273737525da8935d4b1c995016ecf45b7a08ff25b17 61062ca166d76198a5a0382abb0490805c144773e1c63838947dc926d61acca3a7d765d5ec2ab70c2b5cdfdc75f8eea0566446d8f5441487d4f57dab4a371603 false +check_signature fb7c4edf1daf516384e71f32ac7a8bed11ffb61583411889f139c72f3f47a57e 7db381f2ca072fcf4ada5830edd7f0d19353c5e8006b9598be39b9a12c4d0f92 84e0f33b6e63afe2cb9c4d31ab60ca38898cf5706b58b967468e61c3730c730664fd711bc05e8b9127086a768632814b97fdb9925c97f345b507494bc07a8408 true +check_signature 35e7996d549c84af0cdb7050d1acab37144294a7702a8201ae6fc6696cb66a8d f22e11413468466e2a72406fa9e222e1abef336eb867c85f610ed62127bb9020 9f94f7277338b34eec4ae58c83dabb12a398b8b1c52b3cadb3aa1a32372baf0f9555e5151978417af925bfeeaa7fb0b16e399e4edbed5badf90413f66faeb637 false +check_signature 9291dca7a857a98ce3234248e4a0a58b5f8fc3dffc77b0d406e9b8ec6b11dcb9 3fea9bf99bad6622b20f0ea9d7e2cc1f29253f5d1006a329b6f333a207a641b9 2880283e23e96f3d5064df3ec3e900d0c0832b12ca330fd4e4825c2789619088718685c6ac2f81fa2ea5b680e041a9cb46c8ee2dd15ffc490aae77b55d524706 false +check_signature 9578002aef43b7f9e956683613f1025baab1ba8a4d177e34169159c2d678d522 dfbfc9adf7197ba736f93822be4f65bbd33956ae0c422bde797d0056ad5f8686 4771c2ac7f3273f4e3c3d7f4964303e7c75d4160b633b606dc21aaa8696ea5062735641f6dab203a397cf136f44083e43b07a8f3d1af2dae8ea6ea8e15d7d904 true +check_signature 3c76d1ba6c8b652a812348933fca2d255ef5b345ab6f5a1c2b120263a4a5ae9a 3fb7fd1e5776f2b7778f5ffb73c44c017c96565540572aeece9b5b7bff8b6d6e a67d2b10ded01732e5d50b50afa85bcfc0b3dd78b81f7ef0fce0bdc95de49c0f41d4b24306bf89220544106d9d3d3ba39c42aa26a9c4f6bc8192bd7f826f27dc false +check_signature 86ada139988187d101e04ae565fa9d2d8b42be7f685dbf1f00e034f62111f329 126daf80e5199eaf8cbf77f3383b3358641b5762e58dce4dea87a20f4ab9f8de e3ecb251867e25ed81d5515a56cb2a3eeb610a8d77be470aa1638e90d25d6500c3fd605094ad5a03da18c9eb63860f3cb6af2c0905b564f66ec794306bb5b20a false +check_signature f0235390d0423d4ef7a9902d9f3ede98faf971a74a6c713b9eb909ad6168a026 e97e47e1a1e3a3c44dd2ee3dd363a33af10119a1f13b2c9446ff691b1e84d522 be4a9272fea7e139be4be04fd03bf650a88bedcb1121b4cefc0a0ef1849c2718c7bbd52572f5000d6d93b2666b097e20fd877b67f38887f58814b989981098ec false +check_signature d388955d5a3739c28fec78eeee112fef267c185a8e9e3b16886df455114f54f9 fd81db6e0e62239629443ac4a362a6618e243a0495b462ed3f221bb2b497ec7b 1b6546d013b0e9e4191b58eb3c81feda2b2335c106157511d1480cbab639f409f1f00d27f5a74195e6ddaaa490253e1876630bf5ac236a91c1352c218e086289 false +check_signature 7f137398de95a31b9ddc0dffd03fddcc7f3b3f108598b50d390f16f3555cb817 feb26cf841e5bd06695cd56e54a664fc2469c8b72a3015b3e4b24895838c69cd 8d811119a4bc8ce734f6b88dbe94595eff7d5e29af8b2a8b67e93c1e61f11002a73d2528ae06beca75520363d732953709a42a82c3855817bab0d7fc4ad4a271 false +check_signature 6217ddc1d0dfde5ac53a91026ffc848f779d56121ede0447ac3ba6882123f1b5 c875b8f4b05d93d5e34c589ed9ad85bcb512e0c8f3b480bd6097f453acf7f28a c8221f144132851b192a5492ed41486f60b26ea5fd13a8e5cdf4115dce2baa07d8d4af5b6306f2e34ea1ce040da857e720382c47d452b51211398664bded760d true +check_signature e64fb75ab5f07a37dfcea7433b4ebb4c3d1fb870f27878e42867708d5b19fe68 2b8d37d051f7d0307d217d8f94a8f8cf89f0570ce593515115dda79114054447 3c72e12e8588f9caa4ad6e18d70bf958d7c585474a523241a60dee4d2fb5220451a1a1f53778bf66ac189d621a61f32fb1ed7de18fe20ce5b646ab37f6dfbb24 false +check_signature 69340030ca37a18af2c97af6e75869ebdcc31121a2e665abd323f6444ac6c46a d398c4da30d52d9a9effd9a8cd6faa7252e1fd44153f22e8777dbfdd26875c1e d56685efa70e5ff3eb018158437b961162ee381361009d37d122c2f018edc3516e83268d421f26146e7dd19a8a3997463a1e2d87fe4e4df169c323ae44557b1f false +check_signature 70b172202d389514d3b5d5c2ef6cf9e72a47c744c07ad1a79fd30697ae6320f2 796e3545bbc1aa44332b17d9c2f491700f9e580eb9ff8d55c63ed9bf27d3971a d7abc9b224967dadcba184ebc3062d2bc12e39470b056df498dad821791db405bfa63bf7c55b21d2ac6519593335fc5682c36878805ab87f4bafdb2323fbc00c false +check_signature 1db217b8158acdc6f29011afc30d18c53f776636bebc17c727d7f180f66bff72 1c8fdf48559b3d1dc8a6713cb4f0e7f5dacd96ba9632c827a1f2bdaa2696afe5 f4586d2ad86534e419406e70ed5c8a53cfafdb943b29e708d6c31e5c42f6d308bc55d4d4fa44bb5a7291dd8170a156047b80323be6159cc2e6a8574c66048f0f true +check_signature 31d01eadf8c241f863a20c706eed90fc742099b0ea50a269e0519ddc6ea92112 1650ab73d95c06bbd3f632878ef149cdfe7d37c87cc3068b3a1318bab2aa3fa2 42b77110d72e69a335e8b630926a3063062bca3d3d3258731a6d5f613f32dae24c997d2580a6e8a693ad8c9d4aa7501046bc8b0eb1a4eb381b2accd81119fb00 false +check_signature 53caa369ac6ebf9b7375fcd7afc34cf25bba053eac36976dd82c14fa222bf89b ec490202e7b4b81ee909cbc9d2ecfb356619472657103d9665bc1b3ce0faf7ae 8f5457f3d2577482726c6ea7f2c0ed2567343ad231ce6a06a6048ddf7d15540d45319ef2801a2c3cf4b53b28761b0d1021d351c0acb163f3ffae5e386dcce00e true +check_signature d5bcbae7bb34f53484c075777e5e70133eb6056269c404792387c5af18d53ff6 74e1ea6e6d19d0bc3b06d699c41875bb06717423b7c808adf2f180b36dfc4188 68d5ae8d895cdcc975cf13b9ec79b4a004542438a91de1595107748afbf4065237dbfa93d5718716963c9b9a5386e5da7e3852634dbef2707bd613e674874c09 false +check_signature 71ddabdc029ddd9947ab0873a3355d32db50b3a337caa50f77bc887857f849d9 b11f5f5b93a28d927bf1864a0d92bb72eeff6a8eae933a671fd074ebf42ef24a 556c428af30606371b106511f7231141a6ad226a7b3a999abd15886a737a940ba1aa180afe46285d4a04e34fb5c705942cbd95a118a8a91dd31f43e03a8e3c0c true +check_signature d4dc2193154f243cfbd0cd79c81001e88d657c49c5505b56c089bafdd9faeb27 531468a09639ec411511ffdec1f6c44c888045218c77ef6d7215a9f3dd2cc062 94dcfb1d91c0ef86815aa8f78a003238f92efd83196e92bd6f1b397cfdee3e0eae65a33fb77969aef4642775be78fd9b3e2d767129216e1a0eb126711f68ff00 true +check_signature d1d5e3c2603a816ad8865c327d5aa4c95f6f66a0e74d81f67843f60f1e6fd3df 205be25d979cf6eb88203ae5bd1c77561b8869b549f690398afff42dc332aab3 6e8218696c3212ec1aa71aa867e85da2740dd358bf968ddd029d1360dbd9f7013cfd88d1224a0a885bf8327fa3b09954ca178b1724f8035040f0f947eabcfa87 false +check_signature e19aeda4818f3bfbecb4c4311fddc05ff9aa1c7781259b633017f30d32a94fd4 0eefb1972ad062e8329dfd458a9a0c967ffca16993500d71a5ec906c1611c0f4 9213ecae826cc55d1754bf54182bcafa239038bddf6083de7cb722d31fe0cbfe4c0c8d5a318058fab072469a5aafd50bd7224d9eae1d95d04f806c290df9c705 false +check_signature 19d88c6a745039a3995a0e12172992708cb26750f094da6913dadf98730b2f7b 70ccb7a4f46110fed717728b073484131f8d380e15c30f5120b7d46b6f2332dd 067e9b717aeb87c0b1fcce748b29bd27b2bd47de2a9316b82a095107845ce215ad44433732fcba3c31364d0141a88cd1b51430293078a619d25d3cb914fbcf02 false +check_signature 132f23d4ffa4c55b3b9071dae9da7b7f2644de8e59877e31fd3f86942a78c60d 3b810c500ff5917f7ad2cca24898bda92c7d51fb5864cfc708b1d2f32b0abe3d 09cbb47d080ff39b97257737accbe4fecb2ff42bfeaeb39d08dbedaeaf8d454c2876736e5e89018cbee49a734e26a5d989e125977eb7ca222ebade318be0460e false +check_signature 876ee14c237ce36aabe5dd0631db19f6acfa62c8af6702a94ae9db5725c773d1 b014bf229e4916d7a8fee8f97b64c82bd3742a69225d687b1325e15415bf0863 42713d87d1de152e727dc342ade3d1bb4863afb93511771a6539e1553aff130403d24e82a161e96c943f091bad87d87d508c0a65f2ceef88e0e71c565718350f true +check_signature 08280536e94f62c6fad3c5786ea2757611cc8b6b4f25e3e6fbaf6e0f39239054 0e6f96a86698814f5f307f36177a5c8aa8cc91f8e4441590054256e9ae41a549 5585338fec4de424b46dd876c3020de6e14bb0c9d56e60240f1b5f4824a23d0f05fb68a119a593b114d427f619f45ac0f0e642570ff78c5a938069828983fd46 false +check_signature caaf845a85c3b7d99c5485a7c4eff54894661fd312ab36cae34de571894fd7fa c215e67cbbbc2840599912d0d3532224de2c0b7b4cbf4e71bb260058088bb6c1 c9cd72846a811f21b3ddc73efbcb489909afafdf35f1363fe841f3319270bb7a5c3a59bf95d04f76543dde311a17375eb7593410404abdef6e281e66bdb21e66 false +check_signature eedb01f7724c23eedc07245b9b0c89bced2bfbe79713ca9eb316ff4f7ff71e4f cf48fd045fe8d2b19c22f23d4c2d12641e3e7ebfd4abc199597e01db179fae86 429ca5e8de8c407e736b5b7c043bb931da0bc17d025724576efba574773af408512f51ab3f25937d7a0724be2ed06e07bb08715db73a53d274933e883b7c9e96 false +check_signature c6f830f35ab0581a2a1d73628f064be4c0f8de1a4fe808fe01b0248f8b1a05c8 5ec6c6db820a6ee484ed0b6482a869b3cd3825aaf8be4f6c320b247b524ff08e 4467315c7bb051b9365a84b8dc212098a2932586b7fc6d68d4903baad6f1df00b002c1446594ebdc8f9bda8c3da38e21a9b6a5976839ea538eb83d7036bc170d true +check_signature 5ce9b4a67c43ec6fa2f68319b6b46017366e8cbbd79bcdc42de37ca8da32020a 1078cc0928ec4ef2c7f101a715b6e08c44e0c8f79869b886eb07d3d7220f9bbd c8815bc1f7c1b7196d67b984eb6756e67b0eda2b654553461eb2ac98642a13befa02d4405c6d5658d751019ee82561ca5bb372ee6ebfb0c2b557d7185d6f9cfc false +check_signature 878d1b3f78e1d2060f5395f7cea8a70b61fd2b56cdb13627f3b9552ba76addda 12b542e77d1ab0fb04c2fd10c0611f12570963cebbcbc43ccd53f52b50de8cd9 258316dc770b78f07ec0b25971803f0f4e8094845b040edce45bc394a073760bb3434e90eb74f1f1a3957f5743ba8ccc11e367230b5819f6b6f8a91635eb6508 true +check_signature b9639ebf41e0b0f6ab8b5705c84a2b14ba9248562effbb5c8532c96d87455f87 4044f714dfcd5e0918afa79ca09a2a138c81317216888f2f6341bc9b4a183df1 652901013f58d92875241c445ab0edcf1f47f7ef754721af00ca53b5575bc607e54d3749f042de359d1ab1785297cb68d9215d45835be608e3352c0028808c02 true +check_signature 22a7f5be60f26ae88cc1b10040ffdb733ada205444fe6addfbb80a4a13aa355c ae7bd26cf988a11ec0939358a2b87dcfc4d13316103e15f5a2ed09a4f5fd8084 52b9e20a963cfce2cda8924162b157176b879409a2790f6bcc8d2dc94bbcc36e179c734096d5c83e11609de17135feb1e4ab4b4218e4260447ffe51da39e1c0d false +check_signature 0af57c7cc4a963201637869131f13d715f1d9a08d829f9f09221a4df854ea969 97df67adf3bb3918c16e2ad5c8f5b1a9ecc49d899ed3427a48db3c9fc651d349 9f1677e2c33c0abed006555e3be962620d05621b5608c99c975b974baf694e49ca0a71d4e00da56f27231643e1f677b7c38d13d58d314ad847a75375094d3909 false +check_signature 937debe9fc420d2de66ef033fd894805f01a1980852858949e5c8a0a8f29d54e 0a6242246cbd5639212e1100706d2db4289e272e795f0f05b0b80b28d9143013 7bb7fe8e5c8db2cdb10a18171ee8c3e80fad7d2f8c66128ef60a759a8cac1953474cb3c26e097d8a423fd56d7d2b06b2aba89cebe61d13d83ead7f7cf1b82c0f false +check_signature 68366b909b8a688b9bfb9aca3b7bc0c4f762b66b9f2cb692481ba593e1192bb0 07e7d6f504ebf170bcf794acf9cfe435f4b2ce8fe6133903ea5400f28b200f2a 2da982e60722a4d38f6f0372717a0b080a52f80c582051ef0593b33d9c1a2d0b42133c11a5e401b52a272e4668cb7dc2fd2eca8117e498dcda3072093da8840d true +check_signature 33cd44a908f89a856d056f0677704daafed118cd0bea38f244746c4baa152837 02c6111173dc27da44f4ce153a24323ea3358d5367d75486176af009fe5a4950 f6e0e47d33e890602e1f4087a6dbcfb83b53298eac36083b41c14f85691a133404e7885d240b19420ddb71ede663856c8c50696af41235036db724591c74b0f7 false +check_signature e54640715689d41d6856e554a2d5e4bcf909b5cfffdd37ddc6255807fd552135 6e023f4d8c2ea587c4045c99e660b0f8ff80fe4ee0facc578a3501964ab624d3 a82b6ca05dc8119546122846e93cd437f708b034196f5416932266eeff6bbe3683979474d23dc0933c7cdbb7ddc7ba532a0f48f60f9f395a9a1d55966a9b7d06 false +check_signature c156d711dd018dbb58b92100db9b5de0baf5166824d035d006b2cc48069e0323 46ca49198c7ddfc6bbdc34bd0220144c1e066e010ad3b330f1c7ccee281f7e74 6c359a2ed57711385ba580b167f394ac4115e8dda16f43ed4c3a725a1b04d0271d8c85383949499947fa6d11e58fef2bc61758665a8896effc9990bdd32dc70b false +check_signature e2c6cdff5e175a0322bc6d78434c49034e355cc963a6a36bbdb54221f19fcbf8 ce11a2ce5cd427233e9c28d64dfe4c159a10d63edcf5574179ea2715b39071e4 c85402f8c7c8a234457d309d2c2ee0e6d06e388d710b47d9b0686fe208e53ff2a1d45ff4139756463bfa4d604c5e72fe0605019192f31e481ef15e3d8479b60f false +check_signature bd7e1b95a4a4a33257d2bd2e34cc3a9acc611d11cc3aa695c8abda5806f76496 fdd4ce78d4b62c23c8fe1739b877141632059543e4253b0d3e0a36333725c4fc e71e4efef507641c20c374af5280992019e1f5fc254a3d0c24aed006669c5d61764413ddd791a8690781cd2c4e029c0ae3dbc60e0427c6f756f8ddd415ab1200 false +check_signature ebb7a0d4bc597a7f49d5b6382b40438b99c849bc40ce0c72bbb049882c73b34d fa4de262e8a877c402b5b102e72cbb8ce1aa6e53795dd2f029c419d2c2563fba b7363daf2b8cda81277e462abe6bf43c5cb6a6be43b5628689b45c594f5f3b076c39b31084ae6319190664dbfd285933a297f75dc64935fff25b875c9b55070d true +check_signature 31b243dc4cd5bf6e557cf60f16d0fa351dd078360e3e49fb12f26db6c30eac6f 53c11a11a3038178b41a32ae8af18599cb31883d454ab7fa6898321967b0594e faede41b5ff0c6b407e324fcd7cc0dfc3505563095c0768fd3f74fc4c1f3bb05acb40c71c96e2a84c61ff281a93a78333645cd35fc69bad87120530a92fd250d true +check_signature 154b338ab1a2ffa8b280f55d9c3383577f34da0affe03fb500882ebb918fb874 311a58db00318e5e641946cb921325d5033ab22c8282206325da869423998c5e 42e433743aa4321595f58aa8352686b14981a5f9666eb53754dcd3a99f32588055c4e2e8b010d77306c56b52e9ffd010fa4a4f843999a237460ca29a6168d80b false +check_signature 7b8a3b7bcade546db6329116c31c14dc3fdcdb48d0f62eefbab3bd5fcb030874 573c99f4ebc64b449e68b93e18e30db189bce65d5d18232be50a4ba6d106be49 5ac2c25a94f9b832508a3f71b6bd2c4fcf80e70e2485f8b2a665ecefd70ea20cd8b82b9360dd7c80e46c7021d07d4158050616c31e5b20ade82a660e09b4470c false +check_signature 6e177ca3ac668b402106590a546cea1a314860ccff86911cb058c2b54fe28358 7ac88d790d05934b3e0d68ff09ea7fbce809ae23664f3de710bda01eab3530c3 0c93a70af85de157f3b02dbc15fd9274bc0478a3a4d25c8158661644ee3b8e06c2f55a6880728d5eb841cca7179a271f2992903ec00d908c7b486afe074ca16b false +check_signature c0191ecd49b3e32e02e9584b4c3f394a57c7aafd6332eb810ce73d21a87f8cb2 fa74532d626f0616f7aeeef8ac67af441ce3f9f114b8236d8599499e3da1c323 081a17032c4b9a981978e298dcf94d2d8c842f4a4e6b59523808449c3e180d0b8b04efb0d40dfd6bfc498d82ae99298c1acaa56dca5ec9be1e42580355da1d0d false +check_signature ee7a6016774e889be3074574c3c76200ec2dfe875ed3c77b3dbffcaaf1d4a197 52b2208c523150037f1c4d220449b4be894c13721d48fb65ca80a40c3d52c74c 03d497cc4c33a0f4081302a7a5ebe7be011bd0547e1ce071fff5f7b8196d0502ffc329ee2f5a65b42166e2699949401d555b9d0ef676a3277c6890da052c52ec false +check_signature 7ff2189f2481ccb6a77e20e265f3dc460d24a1f371cf0927c6f74fd4e7305252 cce91ca91c348306d48ae08a6fc66a4c3a4930e3d95e2a1256cebfaef07eba51 68c47b199cc65a99df35eac3c37e70081b885da92e2815c316e6b261e780d6d8d26740734ca7a1fd3fbe85c9525f774cf5f6964d1181693cb86a52bae9182500 false +check_signature 51b8fc825b76e7a80ae3b8eff61d5d804080b0e082dccf957bc05642791f35cf 73eb03077fd2df2938081cec9b21c34e11ff4c80df6cc46afd4d1cf9847dfc36 7bb0d0c0045b1053be7aa91621333e55ef20f1f0faa298352ffe4d96acb18f006b91255b8adb46c1598608d77b990faf4d543018c44abbb54e01044c07a85b0d false +check_signature 650340487ea82de160fe812d0af4494b752ccae2516f8069826808461adec841 fd7475ba61ef2300e2d6b2896eb1dfa3a9efc650d8bbfd15bb00dda8e92f21f7 ed554ecdd7900d7a02d7d55d4dffbd35ce924c287a52446595bac78c00414d696433b3d56317764cc086c8511012f02589904e42f2ff8e4d76be392ea6a44203 false +check_signature 5814d29bacc9e55459e95e80e56d46df65511f8a81ff52062a527b2e833ddd4c 60f70e15761877292ad93a0b5193604b1a9923bf479dbf7abe5f7d1f4bca4be7 5a89bb970f4aeebbdc40d390fee3f8993c51eba928a6f564f4fbac15c9582e09a7479e19421eb90f473ba5b6efe63e93d77bb77f3e0e66cd778d9b6d2b9815ec false +check_signature 96c1c7ec680b063716e1e76e036b115c8a0df7416cd7a0cd51ccbd3d6ca00d83 4a19c3e6129cbe4cd3b7b57e0886b2b965efe04584a54db0d4eedebc7a160393 bfd39c90cb988ca689d1d8706f1e4faaa47b81b823085ac0f15059ce041d280735863ff8a54dfa4345178f457328ea00b39ac274efe10b5ed45b34be5c95090c true +check_signature 7ed85d101ead78bca7616ef47dcf0ded9fa4018145341cb6a6a78d59360371e7 e83369976426aa24aa0b782265536b2883177f6081de43c000fdd55a24e9cc09 422b8b4730af951c7f6007a59f3adc6dee9129bf7b45ecf23766049bd5bf2a0520609f21d15ee3ccf631efd2fc5a514189f30b439a4be5173df9a9463d4f6f0b false +check_signature 63dbd4d9a300d6e306887163050c08a8b482822ebd72167c4810ac5d7d959f3d 050b715c522cd98fb39f8b72c4d3117c043e5add420714ca387bc3dc2d43b0de c94c6c1b6486688b1b19d95764801945b86de2516a04a1d1daea4787ccc6ebbd4a995c0234fe72e237102d2a60dd525af60c68eca3acee81c40e61bbd12d9309 false +check_signature 25e2590190acaa3659dd5aa7b820af0180cc80232aa2901a0dcd51feb3d14723 4fe77d2e4b95a74e69f45ea93bd01fd8bb0bdf3ebd9ad9d98f9bfeff0d47e14a a1ccd164488d62cac2bd31812b66b499f6861717126d1d605f9132ee88753958cc7264dcb345c8d00a2310b60bcba63c41a18b51f857adc02aecd774bb40820c false +check_signature 49f77edd4bfdab034f78296f0ed5ae702ecb5d9cb231d042cfa96ba0b492af6e 2d78daf80c62ca09eabb466c649fb2bec07350766f6101097b1317704c23a7b8 982e713583f098bbcc2c3ebc6d52d36be8e12c9f7ca4a9f438412dfc5a6b770ad475e9daadac98ef18389b7ff8935544aace1418bd30eeed61851c3aeadc3a08 true +check_signature d9d17fcb8c69498e56d893bffae79f2becc8c655d16902934379f2041cc54d90 396a490c9869e151ea4640db0b9b25d9a982351ff400de5edda87d8c96dec50c d1bf5d19d326d67e70d1113a95c4507535eae60bca6d58c760c3ae5ab4fdc70e08996558c075416127af1550cfb5716a61db78ca6699dd8c20d7b39780e27d01 true +check_signature 60537ddcc013c5b770e7c6c1b21cbf19717f396d6bc0475db7a13aae95db5659 565b3aefdb8b0972c56917a63c3299a70968afbbca18b5fa4b74aed2ddbe07ed 0dc9c04885782971bfd64f97592cb703ddf14a63d743eb420d4ee1bff0c4c3466a39eb365c498922e7601c884eda845e9d7d1bebf3ae554622043f098641e461 false +check_signature 9978f4342bbb06e89dadf5a25ee27bbd8fde0eab64eaaed8f43658c101f5b894 33d10dd7430f8b42016d7ddb9d22fe318973eec28b22537b2d75930e7b84a50e 0e4d19d73372590f178602bfec8890c83c41e94625d7423018924d838cbc9617bde83f169652d0e668a3a00af76c5b0f5f421a5b65cb678b80c94a46f8c8ff8f false +check_signature 0ce32e38cbc229f9e9a7e9734af4dac617221d7224594a15ce9c787f93ee5ba9 ec6a7f0079276240a7229bc18414ca3faff52338a303cada9c47c316a7caa3e0 0ec35fa5ddc4e8e5082b8c9be943d08c8a0648521791a3a00cd561baae3faecf95bdf82ef97381f3620b196d2aa986da4ae110cf6668736a5e4b38c95193ba2c false +check_signature b40d567f073e9da37fe9f25f0d80e854a09ddf01ff1acbf5504ec18e8d17b7dd 42c2b8d32cf1b27f9432dbf9ab9acce304bcd2fd0ee535b7a7bf2de0cdb44cc7 5d47cac547c55759c3596f09964e52e0b6cdfceda1876564832fa5eadae4f60e059e41e7c259024d7604ead1d9f586add83c15bc7d57f4861229acc832a0a690 false +check_signature e47960273c481d4ea9a66a7989b744f6bb20fd20ebfdb4890f3ebc65beb0380a bc34047ced8be97ca687ce7249f29444a7ef1180543c75f525ce4c17c08aa97f 9f4b6be000e3785f994bd857686afb3d4a0ceefe395d3a82aacbbd47b4dbdba2fc977ca3e2a876c4408d9c9ee3b087e4a4d22bd8ce13ef6dcb0ac21d417ca207 false +check_signature 6fd79a2c11dd8a84bccea627d68a8d2280b57e7f27befbe8ee048f61006e53e3 074d4e7ad9a0ebd6138146c9566651c9ea2bbca0ec69b3e7fee6825276896080 d438fb41e93bdbab6b44b99d239582b9b7f0a65e6f91c35ff2c81ee460b894f21a48e223ab32c3d6f8042a99853459c9df6cc0d65653629284c9570a623c5638 false +check_signature 3e3f0fe3b52eb77e55124a43b945658e9598bf0df28a98636b11734303ac8e37 5821c390549c6e02d3013d2f10fa46f6d1c7dc24be8ba62b8333b564b7b9807a 87a99628f30445ae65b09bde72afdb6d613997f5b85ac64a9dc2815cfd24c5052fb45a32f9f11ab2b920f5009fce154b96e0e13c23aa553957145f597a6afe0f true +check_signature fa10e3af218db85d18b2e0fa6def1e42a15826b5fd00829fa25e678a5aefa4b1 c53c5b9e8764c8d2a6ea6c2325f8a6b42e98321abba1b329aa62f044eec25f6b cb5d7625de961f26360fbc5c0e596527578a4c5a2c895e253b1b5a28bbe51d05dbc56bac7b1c8894736d4596f41aa9078030285bcbc62ae9f86631f367b8df2c false +check_signature 10aaac20f123f6999841b6243d6a6ca39eb9506eca366b01bcb44c7cb725a3e9 151d86315154d4457fec43cb12dc6d6918be72dc645fad211462c6ec9fd723ed 37a8984ecffa80ceaab5640854b985d967b40b666ab47ce1bdb25b1dc36cd807a84ef8f68b399d3740b10ac60985e5b36c16b15b49ad7bd3a8be55a71026ea66 false +check_signature 18c8d7bb4e777a9819156f3b4a0b93bc87349789bb83e892f7e04a627c7faf41 7be7f19d28774ce0d71cd78e5150e6a3569ceec4d9335f7dbb30fd043eb1fa53 b4f8621c941caafe345beff71059a0918863b131dbc78576c324fd9f151ab70d15be941b295143b0c7555b3270dbb53e36f041501148d257f9f4a293619d2603 true +check_signature f04e51d6785f8a819d3e563afe4cee86c2612f680ed24b89a1be1b0c6c0ebb42 39f5e6e5c9df9a24efe7d1bc6ac9e12acd90dacaf0da955433de2e964be62320 0d38c0aa7eeab62a9f4d4abf7394d2033ffcd2cac28b14ff3f5b128873d0e5dbc87f947821f3fb9f18a46e39aaee48fcff12adc4968227b880384c022df89800 false +check_signature da804233655a00252e1e0c997794d322a519360a5fafb4fd0b7f353955f1d365 533592e8c77ac8f191096bbb27110a72c0cf76fba0ed63f6e50de80a4aec5a5c ae20f8833e0ae2c47286561c107c2fb5fbaff5beb3db0a1091b66b323e2910049078fbdfba971f5b5f2dd64ccecc84cf1ee0ef3b478eb16748b351e080492f04 false +check_signature b8de7c5f2993d06a746e097eeb5d26612299b8bafff97082993d59898e76474c 9bb03c6cc369098975b6d950dcf72ebb755766ea165b54436cc20cbdbb82f26d ef36218eb01a0e1ece411c0f6cf9859c26b0cf52677d127eb01085b0cde773129781183311da6e1a853f9d365121449ea2bd87c19a51df7e6dbfccf50f6965f1 false +check_signature 783669a27fdd83d434dbe03da5a10454513452a7dbd65169e503d48911bbec18 a4ab311154451b8de2eda0f0375a65f657aeb82c54abd90f8975186c305ebff6 9231b3dc1c0f45d259eb8dad660bcac8f8024ad739c69b4400414785c34f0102e1c39a795063c566e06535faf13cf3df49eea4bb3a50d6d761dd499c3aadfc09 false +check_signature 3a504e7e303755910fe59125aa9895aeb673073d7b03fe52f07c710634231c2d 916eb5d1c0b421e359d4b838a39d76bf572d1cf98336e1a63de604408a9df858 a50ee3b2740f167c6eb44b41f8a1cfa0c0a9d14d01fb7e00554edf074f0b70f6b130bae1aa977f62fd59a9f5a1d57e1b9e802b5a042c988f866144117ccb118d false +check_signature 073330367df820777c50e1364d0776ce39abe3838bebdb36584e6d9b5f134bae 2ee71f0e9cdd232428680c11180ce0e248d5d4bf3cd7ec32eb6f277503f65695 fe58cc0a65441e7b8a41eb2ee9afaeca34b2d4a53f09c8685bf6017a7abba65e31d566f354c54a53d1ea2b43cd3eb91d07537631e19f0c45ced5d5a869ca1b0e false +check_signature edec59c7c9033d695b639fd862f15a8692a1f9fe0e90dab282de66fb1aa4d793 c5c9e16bbddc3209d08c1b6d23774922acab460115f160b728dccd619fdb7ef9 df8c8586c036f5e6f2257a6eb99c7c5e82b3623bafed617e52f8ee13a4588c0f125c36a53fdb7691ab1ffbca7123b636ae6ec78449c28fb42c217cc64993bde2 false +check_signature 41e356b3457b7488387573221ae6c6cb062a0c6110a9380e934a364b5f842053 708dbb7bfb7dbeebc9f7c0dbc0d10cc1105e5b3aa8f6783f55a8a13933ae90aa 7d67152653ba035a2677315727a1a8484b5a7058c7bf728e29ac061c38eebd0ed2bf00fd16c19fada1d608515d90d96bb5f4f6dc0cbe2dda3389a3d2ab26f107 false +check_signature 5dd391040b880769953b94e6f0d1bec4f1fc9fe254eee253778297738ecd7884 7f3eafd2acf175638ec7b82d48687868f7e35e147fe36e7de568a5c00b6b453c d5a9e5047774326d4df311eeae119af34d43e8fcd8fdbf99c18abfc8253a0745294cec6bc89bc92c3566ac6396cf7f0c90c1a713a5f39f0a5c51862ac3537706 false +check_signature f6c13272449d04666ed5b738867207b5839b0b5295a88277a4786c4cff6759cc 9511d71fcf916a879f6585d5e604ed71dd9bb3b43a168ce30ea1982ae3f8dc4a f5569bfb34229ab46c53e051c10706da0a334a9936d59a3bc022fae9afc0c600ad1adc526b607f45eb2b057fe3ce27b22ca8b264a95f48bca8e7193cbfe53d7d false +check_signature bd6548104e8046da7f996e8172e4ec0f5eb99e2b8ebeab86994edc0efa96a20a 16b26c2706019097318821d8d3474e551ffb2430d54f5b2441ccdc6c2bf9cf84 38e3748d25a093aa293574fc2f6cc38192ef167333ada7d02f8b53a8210fef067c9fb615e5787a532e80ee6afb1d5462f63fbc11697f8557aa5bff2b47c2420c false +check_signature be05bcbeebda332cccf5f2971f4ad4510ca818727d6b03c4ce3bfd340e026c17 b1d8bb01a445b371320f982df123f2d614e65be5789cdb734abe01583869b2ae 883719c48f9ad7221ab03ee81317e8506ef597911b633a564521fff605da4668912264f8c651fb4a0329d856a41df6fdc5fd82fa280454a2ba063971eb547e0f false +check_signature 7a315e6dd9d98229ee768aac8df297c5725c95f3c6a4eaf5d293af245fa0f5aa f58a1f0cc06a245facdab56273be3cc8b55685a7021e7821e96ba0f258827a81 2b2e6cd3ef504fbf20f9909c7a20662fdcca20cec321f0d7264eae10bf63300d283b3567afb42c6b39375ed90dcb89dc1ce60161e2ab318fa5662356f190300a true +check_signature 9a31ab23292b12af59100c1384e465674101c00bfe5776d595e103f91c11f34e a1fe54e3290d19d51653b1379dfe3cf944f8b47b50d6fae77f60f781e3823dac caa8aa039b278f5452f27334f8d93ef5c9d59059f3f6434eb55d8174aa77780803f66fdb3153bd487dd4500b07842e0997c0015eec1f040f9498dc519853e009 false +check_signature 0a78fd3f3bf22e43421286d6e094b4af37e40d60cb3c59cc51398f98fafe8703 9c1ad72c363b780453028f5205bdbb4402f6e00b20b021012719b6d2e1346c3a 55ca5b92ff13473f51d41a392fbf9019a2d015618567a9068e72b86b6cc580124172ee1d7651e6855d96c108d48bdb091c9791848062d947fde10ff6db247502 false +check_signature cce84cd89c8df871436d9f1a989c1451b536a2cccbb281161d2f5e58df3d3cd1 f132ffbcfeb50354082a69e9f5ad1d763fee533077eb3e71447040092f8db411 e0ba068cfaf2f729df47268bf8a12f58ccc6d5d92972ffa64d3966cc3ef487c60496ee57215bb7daa14f5ccc283eec329cd50d5032bf24bdb042950773f6bd05 false +check_signature 88e4cac6883c1ec194a8095c3ab1ba5c2b505f37d5189c8849d872cbd0421c2c e89971f89ce3556ab5cefe42ec9aade9c484d22d3163ad150fe68a127fdc6f93 db6e0f2d89cd98b9673fa0b5187b3665a6d8b1eb4a648bd29488ba9c438b080eb84402f44637ee321b3e1cf3bea501947a7c8b9c80f9a3ef1d2c52f2b72e6007 false +check_signature 0596fb156a04f10c2a1d4a84f9decfa1b90ee603d568404417217e83d799852d 6eb1d4186811908ccd9de235f4e37c503535be0503caa6a994ef1688c7587da7 cb6bbede4d3353a6ce7d072a8c48c9b874c3423eeb065eb6fd8104245b04db0be6d3a2b534162e7fea6808e76a6ddf92f4f17f68940bba7ac3a19e510cae460a true +check_signature c56fab461f80ba3d25b60835d6a23dbd2effa61f71a7e556a75168a382a09d61 d8ac4d61ebf29283600c7abeaf4dd1a6349195c8ef2cab9b87b48669eb17fb36 1fee713ff55fadf42c305bded2c8a77bc6fcee66e6135dd76eed669f230dedb174921f4ac09ba732c1c3e0d988fe8fb2c3ec8897156811cb4f42b8a90c6779ba false +check_signature a59671a1554049f512cce865a6c8d28c0f15f5344753f867fbea6ef943cb00de 9ddf6beef89f1a78fbec4c99f5c9b4f93bd9f7856f9bb07877e7608e938cbe65 1713021402ab7bd0479158435a353122199df40c039b1c36ddf0b668072c4d03231f7e06a58e97411bea95e1cf627225e0a811b8eef589fb3154bfa837e0330c true +check_signature d7ae7c1dce5c5cbe0aadf8cb30fc75d2f75c771e7ca0aa5ac54834e46166e08a 6b72f831673008fdb884c27357bec549ae5c8f77bd6aeffddb5f5634b36f2738 0ea8b012ef7e387c9c48940cc4a45209a31a18df2c587410be07b4d70ab36a0d3b8c364fc94c9fd7c479799e6c2cade9e44a847133f11b0c0304b2afc6c31a04 true +check_signature e5dc639811580bd16941d5b30fe26523a4836b9bcfebaad108c7ae396d5cc109 c5784e56cdc45180f7746c063ed24506c8c51409daecf9331900648e65f6e9d1 e194195b8a42e23a7c094278f16af0917ff40c0f0ca24dc49d8016064419580b56d22814c1e22858cafeb9ce138e3de54d2932eaa321f7082e9dec3fabbaac09 true +check_signature 54401c1e6325f2fb2f89d160200449b78e279daa7ca9d68bcce0939d69996aec 7b01173f12ba49e3bbfb18374aa6f96f339ddc08e60a4c63ff9c3a29410af3d3 61a4990dfa544b401869f631a188a260ddf1dbe380a2da7883001927339de507cd78d2c0d8fdfb622b07a959ac7b76219418fd0ee90d0f2ce4edebb9998d7d05 false +check_signature 8e34f5366215e4b57ddca7a1b8fe2f35e7b311d982374fde019df426411f473f cde1c9e80051fb81c8e3ea3461185a538186409bb9b94c444758cb8957aa7bec 4736449407d24655f740299708fb24b6afeb32ee080651d97cefb786f5c912d38a2dc60732990e17cc5cb45ab35bb881bcdfc2e9d1eceef2e5b71654dffcad09 false +check_signature 53f5504130637d286a23536bc36e0883a8a566dd2e5816c38192ca474aadaae7 c9e40000352714b571b2bac9ebc8cfcbb87d6496e9e3adc9f28ff7b5e249ea37 38e8be45477718e65c0c4dfd2789ffc3afb79dff2f315206f3c86f80aeea592ecdc1dc131977439a60323135bf4f8e9fac1e5682aa64d6dc0db44c2046738c6a false +check_signature 584182d1fdbbd4edfef3f800e53590af9ba4f6ba256da163be28d206a29fb091 3eb8d3cf4db01e74f78130693d81901fccc8eb770430069728361b33e1ab05e9 efde8bc91a03727ab253a59708fd903ec6194e831faafa391a56d598fd65670a95fe25cb31f4d3939221bbf32f2a3241e4499f4fb7ed9c40b0c2978f393bf905 true +check_signature 40996ff6f46fd43f66255130dcf3a944907015d6e1b24c0a639c4925787b4023 bfbd81dd0859818c84d2bca2e8767b20feee1b1aa8727f561f56bc345ef6b6f1 2e51b42774d327c236dfdf564764d6257847e853f166ae69c646dc8112dc0903d88cb1a488d944403784e4526689e52754f1d8a335512058b971b3ab6a674b1f false +check_signature 04b81e2d8fb1be7d2f3e5cbebc2efdfcff9cdd818006715dcff29b10a9942791 1b6ff934a237ac30e2f1ed9040844ddc31d0e5979685cf9d2d79716c19458934 cc923e277b0018f8215120cbee1ed086e5abb8f1bfadcbf867b3e3933aeb22f6702258a3f2655aa7c778821df938d045617db27087d41a5a9a48c05078982a8f false +check_signature 6a20dbfc21a63a590a36ea4bfaa968c2e18987270c2da37a0e6d97a33213e3c4 44774e4355043ff94e535515fcd31269d5abd5f9d654fa666d58b2fe0f94dbff 5e4b4716c533f979b038ee8a0c81564fb534e100b43781b1957a2858ee94670a289c739d125753bebe7e214bafc1cadee461a42b15591d42810f0d00e5977803 false +check_signature 7c720c9cee61e08180e11efceb6f38ab532ef9df938ab6afa7cc5ac9d9a05294 adf8d5eaaf60663b8b7bde64404fff780a31d4facfe852f3225391454fe3233b 6fb6066ba480d6b2670437bb4c24d2c41e9d09e5cab948e30c5a7b1815d6998ce57031734c7df4caa1e2a806da5f221f73ceed121b58d2cd17b3e08f7f0db70e false +check_signature 0de9668be5a410ee41e06ecfa8e15a3ebe23963dae1fcc344de5614335864385 61de29969de9f1ac80ed5b6a140055b644b1a21063c5bcbfc482f3508cd48989 06b1fc9409fb1772e691ea2740789526641229634ca2da6e308f71b68314031db834be0c2afe4665cfe095d88725742800dcd7aeb90e7525817e36a3d1ef8c04 false +check_signature 45de4e7ae19bf40a9eeb5672a22e59b2f2a1ed5cc2d6bfb6c447e4f83e13243f c2bf3ecdcc91fe7f7753fe90c2e3affa119acf0398717b364b058c598fca524b 36fad9eec775996c278a49ed73dacd6c5d0586d9af180a668eb5251543414b0591c270435e44c00122eee8856f618f4d5dbaf8060077e5bbb99b1f4a56616519 false +check_signature 88e84a6d756ea6c60fa4fb1db05b6e3c18d2710c597e5cbcb25399174bc60886 006f35df9061e48d71e0acdd816eb9758b726093abd4e2657d7bf4861b0c292d 3cabf456a156e506360b10b283e3b3cfa405345a05bf7dcd82174010f3c88a07a5e9bb3aad9dbe2ee7d797fa543ad2c8566272fd5f1562089eec318f90b91300 false +check_signature ebbc11f42bf76a81cc3c3ecde972b711a59bb6a4f8fdb229896f80c437f5875e 44c2321428ef287acfabcf6d7fdfd9674ef0aba3694c04757b3997ec65992450 c007a91e02ccaee30b1297637878e3673694938ea1e38fe63f23ee4138bfeb68a3e62b46a2bed84c2ba6925962b9d543556e90f56197ed239a8aaa69f33bf577 false +check_signature 4c3089e6ca67152e9324db73ccdf0977e3a0b4ca7ea39032d92fb66a54ed1c3d bcf8aae5fedc042fd3b628bbaa8368d41760ac4617faf4b8b07ac670cc12c8e8 9efa1e46cc45a185e3033695dbb2349be790c5b84900c12b19779293f6091e8bbe567da7c3094e161b725e4ccfc22cc7c026bf8b8b3a0421dfd81718cbc31adb false +check_signature 311667149ae5c06bdc36d2dcd19023650fbaeed9b934f5adf0bf3b4b898fd205 b6763839ea4bb0e4ceb9f7bce45acbe7642a154b82428df7372ffe0d78be2348 48fd72e33435bfca5cb4b39a66694d956c9029e144a5ae829e3556f01416c734ddd17a5926faf320d07ee793af5bec31b5854c14ab59d2b99db3340a36b29e0b false +check_signature 86e96a1b247282b9d28ef15ac949e6c26dea389fc06376be8db263f57452792d 7575032e7d436ed17c8f0f93e268df2fa345fb1d7848ee1e75a16e8704c7527d eb4a75803ea17b1e84ead3249cf904a76ef6441b7bcfa999ba077e90c31e31b6ce4afe5ae2520276988ef0cae34e99b5ea9ef1b29dc8d34f719fb5b1f4230c03 false +check_signature 828b409b0a08087783a5144e4cf1b0b4616eec6c5651e5122b0709c2fa2a324c 266e49ae7ba422d708da2773700da6c2ea84d21d9b6301229244d8a01f650db4 5f9f44d02dcaee7540a564bd0e46a6b28725a97ff9468e3d0d5a246ab27765496df3db8713aede674cda31bb61fcf329639887d047602b6e079cba546bb9a1e1 false +check_signature a4a8627902a5b64f331b9592ff2960eb707d409e965e259001e09424846f80bd 33c50c64090bc51e9f26c27d5771c8b8ee38f633b969add4a21b9cf1a3220919 fe1f31ebe8e3fa50c42f9bb6d7fa46bc8d203fb248d8ad9f23deb203041a84cf90d385d2c19ceb33144ef7522c8953f74005824fea8b6516cc41e4c6972ee61e false +check_signature b9899df74ac852df67e191c9bc02daf26d916605eed67249cdef7dc012775289 98611eba6e6bbfaf4688a3aa342bbf01c61e8430c51a8082e819e2b2edc928bf 263b82c08b92fbabf2eb7177b384434da28eed47d178947b578be7dd8a60448751a035773a3a9353c48669f553be631813968b428cd9d0962cf3bf8d89b4360c false +check_signature 812c29395984ba009b688c1da5e361a624ccc455618edf3a17cdb47258cd4483 658974b20e7f65cfccb1e13736d0f448ef81f952c8e29dd65447a2119b0d52eb c3c61a10d192fef98ea6d585423295707ecf1405ee6f6891864ef9c9d013060967f01bcea09986d9c7e6635af07dde3e3b08628ecce4b3a0b39c1d98f138d30b false +check_signature 797a108703928bfe1369df106cab0c394b1b37afe6cf53eac14be81897b931d8 42ab196d06d7ff04c5f7bd9539378eedd5ba06f2cb1443d7c61a9662901f77b5 c38e8f0f8bdf3c041a6473ec592d0f211126c25d991a42be0a6aed67bea449018dfad447496df80d50d6f5697f2e89d11b77f3053b1244585f4f52c97a952f4b false +check_signature 3bd29f3af1e94be1d7c16770f189252b7821f7f66b4798fe49b5c2c73b908623 0ef3ccb008cc8637de969bf8e041a93f975a406d8cdea6967bb3ccb01fc01c03 473160ecdf793caeb04d02e19ea92e002157ce62b9c0c66063735d7620602b093dba9e47a821bba62dbcb112dfef79aa5461ba81a82bd51f427772b842b7c508 true +check_signature 5124032330cf46ec8a4f188c07b52d905a97a5620c21bb1d8850075125fc1d6f 6d97e8111394213c1002b6313a31548127f54ba557447c211f54c7d0b9cf3d15 f9b54abbd8dc7fa47960668d33d8f42eee271fa4829159a80c31c4f4c792280eba7d8b462bdc0d884c01b89c559fdf7b2109974e9855967aae3bb33e522e9705 false +check_signature 31c155e6c58a07272418e2a62f70bc1a581b1164d003423d8618bd1bfaf042a2 eb4b016719fc68d5162055e65d11f96f48ab2599aa498016ec2b082ad97057b0 e9a669c6fc57883701811328744a634f82a5cadd0cb3729ab7edb2a18d9f3c5a62aa2a27064e3bdaea5b7e16e39a7bd4efa5051f620a15eb189723518ce99d01 false +check_signature 80de917c6bb622c787e95adbafddba5374e90bb9bbef56f0d2cc7fd31ab5c5a1 9d256eb3457ab86e6ed5b37a73ea221675f72463a916970f9561ab9d00f1354a d63e385902bf25dc0047dad3996ac0d60255b9db997871a67cbf802a61f41f093cd710c6e6604b0cd7ef7e9485d62fc1fe024b72a7e19ce3ce257f7cbb409f01 true +check_signature 2a2566d3edafdde2c7820a9403b7c9b326787e0befd197896b1af1b35f9740f4 bab66bc72e6dd856f63b27fab60af839de560a972dc3d0a2484db884bd48bd69 4f8eecddcb142cb8ad4de31238a6329052c64bba675937ab1ce4a8a9b585b60569f94a2a0a375c1a1b507189e35f310e36ecc5fabfe8e57ff9d58a298161a393 false +check_signature decec68ea3505fc2dd5299f73a613b6cde6d85e8e94eba9248477d30200ba198 8f1553b1b91372c976a6b9164df806db6aae583e1fce551490b40ed627dd74e4 6d78fd299ae9a3c336c3391778ba3eed748d440ca6a820d40953adb4124f10009840ceb091d10be243822176d5f773fa020c1a5478efee0d5fa343e1abe5aa01 false +check_signature 54407e367056e0efbc76a08434f69fa8c41c5a5028d15a80aac28b1f13cae842 44fba0c763722d4621894f73d53cfcccaaef6263ec96ff4335a695ae22dd3818 62ef88f245e4acd417808cae2f2c5060182f003362639dcccfb6ebf08b15537706c8efc48def718414880fe52fca22f0cadc7932992fd2e224c1b6a68328e50b false +check_signature 357610884e1d452de4b2a1e4b29fa9f0829639e65da8bfbd0ad4a8c51d715f46 a51fad1dd6813aef0c293b3ae6eae00d64b3b3581ac41c46cde7f57a6adf097b fe7fb2763bc56a9294417ef1d9b437f0a0f962db38ed4d041c0117fadcfac90df471f4a17e3702b334334a9643c160e1b5d60d70d3c97f16230df83e03af150d false +check_signature ec15483e9c07e902184fbca55c490df2ec6fdc3f8b022b551c380119145fdaf9 f20f4b020f430195d557d8997918abf1b05fa3bf0e7addcc2d305ffe88616272 404a4b3a0f81e3c3628f0a78c2800c483377acdd63e3cb498d4bc15602d7760a6a4865698ac82f16ad0df607dd24b3bbb048bc37a6a15ed6b561adbe8574a900 true +check_signature 1f589df8283a6b8dae4983cd5da19b22434396b554625f1a4235761eac150ed5 b3d72d52331f6477a38e66be538dc3b3e44393353a32fff42f22e751923b2d61 cdabae8ee449835a3064ecc86b2c3214a9b9d67916201baf055472bbf2f1b50d89a316d4b697ee5e183b0cf7508eeab016bc6ab903734d40b78b160420b86300 false +check_signature 89702e26e7d22e42dcf251f427b21cce98c9fffa13dca7cfe02cad9b08fe48e9 7ebbda14c836676544dd6f3f5f34545147f730ff11b57bd7bf537ecc00c169e4 ac63f230347054911efd6e28ae280479ba25dabf143b32bae9b694e372053e02cd8a0014d5c1405861871155ad777b3efe82ff9e451e12da6c606e2b4790480b true +check_signature 9f644146b5e64e212761725d5283a0228720d0411031883330a4710d80bb10ed e42a3733d2d57e1ec460a44f9496527e056a8a240d2d8fa90753846a2512476d fc83be6ea82e532502f05ab79ae916379dbba8872e1e355c423b96c5cfe2105664895785f0f064c81d98cc8506802b1859c718521fec0e82446c7ef8fcdbfd03 false +check_signature 2156c8857eab4ca2916471516976732ba8eac350d6465a9d0aeef752823cfdb2 841b89b3a24bb3bc16f058b54d9737b4d9e293445fb6d272331659c2b9db2dce 614b99a5d45928cdbfaa8da601cdc221a56b45606046b82d6107b1a66bff61005bff8ff8f4bdbae606ede280383309e7bf6c92b2cabf8e325c0cb93972357089 false +check_signature 092f1cc8057d8b7ba21769852d99d77a524ecbc6c385cd2fc75f0044f52e3db7 29f403ec1d71a8e5ab8ccbd613b40b4daa030eca4ecabb79cbbabcb62c47014a 0a6842046c60cc5bb1d8f1a616651fe8aedc15f39aba2f8a6078387641ed24010fb6acb37f47eab9ac597ecbdeb31247bbcda6ee2b5ac054e11cb0f73f76661c false +check_signature bf739ed54ef3802aa75adff371d17fa335021c0d3bad2714059263a245a80452 23f13946ccf2f92dce370724345cf080b102dbc8c9fea8c274fdc47cb8e1a181 a180f1a8edb17cb3b66d0651e8c188fa1227e35fe53c15f90d31328d23ea7d0c980fb42b867f44df96627dfaa33a870a940747f2f3c5b8071790ff85d64f080f false +check_signature e946424fd5609cbef1244644dc6d9dd54ef0f2c9a3e667bd67a2e45c1e249888 9d92bbb85ee37ca6439792d27f9fa68698f965fb49ddb2c52ed049921f8ed617 45bf14422725e82d5650cb679c07f4859add654e8edd50dd5899bdb45b40cb0b8180e3b0873098a984353b849c32b48ca194445c572e9e6bbe2f81b954280f47 false +check_signature 28a770a01d4c96c96bcd7b0450f3f8921c08607a6a39cfc8f0a76cb18ffa71da 4db74adf61874f3c90a8198b10f49986a81f881e855b6c85c3bcb46d513e0799 78a01e16ae4864b18852b89d26a71ba10e26598b4ab7327fd9a9a3e102e0053bfcd381c92e03fd1a7ecc44bdac0b635ca2899d0fd2911dc3bb921c84c01cbc04 false +check_signature 219c8d25c499f12453b017fc648a1134fbc31c2213a8fbdde464bfdc573975e3 7f21dafbd0cbd219bca62c9f77059ac3a8f6e6f26c4548fa5347a035769b2270 79db4aa5eb87c290d7737a8364c6d5744de692ebe63ca212e8bc05f748727209488864da7152452b56e877d4211231cda3a18df02532c0ccd6c20cb662270709 true +check_signature 9a28c6a1767da43628a61c29a0332e888206fb94f8265b309376dbaa319c43c0 43f4e3662451ab591b88352592f1105a66e11f7b21a9c507f5a991bbd70e977b 4c5efc8b2b7534ff1036c764406ff2c99984e0ee6404cc5b8bd4dc1b944232000bb7493a5b805c3c759be88e236788e8b6e6d1964fafe2fbdea9385433746d0d false +check_signature 882f4ca24a183d2ad6b1da11a2afd70fc1a85051f34d30fa95e69da6be3d7ad6 ffeb615687de08cccf26b0d7e92089a40fbe573090d60f12d5ec3e7e6671c5af c7ea4e1334467a042c562a80a539bbba12b56bfecd400cb8325cdcee73863bdfdad99595cbaa0e01a0a827d66de095204fac501a719a6c5e231aea1d0265ecd2 false +check_signature e2c29da7a45409c7896def018f5b2369e088579630276136a060ff1e0e427316 11d74c922ebfac6e8ebb9802a44b75ccaa70765de2ed3a854052349a3bfd22db c258bfb7685799066f00a417fde5c55a8d408cd6dabe9e914006118d6d078100440575ead00bae9fecb3caa54fd4cacaf1844acf233c8952d803094ecc6e6b01 false +check_signature 65d8d714f7ac38caa38a16324c562a871a44b00fc5482e42ec67a580bfd9167b 9d007fa66569e8caf6a13047ed2732a0f968064eec7caa77d6799f7dad0e0b80 7d455480fb3f72347a03a3681edd6241dac031dc498c62ac359dec9394281e0e32f054d9bf6a6818911acb90cfb7698625d219fd580ad2d336504ca46b5778d4 false +check_signature fcabf9c0b341275971748d4bbef97c6c02b89647864bff88e490d4f2f9e08a81 bcabade90a2bd1967837a070b874752b53bf24512edab5c6e69dce06c226e28c 51c22937a8716b0eb61566113b18e380da857cfb08713d3b024b8dc651e83d0fb17c501239c74f390ab8979abc959a1c46b7333b743bbdec1489f60ae1544d0a true +check_signature eda0f5a8fd5ab0531d99727ea44b24fcc0fc2512e800bef879ced4c306dd82c1 b5291843ad22ff56ecd9ee62139e8325cb80ccdf8fad7b37c691b1b106f206ea 5b3f1620216d30a2062cb2ce90dd6110cc6f2633657ba81b4c1d31fdd44a49015bd311393391cac542b6cbe82c603ae5ef4282b92cc07fd3fca3208f1b3f240b false +check_signature 9bb2b50e05290db99b921b27de91475f017913390c838913ededca26f9d37e01 aa03111403437be129bef068f065765c53dea5f5e7443a138bdb06b10b07c1ca 8d60fffdb18842be7eca7a816eb956587be953a261dfe6c15985a5fb7397bfeabc6cfe537d974dab641fa626783161fa8dc9b376c34880f0255768a405401703 false +check_signature 61df0c9137cc5e798a88513528aacf49517d4e6ffd781b2562a4ab3843898dd8 07950efe8a02b581cf560a8beebe20ba5b80a197633a9f9c16fca9cc10728a7b bd7a8927147c1d0283a43d6120398ef52b380047e0cf1a6dff6ea4ab2002520c5697342a6a1bba08a73b4ce46ccccdbdca24f3e46a65a661bb02f5c595b4eb5b false +check_signature cd79af2cfb815e0c3b4c37f73a898d78aaa9ce2ea9c44930ad06301952d5a3a7 93a305682391268025af4f752b4256cac29842e8b3870c87e8df1270a2824ac8 89cebc36b7197266a1dbda1138aad8796f253a5f588b7a66ae564f560dfebdcc5383675ae886aa04e9c94fc58388c4e7a57b20c97d70fef5ab49af8a937f0302 false +check_signature a3e56a9b20fa9178a79466141411ab119b66070157be17073f2720481771107d 1dd324808e98215e6e26641393b414e4797b3c39fdda1e0a65c992f12fb66bea 1b4fa6200065ca3c7aa9582fe1e4c203699cffb838ea1c1fbe42c1acd1c553038986ef8a78c28334c57649d04cacda0516105340accf2cd50b54ff0222c25c32 false +check_signature 8ec1ed3de5f879507c855124273775e22247402e8a483cd392f1c379ae30745b 58686568a4d49c0bc55725e0358bcf6734783ff45c43e9f97367018eccbffe0d 353ad0ffd0663e887481a6ef709b9ff0ecb01cb0831fb2be9c2b5380ba29080ca00469ee16fff4513f1d1fc00fe010b714c2dba47759968687ad95758d0d8708 false +check_signature da8a206410b29bb577acf4056b92a90c456602f874bf04ee3fd6ab2523b61d06 fc7beb09a875729b69ef2e206241b9c3a03f1e83840136479c9d211a615ba01c 5223af518a5ef93968fbcab622c36712f6141f5715a4ab1d585040fb6b11c7026cfc7785c060cada5f060e3e1f3ec052566e94f5af8930972487b6a2f4ec9005 false +check_signature 9d74784a17708be8fe2bff8512f7d0630342d22f676d395854ed533dea769246 ecb6a904c2e4cb0ba0d37541dcafd1b969a526c8531a2e719ebc1bfed9997e57 42b0d2539ee50aa4e8fcc8d223893086d18129767f7394023de22fe7585d1901061807d0527e8e1fe3a5f2933fd514b1a6397caa0c26e3de8f4a121839b5ecd1 false +check_signature a01d3767d7379e9c858a3c9d780c9a2a2ee13e1037e288706fa18c835cccabac e4a9c4f8ac45cd98004953a5a043f3ea3845d9737da1af9f1f3433ed4c0bc569 9584bb564548c9b719e3679c756ed04f764a546a830da86a34606294f167b601bf06eaeed883a0bd4d2bc29417906a092047a6b9b60b6ae4eea09799cb34eb0c true +check_signature 36985c9604417bc8c50f688114c763d059798207d4565a4d561b1192b9a962b5 ff7916665316cd72c9aa812b2c24364d47c98f60475f014f91c2ea71ffa8f86b 22127f92e1c706ca01df36e6ae0a9b0719c481292641428ccd3d0e978ff847dadbdb023f3b5477781feb9a17f48d790b5d31e52ebd9cd1a07006d5bd92ec0f88 false +check_signature 8edd913f8c0d41aaa5ae25cb2093f3f3fa6488d46e2de3f8579af7d232f704cc ea6c5419ea1ab2531aaf67941544e4c425239a233ef597b5a9dca151b0493a05 4f422a844b7c298d47d730452fbafbd6540de576a3a0d28fbc8d5f340271aa0f4f028c41c131e085909bf902b7cc6f1f3d911231a6fc4bdb39e23e6d85b78a0d true +check_signature 376d433f04e1d7452a6cabe6f35b010cd5b9b1e3de63f27475eb69178ee24cdd 90f5c0355eedd2410e4640423db0bead5f7fba7425f485955c376973c15775f8 fedd296a7b2d37589f136314cbd0506f2e0db55ad13cbf6685dc25c9b217400f7b87de2ba1a811e1f8955d1f31125b69dbe48406970e25f1fce06fd711a78103 false +check_signature af95eeeec4076234828731e8f99b68d9c1e065515fe2cda585cdf87be3af29c7 1414291af733129212a4347ade7f744ddc8b481fe40049e6a3324a38b436a056 69da22a30a6533bd7089fc60f6cc046c636a507bccfac76f49710acf82f75a042b59f1bc07ce8a7ec118d4d6662a4cad0ae2baa4c1e613dc94d21fd0d31b8eef false +check_signature 4a4a24a3512969117d855a2a000ed0ea1baf61b79284ede1b0eba3e385fe2661 05931c3be0bddae56f55ef656a90cef1c1ff634dae590a8d4049648cf600a337 43817c0cc42952f1c54992745692dfe6146fa9d384f6a84f427cab340c429f03bb918881c23a18b44f8e8fc2eb499cc809fe33a42e377c09977925d61b31f008 false +check_signature d1902aceaa1a64b13bf229eaf32de6afd6c6b81aab94088ac351697c52fff516 9d7dc5470b8b0d19fafb4df2470f30e4afa0a818b17fefe41bd494409d594424 c8754f18a9421096546153848d7692bff7042fea6a46c669b0d9ae774c9a01e6ebac7683a9f6a4ee66073a2f430254104b6529083145a8d80ab9d8587addb500 false +check_signature abaf1e69fefe2a8388508b55d3ceb6f11673ae97330dec0cbc10ccba1bc5b3c6 0c18f0d80d5f0ae507a1d7771b930fbe681af79e32fd5343a9c1ac1c3f799177 b850f4aee6aa1c9aca71915f9cfebe1c3fdc887455166cf3f3cbd3c004a2ef307fbbc7e516d57a0e6f4140dbdbde0900fa5b141a4458e3bd00e24a256fe5ce08 false +check_signature 5b29de2735aab9933c5af2f14ba519ab5183a524d4090859ba6bf4babf30ec0f ac5361974cfd11e7faa9a4cccb4eb8bcfec94e0fc96402308c3ea9cb3a1b3b9b 7ee77fe4b6b72986a112759ba22471ceb06c4535d01dc1abcac82cbceb5b1b040c9c6da509f7086213c1b1e6b5d38e78523395785c60c8f95e6f0739fddf6c06 true +check_signature 71b00b6aeb8d0ad134ef1cd4f23605340bb2bf6e6c7e87e5b79d7971c4fcac1f 8d2564234438d216d7e409929ce514cff5ccf70d09f3191b0e02c1c03ba94d72 53c45c8e02674654f8b057ee609ec62a374153eea9aba3ad9267bdfca555a907429ca152bb395993c31899028f397e2e295905387ea64bec693466206b49b007 true +check_signature efd7f147cc3a0c156ac0de062c27d991ce1b18ba62ab8f4ba4f1b634b47a2b44 63c1aa968d0f2ea34f85265d7c0f29b8240e620e9f4bcaa1bbed7ff77462aa36 14548c49180e690e872f0e5f3e4466f43843f4ab888afaf174cba5c4f1480bffd197700f5660db137723e7da50d8dc74accf0084f9d5d5da3f60ec72d9f91498 false +check_signature 6480234a3bf6106eff805c89b3435b50e716cc8d9b687f7b5c9ac27331764a87 6ff44626dd98fd1e638bdb1132aedc4d7d1ac64815c9333cb4f8a56f42c60ccc b576a8d9ba5c39dd9fe6d882f0243b85c2d75ec8c9d1e807d10bb1ba077745a30567b790edee3b1a00c365978c334adfdd2e5d536b93dcc03238a7906da31b3d false +check_signature 01cf63333380655c425e571dee0bc85a47095289e6c25b4a5b83197f395c38ab 98da558e3449a56fdc1d44d1d204831f4476e74bd42ee6ad7eb0fcfcfa9d2948 5be3b917a458633108b056dd885676489e4a88bf7284da5b8b005c45ac92c13f861e97b88a7d5bfc25c6f06a7e03ecf8700af6edb544f24a47f68c17be137d05 false +check_signature 4fbd4283f855359efc60118907353015018b085ef6c660c2480ae8a9d4704714 8cd0cb592bc8984682fcd67daa18810a04ba396e640322e43611d128279148d6 9b3572ed813771dd5bfd0c28ba065f891aaf005086942963fa0b480f4c39360bd46d79ee7c360995736590622db2bcde04275476a8de7246f9501f035b4be70b true +check_signature 00cc24005134a08a7146ac18c6a24531a699d7dc76aa29e6204eea5e9df7d33b 54beb7bc656f494cfbe6b727bbd00667118ef2e08f19c4d33016f4bf01021420 a9d623819e62fa5ee8cf958481d924ac48f0163e5df652842ee0a71009f5feb9ef5fcb076846ee3d2b0ff5bce9e29cd540ce971540c820058318f61150932f0e false +check_signature 722ccbd00865ac9477d15b4a4abae674af0976b5df3c51c61948599593ffa828 015ca25e88fb25575dc7c8984447fce7308e2875a5c4108a931b565dd60a17c9 78bbc031618fa51581d2dde0c0d81d5c1fb5ae80886bd3d426cc69eecd291f0752f4153e2a7c456785763d831cc8d181aec211c2f4a7de15e108711288572706 false +check_signature dc0d01c003599d10f1de458383855f6a8d0446204ae108e082a91e455e31f6a2 d827a703f0a2524659053f8325e03d8014ec8b179f8aabcebb5804b9ebd96239 9a48f82c902ae6c0cd87b6c10b945a14eb2e0a61f0a34c322eaadc3dd337852411da5491fff046284c923c307760500d6dd1bf464bcdeaf895064c7e6ef13f6d false +check_signature ad32b7fa2174ffba297dbfa15c30fee9d8a89b1ddaac77008d508c2251ebf436 e3f6c132ffa26ef5f5b657b31ebef157102545106a9477f0182a9d9e16fbe1b9 74cc83c80405f9b8c4247ecec8ae17c69cdb6e313b8dff3b598125a581efce73050dc0c088a7e484de436ff9eb612d24ae255b2c2674dcd3cb6119b567fdc201 false +check_signature 1b4dc80bff1a70234b5b25fea592e56f4710f3d276e6b3e1336163097bdc7095 dd0267a42cf590421aa2a580f556d7be658388ed234c7be0b4df51f2836032be 2c02108f623f5b91a51b67817c9029eb6427876105037a35d76b4e9aa8a6de054a965ac2ccb97ae6a1c145b9b8b7bbdb93805fa7580ce448464c0169b02ae401 true +check_signature 22327d7309e9c7655dcd2464bac54f7587c723bace6c69bdb5e8ea0533f21f30 fa29d7f62df04be79bb87577e8043d017dd79160189548547bc49280cbf26cac 452b7f085976346ce5968b200ede96bf14b952fc84bbf02d3c41acac4f75390f77004342dbd5ce0e644d35c34a0476f3c13c22d7b26a6279aff1005211734c05 false +check_signature 05ef07ae2eef7f6d0415f389b56b41ec113e5292b5929e498de1022be9138b32 08a34f2ae1d2e94725a472f24aa524a280c5ee2bc256a7c1cffc3fce5b59600f db8e08f0174f2fcb752188005be00025a471ad0e5269285cab171bde3e66ed082780c6a6983d5feb5ac1048a88ae91c7a56ce59c4f6d301fc77334c30912210a false +check_signature 7c99a7479c02318b890dce6107a13af51eb1214b5044d0ef3971d95559b4eda3 13a1d826d9c4c5774365a0fca8ff3bf8775d4317f1807fea1dd37da1b10a6257 0f1436b8d8efc571fade3a2b52c442d1b8d70503188316b6337b387a766b3004eb9327fcd6a8eb842bcf23368add0b2b164bea01ffe54b296d324e124ff992cd false +check_signature f66fa100dfb6e5f59bf584e771167a115639c8c6e6c598ffc332af53313dbd04 bf79290b19bdd8157a0a35dcea9089cb0ee04ad611dba5cac9c54ad551454065 29d8fbd2c63e079efa05e71cddae20b09a7f88c625e3f32e24c548a4ca11a70a44cd30a0eca8aee4d78f91955ea6607026c82f301fa4363160154408f3dd8d28 false +check_signature 45cce47f675aac7ad172614ced968be7c2aa23aa039b8ed63517096576ec8c87 f81372882e5d30d9edb1c62842d235871703be9be41bae630671d40845287e6e 5a8729063a844dc92ebf3b0f3f57816f7082d4d39b7c9beada22e5f95ab68a6fcf8f0d4aacadc545aa8da2a38c0dbf42de543cc223156c723b602511deb10b0a false +check_signature 3d510515778cfeb1a87d08e7c8054489cfa99cd7302ed1494efdc2213dbbffbe f1168a0b908e4ca1d8f9110c26489df32352b21ea95306bb122b78c39cfe4513 1e85b2a8179dd08ac91ec6ed6a336900936e40cc6d7e3aa603e1e771cbfc09007cf9d820558db216cc0492a3f14ba367ea609cec00a669c6e2f844548bffe109 true +check_signature 45be1b659af491ecc9faf34cf708f76c74c024a99daad038d5ac3b0e7030e6f0 23a5413fbe72e1bfb4e7fb6111e586dc8e44684bec58d2031271eb65c47712be 981fc816b76f3bfef712d391caafd0605e7f8f15bbfccce28132e2ee41e3de0fda6620f4a6175f56cd3659061baaeb91b877a4e3f7a42a0bcae4f27f7961fa06 true +check_signature b44f07dd4797a672a180efca0942f6c5df9493a576e19451c68a2824c8f11b77 d8680dab2a8bae1b943df0927fe431c7da652504ee42913dcf4fb355570bc0db 6d899e1bd3672e4d547ce703bdfdb5a499da0ec2465bdcc70f5525db12c414cc1b4c43223ca541f757901be5a7222bce7a79404bf9608b26dd16a261c50c190f false +check_signature 13c676fb4d153a4e685567668d732a6ec4f4de95c58d8834e69ed86bd3aa5a20 3e4e50c284ed939b6f19a1ced6b5d63f35576af2e61dd97c27acecc9c5c87bf1 1ad1e68e8976af7bd347e8db009b4784d51d6df21d6c877cec306de15b2c9605c45a4c8c39c868ffbde735e831279dbe36d49563654b78ab201152f73f351109 false +check_signature 490a73142b1ed3f3ad68a0216be076b71c6c68a113458d4a658f3d288b7ffa71 397ff03b6331e464500b756d2129732e68c3776f2006ae0ebc1d4ebfca3047fd 920b2d130098d27fc40c2ba50a9a9172079d0e91ab49771c6684937e14c32b6e65605c515a1a707ae7f38ad9e686b46a73aa9ad8ddc67290c6ac65a55f8c4408 false +check_signature 86a4954f6339cf2e777354ff33d7b1137b28759283642bedfad63c1df2e0a56e 88c9bcaf48e99a584add9416fda8ea1f3d2d047e286e4e3be16ac19171110490 afc7f67a2c2304c40d4e98426d8f4a3e96aebd2aa1e3f2dac5a55bf3114801ece26dfea013fb327a0d2120a98297466eb8d077276818d9ebead21ca971bc77c9 false +check_signature 68e96a1a5e2b66ac4be973f67c482fb3d1b9756e17e1d9a72658bd043bda1f42 5ead69105c3e359085f2949215d437b0a77a7894cefe0ce1f5730e757abfe8f9 afcfa79e7fb1825163de0ca3811533916b09b91f2aaf540c5ee88d44d1c4088749d9b328319b18d7855f5c995949fe1d2adbd13cfbeffdc00490703e2af9347b false +check_signature b43f08a1b5419cce0a8406af8f9feb53efeeaf8cee8bdc35079f0af6dee0be0c ee688473c6113dcf069410a1841b5a308407745fd6c2ba85d2f977253572fb59 05ee52b15be934e810553eea78ba58e21fd834c11d8691e3405afd1efad15e0624f87276a03fe48702be34f031ac9be1f372af8984e53b682754db0244349394 false +check_signature 7e3c4303193a9b7dac718d24050716174c9571df2b5e8abdc4abd33cf249b2de cb2e24ad11686e46bb55b4473e7c7b710f79de120b2fca564f4cf731d797a1c2 4acfaa1998611e6b91d14ee92a2d02db3423bcd9087fbf5e8b6772e47698bd0857263fbc213d68c6a08036f058a5d9258d864d47b7e62d61f43a51906ca941d7 false +check_signature 5e5fc2ea081a088fd6b58038f0dca267f5af6d63071d0e37674f606af0529721 a054a8dd9ed21a67a3e3d3222cfb22d9b021898c28e1849896542e0ec028d474 5ef003dcc7dd5d3b4d99db3621db71851a8b98e53b2351ab6ac154f0c6294208323e1ff4f3721d1944c127eac7468f41c9e9925d90740bd24885dde163df9f0e false +check_signature 86a586d2afbd8d02c5a07733a3dcb5c19a35bd896849e821f58d51005c901369 c396e74239b57ba22eef764a3f013b09059b4e71e238b52b15aab2a9a182b9f1 1d5e94dfee5c387bc5d337e5fc22684eb38608c553e5f0b83632138936b2e80a9dca5627023615804345cee94a8d074ab594c74f7adffeda00164b7d1c04d002 false +check_signature 42de9435e29bd3b56d3358cc78e120544ffd3c49671f2a3bd8a7295b6be965a4 07ad48ff1be6079bf6a3a848a36086e1d4c57b39d69ac6322b909b236555ea3e 961134ba0736de6a7bcccbe7cbdf29165ecd6cdd514db63374860ed9fd8d940ed3590a6a0c3f823f507547cc786c879a2a579572c4624a53d14860931694a000 false +check_signature 6fa264b312897bd0dc44eadb44f8eab75881ffdce8da861569f366a45e2ed066 432a2dd2ed99af956bdea9bb69577f0d9797b345a067ab2e44077a2ce2b27ae5 5c469899181700ef9da975e31fb4fd6427a478f2ac1115af2992212f74892e072756603916fe45dba817b7c85db581235387bf4a9fd53aa37db926079eeeba8a false +check_signature d4cee1ac55711ccaa28e3d307111c6c3ef09567eab5aa94904ab664c081f2cf9 75fe0dd997d9c85b4bd4701d60f1b3cc83fe6dd9f560d4739ae6d9130fb155f9 6cd2039200656276d5e40512bbb99c07a20274c71de29aee66ed783a07b26d0035e895b206fab6babde40e2a7517b25350117cc9f3b6c8888b45c095afec7502 false +check_signature a4370ca5854b70e15cb8845094bf7affd7c8e54643fb4f2b72dfc936e39837ea 9e5d2b9f169c4484593f7ab16988ffcf3e4473c40ec2de0179aa999a3777d039 0b884a049762b96a0bbc23c7d4ccd7185a4c3ad56ecb3bd6ce389bd00d70e90376996f8c2492d007c2bca11dd7b69e61e6e478f08a94bfc6e8cacf23fb0c2f0a true +check_signature 5765f497c4f9da74b73dfee101154de8ac8290369153b761e1ba70e77eaa37d5 58e77156c193d09eb70c864b8e48208e6eb66819f49dede3e46c4eefc87e0543 4b53ed5f4919df9febeca09692a064d705b24d1b8f80ac52bd67c338077e19d720eb2e9fac7fb56b63c2abc68a1baf43fc211dbf9137826fa3c47804dca92f09 false +check_signature a1af9ac5e5f8c01f4ee6693b2a23cb838a4cb4ed9667a234a1c22fe38cfd991e fab94d1a6126ff6bffbc1af1082d28be6f489dfb0e086c22a0ec97bc3ee86b6d 49140e856892423ec83e1fa4c0b14a5b2abcc33148d64715e55f0686931c490465920f2fa3040dc8477b1814c9542eec5a2a47e6c8bebe8b54f018c56debbc03 false +check_signature 1e2665a51d4d750bac77bdc1dac4d92fb609006e53984d12c3e9bd736dfb5a8b fcce505aed08f419725ca0aa054d051d319141fa8acce36bcfcf0c65f633d3e9 a39b01d3fb9016e5e9967e5890f8996a82dfb84e2e5dd3a61ae8654d6cc09051a57d84560b7fd3ed16025bef497325d568d7222797f7c86db0d45d8c95dfc601 false +check_signature f2208fc89d1dae21e0b4924ab6a4e23d6111f082d0f53152960e76707859e119 2bbd775530062f86c14837044b06f53bf1e0c744543710f1bc5f32a4326ec723 47bb46fa4277f7322de640d892efcbcb4ab9b55ae98141796bdd3bc55700a5e889aefaacdf07b91601beab25701b67b1d8143bc45fa424abe17a87339ee81605 false +check_signature 2b38db2cdca299027036545c23d5393bd77d436ec64c6ecba502696389c96cf5 579c1df30d4835e3b2e936338500288e79e12933febf02b352eb5d27294fc9b6 19070829f79aefce3d2ec89f9edeb0c8aca397be9dfe7c7e61054f7855db17ed443bbed556ac031173e9368248707a1f448cf3d30a5ff2cd9462d4f9519f6700 false +check_signature 7c6b87d8fd47728d3a884d759933381af6bb4d2a96b2c49bef5f53e399a69a4d d7f25c022d5605a83302313d70f6a2ea713c0959acc3b9a62201f7d6a874717a f9db0800d0bd8adda73f1561b2ddaaa280b5494cfcdf50f17017db64195111573af9290a529d3b5797beb64131e81accbed2cdcd24b27e866b70f9a33a112108 false +check_signature 1c2b387678775f50b8f43831bd95f9eca95934b8740bbbc98f4f0a837696dffc 4cae4f7315bf691032dd69f954c15b34e4c9451a5ac22cd362ee2c233597c516 93ded767c4e919864743858366337924c77f48e8fc584897a4346fc6798a5d0775b926dc77ac6aa00ec9928b2d026b11a95d975e993ff760d5fcaf920c912b0b false +check_signature 22085fbc90aee2d39d163780cc0edd277fb8cc21a9de6eef48d8443c25e796fe 0af568edae4573ca7000968aded0e683ea15312ba23a8ac507a577b464d72d35 f805d9b72ca1a9d38e36f6a87794fa43820bbc0679e8e5f0e00876036cf88853df4c384ec53a0e73d53c3e9d6518b9d129d62c391897aed077cb3d6aeb311e03 false +check_signature e67361f87c1b692d2740069d0996ab25b5852a84954f99e738e434153f341a09 6c2924a9e36add6fc571c590c8a7e42838f6d24702c4dcbefe87861baaa4144d 2150c0ac0c9e7287c48fc1b4ef5d7022533cffecf19beb53484ed000a635f70ca3d8a59e5526cc1e6630919797192ba29361ad2ad56da174ccf380a5696c4bd6 false +check_signature 41efa2d1cea36c38cbe8c2b60ec289dbe3196ce243427761cf13dbf106599c4d 948bbf0bc9272d659a7e32d530bc8f9f6c0f3e8e29a5574d2210c861be0bc585 846037f670c59d55add55f5bcc6f1ec9519eda2a98a0e959929542fe7f55d105cc1f09ca1f94a8291b04c3362eb7ff1bbf207d816f97628a9e1b66781daa8909 false +check_signature 99413fe3e6df21b9646af0861b627057fa21ce72afddf9e988c8fe8ad81ec167 61d3821899fb0298aba85647dd1eceb81d0cedd7fd56f3b551ae437072fb5b0f 8fb61961dd380b39d72c0051df3dda80ae952061758e75d36f956311aab1d978409734c57cec8019c189c73b02acc3fd5c15491efaad8dd694dc099f24041f65 false +check_signature 2eed4b90e827a716c6a6031b6a8ce3a6e2d8050c113b8a35793e96a3f587c494 46289a67054ed14389f569924a8d5a43f797f99e7997cc55fe332ad4edd111dc ddb9ba292f3bc7f3cedb7f8002afb48fb945892bca9b3663322c704b1e6fd07630312eefd570d641b9ad7cbc28293c478ac761a61e50e69f8e375d64a5b5f301 false +check_signature ab4ac3cd17ebc3486200644724aba70e94a2fb229bbf56b61f69be7ca31410a1 3ef18a28a3734ca5288b7958bf7e005fbcb5474e55481f6db9284cda3b998c80 c9146b5e65ca656cf86ac9d723917ff7e28b1d37613fd624eef0561ea1d4370d040120793b2c365194416309e9b74998584e275024d87b00a767375c70941503 false +check_signature 36b0f1670392eb633550a3e33f919f4b3cc6d9150cdfa342fb9d978ce80bffac 32bb4b518e6558c523b6d97306174c733d4b82dea77412e7fdb8a58d0ce56a4a 1117214a2e3904d126e35bfa65bd6e2cf92965eafca9ed24bafa2e086fb21c0ee5c311a6a092c9f09910e611c4695a320db2e13bf3abcff1e724983feacbb401 true +check_signature cf7c8c99315d5cdaf60032a2b68301783f7b78ecb54f91a766471e51145c34ab 7e533ef03f6d1194a6ee2e28d6861674fe84b10738cf50d6aa74c7044d9a7654 aa728e3812451f2dcca9a76ebae72a214c7d515446a4290850bae56df8e29e0f1e1ced2b5ce90f34824a1e7b71c8fb8df6048388ef9a096ff9f7b7e6471f713e false +check_signature f486985f8cbdeccb31d31c44a3c6aa2f947c075eb38bdae009ea24fd5dfadcfc 99cc6d26371379a21b0e6211a2117b85ae90829c531b0cc8f1999568015fb743 c5a9b6f8ab7726e816e5a59f4b4036f41988009abf467e23b45ccfffe202c30773854002ca95fcf393c76ec36b0c0701cebf2af932a0cf58f48acbe18ee5e40d false +check_signature a616dbc9c4aa24ae07ea884af436624effcde71c36e1fa06545bc46bbc8f6ee6 4b213e25d9e27995fe37b1b2f25ff83b2cca4b2bae414245557321d75221f6f7 85f5353585a71d15ebb38c01e4c745ffb58bd4b71ef655f975a1b206b34339016f2e8c4afb3bde894fc13167b6b10ef505d127b998df095ffe6f58c80d41f10c true +check_signature ebeca3d6c2d79efa4fd50f5e6002a153daeecb5a33c9bcc6b81a90ed7106a4b9 fc028038cb797c0b5efcd3a4628b521e8fd447776902d03223dcfa7ac23842d9 021e1aac6ea35fab2d01ba32209aa3db7dc968a89581102c085ff9e2ad6c8156ac55ea85cf2b348223ac2d769b4a259c868cb2840b80aeab1b3094cdde7be7e4 false +check_signature 9e0d34ec48d4235a5aeeb9e9e09eed2980f60f4bb1e611cf68ebc49f6e7097b2 1a8352221af3f0f6ea7099907db1e60637d21b0ac59ddc9e140c95cfa43d5b7f 8e1ce199aff3e9cf748cdb2fe42517defb3ff8b5a3e47dc47b1338b88580d50e168ce3906191668fa5083ba71e632da4a2ff445819c9efef9489420517dc69ee false +check_signature d06a757776580f7222ba44e758b43f2d1a667b10605552045552a95a685f5543 91a202e702edc9e2ecec11d3a8c554661a5b24737ccafc930cc836b7a64af596 7b58a8c00efa264e6318e44d0dfae8bb8698bd2206b1508d34365ae92c80910ba765d145c8f78f07218c8aa1f8de6a1b59fd56338e18a3b24287088909c17709 false +check_signature ba2fdf6501d9b35e069ae9c6e5ab40b0b33145ac19be58991b0c7bc33320bc1b d22462db1932f9c379fb18d37921ef8d2c60efbc60c409d1cef47f17fe3af3d4 59ddd39efcbe21d152d16c672a478c995fbf19344fcaa6b8b337a7f382c9bf0421214c27ef4c8e1cb761699423042ddaaa694fd33ac6281736eddee8a8117725 false +check_signature dfd6269c32866dc3240843537cb25055528c37c1811ad9a26c43656e8e5ec24f 353eed50f7fdbed51c220917815512524a1cf56011daf5351f8fed9b86c0f8dd ad59d195635f49f609741590071fa1ce21a85fad10dd66f2cefbd3c5dd219e03cc025aae6ef769d5112a2a11a84577da52d5a0e71f04de182dd8390dd7be0e2e false +check_signature b40eb9920925728e80c62dfd175f09cbb3088b55ad3c50801885421c417ad81d d548f2dc5910ef08742db181ff467c3a49f6ea8469bba98c31d7213e2bfb9082 950501c1dcec88465563eda272d525104b11a0cb4aaf30bb287bc80c04212f5fbdd84140e70e001a9b504530d3e28840a724de5e316d82afbaf6f3dc7e722ac4 false +check_signature b0f7eacc37d86c0085d05382d10b98c87d0a44df3394cb4b4a95b6bb5b0489ca 945ca9fae0c031b56d6455577da9c6d50c34948ebe4806dc3e7a55ba5e3e5e2e 363e33c0c79db01f1441bf473a1f6373752a05011335e02ae7277204238bef86e73755211972068bdcde0d5bf71f4df11ef23bfcd365920a1f9017e7a2d6f4ba false +check_signature 044c4d99dbc3799419be7ac13f3c00c31d7e68b1ae1240fa3d7c7ecc5d8ea047 e9dc944845e47c918bc11435d936ea1d5ec4d6f503a5c1ef26f16d8c199de468 879c4f672458893e99c7a52b73552f0ecc68755fdf04b2f9d489d77c78b3249f3cbd316509f4bc82f9583c5721898b9ccca04c364860874cd9d676f5cd5ba2b5 false +check_signature 94b2d5a1402e969bcfde41c40dddf9da247f678db2c5efad164c30a61c9f86a5 cd735e19919b83434a2434778b6ef621397b3912c128c192a1ac23b0dfd9600e b09b39cb37dbceaa6936d1de11fe0cb5a2e70f45a69ecd84659f1bb099aab00038d9e7a6183ac3993959c67908491b8a8178047fa11e59b24792c39f9d4ecb3c false +check_signature bb1030888e63b8c7110013b097f723446d1217992f9939288658f51103d1d3ef e5a74de01c357454de2cfa526726f516de18fa24d5b0d3a4ea903ba792f3fca6 fb15a6b9ecbc2d5f3d0f2a67b1145d9bd207c2b107f7500f743311aeda20aa0a0881176c67345a5b04f1f91b1c90bd66e445c99fe72bc87ae146a8495c961ded false +check_signature b00959496aef4477e128b0da873f681254d9c70d90ec8d080531d24b0b955382 b61caed00d0f2ce043cb35e16cf10cf51d07283074a62789dfef1d6db9f46a32 9400b7f037bf357c210e115814f75acd89cd9dfa8952c068a37ad3b8a42df506847b85896bcc026408f9f18baf1ee3b7a628a7acc1e1e9ad97ee86162e0f7a03 true +check_signature 092e799c2813b29f92a5d0b80b211e8c39e6016731d4f75832df89d5d7007fee 072af041186450771f90a21cc255866fc5a0a3df8aa5d2867a1c906b161e6e46 d3d4a4409d76e8d92571eeeca44ba14b2ff1f4c303d8a14b6bcc01ca67e40f0673abc44a02ff63d85ce82e5bfae2e0e773f800770d48b5db817789b8a5963a02 false +check_signature 5d782398ecc670760cb2d1e4244417e77c0ec2543865338c41b695c6b2ec2604 27970955a71ceaf5130c639d5df70a53cf543ace8a0650eee83a6fdff230635e f575439d3a737c806bd0dea7af5778d3bf67e071c8da5dd75c6331fd1c40dbc8fc2a8f58ee6508b3582042a9c9b57087c9283e5c631f1894a2dec803a73e377a false +check_signature ab8a045f9e375e82fc0c0fb392a595479f96a02d9df4ccd5c513452929254c13 8ac220483081a0ede1542ac614fefcfaa7871091a80c8cfb953df4923a436320 625cbc5ecc85d90d027bbe9aa5d4b3cefb672ae751f980df3c0f2f7d523997bc0ecf099e1a0fd2e9f27b4e93201bd6a16e01236870ee6687f18be2e6bd8c130c false +check_signature 59caf0ee55eaa045980f109bd33c874ba4dd08abacdb68c120550f08b759c115 dcdbc4856e3bb58f3f93f0e01f6c4aad29ad004f89120c20e23361b4b7d92bb1 56625ade72d9879381f7e49833496b74415926d7a5c876b083392e1dd21310ca41606157aa9aed49762d53643d3153d0f096b7fe66a92ac3a149610ad8ca1bc8 false +check_signature 72ed2a7b4cc0072c7122500be039c84093cc33ad13bc6f607b804339099ac942 4669c09eeb0f1e91188f67fb173ea55cbacef93f6f49cb8e70767bbb5cd18a66 76066b200dd4459ce276802714e3db8a55ed667b101e04fa325aca0d003080f81daa36d6012f983f503e93cbb6ddc15c5160449ebae44a476d050bc24dab68bc false +check_signature ed30b463ce0a809ba98b1eab6b00033f8a58f41dfb70598ccb6e5786874ea67b 0b5119b5a61531790e12c021dacf19d99021e8ad9a599d590d2e7f926917c263 8bab786045f591f086f6edec2c59f367f2bd88e17fbf05adc3ceda0864343e02b69b3467f496f36008c29459cdd265c489a8ad206579ca8abc263617b11830f7 false +check_signature 5d3ab6e53129069a3cee7d934129ebd683a629cf6471117328063f8e825de846 bf21cccac5af8fc88f4b295cb76fc53f236ea90a9b764d91818d12dc3b06524a eebf4dbaf5e9dfa737c50cdd20279acfd0c81f2a4c377adc252cd7009f2f2e06c0b914160d4d9404751f4ee9909961d17fbc9de9b33d81243d156c2bfb0ae630 false +check_signature 41f467dbee45b9347bbfd053d3b72db737486020a1c40f06e0b6ef333d1f53d5 1e91a1920e2728946c6e832085e95e1a93c8df933d5225bc4238a5e67111e0cc 3e9cb37a64d0c83dd2cc59c0450c5b30346a2885193f1c82edf0c3ad9811cb0ded14e2fd6c5baa3ffdf843bd09adef39a870cd518ce6fd59da7ab99e0cad9d38 false +check_signature 83baf68adbf5ba5fbd469d3fd813b82d6fb75bc66497f12f28939731af02736d 199e3bd8c34c245f34a6304a092a54e0865286835e06d455549c454887a3e93c 3f262979546c1cc6f66bdc02d64183ddbbe478d3bec2a15a5f65f89b0fbff4000b6ae9a1a0bb0490daf17a40fb10e0f4c2c8528525b842eecfaf78b2b3da0509 true +check_signature 1625e584587542acbe02f8c8a9a0d3f1b1863f8e045f2d4f74d479ab0e3dd7ac 255bb90acdf25c4ef2eba319471f855d3dd2de4d9f28d4d56f6076b6a6309abb 250bcfb6b08827d0bca8cece09fe9cf1e5629fa4b50f0db1cafd4f41f4257a0aa10d303c071cbfd7f645d74d62eed16249ebf50384c3f986969f84b7675484a4 false +check_signature 95ae7a0a10f3092c7bda4f92fd58bce434d5e1fe34af50beca0f12bdd6ab0bc5 f654fdd5a7ced250731f3080f1bfa5e8d0395bcdbd918313897eafdc80f2ade1 bf8dcc2a41ff3e979a0bda9417301c1cd61c325d2e57d3c534696dace333e7b2428b527ad2961c40fe1e3ea1d41753b03a404f323d6ab48804d663a3b2db8570 false +check_signature 394b97139da05b290fd55fe72b1690f3b6b47573a076e30ed9d2e8cd0925f2cd 8aa444a2fe9c7484749e3e7a440cde97f16382ee974004139ec9f7b4ee5759bf 50172827ab815bad05b0f17089e3ceba6d4edf6f8af61b7a38e74125f3bcde07075615863ce9dcdb73ce6b9de343283af5553b3ec49dc959047a13e14c0acf0f true +check_signature 7cfab202a830d330361ec741cfa5b7d5c7505123bb43257f033a2d5764664453 b6cfc72898efeef5e594d24f00bd97d86637d73f589ed323b853b2b5337068b1 e6b74ff2ad27858a7ed0f31ca08db970e8333a2882129d6cfba8e13c28a6d4084a72b5c09a58ffe92ee25f61de56c534a4614f24437e80a612e7789995b5ec05 true +check_signature b7de91ef54c30e989a73b1021c1cfb975526434143b75a042b89cbbc9f543b69 174c845fb37d67435e3f2ba95603fb0540e5c5d487ae000d67641519e3b6338d 2a4e9a8c47f2c7d13e5cbbcbea5cc2e476cd1a33ca1eed8c42ed12a7799d6bb1a96f23b6ccfe55524b024513ad4bf3a6822e8c158679fd3aa3ef0577f30f3a02 false +check_signature 64a6ccb285b96ad91239a3d01677e427059402490f02d6c225416192b28ac8dc f725ecee5970e06273a77e6f849e3ec8954dfb534900c144de3532abecd20918 372e2ff1432c19902744376ebca010dfdf42a648e39d55a4e99270606a0687067b694b9b86e30c31583beac0f8a3fb886b516d26f9bda11e0b208ec78a4694ce false +check_signature c69949de32316234ee9b4edf5c918c6fe08e9306b6d033a450ecef1bf8a3a2b0 e14917d0304e53092977f6d2f6c2dc3977a8d1c3481f02b1bf1f6ddbed8a918a 49858023bc3e1fad6e61177fcdac2d1a19ee364caf28977869f2513e7ec9160bec60f793ce21c9b1266ce6caa5aa74023d5d499853c15dba4520c51fcc99e603 true +check_signature c6de8b6be8f55b03c8d2beb124702a9233a153c3213920f5f1ef8b8f651ef679 bddb153a531e72bc37c4717265632621db77ddcf9fc7639ff07fa8b2594e1934 170864a88f7eca4c8562dace7cb8a7f22609f361655e12ca8ad0072e0f7a2fdc378a671520487c35aa39e9e60e20df50cb84e525164ecaa19be8db46f6fd180a false +check_signature 118cb35717d5b9918b9ebd981afbdc18ee601fabe31c82ee303c4fe3f4779e59 8e0241b11eea585ccf211982ae5e04a6def1ee80b3c81d324aff93fd8635c5ed 41cee15c22217fef626a3a89cac811fd6008816f0038892e07c6211af0fc3793cc13423c0c429e3a0a92bdbbbca3665788a9f07cd38e8e11a3631fb7baca929c false +check_signature 4e0274f4a3a511b3954c6b5c25c6b2d15f41b404a2c5358905428e5e985571bb b3169603befde574a8a1a8a12f6ed0bebd409b4b87c8c234a7bebd4fcb039b6d 485c14c728a9bf25ad8e68445c84b0cf3f0c1a0d651f815f85ac5c3ca00c7800635ef1849d0be15209f0ffa6c3b9c548a3ed83b030a75d49d41752be935c2d00 false +check_signature f5835297cea168aa8d232612bc37898ce575102fd6f8705e2113ea20f027bd04 303b81c4e00a151ff9b73388ad1b6d9f3fe82d18998329f8557acaf355b7e894 afaab2acea687b7cc0ddee0a725bbadc4d57df0b95ab4c9330f9931ebbf7710334b1c1bc3d762a5dee9397b233836a03cef6c5a403ae50a0a4691c8b8d89d80e false +check_signature e8bb0523d701e829592a47f04f20018a16bc1e76320ef0b6366c9d6d3abaf980 30c83582950a0bc8ddb62ba40ef81fa3e770ace71fecf525a0c7747c29e441be 8a1eded358206824fa78470e87a2707c7aa104bcf1ab6db1e1553bd23d6e1c037f875120d7777a85487b6b10e9c5af725484b711df17df457865222e85a90503 false +check_signature c3d9b321372556c332cc11e3b2ff54174462135a22fe64f5447e2497c3ee2f23 dbd65651a202b07794b6c086300e55898ae57a76512d030339b6cfa598950228 a1ec3561aab3541efb75694fdafc94aeb7d2b974574de4c0f3db1fc93d755f78f6bbfeefcfd9a32e0b735da9195d8dd1a7eeb6f0ccc5e3b91547d6e49e8d1d0b false +check_signature 861a683b22328b1598e6786472b37a24dc0f558210b70a89862332c282755771 55262503462e16dd1160a347eff6feff498d052b881c60c3ce46ef78776be9b6 f2514105c2ba07c2bcb79e038a3ec4555b53d25811e82d5c395eba10ca3ee6258b361c0ef7ca5d05f4846d63f10c012f4a4b54c384ef269f68fed8fab732ce07 false +check_signature 5fddbdf64841c045c1f5d48bac4f86967da539f077e225d9b0649f4949b3a066 85c9cdb7f15a1c7a7a124eb186ca25bcfa777c65b3a9eec047cb90e63e78d5b7 fc22e3850b7c7b1538182e6bfd4958d90ac9ed39a045cc82714f58b1b70f9b0a8a8108dabe3aae55e68aca57545e35278a215990a4525e5e3170e107c00941a9 false +check_signature 28b3a40989adb52c8a9964457d547a5a704f81ddb7ab1841d9e5369ee8993aae 246834f03214c73f533f5b1af9bf216c03d3341bd795549f0a8a550bea1c915f a1c1781b1075ce9cb2511489b8a6d49cc54bec578b159bdac04b0fc8072cca0048cf2adb2ee3306a22941deafe491697d78aa5353e8f3def0135a7b343aa7406 true +check_signature 267306ca23a5f1e4c6717db651e8e4629872c3cb3baa266610588890c94c89cc ea7d4f99f55dafc9ee05275b786456b995a9c9f7bd4fe6ece5426486ea3479f1 9c3ca2aba1961015efabf339746090baea50d424320960e08276635ee4e17141c95fb936761c00684210d9bb1d3a24a2447c5f1d96311d64f136576239641c00 false +check_signature d037d4174f2ca11c3f1f9233bb72bb29d43144a762c8c68eafe3725a77af170c 7d602accb083b6c9ca821213b4a521a1e3e8732a4de02c30993a6d2cfbad0291 45f05fb9fae696207dd875bb2d94cec9c2afb54073b661e828239dfa7fd771016278f568cd1812296a7ce0e9447a49f58c5ad4d26af64ed42533c9f5c11b6200 false +check_signature 10866d2edd42bc452aa34f9d7ada99e154756ffd5d63753fda6903e8a36280c7 2e01a48d3aeb66109af7c6a4bb9881758bc708c686150407a34ba47961d4ad09 4c8cc34f3b9a4ccfa383116332b215750614279d4c943a6eeebdb36606d2d205b3d5a766f38c90c6f0c19efaa0213e950c1a9d93a2593fd0128dd87ecac91603 false +check_signature 3e73417b2245624d669854705360748852910b46d9764f84263167667be8feb4 c363177623884924272f4354cd4795ab99e8964b68f5f607eee1f21991bfa134 5e0b6a77d56263e966c8827d2b2fab5dff2f490a1804183f8cc7ce85250ee76ffacfd1b2370c34cc970af50394375b44f26b22a749eee0c0e85c4c779f8de604 false +check_signature f98ceb7508819a000838c4a1cf87e62438e601d4e1ceef2a2a069c23441f10c9 7393816e3e46cce00659bb594783a7259cd03fe4fe6c7b9fc4b7fd03740d0648 6ee489176426192005ff692610ad64c965a695e76b3f4b197b1c7260a2a5ab6b8dd0405b9ff7fdb10c90f64bdbe6520393f9f0ce5cf2781843fbeb51cb16c902 false +check_signature 67b6d3eb4517324dc6ad87f6f61565e1c4420a208902ebc67869316d34f6686f 7337c8634f92e66099720d6a022f54adaab67a20c070bd153fa079efe8ad1483 84039e5d11845db1be6426a3a1a0bb82fdfbab98d6498fa061a1295bc42bdb4c1ce9ad97158fdbbcabcb9e50d60dd2f05def834730cea05e0a1c1bf013547809 false +check_signature 1160b470576da71cb632b59a72349e3594f1fb5c33a7e546774c33a166e641e1 2b8995d267b698dd92beb99be285fb5ee23da80050ac6e8a5c8b7ba639e49c5c 05b07116f625dd013811ccf7f6a45c80d62af318be5be397cf424b64e8663ecfb4289233aa48e5ea4d38f29048edf09b788f607d94332682d861dc6ddcbae372 false +check_signature 2227537846d48168a86d4fd5ad908ddef0109230858cc62f7099f4719fe136b9 3b64b44ed8b79f96884fefaa8ab3002b815fb8ad78f2dcdf3e2bd2912ef4685b e1c832f5df31cf5382726cf59fcb380b2c7a3af96f7d9dc267e15efa4a20140b88bcd02e8281d932f57e480f51c04a2fe502ed7b7dcedf04e046bde951db5c0c false +check_signature 869534f1647cb409fa70864c984f2ae21ce5aa8284d1a3668dfd4b19468eeef4 b4d0d433ab18468f0be747032fb83239b897c2491e6a288ef6b673d74155e765 74a44f785a00dbef8307e44ef179c17d555c1579ccf6124c93c86c4c541765630ffde6948a6def9bceb4c3ac008d7e44ad16478644721735450326a2a0acbe04 false +check_signature a235c60ba90e0ed41f73154fe3c031bb2c5727cb440ced85af1e753d22a0e333 536f9111335c4fd9a5fd8e5d3c80c40d9a4c263741d338122ac95525323c50d7 c34666d3738dfbacc7f0581f9fe3af21c372e11642e808d5b718a09a381d60844b263a45c1af0b20222699cb7d095a10b7b25859b673112ba91598d6aeab8408 false +check_signature bc04e47208779645228f91c1918a09f05d3cf0c8c32c0c1575db3d20227146be ac1e19b618f00b2375710c8400a988fc1b56a673b395a26f951ecffa0340d7cd 14f1da434a10c517bc6b7f4095b3464bc4892773b911c42a8e47e174e69f41f320f18eed8a2e191c2b42cc3c830c58a67c9883e144e256b4487cf32d71fd2dc0 false +check_signature eff9493661f294de5c54dffaa3286669a060f42405f80608bb41233e7220edef 1a5f6faa7f79ee894deeb2769e6a495709c52ff0f4e030d56b56c80475c99ece 0cfe3bd359a1de3f84457060967a9e0162291f52281ac2f5af6696f869998a02493ade86061778ada01a2b1900f98afb5abb39156aa187288ca09f61dd3b7b0f true +check_signature c863e44dbae357a3708724ab8a556f9108ab6fcc41dcfe075e15c25fc2eb0438 e9758435c8c570a3301f53f53532efd66cf41ccbe75ed6a1efe489a8e2e02899 ada56ec88d50ee33fc96515e4b64f0490bc82b882a15291133e42c122e697a53dcc7b09ce25342e9d08c6492e7802d328dc151bbad690571f025879fedda3b00 false +check_signature 2048b7d0a00a1cbd656dc1f85a40e62d989a3ff84d3f4b01581c32228514aa2e a464f33c02f002293e877b3e286cf8ffdfde41ba151efba40d9726a8889daea2 fc8b5995cdcd7f180d30458de3d275ce0d83fbf3aece1233fba70f3f79276b975f61c9a80932cbbea4967afd0758deb2ca69474e7d1fcb2687c9bab6b9018c05 false +check_signature 79a5d24af20c513f836d7c4295ea4f98965a8719eed986b27562103bc3361775 7dec676ff630fd328f27b84a9d802497534083acd1d5a3d7d9766b79fe01b426 c58fecc9d000e2f799adfe7bd58e5b6d29d35d36aa8af4313dfa4e04b9379b27aed354fe46bf9518ce7afb2ccfbc6f6d6c3d4609cb93ed1e13d7dd6ee7a8df00 false +check_signature f7a8c8c057bc7b77b663e7d0f83e5ef516c9be2522bd391fc903f0d04a527929 bc6b73b916f696c176c84a1e6a86c840e65797fdf521db4d67aa0a1392bab1f9 ca3e2ecf270d6bad21a40e73d1df8b7a4700d9de0d424a512bef08707dde8d2127a4859cbef7babc243d8c41f970df8cec8daa35042216caea5001b1fbfdf509 false +check_signature 0d7a2bbfa8a9b5f7745766f69f8a2cf2cba53eeda7bbdc64b9db57a51811a478 e061f576b5636f78bb3150b57f4f0b8bc60405a124b6ff5f0146cc7c0d450676 abbbd119963ff747f41233f891eddb589dc4fcc966344e645ebd1ec83c0f3108b1f97839cd0590f13dcf134a3d221df727a0a211684861f59bf7ac103f7ba80b false +check_signature 40de75c37c2135c938dc26dec5a2edd2bcc6f3e992cdf3a1477ab09b59e395a7 f0b85476fb77bf8e5027f4129ba0603c5cca7c91af27a60f69064bb3fd9deffc a58fddfb5b812b4d43cd5485837ba475042a407006174251067fa472deca8b054429e51745eeeaea365a6eaa1391a7e3e960ac514c998e8d22269cf85c572f65 false +check_signature 245510a3a9166aa3f0eecdf731a09b7d649564601f4088db9433a14aceab32ec 0060e8c9a110c8488130b92079fde883b9abf8650badfd048baead8b43f920fa ea62ee9e7dd4fe820c8fd68472583771c85121ad4f64b57777f305f80daeb6055b9a4d9e7e74ba681fa450ebe7736e3083cd31d7e0518a831cb49b4e33ed6e05 true +check_signature 1d2c08743f2ced67fa2c81d22dfa0422707b6f5bf2c6fa5fe5d2fa98f30e9c35 43244e32d540870dabd461044a0ea66e26022e20c11b2408556e5bb5487782c1 67cf4f6517ba1c33a55ee669f58b20e3872cc48160cd4bf12d02a9dd326c3401ceafcc036dd3f1d1c60d152bce9221920ab4e263d8dfb16c8ac068160886290f true +check_signature fbd6a5b5149e48b59f5d9b224d94825e46fb8251c13707ee22db309b28b7241b 6bffa475456684fc41717e54afd9a7c87435c1a42af2c8143dbd4f4a6ce57940 21a2ebd77fda5dfe55d379e2873a6c82d0860c8b634998380bf30783fb83ff03da7e47b1f8fb1973c05278f02f9404eca6ddac607a9492be497d58d393083101 false +check_signature 49e4ef0f401f56e8d13588781372d52fad8c50ec0309f34951ae7720207b4ba2 c75f13ec73e2c969e01e2e1b20bd4875c7e6dfa8245d4c8d294c72d17589947c 634ca8a07e91c8b1b44517252e70c3b25e0d675672ac8aa1deaf4f3746795803368c0c5ff0638f519682ff9a644a198cd9b304a117672f6d237c7d3a9f67b1ca false +check_signature 922eb8a3fdfdfb72c479752a222823a4b9dcb9b2adaab9f3290b12d16187cbc9 127bc0442e503bcac784f63cbd7a67c99c658ff57aa8a04bf94a773acb65f960 6b32d5430a8dea71bbe71171c672b4d66035af06fdd40d29c9cfb3b74bbf9407758b52dedacd4d59920f6609ab998d2fa4b0ee99d729d9745f5b99386afa2d02 false +check_signature 5dc483b7c5af7d72d1d1ec90298fce1b2946bb24c4c9e7b85964c2e98f9472f6 22f2b824ab15cd066c8218c80b3a347754c4f86ea8dc7da7749181b957b22be7 fd6122d137508da10e682e1f5a0274476b78a166d27ee73f88e6ef78fdbd5407e393d2bd4ab11299ea63451894d6365757962922d6ecf70071472aa9e947500c true +check_signature d1c739fd135c07a2fd7382334dafdd6a5c380f98794ab7a9be7c2d3259b92772 4eccd1c1bc025911d37f61f543d51ea2f49829fcac43d26624a12430c30940fe 777fc6e9b758958e4894af3991cee3220b0c3fe173043ed106d01fdf9dc6e2ec55695ba1e4580621615460b85373d7a3cb05a3846008cda1755b6d2f0d3b4b03 false +check_signature 158feeef4e3084c3562b5e546a230dc13c2079860755c53a06f935136791f067 57ea0e532c7713d007ed90137e8661c2654027448160dd91a59ea52b540a9c8e ce437e74846432d076f4a764e21223c4e73db95cf7bfea9e618fca8d9a0e830f51986bc83be75cd6504ca5fd7da3ed67c0bdd6460826ca05a259e0fb805bbd01 false +check_signature 740d8e9f7bd3d7e8b47d86b0440781914d9dfd7aa7ae9efab41ffee4ea58fef8 2c85e9a9c4fe99041756304bfb4f530953a39245ca65492af169aeb22173c0f4 259330a1af4d560b778dc0efdc60e74cf762ce2b7fac4b6237bd5315e3fa750d286975e2bd088d34f94f0c83d5de569ac9a0c76029c7da83ba834f369db5660b false +check_signature 9aaef40fdbb3f5d1d3508a07c42269538c88b7c4f406e12208efa92d8eee38eb d84204a4f7a5252923d12a1960e6cbfc577be908c47abf571c1015257c313766 15e5ff382ec4275ce0ea2a56d7456bc2f04d7495af75bd2d3942dda91a47200b61b54b14933b6e80438f9c6ae2b59d31ef1ef46421ab5fa084e163a1c057aad9 false +check_signature 6e97fdd66eb5b1ce84f1eabed2bbd3f449dff6f242fc4ff193aaa305477a2da3 af2f8e3668e998db4b647f09e72b5005599adabace7a6408572affe81aae7a33 e997c38b40c2780979ecb410736fc035a93886d97693881ffea556d332e41d09d943e19e371f5d140e1b64f25cb660ecad08b42b0c5fab48b2e8bec5bf4492af false +check_signature d812d009aeb894cb4e968f0dc8921fe67efe0a427dd31adfd665c1b487a02c23 617e023e1e5c7826c9dffce3d1cf74f759a2f34a6de99b3ef9750a44a64fdfea 9a90b939a07b8bca8761cfbaf1d88fae4372dcf341aa18691ad6e8b59a108a020c55d4d8ff60c6f4f6e035b8506f78aca58633dce3ac643ebf772e85b3490807 true +check_signature 4743061db604bb4252a6c3d9d87abe9a2976bb5d681a30b3c7c2d9c46970eeec 441d3b9612e3c81aa619a4380cfc02c44ba2a3ef0d2457178571310f8eb2f0b1 a89a094335a62476ca1fdd466e4d62dfccc1f337b2a912b804171937072a159882238104a3cedf3f4b969a934c4a857cf1a191ff6de878350370fcc227fd6c39 false +check_signature 924fe854330445d03c7aa53cee2405718039972394a38d4eefbdc2292a34e135 a7e515067f23f75f45fa2cf61e8074226b7c3ee3310956cda715d22efc9a994d c18e05d8e05c2426dfef77f528929ddad22bd5c300e5dd740631e27cff76fea392c766cd2433957512cd6d6ae0496d9f432e15c2916370099de7b0d9526ac402 false +check_signature 5ce1e57225dd26a02b9efc585dd7d139d41d24e167156e098100c10eb491ce58 ab2c1880bc68b85d4f08742e1614ffa49588e9c1dcc2fdf9ad6d1310f988b929 b227e6ecd15f2a0e6169c5d7b2785b9df3625cf9030bcacd3615d5edfba5f7087d4d22cdebb9df7846017cca29475fcfc952a4a1d0f49ee50701d5812133a5d5 false +check_signature 64712577bf51ea4ecb70ae3b6ae9078af5f890fc4fdb5b74048c48f4404c32ad afb6a402a3dd348b5bfd495fb5f86d261db4e9da791cd48e5d2c40820a1e6962 33c68c7d83be9529994d353450b54965b508894cfb7ca72fe094e0f4c04e7e999ab8c2a18b747bd7a07f1a1f64bbb7c85dd58e5dab1e2d62f58ed4d3c592443e false +check_signature 9e191befba7a939446085cedd5e757d55ccbe126a2ef531cfd5dc75f8e2793e5 f8fe66935b51a1fd702c784bb81db3d1738a23da0fda742f2202dffa46550780 d93cb9bdb5a9bc374bf594d8e325cb9f74f1e56510c6717464ef5d337d739c02e7d9a06dd9d82d0674b7f476509a1d57a691588486bd4c5ef658e4196eee7658 false +check_signature a6fdd3464bfe73871ddda80d628f14566a540230a9a31cdfefe68a749dabcd9a fa9051cacf454963ed5971d45f3212b78b94a7112a9db3b6925cb9bd0439ea79 a85b7d65cf3869cb9c9b404ec7a856ff598474691d491c82df209ad287e9297c0544254c04beada9e4be35267dc3c6e6197915dddfceb4eaa51679136b6d200c false +check_signature 4d369e3365ea4e3ad331a67c4f33b3f267ef2482a6232584d39416f1db61f215 4c88479e79f7ee7d211691430e282de47a6d94b49508aba4fd7b4f711f382c52 efe86e02acb1d4279f489c5ce39e046aed0672ea196827fba5be4b5a50a455ef60fc2ed5ab10306f72db6aead76663b52bd22c71fef1a0e467ccf5f869eb9900 false +check_signature 8035926af0496f8fb65d19dfee98690b1e8392a4e0b775e60fcf6fa73d884dc2 6afbe46f6523994cf8b2a2df5e4df32ee981fb0215cdc7bcf80c87d3bac68930 75ee122011611134b5ac3d2780689f9c4bff993dbdd91d7cf8396e4c81150508e1d4d9a3af36146597f995ff8e51086d590e6e4fe9304015813d3986970f429d false +check_signature d24e2d8fe4c98742bb11df1c9be5e279b552e89bf47b03c5b4e2a5a4f9ad9cb8 fa605f14bb7afc0ce33902eeeb247122689022df252d9591697c6681ad40cc61 cb78415b418d64dcb7b680d5b8450040ceb02c020d75baf57aa2daf1c335260e4c93ed8180fc00148f812a5cfd0545900b1a58ca0763a68e50b017bbbd98c84f false +check_signature 508c9f7429a3d8a5edb922415ee04a1ee38fa78e871063c5615df1565c336808 3b697c3ce826d03c59d93793488bc7a3536f89ec7df8975a4618d1e1522a158f 97186c2eeb1d63ca0ccd62461d369995307793e95fc0ccbf970365a8adaa76e4de2f16bb94d390d348faa1298f0fa5326b132058bf0cf9c73146089f8749a805 false +check_signature c42c5e4b8591fc49d82c3f75d92c2c6f8b82c5da88cad3170856b767b8d0583d 128092b4a86cf0efb3d7321ef2f9a959e8f029567f00cc6a870beb6a4cf02b55 d2721bd9694f584a18b01b6a4a5d1602c46d029b6c7ec65094a96623d798bf067f6b59fae6e39b2b09845e212ce82eb9bdddfc866744408d8fae04fd6e9aa301 true +check_signature c20230e590ee7c44f699329485fd0b3596dc5bf8b3762ba52d8053fbcfa70616 8dd9024319fd8aec424bdc546f04aad42c5e6cf4cc9d694d8e745ce81ea8a2b1 5f2686549dbfafc39b1056500a68b0ec113fa37f26a1d64cd2fe158524382b0cc7123112e4c98f8e8bd0ddf4436b3a42b822bc1fcf5212d942c25fb3adbaf9a6 false +check_signature c79b6f38a06078443943087b49d5a7a04370ccb13658d2a831e46d88355f4018 c51308bfe3fe31b4454ba7badb2442bc66017a60fb8c76e08fba4654a4bbae54 36923d696e323d4ab7c6d7be1111908eef6602e64898a95b42fe58fedace43444ec857fe130b1ce1d8a2e2e56b16c370ffb53eff6fb5dd85fbe0845205156f08 false +check_signature 9ef8c533cdb0f4f1433f6501196a73ef242fa7075bcbedccf4592cf1983538e4 e3b926c54e8abee5ed7f6ab361cc0f35620dafd763534f1102cfaac5370a0b17 eb6527bd35cabc39719ebd2d75dd2cbf90de0f2ff852b86751883951f586990202da4e1f496df96071296b558032b93ce89e06074348b97ad989c5edcb29773e false +check_signature 95be83e95c23dd2354ec575ac19b885e9817b35ba5ee824865d30c95ff6018d7 771bdb7854f0084dd3f5a98886b0799052572b2d2cfeedf961929370936e8923 10a8c6ac5db0bdec73caa04353833a08308633e4bbd504715a4bf075adafe19ddedc0f00a43ccc7ba050b2f477760508f88fe769f46603d77345f915a5307b0e false +check_signature bae3ce76c9dfb9dc522e7ff962f8091dbbd66ddb3631211330d7b0e85ebcaeb5 fba8f63d0c264328a06a0bf36cce9a1e276639de60f8218cf92e6cf7dfb595ed 8fb13aff39944d9e35e32e151287f7a34312128cfaea093a8aad9d3fbb5d69344cf3cc57aab020dcc7450e51dc741a50d6742441d980a8ab6e40b1ad3bc60c0f false +check_signature 6aa54cc35162a6388953a3394cfbed7db5dd8364ee60ffe7c600508d3c57680a f2d6ef311174cb822e5da499db677d6f1434a222015371a4f52d3ce84954d7de e5bad9b964084049adfdb1dcb278b92654187eb54411137210ecea5c1211cdddc7185e3d18dcd3a3a4c4a574b7994e024dcf001dcc8a1ac02245ff5aa02e1605 false +check_signature cd1f098b076220acfb04b0711a030e7815c761780aeec60091e2222f6ef3e49b 383f5c3b5d659c9c555b1b60ed2308668df8dce048693474e79fce6a2c98d512 429ca8d16157eec8e3e3738b24c99695fe714528b4228c87ed3d368c939fad0d5dc1d2041b12b48131d31c3f9c99410a8440d5a2290745e4779d715ba8551c7b false +check_signature d5e962ca9d8f497ed6fb556f67dc3c276f1beb19b87404b25745b8e9b7c1c785 c3648f806838e03ee63854e9b654242b7925016e50c3699ce4202ff798ded878 906a8564c7fb2b3e88ff3d297ed3e4c9638f4217e73b576091940a271056d115a238290fc6e59ba5f1507fd309db6505553aeb18cd8afc19410f56540a974e5b false +check_signature 6f0ab5ca8a477a24cc76ceec8032cefa6ba47e2506ea9761777c918834cfa116 a16a5c42afa56cd3d4eaef86c5ea64dfa4fa2c42f7e60ee9bced6a7d90e69b98 0ddf11324d2916627da61429a5b35e6873ec15045b6dbb7d50c4bac6c30045012a8eead85c15f263e22feaab0b65689a486bf54bc03bfe31fa2bc56c6d112700 false +check_signature e78d5e380e71a9014e8347eeed5907d28cd8e9dbbd6fcc672279f09b0e17e68c 16313db313441745cacfcf3015a2cecde652ccc8645ee345619af321bcbc61be c5afe26236fffac940c7299f8b3252ab3f1b16d81a1d56487ce1d941ae934df77a02b8b65bf0ea3ec33475bc489ff4e0532b365adcb7923c45c0516baede58c9 false +check_signature ea02bdd5b1af4e2f1da22a1d146ae05ba8615e43427277bfb8968c2496295238 4a8cd8c0243f6dc3e20076480d58c9068e79c0996a411bf6388b4a37d6db37af c1a278deb4c4d3499302db4a717649e2b5ab360e245ab1d74b40fb15babfb1e1c0db6b5eeaf66ca8b36d497a72957a29f73fb0289ce50f6fde9b10d56a669509 false +check_signature fd97f8b9e6988b85b1938f413838d16409c5b78141aabd2faf879f28992b90c1 c68840202e27035daa527a90f3640b7be0a90eec12d3060a06c785208136b2ba bc028f5ed2f67c8687e324ef3b3009aa9a184ad882187afd767a9da57525ce06a59e9c1631358c3fb01c22a9079e84252cdc518ce819374b930769f5bbd498cb false +check_signature 07c67b70a7362779fa51a75b004c5ff71f0042fe26acb1695f250738e25c83e4 b2c4857d1ed627fd58eadea4aa0cbfb854fec758ba31afc9f0d6b760cd1731de 9b8c57c2fc826e7760a7c752ffbb1cf3673c768d01ef2afd264396219f527708ca6c27e1673a502770044506aeea234ce6bfe5186adcde6ead43da10c5a367b2 false +check_signature b7619090c3b8794a2ab673024b703f9fe9323525e0bc3e3343bcda95dd930ad8 f7283136cc8152d87c9d5f3d66cc8ec0f0a35751b47a252f907d6a0f8f1291d5 33c7e713b5882d58d96023901653a4c5f7e2225158b74babe981603f393f750b530e9f96d9606f3f47ef62ec9afcfbb65bba51b7970e9f852a4f702cf60c2107 true +check_signature a9c31b6cf11ebb7c4762d143d4e2ed89d98ac45eb7103f2368644f5c2738ab15 f3bbaf63fadd634ca1b124fd27adfae9735c3edf67362e6ad0b3e23fc22d38cf e583148abedee4177eaaaffe638bd09ed3842296d0a7772eef433e2d58283a062f1c14a5a742ad0b280d78d25d894b978c310257782b33a4c2f2cac1b2700a02 true +check_signature b3d0a596235f51117aab92250348c6ca91c9b348d3822d95da24f699defe8961 8072544077252f2d8d68854c214c397b7f80dc520e1896c6510dbf9d70f32820 b67728ed0a02ebfe80885f214030c71d91de9c5323273f3837e263ed12f1dd096d22f3d26be5842425f515806d695ff7ce2e85bae842f4d62f2287b725805a04 false +check_signature bab2b1f0d0cd52e3d6648f7f436857b5ff83e6cfab06f2e30e4a80ff3baaf2f0 aba257a966c6f235a02913720c48edd9a9aa440c2b5a6d348ff629b456bc29f5 15442eccc2454134fac7abc3b94c690e5156780f35fd887dca5c3571364b83ebd28331dab24d1a858a0006aa6528fa57a6e4c68abb0865593c52f42b8d65130b false +check_signature ec62718b6b7d0f15b2b2df36394e33cfaff5704cf3792e7462b0565ae216de9a e2ea0427d163f5f769a7cb90ef4b777e70560af1fc1fe5f98e6ad9e3dee3ae6c fc399e664c1aced9b41295ccf606080508c487b22cc904de0a661db3fb37e20528b55c845e15e298057fc24e92bcaf9813389a7962df82efb13347918772f206 false +check_signature 36356877e7118dd9f513b69722109d8e124dc0f470528a68fe84b9a9a6747133 d701d942ad76ce1a34cf0dbf44c6c5a402b5e81b2628f254bd7703035aa54b2f a7aeb280ea3210ba5d6b0d3c0b9ad16d9dbcc1e7db21d86585be717d83a66908f803414efbb914e9d88bc2c9353b8f28fef909ce04d1dc6d7cedb6c65a8ffc00 true +check_signature dd42d467727c9167a77967ae7850a14cb29dbe1219dd9dbdc3083d3810be0395 714a876a180d2901ab556f2ffa05e8ce0150abd1823b402e0aaae12ba971670a 2b8615e4f9c992eaf6d5fb61cf30bc8de52b903ed3fd2c1f01401cb5856ff101299845cd204ac70ca69400d8f06e8b47d988d27b76ed1f6268a65638e6a1910e false +check_signature da4bb9653209d3c6293e7d44234f5485a5cb465120c4aee158aa2285103eada1 7c604a039e365d3c9c948ca5b7e8cc848e14993dc13cdde1aa974c5ac472183a 1e3c852c686ba17b43d09248da7b56f7bbb57c3cfd46bab7a168376337a5f80641e143005498c4cda055ab925f2fab85177f59451d29e289728f3e65de2cda9a false +check_signature 123dd37a0fc71a8f7dab3fef9c58233bde327e505deedb2ad0c087dd43ac4e8d 1f4c97bdcea9862dc0f469dfc1164fb5b1ed240de8f03d803b70963b51089e7f 2d885ff500d58aca90ca67c64a447e4010a78547cc0cb7c3e33265ae314d81437a8240be7e8d6331d4ba23e416871324f35c3a5f2343f6dacf56f08f9e7b4a08 false +check_signature b24c292ba9067d9a3487f87946cbe816f56bf4edf91254e3be763874b841b072 54fc44d8ee7916fdfa04189551b93a31bfecbf44eea4831a5d8170c4a2887a05 9c3c00ade37c79a3c981c9bde2553b298808ff7c0c2b15cfd7baf69b86b1cc5ba07e3222947a969bf34eb89bd35c95e69fa2e1f33aae8da251c10b5bdf4dbb33 false +check_signature 6bd812c89f9914e39c43c5db43c0c76e76dc3cdc4260869280c751a52ec04288 4f82cd0beaafaf263d4b301cccc456701c9c4e84f535f7258dd35f437b697288 8127e8eb921e9e0d6fcce449b6deeb062229888d397f05c45eeb121422fe55a4fe4f824d057232efac39b041e629a961e0410350c8542510d26b7dbb8c59d700 false +check_signature ad37b8eb3e902802b0cd0f453c8b3537803b60239a2a945bdbdad8d5dedce22a 10b5410db11f23976ff72b144aa6e89a8e467d952fec3fc64c8ef1d8d385566a b34f41502b0b5d06241714ed3d8c5af725aa3efcae3e1107bf28bffc676274017a1f9a35167956cf45414b6d85513d323d052f1036e7fdb3e9bb215e4de81f23 false +check_signature e332b68193872be0b7370c52f9342d2af493d7c8ec8fa0fed011a072aadf27af 584042a1a72c083688740e8f43e6d6fc45d0df3895a2f642db2a8f8093665407 9f61c465d07ac82247b838ed0c021c7dfb3afa1a3ed2d4b27bcd5a15fbca9a0e9a99483cadb88c3f728392fbb8943dc75effbb42c8973645090e75ee08624704 false +check_signature a11a3090f04fb5b565e84d66e6559dacec6876590e144ea7c7718b4af4fc8468 bba85d6f6d2e47e9954bb5ca8c413bd8b4cde3afd67ea3482aa0867f76fc8b20 9672d8cc052fe43870981345d5271fbc73f80c3651989b808dd73da35e528d09f0c68483ae2a53d8b3557cf074df9b7d398bafe54b5b5edd0562995f7aed0a08 false +check_signature d2e7afcd88f7571312e7c938deaf4688029990afc41ba21cdb3e27e5d96f20ba 159585cf59573acd1fc0af4024dd5ed3ca9e384e4944173d1c20505d33530dc1 420328d38078b636dd323d51e4b280554079edf8307967879372663ca91fb902212ee565667b7713b95672bd77f31ba630a34ec0e9b4f28ce569fc2e1b151e08 true +check_signature 393a074d8661c1addf8517d45fe2f515ebebffae21290cf3d5a068b2d0edadfb cb5fb3291c1f962501b7adb62625039b8d7cb03bdb45311c399f0bdaac128ce6 b852036d43318d77a7af26ad23d14837ddd2e7d5c95cdc8edd1b10b0df7a4efb0657cbf03a919d7ea9b3d508896328c356e902ab73f4a8c5402df385c3988bc9 false +check_signature 86578822f720e4b405683f600715e31e1578ed9b3722d16d2b14ebdc583022d2 6c4d7ea00922ae96babc1b4bab3b00f92ab85ff3fb5484ea7eea53c886745c63 425326f54976ade0ee0f5bf02f8e776891b0b52baa2ab751fa063a71767808b63c7e032aaa4864387fedf4e8ced18e529845906ae18a8074456024cbdc2595f4 false +check_signature 75ca7229833ff99e10cab1522e12d6b72a383537a3d671fee876e4bdd6f3dc33 53c4ba122abbcad244fe576d98beeced68071d2a3e98789cdb55636d6475febc 2513598e2465450808987005b37e879192ac01937a772fb43eebbf04456fb30e1e0a222f953940586ccb324e9dd0818809ae5d72425c76daa1ba25667ff24009 true +check_signature 4571ef108336a669bbdd8248961d7e2be85525d07897e91082a555da56d702a3 8b91786c700d6363e711414d73ba9492c705dc4942a39d9869e18922b8cf489c 070aa09bedab79278ef75ea75ac8c174842f6f83f41839c377e7ff71da8c600f844a59c7a3bea7495f4afc422cd22a929e3738236ccb1ce095e1d36469e8c80a true +check_signature 1f56e379c9bf435c33ad703f3c99fc73f0535b46c5e75cde31547f9a6c7b8c38 6bbc618ec0d00b9fb9bc35dea6cecec3a5cb19e273415ffffbc00f94d4f1bb23 917eabeaa44a910c70510bb115f0e3990b8aca334098d52d08e77d7c127dad0f7ec743ce9ebe75ddeee907852bd82129fa7e4c34c0e748efa0bfb4c33a3ca208 false +check_signature 4cc6b207f6b4081508df80f0b1e271e419ca632b831ff45681fbee029886dddd 544ee5b40ac56134ade3b95a8ed49ef12bc67b6738a3c6a22cbbca50c9a6f30c 8717edf761e836b8af591b1e8420b5e808ecf9dad6840a5224493f0567ca347595fbea6ea910d7c4ee8eac1ca157ed1f424c546f9dca0f89f3601f328d1a5d0b false +check_signature 56cd23a389b0422692ac1efed392bb6155558559cb2d3408e10948d370b816d9 ed3e9f902e8722a5a675baeed2940642e17433466da43edc3c4f8515db5f6028 86400760cefefb8277bd766f32439f18928fe04ceb35ad07fe68b887164517015033e4bab878904104aecb33532c3171ff5aeed2fa21c349f0dd88389150ad08 true +check_signature 9afe46e17d7f7397137acee821c8bafa219182566b748f2209b623b1db754e35 5b58ed0ec810de6aacc3faf8f1fc39aefc34e6e2a2c4592e0affcfeb94075968 35893bdbc2fc00521440f6976297c4f3f4f8513a315221f29629afa9f5f6ed0c9a48cebaa415f94d904afa7206d8ce8f2f81cd4e270a66e1623929ba726ba105 false +check_signature 61fd1be975bc8b433f52ec3d324785f538a1cf6ed0a7d209fba540e35367a827 887cd29dd89600cc3a89a0147d6a08a469a84d398ee89c887a0e794a58e71cb8 3288617c94184454936fa54fa55df15b3ecb4c06089d4f7e6d15eb167069e80fb79877a1e4b767fc088b6c7c2381c8a0143145a83c2be49d3adeb528432d6709 true +check_signature cc06ac83f0a8ab6438dec97f6060ccdaa82cf10615355433b0c5ab6d3d8b68d0 27bf516b72f458da533a3570d6a4a1ec6636fdfefabe5f5cbca5a6be1f6070ad 57575dddb8c6252b4deb36f1989716f189ceede7f4a58f9c5901ebc5a1f9e10daf7bb2587eee88116751c861c8f3b884bb3c5c00dd885f00e009ee6221b5ab05 false +check_signature 3251ada4b0a2e90d8eb2789513c92f45342f4ad8c43d78e42bcc9188724d541b 8c70a4ea8ebfacd808bde4f59ab5ed4dd3ad9cd0a1190279d3dd2e9fc0522012 afba63c335cab69c790f03671eb39e366e4986373949e2a44c50e2246614400379a6893295e4a531e38d66a5272d52e31130fd23d4191bbf3a7f8407b9bc800b true +check_signature 7fe8d44a7d0fcc50d6b72f38022a3799b738c425ccf36351762d843f7814a10b dcb30e49caa7277be6bd2d41f08754d20fd319b7dd6018bcdbca473e396240d1 f7a2f31d2b21846421daa68ff630434ac5bbd7ccc3c11f14e6a45bb84419070680e4b8f1ef4e56495a93f20a519eef8d6743d6be12b8a14577fb8b07251e960a true +check_signature dc7e8cbc687e5eb02eba040e050c3d1d49df4240e099bc68b3e6e718a7d046d2 c95afc0c1dc8dd4b8410577b28856cef1776646f3a57a6b285f5685de4e45794 9798d1af31f4bf9928627beae70993c721f8ccb2a4b81c65e38339d9725af450e842f4225598309600fe7a73e0da56a92ae36b426f159ac3664eb141054ea7cf false +check_signature 369bc73b5e4d264124578cb06a9d9c8d1b726114603bae878ea72d1f3f890604 19657028e7095fe21224b86bb55ffbc7571a58dce9562880a3e077cbcd6acf97 1c8039719bb9c99242d14c15a27966dc5f318a589c0b9d959d7bb2f15da5d0024d6dcc8cb2ae743f05164265113c55a656e34da2396aabd70aacde4724135000 false +check_signature 87a7906aa41eb48e09911fecbff8aef45b1050f36c3d51c506462104eca31500 906cf52ffb91ee9144a050a69cd744b21153b259fc206a81334ab470999fc0a0 da3bc99ff0ba3cba6474106a73fe30e52afbf38934f21d52c27f0c595d37390c6c85752c6a9e3ec942ece3f80a649359377924aaad4adbac0ebd27c9bb49c210 false +check_signature 3574b230a077d4c2027edf0587cb1bc5b4387018a540cfcb35e98f71226c3558 14fa0c4832576c67ec91ef90f9c0a15e195f9e8483bd5791ecac123816851210 f2512e4441e68084c570b3782909acbadacb92fb8ef6b4cad7c677a39f64648df0413925f887d54ceccb0eccc514da7db1b15465c391e646439705338fadfa68 false +check_signature 6fb0022193a9612ba6de909c39804c7e30612636b158c183eab2c2edc11524fb 563550424894ead70344418c04f0acff64cd0f07bdf0a1f598841d5e020207cd fca8b81b63fe8c96207d4bb5997977a4727d23bc619b8222653960fdb444af1e614b96c81d3fce5601eee310a240d10bf0d6869f1a156ed2da199d5f943ce20c false +check_signature 6b31fe8919e7840e2e2590cf6c6489b1b3a28fd2782931f1db6cc29f477f24b9 5e0a8c0d3756f2bb73d3cebc251772f727d24cb7e5be68e724c044f37e0a10bf 0b55baf92787ec59f4b570d37549df70f45c75a98b0964cb8e3a5fe013abb80f2baf1c63023a3bb4d2631cc7e366d728c24346476d1a9e04215bb9c1bc140b03 true +check_signature 0d8ce8a0d66bf6da72f4410132a728d0e3a8b127960322caaf3b3cb42df5191c d086536f959f2758837deab29d4fed9e04cf7a1cfd29a57b891ca6b232b57c99 6085ffe8a5f3cb04776b3b0dd892af1bf84bf332fe204d9d3c0d75b594fdd0001c6c731ff6788116025e74e7d56156321060febf724bc8dd52fa08a207612fb1 false +check_signature 7749c898941d030255ce3427ff8e6953b2f0dcd635a36fcf24621ecb8129a3ee 508741c0150b3926302ea4c31efaabd6e3689e3bab9611cbfa4271054a0e6f20 5c23bd9977ba108ed80d92177cdd9eae7ae9f2274d2b9b4bdf02306d115dacad201572086fd10ca264f97b7bf465b8ee80531c3e1fb86dfffabdcb95d7b35c0c false +check_signature 1ff5a26db00b7139781fcadba41953f1f56d874201d3b76f34215670719a6c64 fb72815f72232f058cd90155e767db835ac0b6d129fa0ce8bffc359546bb9032 95fa08a675882809c14a9a18b1bf684a1af260bd13b76f3acad6072a882ce70cdf8ea14486a9bd6352af890140b62f03700deb5bfa819e19450c1494b0284828 false +check_signature 35868d403800996ab539d392da22d7d4c5d9f4d3b1f69ed7e5e9f3050ed5e0b1 c2365edca7111e9db5fa7ff5c78cf9b361e58f29837bc1f8b3f60ffd7f4b2e5f a96bdf3d4c135ade1043021ad6dd0d56774dc948256f77887508a8e96d5c880885eb9e4c94c6350d141ff902c802fb5c82225339c4ac307fa9bba69eab5d1d7f false +check_signature 293bc3dcc3c80a467f43318b3c9d2c868951d1f4a7dae901539aeb613c08c2b5 4d8c7e281d7674b3fe3970fa14746f22d555e2f428b1bb95f89fbcbd3e07f8a1 517a38457a7384323211a9377ac034fabeafed0bf7cc66fcedca1bfdd01b22b5a0c2b2a539a14e67e905e3ca89b96fa5f33ff4d6ef85284fb21cd0beabc10902 false +check_signature b15c9bb2ec0aad9626f8d9c6c57c86b070ba26d45d6da8c527481c91e862a4dc 206f9a63157d7a4760a83b4971c6b9c4d4843dc2f808dbfd75117283934fed8a 9943ec9ebe4c4a2ff580b2b4bcaaf5d69ec7c14888b7524f0de4372900bbd50fe876d292d0f8234a364b57c867e37e5780b094bc821aa150a15b5658e7a7900e false +check_signature 65e67f12b45da9dc732e885706061338ed7cb239ef6dc77bd22df1cef86cf50f 4cd6f504c9959b8572f3b5426425532f2de5775afc0972cd9e92d0200458ef49 32f97c8004e0e6052dffc51f33feebf05fe9fad6585bb108d9f3bbadf45ae3036e8f3a389e216e7cf65f407077cedb4d9a4ee11262159455e7d468fdce23922f false +check_signature 1b0a83599d84db202ed03d1be5c6a0a61b3e0cf17a18e6c96c3dad4aba7ace8f c6f8dd77d135c628449ea9f7aba8b4f90f0d34be615d590dd98cfedaebd76ff1 b75dc77ecd3e6c1bf2c01ac2a3429f154768bc6b050d6596b3396a67a9b5c0ddaedebd09a2de0c3f148deb12c60fe6d77dfe725a0959241f1487ff75b904dc0e false +check_signature 08187ae23d484a4eb5bc8ff4980351dd1218de52c5094b02fde78463783c1f72 81c2ce90fc7d5a61b4d4f8e43926e128781a19a6a199c689548908036abdd7d3 9b7ca125e23d0ab134934d406e09b7a68a2dc130d46518dd8beb2c9706d54c08c086820d8bd1ae4d5d7db7ff29a81d49587a9c10bc8c81cb404fd48c4aa2cc02 false +check_signature ce2f998aea3d3ccf8285f96da395bcd44bed633550246adf82f8196337ef72b5 0be0581b1f400f9cc43f7356b62a419366d8f42cd88c77ba8e66b74b92e2fa71 f9dff94887f0a03dbee7ce2c898873bb3e16a26c91f7584bad4c303b1f0b380459f8fcaa4a60ab915ec3f2044b175c31aa341f3e8cb2c4e500af146fe8d4200e true +check_signature a51b3fdbcdbe5ba02dfbdeb5e13d98d7f8c22e366ec27b1c5adc83a4324032db 18777ff4880734c68ea9236975cb19e325fd002bd18b8d53932d0c780d33a910 6e044e16512c14f3d20de37541be16ce34a65b3bd8865cd7011a4616eb2a420dc5d89e1289a1b6bbe4c788bc866d20462fd0e98ea0670a80b9cd21a023bfcbcf false +check_signature 1f1ce5c7d9475384a9d039c1abe4091a0b08042eeb518a394b3b839ae4b8eeae 85649116b7355980cada34a1505d67364b3bb328183351b226c916c1b0819494 756274fc77dc5b79c5b258f09d0beb26f86172f2a7c21a7555f376c65060a0029b07a1266aca33dc26e32c11e67cad85220e47a3c087088807a666786c1456d3 false +check_signature 5d52e03633bb6459c3e653b7eca47e40528459984a005dc2d25dbf1e672c71e6 8655c119a2919811e21d1da9807028b9456be0ad158e7603bd25a7e0284c8eb0 b2c6a904b4f9d2e08bf717554c76c675d2623b5329e6f3a32b9916d4cb3dc6f1209ffb8847f8cefe6ffd10ff1e89c294035efdf9321fc505767d82f9dea2247c false +check_signature 86bf131d7533a4474003444240eb56855953f4fd59a0ec95c5f50c4d5981b1bb f444f3856e7998e51d9c52e54f2f3ee02a1400b1de79f19f94e473b3f7ede701 2e7477d908cefb2b39de13781ab0ec680cd4af8311ac73f8ca63efec15347302058a0e9f1e898330d831dc22e400d7af52fafc0d84ae35f11b659e248c7f270d true +check_signature a3fc1d18ecb39f871385e4ded7a5366da6feda494a3e71967182408e9bcecbf5 737d81f1067f8fc41392225712ba219a2c40e0099a98de48fd0936b18702098e 136d6d5c968880f18c754f72aa20210fb89e97c01acf0fe7fd1eb0dffe4fb90b2dd98fc1a18e497c0608f1852f9f80f81c50b22d1356fe1efcadba9144073c07 false +check_signature e0c282ebd3d2d139b8d7332649460c90f1d29ff421794c33e6ea00d200063709 c2cac6f2de132da1559d43e17f108d342d1bcdddacc635bbaa9fa77cfad3bf7d 4ca36dcd8fe8b5a01568b9438f59e2b06feef328a4e4c592562479f001d0e92f84d1302146acffc4acd5b72bb83980d9ecb0f230878092e13bfa26f8c663e907 false +check_signature 7163ce32f6501adf02f7d225419c016ab1fb79476dbbb901eed6342719774050 2af3f4f5215cd483da5f340f3754e8d9be7199b03b295623f451789a2d61b0e4 f1a87db84aa7dee63fc67e06a9cf7ea32a7fc9642064ada14424b5f653cb65578caa124b9ff9b67c6490dcd21abd5c96cec7ef4f247eea6ada351e48bb3db20f false +check_signature 1204ce7c4e23f84cacd9ccb02f76dfaa6927818f729aa94b80ce166b9917bc53 df2124a5e37e0d23df7bcc660eb293e9982cfc2ee5caaceb149ec73cee970d4a 384c2fffb5ae374eb4a3df68615f672212e60f4a717676f99c55c75005ec770af5f367f79028c2849514277585adbfa7595f6b2c93c4d898033286913f5ccf2d false +check_signature 3d6ff15195ac3700a2a997f5ccbcba05318a1612603f6ca64fcf89d1d601126e f7cc7f5f169719c81a9c5fb2845683ee5af1fd7f8e329fedcd229f6045eeecf1 44495bdfa5be197c4c4ba49c4fbe78f04b4f1ba07c39d58be37bb0129524000986024357238e810151d1b4db3a65cddec206a0ee22e42918287cc9389e32cb03 false +check_signature 76a05c54344eb5f19de18400e5aaf0e94986ab5b1c51541600f11178bd110b97 845a4494be35f7c906a5bd7ea47f6a7dc62b308964aeea1363c317cf5ea082b7 9522c6fe49dd1b31a483f8c673cb681895d27e1dddc996f74dcc58a34c00e43701f49e800bac411053a2156be081c123dd3ad84530c68f709c814af932c1667e false +check_signature a2d086bc44f915a006e5aceb9525dc2ecf5e53adc1c5f72f5dd00d7c69a18481 7f64aa391f5affb65a282225de86ac32e9d1aca6f96772a96ccd38b9b8ce5a23 9dc04deb07a6bcb5d17e783e5256766796f241b925e3cc56c085990bb50ecc1a0f07d405ce1b0a0d2b42d1f27682df5246db014b99191dd5e6b3c0b396545f26 false +check_signature fc79665f4dbb32459646b4c547de6f0968df3ea1d21ce1598372244b671eb9fb d3d4a0c35662259d3c57fe987e3cd07a240983d630338c239a186d8e4bf6e0ca 313660a308249db30d2cafa0aa57c4ee432bd11aec4db43b772ae2afd1c5b30d1822d7fb48202aa8420203bc121600cb6f6f4e747ba1c9e0414218316434364f false +check_signature d0cfcf1f958dcc587fcb7f59b8abc30647bb56fae53b27c42d0b7f1ba3cdf510 eb9535b5ddb718c1db620e373fde8a30d404b6a0f7f4644c773d4b631273cc85 09b5ebff09403a927fb28c8d792c10fe042b6f1f77fc0e7dee520061e948ae0f9abacb32fe94a324c9dfc9dc8177116a1a14976c7f0f8bec891c36a5a9ca6204 false +check_signature 2b49eb3f95bc5a338aa354996eaa52ddba26730e09f2b051a4f58224daed0745 208d0ca8d9f5590cc893625d81dfe738821979d66f27609efd106b29c069dc2a 3858aa94412aeb4bd17728d339b5bdff6a808afbfe60aa3c9cf2699632747b72186595594bce65041f989af66a5eb2cc513c46c7452a639e3aaa485d941f9b1e false +check_signature 67fe61772affc1aa6990781ff849c2b4405f5edf89fe2ece6fed9038a599213f ae648150db44f802f465157bd0d11e81c074e974fbf4f41b3429f1c42e3e2716 3f1eb025cdc2e79877a8a6bb739d8328552cd98ae0168909eecf91f1f4680c0502106b3b1b57afbd68b8fe4ed84860eeae4e236dc1169059f324b64244e6fb0a false +check_signature 3d9e9df0b726d8ba561d9fb3d06013d3b726c6778c333a539961dec39353ab15 86a184ea237118e5e37020aa85845401850391b4107ff109faca1f1b2f817392 40f2ed933379123dbd7412912b0cff2c3e2ddfe5b4aa30ac885ce12cad2a626d9d2cc46e037b6e44dd240839b696958ad0a9ad6ba0fbd9b49adf35dfae3ee601 false +check_signature abf8003969e0531c874c347d6a675d6229191f9398fdc9835eb9d742fc297a88 08e58393063c418d0f9064f5bb4f2224f6435fad2fb2f87d1f97da08ef002c14 9426d7b92f2184fbf24ac98ad7002ad5aa120802d1bf7807ffaaf7bd0623d869267b763f440a24fb35c4fad67cdf3eb01f11d1bcef229ae7f8c6f14ebd4f53b0 false +check_signature 190700f6d30915cfe789e36b9579c571d3170bc2bceca93f85b0ec89ee64d221 94c156284ec3afa03587736e8dcd43c44d5b531ed4206bbb455856feae7ee5e1 90a2e51c7398fbcf071ae38ab318b01c9aa8bb9c086940c30732e74669d2280ec0e90cb288e66cfaef6432b759c50efce885097d1752252b479dac5ed822fa0c false +check_signature 2bac263b092eef4a4d21c6bf9f4c7b3bfea30f1fbe184692ca261cf88dee9a46 b983703d72100cf6a434aeb8b42d42a2f8d8d03267c57afc0ff969f6923bf031 2d0d76574836af6e7166e4e8c7142ec95ee592f45af7580f2c9cd5189bd7a46c1e800ae6d97b960f75d9d193d787d64628a518a87303aec2b97fb710b0776586 false +check_signature b37d3d4a1066489ab214afeaa3aceade00aa02f8f697354697eb150c06225a42 273f182de13a2bf1444624a9886c93f275fa8a3baa099a9e9263835dc4a65cf9 e0e00b7920ded2f47730eaa549c42a8ec92bab3e54f389d7c847bf59d39bc500dcb8d0c245155ba3192e38ca5816f006f3f7edd56ee4dfa150d69931eb9d7b2c false +check_signature 5c03a5dfb33f9c0f7fa85d1f94420f5feace501d5b519ec5402d55fd31804e4c ef4626b8d8bd1dc8541532e46edbfade337cb9a3043eeec657dc9072f1d8f932 ad2aaefaf5f314296e4327964a78c41d2d2612d9280a27d5bbd2e8fe28aa203dfde5466b8e5c1886d7dfbd542b70e6457ab4a58de0c11b74a44b41d59f283cda false +check_signature 9be3bd656924ab6756a6dd5dcd29725d642e51c236ea988f3afdad54fb4f8147 284aa799a49f7772fd0416859c1be41f4671aa14dded0dd85c5c5d4bd4509613 4fec639c130f8ce61fd766df5acdd28af6c80bb41d9dbb87fb6509595669c01a1fc3859e28d9f5eb9197ebdfdf492699f868b832bf0572cddbd50f0bd8feca24 false +check_signature 754890729c741562f04f3569365ca87a4379fa2d1226a472195acc1d3c4c2ea8 e09f343a16fac0df0226594ac401cb5d22beb921940b6c4a904dff649806fbe4 01d88643a3ad485a79b9b56cc08685f04bd898568f98784396d9e4c8b5b4760d78b8238857458d9ee720edaf7bd996a89255ba0366cc9adcda6c6163e92a8484 false +check_signature 378bb15cd883b13917ebde02d04e5dce2f1e0872a23dca1feb1f5d9aadf555d1 090bcd8b10e1683c7b6362acd02b9c1f97cd0fb77fce93cfe56dedf8d3a78424 2430021c51eb713f02f0452b506de3f61800336d11ea65d222346825d439859ecd0ed28b2d230621ac63fa4870c845d9ea57a26629f55062ddeea1e63b561406 false +check_signature 123c13ca007dee1bb344b7eae0c2780e44a6103f9434ea2c33f2aefac21b6277 ad064e5c7d73b84420866433065186bd6401863399003369e44555347a65862d 5a3a0a7504d21efc6caf840478a0ea866271c18c8e4ab533bb67567c894e31029c3fefbc3daaa1ee0a6428e4dbf70ad2c18f2e6909166779c47158d4fb40e80d true +check_signature a96686580aa19931e10fef55b475c9c4a633434c419fecb5ffbdadd58ff49fce 704edcd3ce3e8d73e41896a6ed039486b389e59e31e8f1cd1517f21dd119bf02 cd501c9530397c9cab1a95c64fb723560bc7e257781942f6bc8af8eaf38fed0d3504529677489b367d8118d960d2bb61b18c7cce67e5012235470148ddd43301 false +check_signature af3df897ad942cefd52d4169229e23782491ed2ba155a04356ce96b6e5230c5c c189728075eea7ccfc67f32ad81b5cc81f702fe0d836e48dc92ce49e8d80f764 815c6505f4fa2114f5a65920bd425fd3c7574626d16eec61773ff1f220f1bbb9c74f5894ecd598ac880cf65b1e906264e02e00ccca12f0256f1b762a14e2ee0e false +check_signature d11013b7be4967a04f828e4b61e8c23f8ab3279f392a1d9d9d6822bd457715f4 1047ffe453a9ad95d48914d0e3c4b50161dd31084452d085697ddd6b4b4f7adc 8e71274970ec3599979aa3e84f116b6a7ebb6b0e4db8562924202a7f5a480c02dd3b8616d1497add70c3a5e605b840465ea53e4d8899498ad6b47c629d6c2b0d true +check_signature bae4268911a81ad0f5d4c63977b1b1a27009d6208a05cd7236b52a5becd243b5 a3bbf4fc8886736fbf75d8410ffa45730c955987e7a1576390f628b9a34acfb8 1f094cbeaedf29ee66f9bbf55fd8ac6664fc24fa7e439e8c6090e3681db6210d73cef9cea9a4af5dea2d37d2e0dc7b8174f5ae0ad16eaf99b0dcbe51bcbe879f false +check_signature 6055e8bd9191eaa17173663ae5f1eea5b322400b7e89bfd8872b86e626b083e9 00a16e5356bb669a5f442b436eda1b65408ea7580e6b8aac0a9b15b5c554406a 64b5388b799c810a2c27bd05257fd2d6a95e6e2a1b4a917a87d23eb4a0cb8d0508828f0cb2a4059c47289680f15060acaccd465b540a0cd7172a48ed6dca3274 false +check_signature 62cd952a9c9007357ff3f0ca8336f8bca0ee84b3c646526fb8787d26dcd9b7fe 57fc82d882fe39f2ea49f072670df12a9f1576b4985eb735156fb0ba212067f2 d9e4d953d049e969fec5628b21c0e344a9e89b0c4d2c81de4ae33ae7e40f350dc7d0266fa59606a4a08a613aa77cdeee991fa8e3f5916d5bc2761cc94ddbdc0d false +check_signature 0e47d94b68360501857b63f4901607b7271e5543112a2e57195cecc596759e36 462319692247b174828bfa616a40b2242fb119a56989b59b3bf123b839f44aa7 f8594d882a41ecf9f171331c5517a0b676b35dbc73fbf1715c7c8e26790e710675c474e054343c624b33cc806c2eee6170eb8151442daebf458fe2c7b0494507 true +check_signature 7dce93c11527099ac50c000e8dd119ed50ba26508bee195c8f48364d77c045d2 98fac7edc600a738a3bd87b055e1ce941af2d4a86b0d08c54824d0b5297aca02 b67108b12167d498f290c493be78591ebb99e9fe29eb1d48a260f140e5ca0af5936f4bce5e927d405e2f8768b99fc7a9721c91141435e37518093717c50f8100 false +check_signature ab4d84b598c743d837e5f9c4379970093c9594de4866b4cd3297983acc47289b 7cb5185561c96dc2f67e18cde718ea8ce67d047b2dbbe3161b9dc2da70842e61 5624f66082f8ecfddadec78aec00648344144e2fb5fcb89e4e74bd36de5d8709701fb3ac9f75ff9d2d42630331578cdc12bb698a3f99ee78a333534860eed606 true +check_signature 420002e98a13ff42f3ea6d874674d682f6836d7790a181e87b8c623ab940a586 3721f8284cd5cc487c865f49186ce136bf9078689a76d8f1047cd228edb20384 cc02bffc6bd66b665d4e49c0514fa5c3131453e2a43477561ac5be97ede9fa48880a4699911cba4f0fa830cf1922d337065ab36d6a10be0694b55d80acd5b405 false +check_signature 305cd95c8f4b190812d0ff29b4f84c12ff582c72daa7674a835e0eeb5e2200f2 a8ba344492e560d2a83100e02fb5caf2df70a13881edb990d1092233221ca73d 6b10944bd63ebe02d2f91e9abb4401080f73af027e54c9563580130ba48bbd013f26648f841f4020af4f28fde1288e5e0a87504762392cf25e9b8e9cfdb2cf05 true +check_signature ae6cb5f9dc71f0c068645728dfc6b9358dfb426493238ba38f24a2f46a3e89ed 89550654e1586960a830ff1ff645eba078b49ec7cc48454c39805ab3a40ad299 369213e608b19672415e3327ebb27692f08ec7f14a610b644a9e54d28211040dcfa8df064550ddb8745d782c8392c17df4f5a8e8f0d45ca5627a9e4d2189f507 true +check_signature 2f8cd05056bc9190cccad4546acdf9f56ec71b177362df29de378df6fa94111e 30551687a822b23b5014f370996719e9bb8a7325f9e6c40736447a7b6cb26287 405496679ac45c4e45a9d2819d5940243fd77103193f5564a0e48acd1234b00d8fc2b68edb5a395d450b2f09e558185113e7cfe19f958cf0b91465e86e6a9909 false +check_signature c81fc635e98744afa4b6d8a3b0f93e01fb1c9e1e770447b8ec7dd3de2f443dc6 9f3b35a134ee1a2992874fe29d393da853808039c7066f28396195a30b5150eb 4814aaabec386a9a523fc6c739f7036b264cc9749959fc4a4549cf02552d5706b5e164836b97007860a2b5bdc1aac88a442162f10a97cc1f31fbcf0a5fc01304 true +check_signature 522795ffbefd3b04d8ed93ec86e484d122fdda6bd7d6359437450f167382ea79 8f38d9797f3748e289b3a72e02dcdf3fb9f309ba908e47c71e0efedce3be74d5 72910eebf1eb62eecc98a0581ec8a50805d3b57757e64b5d88c8756f11ba8b61da38a96cac338892d88994345d264de9a04442b3cd6a4a301dc6f5ff5c875e19 false +check_signature 9093e21ec9e7d94581fbde9c18f226638a75ac5b306dbb841bdaa83ae79740b2 0e5f4ead94e24f6662962f7ba7a29c07e7c22234d35378b585e9a44c40bb3542 afa215e7a4aed52bbd60dff3ae5dddc6d567a6067d00306a6a99ffdf0c49c50a548c16c827fffa8be6ff7aefa974ef052f745478f3959e55f2adc717032f0903 true +check_signature 1f215f8bad3900758d2cb7c70e4fdf9dd10b899ffc247f30a1122b23d8bd4969 4d24770cd6dd542b0f7cf195b1d4e43b694539ad5058e93c694ce4392f56334c a75d88dbe55bb7323089842c7b70d38efd6ab28e2a3e8bdc4a37f3b22776c1fdc6e3ed1613f205fe64f2cd0df7151badf52a60e133e6d0f3c41b24ea2ad92e83 false +check_signature adb586a8e30f2a8f4db4ecce2690f003e6c0eb0f9025ee5dedf276328b5302e1 2342f047ebd3733f455e62ef287e5c920936d866924c6cb8f9390f8e5d79e9e0 63ab710ee142844367d518ebda4282d55cfbaaf0144fbef4c1be281ed3433c01ae81b5af82cc4e9dd273c6494eec2751f647fce2b9ec1a7c1dcc17120387be49 false +check_signature 34613d1c0d6cd801f07c4657448d56d7caff2034de1a13b667393fd82dcffa7f d055a14dc1a7015ee72dc9acc987ae2136193b5b57ef660ff36b7fc56081ceca cb81e94576b07307fdcd3e935a96e465ab84558be7a52c85af0f44a62392bb29f70a9281a544eb286be4a3c1046383023578fbc62e0124d70400e71c153a0c05 false +check_signature e14e796e47ff728ca84000bf8d2e1cf72aa08ef9b5af8d8f1c54cd3441ea0f52 6fd6885c2bbfbcce27f42a414fb9b5ae949fb7aee101c8f06edbcde303471f30 6ddf398af5cba63f7730d4562f11d420c5ee646300202d3be8d1f4907d0c0606ad54ea1c109c9a1c134a444b4ebc86c7692eb48124c67b56f7d89f8edb22fe7a false +check_signature 5b63c5a0ad92fc0e8cb026642806b3792369c2446d2a9f732d4aa11660d04eb5 9b6d0ed904b878ed5d6b00ff08e114b9cccc2631873958f477b8320b1663c964 b23560d05852677e01687912b42562cf091a2a4d7664faf3c32dc722f1b46406387ce8a35b7e493994c73dba64178bfa830192892908d4382a99625f595e390d false +check_signature 3dfdc686a848ef9f6dc27250aac2eb66317440a6f37e605ec9a5539f7ce88804 22818a16ffc61d88f0d61cea4ee368ba3b1fa0102739cd917fc10b715e217116 2405782787388efa4295f2ea4145bdc87299889596f6e8615896427aca142d06d841e3710c0d86d817e5d72a18e3e2f4e8ca2f74fac43edef5b784c412dd320f false +check_signature 7760db9f5339acb6be2b1e55946d140348ca693d9d48d8bd31f0afd7e6952a12 fd18e50da50e55d2625c3f13233bfd2939dfef851682bce5f17db4c3a8d2c2f4 247bbe65c22be3b00ecfa237eb2ca37e5d91e96543dd1edaef98120e57cdb945938c11dce526721fcc2e8a4851e23ac39c0159a20cb8a143f52d79cc216c0b0a false +check_signature d921d7301dd13a7b2b71f9dcde8f32cd3bb15c4e8e91d3d67b31f58c19141e3f ea3876b725243b7c55dcdd18e9e460942587901e6cf3224c638fc4c09fbb6bfd 288870c413d6964c0beff602d8d441868ec92a06e2b3d9300080d57a96e721024be2847a4387f2413e0deec7f2f724b2446f6be739d0107fdc55bf799f48cd04 true +check_signature db567a14edf5ed85dd81e8e72bd10715902c514b283b978ee8236e8f9b868170 3a4306fa31abede572b217246be244bba99a4232ba25b2f97443faf8b8007db6 e730fa88c1f1a8746d9508b3606f7218857fad7056c3514814efb634f093d80f99ea427795a0378746692d404079611cbf1b5d341931aa737e217ba71eeabb0d true +check_signature ab9bee77147da7dbb8160b03cfb779f2121e48d4e320487e7d3426c39cf10f4c 6ce2406038dde8cc963d4ff4a0e397cc3ffb2efb257989f832c7832fca062570 c5c8f0fe2d0cbf16cb52dcc715e644204ae72551b866f4a400731044c643b30183638fdd5dd669abceccb9deae2a6faf6afe3de0be6ae916b4960cb4c73fb70a false +check_signature 8a3a4011cfd3e9ee40ae9a33359c0c0246d70bdc57b5d47559acda110623e91c 181ae06d05e8c1aa060969f84d0d93225429910e538c1d41199666affdd443d1 168b39d6f430d07c480ad35525ef4fa5ceb653dbe672c45b1317ca3dd90b350bef0d3ba60c1a32b2337606a0ebae8c366a4149702ad646980a1cc271c1ee9e02 false +check_signature 0567ab3b4a86fa3f01cfe9be18bc4dcb541d33d9aadb949346e3c78d065a0f52 4cac0251e5622fc08172d7e570fe3c2acb499f292be68f9df8f7933bb575dab2 1222f87ad3849d86bcc1c730cf9e15d927b3658afa6a44f1c92a94279b0c740a27a35b96027c6dfeac6ef338de91e21e0df7cf946fa10a8d596f6cf930c31895 false +check_signature a42266b69e3c3766fe297cb89a8c59fd0ebdb8635903c3933731b717fc9b3182 21380394c7fd5e956b2439fbf9a257c081dd287c3b591eaeb05fc77ec8c6b63a 3aa2e5979cb6b60ba927afdfc96d058719d8356334a6a40004446d96dd8c200d079ed2b41807a8d8dd8039e26a778ce915b7bda5324c74b3234b263daeae113b false +check_signature 50cfc52078fb1f6265ed9efc984b93ce5086f662ccc4fa333472fc010e675920 a252b248ce159c82e2c349d37f9650f68fe549be415ae92d95abc1cadc26f0fe e6f727a934b448ced5e2b6f8a5408f50bf791b0fe5141fd4ece514d82a37bb0ed3f4ea981b7a2e608cc2d5dad662b00f2efaf61d9fb8982f3f7414ed07f95c01 true +check_signature e69b21c51c46e18dec6ff3863d059d2f88b38c3c990ad0090b2b5720267424df 1dcdce27e6b94287ea9d8754e1dffa6adc937b34c3cd6aa366ef311baa571536 ad2fb6c634ba3247ef90c33e3934ee90b7a53162a96039f1a666163e31f0b003a2e84cdccd706283d1f3b98ddd4d746f3c6cc189ea874c5d8e32534c15cca203 true +check_signature c988218664f470c80ee440e3324d67ec1970a5672901800975c2a8b285b32c84 e34eef02b97d92647ddf273ff771298dcd9dd8f2d6097e19655061c867128f6c 5020e26707104e1c2d85c9a90ad623c68a148a8ecf159f678322d6b1c603295977983394f3d950f7af5d2fb121bf0ae1c74d9b21e5b6650135556e6635ee935f false +check_signature a503c907288dc879226f406ac4b0eee21d0cc37cb272f711f0ee68299d52988f 812c8c2a84fb4301d198f05f9d7aeb35575055cc70c41b2f66624fc3825fcdfe e135b33f2f983513b510b31aa02637308011de470ab9a5e053f80cc76a7ce2093309df1d2ed1c47ab8d3bb1f2c9733e36fa4d66bf81366189951d3dcf2baf90b true +check_signature f0388f10821895796b369bce128264865c59a812d405e2943e13c37677c9523d 734b6a7f6181ae7265956b67bfd7ac0f7568bfdfc65949222f3b4812097798f8 3ba99a4f116f0b6bc06b879785693a30b829ec6d8cb3b824e4bcdb498e8132001cff5118de4094a75c6e67fb9424cba7188f6e30bac66fbd9548df5246f45e0b true +check_signature cf2e777311f0035a40c1d3949239836fb61d040d8a6b2c47564fad4ed8203db4 9b4cc97a0712cf2652f36f259f681d9936c596a62ccdad6d2a31d6147f079cdf 20c4aeb95eebba7084b456d2638e77859d192f61c52ec6fa0527980891923383b110faaad13b051ba04a5ea2defab062849e15c375426016dad4e4e638381f0b false +check_signature 0c4bad01df211c2e0f4f6d4aab250efa5e79042b8876bb160b2c5de8dea5b9d8 f182bd37ff8148f843d1b53240589cf33bcdcd18470bee208047ae84f6e2495e a927f34b08fe4a856d56ff0befd542138bfc7a4a931f7d01e961b8c903cb68ab689ae3f2d5dca0cbe80d78a53e3fea2bb42e88d8a34ee0eb12b6f43c1e57dd05 false +check_signature c7ebd2ce9bc568614af853ffdac9e79242d6045ab8045896b20845fd5c86ec9b b8860b9a00fa576ed0ff359d3ec91ba993e05d65b95c883b64d27e328fe89f5a 9f73f48e9c0f213e0482bcbf9767833d448bacd143466b05caed56d649b59a01fda1e8ccb78a009c46210962af1a6f4bef85767c87cd64757dd88d911fdfac97 false +check_signature 4fe50b42ab58f6795a47b73c802a54b3dabd728bc49d6ea3f00209e610022959 42c6b0c32b5cc4db19bfb63dd0b88732f9dc1b3ce665bdf6757b9ab77e009bb7 0935c139f3952197ed6179240f2f59ff0bf2d23e149f4c2e2ae20bd42c23cc0505e156a7b89f9114a16f1ffc8797d3cd949242fea15740d5b518bedb41f2c10d true +check_signature de5fd05b4166b9bf50e2b87e5471f557d34dfa70e1aa9700665f604bce2ce467 d0c3fa7c1adbf4d5976929332d8f191605e534b1a8706bc74734277244b60979 8ac1fbfcc32b514019d09d55bdda772b4024d0e395cfd47a6d003f2e35fe063787a318ff53600b9f6e1699545e3010175bc6d342bdaccdac9a5feb18d9078749 false +check_signature c421d9ea8ab5c81590736b8c3f1e5a03219270a8828cc2e770cf3ace8162630c 5eb2124424dc5347364379523be05d1b9cc8d7550a037c76716bd5ba191b4504 830536699a64771bd2dd82e472db5621b1a50631487d8827b0a93cb8d753fbde88311764771befa9d80c2c699833d152cda5547a139f40fb0f83d1626d75b129 false +check_signature 9b602af2f61b20851707b37caefdc85575ef0f3ce1b1a04d2f9afb1fc15f5ff8 c138f169656df2f7daf68c9a8e44fc6c2e30047ac422fc83ab6fd6cb10b3c11c afa42252119b8ed58ed3799f919319b489ed6fbd1becf04ad6efe0b473978e4793d6159e214d385e19094d36916e86d9150e224ffb8f20155cef4e35c518c701 false +check_signature fc8bdd7d369458b65a9fd1c8865188e5ddc3701d57bed88b2a70a0d46c0175b6 b2d0c0ecbc94a90a46676beddac491f795a3088062277b7368d5d67ff1aa0ed7 7e5fbfb564abbba01d76336fb6c2587b77ec0aecc2439aa1d01bd4614f94d80031a9377e74807abff6b28d260ed1142dffa36de78562602e9a19c8826e4dd734 false +check_signature 79132e1bdda8d3c757021630a68ce002dce2ef4bad2adb06e3f9153ee6bcfe35 beda4795ddd43c94f69ed2585c6ba8ee1d80c75954763d7f5b428313fd614954 b6f031d802d86fd01c83550d59867149e00fa2a6f228782008641c39852826083c691f16e91203e7aac0b04588ea0d6a6abee4639d80ea6510bf905c0a427904 true +hash_to_point 458c26bb949f16e7f9d03f27abd55343277bc2d541bdb75087a9e2593575b380 7f4ff48aa1739c8c5eec93ca9dceee5af250e0cedde088865e210d16742b53dd +hash_to_point 61df2854ccafdb9206b3dd30ed5ef7298023f2937c411ed724ef5f5779bba71f 57e0d5410b92a48969b5c9aa5d50b42018ca82fe37a8d28b3c42a24a509574a9 +hash_to_point ee163bf9e95a7093d6711a7e940253cda74049f7fe47d3c3a9f7294cb29fae11 b03f116fae363dd009fd819259eb4f1c66f8a41eb7d527695d1fe5ebf363e696 +hash_to_point a2ae114bbed775f488cc88474c2dfdd0b7871d18c633b2908eb654404ae55c81 36b0104c0bb7b049e36149f41353fa872b0680a1178533283327987dc6156e7b +hash_to_point 17789abca254c54d014c38e849619f61f630b21a3f05d552ac94dd3a46fee1d0 b7096e31af8e5064443565fc46abd15c726eb6599cd5c4fa2fa2e75bf3962c09 +hash_to_point 2b120548ca69d7e489840403648a96740a79242e9e22747d813fc6da1dedc08c b53f228967d75e040d86ea0847720feac4b761116e91f0262309a50fb6a79814 +hash_to_point 45f4b1019dca20292c134aac30e33e7260217834af6641789a7e942e85fe0174 5045f74a53c1fe44b5c1947aa04155626912fb9045eb91339995a993159bda9c +hash_to_point 1710222918f023ef04066194e0266e5f62ea461bee3917d4d296ea05a652ef50 ffb3f2663574907386394f65a79f4e1c75227c89554f14e77847e96d3cd4cc9c +hash_to_point e03f27b357f02d54b35e51e0f1afc24a688d51be7b49e949e825aea8f2ffb688 8c4593ff79e96b62bc7f8479f5181cac49fb83270368bc7479535014e04dcb68 +hash_to_point 540bee8b4f7837457415006fc24a34e4ee4d1e824b158ad72d71ee402d87b392 ce397ed64ea48f689592dc111949b45f94ea7d5bdcbb324ce3b07995afdcaeaa +hash_to_point cc61d9e1cc96b616ffaaaf0425e20ca1262040f86cfd693737583081dcc2f512 509c0d627c93f51ec2be589cf175ce4300f75cbdf78c76db56f789d50b6a39d6 +hash_to_point dd8aaa3055f6015f5ea6b06e6ebe4fdb84ae11920b5b2dbedbe2966908552cb7 3b861b91593f9bede5723ba8951d2e1f6f46bc2f72c292ca5d463b86fa5e7c13 +hash_to_point 65aac0c2cdab5776bb9bb7ab0e3bd08c91363a8adac74021e7cdd0fa63af5e8a 57893d6d96d503332fffbfee1aff9cd9ba3c2d00e871990b9d6304e0f8d96e62 +hash_to_point 05fdd4623d60d1033df8ce0ba8bef00b2e85072285570e835d7bd2af26e1e234 5cd2a879e022bc9811d3b6eb39148960663c1d8415106bb90b28d97a6e870f0a +hash_to_point 1c4b0150969c2a123fd7d4bb8529e023ebb93b1dfab38f524946f3508e62bbd3 689ce8f3bca7e98678dc0b7ffd47d386caff981035263a0b0cb4da5b39455ca8 +hash_to_point 579444381fd1498019be6d23269dbff2d09b151b85aa80864121b85017362aab a2b51aba854fed4ce2826bd42b72ac4ac4989e9e653208562d4f081d454e7c69 +hash_to_point 4150af82acc4fb728ab3811c48716b9a4819877ecc292b718d627d67466a322f 7171ff6c27fc150114172debed4b0f8a85e3823569692810197aaf7b815a2ecf +hash_to_point 36de1ee8b0b69d878c847d605dff7ef03af3f80dbb6b25395e38b63063c1ef65 f6700ddb916a14d8b5d84763a6542fa0a6313753d0f92ade7281360b5ea0718a +hash_to_point 009c67c28e428ad8a1fc894bee0f9c8b2d3bbb6c08651f646ac8d30a4e7c0b74 894de9602b31c83255398062296a872fb29379026edbb3fda159bd1ac348c8bc +hash_to_point 796ec8854345ed13a62596719d3b9d5c73319350cbc6cd8f5d5c98d7d772a945 0e6aaa3ff0567289c27b5dad7e5158366e0efcaa6522525397eee49391460fab +hash_to_point adf61021c002c59e370fe0b552381c9dfcb9890a18cbad2c93cd02637317a5e2 176787440187b82c2a5cc99fd039c9ff7b46bb11f6c0440adacd64069ff7939e +hash_to_point 900ef0ed045f44c3495c30dfceddf655bb5b8470cdf251ffdfc4bf3e3cdff17b cc8bc20bf400a0290f0384f767656b9af5f70e5e62f5d9eb638cb44c3d8210ff +hash_to_point 8d999628acb159c9f7250a10f8af63998ed36def2cc11477d94a4d3a0f072d55 5b599b35989df7d8de340ad1cd4c8cbae7581b49b09f54f85750b567b6b9bdf3 +hash_to_point 15101d770f2a4b7f6e949bdca4436462fcefde8ece9e54b684e1b757d05bc6e8 2befc4bd6fd0b54f6b40afc583a07736782b83f81d6875c3e09ff05bb9ea0b80 +hash_to_point 72cb0fee68580fb8434589ddc30394e1aa03d3669ba7bb7ceab4aa929cec5352 b1892e1e4c598f953c1a2f6fb3f901617dbd846615023f991fbfa7583e38b3e7 +hash_to_point 6498d92e81396419cc9298a0e8c486abcbb177c3f901e51ca1328047633237a0 79bb9b97e03c310da8a8cbf5c716aa8b2419f1bcc35a06722592ac2c84971722 +hash_to_point 9b2704a0074a446a16991f9a82658af5bdd55e2bad3defddbd67af46e6d14d4d 2983712be9fc1ce63a6708ce7c090e14109a3935c70ca6e9350b1e43724b9019 +hash_to_point 67ddca658cfedb10fd424ec7fd7b2ed18cd6381a6e5a1ab5c33a30802d375f13 be2a0e82aa6e87c6cebbbdf173196ae45ffd0e297d003929ff731601e3278271 +hash_to_point cda4f0f88c5986fccdc885d24355afc62926f287cdb255b4b769880599b8bf0c da9836d235bc16a9f3a998052a18c54172d008c858d7d218aeded6dd78f8bd9f +hash_to_point 2202270864b7b65535f95ed8c894b67b19065b25c20accfb495d9e56da90d2cb 8a8a6d563bebc90de9276d604c74bf90a48ce7f5662fb4495f3105fcbfa9451b +hash_to_point 0e34be42e37306f327a0fbe47ff54477d40cb8207848058926d3604800983921 d0bd9e97dfae4e5eeba904f63a86af243381b91a363f7279b8f2a0112157abd6 +hash_to_point 00aaf8cb782334274c0bd9b7c01575e88e69803e811f05b0a2ff4cc70bac77c0 1482cb0b9643a2fefc8c82a458bdb30cd493a0d3344ec57ab0a702773f48c124 +hash_to_point 4d6db9ea5e3d3db17472017a2cefa6a1ded3920b32fb0c47a3198610eb803c83 ab5d6f2692a174268f3f126eae6a8b8edc0427ec6f865befb0c0911a79b86a39 +hash_to_point ce55763c8e079614a9b17d58c85518a2b317d5948920095ee283df4d352a1768 4cb2ffb250452da40a8829de298eb1a172059ce9e3d2bf5c0617e0d4ec116a74 +hash_to_point 2f1b44cd307562976a12d7c92faf7e96df08b5bac8968c6c0a0cd50d94912f24 35a3f16ce5921c59409659dac1e5e36a6f1b8bbb7d133b232d7b39240c1fa5f2 +hash_to_point 106a93032bb4756481b5c254f99dc8d509846f39d7f46325d2c47d42a2a05114 d8b4a84174e6eecb5575aabc1fc7a3f370215ed6b0c75df38af1261b5b8c3e2c +hash_to_point 0f92823861e1f6a2ca723e1d53b538da538f28c66bbf947a6def72276c09c7fc 52bb3311a2ae7ad23a36090a1c9699698b67e4a475b9a613f65d462925ac96a5 +hash_to_point d5cb8ae09667056963588c77f743a43756cbee28519ef0056b5bd087591fffa0 8b930cb1675800a390f4caab7906e89537ec4658e86c0219a9325b8129a90287 +hash_to_point 14b4ffc5f9ee1c0b7b6878205bf1e7068a7d8853918dad863c1f333b1d50f15c 2decdd3bdb4aadbe2d56b91cec6cf838ee915e48a085c3cd28156e2beebc7977 +hash_to_point 657b0dec9dd69ac51e099b626894061c93842827a3da7beaba76b1879ba5cecf cad5c365b59208f76d990984e5e2edd8d2ec206e11ba0b2069ae8f6354977913 +hash_to_point 55f30635b49fffc1ee3b7960d6ebd795a276a12b440f9af6e115f722e449b535 5fd276a7ef67253f3cc4bc1a4ab3675c91608783fb2f1d1c7401ed4d60d33051 +hash_to_point 1469ff891240ea1d5870fe6e0fca85ded557e45d9c9296db535fd84455d81ee4 1182a542d7ad2032d46ebb715041730dc9ab41b51bf36d737942ce64b2ca5d9c +hash_to_point f33ef3c7f45f098dfa41c73cea3e7721a544b2ef7e44865dbce1e7938f223352 e5a6b3f885300d43f65951bc0fcd1cc4f09d7cd560ae7af11f22f6f0d73ac908 +hash_to_point ece4e2ed67e990316c22988de273f0fe3aab28282e4af445ec98f16f1757f64e d8e2f61387ca05b301af8723f5e605088dcdb5ef91c1a0474320345ea1530224 +hash_to_point 255275c0f08ded6d9679b84224cb3a0ebe4826a8f2dba2372c8323418ce97b91 c59bf85fa9ff1dfadaa4c9b7e75caec76cbad6bd2ed0aec49da37e5f60f8432c +hash_to_point e9efb39cc46b20f5d29284d996b5f0ce93d90813e05a4da652898455ba9e2b59 68e35ee2fbdc6b9edbece8a6c78c9b8e6c4b5e0e6010553b323fe7a8de5fd641 +hash_to_point 52d1bb1a05d87afc0abdf29fb865b29b35b6e765ee886ac5a7187aae581185be 3dc9a33f2c4a12106db206fac538e8b7bf9df1f670cae882fbd7c61ea8bb6819 +hash_to_point e36caba6b1cdab884faaf140d8dbe86519f8dbacf795df2d3bd269b8d3510f0f b7ba662d1b89e1fefa8a7be5992a8e941539a1312c8c13dffd18a3ca88afdcaf +hash_to_point 5bb71fc9c5cd049c7d27a16148a8824a923c0caa35e1b09ec2de32c3a030e8e2 398f338a5461d40f48ea5f3fbcf01431eee0e12ef19aeab203983b0e3131bbc4 +hash_to_point b77758ce5e942a797b72af48a0101f06c22b0ef60203730ba449950cd96440ee 5740a50f64d4e2a4a14c28bd73f93d1fcbb443575db3e43a304f2633e88c0044 +hash_to_point 313c817c53b58402a64364f6c048c2720ff91f9e67499b9b5c715119fd4f902d 5c9534f91580257e4ae7c3b6b0b2480e10fbb5514535b28abfb09d65e6f552ec +hash_to_point 9e00d007527dd59a7583dc0abafe59e93a11348f1050f19e4744f717534a9a81 f8dbc3259bdaf8e5b90a83e284642ef902113e79f5a9f9c9f98698e3b5ffa751 +hash_to_point b819cc00e91a636574e86f6a4e5c1034ccd148ca74cdca1bebbffce765b9726e 4a643c97d6adf9afab6587f1e601b862469dc95988251490564d8a457f2df350 +hash_to_point 849fc5d6ae3074c4a3a71c09c2e690fc0f395907b3e50eb43b4ea8a1b09d47a5 f6f0b35d6622d7bff80b2584d3181a698cd48eef27a3ac46e5fd5b5f7eb9c82e +hash_to_point bb71eef458fcf0cd968a80282120ddebe3aa8022ad53c6bac20d0e5224a7cc34 5dc84d5c730572fc514153af8472dad6a327859f5a2b650924f0c94fa7c75651 +hash_to_point 4d926ea18e619eeba58f6bf82467f365e529149d2bf49ef403023dbe5da56446 30754ea3e3613cefcbfbbc8d03c7a4573675dd2f089087be7f8a25f2cde25e59 +hash_to_point f78daf10468170134d78ab8f7ea923ba00adc0ec6e085d1e0b197047dcc6b84e 108673c9f70a47cb59c77b56c5c01bf5d40bc38eddab3d0832376072dfd80573 +hash_to_point 7ecdff22ad0a80b4c4b4f4d15b9bb8c1c4daa6c1c52a757f26c13c3e52a639d7 ffac5e28cb11a8b1ddedd18a57914b294eb6764fc83479e9766d1883abf48bab +hash_to_point 78ec71125db23665675b12afc48b458b8b0b523609a8db3d1422df1d76260458 5a0fdf88d027c1edc0ed6fb3bc7c7fd0d37a0381188a75e6a5b8c8bf8a062f62 +hash_to_point f2f8e604aae52af5cfeed45ca28e4ca15b2b4373172b12d8fccca9732406bc6c 917ff55a3dc9c6c54e1684e7373406fabd969cc348aec0cb72bfdb37ae4ef5ab +hash_to_point c689bdbf66b1d7c5cd609ae75d6ecda868f3a96d3f23841f25698373d941acf4 e18ab46eaeef595742548a2edd55dd746e3c7eddc9e3e3a5b48ce15a8a32e225 +hash_to_point 6b18a764bc6735472f4867f76f61a3c083d6cdf82bbfda4a14eaeb86e6ac228d 40a3c5ec00ae894fb89b0b7d9bca148388a8d64bc8d9c9e629e0e85a3ebb7ccd +hash_to_point 906be64fb317a320796cf2b22f6df6f8cd3c688689f654a08b564caeb8a1ef2a 0182f01290e7acef245868d81da632ab986e0ae33c6022bc2c55ad85512c5ec0 +hash_to_point 1c95627b45275b7804c40b40643dc71605c0702bcc1b1506687f2320586080de 8ee3e3a7f02fbb73ca4e9ccb5cd5ac2d601709f1080752d95ae1085a1ba1d1cc +hash_to_point b48c51f3d066e81eb9e5e190e7d027c9d09d60acc34d38dcf090822f74f14183 8d6b93999a35db217e6b041b399799af34043f16c85327d8932c9fc97cfa3fd8 +hash_to_point ccb8ec443e175f59f796f53e47c8911ddbd5fdb550dff8bbd3f265d2c4d70c8d 90ebc76f4bb863d9b298f643857b34fd65cc327cd087c1ee988440f7f8c29ea5 +hash_to_point 442b3eda114e1f53ecd681d6c01a6755424865d8756152a42ea3da56342886d4 e1ccb830f96a685f704732930375c070e22d73ce771d701c58488211efc66d2d +hash_to_point 68f5d747662e61957e439eaf69fde548ec57180428b87cfa17c0c65c8df10b0a 7fb22c7396cbd9c2d63d81f6fe80bf5417ca0eb1a7544e1139975b5ee616a508 +hash_to_point 7041494860f317c0ed29584319c87790fa5dd23e1b46a76657f51db9a11b0b97 7da377c9c610ee2c2dc006fb2ba7909fc55bb74b2a2da6dcd25e455bb1f6bfb7 +hash_to_point 60cec8336cc79fe8ba321a42b34c0d8f5a7e5900ca6216482766803690ad4804 a9f070c0cb9ef51649130d1190b1db091ab6eef0b1a6d0fdaac8004b2cd65996 +hash_to_point acec0d704d3739a14dd251213ab5d369f1949a34036a83364b2a5da263ebed3e a63f3c0a6c906dccaf2bd726898d23c196412b40ba8c6d6b100d1980f81bc0a9 +hash_to_point e871b4ca88be7204f457343405b9e2ebe396be60c337f1b484cfb7e5ee0ce037 e13c2099e98adda5433c6b62968f77c56b4168adccc3c3e67d23086d9cc68c64 +hash_to_point fe476b3542452345e2c646eeb81ed9c4211af1ef8542d72f8891b51c1e46dcf1 f4e91343fc4a37af1e63a023d51d31259527534ba7b0f6efbdacdfb065baf5e4 +hash_to_point d303fdb98aeb24a423c1378ae9c8d96f4a519040b0749f465493d3cd60f7c6a5 624a0bfed9ec64ae485885978fbcbc3d3e7c4cae3d1ef793f82815eae5f4a8e1 +hash_to_point 96df68464e293766bdea144bb27370d0b5156a41c79def4d465a9e4ee2b28ee0 279a8b5c36e34ea620e108050454c84e5eb0674cf8d25014b87f83a4d630b273 +hash_to_point 783642d2e89041c2ef7bb9e7e278887e59c8b7d371179b1477d8514ac505bd79 e42a7fd1e5920ebd9614fe4e382084d3af2dc74e73927bea86931ac781776887 +hash_to_point e27838c69488d4202f0d3537a11dcddf4411fcf00870aa35281ba0d50d6f59bb 01baf2e5d6c4b12e2b2f2bc4098ff66f820a200d370b6c0d2c553db157da313b +hash_to_point 0b9e799ae1e9dab4d411281d878c895845a0919ebef67023db0013c178676634 08a494e9f816f73f44867ccaa622775222abef048f073bc89da7184a77b329e8 +hash_to_point cb938d989703e355d2fbf06d77282ab74c88ec3b0c20822bb1a1c9ef96170064 e72647c8484c331fa04cbf8a7e5ebd4806179975c6ee19ba05cc83318db11ffb +hash_to_point fcdb0916f2f6d08ab5d1b77459c87682450e3cf0d08f5ce3fa5a1f388ed578d6 71b7b73b3ee500ed97b4945064f55f546ac841bfa93cba1e27fe02379adb359b +hash_to_point 11cacac7654884a937006d4a654442a6f0668c23e31c89b0afb57993e66e751b 73d3b1339e96235419262c67c33b81781390dee89c2331b0268fe249007c6b67 +hash_to_point fbe24c1fb84a888583cd0437a204ad9409af867ede5e74effa1a5d46008701c3 e3e53e98c15a7fb75379ca0d5edc2c4d2f70f24290f0d083847c42f2296cc94f +hash_to_point d81fe5cbc2460b1f256d6ced42c5fbc1bf263f8da75596013d9937b150cb1452 6d79c7b802f3cbc4032abbc4319a9abfb54bbc5761bad6527d2fac76b60471df +hash_to_point 640ac19edc8dc0c1dd470663e85b2424cf79a43d8b383d8d6ac68ba28276104a 900a5688e9e5773f85ee8b6f3e281a76b26ee156a556ba6af754fda2d4e68b16 +hash_to_point 39046ab917b49012d3ed17e09a3227a8a0f8461533b83af8a149a5d566cf3811 16ebb4a242dad3113248f80fef0110a38eec28046be4ed2dadafe769f0073095 +hash_to_point 1226196af09995d834d1801d36e27534a61430e2a4f33704ad073f6ead2cb28c d0dadfd64710582bcd4be8915c175189d8264bbe2de1af664406d26432065c38 +hash_to_point c31b73923d25959aafb71cf2e76f886e41c64a499315a5c522c333460f8ba99d 79683b9799c9265ce9c8c85604f2b0748b8386605d10d520c5ccb3ffd2c63ae2 +hash_to_point 12ef719bf8dde53c9797e7717a72935ef525c4733738e6ad6a499b208da88ec1 0738c9e5b4aa655538170765777cfe4b1e3f02853cedc8b6220bd7e569f2f6b8 +hash_to_point 6b92e29f2d8c006d6d804896d9e1d3ea227ff5b42df07df7ef3e314ce99a605b 749734bbd5bd13cbeb06ac0e1e8649f875e498f43409ad73b31a3f2d499535a5 +hash_to_point 6b8024dc4bcab177338f89a019af41c8579fe83964ad6e041f65376a59a8ff28 7fbc190363f169de4e7dd7d40b4542863bd10b77615876bc82cb168fc4407d8c +hash_to_point 6c7fa1e075608aa03fb7c43618e2dee31f6d6af129309f50495a31cca46da5c9 aa975d48fa808ec55c838cfb63ed7d21ec9bfe3b94d9d15c0979994bb13d457f +hash_to_point ffb7f9dbcd06d2cd1d0557e21e09d62acff330b5703e49314a529024f70a4a89 1956c23b1ecf7f1bdfb0c2857fcbccee5072d0be3e1c9799a9cf64b4e52408b5 +hash_to_point 9025258d6d7b43ce6f682a6d968379ed97dedd7531d3af629a544e46f9635c6c 3eae75c9a9f977c49d8313835fea504fdf21f96c1018c897c85ace68449cc748 +hash_to_point 6a620cc76d67f9f08767fe214085f92dac1d120787a3dbb9e12be8d7470615ce df6d4a6bcd2e614405dfca9353beb9eb17ebd724d85454d121e9e97b40f36368 +hash_to_point 9a0eecc84bf85de99481f204761af8464561a40aaaac8c623fb16fd26c9ec7fd f466e79971ee24e342b51e8eae918086f0b37f12857fbcd918d6d6d7f6a6b2f6 +hash_to_point 6b7268c8e1f34d1da623a32be9461919c2bd09146b35039ae8a9f4b25535e038 8268320ae0b8914226f800b6f2e8a4db539c0d8c16114934d4f6d214df7e59bb +hash_to_point 161c20c8bf92f6351433033ac4c8be2b5f4fbf082f1c1ffdb5c8713aed78da3f 5659350377d5c6096a56a96e2bf5f8d011052b9e064e8d5238facdc60b92c16a +hash_to_point 3248f91e0284b26daf6aeaaac6577d8fe8cdc1c7ab27cfba7d98974f700d9077 f744f9e037266dc1b974a739800036f8cf5a75b11a036b93e704b6faaca7b5e9 +hash_to_point 9c2db36a294feb65b0ce128958d3165a74e3bdf5b7e68f868329277b4e54cf3b 2542e2644e142ed10a74a4dd41c2714ec65d039c8ff97a199c2cdfb977df8b9c +hash_to_point 5a3ed57ca753a7a76fd48770db9166f26c24ca7d19f1e2b129b1021fab867290 e242ed9637b663459b106e62427d2f12a2678da9629fef93762fbb66a64b080d +hash_to_point 6ab59d6fbd8cbb8866424dabdd92cd97bfb84b387d303eef320e3623598f04e1 23555f1eb8b61b7b9955bbfb451f48542c8dd70316daccaa03f36e50794f1053 +hash_to_point 7c47b97f509dca7714739b0e233fe3b9555fc1342a8390800b2458380042ee0d d827cec581bd17e46ea1b686afcb5f3f5071fa92a368df40ddb300360e229877 +hash_to_point 4dcd70d82fb4e3a1567136794be9a318a828185a0db06d890c84534cdcf9c40d 783f239e3dd2f14bba85c7b2a536057d19a8b2944544963600a1a90611bc3b84 +hash_to_point b0924f17bcd3ac8cf363cd43b90aa6763205e780a80bd4e4564b35d892032d17 e0845ae0b52a9f72d6562f0ff2df333332ff1544b3c25155dda7adfa845f5fdc +hash_to_point 848c84bfe6c9df1e1fe1a8095653951cd7a26b290cebbd242e7e1e95d6c35075 8ff08dc91575e6a19b81036c880e96e8495f367858ce477a6cb68eb53983f08b +hash_to_point 7a29553e9faf4ce985cab7799b1040c23047db7c2aac5ea86c98c30603e76611 01e4542fc2584d051d90e8c56f03151eb61ceff0ae580f246ae1079061aadd09 +hash_to_point ce726c09ff313486027dfe8e936871cd61c2c1b037bf4c2b42567c129aee7e0a 9032e1a3d9525c3933156e06f14538902be5aa9809f74a44cce38f1a46eb909c +hash_to_point 5f7dbebf6b2aeba8edf2233171e1e58833ab044925c3327927e71c8d20f01ddb 7750f3bd8465a0dbd8b8736bf020b7b5fc795eeb6123ef9bb9474a169d225730 +hash_to_point 04eb6a5799e8b2a0e2212e479c016db674e92c0d754b8f924ab8f2bf93acc19f 85f969502c4307d4d26a0c5262d9d4b9e8da7e23b48fac0f1ce6d4ea9255c380 +hash_to_point 7feab74f82a6091e2e7a8c11ad1d37a4aad418485d1f0d82f40fcfde3a7cd098 838dda30a295a9e0c76740bb615de7f9547c79ec4b7f491dcdfceb93c85584bb +hash_to_point 538d92b4ea3a4ac47676e83f798a3c7aa525cddf20bc0c712d4696a22775591a c449a2cf152da9e8862680c2fba8a7c93ba26f9c0dc5607f7dd0c53cac3eb740 +hash_to_point 246bc9648fb7321f975eeeb99585d72d077c382da69b173eae50c2fc6129ae7c b3381c5747fc2b5bd285539d3f48ef79ba73483e12dd2a551b3bb630f52cadd2 +hash_to_point c8453deb3062653eddb4c90239c53433398fbadf7be2f1f20324f6148031b0bf 18323249e8327e2eef8df777c5d264614c939475c4ec02cb2cab302af28342ae +hash_to_point 7f4f9d7b5dd4bdbf166bcf7ef6c22e4ce8ee08ec329e1c96b16f71758023e114 9de46867b7103851c2571933153d555ca5594bb07e0c64461bfc06b597d83225 +hash_to_point cff5a2b0d1352b11d6a21609aafcff00801e5d794e9fdf56a042e8bbc5cfb7c0 865ec62842683643c657a7b85e407e09ae751dfe927fd1e1892e8af7df722bd7 +hash_to_point 1a9277830c601a56a0fadf2b10bdaaeb20a402845c716fce7fd31cd268931cf5 aa113849f537b619554c767d3c66d4f1d82011cf38c3861868fceb7902740ace +hash_to_point 341c2cb621eb3cc4a8e25c7019247251cb9264268f66830542cb95a89c00623a 71ee9f311c89b810f148dace14fa315c148b8f3253540fe7f3f6c2e773f710f9 +hash_to_point 9102056cff6a4165e77dcfc4d93f2aa3f3dfe8d37dd03d3a9af1a649ee5effe6 a26b719499706f19ec10231076edd3ed4c00d2133436c5719682e1d14c97868b +hash_to_point 906c8273d5e61b9044374625fd1bc9b3289920c6491081dabe5b4fbc80baf8e8 864a698f3f6871e97c3f9014087f9bdde61db91aac8d2d71b0984fc158864b4e +hash_to_point 386d9a736a309baf4877c09178321705169e530038999e764c8523065a6bcea1 1e0e7c7064fd10138238822d5ec9e72edb55bbf8dc7acc4a6325032111456a31 +hash_to_point a25b9ed15d6aa4c962216036a014a795680501dea5d812e13167fb04012de5ef 6b88d1611654aefe56f97dd9cc923f7256ea1915683f6f026a2ee3c2fcddc3b3 +hash_to_point 99834a97309d57be2153a8d52e04beb505534d5c0644555c5dd017786da4bc96 c6251313cb464250c4d18c9530905ba915b4643507f6b205ac7a6f3200b5b78d +hash_to_point e94631d1d483d5012e680f4c35412d95ee05905114c719040c557c6c6db3078a 2bbea723f054f0f5a952192c9f142c13265ea29b1642628fc76cba354d92a4b3 +hash_to_point 3fbb2c8ac30c09cfbf2ef5bc28949337a2c99d7994129652c4fe431440a21f10 afb93e3167b932c746ccee69b3218c9a74fbc6f1b043eace842abd517de50269 +hash_to_point db03dd5c8e178a85d1681affc56c713f73d4472a48d6a07544fcd968800382b3 bfe644b3dc8f2dbef7ef422d920f45b2431f3b326440f6f3fe654aece7091bd0 +hash_to_point aca0c9fb151977cc71523ba22e898a736c37d50bbaadda669a582367fc8ebdfa 799d2aa57e5905d6686b11fe8d26da09b43306956f3786cb7d66134637a6dfaa +hash_to_point 311d9579f82134feedf66ba485c455db6b3bd6a618c8d13932c23150a7384f82 dec323d4a6f0743ea27964b88188e4365311ffd4be2cb46d1b47cd359ef9e09b +hash_to_point 6d003ae0dd924785067acd1185bc0831171e2225de34649f5ce702e0a5f7a32a 903ac289b01d123f83191ae536fa94987767086f51670048f9f1a45bcf6ec744 +hash_to_point 2cc5d982e4ba11d49f9dca445e03ff16d90d09371e2c48f6eba8cddcf075f20e 60dc7f1408e8b6c02e6846dac5cf7c898589469d47958deef11b6473d3972d3d +hash_to_point dbfd0b97730aa48220c18fcdf4f539069c1d0120186eabe67b8b192881eda421 843b312e8be09fb66fc9646deecf0cfffea88aedb18da0117a24d565409edbfe +hash_to_point d338c2c874b56c897d56c69cbdb7461b041d08a9e1f6c25c08c75a49160e0c3e 208a8f81213304d901c19c472859f55cc61b8e8a5ffdcb6ccdf9b51257f790ed +hash_to_point 015b22208219679472509759c732fa647605679561c60624020024ffbf8354a9 e5a233c5497d525378c9ffe8f069e81a063c1fa641a3f42d4267ae7835e07376 +hash_to_point 3aeb78ef56967d3860caf884af69be04e9b4fbb0c478b64c1be6b55351518064 939a5f36dd149c0e55df27c438c78cd9a6016ae4fa4623a075b83700f3a9d1d9 +hash_to_point 487baa129a0fe66275725b42674e46440474e75a09ae420be030fa3b40df8aaa 3512a42823eb16e957b47576f99dc110244f20bf73cfc54a3760619ebeb8216a +hash_to_point 551df034e75302c9808c391ef1d3b6f3a31ff1c087a215cecb029b32fe9decc8 0413b232c61b129e9682a9504624d6a25bf6fa4a1dd96efdb4bdd4955de24f04 +hash_to_point ee652d5207165d3ca4f57ad679f2d2930d94f6d131d41d6cbbb628cc6802c817 2b4e51be5f5d50e7c020c44d3010d883bb106ad8256934b1153731a4c27cc763 +hash_to_point 7288e8c1b7c4faf96f1cb281a7e6c8c968a6c59be587dcf8930b5059462c9e93 5a3829a408441e39f34d37c5970ca66f204dfcc84dfeafd7367e4fb79826d221 +hash_to_point 3f7c7f2c74616fbe1c34af30cc416967de4b1c0f7731fb491543b5f6e52ae350 bd9ca18dc69bef2d48456e75c7cf794eb9f2860b129c53864b8653c9fc13fdb7 +hash_to_point 72941203cbba460b2b27e4c5e92ed3fe5f8c4b3c453798145b762abc98c38459 e69b48e5952a35c63d4b47f5e4dcb8805b26495f77cd10b406f7dc1df9e06e22 +hash_to_point 8b02e12f9a0658cb412f408857e0bcbdcf46b19b6e0b4adfa00376906b4012c8 2150f29c2bd79156c254ba80ef3b7f01e69a4787f6ca8af8948fa447ab0cb577 +hash_to_point cda29fb3bd71fbd71e070045c8c00cb95431732d2fb343fd0f7e61d74678303b c3bdaeff0e5e68e0164dacb108e963f353c87ce514f7b20ac2a0d003b6219681 +hash_to_point e14951f81029802d9c48e2044a78df93c4c4040c59eeb88a6e0ec2cae9354388 c0d15da6cb9cdca19358251ed880b20fe3b126af0ba6ac4de6cb6b4e88f12035 +hash_to_point 1da128d5b1bbc4977e9a210effe772fd665faadcbc30b583c8ecce9904bd0d4e 4e7409d236a7adc8f209ab279f606bb0b8668da57f2de7897e775ff8c559d052 +hash_to_point 7c97e76c12b7177af99b79119805787d6c2753dbeeffe3a504fe081788f31c2e 7b63a0f9e42078b1d0d97883dcd8cabbb7b20a1273bbfeb1712e385b8e3f55e6 +hash_to_point 454f34b37bf7345a2ebf85e97d0f2a2a5377ec5c301e71aa7e478a14c59f217d e3eaa70ab652b84a5dc05667c049dc1d84a4683febe3560fd4d0b08ad7a17913 +hash_to_point 8bebcf44cb09f695b255072c69be9ecdc30b671b2ef88e1de6c9dba30bf24eab 3a6fb540aca8fe67015bd10aed44312c66194d0f668573c3121e6716e31dfd56 +hash_to_point 6b55085e65e7215e7c95ff19116e0abe1952f0781d3a93571c96a73ef592ce59 0b7c6a716b409b04f2c83fafa53c7acb3c71ec4a86fac0c012f0b0690d0bb3d2 +hash_to_point 3ea3ce9c32f1de05007a9cb7cfb6f1f61c67fc236042835eb840715a7eb83ed4 4d85901cbd93214add653049a883036dfac190fc038c51d3425cfae41251e4bc +hash_to_point f4960d80dba8ef4d3ac59b2ff3d8c6aa474bbf5741541e14ee479d77d7ecd3b4 637e12f8dec6103ff6121572e59ef459d72b44b779ca1ca9102042f5a522cf00 +hash_to_point fb6ab10cab06c351217750b680ea2eb4416941cfefddbf766055916c033f74a1 17e539f6c4cd50a10937c12b873b014c4f830aaff437861da50fbee1baed71e4 +hash_to_point aaa862d813446855481082d83e4f2238342c77b0eeba8dd369fb83ed48e482b8 a399efa6ab28383d428fdfbab4ecba7b469ef919b43779ca737e02b4da109367 +hash_to_point 9650e9c66da9fb8970590acdba349c0b01fe4124616912db25113c8beee117d8 912e54e8f0ccce9c37447db0d0b0389d09ea8ba93cd0e102bee59b008219e36c +hash_to_point bc5b4670eb3e0ba564f536afc16fa659a62a024f431b1adc65b5e983002c7b74 377245602091e423d721fc3fea75d6c2a085a9da4ede9c360a779849301e6ec2 +hash_to_point bc2a25b9edc63282b16f4d5c9c6ea562c06dabac4a8eeb99fff9086aabd2136e 541029bbd71b16db8d1c233636ce11a91797dcbca22de256fa844d1f0c4036c7 +hash_to_point fc28d56c97c49548c788b055cf989165c391b6e9ede7eab235470fafc0e5a449 d7205940b219135950c555a657fe7d379ac829a0b63a84e83c13f02ef2d6586f +hash_to_point 17805ac81e2bb1ea2474a3fb814c7ca846896e409385effdd3f8e849847b0c78 31ba43c34e09c3e405daeda67813beb59832a2f23280ab38ec1a7cee491ef6a6 +hash_to_point 78209b0dfd4cd2783bd026f7a737748c255e043b7b859dce10a76ddb0df1ead3 52f7a651bd78263224e6cb083f26874c818991e3bc5694f4ad81e5326d3ff27b +hash_to_point fc750bb7b0dc9d58cb0d3095bb1d107e669c27020f425399cd0e22bd2bce38c1 3782a310df70df5e879735caff7aff726397731c2284a522dbe239aa0a8eea17 +hash_to_point 92e610273ae9e6f777aecaeceefc035d341a2ef698adfb1672f5ca76898158e6 ca8b81aac9f85965b7fd6974ad54fd06e04ed82b5cea2e0f11b9b495f5111b58 +hash_to_point 0078c4864a6dc09b972f045d809f5b4c33cf27f1ce826045e4fe99cb252854d2 0fed1c3568bebea26616954303fd9d2da02a58c4f7f7177452f5e5f52858ac1c +hash_to_point 70b2038b67b4d2114f4743bb39a886e57d28548aaec253da3f452e82b11f6a6f 5606451d0e2495a60733d15963f150e600306a2e83c1f69231686b0451e7773a +hash_to_point 79f58559578d71efdba2bde908ec6e3ee1a67a4186a808ceaeb96161dd01606d 18f5843c7c3a26136bcc0d3c40c1ae3ccc5a8a72bf43d0c5961e521e5fdc42a7 +hash_to_point 11ecaf43a6fa75aeafcc64e679476891329789809830b992e2662a6b9ffa8862 326c6441a90bbb431a71b4f830a41e6e66f13337d81b999f675009cdf5e8c535 +hash_to_point 0bd7c6560af6d60a0e7390c1c9c21f0e6fbde20e73f4324952248165450d55de 823c0ee4c39ee82f9cd6bee0d3d6213f58c8d826abecba93e898e6e44692a82a +hash_to_point ccf05a6557af89ea065fb09c63d0f0e395139fdd616ae36666fbb9fb923c8de6 a6aa6d44869c1d6a7085edaa6a7b66064b4e2da26809a489cd8f5ce22cf3953b +hash_to_point c3446a386c63d5fa666bd28afe4247b969c74c2350de4559f90b1be14d238646 9e9f0cc384d8aa83f9493677752cb4c74eb38b079aee42f10711d97fcd5bf594 +hash_to_point 937d4aac182094f2567a2d6fd156beb6b44ec9573df675c5ff86a47ebb241078 069814cb6bdcdd6e23ab742d22382c996f0bd8465eff591b7d8a2f1a3d0371dc +hash_to_point 7f9df20805cae4be3dc6dc56d25e0215b6e620a5b690ed01280e420208dc85fd d8e7c50e52d4c75861d10bc1b64a8ddde994739656d41290fabf46a38f7f8ae1 +hash_to_point 3555c0fbe635a745e60eda4009f632ffdd718ea1d7ec295e026470c5fc00a330 f01a8a5d92416aadffb428e4fa11da51293cf45dfd1423cfb7b2f688d438170a +hash_to_point 0ff916f124e76de8cad3b2fdd262c5216bedfa949fcd1802b53dd3d1cac325d5 dd72b093680c020058121c7ea1f81512ddc2e821cec5383abfb6466a5ebbeb4a +hash_to_point 6883a5ecff3d4c7cd6e90b2ceee9c14b96559b5ad1c273017338baaa941bed32 4f9dfc82639c343d8777a3690bc0af357f5b764d27663e67900a858eb59fa18b +hash_to_point e839ac85be63900cd50b63a2f32029b61bd9b855d4fed4f5c441e2cce3582f6f efde54daa41b951735e0984ea64bfa1320ba57d6b138d17724e210ca67afbf99 +hash_to_point b9ba62437aa62acc07a2e37e9460a406aea66c69bd5b96223e9c7d6674023259 145f489d1e63d48df4ed8b079ce6c47139e0a36499a47e76ef780e7ea675188c +hash_to_point 57aa0eb0553e37aa3711f64ef18a1752434632dc3f44eb4af1d43b9d8a197942 4ce4c79f1263d2931a012a2940dc5c024b483a80b5d03bff245ca569449116e8 +hash_to_point bb8c7fe2393f8cb982fae094628e18ced64276b37e2913b9ba40313d7b756036 04699be1747e0aa131c9b22491b1e3137fb170f33e52b254804ebfa0fab77447 +hash_to_point 7932200f45d889384eb9513625ade43d8678c4589d570856bd469fb3fdd72650 8369c68e27585df1fdda4c40e1b4f61a22ac6823921764489d440218ba437026 +hash_to_point 9f172cd7434bfe71c4d84dabdd1b7e44a4bef47ef2ed4e68027a75f6cf195fb7 230658ab68fbae88702b44cb71189ed196be5dc37b70533af560ade3890b1289 +hash_to_point ae64af448cb5bfc58110ffdbecabe70e91886b4fb7bed4f0990f6e390ac23bcd 2d9072637894e7db24c67140342e7e20606ea574ddbf7eea64adb19de3189804 +hash_to_point 1f067ee7ea632559594200876b80efa8f8ac133fbefce922048ef33d96c7d426 61b6bd4bed6eb0f578deeea18ceca41d41ceb0be41a4288e0737fbc225504546 +hash_to_point 6455684ad5d36089283624839cf88f74ee177407d91301215c2891ac0113c9b8 261a307fe3a9c02c45c0e5980aeaf477bf70dd7eeea13c3ff613a54fcfea2115 +hash_to_point cb141d468169a86e8c815504b5d3abd25da27325f12d5248b3a9211dc0e5a19c e1a1b6a7e548ef1fbb77994e2a27da7fcc74c6ae070e8a798446686f056abab4 +hash_to_point 129be1e1a4fc8bc6641b4be53fa93ec5b0bd9fc2d3ca0e8d70928f6ed620d0e0 6b989e86e4ba9a3410a4bb6022a4bae91a7c758ea004e33a57a956a801677106 +hash_to_point 72f26ff76e06b7c79fc57c53af0b8f2208d5ce0371199c6e3a37ae136e969c65 3738cf4dd0b2b031d3a9632f5d5138f4a04e2c975aff7bad618db3e424298e7c +hash_to_point f1664494ced7b20b3bd829bbe7981d675352f57df29e1a3562df129a3ade2634 53de664e1da786c0fa5ca948a76792feef0e5f99297ae8d8cfad4a737990589f +hash_to_point 7c23802e5b7ccaf44841f104f96cbf0cb3b67957b20e54e30d443ed83c0f7c1c 9243727210b0f8b2df6cd476d652b9a2b57310ce658fd121565fc60f74500046 +hash_to_point 8ecf07823179047cf917dbb66ce651477df9ccd5b40020e7c5164c50f2632798 389ea3ec8192c402b54e5ea04fae959f530358aac1d31c5a5126446cae996c6a +hash_to_point baaa85d7a2b9b02aae47efe751378a3ecd8dd2b7051c5ad98251293981c160df e45c4305155a1a0d076330727ebf75b394a304821496cc9cf739e3534cfb68c8 +hash_to_point 4651e988315942839cdf994c928ae0b6b405ed8931bb7a77dd8ee6a7d6dc8978 dbe526375fb8c5219b7a091e98c0b271acb6b0ff27f659160f4da299b7fb76b6 +hash_to_point 8d8a00c9103c07a022f8fd29a04e6da063a7ccedbab84e42e5f27f817fc0bc0d 0fc215dccf48514968d6820e05af1c49c3f99f241d148eeca1c27967ba4e5319 +hash_to_point 8fe38b3e3e9d137de6f69750d223c7e86114c7919396e4dd484c6d02d03c8f64 91c64e2f5660566113668e05c6e55e392935cffc1dc78a91e98ff0d6f009c66d +hash_to_point e61c751691e5f07aab238b44ee0da56e8256c8de16af17db8790cba95b2e6451 199999435cd9ba98cb1dd446bebc6bd0b276ad577b9bfd585c80bff165579bdc +hash_to_point b8297057971136f00598da1bddad5e8e142a3926c368884fe3f4f0a6b9a95306 58c0b6a475e97f4be4a181fd65ec6364dc0361937b0b4e296983bd2a35fe6162 +hash_to_point a406130705173ab29f81009f57df3aba1dd7fd89c30bcf6c529bc92271516468 43aefbca509227fe21d2a2e9083c225c6d95815322e157d9d93ed4afe549c48b +hash_to_point b0407a6663e30e5855b7eed4230669a334e36ca8977b9e9a5482f4dc1d4cdfae 0c9704eaf497e986ae25771b2e3ec9d33d32b7bfa2ed058e0b58646a47ed69d7 +hash_to_point c7574d950364f01d3d988aa3a98471eb06112df021caaaf751d0e7d71a7fa638 22386bb12bcaa48b642537f37ee3682164ec84f5315a1fbf59c15edc05f04145 +hash_to_point 2b3ebfa501b1b117cc55664462bca3ce8ef52464e6f1ea848de99a997f7a57a7 b97748dd13a830d4a330c34553403bd0c16fe955c6b4e499359e1786e74c2bb5 +hash_to_point e59225733e6302dbd9e2247e7df1c1b3629e4de94c8b6b76109a0e2f9b55761a a8c9f5cc387eca35074a6805098ee096b08b8723e2d361b8d344740ec17fbdc3 +hash_to_point 66dc37995fff23da4fd3cdffd0e9a0b8ff52645313a9db5a378ee7016b1de879 c79d54f4255a691b297cde6f267bbc213abdc5a2da6d03b7ddf6d044edbb70b0 +hash_to_point 607fb2b4fa531dcd8541c3c053b9595478f75a3dba89e597582e45c48a80b073 d68d7647fcce52f44797d8056bda596391a092f5292d5ca6ea62c7f67a21181c +hash_to_point caef734683f843b07d53841bd3b63415f4a81d1f086cdfaf850effdf83d69da9 9da3b7d6360bd5367f270909a365cd2fe24f6de081546cfa61bba698a0ea55b5 +hash_to_point 874851e287e0208ee09f212b4f2e18e9fad26a29add385ab5d3c7cebbfa30e7d f578d14381513f34a4aeabe92bcff374040e8766f62c8ed15059ae40edede08e +hash_to_point 0a96b3b1ab38c37b71debea32acd5a2052be1bee7aca0aa589a8f6f176ab0436 78b08be456f019df6d7fa08b40793fe63bde4931ea88af9a4af1b9473cb0ac7f +hash_to_point 87fb58526b37c40f12efa2dc549ad27d638530b5538928a3de15637429e03716 ccbd357eca44143debdb1d26d96264b63e46407c52623c659d49afa6c462bfed +hash_to_point dbdfa4237cf5e37f0d0ab91db8f84fba551c9688409400035925a75532cdfe84 c645e9f4c082da44f9593a289f7bad0d621b05c87f6b31cfd94a296d318fa3b3 +hash_to_point 900ac6ff4217a0a62f8b7204005913bc816ddffc47c57ed24f6d76869ca73a5d 8c681a3235ed421920c93bb10274e5dd5c0d62e58f199d8ed5789fb514340aa5 +hash_to_point 0f38edade9096e538273fd8c7b940374e158dddce8e7be8c0c1694b593d8d09b 3e3089ad03352356ec7fb2bc50be8208e79a843aa35a4fd79c1679506069199e +hash_to_point dcdb877c7bb55ede9d1a5efb6d5cd493cf3717d53a75ee8fd48155df5b65c30e cdc4284cc7b776b02077b3dd9a0bfb68c6cf1dbfcbbbd845e5764f2f9389d06f +hash_to_point d02a1067af837748627a89b901a7bc376a9f1b9cb761ded0ff736708d1d1b518 613080f404d50677ca54f82021ace272fbec3e56cb698efb42ee5238b2509496 +hash_to_point 97dcb44f2306e480869161f7f6640ccff8896bf2425600e56b6708d6d5346e45 b07a06e9219f9ab133cc1ab04284ec7205491ed6d94047540fa11b0ce3329b59 +hash_to_point 4dde19bc8e1a99ae80abd4d4e69740560f912d94f7751446746ed56b5e8bbb90 da6e21cb3f5f82ceb891220393388cedad5504a810c0a36ab531e37912130bd9 +hash_to_point f890dc27726e9c6df0f052590bc3a5d04df51852b45b549c1623fe79b553f6e0 2cdec8b02adc8b8af1053058a0dc231cc66398bafe9399162b302b823fdcacee +hash_to_point 357605b362955c84b857e7a5c0bf070f61bf6fad1e47c06d31b563ab06c24c2f 83be3b09b2537e875027d14cbfd9eed219c3a287f6f9051839e2af3c7517ac8c +hash_to_point 5fa8b3c0f526e8c418b8455f2b4177a44beb71ddb4d7898d6167993ffbeaf822 204db7157f592e0402f7d4914c1d4286edb386f223dde33051ce9d149f929582 +hash_to_point 53fbc1029c5ea6e0d9a0bb7f887264083282dbdb14e9702dee66d62ec41a5e38 da05321f87540b9bd0d2d38e327dd0f4ce18f160761986ded19b820d20412779 +hash_to_point 73d9f80d4417fe4608b0e69dfe5eefe405d1acbd5f90a4d6a98b04acec604e1a 59aee86053bd666954a611a7641b978d3ad4f4ad97f300d24f191e2fd8390161 +hash_to_point 6292fded9592418594c1e36d32947cfa61910e9e745c3ca1dfdcc7c5c5348286 b24a7b95d3dcdd5234e148a9463fd739dd2cdf781a68c7d278ee2fdef3ef4a69 +hash_to_point b4394a931a13c8c32161d31b1d45704de9bb42c1e11a017d7e254c74148d87e8 c4a680e08d3e51dedfefb8327bd1b92970ef5a3fb410767a3919b7216efdb8f0 +hash_to_point 992748e5fe3058496e71843ec536279d8952d1d1c10ebf56fc6c0fbb9876169c 5939b3f0378278a4c46668da8b7bb3d911e912023b8168e2dabd23b0bb27056a +hash_to_point 07d8a283bdeaa0ba5ba57573813eaa385df28d22bf2d6613fc0a6a43422d8e6a 8989ee11adf05890153064aac6a093cec33c5d5a73d3dfba445b251d7f61d2fe +hash_to_point 8832b9a2d4087b4b92a9f244117a420788c30a4c1c515e04462719ffb7ebd0f2 6e9c9ed8346c4db0f866079b56bd6842890df02419f7e535b9159a1de4f47adb +hash_to_point 91051c5770213e89b7a8816e7a6d913840701ecb89786440d704cd8cc1397831 e2466bdc40e5458d82d62d4499928bc07fe721406457ccfc8913da4d4d9a9233 +hash_to_point 7e19154171ddbf10f81869ec81d5fd36f0909f5bfaba43f58de6529b78f6f0c3 c25ee9791ba39a08f7df54d4fe2917593882e40a5cb1ee02b4af487adc020db3 +hash_to_point 6a89d2970a550a05676af03ca0f71c4de353e491f98830c3186b40d0f1aba477 ec7824148701ea0ac67af367b06e6518aa15aaf73ca2aa4d1e46ddc21ead340d +hash_to_point b1d7258d551b0abc9c5234ecd178b008255122f57c69e30632ec3ef1137fd91b 0bfe869a55b2bbae2fe069403061ed8fda3ac511d2bfba28352fcc2bd1a77b4e +hash_to_point 891b4dfe926a82ec56d135dda77d2bb0acd1eee5b0de891ce4bdbcbf57558333 29838af60ffd5c932e3ce699882969806459ee9d5d3fee2581a266001d4d8c71 +hash_to_point 290bdb8fb4623838a742e9f1876838b59e0763ecf58d95abe4c57ab2ada35ec2 986e85c9fc335295cb184be933f6da0df5b0edb99666da4168c428ad3e454e1f +hash_to_point 1ba697f59b4ff3f486b85dcd35a09cbdbda8721b7ced9803c5d5741ef77cffbb 65b63c93c1c216b0455204e2bc496e27a98af650bc2aec4b448253a234df3e99 +hash_to_point bfaa1d0371576517cc7e79197905d96be5403252d10e9bf1bf0d672c86da9597 0dbd0260a5e0e2407428dab505c6e5f592091f5f2cdd461e8032509d93e011b0 +hash_to_point c6d59094420e50a9557b8ef85b97375c5801f0ee27401461fe7456b683a715ec 459486f0b36b7372c9ccd5138cbdf965a64e23b711211a6809b4e2c826f6809d +hash_to_point 5078e9dce783f222793e6d0baad978da17dc2df147b1b7ea1cc568feb16ab305 b84cee98b419a88daa34f0eef2fd7932b69949d92a85cf39771fdd05e090b20a +hash_to_point 3242d791dda4a044b2ec6f71c0601dfb3cee3308134061ece384df075b8f3427 def499fd518934ce6f5ffb7d2f078d681442bf9cecea8581bb924d8d4c1c9291 +hash_to_point 75652e10fdf25db807dc98135cb16548f63b35ecc20bf7893afaecf74201e335 e714d09dbf0344ea1a7cfd101ca6f8c43d1149713cc170eee1a5f514fa8fa3ca +hash_to_point 1248a7558492a9069b9262c543ca41c51d12a03db594407c68cdbbe1f7a77b7a 7f86151abc1b4efdb48e6e25cac2d131359f2bcd3e7e772fe3177eeb69b4076b +hash_to_point 2fecafd49737a79614b2b90389c1d28032e9f93d75f7fa5090b54adf42a2b0be 46f6d2bf64eecff6c2caa025ed35d3ae08cc2120de444cc4d16421a8621d830a +hash_to_point 22a3cb446a84824af53ecbb4b68348724dae2375a6c4f1759c6bd280106292a4 28968787f66e4b64b334298663a1e63aea5fd78f56bdc63ea6eb23eea0237a2b +hash_to_point 72e638c5823711fecc15f4c0d2b8a566341d9dcbde397a57bf9e496c317be0e0 f253dfa0650765e068876fb66ff804439fe5673eff8066555db1ee4e41c22207 +hash_to_point ecbb4faad41833dd842402659e3715a2177a6dff9976f8278df027a86f710019 fd1128e3ca3d3de808afbd95ddbd381a80e29015cb0fb387a49a52a7d16b7cf5 +hash_to_point 650ecdf1598b1b74a07ec9a1bfc95333ea73bdf0b34b42527a7abf7225d1ca50 9f06c194ac7ace1671e411a254f7b0e4a598840e62e955f88e8b8dac9f60e82f +hash_to_point ce979aaf44f6b6df7e7de6a19dbc077534a8b230745af9dc812fce3fa1ce41d9 a89c75478cd2ee7f1684c28a50db7fa89cc40a11ad8b95f532ea18a894a10969 +hash_to_point e145f43d494d221357e294aacfb99bd8898f3c4053dd3f06b4c9d24d3d9b5440 c5ea488724b436df17efd1d5fb0d3a58d5198c041d26176d696059365447083e +hash_to_point 0ae276bb80fbc892a881e8959ad5f9d6211164ea2c902c23cf0efc0fa00a46e8 d5e07ce0d8176a2673923f8d9f5fb87251195697d83f44395c4cd302dec1c65f +hash_to_point f18bf646c366dbc0c881e0d39cdf8ac578f7ec34dae7804c103f8e835dd036ec 17976bd2fbaa411f93bdf976ff6bd05601739bc3193a7edab5841655493b955c +hash_to_point b3888d20111946a812727ee59857cc7dd1e837b263844d1bd363afd213904b02 8b71d6813498f96a0c885bef7fd8c6270ff1d3e7b5e225024c7b32e0b847e18e +hash_to_point e5203928a6b1c7ea481f2fb2b48041b9631469e977aa7ecaaef3fa3c7f7555a1 d70a59ccd93082ebcab9657022c909b39e50b213457d08d3c7b5758b4bcab0b5 +hash_to_point 3d50fd2f8aa93ade01570c47551fe8407ce15d2bf9d5fbd0fcfd5214ad69595b c4233ff7edb05131a39b5840262de6c8aa6328bf8c98580863cc041b680d57b9 +hash_to_point d82d68476d20a9d02e68f74068513a1f1d31f7ef115b77cbe690c18e35e25164 57bd39e72d3da4eee80869742f4b5360e32ae96a70a1925987bf99e412d917c8 +hash_to_point 90e4008fc21ee6fe15effa18c9dfa6f9d72aabf3290ac86091c665a0b27face6 f07b8ccd3f63609d0c41d205b80c6cca5f53aa7d57d93cb1c0e3bb0c3fffb359 +hash_to_point ece09937407ea099d389c8b1396bc1b32eb7632b4b9ed48b846eb318c87f9a28 21bed6e7036f57a177e71bc4c7c93d3618f5737fc0c3a2819d109bcc90828be9 +hash_to_point 72c0bf0062d4deb3a1d1476e7e2c219bfce5e939a62d76adf208c9241d8c2309 7abbed2344b55226cbc209694efa205857c2ae37bf213c553bf520d56d7c51cd +hash_to_point 951b5bfb3d44e9f1b4fb8a7aaf91d6b8e9b5980a9827bb99e40b5be524655487 e6f1a2b2393417a09d93509cbd83fe9cbcbf71951edb974787ceba673f5f97ab +hash_to_point e23e8b44a62799e0b12c0c611d12432c92b7ab1b697be6d63612bd2f9862477c 63a6aa4b4f97de6d8d6a91395fb05f401f06b572a8b591e6c3f3507f06c05b7c +hash_to_point 37460478e67f83f6fb00513879919727a93f2c0442531965a9865103b2d3ca5b dfc53f09073ff40e1311cf31b32b93adfc3fcdb3491a54ef90ab59b156792697 +hash_to_point 641626ba7d117fb83c491d45ebde1fbecabea49d463edfe4a0f0217687923549 bc7f10124d869b27c8e839e1cc281e5c4635edd402669596634498a0742a6672 +hash_to_point fc8d4493e5b798afc0900c15da3b9c22e67fecbb40b15fc72eab777cf116a96e 9740713bdd318b1722886f53b65ea0fbf7e5db3693acc27aa0ab097e5858e613 +hash_to_point 6818318031519851753f3e326880f0f21334731614ceb0ee16feb9e0fce20613 4516a9216ccbd8b1d788eeb34c228c3d4acf5fcf80bb008566effaf872f02e21 +hash_to_point 6b991e7070d1dacbb0e8b5f9f1aefcd0041d4a6748a21a2914944f98572ae895 085bb8702666587233808944eecfa3ecde673521866c4c643ea9418b17aff5d8 hash_to_point 0000000000000000000000000000000000000000000000000000000000000000 ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f hash_to_point 0100000000000000000000000000000000000000000000000000000000000000 5278d545cf9c859bb5ce01dc6c8b8d4e3a02271ca6d529c835e05a64981fcb8c hash_to_point 0200000000000000000000000000000000000000000000000000000000000000 fb6e2b404ea0a97df827c8ad5d3a4676fe7176665cbd464c46de72e633caca13 @@ -3686,1795 +3990,1795 @@ hash_to_point fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 4 hash_to_point fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 9b238c928fe8993e63543b87c83d6cf17bc0524dc149b080102671bbcd3cfb8f hash_to_point feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 123a5287f4de990b3dcf9c32456a57349fb40bcf0963912c604770a8fb9e9a41 hash_to_point ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 80c90f3f23af8763b058cf5029e42f6a78a3e48dc0eeb38f31b3a6419e64fdbf -hash_to_ec da66e9ba613919dec28ef367a125bb310d6d83fb9052e71034164b6dc4f392d0 52b3f38753b4e13b74624862e253072cf12f745d43fcfafbe8c217701a6e5875 -hash_to_ec a7fbdeeccb597c2d5fdaf2ea2e10cbfcd26b5740903e7f6d46bcbf9a90384fc6 f055ba2d0d9828ce2e203d9896bfda494d7830e7e3a27fa27d5eaa825a79a19c -hash_to_ec ed6e6579368caba2cc4851672972e949c0ee586fee4d6d6a9476d4a908f64070 da3ceda9a2ef6316bf9272566e6dffd785ac71f57855c0202f422bbb86af4ec0 -hash_to_ec 9ae78e5620f1c4e6b29d03da006869465b3b16dae87ab0a51f4e1b74bc8aa48b 72d8720da66f797f55fbb7fa538af0b4a4f5930c8289c991472c37dc5ec16853 -hash_to_ec ab49eb4834d24db7f479753217b763f70604ecb79ed37e6c788528720f424e5b 45914ba926a1a22c8146459c7f050a51ef5f560f5b74bae436b93a379866e6b8 -hash_to_ec 5b79158ef2341180b8327b976efddbf364620b7e88d2e0707fa56f3b902c34b3 eac991dcbba39cb3bd166906ab48e2c3c3f4cd289a05e1c188486d348ede7c2e -hash_to_ec f21daa7896c81d3a7a2e9df721035d3c3902fe546c9d739d0c334ed894fb1d21 a6bedc5ffcc867d0c13a88a03360c8c83a9e4ddf339851bd3768c53a124378ec -hash_to_ec 3dae79aaca1abe6aecea7b0d38646c6b013d40053c7cdde2bed094497d925d2b 1a442546a35860a4ab697a36b158ded8e001bbfe20aef1c63e2840e87485c613 -hash_to_ec 3d219463a55c24ac6f55706a6e46ade3fcd1edc87bade7b967129372036aca63 b252922ab64e32968735b8ade861445aa8dc02b763bd249bff121d10829f7c52 -hash_to_ec bc5db69aced2b3197398eaf7cf60fd782379874b5ca27cb21bd23692c3c885cc ae072a43f78a0f29dc9822ae5e70865bbd151236a6d7fe4ae3e8f8961e19b0e5 -hash_to_ec 98a6ed760b225976f8ada0579540e35da643089656695b5d0b8c7265a37e2342 6a99dbfa8ead6228910498cc3ff3fb18cb8627c5735e4b8657da846c16d2dcad -hash_to_ec e9cdc9fd9425a4a2389a5d60f76a2d839f0afbf66330f079a88fe23d73eae930 8aa518d091928668f3ca40e71e14b2698f6cae097b8120d7f6ae9afba8fd3d60 -hash_to_ec a50c026c0af2f9f9884c2e9b8464724ac83bef546fec2c86b7de0880980d24fb b07433f8df39da2453a1e13fd413123a158feae602d822b724d42ef6c8e443bf -hash_to_ec bf180e20d160fa23ccfa6993febe22b920160efc5a9614245f1a3a360076e87a 9d6454ff69779ce978ea5fb3be88576dc8feaedf151e93b70065f92505f2e800 -hash_to_ec b2b64dfeb1d58c6afbf5a56d8c0c42012175ebb4b7df30f26a67b66be8c34614 0523b22e7f220c939b604a15780abc5816709b91b81d9ee1541d44bd2586bbd8 -hash_to_ec 463fc877f4279740020d10652c950f088ebdebeae34aa7a366c92c9c8773f63a daa5fa72e70c4d3af407b8f2f3364708029b2d4863bbdde54bd67bd08db0fcad -hash_to_ec 721842f3809982e7b96a806ae1f162d98ae6911d476307ad1e4f24522fd26f55 4397c300a8cfcb42e7cc310bc975dc975ec2d191eaa7e0462998eb2830c34126 -hash_to_ec 384da8d9b83972af8cbefc2da5efc744037c8ef40efa4b3bacc3238a6232963d 3c80f107e6868f73ef600ab9229a3f4bbe24f4adce52e6ab3a66d5d510e0670d -hash_to_ec e26f8adef5b6fe5bb01466bff0455ca23fda07e200133697b3b6430ca3332bde e262a58bcc1f8baf1980e00d5d40ba00803690174d14fb4c0f608429ce3df773 -hash_to_ec 6e275b4ea4f085a5d3151aa08cf16a8c60b078e70be7ce5dac75b5d7b0eebe7c cb21b5a7744b4fcdc92ead4be0b04bcb9145e7bb4b06eff3bb2f0fe429b85108 -hash_to_ec a0dde4561ad9daa796d9cd8a3c34fd41687cee76d128bf2e2252466e3ef3b068 79a2eb06bb7647f5d0aae5da7cf2e2b2d2ce890f25f2b1f81bfc5fef8c87a7d3 -hash_to_ec dbaf63830e037b4c329969d1d85e58cb6c4f56014fd08eb38219bd20031ae27c 079c93ae27cd98075a487fd3f7457ad2fb57cdf12ec8651fedd944d765d07549 -hash_to_ec 1e87ba8a9acf96948bc199ae55c83ab3277be152c6d0b1d68a07955768d81171 5c6339f834116791f9ea22fcc3970346aaeddacf13fbd0a7d4005fbd469492ca -hash_to_ec 5a544088e63ddf5b9f444ed75a75bc9315c4c50439522f06b4823ecaf5e8a08d e95ca0730d57c6469be3a0f3c94382f8490257e2e546de86c650bdbc6482eaee -hash_to_ec e4e06d92ebb036a5e4bb547dbaa43fd70db3929eef2702649455c86d7e59aa46 e26210ff8ee28e24ef2613df40aa8a874b5e3c1d07ae14acc59220615aa334dc -hash_to_ec 5793b8b32dcc0f204501647f2976493c4f8f1fa5132315226f99f29a5a6fdfce 656e390086906d99852c9696e831f62cb56fc8f85f9a5c936c327f23c7faf4fe -hash_to_ec 84f56fa4d7f12e0efd48b1f7c81c15d6e3843ebb419f4a27ec97028d4f9da19e 0cbd4f0cd288e1e071cce800877de6aef97b63fff867424a4f2b2bab25602608 -hash_to_ec 242683ddf0a9fc55f6585de3aa64ea17c9c544896ff7677cd82c98f833bdf2ca 38c36d52314549213df7c7201ab7749a4724cbea92812f583bb48cabc20816ad -hash_to_ec a93ee320dc030aa382168c2eb6d75fce6e5a63a81f15632d514c6de8a7cfa5ee bd0a2facaa95bc95215a94be21996e46f789ee8beb38e75a1173b75fc686c505 -hash_to_ec e36136601d84475d25c3f14efe030363d646658937a8a8a19a812d5e6deb5944 2fb93d78fae299c9f6b22346acfb829796ee7a47ec71db5456d8201bec6c35a3 -hash_to_ec ba4b67d3d387c66baa4a32ec8b1db7681087e85076e71bab10036388c3aeb011 cc01329ce56f963bf444a124751c45b2c779ccb6dea16ca05251baca246b5401 -hash_to_ec 3fbc91896a2585154d6f7094c5ab9c487e29a27951c226eec1235f618e44946b 7d983acbb901bf5497d0708392e5e742ec8c8036cbb0d03403e9929da8cc85a7 -hash_to_ec a2da289fed650e9901f69a5f33535eb47c6bd07798633cbf6c00ce3172df76ac dca8a4d30ec2d657fefd0dba9c1c5fd45a79f665048b3cf72ac2c3b7363da1ac -hash_to_ec 99025d2d493f768e273ed66cacd3a5b392761e6bd158ca09c8fba84631ea1534 7ef5af79ab155ab7e1770a47fcd7f194aca43d79ec6e303c7ce18c6a20279b04 -hash_to_ec 3cf1d01d0b70fb31f2a2f979c1bae812381430f474247d0b018167f2a2cd9a9f 7c53d799ec938a21bb305a6b5ca0a7a355fa9a68b01d289c4f22b36ce3738f95 -hash_to_ec 639c421b49636b2a1f8416c5d6e64425fe51e3b52584c265502379189895668e 0b47216ae5e6e03667143a6cf8894d9d73e3152c64fb455631d81a424410e871 -hash_to_ec 4ccf2c973348b7cc4b14f846f9bfcdcb959b7429accf6dede96248946841d990 7fd41f5b97ba42ed03947dd953f8e69770c92cc34b16236edad7ab3c78cbbb2e -hash_to_ec f76ae09fff537f8919fd1a43ff9b8922b6a77e9e30791c82cf2c4b8acb51363e 8e2c6bf86461ad2c230c496ee3896da33c11cc020fd4c70faa3645b329049234 -hash_to_ec 98932da7450f15db6c1eef78359904915c31c2aa7572366ec8855180edb81e3a 86180adddfac0b4d1fb41d58e98445dde1da605b380d392e9386bd445f1d821c -hash_to_ec ab26a1660988ec7aba91fc01f7aa9a157bbc12927f5b197062b922a5c0c7f8dd 2c44a43eda0d0aad055f18333e761f2f2ec11c585ec7339081c19266af918e4f -hash_to_ec 4465d0c1b4930cc718252efd87d11d04162d2a321b9b850c4a19a6acdfca24f4 b03806287d804188a4d679a0ecee66f399d7bdc3bd1494f9b2b0772bbb5a034f -hash_to_ec 0f2a7867864ed00e5c40082df0a0b031c89fa5f978d9beb2fde75153f51cfb75 5c471e1b118ef9d76c93aec70e0578f46e8db1d55affd447c1f64c0ad9a5caa5 -hash_to_ec 5c2808c07d8175f332cae050ce13bec4254870d76abff68faf34b0b8d3ad5000 eeff1d9a5aa428b7aecc575e63dde17294072eb246568493e1ed88ce5c95b779 -hash_to_ec 36300a21601fad00d00da45e27b36c11923b857f97e50303bd01f21998eaef95 b33b077871e6f5dad8ff6bc621c1b6dedcf700777d996c8c02d73f7297108b7e -hash_to_ec 9e1afb76d6c480816d2cedd7f2ab08a36c309efaa3764dcdb51bad6049683805 4cd96ba7b543b1a224b8670bf20b3733e3910711d32456d3e58e920215788adf -hash_to_ec 685f152704664495459b76c81567a4b571e8b307dd0e3c9b08ee95651a006047 80dd6b637580cb3be76025867f1525852b65a7a66066993fda3af7eb187dc1a5 -hash_to_ec 0b216444391a1163c14f7b27f9135e9747978c0e426dce1fa65c657f3e9146be 021259695a6854a4a03e8c74d09ab9630a401bfca06172a733fe122f01af90b4 -hash_to_ec cfcb35e98f71226c3558eaa9cf620db5ae207ece081ab13ddea4b1f122850a5a 46763d2742e2cdffe80bb3d056f4d3a1565aa83f19aab0a1f89e54ad81ae0814 -hash_to_ec 07e7292da8cdcdb58ee30c3fa16f1d609e9b3b1110dd6fa9b2cc18f4103a1c12 fe949ca251ac66f13a8925ae624a09cdbf6696d3c110442338d37700536e8ec7 -hash_to_ec 813bc7e3749e658190cf2a4e358bc07a6671f262e2c4eef9f44c66066a72e6a7 6b92fbda984bd0e6f4af7a5e04c2b66b6f0f9d197a9694362a8556e5b7439f8a -hash_to_ec 89c50a1e5497156e0fae20d99f5e33e330362b962c9ca00eaf084fe91aaec71d ef36cb75eb95fb761a8fa8c376e9c4447bcd61421250f7a711bd289e6ed78a9b -hash_to_ec d9bd9ff2dd807eb25de7c5de865dbc43cce2466389cedbc92b90aab0eb014f81 30104771ff961cd1861cd053689feab888c57b8a4a2e3989646ea7dea40f3c04 -hash_to_ec b8c837501b6ca3e118db9848717c847c062bf0ebeca5a7c211726c1426878af5 19a1e204b4a32ce9cccf5d96a541eb76a78789dceaf4fe69964e58ff96c29b63 -hash_to_ec 84376c5350a42c07ac9f96e8d5c35a8c7f62c639a1834b09e4331b5962ecace8 ba1e4437d5048bd1294eadc502092eafc470b99fde82649e84a52225e68e88f2 -hash_to_ec a3345e4a4cfc369bf0e7d11f49aed0d2a6ded00e3ff8c7605db9a919cf730640 0d318705c16e943c0fdcde134aaf6e4ccce9f3d9161d001861656fc7ea77a0b1 -hash_to_ec 3c994dfb9c71e4f401e65fd552dc9f49885f88b8b3588e24e1d2e9b8870ffab1 984157de5d7c2c4b43b2bffea171809165d7bb442baea88e83b27f839ebdb939 -hash_to_ec 153674c1c1b18a646f564af77c5bd7de452dc3f3e1e2326bfe9c57745b69ec5c e9a4a1e225ae472d1b3168c99f8ba1943ad2ed84ef29598f3f96314f22db9ef2 -hash_to_ec 2d46a705d4fe5d8b5a1f4e9ef46d9e06467450eb357b6d39faa000995314e871 b9d1aec540bf6a9c0e1b325ab87d4fbe66b1df48986dde3cb62e66e136eba107 -hash_to_ec 6764c3767f16ec8faecc62f9f76735f76b11d7556aeb61066aeaeaad4fc9042f 3a5c68fb94b023488fb5940e07d1005e7c18328e7a84f673ccd536c07560a57b -hash_to_ec c99c6ee5804d4b13a445bc03eaa07a6ef5bcb2fff0f71678dd3bd66b822f8be8 a9e1ce91deed4136e6e53e143d1c0af106abde9d77c066c78ebbf5d227f9dde0 -hash_to_ec 3009182e1efac085c7eba24a7d9ef28ace98ebafa72211e73a41c935c37e6768 e55431a4c89d38bd95f8092cdf6e44d164ad5855677aba17ec262abc8c217c86 -hash_to_ec e7153acd114a7636a207be0b67fa86fee56dd318f2808a81e35dd13d4251b2d0 ff2b98d257e4d4ff7379e8871441ca7d26e73f78f3f5afcf421d78c9799ba677 -hash_to_ec 6378586744b721c5003976e3e18351c49cd28154c821bc45338892e5efedd197 3d765fb7bb4e165a3fa6ea00b5b5e22250f3861f0db0099626d9a9020443dda2 -hash_to_ec 5be49aba389b7e3ad6def3ba3c7dbec0a11a3c36fc9d441130ef370b8a8d29c2 2d61faf38062dc98ae1aaafec05e90a925c9769df5b8b8f7090d9e91b2a11151 -hash_to_ec f7bc382178d38e1b9a1a995bd8347c1283d8a2e8d150379faa53fd125e903d2b 544c815da65c3c5994b0ac7d6455578d03a2bc7cf558b788bcdb3430e231635a -hash_to_ec c28b5c4b6662eebb3ec358600644849ebeb59d827ed589c161d900ca18715fa8 a2d64db3c0e0353c257aadf9abc12ac779654d364f348b9f8e429aa7571203db -hash_to_ec 3a4792e5df9b2416a785739b9cf4e0d68aef600fa756a399cc949dd1fff5033a 4b54591bd79c30640b700dfb7f20158f692f467b6af70bd8a4e739c14a66c86a -hash_to_ec 002e70f25e1ceaf35cc14b2c6975a4c777b284a695550541e6f5424b962c19f5 73987e9342e338eb57a7a9e03bd33144db37c1091e952a10bd243c5bb295c18a -hash_to_ec 7eb671319f212c9cae0975571b6af109124724ba182937a9066546c92bdeff0c 49b46da3be0df1d141d2a323d5af82202afa2947a95b9f3df47722337f0d5798 -hash_to_ec ca093712559c8edd5c51689e2ddcb8641c2960e5d9c8b03a44926bb798a0c8dc b9ef9cf0f8e4a3d123db565afafb1102338bfb75498444ac0a25c5ed70d615da -hash_to_ec cfea0a08a72777ff3aa7be0d8934587fa4127cd49a1a938232815dc3fd8b23ac b4de604b3d712f1ef578195fb0e53c865d41e2dfe425202c6cfe6f10e4404eb5 -hash_to_ec aa0122ae258d6db21a26a31c0c92d8a0e3fdb46594aed41d561e069687dedcd6 5247eaec346de1c6cddf0ab04c12cd1d85cdb6d3a2fba2a5f9a5fe461abef5eb -hash_to_ec b3941734f4d3ba34ccaf03c4c737ac5a1e036eb74309300ce44d73aca24fef08 535938985c936e3780c61fe29a4121d6cb89a05080b6c2147031ea0c2b5b9829 -hash_to_ec 8c2ee1041a2743b30dcbf413cc9232099b9268f82a5a21a09b63e7aff750882f 6ad0d4b3a65b522dfad0e9ac814b1fb939bc4910bd780943c72f57f362754cca -hash_to_ec 4b6829a2a2d46c8f0d0c23db0f735fcf976524bf39ccb623b919dd3b28ad5193 2e0097d7f92993bc45ba06baf4ca63d64899d86760adc4eb5eeefb4a78561050 -hash_to_ec 9c1407cb6bba11e7b4c1d274d772f074f410d6fe9a1ee7a22cddf379257877d9 692261c7d6a9a7031c67d033f6d82a68ef3c27bd51a5666e55972238769821cd -hash_to_ec 638c42e4997abf8a4a9bffd040e31bd695d590cde8afbd7efd16ffdbae63bf66 793024c8ce196a2419f761dde8734734af6bd9eb772b30cc78f2cb89598dce97 -hash_to_ec 1fb60d79600de151a1cf8a2334deb5828632cbd91cb5b3d45ae06e08187ae23d ff2542cde5bc2562e69471a31cfc3d0c26e2f6ccc1891a633b07a3968e42521c -hash_to_ec d2fdbbae4e38a1b734151c3df52540feb2d3ff74edfef2f740e49a5c363406ee 344c83ba6ff4e38b257077623d298d2f2b52002645021241bc9389f81b29ad12 -hash_to_ec 836c27a6ddfe1a24aba3d6022dff6dfe970f142d8b4ac6afb8efcba5a051942f b8af481d33726b3f875268282d621e4c63f891a09f920b8f2f49080f3a507387 -hash_to_ec 46281153ddcdf2e79d459693b6fe318c1969538dd59a750b790bfff6e9481abf 8eaf534919ab6573ba4e0fbde0e370ae01eae0763335177aa429f61c4295e9d4 -hash_to_ec d57b789e050bf3db462b79a997dac76aa048d4be05f133c66edee56afd3dbe66 0c5a294cb2cbb6d9d1c0a1d57d938278f674867f612ed89dcbe4533449f1a131 -hash_to_ec 548d524d03ac22da18ff4201ce8dbee83ad9af54ee4e26791d26ed2ab8f9bfc7 c6609d9e7d9fd982dec8a166ff4fb6f7d195b413aad2df85f73d555349134f3b -hash_to_ec cc920690422e307357f573b87a6e0e65f432c6ec12a604eb718b66ba18897a56 6f11c466d1c72fccd81e51d9bda03b6e8d6a395e1d931b2a84e392dc9a3efa18 -hash_to_ec c7fb8a51f5fcd8824fc0875d4eb57ab4917cb97090a6e2288f852f2bb449edd9 45543fea6eed461016e48598b521f18ff70178afea18032b188deea3e56052fc -hash_to_ec c681bb1b829e24b1c52cb890036b89f0029d261c6a15e5b2c684ee7dfe91e746 263006fe2c6b08f1ab29cdf442472c298e2faf225bbf5c32399d3745cd3904bd -hash_to_ec e06411c542312fdd305e17e46be14c63bab5836dc8751da06164b1ae22d4e20f 901871be7a7ff5aecade2acff869846f3c50de69307ac155f2aa3a74d5472ef2 -hash_to_ec 9c725a2acb80fa712f9781da510e5163b1b30f4e1c064c26b5185e537f0614ea 02420d49257846eb39fddd196d3171679f6be21d9adac667786b65a6e90f57b1 -hash_to_ec 22792772820feafa85c5cb3fa8f876105251bef08617d389619697f47dff54f2 a3ad444e7811693687f3925e7c315ae55d08d9f4b0a29876bc2a891ab941c1c3 -hash_to_ec 0587b790121395d0f4f39093d10b4817f58a1e80621a24eea22b3c127d6ac5a2 86c417c695c64c7becaad0d59ddbb2bca4cb2b409a21253d680aac1a08617095 -hash_to_ec fa0b5f28399bef0cd87bfe6b8a2b69e9c5506fb4bacd22deba8049615a5db526 ede0ea240036ff75d075258a053f3ce5d6f77925d358dbe33c06509fc9b12111 -hash_to_ec 62a3274fc0bed109d5057b865c2ba6b6a5a417cb90a3425674102fcd457ede2d ff7e46751bb4dcd1e800a8feab7cf6771f42dc0cfed7084c23b8a5d255a6f34e -hash_to_ec a6fcd4aecaaaf281563b9b7cd6fbc7b1829654f644f4165942669a2ef632b2bf 28f136be0eb957a5b36f8ec294399c9f73ad3a3c9bb953ad191758ced554a233 -hash_to_ec 01baa4c06d6676c9b286cda76ed949fd80a408b3309500ba84a5bb7e3dce58e2 a943d1afa2efce284740e7db21ea02db70b124808be2ff80cbf9b9cb96c7b73e -hash_to_ec dd9aff9c006ba514cef8fae665657bc9813fe2715467cf479643ea4c4e365d6d 68de2f7d49de4004286ce0989a06a686b15d0f463a02ffd448a18914e1ddf713 -hash_to_ec 3df3513d5e539161761ce7992ab9935f649bc934bed0da3c5e1095344b733bb9 e9c2dd747d7b2482474325943cd850102b8093164678362c7621993a790e2a8a -hash_to_ec 7680cfb244dc8ef37c671fff176be1a3dad00e5d283f93145d0cbee74cca2df4 a0fd8c3cca16a130eaa5864cbe8152b7adfbf09e8cf72244b2fc8364c3b20bf4 -hash_to_ec 8a547c38bd6b219ea0d612d4a155eba9c56034a1405dcf4b608de787f37e0fd8 76bf0dc40fd0a5508c5e091d8bb7eccfa28b331e72c6a0d4ac0e05a3d651850b -hash_to_ec dd93901621f58465e9791012afa76908f1e80ad80e52b809dc7fc32bb004f0a8 09a0b7ecfe8058b1e9ee01c9b523826867ca97a32efad29ac8ceebca67a4ea00 -hash_to_ec b643010220f1f4ee6c7565f6e1b3dc84c18274ede363ac36b6af3707e69a1542 233c9ff8de59e5f96c2f91892a71d9d93fa7316319f30d1615f10ac1e01f9285 -hash_to_ec c2637b2299dfc1fd7e953e39a582bafd19e6e7fff3642978eb092b900dbfea80 339587ba1c05e2cba44196a4be1fd218b772199e2c61c3c0ff21dcd54b570c43 -hash_to_ec 1f36d3a7e7c468eb000937de138809e381ad2e23414cbbaac49b7f33533ed486 7e5b0a96051c77237a027a79764c2763487af88121c7774645e97827fb744888 -hash_to_ec 8c142a55f60b2edbe03335b7f90aa2bd63e567048a65d61c70cb28779c5200af d3d6d5563b3d81c8c91cf9806bb13b2850fb7c162c610fd2f5b83c464add8182 -hash_to_ec 99e7b98293c9de1f81aff1376485a990014b8b176521b2a68cdbde6300190398 119cbc01a1d9b9fb4759031d3a70685aebea0f01bc5ee082ce824265fd21b3b4 -hash_to_ec 9753bd38be072b51490290be6207ca4545e3541bdf194e0850ae0a9f9e64b8ba 1ad3aa759863153606fa6570f0e1290baded4c8c1f2ba0f67c1911bfc8ccd7a0 -hash_to_ec 322703864ceee19b7f17cec2a822f310f0c4da3ff98b0be61a6fd30ac4db649c 89d9e7a5947e1cde874e4030de278070aae363063cd3592ce5411821474f0816 -hash_to_ec c1acd01e1e535fad273a8b757d981470f43dd7d95af732901fbba16b6e245761 57e80445248111150da5e63c706b4abbf3eef2cc508bd0347ff6b81e8c59f5bc -hash_to_ec 492473559f181bbe78f60215bc6d3a5168435ea2fc0a508372d6f5ca126e9767 df3965f137cf6f60c56ebd7c8f246281fd6dc92ce23a37e9f846f8452c884e01 -hash_to_ec afa9d6e0e2fb972ee806beb450c2c0165e58234b0676a4ec0ca19b6e710d7c35 669a57e69dd2845a5e50ed8e5d8423ac9ae792a43c7738554d6c5e765a7b088a -hash_to_ec 094de050bdadef3b7dbaeeca29381c667e63e71220970149d97b95db8f4db61b 0cf5d03530c5e97850d0964c6a394de9cde1e8e498f8c0e173c518242c07f99a -hash_to_ec 2ce583724bc699ad800b33176a1d983512fe3cb3afa65d99224b23dae223efb7 e1548fd563c75ae5b5366dbab4cb73c54e7d5e087c9e5453125ff8fbe6c83a5c -hash_to_ec 8064974b976ff5ef6adaade6196ab69cda6970cd74f7f5899181805f691ad970 98ae63c47331a4ac433cb2f17230c525982d89d21e2838515a36ec5744ec2d15 -hash_to_ec 384911047de609c6ae8438c745897357989363885cef2381a8a00a090cf04a58 4692ec3a0a03263620841c108538d584322fdd24d221a74bf1e1f407f83828af -hash_to_ec 0e1b1ced5ae997ef9c10b72cfc6d8c36d7433c01fc04f4083447f87243282528 6ee443ab0637702b7340bd4a908b9e2e63df0cc423c409fb320eb3f383118b80 -hash_to_ec 5a7aea70c85c040af6ff3384bcaa63ec45c015b55b44fffa37ab982a00dc57c5 2df2e20137cefd166c767646ecd2e386d28f405aebe43d739aa55beba04ed407 -hash_to_ec 3e878a3567487f20f7c98ea0488a40b87f1ba99e50bbfe9f00a423f927cbd898 697c7e60e4bf8c429ba7ac22b11a4b248d7465fc6abe597ec6d1e1c973330688 -hash_to_ec c0bb08350d8a4bb6bf8745f6440e9bd254653102a81c79d6528da2810da758e4 396a872ac9147a69b27223bf4ec4198345b26576b3690f233b832395f2598235 -hash_to_ec 6c3026a9284053a4ddb754818f9ae306ffa96eb7003bd03826eeccc9a0cf656e bef73da51d3ba9972a33d1afb7d263094b66ab6dbe3988161b08c17f8c69c2d5 -hash_to_ec f80b7d8f5a80d321af3a42130db199d9edcb8f5a82507d8bfca6d002d65458b6 aa59c167ea60ee024421bfbd00adbb3cbfc20e16bd3c9b172a6bef4d47ca7f57 -hash_to_ec bc0ffc24615aa02fafef447f17e7b776489cd2cc909f71e8344e01cad9f1610d 5c4195cc8dc3518143f06a9c228ae59ec9a6425a8fab89bfc638ad997cf35220 -hash_to_ec b15fad558737229f8816fcba8fbef805bd420c03e392d118c69bdf01890c4924 f5810477e37554728837f097e1b170d1d8c95351c7fff8abbbfc624e1a50c1b9 -hash_to_ec ec8c1f10d8e9da9cf0d57c4a1f2c402771bed7970109f3cf21ad32111f1f198f a697e0a3f09827b0cf3a4ffb6386388feda80d30ffffcbd54443dafcba162b28 -hash_to_ec a989647bf0d70fdb7533b8c303a2a07f5e42e26a45ffc4e48cff5ba88643a201 450fd73e636f94d0d232600dd39031386b0e2ecde4105124fc451341da9803db -hash_to_ec 7159971b03c365480d91d625a0fadc8e3a632c518acf0dbec87dd659da70e168 377bc43c038ac46cf6565aa0a6d6bf39968c0c1142755dba3141eeebf0acdf5d -hash_to_ec e39089a64fedac4b2c25e36312b33f79d02bf75a883f450f910915b8560a3b06 77efa7db1be020e77596f550de45626824a8268095d56a0991696b211cb329cc -hash_to_ec 2056b3c6347611bb0929dad00ec932a4d9bec0f06b2d57f17e01ffa1528a719e b6072c2be2ce928e8cbbb87e8eb7e06975c0f93b309dd3b6a29edaad2b56f99b -hash_to_ec 2c026793146e81b889fc741d62e06c341ce263560d57cd46d0376f5b29174489 8f1f64b67762aa784969e954c196a2c6610addc3604aa3291eb0b80304dfe9ef -hash_to_ec be6026d6704379c489fa7749832b58bdb1a9685a5ffb68c438537f2f76e0011f 0072569a4090a9ad383a205bb092196c9de871c22506e3bb63d6b9d1b2357c96 -hash_to_ec f4db802d5c6b7d7b53663b03d988b4cd0c7cad6c26612c5307754a93ebdc9710 f21bc9be4cb28761f6fe1d0a555ad5e9748375a2e9faea25a1df75cc8d273e18 -hash_to_ec c27d79a564c56b00956a55090481e85fbc837fd5fb5e8311ecb436e300c07e3a 1b1891e6abec74621501450cd68bb1eeaa5b2fffff4ec441a55d1235ff3a0842 -hash_to_ec a1e2f93c717cad32af386efa624198973df5a710963dd19d4c3ac40032a3a286 69c60571e3f9f63d2bfb359386ae3b8cd9e49a2e9127753002866e85c0443573 -hash_to_ec 76920d7b1763474bc94a16433c3c28241a9acdee3ff2b2cb0e6757ba415310aa c1b409169f102b696fc7fa1aa9c48631e58e08b5132b6aadf43407627bb1b499 -hash_to_ec 57ac654b29fa227c181fff2121491fcb283af6cbe932c8199c946862c0e90cb2 a204e8d327ea93b0b1bd74a78ffc370b20cea6455e209f2bc258114baa16d728 -hash_to_ec 88e66cfaef6432b759c50efce885097d1752252b479dac5ed822fa6c85d56427 6fb84790d3749a5c1088209ee3823848d9c19bf1524215c44031143dd8080d70 -hash_to_ec c1e55da929c4f8f793696fc77ff4e1c317c34852d98403bfd15dd388ee7df0df 2f41e76f15c5b480665bd84067e3b543b85ce6de02be9da7a550b5e1ead94d34 -hash_to_ec 29e9ace5aa3c5a572b13f4b62b738a764d90c8c293ccb062ad798acbab7c5ef4 bce791aba1edc2a66079628fd838799489ab16b0a475ce7fe62e24cc56fe131c -hash_to_ec f25b2340689dadacaa9a0ef08aee8447d80b982e8a1ea42cf0500a1b9d85b37d f7f53aa117e6772a9abc452b3931b0a99405ac45147e7c550ac9fcf7ffe377b5 -hash_to_ec 0cb6c47fc8478063b33f5aed615a05bcc84d782c497b6cc8e76ec1fa11edbfdb 7a0b58b03147e7c9be1d98de49ead2ce738d0071b0af8ca03cc92ceb26fc2246 -hash_to_ec 7bd7287d7c4b596fe46fe57a6982c959653487bea843a77dd47d40986200d576 343084618c58284c64a5ff076f891be64885dc2ac73fa1567f7b39fde6b91542 -hash_to_ec e4984bf330708152254fb18ecef12d546afd24898a3cf00fba866957b6ee1b82 c70e88b061656181fbd6ff12aca578fb66de5553c756ea4698a248b177185bc6 -hash_to_ec cefd6c3cb9754ea632d6aea140af017de5ea12e5184f868936b74d9aa349d603 4b476502a8a483aadd50667f262f95351901628dd3a2aac1a5a41c4ea03f1647 -hash_to_ec da5d0f33344ee7f3345204badf183491b9452b84bccc907602c7bad43e5cf43e 9561b9e61241625e028361494d4fa5cd78df4c7219fa64c8fede6d8421b8904a -hash_to_ec d6f0a4f8c770a1274a76fd7ae4e5faf7779249263e1aaecc6f815cf376f5c302 cd5c55820be10f0d38feb81363ede3716a9168601a0dd1ce3109aab81367d698 -hash_to_ec b6bf32491d12a41c275d8518fc534d9a0d17aade509e7e8b8409a95c86167307 4aae534abbd67a9a8f2974154606c0e9be8932e920c7a5e931b46a92859acf82 -hash_to_ec 0f930beaad041f9cefd867bc194027dd651fb3c9bda5944ececdba8a7136b6d3 521708f8149891b418d0920369569a9d578029c78f8e41c68a0bb68d3ad5df60 -hash_to_ec 49b1fe0f97be74b81e0b047027b3e9f726fa5e90a67dafa877309397291c06c5 0852e59dfae5ec32cce606c119376597bce5cd4d04879d329f74e3ec66414cd3 -hash_to_ec 4d57647d03f2cfbd4782fcc933e0683b52d35fc8d37283e6c7de522ddfa7e698 cbeb9ebfbbc49ec81fac3b7b063fecac1bb40ea686d3ffb08f82b291715cd87f -hash_to_ec 4ea3238c06fc9346c7421ff85bc0244b893860b94bc437378472814d09b2e99f a1fbae941adc344031bbdf53385dfdc012311490a4eb5e9a2749a21b27ce917a -hash_to_ec 0cd3609f5c78b318cb853d189b73b1ee2d00edd4e5fce2812027daa3fcb1fed1 0c7a7241b16e3c47d41f5abbf205797bd4b63fc425a7120cb2a4bf324e08ae74 -hash_to_ec d74ab71428e36943c9868f70d3243469babd27988a1666a06f499a5741a52e3e 65b7c259f3b4547c082b2a7669b2b363668c4d87ac14e80471317b03b34e5216 -hash_to_ec f6b151998365e7d69bcbce383dd2e8b5bf93b8b72f029ff942588208c1619591 6ce840ce5dfbca238665c1e6eddb8b045aa85c69b5976fc55ab57e66d3d0a791 -hash_to_ec 207751de234b2bd7ec20bdd8326210c23aa68f04875c94ad7e256a96520f25d6 fc8f79ab3af317c38bfb88f40fb84422995a0479cfa6b03fa6df7f4e5f2813fb -hash_to_ec 62291e2873f38c0a234b77d1964205f3f91905c261d3c06f81051a9b0cb787cb 076d1d767457518e6777cb3bd4df22c8a19eb617e4bbccd1b0bd37522d6597a5 -hash_to_ec 4b060df2d2854036751d00190ee821cb0066d256d4172539fdfa6fbd1cdfe1f9 59866e927c69e7de5df00dc46c0d2a1ddf799d901128ff040cebb8fd61b95da4 -hash_to_ec ac8daf73f9c609bb36bce4fdeec1e50be5f22de38c3904fabcf758f0fc180bc7 7d8dc4e956363b652468a5fecafd7c08d48a2297e93b8edcb38e595fdd5a1fde -hash_to_ec fef7b6563fd27f3aab1d659806b26b8f2ec38bc8feefad50288383c001d1c20f e6e42547f12df431439d45103d2c5a583248f44554a98a3a433cf8c38b11805d -hash_to_ec 40a3d6871c76ecc6bb7b28324478733e196cc11d062dd4c9265cf31be5cf5a97 8c55a3811c241a020b1be202a58d5defbc4c8945d73b132570b47dd7c019ccf0 -hash_to_ec 0cd71e7e562b2b47f4bc8640caf20e69d3a62f10231b4c7a372c9691cff9ac3c fb8e4e3de479b3bf1f4f13b4ed5507df1e80bd9250567b9d021b03339d6e7197 -hash_to_ec 40a4e62800a99b7a26e0b507ffb29592e5bdba25284dc473048f24b27d25b40a 90ae131d29ee4a71cd764ab26f1ca4e6d09a40db98f8692b345c3a0e130dc860 -hash_to_ec 1ddf35193cf52860bfe3e41060a7f44281241c6ae49cd541d24c1aca679b7501 3b4f50013895c522776ced456329c4e727de03575f6b99ae7d238a9f70862121 -hash_to_ec 014e0fa8ce9d5df262b9a1765725fde354a855de8aef3fc23684e05dd1ba8d34 3857f57776a3cb68721bcb7f1533a5f9fb416a1dc8824d719399b63a142d24de -hash_to_ec 09987979b0e98d1d5355df8a8698b8f54d3a037d12745c0a4317fe519c3df9cc 32a181e2b754aeced214c73ac459c97d99e63317be3eb923344c64a396173bca -hash_to_ec 51e9e8ec4413e92dbaaba067824c32b018487a8d16412ed310507b4741e18eed 0356b209156b4993fd5d5630308298429a1b0021c19bedecb7719ac607cfa644 -hash_to_ec 14d91313dfe46e353310e6a4a23ee15d7a4e1f431700a444be8520e6043d08d9 6f345f4018b5d178d9f61894d9f46ac09ff639483727b0d113943507cee88cfd -hash_to_ec 0d5af9ace87382acfffb9ab1a34b6e921881aa015d4f6d9c73171b2b0a97600d a8dbf36c85bebe6a7b3733e70cd3cd9ed0eb282ca470f344e5fcf9fe959f2e6e -hash_to_ec 996690caac7328b19d20ed28eb0003d675b1a9ff79055ab530e3bf170eb22a94 14340d7d935cffce74b8b2f325c9d92ce0238b51807ef2c1512935bb843194ce -hash_to_ec ad839c4b4c278c8ebe16ff137a558255a1f74646aa87c6cd99e994c7bb97ce8a d4f2da327ffded913b50577be0e583db2b237b5ca74da648e9b985c247073b76 -hash_to_ec 26fc2eeeee983e1300d72362fdff42edf08038e4eee277a6e2dbd1bd8c9d6560 3468b8269728c2c0bfc2e53b1575415124798bc0f59b60ea2f14967fc0ca19ce -hash_to_ec db33cecaf4ee6f0ceba338cc5fabfb7462cd952a9c9007357ff3f0ca8336f8bc 0bab38f58686d0ff770f770a297971510bc83e2ff2dfead34823d1c4d67f11af -hash_to_ec a0ee84b3c646526fb8787d26dcd9b7fe9dc713c8a6c1a4ea640465a9f36a64df 4d7a638f6759d3ec45339cd1300e1239cca5f0f658ca3cd29bc9bdb32f44faf0 -hash_to_ec 6a702e7899fcf3988e2b6b55654c22e54f43d3fa29de19177bdff5b2295fe27f 145d5748d6054fb586568e276f6925aef593a5b9c8249ad3dbef510af99b4307 -hash_to_ec 30ce0fd4f1fac8b62d613b8ee4a66deef6eb7094bd8466531050b837460f6971 f3aa850d593ba7cef01389f7e1916e57617f1d75cd42f64ce8f5f272384b148c -hash_to_ec 3aa31d4ad7046ad13d83eb11c9a6e90eb8483a374a77a9a7b2a7cc0978fefa76 2fe0827dc080d9c1e7ec475a78aa7ae3c86d1a35f4c3f25f4a1f7299cacf018a -hash_to_ec 8562a5a91e763b98014523ebb6e49120979098f89c31df1fde9eb3a49a15b20f ae223bf85e2009a9daf5fd8a14685e2e1e625fc88818b2fd437dd7e109a48f59 -hash_to_ec ccf9c313a47b8dbf7ce42c94b785818bc24134d95b6d22acc53c1ec2be29cf27 3e79fce6fe5aa14251b6560df4b76e811d7739eec097f27052c4403a283be71d -hash_to_ec d1e33cd6f8918618d5fb6d67ad8de939db8beaec4f115551eac64479b739b773 613fffcbe1bf48bb2d7bfd64fd97790a06025f8f2429edddb9ac145707847ecf -hash_to_ec 81eaeced34dd44e448d5dafa5715225e4956c90911c964a96ff7aa5b86b969bc 8f81177495d120a1357380164d677509b167f2958eb8b962b616c3951d426d8c -hash_to_ec 2bc001a29f8eab1c7377de69957ba365fb5bdaf9c2c220889709af920dfe27d3 9bcb3010038f366fa4c280eed6e914a23bfc402594d0b83d0e66730a465a565b -hash_to_ec 6feeb703c05e86c58d9fc5623f1af8657ecd1e75a14d18c4eedb642a8a393d16 6544628ba67ed0e14854961739c4d467fcf49d6361e39d32ea73dabeae51e6c3 -hash_to_ec e8ff145a7c26897f2c1639edd333a5412f87752f110079f581ccdc87fcce208c d4b5a6e06069c7e012e32119f8eda08ff04a8dfa784e1cf1bced455a4d41d905 -hash_to_ec 80488131dcb2018527908dbf8cdf4b823ef0806dc1d360f4da671004ef7ff74d 9984a79d9fd4f317768b442161116eef84e2ca49e938642b268fd64312d59a27 -hash_to_ec d8c4ca60446849a784d1462aa26a3b93073ff6841cb2da3ef52ab9785b00b1fd da5ec1562e7de2382d35728312f4eea3608d4dba775c1c108de510e1ce97d059 -hash_to_ec 68645728dfc6b9358dfb426493238ba38f24a2f46a3e89edb47d212549939cb7 d3253aa7235113dcc1b577d3bb80be34f528398815a653dbdbacbcbdfd5887a1 -hash_to_ec 4e8eb97ba2d1046e1b42e67530a61441e31c84e5e5e448d8e8dbe75d104eaccb de94f73e83222aa0e39b559d4fef70387b0815b9b2f6beff5da67262d8f0eb3e -hash_to_ec 104ff03122ffdf59b22b8c0fe3d8f2ef67d02328e4d5181916d3d2a92f9a0bb7 1517ccf69c0328327e1cf581f16944ff66bc91c37e1cd68a99525415e00b7c9f -hash_to_ec 80f23aae7356ae9a2f9f7504495a731214d26f870fb7df68fdc00b233494156f 7aef046b0a70f84e8d239aa95e192b5a3fffa0fae5090c91273e8996beca9e38 -hash_to_ec 2424b33235955a737ebddbf1c6c59cd8778af74da3bd3e658447666a2ab2f557 d19e2be8d482950fbdae429618da7a9daedb8c5944dea19cd1b6b274e792231b -hash_to_ec 0adc839d2b8f099e4341a4763b074c06318d6bcbd1ec558d20a9820c4a426463 cea5da12a84e5c20011726d9224a9930bec30f9571762dd7ca857b86bd37d056 -hash_to_ec 46c84d53951f1ba23c46a23d5d96bf019c559aa5d2d79e4535cfcdb36f38ce25 2a913a01a6f7dd78a43cdd5354d1160d9a5f0d824c489a892c80eba798a77567 -hash_to_ec 99bdaaf68555ccdc93d97c3a0fb4c126a1aa8b1202194a1a753401a6cae21055 1f645efe173577a092f2d847cc966e28ba3b36397fe84c96dfa4724ed4fcfdf9 -hash_to_ec c540ff78f1e063ad26ffa69febb8818c9f2a325072c566091ad816e40fe39af4 de7a762262c91ab4beccc0713233cb91163aec43e34de0dbcfad0c431e8a9722 -hash_to_ec de8b1ff8978cd5e02681521542b7b6c3c2f8f4602065059f83594809d04e3dda 290601e75207085bff3e016746e55a80310a76dea9ef566c24181079c76da11c -hash_to_ec d555994c8a022e52602d2a8bdd01fc1bfa6b9ab6734ff72a1bd5f937de4627f8 5f6794e874f48c4b362d0a24207374c2d274e28de86351afc6ddb95d8cc2fd62 -hash_to_ec 19db72f703fe6f1b73f21b6ba133ae6b111ae8cc496d3aa32e02411e34c0d8d7 42f159f43d2d62b8cf8a47d5f1340c5cf070e9860fc60de647c55d50fe9f5607 -hash_to_ec 23a87a258c2a5d1353aa2d5946f9e5749b92f85e3c58e1d177c3b6c3dcac809c e5685016f79d5e87d1fecb3e2a0fe64e4875f7accd2f6649d7f6b16317549cb1 -hash_to_ec 43e1738d7d1b5b565f5fc78e81480f7edf9a4dc18f104fc4be95135b98931b17 650f5b682e45f2d0c5d5e8bcfd9e0cda7d9071b55ecbfaf5e3b59941cd7479f2 -hash_to_ec a9d644de0804edf62dee613efa2547e510990a9b7a987ebe55ec74c23873a878 52ad329f88499a4f110e6a6cba1f820012d8db6ccb8f6495ab1e3eb5a24786e1 -hash_to_ec 11f2b5d89a0350d7c8727becf0f4dd19bd90f8c94ff207132ab13282dd9b94e6 b798a47bb98dc2a8f99deaf64d27638e33a0d504c5d2fbee477a2bc9b89e2838 -hash_to_ec 5e206e3190b3b715d125f1a11fff424fb33e36e534c99ddde2a3517068b7dcc4 2738e9571c96b2ddf93cb5f4a72b1ea78d3731d9555b830494513c0683c950ca -hash_to_ec efc3d65a43d4f10795c7265a76671348f80173e0f507c812f7ae76793b99c529 cf4434d18ce8167b51f117fe930860143c46e1739a8db1fba73b6b0de830d707 -hash_to_ec 81f00469788aad6631cf75b585ae06d43ec81c20479925a2009afac9687dff60 c335b5889b36ba4b4175bb0d986807e8eedb6f6b7329b70b922e2ab729c4202a -hash_to_ec 9ef5ff329b525ee8f5c3ac38e1dba7cb19985617341d356707c67ff273aed02d bef9f9e051ba0e24d1fdf72099cf43ecdd250d047fb329855b5372d5c422db9e -hash_to_ec 3fa1401bd63132cf8b385c0fa65f0715ba1fe6161e41d59f8033ae2b22f63fa1 8289a1cb3c2dae48879bb8913fafe2d196cc2fdab5f2a77607910efd33eae6df -hash_to_ec 6559836fd0081fa38a3f8d8408b564e5698b9797cf5e15f7f12a7d2c84511989 28d405a6687d2ecc90c1c66bf0454d58f3fa38835743075e1db58c658e15a104 -hash_to_ec 8e0882d45f0e4c2fb2839d3be86ff699d4b2242f5b25ac5a3c2f65297c7d2032 2771fdcf9135a62007adb5f0004d8222f0e42f819c81710aa4dc3ab2042bebf3 -hash_to_ec 1d91dc4dd9bd82646029d13aca1af96830c1d8a0400ddebeb14b00c93501c039 7792c62e897f32cbc9c4229f0d28f7882ceeae120329a1cd35f76a75ac704e93 -hash_to_ec 09527f9052acbbdd7676cbbd9534780865f04a27aaadad2b7d4f1dac68883cf0 b934220cde1327f2dc6af67bcb4124bf424d5084ef4da945e4daad1717cd0bb8 -hash_to_ec 2362e1abe73e64cdd2ca7f6c5ea9f467213747dd3f2b7c6e5df9cb21e03307d7 676b7122b96564358bbaaf77e3a5a4db1767e4f9a50f6ddd1c69df4566755af9 -hash_to_ec 26c2dd2356e9b6c68a415b25f91d18614dc8500c66f346d28489da543ee75a94 0f4fd7086acd68eb7c9fa2410e2ecf18e34654eb44e979bc03ce436e992d5feb -hash_to_ec 422dc0a09d6a45a8e0b563eeb6a5ee84b08abd3a8cb34ff93f77ba3b163f4042 631f1b412ff5a0fccbe53a02b4a3deaa93a0418ed9874df401eb698ef75d7441 -hash_to_ec ceecdf46f57ef3f36ff30a1a3579b609340282d1b26ab5ddef2f53514e91bab1 9bc6f981fe98d14a2fc5b01a8134b6d35e123ec9ab8a3f303e0a5abb28150e2e -hash_to_ec 024a9e6e0d73f28aa6207fb1e02ce86d444d2d46f8211e8aaab54f459db91a5a 5fb0c1d2c3b30f399102104ea1874099fa83110b3d9c1fcfffb2981c98bf8cdf -hash_to_ec 5b8e45e269c9ccac4c68e532a72b29346d218f4606f37a14064826a62050e3a8 c7be46a871b77fc05ce891d24bd6bd54d9775b7ef573c6bc2d92b67f3604c1d1 -hash_to_ec 9a6593a385c266389eef14237874b97bdcd1823c3199311667d4853c2d12aa81 9f55ee9d94102d2b9c5670f30586cf9823bf205b4d4fe088c323e87c4e10f26f -hash_to_ec 27377e2811598c3569b92990865d39b72c7a5533e1be30f77330863187c11875 abd82bc726f2710a8b87e4c1cf5a069f0ae800de614468d3ff35639983020197 -hash_to_ec 7cacfaa135fb7d568b8dce8ea9136498b1b28c6d1020af45d376288d78d411f0 229fccd49744c0692508af329224553d21561ee6062b2b8a21f080f73da5bd97 -hash_to_ec 52abd90a5542d6496b8dec9567b020f30058e29458d64f2d4f3ad6f3bfc1a5a0 874e82ced7cf77577b3374087fb08a2300b7f403de628310c26bdb3be869d309 -hash_to_ec 5c8eebe9d12309187afa8d0d5191de3fdb84e5a05485d7cd62e8804ce7fdc0bc 12b7537643488aa8b9dcc4bae040cd491f8b466163b7988157b0502fb6c9177f -hash_to_ec 6ca3dd5c7a21a6bf65d6eefbe20a66e9b1d6b64196344be0c075f47aea48e3aa 5e1d0705ee24675238293b73ab1d98359119d4b328275be2460cc6ee4d19cc88 -hash_to_ec d7e6cd0d39b4308c2a5ee547c4569c8bb3887e49cedece62d218d7c3c5277797 793dc4397112dfd9a8f4e061f457eb6d6fbb1d7a58c40bad5f16002c64914186 -hash_to_ec 9cb6de8ba967cca0f0f861c6e20546f8958446595c01c28dae7ba6cfa09d6b14 ba1a2f7502b58fee3499c20e35fa01bb932e7a7c4a925dc04fbf5d90f33cfb5e -hash_to_ec 8ef9c7366733a1edcd116238cdbd177d61222d5c3e05b30ef6b85014cbcb6b79 8fc89664722947164ac9b77086aed319897612068f56ecd57f47029f14671603 -hash_to_ec 7f317a34e4fb7de9f69cb107ffc0e57fd9f5c85b85ccb5319d05cebfc169924a 4b71c42339c73db7d710cd63f374d478a6c13bdc352cff40e967282268965ba7 -hash_to_ec 15beef8d9687b92918a903b01d594859db4e7128263c8db0cae9d423ff962c1e cd75e6323952f6ac88f138f391b69f38c46d70b7eda61f9e431725b6f1d514a5 -hash_to_ec 7a1c04c9af8fc6649833fe81e96f0199fcfe94959256cbe1490075fc5be0904e 0368270cd979439ae0a9552a5d6c9f959e4247fcf920d9e071464582e79c04b1 -hash_to_ec c854c583d338615f85f69061e0fa9c9d7c5bbbfe562e8774fef3be556fe8bb63 061620171d7320f64bee98414ff7200a1f481521d202fb281cab06be73b80402 -hash_to_ec 0fb8af5aba05ad2503edf1cfad5a451da088e7e974772057cd991a4e0601a3eb d3cbc20384a4420143fcce2cb763b0c15bec4f3267d1bdad3c34c1ee6b790f5e -hash_to_ec 9a251cf59e84a9da5630642f9671c732440caa8fcf4c92446a7e5f5ef99da46c 9b9679086a433f2077f40bcd4c7545fb5cc87e7dbb8bba468d53cb04a74361a0 -hash_to_ec 8c632e357cef00e0911eb566f8cc809136b3f5ac1e82d183e4d645cef89fa155 5e06b0f4f278fa1ccb5431866e0b35171cdb814e2e82b9189ce01d8d8a1b2408 -hash_to_ec 4aa4c31463475086a5d96b3ff550340567ab3b4a86fa3f01cfe9be18bc4dcb54 76a2916cfc093f27992e1f07b50f431d61d58e255507e208cd29ea4d3bc56623 -hash_to_ec 1d33d9aadb949346e3c78d065a0f5262374524f4cb97a7390c8cdaede7ca6578 9ad2f757f499359903031adea6126c577469c4e834a2959e3ac08ee74b13783c -hash_to_ec d9217b9a070df20c4d2f0db42ff0bb36bfba9f51b0b6df8fdfe150405dce4934 65a843c522b4b8ec081a696a0d2dd8dfdfea45db201de7a5889a1446c6dff8c7 -hash_to_ec b665b2ca8a285e44ba84e785533b56496a5319730dbb95bc14d3bdfece7544dc 8a804cd13457497b0a29eeca2cecfaa858766ec1d270a0e0c6785b43fd49b824 -hash_to_ec 43b5cbcc21b3404bca97fa9a661940fe64d40f3ca569310e50b1bb0173c4d5ee 6c12fffb540d536060bb8b96cf635c1b2cbaa4d875a8d2fb0bf79a690363df19 -hash_to_ec 11c58f20562c00dec5bb4456be07cd98186837e9af38d50d45f5e7b6f0f9000d cee76b567586f66dadd38c01213bfc1a17d38e96a495efb4c26063dc498ba209 -hash_to_ec b069a980b51d8e030262db0b30069e660f4a3f6f8075d1790c153ba12b879f8b 262391b00bdee71d1d827b2cfe50b46c29e265934dc91959bd369aca0cc6444e -hash_to_ec 75274bfd79bf33eb2f9ab046d34528af9a71811e7e3d55c20eb049c81ac692d8 cb93c850e36896fe6626e97c53652af6736ec3ba0641c7765d0cca2bad2352de -hash_to_ec 5cdb6a24d9736a00f197d9707949fedc5405f367744fe8c83b7cff650302b589 8b4ac03123fab9275dcf340345a1b11fba48ef106d410ba2e0e6f6457037a419 -hash_to_ec 07fdc85f809f95a07b59b084402bf91c512ebbe05c7657d6ba27a9e7e121e3e2 61182b3def063630e11de648a278032bcb75949f3a24ef5a133da87830ae5c4e -hash_to_ec a4188ca634cbb796f9927822e343d7b267e0a609c1a0ffa4dcf3726b9ffcc8a2 a911e4899fda28fd6337d708d34553ac5e810ee4938f6f7d9d6e521cab069edb -hash_to_ec 3c128ec5c955ea189a5789df2c892e94193a534a9d5801b8f75df870bc492a69 59eef5ee9df0f681df5b5c67ead1f06b059a8a843837b67f20cce15779608170 -hash_to_ec 51a4cc7ec4a14a98c0731e9de7f3ce0779123222d95455e940f2014a23729ec8 105863ccda076af7290d1bf9ec828651dc5811159839044d23f1c3e31a11c5e2 -hash_to_ec 1b901a31acbb7807c3309facdc7d04bc3b5a4aa714e6e346bd1c6ad4634e6534 01b3c0000b6c6b471c67c6ab3f9c7a500beaea5edb5c8f2b34df91b69ff67f21 -hash_to_ec d2f2c8d79cfa2e7cb2db80568ba62ca0576741acfbe5e2baa0d9b3c424a7c84d 7df9d9088022bd1ce6814d6f8051eef27a650ee38e789b184da2691efd27139d -hash_to_ec 04dcb7644fdfc12d8e34d6e57d7769db939b4a149ed2b81aa51a74ee90babe19 6cff0ab2dd3b32ba1bd1a78e3661722f3f10003a01ce83e430970557decedb2c -hash_to_ec 222798c6841eeaa07e7b7e29686942d7c7f9afc38d09360c8e1f52f2b7debd12 133e3a04ec82aa9b8dbbec18cadbafff446d1270bf7c6f3f97ddd3906dae2468 -hash_to_ec 4f7277c3ef247a0689b486ad965f969c433fc63e95d7310e789c4708418ccabc 7e0f2c984dd3cffb35458938c95fe92acf2e697aed060b0e3377c7a07e53c494 -hash_to_ec 359b4d6709413243ae2c5409ea02714a9f8961bbbb64a91e81daf01e18c981bf eab69af2cb7f113ad6a27035c0399853d10bd0b99291fad37794d100f7530431 -hash_to_ec 6cea3c6a9eb38f60329537170aa4db8dbb869af2040061e53b10c267daf6568c da9a97f4fa96bd05dade5e2704a6a633ba4dbe5080a1e831cda888e9d4f86615 -hash_to_ec 3dddecb954ef0209bcf61fd5b46b6c94f2384ef281c48a20ffee74f90788172d af9899c31f944617af54712f93d1a2b4944e48867f480d0d1aec61f3b713e32d -hash_to_ec 9605247462f50bdf7ff57fe966abbefe8b6efa0b65b5116252f0ec723717013f fc8f10904d42a74e09310ccf63db31a90f1dab88b278f15e3364a2356810f7e9 -hash_to_ec a005143c4d299933f866db41d0a0b8c67264f5d4ea840dd243cb10c3526bc077 928df1fe9404ffa9c1f4a1c8b2d43ab9b81c5615c8330d2dc2074ac66d4d5200 -hash_to_ec f45ce88065c34a163f8e77b6fb583502ed0eb1f490f63f76065a9d97e214e3a9 41bd6784270af4154f2f24f118617e2d7f5b7771a409f08b0f2b7bbcb5e3d666 -hash_to_ec 7b40ac30ed02b12ff592a5479c80cf5a7673abfdd4dd38810e40e63275bc2eed 6c6bf5961d83851c9728801093d9af04e5a693bc6cbad237b9ac4b0ed580a771 -hash_to_ec 9f985005794d3052a63361413a9820d2ce903198d6d5195b3f20a68f146c6d5c 88bcac53ba5b1c5b44730a24b4cc2cd782298fc70dc9d777b577a2b33b256449 -hash_to_ec 31b8e37d01fd5669de4ebf78889d749bc44ffe997186ace56f1fb3e60b8742d2 776366b44170efb130a5045597db5675c6c0b56f3def84863c6b6358aa8dcf40 -generate_key_image e46b60ebfe610b8ba761032018471e5719bb77ea1cd945475c4a4abe7224bfd0 981d477fb18897fa1f784c89721a9d600bf283f06b89cb018a077f41dcefef0f a637203ec41eab772532d30420eac80612fce8e44f1758bc7e2cb1bdda815887 -generate_key_image 8661153f5f856b46f83e9e225777656cd95584ab16396fa03749ec64e957283b 156d7f2e20899371404b87d612c3587ffe9fba294bafbbc99bb1695e3275230e 03ec63d7f1b722f551840b2725c76620fa457c805cbbf2ee941a6bf4cfb6d06c -generate_key_image 30216ae687676a89d84bf2a333feeceb101707193a9ee7bcbb47d54268e6cc83 1b425ba4b8ead10f7f7c0c923ec2e6847e77aa9c7e9a880e89980178cb02fa0c 4f675ce3a8dfd806b7c4287c19d741f51141d3fce3e3a3d1be8f3f449c22dd19 -generate_key_image 52dd3e80863f42fdf0599bcf742f179957f05c7754f1fb7cd2f81d4110f49fb8 e2cf53627279cbee2b4bdf130dcd21161db64728751d9ef5818b84727dc84208 65c173331469b38da1d11bae02b1c784699e1104f35ae4a9baf94876f279a694 -generate_key_image 147c2da0816929e8b575ea7a460d71946b7a40af3f8543c59779ddd09ce6e49d b4d6833a86ad47e247bc75410c42b0add4b1607b94b99aca1021cff4a9348100 8ed434593f60a913982ea6497ccc318910783b0e66f257597fff0e168df97b98 -generate_key_image 87abd513a1ae21df5cf9a366f0c27ce04bd77a7aeeab6416a293d62567aa82c3 3f0a75ef880a9ce26dc5337061ca80f740881fbd477fb241743294d748a5790c 3255ebbdbbfa43743d6d71b21482c27d096c91fab7acae1dfdef8e314f00ab3c -generate_key_image b0094bbc2a4904b502b2d1ff2e721e7b09fc95f1f5d04934171527ffaf9bb2a3 b4ce1cfcdf63b20d109de05929aba2362a18b015f9ed3d26e19f54dbd0580102 66536c55e63d78dc98546fed91748558af1c71ffbf35e799f90afb1479879a74 -generate_key_image 7d8e1de8ff2a245e57ef8bd583805a841f8892bf2bdf21a22ebea3ecd80a5e0f 8419bf13eb52a01b430f7e2e9bb52348f3692bd35079ac50afe041dcafbfc602 0057bf1e34dc258abb3952d6ac192732d1176f5a18806b1afafee45d4ff1c9cd -generate_key_image 5475ecdb7459c710d5f9731723ca8d67976ca938295089d497ffa8190582ab17 ca4af3d2d5a78bbcbc1fbee9ac0a09ded6941510f828356be4ed6091e41c370d 8e423f22d1ed3ae8c0edd6ae43ffca962bef451f9a2cb91681e09647f05d81de -generate_key_image 53de20a3ef17dd77b415138798aa6219ad6d939b90823115272d768968d383c8 064d5cc8a3f12ba90ac9eacc8df20a5bfcc3f369ccfe0eef19886461f1f70706 84b776471b8e66e99484a0bc4e3a9e34a011d709de78bb940d9e6722d6607d36 -generate_key_image 3f17986c52e84485b1bdeab93bed019fbc4549df3d3b7f33373e30406b672755 6cd3d64c132ab677e0bedbf22a2c5e6442a5db44a4adc93be15860ff3c5ae803 cd6b1de475ab38391f2aca40bc4b8b9865efa508c1d5425cf059a9cc0d53bccc -generate_key_image c12ff64e6109e9938c92f41bb0408ae714d07d949f58ee6cdd7c04c71cd8b0d7 96ad19d850c87f03752cfe7c3fa1e75141f235493c39b85f0bafe1a84144470c 05f403ba20d579704f60b6136870a361e5a4c98db88a967ee358edb72c95eb99 -generate_key_image 208811540cbece3c18c15ae0e257b8a3e2d967896c5f77a21118077737aa6132 624928ea7985fc1ad70d09c4e66b6952161b97f9dbf7122f97865a2d2220d301 19df8f4878d23b0dd4654e8dbf24feec39afe16dc9ec057d45440e1ead6a720a -generate_key_image ce9b99b074600f3154a8781b96a1c37431fa9b0f375b1c647a55f2c2af6fe91d 5f94f690005da88893276ea4059b01bdaf8d730027a622f6e0c2944857370000 e56d4ca61d19373209a1da106cec27143ce63e3fb17e0c8b62331204358ededc -generate_key_image 3827b75e0466dcd3b25bd9aea036f5ae3a57f5cf88638623da5cd8365e17a1f6 441dbc60354f5ad20982613fea34b92ac69a2c279192ada9be456e4b8687c404 d1179455bacfcea133f48cb7d0e92bc28603f918acd96047194e80da72b56640 -generate_key_image 19e40a25e4c46327adec42a70143e1a36e7aef8ae8f65eb958253089507a9502 b11b1411d842b29c5b5ee9916eb5125c5ec314978a4e30ef29a699c5fee7e20b 183a32a4bbc5d2111eefc19f2262d1d5f2c4b7045d9a8c6fc6d312388848ef89 -generate_key_image 6928f07db5f74b877c9daf195c682909e7fb37990bb98b7913a2787aad1a3dd5 0f0b9116d376480cdf31cb600c0f84f29143f01653e2e2055657431ab60eae0f 0bd7cdedb3464490a1d50e4bd1022786f5c6b50959a0894e0bfa2cfa8e73b8da -generate_key_image a075ff9d0398ebe7cfc9510e62305ff279b6815be9b4812c6c27ddcf02028a6c 09ccfd540adcb7cc0163a2cf4a55a5e82e4ab55d9f26612171f242f05ca9a00a 77f2b30c7ec64013206d83c61dc08322d07267950cbd7299f1d5b1124fbc0213 -generate_key_image e73fdf5e701354d5615028da5b2f0a7847afb4fe22e499fdb9a908d6f8ec69dd 26011f012153eb633ccde0719239ecd9f17c203ba853b66fcabf2c9dcf5e1e03 c4fb45b501f814c8a3b77d3241c0f407a5385ca451420ad896a9b91a6729ffce -generate_key_image 74ee190ae0572bc8c21bb826e95f5ddab29c51cf5b4f45246cef9b7a1374d105 c269bef6336e12acce13f85acb6d3c14f8f64b7f262e791e0eee32ebab6da70d ee2cb49823ee2bd686e8e9d1c89ddf1084af34d09c30428b23d2ecb5d0edc7b2 -generate_key_image e676ffe4c8f7d5cdbecd1d5adc10ba8044f1a9233b29389359b59c70a11ea3d6 86d1359ed2bd41fc6454a503d601998cd81ddf90a3dbd917dbf39eb536fa0a00 b51a243a850d3adb48cf526ee4e560096c0832142b2f9e79baabaa3b1ca8971f -generate_key_image d98816b6541587f57879cdfbf010ebaa3415610be81e55910a63e16994421eba 3832674e35ba51a9ba7322a08f186ccc6f7a9e9c1b48df74932f9627b8f43703 f2854c388f632bac0cec5db34a50c3e3e3f191a0b6482437554f7471318336e5 -generate_key_image 43be65596cbed853e51f407ef9ac18a928a5e0df265570ee76a5333d86fa9760 fb41ffa7e7a32694e6d5d2df75eb2eb3d2db24402fd0279ee07e5e607cb87b09 be3ab088f319520fe75894b6b3506eab099c9deacfe809d3e8b91e66bcddd572 -generate_key_image 20ebc98aa8ced4be7ae0ea5f49c4d5f7501e3a9dacf5e34dc4fb434f9e40e52a 03a151dc7238c91981df0187da9d7f51012fcc3f0d431f02bcf5e99b5a73b204 1ff12d06019ff37ac29c1aee5d84fed9363518f37a6b016ad09e66147e302776 -generate_key_image 1d9ee4f511889c9a38890bbb4278b4e1e04364bf7a8f6170d6143c0dc4341a02 984082f21c012e6f047e2b2b31cb812729e7bb1653c70e9851ec9236fd309b00 89a7a836251442536bc8b20f8dc63bc3e3e6b281dc858f9ac7a8c141eccbea1a -generate_key_image 829e3d99ae8c4a6610fb885808d81aa0792a5c88ac0fd2970c3c89b821eede45 736b88d78fa09a5badc5dec77de73de77bc5a4c5fe663538f595d6c743702d0f 263dd98c984f4c5ffd6f33bc6881293f4244a1efa4e1e6bd247ffc9adcc9f266 -generate_key_image 361f2937f806da92b8b12ffcf9c6b18719e1b1cbc77a6fe6f5bf73e8e3fee46b 22a151ca89aeabd7496091dbc8cc219f1b33c572ebe0ddb2885bc1888a22ba04 0278472a6bd9ebdfe1c6439796472a601334a8bf734f8a67a9f8fdfb7608255c -generate_key_image 607ed89cd124a71c95a53167e06c74cae423ee4806eec9dd7a75cede69d41b94 928d423bd736d7d3dc4cd7322f4037ae448736d457fab2c0520e4d190b9edf01 6f07c42c5e2014c53dc7f0812341b53dbc60a6605437236d4aa814e47cf29d06 -generate_key_image d9b23ffa3476ff864bbe124ad498c0d0f11116fa7bf0bd17799c319ee6058033 cad5c699792665d69416f69d6480e419ade54b994e4c91dd200ec15ebb8ba80b a669dbc4554982f7ee60f1544acb5202b5145b5fa304f5ce64cb79483f8d2933 -generate_key_image 64edc37df0be2592841a6917934857dbea8476fbd4c2fc5e5db08783d7d15e20 971ad6a5019de3d1bf7a9a14c563aa35bd5f9c3e12fe092326a839c4caa53602 14a09f7ab1e7f3ee37a1a53f1570e084fb6ac7196db5860395841223e7c6818d -generate_key_image 4e5eb19ea5e4d279889954356d1cff72ea716cc1c61efff27a629049d0a4c238 d778baf5adf5585079fd40e5f28486325dbdcfdd4f831a5face703a06cffac03 ac6df2df00d6a043ed593c922643fbdc1a335a99f31c035bbaac24542ff8b5ca -generate_key_image 815b24cefdfe660e18307a2cb092f05e5738c5ea231f49bd27c58c2a63210917 bfc75313d7ac80b4a677b488ddef363abef7f8055a752d6c8eb3ce55e269c508 e51cfa404a27acb4ac88e58b9e4ea0d301a8e28a24387bb3f4ce186e582e4ec3 -generate_key_image fab3904c39bdf79bbf760d22ed34c256204dec54f07f18692719485c19f914ec 0380e9bfc3418ce2ffdd16ee6973264fc8a1d75b93b2d5711cc97d22bf132c07 fd1866ebc3f8f447ecb3c556373e8f1a9c21ab10c1e0f546892a8b31c876550c -generate_key_image 22fca0461e157f3f9eb68f9c713dc13e693fe5dff1e4fc7694595e140c94f6ea 8712456a39a3aebb719ff8ce01a8800ff7e2f3aa55be0fbc397773c0efad7207 a9530b9e3fe6b840f8373127adfbd503bcdbe929f127cbcdc24353c8c62ef642 -generate_key_image ec9c6d0bda135bc6f3815a87b9aec6ceb90c11dd91cc4c0c6af9d5c33a096441 ae5128f6bdea9b6f10f515183bae2d5256a174f98bc310b9da8eb9fecfa84c01 d19f18ff358c75d5bf09ec63b279d7edccd16b7c09dbeb7e96b493b41026da58 -generate_key_image 382b72e8f5fd0542ede977307cf089878d3b54885915dc441829af94de4fdae0 56917f487974b22da20e6e1db9762ae3cd0a68037cec49c84030288751ba9401 9844de8a0e23fb95d432a809c12e0e65ad2e6de4aae93de7be230211767e9e7e -generate_key_image 22f9cdb34c7df3de4ec58e22a22374ae0e54b5d6fe16659f320229d45d134f7f 658d8408e0d615b54115c2d4b8a2954a1d4c5dd1663d1e580425c1fa9f823a0a d2c85c620301075651ad56f5c3fcc3ddf2ebf738f538edbe4bc479f1278c8771 -generate_key_image a9ddb7ef082930c01bc6233fbc9e6515405ed180a64d1c67c029362bb7b2aad5 cb03f744c8e49619e86c6d350e07d5444c95f84f06a962f48eca57ebdc31d609 e71a07d29a6ea91dc5dc9b48cdd0908bddaf44c70537281df5354fd4f4f1b3ea -generate_key_image e97e16ba01802b5a9a8c5f63f32690129c06cb844618698eb6dfdbc042949d56 c362293ad45b766caeeaf69b94fec3d7d9a9b000143b634eaf0e0a2f1ee79701 1131eb1ac0c6e424a37228ee1102e3f26adffc1b39c120dff28172755384c84f -generate_key_image a7b159fb06c3a4ee3a0506f2f6a7f1ecb4c44d205fbc3a068f15dbee6a588e17 4eea9f3453d7bfbf1fcda219a2f9d62437c47cccb50cbf8a13d47dc5c81d9d0a 220d65f9739fdda7ef84784ed087a7a3d6946f6e71d31ed34b86fb9cfc78b6ee -generate_key_image 4615ef74598eeb124b23e18538c97ecb5b901b20f4bd6a31b167111c0e4a7790 b7ffe6541bafad04e33f00beda714d361f245c4a49573d47fc8bfb0d3fd33206 e9fbb255b63a35124f01fd7b7c05f59721b0d4cdb051f1a5c205aaf90fe90e34 -generate_key_image 765ad846af0dfcf33f0b5b5602b3eb9148551af20ed2fa4461efbe48b7b2bd62 071f44123a457935c550b131706e9b4b99dab7e1b3da7d8d9d76f1b9e26e9a08 4c7d3a84476e401dc4e03b48e0c50dd233d90bd7147034a30c994796452177e8 -generate_key_image 8f5b147c004337486e3be173e502aeb4a4fdd28092a484e5d93a20331abff99a 97f392a150e4c101888acb89d7313b75cd634790b01427bd708b3065e26d420e c056c1f217fdbf28d1060f6b26e54c515f1a9264546a4d33edacc71f008c0bca -generate_key_image 6a153197857a9622147701e5f6f8cf9fab3207ca7596d9bae04133ed9ec4ba43 3efd0d7eb39db57a7a6d433eafab6ee415c17deb99cd98181a44faa32f7abb08 9e9ac6b4f41c0e906d284b4df9d059cc31eeb572e7c2b69cbddd5d7af90ab844 -generate_key_image 9796007087dd2be25c4a26e6cb79dea4f088d0ebaab167190ab0e47b95f60df4 778472556055289578e6d7de1cf6f453236db125d425d04fecd3dde90850900c 3ba75e34a40f951aaf6ca4e8c3ab36f4f1f550bb491c85c032774d61e33cb1a4 -generate_key_image 9241bb213817418ee2e957e9e93431c14394e410b53e63dfdb1973c25bee91ce 2f3771e76c57e7af88b36c75e0fec11b8dd6b14aa18a9e703c5e0d10cbbec902 a2de6a739490b2411014d930d8ad9745b9a98bc1d3cbb7f732ae8924b468f1c2 -generate_key_image 167ebe4d97c5b3a1b8765be5c354273e91a2d3b806ba1b5d0f2e7e3f7ca4f26a 5a5a06aa9a102f5be2dcd20a2bef58caa274fa1fbee6cd48a87ee0d402674c02 3f84499c83a86df4571da112a54e092fca1dd3f21a1db0d1fce59206c39a0df4 -generate_key_image e22ef8b2e47c768bc93fc3b321987de0801b36cf97dd72843a41332bf491e2b3 f0371b9ed1375ed5d8aa8116c2d8c8f7493b9ba53bc3f24f5f17b1353be3a807 eaf91d0a130955f178936257f2a1caace8120e31cf10b93dc3622717da754a00 -generate_key_image 887d25b2b437620cad569089f73f2ec3f002fc839c241c228318989ae49cb0ba 4cb63c1e78423aa86002117376cbc6fe6a1d70b248c730c01925a78ce93e7301 cb4dfeb2677abb5b5b794041be476055aa36de6fd01164f0e7cb33c964c2c4e8 -generate_key_image 29aa03c909c91ea3a506d09bdc769a1149556b85e36b6dcd04139c63c51e25e7 2cf5200d7291a524123d98ff5d64db61d41e12b8ed51a26f1c70656647786f0f 354dd8a01bbe125b0f5c684a3274158e47f25f7f5fb95b61abecd3fb06899cd9 -generate_key_image 43ca0a3a9cede1872f39c80127aac86cd2ee56634d0fa386f6facee3b4df9ca9 d5578abdf320431da1b17d6f893731b587349d8080a260a4d066debba0926d06 2f5eb902c148f19ca1c2323708b11d79da688bb752dfa6d98a9679801ab06b8d -generate_key_image f914a132334c7d4844f706066af75c6d59d6ce04ee04c86b72abc42312ad6db4 3aebe5563aade2de9a8573a9cee3c9600e6ee98a0745760e77849fd37d163b04 164aa088b6f224cad2c5a3a883769ecd274eeddccc88261cb9b35a6e7bb6fa4f -generate_key_image 170283adf2051ecf7510fceae6b661c92b58faa562f802ec765ce43e6cbb3062 a8aa1d85f1148d9ec7ce4fb7e189d170ebb41e4283a6f63269dac2aa80b4df0e 5d0d7677ed38d1db2603ebdb3dfd76e15f8c3fd07ee731257ea00f8974cb35b2 -generate_key_image b89c3d534b01dc1f2e54a0ae51fb93b20ae2cb3df9abe1d3b9ebb5f96c963fdc 9fbb1e56a676d4e83ff2e5fe873c775d9c53d64ae3fee9fd79ae6d61170c9c03 476b2b4f351e3612ef54b5dbd255ae2567fafafce770c6addd3e62c9ca015959 -generate_key_image 55aa2e22a836f0f45b6ca385b243fcbd0925a91a2a5128ef4109b05930704809 a98e4a0daca51f65ad8e3ed3a04e81886bbc81e8733df083af71c275fc3b5402 b0ccc6ffb1762e435c506082b5e7d35d2c3ee0fa33bf828f18508e0dd16210b1 -generate_key_image d1f9838d68d2f45feae44f49b508f51dbe1e7d395bd1160912d3353a86fa7c83 6c64d86af5079db72b44554a9317a2fef54f515ddd95cbb5cdaa76f8033d100e 336cda56fb3b020695744c8cc06d9a94095f9c6dbba89ece58aa87cbca20425d -generate_key_image f24fb1ef284cfc93fb04d0742000817fc89a5a66b0025f83104b7d4ed67d6c0e 49acb83d973e3205b152a6ce2f2713c25d47d169c02e7df7bb850f5e7caed709 2292473e6a69fb3b288731b5fc3830174df4f97a36603f9ecbe21146289eb9e8 -generate_key_image 133265f049079bcdc84bb806ee6a31417e1385263d756eb26ae783bb02ae1b4e 0e18a4f2fa828b826c3240f634c0212f220645e18540e85ebd099d78644d7a0d 351a88cdd1a8b61de4eefc12a57f697f36bfc33c0258b04ee2f7f1ee99dfad09 -generate_key_image 6d54a1d8a99b047f3fb7ce1d03d797a59ce220b490a8bb40e2394bfeb488686c a849f8f7626daaa4ab69dd0bc66148833c97de4fce12f4fa70ce8960bedb9d03 546cfd30602b71c84cf7edb0d344abb2ff04f3e55672c27eae0d4cd45476fb5c -generate_key_image bdf300c1dd84c0339a21fabf56be03a8140aa53582f21240757851e7fc743276 35624187d64314dc699c3c1d7e87e6112e02a6748bbed3511c1a3b1de3821502 49728e7911e15fbfdcd7f4cc818766a8fb167dee6846c714511321f712c17c79 -generate_key_image 8e2322b7ba5d8e6580fbb58bb84ab762afc0b2c0c1d3f50293edaa8014df7ced fe03e67c4a59847d11823d27a4abea0b15d040cb1974c4fbac59b3e60c7fa30a 839b695bf5e41a1d6b7909a39bcb93480cf5d3798e5f792a9baf43367ac4b2fa -generate_key_image 7b4cb69cacac426066060e137ed8c2a3a99bf33509e742bea6d3b22ae1a50f6d e02d75c8bc2bf43ae2b495d142677ead2ad7ade4b9477e1259761de922811401 9b29c1f56ee5e01aceb24ab628d26a0d508bd1c22cdf17839971ada5f659e6c3 -generate_key_image 348e73516d29202334618d5215a865032a95f9c7bbdc703cb9180eded8143801 8f3024484eb3c9177882546152101703c5ab5af4f74b72ddf4b46b64e4dd8207 a30788045c60c2130a41525dea62a4b2f6b44e53ffa44b77e6174624b7e1d6de -generate_key_image 537d3fdfaceb0494d68f2a15ab877a5bdf728f8f5771e3db03890d2498636ba2 07d3200d554beda9cc89139cac99585e6cd402b2045f65930a44d96231756807 8f4ec127e6f74a795e4f54b1b1d723fad043e5e196fb14afed705e712ab59ab0 -generate_key_image 83cceb44d3bf08000fbcd986262b08de4313f503b3a3f8f5e5438345f0ff0e42 2044b41a8868d86266ce0d3d33e499fd6720e114b32cf0ab6c5f3ddfa5ab6a0a 45f5d966cc92a3b764484197e0615e204495529f8b2ab24e54baf3bfce1a9b3a -generate_key_image fd78d538e54fc03e73365126416dcf7f9356c7649e23e455b785f5fc24108517 09ce3ff682b271b311bc59189bc06d912a7c9d22ff0f1ab7b89cda76ad76af0a 5577c44e684f3b91e61772f9f9155c2f43e038dac6f0e9e914ea5c114551a9f3 -generate_key_image 9006f8b8ec1db2d60aed44f2446829c4e1cd3e907f3499ab61d921bf3b2858cb a9631b621fb1674aa11629aa85ce1789c713a4b7e7be6121656fe4897531f10e ccda5755ca132e6ac715057e06cc4d42c173e1df3a0c3a2ab653d082c0f6a155 -generate_key_image 4ef082a2dcb88c0f23e43f245f14cba6790ca472e447bb86acda88b7615cbe9f 7afcd9ea2e2134aa55175fb74993961338bc32d39180622482665d469b87040a fb450a55114ce8ffed8a5a86465246851295ca9cd228f835ef82ae141699a9b9 -generate_key_image 4a0fd51aa66e0dde7ac35fd8278e951d8ad8ccf0f135e812a49d3eee8a308c22 d4932fac290a26801bede6f311aeb7538602b24ece20e86015a0593a3ed4ac0c 3459407fcfceeca7e46ce2e4ad82a72888af7736b10cdcc269c96c2be1896210 -generate_key_image 223a82ad43e062acb2a264677e839ddf8141eb62c8091dd5ce64d09b70fea2ac 93066eb249c3abdf8816b30f6fa6e495cc80f0750f4884c6f99df3029c21510c aecadd087d46bcbc6604bbe8b3b976441b73c24fc43ee3fc5bc13a7908063aa7 -generate_key_image 828e7d1ff61db46c3181f75d73037c3882b3922eb670a7a87bb71e94a2a3acbe 79ed1843f3e7515180707f479ac6696a1a83cd7a40fceceba4d0268ed990b009 a51584cb2b470499c1325fb2df71fa44b9218c2829de8812c19137549d28a5f2 -generate_key_image d19245dffafdd1c52733c96732a366905ecc28142ddc6a05ac514bf91e865c21 a7b4027a894e2fd5fb5ceb9ff196dc62d49b7cce0e6767079d2f3a1a56745600 e8ddd6fdec3570e7fbe1aebbae5d50792eaf9064867aa2bd583ec2c571ae9adb -generate_key_image 5230b58ba915342f567a3e70cdc6096d0e781b39eff9ab925c269145ad28e999 7a86ded747b54456a45bc0e9bf86ecb07133fded6634f9941e9a9cddd0a93b00 b00ea02c76777c527dd3db0e3df690faa1ddc35c4d6c765b714e1536ce06c6dc -generate_key_image 465828f3bb00cf36964f980d449264655e10243b5253cd2548fd6818d4deba2c 1daf4370391d3ea64ba25532f805d6942ac8e495459d54465f272b0260647f04 628f031eeecae2006f9ecf980eb1c09b767590858abba7e152a13717ad68ed32 -generate_key_image 51848404c7b3e6261ab998cc94acd648e712cce67821de9f3839259bef6cfdcb 731657361e2a40d02c48793d95d4af35bc438795120b3271ad64315d23e8c100 f1940fe03444879a1f1a6dea7076395d7f43fd8d87229cc9648c6e0a317e7d30 -generate_key_image a63436f2936ed616819a1e855a4c8d34274323f7909b36b75b6dca6fb51ecccf 74fe9a7e96475dd7390740bf6ddb26cb71f4030edf99a6eefabe39d441abc901 92c517de264a97d051ffe8a247260806233a0bf1f945cd6deeb0d81c8c140bbc -generate_key_image 4ecc650a03a733134116949fa9d487e156a035dc7848b43a6c0f4d61ab8be48b 7b78aad131877504601c7a2ed82e41e09fa09a194a339d2863303e54cd3b0f01 1558d6557989bb3c4921471f5512aa7dcd13cad8ea550c71e23b70cb9a340267 -generate_key_image 98429b0e69a797ffaecc0039e351afe021f6dba97deb1f710bae4de238fcb1cf 85a67257e28b6b94fc3cbf3b1575592b943a1b9143e7b62fef4a2e90dc3bb70f 1b066cea67a948095328d7867a5822833766d12fb182c8365982a72c53ec202e -generate_key_image 5bf0869d0b257af2e50fc84e1497d0ad1b202ae406db0667020c0e5c2b400035 18da5f45ad86692614091594478af98279c5fce755657f89ded581f8ab7f4705 813d004809e5281f5a8813007a0312616037f3e1f9d611754b2fbb411e13f32d -generate_key_image 4801dcdd7e0238b9744d80ab0113ad11c1cd5c768f377a531a79b29a11448c54 156207dfd3da2aeb17fb7f09d52a764795e08496b705fd684b115e73b4950702 0bb75b1a61c92100a9b0485fe9b903d78ab5d2ed901469407c87314f517d8921 -generate_key_image 45cbea1ca36b75c9333f0dfd609d9cb2f26e4e5bc029a855994dbfe92c909ece a40de8b45d2d20940db285d56b31857efaeeec30d4149a969167a00d80350a07 2caac467d2c7a4a962f004c4a610d2adbb56d7eccf0756654eb813e6abb4a127 -generate_key_image 8ecaebea6815717085c19ddce5076d0b46f6d6d914c74d1a954cdefed73134a3 b07e341bf336510674106f3fee85b39f2ad219b033bd84bbf4ecdc473d703f00 9953707b85b71dd78392cb4465a3ce973aa9af83144b3c852809b7a3b7ebf649 -generate_key_image 2bb02693a64f6aa49586099c8f256a46e4a0c1bb1380a67a7c903d1b9a5bf5c4 45bf124363ef4d342545ba44726d2d9036808271d974b27e85626a66bf4c8609 9e6333b50f9be1fee4da1bc99bc59624b215041e697ab0d84ad9d16a19af6900 -generate_key_image 9bdf49022b705cf095289824ab35bcbc1a897f2ea38de57c738cf1d65ae71d13 472cbed0c4ccf9f5cc9721837d66e92864b029b79bebd2355440acf034698a02 359baf4b77401a469b1a0792e16317ccdab732ee012849526f3ece710c80e244 -generate_key_image c1a7237709c5181990cbaea9246bf055efe0b5e963c7e48dcf6493ed87e82146 6202da160056fe70c260cc0d25c2dcc1c4e21e931d1d8cbd9b310115a8ecf006 e77f8f515c067c073f887c7040837e5c5980f27a934dd7778a8141e462598170 -generate_key_image 560d46ee14f47e9cb2e760101829d66fe8c305f94a96b3aaec983405af051747 f2ee73f6f440353d3860a11deed77aa0d17e509bd40897175f04d99f5b5df207 be20755c7153f3a96bdf9587964ee6e804b92a1d301ac8f5e076fd64109092f3 -generate_key_image e28f39555558d0ce047c225694a2421033658d40645817e68fb51ba054030a6e 9b54a4bff4330e02bf6ea7eecfd94cc8cc5f9de3d4aef5cd6f326f4d5cfd9601 490535b50b83e4b80c347ec9eea0e31e3ff0c8c4625442dec41219f92b3b455d -generate_key_image 7038ceb17286de150479260661878d628f65e3d728535c0bcf6e26e5c5f6efba 284c02a294394e50557e5f37cb129951d5603b619129ead90c7a09765817fc0d 05354578babe279016cfdc689c08785d655783d7e247e438ceeae7f9dca686bc -generate_key_image 7064083a2b2ff85a25f543ddbcef0fb59d17912af9ce24b39356f73bad35fe65 06b217cc103e8b43e3c0edb24d02aa6aba2c8828363223b7c94d162860c52b05 c6aef8babdb269e794f32b56f1b95556f3103328f6bd1f0781bd60d5bb087b83 -generate_key_image e47a6c2aa3e86108908ab531927ae2eca3a1882329de4cce6006a84e2d327bb5 79d16c8779ffd7f0d3bbf1f12da4c06fee37b97dde20a66a847be4ebba0ccd00 eec1a72ad91db6b181f9fb105dfb45ec315024000c9cf26665e72f7f4f28a85f -generate_key_image 406a88c8c9f1ca1e093b84334cb3dafef9932fa9d1de6892e6d979ffac8be4f4 fae64b836606d04bdb11e84c4c3322b85f2f18cf8a635bb4af85891d60a98402 e796be0f95dc6cf1a6be63c238d939ee234df9f8301110cd5139472e610b9d31 -generate_key_image c62cf8b283d431d554fe83ea80ab244a54536106432054c8243d45937bf088ec bbab6c0d77bdb21fbfb956f0fc23f5781c882e55ee0c3e6a9ec64d36457e9209 cf9846236af91f767b6c58d3e21fbb9fb8d5d3f9bf722a539bc4d403bc4dbd07 -generate_key_image a25b2958704e0189c1f94eff203d72410a5fd8afca88f0e59c20befb03516e29 18f5118a72364735ba4af221cb2199afef13e5073ce029735a2cb401f029d40f 19ff27e09b053a47885169911de4d8469b90b2503c79852534818fba793d414d -generate_key_image fbcb582cfd9360a20eb76f36fe47737c6fc69fb4001ff662583504178c2a6ffc 364292b825023fb0349aa55d28597ba29728e4b541897b1df7ba2757bd5f0108 98708465b7cda0948e8b17c34583e147ae732c4e09f30f84cdfa56b3bb6b4e2a -generate_key_image ac2d7a1d96c1bea7917198ab09e881536c5219060ec7db1bbc2ca03f41ef4367 62eff273b5a9cdcdfba382df55e10d4efe4636ae64080d1162bcac670abdf10f 73aa5b91125c805343e1fe3139e028ab0ebb56da0a4cfc867413d6ac1f56aa7d -generate_key_image 281cef491e4fb177eb1e356d89f300cd7b131dac66ea2a2b86d3915fa983249d 6b999e222f286e9d2f2e01cdddfab006f665e4fe9cd65bd80c9cd297fc71c40f 0b671bcedbd315826263e7fa4080b586f79662b309d565c7ba0b55b04490f3d2 -generate_key_image 656d9bfb6d987896de75645b0e28954761d1af0b5018d97220e56309f981e232 69ad261a37a7f517c250c5827d48c874d6f053b64aa6ee5ccd337689f725140b eab4d3abba1162470f9141248bd0260d2488875f8a81be17595beb1cc594705b -generate_key_image 13d52ab6504360f376d2bd30de05773766df411bf2e7cda2c1ca11bcc9bd98e6 66fc39f2dfcd57990f6ecf094ddf106b5a2eb74242aa9b4b507f039d11131909 82c3ef7429de98a30b4746df3ee6ff60af418a8c1e3ec5d6787d275f0bdc73d4 -generate_key_image fcc194622c47fdd444499895b33f1f6cf0717a5d99fa8d4becedab2beda9c693 68da6744db32b8d0b49aa47db445172662895bc3d94fe0fa15e756637826f50d 4ccdeb1d73228ac07227e953e8637ea6cb43b3eb78ab64e21ad4b2f4da386917 -generate_key_image 45a3bf603f2a0eaf3ba5f7da558e81d1488587a197bdc226c3bdd1744398e7c3 ae9fb4b230183293752b0aa0fd9148256d1291d6158560d045fdeaca4f8d900d 4325e2a508bc1b2be18b9e52234513fc62783d850d4cf12ff4d093143d485daa -generate_key_image 8bde8b9bd62a260238850e37130b08bc3dac6c62c8b2739b59c5be5ab066c4c4 45e2027be0c7466a09a8b179e5a273618693b8ffd63c86605f34f447d5112507 711d50e7dbc89fde4f833e46527fb55cd8175a41405450d485fedb0f739e5d8c -generate_key_image f40b0eb85b61c5d5cd8309e359abb73916cc7c8c560a25f8862e1fe6b583bc21 5659efec61b37164d5c06841ef1218d27df0bd2fe48e053e580aa1cca10e6708 4dee44d9b457854e3e4e5e56281e1ade0be6712c5621adadec9549f68ccb4f54 -generate_key_image 13c7b003a9699bd58f77a356da671adb912e18fb85dec6684c7002bbf771271e 30bcd616c7d77da550a8d6296efb32407d44ca7922904442431de07996ad7001 008ad45367105df4027df144c84441e8bc71713780c8ba6017f007744ac4a635 -generate_key_image 89d1ca2777590e72fa199dac37754e26b612be9176e44df19b9ff890f7419630 0213873665eca65f23a062bd5ac8dc543c796b26232c6064cbc64878d9934107 97e1cd2625d39b19016d0ac2127dab71761b11a53b4621f606121d032d02fb60 -generate_key_image af97caee8e794deb8e25829c14b80192cbcd1d4f1ed454df22ec32498cf09b12 41918092866b31724a92ceb5852e98ea0224bd71d8fd316272409e4d8aa32b03 96fb0655bd5dc75326e33a6296dc17f46f9fdb40384fe0a1250dd88257be1adf -generate_key_image ba95e05745c37c89d49cb4bda5032ca0fe84a402a60ba41f72670d840ae50df0 2e352df6a82c1b8298897253aa820a4f40a6609dbe91e1305c7cf4d178ad5305 c2ded38b83aceb5828fae63f8b83597060268fdd8a76d7f8e076db994c53aaa3 -generate_key_image a03b2659ad552bdf69219fed3c4d40ac24b6d3da696ae262cf8beff59b09a710 12344a729c39db90cc9e498575ab9d973230e8c3062242b6c1a88a21dc809802 211bd9c2427708e98e1f1ed3b80d39868ec072575033c764a975ba9820144a66 -generate_key_image 8286f78ab7deb1f29ebd18f27b0fe13cde07aca101a136ad71c6ad5d4208e354 92b6b76c89282aa7ec984d207a09873e1156290ba1512b9f4232ad461d902500 52b2e033c303d6ec81708ff052bc51ddaf300dffbfd87ded8b2628236c63b0e0 -generate_key_image d2902d243a0862514ccb05cbc68611fb58b9f865cff560f881ad4ae856117734 5aa4ee853d039764e59103f344476c68e5ead9d8d9b13d3af6bd028819d5610d 1e0786054f8d2683fe3629c093e5db31ed37d8f90dd99a16cb3604169c5ee620 -generate_key_image 7109a7863ddd12d747c4b461e9d1aa05b7ce181c33d87847db15857fbc1d7c60 0ba07a81e6ce83edb750f4c1a9862fbbc765a8ee0df40c8344329a026b9b380b def1ed772c9468d868bd9f484dade28ef057e94cfb6f37f1f180971e29ae3c5a -generate_key_image 52a384e9556f0ad1f1bbac1d962b718e5cd76bc2b8b72529f59f23599f290701 cd319abb39200d61057695a205ef45236e226113248dc89265bf8f2a62b5ab00 d4b0b849ffbaee19363a0a5b1b0db8880d4ab47f412044d1948b9eb36d9412be -generate_key_image 306f78d4b5053bb4e6144b91b2ccff9ee1da06d7613e939c0de2dad56049b94a 359bd157c069a0ae7fb749cfbac5b1702704454a3372790c617818e5c8477401 3a38cba4fde262d14c4ae43ed5869a2ccf7a265bedd00a438c6cfb4e2af766e6 -generate_key_image e12870fff5c6267a58fed2f877ba89e3d49051a8e6d6bde1a91ef2db6ef89d77 31b6ed2af6d6377b209c3ea41d87b763ae41bd7bdd1f862a06165d48decf520b e027a9c186b3b86b9cec9c5a87365930a676cad65f5756e243573177d4bd9c71 -generate_key_image 8a5153df78a0d37ef3d0e6be1b7f9d2bb2539addc1bcc3e321034d5179f50683 71e9f144a4c5f0ceda686f5216df72518904ddc08b158c9808ce55489288970b 56aa487b9038cdd5880c5b7db34903e786c4683b0aece21e43c35240ab1ac057 -generate_key_image 71c1b592d1a95937230f77007cdcee73845adcd3862158a2c892993d3a507293 eb2e6d5d3a4d925e0005a3cc68ab731741b5dce7bf19b4b09cbdfec33cdd0f08 c4f812e6dd22a45a48a5888d9e85ac703f5a76817d43370ecf2b589ce0b16b18 -generate_key_image 696079d4ce7ff5d375227d33501c3679319c28fafcccbe8ecd31421d07e74aa5 b4eb16459b3a3c655b5fc902f73538ba7f3ba7e9982f2ffbd5a6b04ee0023501 5e368fc805e373129159a5bce3690499630bf63bdce98fffaa11e1580b7d7965 -generate_key_image 8478ee3c815d2241bef37e7bd7dac72f27a5b03781e9dc1cedf28e4c256ca06d 325bfcd13d2d1766913a038ce00ac55bb683a0b3f85818bcdc1524499e9dbf0d c5e0b52cc64cc18c30eaa6d60fe9718af18934a7df492cac2a15cb2c0b7bdae8 -generate_key_image 43df13220ff9a365f4401357b5b4d7b9f7002be3022c96a2814be6372f7a4849 991ea08ad34057ea3464056baf19d3e4454b427936b9a8b9e6890e9913ac7006 583c43c34797f7f0e8eeedbf3ad051cb379760ab7ecdf0c049e9612335116480 -generate_key_image 7911c22c7245940fc5129ffb4a53b870bfa4b0d7f70dba5cc54f4afa8f840fa5 cdfd8b4e6ec94269ac8f4054b59f7ef81d2148b649040c5b16ddc05bbde62a01 a16d34d6472b32cceaef8920b624aa82387f70b45cda1b8850dd250a053a93f2 -generate_key_image 53c57de7e14b50a410f35b8af95e29a32e936f7c5e11e4f4d74d68155734b9a0 5d40e2a07fbc20fa3a16a8a9342643fb0d46be8da7ed09f6b5ca075e4b420101 7b54364713343fcec7c1739016240a2cb3ee44ed23918b595ec35f7b70467425 -generate_key_image 6bb380346cd532e6459a124363fc1ff0d55f6cb1b57ba5aedf876da96b60c58e 4268e0fe3cea30e08db9c5066c252d6951b92eab76818df50dec251eaea63802 bdcc73f196ed476a0f145e407891b0df6b3abe0472f3931224cc86b0eca973f9 -generate_key_image 7610215e0e674b36d6c897edd134d1bf1989c6941816c184f6cbd94ac4f4bcbe ce7ddba2856e32c9a65ca2f0e921f3bb42621f12c38f70efece5bd0d30922f0f 1853328317c72a930478b61808ced4dd62b2fc2cd3f250e0e0bc3f48cdf1be30 -generate_key_image 352109a60a3b5c604166498fa64192ee8ee27776e6ff0b5a8d29f231960ea486 83a036441da2a4c768b2f9508482dde7ac5294907703d2a48054cb04d0651f07 a9ac1396e6c9ef42c1e2f277cbe83473f3a43b705b0ce7b6a29973cedd0707a4 -generate_key_image 822fe13d96acce1a79312663ab08985ad4c48fda0a5a716a245ae7deb5ed0348 c31f8606474cb892fdc8d3a583c2503cb8c603097992e320b411e4f0b595770b 6329a318505b4a31f4613b5224373f36527e8dffea1a626828bca03eb261c2df -generate_key_image 7ac22eb1dfc5eda36d75bc6ef2896f08adb68405baca533a22f9c3ff6db70c1e 3dc1e001dde08afe5611fe74e1499e28a0c79e8f337adaf42904bcfa181d2f08 784f92a3276d6fdaee3af237c50c42351bfd2a2144b89a3c95e4653384811222 -generate_key_image 66b0b673acddaa3e96cd56b525a3096f0f3b1ff29f7eac11271dcf2e78835d3f 2bba552a31ae265e30b3f2da71aef98f81e97a2bff63e6f4f67656cc3373600e ed64819f38ab7dad67c13daf06e45c8d0654e67c0428f3a063264309583d82db -generate_key_image dda826fd0be104c8ee06b1057307f57149961c3695a87d6d6e1f600b6ad1efbc 3add7e705768e0f0b9633112513ff23a965e98187e32a0d6de2e3bb5d1e8b00b 8a243b790fffd54d0b1f32a275950ae6175230df76140b9c873b6e363ac2112c -generate_key_image f4ea16ed9336813e72ac29560c638cd81c25f1f61737a0c91ed98f7100ca87fc ac866f384cabe894534a252f6a0afc69e272dbc514f01782acf9812272f5e70f 5510e485683fe1870e8c3db5cdafb4992cf53e756678a91cad65f7836f586f70 -generate_key_image 8e31869da8ebd933521e13c513b803338f781417d5607882fb60860634cae5b7 0739eb0bc1fe6dffad3e631b3f4416191f4f095bf53e66cda9488a001f29f007 3c6e66d05956b6f074a3814abe175f27f292cae5bacc4d2aa461064e9373b396 -generate_key_image fb3188e8929a1e527653bccb1f6e0f811c549380f75ee2e677867d16b7ee2004 c3a7b6dbbfa1ba6f75277de1c3325fc4011311fedfcb3faa8fbad5bfdfc73e05 85f1f988708f3facf234f20175b107bbb10db9ae0f88b4981b3516c6a68b28ca -generate_key_image eadaa7b898c469017b138806454b4af9048818c4f53d66da12adc10c220ad07a 468d23e52d32d7060a984e80176f4cc8d679413bef8f39479078646f523d7902 240a9757db2e179774c7dc650f4601cf299f24d6e0ebfb8d8aacfe71663db7de -generate_key_image efeba8569584403746078ad797fb8f18f15e1936664ce4aff66a4771e2731ee1 ac6a186f357f558bb4e4618d295a59a9fbc3682612a6e9f6b01cbb13e020e204 a27e221f822d53c23d4608c79091111c2a83cd4cdff4727470edcac135e44b00 -generate_key_image ecf4595c999d70be350c4280f0d8cb1143b0d33952c2f84abf02cef6ac03e60a b673dafb8f7be378bbd1b58a3c785079687c0ec1721a5c9da89da0384724a201 c5dbb8828b5fc2675a190f685c9158199b85296429b94f7873db72590c8b4682 -generate_key_image b5878cd00c86e7a6ba6b5063cd041a7026981017b4eb3758438f464c459635f6 d4eac7a03c4564b3136f6f8bac69a1574c199f354c63a48d64383f5f0a082507 6ab17e34d0d501d7be3e396982a9dd1e1fe621e3fe0ad1c93e55bfd25ad36b55 -generate_key_image 586a2e25fe3002f7195fa584dfdea68025cd86e3cf236748f39a568568dc01df 44716080e724597214a76797900ddc255b5608dc4f1289e0c33616d804d76002 6c45fac6f0c581b4028635e2b6da7d70cb89e9480b282830b52e9d3eceee4a4f -generate_key_image de11e87ca7a21cb560f6d828735f1ba80ec32e14c88a286898da79e856cb92be 43a2704dcd8f04204697207b43842051e9b90a322c4e3ba9ef82deb3d2d56401 c0dcc1bfbcb6be8202e367e050beabd0b4fa9abbb86051a357d21b4d1c97aafc -generate_key_image 0723b558e20d327df224740aae76bc9fd31a8ba0a6f376b244d1a67e6a3c1087 8c1d5f8777946913d356cf6d3c7a0c4e790718d91da8880ac3df3a0bc1206402 3761117d575a26ee1d2a09867dce69617e8c6dc5e86508bff480f9f4548cfc28 -generate_key_image 34f865d8b0277ee7a740dbaf66d8fb6b796143de8b1eb0b0827dae3b6cca09f7 c63ec4bade22ccdefe70d16f7a85702cbad27e105c017d250ec643276b0f1506 cb9148d50b0f39c5f429a7dbe034f611f77236b9810fa688aad73aaf14daf48e -generate_key_image c5f25378dc08510e2afb13ab81d1a81b866e2270ef663535eba967205b75a9fb 18bc1129bfa613d697337a6afae1c45cdb16848367d905fdb9f1e02b742e3707 5d2846ca94c67ca64f7f1b995e8a81092715587699236c4e685778242d81d7f8 -generate_key_image 4353b87d19489257239b5a2ecb8d04ea68557125366c6e6f9ff1d5aa9377c58f e9973943124eae5bca1a141a48bfb1e0ead3996a4dc72c1ea1ee5be74c88760c 18bf17f9f258a0c6c81f12751637ab67cf96eade3ffc7d6680ba474cdcf3f1fb -generate_key_image 15524f4a97971b323046d2b901560a40a96fd662fc6c7eede85a674aad72efe3 4aa107ff7687076f43f526dab42e13fe08f60f69bf6fd3d049f31e3daef13802 86cd965b369893ae280bf3de0c98895490557186c82ed43bd5009c133d99ce5d -generate_key_image 3c0e40d7f4f9e119d11b3179fc72525a88b44c8a40ce5221c71ff7a0f4156ebd d4f79b8d3c68f5aaf73731431bd36b0640a77c7a5a8ec95cd00f83c08c438c00 4394e311dfc31439d0c418a3ca92806053c7b162610d8135d6b6de8910d74b0a -generate_key_image e0f0293c4993774b3a49480115675662f2d660f49b70f33f50087e6cb24273dc 9f9eefb1a275502d958c8c4b5261e8bce1df1b38127835f9d00614d7a6a0be0c d42c2b18d3271f678349b66a9763441c56468a0482690a7284931bebf590aab8 -generate_key_image ad527c2695502efa0adc3f1378aea64170565cbe92f0a4b552e27cc159d4076c b4105a78fc0739ca799bdb4aff07347479edfee42221f79d2913855321e2d20a b490bf8afb10813b3565ef470a104be8d4bc6bf8ef262f0ed2464f88ec3b9b9d -generate_key_image 446ba18c0e11cb8af3970205d8a8599a6db3cd6907e0e3d73c5aef5fc05a8831 551ed18b2e34ff087b66cf324386e3bac08861a4681702f1dc3754cc82e51a08 9a69f758f425c70af840c919eeb8055ce8ce74389346863f13f35d979d606f46 -generate_key_image c2185d5a5ac7dd25140faeb38f58400e8f61e54368db1cbd13ee7ec4bafdf025 509dbd34824499e689985d94bf7bddfbe25d4e12a34b0079d8f9ba956934b608 83670bf946e597e1d2b343f568947e77dbdaf741cf354c686d7c59647c41f333 -generate_key_image 306d37dc5370b77c040e99b7944c75681b642b3a08120f9f405499cc83539b6e a8a40ca4f74da67401224d0f4c6bd26c6b39a562a40e5a088dda809866a3700d dea2fc25f3ddc9cc97e51c968c1046834da35009c0e8c0e62e053aa6dac8a7ed -generate_key_image 23461840f101f50bd8ebfbe221d13a2b1a634ec683eb2b3325bc0048a2986ff7 5f1f1d7dba8d249b7bea0042f328b54d0aa947b7556175c21169697bebedf206 d2ca9b7e0e026f770d52183bf115dee5f55b71b35d2f431c48560c03a686b1df -generate_key_image 5aa93fc9ee08b974403ce423129905a589042da9aa50c43b8322c45381f44b57 cafd82eb322a4d6bddcb6160fd8abfda43524bc41c2276c4e4f6287d7773e20f 0d74f36a22a43d7961b728ccb7f88444f02f6cb38f3a141ec03edc7953cc2421 -generate_key_image 28e469961416e97e2ef5979c4b1fb46a2a8ec4497bb8b5da78d038fa2b4cc354 3542fb20bb7fda45ae51e3c49ba8376a0a2178e5878c439f9c6826ed3a0ebb06 b968737eb1486fe8e9e80adf20f80bcdbaebf86b771abff07d4d6c581838f93e -generate_key_image 4d51cc5c7da3b2d7d7f2edc0706938bf9211994b1df36f4411f988bbd21602fe 98b7a1305dc11963906afa62170daadef1fbdbeb2fc6c5e16a7548029c5f5e01 88b0157d9879d64f6ae1e90e7f78ddf2ffa52c7f87a856e258d745a1b40808ff -generate_key_image 2dbf996e011cb1442180f1da7234cbb4f0955007971886a4ae0136df43115a58 d76645cddbf25327b667b5aa9694a25093aaa6f9694064bdb750294143dc7f04 5f43f459980e84b7c9886e729f4f8a98dde4728bd43a6a7753cff9f9f27c2514 -generate_key_image 316246f9e17b014a4fe6cabdb645c2d7ede04e0a409604fac3cad7a98232b943 5546f9fcf8df86e785e19e5aa4ea069f7a522d641a0d77b16435303b1fcae10e b12377a62d7c10812a583c6cdf1876ca4217f6e2d75e30fcdf8b37e6ec6b34e7 -generate_key_image e979c608516a0a5ff29a23b2ab0ff41842d1e1d77fc037a5f94be86e1b69b3b4 ab3e3992ad052620858d98859081b899c08a6a1fba04bc8b08a3da531f1e280f c9b66d1602818b870acd8cd1a15d47a8a4171fce9af26475243e92dbe5c1b317 -generate_key_image 2224895bff0ca9d3f8ba098da4202051dc26666ce86f031f7c488889e45edbde 5658364e7f8a4b31e3d7b43b846b3bb72151a1858380bbc06b0582f2e75a3203 c9299beaa2407a208bd246d38fd3be15c2360400d7667baa87ff76e3c3065118 -generate_key_image a140d8257f73a8107e4ccf81e0ad5f200da42ac6992d564765f49acc1130039a 8a3dfef935048a3267eadcde2a8b7697947f5150b1f5c59a567575f8163ae70a c99f240bf88e272f647eefd4485a0a72fc2a26eaf4434a17e2f8ea7d14501341 -generate_key_image 09815d8a025c16e1d9abbcb7afd63a6e525d0080094e0fef369c3d8cfdffc323 180034c6637ac38cbc503133a16204f4fdb3989f2f8254ad908987fc6b9b1c09 19cabe0e4ce614b3858a2d298c313f69841e99aa21cbb5f863a6b998d4bb5954 -generate_key_image 7b43c63ac6a10c2f23c518a9b77ad4c729ddc441d6f0de1b169d05f1cf626dfa 2b5c7647ca177dbe38ccfff60f6b160bc80fe00dcd72caf2be1798ddb2c4e303 bfdc7eae3f535d625448cf6e7e23dce413c083f2647f0f69d003cec3c131eeb6 -generate_key_image c1cd0214db099d0c156aa0c6d9659eb6e1556d95016e04d18fc7de7c5e33d7af d9fa01b7dd0297557562d5d830a4333cd0aa1010658b32f50683aa96b10a5d09 6f9af925c8f9e695b917ef30f741205ee88bd3174629210340835e736e95ce01 -generate_key_image 0d764c8cd8b43935c27a83e0aadde0dfe8fba1ae62badc3cd5a2bb0f7d999ba0 8e6b3a98167f5304ad9e03a65a2fa6c3f8948f1e679c8bf9aa6bbe084792910b ae1298d6e21d534641774ff6fe18566d81be55208488a423b412d42ceb3afa12 -generate_key_image 5254d69eb2117dd0916a6ecb84379f3e592475b62d273273bf775392bb0d1a3a 4b7b5c60fae9de97fc5d220b370964198987715aa86fce47af63b7ea85781709 81b8d531991035015d301c7fff798deb759c7341adda2c32b76e37ab691ecaf7 -generate_key_image 3e6d3625839597075dc00c00cf6985792231693a5c51f57057255ccaff04ff94 1a735089d65d40f1dfda03b9ace8eaeb4996dc4b5a631c1e6b52d5ad3bbdb007 042546f3e1e2afe63f3529f5d4e5a0970482768d357cf9d53061e5b0e9d71a84 -generate_key_image aa3eebb0c9ef4924096aa1933bd947d05846361ea05d33c583042f175a1066b8 0e6f2bd80bbab4c69856e63b8408a22188b96cce735b18d687f4ca9789b3c80d 5a9ca0d14207c8d74a51f4c370f7534a5b9061a6e7914ab665820aa99f0b9ce8 -generate_key_image cb63c1c68432975d48b241d9ee5fe8ee7f13a6cd2154c4f009a6ffff8d0f1c6e 9196f2973297e6960665af54d684cc4681e578b5f7019323915ae48fcbfc8e03 91452a0a606e67407a2096ef64142a3ea9876a3f5f876735fb9db9c52792c1e5 -generate_key_image bb37bb0c99a96d981b2fc857be8a6ee15f15e8f9cede23be52d59b0f769c1db2 e1166bb70d9a916f62bb4a8b629b49b67eb0a5d4f5b353deb4d435b17306a809 94605dec588f57e7abe27b67b42221f22fb50e6bc3c52290a4df8c1b6bc846d5 -generate_key_image 8c3d4cc8f218e73856a3963dfb03de82b7744f5f889cf4f184246d8c4463bc5b 0502f903a93250e7733a304735e9a4f28144bbd2710844ab1632300e6a0feb0f 208d5e671b12fdce4bffb385cc438f5c5291f0ddcfbbf0be7336127c4401700c -generate_key_image 6bc638650c36303e2a3d91ce7a259bbbd0799b61a547a757c0c26257a0f4df0e 20f0b4613d8098e71947f75256251209bea0c3046082170b1b42304130096108 acb5c48f615d8abcd4f94fed98ef03cf206a9bb558068557bafeac7702ea34a4 -generate_key_image 766cf9be5f2dd19f7ba06c79586ce4261fbdbb7ad8f12ab30e34ed6b1e65a1a1 b16ed1cf7955a06bdcd602cc5edf5408648819d6741e9764a2364b3167c88b0d 3c3fcea8597d24875b5d6cc3a20c0e341e11eaf151cb1ba62ff5d11c0d0f0055 -generate_key_image c93a7e0f164ec27af5b52b26262effc92eac5b783b9496056c6b7a42cfad73e0 ec63fcf9b3828361bb7109e071e71cfc99079776261536bc1bd4763d607fd202 a84bb9edeb1bfea197b38e4f686c775de53dd14d64c5fcdd570a7769ef3f1bc9 -generate_key_image cae52bd47e230cf6871b6bfaae1434ee56c6eb85341d41a8514ad75ef6ee2d29 8261364ef8377117efce3e75ddf3f1e3263e1b00c30caef8d180182816d00705 3604e0646bd4ed71b84ec7fe2cdc55c60bc88ec84f770ecbf65c678981360837 -generate_key_image 2b2655a1463b3c7e76e3f30f920c0eadf59abdf82b547a4f258b664873fe8d70 f2262c1b7e7433598a770146f094010f88173bc5aea8f52e7ad9c757a760520b 47aa7333c86a36664a1318e10eed83ecc76ffc43a6d22d3b89a30f62032bc82f -generate_key_image 60bf3b89c31ab99e0b3fe6812f609f303308ed27e99125f6ce36904e9b050ccb ddeb8c808ab851cffffef29e5e3c053c475f9b581045e45406f793a6a08e100e f2c84f000143ffeefffce626204ca0b23193a3ae7e59ef9687471a3353217646 -generate_key_image 7326a74702586d2c3371a84d8a2cce5e24090e8d2644a8bc84aca59e463fc6a8 1c52595fc8f2bda42b033ea0f4914474c51830f1af30c288624ee90a678a2202 54d4dff1a4def219ab03ab0975dfb5221bcf6386cb96d9ecea4d25faccdbc837 -generate_key_image 56ae1e25e58c33498e75b78bb7c19635eaeb4c4fcabc65b55ce737ece3224bce 59d77951127d9b227b2796a855dc7b26b413adf5f40088185807a16f01680c0a 54a1839e6ddb68775ea2dff4bf21e2e7048987105e337240c5eb2f7661b9673d -generate_key_image 5239b601a807168f02a706ee080f3a4a820a82c4318a44190c8d77b5a4d02a99 9cd0333fc650a7a5b7397b33fe05137be8e7d8b3b41ffd9f043518848c72880b 51dbf8c55363ce8cccbac1f5483e44c3e16751463c25f822e6a08426cd5f2392 -generate_key_image de639358a60cbe3efc900127c0ebe8d4abc6f9ff062a2a2083ca7f78fec5f263 85f6f433fccf3b7f50191d6949cc66714059f69807ab4992469e1ae5a0da0c00 9c356b6d9372905f039c114d82ba24d3df02d0d4ac0e3e643be8bb72f84f7dfa -generate_key_image 3ba572e7852e44d48a862ccb61d190bb40e94c4c8c4aa289bae8f83aba5650cf 5c01351fd66a8ed6dc7fa3c1e81118f92117da5114cd64fb833af3d834f91501 a4ebb657b418399d704c736b5106c2e67f0f8e390ed4086ef5f36295dee41c88 -generate_key_image a5f5f7f1da5828c8af009b1fc0c86f7875ab8560b452248d3776b8744b559f1f 293f58590af5d4d25efbc1f0135a92bc28f70bb0a95f2d129113078c602a9303 bada50a9617fa5c6276a08e4bab46fad22fe0df0125b486769974602fd6ef8a2 -generate_key_image a8f909489661d54ce960b81ef7d2661de5eff77efd9ad50388fec2e9e658ece0 196ed883e9dd30985cacc37aa2bc0235281da73501f9cb0b96c890f661c1ff0f 07be020bca46918517eca8675e54c984cbf24e78031eac35900359fe9582aa34 -generate_key_image 359feec1ddd4b6c8c3dca5aa01f3f08623835329a60d3c9c3ba7c0668c3193fb 60cb86e9be9fe305c6df8d4ff8ac8f41a4e3541ce5e7630a040dc0506695610a ac9d17122fe97f53ab55e945ebe7a872edc9adbc385b738ba6739f4b78223e4f -generate_key_image a67f22bb2ddc3e649db20f5fb163ed1026c34daa0e0d249a861f9830add9dafb 4e543290a10059970b4dda59b2f349c959d218b97466306b5c284f7d915ebb0a bd97a7222befc90a71e16c6c3ce3fefedf541a2f595c914d413549547bc03b53 -generate_key_image fcfea9b25704ade7b54f4dcd61c8ed3e97fd45a13406141fff07f9d3fb8f3191 fef29570476c4fbf2e2f14475d52160a0bc3f248d7d63a22173962f7dd20e200 49cbb8617bcca4191e92bdc742fae784d5f6e7d893f396a1473df0de0089d60d -generate_key_image a024ea75eb7d0ed7e8cb493a62204bb180c46f20645d6a9345cf7f450ac18783 df5cfa9fcf4e404f92b2cbb4a05c2da92dd9252bc304e9c91ac9507020029f0f 849b486a9d1912159b6c016d608d0f65e198e38ab6a587a84b443b39bfefa3f8 -generate_key_image 75627c8c48635ee964792052bd770f218296c8f8bee18bc2ec85a40e4eee3237 c6fcf417e9ce136840081445b67247f16f3641c56ac22cf775bad9289f93fd09 05459f29571949060c4e687733fba5dd49d83551097056afe7c25c166558f9e8 -generate_key_image e39482545822090f2b0cbc09663f2632c6763ab9829fad7b3a90ab5091785ccc 99393809036be1937e4b213b263c7eca844e7ca61244f84a29d6149cfcb35f05 d4f022b5782104ea64349207e5e664b5e5b95fd8aae63947ff981508a7f44093 -generate_key_image 22b32a2761764f24ea8fb35ca3059b6db95334d0f1dc2280f86ba587dc1b95a7 4b3127b68703303d410a07932ca40832eddbdef55fe0048e8f06228d2c39bb0a bb2124b7319e21dedb1ad356349a262fc04ca09c37427747ad7860c1d93bd145 -generate_key_image 54d1d0401dc00fae59445aa4fb259e9f4eae94df037958df3426259fe1d60383 357d271cde1939dedbaefc2f27737956fc0b4c21db129549812e875184983d03 a3b282b6035a85bab88284bcad4a1ff6331f1d18502b25b66f5f4c5ad68124f5 -generate_key_image 67d3314c9635afe506026a747fe9fc0a7e9aa5dd4c8730eaf56a8e3b80627b05 d1268a8a161fea35a87b08cb681deca638582d9a38609691170e764266e9480b d9471e3f36459b90f39d516b5719255008c72389f786d3d6a7c073eb418f10cd -generate_key_image 9c566556b72758f2ca30b71cf70aa45dfb2b1abafaa8baafa848a9e8ccc68d7c 86586cb2fcba0027d4bfeaf3790c6d91db6792a29833227dff4eb7ef5585370e c956013cdb00432253f18dce32f6dde368e19b4a3e766979902ad32e1bbe167a -generate_key_image 430572349de807f60a5d858d1930691b1cef9f17edf3e4f8b2d54428e78e2455 e281e13503cbb076a75669b0344bea0b1ccb720fe7a125d0e849e8f01489980b f8f122f046f1b2b3e200db245601677124ac8646ca7d2d3a56cc89c21b520597 -generate_key_image db4c23c9d819f332ffd3294e8d27b214327f618a1954c26561954dfdf45e2e7a 81447d0a543b23285120c13e9ab040ad5ba0fe92f940466e041df6dd42e9060a f655772efa10b977cc3c0409eaccd118c892487ac383cf38995686df653e0558 -generate_key_image 22ab220cea15089f1e219421e1cb98b69b4eaae574bc9f1fc39c3a070d4226c4 188ef67fe9dd71519a70c5f38a603e3729c227457ad00fba2b68b83c3e293d0a 846065c013a5b75eea03a4e6f44abc3a6875513998ae645626b4f679467843b1 -generate_key_image 699bbf5159593021f3c362db95d30ebc69c0f58e0a06bb137c203d545a4e72c1 b4e018fcfa7304de363375512074948433786050ba8a43f805e625150df70007 2c21b891a3d6d03eeeacccd0fe0f0035d9c3f2de017b01d240811aa6e8c4a1d0 -generate_key_image 4375d3e3adcca9e9cb27b1022e7571f84bdbf3b8f96dfd24c96dc2d9aee52148 645a40e9bb05c2369c171d77d81ad806a5da261dd9b59ecdab488b32a4886e01 dcb5b9ee15cc8233b343843676ac7ac3f260570c34631ddcc624f601dadf7554 -generate_key_image 6a153e4a1a8feb42afacf87d9378e837dc9bab9e44a314332fe297fdd706aec6 c999a27b2818978098f26278c85bd26d0d8965a724b9935cef3a99443508e002 4298607d0a1299ba5c8173b4dd1c9bd5cd25b6c95da0f2f5725aa990d29559a4 -generate_key_image fb3fe6a0c979b651b0efd664adaee524d261d1acb851b9a79ccc82188aa4843c 72d861ff7f5702d97ab5d7fbe29070f9bd8edea211a5444668d0631512bda105 8632613903e5f85ebcb2269f4f97e07115066098f0c7adbd4e26f236e5839264 -generate_key_image b00e0f60e5a76ab753530a897061a33a7d9c47075a3c15051469f404c4686937 8656d0700119ccead033748bdad8252f5b1c87088fdfb81cc43e52dc0feb1f04 824d2c0f1c3e66aefffe6cdb56287e6a652ba5f83ec7c303ef652d0c261f34c5 -generate_key_image 6da92efe2273975e6723d5d554e3b18b7ebea6f3575f36a926558f6da38d6637 2f8ee0dfed02fc0658ced7e912f9d2143855d1c7a0f00918916edf4741400a01 ebc303220e9fa58608f004572fa3ef6cfa8f45b9419b547e1d08b550e7e0fa47 -generate_key_image 2aca886b58cf272aeb3a33872c3fc0b1d659f7f1a07c664dc1373bc31abcc77a fac5edb659a0ea9deba2844bef12a79c3848aeb10f117a18cbc9fc9e53db4108 01ae9486acd9eb86915e843e0e6abc3936f92b6101dacf9c727f011520939bd6 -generate_key_image 83b7f4485137fafebc646e89aa5b5af65d18baf84369e22fce1a8af45b371aa0 5a105703c66220108cfe5f0b7dc8fadfc216a5625a9f2bb1f585f915daa8960b cea0d0ac7c5da8b08ae64d77ca4571fd02ec2d474991aee96f65edb9482b827f -generate_key_image 52f15f7d6368d40ab10e36f52d992e73f084cf1f36d9394c532dd36aa3769cf1 c5c4d93fd7b9e1d1783025e4f08664379de41f0895fd7f9cf87f28d26c015b02 a2218b4a794ca58830607d6dac883436010c9a371749ef601a3f3284ab2c5d23 -generate_key_image 49797e8de9fa101b3e774742c491d993f938a82220a2554dcc89e98b1446689f b035449904cc2232cc78b64ac5b1c72c4f6bd80cc5b3a517b64dd96183fa3c01 6bcef05bfccb15ee2de79a525279aa95b25245011c6f8bd637e322bef0bcf2d1 -generate_key_image b6705cb4f059aff1d8fc57ee3e1dd546df474384e3a98488e027b0204257185f aedb24c811818333d3f640fe5f4e9083499ea2e940a8b36eee2f64bf784c9a0e 45eb00e999d008c0734ccc324450fabfdf1b0edd3d13dbd1f43d91e3d0eb8cab -generate_key_image 07a0e3c734f025508036f9671d4ba5f4e503581048ef4b0dc1d93b1002021640 39d839378af69fd457b49114596516813186c08cdd020eb3bf711389ed918b03 f9235057fd07cfbfdf99057c1638a9425ada579977c4f4d9e750af549acf5342 -generate_key_image 997e4e0fec89e2a7109f566b50b0073d6fb3cbb382548a357d54d53ad601ec73 ac9a8ab3e2a4f4fd5488b48aea2bb297b0f945e05216a01c57db389cadb0b50f 38a8c2dae1d798da7e5150070c6f47b455a61b04a5c31ddf97f185d3138080cd -generate_key_image 1e0172b208c06a9dc2a69950d7b5b27391c1ccbd4ee87eb3b629e1d5e81823a0 7da38f67cc3d6fbca3ba5223a981f5b1af417842f13e9591583fc7c872694604 34badf12f02b96914ee08491c4950447d69bb87b8208465ab5ff7b1fcfa3b386 -generate_key_image 61988c3ceda18028959fe411e61344b77efd873a994789269d64691dcede7aa5 24bf823e74fe4c4401f05aa7d9fe22cd04fa8e68de963660f4d274d12467a40a 4bb47d1a75d1e81346c7ff48607e3137fcf102228502ee32b2971a5b584b13dc -generate_key_image 96e7d25c0f22e08a227192a39635b8991ec832c49005d84e66e21bf7b0851696 6182c6fbb164dd38ed6f97728410bd606dd1f61881d88447f55cc95b9801b00f d7996735b471c833f56ae5ebda086d7f767faca6fe8bfc5037c87969334a31a3 -generate_key_image e0fbfb1ae0f02910c176c2eabd595ddbd24dca372d46a2447792efadc8e9e99b 2de901e4d2a8698471c7ed6ec1130c4e7d4c82a13a45baf9d26d112ab4d4f207 152f28b691e522d04bf4dacaa71e28b904e845dfa5f6387dcdcfb14a3d9bd933 -generate_key_image a980962f257cd512a942924165fb4f219b5ab045a30e3d8fbf12883849213355 c4e5e72cc126f4b532eec5fa64da4cd1e2bebc90f6c3808e514da89926a0a30b efae3802bdd08325c9a7a2b3bac291ba3fa7fe3948587768cc8acaaf3be8310f -generate_key_image d5635d26840075ffd8cebb8bf0cc003a8c2e60f9046966c5d8ae9dfe07d4aeb2 2ba9dc39da2b55ffceafdb6bc1b5810aefa2d419025996966474916ff5b21705 bc111f5e0de40f6a9df761e6af7cd38af270f4a832780ac6a22b4290bd621f31 -generate_key_image a2091a592b41154b3fc80906fd991cc35218f0cf8f794909cb3430c951154240 f741aace24fd62cda0bf5974de84bc1a20e88caef7053179eb906d49b8fdbf0a 315e8c2a60bf7fb9ac60fb24a5d6a74be135a53cdbc583a40e6734e5b8f76088 -generate_key_image 774be323e58e343b28897f18b63f0d7c3f0427912fdce41b964c896e12519d64 1642e209a2469b819739c6b97775063d7766eb5ff076112290e9ef8dd8c1670c 7a3b29c0d46fdd11b89c1cfe84281637a0a9bfdd2de46f21d86e3e445bacf156 -generate_key_image 05d48feae11125f6b3bcb47e236100ef882b9b53ae51a658a87cada9f583401b 5af555bc693768a5cb03619dd8f700caff1ca8f09fe478200d524186f5c2200e 31a43ccdbc484c571c6cb6d99d9e2297ec4987db9ef4cf70438ba271475c755c -generate_key_image adfaa2815d8b0311331bab9990840ebab38b4f12ef21844d2c6ec4c470cdedc7 2fb5853883c321e6c0e4b1f3f54f11725b8a68a2aed2332f2919a02b83508306 524a76d10e7289f662c8ea30c31abb9eaaaa1e55c6652778d02a9cb3d2a60b13 -generate_key_image 23d5a36582128db300d38ccf726046ea042a700b3da2f796eda2ff78a9fa156c c71d6fa00df2221b811cf1232fbb63933f3df80fc01dca4addce804b32a25b04 07a0379fad5c07a8ce6282add2688229d8f773bfe2e85152e90ff5a4cc0d8163 -generate_key_image fbdeaa136c00e69a8de5b60b26ce9f378243ef6e3e9cd9197a1bb7e486340b45 235d88c363a73dfbaae7b3b326af01594bc1f87492178a4accb4d8b64877fa0a 2c1bd81cdb8661705183070ecf5344ed8e1b4ac5b0c171832d7326a78644f092 -generate_key_image a8c4c3a00f310f2dca7cb6b9b433ee7411b76aa9b833ea94b1583fb119b74493 f4381c216f7a868457138bdc75603c3ea098f739ce459176b8868d523ab1240e ce1fdf74bafd291958a898135de0b5adcdc8fba5866a677626ce677acfd6c054 -generate_key_image dff32c36ef37da62f33ffdd370273762df6c25e96cd26bdf68e35e1dc8749bea 716d032f2e5ba32daf1c22cd67a857ae084fd756bf3c6d0f59d0aeb9d11d0b03 fd59fbbdf57fefbdbcf74b2f9e0e01f7b4a3e851b450955d96f9e7efc78c626e -generate_key_image 8d1234afe85bbb138ef110a6fcb85ff5313877b188a8ad42f310529b63bb696c 456b033052916b5e4b414e77d21df208aa4e4139469c17e79dba43339b693e03 b67746a1339c85e564dfd7df2e7f57a16d2bf17e3783cb758b8de06dc584ac89 -generate_key_image 7a537cd018a9d16d185b2101b283edc40111bbb07d772568bb2cc7afaac1ec09 6ee8af6dca10580d54707dca8bc3f58c13c612379208e3d701c6b3daded62306 88bd7443518d8394b94049b19f890faf4c6cbedb25fe3da3112e63ea16448a0d -generate_key_image 2850f3d3138017e47d0cdb826ec76dc680d04e1fc2aadc7f58bc93d0cbb97426 0eb68b1c60bac010523dacca24247ce97917852c624b98a744cfead27e511700 2e63a831d516e6fe9488878fe8901d6df513d5ca7d0fee6b3cc23ce2dbcfe533 -generate_key_image f531ef7188d2a39e99030c3f02d1bb47a899c0a54e230d445b555f0e50674acd 07131a71722a2306f54c97d2b864602abf04f059fbf070f0343b00fc8bb0260d 238e7f40d66f3bbb60bb5a02117055eb7463e637cfe370a59f4c76d24f127cae -generate_key_image 8a1ff39350100d37da402c734cefb13fb2d9baf03a0bfc3785ef6e93bcb19303 b1c372f27abd7da1296278760f7f74ae8c8db7ecb8f3891ea622db5c731b0d03 a03e79adce1cd2fa4cd913f6c0b03dd82dcc80fe988de92e794b72d021a287fc -generate_key_image 20f54ba653372d080357ff54bd1423b6772d1fa5e76864ae7c5c2e4e7fed6ac1 9c05b4ecf56c52dfa93690ea0eae3d3f9ed74cf2cef45ebeb57357fe0a295305 a7d5662893bd3f30e6e912202ba3939980286aca684c244fc86c724bfac421da -generate_key_image c7d19cfed17a1c7afbc46a385fc8a20f8d4a1bdffc39651d42470ed0d1f86b79 a43a285e632a144b423dc815f95e7f7e9106868bab7ae43932d1d932024ade02 97d49bc672e495ef03e56f631c1a12d65504fb19414b543410153a9419a6fda0 -generate_key_image 500e95c7d91fcc410e5293a430ee7f797ba41661569cc27a9540dafe5258957d c5717f2a1bbd1272291c4365ff3a88c6871deb13c1871aba39d7432e82590e0b 20a73cd45b03c46ca9d926e6f514a7205ff9db2790631cce903bbe6f1924b7bd -generate_key_image d89dcca2c675c24bba550c7b379f10b7870173201dbaad7157a9b48212a7d208 7a655d4d87da5e79e866cde2f1f7b2c9cd2bf9046e7edab90ac319703eb0200a 68e622c113a60164b5ab7c1893203cb32e51dde79d6b0852513aa0a3330353f1 -generate_key_image 58928673ec3b020af5b1c865110a3325278a28325220acefaa68110d3e015c12 0ab4857ae4eb69a13fd36e80017a9682b4849c530c7890c9116b56b5c6d2c30e c02854e9ae94ed1fbe785f5f04768b9e881540c8b8ebe24daf73d5bcab7efb9c -generate_key_image 775db12d98ee42a555b757fe45340fb8dc966d3004d1e2e0b0e621774bc547a3 dc1e41a534c4ab85b26005f9892f88ff1c1c38d2f8c8f06ad7f4e165efe31a0b 554173c8bbff2bb7ba0cbb1bf524a87290e1426311f255f03c5f88580ccc771e -generate_key_image 9960f2fe7fff005f94519b74896298cda8b5e3a329df56e3f69ca709b034074b 1350d3b3bffaa6eecb5b1ccc71c66e9993b17adc128b66ab75d1eaa4202b090e ce0095f1b407874a079d676b1963e2b9f6db7feedb6f5e65701f0bed413877fe -generate_key_image 0453318cf2f211d4a6c3cfb4ee7198115d57796079d49169b9f72ba86babcc06 b63f05a9db8cf6f783d79878637c867e2641b103ca6af39bd14f80376a50c001 5cf7182fd8527f25218483ec660d955ac9cc001de3278923f4672ec734374a33 -generate_key_image 4bd659bf2461becec62d3e8b05adbb3350bb8b66cd5b55df0c52caad6c8c65f3 ac896434b0f5421f5d8e8b6fb17358487e6e1667b53b9b422026aa43f0b2c209 66b44ad3e9849adc6d7625bea9d88a3b962673705192c7d060b448f2539f9d7d -generate_key_image a3fd925269c3c3fa1ad5cbf7595e44c32a4cca0b3e2039719623789aad9974c4 ffa75a55113ca24027d3a326487d7f0f40ef06920cc919f3fd95bb5933519f06 616dfc4e776d7273a1323eed0db8fb2eb80fd33671828e63b2e823c096ccb7fe -generate_key_image d8a4367c325edbf5a5e0a36f27ad6f1ef5847dd681bfe212f9be1a48e6763265 5d07bdd9299685febe02946db3ae1eab03efe455856f0f43ea7d8ca49d46910c 4e170b551b082c1b7907bded0027770cefd0fa7ead56f0255629281fd5c5ee10 -generate_key_image d981829c726b537d5b495e386eb335818911a084b728520ad160de6d3ccc6d67 904b7148a5905fa83e0332ef321d4ee202f416e86e5171539eff47999f906003 b072cdc80a3b7b55544f52ce916849dcce5e73613e330a5b7b8ace1e374cf8df -generate_key_image 6c461894e687797b8c62674a42048bf8455942ec804261ddea0def73198d9701 e17ed8c8130f5353f20917ce1b1ae91ef4581d6935ac1e963fea6cc9f08fee01 65fdcc550333facb82d9a33e316f717d7167b1181d63af96aaa4f624a1385da9 -generate_key_image fbd91ab7e63b04985f8623f05eff48f3ba16fbeb64b66881535fff045cb06a8e 164c2feb95397748b10e5e9e408c820f6c582e665ecd340849cca2b482333d08 3a8dd024d961cc384326561fb100357ff426f2dbf74eee430275f5996e60f93e -generate_key_image 692c1f562eebfa7902e1fd2f42435257df41e87564deb791b653d56b812fe968 b2b38eed63db26855a64403e807c9fca86dace87888830ad0de46e01220c4f02 aee831e157fc656b0c432683ef6e0dc50b940169e8b4242279a6361b16f395ac -generate_key_image 8ef9c401f5d1119d2603ba0b636a862f01e0659ecdffd52c5cfd9389f0e9bf56 0a12ac95001105870205ce36754b68dd4eac4e021f0a0deb082644add0c8f200 a2eb8e05dd6dbc0da12eb73dbe02b81eb8b0fe985c8fcae37ed5596203792fcf -generate_key_image 007e03d991c337fcd68b266676f2628788e8bd952e58078f7187e34ed9df94a2 5239beca1757ab72e0c18a4d5c70d63ce95be0569fd128c48b1833fe98683902 940bac041dd9b0e791e195a50e112354d369099039ceb69288b8b53865c1d0dc -generate_key_image feeac3846cc2fbdecab7f71704eaf9561ac695c3a7c0d6cef89c3184a9d67896 5cb0e570a5bceb709faac84653c68e540eddc3f3d93258019f89f20eb6da6c0a c3db08ff11c957c9e46386d42765db28f97daa5424201c678cb08881bc255d22 -generate_key_image 38e6c1b51dccd0d7a1ce3cb68e76c34914b95c2faf443a5ff3dbaa9b6dab44c6 aaac20a72f965c36ad3989bb93e823ced24215d0a7069bf428b03c79d8c4ee0f dff197f44bc47dbb19b58eeb95c2a88d55894c007099fc4cf10d6350e1c20e2b -generate_key_image 7be9c0cef2ddaecbc3024176ba2d15c18f83427648af51f2bb3f199f3a7fd7a8 b1981551eb60e41fbd0c044f3a53715a7c6c6d1c85ae066271ad13595dd81e04 7843fd4cf04059b3129d6f42ec08b7469fbc12acc86d3a03a7b40815aa59d327 -generate_key_image f871604950cb16ddce0b5b98f38db5da1e35322e0d47860471333c45d9e4d890 82cf04215795d5a08ecd064868bc5358b6faf6b609a1561c471b6f9e2fb33f0e f110592b6a6625aa1bb2ae7725c20bb059109ed4b72c37657ce813a3c6c581a4 -generate_key_image ce358f43065a43b5656a5c0ca818416393de19bbba5cbadf686e3ac72d5e7452 212cc9ca4e549ddc1ac6373d56c05bd480aa9cbd995bfafa25a2d9345f8b7806 8504b27d6eab3d38d63214b3ba4721a6d0b718b5caf3e48220cf594f69a63210 -generate_key_image 1e14408cf1c4d7f8916dfac1deb11158576c788272efa185dabc8a0765d8437b 9acdd332a020dc528c373c28a08a0eb46b49c09350b2111309dd16b75a6e290f 1ebeac0b3fda7d38e3cfda41cf69e0e0326e2e3c6460f29e2e0d579f7bed23f4 -generate_key_image e956182c29b447c870293496890bb95b982fe06de4e0ad98ce6d4ac4bcc5b270 fe4a91f276264096bcb5965e2537319d5b4bf94829e5bee646a0bdb0c8b1030d 0e47442e2411f8f93e69db860c202812169637948904a2ea53a6544b7e9d3e5d -generate_key_image c48915cbada882378f8482c7e8ff57505f04b2ec3d5785b660e4deefa2a3ad8a 4d3dc9bbc62c61b8a51c687aac2f1a37cfb5f13130d3ca1669e103584afe230f 05a6bad9f2ae11a2aef79e4adc9c35391d86fe41d0a6d4dfb5ebab480f09a548 -generate_key_image 696e28ebb13454825fd28d0aa1d82dc5e94fa7a00d04d2c13b575fb419fe5722 45ebaa0a477c9f2300c39a7f0e2989953d2ec66b1c55232c24ecb1fe72843402 2a422274da2a2194dafb4de318f86a88e532b8c949a0d2f69df53d180ecd42f6 -generate_key_image a9e191cc12aba250fcbad18e5dcd32dd44a1e96d3d7b65d829ef763128aeebd0 e02f200f8a1f26774b2f75c4f0b2e5d77d662d97586513b8ca0b1da3c23aec06 1456ba31ee58d9bf23b45b9cec1450025fea11de2c28aa805ca043072c302e38 -generate_key_image d1f63b7d407edbdce8b31b642308ade5d5ecf262825bf3c6be45a1efa6befc2c 3e32cf46325bb7541c04d7e4aad224f5914c0de390d6b041963bf5e6a3faa50f 06ee50d11ea3a92d70f63cafa5cf86007a63555b320000d7bd159e8089fb938c -generate_key_image b9adc45192d3096748f58c6aa4180c1084ee1c78a116c3480ec522b542021dfa 18bc52551e150155c6b8cc4f13b0424eaa87d4c1559d30475b0a8704cba10406 32b86a5be046d81ab74ab7f7ed1aa08e8869b86e87a56e363852b76cef396ee2 -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 -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 -check_ring_signature 90660b84dd3be5705c7766695fec404348af6df58f8c5d58213f3b70b8b67a23 6289b9b151eeb263fc29e4b5e90978db7670f06f408403c8973bbfff2a884dd9 2 4af96f2c3a70ac1860d48132136989c1d38551367025d43f36aec0ffa8e7f28a 376cc178d8ae3a68ce467bfbe719e88b22514617dbd1e764e0b94b4f6bc961af 4ccadd504d1d03e385ebd25dc51b98c6f3a0e1c1be7e5694e44dc2377898510ca3202d7872294cc04b65d8c109e3a6e843c327b3416ca3a2b1c585fe4152260555441dd7b1543549f749acf5fc9a93a3f3c240425c5f7cadccdef4f06cef0702ae4ad477d0cb60a1a48c1da22f5a8b20c7c5672833c7ae13f78edeb3db1a7b01 true -check_ring_signature d280b24c280daade9d2bcd68c6dfd39d3a13eb1b0645c4f7d2b0613dd4b5af3d f1b943daa1ef225726215f551dfd85f56a3b429ded8608a09a8310a90b8aa88a 2 2d4e494897c24b1730f018df65468c2647b2dc19f650d1a9e055b9319045ff13 74db9c16b0cb4beb7d48ec77b654c63917529072aa57d381b5e3b8dbb06e0f5b 8aae0a8523d65b3746c87994e4cffaf437ac147a82efe34389d270a976183006c7de37ef0362e13aab9287a85445748a8e0e1a357c6a0ba090f436937a1878b47b41de38a3737152453ca3c0c6546b65ceaff3298329273b0808d35af376a20c1217c85b153d40bc154108eca199175b3efa3f190740325c734d82cfb054d50f false -check_ring_signature ddaa005f46bf73a6bcf4e20d1597e2b7a00d1b802e5efd5c097fbf2b3b027276 2b7d48c34c5f2d7a45e394397e8e1ce2541cec34e200d17cf741cc4c0c670421 23 24f44adcc267f707ddeff7ad59a32b31f9a387f896f5e493faca8183ec076c5b 7809bb7f7225f1f94d7a41538e9f94258004eaacdc6f12f7c3066d8503ff476b 915e4b1f43e0d5ec16ebd365eaad151411aee0cf4ef8cb76171efc70df722b25 37869fed481a3cfaf9c6882aff8145bfb4cb015bb1994afb9e79eeee68f33f58 08c0ec7c18e9998a92c7136a0f2441ef273d1ccd72893ef41a6178dbd4adc25c 824bcd3648388915ba8286b3d6bf775cecede4277867cd419b8a35a119158f1b 2f70964bb89ffd5d2bf10fc00b08d95892a3a347934dc78bb733a0b92f2ca072 a57525ac8c2ec09756a31a9822adee8c2f02b018099b923fc5ae334b8abea96a 02c070891d0f602cb8a985ea4477a071329da3a6f3c692dfb1979f615ffa50ef cb359906601b2236fa25d1eb6bff9c446f12f9be84d5c2df5ed7b5bb12cd8d7f 1d8648bb6f1ef73aabbe99bac25b81640d1700e45e81c1e890c91b13ce7d268b 5e5eef5eaa1603f16710873a283d398f2c8a06119fb4c6bdf86ffec4ecff1c81 1d087b05a1ef4c126087330338dd1043acfaaa1dc46b02ced5d8648968e0b4d5 92f959ab46a78cdf3a2140adf0be0d25230eed852891e9bea8b0fdf1f9da393e d1a7a40aed14c250f020b8fe8ab90a36d4966415be895372d00b2f70f666946c 5b7ce6f2dd8f9d3b82cb19e17dd9a2ee1ebe21386017207ac467d2524369a448 124276b082f8670f369374b390575351a67083ad320544794fc20851daeb8f98 93bc4d81ba1d22b1d83cc8b054a59508dcdc31235b98cef1c5a3423cd2f21c3f 4833bff261b927e2b648376dabba5483f3b733892edca16c315b9fbfbcca5f06 f9e117a70f818cc66b6e885e66295a3c14500e750964ede22b274b4a4334c15f 9446e013a5659a6cedcb4e785dab1b37e5a6338e9d5ad4d9c1057abf1efe0e7d 6866b5fad7f65a3f4101de405ca3c5c697afbc6c08027e545843f45a751aded1 e7b1822a7d7b0fede4fbdf70f2f59977b0dd88c6696698395b0de57639f4e7ed 231d195246f4268e689e1731807696639ffdc88d652ff2d6a133c83d6e70e30d0fd09c58cf204a1b225ecae76a791cf0589e4fb8c44f193056ad691410da750c99fde20efc9606bf0dce0f14577ed31c34a92964d27568e709ad1fd52e4624009a6bfe03eb383781271097f0ac0e87e7aefbe1ac8939879ae4a5159595166f0da95f60778717d732261d60098a3135c1aa9634ff372e65f47cda644de7ef6509018fe90824f455bcb4ce27b357abb3cd5009d9088b5f4d5ca58a56d0e16f4409c85ab95f9132f8e5d847faf661d504906a8d788f76f02cc6866844fb8ab05d0ac32360bb89ea7a4a8736045e32782e3eb854b80c6cc1e2a926cdc53e0bc04203ff62cfa4a61cb1dc8f506ba11736a268b45c91ac98ab6b693770257e23d6ba0fd92dd42f793f9feb08f38e18000244efbf7de56dfe3ee47ea0557c256f45f1005f475d9449a2eff9ca775506bfb819745ebd49c93088e38ffe93fb43836e250ce76240329b46e51694e16ccb930650a30823f8985ab83d7b2645e0b392963d01f340ea09ae3c8f78e286538b70fdc202ec39cd3323536295a5e56e1f0a983d001639a4f2abac0224ca2f777aca58a3e51996d77d30ee1b7c6011a4c2cc1f2c018392556edde4c7a2a1b7ab08b0137bb0b1fd14125a0fb5f839df23732f34fc0fc209d546276a803cf018277c5809ca66e4bf5842f63438440e6b8b6d9a276b09eb2a5b20fcd3e611daec4c2d57bdc552ebcdcdf70431442c58a09609b931580f46f90afc1a0fb613d4ab01818160b1d34f47b558e3c86379dc8e2574f686e107a04b1ca22c58524a7cd9d9556a8161056c9e8be2e32c959da177231b2842d108702d79f3bf59259a95c3d7a9d6929aba9478bcb0100d205607f6dc9cbf132e0ad2f2270cf7d4869a719af6d1c515c11ad1ff3e968f9a94e2eb82776c54516a0314657db8535e6c6db3c20b38063a9316065df9a80d4062d6bd8d003b467b2f02ec9511db28583f38997d8554450fd2e710fb30e9ee2d47333d4e9f370144ef002ad182ca4ff898b00e814535864944a5f2a586bf9b222a4871a16d577f13c204f03323ec7d3a0ea5c5a96ce3eed63179168740511f64a40f8bc06ef3a7dc380917211daf6b75a6f12c9b75391d0b34e3af571e13f8db2715753cbcf215295f0bdff9b7c750e9bb33069f29b1487ced44dddec1e612af05b9dd612c8be863040573d564dc8f045db73f2d2aa6e3182150eccb1c7254c42a9b45d28924e587d30d822e6826b4a823c529e52680bd02ce64c508314ff2b07a057aa3ec5a279d8e0d6ee9b54d2bbdda8fbb8272737a3766894735d700bee177c532e91843a1d3790c6459c671307cdd70dc4e3528b732cd5384ceae7a83cd0016ef005c18542f7f05565f300258a0ac24eda4971543e067bf6708db112251e2f24d7a34bf4081ee0127bc4a853223cf9238a5fb070113d5be52214408311505f4bb7ec2b7b8e5420067cfd1b7d9a841676d7fbca917661dfdb5067d9a2ef1388f6b256da0ab96ba0a56d48ce5f55c9700af75ca67f19206097fd5a1ad91e8042bb6494dbcd61bf10caa2a4a556598ae94aef58f8bc361b5773568004610c7ee5488477c7803d23a0e2728ffe24f8397f7767fd7ccf829a998f78b8c5489e49ccc7b1aa40b124d9205dc144ff184ec865b58664404280f008c7994d56e63863a87c97632c73f50bb0da6c6e9297e30baac6fd3f43cef8d17905a6055366145da6bb8b5d03982cfa80cb88740e0ea17184dc5f195b2fea162efa24f1485c641ed4ace8cc7f7d9b41c00b095dbb5bff934f971d9a3f3a17e97f086ee8c7e95263b340c60fd4e44f2e70d892b9d54ff84de72f1ae15211cb2bafa3fb1ec52f2e0c95672e89adcd3dcca0414832293b02c6ba18cf51025d9b074bcac402325fc82c4bf983c614e885de5023ce174fb7df0d123945f24fcc1482c8f9ee1cc1faf775e9f3523cec971dd9c0d1fa6436cf29f18ba0c43b19c158916bd5f5c4c2bd3a964e828cfec73c8f1c40e33cd0c89b6cc60db0d5fd58ee1186c8744e517057764fd6f55aa193312207500 false -check_ring_signature 17e1d8c991803cf0747a66dd16a3c5069afb0f604670b823b675bed5de59d6c5 81abb2291ae3e208665370f4fe07c1d82d3f8f6a6ccafe7e5fb4819ce1d2f113 3 130f844d2ff629d6374653997afec462eceed08648daff08eac4c58b9006e6e4 f92f7aa2bb9273830b966f71c7d7aa0ee8473973d65fa044c74ec4d4628d765c 8c1f5b3b71c27ebdfadefed2594ba57b19934eda6fb7b5c7e63dd0ed471b6e2e 7e799950f135343936af6719ebcedfe6e4a3fceaa86047923f592e1fb69aa909575174936ecf6615813c0a4620aa77161d8309aefffd6d33b8eb31b37aa36109dceafe0b8b49a5a280561b204f71f1c6116053ed1bac94b26fcad0ec947a9b01e4459a956e4644f7a8c39719164a87c93d21971366e66e0409556fc93c4c1d0f7db9b2d221fdf6fae05cca363b5e9ea1a7c9b0c80080b9c825f9bcc0b734030711b981b71f0c193bdf51b41bdca81579144e1d7ea134b93a6ba40bd18bb74f07 true -check_ring_signature 9a751c0464ad7a400ca1175d0e31323e2c5d718db9cce3e8029537911c89a50b 15cc9dd45463de907e092e45067e95c59f05862c5bd272fd442b097caf9f91fd 223 25c598823a8a735daf500b17d5c4c0d560eeb27d608e36a36bcdb556fa84b43a 7e58ed7359444ecf2ddcc59a5cd53c0644c46d2501d427cbf3959335db9c315c 8334b9a47e8d13734a75d8e0d155d4cbd5a21f2a207e43943116c1871d285be2 ee50c4bc3bb52ba8d24490859d2340f534c96ac4123d757fe5a73601ce53e69a 47824d0b0e0c98000bb9ae22b32d3774dfcea00dbec527f2bc8c80bbb693ba89 d263c549ff299531752a19d33c90df2385adae47f39bf4e72a36a7a5abbf869e be169b323d85cb8d61890c76e28e67191a03869c0dc747774b2df339034012bb 5dd112a3678e0161f7311c54611bff5bddf36fc383ee287b83996e6a76da45f1 8034238bb8209cc534f4447828206d2ecf40dd8d8b0ffb7b93de12f2837818c4 6b9967e8e522c15c8fcce95f81e85518da8cc3a9996c085978d8c6d996f9bd5e 05c81777f078c266df47fce956db766809304a7ad3af7bf37e0c653d1529a74c 332c944af0b2fed432a617e0f3f8a2ccbdaa350a3c38499935a1168b67d4a422 ab997861bafd195270470b3d89aae6df2c114edec83be6d773822e36900a4fb4 f0d7a7bde6ae61eba772bca3965319f1ddec9f0bc4c3f2d27722f9eaf783e1f5 ba79f81e9b7f6643f80a579132f7b9abd30f14ced2102e748699205b5d3c8fd1 397fac21149573b980ad8a0930bc2bb8c8d5c9ec5b0d0f56c4aeffaaf5182a95 a9ba777bc93456ab55557beb39a7074580c049d65efc543c8b183a8ed1cf99a8 1ee12f652547dc4e28a9408963fdb980955af57ed29e799940361044ccb8a293 1a5faff369cbb244b8533409fb93286b03f10328b5f20848e564f8bdabb57d93 3826b0544c0da3cd989bd2502a05ccdce3f0b30b1da07801d87cb078f24977fd 5c7d09b3bdb68a368dea02578ae0b7ed90ba4aab0dc51a57e4de61c043f694cd c8dd22831cc8eb4f1c7e27c01404fecead57dbbabd779498ae88c300ad7ab845 9f60c1a7a708c6496c1ae98915da4469ef6e09db36998cc6083ba5cc81ec0641 5d6da8699dcd1257a3984a97733a6df0dc1a0ac782f530341194912e0325f870 6774a88af95a82d8bdfd118d5863743e39244de4f3b8988ccbfc63ca96dbe39d 76b9bf0f785269d6f487e7900c888f5d59c3b90a6dfcb1a6b496de8ab89dd1e8 6aaf7894f166c1a9eef4a2c741fcb62cbfe11542f44370a7d284a63b2c87266c 3617ee65a4efb0c5978dd00461bf8d64ede4e177c6236e0bc46e36045e30dbb5 18d02379080b2b284b1ef4611639138c74e6238106ce927c1379a27a6aeafe47 3b8f4f5965cb9030c61c26140e9176fdd380fadb354c7b401ebde0355d929e6a d8db5dac1cbe0e44e38a84d1641c06c2d4f35b7a1b9904dfce447c44025199ef 1fad9f875852f4579d85d3a995b9cca5a4b143d5be31be6e0e75ee2e5c7df1c4 2814bfec0a867a41206ccb3b6f3b1d68abcd7bcc7f5aef834ba291653160bb4f 7e41a4a9d2b4c8d3574432f8e2cf66efd9b9d821e09df86b6f4967c7d9fea9a7 7f3d5db7dd12350d5cd2576d3ab73909d255b384c15fa42a6f10c05f225b8086 73e33b9a5a64dc4bf4b8340568f462c6664781bc312c076824e1271f1944367e 2766236897171b1b204025941d0471b2ff50b31367fb7aeb7671e5b811334ba6 16c62fcbd727546a55e924b59375461dea2330d55956e7990645117512186a42 501c43a711240806888749e69bd492274bf06f5b6b6f815a6b7fa4830bf5d3dc d250a55f4c31d7244e8cb052dd26bf8c7ad5702b763b9651ab2edd89b41c67a2 113abeae74073916d82527e52fa1edaa79fe45bc43fc1f00c34668d567827507 010123d5c37d6e29a65c00a735b1561bd5cdb8d331097bd34725287074452117 5bd84f8fa4d53d360f71179164d5518a35e567ecdfe03d4d8292108700026c35 3c48988c932e2dc2f2b142706d1885f3ba7e413c74a652b29ec426fe5e586d05 ae4e89378858f4f5d86e8550c2d41a2c95017716f1de219d08e7dc0df738f967 3538db3a0a2c892b32077bd18f4389bc2d14c44d9448726118b3a69770a827b2 80743f15081be89535b8e85220784c68fb2f6fef84a76b2704be48c689a680bd 2842cba67feaf7f51e413256cd20d387ebfafbb292409e163c3dd275a6289670 9a4525678768e99594c4c82d4c82185459b5d951ef3100aef625e572c302471d 2ceaea82e157eeda802df5b24c3eac62ffd977f882c938c161177bd5316e04de 42afa3850cda5c1f63ee49c7465aa1a1af9b6d858ed83692766b48c94c6e1649 f65613f62b746e6642c7c82d134a4f52c6037b09c7b7461aeebfe3d1b9105064 2bad88604db71146c4909146e3975ac00c5ea147030a0734c8c00f9cfdc751f0 fded6eb3dbef57d3c0036dc8126b7f704573e1d2e637c3dea06d34ca16519733 f3f0ec6fe98c0065650beb8b15550a9b4640a384732ba29575fd89a288c6519b f8f329bdd53124c963da2769437315ed477cb79eb7cf5de364db4fbffb3e610d 5f7f4503e25dcf6fad5388bc86f2411fc3f516d7cb445309cc7372c38aa63166 4f40e010702240f0306b9e2ae700a267fa4d0c61ac64c2edc03d6422b75e805e e2405d45011abec0970a5f9dcf846b3cb67c286ec137ad1d497346ce7c56e8f2 956827c2dea5d34817521897c3022b018340e82fba6a1c47f56972080e2128f1 a8fb4628328dca9f4db64a67832976deba143d950a52b4eae19d59d143705c28 fbb5250782bc2495bc69d23ec3c4539f0f2273781f0b02f89a8e3edee866d30f 47fd116be0716750eed397d4147c223a0ed6d39b090938981d2399ebdadc2645 7b17e37f0ace22462f688340fccb8d38f85877c9aa46527993ee9000a81fd1f5 8bdaf542a1dc9f84ca90edc696fbd4bbc656d51ab884f16dde0833145168b4fe 3a170bd7b1a925972c0aa145ec8706b7b54f70259af9806b385f7d07f7b41c08 4cdb93fa6e8ad54bd0d41c413584bcd4514071f06d841dbfb3494d06d7b53a53 25ddf71c4a0d466237dd9e86cec96e867881b95723d3b9119439a201b41740f3 5886a18ea3b3fcffd0dd86ac57803f23a967fb98a15d6f9268a8b3f21b3f83a1 d3cf07e5e7948e87d7da5287e5ec15e6bbe1f4342e73f11d19b447d03e60e474 46e7356707a2b70d2395175071cceb861d1254a8dde7bffccd979a8c956387d2 b6b7e1fa7e1a08c05c097525dfc06f3ef97532f99a7b4fe673822397c1d19503 bbcf2916a26ccb1b3036c3f56c338341e7872f0a65733609ff91b5a6136a8674 f5b788d61231fe9387e041f93fb91ed227b92ea68660e942eac35c26692b738c 2e41257d147efbc687960b10ebaea135116e136bdbc13975578c25112040d977 997cc5cd12021525be4f3e4f36c5465ec72b8e45d7111cd89271330706c862ba f6bc3c467eacf8fc27db5de2fb36d627b5c18b8e708a09d166075210ef17bed8 eeae52598737fbc00f48b08f5afb521a5938136477d54ca53bba06bb11d35b26 c4876dc57ba45b514b7e2f2d13e9a6c4d8f1d46f1a92117befa6dc8f8b2e8e93 7a28faedb9ffcb59798d9930f82d1eb86903fa172c9a2ea9aa8d617d2676e43c 6c06ed42808dbc2010bae6a4746ffe2ca956df13d021194d6f50612306d4e02f bfbd93e37c2ca49c8c14116567a440e99b6f89344dcf3ef72db00b5a6a0be694 942b68847de2586d5366f2cf99da2a66bc4475ab3f715629db74e3e7657ff7a9 63cdffe9b55b5332ec5aaad2253c8fa8047d2afa7e94432d29e48caa1da41999 ba535825ef36c814464c904f7377c290c25995f1d462211e2665dfaa5ad00e09 1ba70f3408b55f083f3bacd6f14afd25e257bd417bfc105da93a828239c32f2c fd833ea7a72f44e26e5b5fe2c611c5962e1289412ff39ddbe9546d2998aeb333 7cbf0eea12e33abd78e86313b5591b049cb4e95ee5117e6c2e6e93e7dfef2abd de8b4d1f9acab938ef8658784c2730793221d7e25ce06682fab7022c5ee3eb08 cae4543b0e0cebb2744f21f0b848458ab6c7aff77c54419721bade64c5984b32 4c643598388738756c6eec565ed374a5ef25829988087a85d32f3dbd7423b103 cf28ba8bd1eeb61c45ad60d00d46f59cce2293d45765ca8ad0934f8c7ea98258 5ca2d07680787bc7b939748f0bfcd352db096aabb8308a561d4d710a6e395779 1ed89618645e1dd326f5306d53d7b5931ebf087ee7eea00b4c13c742ea2584fa ac11eb0a62e1fcdb3235b056d9e266d89dc1aed95e3b9e9cc81093ceab4e2f5f eff8b57e387572f7be9ada25beb521a320f0644688aa240e6e22c000262135bc 6406e2f60436644b2325e5b52d1861b1fd8296664837bc9336b588d9f4224556 a75d15b632b7ed51a31e5b54500c1308681af3d97af94ea958b4b3528a1f86a5 078ade13f25a704506ebd3de860f80ee7c136270920356b2c4075580cb18f323 5fe35bd4e7ae9b62474651b9fdfd93a5f0e56d7f4f108c5b10747a7c316859e7 291407cac6288d9d300e19cae8243058a1f07d82a9085506031b26dcd5d82f6b 3b67be2fcef32c4c526e9e572321d6407340596252a1532b90ea6a1c8b06aae0 ad2d56228e0bc3b369633f2cb110280381d0a6db250ce7d2f7eb400ee71a6d7d 43586ce67b060e1089a265964ae03f9ff2d9dab700da2a8a86f62253a55f5462 0af1ae2c29c04c9e5b72e6f2705f3e75a936724bd6e5dfc73feb0b12817447f9 89e04ac77f7b2dd0a0c5b2f7767ac507d73024bf300e77455883defd1616e967 fca3654dbbba3b7a005a2a27a200cbbfa09648c613a5b5fc9e8d3223b9b160d3 62c872244a7a958f7dbecd263a66b590c0664128a035c6683d9a5a2c9844f7c0 89e5db265c674bd94959f1ecf40351046bf9b2ac64c20714b584bcd69b06e648 ab012dd7acaf16236b4d44a1efd1b0aae6e45da2b19319e6586ae5ea84f6defd cfb7d1803e50a97081125afe74e90142c16f2c170a034423a116804f6592bdad dbe3a3d30777abf9062b66ae764a5aad8c9297f481c0925d15b730486836bd3a 9a8bdd110a53c6565e477f398a792d34fc1a1738a6851bc52fd2397083d44f72 54337e5daada5e303bb694df5d9fe5df49c82f75b3a8084d87c85e56abca3922 16f77782bced3600b798af539d9c142a963da361c682dd56764c563a1f0c573c 11461a8d6fa8ed7b1b5b043e905039a7bc767959aec6349e58f245279c605c27 dc9cc8c35d65393e6d4b26bc49f8e85939e18a141a4a2675d3a53ed652be2e56 2b048f81d0255b3ddc07e5b568ea8dfa83a6088922a51b66e07d7103f4d53b47 ad117a16448286f565be2c14be245953381bed756eec61c751f2fc3116c59eee 43fe96e449befceb4e5945c0b795d3f87ffcbd193876ad40ba2cbb9a373e059b 9a47ef724ed5ed09298339054c62abad7988f84c95cd2d4626faccdf74a94e77 6654cfe4e18d7eed08c14fe0523519db602f927e60b9b41e759738143be4177e fa2ad87a0a6128a7295157fd7dd932d6f7cfd39128ab8aa98721ebf6826d4f6d aaafd93b6e78a4e01f92d35c97f9fc28298d849de7b14f5b4c15ca52bc77ee21 7d140b826bdd7bc58b84fc1eb68891ac2054bda10e937b969268febdbab8e760 45c044d3e7e54721684d7a20b43eb1b578fe1d5a5354327bf574309dfbd541fd 79b386ad5031c210072e3c36b68bddf2f58fc76d45893dbf1a310c5e5685f64a 0674ac42f081abede7789b72de96f447e68f9fbcb542e0d6c6f9099fe5249735 7421ec266f8d241c124062f9d3252a250625b0e4b01952be05f01b3b1c9f7102 18d95eb62e2aa9da044dc13b3654a7ce3dc26101c07b8287be5b65eb6dcea162 089d5b9806486966272458ab5b6d0213f0ef53cd32d98972360f1c6689842923 ab3c2ee98ae88e3d4d3cfb47ce9cc67ee308f911d9d0184cff586f17cb4a5454 db4dd96a7cfa09810d00595c94b55ce3e78dae1d3cd027ef3ac9827a84b2c603 301c49686bc2b6ce5e948ba8d00ac01a9c1677c7a455f38dabc4ae7f28495977 3c0f3199f66efb19ee7f7ef8fcda7a6262c50ca15a440361cfe4392791d1fb30 a73c427b72f3c6633d78c9eae4e0dad0132cde43b72097b814863e3dc84dbf4a c44dcb6f299885ff77ee398fbbf3976d407d32f786f0a1b146383f7d58798d71 6588faf085224ae2be5395ecf64099b8de7225582786f36b982d0de75b37f448 79ec8a91dc6d90b38b3206a3fc187f58e995a2a5727f875706747861ae5777d9 02aa934da4dfb1c3d2a1a06c26e285467b37d5222553b0f742791457c1b4de3a c5b9bd4f2fec8f9c4879965043e49c4fdba0cf067fd9e5eac615247b0cd01cf1 a055e9c8af9dc14831583c792e026dcf69ac53807940a93d03d295bebbd5e4c7 23860c525eefae3db5ee58bf28ef3a5f83bba49622ad8f93f44d232f9678a48c 22953dc0e7cdc9475f6925090a55c7d0c23d4abc7565dcbba3c6eda5bd4b217c 3aeba840bf54d4a1ed2df33ca3fe7702b1e1448ef80210f91ff48b6788babcab 2a51682b860303c91b8f6e60972fdd056375d756067ac17b9094221a3f503017 8940789eee56d1e29c33027687af060926337cd742b8175f7d56ed1763c3735c 48f16c2c82988f402a439d084eac1fa4fc78d7b6c8deb7e85eb6048e37fcf096 24990d01c9f418669f93c955e6f880098d33c1fc876fd8aadcfc825f663ff786 aa308df651bfc7a03f5f02c3fe62bb77db055338e9a888a8c7cc7d3e7f85a4bc e3cf0689c432421e13978a3b4a401beb4bef9b9b9cd68ffa6981e3957699c534 bbb8005c88a34de2bca5916e637c97ca4db03b7cad6aa396afe230a8d0214c59 55a9252dcc37ff0db7c87b0468ca2e994d04fd11317c6377c2dccc9ace7f10c6 745917a3d4fda4a68343cd48a80d7d69567c6c7d300f32e16205f3022837285f 64f67e01a86790e2ce64fb9d73caf64758a5e916570cd46877874489ffa5fcb5 62fbdef22107ff12cb4d9c239408a62110de981a19b81e03631754297490f1d3 d0da367ac7d0651132058835c1374f72404ff457418b3aabe55330686c9a18fa 7f7b0508062adb69452bee901bb94a2eb13b3d63a9697417e85ecc639599ad71 d8ded863d86f1a9ef88c8ddd3a518da1e29702f3a555a1fdedc3ef48f45b347b ffd886eee0b044e7fc0e90108d0a08dffa51dabb0c92d89e7cffe895722e25c0 8c8ef846d34c9e7b14408d10125e34f9d05b4212147b5feebd6622f0a2179f7f 4293f9c41a38ca39c543f8b732865aed338eb5eb1aa03504cd415aa853220781 6ac2da76e9d5a71c2ca348037a0ab29815d41493c9452d342433ff936af1870e 08ab24021e8f7ba38859be3fa2309aef57ef7bc98092def4cea23ca21288f6b6 9166defc016d8b3a980a43fe69a67565d0452ce78784e1df60b3961480300f2b e8f0631f16fdbd7b0212793ca277cddcf2c3bf14c5498bee63f6d2fd2849f5ba 80c82310cdbbb3ef7a3d5f156222a91bbfa087f16d37d2311c2d2b4a9381de1d 41c55a87036b38c76301a34a3371320f9e2e23c8ee039dec7e060c1d25dbf594 5b639f9f7d89232b92b04ca1181a01f9fb1799b826e54609cc8e4105beb05cd5 0cea4fd125b8ebc82045a538d0d9f49f40e53e063e84a11bcc80f21bbbd8f4c0 8db16188cd4a16859fd0a8ece3202fb5a6c61372787b09f550973d8ccb4b3841 7016271244f18747f1edf0eca16c1bb8a519a20f51f934978ff636eeb604049b ad649c09099dae5aa85338204dd95115bdcde6249c4a70ceb4396288a0716cb9 9f3f040296c9b3d05a1377e586ebaca1ee2da41534c4a613e60344d1482d7fa4 3d85d2723c6ec81a7e034ad166f61e403a4ef5288d01c184aaa2c573fd23a748 8277f7c4ae7b08f1f0c166e4ecd6ef1a5b8463a6fa843a74521016a5c36461c7 bdf23d739866cbc08b5bbc00acf836de8bc16c3dda5272dae2528cfcd000e943 1cffe59415c4b1dbcd97fd1f531654ece25b300ef8497d7ec66f26be272db636 03402ebf534bd9543ed4a0e57a407a3b9f72d9533a9ffa3da4d221993cad666d d40c73c453373808967ecb2e1184228fc12c6b14285f398ecf3ea103417a18ff 1de7c816fdcbb4f092f7c4a2e36ca1d15c75ba6306c390e25d49ba42f504518e 49ca14afc63c17f3e2fb7d7108ae4e14b56126b3c326e4981a87ab9a26eb71f0 9a9eccddb22fbf73a3d737bc62ec0b285713941f57dc7beb95f12be195b1d076 bbe679d78baeafa45a78c3b006fe52d116ddfa06c383b37b8f02ea968cfcfa12 4e5d55ec282fb05c8db535b18a04aea20dc33e672a95ca4924d98d6829e1b349 c67bdfec70c55d683d89ad686a6beafe40f1f605225282768c9af93e28576dce b7d4a7c177c3ec8be02730abef39b65498fe6e82e8bc296c94dfb50b466f0eba b0a4d93fbd26ad5a2f1ac8e7f401aaf19f87a5832b7587f4ac65119f4f324ab9 2447803c0d1f0dc070d98a14c2182cceae5a7ca65415bd16f0288dbcb856b70c ab38a4618c6ab6a440be480269b0d381a7bc8b9bf847c0f106ec61f36df007ad 026b3846d824b072ec3a4d177161bd1b83d0f98a215754608b3ff5710d7b4adb c2d2931764fe31610d80bc768f9d7bd9f142ef08b6b63a82d2fa77a51976b640 877b04d185c0f1c8df51953b5908228710570bc8bf69b569ca664b7632e1b5f8 c8d77ddbf5defa854f31aad040f33de853e717d72f36bc46e113eaf28f6c21a8 5583fb3071cf817cc1ddee10d9e05877ba0fb802fd5be4f87b03b71a3f01d59f 221290c0546d2e77772fe5b0ad84089176465c838dbc55e29ab7400db21e636c aee14e5279b5c92cac5d6f2e62607627b86403339a18fe869b1115e8c41bf7fe cb278c2f98fd56a56e0ddd1198e7b9d4288126796c78dc4f3022600cb85aad0f 209cd8d97c21f85a744b958886fe3881581f5e141f183ae69539176a976ac668 1c80e915980dec7946c932360e37f80f6765c48a2d66c64f0325639903aff3d9 7b076a124918539a8ba65ac5d0008e9b90c94b350c1edce7bda62f1ff1433234 757176dff1e0bbce68c09029b4e8a6a2537f043ffb7cf7139211378bf4a034d0 73b9382055a6a4dcb700bce2946705138122f8c67b6b7789d7af4b3427baae18 1af2a9fdb0cddbdd9420c1744cbe9f47997fcfc92964e143fcae30a8aef7acf0 7b12c0bb4b17f7edc2aff342d1d1a50872fb3949695db5d22c9b4cb5251c38f9 ddbbc95e13f0df9332739088b30d30ae4775428eae4dc55256cf7bf5faad42a1 df49d587fdd4781e4379766f5bd8a31caf9ff82edf917069aaab25b4754e23e9 577b3dc6b73a4f8991005e19ddc75d1a83ad7114c9397c2e2ea4e2225f8aec0d 438dc172379e0c2e88a34b46b39ef2504082d289c6972da1d46b54057efd5abb 291456b3e3e85374452855cdf1c6e9ddeb47e7f11983095fce09e90366535d54 2589335a7e8432f9e8ae1f35c4e588aa603db8ef9398746daa422bf27dfe2aa6 86736ca258ddaa6ffaa1c5d7f2fcee8923d56046c835f66dd5d81bec5ccb68fa 7964251a87c86b8830d144bed2ecf202b8d4f8d3e1589af201e29aadd7ce0160 27bc3b3602c192d2d02fc9d0d7cc557aab927235172576193a104abc4ae0d747 daade7e09f9ac556e4df3c3ebde7a2ba438fd4c6753b1e7a251141e0e74d26d5 59d6c261a19d28febad26f83c2d76556832b42229b8c52b1686516ff9dbc246b a0872d3801bdcc5338c804fce9cb50d49ca570172568eb88ab297bdec90d6b74 2fab0afd9303a8a9099c0b8afc80057f700bb09edb01c6a55f9a24ca98740791 f8859bf2103a57a35c6a40e001cade3cc399a13d99d9f526b3f41d7f38fd500e 3c2ae69f039031f940bb23b94766230743946cbc592324cc4069a2422876a280 36189c2f84bf1c3cc9659420e687b2adb1a2a8a806075769367efd29f5cacbe5 be98d82986f49f7604b0cb45284d2e2f5b4b367e9f5824d250326e3674084b33 2e848b42f2775e128e1aebe0be8be5a5fe9a176ac06d0480ea8a9bce2a94cfcb 9698674cba9ebd2bbeef3a4b2c514bbbb4771139d0b79f7e627a52fe27bebf0a41bdf37dc5d60807eaac64ebfa42505e4ca17710db098ab814025b836eeb1a048913ae5259e2a7987aac985ee4d62150ad022b3c84b35e984090fd2824dfb9023d72baf98ad8a54674b18040a27e8e028506cd8a3d6b40b65ab71cfac1bc0507c1c9ea5bd4061035df3683aeddd9aa5428e87303a0524f3616288f4522443a04a2029636b76d55b4555cac1ba228c747c62c8fda0ce5b982cad46dad3a396509bdcb4a9cc498c914f9eea818613ebf5d5a4b742ea67cf42714f4c01debe59b08322fefb8020a9acb194e0b14f77277b3e79d1adf692a91eac50e208c01152c0044cc0e1a996d9223fbd2bfa83bde7add04a7912d5587c2e0074b0fc30073e001357b4ae991b3284f410914e2016fd3aba05a2b492f3ed45080d3aaf553fe10086f25b03bf7ee645a882cef34cfd4b1baac090767f2d77d7398b80af236a04d0c3ebaf0f8c454b3d96f85de1d6825acfe11bbbfa390d291e6c5f4e92125d0ae09cd5aabeea65705d2e76220e23bf153e4e60df5f2a95f5b9a0e50b782ce8022088475347322e4d2e54a9260cc239008f0832d0f6b1c4bd757ad88d013dfb72809925a3dc8a87d9306a47cef50213820f6304782662417b3a018c598013d3bd50806b95db15afd99c27b24a9917a231177a0208ce30e12caa5738d87b2be3e39035174a9d72da199acda450e5916e986a555391de03188882131a3817d7a34cb04b91f572762d12eb30edb1de8ef823dedc754fb6611c317ae8cecb6e341c585076d330e7dd2944efaa0d3401941e1cf8a2ed5d4d9f4ca7e985e720fbc2b9240048eca449360e397cb47d800f19b05e02395beed1d176c7646ebda0b74e31172026b9e701239cd814a822ff8eb21d925107c5dd1b5f9a4d33808f38c8f39d4fb09db95900ac60862c07c8b430a45a874de2885a3e3e0f35bb22e29a5eab35a460265024dec8e2fca5ebae9c606600f9f6ee6077a61bbbf584ef41c1cb72e6b9b0576a002002a84427d85f7dd5515b22c72adbd7232d4cd8171bbfdadb80692e00d3a01120809fbbfdadcd357ea3534ecfe294fd365839dea30beb55ecb34611403009a67697817858c2320eaeedb70eb1582a03db7acbb091ad7d2d6310570da00fe3ff4401c0edbe6b940ca37c1ae1931d4abdf494d74c2f77021e48c4fac430070472b5b282818c57f27c6894416da835fd60206cac0e658b592902354f0070296b192a90d8d26555ec7b0845a04fae423806e97fc86525f07700724bb98e009f254ba222b200d75bf244665c63733b9e35785cdad25d3fd9e45dfc6e05c970977f6c8a0b2937550137b3d02a026b6c91454df8f2f1c9e9b36cf3fc36fd21a0c6e3619736d898b989694fddb66f210d20670f190682c36414933cb4c2e27fc01c77e0ae41e1f979fd572b4fe7f4c3423eda17879a99b50e3cdd476f9384c870803498291649b7b174ea4475e85b0050a9218b23a253ea2cb6faef0256b501801e5c7e47210c65431bd4016619dc6bc1f73471f33ef637e3568bb5cf13f773903a8a2a9a249bf6cbe47018587d2d0c0926926fee616ad679e77fef5b6b256a30f7b26da73c43756451e744c7c9afbc3d62a1766d922483d9fcaf28e5f4413bb0ef416064084183ae7940d171e417b3fd5786d25040c1a8c4d3992a29b3441d50b33efd008b1ec3ebdab807ec5874b10f37174f10c4404594f9be247eb0b49180084b9eaf8d4c35ad536819408adc982677a14b8ba1c610c0a18aac0e8aae64103cf20b34ac900f407ea524c844e85e7b1ddcb9f590180362f95319ae79d657508ac0c4e59e9a76063bec2d5b83fdefdea97b9d48bd34c56c7dc1f3b318a9608059850442937758a7b0f137978b833a80cb9bbcc61c8bbde3a3c24ca93537c1003ad412133d225181bf8145be5a4b8bb768bac42082ab33f204f9f2aab2f8b9e0dbb99f90070f181d431233afc2cd66d0428ac7901f39a20795f887b764bdd00037d31839416bf8fa0a0a9f8ec4fadefaad0b409ab8957b2bd3d7dc62a277b3508ad98dc0c94217c06f36e05413925b9417eabf534fc89940a03eb8415ba039400b36873e89ac983a9e653811d80888a1374af12b7720a153cb98972fd9f572e0438f3afbe83268ab91b28210ed32d76021c7916da1ab55ccbe0712ca8a911890b4349caac1ce01797744c4db6f834f79f5111f96b52ca72dd74711e222a4e27081e61a42efd6c1fc5b932eb9af256a0fec026f16d93b757149401bf3956fe500a8652720118c7c56be6c63540a67ddc396edb6f51eb575adabe00c1e3dd82b10e481d9d78017320a64b9b654653c593b235421b6f333887f6798c4ae5672d8803f8fc3f2a899d045a013f5563fe77d25c023c4ec70524032f5bd9d18e5b6c0d0d218ff0fc652d429a53d81987e215b7d528560c7423b6fa10139b203d17510d098dbed750f31b2b41cd530f5eb64e45ce3314f96e25c799b9b4ee65f1b68b6007b33a52c18efe5cfca6f524410f3939dfeca1915ff31ed42f69f1629ade5af703144e0bb0ccb9e1e94fd0eb4de931f3397dddeaef372c38ac9aaa4f5888699a08b93e1845729e3efc7d89443473d5b05c2d44c95efa18dce899f953afe440cd0acdcc34af0fbe840870d69914aa18b39f52734f09a4f1517ebef5f20e50afa10db66cae199f65b3d194a8b311a2a83615849b16dccaec6b4f028d5eb220b90209c3c0fa4e70f49af2372ef42f93bfd4465ce4c4be70b5448a26edc4d2ea1131009d07e62c280fe6e48eb0e2f7058c9fa3cbd5926f9a2e5bf1d60be930487f640353d0043493bf7789c8799fa448d3710044e7404ca4060bd778c58edee2b6d404789c02f8bbf4e61fae9d34c5d25f8576e9facbd3cb7f79066899a75cfdd5c90f553fe86bfea41101e86822990747c722846a959bc48af76dbb5223cb361e730d6074e5f5536c2563e3d56ade7d5f33fea2ecd2943e955123393e5810e017300384ea1f5724426e1da81afa661d630f0affda7df35f0d565669a61ee652f8870d2b788aed19a285ca63c3a9bd1dca9e57e64dd0a3d48ea35b9f135c508b4207085ddf1beacf0f11283118bb345febf827056a9c17c2b162c6c22686a224d3760f259415e60923a3f94f309ff8c1a98f11eb51ad3e7e8917d246f936c80412fb0426c1057862ba496e0febaf3172e7fbe8b51519b51b554662982dba29b3cdd80b733e80fa4a9191e8f3ce720e66a40d3328e546398dc1fb7c3300cba5894ed40ac6119f7ba02c2427d7d64ce889dd6bceedb22a0c0b375eb4992d3bcc06fa2f0f4946ca8c0799b503f99d19d3ae2a4055efc7a43de52d3cb816f70701e65849050435e2c8a5b11e1f7bd2c51cd3a0d7985bdb079a692aeaca4349195fc0f35e013d661ce2b05a2f7a7f2ec1133b7faab4a6eb06a73156d3034fc8df96adedbf075bc3355505ba8552ed1659fb41ca7463e82e8c8c2f6fb30a9d4454bbd85bad0b39514c6c95125fe9439f722ff5a21b90bfe50ca4896f13fa1846919d908fca0b4eb2f9be3f15b113304d778cd5a79dfdab8fc77d36a491da6339aac7094a2401017661caff652e9efda4cd5fa749c0df57c9833d247f30dd54f3ebe086a8d6090e8e223346e2f75cdd9988fc49c9a78a8bd065c91195bfe447787d188530670f4199c2cb0071af8cac3668f43910aa517463edcc8e49fb0d0c7d0e75768cc003c59216059782e093d8f9b0ecc1bf82764985e3f5c72a342404f04d11e9b22f066a14e21dfa680cbf31132afb7173f8cbc8baeff70479e7c39a65ee2ca2a0390ce8eb3bf0528601e212330ee406d52e42ec496733dd13a2c2bab31f259f552408a12310315e08183751cf4068da5fe92e13c8c4691968cf8beb872be78fd6bf09482506a61a0d7e04a437805e7753ced519eae6fd1f514a835d8395d40f7ee9061b140967ccfb3f5fd8aaf139d747192af4c9aa191b3457da9332a1741e77bb0a05a824f663b907ad51120b17db1b3d2d674261b0800a39bcc1a474c628d4eb033464507653313af96888cf74f3eeaf5a831f6ac6cb8fca9377aee8a9bdbdd70069c8089fe15301cc982ce80a55b171627770b2b355329949558d16714fc2b405e742462055e0d6f50382fee87e0238ded02ea0681e7392910f4d8a2001ba1e0c36dc5459c5444b088e7d5d571d861e7d7f51e0e6b9dd699acb435c789f8d8a0990b737e664486afa893cadd1616b05c8c1d26d4952817dd66c53338c388736007756a4a584d50795340aceba7ee9a75241907fc8bbdc393900b927eb1c8cd30f3e4c0714cae1532cdc743c535c3866a956a5587209170b8da5fb777dc263990ded2be59df524b95d7181cc89188d1cdcf61df75a2c1aad77a9fe149f231c7c0a90413d5c704d62fdce4ebe23f4bb25c484a1108f67e992b70bf798ac5682230067dbc1c3247cece83b4e343f3677ce4adfa545bdf2d989ec7989e372a92dc1032f2b49d2175376406dbe121a3d672c51a34d75d413ede9c725140e98c1658705bba5e67d10ed34a5f8cd9ae7e809a2d5df063828a29b4bed47e28a972da6a30605a6d407cb14f5b7828dd42fc2f3915c13ec4eda5fe1e57440a18dc358963e0eaa16acd829d7aba0fae80031921e6d4334848cb55ace445d131376f32ac5790c780f4e761fc743f46cc9861acb2a5cb1e6a22a9a0a9e4c5207420aeba3b8f90e517565c14f1f526d7e917ce30f11988e86a957ecb66d76d46ea5c56df581cf0393cef1c06687295a4710b4bcc978a7a141b285a819bb051b8be0487a71c58704aad2cf013a664e9ca0fd2f08e5ac274255bb0f21bba20f67c3dac184b96073021726e5789bb4147b83a1873601db370a9c598a8877c85105db7b0d62a887120543572578e591ffb573581c32f25a6f76d8bb2a0728ee477af011669138d697049c4cfa702b809744c5d2b535dbcace7fe0398db127c5257fe86416f2883d7104d810f8dec173e394f7ae5c1b2863f3a494d4f632645d9d8fcfd01006d7ef73068d582215c4c2d30db62aef52511051daa02d5209b8b212a93a591064694f5102ba43c6684a373d9095d230bd9a736ad074ccc771f40cef57238acdd4c7f633099b86ca017b0f8f21b8d0d6fd6a401e87da133002c6a7d813c7a5abe7f450cb0e10bbc314598eaa24285724fb4773d76848ed36c3f5b63c8fed29ed484926f203be6bf2e0d39ee7de11f164d4e0bde5242d1b68672d4bd898a5c0909bdfb8ad08ae6300835a9830961a8f6a3d980faccd0282bacf6e801d020c22d1da3569b102856b335325a9567145a800b85a24b307713eb6aa1415339c4461dae96079dd0fac2724017336d70efc77c77501c2e73c8259dd084c81376ecafba71635ad290b1fe9815a9eb312411f82a89a72acc18dc6768cb4e15f22494be359f7817b5e09df3f397c7aad897cadc74477eaaad545727ede3c4c2a9a51d8caf06fbbd0d70e78652e6b37cffbe9c3d32dfd60fcfb0e4311c15dbfa4631a2ffaf1d274749d06a20444ae65a4754ae2faf261fb77a16d81d25aac029320cf919b218d837ee5020986fd4dfd8dae56e1017a1ed14190da89d89692005515a5be5049ab115d7e08288335d9ddf4f0b82db8630518011d97d2af328d29ddaeaa7fe37397cca4ff0ccc98051bdd9f6c4650d1d5918613c989fc784e5f6a5e64893471e9e1f1ff8e0488decab3a7c92a1e0e1ff83a4a6ccfcb33dfb527d1f87973dc5ae67343aff40c78b41ca998263e2982d47d06c3a5fee04b185ed39073933089b4d21eb329d20cb1d9b5de85fb18d32b20c224d0a24ead8a2e202f5e19cfc0f3f5cc75ee55c00d4e6aa1cf432d730f52e434106771638a29dc7107476175f093d678ace360420394f6e2ae09162bc75410f7b8aa7b50b494eaf8c4fa819f32319878683420ea05143f618add0108607f95b9745bc9dfbc15bf0d31ee763930eb839c58b06f8d09cfd5862ba6aa41b284348866e0d9ce2e9f0cef445a8b31315ce59ba4f27c2c084d8255574b0698e4b3a529e901c7bc4003d4b8e2ec40b70ec36991843219b8097dca117d8e7299a51fac38a3c84e7ee15f1fd8b9cd6f48b142f93dd5d2e3ad06da74ba197264ce8cb52a188140b6ffc986f39c4abfa3c774f0f39e2256d2340fcf453f0c3ce6c056b974c1b6bbd34d1ee0b6dbce0e2df75191bdf43342d77d02ad86472eedb8c030cc7053c37547a7902d40a9988d39ac7642385443b3aeb304ab29e012c2d7860a325a44027a65fc6955be3301e7c81a4b8447411d7b953201a38bf9262772045cc44fffffac76efd2ad3fa9a08ecb6c6c7b7eac3589a1120d994faa7630c520daa3d03b11342a8343eedddef7d1d6d0118a7f68eeca696202e121168e13d67d517d55c462a882d69f6b7116c3446f43d1c5e1ded199194e0b31cb32a8dd61151f3104fd9d1f60c6ee6bd49ae20749805d67fbf56dfb49f407622104f98cfdaba9d8dc51269223651368fed12fc1c2d27a0990b4177a2de308ffa28e9383b448acfa54df121c2175e1f31b3ba5933f3899958e67c4a2c42d05cfe7cce4fbdc6cbf073dce4532213f77162a4408a78c1bc4c586c187d876110cd6e9fd8fd9b965ceac1f33ccbd343f4a56f57246e52ad8b24532ca8208fd780913e0d064c584cc3c25b7a8379fe839809ebc4d87166a26ff401878ec0063a70a4a6f33152a37ee53bd887e593747b3fc3800216d751d5f3750961e728db6700b87abb17121218d212b6b1b0ea67e8297c617f7bdee7a4d4660846213b318a30b3f639f2101b98392f75c954e32a02dcdb20e0462c0a09b3df02fe172f34d04024aaa670f15e2a68ba9463635d4cfae956fc28442b3f54b6a5e0da3fb3ea3790929be19325c053d6e0498b66e64f51571d259adb6eb5255b221863762c3aa34039c5c74ca424742d548c7ed77185ff9f4459f71096629aaff54024d9e989cef0666b96d5fb1ed8197161283f5651b1e204687991beff6af2b6a38a8333316f3060c37a63ecf0c5970c141b14c34bc4c691a391ca1f33e0c028be6c7bf4a506b07feb310f303298955c3914b8ddee267ec455bd114e074d12aaebeb823b536800baf21fbca6ce0268dbc7ee0982acafac904ab0a0e4fd7d4030511898a578bc704c4577529d923b70604253165805da00f562fdd19d73c240b99a9433a59a92609637fad3516051136a06b63f2d814831dfd053c702de9830a0db1b278ed67d806b80990195145038510076c43a3353d3a1a784be6b380044896d551ed94623a059a6c5c6b39c4fd11b5ef7d6f361c2ea00486a40ca593bec67023082e833fbf066c5733160727188eb2ca8712a9a92768a4905ba4b66dcc14296311609f7d370e879acbe1d3bdd4eefeca8c11bdd31b0f760bf53ebe498a0c34af64096397f40111c6615c087d5549411d58ebdd04d1b4f39f1a30b3289e28e6c45c1da86c6002655c8dd7d8fadae185a5d92146bd22cbeeba59ed7c321fded6bf0280394e7a0ef074416da925c8d058c4c2ff16c6b5780f085d751e7061eba5efb0b5ff70760252be9fb90ed8b78515c1dcf2507e5e22de8d38ce923609e497b06e967cd6410a7a05bf922afd5794eebfe4d0e2ff1bba484681c331342493dbb5e6c827a9ba0d00dcae5aaabb017671c1d934b8566eca50a0d419e5355123c3a0b9ed6bb17001f4064926591c45c0f04e29656d7459219b3a40460c4ef8cca773d5ab862e0c0eba01cd91bc3d46e984b71bc97bb9987615e024a2199566fc2a6b93baa1b4e401ad41ef1b19bcee22341657aa8f1eab612e5113d22a538db3008d949e9d361b0cbe1ae867d5776d60bc049e4aeeee978c514f83a4ed1667774c6d393d68c5f90a951b6ededae0a437dc6d03457c491380feddce9d0b4eb6278a6dadf156ccc706a25825a619bb69344e0c16d068bbc0498fab9afbb7cbe67b2869fc254b4a540c874bbf5faa3f66934745f172dbda260ff745deb21be37d53b24530d876f3900515e686c0f9f418f8575d3b98ee764e03f4149e33d6522b710a76eb64a8d58d0f39afa81734217d72422f0e3f637b970ea0679815441bff89ea03e0bc577e370005f20ff9f0b38d8814cd155520578ca13e8541b4e9aebfa73b022ff0a94bec07b0a6632a721d6887ef6cdcf510fe90ae3d6cc0bead59ba5b601b4f43fa14c10168237550d132d8e5ba93c73b1409f4188882387172fb71578d191756d0098b0b87cba7608d6d2683cb305c6cc7877a697db71834ea7b188f6c7f7713c8669d09204c5f8e7289df2b5927cb57414fcb19b08f9f2883df4bf457dd76813b14120891d67e4f6329de573655fbc8cca29a3ac0f54d29e6a3c56acdd762443515930db25cdb9232135b4bebd0652c5f4ad8ba5fc46c866e8190cf6e142d9b257b200127a2ad19370a24ffeb95c045a64273f562f2dbe0a2949eaf79e944cd3dd9250061b7753b81b0f09940e91e70d16fcc1b477c8d027e383c6e4ab2198bd13c92093e21d0c729b8436dc36b928cb4f574ca1644cf032ddf4c436a4da327480c92000f4e3dcf5db2eedf594eb4cf035fe3f7498ead88faa628c2796773cb9e75cc07e4f77e6e48807fb8fc266f7faf356f3e1e0824591a44b6d3790994abe2250b05b555a35adbb8266c528096d1b9a7d3075f7c07e8a91c3fbfac8334d3e48bb90c65c21961c8d6b9ebc80205f7f79956c19abfe62cf859ef3981863986a8fd71043aca7183863d7d7f66d374f5d5dc33a15ebf2a79ad0aba4cd20420d249a1db01ef54efe37f884bfeca02a4c1368ef975fbd353229edd65c730dd5bb3f6b93c03f49c39e502ee4352a6090079ae19d7288c00293333122ea2d2fe39c7617e240062597070ada8a618d767b8b85495ac0302e0ff3ff1677b54eaf5140b579294081af3b783e7a06b7f374776f5e751424d239cec3385a037b1159ce14ed2e8670532b29b2905995d7f8dadf6619edfa947a8b59efa55457375b0e2d05d31358b0373bef88259645a6e1ec71cf7104bfb9d3a63fc1b53e7d10a92048b8938a64406fbf9bb8fcb90b164f6f5d80d07a00211843e1fd90d7f2e774440ce04b4a7ce0bd508e2a2782c1241240191acec5a7bc0ba2e3aa528b41138d495cca7355a8f06db2ec7288f354752e203959c9f953014c9665f3130735edf51f23ceacd3cad0964eae6d5fa72e230040947a07f7fa2c3e0940e0011d4397823d004de0b2afd0d3ca4ea5e8b8003108c68c6d1cbf259503fbeaa09b35a003585363eabe96c960b156a21eb1d17ee57caf7d33620c716a178dc41bdde5bf9b5dd042f1036121c075a4bd2bc2a7a02d5a62fbde6b7926d0de79163ac75ce6509abf2c41ea0d87c0760bbc7274ecb544c3f76821ae0d8aebf251271f32b1242973f5151f6a63ca00117794d1598652e44c23c7381b7bd75376757732f2cd831c4a502c12c4892bb0600ed714651ced8ba5634857efaba92d7579652423fb88a2e91ce00baa2d70e0ebb03a61771ccb9bde73f2cc6d9217328e6555bb86502257723b4203b31380a0b1802312889f90bce43dbeec82b18143b00e8f030b9a4efb1a58209a81a77eb0f6e84d7a3b4fc59c0ef5810b31ec2139eb6f9f7c0ae472c2cefc43ebafee9a70063a8dddd76283f6c9f178e1dbd941d3e86cfb06801f84a63ec3cd8f5f066e307abef9a8522f05f78bc1c68471e2647fe4ce9ea90afc579b1e9dfe77233a547063c943d04b3a6e928c4a860f1c9d55ce47c1ce6089f0393fc676629dfa29198070ef39ead02efda7be26cd5e1dbbb8c568e035fb99f46b2fdae2e6f7a27bacd0e760aa1d27349f8da89e1d43a3c154482fc491d48c4cf25b4edbb652942db7e0b743d47ba989bca9a8bb2ee99362c58186705a18f67a927ce91fb9c24cc645c08505f08fc3775a7033f4f1413cf42de2aa00454a4dd0dcf09cb333927fc52db07d804cbf95ff4ec86232cc9ebb4bd27dc9f330cf712f9c29363fae17217199a02545b6a3a89ae45f29c06d318d23d61a17f12f6ca87af2200321d813e74e424085eab6c95c8ddd0f0040a4d18434c39d6518bffd5626ff4dcc9213d2767a93d0be317887374564f37fe18bf2d4c0fb77879dca020df43feaec1f3780b8ea2ef0fe1008dbb3fb65cf12bbb6cc6e42dddf416c57efe97b9f1b207aad22baf34950570b8f5b87ca00b208b8c8fee84081a37a44b204b0544c78b3addcb04efe87406168c97c66398744185d6b86746f3ed1fabe36fec9dfc9b17c0ee333aa3f616051d1e60035db47866e9f023ef337c8a974025a4e30f5b3498bde72eebd778440e91c39042275d22583789da9c57853901ed51352e492346bdb09c7638bc2785005792e8606289bcb6e6af89204bd9805577ea4f85daae08cc0ecbafce56f6e8020626f40cffae0126da8c5b522847178c00cec84e0b71e7a3b70d13da3b71ae02ed0386182b5d9ec2fc3891431a99186bd2e35173376e336ed75516213908f10730e1389367a2b4a922df8b38bfd7e02c764ba55d8fb5df30e3e7d1be107e3d00db09f463bb643121c70456d8460b177a8e3c0060f1df71b074946809fab20003b38cf364cb3190cfb320147f06adc4391a00f8b4a61c6923e06cbcd2655d2e0de6cb15ba2fd874662492438462e2f7ee5c390acd7ac5589de1f293892bf09d04c0adf95e1c81a567f9a23d7ff4e160482d2656366c5ae745b034ae74874bdb0156caadc9eb568c452782a562e8c92bb44e60072d1c3e305c7a23db7c43a1150a2f2dbd36537d360b3920b36970787e5db7ab971e32c1432183d7fe238528ca09b3cce9ca945113e8d2a581a3727e6eb886865428acc4d51702c37d60fa56be08d27ecd917ee3652043f52f1be2c465aff37e8a5dee6a2c816eb74dbb1df66104c7e3483f510ccc65599b0e6d42ae6bf78ff1d3e987381c1bd6413bc265c4290df72e5f2601dd22778fd4a6316b4d9dc5b1d4be4602f98038d9a100fadd34610553ce8714af98ccd7302e68f67c6019c0f24719993cc4659b44699b30c1118a0ca62acfbf251944c6a0620e736eed78de36266260c54a5eae9adaccf2638265050727a826010e88a299b2ae3968d8e239c5cab5a93a702eb25021b92290bcbf0cb19fa64ea4d07bdb4a2c5b9d1fb8e6be6f1c05d24c87a961b34056bdfbf86e029fdcb665487a9b22e6b5fadd2088a49a0ba1ee19a2a309d32fa21588cc4ace04a927a6b4730c7f8e2770d8508655eef74e7afffaac7e0fceb2f3b003705d67077f1b36a861d1ac463c420b54208f93b7aa1ebcdbd742ebca1a646bf0006b770dc976c47460d7d3d1cda8cefd156896ab241e08f0993a9265e452ea81dd651706180bbd2664ab323a526e39cb25542179c09be904404956f05e6b133abf36450c329bb587ec607de6305895cdb2c790ff7b2c67c69b939a46f8ce365a6a2b7b05364affddf9e3316d7b260af1eae26c1b4be9019464eca4f2267fdca0e772f300c69c4545f00322e1e6dbb425c7cf79b5cece1c400dca7a71131533987ba1ee00c55cb588f9e178c56696f55534e837a92569c04b0dd4c158cfc882a6d394e80fa94dd970e9b238fa08553939ac7af1202a01de23a6195bea373aa4000dfdc40a519724832ed84005e14ec740d3c3c253a1156f71522212b674dc84fad125fc0a5abd73b41d282109a6b1a9fd3c0c9f2f038dd7fc6459372f7cc8285b3c2c0c0860381c04df880bf71162c957fa4c535d9085cc7bb0b83b2f5e508e41be65cf0ad659348102621a36cbcc5ec424a8a87c8f4206442d36479bf70379905696c9061221a842846b75fcb6531da579121be7a99c70f245ba60871800aa2f46725f0965eec894dc6f5594d3c3ca32a789501c7970eb9e45e854b03d7fff3c9d063b0441084efc982a2cf150daf6830127b34ae2f2bc712ff3f186415f719bbfcd220fdb79b6eafbec535be8f510482600166f87b77e580db5aef2dd09ff1eda22bd05a0610a152fc987532cc918b569bac9235347b903795cb12ed91f3e12934be205e12916304b4b5d8fb6298190f6f5613c14de41eb022b9a7b774395b25a15af06c1c0c68c33c6051a6aa0e38d2e18699efa007fd783ce67aec420bd7023d6a109db0c47554947de623c7e814c0437a146d8b0ec50216b9071e33e0a17a64a1a0ea6cadacf3d2a106820aecae1170fee677d28091ceb97a049fa8f22a52c97a40dbf8904062b2867b3837ca95da0cabc5085a9881c5f713f8307d12c9c656a4804ec2316cb1e711f26113ffba8e44a88ec9cb04fe7a3eb6634bba0b13bd8e76207860b43ec1fb195250a5a570dbfebe7ed49701eda14ec11844330684518c47f0489e5423d5b086513044478f44348733d656cd5275610018a3aa0668a252de100f09ab783130af30859f9fb0e2aa252388bddc08f0014c4986f637dbdcb9c190cc499dbcbbded04162a5f6208d1735e9081b924fb6b117c984bf59072e0b99005e4af0e06bdbb15fae3a037739d1bc72b327a5707ec2638e83fcd07b3e29bd60e934f254c8ecbc59cb2fd05f6173cb12c5bd380a1840e83b1c6fd8429bae39809861cda4477116d64e7385459a6a87c22f7896ba8b8c0b140e04e1c538df7990f97d96716b6ddc4e07bf4fba2ec55198ca4151e5ae37f07467d9b7c4d17f5de094529551ef6f412e21c0749f2803f182937b189ee260a3958ee87ad1bb7f45b06a31d34988f730285ed5b3c930a7dc5cefb53724ab8d9f59bfa63a1dc4646bb07f4abd6efb1fd1b677cf92f357b665c6c0351f81a71c84eaf07e9fb9aec7e9100d64995bb8fdd87849316fe16cd6adbaf1f0e2ee33ca7b7cf0a0cfe195725c30cf357449b367690693867333ccfcddca56ddbf274b7559a761a70e81d4d588d0ecae8c279ffc21cd95264f17dda1a8579d4bb24178214cad21c3460d1e2911a0088855f9912c767c0f87d1bec9e56a61ee8ba0a988d2557a96d9e273ef80c7c0fa5ecf1ca4f89bfc0badfcf4c165108ad40e2802fec73d85dc15f809cee4f98071d75ed880cfbdfb0afd4eb50886ecc8a333fd3f26d008855db79d631f37d7001192414cba394982bfb26d770967e87765744e14515aad0570854c143653a990c9e97c9ebdcbb5612633276060d079e3453e630f17b501c715b40ed122ccfce04a46b4aa2bf07dec20d6903ef56be60a9a6ef5160391f1f9fc373fb28be5e6406aa37006fbc19fa51e4dbbb75234e4720d1fea3219d4bfff4f27b17bd1fd5bc0f5740c7a27acf597da8891609aa56ab8fff370ff9189e7f07dcf6be61915bd80e74145ecf0f7e71a834e2c33bd5a3a1b904a65322329ad02c370e7b2fb89dfa0c1d10f37f550bee7c7ea8876d00e1aeeae02fff0cdad5bca86b3c4b0997420b06074e34a972c47da5a3bf0173680a339e2de09cbec089feec8045d48d11be4c08b9050f4e54e84f6aacb3b916e6d18efbe4dbb1c5a4c49b721d60bf490c853007319fa8dd7dcbef33d56d092bc76e9ba9da8b5f183a9615dd77e9601c3ba5d10afd807a3d0501e4fee886319b4076fb2c80c9d6016e85cfd874be2229c75f970326accf247e30b13c1e639d6d6a0f488797e4865b18825806e1feec432ecc7d0cadb65ae967c4b0c68c0855f883d0a13f535e5f53394c270f9f5be259615fa40850070946ea5efe055f3a7955b1b4366ed2d8572386eca4800a2b1800128d610e6bd97fbbf38bbbeadebba80f6c8b7d8eeeaa4ae0a67ab5b99f29157d269695051519d1b6563c4cbfeb6282665a6f78dc6b3015440a37e2a0ea9cf29d46bc0000436f19899aac97f2ec73f6b7d394c5e127106cc4609080ad22174c99f3863101c31c04fe5ba2007e8a40ae8714f960e3f344faff251f51d24494f58765222d0ed6ab0f8be389a60eabf74ec259061455814a058ad5d97b380a12267c02f90e0ca69fbc324917b40f625cb2001a5ea5f7449e4e271b1feeebfc7a66515741580b953716905f717eb6b0e2d39b8238c93f3ae4a958d67d18d6ed1499df8571a10909b753991edc416c4d8e040bccd58a1bfff2a643a229b19a8ae1fdabc779a708d0d206bcda10759c9afbe4afded5c06775fd52b029c2d9a8478d8ba7f672a405d3a2fe6b4f54abb56afccd5b12e99865d071e5d17df8dc785ae34286b7ebb90dda0ee0ab342c0be4e100b862ba82e9872188bd41c03d70b7d96a192678e60900a3289a952760995423dc1bafd5b41efc618fbfb252bb7b91fddfaa5e1c02560deb11cf5545dc220f8ff6e148093f513df549babd1c2e2d51b01262501e4c6001fa384679b50b16a722860f2cd2f587966bed5cf0dcaf22f557f8e677d1b7b701de0f98aff459fdcf44854e9576f14be9a70f6a71f720e560e781edcf6119fa05a1da719826d3113751d6638368ef8410d97a4d2d35b503ef4c7f820fb4ff2e05797cf996075014b9a06b7877d1a34a760bc3c265e4f9d44ba211fdf0f98754069bf84d31ade80215793f4f2da008bbc0caab27283c538dbbf0c2ab0e60bd7203916f93845cb492edd3ce652262d91b2faa85c65fbbd70c853e8c2f837c720400a7b90cb5c37b7504079e06ecb36ce252c188da13a59a23ea1cbc8c85bc3ab80c35156df6582012bd75284555a38ef17ae55023996549a35fd9abccc91fbfab0cfc3922e6eef66c9fb9006b1523b043643030bbbce7fd9c4930a9592408cde60042f7d050b756dd371d443bb89e5eee0ff49f121d474b89195f9f287c7fd02a01add090f9d4b365f12933554bbf5ba753fde360fadf982f15fd300efb6924480aedfa21de5193060640867a54e9a41c6093e49ea05b361c42c8b6f37afdae7b0ae2c11a8a5df719f8bcb1e6de1e1fc91d1f453b6012f61a68bec78cbf7423e30ae4afc79252177ac47c52e9d8910e687b458bb753432c20ef5150ef83a89a030908e9e5e9bd324b1dc69fbf189d4833a7b3c59e4686e468a5c262514f91a2ef0e5ce44cd0bf44dc4dac866216327af7febcafaee043647a54149512c2667982099d39bb2d4f6f18cb19e3d776be903966773fc98facfc6c54dab9559423d07f09c6e13aab4e0c68c4f2c887b8bd30972c30bb3583bd45250fa20c24f9efc9d7064073365120f8965df2e567222dab37fe8631136852f81e3730ea418b2b2d1c08281ffc5046bb377fda53abff30fe774524be7fd06e1ff2edb4548eb35561380c6e8be173f003db1f47d712451afd84d6be64600ff79512e26239d3db41dcbd0fc629fd93112e2726d6efd7682f8159334adca7fe60820c7fcd5a0027f5ffd80ff2e3de6a6c06fc207b799de0d2070f45e58e77334c336f84efcb5cf698fdd003bc1f91650ba5abdad7e9e98316a36d545c7391ab0f7d4354ebab3f856ab41801b2ad8991209ee8f83dd89273da1bfaec34d2d55cc302ccb1f27a89a6eaac8106274ce5b4e034c763f5d01b5eb4ad3f3026d4910d21b4a9c9981776b74af32b05d92a2ac97900b5ec131d3ae12f9cb7b324b7cdc7c920feb122778e3fc22ead00a6788229129afce87a90615be6b707040c7de01fc5e09d9570f7c857de45a203832a786c22725b204c0af8b8dc590fdb64678ffdd9d166f290841b2a57062e000d5d414824e5ebc192989a2c5210b881004bfa900548da0ab3ec7bdba777ea0215e8077b6d4f85b8fdc48a531f704e6d98b600e308181132ae48ede2b20b180cc35a042c002d50ba11ef09c8458a592071a1cecd87e62db2a5b63ec4d7f988035343b0d1e311b5942be3f0b23991cecd4271f2442a9b57f24737426fcf25740639fbb3c0dfbcc022c46759b4f5693512fbc6e79f96075cebaa6063e09f954404bc2a8853c8c8ae8b416e64a0c4d72440d914f003569decef4755ab4e6c86d904dafecb8710fe50a9cd0b009da1ddc890c04d8c588fec233f1832373ba044320110b8bfb4c92de79f64da2f5e075a57faf8e14affe3ecafe7067eff0b8c0eaa0caf7cd8d23f23283e013b93ffff0d4242d417157a80f275b73aa8706e7e4271042872e6026258f182c4d57e95e18222cd87d9d584387145fd8ca2c7b28639b209af6ce5724cf6f96eb4152a06a9e75e89127470ad58ad09d98ab2d7e55fb9aa013e861f5f8053a1a7a51dee940c551b7fb0d955e9f145e827524b3364dcfd750b39a2bb87d55a3c03a60864a72f3e69436d3b12c4e0f52ef572f8a9e082fc1005b58eb64eb68593a9332ed49b3d09d832afcb0e0e843c30b17437bc265011b6047c5a05a87e6494ea4afcacf52b6f9fcf7ecc828ee78919401442dc17123e4408f0ab71156af738b739d8f014b3090064161b50110652745ca19fc7a013c1db031624e5969802371903ebb80d2549d40370e980e47822c1217b53ded213d2b60b5d6ee8d2db71d4882fedded374dd9f436ce81ad250b7d33f2799e4d1715d80049dc04ef82180a5a136919400bc5a863151dc6be61e1d0a096ae9d58ec85e420c1aa2f4a86ae84b16c1bd853f0d7770db49b5abd15819ee1bdc162315751e5f037c56a0f81a7381f5c63937cec0d9e13067dafd25b4bca05b1758aabdffe0f70e24fbff5d8c9ef40c39b246567368cd07074983ce2cc3693a9c194b486514bb0fdc28e90cf3c37424453934da8b8a21ec7af61cc06685611fcd7b4319d4bd2202032b3a38989a43de14e2dda684db2d255b23c17df564dd5603a931285925080926de45a26aa779f8e5a247688dec3e61f420e1ab18a087639789e1385808c20999aa4aba4b148b4359c2c5ade5eaae225f5ce0a978ea3e3ac070eee86f67ba09f06d4e4c9267d4ecd2fab83a3f1f28886f95bb51f4a2ea2e3410ba0bee50770e8013836b72779b230d7f4a1d002f7640751db5a2b9d18d7a8bfdac1386d5c40ba15e0923a7747c3407af43a530e132db5f663ccb8dc988f61bddab3dc8c0b00d253d929d5add7eaeb2bc3c4967360487908009bcf3fbe36909d3b10257faad0112005215fd3ad5253e7755dfb50ecb6441382e8e3491f56fb4071261385bfe00ecd616d8f765a0b4532732e377f9e7e71a54c11eb177f912a03679b6c3de27042c12724c33fc5e57638cd9216b12ed8783d53b65c9a1fdb37e3a229f16fb3e0e7329a27aa4a9ec6738514077a4f6f969ef4656e539d126ee1ab8cc6e443058001137fb7c7e9ad6b8abe5bc6593ac0f22af6cb0e55e7d67ee579ca08c84a9c3044195f847cab9d83992b2dbe54eeb5722d9c0317978b70e68f78eeba0771b5c068035439ded3cd65625d6e9533c5922d51d29a8de6d06c1070a14b9897da0960b5c3c06b2fcc7a99fbb6550953e6c31f94b1cb6e4be539adfcedcbbce1e9bdb0e3c2ee56bff2bae1eb33b4133d8fee9d48f857bbddcdb3400e5efd781d0fdfb0a145f3668adcf8c7d181c10f8169b7b75f6c83a18c649938b6765c0c7aaf7ec02efc7ff982df4b80585233392f6f5dc3eac69cba2ab3cb3a7c42ecc4552173e01596d5327629d703dee9d34412210ea16c5f671eb14cd7a699d62d8564f84110bc1553956e55a77184c68b3e7ea04e8e9e1db34e5f5f2c5c57437f80abb4fbe00bd1dadf1b015aa62a821bc6cfcc8c908dd0850d125a2d0e27bf949eee2ef1e0bec5ad63b0b7c45d9eaf9bd5054ffb5a339045bb10e2e5aa6f6aec14db3b9a60299244d06e4cb66048e58305d46941a61c0b47d3837ba95291a42e1bd3119a106b0e0190de2ebafc461be9e186644a801864287985c99285dd469377c5f7de20cbd69062f52b8fea468e1955f76c9de0661c8f9fd8222cd812b582d644eaead096ab7bd965da271ba65c63d466985a455d49be0a307f3d22a75407467a3252a03fa3e0cbc06e6b3605d60e1809bd353a0066cdfc11b8ef00fb166e866f75d7a0adcfd28d339b4206252be43bbbeb4d1d43319b5d27cce1cd6dee61a43de4dab07ec65f7627eb48dd8f7a8d32fa6fdef82556210d9a1b3ae93ab3705c544503504672dd5dda4030756c9501ea468ad12ad7231d27df30cb1fdb24975cc9aba940329cd303bc383e6c0aabd326a78f0bad8b3ae20b42bab6ab3e078202b16b7a3057815231feac2d2b2a95a5d0309b465cdb4c94221b7bdb15fad5e086f0afd010303cc8a640dc6a8e5c2659f4d2cb08450d7d106ce13f5b22977edf02c47b3ba0745dc18a1112b7288c6a8f253e5d7babcd0e9c25d582ad6cad69ef625b046d80bc6fd5502e8c52dc2a6dd43323c1d4a06946c679452ec51c3cc9a428d5f7b6307cbeab9e38730eb66cbbada13e0ed3082539c9bff6134fd330aab433a8ed3830a5a80e71e77ca4a1f86987950b4f3019492ca296a22f48c09dcdd5dc56f9dc609f8f8dccfbf84929cd782888b27941e08a06000bfba2113e2a8f00e0fef75660f3765891ff2087fe6c386d021170d0b98d66227fb0776346f68661b034f7f6109a61bb74086706e8ecdf7e6fe48603fa44b51c7f5546da6411180e0b0d11dc90afbb4ff732b16a2179b27afcd3c77468c828992267c7c18eeecad591907e2a30690414935dbb63b76c79453ed1fea98906b74c9f151095d78b63cc3102ecf1b030137fea5727884dc55a2bfb17d05a98ed10e58cf23c28d04e9b66e166aa9020fdb22ddaec06338064be40c5647127e663b46dd1eaca44dff65be317ad1eeda0a37494dc0205b2a4785dd2241031ac82d7982dbd5a649eafb13558d593bd92e0d8f95efb3ae899d0020af21dbd79724e4e319904c83f4cbb29b6eb09de6318a023939a593b6eb0d511620316e57251a0d54bf8dda354e0fc6b89910d9b70d40042aab80ef1482d7e0e7f7e586df9164a61125c053557076199ee27f1487f87d0225e72f9b8db33d5ff092e17067d39ae14be1ac6cd09b1bcc6ba1fc25ddd41b02574167ef9519a5751918f6ce9d7798ba35673d1b094425c53eaf95d5de6f2e0cca40e9c020546a9099c029b388d9c809d1a9b75eb956e10561a8ac7e788e3e014bc6bb80b2e02e67bb493aa3854e0e5b2c598edf64f547b4f3c356307b4fd10e0cf06daa0e0b04a473a64a1f8d7f42c31a1acca471d61adac9740040570e250baca182f8181044781aa0b390185faedaa96d36dbc2ea2ab9972a60e42f14ca0aee4d66ae8573738163b3da7fc1576e2ced51063226a1b52bf974df4ff6b5490fc04a0ef90b5ed104d04903c43c8c813514daeb2135e5bfaf979b514559c074059b8d199907acb4bf02b3062740fd3eb6c73083d5e114dbea7ca7e48734a663071304fad6cc6f7328de1e22ec6f3aa3a9bf4e4b1bfef9cf834947ae9f7d09930284495a7c1b4cb90fe04caf6db98e49cdbc6bdfe3af809c9e5486676aa3ba1a04519bd9bbbb39a6f6c11e3b59b92ef4ff124b5981276d63fe56aaa2eb7c192f00cc1156d753175a0c3b199ac083732110ef52bd08e227b1b80068892bbf69110d0ef130613443a543c9b9d9f9a705b4b578e2fef9b03e612475f18b246acc570f6d1ca51f16179bfcabbeef74bed478a1e7b6a194a5923d0d02461ec93ba59a0e727ca6d2671ea7b2ded3c364cf72b57a7affbee2247a2ea760e656cf88489a0ed1b06c0ba6e7ce6bad5bbe88315aaeaae0cd599ee76afb3d430eb1d32794070d1307b0bf9382ca359529f33dcd5e00a9f1cddb86b867c0a83b8b9016e6efd6029d956b471a90aeafd9806af13edf832e220ceb3917bf12e256e745350651840ee1a03732657b8c0c0ef3e22a1b9d425cb757aeb63e9a43b50d391b0a557ee002ddebc3e6ed665517b0bd7ce86f4aac7af87e1b1344c1f3b41fa26f504845190c0278d26fb33b5bc92f181e381a5dc8e90f6971ea6613b0975953a9fcc2e7fb0f023465edc66427d4db229ea6f5d927bd7f0d1e578763c5a453775e01b7ecbc0feb795cbe61d8f580909b36681836c85ece33b9801057fb1375cfa1f724e1e20a503df439119946eae3d04b2a53189b4e416d9db6d04e55d88fb92aa905e39500e5ed709b0efe332d58fc5b70e44cb7615d1a55c239a06bc8ac2963c5b8417705c1e2ccc9e6440fd93b3fb71e54769eb36aed208401501c36ee2a277f2370b708e01f906acf2fe0ae207885fa4fa452847d1e421fcfd64d6ef43db76095d7440b74c3f400c4f3060e8c7edc5c6acd19dbf1322f7dd107bab85ccce2ac639aa604 true -check_ring_signature 01504ac79366978307ff9ddf25e051817a2a94f1f71e5e03b6fa0353ed25e6a3 c26444038d90ac980e62ae2b51e8bf08eaea3d9e42ebb9a024bc19ac641e4826 2 f7f38889ea8803c737651de3a1be85e5403f4d742a9165e6d36d760e1b1b9342 b193744ea1cb8c2a6e780fda538e776343cd0d6c469c16e60a62793e1fe62bc9 77ab659d67aed19f3a98b3a79d2a11fa1dff903ff3588c343ac6f43139e43104e2861a14a787aabd4f9739e954a07276722d8dd9b567b8b7bdff3ff97dc5b30efca0e003a4017c33d224bf4f2ae768ec6ee51284a06b855faa9a50d643754908f6875872fb236c17354024708e507275e061096d7c19610754161ee45c8aa40f false -check_ring_signature 35797ec034e82d7115b1afeb1f25d33a74924a31d4a700d86b3d86229fc61d26 7f4933b759d6858bfae06cf5f75920dd3fa9ce3d69617c99f3737b1281c577bd 11 bfcc7e37e8eadd2d0d07c322169db608a54167f73bbf741d3e748cd66528a0cc 65f406f067c22a17672da4f239664383b79fcc742bf5f4ac351a79ce1715ee90 6083795e4be4d78df4dbb395a2803e9d78da987606ad25becdff0dad8b8cf554 e86321ed7a2d98dd8141956b92deb1c022bf582035c30aa602a93620cc3d55a9 af1b203c243c67c2d8181dbc3bf5f808c3572b1e68e7328a096a0105eff1ede2 dfcd3d3faa28c1f2758ae832e24a304dc348a7ef277134e174cc9df6b2596fa1 df927d46bb6e562a11abb68d5f424f90a694c74e633df8904ea8207462d9828d b063887cbe1a4695cf3e463bffda4d37938a3551f90fedbe6a1d976f88198f62 f886ae46554fba2c58f14ffc73b7e67c5f15816ac6d37c94d587340bf0924e66 3c27ac545a5158cd65c91096dc05becb17797ff3089a71b9b897478d5a42d64f 4a154c73814b6ca7b9dd0bc852962ad3ac31c40c2090ab2c36c0b397f4e5bfb3 fa7d81fa5aff5ce58d437537b30c17fdba40442169edad1a883b073f4408160f4aa7f1eda2a91b4c41449a5d3744961a4c7ce91c6de28a30a6ad903e2034a501f398f5be17f79dcbc82761e896626b89697bc29db9e6c0720951482bd0ae660b24711cdb8136df34686cdec842e9a9df12f2fa7c0b9134a3549d5c536724ff06a59f334aeed980ed5b347ff4ff8cd17c00a0423ae61262a907d55e6379fa90094939be02fe46d301daeec9423b874e0bfdc190ac1134f47623685e7fa6ff400b91ed7668d2853f6180d090cc6fdc4d3df067a532a49e2dd3d185e2f7656a41044dfaaba739b2832b2d23046fe2f4785694847599aa3811efd34a82b3c6b85e02268b1bb07c39f78b52e6570a852e702991e71ed74cadccfa012be24bb4188f037702412f97af8cbffd29ebc39678c82ef2747656bf84bfadd4b4674ecc13e00669052075ccb6036754c00fea85e8e7919d1aca719783e60005d85101f242a102e867ad893f736efbc00476be2ca3f8c867517025a3739a99e103044b903d310039d68db2463efada8afa6782414cb8be7e36d8ddb4ff360b6ad059165f6a54005ba5a3b57c013b1fae15f53988d251c510735ca7f7a7e35c2c54d6465df4f900032ffd846ce6f962cd53fc3eaa1c1e177b2c0016e4151eeed53d1909f100d4053c23ae2fd52ecf6b610f04794241a217a3ff9ea32c32a65e71f77b067de39b0a8e352d4885635f4933392ac239e1a7099db070e0888a7bac26002b69c5ead8058d869f994b26b15afb901b26ab0bed42d2bd396e2f91b1d39e8b4a6ae144f10b8b23c2fafab267df1e1ba23c27033ebc16584a215b076f601343895c0117a70c87e517f292bfc134761a6675a50246b4df9089422d212060cf335982e3a6b4089045df6eb8abd10bbb70662d4311a3260536abc1c901a2c5f6c7ce6a3f8db102b05da28d030ba7f999ea460c4b25e5c97a59ac4da0e712cc3814768341537f99 false -check_ring_signature aafe6c3ea403560f16f1b0626eac18dc9e2289e4d5b60a3cfd395d0c30f521ba e201127f954270cc047dd65c55a688a0a02c261068e3b36783d312dd8bf62662 2 9ceea5d814f0adc86e394b31d4e89ee8a49d2863eccd92f32c24f42cbe460956 ba91cd9f52abec3314a5d23e9550d760848ebdb386eff32b285f222a8e1574b2 c9b122bce8657ec3b9348548a90207598208ca995e49e61c73493e911bc404c400314e6735969e7b2ae156cb141848bf6f34416d132fb719acaa7ce8e19a3d06c5d049ee5413c51cbae4bde8223a457ff15a0b127d7ce5b2ec29afa50909ff0cfe7d95f2cb8956892f22d8fb01a169fe9be281f83d24ff53b96cc502b9fcaa0b false -check_ring_signature f97d4e9c36389eb32dca83903d86e738b412d0e719fac9de9cb1a79deb5c5f87 a710922602dd9d7891df50d78c8df1dd244ed7258d43384378212a09623f61eb 8 e360cc0b0d54a051adc6eee4446c516d586d5d3abb484c792e15b69c1376a392 b8a788bf7dfcd8f6cab88b156b04dea2bc337c90ff783f75327a7c32368b94c7 4ccd4f2d7aee244994139bebf641fada943bce4c7a5fe96b7f1691a033c97b3b 024d0d3dcec8858639fcc7d1d711d64d0ecff4c51e86b9f00a31f74b558593fd 990bcb241da35a4edd7b67a9595c66053cb6f56a18b28eda8fba2ab1a0c40b3d 628450cc2fbbf75ff8a63269bb004089b46b9041ef45f290813a3ead125d3f5f aadbb083008b069c45bd0838cf9dd55597982deada11f1a6f0bbb12db334b984 f7e4485473d386ef3f6beec68c0c31ab7902186b4bee28012013307604a14ed5 89e0b6e11d9ed3595834d4448516173fb9bfa69bc350442e82f2ff158c72660dfc4f03ce65c088d5d3a988a9d6cf98d04559e99479f2daa4deb9d156a71e120c109472c1d0f0cdf12df970073acda0db505d4708cbc0a974c83312b1e58fbb0318efdb085296eb51ea81de0bdbe0535c864c83851eabd0df9789e75ac0179a04f9de1ffd04f2f4e3b205750858cf7ed6a5694f90a3d7259582e9fb0e644850054ad2bee7f7c75cfbd32d3bf7f6377437d99f818bf844bcac079579583e5f4401d5c0b79b94bf582b2fd0cb9e4739b74d71f235e1d8041361e53ad85e357a620a0df80e51d66476af588468dcabbd399f5a33d003f62dbc34c73cafceefb64c0d301ae7937538a1817e33030c11133029ae10e05b4d89bdc22e65aa73b29ee4004542792c62c62b9faf8c0ad356378b989356c45f8af36d98efde96ebb731b2099058f165a4c15c40182a4b87fe14849d19e9f87ab1bd9f1ec8a24166dda85203ef7e3be56475f20ef58f9a67e9e0ead0f07ac6199825ac7768e4a52add86970dafbb5b8a98ecc0192b074b6da1912a85bd26cd2990fa126a460be05a37187b0e6fe5f20b3466ad71233cda314137910beb7f4a8f92889a1f42b0cf53421cb502d8eeafe6cfdd5c02a9568a45a1db2e1db53451839e39d09dca7426354b3f0904bf941341584735e926e8fe9a139322c2f6bb82bdba106f9e2825ce09a2281407 true -check_ring_signature 4b3685c13ee27fa3c7c4949b799a6d61cf3fcf3dcc0b0b5d163bc523bb58f53e 60e6f33abf3013caf974260f3150460adb67772965a165ea25bbba1ad20f9ffe 2 4471da1870dd4b1d670887c52da935b786155ba1f33e4f285f13573c5ef85b5d bc140365276347b7ddb74f1d9709a1dc706b8c4784a8df0614718279c0d5fd5d 66caaccdeee7e254a5894090ba8295023384d4c34f65233609829f7dffe83805e18bf2641d51d564d53f5a5604e9f0486b234f2465e35ceae867d57cd9885908887ca45cb91c0cb1f3c061200fc1cfbd576ae5c47265a118880dc96ff44614035ff3c62ed6d37853b081f1b397c2f1ad74e96f87fefd502398b7e1deac4dbe0b false -check_ring_signature 91d5d69145328db2b0456e9137859f9e2ada43794858a0f65060b31186b1733c 68beeb9ca45266b16dddb8056bdd44a76716dccbc97ef89dc47cdca8c2350cb3 11 d228faef88768e5a51eb5bf1f70bdc1f37abf92fe02dd3b767236a26f6d93f5b ee5caf80b4dcec7d7227ceb6d77fd7e692e19953700e983b671dfc9d38705246 3d1286dc5ac990769b7d2ae70d65836fdb19b7927b89c4fc19bb180ab5c4d263 bcc29ce010c0a61810fd7ffcae604749577b079b64326f16d86b0f51603c9ff6 11150530c271b48dacc42f2ac180e3838005536a5d8d35e9e613fea9c26d625e 31b189ca5b0faed14d9d44afd6332371cf39c5787ce63bcea9b2a758859a9d94 0f3f6e35b20a11cf37459b579c859fa1e35b92b56f04d1a9b3ee1a0836e51740 eb9cc00929d7ff7617631b5f10a15024be3a1e8e3892dfe7e71ef41f391b87fa 867509166d2dea7620a036da6430d16b92fb6e1ec1f23a6114595e5a57897976 f543b10cd9558098efb7cb077177e311beb27537305139e5e9beb50fad24bd25 e8fdce69689a8691abf8c2349e91976217289b573c7af1c848f92ed46013fccc 3d3f22cf0859853f3954c6f39cdcd0d7338f2baab1a72b32a533ca2d7da50606a2ffe2d032751b2e9f1dd09d165d608895fc9712edfc7fdf49c8c9c0a62b8b083a10098222594efd0760dc5b8ab4761cc4fd32946278c43715f126baed8acc069cbfb6874cb61000b194ea30046c274ecfe7347f9d9f79c8d33f3fb0d5d1cd0fe055a9a0161e676cf18df3ac5722e2984111255926f87e3a768fc237476d5a0fa2161f836223672a098e469f2b37904de7f229fe4d24b30118cb99978a6f3900ebc14a898506224f43394ad166bd692859f015b27286c5fc56705aa8a4d25549eeecf5ee8467bd4287dd6ec922434dc2c87ed567ef9b5e8367db9ab0da1f8602285237ae23be9082ab92d32a41efc6994f228148dbc78537dc97953a6e51e90cda0336f4beff1c166b8a4b9b424e90ecacac60589a466d83d2cd6dd24d8ba4052b9c860df7d53a3cff605b8c21f4bcad637d02e5402fa6d0fcfd23fdcc87029ae17ed35f861a1df56e3c12bf9d48fc80044d09d010c774be0067adf315e53a076e759732fe77eaf82b305185cadcdc01f185be5dac89c5a4e0df908ffe3e2d0a3faa8bef5dfc9c81db165a95e6f98e76ba38a7aaf3a1c4e3445b00b6ba157f09067fe2adfb45328bdcc44c2d77bee5c41760fc8fe14b0ad5d68cc252467df20dfd7dc1825f5aee65f60b082d8391a25ae04511e9a2136856fbe2b863021d1a081c6e1e47f799c9a5195f69264739ac1907f45c4623c49f8b749a105b60ea230f338af872e1928af743297d56177ade12ff67e07ce294f80d13798387b42b07c25c091c580828074dc851be60fddb098fc461cc34ef79cbd57436600d5c58b104556ca5ec3991621dc6dbd79abd387a5c5d75fda3346596599fa1a6cea26c4c09827ff05c304efece79ddb2a2f194cdbddabd54cfb8dea050c72a0dd19af0ed09e99d3069666f4ee33b4a7bbc0c311b9e5d285a5b65d974e38256ededb602ba00 false -check_ring_signature defb20ad4bc0f1625a3a7fa01e434e6e1bf005166da5e57af6ea2c38777cdf11 9d6fd109b16512f803bf2d635af6ce6d7fb669f09c7e5989f1cdf1511f5c0c0a 209 907579631a4adc0a3356241e02757e7288814e7de59ffacf3ee84000adc2c6dc 335038456546fe294e80bb7bde66cd7e9a9b9741ade272e630570f0e5bf063e4 7ea0141d7a32b35e29350f551b547ea6b58e1973e6687bcd985bb904850bd0ab 8560d53670b847103d090f0c12661842d64fd84d9af9d3195dcd76a32f5c1589 622163fa4a24b865b48b4904ee9ac66d751c91e6555b50dddf27c43486d8edc6 108d2501c9e4aac2e51d4831492e8f4eed7e5c2a5a6f4bc7a456ecdb7931c39e a794cf5742dea619ae47fa9bb26649d1831b65fe0005e849449eba7865336221 b3deb18c74fff3bed9827e81b6975091d4c526b98b57123ca9446408584e413f d4b79bfc8f6e2d926fa89be28db8210772fd180518b9e92577b026505c013ac4 9a39b2c0aa7e7e0e5c24461c8f70cb93cf525177d4e208ab1df8196570b0d560 d336137c5f9db5c78c828743330df60c01e053202acc681079a604e41cdfa2f6 29c10001ab4d6a4a0c47db2a756ab14b02f8ae2927021b7d6189f00182ad42d3 c385fba71020a0590bfd1d136089b559b557a20e3d25297c3599f70e5f842536 5d789c5e7cf6fa9ca565425633dd59ce5edbce03447f42095afafaf9dc459877 ece7c9e7e7d444e9f7e7736391050a3ba0143822b75223907f8d836f236e711c 3a5109aaa94ef372ee51ffb1acb95cac27273522c895fbed23a557108f87c7ea b11f1a1ad1ecf6f1dfdb3fb0800645c40b597eb9f01622e424a878864e245b1f b926bef605e59b92dd8d8aadf44ccc8da4207f29bc6023309dc5694686d222af 5e550e3e872bd9bf7b2df50711b2ca56dd2fa58878ec3d9e7de5a3c79f3fd473 327ebfee95bf62f5a13b2a80caef70cf972a1710d77c84548b4af33ee1dfbd4c f0d813961fb47f211bbe3392f35954797bffda90f690725518e664234218f755 ae06b063a398f282ffc7a67a7b196980df904ab13d431445b5c85330c55de7fb ad6260a88d89c0a2a09cc33bc1013161e1d41bb9b527466b2c10327b5937de5c 4ec0b6e5ad2293e0c84c1f86008e6c66be4947be47adf5f2877671f214d8c334 5a5714d03a6bfe53b61587b9a43855624724cf1beff53b2cc116489b401d8041 ceb8664ea417714984100764c8f367a3b28b8a4cb3b6d9012795eeab3d351433 5d554442dd4f53767df674ccb13054265991594505d6086209587039ed79a40d 40f3350100845bca3b77afda05734df0b09e43ff7ce01ea07cf6913d99e900eb 778d4de649442440f14d831ff89bd0b11a07a8a2499d229a9f247390b42dcfd5 5f38935af5b467cf52df88eab7a6146902a970b1222675ccb18cae8a25155af7 fe086fee2b6c6ae919296e9edaf2588244ee3a93dee35292897265b8f0d4f1ed 4c5eccbee0e669b38b37e9265f0fbf3315603c5c2296816de1a6b75bf429cb18 c7d3b3ad8cfb7536748cb417a98e02192f5ed96cd4dbce763137a9c39d1bc9f1 66c1816b86adff57e3a4e5c2cd9c77afa6d2a5a9049bd88f50511639581d3d8c 544f3e7bd69abdb080f1922e8bda2500269cc2d83adfe715a2c6bdc00d8156cd abb6103df565691641ee631c3c6e32de771eb991870e96f8413b824e3d17319d 45518990b77d008838a5fd9b5e7ea3f1f13a248a02f31688e7934543d7113697 1b84c6315fbbe4ae9b5dbca7758326d3cd09726055bc24d3144c2d08100953e0 8e5874c6cf34b75324dc811e3484f0f26d0ef0d6d07d2dc980f7078e7da6cddc 8f78ac43efd3488e98d6455d53ee50dc46a45354ef55b952ba64eab61d2cb6fd eb92db68cb9ffc9acf16a92c9e201c74bbf877a243f9764b6598608f20965738 cf4cd948c2510d5b830291bf28d873df42b0f72ac6d232a8f8b50b2089a71c02 326ec141a2cb2105deea27574a7cea9b55cf1fb590de439c03f7c8280efc0c1d 06c82eb97edf3b002790365317663e4541ca3ac03171d9500363731489d27790 b3409f12e6ef9589d9c554dd8e9b68097dfdc06483372944bd9667ffb1cdb53f e61f05e7e0d6da38ffe8c2c759b224f66c9b04ba2c7376728add648a1a0fa8fe 31f5b9e9e4959d81eaea2866d94b3576f852c785edd5f93f2fcd340815a354ec 4067fcecc0e487f42070b4b28479ea813c71f5bd251e47c6fb48ff0d40525c7d eb24140367a4a54307b2e504ba9e9652e6baf1b280520eae4bc9e98c07008ed0 fcd595e9f02a1e786343a8a5bebd5c338e35a48a282f159f133aa2fea6bbce09 72f93d6eebdda243de0b2cc1f2730948ba7c11e76c690b6faebdc107353eb756 8b08c2bc16f45c3618e6dd630480e9f61256c40206fdf70464c68ea6e20e48ba e665154f0889822967f5b5d105cdb1d02bd629dfa5575f4dc1caecd059cca524 4cdd82a7019501a002e0f46eddf0e5af584bbe576e8912fce300e74412c49c85 8910349311804e919ce48eef679c9d06d254043bae6ce637877815a51914a90a dc088f823385bf5ebbe589b2c3c8da54422f30d17341980934349bfd6c9b0d9f 1683316422987feaf1330da9049ba7ea9b136f73018d5fd60ee9ffe790536901 725339f431e0b9029ca24ca06998b4e942a622d2e98d005256f577d2f207fc57 ae049df038ad8965f7b9a228d1a0ad23b77767d94a2594452a69265874808c9b fa740b0492cb9524fe048ecf1fd896eb4a87fd155fceb0baa3a2e58626d494b9 e8bc1b3559bb8891bc003b4f2ad9ef8255ead8af3fcdee1fa2b5b4b9554bce80 12a10cf58537b8de42d3efe0898d12273fe82360e83de3485711b50310867968 43f2e9142649441cba183e1ba31c69472d06a0850c5d1ce2f92fe3aa28705e3e a20bcc088a09711c99f958199d2fa561daf78105d00ea1fc99da8ba15505a35c 6cf9638732036d2f6e3e07706b090c26a66016478e7eb46725c569df99cce55a 32096a2e26aa4cbb7f72db5ae6fa4115081354c970cc6045a1071e5488a13141 5379695b58add2d6c6756edddf1ca5ff2eed14bebcaffd10e5c2387e96eed9f3 d0b0bdc311e217e49b3c41770a57bc6faafebdabe0ab4eb9d79a63dc843a2564 fba1afbbb053a6a460a8ea1845649da3c4f7d52a06fdc3d6ceac6e7c3526a3be 2ac3760ab2c809cc5b4bf72fb6f03f34799323937e16e2cc0b4b02f8170ae62c b83e07e712fe1b67a5ff4ab6a8f2adae915417dce528cf6f95430f87ab37da88 756317d8cc4d7d4263590f4ee278147affe94c0d966ae0c0a0f532db54f7cdee fa1a96852f6a864035f2a11a89ce0275111687dbbed469433455f61f076d5d76 92ce036dab131c6468281cd0d213ba4bd05549541ba0ef83eb2f784b053970b0 12b4c7eac839bd9430aac561b5706699151947ce9b0a221b6d9e86f4eee505ad ccd7bfd7c808b570f111c3c852b1c42acd2358de66ebec74319b56c7244bd91c 2c817d520929c4ae6a8758f4a6bf0e9d491125df26c4206a6637b712644cbb74 9c0456cef1b652a971692224fb931101daf1b828840c0b8b6e46acb176f2622c 838cb78bf072663411944bbe9c1ca69e502762470dd658073fd9fab6b7732f39 e462125f89dc93837b8c32cbaa7e1571fab79b27ff0c99ced6a9163f5b1b5b68 8a82ad54d6df05e34e220ea5444db184a49ba249fef26fbe8373d1e3c61630e9 06ec42832f8a89f6b0248b10d381636544a756e43f87cf65443fec46518d023b a0dde39694bbe8fe22760123f62b3932fe8a97370c384454024e51f66c82dc45 8417a4c0047074c3affb5b93db8f812c069e4d4a4bcce823e1b13d01b98707e8 261fc5da14f48b592f4d6ae5c7fb83d7cbd045bec28b40c2f564a2d9a1d08ae4 e2ca5c99385a3971eaaa21f862c9227d6a9ab3d1161ad570e1c50e83dca18c1a d9eb0b138848c6439abf81463a5987c56fb2dd7160e455a25c454b1c09a3b3d5 fa7993d710487b5d9fb15f951c7382f4245956111a35f2c95841c5ae158d9992 2a2bab75b87457adc7df69af6f0c50edd96b051924475c853ab7ac96df8238bf a11b833c67ed5f09c295cb1c8c92bb0591bb74455ac1271c7ac2929657189f18 8ffc1fa001e07b83e138c05ff152218b1197a8fc15bb3f1746cd77985811c522 61f8f47e91e0fd4ccd1adedc48cc7067ad92a83fb4aec9a3afb3a8440e87b50f e2fa373b40caefcff6314da7f4bcbea7133cc8babc08b1b772926b55a85e2245 48681d3a48918316910e0e02da52068b736c9fdf6dffe06a3e55b0d50217932a 149bb767361aec1dc684bc3ea6def29ea8f555e426c3e40a2144278e21ce51f6 67d6af97f2037ae6cc880ec745586c11b2a1ae01fb02d607d38ae42768b0d0d0 41a5fa3b4c22b36892f0a563544e8ec6aa25b9c2a3c8e43c76e8cba011ec56be 4b85936372eed7380df021dead81437eabaaa9ceb0a256ac848124c999363b26 812e6013ab6584ecba8e17d3b69a8e73b95fba1f3c6efbc975dd3d1addfee70a ccdd24665999ea67070afdf91e398b7e984e3490281356adb27863ece43a4cde f1810d07955710f13f56ed8d3bf656f40d8f52205ea98ca315fc82e411eb17fe ea6f46a36086b2e42c2de096671a2410662b245dc19de5df80dc2fcf1bd3e2d4 c5d92c7950d1232e01d23ae691a3454958b9c5ecc1696e49e9108e1ab74b9f21 c50698fc4e1a8404930a26cedc488ade9a4f7b9deca94a124e56cf910540ea05 b6472657fcff3ce9861dbee970e2fbadcf6aca1bf87324bc846547937cb012cf cb0c26ea208d75cfdc9ae9e28ffafc62a886f577e2c766945fd4105dbec789b5 b6ab7a39277ed5a94c5900a8f70b0e081b1db6cef66b66333491a044302b7b8f 99aa6fd54e2a55422c976f01cf26e4bb163bcbfe4404db95b0a0c230ee28053f c9cf3187f9a0208b8403fa26f5c71aff91525a98a147e38f28698caad22fc1fe b05417809638411326045f00927a702e6e913f44a5dee440f1382121d0983848 9d2211c3fd7fe17186939a9f49ba97afd0407ea17bd1bbf779c8ae73a5d42a58 ecdc410757fe4155b679317dd626ecdfed39beb317b01b5ba9f7f3d40b6dbc27 a0e2ce926589d49fa5c09918b3a6794055576f94f886208f7d2237ed83797967 4a87853f91c23c153307fc1036794c480dcfc53e2bc8b69616e1094cc1157e1b 00f51ff713abc2378ed9f5b0bfe7f82f9df38fed21f647dff4c2dfed70ca0845 0d36158a902276c16dcf58985eef08db7486195f034b33b567ee570e7b6f8a32 0b3b56e6217914d34062f45e4f0b1235a27dbe08e4da2158df17b465f9f4bb26 f807a22344936358de79dda9e47bec38aef6106840731ee6bf698aeea1b92d55 fbc06d5b7b9c50b56099c5bcc8e04eb1aa7b42cdb9e9219678b9c4938e2bcc96 7193e55d556220822bb2d9f51302a9a8b1c523dae3b1004b68f68a25f318d2a3 206e6df53f4c94af60458d9acfa790a70e059025e0989970bf03b8b52395745c 6d0c6fc06afe964e00c2586cf23f39421eeefcea6af74418409fa7ed39d0490b c7cc15d27774d0db7f3d2fb5c108ca3a3ee4a15dfc65e662d7fe26b325a534a4 31281194e314fd5ea7e10cd2653ddaf940594ea64b4c4be0626b894ee36ee43e 755f01fdedde8d00e617e9941871d65b40d4bb0caed62ba105ef75445e5846c4 d16e885d9b4e62f4d7e426df1c7e613408a482e4ca60d393b16060453f0855a4 8d883ade599534ffea83e205a09bcbde20a8285b1bffc94d0b74c475dafe7088 d738b18ac2ac4ca47826405b46784800f57be40cc4b8af4d95aca992a2f3bff2 7a91a8768e15b9215d228212fd3f961f0fa6d7d8046c4b50c5b663058af98662 a000b2f61310d943dda2746e33e5ac2e123947727010493fdd4a7ac0ab1a99c0 1eab71ec60ab85776d99a118509ef9bd180ee0880f375133d3e25c034ecc05b0 f672f805d05d8116951623e3857eaf68c475d63131e1a3c1926041f5021e1fbd 42a3425b17bb7192dc5d14a7d02c5ca39bbc2a6480bc980df2f9494689dbd8c1 358fe5f8d44f2e9086398133a1dd7c847a3c1bcc070231e58bd7e151893a7730 45738fff039883cc26ce07a94502d07fbb6f39db6f9b379cdb4f6798fe4b0fae 6838ba53a71143549ffa95754782700680f47eb3aeabbc40fb1566e14f4f75d7 a062dcc23b9d1f12d6c4c7485563800d328416f8a2918aa6a1413399f4619908 bfeb27b8ad4f607a8434effce86b8bbdd8ef91a0b93869aacbfa720f6ea4373f 7eb86e3efc6350ca38c7133268e33a3fb9fb843dd29bfb90a7dab5ae4a3b92eb cd975b3cf5d312d163f589cb5ded4d4342e33e8d384e9c302e4876308b04c57b 862d90ebe20bce1570ccffad88ca37a2d1533904ad4764b93bce1d5384487aec 394deb98a23ca8cb481cee4bf9b0ed7fbf27dd917c73a92481d431976f847356 0347f6c69225e2dcbc0ebe632badea902fc9711a04ed89439d8ad2652c6ac603 5fc82593f36b0bce4d3d9c2f67c658f417a8780c8576ed003e85f8878364beac 2a5b6ada0cf953db1d28576641b57616ca1301171e20bec650193fa9ee3a2ee5 d5988aa8e41a6d421a89f11cb9d4ed1468b18320460df1bae0076e76a7af6539 81466fb4eeb80fb46834d318a5a91ca5008fa3b34a62bcc0ed0e0b9a8a4ed4ed b149691a6210f4095ac8ebea4f8c7c0602a4c472898672f5a1b42bb55aa7db9c 0b491955e72a0947e8dafa2c25fc408894b2466c26a47c5aeb48daf642115e85 581647d93c01c738120acce46f3cb3d9692791649dbaed4c35fc7dea89033e49 d25af5457874c5d08e9cff6204563067add715bdf89cc248a2b4b596519e1394 dd86b13501020894e8e51b1caf64fff2e2303ea712943b75de9930fc61a8b104 3ab5c9da8cb31f72b0348275960d1f0e08dd2409f1195c3fc36ec2bdbfd371e8 124172288e5e4453b26e1493e3b2397266f9111181caba319eae25e768b44667 6b7a059246136dffec064f6e346e91c7ff23d1a2214f80efcdf12f5c6741ea39 69ed732a7774e9b1f3cd0293b2580590749f153f6416e38fd17294e3b8cd9d1e 3c874a1244b8260826b135bdcf31b303be6c06a4615af4c16ab1f085cf74bde6 8a2c7bb3f2ce6c395e10e09f1cc97b1bda779954288bd17ec405a3b5efe7a6b8 12651a1fe812e2f4d3c70a7fd37c94196adc794d4584bfb17abf95180f63a1c7 27f499d5ad8d566248545140d4005711b61ef5f421362155a39fea0c39d5ecea d3548193263d5d2b646a424ca93f801a8270e809e9c6c7066951d8ad27ae042e 03eefc3645c3e452e87b63174741657a1a65fda4692a9c3c0cd455fbb3c8c442 a7ccb415c15226b870c6a2eb3dccef4e664d25f62e634c946ceff3da71304e85 d4593ca37c188edf5b118dfc892ed5d013c0bb977eba6cc5e0b9ba5dfda63a18 55b2cf6f8797cc70368e96ba299960ae6ea16bbb5b8d0e708e4919c95c06b92a 874c3c753370983d9fa0e432196e64193ea7e637d449905ade990af214e607fc f1f4b746113dad2798a5b9a41dac8aa70369edbb4c696f970b4ec3ad02aa3594 929cf4a20805416251f7ea6a546d4aa22022369413d9a5d7fea15d44e5c8b9d6 0d73d5982b6249a285c9a5ba81611d686df9bef305bc07dcf5bdafe6435105f9 65c102efcf0d9cb37a0a8b47823966adaeb8914fda8f24c4e80f15714f833f3a 9ac32abdd4483906581e9ec1543cd9c176a7e2e002571cdd2556e4fb0363620b 1b47925adf3b72f03588b2b6e10952ea77018f8b6e8ed435f55e84208456a327 4a157c998cce763c288de43bcbe8286b814e99e14fd8f30c7db222eaa59d056f 1b09a019d03b938eecec2fa1b3b1b73379498b735992f6495739eb446edd3ff0 447be4f34e99d4c6b90050559bdb13cad17e18fb9a542fa9a30cde7a0711bf87 6c0f8965bf97bdc4463bcc8e5429a2c0bc22e60409bea427180547fc22f9952a 2a2def434d54ac01f185dcb39ef40d132f3d6ebf518a72119efef7a67d27b721 2c2a00848669b852f01eee41d094e6ba2b6ca6ebc34b6941ca66ece763584fb4 1a3aa2ec3c953bf81e8ed8d0128dec6eeed1a9c105d7f176c3aa6e719c95f7b7 b6c4899179f221df95a2ad3b91299e90c301061063f73b4ceb3f128f9bfd4591 91013ec0efb74c4376ea9bc9f164ca0928b7bb22863f34ddee082cb88f00d390 890bd842e4448e640e1dbb8baa92bc6b3f35707441efbac04c77a49c2631d6aa da4c461d2ec733f558147f56aeeb9ff131dae54d42897f1471a28921e422945b 8553b568f52025cb263f87101196a8a1fe33ba590c7d4cbc8d4fe62ad45839ca d94df6800b4a1a74929b91306a1899b1a078b1350ae2899546702d67272dea13 31219fff305baca841d0a8fe5f77c5b34b5d5b401fea28b8fe9bf14e65e53507 677e11dab3c7e4a48466d7087883a2dd55f40a5ed6bdfd254c00a5b48dad6836 eb5de00d563ed7bbfb5c7548275d37e464ba7c88dc1482ee7d0e24907817fe20 86b2637d942f9daa5ab8495d342a7ab19a81183a9af3af3e80697dbf461cbb71 be3e4e23b945ac27d576325649c9906282418fc96634fb504a24aa92cffac16c 6bf3a7b0cbc915014d2a96544e517c62e5b6f2c87671a1b92100178cc21f66e4 9f0c42b5be9069ff2d55ca24f0418c8a751aa1832f5ea1b6f5befc14d170395c 84d7cbd1cb628c4c4ffd50b14546432420054498cd05580df2059badac3b427d 8772a3db3402d9b915f7508b2ff1db60d5e3504149f99e4aa07cd3ea0901c62e 591f61ad369fb2ada5a158e85427850f707397218d4d68005c70d5e278f39451 63c2e4fc77326bc1634423fdebdaf1ca49648f97e6a8a1381a15e47217c5c5a8 8f747d581406f6754eeb6948553b8029bb9e56f66fceecf49aaf8eab8cb69b27 37466434cf11cf8e2cb4480c73044f056f8ef584733bbb0e74622305a6a21662 507132e64004254609c3c8c86c9508c8a97301dc19bfd7fd8c15e99449294902 cd1c282c4c3a7b281ef1a35303157cf6f52be2a4d7cf10ca75482b1935f00fd1 d468f0fa848cd9c4bc22f7428207fbe1fe2deca06b6731e02b6b675425193d25 f6b910feb3ea81f554ffcdb389e8480a84e43ccee563823c82da89abbab90fce cc4496c6e6e2d28670e5eecdc1fac2d01881427986cbca55600fa1db796d72ab 0a8e9c08167ee671872882311e27b9ac6e1392b657811b18e2486ebce6062e65 2dc08da9bc599e5c57f608e4a27402499529120cf134d371a1312c2cc7045f66 b2379a3532946a4533417d9323a466e9df75433ecb93d3c8496fc990dbadcfcc 60843044166d026539f89c5a0398ea3c43e42fce6bfd4ef5dbe29635e185cc63 00d9d9ed45bf18623ad1d0369f60aba3be3020dbf49216099768d807f950afc6 d17f85abc4c40f76cc8c46a547d73ab280c84705534ed6b10f7c2b0e4868ef48 df0ff10c93f279824446ab72d6706549733d2f732e59740303a7cd18c737360c0443daba4d3b22cbb7031ccbde4734040c674368185048b44484b772c753ba0cae7850f084291b4b9b5fcb0f59d045cb8e4c90860b7cd19cc222352ce1c0ac0219da9c0ed966af4242276422dd64051050a64dc168a93e56b4b9f6dba07ad80c5b98cf661d7c27c99f25a19c40e4b69608958a78d707a710d4c776ccaba05f0688b7923859f1eb7b5825e51480de2fffc337e4641e5be770f1a072b4909a4a0630ae2735c92235fda24bb74e38d06edfdc3199354926c1003a2fb0216fe7fb0e84a736a55ab0e140538bb0746e6fcc73a0de7057bd9b309c90dd0750fba1570de5127df3bc6cbeb7967082acf64376f79d1d0cce57d6cded9303df4836917d027fef94a7bed34d2aefe649b6b6f1b8f9ccea8e182b680423ddb0318f7208ca02b3b62c8527bb626bd9ca7b3bc406cc782f7b23c910de7ab135655cbe5c0a4a0f1a26a603d096b4762c5879cef2fd0edd214f5753753164c3076bc67579955d0dc615fbe4657cba2c554fea887d3fb2ec7bf2e76ca9a36828ee80d1553e183a0ccbff31389b9a510a30416e992625c75fe80b4231547cc80fc684122436750508fb93e3440ddb62c4c6f741a8d6703166697ac0d3c1b6c1e6fd634ad3ca89c40b61a017e2c3f5fa0ec2670d84b2c765a900d3581998d36cf6f851ed84aa153b0178c49ee7c504ffead4195ae477d845689016bda3f7696dd5bb0c685b0e562c007543acc57a5b8cda546e6932b960055c924273db862014c0dce725d095b85309d410a8d2e5fec2068cc1dad301f049f311cbc6e0b991c6b51ccb5fefa0c44505362459939a466f82e8363ba19ed44f43926ef28e1679f7672745e3905680c207f74d9bd525d5856d8d057dc4f0eb31d97b7b38f74cf725e429652bcb2a18d202a5aff9ca88b3303f34d461267e9a67a59dab78312e93cdbbd4dfb48ea52c8b0d748b5e984d48e98c9af711f2cbf692fe1effda676f21b788380a6e899b9def0715ccece44b7a8188866c09125b539beb33f657087c1e790709a5b5601abac10b3899681791c28a84a687bb276af695cf7e78b36d3a96bba8f971d99e3444af04786b73c306d44c8d1c01e9ad98fa281c31d23a52e0970ea1b5db35a36238ea064b2e5d6604be52ef1c3a2a0a8c87447a1c526261aee574e03b03d2b035c4e10db829820fbce043918922d6d6a917e7577deddc4f2a6c644e1ca4d0939632b00593a69bfb1440712c44e8af7c6e663136f4b5691a0a407bc52322f1ef1ff38b0d949c1bab48e4ca02cc000f2408639ab84249a5dd0629913c13c422ad0ebb160c023f0647e5cd19f6ba701c29e1f28f89aed2891a8b2d2d9ad6b06a67f936f700577e363133ccf7f5c4b992d9b58ea7956d759596cc01702744805c7b279da706da335ce2bb863d7d48a151ecc11513ac145f4c35d22fe137b674a15d2d73a20e3752789dd07ecc0b9fa18b45199c9d39da157e4701facbc000420d4bc4bcbd0c5798ecd1068d41cd419287535ad213db8caff3cb9614717076eb791c5fd27e03fd16bd04a48e864dcc9b3c045ec8c0ccd8ffe066c967e7b81bd1d7ca7f3f610fe7089ec7a2194de4ee0e6d35c5571134b96e370a46e37b91a2095a25d9373d02f612afe2d1680f4178b945e61e5560a9dbea7203820d8f651adefe2ce2cd7c034d2d630665a54f1ef27299a177f74d89fce0c23a88aa16a870906b05558d1e0539bb871534ce845972ad8b2d67ecbc13b7eccf1314d95013b7b4bb642934c700cf230bdda7756685f865305699ecb6faec112b30b3dfcbe8df6baf1c71687d088a92fa9cf991e6c66f9ec9a630901319d36565843e3baacb58a26747e21e680a5649aa89f73a355502cf80c8ac5b649d12962c6c853b31316a713e81aa49c102b88d316f83b6406ab60b50d61ea41cb9ed755ae87e155e0e0b7c0f9a2661ed0780306297036885dbec85d12361cee2c8fb4a3d1557c52d4b5f0ae7121a59570311c6d04f905211da18de7f012058176e5dd039a101f4a50bb7cb7b72fd81a70c15077e2acfcc3ac25af5beb10f87a803347db21f867f32ec51ce14291bc6be004ca0c4ef32d441c5d7eb6e001f9051a444e6dae94fb3c0afd8f060b41aeac70f8f892ba24f9cc3607b96d6e63f7ab35b2a3ae93778cb6d0638dcfe8552b7e80bd76b81dbaa6508b3b4364cf7a9f39415eeceffaae819cf7cc40acce356abcc0e8621d70d8a9c756830b97b7722900a21defa69746232467cdcdd605c6f28020197ee4cd9954f7692afed7f917e1aa6461a973fec449ff81481cf0367d615490f9b58a119d8220d494a433b4303caccefa0f5ead5c5d70114f1ee8ef7f2b1a301d80193823e3787312a3d1f102465e97dee1b73c0135ff2a811d11116ea7d3e0c102809a2629206e59b94d729b9b58d318c9153202f86b5def71fc76e7586d80f418a264877851a16099172a6a96470b35fbc0fddee8884efaef6f0d4a998df09573e892428e12c3e08b23954fd95948d5ba5bb2e355092f93cb07411a33f6f0f3313b2ab8cfe7cf59578c0b85bd42445cf04d36a46d6385f0e85c14ba1b30e0fc76963f168ff8519434407b6a7e4a04679bc44a94c03af7450d21d55537a330cf4a16e5fef07a05d4fc6858831f21f9470bb4091d66ed2e410ff13d34c86010319b21505d453970a3a43bea3b663d8e1f2c536a099ddc52aba9fd937295e5405f2e71aa904da844860bb0b15a496fa802c2c018b8f06448d22b01b4b1fb8c309833ff386ee4551355fdb5b52341ff14a4d695a0ef995b7e5a75ce033040e7c0db366b12200d8dabc7d27462df4be19212005f6b8b13165aa7e2667aafe8c8d0405092e3e30f4a27336a825aeb2edaeed38da7b7f94679d41d4cf6f90039950024a27269d0b9baae1d1ed83e543d0c3f7add4edfbd435cae957163c1c889eaa00581aa8c9936114a37e48483bbfb3e72be301c4265ccaca6141144aea7be5160c881327fda04f69e9078d37f0e875b4cc9cee512c7d7c366edc4328085bffbc0d762efbaa31774bcb0028b31b741deccef91d9d7e264dfee61af63bec80ff03028bae6ed4b521e85abc8412b4792cba3c014e205e79b7d368e7a256750110920159e3ce8026b312db1d1ae356f9c94f5c1537153a74f84df111056d11bbfc6f04e5c8796ebc417f1f4576dd3341cf9bc51f997feca0338e6f0ac4df3fb5561f0820fe09840ec28697a2500646e0ca1fe81826c976ae63f80d963fc03ccfdc6c035da1a8a2d8deac357f99de0c8a30942638b726683a4766bb6e4704cc15350007ac7f0932a61c4903075ee26ed43d6f03230b00fb683602158c4ef6ea83163905acada08315fb17b281225296e94294ba499da349aa6e87903b1a126b7ccda6082ff264fdcef774d0d8c9b32faba8570cbc40e1b26500af48e742bcc792590601ec7ac337487f24ea0f341bc0843594c436a3051965f92acf81c0df4a5a3dbc03bf98bdca34f1a57e14366ef9b401db5c56f4b3ece6ef08196c54f21f6df81d0545f54dc5b1bc959dae75afae51f52d1e7105442713cd21cfd2c891b850428c0667c75004efa354671c0abb190b55d34dbce41215fa870f49193bda3a47d3b0002a12c91cf9cb2070a917826471da9c68331eac7857c64c62d3a26d806e0afc0cfa196cccf4e130c6c191db29fdabd865883669dbeac5372f2b88169a59292206e9f47e7c75b888d6dcf74060dad69490182915fb9399a38d3e5b050fe449da00c94041fc2d5106fd0e1468f861b09b23c6f30f9042f29b3ed500c59a63272f08f617cfc2824cc922c7d42efed53ca38d46436362f02052c27b2dcccaf820ef0e2914585ed90320fb2961e861ca37b0fc1ffa77243a130de51e997decf0b1fb0b559fb58f0690e4ce276cd4a8d6c23f58e77c409f2eefedaf90ea3f5780d11b0f6a1fb2407712cd76aa86e2a7fd3a1e6482754c8671a0c89e3eed01bc3c2a2e0bb02e15de6dec622d4c2b0ca2a5da8de5865332bcf331fa466d6d47fe5ea9370c33695986ecfea04a44df8ce706b43f74ef858da72dc0e1db52c069ace15e61090c615beea7b39e93d6766d85c82690c8831884a4945cb781548de2b05d9a78057330a04d94c59e2b1cf89e330a5e9701d5eb10914d1935104168dac8274d20020811f18016645f2fe3d61921faf657b8ca86486601d304b176b5d92aca4cf7037383779be9846654301ef8ffd21a18fb62b3886379c4a592f7fe1d8e8aa3320598c58ba5913435029e2e3e550aae75e984535db6a09a1625c3e5c9c9c249380564117fb043c1c97b58bbb61651f47c9f394c45289fbad940fe850df0183a310da32b9d6eb322c64d70b2c60b4f0c25c6b3f9c10c12e94fc193564736752873002d6c743c7b616f20ba5efe535cb30f72ddec23857cdea5d594735aba967c7e0d880eab268b6b9fc1cbcfd711a10edfc2254220941b2407bd05739e6234a64f0366c9bf93bf9b4dc1c3218559e4226d8b140f239173fd6c4475c4436622ac2a0c44f4843061f1e3a73f56b49c9610643d8d6a387c2d76eebc63b6971a36e04b0803e0e23d49300bca37607767807304f4c8650d643af8294ba41f4886f8ca08088579b7da59d7910315fc9907e45192b0236faca31958d374686d77d5dd1329042c177ac107cbbd67fe0ff3fb67991b9758ae81b1504c035becfd130b3596a40bfcdcf01fc982f789211d717964c953f153bffbbbf62a048ff65b7a45dee9920d9f786920750ec0e58f506f3157b3587755c7cb36f4f562313250978e0a6b580f54aa8396c9098bd0a7242501ca193cfdec1aaadd7b12e8b3b22aff6cbfcce80ecaf955c9cbc8df0eb6f45850f5fe31e1d5e345d3ee32b48efef9a6f7a484340b96d9820c971889a264c34ee96b22cee7e614ec48a678fb518f8bc14b0610f40f41f4dc997a0245ddcbc8ae3ba48ea9a8bf70eace0c05451fc0a5d37cf876780a2a91785307b6b518f104273b34662e1fb1f42c12599b71fd181cc8901a45af00223a33c2d03ed9c1e145d034f64ab89a8b76e5caf8f645f67a24dd1a360d490aab69dd0649b814a3d27b7eba127cc28ae3c5cb98680fa3614cc7c9a2ac7a810ce3060145050336a9dd5682da33c9ac23a549a3d2cb70ed9cf8d11e4324cc2b09871e99c2a3562f93ed9a6fcb0157de6f3a8926d9e0b87957c17951e4c31a240afbde698d87ffbde3cd0163f27a535de3881727a93b31630472870eaf82a9040736d8babcf3f1ebe3e6ff3617918e511010adec9d79ea0442bdaab8930176500fd4812c115354501448ca1cd9d501226463015a4fb4443f07a1ad4c25563d5e0e684daa68e90110d3954778eb0437cfaf8f0561635183ea3d3d60e98155771b050f59d273e73d6d5b61991e6e1eff887bc595c6c09b33b0fc03c44fe70a49da048ff8986ce4ca77ce175c0af0d367a85fd09bfedb61b7e1df7039fc40620613087a4e975d414398f4948e55f82f1f6e96ed7399b4e4fd86050995c02d78402009bdc9cf8972dd58fa1ec5dadc5ffe3cf0c267edc78c8c5fca94460466c653c504ae234dc11dfbec9fd7f8219471ce50fc7d7471be2850eddc51fca47f719a9a093e6cdb2994dc27fd46b6f2846894845507052439622b064077c4f66033cff00e716568eece5558abf8aecf75fcb675acf95062e8ebba6717ca1ee7dec0cd920406fb76b9105716b64f4b8df4d7f6113c2e3ec39c20d25c3f51e8874167557d09a2947995d954dca945238c95b54700e62e2f094ec69eb57ce3421a50f37d2c0e8239ebe387c9e72d42d9446523bab2588413eb7e40ca3be718318ebb6ae9290b1b2d3decd3a2e16f81d6413707fd1362a6a8dc6195116907d1d7d0860efab9070fc89b10da7514052d898aeff8520ccb4c3b6ce6c8a8eecec801da6891a46c0d651f97afb7cd1b1cd11559900d173a6ad99fd6012902313838c160be7a01cc022e7650b4ba1aa517c9b830e17ff0ec2e894d522dadda0eba379ebbac2b371c0c1fda336bd6e5b53346fcfef3d5ec8670353bbf45d4f95b2dfd9a456b181f5c06668483399d69132b281fcae052bd94b7caec3b66c64f2e5c10ed8d3306327e0cbe78f867bd23e4f2545d4a16d26e48092681df1c8c322e3ac927f0f14720d500f05d6044d6ce6864563b552acf7e690c9715a214536596a86c6a79e2cf5d3c07b38a14b170879c8aadc9a4f401199c315b460825d76507bdbdd03bbc54a73407da52f14f1addf28fa0041f3971ca2297edbab9bf51ccab3690cc7c4e39dc9e0ca31a3a3cf0a27cd4c67d25ca98cb723aba44b87f7c06387e8abad839ca0f6502fa804abd8c63fb8c1af311356725994c3602838e1e8c2beb25aaa93e9e8efb06c1a8ba163144fb902631b9bb120186a6b6a89daa10defd5a8ac19ff576eb9207b75659118e0079ea863865d819de6f27b1a0fc5e80e73529e271d035afe075050b23dd08d8d1e88e9b5c78fe43379caa5caba28c78056e54701bfc821c443f0c720bcf75b96d3615826e27738ff2f257f3cb7a35728db2cbf46a47ac9f1aef04e74272000488b156a7c75c6c0c9ea2a82fa67b5e95c337b1a24fd44f3131010e1b2da83bca9e5d096b95ab13efaf6acded305cda9375c34b3f5d4d2e4211720f895bcadc65d61e6c3c23ffc69611f7e1c9131c0f8e7d6b08f43ec6d916b8cc0d787539970879f78b29b8ccb6805eb98b5647c4a0453440426e323a22b2972e0ee6af952e1b8a1ff1a4a600356b00c111aae3adfd6c70b8b70920992229d606021bae922800aa70f66d83f85fac50989b3363e11b9f495a636c91b32785b0aa0f815a59982a202dbe4af67e2d4115b69eb369c372647c42515a77097e0e40370493b2ab0d411a32904b290a10e73391539ca087a9224c45f3ecc29fd60ce9f308cab066aec636f589d703fd33d4f349852fde0792c7f4f64a24fb00226cd8700898393c6b854ebbe3f10e57c5d31ef63b50425111b93464b3ff486f6128c53707202bcfe6589acbf8e20fd94efac84a367638d250b1c162768f3dc46d0d3ef000ab1bcc51c8ac67908b87360eaf225cca287d33ddaa3874d1213059e365f9c606d03ab12f9cb9baab927b1b0142c44eb7032eef41549b94c8ede9912cf8bbe605fcc38dba7f7ad0af9bc973a9997bca274841b504abed0fa8874cc001a2b8d60fd57ab4a82370e946081a0986220ea41247f4e84b2b475bfe81e9c646d578d4074f1ef3225b1ffe8c92081aee34e893d7331e4cd9d93fa781b97a571e974cd6075ad7e68cbdfd6293fe5810393530b167b94c075585071e58036701225c65af0d0ae8b2f1e37a7334e56a9a09675e469361c6356a4448f92557a493a1d736200046b33e1fbe8ad65552c5cde09ed19bc92c9c971d0e87920dbb4ae8d50bace00064627b9bac38185504e2123870258ca267deb4b7623b33f50e6db06d5fbb11064db06c39e3d6aba5b40e9111c5dc06c5837a54978075c2105fa7d1e150c26400b34ed7679546917f783d5c1f5201c5e2530a04bfb8eebf15118da57be405e10e643cff1ae0081e38efd02f4c7f4a35c6f9032dd42727213e4b6ca8b70dd0e805dd934dfb1682be3311f69be9a93058b2c7f033aa22a908ea8c624df6b715970d7bbfa4cd8234994f53b5e1a71daeb963960da27a5bc682ba26ed69aa3f78dc0b613a141873ea65ec9e2978f648222fdf6638e0072ea58bc0299f6106967fd707f68f4bc279abc3812f4b554806cf6b06beb113f31391e666d4ecfaafe126df07d08884bac9c8a04ae365bf7576cc5a4838d5b81faaecf39c8c2316442d24f00a0e6ae241e0db2eb102cc75a741d2752ac929c856b6d48dae4f2b72c569f1790731e6c162267e8dcb42901cc421ce77a5991e11d0ab142fe40b4f978345fe1406591e67e0a1df214583f280c8799db5eaf04acb2e338cc7ca1d2d0102d426fe0ef0484ff17011a21f6ae86077f94977d5a26a318095f985cdd35dbdd4ece87c052fa782190bcf908e637b954799c67cc04aff903ca0904533f3999e69f2456307249677fb7e79ebe3b60b0d96d7005602204000c851fbaa5d3f3248d9171ff00328eb7b3f9268f4c37128b3563197bed1f6cd652e1c4a7c32c01f8115dac6ef0a3e60764bafbf9b065b12a4d7a43c5afd9e091a5e2ee8152a410594f1e43ee5084f998be30cb2bf6b046fb3745a3bad9bbef1fe61e8552909ac08c74941136903c637c9e4bd76998c83e06f609137508920be4915ce75796f1273a6883a23020099f25c661d38edb524fd161288660a41c1df2499d03c4ee2ad9eaf40e0acff0702ad04836b99a777e703823dc0570810ff16faa594449299295597384abf8b0223eb9a1358a1dca6d29909ec63edb55b059a5564d824aa77a6cd4a0ae9751a00c9b739f01d44eeb58012fb77d40f0992a418479b9f48be8856b5892028233d0f2e51c4a2fcef173bb75211f8a9b668921d12d5236dfbd15498a1e547cf65f10eb3e100ecaf001f2de4f13a9af79c31a31f14124dfe9268e8dcae431c8a7da909785530424b499323d9a496a0d3fba1d17c5165525cfcc88cda5de88965ad250445725b9ecf7dab6e2fd9e10641acca73052c25638915516075d322f41e95e4055e70668e3a68c93ee966fbb031a9e0e65c9168040ed0fe7359803186c3a9f805d3412190c895a7a88875b2592d5776f64ab84179d74557d26ceddc60e7f7ef083d7b4cf1bc482da47c52c610adeff89b7760a9b728208c199693b6b39f097f00451e999b5d35871e62c3ad2ad2f798bcc1721e45ef03084f83ed828791086601f14bf0a0c9d844d9733a1e4e92710f44a66fbc559baafae53c58a8b179e43b0f8b2f28a82923774ad97d82862c4413e5aad1fbdfa93b380a2bc5af8511b58f0940a4ef369a987726484971cb7fc3434ca0dc25c678e4d2950d47455fc56df105f82a808e9b5e90b60aa0d2a355f40a42cf83b281c824087822313afed51cbd003980caf3cf618452fa9c49258b2f1d7afcd7d0dd4a518213a587bf181276a90f542c4543774f232209ab7c7bd0cc9c407bc47a756c13a92e714bcffd3cc22b0f622bbe5c37b5249f25598cb18fca037d50fe85ef6492b432ccf60e35631a2d0a8271e4236e6c471fa87872a003673afe5715b747e2e69aea5257338e13a7020771af89ff6baab8cd748b4bb267be0ac4552574e374a16dbadce572204913610757b1d0a1303e5fc40f44411d2d633f6dc1da2519a22965f2d55b9e9695749f00eef3c60188152b0a316d16039e4cf9267bbcee67d635db6b22520586ed45c60eb1deb75d96dd2721baaad02f79f0e3c91b8467295ab05fd9a60844a7a11cb30290937e048dbbb6a1cb8817ad564c048a08258a447ff576456fc24d87be70e90c0a4e0f0eb1c4d1b6bc570c5bc065397d3f9eca179eae9f05340ce56c4b28e807afed8260c2f9d79ba434619c8371ce6d3f0147eef3c01f8ad955f5392bca6506a7169e4bced56c19100a7ef12f7590a10be8791c0d54191cbfd35b5ded53f3033a104a5160743df374123f3bddbb7a0be3d51fdf27085d54ae7c0eb9b92b6c0b4bfeb7e5303d6b4e043258823eebc6a25fd94171dcf107a0aa460195ee383205b3215817ec237035bc0320859c9f0069f6cec0685e85eede74e85545c65bcf019be1f71b54c307fdb028451b5d3d3575c6a1490751b5676c57402fc5e8e1410ec1866dc3788f019b1ba817f5c7508bb315603661f46dcb5540cb87ac8fa847080ee89776bb05b677744c77f7a5b01d3705adceb7a82de083070642768da1fa0d5c20dae9ef94734e3230558d83ff3dabc50a4b7198f001780aae9f277c697f053ad62f7fabe9227939c1bdf1099a272a5ea81925f4d3899fd93eabaa710a4701d449bfa0a53586738a12b9dab5723cc1eb5cc6bbea81059b7896bcfa8aa4a60549a8713fede02caeacf55ecd0e00821f08695307e832040943a186ce9c3bd20f0c0d5b6ea88e5d80b5198cf1271cdcf207c22f9c2bcb23385dc2ab39e3a0290e38d7f5009d6bf6e8fc1ec128292598c98812cd2ada1fdf20fa53f9ef35448e0176da860e508a3309375026fab33af540fb02b1ef4a37345fa88ddf6537ca240a6089c9d9968458d0a90fd96fe74676a5d97670057f20a8472488e4f1da41c204f58573aa9ae7e15106a57ccf35cf9d8512feb264b6f005fc43e5b5f0b4e8b80ad77ef84a50cf19edbd24fc2a0947ac8939e15975c31781d5e044ed366b5acd01bcc0957da036196237d16e2e837467fc49cdeaf46abeb895d6cb1fff01459d050cb097ec655eda5992f057018652bf6967048b06848b4f725515e10bc724bd0d7686aee18742ac0c491efd46e60af2951f851d33f70d76b8227ee622459d830a7c94590910845dd540839072e7e87073f24ce8173c08bcf2931bbcaa18cc280e9e44d8c705365568a885c436375a00f09427c8c8d66cc438891fdc98f5400a0e455dc0b5cb4c19082b781cba1370dace9d191b199fc7fe53ea2fd7c90919fa09a94ff54d65f347b997cd492ee52da1e9bb1dee9fe6bb56863b878149a061540663bd199d659506a8c9a51ff736f5841ea77862be889868e3eef487de850868073ab9104a571aec917a57ad0213a332d4b51214d1d98c94ff128e7b4f8c047d023ba55dd84c723a6e16c040213ea68bbb2b6f7bdd57734699e2cbda2c44c4e905028f03d17d60e0d23d5c0950d19aebc661795020283e9c77ccb3019c1595300d6dda8c60a10d8f7e8d08f6a3db883d18375076a89a2f06befdd551767c0aa90e08f10c1a6febd6250dbe6a5dd1f44700dd5743ad0db49dffd5213a8c23df6b04c446e598a3b21d8aa5fc6353b9b11ca908025e84ff6c22764a2c75dd50d290083661efb98dad66a9dca9c7b8a1fcf7f6531aefbf4b82e75de8e1c34b783961065abf93813e2c63173714c4ace7ae0650c6f74af19004e9fc8bdf50bd902dcc0f6c6c6ace99e26ead030a32cbccbae6559ba7b674583a03beaa3fda8ee48d7f0f678ca1ec5fc86bf26409aa518164497123a522f95a13fc63c31e2bb72fbba00e1c835c1e086644bd9c6460037e7ed2278f3596bd6480f2f5e04c5e92d53bac07218d837f81256bb00733c809f808de6995c7d0b3a07420b2f223b05ca0fd6d041028584c14a4ab89ea6f5154ff49bce6774cbd576b4ff8bbc6105be41eac9d01653a43193a96f190980c6abf463411f5b15e0cc3f11d37101093de48033c5e08ea4dad1d4bc3966909d2a8160e8f6d1bd9a0a4f50a0f65581afb2f81ac0e5e005fe200e481afd8d533b363654eb05bbbe3e0a5450dd175d8060a3c4cb0877b0bca0b729c01eb944cc6241f5edfcafa3e2841695a5cabbfd9c4795039d25ff90e6be15318d76c27df104700b2a545f40fdb3357293b50e559619b93a4b52b360cf6f92540782cabd9398ce6491cb1633b3038f0b070653f1e3d538eb9202dfe0bdda08f0b2d7de8fbe71f07f9ff86d6faed4ee96a9e1983cdca93e808bdea1e050d62c1420ba7f333a870e55d96d1e8491af6e1777e7cdf4efd2405435609e2036126991f10059571eed3192ec9a0db253c78dd76a65fd8f22cf2ed42189990017ff2b7d30955fc7f23a8bdca86de1fdb32e2e69a9e4859812c8ec11b3bdd8700c12b906bfed72618cda8949effc8850d327fd98393e9aca5fbecbdfaa55e5c0920030a9c84efd769dc0767fb6c7e5929466dde7f01d1a1770211e997c3e58e0e23e3d59c46bf331be82ce3f3ead487c1041f6bc05cf0183671dc0f3925d8c701fe5ac73d40d85008c28987670d1e4ebe6e3dfe72182100f48d98cc0be543a50f50363bb1a86f3c16e45921d17837855bad006310894e070f1e6568c095de2908c0f21aa23b8bdc3ae2183c67c1628c720ac59ad60017512694c3837785269c015f89a8d06ef8bbc60afc2b54ccf375c18af98a211c012393a9ada9e85b3e3c0243a53a38c1f858bc32180e8eecefebebfea65db32f8984b09b37d0c0305eac07f8728d7a872e909edfe50c8161333d4ffbdf075103e94ceacc155e02fb425309db576a12ea775f971603efc5b46b24542dc38b7e57dd69bf3be5f7e15ad5dc0e97fe6e04b65231ced0b027daaf8d2b64cff97cfe6129c99345e525391646bb0c35a5d81bdde418776dc1a5b89372ba352e14eec2bed7c1744cfb6b93ff1c6200a6b1d42983f058cda7b667d570167355461b977ac4091ca8ff0a06956c52f9023eb20175d72ce836898b44d1c89bfbb07319058376d5fc024a42fee95163a102e95546527754e732877c9dc12cb0df46e7f07ce30a41659e8bb126310b2f4606fe28bca49f52d0225a5b9bedd1a1ab10bb946cef0f664af4be4971411f071c0eb0c9a8da42fcabac4251c6ed71b3a65ff9ad6633e21a39cf1dc8f9663fcdd50c198575b6c03cb816c0ddb0c7b9303c8e2adbddcd0b31a4e7714376f3dd761d0eedccf40ec4310b300e80b8284e41d9cc47f4bc7d4b16ca8e1e6057a7dfd933015b89a7756b759d632bdca0887862002a3d760e6fcf4a9e7961fa8a1c8c31ab0f3a81e616bb4b3018c84f17808b23f53ce0338b861c2b1e3d60319410e8e7b100947431aec42fb2f2401d5e63f76ee30711ffc0171349ef65ca184336bac8240b15e639f653fcf388c9b0139123ef34eafb1372890ed3a27fbda25a8635f7f705f7f1d0f63336c5170898967f23b00deb0e1e0897d47aece5df502d1a6bb9630d448fdce20fb1d37f182e33ea9578e4be4709be42feb83c9fedc5d52296ae71041fe1996b9585cbfb585d52fa4cd67639272e8167b6525df84b069b06ea16630c0b1b0191ca56af6e1c166983cfc1c23f32a9105782fef7a25f954bbe92048008de03f47ea2af45a15ee57a69e730dddd2fda01a98404ffd3bd2d40c374be9806113bb7e01eedb6d29e54674f0bbf3fd967b8aadfe11f63f0b7e28105f3f02b09a54b9583116769acd19a1cf32a1e09b3fb64e0f77c69dc3fc1a4bcf18f69fa075ea007086906b1c3f3de41c53732dcecf0ddbc538ae4fc3326afd2a85e0c460fa481d199c54d4cd04e5f90b9a3fb647463d7a44978efe45afe3c83e415d11600b7f2c6a8464e44997a00d8206dc22251f6a7f1743d56b95761d23fb4c576530650f68e1abe380444c876e61f5ec0bd37377910662dda56daccb18a0c2af86e0c4d2903c7f49a88bc69ab0088d9b46135e7f8e207d4cafaf29285799e1af515092291a9d70acb04a67ba863f7ab947a5b0af02d88db3cc912b8fd3f11bcc2b509b31deb0e781b52787a1785f275df88e8af08fb5c8af368d0a0ca09dc64d6e30b43cd9dd145f64d13d1ebf1f1e795050e4869502547610eda97a8754609b5e50f644691cbadfdf4cfb9a13c03fa095b35dc7d6cd046fb22742bba1df1a0c5f50345cbd6d119ff28df67e8f386a437e041b3047bc8ad44ec0d698e22e180bd790d9a16e22378f64de38d6361f700607cb61ddc46de7c7bac60dc98ea78ef80c1070f123832de0a85c0c2c126ef6ce96fd7c1c00a91ada64547cd3506c45f40740e279e13fe6b5cce77597d36e856e013b6e8c79a5ebdeed2272964f976ead47e0ca8e426953a70b597acab7001c8b7bb445cc0a9778f786f4d6ec99baa6e8c2e075ff8bea65c1f04e6943fafca4eae7a12fffeb50ea4378c511c43ab891fe986007179a03ea1519fb1d543c9bca31da91a34af74339f35e6bef772a216a32eaa015ff7b166122ac03dea06798f288bfc81de9618c9f1ea7d1d5c998e0cc2f1a00732a01310df43c1d3ee3b9c7831285f7fc3d4e1044ca5e4de0b122ded6bea8a08726739893985b8bd267552f5c9db9767296a0858aa66649dbd1d657ce5c87109bf3e3d7ee2568a2685eb20424f2c7dfd921a83d65516cb66c520ab2ed724c5078bf25b59a1a4fe2d5aaf7bc419d404f7c570dffe7080569497c2029da5a3080c8bbb14ab1754f9a18c3df830617fc80b1c0706a6da4176094ac44b4208f640070c58ae9534e6d2ec04fbafce7144dca3d15eea5f934d173a91b63b4db8fe480f54058abc811272a493381ae3710886ea8f7dab55afbaec2f02f58782726c9d02d89670d18b3d2ff40952e23ff092d29c2b2c6c328cc7da4e07774cac215f9807ba95e548a7db2a166927ff27df314e17a0dff1bfb5a3b01ae12c257b53ba080365f3cfec32cceaaece807a00e4203a498480bb2629cbf320e8e196b1322b9c059420e7dde9a08d0bb9e408b5dc414823812c91aee37f52a98f5770856f3a82048c9df1cd3212bb5fa11bf271e1af780de70d9b0b50553f9a60665b4295b5170be7701e2ad7e18843be51b1f58dbdc1bb5c5e713ca552edbc3dd01c06f3f65d0e8f6f3a12e231841640544e549085056ac0f38c573ed7b6339a3cc7e011e6ac0507ed757734485ab67516169ff8a374b6c43e3c28ec8c35f64b9ccd2656697a0ecdb38faf23e93ad4a1d3300d4ff76701508f2535dc7a31e754eacc3d19329003fc0bf6d2c2d3840f1ef60680888e959d6cf89119947e00768187a36a1d915204ea7c2eddfbf834655b4dbdcf32c5ad6e0fe7f9b8cdfc8c7ab0761ac01cb04703eb9120a9eb4587a5e2ffdffa020a3be26d95346d41f5d9f06fce21a129d4c3083a88d805df24dfecda869731c43ecc42a926e30b428069005acbe98edce5800f0cffc43118cf15aef1cc4fed9e64fb8e96ef691346135418f13afcf1175768088b8f5faff18f1c783570d9e9975774d6f4cce1e64cb564409ab78f6eb1adfd004910410c938628ec122bc57a7fe75e22a3f1303758b6445c46a16e34f697e305b6a740a7a9ff5eca01cb4eb0aab7931f2f284839ef41d4baebfbe67ae8954902421488f668999d88c9f2e60ec7809daef56e0d28c0420cd60de40c88ad03170f58205f134b712b3859fdf625becc8d0508b950bd64a25313ae366856d0bbd10676350c916b870fc16bfe6404f40a0daad04c481e9ce05e7256fa2b52a0f93b0dce323bdfa62b9fa6da547633fadd301bab988ceb39f5dbe955ce4ec88b9b30028a3a347fbcc213bc80c3ed11e467ea5dc4332e6aec9490be0b2d119d9a49ca0555dd54e2611712cbe59d31f6204fa122706535ed7eed1a59700e946399d012051d0abe7311683ad8bc9d0a3ff5d1e0b419f022cfbd3de68690ad289e99a7530b7fdaee29025d95870a08ef5c97ee05d94fbfa0575b5c2b9f860265239146e201b9efdd8a52d5cdb8cbaced4cb48983428a66a08a7262e1a8142114eec7a6b6054ded8e5254eec984eb1919fab45f4bba95858991c57ee36125faf2ee8c675a0c470ff51ea5e024e4f967fd7bf964140fd4e19bfd3499a3a01fae6d1b3d50ff0e3e69f18ee2ac5f478bbd7cde0f173bafcd3b2bf86d82eb204d5717b62dc68ae372314f10538991cd2769a4d609631cbad8f76326a768f0654c3fac6212bab70d38fd82ea1571be944c556087dcd58116b4415b2b54897079883f45e63ee4fc0772fe5b43f2b0cab8a5dbd044d388a41a0ef882074749f80d78fb9ac5d2905e09dd2c3018cc4f77f4e1ea216ccea2de8e38e4deda1e24e1b3a2b163031cb42e05d1c57f9ece91e43f347724f7c217ff74a5ab717d6ba1717768dc1d301a61f8090eee128d85c63d88189ed51ab8328976ba9fe922ad52e77226835fb5a23337066b9f4cd42977763fbd53efaabaeea30a3ce34968af9265d91dd8a6a1d3c8bc06f3d80cf1bbdf57541b9ac717a0fc17fc8d4cd744bba70dc7c7c9013292033208e0f1058dd07488acbad6fb7e0e8ed1786135cad28c2e8ccb745dd67ebdfb4b03cd21612f65b9243210d9244cfa1ba3b347f1fcba1c14604a03ac055f1923ac01f58ef99a821b74029c4aba4066aea6955d5df92c371403d8eb4085d9b733ed01a10f3f440576cccb85aba19e30db98f981f32cc85316f30d7c8df0c9315e060dc58a967791bd3df977d545433e7a3b846738ae66b31064d04c4d0fabe31d3e0a311e8dedb6cd82bd03ec4c1e3127697afe5420955ecd4157248436617c487b01aae50992bc9214c0291587140ba395ed7ab407f2dad61201b796f7f2f819210cbb488424f4a7dbfb736bdd8ae8cac55d6dde5b4e4575994e026803f49dadb00edab246cbb744467aa620475d3be2a1226cf9c0e7447b41d6e9cfe673053a1f0878449a4639de449061c5c0341877720e2e7d22f32aeb2934d3bc71e2c7df8a0bda66afa1c760a147868bc7e3a76451120e02bde56a4c4056bfc31513b3dd6a0371fba95981f54a7a92a83a257db72242d3d3ab2d928a3024d1455f75f35bc109047dbd0ed246d064840dbc9a88f2fc8c26cd3e178169fa5befcbd701fcd1c9089fb8c6724243211563bc9418e610fe424b426455daf9041ace6b871206ee3e0f7dbac586c385dfeed129fc96f1a1cbf0f29cdec1fb94a1613664d1e69bff3a0b276e8beacb61bbe9bf3ab459321a4f064024db2b17278a2b618770ac2953620aedfaf450658d7bbbfcc588f28092f7b5cda3632310143aba682687403fe51b0dde1b1119ccc12dd09e6f8328a45397efd52716c8c252e8b1a93e3fbc0017e60e518a957f9f6e952db37d81a5932b127cdc354c3ffca4dd1f03a6dcbb873b2b08888bc47b14c17f639690c127a9b1f3bd0ee63fbbc01db754713127fcc3f7cd0c3d9e0973a4027e5ff9cee81a9b491d9e25975e6d3b57a9ab07b9b30a8e1aa9093a499f8fd8b7509db6abdb22fca49c4cd4e1ecf22002b1af2f9fa803c1e92600abe8aed72f94be8186fab635491aeda730bb232ffd44b1beb0151a1630e6fd0fa071ffc433ca709a9a73c327d8fcffa35b66f19d63ea87698224200d7fd3b90a2644740d1c6b9d7e935cb4a9410fac18a54ea60867199e7438271d2b2e96210148ba3c2b18dcb8c8916348ba69083f625290951d72fab5d0c87ff6378480510fdfa5e65bd7f889a0c37da0d756cba9b87ede9b28423dd49995a265a68f2d990b7069d3349e13f2911dc25c65950217e52d175a1f146ba8c19ead1f8bf0f4990724eb49ecae0d2b58a50bdc997cbb4f2513eccf95bdb041a31000dfbf0185c30c14b1c6045f81b0f260d7a49b2e380d760544df6a06a0ec6b2bcc64f01884e308e0160852f3af0a9354f545f2da159575816aca0636b3324c3ff119e84900b101789b19082041ecfa043045dde3a6a9cbaf86b1cbb5019349b3d0b6a5ea11ec0f753bebfb825545443defe03452e2127471ff96a272d53cb5425a9dc71f82750f80af9485a029053c2fe578a17322dee59a4b7d22525b06ea1a769b8a1a9880003451c9a910c22fb862c216c676ae897692cc5f410b84f4400134afe06fb1d102fc98fdcb9ba0ac8ca3b38701d74ac2667a872a933569cde47bb17eb70ff2330c53681969bbc96901c464b36d731cbde2d8d284e1443fa55e07b4ca373ca0270a21a1118890643da7f8552048d1ab6eb9ada22e745e9f4a7d03d4bb03e714130c7e56457bd2407601a0868cee80572ff32c8e86f8ae488433c4ab3ce939bc8704a2415a4736e5bd10af0af3e868cb102b49a71f0c9577b896bdc319a86bd570007368ca513f5e786475d48bfc6e178f42d032bbc098899596c641b3274d5d91005f5ae4e102fc6c6e1bbdeb5cfae7d089731a483898a7f2a4e97a3d9d7d0cb60c2e1a6e13aa00943964c2fc46cf417f0f22e680b329b76cfd1a8f405f31bf0902844cf92a1ea943c9b797c069d64d21e02b32dda64cf1a11f6e9ac9ff30187e0ebd876ab0d372c3477e65879f1c2815298e51edc6cf5e085234f3c0ad750d9b01628a241c0928e6f13515d0cd73bf85c6697ab66d1d73256f2b740b711d16d60ee1b813bde15e5d667009a3583e308db9b72eed1feeacaa9019173bfd26f264010f82d6f25444898c72d72996d2976b2999ceebec2a3bd523117c4e37cab7350be734bc72b813507a83edbdd1258a9a1328ca17d7a7b62cb218607902dd91b2063f8ec64b3cfd84cf2dced38b511f8599e7f91a9e3d7dd614daae0494042f2f0fea2a55567122abc62cce9511f9f029dfeb04b3d1aa22d11e795d5c50a2dae70760d7c5ee850c8c94f41203f831487aa43f44ab51b1b253b30666c5ee1401be0152318cc5225706e4258367e84563c48de4187f63abd8af7e9ee48a37e2f5ae0e3ee76c913b0bdcd2978c292761ae10403b2a9dcd2c4db693622d2e861be6d30de2a8ed82fe06f01d8e5dd4beb522180b52388fdc57d25dc0b0e8857f050f800b6194e2c904e02acfffd4bba07af5c998c79f3fd1766b72bc615d051a998b0b0d633135fb2ea2ee0355ee1364f33eeb652c9129da5d2779d09e6f39c70277ff019ec07982673bac021e9d5fc4ed8632802743a44462c3e18c77157be21d16c00ac31e5fd5a808e8ba805d5dfb01c7d1e0ae399b6d54e02eba00520554dd275b0c5490a36b66fad92258a46b6d4cdc3571b67b31b80441a34b15c43b63d4446e07dfcbaf385b51cb0a82f9203985b77dc45b32e59d28e3a094f65ef80ee0ff6403ee94beb1dd3d29f191470453efcd8a21579c7a02c73c8bab54bd867f4f68fd031da8fbaaa1d23219080327139b0fe3b47c9f79d1cf142bbf2eb85a923ea77a013ff8d4772d34d8a68df3752dd9d0e9b5b34d953d73ba53ef589eb4f33348e905ef799b7877bf4d452f12a3039b9db72fb512877b8c927237df90c811561ea2064782b852d2dec06ead30f9557b9ccd0e774666019f2d3090aac9ffbb4d698104ffd2affa0a817f944a62b2c10c175bb37042ee7ac113b549c64f133c7be4f6055fa67b6263cc957948c0d6a4d67ae5ee29d59288b87e28ccc23e639a29018802 false -check_ring_signature 4b788281242e705104a6748fe7ea71957bc5f97374900c29d73f36abad7cdbcb 14cbcd39a8f6b7bedac51adfa5d6fb82f08d639cd7f9ec07407b237a7cab1914 178 7de9c6c1667245c7395752cecdd4ccb6b1530d839b70c3eaf1c98b6bf7823c34 8ce0d6e1d49757c6870e102234e298f9a6f230abc52b08a01fad62a11360fb11 367a96186d2b23625e0891984b69a789828f3a6463ebaea70fde4612c232bdf2 e6f459267bcbeb3229e025cfcaf0a6aba1308804f363b1b8615fd1fe569fbad1 78db28d707b6bd57823f0308f8563d504300d2561e00449fdf0763c83203da35 d10f6ac1ed0af50f5be172e06cfaf47f81cbd74393e44cb039f33cc3d92f660a 0b46a24fd76b9588f398d1ed2e48349dd627d503975a37f75a1f051bea260f8a 20457a54799787e3596a62857e7d545ab9fcd8cc8576197b6eb7672d578a5a6d 6a1e939b41ab4615cff7f17a8f4844d805e94955d8b85ea5df487be07d61babe 452d825b5b7bd81bd7307a5db41c8ed76021d64fe874031b39df25a57a9fd357 f95bcf52bf0df7393bccf7291d569b31f2ecbdb98c41039175883c7e6c3e92b2 d8aee6a97d864783ea772d01b79e3209f33113c08fcc044768c21b37fde6e260 1d6182986727d7b365545fcb2262b52c9b72b30405de9109faf1f6a3934d289f d61a017aa45d415f06cb4c4501d4c59e1cc5c23d6d470d678b472d632d089773 861dfba61aa085b018c506d9495ed56ab4d87f5d0e5ee56e0a0049dc976a7d5c 2d3a734afa7f2a1271f03c5615050c1bac5661c91cd7f05ef0e3af76420b6d3c d1a14afcb4ae3a9e4a07805c32486564606bf1db446ead862cdfb0746ae48690 f60e166a993bd69e6532ce7008d4d04e01d49282e81183aae285b68a111ff41b 0450304f936d725241b309e53e960c86f0a4e95ebdbda216bbd4a4d9ee15736f 8faa9e9808360dbadcac8b5a220d46481b75bb034771e460903fc786f297447a e9be5d432774cf00af58d8075cb5c23c28335d836f0d13267867b2813358f89d 421889be430eeedc8a1c1c0b2bb82b00e94128907a89789fa17b379007640c10 4e979321589eabde05ada04f08087f748c793fba73123588df42bf2c3cc43aa3 4a885ac186a0247077cf64568d5b0964cafd04b90e6818adecb047c86748535d ea0546b622246083935685063863d9edb48df4e2a2203832400f542c70e52515 9507c22f22948abf6e5707737561b876394e319807f3a810e3973f619b123d98 e745bd13b0c93a19993e092f064b7bd7af3dbc357631f3b639106784eb484079 959a16a2bf5b819e594cf3fcfbd54f5fac21d6454f2335ce52094082e091eddd baf979412ae03844a35de819b751175eb8023bc10fd03ec765cd28e4f03c1ccf 1114033a03732ed46ca09d914129ec0a9555e62a17cffcb286de45dd4e1a38a7 ac844c746c85bc63eda08a0a0d72b9ea874856f2612fc67369508a928a2ba3b6 ddd67fe0770c35818ee7ec4b4cef91c1082239cc4dd39e67587cf40bc4cc76be 5879d1d2a0a9c89e28ebf2cb788f181a938883cf2e63ee94fa7935671d2a6a47 610af4ff773ca1134a37d185850388e604b2c62562524399c4f8dfb892f08c7a 9443fa52babd6a30910d92e9400c006bd9117a42fcb3f4809fb7b4199d6a12b5 463a06ac908abe86c6c9c8ea3b4d06c8c037582f0764999de208d3285faa7d7e 7c3b073ce3b1befa010493d290b442a98b53df376137a48af5ac3136a3321ea2 775195dbe23923cafb2a93fa439404b882281565a2720c56aa920f6ae1bb030c 2d8141ca49085b028d9604406514ae7a5ff6e408e1a5cf01108578ddd95e1734 0535fd8427b6b804b46c77e5dc9c222e21f3f3f24276d06d9949f8dcb5a62a63 655b7914a0c989d2f9514b47e1a2221c109eeb4a083de0428d61bdcbf0b0f19f 29f0b209293987d750ce13f8d296ed519f2f9c4153a7de7b2191e5ecd1154e7b 82c7ffd05ba215e5c523135128d0f16b624068f19ce0ebb51af255b017eb4805 ae0c0a60a26050379f93bcec93927d0d393fdc89119660e28ff771856f58fa31 79fed1e61995b28ae10acb99f6dbcfeecd446c28fbddbcc0b465762299e96cd6 6bd62cebabb577936eced881b7f649c39969139e51eb63ac48f36339d4e16942 196c16783e71ed38c0c2307b5406bdde16c43b2320186fe2ac31c83ff8e89a07 cb65a6c6b4d8ac90912925fd6596f9c2c259b1df2528debb508ebe7933a0523a 2e3bfb8d0610ecddc157552cc0ec954d4f975cba238a16a90d5fbc86e58c4379 428f4758a3fd4b7b2acd3f5d6d949396addea6055a7ad6b4fb7c1bf9e92fc5d2 e44ec1afb510ea66a74a8d29fe0a834f1668acafa329777d3098ea995424803d 9280a0c9acbcc2c69d52f4ed26388e3ed7479c9cc7297b673bfea141f418d9b8 b6e0d89e3d15cd766b093acb8f93e2af91857d5972c84d6d3225cec5f85a90b2 00f42547d8e35ccba13f1d7c5f588f83466d24a0da937bf0afc5defe16205195 b9adfc3c9cb6d6e072ef967a559a1287f0f658f619215c7d327d4ba00c873fae 810d33584879283726f5adb12e592a05fd760f5ec07e15b315032cb516f68b25 ef0871082fcb8190847d798f0b13a17a596dd64ab41cdc5d5c7aca5dd43cf716 e15ddd735920faeccf7f4d0a0ee349def9102feee54b6ab95835757b0c672908 eb2e3245fc0165949bb02fe044c05d13a7511cd2e187dcb01ba1695064ffb86d 71e0e081b03b7386a56776cf76453f7a10bb9513f2354b1197cff381c7c3cdab 079bfceaafc339d193cef7af26abf1fb95b20cb86406f4d1122979b0f076a405 4b9acfa91c0788b5301cbc8bf13509635e1481fd57baacf4906aa5ce08f9f18d 80910cd911f730ac23d60d64c9afd5e2c5f1791d0e4919127dd1edd8a78fe502 73175e33c9a0cbdfb758d94dcf099697b0dfc204c4279e115e902c733c883791 721ba71ab5f72c777fecde9a002474e75e2fc18b37df770e2b9dc526d42df8be 3102748284282190d76105ac4486b21553317f9e727a2e42850f841362231422 238afc411d443fc12fbc1a803afe441f47502c9bbe154835e60bedaa9906c30f 5bec0fe2221175959c451137b0e937f9690e3f334929d98e0a02172ed124b9e2 d5fef926d94538ad84ef34f569140720418fee78f6c3f6ebe2cc42267f7f905d 5905e7fdfb3c00f443aa2ee70892468dd885ecf8d38c2eb8be7e31ac9d0b4f5e 29c74172a51c204dc99e6f37382317ec42dc335755a8d3b19f55644619d6b438 185893108f6e4a36fc5f13729541305cabc5b9a46b8ea5daffd59b10d1a01a43 c47b607da9ab115fba98ecfa2cb9b7f72a06bbffe198b350ac4f855d39329279 1da84da25da64e98f99b6c001afc609550cba65d58ba734b010713dcd08339bf ee18cfd2dc7d1a5e44d46bc96651fdeed16f50fc3425160042e77dfe16c3f423 a331823f61d531e0446fc36cef0e339839ea359464b06153e174cc934551d0e4 e79763e09083e9448e9abb96dd185f4266488f112ce28136f53d1440561b73a2 7586b83ffb90d9815e55f387cb7d4183404981c42a4cec108022f20b9c448705 c784ec66c93f37effe4aa8465ecf96a75ea510ae1e5571383fef836533588d4f 53ba8b1c33b2fc9ef2f8f9af4a7e908356dca3ece73e32cc480b08c31e43f3cc 57d767808cadefaf58fd27d1a156958e9c0ed4d3590f6def559c40477ae467b8 9ac2f9b972a8320eecbb8b99a8d94c07b14f87faf30e7fe87e7e7aa1e6844726 997d7dcc7591f53e3dc8bdca2022bc381a18c9d36cb7ddf4cccb2cfb4c22d99e 8a28c7e01bc27e073ef836d410019c2bfcdecd6c059281716ad2953acd3a2c4e a5bcf5683d79ed1ad5cd1c95585fad66575a00704d6444b8b242842689313052 de4e63e98fa5ce69a018598a0ba97e01754c1b8a56a7197f7a10b3f0610fe8d9 53d1ba96d0e513a364c5d75ff10be45fe0c6af76ac64142614bc0145d2742a30 b0c6cf40bb36b9faf8e6b07610f8a3f1aebb09d2160921822e42ea9ee2e7c526 c3a47543ba8695e9d439d88c0f1966cdbd5692d86baa1549aa4442f2b998dfbb 0013601ed05bc7f7abfc88e73b3dea29a02ff9506f884c700124255aaad9b761 82c77b03c8914e3c7aa8db489684c7b3cbe3c34312dd50d7f80394bf1199970a 1659e59c7217662a7ae40e1cd7003cf3627415e05af27c18c48446021d16d1c2 c6f031896a5e5fc6006a8abdd435c2ec28f1e7d3d6f00e90c959a5eb5e337b5f dded1ad9b5cad09707226df8cf2d37d862262aa8e6e3ebf09e397cfc8d82b6d5 6e9968f44c0ddc16de8cffee1f679c3f40d8f7acf4dddac68012d5b62199761e 44fa775f82f9a8ebee4331b13adf808a46da184a033cea8c630604524d8b7dec 7924e3c82d75020342cbf6aa6684058d0ceddc6c45b030ca0a3023ce5b7bb45b 7ba05f1c1152c1639a8478dc211e60ce541327cbc373aafd5281335d42879023 3a3397f5d7f2ce51921df3999b3d6e4f7ce6f668a0cac6a988855bbed4085ab1 8c4b693e583275a3b5b88a9217a8d593fd559c104da3824f412ab177edf8b160 b185d9e9a1f0d9ced94161a5d60996e7b8c132ed21ac362b6453a860326e0f6d 574d6603929988c44dcd20e0559e1ac405c100fa63088babfb5b1c818ae78cb2 232b082d27cd2079232e9ae12be1d5d14f0156a6273d20b6c192f9aedb6f4c53 381a0c202bfd75aa9e292401a882f42ceb42c34de412a3560fdb8685baa0afa6 e096e3e9f4045478f09d54be9f4548882c3a91756796111dc5da7da7ce113b76 60facc1697067e7f4327830ca1329e5ef10cf02bfc5da0c8eacee61a682c9d36 1dece56b0597b924b36bfef955b94e7db62bb543f54283adb57156a2adfc677f 103e70a56347fde05614f1c7fff91d95b5fb42614f60c259f1a06f78253b26cf a62f539b99a79a89d3334fcb878733d78acd0a362f68822cf09b981ad363dc4d 47a778c04e01ed8d01354619a460146a8153cc46fb5a9a591ce9d266e4d5a270 98eb8938d43d2f4dd33b5e1cf9ef71c4242c2e745270cb6163ae7c14fd78f273 f7ca24f3a3848914166fbdeea4a58f33baa6cd2c2da7b217ba159facb65f451d dd2a767b6025c5b347da6934f806bf7428424f160bc89b0acf85e479230d9f69 39ac8ccb2ac891fc6eae2afd32d6e2623783b74dee727c86d870a5b8bc4d9b33 b4f382cc9bc26d7e96e73f92c0b3938c94e241ddcd8233a5c656144204f5cfda 028e31ddf31b0651133151d21388ba73f33bcaa4a26906d28a594259b3227d17 31df05a6d8da18fc570c612e396c5fed17ca5d5a30ac5126ab1d33b0089de534 d4db1d7c044bdfcff2e35dfbb2cac50345e09834fc4232dc94d68e30d7d3a60f 84ed79802604dc174d9c955dbaf33512bed9239c42ebac40f14b02fddfad8165 bb08e431d37089fce26dedf9e3f0d9ed1715b04a626ee40cdb4b88a86c1aced4 b9d7cb3593c9cd7cc996824bab772b6501b0e05edba0165ecae0f26bbcb8802f b435ce580a2c5fcbbe83f26cc64c785a9388ecdaf89de763c265f99ce658cefd c3b9002d6991cc556f301a29cff6e3e77e1191aab6515a7d85a13628e7d0cd83 f5deefd85fa7af59366e31744940f681c58966f4cc81270cb74901d063c35b58 b7746eb4a8c981b344e105cc5fd6e69488da55417f3823092ddb5d2bf679a255 a93125f9790e01bbbd5a5a7fd8a31d0cc7c199e451d6eee103b21dd57ae1f09e f1b531ea41194e10ebaa6840292298e3ef0526abccda943d70cedfdc1143b32f 5054ec6b6c1cde4c305947a199f84a6e586cca660add0584978f90e55a1b88dc 06b842f5047177714c0e5418ceeefaf47bc53b5e88ac519ea3ebbb1f2728addc 305c85cd5c25dbb7164cabbbc27a5249adf9ba73b3e78b7bdef99e4e2bd3cf53 10eb25364c66bcb681f443746b1a64f1a5dff74ca073f4f5a6957b7583096c36 5a7c405803f005fec2a635940ae2b8d0cc971c1c164c158305ae8fa509fe3b45 97d06281d6c933004a70ee06647e5dfbbf4c7276f36af0c7fae49a3426e5c85d 3c7fd3aa153d1ee76c849c0a8fbf32096b368a35723c3b1513d869aafa83a33e bd27ba9b225f359a77050a572bd4803b44bda741236540043fc664ce7cbfbb62 b24125a96c5d2b902d4e2630676cbf0315b301f5a16643941728e5c4b3a914ce f33fba5b1f16fa28ba7f5d2a9dd36bb66e4ab33e17be432383f7d52adc5b48d8 4b96e310d06622113d7eb37bd37be967b529f2ef3d3a924a683d2a46058b0d28 6dfe98fa6daf08346618b66aa72cc8b48dc2621c4b5acd0f5a1ef79813c26e19 b68caed00072053de5eed7c26c89fbc244af9e6ff320df41db010a7b0e03c131 b142aeb7c10acffa791d9562505411eb64b7f049769c8782266d478010e08184 d09c2ae929aa1c97e2a1bb650d985ccff49304f60be94a45ca9cdae5e390c9bf fbfb2ffe772b78786c0cf5bad2a64fe002d58902e289e64567e76133e8dc98d4 984be7e81d0dc73f51acab6d02d697a7b30f5841daec9c3b9b5dd1ec532086cf 5183463db5e5894bc208c1c2d04daedb167808bf2a95815d63ec46f451ca2327 7d33e1a2fd50b57b8984e7875ee101ad6690b5c1ef112a1d6a3773d32c97618b 74c61c65a3184ed9d23ac32720db4fed1d874d5017a8210b6c83abe3d559ec7c 9bb6a8b5407ac05de7637624f321bdaa83429bc142501e94495227c8d32035f6 153a262d1c6e3dd7e54db3b899df620a533bc7e67372b14043ef3c1d9c275376 a98411a5ca5ed83813b1452df19cdf8eb10f86e0b07e4e8ff45c35a23052191f fbe83ba81ccfc8a44f32010ed386415977889d34749cabd804773914f5896438 d5e050afc14acc43e6cdeec1cb0fa6498244df0c729708f25b13abba743f6417 77814fb0627a76247349180024ed811a6939b895e2b9938c2a4b8cd783cb5cd2 3176c738723e689b00f2cb11233ae0d55645102b03976f2ee0fb137bf7d41dfa 9d6bf6a9cfc5298ea45d0b4f07d6e835ecc3e320800ae9d695b0c5bfda373106 832e8612296901a149b02c3559d79186ba49855c5f5ac4249bd7bf3129991596 f6cb7abed318983f9c63ceaa46ec3b1314fcd05470e321e586eeb1737bbd4bb2 55e86771b4a428702a0490aa9d88420d30166554b762e3274d293b2ec842eb6b 50b6acb5a4c722cf0ac50b6f5adfca7f2daafd263afadbfaf20f3859d84c236e ca775ca8932ff9394c2c2570ca228cf68b0535f2f7e9b561d2e708840e170a75 e07ecd6977cf330faf544ee76b6c6f5bb5f7afe0bb08607c48b2fb867d54246e 52e1a3a011085aa9972c50c662c0c035267d4a8e7bdb816986d8c5d7b543afba 9dc20dfb25a0788b652ef9054a166fd4310141331d96fb1b947e28225d259712 ed7104d43fde372ded634192fec82f0c696e7f501ce6432b80b4719e4420bc08 f25d51351487ed8d30a32b77e95b357e5d940aedd7d6eadba10b74e9a17fec4f eb0e5546957d234ac6e1eafb62c8b46d9681ae8b471f593335757f324f504e04 38ddd8566266fcb029cc196cb615cbed2c6de3f9ce0fec0b889ac2300a494f02 8477009a87f5f93ce00407ddb653c48972cc60649c1da6813fa08035fd8c2dbc ad0bc8fad36c2445c22267277f930a5cf46b7fa1988f8351cddec19a51103255 23c48cd314eea0abc548d8d62c7a7b94905fc63726263a7344e9e13e813dd0c6 1511cadb88688aa556532cf0bba41b8b6d53d134f7168e8609157a02f84ff8ae bc1f8f6fd20d98ed2f8269478e94f59696a3664527f6bbc2a8f3345032cb3b7f 37528b1ba18f5bf3a575e0798b4f62547821ff670cbb254698f55fb326f1b11e f0298165e295dd58d413437bd439908ef7e1083decd896899c077b59fcddc472 247532fa482cb7333c43e09b101345ab6c3425c91c198e23dcb9a370102cb4a5 8e07f1e0e2722f18606095b0d72635df92698e233ba12636724ca1d1122d8448 af09554e1f2581d59b38b3ecb7e03dfd910e90276e655ee02ba81ffdf37b50d5 b730df23f6adb39608a9b41d6a76fb2a70b4ffea39ad1f4893d1a86367ea3b97 9405a79dbd1eaf6ab4e3e637ce28d7aa6efa21565684f7eec796f07d845bcd062ff2838134c8ab1ddbff7227d69d7d23c196dd43378e25f37d1aed06347c0e056f0283336944c67b7e1792777bd4309643419fb3fa43c959fafb990426fb0f088a486abc0e2605d9844d9880dd976ec14b4a7b4c88657bf1f24275e6cc0bcc089a6b69e4e668dccff7c7099b023cc1df4b2183957b7aa38875ba89c0133c530931f4e409df11fa733229dff294fd38ef6bc1205099d4a0ee9c65c45f444a000d8110b437ec5e80b9bb1799d0f1c671b2d4f01e86f8ff85ebb5d2a07748d43104c2e804b6ef4c6330b97b4ccadb795c5149ca4c679c9c1e11fc3030b62852580b470cac82179dd15c846166b8e61ba3fcd069b58053b246c9ea2fb0e53c0df50c856d85617a3863457bca79d6c669ec2f32fc70858f7cfae0425323d6127f7c0a91b2db2d9c82c1bebdf3da3f1a32a6830f2a45476a176fc9c2b04ada89ccb20411170ac73fdb587dde5b0cee0861747a7f107299c9d5598fd63d812069be7909607067dc3efaf711fbc42c8e8335e3dbdfbaeda0b1834308183f37ae4307c70b4408ae44995264f354aa03a2a387f1309310fb8791698aa82c1898d9eb4cdd09e150baa016e19dd8ccbc626ab192734fbd9d853d66646f360b90f37da011af04b789812d13f41845de4f0ecca68d5c5a49b8413b659779c53a215772dd8daf01e1ebcc50da2815770e18eb654b40f79d7c0c1cba8c413dd75ed5e26d737f8301fbd52d24e1f11fb126b7253ac85751bd840ec89970c3c8971f9ea96128f9b7058299395d2453bc7314e4116397ed78dc87db01ab99891b6572dab77c63cbd904e4da22a2a050a6b8d58bc39ce39446c06cfa1ebe219c1f28e7c258ea1505020a33f95fdcb41048e645d75da6279ffee701ad7be91041866427f4f425d122760bdf2ab2119d3b5d46a05e0a6df6a4c03d6f9a34a395331b5680a3a6db9db9730668afa84c1dedecf80cfd01483f46a638932df4f8b70aaec85faccff71fb5df0499db293c63e445631fd52dcd948ca9c685d53a3b4a0724a7c6d783f01c016b06ab3d35db1146faa06014cdce1b296c1208b1e1b0c4b314c5913aa5a09ea2c40ecd2d9ec83b2fc93d8055923015f32c8a226eee33c5169669489157839409a5021ce90543a15ccb0edf1cc5fc1130c931628f10527cb71daa1f2272777c8b4902a5f115b80af80a6b887a36f8112a056a80a0ab8509f7c8a3ac56c18a3bfcc60b2b764ee41b3d05ea8be2ec6810188f812c6e6e809b0621a6ec71b79cbe956208862c3a6bba9f62c579467595ecf08bc0014303dcf04c63018fc85e3f7139c10eb95f2280760226b00c524b5f5d93111e9c9458d6ae0e597232315d9aa5851d04b3cb12b1a43560ca91c0eb88b87cb7017a91c621e4d94ec1bf86b08610100305d8c464d1173427e26431adcdd48f06526728558618e4203dbb6240ed2c4d4e0d86729cd9b9f37aacd3bc056503f177a3c0d32956f004b7ac05b63889f10ed009d35d88844c971f3d7df98868c2f0df25bec54124ac4ddeea5be2990c6df43d0708fa2a2994195476eceb9b0f2559fcbe177bece57fdde5ab6782b3fac7715f0e677ae81cea9f71a31c152baedc2735501a28346f547362d3b910a99ee275d607c862522150e2dcc7dda31bce3708e70e2c8c3929b67e9bac6707b1c52f1ea704bf194fec403e7d96e274d084c06213d97475dff291d22a9fc8b5c06479ad940c15f87353386a4f5eb0ecd0819ec144f81fbce8b8cfc7419cc097e929b46dd70a49db184c233c4a2343ddaf9cc9d24e30078300b00025422aa572da969de3fd07636e139be108fa22f23d3e91328e9e5677e1bd060cb45bd94bab0c8460d4170e9cff75df017cde7b1af571ddfd42645c1406f8728c6774b31a6a8440be007a00da6b4089a69487e277e09d41dbf17cf7640aed0a4802285a17dd5b79e0a829008f4d21169cdac63c845d16a93ae57941cd7ac6e4e069b709e19c11ba8e0f2f0641e2ee465ef6c2b7a90ae65b22ca5280a0f5bfd5998d1f9d4d5def644212c70a17718bffa72ef89c7909ad6331eedbe22e26d7ab8615e548b4adcc73115e0003e87ee13e185744fd2e759d408758f9d0005fb41e9774efc4460712d183ef8804339b73eb5af26d5b77413e5ff0de46b4a1e040cca3c0bb58863db9bd2111860891a6aa737b303a26287c34a4ea12a44656948cd6047020e6d01373816dafc70fbec0a2cf60c04689f1972805116d13b232c703423c68c538b316f12c6dad590fa4afc0db0f2fc4f1b26efe49d5ea6065ae17872efb54796eb8854f20861f8609aa6650b406ca777ec1b4de216d8138f93ad2f957d2f322c22261a5ea84c54a04084001363889ccc4ed4d7f8ee38dcc325b28ea049b25cbc4266311af3d6afa06040f123d384aa6fc3d97a3670a7863cd99fad2a8c42274c3c26e16d4af370f01f65ef60c0389377b23c0aa769017266c69fb0fe4ce8c922ff89eb0a76d58d2019a0e6adbbff4661b11ff3c2bd0ebe47f017d4ac2f4e86bbf1d71efb571d7ec01e39f5a79c78cf28156575a3d9e96d49cb0504fb8cec65bfd9637a83417c2cf0e6ebb9bc1733b3da9a04a5aa37d353847806a205e9ce8c37b01a67b8de0fba6042174525e56f3215214fac0a9b04e2dbcfd49fc42776cb3b7ace61a66f47e2e0f4e942fe40f0eccd1be63f7ed35e178050e3a51ed5a3bb0bf2d54c22b83f0b20ae57c0d88a02e687c0aa3ef69a8a5f554cc21599305a5f89392a19e5b890bca0bc61c0ec2449960cd5e211e16bfc11d3393827cbbf7c686cb5ab051585249eb04f87778b06eb36ca4e790e621775adc9dc58444dc78cec64c1df8c8cbb604a40fcc8f865445c08e44f99e23343b8cfb1fcc66fb513e1d2262037c4dfa254eb70483d4e3c3065b14931b599c59e7049399da59106b57081f553bf3ed0e8e63b9010a5363d198501355f9130df8bdd126432afa126288ea338467cc369e5928cc0ab62d6f899f6bac8005ff80d4b27577f6904ac64ae4be82a7b06a1715395601030520d4e94ee0ab5b95d80f14ce06199508684364b2ddcb076a65248e6d366d018bbbaacc896259a0e27c8d83b9987a7a32d6885e384b2703147ba88715198f09130fdf31cac688aa91839e70b0a3080c6382a75cd83ff1b8590e2857f016d50875716dc604da51669b10b307ae4793fe8c3e1a829b832bdf56c87e1298bfb70f2cb9212c1b9097382766fc715ba602d8ed2c019178e7b36eb6dab565d8021603002f3179e5bdd087d955dde4bac522fd9a3d7f5cd231d939d085bb3c81037e07fa22b3c143e86e939bf92b1066ca68ce786fd4cf46c4bd9fda10fb1d31cc430b0bcc7b01ac6461e500921df951958b943654e7504aa7469c365ce910c3410d0a3d11793dc0114aa84a0fbdd383c56d3b32b0343b4404ab2789529b4ffac17008353fcaa8495b0d9ee1338d0654d31c35b7b492e29d6e8df2f6bd23db9924440783c476bb6e05ca12ccf4e3715463d074c4cc105d9da4f42b8aea93b0bd647d0c134e090f9ee5f0f59e9cb06ce27a30cc8cf0a39ffd3e523c1e42df6d7d57cc00facb22c0f60d20412e992a6c57819b1d4f272cf839152af3e6c415587ac3bf0233c4fd1e4a6bf565623bfaa808be719ea9693b360cf9366523a35dc55661d004c6e809d68569fcd0729e5f0ae4b1bfe290e167510c1848bf86b98e3cf816d00ad6ea3588e52e68098d93b9fd93fe64012263173073804904c5e0ee3bd725aa0187274c910842c6e47b5fd948a80c82b00da544c7ef2ebb9dbe068af38aac730cc30e11d592ebd44b0990e15b133a544bc684fdd32ceb9ef1d6b11234ac4954061542ffd8f743c8677da31ed584c2156c71857f19ed504f0eb89a2d1e665dbe025b3fdb27323537f88be3d87870a0ef0ee4baef10de57255aaafde9dccce05906c7fc8fc83dfe571972619db0482818a7ad88100239cef8b069dae4d76dabb50b4655255e61bba803fc70a1119d2e6dfc5e02fb06de872c10fa30e6b19d6fe102620c69861162da084bdd691c11740ea47789746d8e6e9421d85ee2e3b84d5203bb12e871151e9b81be9316976820d0daff993838165d118abd09f90e985ef60b3dde15a4ee666b49a87e4d1de788a0b091e8df56bb299c70e3ce07378cc0b804d6b6d39faa9b000d2d79e2782fcedbc018a522ec8578fe73c9981ff33aab0e08b3b576e40186baa7d0bf0684a057f1309ecd382e9871f5916d47ec875fc5d903ddd02e7380ba0941a0d045a49455ee074459d600dc0c8898a4632b9bd6817c054f1f09638467de57d2857f5634b339b6202d4daab1bb314494236e0bfb6c950ae13a0e7dd31e377d2796b0ebca9b2fe2da793da4f9b3ac3e09d59e909b57e40283c212978b8189c1cd08beda4f65ed90e0e52ea2fa2c00c988ebc97acb03a602adc4709ecdf62de8b405e0d4808dfc7305f49cb4bedbaeb636c54d180955ef0bf3d1205a7fdc433800dea8fe8478fd2f38f0340180d4be9a845f7aa454e87c003b2f6a55099e4b2d13d978e0fe69a4f4729c97cd85d93430e4acf3025d6d170604510eeed8d115bf5aa14ebc573ae7ad920036e194df1036e3fecb64638a4605c0f5bd3c69c16385ab0c9d1824c41375af124dd8f67a3a3d95de072dab2d0b056ee50c7ef8be9afc92ba21710c4c0c74e7eda642b91b4165ddda72de84a92005be7d9c8a7d77987d7178256d268a3b68d95de303b6e2e2494e009de8eb65db0bdfdf7a2409143db4a9fdf1b402f1668cc12c3c10f21056fd1d6a8a6be87d030dc6950a63b1c060b6a98e60869085dceeb4745ce78ba48c0f00c4c3246b76c80272ce31fb8df4b44511947b13a59f747e1456ef7565a7c084245c9ad185a4730d4d5f964e1b4c9bfb8f9e0cd19a31e7dfba78714b785c2323c4ba3769cbcaa10f24c5e677d2120a0b8dbfb9228e11ef10b96bf9f75a846d2928a989515b06510a1f1921ddf201a82aa571b83e124d6b1d380cdb62e329d3c86767ad30f1e623040cf68990c983ff08a1e827630feabca6916a51140bc9ed8defd104ca874e9e08a1e9a9a44211a08f3aa109de676e0260414827bf9b1d202762e32d8d9a2e5b0173ff8e96ff0142d3d75392c86352e8d7e035517892db2b21b0c56067c947820481d233ce912e54cfaf26408a597caf1119b9002bbacab2363fc278594067b1034fac67b8a8d178a32c7466e23d9bc1fe56115f1cfef356d20a4a7864f84f6c08cc0aff4dba58d76ce445aaeedae55b8e0f2bbbce865ade56e70d1ecbdc56a4043fc499aa79a60da32d51406ebe9df96ad73585d42c6d22936956f788e1438c062eb660dc22041373c730ac0a9bd46db3d0231b78db5ab0574cb6dda3ad020e0590502fead72206e6139d7c7f3a10709467581f3cdbcbd10170558ffd2a43d408d93d80fa72e8b125253b698747b4611c6a5a45e1ae2d443f4f33fca069b09f03b80da970226a0e21c040db1fec04a8b13320fbee7512e5483f7d8c1174482a0698f7d1afe71893f121bd8ed9032f2324ef6a99965a7e2b1ea104ad246171eb0000662bb240779ef1424214538b6609b807ab14ae97750a310fe7e5fc71301101e902cab5a381325c23c3c0baeb1f601c400aaf95edf07281afcfe8e510d6000513f30556c406ad76a2c616216954f3c9ca2e780ae46dd8f24acd5e312e0daa06eb48ba10582fc2ed92451c56aba70c92059c2243025ab96708c1d550d11ad50c098c45e13a215e07d5dd4b6b80d0aa5485e0c3db8792a824c2a615a1b4c904007815465a9c70596ccdec6a6836ee33f34cda28c5757554dabd7ef80bf6cf04002f6e7f87b71d911340cb63a215d4b028e0f5a380f0e4b5e40f95a2715c8c5c005e869ab9b352ca96390f761f323a490ceab9b47319777ec2190a0d4a3d661b027ab6b1e17de9a6329d6d60a645c1835ab336c83eba6791e32100fc30dfe84d067da99819a7ccdbb6ce63c2f1723e2eaf45f03e1cd3b093ebf1f91ec8dc240c0d9953490c1f34320319fb06e72bed9a9f8c2803680251ac70635958303dbc0101e32c100ea48cdc3e50a273c34054eca26e4ec3444e5cb16b056cfd6b8b4aaf0cca573cc05d689c954cab07193e5f3db871bbad45ecc098e50a5b0b0d22f0c904223afb7b96fc7566210411d38e321c98a741c8af8120a9372c372fc177959e0a4da168d88aca54f4db3c6a9821e355ae2bc158c34bca2140ebc7b5a302833c01575138792a03256e85d9826ac1d8c60d99d76383418215bb6b290131ab107a02a230af80752908ad7f741cabcc47884fc4a7337d465537a3d41c932373304000d2af301ed96c8eddde7e1538bcb828d39dc78a10c587a5c4ae73afa4ad7eb50cf25d89e24b9b6737911a1a049bb0de29b0bdc4ebb99f273e4420fce5ad04f600686f798776e16cb137e96c1eccacdfacb990b933ca224a5e3cc14a2857a17802bc12f2d539d58ef6c14e10daa27c7b1748fe3244d875892cae05b754135e8a03034c7530933966fdf30d3905b21626403910509a93dbf7ad0880ff41b2f4aa0883eee037dd1a4070064ca1ea037bdc5fae8b7e4cff986801dac0e7d4ee86ae0cb8d1c5ccf3292046101fc0eb6e227705f34575c023e6e54e891f44af67c49c084d551144df5911ec484297aa9512d96080b501b87d8c3d5e0b3d80f6775521014f8a6121c42e69cd5303cd98df62ac958faf648f77344a2bed28143d900158077f2f610869e35409c026836903cf4d688b5a0d0863c26dd9a7fd31feec08e101e8258c6ee887ef66f1743e122e4aa3f68419fde6ba1bdb405b4cb642edb0cb05be7a28e1777a17f7c5fefa02ffa75afd723b4c682214cb5abe5e14dd50cabf0566f53aa43e2324611639afb26a5d533201fd1ee444cd74c0eb471ce7c64a8603033079ce706459d65b17fece905856f0a9fe1195568d039d4d20c9be91160508bd89ab33b60a140399817dbf2f540aa2d7357787b2e33f628a8312069f1c370e3beec4fd98fd41a2c2d43d1e0526c1165ce573088d95fd202b31c2b58b98200ed6122e98ea97404b5b4fcedac234343560ad07a16be7a88bd9ba90759dc8590b69bf42655705c9bb8df4cbfedb7e92889b02356c165a9235494d15e6eec07c0b2996c855a295d1e579ea8c78828511c86996eb3b5e30d4a90918a2f19cfab60bbe0cf50175ec2e752beeb126cb222e916d4289ed1d92d4c4dc7be430c2b80e013dcfcdfd6aee9b09a6f124282adff138825113e0fe3f4cbf8ebb0916ed628c054fe8b64105819670d0c25354acc4273ef99439f2a781750c56363400ad48970df9fc76f988d31c269f945160f33984453b9bd8a3800dca5d7d4391670bc16c0d82c57b19f8f970735660f451cab6323aebccd3dd772cc340b80efdf4cea7b9087658e1e23c34ecb011cdd63b04783480b557b70c7b986949095e8fcd8ce45703ee6e6b8db3b19dc2226faa06a689e1deda6d008bf23fc05a3f4fbdacf189a50a8c652e34d8a47d53eae7119408814dd0e1678bdc3f9ff009ca0726a243754a0b3c2cb4357a4444cb87bfd81a101269765a50aaad4ceb2aa7257992a7f994ba0e360ce56049942e6112a639e6e8c231ec43cc8e03018886d3cddb026ad68d890909b654b49c957434e299fa51e23cb3e1dd94ac4c0be49d12238d42d9a721800501469b83e866a84204ecfb44465863d9e89f0b0fb392bbc176c88cced1b927097d356c347baec3428d808ac76385cdf95e8e1923bc0ca530352facf903f71d0c3b7ded12186490ccd8f37c386de7310ad5b967739aa1c65bf8b23adf93236000d3dbdc3ec6b78e9cf58995a7e9317d111dcf0c42fafc3748594926d4ab225d0fa93bf87c52082db3b2d855ef7501d8e6757cf8ed346dc3797822b2090802b6063b49c1468c9e39d061ddce9ae81c90008541d04596c9f6752042579955f3f60ac9ad14776536a2fbbcc26d27983cbeabf3651a30c248e916a6a8f2c982c7f9027f180b78d5e7c9b9c46bb2695cf94a5fc66b1464853e94b6217272a19d86d80990e1a76d7ee5d0961da379ca04678483930621d70c92c9197d01e325bd7ba50c3451acb1ffac59e8c3094f783a052667d2342fe554624ab2f6ce994b9b68290b8b47b56b55d3e9c092cf74c2b7118ce7b5314417ddd80b522d9acb4d5d000a0c38baeeeefb3d8c2fd4fc9b60b25581d86e06e1deed84284df1483b7cf78f4a0ed7ffa2c69a1b72ed62a4af1c9d29da7e7237f1bffa759b02b5b46187cf0a0e08e3f74a65a747a7fe967b5aa695fd265cf6dd2812262586a22fd6db5d530edc0e98a92c6d003951e2545c16d73aa151287a46761be2547c3baaec76d4ee2f580f1605e6726ecee70cfc77ce25ef4a33a74fc6e8de3e97b674a3d52e3ae26b1403f06970a412235f1e679126b64d5696e2596592ca107d3f955f6a0dac6ebea0035c795716eaa4836276a5e0c3515dca6398ad47b8e47c897cf4e541c46592150fe715ce2fe566c361876a8e763bf791fcdd0964c9c87d609f231c9fd1e389c6071570f2e7957ec6773153bffcd8ef1d7c941c687d2d55e0653a49693ba8062a07d23b0baf875df701dc08392ef7116316bc23fa7a9a4f5641e1f37b643aae45082fbb28e184e3097f7e0a7f92bc7f14e5b2f41e708d97d1a1eddccbf2ca88b9081a7abdb4afbd8c4b55efe26fc1ca80741b46fcfaa4ec0cb074fae71d5b714c0d93372ce7f7b9fd0ad3d80d741190248355b35430c59e1cdbade927a3c9070a059075dadd845c0fc07687b75d4d1b04e364febde7e72ebb31d9b954620f3751015199c3ff3d2bda85c53816fea619c13cb8cb5b928ef802f40310cd2ca786260c0d99c91623abad38f9989ec98f544a53c4bda9324dd481f2a191e6b5297523093f3eca1bf381532eca53c8b4991230776f8acb25f69dcd9884dd75ed56c2630904508f4eea7f5892ed31d90407983adf669436c1e30018d5ba9ae0f26d2e49048019b0e9c8c2c9bb9f147820fcfb4b0e8c1835f15d19c3bc2f32ed34b490b80e69cede7a2199d0f8b0dbe163ded82cfa5498413978296804dbde6bfb6a9218001cf858605e764cf320b0d2e1cfbadc5c2622b7a2c75fc6581a443bb6e7940c0f256a05fae59c78d9d48f535fe91437d7d53bcd4a5a1fb148946c6340684cb90cf7094f531040a9b36aa9b216a1d32aee91e31133e6deceb74023436e600d0708aeef9fabf7574f784a999e670aef214b7b8d18a478dcb04fb099a46719066a0b5792f07029955553dd420796276b62802aa09889a36b94178447ed85cb8fa90d0f27837be512f262ee8eeeec11b088bd7aadba722dfdfeeea1b4aaed56af3404d2b49706fbd15926d4d5df9116899fd275169ac1e231b07071a3f8fc153970080200432bd95ba57411e3a11d85c005fd4de5c3204c238fdc7b589f99d204970f24ac453ded2fd465e9b6b5caec45d153f810fbb138216614fbcd81649f21310ce54956bac157d7ccb8216525886d907a7870a686f654c15b568cd8bc9cffd50332ace30b2bcad9ee03a771b0d3fa506149be207ae44cb945bd5ab71d9c4d2b012787a339ee3308986205b304ef84ba3114f728b202642c5deb220f493f1f990f408dc8e6aef089ab7a41e7915016229979addecf026839e041392e1af71a7e0033cdfe2f6b901282307a6386e8b646da7ca29b02b05de64d80539b3c1c10e7080f8c93c6f25b85ae5d39001746a1e0674d71d2ed9269938e8d92fb8ec790ce00808de37d8df2e2229a32b7ea3dcb7d107c7b4c31ac7f9dd3d903fb9b40af29086148d51a9a1a2bb5d5889d31f1606d1124b8df2f109ad73082a5102e3eb0870f7adb2755fd0e17194008c3dbf70a6b51a1c35e7e76db147cc5660e68ca641d0a5eeb985a99b3c72a901e5d18272d895923e5f2cd9ef3451a2d0dbedb10bb7705a41058045e581d939867cda3b10293b5c3581ed3c6a2ddb92a8d2c375a3e960c21a31b2c49a69a7efafb12e41e28b16a79a88fc21a8336e108d3a74bd756f40db5e7426a7eef772a33dbdd6c8a253f1b250080674ac71ed807b99e6ad5e9b500090d2ceb9f4e7af0e5faa019c05b4058a97c7fd25e8a5d3f313294a04eca2c0ed151cb62abd02c91882545e24749d0b86dccddc0f2d980e8c413f8448b81f209bff31d0d8826a777b98a00836dd36b01e562ea1b3d7a06dfd31873b550d3a6089e6c7bd16e81fb86dcecd7d9dae88caf8680b1c75de333a0073f298723167f0374aecb9c226aeafa02f381857e7989483d3f5deb3262393331277e66e318800da4a6d48d4ffd709e9dc8e443ed844cbb1b35114e342e7215214ced8e7908380a22c59c6ea2f87c5ebdbc6a58cb25eec16b3fe3afa7e3cb75f0b7b7c9fc00f501f43bc515407ab11de6caa246884e01e72166d5c6efcdb0c49f963e90d08f0c0832bede52a27a2fe19e102dbeb916878607db8f6ece77b679053ee1c210a9cc01d57849d8912876b26024c8ceb32b8773cc18fdf174ed1f54ff72fdda3e4cfc01c9c997ffa017a49d220803f3582aa0f47cd838d55844a140c83a93e6c4ba8204ba8aa293ab62c4f8e9bceffca9877950c32c16471dc217c323946efbc64fe707b54cbb8be5a78b7aa6a56f7949e72f3106eab9db0def69f60cb6868e69b6b406e217bba41a862bd28964b022d1b22bc545212ddf924f251714bb1f6e09d36b0cd419d82b5ceb5fad15d9875b597541bec8d16b367a9cca2e12d512e57b7c75028ff805fc54814ca837070d0acb7d9afe10bd0b36ecbe19577db40758e2b6d90da175412af462b8be5aae4fa8668601666e1d18a91df019bde436bea786d927049a917ff1d057a06b20487cd53818ff4262437e1e712b0bb2db803424f3f0ad0f90ad27f35aa08dfb465982586a5708a4f22f9972ef69f09960115afe65825b047d4996ea1d76053f15795caeb75ada3c892512c7b24346e236d8d303850a4c05cbc68f1f1068f5bbe24ada83b5e2d8eed8cb91aab925675920986db45d1d260463e38f9daf2c8e7cd6b5ec058265d3c00a3aa80ab4850cad39740aefc2c2c10c1e5d70e990ad5b75648f807d27c8696a29439308cf87137d711efc2632b79f0b43abfe77e4be2880294fce7616d0f7d41d42b5b20df80ce7571364cda64af30f355e9676c730514eaa27b1f6d5e7f82a81474a49b9d5350d6c59b2f8ca4ee30b868da04db37e4e0df0732c4280bc7c6c77eb10a7aaf0b9760cd99884eabb0f05d460412641ae9fb3b6188312c11f05a470f9565f363a1fcb8f5bb81d24754708479911309cca5570ccdb19a6ae9adacce992a4f963d873e05f1fd0a2d80b9d00e5ebacc5905ceaf8c81b50c2c4fba21640eedf8dd7b0b171bba843a836b542070091c1a73b292001e43facad10af81c736f2dfb9777e1ec72a7e2d629b0aad0a45027ad1f8366e2817c48618f2a48e07724047a63087b309d70515530eff930cb93638a8bd9f9103fd6ed32c25a64bac71d3aababfb5d717762c8322b675da04558a1b76685f91fe8ed8e457faed956685e8c1910140f6c6021f7b539470730960dc0970abfc26287ad379074c562759996270307bc32f69cd10b146012162029a8ea9036b154e688fc6a5ddeedb9f29c436f1fed62e2b6122f048caea72dc0d89adbc039b5d660b0b1d0fc453d2589be9bba4bc1763ab27343da25916ae3604f360c118e42c7d8128a55ab199cc04d147af477294fbbddc4488c2856f5beb05f56443b561a8208b1398ec8b57bd346e0b0f5d5b25ea4c1d8a8739bfc02856080a653d83956ba431e9a74cd3ea9b9361a8b86ac8cb51e031c04207ce694f550707439631200efa7cc4ad0c37a91f5c9821c1228c6befa37ce0560473e876530fe4be31d66cca4b11d1c1178193732d92a964b146ebac8157be8ca74f8f896d05fd56760a8658d800162b2416d9289097662b61bd06ac4987d7943f476f5db9066ec8874ce59d1d90d71a24048190022c64d9e08805169ee76c17f21dce67e704c643c3b4d9899d5a2311ff7aca1ab4f3cb97cf494fec4966c018372fba6bae0fde92829043203f6f4b267aee7c4093aab87e66dc745f2d69be9f6bdc48368200eb6f799680e71b4f40695bd936574ee4fc7a24ec78d42a522baa71d13188b000ec2187f94538a79934ab17acab981a5831b6a5de472e98e57ff2c70a298e3a0fce164afefa3838ff05265f44d69a3ae0d0cb33e4df505b949951ce4ce7e1ad0e54fac6d623ea3ebb916715da11a4276bb879600d6e7ffdca5f92366f3302e80b1fa398eeed4e435a1afbacd918adc536c49734072fd208add3a12d4ce3531404dd626551602dadc11afcb5277ff9bef58025a500320e5799733f51881c99e706d4373bc6754f182685c197cf81586aa80686ee300fc2f23108fec1cfbe7f7f0b5edad6e4352ed6a1f4ed3368bd9fc44533f2d71a27d5997f34718ec6e16b8b0730b04e997149b2dcaf3b7a6c492d135af6c691931a43d823d4f21e510a34cc0b2eeced540b3dc504b2172f8ff491b666aed50942733e51d8283162ed232f82087703486d0d4d8aab3cb9d3100f8b9dae547ca0dd16ac3fec6146fb95b6f24c05d041e467ed45d3e542d9d1a4263decc36cff5e17c7c4b02d1c7944e62ed618038b8df391786f6acaa24261eb2133dcbd25cd791ac9ef170ab0761e13c3ba4402f322f25979dbdf52f76776eeeb834f28383019eac29ca9556b67918926e6070030623e9e80b0c2fce89c3baaadf28513cfb4a200d6c4ccc90be81ebb826bb10f4beb59e987dab096676d745465deaa8bdad9c73338f81b895d35887cc8ce9d08ba3a0f9d1db1c3ad6105d79c2d30c683b2a86f29603569c66d8c59167df68001e88f604f84ea9b4ab6cd163651999b2a780c92ae242a04dbf00ef4d379eb21003a105f859a03c9646c882356b7e01b3a75723ecbcbd7c76efe27b33a275edd0e9ef786e90df555792db05647c817b50586d4c4eb2927049c91e3b7c42728cf0ba898c0a09aec56d823a9b69b0dea713f127233beb8721d4c3fc9256e746edb004d704d1753af1155781c63f466cc1a371614ae98733e0e66ff24afc529f96f0fe916d016ef8f266d9c2fae13df960503c4cab3e79936944fc2385971dcf1720f1cdf4cce9c23b7dd49bef0c63f38314eb365a796d74a876208dfd1cb7f99b100f0fa3777b613bbefb290c504738ea1ddad399ba7ed380e1cde17e9f7f5e1b4078788f76fd184f43c0e0d399764f1996c69a41611078d8f4cdfdce07091199a0da2dc573577b3af907a4728a53181035dd90bc95fb1868c1e7b7830835c1fe70191c4d640f661d427e5f61a7b9f9e613d29130d253fa379c172437d6a8b862e02fb65a42ba30ed86b13b592c7601be0c097cbbd420c7feacc4486e0960e6a6e0ec8667ed7bc19a8e6a9b27bbca94a2fe3820b6f5d3ea3d5f937b15a7529b6d704a8fb5b95c470b59f8c5b5c78fa720066f44596dcab722aa7bc2807d4ad625807bb9af08c5b4cef2849bda964860ae208302da05ecdff1d8dc23f1341dc465a0300b4c4d51de598fdb06764f2ecb9231271e39bf0da39e3409e5a7d05ea7a510ad7d67187d84661a2a62988bfcca6141f4d5b629641953e1de0a5fe475a73dd0a7ad7bb4312d0a06dbe9d119b2b83bb7e618142816d64754af958b22e25826b0b5dbbb90f3bd69fe779e3dc9cbd6d0197dc65f7f3f48039c8d694077d1daa600296d3715b05d75946a8466ad09845166cd4347cb63d72fb414ff0bbde547b950bd256fc2a649ca5b51925d0a1ca8d42e5803b79af608d9935cf5a654f3e9bea032b1bcb4db08e4be00c3d8c9933ef9808432c6922031c6530e022347d2e45c60d0b386bbd0fcf487dff20fd73fca35e6284c1ac5bd4a5f443d7ceba57662b3b025612197c843f16d9ae1a501276a6a36b007c4f95c0a660f3c24cf598dba29d0ad8ab7aaaf17113bdc9a2284da544523db826694de0047a14155781fc7959c400c4960c9a9020f576f009addb6e155b5a39be140cb75b2a1db48530940bd2830010c8c219bc5437967ed9d9c47cefafffebc9275ebdea089c8ae45bc50a66770c02972864fa834afdf3f2280274ba67d71f44a9466692ce8bbf7413ff78dbdf0de73179f9e59dca17ce219ad84bbb73d876707174a74a1b69c2e4f3b058eac7018b0372ff3c2a3dc46c215534880ae48f9694ab64ccfac8f6e6b4c524854b67049c674e67a7fb64c036bd1dddfb7887f481fde3bfb2d0475b21185dc819c765001ce2482b9ca1939b40ffab38bfb715be04218baa36579433e63ea32b29ee35089b4e9db8bdb8f1646c19b384a32dc6021366f2ca4339ee80e1f1e163ceae360d4d578eb8bb0622885f160ce5a5ff7da841764672a721bac60ee87ff99feb6901ad75520b0acae7682bae0f4f643218c3be2d7d6fd8f2f8182ae4370b1ea1f60e85361915374c35ca7294642b78787ce366832f940c79ccf0b2d3bb5279830507c88f4a70fc86094f87f0e697223db488dacdb2979361a7672984928000998403808ab977a81eaa17ca7c1db37e36e5d35e7be2e891c73499129f6ed1b1772705eca1e86ab74eaf60ef36d1319ec3bd5063c4ebedbf8a2f4f3af67cc0f06ebf02f914a5e617fd0ad1754758ad62aea12ce009a74baac47332cde18636a8f679052166ef23069a5c7b61a1daf7ca668578c28f74e2a199cac3953f6440ce879b0762564ae32ea52d0cfa2c1455c203a341889f89267b4b1fb97b464f977727e202cad412828c0ea500dc0558a23f3b4d217f14b4f0229f00b0488360d5cb332c01fefe631fa73653cdf1db7b9073237a646c5e539201c583b28aaaa85e60ab7d0cc11b646024bb609d4353202a8cbf78cc351358b3c4cc5153677d40d509b3ae01009dbed0eec5c0990a8eb200198a09e881af7cb067215a400d1fa90dae157d062d781fc892e310711e70b0924030203db91e1c8ed06cb5c90993d75486424d0a0e4fe77d7712bec504823bb90b685e540ca5f23b9a65cfcd67a0e2f047aadc0bad9b593790c6af6c6d6f3ebd92f137f7c3f93ed9be08dee739f81aa734cd3e0a46abc64680890055c3a7e6aca92fda2bb40b3b9ee3b0edd288f090b68a2c4e05867a41d8082b629e3b45afe0cdc354262437cc9b32a04d3d7943ae387ca41509bab3bf214e12fb512f191faf526f833b056c440b7de8728065b4b23b5437620708794965db60a76e649a7c22ba45bacf4984a9325d650ee92feae50ad6008403961769dfc8a934a24e8b1e6f2ec442f1da2484e2e2655b5a88e73379f19e5d071327351cf2ed3baab0bcd4557f7bf699f72fa64b66e748bc612c691c562eea0c1701d0071391a5998f824044c7b420d15f4596ba7ee683a71f2e2351fd990802204fd5300a0da7d586d45fb30e8e09303c0e2ee520d10aba98bc9505dd552e0c86741850c4ede6fd60a58cdc1d8c7cf3f76ea4ee37004bf44e9b8175b473ae09770d767b40f28cc6c0c2feb0229914ee09db491787b65388d77df4adf5c42d076650848fb3f7b0961de13f6d1c09846b395782b8140ef7a03bbdb3ac89bfac007a1f1ba4060fe96aab7974fe5c43b32a71fbc49d904b1900341800148d39340892165d248fc714e62edd9462c5ed718a0f4be16471b666194c8a9b89a93797035fbe377ccea1221386a81c0d6376a1b3b9d474578ef7b83489a9ba9dade7eb084a6279feacebb5acadb16d9ec0017d20f6a7951e21d7ccf4292d3f552bdf38049ad8cb775fb13e6dd2e96902099486d30ac89acf05af09f6b5589fb954d7d40aeefa8052914fa34620a705a65ea21b1b941e4f58e759d163522f7ceeb8769bc6f6a8cd5e74c08069b81060ef84bed5c0c3a6c20b28b69a991e0b26939e179b0f47f46b55dad63388062dbfebe4f699802996968d3b4a402b64e963c82a0b7d076d6ed31642a683844dd09a095adbc72d21f0ae1b860be0f7c16b1ca91c1a1f0069551f6e0559104ceccde44494465c54f05108059af753388f81399c1c1ef60d false -check_ring_signature 2684f8e51861f569c0fe97f9ba96bc915133016bf9c4b5d78e2a5a25e53d4bd6 cd2f86eac697799c1107d4cf6fbd6ce5ec933bf164e2fea94208b1b18a032492 1 3139d08878da018064cb874eb23c49bbd3e9fe05019f03e0ddd168e6f9c4ef83 4eb9183536540da0c2cd9068201879cf18348f7f18f2d30816266ecaa9e491fc2dbbe38df202a9426090d6aacd182d06fc8a79f2bf7416188b6648c1fa90bb0c false -check_ring_signature 8905840ba2829e2bb6b7f834db939b7ff006140df120170253e73d6a110adf09 978803e051ac24bfc730d0dd1f746754dce0cf527357883ab71536802e4100c9 3 7f68e1932829141a4823849121662b5ac7c8c50f0a3ded4359896648875bda84 c0bd288abfda7dd2f55ba0580fd0b240f7c202c889bc9de914c759802d05c984 aa0dffcb5ab16eae256860de6a14fb6cf2c474b828cab2f25fdfb704fb247517 741e3d899a92965d92bf4dab092f647138e28b4ac06ab729333795872b26b10cfb5bcb8ed397f5f76b185c6d8a541c3a72bb925b3ab797c3f55ccdcd14215c039d0ce5c9284433be61c16a44c5b4555eac61265b346bff52e926b9c7b2f2e40e776b60386568258430d9f4e2034d4e0eb5dbc737058e8665d1d9fe123a06680c6d113440b33b70a0eeb4a625ecacf30b2de1aa86795298aed80108d0a27ff602d689d89ab73d78c4e7a4c2c744a7dc25fc82372e76f1a4df9b34d68425b10708 true -check_ring_signature f9d51e98ec2e48852e71852dd03092e403ae335d6e712c7ce7a135bda5a41833 856dcd0cedbd4f363b4d8ef3a32588cbd204f2206681f7980d096505a5be2296 5 8271049ee2bb50fb308eb6a0042205494742c4ef105b3b9aa0870f8b334e32cf 5c68f45750e9290b069b8860cfa4d0f08f6812989c8f9fa1802f57bedf67c59c cbde7ee9a3b0fd58d04dfa282e9bcba785bdb1dc60ab660676faced58f691189 b0b7ccf22728c75f2a5238eb9b677cfc87a34cbaa203ea1845ffefc518b9f449 670b179d3efed230f867f3caa7bdb61b4ab8d0cb9ef89d99ade0f58aadc69093 364ef7f698541b4baf8c939806ca83aec12f1f27ace2d3050da0895fa0574a0ad43c98167ac7a36901f2a2f214b0026e317207068f45de05cc4ea67fbf2b060475c1cd6d24be47b99c4f3050d84488b84064b8d307994a0361453e7b88450c0b7b8131f8a2ee22b9d41d490a45e814b86c10a4ef0b074d5dd0b5df522db111190322c6fa14f31b43577ad9d2dcd29c2d5aee632846b14522a4f722ed4647410f1c59bd0788f12ed2ca64670144c2d2ba3dbd6164a8e71eb68bb5b7a1bcbb22090936524629c736ab57bbae68db652fa719d9d45dd20218e193fbe6bdac195d0d2a73260e2e306cfce06aa220d41677c25da6cc47c21d41efe6cad282169e830d902e636613123ef44fa9c4539808a507d867fec3bc063f2fbf8f66ddeea67c07eeebc52a03ec566050f634a8f6f543b94b357bc2054b04f819c31fa16804c400 false -check_ring_signature a582c951daed8d5934300f4f62dca8cbf4afced14df907723bb998ac90ec12c6 238b52edd9a58b056c875871eb4d5091a03c63af2cf923eeef4042f649aa4f3c 252 7e777ef0e3d120dd0b333d5535ccd084feff7256857ed69bc5d4a38cb8ef967e 7b922b179dbf6c782f5b5ba3deff76f3fed7923be210261fca1315f32eadcfdf 6be26c7910b76f1f489b1de8c6c35d33c68cb05e718a87ba6c23f583cefd0223 1c01742ccd62789422afd1236a81a1f0bf23fecae14e525a94b97bc4d337c35e 58364d0fc67af86879e113189d561386cfeb7d2703698e265f2a72e81c0960d6 114e1b802189d26234849e711ae83de1482c9957a1d96da438571da544550837 06ca49298a78979be29eea3112e61a7103abb873e31d594b10f2a6a0e86f2962 27bdd7e9a8525369473f7226a29c8609ade4548613f4d12567958dc9449245da dfab8a97aac47a260e833c97c99784f105eca2c2b266471588b2caf258e49da1 29cd7df8c9f0bf34c0f0d9920498a83fa5b34c9fa5a96391ebbe554a62e2aeb4 49dc207555e9acc438336583f3c6c671cd30e955b5d03b84274f95fadb5522e2 bbd8d1ae66dd2034330428727878e1b720c8f57ccb33453262c8a55a4554f111 66b8f8d8cdc368ad72529a73ef6f6c2ccd48e10e91d78fb411cf6ea2c703211e 05c05274fc5d0777a32b94dfe697458f97a29040f712514e25d0541007f301e2 f0bbf84dbf3162a783d5bedfaf590d00a4d521a3af7a4fcd9b62acf803aa8951 ac6b7380a45753ce46dc4959d8138cc66e5ba88030b8e140650d34fe5cc93b87 6208e49329a1d34b7a83e30a5329d989c7a52b382d7ae056ca119a108815bc36 f824724e0b7dc12c2c0841fce07a1ecd9c77b0bd8f84c7c9250312db037632de a37293bd8717467542394c42c4b7f200922fd513963c71f62bbf97f63286fd67 8a670ca75ac7ca3695a99953fd7e685959b1319e5b17fc3fcf53461ac3e5ae18 ee5d487644b9da61ef2e07b111c4028caca3ff4993adfde12540cb0c265bfc60 4c53c2913b5e3311968e9b7d147e473022173739c7d2a283f4977b358150a9d7 ce60d435e1422499aa9c207ee1cc048af47f1c605c257acb93276341b3ecb310 1b71c1d9d02fc8e77469d335d7e04c9b06d477fb0d10b3b0713767356a7f93be 2b3fdb85c9ad1028faf36e1e8f82a58ae85a0da04bd4f82a3ef8a035dd83c402 43b67114e107da5f5199250067cd9c3e6d2c03ac32ef9f10b94b50b4d89144e7 2e65c6a2613b3fe85a12812dac5af331d67470a9ee7254ab63404f30cc735eb9 d6699c569cad4cd4f24270477a4722c48c6f7216b6163eac31d6305f7067afb5 5a20cac4b501f0813d94c2f90332d45ad4bc89f20ab3825e9b694d26458288a4 466602c8797f666114621662a6e56c5094bf7f572bc2efc2f0cfa1515299b7cf 73ee2bccca9e8aef60c3759902d8989cf26d47bf4a1f02e78dfa8cbf7719cf23 758e7da50e467a485644fa130220ac21ce21206e92abc52b5de4b7b76632633f 3a2485a9d6928c4b776b5dfb776e4052089314c794b9629bf5308e8cafdb4257 1cbb272cb8f42c6ebfb5ef2cc2cedf29a942a1ab62c2bcf9e04ffe68e23352e8 9df54116d6e9da454067a67bd8b596950a25236816a11ed8e3f93d6ef35bd4a2 74217360442ac2659fa041595e4fa94436fc39eec0f51a8206569c4860d9ff3b e12ace7aa27e1eadcc982a916c31d004c871353e7dd6a1960997d67c9f7df6ee 8c4961aad8ce7626cab6cb536eca3a34570313672674e858dddcf1431ed6526a cabeec41334e621a0647bca137e679996f58d79a6b9f21e7b2fe91ebba7c214f 950ea0ed8ca85d2a215bcc940611e74bb8ff45b9a308813d7ccb2ee1c7173702 dfff2636aa7ac1d8bedfbe5bfef16c81bc56e71c62bbb0f67a0d4a305c4f2364 eb09350043d2693c5710ee8e89469c4a852932b8bc42b6cf812e116c561f176e 86801e1ba4f84fdd4f2a37016494e6083079bad1a035919b25645667a8d814ea 64ec08f43f4c387fb79e7e2e29d0e20364446db52f24e54b610adeb303b65008 4f561a8b4655c5ebf1a24d838457f904e705198850e8b7078a042d4089c032ea 9203bd689f05c6b86862be12c5118a689ac4cb97a20b9a47c4534ef8eb8daea1 438387551c640b3e93bf2cea525f09c4bdff7232f7aeb8c0d1c8d16b34d1816b a5c0b15550e9d145a7bb651e12f9a6c3d2f7d6ee4ebf649446553b195409e118 a8dc6ced103efbc01c54b7c49f50d4317cdbe52f42cc3284bf5f8349ec30f255 42428a0d55adc561f0afae568e714581f9150131a26be30f14ca04693cf5cac6 b8de18764096df84c8abc3362fdf35d8e1cddb1565535dc9c4129d791d7669f1 5a48b75815ecb885fb1e4b26a5424f054b504bd95d04112e9d492993119a2f73 5808e7ec9ed1d720f4e0132ad69a3995489a4a5c04d9d6a9d9b2805b43db0575 14fa1dabf1b8ee746a30d935babcd4a0d0973320c762f95259b3904a978540d7 fceb83981e37d5b8632c248c2aaadf256e8c8e779f6af332943580e5d22cab6b b0e8902ea99f6e207cbe709cc373179693361af152085f52e15dba0d928dabce e3194b10a9ee8fd345bae01e1dbf69a3816acdf95d0ee6401f34540c6c1ee304 f89ed083f70a1a71df076746ae63b1b654586c08309578e42bf7db58238b88ed a167ece1891a8888710f783127d8459681ef2b128707a8eca673fc53bdd3f621 427e97c7bda0632258cb6d04887226396fd36adf4addc850eb3c31644cd4e2fa bdd318ae0c70b010bb3d9075295f29b46a2734237cedb5fdb814ad9826645a6f 4b92c0194000f161831cbfa1a358a5fbc209c9d247e554c4feab5d8947f7c359 062e62b52ac8e820e85c66691cc5ea4c5d389b110ede32ea28e438d1cc0b298c 49ab49baeeb383bff654b14105b7076dcca3c9328b4686d5ac9482d624fe361e 0066eae5047d3a56f8d55028ddd2f560cf8ef1df739c8542d33a57be057848d5 06b726f64a494d169b38d1b3b80604058bbe2ed88ba22f85845bb01ae6b6d7d9 4ac0793f6fea64c54a1d8c1e1b76887d248dc8e87006a449c0e2a42f7836ce93 e007476790e068ce71b954767e6c33af1ee388673df91c7e450964d3ed1ee5ab 872c3a9ab8d192ddaaeaade4211dc93b14eabfa59d3a7fc7c8cf99e1b2709293 c6b19b41b19a45c9424bb665e1def554052367378925083e7a5e666093fab893 7b7bd3121d8a1284dc8d260c4344698af9d90fd5ad93c3012801457547038443 ef9753c4715cda8fdfff8f703a6a7d339562fc78a496bbc15a1686576bb2a7c3 23eefde0e4701468bc5a0588d30dd20ef1feafd2181441731bde5d62dfc1bdda ddabc1039fda5d3acba6127e2de56d863011ef403e88f6c9efaf4d7e5c1d7bc4 c786f77f9d06da2d020a9900f746716584c25ac75e73ea9d77437634c99fac64 29facbfa218b579acb3f1c6157980369904dd25c8b9bba3f9b85655b707ce974 a1d56bff2b43a3db39538dbfc0a0a030b9457f5ccad059a313ec873587a8e26b 4746c8364862d918f7ed56fd24b449e3e58fc72363b41516fad0f7a603cfbc88 73a9dcaba1a7fecbed9fef18d7ba61fa94cf133fde4a8c8fc9103bcae075d015 a1070caa8c9a71b70f2aa2167641fee3d84b54465dfa613f7d31588ab92591d7 cc28051cd8e0cb33fd34c55c41e2e6d58e12226c27ccbc7db8a4bb991ec7a148 d6d2ddfd553bb4bc300574d1d622d47f9574562ad9f2b14debbc89540909b6c0 13d941cdab477c153e535996ae0c95eaee640c903b57c4b3b018a867389b721d 402458941512fc096591e76a957e897831ef55383ed8f61b13fae2725bac8ecc aa1df5777501b688a610c81359851c426de0adf15007923f00d6329b47789b34 28ed5fd7b6d2c7de9d011e28694f6b0fed9310c5c258e3f7c7b3e1c0455b4d0b e9157b510bd88a4099af2fb74461d2930a1edd205216b78e3dd7008965a52bbe 8813fd003213d80b30f9390b7662bc0fc7483a79eba148949bc2c412c52a4ae2 1f10f5f2d4a207142eb48a4c1c06f93ae0a3ffd17a39839cd0a867c6e7c0513d 1b18aeaaabb0853e60bf1c08b59b707c1d375c5216a6632b7995014550bebfa0 c660572d346863357a6da32995a421b236bee08447a9dccf34a6d9f0033c83a6 9254c83a3b926bbedc4a77c5aeed4ff6c395bc53c1cba52189b492883541778a e8e45c363933d25b28500a33fc3477d7034aa935e6deff9421242365d50336ba 11036342c257b80f34ae01f74a0511f587de23a14ec7af5ffeaa0c05fbdf2b9f b215ff6f745771c6b5ebe783d7a59ccd3d9ef5fa49e6e5d7fd57116f7d67dbb8 8738b2f086398a81cf9fa5f691c4f01757b4ea9706ae18102e14e842d193ff26 b5b979f067ffb730ea01994d1fd368cafcc0745fba6f207d1cfa47cb1e31deb9 e76150888cabf98de831b1360ae31f92dfe3e023d81d3416696e1a726b79140b 4f0e961cf1b7532df4025b74fd9db339fa5a33bc74bddbef5a9612b44411a00d c4e55b8a2ec30acdad6f492866ed64aec035d2d64a4a1b575e21a521855ebfb0 7b48df08a10e641ece5949f0375a3b7f2081e7c22931e2dfdd87f41690dae72a e3fdcf909780380b7e6327318d38690e91b4de031b05b73a998e6dde02a3cdc7 8a33aa46e3728a78c5981d2cd1a2ac7dbd3c417073343c5a7d273ce119879e32 263eef94470be426658d255544839620342824cce0c99931be680468cc8d18a4 dd5866c6b98bee83fa4d06cd74100646ceb89255860b060322549638cd68b077 cabfcce35916eb6b6ab178d7d4a2237d78a329a205b7cda732965bad88d32641 0c28b6490f8c8a0f1447c9af700a6bc72f07f8258f61179325fe2a89827d52c1 4317b07c326dd869790dd4dd6fcc4718f99b4c4c3d0f029f2cd70770ed050bc9 769b9b3a5b3041f2af835963d1db78f5aee35184f502f3e3b5d879eac31cc4c2 445107e823cf3999b83afd38a2cbb6e560583362d8bf7a05c7b48d1778d50239 f0f66bd095b53ff80fab934b7f9499e13fcc1ed34292a0ce7a55dcdf42cc3a28 81a51f110ad7dff181f0718298114982881cee0f58cd9451a582a9017b1e7216 d4e7bc3d880fcb0371db432432562d828867df68ea9a91ddd22aa0152a9af45e e55136b9047cfd2bb63ce4a5dc0baff0a2776cacf12f7da2e9517e736a408e3b 03f6e5607ccdb46639cf96af33a6e2ed08bc44114a74ed2c1a12b6654901cc7b 7edf02acec618f2e35c57f0b948055084d19ed1608dd176dc7c11028e26da083 1d6b6d835484f15e44bbfcc8e9c5889e0bd657e23d90803e084b40566943e9cc 1eae0d0fadae3ae260093a9f7d20d3590370bea77c1a30d6792925e3b3e63eb2 018b489de6774689d1b75e12a80694279867003175ea9b481704ee40a2443039 a0bf7730c7465e230bfea2aa80f19d5279793fadbf88201291871a0bf57fba54 3d73b9e5868fc16ca57d3b23567946bec36de5e735071f4d80c987ffbb249d67 848218d42e5d95da5f1946d6a3f1dbb60762acbaa94da7a8b5304a6be59c4f6a 586bfc8fe9396ebd08cadf8ed1a21a3ebe3db657bfb6310f82ae27c98a7ce0a0 b3932c83bf4a1115b20b4e02f392bb5543f84062385c93939449ade29e7c0d01 52ababe6202d262d9f0df1ffd29b88ff557902bf18a2da7da67dae2ba34e4720 d4cddf9ba86e29ddeb4d39b5a54da08cd181a1a13e1ad171c860eacc9a2a14cc 969c6362503b7873b2ac2c5177cb4690bb62a468011dc5363bc4699b641053c8 43e0b337c1a50c89ef76544c638d20dcb623274dd4fb771f143ed83781202fa3 23811ce42f3c2aab7801496fcda96e2e2ef10f07d82016ebc67e5a08f29a1f29 c2bb7bb03758868d1af742d5746bab95364dae502fad8b2f81adb31e66bc046b 4534db57eaff6904ed218743847e4272219e2ecd171c604961861ef448934d12 036ac2469c261addd54287d94935e0c12194b304d204884f23fdde78896bebe1 accb95e018f5030d4ce9ab2003565b0a99e93620f0a493ff28e7e637efd136a5 205f1bc7c6081f33add44081d3beee0b40a06adf7b040202696cd51c74b1d89e 17d18cf830b2ff3d5bc4cb0b94660020bdbbd307be28a88f9a234e10cdba8af8 7ef4ea83c78665ee3fd15ffe8be1ce70d3131fe63c3cc9b6f455412139397fa3 fb60752a4e870b898e7c1da122aff9fef8c806ac03d110bb7f20b4b423849005 e0fcf678ee4bdbe854949eddd52930d45ad6db460025e92e609de4c7c65cb80f c42ea5c3097dd762915598dccd636fd00cd8326d174e9d76058aa8a169075d02 05de71cd2659d352263648b4ed0e3bb3da1b6995c24436153c9f035dc0f53276 ab055e5ba753271e3e0802100f0aa47aaeadfbd2d09fe6eafbe4a9b76861ab68 a6b8d764bf652b856686bc41353e9d5d25b353dd7563268fdeaac88d5fc0729a 22514bc3f06716ab2497d954f4f84cfd1d91ff02a3ea7af56d34ef32326a4ba9 56def77b87f9c0be6fa79d52976625f060c6e3858b210fb72a82ed569d5e96b2 c2e59b5e646385224a7e7642ee3cff28e3043ea25237920b4eaee43d159b377e 5c97cca046ba9457ad7c1007b4fe050d155499cf7a8fec6cf12ea25aabda8f66 dd94bc676e43d7b01ebfc5adbeba11fb34340b942b9e192f6171ddfe501b7e87 6d1e995ebe50bd4d1f2cd9ab88e985311a0a6637a7aa7b390ddaa72ee3af02bd fd7da877ee9d36e1cef8f6ede184ba029e828ea9562a6adb4b277fd11482e26f a80eb4834232090f3dde34fea84780eb94009a4b9f0cbc179d074d377e51bf83 aeed805fd70b1cb0a01e76494d96c07c038fd5a3632425707378582b810fa92f 2c4d509ccd0d646aaaff34a3b5471a53687265e3703360a1c40d5fcf9fff0cf7 6685e32dc4011c90f0c16e1c60d6d0fd761a372cb4caeaa0e4b9d6b8107082b8 c0056c29ec6aa1d91bdfc6446e46c2c2191ced61aac796047f17b0ae8194a55b b40d597762e5c9ccd8cc034230abe7a65022f3bf447e02fe6ac5cb096d0861c9 7aea51bdf38d72c5cd982e5d360f4f360b965336d3641b2fc2d46b08526a6526 d42c2c15abcb811e69ef2b5ccd4841fb22dc7e9e30b2ed5a6c473c887e54c9c2 886abdb44416c9d6d01901886f639028138eecc3f492d6e684e38c9d6fbaf795 486c66114fdd7e688c5796c7b035eb8e5b46ada2d9645fbb6400d5bea63b4202 3041100d25e70ab6317a23ebb1a5877486300b4f0c850c0d06b01f7f351887ec 41ded15ba5471bda06972bef8b289ea41938df158b69f16dabe205c7f3c660ca 348f7da4a13a8909ef8a0ebb34418bcf78c384a5032e0806b01b55b943809f7c 75975ab8b8b041087f3978b876f57a4eab205939e6a5b97b2b5408deac941e2b 6495eed19b590fe2413c107db6b9674d0f19062c6e50b66d40a4749d12395118 618de5454c06accbbe39a2adf79dd2a220e544c6f1eaa072416c02ea129053d5 5c70eb68affa529603574aad33c9aa2eaa177d4c2c0050c37b8fe37f8de57274 60528440e957fff5b2df92f8f29541afdaa9b5f3be156d9e4196468ad83ea7c6 78d27cb54d8484c38ee2414806aafc2817f3e0ac41bfe2f0bc656a307dedf3a0 754ad4606300333ea68309a72af8e2bda16a5ce9a724afee3aa8c00e9f2bb253 2f8c2cf6f78998b7ab6d622a75e7ef594da389d41fc3be026c75643ab0ea5ef5 756a7b18cb85d7217b54ec63f21fde6524c610fdbf97381a39d28e67a3698867 729b775c84c83458bb8edbe6a6b4850276ba6d2c773e1b8f50bc4849c45b8d32 be8446cb0553866f692d9da970c1cf133fb985c636c5401415d2991836114faa 55be5bb652d0206e534170610f24345c8024bb6249c70376b59ad1c540fb57e9 44f1a4d77204ed76b1f52a4bb5b6701d74b060963508fff4e7bf77548c14e117 178f6aefadca48daea6f324db7ee845ecff0ad5235bd52e2fad461608c54a5e6 1417f0e49a9660e18720fb5728bd717156e6a0d01ddb4769b02023b2aa46cf21 8451aae431dc72b5ec1663eece440e91f66395aa072e0320d4dc367c86b9bae4 ae08120263907a46d076791ab4659fdbbe4ee78dd3b2dc13ebf58ac77b50f935 1c3736be9370e2276b2dadb6a1d9e7475447e51d251a9fa4d9f562f9c6d06ec7 eb07b2d04799ad0b2c5b67052a7adb6ec4b2727328430bd7af11b48729adeff9 daa8431c0f322310f524fab93801eef5f555d2b3eaed468932b8e9d99b4c12fb a9502f0345859133e95ea5c1088f0ea30dc77a59fd6dd876302f0f4403cd2ee9 881cce607c851c9c3af9be56ed5efeb810610190bd97a5e21cb29b2d99c35880 3e756aea2a78b6db924cf264b34995d9814920c680d768d3a588f5729b3e066b 6a064f166e48d3f5252eacea19c638f7c82d98f9cfb596fda236874b3c0b6ca3 18b4870e1c41af197870b467bc6d9c3637f7023b6072f3eec91b3ccbd91e482a ab5a825f8b3aa69bde3935f6b368974bf589ba0e7169cf008e43e2fca9d4d079 5045c147d2ce820ec92e3e0f242a8396b3ca87a7580980818380246f846a5d96 698ddac885ebf3a6779c42497eca8b7311052d698847392a1727d14009eb76a2 b67c19acf0c557909cc989a41cd7016e59bebb26dd69dc3dcfe14cb3f7758d33 3db9bf055459e8f65c80600e1d55c2049c5cada1832fbf7d28df560d98aaf2ae 6e9a328dd0d51cd41e7266024796d36b09c704df07c4e1a05a8afa77e725e6d3 eb4c727c362dea12b823d7e083b892ef253a7d4a47e5f2a063d80af7bb626a57 f935e42e8d15cbf966199c87518799db008d68f2cbdd776f71e1584e779764fb 276c5a136f0d601cb23f673381cb94352fa3fd3f17d418ec8311803529d1f5c4 c92fc337b78737465212db89ada40b0e3945bf72dc0ec6b7248392defb625440 719d06422431c811cc05f5fe4a44d1e78b2fc35d49684820e046ff6525863121 af051e0f78a3bf31b09844db1b015beca614903502a4dcd5cb182a77d719b72a f17096eb98eca84a9140777409608f668855c948feea38fbc7dd5b03494b6f9b c439e2c4f328eab3f16a2dbc8811806fdffd6f2a090203f2e4d6d69769c011f7 5d91fe161986a644b9834029424fea942738c38f3367b71cf2dfadd5929908c0 7f9c640d9ee198f29b878aa3ec222046cb4896175dd8adec8503dee054dabeb9 61fbd061f15c4b1a9dddcdbfad6f23c5864dffd69cba15a767bcd76ec4344d8b 2f6d03b695c63470dd198ecdc22e497c9aec4655cc4b56ab93290892729e095b 5866bf73b926457785303ab0d238f2a2ac7d65960283a5420ad9aa481aa3aae8 b5e5bf712c9c334d251d3f6e1d605cc77a27961a89782fdca114aa4ca4727990 985c78db864ae7c352d1772e069e308fb1bb5c0ccd84c0912a78324d7ce68e12 86f7ecaef27cee2ce9e751519bbf94e50d209cd51d1a5a43f2a0b61babcbb397 9e7f2cd1a3179299196f89230660b215355c5a9e6de9f014b1c6c459f09ac66a 9e29e28928b7637bba2bcc8dd474430a60c53543ab94ee0b7850322ed8e2121b 661f1f9c5c33ba9090f960f0ccef9f9267eec73de53018f84cdd126cba1f3d4b e279e2c7ac9d7016356757b2a0e3bf3987d07d91e6f9838be6fbcb4bcbe57d69 bf081d226416a70f885d3213d5c9b727c329aa6f28e3af1ac92136f56be78035 c1e23dc3037d3b70fbcebe2adf18a73333fe7782a407059b1d91bd533bc12388 7a47c8a805559795dfa2c73d5a87752335a1d456472e9133fea8f40f589c1ac3 3211f0978a16a3b589163fc1787099f22f98f76fd5a75a8e331138ee64c6cbb8 af0c2286de54908b55e2d6e2a8b3e6416875e97ede7d53eead15a4413891d8a4 8d3d65bb52cf50413169a284d41f3aad96e019a3dd2397e9e00833ea6ff986b4 70125ac9763d647c33be36d7215abcf3244ae1d1b5f17ebbbc8400ebccb381ac 6c101272b368dd7328eb5f04a0f644dadbf2f92d2f972b508b286ae8a453fe28 1cc7cb1f04fe18d893dcc632d62edcb69fdba849163efed71da6aea8f056359a 8d04551c0e670766a80c552215426cf0c90332b2592f50b84b95048385b7afe5 33d5d1c2ab7599da6acb7c0b998128fba233a9bb547859d61193105bb4726d71 765980b50cdf49f16883649c1510687265372a1e03055a6f2fb4acfd157e65ac 47c46f77f2e1253b1e5776aacf8373de6fe2ec8e62e96fd9cbaddc1bc5e5e5e5 bf49253e87a9adc917ccb83071c81201d7b914f41f3ca53b6480de67d8151362 fcaaee825f1c8c4c5e4015ae97947f599db355be8929bc9a144e2db453ee8205 5f2baf493fd8893e09bb06dd07e58eef4d1642e945e4efeba2fdaef0409546b0 b612d4bd57994892e2cd6aaf453e5180d85661a1ef9b178f7531b9b99c7424c4 e7285ff2f86e42362dd946c635ed3f88bbfb6420e2a147f2926b457b13d16e57 b8411d43b3d19b1662e160fc1d549f2f139556846903ff9e1d2e22c38aec7a6c 25c2b81e7ef19e900467c3cdcc044bffd56b83900cb1629caf2fd74b6cc05ff0 60ffebb8283a539cfbefdd3c721a4c9f34370617f98e31590be0438697a246e9 b7ff6f8a03b15b02a1111e67ed2f8bd13970008ffa4fda4630bdddf504070309 c9c433a869678c6adda26e7cadf5cbe7091a44c184aec389b8d33559f5c793dc e4cb03cd5fe163c8bee0f22bd93d4d604b965bb5125f0d637a2d531bf468b846 492cf9787bb24ba079c69f179566a4dfef6fa7f6b063fd880a0b3fa50ce7d761 47fd32b65edd4274db0f6a6eb324c99de51dbcea2d6960dae52d34b8592bc5ce 2f58dd0050af8b9b9c3248f969ab013af43ac9070cffa1c77f80098cf73c2f9e 9f06a9325889797f2ac3be70298adeb75534e165cb39a39627ee631dfe555e2a cbbedc42b4d56f8c4af5f2bd446e7053340c4995bec4d40f2799963ec062e348 e04a87bd9d07c2b67b52db8908bd2ca3c9b062eaa8b11df80d2c5fe0f5f2d601 7dc1f5338c63fdcca6a935a2da04a0f0baeb984d4539a1df81fbb79baa176378 fdd920dc465388e08560ff174191b493ce97891b888f3a451ae59c9314db9dff 8a25cf6513ede70bf4c5594d927e04bfb9c48c43f7a65c5c73ee9c34e664a294 7b780a216daaa053e91d91c6a4ddc2c75d7a535314f1f07aa18561768994d0b2 92a027b8b5a143dba20d5e0f5ffd43bba074dad028d268698631ffb9f09da66b dedeb8293a7099838bae80eabe5ecd081b016ad0d63849302eed21471bd315ce 3e2f3bd5e3ef44685f2423379dec6510e9f6d14a9d6882039888501e0966c4bd 0145be38e28b54ece8b6c00627e45131cca7abf3bfdbc58e6ca496975525ab09 56a3babf870725788bd05aef02debd57132cc0445df84c8b450235e8ba1cd4ed 3bf7e08289beb5ca431d681f5add4339f1496d8b5ce341678e9d3e2cf2ca870f3f52db9ba296b727bee818a26f8ca53c428ffa6f88c9428d2c076178f6ab450821e9c9ef49726f7f4a4a676712c9de91196a4a736e2af91a96aa754999fdc70ac8364c985a47adc06611a6b4888e97b3c68d0bea637d113dcdad613d63ce9c03e69d3f9393cf92dd3d307498cfc5c64b94a69170ed3805aa8baa72a1017f070c438d3b029cec4e0276e8c211c564679b4a2b7206f2d721974930a97f9dba890620c8db7d1286d2813f43ba52ba935eb3cbc5a52a97058be999f774e6512f7c0e1b25f41c1510b398dc29d4ffdf4de7447ecf9e6ada006b660d56521c00f113024090bff8dcc1b05a297d8633c33f89a5fb3865e02874dd9e77ba3a7d138bfa00a2e4b8b3927d35dd11ac6da2a236d658c9ca7e1fd5cefc7cf90d064dc9d1b203e219c7408044ea37822ccc989c0da975a55706759bf8260fd5ea7af53ada460000aa739c3f2f01adddc1a578d35dcd941318b710f5e234e2ae371e890a9e55057ca00ae5b9125cae3e0018b166ccdba9451d619041a969d348694475a3c7ff0c38cc7da2bf7b3e31f289a2e52f74a5dfb8086bb526ba6162ac8a9e2bee6e3e0f7946ceb2891b97a8cb37b689e4688bae7c771c871b8d8d821f9e5c43c2d3bb0ee9b583ea104fda8342282de6266b18bf3f6ca71faa52007478e340c14fcd510c408f4fc9d6c1e43944329c171133b981a60c2b1090b29c801f4b8c810f6d1605fae14bfb14c2ec91c8351db71523a82e0c4579a445a733ac3a72e4490527f60d19227a636bd28dcc4b6712b229fbb78891f096153e5258551282df8ec4f0620a83e53a198c6e891153114f1632d8fce38efb50bc76b36723edd02c6f70b4ba0840b7fc01119c1a2a9e5ac58288e28e453b436f1464aedf252f77862389544e077b4035d152d4fa18e4885abc20c45d28b4e885c3bed5c3dde7e793906ad58c0eb93a5eff8a8fe986a4c865e1926010361b9173476dc6bab8945e9e234ac95e01e1a2c55478e7e9f33e922b83df27bebbd4337cfb3be4401fcd72378c37851c074cc9bf3ca235846289feae7f2fe1a89502da4f0bb705aff1d6c8d8fb32db390236762c5db51045ee163790f333c343ae549e70a8c91045d9647be88460d5af004672a61f7c22bfb30d5bbb43e057d77052ca31fa34712817f0df9a1f9e667d004edc2dfa03871b0a0140ced73f6c2c052cb869fd9db328a9bd75cde31761a80e55477686ce8457846e90400c206231eb9e1b2942ab22eb2091c4b9d21dc7b10a5fc6a413aa8a432e996ce701aa4526c976761a345a1bf3c73639022957ec1b09db641bd28bd400627deec3f565f3bf487c4cd54e16aa2b5f17418cfa99dfcd0fc3dd0821d54eb9944ab68efa95b310dc202b9af900770032ee4c93349caf4b02f236b92fe120e143ac6eef76fff358220b6555937658d5f1bff743105b26120d52deed857a395ce2e596a3b2cd9f43c8e965a0b8625af2e9214623b5a66efc03c29f25e448ae8789780167938b4bc9cbcaba49a84ea29411d5683ac14ba99900f63aeb550951e630e2a41fcc409ea123315eb665695a9cb6a670b61de2919c02545c77a89ea2eb3755fa3cf7bdea6b96d67af7b3ab40a260b49af4c9228dc20b1a36eca9bec55622cbc9d06a31cf06302d1e665411dee147c22198b89b826c0172c33672d438edad8762eb86a86a9d73779c3902f34e01aa9cd7fc09111af20227366dc44451ee5f9996da793192ddd322b890dfb4d6bdfb7c389d6705bd150bc1c563e22aa99bcb61d4f59cff7b1a327b61d18914fa08db04860bee9c13bf0438fb9d50d99dd3f71d3f4fc10395aabbb5caa57f4eca212b402986cda123e30c012b7896a92f2421a57eabb2e6234129f591334c7c05f18a12b9fdd5609b4b0618c310a1aa665428b3c4c6afc1a0de6540f29a3aa61fb2b80f5767a789cf560b0b0a258e949019b22dc00fe624dbf7c12b4c917dad3b69fa370c3fc4013e310b8f8208bce4b1d322a7df5230c2462cd31bc43ffed858fa8ca7e3d08a43f72400cf97f08babcf4e34b2c522843884c53b5550d929258aeb5619c58c8473791d0dae3e341454854595c2316b5db034af4ae8393784108aeec9191ee387b4e76f058b2810727eb5e2072eb26c48c5ddb84b36d5713d8cf8495a0120f459ea41c20e076d9af81dafe4385a7e710f3102f028baaf547fe290dbefb2fefc702cbf2e0596f2d91fea8c4f8a82443b819d5a18377ce9575752989b8aa97f68fd84731505713b3c536bc69ba66b09caf47154c2943a4f66ee97db570e69187567a92c100e21af95b689c837c8761f8d226374a8083edab5d05158b282debbc14bdc076a0f0377aaf6b2cada6af53582469797f1fa1ced4eed4ed444cf01aa2811ec3734030a70fcafe67d4aa893c8c0cb7e2443a220369ec955b18c0c52657b48a363d8048b8ada4f939b468e72943301d956d6ea4cf0db24bca3fb09ec5ee91a8f466f08f33704f17465f3885703726fd293d40780971ae40b68c11ee604f23d9bf86a042aa88b723a5ab778b38b327f00548bd1d4499a9c53338fc8f24a70dc75c85d0409b6207e6d0de4a1a65cc7f2a9636fc135e0ea02967a8e4e821e17619652c20b1910eb9e415cc38b0d1384688e46820fc038229c8dac006815b8d9d82c983b0f2746936700ae5f3ab1ff280639f63f933543b5c510d8f1b367824129f0d6db0c0c89e951311d6014a653b136b4fbe7cdf8bce311f257d310e7d9b8f841d1d20e2748621d1891b637d22c879c985982dac37c1a7f5584fa79489dfa42e430e8045fd43fba05220d47df03c38ec3544f341fbe9f736b2dc3bf610cf9223ce7140e463bea322e4a7ec887685ceca4536c72145aee7fe712b9e1a9425339bddfbf0c7ce15cc1d1a4696485306be99d34f8e62108725b35681809c201e011fa9d5401af7f0da6a7076c28b268359ff7b6368634e3c7ff9d471c2ca68e7c61675a77005292f61fbbabece4e4fd14284067fe8d2929ef6848b9a6672c44598a9f0e4f0862f9744202375b932909dbad831566f57682205f6274cb2d9ddb20cdf9a78e063ac24cab8502a238364527fe0f5c1076d7e06844026563d637863af1e10b03061b224a01275854a9a73a00eddd38f2ee8ee8aaaaf7f15818a281e9f6a21cce0a3d43abf31c8bb3e110b45dbbd232c161365e02cc22a234936d8cf9c373acfa014d6ea5e81e565fefa465df27fe95a78a88b9e3b79b8aabaaa902654594d89608b529366af7356231185c91cead2e68f1248f058b8d00dc48d99e16a77b3be8046efe0ddc393c6dcfffe17c0ed655bc0aadd86add8c1374fbb79cdd7375ff1700c990f2c2b38af14e2802c527da616fd9c111e4c1f902040f8420b91d9dc9220b2d1d6384fa1fcc2c9e61264830b88b0e90ded0c39a84bb7b51158b35c03dc10a67a9eed191d1dc35f3942a259b0e1bf168ef5e50107342785fd1b47a22ccf8037a88da003eb889b52a3ac90996a3a4b711cadf8ab13825703e8fc2ce70b2d70dff64a024fcf1c0a5bd64086765d047da519f48bbf4814bfbd3faafdae32cbd0f8befc44089fa6cf8165584f8e53403333b88d5d5741ede6bbddd4367bec729068390fedcf6705f9453a6b637a10598a7ff4a16485ecdba18c34069e7ceb845060b284e2563a22e9bc275f6cb50a7df2254f0b6153b6ea6382198f8819c4f42025bb625b2509035cac8af075a88a861a10af2d92fa451d267d8c13f178cdad6092ebbe42e4e50a2a72c35fdf8cdcdd342f6424d7e9cd3ed2f0e0093020737b300361e706fe3747fe8269ab346005636f6646b6352962a458a3ce0222ed6535204d2e9d44b0ba1baf0499b6469e6697767eeaddf61b196eafbc0b29706e2515c0b534f9a3947714f4b6525b06880fcb81929830c10f6ac4d1d2457200fd7351c0b130b6fe53b3264a77e4f5d959dbb59619227c24fa6d37ee78ea72b258495030692262f5d33465da66d6a9e7905ccaba4e2a64b1986e6622641227e98590422071c5a597a1a1cfaef73ae10c03a12a501e34a5bb0c056cb541b78d6ee9d2d87057f5a914b09b0e1743789c6c21a6a3659083e1314fd08591152c1ec3530e4ff0f55bc6f3d9429f47f950076dbc4a4bb5b82563c036b0f1512b7f94db74fa1020dfd9ca1be2f2e3500ea0c3277eccfc9af2034ca57c3a76e9a7fe04dfe9610dd0d6c4e52aa8fd48cca8018e66cd45ee24b56d84ef3edcd8d14f78f835001e92d02a638b784c5a4819465d632589c01ba97bef1f3e5b2c28bcb72ac0a962a48f70925cdb445cccdf887347fd7851bc5be9a0c00914c12ca56fb8e69057552650d0428e4dfc694beae1a7e1c7cc294825d4da0e077b5a5e6b9e25c495dc602373e085c874da5079368d1016f1c13fb466ed79e45a96dacd549594bee8687221461021399058c4970a3e432ff56c8375181062a41b28f483e2a43406504cb93de3d038f5f9ae2bb6fd420099a60c5726adbcac553568254b391bc35e95f9079592306283cfa9fc2697f7fba9d4e493ce509bf9b930901233bcafbe8e79c5199e8650ae6236825df5d3a6365f2ea7c2481758df865aa069b3e2d94cd8ce4c0fade3c04c17c008b37b51b97cd8fd8b8d08f2944a443a34cca8dd66c27902b6c0bb03e09363b2acfde3bb8f5274bf57b03c1b5766fb6598ee6a4e98c583760264d7f140ad330e408f75c76c064ab68b81960b5b67805b71a4b075c4c47fc87163c061b00eddd1065213ac1eba09330c07daba241dc37024ff8f6e12e426a97a53c0ed30687e6c1a1558e27f03637580316265a0b998daa5edf5b99895e065c1b9647920f99542f723d2c1e3f0ceaa5b7783e492eae3311fd04a4a7f6295b0436a9ca9f06d478dc754b56629b88d756527bba358ff2cfdde2ce98a6535fb7e2542e8dc103db21cc05fa1c31f3cecccf16386769b3cf741f1cb974ff0f5900fa117f272b0132ae286fe18877f98cb820056797a73e780aa101a3bcf333c4226b01d1983e0e3e8401e2fd594f7975a3cddc673b6146c0274e598cc753b6cd89c079f1ef950f3434c0cb78a67e4c315dac5484624f2b26a58c1d3456a5cb77b79d80fba63b08dff6962488c456584fb3c430bf8644b715cabd707465cb6ae2cf85cdb9caa80870bc9c915b43382198d5e3f23c85cd42d0da4bf54c6c067c0e6d35efd14b040b7f4d3ea5f404b58bf91b2952fdd83b7726a897bd6c431c3f8e12052ae22ea40a7932b41c1dbf956d1d9262f196fae8ea12cbfdb0833cec974a14f954ca4bc507d87a3d10531ad0d9b6ca7379a91e95c285813a983a25120bb61180cbed53910227a7ea0f6c5c175b9198eaf2041c26ae27c54b40cc288650b9389a017cf8a50117367e01a8932b63f4808e153e7f530c50e6535117a48450939f3437f046800978be3f364ef99c212802fdc866d829df1c7d8066e686dd80e01f568492018a027ccf60c8f9234de49548b217277ee30e0772f37d32e5e9ce441f4f4d149430058c8bffa6563f51e8a3e80414a1a0f67a6312ee285f1f9011afaa9b380fa3d70864a7085cac322df9d87b26fc634c46e78c529cf2d8e2a058a10177d0a7a0fc041fa8ed5d68997761071b653da05b36b3cd056aa2d1be3f6fd74b9638f77fea00e0e20cb62ced28446f4197d15d20a63c332d75e7e47b6fa6652bd03cdca5a50e674f596d491400c35230269e16a796eb652daaf7ee938026ed2709f7df3cc70220bd06776fb86b9549e3fc6f91e0f9fe22cefbd8b181126382329f7195275f0ea3124c144c6d0743d9c69d68bf12693daf517a6c5006dc70659e9cb6053f25082023e5089b215c423907c43efb282105db94f95409bab9e2daa287741bf73e07fa18dabdb126af656cf2abe58d7d4347f7eb53ee335c71160189ef7098635e0413bc0af0f06746ae7fa94a8843cf9a246c346500d1efbe728822ab06d49de6093420a69f9c89a42a3d1a66b3d870a3d9cf1ef3887047426602b0777d847ab2059a62d72c832f5008ca5358752cdf83860bc972652f4a39d3f58074df745a420deb53e94cdcfafe794e77df270a05e90ad6712ecd0952b98d0fc5c04b4d42da0673236762a5a53a18eb5c78da44cd6a6d17e21a699eca52631fb09cf00cbf4108362981959477968ff2a8a492aea24c82102f2bf4996a33c297f784ab1f93c1003545213b6c32bac6968af83e5df1f280c4093cc49a8f4105ce2f57ff1e8ea80d91cbc882504ba080caabbb0653a1c29cd670e9f7a7998726871549a903908d02fa465831aa9e36a75c2099f65cc4ebde0788371baf35054fb80e7b6c8805b80e1133bdb22d614e210f9e2427ef025df0e7361a8aec9dfe381c5b351fd0fea00fe4a5def645b2e65443f5324c9d6f31fb3908f9616e9addf7a4e720cf4fbe2d0113f3bc0f0b8a05f5ca03a57efd3214c06b5a021f28e5467438ed8dc383e9ad01a75f33430630326fb552a041f0936a3808e99c72f18698de187f21747d4bce097a3736af7eea13679aeafbb61cea4446531fc011bae4313d2c4807c77a3a0307c89c20813d6939ae0774e4213afac54eafe7a1d192062b902899ee16cb5698092612508caa14cfaa72f85bd443b8c4fb43a37e0c2ceac4d640331e5e9fd4f60f5c410c26d7bb2bf2db6f776645b7a867cf18e37e9d03fab7cff74cf461be9b00b417547ba0368c497139c65de10cc9e09701b4460a20830c7b06f6013a4a530d903f1ebae79a059341826e696ba7d8d979c883c255d269b69a5e75b66154e303d34af0fcfc57212ca9b6d8582b148f6ce8062d3cc4eaccb7d1a5ae98aedf8307306b51b8f9cb5f7aaf4c4b27a63677f497fac65fa049aa856a7eaba520d76c09b37c629e254137e9dbe10db445d7fc9e1a20ec9ea73a4045a55da1c0d939970ec146e38cad95a5e6bb6e8855ecad1fbe65b2c3b2abeff02f52e2544525611c0d7c375559cd0491d12140520dbe24263fc5756d7ecee21b7fe7351d9a41baa70c2132cfff19cab674797ccabe2fffb03c3893308ad7c74995275675cbc441e40544cbf6c70172fee3ce115ee27c30281de5264e9bf4f3fee91b35af45f4eb080e5744fe53d448222957bf013044821cd008d7e9c3cdd30993e9a0c5e9b47d100d65ae60d687779dbdbdff5672f808d24626ef3abb5ca3236abf4d5bf44d9b190219ae329859e39ec99555af51be209f22eff843bd6bb141788522bfde6276cd0106f786872b79afd140d15b9f5e0077c2e27608d8a3dc91f23b7a38f4ae54750c6d9facf4b754d4f71a90d004a02214401e0a0f932b66bd1b9a800a50cc4c870fe5e6f6562949ba4972a99f1556606c2e731b65164584245ce0c1b321bd9edc0783ef3cf00ec9994f1b77ff911bf1d048722a177f2419ba3016f7d26f12941b007fe5da4162b7729e0ec39d39d8cbd6a0ddb2a1336029a834fca7b21fa7cb51057e28b3fce915bc6c3f10aa73531a6e117db8d2e7f432ebebda458a7cda58aa0516e679af69435b8cfa2d25220948eb57970fc6ea1664110accfc81a79b98a401832e03d589992679140b87d9f56906a5df8092da3ef1aadf3af6de87552b1006874e02a63c4befdefb631bd4fb490304f1d0db014cce6895e93a83b86565c00e3a01b39275fff5651f06d137f5530d5423a19d6541509634d05ac3db3518e20c9a77949625e201df1ef317220f137b627397d69806bb68eb496b1c4f4580ab01dd111cd59de5f93918bcc302bde5deb2c1d2e6d2d3c4ced78c9735d0fbccc80517e4a75108c797afcce407dffefeabbde80fd70313771f91b54c0cda90874b0ce1a86d51a5cd7c469675cc71010533a338b7620056116c4450810d9d67c58e02ff06a78b58fe44d1f6b9819d48f56e14ef924bd9f660814a57957f34cb2d58038b804d343b880e4beaa4529082ec56b08334123f229f876972c92bedc1504203677799d7b063ab728965b7c3ceda36cd5fcd16f51e030247155a3229a6e15e0b1306d1e3919399389c10e19120aae49f5598ffb505f9e4442db746b067e67008439144beefb1d1a687714283a9e1ea2fa26365f0e858c6999460708f6789c201a549e6cfd7327a642d72b48a23c18d5e3e8160d5cf78c4a373d795408e8556093c0dbfec80a76a5bc0bd7f06459694767734c47e5bde62c0c2efe9c1e007fe0ee88a277f7949fea8b989c4f60360262ded9b9a739b28b6a30f63f6768696fc0a3cb19bb8c57f520a62ee13476c9a975dedad480033efed154b037e4426ecf1004a3203aa8b631a4658c32267f0e47b600f0fd04a834fc1e7daec465d00b1be06251156c8747d407e851aeee3bb8876a20345c51e7a030bd7c88ba4f5eccee80c372ece2e5ae0533d7a7d36697c1d68321b542d5f9c03ab14a79927188871a60c047a44315d084089ade9616b8a9e690ab9a002c3f3d3dd4f4b3dc9dfecb33508567ff9062a25e5b14272ad76d49838b035714a59524f631402666bd7458c340c6582a86c5c596272ef2c90e09c3e78f4c78e38a6deaf3bedd5a462c2a79dcc08cbfba93ca1c2f99c9f1e7170189ef2ce81de294d321a98fa5995d0c6f4891d0bc6b335b48b7e4f8cda33f55e9f17ee8c842665746ec88e71537c8b7581c4bb02f7c2a6941ac5db1ed9419866c9271bd77c6d626f7a1d32289859a8103ab1d5090998a08c7756f4ee84807a45d2c3a6a51cd8c01604bc600fcaf6a7d752ae10064f8bd3bd2b537469ade7cee2ea0af057c3ab138662e892c77d279ba482c5b806db9d674c4236d103aaba0ed84d775c697ef12b6f46ee56eab8d9ea00e21ecf0bfdc7f180d9f3d55b56dc7d2f388a54d38827a9e1d11cd6d4141c1a1e3374d205112f5ff0280c2ad91253040b1c7e1dccd5c600a0110f064a45c48af38767680693d9e64d036d75a68607f0c7cdd2723a1ab0edc5eb53c318fd524f6432c4ff088f5e6ad692d697fb10eed10fa0709750b18b3eb33073f86eb46ecb4337077b0442529213ee94fd59568efe0e22940a923656a654c48b137b120fb19d5fac81094decdbb3cb3a084c13017782b9762a7d8d39ea6a82c0de8a7317a8072b605b07faea7774a2ac9f634c27e2a6326d3047b4a697e7293eb8463f36fbc69cc1440725ac750196ab11523f206507b29bc7f20b17718d3271c9d57cd0d06b7dd60a00132dae793261a32f438ecda9e1ce653d4739d971e604942802eee20afef0a9008fdffd3abec52df552d3503a806a0e8dbd936fce7205adab9fa933cb76632b00ca668a38289d1d21ca817814e0cb6dfa6ff8e436d061cf305cd563a0f19b3604682fc0e0452100c8d190832cb2db07b4f703b913d282d08b5fd07164c041be00a65a3a6f6ca407bd211a6602b8b284747c3143e5778531fcf7d480e2461a2f0c7af908336cdd817f3c538acd2274ae3d155f8e85cbe86c3b32001db24f6a490d6389ad820fc1d290f687a13a1533ff1d155a7a51aa64f4870931f792b74a220753804abb5fcc8851d9ece737573f76fc9ad39ad3554acfd18d971db4c06e190dc0cefbfefb4b35fcc620fd29ca6b47690803ff8e6fd1458a8eacf60639fcb005f6361c772d73a9411725a1bd2f2bedebd2179c21f30224b5bf9b30b7ffd6350497f6ce201adf6c260c599c01a506265334a6c2480f765605dea20a5f32c8ba04f9f879bcc1e48aa1f4c745796d1cdbb8c77f8116c10c546668db3631d29b910ee45088e4a7cd397a46931e9d2b7bde5288673afd79e64b89f805f100361baa0ceb68ded94cbe062c7c5f7af6ceeecde9a8f2fe85c14a23855ca57d68ab6e97099a2e31c2b8141ca99aaff4409f5f56f1d2f9d6000bcc791d74b55ce9388c52039d82ec32be2639a0b950c2d5a0e5ffcd8bc04d36c3b226b4dfe29ad9da37b3030e6c9e0f2ff9617881d73b2a80db5396c7411f4ac82918b4e15986d4cb6c430bb6f58b473993a2eca4a151b57be8a36f65d4813a139509688074892a54aef901bb3c7b79e8a4272b6075bd1550347cfd48702e5271095326782199089a2ea10a0ea0aa9d82c4769d6e7826e6a462b1ebbe1c2684e6736d8350ed45d1a5f92d091d81859c985c0657ba47cb6508fa544750d62ae4f7f4477b049770bc9f651206ff93071accc81212b1af613593f6e8b7723e9e1eec62296c3de05c5d7d612502eb42570d23aed77e46e47cc0f45d3b7e551adf6cf5cbf2e7fd02565d982100087c6ae17f315c44e8c92e15a857f2eddb4c69c5fe654178a60a96770fd002d10c57fe09512ce9d06498c7d40b16bea1b25c38a9e2ebc82f8e3d53bebd9589bc088556c0715dcb167aa83d19a1c1e2e8dd169cb2c7efd5b83dc340e0829b8c09053b1d40985c1ef909aa2179567b1f010a26112649bb7e1f7ebedbee76c7c1f606107a909f8c85caaef0170877e367eeecc3d9aec5bb55385fb649fa534a797d0c07dfa6e294443bc822646aed3b2ce80f4d2aa67549de9b92bd684bb6ff8ce40ec6163cc6cd91c27d15b9210cbc5a280a7693b37aadb17ad85af1b69d106da60dde45149398e18e493b8638ff8bddd4f34a525302e57a516a94e5ee9e8d7d7a0f2299a26c10e32e59f1b21da69d21182744bc2aa3ecf761bf5159ec4f599ee700fba8365afb0cd4fad3c5020a61fc88f756ad8da83a11a106d5ab70160a0fa201145d0497c4e73acf0b30a91f98cc7ec3eaffe8b6d6812591e1e255c5674b5c07727ef8788dcaff511a0160455faade2a751d0f768ddd1a0f2136669bce4ab70083bfe6c2023f198a0a04021455cb69a0ce7dd71486bc77bdcb3e59734725960718b447b2c4466052a7e11d59a35beb5ca7a843c296af3c06379d8cbbfb46450f0510bb8d377fa0bbb1673d75f61e8acbe1e53e9699d66fac85598562abdd900242075de95618b13d3658ff88ed68ebdd696371ec197d0185ee375f599a04280c63d53c48b7d86c22c6ed8178b04f18905c289a02a5cafc0b380fb97fa07e7d0ed36e469b713dbcca8f8f488debd2c3026eeddaf255d978189d4d4525fcf3d0038d96082264d7df7d14005c70b4d767c9ef599e127ea492a053763e59b6043408f92c28a40746d54ee58f46e21fcb74d425dc113e749e20aef10ceba44c7b050f4dd035d6ac952e78bdd8c462e163b4cdc6be5d3847d12d281aae090763b0aa0b2bc13ad765435eafd2107d8ffdb864196e331155f9db0878ddee747a47e0600b77bd69fc5fc3f12657fdbaa613e1acb6b91a0f6db838c4df23ea4bb494b5550793e4135110d2021b0329dfbfbbeb915ad643f8f12083df6e8f3f061f7faa1b0843529e38c6f98a89ef5ee061cded2c9926839ea52f7391b767cf08e4af4bfa06dfd19be30662f5c2a443bde9569216016ba38138d3bf10ab960c2f47a0f8f10f81535b49a8e9932c2d8c06e22a9e103b6b5c0cd243ab864b52f4e2ded79c040eeb6221d7069514c22d4fd7b58ba9d2854b5ded0caf538b6e364d5f3aacb37d006fe5176a4cf434323282da3b2de40c1a1b49ad8614f8d393c20ba872abac310954a68e549f5e61167015f6a5c80b76f9e4de2429566dfe11d366bff6b7ea340e2b7927dafa02552c46473150cc17e6a601deeb424caa6dbd31d5d337e1c1e70b55c67bd9967b5da0fb8122a1dc79922229e0e7eed8b64f72c7dc6a0cad737001c7b714947c03e7c73b851a5e64d6733f183db8c8f5f9ef0a92bf5a52ec540909dd4d495a09bb04f8c89e11d6fd90c7351dcdb2c5edba228b207bfc6c30571d08c4a78ee277fdbc76866ab091298d82e48beccf3d205caeb9d6227c8be88cfa035d732bd6a885ab8c8ec222002f303511d3560615330887f26645d237f5ac8709342df08e53e03223c5fbe07ece791c3f2df73f47d5bc39e73c3b5bb4cce86b033628b191832c7e98c5a8427384a261cc362f10117705f51a0f0cd00bd352800224dea1cf7af6da04266c11a01d8143173e1dd582a8a52ca2c5b5c4244f289302a9f2dd55490358e872417e5a701c4db1c7d641a3aeee47ea654ab74d625fa9090f5ce59ec92d59d79868c324b9c5d4356330c186948d78c439f4407ff9e1be066408cb50390185af8e55ca9a341d4a5cb0128f2e89a412783c80d590ade60d0731643840b57c74010424b88eb3b55e2f6e1b6b3e489b138627d9e872166a1b0159f19698932f13c51c777ef0685c0c44dd289ab71b0bfaa6cb4f176e230b360815d7812eb1d9a9fc8e69010b4efa624c53932c6ebeb9e867847d3ff4bf6df50e452ac988e53b73abb5269477224423b3e2026673bdf5d6bfdd8bf53005bfa20df6690f30d7376f06db3b61467c4341a52b9bdce9ad0cfee5725cc1c5a990730ab0802effad1c22092546facc96eb3375c45ee8cb502fd85b0fcfb33474304d02d43e091f7fc96a538c799fb35231e141c81361617b40142abfe3bdfbac3ba309db87c86ce09316e9b30929751d1e144d1cd4b577f4843a2ef7398824cd135700f14f8487543e552801284ac0884ebd84aacf05c4b0e002892e705650888bae0c6109ea2b28463585caf9a0c85e8ff6a50a4eae459b75e9e43beafd97bf362204521052a2995fce8e5f047b0db32d99e3da653f39616ab3d2cc01d0b69830920570f5c3bfdd7411d1989f250043eabf69dd46b05001bfcdc344e819d637f7ec038aea948f197f27b9ff764cf3db5590a47a1fee4c9fe37237a93640456da9930fa0fe87c483546b403c261efdf8602a125852b5959afd5569909a5c1d506891081dc923d23f15f125570288287cef161ac124eac0651539b9757d279c3499360299a3e6c92a846ef5332c5c9ee18ff7a72c94d0c86117aede903bbe5d4171030797d0f97abdf8a579e68642305392b9b32903823490b337b4d665fbf0e8a73909808518960308c8f414f9bc284f6220f3b43f07e2eda53a78a864790166c14203aff4b45b0b3665ba59843a504254650a8ec6112efe131931db2cbc83fca30e0a03fc2173622417b4ad08145ed835ab9ed64a77550f20419282b3d98f4d805008ee533dc10f302079c1a44eb9fca5ba28a247d1ed45d5b0a682b87496f925010c040c0619149884bcb30e540b78aabf4aaa670c94d4c2a3989be05aad669a3b042f5a903350ff9db50bb791bd7b22c9d10b929fa224f4d2d971bd9f98e8da520f17d0fa700faca64115f7bda12fdaced172c0a5ca6f0d3c3d9498ebe0feab1808801d3fc024d2551de3ca2e3ebc61e9d0bb3468355fbdc59d18b13bbc95053403254c8a720719be443d2f5e6789ecb21c467787040a49e7d26fe8819f95d67e0577d8be0e483e940d9b5e68e06b70deb79cc3411bc98721e1ce5bdf377da7390fd74b49b4c38a7b991bc6ac259037f92afe82e06b20ff7abbd90cffb02c58a0065de8792cd3c77450f744495944ceac7dcb1dc9abcd2dec4955b816307c116406f7c9524519e6891dece25a375636edb74055ae3290e1ed8ff3d98e6091ab3307a805ce7b753ac9d9a626526e6f0b7932adfab2cad0e1acbf04877582ec935b01f77800e27cd34d59206704e758eadd4e430f04a82264b0752eb778d448ae98054f24d7d98543a79e4b96a36eea021a44d2e35aa1ee96f5655eb5dd1a5076440b7e1b7529fb8a63d3ace684594707cebc30a6d2356c360131aa9d1ad7cfe971078f69a2fe9a63b4a383efdb398087559e66f94aba2aa9bf283472dca9abd0580a9a4f7ff1f85cc2701f8b8a57a6cee0f8d8356a2d063583c2a46d2ed1bcb4930f10f43326d88e9c7fded61b8f874c3db5e0ce74988d1b0c878143f2dde201a70ad447ba53d27516c3f069edae78e21b82d59abbc34b9acd5652beb21e089c67033bbe50ec6ac6cdbbe12ffbdc42932075de680f3cc2ae18e98808d7c083f69e0448e747d8f36b2887c14cc6072e4540b55f05f0fac2b7f794a6040a43ff7a160fbef5998845e31fca23e5bfb22c6fcee3caed163a80fbd5b893b285d2fb6a910f74cb7f2333b6617ce487af4b87a07dfe2db5308446dc2d36a6de2d32fdd1ad0b3cf68e21601ac3bf23fd5effa00cfc2c92787b8edee4b629816626019dadd20e23f9a2dcd0bf9b0feb1abd9cca1583fa3e38204bde0c958cc3528ded8ac7de093f9cf3544ba4dce3a943bb1213b3115b64293fd618deb01aaa7b90ae3dc5f60fcb30a81c0ed0f06937c83a3df75eca6877f0c488db64d1878d32087ed3ef7603ee930700da158569871bc2001242e385703f3cba03c4c966b5f06a5fd398660077200267a4d2fc92b4bc59fa8b05109f9556a964ee01eab2b4269c5e1af1ca04483ea0679231c848370fd0128329b9b0c2fe326addaa21c83be6489d4bc1ac00b41be91efa23ecb150877e15d677d8e469a4dc77a0b5f7cb58565518d1c5640923b94a28ca5c27a3d09e3c9b999913d356327d0fb065ad9b1f1f8e27587875086acad4e603a751a1c697c9a64f8255b27f3ce240930e64ffd071508fa76ee40ed08fd2dd01158c6727a5f3c17143b4f5fa40136d1827c5841de14fe037d38b0181bee1a6029ac3c1416323cabbc3e0a95b85ca7ec8216d3e1f45fc210aada9077c283e37aea921660d721e8e036f4b157016f7ff4c40aae7e0b7a11f4a9e610c5852fcb1d06b000405153fd341fe4f6b0a0c0b7bd5780496ac6b542c440b040ef15d1c1396290e2e9d54609592367511b6077a3427b2561d6af598ed2bd4eb0328232eb644c490c42ff1a245b2b47e71b0685d49f2af0b65cc9f73bb3c85200b711d36af06633265f726afa085d1f661fdf5d2f7924a7d264ad5ec8726b40f020d83b05c65e9b6c5bd455fd0dba69a512d526ca4449cfb36f0eabe174dff180fad3dc10dbbc5a13dda0c46b94c09f4a1b6c47583b72e5e45d282417ba8d23d00bdc200742e410167105563a59c08cd6299eeb74d18e4db32d7dd87cc9f09e20d635dfa6086b2959b60fc9e161b2025d832b788feda1176af5a0bc86bfc9cb6081e1d0ee48b45d95e1467adcf3d5fadb9ffc7ee049066909dadabae16fdaf920c0220fac965a3fa34576f43a5307d55b3949d6d4b34952775bf71ff4e075ab70fa38c63370c35d5c044b13e32c07e5c3db37be9f9279ff87b08b472d2262ebf0a12bda748361624edf05dcea3a02d81e522929183d2cedc3534c8f8f822bad904d95514645249676c427a4ca8fcc2c871304fbd7d28ba92f5014d7338f1571807bac768dab0709357d3da3c59a4a6faf06387cb4dce86ca2429cfb4b3bdfb4404110e382e1c97c91fb1abb519ba4eff23fcc2414abde45ed04d25a63734fa5d000e31628fd4593dd24f2c5dab9b487ef64abe41ec9c3ffb2386e049d864b9580c7fefcc65e8d2103694dc899ed6f7766a033c4d4d2849215c2bbb01f13ba6760b515f067998c8de1ee8df50e604b13483774eb023fe8b004c96cba3e85ce54d0b80858066f2bc0649f25b86a459c7b85d4d8f1bec43a772ccd28b068febf827094385e2b94e0d001925449676bc1899ae463f9c815716d74103203524a896f5067ee0d441641ba8464bc19c1a9aa549ad1fcf6fe85313d6bec97be9b642dd6b058ee7187e4d6c951712965a0aff81701b73f7b86d6c573b6c7bbc334a98aa7b0146ea57ba87550ed6e17577d32030868b5508a1011b508696cb16a29953238d092b7d17dc6b1d1c6c202899535ae91e30735c312c84aea93719c74aae4168a20ef425caf163039e7dd2a22a7774b6f0b71eda2e1ee98dcda2a420ea73f81f160848ca9568944bfe69b5f891a41c736a7bce9e327e3bd94ee8ac532d118c8f0204ade2285a08a36de6815e2b652eb1f770e08d2476bf5afd2b61270b406a54270b47d2ed113b52bbf4a558337bc777f63567c95b184eb83fa09f73a7a994132a0377e569249c794eae99ab0c8726cdc4c8ccc655f4802b38de61d14f3a75f4a20c6e25d919fd08f4a8d3a491db586bd2a267d363669c93bf6427c4550492b60f057aa02477cf20a5f21fcc7dff035ca63e134ad0e0f0e0e5c4878166c6ab204a0a79b120b106e7bdb228b5b0a8fc84e89b1d88e1e8a5812ddc7075476899363c0c60842c01eb7f75f2e2ee6c100a3d0d431d14fe2e8ac9a39a1ee48c65dbf225038143623625a9c06d99a3a1dbf1c9e94b60bcef697088f2ca8126a939d926490792547386d0053e34ba42c9d43fcec593bd879bf857666a7473ad29e5dc7c9c0146a561cbf612cf9b7d8d061aac87c0b68582ade4816b5da4b02de5e242984b0cfaceb082b3437f254e5748daafcc5d53b39039ea6638cca039ba7294faf5400bbd0f326740fc71755986c7964f54c4722a69508400f89494aa0754acaefa2200163b42a71d87454a0c9c9e224971ab4a7f0cf45d998715439566398ae1964d0b9a58af94af2b2e80734249fb36378ec00337c1be82163f9239f5a6a34b85f7003fed671cf50ebbcff41bbe6f9fdd6d02d451c02623adf37b4161601d8711640c2c5ae444c99d89bfbd3137ebd073e50c6fa79dc9ef39395b628b445f5c38a1004b196f5e8634f78db3a70a40b5bc4d08ace83bda4af8692567b4e346e7f8aa0962e36abd80adb6f2792aa2cea06bee39d17191033dec446b737bbec5c9a0580871fb3172a697bba3d041442542568ce649ae191ab33dd4b553691c6f51551009e9fee145cfc0e99aed66a7cb7ac5b24d91f4b3bd21c9031cd012857db2660e0f46a1cffc39f98008af4fe1e0eb52423dcd0553c149bfae7a60655377a23c6e050f459499c9ebee4304f4fd877cb675a12a9a0813defc5e6a09df93d89c08fb0160954081d196b5f0026d9f5e8d625c36ffb1cf9b79c1a257f8548f8dea0c5f083defc48788dee33667b2d650a13de0aabb67c2c73940b740a976b742e98c6c0b9554cdac22b19c4457097efb47ba72fc9c9e826383659e65605d311763820f0b60cd9330d77457a3f0cd8cf7df8ef123c7f60d8d0d121e0dfeb185577b251a0dbc916a97ac3281ad3b0ccfe497dd82c99e870f49b831988a886c1f5ca4141c03e63fe7ee76b5d1286a4daeb2615c88a391b53380f1a5bf1d95e04f22177a83070dd795989af041a670c4dc52c0da4001e09cf340536b96b7711eecbba5c8430f402e258a704325fd0f1e600ec440be0ff7d1838d09c1a2ffed3b457cdadb2f03dadb6d3c8cbd428acaa5e6001e6ea872cf5cde5938141b675c086209f96614074be21a64951857c339b9ecf416eaa8fe8d473d9d9b78bd85670c8e10f2ba5f0759107b57acd33c7405ca1cb1cb1723ffc117e5ba0cd141d5d26a631f79b7210b7ad52ddef3969b696e22133cf3ef2d50e58bc48474dafbf66fa6eafc46e02b070c40ea4790246abb63cc89083ad713c3f0350c7cabff4dd3dfa4754e8c2a5205ee305fdd29f3f5dd7470deb537ada7ea7deaa649e244bef5a8f3b38b304bb006780e83f66dc2bc1423b57ea36843e8dfa6b7e86a2769679018c22d2f9cf6e20649eba72b5ca058582500bc8730e681e202a4aa3b029dabbfd6f435da881b7600fa82a91304805aa83e572f4d8717310c1cb4e9f7f51a5223740acf77dd66f804fcfba94bc5c14dd91a6b73cc4fa10339589a6eca38732b38d9778e446576370f01d9c88115395903008fc03528f2b44398fb4ceefbd9d9414796ced8bbee9405317210e5489eadbaed5c8038cab5b1a8d0ad2a0dd55c8a8750fc2ff2052aaa0e13b5a0335d7fb9146c5edc543c321fc5b5bb0393e48506c82b839765f1bf2503eb0aafa46d22097c10c0b272883b5a1aef32b058b3973a2b643640445cb997048b0ec9fd79cb99dfb344d6c031947473876a00fdf7e734d51c8abf72e49320071a6ad6ff22fbb906eda52fa70084e68bcc4d6e1d3dc2dd5993f0594db87c3e06e383fa9fab4a960caf3ad3b38ca6db2ff1e0d0409b3ceef13fceb699acb8b801fad2c1f950c24225c4a9f6f06f4be8a9128239609b9576052d3f357e40975108f54c41adeba889850c290c4e7d44cfb953053c7f71b793526ac891931798200b25e1154617219644c2c0ad0dd4a29bfa991956f38e461c9bda8ff18699e32d0987223e9d5c5688310a464c684fccff97e3c0f9515f64890f05ac572154041804cd4fca52563118dade866408a8ad7139576e74873ba6ca72deed6313e818ff044941933bd6f4ae53706dbccab257bf0c1e49a169f147edf1e1524f4ac9ca640b83504fc809b1556ed3d24e4d70c081fc77ffa93ba7375980fbe6fbf4d4af8508fac5a51ccea6c9c76f3687310380428c04e553be6c5ab941ac8ac677e1a7d1035c350dadefd2f1639ef79b88451e856f3e0c005fab477ab21cfca271faf07d08c95d7d485007441aada5f7a9750c2a8f8f0979e60955152fdb3701196be0420b3c94ddde2c5324b60ee5eb95532df7c299817c57acdbca0bf1a10c98b3dc140cc5e981d097122da7d1bdcf2636cdea0908173b6a629ea6a37da7549a6a015507b63875dc43feb49b9af19a2878c8497318308443e3206614a2d4f4c7c22eb10ad201f29bc1a55465aa81654c10ffb5ee79a45bbe8ccb1e8e691a17472b252008f6d60a75673b9708903b15158d8de098596bb7a77dd9452479a8c11d4c61cb009cd49e667ef1eab89d193cbab418dadaddff1c5d9a0b5afa7262c1f3f02dcd09f26519c49db069aa7d6626ec79d74ed6e13dea532d006ef6b1b71ebeadeb80031b4d6db4a53a3b68b478e5c15e44a6dc719bdc84bdc5a8b3691cafef66bb060b6b5106d81c44a7f8b2cd0391fc99c2a9a6e3eaf93d1352ec368280104bcb590705db321c98cd4be15cd519a36f40a0cdbe57d4a022039adead487cca2118a40f31dfd594a83ce8b7aaef29fe34ff41e5f3e571372a5873eba186209e0f2ef00f03e96022005078f3576952bbd014f81e6569d854f7446d244ce13d9dde5824035ae71cebd206704ca26406b6e44b9288ed37276341b0b58bde1a0a7f7279140cee7071159b75d3c51ed1224e113fccbe84a0df0ce6d3952ac5917a63cd78f9008253b444e0d6135d7c494ca95ffefc3166e77d968fa8a4e2cdbef009dfcd240425fb5026fc3f858c6368ca16e06cee27b5ea0ed17b2ac4c1b906d6ff24dfe0061bba63c703137c9290b8c155c5dcf0c811ccf3c227bd7a959201afa3acc1ea041353038a5332cce6333aa531817b635764c6432fe454c9fa12d86a599b28db0ce5583b3eda5e1900769c95bffeffe07dfe0372373f23d64f8e967fcf895c3e0a098131029a3d753f51a03716539e9bfb3e3be6a1a65f1a04068f4a43b33a3a0e2f8dc6042c6507b80681b7d7b8754ccf3f399d281e5fe7856b32fcc39db4b6026200d90f7232bcf863ff1255098d476f2befb7456dd7660c7dc25b8e55b0120719ebc9d2a8dcd82ce8be839fe3b2afc615d317f9edd79da22146bffa11813402c3f088a66d99e5cc510ce38f37296ba485c26ea364e3a9b56954ff6ecef00f0c967b3de19a62a251a6cc11c9ff0c98562d0dc98a9919d9007619473c9d8d07088cf86df307d33bbd9b17822418891ab732f31826333d06ec0d0ba74b91f7350752ed98c28c512e98902d1004bd3e4eee7b8bcebd5b992500c25e7c1c1c2ca203ac9d7d5bc3d8a5de0a71e0fa16c5774b2a87655713d6ee1f9fb62998cbf0e306abafb5b5d91f35ac7791f6487b524e95181d203cb4e43d1e5906fea9e91f580c67e01ad955fcccdc925726af6cccd41e98f572613b87f967200563622f07050f8a612fa7ad1e4324b7dbad3b90bf510c57ff29e2a7190fee29d7df3be6bd6000b815eb40321a72c27d5ff64148e1d3803dba0e2cb87eb9ef55382243448d0d00537399bd23e6921f28a8290b37b2604d8521f878851eaacc14738a602fc25a0e457e4b4d5be617c43f98d21dd059b080cab6fb954fe9db8ab8dcd8f4e6d5260651e8f182c61b116011c27515a097d7a39d192e753cea2404271d4357948e440c1583de2a65ac68e20ef0e6f131485049cb083d4799934b92cb2339b84612e400754b157e2bb0a23c0730553dec644a3eed6c63729dbd62abba39058b51479303300d7e05ba7233e5dbc99c3b074de41c839410c8303e505af522b9be3001280e2b860fd4ae9708d40c2bc5987ea23b9d8dde7903deafab8fc2ad315635ec260299ad6242474339d70a1d05816583e64f66b398a71f3f1269c5740d29e5c8f309badc151fc2cdf4f6ef5da4ae5279183a706c70485879359dff348e1981786b032881dd913de1be14c12ce00ca3d6b14bd2d0a0e070a8d383c12dead9e49f980a7da6e5e7e2885e137e72569043c3e1498ef57659033ff85b9aabf37f0418a50bcd483234671653f16aa5c261d4b18e7de6977a4e927699365ad135714925ac038849f6702bf164e91f0b791880aa53fcf711b90d0dec3be3cb9a2641e23b1603990f47e01391232ca798c312e3dd409a6dee88c3666cd41da96145a3ededff0d05e9952ed0b4166f8119dae6d25b37f47d6dfe416ebe2a94079d860f023ca00c63dfc3ded69ba9b290c1d3ea81c2d2fbb449c213230b9ca76ba2be49e3f504053fd262e8d0268d2a8eeb76fa139c876ae05e37e59aff139032ec5ae0cdbfc10b9dfa5716f629d3b0bd93d49f50c73208fe3836ea284501f704343c0875f89c0ea2e2eabf4efaa2abcd00d0eebb77a7cbd33d99000f9d2ef486d50f671d20710cedc134391a9c13d5f4f3537fc02f89ee640c82e4ba52d873b4d92faab8d2d50ca653b96c47604f51fc5c8d2a3ce71da7639f86e6e378aff5bf651b85b335c30f0f692c82904cc1a77f45fb31a0daad87c8202016add2d673c7e04837fb8bba08f362ad489b627f645a7c78601826062b33f19740b6efc8dbeb28ce5947554e0507d53aa01bb37708ce6faf31a1d5b28c2266675ae87b957f00cee51feb974600f540046173ce79f52c837ef9ef5d884a1c440f2919dfca406e6fbc4f61abc80371e523bb913368e84804565be43717cc8735f550d47128b5538fa6ce3911a900e11529a4456980c3fadd4e6e929418e61ef866eb8066c6856e1c0b1de44e260c25c453adf17de255727896b65c2e1e432030eba916961298eea74b84c0ec2c0c9cc8b7809f48985ce096d0df1e9e78995ecf08bf6e05f718d1f9b6ec2be679048cdba58fb1a36fa50b46a4ce5653e18d7ed887bfd66490943b92e01a7e58fb0cb8dbeb379d7847ad1e85a81e2c3166d7d6c0654349fa445914ed096ccd1cec06e379c2301ecdf79f9b5525d95704710350c49e95c24474079a7afc21c448e107bd1cbe5d605adfef366aaa2c91750145416b996b9f07e73098c70147b51cb90d90a628b89b6d4df483c570153e464d7d9498b1abb47fe050f7a814f79a8ef209f8d6fac58f858bd7b5981c70975f231eeeb20f8c69ad33279f43528a8406f80a414a5fe0775aa5f763faffea74c56a6511f81fae9d6f4448f32ddcbaacd61d0345d4a7c98360a323a6d433a7028ad5a6160a770c908cd2600cf3a317b996d90ddb80294c451ddf345d687b02dd75304d2af04b28480c686df56c8a6eff30db011402c9d3cc1c23698b5a4802f8c98d6151594072a7b5c5deaef99a6f91f6ee0f0befd78190fb6b09004e4626499735999dbe9b0fc7b7fb59731757ace030b00b3d57db8d7214bb5556a13a06f851d86b5c5dbd530b93e95613fe7c0087ec9b0f60844ab9da597a202c7e7b1b4fed9508528a1b50acf7d5bb4db5ce78b6dd9e063a4a53b3b06c235bb04e3e37457018676ea86d986cc19d260a88c15b5d338403f8b8cdc91ffa1539874d333922a2b874df6fa682ee989beb19cbe55b2b12f207533a0550554465d891c16fd3042e27ce53fc47edfd8110b841c0a9a796ced4080a1fa0e46c7beb36b96e9060aab9ae09bc7d673ad5e69603b36d2753100cc50ca1fa9ed816fd2f123768e7077a90c97f166fd3f4e89dc72137ae881d04d9e3073b2f1c851f4a85b37801288f67693625a8d412cc5b6986193b10e4c70206d403af1491ffbc9334bde3168adb488101d9cb53116eb8a45c7018d603e6607c0d0b84841145327cdfae6b22bf7a77deccac02eec70806d74dc8c3e4dcd96b772307ecf65ca8b5e2446e67307e3c2bb26bcab4ff9b2104ad6c95245e39890e2f3d0a8efb3bd092150e3c11624c063007f34366c8be3d05bed1d5f697403a3f505b024ec603319da9c690b699cfe9e61783907a7eaeed71bd7af442ab60824527cc0a06d8e7ab2262a8935c69a1ae782f551f3ceb37017faf87747069089f7077c40b6b2fbb3b27392828f42950455ec1f5347f08c8a8612666f8ed6511323615db01860fbd22a0e4bcf30e5eaf944aed804b47b2595c6d72affdbfca165f50bd16058d80e5363cd7522d694f6a057cf7a4ae83b7f897c7c6c2569ed8c3ad4f0d3e098ae5c2a96cc6dd6af19aaaf7235f0727005f727d93999b24a0d91354e6bf2c0e7f46f2ee0154185f2751d0deffb4d7bd10940d305e136a58be79d7cacc57db015438d4d65d3ffa0a238e38ca55649e87e43589a3604be79aebd1292c8528070915ae108a1e861cfda8ff43424eaa3e474793296b2fa53d16957cf58283bc700f7f356355da975a1828150569a1ec3a19f348ac658cdad21cfcf6f596b30ae70a4a25c11b2903ca7cfbc149144bd418ca672dce17eb1caa101799a151553f2107df0b49f578ca8c6bdd10902cdc3e33b6b3fda35e987814fc0bae82233ec84f04c01f825c74b97049cb781734e5a1fb0a813c250be2b2927d53d97df771c32e02 true -check_ring_signature 2220a2888b6ffdc3c0967d27127205ba4ad99f57c7e36d6a4f65a275d53f5c6e 4fe553645eea8feb25e52617255cbc98198aefe5fc8afe3a696f3c5848393bf1 1 a7b8f77674d544d7e7738c47de264190d460ef274b1308964d3466859c9606ae ef44845001fcaebd9217dcf97864f78e27b4af78b41970fb706194ab3b8904870e77951881df0ea748aa91e12dac74be272f2333ab6d4b9ef0b5f06ecdb4450a false -check_ring_signature d69e1c262aa05df09cfe8f59b70c571461f61b66ad143936b219386ec0c31168 d47e95973064f67ed8b6ef8a6ea485a2f8aab04b6fe2262119267bd96abb2726 39 636f4d0e79fc66f4564d1d8702a83458dcebe3e28cfed5a7fa076ae64980fd78 06f8fa7f7c9d8c621727a7412ba5548dc270c575362e416b6010bcaf3a7dd32d 49e78b3af8dee176de28ca1ddc56719a5f0913a25af6f03416e656fc2ae43f51 edba725f05dba3f8e3a0c8157da50dadc84ca82554b564a9d0d6e8b7a6d5108e f5751051e02054033c344013ddd0d18a97ca69a34db0c72e3569dd3ab4ce9ff1 ec047e554de40e1cfcd6143b90fe9a3df87a9cc069f09ab4cd77ac5b0939580d 890dc03884426b725de8e320a109e944a43c2e8c83dda93eaf19b26f91917668 68292bc3ce09eec480026452dadaec3d6012274654f7dae709e1bdff2aaa7615 a170d800806ed7a6c76d1db96c1b964dd3f22bfa78169d736c54df0adb56d230 af19ef4ba42bb74df89f8070e6b368931bffc85c52b1953bf270b7e00508f9c6 e757aed65aeb9e3bbae833aaca498dd4ec3896a37d0daad4f571514373286feb d3a6ab236017f24bf13bcd7231f3490e547662df6b0134d43340b1b659465e8f 969ff171e33bf6e888336c31581e81dbb38cf45aece70caaf6990224cb908b5f 1108b5f9b8b4b2490ccb932291894b863a617bb79d663da6ae3926788645bf0a 15bb2261d45649f6d3718d2a820e7922ef5fe999c15090d8bf7c6b4fd88da3fa b1f459ac340287d9a1056903a7b80911e02d28aa5dc149990287a3f90a56b947 25a6eb1426f4efaf746ac1e39192072fadaca2b34c46081da3003cc4129b4860 0c3a2aa296b4218a3bb9b04e9416cfed0057f1228281f6309a0b772e6c6f280b f396521c7d511c0c6e3e0ee7c43741003110e4dca3933393c86ee1959168f20d 63469feb596957488ead69ae9b987eea1eb7c8619792adf7c35ae3893c502b2f 32154754276ff7784b7a17bb64f096487dd04f292b3ab9d8482f3c4fba7acae9 f7e612d338d5588690cc01db4b1e89d5fc040e1b121b5226f00cfa0621a06642 b1a5c6a7893c3a944ea854b1fb290cd54d0f7a9cd3411d09890495672897b2e2 9969a5072f81f6d4a7d163097defca41dfd192e92b51c7d67a2d0e3da15f1d19 33ce089fc6716102296a39ede7a88393459e7d6a3495c14ef5f238092bb3be18 ff839bf864d9673adbfd6aa4b4dea7d02ecfc98c6006d2c65ee4a3df6da3cf84 9e57d415d41d43672ee185c4637ebd5ef0f8b22d6e04e5b5fae49fed45f6fe89 54e71ba2b81958a13dee5932ed50c67a30185afb4b62a965efb4bf29c2656e50 6f126652b3cda9da6af572620380191616ed689e023a8d0a0afbc5f5b36a2a8c 03038368fd6d0e4224d8327e78b4661e0cfac31f0b9b1e6b18ef05e2dbf5f124 3f0d96ab9edb8d78888d59569b3a4fc76d2a5508b627b92e45613cdc727c3704 79f18e43f844e9c830a6f873f101dda4624dde74a19ab4bde81f7ec13570753d 0f9132ba826b1c369aaa2f4992c01bfdea9036226dcf6c2df90674e5ad05b463 4fd57072013c73e151de66093326b85b92d4a84b7c942a506ec03495fccd98b5 afa79395d3b84874143124bc6f419858daf4345f75dded822233df01a2766298 11b9c5104be95ed9f9be1a48fda62ed4a5b8354e94c96f7758094db6d9649e01 8838fb089ff06216c494078c8a659c58a5e61d9cf600065d0ed896ec086ef693 8c685a3247e753ba4c1504c0295a024011bdc597d41a3a7a92eedf58b40a333f e0401dfa86159f8ac437deaea4b5f335ee992acacb2bf79ba84fdf0130a00c66 d2eaa8e0d6e99a10b66fc53c7856038b65e6bf17f603d83f0cc467e729107007c3f045ea7496da370bfeb25f4e0ace9c535575795721390c675150be99f6ab0ba10bbd169481ba005bd0367429fb93c1d89af4e240d10568de967daa9fc0c90b7942b5ed1210d63bb136249a46031e50f29a16691d8c8f6f050b5792c19b58062b0cc37d664bf517551a79001e22643d3609f78b223194538c7735073e2f9e0f3e3498e66d6f9882db39bc9927be72954b6b358158068cecce028a8ab179e50ab205aee85f37c8c47c7e3f66af35b4c0d3496d6ba972e073e19296c6e6e44e0cefdfda4469ef94f6632684158219595772355e6f1f72d117461b204e5f2a090fc66f01b0460fb8fd14a615ff14a3500726e9283aa4aff70b3ec34e1d0993870ffb16ad6eacfa38821e1f5ce06a3a3a5f949488181f22ebf95b5d78aef875a701f981e37d4880379aa0155278126cbd93473139222796443bd0b93e000129db0f334005613d8145f7f95e34d4b5010392997e0b5ca09022f305a61370b875b50a5f5f641feba6f04fc10126bc1cadd08b25cb56c49483276334e19d73d4a28000c89ae42eea6efad66dd9a7722d4a0b9fa2642ce138d3b997d9c25f515d3e480744e5d659654b8e7741fe0194934aa0913e78c6ed99fdf03370cf10197c25650cdc1423ee6a27a0e18d0f1c50cbcb61588a4c930705f52fc2d4a054426f34b70dfd3b1209bc0575f8771914c2db3a71c474b1ca05c4388e0a756d46c1b9ef2005fa626b1981355a91458abb3b716f6060a7b23b6678f239316c77dcb94b4bae03b3d85cf43367c49147d802b0730ffb04ad9f8a590c3596ece34924e18841cc086e5816bfb1ca87c80ff722b07a3af3e66320e02aee92e3bd0dafb3b0c0deae0787f2dea9c2e135ad9e4c631d8732946c7d6f57a6946bc3d8e52629888955490e183d9b6052de0d029fc656873823f625ad7d28d5ef8ef105b80dbb077d8dc500388e74ba2d5686014b542875484baf4c41a054fe9df715fde4c0e22451b7840d9256abd7e003b43d3c8c916cbb9dd01e0025b0d05567ce90b4b56c446b96230093b4cb6233356689149910fa4b6a5659801c5a37056471193e4d7802e82bf40da43d832c5844921e810c420c51f31235e4a377b9aedd91dd115a9ecb8635f40b61be47ed16d56e47f10e32330da61364477f120282175055cce27704088dea08716a2289b60b0be5b1f9d3ab779ce978cc596f0a2e9a1eb2f96384b02b6f010161b1998498c459a9d3b73c335a8a744290cb2fb734cd1ba0b1ca3e4ea028de0d3a00762f020672be7471767450a6ec04931e239666b36f489c9bf373cf619c0b486aeff209214a2399e7be10579eca410a00ae51330c6fc7990b758d95f2360345deb9622b86b5268c0810cc0f889adbfe229779206ad6f812bd3d1a3ee7f10d5c7f4814c7af2c72c98aa73a066343c58839ed24d00907bc64be2a5df777b10743cc36d6472f68f01fa0e75a5147153f099925103474106550e30e87fca69804c9fd3b4c8b786fbbe2d036d941d419db5a0378bd8980fa56dd5c20df8276ee08bcaf569b494b93d9033234cc8d46053b298db4d32b1d67a061f7245a98f61f01e3d9f6b3dc5502a7610b0ce95b28fd31c969fe13623c60751dcdceca030a4102439eb9352168167e3aed20de87b6e865b9c47fd5bfe04116e09e001206982505355bb153b94b98b59cc8c9eb15b862c591e8d053177e285ab1ad34bc2ff84a062f246137f57f2c64cb06c74069c49be1f524044a8e2cf3ccf7d935f18be6f90670836d82491fd641c47a50a264594bade7fe92c0d3521e576a1d42ad87ea170d3598720fce51aaa3963510b792d7e384473fcd16bb89f8941fc2c512f77f6d0f5acf339621ce04b49e559cada4ac7c0df0fc4009497cf3deb5b20e883003f30347856eee8625408f4e148707c0e1d589b46bc37b841ddec4ff9e7ee13d688c0b6bfce15b52ea32b96d56eda4d51f180b7fceda9a37b415afea36892ca8e818082b003f34e2bd5325e2335c61fad237784e77b18fc37e91e229d26fecb0c7bf04ca07882570c9535931eb6267b8e09fb5eef63cc0c148e021cab0491c59328207f8923ee9c883baaefc376ce95f5468e2b3300a94f43525bde0c7037963267004075d9206878c43b933bd2be2ed0ad06567cc0302948215b4e9ee9eaa60c58c00f327c51121f79e2ae708dd20afd99a482706bd3dd440defed517973d81c0390e93548b7906f3b1dce66a53733bf54e208724958db653ec14b74776a3d56b93097075bf4e7a356fe8ebc6e34aabca098f4695cdaacdee1deba6a0edecbf75f80def31cbf7f6fcc8e7e9a77145f0f2ccc33eb654d5c63731405d588fd54acdf9021871c24dd2cb2646ecfed2c41af01b3b10fd0d8bc10cfaaffaddd42b6a00540df34ba2dfdee28f106af50efb55df4d85dd69595de63aad22026cebabc8cdc60c30495eea598db591101a36608928bf9aedbbdc4f17e2ad4a56571363be92ef01344d11ef12bfbd0f1614a7e7bef15ff9572a6f6662ef438280ba9fe0e132a20d8a51fe792c0da13523ee00d1cc72406dcf6d432d6e56de8a5247cb7d0c595e046e789295d4683367578a7e07fc4c35871ff7db80c6d891aed83208696f45bc001254d856a1a92b096c47c0d3a79634613cec73f704af384616565a77ab4e6b0b12a4786ab0793780c5e2e9bfe516154329db331e852772eadfaf8dcc34252c00459d7c8338424315c785c241cc6b834e323e210d86a0cfe980c175eaa0b9ce0de7a338a070ced0182364128d9c888d97c8bc2357910b16a8becfb043d673ba08aa5494a6074d715c7edce8dacbc9a4825b8fd9c0c9942d57adfb5cae5270f902a1f36e3bbb4fa55f97f6ab357e786fd3af733e998138adf65c4bcf0140b83e0402aa2578913687017c86fbcfff05ee71cf25cc249d8d2fd1087d452400710e08518d11e841bee61fd653a50738ef4ada84602877db80bf6a8af3ccc54c648c058a756eebcc84ca48431f328aeb57b1c743f9f48eff6d2ac81116be273010b5092dc59cba236375ded0a2758193956b752ca5459aa0ddc755e0044c91a7eac9037ae4db0ff4ca0f3fe805266cb6603a53a1e6fafc3ee6c80a169a3d0548b75a0e55100cfce3fe18f4a14f07d5dfc2307cf09b66c7faace86667637efd7247a50b4c80471112d122b26ad527985021f7dd2d1817d11d4a43fff85ebbeea3603a06a431bf51bd717ab214b24d61e3a212a9b25edaccbf3c65b2cc722b881eee350a4d265f8babdbe47b6e12a5094dacfcfd76a90560738b128474f894857156b5046b392c70a45ff43231e98247f11323749b5945ad8894561d59c2dacd93afca0b6184b6eb9f85ee11ea9bedc32f2a96cab1cba21560f5cd7136dc98e9f09a800b8767f8586ab4a0f5f68cc15c89a7dc5cf310075193d191e62ee986fc40fc3504b875bd3e3a53b96b339a265410e1c09a21ca2e27931b72d27ab74e21c09acc0c false -check_ring_signature 69bf851ab2aa7617227505dc69989546d8a174c817a5b8acd23e9f89e23041ae 86f5ae0dd1e83e177561a04647fb417810003f1ef43a3515da4846141cc22cbd 13 b8830752b6c9ea66f5d9a9b6918bae38ceea7930d356ae071ca4f6efd5485b47 d4b0cbe2af4e709e3d26ef0d68904545fad1c9f1eca8e5e8873f64580e2a926e 59baa76b34d77eddf8b0ba570d9c21308241c90c335a61d7b0d6b41ba2a63b88 acb4da32f112afccceb4608263239767e8b52b240504407898cab43bc29dd0c4 dbcc28d3bcaded35a3a856dbe9ee47b99f713ba0d4cb180ae30187d8a16c212b 4001f55c971179cca4c2d9873abfc027fb3b2972f60ce8989765d163a2a9af4b 2f9c5579d0189bff50e5f5f116c4ebcd18bcf696803304f6530b7f9ed470f786 c6e56ae66582816d20dc26c9578423260f56af3d351879547aa279b3a1b04dfb 6ae678c861d529e4b3b5e0d7bc68db149e56e79600df6cdfd6e8fc67885a85b4 320926f177772c56a222871f3b31e2dfd04a86df3c5b201e2d7bc1bb3e2b1166 696e96a81c1e1a5692a2ff697156d7c2aec272e2796ea6a2d5c408c8060c2472 93aecec02b7a58084941996d5609a2f50cc770e3b42b5e313203a3a47afeae96 e10eb638b8a46cf20b326fb6eb2df67e18bfb8c8b26ad69f478230a82ec89f5d 9d494e98cb4a1463047937fe53e9bc45c323b37d5b196270df4b27f3cd96a205ca4c451a8ee7a3213dadd7a891d9e8edb3e5c0ea7c8b40d46d1fd0e0e6f9970e8c43e6004d8172e4cd097045cd98ce20f330c7c355c16963ba7451572f8f6f095dbefcbed8fc89ab0fbf57a19875872f477acea77ff1276268e81fef3c109a0e2fcde20fbe922468cb6701a72176c8d2592b20962d738269f7446056bc02cb0a787190c8b9eb5ea15372a37a22618234c95d63d072a0bf89614bbe8becda560d631ad8e9c31b53a5e3428ef5bc711f9dcc0247f8c6096af3a4eebb3819c0200dbffe031785a5bfac7a83ef921b493889715fafdc43665d6687338f56b19c3c0fa9ba69e567473cb807527d6bf93888746b493f0bb80b3dd5cb1d639bb2521f0ade2268f07b719102f42f13be3c6f0c86984821aa5ff7646bf1615b58ef9ac40f77b83fe605dfe3188dac7d24120dfffcde899e411234a103c0637def15978909b3524d6da1ba746890184f73d8e900beeaef4b316fc89593c0a65ecc616bed03fe3769676b99778972d962e217a40d67bcdf5d085cfff0613578322d52b36c0ce1198b298a858264b2811b736dd8da68c5cd4ae20c7df53c1788ce2da5110e00fce0a65baa73ad56fd079a9fe6d4497983e097d2c6f67e6b5c0794542a6fd00f1ffa07dd41641a003425349fbd9f8716dcf45f6da5e71e6820bd78a5d333390d1690c1b39f9df5f171f2e9d30b055d2c4d297750f221c306f72447a107f8aa028b68688c0870b68d012abe165df0e17aa3c915123bb8c5b37a1bba99ae716f0bbc0d42b4392f2bd744a32d5e8d7eb132ec99f2fbf085ede76a0248d8a757c20064fa82fbfc5f409ee101057ed6048f5374fce0a72ec18284ad459e39c10f0b048ee3fd9996158ab1fbe6338007d001c71ae9cd314dcbfff3da6bc1a56d17b00d1c0be647e64846b01c8a7495823c53653bd2bb32d599f9f8f1bd58a03d752b00081a4d9aded0f8fa807d374b6b94cab25e7b30834b9d4bbf035165696bd67708450bbf81b89e98c842e13427c062cec9f35763a1f2ed43c8f281a8d610150f007cca6da161f5c74e0996c4de0fbbffa0965f52916873b21273763f008d2cf70f1f97f513430c49ab5b5c9e303c19707d712f25b4fb2acb06c506b05cfd46b50c false -check_ring_signature 871ca641081e6ef935695e0ef3422d227ad4213c27942c22697d4d2dd31e37c2 909c958014dff37b0cf50a88997824485b05868c863a927a22fb57fa9f6bd4f3 7 06a1952aba1cdee1415bf87236ee45913bdc02d7404fb9a29afab39b8c9f4796 02482af960c79eb86b7147ebf25ee40af157968ffe192edf85a21e68a2b8d712 28d19654571252b479fe3e22f98ad94dd6061c23e6e3db207defe12e1477328f 9c802a82284e394540e1e6395009999abcd0852943ab5d92a8c3acfba109e614 d712c6ab63b481d0f61a64e2d8768d7773e0cd62fffd1a3fa8a7f729a42c325e 14c192a54f3dda80bb5e6c7adb713dfa8c650b5450423bf7e0790d223939fcfb 38d9cff822d6b5db04b465dfc99bdfd44e80a83f26c1df837c0676d8d91ce1d1 fd988034edb448a9496e1e24cfdd1a1da0ea9f23c4e470a8f380918ee65ec10c58951104820aa36cd6a9ed33513f24620b20a4ad0cdd712827fb19be367db403506118246e50312d75db6e623de81b2d207e7d70ca3db0be48a545aa496a9205f93dbfac04ac32604f75d6d541c745ed9ab7acab5eab3d7815596d04f658fd0e054a71234fb742dd3af0b92393bde83ba909be2a285f4022330b064c67af830416b746bc75cbf41f9ce888f06de4a81caaa4e16c2a3cab654329247ecb4757074a2c445b87e915281c3c2a9901ede421b5705f74d3f7ef962e0549b4a785110f87700b2312326d4f0c85463249cc186a945ba318cb6bdf0e01ecc7bf57d0f00a8991bf61bfecf77f959c9e5c8a6896775a848c56c424ea5451ab3d0c2a18340314109f968594a1c59a85cc3321044009c16315b7ea00d76ec34af3c4bde97204f83382a9681df6caee156b380f6160a0e83b22f7bd11c5a54cb2c15fd97f840163bf8e26f969c1faa2092a9fd9eadced3463398da6f0c8ff03dfd4ac0ffe8f0faa8448fcae622ab552eac280dcff6d05af617a771d93e55f4e85fa134872fa0f2c00d7a7fcf18a6a97eba23b8870d98d3ac3fc99ebb3a09e2ae4f2b0fb7ba104 false -check_ring_signature 60cf3b2fe41aee2a4afb7b01302c091f704032e2e505d3cff82570372df9e23d f19d0e74228e3399ae21a4f783c8112074124e7d7293195d9669c00ff1a3cd22 15 5b378245a463f89a0a3614f459674f4c273a19a4e2d5b63baf6fe08fb87252a9 922140d1cf8c18b555c81743775116d356dd773f26c201bfee20851829c81a43 f1f9163479224b2048ae0b4957253ba64060a1faf5eab93f1abd7cb626b5b545 e5199ba4b537c18afd86011b87820b8641abd5aa1bd00c14a4137834116380d6 e8ebc325dbb9c18a96bea3a7a61bc51896893831536b94d204b2be8cc5828b64 76d8fbc5df9262a26d0c0891a0528727703ade894a92ba4dab053bf905619fcf b18e3f3f25450b9721df09dd6b91344ee75790b3a6d29cdc82338f78225595f0 3d4eb1b6da1319cd46c93798deb2b157d1d9ae8e5494d6be6630f61ed9a80249 e32f76a5b15be83ae37aee9fe5a8d54c184753bfa879913cea27448f319eea27 3cb6266359bb386660f4338e25d433f43970f071dd7a0eab78492449c469aec3 eb08d93d9a6b9a0a67056a9be82174ff6ff2c3f9f1c4abf463246f5dc4576064 a164ec54a4fd91c2e914321229efe70bf21d710339c6a0a0c29b2d0eb2ae56e7 2e505428450e564aeaeb433f73145cd5b919e94ead044aa8e1f7754d345cffa0 bc13fae8bad3a347f855ecdcbaae5b65f39729a374f848423d57d69c116939fe f4418b309f7e440896661ff1b48ef45110e6aaf1ba0e8843aefce6d7f4ab1971 54ab2f194e52034ad6c6d86a3e224d167110410a2cd23bf408e8796af5b84b033fcd587ed1aeb85326d1d1656f5ccd5e8242b66fe71dbbed865ea2f8a99539038bbbbd6385a1cb0cb02f8473889976ce37803cd8c048b327f449f7b01a081602844b85d0dffd44edfe0883203340f8a96ab87845c06b491e9cf6ddd1d079f70a9068f26fed6cb053395dc7e32d430c5fac0dc172fd022b3c886573b2b7479302debea99a8f22531869e04666b16aac7a36709219b954f790685a778303ca6b0b46409a05e4a38a47d6f88bf233535299f82770261fe1b410800f61011a8a7902d25be106d963a9e67ab89471aca11b80889cc87dbc8e6916182af03e8d01f20ac498686b5a038673cdd8c2031524e50338ec781c91efde7dd14d339327ff5c05259ad7d514178322ade2bbc93ad621413c0b705a21eab889831104741de3f90fcc61b800b75816a1a28fae5680e80483aef295c8d3190386b9c10de6e1428e0702a68ee396ab365e415104f6bacd970384bfd2df8cb914b906c007583efadd066a2be8b1fc7e957029d0355cf88050481279efc57a66ebd02dc6ec5ffdd1680f808b98f776ba58b6c7a8b762d8470ca1e1693b3fd3de5081151be01b9c159c00bb7109ea05efe8b0596bcc0b12a950bf12bcc4ba5cd2823986ad5c1d33b9650b013e08c123fd32c427babc712865804c9ab02c64112845abbaa3cba06a56690d4014cc048d3b703a6dd0e3924bba293f0b33cb17417a0c75a4cab7299f971d02c8ddb0941bf2bc6de97d3c0a4f578445c4b6e678bc4f4b2d56d7e44028935c0bf74a1d99887746592bce4f3a0d7fafaa5bd6050353fb215b8d15dda2be757c0a8c57bc12af156d83f6fafde7a5e3573afaaf932cc9fd6e6fbbb8b1cecd38ff009c05146f818702835ed2fec06bb1a09293a542cff2daebfa561fec666e51da07d6337eb1428bf345aaae5c5cbac8759b3267fe94c4c1479811028585b6f78801a9bd4fb8c29748680136bacc88e59b94c2fa2dc5fa701f8b673c1d37ce200104eac24cbe9702e046d8673dea1caa1d502f5dc5ef05be94d669361e1b9d9d9603aaf7333666a6a86f8437ed94efba75e134e61549c181b5c051068cb3ee95d30df8b2fcf98808d5fd7b7e1ea78addf1b264a8c4e5a89be7c89d835af3c390f601d6c25c7223fce7c31f48030264d17e066528c97b2fa38e908ceb557aed9edb00aa40796da20e2feddb361b1a40108196cf0e6a3d00039e02a88062b75c13bb0d2da2076484e78e475d45c84e4b66b7b0caf3e9d15a00bb95781f70a378d9040f56c243d838e404590783d37a6a69c09d7f99a86712dea1d516aa10c96f894e0c false -check_ring_signature c26c9948b63c992f69612457a103a7819d9cdd0343c039d673daabe488543da5 fb3801ffa7cd35df464b3143b6b88af766cd9b879b982300d4b9256b0ba2b85e 1 670da5bd422002cbd20e80ef27a750d9cf66d9ca2c742f02bb14d8ce94befb77 300d41c28ef26fa3ee2c04c992ac75e9da5981e1335ddc8f3883c41df2c3da01de2d51d205b7adbd4cc2c95b99877759ad62abe37a070c798c225a1ec185c803 true -check_ring_signature 07879521b7e1e3cdd0e63effb4b8d71269eec14d29d97ecf87c04f8f4c882403 9c6c9bf2ebcea1c52966579d24cf53c737d5615226086f8d139a977503785087 5 da0a09477280e1cae7a6f64e44b9f0fd6b86718e5602bf4ea46f1ebd942ee42a f07077b0d98b2dc670431c480b422ee7a64c6094f154340e807f734ef622378c f0f1172b16fd76e582411109ffad8be4987d6fedbb741670693c023e0af9d933 1f9332a3b85d5297273976022d55d5cabd6beb7c9f9b18f9cbec55f4ab7dbe03 0e3d171020eaee67c86b54d806dc6bcb540ad3f41f98a8f1a655875168c4b129 ca1771afe52852629e111812b4d5ca008cec40f64041038bb25fed30759b1d02000df68cb2adde9f9fef9e490b4314b0511c3014498927dc410f5b8d47ce57069748f9e612a5a5caaa3568b6a0fc52cf195d0a842f8818e96d2f7768aa7f420fd6ea68236c970afcca03ca9953d384667e6dc80d0492139fd1bffa39bb2d640df3d571a435469869c8b7c6014e5bb996ca925b9690d6cd1efa600fea3f5c7d028588f7b1d6c757bedda7f34aed8323d72a6981885a7b298d33a6154d9eeea104b6b04b9c1a5fe5ec679fb6e98f3b9f6a0a7ee070d55972c87de86654658eae02e10d99ac83cf367a1479e7c998f74aa095b20e511026be359c9888e9dae5610ab2857e818ae31d4fffac45e1b9aaae26fa5cc8ca33c2cf33c6d37e4b01b44a0a60b27d7d272064fc1ce3ffff0c9ece9f510e343b2a07c74a1666812fbab7d201 false -check_ring_signature 462eacf1bc595251b47ab6e307077043e9feb7ca9e2ef9de99ab957e1d25010e a5d80a5310a53213a2218b316bf431d5690d9d8a8de6757858ef3d93625f7ad0 57 53bec99e994ce5c6270ca474c9624f88a6789c34cba5fff0f34b6b53b6a13468 acdc5cedf4fecc3eaa2ac90d0fae93f9e982a3476e77266019389505916c2acd 0e661a865304645ef405df9c25de3ebb73417b04b6ea64cc9b39587bd28bfc00 a3dc7495796383736c9f526533bade9582ed62cc5a1d5fb392f759af4aabdf42 4596cfc37ca1d390a93b3e1a71e6aacbdba0f39cc6df36f35dce9786f863e44f 088020cd3f0c048834a32c87536d706f8b1f6180c1f887032223580288ff7759 2cd1ad25c394ebca74d46b2691dc71442883ac75d86fcebf5adeff4f7bf82127 9ed8a6764df736f12f4aab7565794b4b43e0764d04aa8a6402773bb919039d1e 9d98ad0c503931cf5678b2528f9f33fb0218ad70cc643e88030925d1143b455f 784714ac794e968139d04d5dea523596271c54ad76e0cee587819bf0cfb1450a 4a79b0dff474d3dc00715279133274cdf1ecd943b299cbb62afac44a167cec1e 89107bf8c47bcc78e678fe90c56ce6509284090c7e850aa8f663f10cb8b029b8 05dbadc150a2e5638e0358b43eca67ede4eaaabb3b26076a672c311b5a69b49e 63d0b48950ee0ff28f2dc8a9d56b13bad2ff8f4c98e2fdfdbb5955af6794b62f b4823ab50d227f4aade616aba2c96e3e1e7b3a6c1c2cfc85bff8f3f35bbb8aa8 a43bcbbed123092fc751ed02324ada5155263683aa0626d67c67e39c6bc9af44 2953bc15cd52bf38ae59da9ce019a8f641077e9e3ea8b36687b00caeb915b79b 42064b4d5469643eced6ef6e468dd16bed6fc91b2dff053017d305be59a57899 b9b98c39597ae4655f79293623593e64ffdb7008b2386aa3557ecb97204ca006 fdb42cec5be1374b1075c0670719fa85c05e99d8b623585dc743ff81cbacb392 873cd6be573af757f6cfe512ee7811f9c1d636955371f470d8397f1b4cb67704 2385950df6b4240914225e7b95d3a6d4c534f0315c35e45d031086f141ec5478 53e5f3457da8f6e39e9c0997618e3005567f4ccee0d70c5b6e22696cf8e90864 56b193b9ab413736983f6b59441c81b82ecb6ccf2591b696cb98acec6662f53f 6a5751ab035f53cfb6cb217cf25ac4cb955e13d5c5372ae12ecd34207614f0be 1bc110c060fe1a890fa2adccb20694877beb01adb14f1ec5ea7456cde1724732 7935393b56d6899c9f2316370b775ae67577121a0c229d535438d1c9fdf7d098 b2e6aa3a1c1e09288d779dcd042dc13054f6160c1270db080383864bd481f23d 25efff42eac3bdba6d767e587cc69d7a4eaa3725ece1582814008c1bf2c5d181 eeae96975a4cc90acc0f4ce2d8740ca9e7eca2f5c90daf290f9e5b9c9a9f3f43 54a91da4bac8d284609908d905c002d1c3f9f0820ef4b84dbb95e7c85df57760 f8bcabffdc4e44affd1b33c953c5084afd4713a2652a21ffc6059517269090b5 c328cd9d3a4314ea17657aed70cda2f8404478c3c00752d77bab7b531a25c94e bf41cfbafab1c7e9be9f70ad09a0f1c75c034d57d9273609597d254bdbdbb4a6 8b83fba8ea736e0676eb61aa0a2af9cddd12f25e96e769d64aad1d3253b47060 883566672913ad0c3cca819b596c3317dd7e3d270df754297a5bc3635725093c 7f1f035f3f32e135ca931f3ef52a6c1b637334b825791295a0e71dfb7c6dc7b6 ee4a2b3d8bbefc28653753f8599f00f585b3d92752af82d9f8af3de7c0e33945 cf1b28bddfdb102717c7e193ea06400510c329c7c77f076a06f68be812ecefbd 2c0171c0a046522496c2ffc13129abd06c914941465dc656cc665d230db12bca 689ade56403ba2b1abbd49096a65914df07abb4f83ba2024cff1ff15c8525d01 a0f611eb98d3ec2b4716d5afe0849d883dc2ae1a6e3aa4a4a97948579aa94299 11ffe8c4813922040cffc6be164b20b589e27c755efa48e6f892c9e810952194 8a758a253b7e7267703e41b5e7a8ca1bd79b2ee0e32476bdec1737307f60beb2 8a4eb7462de74b67da02debd40060d41327f393757067156e996e68484d7dd54 024e6c5f664f18978b84e10c3321826fe6b75495d0deca0134b129c0e0b51ad9 515c73f1f9932704e8f432b5fcbf55f7181cfbe3b5ee966c39d48063a9a22a5d 496866b5e13b32ad8438e8159b4d2e75f066033f3a7d5a289e7bd556e75798ea db99692e80843ba18ea17259bed041c005de2c7ba5702242bb26af7bb821f271 25ba60b0835570bae50334773f63539fb55cc2e9b662ad4e9cfe5494dfba9ee1 f18bb47d5d3b77e81d927bd00ca52ba24bf22dbb13ca89b68ea960ecfed9c5b0 078c51abb3046e59659f9beb53ef6283cc08241cc216477720ded958d552e42a 2c8e87cbff9496b12ed1933ba01702973d4466d66929895386e78361fdfb462c bfce9c3d2e231ddc7839ed27d6debb7b76c599298889aae8a0b661d7f37c97ac 73cb9507bb349719b3a397fa626a6b19af761e1a80084cf33cfd8bc9f49c4525 ca909e2b23c0da154a338e6ddcf9ac6f7a7c07365ca40948716a09f8c5ab5586 a91c589c0051f22b2467c882161e154a339cacef335a6841ec895fcc4413467c e42081f2b362337f63b13997d1cbe58eaf4ab3a024d9444f169afa1ace6c920cd645d5ce4ed1e0e5f55110b851b6af636f64046ed19922f2f24f1ae39edd9d08f67ce278e22258246dbbddfc83db6640129e292bd7d9c8c514c02d75ca5a5e03b6fd7cf095676b56702f7cca85ab14c899edbfde940e318734289d84d3584204127880da0faa5c85a86f8d85988589625d62a0c006a7da70cc110ec18419f50c114b3edfd5634f7f90c501635adb5444f369f3707163b8025b3e7856e1abc7001daeb2bc89a36cbb9e4f54df847ea300b80a4e7672a179696fc3b53c6264ff03c83bc95e4fb6b0b4cf1467c2ce341452ea113f2d549adbd18bc44d5637ddf3023bb57756f9a57384fc92e99fccec06e0953fb97fa3af01ac0ffa1618c38c1b0a007e81edcd5641dd626178e4a8a7a31732227c21cb7a3a19684895031df8890813364a4cc83968f1ce2bc88aa80627b88d493ee23cb27be994d4b9dc05810d060613bab64e301ed16767e7d5f175068a05bdb79e001d315524155dba7ac5ae047bd99165413f6fa5c4e7f4acd94f49ef86f4d399df83ae3a8a2d0f051523ef0057314349b17fceddd1517ae63cbd08781b8b72cb658298f5c9373ab07075e50d021cbe9c46615fb1e19066b591bf4fc8d43adf9217b6f724180ff7240df89f019110e44dcf9b19c989862ed791a008754e8dd26be436c0c70d19844b44178101c90560ef42a31a26f35f5101a8bb851d7bc32b0096f2d55f677f17d96971e40ed44385cafbce976958064ebed9df579d6b8f72c76bd0174f10761525c469a6008bcf8e21139f75be689327b9fe0942b67bdf53363369a7dd235f13b4c4106a0871f108e981a88ff0d2893a9783117a4d240c525b02cc12c1ba6385aa74a2750807f0f091f6c32565c6c042dff257798b958a73349f658e4430877cf1e2836d05cf10064711762fac9f6ba69063ab635e69ebb6abeb77f89029098a2b20883b04ac6110769882b319b2392a2810e0d7382d16d395be3ca36f43fd68f78344290bee00ea462086db09c984ce8204b314b5559f1910b60a88c87d3e3ec8f6e7a6075d8ce891f9d19416c93acab2c6a0d50447291e8a377e46563d6f82fae65cf30beab3c9560fdce2be276372fe4f133d2239942638dec507c341e7cc0d80ab57036c85f672f4c0bd0c35e8b37c93a78d7c80802d53595739553a3d5b0ba381720cb1345846dfa0113d46950d49348c1db562c43007eef2da83d8f430d37733f30a74f95e6e7bf9a4892dc27b68f8900604eebe7af2a99e40a4fe1eb2e72454ca025ea2e1e133a46a79e998257340722154bb8c6a3808c0bc6ab2360304d43a1e073e924049eb7424c57ac5accc27f5e75566d70cce3d72859af276e97e87d8af0b24a31e5ef60e9cb4da6840e83b027b3e1dbbbf8a18abca8c47acb75746b0b5088258a7c1bda80e14e7d8bb6e0b80f31d75cf0f851b6b6ce932db0e109c825c03aa593b8d71e04df8a40324dda2a384723fd5e29727b34e8418bdc80a2a7e7c047413625e337538fa7f99b94d28a6fbbc1348ff7a49d01c7c1e62c2a477f1ce0dc16e4aa95895b5afbfc41f823910ccb653fa23cc573d40f4da9729c14e505402bdf10e827fd9477ba87665d4d9342506a9840eaf2796aa2606afebc8cbb60806071340eea7b3ab9c2f4304221756a20db4c81095b4cc29e51485e588c7712c08b4005180d2271e345e2ffde27de80769c4f791ddf55217aa7021d85924538d046ba7cadef26be20940de2ceeb1110cc95a74d3ae4a2e653509bf112c3e71ec0f38dd49fefb468e80e168bd8e0e6b80f2eb7104f52b0b03288fa7eb56160c330d51710b5c36b31350993bb6a229272a731abfd4cb6b8c98ad2757a19f616b7607824c71a5a2d8865ed4fc2108b8e7716a5508bae3454e5575b91a4e4629ddd803ff75ce78e7f7ae65772f0332ac8e7c685e01cbca50dc76959dbd84a3d995660b454cc13817efde6eb046e1bb92ca5ca33ce70093535237fbc30432ae80c01e07385e5a59fb5d2fd4b5fb334a842559196a8b4952ea2fe581d1b85071c1bf1107dcb993c8e8438e19799f459f4a35976cf73ad076e9e567f32af082b6284cd50561fd95cf39d670e295a622cf11b3e6f9f5e499f44ffb79ef92e1c6ff27c60f087c4378dc987ed74b9d14e53ca0af817c3d95cbe47d398520ea19a8d5e92c7000a5a56331c5d56c82741583a2e89e855defbe190c0f49f076d0cb444a8616080c678e901daa781a5bd71ea8c2c47142701f53e6366ad330c23de247fba9d43000fdd9150d6569a1807937cebcfdaa7ec2e07f688e4eb68c9b618cd336f6ad7c0165d11f9e63c188d77c384133eb3010d366b9765bcd1fb4c3b1d1f3727cce5c04b889e5d277f69c282066a1d4a370a62e7dd5f6babe70a0f984627993729d7a04efc009457bcfe4c1d6071288afea20b0341ebf464372a03f81c4002ff2b57a096bebc08d98935ce377d79ff9980d985a4c4f923c7ee793aacaa7a70c1dd2d30870f614be5e328f414fd401162bec5e05fa35e997a6df90d566d83109251a1700d8605006c72e23e5041180ea916a266378891a38fc5395ba0045dc87ea7e8707caf3cfd77fb3fa5265e52f8de893c4a8f5dda9268cb21f42ff8e8b5bf99a310666ac4688ab9bf6a0cc5b63f1146b71cfc1059b4e6bd76ded737f35eb1cd75c0c145ecc009d22b01cff62410c60ab7212c394aa0a53ace6f3dce9938fc13aa70f37e1664e85cc8fda0e1c99e6fb9d32968f0d1ed7d5a7bdf9f215856b8926ed0592303ec6749c20df518ca71c1c14e2f7df0f177ba1bc7fe39116f30b4110ef0d56578a4010319c7e574c5153c8e79bae617deded2af2ef02aa3c022c1eaa19056058f2e945c1739bc192a4aabdd2b9a1ed1b3e6cb4e03b3e5d1a1ec0dded85040a2d43df7892c0dfc3eb8eb1ba751d13f204ddc9c0b90fd3b786de761f57a403fdad5201305e70ac0c503f3d3c9fe01607e1985a6537b0b00342bb8ecc1f480746338149dd8c1088e23aeedba59ec57b3e9e0659c4492166901a94b20379f50d64fcb8945126c4abb07e19f32c496d0ac12e83292290f467e799c3a8e8487b06d7da4b051bcd5db78b89f497bccc01ed34ba9e768544ad1aac094386cebfea0318546fd474c3de8cd94c3fb7029873881a466d00b2d27ffe36f7a9a33667910bb85f4132cfbf8453205ab7c8214bc1ba1f47dd1003e48f121c9cf30908a80b0928f39dc64a24f747612a6fe5013a8188266a44a10623938b5355343d57be460bc4b4311f55ad0064ba2b8f9304de3e55339e2362ed8ab1c4345ac27f1ca1e802f14ac941b0f15c35155f5765198e4abafcbe30e0b1aa1a655eacee44f7c5e705db95930cc0d4f13714f458e63d5cef8ba52d373b0398aad85016d33aa67ffc073836124d1a1d0915d8a51b0f2dbb29cfc05928108faad43bf2e8b341febd3005ee8ce69837a727d1f2b9a8a4345d98600d6706f0cbd28a72d4f5c31e32f13f0fad8729812176074284fd4b9430c6bd660077afc32bf0ff58f564317a8521430a56ae4f84d85206bde504f29773c73890e2cf5a45c16b1061c2db6fe64ea0ed027334f579df59dbb158f8daa0d2a4335b1b9eaa6101c0e1dfe0d543524245600fb642ad7e18c331d32b5a2f3acbd8de7237905e5051c910c9350bdf0edea88e0ad837029200b1e35a2346ed4b8c93a4da188f2ba917a93ca6d107a6d7c5c0490449332f77627663e01a93824fc036aa74b734298485090dbfef6448dfc4faea059fcfd7a9fe73843427ac3fe979fdf1d54edb29e4690eb370e05902d7bceabf0e18a9d043786544f9a43e228d5dfafcef330774323176d1bbc0d3ea8ad3e0bf0903e9af246abdca2e118dce1d02a5b78095b48648230ac74c96aae7317c125c066f1ccb0c867654db9e162773181c68efe3474525a8d8c602b4fadde04b241d09bb6bd9d0c1c86af87c396f5b9ab0ad492e799732ea84f379d6405c9164e9980d81e93a6cb0e1343cba1d6ef123ec7175d98aca0b386c2d7ac0c6ed7b75b6040c8c49d7941fec9fae25728b5bdad45a8f89141ed9b0288eaf7d1808d2613e000275d958a2a6839b3d6067359ea83454862f3b7d2a50369a9ece3e85a5c5c86d002e979705b19597b80c17810ba736ccaee97c5bec8715b95b7d3fc438eeafdc0cbb82d200659b7d453b0d820ad0fc76bfac6c0d2a59e5e5ce55f62cb35c903d0d6b9f0bf34792cfaf49e6a32c7f0e64a4eec23cb981cb3d94e2cc3dd1f1e20f00900ff630a3709a0ed4309598af2b6a00c0647d9f6f25dd2406649cd2a648380491afedde6744fa51173b1ea5688ca1e31b798a201ba8748bc1607ca70f4ce10f334985f899a111712e093566d94540f899d927cd5f1db0c3da7e25aa2836700858ce9acaad293040ac3eebfbc99bc3a95fc9d74f7a0093b91c8a43c199f5f305e8716328a25da5a3a6fa18e914102ee598021c2ad54cba11bd7cad2c6dad9c05b0f409f08bcb8b0757b64a72979c5514232a8a21c26dd85a2e593128d552380e7d06973a657ab9a62b83fb36348f50862c1dfeffa35671a8ce77696182bb2f046a7d17947b01c8fc37ca8f8526d60aea01d285b69127edd6ba496f1034d7fd036013a2062f18d5a7dc3b858266f8e602700021a917016e6811a1b1d7ae660808efbbd964345c46a6b89e54a15deca57d383fb874a1134431a6f92c78ac1ba10f873d04c3038125b490b2a76d083f8e016c37aeb466e47fea51161dd6a1aa41015964296fe73879caed73a464b1bf43d7bd2d03f8f163000960bc6eace3c3d201c45c0078c13be242e5f46105d193e20fc3df0d7f65ab95fc436c64bb9849230ccbbba9774080d46fad3fe0c10427fec19323ed5f5f12ad294938597e8a286c0217beb6019fcf89e7d1ee8de34ce2a474b8a66e0d10aef966ab07ec2420ca6c0f40df450e23f534de437efcb27edcc15a3bbec10287018f20b6588d631a4a420b5859d2ad0fc9801ad1d01258d0c6c8cd4d9b95bb481252a991c36c98fac25b0125e5a9e23ce90a832ffeef5374c118798d77de09d9a38bf902c3ddfd4f0301069dee57315e7172c80f0680616cb15aa0d4017c53ed5b3c21863ea85d53f2e202 true -check_ring_signature 0f46efb9b2cdb697e391eb0925123285afdad53896352000a817b83aeb508c03 c26394e1aca534e1084556b02d9ebff7e57b69ab1cd45a02a402f3d90947af05 1 0e7d3eb4218ec13ae1dcaa361e0765af870354e6cd849bfe4189a2736c85287c 5015605cf9abe4ca679058578dcddb00c6aeac81273cfb6d85dfaff755588d01c6fd9a41390bfafdff88e4141f0e16826a2d04493ae316b3673d36d6a25a8e06 true -check_ring_signature f6d2c5db9f57fca3c124032a588abaa623e16373c859cecce4cbd95f175edde5 be3aaaae636683d032cea44982b860687072eb87bd5d8419e76c19075f8b3238 4 0f73c60791e9d89110da4e459a9c8a08df9bd87f95ebe1553c9605214e87862a 57dec9cd6476939b62c0d1743d3f2da84551486525917abd866415d8bc42cb50 627a0c2a9a5e279f17b1b949364cb62706bb6c56f01eafe7a6b21dee06858218 82e0318d21793d7a50de4cd3210681fee3a94db40d756de03bc8e73ebdfc3946 062f6896ea42d4a27eb2a760b1b3941ba7c36e655bc3e58499cc83cdddea1e00e07d7c00f7ed3accc20830485a5c561dd809e7fea35553cb7e5cdbdd939b5c0ea60030190addb34d76155254f5f290370e3ea93aaeadaab8be2401b1204a920d16e4e63c8d763bfd379b49ee4489de92b2b2eee1acc704570fe594d22052ce0670925c65613ffbc6d34c4202ccab7862d5d89bc567f8f22e748ff0227d2dd208f4fc34f51e511a28921abb92e7abd56e7e2e51996a30d7c5cef55665e5346c035b7b533dc0bdc490f347be660a8d95f1a897cb2c68fbd88927a3a969ef9a610e63594aa6d83863c9c689b8a51bb59fbc6076e3718a76dafe715865de4f62f104 true -check_ring_signature 90a4f5cbb309df17742da1383d53713aeb3996e77251e941cbf2684b87060ed8 907eeeaa224dc1f498e5cfd6a70fcbba470358f6e516f9f4fcfd94b5e6e7fc51 1 f4f44d7eaa108b80b3c67246d4e31c4d7daa0be506f42c38c06f92c907eeb3af 5cd76cdec1f8bd3ab899e86a91fc55f463b927ac8dd6d1e958d735b1b9e82cea8474092c7184e77dbda11d6f668426923e1001aaa73b33f872372f07c4867f6c false -check_ring_signature 4260279d1c39ff2e467888228089e6f5eb74fb938936874422b740c2d58b7d5b 1ceef3d3eddb1cf3ebb73a347927ca158e569d38481a26c9439cd687adeb67fa 1 b1ef542b5685e7b5b3ebc2e1ead4ef838d6f41acfced88430453e37a753ee50c 55352bed2aa9f74c1650ba961aecf8b08edcf22298cc43a13e074bca7bc10f0a7c9003ad0a9bd03c10610634670eb5b8451f4a3cc51ee4ad7d66f66963b1ab01 true -check_ring_signature 078f68c0315b486e9d2a79727800e80e098004b83e5702d47e4a210366bc6277 22458794f5039ad9a855713b008133a3e55c3e0f5cbe9436e5d4925d5d045dad 27 6642663257b3393202e0e00743892e10bbfc8d67aceb59900af4afc31c3bf435 223803fe112bd3b5bec88a4534fdf06931e967eba5e6d44970bcee04e828a397 7d0941a1d4903dae638c579ca3dd6c6abb1cabc1a1626a016d2d2f5af5030efc 0605232955da0feda4b223a4ef84366520a1e33d3207944dc84b3b0266b48ffa 39c6077051b82495eb3230604cb3c0f948639132a557cba7c2f1ec7b7f81257f c604743ba49d0bd2d5455bae57484cf3bf062c6e0aa34c38c48ec4d9e112c6d9 07200b59910aec496fff19dbf7fa66c37bf173b3743b906df498de032b456c09 9d8645b86afc9ac82770d96be610f74e711dd66360c06533eac56c7416c450ee 94ffedef7845321b38eb7a73ff83a1abae62a547a2518756867f081bb34c0116 4326d831bfcd094ef75e2bebef22d26593d25290101ca1a780d5954a3a73f4b7 908843a46e769e47ce5bf21cd0de022b5bda63922e3ecf80f35aeeadaa1e1da8 926b13018ea1f048ef02c64618af07b9f0e80dcbd8d7ea2224c866f354193aa9 56ea78aea1e267f84ec0400ed5b07df0c7182fc67634989418ed0c9828468c69 49dfa7dfbef5923a877992273c77233d06c1a72dc203d97cce2f6aec4a48a54b 6764dbbcabef9659e6184d1235dce63b38f69fa52e78c5dc5d958c6cf8f7ff0d 29d0bcae8b2595273baea6cb9e3883967b7d6512ea14ca86e7771511a80d6509 6466e5a47b2608bea1744ddc9503f03204dceed5544d87dea9bc53ad5cd29302 37017a1ea00999777402b0aad57f874ef479bb972c26a3cda8e2d535c05d4041 58c165d15de9d2724b31007af38b02486a9451c907905cf0a85e83af1d00eef8 8c9bf0b3afcd313d2c360105f6f7eaa1996fe74a0e1fda8a61aae48afdd050ee 749ab1b27af43a538432b546709421ccc6e0eff17989aa3fb373705147044e9b 6c3c3a779bbc479545daa40060f0db24c52d920b93c5a4f1aea5d12e9ea54d14 eda041363bd80adb2f0dfe5f5e5393f257a26e7e24549cb3c8ecd70b26f36a07 b903763e43e8f11a0d2ebb2b84fcb4526f63a42a86ed1dea3e085a604a574968 d40c88bb3fc7b7583079b0e1fd720f0894fb3114f7201d79c468e9cf291a80c3 ce220a43a8b6352bf018e573f9592fa2effc6ebec7db716254c149fe7eaa1b05 0347c17a44fbac174a24346bc0c3666caf361658d706623cdf6f1722a7caba34 2f5c3d3cc9650747345e77f4e003c6620ac7d475a7e58059b8e84f69cb0f8d0f7ee6c36a1b565d9b9d914e61dc18c3dd1bef492c76a19a3e940805f9b28ce80697107542d336d88e80be16970bdcb6a5f14f82574e28a5bb1b8909b69ea20209b6c19c8169969512a2defddc28f639ed8e096db75de61cf1083169b3b67d9041858af43da3957aa80ecaaa6603f139c493e846023ee24d9aec12bdbcb27b78000dfebf15fccc10af91389c848a1e6de819b5427b56bf4ffac22294af46c8230e2ec170a6ff367e40000f39d5e188959259e8b4f56b08d154fe9819b5844be7046c07c383d638d498eeacb5e57e59ddc78e2d124a8a3f88f7229ead8a327042049e9df260126166d09b12cc1f97ecb32b9e2149769c5a258d9bad14482b45a405f8f0c6ac2c8b351025b4d8869353b65966a06db28b1745a97b1b956d0e0e3005066af1711822b3a6ee97570859f5355a25635ecb9b6b1e4173761995f265990929e3358dc9cc4b1fb9092724da681ee50068266eaa760b9996beb94985acd60c0c9c39fe66eec877b357267d9a5f49056e8b07b6c9056ef28b906977e0a6210738c77140898ae727f65691eaab2300ac01058a7c79146f8ed105132e81b32f0d24b7c0239be8566d80f13192e6e1f60dafb5990b06530de3238f3c0545bfb50ca4c4405b0090f4662ffc1c74447f18bc82f0ae2637c865c6354d31c89cfd9b0bd1a3d06b2994bae5454d8d2348c5b3bdee13914ad50d7129d21922cc1f77e3003c85806b3f68b1b7cb65d1883ec78d7e5bd11b062defaa21dbbfb8fb46387f0b36d8707531f36daa04f6497a0fc92ad1624ee6f78859752b74e9aac14e8f69082d698c149abbd85a8ff7d43ec194249f56f57a3173182ed86a8f9b0babde2e0e82ee782816808279385fb3d9c2a0c9e8c14e628b8b2e5f16136ae637679b4c0e20fde61c9ecd4407263be9649c154f9954c82dd3bcd0e52097d9f8cdcb11300c48d9db22fe0389dc680a59d781ebcfc418a58142336e2582ccceb944a534da01cdf5117e15fe7f66f03b13f6e75762f193fb8f86e79a6714f26200f8691d420a557daebfec015fedf285eec7895f038b3cde31d5946edab2e407ccc3a96e9b0ecea39a8eb88d3bda75fcc4f91395e11649691605aebfe6b8f0258d288539cd0eb0d0cc851cf35fe1614c55b12a76d6af13d657aab070279e9eecebe9db645b0372c1673e074d07c23a4ffb02f05883994d4120e74a5a641c1a481a9ada0abe0577cd1013493df69f13388b209a024911c09311829018f00ed8af5ef5221979008f94169104724ce61d4afe0bcb4b32f4fdc138b938c28be9a20f91de8f8946089af7675e1acd46d03cd2cccd8eb4e14a687fe48481afc8693e2094da6f8e4f02d5f465dd985772619ff24fd702ec64ae3976ce5c483a76fd4758531af12b580eff3d060f199b25390f6a3b9ec84fe7913effcd46dcb5328b0f3ba4fbcf53b8019319cb0b56b6d98a41744888d3665b1e7d157a0f1b0dfe3c658c817edfd84f0cd2a3dc50bd26dc347b2b02a69e39037ec0eed0c17cb039890d1c1c4660f2350c1d76174af47b101df8bb2c5eb86bb7a0146ffa23e2280641f4104949b637d707f4600667cd9f93403353c85b0d6873015146be1e15b9ae8e6605043b1de73a04b16db3042cbd7c63b87476238e59ae4c1d90cbf590f976765e78d1e3533681016f20a9a00d5b27bac407d6937f75e5e5f40eeacd19899395f78730f322ea100a97352629b3baab560edf6cb162d5754a3b55ffa0e0442e25ab18a26b382bfe032288ace9ed0924cf10fe92e206a3a49872248e97e182af75764e8d13aedba30a6c1c1191098d93f33511a13e24039a678e2b10c328e7fad8dd8a40e77587ee0dcee21da07ad272641a9b8157160766c49921c50ae0301867634263373cb4be0f7a2e25cf92d4a538221b4ec15665edf28db1603c1b855014884da01cf1d909054a70a6808b4ea72c3fcac3ef063595a5c5337e24471cf998ace9f14f4e72690d63b5bfb25e7cbdbeab7015887736f5d6ae613c2b216f29fcd169aadaf92d75048d4709d75786c72cc90c1b0eaaa7db5f18e0d48cacc0a036f5481a20ce42120a8d377706913984b6b0a8736e353b5247ba442143c4f87d6564f5b56c8824380ed665e99382f2add78126bff0e7940913adc7a9d4f4c6b1715bdfad9ace2396093e82b58491d991e4b539f68da2767722f3958fd751fc19067a83626d86594a03d8e43170a4f00698b9457ea6c5320459a4d520a634119b61745cb7c5373be20adde2978eb9305737d0e5ac6f0e2346afe11e6b8cd1a47989a9878be99a87af0fb722b616cf9c53bc1a0df0e1f350ff38af776068d8142c5a526b87351930f30ab981f7ce2c72f84d5c0413c164deeadbd3e9612d51df1eea8ff4ee1df7b79f03 false -check_ring_signature d57acd9f961f03d020c1e2c4b155f20f8d0bb389434f35a7ba0dc3b831274dcc dba7b9bd98e38277b02d6637e44ba9fb6b585652cb6a99e2cd84e6b46f441ac7 204 281e7f97f6678e7ac4b1d84c49f9542b85ba841335ab41b77cda32e57314b55f 5add0a60b000557abb5b35ab987ba20f0bb97fa3588df749de6d875191d7fc2f 73844505872fb51a6c54edd20dab20603bd09f9326cc36d6453efd421d157b0f 5e62665479d6184e3daf6caa2230e000f51a7217f2373b6522a1bbd7881b3bde 98fbd11b914adbfe0638d8c951f6e74ac02632ff531660656cc7e4671ea747fb 28c9aaffea80abbe262957e509f58d96a9331267610068c7aabade22192e8387 9d1da5c27058a79e850c8585368295779dd4e3090d85b45095c6cbcb5217f5ca 53960f28b78bd4caa3965d84028866aa85e453b7ac6857a6a923c7ee222caf5d 74407ec145c16f553ae9a5d4eb7cb8e395267a22726362b720433e39df13cce2 859d7f7572b2babd26767547069e991432a0614c4b0a4809262d3d8f5580b6e1 bcf2e2b6d645adb6933239e2eae0f1169886d1db7ba996e53e9b1eb66fb0bbd8 fc1b11a5ab8e8f842c7e702c26afb01998de23ced3d27085c653566f263717b2 44b64c8d5b5b14cd60c718385ba04aa62e53b91933d4c80d1d717e54899a4249 73fc5be99ed164ecf2283e4c29a6cf42cd8ff0ac11c204e13a6373ba3cd3bea8 aff5b5344547909ea04b1a4aff8c4f76f7d1e723130001b5b6a6956e578bfa45 45f71d2e3ed8f83475b17993dda8c2ac2966407ec9de45177d0612cc036c9546 cebfaa81b699771176a6be3a7b28ed87ad7d68a190f203e6569a7dadb32c8eef 36be390aff4559e7a3784c296e65852d84fa0b8d889867172807df18095966d5 1db84a7a945cf0c447b9450d73fc69efadd030a5cba2abfe5a679c583a035207 0be1b764ed98315d47097d0d93531c5dae695bf169d80d12dad66b831449683c 7612cc610f420e82ed53dae19676962bb365bcd4a32cf37e1e36a65d22d63d36 0487771fd9c4c7c035494e869cb166480dbc77249f49a242c3d2ff90dff02cdb 02f0e310b38befa937e00b1946b4b8c56315c95dbe49ce1f59e2f8d5807ded07 65d5c361c1fc85773c28985f1701e30118543336cf6e77351cc1c4ef56fb1ea7 eabebf9b278cd9e54d1c2583a4b6a5312d7ca3ac5b94c9d1c2de831d7d16fc44 f7c5fb4da470539069be252959580afee5121bcf58d8640908251e261d20d306 c6961df1a6499674c2619f0263de08a36f04fd13831345666aea78ccedc5c4fe dfb8e854a5641a63e79cabeb4021adefe613dd7fa3c75243a9c3150faf4cfbea c4de367213a60bd9b71628a7ca1d2296de9750b203ff69c77371972404b0f6ad 2230f3de91f8f3364050623185cd692ebd1ed8b834ceda5545770ff0a87c92fa 79e8fc72c9ed4fe8fd2100a0e0d937de4e80c678126fbe58df62ee1b00fe5a66 9a4f5585828603dfa07169b28723a8d004633e04e8a327cd54e081dc71eff26d 2f6011ad43cef4330c67a6124b69e17e179c5f257fa08c3232e17f717bf86160 a40bc928f8e272046f86d2a1f906f3367fbba583cdc5f13801915334a1565d08 6951287ddfcc04df995c3717513e9646cc4028fdcc87d4a70098380ee924bc20 944a983d1e04c1209a32fb426aaa9bdbdd5ecc7106799b411a1d8397f86f4ffd 4e752404fbf9b1e2f1d2545ef4cd518e4b3bbb56740f6f3bdb9f346a29f5462e 74218f9aafb22a7e53d21b082aba29aa6a3b81847ec8303fb04e9cd5cf844dd6 3638d3ba38b61c89354e8b5330c22b15613ec801d677e9e52e0c26c2cd6fb658 d15a3277aa42f9c846a6d6fba375a29eb4a07c7ee322dcc6c3fecbcf1aeb5a13 f0dd01fff61b6d5b5ab24d147b80e173450e781d2c05ffbe8574b30294cb4bf2 09cbd71d52b8278e68b7987d69f69efca680f0261eceadbe165a8cda00cad4e7 5e5b2974b0a2ec04e7653b5f05c36a2ea8490ae87114d4dea0245059c5c6902b 90e81569a37fa3c3d8b6c1dadd6039990c382447872ad022ac2f8452df86eb6d d019269109eaba807f75e0906cbf3f355c28e65653e80e1437bd85fe9ee4e111 c20e69d8fb16a03730011a35a4f38e490431e4a71edd96feeae9500ac39af893 72f3eb8464ff664d54c5a33509b712fd6027f5201ab1bc32227339661da0fb45 4274db6e853e683c388d439cc33e8092e0d3a3430fb6c2a61d1e9b56076d7b26 80987ef66f179fb9c0ea3eb0f123b5f6761a2cd65ccb9f39caf7bdee1d9fca32 4f4998578192e9fec25979adebb539e2975b48c1f6746758888fe1566868fce7 a39e35f59295a62c08447b0fc9112482d51e4c8d8bad4b5b5ba1c0c6fb3fe301 42cf55bcb4520c5bbabdf724ff4542050b399b3bceb7ea6fc6eb6dbf16630575 cc5bd0ff1979f6f916b107fa99b63a2aaf0d72ee96bebf100d481323508f1d9e 14872802420ae07e2acf34b0cbd869c7a6035fc25b5bdbc475a7215810a29cb6 f6c4aa680b41c78ca604f9abf7119cf4467d8ab0f4f53ada7f5d870c281b45f3 3f4fc1833ec96a047f7c2a6c971c2d243ce884ae903a6956b4fa385dff412bc8 9fee026984a3cf11af9bac3c9314ea598161ddcaff94f27dc3d83ba84f82f799 4ea2f6684a97db81024fdb4ae8ccb7ea2c97a9ccc27477a40db5b0de00b832f9 66bc2fdac37cac239a8f83540c63c861a0e0776b085d2889e317dfd0b2cbe7f1 7ce5210650243c02f23246c426c85ca437a05c2d0161ebc43e7baadd76eddcaa fb59e7a2d3338e71cb3cc08769ce54e97b0052d8c06814d3cd579329968772ad e9c87462e615a56e697f6fc5623ce5e56e39736ac21096047fc93e1460af4ad5 f2db548eeb0983ff9692e970530b1106778ced1e5e63bab72b42fe9b8111cdf6 32bc863fbecce3a2a034e57aa9388ea1ede341b498010c02263edfdb88d575c5 25623db5f8ce845992a177fa210b26d798b46855e833affd1b950c3c446e4878 b7f766e7560dba4578efe8563265bde6f198cb2cf855d275c426739b901bb496 741de81ad1a339d7c221f1925258560cb0af44d07e317dd9f834324aae62554b 33b28790157a99fc931018bf3969d9dde08e1a350f3236c649b1def3f3743319 32854f81595bf66870ba4cbe342ada7378739ec28760565d40598a95908c9fb9 b0fe08e0478b944e3be08eb82381b894a295d627ec2cd5b11f41527ba0e41382 06421751b370b7e5b0e418c92bac5bf6f0d1ef4b9446154f6543387580323d2b ae280902a457cd0ab0895dcbb2233da644488185f404f1cf67d42be3c8e267e7 92a139ffda033288ebe6ca01e1ed49d0db82d66989e75a5b9cda0b5a3abe0591 1e50a0a0f2d63e7a3c8aa4b18523fdd384fea6d3e1b5bb5ef068be1d6d455c72 32bcd0b784ed8ba984676cca01128b618dadc7b7095472d65d8ff541567f0ebe 39a714fd44af7ad3bc6de41a12bce5ffe2ebd6e7cbab18dcf915647009141c0d 505a2d5c88267b154a21af1e1316e3116c5605292ffe36e099fdaebefff6f2cb f12b7cf3bcc432533e4f4aa532e860f3de841171b524e4b87c50eabf39f5887f 9e343817c230ca86806cba7b52d96bccb02fa0de4d98c2359ac78bd084d5dc3d 46144a33346ace1177cab024aab3a573208563ccb4c7ae683d8aa9179c407c05 63bf2d6aa3a36e862fbf0127b1b0e67eea5fe2c3c38ab4df7243c0f63c732575 7f7178dbe918fd8c28f51a0233aa684dfb9095d6ddcfe46d48723d386aad2d98 7c619bcd207e18c773047d58717f74c6b666daa7d89a3a9e3de88bd27b807817 43dd31bd4a2d391b299fe52077fcee810a8b4f6fa79638f93dcdc15a426d1608 daf0e61c1087c6bf92f82fd7363259d75a4bf10f31ed61dbe364a760d0306ad3 1eb1fe6f63510b7d481a34397ca74b755dad37f9b57de11010b332f9e40eda34 97ee3c25ee3c03e99c9e870876e2efa7e20cc31c5a4128b933a31cc8b0ddcd55 fe98b1cbe08b00e0f1047339b78f9ea6fc7583e26dc5a11ae6293b7d71a5dc5c 40bc1d2f8664c68021866623eb255eb2e58372bac10ea4c6909c47f437441ff0 a56f095574f36a501ecd62ee26b066bc26ff32287b382181941e33f07f2e4f37 fbecdbc0f866d5278d456cc2334e0f038758ea72ddc7f0186677636567d1f6df bb21fa7902fecc3f94b2af3ec051bc4be8604327cf146617cedae87d0eaf9ca7 5065fd7f2fa711ac6114201e4c359f231c1b0d45702c66dc158e7810fdbfe8f5 5ed2d10cb8373c5d88b7d17b311e688b8010755f88bfd81ef34abcdcda929aab d40ef74342f58fbb8aabe0b4c11e860f8d437fec6ec312b1a9c2bae346015f0c dc9f74789f73ee6752be42d533bbf818cf997c2cd9f30fc0763226965a5843f9 71e29e7d626fa257e1309f055c08754cbeb11a5d051172d0c056890febdb40f1 f8ab52fb743bcbccdd941ac73e0fa3c31d9f99af1008302e9407a2bf73249e32 ea44732dac630b1031ce0f8f7ba4f94261bc42f02807a157fc0f0ea247dae0b0 25ee9f8e26f9d66052b4c187af8b4970eff5f74d6c42a55c9725e2ab540172e5 a1477fb2007910c18f2a4be7050ccb391274dbfc276de7c263197815aa290c67 1ea6c88b8659932aa6825a00defc07a789489a0c7da3c5c66a69aa4e053e2cc7 cc957595f27299d9efd0dfdece42a1dd401e691bfced05635292fcdb9c233044 a454102f74ea75b2d245473f1d8b572c9911990daf8a5b10e19c86059e59fb3c 11bd987767b4dcdd54bb839af2ef3442193ecd9846edd69dcd11911124edf46b cabc53f1d6aa92eb2ef20b2307ff5e6bca311908a992ef1e1edba6bb8901bb17 0c993b27a9527c3d32754eae09041ca5565aa64fa786d8d0f3f7c184fb0d516d 6cc49eb6105e380d042c92a9eed94fe13b840dcdedf2753865e8cec3247a17ae 453baa34cbce39da4653d4d9d9f0167a5817995ae6b95823828ea24d9c9cca42 f7cfcbaf46aefdfebad2231150c5a2c969cb0fecafabf67f752bd959ade59ec4 d3b00a190b10179d2f55cf94cd887e68a1046d74d6f0db189f86c3a30fd7dcfe 77873b262b1e58c4a7dfebf9fd09b2ab1f18eb55c2af06ec0fd962130aef5b91 ac5621cc9a4b3dce4767f0c13f5e802d57fd45f91d98b989430526adb1961f4b 0e9aace66fc15ca79769aa3d707a20514629c3c6965ff8f1b9a44815e5f2aa61 b043c10a24ea3bfc6c95e2668ecca40a2cc803d44e042b72620083669da1e2c1 dfd1252dcde6b38331d86555b82a960573f5773b31a60f510b15b5e2f4e05c9f 5ef97e60e61ede58c1f71b0ac9ea0e9c6861340ce0d7a43cc905ae0054113523 a5f0d8a77ac865b935c17d5b1e4e5fd5b73951e1f8bed438bb327c83aee53d6f c91947728cf11d6c951ee6fea97522c1b3412e24e4cde66b5c05cbff46d20990 2f37eb8f3a0389427e1e660737ace30b97e690572fe4afefa67233837d58106e 70a89ba262706e43e71429687e70dce6d9c0e4b4cd5d776a632f6af6f806d2ee 9068743ea0dbe8c14d29fae5d32c03f6829c4b8eb687b8a2bc33f2ea2c5ea269 7b7e3115788695cb86a45113bfb97ea4a8d04e7286737d39d1358b5f67bf73d3 8bd6eb8d5fc11a8ffe90aaf5bbf580167a6ebc4eb25b04f13603aefd35b00d17 b20f91fbe10f6cfdf80fdb88bcddebf25538dc780292e6c7ed404e4b59eb5804 5f7a725ec083ebe38e18cdaeeaa8f0cae4e0c67ac9b64fa9cf4ac4f12d3df96f 386417849c427e5d3ac81ca8381cbd8a3fe4692e66a290adb60a0cddebc6d45d 430b121235d585e2afab4f3ae640deb915329247dc7f501cecae5150345a8149 0612dcb4bdf808ba171496fdc44f0ec607495b58aab981f5d8bee300ae0608d0 5f6ee4ecdafad5ceab6506f076516ebc2e8995c4561462e9febcce2f7d9d84b9 e18326636c1780514062a9973c35c6470a59a98240c6c9e55474edc16ef81d52 f043f0ddaf38bc8546996f74d82786e8a2320778db03a1959e3f4a19a54426ee ee78035756872f786e71d0f0fb57edb94779c3f38d6ec2b77172c80a2fc01968 f2e3974f21d0706043bdd593609fb23155d3d5964dc43849b13250c0cbbf7a0b 471088ee099131aeff09933f94374074aca266c9ac76baf0f799cd7fe6b3c3c4 c574b860f345077439b9063e3244d1096fd275aa232d93920de90b714b44e290 37e8badbb33e85062735b4424bf355a91539e88863419eb145ebc0c18687f70a 9f0c4ecb98049331ac55cd8fcbfbca8a18550d8d4f7f5d6570ade2b07915458a 00da4ab18f71748ac9077829a8371cad049593fb0d510eb56d0bdb37846c882b 189ba8d172219d13e1a403da9ca9f9f5c31fda4f5646c282b0e319fe6bfa3aa3 e1d450f739d1a1a3568d7e6cbc24bce2ae32c410e2ba989de1416857248b8639 1bfe0a35f27302cf7cfccd678b83b9ed33e32bcca6a6bf613704651b8a3ebeaa edbce2db84439a5049f1d10722feaa35ca4c034f4046d5b80d81895feb071da0 e12c210db4e6121b31a26722ae217068f8a41f7a74ceefbd052d5886d530091e b2ad3355881e4d42c4523214d08edeca603a4f9f6f80ccedfdfd2de2091cb43a ee4e0b3e86b94b3288b75c3ef1e0d5f55832fc6caf0977abc3e0592f6456de52 ca8a524c776b56dddaf5286c5194a0dd7d13b0eff2bbbcc74c2a543865af78f3 cae06b934c051464b8ae378dacd1420feb85fe209441a0d5e2b89dd068359b5b 2d892e1c9f97494f52f6c389b682e696a3504ac20aea7e16655c31c16f008b33 94e2c6f1738a436a203f6ece17d2e97801fd23e768aa196c3cb530366220aa8c 7cf991f5a4e4f7d1dc09030f16ceb5b5b0f5d7d156dc1ef518fd58e67923a55a bf1b06c6b10ee23c8c707c452347f012a6fbda63e348a38a0f8bf5242a584435 baa6f5ed4b54e4fbb4de4f6d9ea364c17c5615fe1e6acc934dbf62249e58f7cc 730dc51128d08bcacd9469c67ad924a4effe6173bf3db2c1dd2b2d38b2e63ef9 d6b2743ab86cf65df1bfa337e71799a26c196c52f6d92bf3bad76e9480acac2a a71313793b34814cbef9028aac476c794f36acdc466ca265b4aa8b502d791781 1fabccc3c004567fa8bcb22b09aefed05cab522188e589c05b1bbcf10f9377dd 9804e7892030503a2dbbb909a40ca79287c032b76bc036ada8cc56f7fc11a2b7 144eb044094fc9bf791e8da68c08fc5cca8791aa42343b543c2da19bcde9ace5 c4c5b5ebb2c0154be9ce1df35126c1db53d4e2f3048ce221c1b9be50d5b829ea a523a8fb0620536ab50631b3c4955f9399c8a7783603f0587fc7c43c2756eaef 6fd5ca552a33fe3767e9d4a9517ab3b8f46bf12941106379e873f90e154eedd6 d597e14fdf1fd752d633645710874f6cb954d4940157b250983fb95c9a73bbad 1810428bd7b73441499051086cc249c60cf4aad6a4d9cd8c5df004afc5770ac3 b9dffcd0823e90d4a4439eec1443f164238a63db323d3abb22cec6529eb85fe9 c42831e1d00cb821a144f488065e232ccce6bfded05c228307d9e7de8accfe3c b47f048255e9841ff3e1b6256cbc2faf828f674401a9e948833ae6deccb5b394 6cb8eaf0b7cdefdac623a16282580b76bfb45ace818f2e7594a59b37d58b17b6 4381e7ce843e051e21e3b627279f340446db0b4d1bc4e39a482ea5d5196881f1 cf9fb6aea25a9d85d216a85dfd13fda5fd20731116334d7439b3daa9fbcdb3dc 1dd72c1f00000052f4f4a5d5f19b5727b3eb14c04397a4590bd083c2af69f7dc 33dbb07005094071a732bfd764a36d9f6d2a3eb10e20e7b92bde85bbda4863f7 d68834f3326be28fde67ec7238f10cb410101ee4b438dcce62f76058bdf58fc3 3921247e65566aa864ee434670b6820639e504f5293bd24c46bf5af0b8faf59c 86e6171e5a4621acf6f542ed5430cc307204f916b7bdb1432e8c61bba46b1664 8477d518ff258669f5961762221bb8af71b02cd0c41a74100e7f866fdd29ed75 20fa91baa8302fcb2b8bfbd29285257fc7146ddf4cae837ec248373a9112ff6a b3f4437027d44128e743575eebfb10bad35b2b145ef0c24e0b34a69db276b0eb 04abc96834e92cc37d10df851ac319a64f2df014cdeda8d7c9d29af0ffa17b67 10a6813ad27bbbb367fb6ba297b3f504dff3c3a6a7cd7d99408b8a6e0c778fb4 ee2ef6c297891aab5413b81ea99656eb35c944a194cbea33b7e9934d439be9af 9c45bf6047476879ffb048dc0477dbb3a3063c10268e230008f9853598f6caf2 aafbbfbb8ac70c56ed7e27125e0b933a9872769cbad2f72316405b2185dce853 33d7e1c0423d82ac413fd99ebeb14549cbfeced2f99d4a1c790e6bd3c49dfadf cc9ca25dde944e9a8bdca747162db7840ae3b5449a5cf61b1b8dcf074b3691ee e4e570fe8f68e76493b9123c6ea50dd60f75731eae978f0b7f456af31f4753b2 ed672a3b22eea6f1db96138f009a8796d280a8e7b54c81edf313d6b2c38cb847 de2ae189761862cbe969f4c64944eaea4b11f2aab63e499203601b1946d3d4df 38614b84e4e2e2e5903fd746e020af0b530549cef308b6ee832d65247e5ba2ed e13deb6c3c472bfba773379a40f43d4d00fc70dd346ba44e4863b4958e18cff8 b029ceb4f7bf2130df481407c9ac5cce9037e4276017792a6de1993ebecc7180 14de2e2c49328fbae99488c88585d558f8a7ccef8dca8371b23f4813a8ed998c 294157f2653c1e4f0968d55e16ab02343f6832375170a5fba325a04c53ba2c91 205e1a69470470d484a8aeb691cb327a46934152df090841441178e9fd0a311b c73ac62c6028ed29843d79f2bfdf5930b1de34c5de81f3bf24336f2db321d2a6 93076827490c5115156f6bbe21c61f72885f234894bf752a7d003eb6ca9dfd83 878b7f094c7a05a511996636d0513d4a69b8af4fbf7d5467bd7330eadf350e0c 1793b95cfd331a4ffdfcb0ff399cf0f7fa1a1ce14278943879fed2c38a47529f 3e4d3a998349512547e073d0fdc6f8560abd867bca08d4e2d20889a31baf71b9 9829f94a0ada8ac7b43668d23126a35f09425ee85fe115c0972ea53f2fe2a744 7eae6b08e798d4d941206cce8f7548c5b22718ea82d69635fe5aa4edc5fa6902 3de4c213929b27241f9b6471478cb559088ac24e9950f18212b1db0baf875a72 068371f64e3c794c449432f9e86fff34f1daebcedfdf9e81f3c51f35fb12bcda 971c3679aa16d9591b73b0ad0a2af612af6273879a0650c909c4c9ab19c681a7 d7cc2023e478ae713895ea64bb31a7ef288152f4d774d1efb404befe4364260dc96ec7725f785ff877b945c93360addbb582a7b2a4600871b71e1a570f37ae0c1a91e5b4f6d3a803473575ba23f230b2e9110c209a016937ff7188ee5150da07b4322c1596906c5abc3e1d09237488e6d10e20261e1a84c2beb8ed66ca03990f1c064a48c9a72b9592cf668d2b589a5b70d11fd8d7c4f05cb3d4f3b2797837060b5b079ac9118e7f7a2cf579c64e8ad00d1025959e48b4706a952d9601e98708311eee0f3735025e16914fdaefc861342b0ed4e4e36e12505f7083ea1eb26d090b1c6290fbc1ff2f28f9a2f702a0077cc5e43d72caa7f6ec9bbeb991eded0500cb640d23dbad81761a1dc1a8d4eb18c0116437f40217dab25306b719fff21f0af168f04108248b3a37c1baaed0391889ea34a1a16dcd837ff912f0ce2573dd0a50b84c0afa20133327705c06ca10400bbaa1af7c9645289a0476b0141fb7b90f0433b20a9ea1bcf0bf68e6fca9ac21038166c38300a8f640079c8d81276c150d102380d097fec7c0d5217cf625cc87af9d80fd2e20c3b8365c26faa681c114062b18ba62d7cbe881193f5711136de6b9703f05bfdf17bfa733ef8aca55244006191dd0d6ee1981d19c194b1b91d4559ce1d9a9ee99e6ed0f2329be549d1c0405592c655c1794b08945dc2ffe2d43623a498b679bec3f86b4c18b9a89ac288506378dc3e52b158a2de0287553bc05bb081cd2459c23bc5009f63054b894032400cbebcdf85113e3f0ce0c15d4a37b42cf4c10d1f5a6dc04b3cdb44f172ffc7e068961abdb37fccfce6ecb2d4b7199cf376854f3f7a024d5d81aee5c85c597b103220af4be71ebe54a6c1364ff9c7b65c49e39a6a48146544261e753c8eb2ac605cdb8dd6161dda47e857effdb5cdb4ca3c12112a46a586ea3097de4feed76bf01853264a962ce277c717ae2efcca812fe056d25afa8d1042359cfaf8a8cf2b50ae68892e5c1bc1068fbc2101f1c19ac6c7e58698b09dcc1623be4b5e8bd00ac0cbcde797efa231c23945a487298338fb131452a57aa225d45cc0cf01cc9a4540f1a44c8ef85baf66aeba158f791dce0ff4cfd5091abbeab4919d49f91b3ea670a4eab8e7cc2e57934dc45e60b03dfcc51dbc489fe218dc23a08908641102d3a0dd1a14f8c68236225e4d8cbd4cb7c2aad2b204161ea71ec9094002e0779741f0ffee978f4b2e255fad96aaec7a032709c915626094c03c5405c694976b9671a0606b19bfc4f5bbfc19d55660b6e6d1824ab8fc116f2e832040da20abad42fed035173b64d49dd84e8b57ea97f5cd41174c0a0fbb6b0afe3551716fe0a43d32c0317b104b687262168100cfd1eaa26781530eb73b7b36c075c4b8716e1abc15d078a1687c2b5a74b14b4b54984935aa6dca3e70f308f8d971fa3041368109fcf07c806b025eb2164c8f5ef272418597da57efbb887eafe8a7a5de01296b835ee0426909f8046e5b5736d928778bb0b947f9948fff6cc45513774c6fae9dcd3f30b5ed41232d51531d94ae5025a972c2ce62a281815a8d069b5d7cb4fb7a118e80293308f8f795b6f15c6c7925386837810276d366768c052009e374c0c2eba4107513749f30028f4b5d255bd17bc828b64edd44d5698d95fa5c35db1df9548e2026327aff06dc1cc2438500f24d66ee110c1a58bbaa3a2375e96dd330d3285a700b7325f06308f2b3cfd949a8da5d7705955b1580200a622899f92759882a2f80e480faeddfc59308c051e5a122ec5e6291ac701cf162290a5e9617636cda0b60126a635fce5addf416b0a91091b1b81896a3d4ee8d9cc7ec9ee28fed4d9da560f0eb07961e6d312bb430a747ca0427fa4dc24ee6c1dd095a642a8add0b204f6019a110975a9f543ee6103ec769d5f342264e71610df12fcc74c02edf32564fc07ab346cf5e773a399b2bed06299104e30effb521da099d71abf9625ad2c370c024ec2000e5de89b9103066ac4549b166bc00b6ca28c7b2e78fef9adaa0825dc03988d75217ee9e6e995f7595b609014e05580351b32f228d3d9a5ea59a50ba903c73d2f1b5228f1265db145cda697228769134093c5026fc9eafe23e07c720306b5caf33de69224f0a86d6f4098ad25521eb6f9c74d1016f898b3b7feda5adc015ee7a2dc2a7bc23ad7e9cc18fb5a0d330ba87c135b8863123dabd7e5b6945d0d7f46cfeeaa1fbd40e915ed10459f9c1d8191576b5b31f9a4dc0bcfcb9abd300590bd14b4638a135dd5576797ce664eecfab4dbadfbf558424c525fa591ce950b113e6892b52c9320c38cab06faffd493f6f67abb8c91aa171d905d80ec338e073df5fbd37315234f4551aa2dfc2f2fcdaef730cb8cff4bbc7d1601749376a10432b421dae56af0d344646b4e9f26fab953c15854b8e9f5b2502a008acbbd6407a9a0c60d7c78caa3cb55404978686b58c7d511aea10a55fca75dbe45ae423104487ee95a5628c0683b0782011a8f97c0385dfb8315876af23886bc7f36d91301f1e8f5560cbe6a6e8197ccf85ad2db89702f5d5649192a3bb5eb434bb06f34064d2d5eeeaeff3a0403ad9d5540dbc77e8bdc26a791ad9246f8fa0914af386004ab3643d12b404934a1e8e3e453c9f9d4fe09f41f14329fd132cf83794151500df1e5e34ff8d2dd55df5ff59eb314e6b984fc5e0e42d2e90a0801cff9e07f3a023fd298a218fcda6593d23ca1540fc3dc7d99387015b7f82c62fb3f166072750f7ae9df06b3bdb426550871779b8a9059fd22355523ad1be8ddbbd03c9c8a270c429d40119491575c0ce1a2a31ff196c15bac5f25c7b829f94e9b8ee71e215e00fbe181a4285148d41f889f489784de2c7f8444c50284e4515f583624ff40b408774fe51600fab0b7c1984c8f818ebf74a76d664cbb277ca0f990c385624b7a0d2a746dae5def3c4b73b5528e11fb4a3cafdf0ca5373572af2cac304a98c8050b63d9e5345ff4ddbdcf6194297510e8b755478e423983ee162905f6d198a6b8014322dd1283262a02c89697ef4b64ec378f53c5113db1457b4729e587b78ebe021c1d0961c847c0cb54247a46bdbbd784af34275ab02b3644c3e7e14f3ecc4602a79807e6e84b737118589d282233762ee2ec4b727e41f310f8bbfbecbb58d10dd3125505424740f1447c984a56adcf01711c03e809bf9a3ebe58ae61796ded0d866f608030949ed068f4849ec58ba8a045ed78f3aeb5110de2039a8a5e055601815a66bb5b53a4d015eb7261c5e81053308e75a4f3e33fed77629255c9e42c08d056ec3c530629ca7135bf57f0ac682baefe6e4d80e39ccae446c814f49ddf0b0b43a910e838c05e47ad87a2b2f519250bb819dbb24a3a1cf3a2e2f8cd18c805add59c80770c9a514b3662fa6c606cc6b691b545f05f4d4e182c52b8d972b101ab2f61149f71adad2a0ec67c3c610af19e20a6e76a58d988bade4355062e2a0190063c525177e841a6049b8d5b9515e9fe25d9d252b4cd34ffc0e3b9566f0709c1acb2eaa1ccee6dde6bc36134506066ea5276169218c10be297001ceecbe007fb655e0530ea0bb12cd401160f7120703cb65e9221236bbfa0bbd06ca13fd003dc93db6d40e6ba55e3c519163a9a35b38f54960b4b2426246c7d9a782eff780d838789f1379f212c55ca9022d7dfc819570d4f79bbcb2892e0e113114f9fe80aed7192bdf557e71124c27b23040de6cefccfe24aa5c79a5e5efd7c7d843eb005eb6d1a85cd80ab12d95193fb1bb4b8ba509effc4e210ad94b3d38a2b93c56d08054b0d2ba0fcea3fc84f5f7a928a90a48299e0c85bea47218f9ae0eb315c57012b3a3874e609f2cc1ff271d62e67a8ba445060b823fa52ead87ea03fdcee150bc3c7d336b8b871536bb7b9d5a532d8c6a322504c2133f80a2c6d99eeb710190a91a4d53c0963fd388897fe38d0dadd474d7f9ef8413d9a7c9e3de3c2f2a9540cb5ec9b58780af2c560407af21402598483c8827278d35835d90deb8547b0b60cceb22bd743bd3914a002a76129cbd3bcf2600ac35b8b7222047a7c6cc57b9205a68ef41ce26168eb9e679aded8f06c032bbecb2b8a3f31a044cf9bfee18c08036907c1070f2c7048e0825f32f9fdd2b668ad2c52594a717802fe6b4091a1dc00be5cbe639a20e09976ce4ec86f64d1ed5c29f79bad19659ac339ac0ed4b5ec03e849e73139d8707df68d38bf8bdd1e071c0238b3ab519237ed65b84a5656e601c9c7970c53972108d4ed51841c36291878c09ee26213486ff4080be5782e7b093c39c8a711a2e956d84e538a2f558336cf8ab47be82d0a6b5881315f312d2e01ac312eaaf8348741fcf4ef0dd5d0c961e81858c246df4c3361e6fa41f8a3d50e42f2e5343eb686376975550156ca06f695423b86cf0d533038998ff906f71903ea8392a9a78dae34c7fb1f208ebfba68ca1673654ab449ada7c239f59181fa0a946ce96924720a3067322ddf75468fc60babf59bbcdd200719b4c6d89d83da0261ecc387b444975ed182a8fd777552684fbec8ab7985949743ae5eccf7cff30759ac0df5fae01a5b40fa49ad94045eca770029e237f5d30a9ab3fbd7005e160f17218ca6cd4729d380518e6e0e2e0fa58ba0e50b7b4ae9f65f59fc90adcd09051f349173826d8d41b6815d411ee56592739b9c4a7f847ea3a37be25ce3ffc10ab9955e89823274b0016b862212b1847151081b26ee1904e138957f7850f14804534e29cb201638055ee431787afcb2cd2a64e9e440e278eeefc1152285da0e0e9b45c65a4851b71a5cc110d8305abe60f3fc8b39f9153bc92b3f8c6d0d072604396d1058663c471449ed621cf96227e24c0d2145653da2d43c56a65f1801920bcc5de62117a352ad005fd93456b56357aeb5713541ff9883b190ec845bd62807e2dcbd67c158c7932afd819fe9075f0c08421bdacb5b3aa572301faa701bc80dabc049aa8bfbea9016633faa24e1e87b7542faf8c130d870c2f99f110f3ee00b007ebdc4ad9f28d7671e3e25d804545e46259f7322c79d1a3b9b9ca8d15d94063bb715d50a4715fcba1cffdc628d013e7410fbec7e66fab8611e5449e66f880bf57d1d097efd245d5f037e57683f394c3f21241c14ca6d0b4fd60348b2ae9a01f31ea5b9484e3b42094f8649b2bdfcced1ee3642aa27d8e9d49654cd3e942906dd57debc1c8bb114a415188cd3f1930de5aae1105fdbf68773835074704c7a06b0dec02810616700da8f9053fc67e022f7dae0a67b0d74781f7886a79e659904db3277040b73d18ea950f0d19bebf2e7a92540da6e2b8002a87b4ee909c7da04e5eb86855a0428a8802417c7f5992bab46435febfc4518c6abb681d3bb3c9005e54fab1fd360760964dbb5b20558f56e99125708d368dc8d8b6263c37b65f408ff3b1c9142203a225ddf38b76c7f15283308ed39fc8031a674841944ca2354030dae92019191d7ab34210c4774f5d7ee5a216d2921ac8c0dee699c4ed519730e31a1b73eae32858224844ba2590ce6e893ea6d005850b10aa28e2bf0af4d860b0588ca2675a13ce1f3cbe6c4ef8f7641a01be97d31c9f78e3ba9a8f99e762c08ab1ff174c97042a021888ac4dbfbf7cf5195c1be01a29ce82ef7b603b6e5730932b5ca6cf81ec0a34f2074d140cfe5b2aeb61e282c720464d44e2c064d1c7106e054f0ae5f6c5872853fa528fa52094622e76be15bb0e2feada056759ee01c0002a169bf6adb8cae49530d54a304456e9ba1a4446b3eb48b0eb29fc4cc232e08bdec18c3d78268343b743cd78cab46754dd8b0403f5018b455178c71d261250a331e61788c3ccd2f5f409ae1aace453fd6460023ac48f3f53033769b5b2aa20e66bc3aa7bfb32c7e7c326c65e99460f4ced9a7c3d853570c66968215bc967a0d88c7cf116a15fb2ff149152a1366beee04ba5158e0789e30888f3b2680424f02274215d369ab592c1dc812667b06215522ba05dbe320c222856bbda057e36a0d8e44761d73fdc92267649a87a506cae896e45e2193a9f67d998317bbf6090208957329b753a8ec5f5468cad2ad888784ea0c3f05f3ff7074f80f8657b2baa70411e0ba9f07c1b330a03f20a644fbe7ece1dd0f7b84fa6713ba89196b2aeb3607e5ece35b532f86f1d59348ab69901f5a0f93fe8f5cd927a6bf950a4dfa21ee0fb8af75e83eb664c9f1e3c240f0993cafa87d94a6ebf1d8fb620df9d98303de0269182a91f69c2dda113b6a0a3b092cf1cb2d4f51cdf4e36bdd12feb9cc49ec0376e208436ee25674b856d1c851fd5e2763a4f89e48954e4b39e44eaab215290bc4b230bb2021049ae06a94f4a3a9eb07377ead47aa31739c0181cba13872bd037ba139165078f7e7bce51b0f6f397d9c167aad6b9f0deb7e6040b42758d46305baaef1d3d409ba27c47493fe83f6cd953fb7afde4ecef15b043722c3bd8d60016965f8311d9bd35f4a97c195260efe8ce07a21aae335a1a48fb410e01d5ef40fd9a1038eed40bcbf3c8cc5b039c78bd4758e0e1466ca620b2f13fcfea87bdb06ea9ed1d6c74f59c8639d2fd459f8be6ffba04407a503264ee16b35d1475adc08ec157d362d3a74ab32e7dd860165dc30fe6f1d87ef889e4f385acdc992491e06b1907c0148c04c3ede1f7a1decfe5daae567b435763a88367e9079899830b005288cc467f5b5c56cf04585b2b4c51b2729f651a2bc8c890e1dbf64755efc1c07c0710a0a9cc81fa3dd512e8d5b2d1f2be016b128fc40851762c6c71b968b2b09eb91b90d7e93591c80bb32e437e0a0d4f3d21493c7c4a7a0009715899e09c304f0f7163e7ab327144f778ead2287f43f00460fe7dafe947b95ef10be8ee2e3057485fb49873fa75b7ab42ca24ab05487140a2f2456895dde4b8ff0c04547ef03a68799f8b8b239377d72e6658c17e14bee9bcb1fdef7ed516dc0dc77ada9b00dc8dba90f8d72a4e874cf16c1b499c9dd1122e2bace4b05d1f39e70e98fa2cc0c93a9b9613b08d83cc2085c173a8e62518ffd5188a99d4c9829fb292f28cc9007012a5d2a4a40acdac38b1e710f16cc2cb819608a586bc3ab91fb440bda1ef80ee96103677a02a1e3599339601b959486757186297551b5fa1d1ba643114a680724ed74e5fd75980ea0fa7ebb79fdd1629e4ccd10ac5ae9bcf14f2cfd617d0700f3cf6f4fbc972cdb4de7a9433317cdfd3cd200880ce6b45d68d617565916f104fc856331557fbaa52565a1a7ebc39be26d337122fa8c3d68d3ae209fe8a84d0f014820a6ec9e1dea0b60fac19ac5d2c0b0919c99e5b6f544bd0b4af934cffc0144064d39d5f9eb789098205986081258ca8632329615d0073170f6eb478e450f6eca6bf82d4d09686443053b2421da152cef0a49868ee953e4724dbb2f2ab7056802ee0441a028ca6c1722fbe6e81142ec72732b59ddca6fb36835b1d023e3033a6a86806605c0d825c58e3d58ed3e47c62a4f761e6ab8ef80e463ca03c588091f335cb255e4b170a56839d1bd8aaeae19fbab7871020b1dbe916d7e80eadb016e339b3e768a101117bea6c128bafba2410d4bec9177999789187d05557dc4057ad8bd75e2f591e113a5b92ad75a1d3d0db1e180a763f3a832b3928710953d008e8234216cdc8765fd65743701ad43b0625d66c430efcf0cfb5070de46f42b07b01b88dd03eefde3d8f17f317a4873992525cfa5b84dc1c58dfbd58f72fe1d0b38de61306afa8ea0bbcccafced590b89204120949eb148abd4cfbaca85ed0e068cbd6d19af54c664b340e8c97bdaab7ed204b958165509ffd647edb1e23d4a0163ddd0bc50cb49107102f58e71e88857f471ab09f8c683a0a6b86ed831524f0442deb0878bd78a22e1e7e627ae23d17808ffddf0e1d714d2b2dd1d069ae1f707b5c634b3c9226c83bdbda5ce0d3c8063e822eb79e590eb9714395284934ddd01be593299568e04b10129baf7764a25b2f1ca48ad95e72733b39144ac9377d70a366254c5fae1187fd9c0ad2ce66da67e3f06fac8eaba2566e79591e4c511090240799d0af682009b293383fb7080d256056e2de3ede196e88d0c7b960d8ac50842135ab42245b5e5423599cca06271781999eb001a8855c51d824f5d1c090006cfab96d6aae2ff6f35c61596af006441b22de171692049d0cd00c4b17e5861088d9ebcb202ab3975559d4ae34e7978106b03bbf41c06e45258eb9e1b501e7407110ea4b36c9db0d42d7f1a237276df74d4289c7d65297a9a660e1c8829cbcc0c7f027a5746f188075a6a27cf8533e722017164140f03e841dcb3d89ebc44d102719b0ea902b5b254834e499515e776c825ff9821992d79f7a40d64a0f230ed06d9cf7ee8a9ed23b57879b1c1978845e6ff1d7a2a27a5db85ccbd5f3957834a092f83f245c5070e36312166da60b6afc5e996bd44a29ec3254588b08c4fd9a007242f5d1692502c3cb1e3591415236d681a1d73c4c601d0c418b6d78a8a2a440aa02440bd9d3c5abec53784d3e2a1f2c422e6184ab3ab7a6767fe96f0d0645e01abe36ba5d2cfa56aa46df3fa2441f4c21aa4436a8161dda30a75952fb86f6b0c2781a0c3fe649fdfd14b53ec4ee40cd301689a15ab8560a341cfd3ecb2d3d00dd18c31deb703383aa0b18f1db4b48f4bd4f5743b7fcac03976d601be482ebe0ddab1c8326344ff248c19dd4a710dce875a4367f03092cee0a9fae119995649072e76292b2a89b7ef1bfcaed1007f8a7fa82e5a1c300719b1eaf4da7a86925f06af88e8230dc236c4f5dfc3e781e8a451c8f9fb7136d3dcebd8530b6e522a5d098105b8822354aa2846ce33abcdebf22ef28fd65084e19c229481e465c8a4c206850bbec41ece75d897f8213e9414f06b71058d28fb078ff66c76fcdb9ffafb0d4198d91100d686e597d585be24c1111884bc385cc91e9b733452ea743a6ecb076ed7ccd001c7eff0a6fd0854d6f5a4badad5d3925bc1cde939a3c4b4a58db103fdb78f621319adc412d17480c913b054df0dc9a0f031c02ad72ce146a6a29308a4645419d0bb37f0404b7ae228be99777b36200994d9153bcabbcdd3d77951061c9669583f96726cade7c3e238b37f5f0ce5bc6793f32de306d66661e2dced041b04f525077f5eb16ad2c4a70bbfa9b921470cbb8513350714adf5e64dcc7c082a874e44eba8bfe0d8105975613b87676a792f75ea6761fafa714b930afc480c5aecc8ac51056e87fd763dcb5cd025c649e759075e927bb0dc237c29584af2056c9fa0b7bbd551e1cfd9cf0cbc4829245cac3c59a21cd3d51be6c194f44b55052cf5a22cac17fd15aa9a11f64bc8cb70f1955b10ff3650b8972da8d920664b04359d1edc1ae3b673ae5b619df0cbeb15210f471571b9079c24ff8445eb98fd0521cc669b2f778ab00052d28f5596800d7127b9a2b49e32178cf1f7f530a8970864162a052766a9c7b245fd3587fca24f823d3e1fb5998ddb4553516bc4f8630797a8c8fc4ff44984a6cf8438fb66d8f67fe3c941d154eb3f1306ee59a0cae00e02481ee3db8973696a587263efe34da9435e101919d9ca648d4982f712e34b0564a164a0e97632dd639a910c879ef0c38f91bfc8129ccbdbe33c501110aa8907c693bf27748dd5de480f50c71f4095ff5f2e69be80fe0aa76ea4f28173f9de0b2110a0de2e110fab4d9f8fab31ac83a240cdfff54f36c2aeee92534a47552e0441f30d0227741fd0d872d55428bfb6da43f5a6c65e40696fb3e09b7cce56e80f765b1b2bf810a9ce2d19d7a7cefa9b3f3c2fd01672a6af4795fb4d5a51b6e10d010ce0b6f32d874ecd0bb7ddec4eb676d04123afd58f76a63aafa7b2b96bf708b23403ef59eeca2bdd46c309b68947bb8725d7bddd7972c36af0d2cf59e7270ec2f880e96ad0a979e208357a94f80df1f9ba0cb56d129ec600f7c2eca13e9503153e057204801f33fb1d50a55c376308220a6b21ca7d9848d9f951c3e17a86078b5de759a8fb18d3b504c551410a4f6af08aa4375b9408d3efb0215d39256105be06e6806c37ee57685c4db4c31543bf642ef4dc2a7e22919976a580fd9fdb050c5ace2be389ddbc12665eba36edbe28f7ac5754ad264c0ca786c2eddf0dd70808a2232af898a20f39301c75564ed2fd73a7d7b373870700d9e9de893cdd6b04e348c66c2f9f898dc5ccd5396b4ee2da44a733075fc521903fb2d1312f571203d180b8bbf699c0dc7baebf10ffd08d8ac6b2e79510155f9e6e0f6d8e58901f05eacf8e97088c5f84c173b0e16eae30e40ec8d8b21089ec680b4726479f30980ef5e3ebac95b92a0b2d2b253b2443058d6c15ba78b510a1ba8fdaf0be22bd41059d75b9cb137ced122b807f2e191fd3a1323717a67464c9fdc5393de97e2cee06f808cb91bcb9abca18e65855abfe250377526fdf5ba54dbf107493809cde3f06d64c47f11689d2c735343cbfb69a1ea3a9e89a93da31f05b568cf49114d89308a0e43e3f718e74baa59b548eb5e4e05dae1a055806bb996296e76e0f6607830eea98ab836adb1e773b9f6e28792f425d8bf23c741836603c926382f3db11be06ddce88b31022e39981045a311fee00ecdb4c1167a4997b82a529294f249381086321951f5d8da1f0caf1a16a0c985bb78debad803527af3010cf70d9094cde0de7af9907fa233b546c192a11fc800e89e72d915b6d43134aae254c79868ee00ae40ea7fa7639ed11a822554547963ad20fff50ca4b7ac72e003d14c249323d087f5c161ac668783164c7f3834ac96af49da59f6d4cc379fb34099991a940be0de493ca7cb9f4929914aa08484ca908590d9b8f49d32012047e0390940e10460f1bcd6b0faedd76fc1ae3bc8037bb55d8af14a43e095d194e58befbced0cd350f907f9ca8011ded2760adec9afee35b274ae5b8749b6c16564ff90d7054d7c90c06b33ff25a6bc182d5a7989f1de6a7c4061cfd13a2a71012c885ce5463efae05f9394c89e409618ccdbd84c63047cff44aa2effc911b71d7c93e5f679d6b59064e4ad0dacf7fbe4b636a205321686fa0cc9983bce0d2023ee559c3aa817934023f8af49a4178f77c97ed30f27b4caff70af21da39e8f57f03bcebe1357149a0d9638d0f2916972fc1c7002473d6b516359beae63e3b99153439b65c290aa2305ff464def6a786898ac4f550a2981896ea1b742d6bc98be11405a951650774608d3812c7a02c54ce68e63d99516f1deb880f183395fb5f6655c5696bc4d0a670a15fb8e07bdac0ad7438440c32d6d9b917245b3bce7f61072a949262dc6fd170e62a1e31ebe5cac8c8ebbfae6af1e1a126fcb875b42e916ef074c7dda3e6f630f6de9c012366a0cef4a2f5b05b8beaa69abdd3c2cb38727e901ad8d0629861c0f68a13a634462d049ca3c7f8a893505ec5415a9f71ae4677633fa21e6432e5304618e528df0e009e1ff53dc975a7965e6e8609ebd38e9d810629987a5eef3860922f22e0cc86f96788f0030b992536869827e8fd0fd7dca9e88d7ceed0481220c64b556c714cd66697ceac2edfd1260db693a4840bdcd4b346d7279d938e616014b47e83db2ad472d62d01f2cac5ed59dc1a8b09935409832d5af156afff374080acfc28776b716cb5ef5cc768820a157e777f5637f531acd66ddf06522f1490c7e8af9dba5b0dccbdf56e57032f7424f2d842a45d8b2113f8aec611dfbbdfd05eae9890b0030e7b17ce6c7859f05970426190a98acb5ad89c4b7b9f0137ff3092e420b249a0aafb45ab98ca3001c3823936607e8cedb44947dd464298c721400319c8924d5e1ccce8daf51f651dfa24f7be74094b5b06c1885d58408a6fa3200b72be545a49a0ae2201efecc3ed74eec2e2acabbdbe4fa273129f884cc84bb0f1e81f73a49a4f5754935161608c54ad55998ecbaa5ce5446f6bc64e814bfba04931f76d410fe903eadd5e4f38e64c3ddad03efdc869406d64291cc717eeac40aaef72a6bc05258a781a416ae87913cb2d43163a92c54ff0f5a91b4a0c87c760e7df332b178155be8a5ba7fcf12f7c9ba4f4348ff9d807aff5c001518d8a2df0ccc82304e93c656ba514e84c161577de5a48c66bb76b21ed17732ac4f30b23203d3efbe839d042c4ef599e7aaa29189a9cf394157c5bc6b50faf02501aaad510ccc645c14d4c33cee4b1aa3ffc21f88cf3f8f1f1c0d7f473d82f3a92c9c21c200ebb4dd10bc6122c51e79ea87539f1e5802d154183e1af2097d835d5bf339f50206e50bccf3f46fe2879041f1e294ff4de4bcb1b74e68673e084f77097b72920556e8cbd0de3389e0c8c806e23777cc6f2e8baa4da571958520960892aa7bfe051d2e054c8592fb675e1c5b13cb1dd5e98545d5d92297055db3c718dfd20a6706dcd7ce14cd7113c1f9761c38f51bd1d0c0460fa602746d8d1c68b4555453a40ed50729d0171a2c1dfc76bd10d168ce1ae78ad933e7b6022991552d7f62d4a9041d1fe57f01ad8dbd357a8d4f287d1b4332875019ac36d5b7668583dd5380b60a68266bd4247a84edb3fd8e34168ed6079ff6a7cef8fa4f7905f12a503175f30505c533635b19f34277ae7397995d5345a3da86097a533560ba9bf0c343b61406491b2cf067d58a1f6080ffa48def81cdbb890639f9d60df4ff4689a2d5290f03335927c5a0cb276285b34499a784206ec1d22a1092336de0cde3912fd1173a0e18108b1c172eb0f1034bcfd634f02139aaf9ddba1d8c7b80dab934cfeafbd100d665c950b3dffe5792fd431bd21c7bc5ba5b605d8988c575a72d50dd861128061bf8630008cc65c5cae0e780cc435ddd92f295583bcef11a021e1c393cae820027036c9dde8924cd3b53d43095b75c59efb4e4160f5a731ac5fcd1bb46947c0bb143b358a09217ed5440ae91eb5c2c270d89caa86f7965e3778a5852038e3f062beca53414fa3436b223ed04b67f4f8bac90f5349432bd5d1e189ed7538a8409b6944aa63ef5a9dd3e0dce8a7f4415e5beafeb80e271056e72d658e4c3655e09b687a2bc79ef725dd7dd9a11b8fc01c5d08c83948a66bd216b787da58227390ecce61a5fee79327a0eff6462477e8086013362c05a37e0724e774b7a9680f2011d7702cdd3a857d3a5d74b0f45fda89d098c8a567eacb56ca1219315bc47f802e79158cb9f6b577c254ec3f47781f290e6e2ef68f2a63da51f3a2d376298dc0e97cce2dabcdbfd0aaf3b96e10070734d8afb66f1f373c1407ce8b58b55f6380b2427694a22e1d2ce942600cf0782bb94d1fc62d460d11372e63d9bb1cee8a10237b11ab0fbddfc550336231c9e56fff085a05a9d360ca568d5efbdcd83eec805e467cc707a227eb90a8d73a6882f846f1f90852e607f2c91b6894944716cad0d4f1c8f86b7d63fe0e11c1e8a907bc939904d392e96a3473081d6499aee744a054ee5defdb10a96e34bbb7c88929f0399c6c8059da079f8c0050fbe63e1c33909f4b3494d040a5fbcf792f0fe04982b686ff67d0845eb55bbc2bb27a87a4b020ec5844ac1a7921ed02bd216b34dff6d9129943b75ebd3ab1bd3254e0a71137f0fc1db7e1caafed51ceed9ecdbdf3cff261db24b8a4fb9e0f66626b106e328ce0b17e8b1d6b30890c0387953b0b2860b4fc1a1862df81948923a90ca01a20b5b00778b5a9bcf0a3101adaf8dfc33540cf4ea127ca2c95ead4f6fd070fc2a7f4b05055c842c5fdd9efbb444077b481ae4071a9f0e668c6eacaa780fffd89b2d8d0e98e9fb963ca4758bf1f0f4ba06170f0cde7553146926efc373d46b5fc340f50db836b585d929cb14a3cd45e9725e8da852b671e49e4173b002bd3467b2fac705b9ab14b3df762772f3c5fd10ed99dd7cb62729d52e9549782e6d8beedb44390c0c6362c085ea0e041e43d270b89d81340f035dcdb3718aad928ab59837e48005d057b1475c894839cab8c1a2d4547fc427210c2a1ccf44c4c59970e25ba3e40410e0e19a7fe82c79349c68926da6aafae33c572c99d6feb86e1acfa8b3c8cf08c80660f897526872e29a563ee8fec051b79236d3d5e7c30f6daa6c7d59d7be04c57dd427c570bbaada77d0096da8022dd9bb9fc53872105e7fa6293969cd8a0a46d1b7ed7b0829867a860e5201a01979a33bc4808f947c5cb917aac351d7e208e37d5e7845a8aaf6c15d81ac5675699bd0814a80a689bfae504cb2d1d942ab0bbc09d93cfd342eaa1716ab20af3e2dc5b213bf0edd81e7f96aaa80bbe82ead07d00c56b17ccad4a6b01550cee203557e4d09e16935a29af79f361c7fe5aa6902f83b61fee22a030cc0d093c9e602a92947a61a28f80e5d7713b88b7e1c7051034cf7f5e3f5c20ab1c1de04d9dcdc6b9df9abae3bafbe5f94833d332976771708cbe3627a0927f9c9c195580907b0a6fd71fe43ad5b37b3b3de17c9c936291606dcf2ed8ff1900dc571f41ef9319317d6516af54e6604d7cc29989b487ee1ee0c7eeb65b8cd57e2e6e7d4005b848af8495a34cfd93f50dc54c4d34979d9519406ebcefe2f3b80e57ce11df1ba0a7c392e9a0c698bc10f1a0caa1886cf014a4a0921a0523e502126be638ed659fa6705dda6e3825f26f4d7521077daf71ec4410313af89e3887a414dce836f06da2c7e983248a320e72723702326d836880789064e1440a62776473d593cb57b58d7fb600909299d3030f396ec4f684b2da06101d36f86fa1e805a0c9a285c5e437303ca8c9940d266f0d733312b12df62aba902046c12eb5f21fcd25511d6428d26ac1fd83b5c1a1e861edf75f4b1a07921cd0a9292045df2ffc5c58abda8b4761ac53979c4699754aa87cabcff9c6f4986880feb91f12233047aec409d5e31b5d826a2b8ee7f434f2d11e546add59e2bd2960c87c8c5b56d0a64a7538e52f7f28f9462d10f17dee2fbec638195bde3eb326b03b22fcd30cfd80cd301735f4a27ffbd74b85ad13a5d807c2bdbebf27399d6490929e5bf1da80c5973942353680ad35b2eba3b72159149c4cbaa26a3700557200dab5571ee2d52f8a62fcfe42942d260cb5cedd95fa8cc97c039d6fdeaa420cb0a4ec9913bda0fcc8fe597903f02d31990ee5ebacea31c2f86f0c43f17f6d8cf0aa5f059765e6abd81740e74f02233d7c295005a8a244e912790127f11ba15b30df8bce654d0f306372ba0465b9e8b136a2b62ae6bcf2623708a3a2db6666c500fb880d690f9696b66f3ed3610acf9717aca0a65e6f7425de5198562d886bfed0df6cb4a456836f7982ee5594870fd32ff4d9db7fbe20c9ff44d814ec9a7eff509b741588c29b58289c8eebe70bcc2431fe6e8642fb5b5175df824c2209f57b0036d3e80beb4933a5b4b1b7be94701f672f4581aaaf06e6da766be8892059bb800f84957b811932f2af0a21483d5e46e67e68a3775d789d533485f9df98927420ab5d6f8966e2f41fb92a1857bd948129d85660b577f0a9cb67264a02f9314520600966f1bbae2cdb8f99b65dfa9f1ec436a26d7106949aa68132433598eb17d09c75c7545f84d176414e27867364661f24850198dd4dc3cc4cc28ba29406be301c5952ae8fd28a3059837555cf6c455e307e685b21a6c76fa6de5bc698a79380059ff0538284716476bc28b153b7e152f97b4063640e71edf6c2b7a357b60eb0b77b2c1161eef74be680707f736ca9d2713c4c927ed5931ee67bbf08578479409eca8b031753a08f60c988ded1a44159de6be9a16968a859325832d5f17306f06e3fcbf075090a81dfe1e92328eb6ec1be3b47ddabcc16b340a9f2dd22e7f000548aa75ac532cdc0b40033b81ff64521fe03fd126dfc4ab38408733dacae1660bab19b6b5ed6ffad744da8e7fbd16ef36b38fad9887923ba3445405883c8bec0ef9a53d7a06db3a4fde0f5411a9dc3c09c2430afe4d8d83e766791ac03e0f5b0f1008411450e55ec16e44f44c19e1eaeddec75d57a9e198129af4aa39c00d73081ad07a04e82b6116bdb1adb6d9731976e6f875ad4e8109d200a73590130518079dee4c8d6897ace1de0f7c694e03f2bb11d748df7724cca0310c54d1b1e2f40ef0ce3e3c6a5bd122f12b1fdbaa0e7cfdac32bdfca8de28eb52d2d67244253d0660d566b78758db409c1d0fda0b7951905d8dd3c2e34f404a85be4cad155b5508aecc72cb5b0c09ae114fc113a2a8234a786fca37d74db831055b31350f564a0bbf0d0023162d206303e09753470e159b9e648a43c9e9d36514124f2d53dd75014d2b4e8e33f47c473888c5588e468f96da49ef67ba68a8ffc328eca282ae80045aab79fb68201f3a5539c699842edc70aec97f2b7cbb595b39ebdd696e459b0b7c6118483cec7456e8adfca87d0eb096d0bd166ca4abd1638ac006e85c81040b73e0a2aeddc7b79334a55901b04910df85f8663670fa896989c306c18fee5c03f24f2262ccfa71c6eb6e5779ed0e39291533920831b2198c1d7b888cf63af90c424ec6c28e3cb1c68f26d6e96326d5db8589d516f0bd6df1f006a08276f6f305287263c26a95a5992e25f7a3f349f9e4bea6ab2d44c9f967d61420c3b6fb80087e7909bfca4ceeea68a14283741258263d6ec064f84c1a6654ea26c68d2e35070b8ec11626ccc6b945450556b932a8163add5e38492f51ee859ece29a970f207cc98b628d41ae51254060be2a5447000daaea9c4f1b4f0e115ff6d2694721a017ed00e26f45fdef0eb6bcc9397d857833da26a1374f854eca8ad40a13531e50600cc0567432a6528eac31f1ecd5e76c5a12b5679210b8d694470c0e7fac66d0e6fec71dc37137eac3b49606ff81e5800d4077e6018da5644d8362556b53871064c357312245f21af590ba6fe101e07290677763aabf61906b856beccbe6e640201d223430c5df3554b9165e96731ec519ec210a57c367abfda2429c1681e06030d651f7d9cdf9ae92119c448afc0f7fc7f7c977f729093e3f0182c77b8c89b0e648b5e7001e028d2c7f33ccc947035d54b281c8df722d04e5ab852d48bf5cd0dd531cf1f176eadef947c098e6664cdec96ade8d64ee7ee2cb84ed92dce136101b96076ca158a63d54aaa158297820645fc059d205275f0a07be7af295d732b06f68d6bec4f7d0f26217d56a1eca18566604ac450d791e968f863640ba829770daadd3c469cca86530c359f7c1e564e0866f810428b07842c7d5b6662fb0677069fb6f7cd6ee34f92716d87caf60b23d53c72cdf21445ca35fd31bcd820b5b80081e9cc8888e39017a1ece84ba87576fa2d8eea5ffe0e790b9bcd51976343c20c8d784aa1535dd25b4e02113107a3b43e5ad205610d73e47e32b6686bc32b980ef67e6f6e45b65a1ac18790a223bc73714554eb7da42a249185a47f5dfd73fe044af255fef54f5a42e571cfad4c2758ef2c3a508379769c35dbaf0dcea3c84204bf58f037ee17e607f9605d9ab2e29045652ddac8eca9fc717e78c26168679c0a8d346f75a7a62fd52df19d8a4f92ed2b77acaf5d04c07c1325d5f67d8240480df12045438ef462d60f716ca6f6c55f1e0582434f92f0c7d9a251651ba8267104f12fedb07893e097e268ea71393d0328d765f0a5945be220cf791fa032bd4e085eaeeff007cb9ef40a62a6cde08f3b6c69711847b0d2f770b1b28fda72844000615f10e7aa4676ebea46983fbdfa4be1ece6b967e46ba8446f631fe0d8775906f0874effd53bb62c50a2e47c18306f2d98091c00c603be55841f24aaa57a4b0c0f769582ccd81b3f318b1fb728ea94790c99b8640087e46746a7c02a5cee4d0c3043126a07434f8e4dbdc5a8e06ae30756434806c46fd7567e60ad0afad43109927afb6fbe3b1d6cf5a5f138f5c3765cc4cf6204441de97cf7969fa31a6f00010969dc1a53fb8a0e75074963cf05fe3b7f31f75a74bfb7621eb1f7e876d1b201f2c6e44c07cc2675def11574dca7103bf2a49d31afd4aed34973f29d9f2e67033a77107a2d56999f00eb74c191802cac9dcc2e9c2a2de09f63370a7a715c3603f3c04ee58d0b53917b5ebd5ee20d7faccbc9c45c65357342e74c2fff07045604167b8ed0e5fada7852aa26f42a9eb02e98a0b69f60b103789e2585f3d30d5a02afcc69755ac25b37224fc52918cdb0180a2c22816f0a6f8e7b11c097ea051b0f1bb0cd02007eb52e8c5529bef993a411065246411b3c475a2e40cfa7b40c6904c1f8b9d60c6a1e78b805ae9e83af59c00293ee457dbaf20be4db4d080973240c307efc867854d1cf290dda5163b2aaebc4092ad8e68d30cae8297fe38a337902d7af8ae42a9a010278aff454474baf43c5a33144c5885efdb0a6fd0f488113056981d80d04de7515aa852c7187a9f3e5682dbdd1627616e79361faf0335cd00ed89a2d6f8d0f21278bd689064ae63256b6dc46e445fc014d89b87c8704c20e049572d1ed758702e8c27c4a9664f1a6c862a016fc4fa8d05fe01e55e01f7b110d true -check_ring_signature c9c4d3c995029038b2b6fd2ff66f3b6a1d6be1f99ba2b4d79dd6d798301ef60f 43222e62e2d054a5272160474b60cdd5dca5af671ec240c6ea7f680094b3172d 72 0dd72b567cca9b8afdae809606f94a13296b0408d921d5cca727ce7caa97213d 1e1f6ffbb446512b6c87e8ba7c59cd25033af78edae05dbd2e3343bc347ee197 c7fed83d060108605971bf534e809183e88efebbdd338b29230438a3322bc0ad db7e3ba17da87cf552510e1366eb83e618fcca50b0fb53ca3724b18f793260eb d40b3f5a91b30c2c210bc1c218251f49c302fa493c76d2cda4aac7bcf2c369e3 3f917491203e15e660f114b56a074ba0d5f6a82dafab2e077687b679b0959c1f 251e6c7b08f443b4f3408779566e47ac9f3c4f8ae971a45d98c3e40b30c56970 19616cde555599f15ebab7f25f8cc9b6b4fc0b12228a8ec18cd0aaa689c6daaf 90a70b653a338019df13f2d73dfd9192893cedcf613fe551aeaaeb12c703c2e5 ec4a3dd3e99b1983a551a4d42b7f4fdaf0db3c92d223ec572a60da54c9dc46fb 8b63791eda73c69a44ddc7e6c12135c0d0803ef052c56706edd9b4e24e4ac4fb c49553c0f1f3cd28ed3836b210552cd87fed45a50ade59b0f1dbade833113d68 59affa1f402a907a57b328c9d08d33b021b160d2fcedc077138fa65afa7728ea 05d3ddc7396decb031d15f8ab2a700c5e5f2a824528c977115702ee116d83c21 2ac6631a9e8b4527f7f85bbda3e8cc1dde98d7c585ff089bde2020716345cbf2 5269412fc2a67717dfed0d9824b27385cd7fe801a18643101b2b0554478621b1 4dea4e2650e463d78e3d0639928c974b62956449bc0a659235cec83772e9d53b 20d7ee0a6958cf6f6298fd53848e8bcb4a8dfb22ea1bdd5cfcb6e61534116603 2d47138c7ee95d0d9a5b9faa673fac6ba5f3388de894e5c6676f65549cc8cecf 0ea442bf754f421d5ca8c4551ae95e9c76e6ca044bb55bfdfcc84ade4f7b68bd 04828487065869cca8cad1cd05995a42825e7a605ff19fd24bf1be8f0dc35423 c329c5833c057171134bcc1581ba28eef5ce177e81862d96f46447e2d670f556 faca92920c13b72f0cbb32fa2995bf2152e1f9856bc9f93e520a86303b08c122 1863492710bae7036c38656c6ac93d6d59280edbff8407137a54042261c9a26d e8c956a26e888d6ee276375a4d7c9bbb673468ab0dc5e8f2fe6f52b078970364 e9cc81d21d54a5afc9dc40f3a1db3775e301599283a1c97e13940d75a45a72c9 8a5993ec18cb99db1e8ec5e3ce03a571ecee5fafb4b4b726e4b13a85a0aae1b0 e58e4e8388e009820cc118e3bcda0a12f5d58c54e17acfa6cf6dfbd169e0eb2e 11dd4c8609f4127a27d9e2fd75cdd1cef089c8218e7d48f845e84ef883db5ddb ff627f7136d6c8c5320ba73094f8161e1604a02bb1b01c259f9a7367fa529089 8c15576195fa82911d67c20d9c4f167790ac5f8a83e0787346f0bb82cfe24780 ff4018253715cc2245ec455a6ac8d654aae1c8a1b59d034be12a00e25e064187 a3dd7d80e1e6e8adbf347055a70725784b2fa15142cc2ecf7364fb8f1b0123c2 e3f7651774b495c1feb8e803e6159864fcbf2199d75ef27d116a2888ff32b660 c8104319f80e5ef6df8da9ac700d2f233c195a1f8101a73c1e4c61247d4a9245 46b19082513ead7f109c09f51cc250ea13fecfe7183d761706a77fa1fce3e699 f1699dcdc6173723de7b3c9f35d9353fc358e9ec8f0f5580c110bf5f1e14e305 8c97ecbdccf3756aa4e8fbf349640988b1ae909250460d3588a0969566470348 0f076cdcb3d5d2c3c8433c53dcaffc2ae2a1182fe1134783b9c2a49f1f91efd2 7d179933c0af80ab58bd73a34d5680bcd25026bec47a428c769d766a1cb63142 be7803532216f9f0312e4330b5844dd32555cf4197a867daeab0e03e18a40861 1720840759966059a92a3e2264dacebeb887b402347a7fb899395ec6a3f644a8 3766047fbac7f577c2d007806c0ea37ae4f199a6c7647ba980413d023726aaa9 3820e7d96dfdebfc8534e143353f603b72ec1455af2b9fd781ae4665a687fabf 13379b02706042eb700a21a85269c606fdfcb7bf6ac32081c2cbaf7b65686b7d a341bd3ece057aca7ff27b54e01df6c35db0ea53d2d50771c0a9dc34198e6971 3bc5f74e792a5889d91a8f7add27024408e216c05ce66d61917d952fb1aba11d 2f002e4150e6a411e796685bb813634b1348ebc3b123780ef3f26248b409b6fc 9ce87c2481b9a36427714fb76c79b955d04a95137f43f550beebbcd58a2db407 e6e8b638b228f10f872a661f940f6d40792b5a5971bb0c7503636ae8eef99096 eb7d11928921350ffa48c739b4cdbce1b98f7a289b8eeae8bdb43c1144777f81 6fc0b7ade87d4a21e7e145ea2519814c679ca816583c6a320726e86af9bdb32d 220336df2bd5dfd44f5449caa325b6a05499690fb09cf9e3713614be30eee998 e1c1b03a7f72e5f6cffaa218dcbb5ecf41bd26535d809d8be6c6bdf19ecd8674 872c640d1f7c833160989de5dab2bc3a85045cab04bd5a8efbd52df0f825280d fbb6cda280893430e276e842f11c871191f4aebc4216d2f0ae6e2fe77a097028 eef28e6312360fc0524a56845faf32312eaf85d1c56bc2d60fd790116e6dac4b d6569af0228a71ef25aac1f935cfd4daaaa079059e5b93b70950436ca129500a bbc51dfeb6b097c0120398e2440cb091375a16a1d9c9ffd44ca072806f783b9d cb2bf979468fc9411d4b0d1d276e6599666fb81d817186efd93190089decffae 6e00e599d18b8c338ae8ad24b810f88f9706061172e498f2527ca357a56fdf78 d1e471bd944f7fd66ac3bf84476c657c091f82f483f757ffed6252c0534f5079 e296c7e71ca65f60b7a3c39e4a93d174f0e5bec9da0d0e202a6c9a146183b703 ccc50dbacd3c373724b9af51150957bdfd86427a691b59651fb2e4b9cd073ee5 3f54eafcced0e0a619f6dbbc8ee915c44733587c77236dd0b8a2facc12f95ca0 ec971b4e27e35a0852a9711942df813ad9497eb8cbbccd4933d668303f7b879f f48a17c77c0eab6ff4c077f8d5556d392a6e6cbefff695e2a2c03871ff7912a9 422da9a40724d8cc0c67bb8f54555de77126aa02761190e47a1a6deea3bd7d5c 855253c1b2d66f39a929c9563a464403490550993bfa0861e78227b4907e1103 51eeadc162033a913952a1f12a78ee77132d3144905cc7b840675d0a18114278 a094a4f1e9d6c814854238f86f84cf0fa78821fb7038dfb598a37d71df6bd3e1 e3ce1a829885c96df163b16006b1465c7ba2938c58d14234239b5de68c9afb34 87a9c1b2b6b708e7ae2d09a5bd9916e956d6e3214c1cdecfae720c58d0f2b107bf9df4646543fc61a855fb240027ebd49eced52d48a14d2413c8d1ec5c19c90d6415a7c36c6b9417f4e30b6904f28c75c2d8e32f74f877d3e3307dc3c7ab51082f4c1497d1afadc22cff146e8acafcec7214232dc73e48ad30c29c8856f53b0eb9c2bb0acbd45395882211f4f60b0f4e79545afe5389a4ce20d006fc6942000ef4f12d8aa35751f402e4ace0fefd37e113c578f03d4059314b5a87d01a6b35021619b015485167726e9fcbc35fa955534808a60bb35c0fcc3246e4ca581c6f0d80ea66bd89499db770f335c7611ff86a19233493293d5ed17b7a896d77e8f0088a026bd2e083065afab0bc483a572240b3b771b64705bb5aee788c808d686d09c23115590d8b021c40ba23b05aad34a8751e4424f77bd1713968da0e4257dd09dd97b4c8b9e7a87ec3a4df3ee2ab96d43a413b129618c7fe38f60cf1f87957022630d38a6242e726a4b2ee77fe10e39819b48ba1595b589c0ebf561a4dcaa30b5f535d721f3d0c463710165829bb8c05e93b51217c6fbaa5933cf6e9241b19084863f842c5a0eb5f841d2059866c8b6be2296ac975377ad97549de449a944a0098ba73983077b91ff29363853d3feccadf3d3f8008e234f181158d29264def011c43a606374c07db03a07da389f585fe00d10394d614133da78c85e20d6dba00a6323e3e2dc9f0d3e9778d4a5cdfe7de05e33cb330982d95595111d6dc8cdd0665001976de52cd0972ab3d50469e9e3bcf84ab13dcc18cface6914b4bbac490f2c6753571584b5203dde7257cd8bba1053fcacb9bacb25bb701648f0890be107e44036e5e684e06e3d2b37b191c2f783957434fe79a2e17f4b18104eaeedbc0a6678559724cce428ec9d1094905dfae189aab7d3fc64c437e8a769b1a2f5e90ed3e636d7376b3a2f17951d52e9fa21ab64ef3e5584394d9426dbce25629c56003c16502fcfe66def91d5505914798addb36833927d850efba1166efdfd70ca0fa3088e62631cfa379d4c7098d49f86a4d4a6d4d40da524772e3fec0d071a8e07a541360146eb9f396d2172a4a22a145349eab8a6c5c42f965ef1e5984db1980979a33767b4fbb529508146099d6b9278f19dcac8922f0d4d9ee9b39aba6e9a0794479a5a50910a6403994fe324d3b4ff0467271676c00286096f4822f9d14d08af11143e2bbc723023db6d25417b05560a783c6bc6289bad6aeb1f9a46f348082d981719de5ddd66d206a49a586b784f64e778e3bc0384a400d09f0c41266d057d8b90c631370c339024f25891281237ff359b76a4fa4badd697d06096024105352043fae6bb4afd20ab3a85ade14e47252fe7668a194d8b3172233ff7f2f8005cee9e716d89408569bb14ebba276a1d2f3483001b65ae905f9074792daefa034f659ac299f1365f73926cc737518befa6c085901979c2eae8d1edd29936460298dcf785bf1449510d3403b4af8cb8e33d3da83b16a8f4f3d6a27b76d8faec01bc4b628fc726676673da6a2a780b5803348f4436f3fb28d338ed723762c5ca0e67cb5c3866f99ed3ae8ffaa3760b847b5c681431bb8cb45ad02b0f3f7188e90be0475dc384669a25ea121c88d9241205260b5bd12f76995f8847b8ae2b588505c53a861760f81f88f7c99eafc3cd6f91ef881a35cfa154182fb5c22a7ef1ac00e5fbd8ac2bf9e94394aee9b39af3afadb6a0fb19224e203372629c9719f1b9064fdd52285d7971b1ac1bbc7d03cae28cbf9b30c8f0c4653ef707a680c3fd3c03eb18ffa155c887db2b6433597b1649a54aa1c9af226bbce77f0a1ff444170e03b013f9ba96111c0096c14a2cf2aeef6a11640938e3a80c9c5d9e373ad38cf40a605dde9e414490397a01e96493046c7f298de98cc3cd8d23b49a30f836969c08f446b3fc265c8c039f0128c2db5be024f9ca62f53954b6437706ceaf7b26740047e7fb28085626f8e775e0a43205e8c1b08b206d59869340c87ceb386f62e10ee64be714679e5b5647d1827877ec4c438b8ae15c5dc21e02bcd0367b4499b40cdba0a38d429d045f108e1605223297dec0651e427c9cc924110bbe4983017000c16cdda3387489e6f40a520c973ee3439c731917529efaa3e0c564261f9c7f0f9c849979f05ca2c6d134ced6aa2e0ef487f1cda236644ef3433cb3a6cc1b510029cc0018b6a8ea029d56e892de3caf2323340f0f69f824808f04cfb2dba7c20dcdbb4b2ca9b22f0b62c2d85e38ef7b2fb509f579f907bddc8387cfdaa6a7ab049c4bbdceb0ab0b7d2673ed96be3cbd34072593e82d3d037d437672e582b0b20347e6185842d7b85e2f7d16509d17f444cb1d3484e06c114100ae8e550c08fb05de968932a5044e674bfd77a67387e20eb242ea549f1aec08627cedc7ef012a08a0c9e47b4361509194a35163c4745c48617de17713a838082effa2a3fcad410cf904f29a4c3fe2aaf284ecf0b6b95bc439b2d280cac0b2bf37042e7053435106e11eb12e6962ce30daf94476035a49500a2f3bca944c01a7c89d538d1f371f0571ffe64e22f5b7bdb15932794d2acc7ac63b975980ecbdabe6b19f588994190d5d5ad717d40d0859a9cf2a7d05751037a5e03e9cf68433100149307b7f658e0b9044389f7cf6bdbf57e818b4292837f1cb0a4cb053a9130e2a9974d78b8fb00939154f42f08885e2ac815fd4e04ec10195f4fc63e5b6a88c46387d79c249980946d18bca36d148cdfe09eec8729a9d2b46abdff8e0b71acaf8024e23088153024e2c3d1ab334c2edfe93690b137a330de5f26b590bf29878913ea8e59a7ec50fef65d61cca84fe07ca56e3af8362033d5066c8f7632fdb54cc6caf50b7932d04cc72acd990c1ee90c0d6a0af212374f1c335c9aaea17e18b925692e4a852d304da517dc2ad33dabb6f377dac848398f3856a7a9730d9e4451e66fa2c7741280e1e6c2d70b51175c63c79c827cc6fa9a74976c021b7c9090987a3530a43f0730e546b0b74d2f26ba95c369033ac517446e319851477de4ad5cc7e7d3e16576e0314bab2332eb7d9c64d569dd2a232ae6223d69eb1015807df2917b16716267d0b192a71670fe3c856d2a44569dfeec1679631813ea7d7adf54070dbda325ef4021d2c61d9fee24de1472b4679172edd65c1a2a341de01865a9290e65d9f16790d4c77a7b4b6f59b66387f1cad630ca6df766c8d88b309f3dbf15da29399c7640d2d9d5bfc1373de27301ee5a2b1372e18c8c72c5da8687b7216c31f7527d8810cd8f47e846726845c51441f7f4291909594456c149efcfb847031fceb02d47e08c6f9db229bb15f5187a1d17f89c03c6d1349e80a7d280397ac718c3f09b23706e4fbadb4123a7f4aaf4e0efcd60968ef1359f053a29fa58beb55e8b428ba540b8f0509243ac8b8ad2191dcbed716233f026151131a7e6749dccaed56ea97780408ead7339b5a3c54a84353f0ccc92945360e17ca7764f259f8d1dd590efb29098b89a8b746fbb057823267b6bc4eb880c003e4087fb6af18a062ffb0f8dfe80ce3df1b27b9794fd50214ffdf03830d5cf2026c0490e1689015f111b545dd540841b5c3d99c785d0587a5a762db0588f0bf421e77d4de4a7f41341ea48531c407d50f2b0384b94cd8ef1c5944c5ae4f873f6e044f2fbbda75c43ad077ccb22107bad51671e5855869aac2f3bae311134479273e2696f7bff8f6d7ba6f291e680bf99c785c2574488148cda226ad4680606ad125255b9c9e9700e889a56c960604ec598ca017a7227495a0a2371107ff7c2fa31a20ef5418ceeffdf7458a1a12004fb6027e24ab212c1c09b03a7e4577638e395e43edae19d86cf0fcdd7d844a0ccf4883bc02c863886bb6eb7999a5d997fc384a45a22767d4d375a6c71931d20e693e2ec3c6d03309ff005efd51fb6bb820256b8875e18e580cb75f9da802b804574d6572ca0f582926db4b44b197a866b90df3aa68b055ecc71be54c658ccc0634d001537a7922a47c75d09281846372c8f6882b833815a207e7593d9a10b60cb9579bff64792c5e77e925d7ede5140f823aced3af4f307e8ceca5232ed49909c479d9d3f908a479e5b10cc525deff2437de6210781169d487ec15f2336ae907833efd033f24f623d4837c9d19cc6abd5391a800909d9a39fbe906f987b71004aaf81718217540ec2438576d98b35d07be99aa238318296cf15edea0c42af608c56688dd7fbc3d6afe6afa2d4bebb63915f5c6ff63cbfc087b6d0564fbc8b1053d0d9f4965331fa8913cbabcc61a3721e4ca67b613a6162b8d67d16da2ebf80bc29027c698b5bebc9019c8dec1b7557acf633bf72de1beab6dc6353737257c0e65c91ec6417323eda83adcaaf3af72d4ab8fa6a57b764798fde9fc4aa06c9c0f133f0226e47efb5ebc97d5a871fcfa87dd7fc75f04d2607552379c222d08c607bc388c663a8153be0565244c0515356f2dd72dd850b07ef03f8354d7a56d5a0acf7e83864a5e93e0b24fdb0aff24563ab98feab457424daa14ba197c17d8670b33240531a70732f8704ae37b69cc62ba0d31e5d8f68117f599fbbadfac863b08b94648a8df920eec8d675e281060826d590c793f065035ea216a9022fe3e66026e24e092217663600cae948918b7de410d2a65ac935f469e1be6577f5b3c090d4e06ed26e8c514a621b6e4ff43e618bde0138af1d75aedbca82a91363fe1eb0ac3945a7924638d6704f60404c6f21e2be4687d2bd86a4b3ab472fd329b33c90254c5a75292fa4f185c01db6d85bb6d944f1432d12ab05ce95cfee84641f78b007186c553f19a5df53d2a04c9b9ab446188230ad441dad4e457f39e428abdd70a16fa1ccb5bf745bef858b3517a7e73b55b37f410969083b8ec249f28c611aa0cdb6af8954e1e5471dbeea5fb89a95dd0c55ad1adc53f747cdb4d647c65ef400fb1c749d3dc0697c3561fe803507d7f330e0f595db9a5d4117dc7a73c04e74307808e7a94219fb40cd382886f807223af3ac023382d4f8df124009cd5642a1d053d2eaad11fd28d0b221410bff2fe382907531b94fe613ec4e4029ea7159cb905bd9e34cf5f4238f1d95bf479c61a98b26add01b8f17408014e1d456a29a9e9077d083ed8c7c6214e2bcb2f44d48a0027a48e6dbaeffffb71c239c3c504f6f8038a6ad7104a5053582c97f8a568e4d86451dcae14d69722a4e1d09fea602de80f83fac80667b260d067335d8efffb610b940210b9ecf23e6fe2cc277ef4b12b02c63af07bd49a33c4f20ee85f319edd02137009d38225e3d00ca44c449dd71108a84290151232e53c27f5c67b1519055283d9883af1a2327c6d5c1832550eee0209f0efc4d8efbc0e63e89dd7aee633811a4aed173ed2300e1d5d90d05663cd058f1de3a534c772dc438510378466d67c7be53a5379ef8e5e70ec0320ca49040e0c1fede3a93e4d0f06c76724ae5590db17d9b2c1c4292fe971d7ba891126f300bde5b547cdec443567b21e2d13f62c09ee8a412099c018ef87df8c71df9bfe0e4c8d25cfc61665097410188b5d07319fac6999ff5b43fd1385830e166bb10509f24d66b53783843373ef76fc7a9bbefdfa83d9c544efb5e5a815b44c2e8dbe0e888b39225e4c3c6d93f1f4acc639673c2854a7b801e215c8029583e7a159600e8a16173ed907c5d118cf119d2ea4b8f35a784627be29654b3b16c560e90259059481e3438a370eebae26278673f5e5415a1d6a9f73d51a38fc1057f9621a510fb47a082c3255e34f2823dfad61c8f276644fce9b781ecfc5b0624e85c3d63700f67561d02973fda7594edc0921f0918a56131a7f86484a451a77b172652f630b0fcee08817dbebdae8d9a0f4c17ce334e2aaabf3eedba8ca2422dae5ab2ea505f3a33f11f0981d5c23a401200bc98c813fa4eea2c4998b0d5c8ca79002b87f0fa659ceb83431b2e1c3095b1cb59c177b77a373d08baed8326e67ee20ce801e05fe910af69ea948cf8a8a96ddf3c9db245b42f55d12033b099720bffae410c1082e87a637e8222c9ff1d6e22b21027614c731e0920caa7c5be7542220629b550d199e38ff30ff99400ee7cdf15b5a3bb898407f4812f374c833ab6d627af2e90e64ddaf68c2398878d598a7da996630611f53a149e6016e7918ba142429656e03bf75fc07d91f1f76c7324c731895a9b127c54bd1eab9a0dcf71d4984b21ebf0e53958897c024d00d71d4e62d933e2baf7cfbfed5cd943f13cd7942d5342a72082d9d22e62158feb6c7976ea229f36a845c1495a835837c6d5227d08f38eb3906e898292926e13d879c0a15c93e5abdb1d2385254175cc16d05bbb25dfead620d72a9ec3861f497aedd000faf83451df749d20189268a123f95a73c2e0dcc9b046915c049bce5c2e08383421ee934c1ab942fc09dfbe3cb0ebe091a1770c10e080858ff9c623e6553c293ef2feea42f4bc1d5280f0dbc88ef992e5df2a1d3e10b true -check_ring_signature 88da38073a8e50479ce975749ba773ab9b96034a078c1ebc1c419075c0c14e41 995bdc0b9f498e5e0a9e5798ff40a628946c426fd690782bb54549ddf9354722 120 3d73b8a72f56d3011e26738180eca74cafc417636fb8eb12a28eed5e5c020ede 77a61a1e26b94b089a0e5f0b821ec7c1d364f00d802eb70642eb120752b9479f c5cc265e4d408b3a98cb1088dbfa76228471bb34aceb459c79e903bd4143088c 8ace4d13fd65135a3a186b053bd5f96940a0b99ee8c635cb753b4c85f4883895 e52e0cf6aca29b1b2c772e44dc3061af0826dc05732ccd5a5cb0eca295d9f218 524a4e85d543dd32691c8895c90f28d64860f3677a3cdc3c0735ae0bf7c8d7c0 1200e3f459aaa28a4d6e77d6529ca7124c0b17abb17e480fa26a1c482df148e8 aebc3f933f50e95d6e369fa81966ce2d052283a7bc5cc14479d6d693f028094a e8276370bb8e6c3e2b2d7c57a28e313200b33ff4d035586a776a72af01e65c69 045d206d343aee003a4f90e192f6eda0fede9b17a0b3f5b0495939c152c3c0d3 64f36d698be14de7f195088b9fe0b23c6091fd28aafdbf048df9cd16265ef501 03a63f73e1bac8dcd6252b5e21eec55bbd149986dbfedcd957fb80fe1cf79a21 9d68e7480c57fa70bb4418f169cb3fcbf69776a0c04a2c1e4d6dbd4055323382 0271f373d76197a55994e5a7f28a98b48b7eb3f82877b9e486e7e0338dc0e979 cee355185fe7b5629b29f2b62fb4aada54dba8d85deec535edf9cbff443210d8 3a793a2c5149d4193ee2826f2dae147b10c33c9767ed502f8660b0f373a38ed0 a9c51d49f3f907770fc78daf62c920439021713cc1498af679fe3fc8f12f071d 1d9b4458bb104fcf1a08618a41d1d5c41e15406c63028b9b80138fd7fc19dc56 6f6283c0ebe1664b6143aab31efd47efe1f06622a0358aee5b592e09185327c4 793c1b0bf5eda91d75873a4a5d54fe20f5fcde1908851d9b7b480bf184df9273 19ebad7c0613f37ee4497b47bed41ba20f7e21d9601d6c20ed043e21f7542dea 58fa7ee4b0c2a97e668d501b0817f1a0f86ee469af8b32f5730098e56960e88d 492f13a69f36402514367801cda191e2cad64eff738928702adb35c1f781628c 598eea4f360c7e62c82b2918853e351d07f3cae1b3d2535e3c1531671d0a2801 6e4e56ed2b1f5a9dbde4e277fb4506bdfc438e6004a44b5667dfab7fd2e9ce61 5ac170949736c9d23d1573c4a72f2f549c0b8bbcd21f1148318205146d365e5b aa821a1123a537e599ed1dbe7337e89d57d1349104672969df03017d6d40ec7b 24ddb92f970af9d57d14490fc1aa79482a3af1672e2c530fada3eb22648d2e17 759e125a3c573cbf432a176b8af8341dc4f6afe35d59ca1e432d1b51b6cce9d0 7d7577c12315c766520553e75e8be4c2832f97a7caddd2071988f840b643af0e 1fc599acaf154739335163be3e7e63ec549cfe1c284934c21064d8dcc9bb68fc 05ce1a17bfdbf563b732da3d40ee916c7d4b75bffa14f9f78ab7e325b560a57e 785dd011c1ba635fd95f6e01e7c29237bc48d6a5000c559afb903b3528746de0 e9aec84c6b7ec7841ebcce27194c6b7cfd58347f2a767f80e90422bfb8d35b9f 81aabc1db28f49f085a449d199fe4553f20687e8388c004ffa6c5669df53137c 7b8a5d57cf2071a5e3e2fd1d85a0a42ef15f883fa0d59d9146777d97cd87fb31 695025bf31229a5ff5140860c3ee721f2a9479b1191eaeb73184e09f46f96b52 b6d45e7a63686a5a6b96970e59351368bf63234743e45273fe1ae19003d1feec 80b3f9d45429721197e11922241de484323095c8bfdcd526094bca1c4ee0ef49 cbd921207bb170259de405d60afc509b971fcca83e4a9bc8cc6118b26161fa22 1204ec4ba9c54ecde64f3b0851dd7e1b173b03ab67afdc3e907f21803c5c549b e5e59fcb9f27e21318654b7a9867ca4f31210bcd42be240a2b8ebfe2e39b8655 fab5646b8a2ad6bd91efd96c1091dc2864fb8aaa9323f79235d27ec60ab3f2da ed37c8883bf471d637d9f056f2e519d4810f877efe47909da52752d13980d5a5 70c368f71b13a6e310b9a1508131112f6e5c295fb8c80f79a954c69e61f74576 7ef4b84757030da3ad73b34b0a22e5cb3f06e6dc38d6f435befb98ec99999f25 e9f1199d595b3070d516e65b47d4f38c1cb27aa2c4be54b57080eea64b3e52c7 aac9528f2eb2d5b957d863e7cf32851c62b8748029ee1bd2d0ba6043e379368e 80eea44ce7e3c2a082f52abe8a355ab3ba27946c33a937e1495c950b9500a023 2a85ed83d085790bd1451e982e942b8f9a4dea23ccb7012641592fb19bb8d678 acd1005c8348e61eedc7b3c3cb4a8f24eca1b0c98aa3284f3492f33392b8ef3c 5dd9f6423e311ef0d9e62753245b8ba85f9171b52df586f4738985359ba1bd6a df586872a9eab4c2c405fbfc4c63f3023c15f5c3b1ef9ec1a601a099e29595da 6228d6904cebe52f936560863ab744de9afe3f374ce64997c62f06e564f761e7 9d7c9a0a5755ec80411f2d57a6d6bd7e7565286de11afbd9edc2991d5cc0a184 264ef3516bf90d8910b5aa6da802fbb0c1065bd4bc1950ea020c2bc231343937 1e4e0ba005992f57144604a75488e023e7b89c57bcf2410f70ddf508c8716852 88ff7848c84a4a1050f78b9930ecf5d8037979c5a43552b13a6b71c6b2fb3049 09720cf45f6a0f92b97330f9c244374fcc22d2b9ab705a3868953a05a707ff90 197d309dafa7663229722a2a35457262b061e09cb86fac8e5c02cfcb16b96709 93ec8007bf747d71ac140130a86fdcca106f76513b2bf0b25fbf5e92ffc9739f 2aeb4d510a230716e1eaba8f0811d34ebf897a92bd65633a6850ae51b8b6aae1 caed1bc81c1ab6480e225757c42fc4ca832408f4fe50f97dd9b909927fb36b40 79d53b27abaa825c04acbe118427e4aab0f8470de840209aa622c6ed1479b427 543903d5f0a10fea741206af2587cb6d56d13e42df0b6fedb04ee2f6b93c4dcf 4d40e956c98b50f079e67a432c39cd1fa849a289661dd31b5b5818b58cc860dd ed92ba504c616467314cdf64e907a01770ddebae765abe7d39ea1d62f5661024 b6e3c2251952c66fde9bebc8143e864aa2d8bdfd81b6d72520b307edc77a5b42 43171efab71dc7f729d9721e36e4879bdbbf9b54a4eaec0a4354b1aee70e906b 6d2d3677d3478a11c04008844c98cd0f4d3950415120d2198abcae61df255a3e 3a141b31aa12e76fd4f3e74e2ec4583967fb40c88e6cdd511a187417a4c399d9 3e6c6285a343420ac60e09d0bc505f08d5933e5aae0e99e1650fd6f7eb23b3ef e9c2e17171dd836d529f678f15d6aec24c68a0365fa13475f5cf43dc27564e9d 8a45a3c0a55775b42a8d6147649b34b7905c08306dee7a628e1d0df7c5430d45 230eec781f02bacab0c59f0a7b3b4dbf576352f829957e06e0709adf21887707 35ced72bac9f7b23806b46a62c7684a8dd0bb02acc04ba81f080f2b59325e149 228cfb5eae60f02675f9381ecaa5a442bd6a77279515e5aae2dbace9ff85a5e0 d2fb692f3fe693947c25378939f1cd0a071f1c5ceb14783f8437e028395c6504 838b81ebb77c651d2d32a1d1ae8e5d827244e312fa353ea345c25c243f438d04 3c70b68602a323893d12f9cca7c66a293d2067a4808eb9c65f2c4334221a86bb ec5153cea11981135eb9ea286285a731b882ceba58bb861cf444e9e8f50e49df a54575c23cb18527801f2ebf809128bfee486ed4095a1165e7cc1b6daba06948 a2bd7b37d96986d9a8f532413685aeda2f4931e265ffed9c4ec418ad43ac18d8 bec53b9de2dca7e06b9569b5ed29c08161d36e692ca2486070df532a4e88b4cb 58bbb52baceed728c745a53286d1f15e7670171c1a0a18a9047787083d230d82 0aee6466a9d85823371c7a289f4025d1d780bb2414f1c38d6b66f8b903d8d9d9 7378dd3be222f7ae892a8a87ea8fd0b90614d09b6ec7ef300fd4e2f3c5e96bab dddbd6c33d0000254a96a32b246677bea5c4eaab6e04697bc4484830a8c1fd50 52b25bd1969423893717e9366694f4aa15378a68633438271cb3768fef95450c 3532a3e005d9573f1d75dcac714d36655a075abd2115febe2340904638932aca fb96bfe55b1e3fae1b5778f2104f1e73ba315ca7f074488a41b9a021491aea1d 434ed6688cf936d6e831a86b073e354e091b6204c9e854957c3721427e9a990d e68aadcbb2461cd93c474bfa56ea1e0bd5faf987c98d9a7defc1da612af19c6d 20bd884b92a60208c1e697b0ee7f018ccbe0d5260478c4df38185c76944ec13c 26464ed5aec89e78005e6befa192034f8ebe1dc02aa18c64936778a88bc7dc75 2dae9009b2bbfe549aa77a243dc8e4937498662c6a860704d5aaa17ce2f1d856 c02aa69041078bb2bc0a97ac0f84b8ca054516dfab6ec5b67f99560f0730ea02 8ae1c92dded04e4fc5f48449cfa21c088fa252bef5338803dae66178af46bd7e 9a2d28b745bcc8df968f4045c65f85e0b2be773c5aa96699248995ac61630795 3654d0bfec2e91713320125c4ecfb990ea02f2ce463eb0e8454a665c7d10d47b 7dbcd53637d3ff250535682e2c8160e43437c6cd8cd1cd294f4f9c667006e2e2 2e50d31737b755542391ad648d8ac2df31b0401ff32ba3144f1fea681c3dde13 99fcd1b0a9069ec493060a05c68b9b4576e22ff1e669c3c855b711abddc91011 8871a0b96363f23a396a3e05714a75e440e2f26d1df6b899b5eb1a5e26e086c5 c42f78d24c33a7cb4c51cd002f8f35d610e91d94f7a17f95fbf26b40db3a296c 109ef0f9792b8c9b179d9bcc4b7c998b75d626acd67e1201910595989dcdbb1c 122ff34cb80e03ca899b284f5a369859d64f06e3924c2d96734812efaaab2a09 7a40363d5997104a79e4204e8c95076ca71860844155a884d1a29774c861c3c0 9c078619b3639501b9eaacb92b68d3c9f5f8cb96dd4566b210519cc5ebf904ab f510d035d50489396904e8f508ce3a7aac1377b0e35d5f23cd7c94b30d729920 7d51c816b0bb53e533ce43aff9fcd78fe093953e452e720e556c3031498c0f50 5186612f845ad2e645229b5050f3765964b178ae56827d10bbe525d1274e48fd 1a2f3d46e50abcfb9b2bccec34d8a777f43781b9284b4c02f7f0bbb6052ff14d e0018ccd6e9636e45d370baf9aa179889e8419352f1209c40d19b0c37cf35924 af58965bf0d6c911ebe62d84c685396074a0018a8ebfaddcdd82507e01df58a0 111c64b429b4be6deb018d064cb79947f1679b0d3e8d9c58493a64ab07f96360 339297d8de519b640ac39fce6c8a7790717bdbb417bc67c5dc691c799eca1821 e92eb1a4933ffb854e03e859fd5c379b2199f9e2a94b74c08bd3b1b599273d74 441f623d62ec657d237123b77de0ee8668ef007ca8be8d325d811373043b793b 39429688b1b92187ab5169fe71a75423a59953ee5befa46b39a19d5b75d31eff 5c2f861affdb3b3aa6b0435918dace65d48fb1c7292d74eb567b7f9cb2c1b90f080be20c005350526ff9dd598a3a0d8ab12caeb05b62d5b6bfa4f258d3c6840b8c6e0c8ab776bca55f31cd04101d84b5efbc1355f80061ad093250f3a715120b4f48dc21ae713b85d6e78984fc969b80165153db4fc0f9696d5f0aed434f5c0f8836d0aaced6cb9896f389690316372e82a39e6f27c9edd25033fd34006b38062b421d0729a8177d752e3cae27435543bbefad305822cb8a7f9a14bc24781d060fb39577f6ea2160dc9e364c811a733114f4e3d4f0c694de5965c75570240703ae08fee14931d9362295df0be96ef3e7efe320fb94218b44a032cf644130b1075b0291e00d351bc31aa529bc4286355a8778b3d2c3f444abb1360c32e62dd10cd7af7adfabec57a33d7b2155320baa40959af63c4bdfdad88bf8b27d0d174e01034b668b6f00d995798435678d03e972e2fee35b81f13cefe23244e5c2862c0b547e0aabb2ffc56cd1a902646ffb357e77a3228cee2448e74580a325490fdd07613c9920776b6233ca9a07ff02a42cb30e624022f0805494cf41ea808ea15503e115b4ca598909dd1a6f625bf22ae8f5442a535d2e826dcecdbec67afa8f53097b14c14caa457c782a2e16e57f0bf4299064f703141f5052ae65215ceedb8c0d1deeba182771b1dab7714864642592e841983db58301928a5b19dfb7edb7c20df5efd5c140288a5147ba39bd46fdeb8171eb6616e17a77dc7251e3d3e8bb76042c03b63268d645bdeb754e6ba2fc93b2c88da42be3f7c2e2e9c515ddaa8989045fc7fa1b436122c4ccf2e8e5d36e31273c408bbbc6ce09c63568bd9e84bd42000506d5bd8149bfc5ee157aff3e9cbd55f22e89563d2048139abcc0e6b42ef90011500392ec89f5f78632433b6179d70e0e2036dd3efea86beada0093ae15020ebb9a7774f65dad28f4a0e2f3dadbf64b2a765671477c0a16458c62a30e1e4a0b8cf5c1244aa96ae0f88b83e50706cbad88f14a965cca59e1397d8c05cb2a9e0a8b28d921991761810e5a89a28b4be0c20241219a30ae2a8d88d55f8126f98b0933baaf4f345f3117d6dab37533035954e90c18ee5cac48aea8c80c4c77285a0bf42b757194a678fcf9c91eb58360e5691896ed4d92fb5048c93c10eba60ad20c264ed48f13861d6f8ccdd9b4a69fb00962ce74e251747a515b19b7591035b40a38d4a66c58a2040ef58bd27826b1a78b06e5e99ca112d4aa4982f3dbab6e31049ad4fd7333bc96e08e03ac0ab3a0a0aa7896926c04509016c3f900648aa29b0ac9e26fb942e0637ef0447177a49edb96061ec0c8d0acb8de67e17e906b4da9080e5c70a6b301c4da7cef34ad99faecb3e092f62ecf71a12876e774c095e7f90591b4900ec831c0cbe8dc9671385825ac5aea89df3585178519e8928f4987bb0f9e2847958ef66735c564ab7b8feb85cee9aab302191767bb05c8ebd0b3f5380e678b79a3c85388b032eeacd631f3cd5b5663e496787eea74612e84dd25150b0eb932bfb49cd72f23fbd7b15d66f303cc5d8c4c200a91c8fce7b10267384881082591e106f4beab6a9f5cecd9f1597483342671bcf0bdacdd8f52a28157c6160b8f7da5541990455c5b614586021529dd6c63549ec5bc4ed5f30e5393a764240c8084c9b9594949e6b6870c751dded19b4fb81b5f9ab330b7eca6dd2ec85e6a017cdb395c32d409e87964f427f42dbc561e73de289cab626b75fa6302303ee90b44066c6fe35328f3c02c2399cb51d400b8ab6c182db5f41750acefa62cf9a4064899e89bb7233cbe9cc780089e826dfe6ba00e1134d39125ce8dd91bc8832d04166fbf60edb68df96ce1c18182b630542dae2c8393545fbb852c24d0516f6706feaea5a6dd246dd80d4178a8d2cf2b8f8cde771bc39c62502b9603384f87200d9a2d218874ffb078f4c3bde9a8df1b04efab71c1c8e50d2d4fbec315850b5e057e5010bfc9beb3913067a65f2ab2e126ae3b1eb148a655a69c9ee0b667ac4508fe0f2c7ad899168ae8311d46464c6269b9e16ec9546b01a395f52f563681a20a9ec49693701de79988a1a9b7a1fb34d1253948eb92a85437b2fee96c353de60d7edd1e9cfb03ae66b029c665621db498984dfffc299cf8445fcb8b9f1c80a8093d2db2624b71b3184e5d8cedf987380017682004d9f242ade00a4a9b6ade7905dc524a2d959a4c428eed0a0fd8b221e1077061bb4a063e891f2cfe9f156e19048fa7b9401ceb0f5cd6350ed917213db5164f7a93a25dfcdf4878603a9c73d20cf01561bb180676c1221eaa1c5512cc71d2e6fc41c0bf7d8bd4ad6b2c53c9690b27cf9380d44ceb8f3403ceaf0d95a05759fe233298f339cc62252a5dc6cb8f0324015b14e9b5e68db31df48f7f6ad44a6ebd9384e4d13bff869084a95b893905c9780d0ac97385f95aebc19e4a3236eb352565bbf29b3a2173b6ebfc8685ff08413b9c03991ea3787e18e4a14835c762bc7bf77289b17f6dda768c9354e7ac03a47e60b50ead8a224bc82b8f0b0d2bdf1a1c1627a854e9a700359487b4353b09fe49983109419731792c9ac937ced52d12be9c1cb29cb85f305f099b978c70095ff164ac176e12c35efa6b59abfb2b24902a9720cc1a1bf143a619147a7465023af29f271c96d515dff1a64a516781351a1e508cab2e938ac7ca5712c052ed0407b1826126f1cbf4db155d851e3dcace2d8be92de40d56f24faa5eb2ff0e120c1df5f2eb1dc95968f6445639e15f47851eb6ee87c8a821ef7a527d737813bd0e522a4685d583a242870f7d2446b057e155abb00c10201f23ec9150bb98671609da770a29aa2d320236a6cfccac36432b134a7a23f18c5901fa376237c486a7076465f88eedc0da50df2b8d17f45d8a858c72923b08902cacfd43c2525e76f90a066407ee82b3ec7466c7c5a04e3bc08be326782ffd35f1c3f96533edaa15b10e81bf7cfd62ad7e3517b41c589adde5f10e4f8cd431afcc83636d89f776e2040aa9bc93c4b974cb42b38bba9295d89143107d6418216900827f2c1e3261e15a08b988cc321bbf8f719fbdbb60cbd5100620bfeebf23a8fe99f31b1bb53da66a04a7561cdb8ed46cb1bc7b1449ea65afa45045326bef01315cc8cbb27b6effdf03d5e6bec7d1d0249e8345fe556a078878f0b28d0a4c155fb831651bc3f4d99a0a83923608f383716b063c629e681eeecee58d3f9792ec118bbf28a3baaac470021538af176d218bc8856a652e29ff5cba807160e766cbe511fea0c97f9d4d6c0b8c81a3d36570c5117698f3ec9ec8564cb7e5898c7058abf3e6f65b0e9afb330c5df3b9af3a191298cdfa9adfde2508f88e39b53d035958dbf052bf8f53d454023bf50a51014498614c003f023c97c1eb7f45e8a7e1781ee8c25d295904556c0fcae1d26e03bb3146fe32c6bde941af93091912ec427de5d7b3e25c9eb17cd10d721d03ee389dfdd6583cf2e7bb56c1e0b653662ef908be64880ce518836d66044692f5f8017dc1d283d9d5a064a7ea7d826cd11ae96b38271f86a8685142290e8b91592ea68b4188a530e9a03d5173ab855a3f3586b265694885fa9d666ce0034d48a4e4722531ff104211105461a824f290c65661a1d75f17bc6663ebf9ab09fac007705dc63fb8df76e5076525a45f9e0e78cb0a0d88cebcfa94545408260957717eeb164ea181d8e47abdec5655d40173cfbb7855da50704c2c266313ea0121c2deffe50b2b192ab656955dd1a1a45391e1313ef06d0ac910fd17126e9f0e633db32a43db3e88da3dce78bd5f9bddac6281488b85eb514c6817a49c03210bf5f5fcc1936fe77943a2f8db48b65418324998e68e93885cb01f33231b49b607e3e3ee693a0766a6f35dcd85ea1113643e76f3f067c99a9fba3cc94a31df070e389ee5882c9f73ce92e33056a68fc73a22028cc6d50ba28916a0e26fbc828c0cacdac26e97fb502767a951809358a1fd7c1f4a02aeff87fcbf772e0ba9ed820c7a7924c4767ba30508a8035c938fac124a388f7d4d5c5a139729ad82227c0d0fcac312c39d807f4400014281bc03fddf70c66a580f45cab68c0a3a7bb7afce0c41c328d97ac15af25cb934060332c0e27f68507c5999dd481a327c2f3e43e700ab2c5608a934ddfefb25980c5d0ec5f535dc7cdb2904c1e32d3ce1b74c549b0a071e2a20ca5aa4a7df6e344990f5a75c18dde29f6bcb9dd6b6da8e1b9f5bcb021668a1dba568a27660739c899faf96b07f0b3d2a8b24bb6a73c0086d533e5f001f2b086890b40b6cfedbf184428417ee96f29c5935bec3763e6b83744ed0cd0ca2c7d548918132f0f38da3e03aa2e2cd316c88883e6ca20f89febb7aa7c20f06cca25072f1717fc80259bbd29861ec08283fb6d94794627b4fc663a2c6b8680d8bdaddb05765b67d1291d7e345fb91f925594a40d5ebfe8c54ab3ee02a9d5a005c303f90a352ec7238346fe486bb682347d01d0e8965def69c496a63c1bd360843ee9fab1fa3b5ff220b4f4945dfc5b1e11dabe54756c2eb1c45e98f26368d0f4fe8272db0dbe71780d7af9786c27a2b779c2b032900c3abf04d9337a3b7ff0aa2d8558cfb00ee0d5970e9ed3be1d49d582ec9c5f3e75ba05f2d5dcb8a52dc08c2932d59a3cd45b1dc818893e4b01dab90c5a7eb494d4fc11b728631fff8ae0dc7064e21ddc853564b91bfa4f1088678d83301e2aefdde85c1ca2ce895769e017e2872a5f3c259e421fea2e45349a5cc68285a1c2d0dcd499b41a465cafa3b02ab9a92227955e4547f0b4a58b539027847a36ff4cffa3a10359095f7d333660043e678f59c775dc7bf81b858ba6c33f974f6bd8d078f3266a9c068d1d6ce3e05cbfd957b38cf391be7a832e4ddaf21efeaa9657f8bb662c52988ee3b56765f07141d07f832e7bf3c2be432a695ee338f1f32c683994b50979d17398dfd9b03072c315dafd2214107ba6f1902cfcec5535bb7af4c115b941fccee0e08569fd6039b76413b5d4fefe7ab00618c9f7c9e12cf5c19dc73609f381b59de5034a5510b452e877392308c68ba6e1170a00edbe2a3f6dede8b1e3d51927542aac5ade405af145fcdeaabf2135bccbeb87ccdaf8e76fe2ded63cfdde2c9ce5079e4a61a008a91bf4d5790314856fc0616b45124ffac08ac739b295b1ec0ed147f47fa0806210a742287a86d6403f065fe7e201b0308d45edeb18e3e7a336611440f807a029b9f632916658f7936f103c58195b4b6154dc43a7bd088d5b09f02c8bf4dae0ad5e829e49c36a81533a4e19aa3a68205ce21e43d3955fa11063f1574c09a050fd5790b16933177a3f2f2825ced846f9b2a3c638423a8717148f13e712c29730d7a566e0242a97fd80bc943d9495b5846d2ffb9b267fbac4ef6bc653ca0f9dc02d130c7cbccb12b73b60169b4999ee075b2a229eaab6e5f55509e4b4f3dc443090331e532409b3a2ff0d1efcec0979a7bab1199bc16a05e6aac52d641c38ff80629c6bd3c42fe5d519133ab1fd9ebe2587d561f64ce38154c822c206efc09f50b8c5ee48d12c29195f2157f8e0618d0f3eafa20fed8a67eafa44b0b441ed4750a23a2b3f4777bee982c54ae1a56f346e5a8ce3d6c9e3c88ef3e6c57d4ad6ff10293c67bb00b6c87f151c9472f2573523817de3e375995b88016d0ec616e8868064cfcfe646c33b7827a5a2d683dbfe11ba7b7c398e37d683fec8b93b6ef1ad30f63163e0aaf9c8962325b4251af3724322e1cbdb64d280f962cad005b3c5ae806e1e102c6a3dbe488285168072cc4235a13662b48e2ed4d21ec6a5d3425f1cd09e8c706acb28b03e7368da17503c3d47f73150617c8e89c05c1fd4e2d76aefb0f2dcaf3b05472c4d903976affb50e925960116bb304171bc5caea0da113e9b9065ebbc795b52ea31830286ccb475af839195a9b7dee64796b3a9cfaad51d38607b947d383e22275f4d5b2660c78991bc6ad1bfc9913ffaa66837dae13d97e5f08a31cca0f644df2014da3d0df84fb5ef1a2d098e3f3b331bb990d4d001a5e910e470a240746945bf9dfdc2ebd8298e633ae0561644f9cc2038e35833bc591420835a8559a7664fa1ab48a7b9de86cdc0acd773a7e6f9afdbc39c377d17490e2047022aaf72c87b239bf51ba29f2a2fc6d1b0a5708f42d49b74236510b023288037e36b33712d18d7666bd151c0488e7f0c625c98a86e95d191030155e3f62e70273186a4aec84fd780db5f997c3bb47bc9a3c97482467555c9f3a1d94be951d04152cea702e4b061ac2862d0387fff54802e3252a10b3d28141aacaa034bf910b2ee372f3959be13922fb44c2fbe45aa6e29e4a836aa41d5987edc7f2b214400d448e8b887093fe84d6d927c7b2e1f0b001defa3348023c2b0c1ebcde6a59c00bdaf0696cdd7ecbc1b745d92dd19a2a1788640b7d1e18ac5f5fcbea432fcb4c0e5f74214d9696cc1634362a756cb91314df7266b37e1364e2b21e0c0f556e6a063ccd86685dfc25ea144afa57de27a5f70dc0e80c4065052eab23790884675403269642690a24ea4e4eb47ec02078fdebcf4949e023f9bc70e739b9fdd9a1510b6931a03555beb176c8faea2dd6dc63b5bdd8479146ec9d67a67a082784716e00c9e0518e541db2436e0fc21c98fb9f3dbc3d1c067efa8af898ee12dbba8d560d9955a9cd855a146b4a1971f2d4e023aefbede81d4ffb2654941718d123f7f90303e35eda77971b8ed0d868a2a97729c56e3d27bffaca80ffd94242d21f0c0a00016cffdbed941fae5a4b22b029ae7f036bfd204fad134adfe4041f9b18bc49075e441e6ed36c71413760320863fe07ed1ab45e22becbf878f0cfd0d3053b520e907fa1efdb30ed6712132c86486a7f0b74829cca9a46c3cbacfe9a70c4fa6102b83dde2a0b6323ea34d41b86e4793180c49213a8fcc216a5073ad40bd73b9f0558372ab383eb4f9c26cfafeeb792bd3f53acb18607dab1ab114f413bfeee9604888925e4910c364386105df4cf6cad64a2e78c41bf930060abb9c59e8fa1a8090f0df815c93468f0691065979abd0bc5defaedc6cbe9807322892f3b15fbcb04ad8d7bf8aba969211f5f04e16b03d65211e14aad775e81209e93bb046d3ca10781c46d8d6ac499d9462de9520c46fc1a277bc64ba3afb1ff1b3b65dd86eec20eef779dc4beaa1364513c6908f40e61652967133c2bc170b83f023c8447f5780dcb1ec2a696fff5b8793f9cf6336f7e236ca4ae30ebb8d4e342a2e2c9d8169a06117c2fae5aa9756474ab2337d0a673bc7ca2991846e23893bc030af7b6c5bf0e2af3f047c5352ce8b829f2788fc80661f7bb7c9341b3338f0fe0547eba24680725863c819a3208df5e8decbaae828052326b3d298fd704d8da20090a2bf796005c06b5b55039f6465d8627c68097a2ce9441e7d3794d3dbabd610e6206d4dc0a8208cdfe7fd11affef9a701cce038d05ddd89d2d244dff3d8c04e13c2a9db4067d86fdca4e28f94c1304041dbe57acdc2c20f1915b0a79c055aa4618626b0f0b62ea4a3bd585fd6825448a6a09f15bb96e374c8167f7c1db97d9a7d706905c00a32287b372bc83d282e4239268882524a1c37d7b6fad3efcf0953d445ca2810bace66caf055eca30d0b6eb2b37c20e5c472c3a3f25580756c3d779273047320169afd4fbb90be91eea61d1137cc7b071fe7f809191c951a8ad9ef76408f2ee0e2909234ce73e7f77b1de421186604d76cb792d19ac898a5bc84e4c84a943ca0aac4d42936d9ff3fd1e0c7f12e8531a82bbf083b1e548eed32878eae9f857d60da42afd386d25107fb7f1292bee432dc867d59de2f255ad5090a4214c0e4752085d5a22052e0a3570cb6f3f9a2e2095a2385e5c0257c7d4f3fc058c03e6bf4e0432969cded67d2c6c4a6a83f7d4ec76f17dc147821be59736c6902dfe3b28dd0f70e54b5f4d366f5e1732a385e3da786fa9d394385cb0f7ec0ec09f318c54a80323caa202c25bc0dc1a048de19aca66e31b8dc27432434eaa6a11aa8ebd670a0260fae51c1f7d8d58569eb045922289fcf47b0ef90f02ba556b260bd93ba79404374f7e5afb43a539505e12a3c0f7a5588a6e58af3bbe020f15ec4eef2cd26a055e2d5ce996db9ae4ffe28c548c64330df250e257c61465de559912148b6b200a8c47e97253661798c84a311844ead07f733216ca40589fde6030c024e1ea9c015049552d9ccb6a0b154a9905f5f04e1d6219590f6a12d1494cbfdd13214ee603888b7c0265a1c1007f92207ff9893cd1d90dd97c82925f199573679e9f7d7704fcbafd92891d24ffda88a1c409c9969f0a715a0eff8c4d4be3af69b61f85440e2f14b6e5792c6ed90f3ee16de3e5a6258389600fdf19a2767252e9c1478bc50c0bebe1915f023e377ef982c00dfd5c1693b0747a5c41a166139de332a3ba2007ec2d9557143c1e8e3f944bc12ccc28e27ecc01570eeb5cde827814de168282024fde334c8ef2366ed160126d05d9c0070ff608dfb07df7117f8cb8f7a77f62024b8cf91b92c41119856461b0c14756910a43ab7322bcdbe93d7662d848c0ec09ba98ba1134d96fbe3869874975a98ee5ed9bcc7f9cd9d528b7041ac7fac94a0441965e76d624bbb31830387c94ea9f4fdd54c85c0b986dadda356059230ab6048c459de28117d8b65a8467563be212e6e4e76b54564b99b36a55cddd81db780c39d513eb163c9f914cd32bfbd907f78336947c09344a6f5ef16d4375458f9e01e07f37d3c303ca845c403a52a523af5c047a7d60248b6411565a6265b2426b075403e8666c8fcb31bcae84a7837813b0229a55a13d3eacb93a73e03912d4200f7c7d09092b996651baa9e78267ab0afd0745567fb4fdcd91c7677fa0dc27860acf3e9718da2ef355ba51b197c83c5cf43b9627bb7b69e9d2059789aac2170c016d82f630a00c156a0df5b4aa546bc98ea7afc0d603ddf9b3af6ead213f1f0008afbedf32c3601c36c51d8053d736062d165a7111d45a19200aeae993f65fd804dcc3f4c5c692ff8aa6edfa52b2da20572ca05f338462186f2bdd07922c7dc40ed43492fafc17a71ddf3a5bee8238541871bf9deece0e07ab710cd04ee1a2290cb4c0002329ac8eb2bda88de88607d77b993f30ab638d40ac2bb5e01906df610c6c2675ef570b294c8c6abfa7589f3980148391c5d1f042427da78e5c113ea8095346813be71a01e3cda2f62ce514a426df0277179176bc7025bc8703b1bf1609024eb52f3819c55fcca694213ea6d89133f7485873558a2f2256e45cd3cfce0175111cbd170a8ec5828be8111d44ae84a62af255badbc382b33262cdc0085c039098f48aec5bd270a0085ca4be73c06fa1cba6a8340cc802ccea6111ab8d150dc1b6341ea44092c526b793130233933e6da4750b1a4cd6208151e8ca850bf80363d5aefac84d42d246eb232898e78e1bc1488fe6976c7e6fd9d26f1ab7c2e80d9461a50b0582e46063c5d573275c322156abfe5d78c8c7af23b53f718572670aae6019a1ffd3269fb833e7b0c996d1d2fab6a774ba012de00866145d9ea9170f2b7d64ad2af4d6de4476ded588bbd294fc4fe019bd260bccf34ae13fac27610c67e4b44236037267c1a0db63d1313a3da8aafb2086f5819daea23b08bf90be08372689a4b36b5a944357158ad3944b7de2f84e6c990cf353024d09b81edb130d09666e92eafaac01b16a64bab9144e0cf7de9ac9c16d50b857148770bb83a804f4d680c5bf83f4c20274834954d1a98d483daa4e95c31b2d0d361fb9f687c90c88ba3780d18a4d55e969d2efae29bc5782d4dd25c4b2d73efd8e900b7de5aa06496933764383fcc9de6f3499b3c6200b73dcf894ec1d209aba533ab94f03f704fc93475c8ea68030a3a84e71fdcd1b59aff39d52e37cc9eed3725f40b7e0740e4676efc88cc4ac8bdc865032e3cc772558496c632027971561ceca5a2efb450a1d2312cdd133209140c3755ea4e8ae21dfa0e8d5704e06fe1470c39848dd7403f4143fb94059c66e3a71338a91676454c887cedabbb32c960447cd7a3d4daf0809f0e858ccb8fd1407313d1a83ef48cc01349f6f46ec1240cd59497be3049b0bc7bfaeeec7a5534f9ed3ec33c9189107d2a41c12f8719ca5f52bcde1126a8e004d1b87aee49a9c4435a5325851c53d34da7baa43443029a074dc451c104b9a04c0fd6c139c4841b43decb5eebce336e70fc1703ecdf9c3e2cc3c86ed0624450a3da2dc1fa56c136f106b08be2b6fdb275e2197360c014238999a80192a713b0b729d1d912b67b15378755794be91cb780038a344e13d3e03847c767ac19ec2073f8d3039cfcc6cc0e0f3b0a4e417987db654a52dfef3b7c72754dcee5adb47054acba621baf69d68e2c7b754f42e1ee6b04d651945ae91585c5ecb45a7bb2d0550d3329b25217ae749ff41b42d2266497ceaebcbabb050c130f8253795201308eecd2a2351c6f4f9bc46fd41d4ad6fd3016c0e82a5ebf8ef17b8934a24f9840e537f75dcf73e792f3f67adbe4cab15feeef46957ee5e0be21f0b47e7d346800d3b1038008c038d45cc9eebb7fb3e7bb405fff8e5c8c1caa89d296462aa34710000206152af7ac5c9da30494ada8e10173b5804deb365a4386ff1873685e7940f09d1ebcdabb1cdac9946ee2c362dc85cbee96786d375ab776523f65496be5e0fb48c7513022341ce65cd9817503090a21e8ff38c717e4725f246459308e3b404cbcd269c023c2460dde91f7a3e70f1d495cb53c33ed3450381035121ddc4b50dfc99eede39e7c636be297f53e9f79be0c846c907b6c1def080c2bd38e597df0b false -check_ring_signature eb8de94d8895722d5eecb9e5891244b398cac50bbb72b1757200bb7c52e76c3c 335878a9c221bf48247f802564476c343a5a91b23167e0ccd9c21085b683c25a 83 cf92c9c7859a2e81aa428feeed1e136b74c4d3c709a3c76d2cc06d01cef57b75 5ee6ed1c43fb19ff1ecc53c2a28c17412db548f2b99d1b79fa653d076cc10703 e0309f836fc0ce00202eb71c1db0238805dd7411f6f5a29a7cff751cd3e6c0e4 6c1d8801062d0481856d8dd0fc373974896008b45e8ecbc419b15126d9b594ee eb5c02e71651a3e00b1c1fb755116936f777aefcde55c52d220b08efa7516d93 3060fcaaa2ec882d9b1476977c62622c16426c529fe896effa1e46ede0d93ae0 46cb20468e4e87afa6c78ca0c08860ae826e34bb1b9c61a4a995686e2498efec 0b468dc85d19cdd77348d5b8528e0cdb9b02ff4ed55f90849bddc66c7ca892c1 c95fa18397d9bac019383ee7441c161d82ce53370929fea4d9114e083780c96e b32256c040e59691b4b97a27a35a99aec0412fc45c62252c59cfee29ad15e518 374d3985044f7699eac0dc127648460e22f9a948aae6080e38def51729566d64 e34776de37a4e4328d4476a7a7e900bdc8d2050be19cc9c825d6ba869a3e7d53 e67c5102074dc6d7b9b8ac312637201c094205cb9e34bff35ff6acf87e299fd9 9f102d80ebfefa302c91c5d091e409e815953712a1c5fe4b64649e479e249936 6267301a26ad01852720774dd69271b80475143b270f86015a8e827fe3bb8fcc 86b76c23c2811883fa171fe83dc5b83785774eec8e29fb501cee607ed09ef986 59d7084b2ca3505bde548d1114b4c4857a5af806e99c73d37716af9ff66a02b8 13f94e09cea71c75667d530a915b99973c7fc3fa634936d8fa8c8cc0c8351797 84cf214d35d5f9a93b2be79765e338a3ee5a89ebbcffac4b7b0ef92ecaa64a8b 1150e21bddfc6bec5f55e66b7beab22a264358de513fe5e84c16f1618dd7a129 10c455ba4ee20d61c0a6b1e45f3173fd4194a4ec7ca569088afce819264aed9e 0e96e03ca4c114367d85cf8ab79757a5532f4f0d537802e570f10718441c7922 49422ad9b741046c1c239edd6b722c1b8357e7109f2f3ac3cded85c9325a2a71 be0d1d95e8db779e3fdbb9a060e74e0028f62b65b6d7f3d46fb3bc93d054d07f c70f6dbddf4ccaf14848284402511cb52b608dbc3c4cbe1cc3d3d4f5df8e4ff5 1826bb96f5a35d01b2b9c8e0b1c4cefb941840d800e83a34d09a0eb6f3e95983 e050c7df92283991ff34fb2fa0324645a6284df1b2d80fb417a6ae44470837d7 f986ba66eed89111299181004ef323bf7a0f5616c11e2ce6f39a8f190bbdd30b e55bbd645ae59378b8925e754a36c24ecfd97deeb8a35199bc4c4dcd3d22a579 bede5aaac60dd25406c3e394aca147f9529bd863afe8ab17e78e7d4ebad28cdb 70e3d7ef71ad9149510e22d6a217b4d3b1df6b8615ce06176f1fafaf2bc2d66e b134a8b7c243ec3398403e53309afb609f349e3cca05655ec442b9831ad03f79 8265a59d123c62abee1dd9b871e5bb99c7595d23625cf08988c73e3765861798 86ed479271a0adaff0e982ea46e3b972dc76daf3f7f89fdd634b9e013236b5e0 0f6394e38017323756cebf793905a6c2e7a35d04755637f54f45427b6faada0a e15cf95b2b980f32180c1f4a859dbed8c8c6e86d7dd76d714a49bb59ef0bf12d 16bdadea39b9404c246f99a04ae8bc01d9cb60b32fabac3ce0e4d827d98cde16 854da10f8c66afce7a282a9a0a606385d2a5fc6fff4756514cd6e37bd9dbb21a 38194373e6eb360c4cbc5d1f9c770f37311d14f4b11de37b91684cadb4752dd0 c0ff87c5dabf174de9adceed0de96b66488a8fc008549edfbc818511752a3a0a 80360807ad760112d6ac33eb61d63df4b535da1bde04f7399132f361139c4943 5d3d51b898093dee0b52671de11c16ed7d11d34de718d538ec821bed86fbccdc b050d61fcd37d22f66a5e4e7ed21965872092b34a8a790f11610bd099412f424 cae876cbef56d6ad39b1de55ccde4128689bf9b0bf1ed68ccb386c8b40e38fef cf7476d9ffafa77a81cdbec7e7288e0ed838823fb7c2d932bdc9c1426fdbd355 64be8e2e2fae29ccf9dad553cc13a0a7279e1dd5e8030b52f23211b6a1cd90ec b708b0805402b275dde661b21470bf606c9cbe33117ce3c0e5068dcf11965b5b e4c80759830bbefe51ec2209a0c050f516d067be7d90328c776c3e3710432664 a53a5a5112625e83abf0ac20e69e0ee9d7f9c1b94c8eed63596ad61f7adaa466 09e12b94e03bddb5d1dca8b85caa26ba0eebe697a152566dc9b3d5f18ecb2397 6cbbe549de46b40a9a175cc93a6bd20c5b1f7a165579a36552c8d5826e909d4b 5f1f78a75cbf4bdd290f1333509412a2f60a51e5e85be5a9a48358da375b7430 604ea971f7043b3afb8a3d57ac5570409fad5044b82924f2664c0478fefa5509 8c1b2bafe6f0a6f720952dbb92c70ff10dc7d50b3d56780cdb1a6c9fa62367c1 5a3ba8ac3951e1e895aa3966362d801a8fb8588c7cdc329b506430300a208e14 3b0aa0d5bdb79026aabba2b3f7e12ff020c53038b18a51e883280d1dfc9345f6 6359e2130bc72cbb2c5f1230c5d72953a60e23965f6a33cb88b7967485956233 97ddf1604fb151f2cac2054dae51c2531ba4fe6d5c69a1395638e4fd39d86f41 e1f48e35d8464cd2a3f95142474fc37d788e4e21e6cf29849c9cba0272beef48 8866b63526d2ba268887fc546cc2fb979213e1fdf3122e76fc05b520b0720e5b 4bb06ad260bb1515c8eba2b5830e8ad2678fabcba27a644106be305b880c8bb0 bbde10610308ea36959277dad8dc55ae3be2d5cb2f4f39a9953fc520e54fe469 d6dcfc0cc7b4e18e2ea0a696ec70d00b9e4746978f4a87668b6a89de5b8b6663 cc0cdb6d7d3126220c13d6278a7a771ca21909d205448ffd72a1cfaf81c9fdd4 3e2d83e0ea67863e9d99ce9e57a1d76bbd9a141e23b55fedc98a8d681e2ac515 4669e63db4e2c106989ebce63071c07fe5e008313e09ea5040208fe236a73597 6f45e00972ef4c12e6f04349da2d5160de1a3c55f020d194a7fd5f71c4395aa3 3b554b3bb5cd34537f60605c69d2d6f2144395028d01749e1dd7b75b181d9fa7 9783ff43215b25eae3edb171cdbd581a96f4991c32f2d0d99df1106dc687bfee 85cb5c2e08bbd9d2ed5f151cf5da2337bcec6176742a0999489eccd5fd59b236 a4e075651abf81117830055f8ea81a5e06d46222732445cc9764e0596ae573b6 9460ad7fb9c4eb3413457100fde8220563d52e536261b720d4ba4af953d3e95b a5240307ec4d5c2d441307bd1ed5b32bf1630ebbf8f2fba3713a78fe869168b1 ad9c6cc95ceaa2fdaf85ead4d2bd6ccc7be66f1f92e1499e748a72101a26251e 23bac5599cf6dc687d2f9c3d9910b273020c9469b3721babd53ec3dd042f8ac9 6cff0084632d38904a7b01d3c2db45a754a643f48b11db84f935af36c8964115 bc92f68e98c41f2514a2d1a9a95a1afd749daaf506bbf4763064edf9d1cfd0b9 5f2facef058a503e443def9fe6abad436fe8c8063539d30fd8705964175a4cf1 128e4d76bbe02bccea42b217be9ceac6de735a8e80306cc8aa9afb78da4d4fc3 7f571fd3fc183783ad6ae9b21c0737f150b5fea3ea9e4037d743fc823db410b6 b701315a285ec5fc795fd760673eda2ee14bb53b1db6b8bd8ccd4d94659cddb5 d3ab4b7232291dff6274671518f4c76b1dd7ac2d6cbef7d28db39647584999f1 41e3b280391b2ab6587595dcd02a2f7c3010e6d659051088de0059ea791ffb4b 9464c1aa4173a290dcbf89e4d7578e920de7b9f5c168a19dd31cbd1d4bf3a000a437673a99a7e840d9894de559faab24d007d7841dee5a81e68c967edab72103ef9e536c955d4bcd4d205c445a1974a81ac0e99899c054a0d876550f9d266200a72b86a7aef49640edf43306e6bc272d852548a10f1676f047c8ed6cb1e87009414af1815f7cdf74995c26f5284b68e103a604af485941763719929d429ef102db16616ae0d6589dbfe1b7ef34fe2c808f51e77812006f514e88dbcd56b4d105c9faa91b40081c6731b21d3f685e42e861a45e7dd59150b37764dafd2084810dd21e72f7e05f76252a4edd4b1555a90a5b707015a6e6f3a63706f37c977eba0df24ebcf843a16b0f55e3cacd61a11e4995211f4b622ef5e5ce5da7e660856801eed5e4ebd8bcd88e71645c43ba19b1cf15b47a44b0e423f7efb13aa1fef16a03b0f68e4514b92e62f84457e128383118e2ec4260262a6ef84ee6491caf43f805d2dd6805f824b430fc873e3c58dd2cf412d01b17e089d0dbc4301b6973b06b03a644011aba676ccc7be5b58c1872d6fe461a415b0bf8135e9622d15307c7e6053bde16b8643dd27f81d9377b94df04e3d605932eeca0ad2b067d8f1bd05fed0af17e349d83a3cc87997885e7de35f1d13659cf99ba682b110480926f544ae7003a6a8e5e5448cdcd17f3664038a0eaa2c61c5a99bd8ca74f09bd8ade81f9e700ea632805bcc8355dbcbe4196c5885e60d8d337022470e2991318adb40cc7e20fcadce79054b3eee384a70037d292f289a0525e12595f562078aefd7b0952410060de6eb1d29c524e7e2036ef8ca1f5e6cb5e7b6ab1f551f63bd9de7bfd1125082c10574affe3f3eee60782f408089c8d970c6847ca83f298ad7088da39ff69016a937d0516cf68f37b80954198ae54ef16b0dcb874d60dab2932b953573388043c817d129a950e7eee90b031ecb7bfd1f7f1079226adcbd8d65be545d36826070b4a9f93bcae0bb9b47c509bea830b672bc69268283a6727bad7e1f93753a90edf2383ffa24139ddd1032eaf3fc951d6e3ab0639bae0346b390aeac3ca5e670fb1302ba548588244aa46e8bd26c660881ab92ff106b1282cd9e95f18cdabc10fa33069235f74d1e691f73429e916946baa6124394c68ab94dbc810a16318100f56af5818ff4b003ea3bdd3bac938956494d05cfebf978fbfd97d113ad78346059c73322c96a54732ed275ba9e3dddc9e5f907790b0ab239264ad0bd915dabf02245c8e56fbe6978a51dce72b284b37ed98677d0093a841637607a4429506720c95e2d648641eede00cdc28fde0aee8970818b9dc8e4412c84a82b3e10a1c720a382f74b1204e248ba8f06cd533aeeb7f33ec53e73f89fc5438c9585834ae190b163973ec9974a4eb7ada124577238d5f0903f213116e0c5698b8d29863d9a809c14dcae9c75ccac0181b5f5559b2368f0e7865ad275e0dca0221a6fdd06a6001c95813fd40a50bc8074a0ca3d9e75bcf81c7c45abfe537329ca3432ed87ae30c87679bff5d95ab8276417055e9ba6332ffb4be797a98408143cc9c7c3de1ba028cd834be8309441429866c4116910a522efd8d497ccad0d92947abe2fcee530bd7c741a6237780f0efba64d7e90e6bef49d668c50fc68c4a57288870028a6609a94d69ebacbf7eb5344f245752304bce75202a3d0ddb8dc8203829be5bcae40a982f5b839949a65890223ec45d8b3c1247075711b9672b499c0e40bfce20880146a0d23dada039490709aa2f156e045b0489a6e461412d97f3c9805f6f97c40b7f756e8219baea900fd9c3c1eeb944061acd605d3369f4b05648d7f5bda30905200a1e018197584540f5846930f879a7a3d8dc7effb2862accf01269d30bf403ceff54f829d41db8637509a0df3d20548cc1a671bdea56f4dd25a5be67410f00ac925e59fa165828107e03fd88c2802ac6df49c92d790df9a91a608297b5550b1101b8f9ed620dfdddd1f060422ea4c9e88d4d338e4edb65ba2b3f9040ce50032ce3b4875ddd13e31babf57a40486453e88b9afef6fa89da41632852f8e2bf0c979c7af416452b9a883aa4cb12ed20b9dad343a18c02bb2acbdaeba16eec17025abb5a4bf6808709976119fd09275ab567cdb7a72554290e5fcf301f7628690616037873b4fef2c0f3c51519df7e3b6621922b933fdcc4d56255be5e984d6c07b5df7c32be3faa205a1dd07f1ace710ee0d99332b1472c0faf9ec187a09e2b0953e30778312e06421094c857e3e9072182f79eba5ca6fd6b1f6b34e71267810915da9abde433deb32a5338e898a023de88d7664f05251fbc8b1380842d4947028d9c1dae7dfeabc4cd1b7d34cb069c12f1724b92e29b34646807b865d15c6701125d201d36111487b7ee341033c32e063abd671a078a4acb07b94351c435d6020ea0ba73522e255f56bd593e60029d1df4cab310761f54a53b862d2e4abdd301d14d57b58bebef70a7a30d3b2ddc4eaab7ee803c218b45a3a812948cbbde340b6532ae648915cba4b011a20df389a8de0b5fd15d0ed3cb37df5ee3eea41ab105e92d6ad62a7ef0ffe6cb9e2918a78d607d223bc476ee71dbd674c75fa20ead05e34c3eb2e11e9b948dfa7e769211ff338d0179ab431ef817b822796989a2cf027fad1b17547f2e2f9f09cde1f043a653cece81f5cedf76350a9542cd4ba20b00c9c2c03e2bd3ad46c34840f4bd56b13c55add5cc49c3ec7bf665299ee5abb305ba2560429b8c6cfd921b452ed2107f796c437d4ebe59d1f5c9148d332c2bb40ada1383db0864e25d4de714d5a2a45dc887c2b2d77d06d5034fd7514f89f87d0fb9907f84eaeeafeb4b6a4a004ab9b11967f00ced13f6a7ed03b97c11a26067014c5e62e9892ac6b824c47eb0f6e72c2728db2eb06f7ea1b9ec99ae3d90d47e00696248207cdb1d8880e0fa754be2616b558707b3d8bc28664a59c535d6187a01560078659851d458e8d4a65147e356404b611c480ba51bf8e45826b05a81cf0618604f6ffeb8176e18639dfff5b35816a80bdc4ad90b68338496a379574cb104ce9edb439ee51e01d59dfd71198fbd6e1979d5bd01c716a6521bd7e75609960169e19636caf431827bd99b824b5526cb87b32b0bd18e5f92166285582e7fd00a7d8be98aa088bf49c6ee620faaa4c593360aee1350ad6c033147788a15df64043839986c775a74bcd7a57506e6e108c75fa4c743cb71ebdb399b464f7b654903198e2b40f06938fe67df7fcc4e57f1dddcb43ce3c1b4288587fad49352a4b208645d66bf8220884e21b7ab6d32b04ed5921e55ed3d6cb0ade6d9c3922ee43b04d2cd246bf04506ce90eb054f03fd2b561736f7dacec8392c5ec547fd83ab380e321d15f27715f68ef7ff779a88af00e364330a428d1dce4d398691f8bd76a8056a775d94663b86ed226c34fddcd21c1f834002d0852eee7c1482265c5c93e90ec89d377bf517921766c3819f688c614e808cd133c221a61bb423473ad1e31e027e19e3c0bc5215d332cc5703ac76f2b8a7aee55c7c4cbcbc8ef5c8b9e27dc006653cc298d5d45469bd24ba4ba652bb2a0807f727eab6d619d5b59a23a8d6c70d082422d5584646a95f02763f40ea912e4a43547d5c57f81ed3519b4e4cbd3106567e348afb62cb3b1bb818472e50c9824c2c16165f3043579c15eea664a2c40eaab3364ee1af1b804dc082881b8ee04757d45fff9a632f91479f085b344c530abfac42c09de37d49a40cbd5efba28f4289867bd17cdf97905f6d544a12b7920989fb9a44746d591e31bbab8d19d55da86914119b37649bb3046ba1104942310b026716482288aa8e22a44fb5766694032afa290489a2d8ba60023f535f12550aa80064782a4a1c9f05ca0dc0b58b5dc9a517ec583e796f621d52dd65ed86990bb78eccd1d69bfe4b71f93244f38ee73fee7ddbc1580280ef3cae88e849dfec0530a6a2d1965943e9d1b1d38cd9c6ab059957d886735e5908b2a06e06f027f60049f7c021ad2dd434e94fd36f3a853f51f8ef0e90fe8aa414899eb8c392422602ffa5a7f5cffc734443afc974230ad0c22e0a1530a8c2223f28df1088b0c79b0bf1fa6c6e50ab45193018fc0b999368c86f278385c06a134bf40ca25e262ba10b6366744e62f953e471e34fe79900834d80b4e5d91fa7baee05068513536d6503619e629c78b7e613dbc2adea9a5e0b6bcf3f028b28e83d4bd627ed4a060be008bf40cc70d2ed68985c3c005f5bdd343934a26ee368711afed8dcf591509bbf05e5e304e892b1785dc9e4333abc628911579b07daae3d4b54ed013097022acf0c609d67c6338cd6368793a4db7a6578dc3ae94ac45bc8db6b9f23a0810c5904000ae4e129d25b25397ef6cee0d420f2b2da4790cffd057fe72ac25a4eebef0802a33c2c0cefebded0cc48184a15d029d0b35d9e312a92b1b1a0f5afe902d18206af8c6a278738c744aaca81ed197d90e27d00fdfed0f915465deb8c6f0e99ad08bd936cecf1a2f3d86606b72e4d4988995f5f971fbfdf642530fad28f9cf6e102fd3cb6b2ef434796157f0ef3f4f432d2e2bd0902b6a7afcb5ceda8d53710bf06409c147014a80721d02eb363a3c237085f921f500b67b3b9efe28aadb9f6de0f8d3ea937fff3b1c3799cd25833e1695971f5b5ccac361c2b79ceefc9fbe5c30403cef6de9921122126cac95c2d64291f7869932dd32c5274df63ce7e85e246045c3557807d991cf34dee5d8f5d43daa89058927bcfacbc377bf810bf548a5d0a8aa7ed029324b8e9804eb319bc8a176b55ba3be6caca31308653eeb2c1d88c08d6b97ef396c3cffc37f08d8eb4f200f4a80df7a6c0c3956e1a5e981709b84200bcd7e71fda537e4d01a5b77aa79584c37d0d5f09f9345738fa42062c143d780e8d0dcb6f02c4d2e0362b4426e162945f85e788412bf8bd2239a92293b8651408181cea9270111931f4a2d73f25c0f2049e6cdc5bf10617b039a1bc0a806db307ea1f43acbfe2762cea266a3e55597ba6b6e745d2b5250ce885012125333a030d4c5607b33b6683eb455b860b6ec84bf6dd0d4c4dae0906d082193dab20cd0e08f6fdfff126fe99fd42973bdb2273c43a4a98ebd192188180d577b4b65f9d150b584455818cdae5bdcb54347fc9603b0d52e63334400c7cd13e5dc0ec1f8b4e00ec34fde08e034406084083da27c8d82f2bfb6e4d738036c0766f6fe986f5fe0907c432b9530b399a7a0ee10af464975ad98a4c22ee5932a84f529b58a9183b0cefe42c666547170a49eaa5ea2a008f04f4eaa6b48bfa32e4536fa670189d9b003a6b5dc40c22f1b4674406bcaae3fb4fc5a7f989d86d9cdd11fce8552bfbff0699433558d39cf2403cc1f35e9ce1c3d59696a144cf6aceb8a4098bf0928bd80f538c664cb49824a849dce28b80d08e82db1717531873b67d45c5df0e54bda80127e5c3683874bf0bfd0808a55bc5abcdf0692b9bb80161a632e37e1bec2c7107cdb667c796d454b9343106aeac53b7026e5021cd4b3215c7f01d73de0e7fd10af7cbdce0ceda4aeaecbf28c3b9649198842b6704afe92d8987c4178a09660901cda3b7b18eced2c03eb668b4d64c696df60d211538628b3c2b7a92fcd4143a0e0bc367269515fd4edf49bacc9adbc5596bd9f8d3ce6565c87f36133d05a2960fb499b35dd9682d71c99eac969305d58f5f3d20392f6091184d9a6ee7029427096a1b0bca686d54440e1a0431cef0f413d364ed835945eb1ecd18f808fe0e1d0a861ca2f85ee238b14253a0b6a26db0eb37238cd61ace5f60f6c0b18e54711302affccb8a9157f939f2578e0436fca1a16b3285bca1ddd415624e9a457089460e645d89fe6efbf44d8f565c607476069817de1d853ba19344ed4db3edb0325800e05ca6ba4146baac8c2fd560f73e342ab8591814e2e77420261e83bd890e330734875e4f7d38491d1555a72ff91e3a7d837b5afcdb0a064b51d33f17c3d72e0ab8c0ec715eb2b5d9f1b2e11a2fd2a6aaec897c6f74f2443e5d0d5dd3e904ea03734a110967f4cf826e8258da4db88475ca080b31db92a91681b1350aa80b550f84f32d3c5b97d3072a531685af0f66192f033243ba9ec4afe9a106d137c3af0f171680d8ffe193b1ff6d65a6db6cb10a9e80fd0c977a70dc1b97f808bc2d5a031e95c2401b4755d74a9dc96dae1cc15a89944e7009dddcadd8f27b5c5232b0011df20aa704a1a11f410cf25fd56c0adf66c031333c4ac2781554dfeae2f9a8049ebbf036212a01ccefa3d6391d858ff6565c49c060ce6b1156b0b1983555bc0bc34064b5905a7f86f1c4417e470712c6f8ca5b4175fe7b4e54876d99ba2cb80ecb00aaf16ded8be3df114ce87c842d579cb0308e0cfc23083a7a3ff520569709cc51411c1d0810e0ec012ed169c499ff6a913f9fd0827afb61fb1f0fc2a6440632f67dfabb05a2b1b12aebc92c5b4504cc4fdc44ea5f34b66857652e0f347a048f989820ab307ca171c9c8a491181f6e99fd59b329b25345c76b233fcd38ec05c72f523fbb0574d9cfeaef8cc3f21fe93280743c62724416f196fd3e51e37606aa5fe58165f95fcbe6a0ee32d4910a8539ac26c11bbc7c5c2c983074fae97500b53e1e6bfabbdf02fbb42cb533172fb00ea20c8060ae0cc40b31209672ff4b054e21dd2e32e313cc7ff41aa33ba9f145bdbfa39ab488eb892ad30f5320586602cd6a73ef380de267887c0f063a265fdc7710934bdf2527cf014f128d71715f08e7495b762f3ad1f3ec2f4198e076be0e9ff63bd1ee97eed542c1cbd889cc270c8c55165309f51d81ce3c3c89819eeedb3778b74fcedf1a9234583f26b3bdd40e30f881bbcdd756149a085e0466538a9e0b42e421970822de500499c55998930328a9c1ba79959a45ca91089c853dacf3d98bc2eb908b53166888601a11fcc2041d73ae80d5f8aa1870d96ede89552eb733e21b7024c668623f244e393e39c10344453c7eb65ced6b04a653d41336839e6d002e22c07c073b60f76f59d7781c04fce5718f8a983daa4a8c65a88de7b650d3cd13a8f0e5bd7ae021ece45d307204d7780ec988763f94ccf249ba3db800ce656340ef256dcb8705f9d9137cf26e054cb1aed9806c0d3cdf80f7bc3d76cbe37db37ee4e1611c2b3a3fdcab03b17903667ecbf31703a5f18124e416e9dd63ee31352822ed4bae540cd4a466bdc08409cc8a8645fbf01160922b18d41f51f4631001066bd084c972d90acbbbb6994108d7e497e5abdc7c07f66fab9be7a804eecf98f82529f3191d1afd378efc66750978f2503925bd0f70e08498638c6093f2944048db0f9a9339dc94c02ddfa2140229981b9954a7136a90abc4a8a2967113b6d85a4d01d7c5e516a52c479d700d049a955f6513a88542f2995c403e5356ed0fa1254499aecb5e216eaa9c7fb9b6072cf43d077fb449b3ca740d1b4d2cb987f28c31b4fd2c8a77451663cf5c4cd606 false -check_ring_signature 480cac7c33c8cca87ed98876437d22cd635f0824bb4dea3c6945219ee4a2d616 2b4bdda01cd4fd5c07dca1ffd358a02b4d03470e3dc74d01fbcc8f6c5dc2f944 37 40ead6bfad4a193bea00b8d871001b53ace5b18671d4ae8e1fb54ca9c8571c48 b662133699fc64848e580aabc7a5e1210e01749314124cec971ea4afba6a0f25 6fe72515b7a44d7ae0efbdb5064797cc4c0f6411eca259e16d779d307860486b 1876f38c61b26a04f28213d09cdac29c857f01acfa1fb0242c3a74b442849e30 600d3be8303ff1b225b556c82488578d58e050b4222c7af4de3115a53713743b 6f370d983fffaaf8b2f51bc1d5674a431d986985197e8840b0a994e08bcc3fcd 69d2c90bdcde3ec355223d06f1f0ffe09f97c0ba11a5f73bf9d27ab5f4d509a8 1ab593a0ff5e1a2b0086f85f299801102fa06479d42fa3a242122cba53439eb4 8c3b89b15e944e5ed129d2bcef5fc558974afd4677bd17bf8d642573c4af3cf4 05188d45d8f314b947bad271a1148de5ed376f909d5bea97e2ac51622e7c2e18 2bcaa3bf286e1773ed78b71065c4086cd3c2728fe0b26a326763e422e60a070d 06a6d098e01ae9e8bd160fd85bf54560d7b8064adde6eec425d7cf3a34112ff3 19dfb5a6fce8857f73433e4c5d857160ed75796ccc4f0379b693511c9bcfe60b d34cbdd982ec79345ae7e0bfd0adc3102418d713d0883d4c846e8dc25bc1b8bb 843c087507d8a62e686bd96255c6113819cf21a373810daf0aa5a87085602c20 6ed5fd6532bb8ee038a609390ed3ed6da0708874ddb06ffdc0f1eef0ea848082 b57e351f995d8e9acfdbe56ff6968fbf9eff7f2d0f4ec802ac9d2059b5ebc636 187e22e04c34fb611105803d4dd20c2b5b32214f328a5bf0e4a1e2c87eff3873 51b061a5b3bcec64f5f8036caf9db06e09825e7a73c01e62dae3428fa1aee17c 38eab88320ebc713e1fe61839e2e2a91021eb93989444bdcae8145fbd7a89434 2a186adbe730c00a5cb3d991fe41ab3c83759168cc46452fe2ea0454d8835f16 87280219bf29533be4f096e272a4b2e44fd31443f4eca11555f3b907ea5807a4 cb6b591c2d0db4b96de5d0d378043c602ac0d619b5ef17d5f9e93834852af4ef 62ac8f38d10a194df254cbd53e42b87f1a418d4a6d553e4e67ae73da1fb5eac9 a5077b486e6d2bb816c92c3757050e706e5067944255d753793ec1c30314e795 046bdd917dacc025c639e5d904985de0b31d8a72d1352620fb5168a09c7998b7 08feafaf935fc01dd66d2d0be3531d6a748ed52b1727573226c89c85821b2fad e35f33aaae369cec851e0497014e8d66135d00533b0c7a9729ae8cbd6227aef5 afdb0038ec22b39e1d9151f842cb51dfb6819d11976a6d12b57b93a2710d51e4 f26e4f244f43f5d8b685dd6fb3a598e42e5c2e6ac0e2343dfb7565d1b926db1a 94edbe859a6c031cbd434d34959df37f182b26b849d5e315505ff2b55c2692e8 080ed927a16dcf41322b4c87e5d22ce7d0d12b63bc1401ecad6b57063344c42e d2e1d9db0b842a7ab9d2b558679772fa5ebd7385037a92601bbb9947af5802af 3ee80a182ec3f5ce240b2b91dbb21b2e824e9c8b8ad4e60b24e4e0f1727bc5f5 7c29953218bdead5983c7ef432fc40dbd919c5dc08b29b0c4f9579df48205d7e 8e822669b2110689f7eaa047fc7fbac39f6d3d053b5fc03e8435659f1a5daeae c06b8ef35d04857e762db9f5ba9823182221fc83e6308fdd1acf6f714e58e581 41987e5c2a7d4e57d863bf300c8a7118f9ad0e4607ca0c8b03d56637e3d19604c8b31694ccebb050c3735bb018cfa4748c37b90105ebaf58a8866732b2998007ca456b10f803a098d368bd1dd763947c47fd8ebcc2a2094ee2fa6223cbd6870144603bd362385575057d769e6341b5055c006bb44193289551e1c26aca9a3a019cb433e92e20e26b6fcc276823569e0003d85744265fd73a8db86a6df422d400dc525a3f8d9915eaaf6a013dfc6b1d5d8ab79adb90c2b1be5b61927715db18094c559b14c4d8a20174a9b9b89d9623c9e346de5568f52249bfffcfca34bfac02d522887a4c230fea5fc29949005adee5fb38ea4eda4d8ed6c2e9df8158186a05706188d4fdf64dde15538d1cf0dec99455571751d3664d2b1b8f2b488e26e005ab7091d2e0297a956a7245748fe587f7cc1b15b38a8af93fa448bad9bcf48b097da71c1eb5796aceffd56999eabcc3504af2c7c0fe37fdcbb3d892c725829409eb66cb9947ada10d5dc52e493d36aec36b5e8e2f018b094bd91cb3734425510f5300ffff618f228f38cdf62ce509b65795c2644200ba7bcf27b9cee62b2935060b4ff7e28d6dccba0898753936c6bac153c42d41009e367521db45b8df7dc6020277e36680e3eae952b43a58b76cffaaa88e6c6518bd15db164a600420b8d508ed1cdfc9471b87c06e364220dfe33cd68622372e324ceaf8710f547cab2d620b8fe37232a26a7969d89e7d69a66dbac5f30d7c5cc0029fa89309873e9ef9cd095a8e1ea6211ffdbc515ed3987002175e9c921f86686e573ce5f9da70907fed0bf2c18a647d38734df4de4963215e8e4f4c84763ad9faf348cb36a3587381a80fc1863214d4e42aa515a95a9caff6ceaec39c03ba964442e6cd0a56af3bcb7c06186bbb44bd5168fa703af5045570b4a531158d5be127f33571ab6c1fe0caf50f3499a46e4c569eaa16ab72f137baafe4649c99f8d699b4518642bb831452570df39d8ef17e2e68738a84972c50b2eeda8a8325a9516e19c1fd16fac8a8033a0868a4bfc8baa51f158792377306ebcd842ec33467ce8ad5b47d8382f730f8b903a4a076cdd5187d5af12f63c2ba16c2b3b4e76e2c07930effe6c8e52e406fad0a3af007c053acac38f08d57642412ee3b73e7dcfd9c616c0edb2bdd4aa61b7606b8a11f8784c0b7c965b08e813b2b633e54909d17eee1ec1815b1ebc45521770e8be896d59babd4db70b1f46b9cf051d2c74de09b2071741d43900346ffd750063e56a9deeccbf52f5927d4ad44559c940205f50ba020c90b946ac7327b67e5005800f3215f1ae8c99f7844ac83db80f1fef3a7c50b75b1e4f51f7ff578a94d07f4e3ed59df8e264781c29145d54cfbbe53834d2ae8f5a217c238cfe00a426f0f16177266e6086e439b157a43cca955187aed8371f4187bef39596dab8a61d10b4cd92e39c8e8355089437397e8ebc84f2b1edea6af31cdd603a8c5c5294a14088eeb05ae92e6ed2b320639ef24ebf39843ee358bf6179534cdbbbd8059b8ba0e8e351e30d7babb6d659d4df2c74ac29dbce1cc42543278073ee0f1bc5c3fad0ed60add85153c5e10052ccb848bf6be1a89d2358a625e6309e4688a98a2e0250f07bda5bc992e43442e4929958a8ce5d70262db8d67d262ec6b1b50798385980d90f69846e7f189d901b7fbd6cc5eff6eba711aadf357e52bf4b85028a00b5a02c871ec195a5e9c3d536a4ae63b0dfe0df002cd37cd396886772d05c29f71350444ea57096a8b7ed1f91b9aba9fca0541d6912fca4c96e81139c5b4982bb7ce0e6e92a3005feb2b0dc38c67dbcf3ce4c4fe414c3970b740a907a1b506997e6f0bddc294349192d32fdc29ff812b6e6f449405adaaeef8fdd7e25ad55fbac6ba02d6cb162f7b28f7b7ce0587f5efe164f4f1f6404fa3a7bed614e4e1ac37adb00ede555fdc83081b6c20a9cf3663b91a06a2d69a56d0e532a73b0cd7296120b50b31dbadbb83ddd497cba4cdde511ba5a6ba6bfd12096c5385b42c6f92b8c7c608ffd93c14bbbac6aaa86bdf6b8fd647172c1f87e2a10bb0eeec814e600cbe4a05fcbe4a0b59daef9f7e16eea453d2d2e31014256c05fffc12eafda42fdaaeb503bdd7a93869a915c5e1e776257fec1de4340bcbe8138d2a065157006eb56eae0bea4b22534f5f5981646f2dd0069f1e62dcb14ca7177f10586783a0450fdd630f71dd124cb16d40b43c86eae85dbefe1d36e21fa22d626c3119fe975f68da5703eda875cfa2694209d4ce4e2094da66cd5951f243d752c9e404d395fdfbe2ed0cc71888ee036876a699d7f1410235fc18255db0f3ac59a1987419e713d122cb0f4c98e471d719e6b26626ad47d917e5124dc77b83e358868319424ced9889e50d96b03147a11d3e478e4fef1dd70d7dc94f67e3f2dee1ba557ae9fdee8f67330ef34cab57484a8859210bf2b23d8825759d4dbd1798ee56aa3a04432577cfda07de49005d17f9168176382d449ac2f3df0f91e7fbfdb3fb91bd911f81ed11b00175b9f100f415f6e72faae5a1eb921302b8a5885adbb155856ef6c35ff0780d076a007a20390c72095d70883c04c0d662c826cc22f1243c70c6d81767ec2de50bb385bc6cb9a5ed8437224f987f18b21e329bc88ada7be91989f11216ffc50807190c261fca8f75bbf3710641c6d8aa390591d61313993127eea002efc0915905e8a3db8dca6203b1c264ee4b84e63727c32f7f7067254d90d75aeee661b65706de0d3e8a76dc44353d8c4079898ded492fc92fc553696e678780fb09e1f1cf04563c719f310ffe5a4dc6cf046178f99923dd4e19e07e8acd187b64d551365d0fc9a2c9c22f51e4c41b054d957739d8ccce7c6632185b890e8ca199fe89831c037f9d46f127cc723746d6a396b188dabfaf6f0d5493274edeba37c1be20e09a0e79df1eef4b02f410fecb7ae4897a9ad4decdd6a31a042b32881883aff351a603087ad852694dfd625e27e7179c3500626f67294315e21736f580d22b95cd050497b5dea10470a976a4ca68932d8e30a3f5694996f95180a47d59545098fe9407e8fcfe9fa1c5695d3180634fde04c6e1a30f266aef9a2811d6d607a41f00480f60a2784f58c1c8cc41a8f9dede1183965bdf0707698cf59c21eba48ed9f7f8016ad824916c10df35e5351d52155f90bf106935d1b1b091b77f9544e2faa9490964cc3131a8674cc3ad1ffea92c1ad6094efb0cdf637ee881f52a89942cd01e02acbf343d48426abf0079f60bacaf8f27e918b65d7f6dea491b4845a6eb4fc404c0bd8b10c5ac8eb00d805919a17c440034f092ff9e80a934bb9f035637a48d06 true -check_ring_signature 402f572c7c2aec49d99da3fd948122490095be0f7be9c14055e97864d0128e72 0a4c3b978fd4e7b4aed8cfb47e3218a8f6d2701f0a100c129bba090f6040d853 5 4db607e7ae81a6322a57a1df78ab87d6ef757b7c0cc896cd6b4f1d7ff947a3e8 b81158e5ea313a37fc577340deeb038cae917a0c193c1698281d1648291bff8b 3bc1c11acf0cb6ddefed2106243a5ffae1593d1f214cc10e42fccedc25e35152 5a2aa82840cc0fd6386b1edf0d84027288fe0051aa07c98d9ecb437ce4d8a148 11f0c2bd85b41c0a78b7d1c4964ced217797510805bf4c0fec22da312ce2cdb0 bef56674fa2c6ffbcd9248c5f6726906a2750558cd09ab9db00969fc711cac02c638af4a31a002aa36e081f2a8d2442964601314c318a8ae336af643b193aa03f0324816eb32a837952efe63adb799f97ea5e51403ab4519f7cc271dde2f970fb2a02c9b0abf087932d192e021bb51a0692b8393665f87ae36ee4853f2240b0e6dbaa47f270415348565382ea222b5a7a20f57da194e0f25c1c9b0583253f20849841d78e243d2f48867faf33647b95a39c02c23d8ace08a41fa965dced76801e03fcd46fa4ba09abdc9b8041721c8ff70c91ef17b542bac5b7427b7d236190381bfa2db37bd3737a2d6412d12cfca7e3800c1096f8e886445ae3735092d3e04f720897ad6f05be5b4668344a339efb00778014be3c31f805393c01cc94a4c0177c20bd612cecc5e44bba17a2ce1b730aa8fb9723bd838b5c1baa7391189a208 false -check_ring_signature 37daa1b0e0034d61dd8eac8d5ea02c6f6654db96b48c1f56621c5fba68972e9a 33a2a5668e312284f4a2fef389c8a1e4149c72c42029849ac49b72d1181d9bca 27 22b95230c3ca0dbbdbea1b46561ab3d29d5cf7b0a3f7da353d37b4c322d9627d 55cd3099c9a503a7ea3b3debc6063350c5400d387ff71f95d38628d819e109bb e60363c732d1272648381971971f8279fa6b54336d0cd3747df07ba3df942edf 801b670f34cec6ae14487aeaff6e60e697c916034294498ebe365661748b95ba 08c7a9b653471ba4fbc1b2b670a94ca4fe285cb33c4855d34a68da607a3c2e2e 895daea9b2dda1c38ec00a63ae12fa70578f9917215af22dfaf399848803ac74 7b390624a3338f14a4cf2adc8dc7c35156629e4108c3fd5b0501847fd7f61ca9 2fd34afdc817668ab6e35dd2ac84d6f5e6bc705f2389c3f2b6e3669229b4a178 62ceef6d78cdda2ccc42c0eef5deaac1265003d6232bb1e911c219ee8a6b35cd b49fc87e6a661a4caa5ea9d51884b1edf228e1f5750b4bc34a3a57b95734e33c e46b8f6991a1a94c636980a8e9070f31accc42f36b79c4e1148a33e20cb4eaff adf145829a899cf383a97178d7d48bfb0e0c6e56941b120fa1c3913cdb330aa6 82e505f01409a8f3509996212298158da983406962a0fbe13433510d2d97a7ae 7444ddc561c2ce132d2063e10eaf4aad61f37099579b41c38eea81d0d3acecd4 0f0b81779ab1703652d5dcb073be8aa2a4c127d4e64303c0bce0327a64bd6a32 806a043fcfb03675fe342809c02a7e6e0ef6345920a33e2693f4cf722a0be4ec 0cae21dddaf1d8380fce4177e7f6a9e5e22bccf9825ebfc1e76a25c9ee30e591 05f981710c800940a2d50922768cbf6a31802db1d0b535766ccae0e068a47a2d 121150281c8871932d5bcaa9bc6298048235131fbe0141fb16b268dd193afad8 2bada6dd0688a4fc9ebd54cd69a08e9ac1d724a0b97642565f8abd8dd185b2cc 2c061096e4978e3f1393efc8036bc50f1a54f131302826ebc174397e152c0c85 2f3530dd899aaa800f750a30e2211abb634bf997f387f843c9d792fd7b4d1948 64fc63e1b314f2780e2dfe7112907c1fdd05daf5293407251dd31081a66bf73f 57fa2912eeb28530ae6645ac012d9f1298a82f02336b8d242337e64e2e53e874 ac494e2ae4670d68cc88a01fa77c526820765ed08342ed0b588fc9a704b74c76 6a06f2667d80d9997e2a18993bbf4813a01867e8b244b1ecd6a77a059f04e10b 74366d49473f590d25e01d7efc568370222d8a86acdfdb403255718731cb7852 5d07aff4a72da118e9be8a297cb4acabee3c3b34176da0cc95aba125d073e5034fc1bd66cc0e3af8ba54dab8453835c48cb3177f972a24ee27f580aa337bc10cde519a116b7f6f64dda82c58f71b4aef7061c5064cf43ac86f2c4ab0b96640094c016860af9246be02c734bd87128312cec7d4f498e0e195a231dd59aab19a02bb151a7baa04a84ad3c2256029a5950763502370f3f97ed706fb4c7b5bfbfd02ac00a788d4285f05cc4a723dc04aa6c9af502294120ae3bff703bb8871bdee089fe9440a4ceb27d8b6936083f51f807ca12f15b5f40a89a9bbe91c7aebc1690cab05555222ab0a397de4133fff1d7b2c5770c232db20e5dcfc99672b8f04cb01f4424eaaec324faa09b8a524719af9d391f02ffbdb0afd7d99e5359d7129f40120ca1dfa8db64f1f14d25d4666b686743790885f476a0563823a320194be21014b88da21d5173ea04eb85f5d348ddefd170e736c7d9b01fe34f7fa6f17888f00404bb7621f1424f28fc2a85f92071fc8d1a0ae6c5fd410498ac03046c1af33041680e71079c3fc304aec2dc526b578264c64fd0cc7fc399d196320da77cb5800d61842eaa5850c1293e922631ae003d2b40ff4ce957f45eee36e94f18a30dc0bad45e63216d8f03214d7020979e476841a2958082373e600aeff32dc55676b03b7d2e1f61441c88ca5da3a9e91df943b9de297a552f6a040031e7b875d7a3f342c18216daf2208f0e9095f85f491d36000362915993c6c904da16ed51ca7d5016425628711d789b90ac68065f875cf58b24278e62370097b62b8f15ba9db460df401000346a1471dfd114e231c4a47eaa912b8c8faefa3cccc7b985e1b1567020b251760ec6351cfc1cb04022f93964e370f491535a76d7aabd90b11aac2e10e1c10da47467c0e36699add58a4ab5f18d1bd3d1699e43a77e886e9b41048370f4f6a5bbc16258269f2901de316320f2b0c5335e74bc81cde49c60f5602417f043613befe75fd354ebf52af9451d68b40107488953de3c8d58ebf82791ce6ef0286c78ec3337026cdc7e2e0636f8868d1b7bc7b4b2f43a448fe00841a0afba3532be0ff41e14ac9506032ab8e395300cf69ed1fc822c5ff0a5a5956709fe4ae0df0408c7eae9f8fc73803d6590cf48005f7ebffcc6f76b38e426d0e450fdfa60311d62ae2f92bae134cfbcc406ad35d400bc2a617fcb50bea321ce30f2d9da60be7b1824bc068ebe6d9a98b2aa7372d690c807a282ec06ba586a4d0d2ffd57302cb01b8766e437e53ef1c9ed5aee393ba8c1f184f1a51a69aa6acf360a7f4e903fcb22f3ae04e2087b460b3770f89759aeaf39f18e2adc19088d4e60f04ee7b0b7b0a2b1b649a20263f52ddd46ec86c9300ba45a08bb81450abff8e0ad2ddfc00d770e7fe8554da0d833dd036d8ae743223f3459ad53f639d5045ec2eebd0100f92fa9a62c4f89dacbb430d4fef9020d6c8d79dfd90ca0f7eadb96c45ce69cb0bfd3d6b2a5490b4bf531f11e730593541e5a3d91ca8b903a95cc2ce1f48c8c408f9e66d8376c96f756cd3698798caba433f1cf63d0a9355a4f60432382654650455d525ef8346df33d0ba10a3cad2675f9640676d2caa86d66436c280cd7b1cf1bd0ce1c88a4d5cf89264f598a32da8e8b0ed375b4d6be60f4cdafdcfdbb74f01168e9a4215ce7e6a8e39e37176449a240d299d05f495d7cebd1f21c82a6411027a002c38c9719d9ab8e7eab7a9731287efdb1c48eb83e573c1e223f7511a070e4f445d229c7cf2f2c595131567f6aedaad2a9211678693bdee77882abd088b09e378302ccd573697931ab86fd9d35fb8bd91b8a03fab217dcc021f8f6e76722052f46987a0ff5f8179813826714488b77273514af4a127b6c333f710288c3504d37dd30006197d88c167cdd599a042c9790b866ee012a920153e32f597ba720a6e89e02f3d2ed4976905bc0c1e9eaa4c6fb880c8ca766ea43fa7b26c799430060583329025f2331f0040d0f4ce78065ff57475c396fdded61d1008a570894e0152bb214ebcbf87fe822f53ce1006b006809cf5f6a56e83b23d20a4f1f35e4501c3ba9bf62d5a583a0873ccbe5cdb8fb6a094e7cdac538ec84a2763d1dd41ee06d7126da7a9b4b291f3d0fa66d660cd72c0fe252d41633876dc18b35813a153084771a3316df917ccc5d0c68b8081032154564725ed9b8c033df4987c4bf72507fba618814d2c6034fed4f78ded2ac57a64b83e3d90c7b2b4a53247b8e3510007576231c0ef42285c4674eaa9abe19785a6977ee6026a2f25595c2a774b5abd0210b19d90b49c81f1707695254e941d74a252701d3122eddee4faecbca51fb60edfc04e1f377fe99c888ba17ec562d89efe7d457653fe93a3e8c0082773dcbb0934f85dabca4ef918909c8171465ea6b6fb0b4b1dd59c8e8ec084a2c30d5db806 false -check_ring_signature 43dcc589751978b1e7ebce1771e400f6a7e54e34e7c0c954d63c904ad9cd1441 478b3e0a465cd2210a19d97267a185601707c17c90b353db4b66914718491574 67 73bf02fdb22a744f25978fe86b8500c138ce46773910afbdf9a2361863aad42c 96a1893dcf66a4864598aed233d2346c5c4296698014ca8d7e0c0e1534d2903b 84c561c3b004c18bfd3f6c715b5c42544a2913885c38d4100abcf11f5652b87d 30bfa895c8938d158cfb03226627b714deb2d89b1a8fbeb68a8c852d553a0be9 a063a3a898502fa146a605afcecccf29e79fa15ec4f70aad9d289e1e68286d9c 6a39fae2c70cc42957f55a1b48bbf40d652cf88dfcae4fdd34678f59027002e7 ceffa9cac810e005b8054c7c689e217a58bb22c95cb1e355ba30e2e040943765 b40fb838be1869e08d2b95a2e04564d47f5da1e90b13a632bcf1d762decee8e3 b9a0d92a52c2ed325223eaeaacf0dd18b18fe63ecf1f3290225855397944f268 eb33413f9c57ce2be2ee84c7b62b85bd2bf3f4d0f83a478b5d5cc585a974c92a f64bf85d22bce431b25c192b6fc16fc48047a79c35b171dd031ecc18e90fc5c7 9ffa64b55788c27c1151f5f5bf985c3c02a21f84abd9ef34f7233a976e11d58c a11688436bce7761dd15e3cc2b2725f8468a15e4052de372ff977737843bbeff b82412ad83cfffa9bca97841da9077f1e8fb180975d8ae1483cbe8b858154a63 e3d4cf0789822fa8dd8ec43293227f28bc5ea104a81130fa446e648e2cfafcfb ab2c76db591d47ccf99afd01e074a835766f7e059f4d3818d762276640d9d5f2 a51373e3d718ecb4700a1f0dc3751b9a2b912b5ba3637ef80dc1a5e928d35e07 9000399b4142b64436e22255fc5eede7a643d16ed54f6092f2d8e66cfd252583 ab26c07c75d51a11cb54fc227e57490d86e01f000cd10269b2cbedfca30d9c5e c7e563fbd7b9e6e20889894ac081212a17022b751744664d80269077d3f74d4b aff51613fa3012dd13b46ae5edf8c4d642f7dfb20cfe0134e83e5dc940aa7815 34d5a089cf38d139b23728966263382e9d47f6d90b88c57454c5c6ff6e54a126 671f5c948765797b91e0274993682c1c0f6a46b8e71c8fcec0b0ca0b62fe3758 dbd3dc472d2e8412f2af7afceeec3bd54917bc1591450d0b194b767d624a17aa 6d077702fbb8b3859df14bfe53408356549cb77f6f446436f4d2201b364fe92c a1305e412509bceed4321d242a29c1517efd086f47c33c1983d8c506110334f8 ea4dceea8295ef9cdb8dcf44bee2dd8a45416823c93e0a22ddb403627c4e53c9 eade4d84d6329fba762f363b5b39ea70507ad7f75a9ac616f3201f58a1482af8 8648c2d5661d6812068d43a7563651b4f96c5bed4e1efcd89bea097ba18d9776 ac5bd79495a3878206db16473137e8b3623ad668a84718d45bc48ca6c026be45 6f208954f2c38255a12ead575cfa614919d51d95b55c27425ca82a79daa6bb23 a83da116bdfbdcdd5a8990d04f560d535f74ee42323cc8e90b5082acb6b9f061 3fadc427bdcbe3a7c0bc5e951bd1b817f6f283b6cfdf01e17f51089041ed9ab8 fc5b80dba3d85bfb9281b5eafc10c7976f99cac32c63900a9d118e2a03f02b7e 594e3a87a3c1e6bd581a4d35b69ab1ee2d835f353a33bb8d2a315de51eebc824 63f76ca819df300177c42fd7b77002d28b512b9a371ef146ab3e241ac66e35c6 e053a39214f0e02cf1eb05e3f8b83f1cbfce39151cd9d88942506dd3c650c7bc dc52ace13294301736a76968679dfe94fa7975511fe93747c489f798e3d705cd aa6b4711fd92dab481df8d23f631b4222b84854821d71b659cdc7bbe076a3571 86a7cdfe7b7f46ef03036c1239871f26efed5690c4b4a09a39d3fa85a0e1d1f9 ecd7a94baa92eb9b91e5d1316fa9954de3ec902e022cb4476c1e3c048aa934e5 f3f4e33e75d8446e0431781314355987f2ff975f80237982227b36a2370d2aaf 2e56cfc74193de957f0723fe262f05f57c49737b1754cf562100f660f5de0598 ec457c5768c9a785150429e66fc988175ef69e6605fc0f0165877ad29de05d16 9914e108d362b23bcf72a1604a0ecbf83cb59c67e97afc8619389d1fb3f9183f 3c9bad8a7dca5d6b162a66470c2058d53186c6910128764955132704f91eb76a 3882df8c047b8f0c28762687f820cbe1c69fe4615c709527fb6513e324b03690 197c3c59d459132f494c87f4b1100b86f20b05e66d33cb2dfe3faa1ebdee0aa7 20f38abd0cd0076c4a45a9bab77f438c1ae3c6d5fe3116c2ab41e46da8954cb7 f650f84ff601b4346bfc7b649a10d1636fcfd976663ea8b02cdcb1c36df93df8 aca1e0d5ed6d60bc56b7f2f536ff6109e21b42b6dfe5ff83dedb1a5fd2f37016 af2d657e0bf239978e4de8b17e9584b594b06f6142c8be5409903ff3eba72c7b 6f7a2f3aa3158e6feb53303a631695f6a433642d84e83090e1e8e36690f7d64c e267a243908e564a6623f47a199a490e8a2acbbbff2ebf784063213891f13d7f 5686bb667b82827e6ffd70fb34f4435f7f9e05a5214cefecbcd4b94ca0201bb1 fa4bdb13052408f4a1cf8beee0f95d862d4c014f476784bdcea6a55766e56462 7223d5ab71b3ae32b297a44ebbf1968562d14a365f9bd4240796fffcd67d28a2 f46ce98c7a6f07688fbce3dd9156e403124782f257a04aede2c8d647e48a0f97 14e7ee8621f1a68d0d0199447e68382c717753e7a0474f03ef39fa95d9c7a4ce cdb7cd8e295e730ec66343af15e53206755934f33c8f18a8360241b2b716bb38 5969f39987a9c6884c6c350763e783e5a69412b84f2068371a3df1612a6f2281 d670c99fc0985ad7b812799ff7ffce50f1703764ef7bd28b7982326de22f2c80 c396cb270ce89e14d2260038c431e90ca6375b8c2218008f0530d4185bcc3aae f2e56a8d5fc54d594171711c79d5ad7816c014daecb554a4a9fe9008b91c4f75 eafaa6937cd83f72ce8e22896f5b860f4e0a3260a56dce1bad41246ff7165079 9d9a2969f3c4b56fd41d9964b6b4f368aff04872e89382a2f1ef1f410bbba9f7 068912f8618ba09be486a693d61c270d460b5c754dd88d3fa48a883ce9f1672e 37a8154343e77cb3d369ade26df5242a0c2244b019467e683ac00a50e30d8903bb9681e194827aeb7f46bd0a9fc06f6a487545025af94c68460cdf89c31e1f0a3f36f9197ff3f6db682ed239ac944b0ab31700dc94f9a95410a21a56d2852d08c2367c235510b746935975675a68b1d1e74c033124a1ed8541fc338b03fddc0c54716a437209d37746af54188769a0ac4bb8dc82c93d966a0cfedd9cb8dc9f089ac7497b6bff669d61bc10e7579ab6cf6b1bac34f5431bf502fd8bb6d235e20e1f167626a0ae6499e14bfa753c26aa04ffb5c058ad98d8fa8811f7f67f158308b8e2434384a5d9fcec8dabd8785340f107cddbc519833b608a8fecd97ce3560d8ba6115f9da0b692bfc9d0bf3ec9245ddd8a8a4e40f4c4e2937695d7e35c450e8714a78a13942f76f7abc1524883711521adc70baa05964655fe8cdcf09094032719ee4e196e3ca990092db3a0669aa4879f687678519490006184f3e1ab7706de81b0c63e55abb1501bdcfb7317c79e007655c5cf8b5232f5a2780e3fa83a003e6d415cf9d964a16318b2592f9a4fcafc40c192a2a06d8d7504d8858f14a50e537fcc1d491a4f7e1567aa62a60b3bde14c43aa8735d13c902174eda32ae560aa40813db8048160906b61d95a7e907663e47f61a9d63fc24d4fe2ced19785b0d7f2146fcbae39e25c43541af1a0106c3133ec14c9eae4cc3d25bd115a4ec79021e779a8d342275de2fcff104c960e23e30968ac39cd8e4d0ad4f745a904f47070cbc33e02b44e60f61ab946d42a4041fa87a1e182c4aac94e720f3872881c301aa58758aefcbfb3c6d626394b1360cd8f9a056e86258ad2e613c835567096d05a1d546dcf4bacf51b78e108dc1bea96ba798a092c9b98ad0162533e041d450065201046d229bf8b0e100b575cb7f7c79cba51e9d9c34e3bfca33d0015049a1017c0687e966113befc0ce39236a978485a5fb34ab85d063fd54993db7d281d20df19d18fbf94ea6d8d89535bd557c0dc0dc3bffeb9bf827c0c72609e4f7de6c0a1ce63dacb726d60efa1d80b39907595e851e8c7aeb9c038f2b79e86cd502c604dca0a862879d42e013afc15c8981f0346c209be22fd90623205a00d7acecc70fd4e4bfeba401354b69bd02264eb228405d0a6daa102da421c22452986821560a97486cc6671a0546198706ebcdb41b6c1b39f18e2d933411fa9da14c79e7c40197e770b7aca0271c3c42327a17b2853a549be0912ec17aaad2bdf905ec2f9204ec3b4dfa58b259018929d7138716aef58cfeb787d7706b14eec0bc8b40159f003208f2e475691be16b07923c743af2946b7a37eb63bf24d62fe94710eb2ccc090aaeaa533ffd58748e79d400dabaa0666f04f8a7f41b13d0c14f3a922a99490ca083f08ed853cb0550bac6393e56f6f48077bb1a9e57efb4332926bb8015f30a65a6635565afed0e89a1b3a96b47fe3218298a3897fd04d61b637c12cfba4b028bb8679dbfd249e957cebe28b2cca1f88f691dff5d48afd1632c7b79f54f2f0621d0f875f7ae8bc232eb23d085a26ac20769aed2386628866da42de08563fd0d3b30226fa81ef456004d5fa453e19a5597f31e52ff47087f45b92c62d54ece02daad0503d56cecd0832bca29ab3235e8e7973c7278c52029f3206ec6513e8509a5f8851189306a8fc00a648cfae764422e879e50bf0be77a736800006107c40e7555242b351fe3f3cc22f5745b5e52cbf68b575cbb2d064069642cbb5c7a1101752f8dd6d2f6f3c5e8e309b35dc851ef0d26f01319d8fb9f1d19fae2d84b26047f682db3a36264f7f362d11d6f73aa3009889ead88e3eeb1d9ce3dd3b5605b02aa45a10d94e453e7faa226e5d6e7f4400db4b8e3ec575f33fe6710bef42064046356c6d05dd004f807c3744b1f4a5cca8add6a177983464e2d2a089d0e05310c4b3766ce5d6b986a186357aac787f8cbbea6f6a0adfa326d8c3eb3824b24ef067409a109ce4dc394e811251d8627352ec78e07001e03af639c4b4823f2f66507df3dd655e30eef06a162114e0307ada9541d7136d5fc440d6e19e685c80c240f148a36dddc3f9b3d7d0acd1c2115e2d3d1e0ffb1ffed63994952fd1bc296e4091a45d6c555e89a57e3c5d3e5dab7264e32d8473d88a6065037c35bf77cf99c0540a9d338c30f95b76521a48e07cd612a6635438c538a2f08badfca84c459e40d76bbd3ea8818f203646fce1be5520dacde0efa40471fb7266b24c78c98bef30989bbe9becdcf7ffe6e09ea2672b97a70722f67839f34c523934f44b80b4613087bf9b6e6a793cb3186faa357af03a9781c086ff18f01e78f49abed1176167602d401ec06e76c1ee7ef774b53d5b877181d41402ca40fd6deffcf48f464707605313facdd8c5200cef89ebb3744bf47cfd68a3a9363f2d67cb9c9a17fe6464d05417bb29b8ab7b9267b487d2200b3d73a42f7238526e7f4e3fbf7885260adcc09afbf5609cd756bc831fbb8a1cf72a0cd1b1ad0af2e056b43eaecea9d6a1b42007928c2c75825dd4c53377f6e7eb55fb3705d254b3dbdd9e15f9c45ff09b7ec009417960c8184a84726480e7374c388dcd9c08ee782c057db7fbda35473b3d10e5cace8529125acb19956fc048aaa132f61f185a40247b57cceeca895ff3c030f1e82834f65b1c6e8a8029eddf0d808076e94e94d56d7bf3e31db2fd1a5f7df0f38c9c3cb5be044565325831ce4da61327c722500f01431091da64af31773f5077786cdfefa80425b7f24994d1d841d8f90ac7cc4ff02f30ddfbf203b4c20aa047ddc2024729bbc81b59c4296026da3981117c83776c4451c17d3b60169870d0699258b51e8685935c7541c18e675347f2191bf5113f166e15315b22a25c7de08faf7bd778496efd8e8eeaa66b7e4d94862e7ffd074f6ef32de75bbda226217049a68643613f9515b84a8d0696239983cbc9325ae588bfb261b46372b14a28b041fe7cf4c9dfe178b18c383526d9da6deb53993308f8f5ae7598e25922fd60a0e4eb285fd2302e977f41c43d563c2e3f41670f7a583409e949d53f63c96a888029a77380a56000e5da2ae524df1c0adde7f5f813d39b17770c84e42bdcee02a0bb216969762890b087a8ffc051f46326b36664f8c9e055cd12dc40e43e1f38a0b7880b02950fa37642bd400ed1c0d330a6c39b98a575a845a17c2fa017b861d0811b5e565c0e80b35409d5499120cbf87a64239353cb244e043f3ab37fd3d6a0a83756d83139e7fa068eb5c85aa0791a63a0b2f182b00cd5a70fd7547e4545709c10e25ed261e15559d1aad67d34463ec07d2b961048c22db77c80f3786f3ef0bd8f7cfdd351dafbf67733e082652e10d40a611487e0e8826e28ff0f04011a2032ac43c3ad3c54534367aced620c1c0528bd315f524afb8bf27ef40b58c91ad09afcfae160438c849cdaff1b9cf53862fe3211e6a25b41b8ed87d4f474341de037f6fc89dfe78a1f5764924a40d472c6aade452952ac0faf809e3338c9b42420101c0245df4c9bab076f74f18ed82a597f38dce93c49d03323d15ae011bcf4006b66815af6f800bffd6e3052824df3fffde310367f359acbaf3ca0c8212931a03b3366c9d8c4657dcc043d86a33a91db609e10d740a0863913b34502e8bca5f07920d0a89c022d8e0a708ff0a1f68a78f2da4d1919c7c8c0c22b905891e02e50b0d3bc168d275bc236e472a33174fa16372d4fcca9207800fa0e1ccbf5fe6a90e816d800dd0d1f7e9dfd6c58c17f835c61405ce9c165c198830d7916da48f1a07f4eefda56309e4e9e2f555827552957a1e669d7663c303927e03f1008a5c7a054fad1e68850fa8cc852d51abf58e8c258040a2986de2300a7a274cd0dd6d8a0355910aa8d78b684b1675df3a33028936638d1d09357276f96a673ee1da156900573b5041aff676fae6be345a2ee2019e1af6b75e7fa59e6ff183af82c9a2480a32dd13c46b44168f23a3287fe9e0b65f32feeaee4ced698966caa0bb686c140a74a962c031f759595092c8709cb38f4671d3b8d1f6012d3bc9d4e0df72d84806d8e4fa50da97903d0c284aa6268620514da2ec4e82d8e948e748c6c87f60cf0df681388f312ce798ee423d4b5d4de49d116e7813912f1ade79c3681aa50d230ab5060d14504ba2a5a95a63ab42fd7c80765cb009d4320466245f542cb0c0900f466c0d78a3b9efd1abc5d10fe56f3a8327c43a0798aafa28a5fcbacdbe23a309b6e14480b41cc92a6594d034e1f6c1e78788686e44162382c94f17c8ca253d0478fd4c7ec6ff5ed2f6d8c1e3bf8f9564ba27bbff5ae87a33a21ebdbc4661d904cdeadbaae847c77e341d6a1f90c1d4a62c9a7708aa6da43070ef0027e3f9480d03141f56e7ca61787273ff802fc97c809b93923cb2ad9c1b505032c4f98629007de0ab2ab010a5ffcbc71cf0d3f0f6aca2cc91647c7a777e39e869e40d73b007bfd718ffb86a908bc548a5fd15a552fe1ea50f1d3081db2df1067dcfe935cc06eb4d54152d7d845f079bb13dbd121dc3d24a2bebfc0a74f859f126fa64d90406c6145b279e4a87cbf440a12f62193039e495378d07ad0365e1c409aceb9c2a0ff8de51c973ab1beacf59aa679de9f2124938b167e1e557aa5039d8e46cd84e00e236ad7e8f608c6cbb451bdf6ef4d216f83e56d555e1f030171c0cb91d845e02c4ab25503a967ce7abdd9626e834f5d2d1fabbe295a96ae52e22611c82e15f013a5b1af61b5a4e439e7e608080d99dd1e9ecf1da2c7f7f405742abafd336760ba664327401f6238fa3058f367fe1eed8e2dc078cde6aa016de39ab8ac7544f0f32d567ad637ef7e614c300e941f7ec9cba034823c80f744cd83702b71f621a0c62a9bec25c02822e2102f4893cad351cc5dbe989fe36842b07aab74d920a460e43d30cdec78df7323f2cd3c8c5b273ca99c0f96315432ac6cb4b75e57989640503ad6ca5bd1960d7e7ff582c064a6fd77e6afba440a1d57bffdd028ab4fade01ce6e8d4e671cd9ce6b33e108d0f4ae4f362804451e414cf65ce88a5df09877014fb6a452ac4d6024944fa53b1148fcbe2c31707d2f53c2d45f41541a8cb62d0f84f5cd6c4fe8b8638e414b40051bee64d39450f56c1f6d5964fd153a1b5fa70bf5bd40f3f1dd24c01a711c637ecf922783a483f902eb48b62c6fba95ae3c740a63887d4674622f0223556c9954f86016fc5661acd7b38984227a264d75187501da6a05bcf53cd8bb9a3a15f991931feda0226f1f713a215e1febe833ce3c370becf266c53629baaa13cf11b6fea489d70e72813fd7a5a60a8b0a761fe468450dbaede5e5afbe828a319b4e0349343680d1843d18a4449b1f7ad2a40c0a31af086fb89b2a04262b13fee96e266e1588995f23b0b8ba0445074434dbc1496fdd06a22906ee8e1e75736b6daa930909806b36932e63b49b9b94eb2a26d282de460bb7b709a560874988f39957c540e2eaf5f26783e461b33362f6f3ec1ffaf12404a99d44d03dee980ccf28aea785eb1e6fe89f43403b12dc44d1af754ae6fc400bbb472030aba2d55d0238d795250ab3b556aee33b8d0515f1fc5ce2931fea2307534ecebca833f174ab579cc829499e89bc88c42ce8ab45b846fccfee050666009b75fe4eb7fe9d2dde01b95fe49187da0ddbd3390f5d174e347cd62598fe720623adc5b7d41397f7ddf680531ec81495baf7f6b83bb95a2bfab115fa2a4e6904353db242b623ee92d3fee83c7a9f228fb4afa51ef8523df5fa0e57077cd6810d362296b7d189ffaee16b317d79344d2482863326de6bbb1d42de84572c2af10f1275a9f49f574604c9bd828b92740aa6b07a606704217b9c1a9da4c12d5a0501af619cc902a4855c9b4900f671336eb8a57b5bd2cfd132defc1cf13391872e02294b09c164bc60d9901c0009d58402c84d57280dc0fc16c5012690882fb2de0ab391e0584c4850882ad81a78273ee85dcd7c664600973be4f210ee6869b63a0d5a15427cd5c0e6a65ee1e05f9395b21d47ae4805d0b1c28539171a3d8e006901 true -check_ring_signature 85919cdd5e08455dfccb02a00f872d9238cd305a69ea46cc183c74df7d1f8da4 59c029bd06549d4b03f1688f0b254b487b589d259e7696d036857fc6eaca83b5 116 30e9543be528164c00fd70bb15a04807d4a589722457f01d464a53baa2ee72b4 86e5af830c867bdee0b1c438e9c5a7fd4be049cd5def62e38592ac1429f91b04 ec96eb3ec1102fa9f6df5e33093896ac61ca2fb23a10fb3cf7d36f1c5fa32698 9c7b9f8668ca86256edac07b67eb032b356902686c0f5d707de0b570374a5a75 d4ac0f3dd29c1b8b6fffd862cd4bb6ada853f0b1fe0cd2e3220d7954949d4d8f e204fc2293a806100b6e34c9f4de19d6d2346adb06e0981bb5fe50da3d3a0f0c ded372a3164a232d1509252ed4e1236608980529286e159fabdcdc5b28f0451d 6c6829cb8e8e8aef7da10be454b7e299465682d41d9949a65d3c0524447b9a6e f53b4e93545101c067ad404f2137e138c5ef68b3fe44d181a574bfd25c58c264 1882b403c4ec7c9bdbf0f7958d5dd41fa3c5cdc51432e7c2df5592f7afee7eff 5784235087d3f4a25ea2a13f968f6e7212d24926a20edab822be5753fe459447 70125c272507383f127cd35689e4142a1dfc0c735f433be3a5346abe41537217 aa047be2b0244d5c729c090db1f0a77df47c5d4c6e2ed7718eba3fdbf867485f af0a24d3a935f5d7aaf9e138e1db96e1e86dceea61fc36ccfa0c6e2f71ded80c 6ab2085994d9865a7372363c7c77ee737d386b2ea0e7d9ec5eafdc3cd3ff8aaf 279abc30e52560040257f43f993a4d0b122f4255cbac5554c92f2662f58f768a 3858a35b05cde69818bd671da15c4e417f9c6b2b234a248268bfbdf0d0e35693 3edb2773000eda195d84db462b3cd9d518331d27e9c6e6b1b66d41178edee99d a3e1056862e34eb6d4fd068adcc6e7f71f5771e8e8aad7f6ead1981946bbcb32 6c1959256f8b56e2dc8dffff461cc6c8e96f3754ad289939ba94045db1832779 c05e8e24c6b6b8820b58e959f4a041573dea025823105a33e50519f9e6574175 6a6bd30c6bd76325eadb776daa8036bf1e56c777ca1f290c45c8e18cb8ba9578 e6a3b25003bcc84b3846c0d4cbad407b77d8d43fdd46cedddab05f605374a404 cb404a1bfad15945462cc839c4fcd40b7ec8006fd2a91ed5842a63a5f0cebba0 1ca2d5aab6fcba3713c4836efe7347d749fd3ad414f608c398b127340f2c49e9 f8edb7c361155eec87341ea1bcf0b329af0d2ea618549906a963aa3f69209bce 22d7c785878bc71f9cd706fdf9b892fce645c88cab4432cb760e2c40a87ad3cb 737645d1cb47a9996362095251b8da9e05ca786bad735b6127bc44f25886ca87 3634d004893181720cd7ed37e52ca6b362e484deb44df124115c411b52ab465b 12072175c95ef3773535c16a17c3a190904701388a3e0303d11534502a87c524 2a306e05ff290f73b1e993f86237261017f77f74f2aa58114d986843f2dc5206 dfc9cff11a13510270cf1cbe2599456116a14ded7078e29a8ba3af9f4c8a45b8 5c8bb36a6ca17d6d81574b8d44c86111800033974f117cfbe3b37d59bcdd3184 96ea595700c18777ca32cbabf43e34280ba193547aa20dd72066004493977930 2550940e22b64d96c2e6fedf22e9a022da1cf23b9619da752777cad62dcc44f5 17e5f605c12b5040b7e41ea9f1279ba4989e029d697653138fa67170d200b38b 6d1f926306d22e10ccd58b9d55ddf912fc85c5109eb4743137f33c1882c46d82 4a524fd27a55197fdf1a64f0c8dd4ae3665c86611977f7ffedd527240c2158ed 8fb541e01c7402737a2b6c1a036dcc71fe789f324759e461bef0278c098f4538 d42c6c0174b9248f68fe106e5dd68f04c8ed379b75151458bab28c54419d85d9 4b55c1436e81d6b2b71687dbc2bc5a3aaa4e463bb001be8462006d3a2e3c40dd e3b037c43e24b1bba8ad64dfb6f132780195416a7865ee48078301429da44b1f 07628c6e37694c2964d2cb59c9742f56d22d4e98ef440c5842e844667e26e3f4 a2071dd3740a132c2095344cee8b972d7ad24d7f1289d0281d046285b8a0bb4b fdf01639e3c3781aea49ed8c74e7bf4bda8c303e5cc2cca5a59060afdc8aae2a b2f715a8908273cb08b59c873ed39a4e1431b8f4896e40d70510a1d433b73622 6299ca390640465fd3f6cd030657f3c6a53bb90019d45fbfc2c9dc7c68a7bf78 52e0246fddbdedca2dc04d0019038f8637610415f966b490029d5c9b7971bce7 346da8d2325afa0922e1bf8a1b296ad83b2d007c2d8df2fd7c0e55259ff1b608 fe17411c5877a217537811c421d0ddcd3593b210c5299fcabcc8be7768a62c03 44cebd130c11d4123c85c2d43bca69bc9e4ab3bb622f1405d99176f9a4543926 85b7c48977b0112bf2b5b80037bb47605596ccd4c570d356eacadfb8f6573f2f 9c3627cc176b1b3623ceb3d76b0d4bea47e60db563b4afeaaa022ac156aa2af5 6cac647c79c469785669815f7281a08a54ae38d4cb1f27a0a0f8a23a07bd31d9 7dc66393f7a683c93118cbac286150669c90a66f17551ef7dabae0d676857e7b 4e8d652b12a76423f41bcf7cb756503e3550d2c039c38376f27f79ca47be9799 46b152c333b6ad5c2e61193340ed8dc08308db63b37dd622130f4e5a33ab6d76 b29a7af62ffb071abfa8af61e45a35ddd78f51fa6aa6affb25622c7fe6ce9080 40bce790fdf61bc6e500a4455bbc87788609d96b471d8bdb1c80494702708e65 8341733aaf38dbb336d462cbe478aaf7bb968a9feb2f9be848655f004692fe47 0a20d7df975f05a702f369f422233f01cbac3e6adc8439a477b280444e8eec47 5b434777964a5084900e2eff629ab8aa7dade42d37203c225ccd138d10487f71 06a464a52202e5668951aa7a624f034c925e2400e2f585f1de03ca0ed025d7b9 3000e767671f15c36793543e6080b9a55f38e3604cac3742a55bb23ee5792c84 44aaed90e21b033a71139f5606298ace7605b355528d1eb95cd204e10556deee 72cf84f5c70fa73bee0385ec2065934e9991248e444071cadf40204daad55835 a63efec33c90410d47ddc540eb7028bb2d73dec776e5c3f15fe4ccb5a224e070 056ae42e07ee14c810a905c75844fbdba1bb013de018b0fd7bdfeaac30854d08 5a3389b99abb942ea16bf63b1ff257d06c01fbb564ccececb39e1d258d05c99d 8b79febc5a9eb3eec2e288bae8a0ad9cca5ac63d584df941ee5c8e0f0b2f250f 90ec99e002e0fd0f44b8ef7015e5790fabfe7f69bf479bd2bd71c538410ddf5b b3e347d1b02601f54429755bf08e38f3c5d37ed4881622c776c420dee3e861cf 3d649f6c6997f1059b747265672e927614bb9da1619be64b8aa64a77478d1a92 57f2802756fa9acdfb65dd1d5b8267e7585bfcfa3453f1a47f5bfefe40344b6e 071bfa5bf138af95c961989f0054007b0b95bd08e47dc3d883a1d60ad8437a7e d4f84c680401a3a63af8614caa1686e6f46213682c9fd75cdf2ac20e4c90ac77 83aded75b9259b9df01a3183829cf5f6eee5b55f8228dfbbc50b4e3a33910c27 16314395b013e7a310afd666e83bc06c0be1732bb7c43d3f44db2ba77d422bd8 4cf5b2fa58dc2842d492056faa9d87afc1c4aa95d7ab111f8d94cde44213837a ed7a6468b881c68820cdd6dd78a86d2a035593c71d651926855d70c8592e5ba6 11161763a337afe49d28bbefdf15fa11161bb048fa85c4482c80624a7c64686d 87ff8f524f6b846688fcdeba8ca6cd95189cca1cb4f009a81932e92b439a75dc 274f5ccc817df9ad606c3ae4058723c7e31c8da10d95cd5b6b1f1ceaf168f60b da1fd59fb499662535a6163b561de4075c3c924e257751a2ecb04987d41169d8 a08b5b569af9d70003655d0954ec5bc8150fe31e223c57fb670af1fa6c7ab704 40318267ad5a4228c6a0493150e0d39ae1f1721f4998480ce3d0d0c636f0d0c2 684fcad029ade48ef92be05fc9ad3f6cf37c95ac7ed351c4ed6cc2d7be6fd391 df486495d5663bf502d97bd933b05f62ddb6111049717d70ce50efb8cfcf11b3 8c61ad6f3a41e66fef2fdfd37ab929e094c8836f92b44a83d12590ea451b37d0 167c89c38198ca427471cdfd1921aa6d33fee7c7b8150e1a05c8679d4500e4d4 09af1c7743c264996e5aec9d7c89e7de0ec75eadcb5d5fa5f2139a7340d1e9f8 a7952149344a15ce7ce7230c57ad799b72f764e488951b2cf04d3b08e7c1a994 39f52b8dd70b2f78a554488b9d38845d6c5f567602681c6ef29d2055d605faee 26c9285904e2b40b524ed65960ba65f0cf87066139be4010ec4a0872ed1612cf cc1fe621049937ebb37dafc7b86e62792faa5bcf15108032e3c62d22bd38dd3d d4b023ad37c6b40839063363296059c0954279166b753ddc2d04d02d1c3fb8ca 7b59b2f12125b5012ad44e2ae8fd925730934d4167ba2202ad88cf9660135cc5 67a6f91bc66b68311dd15b9cbf5c224b80ba40c3f1d965403be6a22f2ffbb765 3634e9e0c1ea06bcfbfd04307ab694696929c379fe175d2e5ab582eb815b808d 59b5779590ab5ffce7545c056aece949e18697c7143f1e8bc857d00638db0e33 2ce1f30759c7b8043dccf6a82eb6a924835a406c6f9939d15ecc3d148cef8328 07838bbfb84ef0165899f9fd0d82d4fbd24639b40963227b6666a8fe1bce9277 b99c35d9dd7652ae9960b7b675e2c82d2dcd7b2b207fe54d31c6daaf9083b077 e2326bf991f8e390b8d59576bdf7ecabd91c5c219977b7660733affd6157d594 52d2503ea1a62804ebaa9683fa5dd278d1ccac455e3744b893575727cd1b4f52 2d7e70818496d0dc540ca42ba4a756b542719ed88486eed3c657a77c42af6dc0 5123e031643a7ba28e02a8a298fdc8dfc4d62dc95baaeb0c333a6cb706eaeb5e ebe7f0dde7140102a2954cdde2d8af1ba4b24035e47b21843db454c26492c44b a873bb12eda015feab38a9da64f3c22f595fe3df6fc91ceafc5cd28a805b3802 940e5bfb1a828c3c2466c783e2e1805c25aa1561c55544b89942f5f53dc37969 3f0f9ae294f3c64597774b113bc3e451a71092a749f8bf1e31e44838c8cbb16e 34b2c229c360658fa49b06ca0aa716a3bb4a4b551f2b087c5225be78d877b547 29cc3649e9ae00233cb645286a4918a16421204258939275888a2f875abd7834 29c34775fe36531784bb20ab10926a8a4ec7fed36271bf5a9856e54a62e74030 977b5a57f61875c61b18696bf876242338e95d0bcffbaf5a8f641a92e327c6e0 3b2a7036477c516dfe15bf19840acde75d3b7809db33dfaf9189e9b7424cf0a6 d4e828d3b0bd8e59c4d52514e12664014514c60043ba9d6991202681e8edea06c9a6db07c12331d21d32269385e375e4f564598bf820e5aa271f26575978290a665033274d787bc5636da0edb1439e41bddc8b5cb55632429879b07952eadd0f5d575ab07f118ee644a4a8ec5786c7f54e037fc3af08cd9dc407b94252040306d28bc62f4b542a87ba482f0dc457fc0c2e6c986b768b3361e1b240fb1dcd5603823fbb1e490d8579e902c045160513ce3ecae2f6c060b88cf857307e12bf9a09487ca6bf28e42aebb074971d1954a5dea1a669748963a31190c53dcbe792df052d33ff58fc5fa2b835eeea87cf6924dd6fcd208afa830021bb0b564c20d0d40d8b807e2a589248022e875bf2fa2a111dd7d4dd71ac023f1be94b20bdad267c09151a7f1c53289e494c1e3aa1510bedd7010f4b0ad7b5fcee02b00ef43bbd440b00d1b206a4985d0af4c0f438f49693cd03bcacb50e729bf63255c47c297eb502d3d3049195b30de4c62658878fb9ef4b532bd79cf17fd51ce97492ae4828660b9abff10208daf3b9d84a612df9804a32d2ae58709ee64668cbd1b9427d6b7d07f3d625baaa11fa34d638dc7e7f8f600130c091e1b1ecb487a324b5239261460173335716335e23c149b83c2f8d64cdf53cba0dc5fa859e46ae767a758a9a0e00958eddff803f50b6f4b2ef9a495805e61f7a8179a84e97acd8b0baad87cb470c27766fe87e9ccb341b63f30a64e5bed5235534e7557f05bad324e97c2244580e7026fd42becf619d221888c772991f15ad05b0e97d973bb2aeca449d7727c00d666573f3669dd72f92594bcedf408135cda9b93ed2583c69dca0b869c1b4c80cbd1250fec32c8da2b0bfc8948b5cfa43089c4f1a2f2b228712b42f8984c3eb0836772274490f292c7bc780f61c5fcf7670003b73dd95484b4154aa6a6dc8a008cf0cb6f7139b0d9cb5988a2247a01cfaba5ff118b39634ac6e1aa135be2980058234102061eb1c69a946732109ba46a9b3c0c3044b614e0ee371c1f0f6ba0f0ea6da3a02995593ebebc2349361455e208aaa5af7044747843db71bf6136dee0f704f5071d5c32d7639a214b241ca85e4cfcd1ea729d7a7ab40543f2bdcdc810e0cab4dc7f61ec01240a669223d3548618e5714a79bcf0a7fe585e53bb5c4f103099b36d558af8d3a465aff4c4f5723fbd41b580273514e90ea004a94d6a646086f3351317e1a05ffe4d7c525f4d63c5bf4d3fed444008043af77cf65b93cbe0d92fa630863dfb47d3fa8da92efae518075a6074cf1097f215ebd81d99970110bc97a4115ebc088c36b71a0a5664f37edbb59fad4c0be11d3d0d0b4659fbca80e938ed229eb6d3d5161966eb58a400650a623133c975171125340822ff2253c0edd69a416719e18f2330dea2bd50da1c1d1f4f5a48b9b9163efce73283614c202944b3d59de39eeda819ba267671bb4a061724febded2b01bbc14a55a40a4600199d52023c8b930ab71dd7324aa8f0fb416c50404cf592807d8fa709c0e12190d3285ade9513935cafd143c0a8cd003e17b6ae9161c77accdc9deb5ecda05e1076e5c56009be1c8112be00013274e8620a20bf4ca276d57f5f76e37f0874388085877d755f0c600bf0eb3a31fc57f8646376b795499c1e0595e9d1e1967408a0b51ca62c65d76c26fbd0162ace5d85fdac6cf2a8061a2e9ef7ef260e874935d056daee80c1f9b45ec6a064d61b2adae30acd0e46799b49a546a3d6ed2eacbe505dfc921fb84e08eb95d383ac537d34df9dc1ff9cb5a3a37e160257a0faf17c304fea7384dbf1e77ec54b761ced19818b58266a2e37a8a556d02e148838313770e543e6e8979bc0f8aaf695533eb14e6c0954132e440bdd8a2dd447063c0c10f0a2331702cdc00af8e48889a2eb4376d630797884ccacc6561c78bcee36e3ef2061e9fbca3e202b25f20523b074439fb4be654a61fa6354803ff01c0ab7c300d070d283fe14bb6ae6e3d624652056fa82a2efe57e1f23d7153f63239988024090e1b11454b821b682519780145ac38b6893aab16cd8c4e2ca8078b3c4b5cc88b06f3314c107e7a25aa050759f4f5aad0c37c511db8bb2287d485b8395e6b5cd407500e7167268955d19c748597f857848755c42cd12e785724bbccb0a69833d005e567ec4feeff816d2d603e473d364acb573ab802e3f21fca77f800b78e04f60023aa3c6f4b3afdf593a0b5cec412137ba4c6d7d35a027c3ed0263df780755e00a15f8a22601d0133a562639bb4de85df0f9574ac22237b173801c9e2fe3103062bed9b28b6701ff71a5ddcd24ab91b0fcaf4f8bbb9a6c44d966a8f572501f00ed13e2e10573b4684da93e9ddc5b92160d7af5cd85973969b84772c46deabe002cda397716918cd2252007f16c14fd176ba5f4ba990e0b8133d72ae665211ec0de29c32bb8b153112dbfa3fb7059f5bc7aa8ae7334578e315807793b4d2e19d007ae177970e9b0db562cf53ad1665660f2e48422d52e44e20d1ab353d309af40ba05d08f308461b6f54e11cfd1a32fe9fc8c5a01c4fe5bf9663986e99122a870960466206873c7c8602c258ccd73ac153d84deaa81bf62acf60ec8fc7bc67460f0ac80e81e3453b787b15b85cb3dc7b336035a33194e7a3e2d7a65e1bc81d3a016ef1afc8eb6e10434e1c2536b2b535d2dd836527277934d57c94b4bbaef88a071f7550d5919292fe36a19ea5d012d68669f5a9781c27b73f695c996f360f770e655c8e3d1121f6a728107557018e995299ef62869e2600ad0996aa8bcf6677017ff0cc53c62bb6e7ea94d04f1f9ce22c27c0c6075f3aaff3b1e8f6b69ad6910bdd5a867fc6726f98f03d38bbc05f2fbbb0da88d8d4a74f9b7d9e16dd3e789705a488254cd2f752f5be45acb730a440e10b2fd91eef47f1aa80f45a9853a1f803df3acf994c622ac0c79a1d7aa7db9af74684b410557fc37f02cd9c8f100398005a76af83eac77e8ace1208716263d4a44fe2130876a36eeaf7c48a350d3f6a03a3776b527ada2fdf6c03feb27da52d4616480fad65002788e58f00f0559153099059e6266ac2cd05dc6bc78397bab1cc16ce76f8abb8d96c46d18c2a9a323306aa55365470f45db86c1d3253c5b190ec432fe0f73ca01ac2d43846cb5f7e1a0148ce9dfffb49bab2f0f44abe3fd70ecf903ad4ee9f41e9d0de758f58207c620b4e395e2307ce5be78c18c95dd35d1acabe69a3d0b3c6474a6dd0b88a8f66560c9e80424d550f9b1503343a4b06b571f0dfd142e7c05d8ae7824b4a440bdef5004557eecc45688aa974d8b4c47696ff908cbf96b7b3288f02f2a81a77a9aa40067944b48415812f8041f5389764a1bc2ed0e3cae855f9eb276d09c02ddad5e90c8734340a75dba92f180d3510c27eb8f69d4085e7e46a577c377ecadea61f92063224b241bb25e678f1bed27dd11eccb784ebf4975b9543075ac361bb08708f042be4446d4c629b8f49edd3acbceb24518e1a6aab84b29cf12ec1a69e3193b505dc68abd2dd707422316bc8ae34cd401cbae022e44a0d56b1e2653c048e42b6040c26b5d1b4e2daeb9b479615a9d64bf7e24a69bcafc1d1c82a88625d5c52c409bf862033f99ca261517f3b44201d60f0804c19e4e676dfd55b27730a0539dd07646f22d85770bd46730e1f2729877fcc4b9c3b06e2daa18c28085d24834e0c0a48154b234e60385abfaaa2760394087582d049e7f82097ff755bbdd8fba0fd0b9f96981021e95be75444e8c276f1db4cc4d3be1c3077304e4aa4ad1541dac308ab2d8007b999445e43768871804e3d3eb3cb6973b7f9ae1ca3b8c91a1062880fb5fdf119bf3627ef3063c6ebcb70b832badf21450dce1fd88de9af94ebe7f80598e0e5b7a09c2fd2e9c8f41b3f39633a1d395bfa6ac5ab92b381395133c52c0fe5c3e248c00a88825d245ac8daab5de762243e6c40111e57f878fee074ecb705f8299ab06b7bc481409a5114a0fcf10d9206b57d4da52c7f6f5e37ab56afd60da6c7931694565d16a599f5aeeb05987ad710a1f728d125287160f23a91afc608617655bab6f2d2150a8c4251f2418698458518de5a77e6268fba71f41e39230c5f03c6f675e1018a73726e176d97e2119b941117e9a94c9bf94c2b9f14a643004db3c4497c7f8b130b19f8674bb63486205e5a5231d67b49ee51b6414a2e4608c652bf1bbfba3010750a5e39ff5598acbcf6e36f94e7a78204ada7029187da039caf37f4e9bfb5e483ffa075714ac6c005fdc082ba90b99b30316ea4bc26f70d46bdaf547328d3fd514d82cf8a08bcde1cfe527101cad9df9c9dd22fa1ef730ee56489954f782dd52e922fc6691c4ebde1cfed391bef48ab2ddfa7727c23fb06aed8c0895491a205f0a0418fcd82b3474bcf5be213eab97001ee80637481e30459b5809a1cd0ed380ad4e828a6a997eff2a6b49f2e7a1d865203f72f543a14014a138f38f6b16876fc24b70fa3bcbb763cb204bc46ae975a3fa92b31cf7b1f065a89c39d4607a01fdc2a04d76eb8b0c72a40d3ff940e33edfb5020989d7e8506cb73fbfc7cb57481dcc677812a3bef75d1a8310337939d000aae6fbadd4df8078a716e417d0e04a52d39cb9383c4ecbe5605840d4c3d94f00ac37a640d20910934d13f81290638062be20e504b2f655582f5c69f362fec9bd0a3ec01c2757a0133387baf31336145aba29f34f63e111ce7fa3400fe62df28c00b60b74eed2e0342de359beabe8fdd364869d55d80ad46f6614879622a7bbff67435647bc70408a081e73c164d420a76cc8af7222eb1fba9310ef6adc5e38f88e654c0417b350130b967a3006b8ef25af7ce9f69269a0a85ae7f777c81322a3552e05d90e5330b34e5def98baf1e899db3631b12a7babac89cf1404639dc92441487aa466bd80bac6db47cab6881605380fb557b98f57800ab95f32cc253cf99142c4c804fe506295a26388976d4416b6ddd4c348dc5a7cd07695485fc299624a7ba9b909b5b04501e2541bd1e1a4c71eda694233191c68250bd42ec713e05dd839dad33e97c0860d674587092807a41c9671caa4c1855934f0a6fbb08c187c4e9c7fbeccdbb08ea2309923060790b707c21363d114a5d62983934fa2646f44271b8773b385304370b11836ddd89668c060fffa2f82d2011243b8bf36b7f0bdb0b189c9b526103ff00ee9ec080dbf5b189161fbf2eca7daad9bcc5429361334a94d3f3cdf6fe01c13d47d3f73c6d3e284f3e6ab8d67e2de4dbd0ee7ae9164839da4910b79d0507a30c6ba6fb64833420a970ffb47d41cd1c03cea871010a7188dae9894b93ff08345537b18579a1dc0330facf42e9965d09f71c99d46ea61c5f636d1bfee1e5015c3e545da4df2f27e4769d813313423ef92d281efe457178b5ef8c4887d703053b5694836bddc0c2d1eea408caa94e391325aab5af408185393196f831a98c035e6ccab57c1974a5abc6724cd715024504089d076d0fb6bf143eb8c7f95e130f37d6d46070bc56889709495deae7b26a7aec7043b7b64d3d47c779ef11ed010db639bcc682804fdf7d3a49a221973c8cc7fb135d04359b445123e606d9d76501ce58b4692d7c71a70898153776db34a778cc1be3789f3c744eb1f8423a8e8c09d4d777409586f8ecc12cc299074c461300c1328879e435b98a1c0676e1081b0a77bc099e0071aaf5385643551e54a9f293418a050c141ca3760a5cadb1d61009955d5eb8c7b4f8cccbaa4bb06ca7fdbb6019c1e2753edaa20ab833cf7df8190755e337a32f14477bf0406797b64c5a4360e688756d513fecb5d31b87d9d8fe05182b6e4380d79fe0c58ce24a029c8859ddc4694cce86cfaf088a416f06849305dc6de776a5aba78166161222e22ab1b2b96a5004cb2d47472031bd4dd6d2f109f62d37c701aaa9d8f0f8576bae407d895d406207da1b850e27483497c838de0b75b148df950fb82108c4714d4399de8099a90947c2d276ef0115c6be35644d0667f900e18ec0230d97bb89cedc33a390a97a3db42a7511b6e16792311b739505125129e9c18751aad691835044449aca091bf09c17b60f482c0310ed1c95200d7ae2cb3c9b2db1a4d06c0f45be85db8d913fe297da06645a118028f3c9a5d7027ea054c5730ba4675665684be2ac351f75369e51d4f2fb469d50df1a5b325105e89f4eeabf214ac2b95eef638dc9f5d1fb5481b40a4a2cdd5c7ef0aa93384f0dc0e2ccca8cd05daa18ef68bc273b91ba15d07080f86a7a29e5dd8ff309ff6c02ccca2aef9983e506f760a3d90960509774adde8e4fdbf894fa5921b7f64ce7098a21df7b8a303ae741066282971326f7a151aa78255ecba04198aa5ef3918100f862519b2738ac350dc3f4c6fba597f18d3e33da9a1bc0d5041dd588debd6a0b4134d78534b450575224a181987ced23751e2d44a2284f7662f4f9600713a20ea4987e64b773fa0d50a602972916dbb39d29b1d5313db4229e51c214645b4907d063f7d40ff98beb28882297c12b4787be4f5d6dc912c38e645dbbb85d75100dcccef10d0fd094590663e3553ce23e112f8cfbf0aff0a58a44ab1cda11e07c0723c6abaa87615966ab1123ee759b7db0c001f688fd852718e429bbafa0e35d03039bb8267a645924eb71aad4c35f3a8d2f00776c171f278421bc5ecb48e9490dcb93bfa5b8048af4ba55e5a597d43fe614e34a8dda7ea4711332aa637e04ae08248f32762337a9abf810d08bc2c0e7f3a9b544461e182d66cb2045017bce590e1e14105bac61face9df08b2317f48c8b4d517dcebb9393d497cee80213c4c104b7b6daf1fe1d8b1fd15cd204553e774de9b232f3b9176257d7792d695a78b8031c16f334fdaf447d94bc36e715df0481601f28224732f6fad15f037c71c10e08d953efe195a862e8fec78d2181e6b75be0d4ae629c1bd647bf70b853b213e70260196442fd5e12d00abd4de442b5c61119c9b694782aa705ba1c75d28923f1016e69f1a9bb08681e977b6831521ac6ee4b7d25ca6f8aa68451b6bfd774c08b031c8a50dc586ed248ce5e17d3b2fe794662949d998f26f5ff9c9255002b2066074bc0c691347ac0303d4a1903d98a256725e7c55cfc7a3b5b6978c98722fb9f030cfc6c3450c6b3bc0ec29d9002e1097a792e9eeb0cd62ca13df327e7695c38051cd2865fe046dd3561e1e1d9ad361ae7714dd1d2ed0f5ce1defe3632ef0d040012358b722a1dafcf30510cbd8371334baceb81e788e1bad8d51b3f0a9ecdbf0038dadbb7abbbbba87df4520829447be08de2237e1b9481306bb281dd970d9608a72caa9dc27bf3d71c592ca656c0dd50a062d32c8bf0e5a05cc45d5f71fb8f058f3bc94d4bd02c8acdcbac8f55b71104ca319efe108ae3b746bfb02d04362103a8ec97d707784233fd911aca6f923cc66b8b4d409a74ff920f01227c100ca90d3990cb5e32e1a27c7230cb9e48d76af0d189184dbf9fc1e1a4fceebeb70ca70ee0b31282c7c6d38986ab921aa0a7bd7bb44064247c7f62f798a66755c73d3a009bb6badb95ddfcfa609867bbe36ca29e2e3e547220c99dd12130646079c70b0bbdb62675e0288dce7ace460395f21ec91264b8dee66dcb0631de3ecdfb10da0b21e7ea0a66a0eda32a3f99cec456df822d8c27322fc9ec2cd559439ff0ba6a0ce73c1fe912b68f3d4aee88819200b1bbea4d4fa2f6db97009c1a4e71facbff02247729364317f83d2619b67177200f4e0f034b124ba29bfb963782790bd8ca0fd282dd497307505a8d6faa7f6c73fc9b97c5b6c0a36c0aba126ac5f59646630863c2f01b68acadd015fc1521c29d092e159cc2542a33cad5e9c426174dc9850f4f6311445c64baffabd2935b6ea9a1d17a4778dcba2d1af2b3da42759f4b7b0bd7c9accd2945e5a5be847796635712ddd81128e32507001db7fd970c84d74d0ee94e400dc3611b42cd3ba712cb026baf259198ac4fb85d9f9e69f38e6eb91501fa2568c6783806d2f5d2e1bcf96664d4e147ec907460d8abc4f0c2df9ca27a0174933cc31889fd384449aa04eab5fd5f3dd0f9486a39c704b84e1b0555fc560eab149fb3633135f1cad24c84d62e191e5988ec742c4b7463fd8de302f54e6607434ed51b34b63f645597e442330c53030a22c1c6a3e75ad0665f645566eb860dda030910655ff25150f6061f971e1d7c818f8c360a7b3e599b1522462bafef069168912602d0bd861cc8329497aeb48ae99a956ca9bc61a717f51383c9b8e90da544b5654df845df0ec8857fca559d3bd4b3c0be3a0b1868c1df38a7d42fde008becf9f8f67841f4b7bd89d7ff61e25d498a862a8779937361674388d21c5b053384ab2c4638a8f6d36c80195c0ded5f26bbc962005f9c6c6e0edbd32e35ed0eaa0a24fc72e56ba1ba6a126d29ce1e8320fe0b33cca47cde56a313c088c0c60ddb64ac0200085aa8ca57b91278ea01ef7a58bfdac646beffb45c249cbeede30f28c3c2b70b9da1d65196d98611f6597367875e8354696fd3aa91a359e452dc0eef25b0d9f2db003a8d1d8b551ee67ebc82b64374381872f30f4adbcbae2b5805386e06b1a053906d9d6d13e9ed05c5697651f9379c1be71fb4d9d5c513e394033b1479936548c3f8af1bc87391bd07f2d8acb28ba48fe64982f69d5ef07b3607531ab5c9476411795afa9035cf66ecaa967e336123ed8596217bb05a8901f10d61300c8587877d7acc6d3693638949d116d2789425a7a12200afb1783c1c2304d738679a049dabdd873e74852385e38af852211bd46e5a213f40fb688dc81101b52934816e767637294537348cb87d73b4c92730229a5ceea0536289663d8a04ccfe17fed2097ddf6510cf9a1b2f0b5fdb124f165fbb517ca2b704fe551fbf02c46ebdd39a4fff531738513f5713d801e8e9af643ae59b9a1e63c2188b73b40221729468cca6ad5ae75e4e51c528fa81f147aab0b8af565cd39e9eec0b3a5507711c3c11966e9c53cd59b7fc837bf542b7b6c15cd8df811a9dfce71c09315502c859cc3a72506372b2b409fd276da221fd91e1b0cca10967cec000ebb9f53d0b49010338932b85aef136e96e33b6d9d3de6a85410ffeee2299052bcc9571c20a469acc0b26c7e773184ef94a69cf8f3d861e6adff803936ab87f70bbf6751804b949f2a6b4898cd6823cf59fc93a82b79896f92bc179d5aa9fa116411c074c0dd77a8493b5259e87e656097eaa1b764a1b1883570c8be54d7b819eceebacc5078f97e2d6e7e97965c7fd52751fb8f5a5f9d93be50476b0ae2cc78f9cf50cb50e79d4dfffdce80abf29b3bcbdba5870ec19cb8a007083379494859097d963a50b6a3c20762e4d33dfd7d5755d8562c1db6fbffdec027734c2c1f02a7ab143e5085148a5a7e9578f2ca12b88b7d1435ba372ae4267103521c12204bc11cff06d09e034149c06ea086c2f7d6ca572e6106db749cc1bc3725f89d6650fa859428c009219eba13c1bb9bebb91883b237d352ce7cbab7df0ca636eb25ae69a38dabd0ee58f6d1a69fe8e1408f4c03e527756cd9f7994092ca73b2c3dfaf700f3e8280a9b6cb397610ded412bd776f86b8f4249e0a43e6ea84c1723ef9e2aa1ef43440349d5290ce08744577ddbc146105b85817fbc89b618ba486088c078e174e2390ace9a233f103ecae9f7452b913ae827ed935feabee6c891a605f90b3c6b914501a7eebbda7a99ef19aadb4305e7a292c8476c7d7ac68c8c3277555e673ec5d60d01287f324903811d66f9784ab700ec020e58b533cca955df8298e1a3c8273c0503048378ca1c5b7de8032f85143987542e5103eed1db3c68d5c1fe4a0d2faa0de771f15cc7c8a8a3032f9dd28db19568038f22d9b96a521751553a3d433d1901c6a06a878de57a2281e358ebee17ee85f6dd9789e6b41300d0dd3f53acdc9d0c7e7ec3271c2dc48d968dede3e78d4c3841b2a5a20452050bd4456b540ee8660f234da2364738649bb00fd499bffd7867f63c008059ccdda93baa48674b9e840ace494871bea8ee2386e5e87b8c9b1e59b0def1abb9cce4cb535c43a689065c01bb00889f2003e0421c9e1a337d915aa290e832782852c18d937a23cd84daa609438b298a70b20c536cd13228dda0e2d0bcf870345ee305d1665941d0126c700fd6d9cc768227f0d7cd3fe8c26cce42a4d6b769879cc86f0e056ea8df884e5204e4a6ddebf93e43b1a8116050ece1fdbc489696d99117812b89b330ae6ca2ee0281f30f1c3819bb0ad3af93b5d1c45f8e93fd32eb0c1bb338e7cd57c32c0ffe0d486169a2a52e0979d97a4d88b88467102ff67e834abd066cdb952b1bad56b503ae111e582e21aca543112281fd75f6b4f0571c383daaf1d3f4d4e5b1f7be00026e1a74020c8ba6c20f05c3bcda75b3dd158d23ec2d28c2ccb61f0c134cba9b08e99657960deb64480d27f37293a5fb64420fe7c641eb0412fa3ca5e2ecce6909 false -check_ring_signature dfef6e070fdf7c59eb55b33a1018ed9d2c2b89cf847314ce5cecfdb546a02a86 ae9edcc89f462aa2e48cba8883962f20efce779acdeeb8cbdc961a73519a3856 14 a66b01c9162a2513427d1fa617b3f57d21ff67d6c55620fa06f0f62b3086ccdd 6bd16e81006c460eb0a465743116a285805e8ba195c2e0081582c063b8afa3d2 efa23112e3968bad38adf5c0533e4c7edef6b24b6f177b92b70e167ec4d76d50 08686f9aae7e81ebc98675fcbed68f256c756c9f9b499036b223e04e84540a4f 67dfb6ec3960f108ecd986502a64393612f1b7b5d765570bc721c43100d7e395 b89ed8c9e0e0dd7c7592d9fb15208d2c931f8298a5a4814397eb43ad8314c1b8 e19888916e12f0c7192f1811900fed48964284e2c54e92a70d4b609a8ed34814 c3242bb3b62b99f74bcb5b8441c340b8f8856613ed67e6a214c3bbd596cf0350 de332467c71f6a69a631a0dfe4e8df859bec53c4cb74cceaa33d8626d08afd20 db5b8c9353d45cadb8b13f6a82773808534b210927d0592de88afaa2fbfa133e 1f5974127e9cbe1b4dcaec8a2f8186ff5f92ec3db036bc3f7f22ecc9b2121a6b 7dbec4784bc2a6a82ae7ca805a114508e9e1eba6ea71e7171764ce650ff3619c beb69abab304a89ab14045088b1e990784aec640e3de396f4848665c29522ae5 cea6725269bb0017440bd00d76782fa428fb417012a14324b3f78121399cdd9f 5bf3622c8257cbdbf2a54191bb557b5114ca1820aa0739c6fd17f2c971a32003fcb1096af423214032cd555950e3fd964293ae4260fadcfad93eb7ac24cd87009818b775642084e2ff449842135028a6a23ab59f555c02aeceb978750e7aeb06cd7000255fc4ef58df86ee5bdcacfb58b1e63cfdc27bb7faffff9770ea943809543711f5abe846869703999232de4c4f7e259aab668148d33cb31418e3a6060baa742afe08ffc49fae1b30d5bc6ad0cac316905c11860f0113b29e1431637103a512be722ff6276cbeacad0568cc3f502b9a3734a27b88ecafd308ec3f9e3606f10bf8a730ebf101309c28131462f207547eeb323abee8771ea79865126b3b0ea6163053f718e8bbd1b09ac5ff5cbd6743f81b6b51e83aafa7bdc9015ffb980ea52581c7aa40a7cc32f684d8356615d3f5ac84c246f5c63704121b76118c820ddb560cca23293aaa20a0abc5612c9cbdbd470344cc4359ca7a91650f032ab80607cf7e4d0a98250e0d1ea89506940805d0c8e04a357003bfbba375b8ec607d08b250ab42662b547b65b6058b8a103b1df51eabe3b093e68ab060eaf7fb580402d08d43c6b72896d7809a3f30ffd9f01e7b075fabf1f449c3f4be8bdbe5dc2709ea3aa8c1f40db0911e75c9cb7b1e9ad94bfbf233c0a3df11172c586f5627520f13692f79b120c9fc2656adab3aaae3491b82720f4b714b6260208b53edb7220236de430b083c4f9827339c591e336b2d17aaf97a8308d7f36740768a2bc294022af99087cbc68818aa29f6a092c573cbf8c00fdd7a5085f4d33f3050f49b4b0af607af5514cdeb3496ac180063a71d60cbbdbaff38fafa10f75d3aedee0dd50f24115616433cdecf9985ba6c147e7b05fbf5e197dd47dedc61abe47a43328b065bf30d5654eab6993689b8e86aa327f78b4925197356f8273fa7b312dca21206f31ec9128e2d7c559e57338dbb21aafa2f7a737b5c3905ff843cc54042f8c8019eebe5a7122af722943dacd8e98d190a69be520aa4c9ddfd3715846f5002910020a16c27fe99549c75e2e6fae1eaebc340f28ce2d12861d5ada8f839a31dbe064c2fdce6a620e0db0ad280b955ab9b17d3c289c121211a8155de526d5badf5057afd52499e90c750ef30bafbdd0d72cd1287e8383d9fdc654bef69fd50e7b10c26dbca946132499a61398bddc81b8727dda7505793d14f3e4fddec8dfeca540eb23380b3b3ebc3efa0ae37fddaedb4bf7213a7bc8b877056b286e0f830c27001 true -check_ring_signature 1070f4e3fa0df30bcee28e33a335b121e8b90d1686b7f2ce49374e6f2bc3c2c9 a59e8663fb9cb89a9caf108f8477109c844c5dea7c22dbe69f88342bfc328a4b 61 f464e96ee08be804e7f29764cf51b5a7962d851b46c196878c0b12730b4d1cb0 40f500a9bad2caa6a6c95d52f5059f56d2d58fe63f3936c505fb78abc94b78a5 441dd3e817fab7b0b1f667e1f6d9d85f0a211f23dff4bd19df19976ffad4ab9e 1c7ecff02c74f5467ce13a2788524c5bf8027a0884fa3e88097c14f47a6a439e e0ca8116cf604c93a7191f85f0546a2f4c1fd71349963630a569a8f4ddb67845 c5f0c959e05ac670247b9284a6401cbb51c5c09581575225f2bd6ee37783daf2 aa1abd2fddb8f3179101e53262fc8f7627bd1027e946e34af060e1fa7e9caafa e0e97a173690a10ba8df5b550856198755ac966fae96ef5397adcdfbbb1e4cef f16888f3800f0541ff2fb46be4738739c1c77c9b3ee9ce9efd1bdff753cf7f8a 4001e2a9856998f2e820686a9f6b3cb6e8c5964302d832189c014b55a56e5ef7 30a8bfecf36d1783793919c84b22cb884e8ce37f40d90068a732d49aaa1df010 931f1eaa55d904daa79dce95e2c54d66a279b700836b5c88bae78d382f36d91f b2aa64ba030353de9bb76012d1d41603550ac594da611f42053138d22241c62a c3e63c163b4fd570df2fbef8e25a5cd26cc831c3482ae50f2484d04d663c3b2c d8d29b15c0fba6a4731fd3bfe3fa6045699d38667633219b8950349f24aaf340 313f5627e27ef7304a9edb78f79201533c10de7138bdb1e5c2144dad7d5fab3b ad88d678b7eeaca413774db2a895b12bfe860c14d34e5d636a5346dd28a93efe d63045e729810e6900d9221718fc8d40e83e0e897810044e0769dcfa9a9bb4d7 42629688e7756d3788f78216fe35bf16d85482acb81ac8fb229c1b52424d2f27 407e97e45d5c2a98cc4200f6a343625b7e125ecb43fd9ce9e1b13a5830642b9b a1fdea10376ce0beb1192f800d136bd2f00044fcc89d772cf2caf40d0825936a 05351d021e8547fa2a2b92a5022a6d2106d8368adf53972bd2357ef725415d58 75080cebe0e00331b3864800a543d45bbddff70985543d76f36dea85d1c6d46e b808d35529d72c461180ca3c1f8e60d01de111212c67dd81c373271a81ef3348 1f9bcc3988a200000348ea84fae8c8ca0b35faf0ddcaa2dcb13464b83c263fbd 2d0c365bed0422ea3db9e6cb1a8000cea0ba1e89797f8c7142f062d29ab22ae5 5e8654b7053c11425e6a5017f71a228b70a0b73438a56dc0a5d55b8221fe11c5 f5ebda6bf1f9b03e92a2d03c2496dac2de9475c4611ca2cdef43d6a19292bd8a d5e4beea7ce2ebe8ea55bbf209ade16c1427de8c2ed5269d5f0c64d95285cbdd 1a3eb814d05e447eab2c076590e976491bd1aab22e44c9e6074c9b5ca1f257c2 92c0efd54c7f0af52eb2528ff10e70453e54b171d80630d3f8fe1d08eb418e1b f11e2411cd41d4ec19fbc6bad886a7ce3260d85d658569856ad7c6c5c53a42d3 e1ee1a500ffce398a1a46059e68fcb58e94974b20783dfc4c374c7285bcfa27d b996e2d21833ab55f0dd32652f38f2b974798ad1ebe639b5295af13141a13ab7 25384314a43cad06184669a071dfac1a4104ff1d3a25c2a23d1477da49872750 0edc6b97b6495f94de58b4dde4ca69c01071e480ebbaadf986aaf950449a34f8 23a5850aa86f7664779bc782527c0c471590afa20230c00a439c843d24eeb3b5 5d82109646dfbc20d98d45aad80234018c2bd10d4078aa95b1fde8c65bb74a08 670f583c03248b262f16aff32c11ec3d4cfd1f77b8dcf0f4d3d20c4e681f5543 e3e17cd0d589197f5441860fb646636dce53e88a57a28060837f9ac420b89b5e f90edfbc8ae35bafb8610668a0e5c46deb4ecd092713c644cae461e5aafe1254 56edce6d6d59a1683aa564088279ea7dc1721b7e71477c5d32af8430e64615b9 20afcfd2ab47c766ca06ee6fb27938be34c8280d6af6668d05bcc1c80a020d4d c7a216d414fc2e6a00cc63736eaf63c7bef2d24e2f8c4dd3bc268cf57f6d6571 737ac89de3d6037288c9d5c7d489ea6a8605dffde648ca95355635f8e58f64af 3db513350213a93f28453b789802c56ee6c1cd97a40e989547d439cf1e32b4bc fa877b5a803a79651230ea30087a263e2bc5f76d62ed78ab69ea13dcc622e9c6 e8c7b3279dd7f01e94eb25ea64e7426c42e4d1e33bee6f3d9148775ee5754eee afc30aad9e2edbab26b54505bdef24e81152d90982761134bff3edfaa76ed19d d92bc3299925ff48f688c6de8015f0d9883cabbd737cde5680fe1e1d204e6dab 48d0c7265269325c51e056df53dea11f85993c1c41ab58ced320edb4b93151af 86f179aec7f286a03b9d75fa7ae5dda5f386118928b8749e1c5e01323a025a90 076fbd42f4c93f6ad8a441626089ad896bbcd353e303e11e4b223f8b2c547dd8 4617ca54e5f80e5a7649683d73cd1795ac58a38336f3f6bc451b252bd3aff42c 2f98550965b07bfb1878d503e3bf25d70ab4afb54812a2a8bec5fe2320dde02c b1f378b88d1be4f962888e21f7471714d70eac205d759b8ad8e219985195935e be1ace658ca6b128f68ca2f4353da70564f4daf1e9ee835db8f6a57ce63ad9c2 cf15430c300aaf391c9b325cb179255d2f16ef38f39e5c9e6b302101117baed5 2574b913d15a6987f66e817537aa8aa394480dee3739a2a5bfee02fb2b7d3047 374d3ffd96356a0a153b3295a5b27da5e1a42e56cd9e5e60afe505e78a242127 01c3f6388b7cc47129ade2eec2cc6fba05e0b0ee2824d0d7d12b0daa2ff68615 3c75dac2605e11397bc4e4ce2dbd9e4caf4937da4be4a21ce2f634770a2d750522f9052db059031ab142ced016dc778eb296aa0d45fd5b836ce990710608100fc9e3a29665a17a260b8c1f6033878ec1de58c87b3d31ae2af303570553afd9064daded8b1cb91e22b73131fee1736887ac7d6e51650a816840bf8c7d167fa10ed26f6e1840f76b30182b837ec71808665d2d22e3feba924479209ade4f3505096f1e74d3dca3f8b20e731bc7a1650465a9ca90426c59e86cc748935543c8690a66f7306d11e9ed72ae5898c539c92d2064c4f9017b7724f36c647c1a85162d098ffcb2cccebec67f501cd311e706ba656934732966fc525f1b58567221db120c3e6246e2915c470aeec95b47ba94b216596284193874148d39d244ce56c6a40ccb9435d4210e9e722fa53728374d13c72aea5d995147365b189afc72f06cde09288fb2475f503a43b740049ef549c9f820f0910a3800c88c7f5b348883131a060f40c8abae0b77ef2e3c7165af83f3b1d751fe581d0e1bbed7d454e14b8cf308d25906a56ae184f584e2d1ae7e28a6659a356c05e979f6b5c5b05ecf4fdf660c19f86bca156b446dcab644c131d8b902b6ae8531f8f1d0c99886a4920645a50a8a0800602700900069fd54a788d4314561a3831f43ec6a51340f3cfdd0a3e003ac746d3edfdf3b9e0245b9031798e86b41736792dbf44a62307badb55c328f0096dcf7923c2875a875b1980df71b2e40a19b1950a0ce9e1684614e90c044290e8f18073812c1af56ba1e1a3ca8cdaf729f42e5d29ce980a16a0fc075150a450b837dd5d0806d81796cb7019cb3abca7a8a126d6eece7214b3781489d2b7e4f0a6986c66719452a5ed5da553eb92b12beec2f1ab1566d569a33f64335a1c7b602307eed49277982592d3631b1ff1c2ff7a40e2f7c9930558bed3de509a6c1ba0620932c3f6c991da53068c39bd89a72781b88dbd84339fea583d483c7fdb49608bb7ab702723c399fe70f6438112624a36a48cec7b7b6a1dba7f4ea4c961c6b0b858587725b17838c671352466735585abb43f0a5acb2271a6e876fe053738f089b87cc85586582257df57fc32ada9b44b2590a1f94158f9b68b21544cf252d0cd89b10ee6f3a7cb2cec6541f09c66c3e58c2899366677743f48bee2a46c95407ae78fa58569a0be548225d56021524445b7ab004ff684c68e87c171d9d9ad50a96c52cb04bcfd1207cb26489b46acc4b6b4d66c0ceec71dc2e8fec0e239b810bbeb80814bbeaf28c8b425958ea70629f21eb63ea7fdd9c1d22374c20ad3987083cdf4514f7ae6764bd917b80a1f8ddc6429692c78a8d41df43c7a3b94f97a307a924c8acc66412288a4a31ab8d4278ddcebf2f3c094740d2f1c5bcbae3beea0bba59c7cc396305cc72564845153aed1aac02785fad6a13b1b66c41522e6b4106e1df8b8f526fc1f5e8f144b7bb396515f54afee50350a0bad154e97e8750f10a03f692cf222a58998c97b3df064fd309671129d4c0e17d34c2ff5215a05b3705efae52e78e923fc53efb7d12f4e4dd73657eb93ab731d978ab660f9e01059a0d625bceeba43ad2afcd842b7c040618a490b2fc436acb6ddc2734e3fefb48c80b5ef9fd76ec4b4b085354f5b3c873e253297f8f774c58aa09270a003b41e96e07d09664f0b3a81394d60d758693c705186414e227380efa5f7b33c469b45b030affe5689eb3b0345b1ae5ac3e6cdb0deac816491ffb4100ac526f6baa3203b5025498aebf6083fdb247a8b89c476b763760dc1ef2bf82ef52f97400b57065490bbc7f4c8d9472e7d94e537797e5280f3a4d010d1e3786a6a9b671396dfe965303b585a16d799409b3a0ca1edc49d2d3e2ed6760e9a876764e9cda121d6c27e80ed882c620ccdc1487a3b9b877587e25330d259e3f86bd2977c838245b436814070e12cfb01848f73a6279bc125ade5b61925bcd881473e5185c737b255befee00f8dead456be7b784406b32c80d5b7286d75a65c3f3c1d4ddc16d341a354c7c0be4c345ec8bef84d43a4882cc8b125344fd386b92548fb20d85551e7ae715af00041c2141ce01a335a2a9e99c497b1804cce0a598de4a3f84ed97dc8d64d07e0edf578eff9fdfd4b16021f97677c760f8a6284e7ebf5a7c58d3c33d579e149006098115ba8f17f9c8b8ee04d88f63a9dd2b9b65bf0c47ee0bf4d329fa4053b10fcbd4593c5866548741d5cb6352b45905569ee7abcde2136647bc7ad8ddcc5b072ec87de3d42870af219b7ce1b7e979c2ad0fe2bfcd32f4d3ab3a530b5f3a540918d7c58130478ed2d53122280b6a6d1bb784a64b5315ee4cf6864596f2270a0d2580b2ece4caf33a83e47e8e35b5c1829c25967a8afeb457140f2130c22250025e38ff690ba69804cbf3639fc28025c9299785ef46feccfd5cec7f448268100d96afeffd0fde5e96e5189c6ac1687b6c81cccff0a3f508ffe26ef9e078e45d0cef382aefc93a2224461794652cd508683c3f8dd035500697817d11406e1f3e0749bec8f56fdb22604b345ea4211d3cea88e72d3f5f8ac42b434bf42bbf920b04973f950d8e40ad5facf77ed07fecc446b094e5944c9f2d28ca3f2f2de2a8c70e881e585ed4328cf6088f3e6f507cd2102aa2ba9645b1801af40e75988a0cbe0135ea58be4b19340325c67c0b4d02f82e5d5ea26747127ecef96d4668c355ee093bd1bdb546f1f36985c1f18343bdea3f89e60b1ae587aac134f9fd416a7ef20e2741e18ba6cf471bfc1b82b169fe98ff6d97686082bb7c7835669c5e1d5b2305bc8b5c085080cddbed7630cc04d2cefbefae8b38fb99580118366b3090a4e60ee44b043e5bdc32a1670ef54ec10ad233f491cdf8f37064293da6b0ac4162f30414d21cb97b6f3cbfd01398ece69524eb7ce8b2da001aa805180f1a36baf9a50a50f2e3600625e60dfdade8742ea8caba6d63b7b72c59a462a1732b0d08253403252648233084d9c4459a650d70e0d066953d0388b3e2ea3669ec3079c03e0504059039a8f62f06c020321a06634f1dc52ba79bbedc134f34dd69e65eb79bb80a6f26c5419be058aeb942d0614654a71fbc1ffc3dc5b4270d34bc62a2fdf37b0b6173324fa9124c2cc0067f970f65e429a4a28a5525e82e4e901a6facb0474b0a81c145b6882b5ea5bbf1a4488860ca999696ef576588ed02da2fba2a87539e0ecbc274ab5e0d3c24cd51156ae42a1b04320c31b2ffbabcce1af3818e6f2b980ef0dae22303e7dcf20b3ddfc623ed2de055e4885bd2161e1254ebb6ffa1819006e68606ace99ecaebff155bf3d9e764e403c121dbfc571e7bd731d593b355bb031f0f29bd62a362c2dd1fb2495ebdbd46bc4c8d3dd01834740aca40cb4d76e90aff4a5531b8c6a7ddcec7822ccad940904ea044b4b200a000693a4b238538870fa6a3ec55e76f9d86316738459b1aace6963e59e4fe0e659174aab69bcffa830fc6a02c65986a9a1045bed652fad7e65f2960fa89902cd9590dc747e9ed0fa303701fa2c9522b99ef570093a32afe675a66c0c8cd6bf7ebe1ce2c0c2bb5d70102dd2aace6039fb1281e911b9696c928b6218aff611399ad2a35513b888ab7390a1bd5c8f81a8734d4105f3dc4df5ff23c61e16f5ebd9daaaa339eb9ed786ca602b98e5f5bdfa39b068d1c36335580715bd11f55586d1489dfd89fcff2f830480e5fc3af9c01a0660aea31384773d690ac82f7532f402396094cf9ea58334eda0f935fea53b7cc4662d990dd4d5a873beaa02347aa2eea7c183d2a972ca7bd030615ca97f61c084dcf9875cebfff982af13425f148743ccc1ea0dbcca2eb0f6e0e2d635b013b3718665373311cf38b1eb9bbe2634867f549c4bf71fe1884307702725b1b1591b70c50f53517e641f1f756a076fbe3b3017d8e422810ec91caad015bbee214a1428613ab6b9433891f8cfb6ae42c9b946e138f7effeb450cee8f0dcf1175c14730c350b26d2abc288a80af98d332cb386d2a63a5aff15b1ceb580a9556bffc4ae6de016f81198f16a34c74df2781491388f33b7d05df2f30e8ea08953e382bbc8e99abf4b00562711c502f4dba934c762eb98cfb9a08b26cfe3207528c740d7063b3796a859046dd7f358f073c810db75e13f26087e54e8ea39c0e8343d1a7e4b426c197b6734beaa8bc03340bfec26635777b8554a3070e63e807af13258555050df90cb06fec7cba8b00d2cce2e6cb42fbaf2f17b9d16cb1380e14ae57a7f10d24be3f0c48658d597897a0f891aa71737e1c642236dfd46ec2022a90d8bfdd66743eabbf165e161d37c8a757d2106d769acb8cdd5c6c52318a0a7c16495b0157a68af6e9cf4754d1e1e6494655ec8d750e083ecfef8efea3d2054b97fee0efb22cbf02892006f61874eeaec365636c8b32be1fc332aa68555c0c6c88c694c76e338abbe470d50a7353039d61a86f1f6f6cbe88a2fde0ab2f9005f8c5c8f81bcc2b631a5c52260f721dc47a2b8f099d4d0fa01c48fd82023b52014ebd65b8e017fb78cfdd464f9b7cd1325fac9f3df0b89a55a7d0fda6d2951f00d0d40bb23ef450fcfdc3ca47786d75cdd64646c759855483c5e91f5aaa656e04803e9d29aea82dde9188e3e43a4dea402dbc88b29f32c9a94a1fa6b34dd4c70d8ec5abbe0258e10aee490b0a1b7f526ff19bdc00edcdbdbd76b5cfdf4757a409b942de5aa79823c23525dfb4088b12c68e53c783505e9550e04cc8e11ca5eb0ac2384b9682137935ac0fbfbdc2c1bd14906ed0504b124565d7633a887997740351af0cc455b77d054ed5c442e301861c5ea219ceb5e2bb75b9af00e2991b270b2affd2379d586dab2b56784b3c184a0c043c1d4a602c10e575213a41d1b32c05512e92d1710bb40de3f5d1efb27a2f035cdcddeff0e434c5f4c6dd2b4101f607d44ef6ad39e2227e9ade0cd8c7334a1f29b64cafbc1ad96f29f2661789a62a044b26b7b34b0738958e9cd87901b6bcb68e6206a76ef4a3b0600cfe183c24110c475987b5f5b627b13dad4a40f9652af006163987f42aeb6086695c226c479a0e5011541d8003922e0aa83ed70b4d598ebafbf821834306001ec4003dc706f403d245d035b0d0712e2dbc04604607c88d8f7c1475d962796f657c19d3fbe3eb0e2357c86bd679af08526c851de87e5edf447484324a157968195e181cd357c3030725d74e82eb8719edcd736dca1088e43fa935630dcef1e66670dc1b89ef910342b2dba413df990d9132f1756300002d85693a84f2335cba69a01136df462a0c962ec316b50ebf6b87589b4a9024f15c7a788acfd1d2b462454b42188f0f2303b1fff6cc4d2ad5f774670b22b86ec4a6b04152c7f821701400116c649632a90de1f00d1fdb082ce630d9b7b6b945a939d87f162b1a85ad1fedf735579c81d30a85636eed74a536654c7edaa7fd897af0fafdb8fe031dff7ae66568ba725de60c057d7cc12244574be06d959de19730c2a5ca82a8bad6779f7ab19abf8f975909 true -check_ring_signature d26f225fcb6ca8e13089a96e17d1844cc0264af9f7149048b96ad0de6f16567c e3eff7c3e814a43140a264bd49704171f93baa8806c561b7448a0441ad1a32cf 1 9e5ba7c35a5a809999bc39e543adf5a1bf007c7236498c3a772f4daeeeea30e0 cf80ccacc126c14015e06d5c631484d659f75bb87a596e9e4a988957533e5307f8cd8bae2044231358b1d495814fab2710fd47e7529625a1f0fa2f2093e8a40e false -check_ring_signature 60c40bbbc0c6f7c88760327824f34c37f93d86a1fdd470b57fe8331f96f493fb 4e1e0e2a8ad9cd1ba1cb8c1e57ffb7344217c1ff2adce7625d7b930feb5cbb54 2 d47adfdb4f7e29ea75f6862e76dba8f8ec3448d3d63d8af1486fd120851eac3b ca07d1f4df4af7b418b3a97ea87ce14a11f5f0817d7e5dd10a1917215a5aa365 f47f9a70e41946a3954de02a30a9bca17de337b4a61bbd09ad95bdca1d5b1c07656f2ba6eaf95e4c3bbbd5b3d84d45972e6ac4983aa683a2ddb3761530513b05da16d1dc034cf3efca7b2f965754651c203f4fc6fc84173c7164e0deb3a8df5083a9bd7bc6fb6f4f8ba99340a302e72da2c2ffc2eea0b2b14cd5024215049207 false -check_ring_signature be854a650c705b8fe2cd63a59e364290bdbec324107cf340391df133a0cab5be ae4aaff151154dd3c53002e56845294de6f3769affff0321d7c65b149d7333b3 38 82abc93fa65513dbf0256f7219d67a9491fb3ad7841f058be31e48208d828574 bf6a8f0a172527eb456793b08768a9556f94903abf1de4318ed03d382c83747d c050c91a9412fd25d7840c52f5a10159b46a48cbd0832451e383bc5436c1785c a557190e9a97e8a7be7cffdc62cb80f8853283827effff7c92ca80fd1d069a73 219a429b0ac09dcdf5ea535f43aca5595b15700f47c875252653516ae2a6be3c 7e0f898350af57d48693b3277b443ffe79f3360487615d2695f05224571e69f9 d09905c6c0bd99ff90e5fda34b2424642be8cc0c6f49f7eed93276c98f85b22e bd936cfbdb331b80396b983442ceb3be2053089da042f338a16ab46642dbedf4 e2a111fc7ab82f95ba4f9ccd50e340212cdd50d1edf6ecdafe1607ff013dffad 0ee8f00209841acd2ab707c013e6d3eb94fcec44c22e9120bba0c3ffe27f9d1a 7a49e272e511b5f2eaa8a91ca1ffd40e84f1ba53b61954ac633d9efd9f989905 2ac041dbda72af6d40699c9d9a4dd94f3287c1c45225db7f204e2c9e5fceb5de 4cc8b884b24f1ce2e888ab20f69e5a9ad14b2e9541763f0d3c28032ea94a8355 fbb48309868c87fcb44cd5671c0f727037968eeae2943245e066f92562d45531 8d02e9d00ed1ee613c6d7021c61a6e7ed4707c91071e6f3011d7938417ee6953 9da510d9caefa32180ca25867103237871ae556f30f2e7333c2027f998395a9e 7b90d5da78594ebc3aebcc8eb357b8cf2acde0a21e0ec7659b383ee892eef9ed 87975ee2bc29f134228662a7ce85fa260c26dd700ae4751ca60be60494de8d38 cc1a8803ed859ad92f08eb2ea6da51c6c076df872ca17f7a9d4744b8abe15288 29e403ba9af50705079cea5986097280775d19105b92682d5b691d7969a7fb42 dba1608834ead3f2b894f9d5329dd3040c12b65cc3f842fb01135fc3e4ab9d52 138720ed69961adbb151978559894f866ef245c07d01b7e6d647c7926069d7dc bb78bd42a34d27fef0c8efd8bd07ac5ab822eaa09c06db30d4f6db7748915d8f 1abadf62eaadee2f77d0f61242b1e8c09ddc1a92c09eb7310c56848743f405af cf336a8736fe1ffe69f021f69b9bc92fa64535a368efbd59af44cce8f7bee0ad bfa3b72a50b7e28f041e841a082db218ba8535e1b11629688bfa95e8344fae40 6d7b2b8b98559d7222ac29100eeeff95649361336ab1b4433455297335503f73 e0ba590a40e3bc0528ab02ab724879663b51d1c8199ce470359cdf39da34eccd 9a7855efcac305662b0b7b429d6cd32a4cb84dd969258b44830e18ad39277be2 2fb86502a0cd14cba12ad04f3031a4af4809ac349d5ee10f5dae3706ae91b084 67f6c7f8d7575589ebf4025ed2c25e70f296568207db0a2b03052f24fabc9129 4a9a1ae9ef5904f32551cd80a1e7a9be2e80ad90a06a74f6f227964ec5689b7a 07b82291d52b9b6133b9e97273910f16f7586c691e95722205e7c2b618ecf05d e6fc0c3c677ed340abf3c5d85ef84c78bc46a62de03cd9711f91af68c811f79a 0193edf9b1eab1ce89b2e1e2db011c105868c8ba10ae205b5adc127fe0be514b abf305f714d393fab65b6e2e8edc1a42f90999bf8c8cb410f4f3f7523fcadb41 5091bd284b5377e736c9fdf5b4b0c2527d780dc4650605088e686b8616baba3c 3ec4556e8276f665f4a11efd7cc4721e142eb2cd77fbf6c0fad37abdf8d3184d dbb18e513ee82478c93b6b6acef9960e4c96f84968a58d9cb92ebb70aff5680316262109ff55a07df6396a7e0913fb7c509c4ac79df1f03a3791eb6e762f85085365379727e5c1fd61aaddffbefdcdb14df1595db65f51fb6f80d22c3bf75401b9fa09a4929c975dbee81b409833f4d4757f7807a2bf11d85276944b9b9dda0b1c8d3bd98036d64ea1fa9c8583e40360d1ce10cba5fa15b7e0be982c02669000d31778f7e987c8b22b42943c72428dca4cc16c0a84bd8bed8df3a2d006d3a208e6b1c544012243e2f41d03fe4384bab91140ee25db8641b486eed69ec01e310eee234a4b7bafc824bf9afa49db8f128c116e1cbbf1a5fed8e02f3f0d4461a106518e3a816c4084554d75ff883ada01591358cf9709a83da08370d2b6122f1b0aa39de3a68554cca923126d8ebc705be15075997e2603dc7978e3c00266bd5b0992b97242af9843d2861d1a5cf44370fd3fac9b08ef63464158df65a233830803d4e3de28945e9c8056165a6c1600103e5f59bb5c9327c3d62db586854862f200209cd04561a664fbd58cabf868282664bd5b13aa45eaec8beb7981d0199a5c0d509456525e85693e12330dfc3376a8f641f1873b02f3f125ac1b2069e088470ea5afa87c1c1533176c5a83bf1fbba4a5584269f3af6bee999a5d23d7a8dcde0e720d2395529800018dcf7eee458a9a3b90f06c2b4ef2b9958a7c849271b30c08443043fee724764a429fa9140cf0a2dc4dd9705e4266d062891317e1b349c50634d46bb2c2cde848a04e6ca1387a22cabf7f19417e50a0f2610ec4398a56240e368bdade1ff0b4999a19e3a964ca65566c1986993262817d96f5f58785b3580fb25d79d593460b25b328d3dcaab214a283aad35f47e74f398b103745a53b6601418259f43f746272b128113981205155eeff72f4f14dd9f7138ef9f43ffba407d17358e495b626d18e6daa148ae2d3345647cbd2229af0769420df03ecceb60012533f4a2f987541568aed6a5e767bcd22d1ccfc67dc4af0b1f96ce0944cd00b209b397d6483a0badd46950dbc212f8a9d281d49426dbd9e2e548466b6369305e832e981ece830e931abbdeec8770cd975ddb0de5170990a86f110a08934b506b5b6e174c83c0c78c8eb9f42f5eca60fb7a5949d5b2c902143a45b936dde0c0a94bd6951dd38615a1b3b59cbe38df4c3da592b6b98e09cadbb0598f35204d10ba262d9478c022eaf877edd88a7303b7b1075920d2cdfdb6d592297648082870fe6c93c2fcbd06d28f1b5e18ac72bbb09f1e301ed8772f7fe9f8a238990beb803f94f24042e1e54051c3b7c103e4d1c32e2cec878d4f5efa857911d0b00e8800df1272791e4e24a477b599e0565f5d05a19fa06e42238cbc564b4749618f0be089e6e8b9d57560897fc8d64e3d831456ba8771a214e66842f703232a0354fd20c1168de8c601ec055e6e8cd78ba81734119cbef7f284eb630e483179eec46680f2bdff4082ee8b6ecddea930af1b6fdc89deb8952241585865394a81497f8640d6d2d3689bdfb393a3973bf2dac566d91a6e36809d9ba5297803b5d0b7f159b04b1e440543acf0a223a14128a88d626d394ab7d144dd1bfb086712493ba50a70b47c7d88823f9d99df878a1604e391b513a74889f4640f8d619f38f598250920798723a35a15fabed3e73a099315e4be399df5e0470f21ded2d789c8ae5f1b606a47f6745f0d57b895f715c3e1b3cf8524dea861d1150c1670ee3b27d84ff600ab1ec9a09c4d1e3279b4c5f55707d70a543477cba663cd46003cdf75860eef108c7fab3e918f14749ee20ee84b34d431807d7984dfb0e321b332560ab6fc5520a1eca7b712fd108b5fc56ce550ad66c458a69331635effa412c7da35180eada09aaef8901725396f94caf7f8ba1f3b3b23313b878ac7cbbe6c38cffd05537fb09e1f04d19505d1ffd5d891a732159fad81d52865af6e4a789f96389eada0229089e67e18f988d45d0e55e4637e688fd7b2e8c9532dfaea08959e006f83726a80b659a7a59413375c5cb231381928349d34caa3086b3d58c09efff98141809ec0ce7508211b833a71f641f9cc0b24a24c288033b8c46d86775b0d7837d623aae0e184df7ff45ab19c25aa76181cad4e239452ec68e807de7e3998782936e1a7b01a9657fa2d909953a01bfe34b1600bf6c45353d7857ffe22de293cd3043866a0fa1377a9974ca00e753587c7bf2eb7b844285d8717112ca7ca1e74476889fb1056951e13c904702b8e64be4f048ee890b88e8b7da85bff5b3ae1c5e414ee5b20ceb8e9faca5963bd49daff903b19312bb32c2559d82cee4b8775a5f8f3947410da7b0fff33e6b06015b184875683ae96896f97892c9e5b1bd5cec4650547de401f9fd5a8b6aaac73cdef3067f7fe1bc201967dbdf63d026c1631342ac9506e9021e69311608730790bed102eae11545893961426e753aef07c377a3ada605d8010fd3314089ac9283bbcad46a75d477515a5e713148f7c32e863e327f770a3e0799a7b79ce415bf8f5448855c73caff10e0b61c692dca454899b0e0e6307f6902c5fbba80c08c75bfcce608bd0095f2645af996a84436169b91dd2b02e83b1402acb0a9ebc89556ad20ddc71f5013b89ac6d2d163cad2a24f33023903e0019c0c697832f831782d4a52cb451c8356da93ef103d16443a7a37ed7d9f4d9dcbf70db93890037f56589f8f025943e4d8cc6a2e6fd408d833f568d735a63f0398a40ab60fd8b4ea5d95876fdc26ddaa74f4c83e2e891b9ed07363e33ba512fe239e0b56ed26c8140fcfc0b9ffcfee12da12cd3eaa435cc0de7d39db645f54bad0bb0f4cba7146a52728c6fc3d617167793808c3c4e84c09468865e7963c1150ea410e94d9c082eddb6094497a3c32a7886b81487b6c3c22e8217442756a5be477220ad5ddf8303cc5ebe4facae2c4e33f9ab8bc623f97df4acefb34e120e642677d0d4c4cc6284157a2f28030f4140f13b602ab8be6e62c921ae6446d85926c5dc606f0c0986bdc0a136b58155df95527a350e5f53641dd67f949c59f9b3f712a60044adc3991af54b835e78959fba686965811adb41cfee18fc68130c833cb13120dd2f7144c923d88a1d4ac7bad1fa268f2a1737a3ff6b689d4cb42aa463d51380e2b7ca5fd0db0f98a087c9547f0115d70af4b7d55796d37e18363c69e1ca7c1017f9b82703b5cdbe8b84f274c05250c656b42ca97cdd159a4d647d5d4418c3f087f7e0228828741679180a78781eaf9f0b5e0930bb7015aecaf76d1bc4916810a4fb5a4d255fb53a21487d866e785dcdd059b9863b4ea74152de9ebca5ccf7a0f4387d600ae54767ee2a0e139e8242c9b7cb8eeff1164b7c6d7858c6fd4bcb4047c5c8b22eaf8b01768812ee6e3468dd02c7ad8284070b3b4501075a2dcaac90d false -check_ring_signature 7910654af7ede95bfc9cd5141ae560dc92e65a931fcaf4439884a0784620135a 61121e5e2fe3ff5ba5208a3f1f2bf187c7fb90026f5f6b705815a03a3928b0f2 126 9a544bb621392d7b69055aed9036feb160c4ef704edafd83bc63d45185d4ce7e 49cc88487f385d1e20b36393df0805681604ea5f54a773f0cb474d8840d12446 dcbedb1dc43cbe64f08ce6951bc7d010b3ce32bcc7a4cf8ce1e03a12b9693f8c 1efae200170d2c9ec89b0309bffa36787ca340963c41986296c7f91213117874 ac602fea0e677c2f9418695b29b1e6a45012b6227c3baf1b4025152cd74c9248 2e74bced178dc71904613b0d13c7a9e58a8b757f947af6fb0e765ef171bb6648 df17581758c97ea5d166e18340030e09c986af57d1aeaa08bddea8e12f282c54 d09e05ca3b2043a887150beded3c7eaf6ec8f7b58c1423c3c36058be092f763e 74d4bc0b806c9a39c158af01099ae29d78e97a47e4f08a2832c5018dee6e6a8e 5a59774ae94ed10df92c7884663b3138b3078cada62fdc165a57d4b9feec8ac9 336a7f5dbbe8d26cc20bab073f9eb07c0241ef2f3b46c10be44758964d013107 7db3b997d7623abeba4ea0cb28fc281401be469c54693d28038b54aa9f532136 dab4ceef490281cdd20605afada06dee473671a30628810f8af9af8ab6cc6f16 f7a56f34ade6fa2b7bfd964cb475cfedf94da86cba5c2bdae3ecc1e04b332852 536c90942b71a06264043bb59b1f035020298d3cba86d586b945f78ecab90c3e 5c532dab5114bc73cf006f7aac2844d8e116490e21032729e1f49c9b74222ac4 8f1ec3a92532eb59dcfc6aa30005482915ba1bba42800ba74ef73e1e8f49b31a 94c929f4d1ac625134ee206ea2b9ccb51b6805b6800f71571a9d41419e69bc66 1c31558b0791ef3b34993b666c5537f118f28c8d5f33f4239e4ad849b64ec4a8 0d92fe2e99309c3b31201bc7adcfbd8926512ecb7d7d5592007c5c17abf0868d 1c94a93100ec03dd2eed3c161f9370c1d4b1cef1a9ab384efc6c87a72c1681ff e68f903d63f712b3537c2a75dd6752f9bed725e9ebb6137bc08463f5cce05f1d d53f41a9cf754a041decba1a459810d8f5a1811f37c3c795b1480b6afa748b2f 984a3fa3b92d387ddf013f285bb6ef75d7762d387ca320213eb8ac3c53d39859 6f44c5a3c8f2022f1701d7fca3e44bc096ffd1ff6847d6208595685bf629d200 f0ce952fe40e25d63bb8b4ba838869d39f0488353b17b3b3d4e119384263e8cb b7dfbdcfe47eec27c7ad51791bf410d5712d18d4661acd1dc966ed830ff71244 5c3af978ba28e055690fb897f86d8ceac8ee70ec4cd986daf20f0e19989f9029 898daab6b67954317895003328abaa1202916a7403b6bec037d0716a606e64f7 dd049fda3ba0d4f65156dee6f4001bffe387ce2b37daa4734204a7bf821726ec ad63c4eae01740b6a24161c162788a3fc94a4699d9ef13882d66a3f88e411d0c 8cec5fd1ec1a75c829f8c5ec29c13a19be60d82c873f07cd3dcb372f39e6005a d20d6a6c01414d51470aae7b30cae1f330a853103f3ee12b51767cc6598668a7 8301ab8e745a8ef1dcf85f7c108f063b5cb3c4b2ed944e3eace1e04a47d293b4 e0938c217a22fb6133f2d3e383989d8ce8e35e64c046a5ddb13d1688071c33e4 9af12c4f327a81d6aa2d5889212196adb4df3e9b50cdda4c96b6adc634feec9f 86a7a023ff33e3c6ffe8247b3872736bccf0cafb7cca44795bb8dbdb4e61c9a5 4fdd024184ad5c2bb665a9443861dbd9dca7c5140bb0397775cb49d5177c34b8 bd7a187c49ce4e74b6d98a55378e1841c2310c1d81c3124b2e2f1a730b56a516 d9ebcb2daa010a2d6deeb3b91a12f3ece1481d5407dd0bfb13d7e0897e8c0b08 cad14427ad6ff829e37ccfa6a6231267f44593418e6fcb4174c03c3d7d100331 8443ef5b52996a0d614bc3d130024f4e04b5791fb7fd15afb3177a2698d93737 c66f291f9e5eac69798173c950ebd512e63324e7f9b8ddf1c5f46ede3ca36dfc 381939bf0dded42a36a5c63c99209b0f2b738704abca1df66f435e18af1c8165 131b67a834ed1feb2171ae222fce82cc6752fd50a73915704f4109c3569ab4b8 cc7bfc7cb65d10488933fd2f095ad2597f2367851673bbb9187c0733eb2a61da 63e7159154b29d95e956e150133f120fcbc3e48edf607ac6cd1e68eb4d7e25bc a81bcca2c340f14751badd5f64b1ad2ac0b9f45c2a146ddff7f5611ae61e22aa c38422b90a95117c112c43017b6f5942880d26bf46895608e2a7d34d3d207b27 87de3a23b36fbba4dfaf00363ceeba9c4f9a22ec33d6fbb58aa5f4c95d235de7 d19f2c67df9ee61b85aa7085500310deef0d364e334688dc6359d67ce347492d 64cacf1d7824788e27194e776a5604352ccf6b8b3dc1481d14eea3d36f6360e0 81674b59f529db915c5740437da3820bfcf9a99ff41f7f90f5fcfb5551fbfe68 fe9006e3e1fde2ff1abd6976fbc541bf408801742dc3f4b673e0a4dea9ebcc98 1510263a65b22d2cc9eac5a794152658bd99de6f36162403adc7ab7855f14bf1 0a434ce0b95ba212e6ce96371dc29dad62fd81d150d74b87500c0f7e2c19a014 2498caa5b9d238c0fade1df7dac0259690455c22432cbc88ae74622b6c9db4e3 bcc2d6f11b2fde0103eb0c8f209a43010cdc0e33604e8131c7258e936ea89ea6 45e0657c1fa5c94e750bb27d657a97b2610fc7995eb79a5cd089f8c949b5ce2d f30bf7177230ade8b6309fc6c84270d11cc92eb67740174e56166439c34e5f31 a6565a77f5ef0aac87df82ce6e891c6cf7d953bb5ddbfe31edf45b5a1691b809 381d66c6679bcaab8e5094e754d2aecb97012d839bff19a589257c90cb4741e0 03569079baab5afcf7c4a37cf5ae217ff3bff7071cdfbaf95f3fc880e399e690 72c977844674de1b64f974765c2fdccc29cfd1c156eec6624ed798a2d314ebf0 9a9532b183dc4f891a84bb5e56034afebb894bbf072c9b849f92dcbb0bba3a82 03c23571b1c1763e6dbb91cd440d88fb69e8b9311e5817a62ce0f1404dc767af 427ab3432f1c168c018e3bdc5dad52afb4e734e30e57683c700d095834547212 b7badb9a822cf4bb0b1a67169956f4cd09c5465ae69af88a3d349eb73d98a74a 6053f833915c62a4370dfdb71f7c0421ca46a60b2838f6945d5005af7b9d4ac4 c113364c96187f3b11bef9c6fa0a0cf353f78b701adcc79ed56cfa0a408be514 deee3e7cbd4810b3362f10102b275bc56092424bfb5d150e61536f62426ca083 72b38101b8a1e5a8163e2acdc08521006b8aca702aad6cd06e4b9dc6b8d58a0d ba505f4925d4902ceba21351361a1e3d81277e6b3681511609f49f98ef49d831 0cc0e11fdac66f6592b1a46a2f9390dc3f56eeec9dcc47b44ae19a0655704db9 6f33894e9496a23cb577391f4be82977aa445050f0b2240aaa6a13d0e817a039 7f809490893668af9a52d1be6fe9075111e23e5264c89c9e5634b9d24c33fd62 06af02e971e52215532724e0746e2d90f3ec8b28bef9e51fa1233af957bd89ff 5d8c9e316f02a1b3a0e124b4ace1f0dc28eb8b457dcbac2049db979e5d5a498b 435256fc28ce12b6e97973c4e3eb8dd6265b7a40345b0a1bb867320c70527363 01e4d35f2482aacf54449d8356d7bc868fc47437c617254bb38a980d941906a9 6c97b2e111867de3c41f783c3d50f4168f8341a5b688e13bec828be7e57ca098 702e0ea1dc04fcde15c17263ef0404d37855232413c57c58d9160e408bc95192 9ef34d139fcfae9835c62a1bced2fee52821bc9bd7a6248915968973e5022d6d c4766ceb35e3d70663c5b582f616446e39e4968dc8efa579c33e558be60e3cc8 d6b620be9bb1009701009a74c652be58b28fa6c052b0b8f3b8a68f613a01b9af 09747454f63f968423153be318ca453bf7b7bdd954f2bbf43a40f2190a3c8d2e c017b71d535d9640de4abd3750f8a3c19342e15a9636d7b6cca1575176af30c0 b1cc18b8b7b8202abfdc54b87f4668f573b8600a7093a1f1d03ac4af48437c58 90c7cd1e222a8f1349ea819b56141908f716459bd4646c4abb3aff2f9fc9136d 61b2cf0d73d9fe74f19477e268022b1eebf829ad39ad61c9e4fc171ed5a3d362 2d9886e62d8e64811ae52bcc81dbbc9b17ab97c137c04708eff437b3decac131 a4e3dda637c3652af5ca68d81d4b98eb53ce242aac427ab11ed4d0c86900e5dd 178fb949cb6e0ff46750b9c589e4f5267fca0e40b23821fb111017b6eaa61f3e 9443baf841e1912af153e4e8afe414dbd7cec401f71136c33721658f2171e1b7 8caf966edbdb53078908f4b82840d994cedbafbcb2a54cb49516b7766a1d13ad 7982f4a59a130440995758147f49b5509cb35319d6fec7b06b2601a6439c812d ef24f11a7e4bb6314c18a69cc987cd308e4bd084a01784e9cdaae5524aba5e8c 52c72d407a16172138d90bab5c2cf9dd009dc60b8d17ad18758e4547b5e02193 389b818187d645b2cbff1860456290476b84cd0ba7f969795e76d6c285a59f0e b57e3d77d85acd8e7564d26c02d409b1b2c9019fc8b9e4f3cfd4c4df6f27ad7a 237825a48ee5dd94916c57efb732a964fccb46a3bda93c8303588b04a3d12d79 7598a392b1218d499791df30c862af0b963b6a77c30e6b20cbf136efc92bfa33 f98a5cb4269ba7b81748f282f46d6a4e0aa86e3aab701b36ee9a5204de3368db ddcbc21640959f2091890813bd3058056e3df75034ec51a8948c4f6c9e79157f 2391864df935bc37ed90548dbd555bf57c9a9d47d3114a7dba08f6c9f83c388d 08a5a9c2e81984bdf0abafbdae8f685d6be3aebb817294c4a4cbd92c2a36055f 0dbbff1c9e927fa50a16605e14f67c4caf9a24880efd1eff338dc7773f6dff10 a4e560e69c8353cd00afef3c7fc1be98246572634437bebf6c35ea353829f067 dc1642f3cbf23a14b87b08fb9bbbe0d3e5a689bdab01e025ba3b41f3d19a1c44 09664d84a098ff7c5724070840c43089cb8d398a79d87c614cc3d3bd81c35b49 7ca564bf425d69ca7d13f417c76bc29c7a091141a3675ce874de588f77154b35 4b063559c2902d59da67b0525832af822e1a1887e3bcfc9177e310d79121e062 185a2cd1c9197f7557fa0f02c97b5b5440ad8067906becf86b8e5c19256d793b e8d844f4f3626985cb0467ba3585d4875d9f95051018aed5ea2caff7b9531aae 9a31abd129a19ec6560f5e2fb5b11500f1e6f672951a2f3f892863443715cb4d d0286d57fb16e9c84e0ebe9f97841f84c62f9fa866b22b90f564ac442fb15678 b6d0489c21a2d4578fbe78fbfd7790f5e53c194af0d15d181a5506f417329e4f 930b5236b91ffea56b5023b8051a3b802425c7a118b4c4e14cb0856f4f050e6d 77e3bc999eb13b320628e4e66116d14bfd03eda1f4069b08f59baf4a66ffb96e ecdf0bb46d787bd3bbb74a34a212a7bef948897be52c22abe298165cc9347559 77fb4a685a357b2aaff6f2d09b4dbafece8eaf0a90182d4ed0b8fb4562c5b6ea e0482ee2e1ebd89628809cd96c3a6d369bab86731a11fcea7ae00c0ad31f80c4 19dec8e94e8dae8526fc353e8231804d5e5dd385f238451c81b61e99eca68a6c d3bd6bfcf4e6ea7c04251ff3efcd08bfb23bc663eb25affe741b4a6e24974736 339392a4d2ec13252afc021b06b6a4713bda955a606d4630a1675b05d3a59e50 99a07dcdd4a7fb1c79320d0061f2d6d4ad84daee2f32fd0b7a4393ab219d487c 9dc46bd742940d99d66050fbb53b43d934f1dc334ca5c4d91802f1720aa0a2077aa775368752dfbcc9608c0b017067944867d41d2e19e068e292e1b070f3740840f54a92e6b952ac4451ec1bcb3511cd29b68146bd2064fa6fab34f562383e0fb908ce4e7172664bd86862bd605d7a9fda5b995c92737f77ef49b473d1156f0af3defaebce957812a11f2dfbd9e73061bc6ec4ab771c3ce9cd8873e0ab97b704023bf529a68ef375aaf5440bfae05e171b236ea0deef4d5fe5ce81dea40c1105f0adc84f8e662645a046d68f6bccb57996406913cda89aae69ff244785f2090df618eb1460bbae0b0635c0df0a925bb984bc97e5b55910df1e1758f4c9d9a10391619b85df5ca11f1b8f8a5a237aeab7d955ade8e1a3ccd2a324321ed35749099ad97238f61c43b470ef5c8ee63dc992bddda2b813dd9f820e15ff09a1fe4608702d0934090a4b00ef9f96245e9f492a3fbc50ac2136df6db46c2f57eb7e59017fabadc44f83ab4cf17b6b4047e0611999344b4fdbe11bab237fc165eb4ebf061124ac8b4a4143d5f6f9d00b58bc5aacf4bad07b6de7cd16026f7f30b5e0010b8b399f4d2d23017e96922bedc6fd8632c728d4047c3938a96b60acbe7b04c403ea78636853db5728d548b67172d2bbf9d4cd802ee17aa645d4571412aa62d004358938583a8bc8b16d4c3bbd54b078d178811d3e2a7d03211d8ca7c48be99c002709bdbdbc300ba4602b03cc09489b1e156842df292cae6269faf636e317bf0cb6924e2ffae43bcf88a4adebc8a706992c5a2f310eddd92a3bc1415ee086330178fd72a1ca1511ee615a33e9bad0dfac091aba542a65165798a13c5cc262d00ba27869932ad627d74abbd8bbb64e25f575e223da0eb770f17bd71097ebf72c0c323b71ac1b2a21f7b1ec9c3f7921021d53202219d3e3b08de4c97134f8fa8b013bac8872f6626c75baa5d502be6a38a973b49d9e4892e23256e1c0b8ce425b0b16d9f5c0063ffd957f193ae2bf8397b2b77c89b372ce5c8d64b6259adb354302b3cc21a8b07520e745ae56514ae90cfd0766b632c64be63ba9ac0aef46174f030674e07e7999b810d71b792824c5560c226c6de6ae20a130c8477126e9f68a09cf8a7f87bee4fddf215b77395571e4123ea6fbdec20d93eacc86da7e419140063154f909e0fb471dcc529a57ae3ce498018d8d6adf124178bacfc38b5de48d0f8991e7365dd5107d151f83e5137907a15f6c67ee4187e8d9fb3fb84e9e3ad10c2c5efbe2ea6f002008e121c652e16bbae2d9f4333db64d36b3f7f4770d1a2609798291d2c27f807028885f07ffc83fc9c006d597641036909c351b96fa28f40fb0e15d5fa22210fcfb35e06635ba98e807e87faecac5f880687cba13a6d1dc0042d80d7b1757a3a84a0e3802c79dcacd1fda34729b6b9103bb0e682c60beed0c7bfb232fabff32409a33bdaab591c78578ce6e937d0f6d670d0fc6e86ae1ff06ba03502e71c084bb1858637a0c180a3741a0bf89181fa489b429f4547c17f800f5dfd06308f6b4298837d57df329d84e57416e6e25f7595d693f24944fb56d000d2094ace58213547281f54fb5ea6babf0837ee52889e2e2bfa3bc14f2272a078ea65039310dc83c8b74342b733d1290c3acc27e453d936503b0447582c93e09ac8950522bfcb92f959cac090daea5773df82eebdefc2645c91ed43b27cc3907479deb65482bd7ea039ff7f10e21f937446023f2441476e100de49694224e6013a7d7253ae33c41bcb3e2adc8c977a26980cd65196c64ace1b8a39a57472a90476c4d85221f948bad6ffe9a70bf4df8be2bcc261c3900cf6f6543f6c92d72a02d754e48c9e5ead8157382ddfa4b03d4c38a7eebbb144f8c0b52e1758f0a4d401c82df4b9bf0470cf32bb957b3123b2e70fbc2f5e50214e86b63fcb9f276d6d00c46b4ecc2aa5ae0a0cf8cb105013092cc271da54b024954fa9116a2890a35008c437b0108264a51fb81a278c89b25aee438b66b2384601cb6f711e074591c30543c515026fb9230d155fa71eb1cbf014d9f10027416a601a09d2913f1dd7d20a715441b42ace27826984d18232b6f0bc1e2aff3c4b1399d5fecdc1bc0171e0036b7e5a0a6d95f44e3c84e13a83d7f0a25c50f3336213df8460c1fc5192a70d0fa27fedcafb286e3f474f35b5b4b1665a0cb7a04051d99cfb878f76113ad095076d6241187db3d05d103b1582f650699f1face68e076b7f398fd49e12513e1309134eb2e534ad0b5f39d3d2517b0eea332eb777c55898c0172045c2fcc1ecd5032ae975f6ff5d6fb51508354791308c09c948193cf44f639ad2c51a7d71649d005cb6359b04abc798631b848043fa80810f7b7c7b1289c87505a639d5fbdc24057faa021f83ce8de499d2d5463c9860abc3d600824b372ec3b653f96ddd510009ddf1d8ee43fcc1c633cc05cc4e167da038dfc53c89efbdc297adf683f170a304d798c9838e04c4d7403556c7be8e3a7ef12227047fd707dd822466431fae830bf01bcc03d959f9c66425f0165bbbaad8ec00cbf1ddb855281af53d346b94eb0e1f443db3e2ff96ea184df6185d4d2f93ff60e16200beafbefda7721ddd903d022c8a23431bd784b6e358667ad8cc77fa0e17f853417d34b73582010b62a99d0d6e0684d56af31351b740b6a33199a1c499c01f455df378cedaf9cbb8338e8205d5a321898a60e7f07028e92d7745a0dd04e4c46e331218c97cfab73415662e00f0d517b561a2a82b5fe08910aed0a9199b5be0ba7105c591a381a7df118dc80dde091485b7d7afea79a1ddcd7b2337b5a1a9f7e45efdeb23f7be74f2b1e5ef0f95ff1be10b85b758910c1af9d3830bd5b9a054eade3dca35771e1148b7270705b4f4bcaf5582ae098ed074dc322fc0722267a722d4069b2f1cc3aad4b2755c067cd806c83d480b2b05b486d68a3d4c4f4c76ef00759181e5604947cd00c46e003c181157add7b18d25631f6c9193bec3ae618ef0d6b379926590322103784c0eb19c2c1dbc7d8417c7cce65d80aaa692d4d93b6fe5063dad52e0dbd22abbf404b6aed6a6235b21a739a365c20ea18f54602c7a8db09bbe4d2f5e569af931ce0c28964c26979bea83adc8e97d902ee382752e58a10a4ea8c337abfec082a25a0bd5fee6b516fe4bf8c5bb9f685d959d8d40e30306918b2848f2172b061df89f0fe54c229fb2bb7f6a481efe1c96b35512e4bb4d581e047df32178cbafc10790029e622accc816fa67dd7a45a37374b1659ea5603256e19831855d5ae0e46f4e011937d2548418470c6e83a44d4443e356927d543e66b3139d67d54bcc0e603a0682e03a4f9f35eba232e25aab8a09ce7042d236924194a9d8c436b4b8a114ba07affa547acaa7364937e05c9d4ebafc7b45da25d8e5648f062e92041fb926740980148f6e1879530a649a9dd08918876c1e5d6024868f752de5aa60a52072db0c72eb4ea75785517bfbed766d2551300f08789013ddb4ab3c81110d633d807001a0d9633d501e89821b7e9e8010420d806ff5a17c59a12190702a3b3c1c0a4202dca51981fdccaad1bcbe1b4a375ea9503db2e6528bcce4e777967981dd345503b8041d2eaaa080c3703be9d4140fe261ba8966bf6eb6c991fcff23c38bff8d0bac1b23aa87dd667f8fa4fa61b6520c55c46a758952061e2ef915202ecf97870cddcf67bbf23e0f8afd16c117ab080717a6df5e5f45d1db55676b312add987104a5cd7279f445b22569012f74dba35ab71da11401daa6b420f796b136bb005d019e1925f7bca0819c2f40ad7139081ce91536f006a6fa10abbdc61583243c780d2f206cbbd0107d0440eedb02e3ed0ba9ce6c668c5d7fa4b6592e9af01f791405af123bc15be3890089d011e353470566386c20d92ea1e41ba9a8489dc0e1f90124f1895d7e91dece736bfb263d5467821299b74f18fa32956409fa51ebc4d9089fb2928d58e6606b73bb70dc04ac36befc8be73acb4e17ae79924725cf14f303326890d688dfb1ec908c468b74b863db6b966304ad4570ee3766909b6ab37c05de58879d7d19b59e4b8e9266fae447e19e536fb1e5f3ad86b6f2dfe5ab2ae10c37014ebd9743a16dcaa5ec5d1a2f07f801b44e6bd242ee3914fc46b5c1972a06277a06303229c8e96040f127d5ebc8aff5ac805856814d00e3e789ccf90a7e08a23fadc69efe170eb91134ef71b8d20fb38dc69b6895b0b109bc5691fc843e0658fd2eae726192b92639ef85b767bb12fb9a091d3844d182313490b39907d208b7aebaa010bde5fc0ef3544207b3c335ff09762ae3f8476b0f07d06838d809091412d2acc2279962aecd9682c797b1820aeef4eaee0a717c8e1892e302adf5047609341c4a866e87b82f0296b5c782a2f0a7c8b695615b634ece8f095b592706d1815f65db61bd36547df99fd7a095a058a3ab520e4c401528a6b240b0e3bf00310e9dda895dc6fa91d4acd51fa88a425ff3132abfedaf738e7abd26c50e99055bdab1831f36cf2cc31aec279893c70640b73a58b83249b2582ff8e2c199330e720e788d308802ae3cca269b0550983ccddb67f49fb8ea7f87bce3d96b6c2d0c3edffa9e875061fe4a6e782162dbb9ebf8412604a064897f851d0957b636af0afc0836b8668c750d027beef065221c09c79fd1410cfef23c12327e5f148a600e4e14493bbeacaef9e9485f9925e062fa71d61fa2cfc5a953454f6275b8356506dac1fe6293d24912f4b6262c8767a44e3988cd115913d606d0d38c5d889f67064924de0d33026d935232df0bc5e83813b0c101e4cb4b396db6af3fc67eea2509c9c378bfb6a31c2ae57b31881450dec1f086c124a5ab2f0c2928b70922dfd6045e2181629c86038ccb49cf895e3e16f29cff0cd727a0be84c2b200c3ee535d00938161ff663250758f18fcda32da1c32e23bb420fe73c8be2faeb60ecc9d760df5ef1d789d13aa2ec198a8ad6a1c37375a97ea989d6ad01257511cd9c16959097aad41fd85d04ba5da833d02dad6e62f32b32f901762743f4c9d0576d40df80ba4f4e488006711c3b1f5ac642ddb6aed62f5fe2338046520ffcb77943c8767081b0da1fe682d65f2524306ea2f869f51f6dd5697ac47e53f7abaa17cff52c305d1a5ee7620565baa665719bdc16358d1ac8821362a48058bd2aaecd283343f0a359bcf1bb71c211c2be13aad90b317367339ecb6ea9f1c73eb483b0810f2880947db218db63c7e333e88413e074919609b420d4a71548bdb81aab6ee5433d609c5e653f6b436c70649b3600fe29611b847edcf0002b1580682d50f1a277d9605b16486fb297b74005221a06ac6cba249a3164301cd585474b6bfdee6aab0bf0af45ea043066ad3d992870f09e125319b54604f5121a1a351926d14d2f892ea085d1b34ec3e0968445dbc0f491a4cffc4811bb78d3de4f58286347f601afc2707195356059263e58c4d198fc59a57d1aaac12c69284730154de15c750bb69a80a43f3bc4418518efc4c8bf0988aa06a7bcb9d34d654ba192d30464c189fa344065251b27497409ff0d629d4cbca17a67f7e9623550fad14dd38fd38a9ee9abc09d4a51136430ba00dff04c3995b299bfe4b9b089449eb4caca2d38cc2541ac6044929334951d1fbee0b1fb4e7b6d3b98cffad083e13a6cf69dbbac74da509890c7d2a86e32966e43248f3a654bd5ce118af58b43756d8636a9f5d61c4e0ebee0ab1d2ee748fcbf93e5278c34134a6251b0a39ffbe17f11f00b7a53ed8208a5f0219e86b9a484da638e8f2a794d5cb7cedbdef14fde3185fcf73b8dd6654d4020af47da0705fa3ed738e24be06764d44d7eb40b92147cac1b029e2e31ff861ef0c93fe19eec80c01b3a3b58866a6ce2e8251a3d90744d755e3c1dc0d65542bc90f7175de86812d180922c4db868d1cc927e51a4aa5bdc08db5ea33b8acc868c80ac7e092028ca576f1292307cb676c39fd8ccc631b87805efbd04fa842e2ede10e627811d6a6a6995568788df7ad6c3ee63b210a565fc81855cb897f57bcb85d0d9d84bd870179d194d5a71e22fe9c7c8cd85ec69cdd7710ff6f53793b684e2d0718e464a3a6be5fe4c59c260249a029ac3000fdd93a3f37175a826f03ceddff0bd9fa3367fbd8de231fdaf7f928fe2152426aff6ddd17cc7b555885cbf58381047ff172a44fd24ad025f4896d8cf6762d1b5050288bda57f601c2c0b1602fee00b26cb70a47dce862af9be7220b79c72ac725838d2366069b63b3c74b01d2330d1e167bea89a184e3cbb4452da61ec43184783a8d048e4e364cd7904393a9630f45bb0da17a8d2460eb69bd20d70792b5b06282d69090d02ff15adaf017c5830ffdbcc48a40aad68e9f1b954cf9dbda61a5368bace3ea802628ae772f444dc60e433af3dad79c36e30fd3dff2984be36a2364edef6824a54b2c3b132434f91f06e7ca30e37d77c6e1a5071ea448d703977675b03dca163b6cf6ec4308db543d0002316d5fbf026de8ed7d696e6c4246a05ad0ce475cff84622bd4876495568d0a8a82698a45e3d4ae9e3d9f70b33fe19638a102a7800fcacee9af3363df9b6202c4a98a5d7e6c71e42e1553491f1ad927bc0456b81dfa2a231d99a3d5f7068e0028efb71a30a20ef5cf28a02c21ce30302f18758bd03610bc255cd787fb4ba409cacda5f1de61c5b1445b7ed239870afc6f4152f51eb22a17f0e77cd8d743c20386ad344601b1e65bc4cb672ab4062cd26eb3c5370280db9a004431e85d3d9c0d5173a30f48ce3b0394ac9b0ed2a98787465855c1096620c7c1168b6a420e3d01796a38ded63e30a1c6daa53efab11fd3962d21ffc8d478a4d222d3a71baa810aeb24d72c7e8fb6ea15e52caf33eea9b60fe9ab34fa1edf293d3741d6fa8f040913e7391bef086eb81b3bd53ffb2e3cbdaa2f1137325c14b1fcd87a3f50b8160c163ebe3652d1694384fb29e21760df7100d99fc69850d32e2520130749cf250739566b5e583f637e81be6bbf9a772be8c6c67bcfba3fdd44b8485158430001061b5963dad3f79fed426050e0f4e06538be6ec52fe134ce5e3388e9e874ff8c01c778a7db6f33790376394bc487e015354c00d09b2f6b352fc09869ba0389b3034a551b30ea5368ebbe7e81afae8e1fb3152c7a0a79c021ce36c4faa177742a0b1c8351dc8e332fd8bc0b8dec25e24754ed8c21a7cf433a5dde94305201462600240120de10f584309d87cd78d30ab899ab4f5c14d12cb89775df98760d6195082e3bd382f96a228095de2e0a1196d12eec2f8e87efb305109393a7efd590c60b14fe805d6b57c56abe90d14f2175f6ed9952949c1aaa4d2514ed9c8d61fdc2091fdb1fa553ac9299c926b957157218b8f81793c252f328c0c7b4f998d86cb00080ad429ae8e707c7ea2fba91f3654fc58130a5a69d306fc03dde2dfd9d9b95035d60b1d187109e7aa84314b1f91e009d34ade0cb766516252ac79cdbaa6dec0b2c6ba87fb5aeb3929bec46295af951592a0971a564fa8e1372af1d8a13d77608a7b41f42cae8d5868de4e93a8b371877b2654ea276e78f8f51d6c938310235003ae77ec37425c0c693034254aca7edbdb47537e88f864b98f123c861af745f02c7ffb2d7561d9a8c9ff2d302e5019bf5bc895d47bac65f88f0b798fc522f6c0232a0dae606cc08730d75759a90ed460d1ed25d13ff2f2c6e30809da3f2ec9d0a3b4c8fb492cc264e005390a16fdd86d988eae360f32bbd70ffe58dbbe989e208411bd067c936d4c07155787b9724d51d67063e37b9e8fc126089ac0c4b1e310b2d6586147a5368bce2a9eb91f740c52db1a1907ffd64d88337be665e8587920a277986ade0627ae5001ddf479dbb76ab41e67fc2b8713a366dd0e7fc3e804602af07f4507423354ea3bf63e5b78d7063fd75d5c2492d3dadb12a154e37b60908269f6a67f1594de4f59f03062685ae38e49d63c0f6b6ee4e71d3cafd5bc7160fad1c6e5856674757039728c5e095a7a81afae86bccfc1c132d12cfa5dca2680b559a2a529fdf1df0f1448766eeacf8b8fbbe96218956f5f6871bc8e2a9a4bf0bdf150d5511e15b50b06910af3bfe44c8c344eb37b0c5f30b5de86caa9b60af0e142da14ac427b552ca61ff38ccb231c39fd8ec8e900979d8ad908ad8ce6a430f85768abcfefdcbbfdff6caa21abc5ce53a0b1a974b6fd61dbbf5bdddcb595d0106ef032e9acb4c86d4d5a47251b34a63e174681beac58f4f42030e378c4bb804610179fc405d2deff78dce2ae64b47ebd8cabb52d557d2429254533679176506b5a2319228451d6cc644aac496e33226ef8a119bb0e2592490a645602f5573039f133d763fe966906f06bccfc4e1cc569538514dd2ef053bf5200696fa7fa90242590b01e345bfe2cceddfc475d568872d486b3f851da99ab432b74ecec86c0a4d359a9f4954c72d59426d575194c7e3924ac4cdecaaa86390fcfc543b5a8207dcfe48255b2ed05f05501464c3982ad3aa3da84b90650d26d0cb6823643f3a070969e7d4124a44d4962713607bb84f0b52a429c92c5749a4fb4a498fdacff30a6c15b9757a52f6c45f36032385e94b445a6cf17b0107b56d5fbbde55cca9920f78eb0a97d55dbd1d1328837c66ce7d97b48874ff5659a3e9fe7269e9d69fe00c58ab2d7af761dbef5071463babe132415b8f973f5ae84628352d0f7cdf6a6503307fa9c0067498ee04d7d7aeba1abbe71108f3f15a3243158ff28de2289aca093d7d729beae45b7078206f03adbd8a30cfacc4698ae043ac9a2e5c028f6ac90b5f98e167afb8b7692c9d0370787ba69d080c35ed5a47d740b5a284d0cd5dfd01330756ac55a93ccf5775dbffcb032aae5a3112c37f1bab2ef4a000c5b07fcc01d8a4204adf331119749431db809f692378feb8804fd9bb33aca402bb6d06bf020d407ea8639bb621107cacb9b6c70d016d6adc2fa210b9bf0c29f8230170be05787c6c868de9a97a1701e72325d1126ec2e4f3f3a299b2a9dc0030eb2da22903a97f0d17dd58ae7a87ee91fc7bd041dd8672f8c62b55364c46ff09b08f8e1c0a4ce3ff8b2bc2076aedefa02d16af78db92367a9103c6816ac95a87613358b900e8c88662417082858fd16c9868b319b55c0c4dccaa9d765d25eee055d7582e00ef2b419cabfec198ed59b441ed883b3793ac9c3cba085e0b371b03317001910306786bf4102e412a72352559853b124a630ab186fb25dca89ae236657f377e0195ddc1e3d2a493ed9515c1358d089fb06279499f1fac05cf9701668eeb36a801d0b6472fc685d9cf005ef7395a57380a64982d0da96625ef9bf86a269e251606d69d7508e07a148f023a4b122381be52f5a6f971d4b8d94907787d49c9abe607cde62fcd357195cf74d27fa92fade79360ad291aff84888696597ab5a283e70643ce2a500259157ac6e57efb996b17109e109c27578fb3ef17529f69cf50c8041efe320319f77afe62ce78632c04ce9f612c9c15426fa348d568799425929503dc8e06967aadce44e4541b942369aabf961737458bb29a69c3fe752a57df990d85ffd029ef0badebdad46899754aa948c1dc43b34f5459b6b939bdd0586c8c077ea9f0d662b0a24d8295dace6e8a6a1c5e6685e3a995bbddd928d90f17ba920fb0fb5ae84f06b2c9f49455ef0c4a5f742d4dc68be2516f0cc8f110f7b8226506a8f8d1b82fbfc3c904d5208b40a02c31620046c297bd04913286b3555ac20902b9b49227732aeebdb275028af779c826ea31707343118daefe9e4a6d63b00b02997928066f17d338c7baf017565d2595093d97879756f1d7a27f8df6aaa4a2099c4009f2905b5eba357c77020d7fb070240e199b362b04fa759ba9e0d6052b002c53fe0ba5588775c11a5e2b73186cff6207d5a7259d85bca8f80f1d97717f0bac2c360f13f0e774248a237d940c47be2486ba301d3ba01aaa3e2a3ce2c1a503ff82734152880c3dfeb07bcae847ae419c9c4485b1d2d219416982d8b9b09f0405433a42f5b00a976eddb2bf5bdeb6a07c1dedfdaafd30213e0960cbb50d130ee73cb1828c219118c999a46f718804a0c4c87b9aba7befb0b07c3463d74cf80c5b08fef2d0ca7781c71edffda1d4963683cb5275ae3792084ec94210fac4a90ddce6ec1900bed7bb68d68b044b83f09463b599cc08f532d566fc0566c0539004dd16bd28d3739a148f6fbda76e140a5546746b3e1e68c38b702440ebfd28d304cdfa5c4fb81237551e76bfede236946b3780bb11aad4f1dbe3768113509a170ceece1657db3a9641b4a6e79eed0024625b7d1cb35ed6448da8642efb19019f0e5eb041a595967e797862c11df9a641194e5e255bae50c833331456cda0f2a40e3d61b877b35a25b73d9df6a8a31a2b4df3e4692546a1709832fb5047ba3c580088bb09cd456f09f19bb50a75d7a523920f1ae62f1c797360c10c67517015f206272dcd8be12d0a9f1550898d41d3a67e7accac69db5fc45d97e070b9664b830ac71e43b2456c11cbb9d24fac76c72cce987fcfe82078883da4402ba4b7e9c805c90c9080308cc772587b7eb2afc67819263573f13b97d31d4880d4ac84ce4d086472d31c75e7f8135af76559eca4d89851c502b26ed067c5ee1127e65dbd2106d5b2f6994026f33af642a1604be2c77dae27f7ee2f24521b996e0c17971d2504207ea6c28d193cfe8d5940d2ffa7445791b2341d5093da9b75d3a518ad7b5d05ab5f10edf4f18f1cac3cb1d7fd4786bab940f4c5a3fc8c1b42af5ef0115bea09ae66dcc6be21cf7619f836426c82d6933cee5d2884bf4cc0a3d7d71b9a50810967099df945eda88896bcd194cd3402d40ad7059a6f5a6be7f983f5cd1e9b09090a1cdff6626a68a612d437044c74f6623aae7ceb458b2b327f70da6c1027010f0178fbc1e79076e047a362d8508ab33ec9f87fc53ddc81cbdeb57611b4c00c09e02ea51cdc887aed0920022e0ea01f772af955f6b8e4ece67c9ce61d9e87680cd07be9e6ab2887049a9ba712cf3197c0d3274d2fc5067c29d45e3bbbae367c047debf3591033a150c330437da3e7a1b8963d4f2dde44f343b5c2b7ac26f3270484780fbaa7ca9605e4f6278e412a722e43136da5fd466f0c139677642b1d8000dab095d79a8ebc22b775132e7997100210e2387ac7739f6eb242173f279f6e021c4a072c933f55971d810a3a7965aa203a27d599385127d7e2866ebd0a1ff400745af3c34aade1e13965e6686d64f0744bfd008c640426b07967339dfc878a015858d926c853dfb5fb6713eaa3175fa2ad03437f9db23d1ff9ea8c39fa65c300f8a9cea031a4bfa197c2188947278df0794c94504186c2bbc3eaa70c4f0c1c03 true -check_ring_signature 573af2b453c9af913387cd3de4a66e92b640e2fa1bf2299ab44d5e2d703661a2 6401a85119f5420c503db6495e6b06bbae2325dfbf3e24f5ea2e1895b62bb3c8 15 3a7fd35b8437499d6a143c43087d2f348d16bb08a614b433265ec54c406ef203 6defb910d7afea7ff2ca633ffeb18a0d182597e9db180cb0f7a241b2db704c63 e029dac4dacc564d69eff9080e504aa0e639bd098077b4789039955ba61dea20 225ad1b5829687721d277e24ffbeafc99c4234097e392292d2702f3695bea1fb e4b78323bcd8e8aacb0d66c57c1eb945ab9c413a1c3c7bf7564650366dcf08c8 762a63f8f0cff95b694e54bcd3f4fe2e93d711726779f2d206605c23d620233f e12e1668124ecf13e8a3768c63a91f187c7c2b03fe2677f07db793476156ef35 71cc3b80418fc74f75cbb7b12d72b53ff7461b151c827179684198d046efa0ce f44a753e64ed27f53aaa4eaf51d562b3e3dc6536dc9e20fa7480c208245fc7dc 4c0eefa52cbc2e1e7dcc7461f8f699aa114b8ae2f2422c2d438b5289136e0fa4 47d0caa1c2504d3ad4d3e2e80533d71ebb10846056f4e7e9191090e17660e6c5 4e7eb9ae71e47f16c27e2b7b02f7601fab659fd0f0961d27cd6f75ca8931422c 42c691f0b5b2d6a3cbebae1a87b4e8f005384d99995df10ccc507dc6341ebaa5 e320b54a0b1ead2d71986f6ab57c2a64d60790330ff00d236a339335a20cf3be c90700df4b881e4ecf06a39cdafc2779ac5d76d251f80cc3b3bc9093431d47df 42e43f7070d486ea68812104ac699a4299d4040bdde7ca42c4fdf0ab1f91b80d2c6234ec13595fe0a0ac76dd8279fc21beadd427a6972d3c525b6643785b070ef865960ada931baedfe702da97a43177d80aa14a15b8cb0c90d61ab0c923b50eecdce588d946f8280315a05209d07a7b4e63300cc9fb5ac7151aa4d0e5f7db059ba91fd0b2475be24c4848e0cda27ef03cc14808e832965a6136731acae4c20639f1df8e1e436ddf4e73e6663edb11ef1d70ab7bafcd7239dd125bd7a97c26088c109c33a603ffaaa6aa6eb16faa3b2a00b23e4ad59517801d4682d356228d0d1cb676a685c18c7054c8c140f75a2d4cc7c88bc8f5be3a03a8fcf0d5bf74ce0601ae9c7e3df70b1ca74a5d1e05c140a59d681369712618e785bf56cc68ccd20d3b1e4db2c99acfceac300755cd37701129becc8bc066298c3fbc6fecaf27920770dd130eacefa49c4e594376d929dcf87778893c2d26d4f0dfb4b5bc065d0888d231477c47e273f740c5dc44f90b01f165345263818f124876b8ad2c32b6970c1fb90f7bfbef6102fd313e0b6771c893042019905af88c5639531c75c10d0708b957f611a1467247b55dffe741b7cd241c58e7531feed9280af525b02d649901715743976d38cae5abfc1876a1b31535225bb4c7ef67a46b242edf05ea105c0cce3c50a1763b22747ac4a32b5b6fd3de6e30014fde5f136465814b56dd718e0adb73a99b1c4c6895c587dbd5fa0cffd13183b7384ad53b8e3775efb7a228f00315202f5d8ec37bde813318fb5e4486e2a1709419156430183c9a7f56df59fb03f4dedca0f4937e1c7a50d330ec13ffcdf9b580d69e0afa0e08f961ef77923e0a368ecf663b01552c71fa7171e671b4c6e8cb48435a9f87e4c5b4178009cf5a0714a0a9f54f2fde51d99ecb3e5ca6325aa437ce787488d78d811d41c00b2e3f0428957f4737e67c30a693529063653a45a4e307b87ee1b015b915ca6bd711a503f5ba45ea9996f40894a1458b32aa0fc1e32de2dca90f0a11e450d5ced6a7bf0d30fc6a3d7114915aff388032ed8fa32ab957d9c9ba711e1a82ff9a3ec7563d00bc531cf3a94903c4663c543a85eeb4a0d65e5d6610d5000d50f8109a039f18089d0d9c35f8f670bbbf140850e3ff7f6ebcedb81896830fa07bc6251384e9140d541c2727f4663442b31da6cc14390803a5f6e0aeca4a23bb0a0ff307bd35f202a6515f46d8d0645e48fee405f9e9ca6b84df663c5f72015ff7aa9bcc6d323a04bf3a8f498f4e5f650193a15ed84a37e20a6c5613ceed5871e3a1d97dcf064f076a3935b79cca7d22921bb5fdd2b6b2371fde7b2b39e501b6417ba50e02d1cbd7 false -check_ring_signature 8e3a03f1e02d31ec5221634578fe8e3c09f7f9956a4d51ba52d26462c685abdb d01e65aac9eeb8019c18cca456c8c247fd56cb5e94b23e95b462625ba1edda0d 14 7638f7902c57810a229750425be5a353aa2b1f739d19e09f6e5d3b93f0c24e87 d7e9a5a92502d00fd3a271e275b936496a537872db5d746556180bab6b1cca0a 097a04ad3c8d342b700f64fb159bc5009835c51c43fe20da119f5707a6fd9a6c 39bee7bf5d4146cbac2554074a1491decb22f2f4eabfa0264d852cc67ac2786f ed547494c54c393390b15ee37d7c9124021eb2aa8bb99e46a1768dd5398ab370 371d95e9df48c470f31732c5a6d3b592e65c63a3182eb1b0cb28d7a5872ea50c 1601c6c8b26e3fafc60ef397cec542a436ff4fabef2feb179bd4ded6d86e0f92 6adcc59d0919387d80cbce1b0b5ccf658930e8221c648413ddd8f4746aafe88b 20f784921e1f202c671087f53bf37499f6c49dfbc9a87b65803414523b9e0e72 506d0ed581d757726de1fb20388fea43c27115ea1868657cfe51f05c9d3ac333 23ce2e85dba374d1d990d40357f85119f90e7a18ab640d7173331d1786e531ec 68b4259e30865486dc5a363a956bce7aa6340e5489c82a7c2ad5ccf6318d6e6d e43da861b135e3e3feea1e145697b77754d0788dcc652c49b9c03b95ae15bb4d 1598eca4e7d2f817dcdb4bb2707d079db65eb407012371ee30c04dd253c0d225 33edc8bf4690a52e378368d4487352b7f17f569a96fdfb6415e056e318d9080b23a4aaf60830005826533c5d45b0b125b96f748305608144b7bcdc9a6e7fc807bc6521eb3ec67e23f616181f75d318c8f0858322b52e756ae32fb069a5a5830688edf6739ce7263e450cd17faebde2a58e7111ad50041b658891dcbb465ffa015c3dfa86f65bb6468095670bfea924dd10ea685bcdf30f4464a7876fa6be1406b9fd111bfa75be56f7641b2030af9028063c2724a511e7f4265c5791e720620e3aeb64b863ee9e9921a10e9ca5c173c3eff213376a3b54eee8d598a0ba9bf80571749aeec341e35c7fd1e8351dd1660b2c829375ed014bbf3d9ee8ecd417ba001aa3520e0f9d3b32e86dc8903fac81b7f74cf7d2f5434e13ed1b1ccac8814c0e6ad8256a80aa0134c4ba5b5294b06130902be2610feb4539961def9cc4893008ba0a3d9dc51e8a9bca43c9bb54f6a530e3c7624bf2a036c478f5e5db224d480dae63e289eedc7ed78150160f85c345582d92911bf1422b8f67b7305a67885a0515a9ca51faa744ad804f95aa2fa21684a8eaca4e516f5090d5687a55c7e1830ee75ed66f85f3340ad82a0736dfafeef06b15c636546bcd76ffda970dff13c00702788b60bb38fdb7b736293d89a15a685edf757b012cb0160f3744c65b880e006cc18dc41bbd6d7949815b48919c15f47e72d65550f52d67212c5ff36702a50ca5af1fec9600f198a657d3b5b49c156a9d7ed2dfe085e23975483e8b1698640e57d95a9a522e715cd19a12319bd5bb6d618f83bbf0bf5dd41b939ea50483460f6b764be9fe9ec65870a0046ab1359e3876d4e090ea152603f5ef968c230a5b0a9afeaffa74d85141654b5c9db1270d01a3cf5268057366b2122c42bb994d9d0a32f8b1569ee562730598669a2393dbcab9d70d8132ac1db65ccc71f07cab4c0720fd73c7603e139e5bbf05452f1141e0125ea290d4d7e06e5972cfd8b267f10c5e8ee4c4b7431460174d231a719f76447ce63150d80e5b827dd6137eeb0f6e069340ce97bf15293f63e53dbbb65d53ecba76573a33afbf31a099a5fd4cc6eb0ec1d7c69e4b7c766bc89ff555aae633c716d4f24415408d706c62d0ddb2606a0c8bddb2c33f759f3e4c33d345c054f2ccacafa6d596a69b298dc3008f914f080e909127de7e8c40730e30f2088088194d52764631a726fd48ae14206f289cc0082a8ce9462a63501c8c30109a705abb0c42031c2dc227c919fc29d740d1077300 true -check_ring_signature d3c41a4df82ded01c6f76099bc24f1c353c9381129d7f6c148688ee1491ead79 4a251904a72a0d9080a4bd6329968124bb0cdb5d04ec48f439e6892eafb42000 13 d7b0f6e24fe5503b1d0e0a91e88212e2f3bc73e159aaf58cb8150bd2e4314a0d 39bef5c890d5068b46ff1e2938f553655fafe73b1588f33faaaa6543d5781b9a 2e20e5f64314403c6a9cc03ef22d36daceba581b8830a594d253c3dd86962a7e f54e652a7305f6b98823172f5074bb88c128344139f3586eabcae08be6aacdae ccd142840f7582e7b3b44c05a2984bc175c3bde9aa683da744847eaff3e4f7a3 1a47b288bd44c5fa7038d545b9b94d1a4f6a7a23cd2243c85e454e1664586ea3 572aca36e06daf8c2682b03e60f696ff6afe00d0f6330e4d531a629e0c3d94d0 3a45fad69dc22fa89e4d831526d431dfe6ac351b54be87a40b6aea18c735de57 f7c1fb05c501b5dc0152cfe089d511af649eddf5a2374d705dfd89ac05a15296 d25a1acdcb325be6ed061a183d17ea77339e1b6cb7893d7d9d9870bcf7a2460d b61d961e87c6b6c51cb9ced65f677e5dc45dbc82f2612f1cf07101b55b6b8108 6b45a51c82a14ec3218c831948194af7118b949618bbf9db615f7c6d0e763d1b a9f1060d30ca8c96aa516d4d91d5c02307f332ca673bc5259ba71589437df826 197e4ff0adfdd0e725ba5b6028362e718f9cd3d30ae4f9024d954c4e07c41102928c749787d4987bc126af511d2a2363158a7dbbf9123295b0d9172a4bf41602d41e108011da97583618ed1a185a4d6ec627dd252490efd7de1411fa2248f5051a5539837304b761ac2f97ab838ac95f7684a20c9e237ea938151609f0768b0b69d4b8a6b24454576a152427bebb0201c1c00d4ac9b0cfd106e663232e06680f9076c44d5e1d31dfb8231fc4279d9978fd068db6e18a5fae45a3d1cb5399a50c4bb92802ee8a8a4edf6a37b596ed6c097bca281ca8dbc2505b07e1ee7b6b5e050f95f29618df3cb311034116b2c8338ae3104e15d5cfc61a89b3e408ec2d7a01aa304f2481567ce8985c07c3be3daeea65890b6b1fc34a6f4d12455a4cdb3200d4f746109f9095a8ccc65b2e9f77f1b54457d7638dc2acef35675dbbece8340651e248765686ef4347179b4618d7774ef241a6c0ccbbbaa1194ba2c2cb239e052412bce9ede88af04b3c6d3fa1266a19c1f80146af451e744f1a5e188978cb04a1b4e5531146256d1fc6013a5263a1b83e69a21deb6a087cc6d039d9d372ca0e28cdd5a4de3f0462e2e7b17650f6746bbe10867f6fe965a99b9aed6e783a1e0d87f23558a9cdfc8356c49c791aacaa01c27e0282300a3ac9e0d34e0100c39209b29eff329f7b5416c1a78ed53aaa1b591d897dd11dd61195eb37f5409b21330e016f0e2b5e3a46be0fd6c968a6b36097888c053b872dacfa250b5c88f7625d09acf1bfcd93a43722beacb2e196c11140ebd5afd144d57df6395ebda9fa284b0c8a3b1aa149f32cdb3261e8bd4f537c4a1ffc3ba766095d06b1f1036afe19e307ea678a74229a0a13bd5df498d8c91f547467fa932b68b4d4f5ecbd602ec88104dc3869686b455c48d848990e52ea7da7ac0c4bde714965aa78b256962c81cd0351ad69198b35ab7c1291c6c7177b2ca61e3271d37740ccf5e4df3d6f9aa4850369ed6654789775e7508263300c494525336e59c54f2c80fefa1b5aa3cf66ce04ef0298f8ea08b67c902826b9597b6d4d62988ac559b78b7ba989f74319f04805069db53fb21af8c69f1e986a48441bfd68f9eb5ff7fc069671d408b33f52e2099bcb4815d71cfd851552ad72ce16d2583a31e27a4afae39e6b68ba3f0923af0b false -check_ring_signature 188c22e8e859c966a0b77b741aba8c7b391398713ae8aa59c749dd8230f6636c a0554aca43a9adddea95d9ca8c95e945724dc9620bea878bbf3210d0758172ae 30 7483e06af1e62b20bf6cd094e941994661bd2e684cdecc6d699e2a1ecfe0173e 6b55a65b5014d72a7c29e16e5670aca1acde7d385855424caac30283310e8bcb c15b080554fc7a0d8e0a438079ca1f470da2a873258c7aa26e381090daafdb0f b2e5030d5be8c8356de7978163aa996e3ed2d791b584284089004381f7ba22f6 16adaf4f0ef6ee681dd2ac02715a4f3f8f067a1bcfecf952e916d98ba448f6e8 e658c5ff535c37e42a8728ed0710fd0b566e40aa746f506ac57a5ffa075a6502 c0a53c2f8a7f3768b0475eafb72cf7aab4d8b7bc80154743067ca5c8dc9f2b2d 222798cc2a4ae9580f9761f19bb5a2c1d9f242d21b011ebb0b3fdaeea027d578 ca994ffbe308098d45cfa07630136dcff7ec46be3257f5f1ce38bab723465bb2 8733479cdd5ba4e3b4c5d187f9ca69a1af4f1a265ecbb6abb93f3a9db77bf067 0ff0d6102443ebbe27ffcd47cf1ab3358d57a2253ef50a35ed3f25033c636b0b 81f0a510ecc0e31049d2d9d6ef31145d0fd3ea073652839feda1df223d0c3794 2a4555188d203db67b317b20bc6ef9251880b608875e1ff622ecb1fa555a2e2a 661a7b172fdf8aa8700d3cf37eb0d48e6365fcc749487e099877ab52d5d243a2 a461d80ca1b9be4a04407491d34c37fe978d5314ee4a96b4373d62fe30b5a115 26c7934fcc7ae2372ef24bdf8f7cb17c1efea30c4cdb1c7cc389b2a6aede9385 29867e558127c8f5f602b75341bd0a160e8c3afeaf08f491785e6c486105e13d 4ae003b39d4c4572908e62ba711aedb154f7da5c3f463c1899d09a862cb940f7 0b3d2c7f61c9f8e3a7eb3cf2e7ccf078e195c6d853ea321124a935e9f61148f2 6c3c204017a2ddab2db70b9a332268884daeef6902c7429035beb2f11378bd45 c6b727f2978bcd1c1c9b033a36d2e93ce9b1d4f86fd9591603a3517b8f70cd17 91d642a70d021204b16c4ab788b20c470569798bc02f97d7936608f571a69da1 1c74c5f106952a9307483e624bd109f4969c818587f48611b8aadab6130a55b2 5f98b0d4e58bc6ff1af191dcb35081dea678bdda680dc8f822ade7a56cc36893 92a2d817213b9e0bf4c7163a398cf0f11d335d44836ff0a3cf945b2a1da544a4 bfd7fa807e734b046f2cacb9bd806eaa726858e16fdcd8b20ed226ab91532925 77d0b8131d8fc434719d1909e3938fa12dc60aacf89fad526253a5549865e2a6 9fe2a61e63499b0933c232105109cf367762c99aa3bb803b6b4d9069404a563d f11ed64dc6f438b494b9e7a336ec7c94ea4ae3f636947643d6607717c0e9b5c2 c57d6ad42dfef81255a4f96af83456bd450c0b3374f5a76072c9151b0ef06431 5f3804a4710378b2446c0c90e86edeceb8d76157c00f0fa3472f3c6dee00183d3d3e34883229ca046b8295031673d93ec7969254fe9a6943f3de6a441ef6af0f2bb20a4f9660380202ee738b2c082cd74177610b16da94ce5ca71487e5d2b604926796cc9edd10e367010b7bfaa45344cd560343103388d20f5b3e7549128d09d232ef997c6498c9bd238db9423e1d2b7389a4d54a90e6216ddc649ca63d080b174de1018861c2a2b4391660aa22edb83e1c0813a8279d33c2e3bb222fad79031236ff3b92f4ade5fe71fec8a9663ab761a16218440f72f284bc9e37dd223c0f1027a356ad577634e319d6e3a4ecbea641230869a76322e3d5714d9f4d360a09217a83f978b62f4673442a2d5479d4b8625e5ecf41a5aa83a231fac443acc505bf0d511149d7100c75fd9423b3cd15c77ff57e4d5622af227a65aa857085a60d4e5fefd6957e93c0a90e462fa3325dc1be97d104f28aa73c956dbf690a2acd0e2b5d04e1b2b7c483c54b31b95eda4fc28b69cbb0b83eefb37a38a971baed730f873b8d4c6ae6b00767a5d89ba189019c71da860c395eb45fb7655b7f20a6ed065e8aa932f50eaea612fd68d27e4c412825f9c03a78aa01834f8196647f54e905e5cb45e5a8f656c51f237c86744206057941653b050eb2301c780a7d1583bd0c55816037bb6eac5f085d6195f53097a8315390accaf7f1fb2a89c1778a68b00b938825750b71637ec1dd94bc6f54187383e207e8287a91bcf4a5c5d927f95d08c6f53dec1c711f839ded4bde7ecd853e50306b9e2b3c22bcb06aaccba7cc8e04a7eb478a6c95dc40b23bd34c2f2161efb050e614f1fe430dbb4e53d124fe59085720af558340e0dbdebca02026c4c8dfdf3316496daa7b3697ab0e92f3403b00d9d8a2d784c1085125e7f5ea9e1afbb3a1cdf504120c4b55c0f34e805dc68403c929c4581299c88267760154bf6fa897caefb210657752eb993aab3a4d7d1c0a6b4df1b52dc4bbca7486b7386c59708ba83c62c7494e34d15e507c2d4e3b310604e39c15fd61045443c2b40cda473deffbef2637ef64f238d9844dc2c519f50424800a58dec0fdc44f070a9c14eb895c660d29f58cba2953f89b98e74bac5e0fc790f0795ffce52fd87bf1b72902bbe48dd7015cec695c52c7021d652ba63301aa30105009df3821663b41f239a40baa0ce2333dfca22c8b099cec5d84401901f8ff54b59d8b8db6e9ef3445bcba785f3ee6c37706dd06bf5750923fc6c2c90a1be66d92f588c8212296fb8c17d596657e287cdefe60d56413fad70ef6797003a93a0d405c94b24a4dec22fc026994cdb71c49a14eaeeeabc5da6bcb364d440ac514a139fdc9aa36b951ad869997c8a509aca8f81106f20acfd34de01ca7e307c4a73dfc86f8b9941214c6e083f7be92d85d658e8dc1ae12e0a8a165fcc3310703406acfa2e9bdcde2736ebcb0f49008df1dcda5a1bd154ae4eae85162a6780c39ec185e236c24101c804f48aee4f77a323c03a454c65b549bf9c962704b9405206952c40fd60c4562c487efc969fd82cb59b549bf3337e0dac3a6f40d47360ab3337372c1cbb7a71c7b1b53b61a059d1e75cde717a5c06edf6ba86669f5df0db4403093c3737a65215aa8663e163d047ebcef87428e1466fd43df53c720730050c04a64b8d12051f6a779dadc96ebae5a27ab33d0ad4ad4bf63293669318903f36ff18f648febf4c90de65842f803485ceebdbab7871c3c32ae1d33d60d7b0f9026bfccf37ec193bec7c701b35bdc717c8f16d5c57d8e42780c2e72cdbe6a04619cf09c3e39803b36b726ad6c76483357368f612431539762ce68ea12da5ee2a154acd5e324dbf11897dc4e13d7da8e2a5f3d678901c298f7d92e2f8291180d3c4a970c36203b1725e89f8c92fd960cf0ed683c7362dc3dab883f1da0f28e02b35eae66a80ccf2b45d7a454b8cbe825fb442cce43550279c03efe64970c1809b87054e9b4a09f10e8c96128f257533601ed80434448364a2576f69899e08f0719063d98c5f9d7a78f86a1dc51a456ede207bbaf68d38162d343d497e760a0020a9caac9a442fc21a899cdd1704f1fc06f3abd14d5120dfda049284be1bb7a04b78fa3797b718f3548b9aed4f60719d8bd5d8fb3cab5407d64d820390613a900272156c7ce78f20422f5ae58c8e4ca34901e39e5e1ba6f60ed03fd8b2a9be60756c757253a1bffe2deccdd3f20c8d4b6c03ab9e6e28ca968f677c48267839d0ce44279c78d2bc646b7d7957eee438d45c1cd52341435274adaadb8b6e36559050cb1f9c451f3896e9550e9f692ea0a4e0d6eac66fbfff2519e4bb617357e0009ef9393d6adf7ad3c02d021f432345119404102275c15317a08dbedf7b11c9e080f1f5f66e50c6e9c21c1d4788ff73118ab2a220bf26f220d8ce1be09fd8aa5023f0f5862fd72022977d2b81f1e2292369a269dffa0867f5c92064792b74018090f715713bff61a74cbc6c348f82b1ebff706379c8b1082f0c48d555467c21f44de656ad86d5766abc0999c1748c4454f371fa260a6b798abdc51e41f795d760715e003376e2b0e620557202c04d4fdf1ba4f26697eea587268aeffcff83dc30a400d3f41279ab4dbed87e191909b8e7bede6d4b8bba34c3961e4e927ac69e002661d081578800b5e75b16c03a20d030f624683caa3380577a5d96ea557cd150f false -check_ring_signature ba5855a4dbb7a3ac0e18790bf3c939f4d8b36d8b427c09801e4dd229965ab62e bf7ed96c4069eed41f558c4082d9ec4448005b32a1608b42b8a41b30fb622c79 1 d37da1af046bec92cb1bdd8dba0c5899e22b4d9c5639007416c9bb769ac6d0b6 72448130f5606de66aace7c8d8b3e4ad1ad9e9bbf7e3d027ea8bfa1d131302f8e49477292a0fc244c47ab4344f757a3b5ab640f616f8a87e35d36e72603107bd false -check_ring_signature fcb52d1486b4307942a8909c8ff1b78b20fced2d9a343e2f65448ace0a5dcecc 398ea73d11205e18bb33c0fdcd3a3b0100791753b9dc59428f1b2e964e40a301 7 d4c9ec9583a9cd6a7c847dd15f974414980195cffa195587a198dde5e88f4eed 2e9dc728ac87d8b28870be5cb6e32d1996dabc4aad0afafb0c790fe4cb1460c5 5212dd8adedd63097cf04f23ddd1714ee2db45cd81d86c8890302e74353788e7 bb22c1184fc89e6c8a80ad9116d78a40b2e6c27e289a7f6636bf230660de8611 307a0d71c0c495ebd05972f79a502fc3476c1e449293199000dd64a88f13ac45 fb1aaae67510ee2588dc79dfa67d1e8c027d091ff549e595e5f27f038f13178a 2e544ae0c899420eba31baaa09dae09e912aeff0ab3693e4656d0399924de820 770accf48b380e79766c88e2b9774406113aea5ffa57e3985439185b7747570fd71e558309d79f8b4c6fe3973cffb858f29f35106e2ebce928f57e692b30d606fb08666fd13700dcdf46ef6e97cfe23f306351547d918db2ec0b0b2bfa3e6409475a8551b35cb4a469adeba5e116b8703a68af9f59c1eb627b5d74f4eb4f02064a42570e221d8a655e3456be4afc6ab3b42882db780bfbed2483cb31e873f90473094eb2dd82270f84d7b18676a72645273b8e2ba7edac7fdf244904e14d5e07a4b7ea0b464c6408e094cc12adb03400e14b01cb2a8a95b25d951088dd03bb0a945bbd207f9f72d7a9cdd0d6eec98ab7ab92ee916bf849a3fb1b6da236d23409ec961f14e804dcb91fa5030ce99d85343ac566259fb943dac3a00c05fda41e077b4739e8f47da03a401a63de4540c40ffd8c2d3f9ba35ed7daa404119f6dd701d280158777ac568715f0743ea4532965ecb0b09b935d94886782acd155e1d00940a79ac42d84651eec4ea28578f9a4ac9c482d3696afabc2d0b692559eb3fd0e4dbae6c1a16797bf05bd9bab74504acb5663d9834757736bcbb3aad75a8f2a0adc0e2cb02cd35af2ab7225f8cb71b85b7b09d24a46061f75b39dba5d6d95340e true -check_ring_signature fdb63fb4927078e3a16f5ea0152dbfb8133dbdc68b192409730489626dc1636b ec795ed1ff6070ad4f875980c3a2428dddbcde0668fb3669700298ad65776cc4 14 8e093f6c9c4d1ad6336da76598c4e2ddb269546203151b629fb3788fbbc17a1b 98c9eaa2e9dcc11bef292f3443cf18ed6209e72450c3568bdd438c5e83352ec6 be86f155bb112eca8a61854c376c5ce3e83fbc88f86ebe1b99882448fb805a0e 10cf2535ba4f02d743030843786102eb586889573a09932608b2c0ccd918885b 21b7bbe117132c3fc3e6325d8296238c3bfa0072e73689288f1ba2217f4f6b1e 05e38104a5e022e3e11113544a8071fb8279ac8aad81d6e47abf56ca17ce994a 3aea77f84b27f897944bb25b73516dd5339a1df98f766ae480161faee9742070 6bf72b1dd1833cc8e3794c5d3a472ca82910b87e06f2de1e96aafafc0e0e6100 1177ad157fbd3c945250eeddb94b7165b4fc3fe7002ef18eb836acdae76fbf2b 1d067c21cb163f104318f20170cf3c7826d8f8765582134ba29b614ad7fbc61e ab541c483a88841d406cdd83c0bd2735a0103040340d3ae4fd2b0d4b2a63103a ad9fe41e986b9d2a85aa1dfd4180282de83b7cc479aa8f84d10d4f8fdcc3cbd1 ad4436ed4aef6c8aa18fc88f0f77e7033f0f1e5a1776357d1acced9d46f27b2a fff165e3adff4e9c1b214fe67dc999f594fd73170a862c00b02364c1e0ed3f26 460fd0ded0648e7959194269aa4c3f1b7a3dd720df73df20b2d07f79f4a0460eae99e3d3ef6cc64ca9a0025e2dfc49e55c02f4aba9ce8b579901cb9cc8f6d20a8b41f728ffba629f92569e8af8ef2e2840a4620bd8efc57c50f3eb7ef866e006b9101463fcd5787393d6c14a8f59452cee0ed4ff81fee67922e669bcdbf06203acff17a96ee53f1cc5a81e56ba3b15286d3dbef806675c7ddfa3629a0280a6002cb9e6d647777c6d89748adc17674e1cb04254a384c4b420497fd61252d9160d8f0a04580da3956a33e01769ede4536a104d41df42dcf6589706840fe85d3f0b5b5243bfd271d4cc2b21644fdef7446e01320f85a76d408394f5f4a4737c8c02823dee9db2f069af71a2a891692984ac0b1facea92ee741d40483ff7105c9706d1660a84e5066a9b9201bf20b5917623224abb99c0e521b7c7b6d36d0a907c0cb7cb96c2d4426336f3937cc656b6ac279e1e4da14e86367f1ff83d5c05517f0c2f51291469529e2b5313c46af37bbcf70c232517bfe6d6298037b87af84d2604d1f5f912e13fe78e5d9e5f0f0232b5c3f9a26ab31356f6ed0d467aefc254c009f1f1104d142bf57d9978c7e618a1a1db6b50dbb9849c1e9fb47143ae0e74715b3cfa2b6b77e4b35340a0e190d9f0eb85fe733b641bc1301bf709669c9ec26c00215fbf4215652a73b8a57d6d4a8762c236e073c895416e3ec53065c963ad41055f4f3c000654e70abd5fefc8011448b7f23da21e7ffa88d3c05726a5ad318e00cbdfdb2a999d93324e94dd19be674b62b17f4ea2ef00766a77d1926b6396110909238d5b69d9be747042c4871fe553a52eb7c385ea9514fb488a96be15af8909a407b99196d99b815b764d1376293b4bcd8ca395ea680c309b7bd427e0213503bece36702398caf7c6b5212a8111414e5fa0d8f9ec2c38e3a741ca9f0192bc0657166b161b1590cf3f009e1e167f72c89db1e9bb426551740c5b68be269da80bcd4b587b4ddec560a3321e3e516edf129aa7fc29f823c3a4d9d343b0b0168208b17fd99973b3630b661f08e4299269c47177efc1b67bac189a16633cf303ce0ea6e237732f5b228851cb7d156e2d6345d6a08bd7253d3b30f32d953c060b6601922a355decf6ae5eff175d7ef66e495f22699449bdb3b824f813fc259c9a7005860cb1fd13c3a5fc04f02ba4a9a20b139d0c103ccd648ac065a1ee07ebde1e0c8968891520f64089d647b3d863af25c86f742dd61ced6c2628ad300d6ae1cf00 false -check_ring_signature 45335cf12f296e8b179522e744a9d90557dc004e0f91b7b77fc08cad3c98d476 c06d2e927558af6f806f771001f2db527c31ba21cb01877f9f7c89bf869e54da 1 2dcf0da39296aae6badad764fb03e520801b2453f80800f4eb74080809ba409a 953d98b122f30716fbf1ac8a972eb0e01f13e6f9f57eb90b0dcd19f80b2f1b07b24d030811faf36643aafc07872fb8589b95a593e51e35efd44da6d44ab6190c false -check_ring_signature 0738b0f61a54bc710961cd7fdb5873844003a482e7b4141241da7fe915fde669 2624638d96b6a0a3c28590dc308e8a3c0887ee32cce27116e9dd30223103ab8a 9 d77e559e816c239ab6434b48911590b5ea90e59ac77a8ff73c584d3723cd2652 a8952beff5de7c8f439dce4b37da9c856fefc6d616ab64d166fd3ca8e7b78929 b0796d5612915004a9e4cc6ccbcf16a79b5dfb3c375bb635d326be276cebf27a 9a6a88c7f90058c29cc4c5968e6384eae22bcb38518526aae076220637a5f158 c80ddb084dd1169b327ad39a7e0bddf41c6bf0e830e6ea3956f3f489e670b42b 77f9a2e8e763ebb2cd4bdfc3f01e242f2d697b12001af9ff724f7f6f865a13f2 a2f1615c8caea5c6cfe2afd69d48cb019a15251627fc13707d57be6c159ab449 b52ee378165c9b2ff5c8c891ed75af43032b410e701e8f38bd2455cb5e36c430 935499f488be9cd4b866b01d0e90ca64f9448f975bb56fe54cf925947f084a32 8296102033c0ad1235963c17cb11f8e0e00bbbba9e189d55ed3f7fe7d0083f090d204b397a5152ed1499539f2cd2e16888985065da82cd37cab8fb689cccd0081fd9a919f644820246e19deabab851e20558d767086ea9fbeed1eede5f5c0203b8be634a7399454f0cb388f38021be5f5616e0644ba98a7ef69db4afab0db107c891c3920b9d820e73cb46ac9f5b705057a868321cd99760aabdec30dfd4980187b0f53bd3336a6f8a561c034cb88ac78d984f20eaea0d495dc64605321a450b226f5760d0ba29b24a41098b3b69a5e7cbbb6d00bb43007b35766954f75831087e28a49079bf1d5a09d825b0b260ef9ae1e1d7c971f80e6c2e763778fbcd9e07ddc6d5a7c9315d187ecc76c72dddf3ae0bf4e7e6e30598a8282afa5bf553c508fdb49e3f4044f2376ef367a877714fbdf0685f5118928c8ecb03f29f2a6d9e061265462f944713e29732ede4274377ecabb026515f4b6e34eed48038208e710f230765ea7468d001dcd6bfd2d422e0fe8d33f1c0244287d3531a6d1d0c30eb005bdc9098c93970f437fd839f9b4fe002625ce9f2ddface30bac1841af0a6810cf13142fb9974eaa9e8291fa2d21ad8344be19ac668beff6b93f53742bc44f30dd39189240c1021e4378fc6db7bb47723ea4d3ddfb85d30349f9f7fe36ad6f70ad3b577eb2bf3caad1e7b6b5910beba128552a13560d760ba074cdee00651df0319d79ddc3b50ebeec3c04936c7522a4c6bf0f6b9337dea157558e03218111b00950b22f27470f5db6ca0a5daa2b5720ea54c9d21ccd5fd292dc8393d068a920d true -check_ring_signature afe96fe95566c3d3acdc0e1a3e186acd5c6adf602e0d74e76f1a3f9d6685246e 43ec14fc375955345b58e15d082676973fe8ffc378e70a2d59070108b24401ea 1 b8065fe7bedde0fdf56d54bb1be8b79aff30702cba7522cb95c47a3a35ab3daf b8e20ce9d35f2f716bc9c1066f3b9d16558f7111bc3eb257d880fc5e399f890d9d1555f4d3a4e132cd303782ef231042f4f62dbed6b5c14fc06f359d51b400f2 false -check_ring_signature f3387d26d154a2f707c09fa6efef0d071b546b9a64c6b917f34353bad5cfa773 b94163c19be88cccd690607a9ab0ba5d760b19f934c6184aae9291c1bb2c194d 1 eff9902e66cd3784691c9246a4645a7708643c05a07cb1e9e9a273fcf5d5ead9 09af637beddc0316c988fe18ab2002b2cfe424925fbfbc931198dccebe6dbb0332d10a54b0bb8b923145d9b18178c4f36a063abeacf13e3858d18ce6b26d1e07 false -check_ring_signature 6f664b839dab59f83cccedf309e3dd788fbf4a60be528a775d0f3169593fa278 9977618b062c20571f65f1163a3b8d3bf92fd11334c2a70c96c2b7442dd61e8c 85 ec639fe5a93f6c57aca7e7f84537adc707b13f81603b75e095ce23571267edc0 604bf7606820019f69d28291913b02dcdf3ed23c93ae092cb283d79d273e9189 8a44294f58ae11915129b323e974bbed280625d51ee7588b7b3c67637680f618 5975ca7cc8d3dca177c51fd61a8806175c3283eae0de95a8bf083a3f1ad535fb d15b4815f0efa17114955915ff3a59b160945d1c841008516e5c5c907e23d034 745318b810c8ffc5c5a192aafa11969ed31144743d3cc49a45ec560b8db7b29c fd819ef55c0cc5b8ab5b1280b50c1ae9b9313e74025c263463aac555da3095f3 019b8f8ac307fd49dd07aabb0dff0749780144bc1251a15ef5627c3836699105 a6f2d32ac7d073bad2ef78f8fa671f39eb3f620131669dc60760b3871714ba8d 5e9ed4690b0168aedd8a0cee1e387614ffb7db7aa161c2cb106804ff74f5f88a 1d3be87bed4cec413a10989ce9d551b02e6df48e3c5309443cf3230be6d79622 a35827ab81493993ae3bead3b5328855735fd7897d21e78286c84c9c1ae4535f 1981e8126c750dae73285e3c29e212a44d45687bee4f4c5ced0454258bc68b44 2e0b0b97ca7e40572a668caf3a22c6fdec98fa3bfc926edcded17fa5040dd88a 115b01773d0575d600eb786d2a2beed8e548b57ed78d78cd0c000fc455a4401d 613d854c91ba074d97e53138b5e0b987721e6747c2da25b04088a726d7663a2b a66eac0ea800c6a30d1c2aeb8ee53c370d41ffafb67719e17ed375a29dc48b01 b2580d540042d9512a14087cb8e75c56512a78c9b54ca6c1b373d3d481d09f0f d22aab000c6af4acff9dcd63317367650ae1e282ffc09b3834f09902f12bba06 9974737c86e04710e7c1162e1fc0da62765bf7b0b3a79837af7827c055ef33aa 154c24dc021ccac26d2f4cc1f3cf390eeeaab3949f6691bfd3cd6ee38d9085d8 dc8ee1996da88c346bf481475741a8d39f3cc2ef30cfe37d9ee6a512569b30ba 33c71c50a61182882d67face56a84fb12e06cd970984b604366116e57c788dd1 d9feb68e888c12d8c6bc82131e54a6f1fc66bac00ccf515994a9bdcbb4e960da 32f4080586d95600d39a668db453260a9219a6f8fdf98e0aa2db9bbf6ecf7354 cf71f0ac428b4411f1be2e7a53f3cb4a9da4b200282c730204ca003c1b901cab a9054890ac46f1d9d974e8416dbfea167d82bb34988862763bb56dc934173b9a d404d2ed2ca07864e89879913cea390cdf2ac088490dcfa6dfdb40f244bea65b 1ea50b763c700061f1337e5812d0fefd31fe1e0677efb14ac34dce473488191d 567f6cd89f868316b1db745a39e1ee682fb85061da2041a6905a613c0b52bd8c 0d466bfb462a33755db82a98b086101a675656cf2eb3fc6a830b6caa8c7f5352 8a7675dab8704f36037722da9120c503c6488cfda6bbdb33e7f303a0ac969a60 975c1abedc5191e7ce27fa54538045a2a49328bdcb32b0eff085c1a8bf1efd74 136f97486ff1bb4e3bec5bff6c911368a714c13be5a7b3e2657c6ebff93a5376 82f13def0509ad1f1e98be1fe420c701fc311e3a0a85a6a1017c8021252d7e7e ff277e64567bcc710cf0989f7c28bedd92e396cfa100058e5676b74b1bcba114 725fa61563b94bdde0dacfd58ddb56d2170680de9c13bc75a9070bea31bd5a52 090fa623eedce0f7c869687c762fdff213d5a951917ec9360cc78f8d5e748ad6 dda72833b432cb1dcab2b5f5c769442e3067f18932824bec80457e3a4415727f e6d74d2e2dd38b102291003a5fa169a57d6a87fc9c57b4dc65355d593cf14ac1 ecce6658e59d9d6decaaaf0b25b21700dd28dcef298fcab23ce040b02b658799 d0633727f77dc0a4221fdd053df42b6f0cc5778c79841788d10bb450c6a282e0 b785bb82cbba723a9d1cc3a845426545cd350d15d1e17216c7886ad2ce9b90ca 57c13d8a1c659bc3cbde1f996ddb280157999238b3184facbe5a16583b49cbcd eacf3a5e02b1bab6004dc09cff716a5ee8ce6660bf3e82fa31384e803e51d9a7 ca6386e3e6343ae0b64a753aa6b50cdecba752d763b6ea2ed423ec8cebf3f16d 66f1551e910074bcdcbe79cb6837ec3d35f3fb67e11c726b70d33952f05a11bb dffccba52f8f994e803e8cf4c061525d65fd7ca8c79adfcea209eaec9d77f8f6 9e2fedbc3758ae19773d6cd946ab78e53600b3aeedf076f2e6a512430cf799d4 394f68358a0073b8c9ba27f50656c3fa69aedb59da537a5a4a2776cb7e3cde97 80ce888b69619f34e32680dea4a5608288a2f37052d5deda500998d1b5857fa9 b9477e570f3b66bb82d0e10544c93f40c16a40e3284a8f6fbe72ce3de1f04c0f 31cf05456a65ff6a8fa403cea308cb6509ce0ef33c54df8429380f22459c36f1 af2a58b88e984036e8fea07301f7aad600c3e5bb1a8b0c5a153f4851885670ed 3f3776bebf4477eab02b6ef3325199c73631d936983f92ae53b6f0034b254575 d61146f8e2761dff6d7fc8927303a54cc1715e0192a00697f479889030b19d5f c9e8c254ff6f4c0f5b8257b5789885f9177184cb241bae54c872b00a82c06e76 3fde706a879db2b2a927a57bbc6e6d7bc7901d2b3827a98ee4648b7f6131aa20 cf396ac56afd8202a6dd8984be3dc2dd82bc412b12719a2a5fbfc03b26d7e259 91b0e5a97e1582cdfe2c0c528bdf51397e778c37d63cc21ee68b21c1171cfba9 049aa09f54cc8501144eee50cbf34a8bfcebdfa7f127e63271996e95835813b1 fdd03e102c05d75a811fe44ed2a9adcae96dcac5e4bcfb0139d3fd3c32bcb658 9d98423c1e6abcb6ba6493a7c40dfb77dc5a073855af8ca555714c85507568fc 14918d8ba5972a3c4ee9a4f6b8cd0addd5a1cdc79ce08fc7f31ed6947981d091 a27f9c4183b2b6a0c413813b668c33573741d511207e5f5011b59a21d88e70c4 f440153ed292e5cabb16b4ee22e017372590e7daed83c34a258c161a9bde87a7 d531f248264596f03f82629d70c6209194111216bb2b976f0b0a4253706ec0ce accbcecb119b968c418109a000736ce89103fdfeee2f21ff622f876f4d88fa05 c445b0e12491c61379c5418c6d91e4811ffa480b045315dbc9bd65e42d02cdb2 6f34ca0b30da8f200ddde7b71e53cc9292649595a556220a4bd7053d53043e07 dd1f9e0f0cad293aa2d57d729986dda972f375c5a63755adeb550c72ec33fa71 141a84d0170756ba46410e17a0ddd30fd51593fc56bdb0e7d5714d75100a8e35 e22154175cc01d7b7f9adbebc29a3d7fcd2a89505f35c3f9193de872b8b5c0fa 8c72b635554eb634d4cca76e2f0a90eac4e38d5347eaf81d8d18ae9dbd39739f 8c61bd0d25d79a2a04f4f13cd6001fd28e57d252c0fb1bde940bb7c744adabcc 2686eeba09f5e737f3845acd938fd9d90746b93310d36081fcb08ea0b6df5f51 332f1f6602dc4a9fdba37b61bb24a4ea8b795049c842aeba39b0b69cd6da47b8 4df6ab5aa82f77facf6190995cf55eeda770240e86dd058708da0f85dc7bb751 4bc8611f2b5db7ab0871450d9db7ce8f25512883b1fd254fa8b4f412b9dd85d0 1e939c17a56492c7948de1e028e7b8c075858fbc18e8b5293800ea8accd02e71 ac5927ee124a9f49e8b7fc9342bfc81fbfcd443917e63783723e09c5d0a85d54 321b161790a13422d8d1760229d12d2c71a2e608786703ae811ca2e40e39efd7 0035be09190b5a8a1e310a44db8e0d1b9d9d18965352582eb66366c5ba35f104 dd602032f0ce07833f4cd52334b35a735ae882a82cd4d4e6729448cf0a44253f 1cec53bc12b4148b1bbe503f19da76b1879df437269449d867404b5a74b4022c aa595008b7f91214ca7438e731b64c45fbef2aefc22e540429d5c5140b542a0b079691eb1c40a34471d3b3c70bbe6a2db8a86bad225a4c217caf9bf78ea44205b7b51667832616ada00fece80a1e5af0aa6b4d2ee6243485f2de181de5b4f20e645df793fab7101c09ceb90ea8fcca6d3bc0c9c1875ceb6d23d19f2e09ec990a49f5ac146ec09a5ba595f6602a2332dba007c9e5d477755bd2f69109e680360d23c18faa690c9f03bd2510a9307abcfd3d5f025e3f373ad413a4b0b1e29a92044be48ca7487945494c424005b9a38124ff9095b42e51d1b1304f39417bc6110e908b9ecc9d87ff3a81da764a088a0d85dfe5690e15fe9e01b2525ae1fa57450553cc8d077336876768a748a494c15e5074565883d06f3fbcef91cd667cd10a024bb9bee5663ae08deff0d1c2a751f425d580fc5289fdb4969d43a0ffb44c1c02659de6409dda12f32cebe6aae2bb62e4f3dfc52f7171de42696db389a5e22e071b972b741ae6e2c878dec404ef0b62f0198d63d3715ec95a3d63b5ca28ac630f4c53d4fc8a6897b29ed392ddda220178a510a41318b5304a7dccfa6be93e1d040d44a8b26b627ff5efe5cd73d1e5620efc7b7850a22537e74f7486cbcaf1e90e65a9f8745f8234a8d0e128a94d7d29f05a31a7aa160faafccc5dc7047931dc02837cf4423869ac6d647348735223162857880f85866966681be733a1cf8e590d085ded11a0f668dd14e279825e9a55421e0e3677e650ea2ee3ea6dbff878a4013554a3954c38f62c9822069a98d5481b95a798089645a7329b7a7d57f1a042046d949a5aec7cae39295b38106e0bf5f3b15a688a50f79e60785d97b6bd94e00886656035d33aa9747bca80974d892cf6c00078b27d2b81fdc596d00cd995e20c0ac7bf3396ee1faf6b562903eeeaca953dee01c1dd728955f21f1e6575d98d0b21b38686331846364ff12f2d5d0d3a40232ac9de24519c596ccaf3ea268f620081fd6d9f48db41cb1546e8ca715e8636a4a1e49c8c4a6af05b0d85977287d508a1176cd2908f28e8f4368f3c51e968e5ee349ef1fb4a0aec2f296117994ed10046c8e5e10778d080655faab029135c85ed6638a63db18725d07e9cedf6963e00a3a51b6281f813a82c88a2bc33dd7c0fc87d6a2c155517ca9062225363ff2d040d806f39445a1dfac7c3f69b783b4d7141000cd9e9a4bb5c8967eab2531d180d92817f8c5ab93713bc6957e851d195cfe424df03fd0971e61209fbcf06fe4b0e9d572ed281edf26aa9c1855214e2665b2df1cf6e11768651027491642cc91f01615749c735304e1fc0c651a6a5fbfa9dc7c11667f1bbd6fd6af4e8174e34050c7b24fa018e5c241d39a2e79972cc2331a5a4e710646212209e3831aa99dde901b56ceee7a31cbf948c767f8ed8e81162083309e4d06f1c2481357fcc8a318503abc750322ec71ded9817395bc349c6e9083e85119ed9c74080d162cdbfccfb01f19e0ed2e3b40a1ecce8f2e695ad3db0f2af7dd82567d77b406a81d5a2c96c02370d6d8acce2b385867f2d820979df9f4605139272a3b059e06074e176f843078d807430bea2cb4b6e56d4a5029f525c4f033b2c9953fb1124ac1a56eddd2f01de41923a070fa4cccbeda19a7ada5b2d982b9ab70db5b92efd78033829fa770be5ee932269a42898e033ff4e2dfd10827c55bfdd3ad35261df719c5dee1db2070f84b0f9f5461e0ae861743f1ede9e1219d4be0d65763351f0ecc50190bd740f7a363cce68291b0c02baf0e4059fd143e9835a5c9efb13965bc98af33be135002a95c0bfce1e5a8c910ddb9fa47856ca5be48d1c45d1d1971095dcfb72345c0d9136d0d5bb9e0d353c79cbf4924f68ad4e74d941aa5e3a065e7e823e23536d058ec3d23f3db3d78551ca28ff2746c857d496491a0a5b9571feb858be390d890885ef520d047f511c545c8cd3947bab9434912e28f332c7935cf6aa9d14e51001f59b4fcf3578d629d69d968afe1e3a3ec49d9aca299743bf6baf24c1a8f8da08d4811df90df9c6e3897de63b21fb4501d8313d29be1b611b2b73a170b6a86800b4886457ea26a4bb7c37982189f8bb8ab7b736c109ecd80742399a7d6a8af00cb279462d243d0c417497543621cc3125b97439e7af8cdcb31b31869c99247c06d70be673d63a51356c1359d6c7421fbb93a8c635620e1ab12f87bd067c845f0d86c3740acd88a499e3ebeb768a0cca4a20826f25231aa15e708836119a3ed60148c3faa2d0c7d87da1480f84ae3cdcd2e1d7b232085bdf51c2381fa995862f0aa8d5d6336c01fd0f213b1fd09a164f73d2e9778068c859a7ab62fbe214882003a87c9f7bdba6b9fa12fa27edbf88f0aac759009caab2072de2114b77f16e6a09f507f2eca30c2547dce554389e290ef674583654cbddedcfecbbcd226d71e004b883d3fb04a70889393cc20062f53ba263ede25b59b3d4b1951fb8072fd7e803cfd3056a8dfda9f6cdfc0bd720a3b3d64897326ef5d0c2cce8d0b09d1c71ae0d4ed68e294ce7a311db394362cb1eee84cebe823702d99d5644c0e650188fa90e369767b30110a59ea4a5f371413164fc000bc1779cae6db1ddc1a17fac35010e1c2c175705ebaa20f759e3eab605c2ff3c94388b0f55a0424cbea474e045aa0f3864ab819dfaabb49d32a856a3cf442230cd0abceab15b1cf7c7714c3f75580f83e0724ab4554c09b91267e4cba6db2108c0f2784eced903eda1626f8986ec09eda6d2bb81bff16b223938862e5c6d0547f1a13812e8c0fd6489703b366e88060640084fa3a2f8f4c211d33dbec4ae5a02d4a8c4d68134d5196d0d4a107f060c5c30bfb0fa69b97fea29ff816d8e67cbba47e1792e8caa44ddba2fc60be3000107170bbae6a6e1b1aa0c07857a51a832d400b26f8608f082185d6ea79c72f20ba487a4af2bd8ad61c370542d7ebfc606bf47e4968e2b81d5b3067c89c325030ede2129e32c147e8152ae4a019dd7e41afa4722b44c85e927c27c6828fec1400a282d052f65599dee7d1d8f8a13ef11a25c1c8785436983d0e529f2c27633e302ceae770b29bb379044be24cc9f9630fe4205e709788e460a9b2e270665b5c308811fc2bd52dca65fa168ca0b5f96ed67c8474226a4f05634014086c5a21f18098a7b11d7eceb0ed0c9b638568f016d66e64d139c0ce672be461241c2666d6202ae55399ccec18644c481e34b205e9bd9a5b5114645b2fe5e7b6750486177c50322dfd610706cb85603e2221d7317711f419f2b5abceb9d9eac5f9dce4728620a2d90051db1ec73781b65c29eae5a6e965bf8496d3d9c8782003749e22fe35f0e7a3c9f4727dd6a4b57b96ec6d68dbd259623ec6ddafcb1a97d488e13c4cd0208fbcb1f47519a31a761280cf1696115c5aa964fc34e880b8f26720e6f6c8b3a031872b1b32bf7b0f2f6359629b3f7d5c402c4dc397f9a121b880ce6f105eafa079eefc9d9f3a66a33d761349047df736e492ad1ef7ab00ef0577fed029f12880268dcca14ada5641c2d93e148b5061c17c2c3a52fdcdc21f42101a59ff0329c05052307b8adf592f8b1cb701b13c6679a83f64039997d941772f400c8c458550fa1af1be9aac4a3425608d03bd8a7be693c431faef733de849edd5e2998fe25048e7c301b73abe08c932d57905bd01c57caba2e8738ddac370dd43a2da00d870dfc674411121ddafa8b4139e9e7bef9f8a9ba7ea8d3101883b145adfee57d4203a0f54a8b1422a7fc0a964ccb2b0a2808bccf8d615b7a419146007eb87116ec0d39129ea923b56290d8c33934e1e191d857154fad8bf3599b248388a98ed82c084aee848fe0d224e626f3720de2dbba2dfc7f02ff12574eded7d21438fef3fa0fae0e19561449e700db625720c1e9e39712161ba7617c651a5b9bc7f74e43710b5c4028c2e4395ec0175e6a0adae15c8ff990d84e6a822c1628046198d5884309f52e87f9348bc59c74c952cc6afdf3552c66267b8e949d580bbcc976246bd401b2540b22b07607cd83adb1d61c119753ac56e7a04471b52a802c72ff20d240093ee3d1d6b62452913c761f145602629c288c41a45d315f7f887e40d66f805d0dd92a093c3b2f468311130849c2e3fe2c8015c4129886fd23c44e68e3d7d1d20e6e31431b1ae57c0923b3ca4d28ecb79309faf20dbdd101da104318e71a9f190b83e77201b6546aa77015a6ff5686c7afad7932e00a5969f9fd87be7d720f3909e08b2233486dc550a6d21f94f4c285890d22b1f3ce8fbb7bd2d40b038c7e720b9d276331987236adc0b4eaa9ac44e49752d2e355d77411a558d686cbb996df020def4c31bd32c1af123f65264633108ef4ca0960e686ce5e11cd2da8df0b390424cdcf2c657c9ac6e0d606a04af3443e633ed9769fadc620ac03267b326ef709e16f0caf7f7373da01081197ef1e99d8b0285bd6f6596ef9c822cd082f82ea01511e8c8674359e0c5cfd1caf500fe41aa2b8e01fa2b207f6b4cf4d7c55dfa20b2ed5f568195f1768e3589b350accfa1b5454ff14bf96c42781ec56f0665ed7006ab6aa7e35c0280f45a5d5440e3e7949e86b8f17b42796c433f8f3857e34ba08bed9f937651495c12e6ab7387e8cafc7132ec891e2778822773c70e3d1f0290913b8ccbe3f999d1efdaf5e84b13e0757b3193b5a1876f64cf42ac4501db39c0f9b674f68684bb9a5fcaa4543d370824a5e5f35a36248110c04dd02f4b3f5fb069b3cfc13ec8cbfb826e6a407e0aaf2472c061a0d22db3ad30fdcf97565267a0b1cd24eaa5b4c65b0f01bd722cb2007bf281bd2f2f8a9ba8e7c2c6dcd0cc96907809e81a5ce471eb02269bf035a484f50676e15b0147b4e42f178f4edcb0046047db5f5492d3e60aae70da49c97d535dc864cf5b8e61ece0b17edff26d742f4009acc470a86d3a94bb7a89747b91e3ca1c8e2b352ca1b7fc9a1ec992d25f117094b3fc2bd705a942d301ffb0fe93c41fb5d0e019a3d550e2726ef50df07d80905592f344f55c2b3b30730bff3c49a4e92dc5182be1d70cd3a7e8d83587724c70a1b8c7469519d931842b838d85647f603692d799e692143ec4769ed144213bd0520fb7ab7caed598fbb8c42049d818a18c198624b965f8ec79dd2c1310b7f0b0c6a94cf0f49fd4891449871109a625890188727fa851c5248fab894ca8a8d6a0358f545f5a6a4aaf2de562578ba07646c24069ae77b4f086dd1e5055fb29e4601ad5825e10bbd61b15db7ac15899d653ae71275d2e5c1462b13b6b0ef5d96c904457c4f0e6a1713177db0cb0acfe9ca4ee688bec9c6f936093beccd9677fcf4007309f42c167c6aa39392eef3bf142c5e288674db99ffd85bfb56579c788bf50c55051dc337d489f02f63e378de3e6a499fd992711bc059b3117ee0c77d2bbc09a4ccad7c16750da4b5cf23368a5f5e00da521e06e48317fd33534ef02f05820cafe13e8ce76bc5bcf63876c29de7c45ef48bba218715cdf4b699262db546df0a58f3b71e1150c7b423b1b906ea61191c39f8b7fcc4433ab0dd107a29b3a84a08d11838f087a7ccfc14ad01c9b9020229b799d040da50be57a708086edd7620085d3f17614c3c30195319d2458ebcfecd28a7ccb81eefd67a6068c91de2d0060f576f87e2ff96ecbe1a5c7577a45c350616e91242b6c706c28b667fd572319a011433e3af987e49274df59a247180bee92fcbfeeb709895b1f54ab9a848afe6064b2250d2ff315b4c888b67b2c11a86ff06bc0e5d745ecf77a0c53aed7db2ad0b5b2ca888d58ae5638719cb1aa35750bff7432c1c11ca7a1e95ec38b1542362003ec670d064b9ee4a5a9e496cd348a460b90d5e0ce6ffa869f1d339aa1e834102627f7f717e187507f485fb0643e7773dbacb2a52c67de4be7a6fe4b58bf97207c3b4754ae9e61fe801223c53286a75e49995656bf51a2940284455de6572600cb3a7b6ad3e39c3db45d73177a299a7e3704080b3718f8f5ee8b32b0ca233110512ad2e528c005bc4c7f233fbb856cd1c50c5f73f8af18ff2d364f1c49367fb0fff8bfb0f8aae5c364001e2824f7150420d2e90bf0e3eea208d437c0f3f201c041f6de354b79b0dffb7bd2c1cfec8d9e32d1ce5717bdc1e56b50a1c18ac1b2a0a178f58d1bb3476ad8cfb53b20e4c740624815be1bc0b270f07e1c74553778103a2fd81b095332f59919ed8999fce4db09fff47b33f9cc4e405c618aeb1c8b005fe95327e6a8c6a4c66c0333bdb00d22f9200670ef3338a4976840c3ac16af00e8a618f4c8bc764082f06900f17db7f080f6bf1fb4c3afa8544ba0388a7b6db07309cadc469ab4842041633d607871d0b6326aeedc41ed0aa48fa30c013804102acef643f793853ea2a78639b0cf2171890ab302d3c72262ced7afee50983d1087ad5934419421baad2c89a5859bd5f6373818e6f4919105f21ea297f93275c03736a2533f810dbd13f2dd0a67a6ba656e9c90e44d20fb4c484ffbde850c3ab09545b811447060d5515b3758da7d243f95c4fc0604ce391b2439b8db29f1f5a02cf9a0e02ae4f872c094a8b72cf8c3e3e2a57944f01da389c0d14e8154645c5004240d197436e740d22813b4cb422709072aea35c132c81bb6d456c5e742ffa0ffb0b4d0d9654655f8a8ed2b27693c1bb1b1c05780f03f43c50338ddac7939d0e6b2f7cf91d99ea5e80a1a3ff1847dfc13ca9aac18b24d8a74a861e8cf7574a003f0234956e930f65118863ba3913cfa0e7666bd4d1c26b53c337e29ef4ac140c49718ac4b6e4400f9fc52242c465216b1b88d10b5d83b1b2aa121ad04591a70fc6391176c8e58723a771af7449ad4b0d63d4ee10347a9ffe5518cf923cc39001924c3508f08c27021cd66fb79d358f60f2981a73c1c1f5a8ba3a6bd9221d3b02a1faf9e0888c944439f9c75e48b853fb9cbd6b330a6fab2fbed399dab68b8002dbdade59023fedfe24c80dadfd8001c92e6c292d739338d73ed9609c41274d00b4a790b9f2dc42a4fa1f2b0c42996b55e6b569d73e83fd2cde1eacdc836ceb055197f8c839d5277b568ee0635881cf85c3b7128b609bf1bb6d3ae7078817f603bb4aac86261c700de72c35f76bfe35d8ba105a5d6c9a41c851531f4556ed8504d589fb5bf9493881333c8bd9a166d9bce2902113a4cb401ea7b8fbe41892e0000392a2a6c7a9f5b484b4b32b9fba7409dffc028cbe5150de62ced0b60325680f7cf35beee11900663a4e1be665d653a5db3317557870041d8ea24ded159b2304cf80f4fa1b837ca2eb1a23b99fc057a3826809a5a750f751d16a21fd8baec00238f83a7dee705e75a60546949dd68321cd96d2caeee8d0178f0b5a9048d9cb073b75a08e42bbd228a06ab9a953f31c1cda6ebd81773adfe731e1932a25269d0a1230b85a8011d6bef5e70f7ff924746999d77220d05d13273a226e5688e78d0f41b729cdf178ad7246da9b949fd698ec7df50eef003484fce5bfba48bb632c0d7f7ffd7174fff6a6f544f8d3943497414c090e59995c40b339fc28ef02104d0c8516c942e91be8353ec23def5460eb043818f66efb65d830d8fa7b2f3d441805bffd2c47976adadaa2f4ae69659be698e8d33aee5108e4cd368b9beaa49ad8062a96a2243cd4e34502be39cbb24742c355903789738ed9f289e104e515801807 true -check_ring_signature 07ccae3653d74d363fec4a0d597bb262027216043bf4d2673ad678b7bb3e6fdd 31d2deb7febfb87abe0e259440f7539fd756d011f35b45ab3c043ffdbbbd25c6 1 d7a77c4eb3859a41bc6674a799975bf76c1548296db82cb749b71e91314e817e ea26358916961b4233e82ba68ea496c9384ad1ca422566f614f32c49e35884065dbdd5dd82470a8b793314342204c05f1a308db2ea7e11d1812aaf0ae615900f false -check_ring_signature 010d78e8ad76b569fe3070bf47c352d35357c114c9faa3d10d2898d0149c54e2 481808cea990aa3546977404fdfcb5cf67d177e99992e7da967c0ce68b206ac9 2 ba2083f3ebd4c1795d99e2db5f1b592f4538675b05c74f630f704b5f527be1e4 e0795a8f6334966840f44ed84201b9d5360ec544a67ed784f1f1ed2736ebcc81 a9eff6ed8775057625541e711e5548d321728b8614a443293f55653d15275b224dfd64b0fdcc19cb10d7f4033c2b541830e8b25ff728fa21f310c455989bde04829c0c553c334219ef83024fc5a619f2f653cc3cdb6dd6e2ff94f32ac222f909afaa9846b77526a72b08a3b4eb3774c66c06a089403d7d76614411f79a50a33a false -check_ring_signature 63880b0f5dcd5e24080d29503b4ae6199742b5d0fcac50ab4a689093601515f0 729ea7f04eaf8bb898a242d372d9498ed2212ffedc504480474af38fab4a98c0 6 47f977acd250274ac18a0a52c9d5f47a8d242b330cb1baa084f99bc39a62254e 68520248a822c2c68123a77f9450ebc8e911c0eca4230cb5bace3549179d5ca6 bdca79fbb7751fd71edc170fa7debe75fc0a9c93e0b6412621890483c69fb069 94a47b621aac4f139e9cb58e9fda446c068d4da9af27597d3e855ad7ad93f040 fb4ed5b505c488ae144efc968ff40a6c6a96c415ada80733c1f427d66565d406 f3316af8eb3eb6bf45fb0c787ee513fc59cf5c9cbeeada16a9f543363a8d2305 f2f3d9f1bddab6ed55f88c1f2c030401a9003f1621ec33fb6350b933849788029a81ff3a9019ae7910d167470d92af9ff731d1715f696a73661cc4d28768ec07c5bf8292e5a5716aa3ede860f5fcde29b992bcc035fd8753a70762d4c9f65e07a1315e8a39b440f82cd77ce5446585de2afa37bf094697a5bba7f8ec8aa5ed0c3fbae2709905bcfc2d77e99ed81e8325edf8b7de5e90ac1cbc2b172bb8f9710683debb541f4c720d840fedba0c9520d68befe33b56620536a1e753580a537706019536a0c2e6fff1fec67600b6a21f77f07d94306192e975bda099f111d6240efb1a792e1c64712618e110bbacba19e6f453e8e792966e333b3079e8e2a2010cb92e885467e09bc1bc38715a784a292ed4e399f67fddd5b1175e06cec6b4c602efd0325a69b6bc386b0bd4720ee9b11486e1bc766cd7eab4ecf67ecfa34a2f02e2f5117dcefb0f6085fd51e23c54e9be952c8099c6b1d45a6441f89fc89fa0021e1d90894026b8195565a93b4028fbe08b5287bcc7a84a541f1d85cbfba8d20a false -check_ring_signature 56bfc7cc16cf261821aedc6393c80ee0d52c6d974814b6c8869cbc1c3eb04474 98bb5598bcf7fae7a3fe7611f9998db6d08bb0edc66d5ffc7b928a00eb191497 9 e63648593e55d7bd09fb9f5cb2fc61ccb75d65a94c7fd6af9cc1ae60e4420337 5bd5502b2b009277cb60e9d862cde723a4073106b7a9549ceb6c56d64de5668d fb57fe98887205ac18e74c5c927361ce842c78ea8cacc24aed0d6c811ef42828 513a52e72fb19e23eeeaa45ae6136295a685af94b8410e49f3cf7210193a415a df22b4d556aaa1d5b8e0e89db2c2ad46db035363dbff2a02cee39ee1bcf7dadf e748192c62ac37c5d9cbc6949eedb0ac2d184669f01c333bcb5b37927f62d4be df7bbe0650db811e878debc397ba5d5cdb7082a22d1e24fcf0beed163a1b332c 5b977f16774f7a6109eb5df53302f6b31ba8f968c20ff532cab23fa5609f3ad6 709c12448bc0d8895989872ec67827b97e360c0c0ce68cd665a1424eedf3a347 914dd5080de245799731d28f1ecbe8affbdfd1e0a9abd363bb057fca9bcc68021ad6147db44e6349eadf4c9f255e472402e225937722663d240544bb883fa60c68c818bd5e3f9ebb19803f14f1234c49ff7ededdecf9b73e24aeb89a1f083e0917e45fbdfb1c5deac93ba8e7b720f6d88af27e01b0336822167f097474b8100c4ac2d505afcc8d4cf48c7f616df7f2e8695063227aa0bf6bf683a1a6ee85d90ccd794a7f98f7e0ffbb88e2578725401c5ae6f268744d90799e9f35e8ff567602f0c32389e5470f59b486ba236b8085154aef5e873e6fa961206d74d1e9e2d90dcaeee648800e3ab11e224e717e928c7de3f834c99866b7ed435d224128d4a200cf08dc61f498b88dc44c77f52ef8a9edcbf8b09043f6274b8004a41e2c82950a1a47c4d305ddddcbafb809b8dfd221e07ed39fcf9d7e03b5d41cfd6a869f260651b210527720dc50386b8d223953bee4e749afe74c809afef592723057eb790fc9ced8c4116da926e7f6715204cbbe5206f805d31819de49bff68a6209979d06ff4ef950f749ee11d135dea55f78e22fab6eff502f7694f75f46b117991e21030e7e5e189cde86fe0d333d3ba53cc04b65fb6b86bbb0c2efb2ea140394754d03a4746fcf6b5c0b571498b31fcf8817fd417a37dac3b314c76d63c11eb1c4850b137314e58f3ea1f641de7f43495ee4e89e0963139613125b1945192540c00a056bb4e44734e25a0bea24a45b61620fafb06347ad30921b7a5acee61ef8c46c026e1cd694aa75bcb28eec4c56d4d36ee0520f74650426ed510239f077fd55a603 true -check_ring_signature 6a07069a48e95bb7bca1a4d605211c2ec79958c59e95e1d71124474efce2e6b0 2b5d5dcdf0353e223bfec0ae0b67563b95b09c903850e01964af8a5f4b8807cd 149 61a146730276adfce45cce3389e17871bf3510836f839913d913fa03f7029e88 47904dff5a940df579678576956add599a67982fa75a3245dd5c7b881a08f8fe 27a0a1bbb69caac70f8b6c4ef92c871fea01f3b38146128cdb40bd5b25cfba43 0be64d7bc41fecf980ade51d7d02d8adf78bf60bad905bc41eac56f300a1e064 dd7649b831b4c32fba162fc7b8746314cc6d22c78d7ebccbc2776190aea0e44e 3a9eb7b4f1df2adbe7330bff9efa00b7d45c9cbc836c2682a74ee4a3aa019255 439c580bbaceaf2ceab807072f9112ef63b2b93b29501b8986fdaa78b43fc56b 47257eb5aca100a95f51868af09d7d6efafbd5a0c90a90ac7c1443888feb2951 7d3026a0edca07808b228fb4ab0bafb09c461811e5055fccd8af54a580c31f72 a29e97854300580fd3ee19090723e92dba5c1f1b706602c98fb8e97d5dd127fd 790c7136d311ef13ce77e3b09e91835aeae4513baf7312427193c1c04e965c9d 43e6687582d4c437f396f37a7dfc0d437bc33ba6f0ba9c202d30a7bc60a645b3 1cc617a27e62849b260e5518f8f73c00a1aea24af1910231db3253cd226ad336 941a79f0348e16581d676e485871af54fd98731226afd07d8ae129f5bd25b625 769beb978dd24e59f8b1b0033c977fb0353ddb22da805ffd05b5877286be04ce 4e483eef65ec512a7bd4211d5104bc2638613ffe3a026cf34a3037bae2d7adb0 07923722db6cacdc13a14a9cce7eec6be5d339fbc233408a2d98751a8398b0f8 f2dc4808d136526e60c024135cc9a82b5f0c5e5dbfd767ac2d3d294b7ea6bc7e 723c154e20eca32c8e3dd065f6645c45bc22b8c03486f6dc8bf084133aebf02e f83959bf448a4973bc2e3288cd7dda2c59a42450e807027e90e97f3bb6750e75 80c3ad317e6cb2ffa477fee68b81c66ac66ba83aea6e56ca5c32f3979f2b6a41 821df0848e2665845cbdc5958ce8dec6b70df4af816ec43a4716366d155cc57e a5ac5ae457877a808c153dcd69e82e19749d8783d75471a2ae5aef2fd4bdc67d 774a7d1f8dd58f3bd40c6af5208e9a4a3ebf8795adb7e7e7c83a503eff33fad0 6d2489da99f28613832acabac60017702155741592674b6f37984cdfdb6da9b6 49d12d0d8ad7ff50675f3dffbb9a345a51afee100aeb7b2c3eabdd9e6fbfa579 2fe80089db3dce371ba1ac97bdb72db3c6b7c7903fafd0e881c63e8343956b23 0ac84be4f10d0c18212d0ba34ba7a0706b72e829313d5483c3e82b716876ec45 ddcaf7cf09c14666871cd3499de9b051f7077b57594c82a3b482e7a3f5fdbf74 e0aa4b20a6b8d3cbbb7a6d3d68e81e5734a4b28944d44de9a0c742becb57f1fd 11c95337888e330acbaf484e01afd76a63743ae947d6fe4f38698942111299f0 6917f9d796d9e781fcdbbee7a63805499fa8af41dd0977294a67f444179b3062 c1b752ffe20637f642267cdbdea4ae4d65690ee445b21fabc1e7a83d6477f905 02726a678f13c934fa8cf6612df517db4fec50cb2128a3cdeb65fb37cc1154b6 7e75297190cd7e482496a84e88d5cb06805b02cd37bfe867b7d335bc3ca66f73 ba0c3e06557dd3de92b18212bc0de3ed21b8831b2fd4e4953815058e0ba3590c 9598396e1e722b41f47322e83193cd63e6aa2443ed2b92a464a2d86914ee912d cd460bc9e523ea5b9592ea810b7836e3819a0cda756355664b8c178ce9968769 7804172eb0ea9989aa0496437b60417fe982d85944c7598cb4cde769320ed927 375668322a50190f3d6d999e89f61fe1f7514f5bbbbddcc6c2fc4b278b6d5b3b 8ec081d471bb8efe469f4e616af78f315064e973429c7ac1e23f29b352cbc7f3 5647ce24f571757c6bfff9e269ea97bf6269eec5617fbf5171893bc4dbb4b5f4 9cc575448aa61aa021fc875f05bf4a63a971b12e88b1f27e4517f3f344582cd0 fb69456d04c8ef0073736587e599c58fd54fad3f2d2533aa407476780f255f5c 3c9ed2c6e8202d7060897a60788abcff1487c9c997bcd0957c928b83481773ca 45cfcc629f420f4b0703ea2cb124bbcc1d652d9d1024af568360d1f73444d836 fd9d53e2c83dd4f540054932d3393eabfbff7609cc25b56b910f4029fbb2aa7a 5ccfc66f003bc174af26c8711a9f4e0af3e8e43cffa17ec493f6bc3098a026ae 9f66c20cce536ccb73233e6d1ecc91a94ee446414f404926d710d74c6ac54538 e70926601ec70520b35ec045fa6d61c82aa5e2b15ba121a4b2f1c827c1cba117 c8e6eed6a151d978c5d80207e3ede951e7df5d35966545be3779e4224cd0051a 9651aa506b83fd0f355b1581d8fdda3d2de85e627ffab3299f013e82c3fd2b95 9c648ae94c2e491a9daeaf3fe7bb13ae07c10ed5f3c8e6e0fc9fefc9c5e02bf5 e29837ba772408c61eb55dd9ec8b70ef149d20c9655a5e914ac8fbeebc740bd9 38e1e459c56400fcbc6f8682b9bf41cc5fc34ce98e9881f9c473da2f1615f73c 31ba1f1826746edd1d0c53ea60a7f81a259fd4309836c0a9fd17d26510a585b2 fc9b0fcff600c75aff30543d2e861d331f89c08259b5b8ef320d89128e49943d ddb8e518d736d058ca470d5aab0533f04171d5786afe381f73165f5f8530dcb9 b71a0f98880614a1fdee40faf57eb6f1e6e33ea3a1792944df86b71ba699f6aa 659151cf5510fa0ce1d1c740f46797ef226bf6488a8a336f0772844b1ae03cc2 6460f6fde9711a9cac4a6e4afbf7a8d5e7a9972b92b4df6a7f015e3cdd55590a 3f438e771f0d422debe8f0e6034deabe20b4df937abc5855e9fea62dc4c79904 4886758ccb2a387caee1512e9db8839ab42239a46a9d58dc187df6a44ccbae71 25efdbb2b406e0acfd5d6fe72c2d5405161e2e930cc4a59231eac969568b13ee bf658a664d20b48927b336d11cb0ee56fcca17278475eb2e146db3263cf61acb 8c2cec9a9eec588d378a6704ee6b1dad6223580616961af10360ab2b7e22e2d8 a57dea34f3fa11ea45d49af09d915e8025e82af02cbc3013147447ab15781c00 48bd8ec72f764a39a2036fa29190d065b8340c6d348a58cc37cbca425be9f9f9 411397be07715b662bac01fd61495d6c86627bcbc76ac5cdfca7d12a9a8626c4 5f52f702ef5dc2da7509360870a4e1e9e0fccb7bb470986b9cf417e883ef5b9b b3be2112f8862b9b235dec749e631cd6fc7ecc2c1ea4d4b17db621277bb91cb3 fa13fb08ec7a990b4747cccbc92548392a6ec17f9bc139a1248c340f1e80fc3c 3282a1080212b336e3231fc5f53ad25cfb1c0653feb8356dd2c5e52333da6b86 210716aee77fe1d973022a273cb9750218f83f8671b5be27656a09b80ec67c74 f610460f21ba246984a51b74bc0b37670f574d420ac3d3983aad5b019bd7515f 649ed5ef674799ce65811e8748b47c0e5bcd9849ab66a8e59280aa86e7be4ceb b44c6f2122e36b3c4bd7c3b83e6c34067ef1648f71795e28d7b73c7ee4c864c8 413692c39354a3d3f865254fc5e57f0be23f25f026ddff431965c8970e200add c2e3c1630784c72cfc3c3f4d2b5b9d65fbff26ea7212e8f3289e988f7d45bad4 fdff9d67cac3082a5f6da29dc4a0557d98e6d85d15ca5f77e81ed6dcfa5a9004 d23c1e79f5baf35a0c782bcc38a04ff5698adec3660773df2d400b2409340264 0d15ad9ec85461c5e2703af3f27421876de395d4ee5c219424547070ae0095b0 aa0d0e5bf00c2135f88dcac32857f52356421fee8ba3618fb00495b5eb1ae196 953e64a94331d80a4f47fe816c52c39023cdb82ae58d1e0cca7bc29341603b27 e477daf3f777cbf30b8313130a5e9181ddc98b5ae0055ac386c7685af7132e08 afc64aade3b87a842aff9a00e5e1ca5ea35bfff124ec6198e682a70a4643c92e 243ee3fc5d05f7bc6afa4c508a2f099f9756785a4e3631103f4af5bd4d219ca0 a2d5ba81352c1a80bd472226b50432227693b01f5db1e7140eae34c35138d0f2 fd17fdffa2891ff52488110bbe67b50382460a6880df0ea01399f317056750b7 3e5ac402e83fe3760507dca5d0d3e06bd34601ed3b2a3fe1817427e8311db21f ffb89ab0f2e28e1ea3d15eff7ad3bc9a6305066d4bb3679323d03054dbac2fdf 1af8eae8ccefeadc78173f5523908d06732c1b30f22c76d6fee203b1dc0ec166 55d0a98a18768c575bed55f410a9b7823bc9b3dfb8cf639ecfc7919f267a7585 e8af14ceaec7fbc25d340f743b4375b574bd025f5dc6a2bd5c28eee6bb9ffeea 12c0d4deff9aac0451dd32c08153dce879b39a215d39b8c8a660504f172f8a4b 8550c4b676250db4d157ae59b28e77caecee2a0d878bddc0f4633b63df156e6b bc386f7a58311ae3ad3a2c87c43defc24027a0df474a550cbfbe07720e31e341 a075f9abb1612536e597fff1ce8fe416572a4827cd971516a1f88572ab342875 1ad0c286b8b2e737e4abf601793a344c57ca35583f821b7e3387942c23475afb af3d99cc5e1ac3f0250f3eaf86fb6e8a8d8d3af19e1f0407c703a07e9788efb6 ddae7ae51e3533cd5939f2a6cc9bec6ae5fa4c81c6a2290b6737558ab5f0d589 64d503ad47b36ebc770c804a19b19b963913c4c08dd63e98973043ad8a0f5139 a54edb5db706c31b223695736f9b3b0d773b4f5bdbe850817f26c793051af48d 7e0fd6a9fbfcebee9a77ad9b18248ebc7aab17882d2250531c6287b2ce3c74d8 949ba6ce0a5681490220a876d639f3cc5b6b42a69c49bc5f19d4f8e06888cddf d2cb7a82e73fa5a336a3ae2d02474e0708ed03f928da1115d93b6551c564cbd2 75469743425a7e9ad4879d6270dd142746cbcd13a5cc18351aaddee91f87c7ba cfba6ed1e99665176e4630bd17c5812e2116835de2ce7178972310500a4217e9 72e3939462ec07bfd69a61c9b9cc35328101b3815070c74e4d49fc124d8d8fe4 e2d8c14ac52142e92c45dd6e920a1483c6239c421bb4a41c6d6badd34cdd728a 8c290466982984c5158ab7f6397a043e1f08072d82a44642cd922546aa0fb70c 9d49bcd19e0a4382b92cdeb1d9b60528ae63b544486690d371e94afb5bdb9072 cc6f8de4d4cfac2345a0439317364e16e1ddc1f40e85f5590405f31833beb554 91e0a00feac5fd61cf79b60a06fbbfe430765abf1b7092d94aa47f81066288fe f330ab959be24997c14563ac9e37290b1c244120f9a3bcecba4cf584ec669c75 2dca51c4d0340465b4a8b26d167a7e742ed36c391ff7dde80ea7948faf73e597 c5169915cc126deacb3bcfbf6aa7a016789eb508213f34e9ba0d6fd5a8a4bae4 db8c1c9c0124ca7099b75bce2c4a09f1926c05a478972d2da72381cbbc5abe0f 658bc638e6d5cd6cae16fad7685dc42b86bbc2eb9fc3156ab5af76b5bdcf4c37 b448310274dfdbb815422305d0b92424a9703661e0e70c72f27390549be12197 0a303937c31edb1a0ba58fe50dde92901855feded942cdf1b3e45d7209cb6b12 6929612721d2273af1a5060dc25e15461fbca895646b951fcac76f3a8e739b3e c0afc433077135261d70e8435fdb92a0a707a7474598a4fe1d2934022dbefe92 092303997c9ba9cb6c88b1859abd33a314b89cfc53b8d2857adc850c68cd8b3f 0683dccc2306616867b8080ff0c26ec0c4725ca6e10582e0e65576cc5e2a98e5 99a69cc31494f28f6d5ddf7431a139cc9786b2dfdabdb5342ed94ddea9433e54 74ae2a459a5c3fb4333d7b1d67412e83586a95c87befa029116759a84a88a8b2 d7fb799c5c1092bddf136d5beb7d637eb3fe20f55aa38b18c3eaa66de3ab6799 c33fd7fde9a9c86a744ec01b452552223075bb8e154e66c81655a02dcb2e8380 3a1faaa8445d9e3427617c92d748deaabbc2e519ebd05815fa895667e0bf00d2 2103e782187d4dcb0b48bc498d674ddf3b2fbb123f558e031f7b05482c1162a7 46d70c5a475cec230e57f01c1d4171d5c1f3c3953ad8f79aa260a572067a66a3 61382cb1ed2898ac41d7b32bea33c19ef43c6572088aba3ae83fe9bc4190c091 9d84ed3c2b906adfabf0c0fb3584add059c2586d1d3e91f06b09821f2da2328c 93075e448ab7ce47caa8dfb4adf9e0c8be452e3b3c44ef8de2a876f3dfe031bd 62962ff9d18b57559341d696612e8431f26103d10363ef9b7e234f45772c8b42 636b74638c34ae8dace85aee002e59bd99c0dbd071f729f463f9aaa13eec6cfc 49e4f6dc9f0a1a4e6247d30f5bf15157ea0f8cc03cc292020fe5450bf1e08d37 76e49d7d282f1f301c6054a764d72dfd9f4367a3d4c2dcd312906734952558a8 659a5a15e202ab7bb88794ae0c5d220cdefc29c72a9089f22d20800abead1c74 2dee3e5fdd2db5fe037c4036659fc911d2fda75412419f9090d139a9058f10f1 7fd079a4a97564c6de21bdb073d7737e74a4b1a5e62a42037b30f1827fc25af8 6ca9fcca65c4eaf796d232bb50e40f62e3f2701e982dc25eda98bba2d7a95709 5fc07b842ee6cfd08537b673d4f634064cf66cd9c316b3ab58daa7b794ed7aea 0216d9d6d4a706ce3a1778d4173827d0779a90181a9968a6e3a412305b0627e8 7832a7a2941bfd939289f8444206cf74f30cc2a5a4909d3887316b73aed31540 24a51d42aae7353dd9360eea854e4a0bcb30b431cbb47854a5418ce71c6cfc1f 5dc0aea28e02cc816d548116bb2a0d14d29479ff39bdec97d92209da3c159055 5ef4cccefd3fdb8185433596ffab68a844539c546744529e8c26ffa7b3b06832 fd05c125ed8476e6b74e856aedc943283a00d5d08e291aa1e979c80683db1c0d3b462b94fd3711a60333133285cd600a3d335108154fb0490eab2b09d80dae0c122633a28415dd753b6411f9016ee185d00be8fa1438152132b784e48a86b204c65392f007cbea6de3478f66cc2995d9e3d6c7ec1ff5589a9b3ed05e8454b207e698ad482ed65745ee0661aeaf9bb5283bb64599b5110abba87257cbe6f4530f01118a7c6490ccf662ec9c09bccc78e52b47da9fe097e22fc5b479d98458ad01b60e94a4e832c420fddb92a2fbcb26c45b3bacd6f7063a2c8436940a1650a703b7496c89df9735f8de2cdebadd023bd19ea67a05d2990b090228adb2a558bd0027d9835b478fe488637d3f7f2034d95ee2bc8e4f7a48d97f1ecd20e27c0ff40dd0bef3fd822bbe696d7d7965db553a0b9f031502ccc03a0fbf580086985aaa0222349df638643c776faeaff5488f4e2acd039e8772e220958de375b84ccf4e06ad80c5cef8786fa0daf52b8fee39d2e5aa37210ac01ee045f788fe8e68f0100fad48d62fb561aa9718902ab574ec1d0fa9fa53e8aabd6fe8aed53c50dab7230a5784e72140c7fac5e79e17c005d99dd65b6444fa802674fae7f6960db39f8a0ecfe72dd0d123adc82c0287fc8475289d1808c065b20ce2389863edd595fcd90a92825796e8fb85653a0534fe223454c060c39c1c0c81c4bacc00c0132e6fad09684297677c5ec17b81b4222e661210a9b1a3fd464c61baa257e95b1b359d25018ddd2aa3039ff10c4fdb7a72644f5ae05259c1d90f2a7a0d9b18f0c0903523060ba97744b0758506d3dc9d701694e304a42bee1d9ccc3e265f453d88cf160906d480b799d75b1e98401eef84dfedf9605e62797d1e876a0fd96f23d4447a050c1482478a9852cd3e90b729453cbb2f255b452a1aed23118259e3528331c9e802da34b598046bfabd53d67bc2db754b82cee1040b2ff8595328d1e96635eaa8035d3c780f2766b64cd56f2884178f23f8baefdf5b28c27af36ad02a3992e3330db790a2a25bcc587fec695ec6960a3048ffe0fbf7c154b53a44c6bec490895a0ef09e400b278a93c709020de8d61b1b5adeab7a814015c35f364e7c6f38909b0895f8774ff229069012501736f365b6bd94c65cda7868053f0037dad427f5780886c886a563f5ce965871dd49de73301455d55c80beeedce21b41dde1b220d909ea7876c8a6abd6cd6986ee633b06a6a70bed3446814dd4cd5b4f932b00de7d0539cfd46e42bd2648a75c47c19ba21b891a5c6b9d774b1ab9fa22559d2faa1c0171b72f182db64a9cde2cd9adcae8dc16d92bef27b0c7f3da03ac94660c78db0860c9a5cc19b931bd86b13a67faacbc6802a9a61d020cb02583506e8f31b29c07f021799a08bb62471c11a5bad368a1f340d57cb1ac4384f34521267bbdbff80ad922e8533fd8eceacff193c0a87f8f5b2e50e65cca3bbf91a93b7d0197fcf90e694f0e2575dfbf8f3818d66683d135c2526e6e2808afc6d8bb54564213458009906bd308503086e2c91dea79efc4eb7063bed9535908eb17f57b2bd48786d50b934abe9ea2be772968b39d7aa5ca5349e674a610357ef7cf3ce4e66756ccf70b84b02e855379d2113dfaf3d14bcf9f2afc0f08c1ff587f651bf95da919f99d0153465863615ec2e8bf6e00c4fd9f810896e9c4a70ee233c3a4239cb4a9d45a03085e38db486f54aa33a811bb49d71d3dccae1eff8af30140dce06b66f99d250c64921ed09ec89d9b741e61b72b7f4c345b7ee33938d642860d235833ba7fa9062f7c3c73f7b62036f2b9e6c913495b7744b730d61a390d1c89e9c896b123370b0698ef3bd50846773c3dc0eeda7859f986c1c39b0a0aed0691f38453360a430a91fc5cb652f1bd5272fdcae975dc2cb3c46cb68a56e4bcf8e5e9db5bcc8c79053554b0c6e9bacaec55d52c103dda8f5b48ebaf2f2cc76f57b1de6793132dfd024e2a30daf0cef1af3aa0ca24e6379f4c29b02a909674e4466dd113f089c2f505e320cc46d49ffa7e0980e14f0f48880079b783cdfa11510130dc728d78cf9307a7c36d75474304d5db7960e2e154145c24f37c5f047d590843888a43482be40caad70a05640aea4b1f969ba651f286cfe3a52308292febed5f5699ebc275b009593169f7dc220d0c38a2027af2df35ba809e784d93a5e44985fc5e34754da70775e329e0fca11e0ada9f074fc2483a379e17a02ca086d2ceb3057aba5efa9802f07474340e92fa9216197ce6b53ff2e41cad0abecb047c1ed29e5aab6237ef0bc5e0d7037aa747079ca33b14617720806cd43513e22c797351af960c2dbe0708c12c098833d76a7b243dd4874df6b3dc4ed2a258c3f73e105ac2b2d3f09ad10a8655876712ef06ec3af369d61743c6749b1c8b87250a2679870b0170842e510920a20bc5711b9e5a587756971e3193dba29ae4c72ae2f1a5d23ffb99bddeb207c117c2e9510685a4c1887737247c40879f7a8d9ec46de474dac1e15f1fb9a506b5ce9366b9763289495b600254246f8039aba25ec4867874afa8a3fc2a9ea709cecb3baffee498493743dc5061db476b4e0174be43abfd714ffbbbc4e002c007759cc03f344044c9e1d0353bf01aa7e5fe6d4ede2c13b5b33adf16d8606a180dab2acad640b0dc132d8b25675c1d6da725f451404eb4a9c1b25c9bcb92234506191f1b2bc358e3fce3308d6d5af101ec9752223b7e933beb5a9cc4a7091d49092fa500d19539744536a1493acd0d9b44a88502aeb4802dfe5a8215575e5fa808435d2ae7795eb20bcecfce667443121a8576f6bf5f989e268aaaf63fbd7b190dbdd69aea64e449fcb5e5ed9e93f2939ef4741e83daa2732e4e8ff33b629e670921776161206e81452205d442656dd6b415175d5091bb0421e53e565882e660035aaf65d6740fdbd9a35551bbad44bc0409bf2e40597896186e32e488faf872000a71f8123f662f55d7c9f709bf4b52229cfb12f6034acf5a21281d331253870fd3165fa473f6c2512121a06f617f79e0508e49c52a115332f56aacb1c5b03a03c37d72cb60959e45bd3dc6e020c8fcb87505f4d5a685159c83984a17e94f4b01ddb43de97efbd270ce144fee8d60a927b6e9dce140bd004ed54f3e7fa4ab07094c315f4baba91cf0eb6e97016c4f37d6879b28e0d1fa1a1107bca7d59c8f2e05a228ef317dcfefafd4cc3db81d516dce783ef083aaf7ee61c59c2c20611b010302e3659294af2414d03326dd8a221864684d5b51ff5c1f50fb0ad3b2b3a30509211f9f563f50799afb496238f4bfb020e0477ae779cfd3a8cb38872076142e04c1eeb08edd57c801b65573f0156048e86874ffe4b19b713e3dff6a9519e8e40cb117696b7b7f8facfbe4435dd2daaf0ef47b27278e10cdd1a21856912490740fb2731d403b0934da0631b1522b8b483b5f38ce0a718f7ab797e6bbdb132efb0e30e8ea196545624bc3005a41a2a8e314cc7a49718a4a250a212c795a315431045c14bfd0e6a2baa3491287ce4582f019503fc75fac263aeb864dcc23ab72310367b7dec20e33aa1d6d3a74033e8768d0057be98f7dafecadc7df43dc3ab6a3098ba341bd1ed707f4bfcc5e7bcff9498c44936e0acec08feb57db8c7d87a06c01f42fae201a75da42b6686bdf86cf976bc5732f3550450b33823b9a35904707080df0441ef7b7adba5f5c589254f0609489db3883983ef05d9946bf22a94ed300b19e061df3a7a1ef346a226bfd64053932a4fad49e38d57bcf8566ed81a1e2091c6383d1ac6d5f6adfc7265717b0f0fd60e408f09859831e41f5956f4d939e053f0a711d44fb9ee2937b7f1d1ac13932b6a9f5c8a6e5ae0a14f04321d23bc30ad22b02abe729724d7a898d91389e0f5fe718375125521665c942953d1492c4004add8541d2ced559eae215ceac4405279bffcd5d0209e2701a9961fff857df09de024f38f5b81bc5a7cf95eb3eabcf08e07d95148a1fab4abd62a24904a16609ee34b12160bf9ed25850898f19ba6bb367f3525ed0a52c5d3dcaa3c6534bd5081a37495e23051f3d6931373db374933db31849b7f166d5d4c736ab275e3bef0a0aba39c410d71fbab6c6a9cfe8b8e1389e628803fd92542aa2d6316c0d1758042a6e6aef73af984414237b23d7510615263d3311a2d4dfa0647bf889a874210248e8502dd792d5bc2f603f37d03546ac4a5ff7ac790bfb544b5aa3bc12a16d06bddbc6da03e51244f18d9b85c29688344b3a6c9ae59450b1a9ab287f7ecf4c0fb092ff0e3355bd81eb9ef6b4c8fc689e0d665c8e43c8b3f125d0ad698044a404da6ffeeef4c5ff436bac104192ce685ea3a0225ce8157d41ed6a4490be325a0a354cd36497185192b7f5a5dbccecf6d3dee0bd736d42ec42667e37090f2626006811d578857401eae94ad82e597de195b30e1a599df5131f5e03a03b2baa5f0ba795b7a767ab217bed3fb781ad159342d6f25255578d0352cf11d1980101430d8e37e47393c49878524b12960cf5585769c13b74695e2a6d59d999b3fb861108604e0b0a798cfa1328afbdbb81ff96a826a4d7aa67650fe09d364e65c8ae680e36d0ba8ce8161af53dfcfde4fae3b8cc661d0a92422cd2f082038fc1bc61ac0aca95d08369f5afb656d3f0ac71e194f3b6cf4d4581efc2bd7edd24a49c33bb02b8c0b33736c15e04a26bad09f494de168a17258a2bdeb6da6ff2ce0fd511430ef15c49df92c4493d280ee889c47138846a10fe31542c2153dada6dec8c161e07cf8bda2c3d049ef419a605c3dd2998ec419321a0d20b0ae917506e98e60276056e2fd9d12d69ce08f8b07de345874818dc81afe0af3ae443a47e8a4c260c7d0fd2fdaf1daae56186defad7f5402b27f200b369d08caa1a5cfa4299d01b577b0014a65b06ffbab29e5c36f53555cffcd15d929c5434a3bbfc0d4655226d0ae10b2d88bf7130db749c1a48037302e2abb6408c3353e6ff8373ac52c3e4709df206bc7c5ed5be0eaec6cc9ad7e7d451d4a65db2ed24e5323c946f2703da47cf160c5eb2aea73bd0984ada3d9961d0c9867d2b9c0a8038f6d5271a2e851a92c29b0f6755b098fcb57e84f6cbfc08a21a31ab92e20fee73f434e0f21fa5e3e454a40389863d90c22968b9e24a732a00f033a32c48bbcb2c79e008ecbc4449da5514079e27abfbe38ea65b75bbc6614192557660bb4f49956def4304e22d050aa6a000001dada9d7e884c6506be9f6d744d0f443368fbd4d9b735d5725726464277b0520fb6022fc49cd609789a21055ff846461b37e80f33325ae23d4882012ac8600c8970cb97b58b7a9371fea7c6cd25bfaeed1c55926c264293566e0aceafabe07cd071f974d9fa8df9def77f6ad19e9d3adc3cdbb2d3c9ca3569c857d2f1c610baf8aea90c474c7587d629e44b602f3ad1455a79159756f7d03dfefd09e4bc80e31761d8ce9420cadfcc3136247937368dc35fc46a57f77a2a8634fb7b5891b04c9241724af9e01873ec454793731c1b054c516999995b03db5fc9648a8e6760c55dad7cfbd1f482402cb9972f3bb03207febda378decf8e755dc744f0ddc720cfd1048ec00dd39cc61d4effc6e636bfa57134da7aa295956b156b1bb72d97f01486b99ea38e4e9b39010a0680370c83460108a293f66531e93cc57b51ea889028e92b715efb951ffdbd592fc71f9821596c2ca8bcf6098adfeb5fb6835dc1d05ed4ce262c85f4afcbaa355e28aa8f1171c8fed847cac618305a0f3adc043da0ddd1c46ee1c5b2d5a733a280f0514a78c3c57e53789bd7db5f00b0d2be95cae01a4571cbc0df1fea8c7c81cd4af583def48f98d496353557b11463de60e4d3e0516a024a81e4a243d98846ba8733d7d806f0700937118d3f3c7a0b5f664b9b30f3ae58dc43b320c2c91709f97b5ca48ab45be2d825ce557322044b45738f5af06dddd83087e13bf1c2ed771e8f026f7dea486ef2754d1b5abdcbb1f9c7343800128155d0cf1c8f576646a89eb740be8fb5a6bcc15c49b9e275d2d52b6ca0b970ac13085de6e5e40fcf9dd5fa88c4ee4632a14dcfa6baef863ce806d3ce855f609b9534518cd85b0adc6cd8ef7ea5223ca97440a80a42c74007a582ffca17cb70be269d20285f2bacad848403a1bd2d8e9affc0bdd4f1ec691063f7a5cef1f640c436a276f256ce79b1f628349bb966d7ff3669604c454cc340e9ada8e2fd36b04faa6e783b3b97e7e5441d7f1719736d57a3cc3706fb6c033af4c3ec9ffcb4a0f5882dff18d21ba5812025f195f969857739c71739f8f3e43c65661b8d5cac80d46073f8e9ea07e4794a86f3cb309e3e93789247f01b4d40604dc395eb021f30be43bc5628dd0ba688553389123315097cc5cd11f4e9e3493a879e70b255464099525312bb33be494b460fcedc0ce048d94b2ce3c938ac727f447cc8effc06402687d24c0095c98e2bac22de6d4c8bb348bebb3e6dc88a4ed904edc8b3109b407f53ae93bf42d05fb7dc577dcdcda5932100b85449fa2a7e8467210f3f658ab0cbd264963aa93e14d842e7bff3e86d591466d82f4c04c00f30b9dfb210db52901d0cbf42e320130e9a324d206b946c53179e26ef960bb269740c31d0524024b0771cedac2be19372f7fa8de573a309d2d86f931005fe2cd021ce41508e19e2e0ddce01d34da8013960c2204982d5619f73f71419c7fd3c6fbbf258d91298117099d4ee37e91487e2ce0fca8b16d9b3263f1697d8a55132c6da4c13c57914baf065404cace3c31583968345f614e99290c21e575079a46a78926e748793130d405b94fde639bd511dd6843e5344f5aa0fb8d2236ea1739a05f0a69e05b195c7c0cdf56e67a5babbe5f4298632075aba6855f2ea0ac36c358be3ffdefeb1ca7ab020b2c911eb9a2c82ed2455a1685c8d6d0f5148cfc14a918129cb251bc88bfde0bce669aabf1e719c9cf7f25c6b88a87f3873e0a8cb8e54125f6343fc416d2a1062db017f68dba1ac659611a168506f9431325a95dfa6754c1130ecb583be52d060fbaf05e76c69a831b5942ff2079a98e954020d884ae50924bff77ed4dfccc08ef56d53e19accac753aed8a66b50038c2006a25df221cf0a6a3b9fd6f7b0f601d457afd07dc03bbc9a9b3e631d0ae6e88f8caddcdab0e5c647f70332d8cb5a06bb4a147435f3058d5e8c53ab6589c78b985b6c8f99a37519b61e979cb2ef4f0a7a0be971c476f1abb59c1709dc71a69abe55c07db9d1f201f87e6128dad22c0573ed5ba73d47708148af51b2cb3cb8367b91578c98ca922001095a8a8f782203c86d160021eaa6daed4363b3957b33c5ad4f9e1a0ecea8f18843e02c67762e030061cf315a773b3ada0be95be43148af525dae54a2e9fc2ac6c83b3850052e0288e2c98d744615e792e93aa44500722bc093b7754d530031590faaac9715000f996d219304dda0f7a0b86e8dbfea9e205463f1374b0bc904ad45d223626c470589791a146601bf74b0adc2335eb07cd7275ea312fed4135c63a0ef309ae05c0c64a182879a9ef44d733786585f09ad12d3e1b217d70b122f6e537a55269e7f00766eae322bb7ef569acbff61783f71e8693a53452f70a5bf37e598812cbd590547998d33a039070f5593ea1160022627edbeda6f7f2cf1d4311b258038d811022715afbeb839093cbf77ff2f90bf8bf8eb84ce7b4fb9d6780d6a6e0531c62d0abe95975e1d916c96ea1a505630a9c1efbf1bc57a35d3327cf8a8dd3bec48670bf85fafc3c75d88c8df3e85eba4c1f0d6bfec9852ee64262cd326b4b9a6ab8c06b3b821836875a70f08b51af8d35652f92813ebeb17093e168f213a1782d2ab0a6d61a41de240821089f0c9d2cff7372e25ec53b6038eb32ab6ea62e41c2b2100c3447d4a55f161ba9e412c71ceb226bdbe8a2b5dca99f5ecb6d058669cbb220a9aa18f820845a79a3e51487f226172c22dd4d7adc282396131d7d610da57360e3770ecacf5c479b3f6f64f153b9c89e4cd65b1246baab29ff6772ef0c0590a04ffcac6e8a39acbc2db1b8b539fc358463eec3dba06caa7a55ae6dc4f434b9c0b6fe6860ecf6776066f2af8204e957355bd352302a287862323bd90a75e68f702308daa3b6abe9a1fb57a24680b8f2131f1f9839dc435871fb7f660a3da4fff0f11b05fa16a3883593ba34ead720d102d82f0e3965811252f55ab858acb81240139d0cf09bdf536353dd9f60d793a70fe32cec0129c700f6961b39be259b7f509602fdae60c59903bc4c9549cd7225bd8c4b20703992846e6b8ca04870238d803bb660f376b52824bc299fa60e04d204ab645413bfa1c691deed96c5dd4432003ccd49ad85fa0b6bbf2210d3f083125e8c8493650f653acd906b5cacb1aa7160bfa7797cd76221e6f99b843d735f79df715177638821ee04aa4859d4a38711408b5700368b6e824a116ffbc630526de87982b0e5eb0a28cc2f3dff69ce85b0102bf85439eb616b1db5057f743dbd145f29f84017b372b145e3b8464fe6e5e2a0f1bb9819afe756cd948e39ad849ad743e26fc16bb8ff29e6dba8eaf901395b90eb01bc2f6fddfc5c918bb4f52737151f66e10be96e63de0a22dadf21b90e3b601160c609790bc4e88b4bffd852494af7b684f3f55c42864d9ea27b5375c74940482982467530041e820b1226a29568a00dfa3c4e110ef1c889bc8c24500c1c30eaa72a974c761d84c2124d1134e3bfafe4d88d8d9da85303143915d807e9210079de3e3df6e5f3a2ca0554710d64bfae2c98eb66dda9f2d968c32a4aa81ab7a0ab63e56617f78d867a67ad62c8623b92958d158f24302c1a3e54e8d23a22f890cd812dbab1907713901be97e3236eaf4f082a2ddf183fbb19440a177ec7fa28023c70dc4811808c96d9650f8e1feab3abff6f2946729f27a6a91bc560ecff33016f96cc33e0a73944a4cf86f2d55dfa6441e099f0fe82861592b495d75bd0930bc7cbd02173b58c0e82c28073c40e1e12bf37b82b2a583622f834bda44b3c7e0206f560e2ad588fe6b8bee24b680bf77640a92a088d4be6ab372bfc77ce8fe904846b36178609131e7bace7e9432e2f7fa71bca8c0f3d05015bc237c05c2b5c0696e04e07cebfbd594ffb760590228261bf90c02344e912c286e9a265d80cc9058427936a65aa00a76d9f5fe71b5a6c548fb557f9f0d2f379b2ba586dea99c00d2ec12db2033d472738885212d0a8e042761dd47549abe0902104e9f087dae507a9675b24983007bee7a00a8d8b972b6db526c0c84452544ff1080b852f3d93003d1b3a9531155d198e58b08eac13dfb8882bfa01175f8f265994150438ce7c0323a75944f2c09eb1c4203f8af4aee4ce09bd1d8ecc7beee1a08223437d2fd204c6f9e2239c2a48dab83602ecb5ff39a05ffdc68779ffeefddce33b0fcd552a03be0bc3d09fa00d11bcaaacf0fe505dd291bbff2ac9ad7fb4cd368529601b3d0508b4f7bb4645093ab8a7dcee05a80a10c71dc4718e603e3f8ae870cfc3d9590dbe03c3a8bce534f7a4f14a7fd17edb72204fd316db10d414f2c709465730fb0302cd8ced7eae7eb0c8fe11acd05de8ab31ce9d4b6d166604ab94ea64f485680adf59b3666e879705f73f258c18afe762bd0d8282291d6776d827d25a2f670d05dbe31a8ff0e1b84b4b417b514aff1e5d0f7d3fc9f17bc12217896a5d9d9372046d00975802c471fee14832dfbab71cd5e59d9e9b3ec3265c6b1256c2166e1606f3a0052534ab85e725f76c48c1eb5eeb1e9604ac993d5f8a4a34806a639193073f94a0f038c064467053e8efcb1566cd7dec159d4a6b425827f05f3a6723b40c53fc869dfb25463c7fa35e1fa0f837d1ab4c3cef5844c59cf4ad41382856a4086b4cbbd6b9373e42df03ba3ad7bc00191c1ae094c7b4c3bd47b291ed05c7db0beae248ac11b10b7d5f5053a4c2d11c0b392c9a6e2ccfd622a46f78fd66695706148c0ad1ad13722d5871fc0af60a6c5605818bb755dd622fd4b8938532359108b99e7f1c32d9e883d04994a608525a29fc4844b329dc3290f7db19d9be17ed0e8254b0ee1b10615fe5de6553b707638d3c8a79407ad9e1ce0243d58310c6580a8e6b0f14450ea21ddb68090c4e010366dee777f87aef5fb34ca63cb12531900ca5223bf755866ce93837c924a5f91a99e9f682dd62138f6493dd6c293a22b90f3a46079f1bd44e249890e18c0ccb496ee974a603262dc6badb9adc794b1bf40742cbe1448f543a9199abadcfc4fa657fe6acd61d9056911c0d73d28d13c9de0f5dc45520d94e0ec63f946a8c2647a3cde981dba9884e4c0aaa9b62776504f309518bd5f4231c5c4075baed1d0d9bc3bcb7ddbf0143eed35728c89e8131bc680eb12bd95edd333dfc9d841b92066a46f56f7e2ac890aa120a01964585b6bafb074879474392e81980b0389c3634afe69cac257d261d90ea48ea280c39e4ff630847ced01da03030e7b9138f1f9f48a59ac867f7b4fefa82e227b8248f4d5dd20aada5553edc9d162ec4f852ce811bf727f7b1533a90bf60d5389aaac830c65c068d99f8a93d31afba8476f02a9e00e6ed21a5ead797008410bcb4db199b7e430cdd65da2a838d26c26f044208b2b25ba932d10b05a6096a20c8612e6ba55a200bb3442af362707021b08d9e90e4b83ace871e8a93380afdb7aa82f6e7c19d4f0b45e3670309e972e4e7773a153ea1b54d181e9095ca544518b527efe8e8c3d1022a4feaf27d87719b0421ff0f429a7fe371d324f61f7f45d30310ac797598160525c7582141e904008a44fb8c3645fe5d845ee45b733c5555f713feb7e6bbdb0f8c721b8b6fceefd4c1b81bbfc1b4052790bf561e09c8da2e49de6b6ad4960704fabe3c9e4b5d2a7911d972c7c28b3a802a9aebbb772e6c50db181c695f4fdd0d9f641576f529d3fab04cc52e5da173fa1612f9959b237eea697cd2140e74490a73cddf970d5027d141edc42eb5df1fc202746bffb1b84b5757ef88b76e0d9f08b932cd83ba43d2f0277b7c24501f6d9754004bbca6f8b0ee903c0caf7969110438c22cd1fa4d566dc9197533a5629679c6be61ddd856f5522cb5ec6c5978df0e39499befb5baaacd9e94ef157289d172a77a66797a4abaf954b63e70da6ad50bdf0e15467eba478fbaec8211573ef1c08f1441c47c24ae88395c97326185da035977f45759a915b26fb4b0f2f7bb01b4a6d575c7b0abd956fd032994ddef8d054f26079c61f0e69ff4443c081161b2a43221a07bcb000c51214f607d3325b30b9b836bbb8ff2ed33150a500ea61596a37b393c39e3bfb5c9317ae954dcf4600dbcd28d8d28b8faa36b6baa7282bba577f5cd5d46588456b167caace90c6a8605ffbb95dddbc67e57a71a0fcd6aff5384d04628b8dfeb6128959e3e3dde671e0ee8aafa9c1093c9a6d139014de38793c2fcba16e9d1aabdfc9e215e60114c520b7d29d0ec46cb01918b03f4db10a918158bda2c91c54a037ac24e058fff88b5064ec3e51d6942131b2f4b421b839e117492c1d388a5bfc6aa4ed29c5ef01b9e0b7593ae5aec9637b7b7d22d5534432c5aa8c5c2d111330f91844eb386441ca20d66b8ba2236cdd425c4934b0333df2d7a0f0a7917782a27ea94fe6fb20810b6031abd57d4cd1628a4ba772fc97fa17c8a6232976bb2357f2413048b2f6411bb06554c03d1c9a48e4ce57a18b930ae2d2d713ac1a81d770d0f75f295637701f30784738b02ba87a434918d337a0a838108bd918f41484be28bda9f2b53943842099f8d9cfb91346e11270b5a5a2f42f53a6c7afe44546ddab2f2f1691c074b170443e2f58a3f5358fedd8ad310b2988bafc4233b10a3369a72aabe88917cf5a20b525857d6fd937496f5941391d0f46a5c2ed04e7b14501d5b955a527a7222de03ae6d3c950e08f1fafa4408bac96ecb979552e29e2dafcc647fd0c5d3eba38c0b634f5ad02cc147f7c252a7f8bdfb0c2aedd1b4ee74b0bf38e77ae0283936460e2ce42d303fe17571df350edc269c584eb83d4927290e8d759131c20650d7a90583480a5dacb656104363d0f99c653fb96054a68b7cfcb1c67fe8e44bcffe6009a61aab80210852eba0dbe6adf4e6736a81ea9525f98ee4f72dd2a9b4bcae4902bbac213d51d83f82a7dc5ec4dfc68935f34aa58e6a19bbcf79a2603f7e51a607a658ea9d9b146da11b71c3a8d8702bc2ad6c3756ef2728a724528b18b4783701ea17532b491c18af0e9f8c191cc6f248e8aec59076b77c31411a6ddddf87110e113bb1ee1efd72ffea1a33b90f87273dbf5f7b371543c3aebdcfc51c5e16130f69d6b9f9c302447a521299863b01068cff09a1ad4206488f11f4b7d3fd240705e18a6cd88e4be69d1bf2adba70dd5aca1f43bb5e5b8b91d87510f6f40c5b57028d2da59cec656bf3bd978a5f61146d3c3f1a1c74e9ee83a2a12c6b9385d6680bca286534ab9f24a0aaec60869c7dacdae2d4dbb061b8eac41789caa9c738a108b8b40d9acdbe13adb5e3ff3bad44ac3ca50854be9f7cced9f050abaf3f6fbc035e799b7dc1966bd626f47d8b3ab52b192e6adadc210b3a71e6e8385da557f803b3ceb065e2da4ddd97b116e06c31cd5d14811caffa9cbb38138f94fa47459b01dc8a68617a9f198d64ad016fa0e0b3a7637787c49483b66741874ee71814c00fc50aada675245f061e6fa5f9d452a163dd70415472b5da8289cae54ea74ef404acf69f17bd9a5e31a3f255824bad3926e64441b45739310e67738b04bfd25608a193c018568657ae8932f726d178290e07c6693cf1eacbef9e190778e19892070712362148b9bf4b38dd87b42324d50987b1256b08ab9c8afbd690e3b80947023e9791f53b7d0d4a99ce7e2250a364f9296b1f363078904d24df814ec2b4ff0bf12a368567b74d1c27e912eec1568a4f8831f9204e8de820f8dacb1900743400669e70e757937c4eb2e5d35a5bd2e6e71b8257b91dae80514b5930080c38ba0639ba6981cb5b6829da79a739e1b109881c83e063502cbb77506fcd39561fcc074b68078e1bb732a22e0ff27eeb14b1a699b26e34acb671ef4a8d98e2c3b28205ffe70e37a42f9a498f8a12fd4516399b2600fb6d07e75d51994bc7e5bc9df60d2a09f656b872fe916eaf883314fa5614f7dc2bd868ad8ff133ce227f7e27650c194640ff4033e422fd1febeb183d494bc2c305fc6955e3fd16100fc2ef849603640d07284c4a6d3bc6e3d3a6be53c343a5182346cc58866228a749d1536e1402892af294bd5f5ef746ae4bb085be2a2e87dc96e949d79acff1c318604dc1070ef53b0768ed53c5c2df05555fe06de870d2a2bc86417a3d8b89c4d27576573004c3166f6cef11f82c80f199b7cb10a598fa1cf50a5d1c09a37c72a0a5a1312607ab736b66ef881c07ee357855efd5c856c9480a8bc0fea7463134ad7841171a06 true -check_ring_signature c59f518322102572d1028478f5bd4cc573b817bfdf9f94d0b7bbc665ea01812f 4d11c84cdcb3d7dd2c6d827615bf405327f6a325291be8dec6e3eadba05f8301 9 1f9bfeae485428d9a1bd41e1d3cef39213ad303d0c4fa5c5b446366ec4811a55 99ee6b67eca0a7e2195d5a0162512d53d45db35428e489fb5eebb3abd08da8a5 70eb7f34cdf3f68ff49dd2c4877f95b1024c5d18da8f69a40a224ef38400b727 3f4d1982531fe9fe1f9f6fc38dc46d791a269e474966c1ae07d573faac9ac11b 9b946bd0c2c8188c2dab5e6ebe6f443b2c02163ae8c4477ef1165662ddcc6280 42034fa43b620d6d0fcf8c6019a7104a59c06706fa82170082120d12bc9a1390 d5ff6ce764f0843ea8e4a420327f248652efb708f1847b22ad00cd2e3073bdde 3137229a498227d3a116bbc05bbc8b0b6c8320846b5e6c61bc3b28060413f979 7962d1dc9bb2835f7a58ead75ed835eee29d122c3674a2ee48905bfe60423b49 8b2b603a86c86de84cf6e7c87e81913200252758fb080fb1351134d627ea7f0a69185cd21b0bea05d84ce8d6b012e3da40a7f748af7a6c40400a77cc4c6cda029f7a03d20068e376ad21381f981f96c4e5239eed10fd28e488ab8d96b868d8000ac18c09af3294aae8945dbdc48700f101f96ab032bef1f46ba57d744033ff034fc891b0b58e38d0989d7da97bd270f08c8a4514da703fb0cf0fc8dd9e1c2b00fb3a1b7658ad9d4a5306db6af062864639eb756c00f0d8a2d3ef53ddd9e1ce01f31c27d3e199b596d79b1371614bb8691841fe701acec50f839909c5a4c10800030c5082a23b75d5e31b8f72eb7238d5c77856de40efa74ef384ad731bd5b80e7151a3145e0c4102d07a35669b5cc8c44cb4939b7fed207ea430113a71626d063215d065241a2a62fba12e79eea46a2a99b815035e1df2db809e0284cbd79f0c9985d8c9d52c2be9305d261dc2320bf6aebd5efe741144fd60c32e7bb21e150697e338f73c72533ae80da03b7d009328a216221a1cecda4bff0c42069120eb0301d7258a0d93beed36da95118d361dd4d80c5425eb106159a37b803291cdf00143570f3a63ada84e7f7bca5d5d70574bd09508af73d70048140f652a5c859e0b28b4c354e1402bf65d0e32f1ae13678d71d3ed62800b9cd284b32c029cb94d0cce477bac3b6c0506668b40949169946b75a7d6fd811c5a7737103a31facc2307a2fe739d8d670343cf538a694ea61b47b01b7e62030fc060afa28e8fe10273084c551e84c297ac371cc2062c560576e5d71b9a6e3aa02c4b05a5844bf6a61f02 false -check_ring_signature e287af7fd46e917d98aafc65ed0e10056ea354d9bf2f75cb812840e7c5d2f7fd cc166532968de6985a278feb15fabf0c60835fc0a1f6b172f136cb9d0a356720 2 76c77d913d248f51c4c7cd59c1ea2f811df57fb882e702487389027f9f6695cd 152229d5ff1e5cbcec04c362444e11779d06e5fbd959af3c083384868036522a 6fa6ac0994de0346c7da4898d9caeb448b6769cbea5a3c1268884957debd0d7ef334102e6de805542588e562e91a83b553856e7706d755c8c75b03a6cbbc17cd7f9e87f868750a01c7698ab3e42f3680c0fb6f690a170aaf7d322c1b73ca8ea1803d26095427dce2fccd359a809dc41a95585ee1111607b804445412ee41129e false -check_ring_signature e900797f25b4c75d78ac80b3a5daf0f7c860e8c112f6802ec9e5ab241a9e34a0 0fa8872a015b105d31d43e7824681b300271aa97df1373c7faffe735eabd7fc4 17 0bff97d10ff550ac824e8919f73bb1e7de5dae97f954902fbc13f07e2def6520 4c915b4681a663abb74e701ced0cb4eacb82ec970cae4dd957c177c63f25efbe 7cfaacbfe04a6aaa53e30c090b40845d526b821c553162ea044a465d52b58009 518207184a117714fd76765ca1261923a2c15924ecb7110946b411351ea59ac8 5dd1cdc779fd26425c5b7a3514907ee5dba7f4a13ffbfec74c4a27cc63684586 ab5a0f76fd138f410011c47fb720a3e077071e416db0334b2748ff71a73ff241 b8ded53afbae36905eacd761d8ccac6703b5efc79d6ed80030768d4ded15482f 139c38aa303cceafcf9b6a88b466e4eb75c703cf0b5e944e948f1128d934ab6f 5aeb61fa9f96ca5f534ee2dcafba57ed1682e9575ae42ed1a6738fd08662fe2b e33b768494748d637ed67b09b6dd0fd298e789e5f19d63329412b6e5b62ddfc1 2358e2edbeae1a0fcd81c3975d751fd9789701d179fbabad51b54115baf40b6b 4339f4ef70eef44c1d43357ca5368645d09004d554f73dae1ebd98c84c4cac30 cc78e952a9eebb6b8b73d2d6d7bc74d22cd36453ad093f0568de77924bc646e9 2ee1b18a62eb0eac1fd0c35034c82a40efa342aa3703aafe1edffacf0d37953f e21ec9b038e7a09b34957174c3551f689eb601bf7920d49a71337df6017d5f7d 8bcbde2dc9ec8ba6bf96c46d0d77df9521320176b8360b2b22bc3d98b7f5c9aa a3692fdd666bba4adced26b8edee579f249f00c0c2668e407ef937170979ef37 524f1a319ef49987f057929b32d34132b9ff66108d026d14271531fcbd72ea08c7df64ff038f541cc45263f093a1869ec815cb3ff8301bcbb3b8d5fe76523d0fea4c37ce6f469169f43fdec2169e301d7f8635337a45785d3c192c7d6ba28003c31542f6df0003b09cce74857161bd262cb68ae52176c129b7d876ad6a3e41065c96dfd04cb78c708f1bc4836962dad4ccefafb62a6585912477a314272c4501715644ba483bbc2d94e577a1a43e94048b7a056a3cc390b7bb956e1bcd1fd3034187246f942fd26cdeeab69bb3f0679c123c6219b66c9affd461619a4f1c1e0b0a8f63a0d5d5868516cfc807fcb644547daafd3f2aaf97caadeed14e3dfed60b7213f7c1552599d03bdbc357db7935073f2adda79e2edd5c853a6c8af8173409aacb111738aff72fca1c891933993cfefbd8bf856a919aa9ec4a3b15ceac0e034f7375581f680fae522ad06ddedfc0980385e84c71e89e0f59f0f1ec76eda806bb9a6b19a3d36cbbd509753f7aebcc8a96ace3ee638d7b57393e14e391a40802cb846a34fbd56c66f7e088ad8a0596f4f04a1b90a2422a9a97e433dcb563af0f26d9ba15dc5d4d48f3956bf583a53aacc1c4bccd3da0cf1a6d583fd24fb10803e37b29523b692859395f0061f262fb205717a1339a5f67b7ade0152ce407710e4a6cf7f36e9846ea57d4cf6ad7dba0ef4da84cb10bc40fbde4beee8f7c093b071f55b2ce293a9f3bd9d5779bebe83cceb6d3a86763e798587a5a2202f756bb0b9b1a3e456fb3121c10064ca515db70a08ad7c3998a750adf4cb093eda89b36049bb8f4502bce6b8c434f4e393b9bd1446fa5ecc047072e55fb9903cf834f6b0ace2bc95ee62e7e818ec7980ad50d0b2ca1897404e73fb1a0ac2d8abfa8193b048bbdcb672826d20de0535344c3aee9e185200610e9f5084a723f5a28a200270f4159ea4414825b82b30e591269655c57a3ff5e0c2f9aafe3c8c6a8251492020db4790de96cc0dcaacad4a3f4b6de8e406204305cd341a28d45cdac93d2cb360478bd5766ab6c8ce17d7457d45c2d0dcdfa9c71df1bc6d0e353c3cefd3a5ef90739937daf55df8174ac74cd34528ad70c2c4f698b3f734872adcdb9b712340b0ef905dbc391db1140dd0cdf3554547413e74685871554a7bd32e22164f5786a0b19579f22de991a0c9c9704ef38078242d5bc75e72a851801e248f7250e118e0ba559cb83bb6243116c3196965cb91ace3c1ead0b1c7e36eb0153b861598aaf0eccab4a8fa8906b4dd41d84876f0ab1060eb19f58c4f7f53404eded323a2eac0a1785b4a46dd7259919ffb65e29c4bb9ac9bb0a1b6fca021fec629e77b705860ad4f272e16ec7d281c9e7c91139c3542f5675d372f070973f93ed37cecf71820e00d9e6c1e2a6120ec37fc920011039031ff190286f0e9cd3133a3baaab59830df2cdd7ad0a0fc1fea2cd3b7b83c30a30de3a3b2a044ef0f463de26b282f81b0ec480eaf3cd4d120dc83331cafb77d8b05fe1e7f9c38e632e93dce54908a93b04 true -check_ring_signature a3b3d1d8500ef306fc2bdac3005f0d3fab5bd655d3b30d4083dfb51826f0ab77 b864a820679bc69c9abc53f41e185a96b0d9abc63bf4b914063ccab5e883ae34 30 6c3f54b8a67341974efc6d4a644fb6e6a622e7ff0402919af19cf28b08a9bfea 24f320013bead0beb5fdbec24b9c3e3caed9bc98f080d286af0daf1d7591e6b1 1480eca360d32bfd61bf26bedaae60adf93d0f2288d7012f8966e36d61a9a196 7b11cf67f8fb677046db8faeb0c556a13523d9291cb27eff779fdc59e977032f f9ce9656c9cd21e514329ba6b3fb9606d77b19890a8cc69ae76895443025182a 6ccce3e8202e1d907af01fb9ac25bcb9e85e57c79d10c1910f16b395b7b4109c 6b807a126cb4c90004e8605d15baf6bbfab80fe73b01b44dd10c414da3adcf23 f9e220ae3bdbb30faa05306d37204ca5e27312c8e1f5cfb15c0be3b764075bb9 3f2046154f98077667ffb8831b838432b6ab25068fd9c0cad3038cc019e46623 fc1e214f2fc6dc8a007fcf56ae9ed855e735e2522887348ffb32e3da301a89ee d6d2d9c6607d7bba81c462174ed2687afe7e70182959f27b1dc32279173e5acb 261582b1316753951b4de9759f0abd10aef44d29b07a23ef1125df198c4911f6 8500aa70f775d722f1ea5ef88a2aababe50e894cd2944f38475bd8ad1b9860ab 1172387ec9b573a7d8e257f218e2aa7303cd43a3d4b6c48e1d16d423818aa746 1ba0ae5af3c5cb70970ecae314e8d54669f7da50e1f825ff4edd720aa7938f51 3362252bf90af2ee0da1bfded980ea272b56470e68d111dac0e2f163d7379cea e1aa85a4d779a769ac46acab2edce864b7bdbe5ac9b377578b6ebf99f0de17c2 a7dfff79ab674af4b031feb48233c9927fbeb336ec673d8af8e41b4eada60354 3382b996b6aec0266d46837558e0522a0302b0b1871a9a1daeeefdc848bfc706 a3f6434b1196fe613a5c25b59ab090c379f3b3da3ab1482b2f56500387fb485f d1057e759b40cdf34dcdc77bc390a2f165691c57353443ef829135f9103b368f b3a5aafa814aea5dd3baef1000218fd817b68a43f53e41a3ea48b5ebe88a0196 00abe0ac88ecb4bc95df5b5668bf1ba90e93c083f8a9d6c7bf225b009dd912a3 3c7c7514a6c2a6502400f85e359b535b35fcb150cc3a89b307d2d872d77d6776 0e08b181cb586641ff57addd561e015295a9bffa8e9017dddc69aa7541c4ddf2 6d54e0c9b8dcf5cb95ecae3199048ac87c5170ca639a6db338601f25a0626e17 16de0404b5fb05a552fb5468afe685998cacf5d5f4de284b2e1ff9761d550199 7a7fe658455646024bc29e3e01bf485a83cb81ace661481dfa1eab92e05ef4d2 348ac5b1a739ace405020899208e1696bd5049a974adbb17ca07cbc6e9010a9b 08ad0ed89ccb6236709d0ae87a209f8b7eda6a166e8b5def5236dcfa9d64e7eb a12865f37b54fc40f0b7fb06164f85b65ef0f17ef1dc0e08f2a31bf9a8b26d0e3d4fe9e42089661231f986a26a77fe3eaa064972fc7b3f2b715864ae18914004e09e7ee8b78350293d4d471516361e1269f0a7a1ea54af41ea52b08c07f328009d46414df8aca7639e778095f4385ee11e01cdec25d325c274873b3530bb1907e161387c64f8dfd94a94abc0e5b67e4eb330950f7eda292275b0eb14216960083e457ad17022df417db08ea5ca40716f7e8131cc55b9ce1fc115cf5891035807cb719d922584f9f1e790cf35ed48163a7ed247ec537fc586b1818fb4b031bb06f2c7ca71669554b7f06de9f6fbe2f9d15377704c28d3ed28cb7cc67b4566b5009139a16a7f700a9464727695bc7ef2bd225caa8e3d47a241416c2efa37eb09038592c5039d44ff1d6a5e70ba0025271b37222d6976ab8d80b98fab6fe05c5c02ff715ff9e878914d7809067ceba611e9829532c64444bf0235c3929deaa7d9050ba21cd7ded73cae44d3484825d9c9da79d87a9887ef862db2949aa61700a305061bbcf5250ea03426b6c17022b7e3d7a5fe54e5da72bbe0683e9f2357884c07d04491fdad62abf0944fb295b98f6ec5bd22fa3dd54f5171575d598759ad890d8e5cd6dc1e614eff09fd47f78e43c4cb53d8005a643babdb979bf18fec799201589fa683bea2d3e96e54bdce0a62ccdde8d1548219803fa99653edfba3a10304c0933bd1af724e19a2e5972bc8857a9106985a812c8cca483dac794142e180064eefd8be5c2b69772587a7085682606763d0ba50c923dcbf9ebc9b1547e22c0c25d04970d6bc4eefeeea10ab581203129bbf6dd6d37232205ef0ea7c1f6b420e314be88d49e5b22468c0a1a94734b32d65b1efb8a8ec9db88fb2042cf4820601ba3666fb774f1c222560fc73ca95907cba4d15aa68060753de692a73b3045f0eb940b6726ec512a79a676f6b0414c94a9d9fcb7e0f8fa1973a58956e1167900bc94a42c83d52a62d703aa24314e29ca40ced7c3803da9df11ee24873d5760f0ce1b9e2154a8da34dd1eabc8904052f924975af53512547478f4b28838a230409f51a009d6947cd087ac9a6ff6ed500d6f98417b1b0568e638e5fab64d1007c0f50171263d0f4e3aa1dd0707bd8dcb6c09d404ec94a1378e3570c2e209ed3ba0471a671acfbc1fabdb78aaff7337dad1166f15a716c3ddefac984988fe2072008d375e7c50ebefd5e41e9b66fc5cb724c4b6abffb99beb46dd05b6e727934c0088df22ab389d24b82a07b4449a17cb25487628575a394edfe7b470bb9b411d2053388f8d757647b9e97a61cc644dc77ba35758c81f19b6aa81ece7d29d878a4047e080c1ee5b6ec5a26da39bd4c8f99f87d111c8537e45cff024bcf794cef5608bf35c6a41980df042c55856c7a0ba5e5a36bdc16dc7e2235cdcd482ff968fb004335c20a4fb89b26783fecd178f0cddd1d60d6a6c78da8f196d4cfe3d963c70ff5c65f21b400f346bb9e7c840219469d4850f1aba757b38ab453ed1d557c800b54cfff1369c1f3b15ab238f17382758d760198cc744a79712557ad2a53304a0dd0c2c67b71a3837ed8ed182526c8c24f0d42e0593a812307aaaac751f6995e01c792657a4c9fdf947e4bdd1404ba058c01faf78160b80b52ab6b71fcc63b2b012eb88dc42deb5fd4acc4a03948b1072009168abcc60f350abb3c0613836d7f0124547fde06257efb5f0e41cbbb9e24469004a560fca110301e4689cc2517f30908bf665ce7a54fa3376bd83d306ebaf01c05d1ada1d92a1777585b8e46b6730888b03b3bf040dac8a6501a96183b1bc6834c091cbb6c917d496caadee033520753f6632b8553846a048fa7563b89dff051ac4bdcb1a82d41cb0f74c5b6650b0cff0af180922e7a2844e446dd5ecaf2e5cf1a904013a56a545acd68795ee1030b2ac2a312b0ca59f45e612fc1b5749e0295b0913746ce25da0da5ddc4a036220dbdcfe42b29f18d800ede020b0b7e4e723f9c5296ae63b1af4f3e5bbae884080a87296296ffb25d7b7852a1f6125d3d2d8df7ca0f2f08c55df10352c23bd0190164cd21ef6eecc6ffaeff84d9a97ffb38277a20110f5b3ac8cb86a7f2df2f2a08e2f9a8114173433e266508c3c8b87d04c32012e3c2c05c9a6ea78430fa1f440f606cd9f8fc2ab763f2490d73382314ea31e1de97e01f82df07687f166317ef00ac702af83b8017e4abfc53318d1cdbefcbd4d1ed1afd997b446755be5e81590cbfeeb1ecbb1f182ecba9efcda32f2ed00c9ea008cf5f14f74d47f1643a8ac0039af2f14688d9a79bf184e23cb422e7d980d1de037851bd985e715a9257201c0e2f8b94909afb8330cb5ebfe7b94672fccb352f5f73491fa8b5021d35abcb9f0cf17b82ac96ad1bd445f37eb551a13da6ee2e49e39293a833f3b0e13211aa0e0541b84fbe9400eaa1fdefdbad40077e8651f9ccde3221043a7ba42548cca7970100971489df757f812775ff284988f70181d311874459f3e958a5f6f8b6afe00da70c77967adcd881f0386e2be2e00278f9ed657fdeb34da7e545c9d12dc5d704cbd5e20b4e94213c988b7278cdfe80d35e027b1c0f947a651c24ca8d8cea7f062a130f6ef462893ccf8d6586183ac5408f9c55288a288303e21fc60fb0115a08affa847195c07ad91cef59d5e492767850ba130c01d7e2a79515ac700544a108 true -check_ring_signature 948cda01dbde115387441525942fc4288e41fe1a2d72484118a18710e5eddf01 6784ad4d2d49e4396381c9b2fa9ec7a0b97f0edee2963ac8a65a56abc17fd9b0 39 64090599832b108b9315cfc84ac4f68717b847aeafd22cad75b7630f8542dee4 b2e5b89e807938c6d275758d60ca0a37349221bb6310f023ccb1535153b43ba3 24d3533f282eb96838f43b1a186562a5445f9e7f660e260a267b94d45b575563 6f50054c99acbda4cb93c157d432a3a0b85e261efc8b3881edd7412076c37ed3 36f25690fe54aab1c23450203855146a663146ce7d7fc202195b7f390382f5c4 3e5560a9ee60dc6f1f28ebcf007bca95bef4cf0b5ccbc1ed5013e6be42702058 17f69edf9f821e2bdf908602c1696edbde994f02d2d89e9f96fc9d68ed8616ef 73531b452a0759903f71cdcf1f2f6253adf8d9af294faa4ad0e6011bb84d0df4 8c3bfa8b24bbf8232f0c09b96cea89b0457067d966e26ffa655230fca9872d2b 215c10039c3b88d43a02d3e9298de935b0dc10e9ca6d5e2c7a03785e983f82e4 3b5b637b9cbdde31de0e5777deb2f4fd833061e7c0f128ff43ac08d7680ec65e 41ab923393f2c1703d4e6dce6c68100f0b8f653dd3745e5d536308f880f49ef6 c98157ab09e414a98f2e4faa042f6885b1a9d4257798cc3683ec7400fe90439a 513173f433962928934215b9cf0408b97132ddbed978bf1cfddd13d0c5f7c851 e80e0f070577da6c9ecc49d56913f894cbd139b70b46835a61c67fcd9380416b 492e40d74a024ac5ea1e77d4409feb2b8a40e8eab5663994be9cee0943f7269f f1a75e99e3bdaf1de23243986738ea3ef399d815cea5f89fbbb1113529ea57d6 95a5d0b66bf982f697cbc98fce7ad2df14619b7e0deeaecf9a3d78c96cfd81a8 05eeffee18f082e7ad08f3489f325bfc6fb2e7d5707b69ead8df2c25bb641bfd 3d09a3fe4678709acfdfdf31d6380a1899f74a15c5935a145f8ff9a483bcb152 9b143d76454e7a026ad3562daf6cf43026a856cbbad7002a34d29d3b8ab3996b ccdae276fcfb73b7335d10d81215d9690314cca2d03257ebb45e4f3054df2c50 ba199c279e73eb5a2086fdacd3274f710454e58ad7a283f35bde3cdee84fe94b 18418b210355bb214faf58e978a20b1f42f21c05c9c43aed5d431fb89f3a1ef8 f90191d5daba2b26928c9b2bb54aff71ff13e2ce6ae75a662665b7314dc19909 9276571b9cc823f4a94934e38bf555c050c565b5e9309ca3e01375caae68006a 87d5396e09b92a86feaa7fb59fefba07226f6fa4d6e233bf2207c96229235ee4 5bb87c2fc8ce17bca40f76c554c0e5e79197c5eb0ca5776ac7bc7716ccb16930 1808b6cab44c5ac007706995173c93b85aaa49d16d638f5e9d78ad50b8738de4 c735a4ca5bee1f2c1e9689e94d7b66a77435d9c3cf06ec8479b135253a29d82c 141849214adedded9ed7c72d1e2c1d37bc089f7ceec8b3c2eea5740b1c0aad3a 044e373335863bae65e09576a7fbe10732e9969d8286c75ee84df115bb7ebcfd 5f59f9e717a44515544406129ca1c997685c29dff93cf5e0d8ed0c9daa005788 c47b97a2a9b0e5acda113d5bf6822b3c909eeed20ee11ee86c3a03b5f83d817c aa3ac5409d620589bf3f34fa7ed176b9af8c3c6a6cce7739a1f80c18ca3938eb 1b32db4995b4204ee0c68c6decdd864f9098bd6fc940a0360ff419fb271a2a2a d10fe0e4009f962c8e228152f9d5faab1aaefb4dc6b80f7ef815547217ebafbb 451e6c847941fa6a746fb7fa736ea1127505f818f1ad3d7faef45d68a549d33a 2d03247168eeff5afb9f27a214271cf60952faf0f85864fc8661810bd358566d 0847ec5db8f9fce1dbbd81d1772f07c5df6dfd08fa6a8e5b687ddcb3150aab07635f9582492cc6953430b5c97357bbac9bf551735125ce4a9bd841aa5c9dcf0f9c2d754a7620bf0754588ec80a45efe79e13ee4d02254e6d93d9a72632455800dac94a7fd0cadcc4517a356acd48f1fd5c393882033058e90642187f6067f00d3926a6f1c085337cbfd94e291cd4dc4a48984cf6aba36a53baa371d67cdf83010b12077317c9b61528c6496c728d9079ad1c00e59e77f083422bd64dbc27b602e5659fc6bb1986449b6cea8af7fee198ed12d8f7340ae0fa203653a67304d50d5861fb35d7569892981e644cd2a6fbb68329e16a32c72cdde3f7f533a5a331049f41f08381d88f82397c1de925259588bb983a7a549940fdae32792c1c84680ffa38f48a8081249ee91897120505c6a4ea06caeb7eb04c979e1f17c26829b0048e5743e1fd477f4919c16205759aa1716e1db84da00929f17159ab827c7eea067aece76e0c9211368fbea566e3ca81dde1bf824650b2e08287da834f6e790709747c91fecc7f868c8c3810196fc3bf5d145c8690d78fbcc6567da144cf678e0a670f7c8155dfe8abeeebdf146235a0051a14b7a8505a9a1969cba7f4e5a6010f97a834d28be7344ef645eba689ff6a67355632cf6bb25f7dc72eff999093250ed758fa19c43f91c377d7dc332cbd95797ce310c502cfb774756fbc55df4adf0505eb38253fd14a9d2214b022c7113867323cf6f3aea01006c4bd73cc297f2d0c07671f78d9a008ab925f62bfa63742d08a9e3752d88ac71098dbc625bdbe7b08508e8efa4d11e04129cf86270f20dd4ba680c311af800304560d80c626549304aa261aa4027806924271b039dd021812d44961dfa74e84a582665643c4d6e0092147453783c33eaad3b49885e90e785919f7a4e2ff00da1fc6607bff8293d70fff9a9b519f35200a1e14f8db69f944d2f1135a8a8d7c0a5f81077fa9b0ddd603d6aad05f16cffdb3bb07a6c5009c506cd8258b85edba562426fd40d1658fb109c2401d667b969694e56e880efa742006fc117117b4d3d4b9cc36754c010c300223654fd18e1895a5b2b5f1e7a7ac033fe07795533c76a1d2f813b9a513910805ab15094a4f635e5a9f4be12ca84c6221b97a679a57607941c8bc5595a92569060af25e1ca2e4b695ddd5fb9365d9125dffe3aa8d96db91ae725a6e111fa3ea0cbfa3aa097ecface51b8e4d1ca0bb87cec3f6392d1d8fe8fc799cf2fc18e44e032be134511c74a94c59342d77b872fedf122acc781cbb50ed4377e8c296d5020880a838207ff61b56d8f4ee8d59ac5353d92d1d779928dc9aa3c8b16398c9fb0d30a984daf1b75610ed259b5dd942cf3274ae565fa86b02ae607ca07f5c1c9f0db4d524ffceeead910212e02811cf993d2223fb74c14b166fe305c78c404f52070011bf2bb73368e08f4726049e048974d973c028fe437fa78add841fded7d40e09a1fc2ceb9847368fb4ca6ee98ca0e0f46f48e72c9750a0830738381d2d470b56c24745f4ef542dc8cca2794de66ce9f7a6a549327945d3d499e2e3d5737c06d5bc4a41ac7d7f0e75e0c406e22998fb0deb789076a7e93849effac95582c902b2fbcc0259a6557591c518461d4cf51ed0f30aa0dbdec363ab21b0a9350aa30dfe1925f75fcd5301bd31f3436121d30a0721c02b244f4fe5385c1c31662ebd0fa2747acc2cbd5f4087686a3fd4dcb1a7997eabb4e492012ab70df31b40cdf60f82be3badd1373e8cdc19956ac352a4f2790f209966ed33319054aa602c919a0eec5161c10b254e9cce4bdab21f13bf713639467d4c723c8c55d3b1bf356f610175233797454fb2862d001364eb674153e6d0b517059cf99e103ecfbe5b12350d638f04cee400f8357fb2c72067ecb13d7438784d7ebeec784b6a46f44855fc029592bfbe4f0f926912e6531cc8971f0235b74fb5e214239a4d2fb1e7074e3a0827e247ddbd289b549d5fece3f28f22754d0fc09782f3777bad20173e546c4b06a156007fd5460a061c94f9083deb062f597eb017f1dfeee1919b4642ebbb9205fae741a873ca30de35279af299f1ef02574221a6939ad562e86ebbf81e07b405a166cc022728be317cfeced60b2700dcd1a3e316f30e91a43f4eb819d1552f00a0560e8035b577862d6cf1fd00066f5c22c118f7f83a59942f8ffe45f239bd0836124df13148569197d941a131bdd8d68e2166215885ffc1d9563d1cf8d3f40d123a962d7ef9bbbf7cb11e7f0edad3f135132944b11ed5be09e61d14d286f40afad5d9e0b111eba16ae7513e5ad8e03b0bde1ca868a212a3d0ba9d3b3f9979059e6298a5c16d7895959f7304fe27a4d7fee06b5759de5644772a12107f62b303067e69525293d18f6cdec2994408d2e4cc70912789925b09f331e9d56b116b023f3f9b45e6c6dfb9f1fa7a81b3f391b33c110c22f8ce84bb9158d69b35bde7097f9cfc0d9ecefd4c01b7626f8765481ec188f2d9ea0c0b0a4a94bb32fcb2ba09b37f1a8125f82a5dbf758776013119226586393b374d9d1712800dca1ff4ed048812432d34f27740e2f84a1c616c3d6df29c9de8af8910e30a239df882e9df09adefb34643a017632223abe2b1c2f614a839e01ca3d177e43dac22c9ee21940d83ebfeb61ef12a9d355120eef9f16a23a47d03701242e6c3102b1f92ef0a4f03529aefa34bf5c7892f59b7c420893f3473d3c41d497da0542898c858f7ab3803267ea2c3f636bac60b1d25f6b0067f521a286f4acf21e0466628861846e29908a27f60d0a0a6a9f81b0a218a4f1527974457721b8cb1c8d086b92190aaff9500a8ff6f41bd186dc9989f2cf20c8303ed3e961e76bd6320dd11787b0f0d8ba20c60f20997894cc149d2195ac3d68f8520e36cd1e29cd6066511ea623aa88d970ac979d24a150a0a529c8c76a49ec6c75bc1b1c1ec3706ecc7319e0528ca792607193f6ffb075280a243bd82e4d4880a7923ddb94c83201ae43c61d511a54b5b04cba44d82ef78ad854e80f6224bf5b0997204e3163afbb73135f8556b68886e03a2b24d6bdc6c3df43c46d1897aa30c9117a63201647a73703ef1de8ceb3ff30bacf14adec0a8dfde1f5fce7d6e0d1794d855c9523ee4409d088be1168a5e520cfc7a0c2f1383eeedd099e766166d0c6fac9c97f3a1da772eb0263223547fdf0db08ce02e640f68e67f78cfbf40c28d540585dba08a912250c25a7d91b04791009e7b2b30774ef14e8f515e9bb135802cca8c3bcb177191dbbda14e865ada560ed6391989f5ce03adb51228a0eec9f5ebc1412d91be5f1014c01240d3f52a97058afa93a3f92378a5077824408bd287211e1b7e61a8c35df6f7f0f6d0a6a24b08bdf91b5f428ce52ad56632a51b5e897d44f80eccf0bf3c1c5db68cb10f3b670d023e848383c74accf8b275c216647cdab84698df0196a00f4499bdff127f2701bebe208ce7d619ccaec10b445befa1f76a146b98c7264c57fdb562294ece9005 false -check_ring_signature 710bb47fdbece67849a69db2f7c1f14f7d1e4dff1c3320d65d80837baf11f7e4 37b0b9b0e48f9e013379674766be196e16ee24314eb696304525939e6fa98adb 233 bc80069d681505da44b09790f40fb8db965f99c287d12fadc43ab3fa3b75ea53 3d4fdba1bbd09eab73ce3ecfe604993f77447bdd64c2ceba15d120ef02ff0d32 a3bb3885f01313a1369da0953087deedc986f00b67cd31c17aafe9bf6e195e3a faeae6370f12516cc81b2dc23e46bf0e7eec21c85c44c97d63eb773a8fea1114 1b181665666f25851bd423a4a3d3b1cdf2ccdc6a8be5410a61e8ca9ba8e0704a 920acee59bb177d41044fe00e5ef566643d8516f41375223461f4224219496ae 38d55febedf03c5513f10502c9e7a6b16905a6341b667777bec3d7a266c6f14b 17436183d3224d199159a030e175e2e018593eb600ccb242e9d34c57ee0b2d48 e877d5ddf68802761112abd04e9d6598ddc13a62fa7a21a1244cf79a4236f79e b36e2445cadb94cf9d9146cd9d4a2d28a57735a07cfd4d79f8e6c48d0b073afa 77f8770e24fa7cd2ed33752c18f57214ade2abd4fbe8e8aee901611c5d12499a 3ae92994f4f44bc321233c2ba9ae8bea127f09ff53cf9ffd4291bfee8aa29c14 80e4ba8936fbe4dd755ffbca7f2ec7002aaead35c8118d2c774382b1654e7de5 a9461f068a75f6d911af9b8063c2f0b6ceffbf90becf8e4c13af1ae933c21284 8037140793103f6292c21761682fe7ada52c2bdcab879c02a3f35be691d35019 71031060fb8ea5d3fd1de7ebec6f70c3f197ded26bd0c75fc96d9b35df664f8e b6e575112374e810e1e472a921d905fcb426fd559c5355745b9b35519ba7a0c9 196f4e0cc968c423af46f1db95ec328408599739e49cb6d5e4252702063e408b 4205e2fa9849e104a2e7ec0a538165a49e582279f30dd3a64752042b3c00d7d0 92227f334cdda05ebc6f717ae77e4acbe2d120453253cc86d0c5fd133fedd3dc bd174149600c274d08cee7a567ec376ab5b3a5f562c1351eb92888358c05cbec 88ab84d977faf5033fe80b80627e3d08ce2405a34cc910b2b6a54350c43a8dbf f1d3f7d4e113ca3c9cedd6010998ad257ffdc2af5a2363a7f02053d6d0e3c9ee 1db8f97f4bd0eebb38961ec4198254dea9efc2e8ac01e496b59f37c3fd93fe3a 1a2b88308e1aebe051a99b8c87898d738e2983e085ce42df2d2fed416d2c26f9 785e79ee81116104ba239583e12fec2aa2b1d58ea09c55c90509c4885f07baa1 6c3c5af564cbbee0c05816f902714db649799f387836675b12e6da4249ff4127 97c154a494259a834039cd7aeac46e436f8a398e222eb93ee3f7c45d2ffb7385 6d1007cbe016b2e2a13fcb2670c37131de1136777df7dd7492dd63952e732e40 b1e72517b6dad1df31a1af9a62eab2c55010dfe2865716a608d8fda9442e9c93 9016083021d03ad07cfd059e4bc7082bebadcd0d54f9a013d851795537f23ed8 c96413c3606cf6029f529d2b617285e93337fee09f861f34c3d9593cb009a19d eda607e9f3b42ccb772333cef5f6b8f0defe6e0b896997b1951cfdbbfa501f5d fafb72540860d1bfec37603e55cac2045fc8020eaed4568f3a5d64ee9854b963 9e4eb16562602878c14ad77a8f19cb97b374a8c61070dee8b742064859890c26 0553abf139a1279d0683364122c49b6f947f2412b3af93f53de949afed063a6a 913b50d255ea55d265eec418e7e57dd466575765d5b56db4217054c1ce1c1277 43544d15a86efb90738321265ecd14da46de33a5a6353eedb3f982f71fb80756 ddeaa9c2828367abe3a3ca23df18c1924e0cb45e530c08bc26dde75fba13093d cdd0b203391915502d7e461baa8209fa53c6789b18f9c6399712592d35d7dcad 2f1c754a6f89b98b7b2f18e3179f68d62ab78f8b708c0bdc93804b2b888d4742 b78e2a22a31618c8f95ef91d69c82e3aae4f6ce99b0b2fc22cd59eba79a8ba2c 796bf603e623ef9720be0f1f3015236f1782a1f3c9265b6532e16d35b00bbe90 7b22ff7c4b7727cbf6a17782793dfd697c76cfc9a8ef83e8b64e4af2437c4e21 eef8f90de3d94826e2c7dbe26bd983cc9c927a94ffc2145ddfad3b22eb00f1bb aa2e27ca4f1221c944b8844431e06a235fe4490d947e61709e4c6693f9002003 53d408f10fca55711e9fa98d52410d935ca91400e060dbd62f185f25082e5c7b 4bb18f29885abb5bb19a4a411f821f5df8ca89038536ed2d820a667a76cd4045 66131c1711c5bcc27febf77df1935c017c7777302f91ba1bcb334929e0df2057 8483720b07f73b30209af4899255eea94788ebc22d0f3103e6c77efcc3fef5bd 92b3c6762a86351fcb6b4f54eeae34bb30dcb9c4ddc17439768ce0c1d401cf43 8d0cf24563b1b62ebc7b7503787db7708bb09d31bad3292e33497812aa6ffef9 829c31c2815a9eb05dd7650224835f33aae76b10a1faf515baea1fccc63417a1 fd61bceff6f9859eb7cc191b1f91bec9b4ec4735feecff6b80256b879b15c1c7 6630374869c42f865663cd5859f1e4beba9b8e383d880fc53106ac5383b13c34 f5e08a9f5dc42c23a55c950eee107487346390772a5d1ceb67128d5650d77cc3 4040efaa07d24b282159775d48264b3c9a7f20a8afe983ab52e050a1ed2be158 fd273dc9918b9c53bdf11ac1dc21cd75760e28ff353bc03a8b8ced7773ab5608 287f9c4ce94f2f0347e1ec33803098a620fef03a381b7ccc4bce055a722b952a ca03950ae767f48cabee7187a59263372baef4b0691c42a34cb6c9d56975fdd5 1ac0ea8ab63f1ed9094ab009bfd3021116fce35e7e3a7823f34153b1e5b9e376 4c6d5cdf25d767a3768026d25bba3cf97fa2c592454750d9df3a52cad770b5b4 ba1c2d87572cd99b8f08463b3120d36f8fc1ece539414e03182a1824097055a8 1ec0962b71aa55ab8f3787367b51d8df031cf21e8b2f14b791061a335ee9a0a4 d36628a8ddbd7969ae0de4efcc076eec1a335a22f474b01a9a09f6a9723dfb4a 97d60a303e7066e111cbc358c23912ad33e544c02e37fd7de74f810f72558dcc b449a401d23bffbe40277e878b88bfde15c9c3cffbefbf2cfb92ca34981f8007 fe9d1ffd44cdf777e9df295e6a00e249b6ccb60756cf52602bbaed7b4927dbc4 f2477cda48c4e185998b2254a5825bffaa62508221259affc1f84df7fa87386f a5856e89e7c7dcf8de727271c11689ca32f1121b74f4fb10e12002f4604c5051 b27dc709b21fe9c17e53a88cfc024319e72d21e2117579167147549a65491826 8249a2e462d0fdb90158bb9a12472a0da93583a0c5f2456e31e528f0f4709d30 04e3fb91f5fea493bbfddfaf61eb5d17073c873df83e870673592ea1327fa3c9 d369055c1b7803b017d60eba30c2762c6c34706db5a46c1c2952577083f2307e ba225d5a36d9a157f6d9a4759cb1bafba8ec88b23c7151bf1f3c9244ea757e89 3b098683e463b7338423f38f6b6cca35c5126176ddba65b1f85170765efbbf29 cb6d471c00209f4c3186873cf37485602e9edfcb077d4681121e157ce728ee4a 90a62c92fb669aea4ade00bf311a7db6555ccb8b845bcbb24a8b54bfb39e1cd6 a4601ce2f84739339187a9c21d3417fa20972ccdb0de2a9801922a17ef504f2e 163aa59c95a7b603fca195237a5ce1fe30440277ff039a6eb0b6bc6d5686223a cbee74a84e9e15ceeea58cd42a627a32cd117df17ee0a9926285ef0ed8508178 bc8a2db70ecdbcc8a421b31e37c8f677f0e3d1e76ac8c7cbf97a5cce59f1b532 698988dfc3a6069ef2507f2ea164a2e0dcb8924edc9c4f5dccfe8b9fd9e9b8b5 e1b8bfa048ab935056baba5ebc17b567e0152dd4cce9b9b5105ab5f8af57827f 3fb4a406fd381705063b0ac930720b6c20067d475fc502c182ebbbf32dcfd137 2f49d0d33769ef690b78e3f2196a9db54f891acd1628ccb58a629fe5e8b9e700 1591398f5b3ce8bbfb3f891615db4bd7312c1daa29cdb7aed4aea05600679f57 6cddc7b0d0f3044e4589beac73defccfaf03517b5ddc8122b6b79ed92f911b3c 3eb795362dbc461b9627fad6b5d882439d23d0d70da406b54ad54a2ce6e5a9cd 2e6d7a386112f9f71919adbcafb3f332ce5b740684b6f5ad34c50de50ac2c62a ce139880e5eb648e6ece23d9b193456134280ffc910c140848e5ffbd5f55d444 c7b008eb8965440422b416431f328c8c71c7f2ede35d996174180776a319408f b132a44c95b5eca28ad6bb5d987a411f4efaa845c35c7b977b285f4b6deb7419 94968558fafaa4cc999d77365ec6d8e28f09225832f95214b9f6d1bc982fe0e7 823dccb4a7f2fff08e5a7592bc135e3d8d2bf20ac49d51083d77976bbc4314de a6fe307b1cd06c75c19973f6bbda3bef0bb4a02f009bbc4badbd93360c555a21 64d378fbf09996fcc237da28ab6ab765b764d1b869e218f3752a076e05f24dc3 7321f670a09d9028329422c7f80329656864a6bf0949807b2814f46fff0a025a b6aa384830be68d59ad9e5d0803bb92f8b0314762abd2439e4d7fc4180399ec1 6fc09f62336656cbc5b1602216b9b7669067d894a6fb253c591a6d63669f7255 41d4456419b822173dd088de3258ac31ac6a2e0f3c7b6ec52119a0ab39d4e063 623d4560a075a6b703ccbadbbc3ee2814c8bd7999d4a622d4cdc6527eac922c8 22551f6f1a3e33c51e2fa8616d6067539d2e33cb2ef11edea71366501048debf 8bcfb640cde6df5361138913a0b39955b5976e6234b4581998a0f75f70dd6c21 8ba8822579563cfd832491008d4768a98fea5dc1d41e83eea46cc0e19acc2288 7507b17734cee1d9d053220f569e7bec385b67bc542adf8b465aac67d7efceff b61afa746bf5adee1c7c0be9d1917fddfc50444ae44ae002d0c9ae5bd0a29ee2 c92faad79bb2bab0e37c2694d2812fcac75408376b922ba7b1c509192d0b86b3 34196b86d6d5fb7186ff79f795771fc4d49a7b6092d554a84290ef12f02c3b28 f5831d627338a43824855f99b22f44c5a68d54b48485eb0fa732916d44bf6850 e09ba6b0af925a995ab23c65934baf9b3b2d07aa41fdd9c5522665536a88948a f9156019300701ba407ada5b3407a9149ab1fb40d35ec4432dc03fded389c39a 964f886c18d8e2c30f895f5f63f2f22afd66a9c4629cdaa9c465b6c41d89de21 64b941c50d337074a9e6a45e38a0dc80cc94188810e1072f70c99f7e45f9c852 cd55b2b9afa0615fcbb8eefdf718592eed4a3e19c7d7e2a2d1005e77bcca8638 71a68bbff0f53a9a139953b8f81bafb664ce15d28da962e313531eb73ee3ee12 4985706bafab0647b6de2e5d6810dd248d15c2269eb28efde2f6fca6873d9185 35ef4fd27d3ae0eb01781e8838c9b814e5bcbcc065527a7ff83c0998fb6cbca8 f7495304811ade10cee14bb9a4c7a3d1eb0ec7ff67bd4b2758a2f351e7230fba fdfe233c1daf78c1304e3f3b98c2c20fee470c335f3849dc6fba329f15b62216 9048ea3068af74a7e0bf75df17eae1e6a9a0e0f63794360dc4aa216d9a05e1c4 7db6dd7d8effe4b4f1995ce6e64ee81f0062c7677634592fe537716b86e3f767 5836013c742731f71783874f9d3f89cf22011221c4cec79d4f57dcab948988af a7a0f45a1e99da0b191cfe25403c22161e14abeea6d0dd8d484892a0c084349c 3ac50b04f4007da416a419558d518edc4b77d1310021a7b96e99601162817e9e e2024567815f211c571292fa15c79defe39c9cb56a2c0d9d86341848e6968c33 8572fe88b876f46b5f63ae31e1118ad4581d14b4e5cd9c6af76a383965929629 a0fb3f48870d736dd8fee14017aecdfb090a0f022a0af91e723a6de88439226a 08bbdfc4f60470f196886a7a32b902ffacbf748865d46b031a2208becf56cbeb 421cb011f216c25af06bccdff322bd8ea0d9467631d63841a850d2ae9195e123 31013711fbe2d0633fbd242353f9c5f9e732ec16027bf6c5735de2954f589cdd fd9bab1a0739e42b61eac8bc1d2265cc1ab36d6b499505e53485b7e04fa78c3b f9f2d7ea49a800d2e0510fee03cfe263eea5ccac4abcac3f02f5a5c8d5fc825e 598f65bd3008737117aedb50b1799dc6979054b87a19f5237d990f747992d14e c039e6b9aa5fd57045d7f5f9e2b4e6c38599c2626e815e3e2ef8621450dcdb6d 8ed5e094a77e670b51ad4e122d5dbd9986fa2c9b3a289e6f61e0fcf1eeab1ab7 51d428440d74e0b9e502514b8ff0c8002f27091f49091f1b335b4caec87ba22c b603a54b76d1861b02c2e8c8c23054300f76387026e50e67cd2816b1ae4be42a 0237d82cf2852b9f71f8011d77703afe6d8cfe148e96305349b45de9ad9623f0 db11dfb2ed9fd7f0eacd30b232e7242552d33c03c3911851dc543a2da6a91dac 863946ff290e26c9abd890fa71a17fb3ccd6acad35ff9af0ac0ccfdd4d9a6f6a cc8e7dd9f7ff797064a53f6d5bdda53c2a6b1cca8d3e9804d3d60480d2e2dec8 ecf658a870185a2cd506811ca0836ad198538575290f89df07f9d2c82169a586 d66034b8f6cda398d87c5fb92464e8f7fad4ad094d498186a775d1aaf888216d f945c6ecb12e2a0691f8abc3a786c11c6eb286c197f3cd9fd6095e1bc07760f2 84d8d4e634bc87af076d0fa6df175f15a6d95eca004e28d7008909948f9d2346 7b609bd02dc2de23e9820c5a7c8c56970aaa334c0e0f791c9e14c67beeea4299 ac24e89634d6f0bfb6badfaaa6af1a970bb76bb53fb67773f3235de5bbb06c22 9c06d1d647b2f8e291923f26f262f7a19a7087799b24c14c6b2489c82b3ee498 12db474f3de60cd3824467e72324cf7281dfe249e336758010fc8db585fa6791 dcebf80824a54a422662822d7a8ddb8204e0f66a923132c313cd88712825b30c 4660487319d300f527d97fdc19a9c1e8df2e98813860161b0a8a846b13901aef 8ad7a25dcdac0593734f27208e78be08f1ce9ee02aeac60e988e6a21602cf1c8 f214f4bf80612b1445bf0493e7e1001ec1ef4f4142e3636af49956a2c7fcb0b1 956a212c13a3e0dbdb8e70fd5e149e27b3458317806c16407e7b47143dda7559 5fbf31270fffadc19c2582a9e3bff376af6c76205ff09e85fef2e890dec09669 ee69513a9326e415715462e7d0e1ee5d52997e9b7e3788026503a6e34979e211 f81f25f2d21e0e6fb67ad60d2b8ea9430f23715326ed24e278a1f8dd6a0beac0 757c75c5684416e1e6d5dbf88b2c12abed435123a7f5fbd9d3a0f05c3cfe3f91 9beffe91f4e5280defc3cbeb2a3dbf73a782dbe3d995fdd680b9433917073b9f 7ecd5ac800a544103f4de7924d7f8026dd4e8e2693b8b77ac7980719257e8fd0 6ec4f24ce362a8b3a07c83398433d6b55b02bf1fc9637efe945e111823d15fd9 e917dc611ea921cc0894b24fe413e130444a169866b2898e5dca78a6841fa605 d3a40471d9be9ef973fec68724fe89906b27d7f829c517e2f1e5f16751d82048 0c98fec68782332f2c02a8815ce0b1bf793c2bab6858c538d1c8cd6356b02a1e eef1cda23d7cff9d938c360fa2553c5072e09f328dbcbefa10e5e99f83e1eb26 cb8c581bbba12167dd1713bae8a7603633d68e5a0a86fb6c917b2a2d097bd6aa 49102bf3609fbc259bf4d6a4dd23f6b16899ee6fa7fcf7cbd0b80dcf42b84516 579612b110e3bb4c3639375a516122ced9967418b0967ae00e1eef8ccaa5f42e d350bdaa74b1971f81c85c18e230ebeb33e429fd25611e5d17a381217592a000 1f1b625d8e4d549dfcca099bca9f802ddf241d869f215222b30659e939757aa6 d925daf1db3ef9542dae9a8da9d6d6d36be276a6cc15e779e52ec12669b6da0a 8c9f3de7be0d4ebc1116224a09fcc8d90b1c266b3b1a794c006d47e16a1aa970 d3435ff694981c34991376c6ddc6d6b44553e4bc7a091980745366235b7a15e0 9f099b99a8468b043bdceb15eaff9fad673c87c8fa2809694744508cd7b8e02e 9c7551b9e8e4af5363974b50dc29d8fc2dd97a3a7e0c122570d76dd1c4af47f7 02ddd7dd7d4713e664474232786f7a32773c34d3fbb652757a185e57c442fbad 5815a99efd92de9fb15ab3cb75b50fd0b32ec312ab583250c19e9d84f4d5e045 dc74fad1e8b72004606e40eabac2b60105c180a11246bf933eb4db2c2b5201e0 81fd463e5da413b1ddf2f85caab4e6d9347d3b079604c426ebcfaf7d0433593e 9bef235e92ea461d1c7ce4d18cdb72774d67d00b70173d4064be0a020d650a4b 877a8ec853b0e2678f2f30e0846564247eaf96cb7983370fc4c58569c9331b0d 3bdcc66efa5b647ee39492b21cd53934fd60a5aab142213d541b3d92a443e1f3 b6f9c43605a8710c58ccfd305c6fd2f8fb7bcde913f2a73ae970e2ad810a26a6 fa0dbf029d9f0fed4e5940da174a80510e6d9586940c7157633baa42b203e854 3108ea011e78c25dc6aa4691d117b001c31438197376de607304909d4dd00823 aafa8c03346d29649c0acf87a3cf4ee664df8517dda7a55f9297b05b27a54a60 9d2ed2daedb4261c0d2fce8094ff97a7dbf86878f3f97f40e215f4d1f0bd3358 1fd69e2dec55529c890d154fd6b66b5608e208142505692bdb65d0e51715ea3d 4acb904329d38e899d0f9380c262a3200d977b0a6452a08bd511f7b10190d173 5af548b17b5f25f935343449879276df86a97ff58e1beeb55b966001abf0cef9 7b43a3a6b6e7e6fa2b2ff0745e510364b7335228a75d8e5d37ea72dc1b040e16 8d097028bd633673f969599e02730f6574c7c4420c6ca43a5e81deded730a3b6 c9f3075c9b451cff2754f64a382e6885e81b26cd38c8fc28fc9d096005ddcc3e 417fab96b01052ee9028a2a2bf9298687a95875340ff1c1f6048cb600703ffe8 615b4703e33b00d4880dc92eb1f636a4eed4c08cb57713f72c5aa3f82d39bb2e 5d45400938dfa1f1ce9bdd8bb907e83ba0bf3a77f2959e554d9ad36c1a890c34 64b5286d485b6df0de9c0609849fd33bb69f45e4ba9dbed5932768900ca8cfaf d536d848f44f3513e376da3a9f7596ce44ff0382a8e127dfe8c6239eb814b076 5cc226e4ada5873386ebdcd27bf67314772d11e7b5e7d6092bcf24f5c8b8bab8 caedac96f88544fa6cf59b4070fe7c3acc93a7ec9b07fe4eb04988e6b8f2937b 0e1e60423295f8ed86465c9d2d82d07cd8ce6ff7508949099413ac80a482ea41 8663e84e3545282226e9c6f4e8c6ca8b03baea7ae2a3d9901e73e9595259a30f ac8a1f386a3c18e07f45f600ce5acec6bc65f9b74130fb31033df01fa5081732 6b1cc3c5a576cc1566fdaaa17f2069a719fee59b0209e0a21a38d3d38119c6ef 38f7a0aba1dc2b623f93c20a09fb58adfc0745c25473a17db784476cdb00d182 ccccba85acfe4391cfc2c6e5589a669e6adcfa91d48c393317ca77ac606f641d 78d83f7bb8489f5d152e42fff1e4e2a1c9d2794606051fd779b77854332f6578 212c164ae9b5fbb7051a83cd116cc8215ca49d7ddb625096f06bbbdb12b51014 d280ef2f72910337d75b0786bfd050a4cffde28b899ca6745bf2ab716f6275d2 6e586b7075d7cafad4aff12ea7b991ecb47a8a5d808b626d9c952e190c72b890 05c5a5e0aee0f1227208ba7d85c1591a1d36e38de4918a3326a5a281889e63fc 107de4f351829397b99ad7f44c061d4d00ac5773284a6964e8bcb9247d4159e9 6721e13cec8a6030f83e04617c8f2cec7c92e533ff059a51b5629a9764fe8fd2 251f165ed98f1880dd7a3d9868ca711e167732cac5218e1c4dcbcbb10d131eff b7dd24a11932656fc860053236033413e0b8934c8462fbe4d58b7ce325977603 69d4b106ba61e62961134957a8cb42f4fbd7ddc1f77cce8cc9608e7ba7e2641f 541a16f43bfccac4adefa65341c5ce9f922f67a8231590cc3c3d3de29e6f0e53 f1d0784fb3912a16b8b081345848e598c0792e7c3602048f777044b36d0849b6 523392921869041c89ac0d2463496b089d80a70a319504fefeeacc54a50d52c4 e2df0ad98909b9505efebca9694c95aa96d5dcaf88b062dd14bb1765f49fa080 47f22691794b3ab08c989e339cac130bf260f8121862690f2abe39fc552092bb 3995e5cac379b534466628bcc9fa18445df6608ff27b864d4906bbe734b4179f c65121db8d5e829415d1c3afedf866ba06150e0431bafc096ed1e9f0dee0a439 b7ef82d8a0205f157373a204c5978aa912e2f5143ac00f2402135d091530c0d1 56a7963b4a26aa3d1306b5b9eb9a34c9a45a82cd4365d512d62d7c30643f8954 4c22da178b9f8ed1b50231d57b62b2cd996c95344694fcc0787d9d832c6d24ef 1c57626ec57d88867fed3065c69ea7a2839c6800305985d3b7b867be3378b978 0749b871b45c1f8da2d843067439018f4f20f26ee241815b7d0bc521b4d0f078 67a4b61755336a8d558ff391b2e33ceae60071e977a2f27b5778654a18db2245 9826473df20d0f16f54b0355d48de41290c306e8bd0acd8a134dfdcf88e2f91e 701fbb7aaaf9a3bcc7ed0c9e75ea4394171a70942558a7de1720f851089d8fb4 b33b4703e92527a7cc3206aad9e6207aa00a2c38b9acbc22a9c112aaec565a0b 6f30df0329fdc737f6acd388217f899dd136448cf34b2000af012ca40e3403034b3d743267a9383f4b5f83c1169a456deab8f8bdef6e1839c14f3d9479c9540abefbc8a56b90f4b30875ab5575712fb1ab96b52cfe3d71a844ae89275d2c150734563f5b46aaae3c7b34d134c0e22563ab48d92eb477d8ffb9f38923cf9298092bffd49509ead109c4907ab1c6886f073dd71b3f0f04f17789def9fb8c5abe0b97738a3493759ea3d4efd0a2014dc48f5762af462c8c8bc01ce469216d7e3601831f74c146c5fa448fb893017ca0be833e6e67e63feaa7cfa34451ff78394806f99a9d514aa3403e258dd4b8b9e02c4cd2cfaf26c0b8a0eb15fd7b05a000a40398505a46cd347aeaa047ea51584e0a9d0ae7b5a69f64ef61ec03e9ab5871640d90db666a0aab94badd87183f64e30f7a2a0f5e807b6b3fe9455ef2a1cf38c50dd23cb4ca8b42735345d9df00d9fbc449807e1f9ee05545427e1434ad63bb4f0d99401ac751141515e2a6314d4a849d45d8edb9ddbf717e8268780707331fab0f521203c6a59d6adf28e0eec91e454771dca2d5feaeed83fa453a0bd6fb106b055035d00da21aa8be0e61232eaa35c33da8df7275018c4ed44061bd8f6f8755082877ece91f3fadd4f92a2cb9c5d542e3c24579fe820aa0d4d479f97d7fa65a078c078971b1598645b4b56c5b7730e888554c900abbcece76b46798945e41e406ccf139ec92414e30ade243a3a2c88fbf63bb93a46f46c16da823ce59d9e5fe068701a6c58a1c22985f41b39be02555849f64490f1ca7003c2b1bca99599efd0c1fb95fb52ecd3031ceb0b3d39765b85f2b3b8cd2bb5464a67140db3625e62e019a6ca15c5e57afd0030233a52301cedaedba89750f0d902b95cbdc26782694048986f24b25255252e10002d88d7362b4a043558ce7881da51c2db8a76fe5a7022280bc76263f6509bee77c07ef3618fd957e1357f200061d70a54c2ef7e80f06bf8fc973b95da1868c04a4768bf0d0bc66cc2e079f686b70076843e999d86a036be5eb3177c54c827b394a2b2cefc861a82e4418310ff389f1a8c3173cffe00981e46ace919a2359c8b2257cca5829cbfdd7b78803ca19c8dbcdd7a40c15de0c7d0b7bee11d8fba40866982dfd59d2ab873a75312f9b696f50eeaa2542a3a602d1573beb5cb9d1ec43bae7ecaf3cbbca1e33ba13f63e43532feafe8e71c18206639d8f1e5ef0b9652ea72f04cc2688d77567e1fc529de58be225d50327abd20d94ce4f68229b97838f04c3cb12cf763ce322a82373c03057d31838923fb51600f6821990f064277509ecaa52ed4961e7bc1e01183342ddb7e155fe6a511bae0d618523ea354cfa25b8505cf770435d8d9d4469efcb262f54f48e690376107a0acbaa6c9d90fde5e0d0dd584e48ceb30eb0a89c116e3b630bed4a1fc2fc89ee043d755e1543c7f4f6547842af05e813b86e678c332b5471bf8834e755fa40da07e000a9b036526be54632b5b1defeca7c19c378bbc45b75cda7adbef8df3596095b68953eb22532d1427e449e75518fba3f182c546a311244b641fe6278f98500fa328e7792d7f07606ca255fa4b60f4cc9bf1196839625ea97e60017f6620503fb25556f844f77ae7262022d67c9e28929466330484f7c08f6e4aacfeaa5b50e78fa2449a6f54288f19efd3d38151d9713ce40e0eb448643e34c71dcc26c890d92768585af97cc888acb8844a676db01334e2a551315568099d5bd43615fbe06c54888017dea755c9ad60bff76217eca1c3646c803fdce660929ff0dfbae6b0d2f2903dbb5c5f864edc339280f728f7cd5885b3e2fbfefb15ac6b9ec8722130133b3e334bf1a7cc9c3bb897cfbdb423ab43d78aa710bb561910a68a63dfce504ed91e7d201377a4247ad17f30481df407e422151bfb9684ed95cf858e851bc0b660ee058e66ac313b3ca79c85f334ff23701977aff958bb50cc7a304ee20c3045dac6f920018307972d0dd89742310af8d47a5743a0a1ef6062c2a20b3ed69037c2014e28ef9a3aac775e1e6584370be85b4a819612cfb8b6572ddf3be5c0f08fb08f1e0db3f3662eb3a39353191b526b8bf8b34cb6ccd940e0305f825e9820167365de42895db0fdd30268929807b1e33fb5ec3b03ca858d999fd1564dc560d1e47dfdde53f7f33a4a9bbd19cfc1120bb9587b227cc66e167387cc4577c7f030fc8851f12b864e838993f3345dbbb7c0666dead496576ee9741ab2a59bbd1080fbaf611f27bbc75f61bd1e057112b7447792aa66368742342d4135125c2e604880da4b4c60441a185e682bb2ac4b7c058a57d202c0b80803fb8861b779ea907d1f330231d1ac52892964b491c9596121ed9b7c9bede3d2a3edf357cc90fcd0406474bac7a8336475a08f72197d4254945e21fc14e781efae5182e97dff42907527c20e550bcfc2562c1021db2d5428c468ebb05ffa2f331d954a6b6c17e5f0089d76e8160d4853f09b487c5e7b4fea5155c3ff75dba9c901521340023a8260192212fd1a609fb41f134aae46765f61f570683d22acdb5818efe8d66afa40a0a0cc80b8c490140b45d7c81d047fc904cc18ab08cedda5b84be70b2d3b1e9bb06ec5f548fade2ca1c7216277214f0224c0352fd68c7ce507ddfa0e5c623328f0738c02de215875e6d33c711741de7bf76ef001dbb24bc4587456bf188eea2a9049e3f8a24f4838f53149f39abcf3b8db04e630f32f43d12fe0bac999599c7b90d8164ada201cce537c62ea347be5fdcb39663a1c9bd289ae4942fd74d96daab0bc935f68b5dd6a766315300639bfeabde5d1d6a223b280271328890502bd1930245726800514c07c9be566752e866ae60729d09dc97d074b957e179f6a44a540496f61e8745825ff16e15fc3856e130668d07b0ecf796cee6b2f2fb235502750d4194e3c34870ddd5053edbb8574e0175e7dfd8a231cf68fbd78605c8dc036d0635a437a9430b0b389007f44db177a7af0160f48bb2ef67acd7d07575607c6509e211674f5edf4963c59935f0daba9abb96f7961cb8694555fe9d4bded6b84002e0bc675ec2c8eb9642089eb67b719bf56417487fa9b1d83367fcb9d6d41f380a3d4ab5bfdd5d71f9ebec69ec9ed5c97563464ac525a7334803b8d16b6e7a520c30e00f1d98950a6bb8f61c6437862cf735394630bd4f6df6a3ef215670f02e04c4b26ef6e132b01815c9532046dc93848e61a2dc5dcc05001b013f81a7e1a7078bc4734fc5a865ead86505df732ae1a00afc64fc433051a93e26ff2105ce31045154cd3051bc7f25a3cb45b18238f5f98cec78d2c492057a63376e267567cd0f0a966df0aa107820a2b9a2206539c24dc51550d6576bbf3fb914b6123b6e620a2fa3bf4ed684b934b8b83dda3c02f78df7906eca53702f88e9f203389797920c58ff5af449fd2f221280ae14d72385ea93e27e76f0b79d01870a59f05b5a8d0e1c9ee4ec59917ac44a079b29e99fe39c2e50fe00108db75c555fe8902f94e30afdc379f41869f5160b5d1bac8bd97a5ad5404ee3cce19c8b9a23355dc46fb508fef4a4441c08206041807f24861ab0767989c3b2543f3ca7c7f5409060cc0f01ecf777de5cb71e7e96b2067f03a33f819bfb0da835ec0d17c01660ee5f0c2e055962fe2fdd080fe2f7eed9b0ff97d7d5c4b622f4ce07b7aa568a04d517304f0758745736fb1a7145b424ce10f094e2b7d89b6382990316c9d73e6a3ca7bdc40ce8b55fd0ce1d50a8a4d70887f14d9f81c18ff07d6bfca05f03e48087f57f4206935584134c27a04d2ffb426e6a1dc4db24eaf66d7eeefbe36386edecb523dd04893cacb5313a27ec4a5bfa3287b12c33d8c3b03d2c460929ff03d6b034649604c94e5c8f4346e90cd6081521721c1cc0f9d4d004bb37b861efed3742e89d2c042063f664369d88651f7d3bee5183888bcaa6f90c164ad975d0aac53e0067190369e417c4d2f4c1b6b2c45bc669248e3f33494fd1d8351046cec89cb72cb8580b277e1e1a06ae33034c0f97345700704b042ea20957af9def4bbc95938ae04f0b4661e8dd8d2d0724b48116dade44f60f9d20d5bd81146d872b6998a186ea8100a0119e6035ed659002aedd3df5dc499dcbbd8e5113f90dfd343b4528ce544d0c6f36d0827f7e344e2beebbc7f936091269527f211fdf5f94a30b5bec8f6f5b0a1efa1b31d8fbc9f813aa93b160a5e34dc91f002d1393dd378734c38a1c98340761faafc40360a4ad15c4d1ed7758fb3ac342727bcef2340c281b22da963a29029bfb11e972c51f4deb013d084c4b0a87766f1b2d64706c905540a53ce981ca00c1b542478ea242cbfd6c9c5598f8b13d6d6e5165f006d4372bcfe52fb5e3ee08d6e830145b2dabf90c7705a369febe5d7f8678e50bf87fd54983e2ec2966340c65dbd329b6368c5d8bcaf81fed02cb750133a98fda30b9690e2e682d4d515f0097bf5647bbf4a88ddddcd33587493cda2f60921fa85098651ed467f503b2140c0355551fd94007f9826dbb115285b23d6e18026184890f1428654a444bdb80036eecaeedbb2c7b50ae9fcd88a4a72dca60d1114317d5fe1246ef4f000d540d0aad8463682f712db7ebfe01a5b50888e5ab3eba301459f2415a55ae68e0dd7502242438c1d9e06434258cfe1f98d4146d68524bfb47697a830394264b9c22a70b0633fbd43574ba98d291d8b5a4782c0c8a97ece212c6fa83ffc07eddf4106d0c6e33f3ca76b3d5bb98a7d3206d877ee243f302a2e3f65f942eaecd3edc1e620335a6cc4f68c94f55de65ea449c5402df9d1023657f974b73c150b10bbb573b00c6f0ce7af706144ad3036a239350c6e3e660fd3b516d6ceb63eae777caf326018e98bd34dfab5a88140f9776d14a6947166a78eafb9ea23aacc9604d0f8ff80f50204c30687a496e07a6d33aa22aa85e4c2a84c01c8c3da0a6aeac9ad519a100d3e495dd1221f8385bfb0d61d3cc7dc927d8c0d6f4ef4559ab489b1433285a0007f8b0dab15d1e1605a461377f43462fa72f0814e089c98b1d2c9f3e7f11aa05f30f9455f2423b15c4b3983ea945e7fa849349c10004dc95edd168a4f5f8de0f2f73c966ec6b5dd2de6d40297e8d895ba13f84c7db7808abef965dbea8d8300cc51fc38fa3c88bcb4a111aec6b021f228871b320d0eff1ef29c0606fc9916b0b5d6188aae06388a2d01c129995f74889618a7ae5c04a34d306ec6c09c0d1930d12ba47f1cc3ee3df515a9e2930cdf8d159487a479fdbbb766b00737152057a09567670b3e246e8c7b2b99d40d3d10d066d07db5ad92ff2fad72b2e617a6c950cbb3feaeb607fa31fc3193a8a007b462952cd23540692b52dca3923c14dfc4908e584210cb639061b0bfe3fc272369d1489e616471a59f33bb14317c8e02eec0ca7c9f85f76f7210001b173e73611c889cb18cce4fbbca4a1716bb61c2b6f390ce714e3b9c7bdc68c1a3fadeecf51aab6887526836c9068ddb3090477b117f106b986d8245df75c5dc49bdf8489c6f564769a2b3154f91d1f15feda291aebd6050ea0092fc1f2c0e3c3297ca08c6afc8d33db887bb73f8ca5fd9459b9db902d0a0ddb2148cbd2c95feefd1ba20e6251a3c26af2c7177420d14dca1396a21acd0085a4499e0a11eb7e931b89cff44448591df0541f223d59aba64c791249b9ad0a40e9596cab6c573e9f3235df84e06a9e46b4a86a78d76c15e78d1dfb57f40901509102ec1c52e86d5fac30867c6e5c486eaba4ee5b8f02d2ccc3c39a5b727c04b130b36a93ad5f3af937122c39b3f65fc1de4325da2e448dd620005a1887cd0525f30e89ae32194369e761f6a7c4297a07f2282d1752eeecba87992ddfe2e9081e0c3a4ff7fd61b54d3042ff412b6fed0d37eef113e8426d51e259cb0ff0f70febcc15657c515aa4dfb24907210ab501f59746ecb66c3521776f20df2a6e3208c30fe86b28b5304c41a7e5335126015e738990da7902ab3d023dcadb7bf3250dfed4343c8bd78fcf6282aa7c1bcc4430c203f8978c247d529905a17d6325620f771a2bb2a471e408d016723df25fa0e3b1c98bdb66489645bf17b1391275cc01f5c65f8ef385ba885ceb0e89bc572fe2998cb6d1e1f280b60b74ff02d76ad402b136ef51e88685e0e67f6b7480d3b196d518c19c0868627cc26413bc6adcd80a672424774e8e81dd9aecd89c23b17cc3533d1a7c7ba127935b7a9bcb60c86a016144b236bd474215a2ada50870f1c262c0d7bcd178ad2165c910e7831e4f960a21167fff5b24f352dc1c464a2122c0474ff8f1facae24334377ec6f4b7ee76011939dd9147c00c3f1d450f35550e4405427d7fcc9812f43a88e23beca1a54800eb8281c5f9d872d09980d1b9d14911537be1ef8f35fb2f0ba46bfe1d0665740f99d197c040669336b6c5d1a4fc092dec0f3d78a4485a3a55dc14dd759862120c5f63e32aafb9f342599db2d8b4f79688c7b7a4fb47e779c902cd791555c6790cdebcd88a563de7689d2778767165ff469336e5878ba56ac03475beff6a76960a1e88e81d069f87c994e94ee985b8430fc679cd5591e9a5ada7b590a7f7b4680373f84499324b54b9314d59be2e46158eb1f5a30dc1eb7a897c11d9890b30a10f5077d2b1d4ba003d96560d6258ed581698b3784d1233652464ffc3ff43ba3b071e369eb512a24fedacd9221672a6ff6b898a4478bfa46d4656b8de3b4feca0000a92260809cfb85305725cb0b3c3719ea6d93d8cc676a3724582d73ac39c6f05dace23ee0760aac0c9fa47ac07eedba04be289858d0c1efe3d286a0b2f67450f40da4fbf4388592dbdb652c606e8613f5a2c2467bc0c62a213c42c5dd3757009f54a4a5bcf513cf271528d1935e5281188bb020aa58c59a050a58c0b199b1702239783adf0db48b71e27598eeb7dd705158a468c32ccb460ac98c15a957e6a0a1f8cc30f52cb1a96f5eea4392729a5f7bd3180be3c0d9646acf7af37e27df40217d1a59f9d7dcf8b90ae149876136bfd8b6cdca5f397d77aeab029384c536f0c9cb01ab73c94e7a1d584e1b4bf364454c5733b9cf183b785578c07e4fa82c307b036f8855de42824d7c35ab53c00d9c3330ef8e7a3834de859ef4a2ef8eac2042b5e1dff12aa47b6d09f6dedbb2b55f37b2cc4d1423279b34e8c6444662f8c0cce3f172bf7568fc03223e6d444008a5240cd890acf1ad65df9f688080dc1a40a0e1d374e1c52777e584bc90b9b18c5d4aeca08a1feaff59f1f41e826e8c8d505188c505f8adc82617238e7fd803f952e60b1e5f8cd08b10c372e41dbfff47506bf29c3463ef445c50d29ce4adb84e5e42a663ca4c6bf8ee0738ac6d48cc5a3078a122dc7c204550333f347607475b905524f0c9810f7bc48a5bacebff1e99302ea3d2d8fd62853a9eb6598ba90a9dca5d70aa6b3f8d12a218bafe5d4edbdae0081ffda56f4a43cbff356b713a493b5bcda937730743180756b55379f52badc09e1310c96aef03a1be47edc6edbab53296153d73e09c7bf80bac46147e45bf207c89686a9ebe30e1728906a724cdea902593ca327042b87555949cfa963fd9101ebd2164dd0a7acd5def2111a6c90b0fbc69cb18862fd5a4fc65de743d122cf03fc6f75b215f3f6732e87521b7b8411304c6095cb14b8ca2fe9bd0a1d2bb7dc0d53689ceb768fef2443d877a513f62025149b5eaac24e37a94ccb391e3425840a2c29cdf0e48f182089ef13fe25c05a45123c41828148627150ee273e0258e10fca1b72b90a1f5feee739b1bd23f9bd1cc1c38adc8c165e261d1c1fa25c99fd0ed6e0759d9fce7d30a5fadb087f22bc0a3ed8c3b87dd2576b77d49d7ba5128b0174491db6875262e42511b45c47b9f4c0b052ea6bfb224cc001259111455b450bffa7ae2480acfb86a6eab03379f10393b28fd397c83cb4afb0d842eca2b6b8031c1e6e1b4a13ed135e1c09fa94a28ca24014ed9c53b5f6343eb9087f0a8e260d988b3bce5173db2ed1c64347d978cdc22461f0b19014af64cd4f3b9f94c99a0368ee4c2f5f690ec0c7edd1f500247a0036252c112ddf4eac4b6df369c3dc44071889543a494af4b341eb9b9c76fac772ecf6f9caebed7cf9eac5d8eb25acb70f8532dae8cb44571fb910a655a0acdc8417382e48e63340cd12a8a04d72d9af04f228bc40901a7c0606aed19469b8c0b7288b65b6dfb44bb1d7c3c0d239a2c002dfc1f4702474c29e906c6459cf1804a439dcbe9314590ca11a3be207786e4609925e864515d4ed9004efdd0d5fb271773ba168a7c213dd855b7f6b0b7413690dc318dd26dbee3877133222ce894b35f8aa154641039f9e59e10b6e3e79fc7309c02f81aa137a2fc8708ec624125c7ba8eec62b62dbed605e11d4b36ba4825909345e0bb9a2cd36334f621be41d6dd2d762aea92af9b44f38c8c664505aef65011ddbeb7da667647bc3c6ab0d67d4f82d927a8570db6581697cb9631b2e559507377c40f5ec7e8f98a1369addc5844032d103a0bf92e539c78a4d6686ee2b110dd62f4c1a42df66c987ae4cc6175647e4ee91f6c94ccb0d9eb85dfe1bdeaff4083f1e813939b8fd22b81f29984d03455300365230a593b8f06e1b7792fc1d930effabc91181ac59d5704f2723087ea06460d5b6ff178807904d11ea7ae2db14056fd6d17e40439416e9033f459d34aec4fb097f35d4658ed5c2fc76f508595101629a6427c2258b9d68acf2ac387bac2a99037f3e873c119a85734010b8db530ed76d392d2b305ddf6bfbc4b09d08bdea5ab1614c592159405a9278cb005e9a081073ec9b62f021c34922f1f6492150836ee898f45b4419faef8ddf00b8b4820b8de9305380b0d1e43d0eb1c07bbe5d5ad17700068f9138a986721994b8289c0de766953158b32c9478811f3474fd598631dc3d65f47b199b72ad7e3c8648470e870afdc49c93551232a26191c29e2b5f57153433850e168168fb147c49e11002ae89be456b74ba52deb69b9570d2fa2ae32a697f2ae1ea856654c084bbf20903fef636bbbe47767bd945a6251e5e7c558cd2df087c2c0a1889bdbefae99d360db89c38fb9c21b7b0373237f1580c3d9c7e2ffc318fc2aa4c60c305246fdc0709029dce89bf1d1d789ef3ae6f9b76f0569baf12e29391e6a3e3617d138572b505859db96c0c7cd99e0be497f3d782809803928633d23e448a6d56579d0ddb4109a2e1be866db306a52ade484eb1a809f9495fafd02e840d79672b5c2ce7704d0a9ef43e7f7e742eedf7fda31a64d27181914a0d4e4ae84755e40aa9b63e60eb081bfafd5a652473a85b49f65810dae389b3a21d77fda864a767defae726b72e08d65894fadefaf7c7d337058f507886b17dd8181f0a73da72d0351971f54da2033cff12aef5d7bcead254e673e6b6ad29d9cd7d6fe64586f632167f311434cc0a9f84c3bbda781e4611f1a43283e04aca6a29236f3736cccf2470a1f6b2c66b0230e9ccd369c2a29158c4346aa22b3d174544c06ef0472fed65fa5d8bd1c00203607086a8518333a3c49871ece391d1b2617e3304733cb9917bb91f26a0a7a10eaeb2704149c06f529b56d40f711b2a8174f97c9469ce24a673fc043458f173017a5da21701e4cee836d0b031d759a77eca42e51380d40e78d6b5f97b5ac358011b5c729d779ca201f0fe1f774e3ebf9ccaffedc15f2c48ab3e4e51f1f544c005432906c9b8cabb4aa5db3f4db695a894231b3430ad0f063cd36ede5514a690005517584059bf7f5e8e8fe8e4bc4b60612045d10a7ac326f2f6068fbd8b39e005e91f5e595958750acbfc020b4f600a063a70430d80989adf852541d0402f1b0e4f0d55ba290f589fffd054f5d1816fa6dfccd8efbd03c816679896a082022f0657d98a282f396240140cf3083593c7a857bd66b1d80cc84215400f1675c28c0295fe69bd5859f9c40393843e85e37e9ec195275e1d7f6462dcf17520e4a2610af5fabf85b7808ebffccbb527d19cf66383b805ace871f9e08d3f68bba9b4f8044e423512888c9f89f373453f16e06dfab96a2f0a9fcf444e3a1a2f4e992513099f6483cbca7f655a635ce5ffebd53ccbcc9f0d8e476ea7a6b4069aee314b8f007a1e59c43e5168a7ef9d3aebfccd19e4cadee442f29216ecefa34834d004e301bc53c5642c25e0ae477536daff4763ebc47106a86b8febd46f37377799116d080eeb18822cd0574f3b69beb214fbc64dcc2eec236c853d8bd1388e87b30dd1022dc4f4d5cfbdf14fc8133d34f939a68ecb4743575a29023c27b219980afcb904fbb780e67d7a5fc448e75025026792f7dd536e1114f326a62b42e328c8017908f62b79eb9e5ce185f0eb47ec6a5fd123cead8678f057d7104def89c33ed4f802fc1fbff2639d6266dba0b8e2ada90659990029d981f77f401dc91bf21e1b0e0cd2a85a4308b717d606088097816bcb7c873744414a9c183030065b85cdd03e05c87e6e17f946ccaa757f8e197527eb2ac1464603abd79966f9e9b027b184e308d8dba31b871e15b1ffbac31e5696ac1de3d0b10b175275c1197642b259b4060aa8737dbf1ba00f668b16c8280c3010bb483e5e23c115a4d5ec95e17c4d4ea503526ea47357f2f7e832b0b27d53a50dcc89d08088b0d2d479b9b05c7f5d30c200b2d0ffe5bda0f6146b0120ee094dd715ac26fc2f02af6348fd90a1647e42e10cc23feed3fae73b44d66d96406c8b2e6db078f2acf6b41d630c2dc9e4eb4f6205e466d298f19ef54d39cae44eec7e8c4376a07a29e75ecc23009462791af6080c5239284f9597ed092fafc135d13b3d429a6d9570a01b01a6934340cd5ef1cf0ae09f446896b55d35003262dd44a3d267421cf38575b8f9f9f77f03180cc4da03de059651ccf7403677e90ed4efe7ecfca6ec33a2d503ed736cc6ef7ab441fc0af4ae6d59f3cf21d13cafae385fdc69dcf4bd46302e99bb607d1ccb908a702a0ceee63969873a5e1bf22d71bfcc71494740d2554696f8212a45c0dae7ddcfaf077e53180180fbc338481717695c8d15e9f0788b0b569da6f1cacae9e11c20c50cdd90f06d6d9cca25317518ae75877590f5a1028fafbfe9f276cbc984ac5750028b41934043addd2c1db2b751e4611ae012a8a006e5010a63b393d8d961249f08e0ecf52d3b560935ad4abe6f79f8c2411210446da5762d738dfb7ebeeab1ed05ba3c5fe2ba9f452d95d698eac49ffb0bc224d33e0acae72032d3fb4da8ea680f84c1e2264fe4eca8f3daf745a26104d328cc0e53739a3f3a5003e9f56177e9045d5b38c71c6e2d16cfdaed2a581a5f764e61738b800b9736511da91eb9f0680a3dccc1a449ec45eb30117d2007cfd8446eba1ce4895704ee5139e57573f84f09ea428d6fa55354c6f5392873d60399c488650b23837c31f745f55229c08daa04fa6af35e36596b5c99eaa6e00fbdd1e303c6cab3e5a12d47a42bfa1e2d720909a25db19f675a2fd8db5288326eedcff089d0ff7847bdbdb60f85bc634e5e6705f0b1e839a80b1abdbf5be355ed251015814b02089d1d1da93cdbf42674a40603dd6170d9a0e9cc61f2bcfe994724911c3d65cae0cdb581267901e1a0fde64c0f225e515b6f4179f2e0926fc0841271d988d5dad7ff5ecb4e311a97acd6c5780060531847180f348918833a8fcad400d0ab051900164c9e056135b8dbced2670cb7622b61886fb9e9973bd197734e6b4a56cd34d878a9092019941c6ac8eda003a92607c93b26edd8143db6a97da50eb77b0b83346e78ee7a3b3489a1f3614a070a9d85c1caef17e51bf0837e1aefcea58629baa36ef58e5055ec2521462514019be951aff05570155d39b748892083456a47f97a48bea30db7a76bf3780ec80c22ca58f1608876e372d8b531e92ebe19f98d01cce115a85bded655a9d1af3908f0e898e3c62cf439827741b648061b1d32613e8b205b7a6dfbca8b0cc7ad98003fbfd8a8b5517fe1efc7dee1f4d6902d77a2837da2c75e6a8de06378889a820ffcb842107be56e622d7be982f6becd7f83e2587bbfadda3b811cc8ef8a4c590a70718a02bd3526235f35f608b663f3262c4f497a9dd7e4f2bd7eaf891093e90e713983d6d50aa5fa0014d05a3060b3682d79e7ab9b68337a71e161998a2b260ea9962a32b7bcda73d0959d581a6e0987f5341e430daed6bc160cafc8255fca0eba831a34114fc00d0cfe2cd2462718dd713db13714b313b4103d7b31136e9506e71f0f41f3f3b7478bed38cacc35280cf8a136383c26f2f353b0a29a8cbe7209e782670ccb143a168ceac7acbfbc76077632d1a46eea68704eb55d448786bd031de3e068cf658fc47d1e7643810b50b8f6bf673b2859787c5375df9ecc38ac0facb9f27c706f2e4989c0a13bdf5bebbfde265953a72e090b68cd51579a86630daf3c0e780127b3d99420f8d019e82905c6cbf560ce73d8d3d738a6a8539b53024a678a6f801a015bf8685d5bb6242cffa8ab8a877ed9df62c218cb8213063c082248a9bf751a7d9d852480574731fa62256eafe6d587e76ac352412e8262aa06477bdc2559ab51d233173aa7fad8c352a699b6c6dc57c5b2b4315dad510fa50ea5d2ee666153685779c39a1b7dd441107c5500ef822671a0e242a6db69e903075f6508b92c7b180efb257e300ff11422313f466370453743dc0687ccd1503406c3f571924ba8a7331e34a35f53ea59f9815afac4631da04bab5b6a3b774df704c1f013b79b71a5d3b77565f2475823ef3b50bf6c1dde7a2d4cfc1a118ff32b03c7743bd8e17abe54254117e936889a53abce8ab02097bc747e1870e9d3cf73044d8290c60573241bae7cd84c0821e1c3b48245f02b4091bf606769e571cc9a02dc72d352796bcc61f669f742550183953e309ac011661a75a5bf610a477def0438be875d6ecefb3871fed2a38e09ef44b26b96439db866c48694c82a58944603240945c269770f54d78c9ca9b1aad1d01d426d6bc79f3518a877906207eda1022a36fd73dfa7461ff16babcf80d4375c48e479f5da83dae178188ec4a7ff2d09fcd3da5e1bc66695f86f6cd02fb1d726834d6c4d91d18400f48620dee48da8013866355d188821e3bbf195ffe9c42fef161be5de0ea20498816f4aa07da1aa0a8c7b54603c6bdd47b100efd660192d632f92290bdd89779b0513e0c70586b801711267dca93dd6525e5ffb3b6b0821931793cddf718fcecda4f2657a434505049bf353fcc1cc5b05d12f5e7fc0d5d1302f4a6a4f984f5b3b1612bac0b5d48f009f6086d2fda20effcd7790e7aa6417efdb15ac2af690c6895265af944f478b0b6a4b0663a77d38e38156988d14614f5a783222b2ffe0d15946be3cf3b6d70d09ec821a3d214ac723dff567c9e6817348a32be801786c1a3c62c43a399ee2b000d296a5f0f209777369de42540dbd0bfd989425ce3fb8f97221c1ebc8948ee505f74566be3469e867f54bc2194c0a32bfb631a2b1fec6760d543d1d7b36d0fd075387b2d9f7493f6e0ecafa005a14eea10e21b5b72d2413ff5ed7cf54d6669405213e02e0e6877a26a8624733f3ec9a8d96d3ec71cd8c8525b3d5ef7860f58508854c81adc5dcc43fbf181935cc3964d67cca42facc0d900f31bae0368924300d8682cd299715e4353c942ebab64edc43c5348c96bdef81a2e591d8dbdaa64b0e8880ce1f165e4855614e655fa9df3781e8440f7d44c40fc06d6d6d17aaeafe067345f251fbde4eda6ba4ab1fc3ba8570740b29e6704cbd465d1dfc960f2350060a19bc668303b058eababe4dc5291f3ba31f66fc29df932f1af728e0bb87110eb46d39e2f14c2e646f733ba5755810d9b1dd37e8c3a5e832efbef0af9e22ce09f804f42543f6cce09e9fa729a614683f232b8f64713a784ea408cc0b4e782c0a15ef0607fab9275c547b5ac534f14672d4be1efcb368ee847d006f4627efcd0d3027daa35bb6d0d81fdedd878b34a81dbe8ac18a65f87ebfa7d187a6efad290c4445a0703aae5eeb25c51954c7a36b7b5a1ec798018f45557f403964275a030d36dce85daafedcf8de15d0bd7bc2aa8e314872662ea51e3a90a6680f83639b0c9e4989d1e25a4d938e24fb7be387830e4de3f8ab05736842a3b4adc26420600afab8d888126114d655aa3cfc008a5cc0155f9437254272bc8bc188f4bf491a01707df5498ae67555881a09b7d8727c094f1216f02b4c5316ce857027ab5bc805404a343bb827b3229d5092c570551b1d4decbc70c777f7e0c497e37fe6be7f0c66d3a269b3fb14350a2d76ec9c25f78c24bb410c5741c3c811fd7fe9f391b60aadb9a1604be8252645c41f1049b53fc75743871264c4cee25b179b08b6e1ca0b3523dea6d746321f02805f38de0b7a424a146bccbfb22d9a2d0422f7dfbce90ab2f79339dc69947408ac76c1602ba31c53bedffe12a9f53d91d77f2ec365f608908b4dcfaeb9d55751ea6210d93c31d173c774fdfa1aede98378dbb41294840e5d59f29dd1efdeae48971fa61f4018c91b7c68716a3d523e2ecde24985c0ce0ad67be6c23e4774fbb42d0d05a92356149423a05a3e4c39aba55a48c257f23e0372817cbd832327bfe6f81339db0f8dcc75fe56d2dc5e23b56dc7f497f232740d2e6e4b17f58f74ca07b09cd638806a85bd0f17d0398152f096652501b37901050769c69a4588cfffbbd586caff2a5d200f0bbd723b97644aa335eb26a7a39903ea8989a0ad3fb32a77197fcbf40a3b1839e37668ad027dd440f0a4a460427201a3e931f1fec7f7b56fff55e0a14d5b57ce5056d5db2c2467884aa6c0aee8e5085bfacd8a98b82b1cea90e563957ca66d593b391cdaed0474a6297c3b3dbdb309babf0eea6c8f9d2982d0a3f3a65eda9c0a2c7e4c5ebe1c48ea4a53884a251606876eefbd738c51bf3820411b3707ac85a9d1cab60ed09d2b83a1dc934fcc600f424b72aed6b1274de8e1de39d928ec31e1749c1e85152eba54df71d01e57020425892a6333f72fb28f2cda61ceed80edc303914439eb84725c295d742cc81b0d1af82e54f8568a83f81b86ca7e2642afb71318d429d47ac98cfc4c66d8ccf70a407f275fb964b8343d09d8ab81d2a7c7c068be95d00e80eb43636958fa4da50ddf4c74e240eddb13d43299d1cedfeb9957f1f9952a2a4a121593f5acf92f6100fac5187deaa96bc8730b7de520ff74def4293f0bcc4e4116d797beb8ab7ab1001f71f9ffe053d9476ce889e6a441713413314e94fb1f2c327d90f1fcc2007a07e9cb624684daa9e16ef38bc9d11bc5c0db4beb44961f9b575ebd61ae66455f02c1572405d1788ea92162f21df23296eb6e61681b542c63e4e5379012b4b0290ed4491e6147703ef67f2efed7de8d74b31e279d2f3df69dd7d11d9b4eeb3ff90c5921758a438f0192e12621ce96a6acbe3595bb607b83aba57cb4a26b609be40c87b100db7ae2eeb104ebccd984f0a513a9d4bc53c23a52d3726d40f2d636830edd08accf8c5553ce6be9d49999baf02831e5bbaed7008eb33aea4a709646d309246a03581e851a917744b58b20579c9463afe03997adc7ba274d8e7333b55a03f748bf6f3e2accc59167bf2a210040d6f378742a5c8ea969d90482344bd9ab08cf41818d93d0be0ed4dc0ae0775ce5229e4211ea3d8087782b5179a155f7ab0d2e3ec7275fa3ad4e6b37696710b389eb7f040fcb7a5b305b418051b6b1dbcc045709ae1ef409db64b89776c6d73868591b8f4d79df1d052e0fc493acadcc5b05274cc402603a13aa0ae7ef136d562f17f146ff410b8c15abf447cf2a60ae7904a15e735e7a0bf305f36f55bbc04763053bf39478c9fe67a9d3a27dbb815599007575caa6f8b516eaa3d3c4cb93bcc5ce58296a264cbf3ae884db2c15e0bb880e3d0d0da995659bebcd253378538434d5559bfb100d1d90af3b67bd655f215d0ede329ad8ccd5848f78b4f2cd3473fa3942df9b4b48d36aa15c405fbda9745a0b3213adefc57935842a95aebaf7ab8226720854e1646d767a598c29c1fd8c1c0390e90ff3d856ca2ee5772082397484c8a2db632e9f77e7b1342a8ff1f4c33909d599309e6aa7eda3c1126d226fa06c5df68f64c5a5b7838a371b599bfa80070c5f0979d705b42348ec4e5650e040d8b50b141703e3c37ed4299239ece42c040a635fe0e8d910254704189bc81cf39765c613bc6f6e0bd08f5a20511a90131b03d84055fe96c6f640bdceddc72c1e12d1dc2a64adb50ac09b68c3689bb1f0d30bf5c1e5b6f26151702a5c55ab5bd0f11edbf768171805ca4f4fc06ffb5f745b0dc324468f0312bc2c201a953a72e521bee482a5d729bda90e7f191dca6ca9c704bdab52fb57e37b79ddc326b87ff449ee5547e82605623b17291c635ce0366f025159ae9e3b3cfb88e73ca8cd86b49d5c7c6e27215713c444d3649c3d2c8f3e0c46a7c3ed27d47f8a17b2f070ab9b41a09c5d536874cb1866a43985385a33e10a29676ed58a2a7eebd3fdc995600c2bd3e506a0c3f3044e4753fef8d3c1a42c0f70f00201e2c75706daaf20c3068fe9ae804b164c5cbb476f375d447d103c38014f60998f7e2d033872932cc5954a99f6d0b8522d2c94a4b1e638b5fe258cc50f28866f229cb562b9aa20d2bd1fbaa4b0a975cf297e456cc3b6894603dc7f800c9291d54867fd806baec04df5a992881ee2ba437a287dc0022d8212428a39da0eab34b7b91af79ca3d7a290c3ddd0b53971678b091b8f46b59a6c7281d940b70324df817ae66652c62653a93b7b6eadc46c6997f29b54293a58e2da8dd69a800f8cd3789fa4373cf95bddf392af40de21d7b2f0a2f4c2a71f8f4c4b9b609a02056c3846e6a13be4e5c947ffb12b9f050d21647cf2f8254f944358d63ed809440d5c42c07d9f5029acd68427969c5a18cc0cffb414f0df40ac5be6d805c10fcb07578e7cc2977d77f3d437368db3b3bbd9542d3db76f2addc6a79df2da8765410fbf92fb6a768cf084deb169e3a8ab4eae2947a16b1a7ece2b4d1c9a97e1013c0a5c5ca993833423569c4e2267350dd45ea7b1b4d1f92fba9e3309f28e15b004072dd4a39a25933710e99f63a31599eec6c2146282e1b3dabdd0743eb9c8476c05b3afdd804c47bfbdb592a34e65fa4ac5294abd13fb88d177c4a04b8e6f0543098c810ef6d2deebe87606eba60991c0dee76740571ce259483abe37d6715f4e0a7510f6a6bd6b492da103b30a0d4153e29b5cfeb6790d357a22f8556587a3d805dceed001939022c2251d2a355b1a6d3a994e2bc730cb53401dc94c3bc7366902c1a7d3469b6a42aff3abf1d1a38fce451beceb1cdcbd3e7228385b432e7d0f044841749873e7086a692be750accf33d2d7db400d952274554a767c6994b5c907eedf3b3512edc3681d394ec7e8c42f121b3edb7c2831f344f12e6bd61e051d06239fe6e6521b824c05b101f72aee59d4f34538e5656ac0a28ffef53de5ed5a0b74a697cdf5cb3125e00f8b2843417df94da955be6c191166017781489e454603472b01e66045163dbfb7f9a6a7975ce931248719c3791cffd95ebc6a09f3e00233ef1bd66f7e0d1d1a734a0f74c09d4a0df6fdbfa0baf38579907457d1a6cc09358d668613a6cdd48171beac167388bff275746e8d58d5d3c83a4ee65827f7007fb1da3d3e17caf7a5bbdca35bc886f141631c5491f069c33ccd85eb5c37bc0659ea58f0ad91fa664635147506a311fc285714b7232b90d9436725467fb0200ecab0f1dd96295dc72b36680aab298156e9835cb6f4d45aff796db26723e52305e2d8cec8435713103b75b5832e9890763a0604646dea297cfc02ee15781de30ab82ff4538df302915f84c3ac611cde892f42b5176ac62956a7ff1b951ef463075fceb1894a7cfc6d11f88fd7f385748f6e051e7570ff82ffe6bf34a63376ef01069402c9b2d053fd30650ab85a49b47f45ebc12939b4422c42077fbc45f3ab0bec6dd35a086369854a766476366f93a750c74807773926ae5626908ec2c38e0eaf87bbc6d72194e56467cce5137a72d0e34615267cd16e9ac352e11435937102a4819e96b4adf93c7a230f78f4ce6304727f51ab15ca55cb40c58eb07cba14087a29d818a3cf53cecda6137ac7261915252423b044faf8a950d49d2b378726050e2ce987c5f46365130845bf29dfdcde4244fc61a92d7a036159a394577fb601f720a5f05a7810fcee01a1d10be2367c584784122266a0d810bf3eaea2b44e066ab56e3d5f3a40c9e75548be8b7d9a2cca13462d262221633e951cc253316903508bed424de6731aed6e540c2efaadab39164ec21567bcb449b130877432dc0dfd612ffe18ea59928242bb02cd06b3ec7dfd71d59b9a3e9a93c5c368d0793f07fc392a4356f3cdbb08fbeb05f00cd84c2c9bf94fd358bb135ecfe36154eeac08f7492be90e087d8ecb28ef3bff1cfa824f7de64e7db9ac1527deb5e722bc840a9b5be3c3f5d68ac1c63104024ac187e8ebe12668104b1cb93846863f04134305c3559da577f98d80695d96fd00f966be07ee4d7bd8a67a45ecf41c5fbc0ec90aa72702c31c1ed7c129f5cf162211e5353e75e3a8423a0b5248756c61e4f1940feab041f1386d23435386764bc01f377216c4f0ad4462feafca3400a584e26f00b80edd68a15bb732b0b444b860f90da366dc7bd9cc8b5be3b8a79bf095000007fe9a6f4ba6d9f211b48a05398ba7c41ff24ee0f3b9373a21d530e98c6076d5089a55ecd34f64df722d52f62e7cccb34423578da401e7663b62812c091b64800ee9e2ec394e2c89a8003d834c9398e4b2129afdcb108bf252faecc55f15462305d990872aa528d05b593c14be02f4b2e9cf7d797fd2e37a1862cc3fa619c52a072ee42d2affeace0287ba883b2e5f5186a304826a5b55dc0e2b3bcd772c89d800950fc86907065c703efe54f7df2c24a1fe10a1efe5bcebd78220ca37e334f602844312767d29df53595eef5eece847f8a4513d514032d2e08dcd08ae98b394028d19edba388c61503957b0a8278f6516001f532d3e075cf5a8eb90ef53734200b3d90c51120ef3e8f8f67dc42ff3f31762a68dbaa8951af048121e0167eaf206ac70a9fdc80dcbc4f7886b722368fe3458e2e4dd168c9b23f6531a46fb65320011a9b0970a30a88dcf905aa1338e276fad01b0acdfc7b1cb89bb22c817243f05feb6165f48e485b31e7165eec94c457dfbf890c642e2e293da3c190de77ecb0d8b6ea711d54185ec0500fafd8e02ec7380b30d1e6de529653cf0599e4e1fbe07060d313196b7c58a244c657baf612e4bc94e318c33b67adcd2423cf96a0c4d0739c957bc71899b1acfb40c998cf8c43f756c31d3a617f5d0d89addd5a7890a0f1d082abe94c8999e1c6d11239ae36a119c33d4ea0a772b9c3d44708f4a2b2f0823b76e18a257ac5da1646dd939c021ad8ac1dea8df57ef641bbccdb3a4d8530a5b2fc8f65acc40ae9bcb429e898b39b901cd5041a731a27ba1a2fb9c72af690013ea9f4f2284a5f2028cd387990237ba12499325072980f036df21203fe7450f1eabe7096d0847a601918a6a71d98fee62572eb841418ac785d0874c1fcf0e0a2ad70b0d770fa242fff7cd8f4d7ba4b600296e896ad72e0be7d04a5dd9dbba0763ffdbddd56fb82f1429775d4a49bd505b607e1a8d4f61f81f36b56d298fc00f6d3ab01ccb381cee9a2061dbca2d4387917bb9800b90ec191725d7a3c4b9ad0292ee3d2a2c073015ac3834d8e2c12bdf147aa773472470fd3a46a7cc237bec0a73c6f85c9293aa013b874124a71dbe465446f839e7cc23bd67fcfadcb23c910b3daa7f4c629c109ef464aa82bdde253e0f71420bf9e26b6f56a584c7d86563020272d803865b2ab8a4bb8a050a2cc08675c7242ef1009be25b1aa975d6cf480608e02c7badfe414d2f594f15008b17723f0a4349437b2f8aff1de90817348d0774e0ab4c5a4073a8b82cf1b4652387ee12453adaf6d14f838f5a6f64846fff04d7583428703096da118d2f1b2901d2d6619dc339f360faad88aee38d45194c04966db6819211a54852460a07e925b07a02c253f88cf5c4d6880fc148cb4a0d0441b5e8495b4886b115b5085630d734abb746d1022ecbfea9c4a8e9a70d08c80100302ce2352ebc7a69d3d36cbddefa597f757ec95acb4766172eb8d3d13cbf0d12ede51e0dee08de166900bb01534dcab3af95efd3f571e04e1e9ab2b7b5c501a1324670a7195f779e35f6a9d91abf62ccc8af7d5d38d202a3cf95394a3fde0e0832d50aef0591d229db434f16af87b6a0025e781004bd29f20b020bced1fe0bfc5db9ffd25b9d099b801b8e6cdd92210576fd8c0fcfbc1984d92f99ca25de05b3f35ab31816e380b62f880285d843dbe666f184f49c655d2f9dd4f794d89604315194f990246c1f364a7397b0c0c4370e864522c88270d41e4a9bc24f86780d8420bf8c9e60d0e903ba63e5492fe677d8ad1e63cf4d1fd13a9351938f8ced0262d2b0188213babc109b770fa0b815fe10f6b7a1ece08117ada6ac867d162e0d731d1b5d8d5a129ebfd33c3831b4fd4f7d4ac7f50db46025e7dc69bd915d4407f945d0111e9451dab2dda2e9607305f17e3ebc283ce932f403792cb05bdc850ccfa26c157569237a26e0b1edb3fc1571605279aaf6b3ad94f46cbb6fa573cd05353db24dcb5cd5e4b4ed11d4850db385c11ca59de07f67950c499ff9b14e30041321cf48837cf6a1c5ab6c9ee2b379c648b270bec60dcb7ecc54614d0dd73807084278c515c9344b4137a35b29e189b4913c9e91974226b70988345c620adb06d69fcae290100e03a2e6329dbc656ebdd07c24965b7a2d4fbccb67bc8cf7260b820bc6332621e251f2c7dc15e0807b74e9dc668cb9c30f0fbd9713d86ee1f6071d6ae87bcea9f43fefb7bcf071e4f5fbe973055de7a17422197a13e32e4c1204596fd80f57a22f574ff0cf152edb4d308e1e628476b4545485f87ada3edba503a862b5461156704daf46f7a39497c9ce89051fe37183396d2fc7f123eb637e0b false -check_ring_signature 4b34aa0e8fc5f46e602b2f228fffdc0dd367fd13fb15e38fcf873f1693742638 9c66ae886dcd58d588aefd2a95645fa42ed1a22fad147dab7295942c05eb4b9a 1 8efed99dff83cefbd08865aa49de3e18485f30db83736ba63d4b4ac58fccd35d 5f1dabcb2e6530c860637276c5298fa410bce182c3be1cee8e8f8a3988db3406e8cbcebcbfa2d59bbe663772cb2a6f83d3533445db720fdc1b42fb2c26d96405 true -check_ring_signature 23f4ee5db2e4b8f1adb4cef0f6fd4eb45b4f424cf458abbc5d89a8491a2e16bb f58e1cc5cbe5059dc512b68e4be623ae56e8afae8b9fa3e4b4cd2db9258c7d00 1 d4d0de842a6269a1fd83485708171e5eb51481dc87877932c5798cca96e3d7fe d77c127b3786dc85701c00ef50e41e96515af68f46649bf17665d9d98f228900d74d074a712265a817136e0d4d954f066085fe88cca624e29ffb0c95cf1e4d3e false -check_ring_signature 729250457246c1b17de11cb7a59ebe9e7979062a8d9442beff5edaf9462a24e2 15130257023e09f9dfc9bdbbb2bde261f545af39f86a380fc96933c67ee9a3fe 4 66faa62b9d4c01e0dddde4998da041cbed6079965576f739ba403d9b7354b229 35d2d4912d371cc0e71fe7c53e657a6e3532acd84276aa730638d36228c25adc d570cff0b5700227500a8066cf2c37216f3a367703008d378ca424809c7517ad 9f8f4cbb96d99cb379c277dd1a28d1b7afacd63a1bd2d3132776a4364280ad15 dbb1ac40e2c86e1f22583fc14687483f48bccacc5116c1cd285b7ccc452af601a70d680e7c386ca3b678290215df24bc8f2fb557e54ec220204b2e2cac652f036b13f91482a163f6f3f598e48674946740040f6fc23e3c6d6aefedf35a360e0bac3af01a775b76ff7e09085ec35a654b7d30f07999b8921e0549df085d1acf0fb33b873c77ef77dc2fa5d96e0605422630767546f858ea9b99d2092bcd6e3a0f8dcadac3441d04dd508ee5e0904dec0b4a2668803972b1128b021238b7d00006916df635f2f4f9fe68929163516f8d693f01c40b5db0996b764d796bbaf07e0891d5f1c0fb613033f8e29a7ad271ebcd0c2186bbcf36f3447602b75d45669504 false -check_ring_signature ac54c22451973de5385a9dd43d4abc7789cb4708e81fe5b317d3fbf482332972 3c9f126e5e6cb28dc3be453240d1ee23cd0514a09237db06c666ae69395288cc 7 cb200fc9266274535e2fa86606bbc5816b440de1777a146b47cd4cb108ef1154 c0f865d80f4283b0b2d859c79ede4d694a2b152b493478be5ee3f3d9e58a0bc2 c4e0e1ba9e24b86cff0c9497d61e84542336238937000e8c183a8b519d9fa356 1af3ed4aa4f3de619ee1f848a42eb2258cc252efd68bb18a99ab90e97266b892 3fd7d8537d8e8e945699c8293ccdaff1e386dc21661fd880b72d17c9c6eb4cea 619f6ac297bbc6386490b1d9615137726e5135484a92e4f5a373a53c253a0cbf 56f85d6a6231ba1da75d75bc9c398cf005bcd94b7042f6880aef91ebbbea5ba9 a45f677d603fefd44ab1b7c83b2ccec5d40f1dfcde81d77edd4eb4b4616b310b091e70f03f12d219c16ad48993e030098c9584d53d20068aa385883939c3480c7c35b0b3c6cac5b7a9bc776f4613f83d53723aa19592d272183152eadc0974044ae0310a1e162a642b2c0736a63da974a1eca2987a0d5a8783e9dea872c0710b8943fbcce97f033fddd772c655faed6202cfbb7b733c5c244c8b8d39568046059a5a657ffe03e7a6983b79637a8d4f83d41d93b3ffa80a7e0c7652f814518504f163f5fc1e90e511747f4bf27da726d71083fc7aeef528fe868fd8a68b230e05c5823f0713f647830ecb773e7211de95aaa0df32c3896d58f9d677ff54121a0efbbd4f292b4f7b24d49b571b71edbe50e7be9c9421a215e47e93f236611b5008a188d4be5dccbf57913b82000d5c7797aa83e4b3250c3359835fbef246608b0e27eb4ab4c46c02e32e09dfd57b28490c4defd00a40d094e61e3f9993ae4be5062f0733e15b2e1518be7d27dfffdd3ebfb295f7744dc83d6ebf4c7d0129562b06b6edbf6309927090a2370ed8c2d623259fe9a0377bed544b5ba449381e1e1205ae841a4a30a0a68d6f999cbf65afce7ee79070647006876dd71c9a1701172002 true -check_ring_signature cce67bac6ac20076be1776ea3cf9f23c7c0e23ef83dc7e9e9ff8546ea827b766 811fb394fbf7e2589815e50c0c759e20dd43fc1b535c2ce53edf7aae33c2b54a 34 c8964839d3afe01336e5a252b7162fa30dc8937a428d2b04cdfb7167df8f6755 03dcc6b69d31841740c3975c3d0560bf270a4c012bb0def436f4ad38678edc19 50a8afc07b4f840956c2092a868b258fdf8850db7a315409c89c2df6b81dc808 23fc4e820e128540c75ceba9db978ef4f0e6e6144b31feed12a30922bf1e4eae 4c3c57533cd404e83caef0193c980d9ceb42abcc514226df194be2b9788b4674 f5692f595a97f46cc1b73eeb43fe14e003f88059acdf8830e3938502e8b647d3 913c2cfbf14ad10c9e2517aa4dd82bdb098219bb251e70882dc3ff814a7d0090 0292c797d9df658714358840b97868b0415c2c4a34f2544a9169979c7ba60d54 63c749718b7db041ea395f53627a6d3998c36dd1699da1581ecf95c467a37696 f7db7e025e1939a27ea6216089a48f2a86c101ccd2cb957e8e7aae72838d5b53 c48886853c98ada835903968b1e130ef0718432e3c07dac6d54326bedc78dd6f d2d65c72395b76a04904504e75f83b37bbec133a8771c071d1068dd951b5d806 fc6d1352aa22c49b170ec5a5bc9861d956e1551d74cf7cf1eaa3483ab00e5fef 5daa1a0c8fda5cbf030f7659151b6f6a89e765e4d64e49b23eceb9da5fd3888a f05c7c443bdce9e852c49e7d92e138e4281a82bb7861463ff699940c3fe9951a ca4b191ef72d68fa20b09a87270206780dfc6ccb85f38e30498f2d553503f380 31daa5e76eccd76737036dbf026cfb7305999fa490bf866367eb64c055286855 3ab46ffa5fd36967c85ca41787982ff8568a9b4bf8fa96ad502d0b02168777ed 134b95417fa7d3ad732ecb3d90b63a5a31e61d914a76b114df6d8a46ed002f94 967ecd5a7b336193e89f0f6d1e05ce1134c051f3eb2229000d6c2fecb0791f20 46e25ccc5df0b9a4710a707fb9726fb9fb9e34520865ca3f9537cfe216419277 217b61c74588226b25b5942a420481cc243d6def57843cc36594ec9e6e104d46 533e91dcf1c8d6d0c5f27ccaccdaa049067b95a0b5728920362cb4ca474e797a 155c94bb372ba303f509ba3a0817c89b988c9137b3c6ac4c2f739bbcb0dfa9bb fc20f84c7f6016d4578aa99a6332c45ce7396dcd92439ca602431d4c30155e86 40a6fdf6b659a886dfd6003c45c922ad076d264e07e7fd5391d1a5d85c609f7d 848ed87ca6098ea67a1e5a5c6422799a1b1c7efe74f6826b69ab14f7d41e9568 3a3427143da401dd93fbbc0c05146f4511904aa1a03e7faf0e27ef6a024d333a 9c347d6691121090c18491b7bed8d77336d7fbd5abdc7d3f8ba64a4670ea6f07 1dfaef2031743459f1e588578c65283a31fda76418158f800f3a4fd349e0cdef 0c5ad1f33956293b8865d3fe18d9b5fe40873509d9c114abef51b824527dc5f1 212ec842b2fc0c3acbd2ff86881b7f5ad39035d1633182b9ac71d229c0515504 d1cd305bb6537dfadf2abdd0ed6d4ec6e9171d78e943ced748c3383e44f337b5 23f75e0f8f7a325001494286bc4289bf4966b86790a13e11f1904a2714e6bc52 b63323dc50ab4e032dbfde87cddbd16a9d689eb5cbaf1471db6ae38204a7b8031ddd2b9cb2a81bd795d373cfdaafdbf90f50a505a21b7fcc7501c2c08b8e3f0429ab7040d07121123185e36e9db037af516ecb5b8c01a30628d752dfa02dc10435aedeb9b1751cf58dbe77058398fb389e1442332130f1aace52653374bb470834ea8b8a9eddc1f7f190dc5caf5447b216b434090f16bffa4ee37dd07b985b04a8ad9d32c68cfd3a0c7e3252be54e7f98c05d3e15a0a897258cfc490e32f040545cb233c6facddec108ca8975cceafa67333059267752b239a810e515344fe036c772728755fe8dc7f2f75fbc8e7c97cf1e01237a7b621902a3a8ce7c5c6ec0771f57b86d2f01067ce042e76ddd0684c4bf66aa5eaff1c38d6502158f8d1970296a937715dc4207d70876128a47458590bb611aea00e897ecfdcc2d84e65a90c4dcf1ea624efb7f79ac88b66f8420484715a286f4e424ef5f04cf93fac46e704515a8ca61a35527d89efb03cbcb88a1ce11eccaf66c6eb30f10ca4764f661f07b647aee0f40895e5dc512ea9db57405850b481fa2e154eec7c13e7879ac7c603f7b30bf410978eae870163e1e0c56979713596628141024dedcd1d48f6116605260481b410cac63694c99bd692ffb03229a0b6fa4f2c87a773e8107c6f25d70c08f41973f6add92dd01ad553cd31fd6c103229154e09086e628a15b6add20cd102f6fe4281062968a3c53c9e15ca710398cedc93cba579f5d80ed08b3e4a600a361dd3bb931c1242e761753087739ea3d548f6a446993ac7af3757c8b120000349f64b7edeb5f30e5cf646333f1c44d78f218e7a843b44f43242b9c5c74a590c1e1adfab948901b8ed2230cc7f317072abcc9ce67840060c305bf35702414d0cc7158c4ab22bbed8f9cf0e123bce3fe743df18d94d92c711a453495a67f45e097e949f25c3f2ffbf55c112986c923ef1da8d8928eb4ed8d6cb8bd9e27240020798bf57241459cd2d67a9a5978111765a1f3a5c69874f76231210d2365e20a40f6aaa852b40d80fe962c6d1634eb5bc00ae0a3a63cd60329b24f9c408e9285d02892f755082cb60052f94d03570b342515702fa5e33cfceace88a0572409c49064f7cc5652a99567504ffde271ef16447cfc703e493e76a0aaf9c7af55068c002e9fcae814c4a2522da1d3c3276ee2d1cb466b51e8e6ab1a42e40ea58c7b60707dab47146333d795cfe8c1f7669fc8e10c4c0e54c51572ff4203bcd6009c9b404e8d782460e8cd6d2c4ea83986d30601d73c29227239053b4f83cf7a0beb83b0c1347dabe10389d769498860c83d707fcb73fbe9d9badb6c13bcc378f98447109aaf2960f32d83f67cc0ea2629a92405018872f307b6b793344336ca0cc8bc702d6b01d2b0e2936d689188927e000fcfb324b372125112fd249c92f4fa8576302643113aeed495c3cc9062122bd450d02d9ed14e787c83dc93858c43c340743000e7977f3812f4520fee953917a5547bd6bf671004512f56ae80b5e1aa072fe02c73ed894c0f7f6f8f531fb608c16d816c359187b5c84b3e5a70a881f058ff60bf9de58e64546342e16336cf95e3ec95844d05f8373787a4b5ab4842f88430004d5eaa92895ed90cea7bc1a3cda3be029bee9bdb4d3b48f6577c9ed5124d0f70b09d19856743fb90ff2a4d4978522982c976ea8fde53d62ff349e6aabade67300926895ef8d9f89d711efe78302f7ded78a518f713b9a29f85a9f37678f41eb0e3d20faf3e06fbce3c845b4efe360b71201bc51e3eb856a17985ca80588d4730976a7132c954b4da091c9aa2a4a9ebbe3e2ff56a3249dc2c887a1bf07737f240ea839281c3c47d2a88cba4dc35dcd93484e85ed71d14435acc40b0e1bacdd0b0c256eff17d40d1646bf110828a06d81c4a2a2f5ab84a54694b417bdc561fb320e861cd9d43dd2b45f67efad4e9aaee0eec3771b650153a2145822b1cf7519630bf950ff0d7f3cd13eb5028ce9e9b0672e49fd8cc784ed0e4306e392274cff34064cfb31a73fc8c40bdfe32ed934c837ca3ea3ed105871c2c958da98b13ca0ce02aa36b465c34ade9af31c6eb56b3d7baa3da5a8f7fa12abf1d64b816721fabe07f7f4234c346fcba42cda3addc61994638f70b99571975e2c2b2db68e55556c0d938cef62132a026e4e88aeb14bece3dcd8fc4a33a6d7e53acc1c6c9e36001f0021cfbf57b57a5e8985c45e603f4a9f6d71ec168d2200b97e056b9d0e5f6fe0081ec71d1d182a15c2c58dd629ad02a51c8388ae04c1cc23573e077fbbc114c301fae248028501c2e88ebf3bbe070b185b7414e88639a261b4df8fc4b227f209f661a97a8495a1288406a4dd0f858bbec0e413c30cd17b060aa0afe28a8f3b1700ac1973d7ef597ab9532504b65c7a9d3b8af17f830afeec1a764f936115e2f10262caef4e56bf4dee30d493731e49d1df35ca61601987ffa7675919fd79e1e50fd4a60b5b81efc9063b2ff9854914901e2b345d9d6711db0fe17eea19e030700e22aa605c4e37d49602514b770faaf9ac862b3061bd415d1181dfeea70795380287430f7d5a710fbd8447a39aafe2fb9e0f2f0cd07163868ddcda0677f312e6006d8feb5557d73c283a857e243acffd20ed6f6c002967eee2359cad4f21e00f0ae630479dd25a83b1dbd4db0d1d7fae5c1797861e783274f5cecb4bc0251051059080167cc38673566ccc0628fc09259e4b28b6aee745df025dee5b60212c180204eebba26fd1195380319652d84e2c96ea1fe2bc830e66866672fe9d3d810104f7bc301800a33c86c1eaabb7588edb5b6a556f52a7b6f2b42bd1268e9501ff002debe382ca67791c9a6615dcffd930a19906f5f61e5a961f478e0eac171dde06935df2aec1f94686a3fe9a97a688aedace83d0868b1e26e0c76914b350036b08f4e8b44dc200cab17b3dc2914a80f9c01de281065c1a3afe374e1c47926283035e6fae39fda38a73accf647a038f37db987c90f5690a63090e9d4ec07e4df102c4a6befe0486b30897068f4a74bcfadbb5c6a82d01ab537e2fc5a4a88454480d false -check_ring_signature 6ab7aa4d99e8c5a8efba581aa5922997afb4a0250e345b45612e45511ae7e4c6 4836a9337843406fe9e8656907e07636a2c89f2e7661f64810c6d11b2702faf0 33 938eb72a4b6277c163e4cbb29508f74b15da607b6b7d09f05a753437ea194efb dc0787fea3ca0c100419382cfabcc3438655c659359476a2246ee8c2100d42b4 fa50575a8e481a384b0eeeb92f329d2ca333f8e8b0266a6d3501131595725883 29fc674acf6596571338c4312a75514ece2422f9b0c3c7c9f1c4488f5b1801da 7fad7a7a9c5d1bd072a06fa02a09b54dc29d0e44e7ae906fc683405f1079ef3d 4b2cf0a0b68e6bdeba417bb30cc670a3797aac2d14afbae749e5141a725c4892 6f57744ce64e53b84f4a284d1de2620b869d0ef0f14ce0cc2122003e5f761e07 e7030a1a8cdb43344e13e40aa7880116dc731587d8df854b8baa4bee4ff0e221 04404a9c77801a8d3ae21e63d843f244c9462bad85e8fb0a31977b534404abb1 d66a1de4994c485a150686885ad6d251c2364db9bb55abf70511744495297244 e34c8a7c068f97ede8b47e604ea34b9b23279ecd90aacb176769faae04aa1b7d 1e7d3cbeba4a73518d00f043b1a61084a9a23a8e9d852ac994b37ba0ed1b9984 77e46b5beace93b87ac0b4bb98026892c21189a576967bf0051fff60035c4287 7c723ba6897adbcbe4af5cf3fbb9e6d05675116ab1ac1400f7ab7f2cf9498214 c4deff9adeff39c9d5884fb7f4d0b8766c0bf6c89616e46b5fe19abca79999e7 4a860284c4f5d48106b2a8f107143f3c4a71fbc52f922273af101d4d214f1bd3 a52b8c091165a9ac20b72a3c9b720a705d81578681bafd0a85069396e244e8da a9c12c63f29d4d1b6fa37686c1fa294329a915bd52f6e4de70d9ca89f4bb61dd b9a816bc29f9cd537f78d34a607f05d75cb0a7037465f9a89030c99fbcff0767 7e16f9dffe0e87f041579b9a9273b8ef1d3ebb87c146955eb810c1de2f0bfd43 6d2daf4a7516cf5584d8494f61cdf3438368166e94eb160c7bf3c719fe84c9f7 b37ec8083aff9c92a272ed2e50c567274e4ac7d76db0a6b1d0061dc02622de77 30f4b004d38b1f6a66f9a5db09ac3a37100040cbed16ab81ddbad89fefd006c3 699f5d08f7c38c2a2e82454a8571e5776f42c17151fac9d32df6fffdb9a93f81 cb7db031a9fc674b3fd85dc033cd1e430036c4ed2e70deb85852030d4a975855 c4f4a1e40f5e3fcaceb349e0d759e588d81e273097c8d3c414fa1e0b4092fbc3 3c898e87a92f03bcaec35e069e31d7f3c536c3617fd3ce45d0aafc3ed1cb4fc4 9cf40d22f206df7f40c337628def79a579500979a8b8dfa325a3333d8988b705 47ad69a46f4c16723234b0e1e405db9d6a1a7a14bf8abd268e8daaa2a3f6fb53 169f65c637d17635bc362bb28566ed1cce19aa35deecf4563720cb73e9ff5e49 f17e8d9561e9d50c64bea540191cd3759f017a95260494b723bcacd708f57ab1 111a5ce9bb6e1c77e5f7fe761994f76abe6704762d7a2dfad45d638b1cc82151 ef255cdae92146ec4a9216aa1848d7d0d709dfc548be4d52bd727416c5a45e01 5ede6d049d4e3788eeefc47d43e5f8a4eaaa9fa9361f8131a1fd1e1b4c742b0996e2ee1f200cd912c613f3184de69e1f6698b5ff682143bbe341e6019bde7c0d2d284720f6041a08cafd2c76f9da2365e01ce36554471788e7c653499791870c6852375753b941265031cbbd23bbe01ef6e4158b000554bde7870a15ed9bb907f9af91b16d86b64a3025882e2d5ce9a9376507653b83d2597109ce0a75bea506dafc0f206a7ff3a58eb8e0be09626907faf74909b51b5ee583ac652d39ed910806ef57d8b1cfe8f06892e46d004221800013f1e37f1cb3379ddc4c4c54d74d03ba6dd4d5b79744627cfe213479d2394e8b4cec92a812fdf5f400f328b29a450145bc6db87d057d6fb5448efb7859a150a713fd6990daf3b96a7fcaaf6f946f0e9d9f0a7b2cef8f4eea723701c784d59d9030e97759dac6cf02075f46da50e205bf72bc70e29a494d1332c2b0a148e8ae128f17c7334faee419d64d15a6b1b5064cd17cf78023a7a512b0614d20d330ff616d487c948c890f25a705485fa4210c4f883e95fae0031940a4fcae29dcfa006ad65589ef8c07e8a37281fedf073200d060be2988999c70dd39c67766bcb25f09bbdb160c9ec0f27a5150cc36580a013cb0513734565a459c80d0f55d25a5380742c7952a3cbf69ce1629be5d2c2600c6d05152c2d4df7cbf0e9827fe6d2b8c71c94b293f6f88cbd9c8113e8c387a042e3fe335859fb0452620c69f8b938c2f78c7ed34b0cd086ab9196ea847f087064cdacbf3dfdb1a75c43b94fd6eb4a5b10e2cbfd0456726404ab3bd4b869905034466d5e38d68f35239b196ef8887565971bfd8f2b6d9de184c72aa6862513c03790e92a2a007ef6919838374627cf66c301d92f4f54fdcc2fd2640ba5c7e8f0b2ce3c05a9d300f0971dbd3ca03d6313533f1f4f2f69c920bbf16797acc358b08397b121782946b43e25fc5de48e4e22a3522efb06653312fd9db9d0c61248e0aac21fa596bc4b03ec7e9486cfcdc17ba53ed90d56df8d820959440bf728d1004e6068789a792fd68e654c5a428e9a83814d51499ee4402233d361fe404261404c4d75f378f0801714611689d969a3c0e9dc7979cad107b253fd03d0e856ca405aa330a935a3f0161f4b172820ffb26d6003f6def757546566360c5d36d3e570eb55deea59d24d3beeccdc7712cbb5189bfab58a77dd82e962ae010bb26e77005c6399ee42280ca81ce236012c4ee2d9e68090b44b404c773b00bab6f36b152089d2b30e945b09d5d697c47e1478d521739f0ad23c76555ef160cdbb8dc1939065415b80c6be2ac6ccb7fd32ec795a9936f00a1438b19b2d0f464f36c13daf00a925b9691fd213dad4b6b5d5522259887b1226ca24cab4f42d3e96f333fafad01db697fea0ddb5d22f36f183423cf9a48d74e3fcbe4d72536553081a32c9002052c63a8a87d43de49974366232c2218015ae3e4e72331861e25cd7e1bef7f3d0620cd98972075bc99c8f224dfc53540b5efd1b3991e810e0884214b8cb5fb3e0e9f6c51e60c65c975dfb0538debcc594b0ad6e1363a80141d595e3b88929d87014eb1929b676e4df9e11ef63d553f630f8d62f89cd637382793ff648e85a6800d65155207423595f7e14892f8916e1514a2d98ddcd82a4ed0dce6398699681e0a8ce4516041f4c7032c71e7f30abb81933a832ca6981660d43a50b24286c20103662750775c846e3c43f26545e660cff3e08b7506c4efd81e2620b5708a804d0e8a0b439080229cce16fd21adbc73bc712556eb0fd2819d11b99c9a127937e90bc9d087c0f4ad38fb8f0364c7c305ab37633e23e4a17f591a3dcfaebc362dde0d4e890d34f9bd32bb1c2f6cc75bff77def42f3cab8d2fccf0deaf8f7f2aa03100efb72aa848ce166cdc7f2874e264e60431210fac2adf071011f26ce2b624a606e2dc72f1acfd09fdd0c2886872f601bf0f738188a7c76bfed89ae6f21fa1880f84f9c9aa52b23a5adad3076420296ab742168ced70277ff114bc45eb8a80ec038fbea3e00412ad96d73e6af0f8de59b29db634d68b1bc85966c5aa834f03b10cdac4a0b853c723ec469c94e069615bcf80c320a0b2f241043ac49d49cbffef0e9f62b1c00726df187017cfa442e50ffb3cfbd9f8c5db0b044c96344e208b5f08405aab862c1fe4dbbeb156d2e7a6427bf6e6fc650e428becb749a42481f55709b0c17b9917d91531dda0b9780660c0733d00c84f424817d7d0a459e5b1dcf800224bceab41c3820a515da9cee14f814bc884c206b161ab8284c842050edcb906272f4c54ff6e73d8c22c138825fefddd9de26d252c586c6bc5acb45f6b76050758aa27068b28e1124bbec9f26644c48f0d7a02873c59f55a928db2d8102b840d4f4e20dc7d85b56b9f6c46e64095a794cf24fac61544f2717dc16aea133f350f9a798f77a86e47e588842403082dcffa407381bd6891491eb581d56ecfce7707adc47ed6112d5c93784dc31f16e82dbbe61d29f847c33bf586a3f03b6ffdd306be10daa7729768fc9d6fc3c5c5004bcaeb6f5f2e8e6c931a03c4f789f249090d87f8cbe241eda3b4a90b521e80bfd334cea4533a7cc6a1156ae5ea88ced68c0f7d1f1f2f951fe305874f20e31e6f350679a892b0d7e80686a4b6aa76351e5a0fc9d69b6aba864fdba40edb85c3475ba1a0e96afb8fef0278923bf385cf03210452d2f6d63e77b6f93e47c05923ca30ee1d6cda1db50273cd6f63f30f6264e90a2ead3a72beecaa7aca9d9da832de069a64755b2292a96d51630b3c75e59c8d0815596a54d92df8f55743e6ec2ddb25dc5137199012d66eb6767e9f63e6fd65050900a9e622034d467c151340eaff2e81c0261654c8babef197e0773b36a0d40b2439706ce218565d0567fce3423c46322735f7f7aaaa8cfc60120e418f66520a08d51cb0d1dbb974bf04eeac05d394a9aeff58d4eadf80904bc60738f4e12f07 false -check_ring_signature a5a9a4f983e8abe7aefee8755bf30b3eafb29255bc642cc59d8c21d94cc027ee 0cd0ce3029f01915685c5735d46643475a0262858cd71ddf7231d7419519b201 62 dd0e9db3ebeb6fac15f0b6740ab5b7a8e0e73f65eeae81dc77b0c9db8942b5d0 f3d5631b42a88a5ea89fc4be146de3bf2d98d82e9e5565f81b86c69327f5b4fa 78e9aae902538f00bd726be7964e6b6165bfdee60c714094f3705ebf550f1ca6 3aa4f3fba4d9b6df6ed0e6d42ff2db0fc0ca25c34f77595254510b8af07e0fd9 4a2d14e2568700d4cc89654986e6811ebac4693071f20bd785b6dd2ee05c910c 6a70c4906abee044fbd34e2b70117ce1d9278e4617f6f6d396a63c223a1bdf05 514a98b4eca8fb2b4074f94a6249eb0b9577382c17618fdc19c2b7db3b8e112b 822fc2a833ab3e2091ab8469b4a5912171a57d058a9ded6822f4afc77e0e895b 88ef0b9685b83f83d8d8ccb68aeb5748268ad2a8fd0fee546f94cc4999457535 6893a6acb1a3e399a0bdbe062dcc9584160371bcd091b7b7e5ec8baa69dcf81b ca9f7e201b2d2bd75eb5cacbdeff2917299e91eef907c00c8d08ee103961dd4f 564ef0638a4fbf16b360ebf3bb2c57e776172a39cdd4cf937b93966c8895f889 1d172fa3adab61ec0af8365989ac8ba102edebb4d65393bb6e834a90c920321c c6b5e0ea00841db6dcc2cb151f35f7b91485b5b5f5254532b5749abf288f2689 380ca04a7e605ca147d76575d0e640b44d9318aeab47bed1d9aaa4e3ce6e237d c6859957039be356d7d111db5596adac37b3b848e53a483589cd49543e34ac50 90cc20e9161583eff84cfe0975582c06de758f1be6b8d91f8fbba84faefe41b8 b55a6a5732a689f1242796f6bfc5e8c738dcb84c15a2706d59624d4a4a5c20f1 5d6f40377c3549d92453c4996db542503d1fa1bcc02b7fcfa36edbe3e3ca686f 6ab12c0175bd2d9a7919b8be7682f1adef7458c0039ed19dd1f9447dda9b0dc3 9116602cd551cca91ce9d192ba666d88a4c73e474d35263506e433cd79284328 0eaa602fb9d36661ab21ee40dea93019614a52f821c1d814dc58520e6a2c1fdf f7152fac23ef285a5232a6925c2bbae68195f003e8291de08036e9bb51e2d162 842a1a40f69bd75f5920e6eb4868609c9ace5c5f54f7bbd5911f3f0f48746962 a1dcf0ae09ea108fbd3002e376a72c2edd0af53ac1e325ae17374724546a4ddc caf905cffcf57c6fb56c5649eb1c5f3429bae6ec6db079b5d1b5566add11296b 154e380a686d3dba7ac1ee631c69799d57294cabcb557a31d423ac5a1dda2363 a3b9a7f98001b5e8774b85e1e8b21d20270094e96d829d1c86d4d52dca93b910 5f7d650150ab38fd021dd7c7e7d73c43d44219db4b8d406b7f37dd4f0c59afa7 93b542c5815041ec1bd0224d94f3cd6bf2c175e2d3c21a41f1b7548c2956d2f0 b6e258025636aeee87c5460523cd856b60887783314528f1daf2850692f67799 6beb5d973a30837e01261f7faa4e3d0ed5a8065e9034d532663c6a0680849388 728a27b2cb8a11e41c0d720f011e25174b3b258974b98db36f0fce161ebccbdf 66c171125210bd81d5844dc48003ad28b9a2ac69f6cf3a1bdf4a4e3f1e03b90a 9dc5953b98d958717e92eab197592f368afe656ca1c7106a8be86b7156585377 cda2e327d01059231ac816b842623b99eb721829b5ca144b162c6621f6be1c41 581b6b6f6c950e3a5c2fc9e639590e96172c141b5ae8a9326008cb8cb0fa1e7d d9ab0fadb89e76a8910d78376c4c106725859da7fa6f84c7f51a95e2527f8f4a f90b134e876a7a894ed341a0f78d5c60b4b635a488c7059e05d4a9020920ee4d dd271b6fabd133a97462ae162b40b682c82a3fbf5ea311ec53c83fc9fd4d847d b6c4f4a52d670d0cb0c9180c3b4d870fc9b221df7423c7849f7a3d3797aed696 ebaa0845563864638d90031ecbab161d9d23be3da1ea6dd32cca6463d4165212 7e41fc663837c86616a07451c50cc9a31de84041cdf0027bd89a266c3e2149f4 c4a9336984ac069228526ca2f5dfa52ca1491861d00dbaf461b168c530129f2c a26daf5633c75ad12e5dcd7af4b77c215b9ec2df5332fe19cd9257316f5a2f53 b000b238fec9016e51bf3cf050e6c99feb7740d95b9ae8f4f1b90bf5f9c8666b 7de83da3ff5ae6edbd312eab3d1ce7787c8d860a7a44bb53361cba5a515516e4 e33a27181986cad7e66556463d68ec7adc6208a510c0c32d18f85419043ef5be d2ffc42b3bebbd6b64be72780aa923c4627a4d3eb64b5d4075790f24963182ef 5e2019cbbaea01485d54da629054d9a765de54d566318d5d1959b86c37180f8f 00ec248e1c4e940db301f7bee7db9f717030802b957cb855a02e0ac62ea681ba fae797aab7e9c0043ff5fe68e2e0772d8587dbdee632ef8214db9bd723db2a27 cc6d181dab66c123f308e0f5c31339b1e5ad1a59c8ff38e2847a7752ef4b7029 1200d09ddf15304972d09e325f75ba1b2e72b5775d7c5c559a758068a7a75c81 e1266954a3926f57514f9d943ddb637677a3b0ff784ed23b5ece49d8d26c3f29 374cf46d6c0139ed205e79812a82b34714297a0671a16d18360dba12296c6c31 9210231c8693c9e19969644050172dcc66f81e1e5a06a4b69e878e95b655ef40 6d2cd517cd471e2fd3e4f380df2fb50a6013ce8ca6f84e28716350d6843924ac e704a60035bf5b010cbf452a960f2e06f5064633eb50d04d6324b76cfbf732c3 4606134d1c691635137fd3079d25b977cc1cc0453606045312fd5dd3367a1601 700d6197e4e3fff549ef5fd4d739a059c45ebd5c5bad0d2a2730594e4b5974a9 01dac8f2e0b753c7cecd10a78c6771f04cbfa3fb9d5e32f86baaa505e128a80f 13aafde40f73d97c9fea412c1f0937752fb813c824ed73570187f0aca942d80b020c738bd19b25f071685e64a97c36fc1965107b9adaef0bdd1992f0224ef5054d2d90df2971cd863c42da3a012291daa204f822bce480e43cd456aebea9680a006990a867754a84b602d1cd1bfdf792a38ad8b07471a426537637987f7ddc04a8a2ce468e5647b0a6245631d25046d822edee39290156fba873cfed710244095524119157ce551c170f4cb57458c0f95cce000c19438ce4507e7488c5263e0c21dca0fcdba134bbe9d7dfb3e22fcfe83d578fd939fdd152bcc396b545274c0fd53e47f77fb346beef913fa1bb363024ff1ba7b7d1189116c198b4fa499b9d04c16e61d354fcc3d6802550d476cc16be668630e9fede610e508a021fc74382053bb426999a43123525c6297571f1e5519e7ac3d7b54919d21a7ecfc9a352e6014a84140f1dc95c239819178f5acc59e3c7ddb3c0927e3fcad3db6657045ea20bb329f01b4a6d5ddf31d916da0e2d217c120aba4971e2018637a57d53868f16062fbf28a136a53049525a1f1cf1c1e170661c4d968915ee61f06aee8793b805036719e753ce3684c9d96399ca764cfe96dc63978f191fe8c15ecd76558ec4110ecf518afab1d02d434ddeb5c3430a1b6cab82cbf3b5e4ab25d6e86632fe92300c4de27e50b6b0027c3a8614f71954c6b4c1fa603a9de7c82caa885123810a680047d24e8f14ad86c157adbdad67163130da35b8e10982c2bf32a13fec6533bc0ce3d347c90c80c7fa038fac6715d4711731e28abbc5725aaa78aa7df080c23b0c11629e89f056822d177ed48f38e09a7c0519b2224511968e3786468b32649c04d2e95841c587738a591470bf6801628452ea13cf16942fc14ca8fefe3705330860d6719a34d09ee5cef583a03c0becea6f8d286b65829b638234234620f66c0f62ac51c494a36ff7c3f9682b5dd6b0f13c53654fdec0e3c9f52861074079470e35e6b3e3bf090928362c78f594ebccc8eab36dd600b5c0dac8831b57f2a0510d9343fb26dc66334fcf781f0e8253df58043d7f8f5f02df8164524b80464fcc06a885794a4ecee84dbac30b6a572ef4515573b3cb95aa38572d7e5cad45e4c60da8f1df7e249c61faf16afed522895a4b089e017b70cb4a25fb6f77779eae2a06a3632e6446805cb4d13a996ed504cd77c9925f1c1b80dfe91fdeb6faad33db0ef5f2b72b272cee782d29181fe1bfa8df004fb7e7d946e870f7f7e282bc491904ada0fc2c068f2573f36d6ba2929c5647406a5b492170fa94a7f9296d60bb8f0f487edc7b4e652ef4a8a96f192c5682fa308a1a70a9ee4e0eae23ca3fbe86cc0c4a469db3adc9ed5814f016fe195efee2bc2f3adab7440955b9f5cfb2906b100d8bf51d7286d0820687dc8fc716e1d78505af23e73975a0aa620c25953fc71e09a7dea3bb85085739fbbd03f415cd2cec5d27d30006dbc1c67186d07eb25b5f04cc0e55a60ab8f0cdfd1770b206060c42d2cc6e03e8e1fec954cbad29b1a1de053fe8d419e1ead3eabf95c6bfcc35826805ea092c43dfe4c2a69e9ea43813e904e287503298ae1941b1b0e9e98b714b268a658dcc508d03fdb41a339f482eaa0d219de6ba2949c73f06be0e5fc73e483af75fd29b45c4694a90bd33b46ae51c0d733cc32f2b7e62b368fd226ce6107f26cfa5033902562e0616782d4360982e0ea8a01706aa6e77d9ffea8440a4b9db68ffd60cfef8e233bd5573cceaad06d704ff2f85cd9055c8b9db91cc870e8185426216ac62f10cc63c5d46bdc228a525088a4bd894c47b27ce598eed934d59c8db9faf871c035f1c3e7116d9fb55f73d03698c5be7e67f4e16fb65e56498e61461b22e73cd2b39bf0881fe83973998d60a1689fd1ce8328158a8be3652988e25384e215d451a451ce6448d4bdc743ee60819bd433a6869d0d2a9b06ab2bd2108a19878df69dac52ec23dc9254502024d0cb48b31bd11771d9a173a0b1c746382bc518934c7719a825a3a9a66f08920190a217d7d329ee66d1fc5e51da8a136306c46a8d81acc5601495c143264233562090a0d41738ec1c6450d33a67ff79a2dc6e873d49b7f5615e168954128a8ad18042cbee037c3f35c9ec4123ba13cb956b59ff5f03cca84ddecc028260afcff3d014b4ea1a6f89a89bb2c193a79bf63a27d11db487b670340fb14e2340b0996fb08ecd0d03578feeb1606e85779955bfcd6563ea035a828da413acaee59b6589d00d5f0b7d846929a9466c24b2faecc2a5f99b034206c66a2549c0d548ae1a531067c7d2e53a9d3c4b3afb5652a42dabe7998a4cc3af70f4fad8421826401357808b0e275cccd42903db5a39ec3090e9f0a98ac710a1a45b5ce0a33acf77332b00bbf2ae1199c2cdedeadfc931183d3438867a8209d7b2ce812daf3c76a52b030063200dcbb0dbc76c97eb3244b1a37c3bf008544ffa636790e667bce25b818950306557d9372fd0001c0b39779427625cc8cbcb3023d4aa59747361971b7cfdd04f0c51a064cfa2303a33896f598090c6fb34f4d93f6563ef72321ba100ae08c0641cd083ccafbbb4503a6835750cd0aa9369bc92853180a44214ea43203ce100a99c972f521b8a927f72f6d477538e66ec2db4ba313c7f3c79b643d0c0515910dc97cfa78678319a988a4924db82b39042a9fd3d5d77bec877397affbef9e6e0f8b8cce087ba3cfde378a7a249bc5b7c481b4671bf7ba88a4073b6d4e003660037a7c1c6abf8d5ffb65e447309d46a2660f3e27ac225d550a4fbe8de0b4734e09c01d1739ac5269c1c70385852e5743f0ab52084aeae3771ec70cce3f06f3fc0077cfad9a2077f5e685d0874fea4afea873a08cb2f34b4abd135cc0fb9023e50f0f6a1ad981dadddc4c8feabaae44fa75f92f015bd86d22e819fe4c504a43c608860aa5127c4134847c755f95090c75563a7b84cd3cd2741c7086d28dcf6a3d09faf836eb71cec5d387cede8b6c7f99423455c25b3f8f58afbeb65c52cf2e9102894d80afd41f2ef34ce838e9c192148671a8a1a6b507d7190aa64692de24790ca50058056b08e6303291862ebabdae65d06fb5fbd1e6bc7e6f6c6419b8211306b0b6e1f81923ec800dfad4e950d8aae88003cb8ada8cff5b971d6dde2ecf4e01dbf23f3f3c2587ee4da8232de79e82e943ac72cd79b63b6b965fbfe24535e00909459ab7851eb96fbf47b6a4998287ca0f41c8694260e1ac666781feb8692606da371a172409e75827288e8ee3fac4d592d0693cad541667c824dcb22c97b804a4245aed901c15205f7152b9458e512c8d25bd7da125f404632229ea68face0dfc7b2d218dcb683e23ad45352326fe79b8ad4c2162ad48b9a1486e6d6452be089cab037109855610946840cd7ed6332d102d08f1be8a5f27e9b3cacbcb7f5403ca44c30f0ed4519bdff60ddcffd2afb8d9d8333c39392b3dfd942d9617f7b70c7152964441f32e143854afe661786fe04827a6de1d526706c278e3398bffcd077445b8030cb5afa7eefe4d3bdc667c69e23e4671393c65394c4207fdb461f7054549d9eeeed0b9010a6e72278eb1d82ddc771ea40685ef84d6fae6a6e18efe0e9cb49d621e9f04a06bbb221194f24003c08e05db0e3e5a2f7573f3bd39580505598ccebeaa504812f16b2ff07d85f312a15091125557e6a14f65c23d1fec33006f019a535cf158253fe661b201435a02da0e07232dbd1d254d3a7de73ef9180df892f022577aabefa23f756c593aedaaa0ed185b01ea555fa44fd3dd9ebd7c0e1bb84b3548bcb1fe5b3ea7dfc081140f79c2c4c31d6e1185985db8457139f10441b6ba90cc156e910ef2d562c24ad44b14279879e3921710e66ce5a2413727033a5d245467e98224d9372c4d32bf944e61e25d89b8b57d19d136fdb76da50c003f3932930bba87a1d50eca14c9daaadccc78a5363e2bfe5717ab14a26403630f00c0b4813911540034d2d369903acaa5f1a8ef5a205c023737ae55b948fd9b0202d239792d44ce30d96058588c0fc4f6018154f8a7310afb3532324496b91708f42c5fc234dc24213c15aea5e7c84fcbe9ea57f8a359db156d08a305b64fa1023ea6abdf7c5c101952e256a0ad6a6694eca0c37d53e1cd681cc0806cfe700f05762dc7371c8f01fb1a4c25a30740c1b88af7a7dc181ad2d778d50a2ab99fd400fdecd352ac83a5adeed5c3e5d742ca5d8d19314af04026fe4e46edf7ee882d0824d4b3c9cfca8c21d5a8c478bc866cc47d308837723f3ce663f3f38b1e8abc0863efa1461c3d2ed2f2714dffe9807d4dc42f56415c69904a9a8b6ca7cd8a7e0ae4e0ad1acc5cae0001f4b415378e08abf140156dd31aafed1a7abe6c1851750323c7c434ea380d2c25815010d7c188e14c4fcefc78c3c6b38418c51b16f03b09ed590f9892b9bf9459825d06193612f0ee9400025adc11f88540c6cd3a7b420cdc10fdf85ca75dddcd7c6c51d1bebff0634957700ba9622b078c1c107c57760c3a06a60908a3e8003802e9e13dedda6fc5089fb42e91ed1b0422b629f5abd70d146d774de3e06db9536fc8e3a5b3c2e9bd0d172119c68a4a691a122347a1dc0edfb837fb987c1b1a0e243349b776cc588e4360138ba2f2b18c2fd3d39ff4320d21c85c1767beb21fcd516ebc4a9257a703508fd15e8fca1b1e0032da59f6b8035e8366d58e27a510b4e99921249cff3f47a76e297c7b189a47c9118c6a56b60cf41a6e4d4b9e589ca11c7159a8383db29e9d673f0085d8af044ad045101c220bbbb9f870ee508f12cf2e183539628e333874331fbddaa458746a6355bc3e070171cfe790aad9c8effa7395fb499d0d046d0881d0d5bc99c051cca0ca45efa902fe399867a6414d9f4b15e8d98dd1fd3a54aa40c6ae42d8ccd9c7a0f6cf40290d26366e3690779acce77d8468dad25c1898c4529dd4a29d183fe7964dd0b9260b612b71e621430b7729ba7c03c107af02e0af7b03ae6821cbaaddd90fcadecd093e303d05b2e2376b1ef2625f07a60a6be59f97156917c6725ad8b412c298a60748ecb2666aa21b75fe972c385c6ff200c6c6e0e1989828d0b8e81c8c79610a0e55fabeecd0ff1c3caa7c1d3afd78c5986bd9e29a33b356b3e41ef7bfcaa67305669fd48078d704afc64d8c702d47bf6a853d4360b13c0367d72d4a84f8ff2209a2d03c55292b5b27568f8afd7211fd05ac81fb4121d9833c168e8bfbafe12404a53f945b0fecd093ad7927cc9b00e4282058d447b24a9f4c00c9dfa6c554620255bd0827ec4b3891d628702b02cd619e1126dcb69b3ac30e91c25f39eb9e5700ade91158a4626fa71839d2c71fdb5ea3413e8aaf0d6c95bbf1171558712fc802583c5a6e586def73befdad3c94ecf68d89c6cef4351ad9459801aa70bd2bb8002026d69a420c4cbc582d90d254f3a81220feb3cad26d4863978c5b4f8288f60ab22f3142e87cce15bb15b74feee9a46d13e1c5789a33d54a2a15c92e496931018f71456d1be59d525bf06bc1194962560d49c0dd18e85bf8924837c1cf9d3109a7eef9506bbb1a7ff8b58f029d4c91b924da1616825a4621f3628fc5a7844c0a false -check_ring_signature e20722d599d4d1786bc622c0e473b1637b1cdc0ce821d1321b57aa526246e6f9 afb1278cf3293d6b33ce0d19eae801470f2930ac9c991d1eef912c47146d51a4 2 cf01e21e02f41d5c9e8fd1645f3c24847d73304413999fd0dd9229c549a2a59c d2e980b0e3f58d2ff57e5292d4b088efc13b143363ef94b1c3b5480c97e7a44f 5b5b3e4cb1c4fc791d1640164a887c2beff2bfb0ea3974de3bb8752b36d9100a33cd639cd5884aa34734308f9d4048eb2377983b346890780847ef9f97b7b789ab20d59ed6a9c48c042c826d54a9a9e1f9c95b387bd617ad49d5d9761246860e561394613db034e57f8d37927009cfc98ec9e15cc19904855328fb40aebed405 false -check_ring_signature a7413a44325be45a3c869e0d408a7d7bc45b5d2338d1a26d8a135036feb85e82 5c23bc74f74412d531ca0645165e378e1dcd338fc9232d42e334c63f965d9ebd 223 3452087ad769715060f53ef463caf09a92fee67b35137df2fe4dec3de9e0c538 bf88f47a13b040971506a63e1f4a5138afeef6fbb1f8787d551b704a418a570b a8338ea4939e4c95192b595f96d36e51cb6908bf066dd25ee4d1d3d32eb862d1 f69ca557d534275c5fb4cc3f28abf6a2391f5febcf94f154015269fb88ad4efb 4ab5ce537af56cc2bf1b8b08a3d60851ce435e7c7f1ab99eb0f5459513698e11 17c5911670c538887a61990c0581c3fed95eeaf55b57877340d219f1535efb60 4b300ff58236bc44a27bc728620f938eda54e8e1a9d10f19d3327f7d382edfad 67a8801a0f53eefb452c470222e5ead5d63e31acbfc1a0b77e3be855593d3513 96231fb31f30994f543f580d83c5bce78857c932e6ce74327ef7cdf4862ae1a0 f568cb1f4672d8e55a187fc79d6e7a59adb9b4c94bfafa6572bfce15ee7203ca 299d301ec095fcead96bc3b38f3bf895dbfbbb4d7956f1e44766160a68c1ecff 32a5c4263364146fd9ebb7ce0c10689ea508ad0db38dc5644fd3f307813d83f0 1c5254542e40d71a90a3fef7f3b93c644d2c962b4b394139f1483bd41366ba21 aff56958d56af499a5614caae1c5f912936eea4da1ab538b0715570ee93dbe85 717e8582b3a1ca26035da91d0ca260a16bd46d8b422c34b981a81728f20c13f2 0cb7be355fe9da2fe1d94e10f79d5a646019fff256ab30f9197e28a3b8202a74 f6e6e2f139a7b0ac1e16d53f78afbe3e826b55a5d3a8ac1f08e62a6f13cc6843 8c79c816271fcebf339fa4b9a5eb7a4292bc9087772ed689e69e53228b724630 a418b9598620a1b73edc0d952f630d63769d5d60a5921e80264eb310f4abe897 9be3c13298b4e97a88723989408e47976c2eef9412874b689f8fb285470d9037 3349bb96e662852b09d9f69ccba63fe6f54b9d169271eee1963ed831185272f3 4a6887a24c7dd5debfd5dfd69fea32f144c2137a0974391f45c8e737f03c236d d64ba4c281e57ea0fb5464e4efa9f091ead8f20b6fe8a16c958c6b7fcfb78617 cdce23dbeec4941b517480e5c74e273a29ab795239de066dce37fc44f1642158 173dbedb54ded2220e254e251c1547f70e7ff67580878515c6aa4beb9c6019b7 48319e9d702fa616ae97caefbf332bd1872688067f2f726e38a7f5821b1f57c0 6c145dfdc7f74f4796c56bcb259fd949137c17c305586870ebcc941b0296124d 78684a0c6c33966e1996f534cd19f183cf85f527d6705a9aa00bd39cb9d8c3c6 bd7a4e54e01a10478b014b74e61767b582bd5eec4b3eaef2f64a1116baff3782 8e25f9c22f3a0b82ef28e303bf3f9db5d496c904fb65e4ad0a436bfe598fdf6b ee9f29ba95203b791f97b35791221fe3ef7a481031bd011569dc3f908a28af4d 734f94af35c0adce5419a4731df1fe688389af5383b09fbcf957cfa3a425f841 f38fce381744e2898e1cc891f803b9be4d00bfa07ec523a0d61304e73f87ff8b 46fc7c29f46fef4ab38f4c94df267f648a761eb6807457b261f6b31349040ddf 2a7c1aa4a7ad7cf11e3aea09e266dfbf2d7b47bc709d1d2f6af76ad585c6c141 ee98bf436d84cd82416269279cbe99afd5474ce99d9ff3592aff822d3de529c9 cbb4fa2a1de8951eb96263f85a0c0f391a65143c14fefc73c2a5809bd6b0dc32 72d51a3a6eb93162627f24e1f5c71f27e1f12d175cc24562695a1ef711024e23 c199901ea4ae3af2da84b570ca624b39a5a1575d8418a2c2840f07439e1a0bc2 eb81fa2003d41074f3a50740c447f9b381b3ef450e47cd9109cb745c435b2c39 43d7ce313174316bbc04e5907b38174fb66115cbaca0f8ddfd0df089b00d459d 769c2f0471821c538fc9c40f90ee6074b150284e4b61c225afc682a2e3e7deb1 d3be754bb40ef6d47ec4fc2e8658ad71b0dfdf642570f80d75535fffa5fb9edf c5975355af1b29a8af1a84062fcff80e28460a2400245378d6cf7f3c29849fbb 3733f48f88cd5e56e8a26210d3458efe4f117b0621e1be584b686c88005833fd 1f6e96c4f3e5b38c1ec4f70bb1df118fcde8c6b0aeba95d141e5ab2d7cdea94b b380ef76da440d3e08634d67556dcc4e0eb0151cae9c766a0b50260d9d54562c 7bf920d027913f698ce4dcfac47eadf2156b73884e5dd438d3c63a2a51658805 79212abaf0936575c4065a6583ae3938afffcd0c901120631a68f5091f7c8dfd c0450e36366b2ae6cadb55b302fd51f46c908967b84913131008674909c28d78 8555e992dae83ae354f52eef563c3223fe4ae80a8692eb9843d379fa72d146ba f8df9b6294bfd7357102e57944b39a6ed2081fa322ff7a616ef2076e88d39746 56b7ee74c8fa5f5715726bec19a8f68351b60c70ff2fe614aef0b76f1ddef1de feb0c8ca4068f03e591a7b47c6d6d076ae3735fc7f38181f55cd6740a6167af7 807114da8f392c924bea16146f56c760b6623b69ef69fa6b1c563f443b7092b2 8436b0a41f8560f253f10afbb3fedf553a40ac33f153db12dd5f372d5efe159a fc82c1dce712160138ad6916937d37cbd40a1552a23e7a03513d4ee23ee2142e 052d2848ed9ad5389c072d04d74c87e7669fac736a39405197de1f37e7abb01c 6462c1080fd563b60c486bc039d228efa73b8824e2821d4e592975fc39dcdbc0 c400895fc5b8166aaeb13ca8b7bfd8d92c8cf2b12bede476ce8072d6e6a4f64b f2728b7d927f1ce45e2812f956a7b17e0d9fcfea80179fe445aa8d4987866fed 80a5b3d9d632dce43e76d3f1311adf2f9711a9712efab48587140a5182cb2049 834eaf1d9fc1c3381c5eb39d202013af624cdfa98393c9e36665edf2b4cf2077 a43995cf143df880b63c2af769d43390dc44319a80f7afc3202279c66d0f0a90 3c6360b1c7ff9844600a51e2a0edec6edbddbca4f8a7322a29c918c1eb63144d d9f814aa57ea682711e82de8084ac624e9c05c081b39670fa127bfe67d6c1398 0a511c706a9000d9e4dc31d2d4c53f7fa0436791be5a11eda18c0c0820b86fb5 f7832edfcb10419e7220edb1e02ea930b7fd114fa84faa5e57c87dc7c604b709 3ab4ca537470cbbad2a5df44b3727ac3a5547e88bb57eda53f6f0fa5d0a934bd 27894c73d123427fd8918e077f060b1e97390dd6d583c817ab24118357bd1651 3a537553482b74b003c9eb7dc828fea0bfa11ee58a64b620e223ad641474d52e 937e8becacb537e5b4212d401c293b31a80d8f1aee1294a0fe33e9fc8f317341 1857fc187e4ffde9c3d00f672d7c5fd941868d6ae948a0b857cfc473d470c131 720dd730c992ea268d0a3b1eef8f319b7eb83a6dea9eecdfd3ed951d46f36640 7f9e0a0dd6420078b08b9a90421f219ebdcd7103ea7a701cf5e132cd2d788766 ca4b4776ed810e916f501ea51c26b4f1290609aa653c8a708d6723afd01b792d 3d46479b74fc6e7ad2474468c466caa8be58103471e50a50b26fa7489ef0eeed c4a5ee06a88b8f36a14a640e8da4cdfc59a29c56f48acb98d1ba76b9a03a581b db719528015da7dd7449cd90a4e7439b9bb4058f1391995fc6b9cd89f253a582 1a71b570154de689e8acefa0a96c7c545ceb4cbb0b934f3a82856e5b0af4551c 7cff9111dba0d556d4eb0aa73b3ab0871050c9a7012b922bce1d87a212aab5e4 de289f1bcd9245ab67aeafeaf647c31d6d6323584e2cbff999dd955b7f5a3f35 6cd41f5d06b7916e0af2884e22c7cae7add1d695db12fe1cf73b752f33bf0586 0ca4e4c2b94cd8672bf413a4530410bbfe23d589e17d873467f93aebf236fd1d c407a10e5c50cad676e42b0dd96f95ed33a18cee40b6d361fd6558ae9327442d bcf76e2e8171e5e3e83f62ecb65ea60ab0344e5c93c5a71725787f40635f29fb d91eea66d752eef3afb32b9dc1a058e13d7090c3bd7317a820ad8b1caea9a2cb 0e628de0271ef98892bc5c79fc0fb3e33eb8ab4ad4f0fce5c5f97a741a71aa59 fbd6985c4bb2d72dbd73e92ca5125838c0fd6a372949e6ac71a637e97f0dc15e 8b65cf5f1ad960c63030bbf48053adfe72692860900abb1bde153a4bdc122c53 5ea027382d54352a5ccda02d2b386f872bac9787510ff101ee89f86bdccb971f cf08d96e6d81f75a23ce573551e865c8c459cd9cf48064fd4be5a25d01e51028 7dfb50d20b6ae5eefdcd8abd688e1a4b57392d2a5ba4c9045fe1b5bbecf30834 627476458d573ba3c7f709a06cacd5a7f392a6b00fb486a578df79a102ce8682 0a4744d52f0e203bdbf1154544b95289c2de96dc65b053a19c8ca15a7b3d379e 93af29bfadbba2c9c6234d07002fd34a33483ec81184bc51f945bb85fb0c39b9 10ae3196aa228fbc7528e46fc2266e91adf7bfe21ff11ce94aa50415aba8fd89 71d18683f8f0d89fe9ea94277c909a14216b66cdbc5f12b1827cb055b1e42c4e 91e90aec8639dfbaae9daf63eeaed2b20fea0b06bc154a4f342aadaa7414a774 110d8c2d90c0de8a72f7669f8276ae11fd6ec7a9ac92aa09cc14a082f99eafa0 36477987dad28fc63da4181089f463f4b8a7088b6e4681be81c82e661e574471 df2a844d6d1661136ab30b24223eb0aab77b261285fbf774124b443aa57e4a27 5ae11157b68640bfbfab2626aa698b6e9135d20ec3b8939259bd125b60d7f4ba d5c8e31ee206cffbb65a48a9db3fe3b729e451d907b0eeb39ff045ec4969a685 f25d4fdfaac4ad5f46e0deefa767687f0cced39397accf9904d296852f9403ed 11e01c1abba242d36c7ed68b4897787eef0a76d408dd312fe6148a0c2ad6deab ec614062e9e8a115c4789dc1e180d8e07c72a6fc0379ec2cce88c14b4dbf4bf9 796064c6fa1a45f2ca973eaf0eee646931daf1b80ed36fedd6c464395f3b2208 1ddfdd5e4f51700dcbd462a89d7bcd430d175efb5c682b459017ada1199fe207 9202823467b669789d77b089e8bba8fffb4c5637f7b53214f8e67c6f55c184e5 43340b059c7d32b767beb2d2cfb727c79892af1040f64b63a886b10007b527a5 e6e1acbc321e3ffff0376fc5367aed7b6475e1cf4217db325558036fa9721102 206e1cec83e35668036c4806fabacd41034e783500239081a159e113ee81d533 27fee5fe50e1faeb89b7c7206b07eca276d242afc9fd3b359c3346b05c81a3b7 88b7bf8d66a4b069ae7b17fed0bb428b6f03062c679527f17cdd86aba8c6cf94 611a60e9d9812212764c336df302e2f2e3f37830131a82b846cadb2320054591 2b9ac2bb335cf5177d07efe1318b9f715a2679808df44ee3f5c820e1715a1f64 67f58b1fe4c7e938685a0e8988f87a8a6bed6a77460d213db557d22c368973db 9a9f935e56dde39e139ba6498fee3388bcefe6897447e217bd00fcac180bce28 cb9efd98c65a8eecdcebf193164c6880037504168814baa60c6cea1ce9027373 439deada7d9a69169798625bfd72e467a298f808ddad671a468baea95714aff5 a0a08872b9f80db037fc25ae65ea1f0e631983b29ec7d15eed369ca5e2f39d1b 6e7c331d1576f303f034ab28a3d0ec96fec45b5ccdf2e2d879eb5e2f6b35c9ff 6df03702ff5e7d483ff75f2781bac1ee4bd8b73121d812bdf3cf282861d8990e 5c27451ba42f9db2527b19eb637629496af312925c2c0a81e324c9a920ce2ea0 b01a0ba253d9aafe7ef0722df3cdba942042569214b5e92d1ae60976362434d7 664177130bd579bcdb546f476b3a22f9a96e1318d35f55ff6145e1075bfca5bf 5bad3882f31b4a35357eb238bbdcdd57dc4e48f79da38ac270ee6beedabbdad4 940fe67f9e9ca85c82c3a2e7673fbcbbcc4229f512b03fbee467b1ddf38e0233 a49899b42cc9d53d3bdb82e2acc81f001d62645ff8471ef49c3fb6564c2c67a1 3e5d6560f1b851986974764b810b0e46644b5ae476c7cd369cb1f0d0e5226959 eb01417e9597275c4f71454ec7df183777ca61e140a7c5130d2b902a21004651 8a011286d1fd5523cc76250414a096c6e5b8a3c91c1b9f29c8ec5b40ea1ad7dd 456ed496187fe79fd04bc3029552a8a00fc57892bf3480dd7892d0a1452cb31a 480afbca9fa8ce45d546a51fcde58c404e0d2cf54aaded257243f0af8cf159fe 686934fa2c245e8a4e1ab27c0d60c1edcbcee8d04f4109af5bcbadb8be44475c af019048318f8a0a9faafcd69af33573e6fdd8821c2f1e7b1ef6a9e8ab79b0b4 8ef52ff83a594914bf5c638a2d04691228809abe420a715d368b98f242925c33 9d1aa4e3c2dd57937b4780f65a08841b2a058ea29518b732e79fe62ee04f676f bc96b00867b1fd79993ecf627794f6d6657fe8a5f0810fe13750a6b64947e1da 1d8af4aa533e2707f9ab67c03350a1f900c8ab495ebe50314654c83f2e0bb454 15c434971ada2ad3544d9b012945a9e6c1e30b55dc58a0d8d24210628161ffc7 5379f25b7c3fbcc68043dad4d4070f74c18077f11c6837c739bc61f436e9541c 3ca9063f538e49e1d5f1b8c5b28d9acdfecb0a3193158c460e2c7a8ad8882975 ff9b06f286404adfb62c97e9eaa221cc642a7155e082384c543dbed190307620 b6c593088ebca3236ed6539d192e59ced174c67a05e6baace7e2b6a3d413bc6c 7e030bc251936064aed61565b4bb6df6fe6879e3ea1b3d2bb151756f702162c8 4dabbc16ed11567876baa5d18fa945a9cc8997844acce9d1cce639bab06dd428 3f8a9e22246155002cb97b0ef1db4156a4701e27b28bd018647684f5ae311a87 637ec3f6e43e024fab617aabd81154269f5065f6856a59d1e7bd9d1b5dfccb90 8a7135fb9316f4573dab6244b0cbed9c234d57a6b947f8715167dfc957376f91 39233babd750ca3ee46e508ffe9960ff837247cba3d7a4da9293f695d659c86c 539739cd03ef378c87754aff225a74bb74988f93083b663757f7ef5d78dcf30c aa6d1320b18403a0f982dc21559e882153f65d8095334a62fa821d463842055a 039a555b68e1551522283e1678fd04def6c8ba8f743d83c1256b1a3455f05a17 f69ac6d46e36a0ac02abf13643bc4c603e6fde7fa1bf275da6c86477673d4aed c2b063cdd12b28a18bb3b89b16449fe83ff46a495c2746e7cd8e1d0902912677 28681cf0ae93a2ef3fb878b0f1b24c838e46d8801d5fd1da2d37413644cc47e2 7620b3061bf5b9f4c0fe379e5106afc93c5bf2b02ba61803dbeb1fd579ee0ac6 d247803d16f9de1f8aecf3915d7897869020ccf7fdc4f9d6aa2b03090d6b0127 947e4310a22d32d99ee2dc79e28dddd02299ba6aad05533413122e3fbdf970f4 2be9f470467f17c6061004fad5cae4885736c587da6f0b23af1ce24e4ebf2e57 6f0448d10226eca4b17289a15e9524626450a8bb3f07f11b58689a69dfed8d08 a737119396ba90c9672944e24fa08b2600d1951828576bec69eb9f041b1201d6 1dfeaab88453ab8cbe6468b9fff8a20b81c104d640b342822b8fef4366f48789 7e57252827c335f72c9aa12b1324ef6ccdc94b306154ebe1d94d7773c6bfd203 3bbd29be1bd6da733331fa4f61c81f4e7bdce0021eb90670084f9f74b343a2e4 06e553316f57d466a8b58f3d5b70451fadcc8435f0c718f62c74869ec8f31287 33d273bfe61426bf1fe011ce2b862435751659a548d848b6fe572dceb5a67a0e b2be35a02d5486721292dcb4073b502f2cf8f0b797add2045730eb15f81d8179 e1edab3a0966e771ed497e1db24eda4b511d4ac798b5aeeb21d4d02091eaed40 b6c4e1248113e59981c6f0b1ba167911ef2850f5c4f99fce2fb77a4a3b7febed ebd37b8b066e3712d5bb84c3dbb77060459c2a51c2114e3e19709798d6fc481b aae18991815a6af2172e4009ba74ffb6633b6306e59e60f2d636c9c4750ae6fd dd1cabb5276730de814300d2ee70c35c2fb84409fe4df16d2d21afa9f1c92630 7cd18c446898bda67895bd1e3445465039ff75dd3ee447d2717708ad9cff5275 e96ee1e07f985ad87a60d5f841d05a5cc17bff7ca73a69d6f22823db8ae1a3cd aa1423840c79ce16d7c7e8832bee532d43fd4dd6907724ab69191f899a8c6ff9 fee6e5f82dde85cee4a8fd4b788969a3c35697d8005f2517ecef5e17392bb646 cc96c4761bb7fca1e9722914bf9efd37d2c46f25e18fca1da4a86be0154719c8 4f0d5ff5a72503ae10a50a34e8c9e5d9770bf63fe417e4032ca7e98c93a82b67 9d65bff86aea79b53b888b9ade7c8c7ce0974495ade21a022c736c5d05daeadb c4d940549bd7a8941fd1879bbe4930ac318165bd3d8c44fc0a81ae54414907dd 733f438388d74f2dfcd37dbf56ce23645bbf36b1b4cd25f898342a00ad0f4db9 46c7dff9622e8d4637dc201b6a4a40663279ab2846bb714e187500daba35a048 6f862bd95fe45e181999865e2a9e06829824eda484812cc01680eb90d30e18ba d760429d1cdc9adfa2c8982c1d425d6b117c1ece9d3df780cf5530a14a4f90ef 6d2b27759a5cdc7c5eb975f672c99c2680c613794dd257f144141a8b9cdc06f5 e51d1b544dda604a7a44bc7794db9d1f39597cf15e5887d9d25ac743bb8052ad b6d95991c40f39e129d67ff1b1bcfca7fbe3030bb497911b30f4f251a62ffc47 640c36b3680f98e200369b98541d0e2abe18d3288142cf30d1eaed018a353854 278986e18083954aef1e7f7fe1d28d5d17c89b5ebc658747bd2862df569d83b2 0cb5d039a445b060bd8c2864c53fa70a0772eec2468fa6c86410add3896c0070 440155a46a8b6c965419c0b05757c878dd2b6b0aa38e868ce963e7b251356281 76a6cdb0900b63b39912a2e0aaf1846637690a4c2b929982d66dfecbd12e51f1 1034bb16504122360e84ddb5a5ab585bf3edadeb7dd66609249fee6857065931 007e80e05b82f7d0719151ec21d7045b6e7b94c7c77ff40c282f40933bacfe6c 1969cb5ff8a7b731ac92f6cb1089eac1be0264400ea769931710399a739d6df9 badb4b05d54ec2389d01777b922c6087ce61f69d86ae3d0c18ff3445dd821247 1f322bdc968cf39762a04c0782aaf30256e530587eb6c6f400824839458564ab a91807cecd1ae32a2aef1379824d03affdda50077618dc7caa10ca506ececb99 dabab6863fc2fdada3df2daeb85d2b5982e6c1c96fe26fe57bafc18ff39565fa 9933f30447a1d599cc0f15f725d178063cf7d27bdfd4acc484868ff70af0c273 70f55deab364e09828a7a90a8886bfca0cc62c903171c86b4bd55f4d981141e1 473eef4975725caca93422b0e5f34fd0ba9b38bf6a8a5df570e0365a36bf8283 e259e4ee6a72814e98a613e4e714dc659b7566f8cf19d664fb0e64a461bf9357 d8886947608ac2db3d6ce7d7bd9e0b47d55ed3aa96f082dedf98fd79c061bd50 cc5ce4f7eecd451f237f013c016479d535e163de49606eb31968ac8fa30d443a 347585437fcb1761e04499fdc812b7c6b03c0d12916d5bc8b648cfa5bfcca41a d75441bef497e5390f2b2c08c849c6798124865884e1de6cca9975c742dc110d b9dcf0541fdd05e55eef10873fb68b6d4370cd9d4463c76e529d90504bac375e c788c2a35a7fbb3e9e740d932515ff0d03066ee3f597598c2d2bc72cd63ed184 3f5256ab5bfd3e239ea02e12c897749d9927a59ca2278a374476ec2f243d529f 686fa562ed361e7f8465492187e576c1da94f292cc1073a1645a2d9c7552f0d7 fec1fc4120e3efab133324cf5af9a9e91a220e29e2b442f84ba062391f187bd4 113f6bda2b31cfc3f6016605e7f3150cba3651c2c9aa26a7480fca618b2c87f0 711614d7febee595fd7d13701e1e57b96a9c53cb9a4e41c8c5c5a076fbd0449d c73f990962815a95e257feb616f65f727e5baeadead52e63395c7bfa770072db f534f06dd6f4a2dbcb7cfaa4e33011615ac752848c073bb26e4699a432c8da6f 768c7b72582eae6b69d19176b09126e717684ac12097cb66abc105f47164998a d785be1102d8501469587baebc7abe6c3188d02cf72f808e30e8fd8749195855 73f8adac27f584069ade9d43efe0efa72e0f6924b2337650bb211cc274c45257 beb2650cc093174203543509298060d368f52065f822e2fd6a6d0a86102142f9 59a2a181ff150f41b10bf24bbda8d0730f3ad09ff909ccba60b14366ff3694036c280f78cd9ffb6e5af8d6155d8b2c5149b8d59416b10a1fcd5dc492d41980072aaec911a612a3c39446bf86e9bfbe811a653a3825c2b565c9f4ac73ef01440ae35310e44e11cc9eb56ea8a76ed062528facc2f8c4b86dcbfbc6b3bb3f27fe01e3f747335401c61b8e5d4957592eac67fbaba9bc097d0f6e4db7c7c9f2b88304dc0fd7f8529011b47ec5517fbcada7f115177301e104099aec08d109e0cc5603e767bd3a22a96735d00b00b6a07e688d8a7038ca81d5408ec075d6ba51110204b6b584088b8a20a5c4ac4a855099584f8735443d4b03d0141df9b0b5ddd6ca094a6e0bf71196ba85c5041bb2f781fafa12a84415ccfb1c8f0dd09218ce57d90c2515743f3b110f207b1c34fa60eeb62ec6a617e2e6a6d4e99c6a50c13e6b760472592748640c194d6eef942ad8bdb391453a1dac41821b45d3091aa85c6705062ead8bb173f469911d4a1d67678e3a73410a4808bad14e785905ca1b06a9aa01a16ac8c0588dae4d025ece33a8670ee4f54fd4c87ac0f0f56732f11173af9e0e4de7d3a6bded37959b7f9fea48aec49f84d66d36107aff15b8b603a35230ff0ec684d9aa746c98c2c945afe7a906a8cd31b5ee3e8c3298e8186d09bc59a30607c8dcfb45cad92de3e075963b7ed2e74b9def000fb1b4590e1c4415dae3fe2f04c559cc962db05b768b5e0b5f8c6abec1806db694c5ae403fb3f8da642a15b20cef3ff13479245e1702fa5456370bb4fc26dd442a8411bf744aae4b4fd0d1770c0f9b436da704b73e5990ed49c74e7ea9eff50fc302e4f2581564756907a2a0091d93f1889f47cd961e4fba247e39d771cea23ae118c7838b76b320adf305f70370e068397f13fe65ecdd0c385d0b549bbcc114a64962608c461f3160a4c7f109d022a086206b1fa0bf6f615a7a70bb93b8b6fa12fa2556ab41c57089738ba10a30f461a4241f226780c457afe906fe137c0a5111cc36bc4498ed45e460d56209f8e361c8482827baa386b4c4762e8ce0228d6df31e85d558b4de22fb5f28a508bcf5338e70b33b740928950e5506fd29731673d2ba781663e686e711deaad904e4f3ecbfe8a02703505843977cc9c8e5f4c939d3c94ad99fd9ac5d6ed773ed07fccada34546c912943f606ee4c2e382b85b07a90b4e9e60d677dbccb65aa660b07fe2e31956b5f8de7e4006b2969af9bdee1d10255cb43296dba514904a9e202df49a4e0e79dec431bb1480a07813063dc60464e66d4f5c571cdf45ee078440fdbaeb9f019ab530d08bcae08bb861cc7edb5a60184c5fd9a10b87ec5d801d201a5c1f4fdb8a84f81917023f2c5dc54d1d38475b396c219a184481b3a644f7c022fac285674c9f3335afc6aca89f0fe9c93760ed95637fadd9bb1fe44b5a8c20b3d9850c935ade18c7479bbb92be4a78a686f8cff9d6dd8cbe9ec06c4b68aca0af39e38f91c0a28c2b00e17c32e7696a10511f0729cffc276fcfa24456c4b170e3198e06e9cac2708a18c0139d5fc580688539aa04044bf681168f7c3fd44f704492576d7111e3a83d8e048ec6cf7cba12ae7f4458a622e249b4459ffcab413071aa108ecf51773aef81a9a8a5fe6e25bc8aa2b137bd4473806a3a53569c05c075ac06c0b2e235dc3f00714b34dab879f1d24c0289369bd171a88a437a99d35065ddb0339d8547cedc08198eb7ea84a3ec3fb98a95ae9124ae9a33b1a867b5d01c9cc061925a73fcd67bd9031ceaa53d6455363f3865991c04b631c6a157fd20af573ca1ab323fe6d5534606b5459292f79e66c482bada7fb123776a978a78c01c8e6a91c1cb1614ddbdac323cdb3f73765aaab1d9ae0e2762f85dbd71483650017fa937c920020b7ebfa5ea39503d887a11ee4e94f602cbced434d9e179ba9018007549d56f015e3ea878ae90e462e909da3b1a9aacc3ee55598e2369be1d6052b91612efb8656f997380e29c47b5e860e69d44cc5882bf150282ba46cc8250e035233cf567aa6a146b03e94e2d230f53d32238ff5d6c6ca0d9ef2983cb11b0526d25a92e5f6992d467bde74d70f256d65453f221534b282863c7bb208b0a804249cfaad87d7e293ea952de4d10a3627cca90c94b99fbaef4b11866fdddc300be2aa31f457c3309160aa978891813d27c2c8f250bb51a088084ee692e2be6802c15c85e8c90c971730581813576e1d4d608712c7f26e28e6f3b78fb1f1beb503c90af1a0a974b442be8d80ca842c5acb99e868ea33ef5c511a96add637eb6c081d57129937c04e2045d76fb28db47c79980f8ab58938609a7fe75cadbfbc680be9f1face4c3c9b5fbf75d6309b5b4c4dbd67ba7ab683cc590e4b4edf002d72037d29f159eb0dfcb422612ffafd25af352ab0b09a760a670cbfdd1a14f225a60001937ed3098b62e60e688c7389cedc3a02a80340713a70c3ad64680248bebd04a594ce039feeda860e5901640ae2d4d1efc7ae75c15222592b6498e857dc230af6d3c4e3ce52034aaebe2c54b3c0e590cfaa9264d1d06eb69687cd4bdbb5b206a8719e0cf22dd3f01976c71fdad169224895d641086de794a196f4ec9b856e00757fef8f38e2b7957ae4b45027aa9c39ab0eedafbb96a0dbab8a0331e0542b041c5963c11ab3b32fbb1d96f779e052eb4e5f0879cb07eb44e1efdceef44c650a112fa430f2e459d29d0ea08ec28702632c2599f5f5a91e1dc15c23570765a50a06644f83e9396c433b1b07d0e7c0698d1343bd17bca7f38329810e3d8a035202ff06e88c20f273ecb1f169ed0bc99cc79a31c70f66103e59e677df304dca8109a40d6f573fb478e8e2946bc88f429f9206c4b330c0e345917bbf062a55f2dd0bd8bc3d9f83816342f6f3369434c7032de90006e38c54c6d81103da27173649052aaf0520b98400ed61f0fee7f0eb81a883aa0930ad9c04a3142022251e20fd081cd01f48bf220f92df6b0bb5cea774b410d937f3417a6e6ad0f07f3d3e9e2e023677c673aa030f5c1b980e1e590f59a2f1bb3c67b8d3751e18e3d3d2a43c800536f9fa4808a82a38797aa7c00fd0d44816ac052d843c237031a7125e93d21f0cefa1b9b91f84c807dcd233e6ffd80fe13d38ecd5ef8b1fb77aaea499ff5a8b08fd3ea37b101c2610e23a3e45d03155532f87bf5d934b747ae53336a52c7c80089bfcbf1d5ccadd2a7cb874de7b05beb6dfaaae03def08bd9151f94272db53e0982fcda727c4a837faf531b1a452cf2841128decefb22621437a91381dc0f2c071668fe35cfd9f366d0b306e9b77d9e2a69286c4c6ae34794871dc42112f29c09e42c3287b481f3ec7bdf9de70999812b0a44c1367f885d3e8280ca0f791ea006104d071b8c0efc3e7a48cb16bb94cf3d2452d5e4f962a9f048f5176919af1a03b83d441c4d1f7afd849f42c3a94402d50104b463f3a9c002651b6d8df2046a071bd60b00e0b6ef635403f6d018b0319c50b6ea7a9b9185334b679d0dd9ab80627d8130687bae03eff1ffd043294923d9a7a06cd2be7f4ef64d79192ccb82050378d4ec9ab2124c5f6abe66ba9bc01afb6c962623e171db9437955f26cd01c8082eeade37bb3a73c6826ebd0247b7e7a216be00edccf2368f3da7e260f7227206c2bd1df8904b9d6cb657b797b387b9f6b49b537f0b66517ae81b31d12237ee093454de1a95ddcc702b7704cacb4c484719c7a674457fb635af98b8621f57e9013de692290a1f133b1c2dcaa9987d3c015f7b83bbebef53cb5bf6f3c9c267cd0c49b172a19621872c628adf818469896edcfefe41d73ba796edc331204b19a90f51df1d0d2057300c3da1a484ca41a4afaa89d4d3e29cb340a2c1e9a5f7990a0b4207705178bbd5e299bcb61bfba2e270a5c0719eafaafb5dc0375b2f4db17f0c089e2d76f0b23bc47d4c16a228a5025aa7563d9c7f65cad55de648acdd30ba0770ed502f689cbd69847f35b027ecc1bd8f612750fbd6ea19b656de14d8c8a20870a265e30e61ae251897c731e012427a1a7e223d3301f42872f291087edc4f0b29003ec3ec27448b06a13f1aae3302c036af629f796c1bf62ab5af4b67b42b0c1bdb3fa916016f229b800b534e729e164ab97577a133b370d2cd63d93db210022701fee67f464a23fe1cea2ed7a3f72fdd9164fb1d6939dec96f002949d029059f8580d638fbda4b437f8e48d9327d6bbef54be721b7a111e3febda57fe0f100ad738c2bbe42463ae284ae4ebd60e0df8631e10f39e65612544c39db8e8f5805a2edc182e8636062e024f6f34ff6bfa232c0ba299b4f4a5855ca1fc69d7735025126ebda3d2f3bf172aa8149fe3a29055ec39059ade8305e453aafdbcf786e037bb50542c118690d965f1610734691d83953a95ea8d5293f5dcac85114909e0c7ca52b36749236f3e42e1f6589625a597a744e71abda61d509d0c57688f9330a18b1ab8d752e8eb7925b8249dcda7c7f1982e23eae5ae8cb0288e9dcc91ab905dd27be0b954f3103dd0fdc5d32e52ac125b3cc1c2a1cc52317646a230a3d100e866aa9862157575cae836401f2a7d4c573ea8547d5e9df551178dba0ead7a80a829876b2722de170a99449b39cb27cf4bcec4676364dd16a46252473c47065076bb987d38b0e2f7e4763e25f8f1a0d0560d4a1b1f3ee20919f37431279482808d45908b85c0eada0c7f09e1219281a9c82eb1b789e2544b1859f9474ec32360ca4c04e77c6279d68f71be5adc006b10ba84cce70e5964d6fc4738cb5c3231d01b7445a35f9404d15e7d8db1a98ae8a979443ea2951525e9161f109490b0eba078784041191dc9f27694b10110cfe8d9ac1f697eb56ffbad403dcfbe5be76620914968c7e5eb55013d34ab3b333a7ba391b707199d5ff2468c005b7453eed42060a29d3cbb976ce14a248f05c56d526e566c01c8e5db560bf7cc3af3e907bdf00bfcf8c78a102d5e0f3901885f5610cb71d11b04d0dc753b419053fdcf10cea068dd058c9a48ee7a6c10b4521b440176d5fa1a3a1c1b188a5213ed7ecb97f400dd23b2950c87c3f21c8280416b328197915c6098dc3138817d324d0d9a2f9cc0d5716d8bbbd50e5ca617f80cbf54658858e9233f9007fb6f41550ad4cb0e1e80da45a2d238d9f6bbfd4f0b3494fe2c3fccc2109c7fd1ba65d4d453d843ae419038bad0cf6f85058bfe2cec3e7cd478161e0d6a7215af48f20ac6d87124b7c600493d3a4f0ebee93cd666d619d812386b5e8238654610bec8a2bb3d1ce16f7640d7ca08c9fac9fee1d798b6da88d8108bac91f5bf6d0711a43a883c38b6181d2086d8cf87c52130fead2369b31674f4abf5e937685366d20ea3bc931beb5d48f022cb167b165e9cfa413c6e03c85b32d5178dc727bf0d20300adce6aec7377ab0fb65450f3e8d93043d39157750ac1a184a5ba6bd87c822b5a551028dbd99ce508db2193ab89651ad6ef85167f217d2348a0576d945e2e26fc346969343fc1770cd354447e5a4b7542ef074602d879cac63a711c90d1c9f3ce085ae9aa69373507b406103009f1cf62f0e9867b2b1661a17f93c75348fd712e047e0596eb43460481abd3512c7912a1ab0125238fefd41be34270d701916be26e50ef1284c02a0558491d008e53c9a6a5f6ead351b600bb8380fdc39fc69215d943882192cb5c00b64f3eae2974b7de4607b4b244067413cc6410e32bb8c4982f0d6149424277027bc9f524f501c021a729079ba15307ca02f014c112eba87b123243b795588f0fd46cbdea10e430428a91316060f4aeb836798a8ffdaa2d4346304a70c98bea04168275150751918c1a6fb27a1ddc6c20f79cc851ac675c0f1e76f21234efd50aa6926a4b2a5c359cc62414149f96f7703de46ad7ec685925ba206efd7bd26f0f0320b43d318dec6448c8a20f2997d1e7b0b7b696410c1e3a8071146b3674a902a048bf366c0fd0470c6a881a5ad0dc9c8208676fa61181acb8057f86da2d0e0df2341097edf8164fc62b8574f3300a88407c8cdd20fc72e333f0e93372ca620d6805bd4dcdc8219697520400b1d817b9d0fa71b0d49c09c1c53b80308594410a31f7b2f92dd8120e655a933f6b6ac1eb1b7b7f977a86b92688ee51767c3196081f8deee7591e149e22fdc6ca603410a5aabea68b46bebe82c5fd2d6da5b7c404dbf88cdda802a65e0c169b148beeed3baac4ab563806fc73fda42c4258538703c86dd50de98b1c673750206a074cd08674e7607c9066fbe7b5974ac9193cf90436ba4ec39d9c48cc02136feee7209c15bea43b82d6e0769ffddbe075a91141022f97029c0fb1029bab7d418d17e249b3a21799952ddefed0ceffd45ff045cc0c0da1085fe9d6d996c0d15ae22cbe6c9d385a08093b8a15b831aebf80b46e1309e1a5b106778ce66031f274222c6c1233e551adab33af62fd49f8a0e933875f07ce64ac0ee716bfe986e52f2ea29c31da11a9e9ab571cf9dd3769188c5cd21604b1e449938c68f9613878871bd63b8cffcf564da1181735f198a5a5bc1c59d401f502b06268b4ba4ec6f71152157ce09ffcb40835798edd141debab61ce7da40555d82b4e4264ce4632ed911bb0f7d117db0721e0594fea6d918e57d98369580a6923cce658ea52fa4604aed51d40bb1a3fd08263fe25da68014f5818883f5f0721554dae8889df1bf89d5f174a8eafeeccd2db5cb272ad0736211b8f388d3409820670aef2f43abbaac6fcacf81fca03d71063182b1a1fdc94be908901a08e04f10ebd9761911fc043c1f63c8e816c454d914328487015ea722ac8985b61a30318fdbf4eb49988f101c262a6048f28c62c821a990d96bb8e2d2e723c12d6720d8f602a900c9831ba6e5fd777c47be2f832d7ca8cd04d3c1ec55d798d0a9d10060cba433d0a236ec2153cfd2ae3e65edfb7f8292aa4e6549791c073c15239bf07afbc6f720a97a6d357b5ae051cd89166114070281f5e19029588d9ceb41bf50aab847fca446a75a634d08f336b28f6d9da5d84e5b1fa0b3dcbcb9e5800a42d0fef4ac225c6e8d10e28d1ba7d61bb3653c04f55489e354a30d796309087f74d0bc40d65cd02928f25286b7e1e34e4a80c515a4f708655155279fe6b65c8f7b907f8b596942f9f667403640019b684bc941815b9a45ed0a6177b64d30e174a2f09e3f9a1ec385da3c9d83949e8f963cf541d2918d33b75172453f0490c5f3e800601558db2172044121e46697cd1ed306f3b5db8a0a11a7f40960dc7f483742a015df04fb438377961861d58e42f4ab694adca38b347b5a543e9dd6342a3f8ea0d9e2803ee4cf91d7571c4a12daa1b20302819f69b076acadee87db7adee5ac007055cc326ad2b9950416b4880cb985ef94198c7876475f8c1ff3c9f68a3873b0ac573e69b6ea6bd9eb87394bf7da24624040ec9bbfe82c2a181f6d492bb052c0e10329d9e2311233aa63b8ea8d1f58f2c7d4d11c1cc83df45e6583bfd4984d202f3a8d980ca4649fb8d8244a02b7e25c4cd9c03686fad46ad9b4a072174a969014e3e57c04bb354b932b7fa46bec7c6a1061c28a32e2725675af6e8371b2bbb0ba29bad67b7725cd757efd867a11bce4b438b50ba7892a80fecef7b3d4f01b90e450ca02fc5f9d284fe3d9d4185169472890c031aceeecacaa9d0a9bf94363806e0908081b93a51d6cab74f7ff50a815bfbb66c3d0b3cf2c0cd298a9efcb0f90a97f612bacf8ab453417e06ac2ff1bfcdb5452f7787d92d23f37b7b336ee1f6098b3a8a732cdc9a5b3a5bc71e53e251ff58021a76a65f83563c90e6a41c51d30b6f58c77cb593a7077729b765cdcc3eeb7dc3b1728b722dd468f71d88b6437d00e7903c0d69102d42465f3f5fa026462f8e7ee49ec2bc90dbb1bc9ea0422ca1070dd11da091e187adf5559a53edf65776239a892f94037de7a9b2c80f7bc6100f0fc0523b6fc4c586fc3be5bdfb0c8d8c5ffaed12afb8fc6cd388bb9862aa790aebcac9893383d02cec71c4be68670163ef5ca9cc06c0fa920afd5291cdf5a90143e8e1e9305b482b56ec07ee1ba756da9cc5dd9e7aa2297c0cda4e2ee4ab3f0ecfa897cfa58cb0ee06ea874aacf01f64db40ab35419c30a5142a2131bb0a7e0f8380736f399827f9f69dc2bed7d1788c929f725cb32a2876a3e38dca691d2d05ac8b71bc14d9e59297840e25c7407be532600337d1736b313c365ab9832ee30f4a8da9e2534f82db2ac60fe67fc2daf1be8cd3e67d533e2bf05ed2dbd0c4390061593d662348bf4cbad0d55494f3b132a25047cfaad4371824d5d4570b0e4e076d45801447f57a84bed1701924bcda486ab873723766d490b801a03f5d8d55010973610f296ffb0f6ea472b5c7d7015608d6c2298b1f0b0c9fda450aadb620034bc85e5372d40140a124568decdd0bba17fa5fab4375bdb09a3aa5ef1aa57b0614e391b03b0d19debccdba69ddf7d0b7f70ab124bcfe6350883c5d09b84e7b082d798c6841c2257e56515b9e035ec80ee718dc7b0cc2fa5279bbe46caa5b150dc3338165d6082769c9d2afe5275f16b96ca6cdf7c34ce77f45064b35ecfded0bd7729e9122641ecb47370b5188991c98994b9e5c3f99979a0b12d35cf980330a7a74bb5398e957d7d5d9f0b6f8250ab18e31a554c22e8b6e6b0298024a32860845c68177507ddc775c725ababed7b99eb8a703a9f66b6518423d589110db9905ea19d5dae99402c24ec245c6541ce0f4a1a4b783bc241ec6e028eb144af2590f48d20389796fc1a1c843ac91a0c0abdd77361e993d7dfc4ab8dc8153a974aa01d38be49063be1fcf16bca669dcc2b8100467bfcc58741abcc4ef7b8b5255eb0a10c8e7f291e45e4f3f0343568fd7e2164c3478aa1684239101f57528df280f0fb26a85d743ff05642902a3f7125a328b142dc72b9aee04db18b8fc8de222240b549415057623eb8ed01e78f87ba727bf7cb781281fc14e7949af403b1aa21f0552ddac1eeb7b534c82bd05b6e77c477328e233560f6c1ce18cd9b8a7ee178e0d941f5580bc34276d9642719c1d303eb5752c9da822d68e3abbb0ac7f2f15bb072b68ec23f09e9f7a501a77c0470656d4e73943ab0085273b5c9d2f2383267b0425a3a106a184c120fa0e5f7ee99a8d2eca8fed6629ce4ba04f5e87abbc90530b7b67b6e8babff7076791a5e1dfff648a399c1d52c0f48f07f80db9d965c1a4040038a3f0f442345276b2f53d50730c6f41bf8d939cd917662f2d365cab8e310145d46e33717b3f7322e0877b6408f7557ae1a6fa4800f48dd281511837286c0b8f0ec34a29b0a03fcdbacbeb237969cf98adc28d7024a99f9cbea27150f9670cc7bdf1c2bbfa85f6a09106b633e38711fb8e26a92951efc53016e9ab93d92203a07cde9bc6858726940b9f49ea88c80c112a6540a367bb8ee8b9b6e26ed69c09514cf5f6d6b0b84c3793c52db1be69a3b23c4661be708a9e90ed47332e195604cdfc6ac9eebbd8edc2b1218e14a9bef8a7de5a40118f451f11471c395671e70865ab90ab20c70b044e9a074cf39bd86ec81d3a3b62cc854040a401eed1e9860cdbbaaee46552ed78dce9eb04002df8fc4baf31c34a7e84591ba77c2bd25b1000b0118e113fb48e80cba2dfeceeaa2d7498f57c337ad456c91801b6fdec47c2010d2260a83e44f18b47353e42c2059b7f5fb70668999d6f28474fceda09ef8802ec3886fca7c96aa629f43b68b7c17b007be0ac14fb3cd168d090983e294f0105f274dcf8c009dd7effd966e2c00a4b1b82fb1181abd77ccde26b2e3936d5b8023ece1f21a01bdeabff0cf6d95ff8cca6f28215b94f067a233eba9fdd35e9ba04a2a1b2935eee0559d11d5daa216f63f198637e9c44912ce0f408299417929e04a207f56442f18f313da846e3bf2ec91b83708428ed7c5fd218688029bcafe3023c7df0055609ff148bf4e5e7358ee3d531db52e610b6c209503589a3c5a9e601b781e38ebf4d1bc918dc27944ea019ca34f61aba20265d41c5e3a46f841ec703c6b9d9b476b4024df46cb6ec36dfdbb9a61ee6e4169bd9c4a44cc0838d4db90c6a934b8041ae73f53ed83cb3dc08b149f1122d6fd2b00e6a90a9427c2edb1501f142d5e80be66e0e95ec03b2b432c08177a6bcb6a6c6716f722b088c49c77c03097e881a2fdd7958f8e14f6e67c58f05ab90efd6b8d527d0cafcf069721837034c32e273465c99ef2d085a4e42f4387b8f6e5d8e935027ac223232686160880db34523899a0a9e7ec7845ac6e53f79a588f603397c7ab90bd140eaf8e557fd07251102f3c06ba64ea7952e51213eb41f2758387d9557af2120dbbb716ed3bf09d2ab7f6cf8b6047e9c494d11fc82ca330ee7865632748be918cedb9774300208c5772c2a8c436e3c078b8205a019e2943026735439a4ed2df3dc9c631b81040e1fe886cecb3a227b099e34b1c0f044eb68fba54d5ebb1b7e34a7c91b9ed6dd0a0d3375dac33416fc52fe1238e02d7b399a12fae1cbbfc393482208cb3067f60328d8e95575f72aef9bd05c257c4bdba30e9597f4ce08b2337a4598f5758f3d007a4496b5547c35dff21e9c464cd4c344e25253ed9f1a11caaae80ab4983f9d06b51b8b9dec656aba5d05306b0e32cd7502158793ec8df951fce9f255cd153909bf6f85bb420ba9aa5cf9d28db340b9e78068ea57f2514ccc2bdb771d6041e50d7cb35d0b27c433e35aced3feb62b59bdfb0b57e412913da954265def2f7635062f6b8dcb2956c10fbb20cf92ef6a84efe5a7f271e017bbc7e5793eabdc5bb4009e8affa63acc1dcaa9828b8492ee752d678ed4bf70d96c513cd73faf3b3c210aaba88bca14e0a0d7429836550b93aaef7c6f46348daf790343b5d5244a102f0d3569edac22d312d02aafee5ba3cbc109a2dba3de91a9a7884353d9b9910af40a26c49a628d54bfdb1e6a12c563fa12536d88a85e739cc814577f9cc7dc887200813c9d561915998315cf9b40274b5f11e51c5e5e4cda9a431ef8b294cc4254076fcc29572d438ea9639d53d231cb6174a7c8e151f3d393aebc2f35028b9d320ee23b7a1875fb6a9c03faea35379e48c9b662654d0a1ba22fd3329a0d25e99b043caf9fef87462baf6375f96dbb8ab590171ef4f3d149227c36cbba604b875e016153e3d14a1aa955872adb034b32b712fce56e32a579dd6c7d304d9881a92c0ea984a64bac83446075cd4ba81bf40d76e1b165bd45af212fec6d862e8ec92008299ccc983441a8b5ad400115cadc11926a7f3bad1aa56a734397b079cb771e0e5b8af48c62e8248826e3b2e94ba7040b842f7d8b0c871c72059aab9f45784a0556d706c32b30808e4266ccf3a604036b5070744596f1b0272f3dd761fa30a903322ce5f470bdef51a6f93308fea5867e7315b487fad6249b4ae975feecb5fc00b2d80f4fd7a2ef5c2b5ea7fadd162e17f7461691748b93eccca02785279af0099b74e5d75c3ef993f16c42fe0b64cd0e6b86672ff9f1503c7a052c7cb83ae00f98c8b02f19dd2f68cd728cd3c413ae65bde796c466b9f50ff2b8f1d8a1fed70e5a8568d6e2e52928d708f9fd7a35b1a36ab76c29bd0a195f6f878693917d1f0d6dc5b7d7621c4ba1921d71b3a2647fc4dfda75576767adaa2c9a0cbf1913430232b7a6421cbbcfd6e215fb331a581a7b11b4c627dbdfe9a6708f19703288280a90a4b91a976d9310f8274bb52e4ec2a295ccb66a7a655821f40e86243f82c70e2c2ca42033ba20c7cf95f93d6d0aae91471f6328ec5deea7b5edacfedc0f9a036790c5d0c673f5ce788434ac64970c2f8fe7d91118ba6c837c02a5defd742e00603e4e9033aad671cc4eafe8a2f622c45edcb1fdc0d59125ab768f0194aa4706701429d2570638c39a3bf4e309a9af137f80917554c5c3e3c6c83f8cc67be208e9f4623e112c8574182582515386f1734eb1cb7dcae03d11dca89b924db15102aeb8a7eb785372aafb7efafeb44759d0b2b8664a2cf18dc0096e100b50a42507701fb4cf6712c0657151728415a56ac535743750738da9c7b963a543e212d00cd7ba29c2a09bee6063f3006cb6254a1a13a0ca31978174bbd7f66043ad42db00513dcc8e2d08db6fb2d97a33254d60820c24a259a2919852062379260a77c705730a716310974c558fd95474bfd0e58303271ca9be5446c8a3b51ef2a5d05f0e50daf815ebc775ab9d3209403d7eb9c2df5bfa4842fdd92e137e013be1ba91017cd09edee2e5ffa920f3342fbcad67c3bd1d6b62064ef10602f418e60022be07878a4b97f78ac04de41c046164b17b99ddd5ca5c0ed180a22d7289b3d8543b079f574a396ba5d2ac2e651696ddfdd443cee94c4f6d3b8c8b2c263b961c61c906dafeb9bd8553a9e5b8508ac597cadc55eaa1afeacda2f7c7d622ef11d01941000e2813e39e38d256dccd4f9ab9434fe2929b9d4885d247c039e48c68e873480115d33641be48b348ed613229dc800f279a9c486761230732c613efffc92046004c8680e55bd65d6ba4796e5371abed9674f3b34fdda0cd5611b4cb322066ed0cf4c877c742d949997349ca090a034dd58a1f99d092940101ccd203ed2334640d6290a8be88a6690375f3741fae7681a38f041c719832670e170aef114754610ed20c2c4426124882193dfa4ec11fde2873c74da7fbfe843b3331cc7cf7824809a620fead1afa4e1b1f9a6c2759f8502d20f62a30eb8a98b4aea66ec2130acc0cc976bab7f62519a0ce17165b2809465fa2c49f1ad42fa3b10a9b471f59e91f098184849306dca5f91ff040ed551960a894fa2cfacfdb1bdb9ad54a7ab1146b043acef57471bb3a2dbb167402af6b2d07f60d3b40f9a2382ad8bdd40f8ec7c10de44f071c4c4a76a5d5df20c938e427f310685d90129d1c59e7f1a2ff20610207354d857cbb6671d9e023ef25dabd71beb6452c1dfd493a3078d9e72cac0eca05c94e9e19d2320cc77185c8a168facea05dc11593b964fce87e3d914a7474ea00826bc2477fedaa0a8c9a527d5ab5df3514a98dcb6d20ac79edc7dd34d8260e02a888c3a3ee2a14621a509d8a6e7d88318ff1430467e89900361c3e632ea60d07a47b413b8f325550daa60c400cf668a2393e1e827a27860622441bf2ac5ed50a3a75aef19023fcdd8b1cfac56e92db191fffca001d8ba028ccaf8fed3253fe0e26c5fb54df310dc02e6e8e3c2c56471a1b8f93eb5418e3004d2e7f073ad95701fcb702190b3bf3468d205ec966d99ba426a9716baad04720ecee1f433664300069200ed6d12a8d7319b219e1de0a6970640a80cf09771f7a29585502841d730884aca829279900fa5504630b649027ccaa188e6c837e9d8efc739e83d9665e0bb574c0dce396118b404014438430617be00ed9039df83f4290581f608f9a060ce30ca6f72b8897fcda42873b6e361df0703561fc26468aa4454d939deefcc7080b24e348c15876ace9f98ba44d987ba294ecf65be6af98b57c83cad74663130561163f80d2bb288d79d20da9f6aab1c0377557c48ebf50058115bfc0de328e06b25f143dfcfe09a1e70f501c7c0530976b039b871423e75a57ebb279c79c19052776f5e557f4d711f17f489bccbbf6280fd23d6758bc95083ef054d1a716a70b667a21f1d0e5b723b0968f30fca80d3a0565efd96ded3acd68214fa389ea35042d0f8dc655a11d9500fc062955f94ac763154431d0afff0f0bac050c9c18030f1c1e12f012f1f27f0219e4cd891bf8d09343cf175fb5db96497e94114a0db9056c14298c2ec256e6c74e912ec86690ff641f07e6bca21fe7390e5533c63ed90d12233234036d25fab897101f1523ba37f765c895b46e887d9ad1e05aed94290fe59eb7cbca08066fe0e707c9be8439593f64f73a103a90a793e86138018ef20baee898d4394bb27b3fbf4eb9ac4ad3404cccdfbbf1b954744fb47c63f97b0e0b75e2668924a7eab1fbeedc87c77b4d3a6f9f1395b67dee68d1bb46d62524f4008d4f54530b141de04d4b87706def51e66ffcbef8212402ce7e9a699cd677ec0d53a799f5d38c7f5fa3badd29b3eebbf416fa8492ec77b07fd0bf8c1e6bb01c0542ab97a713db71c406873630eaa828c04cd14fffc09f313ebb1ab9987dfc4c08c07ebe57725b332b7ccb1c84fcbd884a5dcf13b7b3da41d8dc5b0380ff4b4f0130d87373efd440f1ccc16102e76a0bdf2da80b1da176d4e4f4a896d13e2e0d09069d7a6785ab349ef5b1bfc713555f343360dd9c318e2caf0e8b2be2e80f97049a8ba98737abc13d3e0889ead1984826f196d714375a6df4b8bd258e20aa340e9a3f0a419537faa0d4ec81dcb6f003d76eca54cf7352fee019f2b897422da502f0d3a69335078651c2f22471c0534354b5652b324ed8be8aceb17d9c2c0c12096c92c05e974fe3f50932c5477b1eaa44f98b5fe6f1ff6445c6537dcd096d8e04e0838121bdfee3427ad5c7b7f74a5f893f69c4b9a7b8d9c2c6c9000d92450c0602dfd4552f12b70fd00ee826f7133b882514b485e52024a5a0f7f17c961e4d008956f3baa62a5aeb244e0271f8642a7dfaf6d9e375c2785b2b165af7cf5869096c1fd18d079fdcd123be5471b035a490edb694950208fa0d7c24fde553511a04c7c56688a3dc1fa4c0d2f45777885d3864a0ad9980971a1e27d03d99e954120dfba223582a23e36a6d45a18c1b3e7739e3248d7c1d7cfe162ab05aaea64ef406fa40943139765e94f8325c4daf063aef7076951dc7eb169a5f9152ff177dd40985083624c426fced9028ba7cdb2326c6c57a945eb856781153826c6907450a03990e3f7e2e78958523afbde3fbda2c907cda675a158bd26b3ce9194120f3750c2cfde093d1461d8610d898612f2e797df506c5f0415fd457546503b3175d6a05d0ca6b4774b59645f89653eefc3b3dfaa62f6dd69c7caa759abf3aacb7ae7709033e4e1ca50a8706aa0d1d6def49a6634085249ba0601dfec6c7eb6b95fe790faa502fbec10c95680b353771270d63447e19745df0e299dbd4ab631045f3b000483db6c66c4ba4939ffefad1459cf565a79a2045690a50f621b70a5820e80e07c7e22c1b6dee76a48cd56366ab45b6f11e7ad6b8cf9b8ef69af84358a70f98076d9528ae95cd7afb379f44b6e28c88afcb84dd185ad46b48bd158ed1321ff90926ec55f3fcef8c27495446426f68dddf6fbf3121ed5453a4dd64ae3826cecf0f243fac16fcdebd56e23e605e5f6e7b32fea45ade0e950ac45d38257edaee9500394d09547452433f834ce6fe96d7e010d6e53b5579443dda2a982f871c9a380b956b3832f1c8ebb358ed45b4b005279fa0bfff169fddd0a0b28cc263e24af60fe95e3f841eba39d674aae112f2966c79b90971f805481571fe627ee85530be0297dfef733027967b760c8b5e551b9d21aa6c27a4cfb66c59295cddac4c8a8a09901df84ff13df721a1dfe97577ecf25e200e49cce8546b4518c2e8b45db4e10bdb5d4df43052204814fa9b9dfd481b4abae3d07f3a8c6bdf5aba3d43fc00fb02de1b05108978bc141877e55f585a00ec0e8c45db0ce5f417ebdff7147423820fb8dd9e9e06dcd750e0d8c7dded3aa7a20890f8c9635b5c1d6bb06a27ec9c3902846127ad081ec7f8691ddc7434cc064a471b83a99407386f4e0a3b160d15480939982a04374f0694baa5b2f49d1b61aac4ce71d52d8ab6c1c5370abc0fbb020462b86a252da3e8ec63c12416dbbe02de24812eef3eff3f617ecb30158dc7420559c06bf945a3c64cf7e9be6724996bff928892742c48652ad6445dad83e6bc0312ba0b0f9718398ebe08f0e74e644f95d2cf8ad8d698f7d3b220c444d660ca08e15d00836ae96624b2e710faf3f25f0fc4fc7b34ff960075940a05ecbf107206fddacf34bbae8248dd8bcdc2c81eb1bfe93d29c1231dadf7c901edfeaf5bf704b1d29bd1762e2a723e12711c54070c070bd052f13ad82df46b4a303798c60d04fd5bf4596fe6987a7d588fe07d65dc753232bf02b0dae9bd9eff2547368797043aac1178b0458b464ecc3b4323195e8c3967159303dfe5c20fd3e7b8fda83100fdea48005d612df51748cc77aecde1ce1c88d1c9032fe8194874237218bc930cfabc2a2a2c751f9bc93e186a4e1f29da58ce441622223a4028ee1fb12c1788094e64d17c897051a1afdf57ef6d8f4315215561d419c1733e1e854408cce8f901c5743ca3149e6443b4697ac723865556832fdd6f66b27e436a8c58f21149ec0988f4d5dac6a58148633980323c55b14f71c1b902352bbcde4286b0a1fd4d420b36653dd234af6cb63491d6b4a234cf7260927c53a74e1e29cd4d83fa91fbda0f70d6a23ebf9bc95665bd22f071479e46495da15b71db994ca169239c0752720d15dea28d786294ed9979deba6e98f76aaa556410cd6edb4a18ffa5cde798690197517def40da5171a805cbbe0010533bba7922edb3e2f9c6a24f2adf1acb2a02d4fab101071c127f856f09baab9c32bb00fcebb2d44997265db3f1bb00062a0f84d18fca82e22c49067f5145e3f6a1fd731c3be93932c5f6a32055b18a3dee00481cfc5728b2a3d6ca0c2e8e0b716c380f0e0d1d2d6d9cbb33cf3a7508a5bd042f70ed23eb5d41824e3b43bd925c00769a65fb9d270be8fcf238a44e44599409ce9a701d502c47ac1ac8c0152532141ded0280233d4cc7a837b1aa498d9bf80f5cd7ff744340c1f31aef16139255995487563009b344c8b7c33486dd4c2cbd0768d30cfe0920d756d76591ea2bd56142ce41293d64dd161d7a07b80052857a01cc3f99104c570bdb0382fa3ce4b9b29f65f10e907bf5543c69ebe38a43e4bd03a144094829f8b22ed73c948980f5095326b389bc23eb24081d39fa42334dbd095e9fda8db431bb4bb4742aa27fe04296b0c42441623195695f9679cc5254430e99c24390ee3b6ce9222df323304627548a84f65711e06debd8394d3d30e3d608e388ece6a00d1bcc53470e552d63ef833c565085eddec9f4cca1af7d096a0d043fb2a05e8d8ddf45b221f7a9a468687e3be8dd9c5745a84829f88b5d828fd5007b1dacdb0eb20d6136fc09865f62368543ef136a7ce9b444f9bb59e3c76ea303b8ed6acbe323d4a0fb8b290fd1f89d76403338a925812ea7c6cd14c7b45a4d0826093dada2ce3d08251114d908aaffea1f3fbf39ca1ebf1b4bf4c69581a15b006dfeb50b566fcc8b7c7c21aaf43070195d7bd38fe4fc39856938f3407afa8706451657561d0a0e21e210891b84e2dda91607039f0a360f3c809fa2d543846307d489a5a9ca12d4726d109f210eb85fd14498bbff9a4764e6f7912803f552e406f37ecfb6073968812b1f551397bfa6dd9bb9d69a096c9d1d50c1a117ad6eb806dabfb34b35068c84d4509070094189e141365cfed61ad499e6a0dcbc10f49501211aad79cf168b4dc8e63fee586cd686c1e340db28dc14978ab26408ed27ce0c38b6ba509dfdb473394a330b415c29d44822d78948cd6ebe47a9decad1f3039c2945d078f6f699b3c045388f2a2a5525f06d947881bafc3773e9ec3ec8333f03968426074df397745ebccaf24da52a8edd0632704caffe22b565bd92f97159045847af50883693aab46554bfa1c08c5a00bfad82b3f887b7b997662ca27425028f28263cc481473ecbcb1880da3a5e7b0561b416beb2dd573311a10cf0eded012f14ab0803be364afcf4428813de4c13c840075c3c8b49958dbe5214968e7c01e4f8efc958365d7b1b68f8e1d52f9347341c512bbccf0e942a58d8898977ac0f2ea3a91bace12e9ce74d2ad1b933afca49d1a185bb6458caa34e3b4725bbca0d68cf78b0fc8d0a8b9671ef29ca9d6acf881e6f49e88347f0dc0cb527374582090f69cd64acce915193f258fb2fc4011b8ee86d3a26b6da4dc3817736d754dd00c6d43a66c963898afbc2614a3d1004666e5ef0ab0d213e8a304f113fb81b7c0529ebf5a8a5f2f7bbb5a746a3509ee90ce67ff7d9b86c5e34fae6c96117b25b045a9310b0f3d758498bb53f752c783799a1be7b0df539ecc257625cd0bf88080b84b84d71444673b8ab0d5d769de3d1c3176114bfe8e7d263e9595d65995a7708889a9bb22c8187435d714641ede400e27cf276a0a183e744ee779c7028c845019e40426b632090b2fba6066713968d85c7b05bd11cb53221f4acce4844ed75031d7e4c2859f2d86fe7498e8b54114decd2f279bee78bf38cf5600d67e3d1ee0f45095a3721aaf0add4d064185e41ddab08678d75a45aa6aaac9557f29ca4c6039980a727b8104088b9726c8c6707d630ad42855dec5419cd23b4b428801aca0cd8c46db1ddb16b1f1a41a782145a30ae224d6396ec92c6228e797818b6147200ac89682adec773530619a07343340fd48379c40811d2d7bbeaceb84658549c0f581ce5c2c2f1c6361bdecc4bb66969a81281c21b73ca462773d1f2f4f94ba10ada69f70a2855eb8f148a5ead427710888b7309a4c350e6b0629ddf5e0026d009d566b6366ccd87138094c651b1090a149570ea680fb85919a1138ff42c61df05eef529622b02f9f8fa42adbe99f97563ef7088c5c16d7bab8a3649ba5bc2940ea9a25120bcdfb13f6ce9ec63244d7b8c2bf8fde1fd878c2606e1f8f528fd5600bd51011101e47e9330ba3caa67de6b3b28bdfa37ad0f74de308f6b2ba9504e07b92d456f9901e769dfbf85904cd23420fad266b7993e342374022666cad37b003fdb45bddc9eb993777c4e1a6d5475a111866ffeb71e153bcf6069f719e55107e6f839af20d877f53b73ee27296cfab7c460517376b6ea89064a809ae3b6fe02a9e42b44fac68dc634b230c0f5a2e0d75c12a5fb6adf8144bacab06335f4b208f0edc19d622ac39388c49fadcd36befe27b756b1dc3b14d9ae7d932959d2ac03c733b14925d17d6d77ce25646b8763a1d8218661715491e478b38f6231ad230ca0219db186f2bfbe797572b7ec108d977f3c33b70d469124c091d30d5442a708a5ba69f371e5a2273a613dda59b61db3f84ca13f9d64b2ac029ef6e3581db60e8f3814440f42066bec1d57e12186cb85465fa7ece2189a0c81f65c2b7c77050efe073dcdde890704718001bcc0e53d607d549b7db6e6e2e567552f1f6e36d80ccae5db988f9d07dfdd6aa8755729dd03248b579bf99f3519e01cd641572a1d0fb02305187f55a962d8e7e9f1a5a2ded0adab0b56332757477f4bec3ec787fe00fae3be5d769edacbd0ee5fd4bf149724c18e11ec0519db9633dbd399bde76207f8ab3a84cd385f48ba6683cda12e0ddda3924fd9a1ee6c9861dd3b90d106cd0338e220889661cf0a3f3c395f46db34d9f90bd8531be1b04563eaed6a55975a0129a5cb5504c7a9bbfff37b136f844a40525790d0e7a038dfe416aa12b6818003e53d76d12164b5f516b909e9544210952187f59ca599f4efe9526c9bc2d23e07219877d7004fc29b6f34274b0d3fc00a07fd60e56f795f006b06533f11905c08e92adff8f36772c2b7d7ba25924138289d0fe49c3ac2a22f568deb63e7f36203f443cd90f3813690eba3995b827e100bb182dd8d6466d342e1667f6a89b54f0986fcaedfedcdc8b9bd1e0d4b4cb1f2284624c72cefff6d2682ff0e66c68bac035e797058bc0cdace5ae99b5b8d9ad1045f509e5a6ef99bdbfb5dc34fbdaf440acab73ae2d4cd961f965b2ae03349c1eec1783f6a73b29698f43f5c8f0743b70ce4c747ffba98f3d06e235375bc1655f4ae16fa23af4a8c8a5792dbbbcacba0075c9c3073762b45f285c9cc3be8eb46ff3d0ea0a408606d8f353d3ff32de32705ba6aef0b2de37044696fa9b409d96c1d3b3f85638f1b95382feadaad4141640eff0a94072cc96e0ec8a10b47d685987a12a8cde6ee3b1bfab6fbd4f148108903cb54a0383106dfb50801f0e6c722e1c4c6134dea7b923be539bb282a4e3fc50c4fdd7ba28720f7147d1220ab3539c23b881576463b4863b81fd6527eadd56e0f6433d83449fc54dee6c4bc82c16fefc729197dd8cc6ead7bc5ef6eccb7ad2d0aaf1861c8e91e8aba07d1c5776f7378facdfe771d5f76fbf7551b86881795c5011e9371875fffd8d59ace091bd6d450c05a764aa20fc9760a4786c789eaf85b0d false -check_ring_signature bb47a511560c7eb88e710f50c2ea47dbb4389c4916ec19edcee04853b9165705 0646e45ef457cd8637960284cb759c2dfc4ed6628569a36676ee466b0d78a7ff 105 758f3a7249beb47702a1f913234cc8fdcaa50d81c399247a3823604aa98ebe6c 29e286e61d14c7c72b1d7e87ca2aeb820bccdd2acc3b7b2a0459436446096c95 9e45a40dd0d4d7a8c51b784d8f6eb7f8093291149e9b08d773507010ab6a43ab f74630b121d8837e8f82fb069c5c2a4fdfa413fe19822b4acd71458cd1449035 6275a3e58e4d2a019e8feeb0c5b23a224110080ce24347a694d9d727fd937e38 c743080586c2316449650ae8ace72d41636e6aa1b1e1f9e0322ce1ac7135d86b c24c4d577f75c313a6dd7c3baf6f76ee8092fb9389e81c5971d0724331cc3c77 9e846b33814bb3d36d697682392bf9e267f4aa444701fa7ddee106b7de939940 67d53d50c0835c326f4112de5fdd2bbca05c2df224152597ccde3d174b7ea937 8f7ffd5fabbd5c2df7bd9db633829f40a943e5857f2966835374d5b81c94e1ed a6ee2e158e6152bdfc5c7e0932dc415d6c57328d040f3e8092d4a6875206e128 2064513d754d54b51cb513a10a7ecc498b93dbf820271f10f74e322dcca1d6e5 c951a89f0844f7faacb67f6f004bf0d2b5d846c8108f45590c9f5d5257e19f66 59edbf51b328a0296897fc5cca6502643ed712242a3b2a3065c2521dd02ba92d 27590db95f5cf84e1c08b004f143d3c675e51e8f49d88711190a3b463f665996 338d243a84051632571cbf601e41195591bb1e061febdb3025a848ce72010f61 3b53f50c9772650a7b1e575b0367ad01a72a58029fa8fc494c874352366b1c4e e90ac22aed7cb40b61fab3f04ccc96a148f959b51e2f1d3a3926e7753d152495 9d4ac0204ffc263849c02ac8381f924ee076afa8756014ab900de18fd5ebe8f5 dc07acae81eb05597a169a9252075d15b7f8bec1b7b36d1ba2a67c448fc3f8e1 0062cc04c740e47766a55989463c676116e3ca68a8dc14938c735e10685ff3e5 fa5c3385b44796dc760172a9543175660d15b8da2324a2a4ae6e9a8cdb063fc9 fad986c7780fd91e23bfb5e6c78c8dd517442c13bd29661bc41eed516d110196 ab267d18c5bd397285ec2c36875c4c811d1243001383d58a196c66389a348cf9 3b35801e80de8d8cfc250ddf1b6004c898dcda8867ba2c1eacc02de7b8d433e7 6a1482fdaf5cc86c1c3c703dd6d228f3f867511a1b02d42e663cb94905eba341 57cbeef71787caba3ba36ed525f0f84460d256193befc90400c81b15569aed4b 763f9174626df632f99a529221b6954faff513c0c0ac38f15bf1970cc08681a9 bec0683b888a739b2d5f2c44ae876ac9440a71bdb9fcb3ced2baaae82adb68a1 9e9375e4767a8180c42f811b9dde2cfa0dedb7a38d44b90bb0c4ba582c4e8f0d 3175cbdbbae5d9d08dea6bd225368eb2f7aaca3378d8b175e15fbadab5dc000b 4eb489000dcf9872c254a5122807d081f1f10719163a5a7da98ab90590dc3f28 60272735e0d4157af04f5d9f3832f79a5748dff997888bd5655b870d9282ec08 3ddc08d88e77bb2d28b20313cbbcc6cdc6f373923608a3e0cfc9082896724322 9de846490289a2688ff94f073ccf41619161adb07b998352184a8346ef830d8e c39f018b6471bb166d7b6d0cac17549eca1029245b0a2a8931d2efa5f764f828 7bc0cf3cae4e7e35ebba5dfa8a205b050dead5d522a3afd88099c45dc7a15c9c dae33af30ac2ef764da794ff5ef7b3c30e43d6e625b24e328e7076de3f75c50c 5b786902285b4290dbe0c5cb3a65b6fcb918bd57cb6525019c4e751757ae4327 fb3db6515c39d442fe96a4780d72e490de7a4e4dc5d12f3ec2630e6bd098c028 32e8ad0823d5f94afcb535ef55776be1ab52d446b4a72be9706550f9dbb4f821 8c5841171cdfd8e5b43d92cec90d93add337ffb2fc61827bca583e07ec63ee2c 90686dfb958c36f4864676c77af5fa27d1ec743f0d8216a9959415b92c37d506 19efdda6b9ed6bdcad8ef0fb0f367bcc5681c714f1ca90482e73937e310b209c 39a0e3c0273e9e3837da7aac74f9951bd2b7c257f8b0eb5ddc66f92c46d951f3 49b48d769b3a510919c09681e65fd1e630c207f894f26da2ecbe31eabf9fbcca 3962b62036a3730b96116636577454d6b2438d7c84346c30b2062b862d0525a7 0b0593ac1b127c68906d9e4229450bac3c01adf2f07520f1468d8284443364eb 24b212d5723de717d2980b9ade1664e8d6e954916b52993d807a138b1e4d1af0 2031a5d37a7dea2cbd1b67b8be8baad204a35b762a7c5d66abde5851c52593ed 1d4b1c2dee82a201c4c5549e496e30fad0d403e511d695f078762d7e4d5e0138 5f2246f226b91c1c043f3c1930a4c919678fe8661ed91bc62e79e5c800896d08 b722154493253251be7c83194b83f9ac9f95037e63f3b4e5b40b0a7b8bf53e8e 0aeecae0a098da0c2f640d48043824159d95132d45b0de7be3cc188d4379d863 c4f8f27fcf58c1dbcd69236fff31aca140470b981eac5bdc54a2c0706f7de0d8 02b969c780af6742265edfa0ced4a8a1452c775092c2a262a446baa78a6b5f74 ac500ec94e34f35b2099cc0219f71324110f9c45bcbc4537363d8f613b73a103 139464b8a4eb46ec4deeba5a191835fb4caa71fe3bc7706e26f452dd3df78e18 6324a2424a519892702bf0c4e930e9e3f7206ce135f9e6859131e49d989518de e5ec76f27d4c3fe7212287351b21ceab573a93fa4035cbd9fb8a4addddf6484b 1a7aee9e7d56f8617889e0ea91b1fa0dafd061295c1b46d06ce7f704e9904e1a 97378108042c01c4a3ed042948b1862b10e4b55c0d24827f82bea49a18bf99db 7a1c0c9866e4af3afb1d7b28b8649d403cd325532aa45e737d6b12894866d482 c3237ea704a143f1c7c5a387d80d3c31500658f0e3a981cdeb9c33537a43eb2c d10f979522cf06afcfa22d8b19b8caa1510575c6c99db4556fb04f070c73a367 11a50356660f05758b619dc80ff75d78d5c2ec7e77e2ef95c77d418e3f7a5e5d 8ac6e8c53f09918a832081585098c681a3b1515438af9e45d35a83ceb51cbb14 ca023cc34a94410dfbc528f5df399d410b572614baad239c505be5eb9ef8d6a9 ecd25718d233f9033bbdad9536ad5c66e04a3a4a5f24c3cc902bcb28436b4c46 ec3e41855f7c1786e6524eca25119af510a20eada566f1971763f53d8be7dcb4 c2d7a25b4a40de91f09e5709cc1ef84664f1b5c23e2bdc715dbb6d0d7e3f3f69 1e6c9b6929cea1ca575a726a5c048f9ce67a99480cc640ed67601d38238dbc82 6a3b7b639048d2c99056988373b4163c4c7c49441a1ee100bab0b2925b23a907 0e5235f4e21f7e1cc7d7aa7ff6f5e4e1bef1a7bcd5c8249741f9ca4ec63192c2 b5cecf25183a6becd879529259f3e1e6d81567a6480d6b4a5251030581ba31df 8fe69b9aa857a1208f9a7dd25f0535dab5180b28e96322a11bd4eee5ee2436b4 3a547e5ccd23e3ac5219212b2e79fc9735947ae974a513dccd7ffd659e8349c6 aa747a3e572145bd124ed8964b076e3ec9e94d80c9593f1813b4c904c443e97d dd5d6619c3ac5679f2a6872538c5f1c5e9967ceac44c33f6296bd6b1219ba3f4 580bf3e1cc1a5c676ca0ecd4de3ae75049558c618dbb2e9fb633d352f75cd4be 8cbde00ce32ecc2fbe3e6e33e3aaf1a83fedf2ae6897d575fd9d5e6aff7af525 754756b21faa79496204038cd3856b7c1f83515744f82faad773c598a043a586 df7865be23bb2f943b7270c9d79aa427841f79a0da937a319d3128b82ecf361e a4974d32c012060dc623118e46c9844187a70c746459721486f1468a76f57ca2 9b2190ddf92a0be5266536cd84370def70dbea9a0c8d43a82acf2de21ab32742 90605a955d7393b210d36b3f758d2994fdac8e677b45cce9affe56880dca20a1 036207315e5036f2196d12493e3123ff512d83a3ed166cb4d6ad05cd36b83dd2 8eb5b7dc8e3aa947723329a760faf010a1c363cba464f13c20364957151e9ae9 e0c1bd13217e3827c05695a74f5c7237dd5221569710d28f063ac85ba60e6176 83a59c461c9bc935f3c451eb081c736fadf1482a5f2ccbe0650f154a6db55374 95833040ee64350cdd0b4217624ec208e46ba1fe9aff020c1f1abb93f864cb32 9947dea6682e80687e1c2a81467c341728c441e7addeca837473b720604f4311 efb0dec2dcf146a699a0944748f34b5f04b46589e9df9518b2036ad9fc6e565b 737f5a77c10f8a457317becd8a0bf1e199417c86b0911611be6eb978743a5d38 ed19e3f79b5bd036a369567de2395068b32dc613cc693add61b22a3d94f1f025 258e1fa53eb01dd9d0b89f34a5acc29e66ededac454646fe001d9f29715cbb20 d0af2b09f045c9b80d17016fb0910e8c36c0c363cff6c4358363346d34cfa03c 0cbd2b5054699cde4ebfa3f8398d2de8b85a5518b8f7969ecbc7e13aaf3ca01b d540c95052467e0d977ac7dc22ec36dd4cd03508ab7ca0bf5deb5b0e167bb55c dfa365f16d0ed6645d908efc104a69fa66e699e871023145645df7eb2f7cf620 f178c4cd2b6a32e7e5654f257d3796ebd398a28f8f45326cbd2eff1ffb2e01de a5db5899b905046eb3d38c78c166f3b0d62bde3425dc536cf5005f6d43662884 cad43fccfb7e544cd06300f1ce7813d96465442612a1b5dedb53028b82c2255e f8b46426a2217b9d903102704062ad259717387425854d4518edc893c929796e 459c91e559dee364075a261c05b96b76444943748a07d1f3ac53c4e461779418 23330ab3da7d9c5b22dcf5abe92002f5b9965c826c1ff1584faf1eeece75e4095599e2501c517840f925dcbdc4f5dc2c406112828c1e4a185b0e7d67843c1d0584e9af46e8e56d8921a2a6b882349c21315ca8ce4796a3fe4f6ec8d94742530bc0ade4cd2a4458747598d0478ea3231a90f6c019d0fb789120c86546b283e10ee218108befdc3de2a9a8c43bc272faf04e82aa680a33b1f77381254f262deb0a67cd00079a720dc7b8b4fc2586063aa5c6ff6359370ee2f784421d15c032af0f826eb6e0bda7374268ec3da7eb9c956a1955b192fd71cd986d07da1662645a0e0ed99c65c177e9718911abcc7ffdf97d052a6383de5697174a7044ed0b7f610c904ca09d1d25d3ee461a8c60fceba90efb126eb301bdd477e95a16e9d8c78c0f40b4e522756da69897f372515c97a54bd0391517031d48290059a92fb0aee6013db0b5f7bbda38c75d7f1b9afb0e1282468e80ad9e426dc518747779dddea405074d5a034e99b1d67331bfd8b1cbc8c73421ac540370c88b6874cc53edfae4069232aed7edcd78f0a2a41a24c8883308543c8dbd40256f4a681dc347a7987704da503c41ced5c78961ce73aba64a10d30f91de36c3df9b619d7ac76e4067bf07d328013ced571d1e1914bdb846681fac2d962c3e5a071d17a082021b9f875c00e11cd7debc3c6b92c4ae6b2153b16bf201d1645b034974c0e268531210204e0d6d1fd7a5e74ec4f2285607b240412e33fed5ea473036082bb1ac5c3b680880001f51f36aaf45e4a8022605723efcc2d14a9820a2182bad6b1b08cd37141e87019ddcc87e050f03d58115b6f2ab0f44ae35b17015d8d817a4e7691c070596ed0af3bde5c2ad0fa5c7eca31b4b0e5e8d70a86ede6f3f24315cb9f5e9882d733b0595aa2fd00d678c85ecede8dae37fc12608905b0aeb09e39f7d0500eb33b3260eadcbce68c71888634e314f72b80349e0f7c6aeadc090fbf2a8ddcd54802db103602a6f664e7c17d8291b94951ddb9c16215389eda72d2eaaa673fb0945cda4093e42b3b04b15647d015bc819aad014330ddfb548701f570c2eef0039b2e9d9090d782d1a14cc6f60b28ed6209716e53c8fbfebd5a5013b736d07ae8e0592080a88216399eb2789cdcebea09e9b1cdde544d8975ac9e1a45caea49f3476d95a04eec174fb3fae986eb26635c96f5da0fe30ee532aa20bccb13e2f91e9963bcd083c66260bc35d9b175e3fed93633bb035322d7994f56a48bac09787ee591fe00d5eb8aa9a79a7874fef015643e3ef14880b692e59e291a38d262d0c5e98cbb4058c7b7dc163baeb48478d7ce505e3427f930d368394947a28d9c2db1a719c91035f3d2e500c1d3bbe7d7aab14d6ef649f5ea7f650c979b54bcc5a1f9540e0e20bc455d26e2fc9738e3cee324145037ac4e3afd43646bff7c6798886f347ed14002bd4674e2ca92c8b1bb633af0d213f714190a70f1b840854d2aa8cf78522d100e3c342ce432d5ac933973424e351f278f6841e9ef73f3fab35ea74d7fe0e900254421e39ad99d6843b7efb25363fb0aa479823016855f61433fb357180a8c30f9f3613af76add585712236fd73a1bdf566d07441491a918dd677b90cb1e35f086eefd9ed7e891e121b3e16c04081617b9e5351092ee0ee95bde87a08d9c5b00ea9f80fbeb0ab7fdcddafc54d02389cb60f9aaf514074e24efc2fa8bd50a7390f7d3636961071459783cb68d8d5f6834dbd52edef553f47b574a0a3ac7e08600126b0b216d6e7e9435a3ec0aff783b62f1ce9b77a34623e7b6f40723b7b6f350b84944b10593e915df5217aff21fbd9659f378de2220b7d4d94808aa6eaf3050c17b4d666d31b431aa8862df941ebdc3f0bf47da28981a209fed5cd2b1b781007d0ec9256374ce1dd740323f8226c506d70a4df2c514f010dc0a022769a6c3e03aeed750489eedaba7b4a5203bd2b96db0d401f68aba69338120cb1bf700f0807686dc0e23f977d411732a4382f34605331901b471eae79acfe46c984da35150aca4a8a351a57b8ca774745861bb84fca7d59eefa1ea42525cb4242b2589b110272c080e560be844f98b2c0c3dc8feca81e2da75b558c9bdf280761e33d6dd108e91ebb78e6ec1ed07d0012b68f292ea6b1ab1b7a79cf8bee1553c2b161fe400e6a87e2ccb8ddfa43a69cfc4422409a5ee67060bffa333fdfe441f7f637ecc0074cf64c5049983cf5c469ec06129183c2e92830e6f487bb86e2f871a24dee73069b5be4f9f60fa794acce34d8a0cba0606c34ed2cd356c537a44c5cc653e1fe0f42ada8a25642c5c1a346dd72ace360dda6a8196d120d011ad52cb2edae8938026765a370d4b2d42826445b385d3f0fcfdab606da6e2f67bc8ab1394ea21aba0c8a02804ee79e48a599a152a7bd6152df0e7cd34669eae3ed18146d0efde08b06f93995f6311eb594aad9e0fc19aeba7985112ce09123b411c8f6849f40d3c00113a4003c786d731a70a68ccbbff6da9cc03f3395f7912ad1b80f99cdb79feb0dbeb8dbae47fac2d675d4aaf561174c28cfc3d994d8d6c1bb0f810cf54b2a57046de8ca5b19972083a88c4f22fe0192814f08705f327a53a8ed4eac4af798a00417e7a89d38d76331e777ef679023fc0cf704f0f5186f2c0fc705492174a9cd0ebb7770d42718a6a8810b520f64ce015458a5bcf90fae22dc593b000a77c47906fbe126dee0215a9efe76fb6688ca6fff3a06d996d1a2e1733a17cbf03a9086063d61f76975b8f18a06491756654b766d836e65cb5a809d4a9556b1a905db730409b1f100a82246b1dd03414f65f13114f6a0140e114c85adf7c821f99601830757befc9632d76f1b840e3353513028543cd6a5cf901c0cb84c86d3f7b2ffac0f419e976d9c28c238a8117c997a211da5227cde6ba400d079a6d8d0737a51e4088444571e7f8c010a2159c1cc9a08b5cae0e8bec15e84ee090b28f413d028a70f06a748e079b43c878157dfa0405e4dc4e4a095233eb6c33cdad9989516e31505b579140ebedaa12fa9a4b5985ed208b8a039b4d8b4e0c3da634974c83fdfc6009836b7fe392632fec8c34ee50c2431b0689e3c10d4e16b15b3154ffeb91cef004750c00b68d36059451b6bc8ad2d2bba7749d6dee8ef03389a801d5d38ab820546cb535ab3105411d9ef234fcc453bb358e6974bb12d277a2b615a9acd93f3093590af9614b2733b3056d9c23117f7f02fb599f3037eda8e7b87a7a4ed31f60a331cb99efff523d38de704d93301cbe481259049d110305415120e9784d7ac0d1bfa0f2e538de39c202fd4e5169635b8859a7a9ccbe9466e7a8552ab71c735024cea91a03103a4b4fc6d34582daf8cf5cb166367a55788621f5e0f3c56625e0fb84536766e1922087ac67732bc8e714a3e459dc6446e0c15c5f2a11b0bcfd60fa129e8ef7cd8e1348587dde2d5bb4d2e0f64acb50b3e72e13adb538dc32bc80b313d480a39b60ef47bc67bad8d28544de3ff30d602716d56a02f679ed6fa9805e03e414b76b0cdad86d8f849b9b5f0fe6915daba2ea45bf0c106ba1cecc8af0a44e8ba16bbb484315444dd9838dbcdeb8260543093445dedb61da9a688fd7904ebfbc2b07b4cd1635d45215baa4e0c956d455ea9f616c6791f5b5025695e1c0a66f6c9b56cc000401dc45182785fb3a3ffc88006ea8279588830f9d8decafb049164c8990f8cce2fab60986ed7eb4a9266555dc5ea8ef13d9d5a64d14e548b0b4da4f4d9c4dd70381c8c4973fc949a2880e39d1c4307c4972979bff454b0e606eb4d19a7c3fd95cf819978c876fe31dd79df6995c44c3876942ccbf66ebfab0dcdf45b91d8e9f865791c78cf6a5e55094ababf270da98b3f5da0711ed51f9609a1baac888abca603a968f25396fbca7cb96c71785af83404996bff591d68300b3be2e208ee11462594618e954f6aacda1f728726ac1cfd3f6ba0605b05b47f0c76e4c81ce07e37f868652bbebd0cdac10afe11f1fc2ba3a5c8ee0eb7056309012d7a382a53ef56d2029f87a0301a0cdf63a52ab55d801adda86f40ce8f53e206efe20c28a44154f45ff73396a7b0bbf2b24a9d87e9485334438c4ab839fbe10ae91b01900d70e79f33b8f74d5b0001cac9d458cd3b1478a811add1e6162ad101bddda9b65ff3eb5f429ff3c9d94774406018d8cb55b7cdb2922f376911d75d041db4eb0f621c2ea1a3c4db60b4e5e494446ab6d6d734ce01b8164db0ac0d48035db8f55c0c9364281933a7d7ef124f5d8ce5a986fc1f71afe49fb204c6d0f90179d270926f6d256926837cf7b9caca8c275d95339c71770fa89f792724362705343b12b446aaa57b637d3f1f350bb8f6ab57ba224706e4175c0885ccb80e4001de9601095d8bd90880df9cd920cfe1017067a12165d07bb75c288c36c90c2501a9f0775b28258ff2d044b1593e5126af4f17bc7d3de61474823c0b6a50bdeb08a5f131d3458d3e360519a4f801df9f354200b2ddfad5d7c56b2b67734416300c291bdfd9cda2bddacf8c10c05c5f92b5188e10e8ad7746250901ae3bac29710bd2aa1c6f63cc6a64c9910ab0a59fad9bea10ffcfd7115cbbaaa27173cc4a0705ce13d5e829e46a32d8ff486311f38162cd8fb9ba6107d5a0ad278619c9d1fd0f915377a76fe7ecb64736d0a3b74618c811f11e40d86c01dfa44c1aee104ec50a7246f3ef8baa9f0b302109acb0541ed166c6d1773512ca7e30b8595485a3fe0fd5ca1bedde851c795bee9cd0a96b821ce146fc5f6d12fbe3a4ad2f3d2378ce0a205f51df1ba7fa7d340c6631ee0644da5a25758864ada4bea3285a797f76990c89bd8bfa103453ea8bb9f91c9b902f96228ae3cbf88bd4d8dca32ab7b13fc40d1b65f307b5d69f99280b09723024808771975e4de247d01499c7ea483c34fe01809dd291b83aee77be845a1fd0d1d40f69d8b75767026da4336eeaf399e182022b6b94b673acf3cadd4ff8bf903aaee944653020a35a6ca3acd11b02bcee080a2be75434548653687b83ac0dd8c6c5b5bd3f8bf5833b08b4ccd94a834f9af60e7272f111c8c18fba0b38d38c7d931160431692527e152d31b4fcf4255f58fc0bc3726b6f3f3d62fc0f5b711896d985cdca79e3b344bfa05336fb47c3675de40efc6a1dc18c8edd8a196490137c47e86fce614c8e908a37f7ae291e445b72f30295005965a7bf5bd7e6a944fdc2a890323602fb6ba4e2673057c48d240c72f905eefd83339336b71fa731681d756cdb4d35be1bc9e0daf6f9c646b1ef951edf03762b10b8555cdb7c1073f1973af224783508c57e8391f417eeadfc63ac7c740581f7662fe33b6e9ec0e13d68db8170128da29d2a1f124de37450e25704efb3066914c5c27af14a9a6b43b7db68d84e3111c85ffc7831b502a94bb82b1e2ffa0c38f2e4a2e24588e8ea651dc7c7f2156d46113c7e30d926217901b824412db10a6a0884c80d56456533d87a04abe0d550255ef0b0297f2b91842df320821c6d0e1a04ebc61da3389cecffb5351dadfd96b980775148c94255516dabf87dd16100749d948c647db1c15255fa64170f7d6257a49223d09dfdf6dc9a706723e6c70750dbfcadfbfb94c797ba0e8284020bd1640aded4c2a0556665dfe616fe02300ddfb28cbda23edeec9db06d0c5cfe01dd535cf46ceaed09ada9fa81a9bb3f6007f3d0c59885385e8ab580cd3c7a05e97bfc920084d200016a13656207f1449f0d35e0cdf66c01f95654dfd84402fd85a52f2214bdabd541fcf0cdfc02853afd08cee41fd06e1e950eb32d266715ffb9d72807418aeca8d7b4b1615a38c880580f45079b1e1fc22d1c3a685a1ae2bfc143a59373b242f853bab0ce94d72547e2080b1e5ed6c867460d94e5dccfcdeaebc7493771224f7d88a556f9893b3ed84d0b324ed684fd8fc73644801c21e258aa9cbded492a9d3a70fc31118ca82f712b0d3d17a43e1ec2b09aac2435bc20d7b1a62886aa2524537e0b13a6448a63312a03e34c462f630434fa36937a7fd850f333f799dc8747acb5e180f66a8e9a71130a6f35c0e5512c008cc00b8cf461047ef849e1905b205ee762c32d640a327a890097fc967de9460c46d7e43eb9b7cd80423078bd0ba7f9d35b9331abb7f07bea038f34cb10a7eafcd3db1a91c7f983df9b4148ec15c1dba573b92b185dc698a2036575332911b4ad1045e383db57fd5ffca250a124c93df2ec9ff6ccdbb3b69d0625754cb7b56186d9055a2ee473a933c0b2d444ab06a91ae87b3920151f4b9c0cad1064d8655b2e1ff9327b00d681e3dcdb3d12fe0cd7a09a308769e9ea719c0e5d38e6c35d7fddc1d8e8358c46472c18120115e81d20803477928334452046020a485c632d4ff22c6393be19761d4adf4277c13ded8ce6a53000db439e7437092bdeed3176bbf385535f6864e14bec524a9628671320cc73b82970c3538c95071428ea6fe484b77d74b56c815ed8a56a3441d520a4bc1aee40e2e92cf284b30c917eb21695f41a7834419b04ba4fc83c55dc1b09f3e76ef98847b970e2b33706d5a0654276c181fca7e199b2d01f1b5b08d8c8af5911b35a705c3e3f1b6b440804e75f6fb2c7aaa7bc0e63efc23d3adec0ea70397e009d1a7c4f84ea22fa2b02049d127b3e11f8382d2a3125f6701b58e0401d55087f8634be98c90ab44e3300987c40401b493e831a7535b43cb6b4296a3ce8a6e7db9ac5efcd32035b0f080252d59cadf26cee13e2b0be221291c181938d8d772b04fb8532bf37b1a5b7870fb7a5a04a6d1c99d9f9725e0922f7659b027c190c93d5ac6bc8c2e359cd0bb803e1dbdff6a294841ede0d64320748143a56bd4eb789968edd36ed9a8f1a04e70dcb569a257912ac5fe85133a300ee4643283cd22abebb2ea49aa0fac7a8d58f0856446922cc49204d7dd86d8c8c85637e81974ef210abe55e975b0d97383c730d4aafc0bab4165f1ad8379fcf67d9eda7df94ef59bcfaa5e6725f856d942e180071dd443b3527675417ece528e24b0641f4395bb2779806be90e61db951bd4c09b6993db193437cf6ad19408c6ce7e9200877595b952e2c54f25d118497e2ae07837c35959f9483ed460536038e18cc39838b20ca412024b6ca0ba0adcf926406ed7c0b0a43e015ab75cbef3b37ba223111121c6e55653ebd1a9cc05d846d2e0852e7e2aded6e54a513f2846e1f1e0861ac363f3fe480a6ebc3afbfa5ca46b10cdaf21d0e49f397d2f9de134f632ecd152d8cc1cc61723ec230090ff03e03aa08af7661833c54241b1bed82817fc06b5f1882c548dfbdd1104a0fab08035b4d0d781fd5c47f09fedd7a6c03e1c2e71daa7fb76b353ee3fdc934aebeced0e7c20258cd066c7af944aada12abcb384fbfc9561e8e3450e406661268fcc68026be0f7256434d338fc870757b588179e1e50cf2d30df11bf2f3fd7f88748acdd29701250b156d65bb0436467c6c272015c7f3dfc37b70d782a94635957c330b8c3b08bde2b756c9daa1eac0d3fa7af68138f71f5e9f34170671ebe5205d9092843607eae90994e7ea59a04b508b354feb0eeaa2ff5de934bea3c985ed2659b912f807416c09b31ecef65a5c34ae86bab56a402a7c5175d64b5c3500380ee2d9ebff075d2d52e2c972c972b227a618a809e248778fb236fcc7dc74776f07ac2d80040ff82047e9aca845b9564820353635117e67929be41b583e3122684c92cbc9b80b382b409b730a1e3e86bb6dce1ea1e6cb96a3b0381f5574cdbd56b01eaf01670816ccce77a9ea198ae461aad3a4c68be4a807f06c23c9847664f4127d3f076c06b974406c4a6dda7baf756dd394549d71d7359c9fdb64b147c91c39c77c97e403c375b2066cf766618a88a8d379b09a4c9e9824d43f67e0f04411306b2fe03c064533d9a0c7b64a472cb1a049d3ea5283e42b190bae5a68e4b9ed6a3758439100b9fcb5dfdbd990e1215b497e4ff90e44eb3d9255135fbe01663df4d92c2b5e00e28c8e2f638012216e14039d8e03c3252561ffed6f3ea9f232120ecc2b47860add82f0786b55d682ae36d1e029fdd018c421f5775f3fe548c2e126e7d4403905e983b96717a74869d4a2b3fe28803b5bd8efd85dc5fe324d76e2d02da7b686011519d7224e4c6475517e4b29d7fd2e52c99c21f40bba1d7df117349671605a02203cd7c13589c501a25fa2c326f54ce4c1ad7d00364669ef79a69728b09b6f02c183e16f288ea56f6e7b1792797a52d4b52b23bd7cff8b88216a7d2d2f21c10d72465cd7f4bb917639a10a0ecd4f09c3fddaa71d218c0153b790bfa2681ca40027120e625c8c9eb9ee8072b96a5c7aa2eb9c40fc3ae374b9e5a3137e78f0040449cc8e4acadb15751ff3dac040fd17a806999beda71c46e47516afb521b95a0607fd8b274dba6f8e7c94886667cd02db4ee8f890b31e50f21b28c40ae9d38703d4584e7da816868b0daac9261d615829756856a43db4608542887887efa3100f23b3d5b73187678ec8e144b86997ab17a5b17c9dbe29f7e645ed270b3804c00bbfea9d17f41ba1a0be533f24b2fb27813ef0f7bc6f7951e33af0c0400bee75033e6833f5c2f48d908d1ddca6bde5f57ce1e49e0a6cc46424ba564796ad4a340656e78bf464fecabc5dfffd397a1b818a14a9a46d1da04319f492b1c0f24c1900baf0d72712a9b3da91103417386b2cd2fe44c77694fdf8b9a5f200218906f40843daa1dc33254c25871d848574c46b36989d2b1cb0801201eabb4c79f6647a0af5c7b1dfaa7dbcba754a72a6326bf9de538c324aaa9800970a0d87b5885f240a8637a2f0e407f9c9345e70252c6c23a6a2c9419c5e08e78b9d6e1b591594710d2dab4d10da5b80d3f801b64daeb68e35a0122c9deafb302f088a353b3f6ccf02e333d73502998377cb4ca609acf445b132e73f55bc39eb79a4648a9e56c76f0b766416fa74d31f0053110aa80f8a99d281335ed963bd82ab0da936a52cd6160c3c50ec3e39065f6a72be12e60ef0030124516eb68f2ff210c771cd1a9c2d6a06757102e0ec6708e32531cea984d06daaaae794c517b9d5a914d0044ac62bfe0abaf686b95823c543b3819ddcb1aff02ff89472ad0625b316467d69e23639d20037d0c51fcebc56d0eeb6f8c2e0e414e80cdb515b0f1f2d8707e7d3404e67dc022f3eb20fda6486b16a07231bab4ace8c31c3c23cf2b85c71dc229251a03b440ed7530eb3e7527865832da6eb93b8c8522aa9ce295645cf309b950a50a6fb3f0076efa3276b97aeadc91595d4669df72bf061e7f8458ca151cdf2dc662f7fb80b028ae0ef2d4d100aebe2e0286ca15c7dc39e8d6213073652d8a7a6afeaf3c80c060ac0d2bb2a52cd727b324e34843ccd879c7f6ab1e465374cd19407bc29c006d99b70676b02a155a182ecef209615af2b89cf5bd8ff760a1885f1d69baebf0315dc915c37e9b539642d9d0ee8a507e59b3df0c56b93060ac8106d81d0724104 true -check_ring_signature b14d81b6094414a9ad43bb61a9e8bb75ca9d2fa088417499ff3b4ed52c088551 5d4589e61253cfd7d6a32eb8ed35e937e91fdd98d4b24b67962a5e957816ebac 1 952fdfc58c8c9099762ff13932cc5e4de07194147c1278bcdebacbd833ce3978 c8ae00eaf1526efc1af9c73f0256af4135273935b28e0ed7183f406cf762e0005cb8e55a175106482fa88f982674f4c22c233de7361485fbc08b4d9d9cd4bf01 false -check_ring_signature 297d731a59fefd45fd399dd0b1e2ad409f01ad67eb8128c541971739e55e4942 f8a99ce84781280be3658e8c72e80ccc1efbaac596709b60e5ed715434353280 53 36274d7275fd90886f4716a7b08fd363b533aedd457fdeb1e8ea3c814becea5b cd28aeb23c28ad7a53a5dc58e47e752a58cfc49ba12b47cca3413b3cb04f7898 55259b53e809e46e6064f555eec026621834f2fa287043d0e4539e7f7531631d 528acd5b22f027003a273b3e6bca73a4e590de1b22a27a35bc5b8d8f73f29ab6 68ccf124607b9f476e07abe64781d16d85fb23574b0415c150996ade07f4ef46 f3dbaaf0f1463e94831caa984a811f4986d199737c2916b8f264e1ce1f444e20 35bcc89dd64ccab57d70fd46100395371f8a53598fc1b138a9c11ef2eb46291c e156d11f60dc2de4f48ce6e4149a79e267103e42eda341420832761ee3c2067f 3178b2123fb82d5af54c45aa6540f6767b187b7fc84aa7f1378f5d792b63e8a4 8b4d1e544aa3b960bab3c275e4b78c44394e2ba1c7da4f6f09ac4235b9887b4d baf042e4efbc3d970596591adca61e075284a412464ee9f0a6c700157e029b57 8062693988da149d1592960893aa1584e83763defd5510c7899a91c1a358300d ae191f4df4c2802fb7fa57bb826b0b720a3ea12c08629285ac14f34816d32e81 c9763ac2cfe5f955fdb936b4b663a0a3e55b3be32dba10e2698f9e2732454a56 2bc8b852d3f942f490d252ae86f5c1b0da906c35e1fe6328bf15c5742e716088 2fe0045f0b21977ef84fa4b2195f59b98078a3f64754aef60cc317235515e6ae 7050e33786c114329c718aa338499c90451ae9af1a1430cc809b1b515635f8c8 d64004d4c2553acd490c38d5a6835870cb5bd353738dbba5e49d897aa54e7232 f5fffb4f6a4ad76a02c1a0242f6e3a57202703cbc3989b58019c898fdf8b08da 3cb1eb44f9b1a59f664b47aa90dcecc71580a61f6e09861843575a0898cff01f 6298b8cc64d15ca17c418adbd4f01881b28b9cac5a12642780af764584d73ed6 cf414b7814ca1b7a7ef55e9d0b1341ef3736331e731682a4fde014632c23f030 b0ba38a7f69446f11ab19923fc6212b2e39b0ec3ed7645c74d988b73332a3843 66d7ecf0bba045b73ef394bb7d2b745c852a2fbf1822cbf17c6ac09f571f8715 e5517dfac9bb68535bef19f6a9853574d44e7013e0b1b7b6ef34bbbe2107a617 1681268988518210f2c8f9a202e045e32d720e958b90eb03e374e92463e184ab f8d63a6e0cce442b6d92eafe2c9a5644b4c8160a674a3fe2acbcd24a5a6a900a 4876f91d5605649c1957a15556c89700411ed617dee1f5b0fedb59b60c9e9320 337cee8759d753bafee2ca4a6f21838eea5efc1643c90e3b260c253683b50ecc 07da21cb19e1ab8084978afcbdca44fc0acafa5da690726d5bf12d69c2144efd 96221610a96be84ef033ad8996e427adfa16ace6d65577389204a9f59c3fea57 0f16bed7039b45632fc432745ab7a273bcef4bb34bb47ba4e91a380025c91c80 3b2423c65fb114b03ba5dd69ea00d3fc5ae3a33ec71a232cbd6a62e454ea55a0 e060d1fc0a31a8cbfb657bdcbf741589fc6902aa5691661be468c3b22f43ee39 962732f7ec5f5eb51ecaa78e4027f3388c586ad8493d35019daebe881576243e 9c977d6fa3033cf2f3ff299f9d072757ab505c2f65ab5c8f0b39f6fa69250bc3 310403471d1857b7092b9f07f5563d758d935e82ebf1a68b350fc0538d4e40a5 c8795d609cbbef4177a8e65b6352b793183dca30b789e5a93b151585a321df99 7cc49a9a936775b0324af903bcd274c02efb5d54d70b7959d03bbada1615ad82 f2646f7a4404ebea70019fc52a30e7f834ff20cdd855e2095760aa4f3aadca9f 751e2ee769db3ffb4a59b995f0e6d26a87563708df17716a0c634162dfb689f4 2f7b513459108bdb2bd223e74097010e29442165d1e80689b249287a3e3e3447 87ce730683ddf6e9150683219d96d17345aaf7c03c0c1aaaed9b981f78053b99 3a762656613ba61a9a9ab2b27a68fac104e6668671ffe870effb71c2d6c4cf6b 4121ad33cfd14107504f4808d6509c90dcb3a84de340d36ac5703845e89c48d1 b47f210d0c5b8a6e0be660af1612027bc38aaac095619d489a802b3788c71bfd 36db42c455da5d405c28df7fa4b16921b74a4bf016b6cc2653300ba6dda9878d f11b91c7eb648254b28298803260dad11785144dca7ce209423aa3fe8c5bcd53 15e4d78ea688f6b9e394a3e39568cff0a83977707ce614e8f1a7d949e1426853 7c808c64921ecf0e36031e01682b909976cc8783bd1b733f24d365c081f4ca17 1b71491aa6cbb8f1db0248fc87eafb616fe2aa57ef008cc74e9fc7b1afdf3550 7289521297e299ef1a1f1bb63a89e0e1e1c573f1063bc3e8dea4a71e81775e5e 23f231e314d32faee1e15257360a5f790af608680c6f4a886d000f0dc139fa57 a591b9fcdf48d3999691dd6a5af500348945656c7fc8244d8f33e623757c6c0d5538b314a9b1251ea78d5e453f0b61fe52bb1f1fbbd53c949f5b59c902284305c5612bfd5313f1b1518db2649b96d34ee703996901a90993da714f0c1f323f02c91474c3e68b15bcc4b3a68afe1097c3889c24b4c0de9731674cb385ab41e700e337cca883164b1860bb1b62fce651d61da94fdff72ae3a7e17fbd1a6c3b4c0219e405e443545de15c2d848f4e6ad226e9b7fe595321feaca7091ee88d3fac0eb360ce88bf91827a19a3f0d2a4fb07260c3a437720ab4e7a2ba2c4fae22da30ad4202a9e3c67f6bae16050bab68c0bbf527918474dc9a740fc1bcde8cf8e01022b892b9b5b1fe374c662d7ce08c08f21d2324d55a921f777ebcf761a24861a06c24bf8c340c6dbbf9e90c199499df2cb98fb141069e6b350ba4f0065367a1300048011797ac47505040a4fc4fa5742b62da119d6c066f4a12ce7912e8098ec0bbd1d6eacbc7fffb70460ad4fc2482427a95b0648c373ab80e05416287e14e707483841fc52e58b2ab5e10e620bf254a04f1d79f833ee46d7bb95469b7a7d7a45fa2933bbd5d4eb8dd9636741a19aa71521a949a924deaddf37715eea8970bb08bef9395714c65723d6d78839c90b023795a03be9e1a627de8cac786f155d7f0fa86a492dc42bd3b814d79861f77bc755a2dcf7d56d0128ddf6cf0d5640a2af00e8ef2ea19e532d6792ddecfa082a08ebc2c6825357ecf1f7ae8361ae1f86e00b16b4a4335892ffcfe16759e0327d772ad0939c142ca6eaaad0e05ce4868c08008492ecadeaf24f1d192f9ae338b6384ee9f111286b3e1ad6afc4d88c6c0f9400f1d2ba384faffce0c3acd1ee1e731d782ebe18e287ec8afa37ad4c00dbf7820163c4e34fb025a655878229dfb37804fc92fe8b0f3f555e2213eb3ed9db1c0e05fe016f3f4a31a660429bc11b0e810a51f30df0aa07e32df780c402b2e8a03e0d835ba75a9e7ba5a62c3f1e083225d02458c2f97f35ca89754b2fe4143f0cfe060aa5fbd84d55b7746f42e1e38ba28137005fff5909c2388a7f7e0caf4cb84d0306a8bc11960b5dfc34c88c8bae8df679a16ab44736a08ab70a8412474b97620eddd0986dce0e522ba32dac1cfc38692ae986a8b8001f13a31ff22acb84e0da011a802e2f19e39529ae794a66afae6d796435e4088bd1744b3844804fd3a56c007688e1462a1271f2e78f9a7d18f4f536f37219bc23c43e39cc0f707e4a226a0ca433687eebe0b2f75a54bf23e894ca986cdae4d9a6eb85601fbd6eb96b74da041998efa06a1206eb368627164997844d13b7eff729917e03bd50a588fc0cf902e003fd000f5dee8427d7b5b726b400943166381d2d95e4bcda80dd7ea38d310784b8ff570789f9006d69b5815f38b526d7deee6f692069c66b129fcdf7fea00905a88702032db31fd74c1f5457ee1306abff68e9fc29f2f0345524df68088807858a9e6157b7eec351be3f33e8feb322edc22a651963a02eef1da755b57bd10b340f5a9c9cc60cf4af189962b283009f5dac620dc27e885e7f63da4c0fa706039b4bb24b49b8037b5a956bfd5142476ddae547f6cd9fa44fe49ddce8f06c230f5524e21d184c54deaf9e2832b39748e4de00d8e20948e25bdd6e745a87202009b5dd16223cf6a931d96941e63c71d17f25e681b78a50f57e8ac276e343c43c047c16eb2e8ca25ccbe7a26dcbda211ca0d5c4c66a52659bf58c2cfd611d13f40bdb2fa93307789703c8341fe4fb4fe4c07ede876f7d8066503b31f831200d6608f9bb73d89c8ab01f6296223948560b8dd8cac78b8ff7d4377616f122fde1f502b29598881ec1901739853dbd0fac330f000733e7833fe23ad206a327a005fc042ab00d59a20a6b5c72a685b86ce91ed73a5dcd039d75b2cc41304ac538e9080793c644114f658c249b1166d65aaa6856bacdcae12b280768f2b6772661f92c0ee3b5fcd701d5136a85fd7905955214c7131bdd50e9e32ad2b4b9c4a850e4e70f7ca9ee2b50483609ec24d975691dc56fa29a90d019c00d654aa9d07c717dfa0d8276bedf807de4e2d48c98c80797083da1f1f9e11c0093c8e08fe7a1f7d9d906f2c9d0556c20a2e972067d0fc9373ccb42711c05647fc797d7709bbb5da95b0578ded814eaadd3af4c62ce1eb484df6df7a43596ec29ebc97ca038afb3e24a0a6619083ffaef69a730ec840b71dcbc1b0466f5f101e070e03375c2b258eca20c32b0b8ee9df3ea982dc99d3088312d3992fc55a5cec822e77fe894e99deddd032cc38fa8f42bf953cc6590e5ed05f1f29b180670322067114044cf4af2b8a50e5d67b9ca897bdc401017224917b1909339a9c948517da30ecfe390d1cc7d910dd472f9deefd8c101e02154e0044622de5707fe134239295dac1cefebc8ef9600d6f14fbce1dbb3234a88c144eefdac383ee687c2f08e41e5df0aef84d4c3970f74081fab1f9076b086f82d5ff5de98c2fe9ff0e00828c55e7507104dab3c220eabda11a0acda53d2c99956e723e0f196956ffb5608791535c4fea0e433c66b05470959aa0920eb8b8c9126f584788ee647f80918451c861c3128e1c010e30307ef21bb6246cf58bb9de4f0ad964121649f2d1b030676972fd631c2c8c57a3c0942e0a79e4dce56abd4fbbda867c140151cdf41f20403ab6c75da1849bba7f90af7075f8bb273579cfe9dd72bc384d11b026f13d4e7bef8f04540cf6e6ebc47075d9962b2d483b051e2e94275f0588a191791cdd5a24bef1507803d02bffa510b7d15b2cf428194fc816032cd2fd683495dab58834ae4ff78d7e16a800253810d6903699ee0f3c3c6b74a4395675108a98fca9203c8358dc6f575941a5bce7609d0e260f9be21cb8ae44ddf3952778cc85a07f7dc6a38c65ad787da16c2ca1d01157029dff5b19117df883fc6251c4ac57d41b16883d2cb2d0922b3865aef48016f5cb24b30eb2e0f1cbc35dab47fb7ff1c5ff16653216980d77d04a7ae6875024470ff4793f079d24a7e70e7ef78f3e6db387cc7b1695af03c54bf13b64acc0791af66e8d3d4d81de8a101b89dfc1bddca0c4503307bb75f90acbc030055590cfc18a7dfd1f3e6c03303344824fa979da672ae2549f6616507c121ccfd83980467c54010add5818e01e63449eae902ae6b3ffa4f2bbfa7a889413cfacb0c1b0d124d09aa24f256aa1563de97276baf3b2a9fff7af5260ff9c01dbd67dbe61407c4c3756fe61acfb28ea85f49c397996bb87bb228cec8f2f69a5ee758bfd2fa0a27ac09d211a7ad6a868bf0336ecce88bd2fded64b8ff35595c1b7c9ff78b480cc8d32887e23251314da0b1722714f052f8b092fee8f9feb2ea77068e06435a0f0e52d777cc3cee9606e2ab42d1321dd2530dc817a8afc3df476f76ed7773b00746cae29e59ec8cd0b84ef45e1fd1563f21bc7f26e8a8be64b6a7ca39027e7a08e9bd5fbc5637ffdd37972e87af50a894d6a4276db5bfcbe3f38a31b80c7e5a0e45149498f1d5e0df1664ff839bb67fd7b664db6980e5156babcba42a22b4d009acc08f95f512aeeb6e86b4d4a5b2502f97be2574694b8f95d45b08809dcd46097182caae5b5d9d7a36bc846567b31d704836f0de27c7e99a7e5a79488b7723014a71e089678f26a493716d187cee5830edb28f5df6d001c69cba26eb5d5b1b08a93b04c40e539660329c3b36bff1351f1dd6b081e5478471d80011f57c8fef00cd07edc64785ac8d3f1eb52d29b6e878af2d1103a8b6f7e76cdbd4af1b868102e90968ede65a6d297d1f71a1aa062b564a0230a500e0486d6a6329b96124fe0f13d6d9c6f37adb3b6b96d419aebf996d4021f6204ce3baeb234aeb7058efb908ae332019c29613c7866a4b9066b10b0ef0b6d919c3bee0199137a7ee63b21f02c4ffcd817255c92f863f7d1e1c7c87eb093fe1bb66b891c0625ccd259f4b1c088088a1a492a76bf8f2023ecd65a774c52488472896595da2b3745b3593f9e80285f9f1b479e00d4c2ea4de19004164679cb51683524198a4f6569a1726b3b10da3bc717521836ad73e2f8df4a71c668312c8703c7cfd2e5b557fd51cb8abd20783ade7aecb2e7121839a0450b2337448393bb5b5c1bbfde5cc2a67c027c8c103affe56aa8f923308222ed74a61faae6c332cee753277055a7d599849aced00932ccf598a1f87c03204bba2409e48c399edc206a45d5ab06739ef202d02836c070a2510eb57ca553165749ff015cd21f874f058c1513624657ecaf840cee4ab07f0c911c5e95cfb6015d053ef172fdb3a97c0c35931c3e8e13248190f21b7350f3bc1540659ae4c10d07b4bf7937436f5189093ea4a301d77337ece925c14ee08fdde310c0db7fc6d59b1480ba23cae676b703bcb06236a74b2fe7d725f96ee07e55abc77761663077488a561e53480dc54fc4fae193ca649a19646d437134a0849da9628a6413aa16e8b904a5304d32d22c8059363cf39d3d6fafb53f50c0904fd9ee9d74408b9a263fe17662c8d3a22e261561e8095ea509e516583e453cb4e6086fc79616e324a16ae3795a90fe1c95be24e512533397492a125931753e7064fc4b310403b8f7cd9f3bef488754ff334f0f5489647f642d2efced346be360a271fbe8f0b5fec82bc3dc97989de1e4433f72f1afc1e0e1d5f4954a344569f0718fbfe53c166645489f41ccb6ed2c7d6bb51e82f875afa8cb17503855fb15debdcc8972c11333fc574748d6f61963bce31244ffcc5d682538d5346a0fd2e1d04 false -check_ring_signature 1bb62d3b495dbc3cd90ef33683cb91822a67e425c03b9e8788061984a1b7024c ef94eed4c643543a0ef56f53cbe840ae2f972d9c0995915a77ed3d2e0dd76a83 4 82bec121e119942329ab5b964c76573a8b005cb42c872ff390f39e1e708b0949 63bafb05b0aaed5083c7b22a3108bb39d18ec71b28f3ec8cdeec037ca683946a a19b1997d05a0bbe00189ea00a62513b7484092636a90da51cbb78666c982148 f578de6b3f29eb1f2963bf13023957495eda47fb4b30ddcdb53ad373d194ed3c 7ebbd275f271ccc80eb94df133de9d5194007faf0c60d89319e465023e1ed90c065d8a560ef8cf87c12376cb171f71f0687514fb0b64e8613fb0ef417264df0373d6b73c4fea55aa01dc53238108ed7d8d211effc8a79c3ebfb42018ea0b380a56372fae4aaa5596e44eb5164b3d42aff306a8fe95965294f2c60e28e75a030a7ab9b39dceea8eeb774c553c27907374711acf18cf209c198c01b532020eeb09fb3a1f38065c0feacd1db5366c71cf560039c79ac475143b95b49bdcc2fc7609418a3077d7ef53792ca67d5bc2716e07f1248821f72a933e4540a251548310081ceb66286c1f876464eef64e5034a9e599fa587f9d89f080012702fc5e76a806 false -check_ring_signature b04b4d4b6c3917204674bac1b80ee84ddf272341de00a513059dbe497bf59952 553f7c3c5ab64565677f7dc0c243b6b69445ecff93969743237708e7f0b2beb5 1 387ad7cbd077f5cfb5235d44dd32a9f487e649c7d9fa4ceee114faf9dd2d034b 62f21f8fad004f842d7e270111f4623037e0471e699ad8911f66941e68de66c0169e1332ce2be9772e35de2261509d0eda65da2e011b111e74404b6f472c351f false -check_ring_signature b458df7d89fc857e918d070106f925ff730e0ce983b715dd2f0190c21f16c7b1 34689cbe6a2f1252959244093d1f6d90c25ede019a6040d5dfd50df80cc85bcf 15 61f1bb12ca5db412fe3d9051d4bfe0e5f2d741cd971db2ad9402c770557eb5b8 37f9a0cc77fa2cdd035eb01a7c2283c2f1e70653898086c75461061c294eb9d5 630c6c7c2e8a7bb028243e869b871641c5175466c9c3a299cc422dafb5231e93 75f4a7a2b665aa3b88c2c3596b4e88c0aa83c0a09cf8e2ca07f47de228ff9d09 b3f3cc7bf731cc6abd97c7f829034912682f027f8039209af7bb42c7268d0cb0 7b092484f189de0c8916f57014c53d97124abae346dedfd77e43a6ec7716d7c3 098a1571989d633dd40da8fc1d897ddca8035d8d02106a059eede40d1703df59 b874425ba8375f6bc89d4a0bdf65b4e74a916864ec909fc932faa6b92dd46139 f80ab332aa5e78df06b9f126c9e5bc80ca6924244582a3174741a16f40453d08 70b76f3f2bbfccab6092ddd9f542ed659deb0a2dbf00990523cb545bdb46e95a db0da1e874e71cf22171bf8b897cb3de56adcfd914d7d5b231aa6ac2da073a84 1789ca90e77d39fa1f07dc99bb77962942c5e1543cffa544faf288f16330c9ea 39f79ffa996a72c5c2cfa89dad5784befdea062e1a2a4e55b9c904fb67ec9a91 cdfa37694bb8beafe891c35b3cedf64e063b315519452bfce2c3df2aac765f0a a9becedc6b6f8386883f3413b87671076bb2375570558f72c38db4310493193f 8e0cff1ae76ffa5f0611dbb2a29fb1cfc01c96ca9566bdc38dd6de80729f01042dc6fa59424de5be15c58486d92850444dc6b121bd33ab307a112c4f0af8860ffba63a11062daa4e29d2e1d8ed029011f06f65659422df042f2cf3e44cb2fd0fe184bae0c013e2e4f0714817af07329371d31eac2f9a2ba71878c63861606b0b17dfe83ccbcdceca3bb1cad4e2422920c933f464be483f456022536216c801010e82832240db9ce0dac540af1636d4dfa145fc2db2ac2c373adb62fe6ce81e084d191181acb2a54bd9b0f6d0191fe9f0ec262b536bf1e25bdd34cb7f1f02f80be2d7122a0712c2836f72fa0c102571dbf27477ce67e60cacb19cda42f11dbb0043696cebb08838dee3022872932d0284f3064a94b7f58137835ce03a7d10400119d6f03d58b881c5b2e9b118a9c9447dca452c8a82fac0a8a5aa37814ea05e0d5f2966782e90bcc463a7a3f862a77dc9fd83b6663d9afbc0431d49605074db07be8893dcfa324610fd81c3d59911c2413847faa0456c35dfdff0db062baf71b43cd06f212563c5e9a5dd9e32d826a1be2bad6069380e3e7808ddd6a649168d0aa31b181200abec1ae42c8526c4492eb9abee7f511fdebe605991053a04888903271d1790b1dbbbe4cb48af8966f78e678def8600d7fbab1145c33a600a5a6e03c6f443888dae6b6aa08bd89698648aad1fb43e26d2eb0b78152c03ec6a517c0209f5b6e8e0a93e2339ce2512d69b6eaf5ae97ef9e434e877878a809f6bdf320a879640150ad5020ff310a6d86a68c3ccac0145a5d38207d152bf14d54ddde608caf9fab6ebdc20f700c65edfd26d3e94f7439a401e0b3fed4a890bf4d298390ef794941935258e32c320acc80a56fa02f3d0834e2246ff77f6fdc7304bfc2a09dedb144d370be6eda8c7dbb808345881f86fc978b3d7bfc762a7b9cba6659508e314fbe57c3a23f508bbe3bac62eceaa5a412d3f6a5ea018749b0d0ef53f4d06d892c71c09efb6ca7899960a2366fd60f430bcac9e6fecb3ff36401041e6bd035f4354a1458755e97af13e2a8ceba3f4d348c92f149b636216e819615bb09504606d2fd5a7a92b06cea7d304cecb54b5d7e97f5dca7bfe18ad1853c8d2c43603d03dd169f88f9a40c5059945bbbeec21585342d350a6d7f09d5f5f7eab9b040c074f8ea66e3a400d820d3f56ee1ff5ce40c2a507877ad6a218d9149fcc42da058d40f10d5a440ccd67e09bd51da203b12b634db61106de8cba916f4353e2070448eb00488c4c0a2a3ec0b660f762b15f929cbc037b5369c64ffb09ea0be59e0a584e63fbd9f4b1fc4a8d37b4c3d380934d07eecd7ad7907a6330251bb1bfd00c false -check_ring_signature 85d30353ee783d9d4edc60a4146e40f7639369520b3e763a1cc8fa83b13b0808 bf7f712e7638017b0111d48f529b09b46258a09293bb6c9401ffecfe0fa6e145 19 3684f0c767681a1cdce39986ebebf8549c1454ea8865fd4b0f4af839afd3e2b0 295ef62e37d00af2f18d71478bd0e08bd7a2e37b633c31fea444832f74874c55 2a63dede5b30700f539eef750cc019836fbfa65b9c6fb301e048aab3ba8e3472 45de0c043a2340e2ac3076eabb7512db98cee55f8778a958e1b2988415677ec9 ba8ecb332999377aa54a61f5dd9859634c0206e0db50ddea0f2412b010d0d5e6 68884ec65d203906716a577f7ba4952d54022dc2b20ff844cf1f7c88e83e6ef4 062e84df16e7db60e4299e1d58141e1935eaf9baa2b436b3dcc2b96947a7aab9 c5dc35603ec8ca2c3c91e3a8db97d5fa38c4edd631d250cde1656ec63596618f 20acda6adceae53bdddd3dc03138dea2e3731ff6ff05c27384bb65ac4b2b098f bc4c3a92b9020e9ddd8b2c8837ba89d180b811d8df73db7e14a1a8650d8098a3 9ea77a09fd59ca73e3c90c4a4d6499ea2740ca53b419dc6ac6476959fed2456b 73d02b0f490c277b375c3cd043ce2b5f4dea5e7f3556d77c0ddb239a225f8f24 a89c725cb007dfef226b2717eb02d34f0d048277a28b333acce28c154751a7d2 27a21becb5c887ec053c73a0707afcffcabb9a3530bb7566a7e7ae2cb5290bbe 4b76f3f974cd4b8f04c636a2e2e1c12fe09982c529b3932ab8480d7f7a0399a5 93e507e1e8f46b4889e7dd264f402ec1c1529911ae23e246af1b236c38a5ef90 75ec35c5fe59557b889d0b512e366e762d82cc795a528ed92933995f08858d05 a0cbacd6f793453f02742609808f3c85f47d54764764e2d969d49d1fe80c8ca4 514e01e6d9f943f40454468fb8cc146226c7c4db8e0b63264acb56bd377a60f7 1edb9e6e03fb83abfb73f9d64d120069f82306fd7fd7c507de630c498b259902a229f86d6f796cf3426c0eaa2b28c3a43dd642f441453456c89b169b2de7f90697debf18d48f3d49c0efa4eba1154ebec3d6fb69de91ea2f68daf4e26ec2a10ad322657571c7b911f1b3be645119a5c0d3539bf9d1256fd07c9733804a0d9a0064bafb1bb9179f5168ad0f113256566938d99819e04621f6a47260cecf872605dae8a9ed82a8a2c921d01799352d87adf14cdf62c51b07201237668ec853c30be26c9c800469874cf7b74140dc4ec3a37d7245cd830d00a800c1ad717873ad0bc6fd357d59dd681d2d1b2373e18408001dcf89290158229ad16a8b914a563f059ab22f33671eaddf263edbcfc97c0abe016cf1c9b176de9bf1ce153ebad43e27115a73aa9b67aee90609cb8378fbfe6357ed080d006f520871adb177fc26660b4e4d5480a354c460d44912d7dbf669e1b04af71aa5beb82a38ecd7a1f3217c007c37cb42cbf7154f3d685d5e018cf9d1c497036b58c06a92ab576d4002be7d046f1a59fa9e8b0d9de1e8a4e22277581d7139d03c937eda8e5d8248f72c16810a1f2c4f27f2962235923cd9a7fde75b8006a7ec789e5d5f0e7ae1a5fdec1233024ee128c361bc1af2d65f4662443f32dea27649e6667a5caacc31107d34b49d0e28a0e91f2f8070fad7c65ec0f7c7d8f27ddc1a2060d62c81415de67bfebc3b0549e1afcb74eaad7570009161f86119e953f3dd97afe9927d00302056841c5f27fb5545337cb234bfa5065a77daf016e77bf7ee1248acd07cc138d33c8247d90383238e6a8e44ce11cb91183fe82d5ba462cc581669c32ab23b5b49ba28be960e3bf380a898e599b8da44b7380c0da46f4a7326ff3a25d80e3899ffaea158060742fe5e5cf44a1b64811d76af32c78269e9bdd9ee1555273402c983829e281007d9c9b87ce6045ac1165243cea67d65dbc0be5747b2a353c2abc505be70499006e6955f00711068e0deb7bc7d46ddb69699b01c753207195c8d23595932e57a09138674e3e4743b0af0166149f72ed2b74daa1f917585fc1f36219b998744d502c172eb062bb54fcca7f1d515977d99dab2426a52f24fc55b600cb3b99d291f0e8f702d66fe8ff4f16404471c5b910aa87eba1a9ca77e2e6b2679eb654a2c7a0b3767638701f219c6de2c33aefa1139a0900cf7e8e292ef7ca1fc64025c594b03399535201c5e7e6c9aaf372c7ff1ba4281e32e4671dfac5533cd5fab7dee03063258093d742e3959f5dcb30fecc6e65f8b34a4ce7fcfcf2d7778b9bacfc5f60ebcdb49f33842e29c454189ed448aa58650f4fe2f0cfeb82bb7d238236b3c790905e55394dc9179f7dfe46dd7d55acaaf77a69cc5e63904f2cc12cfbfa1abf60e99e7b386b708d68e8f38efb8a63058cacfb3d6b0b07c9a58689b4ac788d4e608b351ff546304bd7e8b455f74d90d32d2d2d50507d8d3012c14e7bcbadf8d1e0c8651979a3036a23873127397268e1538e061b9d46087299cac713ed59d95a30721b1fe97f2f7991fe972634aaeb8b87a957b3e725e086d7e50b571a41cddf808cb2cd516eda87f608c7e93ae8586d44d66e1ba65391947509b61880acc898a05adc4e41ad30c9f88034f51cbec022348272e557216ad8af7ee1645c11845130517e6fd5a8192236ca072955f1dbebfb0a7f419c175b72f5f7a184858a51cbf01 false -check_ring_signature c2490f6becf6f1f9587f3e77b201611ddb5686664c759b51bc438b6ebfc7985b 45070a532857b595db8b016b524659ae03d5a7e406ede2c1bfb00116fe219477 25 93d264a15c52fe97d72f83c27717cdf5cf4babcfddb2b04376fa4847f08c7e54 2811208ffdf7b8aa6a641b620fd90b943e21dee826733c02255cbf8885c9cb7c 40982113e5c858479febe81263c05c1c724869ea1a6db3876135d0a3977102af 103edb93ae47287ad03df3f8b636ce6a8abf19e7dbcf945174a6d9b3c06ca8b6 d52e1459b13334eb326a62a9b38e1ee5ce24c7d5aeb9c579c7c96e57731d2c0a 3609b620391deb4d1b2c2a01967744f1c8e056b51cbaf7933b5adcd2c40b9b06 75a01b936dc45ee252554ab01e748d69e9bff61b5350bac16c2c2fb06358fe7f a654ea2e49e1d79cc53c7f41f61fd0448c16f1d0f325b0a116035cbd64420100 4ea20f1e3eee368d7ebc70bcb5d72351fc4dda029e90cd5b4c7743d1be3c7b86 3b3a8172cb96d76c38c711ee03905493387078b41725e5b4d076b47ffe6d5cc2 9bef5143215218a41442293b8b01dbaf709fd02cf642a5822f2bf84099cf2cb5 62a89731c2c8d8bb398a4996cad19d1c90a7d46ad651c3c5d7abb445c10a26a7 1d2f497d16b82a69b3ce7be257cc911eeff89b7f3dd3b863c9f47e9ddd12a1f9 f6946b1f31f1881fd6f5304d4653b687f1f62f9ec233c2aab99dbb01f29fd8e0 b3a46794421b17c3c8351f6cd6410264746860899cea2f22ec6e94dab482cc92 eaedd48f95522ccb38646d2f846e8a63d6171de9aa9ea55139f8aaf9a6231d6b 79def1d2f0c89432b05cabe96ae7f1c6354abc6c4198671ee27112bdd0ee73cd 3945a0834107b4c69eeaaa15857f94da17c02fa5a8c009604b2c28eb02ccf680 7c8e1967299a5a625b7f1d32adf57da409da7aa67e11158473e9a48e79366048 d25b295ba304454e8a6d59c1bdf972261545884f827abf45b1cf03be835eb6dc 09f5fe690ceff788a112ce274680e1178c10857dc1146b4d905e24754146625e df51c10932d12b7a3b13fa87e62dc8e4f5f35f4848bd1f25afde05869428bf01 6e4d20076fd20016e886a7e42ff851163a893e31e35b76dbde32dea7aa72306f 88c081eb3a57426f28a56de22ec7dbdd64143b58583d675ec1ae7c317a0e7ee3 2bbd64200714361da0eab012835793b63c6735581897958a9ad9a72d6a37b751 f3469ee609384359a4e6d9de2bcc0dc39f2656d22b6d894247fb5fb376d1390bb5e193579b002f06a9c33ce1f88d864d7ea405f028004e8086a8fb1d2c5e0c0e3bb6a56bd29978333e4a366c63ef584c1f31d1572ea710a6a12824bb5c8cbf22dd27a45e12a5b02aba9d6694e6b447471276aa598c4f456f542178fa09d71e06aeda174addd2aa0c2041d77b46f86c386b2fd0a206c6588d1b58a914f3b8d9033fd9bc2f13b952bce6c2ff78ec968c81f83811afeed9231eed64f5c88cf3c00573ce68e3be3d03accb88fdba696eb0ed66b26d1efcaa0394e50f66156f744e04e56d00c4402b6a12bb6f4b1599449a50b8b4b0aefb44d9bdb921597cdae68f0a7fa280d191e607b57789b44e9b8060590f8189eb20473d94b08a0a7f37a0db0cc543478145a5349277288ef631739497251428bb2325905fa7b44b57ab0b0b00a5ed3a10d28c07f0b407e7a6dd6cf55545c08b6445a6f0314f44a96e56eb1c0df0a64fe7c383178d22359abebf86aff7cb61ba89fb341d389863f0cec897fb03d08b03942251857e5565802e725148db54d0ae76bbd96c1d994295e2012de00aa4c20557a8ac1dc18cab328dbc5d51c10c1aedc91c2b7a0c1f4725e5dc09930d6d2965945a3c2d5185fb8931229b7cbe7ee232e8f6c8ee451f11434bd1fcc90a0179c4273ca6d04ab0de5684fc980e9b39c4fa9df2d1ad438c922e99169df30e060d3b41deeef4d357462a9cd87bf9d151ddac89f5d0e3ecfeb5ee3970fa0c0026852501d35648f51563c526923ec023c948152f2d4a61c9d5765deff19ed20dc575c172184454d17c4f4c2cb132fb86d20046dbe3e97f7342c3a1567c7ca50861754327c7fe9c41d6f4fe6dfd8bb2279c7dded2fe6d75ad3bd7b96333ae2f07e7b0732e313fed4804ea57d4a978c3328577f27456e0a52edfae626cf04d02070a362e364679aa3f8dcb74b7fa61b2ef387e22f29500e947e9636136b839c721b8b0cdd296f2e09043058ed245a465692018069157d2c99e32327b03ac9af300df44ddc1908af595f6f19dc00a1a43de38a80e5984487f79d5488ea59eeeeb045edaca6bee90ac81289c15d60fa0dbc6ecf82ede887a6e0c4fdb63f2337e2f0fbe68ceb77671b7147e9fa82df2036643df427295d69fb5b7b1b8f2f58802c806c27415310f65f4609512e6943be5a028bdd14498edfb5174a8bf20273c756009dfdd13fc057ea0b5035cf8451ea8bbd44547d6bf31ab6fc14edc64e0ff8dda01f9c5f469ae3c515974fefc3b73b90bd0f98d3ade36a6f17264a09541ac8eec04082c512241bf375c6cbf274ebdf1d74b7798576f752f40ccdd345341084e44a4bab4c0a44fd9bcc6f7a1cfb80432d309334cd2a270ae64fa03193e94ccebf10c2ce4e061c35058d6f13beebc263a5d991d9e50c2814497e7083bd235101b887335272b97136abcfac48d10080834dd04e10b42cefd1292708dc05abb86eccc0935152f6dd7f6d4911e50006a3e03a8ec6fe9faf92eae34f0053cb34ad676830c89c9ccf2d9900a34dc7f881362a9d081cfd8b951a49b5504685075d5efae920cab63e01cee9ea02b681459f6ac47f823550dbf88d709602b3e047b1db8cb8f05447b9de84a045eea303b9cdd4bc072b9247a8e4c3bc4efc44e28f0f159e01d0b7d4b785ffdcaed869f07a25ad63194a7f6d5e2127cd0856a325ee0eea2f5ae05154604649d401d6cbb04e0dbe6a54b6cd8d1248b2459ca8d1d0714c40d38000e037c81014efa1c855381369c486aef2448366654c5dc69f8440ca51ccb5fb10ac5ea83b0b948fb3c3bf3796f9b6982e4c0b6ad0d67bb0f0062e2e78fe7a2320fedc36607ef4374dcc81da14888876ae8da4ea99033c8f473df426d0d36d019038e4b47a562c3e23b3ca02dfefb77f5d06334cb108419c4e700d2ec3361abe80db72695f424b6b6e83acb7a827505848a99cfffa1c339a83551c1d88e3cd119094c83fa4d6457370cec0435c47aea968d458d42f4ef9177664b684010ea32090a901ec4ee5e1ec0d98a4d9906b6f14e9f214531b84597a408ecee099b2bea6508ee31f5ca326af36969409f8d65eed2de7f8d126899914ca5843622d0104b9106c60bca4889fafaf5759bfda644a89b274f8aaa97508600fa85136b806fa04009d58b94ff6de2242ff23062a1aabe34584d22eb14a433c136429b8ece75d5d708b98bb182a60d3dfa9d5913902c7554a61387cd8faa9387dedf0a4fe81480ca0b false -check_ring_signature 777a8df78cadcb2ae93dc59fa8c0d9a47b40f0162ebaebcca417f503630a26ee 73e2e7ed54f0b732cbf0ce16851732401b7d05f8fa4f5c106220274f95dfbad0 8 245d46cd8f113b0bf5506936a4ba110d89462fa3c9241a18381933af03a6e80e 7ca97c0e5d0c3922a7d3362d53e91a60cb7937e3d82d34b1127481a3459f67bf eba640ae37d4bfe05353d9ad7752e6924c41d1e0326f479651528a31d872e32d f61a6e09acd0a93c3404cbf9e2b89b800d8bc28910b6ec51b4d21482139ed65e 1c2f5e657db4d103d8fdd0bc689c8a3c9dbb32d91b01cb6d599e3bb24fb01e31 99f0fccd840e360620f8251bd3a77df0b27d23dfac9fb8d1c7bd9aa0e62b7a7d 08020aaf3923bc734d6a985fe83db249f762b1aa67eaa94a99e61f5529a48278 ee669a46165493791ce4399c3458d5604bbde9f5b040505d312e4b44da6ee557 b56dbd8809fda5608d06bfe2c172d5e9ce9fd7d207dc12aacced2161e200a40a8bace736efd00461a7e5c1d44f1cfa379b5d0e1c3d07b8810d3ac50087762ebbef408eb562e5866451b6279f9e336f8a8ac8b4e6a4246d60656ea32a13a0450aac2cf0a5d0c31ad19760a1a65270dbffe839e13f4fd789f2298e2978bf7439067afcfdd4b5c8106cb4b3ed288c9e4c75b21c483dead15df96931d6bc276ab00ba2709bc656d2944a68a2790645abb3fb40c29fe93fcedf9a38366a5b7de9e909c95d7e6591fcf1efd4a02c26e1f699fc5f1f0b2a9ed85206a9e1c432623e5d0d28a0624a5dd7d4a79942638c46a01d845ffc5cdb78ea1fba0dbfbd07e54c2c0a18762439dd3fe1bfd82d284afef5b861acd16f119a57af55ae63d8725926b3004484fe7e0a4a3089ee87e8736488bc1b8b33e9d2b5f58a02382f4e96be84be078ecb7aa4bf5f0a039c8f74082c50f5721b10024ec297dc7ecb6275bd9733c70c3cc9979456362d38d1f68e01238e6f9270d6ef11e64fe2523cf70a0689496b0571bd8d715485cb83b8ae52c1502315190fd45cac20d676e46547edcc149b7f0c99ed44e19f8127e91ef87cefce9f3d0339041b275b5476e63864f48e69c8c3087d1798fc3b9cd2f35aefec57738e61a37f4d5311405db267e35950ecdbe10d06c56551d9fd8b7c1f8ddf3f53ace28f388e564df032b865e8099659d0aff7a400 false -check_ring_signature 682fb6662bb3c03b9ed55a361cbfbe07579c25d94d829ab708b845c90309f27b 13b18ffab10b67e0023889543c644fb8c119e8abf54e77d7be5077d79c22bc5d 7 82c4540c2e79a43a001ad866ce73c36dfd1524153da494aac2237ab705791ea3 0f9a6392dd31765499f688e55346536b267cd578253303be887679faac90437f b4eda225a5a1f73db87ec4432f5f7b5fb245fe8bfd61adf2f9ea76152e5798ee e0dd8b02b42c626146bf9c39c9e3106d5c4492103da3d2f2e907fd3dacf6d335 16a02931231f69a7e9edf9437aa27231287c4f0128f2a240570758cd1fbd4a94 7ef9865b522a1a23cbf415c110e745b00b522408e61b6584aa538d57b60f6e0c c6e1b6fc5aebf4a69c26847596405df94e96216546a6dd526d7761de9fc6b68c e4b92d8d97c664e85806bf9900984e68e511b462b3b758b29128dee48ce0e906dab2ca0316ac6de356b91cb0ab092abb1a926cba04a00e139d35042e36023f0f7b3de08ec94f10e2853a78946165ca4d38dc411829a9da61741a3d8dcd87be0046e1800c200e9075f6f383eda59dbfc871fb33ead386d444c61d1827a0f4ac08c976a1aa004e098e5a41f3d5340b1234923b19886ac5a66870810e8d3ab29701f3318d6f4ed017e452c30df08f36676fbfdc48bee5597de979c68448dca51c09ba727ecc4a4f07fa9d87e1443d5605f245f2f8e1caa214b28507e37d4bc1600c70a11115bd5bebf8bb0d992964e9ff0cc81d8cbb1c30c64af55079767a27580ffbd0c5143bcfacf2de5641d63f12daa3f75c08cd842f6282ca507888958ecc0758f7518247a0a4f9c9b07448fc0373ff5915d274c88df4501fef4997399439014996fd2805ba86ba8c4f2aba7fa2fe5ae023e4ab8da07d5b7bb0673a55ec1103bb3fad9dc21e9bc76643ffd77c134f46723dc565940c013b282f175ab1cb460d414ac8e9bfec36a70c0ec350a1e536ce8a02055ccd130ae0cad43171e35cf20f5f4ab4d38e12e7d8eb91567d89a8322174651f035d6180fed41dd5a9f311d405 false -check_ring_signature 44243efe01f46dd04537a2bbd68082baa5c988da04b4265b38ce337eb6ceb33b 3dc130ffe4673b1e95dd032211b5810ca81b8c735f5d87a9a50ba6172395c741 31 6f8a7498b6a6ae08e8631ca74088629583e2e25e8f17770de2c1d5ff5a7d8590 7d86d0aa3094dacc3abe79bd592f3f4e364905595fd672013aff940152c62793 d6aaee1a3e7f89d1d6743f8e7ee05555b87c46243aa5b5d35fc3ae293bff508f ed6855070f73c9dd8043736f0dba8ec54fc23b380cc5e938d93fbbde503242b2 f8c09cfffdf574635de671a35b398a44d5d687f6ffd95935cd934c542528ce23 e116c8df7a1fbb6c72fc987db571d946939d50f6404f49cb83ec7e93988951ab 8ceb93a88234a8080178b83bed68202c1bd7568d14423a438d49588623a80e18 12704bb5e11c2fbac3e246c2b28d59ffc718390d24e1780f77ebbd51f7f1ea7c 19733736bfc4dc2fd0dece701eb1362c99fd194d1a4b517d6d8d37e40233fa57 1dc788224d32cd07a6c63d3e07a9404f5bfd656c6899e777d86a70562a971cdb ed88efedd1e095068e479d538638fa11287b163c9f7bf8d2516425fab8f40142 85e1f57eef298700556b948549085c01602684088ee7751f44dad23ce8e83b2f bb26389fb116664a94bfb93dece10895cb97299275f2e4e7b905eae66e459760 4e8f8055e8f26a0ac3788e168645eda0e6597b65b62f4121b579383ada41897f 94c8868658b4a87af53531ac5ee3cc1aa490b4db371abcb6c991c6c23df6077b a5e982e355f8f08951641129cbcb2026c708886f31ba535f9ed0cc67677dffa1 77e3dc528420fd03e5118cfb483f7dd1721308b18e18143a8d3bb5da0c059e90 512ca3127f280cc2c5a3c0c7fa75426582690974e4c41e06fde39bedff2c284d bba1bc71c16d808491079cde729458cb80feab83886ad9db6701e764cfc7cbd6 de09597fc10ab11d4fe8f8f25957874614fbb7026cf967a3563ca73919eeeb0a bc1d287bf99c5ab8dec40ecd15a950884353c3b711f90744fa709f0ef4baf94c 4317ff3308225e7c84d91eaaadd45689431f4b78c337fbe30849e542017a85ce e51f2247db2ea9b3930f938cbf96bab8cc2bdc7e45507a72700e72979e549a7c ddf0a39e2e3d663351064afddfa6a8d11aac9e895f8849e41beae8872b7c1d15 bf6da08895b7e2fd2deac493a619ac362ce66556d6de8a51f04c692c0c79a81c 948aba46d693fbf2ca6c1d12bdacb1c370392ba8667ad3baa090bee1a99d6b12 68691b8eb9f54030906f58188338e3a56549bbb4bccf3d742de82a4215783494 026df48372cd6b3d827edf21cc3bc0f7ad33e94b36da93efcbd7df5aa41a3e8c 68f6e42d8f398397aa1d419746fd6b0a75869f4096f0c0c8a20670f861c7e9b0 a1ad8e263508c601b585362536a7e0f176b8927c134ffcd445473a1f36b4dfd1 3c60f01f936599eb5fe6a0848be650725d4ab28ab23c76029e138d9a7d0ffc32 f82822d8ce6b0c7736efb846af124f1d8808d1d835ee27c8dca829804c2cbb01172e16ab78faf0076f7b12a6f7e20bb02194cf44d879801e74904e3ea6dc900bbc0fad8c09cea012e4398d5aab2110e2afd0973ed5da61964a241363e5ab7f0e54a34cdd163fa7b2c3066c0ad57e58d448eb1ad173605b339f0db53333d4e20d1db99dbc5b17b1f054ed6ceee15639aab05d7c96073357cceb9e2d098785f8000313c6fc61bab392282cb05c1282d29c36460a8f820f25b4e894908eb025020bd397d9c3cd505c14f28a18f2e7248f81be698c03c35ec9d671e018a6cb79ad0da29afa24b2424bcb7f8b91b9411244f211659ac1e848b2d63b28d0ce6d256002518186abd8cfd947b5c98d0fc0395d3f8537d51a376f1cac7d055871123d5b04a8bc79af695f47cae44d119ffb0baa417d79ca5ba02036602c079f7ebb12b00ed69165817d6f76bd9a970f71b463d9c959af4b10d73c918c9b15cb8665b7f4084fdc3c89100de2826f9e1637e512566a83c6e4ea1b80c3281096c6c72715ef01382666e68c626cb3e266a2cb5ef2b17c86d48c72447b4a9c9ab73fab05f73509314364e80495477e94b2522448b5f68ee9079eaf98ccf4d4d10828c6b60ac501c5b8c5141b48fcd6be16bb0f4a98b3157a263135a95bff9698ea75a7e784f807db35a6130a0487c9e31aa50abbbebb76f2086712425b97d51799f4cce250fd0e40e8eb1e2cba5d58151a37de2847dbef93ea60014d4424a372c5d6188ed83546cbb95a5c542ac3d78bf56d2cd214e47e10c94b16c3d9f44ba65b8f3e59ae410b2bde366ecd025c780123cf1975985fcfd5b15563ba17833c3eaba5c4359a65074f73fffd9d083c8540ba7b7836e69bc6e722e3197e41c13ead9b6d52f49caf0fcac2b449546efb2a5fe309bf8fad9bb715a328045ba975902f122aec1a653c0aab6a38c026657cdba4c83e2b195b94357b0ab734e6ec861efe11a9dd25941e01b697a71c3b258ea949e5292e75bd2c906861b55387f4275cf59e117828a062010f2ae7446f9c3037af43fd97e2143be3be6764c554ccde748dfc80bc3255570eace118c8a4b9bcddc0c372d5e8b6443758c711ed783b9a5543216ab010b692039c51acaed4bbb379c8e808c2c9d71bc15f7794b4d7b9b8905ccb3081a24b9e057aa4a3da337065ea7ba03b15bdce14b29d1154ee532dad0cc7d0a9e869e4010e3a15d0369fef502523963f3bacf150db30c9187d77afbe3c6dfaf3b64ac48f01626416bc33688be5ad2354e38d2fa381b0b14fb858dbcb6c71d7583eea0cbc0c8877e0bf8d415fd45370d940644d1cc72ff572cda0884ec6135bd5e12a6ba302a5adcbca5ae47a4d05bacbcc2d30faef6e4ce0cfcfb871c974f010d98f9bd003de026d73c57fdfb1a7c8562fd0768a67152d9c55b9b5632c5e54c95b0971eb0689583a2b78a7dd9673428c17e62237bbeb5b216ff784383638bfe033ead98b0d00a87d6ec6328aa15d4fa8d200f8127c4252b1071834c1c4668f1e6ae9a8e4013a28d2f067703a51871671d29f50beb6551052aab6de478e4b3d724b3c97ae02c1a3566291c91caeb26aa1593de7fcb10cd9216c6009fbc3a576f38479b0780893df685253e5edd1eb276aa6c690eac3bd63bd40517ad435e7cbbb3a86fd740327476bd9843d4effec5b7e4a059068cd645b8216bc7e864b8bf480b0ea33560458687dd5868d0ab52e814e8da463172db1658f31b068d890db7ad363d072dd0791ac277fe5c4139a0bde5010c4d1231182b38d6351ae026891567326d2b68901f9733e8de6ed70eb7a49302b0dd82273a82ba9dc42794678b8f7b67bc0c47703d10d8f44dc576570b1226b3bff99d6277693b461471b6bc2543508561b64210ec0642bbcd7d2e77eb5b443e69c98d79a914f1a1e15326e900f660c5ae88d350ac9d284e497b624cf39ea22a03afa8a78fb47845944878b09eb72a9d4c16f6c02cbc5098fd5995c778fa7db76dad621963715404fd22dd8a8930b0385373e07075c065db9c859d12f9130d9fbc3c802c231f93baa4fe11b64e60474aae8cd7301159d4ba140b2137bfee5a7c5d38e9959ce7373f31713d454b0428d736963a200b6b83b0a7d2c5e82b55d3eee4803411fb183376b29b95232edc40ccb10a530020cde850e1b88aedb06458a45877889740c9585ae902c29878a9bf082b364020cb73f137a2e2f2d3709175553498da0f2bcc4ce1c595d12f21e7e16acf8bea30f34a04b4383a794396838bc71cd8c25609447602a309a7c034c20ba6f16f51c0a44325a6e5b3a6890b9bb86c7d0b88f3a809d8f7a5be00042023b4c0c913c8407456a8c4282d24c94d2ef0858062ece5f10f7e399598021e7546b1abe2b829b04c59bb18e4c564697595e64122566122a51898fcdb52cb960b1798f5dc46de1019bb70b8814c775e02cc24112d5251ce1ebe46aa3bb7d2778df9fd0ee027207038af5d737cd8d48d4f4f12b3477b0ab75b3e72253e2f095c9f19e7081087113027f675967f9ed5e0ec2bc1facb391d40f4e463455d9c043b21d81033a79053f06ed324c7b281b0708c54ab1c6b42a2ae67b86b7c74fedaa85459db9cdbf20010b9a40bfd9ffe049127df852a22c15138a705f86e8097a7473bf605a928a66fa0d1b119074380fea2a24fff81848933c7a1ff9f5165e7a438db69f0f0eb2ea21023ebb9fc350e20c4f2837815ab0c12f3a189a0556aa40e5c8f4777d6df9e8d8075036d59f2981c3395802f37521fd124e309ce6b562b1ab8c8d74d4e105b83f0f false -check_ring_signature c97dc515425a9d287a62988ea2b1b692eaaab8a5595d0fdb09f6d5afdc6a7660 5fbb31375c9e41214d8321bb99fca08a004e123edd151a15cca84fce053d184d 13 47e13987f7f3f086268e43014cffd079c96f53c8fc7d3e4356ef1fd9a68f1344 3bc6b9001faadc1aa32048a171a3c6b48f72477c1456f0ee5d31d078c3261995 dd7aed65a9b39b846d94bdc5ef343449b13687ba9d569a7d14aadfa64165ff33 eec386d35214dde26ea9e1839ab2e3d7701007e98b9db64aad44104251facdb8 9ff4ee609750e6104d88395d93cbe50ea4bc56b6c67dc35c5006f7635a37706a 9a0398c38e4717777177844ae193186b2549f05458f860679ad96e8fc723d225 10fc9c7f2e8dd1b1844a124461c1db03673651b2e2893566b65b6af251c31a60 ba4347e4b4d9f93660ee716c2bbf5315d913a7814c9cf2e29e79c90211a87cdb 5fe10077fe98611ae88e1e02c98912d7a9c51f7d45a9f89aa4c498dab04a573c 6c2d3cca8b22f749cdc182cacfebb9a3df64d9f7e3b9242f4b0c830a2ceca8d9 c437971388ec9c0e085f3131478a65b81193def418a4b4156d1106a4a5898f93 34d54363195344177436ed0f0a9a22d5b079d95f107ff7286295779bf15160b9 c6c3bf9836ce5664a7825c8191bb1f29be713258794f4d9ecb65f5ab1947aee9 7e57411c60635be3ad3b5e2d4302a22cf4784b75131f904226762f7948b10e0a018c28b5bc018fcddccdd5afe9abef9da9d568e8e23f5558a3cda8e4740c5c0b292bd4ce481838ffa1e2019433db9ac44f0f76889591035d6f55bc4b2aac6f0de87b0490d49a5694fd0ced58d77759665fac5ca90947a68f7ee381994b7a2b024a15e1d8770139075d778f5004832d49a9f6d9ad1ec976af5a2f01917f94c40144dded92095b8eca6beb6424e125d52c18e0542799cc17eda2c1a07fded3990b1b3f54097ad3e7e0602e4f7d51414017882d25d46146ec2bb299c7d8e069310da3383ae44d69ae511c5c641df8d5ae4eec00c41c617b310434024cba4d3459022bdf91dfc7536ddafa46cb9e11318bdbc9bceab07ff35dd4c768ce57ba9ad108921c3861496fcea36a7a3a5fa0c10d638822f007702d15a5598708c0854c6400f71529daf899f66d16fabcb565d264212a5134d70cdc8028216430aa84691906128aeb81b2f3bbc7f845c72537cc8d024247d272f5b71cce219c856e93b1b00be29152593c54e225a50f4bef069b3e6caa722a77abb9f362208c6888a367050bb42748fc67f17db22fcbabb45fb5e06cb14686c4f8e6fb24368e442c8838480254ad560252758d63a4ae653710ac8d2e38ebde3a83cf1dd1e1ac2d9c8cadf1006ba06796bc32a044d905f93adcffbf1279c6fbe848f38a6b347307e8c37d2d0e4bcc0f9b2251234b8ae8327a0e0a4cba31813a8ec90dced70d34f5a032ca1f01f028a0316a9fa68c772c3db242bf7a4fcd7ded0f564a74f5292c4776d679ca080fdb3deeb28e613e0e184ba23bbdf616c18028c4665c981193c821338241ab0c646f52607c2bfcced2dc58aa21784d5cc2fb850945f2ee57b2125bb78c7f4c092a32b6dd2bc9b01d85e98b000d17d9e1f8678b2f10b57c1652cb191c3d221700b4e274f43929ffba9ec5e62da2ce2fcdfa946e6c20cf49532920b2fdf19d8200235db3690df76a1dcb5ac38509b7dd124005543b02c5e50b3c69dc3ee4fded023c11c72bc1d256f6e50e4b091b59011c44534aba5b72783f77a758f5beb94e029a56a99e592de19559d8a7ee36983097eda29cadf78bd78c5eaf91414278610637d13c7a244dfab57ec53566ac5ad99ee898aec91fed46c1bafc495a53e9ba0f true -check_ring_signature 0c5d85181d08f0eee2950fe5ba636cd14e25dcb0f4af95c3d052e43f878c26c8 d053d357c7dcee9655c43e8f516d8889412328e67b2257d7e328c356ee48ae01 13 3756f63923a5cefe811db32df8c8e611cff35e71c67071f78c19d9aeb29b87e4 a40987919867241b9d66686c1a59bae7e75ae7ed09c08575f43d9c86c8dfd8be a5e0a4001f7992c09361073420730276da694520faaf1232f2256cdf7692cd5e 932cc177793c5d33d7e5bdb6c883d8d7844c662eda8e3ec03effad7224e7e9e5 64a1b7c57c69ffabdf672aff8dd6561a644097e5ab7d0741ea5d96a684c70eb5 f9db2e47f3ff915919ba91b68ce7ac6b849342f0ca456cff6a4aa57062eb9974 ca14127f5f5ead2c682c9a2d963e63a9bd1305a901e299096980837600066fda 0f205c580031615c3f701ad049d21884cea55d821b07b7640a56ca54c9e4763d b16f15cfef2a3e78ce6dd3e48004fc72e34e3e60d93461b48691416443ad9575 afd87d588bdb5f73cf3b07a82716329e97a4a386d90a5428d83c959bf1517c06 6c231be23b95c0e48469b5a95cf262c7d5e9b9a39ab2af1a48387c64609b5555 8bd143722f38f66573c3f7fce9cb313760ba041530bb4d7878b9beb54dc8030f 16f4baea7a30a771a237cb53df347f699c3c384e5ba995daa956ebd1b95881ca 7d76bdc8116530a93023a0216e01eded616c2b054bddefbb0a43ffab522b8c04f3f78273b8ddb413ce13013fcd1717a6fdc8c56c25d2b55e4b7ae04a5dc6b10ce5fedbcf8efca39affd5a3df7e438f29b56f06c8eed9d1ab0733e3d8765f75052733b5b75ca4eec97507a5c3393a65e69e9f3c02bb87aaba10d26d8067386a0d68570cb8c2a97b835a76d253939c979c6d9e50916398353683c51df5b06f0800ec69c2429301302a9ee1b356be550284a48263fd3f58417c054399f0e656230161d92e142973f4caf4a60ae6be17adf89cb921773c1b9e6bae62e12b1fe76a0dedde62fb0739b4bab40b35e95adf027b9815347e983ceb6afbcb97829e6c0105df029800cb3d935094169681444ef6ac5affd2ac38a4bab37b153f11e6e4130d74c98b458cd9da9e75e284f9c18d3988f837a71f12d22c0492341984740b130ac840e777a1ff82244f5c59138ccba4c448081cb726d0e7bbf66a949db30efa0c8d3dc5a18a9966ebb74f0b87ddc6d8300fbaf78fc7d6b824d15b94870068a60a39fd38a7a58f510a5283821f777e089269bae04d4225446f3b971d642a2a55001b8c8eb6001e90c0931628eb45c7861da4ade7f76fd0ffa96f2af241d8262a09e50892baaf349ad14561675957e7347f43987a9eac86a17817e5e35379fefe05ec405d64a654bc8970015189e08e62630724e0679ff9767063e10bd6d0d5e50fd219490bf69a2493fea665c9ae3c5832259c2e8f24132804a8c28a9771b88206e0b8302087d8510fee59fb8113e42c777cfdef4530611de376c9e47982e27d0202e952a05249e7ce5ffaf514e5e6618a0d3196df2931868621accafed6837b392e1477260433020e7d06ef1a0c50d84b961268880b112988bf70ef387102cf0f01692307087ba6610ce2c7fb4f0b87d032bfecd6211326bfd0da4662fc8e9500adf1698f58542c4b69afc4f8a2aa2b33153b66ccea7caf0f9fef5a3c360d3d0b0f75ff376ba80ad4b15d272d48b6f998e89dca8eef667f78abd0b7ee1781db043ea8fdaa5950252a6d3e912185b07f36917a6a6fe4b3093c7b17e2cb0a3bf207d4f1d591243951cba2f167bdc92e0026ab3eca28f5e9be366c644aa2528b86020193e2595b8c0af11072c113acb7f977b9ab01a07a845bb5b70c711781b4a707 false -check_ring_signature add3cc59895480f505942362d3b43d9c85be1dac1dadda906feedfab7514d254 788b2c43cf4a7ffada6ecf40c759da45eb940bc3ca57d102644d7fd2563bd3b8 45 88bfc00f9ec6b0e89e5f4ad5426bbff22144d25b00c8a7bbba0221aff91e6aa4 5fe27a88529c733fee5143f3ede582f8370e69ce70f7b2081208840731a157a7 940aa786cab3a08fd8c196cb4a14e71d78730c9aaf2ce82554a608ddbbde8a47 343cfcd98960c13b56d66a5eb2704133707242dfc05470ac7bd72e920d9a8ce5 33c619e29452b1a59d958f1bcb2b8c0cee3f98d560e86014fcf07efcade56a8a c10a7ac42265a0af39d2655dfda3e81714f1207838cda5aa3a032ff9c8d2ac02 b4f370ed754fa9120c5d3e497323d254b2ad0da31e0ee62ca9651ef513cd9cc6 53c754b03ef667852110862e96c274b445645a8480cadda4fe33ea8462ba191a c41c95cdc0dd2240ef34a547eaf59888015215504f05e92aaf56a1e6bd522454 89890cc574dc71719d759051201b3b10af6946448fef863040bfaf3a2abf6e79 70c29edfeac85382dd709ed76abbc9c6242f0c472241f085ae73b3eb2018cd96 a5b0292c4bf6da5ad59ba693a5cd55f8308814768c0d902a42a73022841f294a 80a25ffaa376a6b620701f185dd7c58504e3c87f671b3628d20f05ea9661e21d 3f6711fa42bb98172bd94ce43d6c7b782c9bf5cf667cc0f1f969bd7e9f665d32 b1e4c2da44e8b8154da3c08ce531812b0bbd817441f6c406df7f45af5f1049f0 3768c9e7ca50fe0adc78338710f91c5ac180963a00692d323db7fa88e3f8ed1c 53a147eb4cfc0cd667df6ea229177e85e363ee104f80c1d5c16013caf8594ed8 27769bcfbcf292d731dde4bea77fe8bbdd41c4e88709f1f57b688cffc6a2d7ad ebb521d964417d7c5e28883752ab010c01356bc58053d3c4c3d35693d0e7d6f9 cd2f29945e9c4c6ead5a5c1c57048a05557f31ebb2426c3d67d9792fe144d88e 65baf8264b7535b9f5fb39c8a066659e8fb69cf3eea4440e691e1d836753ea04 65f6dfefe8332e6b0c9210d536ed7c94ce32232e188786eb32a3b2e9908824b2 87f318867c0e544d70aacbb7aaa791875a665726334d5cbea77a0f6cd70618ca 08318526c4bd2940290a143db3ea2bcd18c433a8207c0f7346e10f2950c86fc4 f8e9b2cf9b6cdaf3c13404b4d607b2f070f0a55b8c0805f228566d537ae8191c 2693c9f34ccec77476b8f676ca6a9e3c3aea10f2558d22f8e906062f6a3d4435 93c75cf3cdd973699ec32b8622230d7166bc1779a9c2c69a687307dba42cf523 b1cfac66d2e57a05177c9cb1556152ebd697824f06d6e5437bf078fb1bbb6f02 088505e010c6d3f0763dae32878c9386338c209353869290af79cbf36bf29170 3acb4f065038bfb1fed2096805425137ba405b32ad532959332c9495a9666fb2 35efa2715c3d9fb611c2369d9839cdd2ffac8b5b1ae15d15969625456597b1b6 a94bfe0c0433e63931a7698a25eb383455ecdae1f8b07a9b508222f2ce32b964 c971662d7517c12ea77e60aa20de64375adf297a246519224cfa7c7ceb1defca 7b08a8658bf569258ccbacdcfa2c4cb0e541af384d0e35419efe7da2b565ec73 6d13d580a1bb45031ee008cd24809264bdff6b3de3678c0b769da8289c642c61 7566deaa128d4fb2743d6870dd6d796bbc5167a9d8487cc5d308014619c8b48e ef512875ea80513692e29605db1a0ca08874773ba87708d61c11560aea57810f c2d42387837745c083fbb5504e0907344cb0b27886d5f34d2e3a9f458094abee 3829b4551d20b2c417e7b50dc8b61cd766990414b697322ba09c675bc2171617 bba9e7848ba1974e440ac78ec4e88c6a4c6e3a84f6f4b8e989936941a879a8aa 087b89c8b63cf26eabb1547ca763425dace47de95a3803f702cce337d1663431 f6988029c1a8401e02715cca9731e4e491c56859a673c88025eae463cd8faa9c a6e66c46e82a2c4fbd6bf5aac642fbb958c842cd632b8e88d562bf69d3653a87 115209679769f9230d77a0b4ed1aeb5735694616eab482cfabfb3446b323890f 56c1e361ab792b69612b88dee6b5fa002f5030a8556dfd8e00174f50d30e5d54 499cabd535ebb4aa3542706ec9e58ba68a1004fa18cae2d1ed247421e5c6cd0522e443b324317c3794b56812f5ad803e3c516394be890d21446fbe06c657670c25be68919ca3a2dff68ff46dab2abce1cddf65998a1e7d5ce18c9ea78481df09e5173284293a78ec9e94ac4ae4144ed8225636720a7b4ba7bb35ae3ad1947006d0237dd7af0ff1fd639de9f1d0d46a7fcf8ed08803093af6500e75828836e5098b02de0f9c2565cdddfa9666a1cf24622357766086ec5603e5392d8e626f8e0fb237f1099bc15001108b7d8d05480f9687ed3a5e1198f68dae9a944054f39202912e1c7005c9f0d29b352942a997168823b6d1c2f551900b6e3e7e427c743f0bbea3f596c9eb7f0cacbbbf261790ca2b90d66249f0bd468bc5d0c297591a5e0e73245c34b327cdd5298a08027829b9baecc099e423e0c51d02fd77a74547d6074d67402426a0b6dd98fa13ce4a662027b7d37ab302936245d57380cdd4de4b0cef6264f51d4e1e708496dbdf40b34636819c50c876b66e836cca707300c4ff025ba33958ab2b83eb8025ae2ba631227225bd9138f02edaa3b4f5170d5f210e0dc9da6983812f923b1792018d484a9fa517394e380d02d50edcf501bc718e190dca06679bb7fca1db8c27c00507a05dc96b610f664df2f1420e3317b4124096017042cedca92e4ae2ec19db47be00ca47012d384bd257fec1a4f029f4d43dbf0706d96c7e9235e77f5632d14d118a5d945d16d5e746e1eb590ee48c8c4b115e0db4425d9fed060d0454c12de49a3286ba04d108f46ef60398b0a01d4d81cda901dc3535078035713bdcf2464bf7fbbf60979ba38e56b56819d54eda71bea5050ff62f01090018ab992f54716b0ce9cf4cfd04aa02d2a24d9acce96b70c0f33e027926456aa2c56feeec0786a0b5bdc71eabd113c5de57c4dc380bbac940e5fa0f4846d63193d3610910199abdb5de31a6bbeb844536c63dd40e5762e49611740c6b20bb07a355cadab4f3e6afa6bd5a4e26fe7e3a4b7182259734fbcebbe10f07a39c35c131fa959011620086cb8e6e6df893d1f662c3b7965b1208bebe0fef06e6369a70cf3105a40a9992cc549c76bcbc2d1730dab35a45f74d0b1d1af6c90cf1ba0dcaa60ea4ef4f99f3c74ca1c230477c8516a7d1c6b0adfcfe406867a205d77955f719c01d72912930145322b99b9c6ea42552cebafe11e32e3cb4e76501b076a4c2abcabd7163b82f151afe805cd9f6fcd6279a725f5a7b1bc8f40afc0e4d79161a69f9f1abeff89755175ac2b9181d5fb3fce9dff3f3900c1f0368c601af76756f68d26bf81000746a902a3f29f8bcbae792fc4bdc2a93617998b5d800c2e9fafd045d1be34e3a0c3d069869ec6fb1bc72ab218057c87487722892820ac9d2fa690ff92d3bc9f044786a8aa869681dba9d35e2b1a0f3791144853b9b01dc3e7989004956fe7006322a6441ea91278b047fbb60f53c14fef0d1fd4ff1012aee2cf8126d802bd884dd69c572e24f14e9ec51a6adaa9e6337f51f97279b052081a5a77f81a7922608136d63c691d55d57c6460224336d959908f076c01e07579dac7ef0a2f230ec73ad36b166d78a1ac3bf93f97d92c48cf15521d5cfca0516d461f782031b7111def1bf3ea74bc48d05c9f429d485d0813f99589f265a04cf182e35d9662d41d45fc32ff30efa53e522a553699f67b6d44a255cdfd59b0cf595e64a788f02b819b5b7f770a23ad0c0c2cf8ae682806f950609cd275afa0edafacdf0541f0a9e34de2c45694824748251a7fc8709709bf724c50e2d5e530840b1e3540717c8b0960ae8dab3f4e03b6c953ef75afdef733be549438bb2bb079787c8336bd64529cf4a955d303686a02cc1e7692bd818700d233b45d6f99505cb35476fc0713f135bdb3046967fc35d5a6153ca42251d4a555e0c98c1c9460fffb885bac091549a07784ac54b36d6363cb87d1927b68d784e0305ee6f0454001ec86b70ccf5a3ff7fc39df00c688f1d961090eac88bdc5ecaf93adb82925609f4f0bf3b790354b66114c15786a39c5db9c025a432db5ecbcf2e25a81f0879022f8754139cf2112a302f2dd10016f424325eb93c0daf26f84d684c7316f2740b5f7745286eb77b4e661b14715337871e28594e66e0f956195ea8df63c5aa0f028ca6649bcc25f3a20b03c952f98ae3f87ded0dd6aa7e063ce8c438ec7b244d013ddeb464053d7ab1f5f2094758adf409e25e8d35bc6727db951bf3318ed8140c41a800076bfac65d2428c32c44ccc260e922af70ca9bf6e74e443bbaf23424026f9eabee1f90b8fcd940c143427edd422f833d4966821d89e7ac2658bf0f0502f9c941f5a30bee747e210db1109ec036427cc888dd01f424db72dfd75b89a702833b576c2a2f5c3f0533a610bb59ac1160ca3ec06c6ed48be1dfb6f362bbdc0ce75c66918d7e070fd3f16c514c1cba3fe5b57c0d86a78af5efb0f1c81e730d01831e97c8f267553d378cae46d59feec4069efcc2c8db1dcf9cd107d3c460eb051f312db016f4d39c38cdb33a64544e1b0f12e176d48038e535e5ea4ec6f5d903e1a28b72d91ab6fc99168c64f005dad6bb87cf58f8500c264e5310d2fcf328098e215864343345cfd01488009755b86902194d06ed06477499b11d8d016047013d3cb4a842bf80002b56e1e48583235fca1fed1a425dda8e166b392a2aa04e0a7437a6a0c6c6665aa0993ab0e99cdc98dcf8cf8fa1156ece9890f0af57c7630233fb1c24908250c27917ff954b2783edc73f49ee3c1b322438551544ca5558013d2401b93e7a0113c7328d8ad7805d8d8e4b0f4bfae461c924ebb3120408fe047b90625522cd8247c9b26a31b4232c5e451194d963e42332327ac2a2460a870654a71830ef9742f937264aa2f3c8fcbac62e5998c758e27bb6589e3e151c0e0b2c2398706e8e885a9441aae04684d64c53b8ea835ca1e0cda9f7173c86e9b20816680011d2453d3da2ad8940b22bb06cfacab10fdb45ecdd476a628dfd326902762f9a6b12dfa98897908b02148d4fd92dfaa6dd7d69815618ab3620b997790604cdeaaeec24d52e42dc011bf74f68be1af4c9b492ae87920246fd9ef2568b0248d26546b05004dabb8bcfc18a27d0522b54e5fcef83965e850301afc8687a018f56dea0a613e1388d1188c69ff24c37cf0a2d64762b114ad2735196f8b60005455c43e4360f6064cd2cca37b81de0d971810e0b2f11c73f21177b40313a97088c203f5b9548b84ea78f7fac58b84e5bdd965cc684585503172c9b98875b9f01629ffd774b2e7148fed785266071926aaac779317f238fc7914d0a964f63420e1b1d1022c4dec8aaf899fcd86ec4564325e04cec6e7c9fe825c7a6c1d1776804f9ec4f819cbb2885de00d9ff26ca840ec89da918d93b45163a5cbd2b53d33f00dc4412607c47bfa957b56492ada98116ca75ceed2522e3ae47993038b4c2ec0ce4307aae1737773dc33d168ef7b4d9c3c1900c61e3910fbec1f1077da83359096f177eefc603ff3b48f3e669726f3924a03ef9b971d7a0195d126d2dcef7f9065eec17a486ab73bb8e20c9adfffb8f75c7afdd53a8b3c8830007137c08896c07cee90f93b9e8de45d21eee95aa55d07d108c268f0f848fcd932e6b79735c6f00fa2a2110553f78a7e5d6d095348921d282e279b1faf0479e2ecf71f06c1c5805f4e4cb4130b6815bdd4be39f64eef77c0f41bfdcbaf612cffea23bef49a7d60d7ea3cf021d695a860ddb94166eeb200cc6e89af1a73cfc2e5653044d1ca3db0680659522c886ff47c59eff8d7b5073dea4c649deec92b3af23f166d98cfd550a1c35cb9b314e5c9975365ffa21e99e514700526f72fb7e3f95b4e477f28526068f8a7f58a80d45d76dbbd7db789a1561bf852eddd1b70714b96e3276f72d5c02cf5ce2da20cb45389900167ccf0b663b9b6b0e65384836f13cba4c937dcca5005a02c2ab50ea1a5e2e522a0197a7cf0cc8c1b80869b6ac0fa0328b31dd8b2e0e37dc410f7080d53af5f35f47efcd05f67e5feacae4fa329229e4322b03b4180e true -check_ring_signature 634aacaafb64ee53d6d1f41918de5e8f77bd8715bf07633cb497ff539abb2d23 09b2530248dc106fd13e2c5837dbee453614c3ee787230c7c46baa5d3f64b641 2 ab020f0efbcea8437bb434fbd3f803ca7000d2528803a54c7c0e34cc0f04d971 f7abe91cb0027846db5ac6b6a64b153b96b70830333a8501586b59c5d08e21df 3d9eb79aaf2bc90ec2f28b289927612f88a38a48f3bb740db5d5d2b673543906265ea260127bcbcb304396a0b68237ed3042c4fad2f655b9dc8123f4f12a8306ce9b517c997e1e01c1bc0213442ae3911b638cc906d6d2a547646f3f5ba80d0679498a7d12c9de9f6056da7069963443e019813e37835d8b991de6ebde9b6007 true -check_ring_signature 5ff6eeadace07009b06de18a7b1f1a52bab052f0f12ca1a309f1b3a2a41eab68 716e8578ee13828a4eeef26be96ffcddf3778e490ab1544d16dc5b2ea4a7eaa9 3 acd7be5c5516a435cccaa23ec5ed35243d68eb1ce6178d34bf11ddbfc42a42b1 153363bfb53b82d9c00e0d4bfc39ecfd8c9b4303ebc02dfdd8b0ce28835f699c 07c037a68d8956bf6a88daa7282f2432bf2458ab5e0cbc5a4a88f0f9a1803b2e 0ac4136c8ee9761504b4b397f03fd440f05b92dd0252db0c95adb545c41f1d0c2b3d7a9460cf3e8ec61fc52cfc31672c9a03b5c77cde0cc17b8c11526f71700b79f8ce45a5e459a401a126947935d8064eabe59a00f8802709bcf66d2ab2b405d3c62629c91017daa20d9b5799d7ac9902305a63122da21f57f944573d3f8d08f574e868e28fb449611c2def05438ff08272be2e9b182c3a7972bd7a1faec50887ae8d13eabffbc3dcc9d2381711860d482f6644d7a22531775f18a7e894a20e true -check_ring_signature 8de9712f7be23bd9a8c5d4c917d82bbf4bb3704b7ffb4e0ac671cbb792b6390d 5714b726fae39a7e6c5e9958bb46720c8c51247eeba4891934e67dbcd3acb048 4 5d3c14d793be0fa1edccf3d7ae1ae320d40116e0206347b8c033682bf2ec2e5a 51e5fd3255a7b0447c6f1e16fc50596a5002156d6edfb60c2822261b37443c01 ce959da6cf5b2594ba77a1f164da973fcd718b778b1de072adab957be9609467 9199bcf6dd5501e06348f48e821e21dcfb7f2aa6b33a47c6ef46774363a0befd 5fa3d59afe89be2a8b4ea8aead0b3860e5cac8edf0b7b0776de764392d52160c8531d28a5703c139ac2cf4ed8ec06e8dc0f81a4549290b134a5e67a4c80e38088572fd79f042b718cb9939c28c191e3c5091d64cc3ed80244d16e2ec8f180b04523bb3c6e3f232e69b609e9cf66acb49ac65dfe8823fd252beba85e461692709e8f67d4ce840a8397b01698b727df78a0b79929aa84e76c6adf2ed4d20a6ee0540aac64b477deb212e56f28918735268110ac977962749d131481a8d33c49905dc834be581a430e1c3fae7743d004c092803294cadf2385deeff6c80b23ca603ed03b747e22b2832866ee0ad5666ea1bd706f17881d743718cdc82de7da71b03 true -check_ring_signature f5fe4bc6b950f98ad864f82deb864110f2d395e399b62d9d9b9c89b063c4a535 c94f2b3388a9bdda02f7a2ca5c1254a6d2fb4e3c611681f83464578df932afc7 4 baf7c6e12264853d4ff8376f6cc2e2bcb66aa912577817636b2010f2157d6684 8bfcef9fc057e42fb1944d16e4942afeeb22ae30b5d890e6a14ac6e7c3ecb1a2 2adf439f1b090a3072109c77599834eef4159e2de317ed73c057cd1dbbb4195e f4c410e8bea7072adda30daec2ac239d8e6294d15add6378c5dd53d0e742c39c 1a2ac8f6022e4758f4967d458f0c238706c1c27afbe3bcb26c1ba9accd4d71054488e507250e91ebf819af68b93d464cbf1e436b0c71c46cc0adb39628c84a07d698274b398f915bd5636e980edf2786937fccfd89eaa3b7db1a77a63079490ae715c9903754ae604340fcd0b5ec4c8bfdb5edae1a08dec009e853e2a19179069a22c39a0353d983e60bd808d83ab971e0ccef64ada39fa883245390c3eccb00baa37568870e96eae60a611417db31761612509ed2563975c9fd69260793ff053f7a159e2d79d1d0b521003067cd1e4fc7b625b15f4c91b3c7b1290c1e3edb022ad5f42a16c1be85343cdd63e6b94e818f847548c369d17f300aa48bddd45a0e false -check_ring_signature b80ef40f254ad7f3cb114f717a62252d99eb4f3f7d11fb038eb7b55b3447345c 746199984cd9bae7ae1f8b2656eaf00ed77b81d18aa50db7eb2497c9bc35f9fd 61 c7c32bc2f111768e7c8440cb978c222cdac2688861e7cf72788ea14e4b1b65b9 7e2e88e0fe53922711345bde2954b1bc4ba6215a1fc43c70ba4f2f16ba738c2e 7bba81b6db0bfe3e92fedca1f33e5f08589a817eca505f02580b0668166eced7 f1aad9c1219e053c1b8abcdd7bf7ae385659c41bcd93c49e31df3c963c75cad0 05a14b30272f7fbd0d4c7a48f8315a0a5e88e84f25047780f3784ee995c35973 f6b168301cb8a55e79a9c155a42253fea62e423961c0c4270b752b35acd3561e b61946322038361116edad13dba531e8bd58b7bae05356f1978662df88a02628 1e9fd0212ab067e0295e1ed0e60706545025444e5a10f150cd92ec3a6fb69189 c0f9359d6efc181e4342ef294bd2290acde877083d2ea3cf3389f4aded74a102 3b92df93c46bf5cc42989fe18efc87793ca3c2c3f2ae7805d6c68c3bc403b844 900c982f144d91d9fa17cc4f9c3115c6bb7d0c371f11c89828e11fccddc37b07 8467ad31c1cb09fbe50c41e33f1913d69a7343ccf3020b6a1589e6ab90181ab2 7fbd8d3ba5858c6aba5561aace0a00f6c994333111ca7859e880b1587015070f c177e6b462bcf04e8292e9f2f9573ccefe8b9eacb35797f139d54aa177dd65df c5cceb4b25b8955c7a1aa0329d7c9060559d876a00c6dc26283b29f352032806 c0264b5d88610c3c84e231da1319b339fc416790dced2cb7b366db7a3e77aa29 0646f7b89608aab866cc2f137f13feb22eca22100eaf46e128223f916b750a4b c7a8a170da504f594b6be9a6918f190dedcec0f210384c56cad1cd7e04040d24 27053c0fb3648562700488d117203e1c9989847f699199c1301b857c223cfdad d1fe75d6c54aecd38a11d2a0084c46cd164058fb1c70498ba08bd6f143c89157 1ecce3632a424c8bfb0ebda80d5284784127cbbc3859e35f3379154680c5befa 21a21ebf6f25f4266766f35c6ed5bdb8257dc89d7428e797e1483376a6f4bb8b 50765776c5e806020f29b2fd4c905b1c73e4d9b704b2a9a4e17cd651fecb2905 5cc26a569f320b8cbabc1e08caebdb7438e458df83ee5bc317704f8420e075ed 20d04aa9efaccc337d4f9dab0687d5b9c0638303a7c8f820b7c279496f54f041 817d8b2c65389df7fcc31a7c3a43d2b8a9f90e73437ae71d0da32254f86ae152 6982732d31dd466df304f9417822056c3c3a36bc6ae4fe47f021f3e587678bdf 3ba4af1968a7798cb09296044717af8ba7ad169940ccb275554bb0dbc24c8e7b e21043be77f20c8353696f2d3a72fefec2d78f941e72dd271662bc8f64fdb456 b679a2d1d326d0b2579fc443fcde565e9756b86fb3686cdca84595e47a278699 268f17e4e318fdc3df24e0e97ceced35e02d0c210b21a9eb5e389b557757013a 1ca02ecf8c87b22f572ba4048e9c9d62360354310413637696a2576fd1398ba1 e822b7965f8ba496c8024858d7f3a7f73aacd60daf6fc411ce23a684a96f2359 ed8a6f1f1ceacd48898a54bfdf95814ea97de694730de265728048c5dc1e86fb aa88dd95d2ab57819457971035163b5118074e937e92577c89959d33896fb14c d36b82c998c68823313b24d59b711058036802e531d0ccf25dcf0a52285581d9 31e2d24ac393c770f17905b5838bbfc9a8913fcce839410abc308ab71c2424a9 10f7fbf5a4d8448acf59f21877ad0c9791cc6e213ca70d035da5388ff75faa16 5cfbd8d39b9c103f6063742ef58bcc7d5d78b4962cbd24899d61c339219b4dde 17aaca71b97181a88f55ad9f09fb56d37d75b0aa05ea3b17a7ff2e08d352aa42 4ad233cf58fd47692f14a4c255bfee3ed7717700c93ab5224e82a761fb5b41b1 278f67d5012eca5d81423b52c930773bc65b1bfe97b93fd74ea255af4acc5b06 cef9f3cf7837451e09472e5b8d2c300b065f1529ffd2d0b58723787bbe8068b0 7a3f4cc0064f5bb47e0fdd6a49da9dc8e9f287fe5b8b3d4a085449fbb5237aba f030657c0aa9ab4569845e9622c0a7137475b07bebfc76820ce2cb0432cca680 9a6c273948af2333791ad5052cc7c9c6e6ae6a529d8bceb20a7ae5d187c372f7 ac152f435a0693fc11077fa5b4fa21b8ed1c4bb5d006668413ea5ae023e5baf7 580e009c4b9369b2051ecbb2324c0e06a5b828d9e839f1e21291366e802f901b 461566afb2c234839f7edcbe82c80510af94a93bed7be23d56b3ddce3d6dc4d0 b339d6946f06e3ff6633bd9dfd9e833024b3d8cd23ff2a2f5c0d99178641f3cc 0c38a35333d5bd74b17001a48502bfaa9878e78ed0ee3fe8011fb41f7f8ca541 b99fbc02bda3ce2e7fb13cb1cd4e7a86cd40d2c9b96b14fdad3e139b66759d8f 2f1d6851bb84b81257478bd7c440af0986f3308dd8908e90b375cd433aac43f2 66efdff5beebe3e655eaf31bedb13f1977f0122f474f0cf6a1d19d7bca0b7c01 4583273083587cf6f5e37f0eb3844ff3c2b4c3bddbd1687c46f37a8c15ac2451 34d9c15135f4a32dc7b765053ca51e56f2d8937769ccb19bb80b45e09aef9dbb e38cac5d778c035e32defbc643e15957c9a9aa8405c10e93a4703bfb8df28335 4d4be05c01990d2c89a4c91e6127ea4b997a40407cf2545c05df5e305bc8d7be 4b36fee013c9b90acb9cc90942d43050f8ddfa4b2d911937b2fb32e14ea9762e efffdebcd4774fdab34f75fbce0ecb1b9d9dc082e4b1830b2f313e783c48cb55 6ab226de79828b6d70228df21c6294fb0f036802e3d6179d6c31eb6f4f40915c ab1b790dafafb84e5bd7a9b701d7e3c66012c60586defea58975dcb37c5cf80740a56df5f162a8f130032e8e829e6130d6e484243beb6d26f92944f6a118230c44545941e61155c370e5b194d94f30643585cff2d55dffbee1b3c7c4b632ed008b025c7bb6d395393256a1f6345bf416c607a7d748c5a4dacd0b8daeb8b8b00cfe7587d32874819f351b9f71bf7f60ef175220473c7d9fc801844604f2e2ca07c7eec698ea909330864d46acd68495ac3518a29fa806882d4364eb88d4331508c351b91325d61183b89104481629833ebd34c10559875715d9742c17dd57a80ff35513b4897839cd110a93ba97ab14ac26d855ea8acfb7c7c4c864ddbeb13a08311185f33500b2f9387ad7adfc9a3541fc7f2d0054ed6d34271baa9c521005009ffb95ec83d7e4922091c15876014e5ca0c810278ec65a71d4f89284fe93b2051853d53bd95d302301b8b285d8c31e3855775df1927c0074bf0e3711bf59ab06a90fbca139eb0fac14f17c6b1a814b11719a9b5bfb05472503e55a11f38b560b35312fdae16f3521a1f81cac6d16017d8195efe7b91b27a89c4dea008e4a8f030f0d20747a7f97e272619d130e6306ec7b3115a4f9d82cf9cedef1e1747d980944432144e8dd14711269eedfa080bbae0838ad7a3ffab447d8050456171a2400f001dc65630d2bbf4e322cddb84df28b110e2411965f8c5002b9836453d29a0175cbe35e9f591ace3a3f63f5cb3172913189594a49235254a4c328d26665ea010008e00b494e6b2ff8392d1c880db7269d65ac1b9302d061486f3e0343b87c0659c0b40eaf9cec3e123058e0c48cb4b316e09fe499a5c00ba414172cf397c70fd909c25bb7d9633dc8097d37270833593807fb17255610f20a85850b0553ab0c7369102c6007278dea4b6677299775c220a711dc7bec6ba40edcd3e1a3ba730cb8982ef9a17ea66f254a0ec195ec5db06768ceace787cfe5964a10f7a645a201bd38e3caf92e0d18410bc1f198e6451af92fef50567e6ad2a56c63bac8df630684cfc382a4e5f18ba6c21f93e66626326ffd917a632829df9b75c5bad5dfc00b905661f9cf5560418c66cb768b958eeecbfae5ae0f116faf372a9fbde94b09010ea87330a4f122f38b2e9a1b63c62e0b63029ce0a5df7cac8beb4356fc55f80bde93ce6fbc4fad6275619a39181f0518821375996ccd4852c6decf0e7c90b207179e3f28431c3fa86ec61dd621e1de3019dc1659ad26c8e199a7efc0a9608409f8af84e3e33b0a13e7c45768a9c830545822be81ca525be8dbc40dee38cad40492f42df2d17fea05bd494e349e4cde8200a97df2473ab33f692891da41c1b20918b77126047bb9d19dbc0d4e602581c8f5d3284215c2db0d5c3e2c7aa37a2b0e47c18836abfb2e3d1c1cee4245056341f9d8400fffad73a48a499a4618551a084087e9bbab20ada6c4cdda5ba7258bb13863f53bf420b2328cdb23ddd229700c76118410d5631a39114d46410423cc74235129b4b4a74c9a61346ea83e9233058072f48c7f5a23a2ad84378f89c42ce5939a51c2add50ef402f5285bd6537706b5c29485040086cb3e94130908f1295f174bbf6ab2069bd24dd2cb1baec43903d3db8221191574a68b3a56e6c6eb159d3cfdbf27afcd73d241df51ac7e0172053d6f7b850a6c453c82acbbe525d576df2417d65fbda975e1fc087be92208ba0ae665496cbc28b2fdc9243332d42a45f52e0f86b58a12ae8d8eec963a54f15001d6622cae053dc2f0a5e96aedb22b8e4769f970cca4fee24a49fc31133f2fcd02e5e292d0c88f09b38d3c6d1a27a2c0aad15fd09b37f098bb9fa94ea7a2ec0607de75a9ca6c471c15185f8ec04e491f427015313d921691cb5aee76464f69c6065bd1042fbe4d48ebba76e5b2600b4c9ecf0d710148f2c7bf50d8dd6564974a0107a7c0d69d48c1211aad1fa815d17000e7aeb2a20f3a8741658b8e3d25d28103919b9ada17f3bd0e70e6ca18d10bdb913be8edde59ed87e341c33a2867e4f50aa35f604db64df2d150166306aaa43926a8f1805031809e3d80ba7c3000f3140c03282f56bee8a558afc76053f97dfc55a993ede9102575c469b7cc491f7c480eb8a971f33a19e581fc2dc2a099364b08067f08066ddd51a2132f38ef214c3b05b53c753f215a72ef47d6a1eafd8e19f04479b578638d1f441015dfade73bb6043644166ec09d5b16c949f7b41c3aa95d8e11703fba17cb79427349c382cf640e5ddc8a4785914c3bc60bca398668f1dbaaf3aaeabc61a4e3971b4210c4b6b403936f67b61264ce1ce40c9975d87003b50feef07b60114e1e1f2b1bcd354670042409ebc87f2ce74a320f31f445969b61b2552a1d8b551ceefbc21b0fd0b3fb027ffd87e117662058d342dd8a6d78a9659e974e461a7076fad67d5eff98e8d307f10d1d368b006f37f1c13651a5fd7c9f556aec8ae952062ab6fe24e418f44d0ff854530328c1460da4b765ba3e0b5dcffb116b99ad276d8b4bfcb4d03bf8ca03a96798439843434125d890db8ad0aae1eb5485633399c3cbdc39d86bae84ec0a27fccec453bd5adae9603d2ab2116b938fafba170c2611a139a8b8c54825bbc55d35b1dfbd702fb57051b69919e5db8b7b453a0062346ca4c14ea52192e8890ed9afdc9e73e2b521e29a2c40527af740e70e7452ea1cc855ab73df6576bb5907b1c028380c0e092d75a467bab21667de228f74fcdecf6d68aa585664e598300bed8e4f8f102b4875e19018e97fff598f32f658a5afdc9ee9d156361016ae1f0b0f1072be9e06cd97ad5a80e5aa7cb97c998d48963d1088646b36b15850aa870ca5cbb4e89824e0340de3427cd187bd6f4177145f324a81012c7aa781396996051ec4fd158e8f2531c1696f3bf845dbe2005ac3c6f48efb20bae972070891ad0b485091cd44ecc3ff23f9305e5af867ad8f075372b0d646206e20b88aed0c770a4d8499918e04767bbdf99cd71c5106df84e6d919f0f2d17a7b0a368b1971750d0d9ee42e892ae22dfff3c6443eb3e40454455195333ec25ee055092cc844de088408c440427b32428f2a9909ab9b51129b048868ec882f4c9c078029907a420183b8efc7e03ae7cc9ed48a96e86a1c0c3388c9d6471405c867adb7012ee2d7065ca5d6a8cd83bb1d5a24a34f11c4872049629c587a4d27d9a42a5c39c2720c05a8a065e0b6a04cdd6eb4e08a994745fe87c3301f10b56661db0f0446998d840cdf974d41bb688d52713b3401c7708ea51ab464a1970a0378f366e1a37796850d395a7df7951e26f9b024db709cbd358c859518d539e92451ad932944cde5710af4ba4232471483cec597e8b987219fb9e179d7e479f39f951cce700e2b3e3107eb97766fd3ac6f8402e32a7adc839a51c2d5095cb987b84db9759e80c0d09e01151299a12e33a2f52448923d78746f92b65459161a24a7678cc5309cd8fe180b185ed0067dd2f6fffd96ad1260c3363ed317345b11fc8eea3c43ecf2b1e2cc0b483460c6862ab40dcc23a11fdb8e9ab0cc5104d4d3ba641fd062f4b8edb21d036cf902455041f007348fdd5673374af7f51581c485ba5a9dfabdfc9b3b257a083df86d1525acb935af6367aa4e414d7c8743ddadb80b02440dc0d529a758000eb8d9a07655eb9c433094be66514e6af8fddc58bf20b5710c9d9b3ec216c8430048c0a04fac328cacb469418af97434652fd6bc12e7ce39228121820d5f492c03836e92a7cff4b2f3dca8a940e0c468db81360f2ac8dc909d20c8e78d1e8f140c3e9e71d5c13c80ae28dd839d5d987f5ea3648c1384b3a5e7bacb83b16703150aea8a5b780dd1c102533161cacee7cb49f94210690324948734ebd3e771101b0a67fcf861d56a8a764e6a25d4c985ab2b6ae4e6f5c545c51f6bef6c41158b250f731e93bdd9e9ec40be08b11861d96206be0bfce49f400c889c7a017c2f32dc093636071caefc271655526263cf207c759702f2d27c8b433ff06239e76bf185084ed7b2cef34c9c5486768ba105088c525ff19b9509fb321c59d2be55254498070b376b14c310a804adb6dc11a98aba7aa71f27981d3d9552762d935908de740132e8ef6b470c1aaa56be1a18ff15be94996e14f2acfbf5acca01fba610dd9b048b1a80bbf2a497921eb47ee6a9b29d77c95f0d207a0e136074c7bced23bae70ad1daab0a82eeb09dc49f49b44ab9799e7a2ba14286254db3eefe50bcc0b16403818c4a31343c5d1f79ef37e8b19fa3ce3d78f81967bd65e138c58628017aee0f43eb70eaeba79e14bf5aa29f07c98bc7b0e2d42095684a3268f1956d07729e06e804c0d61db6daed6e25da4cd9820f275c9d0154640689d183a88a8a2f1ec904d0bb0439b5a023201ef7d35a4165065975876c4c4257f0a2c6a055dad1efdb0df2a1fbb281f0aa35b16bd9fb97b3b7f39808b3d07365e33c8eca9e81d1387e00f5d3835f8df3f25738e0ecba3700d6004cf86875c341cafe3659871c32dc6d0ae31d95191a498181e67f9ed9dbde35987aee51658967187dbee99720a72b8600259281b0037b7915bd3a373f295d97f8c69f5441e59b9327f53e9814580a470991145879194d1c21404986c2f2716fbfc348438c96620e75479b34cd15da4d0780ac9bc9c34741ffb1c1d4c20ebbf8865ba6a1a60eb82a0ab749e281da77b208b375871469ae5d192bca24081b82e40c07445a307511d76cfad5144d239cad05e44844f53ff499c81e2fdb10632367e5d6d0124f73dffbdd5e00fcc76d3b710fa8a933e75f304238ecd093c7e77e6d58c6795ca37608bde2071fb04dd312c008b0febd217c28a19ee1caedfd71b7195f099463eb1159d8477fac8703ed1e1f009b7b41d645332a18b3112861575b48e985758d1c1b8f63a0ba933ca00c0b0803e552e26787069e6b4b90ea60c11557fec175736e3fcd256b02c685613952dd04babd439d9e9898428db96303d8236dc47ade578e51688e83f31b46254cef3706897a633f96136f8aa0a6cefe1a5238c1a85b36c9fad6967df6cf4d2a0a22d909164a51ebe1e5876349f9c8c58ed773a369ed0eb297f2908031358fd182e44f038e817344e74b2510598de9879c455baaae9a72570a8cdd78c3ed16aadc5a3600c24d2ae831281b9d56c0a9eb1da0e11aac8405cf9eeccbec81bdc3d7ca71840e191b5c8a569b7fe80a07e1f4bcb1a168af0d5af4615796b40a8f9fd005d9ad075ce2e9362a2f6ac08bae6d4606643393bab8bf23421e498e5be1aef6c7104a03c3a268ed93adfde4a57741955f5a79a5378622d82a59a4ded06a654fea75a40683d02334583b8a92e2505a1872e8467a904893df1dafca8450bed4cf2d67f8076f55df2da764d788f8c9cb416f7311e0757e2f06bd8759ae1391134b017c9c0b7e467e01dc5ee67b8751a25574e7614cfb689014a73c7e3004112a2a66259a07da3b8ab61088c0c6701453981a016b0a1a2910e55557f19746cb4c6adcca9b06 false -check_ring_signature d3b4ce61d1061b137622806d7ba34406d4d75cd6d702023b878cf10bb3aea421 392dbaaf211db262c97b32021f697cab9bab473db991f8190ad80c26f5c3efda 17 c8c25f89f40c962f49c05bb99227fe0cb3fa7ff7e640bd29be0e64472e5d0395 1867460d6092b97737c5e0893bab2d65fd388d7e8898518f0b84d404471504bc 1146a8716bb6bc9d47074dd2748207d399b8e80c533149c01e38603fde6d0b8b 6fdc7b40b3df790d61128e84128ea289a6c84ad88497831529654919f4dae1aa f5672c94bf6bab41933f6f4174f363a28ca2b258fd2a237e3ff97aea9ef375e7 bb80d6c294560e3dd9d525bf61320255cd7dc69ff163bd3462db8655a160c97a 91a80e7932cfb632a98a5cc76995e515fdf483d62821f4380475005dd4ec220a f9f62e73a1f66ab81ddb5deed2a487a32b557d8ed025f7948db2afd4c893ba48 e9d213c96ee00b3b915597b7cd5c6e7ca8bcf09aa4effff3063b47b6343f3c28 1c21c3b2e507a0c9ff7f3bf48e72855d3a9af8abd7c08c003f8c617b851c84d6 bb8ae623a42223dc1463c2657e7874dbbbd652ef207755baa2e799a3de8a0883 6266eca73b6ffe2bed366dc1affdd10e35a72fd1d6e96dfc0780dfe31ddfe26f 6ad423dbe191662b7fce99f0e7a7f1035a4945eb286801f4fc1561ee41640742 7be36751a3c74e601f7666a8111eb8c9c9aef52f650488857800022a862928db 5f45254cba8cd88bbda16bb49684e118c6a474e40e7755012f3f7588325503db 812d15185506cfad953d2456e914502022c84ae02794451a8177503f8ee47f51 f1b66bb1985b20b66f4093b2b12aaf9eaacb95c00c58dbfb71e1e43ea24efd72 fe1e96a3479fc3026816fb3572c510e19a9ddf0aaf6f20fb8b9c42828548b005de6720737ea833daaad3298a8bf8237b7f15dc25c027c90394db32f8b40ae70626c4c0f273ba0b358ac69bd6c1f3398e0e5709e7c900a6ec4fca3bc768a14b042efab7e119573511a3c3fdb3a1c446c1ec063d8919e2c27bb214ed4d7e70760460d6c3beaa670ce47ee3d742d1d4b229d7bd292de76237ebd65c0025840ee60c5fde731bcb150e9c130f509585a4e019cc60a87736735089c20399cb7f12eb000f85ecf7d4f3571e673ce6d414793b093125a782ddbfa4047fc81599d1309e0c20674a78745c041a1bfbe0c23c0e913d64648d127ebd32bde3bc1ef3ffef920514ff1c49e3ee3d80407bd4c3f0c3ad8ae5d194a086a8f8c5c25ac5163b12e90ae3cfd996b01c7cdc98be7aada437241ce0e7f7dcca542a5bb1c8b0b3895a760b827091dcdde50357cd3d7564b272a02d44ec7813aa8960fa14aa4ca371736c0a5e742543727baeef81ca6de99cd9ad91684a04896050469f9021d2a169327a009cb2f2d4367c6d44bc3b137736150bf752ad7cb6c8e63c5ec41ec98d93d00c07023fd8c2d3c5355b195219bb0d197b83291c4543d11257d6408ac0107480be0b1e06321f7ec20ae5378ad790bb0c2e85e7db2f9b22f4e25f3032b6caf53ec506b8b3f6cbdb201eb40a3bc7652cd45f37f5aef7b5b7da90a332a8201d2d10de0b22464d07f3de75b71a994062fc8448c6c74203e5482fac4881540e2bb6de560e9ca6f14b432da7a0677a4213eb986f42af1ee99ef9d0b4697e910ed8502c1b07b102fcd0475f5b89368263ed2ea1615d97446fb6987b37c79f9120dec526610128a445aa2ed5e571370a2ceb33ad4deaf98708f52add1c9650b07f9d8a38fb033f907036092035d3ef1b9aaf6e64c5bf4293facc5b0891023167e5628d1b8108c338053e4c35fe50bf27145f0d428a529793a6103f0f86b1ff790849f113000fb71959de4d1170547a9e0fa4c6ddef28433f557f5b460c4e9986964d9f0ba900d012435f770e446789ccf1e77d72e0e9a3fe3204e7809237a65e3d491ed87e0e21c8b742189f719dacd69eb1a3f412796227e6847875141ddabd5bb56c754b0fe4ac8a2e4c7530dee7d2705faaae534c0fde75583c300fc0f30da98a422b470434e06616c0a971aec267721b44e189dddbecdd9e32cc030c1abe7a0de6f4f407f872aeb21b57af7ccc0f2a9bfdcd6f0367451e3791285153eccb53021f63970ee55720f4ffcc7567c9a44d3ad485670f3eff07b7f219de6be0590f385564c50230471400d9dcb0aec051874de8750ddb554c8fab0717da4097e4a7d7a025180e42786733d793fffdafed606db0138bd3462792cbb9723157607e6622a86ec7046114cda5c0dabf1a041b81831905f9f4b808f2bf4dfcf127520ab0de3a024d013c08e06afecf0ea6e1a8afbcc158bc1462d309cd46497a57e6895af269c23a0d546aedf293e16fb8626b84b4bd345cb967b9e946304ad4d2b55e948bdbfb0507 true -check_ring_signature 3d84e70b66afe2ceaef00ae914c6dce42cb216905ed3dd8380b7b855d682b0e9 65986f2e9e6c279f71de26152659cde58cf5dc2b9d8b573806b57024277bd9a5 28 4eb1c0093a9e4f488397c680e4824fa89a191c7a2b8feb883437e209fb78791c cfea05fe0abe49061e7eef62bff017a08e30d626ae0aa24e161e7cb35cb0e09f 789b786e0ae915c684e445a445f32b3aa696ad199f1ba352ab6e4fc28517ceeb fee8bcb98edf795b9e24a2b813ff97561a8f19945ad2c34981fef5986382b0e9 995a90ac133ff2701641b21591f6ff3aec49829f05aba658f0c02dfa5a544e17 2caef8554958aa5106c261eab9567c8a0c389a6892eb5543077508d9fb88c55b 3186d1c8149584b21eb9bfdbd4960f93b9537d028db5b07153634180c79e7039 00c400053c0835898b346ece998de23706a75f8446dde6abb428f69011c3254e 8d8e8c5444afd059f8a21fe2e34df1d2ea6aaa9ca3e91d6c27d8849779094f11 7fd1303606cea1a99af2f3c02056f755ff3803e192cf441c468a5d58e2c4da8d 4a5c9d649820dcc21bf95554db033bdaafa303fbbbb926431bf658e5ac6e90cc f97f76e75f2fec1dbdccf96579e874ccd59b037a277d3a42fb3fe4a14370ab95 71b4164127e90bf8cc9cb949f6ddbb5f0eba7a7ab9736e7a1dbc3618f258a13e 9d44d99e9e967214f763a388e23c63684d72182578e64342231ee16ac701ca29 9ce1a9e6b0a155e97213f38dbf201f21bf0f875baccd680e230fe76672b56ab0 14c29b5c86c8620ec1f5467493589010daaee2258b72a1d2183a826cd764dba6 4f02dfb923a82d1cac98f64e5b9c2705955786a6766049af5133dcb5bbd49fc8 d94b304555c984a8c835d9a400fa360ef605d30053dc394189a2ec68d43b930a 17a4ddde943e487597a2fb0b6367529d470f0bb7f4d68f6b2f1b3da46389abc2 d94a4afbf28dcf0796bc3e44c630097f5d689a325c4e38c550baa0ea23afc269 af263504ad6d9ebb5a2a0cca4064a8de1f377c28d374610225b192845f08d12e 94ac3427e93ba33565900400417425e57968716abd9243761547bba0f1aa66b1 e02769e98bc9ad4917f590f6bb53a3c6f707bb89bb7319fdf096ab083fb5f61b 0249192bfc1985c0c6859bcc850c3d80f9fad169c29dd74c98830085f5c8f3c1 e79918b2b7fd6ecb0e2b4957eafd113fc966e57c59e6d8298a2e405781121ad2 b472f7d4b5135074a9b359885ce58be1cfd2097dec293f96e7937d8d19eed28e 474f5a0327a8c07cf74add072c2e3b3b4fa1384b719f3d7bb1adb15e7f18c001 07152a40b2e33406302b966ecc7573d69e259b1077561cec1eb7bfd594e6c58e e53a2f9fb5760d305326f87954283fd700476df30f4d0d71dccd623bbe4ea508544b5fa2cfd937e7bded7708fde625159c7da0ec1467295c76f2896d1f3cc70afd079ec56c3dff8b44e813a31721c56e2991b3e3f8c32c55fce7f94aec1f0404c7305ab0a1ae025ae8e904f9785bdb64337ba8cfde7cab35ea62a30390547e0cb2bdf28902f9ce20747d0d757d8a5b9eeb511ad673f88a41a54055f2dd490903384f2eac963f5f4682a7a7b8074d8e9559801455ef6ca72c6d2cbc032dec72045c4cfaee35fe4fc233df449e434c7df0af26196c2c65e3e6ea174dd6117ac90ab7bdcc2129bed2ab6c718f61bce2bd7e90c7bf1807868318b4b082ed41ad9e0a468b448145dabccc0beb443a37ac4f750a852d0d501e0b4d8d93aaa683e65c0ad808f6464082ec9f1c5802c25a4a702a42a79d10c4930a3855107f227e27c808b1be84495c46bead28e6615da0c1c08b0a34c81885d6c5a5e5b6a9697873ec036112386a1aae51febdb9156d5e9469126259ed935c9eaadbcdcbbb4662b5af01820cb8668a5a5db4accc8763e03f9e3c778659519931bce02e08ebd88e807302c5b29c39f324aea0fffbf95e339a51b99aa08b5fa7bb8aa1316ba85e99ab87018d65e64c3ae38a66c736a633ea665ec1c32ca85be302a39cb822d83dd3c40e0faa2b22bb5d0f4db412c24c2a1b728429a590328f5b038620ccdd676cef05ca00540848025ec7651e9795592958ae4c5d9d130a7bd672694143c5cd4ec21ae304126a698d945a55d1f9a9f060af354187d43c5c386fb7344d320b377e09cc8d0c0bc0124e0dead2ab40146ffc336d72f3c2f3aa2b76fe5564329a15507f44ad082cd0218b2e1f7c242bc9e06b916400fd0afaea4daed690dd1ce7ad44fc5de80a1d4f06c2e5fab46319685764273929da38441e3bb69e38b49dd71299fe272d0c5399e5a77c2db727bf756e7782fa5c223bf3accc4194f819d9349d23f03ae801ae9c05bd664ec9de7e5b8f9a0916be6762897c5ea924ad9dd55161ed89d6f806b62bcfe8dfd6e2d5d3eae5c70628472f03ce361f6067e3e7e3515592d306260c84594996f571ad459408cfef7e5c16c50ff1af425f0524358c82677dbe81fd0a1aebee3f3b2a89da6786142e216dcc2dad8a969a7e9cd833e8e610dd2eebed007e50969f0f52cdc435eace3c4f1e6cd3d390d2cac24291196faa95ebc593bd0a26bda9a9384d7b18332786bda7bc7aa4324409436f894f8b806534cd00406f07f4a324bd3f75049a2f6510afa475766e7f64d2bf7744e9e1ca18a5ac06dfd103b7f22b54f75784928d337bcb4cb5bc1fd8a8accc067ee9b9c3924f54ce7e9504d84e140031cc73303c3fc2de486b66088deecfcfeeb79ccbe00a38b72201160a1c792561e6a98fdd1210b57820209819c0d0046077f01e3d4e38622c2da8b40bdce4b36fe17ad9983a7f571dddb2e380b92072497fad05eafc9ca4f90249f608ab9314942ae6a6f7ff64945cb1d24e1fc227454b20553d7aaf5a3e7077046a0e3216076eff94e3f6aaf72b9f6f114f2f1503ebdcaae0238f23cf546019caad026f560c96ab037aeb530bb6a0c8d657a24c15b699eaaad5353b0b725cdde0380bb74dea1d8dce5ffbe34ca558c7327fa8222074b3225525faff38a7469c4e4002314dc27f26ccfd56c33191d1c34a263ac10d257f3646e70983a24aed25e8d306813c9ab95ad55865da41d9e794e6ad6603f82074e4921bcf7c1aa97cf768d00d923f8f0c918c8cd739801b65467bb38d46878ed6dcf47977127536780fa9a50a7b92b194acffbed7efdbc8dbe786ebfd90a2177fdcdc7fa0868ec6d08d5889025e12cf5065303171d5d0c227d89015b3ccba92f0ea5ea6b047ac31f3ba7dc406cc022e44eb15981aa8e965afaa78d8537666befe028f49e037718c7fe298790f42b78df09a8e7dd745e1859ce2806cf07510ec523e4f1023bff5eea1e4ba3802553f6267a1ec063e42650108782b600970366024a706aabda08a5cbae73c940c3da082e102f20cc04fe8acf37547b44f0ee9d4e14240f7521a77e5f19348230ed2e1d9714225fab07ebf0b41f1451937da42c4a237e5dbf2951325d0683fd7009fcd36445b46764557c9253b73ed69e4a706b8396eabb9e9ab2534a9b9ef78025414a8fa62188786abb52c19c3460767130b0bf6a5294b779091f8b433d55f07b9ff4e018346bc20e6639c5940a1af69e72a09b9e94b6dbcf6a9c52e097603d3377b189118beff3fc366d6427edc3a190976f4416c86fb52c9bb9adc5c4b960175cae5adea8e6265a20cc5cff11b1f1eafbc94fc9b8dc53317a33a385c3c6b07654b551ce08856615b251d229b2595a70e67f7e3bd19a4341cbfba66f1e5f10a0f0b0e5cc93b391dc3fa51866651841ccd40e8fc432549bfe49518e9d8f40908a4241c91357dfd5b71c61a338f1d7a733303bf3cca4b11ee1d0230df01f70d06043b2f3e487ee11dbd3431d1d0cdd53dfeabf547c71133c2c620566a4294a508 false -check_ring_signature 08c3034a183b46f4dcfb0ca9cc01964ed00da66c9a99c9e58f9a00b3a59bae84 7b414e2641ce7571f8df8261bdbc97b207a9d70d8c2984c1e67cac599206c164 1 b2023f5267932cb9342897eb00bd45bf0ccd2959f0f2c060d1aedf4896145220 dfc52e7fc24562e530eb58723d885da9bc384e17906abcfc951965f48f937007010695eda725ee9edea80b04dd1d6b859efe01ab0bce013ae16782486ce50101 true -check_ring_signature 79d032a81d8f72f9df365acc75e30afc58949af86ad68b02541a66ad2f57eee6 ac4df8a6083a18d538b4f33ed7ee54e453e5f211afb266b6b360e69a0eb34477 2 0903fbfe755718f87cd159e809f6fb601ea4ed3dc21a70caad4d9ac186bcc92c f53afc0fbef7ed3596262051e1f92f79f38763b5985b52649273de9349cf2ba8 591d840180b104702d8a1418eb6933d33b9575aee3bfc34830fb60f78bd1e20825defadd3ac9c5fc3908a7448efc50f488d7aff3ffa7c3e9819b3cb3e2f7650441786a21dbd981aa209ec2256d654c25a876ac265a9e3cc4ddb75efdaf898b05e2135b8ab8484a7b947d7a8068257e77638b06c604f7e4a8495bac22395ef402 false -check_ring_signature 3c2fc62f28061446811cba44addbe3d9c9722bc0084bf494118a77155e73738b 85d15048e9cd5f3b160dbd3fc46cfeea482c5f6a3487c456292d94bb74c0cbc1 4 2bfe328b72b6c30ff7db8208215abee2979e04f891ffe43a425be12b854d8161 b6cdf64a94678b5e4e55d027df4400ebc5e03ec983bfa05f240a18422585a9cd b03b99572f79db5fdd03fdd639d606fee50332cfe16ec9423b03d04641c677a8 6690c7a7000ddb6fc52ba68ed6f66b82e6d184408de7c7e11e395a9231589202 a3e69b6c912a3a0bba2b2433b798630fd921077760bad66e0d57e843b3238ca267420bf0113567bd43722815433dd7e6be7415b43f3d0623f52f9d534237890b576bffa17a6a1949f6285cfff86b2592b8f3d2a148e5d4c9258337a741449a0a6f5a232e6f3d220074bea49127423da8ed4eb38d0b563ed449af824d3daba25c51c77e603aec730a52ce791cfcdf3b834dcde6eea949a767f4a28de9243390078a8e0ab20bc7138c9f44f172d27dcb6b9d8da4491d98217201d0381e4f3a7e0f090ffc1a116849bb464e61eb4324e6daa9f380ee33ca6ff0b1c19992eaaad50c33062f0dd79817713836d9f2a435032c61e82b25aa06aa01171c6650f087f308 false -check_ring_signature c04c40c151f5bd520c088ad6c2b606f9600c0e3244cdfe7de9a689df202115e4 d3a4cdda6b6408bcbc7580b72754f5f87c6ea7320c9a720a7547dc0ae0992a2b 1 667a8c67cbffff6e8456ac87f5ecf55f4ee23ecd83bddacf2c87189cb62159c6 5d016dbb72c72c547b6d08a5085a4816525933940a7a491164c842864b567301db541f6ba77d672c614f4f970eaf1280e382d3246acdc4e4c7310dfcad19b203 true -check_ring_signature 931c052fbff22b5010803fea822d241fa357b3259198521fab11e92fe33cf130 49a241d1e285cafea97fd7000fee4eff3f860843e6bcb06eac43afa148dea0c7 90 cf0790aa9d6f961fe5c2be693d2401a98bdea1bf8fd7d9398c41e0140fbce108 dc4eae456e3162e1d67252aa87b95deb833e7c64019068517f5ab9cfc0b4a2d9 de09225fc45e92ede9a5b827cd4dfa4bbe36b2f9113d61f4a3f2b131bab34a3b 8711201c02a6b27757eb7cec862fd802a3490e61f3adc33d1791d8d72c29738c 12089d0fe83a065f6acb15ca82aa326d18af3925283820da691d642c23ed6b4c f6c841c5da9b90396c7c06e020cc3b178dfb530c4e41b223dc17d336a84e4ce7 a86fb6adac343d055e4fee06dd498567a1215f42495f2382bf60dd508ba0cb02 ceb788da42c10a06748d46f4a1dfab4c3e9528a67d982987f92ddc576c214fce d80f90ec40eba00c271c664f1a3f953d8a2dccdbe374a6f0a0abc86a709777ad 20d8ca29df1a2f8d3f0520f7a218208e6356b201cefaa3780db4ac662b7b2bd8 67430e33ce11afc5ab46f8982741cd09dd2e5c2092dbca82c74bc2b8231e6c44 a25e153b464f68a89f4b23abcd5c6ee0078ab7f7534ee77775f50c50704ccc27 d23315a90419c0f73259ab4d93fc188a54a78c273fb929c527ad3cfc5c818088 869860f5f192ca0b26e218ea01720cd58d9bb80fef77e1869178dfb06548bd13 8c2d5217e96159b89d0cfa4d00cc681311c1e3449b510d877ba151cf38bd8886 5a428e84f0fef98ffa37fdd7cd676784b42712aad02f3f42a8c26550f9c1cf11 01eceb0c7a2859cfec504406c7871b802cd2d860979f04221fb29291e200a4e8 e0be51d62988c2784880769473aeb9e26cfe5a8dcdbd5158435b7b62523ee652 3c00417628bbb15b706bb5259507b6507c1f3355b3a7e53728520a16901831c6 cd1a6ca7f29e64c6063bb1e05e604f4cc3dc6871382dc490a3f404cc5cbdaeb9 87aa2a4e96b6e876f36d53bc6142470ea4a3c566ef5841c6f02054bd0a6ece9b 3be50e8ecf24a94e63134567c247df7ccfd84e54d9ad94408e5a91de53e756d3 53bc75694293cb044b2502aff8b44e335aee7047d8baac8c3aecb65dfbb777dc 9d47bd776da86942111de6ff484e047d3eec1d1b4ecc8158ed51dd1fe0677e42 e62334758acaad1d206117556c62e969128d87438fbacfb08bc66cd93e6a4f58 c43aa664d81f2a3bd3ccb5b376390864d29a65d43ddb1ac19538284d77748a78 86ca182b731f8fc77cd2891ec59c66a283a39760b89518fc96b2bd158176d170 27bc6b81d3e29fa17f660eef03fcf65bcc7dd562bdb8adf7e999892811ce9ff0 ffe00ecbf9f4e33f00c0356513e9acf9a2a7c00d554fe1a7a10f1062c5602fd2 29921ade957f663a7e947c0ba9d7a3d7f36522366aad00498a120ba69fb30585 c64be59f562f3678c24b9a703eaeeb8048f1e83b07d3ec3d600bd351ae75aca1 20920534bdc9354e13e95e6c28dfe69384ec1e1b5e029d80654cd4dbd7f2a5de 2899ff71f37a71e1a81494cd6b0e304df7ad40071be32e65be161ecc80e0ad0a c9c4e7e8d38f585a1731dca862f610e2d2abb32c3b44e4272445ea9e713e529c a46184f6367201558b70d1a7fa91f6b8292e3cc53465430d61f825067763d108 d26138df4bcb235869900b5e0d2c09452cd123ca31589e3f9eb786baf5efcf21 d469c9d62e347e77b1028da7a0d6ca79c471bf9d5122de7acbb25d19526530ba aa1f27c21ff21425b1450de9434eb7749834ef0e6c382397a46de21c8ec5f058 b0df141768f813bdc87ce1366e378dd35d9a3e41b4e300a51fc035ded3d632aa bc752d158b36a4ba83599bff0df53247f0749403e7ae46a7dc11044f958c8f93 9507d00143b32e8c0598213e6306a8c942ca7b681c7ed1b03eac55869d724038 636de4a11d694e536c5b33583fa434c1f5fee41950e781332480d745ffb205e0 21e5c287981169b0e4f00bb3e3b90e7d70b3b376049560494d18660124eac02f 1d1eec6b0de3def433ca245a67622c8fa2218907d7405a6c237b98aa643b7e6e 900550d51669c658ea7c0f9981282d5fbf6b6b59e4fcbc2f133ff07ae1ea068a f8554500cae14f97c93b639b3e301c0630766a371293ecb37c7da88c8bb817c7 d7e795cc5ca4c7786785e91f1d89b9fec619522b0b2c4983a881464f8738af4c 8b712447ca587048735d7433eded09b98d7a4a58484e0dfdbdea2e60959cdcf0 8748e9538862ea097ca51838536020a38bd2a0baf904041b19792ddd870a8f88 46ad4e63dbe9f2a6f5019a6f77028b6f54eead8b03f1a77082c9c96e4633f6b8 5c03c02756c75998c5d55c4fcf5ca96162fb95efb4bc5fe4d7acc213cdb9a529 e5134246adb83ab9c81ed599108a023471126ae9dc01d1642618c1d4d1eb6208 b62d14a038d0404bb63242059effd4185cefa86ca5e279aa304ca83f179d7557 de129675d869422b77709bf465e5534cbfe0759c0db2255dcc1f6b5e138c6639 e4bd33e5b44abd778377abf1019dec0082573a0403f1828221471535ddce2356 6888f5eff99b4850a77ec7be3f1d72a705b97b4c17e04af7d06e7bfdfe62a4ad ed06ca4de249eaa8b9918ea7b0d8a3d625bd9a5b6a3abd4b29dbf407d0e28cb7 da1983012f813adb6a8417d15ae078fa4302966409911b91018eeb21f357b801 c75ffc6f396bfa80eee0702bc43811a6227c3d7f6984baeef3e0e0098a89f5d4 adfd8cd4f5d20b5853f6ae06b1a24bb5c4ec0225b7e6609afef2fd469cc97a3b 1eb1348908c8c77ff10d7f5706e46c9ba2a3fee27d2a33e287461ee0e2f5a27b ab589915dc0e71d54c9d4286fa40ac4e6c3a89f78f24cad92fdd8b1ff1cfb60b b059149f4b7a579832275f7dc8cd4aac198bb8c878232cf02ca0c8f5c99d7715 d982417b819d2f77bd5a1ca7cd9ac8e086efd1e136eea0ebf3916b6d6bdca721 f57dc14bdd1e6c897c799d96bc5380139fbf34042e209b7a458007fc51f36aa8 88221ce3deef25360a442ae37512a88ac191a1907c27e3421890448f38a4947a d954e7c0e2a0d8f58878626d214443c2d016088f095ae43c6ade7e7493ab7c3e 92f7bc7e22dd03d85d99be04e9c704e7c35e946f13d05b1f734e25a53bf97145 08bd2a4efc9f666149349c780cf3a340b38c60afe2ec459c83ff8edcdd1ebb66 56080f2b11ef509eef3b4ea6361a91f11c38cd49c49c9854d96f4eea828c1d54 4e0a0b965182ffb6007fd2241cbf2d9f5c431ed2668a340f0091855d76588e5b c3f45ffe647d790dd59e43421083df3dc7cbbfaf7aa67182d7439b9ef00b48d7 cce9dfb0fff3ea3a2ff72c4071d69c8a3d76b63267ec62440bfc977e5fe68111 123268aff7ab5ce18e5a99eafc7b3a88068d2c9d0dd00e91423f5784b3ab35d2 1dec4f492a04931a67f4432a0179be213d3ca3e06bf162035d732dbc495e4bf3 b8b55d3d6eb76b33e8856f46ec66e8a2f5b162dc4d82cc22fa1712f195d788dc a4386dc8c91364623c7f6e17cf701b0d02d1570519c2bd71a85870c5642ef50b c2eb7af926569eaa71686f7065513c14b4189ef0a12154bf2e02d794b3f49e25 7f3ebf513658f22a7e76993ca504495bd6a4e398251992bab432f91af67312a3 b9e08d5f6d6cf2f2ce819878a5a65f9794c4c74813c95506eafa6911d947f77d 9186a68dc408b10f693f3a4a1582252ba04c46eb447104e05dcbb70d0a985666 f07f4354594beea06329ae4968f93f615ce2218f00e2d826f471f20a1069d2fe 9a30ab5b48614262b1d1b9b1cfeba865465950af7e9bee97828a31f8fc59c5c5 184bad0c4e766e6b5986f5f18aa726da2fddd6ef0e3062904f96719588e79577 fff8d4c3e4f9a9601712dfec169c550b3c3a9df0701b007a143d12d4d151e7a9 8106bc82c5c68ed27cb7bf4bece95a711057b7c81438e57aa349efca203edf31 9a02faa795062d4ff1d5a67507add937c15341a7a03b9c9937c44229966d1ac1 c94608aef4bd15e35a483c27d127773d31002e0b6321420ec81413b6c0d878b7 456a264756a969730273c08bf16dadc27f67a3a71a9cf83984fca36234d7b771 e802a65ca32bd3437bd16887e41fb66ed23c9d2e8faf78fa823726b7f1c73f8f c05fa16188e2ed0c0c638ff5f8733eca726676917094a6459b18314ab108ca01573084bf7629d9f6fae75d28ec851a45f9e30fb02e8e20a1563e646154b2f303e8417805464807e2c8cc43ebebf8b725d49a572adfa03b7e6fd165f6770e7e04824bf9a7f2a0ad12ac5bedf415d7fb5daa7fd42d239e9f20ac08c2f56c84a20af9f9837eaf83155aeb62e148c7b53c6966a0bd638c287f0ad9b7236f3f614d05fe70a41ba042484a6aa45f953031062cc4940a7e3ba17aaee3a9fbc3febb0f0afac4af9fbff4b3db7f64575c81c6b2e61038fcf0350c34c010e3680b4c61c903c3e41547f26b49e9847b3a1eb09ee36a3bbd0295d1e59e4580febe76570978042ff43600aae45a732c231a4980d9ef85a3f2a2c2465e25e04022e590237bd40701c89bf0302655354cad2c45020de0c4cc98eea0e43daa2c203f074b69ca1b0e401d14db9789cbde9c53bc82a4a5f544ef9935f816a6f61cf26c3b015fc0530100a57090a491652f33b2e5359502836d60497033772556dfd160bb405b0e640c4776166cda5e0c6f0fa2aa054060429a3e930c51177d0921dc4cc61e44010c03bb05bf42130b5536a09c7b277e74b74d7ed223f5cb12e06cbab92fa3f372bb0fc5849eaadebd33f35d2b48e99e5f8c30eaa2a8bb938d0ac5e4398b6b50b6dd07e13ab3b00bd320728012b8a0fec189f6bc6c414b64d4bf0cb46f0409e96b05016ff379183439abee73ca7bd8e9c1fb3a9d40a01420eabab97d542fb1a4f7c00e9d351fe84430de4b2d84df815875266069660e5cdb5b3b85eabdcb080029200e29a4a861f694bcc939752d710cc6d7384e1f133183e7b03e79b62ba498abcb086e7e0403e6dbb425985ab66372638ecb534cf18dca292473c3e392f84ce5910bd207ed3565e77273793188ff69868998246d8fb473d853f34f544e62d725c100c2adb08a0037994ccbf7e3526e75b6185c3155b2ab7a629d654387ca53aed50e81f26b8e13f843f9856b5c582f7c68c309eb312eebfd08ce0417df12c165ed0c24bdac5bbbc59508d79fb0db908ae991d5dec29e11877dbf203854701980610bf211d7e9541d17b88bd02d1394d3228b8c1c989310e0e65f5853128b9e283e062b38bd0628061d62fdf536299501793bb26d04610b197e7e17c97e5a95251d0512193b956028f5464da892e9f3122f6f1d1e370202d8378b419e10a07a0d4e09f5d82fa357884fa325eeebd8fa6a0853360cbbfd4e1a87d592dfeed65426790c204851ea5d01f7e28dc03d5d9f2db187da48a55ff62721683e081ada54685a0e03aef8cf9dbbf28a14673870ee36752b80a5f5fb0c732ca349ea79c35b6bc802559d58a2ad6f1522a3ccb046755944f2f9376598637753036268281c4558820fa160d24c8c8786d35b6ca09d60955d7b99919c969b266a3d06f2aa2f659a1d025beb2f144a0c87b9e7b5e59a30ea3ff8c3ccbadab8b83cf3f14aa7bbb984d3084bd351af7a6ddcf7a2183f0fa65c86945499878ea8507fd8894c5aeed654fe07b1794c930f24e75c0f2d6ec4bfcd83b01bcec1c89a9b8e0cef375f840280c3038b2c852827e3b4b6d6c78fee0213057cb5708aa431bb159d7199185df269b502ad0b6c0181f2257af08df35ebbe6d16868c8d19a5efe4f39a47a8a3bff37f80fe68100adb79c0d7c4c2bb6717aef85a6bf2d7e96a4b1e99e73fa87877d753508380835a92ff38a2067d670da1f20d2c5dcafc16bdd8109dec5ec5a866f2a4703fd7aec94ff8963caa0854e8fff5ed343e1117dd6d8a7ec77398c356fccb54104c79fcffca2f56af77775c89f2cd123cf57f6c0f178f71e28b9f1c9f3091d20030d2b642e1db19752941d0cc29e8df57895952c0e34e95618639592eaa2764f0ac9696daab3e9344d4e3baa52e177e966e3ee51c0c98c5cbd524dbe033ef1340cb1b963aa6f421b91540650a3a69a76b82ed669b88774da1e28e08efdbe93900ee39059ab6d10dd542331250d46b4a964fb40f0811ea341c8f45147bbc473300ae8ed2bb42f6a17efaf47d3ce4842b30d238e62bada94f8920a20ea96356f5300f8a60022a7eb26e34ab035073b1a59e64e837f49fb12afaa2b1d07c684ac540360dd7c90a0e1c75e221a69674251d3dc578bb1348b4a0a620fe9ca7bc844150937619011b8e47e1de8943c59501718151e3dc06b1c2ffd63863381621c88b10738b3509ca8d378f4144c61c619045f22c0e703e615c29b2621a991e5a71317037b2c60f5d59a631183c65dadd783f2aa71fcce7cb90f9d7979efc30f14c6510017c0e14d1d28873f65e0e848a5db776982f3dc6f31ec845de7d24ba374201d0a2a0ba1e212eafd8e0a3301e5eb5759ba6b9aee81549f966fba024314d6390902621d20b3de77b5c4ec49d307c310509a24c8c18992fc2ac754dddf676008530547e5f44e4aecf55bc12891dd3bfcc6eafe084f591e6022d80d48797bdde34e08d23b80890b558c9ba4d3123b8bec1088212fc1396709c56e9865316aac182b03166d840947d35243631fb4b445f8180379414224ca217036d1338aaaf12ac50789002e478c97ee67127ae70f70414810031f8e36b23cd35acbf62608cd7cf40fc0cd40336739e7500b76d14afb6818bcc4ab3582c2cc4d00fb6baf5e8ac5240361d4171d3dc0fe0745b332558b4faf74c94dc8a600922419fde32aa42ee69a095bfca33ff3f72005f32f1f5f5d203e00f432b2b8a971c24cbeb51a9ee899710a6d150ebcb275446fa0baaf1066f712b5d7774af675572e47fc80bb3d5ce7ad0bf71bbe9e9b2176d198d285efd77156dc6e2ed30093cbca1e640486f9edaf6b0a22671300cf9b2ebcb097f06e6c97b5e0eb40ec9f77102a7a179a7e8b1c93a90565ff4558d4ae4b1b3f19578e5579795627186487aa6fb916b43521c80786c00c3fa625a0b53f8d6b5ee68804c14975146fcba513b3f0168a10e65babb5f85f09addcadac9796e6c1842e35843f2bedb74e4d36e8c2f48c99df14df9c935c7e0e8229923b536f90b8893f73a68b483f9e71341a57be99b80b6a222b37b3efb1078c84d0fee00cf5063c4b933623a5031b93c1a61f1face5b78977d9c23893a402a1b81c3648fc7d6801c7cb07af8e6e5ce71e8957cc12bc432f1e71302024c20e4563a6f7323406a05d338e6da304da5e4cb1840fa4e2f13fdb9daf9b5d8bb30d9c929d3b496797b562463a420d2fd35c9e089e9a1f570c2f0e3c5a0eedc6c80ae43f4868b0993de214106398f0aa5981174206e904c0b99950b204e8c201d705f7bb5da84b3fa248ece2a6b4b9c665b676e4f8f44e8738eac09a86e24ee5400dc65dd0792000ec0bb864b36e6acb8a483dbf8fd2551bda99121d94d0bbce3307b0b431717a6bba4bbcd60b88fcd44e10898e299159fefffc08e906f224268c0b2e3d3b0ff564bf05d5682e3cc3d45b5d655da391be035c05bc663239c8f5dd06cd49160348c5181867b6c2a2cadb2687d545e2253f559a41fecee000ba46880e0ab9d2229cae5d88582111de5867ec57dc9db994757e90e576053565c21182007a4ba2e7c86da0de97b5421ea43b6f31c34c18516c16325ec56f6f9fcb0f6a086bd38971c6eb952c11f7b24dfc882f705a199f9aad8927f0a2a932f62b24b9025a3d20647f4b0102b62ff8b02c84dc37aa5141f3c2597c618e59af52b3114f059552ce8b17c23b69903a71117290e6c93ee4ab4e6052bc7ec39b307181f4d70c45ad0c68c64345f60283dfc6685b2d7bfbc221321370a1451bfa506ef955ab0119d6e52dc495077259c983f6a29ae3f2455cae6fe13e191acec3c6cf9e7ff207cbe16d62c813f324921e137f56952ee2c81199d50b7d9fa5a23391b6bfa1b30fa30de22d72d8859f30203bef8c84a46409548c9215de18c98859b5dcac636300d780b34a37e7101df8ef07acf0fb209aebd71e78035bff03c284495d745ee40cb1b0ed825b1ebcc7ef1f230b96e76aedccad1318cdeb4ec801336b8deb4b3e06041d6069870e6e8b98526f071c18202610d4eae3dcd710d53d292a362261d400986690c35f555232c0b10ae713d57dafafd009eac094213f042d88150860cc0449619d3d272cf05b70cacc937d9fd4df4e6c3908c54b8955d9da6f6f17e81406fef44aa36f2e86ab56ca8c95cac0034d5f14eeeda25a0af057fe6451897c300fc044ff4e51df1fd0a4e4576f837d560e6a71d00984222b117333b841ff37ec0892d42e94682244d7ed831713dc9c7d1536a69589449acb4c2cac174eca9f6101d673f4516b5355b24410e4a1213d6129259f837e386ddffb0a1b75ea4a94e50a9004b798191789d0c380c4249bfbc734d10029be87db88a6497fd5a716b98809cfcc400c6574075416b65ae8f2bcccf330c38f9dc892bbad27fcb0b9c71c02079aaa0078baa1b8e061f2682f10949bd8dc840385605cdcba599270b8179c4a08d24a5c646026d4323117fb49f7ac2baa7629d26351d2b566234bca20974b9e0d6d15d25aacb5b65d72d4fd1dc2040dd546e708943175e65fca97ba0623e42f026d8a2c3a39f1513b044e3be203b3eb0108f256bbd374d7d4928a2289b74c6608119ae3fb2478eb239488528c346942a7ae25a5794c7bb5f2db9f3ca14df66e0068bc1da2ff2cb5ba93742f50ee4b2365f9ea3412a967573564027bd7bbd98e0d7a946168f839a9d3c3847b4bac314975f96902ddd14fa6ba8e6060d2784b3b0ba9a2c2c87231bd6549cdd5f415a4c25f782beacbd47355347aa98894e91cc903b7c3470f408361326848be8f56f82506876c9572a8e971454466ece4d8219d02a57704024ae0357b1d1bd1fca63994e7616ca3e9d99de41cffdc653836305201c249cef7926d9ceda43bd6a3ddffffb7a1e56224db93b0f3a29b8fb5818c670f3dc963099a72ed27011e1a9c4fe3d25a39956b831fe4d134b8dc454c7a8ed9024748ad29517821cf24b3e4bb9f4ce45153f571926e7fd3f23d23293ea57a560d1bf111363d2e2aefa3c457e6af314aa79ea3e3736330550d80d665909c183108434c53a03a3112aebbeb063face5df633c6484ac3c724861db82d0e88dad860726faf5a726f7e5ba45e816bda0d095edb7982bfeca5ff775ad4267bb0ab3be01e97edc96a7b688fc89a619f0e31096aca48e81d45e7a9363443641f8a8bebe006bfe55722ccf1d220b977a4543737cc4aecc0cd74147e8895a2bf86025d76f0cad936fde5449f3aa6e548413db42bff0254a4a879b71e24aced8f3314ee1fc014edf613ac867c2f468514fd9ee2ca1913e356466d4503313c1ee8135a5c60806b0f09d54eba44839c2b871c0c99492d8ce5ed61937190476f21b3d23002f3a0eb0dbe521a477fbb9113971415a8db500baa549bdae0254f354c78552a8c5d002c43a1780d0b7cfdb1b42dd2e2ffaa992f8bfa93af9b2beed058ad361e384240fb1ab5d738903397828487dd28573b99693d09e23f44acfb443615aa63877cb05be8e07dcfa23823257b0b7be15bfbf82ca95da6c11c067880d4a1f01dd9f520fad495acdfe2f2052ece1a31a22621553c453ec602bc0111d2cadd0d1a9347500aa5b1419ed4f252bcad07ac9b839735c5b58e662c3f69aaadc8d2090f43aa90c80e40cabcdadae52e66dc27a8659b856f08e445e161f58f10d81f251e45cbb0cda866642146278d9fc2d9973360015b3376106f6a7ab3a93c6e96ba77a244f0cda95a6bbabcaf7f0a3c0da24a158c7baae9d89739356236218b08ca56f25180a4d70987df7a5145187e807b3e924988892ec758dae11c6632c42d0c831cb7d0be16d6ea03e2a90c182a2b83e3d0b445175f41583e97be99949cad48db0b73e0dd996f2280c3a97e148ca9a73589ba893d9f592b22c7872025e7b3f8c4d0f4c0e4eeb6c452fdcb0ca3ed823430625cade4920b2f4058676bf94e8c523add1040b3116d977ab21f99173764774ed4bae199bc699f2717bc2d419934aafeeb7480b81ac16062fcc18717ab4444f40e3e71e24744e9688b446edfd37fefd9a45440e16233f6a0d4f19291cf94a697bf7c320f982a13a7a28d7286e44b0dd93c60e03b81c6e16cba493bd0ea6dad3a64e24ba2ac2c303883a12d04d9d0e2a1753da0822ba3a45fedb2095110ac92cae8e68620fa6f9edb653d5ba6d3b8d4cbc40020a7d0ae7502de2e557d9b6b758f13aff6ab7e204d331ac5b7159a5ea7b6bd3550120db234515c800018c34f75385be82e4602492f56926ed262971da5acc9095006807a92fe109359dfaa981a54e7ba57a105790be6fc89d556874cb4cbf422701cf45198b8195cd9765ee19ba5234b4a25b8eba7ac376687704fdb2dc8ae22f0de89efad509202d97d181161a6bfdadaecdd6459e571b8f26b93030c57a57390bf8904ba47389ddb6bba05b90be1e7384653abc2a4a5d8681ba2f3a11807af30d0e60eaa8d2f6cf7563915a565938bae98a56454f69c244ed3ba2c5abd3d8f90710a481e0c65b0c973746a5a15b13360076e52858906ade87f615664cc2c54d0c7867a786b616e5712b768475a1d8387fbaf9aae9e8ced34b3fbf99f4607f8105bd0c2a6692223e54a06b87d67ae05c00ed709ac614ca53a15341f85fe520f50255e2382bb6c23a50bf3921dda259d754b41a34e562e8bca5c70aa7826eb0cb04f9bd341c2f580c00f93e754d220aeeb71f54f029368cb352fdf95932e9871f043b5447e0fc76c34be4c44fa1c4559c92f11b50cbf40da4ff1be659e91dd5a80356e9df293e1055a164f216cf02b43ede0744d74a672ee04bd7b14d48d9834808165d03b7d3c0faba2c08414a2144e128067cd5d842c3e69835d46ccde4acb30c5c74d823bd14cac6694420aaf196ebe04b99de2325d0785ee9a7d6a22aea2407fa68d3b6a7696d1e91a80ebb29474c47b8eb480bb34cc81d1fe47282522f6e0672d492af1e170ddd8f820709c4fee9f0a482e071cfe06118849b99433f656d0d092c5234bf7a65498737b653444d7c13b6906e9fc9d558480cb52fcea8789907e7ac02d59abf9ec10eaea3cab29b0dfcd33bcc366ae236f9e540cced7a81820074c2022e83ae38af51fd47a521d6578ea5003650e28f6585fef78dfde3c8ca07448c5717e3f4d9894d71db49f5d1ae5d8250ad3ae9638a0ce86a06d89b64b30de34366b1ae7b4b06737d57531e5cf37de6be08c2dadbd0d3b126c4ac79febf0087abe2a6837d0854728fc9566955ed1334067da4cc704f0e758db0286a1ad20b6054ac318d429fd47d80251ebf97142daf8f14abdda0e8f9d756fc0235fdaa064307251995499747c45d4c2859edfe01a5eb3bd8cbd3f76332c0c996f630ba00d2f6031eb30e8046bf11d339ba9d79a4524a4cb9739cb46f78c8aa295313ca0caf5a740b11334daae228af221d4a89036d7a1015cf9e926b48d8a5cb65e3cc004d3f9b51343f14e67ac9bae686b534868d0f79c0c84f6a7f0b8e6419e64fd40bc506b2eff0ee4a2c033829e461572542628cffca7163564664bb48fe71358601a1a31c2906ee764da98307c31933a5423f7faed8a0a3a77a4b28cf7759a9090d2dd13f37ddef34ac26c083a412589acd1205ffc858fd300e93139fd12ad0340a7aa28a3327737d37b7b1203f1901a833b927280494e91abb3ab0f576fed00700ceff13d33f0cb37f50df7393d78f1c7421122975a9101b3c492c8348e7de420882973d1820906040bab90edb60829066b21eba649585d3e6c08467373164580c4bbd2d34fb26dd2a4f6017b8536b0e6b6820f20997427c792e4cb5796cbe7f021e2e50cefdfca7573141fd4a7ed5923a22e14285b86c491b3f143d0bc6a3eb0ecb3563558d3895bce051193f5a6cc0bd78c924b50c3132b088f513429a4800016d9dd24870cfeff3fcbb6047610e12bc9aa451ad7c264ee9b1202d66382fa30e32803a9eab46edcd25366b0e81c1cef509184ffb7a00c4789640d724de64890077ffb2731f26983aed54f3ca02f2adadcb113412393f24ecafca5550e331780b6c0528443c719bac3ef56e843db2667dc1b2a45654425ccfd7fec52615aa430bb538b2a909409fbfebe3370155965352eeef013c0f2692220b22bd66665f620d true -check_ring_signature 63b5a192deb37348d777b8a3ef8d6b93967df800837dd2de304c069c984e19c0 8ff2401124f3bb74abf476f06e57b06d80be06e9e4d60e2d5beff72fe00f04b0 10 25c7e13f4a05d88643771a82f34b182adbb12370ab47e63ca324761f69460995 ab2ab944a46b71e3013b21a8f917b6f5fa9a546f80d6fa7eeb322e6dc1454500 3cc0234031d7a101672c4eb7ab456d50f592f46870226f617f78ec6e65b94d00 bb93ee2fade9dd6cdb34d222b26dabdd6e5612d79bb0c98eb5a39bfe05c97d86 bb099e39714f7b582942af992ccbf72f6935fd55fa07136092bfc8d5d3c83fe3 d8adfd4146244ad3888313ffd65d3ab929c58c15cb824414ae64a0378e9412f6 9ddf6ea5ca7ef25cd11f0c2317a9aa9bc485f67edd49f31f23ba2dc6f0f87782 0e52030960b6e155c91378705af739028f90777adf7a5672f8194c8f6f689ffa 0cb27f9c2b22b9851a540e7d723b653cda4313cb1be99486970fd762feefebf4 60341124cae1ac2ba8288aa407c0f780a3f01e5f7fc61e4f64badeab5bf9f018 b9b761b3fc03f009b7254da7fb0e764ca5d6f8f16ad308c284f80bb8a7863b084594817dd3f3f5b6145c51710224a378a1cbe4ca35acbd5f8f2e6a8a7d8e8307bce2eb7f8134258b22d906b12b9c6729822d458ac1b043b42184e6738583a50a548849fa903fb32519090d52b3ba8ee9b2274527421b0cfc91ecf17413d88d00b1f3ee62d1c918fe6a9aa3ba564ef830b18d3950e5e66ac387e7b30f84c3d10f039666530ba91e6f05a117e7118d8da40da34049b45da03c9cd6c37552925106e306a76b65731d037b29ceeb54fd28b0a1e39f86680774857a7995c1a7dc990acf0861030973a8511fd64ebb939be8e77aad5d0b14e5560631d8e6b5784fb0068c8437428c447998dfa56132223c96c417d014c0998ed7e824f3d6d497eff70ce435b07ca9fd53c5d8059015f5f6bfac9c47e28c4544632a9c4162c9529fc70940595ae101236b279791429657371b752b0098a889dbbd229273c9763c4d070b48491e8d7f06eb60b348481c16accba3f1813bc84347d06d6487fe0f784acd0cfadff508dfaf46d29de153c90af4791b3eafdf302ab03e4566b71aba0d936308ef9779ef4841f7bf4535339cf208b0952b6a6cf8d020bd3adb77281f3a0cb40be7828d8a37498517332c95cd67531d9f7d3b3a6c9128d4d5287e114e24b3b001031836a533ae5a97b5259fa9d1ea3c48fe824b85de6c2e1b13af67789965f80906c1219272828faced5fead4adedeef39db823e31bef356978dab9255cd5430e50ae2f690abcd6b4647c6032210c348b119920cf4d88398fb8bc4ae6d74e200d0fee1a26fb3c34dda3f5a53f1e52aab9d63a4e72268f936b32d97512e941fc05d2ad8fb86bfb6eb56ca6753d94116f2f4b48c2993c4e23f42fdd0e330af9d909 true -check_ring_signature 3705b6431cf37fd18547047bc95658c6ed541498df6618a0cda1e9659c0be4d0 dde1da7becaf592f8ffa898352b1c93875f1e67715e374a0f71fd33c16da7c2b 5 8f4880cbaad6efada33483e378b4fb1319b03a3ccb7b7148ab1bf5fce57c86f4 5eda0af9ae30bb55a577d948c2249f5a7734bc7b9b4424dd044f58d820920e85 6cc2cde74802dda8090b949249def45b27e4c6ff5035ead4fdb30219b314aa80 7b1ed0c415f70a1b71b24907df87f796f4763fcd753e3893e34da681e21452a0 9ef52733055c3f52029cee466b17501579afd394f8d4ab225f799a2b9981a727 1dcf17c1f18b248f99f98c3278709c4a6f95ee3e865606fe4547da46aba8250ca05e19b7964aff285d96f8aca76c8dedfd0a4458993807bdf593223e1c2bfb06fd684bd94ee7d491922931005f2b3b0272b9d8ace28402e40d7dbeca71f76d0847a42852405dea5d1fa243fbaec4bae781ab50d9ba6bf8f8e6e423ee1d40a4f476757f5df819b34c1e0de6be1369c59bed1cd31784101623743e4b493e29aa08b12c93cb1b91996b0e15301a1147832567a567b531ae9ce1bac3be07c7ae440a1a189e63423127510447f8a31645f71fb9155af4f9bea759ddaae96c57cc8a208c424195465eddb71be1ae298522ec78f60ad6e146149eb4b27cce23d3f3050c5248854e9d53484044bf30825d9f5700e5b9a9ed0f2aff5d4dac5e6e30cc52038777a4742604b1e3245f7dd6fe54ca0a1c83a175a99d5c61ec8937fd2275830f false -check_ring_signature 2b454e9b99e05d7a5dbb904bd42b7c69dec3e2c5e1691aa356029e420e5544ec 33ab95e5437805a6444c0447b521f390ecfe86c03bed4ab6f36c51e6561251a9 36 814fbb5245c8e83c453b013b11532dba4ca450a5c3ff2473373bb6380152e4e9 f65fc6d68b0dc087a64351e24c45cb64ff9895c60595a99c9c485456b3257ce6 ff72f7a04c0c786bdf9375fb1a369bccacb2fd198ae8a04702f99ec62978e9da 8f7b06a9f77ae9cf618044ba5ea9f4aef1d737b695f0a702cd760431ceee31c9 2fce51d40a292218ead0c666bb66543bba2c88598e94448ec747696044557da3 98e513921ef8eec2be3bcec21ca1c1d969c476cb8cc8e978341fcc1dc6875b9e 6a56836fb82b6f48800c3d8fede2c99bfd2ce611d9deadb27ce734a63da2eb7a d25cb90e5d6d22b10f0f735029e1f0a6a80a9ec76b7d7d99c860901dbc79ae8c 7aaf4f6d45f13d514941d9876e28f21e3dbc073d8168525e36c3329baf6e35d0 8fa72204554d40ff301e8994d19d0d7febaf5be92dd370677f580ad6b7403e41 e2137334ff4b6af2a8bc163a293eb4472f90fa84c0a59126874df4490b38a64c 9b741efd276027c129a82b184674d18ba60bbc0f5e047f568aaf24bdb74f187d a6747c5d92ea19b08c3e83bfceb3382ef02c2c5aaab2ac49fb838c82188f4510 afe152bc1c801b49c6e5a9e0cf529ae78a7de2970e6d30c027216b789f736e6b 3a365eaa29232dd2fb98e7314e8a86162736f085d6ed40e2200c16deba11719a 3f20a651357abcac2e252127cfd00ef14929e38737701e2c5cabba239c72e236 23377e8cdfa9055665aed49fe4ba6a9c9739f8d3278e93f7de31b2028415c204 354760aec0cbb8dabf12720c0d932fd1caebf6cf658cbf0ed7cec7c3352dd704 00cdcade4d8716a89ebcacb1fdf869a01466b54109430d7e3dae5ebe5f1d9722 5e3c34c7ac202608bc29f59e967fa6cf29096185552acb3f61627177b29a4e2a 432d8b4ca4086e3130fadb83c0682963c4359f51578f9565d5b361a97399f64d 31d1e11222ba6bb224898901ab74ccd9bcfa8f9d03fe0d4a6acc63c8d76b42c5 ece1c905470a9a448640cdf61cd0f301adf684c1d4d96ee57f6bd3da53ba15f8 a1482bdbc2ca0cb99e4517ef3fdf8b413f8b97761311392cc7b62313a229edd5 bae4c5499eeb446f7c2891f3009144b659153e55bcd5bb7d0a811c148c4cc445 cfd52d8ed057fae10c6431499361d446a6f6c1da65da9b1d075a8ae39f359f1c 0bf226d3e1df97eabbd310b8f85201cde4fe7433787baf28666fcbbaceccac60 b90c7108a5ae304db3f4ad748ceba02d4b1b8d661b41535a303d1ac0da2403cc 5b0e4b5eabfe175f1903c4cfbec11ee5362a53b301d0fb8842b1023aaf7e942d c7009ef36c88dc69d7bf598b5356f0636792f200b515811ec2f497ac4251b983 0a698c78253415b8d4916854ec98b84603300eef7c42c88fd421ef95e89a1446 8efe9dbdf376be70a7edc156b073a58f09d2dd29153cd3318f7bc0736cf606b0 d448cc4e76713a1297a303d9eea0eab3706818e82d2a22d4391817515d52502a b01f789e528145773c1ff9bdb924ded15168557e8d066227414813e1bba596df 60d9232a18e3bc3fbcf7aa9d8d686ff9af0cd13c9b455b07f85559157de63a50 bfaccbe0e16d2724aa258e5fca5694fbb39127d30a315c62f8afc32655e5b6f1 51eed05967d4587e5b4e26959a68ff098508d35846bbe3af16d6bf2875e5180d6269af36db65494503d8f7cf1f3fab75196ea0feffd33dd5ca37b2e54959870c927f8beb990949a31bf005994377a6d33ca4defe7aaf5f290bc9a488d7b9d50b0957e42a60b90124fd8b4b6fec32744d03ade55250328739134c84101d3cd7028dceed91773f6340787e3b05e90c7b1e3e2a32421d53b621ac2de04d751f330eb32ef7026872bf25d6e0fc9c78c08a9db5c7bc546aeab4f4be73e48b2a7e7807dc1f72160d3360fe9cb8992429cbc008172cd99c356ddf7fbe8590b45c07860c046be2d27bdd55205cd278729525ae66e12a5a91bfe925bd1729d5254e98bf0591af3399a870a17766184851f4ea7a77d2dd77bbb694e6686ddbfa19f7e10807138f8e32621bbb1434b1d07082a58a3fe8f0e6030f059a52db29859d21b33407dbb6804d02e502c31ba44f59d4b2c19442d4a985d3926904c11856e5a38018056d932872b51ac430f75873fb460bb3a629ab394db09a4534d7f6a2cd67ff88035eb02e98231c432ae9163a2d6f8423b49e87a25b7ca4df3132a50a7f31f1ba049150e65502e708e6492a30f08a50767c801ec5534d3f10dfeb20175874c39e03f301ce42c3b1a34c87cb0352bcbdd13eee9715b56d3f0ebaa112cc3bc6458c052aef22db097259ed41552e11b66a2c7b848062ee0128332b08fcc3af126fe50c0f5a025d630c3e89eff03b560a4276d4eb6741994c7f67e092b31d45848df20d081d58ec256a3a60395d7cd15bec4e4a9071515401c935874a47cbf249538c0604793f00b0ba796e3e4ba3d00ba7128c9e4f5abed3cfb75d92b382d3d2eb4b07460072da79cbe7e30ed0c6856d2240d1164fce2e30e8824abbcd4cf2b034c80b6a36ea19d9ef9a98e1e31d7427541bf910507056ab3bb93857822a3033246d08350efc439770946c7867f09b9818c8d5603f1803d6033bb941c719dc7540fe057e42a051d9ac975105c18479737a873ad9d80625a0a7bc437d8cb041dbf7230ce3bcdf5a3da920786157d7d75224ceb12060ba8cf82eeb6920f395cb934547090dbf463d2c08daaaeca37472a2b8074f5ec2fe2cb0cac021761ea3b7391b5b0b1ff87c934b66729c753d2c57c2f4152e018f8ac56fe06575c2786f421297ba00af27f953cc813d38389677298bd92b930bab5b44cbf13d179a1cadd9b030db0f3a6032b8d770cb23d42c65a08f5393b482cb51878cc56f4d849ffed2851a070a44383b6e67e617f752927de6293af0b40e97c1a6ad652701e3aa6444e169620d02c63eac12b4aacc035c0c1c3b79093d1db4222dc0d046bc7507d5a56e153b0dc58b02cb54c551a087ce4e1a008502a0101670f637b9ada56fea394f4f367109908118cd0e57aa6d097ab9f06c886c52116000544c7c96d179346e9738837a0cb16760487cef0730c9bf11f61ee0df1c8b2346a7735b79f14b0ec49acfbded09334382862c4cb0a1e09d4187616d6281ad43ebd5d25e2c36fedb19eb23c6120ff13a1b12ae416aa0824116871048aa899b2c5632638860582c0dd49a3892830664979b0e6ecd37510a55b71fab47fed1c3f8cd0549110c219635804e87345708f051b178228862c12095d46e483d3c44351efe959dc7b45f00539fe506d21509b224bc2186df7365a972aa4ad14da80d5a519c4a1a2f0cd32837d4dc4c67d3090299972bccf50b8f8cd2cb15d1fe8bd31f68130a5d4ebeb411539d58af40aa073d9a2c90f9ab94cd11b01547c9cf06e58ee494611eb9f7a407d3811313022206c2d7277157fdb748f80fea9d225a2c33c64e72b27273b9557e799d83d7bffd039e4573ec1619b00b55d97a5fdc100e125b50e5bf61b02d298d5893788a2fb40c5a12eb6e1ea8c05cf721558b76d69e5779834c7636a761ffd6b26a1399ae480f9a61029dfeda11904759d2a7d0fae83d624c5b70afba751b61961eaeeed90802d9523afce1e96bba2739728adb09cc7dadddc1b29b35b255465c73efd534ff0144766c67578436c3f1aff9314b4561b8d2c13640c94b881a17258f326b803907eefb2ab26ae10ee8344104e11aeb3325b21c4a228289c7b8b038c049f2b4040769654ae752a80a8d82b9da58c810718b0a33aca707c84ab357cf4c6b49b22c006493c7fd2d375e09772df2f8c8680c068a6dc1ae7a231d08d9b1e85ecb1ff606e07f1ba0da67eaa63cf7d4fda5548100990e03490f13224f52d70b5d452af401be6af58f887c768c0adfd045f93074d334e943b2a1b4a7f5460e8cee6d574b0b536b4d175a7012f28e3b89b4ecbb5537598f9aa8060eaf665dbb07155ce2240e99e2f8d521df807277281d015332a7c7578aafb8c8fa654a455fbcc4dd340909bb9bc23ee69549e1d096cdd17b2f9549a888aa51aad24f60c33c7c19bc37890d8340e7fb1a99d2c17cb71d1945e3baf2632f19951ee6d018a14345bff59a0803b2d468644fd95a657e2ee5b16fdabc7e3ddb086c6175ed52dbd1045be6249603f22a7fe46a0c4277c6ce60451404c9a44b22e2e05f3b60caf78ac1fb6d2404082366bb675ab358429baac7bd4246bdf5fd85afee7c7fa3eec107012e420dbd010d4c8f997a6dbb78fcdfdbbd643002acc10e9cc922a8916feeb9f53b088da30bfee88fc99c8a7562ba7809db7a11cb87cd642fcd72c6ba31852d9932c6edc20036ee9611e6bcdd09f910ddf83740fd65bfc9695f776e666c46c3a40d37c45f01485f44a50cd5c9d77b130632878b8d80ef4be82594bab038f23dd52ead141f0ab213a3fa6ceedec402c781612ae3733371d5ef5cf0794826f5f85837a7919404c29b03d1b073a407819364986234230b256e5dc93a03cb5719e77ad0f324880e214667427452fc917b61bc4abbdf3284df7c49733b1e50344f32ceea50638e0f04ae4c2c0a6e8338276270bd672f8cb6c137683dbc2cb6adfc062b46e1fb650f2686804ac40e14b76d69a21803afe7cf63fc086b8952c9d76e215a3b61e49a033d19d9401bc8d0737a670df39cae92fda6409672b802e35897b6db970173f50375a0a582d3a4917c93c6ddbf17e60ecb9be2c77e79c6bf1dac4105329b0a280460d2d5413fea4b0ceaa2c1c025c242af452f827e979bec69567a0f3fd522ec0d6c35edc6a80b44f8a2b79e5b27150e8e29ec816bc75031e3daa131c84f5550025194d5bc6eb3b77239db4f8bf17f4ad45425c79120dd0982ca1be533d26a8109 true -check_ring_signature 54ed6f6ab93249e8a6c9a32c2843f97096222f509158eef6cbeb34e72a431d19 2b00386524f32416a29660e40b775f189a449cffa2a14c398caeeac5e7779411 1 b6a505cb6bc3d4b0ec2da3cc887df7da7ada7fd4d57974a97fce0ca33787e6d2 2ece9e456eb627cb12cad4d389ec21f76e634053ff1af4a03eac76cff0ce910ad75f1b887fd1eab65ef9ba3f4d765463538dec15b934c0019be0a40eff5ed20f false -check_ring_signature a91a6d596e51ce9b64202403e5abd4efd31ea3d6a4f558f7d01d10a7e77699d8 e0f655993b8a96d76482382831431ac63c26a2e3310cd4ac1a7636927bc9438a 1 3e757220248a96857f81b1563451ffa7be41ebca5d1544e3ce38a27bdc3580bf 7fe91c661632fefe503c697af59ac61b6e4f31ede654335c79bc43183714b70168d860d36d0fbac5899fcbee5cc02cbac9c2dc64f2ab53580bf5c782ae6d190d true -check_ring_signature 1403ccb9336734ffadec14f131dd081c40d9932dbda084b39d1933178c2cdd65 50a0b166eb80d4abd890d52af621e491a1a7a4306470cd1ef9b24c336e079df5 16 83ba747b15c5022c7650c22c94e6aeb4f0960b89e88ac682861d1830e5e4d466 02bf063d186384191a1eb8d5d520d5555f4b6da4398930f6ebeeef9573e39198 7e96b0a15e865d48fa142fa75f4e358c106d68c6db8ed355e21aef737572ccdb 2197953a7076860ead8ae483093fbf8aff212b4c29ace2eb1d806bea7fa91ad5 3b572cbc150091dab0e71163baff519820b508e78b9958a7af07ed1bfd5d0ace 9c3186a37a22fdd99eedec7de5c43133cc3f9dcb6830676c9073fb2be9fe33f8 151013c9accfb3b110aa73162d75da6a3e3fedfbe95eab9b4ee93f0fd037c224 027659389c25eb6a1263893b9c300cd657463b728d2a7be1714de28e9e14ef8f 72029a254a0d7a364a9ff74c34540bf76967c3dfcddff002c1b3d91bd557e632 90222af85aeb20562d1e9bb89ff958e22c9cd302ec7db957690a1cd9e7037449 8653d73300a9c5e44a50b9a11509a7bf3a861485d3f255f430342f09f1ac8a87 6213b10f88d73e4c888d5f38c139aa9f281e2020d36e33b18d2814e912a5e7c2 796f8770ac384b5369eadee2fe83ec56445d5185ce7956022015a7d5abddd64b 60f52b1a6b5c83df278a2c1b178d6bbeaf69cfdc80e403923b593d7c7a767ebd a5a2738981ce432c5361a339bf135230b9070c104136339997bcd667f154cd49 8d11919d8481f00eec2d9af985a5747d22c854567b4b5bd5ef1f8c0ca0d5b84a a062a78bc2ecab13f70dd8b8f603f813a53a2530390031d384f3db9285aaeb00ddf5d838d6c6d203c368ad4159d484b039092e8efda15a4602c567bcb2201c0ec87210f52c12cb99993b03acb58cf76151f35d83a6e1815d4575d62431a11109470752bf8bb9ee9b72f34387a2988efab658bbfa111337df9590dcd750d7c3013f266a03aa6087d3c046e45620713fcb10f6aa7c07cbde92edb317a341492306b061640e366f9f8184427e57a82e1994753beecdea49780413b8a7fd537cf40871bb636b6c9ec7f7793995bb0d5353fea9ea33f981edc43f6cd3770fd0108a0e1f5275ed7545db11391b0bdcd219a55c2464bfdea03967e82cb85c947e2ec60d311e36d1c8d1292e3066ac0a6cbaef1199a2ba501f7d842256e2e6932f0cc706fa9c7298329e138558cad9a655b2d76ce01cf5e5968e8b6fec21cd591f1ede00a657906c77785fc473bb1b7842284d821a5412492d1a385f3e609b009d7106012eadf4f53ef5a0a975b3b362c316f527b95554aa367811565271401fd207d20e325911ee345f2e66de9db51b4b27dc28682a645659df03466815f9e05c86a7077d63a5bc9d1f15ba6f9f3fc31d9a3dc04280d24973f9995db485f4718654e505c2682a10c9ad8a52c3d557d522ebbd2c64ddc977f635e34561f93788fcaecf0b90460ab52acb093ec5d6a5053d10cbb28b0ab79d3b0dd00b4de7fc402dcbd00c62dc8ae8d849cda0cd4e8825ee41850a14f242e3ffb306ad016b6ce00565f70963b771e11600d0e3e8939f0efd608133ddc4fbbf86d71afd38d5db61c9847f093c3d188aaf9a58f4e9295bce87edb3b8e72a12da8f6ffca735689733389f670cfd862ef0e485fdf8c178ca115313dc96ce702abc8fdd0cd2e211119de45537031eac11563216beb0b78d85b3afb7b485cd251738a6f6662dbb39dd8a90ee190fb55a60aa42a9aa419d0929bf1b237ea98f7659647943893086013c2e66024108563e2423705cc2ee7381996cc42e4cc5c44ecc14dbeb4df3feb99f23ca27ca0193fb874f5da057095497a613d9022e64d4350b92d8959302dd27ea7ce9b85702348bd9d52d21f0267f3839e4d6df93a84fdf54c29bc9ae49fd56ffc1b59f75023ac0ab7ab9844dacc84086fbbde37b42c01ceb368be13f933cd13d960f6c750e2b3b9296b72463a0a825e1fb7e6d6871e0afa04d5e008350d89d9ada47c2ac035a19ca7f8ba379a079c9f60413e6ac436d3039b186bdfc35fdb91baec0d1890f82fd6ff7e5e59f1d7faa0c5e4e3b0e1e53f4bc10d90e8af6448edd420869ad00b4cdbb010a0de8453e1824bf7e4b73b87be5f0766069ab252617daf5f2ab830768710add5387ba2fcd0396768a97b556a3b1dbddcfc521c776dc9a0f28987001f28b5688b2b2e307b43ad1de7206ff2999ea8966aa3abea84bb6a12036d60b0f true -check_ring_signature ab396da5fef5ab6406157774ae993ff9573807ac1fbe58ac9068e25f5eec2e99 3593c2e7c1dbbc208ab9c5debd66727e2be11b61a777fce4665ca60fe41d5f3c 4 3341ba4e894c6fe88ff1a2244725e9172b6663c4b95808ec827eeb3b760033f5 0ab64b720218ba936b7ce7a20afd68d892fbc3ee26201414e8f1680efeaa5dd0 dea3e16e62bc42e524d4b59f04ab911a26ff19a9714ab96cad8e397aeeb785fe 7e3385ec5dcbe6dfa19a3b0c2e97005cdfb71e020aa633312465e4f6c08207ca 03abd9f2490b9726e7be5827b29582a2b31456838d53d6e7e415199dcdd778010cfd02419ec3c2123062d6b09c92cc1a2bdd983a555729a637ca4efc50a8340fde4487c39469996b7109a87ec5b401fa6660cc779c18607ade2dbebc991d5500424bb9e7f9cc55a5c1a2fdcac7524476d4e6b0a30836b89c4907dac9ab70f700d01e58c76c11a251842619b0464a78d19ce0f7213c85e9683d8c0530c6841d07d57e27ab0495249a681e2f9adc89ead729547e0f46594755b8ce89424380a65848cadad581010a3def5d040fa054e96990a0f76aa9d50b0cb92eb3bd2cc41a0fc59091c9520d470ac3b1f8e0f8e0dd46468a88efd9832687dbec103523080c01 false -check_ring_signature 3b4f1d5ec75affecb419325c2d37fb3a0888f9e68916fafdfb86af3e38b750be 24802af2aff5aaace116c0646c1d6980f995db6e1104ca0805c2ea711bbc8e63 3 b1405b617e7243635c3edba2c0c6ce9fb63a1d8875238bef868f584250ec6c58 26c4407f35d8374f0469ea0a85e2a55dce67c7f7516e7673358fd1914cc412dc 8c8deb1f91f5958680be9b17c71854b6ebf5ed92f7cd0e68027f46aecd89c75b 93a0e812e8db1f183f7a7c4773d0f719218c877d1d4a4e934b5892f90bfa310985204b3e6b7d4d4bd5d97322d9f394a1dd699f770e44dc18fab64e0ad2ee680c5fa08407b9adb578133560b67824b142b4ab35eade7de1484320df0a55046502716a495d279230fe4dedf8776fdb24031b7b7985e39279bf681250af13969e0d956cb8b69ae599824e1f32b79224c9c4a54081908913498147f0555efa7edc0377547465228c414fd0ccc76119b2d2ea89ba88baefb2238f0b8d6c372739830e true -check_ring_signature 0e58a1dae60b0958b65a5d24bcff32454664f3af541790b0a7aad821ce3a9b45 8ebd3d61a6e4a8c1d0d186b8b5dc9b9991660dfbb322f5cda7199bb5dd80abb3 64 c0b55fe81fab1975f0c7a6b28d09ac926f81c3f94a1031a0f2c731407573e259 4afca75cf9b3173bf8fc1766d5cb6e417dbd9836e882e656ba0e0d0a485af6dd 2e0273bf427ee7dfe5b59e477b9f6bde5d8417ca90648cd5f84b07dec25d906b 8e3033b7f506bac463cf1940639ed8162e2beca0f5f01cd4dccee0298ead57d6 cef2255dbfcb2cdfd354c2f2f84d578851a776e56973f190b0e716526454e621 69f052160b5bb061d62db4e8b0671b79620b0ad60138826c254f618a9970c018 b94317e92a1fbc84bde21b288b59c725c4f467b9c0463846a7377e2bc057e53b 4015810a46117c3a1304e29411ccb0ecbdb7d0041819473addb83a5719d80ca2 a68757d9d5af2ff1d795c6b7a57b8f352112457c19b21483d9ca47067049dbb1 d14ef297bdc73c661014c898dc601d0a857aab477837a887327b7026ff8cc490 173201230f111fa0bd3eeb8ff2ce5dc84700c71154ffd4c79c745ba080326006 99c4aece12477bde003e316f1d63d21bd5defd7e675b3f426b1cd4bfdf4c6d1c 77f2eb895cbad34f5d66d28978236c98d48733236fb79dac5080f661a77e3fae 62c9ef9c9b181658653a9a72179356ec76729d59f48ee9bf02ab42f39c38d504 d862faf98153aed5df6961c87d07e87b69286482efba51781c72a218dc1fa38a 116ecd32bace20d2ab2737c059b0ae407cde4418e51f7799dd99ac5e19250d55 75660dca7637164234d1b8938b793badbb7dfefd752d71458076cdd25ee956e9 08c76ff16e2dec437cf9d3d95d3dcc4d8958f5b7e9330d60188729d7aa1c17fb 42efa80dc099ad86322e29c174242f2f205d7d4c5365aeabaa18917915f23daa 3ece4ef87348800f616edd0ffc1ed42523152e5c55d54254171d0ebe0ade491e 5c8da075cd30f3f51464d2dadbca9883fd15e69873580b6b039dd72476a2e4f8 954f62e4e2961d99907acc65cf3806b8e3bca18be33d97542439582eb17a9d91 1141685a388aabcc09822de3d2c0cce37621b0a002d99d4abc330e8c99e18e96 bd12f93391afad10aa7014509fa648d8b19c3c1243832e8cc1b8c9c40cb1d855 d41a2c0d98f0874b273416dabda1569e91c00b9d417d9521e123e348ffef3c52 0d44e60656396de6c3c31f5334f9e3d6f2af0e71d3890dcb7d61e07e4f44eb36 2ceb2511f01b5f120b50b8db118fc2c7081155347215d7b046012ed1ffaca018 97f803c8ba429b7de76fe4f3c569e9b9eafb64c7d8fa1dbbd6c709a9c466050a f8f0a9ea9a5a870851e7ec92984da77529404535c17c340f2b53f8d154797c9f 170b1300bb4a6450c9af5b8c61743fdcf324568ae4d11317301574a6be80ea5a 54883e44dacb182a009aa1aee3cc6a66db0ee6ff38feb64295134658f9915272 73da11f9e2488c17fc09e4766745bff1fbf4983465f4d96084878c738b06b2cd bfb7aa92ff0178f740489bc6558b82460d61f30cdc25c4468b337ce80b825c3a 231632fab530ca8a9b8f3d99530eb3280fec1d10d3b298735fc31ec6f616fc87 477cd97150d6519558e442df3e0300e245a0a269ec95d4d26920254155211caa d291b8939788813884961e2b21e86a4f11084e1e9abd8a039710cda949415675 615555c2d90d5c85c4b430175d42871f5101d6fe9cf27ab3361d210460ec1da6 bc64fe638fe719bacd2ab5eac7f73c723aee2c47bcd5ada2b7dca7569ed6d0bf f5cd7ef6a9d4bbf5793990654ec6f7fb77bee475096f544d60ace19b0c63dcc1 62424c4e6d41e81966979f5b9464976e82cc6bc8b06ecf1b3d99f5aa67dc3526 6c040416ed9d72f7b80ad751cd3feff5ece30a2d7a67fbd527949d63f2240a84 cd23225a4ab5cfb9839e28e210f13f46771770399aee90a59f1fee3eb4967d49 81caae034cebf87516fd25fdc7fac1552d02e0dc97620d1f25e04f52fe4612ee af8a43ae765ec50bc2a803e13f9687fc87194257553c047dbcad1f20eb79405c 3aa3c8f66df8c566d1cf0650190ebaadf45d0a3e0d6fdd74d1032a3e962f0dc8 ed07b007dfc03bb045c8a84c9562b2f27e084fa372aba7fe3edbd1fe1c3804c3 bafc42df978d31f50773ded5597709b6d4a250dec59e22c11b50f1578bdb29ec 4e7d190fde1914a9f5a9a93b38acf37d85f373f795ac94cfc38ad079d95b08d3 8fe1fbbd8a70b01a970f4afa6f87bb4e002a7f0b074fd82ce3c7bf0844f63ad5 c201ddfb7d46f9d7561c86b63728f41e204b1706a52f644b17305155cb287223 1592bfda59fc101a9cc971d8617edbbaed9263a6dd41a83b4d7c170aed926550 21ef61033b9ffe7029659f09cfe11a7370ab614bb1bd71ef61109c697924c31f f58874f5ec733b2ed7487e5b0a08625c121e9de654d7507139b390a27a24460c 7c867a73b4e76becc1c6293b8bf5c41de91b3e5ed58511da015380aeaf24940d 5a6af521f85f63781735481058f7b6b38a3e7b7bc24d67c1c67f30086f0075c0 f3d7583906522329ef17fe7069638101e465758c4e47313b9f57cc72d97c16fa 2c57add91eae939f39d5f402b73dcc3d456424a1106202c5db6f45ee7ba33eff 635458c35478f3683e380698c878f44ae9816cd94688616503da2c9cfa62bf4e 6691af77c7fab4d8afb20cabd97809f3242bbbd835bec9c0da8ce1a675d45885 910cdf3a80fa0b4250f6666478da66e5e34c33db68e0bcf792349b65e60ff50e 060ee23f790139a620e28644f1f25d68127ba97fd904910b9d1eef91907b086a b3050ac72f9dfe8ac55193ab8fef0898722a390eda36b29ed609894f3f2d0b3d b79cde6b77791587af70a5f619910356368cea86eaa09d2fff7282e012d1b400 f64be348dfcc802d1caf6dddd41bcdb3d2492c095d8c6a0010665b67ec71e944 5a0df319fc0f51e8771fd3a9cbcc2f22c705df23b950e2b6b15f1c1534aa6e0265c6db8c72eebba3a282a4b4d81a682224a9f9db7a6b46793df3a27985fa470cf89409f973b697b21a924bedd150d6118db2587b937a77647ebf2f90ce12ef00610b94c6d1e93b52f749e549591cb1b027b595c4fa9957f8b54b1c67988e690ffeaf8c61b3420493f7ab9a582691f11f8f13b107d170cee470895c75ea6bb4036d52bfdf2e152aae57c0fbe26133406ebdcc54c3931166f57f33ee3449669c02e274c7368811ee3754dc114992b4a7e067ea718b350687c1eeccbc90c45e216fbc310a7f69d301f764b415da1e21e7a20e35e68e604b54e37e2a54b09b4f680aba51c77e0e7e3b5bd826b1e1f6b53f0f4b3417f63c2b6d5ee6b58b22d3f55e05ccc2427c256b3848328431011703d6217d47a52263a4e813341c25c0f4c7d9021827c6159d1db2da44d6512916063d8763f03d04f58e844f106f29153aaee50288a09a8ae3d4cc7439ab1699a39a67275c123d6b5b314acf90ecae6e0cd1aa0da0e0034c7ea52c8e9b3d980be706089be45249466f0c14ce0a784581264bf30f63d87889cf99897418972949db66f21da338651c0e79cb90b6cfc595c9cdb10fd0d1131f4d94555486d3cc76ed1388968549e3382405adfa59ef972fb0117a0d9a1501e6eac0ab18ef9cf17ec98a76e11b6e01f66d42fc003e4baf270299140bf97582ade68b30f2db0f6f2b69147bbd5364200e4a790fa83d732a4033a673087ad74ab8d8df9de1347475e0b84333df893adcdcf0927dbd3e54df7df7263202e0c62e972faa960dc726c66983e7a37a2aa4ec00b1a13f79c118d2971b8392097b0aaadb5759c21a8f0237bf845745e7cbeb6f544a4b44da5124bbe48e60c0007b814d535b854a097b06d7a9440a823a55ef23443baff4b4b2be70ffba983905a4049552e408657ec75d6e8bab33c1dc09d0a0e0d238382fde7492ea09453d048b2a2d40f38e940562b1cffa105ff51be19b1cd536cf42b3125af03240804e06fe5cc7e9b3f2be4a513e2d2ede0e57bb4d0d7a4dc7ea515639f62dd7b852de0bd86ca5345e2fae412ae37ea4dd0af97964a9e0642a8601a864145fc016ca210161d31239df93cc3f03825410fdb481d4ea533daf725e93304f9ea962cd424c0f70355618c5ea2e9842e13dc9547849a55ab5edb6fd055c2eb53dd6976e2c1e0462d8d3d400e243b36f41d66d0dbe715b2187ca3f0313018da8b55a71a9582e031961f2d254c81f7ea8601392f3a061c33ce11eb961320091d89b28bf81a89808adf04d90e3dc872ef6566e051b276acf2771507e42c6144b9432128fadce670752df98349624995e82fc101d9bfdfde203de71d2d6df35cd16ff881db634e305d11b17043d8e9c23fa9db26809da276187c18e23cb5b5800d2bf936636179404d34947b3ce7faa268676569a9d1745b1ab969b3fa083a1a0e9655adf7ff9f809a4d1c5aeea3d996d52c580a926bb53f59ed4714d3cfbca7477cfe073664614073b8c85dccfea91448c4b8dc37f7e90684dd54a90d34e93d41e91f7274844160da65c7a846990ebce6018b18462b68fbf05fe5f7b83c96f0e0030ec1f7be9a201d6d1a9ffa7112000def9a8219bac45422efcaa6df54636dad3e6270a2124b80029cd6f7ebda5bdb9b0bb80555f3f0aeec8fadaf7018c1659f50673a6488f2b02e4d62d54eb2e9f849f634aeca83b9789d8e74d54062011ef6f494763ffe0900cfc6cd9c09869d1ed30cb157de0008b755b9beb13c4c3260675b7b52a7d53100392ec7666f52f0215d008f1fe9113200efe41d74751d5242d36572240ff63e30996e206b647019f80014f0ad8f41bf414a884c8fc83583e6fba5dc77ba491a3050a5980d0873e11aece4dbbeb0ccd889c0a09a43f13a732b1dd0382cd4eca700660ead8db603b5abc7d6bbf0d12915ddeff3198d538dc5401a921b2edeb4174084dedd78901d632ad1eec33768fd938ff04818091edede3e55ec4ca7dbb1e1a075daa587ba1eb72dac5f1bbf7c1bc25a0af1f5fbdd4b8f4e4fff37c2751826e0ebc4097f961482e6dd3880b45c6aa06663a8eefbe597af8f1fe20df4f738d72073df3dc21080482691caa3966af0d195a1ddb922c9c4aca5162f783e73532d60568f40a59610f3f08ad60d4a5d4764dab5ef5ef286f34d9a1843f2c3abbc4320dcf75a80f5536542ce5f598f31dc932e4bb2793a27c57c5959a5bbbdabbf5e20eac1ca1e22ab432ab0ca44928d5c9380df7909a04c392bf3d00e3b29943598e02b006cce0e64b1182358055a2b97b30769a06d01ddcd708a6951e9fafca8f3d0841da68c7043f263139653a96190834fad943a1a836220e207b78649da2c2620ee2b11cdb7d3796b594ab8e005d0dc9cd33aaa7325c54b5d2526f991a3e946908e784796043387b4fab21a716b6a4ba0e385b477a30fa0fa8cbac571d0f03c1041fe06753073b53b4baf2586ac9b3bae450294dfa5b272d2a3b6c035c9a6888015df2c5333d15469fde2efcb52f645b6d659c9bfbcb998867506a5847dc0c53086ec42bee9f2f3c4cdb51d3257d891db4a2b899b4236f7ad9a730d0682a998d04a3a925027831acfd6df570fa170e627520699eb1bdb03d58cc49fe544da8d107c4363613153294b1f397cecd994ed3be8ef357a87ce1ac5d367b69661c447c0f16723e11cd676373bb5480e24f5a90393b9af46dd0076b1e9a7bc8ddff6aaa0e4376b026785f2f7e1e07ee6ae0aa6e3c2666395b39e579e282105e21e134810ef7017b0b6091f18344fa19f478f6618e5ea4fc95058e3377ba53f2c858803e08d6fa6db95bd4fc1fe9f53e4193f26d244a4d7153252592687a9e2b2c95722a0181a8f206f7cdcb9873ae643f79bdb6e99f9242122f1616832ea8a1192265050c41da0a44117cdacbbe0d64d3555bb15eb55f450bb38d4678cea4c6966be04002f0241ed8ed0f9220eba64174136fc377e4f5cda33a0328a81b2b7ce4b3c3f802e92cb152beb5dc9d8d342e479e68bb8624efd0d5a14c9a3fb1c21e3636478707f8fddfaab2f9ae050dc0f89db276655a305b59a64f058a4f1ddf576c98fb470c41a61aff29863ee6c16c8ffd8b8f68c8a61d01dd8d3f99b065cbe9f4ded9f90b8d571341891459b932ea499888ef0e83847553534a7aebd2de15631ff307450b76a5809a4b0ec728f9977f5f9fdb42fd1980ea5db1aa25cb99e794b13fb3810a08c21e4dad1efa8090b51936891dacb37e1cdd260c5bc137bf69e800c7839009e89ea2c91b52c6ef40aea0b8c9ee417f58278048820bac770451f1038f94f1000d6c4083eb87280c257dc03db7cfe47005def0f87a325528b17ee8b450efbd06943c357c59eefa60784c1121094b9924a01d051fdf73a409fd89cf138287ee0b6384cbc72666c78e40b18531432cde85e217b9f35977a422cc526163855a230914059ebcf4a9a5944f1794552ba683add6d3718dabe5e75e8743acc220bd7105086d14369eba29887c52e2dbb67a543776e8e0c69578ceca98e9ee79202a470b4b4a9ec7748046169fe0306ba153af8d406eb0b9956de0167b01a23cada9e906c32426dd29afd8207028fdf7fb2ba993c4fae14c131267a30ace5908ec34690a38838dda5df7d9d712e9c1cbe192c6a340cf7aac995cd2de7e58f2519ae92b0a02ab6270e76987c015301992b52aeb2ab8ad6e89c5ab625ba58a8079d3b11e07e7648396d10059daf4b7569d6dc4c876e46cf869163098d9f0617c423617690360f664c4a1690b65521045c71def54e3560e7cb4919da2ac7080bdebad63bc0b7594c39aea1a900ed8d624253d6c4f6fdcd75937a41392a4944e3b61203846068aa26458766a75ade3273878fe125c6f95794ea26bab293ea967dc79900b1e086740e61be3db465a962c415aeb0d6ad6742e843bd932d8df4378dd51daf1b10745a9ae63b1c61337b08ab9feca8325461915ffa7040ae23e078ef220968a1f0c93e65512c2cd3a542763b6be1718a49833f0d17a92f29ae65d737de4aa4f5e0ce07d2df341dc5ede98934976e5dd8159fc31f0cdc8a78bc1d90372f3cd4f9a07b490747152b39f2bab10f28370648a3c531333d7ab2e85310c7fc23e207eb908c8b988f2e72ae92c8ea464b954cdf718ee37c33ba4bb845190027fdbb8dcc50139b43fa0289758b44d4d911b126ba8dc80088a01e1eec400d2fe1cfae190710be8558d92ec254dc0ef4fd8efed8d884bd9dc3ef0a122327db6be011c2f18260e78578add8f843330b215a91f8f364bc934fd7717486243876243be261fd1ea00a43e8f71411d8f0734e819e073aba952be0f1e1f12c3bf653dfe466e0380570685c287eed30f4bbb846eb1ac98031af9287ef88067c516479ac01848058e9105053de1f39d3b4a19693c7e8cef459c0fb27c228f23ab0f88cb61721465f84a0cbdcc54d4fb2c74eeddd3baff070e43dbb9bae2f329168086ac1f8de133cd8d0bbb02db4d2d29a92a4150c26921c14ad082a7ae6e76cbad127fc0a574cd0c910f92af7a07e1329a759634558900d0879a511fc19cb61f0603efad3b99726b5004d8a655c8109ddb582f1d78783a7c3ae70262df4f6392404178a4a5ca9b0e810b29af040eebb0a977594e2eafeacfb3a7e2a90934e14d6dd5b41f4b883453950a27ed63540ecf7a7343189e82741bd7bc23ada82ffc946fe9d076ce22f63a7902dc06fa9cfa3c74e635439f4937c35bb20e5097cbdf2481cd966a94fb47e6a6032ecb01ad05603c207249e888a53d60b714a6c94d5f4ec8be95324b2a2a159805e13b3c070a243d2394bd1c5e8033f2f73e17b4aa8d158cb0915a9458c983930a01484e6c0e318e8ab0367fb733ab782779f44019c4a4c52be96afa87d5918b022df7bfefe2fb833db59e479aefe8c301d3f7b19531775d4b442467b49aff730577fed3f001213c51491f9c4c5ba249a0fbd8397af7c2a8ed0e76a4637a1e56024a36e7901824a7c772f2c2269a461f65614b9081ccbb7748e46ad8bb01ec2202b5743bbd129c2f0ab3b5003c8aec0368243db289a0e16a10a870fdf37dd7a7075cea216447894cf8751d5dd453fa9d50c98c215e06f6074efdf62246a39dd4099f883a87f8e7a80ef4f3233d91039f105843d60ab6b219e234e37911ea6c3b08159e2f7036b590a85be4619ac4ce5bcd2463213e0cb1563e53c67d5d7b663e0fb5037e77d99a17d94516709dbc7cebea63b5fd8466ed9bdf603db8d97d33df08d41efbb13bbf9f82f1dd33b63665e854a71a66208ea55f40952d0016879348097ad1984ebcc67e71b9e7d48528893bc142c4a82f1c18321374e61dce81205207053b8e232bd813e3cce87099ecb22611db49164d50eb6e5f482de2851fbc7d0c2fcb056d29ad6d9517e5b34626695f59efae05aa1ab815ee279481746924ef0884f465571b76242292ac02ce5c76c22fac9df1f2aa9e6177b24480cac21b360a65d7c12aa84f206075652dfa63fb106d01f4989412a24162f32756e3e956a60fc2216f7e97304b25512bff2bb25351ed624173976f5cbc5b0b410b3cfee6f804f528827e163fd4eff3551d860b36f878668e3cdaae26cab892da0cf1ddae9d0c546b6417343026dab87fa957dcf7b856e0dd3217c9737d3e0f2cb7ff40e8670b5460389f84438f16559bb25e7a2880d24dd646a113b8520622ea439ac2d5710a6666fa451c555182744ab6e28a66cb638378863da0060e646ad0795910670a07 false -check_ring_signature 01da08a30567d6ecdba828b75a0ed70ea55534a036a1b0ddec8f6fd353ae5279 b511fa7900c291f6d22061d530b656ac81b822e8543c44503f45cb623894c472 7 6071f5d947fc4c0da9a959d99bf86cc856d0f32772d7680adaabebf5bc761e3f 8abd29aac1520cd41691383b8c0fc721d7995d3669513226922f4d33f7607879 e290667ca6f4b840fe6a42d34cba4990a7d8c68fed8557d1b03b528f2e767462 98773baa374d7897b4cce59d01fced1f5b30ef05fc453f2d9e21cbd969000388 5e2702ab0abf228beaf0adcea36e7c6f725f5426267f8f7dcf7e4c407214c156 e1a745b7b1bd7401324639794e3c0e18f38463bc2424f0ef5cafa8cdc6194272 8f2837995422c834cf9927757ed3163bedf649149515ead9d1002eb355bb1a5e aa66ca8733718ec685d8818cd6ae0cf41ba79301fe8ed830e9646d526a760204077f7d031b36ec636b41b05f2a3b3becc770e4605a5f752b379d97d7e86a190b99e1a6594ad73e48ce57a79a3b90109ed7c0230e23cbe321f4aeee982095dc0e8b54c3e03c9bc680ed616d00bcedf8f557b496f99015488e6593ff13c0dcfb0a846585a1fa03ee67a4a0fc57e2e26fa26ba850bb13e612173e40dc897eb20301bdea9fe62fa11ceb9c604bcebdbf176ac414db2075ef7e1d5ed8283c7402560b4fa71cae6a93ca18f99414b12c47989a3e5d98716e74ff3c78c46f662f912a025768c14f989835c12ecec116d875f2ff941bd8895ec1adf476e0149518de590f2a818e87d312c00a2e42cf20949d6cb45d3f720e6d17b23f06209b7952afa0084faed07bc5040b841a1dc99a0bbaf14ec06662e2c3f3d0130382afa6a57641087a2c1f98730c547ca67cc03aba7f91676fa704771cb963ab74c68ec0356aca06e852462bf007d0802c60bf71c41a35311af20b7212ee4eac1c6b78a664810e09a9ef015107bfbf0becaf1a1abdcdc112458d47bbf37e0f1e671ec57921ebac0b3c43411fc23416bde0b9b57665803806d3b72c550e2d1893df629eec292c100c false -check_ring_signature 94f729667d6e6063bd223a5d78f085edd8d6deab8e5bc4fd52eeab3a696c1929 f67f198163eaa5d4913395119b12a4efafc4e019f7be9a27f89b7d0b34c09e56 1 c94ae4c840cb95833a69ce4626448713466433e6df81196217639c54263b42e3 bc7fea307918dc4436261e9d4bc546d4f1e7a3d0156d61beb75d4639b6d8b5dec543c7e019fd92f51de5c6a03005a3fdada39e3797ab9c968db6242b753f7100 false -check_ring_signature 49cb0ccca07b5ddc062be0f909fde2e808bb0a1dec07c15f0c6218d4233fe451 9db95bef2e2f1df15221f811c41df004bc73ad611528d0c7b1e3a459dd3e666f 51 205238d0211623437a07e316cb658f41eba7f57382d5b764899677b4326553dc 73b9eb65369b646227aa3e70f0c2357ad0ab2843df4fd18da1d9427fd21afb33 87d0653c5d729dd89ebb0502b97b7648016683865a360abb7224ebeff76446d7 b2645fbe446e2e760e1011950cb0f1f8502c01e7a7c107d16ca9060c9b558ea6 e20e50ab0afaaa3dfe596a44fe2382e51a673545f3462f6cc2b3015edd7fa59e 787d70562900a31aad4353e14b8b0e904a697b5bcbcb4499b9571576ad9c97e1 e0116bc6fffafc870550f14a2456bc7a4f440f8b2a251408680316ba457dd6c7 6c478a9bd4bd198b8f6433aad4f3e628bf90600012ea377990365e59c4615675 2fb8cb8788d6b69fcb5c893398e4842da34be2ead5143c89a33524bcdb98e81e 8d0ac82f5c21a0817998a23b4a3c17a7772b54267f7f70bf122abe1f24722912 c797ba58cb324ab46de627efaaedb253d8c6aac72231471a4568ce61ba58c9d1 878cc25a26676df88b37b5e257e4f923350ce8148391c498e57e18e84c6f0a4f 628e016bc577227dea78cb96ae2b95dadb35611450fc9a4829ddec9bde68196c c7804b8da739b79c53eeb0a3c1891ed2b0f95f8ff00774c16ed76b8cae2b6c79 0ce3a22e8c9973cf8eeec3bff3eb7914b3e4318c79d26722da0d7e17201da97e b9b43b14197ec1b448f012bde49903e2d4f6f74680a2fb90144fda09e1caee43 44ae0d9d516f3ca64ab47ffbcdca7602f7a69047bd0072d75802cd2b0da0b42f 48e5badc8822eae549f3bf414c7560a798211a02d1f97512dc47262d82807d75 9490d09126b4c59a62fbe92111fd0632f5025d43084c19e7aeda849a62500c8f 8836736fa154f1ba4934d50b4798b26550c33b70c9f87feffe455df75811e6b2 e42dbf1a903bd73f3fe591dcfb1eea91516c0e94c37ab79f923854e94b50ec28 2184c75255459e2b6aab1ac73308ed492433468be30284d7da724583d42f5cba 727c4322a3878410677ff4da8857a2d6d212c0fd9f62ac13cf4467252d911b7b f5958398c277c4ae8fe909bd40735e3183dd6340d7072ccad9f052d429a72107 9ac7f2d9d06a2c9c8212dc8f37856c7cb1c2d475d0f18e11566d0cc516e68120 d9209571776f6d1746efda863e0e031f4c11446e94543bc23fe113ea60f88795 c4294834884e64df8e75d4b87e644bcb6dea9dccaebdfd30899cdcefbfe221e8 49f29f8addde705d9064e0cb8d2cea1de4f99f72ffa46499d4eb09da47e7dffa c3c22bd945636b256ac0e9b68f66b7602d6a61d6bf59610a624d923afb1ddde5 583f1cd8d1d7310a23ddb92de07f455ed5ea6ed26d4abb4ca2ab3920bf3acc50 218aa49b74ec5df02870a3cad8ac935da5e1f70deb4bed3d454ecabe07e873c5 dbbfb9157259838621f2dcfeaf4addb215467ba4c5cee02a01b21a6de7cc08c0 007997b2367ccc32ba6a2364ee36e0e8eb5de1344230c6d76a3c7325eaf0ec8c 67d711493194189a2dcf0017633e5fe6a8cc3c299771f1738f716565dbfeaeec ff7e6f3eb64b0c5b3cc3187975e6443fed7479304aa7b106049784fcc208a624 a516c2abc15c5cef5d80f0905f29b6f5f05dda28f17c4c850ade0558225fa9e8 57e337dcf66f53decd6bf4645851fb3d16f7f79a84ac28b9fa77f6ae482c318a 9dc1cd8c0f8ab4e3af3db17378dae604da59ee576931c2699b933a5133958692 6fda7123d3d203cd1aff58b234566f6b1ff953c66e84c6487dad3ea5fd26bf88 439f2efbf32d70dce3911604a704f3b95573efb001b7758e3b48fb3562566791 34ea1f27476386ef2f9b35918c8252f1a053fb8d8f33ea96ad7df4a10c264d27 c9c298a0254302df24425b07eda8477c18e4801ff7396475bf1cf99e0b8b23f9 7e07cc88bd1205d54fa157d105c676cdbbde931a8bb82d735eebb2d8595f3706 d612d4d376155d248f8fee97d4d14f8e1efd1e26b9a3618722f5c742f75b93b2 fb31ebb01a12e6760d0c2d7c2fa9aa7867d83a43dd13185b0a9dfdbd7044bedb 12d1e61047b71ebe737cfa9214c0391f7d762badad108b8220be73137b9a9232 758ffc63594b595c7586738be6bd74968237affddd94864791c97cffc474c58a 4ad946dcd4f59bbbb889ab58b54b99cddf7a71ddf4c84cd44407111d3fdaf27b ff27be7b0cec641a134236b75548af1afac67ff3db58c460586df2c6a39b4537 7bcd8dbabe815bc778b13c0be4bff41f1b7415e34921e863e939de61513fc9da f452a49033505494b4ff528b87ad19a2dcc644c8936e61018d3628a47f9a9ef6 a059cd3bdbc1ceab1f0009e346508d25cb2016af0888e095c98acf5a5b27f706634b8c399129f1ba034d6b8ffe048a084e4e84f3edd2979ee0d9a6d71991c40cfbb63e9d3784c185909478d4f157c0cd317951665ce4d99c607de38efb6cf309725156ae29f8f8db3fec21ef9cb6d50bf62cbe8c3b1e372f5ef78b995d03e80d770ef83fd98e462e62b7dcf1240c0c16843c16ada349b8031cc1934bd1b1ac09e39841afeaa14f155faf78c20d17b5f65e7e134a300dadbaa8e6e6b711591f0eac059e0f44975e5cf89559819efec8cf3e4f12035c8af163679857ccef5385057174983bdc515af89299802473872ecb56cf0e0a839ea9277161a6f74ba2ed0e2db6366de5c39a92ee41d34c3c5bf14c00f6aac9d6ce6544ef6cd64d8f513b0e9ef46736c5a73d466f61e8a568f5a783db971fafe8cf7748619b69686706580c3ac6e0aff6a9607980de84740e782656ebe499c630aedd9d2ff588cf12bb2f0705df8ed2341a0f2bb0e1cdccae0f20512b9d604ff559f247de6b39c2a34dc00640280a92503e6ef183c1a2ec3bee13058c33e7081b410fce66642ba03c5f700a404e36d958821451c94dc269484d09642c4e690c795ff58484cae18f6ed0f80a4311a17ce0e2ec4a408a65ee7ce0861216dc1fee436626a0b480af176178570499b6213f156aae56657d6ebe52404dc06fbc2c5ff6ef81354cffe927bd11740a813eab7705414773c720c3fa9ad1ffddbae658b2bc7b5f873debc1a334982c0a6c3cbb94a34216a8fd11366da094796b0dc44d9286135a31863421322e1fc00f2491ae322220be330406cf0cb4e2da2b5826114f3a7cb9605a71f3b7c82c2a05b9f67600a7980eb4cd0b8bba10e679922940b627ee91065ce72c44e04df63d03260775b9bbcc505b2e784a78cf2d196397ba4ec933eac45b7932179f9e972f0d06903bcce85f007671694e0a2ee57ec1085d04a1b405cd31d18a8e9280064f019b8ed6ea76014f2067dd97b95efa7b8e2d230cf333b5f2de7f2c316d7a859e0a36635dca8792af5896e3347e1268276be17181f5b29151d9702fff78c458da0320e7006d3b3fc85ae145387af9681bc943ba9c73739de427e20b8e4e3627ed0d296c8d68243b1de00647899c2857d39d68b4ec4a7b51bc8d7dad5ee8957bf6056db60acade510927be5c1a661702ff5dccd07236770067e34f0e1a003f5b200f59b464e07de7d963b6764677a8b0e84269c622c7cd090e43b6df58242d57fe05727ce13b2953c3f2351d0b8898489916365e6fb32ed774f324120bd7d853d40854efcd2689a8592df67cc0ab4a419ec6a9529235660588bcea8e76851b93ac01cffaa004cc2588042583478e369395905552ea06be7176e61833635431f8590328ab47cac137ffbba99f6bfb0c144d1fb7ef5dd061d8a49ac14839ee86e41600421549bdf50c4fd807de369c52d6a50b75924ee90ef02fbd77b9ae594e3e3d02e0bf0ef67766705a781aa41242dd40c057e2c1f41d20127f9b939424bea9490971efd76d7f42b7cda3af19efb6efd48d7f126e65dfd4d5c0af18627efb53db04ec3498c57accee1c38948830b35814b758830319f996f167bbf590d01bd3a208183784512a648d1e0c867eed72e7938e8419b2dcfa39efa578ab8debe0ffc1092e94b324be84f8eae72bb4377f3e0bcccc1132696278f5c3b90994beb8a8a00a7ac128058b605f86c90a50929417084035ce409f38bc416ec2f1a58af7127e0b092d126eea0e11a3e87ac291c6fe0f7a850987c51a9f450ff6e9966a02b45f06dd67fb02b2997957f9e81f37e19d5f593cb7c20724c9a2e6bea05fc72b656d0a899597debe0fb3db6786d56a91ee84f42d8610af76e1ac740b8db1d8941b550458d968ca049efd92eb09fa410bb0d0673b3b7951988d45aaf57c7c64d3aa9c076bf97e6ee588443646c807c6d5672e1ac67537abba9b1eec87096e98e43d92058e8ca5ca75c627a128d9b77fd8133093213fa8ca7137e23cbcba17f12d6e9c059c9a8fa924dddd0d88d33d0eba66be4d7214fab9a5a9f3c64502d170df81a6031536c082141aeca8277a5948cd7ca26a7c7e01c74f5a09240ad1ddd489250f0a9259e8f1e41919e4a8db6f60833fe19a6735d40841c58cf5afa68a413c1c600206cbde58a64e216b88840a1c006cbfe0c1de0ecd4c40d29f381cfd00f0f2d90e8b23d1ea4a284387befa564a200334359902b6df63b179de627cfc44c65c6a079268d724e9191835c8fa1fed14f92fcb1ae0cd574959e001d528c9b7eb50640c55f1bbfa9384f2d0651b4c2ca205aa775416c8990c4608e29698eb67deb562003ae3da640140031c776e443df99b6d612d34bddba718e7300927062b824d2d04feb0a65323752358e1f3a4813fc7028e2934cf554fafdcc87bbb3ffc981bc501f2f638329478195aec900804be8b8bb40c80a8627debeab1a68b851e94ade40258b3a795563f2915fb293d3f576b4e028487f5eac4cbbcf6929eccab39cbc90d50ee21255509aa12e6c7c73a86c484776fa8e4a6bc23536a38a93d410b97870256995d54e16b88c15912a9b706628a1817bf393b12ff1137b89781528fbb3901193c115da62d1c80377863134231800ef244124d76ef9402a146e0fb0fd5310ab5c29ba55e1747024777632f2e6f28512433d0ff10c57bd5d21e9bc1ae66a20a8e99f475fd3c0ce3918896ae892994586341a31e590d54615f61af229f66160df35e957c5c5947c7c478fda6db36f4851cd5d05eb977dd9b59ffccf5bc3c240e7a2359ac0f243e4318c0e8df647fb66fc5153bb3bbaed05aae906e5edb37c00da441e81a5064f290f747897418e6885865b0ab875b82b7479a91f7ed4a3a8f0b7bc4f3b7336ca78cc32b7d2bce9ab952dd1837f18b2b652ea7971cc5c58c69068a639e96b820ffedecce845cb3c0fc8f7eba18c0b6e964d16a0695176198eb08222876129518cc2e467cfb6b0f0ba1e9d74164e289a3ff237ec3b07ba851b50a35c8c155c78a39ebc3172cf7caa3d7cfa3a97c437b79d8f1aec314eb21b5a20ea7acf7869780fbea875514503e74fb57ef8cbffe55a4742ab7916528ed38238ff1773d6706062a1eecbb70e67aa39b510cb1208aa98d448ed19408723b837f0c076f6d6fa81224baf756fa6a9be7b019627ec0be62e2d3e3adc63ad326bd3a04e88bc1d7687e37d74cf5b755ff735646c7575880badee5ba3d46d66c3b1e6a00987fef8e5f4489d2fffb859f67b09825c12c3b46ab933b1c5c7a14b84651040e886e0dbb3e33e1fe37a18677d5eb15a594e2139546301126a1b86b9a36cbca0cf4d558087fa2a73182fd8093d4e53ac282f7d4cab8ca024e9972a2daf16c940b7e2ecc5ffb68dbdc172c224d55b798f5a9d1c2c742b855f54ea17450d0915807b290d995fa977466887d247c66f4f57cdf2c70a0bd6d212b46237db8d58d6e0fcbee307ac3b265673b4aa85fe9862ae1f4dff14dd347f3ffb3a05277b6c0b60db823a154cce3bebc9fafefcc1472791ca9ff205835dea88d567409a6b9ce790c6c3e4a6f88b08dc885db2c3d6efaedb9d844c8a1d8d26376ec3919bd801b2c081ea6dff5b84ec9c92a43a00040023e2b21b12fc215b6b01077bfb89d0428680a27065486e78ec8c39668ce41e266228d5cab67bdcf05f25fa063775f92b15a0bf475b2758bf19442df73618bfef55b8438133f8054b89ff4c6572ec08fe93b0c85d9d66669e839ef6ccda3d6d5f90804904a592fcc2884c08ed15a6f20b86209cf6915e2b0c0f3ea318eb3a59ef401a5e8eaf3d3b5d4987e44291d74eb15130ee4f0ce65ae68e111d11ed86ffa00f2f39bee89ea842657ff631a4be47b9f59009221855cd7ea0138926e273141ec767287d0824c34eae3c8e9cf4ffea7bee00fc17285e4f7d81edd68e4f946d5290b9d6805d80c9de9c133aa5d6e4c91136107fc3abd791a33472bda64cfcb5f46c0a3f9df3dd915242e3cc81b855d433fd70175d4d343b6312f561be9df36a9afa9101847cb0e553a4aef85fe625c6c7a460ddb3a3940f622483e2ee4da093ecb4090e275e7908b6bf9b6cd73886c34dd610cbab171317eac98b2010405d857bde0c6063215805ba491d398faff78b6d98f02787eeaed4d61ff49e312ea03632d416cf10c7b58d20b01f9bf0933aedfa4960a8119381f912ded1b4623c54acd85fb1b5802c731e0cf079533447cc33fbe7208b93c72d2a1b189d4b9c96c896b960cfa40ae1498dc1b5e01c360f78131c8c305b8def4ad327123af258488be82c8eca4b211fa14f5bd0ce91e3017512b783d0ed1a8c175b7cac170adc4780f9595b29619003852fc8e9cb8fc3985eee9038a0f49b06f5606d340aaaf74985a9b5be7391bad6f6b8a71528055bc93303da41103fb7c703c57d79e296c15bb304778e06ff55d00824e6c04431d88542489b29a08acdca42a3477db2c061c8a2d3f94382869f71daa0f3effe29fe79b67d991c206e25058eb736e79869778446954557db159567066b1ad63afd784b614526f5601c47ea755e0b09769e0fd22848efe2d4af05a631baa7dcec40f4a981782d62503 false -check_ring_signature c05f3c24bde66da6cba56739b7e994d13f4d5f484a0b464209090528110951b3 a38b3116fce718396a5feb383fb81ac6de74e38cbdd5a40b565ce6db1ebfb789 1 329427ba240eca464cac0c73350ca9df52a5478e540f9cdd9426ecfea7449248 b2b8c69edc2200915217f6c51162aa546f089385a82f93ce12f08c5e51c1fdf2f41edbb4a72f8f978d7f28bf00a057e3e34bdc0e21178961530ca1f6582f7d0a false -check_ring_signature 4be9094694f2dc8dbf6633deef679286dbd459f80e312a71fef849d5c55d453d 53decc83c09258445e164009a719826fbaa9ef8efab0b674fb4a4f804e44a103 1 5f095675c445a4d074c68467f6d74d9d1177f06b649274500d248bea0125fce6 3420c6415d320aba95e7e389ac51a593da67039f295848606a411743d2c9c1075aebf02ff7d2b404a6920a2b02adc07c13a3b360987f388927b3307a4bbdb107 true -check_ring_signature 8ec9b2be52ce8e67b2266ce7e69fe3c0e252758f301f1c1e868c4941431fcaa4 a5b5541675c1c717600ef5556a1b8b71813448cea72146be63e354aff4ba7854 7 4516af0c95309ddc08236f640c39a9da42b0eeb7632a95c69229f45b56035570 2a7c7ec2ffe294f0973366cb4d7efbeceab4de126b7abbd622b09323258913f7 08908d93d5a8ce574ee2cd1366fbc5b31bd9ea3c73b821d6fcf53671de2a0e95 8b07bccd91ce15e5157ba6bed24bf199c52e082e142c1d1e2d033315a9541272 e9ab69776267e723d42163187f540d4ec0e3dbf63a8deae4edce7e49e3ad1c1b 94dcd133179345528d30af01382d3b13d54a034eb128c203668f4c0087c1b27b 56bdb26b916e02a27ea7e7d053d7f9413eee31180562dbeb772a5d9669998a1b 5fdc466abbaafc5c08fd72399f543d5930c589f185781f9a3d67a6a0f6f52308e6518210544b15967677bd82e20b942ba365a17377706761ace051d0a918d3cedbae2cb219c1a973df39f13595c363a1176b27d477f46ae0f84fe766243b4506aa9f74d868797cb6a4f167d9793e4f9eafbfc176b333a820b93cd8eb1dfbe101792584731879a037e3206573af81b694a3c3c4d4a86cd441dab98a2bac50c0ae6797f771d4ae3bbc3c4b95503443a14df33678da9be66bc6b3d6bdc8995207020657b313d123b3bf7a45c39f81c63160ada34f2def245f6128d068ea8ad1520c49d6dc463e4f5f474649d2d63605021b5d47f1d68d11714af6c2c9d372ce450b93aed6ff4110fbf89155a6be66cbd1f61796551190847763c41b9fb6b07d29016b13cdd9a07cd6e1c91dacc68bf6286f672c647268503f80602b7970dc879e04e3eb4fc73c531f13504831d24acaa7f010d3051b9372c4233ae36e54e6341e04fd208ae3eafd6b3cac0588556829364f9b22e7edf7e4b03aa86afae5ffbeae02efd3e72b5a2cbdec4a98e0fa14934cf2fe01a894a82e5047c25bf35107cec7ae82b8d5ba8005aa5714dea3633cc347fd5f8bf43c950634791054565fcc1e800e false -check_ring_signature 148083cb1f9ea73600160d5a3d471fcba9e8264ea2073a69d4127e2ca7a9d9ff e4fb152b0cff5e5bd70319089443fcc7915eb7ffae81050b03fe9090eea3e6b8 31 5293c8807040bd7b31ed56fee74f968e4e1c2a79f45abc9e32c59c9d9734f32e 130d5f75ce9a96fc5966a071a19ef14fb07b6ec90ea1079a02b5a97627bb2034 42806138fba1cf220d5acb8fc5094fed14908ab4363001e14f3d498613c46eda 2380b651953ee12c29869a0607f5a70fe5c130703618e279dba704c5979fbf87 ca20d5e72cfb56f1ed874f39a439d7e635ae83a645312e26aee7592f484f4bac 0393c98aa2eb3feaf4ed0d46d53604480da157396b03843172a2e54851e3f93b 7b8fb71bd7de61b6676560d19583069a828d97c16ae040cfc68e6ba32aaab002 4accd30a210904f1df173d63820662f7768a6dde1c34d2baff76044bd7cc5e6b 7d28a93019976f0f6a7b85f273985f0073cbb98e1be7bc83d331b1b444cc4059 6cd39ce28eb4ec8ddc820eaf73b674de648318749b86aac9230197f1a2edde0e 4557f1b548461861e7f1cabfaba8820f7ae1eac3c348bfd1c1b4f17c8c05eac1 7b7890c04da52a33a915f392f7d1443ea4ea2298e4c6184aeb258c3abe260e02 a349edb224392df00497afe7be4570b57b7b774d9ec9fe0bdd7d29e4fdb70a7f fbd27ae97f24cf1170440c220aec71c276d8633a856cb76cd5a223b661b7cd9a d7ffab6170eb15a7beb8829bdc47a25985551c9569bbb5c2ae6495b345e92c07 814518c6a93a6176d625bbcf73cd86c9a17ab4f8dcf053af939cb2db1e230c19 40ed6780d8b3da1c141b6d04b88c32e01aa4aec039d5558cff397f51e471e804 09b31878da71e3de219c8b6572a4d7c7c11df1d2fcc9230854c6512d4a9061ab 855beeea70a28f34a8f038517af0a9681b8980a73ee9db296b049c535af8aaae 5aa08a67dcef8acc8a43ebe9c14546167c212e50ee82a3968c1789aa96f231d8 066dd0a5a9380ec35a3e94c58c5e71def606368d782557312fe891750f524fb1 19ed33d6c0ac1a22478c2b8b0264934a4892a5107c6e392e0690ae351163b130 477bccfebead71c4903d41cd10944ea8133aeed2e9751e946f67f2e5cb46d543 4613bbe851819a8c536ac24d32feea03725be40c24a87b3803d94e715ddd7615 3074d696a39b8516a3b6155e44944693b9a1b34999c3d6266953b69bc47d9fbc 5d7b93ddf6f242598cb32b1986b0da41274bdeab676b2196e6c48c0acad8b275 074a3ed981b08a7306e3cc76e0cee04ea4fe8f5bd7402754a9070c6e0a50bccf 3aac04b90f676e335583944ebc6136618cb714aae4a57c20a0215cd6d4ea57d5 c1104a75b25167bda2a0da91f2a26e9dc4d1a163a2aa6a65c19d1a1b02204a94 db68ef979f8be6aa8d233d1ff8cab25b74792be57d32e25fda7da91e199f7df9 f327e1c37edc573453bcf7d21496f67b4276e4fe638f0f015fa30ecfd3e4ee74 5ae5b71322140b6adbe10aad6c69dd831318976fb23b679865bef9a242974e004f478feadd97da725ba4c9b505ba19ec779457a9797d48d2ce6fc2cc3122c506026ca30f31634b3038b6314498e7edcb7747ee60d1ef338b180e12bd30fd720053f0f75747cafaa259a19c68fe399b4788885fd2ab7b7b6bbfe0b3aae81e7f0852cae86f22c27e52a14e9586c0f86fb8e800c3e8f041214d37d5758af1b75b0819ca55bedc4f44ccabf50773e6974c9af6f94eb66639623479b183654e69f204bb279143abc9f05a83386e397daed3a233e9981a6564e5a5afa6ca715c882f0675e9631d7223e6da4d2bdc32ba21dcb34ca4faaf971c699e59dabe2d96f7f30f44ab98102a4e722312651e18a53affd1bdb3b63cf10b410ecd2c0068a97d30010f775f83b0b42895de01a95a7b0b53c61c0effa4c47b026f4a12f77e699eaf0d7c7d594d827c49b3c19c544b0cb85b9df570a625d7f3b292721db260445e170c0860de2a7d5d0b2cdebad4912b5341b35762f157ed0616c171b34a1ccf65b90e7356bfc2948a3c7d32ef657f5c73f800634c0330f510235a952be002aaef90093be89eaaad41255203d26db041365400abb043bff7d2ab0b1e28f199d62a7c02e9fbf14b604221e230dd1d6d20737a284838c70504e2ce641199ce75b9486d0f5d27fb74d81ec74bc70161ee0e9de86ca197c3714e5ca3e19f6fd10ec278370f3f099bf47657b7f89acec8c267558a71960d58af77de9474383c17a3c447970ac07688d7a5664bee0e0f4d2b80a6aa5a062aa1dabce49ba6bdc87b9319e96f089c7c729038d59af6302cde189394f97f2e52c9ecb3774bca12160fef67f9090404267655d4be59c480544bde33eb4a08582d62c7adf8b7a9d694ee0ee7daf6082f3132bd4ac62377eaaa324d08b0218858a2b3e5c5504bdd6aff22fbf8103d0908208a9af77f844e427179785f1079b8a0f337560d60572f51584b53e9792000fc3ec4015b4b11fdebacb47392a70b30c1debd8615069618565dde0e4fcbf70eeb134c43366b9c765836f4b41273055dac32dcf246d701d155c2a441d269f70af7f9db08103b442c7238c795974f79e5ad98582966f37716870ffc2198762f076148d4c3319ee5c76849d2e9cdeb3c1d9bccd8e8ac8c98725060f6da3a672f0e1c966c7cc5974b219acf0028681a689378d406ed52c4c3d2c14b15f29944730d005c4dd92107fa1d18d7cb9c269eaf7c1771f1448b8d607adaa5af272fb91e05a8fd60294a1e6408a7bf9ca4ee95df62dc551a6d14143141fae9cf0a1bc35b06b62c87a64dbb7a4d582bddc2d3780c5837963bef7da19503660ecff68b934907ec2346e8f3fb2dadfea651ec871a459b49c1648ca314d0630ae448ad1c3d950ea12772980c48a259614b9936692b0df51986a31ea202045ceb4be6d2657c4f0f4b1c6107dc0e7f52e6cb823beb9eb92ba69141bbb219531c04435918a9e4b30c93901940b30693171b2a0d03c39fada53bba4c3d7340a210df919b7c31e35e09701f81788bc5d7a1c5cd51547f10a2870e064254f797b6a89fa7a6ada4801d0be32bdce76128c7f17233af0f81afaed7a7180bae648b878b933a572927a6f208402f49ef20634c89e803796a035ae85ebe1e28dd5619d1b502b21565693d9500aeb2a50b3193714a99dab97da1557b7c8c1783bde2699a8566d39bfe8e5b41088727dc6b796e9cb990085f7892cee80ef1f2ea4481448b318d419c6fc075360f5a18b24d2ba720558d5a9c21d61915e0681ab8f8a8e0c7026a8932ac19738f09f94ac7403e1531e7751605ecf2b019bef6333cb595d20f3092a396c255244a01aa5b3d6b0a1d95909161a298ce23e9308128dab0b7359bfb6c410ad152130d0a9c7cce8e3b7b8cad182afe610088231bc0d6943f582be4ba6b9b366ed54b16021032017be506d7f3f994b862e2dbec174314d69538d197a20118ba4a7979e60e45e2fedfe0c983bca60d537dc1b8181abe6b86648f4429c023eed52c96c4340ad467b41d93d39f74577b6b1a2430206ea5072a93a71546ed0e02f2e827645e01a92aebe8e2158dfc40f4e2cde2f58c3abaf90c4ebb726ba14174937627012c08e478210088f06d2426bb20951f682072cec7d19ec62f6bc5d823d795091e0608ec214455a8f18bfd33ac2c68fe9b37278a1de8d49198df8f747964d6973b0c0a403744a714480fe2175addcabddc3c9386f81cf21e4225eaee3dfc897ea3dd07e4c2a8831fcd2636dae4c3c68bd45609c0cead571a2321509bdfc1f74b6cc80dbb83347163f60f23b609af0eb2a657e4c244e5aea88ec9ec55edca135fa8b802da0f0b275d71ba599098135bc17e1504926c0de5c8e3366f2b7affa0677f6709ee9ec801b61e84a44f7e05d8348a1182571e74488457fd9bc6cd378d1e200801ad83f2ecb817625d8cb9f99e175933a8a2c01baf15b14e33b9756101052bbe09473d2634c7dfdaa8c81123111d0f913aff039f6b72bc9a8ea9464c11975e130511f35405622b86e143c176273ccf2633df509d95514b2240dca2513cd5f8860faff632eaab3ca3b2c1a508358ad795c9e82444e8879397c1891f2e03be1f21075f51c20b95fba99f33e4c04a014fd5f6fd37a0118856aa294ee1db405fb64f03a59488d3441f3dcd4946fc459b8320357078d18c852ec35a5925d8a09a598603416260e83836c25fc6df7003f4696d0088a87c885b0b1aa701a72d24c117030b774a975b0b8e51e6eb80ce4956de1e093c62e1454ffe06c56fcc96d726aa960e false -check_ring_signature e7eb7ca60a9e2a60c47e84e8bedb776d0e8a6df0974136137d32cdea8746274e 9df67fdce070245082ac3cc491e142bf1bd60a353204d07e38383df0639a87ba 2 c416d87a918629623e3fe6bf22d82ddd726f5be080017f503475c9b32fbd3d51 db5bfdfdb3945ac22b56264c0a0111c872dc318fdf545a9599bb19cfc3df0e03 ca74a7dbc17beb5ece865612e54ea3a4b992eeb0913973586bb40ac827e4730666ef46388491d7ce50294ab77585df90fb44737cb88ae2577aa838096078c4e9d03d78d1b81091414d67c51cada76d6c53173d86bfb80e7dfdbee9d79efac9c806d0b6ae0e07cde4e3fdf671890d0aad8954807b740f9d89c97e140ed97aff04 false -check_ring_signature 3a46eac6533dafee94513ab3f3d2aade010ada6d24b9929a944974844bab0e0d d1cf334eb356fb9821c19c996c7398ee504e4189e81e4118d10d5ae10ebe2d27 10 7dc2a9bf61e9fa2446ef5d2119cd4d00f19a1a060bb9bf40d31de49b4da1d2cd 7f40bc166c771b9232b731c7700f7fa77a088b98729057af1c9a9048d2cbeb8d 25c9a38ba7c46671b1172b6c4d71c8ef203aeed6c44f2961b731afac25b8b054 0b119a80b0e76255dff73bc64a169485e6b8da332786378a5d9cdd4c94aaaf91 71dd44deae7e44859cd0b8cb713383de17aafe4dc0119b3ae3d3b3699abc5438 0a5c44eb355da1c076fe9ed4cabf0de8ff09d9997d642218ad161adbb04a0498 0c8a4b2f1cb404819f5e34cdf56648a304079ea4c4f26152493430cf4bc8a14a f6a81e90b260f63a999bd9000255f84f5e7e802df6d7a182c11e9fcbea334632 dbd4365c398af43ff8ca8165d203836cd860e6e88a834557701e9642bd4e65f8 f5ba92129233865c1431ad1b369a4876b9dc39e6d3aaea2caf378f2af8ff96c5 123a84142e243798f171e72a008cde5bb3e808c839745eeef41c1711dd840602a7d138bc5c77a49f4e5ca0a2ccc2ec92f8bb717f937b5b35977bac8b90262b05675a96bebb0dd05f347fdc7e1c510b340c265162545a979438fa63d78b6e0202551090462613859a184420925dccae384f4cff5a5e490d6172f798fe5c7fbe0faedd22a99d76388f755ee0ae051a7c39c2ce10320c1a296161b2c1444e2aa308ef00df0f5a6f3c108bed67be4fc87c3b24d4b0bdd436707626dfa89115d79d0180707756be9abae02951a8189544d4e782bb8d5f891ebe248ebbbd099c379807e078dbfa1bc43dbea505ecdcd61ff763318b97df2f58362aeabbe6dacc94ed0dc4e6165e1b5a90c0325f379e2e9d711e4bcec5d34eb2c375f30db8eeb9ae6101fca6adfce04a4b057514c0eb5baa4985d1acb4be088e37b318ebd4e49aa2a20aa9bbfe3992e6d826a2a3f9154bc4f652ab06b3cb0be643e04fa2df67f032c604485d7ad810aac57ec596af072a0825de496b704a15c914b1dc1ddd9ccc0f000104cfd46cd424f5169fc691cb532ef6405b1e19f8352f9e3085b8e5e42a022e09d84d869a006a3637a080bfbb297899b0e6596b93830d27d0d85d5bfdd220bf0c72cffaeb09b078f6aa5c999a6ec8ff3f144e49289733002dcd8f684bd2843c0213fe92870d6b2d40410d24c55f2f86a022ca1305c5563f3935e544ce9fe6660ea4648d38f033b5b568edf46c604de4246b0629826c471a2bb0fa4b747a3e5d0b3a01a7e68ac51382cab313160b76c09c4bb8bfe6c7bc12f5b9726f890668d30740f1a401aea627093662fbd9870296be774b6426daa035725cfe993dba62b807d8c15fd2b54faa94aadff0e12b8143256a5324db1f4263a6b293d8798b646d0a false -check_ring_signature feb03f9d8137a50909e9128ba247ff7991168059dac7935d619cf984179142a6 4a8b504d92f63af1bb28697b80be761b815ac3c6f73b131c4fa369311326a104 11 860e4ae88ff305c838fe82379cf26e9207f2f9947b62839c0232ec1ec6a72426 b068a919788ff61d27809990116fe68a1c70a4123c7a7f2c7a1bf3c16261c691 646aecb09db8878fa3bf85967f4eba6b8afa04bc13a54c34a8e4811cd14b1ccf 9f4093520e0550264ab72bf792a68248dc1158f828a151fed7b8ab3b70c86920 fa347cec2f4cb5596c03caed2d3a939fa75324bdd7de394b51b1da537b209fa0 6da159080ddbf59d167eb2c69a7c3a2a0e22080b9d079da15eebb8194df368f6 4f9116d28d17805e56636c20905c2985bf746c2838a3919e3fe588332dc49ebf b1bba668a79b83ea96e9b50ee0e8385a9288e488c261aacb2a3da3e5bd671ed9 74e286ea8f46f327cbdf1c93c4a3c702e5babb27b6c05ee9331ae15154ba8d6b e5d5007d06f7587a18812993dfddf40d83a30da0dea8d9e536b45f23db60a80b 3cd909a59e588bc161537851e7226b4cfe571bdddd19da93fd2f268d8c69954f 068bb176744621f938434ef994f5c65f04182ae292f4bc2f9cf8bab31dcd1802e9c84256a2e51bb393815d212a7f1f0378a328418a5f41059dd9d92714a492037ac7643a8c644c35f23c63f2bb250bb28efade9d6c33878f930ed4739a2171996e1ba62690b522d974e0660bd192bf0a53936a4e5878216dd74bc47514d8aa039fdd509cc13592fccf91aca5a83b5c8e9906a9c74268f7cc9ae01a6dd19242040b320eb1bcee09cd845c17e4724740f76030e5fef120aa4444bdcb40cd2a140678089171fb6e7855d62cf6734ea0ba9d591a3a3833ec8422fe1f4f3a1a270208df50ca3a552ee92cc750f9b6a2755f14f4229bc758fd58f12dc165d3a517550cd195b952e415517516dd198bb3b5332a669b71929044d2180105e5aca9d46302f0d3714674a9826d4a91ddd2a7a32dd21bafe7e8cdcc0734d84fa6599b878e09caacc90ee8a5c22884444fd5ed2cd5638285e8bee9e2d86893cd196afff1bf0bb476479b1657d6fc60dcbc62218ac17b83c971c454b4772bc5f6767dd9df9c0c014548f139af2e7733f39ce67f21970c84a1004aebe7000e6060a6072d84310fdbeb73517aab60a152eb28e7281b3bd6d87116137d02b5c7cb913ba89918fc09bfa2c9ae9cde539483c6e5185ce21da7adb5af0d7d989febe3d8654240b0349792589bf8b5cbb2260c499d2f666d67669e74988e187986477486983377a1030c3de2e7c9a633f97cf3cecf5afc2e2c61a78a537827b46006afcfe8695fdc160d78f143e792cc180c3a2716e0ee2e13ab9373834274b876d022fb41940f685d0ffcceb9a918c6c68428530a2fead1329f9e15e519c79cd3cdadb4cec7209a1c02860854929bff674b98385bc8b7f9434f870963320669b6f53eb31485c3f86b05317b4ef550d7c1918577d44dbae8c39396cb02541ae01bec7512838a2709890de102348e4ded460fcba37ea0b321f1aba1b861889ecbd4009a9266e9dd1bbe0c false -check_ring_signature 5cd7872f8041b2f30ff643dfbe40442b1669f37bd83b72ee7a2a769ea9d70850 23dee20054d97ece465cc4d57e4245376ebf47b68fbf611a867b10cf89283a90 1 9bb4517a572d3c3fa7b31f564849c1cade44b248ec6fde81e54633be0fbe5feb 95e1c570aca991e4eb414953de078fa3e4b0193c0b08a5844e6b474f00c3d5009ff0483ccb0639108acb4c2163c38c96f34a66bea624b36e33d4f2513ef1811d false -check_ring_signature 4d36ae51ce768413ea906505a39f939009e3c2b718412dbc02f6854866be0113 0bf77c5c8bfd5c41bc73eed6fe6578296fdf3c86488a589c774515b02e2dd13a 1 c7fb196383fbbf6013fbf94c1e5e87ca6e7514f7d06e1d6e248b82d53040a490 aa5afb0872a8e378ae463a230e455d279b56cb6ea024f80e1127e7049b515d04fb12720ee2ff283e0a7f901af99af09e167be8bfe9ae779353768068f23ca102 false -check_ring_signature 0f71cb3d425ceccae4f9448dfaacc11c89bda536e75b68f95d22888b7210c004 4d5291a288e5df379ecebc182dadaf35b0e1e81fe40bf8c3f5f941dcbd85e695 1 88dbaba577c93003c7799cfff085a6f032c3475867ec342a307f513e29792913 12c9c9cdb15c965925d92c476045ac82e54839d30e18c4b469851c9aa6b64f052f682bec37cc82e7d287cb1559b8a7e53293afc8da1612501d1c9bf64c7a6608 true -check_ring_signature 034b95eff880dd2b657093f53ba15958952b560f0a6597b2dc64b1855ac2d857 70e89067b0cf34bd26cde64f4f0d9dd16e9b5b0602ae269ddb17e4ed2a69e332 26 b49afd99f12f1a47948ef49195a9b97a85096b9e9ee2cd6cb4d05075d796986d 2ea22924ff067753fbe253741ce8bfa8eabe23cb6cb035c5cff816f3cae1879a 9381229b18a29ab809c431719ecf8e57f37a1ef3d62beb744d2eb811c6e12a3d 2f4ad8e6d8aae6edf489a0bcf35ed7fab19ce4f5cb88d7033f71fd67e8f27e51 d3165f46ec3c32887a10e4a952e921b5d8878f7dfe230f9509f642fd1b811768 4a25587cd54e09d3cd7db28d579698964ca2e673b0eb989baad207640f09996d acd8f181dd689fdb0a6604723d177f6fd2078b81df4923a2e0808fdccc9c6151 2d826b291eb467cb1d583056b25c31e79c94ed677579936bdfb02b8229b8ed17 801fedbbcd52175f6154591c145476a732b520b3efaa146c40deddca430220c8 047dd5a647fdde8d81fee0c727617454e109921199d4ca3320a43b1045acd875 dd252f1bfc0b00626efab7f8829ba9096cd8e99d4f79b3b693c20eb46a3dc354 7c15f6191d4930701d01ab7905a98cb40acf3ed9909745a871a3b5770d21f497 c31e881a3b360ace0313908c8cbbe7247e151d47e24be47c4316fb74d2a43a13 cd24da3830245e81f0c02b402a849c4a9e57d40e5e201330b471bc25b9904881 42af514f35f7906ab8b13d8d4129c72cf917bce00dfb830e80ed98ee6399a777 9049d9c9c64f9fef9cd93284babab470ab9b2ed021d3d15454173355ed20bf84 bdd27c063da95c7f16ecc3aceec1dd94bd7a991d1229e118112e3df13d7816f0 0749637a72fabccfcd4b9055b440d1f6e9d4e5941585d68c60c9000a40a8db90 f2c8341477880aec487afffc285425ccad0d3e8b2942ed159af6e9c1e68dbe9b e197f758fbbc2c7109ce556632a5a9daa7e0d99823889124dbae109c0f1a6fda ee1130e9a9f486cb266e67376b6dcfdffd8324fd8f392a1a4b393830ec222c36 4d83b7e77add3adfbe16357a80532e946eb27fe5a2730d4c141b8ce5f50c82c9 5bd1e1129e68555a6e094197ee1441b23185b346543e703bdb8753400d5b353f 76207f65654f12f6a5fddd01ff35e2407f157d0d9c894b434b7f7e32b1b60c1e 353d97b8862d4e3d5695f3caf0c5499aabbbaea30456e8004b489eea892024f6 671bb1cfbffe365f2813f9fdef0eedb4761e9e107ee7d03b4e4438703835756c 592cb6b6e52b229b429dd534fa252f9315d7d5d33b8ad715ff5f4a347962eb076f9e1cd980869facf9a6e5e97ebf501275b9c93117e7a53621f253b66d64af05a50217e7767b0f57866e7ad0d1c9d6b2b404acc2335663e46f1cb153f0f5af04be75f385e1059faf5c9c659e2e460d165f2c02ebd2ea2bab52c96bb94d33e0040a759403af9cee1089f6da4b4956f1970a3c48680c287094bafb491d07c96f0dfbfdd317d7001396c9045816259bddd1a2a21f83fed9a3eb720304870b777509fa10213134b152815861c2abe81c1c3069ce0b8448ef895ab7ae19e12e23db01894608958c12e8c235390046b29ced5a94d45a825613f236adf9c8bb703d1b03fb83dd601b4dcae83316b0fb9ba0fa501599b71203a4ce5f3a8cce870c2ea10b895ba68b1d7ce14944dcaf6ffde16a36d432eb299ae5e8cf708af02aecde5c06546b843dd6730d194091c016a1c4c9f347fde2081057614da94b850c354ba20848f04852fee894eff9e2b706e5eb6b9e7323e4ba57cb4e740cbb7d140b67d20c10313af319ce54ec0fa29b40e67e1d5782c6d6759cf0eeb0d8fa7903922f7a012ac420b81acb3fdd9cc8b078e310d91f1cb7a592c7da2ee2f39b8eae6610af045aef55a54b7811fad3a09278e28cf3b28a276d2b2c33f704d60d1a86b38a3702fe9a1f4608f71bb0433f21e4b8ba1c383744c787afe9cd60aa681ef030ad1604eebed318a511474d6366c8688333af08da9bb2567ee4ae2139101cdd0efc8d070c2a411b4b229a7013491c39a1e3fc770448ae772dea3448d4d82b5641c8c80d22489edc24ea39fb8f84b1691c666328461a65e7b266ea5788adcb5c954cd80e0fe7052f562e4fcc33ba07158470b1d2622c0871a4a2ee7034385d1b44721a0669edd5ef4164644815f254db2e9125b1632058cae4d126423bcf33f42e312a0c5d019cdd465a9cc7793560bc75358d7265c008b712542a9a3aa2e4b9a96bd00eec0410f40c5bb2b864f963b79016082f40b0bc7945f83d8ea6b7925e7319d20f609f1aa7c5a94ec8103009a6661554509f10033fcfba79bfc83cb30830b8dc09c9b9eedb8f0c63ff768036d430ec7a13463569d921f026db726137edf9b45203ad491e0b78e18a64f8d6e15f94878039ebc4fb47ee09c69ed06d1bd27f511809b5f3bbd4ab05895cad5e63dcf1a62c5f1bc7ca75d58e92df48fe1d5a635a0302433c81dd5dc7bc9f2253ff38073606d1c0df7b7ef7df1c8ac62f98bbf0df1203864440e02b56bb482534855406e634bd62f32518bd2843b600fae8d9abe8e407c889dd9b5327d00619cbbc8252ee8cc3308011751e5217cf601612dbb75739066e4181100624d4c88d73d58a464a9b84ae1e06232092d2e201073ff1b5ce1f05831e5bf0c8a3eef949d7bf66b7bf22412781bde7e0b203be0ece516fb1702a0156518234bcd5a0770f12109b56044c989aeb74781d5ace7be9dd9f4bc61fc30c1ec663b2ad70cc6526c8d1c8063055d61dbc9be01d976ecafc9631ce838cd20a701695da50651075dd1c6555cb1032213bb981919b67ebb9663a76ec29f2c8028920ea710ae3d1b1c37991be8a8bd430d80c2a2057e783f832e9c967c5831a0d2d55198327dfab0a473ee730172707a4ebdec277d6ab1c96edc788b88f4b190012b46cb7676f8f59ca67e3bdcb14e7a6686dbc0d0ff73a6b3fd3feb6393581095bdb72d12901365f32cd89abbc462e369a190c41fb3bb45cf34592dd31817204efd4b139890dfd532db605adba05d9f3d434c9d10ff7c6cc2b094d9ffdf09d01568c8512d47398e8a8a6ef9ebed2142de8d8f6d90024bfed5503061f54b58907c2eebba43c54447d8f00b0531b4afafb41f934d8862624b8647b8ba01140f604d8961ad643ef4dbedb36c2bb76d15e55443c261ffe4d0d32e86625ff48f3bf0ed42908c1090ba7a1d3349caa81635c1e9da4ac0d10f3fc13ae7b2d0d38bfad0bdd6399c5a45593dd0c288b683adeeb3950c55cd3a5ff1b987c2adcf7ba5edf0457521ce80aaa4f3fe373d3009daacd326dc6ac1832fa0ef7cde9e8f4cef46e016ac253fa2eda45666086c666001ff458e9fa86f5cd2e9349077219fd5334920dc6fe0aa814c868d26cb8a02ff28de592349de7cf7d22184948c67ed6b3f1e207856b7f58170bd996c05c08a3410c11d5b62b89b96c88720e381de3f6356bf70cef2008ebdd6b6b00bfcfc4477f39562ba834b8db7ca8b882d8fc1f756322d20df1e41efd6288ef46d5d960dbc7653294473381bad36e985f540d2420aa8e5d0dec072698669b69231ee8c98d708aacf00e3b4ab7f367bcacfb407c9878647b0c false -check_ring_signature f13f96893ee5eca557eb0cdc6fa2d27ee3d2c127837c124a91c6728d972e6437 5cc32cfa72a73b73a5e3f63c098d8b34e5669b26d852b1d68cbdbbde8f0a949f 23 4fb58b14f063f9243d16739fecc01e306d455b3fec24439962ed8718eb90ef0b 521859446e1e2022ca29783b62953bd6f8c42297bd066b45335b4d89f0a2b6f6 a1a4057f88403ff8e07498c6e250cf65d939dae022084bb30bf44687813858f3 b78ca05734705d39298b0bcb2382dc8ec22e6423919d1fa8d5b81bc1c6acee53 457d6d7b2ea8be06cf8e0f4c471098507deb1f2678cacfddeffd5ac1f77b1aae c19a0411a650168c04dcd042414d2cfa0204e34bf625144c8c3759e1974c7047 61a1a1fdd6b0ee52293186bb3ceb0640aa0707152f43aee49bb3d692b9fb7b7c c969732c51b9df1146cb4aa2d68bf5a9205b687fe6736a7740bee7ca0261dcbd 17d26b8f63632ddacbf5ad849a31343508045261fcd4248da9f33bca1bb78b58 d87de1c964cfc91d3ab5508e4317a7a9554918faeaf5fd3770f9b3a69ac4bb88 febe66f904c18692fb647a5c9a7b58831f6c15081e8612d29acc209bef5e5c02 fbf54795d2ef464c464bfe70971563a9f1ba951164b4aad015593ea1f62c2920 f70e92eb541baeb41063fa5644249ff7ec57d57be082ae7d066ae3fc5fefeff7 facb7e293d2d099ea65858e6f75822b455824fe22a170b5001f8828122fd8369 9a71305751bcfc5617bd33aa13379085e6d6663c30ff20236e36493eafc6a7d3 893268df518c452bf28f13a73649d93870dcc7837405c75ba4eabc444b2f3b79 a929a6f4613bc49dae063c0565e3cbc79034bcd7be10018b7e9b1343c5a62e98 0c628ac13ede3f3eb0aaba0116c7f2baf1c999b193347d1cbb00efb5f77a9f70 3f4cb8571b371213d7679db805de07acf419e37c0a783a36f8dc7921201417a8 49087659265798b73f20e15604744945149eb11fc67d7ef0e1c83366ce274b9f f678829cb6403d33a14192b1814b4ae6be26cddc4ff96a374c144651811a11f9 50e77dc1e5089d4ff8d1a13f7eb34e24fe697dbca23f917fd7b054dc3a5ac548 8a5ca58182e6d9f31d4588ee592ff11620623a7211fd3a46cd05e5cdcd0f9ec1 6b0fcf9536f0da64c48591f493d6ce62c6ed40e2ef166dd7b4a400e51cd2a7072a5a49432fb13b0925224cd448e6d0e5f1f7c8a5f4bd0b283e52465733aeaf0d59f857cc37c3a19f5a2874a2af5268ee9f1241b2765b5b0458e018d93a8ac60b2da3a3eaaccfa2ef8a1cb19ab1f4a61ac424dfadab30fd6b8812749e11a0d6023719363aa6bd4d5dc69f090697f9946448171f166cef27e4246b2cca4814280b4321c1f34a2247c1d8be74f097235a61f8017ca12575d4e3604beed99c6b940fd2ce698dff4421f166cf8f4056d6ba69b5909064539fc41ab0cbecd93fa1400cc474273ae389d85cd6c65653f33f1fca701c551bd10a399ade89a83af66ee4057d574a4e385cad2409f62934b887ee1710c3c9a9130681ffa9b16d4236dc8e0673e2d19eb2f80f93a9da09a4e306f05e4c9524f3a8b152d261dac9d993a60809970ec7383c551a917a849eca52beb2d9827468c0c052c226eff228a8c039f30e40da419b2936e1f340262ea3cb1e1aaeaed2d7096b5b73b8f16d4eccdf792b0b42b75ca7f0a95c908c802e00bfcb79e34301f1605098d0f13edd27c058ea63072efcbe137455185872dff9afe1da2c560a1c23dee3c2268298cf89a2891cec08b688ba1959c181899abbd309f5808282089ceec544bcd7d64405355b99aa3d0dd9ac59074bf280ce06cf2725cfebc7bd20f0c2954d2ffdf5206dae6964d56a0a31f9c7620145f7f9e569c3b7d88f9eb72ca819187f7bca8b3096574bc21fda0a08ce2fafe14213e8243bac7f9478171f43208b1335f420530e40c020bacb0b0689f92f0cf846f8437362f4bd52d0cad5d30b9978ef9a98aacda59a607991340b256c0074a975b232fb1adf35324a70d0ca3a0b61984db09171268ec2d7753a04bcf3e58fb4426eb325947b7a908320ac83948702023dc078cd11d66a60ed770cc2fb2b307aa9582d7e6d30fb15c8a2a6d9341735afafa7f542107cd153c63106f90eace0c72a2286b7a73ebf7b23769abcf20ed8c5b0ce363d0db9ed813f5607c75098f5efca848ddc58f647f144b11d79f510adb641508bb735f112a756320f414e1a4d218439cba76b993f3fde7f4c2fe70aab1a2dfedeeb5013ee266f49081d87e287d1cbf46be1bc08dcab266782acb13899eee5a59826e13c0783826d0a4f802f93ac4b9056f7ecde54bb76debfbd821b88bb607192563b92dde547eb04c19c5af9bb6eb1648dfd1f948decab721a9fe7579d757509f397f15d0277d2087b22dff36037b066cb4cc2c45b463750287d58901afb85543144bf4c73f17e02ef1ac0591e432a13c3eaf6e4ef86a198fec7e55246f1297ff8ae0fa93b60a20564af531af66412183c1d023cf64e3a5632b430e2983de2c5216e099ce1ffb2092c6eb4136a71885fd7577fa85274d98f18180c20b6bd0b9a58a35d8225b75e9f9bc733f9bee00e1e43c4ab496d899e1bf883a33756de2fcf97b96bc9ae2d6a0be03b74d07ed5ba03ea75034c6d26dd71652cccdbc0a336fc035b83c02ccbd809e88396a23a5470f1fbdb41cd332e90f2e8fa1ceaf9c8f63f95d9882cc02d95023f09ffd3064dbe11d1880511eb082986287227acdc27813ce268a0da70e1570fd8da47ccd245c03b99e67596dd132d78a66d5af3960daf42d636e2aba75de70bc9765d78d0e4d8eff7642c6f34d32805ba6c74966c0997d373183b592ac23702ad774b667f91a614fbce8ae41768ffbb770e6689393b7c5a63180d217243a502787c759a43f545ac615e5993bd2e988b7df93a006da039a712e110eadf278406a8f73393b4bb79215e6c99a88619f49f8130a4b1dabb2a363443135f0cbbad04f5816812edb0f10f309ea3f6f9f513e38498bc054291ca4e03ad695f21c3d506b5f1d181b65af71234e251556c8ebb2bc6dccc592993e8d2fe1713cd879e850e015c136497b827c13561d0478c6a70df4dff25a153d24be533c16f5f6a18040a8286e8f8d99e75ace2540742eb2b2b11cc8ce0182795d85e08d084bd3d11610777d0b9cd58704ebeaeceab8b535ef9c0d5d8e7b61d09662018e16ca0aafa530e false -check_ring_signature 976d490dac6cb62f3414608193e6e47789e895a987568789e8a249448b65741c 45d9c0a426cab6878e3a57d6c08c6c3146449ce18bc2fbced760aed19aacc80d 1 47a923ce3520e8f5b954eb65492f4ac2cbb094c228bc0dc35901fb1a05e87dae 98eda5b352fd24578a5e9b184dd47f6a26bc103a3239bac4497bdea79bb4a40650b7b9c68e258e66d61a7c2cbce528cd02ab5b653f2211acc7d29a2cc77f91b4 false -check_ring_signature 001df89f5418d2741c22268e5405b2b1b68625aff1c75e3060f53db0fa71aa59 e26183ab4f2935e178b830ef91801f71ded2052fad100d11a8f48dbdfc47781a 16 8db3fd7345ab15941b74feca1b2e0854b70dbd2869d474d9f8c8e12246e5abc3 93f55835b1af1cfa8cf45ca01dc4c1c7d4d87c653868359151ffd95e504cd48a a6f692eaab89bfaf352df33b9344bba01b5b2e163317daac8af7c6485a6753a7 0fb6b84bd9125175d2050032ba9f04245276c5243db630cacef7eb6e260e81bb 582f9375baaa1fecea77fc1581d907c63029cdafa7aebb31cb1ab474b59c51e6 e228f42a717f9cc706e745255b0d60f332de7f2fffc2c4102885173e8864cfc0 d2d28b88c25914e8ac99213e1463f47f866fe3424ac7b718b7cbbf596864dd74 62a19cd03288622cf871d1e6427c924e1d20046471f1e071d8a333fc1a0b6403 9e017a4582cb4b32d4b703e4bf01d555dd27640655cd59fcaa50d8d46d1c405b 351d9b56fc548581cc0b3b6834110e08b805185c0af0ae930934c32d875a7bd9 b19f93572ebd644fde3cd723ff1f7a6f32bba8f9e4a686ca1a9e86cfe36ade28 6bbe1b41e8d573b0c9e5507ff0757124b6a944e59f66f7a2f1ea2f5fbc9e5e38 e5ca8fd2ef9a3f1ffec741139c004cf0ffa719ad4ec8810b614ee595cacf15ab e89aceeaf55d13a8b12e5c4b0342eff05861cc6b66909e04b66af5d121dfe5d8 147be67364bde26678522ed932871377ad3a1c458c9e6cab7d8ae92a0005c01c 882f548c044c5a30ccb9dc038324f4b95b5ffa6fd6c2f594eab1a46ff9d58745 ff09818c630427d4a12597f09a92a8debf905f65abf1968a0fa87f3e8d17c0022fa93dff960033196e1706de93d4784e12b3c57e8825417d354191518541ad05839374d6bddcc77ff705b4c99394f6f2972684345f7f891e1eff08cfb8a2d20e1abcab925a8853514007f3c709ec4845fae81191b6d40ace049c05560124750c73df9a218686b032cfa0f973010e37682fa3e8dffa6bfbe0f72f1871f241160fac515154912f09c89534fc3e4403b3a47769d43ca18663fd7f14e2618338900c1dc0a65b4419b3232484eb42d7d90ccaf231633ed3f1ba38398a424ef5cd5b0d5a1fd0aa2d0bbe9f5d2df1079dec7498458a72ee35a92b29e6e6009d92007c0a0e3d5d6a7e1a9be05cb01f8d303d06654b9d757aba2d281c410a82f5a19eae0e73ac5b206912e41526c996ab2edb572c6215aa0cf5d7e419ec33c24b8a0944054dd59753834b061b3502d69333f1175fadaa4fd62650c85a3e4437ec67a09b00b5efb5fc05d041d3f414200902bcb7f8d911ba0a5fdad785fe868c8a61c54509e66baefb916ba86eb35a96212de2ef78c7712737349c52e841faed8ba32c520f3ec28068288aa3f240bc50a6a4dd6bcdbba61c545226a3500c234e1a6344050329b249505526da4c0a906fb100cc8fde3eb1fe2be5955dfa915dbdf47fa25302d8da423cd45542fb123a50a7b07cafef0ba7319b27436708da367f1d100fb3f0691fc71bd7ad3747c73aaf5a12419ba4625686eb30edc91b2aa46ec82fcd08031550b7b7c0204a44d73d992125ac6c58fbf24d314fa3eaa16ac5ae670a71130ad9a4c6457ec41e9047ec09f661ec0d0b2ad2aebdbfd2a23371cb11eb552c8d0ee8793babffc19cb9b8afb71902842f0194dd8976e522d99816706d12b3a40201be4daeb008fc3e2a54211fe63e99b1e610d201f359064b76a1627c1b3cc3b2e795f838896e3a8aeb6dffdfdd7d58db33d52a9accdfd7506987f8c1c65ae3520bbf5fb809fc214e4fded3e4c0cf2cd93bc808c12f87909db898d055a3f55e600a7ef14d92824d116c05f3386e56002082eef81c91a4868577053225ff5071480ed73f2d606a8c74931ed46379e2b6d8ad9f452bd09d0aa46f85024277c5ad6005886313f0e78ce8eaff563fc7f186070a607dd8470b8b68beab0f52de2f16fa0a40b581e781256c51caf1f737cc4331ca94ab3979b7d950567f6ffc91887a740f1972cbc9be4097107d5bf99ca106ab50ef942f77d39e7835cdd11917c14f3b037e3c8125aedd1dd3e8290e9de894621d6c2009765f239fa7d1851f8036ea1c0a3ede5301a51c6881d68f5d46bd0661f6336740ca407eec79f794c3ab7a0fd90d5e2343904f80f774ff6050f17e9554fb30b6050f27c2e4329fe7d7d7af41910b89568da864a3d0c5391978573af2a7e4cd5a09ec8f92b56d0158f24c81a11306 false -check_ring_signature e2b53aedb8c4601ca095d85dba5fef7b047a68cab81e16595b2a613612e56e1f 29f4b55c2ede8ee4f5b28dedb5c941bc936182c3e2ec5ec74f01361e3cf0a305 28 23ff8e0fb86740e90b2d49be9f5e31cf3a3c8383529940965b8d5e84e459dd30 4979a2f898e3b00b3be2ff6cc8244e0c474e72a63451b1bbf22f63136349fff1 b6e4678fd8983c820c89a3c7428132c95a9a847c3d23c988356d7595645b1e08 24f7ffd02d488118a34851cc6b3c17fccb7243040a4db36fa3fd2b959f9aa4b4 344074065f63995e3c412e31b2743e0c65bf24f78f1d84c0895bb9f704968e0f 64d6f0d1bbdca9264d66ceeb112bb462c4c8eef4f53a0f54f34d67ece1ff70ab f3469f265e2b997072535ae50f3331fbd58fabb98a9b81d009041c6001bd6646 48c4abc7ed29afe7cfd4fc271cee57ac391ac58c098001a3a39541ba5bf08ba8 2fa92fab4c787d696b91da005b5809ab3c539e39920aa07adc4b66b0bb678900 3b6fe6c10c0885eecbaa5a99e21f07e3cd5c1ae52b906c828f98d342e4ac1228 4cf0e12130a0fda36c0645ecaba3368d86dc4ecf92b331a425ad1202fd8331cf 066ad2373ef78a05dad948acf864125caf7e9f4502e282d2e83dbe243838a2b2 9ba1673116f0eb5572678bec67da439aa51d8583e704f3e8253c40409719c75e 69f8ec8eab0d8279d7808f771a7d808a522d2bd280b4d9da09c32e13276e9348 fcca9b146ddbe3ce9b9c13b64ae75e781a4ddba699fca802d517125ed2ee0fb1 bd28b66475cad81db738d1ec702ad3c93d6cf8a4de9122968ef8974f5e4031a6 f5e05b1d3de5119d3923f2c875977f01d4ca0ce78e50061acbc6cc0075b4b6ea 5427d830e98586d58423881cf1924d3dd4a8c6bccb67f7473f0fadc993aed9b4 a0cff5f82ff3510021e6ef6a82b3566d5c3c796ecfd70ad7f4f195769c0d1d2c 14c13f20a0d96963ff5f62aab18fbd016bc10794f8069d860b0c78e445171a5a 5749e9c791a339a1d1ec037cf6317d8a47774feddb8953a7162cc8c7839b6bc0 21e586d319ab184e5005322e443ca8b6270cc45fa0b9c22c441a85201db0d7b9 58e8145a3f88e381964e628789442eeb535a57cef14427f9ee3481f2714ee3ff 986b6c573a23810c26c4e3ee4f93fb3d7c76422ad2ef2481c1f587e531c412d4 2e4d2843e6271143b06ffc2f1ecd0de50abfd3fd2a1359ea068001bcde716cfe 48b05209a4e52912e8d7a7da2649f0f9179d6153304966fba3a0204d0bd07a5c ff894c636ec6ce9a0b6d0845990857642ac5e353e62a68f8a306b425002e25ef 5d16a78e2583bf3f9b6e3eb93222b1f5cd2ae8b8f634fc2447f50a3cf9a38f65 f9c045feeab33242330501c341e261ef39f1eeac150510ad96beccc8e859300907da77173d65d8ef67e943bff2105294868fa49f955132594f513a6a33693f094e6970ae523adf1762c3c942f8e6cc140f3eb1751ac730903bd83d36410408017ec6f57796c102d3e62feccf837497a7eb7d9a52889e237467b801f8fe7ea80ec5993956b5306c4d0756392d821d298786035a18fbc0a9533143f827af97060c464825f8743d64b5806ef6b371d9d0da1f70008b495a45554669760e66e5970210545db41b9441274ce5280316f8f8eda2869ad2a31ee027ea655bc2035fec0617202bafeffa1f17bc390be63bd2b92965729e9024668b08ff97d79953d50c0ddaefa36efccecf1f700b1c38ca6fa180b6b46acc62cf2c747cca24b0721ade0f3eb765e21ec07fcdcd339682583d8ddb15633066bcbcd241319cf56e6f29dc0879226b60ae506af466f55771af68c16f84a9033fbc8d43561d6f70f89084cd0516af23bd7833131e3a74c5ee1698190f3a74c4bb32855e3d2a2991599defbe0c89d28f3613a2c24f6add8a2efdab45cf146513a25d125a63a1ae2951e105070672a53ec8d77afe2ee4751b67299d766f54d44267349cec1fa10d5e411f11770ac7ce35e82bff177b70308d539f0b0f123f40c3b8dd00f5081d9617f16f04cd0b480e7ccfc7b5829974169539944594bd639be65a0df994c880eee5aecb804d04fa09b5cdff978f9469abda805050aeb7cd93a24b8bc266da8abe1d5feb43850a74b22132cd6c603fdd47a2c42541b454295aacc71ad5e754142482fd5095ba04f3df8ca4124bdc90e3801dbec392b105637991134452ec9e915e0ad0976ed708f6042589a4b0a022959f84b3b35de305eca3dcd810e026dc0676711537611602f6c5be2baf0cfda9e164d6c9479bfa2511931ca6c7e28a38b09f6b1be56452044d4e4e055728ff913a3723edc8f277410d2df8c159def3d87e5954005cd58c02f56637b56569536dcfbe29c95cac64dcac6e40361b91e12506726dbeef9ddb09d07cf239c023130448b07c71f25ba984eefb31932562ef342acf9cec7350ff00a10e914849d02217cc7226c39364d328713e6abaf191dda5e762a8753f0caa041731d16dfbd5647b9a8ce1d7bdd49f25989e9bb63b282e675a04b82adbe1a406696247339ffcb7e09ecd45bca52083e7d138cbe92c3961c43ac86658fee77a02e134152b7534b3578875efd788343b130544511a7e38366c7837a014ef542101ffc791d761733e486e3f9b1a615f2fb00bde64f47b84c70addc45571426d9800a1a47744228fab0443fbfaa4e9b1090d8e987b9d485aaf9c192c1bf3f4706a0c1707358539ba1637265f7e4c8e3d9b65cb98927c5e98117569425f61f81092086f3b0a092b75c6d71268d9f7cc9dab7ca0daa40ccdc822b6688def9f4202ae0653a27de9ddb6ddb89e2d498a1ce60096a17f578dcbbeae6ae6b807efa34cf20b93fd5ad37b76f7850ac1964bcde215d6541ba60b53d8fa05ae1df87f5f80030e7cb84bf83d56acfcbbb5b472e80ff1d74f1c2554fde0d7aaec8e53888531f80671ce4333a866a123b3e9e436255e50925914b2bb25828f45f1923ae1de68c003cac747a8d0826809503cb52dd0fa5d7fd28c606f56a279e3ac606b44e110160ee220a4f58a355a9d88e06594ca6df6f828c78c4bd5887dea88f5fcecec5c3d052148ce6b95f45cee87cdaa3776c2ebf9b7132ec0c3af7b9d362df1aaf8a4e704d4aaa08549259e7716cf0db3d30dcbc32c5d5720f046fa9a53b7caaa4b60b40beb87fdf82877ed4f1aaa6acb0499e7899db80f573a737ebf0ae03559ef9a6405443d5e773bd361399a51dac85612a823956f188708762fb4b889b79f0ae7fb01b48d2398166910dc435e41aa805f32b14c9a047a609f7e5e65833649ea6ac90aaa57f55f1e6217eac8ed95f5265bd1775508764a6fb8693e5aad9fc18d610e0446dc4de10affc4a1c4a731fc072f63c700b2562190b0af896e071e6f7628f40c5a102dd33518503ff67a42c2603b72a8a6e05020888d70847fcf7658baf2f206b24cc2b3b632826c87224fec2b85ca683c225e8fbec21460f07c017c029f460fd2bd442f01c90cbbbfe430ef05b74ae8cdd94b92e511ffacb1f583279d54310cab1d4f1dfe82bbd69c855d5c13fdb7ec9c73f1be851ddb6d0c4917bdd2611d05300a4a8272c40ba6d5dfe38cc2be93cbe2e90e41740fb52bb86c27b97d9a8d063ac0a8de7a5b3adf5fcbb0228761606459c820903f02ed2b3042f1a6c88b8d01b0bed5514c965cf843d0b0e0d3ef2fde0fd24eb12b65d084eb55c8f66c4c8b06aa9d137e178cd2c42439a4b8e82772a7e21c71a3244cae8aa8bbc5b26076c90870c90932d6181bd3915d455033ce44f0c03ea0ea222003ad9464c174bc39300632fac726bebc5104168f89b44111279b2858c9166d39b398a22fea6c6d31fc036c7802cd703acc3997ef99c446ad8bcca564f70c987bbd5628442d2c3017ed07 false -check_ring_signature 792c50b20ab782522dac38a12d8bb3fff02b5129902cc79a2a60e9cc918f9581 65e5bc89155fe0b0470b5e2e1f1410fb18a56263ce6cd2966237e41937ff3d93 6 38f6d2e2bb69479479e06f350bd455bef176cf8429323a05c5c05e28d393aa3b 8d77661ea5deb6eaa17dd24610f30375b6896c9ecf1dc350e85a7c3a6f91662b 114f1e15947184ad86841d4b0b7ebbce93fc79c75ba3792458d7196e26074a29 6542f6c20f3e8910f33ed00f4b03868454f4c33fc405042c038d965f3b0ea834 60552a0f65b66b05425361b6a171deaa6bd12b762bdd26afe8b48c959335a931 e658995c3bcf5313cff74a507d3f1440edbfb603fae544cc04bf88d3b562b0a4 02c77f5c8789418d7cd1b868c06c244c7cb8fd22f328fe10697f881fff40680a98a6a3e8c913a5a4277dd891110d423e15e854601353c22fa2db16e26d87a50239f99a7aea85201d68cd819ec3235353ef0af7fb4d181b9c7fee9640f975ef0a6b131ef551695c313a67517cdd82a7dd78da974b23f4c1feb867d8cb19fcd7ff57f44c9fa919229f6cf23791720489b72677d296a4da676c97655edb91560e0f42a92acf8c9023b9a78604a7f98ce6b077277bbc1e0b7de4e7b30ef51526b501b6a11e0222479a15db7664829490722ece05681a3d67639a4bdd1e31e8aa0c0ef924e673b358ade02a2b375719376a35b7b03ccd481c1dd0516223c4c38683024ec153497cb09cd7d2a3e519246182d2420ef0ede3206744beff0511c7f04606a9c50899f8f1147c4551cc9aced3f6cdce87c55861ad8a76a583bba8ca632e0ab80c69a66bd95e367f76eba556df9561602362af8c2780a4d2e3697beb78c30772a9aaf3cfe5d7c6d90e0c1b99294e9dde275696b7045eac8d7662ff394313ed false -check_ring_signature c5f6019d48351a26af3c2dc327d8e3f24141475e15e170829fa38255fa626642 24816943693021a21ad53ca2b19e00fb1c1a00fcd6f23e288d94b23e2aab395c 3 8c4f70ec908cc13f6fb6fae436eb8f66c2fb84c2dc44aad5b148a1e7b909daab 1b0a44872f41f7b107d7cc1f8fe237b20f56f17edd3e3c3e0795a07e3a25c9b3 686767d65cbb27a5bf383ec8384e3028f119a4d5cffe0b145ddec48b0307574a 67050e7f644a05e9f9778137cf51101bf52840bc38fb3b240e1f496765f20306a9ec714de1341ba9f4f54e2cbfea6299357636103bdc6f0f324682f4fce4199c16630f9b0fc82063da259835c1636ebc0deb4f2c299aa82f59f38bf129c6ca90386e9f0dcd9c863398900a137d47d104dc818f442b3928fbe0d41d4ebd7d220771eb2dcac77c1f7f1f90dfa09f7cc12a02932b1be51b4334d4354cd241e80a02f388af252f91d107575f414c6df3c9ca83443a2b7d4149a2eb220ce83ddd1607 false -check_ring_signature 15da77d946cce37b1b1f75a35e5ee1f9f07ae1c1e80b86eb7925382f1f5e52a3 6c999c81073e5efffb98d7e90044e390c260bca1af17914cbeebcc60f037e8fe 16 72c2f2b9919b168521ff61132c823b3bfc66eba79e443aaec9b99b988d767daa b117df035df9217c3abf066540b6633633bf4d552307017563afc89cd63e2a4b f0356db11536f66b6836d54e67acb5970146585f3b80a465a29669bb4063fe56 e7af2e32fe6fc5e882db4a8efede6692b00e340b0e8378d218189ecc1d20ec9d 3149f86306aadb0ae0cf3e8b6c889f4cb884848e30b1f57cf003430a4588e566 b7e79f41ef196abdd446e1691557720387e9db2b947330b97cb464370b2f48f6 2e736de81995ec523c9129384f6488176ef156e072f655275674bac737ecafea f43fd26f1ee5dadb1c60b42223d3d5c6f2af474a2626712b5a01e01ab1b01bd9 3ba3a0de7f8c68eb74886100fe160b8d1d719a0324dfbf080e105fcc28afe290 632bad66cd5d3c1264aff0aee7cca12b738d50f05f55c7525a75c51cde69d8e5 a81b306c1e5502552ef52d0b0bb4e8d94db6741a37e21765046df5b254c2fa6f 4a0154655065e7b144846fe963c712f4f4b250eba9114645b5f1bf3d2acc37ea c183d4d33a895659b96e20eef8c232d16fe98c7fda62763e0c90ed29e66c51ca 08605accd4e48a12b5b2ccc40b2ec6cacab46fbd54c5ce8d29586e0f7e384fcf cae3d97fdf734a99be31eb915dc13cdf16ec4a79a10a7a8399c1ff5e844aa327 1071af5adf5f126063fbf4273b72094ce397daf42623a44ad05150e093a79822 528c6c554b697021e1b7aae8cef9ff16928427acdf7da0fc58bbce0852c9ce033c9f3f666cbfd23555f859a51eac03c674d874ea0449a63ec1b2382659e892035038e934eb93b01b0849e78575690c688e61e3af90bd47ae2436637de0afcb030189a368c462afbae3a6108cda4cf19a0794a0e555f645b0ef797ed438928a0d41b4226b14f8367313e0bbbe98ab22d5bfff9108388fb8f7a6bc757ebb676607c29913dca50723a9adaad77010178eb1f4b850ddc4fcb9cee9c8e3a167afa80205da5f5736f6a8b36c51108b80b8e382bbd544d13747c456b770b349956f9b00f9ca3a170a55f2d3b5332fc68ce5029d24689816b7544e36694571def5ee0f084321fb762ad7e03301902627725cecf05cff66ad431fc140f2697fc2e02e98053bdd6c2bd05a536049b8edfae0dfe2d0f1a1082f073055323a59910baa247609922ad7cb2333544497ec25f47efdd07cc640efe24dca7d5cc114a3df7c894d0c0017d6231f572d317b48b32441ed8e75bea6a7a89f4d0b89b948db87230a96062f7b0e034e556d1d9ad6c320ad62737f4424f9aeb460094b57c0e1dfb273300b36a30456163468bd8ff19024b095cd4478f13e932c800b9b050bd454b7fec708a65f316305d184bea43665854be81eee17d5293c89fae1b27cedaa683ca80a0824876d5bb6ed73a24ecf835646096d632f42d8f8836b0f6b9ffd1b24e5526f00668981ddcfabb5afa4b7fcfba6b12d8192e7f5508ae4389b06a25fed92fccd04a8fd1926aff8c71cc4eb8a25f905e662961aad5da51ca93bf2f6969abec62f005e2616c6052247b5e48c80e533abb0f4ae046ab0b6f58995fd1bc4306b9fb80123a6635d120feb9165c93657580f0975a5c6abb1c18e79d33e4163c5a93e6b00f9ee9469b923c1d0b902e9a9b55884660c4b6d0a47960003cd280bd8831a01046db945931aba1a76659df4cb94368eb7a9c23a07b42dde4836b220bd5ffc2d0a3d638f46d02d35efb6cf6fde529c20cae7dc4001092fad0a2d3e2d0107e180078f001abb856781bd4b4c5a403173f1a579080fa1b2e4cb4649335607446b790105701a51943c1efdcc7d24fc61e7cf309bf4b3a3da23f53e9f5dce3d8fd9fd037fbe0c1c78ecedeb8d367b450b4e5bd52033f3637fb0adf24f8a93c1da21ae05265e87bc9b0a9798ae15f00f036c4b7c594cb6feb7b26f7a4b634056cfce7b0c6bc1854968417f13879e847520f04827fab31c62b33115448f0964869871a00524eccfb0e388c8a9488ccd13d621610f4c55ca0594470bc402ca0e518586280d311d910220c295b2fedef7d36940e32db80388dcd781ab8bec1c2b9a617efe0273dfc5634b512bfec134ec7486dfaaa5975019738ddf21e78a878aec78bdc20882b050187d9ac62d811e76e2fd696bb62d0f0de989462f5503f58067c294dc0f false -check_ring_signature 3e209b2f03ab4cd599bfb09b74438dcce9533bdfb1f5709a0faef08a30511432 7b2a1e4af67f2b0b15b9254fbbc55c5b01a71ee7cf4447812224c37a85df5b0a 2 fd5ffd706ac4ff0694ea9988cf197416f6948bcb5a6b3dc40e72b9c3c64f6be1 68bed3050749b8c4f96a0fc41541f95701215e51724afe60e39f318b765e4be5 6ec7ca5a19e3c72b79fdb436ec8f0144f21f1e263e35547b5ba1fb75e9f2d5008fb50fcebab3f4f99bef7013e4187696bc1679f18eaf627ad64625717670f6c2968832ea9dc5544e20037ad116df9556b155651fc2118970ff35f25458c94b04f9a504e6dc8661f740e6934a2d2382fd098fbd6c2b7faf532715b45afc7d8b01 false -check_ring_signature 19e8947debc57401e507657ecafb8b283081ab3c0b727b596af8f7bd6ee1a6e4 5816ddca6ea7cdb2cc7892b4584341f30be5c23bd3e7f7352fd68e3cf62f8660 1 08aaad567c250016d7706c479d2ab9818d4fc2025efca32fd0b2d2ff7f36090c 43154b38fe9084a0b36eddb933be8981ce3172d37ec029162b0798d6f1dba30def70b9099a839f4f8500312701aec4db3934e5297fdbb3c881afc7de55cf010e false -check_ring_signature 2fec0f06d0f8f2c31923c06fb2f6517f400f659be18d8bedf2bf1c19dbc8b864 12fbb734d4fc88a384ed544e1a2243df8faa34c3a235445bcf792d20c708bb6d 114 ef6c864532e1e9f3b157397b4399853b89f72bf7bc1002b49fd9b8585be4f57f 658c34922256d5939a547fec36810c3dd963d30a397423d1dd52e1104e9b96fc d361393b0e853611820b70d69d661cf6d70ddf1402f5963e9c0921f5615babee d2b11c306cbccb3451b7c8365db933d2d30ae17cbbebf39a6f38a1045f7c849f 751503e1a6e4d76487b1f5420bad393cb6136a467bbd77c30c4fa9168fd6c058 5a7e1b382ddaaf6717c9e42075dc5db8b7b320a04bac8b1ea129a9f00ccbc3b9 2f56587ae745adb39bbc2ca535162f5d979312a2021c1e7b01e0db252bd56d91 83ed7b733d606eaea547e8502abe5c5d1d5b73d50d3aaf30b2fc004a001f84c0 eb7755c076273a0840ce79f0687382afbabdd37b8c3dc014cf354d2aa8b5a774 4ffcac78da47b89426d6bdc0e5c034c2efcb65447af79371003782b6ad171ef5 f0abf010bd1594c7bb005b31df04967c9fabb4a4f64258865539bd6af70ba4f7 84dc7a5950d22357d3d87ec177aa7baddda970ea7f8764ee5722fbc4a594e9a3 57b662e0e38f6cc50aa69ba917a1f60438b1031f487865e03ee45e667387a94c 797f7d1d4cd69086c5144b2de9ccb5d44a50d1dcf0098f480ae0c04e2852696a 7d25e55d240ea5c7bd6a82d614ed48d38f7f4be238cd8fc01e4a646071ea570b 94824a694a88954bd8278bf32eca82877c2523593e223576545701e8ba820d6d 25cd8d0a3e14935a6e8f910a0ed0b96842956eec2245b7cb6de7465ee0623b19 d1b917a39dffd6a14d4cece46d7b25c81bf9cab723c82ee81ddb64d673d68f4f e829a85adb66a8b52a1b0f05f0863b75e00dc5a49d65a1e9c21d2c0f03a9db4c b29648f400d783813f3070c97aed95f824e5d544339e68b7c64427e8fe0539d9 5d927582683552891fed19e065bd896e22c7ece1f1427cec0b471f0bd4fff7d0 a6fcd336a973576c3d8b675f186b99bdd8eca6a774dbf5ad3e9555ada27858f5 23876115bf1a2381ef0f86e95255029e965b8436f45e4cc8948434cf86e17ab3 18e72ba0afe2e820d107c897d70bd7d440f5efb8929fa979c095992801d28c28 97cc2da18b9f13604e46f09c1628a3cea24d808e33a4609d2a346cd725272971 1c601a0384ef8777c9da7a7ea3481a3413c195fd8b4353c79613f817149e222e d866114b6ea854fcf016a6eaf5587f61fc522b3d57949b7caef0528c5794cc18 4b7c0f1bf211b9c908ff1f7b3e5728fc1a73616a8b3f969bb1ae18a83e93b492 883b7f9377aaef9a9f13fe4bdee794a88d6250b1ada6f671c8f864a6fc05c186 44d8447cc648cdd8df1479af36dc353c7c1c5a638f9bd13ff11426125b282182 a1dc4761786466177a994e7a668e468f9ed90becc099c9823757a582a2afabe5 b1033a4fdda4c1b086219669868bab3b20b650415d85dfc4acaa7c98db11a37b 157fdfbb3689ae7dce9e72d7f1d5f4cf88fd12525c2cd00da0394d2067378b70 c97472a213e666452ad573b39fbbe6116408ddbfdc070df8fe10b35202e9f989 2d6a760b47c777ad57552fb7ab79fadd5cea404a5db9e0ec57fc39ff44bd6544 63ca4daf58c40f4b1ae8d75a998c4a407a1012cd3470850b3704b17ba2ad0242 279901ed531b8a5dbfffa2d5de8eafacc768799f49a99c1fb20c5ada4ccb7f2a 8c476218259cd5ff956e5dd26c0de28b4e50f327b93c68a924470345eb35341a d707e2f242e2f07c83f5450fca0599e4cb5957bd5175b0cbaa36596371bb8a03 25700f50ba9d940df441ceb802a813c99796925048238d313ee1906534d7117a 30fadda11809557dd2758eefcdea440ddc0b33257c81d07dda9bc8430c835bba 0dc17e8215d5642ad3f1025767bee4f17a5a59351aecf284367464b3d2050fd7 9bc80b864519816687e5b3737b6680986b7b657088ba1d07eeeeb536a4b4740e 6aabe83b05254c34dcdff75239fe4043cdab46381d62792dfe018c943d7b5e05 9ac3ec7dd66680e56df132ada12f75381797e8ffa9353cf6763c6af5bc5d3ff7 bcab08b44c97c0c20133425cdabe31df4c63b23ee1aac312ef113a3f011caf9c 9c70d1924d426ea460e645c670b5b660b32b55cc831040bd3681a85abe242ab7 a32b67be672840b59d3e039080cf49cadf212b150c46d4a02ecad5e1b4d95d33 f590c5366939238adc81c563b14c69b4e9c30e3487edbc0f4253aa4086184881 0c46ad61fa3d771b888b562df344a5c251225b3d2cbf990d8c359a2bd6797fb7 f5a22c2138bf04957b01538a2ec56a0c163b2ff93dd5e703f8bc3d7572b607b8 8dc91760d232fba0f1226ccef6272680b47524b9e1762a8852d60ac93d8b635f 044d2bdfa43389df485e379c434d09d2976df9db94b00ea133a158d4ad863a86 d62ce11aa507b85541b71b19ae167d0757f4cc0de0bf3363395297123e3df447 74ae5420a3eabcc16d6603de1d247d2c74456d18a542522ad5252bc257fa6b8e 1458489b3bb1601f461ad5ce5b63ac57f2193f091606db9ac2efd7bfaafa3a4d 4b373099516147418f2be140cb724db946cf69951527cb96864f88ba26d3b888 26f118754d8661d52f17fdf452797a9b5f63759c31820454ef0f221ceb95c51f 8e5948a37843c2d1762eb67583b07316ab82f1b01ed78e4868186c6335dc0008 0fa095f510cd633cbe7d78d47f7c06ad8d20b4489a67b4c8813866961472ecdb f574b9ad2daf337b92cb8d556ad4d21659432d80c7a79589c44c1ddec445aa6b 897497f2a855f98d4d5cab7a6187773a14498245f68727ea3e904e7f5f013a09 5f987df92a489259214307aa6cd34638fce7922743216742355f6bbba347f2cf 19d59ae8f8bf914382f49065b6d8891f0cd001d710c801b7a73f7cb2a49c2826 53b0f8ac113567f8961af8b7ded2cfca87c7945bb27b1930bf9e092238af3491 81f606dfd6a8b4a06412c6f90411814c1c17a747cd6f4ff0f44d50ab749a0dd8 eecd809abd04ab8e1397c2e38a1c5d73cd977441112c670e6a9f7c854ba58b1a ba924cbe67e8d45756ef61f1e5520a2fc0b911c1d6bb69630c6d126cc7d0e4aa e6f28856bf6efc3173d431bab65f60b71bebd511ee30edd5dc5f606e034f352b 5b1c49f574a97616ddb9e143c7084f62f2d7e1f467f81bad49119029606bbac0 0a79397dd9eab8d94e0a13b242e8498317ac1417475fae4003f53d944bab027c 6b865abc0d443c6972ddddde967a0d5f65a45d7aeae9364037bff4fa4175d4a2 2cccf465cd1fb8386abea4bf12f407835498d0660e36e2bfeb5cd0a7c0ae1413 70c08ab3824aebe5221bf8326004a351341eb201d09a7f01507e30d49f644d9b d03aa2ad73a46d2083bf7dd745b809ffb13bf36c7240f0ee1568b2567c1bdf23 60eb0601cba3f88f4caae92a6bfaac054d9c9b4793ec36b8e9b5e4946f4bc7ed 11f16f5cd49bbdc71ce81a04dfbf852390d7b81c9fc8620f6b794cef5c51b2b1 88e0a8628f1a844a2b71268dce106c8e2f1e8a38d69819babe2075e00680033a c1d7e44251824317c55f2454bcbf2a5954a7afed61b1d8abf4a7e091acde857f b31dc3de84cd229427369782f3f910130bccef61679ce0b1655bec13de335362 885dda27bf4070df88440245281e12104278d1f8f04daff49fff928f28f77545 39d139e3231e711937636360c6978fe0870659c78f781870ab73ed7952cbeb82 e45a4219664e05995a66e45b098ad1a3fd5c279af6bb713c6759280d8ba0f876 6b3c8896fa7118034f71647baa812a2337bcd4a68594fa058e548ddc9104f23e 7e120eae7b4b9e6df0f950f182482b5baed0980efb8fa644689d67eb14e09682 5216104ab9fc5dd09af6c7423205f1562997dd6452be286412eafaea92697c7a a5c74db8c3d1dcd79a47a4e1e1758d48c227a20d191813f50913aa2dc28e6545 996800634f9b7bc1d6d50f7b9929d55eba505f4238b9844edb890089905cc411 da53fc60c4a3ed49e12a666bb0f1504961641da6928ba1009330f312f313ef64 4a0efd2f39b4766254cf2892a16d423909b53bdf248668baf3daed738ea0d774 8b415a19a25f4645b6e1279a8b933e66f472b62497572aacbaf1b6aefb58136b 912891a2bb2b837614818eb447fd687e116382ad06afb2e4456c6fc84d4221c4 f4e3885a01f8907758cf05da486130599fe79f4cc2aed929247556980af22ee2 3771ebe9d7509b68065e3261e0723a736e85af95dc20298b768e507c2fc98e15 b43b2c0a031363c645f5d871d6299fde3bc08cc6508ddbe4704c14d30026dc42 0adfb821ad38f973b3970ad8f910de84f42ee637223b6153c192298334b7f030 80d1f77c3405d0d1d25d69da584933526cd0834fbd297dd7330f3e6cb04ce685 7a17952d193083c5ba6f0d63a29776526433778e7fbc35ebda50f79cf2ea05ee 94cfcfc42ab2dfa6a1bc5cc2c838398ede63e6c92f8891e19c1f479a0b3c9d27 f1abeeaa380e36a65437937f45f3ec9895ea10ad9021830de7c625eefc49459d 35b13a6b67a35f791d9ee1d2af6fec91b9abfcde72c5ecddece4f0fd648f4f90 31b870fa205fb5bc8905157601babfc4562e67ddc1d8e8397c9d46058ede9f25 2da02afdb5eeaa96cbfc1f1e05ff2efd0eefc9997e6077b0e550b57e7351f27f c661398ef9ac8dd5ad8cca1af5257d5d3b92d4766cb0aab1439ee02e4ef10474 e09e742b01847e8fd95921c8219e6f576225e374bd7aa3fef840208968b964fd 1ae397d30cfa8d549006cdfea353f0d1ee5052b157f7fb07d7b148a799899d20 e2c1f1eb4d70993967c8608085e40a28f3ae15f2281eeee49410ac0251a0304c 7d9b98be076a659c8f6a70fa23db0a609e30fff720b48b99cd8074d2cd01804d af1313f136b85bf5a1387b4ae45a3c9945755d9ed2d5af6bf8b7aebd1d7c86d2 2162b5c96dee7dd54881ef8c4fc9112e3263690b986f296b751d6b8e0adbec6c 46248fa8a74d015ac872943ff9ac835fd53b2fbc5a3a531632b9c54982ae2148 47cff10780d49801b9001dfaac8cab06959e744949b7303ba8a0bddbc1a737fc 1ad189a090d3fb9488a9346046aba06679cc20068a82d217b56975ec2864f6c1 f594e3ef54bd2a9fcd361d318c1d250febcf0e511f79749a289316e1468821bc 2701bf803ba06665f62095f578470adec380b7d2f27da727679311e1e99bf100c7b9ea8a1df0992cd416f743308bbde810af6f8863d32ccdf3d498281d568a024381fb191ebde91b8280a92847c518ac5bf18ac850789997cf1749b5773f69065460932bc70e2cfbce6d33356d3af7de1020a683951c3b1f1886b4b0df626e0c4b2c2aa23f521b8ea8c83f7c37bbed485abf2d3dac8f81a0c3be5a700b4b9005604f91e6967bece7e9af3facec9c5114caf8452d9cb83fd5d4f93d91054bb1069c1ddde6c8ef21bb48953e2e66764f288631dbd8d2aec44aeb386c714de900088002cb238d9f413934ae9d51177e1faa2068559160d81fd9cd8e9cb24837b20cd928b8a4984752d02a43fe595d3007c40f6cdd54d3eb841eeeb460904c749f08f6bf842f6e34ac7db9a50670cfd743723fe8f0f062677f4c34fa6f18de32e50694d265abf2250d6c3f2539ca13182ff3a3297bb7f958202bdea3f4093b00810317bdb518e39a4c7a81ddc636c0bebb94d59c242e51d9afe622fb20a94fbc3d021e15c0608f51ffe1809ed753114cee3b6bb5519a9f17a0cb016564bc17834f0077bbe32b920ec896d2a40004b658c0eec11db9c0b581af89afcebdeec715eb02f658d6b809edd22e83c988be46d0781dcbf498e1b8f18f7f66fec6f4846ae70a132e81b18e1d7dbbb199f6cc6af3d597adc59d599ebb44a8a2b5bc47fba04a0a9174afba0fbe6019baefe77993e91237b6127ad1ba7c8019dc28479a06c3bc078c29b20b69336eb5643c5ce8f6fc9ca41b17b5d8be09ed96ab9739fb0676a30a51df3eb5c51b8c072610fb012c4d19fc058f08dbd3f1da4b49d5edec67fa8804f23d2bf17b647fe694b50ed9b1ca7c316df3309a307546685ac5f4d5b8384308c7378cfb69ead527f6e4f488b7847b796ae1ca2e169871c5b4234f6a79642309e7a67a6bdb525d0046ee5733368a27206668d702d7d9fa1b4c743960e16f1507dc33414880ad68c3163b6f173dcbe727ff1c7e6bad661ad7a0518c8dd7f80f0517985a64caccb5b4b0ed5b657adfb45711bc106dd1483c2f38894a11bba70b088bcf59769aa9225ae0b4547b65519a5e0032c3fdcd00954e079a541aedfc860392a60380bad9de3ea25d9ce42470aab62b5015f163848d7bf1e80211bbaeb70944aa41feb6be9a7f0159691f1e9a96adf7118c94a3363d160ffa1be8d665680171f0dd051c9d70cb2516f6b8f8fcf2f6c31d60e4f934b568d078674acabf220bf7bbe4b852d94deee74f0bf4595da59453df1db823a335bfc1dbcd98cdf90c0f52738a8db0465636d344307b9f135b455aafb056699d208acd7f869163d93c04c6713b741a48d4d2f34f9effb8bfefd445cabc37341586c297595684ec0a8b033f509d34c51e1ef9986f32d09c1eb0d071489301799fd40cdfe045c80312c20585c38b564533de26ed79381db5fa2adec125cc9227619b102ecd626f0d80e308cdc64826786d4f66eb65698b3e1a0b1eef02d6ed140330b09473a654ef167700113cf118e3ad167c0699e1ca363c8f55628dafa787babc5eefb0a07513a0a404b2796945d6c18df166eff4925750715503d8dcc21e9ac38b247f902e68459a03ed7f892aa389cd6a719807997e6bcd76576e49dfa9c601812d10706d1ba50105248568fd260180feb23f033ec08a69d20ef5afddb27f4e911c87500f632ccd0b776b3d80caa6a045153698b8e730cad39c6e887b0a8aee5ecc9bf2b6d703610e5f5e30d27388692dd1ebd52ed1ee3d488ae015a6da71b69f51b5495b11271a06af2eb752fc7206316ec3aca9e8664c8b4b12a42665171e4ce8d7cf6878211f0f97790120f99f255bddc4839f494cc02dcba23e74e96b8318ccf930bf970e2108a6a378f04fc6dc6735335dc91c90d4592ed1fc072ee4821fe4934db2af181c00e348eb0bae52680db4becb067cb479f5cc8bb15ad42b88b983075ec5be8ba0027c18337c4279423bc43d45e84d4ccf7b91b232d39ab0b67ca97c254656101c04455cb7c5543e9f4f7aef94144128d033614e95376eca51bc0fccd8b654d37d0fc39eb674fc6d81160c1cf0cf1656b9291aff39b07351062d5468effe4fa9390ff584ccd5ad302d665ce0d54cdd4d726405079a77130afc4e8bd473506ebe7309270cfc5464750bac4f1177c32b50b52014183def9853e91bb0c95f3766554503b803e3dc824d4c37cb6807ab668bea947117c29e89e962329281ed469b0b910aac609014debae07b84390f427f46a84207c683d8015b79e810c93536c00487098a8c4aa243b767a80b549c44b8464b8ad63e686212ee739e8a51f0d3b356ec0576026c64bd3b1e9b410a90f38740049d899e0dfd7ba81e258e6c20ce9560c70264501e9dbd6964962e395926e6f56fc3fcf717d683ecec0ce583fb92daa81c028f0cdf5d8c0d5a52f46a2224bcdea8ac3be29c1ef8e865ae249488cf18c435091690cbfeda777681db06a01312cf50618178d5a41db68e9be26733836ee41e011c90cc726b421805392800b4a13c817fe13a8a0738139b7c6fbdd0c67174c00c8640cab1cabe581872b6dd8e276d6da4675c31b5e30a9f943fddf0336f56db0a9183d34fd5292f61bf891c2dece8c7a2358fea88ad3d3e385478f94332336303c9281ae785a7229903324ef541b9c81dc224f3ad35e1bb9dc3e70f4d991ae604b2660246cfe010f9aee6b76c5bf64d54b53a2cfcf1e59fd3b16e2ab69f7ae303257627a830598f87fae76d13a5ddd7d1db5a31fa820d6054313dcf100459ec0d5bddcdc9bfc324bfde100cf1cafbc562d11f871df2a6cc020bea8b54b02e260be80c2238a3f38d3fceebf4fd08a46bab6cae64db56afe2a1ae1c24a738667a0a87295651ceb08a9fc996deb319c6ab826b45bc2bd525d80d29b64a64ff09250263e3ef617373ab81b4e4ee9cb65e0bb191570811fbc3796fa7ecfef21cc1df06b6c5fbf9baf02bcbdeb6e533b419b9e180716a4d79b067f33345e5d15e5eca0bfcb88807bc4f8add1caf20d02c721ac3678434cb4ecb4d7f4751a045c08e2a000f9b1f6e0e638fec59e06186f3715bbab36a201a081db7c678202b34c11c920942910f9f1b229cb1947f7f8c4917086f1fdc6a707c3a3417a5d615c343746f0ed56c7e8fafa8d6b688bc9ebae443401575d36d01013bb085c264d258e011ad049d68781a1fc6cb5e0fac1d7153ea4146c0d2979d56a585493efd306ec30be401954432d7a400a90d94fb790bc62a53ee36246e00c43d6f16601d877cc9730502e7fda7aa2c0b9f688306261a6ed4423b8fc085f70a5e906a47e72a14ed18ec045753173a2a25093ec8dd6dac5e2de4c2f74888790b50166f51710396ae2d290709a2dc26ba2620230c4972939c09e82ef44dd4d87c30926e4debe034504790025416d8906317b1b5e1eeb041b444c6bfd12f050a3b30d89f8e638d4c962dc3074e5788b7367e654d7456ea85faaf991744defcb339183af3713ca32782c3d10a0855a0095eb5cda6a62853bb9006c8fb270506d82c634d21eb72ed0ea067c8095df6eea51f186be89a15b937d5ef59585a78a9ed15dce270a7a85c212020e40c0546667cf3b45c757ef7c289650aaf99ca945fe0865cbc2a00cacad393f8910b1a663a769fe34994e8dbac3059e828e34b962bf28486e2467474248ab628c50954d474480ee0a0a65ff10e4fd183ffa4e7b4e9efa649a19b3f137a798e678009e033fefe738bd8a57aa49f39319399fb61038e07500c31de54c29c7fe18d1a0ce946abb014e85ad5331c87172768e01ada130192dfdff07f9296e050a9fce302d9ffcf1bddc87b380ea0e06a5388fd1aa5bfb8afa38ad4b1f3304ca352cc0b031cbcb462ac453bf5562fe0c440e4b91865446e8a24fafe1280ceed9dbd92a5053982bb66923eaf90ae688ea57fe9ac414101aa769da333dc0c3e652173ea200b5a5b0d7f7dbf9bd28613482ff71758b41533b3556db0ba05476940c88ad213064f1bab967b6a1b5751c7712a494e21c7fbdf1665fd265cc042377b0b42807c017bfba3425f9a33848436901ebe420b65e70be596fa14cacc8a4c204017c4ed06b584fae072d7a02447339b2b6d0b82134958343d4c5cc78b5e1328dadd25120de2ad5890dd091c8e2a19f5cf85f2ac278f17395ddaa85d1c63a1e531ea470c07b9c10511e4ddde657173c6cb56d2894a1f3ca1cb7196f13e18544f8d03595706c344be6381388d0af5ceeb0fe7292f357a815d0bf95964ab44f2355324e52709a4dc7ee627194d437cd031db47e93ca16313f0496d00ed62417d0addff22f506f6017fbc7b1560705b0ffa74e990fd6166b175f174534acb6b960f3815ff8c0dc4e969f4cb23d27fc9078b122ffb9cf68ddccba78db9c1049f5abc64f61b46077af75541be19aeb7bfdc91626169e432531ad972ca44edfc3aa722024c774d0a38c7df941739ebec13fd10a61cc59b4a3deb5d9bc130b5455d48d6393c174804d5c12686ba7efc8987fe81b60d39da79812a749f94c97ca62d6b97881b388b0c23b7c3f0bd213c81562566ed4bf926526ae2530a0169b0f203a09871cd57ca007fa181c385f395f38f6588541d94ac28ec600e13c6bdb84fb5b8d242b8d8d500bb854019fff52b3bc34d2b1bd9f3e4c02a41462b29bc81495db24c29f586ed058195107438b6066335bfc8a321a1a6369a339b00a6654edf8be2f594968c0a034d2e68cc4454537338223574158d20f9b4e4af81c9a469e96104f098ee834d0620601f655972093da8089969a7e2245eecdbff4c092f6cc67f49959ee452340d93459c3650398f7c87fe9ec4f1679de7bc9f508f857a4b5f9a7500793fefbf0d55cf7942fda64beedc8dcff5bf41285c0559402da7887cfeb6f4a204012b5e052506bf20e4501099e554698f3dfbb58f6880fac1a7d482bfe11033ba19cff80e34596e327d906a7a4aefbbfff9d832f1a842e5b48b2e0d99e00870e6473aca0ca460d1165e612913785aedcb38084d8a3e28d709fd2e79ae2b04e06ca549040203aee4375fcb19606e8d8e136464b070b9f8be7de6f1b441423355989d79940660afaffa1a4b24e1b0b64d2e2def9a761b2a189b895cbea8c65736360bf5d80a4f4ec7e91d4fb000075fcde820a2144f909d0c0e2d132f4731608e288ae4820c9e87685eb955897a40008b3504bc067c6dadb6a1a3e88cd11d418e3772f415052f858f7c3873558fa339eb1fd4100f4df31430f98e7b04704584b9868a8b7a0968814fb06e04fb1a02958fb900a36efd871f5aa684f24ab9456a70d0cfd45c03824f0ea0fc2e77abd837e9d99bedbcca27a58d00207fd2ba538c206ba914ca096965ab990abcba85959744bce1d6b911877b6b3ff4a768cd953d06435d5ff30d08f453a34471eeb1b3f59dbdae40270fdcefa469f6ce2ec3893e41a3911ee90bb4782ab8d53650b3672105e5e61a880b827c362658a0f873eaddb9ddeadfac01dc564988513d9c40c8083808c26b7851d7cb85929fa22bf18402a3be10b5f50438082cbed909eb968c581489bca91b797c354ffd8365cf862632d533bcbbd0063b3c0a80934eb914f92dec6158202f4fc2433046aa157f4e30f88287f4b5350bf7614d7f4457193ca01fbfee40e7915b01fad8fd675b48e4dab5f171985bef0e7e16773920dd27cc9c71fa75a19dd7badecfeda6441ffb727d3d943747e65b0f4b1ee6561b03691f989257548f420158ef0aa775004fc1acca15a53c3bb253027f43f82bf9dc4be9bf4c8cfe6b3cf4083d7d6568d41a2993cf7c1959b379aa000ceffcc70499f35b6b2cc82f3165e18d94a488b991afb61cdcdc1d50e32ec601cdd096b83682c80c2696f68c011a90dcab055928cd305aab71afb6b3a0b906015089a6c648ffe91ba83ef9949a817636f30bf2007585f2f731f25ec6bd52f0072339397cb5a7ee75c2bec7dc243320f90f41488cc65813a0b6bf708ab95fe80684fd429b457b1159590a9fbdf80bd913e3d26bfeba833861e1d949e4115c0102273f2ab6cd81243b5f06a0c4d42605157bf95be349e73002f20f6cb880506c05385e035d3bf2df8e8415a2b31bde5b27445b9de38f5eaa8ad62f826bed3d0d0ce1a26a9fba1194a4e293233a3c17b6f734826eb32f8d9285d625bf6c68e2e30f3f95c8cb8d1c068a5f237f37aff23805f589417ebaa4022a55b8e830cf66840d33a8777ce58d137829fbb618eb2dece6c42e5343d8e94bbb2cf3a0f1d25b3105742b287384b256e3b9e9c37c3185b2fe6e3c8ff9731d7b5a610ab45076eb0e06e22170f974f735e21fad7941fcbfd244a713f229296bf6ff9ffc3be5f0cf6e0216a352fe8080448ae96ce012f92cb7803c3a96915b8b81192a01f6a8b205110a84b478e0e0e22a30d654859658999c6df580c08aa4e698aae4511885d2fe09013396109e7c34ce3607808f81529012826773f316dd503bbdf943c4c8f7466b08ba2dd7ee301c889e4c83aa341f5d0d041688089e6511487238a3ec803887e6044a37790ecdecb6c969d4d1b9cbe4c06c10abf72c648316efbd57d7bf40da8f03e4a3598cd7fc48270971c52f134e5379b1f00fdfd048e7701eec1a56af1bd80fd2f4784535af067e6069f4e2b72997a19bedb66f094bc9591eb1be1eaec4a70291a9e81cab63fb1b3d4ca0119123403b122f4451b3405bb8eb30286e5dc39a0c1dc9c18c64e12e160d5125bd2f12a4e1cdddd7414bfbb39b3969b87b3c139e0ecdc8f69a028323328da0975ed869da993e02c0f210feed0dabafb5ae01dbd706f675e426fc21b01a4a4d964ec6562d76c517745f15ebf3b1883ebef5eb22480b892c7daf6e420d2c38fcec166516300a87eec36529956d363606377bc23af20c61010ebb87e4d5326044e0404a10aa2ae944a58e03429acafb4144026fec2e053f008290bd1c8e053886feb76144ef9457f1e05ed7d8c3f71da0c239e32be003d295b01d8c38cdca80abc44f16e010f3aa4c028fea74e326874d0c21e4ac7505956468cd192ff20d04233d17e04fdad2d4989c5fb104c9ee0967f6a943f89f0baf4278c7df4ce5481aee4ad5ad30355a448a94dd198feeec45735a5ed3b7d307de0bce6e2f1fd22c673716d9987c1683a281c3174fd4adab99198a3509656506a945fc1c91cf6ad1d0623dcbdea0cf810b3522919153cce4639e75f1184cb70265e475e9b501e3944092fe4bd31208f86036de3fa02255195fe78767aaeece06a1762e8600aa80baa3fede76d512b66fc9712920e43880d6ebea3f9acf1aa00e981bdc7cab48c9abc7d866f67ecf3c26bc2aefa69b3eaa1d7faacd1da70bda02594186fd0d507702a49269e671cb9bdc5abaa36379ca71c972068e848491810d5c1ccc29b2a418feb71fa3d9bed5dbc059578758cce8a9e2cb5f8389217cb6044d4736f1cb494564f3e903ffea41f1ec1cdd628cbeff95c62604c1ecd30a8d0a957f5a61d07e03244b5390e1d5700e6c19db1d136d9448fb68ec21f5336a550a5dfbc3950c3174ef1fcbdf53f2cff7a65970afe63b7db9407def8c8803aae707c87866b056ed45841f532ed31ee14cdfb282b234807a4c030c5a4797fd48d700d75075241c6d2f780d0342dec8018f0c98793cded8a0b086abffbac49327b3012872e74499cb6fbda50c8915303561b56116b25ccac90ea3162fe870fc2e0a00980f1103ab31ea98dcb38d4cc27dc73caa167f87ed8a6d369c7fb281b12c230db47f4c85fbeeb8319867d089073515c1d7f38f666d8b1b143e2c83357d8e090766daf1c8806b9c94b7f7e36702d3ace7b0b75c2c97c04c16a49a7b399e6b850a654fc97883e6a3804e35d7e859b78265cb62001df2ed530d8bfac6c1012a99079467c6588bcc8803e478e009fedd1a641e41700ff883a36f792636df69be9906920fea1f41d276b286c0cdf607c6492016a6c4464ad94b6e2465dd68a170f20c67da41cb79012e35d2d2d09a92b998d1816e3d8d6f6285235fb4bcb179f2e401f5710ebe771517c986807f6b6ac96b4e141aa5edce132aebf4d6891587fcd906bd0e3a1f0f43949b1e561c98d34cc468636245fdb83d377286059b3fc06400048b3b4629b06a4ebff500a646a5997038705d4f368263ac435a072e8304244206d3727f4abd52dcee85d7dd388bbd1f6aa5cdf8af0afccce230006f77c84417096e750ad54841503ee1cc570775fb555167d1d2a385b8382c22b8f9d34b4feb0ee22af46a1355c7767df55411b83de50e040a19eb9278b9117d56ce27ac619f0426f843d6df7ddaa085f003ca62267be47355bd33c16e6573fbbf46a52698d40644f388d0d652fa50beac7ec0f47f0d7d0ba783c26a552aad6775b1a74205b60a8c3d16ad83c6a97d3e0fe6a449104d0ddb417d42b345e330264a922d54f15b0a692a00a846d8f50acc1abf91e564340a31cd773216b8bc8a8b1d466c95081909d23cf49307e93a0b9468069e17c353205940b2622d86683744b5b3becde292034ad619619ce5be6c1bff1c6a3798ea52f32130693384d4c3dce597509a257d0cf43d4b8ae09f434caa1e67a39a398ebedb35375520fc88e0c1d7723da7ce76041ee0ce7365ee42b7b615bb5984acef1ab5e536b9f5a84371d8498c16184c8209630bb7f9ce3dd248eea13536a0e84d2da6c323f1f4e58e07ea8ab2d0014dec0c902eda23550e40691158f93dcb67f3784e1390737ebb0037aebe322e2c7eb30264e962fae151ce8eae04856e59f1413295330157034a5417ee3f3ab025f2ab0f2bc18dc6ea8b62d3088b5f44189348d193d663897db455d833de49ff878eda0c33c630962598f19de8214eee5f46f537d04d103779c1fdd6d99812820fbcd406c8fe6bc5f0a2283980a2ef6144e74dc0ef02e2ca170f1748f6fd258bdbbb0506d1a2f919fcc2d712835bebf4f80c445161551bd3bb541064ed38b3f7919f990513f9d45fede6ee9495dbfa8a78543c8a546f0568f374d8cb2d217b9c744dca06daf57a05e4c4356ccca3a2906aa059d427d284d2aaf72f24599ee3661e32dd01e29273a8742dff21244eeffbb3b72c6d59a71d6bfb81c2cfb2938edafb9b310992f97fd24fbeaa3ddbac1a9d6d11f8b3d6986e3476a7cde4d122ab031274ed063a22a1c13d645a3eeee5004a8243da52592422439729359948fddcb05fb8d503b017e5022165953d479ecdfa936311f7a15c922a72a2ac1df48dc81b95915901fda045f90dd72c295f86edf9cba8f470ec343d4b87f89c87ff344da5d8553409efbe169a8947827073dfed639da8d7574c1bf82267a31ad3738d296b2a681b0452c29c2261d89136d7f818986b93e6116d8bd07bb1779dfb599df567e448ca021338449890172a0e325f994c664067afadc06c2fa2192f688228ce546fdec90052837a5118ff636635e23dae9b4c5b4dfe54b803f74d83d563430bb79447a50f76542c76615b45134107719074effde412c8cc5bd07923b52d7e29e7fd7b2b01f4ae4e781432fd64a96fff2b7d1698d40928587951bdb829f86df54933bd380e25fa21504ef0039af9f67b3759ba6c45607656bfda525f159d155d29881a1e06b2b09dee85dccb3dcd42027d9d721ca37493b0f10547ab38b8ddc5551442d801313cdd1da4c419afca36d49f85c349e4aaef70002343900daa1936e1908afd03fdb4bddb2f4b5a906cb262af7cde5167ca4078aa9838bcdcaabba4d1eb4fca09adc9e2713192a3fde07636a5f273007f480a9f35d3c5d54e36e5e9dc070c5c0951c85b97462b32450a37805d9b9afac160a80377eef9506e12a7abf9d518a50abb4597b6c3b1e1838b0851cd5dc23e8e84cc28c130acf6abdd8b954acde3a30e92b369d8cc34a67af91738c2a357bf08840bb03675e5175b7c7a06613bc90e00c28de8fdd51723d831831ff2e602c846683e9a93336adba693d788b8b88898092b72d977c411a9da80df83fe48fdea24189c4e0ff80c8c01313e52c6056fad0aaa1f27d1de07fb5ec056092a72c524f8e9882ee3b8f79730bf021ed5ad7719034b460015ee2bcfb280ae40c8eafe24b52fe60724d9cd830c02978a199c42270aaf7be141ee57f57a74a5107ea0cd7a33050c071093a04640402a0acbee801206b1d5d34c930a890e4f7a1a808aa5916279c5c2faee1e489cfbace139252a220ca73477448fccf4213a842c4842f89ff5560d5c128de903adcffc3c64e1a11d010e8ac1be500615c62ababbc4d90dd92fbccb7835f49746bfd2569a453ee5c80c true -check_ring_signature a7e88fe18670ec5584deb2a49e8ea586a2e0654a2028d7937f1ba1033a526768 571ac1524f5a4b0780c359c815f903d6a44bba8b28a43207cf32c62ab14de18d 1 dad051029c46b8bdfe89c3f27dfd419bcd7374101f957c9d3a813c1571df39d4 c3a6baea1e08493324a6db2329f505ab1def3eb802ac2f02e6f8814dc789c40c794987b14cf0fa642eec63e3fe41777cddcf1aefbb24130303c5afaef7886200 true -check_ring_signature 3993efaf856464dca73fa5bd7294be05d34a3ce8532ccf3862adf3ba9fc00575 eef681545047b99af0f5f948b58f599b3f27ddb19fa2c220511eca8c46140193 1 e627302f8f1d0c8cc096f4b4241036761c356f3ef5ebac210c476bed5d8173c7 14cae9d3e8ea4243bc696e86cb236b1ecaf253cbb4dacb9d07b08868c474470a17f252318e07e43cb4ed5e1ec725efd2f3d02f53b9dad5bf0d26ad5269416d0e true -check_ring_signature 63f9c0725f18ddcb419e629fcdc87f9aa9e96bdf855c1f9063e1acea7f92f243 039b4fecc6ac7ee5d80f250acb9f129b1149f7ca81fe8156743a9879e3571fe8 2 28c73050e79d9b179277e78ef247ef816a3014bc63f9d24950de9a35a8247b7b c521224c343b4e3cef3126f3b26deb9f609ee56869cf49c6de800b431eb16f78 cd12f09df0bb3011d259b22b3097224162ae43565c5beea152ea378b14ea9d0bded0be5fe06d5248fada88cf0c8e3e705685463b1b90659460bad91727521707bc3eb2996a0e80ac67f176873f0524d3137f98365631df87430bff2fe7a7e802467cca47ef6cc1a113bfd71ca3ef59a59d9ed945b5209c5f3e63d75742370609 true -check_ring_signature b897767654e402d36bfb92bd4a5024822499baacad99e1c6934eb12b00735c38 63739be1477b05549d493d149c537221912d2505ee92730bae6362e256621eaf 186 1a79aa5bb9b5d76935b0909473fab601c496e5b0ec6a312bf11c29a49b7b428c fd74c80b6a46b45cf4f54a6b0a17504a2215c5d373a4744a41fa5ed74c34cd0a 330349a5d7bd565c2be161b897423f91fab0bd096523df3fd24ed14a8398bd8e b5427104f63296ab47979dc8eb71d69dd4f75e47a55fb8b2a7b5af1da8381968 f6c21c589c70fdb7ecf4cd1cba14ec6962bdeb64e2b0c13a8720fe0a2c2af4ba f46e7b056eaeaee5221ae3221c13feaefe2ab624dcf001f4257457757cc8dc3a eacdc10863612f79acccc2b9908366eb8ede603106907d86090ade9f469fab63 95ad3bc0ab7d295430f085b60ed935bb63a95b606ada15c1ddc244048ff56e25 f334d615d88c75a2accac99f0ce5c86f74dbf7f4c1c4f7543007e5346670aaee fadeeb84d8570dfd6e91896ec5fc927203118cfa0b3e0b438e8807f98424a6bc 97ec454512a8ada399e0778baf76d27e9b006294ad2b261bb7b248044403d4e5 acd7d383ac7ca753e34608fed714f4c1d72060211273c87bf47735f78ea9f9e5 8fdb5d10f35f167c67729693d6ed6fe0cfc36eb5289e78cd46d6d3068f59ecfb 9851962775d26943fab5bd296aac2410cb44af5ec2641787ce441d68e2a4f88d 1aff5da3b277b2ca9969b767ea4ab361a727f645f43d23af5c65e391b27599f0 e27e232ffabdf33448b42466afabf43b200b0511231974dcbc484825219a133a 3aeacee3f85c090c7692ae62e5de661226635abed852b654687c0b5f7db4b1eb 52b7702ca00eaa228f8643dc98536573ca77e50db250d55019bfea5204298dc1 179220df8feb6be738e1a0f47179da5f824bb72417565e8417f986da74a2fa7c ba50ef608673df31c35583c67cba7bdea0850cc4327d57779c96ea79668ae52b c0ca7693034da412df0485d0ce10e615643df970e09a462c78b7a1f9e01adc16 1ea4ede63de5781959691ebe5538e620759709ad0e3c1a6d15c7b0eb94c99d7f 5c850a274bf91d06b0938923cc7dfc3cf255188a55c7d92f1d723a6b57621754 de26829a5c1e8488e057d4e88f14f8d9a0214e4572e8fe45a45c9a6f4db3a3da 4913f53eeb07f199de41d9e8a41c3ad3c21c79cd2bc5650059e68b23260f0df6 bff67767c564f385e033066b9ddd21b7c7d30ed2d1c0f90a38f55d87b60e36d2 258be5f0f18da69701edd9b1ac70bfb3cdd566682a178c7bd7624d305d0c7c16 9b6819a416f0b1465e0cdb2fbfd92a0604a82866e27d8ace30b1dd4c9e7056e4 7b4507c98e60f49be498796668b8020b8f2c24ffbd23964af33ff2d906c4bd8e 386f3f84bbb07121ebefb8ce9ac9b4c477421c68ca5bc713188035fa243b9fa6 9710aa51bfb86603fb86bb40d6f758b082348a2097ccb885a83c7952528cb3ff ac5b187457f8e4f8cd973fee418475defe6d42d7541e26748fa439a2f359f993 123fb72ee54c1367948c395a7194a83750f5aeaaca316b5f72d9432e2d3dd879 69c4d592ffa35cae32a993756825b2914d3073ea66924eff0a7035390bf8628b 09ede1e77ba4be3d59ab335773ccee2bd492a12e1156865d68d8bf3fb3d42fd4 85947a6ffd4a281ea43e1cb8a8a78d1697b0a18fc040a05c0534a93f7b71d0c4 d8ed7f79fe33f86eb35c64568b0aa54ac5cb6ee6fdaf35b719a4e1f5e81ad4ef 2bae6b20b99f79b728eedbbe672c398a81e8bdbea52c6da0b3659f14564ef828 c03f8d752555b2c80723bc1c0d03bc7a5b6479ba8e2f0a4ebb7a78777b4312f1 f1052599aef15f66de51cecd9ffdd7bd7823d8e1d74a3e5c1b30a449155dc801 985439e737aee8f7f8375d33790652df88b86fd7d4365775579b7c8520914626 2d3ec074ddb1857a81fc24b08615937a48237155293897ae88900626199f01a7 eec80f893f943d84bd773eb0d94cf1baeda04fc9d335cd5a7282cf2670a4e925 b1c25dc18d96b03b033d47d062204437c9f841d44276128ecef79befdb4cbd3a f7e176ea4ef50435c3845fdd77234c1888f9fc2aea79d2339356a053f3179b85 5bcf8b0cd2b220a1b88f47d7819eab0acd261241d5e24210c970182de913898e 640a78ec9c74829fea70d12a9120f1ec31db051e6c1929ecd43d392595e56d32 466d729b35b915b824a71675a1bff232836e5005f69f1f716ac383b9c38be319 4f49530cab4332f48aaec658a1cc56c50fed688ea9b2f726ffc41c2305bad5e0 573d50adc070cee415362aa716c3f9c9a0a17bad3addae7d4d04527139eca75f ef0bc94289c6c3ff08142d096d3e917e611112a8aa2849a907b10b7c4cc88157 d328829f3e93ed8ecc2c0c20743e7f04a1a2e6a2a188e26e7088ce115918b6f6 5aea963b4171cc6ddc261f1f6a9222ea7370715e35d4c9a78a8d53fac40ae7e8 f5c8556784de5d69a8217ca8305a47d5e53ac4cff395ae5b7df60fab39a19e76 399af49225cc56d85b9c47a0adc3962ba124b0cb0b512942f571ccfa0d1ac8d1 04f868bac8ca59f85d83a8e7d45b59be73f56c77e5368d87507b2ed19a405d9c 5499276c6336160577249a63273918b4874b1ad55f529dbebbaaf1d0b638f039 407f2a24c7365339dc90e2ef9b7bf39d4c769749fabb5a57b3aa125bbf63aaad f20911d2be82de9ef516b30c645dc2c8ec32bcabdd12969e61f46c14e7abcb61 1b8eabdeb21a3220ec8c675a036653e62b8679e64a097d28e49db52e493774ad bb85ed8f3c8e8d8093313a61f472a3d648d59d9b03dea6375ce5850028f9a2b4 4611787f9e48e03bf6f60a40d460e4d3222e97db263083e825a954c4db565b6a a5a67439a7fe7eaf1d0bdf536f9deccf00533a04f0cd032392ddb8d54b2dea2b 08b7367d71370e081823c7164c544b6d42a49ed79593f472adddee2f4b29679b 94ccb2072796f1c01066bdcb8afd8c128c22f68cd7ae690033834f068bfe53a9 b9d49a1f7199a4b47b7ec951fefab189481974c7b276ac902be3222f6fc6bcab 7247f13e1c507e6d841bbede828e0aa3de06a94dd17656fa2c55196ebbda8d04 814645a0944d49b9d8f783d10ed4cf23b70dbc519e286e4e97433e5b32c3eb6b df1d7c600dde21b6a7691f1e3c0534d672d356962ff719b6f585be9017e43173 df16c6d7a80fc047c96cb84f1f15cc51b0263e91f7250b45cd0a8066279dbc99 aa2e391470970784c64f8641a27829e3674826c598e2963fed94fcb58d6917c3 13dd46a165c99601e9ceb40b3ac5a6f5a562e7b20af4517f297020ba1785feb7 e68e03f673104e67a16d42c213b3ff2faa1ef96dbcd3dc6d77935baeda0b3b92 662465b2a35d3ce5e2fc59198122ac424958fe051e31479f5818c870c3c81d65 16744a5ef59f7371a05a3e2947ec28009dc4cd2410bd6953f8ea86296000167f c26f23b8fe6a358438ba9b231eaae73fe8325576e06381378c97f4aa1927da71 8321e307e1e7b4aca16690681207e27f8ad5df56738496e9d3fcad081367d041 576d346e2be7d2e15cfa6097a4331024c939c0f7861fbc2a159caf615f3d44d5 a62d8ebf3e23068a7bf42d666fd1a817dfc2d33617eaddecd66d5bbd5ebedf1b 0f31a23014e3bd133c353b6215c576eaf002cd36a1575a2311bb9d72ee594e8d e19035cf9d67787b373a35b02b6dcd1cf31e3728de09c33c3283c3940be24eb4 f17c801a680aada318fdb88fa5c2fc676c5452442a7beb6c9540f6afdb80f41c 3f37f831cdaaae3d2dfe81a324c805ff17047c5e3950853c03faf661a2da7d94 a325e652e9c828236e6d0a326110d50731d98eb2767d8b21b15b8522b31d7609 d82977407eb7adbc022373d3203d324112ba02e10adfc636c81fa3f5a2ec0b71 fbff791dcb9c0b80b74f09746eb197b1db7e657949672f6c19dad1b410402ae5 af7d7c2f1bad11ad20af1b4e9273589f1807ba8acb53c328b9147dec5392212a edd066f3702a4b0e506e8ef211e72b8d95818f2f663245b2c4016e20aabce456 c7fc407ab5256a99366067a505a65f9dc1f58bda9c5f5feb4b0bd052022c773e 90235b8d42f83a7b66c8b958f1384e2470748c9559273d685f2a8f72d3990753 0da3b7dcc21a44e402c275b78b460831c817d56aac6906af4192e12626b195fd 3873f0f7f360079249f26cb0e652d2d2557bb95b7771fd20cbff179aec23c187 8fd3a01d531d907d1e5cecab7885019c95e31465a94a295249ef2be6c6fa82d6 4896d6e311fcb4041526d7f639ee18294d18461851f8c2339f4161c3ea1a78d0 d6d3bf08a0734cc12106d46a083ec88cc4b0e254c01f3e72c303273968599d48 99e750fe22751e8188b786eb8d74d637d42d783330c3927cc8a5710a84570dc9 b08f7d4bf5e508f2dbee1581c3690d40faeca0e21018621ed2e708fa148b8f39 81530f6e79c0fc5607f7a13e53cb23e1d50dc7b593595eebb19f3646c1a35737 5a190f5e79f1c5d4a59e5c034bf904e72c868d11d3d08eb57b21f5f5f7316127 9bcab9b9d13ba7df19a9a830d12549826aa34e23c6fdb324544cbaff3a711aa3 13e58afc69b0d4a4fcafe2f28a7602ae24c2ca35f61c458b8c4a692162d13ded 1a208a321bf537fc051d613ffc961df6a84f306df68049a827dae1bf46b46ce1 57091502d9392300bb8ac7456b4710a3c6356234281ac88a30c3229eb5f952ff 8e740931017e220122620b480673823420140f569c6a036a128cfd583e46b189 a86e919e84759fbb73f22cd61261bbf2281a06e0a7af8684f1b52ce6ec5b0337 a220ca4eb5d46ca7322826dfaf96d522731cc213c06e78ba90d76f72aa76094d ef5eb8a547ea07e22c2bf54d9b789faad6cdf5ab6bad6695e03832eef9db272b 5d26b8ac238ecdbfe075d52d5c6e88e0e657bc25eca89e7d893b17d6aae5de85 bacaf10d2e60b846273baf1e76ae51bd0f4821c2db73ad684969495be86334d0 705a765bb668805b7a2a71bf183d39962129d96c8ece24ef00db44955a2728cb 98dc2fcbe9689c7962c3e24453814c89ac29ca7e022f6e176293617ba77e0db5 bd81e9ae7773f58fa361a9ad94a50820526205b41281aa922ca66a17e852e01f 799a789df72be3721cf6a27648729a25f84d70efccffb4d8db6e4ee882f15ba5 ae42687d3f3554318e40084523f560689b7941f56fd6d585cddbe0b461bf6e58 d1208e5d8babe670831ce83832026575907c5161418c3d0b62113e54da413d20 63f4796e17c737841e6d19bc403421c5b9bbe348f23ef52455cef54ed0b7e002 57ed93e77098ac871cf6fc09f72797d3745fee4dee0c84597b6e5e61399ff91b 46d6deb7fcbf75bacc9a15abcb63938102f97a75bb72addf57f5999c8b2d59c9 3a477b19a09f1cc6c08cd9345eb4b959d32054f4111d198b3a7cab8e792b0976 4001f108107edf654de6eca0f09783a683a95b2919707de246a85e351585286b 76b703f61156df1a0889bef90dad914e50d23e9ba49d19a9618f7f88c5c0dda1 fa4eef2b73bc062733feb043361db8a4ab906b7dbacc8f3770d9270b43d928e4 0e5ed9eb248aad6cdc71ecddc97e9aa00b8b57ce14990c847ff50e44464e4439 52b16374c390e6e59ef2b7ca7666c2d5d1014cde5f7bca905623240a70691c0c e542d03b6326c024860d860b277bb4ac4d7f0183715861cd60fa5c0c77c810a5 200f846219ec5482811f333bfa23f3f9d6faac875f1ed4ea0d7218567c9cfa8b 937780a4f5aed2a12ea6de17f3e0e4e73c0f53c9bfec7fe28f643374ffd6c7b7 50c95c95572c028afaa86baf2b441936c67223ffa17d3e2f506ab9753a19c8aa db5d2fc106fe7d29aebd8222e59d1c243a427956a7496ff8c5b93dc9b35db404 870dfbb6ef6934af689b265a67f077a33fb33556cb0317757a3975158a8492e9 7973bb064e8d75fac393a8ac08af82cce6b0545f7b49362f07549e7c67d0ddbe b63192a55f916438e8b5218653e07a124a7eff2e92a372f52fa5aa3e1e6083fd dbafd49af42e4ae7a48bea12a7f5985374e22b6f7ca70011370ffdbf95647e1a bd20e005b5fec09e635743d69ae2beb362150175db4a270052ab0ae41b1d344a 482026fbe47895638db9f2970f95425ee0744bee9f097af623b7a27b792eabcc eebb05ef6a1a7e5ae1b02ff2b9d75c042e164bd54b5f0d2b05a509e1bcd8fc57 96e0423b00d21da991da6797465961ab6171b105a8ddc544041a15ba4861afd5 148d0622d401c1e6d90ea4ea91c5ba183f8f27f37266a11e8ea03e0b9cd89657 8b26559157b9d46bf4eda311ec8e2e83832266d4b4784e6be50b794371a3eebb 5c7dfdd897c5056f8ff3e02fb839220671b9bc9808766ba1e31111dc2fbf22fd 2d493ada754b5fdbb91145f1c2a540ef019bd665bd22a2602906d9091e77f421 5a39aa2150ff881f4b4d851d917912051f9bb8f5b605f7a0d285acde2b4ffa79 4d063bde7f1a0d4281d27f600aec0439f3ed057f0f7ca46ce5a134389b1c0e3c 93957509d60310436ebe0ada6b7e5d30b99944c7bd2271c1f234fe35e3c7f53a c147f15325b185f5849e92288e5839081e8e7d66c99f034dd42d8f802fb82c79 759d2b0a8233372fefcf49795f76d5ab50daa9298648dfbaf490af4863eb8827 d85d058942e2af2259dc3e733dc3f25ebeef85f83321fc1d70e44c79ab57125c a090ceb71ed51c955b57faefcd3d60c0996048584a8adbd74c82a5cacc2c7357 e9e7263fbcd1d44e6dbe29c682310796161dcc115d050ed636eeee28cf4de045 1117e5c8a046f2bdca767f25d4c89964e9401a2ec6bc9e9dd297fdadaa16dd8e 4ecdabd58d037e8e863c41bd632df9bb23237405a200b401641c6ea4755db66e 72e57adc7a9b29ae6007b12b9110973232cbf87e61df335fc3cee47e112137b5 fa5e9b399e027b598333d2daef7fd5b1400df5313034148e5c4988f9fcd87f03 46ea0bd4f125f58cfb80bd126c65788abd3d64632259045317567053f73d773f 16c0d2954f29b4ad141da6c70b9bf7c71cfcb51f1074ad2a891ae6a7d6356969 9e38dbfaa8cd1bc33be3b339a7957ab0aa934d2b7074bf034bd01fce75cde720 26c3df8a57a9a48570e44573acae6ba86a14b48ae1d25fce03aa74e66dd57aa3 bdda7d9d7fb5b5203649ec9d2e303f186cdc9ea9a474b341eb00461aea2e0dd1 39031119b493d11e82960e0e9db35df083933f289f620e66af5bcf60651a9e0b 1c68285322e930848e2cd874a1722f1a96395e3ef5eb8e27f4364b2679ea9f55 0cf96fff7dc647a3d78daf25cd124ca20cc826e7e43873a8afca9b604aba573f 3763ad52c0c9a8d6b4a65cf48f4c0d5250b1a8c673fa59914364787e327fa619 bcfa950621408abc21e6773fbf69a05890a31af27b06e3e26aac05e994a915bd 9b651684644b284a2573b46fca31b27642d6c453f512d323cd1cf5339eafa494 e0447f73d8c4b390e97d87a91e040eafdd4d0fd4bb14366e2406b236779733f6 e8e889ed77d150a424d236f29d760a1bb97d4bc58b1975fdd887438215a3e6c9 1a8abb0cf29816d77d3d0163aae1ecf26ff52a2fc0bd26118b517b2dffc1f68b c0f6bcc07c4ebc2d3af73e8947654d0c14084ff6780376f591019f706b94f68b 91f176bb2e2c8d69960cdf18a8352dfb59092dd5aa2078836ccd7d75be60222c a150f517ede6a5fa27a6f37860e509c4a427d567e2a706429332f7bcb4f69e33 a410a2c480bfe5d3b283eb361b0024607fbd2e6eb93db0ea36e8b385f1175740 4b73205d86eca8f160d5da6f88276f25608c208520abc7aa95a332438730522b 5d5c4e98d64ea1e0404c1972f175fee65e0bf6faf02859b4dbea8731e6fac0fb e0b3b911e225a963e914fd944f1060d744a7fef49dcddb4cf9a388b8b089539f 842b9eb3d83768b0d087e17dc3c763327670bcc4023f6c4d5e32bd863ac35116 bf6c7c82f7e2736ae1ff35a83a7588092268b85ad9beeabfe57b787686a2ee9a 288ea695a3ead9ecd54a9b16ef7f4693cfd4f639eab006efbc56066fa356366f c02b0262463c152414a4f2f2cd452c65ea5627d1aeb0d7e85c51cf5d9617fb44 e9f7a3cad81665098f6c9fe5dd49ec43fc758afeca8556c8711edfe9b156b13b b265b7cae8cd40d34079a8a3c6b3ebae18cb80321e02a8c306f041826222fb7b f7076b33dff0140c3f35509b0a1ca04e49277660028492a21d6e027e1fa6e28c bff28429cef6cc90cf0fdde40bd4254fe33eab16d91ac23c29e218a115ef4527 e5680b4dde7ff78015ccd99cd30257ad7a89d90f402aae036800fc51270ba97a fc9e2c64a2b88bb1e83a1f424e14c4556f006cf3cf375e8a06c41265488fb36e c16b4fc9ca7292fcc62546301fea6fc6326a33f3ef7e6729b28efea7aa9f3ae7 9e129ffd6d6a66022469cfe7ad9b251c47c11103aa1ac5c196c10961cf5d1baa 32c20deda272038f3907abadbf568f0ac3d1140be8eb6802719ee31df584190c6935554239618c57ada2616ddeb6ae44a12a41ab393425e8bfc00d419b935c0a4cf0fef986987ddd6d0d80acae433595acb28367fa5d08fba3eac09414310209a6bbefdecf678fa57b93aa8608ef9a9ce54aed1da0298043b9670c8e7d2031067d0783f31fb187a43a1774f8b3449fbdfcfdc80c8f69cdfe69ff64dfe2a48a029bc16e79283316f167a00fc7ba1e4858ac89a85b97884817d26576f46ab9d00f6872388ece414eea53bd822789e6d4f0331fae40e19521ed4d2fd1aee929fd0b9d98bcfab58aec0d53ca21c798f2e6736bc4496a6b7c9836a4fd01da8bb23f062d8f6ff39bfcf65151673dcc36120b326effc93e6735949222c85ac90ca0020460fb43db7e672d26ee7989b05d06ee00cbdb15c6e1558ddaaef08e304fb99b0d0c9cef394c90a23bf25862ec5b89c425f2667e71ef50035877a1bee2e3d215054e1543675cf0c0075414bbb08b91711c61597a3bf377ef170b9c254ad61c150b795c7e4f42b763cbf3e3d84b3caf6674c8446784704ae1b5c74da7e636ae4f06aa1bdfa3999b2b07503f57b32675812e62ed6faf993f0bb201b8b5cdfe7d800e709af173f1c597d4ee204b1f55973a93eac0b81714335e272198e2f58239950cf85769f2aa3d6bdd30c5f8402089b003185d1b2a9e75aad154344a3f3a5a13077e41b9d00a29f4e5cd8f21038d54c2e61f3bbfc713ddb3c69afca0e440750e0b386a229c2dc20a170a8a92e77f4be2ecea84a0d3b3db293cd00aa4723460dd0f898b2ac687cfe32c51af05e55d52a06840989036d68cb603af5c1e28d79cdd0b066d10586bfc14dd521a642d05572b83b3818cbf5a3de63c51b7ea4c36578a058348f65edc1350b0cdbd3dd5afa8513f3326a91c8693018e314d4d5fc6e8bd02ae825ec6529c954ca7ad320103cbde47bbf8c4692279e8c1610358227105be0e0e54303276d0ed20a0cbef6cf039729556fad91c243036f6a16c920bf76ed407382484348cc4a98cc884c6c00f39588dc4a1e5a133a34b548417001b461b6100c2723628e55287cbf8a8e5ce79df27f394a7011370c0642c6deed24cc4f4c70cf7a9eb2283c2e53de4e465be93b5a4447747343646da64750faf9fa1f1c2740340b4f571aab20a10d53ddcec8a753ef01ff6b42b285e3379666153e82567e6008a786993d8276b47a9377250a96f269a94ddfb0dd443ad780cfc6440efe51009d2e22fc28662c400735a80454997f6cdc3198bb2c9406ec1b6f13ca4d0cc5f0adb930177df96f72218573818aba28c1d50343bb9bb44d4ddfb43c0c2d90f5e0facec3d7c3a9ee85f98ec10b326de4dc5281287c5d7f2cb47bf42b6e3e11d0a08749ed669ab622ded4aa3fe5afa34a6204d8a13e4c9f9b37ac3b6b3d6d30dd00628b53490c2e350a575b0dbb04cf68b6b9154623836555b56b4142e4fc485d306a537b092e1798213d5729ac32ada2699132e5c85dd052b7e384dfaebbaaba80b26419ba9b8f70b8410762b67214c19ce959e16419bf182b43d93aead1bab35019a6b1a104aa242bcaf92f89a95b60d9f34a205f6d75187dac8a346a586a92f0c4cdc519d11384b07845398ec259cacddca1fd30af7532e75d670cca54e078e03966adc49825c189ae3d1e37b29db5ec494edc9d05fbd2930875366ab9081b00a29b32d16612925406d3806c4c052bcfdb1bead56021466695e0ffd3bc7655407ee84cb6e70df4bfc4616dbf8c1091e893677ebf75ab8c16bccbbe12c8b80a80cc6832aa79bddaf092ff80f57b4c4f17a25877c3ce1e3a3a946ff5ed0d1542709abbfde615fe72ddcd400ac19fc06d1287d6f14b295c84e76d892796f7b5f4404984a51b7a008bd162b56d8449b63a0032ab51dd7b7ab967e5a919142bbf77d031c5e77008d24c0606e5b01dd6dc7694d49dbb7a1e2c0aae3efa64d2f4554a7016663cc3fa51ba6217cbd725ed6471c75a3525c7f98c47bcc964c690d2821f30cfce3726f214319ca7a890c1b47523b35c32c1cec608ec8cb022073bb832881004f4ab3675865cf8094f93477d708a35cd687472826a6216d5ceb6ef3acc494017c58f2295e929f4b98b4f1778996b577a6fadf53989b10e13e3109e1374ca407bbc5b71f4e850823df2b884b392d1b5ef308212569246f517d56b6f5abaf7d0c6b091b8499e282fe969b65443ef8b228cc6a1a22b859fa21ed70af3df5bcd3054039111f8e34513cd19df62eb9fa3697cb21599b94ec8e975801cf1a39abbd17cb4126b8d59d0e858174938435500fd99c5f4c7a7f714a493e499d5686ceae043605d6739790bf523acd85e19ad108153c39bd8c9584e5df61344089751d2a0bc657d7e7f6706ab1cd1f8cb8e38e9257d60224af496c6f8d8baf1e1415d3d2077c410cc7c34107b214486e0f1b7ce0d82c08e113b7bc75b5743a42a79b0211028db9cedfd5e285547c490c6af72f7de07631e2787505362d32002d6eadf65a0c85516598293cb45fc0d150c83dee57a1bbd59dfa3366f8f893517a42bfb4ff0836f2c55aadd2fb4c60e27927333fcce56aef6d41d7ebf7ea51cafbe062631e0723f574aa8deefed516b4f894a66ebc9236a3d8d1ebda7f60ad628c1ee35d0b0e84eed4c961b688457b0632e09d8f3439a11442c9b1ca8cf14fa9daa383b77e007a57ae70d59e4c44d9eab5fae644d11fcb5d82e482199bcee44d10cf7f01bb0ee63a147388b192f7358e415bcb3d6df8dae6ae9d676dcc8a6ff81ac5a3b969087e754cd4f4fe974716326b618f1660b9318d42ce8a7a628641d06766e60a34017b3678fdf7e13695b434fe7ab2fd297d054242a0a33da5264969e11618930a0c730e05704d004c77ad295a6b63d08060b43d1c19d29ffd8912d6a6927ce3c204be41bab5196ea63f58b2463bb20a40cae80876fc601d271d13c638b131ebdd07cb253f97b50f7427b2012e394c326527554c38d12bce8e53bdd9a8230b00de0b9258ae0504d6632e2e7c564d3deadad385a7b5baa32675f8d9642800d37f00096c9e41f77f327f16d7dd85be7a32f770c76ff19ac53cf0fafbda59359dfe78053ef32da38107e555b5414b0443a194180f9d25903a023fb00c2a21aaa45da601cf117d72f211ebee275cbbb45ed86ecb0f68295e5fbc89ed502da430602b770df9630b7c52006f46b01ff7042ef5ad0af451b310f222b599766813d5026b430bbf3e0cd242e726f167a07938f410ec9d2795bff05a6080d4cff369d69a3b2d08363552458da4af6d27dc77c6fde4bb12ee6168b99c7c48131a2db0a23f30d80f69b58dddae254575e4dd13c9eb8cc8f24f2a94b3b03e35c8e52ebc2ab0aab306f3ea5dd2a8a1209fcbb52f409b224a9a8b618bafe58834a4884c33dee4cf7e0715ec6a82d93599be09c303a1ab9d9b45e9925b938763b2f3db20f9eddc9f5f0f66e5c9e6b7949b3b960509e3863b92065cd4a448fe57d6c49750b6670c877604fac2e921a8b245630047dea394258f7eeb9105be505f9f90ac6136cbc475a601dbfb38eb2727b02f73545018a4176632548748ca28da4f16c5b048fd7349ef0a6cd1d34f8785bad31e5dfde8849b06e09b36de29e210fafafa0209a2705ebd09c009e83470d77522fa843ba26336a5751e3485f1116604cd7fc026745418fb0669e861ca3b20c2bffdbe75d4de3c439354ca4c6a8998c8f573c94b9717dd4004341f43264c80fa5478857b40d2920bd7755ab7164814aeb9ecc250929acef70d4e31f373eb58067a532f0fdfab06ab87ecf114fa32f9608495c6b97b6e28b30f573e470b27050506d059d4db975328ae3a01ea75dbffdab60cb3ffb7112a2809a22a0c9a62edc219917a1b7322b195e76c946679481c2cdf0d2c5a94a6a8f50569484682762bea0572f447824676526b657040ab47f930cbc1666cca20a74f0180e6b6b5089714d4b4fb9c5ccf9abad47bfc0717ccf00d58bccc286c0ead1a06a89ad30da7ecc4ce1d1982efa99864afba480ea28148a8495f7a46ed633fe501b3f463542a86dcc4f1bcc1aaec6116d70370d4f5bad17b7f5897fd4ab493160fd4de90d6a3cdcd753bf32a3c520cf79c817e325142475a2f801fdaf1168061048fb650af87d1653b05e63c6072436b6f47be122cdd1f695d7f561ad10a967d032f10fff10b97b70e1e60ad141224f4ca880baae843d7afa20b11dcdec0a1bb0c2e3361da75ac5b67e9c6f132a98d612b06b30e7109198322766a99f5e08859072ca19f046e8ac152e8769c38a34bca3dc1dd67582e973f5caa4c3ba808635702346bfa5e16c9f1631e600de68a281dd0b5273ee79053826bf4ce1e4f7994810cef284f534cb88ca0350b887b91099874221484cbd96a9e1ebf0814d2a40dcb07d33de883761747e8c3539d24b7b32eb83501dadf90c5cd961dc1786de772ae00b8e8434a3f4ed5bb929793df9ce384afb4a665ace38c7a579f13dbd80a90c20d235e7b88827caf68e2babfb12d10c2722c650399269064cf9dbf2b1cf609df03d7a0bccd72d2d44197277413ab496b12d073174961bfe37dedf1092065c0950fdcf30a26ec998bde14753065b289a2d40c4e980a2a32268df4d0f86920f9320d8716067cf72b5d98aa3d272d079cd105500ba4710752425939f7d01aa791e202a28225a181c9f1bc3dbba361d4a2d4029cc60e32218ed957901be5076ab6b60bb264e205209611a6e512e5b3e328da0155aa09bdbd71b8c63abe92750b71b60b1be823dbcf7c609b22751c613b36f592cc79a55a8b1167f8ca528d4aaffc6b0279fee981541408207ab0ef300948c3be34f558f5c790e1eacb285c228d69c90ef1022f6607d16c9be6164fd3bd2ade06fb2298a48f9416e1d23486cd35754a0a2221738d3183988bcbeb01632e7164b2f870b60339dcd8bf63eaacd7d06d2a035507163f63c6d45e91480cde28c3404bc7cd60cb7856089685c66f00b9ade904a3f82170f2e1db1d32baccd4a5e684edf5aed67a54ebeb8c3a3d0beaf979d201040ef3b86b33de0ca460854c51616a728609ce1e9d0a78afef67a1a53ae3e50d56c2ae0b9e283072e5aef325f20cd8cebda8244e7d5182624acb54200a90a90226f0049caf1c782a14e233b4889d0f6844b4ac7bae9d1b15b348e0cc1f255a05eb20c090f50ae5bb7ded1218b5fbf080e333fc254c6b43831f590e8ff1d2850eae2209c7fb5a1df739c1a1d4cd7877d2668137cb400fd3e5fba77b8da6fd0203a1deff235160948ce9546911a73dce1f5f97c072ff87bb5827e7a760b154af0e345e0e28a3e9170dd7a9dd1b5fbadc66ab03b96d827a75b1f2423bbca37a26049d4a8d9817259749db6d7af5a3a0983fd6b44916e80fb8c90bc4988cab2ce900e09ff3e9016ee6600daaaa8d1b6c3670473da3edd3d4db2b69aea74a64b7fd06f7ea5debb04a291388f4c013e9d9e438019245dddcee0e64c7504bae06b8030fa3cef6bdf790246f010277badf1d159efd3b4f549ddf037ec35631d79be46d022b9c31651e4b58107b42a8d163d4ff1624db23765e50d8a8cb21b5ca6ff4e3067f71db3a07f0d975f0253dd256c9104a30d3bef2ff14c868eacaba44f0789309779286dc17d06e1cfa6f06aa39ed88a3666b0639fe42d4b8032260522bafeb00862cfe90ddef6530c164e53755d9c7ff0682853c2d4db1eb36444b19e6783d07386f574465c5db9c66677a48dae60bd7211cc7a1e065fcf4fb1ffaa85ba534006172383aab64b4b0566dbfb58a5d1e514ebece9918bc37d41c62733699ff45053ebebddcd15f75a0deb725a86ba512c38dc818cabda436f4e510a5efac7a360fedbe1d40a3eb625cac82baef3df4ed94d4aec0d56334a0129278d99501b38e08ee319857d7fdbb2cbbba67813371b25c0599f28b7aa629f816c949f6fddf7708c4162ce4ea3428221ee5fe3a6a90932bf854f995c17aaff3132fc10e57d20d0f460ef605d5466b6c25eab3d8c9954033135d0e08ed843e6efc4cf57c9555bc0800bd1aab3528670d624120a28904eb05d5238a27c324061fb30977c2a99e0305d21345a4de739d7ebb0121c7e1f7483676a6815dcf09ef4376460d4d0adba802bb0d01c7d7243fc91efff1586992bec2fce4fb2a3cafa7fb50d9e895c2bba902afa935aae940f2ce3449103d4968e91a08f7a04ea3deacbfa59809fc478b3e020173a03ef44593a7f295fbca356405aafc08dd62878a92a0e29ac917981c0f01d565cd06ba00b5af0cdcb8bb44e2a3bc7b04a4095f563e456fb512abab8bf50d4e63d5e6d750370487779095ae550b8b51b01c0fde9d08502bc6c01b77a59902f4247dc7b5a5f5b6838b26453a02ac3d9c016fcea38f06ae792b669344378f0b473c7b0c6d5270d1b089a4626d6118edd9ee9e0c3755feb4f4ba10445e77100f746329cb5a27512a2665bd5bd2f4f30b223e28d93f5e3aa9dcdd3d659c8c8d00c3a002a8472d908d70c9216ff0ba0cd7f9a8e224c90505f6b9fe98c9a07f040feff2fc78ba03796b290dfc28cb3546f9b8c9ac167662404ad7c378d10c6c62044d728a46d4596e49b91fca62977f0337809557c72277d752497d525784d71b00f0d85b576f07150d7144eb50ea879d3dc99c72e3a6fb41de5988d8527d7fda0b6969917f7634972627773fed36bcb1ced4997041f25d644dd142f0814aa4220b4d99a7f49f1829a91429596549d3f2a77a908e5cc3bf16872f6251024cba510d320a9c49630cb7c16e0f80942b5cb50fee85498e2572c6ca7e0ba58f1757ca05fe2f33be9f0a80e7e1b662598f49320dae6058158f08f25c96d31316ef633f09c0389243fcaaecf1b86356ce9c9c68c9744a6ee5c4c124da0efc1d9fdf485a0ef106941a4027f84b432985266c4aa6b57239fe1efae925bbc260fbc8b627d70186055d70802e715ef0081105e64aaa5f6075e8c96a02176200bf3c3f033d5f003d630335799037524acf73bcf28ca73e6ba63a510b4680fa09ac750f4e85020afabe7be30ee333c8b6e418e12454a7b875689d9e71cf7efe4f221b8e725fdc0b2ef23a8be1c7de885aecdd8f4068599491c30ab2e10af7a6ee668dad388ec705d0cb1abcc111959d20f5632038dd2995d511e48dde6de5f2288926434a0cdb090e84b8def8befb9ba0b643793a811fe5f1c239eef89a6a22bad10371b15fb00ac47120fcdcea88cd8c6aeb7d6a7e699c4ed37687a829b5c9f6e9c8aaaea97c01a34d5ca32f31ad97c5e5aa876d0d07109db2a4bae5a43ec2abff129208df8f01806c6524200f30508f9cef7f22cba4b35bac2bea2e891e9cbf4a19b0f8f58b00d900083696d655dc70b532511b43c6413448442fe87e0dd70a4cabfd4076260bbdc071823124d4afdfc95ba2cf936be1ee71e218bc654488f9cd8d560bc66004bfbbb7f3aa9389b3316e8c0827308803861afd215fb8813b918047b061859b08fa742a078a1d34dd4f264612f0f6622a4ad62f6b152d03557887035f36043b0154793d4624778dcfd77e0299fb25a32ea8de50bbb197ec033dfa7bbaf0be46094b3633e575b05fcaf092aca7f83086ffd581992fbf3d7d0baaf4f05f436e6d03d0c6e454944139451b37ced3782a468dc0d38b04da3b788cc51d2428922cc407abb1454a7a160aa6fea5c9b354577aeb295486abe64b4e7305ce6e53f2ea0e04d5d09147a4a7ccbff0632695fa8f8b02e2b06e2d5f31729bfcdb3fc582f71007fda1a0415c1a7acbe19eb246e7b1baf73b2428e562249182a11022b8ac948b0e18854a0cf5ccabf3a04838aed9d09e04bb2c9e599ac7987fd9f71e4477c8010f0370cc043eb81bd218b9b31c51bfd38e953cafd92e683adf9af14b1df9df0d0736e595a36b386561cf9ddc1c09edac4656f02cf9fcbb8968af9a16319130cd0744891295b6fb0637d42a528920327e5e994a22f9c94b448f10a8933f1026ff09ea8ec28fd32ec253888077f5272af43344902e4164c67e3cde6ad0ee4741c701d59a9c90a966d930093e45f86135127fdf30ec5c3b809ce4fae21eefd87c27060bb9b1656dd653dca0f98a390399e42609b7c81dd01f703ff001081cc7511c0ace90c99881ca6b19e53eb23b13dfdb38cedac01136f8e234a35ec80e6671810232fef4dbd45c6669134dd826fd1a321f636d7c9a75147136d010ee7da8f414097982632cdd4737ef6f0ff702eae2139eef4061b26a3380a9196917f24639c508c287c25b897faedbd1bc860321e8fa4e3feae9d7d3d495a4100c972fc5573006e4692b5e92b2102449e86d8f105d7ad508050f1b1c81f205aa63263f6920cd078273eabc815678a34c7d6a02a6eaa5c734256640f4b188226858f458251c1903b2f2cdbc4625ca636fbbb2f8041d87e1254d4e383e56db05a695f28749d059056c1f606fbb9fd2f0506e898767ed6065b30090e2eaf5a4bc8f78af20951daf0ea29eb691d708decaefa444ddc822a506f3957a0ef755cab14c8bc0c1c75f980e415dbdba1fc8f89a344856fdcba3ac167f10a0bbf019f2f0b13b85dfbe90ba0de4d4fc74b1bb8e2976296cd764de2cc326c2b3c18af480550325caf46b333a02d7c2f39b0bc6984e264fd94c78b1bfb0c9cff237b52cb7d6af5e91222446d509b2aba10197c28a0bc2cd85ee1479e720225c9cfa5b78b97a0bf8ba2e0287d3015754992b5d33ff76e89aa3b870a06abdd2f2580285a5baa5baff5dfaa839cd09f2834a4bcf85332942bb7f9fdd4e4083d32cdcccd8b7acc0b90b58d54d8fe70e89b54b9ee2eb0a0d111f0a9ed8ebb265b347bb2b36068ab04a4c3e22d2c06708ef440b6f78e954a9f502c7ee33193dec92fd3c50c432e2d29c51325be77a15084b44934e22088f688827df2da995bbc572413c95ebccf715725c94d0da34f10bf6e6958e2c20c28e4b77efbbe97d36f60226f636a7ac9c606ecaa3a449ea47096f3e4a9e5cc45f20c148170585692f8c668464bf4bad530bf667d2f2d64c9506791ee1261d3a9206b5203c8470716c99e53aff6744ecb7f3403b91d25da38804a60754de15d3e1cb1f9aa0e4efd690f5f21e6a84b096e69904e4e0d009f17d0dbd2ba369fd831d0874f591d3c2a57b683f4b05175a4d62889c831a24141876033dcd5bbd0edd1ef8610930ba1ac5366b08ebf57baf5e701e9b60e010addded0cb58697abc695cb13ddc6db926047083540e329a560c7381d405c91c240e980025ec99e5c9741d539aeb637b02717407565ef6693c21d1d9915f6b3659f4fce0ff3579bb588d00ff6e2b96481d22e9df9db6f34a27b0aa16c41de35a6e42c870c0ab13815f6da74e135f68a2ef0158c0016d711109ff1c4063aa1be620b950b04f2048be11bf065762b0ed0ab81b1827cca136a33baf0fec99626566a2a4fd90dcee108c5c18380ded45baaeebad0fdd4ff2423ca4d1c81c89bc74e338a258f069420562e365bf831d44d667e769fb7e75caff233bc90a2cf09a0bd794bb2d80fea8d874ebdba2477733df406c5b60c9f2346867ddf7fa50fc2b586756bfb770620caed86529ef267ece0945989ee4d8da230e7956a5f284eb5ed1f4ad29fd108421a22475d35af3ce12bac5256ad9db4dd8ac6a4bb0563352dbae8b594811a064f9ae393100e7a3ebf4e2624744e59e7e25a545ab714fd419c6c43726c838606da4e53b4e46cc4cd89b173d672b9c4a26cc9386ab2cdbe6076e115cd492f1b0d5abe2cd31b4416d59ecb7ff4db31cc95333a5eb7993c4780e2e6fe0e7dee070397da090560791a0d3523dbf59937aec192ca0706b614afb6834a08b75ec1f20ae91379ca16be5ebbcffd69223426477a7c38b471eaa4a028f1c351b87a8a050280180f18694b036f258beeea116f959e1444b65ca26df89b5d361c60e312200f893dafc08d0e40a5f49c7e18037d9f6ee9fabde765c0b834c48ced8ef211d30610cef0e4a31f5fe8de4c22dd1602f2c5efb6431b8381816256ba92886e1c1b07e5ad9d2054f38272aa8f0924b997f5ded4d64ce742011f95754bb08d96ad6f0f60e3965dc1b7380b7ff95e74b1a848f9fbd0ffa5042ea3a29ac1e430a6d91007a7e1280eedb2b0ad15992000afbb841dec4ffb04eda5b50cc01e2cb46d954f018755dea76710517231741d17eb574bbf7f72c2b9afdff78bcbbb06c08b764a0c1ea08a5575960b9d8ce0c0736933e77f53eb4f51d0b199add699df85cc79dd0987c1174318503d474acb04d4e9e4636f5df71468eff9bdb5c5f6d193a56ce50ff0c46d974ad2d52a90372a3345d5ceb4a0f0b6da281384bfc044518a9c6e0d0a1ca34da091080701ffe28efd40ccdc60010f06ccf5840624220c3710779a68053d963b7a4cc43d5cb1bd17daf6b027411401cd7f5db448e0cdb4cdca1b1cf60f03cb788394ddfa9048bbd5c8b43af3fedfebccf5b6286cecab7c30ee87864c07bf5f719980909ab7a4ebe4c3dffc077bca55ec7cd68585c4dd167afe7a565e057c9de20208cf4612beccec3bb7e0b8cb342b49629ed8176e633fa94845fb6808dd74d71c180d13f5106307e5b45302dad1201d35791e609145fe889c734f9d018c80b02ee1aa51e94deb00c685b832186eace7c4470cc251e4cbad7b2657370ff6e5e826eb6633866ba99b0bbf73728716437bc8253ff1405157291d14e14b0dadbd0de8f4e684b0a27996d087f7c43a74bfd41fcdc471b4ac4f3c209b79b8091b079a4c811e1f4f92d12409029e6a5372720b0bc1b1104abc03c9d69c27790079df58bca623501a81f0e922b3f2398a6f125cfabddcbd2bbacff1713982c6052f5db5fabfbe93e4c84ee8011cd9e1e8b1c706adf528641f5f5e5a63726a3807b8cd7cae27d2af8c8938f761402ca7c9aab6759fa7e2994ddd9566c007cc4d00755a23b3c4651593bca711bb093a5ccc0a334d532853c70b471635eba7fadb0290eb0739ca5ad0f6cbbb9b18bdf74fe90b3cd46d4d9d1fd6dac431ee3852d00b962db968afe36acbb16bce1d213e9edd5f8397f45d7d849893f987d29499fa0410b4b0b90c07bcd8474488ef2d2868fbf65b84fb0e34c54926e5c051c520650971b95f1bf3eac527fcb7cadb394d706410cc7c74809059a30497721c7ba02a08a59d23fe7158eed4daf924308e2c0a46f18dc5ad9816b7b5c67aab93412d8c0892707c5715dd164699e67a71536be8194efc2eb6d28541739d0fdad78ebaeb046d03d5b5398632974924b32c862e761c212937462c6db2155cd3a8a0a5c3840c168dac2d54418a7658ad6b9ddae82031cd37f6cbfd15b8b746300f857257970b7caafd919a4e7cb9b09490b1f5547b2f9e183567b66590c61bd2d5dc398cd60c3e0d315f699586498a49ef2ac7a8ebc6537ddf6da4b59b217283d2640146d2087cbc4af09f908411b30ddd6e52890d4aaa18fb71d31accae682ca794aa0b3e0229a591fc056e1b865bdbe37e1576c3d9a72878594b2eebdc213a6e15564eae0d303c71f452ea36fb3d8106e2b4fe8b7decbc5e21581db5b43789f82c0a370a0712553a85cd52df9e4cb033ab6359b28d7f5974ffd488f77da3029249076c280cc12a425eccaec640c3dceb646800761be69d4fe1eaed6aaa8a98700895694f04c09f5bd4b9f423905f3e4a1d97d980b4086c5c85ec4613f39ab12871acc57e033ae0b2d71d7ddbab1331283b61b2f7a71d576b4575278dec6c5a86a6be24740e4ecdd6dab55b7c1f0bc46615b6a1df598dec6e7b865fbc9e545890f7f4c34e0b166ebb8a34723efe36f08ac8694139e0a6c653819d7491e22010f169d21395000a633b05bfcdb4a34f3a2ca0b6adb1d9d9a347dd40a0edf0baeac7ffa5082a078eb4b77d8403f4f7d1a8595b3e068932c678702a1837e1552edab1c94a8fb70960644b4fa8543afe32dc6f99cb5083b5d448a914bbed12a8e3a8bec2d3bccf0249666ef7108ea7f7f67b2fa9706c332b637745a51a9d2b42b78803b61a8f8f0743f0b1c519afb9c072ffb757ee93f1f1f1f9c9da7ad33bc0ece425f923db9a01a0b4c29ade98db7cea365f5714b3070463c9b4312f5019820eee8c662a352d015969c2a0577c4c5e45ae74b13e70894a46c50293afa9b533fef4baec4e48fc057c80cfcf54faf99fac92c290881c1deddf6403416e28d7dd4bd6dfba206c0b07e91fd02dea84aa3d6549fe0a79724d5cc75bc8d3f4b529c37045cb9bad81da06143df43baeb5c540487177d965e0dc1ab349ec46fc55dd4c653f064bf46c470803bbff2fc1df6268eba5af906943718ca7f02acf5d7f73f756b1757d7de78b0b0c1cc5c4bbbffc0b3f7766179b26a53593d752dff04f3c3fabd85363e6b55001d81f436ab4537ada256441077bc554dfb8dea769482fab767269122237ccb601153be9b8eecc4c46a9cd2e448df757ef91504b31de6006c6a89e3b7f0185d908ff1cba03733feab0f8c73a2fc2c1a5e6455432162857cb54658a5ee4d63ce408a4191e7740c76651739a6e061943d9a950b2858fce752db7240a809710007b0aee56935ac1c9279d4581fd4da8c9c9ddc6017fbddf63738010603a37c54c580195b94ef2e9141d1194421a92e47dfba7675ac06a93f82803643b3d74574164096eb8d7d849546e9d0fe648edd8834d1f7c8e5469432dda331c751773f5d9040ef27dc89ec2287fe0c090afb1b50afc1ec949adda4f6b8493675a9c8442157201e8f311b1e01ced6d51118ba5d69fa8216c496e4373450f70b0029f05e343bf04ff168c7e0fed2a5b776ce7f53b7c6b14b7b3ea881ad4a091e35d5ab8d1a1b60059753ecacbfa10615851357f2de9e98d8a342805d9f533136d19c3cef01a3208be56d255fdc73def3cd202744cf57e7b8ccab90a7b064c55c064e9a00b55e80d6824b9d370ea801f3659c32d60e48e5271fa6fe5a04371f3952e2b8d8b48a500425ab9de7c6f12b0093674f6fc5ed4ab00dc121c1ec46b4bcdc2baa748f4d402c5d99f3b4a6385a3e040aa3acc29bd736ce3917dab71b6817acc02276bb3bd0ed2ef4e68ee0944113af4baec79da7c25f6457f642f7371dd6261b9cfce5ff00fc5d7d8e0b4a26cc082a31a8111b74c8759702f73b27c779112f9fb677fd9a00efb5036db0842db9ebf59dd95a6ef96c77ac3fc36a74c502b5a0a44c921bec5033a7c8d2e841fbfb03e044a8e5c62ddcc016a3f8da2019a4b9c66acfebadea004ca1929d4d656f1528e828bfcfdc64be004fefac9a500350cfcf08d3c023ad40fda565c9af7fd95cf5d41d6773b7f7a58c7c28ee9abea6638358ab11eb109150df16f4fedfe86d1ed34643b2a6acc339b60d62fcbf4f2e2ec6b573767cc2c4d0191578d1ff56e357751167b1b6b2cf62a4ac45c34387c5b057f1170e89a74fe0a10df343bbc419f877e6c0d4706612c03e5a8c7d434d08b0d29a374fb31e0c50dbfee561a38fa2b162f63183cec174aa005301f18076496e1abe3c771f36291071b2051ee4752be7c359c098f8072fba215d0a03338debd07db0e4fff3659990259bbfbf825dc5636e4cad8e02ffc0da7820eb6b77b30238b8025f8db7672ea0782dd555b6f6d56f2e4ccc059a33a4b0daef8e0d60fdc9df4316d18d3a3547608b070bf1dea2e2fae9fbbb8f334117a5d50eae33b3aae297f6bc06cb2a85197a90640620b177abc5edf5804e83916869c1052411d938861cd62dc0e63c5c5730dbb78c6d59a42ec1dd8482b94847139f1c0a8a5a2c73bb5344fef8c303e01500852613c0f2cac0e32318f1e91f0f3358b1771c1bb8a756271287fd14dc915ad051b4604473f15bb134ba6820c206761454fd4513ad53ab973c5ab5a0511b39207afe0a07708e71c380141229393b6854f1f93c38d1e25fdf33a4573755a6ccf0e6dc6b2d6dcf6c839642310a1cacbc18224c424813708a7bc585fc4a4c19c790be07402cbb8e2b02f99a77c24d6e9be194205f449d22a0c560f8cc687095eec08f8fc3c1194461fe180d99e555a62eff9b543c95308587029a930e53e26f4170c2503b1befafda346a99c807797f328c0d489be31f5d11bc82a71190c6c1fe70ec6010ad97e1630f57546c00599c95a19a08731f76788f7ff5cecb8b5d0e77c0721f956752514521076e4dbc04495160a6c424ea60f74d37b4af105a8c17f1a05657a8495c365646d6e1baafa062155b2bde02b626a23997b66dae8ecf3fa6e0849aeadd7b863e97a67faaec6944ee7bbfe34d81c519ae693c0b9b203f4e4e9098a82a4e9386b8f1419e1bd5fcc8b82185f84a279accce2b05686b2c5a8662103627def23d4295552eeda301af3094132fe596e4d2f266b222c944853410aa40d8642eb7896e302a883f0c3e4500279bf3ae4ec452973c49ec232fa3e42b8a20d19df4c4fb34929d06f6bb1ab109470ebf3de4a716d73a7f6da37aa4f160b7900702883182b9b11ed84741b4d0982e4dc4fdea973b01a58a2f17180ca1d0cc90fe0704768d31eeb56c284c7aa3c5a77c3b9a1f84cc2da0e3b88cb436bf3e4700bc87a565376b7990d7963025d515a1282d17c2db8899d196db5d9471543404603f0552ecd6fcbb837b34e617c1ded5fc203c7692fd65878655234d6fa6251da00852aa9c9d16a874b9e620344726b9b8b7eccfd3e7c555bdb90859b7a4cb264089e7185a5f986cf79f0edd11d77a97e65ab2e18a579b0481ca07dda7c40f9f5085938d7bcaaba528f27600b1a5f4c706ba0891589488aa7894b99e12ee7f1a7055fcd547b04fe04110b9d2692368c9b578a5c2b3e0c31df157806db82d820e20fcb491cce39c800654e10614f430e5520fe1a86c95c0f759d4048847277863e0ce547c15a462365aacea7f42a075f5506030dc899ed89b93223d6506e2a16b60b13c349008de24e0babb2c4a90810522352e4831b2434d4a6782fd99217961101fa43722bae44c72747d1db878efa735a59b17c44fbb13827420e8a9f43257b0fe0b36001505b4192ff8494b267bb74d631370bb39f430097d94a076bc87f0a04d9a2a338005faa2a1f8eee84943532bb4e04f0fb0e2d172b92b0f921fa8cdb09e3f65f43016e7dc422fa1dca4d2917f475ca93e67df04dd84099d16fff579d0dacf56e44712822170872680c83c960f3eb6f727265fef9971dbc25cef15e580a43f24dc3ad145d70a9c1b01ec666deeb302e5fb0607d84f6720bada47578f00adcf2166408e8b32d3cb1c2af8afe624c222d5b498c978912413d58375188f30b59331f3836d7fdee69a9b720753f38e96bf16516022f8b72ba46d25c73aa6b0794215784eab53e6c1435e43906cd3a1b20fdd96023ed9f4ea0c40b5a9161100f3410244989e8bb09490a29f3b075b738737f755014e04c71c6cf846f89faef080b5c461295a7bd25023809c33518ed7ac6048b3d78d86b763018b203ff720d0d1cbe5cc0ff451b12d81fd79ed1af3150498abc8a8272fed14f1038055c1ed0066eb59a6da6b35a68828f21549f054c3f0556cf8a39eb40295633c4e26576d008a7e120b11bb61ddf3237181a725c3fdc0814fb2af313800b9ecae5dca8b5980b38e4d5326e340e296f7b8d82d89cbaa84090c59342cf4868d91b42b96d5968036c5fcd9635af4e8a3e0ed75b8ce69479575396d22ca9d71ceae58ecb72f4440021565618db40b5a81487d43a1c057a243f7b6ce558cf74435d84eb229ba23d0206857b6cb77e172aa3d7f1a95a5513c97f7e6a8e4e9864649d8d9e8c3e2f45031ea1ad0f1ae1f54d6cf3e19df65eb4c67d9070c9068fde1e948cbbf63fbb2a0f4696213ccb9f13d1cd34dbcfb3c4e0432bd27448add55487bb504c7cf1c1b80b37cdf9857e8305c1f42740a8ad7cbcaac4fbdaabfa50998608a95cf9bd2e4702de7c1d09118722abdfe5cca3b271cedb2114eeb27a02b56f36fa2dc0bf7de200fcda95b42611e718219b7572d2b525d910e051477e0f6ea27e09f3689c30e504a10379ec0e12d9c9c4b783432e5e7e9d01abb28df01880a1519018fa542d8306d508f75ee8eda6632b61c41bb918866ebdb2c3771e0f0e5aa59c8c825e24d60958b5ad1aba2935e0286e84be0c8d95951d69d26a95dba33d972ae4cc57a37704f50540821058a2daba351b0c4be61a5764a868159d58861eebc628448c415a0bedb49cbc74190abbad5c7ba59f496384b8d9fb3b47fa32cb0aac3acfbb4ae4046a88144e7d251e30a0ac04649873750ce363d90221a19b6c185aba8d2689520e236cde3606c24eba279284b2d19426ca80ca6cf0f0d15f37439124c1068a540dbe13a6ecea1dceb6b4a31d6182d01f23d88f326ec6b54ac190610c8604490f074935f6b89c0a3704a32f80fb7a37f27469c3151949962ceedef42a2e210e6e0151957f21a7069128a56c966c4147b5ad4d05df021e41916fa5eae51a135aef0b390439bbf88c0c83419fbbad3e33d34cae5e7f419b988d4faf5c84529aa1d40354c7c73c6d3e31568ce43ee51ff6c9b416998c0a7f505ba4d6c68c952b118c06f56d1738b3f080f4f08cd184964d30c7e1965eaf3d2a839767009b56a5413109d684dc143f71b08a2c0282eed637334b0d2d04a41e5714e6662c71e8c3eca40cb3dcbe85dd3d638320ea865fe1a77a139fdb50bd8fec53ee36dfb7d1c78cc1033c4b9ffd335eb14ce7f1c8a3c1f6236ff1a3609ccde4b91e800b32039eefdc09cee217c13e94adf83fdbdbc52addc1c872b0a6a82d1cacd204e2f0c6e3f8ff05bce0b5f193ca28e7227805090ddfdd8bb480fbfad4285c3293696d1a9884b500 false -check_ring_signature d99b28a019e26c4fea8cbe5816996a1cdbc1530791db96d5f13e96e50830e6a5 cac2ecfe87f7c6fcc289dcce538af6d74c17f2622844ccec37b0a1a62564f6e2 24 b24e7293466dd993090c964c5a527b1bc515943f5c3159d8a69099b731d77c57 d66e326f8921dbcddef4819dcccd7ef56ccbcf93ab870365f2ef2154bd9e1829 127e4c1a0408c2c2a170c195df5185c9439c23c973e0e0dc7fab2733f820eab3 3ad74d594ad0844f2cb4cd53d997f858090e61a79775c10785db64faba5ba499 91b28c834b198c915cbcfb82ccb0edc868b88bffd07b6b7fb3977246b6f51a60 70c560a2c4125a485e7198e3b02876d4123cad9c3786cb17a763eec5cd393a5a 587482447516730101a81c415e5bd191d8db1c10c86795b6c311a2b5b05f25d0 82b4f2af0c9f2640cbfb36263da446dfc522601e1b8b6284cf49057af295f999 bae98b5902f4a08786788d741873992d463f6cb4ab8b67ad054af7211dc5882d 0ab7b87aff8da6869a591eb83ff2a9920e43a82db8a5f08156019314fc9f45f4 cc76c939b802a521970fc8cec46e800b4e55a594911178411fb07d644f4465f0 6ae7785c4b57f2cdf68f153da0c02e25423e5bb4484ca3f3f44d6bf22671276d b81b90a5ca21f0da5315c063ada32bd256b2c6c2f2258e697b69c128f6c58cc1 dec0106f65df18cb37004f6dcc65bf5646280befa6e022f1e73008efa4606574 148e096c434165570b839a256bd8e2cd0984c48238b4693dfed73bf2c34f9b69 bd7511213a8f79bee74c6c3fc5ed8d2f891bbc945b1408fec9b5c4a7bfb25475 afac1eaabd1f356275f7cc35cce27ddc6f568a794a8c465367d264db2b02e6d1 dee7a747ff67be9a0ff2edfc9c1629d6ee136f2c6c1d109c0f6434b386526895 39bf7a8866685a7438912b0f2fc6979794e0dd4d8236c2f0fa9bbbb285151d2b 09f44a423fa54787f3da5e898f07611410c15b170dd80d19968ffcc5cdeaac0e 11359b7b1b5f7176ff1086aad363af727dbb72f742884d659bcd25c8ca95f442 d94aaa70f0b3c263966c4a6cf7e0e8b0b045b852633fe6a1f74cadc6bf54acbc 79da878336f0743d0f4ee4be53a7b028bfc94761e55632f0152e0245542bdbe6 eca4ff2f3c2d02319960e6734e9fa0925af03eec383f8ecf8f4a2b01d3bff2eb 76e8b996fea524a0e2e43bb34969aa700e7ed7ff94ddf38792e9da57a264470e11c2dcd0c5c58aa53bcbe0cdb4de53e04addc34b176268047db839897a16ff06b0a6e7b0334c9f4eba1d80a5f1a4e81f29dbc677b7510eeff9cc45785dcd820d088b8400bf8ef2c32c4c170f433d83aaec18c202846fd6de3abbf2b561d1020402aceebb9319431bacd5fd02ceb2a792aef1f9f279a809f0e20e43e2fe2e090053d525ec193f824f19bded236fc26cea87b3370b1563d7161083af85227a010b460cfa591148ca43d4b2c872ca83a34afc60c30b0bdd325fc94de30b68012f050faf4ef86e45813c97b950803c675bdc94e89f6c1c32960ed6f77c6dd6ee9d0e2969ea27b039ebd17a8381d1247a1b95b0cbfbd080a83eaa740042b05515d40a657803a21d165b198de8772bf5ec5002c8707a74de597fcef36d76bc95866c0f09016ce3db9838ac45b821a6c653e079f445afcf9284af900eab9d821a74700f8b7dca1a5f3ba47b8e598cf1cff481a56f2ac567a81ca42f91dfee2959d4bd018becff5f9b119cfe50fbb26dc06a088403d995349a01da26774a6d93d932d3074da9ed6b5df95b6d58790c22bada1b81a55353ae8f796a4d453ec95ec4e9b70371c0cdbe6ccc9baedfccdef57da3577a2b54b62e81ca99b6c9800d774edb19053cee4e6febb49945e6430d52a0f42c30a4220b59305fc845c01b9b8b6ea6470a4d7e6ec7f33a5feaa089c90c1a0a169cf3f8df991651cc8e137039de564afd05e5064d9d7044f02495dfca35603ed882cf150c4c9b6ef9200a21d6577ea0df060decf6484fb84bcc39ef3b85f9fda8e32d0870920c7b2cea16d236627e273106808208dae5fcfde26807b629cd4357d9229237d847463edf5050f92d60d4420f75e03d009926b5612cac3348a73d121ccbfc1e304f30f3fd1fe63887c0b79e043543ce87f5ead1c913c093c7f96ef9a210069267543fa34e556a55c389c1c807844474e832fe6fcca44819c58d63ce68dabc0ff9b6314585bbb31ce94b03400a0ff7ef1117c55ca5104d4037444529f5861deedb2528f757af01436980a6fe0694b048e7e957e4ab8fe74fc4db46fb950de383790d7d86a2f4762ad098a8ab0824e13063a2cf81549b4e2b23a756de534cfce4e2d851863b0beee45346f69802a42b5bf322d4660484a121124e101518be07098df7676f28bbb2d4daed0e2c031392998f2f47f44ee0928abebbe2f64b24e595489708dbdddc0c763b3546c4007d053123095dab4b7e4892e3a5fd48fe2b33a401e6e18e72277d12e74dcb8401fb58085b8b69f07718fdbd401f9e2358d237e816c8c767b6f52bc1926fbdd5080776a9073e200106ede8c50fed5ae94f5fc74230e84c82fb81936fa645f5f407cf56edc88370a6a278d30b34dcf5c9ec04cb448a8c21e4340717418df70be90c2c70968731d27a5acffbf105dba5e5b6a6db20397619d144db9311d6be35290cc21c4a7ac865c409956edeb2649d7b5c06ff43b84ec19cbf64b07274cf95aa0b21d9b1d6dd0cec1f4f97169262847f247b5a4e1e05226a9455de48b6c192c70682d3d8d28d85a94fbadb91b6dc4871a07dff87298703400b624cbc1d78a68c05d875ae9130b2032357d45ab27f6423f4a3e8ff3f5557a303ab98007276fe0109cc6a58b545629520bd66fa43daa19cbbac1bf8530069fed3c7ec3324235b9b02b194a68549282a6d0dcd7a4fe1b097a915e7f7c75a830abf95291c3dd573a50faffdb3c8472c8f6160d7db0968a525c3c682f111dccbd2f96370c2b2056b3307766c2d7ce772ddb377acca3b456a0c9080238a24b3d88a3419b5f6ace9d3b2043356c7984549822c2218c5c922961213ecfddbc80b070d482fcf38bb4f3d6c0c65081b9fc63f625391e37a748e312dd84667ad7c5fde304162a2c2ae2ea46c014ca8ae5871272ed362dd3960ff69b240bca6413ca54b2bb095e2c78f0683400addd4d184a0931afc6e1addb2634e0439ad79b82b42375f0017511314a73eba05edf67ec3bc7b3b8b24ce37d5e16633b390c039e1129ca72938bb4465209ac1072c5d42ab8fa41da6654f5aa93cb909b03aa9d3a0b3d55a8573bda86b5932360d4a7a68918847691facd72d0781bfa07fcd152230e09d1d4a9314d44e02256505 false -check_ring_signature a1984ef87b407201bff12ad8fd34d6411bdb556f02d4e3f453a8bcb54d5f07a8 b89922253f3a94e5a37305c3450ff7ed94ad9ce33483f33fd5ffa41255fb0146 2 36da1167286f1998fa15706c5d9da5a29df75448e36ee87e259d828c01901bfd a66b4a43875c8dde2f2e9b4dfa68f605657f2061e58df21760e65f32412a5254 4a724665db8a09ea5720cab1c798006f1c9f30312b3b002592942662c375a209f12b2275981c4dc370088949af3ef94a66521a11b2502743e23092800a44570a6e84a388aeed513c03929fad18df416f9ae80e8ad88a3576022a768566ce5f070dfc379aa8f5018767c8247ab341d53808afcf9a9510b19123c5b71bd71e4f0d true -check_ring_signature 86b5e6d9837438c1dc58094bb91443e169e5110681d69a0d895f78ef23d56edf 186835a194d80c80a7d4d66b680a56b2194c46c9668d1791b20ef7fda8366224 10 b2e495a39b17035cdab120ba14ce5bf37039d5348f812863cc9cd60ef6df5ce3 f9d21a8ef5c035f1f2947e48323f94ae9a07161e2474192dd20aeb0088380115 bf34d23728580a631ba89b9e3e09121ad0533413db878a9d80d27330c6e0dfd7 5de721dd574e065c6b6656d0c53e60335d8e2c5f0fb7216ebfdc63fde7412a6e a809d77d133995d35294d281194b74061d19708570c0c71915bbf464e8fc0a66 2119a3a0ca7bd9507911282a4238fb653c5705170f420a179b86772205bf4982 23f11c4eb1cf292ea4e3990af2f3c3d3cd9f954fd9016feb0370d55ff814620c 0d4ca9e3507bf3af7849a0cb980cb00b4004bcc1923054f9751d0b724cc86846 37b1976f3baeec64c62c64c7560d739d2fdfec6335cc456c0c3b64650ee94cec 39c49b4b7e477e31988f3f5cd942ec2c1b25f4ca891eee35456aa6b6e2e5d5d0 b5f6a0dfbbcbc40f9910e2b9132790aad382d0d84dc37a3b90a4aeee5cdc200bcac1e194bb1363c7552b30c0afeddbb7c6999348e4e460cf36089bf8109d8603c8806b458e06ed1013c4dcdf1f633c762d06038ac6e3f4c27d166241e653ba04926ae3ec69a4ca0a8dc34ab9cf9e4554633360e22f69b2f578b17a9f323f5805d93b82de66d6597ca30e2707cfae615375951dfaf63419c3a45b4ddedc2cfc0235b42daf547625ac9199389e617037853afcd0fef5879bb5fc15d715440b4d053f2d754690b062b093882964f85a2f97aab2474700616d994cca6f3e9cd6cd075f2f2d1c9361f10eb8329d4d7dae72efec42695fab60c39a1cd8682094ac3309e4fc1b2d67e85b018f67c39a89beec485c624b160c6540a52be9c7d2ef87180cbd7beb09cfe213a796c7103c2677f33678a540fd7166e8254a9e751f7f46260a5edd7562717bc964d93fa5e6329aaacbf1e150cb6ed7668264a145d209cc100ce38e88ebcb6f5fa0ad30b3042609fdad702392e56fd964da89196b0de7299e01e2edcd2c71e4c5b861896faec1eacfce03eccecef7a3ff5d69e8bffe37b6a20e171a11db9d40e70b0fd718eab444f72f6fb800b1324428b3d7870c083e195703980d2a81988699496470cafb896596f64ed6b959c733d9437426f0287ade3d087e5c2d3eab069e0f98b5d47d8652304503b2462cce0a319e4da5800f121969090cbaf2beb4c0adab9a5efc2ea0d4baa826204c61cb7579bb0f57df272cffe70deef23adb239cad66ffb0c1dcfe35f3da685e991a9c1fdf3d7d4649bb5072e5029a6b44d3bdfa5ed033f84874117ecb0fb8fa22f0d95afd2a1de7aeb359dc23044059700e7e90db9fdc728f014edca9c9d3c50ec49f92b7271cea063925ebbc05 false -check_ring_signature bbf328773f78d38afe35716f1a9d0392867d76336b3f0218a5d2e301243daff4 45cac4bf38eafc267338d9d3f77403e1021bc172570a77a468ca24334519a5cc 12 203047f08984333521933bf9d830920069967a60d0efe62f4478c6d0744783b2 6e92352a6f6561ac7661dd649d761ccc43393dcc8c3c1b09ee95800cb6cacfc7 610e39d65aaab7dcdc16c3b1bc5b47d10431e5f46a46a3626a12055df5bc2d01 87bf6b9fad05d19351d3626f38dffffebbef49684c807751d0aa192241dcd558 ab798e40aa7899834f9a212380d88eeb751a81931a59c1deaed34c2e82f418a8 ddbce0387af9a0b22b5cddbb91e6ba6a752cb57c1e7373178f3b695a48cb6be6 dcdd05d17aa2ecc2dd281ac1a9d0b940cfed02252445dc1604b920896cc67c63 4beb945b0d429faf2cece6d3b21740280141a4296f1c380743ea4fbad46da525 14ce64846e2e70a46be4d3633dc524a415afff2e5130fc282c32e0f080f871e2 42d8eb6c3ed9c4c383bcaf16588ef2820ed07778e4e8be2654652aa12509d076 1cc8c8590e13519fe0d11e5258a04b9ddfdb709c2384b6d9642aa74dc9541669 ca7c9ada257d03d65c7c77ff3a98374e5808c7340a01e4bd57281a6ee048b582 36cd047b9edc5b4188a3ea0fd6469e2dd797f22c2c062d10a9ed10d720e3c806faa91d182bb582f703938e7264162f39bc696b622d32a0b11356656e1823d998e61897266b276624312d1193f124c9e8d5c07da7a8700d54265d17f1855f2d046d0549931bcb300bceb4577bde8ef167d86fc0d5ccb843af3e9369c677f1d40bc3ce04867f9317184202b30f986f1694da94e2384f5d50f2e3a4b5c7c8cd9b087d6678f05de07e0e368a13f83442630bcf14c08f30784c4cee9f9bdc4f1c920c64278d792141e8816dcaf483797da23ad924a1be5ccd6fb81199384d3bc31a039ff542cb9f15fc1e683a970f15b1a684c215060567352fbec3c9aae15839aa0fce7eaf2d56b470f51a7c8869b41af16920b51216de444b3afec7ccd91253cf0db76cfc671528a874e54f54e0bd3ec48ff043df9871159ea7f33fe6f874c4ae0e0c5310936f12725ac83577ed6afe48807ed37be902f3a4e218295fb6b5c26c07b13f1dc3a78345e4d3bdbb11ffb055a29d28e5dbd9fb86148764be28a988e7030d2ed956b41b79ff6e6f52bd343d608ecdd820ceafeebc8a52b8b5076b61600c383e96701a806f50fc936c52777ff9d248770466f0e6e56745adfe7733bab60b5fa798f8b09efc53e003535dd12ef5f6f3ab091c86db4094036aa28d207b8700fd82931d7fd2b26e0615bf5514337b5cb0cade43bc1719b7dfe14f3f8327870226f9ea9f28b531b81056d38806fdf172c69677d06b0177bc9aac59251029570b575563c686c5386a12602f862591fd765ece8e9329364fd084246a183164df0e1dea963d8a4220040dcbb022de4a6e03192d02b8521bdf1e95553f6acc5cf60a63f4f2d1a8a5404dbd8aff397c77e1425f879803274cfba5c414f10cb4fc6f0bf4bd3ebb560bd4c9255b41da0b11640944c1fa87f22b4734d02ceae590ec970188d1f8d44109691b564c74d8ace3ed330a9594a9db572b0c105a5183a5bfd10d40afb61451a461bec03e0152cad503f907c2cfd41c35bbfbcb32a069a0eeeb092ba094ac7b16c9500d5a548c5131f2e0b9ff1856cb846b99a49b1919ddacb607 false -check_ring_signature ecbc4bfd954a0035d0ff12bc4d0001c7cde112fe218acd5a5ec88986bb3707c7 969b28252666e17c623ccc4c25a92926ac54d4ca83c0860650bdd77ac77686d8 88 87e3c3adb8b3375a0bb2e8f8606ae6af035c302ffcd931d8ac632fdfc9dead21 aa2a0fc9c035053f72d4dd5949cafc7ccb644a4d57dabc6d6f1718c2e74c2608 820ab16f187de021b3fa2a3c3c7046ba669c18b9d9d3a08ebc0fef3c86d5b683 dff3b582f806f51577051dcb23648da8a690669e03ba07739827fbed92789f2e b1881266aef38fc31e48518067518e6226bba2921182ed82ca1a5bf13978af74 10009c0f4d431c97a9888b6d12e7174ae4546b5e16f8a727ed7801fbdec40248 827c9edf4e317cd3e41a2fd306b77f2de6755679711538ef3c0628f225f91c1a e15bc82d148f436a618e91697c94e253253731afb9e1a938a10ade31be4ea50b a6824beb2f9c70eccd06f730a0c76b681f173ccc16b9342e4097d3cae58425d8 53ff0429df05db495a88d1b5e37ae26c4a6fca28864960c9e52442711f8d1bd8 cf6bc4dbba483dea1e3c2812981e0c8fc17181fe76782314e120ecfeb1bc81b6 cc1a48e8e22da0c0d8dc5f1c2ead8c977fceb952742384b7e9382876469d8800 64ce4fe4b46ae1e38f00af93612f7743a7804bfbfd700c9a107507f4c5cd2989 83237aeb4c97fc4b05cd7b31899d753689281d0887c94e8010c497058f62d4a7 afb817ce2525afd1ebd774e03b8281784c446fb657bd2f745e2debfd1ebe88c2 a99d786052a94bdccfebfc5c67863a228110551c91d507698cd9166b1ef92642 fcad3a54cf685e4b9bc9f4882fae1257e14e12a09738a4e8b9f1eeea6f744589 37f7a73f07e41bdba8ac422a2d10348c6bcdfa79af81e437eca168a00632c51f 1b7d3ed887c7fb6c302fe8e1e094c63b76e32c107fe9cc16cb8238dca689570f 7e8ae40f8625fdc9f99e12e8e501612f84916a1e006aa01ec4abf8eaf43585de 94478af7a64d6a8cc31c35394959ee4bf05e8ba661956a688d33ca81979df25a 8b6b6fbace6258f51c3882b40fdb518f69cf289ed4bfe8b1d19f877dd5eb8851 f23bd4aedf3ae53f73e9523424efc59e12ccdf6971d8020b14625a3e401f37df d510074454ecde3e396b9be69055094699bbaad1d75becdb762b82368b4b3c06 26ed01698bf4dcccf8c4faf797a9f9acbae9a5377d439bf2250e0d6b668ed888 54311509b513ea111d808fe0d97fb215be4217ac680aad3d552d61ec66cf61a1 c9a350a83f39804d25643ab9909c792025db8c7ec09001a30039a37353b32615 7af97097ac4a9a6cb852d59e55999a67ad0a4339da4cc9caced75470219a5a21 475a9a9024738cfcee39701e7cebc27b278410aaa86c1a5dfb9d067562b9f7c1 4083ea686e0e8759835f6104c08ab909db876e90f2b088fc44712b0a98ea77d9 dbf45ef941a36e089ab83482a1eb18b690cab0373065b7ffd5688dd7614bbe00 1d6c895b503588dbb44d354c6a5368e5cbf7c7ee7300ca5c1baacb25ead2fc58 815b9e187cc089733c44fb863a82b463bd58c4269ec23a0abd94e60a27544c82 7aafb8c9bbcd9b9ebac5601975c2f0002c132a63ec0a67ad28fcd54269c2bb3d 33889c388cbf4269e27608a1b726d40dd54ac2dc9d663ee4cd7c00b8e164258b f9f41ea7645a7754ceb8f38a0810d569f32daf3ccdc599e85617c916a0804c78 cd5b7befc47ff7ec2cc63ba917680c9ba55251da0c59442f2efab919fbd25f7b 201298788a448e008d140d0bf7581343a511139095a4c0e750fe1f14bede6eeb 5eb2171d684a544d8feb4549096f43e97d62759943e69b1f6c4a0c76541386c2 98082375ce9aa7ae49f0d91c01af935c5aef3ddbc042e400b1209fa5cb3fbcc4 657be3e60188d305ec04d0a0c5d79a2704aa1043d721e2cd50566d2fb66eede8 57c48bda7ebf83b8f9a473bd9e3474a534240996268b024213f2896c588863cd 7a5df4c61287a78ce7448bf83e945edff44be3128472ba4623c7046559f364b2 e9cde63742115e1c3b409b54dcfee88d0dda19721c9bc2ebb0daf9fcb9384d73 e5fe5e9d5eeb13ea6e9b0b02a2b99db4dc79ca7e1d59c8554ffe3ec43350c482 6e88fa40e1c7e0d9d7c403266362c6b1507bd5bc87b6222788a4647fafe274a8 b6087e68664dbdd13995edef94cd99f6243b18fe05de48ad77da87d8f054710c c8f7134eefd7ac02019f4bae9fcbac728d3a39dcb0d966d335b39e5bc025ac0c fb9f59ed24dcf1d7d484bb9bf71cb36acda0416e28def0e03aa1666277d324e9 d30cc6611137fa39abb162dd2c29984f56fce916056bebc031529f8970a5f000 4a7108121db86c82e74b6d081788b2f98e0294b9348e9707d40300fa0a994e90 755872e92a8fdd50a4b20928766773847edf0e3d1e7a3d522d7c94cf65d8ee02 935a610e9d80c56facb2d7253cf265a268e20c61d9cf3b38721df98f78dfa50a 7b45b16d15e25b96710b8a48ef9b63939aa54ea945dd3a3eae7f09bafb1e693c 3f8e70fc908973b0ccca561363e441ce585021ce2d24778a8c8ac1e9cef0957e 1985d7edf2f382a637936d83ff24350b283765188b2f994349db7b59d0c09c1f d7a13084753555e464511355d4c6f9397080f40004e61e4ee48f30795262af33 91d8b5eefd860c37859a2f011003cbfba8ff1f6e1fa31937f15794a35571a8c2 f4573c66277a2cb5beb1a20cdddd9212e56b30264d675e8f3e66c48733d317ad 02fffb490c2b7547a04196f1bdb176d30ce2ea5d4c2b4d6c93f0b60ab781911b 54fcd6cfaa2c2682cb76c0d776bee971eda01989aa72e1478de58952a9a40fdb 3f796c91c3460245ef373d198635dea353656d9d13c0b0e447632c8cba2907a2 3bf1a4204c28794bb4ed569101520668ae7d635216760194733d47b283007a7c 611d9397d1378b93b842dc70df0542d6945bd9a852d4236d7efbd4e285a5a510 6070941db89999df2d6921e087a2d035385207f6fcd23115624a4fed6b945eb1 32f01dfca2e777836ea60dce7bcd594f0883024a04734d2badb7f8d542a8c0b4 d6e19eb3338da496f7ed866a7039d264c6f2dd77e050e74dc4346b3c0c2293a2 57a06fd19a97f06557d79b14b73d31f5353a8df29bf001ce0f40d7e8145da77f 1f9b516a1797fd6f9d154e941dd1a52819a092d7d90f798fdb0c2988e533a659 e8d70bcbcb9f00cd0b7b63ee6c6c464ed9cca7fd9547df9bfc29a6d9fc3c6653 97280267a28ff5d9ef5554f92f078afe41e43a70328822a80e96f69b5b45edd5 5361680bb40c7845ba30b190a4ca7475e72bb8ee3f420841cb0e98b58525589f 9b02834736255b7fc8270ca6c88a95b6c4475214974c3b467cf0dd97eac582fe 8585ad47ab77a341faa32bbe71615d189c66cf483fd65372bd52b92521ef4842 469539777dc7cc1a0cbdd7667d3b78126e902a960bdd68da75d8fbab3640c441 166c5215b58f88c131e206824543d991738a997fac505b505f7b1f77d4f54e76 137a227b1e28766e46e20ab9f3af211d01e720c92eb9dedce2f9ad5aa263c67f 9d1cde6b645610af29b09e69b0ffddb389cac35e542bb9404cf3fcaadb8d2610 94fb1e10cab42fdab4a4803bd9467d5f64273129f3bdbe4d1f949cb1672a7a6e 3068e9dd4e93d517a16e41582f0513066a36446980e63338b5ffc510197e54c7 58e1711e22d0eac9648f2bf9414b3b8f1ba3381dc662914097aef2603045f770 a28f55d095b90091ee16feb79c1eb9febe7ba5cb169a99feb89bf4b70edcecb5 6be627aee55dbb93ab1faf37c38fac23d977febcac6bd7986fda40835a97ceb6 4a36dd3cddb74a81976b81717ed43148d5ac1bc0802dd786b546f6622b996732 d2b73bc8fa1da4362cd9e30fc132fdb0d7e4f50ec61dae74cc8e928a257deb78 6adb05f3e535a45a113bbfd5da55cf408dc66241bfda8ba11f6372c9a1cbd675 8217d3d977f8d231e1bbfee259a9fc3aa4702e6d28b5c2062fd6fd3ad9aea0c0 a55ef554d8748c6c6c1b8785cad90e02f7a09a143e4fe8339c312b133b5d3125 0725c496de9721902907233dcfcc8224dc03aa1b7647ed2f1398f58f85bf2b0ede153d044a88d4d1065f7be2c3ccda116b4fbfccca1d837081d09b63d2d04406a5d3902f52d463f7ea091342edecf4fe13fbc353a2c027973f409d5e00c733006d998e5bd9f86d89e9bbfa6395edba361d105541615c413940c693fb25129c03461fdcf57374654c1fe408b10e245c331946a2a560f69d7a7ccffd29517ba802f27e67afde5643417e22e3418707b6f2ac5557298ea9d012e4d8519608f2900ea8068fd023ea549a014f9b0f087a18b68de8077ea2fd2a5098ff46d83936b80637ca99bbbb04d0b33135daa38aac9c03f50aa0e65855d5415020f56a37b4020be4fcc13bddb1131c08e71b9d2ebf0a1161a03fe0144e5a004d48143187c9d2036df9ccf4aab6a562246c5cd2f867c2f2ab6fb0162bcea61311d312089f4b5406e6e9c5e3f7bf5eb05ea83e53c40f41deb664caf65369ca5d5ca94f9a5e1cda0964c6379987f847adb305a337847d8bd81e4272954f3bdf4083535f1ee3a9150e83ca814c7f925b8d6b25eedc60195c44b0361ae9bac780b739740a96dff30e0f5f0352b461f2e988c98461bf6f86b582f08d5bfdd541ad5ffe7342f4895bbf058b00f929a9b24e0f09ae5c41652afeedb5488e3953fe88bddbd4acdef74ec8086737e456fc48ea68d22c78553104e217cc20a176670c18c9b4e804cff2e17b04c178e19d7bce938bbd01e0605cf79c801535ad9a6db37f8cde40d689e86d3705f98880d017c93b6710b32bea39f5aa77020d03d12bcbd9fb6e131463b5765006e9c8d9136553c6672fedc005cc2df8ac726006466f1dd71f74c295b82652400c819afb1936da1bf39f6c6990f9b88a33bd43b7f7a08423ede40aefff51493d05c00eb0ba72f581263979a0e8d44be06eae40fc8a3c89b75dc79104c7400ace071cbe74cb4478687141b45095528940bf6a62019f8e13de2db7762131ae2fc201648594b7245db987cd54c10cc4bd2d2bd378be6dd00cd1ab405c63e7351fb90a4a13ebada49b7cf47ad275be3833e0ba32bb09df8b7255ba7318f1a388698b04f3c0e9e6143ea6dbb5bd497123c3eb98bca092c39712582c91de94b85d8b1503b3e542cb13acdd0d7c4664ac74b192b1daa365a78db525a8ab6bf47934b72d0c2ff77bd71c5e787e5f691173e5b435fe224dda8252ab576f0356fb36a285f30103ec6e7055584434523a432fad1072071b398f12bfbf9f69df1d6138298c2c0bfa01849007cb5a0b54a649b8fe12d460c48ec122567ac6034c4359cd4bdc090a5c2ab57e50178355b66f6ff1f89e071b878d18c3f8122a44438a562bf242c50c7f261b9cc3a32d1cc820faaf7d41d02a687dd0cc01843d7b62fd466072bcd70cae130cd78acdfa85f9baa6ebe10cd08094b35819ee65e62673da148bfd3c6006f3ba8efbf6d0a60d8808748e6c8d8b9b9ab75ee27664f0f85f53dce07556c40d46184bec50d4e881a3e3c377c77e5c7105738fa8f326bc596959635bfe05d00da1d6197013cd508dfeb090771853536149bac9058ea5b213b8b79e432233b80916489c0e5d87d0b5895f53881e76c03467fa6ca92994ffe50217ed773a46f403f52735c1238c9e32ad2a3644d02ee4da7ab0c71e2494afa3cbf994beefefd0053656e3ec57436ba6fd372162205ec3aedc4496bb9a071051e6cafebe6f86b20115ffb7ac9b607b54aad8f21c4fcc83b2ad920f78cfe4e5a7892b3abdda42f6032188a9cbe49dad7ffb30535cc204ea53f1194e77966f6160998679ae0284260b230260c960d32adafb80bd289f31ef6635a33328485adbbe20a2c36bc465a0041f402119de5b6fe315e2aa21f5128fa4d5053fa5ec86b18eafc8e63ef3d9960725e110944b19fdad6d6e98b548118b7236cfdffd80139852019b922aa674f00e9266c966f11ca8c85884828433cc4de12fd88a29b217e038d79c4eb38b4f890b6b03a64e0635be94401fe00df1ca6eab43524bf453c4f80ee1c527e8e9182005adf3b8817b6a198c659036581947a0bc0901d45165dcc3eb7631677c43e3120dbf26db3dd5055db582a5858bd3991500a3cbf82454f89c48aaf4a20ff0e63e041818699a8cde8626f86acf8dd692c32b78a83373136e793f16c4e7a3bc850a0702cac62218845dc9487ebf9a9d03996b3fb29b621771025c106d716e0527a3085ec820e5620eed380700e18629df94b18e877058a12195c796f9a8d1082b8802f8993d7a478ab32e0d3bc5f65bd83df0ace2c9172b7f88e20c09ac2d9b01d0015706ba94d2550b85106cc4d856747685f9ac1a7d531eea1ec7ff7e26bbe8700e8242202b5d6d968a74601417e28693ea84df21527d642c6fb2268887b960170ccf8f7b3a1d52855aafc2e034229ad74b1ce9866237cf86eee1bd9ec06fcb1109f69f453ddb445e99d9678b2309b486ddc24c232e5db84adca8cae5fed3ec860556c85ed8f806f6e15b81c3810df9785106703a46417c09c4f34a67fab835e40f6d77d0b250c92f2a91adbbabc375a53afd20b83d409e30224b15f3656d4faf0af96583175f5795b880da8bab7cdb4d214d21630dca95306a7dc6453c63f0290a91186166f90dda9241fb4b75c36acecf0916426492f2439d9649b5e299534d0a31beb0656026e214652ecaa9a4da699a3eef8a597259ae2fa5de6f0b04d67202c35266ea6cf6f691864692aa141b58ee1b7d844380a1e47c695e953f86aa2400aa16464efb8d2fe0314e7ff72b86239c12532daa4a6b505d42cbc3f175c24301796ccfdab3a466b6a45347c3483caf228435b0c5bc94e75aa5a4b8b07a67cb04b4fd2c380b08d6d6bb37c95fb1be70c58d8d6aeb4823fac063cf31a42ed41a02df275950046b9a5bc391740d2e97b111ad4c93fb63c9486609f9b36e57e02b00a6f998b7a7b7574ab3a4e44451951135b4c785a39bb4a9c186cfafd6172ac90a946e92dfb68d7d14948dd2a0fc9cb72c1133128fd30a115179f0cc1f4415a00d7c055ff3dbe30a1296f8b592bc7967110e399a5e2ac8a2e19694726cc4d75a07fccce4ee805b20eb1fc25d51e2b24cb5805d795a99f1d26f0e1044c5fbff920d4a430fe72d2ca25b0b0384134ca913fb819708e81f36c6466755883576172b07d30151f890100fd508ba291d02a7b56c7d5b8e0e3c4f87011674a9a36249950157db95db7ccd814eed7e0198defdbbf3561789d9691ff7574294bc50f43f3e06573bff96f9fff5eb3c91d4febe7aca8e9f4327aeb76346e5eef408147e62ff0cbb00d3539501aa6534776b954f2d2ae179253e37afa8a0f7e86b52ef5a92060f9d6a244f53b4718a459b6ea961ae5e326e4f7ae8f08f75fe3550bef11aca480510bdc2555bb61f3692b9a6d33cd19a84cefa69396d4583e66b878ef4c38f2b06274f4139e47a6f6eb149806fb97f05d796a905fd6a1dfcf86397a758a26dfe075a26c7b128e6aad33866e60e29747a21e107c1d2e8c97cc9fa91e6f53dcd3403ddff52bbb2367cdc2a79313533b7c43a2308736749c1d984a979436ffb9f75000237c4b6bbdd1e08ad136031eba6ca656c635e503c95179776a57ffdfcc78b0e44f1a027d3550ae88f7d9eb55c88ce4f5b5b21d2c977cf065aee06005c3662016a4510f5f56f3c0e875d48e9c1b3bb04ff0893d9d36f568c7210c2c094c078008c5fe171c2b65108c6bfc8afb929b70c3357e585983c907c843b65e16b1cff08fbf2fe7a41e80208411cf0111a5936dc9bb0186c7d8eb2215dd1854b3ecdc6016efb6ce9ff71a764b89926f70a8fc98bdd1a37b1d15dc2a53c3cc1d163ebcb0ecc846a14b1344e5d313bbc13000afb40533bd520381d12b0745824e40d6b490e9bdccc7d6d176dd6b491d0dd5ea5a7a0e15462110a47511208151577bb678f08f8cb3f23bf16dff733affa6a713e27c1fad0880a3cfa659d8f86557e93a24d02b2edb64bb869ed65eeb9039c1db09fbf2d1e6afc88aa437441b0cdc6ee9cc007e14b736659b5d8df3356430bd4f1f76d6810913f068169b67a09a7c5bbaa7b0a4b62bf5bfade9470a8268798e353e4f250b01887d6abdbfc6af360c32f0de50e1f93b082fe2f17b4e193b48c18bfb6f3f7f329ad3ef1a5b1bd4a1d0a3b886e05c78ec6bf60454a342131bde536e203c65cf229372ffbd3bcf804e8b54ac2d10a3807ba6e21fddb0d97492859fd0c9bae35f9b5f0062447759b371c2479519e0e38af8bf77dfa6805c91399e37fee21513644bc07cf78014a41d8182065bc0a087373c2a3d5e1463620b95e9a7fe068658c6aa7437d5fc54adb7ad806039d5b05e2ea4ebbcd9fc0549cb1d1b847b34f7d07c053496905ddc73abf95b8753dfc0a33d44b06f98abfaa762a042b653ddcfab224fc4ffb8d8df6bb1c98172bd18b04c893e570c8f4c9a5422dafee9ce082e7d8c57dece288deb5ffbcb09201faa4047dd7511e65884860e469e5a660d2bb888ce239ae6ed1c74ee66c6ec8a5f8db04d5c8307caa981c73b3f34c3e25f791f06ee9c3cde9b2b4858add5805436e8802dcf6fa5c4784149309cfc20beaa27dcf7ea0519232ec06c4a173c04fe963c20592bc49752653d5f1b230ed1e3bd8dab6b2349f13d5520922b6813bf6fde7760b10c8f911fbd4a2efc0e17c6aedbb3e1f539b89f5ac512617122003b08acc2809c7499b79a03f42846a4f06f55459ba9f05c030bc860b7b9a7d8fa155a08e0a0b798a0e8441a61f9b96d52b1295411b03fb3339351352fd8a2034568b7eecce0a2b09aee256c6e92e855b5e2780a1e16f40941262929af61ff155866c1cbb17086e1f1d3d2a9c336998a94dc6ff11a542effb238be54edd4dbfcc0fe8cdf7ab0a14fd9d538984a68c05c4d2e7313e60b6b2df0d070e47c28618ea705c2f063f06e2a350f6385875fe1637ca8b350aa3cc8ebf394c4fe9732dc4c400b8c5ae480fdf9d37c17274b9cca93141737cd8766e9609d406c21b688b8160fc862c6a4901d0ab2c0dd71dba36e6536c81e93c801d8e90d5d879afdbeb29f455118029e90c87491910ab47636afc31d0c5b11bad47da4def1970cbc9c349fabcf2d3294f09b4a3b502acf75983e0ead1accfec1b997e5e126c4f257ca085d038422d60e0038fe1198b7ca2dfd02fa6eef4c07cc978ac63e1ac43e092ca2eaaf86a18e4cd0e8b0892854d802e8a1bca44b5ca9584790876661c55a18f797611485492ce06076dea1cff67f2c1b676da91e23e5a1cb0db17abe84e12bd8c662c8c152b9036076804e99ec379dabf50df7930156e476c90115db46948e3b53dabda394e8e890db1141832fd8fc155c71d7471018b72f584f4853658522c0e0f86e91f8356330c602ec2b291b62cbae6911f891c08af9e443c230cbc6fdd7477cd3bc6e6c07903379a7af3c8de96ff618ceb3ff7005e86d135ab103e28477a9d1e12936d8890092a4df21503fa5034aadf04613447c75e766ad75207c305190583663c938b0003f832b36d88204f94314f862e4f4dd01bd75964d8bfd7815e59a24dff0d0e9700872b80694f3c019e6ce6f67de1604c4046634dfa55b2b228357ab2d13e44f9098e179b2068789fa7d13bb9215f3faa51360b9e8f951b9b1acdddb3a36ae5de05732b7c5aa853288ab9206cd91a9d0fa00e6122ba39906bdf2a2b4eab75f2db045b6d71b2ed8469758919feb75853d15fece60af8b40c5c4df788b0be68f8de0c41f45058bd55743016e32ac633758f209a394d02619e2cd3fa6de68db095b3072cd53a7d4240825981d68a071eb3fb0bd8f6720038deaee9981dc079669b230f3480f709e81b4131b9a310ad0e244528ad0721a27ca7f4eda060c00dabbf7804bf018749ad8822578b607fc2fab6aeb123f6162c8d5f4a7b9b9dff2143641500011d7d09cf8ad7e305b4a157c0698ed70c901c8381cb574b4b7fb6619298a30af7419f5ea4b691e9a9bf6fae152716381a32b13bde2df2b14265a38e8fff4e030ec4236cf950b87cdfa53239cc9fe515821394be36a325ed01d96d7e42ec7e0ba3cd00305a50fd484c5188ab93a97c60f9ed21d6fcea2fd56ed7450f9058530e641991744785374eceb151a3cf661ff2be8da1b8ae7419ab7a68c6ee287f4a085daa0f5cc56a60aee7a64e37d830aaedde654c17bd12bc8db6672d9af135cd086a7ee5cefcb669234cfc7ddd556b7e9e57d9e14a45f9fcd5d400600c1bdede0fb367ef71507d857f189eb948b449da82981c23f7bdfbf143592dcd60f65b2d03f6530569005a72fbb02c2c2d571683a154ad5d14cab79e8dd27b823c3409f6086275c2684082b99dd010b2e191b397cdd009cb6dc2a79add853d8dcdf9fcc4079e4a44612d1e7d591d873d86892df084c90c37614ee50905be550f7262bacc0c41097bf851bfe66200da4256a64f4bc99ad3f049d9ac7f53987463804aa8b70bec27e7821cd92693d1241f1b928b401995ef925b1c24f666f330fc3e197140005f34943eb0230d12118b417b76275145e4073a05c200e29e3c685fc501759509efe214a4f140a824e1534fcbe7ced9981a6d2110522e88c199f4955d251c0b063f9a38147eafa5263cb38f7a901621fc5328efd95c2c5a4d6c74122825505f086985c44950f48d95d9b1d693e380aff3b9267c0a1c18469bc9969fac737f2a0caace408e2b1e4b396dd749616bd3433488b930c5cabb8ecfac65000dc73e3605b57305de61677a959bcf6da3738cc3d255383832e713cc94f3b5ead1c0e68f00ec20ab30bcad3e431a95886269e491ad0ea6728bc755a428b723c110e1184e0b71ccaee142b2fd3def4a1dd502301b293487feb2aeb25f1fdf5d18cebe15cd00155b4ccc23456902d6dcd3144af811fa54dbe36410637b45199ce7b5d86995097d1ff95b0920c7f7864c17d92b5260c720804c856c98b1259e37fa07e47e540692f1a36863c5d74363d5e518738570a2446669f9c33970620a562e435fecee086d7c387d74bdae64206bc8ecd413f6592be9dcac8f6e6482cc4edf9cd3f02c0b247d53a56c6a328608b437084c94c7af362dc7237c71b7cafd8646736ae7dd0f7167c4f11ab8a60989d1344083be420416adba51d6ee3eb42ee7c7bcdaf44a0a0e538574ebc359e4cc3059eb613e124a62a45a99ba9963b3fea3a3e75938ec01f80306535ed05d7a76b9a79261d2fcb0c8604e829cca1caeb27323048a4fbe02508b9961d502a4fe6dc3f9af1ae0bd7d24bd9c8ac18ab3c14d256cd988b4de0ab3015a29213bedffffaa4d5b85b41cc24e60fd50c5d06cb99128197bb0e1a707d83d2e1f4e23b941cead0652c331d35d0001cd35180594f409e7853c4402360457d5261c5856893fd428da42c4eb6c5bc67415cfb04e3a701e474dfc339b470509a1e74971cc76c2e3456b886db4cef825d90f148d754eb59335c3276e18df05c7a322415bde752f373a264963cd0e4e1058e849390053af2f397ee7bb22a2020bb59450f0f16e351d01d12868c0f4fc44b17e391edee62626c39aa329cfd707467b10e4e004831dfb35052a7526cfcc0a58d02b404f29cd4eb87e673492bf086f133a2b0c8e757487c6a866601ffe9ba94fc7d70aeaefba125666541848080cba8e5b71b5c397a85c18ed189b647030b7bc5e2a6f41a76961825b27fd214500f6f28e97745da81f70f0a03d74f1ad61e37e4cb22bfc3578c0f1184f380fe5044359909b01cce8865ace60ec6d845c30010c766ce5220f11e6eaa454af2be00f3d72d0276a240742a0261cf20636136696995b6d7fb70bcbf417ced17950840d9c8e331b12e670ad688f57eb360e6527cdd074240aebce6d91c057aa8bab79038e5fad5a133a2341fab0714556fe93c7f84336733c5e001f70b7535d6167b6052019861d2a818600f7f2762291f2c5c19bc53df7e8caa171031a4ba1d132e304 false -check_ring_signature 990197794563243b0800b219f98c5b494ba36cb15efd9148e44e8bc59cf8dc0c f45f812e2dc7f4e21327ee99b4f30dda3c93d52008933c782d1ac601b171a47d 4 49064be657986db8d5d5e805557187084b0f263261946b3885903b360b282265 40deabaa16ce7dafc086c8176a1fbabf4b285260e4492a2414ae8be2dca7480b 08f548aba548c423346080e002ad65d0ce8fab62647174ea960ae70cf7720a36 623c4e66767d71ccd3db36b0c821e50f0d8fd75e58fd0d0526a2e96b08266090 949ae399d6b84f166586df649e3b7d7fd378d3424138979339654bf379d8e4070ed3c0755baafb15959c76834d022e82d1c1d2293ad64eb7282064bdaf8724057305e1634d4f2083bab5cc3a7fe699aacc308038eae460a0fab562188eae910075cd3fa72fa6a221e14fc9bd1512ffaf2529f41fa6d5c17911da013a1c0d53064fc006ef1d1c933affaf4ffabbf40ae135970e0b86be4f60fa05c2e14f132e052af7888755051236dce0641343e99de4ce4d9e61da0363522ccb4a6296fa8c017a90fbc89ab96fb7d3bff386dfb0929787aeddc3fb1e8b5b6be806f6f9557c0b0dfd823e9932d29ccb4e2378eff942fe223b3b4418d25535eb23749c80ba6304 true -check_ring_signature e947bc7c9a18d27883471cf208d4c9ef763c66f3dbb5a8b53b9f10373f549a08 7487ab39c94a1b545fdf665df0081a2c4daf7da0b8200e49c393cd230963a7e8 81 b4e698eef665aea057edc42010b5b073cf599309c63ba5c2b2d959f4fbb7954b 6a7c7b77bbdb964123ad7595ba276f9b121b745542493b2e134d57d6dfb89707 0d68bedf91748d058f114b8bc81639f2afb59974e3e4d6e38ff914c873059198 d9506a686ad59cb5a5903d2e6a3d09843426e619e957146d4b1bd626718f5b5e 48c88d8f510f8971298db47354ea23b7984e11c60c23c30b727365942a5a61cf e227f0ce10f573f79232f37bb4a7a066488c7ddad6230aadc09f028481332eeb 4d37128ac54d8feeaee5d95a359269b7bf508e5366d89a2a16e4866d3ba25c2d bd8c4f5659100c8878f3920f5f7032c1549dc0a701e39af42bf13b93a8a0d272 d6e528ef666132754a225fb578d7819a7267c0c37dfc46bca3114625f3586f50 f28df9d876eb45f150b1f1dca114588e233e5902caa01206db3175fc1a3db268 8fe9330dc87e92d23b2865adbc519404190cfa8c134babc2b087293ca16df773 a183c9e497d4d058bd0388425b747e8d9eecb3965281c699134ea346deecf510 0f6b381e333223c28515b08cd07c5b2093def3c528c8ffe2fad6d20da0c63e48 f2b1054ea9fb4e6c21269333b3a4db7e244e4b9042d3e961d1adfb963da6dd67 37eb97951965a7ca9734b072586a43bde45404cfd78b62d0b6e11ef3ea36f614 1949de7f0c12195bab8baa1f09d4e712f49d5f543656f6e749e75acd8b2d7900 d41627db7013347a2886ed7f5884906ffdd7c697f490c9841c5a7b04f18fdcf2 be3b7ced14cb6ae1ebaa6fc3b6a44210ca0d6eff6911bf532cca246c36f65f50 7d1239a33b75cad6f96fe0cf5d57ee00105c458f969cef03606ceaa105d115ca 78b81005c9ecbe8ae67542a3a67b1b8de3fccdd96ac793376b09344bafa2bc3c 0c866b5b5271bc4a317c0350255dec4f0f8a2bbf7ad19fec56025dc3b1b781e1 b6b8524ec884df2ca3d976c76b8ff4aa2b6c2b15885b15135f6371d2e6caf8e3 51d230390582cf4246c472a8f42f7169a000b0e77ba23435190c11dbdfc4d0e9 a7e50511d05d17cf67fbe326f30346600290fe7391bce4f707b7afb6802123a8 2bfc0662e4f7cd8fa77c920adab277871d914c6d49c94c0cdcad97e35286d33d 39802c5f24c2ac6dfdf82cd29e94f157bca99872190f3c42904a4ef7cf2b928e 8c7856eba11457a89868bcea983d3b770e95a618379134770ccba10f5b51b100 66f67b0d6f7afa5eecb736796bca37ac266507e91812634598c71e0c8375ad6c 1ba6f3fd8c91bcfbb0b81b2fe51385eb19f0e906a12b43d83273c4561748b8a1 2faca7f4af6895ee2a9ee7fa9f58a0867eed1c6d577e30e9a268727da5652333 971c2881b7d4984b9279c81dd71a38fce174349780ac67e373c43dd19a81cd2f 8e2e6436961bfe69a65014c0c714a14e4b46ce0ddf11f098e6e488c938ec0102 3f9578204ea1d42ad4ff9f4e4d73f9454a22eb7ea63126a30746879807a0739d d49fbc1c9d9c95fd2de91d81c2069b2a24f75946c764e45cc55e05fe15bed66d ff97451524d50a72ad9127a30c59a20b6810ce5331c084b36da14b7c8231f26d a9ef558fe261ea7b3288dcf136138e00e052f98cb42f20d4f0fa3c8d60f23de4 2960036a817c985d6bf4c9f16a5705868a83d4ca20f540bdbea8b6e450dbd0ba de4fddd1457c967984806bd4f768d6f315bb1a0d81ef175a4f679d9eec408be8 22f684b833687446694e6a8ec03b664919077cb71b0cc4bcaeda8ac43d2547c9 fa06fa5ddb638807f34eb42a44769bc766a0263b2ebf9c5cb3c4c7d526efa68f 3418173d00f4746740c51129ec14c240101efb26d9fe6a93e1445a7d9ce2435c ac8c9695e89406525a4c4ae2927c6bbd7941e1c087450350c9f3f4b4e77392db 2a772666deb8b96b03d92ec3a2b26e8de07b53cbc2d2f3de53b9bcfda011971b a9bb438a55d4f10fc0ff7b8395fc9b9f1f1a7130aa34548607b7529596f2841c 68f6821d13000de9ea710936e5f4dd0cebc7778d4c9db4472ce76eb07ea9aeca 330873b66c9e6cd0f298890450ce8c9576ffc26345d602a78598e5992b339596 dff018e7209aa6519f12259277dda9cf474c44ac2a320f929533128a110d0338 023715820bae561daf3f40f7b334c79cb4e85b58d829929b4766ca5d48230a87 11beecde4a48542b4a333950529251280075704e5d174349e0be9da86c3b811a 51db69590dafb11be0e8dd9f23f4a78b2a8047899bded6ed474c39769dc25d3d 60fe9a16f2d3e8184c42c4800b99830abf16e7adc9d739c501259b3cee8508b3 e685b0c3e0837990edd283f0babba29ac4856aa6e640b7b2be17bdbecb638d9a c8ec2bb13f0abc699102d3086874d491bc1a19c12f195e59c6025200d77f4c80 4698a3b74dbfcd952a43c32c1cd9ff349851314ae0e7b9ffd3df08f185df14d3 8ffc0019dc69c659a78ff2e7fbd53248f31a27597e471b0637185c13735d2a42 b32ca29a5585afeabef4d7a4cf24791684faf03d9e8125e29103acd9ab106ebd 5e086e9f345e9228310084996a02bb099cd4fae2e5855877779c2545545c9f7c 4d9c59a7474cdccdcca9a3c63fcf67074e80ba0ad45630673863e531a9559b3c 0938e25ff40b34c87c244d97d8dda7e37de7c9c71f073c0f1dd3c0dfb6a2a105 cd1892dd29a859b17febb4bc39db40f39263d7abf3a63a4e5001082185215843 f073fb1abfcd2810df1dcea6b1b1ed99fffabf31145834424f8ab380e6ca35b6 863dcf4bc294a84c814f24ce0d62b4aedbb222ccbdf894522b84b5984c3cfba0 483cd48781f0634af0c09460d9cbed14508d2c35ad667cf3e911de1526492922 88c8ab287793e112333bc7c92b33b51c7d5deba45291b2a111dfac7998e548a4 08e7ab9715555c7635bc6bef4ceeda470bfdb2b3c3567d6830f7e0766fb7e778 c0ef7a09a39f5dec65370f9db83b7daa77b06febd442705d6427d50e787f75e5 bfc2ff04ddb999bf39ef5c9b29df989c50a28ec92d2a911fbc0f015f0e6d8806 788befad4e8eab98c83aedde6a618ad61bcd325938b647b83d4585e58ac2e334 580edd4079acc73b7501688e05898cd4b8e4e553967ba3db82cdee550bf46607 fa0cbb8369c97a09eefd47f540268f835f4dc129ecc3cc5829cab023d22779d7 b4c9d6bffe138f8d114846180215f117e586d2d66a013379a409b2d2b8956c6f 170b765745a4101f450fdab534fa95f1eb42e355d4cd59f16e6fc620cf49be3f bbaf7867c81fb2f8e2a97b353e5b8a3b44b7d3f7b595acd04534bd6a9fbfaa1a 4bc0d1c99f33a51ba0224349bbd88cf2a1ae6e7c761a5035da78574147034002 20268612a833a0c26a63241a1f9d069848dbfdf082ea9f3c639d25e57e4dab32 c45563776c27a57544b4e26ef8470651acc647e7f1b786e778a68ce8dbf85d0a c1dc110486c720eb920e348c8275fe54f0c5b2af669a493d4dac9e66ed8905cc cbb7e4885b65e6dd4aafc4b73d3095a75c6a75640a20d0a6dadd3507032a3d2d c3eeaa729119d6fb212d97c65f58a74e99749df9faa0c29903485c73d18053c6 a902be1f03be2cbad6a4edbec96a3ee6a0a1810847cf63fbcb10045a106307db 5ad959d250f34305a897bbf944a9b60c13a7a770d6d473c9ab73d9dbb0750708 992bc04f0ecabd31d50d3f3bf794ec4a9b545b967f35b717710fc99445cf7a0b15c67641f0763cb2c4e7f2be29c1629e4a364ffac4164fe3190163c1fde623190f087c5a937c5106d02e6c0489daab73e9f71d45fbe30f36f3b2860d3a078f08ecbf6037025bf49cda6e6d90a52062b3b07883fa20814aa3ebef0d2196ae5d9e2dac115d8d201341da9c7a850dc595d78d530924e296195305f988bd04a0bf09619785b889424b0399641a3f5532fe5f672ebc4e916b726ca3924a85da78d4084d6ed91e4b6afefe4bbc4edae8119a5c46e0c2345eba565a63a5c6b36b559e0bc09e161b85a604c331da96775f070fcd34b0db1e9379cbf1f4b4ab0938e2ce08821ca76f35709536f9c14b6fd2cb58bbc064c28db07579250fef3b53fffb3f0b192ea91a35a9b8212e364bc5d5f7a3015821a32cfb11f3bc47e5a990dbd8840745b84ef121386c073d383efdfa83f5d5fa5100cdf6be8af9faac8ccc38f5d601c4d48781036050cd5f7c030584f5c425a0987a7e86cdf76987d52f696c01900577564daa9c4fc01ac42e4971f99ebeefafee466f9684f8446a86f3e3a5baf30c6e914dd35c874cce7240add3f71a95c52ac5ae2fedaaeb284d4ca7b43298ed0fe69b3ee348a416a536c9d35daf1f9b36a436e52b8a983edae978216712d1eb01b96457a0968b23837ebab2a2d1d9696e8e22f030826af8a292b6bd0c49e6970dbc3ecb64858d17014957239c306b8323d510d86d22157ce791292f458cb89906bd00b262348c84c5daf2994e94ddb88afe380056f4c1ab518c780b894ee47a06cbccce5d29ac88b9c7141506101c24a41d7eafa2673b1204104dc18cc5f8f30060e1470ec2bf4ae30185cdd10e76568db8bd48be175f8074a50142cae84705067ff2531b5659f210aec726521782283ea536e60e5f3e9d1ca9290b3df24ef30b28f45fa2b7411cdee94206e97782d19a3312c39c1d503dcd00923c959720b903a4336313a7c5840fbeadff46526a3237ea51ec188af1b067513e732a44544d07144801acdf971983a6a90d93f0f42e89c058fd2937e0cb0234745cfed0da5006abcdb5973fba939a229a6b2e6d154b199b58b1e8e600111f6371ec4715a85703a1cbe65497d38c6f5b1aba3c2bb871300ca75df6b1b3b36614079c8c8b20390a65f939007b10b87301994d9f7fa25da3dbbb1da30cad9441060cd80dc938010f4ea4f64db25195570838fa4b7deccf80493ea66578a5eff03629643d3afb1d0328dde2631a20ef3ffe5e8d1fb201e23dae93e641a7f99ad886fdcdff547ab704b7eff22662cc16208cdf0d90317758fd80a5de2149528c68d8c81e56bdb899035d137e5bc998ec55e0d433ef4088a5bcaf22cda7f61ea1ea92ddd7dc2e411600bc1c4aa940c248501b5c96440766d79c7a7006d3b1be3ed3ace23d4031651c0eb0bdc9288470a6f9be72b7e85fbd6e47bf0eab8cae6f70eafe82633d8be957041fa46f8d58b8df6c9a0e29ba094508b1bbe3896656d5221205027ed0e6390b0f80ba02731cadab4877908dceddc69f5442a7c5bc59a78412a9ac6d058cd5090412017cbab5362951dfd9397fc5d24b5823f6ee075fc2dbe0345489d9ae059b05cceda946981702b27b5ddadf01ae354cb69016e123d28fb88806fdaa3091d2072e9195370a2023b9f3304e0eeb588678da6b61f3fc8fa1d14a5eb052541fd1013a9ae89642aa9e70a52fa7fa6750047d38f88d1d12f5ffbae726ad481d8abb00df0ae6adeace886ab5add0a65fd1dbae220068105849afdc82e681a06f7212082a916a8e27005e0beb6c5555e3eb0c64b7fe6242bfbe04372258ae0ab6eb94031f12129b2284a51f981ed7e671eba34eda7f70b431464d91260bfb164e69980c5207db9c6757c26c051deb7e79bd7abc8b48abf1a7c772b7fcfa15596d4fa90e571cece3e9b83488562c84a6e1a0d7f10b247f423e307f391c93645c1347e20b725ff5b2b4f6a7dfbc76dcecda12c2ad9898d07143fd9081e8c60dc19d5a790a34837b65b5046f9407328fddc0517422600fa2ded64453e2fee19e8035c7e205402a29dbf1300a857ee6862d19a7046d397a696409ceeeff208a01344dc9350d4b47efe2e5b9383e09a3df07cf12a79865348f22647f8b7407e6e3ca4c55690caba195310dd80b3810aaa32cce4f6bc7ed7067b2407c890dc58f11ec6943c20c20c1202ef92f38630fcfe1fc501bf67dafdb40ad9721bdfc215a8595470b240c263683d076cfc6124b6710ebb2c579aad840431101d9b5f43181b494bcc4bf0f8573c6f69bb7fbb3a2a86f475221af708fcc6ee7d64e2e6e9fee6402fba5c402de10a634148e9db88df4be2fc18394bdc7e3be606f8d437e78a7549f0a5fba04ea73bcbebef1121fa2cb8e18f3f476db42353ee11c83022a98810494c27aab036895ad8374dbefb1ab9886ad4607b47658c92112233d021f2efa1d5f6d0a1c0b8bdcb23cb1447b4f038491ae300ab7928bed5d4c93bee4335829ae824c073a08a3ad10fb9ec8da5e277cb041099f5d6bfc69efdebdbb104c49a1b68c384c7f082d7e99c2e15320d12ab87eec66b7997d1f01844b26539f71b59d8c4433c33004bcfbf73aabf4c6e6c60c17231fe96c4d5d0dba6721a3c2c8eb62f5d7dff06d0917eb3719704ff13b06dd1bd4115b0fb8bc53bc12c92229cb5968bc15faccea0ede38471a24e667c12ed659cef6993183fca81898041a8d0cdb4c1d6cf6446803e45dae026353ae1a5427e2bacb3cd45a6da67c9c187b2ea6eb6b5a4e38d3e5061b0b5b97d6faaffaf03be52ab41dcc750f898a8b33897535ea13b179697d750404475736359b673773d1462aa19cd50ed3653db0779a0c330c1991d965dda40b48441e0dee8354844f146b0aec1b88712987d02862df367685a8edde1b93970041f965733ccee7579810f16d41178147733158c24ef6194380755c67b2399a02398b6eb0ab51d818b892271bdd133620db09ad16993296bebdecf78016a630096ec60c1a64d56ac6b0b27e4f9a3ef6065523eebbed7e2fa9605b7019c0931a0b9e02dfd85c20a19143db2b47f73ccda7cfbbf7bbd799bb07f4a531178f367b07f6efa45095720af7156bb461f566de0b802c34aaaa810ea3e6b22e3ef724710ac1ea58bbb5a3996f01e2ddc19aa380991602362b3b4d4fb9bff9bf5a7a1ffd0ab675117e55768f0a3103bcf749d5047c919849a3739288e5d1ec9dd9002b500fefcae1736dff8b89128bedef2866d2ca63ec93ddd50dfc9170772814dcc8b70d279f37c630209e7a84db56fad079cd2923b3419920d0ca5c904ad67e62039308974c45946417283a62aece8eaf4e59385b4a3e293843a415bfa98bb9a89f1304096ad29a6ea988e41102779994b7fbf14c9db084633b1d452ac97669f280720d2f109381bbd5477c098b6eb80a35aa96a752b00f39829c03e655f293367e880477b25be3134e224c0013caf3deb4d0b528f615802cf0d6d25565de6146396f05fbe47f172e2bce6ec862db46224b0a200a432906eda42cc5f7ceee52ad72b90cb76299c82e40310895efb89d2e29db4366c23c0bb83ae6faa85c87ac6322870f1093b67c41da87efff0f9588df3e84d8b3e7b59de97d19ad8ef1aa1c751e9208e583f2a7aa0b2db3709c7b7d6d92441e8492373770936fbfbf062099b990b805e7947250231a2c5fec7c82a249eb310c2f4c0bf7bfadacf08c24d5aacc668a06f826e8523fa5ed0d82648849932ed89f994e8d91252e0247fa33511b620e4d046ab5f78735292cdc3870eac25631a6da33db60bc9cece5df18856cb7f88c190fc519ba0a5df53ac4ac95d63738bdd5f99560d3f095fd2086e4a3db98ee567a024125cc458b6166f16b924459382a83db7f0d6e4c51a7b04bbbd58ac9164d8d064a9b354e975a43cb2b0dbf002a17ab56a99f900a97a1f85cb98f6da9e714d20beea0fb76456679991fb8318e663227dfff88d4bb1599d32ac8f311b5050b9e0a4351275b2d50ffc484a908e53fac131f8a20bb835e3cace7acbbc68b0c19a608c6ace45f26d0796601273096f06f1d99c9536fc88f3f5c11a7f24a08776df106039765814286c87f2097e2f129eef95998917a31af35ae6c9b0a83e105632e01953866d64f023f15312e314a35d573f4101d0aa3edd40b91e8cba090b6e0650bb0d0b796afc4b0f55db75e68e7e0810896a244edc29228042e13b081d443d607e5321493d58e4995a1b5daa9a8ba5c1d94dede2600ee022c1805e98428847a05694fcda00f230e6ac3d1d0b5909ae1928e5ed84fcb4fd8fc8402a1d8ea5c400dba0ffa06c0b7f2c593f46b659dfa3cb00d1538ceabfd2c9a547d15d8de257a0c8c50b85e5c5c570af2c3b6fae82745926b9b035a4fbee4fc39fb5192b033b70cbf4553acb003bb45c3e2cb0b49059f4d3d318dee3fe9ebd1eea12c33469ffb06f581e3ae8105ed7ccc7b91430b07343c97b8ff5e8668bac4ed70919e551b6909df84ee413334a6d72938dd76fbc6dbc5c51444b900c40723a8dcee97f831480e1eb4bf372a5f548b28e32253693d5f7e81e9cecd4b455435c18e6daafe67390c8f4b26d34c19625f95566e2c3d2e4091b38d78d3f879999e8e90943ccd57020ccbfa63395d4fe293257374c79242f84fb991e7d689e2d31f73fa9d400682970df1d664c9688ffbabfefe3cd26a6b6a0899898a34495c5b26f465929dd64bf208951dbc6a6add39e7a1ebc74a78f49f055a772d86ddb2e2ba3f20acd121f0b6029fbecaf5f6735b0b0f9d3493a2c69dd803070c90a1edc2e38a10f5063b1dc30675115a97ea419bf9829884f382414bd6f89d240f8a75e75c052b008ed26f9c075cb3d5a7c94882ed798c18b806cf5f5a45f964dbac1f4f1dbea6c5eb5722770dfd5aeaef4e10f73855958624de0fab118da8d9aa7f5ac936dd9ef4ff347c5e0a10ba6e0cb31bdce587f10ca24e001a83c4f99dca386e4d2011b657a15adb980c0de3845ea8b583869954a56178a491eefe8ed0d4fec9832cf3754fd9bd3c860da83ab4ace99ee2fd4e28bfe1869c79afe456381b4d281f104226b2c87217f70260b82444b22cd13ae5c2cb55a35787dc6e5dddf8ee69b10e4b0f3307213d440afe152df7d573f94d906d11f7fac01251f90fd9a82317b3bdff86e237346b7a027f320d18b9ad6ee46d91b1c581db9148e263a96aa6892ae1a78d4fa3b818b8080cb6dca8828bf35fa9c43afb9a9c5c624b8b98faaa71bf695426038804a02604555ee9037d5dbaa356b31e5dace85d0729d52db390cb9268727cd2de3b8cdf019092ba7b95aa98143112691c9faa219c43a32c602502d682e7d524c8f086630796b7c66deab008fe7339a796b3577f35219c9ccd3b90fd0da687dc7ff1e3da04a069d50747cb1d6224cc4998d044768d6732da202e392d5666a06b1f5d710c08d950b323eda4b0f04ca0f7324e582a1027d0ae1a4f26cc505ae4f1f87576a70a37d68e2f2541a76606393217f2062fa8230a4d54777f636e143506519bbfe6091dbdf40e83b7186a29e4f44c83f3f011cfa544e94c3dd08aa30a957f13f8180f6158041520bf4143f80723ec66277efdfb7d46387372f98a29c1a15bd13b91085b62626bef6fa279cc9dab4c6bca3d39e18df2b8b0fc9ec629ba17a5f11d670bfb364f6527e021fce31831a6ff7abb520126deb61db6c4818bb1ef4f7180d4067e215aea0e10b8da0a21c6bb33cc3b1aa93397bbb9f7bceb21d081d6053f230bcc7f40b0050edc05331705c96e1f7ed188a5c9dc09c698d5d980c89cce00320a0c62e9f0fa9d2a47400c15ee1486c7aad8272c05166b31576e48a94234276f01511c3c1059dfb95256520678b8f814126acfa8dcd80d93f251c3e7349f4e8401cd0a0c384cbb75353c06219aca6b16e56853fc925ea18cee0c397b9c435c9e0bb394e6c24529638c3fd338f799b2e51bdbaec74864f83372a2d5e42f9a306503e23cb0025c2b12fb92818052472cf071eda9acd1c117abb246b2200fb3068c0576789dbea900e4d67c237145a5f5d9d0ebd362ccd41e31a146b544f0bffa1c0cd8a285414f570e4eb8221d6b45c16cb21ff0a3dd904097ee8085a48f496b3d0f110b4d811a1c0f032b9673da1ad2d1e1ed165ca23bf74a895ac0228caed620016051b20f394933a0c46e2a54678c43e782152b88336b837eeb9800d86e42c402063aae7bb44574af1dfe271bdaa21e05c11404cfc3c5e3be813f6f6a2ddf500b21f35166f1dd67241f9fe1c0083d634a0f56330f782e779fbb9b372c6e473904f689da2c8d32c6e49d08c363c77369547b97e68ef88d8277555872b71494b40277b80d4a283ecd99b02dc4e3bc7c2099839eea7f0a214c8f997f4ef195a9d90884b0e8ac2b03010cbe444dbe60e34ad158fe6a3fd8e66048ffd5109c711f5d07bb2da9d0d3e274c211a73c6f78992bd39d445a44560f88c2122647534ca2610b10abe71e2cdec52cf8ba6d5489246ec1a6568325c5003a5d733309789497a20c220019d8ed2ab2e60e9d7595cf1dd716b8432f3c2849bd7496400a2fff397600f0a314c7522f4a4da2d66abb746f3b38412b5c229c754c4dd1c01278fa5d8f091ad6635b0246cb7fecbe6849a8182e0a8bdd2d91430ebb6959f7e297644c660664d4e57d5ec342c45fcbb8255c6c084e483cc9de8e4f883062df62fef55f53046e318e2950af95686ab5535731cb0e9e06c21215dda124e9cd15674e658a400ce3187642487eea2403e4edc377868776190a5f0fea0e56d4899fd0c9b0a23d0be273d9db7192af8e2af2dcbe1b4fe6409c2046a58359b4f9125e0728dd4455053062a0578a3d78892a2f462ae6c3f3fc75138345975b8802e15fe345c4d69104d35ef7b1f5a8dae2fa94457ad1174723d5df8725b26c335a00e61f5415faac07d6405b247f072bb3858255143bd649f6cf6638d1a616d455257e249ab663df02337322f31b1afba50b4124c822707182a0c9c2066d2f7ae0055ce1b500ec9f0263f4bea988a07a941a4794b08e78048d061ffe4155a3d013f121ca820fdcdb0a3808457034285a6f6e863ab83ec7f0cebd636a49a0d5fd4f6cf2ee1baa4f32059c0991c78f2ede0ddefcf160ec5394295985d6c6620a31bd4643173d271e8802127bd9f14260885384d51ef37db5fe91724cc23d9462ba695016d975937ec4010bfa500324d58d313ef59d2b5091ee8e84cb29470787d36100e137016931820b30b7eb895dc9dcd0c327f1587e483d77d9cee95b811ae7b4ef18f7ed1b7eb606 false -check_ring_signature ec8ced9e7a5c2c8036248559b66458653c192f40de6ca74a727252366d225328 6afe31fa803bb07abe6ba1f80d1e35dd324b620e256647160e1177d5e3d41f45 1 7c81f2a7e5f6720b405d48cd718ed3d7978b8693638855dfaf6213bce5968d78 2f88c14a0f24650e5bb108fa61c5f8a53889640d866a6138e2af7442c5d2d804e539bf97560bfa4c3fd177d66b5fe193066510a3230bc6b85eb9a68d61ff8248 false -check_ring_signature 8c6951b93758c90e9ce9035011d13f97dd248de014eb876de0ca202d060c5ec6 24b2e62e1502e4e42f6d9a0c53e1bef2df3c16533b034c24013b9735f4637469 4 6f190ff6b57ff81b6dd027a9d80ec9dcef5eea1ad6e19013966ceb68ca044eba c43059d017dfdb72c2b4b7c699c2cb893bf0067c1fc2ba1525fa8ce7d21fa2ff fe387116e25635f7887bc65a3a40e35e974b101d2c2015d22cfb3595d27f827d 6a7d7d4cd6259c26bae7dcfa16fac377bc1f4552e138daec764731f11e03a7a9 9f9f329ff76dcec91b160414598c8c49aba58d410db81995afcd59af74b673099cef082e9ef80d37da97e8a23dfd01e050d358c4997876359bae68c2aa1eaa0249ba832c62c8d66ae4cac8fccb1a864df83abb9c5f6b7f1c2a6258084f3f5b07a92da45f0496c823cd99527ff92f00a1a895d18c63428a374f99b35928b8a80f4b3bc04cccf37cbc0cb6e283b3050feed2a3685daa69ebeaa2c3dec6d4f7490f13bfe97cd19bfd94115ccb81d2fc4f8b30da4ef529e651c405160738c9e489086b3df601a84656eef3b0d546d84a09eb84d87572c2c95b19c195a72c3571bd086eb1da4e07120f463f106b9c9583850c437c9975618c57c0f33cb5062eb08a01 false -check_ring_signature eaec95168f2bfc46cb01a12e04ed268c078814bf780001318ec48531ec068f6f 0fe919302c0e47e623caf684a22a2019846be2f8885de7d50c1e60812dbbb399 2 9ad347b1e04841e411d30390f19cc662e09ab71b378d8f933c6610a9b3ba6720 673c9aa20f1343816c719f42590610343e6d0a60c5b1135d1a106a9a7d1d527b f6194797761c353548b772294c54434cdb30eed8cdba1f79462f1a8dd93cb10887299c87f323ed1d5b4610044ac2189dc54a7b7bcaeccc6d189094983eb87c0c60d4cf33c3c1c35d7df8e77fdb06dd8f928630656c27e65595d5d775267000043aa9bef26acca6c4355087ffb6a5ac42b72a8eaabd34d789819e93bdd0d5730c true -check_ring_signature 79bcc31f71bfaf32fb9b108a2745bbc71fa22f555a9498744305c8db864ff0c1 0297aae6b9c6c9158e4cfdd64dd425023aa609129be9553ee1f111b1b441a5ce 7 375d7983243befbabf1a15f45390d5b589a6c998e3251c42af4de49f4a6238c3 9fb8c714f1bcd317844ef061c35e17479d841971de4fbddd4dcf2f4d03703dc3 ff591f422df98554055d0c27d425ccd09dfa568eb07f6ff4a50ee9d35f85692e 49507769ae1cc97b77997d26cb2bc72049a3eefb2b4c20459a9a8f694f349e14 7d7a11d904a203c51e6611547e86a5ea58de741af5b4368949508694d5086d7c ad339c71ae053178736326531e7723ddb6f3b85667e902495fa176b50d6c0b94 9281be5103837066b86d5ae5ffcd7bc41ea28a0d4192a75781e4aaf5fa9c2b24 1dfb4db13556436589714378d0101e46f65e4cde69075fc0f01763b9e0ac7203f42e5c6efdfb84ea20629890b06dcc4c02a277748a5e834050e8d95724a5370a836a8fbd8f7d3a2ec614453249f6b39bd0068aec782a95ede12dd5ba4dbe8c0091c278fb8cf53d852847eb3b852119aa84b59252dd36ec63e4714fa7b3da2b063a016320dd848cd2c4f68763a1296df3b9b71f1c7300cd85030ecc42cdd3da0951203feca16cf44e78f3b3b6eb832db2192475061d796f770c1106b20178ae0f80c80bddb8100be65d1ab1855ce210b70dc68a11d2419a288abb022c804d530f5572cba6b313fbd21a886c286195cd967b9e4a9dfe808b1cdb9fd3a5b1caec0e69cdb25b41e1cc1444accb60e1144dd2d327305453bc305f08a700039e2bb00b46e154bd460632d3b10c5c2780bea1459aa48055cb08f4006116b9af48c13806acb2e0b550c58240e896f0cb3487afb237944612c6c707f712f2f566a4559e0117afc3eacf2ca871773cd118bc4537ae342bb5266fb95f22fb635913035ff3040393e4b246db806541794ac4de29a7cbc3c751da55b16866cc41d74bde70c90810c3d4bd6146641c627e4536aa8d534b745ff38c6148df007a5b509937162d09 true -check_ring_signature 4925813e58adf23b66d992cc12c560f321e410c2d4c620bade8ab3e4dcedee2a 52cd06939e119460206d0039b19c661bc7f1ea1497ad9c0516fa08d2c4358c9d 223 2ae2349f1e8f371dd09808ce5db8bfbf2cb82ef8aad61141ff99eff7704ea1b5 8377be0c0bfe6aa093eac00359e9de7d342c67e9158cd4872a2d9cdc65d673d0 9eb857ad6b0d0565ddcd05a7c006819c09a873334c4b9e3bba89dc1c931782b1 7e54714f26789c64f1609f46a36fdea9224d34442af2e6cb70d433ae11d47e77 6f98b4ab55c1bbe3e1e530f4f53e76157011611b46e24811a356ec78bd9a0e9c 1a1d3b9915528a17b80256d1d5487a4dffaa147209a2af8ad888829491f19f9a 36c5072bf32b37d4a39cbd5caf515eda517e43c700ae001b564d978bcf6f0076 37b97740d94b6d6e9dc82ba884f1beecc1442ec37d617142a03f4202ce99d8e1 b57ac08b9d7665ab50fc0478ee58870acfb41a0cb999cbcac26d5481f926d6e0 86ba2aa1c627415861624208cd01b223228dfe6eff7825a034da165b114c13c0 40a2ec1775d577c1f2e810be43447221ec5717736e10c14c828e8fdadfa42a6e ee126a049207991ccd52dd24e3cf392794f3a560dee89e14f92f750078307768 b3058ef5033d87419fa84e44d9b84c6377a7eac75c7caf40a50c8569956762ad 3f69a2012b760d59ef754f2bb518cd2da91df49139ebc7bbd6f56475c6c7218f 7e7cd43c57198e7aa82647e26c707aa3ef2db5faf8a10c531943191b4ff26755 6ddaadd83bf81231f160b140b43b4d6d1a6eee4dbba5d34c84ef5b80bbc01118 e021e6b40b7f8e49f044a55e663f93d44d913a1af7d22c70368a011bdc336a56 728f70e4cf1b3585f987a7acc938ee7409c80bd5607ca09a9407f2ff5a3741a7 e2be9002acedd68ef355c5fee2f0e32763b5189b7a5d215e81fbe9ea4e640d3e 7e53f374f4033868c5aee94f7e2675b3721a9b0b0da81343aac02217c65590a1 3d734c62337d33dd09a4cf7e452d45f505bb6c553868713289c10b9c43d17323 d7fd0275afa71f0d7288b701e453f19a85865ec2fb06fd94e69a59a70f1ca1ca a196f505865b10d17eb3bfd88cc499102213f41ec85c8c5b520657a461f6cbe6 dcaebfa61499f7b0d5062fd7308e2837e4951479f44e5cc709c88a6f6d2086af 7992f050138c07da939ec7565cfd9f5ebac782e02d483397fabccb4517174f8e 10efbf8b1ed8b47556da3d0265c964380920cb8a04104b70344ef24ccb578c3d a44f9b9685654081a28ed49caa5cc7473a841d2ef62ff276c83de2872baf73fb 0c393162e62df7eb7ace99851b74c2cdb71122a95c725a13349bfa6ef3c5109b 043e98f793f4f2bb9daeade066b8bb9537bc3f21fecb2d71e0f847f4cf455cf7 e111759d772173f926078980e8ba46ba0ba3f6096c17c89e4d491348baab90ca e7de1ee94adbcae1e06dd9c5a8b84a14f96e93eabdcdcf2936ccaee0e6f63d68 97ccf7d148ea84aec57f512790a8690d06b3bdf4cc1bdc0c5a9166b17a4ebe34 2eff2058c3a9a685ddf1f1b69f886eea59e93e613620059454b2c1d23c4c1db9 6a09e3e688ea5124cec1222b331eefa0414fb7c64a1325737df41edddfdf4545 236a54154260db96d60ae4329d2339561dadabfba1b5442f58ca017deee834b8 61ba2112867932fbf5379225a495a10073a9440c9b084226009af2729306564d 358bb3d135215f13c139f2f186018af095f2653d5ec845d2b9917b6129e7abac b54acd8cacfbfedbbd99b3727aef8065b96ad7425400792eeb7f28d1d5c435ee bb3d6c1fbe927a4ea43c5228246399e35ab5013a8c838240a1dca59b75c58cc1 d9fc6040911806588ad5ace9bd5789391022a6a6bb9064cd1bc1d28812f8b02b f26ddeb62faf300b5b28d7d99e55195a4425dcbbf4c662bee216cfc6570f5756 ca57a4075c516254a5a3e10b233470cc87662636431a6c4d09a5e5c28f2912dc 39e73a5534983feac36778cd38dc96dc25a792b3d7971fc11e713fed6f244972 99f43f0ac981a747c2fea8b0cdb368ab3c133a7cfca2cc260c90bf451c4ffa2b 57d6b318e0b3166ac0c26a439f7e36c5520e259b2aec342fdfdb1c9dedd21d7b 1ede43d20fac37f9ce0e2c769fd7cbb4fe36d59c577524e5eac08fd1617b032a 9d4be187e2c1fafe63e5506100794af2fb67798213769fe123ee17fbdb3ab31c 1c2a83c074c120bcefa8f6847ddee70ddee80a61a558f35fb9b182f879ea8bbc cf72fc436b24dcd8f523256da0ce07bf00546bbd537fa83662a18ed852359865 d43248f73d9a6751aee68a9bdf284c39e926ea7e1c8c1f4fc23ed4606aaefba0 7816ab836ae9b03d6cda73ad339e123664e9a05b6f0275fb89309df0385284d0 fe677f401d776066099489815f2c0fc84fa6b615984e7bb9d59ba0cbc4149c3b dfb4d0dd4304d0d85fc4dbc1d0d23663f07d38733ec8a55e825f091483e68249 536f1bf58fa5d98fac1f6c8ddf16eb491bd03ec2d816de7505e861250cd9bc62 2aa1668d65348389d0bd7337f73223bb9ca04f0156832d59b81f7be1ec026acc 19f2020c0dd9fc6720c987177662c8d10cb524c385af41da5e9a8e14bd6633fa a886a6b2d34759ccda0c51bb802724782eb4c41ac2ca2abb799c5ac24b441e62 caffa3567ba2c79d422ed640c77dedb516cfb28e03a427c44354dce64233740a 56fded4f4fe9daecf67f1634fbaaa518c48408d8116dbc46c52075a03c46669e 297e4aec2f6cfb949e4010f23e851f5eb6c1f053c6f727e67526dde593b6edd4 5032ef548a8aa318a3c3dddb231756559da3e675347457b72adf29f389c9c352 e377bb8b5dc5b709b5756406ce4fc901edceb93739ea2459625bd83246d5ef45 1b023170c98ab00c2227fdbb69f88888dd2d1d3355483f31a6b9fb51d5aeff16 bf138e0b9f3eaa97cf0a634871dd8d763ba9907f1b56da397a8e753c63fdc56f 7b7ddc7b8b0319e4fb9cdd9e76d1e9e062b7624c4e5897692f24b968ee6db833 65711c05b3436c22e252c2c656de627915e372f7383f3dc72348a891d3d9b536 4ad145c6d742ff07f55d06770b86d1f5bf6645c7b1da87ad07e93a9b5d199a0e 43c8de46329b2d5fdf076476cc82247b1bf886e61f8daafc02bf754fa0b5a8b2 f2cd625da66a244ce94e95a6401c6a0192923c5290ded031eae4730708f21b8d 0835211a0ce0da370fc0019c160c11f59c5572d79d880259d6935197062b5812 24e6b3ccc232e97d3f8b14c4b43885111c6fe0053a121fd560ac054e5eed9dc1 c7a29ad1892f54b3409a3db17507027bcb407f1b47478233a18b0b7951ed8b4b da606fee35f1adb86cd387e38a409a75b010458e12433c6590ae1070f70eb758 0963d20b2589eae63e2905f06afe95408a020e01f8107abc4b424473ebf159db aa6a834f577d43d012d0b9add3bf5dd804f0f3a10e929169006f3707f24af39e 19c442fc9e4b868c112665a1f842545dcec023d76cffbdfde83afe77151550f9 966a7154bb887741e0f26f877453412827138b5fef595b79cc1555f3a58bbc29 1fc3bd3958b9f8387550119690738b2e54c006ce553ee8495c14faed6b72ee56 076ea415369aef990c1fa820bd1c07c2aecf534f90c748e81c7f2f73c588ff54 a0e63501eced2c9a65c1efb9052d0a99a4c93cd388d1731091a1102c9e34d0d9 d040542dc1b485f5ae5f8089e4d922fe1d68db7a1504c49d355cc4730159c6b5 326c94eac57fed1059d7be8251536074252066ad68dfa30970fac1e57b21805d ac8d374d14ef7f844d70b19287c8b3eb2647ae886c6e226081ab47c766798efb 5a1500e825995899a4c8e7e7a096a70f848fd179a57d4bd87f744c9d53be5c11 88134e9f29f58ba0d1e0038be5070032406c727edf7c87f4ee7e39ac32975bb1 1c840347fd8ee4f5de266e13ccffac3ffb83747ac369233d7ea216b17635b87a 99d5fc60c57a599830b2a8aebc850eeba0780831ab3de550ce8c7a1654345938 efcb8855b214096ca03635ee1457a4c731fc75a63684edba39cefe07da84f33e 9a15ee8172afe6141dcf316aeda37f26811279d01b0709a6d40deb124c571b1a 223ed097d97b048e3fc2ba3870e01f1187b1111dd07333591f88b56fac9a0988 4a71c763e741ad7c515a75077ce8b93f568c3575fcb6c8c0de9672156d753cba e7dbff8abe0593217c68bad73f1522d98b9945309906ccf00895008840eb6274 99e4523e782cbc85495d2cfe2d5ff91d52e1a97650906db3f2bb633fa356036f e9e0e949cbafaf0245a060a968022cc05d95144f77dbf305d834b4864333cf48 9a400d59c7e2046df39b1297a4af16355e9d0a0983d7aa1daca39cb983dda9a0 95460e203aae91421efb9fbfb74be4fc2b2520b33f9f441b0edb966758f188c4 6d995ab6bfbe2ca268f709246791bc7b3fc487c4fb6034e391de71bbebc8b6d1 b970986d229711bbd8ddddda16059f1e5ba2b99a64f9f24ee7e60c66e10a6f27 330b53652b394612c23a339e9eb2b7abaa340a8f86a9d4806882625ffce3654d 983dcb6bfd59e0ce69a96df9cbf02d2737a965f609566cf98a8e5ebdd606b836 5f9c4fee3523c486bba533c77ad7769e5b5d508691ae7c366b7cc34da030bad6 e932af65bde971fad3ed0769e00502a10025daae4d162465607dc77a98741515 a28c638da3fa656819556be0a4b49ca5f26a4393aec1859f6b1540f52ed24142 a30fb73106c9e07e881858ebc7e4e0104f626da8580c2cc4fcf69d3c4c78f704 6cfbd0a904dc955d0e7ac6a37f2d10cde76c3a8b5e792f088ac5edff28bc1a04 2c308bd9005547e722fb2e4f7b5c580df3578285dd47e4ad23fade2e614d92f5 2a7eb873a7b428e507b00a97e576ec58be62802aef21ea3c511837b64662532b e1d8e813d7f9ca0241afd84797bfbca16de95d1dd3620dea63cd803bd23e2c85 9db661599fae25bc3d516bf86af80d2baeb21b1122df6347dadc8ce1d963ccc5 1314e87c099e0af697ce547ca60edb9b148f52e305c678ba3974a76845608b79 cca918f2ba22b6cf71644be6f5b383c411b4bb94457978d2c51517804dc67bc1 272ed04b547fba0bf1017caebb525847e31bb6aab4327815830c8d91ea99f495 88c1792efaba0b3620822a1bd9e34d759c74c32442ec74a88ed7e6a8b0d93e69 dc613e6ae4df2068eba2da52aca51e0ed65f15450b32568afdb2d2eb1666a63b 7282e9a3f7ed63d80133908341e57b94863f079229b7e2241af839f55ef987a7 7c51cb827e610d52117ac38fbe884d07fc3bdd7f765edc1d19c204733bd7e68f ac43dd024457c739370439483ebe067dd4743047f9a2626d3a45969c0d09e4bc b089165e69aac5c20f4cffa27abc37199b2539a1e22e4ea950803879019d9f95 9b1583e11af3e97f85e5bbe9a3b2685ac424d259dfcdad36895635f620d7a879 87c469be9027a14e1e5c99ff210ecb8277d98cc1b83f613c70a2f45127cf2797 6762a1b09d224c31aab631f59e91c0f0f363817aafe6af13c3b34f678e6b8b7a abda8b9ad0c3a239f964dbe03756f8f7a1c918566cb6661727df60688ed05c06 72ba5a4f54f66aa8c79d77626728b4783d93943753ec14bfac8b0da337c32d6c 80b6b67f8b610d58abc67b2e45f4d428952a0fb81afd53839ebaf51fddb0acce 74164914fed99e09197ea6a4672ca5cd57dd24aaee78cedcd73fc340dc102747 09b969d05354f0412cdb458d3e298f100bf64feac0a1c16c35b1e4ce2fc585f2 7bf2422ae3881ac818e8eeb06e5a130f5384bddcba21adf5995c0cc7a806b83b 550e4bd8be7a08cd31d666ca683b4b11f35878cd9b014230a788156b717783f0 abc2d8c55184cca7c8af6611e675e225ba8335d63272a1cc82cd8a2d1f25c879 ff2c749fd4da08fdbaf0cea2e3322d2d0a2b3aeda0fd873e929d7c917198799b 194b32c697046199150f524fd972f8d7b47de389b632355fa7027060f27f952d a41fac2c6e6d8c1ea5b2c1c57e05f4e440d9cbe126473fd366af4cfe6d1cc8b4 75071502a7a3570fa3f7c3e0208dd8fe0fc697267bb6f3ee4a8a56e3ca45aea4 c9116b665cd7d4d466a857286e9db1ceca064cf933f13a6f7e02be2283cc96a2 54d09f623b3371f524f75e0486bef47354434a74b94620f1cc47ad856b73f067 895e8e65ac2dad8d1d64bd9eb90cf7165fd882a2d8c395d8a777a6e26f2c352f ee470b87ff43451b2360ca01a23cab0ad890132a24853a401e4eff70d3d49e4c 37785535b1c5966c748aa2df6d5060ab776d88999a20acfa3150692e2554a2aa 496198930a1423c58b4c11959ffc118edb7eab3fcf9b5351217f5f19825c9a05 50d3ed17dc8ed223086f1cd9a37e1b89a3acffa528a98ee31cc12c1a35e2c6ef d534a5c30ea86f05ec2b2c07c6dafe2ff20086e5e550fe62aa7d35218bfe68eb aec93dda5b1bdf14131976e5a97a808a8802b68e40588f3723f5d9bcf9b889f3 c41cdfe43a52039cb24cbac6fe1e86489a637e39586f604134d57c64037f1136 a446e2da2fe056043ad71ef5e17a4c3bdde0e533a170a3449a293354424b4a72 c469814eea2158326c1a98451475d1df741a7a28c49ea1be2dfc8683f436dd18 e35ca754c8c4c2d4d7c4846f9d3222e50620028c168375ed4ad9f77678aada98 5e186e80ba56fb5f62c9755e1461f45ef46ba0ee8d4db59845c8876466a05355 9feffdc11c880871b4506f84a5fcd9c8cf84012bd2671f14cf34e26732edca0f 7864819c00fae356784b4bad8bab79e1d0544ea8dbdf7c588da150190da6d118 e18f578a9e56bcdc8f0886cdf06da000e4319e3fc4fd78243b9f6d384591b1f5 79923d8b5674bb5e33325bd2168a6deabe1451e81abea48d6431595d101344b9 73cb2de48607767835b4e70df88008a0f8c0ea6deabf9507ac6cce6e1fe11647 9da1ef4774b0b9ba1d6e2c93654f4fd811dc6e025de1d3e69c839b377846f01b ac076bdcf318d0f2e5cabc8b405c399010d1de5929fab7e1c43588d859d47819 6705355320b63252f8fd22a5cf1536c13c57f4690d24b8594fd1c1dd94ec92a9 f169c24022cb8492672c090b3019b18d66aaa623f60584360bcb201bdaf38ccc d0a41379c2031a89fde2c1e22dd4798d11c9b3744671b0006fb22c4bf533ee6e efd563590b7c48f1ef61dc398288225d8d87b091cd23b6315ecb9ad46ee3eed0 017e3324e1be05cb6bc3ffb6d021d0ae025a1ba34918d346c4690bddda65ebbf 8afb72183e28a105756a665edeba8a2b1efebd800605b365852e5254594419c5 27da7c07fb5cd331e785a03184a8aa2f6bb5e082a10072afc3c0d990c99c292a 1a6df3070fe83c3e0130b686b9c7af64fb08bff33b067e80aaf228d21f8e9e8d 28a01d55b2aec1d5c24d4ba05861925fff3f11e75c6e02303797af14df752145 857055d79792a281c125747b1593c98860793200ec2f193d1ebb332066515947 cd40303673936c9311085ff8100241d9f24bb52adf1bcbe67fac3f303da5f1c8 ff046082abe3af7790e17967627c0255bcb39440c4431a080b7bb318bcc8eae8 56de252d31bbdd0871c97ac046e755ef71f632206f329aaaf39039b24085dedb 48ba0f66a4ac1678dda036704b160ea6598831d00402851b9db7a4b2bdd270bf afb69a03d6608ce9d7041a3c053ecd5e22e12114a94059f7676c6cbc5c5d5143 66576252e688547af4fc340c8a5353a482c82531b9b54ea13c39a4e8e4dbab0b 84d7a36383253410a41ab0ed97ea387ba746bd711d776f16054db1e02ddcb9e9 2025c34b2a3c40ade46142403db4c61dd657275d28ab789ebf2073e7074cda0b 735805b1b92d6fedabec7237731ff9e5023809e575c32efccd07082bd7f91598 94b0908113889cd58df4e39f78d2e87ebdc2b48b2d022f5905986546d6cdc1e8 9a4f0d3d81bde0dc6ae96146a5fb9f1783c934222d6a9b7cd5096ecb5e640421 61257098404ce32b3bf3885bb17efaa4fecf42958c3c9ff2d9f41a166645f749 cb8c5d1c7a276270b94f6d81cf85e2776cdac1fc911ae7fa234959b1dfb5c02a 9de4f554edffff9287dc84e5de01299281865b00f46c3614f402e1ee83dad884 35f79d30bc8a5617170f96dd704385f91af0967175df231a749473e752477300 4cee2706e566d86595446ae7bb0d47286cee19d00be2b8e8e29c3322fcb7c379 f8636608dc16a5c0c27937bb1c88de8e03e322b0e70233b8a442ec07ba5b1f2f 18c3a1bebfc7fc350d4bb08f189b7c5c3d16109083fd6f0e80b2327b2223f2a7 46c61cbf074fb09fc6fab281550b5ecc628efe859240fb7513565c9d6f1a79d9 1f89f57ae54b17d0d00a46e87a521e9d0a2f6fd35d4e0cb845fa90a5482c7264 11b0a065a5ebdf30162e034625000c7f534893a8fc365d78b1065137489e52e8 c3dca1c5dd70d5832d7599844fd4a5d0aeb0efb52666064724aff12edd3bd783 43ee809371d337102afbb539da5ac83b0023619da4a079935e385c9f81f39b49 a0b979ad514defe78d3dee63917a1b063fe2d0fdaa15a5161ef7a6962afafbf3 d5f6445884ed55b72f17eaadee864746a17df29bf16ea32a4606b5829511b80c 1a862a47f2002c41f5978a4fab517a4424e02a1f54ff988227f9f8e20c57cdac 0bae20d26a4285c2c5fdbf37627fbf1b033daaf9513fac9c31b1a33b95dbc2a4 85a1450071d78fdfa8d79dd593298eea7606496fe350291163800ae167778f24 1a5e5379fe4c621c21d43de42799ee8483d5a8433e317e2634de268388eee063 ddb017b08ad40ed5dab8d58f241de56973732cddf0ed5c8ca521e2ba0572a9e5 e1b750a7775d93b261506e89eaa0e0edaecd154ca92b0a874e2a2d5b8bd80d05 55b481c2b99c415e4ef662fe8226e2b82cfd16c0c3d29121515cc4f54096f996 8753432dd68b0a4a4cd048b7bb04b97bf0ec5004af010881126667eeb97b155d 1de90455d397177d8cf8af79b82e90c692826ca2704756a35edf3d2a9e856a0b 6f4bf7025e80db4fb13aeca40a7bf5b98903d734cb62fbfa080bff159413e849 ad0d333ae8f03e77ab00b82199dd3ea3362957c1b4c9e8f8c55465b92a8e4c5f a62f0cebdbb2bbfa3457a919b2af1f8cbdcc47006e32fabbc58c33647bb60052 64544482523a490a0c186ad68759d7a4f81f5a34e3867a8ab01e97161856ddd2 bb7b4351d626a310842a98db927c78756c7e3528d6ead47dd0a53e9b3cb5608a 8511c77c47b9c8236f301f42b755d86058fe027013c89d953b59629f9cbeb657 d4573b1523ccc8055c3f52e1682b20c5249ef8b3c95ecbb5b9cb5998ed436c0c b90b09fbb21e59fefd27502caf1c252f5857d08b896fa718f536038f259747f0 423d81f2a66d9c80435c78ed7139b2e29de138772b466e6821cc7b53c9332af3 8f23932a147965a329b9fb612d87da528aa3547892797221a97c67d71ade0235 5d6b1941e5806b0e22a5e757def5c851e1929961ab0a95e442af653991387721 4710f9eaa13b2fb146942ac301b35ab39b459ffda925acc8837f2818d47037a2 15640c1c67985fa7745bef3e6e5cb49797b4ad3a7b9c1bd450ab6b5f9a6ed0bf 44ac379618a6cca7ae90fec328161fd89c322ad819032b904d1108f27fd037fa e0836cfbea800eea1091e19248853a5af4762f7cdb43bb51d18e4e815eda18be 7efa686291aa701031a5fbb76bf012278f0d0eef4f071909693f257cff52903d 8b8b33fe249adc3835a749f584a15f0731075570442dec515930f9e7f4c48d65 bf0889044e9819ab02976b71ab8cbf7b6c1df9a3afad53c3a2a12220ffd7157e 902b0b45e02db09ea2cfbec810b6eaed44bcbb0ba1cb0f2c925ede90ee5c9210 0ad9f5d544421badc1eb602f5cbcb19309a877a25977afa8bd64d9cd5ec42274 3276d8979e4ebd1a1c199d169a05c9bd911b4ac260821855d8ce7e8fae1065b9 2b5f5d8aecfa7a13cf7c49dd3698fb150c7ecacb1cf023a5385c8ec345547248 87b6ceaef5aee5bd6c1eaf534c09ffab957e7014ec9b54239ef23ce9c23aed48 8af9eaffb025d7d2b555240c869bfc9a854d0a260d5aa0686ba075029296600e ae4a7a3ac36a5159e14807c0aa486da94dceddb2fc79a3a15e6c131130190e79 fe827626a6af3f0f3dff3abec54b894ed9d8e21c9f8387337252feb0c52ddf078e2ab3ffb8b1e23d343ddcb3b4e78e61dc1285ef9110183aa15aceaa51e61209eaba3baabf50c766c8da2094fadc6b2c628f19b22d05f2f5b643b2a3f74b590921883ffc97dfd781077f1c6b94f2feac5642d11f5a03aec1b4e036a417231b0eb43808cf7093b8ba69c6549806d5387c08c2366e000d88f12f5930c9e93e5d0eddbb319a412aa6d2a3b2a2bd818a3cdbaeb90d73ccc09aa514fbe9553a9a520b7c6c64a97bc134056eee4743ce2038314df67ccaed8b3fe020a2be051075480239680cee12387cb20734acc902a95b57b55fd3b040bfb8fbde5c8d0a58a2be03f90246bef8e6fab148c4f85c8841a478da560305afc634a5112b7f1cc5ad1c09aa2826d0c2d2b818d6d293e7379adf98b96332db72c9e53b97e735825b1feb0b8a5f84e6548830e184157a79363fbfecfdf76f07e42fb6837807a7bd37e7d80b8164d5f7a5dcd5e7dde546e91521d41818bed510044583eb7520b9d60ef9c30ca15b42ee53518cd25f79fcb2613a63fc53dda6d227c51d1e7b2156cb37d884059c23d6e74994ec326cc832b5a7150e9d5199d582468c8b70f50accaf1ca0e7036a8b8538764f5985eb2248f922b3fda681a43bd8fd49fbe7d38b1cb18a5d820b97d5385b0ab9894004acd0a6caad415d6f18729ef948bcdc087e34ad6dbafd00de59b1720b9463804395041cde398db1d1bbdb415b7f087768bf20c4e54b910bbe6442ac6ed1e746658858cbfa9c666a54b3aa8eb404e713cda14c30edbbeb093ece1bb9e68212262d94088b746fdc84027bc139347d1e93a1caf729e3c2b002655ad1c6b3afb7442abfcd2f8dfd92be76718513f23fef48bc5224e2bdb6d90abf18a49dff40bfb2620a4456b219bee9f27923b1cf353e88e894031715d4c30d4b37949943d6e8706e29b6bdd0925c48bb34c50aec47f59c1086a0ae0cea84039e973d4b40a021632a458b34eff6acc8ac4b7276f1264ad63f95957bffe5710da5e087fa4dbe5c7c591e6f4ca23d7c32f038be9ad7f84d1ea1529765e4b34e0cebbae79dd3bfd685c18e2958cdf6155cc930f0d2d68f064b3db19ca90200ed059d298797d00925f6e360fbced085680244e951fe8ac2ddb830fa677cd219a50e2bd44410713c779fdd9b6c0c4fc3ba960061ebe03e0dbc3e64dcadeaa63d5c030997138a58244c19a6af1c8337384cde58d41b434e36cc9610f0b4474a29910d50f1d3971fe763284d4233b6f683b973a99fa866dba35f79450a8f31517095082a80b32a8d9afc775aaf953c76684c1d4b21bc97d2ad7c29056025984b5461031c10b019a499ef9d3fbaa765ca97e63186a65664cfa27bbd4f98898100b55f0378b54ca0a6ff5551dade04c800cdb195e135301aac033534015c4e2ac2f23e0c75129602307366044a0fd454b290591a5e2ec45e21dcb3c97ce22485b2209305b75f8f4fc515a6826835fb97f6ce768e79f00fcbeb7c43bbdfb35ddf1479080ebe5cd12586c1c62fd53529c732867eeb3fef8540dc89b01d4b96c341ec79e700f8e6c86cb81c530ccf2ec9cd2872896f58bf24cf4912115cb565aeb231c9b2019964e22618f2eaf611a33c9b67ceaeb2578f5130a4e8f839a42b229c028cfe0e34fe774c7dc2dce8d69d6bb40c746b947933236f97d2d036a6c31258e3920c0fdb231fb506e79555d2e8d370332430a5862a2cc570a49b2dd9df3ad0c8dbd10e7c358849b493e7a8c9d38c92ffb2f4705ab9a4c4775a8445f0e7c642b30af40c0bd1a0e077a684fa9e8f435bc7a0e0fe1bfded976fe0edb840f3de9b10a22d0282f1425095a13460353e4995747e8057dd70a2de571f9c0130a7bda3918d650a4e8013116d46efcafb3d38098fc08d19f5bbe54322f95b62d2e80ac1a1d4200b395a6ec0751db06b5dd5f71bd4d8056ec3f46c74270681998b66015ab8f9170b08f8c83840da91767f1743db2fcb51c6225aa39165b047e5f56a88925911a2057293178336be907edc52567f139c4d92328289414d03ba1bffd38f1cf624560889bfdb40b12047e1b8cda3f495c8878b2f8ea4372d5cf83ae7b534bacd531209a37181e8fe34cb625547637c09157f252bbd0ffc00d3c0bd59c075ce532fbb048d53d8a734d583b33cf627b8f53d0992968c985250ed75841878285946d752068e5f5eefcfae50bbb6bba43acd94033badfcccb2d1dee9c988d0def561a59b0483f86db47808ec1ba4ca65f19e457866d88f2f0db75fcb2842f006724d022a0e34308d417e7cc3dbf2265313a4249d2ba367153325fc2498d0c846785b4c5106e97b1713d4442ede468b257ad92766de5f4213b1ac927c995b4661fe7e1666065560dd0a708f0c3adefbc0d7d72bc88ca5c8754edbc3c02e2f8b2c5603dd2601d45ec866a40310cac65c87c135c28b9c3fbe439ea5fadcad810f0a72856b6308432705aca785568208387ff5eba8ef28292cd9ba39d73ac74df88aaa80f4af095c70ae6834637cebc7c7e46f01a591f9ca86be8446f041fcf0a792fdd3c83905dc13f17800931689701378a2d19fb522ea652e46146ca5a9d20c35fc9abd42024b783da29bd848f92c9ea068b3e6cd7d86f0533ecfadb888d936df58aa16a50cfae3e924de197406d73ee1b88bd6bb18e861df339d9cfb8113efc842661b79055f46f42ee0efc61d5a07af947687b4d2796694f78105e16364909fef6db8b10dd8efb374e03aa18e85e70f9f1a1ce4c21676bcd030c8b67d716878be8749a7054cb9190ba117dd97fb4732289a75dfd1bfa767ea044c0fdb8784e7992326140c3381a24e45ada4ec7e2d3a496e562a47524b08c462bd14e7a6acc3c6ace2a9097a6e7fd13f2e9241e70bbb49a4ccd7cb857b5d090bc736346777edec8de6010ee3e18ec7a8802d3b1cfad845c9ce67965bd38c05a121730a5f5be598e05d19070fec04222e382fb5685480f061e98bc54f08914582af3388b0ef14a0b1b9a90564b705b57420b044202ac14aa6a0be9b53096b6acbeb9ffff1ef930ac418d7079e044b9e98a6a2ddc46c240b5a95e7c0d6b039b0bc622dbf9e5960102c9bb90f18c20f3669f94261dc4e6a2571225ab187625e408ea5fca0f5d26683067eba00066ac7ab1e4406f21e21bb66c34a57b424f8c600a7a84853e07067e9d33359093b29484554d272e4857d2426598b43efddae5c651dfe7ea918f6a4d9b199fd0975a53b9a15009f5b836bd485ae9379fa8c3ec76beb2f3a1f065074321ceda101c4d5198d220cc7d26f8c25c1cd1b1282d39fce12b15c721b22aab9be64f2d20f1df9e15d5d4e5f7aec4bb5807e03c2207cb96283f800865af6c86ed9f980cf0c087fbc4f71abc694c469090a80677494c15073ca783cac50de8b1d0aa523060cf75e152ab5d428b790fdb44d0bdd40ad9ae9faf660df0ac1c64fadc4b4343904e6f2b684ac6ebe357fb48da8f2e4abd376f4d3736202b2905f72da9b2b633f0144e1c933cdc8a5094d5ced0c66884c634cdb95e65492992687b24703a0de2200aae635e9362b05d5b8a0c1a3fdb5d5872d104df858079dd786895166c71ee40ccdb0abe42f9cd90ba1154ddeee8afc8c5520be7e1fd32efae3259c776d82950ea5aab5469dd85a33461cdb62545483802ecee0f4a57719c6484393b3f7017b057ce47473da5f9e61a52a036f1dae17e4036692f85a9e44cdcc6505c84db87e090cb020f737e67618b270953b11ec9f3c2da0ca44560d86011429915c221f0f01c919806e1a52b4dccee74787799eb68436f2b6cdfcfcc650730e7b13f5b9970f1ebe40437ce3917fe3da29c2aef1fae50732a97a965258440343c65dcf396e00c2fa09294678d01ca59eaf90732e62bfc2bee64bdf04a36c97f255e2411b6a05babd82ac2e9c153db2deaeaf8c832e4b676aeb527b2c56af8bd31cf4b44f91025bde006beb6d30dd2edc34063308cc867bc64b5fd8815f25addede19fb9eda034262cec8f9baf95a86da5a7a6ee1a50e28fab1fb35aa8c49d5fb36f97ec154077350628b1ba5969ad4cf9cdab26068cfc7cb486c505f63bfe16c9334b727a9094f1ae3b9550fb54449923bf90d9ea1ce775abcb52720354e6129262fadbcd800ea0d201414a672dddb227776a9b81e6b2eea75fd09b22a52ffe18b4da0b1b70c1696b2e505228d1548336218528317edad1713ed57a39bb4a3bd9a446fa4b20a070e83953f4cc49604777658282c82d7fba1afe9517993508dbc8ac1e7aeed0518d143dee531b2ac0e5d92c338f10ce8829dcc452c39e0f4314459d8244eaf0bdeef0ae96dac9232304013de3038214c75913a20ae7d2349c8680a4a0e358000b62bc2f89d6e784f5ea6991ee5b30d0e9deaeea9a9c07fac871cd6995759f7059d92b32f8349ae3ac81a5c0be72954dfef24d42eca3f2b4fb3d593481fbd5f0c41805378b85b00bad6bc0ebe549f2bf8b5475a00505187f93fc999efe2649906183644441c7da82e94c21f0b21f32ec1074f17be2c748a88372303b12db07104b73a6e6febfebf276d64ff3c7d8e98a148db0cbf590e31d1fd570b082315ec0555c2f4a4938f41146d0e910d2a711c1e032da009515b6a3a3f191bdfa9666006352dde4f251f9e53bf16abdd74dff11e3d8b99d651145b6127058a788b240900f0ff23b480f298071328af39af39dc353d42fea64401eae8255d28a8981a71051739248d6424429618e5a6f78d1fba638da2d64c4a7296498f2c0bc37b857109d7a49c1f4d2a6d3e0debdec617aabfa5f5a5888b5ba23e0d44b50252a415b9035b341495bee238d54ca3dc29fc7ab2e729d2e034eb087d56628c547fdcd55605e7a1aea7546617f4a4375a2dd77c20422bf12cc18e03c276a085e051c6aec806bf312b5627b75ef34343a36b673b4e0288212e3b640fb395045148809e17d9064b49a93b0c75a652404eaf496be20d6289af76ad3c04946643960ea52563e901ea1de83718f0547d446b71d1858732bfac5a3f1461d893ae40a8b43ed89f3d0618de020a9909881956f1560d6b5b44d256a96439520ffcc8512bac5426e6c90d853918f9adc4a472cc24e677c46cddd719b685b773972eb3901e8f8c2ce5a40d01424ab165fe950688cfc74097f6d4b7c2de322f2726bb2f09fb6fe1a95d9406233f9b3801e45e82221bfd480ac17f4e4fa505a7165d2a78d682494a5e07710c4a70bb39e255637833df995b371f7811533ec5406233a1ad027d82853c070100f6447def46244e68aa608b69a9c2f9a270f2401a070a001f041fe50757846a02143d373c4e9a1bd365d3425588b9dd7a291e36a8413e471395ce7d32f087230e79142d4fe65d5a06e225742affa1b5c8fc3255615acb01b0e826e5fc9df5890920fa54f9776eb0138d5b46655418792535a80f98a9ae5a92156ea86b61537903735d742bcb8637e1e90b38a33f3c0af597b30d11f9589332b7f216d7ae82930ea9ba67d7775aa1ff481ee1cf9376630314b7fa39e12ed27abd08d08ab899fe050d6a89f8564dd6f1dbc74e272b81ad5cf773d213ca0c4cfa37c33fe6efa24408eeb9b70710bacbf2faba7f8e9e16b6aa6de551e3203f067e167b137b47df1101e7f8f7cc7b03a094abe8f6a2756f5c1b4ccf455861bf6aa6802f32b5fc88be0d138caf6859880437f9a6c63c7d7549ba12bf7f7dc06ce07a1bdc0d00a802c6094108c50968bd4ea47e204f8d26a5d6164e264a46cdf1f87e32c0504b613b620affbe27cfa3fe25ce564837300ce7aa9e8fca783e31e8cca29d30c2182c3fa404cfa18fc77f7f4493b1302625b8ceb6f4fc41f33ea5870035dce231120402ea07317f0a3ba754093247ec13a3865e9e24785ec1b0e82fc1b3f6de2bca9838cb0c73f2dc12b733d38c197b3654a8d266823591136e678881a5927afb4657c7b804f7a4aec79f136f83e795b8accfa3304c8a6493b0d02913f607aac517ee5d130606322f03ceb3035e66ccfdd134799eaf9c7987d5e5433ee09ad90111bbc8020aa64bb1b07a122c73fd81109cc5fd041bfda87c6612679ede107d8f27ea108709ad443667f348ebec3f2ed2189af069cccb8b19e4de8eefd549c41a649b3b950b80c598e2ae6e18c18dbc52d453cd1bde7de28cc478859c6fc2cac5bcf227500319e7a0fefda5b199b3165477d7eb0ff3293656240d7da4b9be2aa79a16e04007c43ebcf16bd4e2350d0955dd99662da7338729bd772fb3a774950ef68bc58e071a03666bdd7a608ffa8ef8437c00ff7a12fd52655ffde4e2ba334e1541c65d07f2e44f2dc576ebbc123f749b54c5876434ffde07de4928132867d64f0e6254030db8900b16472996211d37ff5caa34cb630b2af050b4d32976e78a8348b1ae00f9ad8b98ef5bbbd19c9e1ece79b0065b7869d4814aacb22a4f2b051c7af7b80996dc9b4ec5aa86fdef7fd328bbd4c40200d9be4a81d0a26b077eb325a626140159dc505208838f8fa9740e7589dd00385a129ed754a60f1a7460b0475cdd230a6427d40d01cf7aed1d50646d027d500d7c8da8a9da5be6d71528c60e85ffd1027696c743ebdd78a7c0a7e215af1de57d18702a7cbe97653f4a6968d2de36820fb858f4230cfc6fbf886e0e725863e25966a1b554d1563a1fd377fed462c7e40add29bb9ab205369950a9d3a164901219696691706ba0ee3c9eabb0cc77b67708a158ac38f7896f60fa68d00bc7b13262ec7414bbe2df41af6a979e22b42dc2000420ff21e58f5c7cd4b4fddfeceb9666e43f62749a3b59addfe6ad9a76f73d03ceaf370ea6a9295ac641e122b890209a7dfdde3ae6cf625695e6405f32327106bcabe43c1bebf00020d25790e1e9c4393d94eddf8e1ea0a855b259a8ba925502aa765ae5167adc37018dee7659dd32b34cf1ebd4e3433b03b7a27e325daed4067191386ac8900d45d1ca3152fbd69276664b9c3432fe7e3ef2f9fe3319915804886e545d0584aab93c2f38ef2d0bdf76f5569ee6151803fb8246cb7ea14a0d01781d625becec0ec686a3a446e8b966b4168d1dc13529ca19e77d3b2049ef9604c57104e5a5743b704a7579d886ae29e7b2ebe93bf1e581ac66e7f34b29870a003bca5dc17b33c83b5f45d4458913ea6679b740eb945743b62a7bee253c5ea307f052d88d5644b0dc2a7b399f4bf41ddbe18bc64c7f480e67241e05f95634c70746926d1cda61d9d5a8be2f6292bc9ef67fb952cf755c549a433af687891e3c061bd06fec017246db4d0741cc03c3050ff7e615d18ba28e0d24e55e7d5c39d607f0a681341f20a5acc952571bf92a11641629f9fb05f85732968672f6aeedca07336aab0fc936e500e189732e08aaebe749a2d9b98a346535a919ce36ada8760f0934215a6af4aadf503b73941c86fc4a9355de050000d7904e8f32991d973802ff8306b42b9f445cb1db53aea061cd5ec969268a91a29526c0d9e6ad5e5cc20d23b32cfe034be6b87fd08c0bb7f164f6e76aa583a7588052f6c975ad4570f60b750a610a65c204a2ef532e88ac87b6e7c1b156e924b00e22251cb04961846200a0897404209091af7b9d47daf561d176c1754a491be15c2d8d6514f89d93dc0ca069b798663b7c9507c8efd8a725ad2100e1c6209b2f479466840bda266cbe0e5fdf2b21679063847e80b9a3aabb6abd5285c109dc4c68bd3f68b450f2a4ca08f7b31dbea75edef90d29b7cd0b164ef72c90158e7d133f331524c331c8a8e2024b44c12fecfbd776caacabc6d06c29218a64473b782a3ca4d51ed31fe647f6020e7c916f1c0d772701f83ff7c8d4252d4a64a4524a2e98210e755d260d07cd06506158f724af3ba991b361c515863cad56da7fcf5b533dc50c4c64d9ac56d50dec68b5d02d99038ac1901420666970b9fbee15ce019f1f1a2075f467d2be07041a4a0ab7549c50b786391e5b596ed271db51139a1b5ce65caaa002afaa364101dfa83786c33b6fb91bfb22c2c46b1dec653a5998778fe827d72ed1873bb7940ed4456118518ac3013365f6b04d2b1e44a8ec55b3c9d8a8884432a0ec6d9cf5032dcbba712cb0c69bff758df2466e24774c477cce73d65f9d1dc931bc136f630ebb67013484541e85d7a080e1780604e9decb260f87fec2a2412dd06dd23f480a76b6eca14015886a40e7bd37262e3232ee2483d64d5bd504ae6c05573ed73d084b4cfba70cb1b1ff4597b27bf11aa6c4ffe32bc9de8e2232df7cde5fc0c04b00ccf93d04288c4736ed02e78985ef6e22f598a5580fab5156d3d14c440a87fc0e4eca21a78f9bd04ff4625c273f6718907ccb0e7d2f5b5173f0a9723167fb210de6271444b6bfa7b8ce733b8debbae88fd694be3c6f03431ecd58d5e96f5db70036d217fe7dcc1102c1ccce529585a1eb256a3c120b90f84964e3093f7809400025ba50a2b43210d00b0c221b33b1d93c519b3c175f4526a643c0a2b842977607ba79274533af030d325eed5e2c8c377a26b8a24a09f2d9f139797206a99f700c2631c66745e2322f4d396784831b12dc8b1dd1c250bf81bdcb18f692bb9eee06ac1ba336a77581eece57228c7b2e10eae89010995d56e493982de1e3314b52051d334fe78e364f175514d92723dc92dd5b813d2a47e7e5d2c6bbfe805b7719fd5e2b6ba9bc088667973a510e68115239419d819cb3ee3c688571b685b768280b8040b37a0b41e865fd9cc3d7fb3b3566cdaded9594b510a4dac5d592278a1904d7e1886b10a870afcfe6f05546c63ca42a31d757f320a24fd35894202edcf105b82d14ba41c9f50d518245bdff93388725f15ee11fdd9ed1e8e9644d055c3a081952d933d2deb907cad5ae50faf670d794dfebda4d088ac6ec58e402a7f6660c4a2e053ad9162a1d6d6de8a48719fbb5648d7dd3dbef4b3bca041b04d10bb30c8f41a284674332c160d5e3bd9c40d3ce94b0424d17d873a483d630ad858ab1064b9471cc4de2ad5b24b158d8afa0e4b12f49f9064f1e45289ef103aaa08dcd054436a9b1089870b25377e3444dafc2c1218a47308eeb4e30ca3c6b4f5a2ec409583ab7611f67ce4e66fb57dc72849a384a6c3cbd03adf3b4087ca2874d0c700e1c6dafb622e59e8388b47340ff56480b6a18fa6d11c47a61ee0b18904b32950960f9db26135419229dfdd8a08be7dcaacd1d021fd6fe75db2de836d15a526c009d5af4898f09c3da28c348936af38f8f4330a312cad2337ab872d40b5f08ed0a1c90aa35a9f1f81e461439a435e793811cbe1fdb59d47763c4c8ddec2935e006abdaa157a824e1bf167bf03360397a527c82e4ed3896603339999ecbc1f71904b2f6358e01e64f0c24d1666694f7f0e07c99fb4e2860361ba20dd810cbc0d804185534fb94f7d316270e8bce19c03d40165bf3b2b820c381792d7bb68fcb00002c0ba6629ee5ea95e9a572985183e3c6032ef60d10f96a9547794b3506ae3b00fb445a43d1f33460b2d26ad6c32b4bb0bc3d5cd32f3ef568f0e30084c7de3f0760fcac33d827a905ffd5dc204d477e3a76a1bd9bab8a20d5ee04bcb9d632bb0f1b10629deea9c6fd06bfbfbdfef276887bf2cdfae8a677d567bc7576b790bb0ebc29c5ef156962e350f4f36dd53fcfa5742c91552bd3564a7d020390bdbd990cfdf957b2c63892239537d86c8eed851848d64a2faeec69d055967b68b512d60be99e0a874836540224ca47cb57decb467b4dc209c39333d160962cfdc1aa8d0acd4ff1692712f65d6c70716f7974b63fefbef2fc5bcecf525d06e99590fcc201da992b798a2490af0003ad16384b14b549606467643e22d6eb126db3bb9a3502849d7d283ae9e8314ce8c7c642c382f758f4102ef5ef9f992e7b7cdfa7114004dfdea08136b7fa7a99db21a72daca914e68f76377f7a31c1fe54f3ae8002640168b5eee8dcd4372f773dfd341ff42794e791605110b29664707321eac877d200ed0cc4c663171a0aedbebf99e7167f3414e51c83fd38e0e668b85fd6527b5a01b20975f3064b5b94c15ab88c49d09e92a418b2e8f46ec29e4290dcc09d058c00145a895a9a0bb72ee096db998512ac2e8f6780ddd8fce970f5e5e7f1cd4d570f2466c046a3849765197f7cbc27ee916d4b97b0ee7ce5829550c9ea4c7e0fa30fdd5f84bb9009518ea7cc809d3566d45292dd70d6dca8e2d483b3134f9e43ee06a21bf764d9185c9dbb7803d390cb33425f1d314c8672c46e3c249460b9a47406f523a2515782cf70511d034ee9c753c6146a3db7be5dfae53478bc45d7ede00eb116e94d453efb3c275715ff6ee6b62f258513c610de3825a3e7a2a9caa1ea039392b3e83dfa68f62e656babbec9fc4b4f565fe51992de1248d6eed875c28f0504bdf7b470049e52b60ed3ce202fd081cdf2605e184ff6d483e4900f8a87ab0c2d78c00187c4d216b51d7ad1eecfccc146da894059cfdb294db12b8e51e5b004c0354f906f2e36cd59bed98f9ba2043f44bf99cd08c323d822421a2f95d3f30ab94f69dc35b5d4000079a3723a0f82b62e845dcefe5545c9a55007f72c93800a1de4210f0f5eef0ed44b9405092dfcb37449b0f510201b44c453acbdb99c2201be123642557368a8412c674ab9ce460f69ba29e235a7a113a4dfd59e3a4e35028352c3c99f568e8dc04d7dccff26160bf4f3c6bff06a8102e5f99fdaccb7d50732304e88fc02923dd8ef78c6a0c2b9a57c3350be7ce6240e0f0247d65cb3ab005f457a8a0ab2c653b972cdac499893e92e3288e091a79ce768d363b9606bdc0be8650f68d479698539eb207bbcda57d9d39ad813fbc582e6f59a5c5a8b1fe6077e77d4316933a392bed96e5747e5c4160571ac468ffc66ccce428be020c74b09ef1fbc1e177153f262b7ff7e6941073c7c8897121f3486592a6f7db00df4f10b611fcf4c39a1123859d2d088747aed1ab16a4c386c522ca123019495c2c8dc0972d4b2f60381b173ca8e9fadda30a6bfcc54829deab5553983374ed0c4d81502a9c334212eb69079f19e60aa05855e38baf304936874699985c3c824f837000d7ed76d6d66fd10870139a512a54c31201f7750c589d83e829bfb93ca9b0cf70a1ed976573a43c87ee00af1c6bc6ff2e4ca2458a11a1c0224bd8b04379fc5680e088cc90deb7c2a329ef45ce307dabe419e3952af58517945a1f79099de90b50556a10c7e5179f07e16a5af5a8eeff7112c56f440a9d1a5ebf7084689f004af042eb4e761ea7a69c0eaefaf3801d05057cdb0d4633d3b01eec89849936bf653031af797675987ed27c30a0f5f52a496622a97b1b61e503fa5bf1f3e9f5d458f0934cf00d31ee4350991a98ccbe11526206d0934a0405cf77b3a47dcd1e2e4b90c6c614181eaeb7f13e8047049e3027ded8f9f077b35ac90370925011b361bff031e273cdfff8aba7e78d7e8b2fc93dfc3f6f6a112b6267cbd2f68befdc0df1103901c1d39eaed203a6739f1c5d59a767cf92e920da45afd6760845fbab1ba6004528cecc9c004563160de7075f5f55367e88c917151ca3c42a884ebfa84683208296431bab045f7254c84c4d559ef5e25203c8d11f62e535cb810e978c3e8090ca9dbf5821bdabf706af2115ce23dba9e435da51ee7788da7ec76f9aaccde34029777e7fe4c2e4a71e37028252453fa4de345edf5726cf884e377d56378ad810dc3d3b8b3affa9318e7f1e12a4b9931b0d8b16de5941ebedcab074f0e2893cc033c3a2c17f3503a407537ade2688a0ae20c18b8fcb431178156f687994405c30da9b42dbe40c00f6a54f31cd2489e411378c9489e290f47f33d1464e83de88900924239d01cc2c3cb1b2f5e41cf558762c9a3d6b07c71a03cc618cb15fd88a909a0d868c34e85b155b1b32a58d34c85d3c47fdb189096b90761f041783079270878e8070cad4e56d8669b5f6e76a7c2148a2ef0507f3909d2389a54534933192cdf2b22ae54cf2fa39a423f10bb20b4ee93f7b4b5ebe4654950c61fe754fa15006f3ecbce774627c5b643ce1101918e3b2714880f7c2ce4e265d24a6413ef470cad6640f1ab5c3fcc77a48d150255f4d1a3015f85d30890c84d3523519f1288050f9fc71c76542b2a22b6f899c76f6badb5e6a8caa5981b4f925c8efd71a55d0fe55b663d53a1943fe9ea1b647437c515de9e6f57b2cfd84dfa2da121840a8b03c8d31367ae6cbff621ceb64970c6ffd010a67b056967164bb595de56f8537e0f60a0d41ec089635653472ef83600e0de94613773c704cbd239cf03c0d19f690bf17c81294960620b61a420d9162a714adb51c2fa1291ba7e1b43f1a02a5b1c0e40e928e44e24ed9d103f549a34536334cd72c7cb8a2806518e152328480fbc0475ffc7238aac3b86f7e60765bf9900fd5451b443067dcb7e773ac62c2a7e4a0bddc8b727119673babccb4845a911aa9a58bfe24b65272a880ad5f6c134c3f301ac32a122bfdec5cda1b11e9fa31d66599cf5b5646d383e2a20ea396cde34ff01566e0e456129c213b49b1bf680b5516ea14b7edb09beeb5933e3fc851e88a408340e531017b25e5a56bc7fc7bbaf5364a22fd2eabdea9c85c249b5d5989fdc0a58205ca6802378cba05c769d51907e9a8bd78a8b1deca7ce06de8a293512a803383a36bf29496e3d400205a1e796508a851bd8c8c930a71aadd95db1b58c2500643ccbc2adece80d9d332c2ae011d06da4bfcf3a754747f7db78c2b4c74dc30709f460f9c459adfd796aa624e814e8adabd4bc61e8ba782ebe80bfd613de6904d72fecdcfa1452301667ef1a33637d39835fc64fd9af3ef19bbfb7a358fb4f0b453f00122da24804f53406ab3478dc311cdddd1f3a3305b979b78ef964b29701654a065b3641194f1e1b08a415e74ea5205cb663a178f27a50de80dcab8285097edc26bab2a1c9e751219236ace816e857276cfc832961e4bec1c77ad033cc0210f3a68279754db0a6959ea1df62324afbdf6872f696d3fd40535f890bfab6043a7c214bf8a1fdbd24bf786b4669b0edaacdc632c2089be86b58382dc4e8820c991e2150b950b80dc1e1f79633d2da7c9e65a03265556448679c9d7ee3f4c200626ce84ff83e2ba642694fad60bdf4fdb6e48504701a14ba511a2a6edcbfe30866308be60daa615d73970e76f6935ce7630853002e2d6f87c4cb6449023d8004f1cba13059ef2207a00f108b7ae83740ff4f334c41258f720d177e557606f40ffe0fd53bdef8aea2e4d523bbf4961347527de13378231ebd59f90090fa5fd506f3e01ef805671916114730a63b200db70f12abedf332b80020bb22009430fa084c05cce1620cb7e9b42ac62e12050b5fd47039a5bc6fb1967ce309da6228df0e672ba913e30b5735d66d38d36b1978271ddf489c407d0968395a7576dee26806031193c9f2ceea597a035b522179a6b9163a9d9cd3cac90c9a262f866cf7cc0ea8448dd30ff296cf52168856b2d89a85fae69c01f14d670e868597efc62a140c4bc694036f1fe5d0f9d7ece3b4ae6eb7905b935a983ce21dfeedd0e023214f0b9640d5575027d5f2bab74bc3b4a84752e723fd286fd02b5a8aa05ab9e1ae210ef03b56f3b26d8566f4f3532d6ab7084be4230a5afef918b6f5c7cbeb257a8505fe2ca27fbe29fc9a12a440199b86a6d1e5f766122d6567c3b9b28437a434ca0e307eb50b91a74242120479cc97c4fd0cfb70fbefddbf47784129f184f89d8b03f78cafcc2b2e6b178f790ab670e8aa17da80bd71dfdaa089c6c943fc1f022202d2a75f158547fd564f1a18c2ca3eff1eb201c76dfd5c4a34ad7b55f61736e10400bb99bf7c7d06d18fa47f7e3cb441a3622c10b62e399f190678246764e9cb01faa783dfdb9731e1f3027f365db4daca29fa74ab91caf26f6cca10930cdddf02685d323814d59caa24265e57c137b043a63b6cb8688b391747928152f6d910094aac27d890555343898d88e32b7dd0da2c82d0c29f8a02775d6ac31e2057d502d01caa6bb3d7b92173725c2d9cf5e6001a042d4dfd6580e320451d68e5d39d04ceb42904004a4f92da34b236ec7769a2b18f5bbb69fd508691423bd956d6eb0aeaf0e0734bf9820ae6dbe2658b2fb1d27451b41765fbddeb09f9b93e8e60ce05bf450ef578bebf817b7921222189310557b9775df8b397317b551843048dc4028dfbd5545614de10202a185acaa3d193ad6f7a409945fb36006f006d5e93b5060a8dd0dda966e8eae84a40af52a13c8ac98b195818af8957547a462e8a7878081dd807dde8bb73eee51a568e0684dc238bcb8a51906d8496394f8f4640f15704336652c8ab03cd5072985e14106c7bc6c557b0d2f764a92418029bc2187b1a0f9c202c286990ee9590cb4e0c76df84a7418dcc1a9e3a7b01e4547f85d197d5015a8128329aeceaefb575c37a5b810e3c4551ecb820296d59d2e0ff59da22220843b50d5085b0523dfa2851eba405c609d3e07b33fbc384f8ab6eb6ff3dcc4c0e24e381dbee714094712d7b19ce0e0152b3d80cbb0b569e44fe9b9caa9effe6061a923053e29675b77187bdb7e94160a88e7c0006bc26dd7692f6472df2ec100d40d1c78e6aaad56439c47b0945887faaa561117760afe617fc63e7409da6da019ac9c8e6fb9db2664adb889437eda55cc84c7e4c759e0b69362ce09c8eae1b043bb4b5b2ce5c3566f1e9845db7ccd4cb57dddfd9d6a3cb140aacb70524bfc9058fee2ef3857bb09054ddf5b38167e6a663783f73c5568f48ff84f4609e385207354def033206065209856940a8617f20b81c58a14702754a66dbaf099d42840d6a0da859f7742a603574d4ab6164b8a95ebb869a98284985cab9fe527c77f40fc3a4fb015c4c7716325de92113c1f8f7347c42e9ba52ac2edb99bb36344dc605a75335a54773f028b6cc00dafa7abee54f1d97770be054a14ff3ce83cf9e860bf01ca5382cb3396aaf2272e6b5247daa6844dc33090b8d7dc24ba5321c56e10edd9e49e3cf1219744d3099005c629547f45d20209ac84277b83e616a55fe830bd49f19e1ef10226c51fff3c650eb364ebed337ec9bbf43e1b86db422d385a20f59982d4949998644364a84e3f260c9667a4b78c0d49f9948a24e3551277321034b144f8fb37e2319276687ef11545ecb4d8678b4c2b88ea0cb6da5c86ae37102ab8863702f04be2e3f6f4a0051e15841aec7210ef8be43c0d120f2e30511bc06923bee2de4888ca440199a7585214114b133276dbf73203fa1b7ad4784fe320bb528a2f63ffad191c2243518b9523d2d0be9f293fbc6caf007f5889ddc3a100ccb8d213f0d5970c19a6a4d79647d2262da39a163016da985f425fd45c8678683293fed8462a182702da440418bd64b6ef33a620cbbbe1487ca1d55a578642d0419223af0edb31efec098f146f47dfaac3dc642154e90072bb614082e05568f0504206a00e182e499a2494635b2799465cef0a234e654c52746fb05bc8f5806083618fab9ea75b79f93dcc3a3e29733f9af2dac3779fd4d8ab1dd7505dfa5750c9ee7b7aa10b9644b14c0d2f70b99303567b18e28d669328d151b83db312c6406b1b56c3fa9508078541736392132ee5a21e73e97d2a60457ae7b89cc84e8d20905ecc6d348525179a06e3d995ab73db0719495114a280d4111f1fad2d571cf0227d4a5dbd8ac81fc958b97c3ea12947a37f8506ef418aa350695959a9b208707d963576d83235f6fcb7ac544ec594c0c9206ffb9d7565d188d38396db2040d07e372e9f5053f91fb2af08316d3a591b0a3ca3d7a5144ca827d9563c98135830c18fcedacba636883684a2337139d2a549f5405f73d50e61e9103a01437da7903ff6de7d682d4cb0fa95da0f48853bb557e3636d3199ba05bfa7c2ea8827cb00c0302426380797785ba2c32c31c2acd4044799888ce80d813f5fb7493a3a274082fdb57c3810db6e1987d328bd355d12256cfc16f571002f224afb65ca2716301ddf581023f53eb3a023315b3d17507c13563c73a6897b40881a06145fc6d140260efc2fa66a32d79b9ddb5119a7a48e3a4644dd12e3943d007b4a95001a17f05afde14cf63162248b502686313b40c6d6625ba15e686cc4affa36852761c1f04d0d738e4052e6f3b8183973f103abf7d9c0c33f57d016562e8cf0fc8714393078ab728ba6961595b1322909536555ec218fb2bbb63e76c58ec61954053bc4504509ec29b45bbf6852493612dcc355b1c171cedb77b7f1673157be7308c3dec0cb6ad51ebc0f55728d151228ede5a04887fc251454b9d79763702c41ea32a1b0595c94f355fa8f54e966d1b0ba8f7f34cc5d20f1582ceb1eecc07ef288694be0bde82a46f9719b0be953e13ec83c9ae1a87e20f4cbfb470b9699c0a8ed5503b0b511f33a90407b3aee5b8c84304fd5236b64481c1be91995e22a5b0834fa1ee0794ee6ef3527edfa1e522f546250da5867d5fc7d10d5afb43662413fa6ac68e07b4840ea862fe196164946deb39ea3ca26f5f8c2ba6bf3463adfdc3833a7f130c5608973aa7cec3baad43113bd198a8e3794f1b738a34bb39382251cdb16f310e04e0e3214f1400e55ec68219538bdacb12b4880f6c981c960d6968356c6eaa04b926e940620e6849c53b735d0c6bf351fe3c88718f574506ded8df7fd061560f5e1d0a452a717b52487bcc255dd8a198abfb41436df5cb0d3b06b2aaf1f5580779113a3d71a61161ab310229985417c6197e05b09b39e2538a90c87c23eb8403791c43238a5c46961d5237c0533de721b2b1b464cc3733426517ce998cb1e30cdcdec91a29095c3d89f485f890ff9136785fd967f6916646ffc071cb78dec20fece09af275b5ac23f43b1d08adb8adf2f39081fd6a0b618fa2c7aa70903ee90b182dcb6edeabea0797c0a21cd74cfa1c7f7849f266a0f11ff3c4c213c5fade02f26f9e9e0c1d8301c3e90d4e129713503acae50524063518e8ec71403b89900f290450fbea38a7b1b5dcefa7f617ff9beb7efa16f5e7bedaf9604e8ea78a040c7be8f19c6ab77798b1130bd4269e0e7d67fdbd99b036f95333831e426fbd9d072333affecbc8b8afb2a2ecc7d7bceee3988bcbc8dc232c0d827b9995e64e140e66c607fab5ff9b9cde6b41cf4f7032595c5bcb4dfb0c069af5a3eedae2ca4b05b2da8f9da02fe9e48df258787881800b14ec78cf66531e5eb5d810619e087206d1083b1b8896bd229ecb9f55c6886447f2f9de8af415453cbdc29a187022aa0ff0a63778f1671a0e3049a67879690cf8a5e3ca27dd29d5422beaf4945292aa0ccc4658502e3249b5f2d0c5638bdfd565733f5fe818a29dfe943540601cc8bc0c5657560375493c263a4254b4c9cb32b9edae75cb21ed03d98a32679bf0313d098e533f6ebb56e46688b642d28a88931ee9cf4d22075c5b166e1cfe9c389c4705c87e42b5ee4ef38312113d177297722e705e151c90a8c2989226b175ff6fa807e22a00b188b342f71a1fee82d276cfd2e0d2f7e3e5b410546353bac8880bc009c32389e95b193145740240c46b9e4b939fe7289e0c5a0b639133bfb2b337b10be6d95f698f5a63e1716f7218c2b5aaf8315b8d27cd4957594375115cb90f9c00ab71af5297f6fc10dd827bef3666490cf7af0944ffe9901b9f197ef144ad190ad4530df99e076e37995514f03a4abe23a3efd00b12bc223f5bd62f2bb6cd45005283003f72bc819f0389dd8dc0c64e0491e8115757dfd2a53aefc3660d96cb02f077968766a32b6723d0badf05ec1de7083ac692b4918624f4932fc22f79c605857f4a41577b45e2127f59871b6ff97a0c3b7104e6a77a75affbd16a78c9910f2ad60903cc1c94bee0830f04942533f532181425ee53c0c64077d3ad1de46000e98962819f2073b1657edcbbaa22fea450606e3d610cd9644511ea4ea9ad390da74c0ee4186ea9c381ca561f84a962033592c82b6877fde9836fc4eff9d7210115ef1067093e7ae595729db7680be60004e8127986a605d94c97bceb44bf000d746045b7f5fc278b364f64e44d45b11f3dacd340e0c3f339958f373f0962f00435b2270523efe60b61006a9ca7ac083b8e008eb5e19a808ba1135bc8ecddc00d1ef720f8cff5d667835f094876630973b81291c938a002ad9f44355b67dbd9056896dbaf189198929c8c658de6299afb7ddfb7fb3445b41cd7864b6771ec0e016ca4475005d0014ac922d85328a3bf918e07d0cee197f6a2c67131039a5b7a03580fa7f254c8de307d2c4b88bc416b2699421cbb6a9e256a6452c1406f0e6d0fc4ebd51227f56db3833cb3a507541cd1ea09ee46c7595244d6a8b3198f8a770b516acb782e4c213ca5eb26600645faf5ac183908417e2bcd1c13e8c9deb89c0cd23c5b3e6e7cd3df13d409b20d73b80df45a885f4d81481d21b62ac5cff80909244cae620092342a42e4e0de909aef2e92263f4b63f9c14d16f51465f0c1d40a5ee514fff365135ee542e6034ba00d58fd3d71d26117e2991d9e0abbcd211708ffb77052342d5159b096398262aa1916a2d210730fd74578854c0d538e6d5a0c1fd6be6c0c44cbae185cce3412e69ffeae1a653ec38ae92e25132efc0ef00904ec806522570dbd0bb3bdbdbc062d77184206fab2111d0895a1921018032068078583b987b1b3cd4549b5441918b3d200188de6ea655f81a2f0f95e76ebfa0c0c3f446cc2b1802f97b65d9b437148426522a05a48be6fe2ab56ac1dbe45dc6c08ad957ef56e0879f9e53f4ff56021fb6d1aaf41854ce1ff6fb1463a21d991f308bb1a07930234be3819a938aced48c6b63ecfc6badc3669fb5fcadb7ddf3fc403a9e47aa841351fe82f82c8a32a57a8949f1f227ccb0870f49d8072c906843d05ec93f55a1bf9e916a92b9fb6b32e14b5d1a370c58df863fc2c0e0552b606ec041f38ed5347a1967806def497aa0b4504ba81153e091d1d5a15c6465f51abdc0c836d443bfff525a83c24014bde66bc725c514e5193046ffe99a45ad9d8da73047e142e82f6eb4377864431ab4cce727ee8c67a1430466d0fcdc7ef502b8df50360c6ed74632ab685e1d2a1209b888a85948249ffbf154f263b080b1552c78f049c98ef298a9da070b8e1baf728d1a82eb541d89cb36a14a772bf99e633cc1b04bbee3a23dadf9ec8b6d40f7bb5a60cde701f0f1106691aff9d881f43c86c9157c739d11b2a5c69e2bd456001ec38a635e4c5d341a09b03f32720cee0f1cf2f0a8f227c2cb73c6ef1817037fd4ddecf0713b36352eae3b4b005e9444f8c27c50fb11cba5ad37c84d184d2f5e233e016c738d47952633e8bc24c23fbb62d85450aa716d4ddcb1405f42105b042052fc3d955e8df0d2721bcb7a4869e9acbc0de02733366bce981f9293294d9abc48c7105e1cab25f344abb45f1d86f9b6ac01b0f70c0bbb2b005c807f17501b9fc8fe3708892de9abb2d4a0ac6942794b22cde00d253c2647d3a507cc9afc18f27ae98407afcec616a5226c6a442971b1878cd0602fb76cd612ab9067a7709ca981f8100b69e4568f7361750a32600fe592a18072b6a774ad6aba136641eabf3843c6aa3ab026cde1276862240f4308721b49001ca5b41da941e83c90197c309689a8d7638ecd1867dd60f2cca3126ebd198dd05d233189717baf1f19fbba7abdc518e0d1d01644807607ba1d5fbd4763e51800503dd463a8e92199070c6619d64dc86cb95a0e4b8b71b6df45df647f8d06b6c0c4ba61da5364ee80edd0a3232ab3ed742da67ba31ea55181b7b6d62f2b904880642d35b9ed4a31d3c2d09b1d4208d2f7d689e748a745e819ac3aaed701fe55d0cdd30b6fbb40fcdc410bbdb7f6173564fe01a1e3a85c74b6ac4d212204ecdbc0d77f54b8b6c52ab6d9889ee8e77fcdad6adbf0d808964826de0200f083db1eb0d3313de803e303addf81504bf419b5d0978c9d399eb8f6d86bb9c2afde9409a01b190a4a812c8476d3115c0afd110d68cd9797c68a1dc6a1877e4ca4179ebd3044d64a997be58c993c5f74dfa64efff174e01322a455a1f4d96fe57d9483311031d3893537c8ec75d26a68dbd0fbb6858a316afb1ddb2b814050b3e8909f2eb0636b5b0115f1366b9884057a1e5553febf438005935874a031c21b915a5043107 false -check_ring_signature a7cc78fa4282c040d0a2dbfb57226ce23068b613d7406e348fc8ec2cadb74313 70ac45ebefaaa3328bc0f6e38520c89ebe7b41393705be8bf6de064f6cc59c08 6 219440969ad536be75a8d8637f411cc5ed0f0533db103a91511a669b4a17dd16 80fb0abf12712d12b6be6ec7206a6c7c360be2a1558f01ebc13ba50f00a491e5 e2addfa521a313a0ae3ba294a4b941b60de51350b1c913cd8bae3075e8ba037c 832bd63cab00523e5c835efe508b2030af9ec9352c4f1a0cd0a76d5b3f35fd76 666f17937b6c312b0890bb2f6245a1f45954601c2b4c285e9836abc8957076c9 b0aae005d403a490ab4e7268981cb90cfa6177f3463435564e39f01ea545d941 76a743747681736855ad78102b88f29d38cae409223b1fb7266995a9ebcf1b0421e7b9babd3ce5f7c35eca29a1bb7aae21d599ad6d997e5350b2e85f62f37fef354a8969c49c2baac2bf577b438e516f6ff563a25e6c59bf5683978370f4c208b2e7e6c6be92959122570a10e5367524370f1047f79aeead58a729a18d5bef088ae5811b3f04f82abdd1b62d977cb8b557071067edce6ea19e79335136faf4028799be54f90a89f260c3ec63f2743b10782de0d48115a9b8330235a54cbe230a86ff9de09bc954f7ec26c3a0388dfa0e458abc0b8e32a88e5ea33c0cf2c15502c65a129d26818ca49a345a05ffcfa5a00e0b63a580cafdecb07085415646bb07146b43488384fdeee84af641723aee7249d1bd82b37d32cbd4a5b23a0992a200d969a0c21686c9417a85a2dba5fee995d8b58f4f006cdd4ab111ee8d18a2af0233d04ddfc2b0a1b4bd18f16f634eda78fc29b934d56da457190a3d929dc34d086d4a8f687ee63030244151f82edbebb2eac8c7aacfd53dc2b9600ea895276e09 false -check_ring_signature 2aaf43339bbe646ac5905fa6ab64b672a3409dfcaac13f665ef361747c67c6ea 4f352329ebfb0750d34da3512a56678959d04ddf6ab4ab1259516e648df37f8b 19 8d5bec4d9b72eba1bbe9971865b203c4eb89032f92c556be2382c21eb881175b 6b512b3c7c3e5201718a22de9e011fd783d0a893f4c39aa8b752190adfce447d 39596139574df631c05238af4ac3d26b92c24100f47e3ef084c8f2cd945e3cdd d134b455b53f0a7687ea11a208556b78ddc67038a04b445cce3bf2675a48f745 41da3b5175a51235010a87eda15b7a840bb1abcf663bd652716a3d210d014858 2957dca81e25fec2d8da0df056bfb7c093b1949cf54e576ffbfb114c6d451b03 16e6030b57c6695879d1a199d65c0423c2ceddb03e59461b60ce3a1938b11eec 89d47f7777ec8ef8c8b578bf435b14b748167074d0e292843fc44b5d855377cd 62facb477d9b7c2c00cc6720304dd56bd9d779b3b549cc88bf80998bfc3e2000 4556bd9a5a0be94f01521be7a5abad168a30331e6f8ce747f0701a17ce9009a1 45891c971a48d07a1b744420fb0d5c28682846e577f436e2e6d7bc77315f231d a5a17acb6ca6457e24e00b52e93d9a58d2d0f2352ad036e87cd7cba39f6de386 0a22f334dc54b21cf1486d20e725dd1f9e139b9d926037321cae4dee82bece7e 07fea04e460d16735b14fcdcbb9283289e75926be09d89d8d6164e00fe7e0a4a b593e54fdc9cc3cc543a7fc41661d5d5cbc31e3ab2677b59ae3d32efa09ec54a 50d2a2f0749b50715268bbdc12c196ac9f051cf9f892198703081e7e6451203f 88dfb15eb0507fbf44f9a65e215f32233cd9a8fcf0b60978973fa9a41e79a311 18e34f29ff036df567f2ec239709fb6361d56c221433cf2aab96347e60659367 0814f2e6dfb4be046c4c5658e7b778cb36ccda0b35866f30ba37edc10c3e6c6d 9c7b2de40bde1efa7832264e0613bc21deb29ecf4aa5a8b54bf5317214468b02eec35dedef2e6473b0c7871bc877a40d1ac6a6a28828286e242037b7cf3aff063bb6276f57da079b5f5b5f875a909dc4ac6ad5d41be5943c54b317825f301601555a1bfe25211e76550df138191b5a8acd18256647944446ea1ecbc476bcd50fa6238895e56845950dcd4e1e5cea4cca4161bba7081d396654ae55d6ee5e33043a70943f155f8370218284949481ec13e7f7ccbb74da4c4928b43c90f612f50f5b23ddb37adba1cc48fbfe07bc8900a518cfe9360d001cbfd367bf2f8dc4ce0d07941478c280eb4faf34724b3ca17060261e77bb91922b50450f5c32b0285f0a77480a03f242ef5271723a2eeeffc6c620bd1e8e173415923e6a1a1c0dbb8d096c480a571ae721fd0a301f979d987d94b93bd5c9ae6f2f90568547e1190665028419ab68c238b61ae1dd89898ca6d6321eabee83e9f7a6298e5576f8d6544f01b704ecc1f1960c08938b1675bbf7efbe5fd194fb593b01f0a58da47030e91c02f516a4a761c3aa210ab1379caa932e41cc39625bada8b8c784cf371772831c0d67e5b0bc2706a4ddca53c0da0c0b5a25dbd13625bb2a2ea52ee41a5b35b88b0eee84fc4db4509762507d2b92a677b6a0b3600393afa93bc396cfdf9517505d0f033a78a3ce48264224c1c5b29c81b389b76a669f6aa51fed2345a23633f3a00c001d06c0cfb238c0edc0e15de725afc1aeab5818ee105e0126561ad446b02e08008ce722b9227e89aecf682109cdf772254b8dbe29aa8b608032fa19954a49061289b3d8f8708e81350afc6be70084bf44273e3241b28a86f141c1d648a73d05a0bf4f2d6795624c6b8c4d346464968bcfd1eb8f775229ee06e308842621cd0daba825d3eeb142199bdc91422541ee7d6caddd749fdad60f7500794fbae224018ce70cde4460f01f95708424266978a096e1dfe490dab5dc6557638ac9648106ecd32ebe11006a2d4b99b2045b9307995268835e81eb0fb09bb3e66075e8590e4445a6d77ae6f6de6bef8aa1244a725604d63b672c9a96a0c37b2537726cee0ae722acad4501a3c4704a5e0860c360ed7a8205336f6520e7e49909b61b254f027d327d876623c81dd82c21f9d839eab9f3170cf5f32fd0f39aa147744cd273053c08055b9db83b2743443e0ed8ea31da061661bad3e09fd75752f7f2b77e950a0fae4dd12a4805b0ac692f86484b056f17f291d7b9f75f5b0eed70f419e3ef02b69f258235cb93e03f3fa078eae8d9032d6cbf7eef766eff5102d80da5006d0720f8491ced979fe26236e0dc96791ace0aab62d0ae65171a166ba184875b91039fe51391b9883337a6170eda6dd565b63ed9ac8a1f01f5be828caff07b9d1c00ef579a31e4770d6386ef100b92c331c13334c08136f60b89fc40d927a4b85e02392c2712ff813ce00aefa5cae1f7b57206a4a493488ac792b69ac0ed8a2dcf04c05173a6a73e63ff4d83e897291583ebf36869c33620fc1faf93de4f32e8f30a90cc216dab618482113d4f1188a15edb611a0e12d87d535e100741104d081c09641fe9df5598dd8de7a87f9644703391b8c2a6eb6a04a268748ee7af72745b029b4dd9be9b12d7ea6d40f741e379655524a7f0d9b0c0aaab242a7bcf400651031f979991442e26d82e7a31c77ab9224065446fad06b15d11f5c9a20ccdf5ac0e true -check_ring_signature 4434b232e684f1f4f9bd1b6cd2901d594394f0d790ef6ef9016cda47e95199c8 4e4f8cb063f3dd92c10a7a389707ee9a0772f6f51daf3fc2e047545ef8a13b93 7 61ef95daa628eb82c2ac04a63af84af01272d33c5aeca2656c2044c622a754e6 6e69ba8de7c50530e076eeb400146325eafa376f30990baee93f2d8bb094c030 96e30c430805eb2af8ff4db76a9122d5ba489028edcef2ede897ff58d0a613bd 7067b5c4ed4e100905580c61cf0b41fb80fe70086dce7b421806ab2fa2ecb2b7 bd1fb3844be91852b99acd0706317f55f01532a66d0205abf8d5fe2a9482a25f 6426f4daa3e7edbcfc8bab4304829f9e406ffeb21c41498001f2c68345f9c996 1c4426740d533c005a51d72d7e32f8a4b0f3cb48cf72e4fee9b34a40d1fe9450 d4fff1ef2f43b36fb459b060a069308dd8e19bce8fad0906f80b39ec27f8290fc48b961c53b6592d3e2580510330c5af6125a1f345a5fec3f184276dee6ca90c57e271f986ae1a71f9db7a2e4ed4b81919f1616345614a7f14341bde68c07b005d7f6c9013a0d73c57233953203f8ecc078ea91c695fdc5860af4118677d6b02ca25bfd3112608f6a200601bd5538edd37cae8632b279e0124c9c39d8394c507a8a83df5fcb3033b1219eec73459e2b0508b5dfd65c344f1b566ae94763ab7078e4f5e0439e1e626be4ef6ece29d07da305f32fa5bf23e7f44e4427eb2d3ce0edc635e62f47d126617d1250b5f02e2efdcdd565c33938667d5d0d7772dafed0b801f2818af6c57d44b7ffdbe48466d29870c43e76dabee297b7b6c77624fd405d39db44817de3ca8659a6deec934b64c017874bd9bd48943ddb969ebbd80c309f56ed67e8fe7c03116246ae74a25ad7b2d5d8c1ec1918fd73b899fa280a7be091c89c7ed660d9e58bf3703775bd69add62ce071377399525875990a626bf5a0465275f8e47b78f6a1143e7a71f9e8ce464d7d523dcbb3c40bb4fabdc79305306509357208465f7c6682afd34561ec98e28ea139f62059bead6869e243ff29300 false -check_ring_signature 41b06964198374fdcb809d0f8ac649f881fca048d8ee804b19986cbdc86f5bac 7b0dd38dfe85e21c01f22f9a831868d484c70281ece045d1ac00d8e749968e49 3 074db09f9169870f2819778c146756ca3b17cc112b068ba711cd6e549b9eb2e7 3ae201490ffb3d5640f6136cf93bf1029a50d080365530f41825c320136557cf 3dccea3b2fea49c2ca0f078d228f2c595c745af515cf46fd921e1b220ad58ccd e00c073c229654d8db0ca690b8d906529ab2f0bbb78ebb59e2104aba8c1e690867aa9b5afa3a9b8d953bfe050c169dde85d828944de4167cf87596aa98273708d4406bfc6ff8a843efeeb96cabe6023acf0094c181b9dbce62701cbcb868b6041cb6717fdda9365cc1bfd4937b7cf5ab0c26d5b69054d681142426b04a98190f200df5cce8e82569b13d0885dd1e6a0616ffdb7324ad1df2509265f449c97f0c4fbd27d5232b2482c80806d94e1ce5a4089093513ae321bcd5a363bcf6b4160b false -check_ring_signature 6ab5deccb70627d1d56749fe409d1ae6b06a94c36b4cb90d047883c07c035db3 820298cb23aff8136a4c3110d4a62ffbb26c405828671822d8783d394faf953f 1 d37cc0d63e33df200ddff7a452ef303c3d89da65aeb9ff6204bcbc952389c7f9 46cf85e3c436f122dd465f9bd767f4d4c622cf6c41ff7c6a532b5b4d9a83450bcb85820e3ad988c1045bb5b8550788214447889787135d9221e0fd635bb9b229 false -check_ring_signature 5689749ed97379fff416c74c6d4ac519b8e18bb7360b2fe2b9c3631622c7c444 7750dbcbf1fee5b6fb383c7b8216474a473c21e765e96bbc90b198f75bc257f6 2 6be314d485b966669f73e5b90f014df04f9cad69a872cbaf0cb9575ac7a2ab65 918b061d98967514409d7bdee17647b48c234f75f33dcd2b95f320a594077619 7d64011154d8cf2296a489d648979563b49c900e3f73bb8b80ba8ceaa0d7ee0ef744c1cee7df1878eacd0cb863bb092e135490cb30099c903a09e26289c31903f7effb8b811c224008761535e7beb547917ad6337f78a47a4dbb648d07bce90b689bc3e5cf88e8b031c6d415af56a4b918b26b19a57ef49d491529a5ac968b98 false -check_ring_signature 2143db363ec5a298f506af5f945ea58979dbdd42b0312c5c7a81d0812ef22be8 5c58619c065b9f0f6b8f54d978a7089941518741c33028fa5bdfba74cf0289c9 29 aa1ce7a9be065a281e30085f2a9912b0909e94c19cdd3e7b2bee65b2b0e52fd7 2734d284343d304d1d6b96d8e589aac1296a210851de3752d0bc7736896848c8 830ac8ca4b3c04512cc7494e1cf596acbab0303adc731376ebd4ee2ed29bc9de 639aefa7494759afe2f08aad2036fe98006940e6128a449aa1cfe1b342e656a6 ef71a1c6a8f2c1cd57dfec1bef4cb4dcbd78f3bd34889e51e0a71ecdcf23987d 62c18984ed2feaf1c41e8114fdacac75e7b1492e02f05b67f8ec641a304dbe62 8b4f82243529083d2a2e4afefc97ea415ccbd9fe7ccedd8d5f32b99ef79b506d ebe1a6525deb8cb44476b240371675a97cfd7442f9926107ee2cc11b9b1e94e2 59e3126418d2e34ea0fbdf1d1b3bca4d01b06de3f173aa36302e6d34f4805d4c fe5b8c872a3ed043ce2687bba0d0a5c1dc2859a1a9910d58276e4702468e57bc a29f317ced05e19c8f596a71f4c56a19224bd7749de22a69795e1cb3289e7b32 a9472cacc2fc78dc73bf92553352be8ab7d5b327039f1ef8b262027ae4e7832e edd0fea1ebac323b7b18f03a3b207f63e1e5d51d6744719c0e213ec794c5308b b1362d6cd74b1e49775edce8eaf71172f89699fa1f0ba18012cc008441169a9c 75de0d63e031fe6c61f592f705298a67477fe05f5aa6d86bd03f58d3a12ab613 97a36b4e3e8ae9cd542304f5f22fbdf0047b0c77852d327057a9ad06d873eb9c 96ec04e16cf7d73def9fbb695a23384a962fdcd9d780d87f79af58d24f6c3c96 0d2c2929d7751211cfb02f0301c9f0fec6400aad7750310955ed1c90f509bb1d 76c933b5ebaf0db69414c5ec30095a76bd4748417aec2ad22ea8f78f95a204b3 043113e729104841bd96b7476086a7a72d78a4ac47a0ee07be914192c9e1fcfc abf99d7dcf897e46e733360166857e8bf0e04db2cfe6970a3281addc53c46f1c 8c1619387e696be38e59c120b71e937d40558c9afad9924ae3ec370a17fd4fa2 ef5ec4b4c1f16874f215e20fca33cd15fd240e9812243c1c5715b4485f2690d4 41dacd7d5440dae2cd455c9e115e7a0e7c3c85730b698990d5c5c33eb28c9774 2439db0e11d5699ecad4c49e4c2ab86212d643d05777170611d8853f4e56900b e542734c9229873c176e0d399415f09f4a8ecd2d65ac2a175647dfffbf422e84 04bf6b40b130a630b8ea7d41d414f2978d83796c2f2dd2394864f1cec625cda8 46e7d21d166de0347c04a02ee12b3dc2dc924bd2459190e4beab6f7b0f409335 6043242708cd2b81b274fdb6a4ce975faf8ee5eee984842e77418b47929faca5 2c165ddfb5920a058f53204b7561a78d7433d7813894d05fd6d82129e5f52e06c79d50d9c3639cfeaec7a3b9d634e3b94bb9b248dba38e133f24623f5478160c7afd26363fab6026dcf3c005cf7b8b6a250ea66c4c01e3833da21bce814d260935667c19de0bbe4ea780d0b90538e2b06c3ce699765129054314cfda97695e08e98587c7158c7f73b1b9a3bd08134a88019aac2568ab10f7aeef08be63d9a90d8b4a60590d171d0edc0062954395ecf5287a2ed66ba671c47418e87c11dfc4012667f3f47d18842701460dd55f41ac7b380151226fd2ec1e92440a6782a3b409cc5cf05124f54fa063a591d2a760ac1619c080fe5d368c44985e7d111a7aba0f79b5784d6de3fb13e9915e6fe387fa5f44f6be4ec253f35e9a8271fde36f710e77c3d0806bf5f59b905917256c0d73bb4a30042d95e4fcdc1e95455153211807079ec731bc640e8f345da9803d9578d3b1b8ca43559396f45c38b77c83a7270d299a68992bd958af2b82173a99fc5761b1dab7a8aa41cd1ca9c84e16e10da00451fda9e8d3009a69d412cbf4258dd1ae4f18e6229eacb2656c431d1f6850110ea4c85c6fd56beb88abc0c5965270e845d99fe530187fe8e7275ac6121e0af804fdaa2bb153f3beb9db3c6b81884fa8b585e68e37084ff8931a4a6f171476b008d087aa5dfc6fd5b8d9908ab54cd9093434956199f420b6a546020ebfccda6603582921418603221facac0baf0488b66799478e4cd1b66be664245eed720bde0a84cddc9fbdf5d9841c0a6c35d155020814059c36fde47b1741b1ea575d78dc06887625782db328f4b7d57b5085b22534d4361bee81ad9bc9380d0fcf375d9c0bc9db5915ce1d9bc2093bd429e2f7eb9bcced00ff9edf1a700c1b45ff1f305503f5dc7f1715c5d4ad8301fd1accdbb183bda1cd4a6c9633ec2da99a3806859c0bcfb0c00d9ff5ec61d776972f68dc49ba8d4b5a27c33213e4b38cac64c4e2c70ca73713b3cc9ac2c73d1aca6d44bbb7ff7c67e8e6ac1f6930841f3fe9e0a2da0e7628a232fa4e96ac6f4684aaecc0ef60b86294a02de91db0d78736012b8de705764c91da785e08dad358c6459188cc5a20bf09b6a12bc349d8c956dd0eca2c0d02fb3558bdc8489a0d5373aaebaab586dfba340a9e81d77df59992d36d4bef0e8441043837541a4300d98a26290b5209a6908469fc2ae7a6b412c6c16e93cf035029153c7c629428cdfa460a913979e57a7e5375b196f5fa4f45cc47ab03d503c5f021611b8a742decdd8f5bcf8e229c59989b46629a07a51cdf8281ef121703a5e524380f2298940e2ef4172294e2f9287acc86d3399df0f9a1398d601cf5025553d90b05cd7f1f7454d2fe8ed1540433740425d43c8ce2cb414a9ae6688001569aa1b3a2425460cfecc09d91c9d1d1876dbe18d7dcbf99bbad67584eb6c20ae53383f77496edb61ceb3002e962c1d1e836c0be10182e40ca6488c2034ba2075af029902910243bf3f58144b29b60bef3721a01ec8712350f289cac38800408e41f3e52dbe6ed27e59e3a0e3a290c7a447f0e25e4a5f992cba45e28ee2ccd0cb441a8ab53f95ef9d00c4f16c8a8c0055f5d745fdc76b4b907e00b0c18389b0a1ebb1bbf31ff4f05409db4c42e78d167ae2985db57319b6e0e453030c2e3850384f5345c8c77cf4dbcdb8d44f0e729f3bebd425a6b909eb6733d138de867a4056c694443e643bdf3baaf68664dc4f05721b63223fb1a5b21f131029750a6620362ceb8589051a096aac034277bcdcaf1c2b2bfbe3533a8100f234431c3f295028262dbc476ba3ca2a0e917948aae60bf59b5f486fca74620d0a8905b6da25901c319a5413696266cb4820cd2bbc57763ce3acb9bf26b00c66daa957d6f9e0b0b7f267b515d146701a337262b839aaed252a5fda2820ec2ef659162bd68d04a0846a820d2b3c9dae68b02f2c32ad83787d3a79bf7bac74a3288486a0689039900fcd81994c5d93f0ea423fab53c5729d3d8036456f9db2e77c164e9c0218383092ecd09fb6d1fe5c1536949d7ab15cc6eb249aecfe2385d240ac8c749b6f90e0fe496cce30e2a6f1f047f7a2a1bfbc5b11cc622eff4f278206198c352c178590194bd204ea100b2e75e11892a29b4cccf43cfbb482617b7f4823815dd2141f403829a7b2c583ccd5c49135a1ecbb971b1004ab458f05bcfd1df690ac26ab9c401552148419bb9b588d0b51a382bfe8d584025480f078b58e703ee3d5d744dc80816746593619ca38f697e217d0b0d0e9e89be85078078fa3441b85a82fe9cdd0af1522178c13ab86becb2a27c157039c6d7cf931721c680f03353dd9e362e810beae282b1d16c79782ad233b84e50e12a5d8bdf2463cf650e407d9989b7021e0cda5902af0b03a566f3872ce1871ece30c41ee3cf002975726c93917d6408de075a15407ccdf748f02aa06fc1b7d42c18eba95ecb9400cbd26bdf992c21bd7b09338d7e350c89e68b8ba7b19fa2869d23ce9612d076e4d57bea2c21a7e73e1907424817667746ba78db8434cfc26b1cd5ffe27e8b3b433755cbcbf378793b820c1be927a7bd4a7720c2d91ab7ec738052656bf435c616b5a4d830f316af50010a false -check_ring_signature db30eca5ea00bc3539146a87594f37843c3adad86fb0b46f29c5ba470d14ac30 a18b401dc49b7f85afc2268f85908257dafef6b14a80818b54005d13244deb06 166 a67085106b1d0500cec03b3073d4c219c11c40a3d8afda2e11e709da3dca4b6e b65ba10d827c282f3040c3f50413bea2e6fef3e64effb251679fc520b7f3ff03 c188fd1af0137dce6146913f9ea675f52f591f1986aa6fe257e29edc01ffcfef 8a28ad8a5c0b323edca81e51c9193d3890dfed9cf9a98179f1161bba4ee74c44 2420b9b7e9d7bc0ddfac00d97e0b3a695a2bcac98969991ac3977ed757cde839 75fdc41c785cc3233905292c35d0062d4c4a3dbaf4f7059a74a9d9bcd422883b b6f14c14ee6bf14425007bfe1356ce6a7ac526fd499ec72114df34543a43ee85 88d4ff189bc5f9547cba0f975c69d3b577b4f5a7f75f3b18c15b765c6af61a31 78fefcb419549f17925545a8d69b56e65afe1cfede60473dfd1a2e28a873eec4 36d95bbd9f0a263a735ab21883def96f46142f0aa7c07563db31e23e4585214e 72f908836a12625afa4f0794970bc0ab2d47a62cb7529581228e51cd8ed9010a 5575f5bf07e1cfc28b61a31b3bcab157593947888a4277d3eb92a24eb1eb6481 80d6edd48220b40945807720f7422f8ae9e20714ec9ca3b75fce78c03e970922 b2d8ee8d7a9c1dee51734abfa1cd581ff54afff8a83ce3cda40e6ff81551547a 990a20310a97440727c7255c585d61fa43b9f2ea375f107c2f96c3e9d00988ec 35c2814b52736f05d2a92a0434cc9840796609a1b049ec222bf95581ac48dcc7 f039034ab869b5d10e304a105ac8e5d2332846baa3f9ec87b16c78407f40b1c9 4df051c6140a755d27fadd9e01ea95eb251542fcaad42a1999e6512f6fab9051 009abb71aab637fc2735032915b98e7bc75461fa162d68821d7328f9d9f35a18 712bf1bab4c9b05e19dfc73928958b73b6267426a1a50702a78872e0113e526d b7a82115e63b2a5910eb7c78ca783e5c53e66e6d1be8dbc1b8566cc34a86ea18 bfe6f456cf79a07720f99d29c3203fe6eee64911745f81aad26e58c4495869f3 3e4cdc12ada7fd36d4d9b01e25fdb2c6a09a2d7c8f5cae43138ec5eac060ad1a 255c3c28d635edfe952245fa6528aefde8c61fbc5524eaed90a649d83e20345c e053a61c0438927e1e3de65cf4c34d6adb843237f667bcbd15a0e1c3741d1e35 902ff3521d6a240deca5ffa7393eab9de423f76f2bb6c2a943a62356fefc46d2 1bef01cdc0e086135f73877685e38ef29ca75b5a22c49cad063845b083aca87c a0b060bd49fbf582e9d8fde88ff9a31a3a99ec06987d46e3341ada8a4197d207 d424703c8aaf2742a9bcdffa696e66506f096f7d954e0c9daa44456432da5147 d96d15ce6110c63a835c51b36339b38c2e7796b607ee7fcde4586946356ac285 e097735c3f016c051979d502a2798dfe53b07ad4ddda6952c9e9a8f9a6d20442 ace0c235bb52fb8f05d173a9d2fa6391d6b90406772a6b9b7f49bfeece6e3f69 37696bf75f59caae537822b9f3e7032941881a8b8da963c9e1792c73c28624f8 90199a5d4131c8231a7c2dc8d6b04d4658feee367fb9c557bdc3855df2f6671f 47b9f55c734f06b97e6fc43d60db61abd440cc8f8034568a0639dc035e9c5e50 7149ccf00b341b72f521aba18106454638954727336e332257ea659035393980 281ac386fb4379a25449a0f8414a8a878cab5f715b95f93d8d93714c17c2df68 3e18b852bb6c97e9bb2eb43669f817d245067734b8c255ca5720fce9d91d2e19 b13ebcf6b9c2eda5aedeb8b416428bf80479728f6f0ce79b4cbe3baf4001f887 0455a7e089e82062f2d0aba322c6084de4eac9ae800ae3c45b86911ce72109f3 3a756fc2d5d54eedf96ec52ed9eded5f644efb64ca05d15d9b8a299cdcf63328 f0231a4d9b6caf4fe946e177d2cba1be1690cd2d5cf33ea959448670ad37be13 978c1471468d780eddc3d5936da2532cfbe9162cab3edbd1b76ebe7927ec38d2 9eaddae19d7131d298887815d7909d782a8e9834117395bd99e7b6dce9e69790 00bc800711f3ee4761f80e9c3d6c345be7232d243182b42a2f22180b0faaccea 794c2fef788fea6c7440e174c5faa1948bf06ac7e94824c920b7c875c515c279 b71a827dee1ea4e2051d9d545ca1a7064f888b6d7a7195d11ec854580caeeb0c 652bb9abc9f85405925a10efb6a5b8377ea8e8d6986312f1ea107ffcd464602c 414256bfd335cb0041156b3a6070a2707657a684b02eccbc9067422890f46c39 2ffb297359d244ca41cfe14b806877f875fb3adf7f731176cbcc7a4460359d8f c72fd1d12e5d04550ed678234f6afb1d521b02d3bfe4a8fcbd8ed1de991bc8c2 a607ece31100a140e9e0f4b63953a2f8221869a0299ead7c2a39b812a8321a3a 043e9d4173d20a21c5e59561916fe3fc2ba729b1d7bf4d57c5fa1391215d02fa 72deb014b8b250db90fd02030124b203d5aa6eb235eb42ef04c194ebbf05aa86 c8303399cf24fac2187e2c6408277a9173a894d2cb80e39af5093607b2846520 002050c149c4e7b6ce5f83549e446d9da03ad1738db8dca8c6ab3b3832158c24 db6b96b788b40d53f8d85f4a7dfb9bfeea6d55dcff6438fc644113a0a893f6f7 3e9dfc980ad7056da966d32e186ca449d62e890b1c4c31d2e66c37704e1840f4 854b3992a1e8afc40abd61227d4e321fbd99ae17dfceb4a3c1ada0a72fa09c0e 6deedfef7e20a06837ef4885d95d3c453b82dc2674041806dc8299ce2308e9b2 30e5757df447add32a6d8516a5510b6c58c1a53ef62111b1a3c0561eb9603954 ddc40ef5332ae78a9bdeac2bc20f2fc479c4becc490616cd629537381b1c3dd3 c9a62d12e53d78b49f339cf76394d0c0b1c48e977f8a34c6f8f1921b9c2d6c5d ea25b076dcfa16128b32d19eda9c2c3ca898347f301324e53871bed11d65c057 6358a3e7b14b13f9d845173eac8bdbb1f90615a2df46bbc734f75158bd1e8e5f 93e34fbe0c1ce510d289a808493d7fed8d9737a29bf8b908ecd811650609084e 8072b3427256daaa81b352793585d2ceac81254463adf2d6257fe4ad089e7dbe ea753015e00281309587f9baeda452e168b561289726a154ff0c0e01704326b9 e20f3102ae0dab9a60291ee43d90764b57857743cc269bd31befc9057815be21 28d01f1d3090e122e16736368a7a1159ab8e74d6c930ad21e59058914132e25e 923004f7ace870bf830e31999bfde81cddeb079cc4a0a0f6b73f955386b1d7f3 061777b1d92986db1260e9466c9ce32dd8ce09cc3797ef4afe8563f4813109f2 44947b6a652d9ae536938be4c46e7c46d8d7c96025316fd8a263166c21662a22 422d624de3d44ffe4cdd2cc12ab65358b8f3c7a6049bd2254f5766db1a1d98bc fa80717fe19119954f0a11b66c9e58c922f055639544daeb626646e2ff0c803f bb1c33fab4c88cecd9360a19f1fc2edb94e42c6ef24fb53ab30f010924aed0da cd98a8dbe67e54f9cb65dc06dc2b4f0513c85bf9011943637026182ae7243c59 740a659ccf48e7dd901b18215582366be37e9bce054dbcc39c0cc731f82eaa6b 8e0cad9583af9d3479671badd2e710f5477cdad7cc73e2cb861aa67a097c3790 d16933ea6756815179e44caef3a6930e15cbef789a0c79ac9b80fa224037c37f fc588b85e7b5c55cc5cb794a768c946f1583812973a29df5d06856108b482136 ac7e853c28a733671e231bd3f682d785ea17b2de9f8131ee6355e0e13096a276 021a2a1450e4c6bd1f6df084f432b8802f52a85d853ee8843a13ff4eeff28dfb b2ce7b4554da818d78c502f53a9b9693efadf1ee853d1d0cd3b6f742997a2094 d6633a248d421192ae77c3630ff8f7d9875857afd2725ad97c688b3c41a729fc e13d3d1705ed264a1e2f9d2f026b14a89412161be5f0fba403d9edd279e52c80 050857517be24cb822dec716a7ba07b8ff47dc67e6233ab96ad97464922536ee 5b0caf896a6c4ffbbc012265186a91cfdfd431d60242a8b976dd46b62db5171d 0e15dcb8263ef6f065255860a3ac97d1c50e47f9ae5d74d9ac365e1855d22db4 8ed8a94e2fbd76a1d07e3f65a18b6725ca088cabcaa19f014aa2e512aa1098e8 d02d3140405f2fe1bc49db02cbc8eec8eca3d1b9691ac56e8fed6f66e3fbf1fd 8f76f7c7f07d4ffc00f4ec436c43f51456e4b76d644ca565db3107017d05b721 47a80a45dc3a13be6c56f70b96bcb87ef339d56acb7c54578188104332a1472a 0827f722301d2ace25c856ab6d2f6dd33f54aed9d7451906bb62f6d94510d224 3ae25a4e44087f8b0e3912ea07d62997921edbbe565ee23bf4724823e399cde4 b330ee798eb4ea44a2cce609de7085f5d579de3081a144699c26b57122963ff9 5c50781b088f444ad71eae555a36b465b48dd59baebc1b2553ef7dc1525151d9 97d166a5e16096f37931fde632b863a0f67285cb2c2c5cd2a9e539b042f58a78 e072e20f34827509d54e4060f72c0b8ec26bf1697c86e04181155ae4a4f7773d d26681df96bdd78b4e73dafe5ca9b1847ac4ae6e76d8f7b46b24f9bfa7dfa7b5 eaf92e9656c5352e6da72db252005b096083b92129bb629dca0b98a8af824eac 29058d750be173fcf059631accfc8448095dbf1e074f7ae2d23c1069fe2b8bef f713b750cae8bf74ce2c7086b324ffe53c96b16a1089fa81130ed229b9e2949a 5bb21a234d1f04947b8b4d2189b62a7f10861a76309ee104f646ae0edf0cb5ba e7d3e55943e22c4ebfaceaeea48cba4e3cbaf3d065997d1acc40d3224c5004f3 40306a28b315a4dc419b89d43c503a85c0506b17953bea6bd1234b5aecacf821 33b21fc99cb2d650961fdd1d99e40a9a5ae75ac9323cda707c296afd41137730 f8c9e6e2a5ab7a4670aaf83f3c3accc699efd82b0be5926a5a2a1cdf6f4503f4 6752a646352b82baf6d1151e9ef4ccc3b3b7d71ec7e9b4c5592d0ca1de6ea60e f8333bc49c4e9a44fc7c3e6226aee0b9b0055bcba4da377a2dfc9c285e5893e0 6c2fe7af0567bd5a21824fdf55b67e50b6a8b8beed005ec195d2af30ed59776d 4ca297ede9d6a215074ece4e5c2a5cfa78ab9dc625f8f254b6f7b4f407e4faa0 3e12a675e89cbd338f470df9b9faab924f75d5f3519f69f5ab1206401d9adb66 024f2ebee048bedc88620801f9cc4910c46b1cf206c6a09e0245562230f6713a 3f06a7ff1ac9fdfa4853dabfafd312a6429fda926c9e5b55476ae78277883140 30d7452184f19f68497f3801978bb1c2b89f901411343c3019df0d242f8defb0 73aa7f0b2a670434ce7b2c91fb44555a64a23065741e2747cb61de3284701945 dd76f5748e213900b27854c04e94f784db8784e1c76fc2ea0d25e4e48c0f7588 387c999bfde79ccf3a9608fc74b97e482fbe122869b739e6f61d172fa4b090d0 cfac09708ab1652147076727e9aef0a0ee4e52048434c899f04d7d43e1428ec0 e04cc4fd67617aea39827cfbe42d17fa152b290a864d0d2af12a710c13e2d764 29a2bf70178240f42a1de4063224569c0baf8ffb121e7c0b146177bcb0baf7e8 bfde37f805a16cea0147e6e9bea9f30b17c66020de2b77bf1b17363ff2b4578d 920022cd555d3cc3108b39f71f486e20c8204cb77a47ac5804cf4520769d3fdb 7d25497654dbc7318e928e8cc7547968aa341d05f51815c5f27634b0397dd551 b21ccc6c992488edc81495ca7a010b01e6930597c19772d29931a9d172147749 e52df8a52de147ced2419d825204cb20651e218602ee1ec96e2a5c4248f8d8cd 8bd0e96ce6bfcb7128034d7e28dad17de220c406c213dbb5aefd6add69d8374c 89c086d334046c3f26780950a7950f46f7b30304eed8af9bef985ed8ae53c4f4 ac545c0c6822b6ce70ac5e0d9fab17fd8228196082e80a791929ed9c4c0f3c0c 8e64571effd742a3cc31b3467bf5caa7d248471ce1ea1c13a49501c1c9de6453 bd60439d7abfd8960f13e08858d5964b16b24394d8e921fe28a8930140f7caa0 3f7bf90da997cf57eca63e3a186ac9f5a4410ba010904ad57e322b783dc5e2e3 637b534d3c6e8f1bc417a4189cacb043012ced26bdde7217913df9e00c265489 558b4ccd96f48de001dfe66c959ad0ec7cc98c328b453451dc0e434ba1964dba f3ead097199decd0fac6d81435f04b7bee298145500567328109711147632e50 750005e0b7a46e1b3f74b9547dc3bb0200166a7f51090aeb3b191885e872e55c 67fb2410ba480398576ea1bbbb2bd16c39815f0ed1bc158d1023a4912021a7e8 e2dd6bac6bae8618acb3ea4fd532e9f39c5f6e0bb4e2b92f5df60f6d760c0f78 70738b912eed117f1d7fa033f22fee050931e08baefb7bd3b7769efdb3137bb1 d7465a58b479f1bbe07ff1b8c269190ea66f9ff3376cbbc9abaf0fe723395fb4 e7773fe8ec345ef0f0993c4f161773e72ff7b5c360b16f1537d82cfc02539055 d79164312d8e2cf1416650296303c06486f4e59bd0f5805eda4fdf23ca8c7ee0 95356ec5fa9484cedbdd235bbdcba7a786cc8fe16f2af63be8dfd8ab448d53a7 2b3231d3d9c15a9ab6b6c7973fdaea2003231040ca57d59f4364815e3b073852 487288dfafb742bcee521fbfbc2a5818a95ae40e8bb22d96a0f874a39c40b1da ee1de30e4bd10e76de5c13c69dcf9c1748c4a3eb2ced0e30789a1faeb1fee9f4 3c46119aad33602a7190feab3cfa7af06d69d52268e5b0cafe8265140f50a6bf 6ff7b008ddd1d1221e13170280df29563d026b6646126eddeefac426ec9264d3 ecbcb72e38f7622eaccef30b0ce3c0830722d9dc73c0a8c43efe879b2a0fea6c 5956b8c777125841a1865d79de843bc345d234f7617ca6589ab5440907bb0686 0bb9ef294c9298846693aa219d18b44f81ba5636bf1cfc24055e0f41ac4b184b 5b1e2d3547ea63bb59e80989e5b205ac965ea08b97bf2edcfb980262f2a55a56 ae02a5c2094928593ea4834cfd5b800a082ea68a84d59c8fc9c12f02daf43a43 d9ce40c47650b39584c6c2ce9d058581cc3ac12dee43591dc38c041d528e886d 68bb661d0c1b1afc0578a09cac237bbb2b1959b73d32b233455d7143728d4c78 1f40efd1cbeab6e67245e3483604dcb84b02e3f5dc602bf4e5c18094ea0f9ecd 94cd5758b0c2ce29222f2213a7f64be0a8adc3e8deb9171028f2a2d6e0b555c7 5d0847175a30965052c67bc24fb532d088da8c308b072e623b2e3f7a875db59d ab9ab4492aa7d3b55ba7d436cfa320bdae3e4ed611c18424c6c13746b16f362d 511d31a002e6042837488304cf238ebf11b0789681a2e6d333e03097e85b4b8b 075e17f5dede4d55af57c573a542f30d973ab0c1e679f991912de209f1f23a7a 5840b087585584be1308ad19077db42eb6e109c5d6988474887044d4110f5fa1 e91d6bb4d4f8c476f4ae15511c38d09b2e91a69e2908f2fcd5114a92d0932abe f356f5c583aca308223ed6d932bb8ce105715c7d5dc5d6900437f8436610fa90 9d89aabb82ed8cbfe6855533076fa2bd4aedb5d4caf2c32084e6727028bb1589 f670ebd7cbf4889e44570332a1ed514256b58e9a79eea1865c99f09573b526086a57dac512ea1b35c208b77a720c333352fabcbc7f968f72780bcec6adfca904a21d963ac9ed0223e9ee70ab5ffcd9be8ee2d60fe200dfc4649041383666a2026447676fd69a997e79fdf9addda6e929fb3dbba982bc0de161db58fa875ad400a2c30409fab52469cbd5da6b1b5ba70d9bb5fce41ab3805c7e9ccc396fa7c60099adbda9cc3bd5a81d333e7764926991eee4e471a4e6bfa105b59c6c09e4c00c58d009cdd0c1fd5a7efe2ac8833ac3e2cf09b650168a966340bcdfa9effc5a0ad51b22828d6da35f75b89eef7454ee812d4080621181932288fd2a3579d2ff05b85122df1d62907cbe8cc957be232304c6f1346a7d685fee60779e006252ab0ac8304f69d6808f1f19488aeee8641713ed91e552b7e7744683767377b9553e0d29965c86cce67b973f261cc9c2c560427df8df84db2625e799b6835e1ba3fd01bce6e1fbbe7f3c2c5c03654cd6c0b55d76cb7561128138dae5dba6c51b492a0322a30663f9042370ef644d66a7d857dab50384fd0a127a7f4b832b8a29248105559a7f6057553f0e2bf2504929bbfc5ec4d356118321e9f36a3873238c9eb703f8f60ca1b0a32be20f4bbba75e8fe8adb2513753eafb8f8456350e08b5eed20345633803fe5917fd802cb5d5d4d6e1e5f44247a26bfade270d29b2bb8282f70a58cb7e03944661c6ad25068edd734f5c8dcc12dc3475ad68c2570a490d658c080f12b55818374ae22abba866d678c66655825182d74c4c804b53655f9b75ed05341d0964d53a955ff691d4ec08d5898400cefa4115ae5c4f64db58f057f530075a9a651dc74c2765c7691217f332b601c186d2c0b1f73907a251b28daa2c47002ea0fd0326cfa61d971416dc5b5eade606ee525e15a435e7aa46169bd5e1da05af5183867b844650f457992de6f6c986a0d3388d8fdbbac85b60b693973d3a0faa76be466e9a196c6ef9230c9f6bd8e84f1bd9942bdcc70f77412e8ae306fa00c2289bba004e50ff09a1f372707f4a5ef9b26d82b888ceb19c59ff53cfb3df04feaa94f707e9ed480ea33eeb70c85fe1a94be7e2d91b72ad34233fe75e30e00caa8089df1b2325a68a38baa6ad1d7900e9de461bf772c86b4dbecc67328bc8094e8e74a5b4c9d314629e4a3b4aa2d2b2d48a85b2de86603a89a276d54ffa560789c34abf18712d356d6bdbfb2fe320245d0cca77531b1a6fdbf90f7383d0be0ec18ef09972350ec7c9c1c3f144f347f382d29898398a2c6973afb9cd9328fe0d7f8927a4295dfda44b9c646c84792980726835cc37c291093f53f5b5719a420d1b32dfdf9ad6d1d72d9779473c7dc4d052dcec01a4c74eb9e54260ac63f0f0081165bfbece485e3c984ef26ff69b345752ff60bbc1730faee5c57681bd50f0033fc9ee877e3bf03981b50b050a7908dc8a6c14b94a16e1ebf9b9722364eb050920f14ba652a036d02dd81db6b8ca53777633e259559bb7ec69c49b5c2f310a018226f8f85450cb5744150eacb95dce0aac0dd9fa4e833589db86d3ed9eb4110dda5915df9c52bb677939d83f3bc5a33d3add9d1894d725a64ade48f98f68fc0f4c21f193b1341e8904d7029a4673313262a68ee8c76ea34bdce69a24df67bb0f0816eb692599c09effd1f504d223dc9393fb8e53910e249af437d586630fe60dfc389d77000fe92bb914dec3bc9c80e76ef6503bbf6c790521d8e2cc5d65c00f0b7f6e987fe667043a014d19c6146d0d0e61149be379cdf83beeca2b5645560a9e9ede3ca6a02cb289820d66da0a4dd0fdcc1523d5526995b7047533b706d607d4604001d9e0514e25d147654d98024ce96a3dbc44f50d88287cfdd7f2a52d02304815058a6775f8d2ed38599ad9bf6f12d267d46f044e0371f3d9290d76630c264b00e26a90b508ea83961c2ee9d927fc53454d0a4f4941296c38c9c8ab260a9d7a2d8ad5e18fc816dea6618c1c8f7e433379a3d7463c0563620c82edd1e20252b2fe7014a7127ca492e27293eebc55bf06d34ca608059c9adcaaff107fbb0593539177c131beb7c8e94341d98f90ca2bd8d7e8396fc07012179d8ffa13800dea1216d1ee3424451f03c8f7ee0e1358fa3b4b9030639e234d9e3704adb8bb0633450a2c29faaa468b4ee40db32f2bbd87f6a943bf9f58429251203da7d6c50c7f1c7dceead5bc8eb96b470348980894d35296776fdd07916b8db08f2a883006c733b32f2defa6166baf375e2da02162abd982785b0218256cdc1f14ecea1b055e62225ff40a5201a13f29f30ef4c8672af8afa476ef26fdb8a168c437586a0092c0f037da0e6a929dd13ff8ead37b4bf5028a206c25e0965b5463accfc43c061f50795603fca3158ac6c7b54ce7291ee7aa6501e170756b05ea1efea3b4f30d69e9ee15218b6d906b5f21aba7a84c42857a84aa04d3799f203bae9bc4d61901140ed058d7dff9b42232900b01edef6134024c7054cfe6b9714a097a4fb30e02205e9462df106d02cdf978c01a1c04c2f7e5a2544b1b11b395bea98c5d49d40b204c5467579036505ccf6b4d17e96277a91fcfe06ef4bb13bf4c55bc7f33830d92112525d958b7040ffe1a8a1745fda5fcab0b694dff401d9fee03478ab7360cd5d2fee00cd4ec06f98021e08fe79ba8a72dbd87df8ad3144963e13c3579a70f219debdfa6fd3cf634c20fc459f4c9dc8976026210fc77d5dc189a487ed3a707110168e9d5790e5dc502119628b7410c9f1a3fbf38fa7350dc3592d4accb6101d022a78746ebfe518db4b21422fc5c71386f3ff8e10f3d8b74d4e925be103b0fbc121c499460bbc8249d7a5420b193e7759d6f5f54dc6a86bebefa6b805fb009fdc4b7a4b771947c65427ac448760f123d6d70b64deea9f967b864c44524da0d14104ad59dc6ee8a26433c1e27fb2ef809f089f3d08bdcffb712ed91345d1506ea0dc81af56343300a8b391f7d153338d3d58e768a111f2b6bfe2713bb2a1306b4dfbf4f4390515d403791839bcc682de4c695ec3afd862a83c394dcc56d17048ee9c0694ac27670366bd006671f3abb3bfa9e918c1732de28838469060bf306c39e347bd45a5216f3403fda088b7b45e4119002b79bb1c41b815115617dfb0335959f1ea326c129e9cb5a86305d2135bc8606814a3ec983a0756c3d1b5d8a0c7e1a2c8d4ef9b6ec2546abb0dc3038aecf4769c3d6475082cbca54d6833f1e0d14cc23ad5bd1d14f5e024e818c8a9323516af4fad6f0aa4a1143c5aefd12b20a8edda3d149ca4c91c169f583376f9b5043406efdf93af1ea3802c10d2d64d80694a63c2a0cb36baa90d9539a863ad964d33a1ebf770c55664aa1c42ba8cd81076db4fc7db80309b51d0e28eb814be5effdb4829fe20c5a6b986028bbc18f7303484e8c30189855cd277c0b7e7d0efbe3068f0003536d39911b14817ec6ee9d0d08b701788807f89ffeb41ddef93d8f1ebe26e1066cdd9854c6f5a235101d9d040463dfdc821467a1a90cccd0632c77e879aa4821ecd9d5ad3e841fbd87860b05f06ce89254e0a71a5ab2775b632ada16c1d34c2dabc27a5e8bbbd77071fe910cd845c66d779d1747a49b04d0daf0457af49767c703df518f17c13a12f57bcf0f2b8fa6aae65d4e36dc3ea396c54a42a285baef555e25554eb13cc5d320e1b10a45b8eaeb64801c14b74d91a09204e065554f080b022154d67c39d42574c03e0ee466a6faad7e240a1bece588453386a3b67e6396c6b261c19eca2ce561cb120eb403dd29ae9ce3610d97fcfaea9f38b198fe4ed3fbef8a15e0a5d1d6c50bbc07569f1efe6f29f4bed2c38410c2ef59b10a4186e93d8afdc03ad1b08f3ff48501e89775f65d5bc14a9b32a6cb636c217dbfd98bc15e72726197861ada7f1d4f097b859c3bc5999a27dcc1d9f90dedebadd669460641f88a848c30894d3bcf9f0b4d4a655277d6b2fd9f54c7726e4030e14e6b449b0f43ea35df6a029f392d150c25652795d3d8515d5d758cfd65ff9bee214733dabdf1381cfee2c851090f6104aad95173b66b36769ae085c7919dbdea65be364cb078dbb357293766c3765a0a5b09d2062ee2b1e36532c6f099570e08e8da38e9b185d41b414ec19f54f1c60950d76b303d266a884ff948f0d70eb1b458f4443738511450b20bd56b2875ee053092e9f6883973b633a03d73cd0119ed8353686feb58b718458b22fd79fb2d053e6476fb3cdb85180c83632669f159cb86b4fb5f1f1cb4816b6ac2878992a20d1fe6548f7b6b0c7bc31b7682ef5994b9eacd886aacfefa350b821e20a7891a066ebacc44185be82d107ceafd3853bd1a3ac330470e4838c43f6d88a5d8ba77036d39e452400dd9e296142eda8e68ae107915122a3e4654fe66ed8dcf85d7be05ed6fc87971d8d1a59b192665ed2686123d2f5881263ba5d809dc20cb4e21a00bcc0bc8614c22731bc92be6089f15b02e160b5cbe6f376d6935c277402f41ab03699830cff10b72e0ce2537f267e77361265cb38a1e8ed250bf870c265ac5ca0f1b470c49457eff9f8bf802915b78e8cc305faf5a8f53e443b5104abbe23a3f088189a49aa57dfa727a0a431e33f8b6875123cf7de7ebb2c2cb186951e27cd3004987d43048ad1a00b823a3969bb4b2d0625eaa78f1d6d03ee4b746254fb83602878dbbc695f542ee1c0d312d5a42023f445f86dd705b85a9a74069555e819c0b592b9affe09971b5421ed70b48bba524355fe126e9e9b14176fb4e3254ed2d0d741ca6a57d0b87712dc82f1f5534da8c88893130bf3214ca76d1a51aaf4b2f02745a492f1e3dac6632a5b439db862e3493f05a75823c88ffd0cd4568b45fc30d7656a99c5f81a8f9853d7ab43c3d038de2868186b49d993b36af0ec72b5c7e072eab2dce282da74d9403253131b1866aede835635738a8c6eac6a4b7f412920b43e82e50b6d4ad1f7b047e8a2fedb54a56d4ae63442dc6e0edde7eb2f5fe88003e878450e5f7d3a9e7369b158862c4c69b0323d6f6ebd3f0eb2723ae2fd67c09474f9e4635efb07d04917e3bc1a91e2d736e570946fd4de627de1a135aaf4f0d3ca9364d5de44648312929c116d9e756281f7f7b9cfb97edf36582a23116a407d81141823979349edd456bf51521ac60788f44d26afb72bc77b6ffee7f7f1408cf7ee227155232c6856b5f4b6894a1b033ed8051ac1e601955562c7f355a8c03c90f8859f3db89f45b962976a4ed33f66762e9f9d31317ca564d137ae3e581098990c287df98de85236a0b645f71b430e772a121e2638939e9d3405757420104dba111792f04af011bbe28594d4fb0a99aff40c8c47f3b5ac2583b984f880f01aed7d1d019f9b93c19fcd31ddda666b917d6a1d869f71df068dfc4b20261630e79d1a36a42ff0fdba31de82fddb5be85a3b0154a3d6fcbd89fde73ef5dea2d047bf735ff9c2afe17d430895bdb54224988f68ad661ba3c0c737fba0f53b4aa05b44413816cfc7c361a30cec7d4c602e15e2ce1e2bd1d5ac287581afb254dc70dea75f360ea66f5c85dd6a146a92210e3687afbf2da6c12f44484d47961bc1601bd536b1fbfdcf0e90b36bd5771e66ae8d1d4dfa87b2b530c9d86d877796883000d2f80e568110372655e686e2e3f060447ab54a16a43555444a8e9872285a50e6bc5dd4dc6b397ade7bd223b71bb9934b7ab0f774325c0973968eeec92209b055b4269e445d0590290adf19f29ae3aa99ca9456cb7b4124d8bcb7875774a4502045046a710a9ff06805e1d467948047ebe6e2ddd49004c66e7d14ac3743fff061c29db9fbf669b4155b7263d3bfcd7dc6378418a3af6b7c93c4b148903da0f01b4a7b7a445ad97751d7aab2cf79934cc63c29eef7c9dd0263cb5fdddf8b4ae0517cb93b9df337448f5edb35f603ee8e2a6e29c7f488a4786409d69aed34d450cb167684f6a62af2357284937cf0bc032c9ff6bd31d4af05d016a528da26c5e01b4cf2cb9d58636fee5e8ae914c287018df6795d8dbbb6c9dea2cdae461d4710e4fef717d06e0e8710edce10f971553299c177642b474ea0f7f54e5289fda8903d6b19924f77038f0144af1723469bc081cc74042508b1c6148f47b2fd55b0e0f90d6c895da4a35ffba9a2e0690c5b5bfff4bd4d220b6065a02a7c1cfd871830ce2a686293593f77aa08bd70d01e439b26098131093f23234b02d1c991b56e00e7bf68ed6421783ba698da7923838dee102be8f5214f4063f4c0e5f7c2ad2fd027c79a69152a163e4e7cb1dc85a204bae6ae8ffc8484a3f3f16cebea9de64a604827cbb2b01434e394c3a7bd6297e2f38132c66456e963552ccb501a4fa9a6f02a114e17e7531c1a95c611b336b74f6270447515a539c381dc25e2c638a7e350ebff19aa1559fa735d37e550ab471b724ee1d5f76c21fb42ade4bd4ad3bbc000d6d4ca031a199cf35ea121543904ee5ad059da47548130e9b43aaa0469beded088fbf91f62868bb1c8407b84b4e19058d7ce616201bb38a28606cf2db42e9860e781b2ec006a8e5ae642587273971e354e8e685822985c8bf1853533a05653d0b5a1d1610bf80b4c51ca6efaf65043137388eb49deaaf603af1d4e27ac9a20d0c4c8b1a49d10644fe98100977abdad5da0329b8bd1cd20f2ba1556fd2d04d7d088869788765601790498fdcc36cd175d740261abdab6bad1c402c267186bf030084582d8b6e43425fc3efa9533eb3182ba9d228627e8fb256c3983178dc18fd0b77a221fb2b39fc109477796d1a67b8249d322d3b05229a57830103ceacff42055277f8b5895daa3e79ecd663acc67aa2d35622ae6ae2fa1c5a817d330f93d70e0592db6b81531bb69a1b4cfe4465d2a559e2752602a0bde7534405b0e333c70e9b4b0f2e3233724987814613b4976687c55a5f26d5b3c8065cd22a9d57fd5a04849186c6363a0523ea1fbd73fa890491659a038d36f11974266ff493eb1c0c00bd277b4df3127aaf2b9677c7c0c37826e2a6217048b3ec23e65019782929c10290f7fb30a36ab24f84269c8102a724f20da096a356571f39765006862a25f60fbe2baabd241dbec9048c9303198fc05c2868fc6424c3cfd63f954974dc0bea0404cbe50c7c106af0f56b976c3ad2376d7c532a11f5e40290385026ada46d4c001a936cf7dfcd9860e6c77b6f7e5911c9e23d1cb11bc8cb0ec54eb7383293280601f4c75aba774cdb3abd85d0c2468cc74bf73483a611797db3e96607be70bd02697ca58ffcb2a0a8e1ff9860dba9b62c848a351f8d36c79c559b7077c87f270fd6e0731a58709c0a2d412c208b86ca9213338f3de434c382f7624662c186250379c5abc3e316bc2365382924b8935e873e6b98c362cd1653793f58bee9f8050c8f90042cfc2029db2a5e874ec7b0ed2b86907fbfdf2371b94b69d013aa05b30d94e0c4fa6e53256ac1fc50f033f8359012afd876e8a1872d99356d72e67c2805b3a32ddb3ee1e65c4e14acac55d337763127b4321863dbba19202bc16579fb00b64480e4d6c39fc0c8c89077bc46506b31c85ac9efe6c2afcf7f9a911fac4f01d4547c61f4e84aa2f1a3baf0a52687fa360061197f37ca1e3223df05502f4c016d7243de8cd14a971e9070d36f251c82203bc77e5c6f9e02099bc6ae156c99052ed58027feb172717e1956eea254abec7b54516063b4e4eb15342e36edda49015847f75a69e0a1877bd79e4a949e1ff0aef8497691b74edd01d5da655f04ee08db613754b124ee36de8eac43b5e89bf2e9b0605286d26c4264ba93a0cd7acf08f3b04ba8eb278ef22654f1a23dbf6436703de035687f9a0ab6c077b72da2c60f9ddb5bbedf6da71b6640796cd4ed6e523b6e33654183bdef361711d4e8fc240aa7e6d9b2fb09f551e7fd3b719a0147246ebe0e8f21569f26af6490a4c6cda909b6ee31472b20f20bb3aa250403b66d9093d9c17a2782606c4065596a17d9ab03e95bad80b84265377368d518f91c37c71d49f5717a25a3b811adc3ba85a8bc00eecb1c76950f1ffcabb4d8a4107fa3a3f9d15fb80c04f7a61d7e9d7190be030b659ddecb6d2d06c9228f279546045e21f80a78fe45b48c1da91d7ca4cd6c370347033fd1e39350c0d136d6105873b1bf8e360651ca9945644e844eb9af80a0083f1ff1afd77d59f99bcdb30bc52b3b454eba2443261dfbab9a28b2cb64537405767efce34164131bb24e836d40c99d7e2480e385a1fdcdf4ef1a104d7e4d790dd168bf3cb539261bf026cd08a5cd9f85d11719be91e6c127d54d812a86d47c09b7cecb8ffde0b0a98b4d8649a8d83b9595ffbc69d55624d83cda6b7f5945df001b4fd4451f8fa0f50c4b7d45c945fbf108058dedec859169467b96bd6af6070a8b915ea46a87f0a70a081c243d12e399dabee683f7d63c544bc2d3465f161d0e15330c1f9548f16faf5e59fcc217121d7ccbcbf531aad228ddf76d0546728e0a45fab1d4f60160fb1ac725f6655e366252f8828f8d59d2e9ae40f6fb39b6c600e87eb8389f337920e85d0ef56b6c607a01a23948559874a7deb7c85a217a2402afba431be57bf39cf297dda48f0fa0ad691ba940cf30c12731a110f6dc455002a031f3435c740d5ff5c46204bb980ebcc5644605fbc68ab49921222c80d25401a9b0b5a21f4cf301b8ba9c7d8db30839ed04a4dc35d03dc8f7bfc0b7f343bc0171770be074535ea17739d961e919e49ddf6983bb4860555142cbc6b164365806ae910e97726e9f70ed67fb7874655436d38f8dfee1dd51898c143e49ee5d170d3e3372b5774af3ff3d7b85810f610b45913356a2573183eecdd3ed09e63887077e08c05e3f843d6beb780fadb4f73a44c76fb63fa8552a71e6e5e517aa610e0d2f256339805d561fab6d2655f04ac28c8fad1fb02b24fc1ce88ccfddfa206c037fac861a517acf9c02b64411007999db6ab635b8d992a636f33e01d03f9e310581c6081aa009dc1f9969f86d0a9c86098a1b2b69cfb886fefd6ce230e7fe220b603c2bba6f32ccb4e130dc761ebbf650ca2c02084908424676ed14b1012e120b01556d93e537dc8438ae5e6eb13091513e94d9d705db12518f13a3c5d156dd0d293ecacdbd2fdfe665c06feec088d0315ed16c9fdb5007b7e9fde66738077b0b3599050cc7c6ae8362d6d522cc1d6e31a3c5a079de2d3f4a72fc4e8596fafa0125b0f93f708fe4de050899cf4a7744955d5ab2a4475c6d41748d79265afe250693ebb3f4f376eec46a4a4e56e3d0f4a2058b347840ca816db94ee17c4c693d077b6a46153e1fe32253ffd84b9911b169e53e2c94436cd36afbf2cb512c17d30392eaec17eec7df19df6ab75ef01c1be413008e3d1d93a58ca830b14e0d0145039f46a0e28584f960dd9da0b4c1a0a0f9179295af3ec9e548e69b5df5bc0aa806c78a5aad5ed055afac75b76227efa6b48eb493e2b5f8b8b496ed45c6155b92051aa438f90e7d4bc4a16781e494b423cbf9ea46ca8dbac1af961a943e353ff60b187c35a7c2d15f07d72621d3c145ec14a2272751b6445cfd932b33dc4a63220683b2284c2df10573ca0cdb2f9c66946c1b40547e74f8be81e189b07eee216b0936feb5a535f665b07e2266ebec3488aeabdd68a40d3f6a94a014a92877884301fa2440191a0889691048e2b04c9638379ea148e4403d5b2a876a14d769a9f20c9da950e2ddd4004dc3f57d2341b643a7968bf10a7ed0edee2beb15a80912db02efa927c7492324c0b58c50f59ffcd78572196a57c71c7580662877a4dbb09b0138336ca2760d40734f9096bf6278d6d1374cf06f0b3a7a7526545b1a8ff22b0c1dbe51e2524a7cc5e25e1b98a0b3f1417c7a1892e0debd9013f95ecfd7fbd70f9528c72962f9eb2bde46410755d5ca2cb50c8b72bdb512bbdc57c05f88eb2b0c97ce6266b241a4399c96be41b845f656f352fc9fa4bd39c050ab6ad4450c6601b7fb816f834446a900ad4e207bd71e15bb2e130df1bb764ce1ef3b7eac26d40f5d3e734abc6701098504fe767908b754a128da5bd221d11258c05332295d3e06b750a7d67b6677c411d08a30bbf5aceba81f82587c9cae89e839f6d2c2830c0d56a995c5d361f231973032c09fa9e0e6e8b585d0a2b46c8c06db22cb7a4ef60980d811a34d903e02e915e26d464061c5de42bf8a2ffb9430bb0a9f536b1e77024c0d140212caa64e50b41904467bc4eceba8d0029cbfc58f000c459e2317a50b43f376dd512a2529ef42b4b039bf22cee7c2dc011af48f52e3031397e032850062d8f8d7303e3606e01a2b3500714a8cc32a2317b317b94784d17c4ce5881709b81e557670538899d8a8caa3b95f137f3e10b993645c51e72e6bbbfa94ae690458ac706422f85e4885c351c557acc37966f4c43aa472e8c07d13a9b4dae58e0621fba4181c9ccb04b2c046646d75eccbdf388aba66e2ad77d854a918937b4004c2eac88ebd92dc07ee88433b775177bae322213aa49850dbd707b61e14b5480bcb76964c0d2871c19fc0beb0b6a4d5a50450918280ec58ca2ac376a608e75306b476efd824ec369df314e5e61f748e769045985ccd2b84677b60e16ad31c1a03c9a94d61e87df4219298d85375885badf04617f0d6e8e0195c09382271f08206819872c2107e34dcaa6251a681f739fcf814eae6db57fd731944f01ceb58880b55f17f3b87b4be68cb5e80976d27821edbe0d75a1c890990190995ae46cb670d7ead75976b26d6dd5e64743da3c8d10b4d443e989b90925213285430c16a410cb976d3bd05c06b31c3404f82b1580452571879e8fc30d24a8cecc7fa41b77e09adf14671c1207fad3eb4cc90498cdeae19ef9407d498556b2bd675e3ecfae007ccc2169ce72cb3adc1456fd300423c6c55d51537d43746ff5b1965f7c1d41803ecb0ba86966f6d7b1ae4df6af3f1cd2e7948a933727de5f0d7f04e7854c019070967db62d3aecb7e8c68008663489df72256503ef58e777c51c78d111cd9e5072e978dc0a5f35b9577e15528ef9ec12d333bc2cab04d7a54254a30bfaee4450a936e5cf6e299f3902bfb7dddb5a6dab90d0d4fe98127b4373c32d5dc427b070e4d7062213251c106c2b3e7a0653f048d81f465f245c2295222f7a76857c827098d5069d17a1d6a84a4f460d510b3493e37a1842273212684e08d8e212edd8d0adbcdcb0db13bfeb6898e4f10a6fcb21af6d79bfbe2b8ae9cea4ea916fb9c840641b581ecb536a9c3057f07dbcabda2a175e2ee9a31de1e4d2101a336fbcdd2072eac7f5736e8ead08cc453ea24802d5187c9c5af642c54ddfb61696d4842880f587fed2151ce21423258d01c41ae613b3e51ce213fc18de1c119603eb4922e04d224ec2abaae39d4dd213de13e6116d0e98f6a3a6b2cf982165799ae3485230d7cc151a3a5825b9124cec1dbf2e4894e6c0785aea831b42a838b8e7491d3bb0d5fd7651e994f2a9903e97637bb616bd6e3c5cd814ca2b001919180f9b25d1c020b2ec967a8a16fc007acbebf145994c80711776f9211624dd8d56984dc56c605c65e9a1cf680e740b4b5bac0c5a8ab1527ca008e120c4034a21d6fcf8188da010fd4375a73da01b6e80043ef35296119b3da5741865a3775778a7626f80de40d526a7f599ffe8e53b75c3ffdc5a9b1e304af6252d27f4541efdb6c8b331f94016b672eae6a46d9a0f21cc1b91e043b015eeda8c3700362af29bfcda82bfde705294634783c32b364a7d4e1fac0267eacf1302cae7f0d5fb88875ef6280330b00e1f680b719cae4063aaa53b83b6ae205ea49a98865f1ab31143fb0fc640f910d99c1c5e6a5aa779a6622a082b3c4e3727985e5129838e95934612d8588810e0e7e0f40270bbac47c818e4b94276eae59b4cfeac25fe5a22f09f692430f08c4002827b3d71f57ee5744fe565bf86928b47d582178f11b1e46faaa21710a841707b223ccd622838ed83275e99cf46a56a43c7b9fcc678143db6935e7100c44710d9c05e94633c2102627081e18058cafafc35432e77c18d8659fed26e2754c4403db51970994a4483a784e27833f23b3e20c79d5ae063824bbcc71564e34a02c0c0baa16fed9ba34610d8f12ec534bc839caca8da3ae6fb92c25f6965f19bc4e048a2fdd0f8eb6b4a75fcc0522ab33a6f15390a23ca43527ffec4d720a79e94309a249e3d40083c1f69bd48a48bfb41f8a6efde198676abdeafce64f7a3036dd01caacd56ee5a2b6df0a6cac307d9bbaad2742c29643b80b9d78eeeaf5bda5c30aaaa99b2320569dc3c6bcc61e728d6d0dce43111fb49b9e930e4a6d80444425017d957134bebf464599f82272616b2dcf19422c64f33505757e465cfb86f2b60056168c289cf3a4c6f870e40f90fbfbe35a1b568ffba2a8fffe9c10885dfd5005e7c9ad391dd986c9ae57905e5aeff798061e36824f5f22d263fda5b071daf600f58586f105fe53febe9210250d246b1d650112768be602ca88b14c64d3790f00efdc9e359b441cf4767738a5e7e5417130b16dabb5388f8f0d5a010c10c73408a91a062c086bc85e03e5c823c583098896e79b6523198255267ab031e8fb650aded15cc0d64dcdad4f467c0358d429f7d06ce6ef64654b34ce2ef37f4883ea02a29d07e8a1c809af701d2158c01788e92a06267bd4f63e1522cf2dd85c1b5c08c80bda4bc2c82ab6b71b3c670449bea6e677805873554336c519f3a95134de005e84ce65e8184a8bca720222f5416f759f104fe1fbe6ced3f1e6c2fc6a7a0708a79621efbb6fe3df08c771f0f57e79ec701852fa6750b5893de934a0908fbb0e3901bb247798da0aa515f571cf42ba84a483212f5c4c6616c36e4345d51ff90125f52c964f9fb5f99ef02b20dcc4044be8c9054a5a00a6c34376b7afa1aa9902295efeb16a02e9fc51f30579581dfd1785bff4885156f448985732498431e50dd32f8f6d9ec46d1b8370b52d75126d9954188ad5e54ae91363e4fa943f3c6a05dd5c9113716d8a56c410655afb490eeb2f072caad0a00b3aa33684af83782b08a9ca09bf178be15602135741350d5f368f3b26474f74d16dbbb0ccb5c9efb30f71f1d3b7d1552453d9613654aa619a0a15dabf1ab140f43b89f16d2baaaf550370358732f8a6b9201847c4355446d72b9d242b6a990f5b301799a194313a4401af025f183140cd252a6de76c404c3b5005f44d2dc0c7ad70e6e16cb2121db20c62c7fe4538729e54109805b2bd3c13754c9adf3fe352dd88ecf8bbcf3b019a00337bd5b2583c1ee2d7a4e52358ab673623e9d87483cc517b3f9a8953d181730c3df9809c16d32f71d03ae8ce8dbd2d700e06567632dffa290c01f4e144c61d07f735cb973d14554ea57b8fd602d84eb6500fe05df4da5de8793e7e040a862c06fe2860ab33230e503d25d6617c375f765efb1d4132afaf14378734c4b756d609895d48c47d69e6237b5c5380b2ab6cf3a8db05a552bc190388bb4f5269b0980c89caf3618abdcf73f57d8ccd14918847c78ed1d1b60e429e5b6b613cdc08e908f3109c368896b071879e43736d180fe6c66b9ec019f89052340dc22ea72ff70e6175321cf21b25bf240af51b67e63f964f1c13631678e97c72aed25e3036fa0657f723a45fd6a189a82c3b3b3359fd32bbe1cf51a4f411c13486d2df6c849701ee50fb1400ec09e745488fc6f593c67394828e9320563c85df2f70652bc18a064c8366da35cd1df06a66602d95ef44004744b890c80710c11b299d28cdecef04ca0506bbc1edfbb8f820de3547a8edcd1f5d89b4b7b6fddda85475662221500be015c692cc1b4d099679e7a475f1541bc9a98be75db07cd133d459b6c33456058cefe4b980cd78eff7091d4ac86a004f7004bca4752bbed726ccdb3d2ac40b02eb23f03161e0362d30e39df73ee46340ca68df8088786e18ccfbb326156dc80a21922e278612701626af92709e6609f62a1d1a7f92c421d8104eaec9cb580506a032a6d39b2ea905a885ce6160fe12318b8b699fa0538f73f37758a0a5949d0c50a0fa75c11421e2070a9f4a56bbb7cbd7ee754a0803746ea4b65189c8000b052fd17069805486dc266170be577cc7a045094e7ebf8a665e90a76ab906ae1900dc49d10e4cae98286d7ea2fa69015da41e4880981330eb1c48a9e0f592b74c08beed5b5a5144ba32001d48043b186fb07fdb12249a8d71f2b99bb687378aa401384eb6641126995711f72bc0ff96995737201ad07875b2a8389ace280fa1ef06ac69d324a9a7ac2f7d16cccb4a0d8c19ef6895dd0be8510de4828db2a6d05b085a5d0ef70e6910834f7101c5fa3fa34f9559dfb77e035b4bd2ca599d429df001a5fe8dfd2b997e606555dd45cd129ff36ef65f007bdc478970639f3b0d3ecf0f2bb02c7209797efefe2ed1c6b38aa605a644e54316b1a857c78b8e5d04d7f7087950ad24bf3d2f7eca27784e56579266f8d971d1f5e311aa724cada442fb7e0b7afa0da83aca20ec6d9d6b288179c688b63897bed10a691d53f457c520de2b04442c83e7225adb47e0fdd3485b5eb94f0761de446d49009890ca955a336ebc03152e4ebad0979eee57620c020c415c440a437c592524ac4152b9f61fd01c54095a5cad903251efa071adfed579e9312490dfe3e3ffe651f8c09812614d0e4f00828f8efcf42c6332ad2b9d32971d2ee9e9d9d8db6ad4cdba109ea45d7e111405c6be516491a08acd3bcd9c6bb3af5b9a064b67459036028b8cec5c4e72e0d60f835ca3a65352323d8ad3e5df7aafe3cb7619cfdb20315a9bb295fc0a7fdeff08208f89898b43636ea92cf1e72613ba056fb9eadb86cc06bfacb6ca4bec000c07ca8d0e8bb4e594b5765893c192836b9f636d8d229103029a051231624cd2c904f4a77530ce1070fd252ef22cbfab3f03235ed6b5d559a9cab34cbaa38f5c8f0df0ab6406ab4e2ff565cf3c64adebc97465871b693adff8e9b3ada266ee7f8008 true -check_ring_signature 7871239e77c22024352e29bf8ca806daa871f94fbcd1d0dbb895ccac20885113 fee442b996fc6abfc36fed87e14cf15c0b1a5282681b9842d4959a6186443c18 35 097ee5596d50914a88d7e057ab331e4fdbe00c02c1b32ba94d8ea63867f1d060 14d001b8d1773bfe0514b94690d2eacf3cd961e735fcaf5190421fd236783997 fdc75d44f498a731741e2f383b1c93a1b99720a91cb2c7b7800fd0c6eb5d6f32 a51e4f33a03bad44d941225e3af9aebf1ea8b18ebacc8f602e859551ba6e8c96 59c4cc41ddd92b02ed017bbc84edeeb435383bea321c544b62d414de6a872b17 c02ee00e75ec6e3e89920e49e8e7d39ee29e2f0a145fdb955367fcab47167bc9 e06c6daedc49e94d0a1abe45717d0f29a5b353fd6471fbc1645c10f7be1a88ac 89615760889222c9698c86da2117fb56fd05a40d08c6fc6bb55e7fd1ae39d364 d82c5041cd4082f0cc6e9d481bc7f83e869d9b3d53a9e36887a041eee25a31fe a475b9dc32a82ce4d71195aa2e62bc02a482a14df3cebbd9cc4f87355cdd3351 69bcc2fb715fa45951d15bebb216d90049211f3b380c176c7f071333b1aea415 b1b799f40b8074cb0d38ba83bed52b8eb7d2f65737e8b38b487f30da78f94c12 1995c132afd6eb9a5517a522c540e3d0d6d2cdb8306cdb847e7012b11d4ac06b 306ee41116d41166afb665912a624066d56d670e7bab015214afb3b086131d59 e12b90021631083e9c18c8b1f44936eea590421cd069bc97b9eb151c656bb061 512ffca190bf52c55a9f4b16f4122df4433930ac7c9e7ef1a99eeefdf914881d ab03dd9fa1083214738e799ede12707ef7dd20d59353e5efb0bd8f1581406b27 264e9fefa787ecac1eb124814e42875f3dcb30c9808efb0b5cdae76b08cd353e 2c24eb34db98abef3c2753e95cf56f66dd948e67d9b2297373668bdca270959a 85b2ba793b34080fad970054737ae998dff01441900344986f782b6c07d5c51f a11b878582ef80b562549d0f6b98c4dc1a3c6dc0ce2e59fd1c351419d8d6ad24 ad95144a93ae44a1082ce51f9ffe803ec9f53cfab87fc2a6503336cd41d402d3 00ff9e7b994d159cc61e4712eded4d19ce8ebbc040e1183e1b66719a409adede f2c24072c829bcb92a279924b6b158385bdb187838df985a0a25717c8d162fd0 61bda32f2dfc021490cf52f80d545dab95f71805a7b7964ab4c4adc96996d513 880dd455eae7b8b3900b97d758f9cf71d234dc1a80b62081f0746adecbfc9336 5d80ecdb557c4d58703d97807959137ce73f3bb5c4006517a96a808f10233331 dce29e9450526853691d0d9387a18d6b1036596b0d0f848fcc23c66965886163 4857b82a094be63d9ed4121ffd64f2e3594a08e5b52c17dcc95f0d0a895be3ea 7dbcdb8f2ce3cdf376252a77c0830735cdb9f50162760c24b5cea122ebca517c 10fc2ecbfa8bae0e0a02f868d4125da61133653a4bbba8316bc81091163a5fd0 c007e3ab85ed28a50fe55f0b31960f82f83e5d2526b18de25825be4004148828 5aaf1362388e43d398f79df7446e27d34ae8cbb387d2443367362ffe7205d943 ad738e1dcfaa47df88c551df3594d33f2a0dc4d8c6b69d0f8018ec670322bdeb 51ae47866b1e36a828876fa65808567f67e891cb7075976e6f8b5aad25eea7a7 feb31c3ff62abad6e18789570df722c607daba4750439fdff338fdee5780de0a63e0b757c622aa96035f1ee22222034b246f94f94f443a2010a5d33482f1c9017e445cdcbefbf614ab78e8a138587f03981a0cd2e43145e64dfcbc63faba510094db45843f5164d29d8e510d20632261c9f74bb584dd3b2c0fcce77158040e0ad52fce8f1c26ace32b15c9687320133d4ad31166c2e4fd7acd2fd4dfe8ebeb0d09d0a00185d48610269f8afdf5034acd476fc05ced24d026b4a79eae937f24082b9a7b44f9bff901ebdf08c2fcbfefd20ed15b87c4beed06811419936171210a4c5f7e05f47e340d1f888724fae7ab82b4009b31f855518e556d0cbc6c666b0c8dc66c8ccc48bf91b16379d8c0ec6a5d9d7714ebfd226147b014d6f090321c03f8d442306e09a775d83a582324fb15c74ba611c177f302844cd49be7f2ac880017af97826c396f2adf9ee92354c3636398d499d6bf612c5fd6a56bb756d2380796f6f16d1467f5c025ee2e471c2bad43db9793da63c39cb113d82081f3f267091bc844863e6dc22e58d53dd7935471114f9092e2c280f8a8a510c9959b82f901952dc0389e8caf28b630e11c01a358db78f1cb69401be54ee23cef1de5fcc904cbee92c6389caa4d317271515568f15a81c4da47c4eb367371a90945bbff410f5c28587cb3eb420cc6303cc46c0ba6140d793e7f1328e54987facdeb3295d001a5317fa62ec7e4e461516a6175b6b4d9db2491dee0b6b89cbe607c7892c5590fd4c2ae3e634d0b2f4f1c221c9766e6958ca71c4ba70dca2a85eebd92660ce004f797d5ae9b24d3f417cd56b5d2ebce8d7901ed59615ade34218069d32d444f0ac557b5202438d8747084d838081d774d144b2370fb94588974b1feca0ada4f065b1685d9ebcfefdf7b64f85ab427384b128f45211f0c4b2b47a9b78d84199b01fecdb348ab7cefc494015f0d9e6839dfa8599459e76174fed5c8835e8920e200dd333f759fb439b3fe46dd091c3423a8a9462004c61afaa74b57a8a6bdab290759d2733660d65cbab07532d8bbce98f5c2dba4a9170d4c3bf405fc7f6633b70d5008c67faf716841f6f5906b5582d8d9f43bc187252f5b6ee728559f6de2f0068402dd4db8cf796cb7b86e1f083c187abad78b3a2c58f09c3d66177b740bb209c3b12fe142360f19d1569bc2c60e8a51043cc151f7f40abad873a79e08be85054e28047318b13b239460b62db46d2f6217b27d961f92701f41e747422844650ec9af91512cf2261ef50b6196ad0aae59cb8ee6d17f6b0f406171a9eb9028c208c63076f0be1010a70f7f4c5cee64e13efd15cd0662525b99362f0227a336740220203bbc17d1b232acf4da4331e5037cb4a59c4a61b4259134097b9c3c4f8f041030c25f11226abd7aea38899cbd5bc263b4aa9575da5ca0fdfa80b8e91503001bf34dcf0db45430ae1f76fcddedee6c42fc60dc22dc8b37cf25f26e6f21190ddc22bc126a41c11edad5608c2959d204a2e89f76f906503961f3683ed445570ab2cdc727535edbb09d72022d7634a68d0ec147ce01691983d2fcfa55522cf707df4d451612240acc73eae9f71919cd4d0e6a1edfa935406021a3c9326e73f402729044d1821228ac1176f278b19636651e84bbb295ca2b56b2128866cb95f80c672038819f7dd3233c22bc9dec3d2e4d66928422497c842c0b64c0f9fc0ae3028d23961496f9bc5d0775be4571c1b563f73cd2a2796a62144c5877074d86970d9a7073c190565b44f8165c80c82e9dc5c2a3907ac6b282d86b9ef26942ba6c0f8dc4e0d97d05f0218d38af4a23bc751804bfbdd734243d824e0a36ebfbc4b303981648baf13d64f2737683fb4cbf23ad458b3db22924945330ab1cd6f8d3050e53b86f2e121fd134585d82b5a96307fa1ada2fdf3bf0b5dfe465b05705ef7e043f69430d43d7a7ce36aa37157aef35ac47588f20318b6fd94a3ff587af0907024e861cad4907abeacb32396d167fe00b9fa0514ea8d539f357ceb96e3668d906b5940dcbaa33ce3f52f5254ce2b2548e111041c316a1944270b7b3a1088b2707ddd3c5a91edff79f4d670551af6da825d42bdb9607a67be0d17a605ef3caad0d39a19523cda86828ccebc68f748abe14cb59ae7f1f23595e48b3d71d8c4f30081e1df26c4fd830e0aa66da7320e735047812bf0e2ad4bc6beb8ca634655da304a300636bd2ba23efdc147db6d2d0ccc164aeeece85fbe9e43486c51293864a06e712ac3b7662eafe2ba8321b15dd05b89a4c3cf2cf03fa9aba8729e988a9c40f95219c93df1e595ad07ee970c9a18729a0a204556a039c8f72f37d0ba2a1bd0b394b6f19ef695bf6d3922ed7783e1d998349fb9bd2b1de4d60e5c9a854ecdb08734fc295363eed8a58be3ede67085d8e7fdc8a499b51882b5366b0d6062f550bba7f8b582c8fcbe1e98a1fb31c08d3a3eb933080302ba3dfbd7baece68a8f80fe73ea4b78fef0ceadcd4408d9060f7dc25fbb6d25f8e1966bbc6c831b493d4015637fcb71442cf217960fa7d47788d9753fb809c495a4ef56a54a08cadeab80893c26f2ec3e77a3a8bf51b8ff445fdc5157747ea25f651fffec4c01d1ebd17092603ecaddfaae006c2364bf253cc0fe8adef3d44fa9df7aa0209442356674f0874c903b6298469207be1fa496ee0043772671fbbab13ab716ded83866b69fd003811f3fe984c454f5ee9b1530a3321c198057ce78650030456701c4024a4bb01de3e63fd6b99ad8a5eaee1b4a9517ef81ee842b9434cee997dd66de81026a80c7808f9eb30836377807b728a821377d4494723426b191ad37799b909b22e5402514180c0a725aa98c9e7c742650a97b35906b43b2c17238da1059c198e21a20c08af036fb491c3aa88073ca4d9ae48b192a292a32dbf0c905befe24787b5400a2efcd11c865963ef394dd689593e733889eb7e08984b5db5be10260feac52d0411808aee3cce6a51667632e17ca71745b1814a644048aaa0e967e0dd73609a0d9de402b64f50654a7928d1f5359582ecee406054dfa65c17887213e8f476ab04225fb7d9cfde9500d95ce6532986afbdc0da9ebba88e0b73a070d515111d5e0dbb8d24bb58e2509679e4688e11961e4e191be38dd1a34337696b524f1d0c3209 true -check_ring_signature f11cf1153c8f29ac492642f58bb63b07ac63c960fb5d296658d9d6bf549e510b 71588604aac03e02eaa35e87658b6b4f71e0594efe7ed0ebfaf4f1155fb6d9e9 1 2313c6cedc391719015f705d13bf83a870f369a005a0ddc62172d55f0a9cf2ca fda6cf89d9c29b8cc564a5d8cad10e2dd933d7725eab14debd0fffcea6d367f81e3cdb03085a9a886142165e78f6104dac9b6bad06b2bfa0fbc00dd0ff99c505 false -check_ring_signature 89f5095ef674c7261ca4310b9e3392ff4c95f5632fa88e28cf280ecf132a897e 8ef5b67637340e1df2b8c697eea6827147c58667069fb5babf16aeeaeb960b74 67 840fbae8d33d992827bb9fbd7809c8ad244f555fae2add549db027edc5e003d6 c5234f9a664cb3ee6f6a3c1ba9822ca50eac60c4dbba17b8e40a93512dac5813 20e5d6e367930d3e652bd18e0ee34995324d6074e68ee3ca10c1c525c84c0069 201274fd52e10a0d7a7067c5b0fe4d1793cbb68c604a702412886f62cef71a5a 836a05487f36eaf5fd399bce705208b484a5c74905108477bc71cc8604538657 53f29df5d204cbd28f3f96000af4c84436701467309c727ed7a7d46fab3cffb0 6eef33e15bc031c710ef658fda5a70720d12d84b0fbe5657f1dd40459f356a4e 5281a235542d9d6078f4318b539b08e520d3bf4d57d9bc27d9a30c904fa32256 0c1d28a2b6634cc538b98c3528840d41f0d764ccb31bfb530d5d54d6a9fccbd0 197acb11b88a16d9221e2a9d335891fd0d18a020801c58daa6a6875a1a089d74 2dc009f4c14ac1c6e10de361aaae3cbafacca0a9f59d4e8e771292fbe22082c7 0f7ccdb0c714c9a94f3cf057753ea47cf83745a0c7094599cc18fc0fbe8f9ec8 53e2ba89e04eb8b20398354e5d4f93fd7843c444cb70f0617980db70949a4e05 465102f9665b7ce0608c7035474ff568bf257da8332e281e47747da030284e26 7fa9ea02535178e7d0597d2a5a4862afdf84f52fe25d7f9663d48d16193ca5a8 770a82f9487f170cbe99cf73ab4b01eba11fdfd382d112b8cb752562830b726a 6d521cd3d4e5af7298004dedf402ea4cded069a955c254614452f198b0c2aab3 882ca4b551bdf4f81d8b0a5754748d34f0ce4cd4692b87fab9e10de42dc1a4dc 267c1d615da8d9298814f5147f3dc17e142625ca4b0e7658e9605b369c3fbb5b a5991a138467cf7b038c3e6f47296a861b790c457669a797a2245e94a18c7d95 7fc8f2b473d834699fbe6497d2c0444f8a56f92f872f4b4ec26ebadfa0bd2509 d4c328042a7c74d0c25b80e01e3b82ffc5b5bca0dd83a3fc5bd158372c52812d 11e1e0d784461e15c8d22c6f4f031f35d2069dbc2c69b06b6cf64aeb5fd84138 ec1e9be896cf45fd57c92ea70c1d077bd28fc032a98c4394e983ede3741225c7 f4ae4c01470045b26a97b2ed5d0a67b548e5c4a54d8a8025333eeadce37d7dad 9c421cc2dfe01eeaa536acc781ec182848ebf6b223c7fe68cd813d25876bc81f 7016d344ba91f899be3873ee093acfef9e60f960a6588abf6a68e43ecd4f48fe e53161724ed99d4ab897a7f96bfff00b60067c4401b64f438988fb51adc533ee 73bd60e766b02a40803d9b137247e4c8420a9c770c3bdc26f00e5672c5495034 8aa23f41871809f8c41f1495fa5ec59d797d0a4ef25636ab154766ac1490d92b cd7d8dcada79db10b50575f3fa6c565ba0cac966a967db6ec6592c7f234b8398 ed4c9022c99de916f18f02e28e54b8809fc8c1832be01a85544c62f8c2f19bcb 8652e7a027a2c2249a5edafbbfbe9e2edeaf79fa14d1205617f00e5e85b2edfa 8c00328b235b8eb6a79c645de2127c63274b056acc177a031e02c193bb92ec97 1a0afd1e9ac101f5d467dcb7ba788cc7e1ff453794498823b97083e9481fc9ba 6f671f997ffda94ac75990371d55e3e4bee2d8e2441a59cf4abf7a2078167d71 f9bff09ce4f59a37d218a574e2e3641ce25e14472038132b9aa7cc32e7db8759 0ec75fd15fa09e57d58a519d3a90d5374b87c02dc147bf87e6da099168b2100e 28c11260cc67b265bd88228898945ffc74ca5fb2a73f466d69a7524890f79044 b70e1f2dbb612a7d30bcc6c9503d628a61df7b4322d856e827709b46f154a888 2245d22df03dede9d73a4f4cbf60c6c106fb60c675a7a3638ad0a11d7746c81d df73543772382cf3e83accf3d25bc3f84f736c285c8fa768eabe8decdacafd56 a9ca4120fa41088a06e41768cb5a19f6d732dee692854bae91ac9905681640b6 9653d855d1404341258f0627ba5acaaf8d8337737e35099fda3d18b52cf6f00e d4e5dbd30f57a7af35ed5878585291344100f7d8b8965ebf8b706a8246240035 5c2685a89611f08658c86edcae7503a8f8b6bfc66b2c4b2501cfb4542d34edb9 7b6cfcfa1bbdad57d97fbb478576c31a5ed3040f038b4b922ce23a5041483401 73f0de280b5ffc466c0b207d769f68fe6fe9b1ada59837039746c8bb560b5aaf aad8c684815b77a53f668dccaf97b3f7acd8565d5015272f474218f17627404a 46ad714d929b965bdabdfc29f91bc96fa005af7d2c4671a2296bcb412752dafc 262d04d2db36c3f3af903582c6696fa71c4142f6b83c0165ac4b552709ac7d0c 2bab7b1f03d8e99f417645119d109ddc044bbff187b22143c68cf0d2b3035934 f02e64c34b007463748e1ae402c321b2f4652318e0614d4e98d91f7c9003865c 17b921f9ccdeceaf04788726d3da8f7795a76dfb199f8ea10423339a3070efe7 ba78f1cae118ab264f1ad712be3bc19a47612c9fa98ff7ceeed9cd53a0988e4f 7aabb4527e05a1e0e763297f8065c871c3139b16c2b0855b8d2919e0c846b4f0 5894d3986241bd16f8b2a04be100cc914a73937025ff496521c43a9cbd7216ba ff60902a5040f0eb572d7ebbdc351cabff608f36cd15188740fe13b6d75eabe7 f46923f940e61af0bb319ce55bfda1e8e2bb465c47811af068c212209f55b0e8 61524d54bcb145db2dd705f1572022ab875f079a0f224dcf0ffd600ec68c9433 c3f9657ddceb1167249b429c54a0d57e8b70671ca0d9c5904816b004c9f8760d a4117b6472f6cfde5bab2a93fdb5434d866c843232dc32687b8ec974345e342d 71cc5eb98f5e4b0d7477d5209ba9483c53c24f93f9bea5f0a9bdb7c2323722f4 8debd1e4ed34c67945e5dbbf607dfff86867a63b7ed80f08624ee3b4d21d7dee 319da0f1ea08b44df16d754bff6b215d9f21ee207a5b69e6e664e4244ab22ff7 a22dcafd432b829e8aa5a4b95991a2a782f10704e6d785be97e14208fbb7e458 0c0ec18c4b8f3f74f56637d537505ae598c8c805c6daed2ad3f4562ae805bc12 af45700a80b9f68efe63695168b48a2ba17ba2cb40e81c3ce765fea232e2cf0150285d0eb84383de073539aa099615c1b9e838bb9a7eef6d3adf44845c68b20402eb9081147fcbbbce61b976da2a219653d11e6203787e83c9b5dea2e5d3110078f7279af25acc46ddfbcdba335f0eaea148d46f1757718aa323ae42bf5a940bb4ba2fae2b40fc57e1048af48c57c9d94166ddbf5748382c85452a458724af0776deb26529b04c0ecec41ee9b661ac5d09c82453b48316a0d244ef249b9b4005970262912f8bc1390d76eb78d84faaa75816a03203574fa9b2a10fc35de26d079c059767f18614194bbee818592c3d4a0eb99fefb06068f6e3f24b4174d884025ad50687824bd35f88790aaeaedc833141588b1bd71fd4302b7113795b7f7900d1c32b9eed96ee6f6ad42a6db66e7099cbd8ea862bc45a2f5680706523dc3002e74c7c9bf94f39ff91f05e83e0242b011a8c9031364acbc473194b7f81557f0c0f04508118e1df5fe4b752516b30d647ba39dcc0d6db4a378a27b9e0eabd3c01afd7bb05dbb079ddbd4f4a1feb4a9220f8a6d29ca9c114075ab0a620fb40c20fde331c082a5b210ef9c8c42b8fc8a22d4194d20061817cf64457361ebdc58609faf5c36583f28a6490bc50e1b5746a8b154ff40d363c5712bab8c447c08238023048ff3e71f1ddfa1adbc9dfd1df16b5dab14c26f82024dd1810248b9ba1e102e3349cbc783f59b216805bed671b1a789d4c5036412e1997c60c6fd73614520821c64aad21340646cc95f9ab3e0d222487446d8d55807f89d930a3f3f10c260a3ea8553a3a51499e509ca09530fbb1869ff7971428f59b2443c4a4561e317300703ccca67ba88d211666e439f80d2cb9cc6dd0364dc0b0fba8d82e4890d7c90e8edcf9b2c2216c0780039725a6302e9d0edf909a12c69d70867c88a69aba4a039bb41e058ec495b856d30d3a06b284477ee925651d530bfbb2305d0bbf5d6a069e74a23b3ae869887b249480d8a72a8b307e7748261034a95db50645afae480fc46924abf625ee3acf23fd0556437a3033b6cfffe22ca66a03eb9a799a8c8e0641878ccb34b52dfc9c6d4578e9910fde68356af9d160b6aab907c97d818bee0132251745ea80baaca22b26ea112072cfc23c5aca73d9e0b1fbbe4c8101584903150cc8a7b1c208e395f079f5f1dde762bc3b14f5edb411d8c12631af70716a0fcf6c0ad313ef5e4dfa70bf1c4010381f14d310b7a91949b3d4ef0c9e32ab450e5a62f2575656fbfab7770b06a04f2ed6a3ace3c5d1c90df3f2615b15613d150eb83523ace2a527834de58bf559835fc7f35de9fcca5165dcb3d4fddb0acfb401ddbbfffd91e2de8ce444e148809a6f815880f6678aed25908ea4101c7bd20f0836cbee3a400fea014b81d261cafcc2a55c7e5300dca92989191fdfc3c3a3820ad1bdf8689af6a70bb4dc11dc16aaf3bc8424d78ac9b753cdd149d5fb77150d0445f94a0c94cffa659efe328206e1d5df8240745eef765f52c14904381b578c0a5b1473c95257c71632d928ea598d8ab6517f13d02b50bc835de55b3de94b000fa8265a5862325e2126bae9e861ebf24b0b9622e0768e0508e64ec310250281047e6dfebaf7742da5d09d44a168121f8e0c8a888f5abde6fafb053c568c43ff05f10516b3fa6f0e6c050fa62228e12e17628767d2a77852d71189d64311033201bf115b676138250baaed4b14ce18352b26b92f0afe02f9324edcbce4a19ba60d23f8fdb3d8be00c016fef022e5d9e3982ff28f4aafe8bed5002252b91c60cfd45bd9105184b41f1d9f84f79a6a574e48b75ca9e000e95f1fac53a6b75f35ce0379ba703453c6202535a023fcb5859ca9e4800f8149bea4b01caa0ca7bf5a1200759cb6893c6d8539958314da4ef6917ab34ad9460db9090a00f0728b50db770dcc1eab004f7a3269900937706f18ed4d982fc8e49c41604111f7e109715d150348976035ef26d2fdcc4080772214a7273e94de0f765bdede81c69e9dc4a9930ef85ee7fde5176b6d9fef655367440e034d5b37c48ec98b9ba061c5c3e1cd720c29b90589b282d7a041482b9aaa4a13a210e5033b507bd8a0e4efffd4b7c2b5047baa6bbc130b1ec0e46690ee2a3f1fb84799c70f648c94b7227432013e100108bd63af08671284662c687b0086b0c8dbcfda5ca82a2cf5d6636fc86c025d030198b3df8c9e5ccac045e6ec049b5fa54306831d62d7bcd1dc1c2d00ace50fac049e6f5db9aa78c73dfb95cd3042d8b88cc512810d15958124c58c527d674d6b02980d5531ea3afe44ae1a70de62cb057f448602ee52ab4ac1222b5c8b76f11e0f4394097d7d4ece03e017817a10d70c2b66b6a6225874dd437f537394732e830a78f80140879a6a2385eeb57c648ad259efdefb739cd54bf4677769201e56cd041e05e10ff10d4ad17aa8041a48292a850a38b721919af0353eddfeb1608cbe09c0aef03b125c2a32f8b222de083ef646762641c6d51242340b0ba3d2fd31950a696027e45a8e123122626c8a96a8fb338be657954c9a76ded521a26600ccf70010dffa5580d6bb58a57b039eccb6219eabd8db7674d1120c9fae05a6f1b74701c13b41493720cabef5f726604b25acbbf28f5331a1b7cc46d9bf24bd1c3b940ae1bd2aca26332e458a5ad27221bd9565a8b96a496d8516b11ad218cfed0c4f0ab0647ca947f55a6aa9a417c948dbfc0fc927e37dc049acfc6594260e493a3508a885b987bd4cddef6f51c52d1a7e0832f03c6d8a08e757be8e0c8bfadd451705fbb77e6c30cf484ed864de8fd832a57a9291ee3c025f5203462cce1eceb7a601629c33cb826d8b966f64156b56ee9bdcdc44e624788c95afafc0cad3b0091405fccfe15df2839d961aefa4100d1ce6d18f82aa257ab5c88b7b484058f66ef20088e76346d8b425a06433e030c9a5e2bcb8d7a4a87581d0c123294237b840140b9ebb840d111088604006baf14972ccafdc5715f00887b0a3537e88a87735600908e84c287ed0e651e3b3de55e1aa8b4c71048620b6ce7652c743ed1e0e5f6a0f50592d04d67e6c5f506f61854ab5057728a31478fbcabba6a9df0a63a949110664e80d4361646153821d30094944b484bdbddd62c7e933579c5a359e2ba8b1074bbe5dad291a3d240f2b267e4dac19f4039e78a836db4038f18d2e7b07bcff094ff35e474da5a5077c005d47e3595b09b8c779a871cb05d7f7d2319538abdc05b8a1c932174d2560ac8d294ddb3737bf5094c8b2c3e6676a57fed8c305371604b55090ebffa00ea9bf7a7439887fb3c6153bc9bf2a46ab1899d8e62383b7da03b47a7177f7df8201d05d640615260c1e54e6072b46e25872e380e4e1f59060025887da61e037aedaf4de4710bfa74b884fc733f62eba7888e6d08305a503d40a9f6cc89c989d00dec6d4051814223c4037f81d4930c6b51acd0ed61b5001d5016cc8785516078c99615a20ee0fa3ab06990477e4a35bef523081d6a600d93301b354a10e1c7876770c3aab6c717aae0595f1d9bdfe6dbdae84fdd1fddc0d1d07d3f039f75a66451c47dec1aa54b57dc7391a8bed8ab66bf488dc44cb63ccf008bf9f9d0e74a8dc19f8382713123115704c5df9b1e0e8a3ec1f4fd59fd740e900fefbed3743d105f396ed43b4c58075f052e9d771cfb2a72ce011b8249b94220999f43007392b5ca624bf45d570ebfbbb274b88d2f54ddf0c75d96712d5667a08e84c4925a1d095ed4546536ab2e413b52036bfac5a0815fa0a34e082d7f33c021c65c8df3f79d7270b51587456229c221ba5d1688fb086588a87df42e375a00770afc025a7c6650766998bb0d54ea0c002cb86f0bcfc4a07e2f6f4060e4c430437a5e77e93eb891718e52a8e842a5a954b87e9884cca9ee9a75eb23614d6520239d74f21b23e889f9d3d0a7834d2fbf132e90bcfe2b0a88623221b30eff43009e4b2d88ffd79f2d1ba526a77eff8b5298768559359a2dfe5b830989caeeecf0fb1b063f61f3f5bab5c393bbf015c0d23f3194dad0e900427e99a6d51d580e70b6c0a0b58cdccc25d61fa94ef591c9f4a632d59d6bd95874834b3c99995b3890c04719ec2eeb167e6982d7511bbebed2808a5adbb8394306b97a3d230773d03066644fc4e3437bc17e3fdcf124e3e7bddeac6e448410b15b08f816df1339ad401f4140d84af4f4b249b251207d9ec7aa2b33947a7d1ef61265fb433204b05880817ccf4638bee6a9d1ebf8b5ca84eab97adb724e1f8c61d70ab71a554f53067001ccd0f423f07b535ca34f33f3c045c8474245ec7ab4e71779f9a3a7f7832260ade6af7edbdaa6502350d1eb9be80449fbe2c77ac00a53d0144dc70aafd0d0b0cccbffb467c32c4f97d0409628382c3d20198bb48ab1aaedfc7362562310e1a0e6dd573e794f1c3f1cd2f38240604d1378df0da5599101a8d59201832efbb440172f67a881175e90a4e30d8ab5f02db5796407ed16ab9cdddc6ee41297d3ef60330a0b258041f978f474c745271d70204b4181edbf19145e88300185ba971c609ac08fd54a3672fafeb61f3fbf78c90258dc32fd3fa87723375684722959d52018377a7b10b07489fb0a69c27e8b5d0df090dec04ec1c77d3b8e5463d8b52150c5566417244966009d539c59c2bd00d3e9c156c4ffe825c9130af01f05dc37109ef4e9ba08cc2818fe543e51d8362c03f245df0d723e335a3ae5a85510844a30c985bb3ea0c3993e5043a40b334c7f7580d477296d8839656c86ca754f749bd03bad2bd156a87b4ef23e32b737f90a549f536f7ae2a2046001fb3dce57aee440c90a382fe3899427a4bb974308cebe2e2d572c61c45630428b1b6b1a7e4a8b006fe352024cc6a40d07b134e7cc91d7841455114ab7841b5cb77276b4c91262b03f717ec773f84558c4a63c371c0a44efbff932c695f8cd5bb993f3660debec80f947315b02041777e98e1356e3c5cded128e1a70017999a78d94494201a46570f2a91c8ed29bafd157526b3fed635a3fedaf7ced08291128709e53d424a55f60718d9d09583ae6de75143c937140f0433a4c8de9e9410517990a7833189188203aee2557f47facb9b7952060f0e1b57f891cb09dcbec976903fdd857acbc0650b4ad96e3414f621be0a7dcf621bcf338c95d0142d36b9afbfa42ffd837ab38902b1f32fef36c4ed8e3990795945d7a2db6fb2779a1e1bf84209217f91c0bb3304b575cac789cb2344b7764b058feabb0913fccbc9ea6c8d5185c73e8388aeab085005bd6934b3ea1c9633af17fed3870a474b9a279f2a1f7154049dbf006aeb0273670bcf35a9e964eaf2b9bfdf88de5a3807ceea4ac18e05a12e7c268dcce10b928b22c7701d1f5c4f5bc571e5e636d649799b77ae92144c618b6ae0b144840141c6e580385f509556484af80ba6ebe3b920e3fe27635938ef234ee00bd51105b947d0fcbb6c019e3560c6c6549a915da4b65f50a3a5a2af9c02e10614fa09056590e243d89c36577469fb4d35870daeb5796b699f031de46913fafbb685ac06aadb9137279bf2ad2134219804180a09e1f946bfbec0d9d2be28b3ebfd56da08103ab84dfec1ff629e18152e4769d7c9d9701ead9f487dd2b5ee93da822a700f983cd04aee2a8c44efe6a790802f2426f1369a4f2c73d74c58245348d5633b0fa0982611aa088467c356d2cb6297c545d0775932c44f9af037c921902cbb350ddcb79dee704b6e75a255b5a014eddd3565f91704525f7e18cff8523f0860960e4f05656b59534378d75eafdd7b3ee67aadd75f9286e274eca601c2fd87349e039bf4cfeadfc314c6e799b2e66d47bbc497d3e22b79b0b802b6190934b35cc00f1eff9848be028c32d05bea78e88eb0644216dafb19a815b03c1a3a41ef83b60a3f46b1e9d5371715dde67263b6b33b37421bdd6474ce3779e4c979ade2cc020ce196319625a7406357ce584a252bb229c8da47127407c21368e1f5898e8af20c00791f8bc3e65e349aaf474518da6997527fd2d032277d14dfad8d973b686207 false -check_ring_signature 9bf6d903237c85f14e1ae11b6f558814bc33fb9d72ecfa56b85b2446ad808f41 99e9eb4c68c3971d2ea83aeada53b63207955fe55b21c0e11f4088dfbe1eb5c8 5 17c2acce4b5b62477196021dda2fc521789d8c61b905a8081df960dca75f1b21 ff4882ac3fb6f79ccf178eaccb93d4ec44bca0f338369128d66d234793d4e481 98549319076f7c7a4200dc8ed586a32e2ae656eaed0f63e4a5ed2e7add582ac6 d847539fcc0b60541355873ccfe2e597610eec58475e565b5dcdc12bf5981d08 a92958eb74138b39b534ba4a53bffac98464badf9c349eac6d0a233b7e761c16 4414ca2c4a0f8f6d72578def45a1c3e11e9a0ecd70646199dc51ecca60aea704d852af8dba0fdd8622523adb3533fb8e10973428ac3c8083a0eac5d2003905025062a94656b58152406a4f8212c3ca2ae1df38272bd763f5fa3e6b0034f22c0b30a6a4caa1e5967e3eea6307d62f8ad73e66311b036873bb168b4d61bd97ba00ede4c003985ee11b76168971efbfe342f4e269f6fb20018fc59dcb43fa930f6f69afae8cf60d041a09c1b81ce5330aeb4d65436758364e569950d70a84654c099c64ef7641a594dc7df5d81c3f43a5e1467ab49f377769a637a24ecbb7de320fdbc1eb58ea626580b5d2641e275c798a2514fa7979482a43dc9190050272b20be03acbfca0487a1c3a94efb7b0c2c3a627684f03689bd982f5a55fd772d006098afa9609a1cbe2061f79c105291af2e72393b57bc2a1bab7d985e9e9f38b9b08 false -check_ring_signature a81e1426c18b575096fb6f80fca662bd7a03b94ea208f57fa4dd2e4875af8764 ee7d0c58edd0a38d3057f08e19688741b7f0455ebf61100ff074030c8896df02 1 0e12b53c4578baa41175bbb866bd4068964edf29179813b7c8468ca714c2be7b 588348837369d9f0028109f46e6b58b4fc61f3dfe55f6433f6e1a4630af83c0c368cf5ab33c2921c56ee6b1ef3dbdaef1a06df1d4f11fabf70ebbbc2aa05dd08 true -check_ring_signature 31d874473578d02b1bc2730cdf92ff784c3ebe0e0e95bb31705e6ad46e3bf5a8 ca7d83a9c6c7c7238e23216aa6b1278034b0714a9c85e61e49ceb00d9c6e12f2 122 0a4e7dd550437c331f6882a0c5b3ecebfd259c0d162fe14308847cd1b999c238 b76afedd0568882006c4d976f684dfa3752171f09e5985063c717bd954e022ce 1d14cbaf375f654e25c37d04449e8ab33a6d5133eb20d8cbffd482b660c6ecc6 f00f5c3fb7c0d8063000b7e28415f18bd6e81e5460f94bb0976838668a73c9b9 6d7c47c61533d93c7f697f13d7d438f75dfcad4a6c9a64dbcc68dc9c8476dcf5 aad422228918331ba9ad6318270cf8c45be404760e3651b91107c79648d83099 cf219db62011772a5add339dba6f83d72218247cba367a3676c3b1854992e0fd d007306ab04ae6ac956e90d4a2dc3bbd9244999d81c73439772b81b9753663e7 d107002ab6808ca751183122722d0dc00019b357558a7468ea105f63893f336e 9c30b41152bfc264a53ae23fc29ff2ffc91c2dfec4c9dea1ffba245c784f135c b7ad12d5e40a2caee6bc0372648031d3ed2a93621f110bf64f8d51e65dda7610 e79b8a66c90cacb9384dda84b30f926a50150dcbfc881bc2c428ecbe8b07101c e6bb5259281d990bf62810847b01f98aac2ee6bb3d4ad9be328523198abdcab6 3bcc7fd286a96e6f7c1f2d5315ebb252c42ef53d153cb1b0ea37abbf926d12e6 c716505419fd08b9b4cde15bf820b1adb515239b8740352d7fe159ce3dbc3726 0166eeffe9ca26598c413f8da6f33c02e25907a7bf7bd6252632eb21bb8e05d2 d5b3e40bae77e6276a0b6cd26c3993cb2afa7229292b5d02fa4d995c033aebe3 eeabf733a6f403bccfe8abd9ce1c29d1f31496e5d2b60e6aefb1a70600a3fb10 3d38583459c134550ad933b109a04ee8ce438c7e0995c7e6cd9e98fbd6c33f57 da239b0fc9280f4b4388ce6a0550758a245f5307180884cfb0d32b6fa6c0364f 92a0c7eefacf93fa7bc3bcfbc6c37dd9a33b28120fc1a80cdbf9e4e6c4c326b4 0bef7b2c6618d9ab943125c99ff6d7b7927de741ff842222951b89878b7e6caf e06a4618dcf7f90cff3d568b4a3b3ee73848b826f9c5d1f4bd394549176bbf88 b7dde4be5b8dd4e9dc9c9485d95a16b5a94088a3b7c8e63a6bb11e50c6f539a3 cbda769a917b3dc34f41a59c576811200f508730192b0c960657becc4c7dbacd 4b39b7c6738a49fedfcec0f0d5203a6ead1421f6e88c669c1238ebc4656dfb36 9c1e0ccde57a25dbd3b71270b6f4c9eedf527bc06cf0e81ad72554c4b23ff94b e4a1c908d98ddab0b2e2b5021b9ce7d6b6cffd42e6716bdecf692776aaa44d50 53d9bda331070cfa630f9ba35397fa3403f030909927d0a6e9074891820741c0 19070996696ec52a85c728d59913a41ad33ad1fda03232ceeef2c8d0f5d9e496 4aeec6952ee3b0d663bd2aa72adac73df0ac9352c251fd98a42bcec15a154045 a7c0d3353687750c02b44cae8c98d35d6a7779b656a13593d2ae601d96498987 bc1198bba5880f84a6b1de76713198e9438f99f4ba5bfee2b9dc090181f8e56b 6eb3dfd4bd582683d9072012505764043be2cea7aaf5f20a54fc5849b3951bc5 b2eb6908178ced73acc6005f3954ccb9a859828ef0a59c5033679792df14c6e8 637e7a26281aca0914e9ac1e98a4ef0339697c74558db09bac5c18a04d160cc6 5ec198e3c1a276c924ea1d3fddcc88184f07ef0367b0a934f2a009f1b1039267 cd7f1c7a65a72d7f75778ce5b0c7bda541f0880204709f82cdbee5f0e0149f02 c04135181d0c0e020318dade33cd42a6abbf318f2f710459a90558ccb14ad130 5f7ee2a4f29ddc36d5d190e9f8733fdc19c2a7bf8ba3410303631d378bb19beb efbfef1621e6cd3850a86517f45bdf97b12a8acf581da72f930a3849eccb4592 284fafca904ff869f62f46690b0ce31b8049020340c9c0d577cebc346a66431a c2821e9622ef6dab79294bb656d691d66ba336b554e4ab1a9966aee26fb7e780 e2f8a15bdd6bed90a95c1fa4fcb7ac5ffa6909ff2a31dbe0a8c6808ce1001337 17a3b5f7eeebc8dde2f1190e132e71941f704be0f3d4e347cd1958ca2982ff94 5ea5dc14e027ab4af1d909b51be873969e91cf62b1583affbfb0aeda238be5dd b2bbd4c34f481c83a7ef19cf7f4b6891c9b5a0a9dc5411f65ef12ea3d4929236 5afd18c895549086f982c74466e05ac817b7281ae965776ce6c5947e5119a654 562a261db5f4495e8638b8c7d3c478e85ee520b83452cdd898f1189216c9f2f2 b7c84f184d8b6f4af0cc3426d9e27be868fe503e8f49f649b42453ccf95eb23e b11eaa318f56c27bc8b7fce76eec964135dcea175521f774754ea4e587b5a80f 2113056e0f4a314524d87cb93e0cd455010e6f687bc035faa62b50723d5ba513 d38106a906a21ef567a6dd9758bdc42977107d20e1b928bf638598bd5bc4296b 04844b5cbb0b53bfaddb2f047447edc0edf7f11ced25ef9ce140406aa8e0f706 aec52f538263360b215f6c04fdf7fc56dcca0f0c5b5ccda010640fa028b1a3a2 6c31b7d4ab9e55b1d770f484e0978107ededde23f9b4d26effa6940911998b11 63f4f6c67292dfe18a4e4334ad9d8de947d2860ff61cc39f0d210be939e35c2f d15013d3625598a3a7278f9395d7e5c631fbadd1f4cfccf4555624886660a6a7 d8618a081f91c0906f5a90b3390c1554d06e152a3895cd988b69addadce1f3d1 c4b5cb137c3ebec3e47379686303623287589bc086f9d0c20ff0eab9558ffafa 236b1e8a9e15c30aa39816b105ee24cd2318f1768711633ce01bb58998f9201c 27938c0bfe9f5822b836cffcbf23e5682c0695899f9759200f720ca8cfbc1c4c 08eaeca3b2a6f9dfa10e8e66a69f3193a74b5f4b353d8a33fb91de68f53b2adc 5a7eb7dc3a7bb0738a336dae9ecdc01b4ae2576ba5aa7fa032053fbf5f8604de a68f2553397a6c1ccb283a2871d83d61cbe9e6479a84e5eed496b82f70e58246 d99c5b41c9bdc21938fb6c3918b499cb3962fe6ad3248963229fe303265df683 2322baf38db70ae6d5511d710cb718ad4d31d2d5c26cc5e9de3aa64bdc8f6c97 790c7c2f98fb2df2a8f329207b4d8a5f9e115ebf48645021380468c9345f1095 66b933de06a6423f1bc52dc2511202579d9533e23e6f6c37bc4da1f6279c3cfa b39f20db5ba767e54c4d2381ca1d2dd313d6de022a6aac8124d9157e536e4f7d 30f49141446a93aeaaba124430152761971820cc33cfe062c3c75d864cc4b717 93177a29342d85c97bd260974f64c511d0843feb6e56866903e6c0486155777e 58b8f8010d483a99318374432306a67c573677752f7ae13a385e6c09672502f0 4ae2134720c16c2fb18a75153c3b70c1c3263dc77fa79b7391a900b61e171b92 0d0c9ac21c7b14aedfba167b722ee571a4d9e9510c2a4f062f30090e31f8be46 bdba3495ed5a98dd08bacf1e9af7f2e00aa187441b921ea0c6c2504b78f89775 778dfeb0a54a7c9c476c882c13a17cbfe6e1bc01d1d50b606c5817e93987c7cc c04ae96f1b7b3cab34d652855d00173b887c0f6f0964fa260b7d6fbba526d3b8 beee7612e27bd65ca298a0ace16a57475a7947753ee346534a52f2bca35f7868 6b3630f2f3a737b30e8aafab506151420c592b68fa8226345ab8a28ac794056b 5a5f32b50baaddb463be85b98a7d8c77c7c39b9680604167d0da8ac054624357 35306dba6e6accce28d5c8d67ccda4941a260038070c5b89a70ea05dd228ed7f 769bfd8b9825b991f5d61f35051ffec2c7ab19c146bc3b02106ed2be0535d89c 1307e3fd51c0ea5bdd32a0fc9ac621558f7c6a90f2c1685f6601147362dc95a0 695c214c3804d2cabfc50b5fe597600032bd10194cdc2dc3d024151dc80861dd f436def367f87502f989fc49606a3380f05ef404a29b194bbec3a25be78885a3 d3810363a1fdb9dd2c18ad4b16ae059215ca35e21ea59b8c8134f0de095fd923 8dd8a7549e730c0089453905952048fc02adc03f91adf11150312c484c9940d4 4b5abf9a050ac8e0d09d8c3c01f9f513a92c3bfa24d763f13afc98856e6695a8 3c36235bf26424ad9b065cd8988753d67c3d62b22562bcb484360df874ab1f27 3def829f6a0e300ae6f93675a841986611e9f12938028b4233e991584ea9c919 c0b260eb7ad604d4998ae8f5b3d1c243de07f83e412a7d0d322776433494608d e50c15dc22e227ff0080d0f4f240b03ba2436bd8d14cc31346bb494d6a8c1957 9dfac700d658cdef3813bc4ca0c079faf62b2368c55373f85578105540e67bef c60e856571bb145a3cc08e640ccc20aa018f41e47ff2b440da89aab7d2da4753 b299ad6e7385b71b717eb1374bda2f36b27f469472c715ca9cb19515e3588075 9f00b9408fddf9889cd212c4731974a1590f06f70278d8e3f8fcfa6a6f58a77f 19a1a15e3f1cc9812689ec9f77b208ba679386910a047dea7a67d5958af436fc ae5cbe69b8284d1460edca2e88cc5b5d3c80bd22d70f305ad73332d4f80ab9e3 573adadc1798fc182cdd5103651148ca4ab255c8085886ea243fbdd67d5cfae8 ffc0a867e5d05b6438767534c6a211e11f876275fe41b86dc54ec7fc8a539d45 51b33c47eab8783ec616aec18fe60359abef3b2b3acefcb940b66b165fd5b085 43fe55e5271740caf98fea194ad31062e1da3a4e71aba0ca0f5a5c1401a4fb27 525859c1c71a2897aca0ef614baa57339c306114679b5134d91b256c07ce8ffd 831929a2dff9c7668a1f0bb24f9ad9385a937ac606b9c88c2d2b79d2a3a86867 a967adeb0d3622afe98b7e1a95eaa29c1a3fbf52c83d66051ab32637c6c81b62 ac66c355f80c0acc36e6d3bc0addf7bda973b60a582cb3f56ec569d460a6c09e 57827ad5fcd81c348392bf9a0334fce083d5bc5138134f5ae05397d37479874f 3ee12b19a2a74fa52068381c7caeef582b26361ad79d28bc6d130050e0555109 49be86a4e541edc5b4140a637f452973e4c6e6c81f1a77637a214dd1c530036a 8994f99edbe3a832a2b4114cf6bc587797b0c615998b9033510861ea31ad612b 865a3d8f99b2ec9dcb49c78f6d0e81dff7b089cc567ed0e0b9b49f4d5edd7fd3 50a161fd2bff9f954e06a73f5d8daeac26846a43ca374914646743b2dbe7411a cda669e63308b13a0161e50e5022dae89ef5b83e6c5c8abe838d11673e292904 ba53e514ff496bca3a0bfdadc35f9fd8ef7b9ec722a8468e11610228f9fbae33 c6cc839456d9bc983de43328d225ab1710acc289499928984443b635ea82d48c 9c5f0fb7a07c2bc7af365c2ed9538c3806d7966f762329a8c4f961119267e328 dc0817436926f9ed3ac1cf1bd33ab2d4964935047042e3daadff1a41b7957479 78d7aee8cd5f5ba88b8db88cad25b35418ae6358aa1dc6f155ff570feaeecad6 df1dabe00d1991d9bcf6a3cc3ace521594a6e996d652a088e781632c4dc4ed5a 6f3df33072e6e9b9c95638b8f3e771b36031f8a69318962b988c77bb44b6ac11 ca6ad9680eea27b61139bf227ca81396758c096579e611bf9950fa245063a17d 60e6a9f3d4624a9228593c7ebb6d9c754dff0251430b938eb15bf454480a790e854f338ac6762fb782e00eefddc75e6e2d8826fc7a23dd97ed8f0a9aaf8216054ac8111d582b2c9d0ef877fdc249068c882b4cf192716e8c262359aed35c66057fcd0220590d443527012c65f89d525be9202f74d8ce45efebae6f5c8283760c6a5218f84d6b2eb87539883f6bc552c749d2c43b603ee300d7727a318e02d70de36e02ae0d785841f365db4750a3a2fb83d5ba3b2519e280b2726cc466993e054b4d85cf0817195182ae7ab02e99452d12bc8e5bf51d942ba223eb180c40000c8214e3bcd1678775967336716c1908df0d0c2d078207229911d2533f5f37580360e912b66fded37b868dbb90afb89685ed63a7fedbf1baa269c3eaadb78a2803a2bbfb389189209856e668c7333539a38117924464b66e0d953271d38a4a44041307960a67b1a0de434af52bec6262c558aa78e51c9772c72a955b5a104a350adef4fb3b92c859df5768d55eb840b09ef47726f42fd72062a75195caa51ae306aaa4b0ad6ff28b179ad96704bc54e7ef33a9c81409a48850e5e5e32041d6ab058043bb9350df8692df09a1d6bf09ccaafe24103b4a74a902e2f198eab38eae0443e053e642b318547010ab618ccdc3c2f5a4cdad0809e8557eb8d96733f0f806b7613b43a49aac8849c0fc1584c6c80696fe85879fc6c5a3d37514c86e3e650437261810313f41f4fe00bfa0bb1573c7702c786554bddc6f43e30a865de3560f44fa830a1018d5fe4cc9cc28a6d7518fbf771a62f9f4f3852dfe022f7237da05d6d9c0dbe2fa11756700206d1abfa8ab144c66be417f03f9acaa51cc7d665109803e35181c1b84bca939fbf2e0081bf9bf135f52f243b6b8afbe7c8ec0685f0f3a0641f1d5ee7fa65421fcf949852b07e0ea2470d6e3f142f846b38c52dbd2081f305f689ef848cd77327c88bf5ce58cc83a9b6fd805bf616f6508c6fec07002cd7af3bc566539fffb087b6e53c29694ca4ed76525cb03cf174081f76e353408dff2431004362d315b55aeaf7354ab0a2cf94e9c0b4514db4cae10cd6b1b7b046221d742b198afcf37e26c6755538fa415b636b512913aa89e26ede1e8f8760111f1d915c4baae7ba3b6df0d42acd250534e4fc2c33246ba0c693fb58a00bf02fb9cb1397f36b755e744353dcdcb57eaff2fb0c5fd21ea715c7810914e8ab40eaf414aba22d682e4c9e288135e3f06756f578a4ba6addb6c2a5527cd95365d0898b5c896a61917be8a3706e8e47f1c7924ec79aaaef5912ec27f357b61201707a4aaaa1e8b1d2f6123b94a4fba9fe092082aa10d1cb3a6e99c41979b32cd370a5ef0330ce549c354f784888b4458d52ff16549a426226acf9ff49626a757540ea83c1afce80c68481a4d99a87d7f3d37ea4baa6cbe082c2758e9a158a713bd0170850925064db088131f9df942bb7ab458c1a82b101ba21d5eaff650bb91a50c90ac267e1b3457bd85731b616c353131c27b55848c39c3d5212e3b801b746c0b1688c8287edfb8cbf00d537126e30a237f83baec3bcf43c8ced181723bffbd03906f52f7c8a94300a21d1f3be57f3ca63ace86366b1d165e0431cdafa811c80b035c528390851acec5174289ee26624e12167a4d4cc3af2cc0873b84f452ec0711ca39da1a4c5bccfed08ab520d8e3588a2e9413468e4f1e3652431942f8c50541b7662af5da2552bb370d5a8f764b86edefbc2550dcd8f118105834831ed40a0e6e43eaf2464fbfac4aaf8189b5688ec4bcccf70e78e818ed5f8f991c4b7d0b8ab6f428cf71ecc0865e26aeb55c24a6e6283926e393e73fa8793abc2627e3083750ba906d1954e03bd03590727a443358b256241883086f5e2d2d53154ca5056fcbee5e292063fef3af6b66ccdbd169942878139af4a757ebeb5f9deb6bdf04c28a60ab01c8298ef893ef7e2f489c52a9b805c2c2f8f20a064ce240e15ad90a0ae13c3aa73be7c742a88676004a55929666c1b76c3c7a5f5ce77529d23922018c2e9fc9332155aeb783fd90f576889f56b0514f76d0513798d113da5f823c0494fd43fd493047f6eba2ae96fc6b961533e3f88c102c1e73234fb8101fd21a0671e8514122d482d380d048587077ae79ff3dcb0775dbcd811e19dbf90ae704036dd0f456d1b4bb94b30a87a5fefecb882d61b683970a9343a3caef2706eaa70477df1bff68560bced0abf1fa6d29478ab0d943a50bfdec101f5659ec9dce040739399ef8ee56779ec58929c8b16ce17bc418448b8ffe28737e31f7b11b350f0a6454d6065d684919af1ac2cd74749616ce483047b20458fde6e968a43f8099069280a554199177947426868c6777083ec3bc2271254590d528dbc2dc789b0208e59851c16b30109f236b10106616b5d46d90ad9a2ca133b1effc18f2c322a401ce146467f184ff00697baefd1d406626263b5f8321e7a67fe9311cd7473adb0301e4a5fae86bdf7406cbb6ebe1813d65c09a3412b2bdee75e86d90220aa128085461a8b1a7d2a2b091d5e6adb1651d8c50d445a6199bf070d0d682007828620f6f40ae9dd06e818b1faeabb510a0fd8f4a88d31956c8f1bf875de0a37ae439062b61a60023aef6031d125f96ae3489dbd443846b1913255c233296f6164e020aa478c8b8a9dff8f47013b878686d8d9bee07f9c11d0c6994ad02a41564ef7e03bdde82aa2e949d9d85e81e8513f0f2319a2fe360d553a8eb3007bf4bf83cf00fb405d0d8e01d32a09894f18dc6c3c34c535a67cc7986d6d356d144e91f93110e11b69d928afbbbd6fa37e4480e0bafd0e9cfdbec10800c915f632fe63366690c4270f4b40ebdca19ac1cfe55710ec6af8ec92b499c04c55728b85e83d8ae120b860829acf72de55ea79a72be078708b09f9d5b3a877ff97de2f2fffe0c631f0f7ab1cbbf46235a533131c9ced25bfc5cdd99635e3049e5510c5a4b0940de0a069cb96b45e9cc9f2652d6452c17a27f3031dd8bf79d41349003c92c71a90c0a0752f54877ba1856d0a687469ee15c7e381d983f99af1227b360ca30d047e1010822b7dbb468501175209c174eb2dfa069fa0a701eb733597f638d1d1a283edb024c8eaf40ac3dbd2185e8e6e2a785434c4a23ae5fb24e591daa75f76a1ed6d201dfcdc2e7686747bedffad5e6a12d09aa1039755dee648f697d25ae44fe9027071aa26a97cd5e9274877ed8f35c8693b4bb941f0076c4749e9ea0086f15e10a02f8cc64f386e33db2747597246f61aba371c2e621ee03757fef71744f1d7ea80892f90b756f4552972cde120d6f600971b638c5ea38fc4049997b0ee8fb5668078ccde71cdfbb9ca13aedb0dabd485ec3029bbf2b79c230e71d51dcd8ed25540dff50898a7a5de7d3a5e6ea868912eb68a1284c21822017beac3dd595d071070f6aa1ceeacf8a749d85339113b1868b5d89d37803f7e7907bcc742a52bb50e5050d2a7dd6fae1d91dfa4baa13db157ab57dcb7a1db928b4b48fd5f9816ecfe5047e56ccb876dde869edebfba31a3573f835d35605f1008c4a4e616d8662083e0f2b91311d6ba583bf6a00a5c37d5134b1b2e584689f8da488cb7fba50b095bc022ada7295b6a71e7f09a0cf4c719b2525ce5f58b944fd890bcaad4d428b960f0187cd7457e98f2641a7186cb9e9343362f8231532303ee621ab0f30508259cc0b25482b1e28e6351d9d3e011af1b69d767079ac351c8ea821220cb5a1213d3704605dbfbdc99a2acdb6e31ae67544b20278e22f4802debe5f906f461e88c360008b51c9ae0de85d5eb8cb48756375404d2b8c5fc1fa7407ba390082c9bec39f096c70a381dc7a59e55b4f38cf6c814f16fe27799321f75be5f83a8e9c3aa9970e9ad3910182fd46c2a9c050528be117641d9bdeee9f63787872860d61435cc20b072b73a340a805e6a258308b2739626cd2678bab0ad85fa5374a20ef47981c0e409a3d0b8d13abecd8173e0f8340224093b3939b528bc30e57f31e82c1efca0880390229c4ba423f8db99be74a18488d4662baccc6b4add7141581a181f7d10da0560fdedf9bb446f291004eea7c3489062e6ec4d7d5bdf0f5f5eb00f967c00cfb3275c77a936aeaba025c8b992a30fd45f3e5e3e482d870e4e427a3d85f3109ec5b09fb9a23e563cc53f87d2832ee4bcb83589962a936798b5762b24e9ab80d09e30a12dd8d641c9700e655b95a39e327a09e54f1e455c69cc56cf81a69f409e0d5676fe27cff6e88da3212c96409f3c35c2c7555aec0af25331c778ec69100e96996c389cf8a464ac82cb9c35a4c6e28e953317a46e5915088087b1be96805fe5eec62e6724a999c81d525828672744c84ed87c6d3a3d4fd143f1064ec320fa8c921e01f02bea2757291fd0983d204e050017aa8d70de403a513fd8d28f909314467c68ef8ff423677e82200cef4eefcefa9753ec6fb6386689bff76fb9d09c681480359a1480cce139fd605d28a967de39050776545734e095541517ae30ffd90a4708843e07dd611cc5cc4c21dc36eb1bebff33792900271b690c92856016c08f5cdb373bfcd4b9aaff61f98c730b2dc138378f40612ef19460fc508fa0276ef4c8817c4b62db2267fccc0b0b3ab93e7912fb3530ebbe81bf24fcf64440f031a7431cfca121c6e8f8d77b0a8fbc4f63e6526dc2f0b0dab33d9b741be4e0febad82da7fd0289c73136b551cf5ef400ccc492e54fb4b19e0ad614cb3cb1b0ff533d4e769283326d220ec17d3565419001b7a99fbcf2ea49a3126f0bf911f011e1440410bdf16287cc6469dd6b73bf5b5b86d3fad6a85051d80599f114b5f09f30f880f8dc3a2f2c814824fbcab48f434375f417b7ae47dbe6f4e3679b98b0448609dc27ab113d452487d1f480c9abc5806ac2579abfc0879fc7201a3ecf108194c7cdfa2d30091aa546d00492fa7f83aca00bceb779ca2f3a6dba42541840ecf03da400099eb70dd5c107974f4e1497f157fa153d487f33cf66c7bec969801f565da418d6a618999966eb8d686e86cc5605b92549ee2baf4b4276136c1c1049dc113ef066447dee7fbf3fd5ee8cd5926a2d3af59590cfefca0273e88813104a84af1986586815d57556f900b4f63b748e093b257a58a1792157585e98af20784d72da091d6ccbe5d6d7d5ad80080078bf32162f4a22e8b3f53ac472d427b00312eb3ad5ec794725b1f48566ae80516f7d7b3c7efa3ba5d20c817002c90b403f798dc158b0ed7d9cdfab070c6d215e9ec5bf80ea0915e619a8c4dcee5faab09b384fba0ed2b2a87e8affe73dd1ed2c117e45cf22126720a819461e1bb07db0675a1ca84c239d68415df1026ceb5e8c6dd62cde7d0855aa2ad2a45c601a1b7007027c3865e13ac543def7ede8fd6bd513d3cd166be230c8871b2c6d51454990aae7ae3df7f9a4bc45e48bdece9e0a91a8b1a54f7c1ef1322081f6d365923bd083143d3b38c161cf6d0bee1d7d45d4279b26c1ed238b05fb4c0e8019471e24e0f41e3382279d64129b555c27521858ca04a0886233b0df4ac7408b08041ceee069715f729dd2ec5090851fd62516ac63a79502ba7be5234098eb5ad15bdc8d1056272206a7b4a02509bf3b90f2bf2d39e0cffb1fc162ef1ab84f72b84b8bf9d0e866deff9b9530292cdfb8f07870bd9f553b5cb0b91076153864358d19088c90951eff91e8c05a9cbfd293dfd2b3e49b7c3df672b4a57b77835e833437228f205f83a4f0a9cfbafa7ea270c6a2f9d50002ccfaa48874b4ed805f2472ace6421082d54fe69f7562bb8db3f21bef42b0d73ad49c30b41536e1441648318e1132b0ef2c114448291cd47ce5a72566898195581eff801ae6a5f34b5bfc4c955293a0bc1551e3f1571897d182165a36b2dffbef63eb19fd0ce348c01d9273a5127d70d6fc539825966a72e4da8b642f042ad2cf397c82bc281a4db7bbf854fb598970839f5322d4afa026a9fdfe81b1154a731967dc80557c8137995b3a3b2ce826a05cd9d1d2401fb0a928fd4d40d9e3d610271ed741be829aa554905000b73bc4d02004ac11d11f09f9151766459076f90f81ba9f52604b189401ae18c5fa5a88a0960fd87cfa323ce3673a089f6549a3d6f0813cb6d1fcab25df26293296599dd09c9e616496c17964154ac06ee2946170228a50bce968c3232f197bf5536f0af0f20ce973130deb194511fb63967b496a975fad7693c15a5718ec1a84b8d56ca04912cf39a0b01965e3d37d2dc68be89e7a9ccd8d0a1401b989b9e223666dc3f0a4b9f877499d60a94740cf1d93063221b93a45d30a8bf1acada26c4770819560773a12785fc94fc5e893f8b81954a4273c44df68b2fc0a4537d04a85ecace0509903f45cea0413e09dee94fb204505f93237f3ef89894c6fffb7de8f35987870d2c8908b6175d77a8b4baaab2a7a80bd1871f1c228406e9b8fda61f52d6c4b2048ecb887474bba48b26f8814f33dfd3affaec0eb1a186b101a8c2d3e1db30d9060134a9d67d35cadb7c20f3171d0ef9b02766b51a43a1ffa8f895ecee019fee0d72f89f9f0553ebe8869a11845ce697bafe5d1b29367ee3311bd3d8a49bc78d0bf822b39118ddbe3fa6d9eadb21b1caf6cf6ed2342c3f1dd6fd14c377b002be017764446ea0373db56b0d6fd68123d51444790e35a15020d9692112564ceb710586d71057adf193d0e0bb23362ed523bc5f8c2135f197c6d6b2869856bd4a4b0ad4a46f3a06993916a2785b6038195887368c66d1a5612df56f824dca95332702eb2c4380888d2d39ce6401f882a2541e356484cbf477f31966d329559cf1340e9bb079846dd4d422e86df57ed8b21efad310d5add38c243c323c6df4776028059bb127c1de6aa0e1f6fa6497410d6032afdc49afc4a0db565652a120395be4002ceaf0274730b36cb1c02fb972cdcb1531235d68dc452a680b0dee87cef9140fe08331b07f49c3d58753e4a9f0f803ef169201936a295f3664efab69b82da60e638d34b5dddd5d770d5c040b9efeddc85177028d2d6a8723d337f31c38949302169d2e9fc934206e39b9814cf81a3fa05a39a9972c2d1b66d848646a146a7e07140cf4bd6d8e1b849613af3f338c258bf3438ef3b473d3f739200ffbce709106c711c85471d06bf076061d8087d872e6d92759e7d64976b1377e92cca2cae90a17090e4618aab4bb11b502a507641fb5550474ee010f8b62526f86918c06f606d1c6419f242ae23e4b0b2c5c7cc0ee2d425467632f3adaa60b6ad440a5d5bf050745c1fe42de21e201abb861c70af9e54870c64e5e60131f9abca43a65f04106f471b3ddcb0009a3c8d4aeed6a477f3cf07690729881b1cdeed3299282790105dd3902baae16d746cb6c14660740e4e53e186fbd364a5221c492bf7e4689ba07ddf39986de193494335f165ba1817576c434ef7d02003a0f0e48a2cbebb0bd0fd44ba00b4547d543a505979ccbca05bc01e37527824fba653004500d02665906a20e49680b9414fbc4592b51de25cca11dc42a26cd45a4dbe728c7e0e95b090c1204d9965797dc97bf64744b84ddd31e70bac4854726ebfb50d57d941d734e0d901d431dcab5874dcfd75ddec2cb3362b5d6811350bfd4fafa667498d446dc0fe3eb8f3053085fdd56c12ad7e9af0225146a365d4a48b3efc8e027b859d51f0ce50a316a08a2a6bc9e3e3876931bf4e55a1f454bf495c0898db19ea5959ad9021c33742481acaf8236c65863240695ea27313bdc3c5fa93a89f47e599d816608d2f5acbf61c47ca61d884f17ebef12ad485125c4a96d8978543060b0da90df043b14e518d38061ae273c63cd777e8a8dcb46b64162f92566ddd05289ee78db0b445bf52b6f5d7a84f39ba9bea96686c7bd276bb54e121e0abf2e28f9efb05f016ab9a785b273e643fd098b6b00f753cd96399cb6fa1518d5187e60f6d5b7f40240d5150076938af3aa5e3d2d3d690cb0feba45a739ade8562fd068dd65559a0658bbe10d3b7c3d4c9e482bed799158b30f591725d52970e6997e69dc7ee7180c5e365b17374aa68991bb589abff70a3d5b355845de39e9c6de024dc35c2d3c001cf9e2bebc4b717fd236eb1d4f7bc939a775ca39f9f75bd9cb4b0099fa1b32088f44f1840cf78fea50f597cb0b80475fe92e87d3f730d319ace6109a76b56d091e1a19298bdd7970035218f431b14c8df4a70cd203ee6c36d593ac39d0b8e40104129f80783d75a4be6fe5ea930f531b91fbf5f16dbd8541bffcccaf153ece0e138d8be693f06e45794de5a1475d4b9478b095bd0674c2f54d6e34a51d7aa60b0b5ab935072d8a279bd89a8d6567091202f8af472e046208a4069fd6d2c67c041dd275d1a9fcfff0ac9b9543ab9335535e7d7e19d42f7865f6278fa32c1ba606139b1a4847b9d769538c29c57854ccf871ddb848f1899c8afc82e223d8e9c90e651c40694a13dd282ffd415d2f4dd58f1d19a57654e7f9d92500459224468b0c1247edba8236c6335f2c3c2ef59f5bd38d53c64b797334f5c2da461591508f01ace6a074a4133d81ed4746f7dd077b6489948de663f229bc0f0937d31556710f970e93d4481e13735ac7f032d17cd26cb78a8e538c3abe5d8627fb10ec7c250196dde136f956b8a3a86dcd48202b64158104f5386766a062f4fc9ff65f548c03d0f3a2f352e22032da3cc3eff0e18ab2a037681b2138a901ce316bc53c487b0d9421561e1381528004916db596229f025b0e6368ada5c7e4c1e55a63231016085a2676b735c190091b27a696b480b416a1bc14c37ebbc3c0dd7985888beb3506aada49e7385f3ac398dcdc65a00aea13605106fe5c0c62ab5d7573280ae0260729cfe623c25fa518d310a072f360205a7a1c3645152af6e71fc4ba9992537008d623f9e0538a8729e4bf5102c378a24b31720463560761ababef2190aa46b00c1d635812d31128dbb8ec86cd41a8456dd1af8f9f3fd91e1cd267cc9434c8e90db721ab20968cf050eabb5a22b02f582330f7281067b710aaa71a96599240c601377600dcf0ca332b927e3c26991e436aabf29e6b754bb068d2a153fa72b51c06fb9422f762a5a3b4275d195de78c428510cb391215ab0bd358b0ef4c9521ee068bb10a2680963ad71432d278f2fd66ee5e62b125f0f8e7b7c94755a8df3342035df3701834104bc876b3e80a600d157c916c62f7ae6666d1ec49b133b59f9a03fec3e5abe6feaff10dfb5b914a77cc1c72b47415e37866601bcf1c0dac6978029e65438d0a1a5a1bf4374b7bab7053323a8cc4079322f65715fbbc4bbba30f077eefd344f8919c18e49828ff32cfcee754f50252ad4f5037d6df25922b896408a5de8b8d82b98d8b8ece1ef5a8f84cf9c3ab1a6b4c4f0a67893329915526020be47645fdaace939296b855fce6b05486ab1e35db1558d5bee142d8a39b886d00a062284830f8198fd753093d874dc297c34363a7d5ef92cdf3c497d1c78fd40a7c4f6c018e9d27c3eda886d62da26e1b851be6be7635f9bec72d0aa3267eca089359e086a3477a009ce194269445fd37bd2e0cc3ed6f00f458a7eca6e4d3010c567fb201ccbc46eb6d429d13dbeacc6e89effd9ee1f2a198402f2ef343c3f00639683418ce7f5be418335c8166f088479a018356f208c400dd7ec5a9a72ce306a56ad2c36b82ad7d6484c02b4410005c1f3f630ae84955f68f080fc23ca6b700f457af52a5278a5f26e99cfd263268171d2a892f5bed3e76e1e5927b4c23870efbe8c85671f507b0a7837e89565b559be1d13c5816cffdbc33b7dc5397597203dbd4d760fc36224a070ddcb43cf13e10ad159c2237907beb4d139598599af10f6846f5ffb930d5c44d8ded7219bcc25b108e85c778b0ee0ac4ef20f681e8d80bbe5234e235b42421709911db23b6b574e69f0e9605c3e4f1430d95db40891a08151617078612bdae45f21505c6bc1ad7e71877e2879c369254909bfea1340b0d49c32fc3e1e074bb709f50ad4973fa76b6a3ecb9a3e6eeb7a1ba81f6b9c6df003dd044ce17199cf91d5e9c0356fc992401567a19de8ad54eab1c2273fdcc9b02c23dd5ec73c0c025f7611157a084e581f832443f3cb22dae5d330e5a2ec5900ee3de83b85f795f28637aa8ea9bc5d2a0688dc983d7f0cb68a8b0894b0fe8d30e3eac169feeba1f7322f9786650781f68e75a79bf6dec2254ec3e5d669768c40a7b5efaa69fc0d581f2d1218e06fbc52cc99fc4e430b26d8753dd055b14fdc508731366df0f6d79e4d6b58542ff4df1ca3e0c29bcff8d32e16a328fd04b4bbb054d7e8a8bd03f28105815f6a028f3372054c9c45527833837efd75a9c132ad506d9c595889baf93f571976652baa06a73a4abd694ce908f8c1234ebd43b34620055a316212911bceaecc1fb0dbe83869d7b61e2c9441e5d166e29225dcf80cb0b69a0a6e0b0eda2038c715fdf4aea3b5ecce28d0aa73c97d647d222c62c145103ac36dee73503b0b7ccf2c67139498ad0565e9161150714318bd69fc17287240cbaadb182a1e6bd22037ce733f28dede3cc5306e953b360a2fd99488b04c1cd03e6ef73a745636e52cb98b788539b690eed1f090098fe0915154779169907a904aa3c5deb5a4460a4c84b63cd1a5c12a8a5b7f50e865b1a764effdd50b270d2049d9115c8152058c5182ae60044aa46a5aa1aed6acf35a69864e45c6ddefd890b9de9bb801e5720bb525e2afc08e502cf93b5a4767730c0484a4340d4ff2559014295f86745b367f700b317fd11f90f4b1cd96b506d8dc86e3db684ca7412240fbf1245c57886df6519a6e70e886ee400607163b09445a5b727acac9519ef530656d67b98d581095bd8f116342f45afc42095eb5dec353e6385010c790b8d910aa5836b1f1fb76b904d7ea4bbfa3246418a63f9514f583198e0c1f9c6af634b07e84df56c248ca3fd532dbcce9d268db0d2a4df2ca9f3de74189566d657e0420fc3eeabf540d6cef9026e4984b42f1701d171e82939b87c3447f3c15fe9f7180f false -check_ring_signature e6bdfab925c5e583faf23a24df20248c7b449accc8153facf889e88581b55f4d bcba89a72ae610d05853fed0b3cab80634bb48074c22ea0c669745ec926c5224 1 eceec6b87b71a86fae9115bf0d0df62975ab461930e6f639bc6f4a821db97ebe d838e8b15efc48e6ee9697c06db73bfb6aef78cb82200037994d71d00d08571dc43d264efb0d663fe068d7f5caa8a900fb219ea774a7dc5d92fd5d459e5ed603 false -check_ring_signature e06ecf114506e80968990d2401e17fe87ff080c1e01c05ecf9d4c4bcae331862 975d648eda499b5f91a00a7c67802bbe470edefcaa2998136901aba7bfc36fe3 4 9307317b83241da5dff9320f84af9cfd6445152587aa76f426fed904d63c19ee f5d7f1d81c1bf36b260f9ef7e401575b1170000918683d707166261be7f4c354 125efed9aa969a705c84b1ac738b0bba013494009925931f2ebe4652cc24fe6e 1d02754fc953a175fef24ec47e39924b292c74dba650ecb9de8133bce07c44bb 95e99cb5d7b7ca78f72acb366ae6fff8c493300b8654a283511933c38e58e90550a53cd1c3c8ec8e5cb96eb96aed4146a2cd6df2824e278b9fe0a0f852f7b80b4b6ff5e62f04e57564cd05ca250baf21e703b0f5285007a3ae2e6614426465096541f5fb8170fbb282a287f60c6b545b918e6069f92f7c9e4e30b0a75309de0d3227de3cc2a2a17f79620b0e20bb39ecdd0aacc7359a41c8d745d8b0e87be003750acbd2b3201b24e32226d913ccfe87befa191c74e4e3e4aa7574a3c3350807dcc2ac60561a48da20e7e97e8e89d0f2d71fbe991c729bfa048224dfe9adee0d07f791ac90e1b23032c706334fe5f1c48509e77eba756e4556cc0e2185ef9002 true -check_ring_signature f9b1a298df495a8adcba5c744f485372ce30794069f3985a6d2500507819b3a9 3b828c4c3ea66fa067a722148a05f559aa942066d6cc017d432357af9e9b022f 6 5026c4cca05f4ee9dda95fe8d1727cccb764a55c48fdcd8d7f83147c6e8db8e5 da7cf86310dfa7d07ff8f268a3bf935d74d2f07d1f80ac28f00eef50f4e1a81c 208a4850eab9a83c7ba558cca232eb748b9e85078309d27b2ab76e85b6a41a12 99d716fb54595f18fed531aacb28264bcddbaa85fd7ef48b377739d96b5dcf4b f28fc96f3d459bbad6725bf515bce2ffee6eaca0dfa4f7dadd5e806b79da05dc e92496efe2edbffd9d7fbfb83b5e2344aeaaadd999c775d1a30f1cf28fb63ebe 809b57967c0e88dab33dba49ed889383d78dec5eb82e91812fb93525652233cc1471a318e7cf00f04a07ad98c2015cc268c9990590073c829e7de28587492400ad2a8b3fc5b9b09ef141f3862c82bee545e5e3ecad4493d9e9e09589a546020e3c814490064c111b65679023f3d9dcf9ce74cb5a12693a6a93782e69e9c351073d87e2443853affac0a19fb18732c4b065731f51e64231a7a5cedf3ceff03201504743893cdc8fba2cb5639e690f80c740c92730c787248a829fc2c0beee7b0ed557744069e36a1d3b34f224350c7a0baf6b6b3f551215b58e2b7c8160ceab0dcbab841f95c2289bf5b254a3ba6ff147f46b20fcab6e64993a1e5ba90dc40801ffe84818368ed36ff7f532896c345e7b8143bf7395761144978c8bac0646630e7db4437b10c2364ddd13b7a0aa5751be937223917786f1ec4f403b6b7f85c5b13ba49384edd5d2f72827c6d412993ca6ed539c2281b4681491dd1288492e7104d1b605a8796d0a62ea9d4e7e8cbaef0fbe0ff4660146fcc35742475df16df20e false -check_ring_signature c6df5540ae3bd6f9462da251ee85cc6cb02176cf9520308ddb1d01fcac6a4f9d f333067841c471f5cbf0a7e903630f61207254a956f1fec474a3c628add12054 26 53c6960f9b9ac03034b32e14d91bd5f278fe4677debea670a1e36bf75cb2a850 0f933cf60b2ac54426aa4a4dac92c325249da28a08834572a245add70ba4b1c3 e25bb46b0b3d327f9a4d8da4886fa3c4c403cb665232a6e2c5a25533f309e7c5 d044fa407d495cf7e559f08a68ed352ec8ae91c614cffbc01ed1bb2f5845eaf9 18606fb82be75ba387a1068b54235f0e5ec83de188e77d3e84b4e9f7a137a737 211c43cd5d8722acd75fb5268b62621100732efdf7395bb6e219bd6c7f7e4782 d90ec36bc9b7bdd6f589711cb3678926fab06f21639bddad3f92de76e70c873f 2cfe8a043c5c9650f60727b7e754db0af8525ad2d5ff2ced68f1b9d85cbe2211 e1d7d1ca2875d2e6507cc0cb7a549987a5f1df3532aef73b76cbb92ec16bb031 9ce67370db9a1e4acb9bb77b1439d387f73b28065d37a8987e6f6ace999a7b05 7cb9e5607fb564d12cd63db2510d3800ba12999907edb58477bc13b51747c6d4 89ed936996b82409bbcd82c7e4c905f6feb90767b1bd2cc3bea6bf73367079de b4b0126fd2840c3df65f95d482bd793c9ee7d7918807e8e4caf73d4aaa808de2 4046166e2f7783b3764390a4fcd3040d2a1cc1393e0d75a4425ce2a3bc0b34c0 998e978455551a707051ebc6639968680a15b0ecbccd5ee1dbad5b0889bdb39f e4194494d30e2246c4e8eae99a7cf38deac4aeb1b4ea798230692ca8650a5fb1 fad69a810924521ee4bf709c716af9f6a78cdd86378aabadbe7d8086838ec1f7 bd7668b2100e6282568f303fc3258d5e22d9116e03965b4cb18cff078bb17b4b a0c15c331d476876d0ff6f515c8c90a8fafafef4cd04c080b9ce1093bfc80f46 84cbbd84e55c7234ccf57ddfdd3814de997143f087008d514223637335e700dd a76d4a8f182741ac67987676ba84d90498cb563ca5603396cee4833de43dbaf2 ff0cc85bc4dbb6952015a333901f1f2fdf3f68c3b626277edc0d081c01d08496 b234349eedaea56ba921289c783de16c374b21cce19a351cfddd891f64d0cdf1 5bcc37ac994ce29564fba9fb07e3d7814bdc27b1093ae35105ce87907ccd2263 21b32f3b48d517ef1265dc3dda184bae72362bc6217dacc587ebabb1600ee4cc 308a9bfaab1e5149bd03b3df8ae20a341ee00fc8d6e6be1c55c198c0572f0b31 bd7a5ea46c4c424e357ec39b97c90fda2e2dfec3ce9cac2fb808eca089d9db070e4e43143f24825bb66cd3aa8ffe838a85aca72b9def898097f8fb6757bf7b0847c5881f859ec9d65b7a05889ac4575e35d59b5feace05e09c8ba5cfe266630bb771254d829449c375a6e3e79e45d4b928ef6c309bfc9c003187a25e23d788035110d7f831b0be43cd6ca8f73d76badca9fc27464c9226e07ffb7e0ae831f505757d4cc13cef35952f2dae53407660987988817585177c9a936f9884c20a010bb18ca4af6abadd520b27b837d966cc270157bb64ba02aed416ac088e4ac3e20cae6a02af679959a6b19802176bf07870f765d1a07dc2eb82db876f911b5719077d899037f7c756b6caf23d32cadc5ca738cd5b1fdc4b15839747eee733c522070b86e23cad86113a08b09ec532df9bddf1911522e0292c2f726c63f0ffcbff046831947c931ad822e2943bc50a6609ff2954cf0e4dfaa9b73302c5f2d573c10d51b1902080e5c2fcddbaaefcf053654b3dccf66e470309945adbffad5593630cb3a0c0aed2e442cbc336a4a8cc5d347dc20ca222827588bb1b8d61fdb4fa2d01c11736b43e5e8ce4f66b9a38d7c1d6e98525d5ac36c71803898cb023a592dd0c06cea5c5b2136bb20d5f9a0fbd2485723f5022b8cf6fc93609c4712f5608d600052b12a257276d10a2eefa24a0082dd89b0d455951442ba3a5db1501396b520cf333025ae060eff982962f0e8c79addea71f6b985d484224fd4b9133088f8702d73d3254e90cfd043e61f77479c6e3e112597d5fdabce7ed6935966067788b0ce8fe974b4d9d5fb922b9545b77187111d4fd2b2242cdc8e67880f391dad8df08e4ffc5f182f521174c7e1740df4118557a10b0dc4c64707ab0291462e4949a045167fe3a81c980474eeab7f387856b79c35afdcc0145f6395608bf42627fbd0ff2ada0c76c2ff3de4e9bbe58ef71be11492c6b433dd3e0af023b9c27e5cd090cabeb40a53d269ba6c922b07739e7520f9ef75f0b2564c664fb30854d52208c05341490ae706e6654d96f14e73e1358ea04402e806283d341bf38f2983f85070ee4ea6d7c05826910b1b257ce9603ac2bf165b965a9560cef9cc2c847306f7d060a5a0364f36d92c31c024acbe3eaee816fa266ef55b8fd7811027fde4a24880b93f00d361eaa4132efdab370223309a5bf102257b95405351e83f44834b871045984988203eefb0d3a535e213ef3a8a194f6f2d89ae32854eaf8f321b2d1a70d3e96176ab35131f5c30db8871f45d3ad230dd3ef1303aba6869140d3bed5e402c7311fcf91af05873b67b4f30683422fd183923162653019b0f68059cd5dc30a596ee865fd13f84240d349ff8360269ec96629b4377b75864fec278bed7d810a357a9156357e3b5dfa4ba7c6376ef61561bd2307d016aeda29063d11b27ddd04cfd16797fbe2a23f42eeb328e6b6499c1108f5c5e773533d277c717fdcf2e50806485dadde664156589e7c47a7f6945ab160e2eddf9b4d04b93f58cdae2ad30c46743d46f085fcce5f6e81d16dbc335963b211ef49446f36a730a23bb6ebeb0580f88daeb8184fb4521d131493869b941168674ce1184430c514c40fdf09c90f8e6ff2bb5a0474325efe1cf1994bdf7b1689ae178f7f4e68fad24e0e56547700c4983d27dd48808372b8db24f6b26d8ef7d1606fa251ca5795359bcc3d73ac040a0be36c49f2b581fce9bf8180675d729c71427591e965a6254f6b08321abb0a5450ecfb716a468af9781461ba2d9175b6a501f14d950d45ba4ab20215cafa03d16286de9219bd0b2187a3bbae8627fc10c53710090df0a055dc8aed4236a60a27b827e6d9c199c66103a8f01b8f561ad16e9f2f7ff8872a4d24dd88e382b0070c1d7c926f918a1835803f756ff2f1f08d6d801fc7ada3a7407d25ca542e4d0628dd5b6ae842dbacc08ff2ae2359684d446726d6e7024ebe92ad923d5650d10b38f024f3f3122ba83d0e416245f4d8e9bd1ceea353a7b771c176c33079bfcb0188eca20df5c12b9313a8a514b91b21a04209e741fff75a89354cfa0d3372920e2302945d054f5c0b1444eac1b2e0c53a213966746b9ca9df3e72258e7fc89207b2e93353d7f81c49cce65251ca66c08b9634391c0f70cbaef0c215439230ff04c5fc4f4b129aeeda0e201b01add198ff7d59c98d430efcb5a0bca55da0c58202a2b1a9b1bef0ff6611cfa28d2b967caac40fa7eb13d3d94e639972a6392d960afee51d8aecd63e1647d139d557c91e5d8aeec483ab4d89bd37482fd1aaaad001d8f07fa4b4caa4d6b87a4ba9da2027307bf00bd6b750f75d939add9b360e0707 true -check_ring_signature cd36644f74572581e19100992ad3ba7bb28cf1c040e2d8f6ae1d88847dc8499a 2a589a7e609333b325e8f5021fe1dbd154230de7b4c243c4a10789d587eefd57 2 972a045939c649f1eeb344cf8439596f2af7cda40e4481678368dd9304465697 2c919c31ab58090bc379da42c8172c1567fbacd63fb023004624811f19fb313e 3761950f17639be0446419e8da6d35074bf6c91610aafc41a4083efeada3560a924233145cfbfed7c3431db41cf005aa2e009a7285b465131e2d0b53e392c309635f66595f4aa8bac86153a5a4975e7dc169c8b3b3f52328b5b5fb03ef6b460fe23b6aaea855ed77a8e34c425e4c4cc348fc3fe6c56920b5daa5994495434309 false -check_ring_signature 9e3e79475eb428b6fcb03f45199b9a0e910e78d5813925aa28d162e175115733 545bd0cd2e6be0997b6d0169832eaaf5d40b4ed932035b9aaf284f934bfffc12 8 98c5db66cb40763b8586d73282a9fd1446e9270f89774686e745dc5d12f2e6ac 45674f472f16dda38bb8ae3066c947ca62a063a30be0ba4e38ab78e43aca7d17 62b47c407cb6c325e8db12711d0ada0fa30f693d189929eb82472c7dd28b3eda d85cee02af27c6813dab6372c2e5a2e3a977083287a4f79372199f934d9e2a7a b52411ebf7818d6aacbb39fdbd8c4097b1f34ca0edf9d2021d8d636807252f88 47605e1ecdf98d13dd1094090d7435ca9cd25c0ad4e553e16088b0d4215d44cc 2c96b10bb3dfe0c960d43084a508104c8c2f3ddd4bbf84864136cadf8431f4b7 0c3ec8c36f7b05fa405ac840341324fca2c536eb00cb4da5c5ddcaf95d394b28 6dd3547b934371a36a3e8e41c3f7c619a84bd66a9ffb8068c8a8735dac77970e70fdfa81abc6209c8c20c1b5e540e90a94d19fd958d0ca2f732435b32e051a049eb3710e340d57563ca0e1eb914ade545ac71e5924214a189d06a4c452c6170d58744c62f78c34cf0d2b660266a89a297c6e9e537b9df71758a2c23085c291051a77ab8bf352d226b3bfe93151e36560be2622a802a674d6844e4c211e115e0910148b6924bc26a8fa43484e461141863b1a862ba993ebbf791af07edd08af0d91d8f5384b35e6fc14e2fc554b329f49612052398d80c04744418ad495300c02d310b03fce6152c184a62aa025aa5879412c41b28cb866546d6b48f3885e3b06bfc26b1aa8023e7d79f99f734bb8d657da38dfee7b5b176ba919bd95bccebf015ab57121410cc12492b365d74310a1752c3231939ae7ab6635e1c10a6f28690599be488076ae57da0f469503daf9a7c32467d6b29e0b394ea4dce250ffd4b60becd30f561201ad46634eddebec9e34a6152b36509ab4ea01a70cc1c47109330044b5939f2744603611fa38817afcae25f28eeb1e4da94eadb3f1d9b84891e20c618be085bc87a97cdb2a9f1f6c8bd692964590a842ce85cd5dd5913a840e0804ec71004f6eae6bac24caf00ed895106bc16492608592c08278938637f442c20de7485fbb698862f8dcb454d60f9ad08bfbe273cbd5273ee7a4edfc22e4ec2304 true -check_ring_signature 51b537b08f61bb939e18d65622c9439f4bbb1fad724355abbde6178baf25f3c1 8dba861a9599caf9169ab884b0330f40f78a43d089ac449b1753678b550b2e13 4 1e068402551ed83437eb35c676481322e48ccec4099a3b5a0554afb978f5fbdf 29910916633db4ab34104826c92319c7f7473149e6f1cbc048818d4f29d90a2f 0ed78f901cc7eaaf5c53101e613ddfb2ed38c806868e79c4d1bc3db73542c9c8 ff489acd1ca0e4ef793d3345fa6a2a37614d65451740410a62590b038c46fdf3 694eb973047606f3a3f507218ac39ac96abc940d6659f4b68d56677b938aa909457bcfa32af7dbb0bbd20a1d86d89e0c39da0f2b59b18c9dd6580d4128fa620105111fb194f9ed5479cefd13fa84d4e8166ac417b4dd94fa14cc2fc57efe05016c2862f4ce84f24d5d8e8e5c01d8589d543d685bba2536c30b20a6f2e11a1800e56ca616f1c82dafc2df036e8e9a9725253223306b0d01735c9045f6953ed6064ae950a5df09c3e6b912f2f3d81a4f660659ec2a6f79abfb6b551a4915d0160444cee0e5e4c3c7e6715b120fac1493c9a364c071006ee16d474134f31a6352017fa7f2457166b6344397542998211e7c317a745626dc2ccd861278188e9ad101 true -check_ring_signature fe6190cd5005ad3a53bc0cd9bea6fb3c169fbf944ccfc0a5500c2e6d8a6dd5d6 5eaaf36499c0d99024ed40783d1fbc677703d3563c1ef6c95962bf4e18990314 235 58c03a6de82caf1170fb3ba7dd4486fbaa12c0a8432d532cfa4c9407553b175b 8f1661f867e7120144be47af26903d138acabcd1feb04789dbc92a695c48dcca 25a74e7ef8b3d18f14b1ffa30ce481dc65dadad53802eba42b5b379bf22cb65b 3743954aabc8cd46459d326adccf3d685aa7248f4aad6757dc757af768322ef1 c36ae5826cf085020d50ec27fdbd88b6a0f836054607d4901a10353226732b46 b132171e7ba94b81cfdc1735274df964dcc0205f6fc9a9d555cb5fd3350d0136 b7bf451f5982eebd4d7df8febe0ea0ec7677e91d71c3383a6300b8a83ef4899e e32fa2366e3f5ded0e34917df0e4ab9820390e4715d9d2f474af6215af8add98 a6c51cc56f4a66ad372eec2834d83a84af6b26ead4aee7fd546e939ec0953619 6c996ab284157eb426788cbc5e3fe5645a541decae2c9e0c3dcf0802b81041d3 cdeadf1a43628e1737ac44015c09ced4af75611fd1a70d9607bfb15613e82f71 4c007df2939309230a0483e5af0be4231a1b27d027b9ccb1259ed408d58eac88 c986513fc32367e66a36753ee897ef990a405134cf06ab697ba69c5eaf2f4dbc 53ec1bde0c500638d7b25835a2b23b61184b6eb0ad5c3b8799659c622efa2c48 bcefb56cd2f240746b072c6fb8ce3d89e443b8ea91a5d037ef29495be5fd7e5e c5a8c8946f3727f5bab289542d53bdd2ee02a3bfbf16e6eb37e2111dca75b938 fe3a9cbe2cbe7372086af102ae415ac7f055818e31c48e929d5af20980b594ca 230bd71bf4cf7d0b581fdcf2499dc8a345156bddd9f81443b5a217e2d2662975 362312dfee581fb963b1fbc426848079a0ce4fc7c90ea0530a18820a5b5c7a8e 30dc67c3fd47eeb21861f4edb35db8d630cb15ac63ee6436a4bcab02b84a31d8 25a1551e138250688185f659c232c14c6acc18e6bb7eefb2fdb6e98ad3832760 4071c4277c80be69ba32b288156383a75cd1a36c1a243849096acdfcab7323ae 7c85a7d571d436185068217051813d46bf40b654720f8816ce426ddb4a13517e e00ae7053957c1d730a11f3cc78a57081f76d4b11dbc92b4879c9ddfc75005f9 8a3617821aa051a3f953f9af5923e48361702553e966f09ff71d3585e06336ed abc8fdd18f3379000fd9a6254a6b25314f33bdfbd82f7b82f2ab79be8e89664a 45c66f18e63f0a9f30375f1474e10f4c81306f6195221d54ad732d7892a11142 498f42dc2f029d40cef9d98f60e87cfc7dd557af01d4f4da0db05980046e3615 7146c5da52f6d26e44991a6c0e0b270e3bdb87c7c9f695e67ec7391e11fb7d4e d2277549ec8f86a3e768ba2619750e74e9471f9e5cf652dc9078cf3878623b87 c2d00d51970ed9e37da93993c3a9c2b3312653e8d0a97fe1760d346fb94526f8 0a84b569df1c0eadf8bea968c1e5714000539e3d5e6a9e6a30b3d7cfb10d995a 81a3bef9ea0fd1abc7429048aa660268900b47fea1506dee2957a92623e23dd3 5cd1237a96796f02e01cb15cc9a51f1d2eb5da9fdb8ed06c177a11a8a969b14d 131fe75a96822bae5c81e33c5d2c90e15c36421036f322ace7545d290a8256a8 dcf2f4dad58ed2be577e317b83250a4f00f7a5a7385daf455e3cd9cf8506e5c0 76b0fbda33c7ec6a839c4fd0f94109a760dc4650781bbc660f9e8df173a517c8 b8e2feb9a7d48a41f41b2198b2d714a935986340dd4add0c4a2c7ca36d529494 4bd9b8f8763e3f23cf87d475e37e4722887bf9c65933904adfe0f273d36755c4 8ae35e528c9da555bed8afc840c020aabfb6d9a8829f8b504a60d8327d612592 0c8fd19272fa78e9ead26ac233153e26dbe57a2286d8b39bd4f31cb823b5391f 4f07eb49e613536b3d3addf6a2884114c1b7f302e5084ea4454c449e746a3030 fa58d3f8c4fc85850f04847504d010d8f36d347ceaaa7ff2bc973fb975abeb41 dd0099b993f4e22e16d2c03d334d47160c1fc46aeac368af8187cd5aef15f9d1 daafd8134df9516d9f6671ac106b95992279ed8cfa56c3a6b3105dd1bd029874 073b9d59b7b10d9a656d4484776129c9d1a373e2f0c3ce44b14d042b7ab0b8ac 33d512d5139cbd825ff3cc22cd82911ecb25143865f8eb2f5d1fee361acee4d7 7cd81e368f3368d18f25cbf5b49aeeea7b76e8ffb98c8c6cae537ad526f5adcb 691a22a42757feb18d22278a122cafa93fc6be3769a84a766fface5b9db37fed 69fc504ec572b933093782f762ba307b01c7fdb70d29b25dbb632fec98426c5b b4649e33b634dd362156284325edc990efdc9cb226923ca5c0ef6d30a811a319 4a7c0dfc26c11682bbe869273de4a30a2a0da4b6653b94ab363a55a53d146dd8 9aa1ac993c358db18326ec40f3411429f17483222b5a61c9ad0309da69d759ee 5ad6d32bd57731a708f3e2d02d36f5ad0578635720c3b6743f9601a77539c476 0a5b0a8a526c6552dd88e3296455c54984c9b924bae4807e30bf03d2cda84c02 ca56f9cb36b770b39dc6d6b7972a504734845885e7218997e4108fa3714010a0 f3e1c93c3c0edfaf8765d0050b2646906bf1c51ed91f5204f9cea88d2d821079 9a66fbe7ab69a1f955de83c42cd827de420b45786a510d9a8a087e9eebaf40f7 fc38dc6a527a2fa17ef6177b2e4cce70da3c5a8385474642787fb697130861d0 83bdba424a39e29039666c4475e23a5745835344b5df800153c29cc0ddfc481a e43a52b88cab65d6474406d1d00949eb48e4a252e093e43ee00ac913a0af79dc 1f1e0af8fdd2a191c571733639680dd529de7ee10915a68571e36736af578dc7 3283f9f53c53a3e34bb04b2fb42722d402894fa04b6df920bee95d934c59e3ab 0676b5515ef689700c4af35d48e4b80005de449cecd4a703c2c1ede737ab02a9 1b29410a792de492b2d6dd0f87dc9fa4aeb27786706f92e771b3feffa48d29ad 565d58811525d0ca9e29de854f693171f00eeffe191f62726c214a9709d2e013 28a0731c9fc8335f35d00813b08982d6e2a749e89e6818cc602d2dcf10c4c0b4 91df292d4da13148d1630712b22bb83faa671476d47721aa468a07f73eab0efa 84391714db94651aed603b26b4aba8948c01e8298c78c7d3f9a7c6e8116bca2b f8b9eee5c87b30aa7dda535b98f48445f89a581c7ff75a2cdf317f7e727b4523 3791ad0d0d42900211e535699978e95721b6cecee11a8e9b2132efd4310b7e4a 28f40a821c09ec04bbb428321a0a5fe1fb683fa26d22924b17c32bcc34c7e4b3 79a66bb2981976a06d3b039688a20c2c5de1ac5618a08083895c48b0bcbd60e9 41cc0d9b89c7b2432b6cb4b0054413e3a2cc2c4a1cebd29616b16c1059ea44c5 10b7327f135aae25c4e5b14c15dea76cf08780bc4b77440c30532a8c53eef45b 1ce8c1d6be2f6e2137fc02c960e88e831db248472c29ceb2cf1c9a0e0eb0fc8c 26b47a39a137c6f36dd29d42512fef9c739d4b3c41ff0f2d2050b9315ca097e5 c391bffb85b2c5a31764c686549ae86a3c82cb87b2fc207edb429005bd657567 f1978d606a388454fc812e702677c9c4eea3f6854173b952b70e07e84d264c9b 5d8102fdeca171a9db207badac2b58619573723185cb6af8264fa201d745bbc4 d9cb19d1fa3e5e2206364520d2249c7a0344dbb2cd6981c59c3b27e86ed01345 2afc72277cf8dde363d4292761d9eaa20b69285081ed8bf868aae9326479057f 1c203c7f77d2f5ef6aa130450adb0bace5757e9241b3babae0b8a752c3eb9ef0 4a59a5649d6196a0b1faf9f3ea96b190dd9bac8b154eac16cd6e8ed399c7d17b 5bf1368bfb231fbd86d6b9c92df03700a27e9aab80cf882286d984f2ca003b58 22cdfd67aa65b6f5327aa1a07ec26b2f4c33ba68387a50518ba1472d1d2767e5 0659428ecdaa0309d5ce53bb8ab493a8d027bbf913e5ddbe10856d7fe2e27987 7c4ffa87191d3499b94b623db64c282c80eaac9bab97ad25206ec6a05bcca112 6352a4aa0e7e31247350b4eee9de11ad28182104e624e4ec7b24383b1749bf17 a554c9a1e20f71ebc25d721e0fddfb345dd79d46854c2c85660f29ecf1768209 06abe6c8329c336de6db128fb557a77b0e6fb26730aa80e2376d8a2e57336489 befc23f89e0819e724da3d8b6b6a23d572ce4b88d676aacaa75f3d38e44e5b5b 8ef5d4b54a259f2d1309f3f4ccf63cf427d6eb3f3aa8cbb521bcd7465553afb9 6a17e28b1bbd7bdea1afb5b6ecc9176556c9734aa106145d57cb55149b789e69 b9869746a879b6e7aa6cb0d5b7fce651b9d284fbfd5ce627008582e0ea76ec55 1056848166a618fd0be3deacfc7ac8d19980c146332b387cb740f187227edcb2 8294a2eca15026ab1b35244eefbba978361f961124dd9134e2554e1fa3a80215 a126e19fa2570ce541190089974e5b3306407a2a2a7f9ccf07ce65fb79d86434 390aa7d119c4e742d7e5103dd901504d2218bd768235020568c2c6d152b1ac26 c7fc667b7ad11032bf29c3db8fdea132ce7fd9c83e2c9e24e30af147e9e51f00 2e7029f243cdd314adc4579ce4e1b883c6c54b75f12b34da7a522c099ec7ff63 a9d16111b75343a108edc4dd9b840a5d0a485b1ac24e0268957e063f905a22d0 7a4772b3b4add0bbe9db7d92f955b173712d9428104ecade7088a9a59f8cc376 9cb0d0f5f9d711c4645a801bfc6f7345bb1b7cba31d5a4d33f5ded8620453b63 7c02a003ab39defbbd24c1380a473d2018a0ea22cdad419912334a79eefcd49c 226e795ca1ff57946ad3599c4097ae709e83a79985e5baa9b586247e4b77633c bfd572015f0cf97d4b45215ee7737839653e4d28e454b4a9459a65b29b36ef6e cc6223a3fba465ce9512c76cd408c267e4c2e93c73753ff3d41d8b2f02674f55 d072362cb97b8c1f33eb24fc33484edeeef947ae86336c73a7944117e8cdf4d4 324d2e0797cb4466affe3ed44d10a06586e02d6ae9c6648521b453794622ae34 3483cb30749184a978ae8df901377003d88ceb099b223873d1ab9a57437a76f7 82b796293932cf54d5e1cf5ec16b22b9ee69a9fbe0b73ec2fae819ac8de65a16 b60dff86788c240a2253931dd1bfe6f6ae203ca4e6c3128c0e72959e11746706 6b50739594944244ea398bd063e50e93ef0397420388537aa2eea76d35385af0 e9e92342db01174760df085e233f41789f27c54ac52add384c58e2b54178ff5b 2bc0f97610cc221c2494cb5e33be47316c9bffa795593acc32783aa988a668d4 803ec23a9fa952861f06e50f610fb02bccb394c7ac84d83f90e2d03da96e1b77 711bb1edd7a1e96edc9d9ac580fed74d4657ecec5ead9fdb844170043752e5b5 8d977d51a943fa3d708d3bffbbc0c43b12034a4053fbc63136f86ca869def190 9430b26dd888eff36137cee3f7c526cc7d321fa60006cf870875cbed3ab6c11b e330b404e8e21dc75bd561079856c39b7dc598827db4606d1157778afb8c4acf 8ca2178cd32a86f4ab5b02089f717c546c3bb6449956f1ff025e7e1cd5e22822 6efccfa7fcdad8f4611ec176e08d7911fef3688c32320242421fd70672b792fb 258cfba638cecac1b328963c9d3a2f7fb0af7b4c63fefafdf598cfa07a9aaf5c ad0713c745e8b2f166eb8dc02eb52057afd9155870de2a57af8e8e09567aaf82 09bdbe7df42b20c3d792ef454414a1fcfa87d23e4100184690a54bc6a20faed8 cbdae18d453d4298c7a30faf9d821afbf859ad7fcd442c8ef3cf2e3513eb6e6d d8da45b42cc1ee535abbe7b106f0bd5dce79be491914b5fd466b27c03c3087dd 7754ba7ce1056280fe2900084157280ab1c4b282bd35bcf581eec02ded2fc717 c2d3f6527cdaa4f57b9dc678741731443f2cee5294a290ae51843665bea2ac16 df53e1b9f723fb4b6ca72b509a7639bee3294e322a9c3c102c23b1b8f96a9401 ed2d8aba0bb91e05a147bc742e2a93a6641973f90dde35f4a3db0da708e3f296 b56c06a106819c281d11eed71ce02d79bc90db77a4c61e4e460ce69fc78340f0 b24845f90c734718cad0f2f7615ddaf9695c628526b8e250039a46096ad422a9 3ea9c0b1a87c0c8ed5c582057fe3ec41d85edbd78cdfa69260054c4ffaf2cabf 61dbe4432bcfea579bd9c0ed2133f8828d5817ac095c91aece413f5649cc82af 3618a114a7838af95c99e1816ded4055167d37eed172567020eea0bd14710c98 9b651d719305d0168e6eb1693c118227b60f143791a03289c5c29162eb8c10f2 38a9b96117a4ad251bd44fc5a4bef42aaf9e3bd7ca98bcc41f05c2960e50b434 e964fa3d8e32c5cff950d09e81b2ad3a8773e0cc1745410f84f5b498a3fba85f d373ff4e6253aa46208016272a1050762eaade074369dc2237e5f511ee11a9a9 227ecb8601ba1efc57480b5cd3f4ef58b3b0da79907b902b2693656cfa56b63f 06afc6bd694904d8a4c1fe0bf71800de773ea5f101f6979c79a70c08dbdfc7d8 2787e6251e3e289c24a0ce77f361d44d086ffc6a68a915d9c7c832171150ad68 3b2a65a510f0e13bf3e1237328ef3d307f722512cd46ec0c84d9123f3ef88a7c 056f9eef8ac9cfb4a070cb38c8d5a091cdcf7da25612b1c7253d20b9c7a35ffb 373e3798f38440f5375b08ec65c8f5fab55d66f019733bc9269d70cc897ecce1 b6a8e8ba03e88591381a0b5daa854bfd0fafb1a6286dac73ee83eecfa253819d e587f863b52c4d7b0448bf14e8f00bc7f1d341a6dff69922ba93771e0a706687 d0a8f67af03efb1a8e336eb4ef81bdaee8f3bdadb38ac9e0e609aff3469bc4e7 bb099d9d42be4032acf4d2683f81b8ba45f16feed967af69e67ce9a232cfaba0 c5411f841fd1ea51a68fc7d76f6abc432b17d82bda5616ca92e50204ee1916c8 a81bb28834db72f270f933e7309960ff363a5457e0ed3148ce1d87323cf13852 147a17af4e35c259e0f48bbc3931eca980b63db68c0a5735efc03d2a2abea84c 87c80db1953751446db9de993efce65269fad4aa3710c7ce6d8be7efb5c78d0e afc167b27fc3682e132b7c801288746c91df8bc4fb8b13b94a23dcceca0e876f faf0fc8ba299d75f2825f041c599bba1b1335209aa605857bb8445a82007b6e4 9113268c30e728ec593db4224e7991f728f80720e5d2a155613b9d551637156d b9c7d2de03f5c2026ffdc125bd1ec059645e2dafb9a31ef1c9b788b0e650c953 75733465e9d041e21018a4ad6252b214c198e77276675707b6e25be6c7b06d75 2cae7c3f96c4adb475f463f3a97f06db119363b26b54d58bd82c3a7eac5abebc 463c4d6734648ff78ee38f8e84a912c3246aec4ed4332695e22fbdb5c8180c10 830f81d76a53ed4f25f6d970fe796c13fd7392752e1f607dcecfbd964826dcaa fc5ee79b57bfe5088db67e08f4842c6a6c298bd1bb0d4c59352ea7a538820d3a 735e5d93a15d0b5245d16d3f215bbe5e0803e0509fbd26b3f58c0161093ab553 5f1fe4e4b9b0a80a1b3300f7ee59dab7695b176dcc26efe6d7c7bf911b6336e2 1cccf50645341e56b69457aaa46c112de194d1e942661d75f691a7149e4623c9 0514960a760109891e68341dafb008796b55ae51029f9cafe2822c769493907c 81b38df6cd550b3e668db5d31159d2d9fca1ff1e6d44320e0f43d868c1355288 30d1350568ec02a62dc11518f7b1db974e93fc97ed51e8a69343b027b411d1a5 a39f68f2674c62108c2bc0b75775e810f5efc09e224af870086a4be789ce70d7 5a26c736de1301dbfccb8224544f8c9e0fecf7730047925f75854b626e6461ad 5cb8acb0a68ba861d759147da02785341f293ec5f2b9edcb4adc26b8bd9078b0 f44111a669fb25089cca63baf48eea28d16575d61c76a113f5e8fa173cd4acca 5e064407b8ff54248d977f9203bdb1dddbb933062a6bfa3b023c3ba5c7f42f01 e05232fd2b0fbad94b7c4746563f4d35860a190863db8070f281ebae9741d607 a2df3961690402b46133d2868a67dc264c5147121fa44d82ca4bafb5965740a6 109da6b3744c66645215fec9aed9c5ffbb10a3e70258672cd91d6e478e265e4a e839ad76fe7b561193fa1c1ef4f2083de45b42c670444668b4deed4d2e9da0ba 4457d7124b01d0a8bd07028e678fd23a6a6ca1ee5c413e0cf2c3576fe08a0abe a84f7fa6b704e9fb8c098f56e4d152b90e6952484c4d4098ed0df3cc08bf5cb0 e5ec2cace05c7cff704de3735a947e4c8d3cb6cf654582795cce515791d9037b bfd9f24bd540f1ca988be5a1ec5270ee400f69cf8a276451433249ff889e9c41 5f92227fa78eaf428356c80c577afb57209e9e322cc51ca1e9b08b58c1ded459 708bc3edca544e220fa21cdf699c02be3bbbe8d88762f9fb0c8cb652a8766333 889766535fc6a2f3c61e401f737c476739aa81a4ac9078b9d8bfdfdef55471ec be631414b8080e62aa03c8af9a2bc41de31a8029ba89695a1d03bfd671079401 93aeb81773fe1b1e97e13f34cfbaf23fe2b2acb6150be1406b9574f988c6dcdf 8d9c0d4ef1c848dceb8770d14bb5d2435c3b0d09cc5ece0d8d734c0ffb50eea5 5d316d6537bab76e9a04316d364142b40b545a2935e533637b2504e25b6f17c7 685080a37cfdbd9f8e71ec3a8e7903cae357559e166c8b089485eaca8a29400a fa6ec260f58fa3163b1a0cb2825f64252f47fb22662f2e5f0acf65e78441bc02 513136bb071eeb9e5b0d5a6a6ea32a3de437bcc7ba71bd7ad4ffcc4207363100 9a80a172c93e82ccd11d904a1be8bf14e59a096bde4cdb63d593f29efe877d13 ffb831a31a2239f20bd8a56d552a93d8385083d8d0832ddcc3c17b0fd5bdfa73 20f4b91b7e98fa52f15d707c6426725bb928ff3a1f30a9b175da8857ac03a280 148f55b8baf0ee30cad2985cd1f65fac9a2b888c0c8277d30e9983ef935b083b 6cd46d1e52b8e21c731aed4c1fe624a50e72143efdbba62acad10210e714b3af 8dd27e3c2ce2aa473e351a75f33334b18d313fdf04b33873676ccfe0a0192be6 4f0a1598aab63406e1128cd6d69fec163e28d4925fc01df8da44b4a21fb43323 abbf0c6077d3acb5ac399113d643637faffe2af76d100e36193a68f9b668bfd2 298a838b954f7a6ebea7ea436c3aa2e5f6a46bb76b3284b4218ee4a3aaec94ef ad68bcc997513802d5d32febec94bd480b7c0a2506b12959daf26dc3c434fc4f 197cfc3c972f13c96d90bc075c109972d24150a8fad71bf5b12a883ff4d6e3dd 1839880329f78c5f04d824986eb217ba26881465221f6fa6df35fdb5a4f89ae3 df438cee0d3e5f4b55807182b68d3b02187db6dca50862872726ff3536f7fac4 8d2f392ed75599f94563c74b27f52e73e16d508ee2ab7a6ee5b5024c8a9ca36f 1a008f869455e551704a462b6cf8f5f9e2ff5b835ac1e56e305ab259b71d59e7 671a05b5c7c2c596a306d8e8637137b737fd7a60194b48a65720acfad684d644 1450b1a4b64b6e1770cdbdcfc323c9bfe17fe591fe22df0f82ea09b945aa944b f1321676fd703fd31c1cc2f57568ed1089affa76b86563ac3b8c62388a74c1d7 5d225d5b686b78307148b76569fe25d8d40ba65906012504e793a78b4a78072b f821629da8acfca9aa46d35a701c4b3fc3f9bdf1a4d6f5f283d83b680f365b8f 720f46ff1ef8a745b712b45ff59522613f662541f237fbba0d363345e94a39dd a9953ff55095f3e025f98fe89c70448ef8a0dfce98a840d2a6d7d7b8f7931b93 0f590a33710792e7c7e3b95f91292d2e89a85b2c95daebe8471af5718d244a0b 741e7a1b0681c735dd1f869c5b13b47c15f2e923392ecf227cf2705b6c3a1c05 3f8b8ab99e04547b1b167293a04ee6372b953f5197ece1c00c56a4688d207257 369785e785011a69b74deda74fdc8224397e920aa028e4f0fc15505dea73238e 6d8d1dd1c396cac14cdcd5f1993bddda32d5f425a5d8bce9af11a78e4d083bb5 80ea9f7a6a51a7937527a0d33a9aabeac456da51a2b122322abda57b99f8f334 368042a150049dc9f1bf55059497b0943d41542a80390f0006d826325d80fe6d 3d62b286c69e9374d3f08a6edce5871e57a40acbbbaa133f4d4782a2273824a2 6b4be625c2f8f5cae61413507486dcdb6391d258ebd6b318bccf67f61b6131a3 f5fe28859a20c253f08bdbbd2f7881e5d7df3d654c71248ff46bd555400e8990 bc7eac3d644184b4d24900f71e0305647a622f555851c0cfc62111cf9677f67c 86c8bf3322ee8bd3bd8073408a6b6868414b939619603d935c2e348a3e15f5ee a9376635f9ea95face899ff8eedb28c5b2aa27d85f184e1bb789181b7746cb1c 8f2025d1af3f58dc4bfb1fba15b7cf55967b0673b14331e306d1b20597730abb d145d1ebea4631fdf8d422cacb2faac25ecfab118c25d5fb683624791a621189 25404d422b37235242b30f9fdc1906cd4b12ee8429e714ed6c38eccc697495df b5b12c0dde77d013b19d2601b9326b08ea4dd87f2c732df4d2058eb6d20ae890 414cf828db333acc02e65f0c91ee10b1a0511ba5168f501f796cf6e87f24865a 590aab5db0d58113fb5e6c9bbb2e859d09507590111e779e16457ab42cd48fdf 4338299a9292166cf5ad4330f66a1eb80d0a2e7c54a1513788582a4fc9d56380 374447c6d6ca03c31a1be4316bfc55f9d0c9982e291b65349158f4dc250608087b689134cfee04278e8efe131f521d38bd2449dfedd943e4daf081482bd9c305bb9a9ece96386b7ac94efafa5548b6e164db7f1d6eb12498eda8fff356cb62035ed79e5a0e14ae1519b689fe0571de932593e5362415aa942c2ede5282b11e0e910fc4e826f2cdd52dde6479e19a4aecf04da9bdda4e800fbb2398f95a13450f15a62e8bba8af17ba67e905c0ea8f9690eccf7080b66a036d3e764ea3e50ae0e940a8908354d3663ce452bcfdd3abb2eb731a4e9752bc57cb29ba0018e23250e95db531b60311a238e83bb9b6417c46f244c03b387f7d755f2bd6a3c5899390ba29256511c4060a4b1af0bcb2c0eb7a81cb7c7b8692253dc31d7b0398ee64000f71cf43502eafd848da4267cb97d1a312c0d0d5cec4c1d7ad6cc6bdf9787b80bac103abdb63eb8986bedc79ec428b43bcf76a67ae085962f4de6e7b392572006f2fca94d821a9dc03a43dd91979095a112a4c7d7112a1bdabc869a20b665dd0289db461f87ec0c7748b0fad2cbb8489ac9896a9d390c55222243f4c7841cff010b0c767c378073a66355c7c5ce0e7396196d5c0e1caec1e9ed6ffbb028f4700422c9cdf5997f2672353bb91c450377c80dac17143e8173b1d2a38b8da7cd600904f04cce2c31ca88a414ffdd0d7c6b7cd43d56733c21856ab3e7417aeb1ebe0420cd2c36e9af614df0d02c2fd9ef377f3b76947b9a56f89aa6e0743efac0570238c3776a45ebc503b568f90c2ec31507829cdce1d462f2075050817e061616029df6680d654113699ec1f94b26bd2a05649dea06888d7a42f9b00239c2ea010ee57c43fa26d20b8bb4d3854723d637bfbea03fd86e7049a64ea5481ef6d92d0ec5648f7e4a295a1f5344b513c92eb8ebf555d638104325abfb8a54fa066274017fffa73ab982d83aa7f11f7fd3aa985989c9f3798e9c7ffef35b4c8c74177a0d40ab16ef1d7c65151cc763fb28d330303a5ad7ebbc563cca1dd623fcfd4c030372a4f0b3b14e311c5b092bdd7c389f0840c201188741b0a32731db4872e83c0c02f56d959fd74ffb2a19c3c2fc4c24c9ed72b420994c4de7a5e49b970c3dc50c8ce8497969ab56d38ce2767204e84f5254c22ac771f8e832751b42b1988e48061e10b0d40952488566a4cf758a313fd123bec4f2f3967eb40f561c4aa267c4061b37c2642b509b405d6c90420d9aa7273381f54b73227ef3c6884b45ce32dd0c11e309f87c2d32d961a6684849f47ab3893d0f7739e2a8306206bb4a460a6100a66c518d3dbbeff0b5d109ac5c31edbdafb1dd35facbc9003b7577114320a4048417057b00961015853a387385579072b9a90718918168151f5108e324143a029ac60fec6ff77c560f785dc0007ac1bd91906e545776ac13804adb5a6164a906cea23ed0fcae8be6148458bd81098e7d733650a593df79269ef48f7989b32509c29d6bebd347fc56752ac60586b9bf9e8873f934b6a9855298f3286fec0f8a00680aa0decf2d735960db27dba29b013a622985f6ccf149871c69eaec4612240676375a9f046e0f5b40e250fa07b28ee84493760006fd2a89ae150bc0b4fb4605e7bb721d69e87d0b4919ca3ca21cbe17ed6cc4b44ee3bb6068dfb534b727c80cf1814612822b49f933dea9b088613cc3a55cb3f76a022586c8af05241429350c42cdb65384c61a8f029b48e9fdd627321b7e620d15a74b0dead20269c77cad0e9d8ce02952357e78e17c5b295b6ea67be7804e91aa22ddc92c35c41596e5130ce2c781462368f159c1d8b0254f52152ec3fd26c16cc562f6a95e588ca9e4200e8d2cb3ad37ee46e5e648e6ad2cae494c1e139e1adc2491a58b4ff24f57ecea007c09510198f2bc269b320ec6091be69c31330ee2a79b12994e5b3a95f0e14800b532d2ec17f35b0c71c90e496f5ac8e091e4bdb8e4735dc24b1a650ce83de80f51e5a99b513c4df8b4afa359eac5023062d02fc5a368af263ff68c820bc4a3069eb590698fb209cfafbc996fc8bb190eab275af6672f4e30ff435829dfe82700e7fc80fa96e35d51a1125102cac169ede8a610b6982e29eb2bf4446a70e84d025ec3c0c2273636887790ebf0556bcd314695abed14229a139c5367a7db772d02cd1fd78ecb4a9263487d1a63355b1df93670057aa2f09b0b6b757ba204bbfc082468494a178ed5b4e26cfd735b8728e327e124af7ca246320c365e63c0c5010b6b191dbd66d4e304734d6aec77b97853ff6cb7dd2183a0035ff5c2be7fc54009b19f48fdf1ad80ec57b435d0aa719987dc194afd7b688aa67fcc1a61ff64840dcf535eb9a73ed8800c0affb552800cc4d37ab8232a3c8e22411496166de72808a51d94360097062af69923234ce9ea64342e8bff64f8a05588f8476b325424094607a60ba596a9248e9f1dfb5bb477ed63601e479b80c980480a4e9a15399d09720bcb648b6551af3915f1a92a7c0942fb1cbd5212c730a06315841c6b087a0f038f19d6a6b7a51862a38fff37e4a474e65f97fa4f5abdc44133b81d909421025482c2dfb8c1092a49034de15e56f0687905d3872151aba59ec193b6c2eb1c0522322ed1ff5b6a6da095e4a26dc713f2ad0378def7085e75d4d276aeae61ed04811b1051ec66d618d68820e90be1a6f3b0809f93ad28d825cd256fb13e4ef506fdea32dd18e07c4689718c7712ca8ab5239a6ccf0fd455fd9480e7349856850ecefd4bb0c9efd094384d93cc62f523007c1cb39cebf4d5ce4ac3477dc836f208f324c9dd30a96f2c97977f4995508ef7cd7a248c81a2bb19aa6e6dcf213772088fddd56ad7113936b8acc04c4d90a5a5069bfbc0e31d2da002dc3e5863039d0649644776e30b476f8be51be5248e57442a4e587e423f01a9d3637786810608096ebf9d753cb9a2ef3ba6502e2c6883cf81f29da2d418243d958f4b868fd7660dd3b9fec23126ebbff301939cd76df77ab908aae0f4253825a32a0864fc17550cbc6bc054b6970d30123178d6101dd9c0cc4d9c5b7ab07273b50a628d1f787300e9ecf99a8e04a33c11fb1d23e527f97e9f9980125ab11730aeee537fbb6b3d025c5e770c9524683c7c7d3b1df4741cb3df94af46913f8351c3350923400eaf00ffc0014ea1d67d852fdbfbd9b8849c5237edf1265506bd4ddf33d2fca708de00789c7e472ca0ee7264d153d9d8ca7624b7825050068c1a5df61064f9102ec90121ce2ca17fea90ea0c057c2fe2c04671860e4046f11ab781fc2e131b787ed20584548172fe4ad21f0a0dbe787012143d65a5be7415d119fce412dc3e4a3b9d0d1ceb4b06b72d3ce54953ece2bef8f070d5bb0f017c9cd26444f19db3db72700bdc7e9247ea47578271b8fce23c7fbb4755dab07bb83bb8cb22cfe8133454fc0304abb7621baf526e87a3a49e857d58a0c4eee554837825cd6ea3fd6dcb58df0cdf438db51bb4d88f3d50a08eacea1a6c4d9a0272b3c10821d3bbebb8c6c845095113a157aa5058d0938887b9ace1de0713979e3000c9bc6e62a240171ce0e101eb74e86b28146244aec09b3e597c22c68b52ab2e05f03c8067b9b3e87a92b00279f460fe6f523929c97b61238ddee46a1d5fa1b089729d58f942e69d701da30e910aa06f2c9593241b963ae0bc2c0166576b28c30852d8b047a3e0e4c599900fc616fe346aa72ced504df31807167ac41668be54b63e185245f80b089a5fe9016d0b94918a3f9c55ef4bc89585641a317cc7d545dc8366a8419286046340bd0a1b67628343049bc4e3e27a613fba6ccd73c51312109353b7287d699adf408808aae9d7f36d90a10778a3ea0896b968a21da83ab1aa4f8e109630600572c83b093b17d41c58edc96add4be78da0959bc8dea6345771e58e5e790f746cc1f5730011e61d0d2247aae3ce8c40445b9240d88575519b4e7b837cca65ae2fa0e479005408fc95616ffeae777a79108694a6fc20495aaaad713fbf4222d1be6f3c810a472e4bbafaa60155e7a71392d56147467ece54a305c6b383308c6413f8750d091cde03f3afd0bc6867d7775816dd1b9223265ffecb76eabf2253e12c7802be0b89531b2582eeb8c6b412a361eaf5285f57a11c741a27f5ac2b94a7300dfe510f6f60332d2242648508db99c6311d404191b764bceb715241683f846ef313fd0f885c2db4b71d993da7e7df53e0ccdae9fca6e15d3c2dcbe5aff81e6a4d0e060659820da2214c4e8ac7a41ed1a717231d839ad943e4b9572df21976ac74a1d10c91a5a1122f34893d5c70d48a14b0e56dead833ae0f275813e1e45888ca3f55069c3956bca87ca7f39beccebe4abeb115f2b1e03697c214361a5eda13db05a003f95b706789b8c2a09fe95279c6ce2ac34b9547ed2bd660e1bfde639b994dc20900722d10de13d7636d50e2b55f15a47c78fbeaf14e9172cc22f7034c80db2b018148879bb4512885771c9b97167cc8704c34ff0bf14711b2cc9e0d1a199ae10408fd10cec76a3fd5089f62a1d5bcf1c7200f55d89ed1bde230053927c436870e3ce558ff287d8d272ca07fcea28271e5082b5b71c3bfa7e56775a38ba5d292037093cb7c12265e32403e4a65fb85ce98ad687ce4b78444db759ade045ee285074e85b2f9ca760d7b7b4ef9d13637f74e8c89ecc902e4e48841ad9af805408807167a0ac2584742bbd133cb6d477a8d8968ee3c5818bf1191347dc4fa910ed403049c5c44ed0314b03f3e38fc02927b037723f9685848e90b6ff628580341c8092742bf6d9ce24dcd490115f99f847e08e5d4d27ddf00625ff863f61770dc5008a3728097199c682f168ebd593219cf6ed5668b7fc0c5af3de78f96197686e70a00315a5c6f522631ea69f430c2f95d86658620bcf6d3cd4eb134a05ff86e370cd07f2155f8d9f7844e7f7c3b7c5632880719f62154888f78133e81ab0e16ac02fdf86f55bd75ee3027a572970cd385b933aa58602b35536722dcd1a3b4a5b20295400305d4ca5b2e9b2a07adbaf79381027235798b03f2b0cd2a12dbfa2d970bd5cb6c4690e41eaf079a60e6891f231d4d803ab9915877a39ed1dbf04b3e4101aba146b694995602b4066bb446a0146cdff8015ea1e765d217f0b686455c820dffcc3c46a5ce3bd81c199f468dc49b9905c5d48948ab8e9f162b9f6deea31e0feb5aa273ba4bd1bfc62a353a3e5f6439512fb49ee091e77f3f14697bcf0a000570f60d262d053e688b0a38862c35ceb06c064a01a18a08641022dde95a623d06f7de118f37187ffa0fdcce83b5a5883b2b7cd60b4a0894f87dbcbde6846df303dd66f252c895b9dd6d390aa291fe712d05979dc5d633ec16661a1c6c0e1deb073ecbb168bd92bff652470624008bb39e7cdfe00b740cfc805d861a6e9237a50ef8ffc0b9662e5bf99512a4568a8493c92862bbb58f46eba41147946bd5bbc0020adb7688186ec3b673ee540caa52422a83b89883f7152883b3d5a9914a43280d4fbe1ddc8b8f09a16dd77276be826cfcfcb50fa4e5ad9009fca53b51899f76001e2fc1dac9d25a67669aff3f503dc3fefa58c420e4b22ed9bf7d51cc15489a0b6050e777019e10fb25d1c5d77bb9b684d246eb66cce83eb611d984c12bf1730a1327b3bd93c8ccbc417773e922d678438b65c1cf3d570bc43ab93968ecaafc0cd38b21a588063abaf4a9cbd7b0e6691d6e6d4ae170936228d4e6dcedda247d0f97873907fd7ac5d97b8f81aba018d80b2ac30655de7926b3b33fd7bd1b7a070654466fbff0798405e2adb0b96a1676b930cb7a9c8b2a46ad7305690756d27902c91ee6e8bbe240801c5b1cc629246d08b8d8a806e1858144f131bd4fe533140d15f105e49c0445d8d50648dd57c53d08dadc678478d9890826d1ea5883018903e42e1523d5995c20eb4ceefd983c6fd7f6bf1d701e83adeef59eab63d81ece0327515e049ca5e5c3a8c46bd0b2d3fd913173cc5196a40d6577ec07a81b515107ab08aaa163faa40b03eab5b565dc508d35b40367795b4e6e436e4ec0a7c6620e22a9533a9f72ecbccf0c2ef9a06a12ceb6dc9eba893fc77f8e64c4058761c20286e6b24a149532f8a7bc6695a2fd24c5e6d29aafcfbd6097aad32847e4dd9900818c6959f18a2b3eafe2a30dc53ebe9265248128aa6b0ae4bbe2f2cc96d9090751eee8f20519ee0d7e11b7b914e4c0ead26d43db885a06d8d778c737c1a709098cc6c30329dacf32c0688bed4d59c60ad5a55662462a534ac11b96d03a252d0ba62d1a51fa7ed723549431c1dee36a698de82e997324155cd128f604ccc3340d9464e98196f35991f5f820b20103286e186079c99b4ed12c4655b1f07fc16a09ed44d342fbf5a755c3cff177708343bc5ee1d548411510dc92546a1f61780406388c0cc39f844eebba635c20c2a10eadfb29b84203c54a30b0a13ac03d0100057301fd1015e1e56e247276bfa04741a4710e8e8f324240e74ebf49d3e37acc0fe9bc6ddf2843786ea7285bbbc9e03361957d9aec1c47638097960153804d540a8b258444eaefb3fe21571a75cd129e6659deea80def5f2b7a27b9544f89ea20ad03f777f949d4bc0de1af88f57453f38c7bc75764fac6c571387cf2aa3ef0d09b6b527e50956b1c234e2488eeb9f7e935d2fe683b3d562bdeacfa058f1e50f0a17d8ad9ba7ef65a393c503c3fb63d802da916f019dbdab5beda55da802146909c90d38c46880d57a41ab9c9284a065106ea5850ff1ffb85cfbf6cae2ac14bd0cfc28c21f3a6005c472354bc59f04df0eb84825de7e2e086fd3bb7a3fb7424a0edcd729bc064b8ee4ee42c05d6015da5888abccaf2ec2c2e1c7ce6147d82fb300f00c9cee13aaa8d98a9bf2fd110b5a8057f1d572315d5fdc4e6d20ab622e50048376834bbb218e4ed41eedd00fafec5d373625c38abd77a969d9be79b9ca0203f1db293b453c57f5610e4862ea439b4f3197328e475fa7f0ca1b38995da3ce067d432ddae6aaf99ec8c3f496befe6b47de7950c84ede6cfb6b2696438732150a151ba7bb5f8395539a75ad892c6c059ad3007d0944aa8d19b2041d0056702e0e3e11dd72199bab60f63737712bb504a220e3b96eab71caee13614ff43709450dd08a83a596c6517de9181477af29d8d4c0a53982cafe810cb70833d2807b090a1f3313cfd4a9f6215760e11f4f3ebacd1caf41dead55d09de1dde73f12d7970d467f9ef3ed4c59eb1a5bd16bc0710a3623ed552975070d69217d4cbd9408f401553817e59c17d749a2d2e1aea28098b14841cdee8af9d0d69c07f45a791c0e0cc87f22728c5cc385eab3c6f671e411e542a304ad6d356a3ec958ed12627d720da363c704ece9d71a592993390359a0bc5ed09dc201c072bab62c9bd28f3ad90edf21ad2ff2d29bfa4d89ea6ff151eff68829efa6e47f5ffc83dab90194f7110050b73ad5e9c6213079a763c1d700492826e805c0f1d917e33208004aa2b9c50adc650bcb0ebca63c49cab9e48cac257a58e066374e769c76e38916e65a14650f97f691b823e6306f8241f1ad8270929c7c88ff227cd2c14576f2b7263329ff0c48b871eb19d8dea4b81a5a6ee87e7035de2162499b14cc3e0e8e9a5929446c096e769c42f220779b7cef61d3069c6f9a7019aa7421c005d488b9181997d0700fcfc2d3fcb82219e18d2769ac227f8bc60350a70855a1a25ba738f13ef0ad0e03a65332777f25b906fb47561d24d33ab70c589211111a9f46237b430c27c6920afaca95a2600179e6c995988cb2c90116bdce2ceae46dac5f65a06f845fceca02dd343fdad28ab97b56b60d77dc34caba2ac1648dcf7ae973507e7df3c602830426c53722feee6099fd5eeabf19f8fb2c8b36812162150373a44da942e2a029051004ed728d3391d9b12383f6dac98907e424857cb1503ddd45d3f7e93f458700d0ef34e4bec749d5624ec09ddc106d4841f4159b156ffce89d98e19b6a26a80c217957daf3d15e803be58d6a48805d7789fb8cca047b7ca780189639f7f88c05e4c7b6ab1ebfb37089d9c4203ff36f1b741fc9715fcb7edcf1db3d1d2da0ba0fa78eacb6311d77e38b8aa3b62d927dc0c16adc5a982b47b424a5b2a67ab0e801c85346d9c831ccd5104f26354c21d7477f347b6972a6b0fce889575d5aba220734610b40567b89a247fa1bddb57a85a8753247d1f50af44bc76bef8ec904f70c68d4dc9458752ed25eceeeb26b16666138192a349786decc349d1f12c8ff4c012cb518ffdde36b717ad8a186e95b896fc3e4207dcd355b30f031a919a18e7c0f5a6d3086be027c12d852cf6c6d07923a0bacce8baee61fbd66b2a8bdd088840e8b53f976779871dd58e57a5e0a70ca6bbe2d1c9c8b20f9dcbdf92926e2ed1c07562247b9d9fa7e4bf1b52b1df1a5acf48984cca814ac034c5afda7182d5f45010b6f296519193f0750f0db07deb40ceb37518c2b888ce9bdc59f006e278fa80378aa24c6b25d7f2522376ec67ec20f076f74bec26a0ae3bf50791d0971f4490a7b7f7adef2ffcb482a3c92dba61823a270686fab4e07fe19265950d310911909343ee59b1db30f21835162fd0760dcd7f24b8ce79d8d654e3f29a906101b2403f29aa641b978f8b6d7cd22b071cf8b0a141be1873a2769110f1463455876cb0e61e383b1dc04c461daf6de60e1237ec233f8198f85cb30e53a629fc561c28c015c8e7a22ce8b64fae641d1629fe284af7dfd2965546dab81c630e5bc5f2fd208aab5e1624883b725e57f43549754d578c1a1e2b6f9fbee07902fc9b442bd1c015a7dc092b54d381e94e8cfc71e08177dddc0bc30ba9b7cf05048cee0b9800e0adfdcc0f75d1a3d42cb031d4db00deb844bb135be503e52b12bc7b31529ae0302e0feb8add585bf08e1b9bbccef453232313560cad3146487feffb69c35556608bc752c3124c3ccdc5616f783bf7e01b583f986d195ebfc259f27a804e06d2e01ca40be178baa62c15586b2e8389d1613916754bf0cd704dcc8959f0775c1920c8e75baa403dc9f85cec8fb26ee5427d3f266c236cd9188aff2b36022c4a1750b0c838670aca6c175251cf9f19a673b8e7c564a7a97a89d46fe545a8c29cdce04e4392e27be3ae1545f2b6ae8d17602ee1d131215f1dce0bb4e1e2f5218ed9201cd3d9d4b53f6505bacafe9fa33132661a1c7b08492f58ae24ebef01d11333a08d02f68fc28d75072fce9750d71838dfc60143fc46ed375036ae4f8f6de54d0032544871dd6f15114e80a6301c0e0e5f843e5e1f1773d31cd3f38bb77b4ec9e0fa1921923291b64ad473bbb8467e56ab23f18cd3102fda4d38d2421c6026b470598d2eef13da83115385b56645d479dab7fed6816e6c18da077b14d8ba358d90e635cce99998d1fadf1bf2462a6672eae29d57c8af5ca61a7a7dddd39630d350d80a9eb7891aa3895b09cf299b6ebe47a557a4aad3c16cded5ea59180d15f8f004a93c8aaa307aa61b12090adb573e2044fa05f3f1c26a6b0ffa8392c5a8d1c063277404fe01c5746dcfe6b8b74b1ef53f5b24dbee44d9368279827183202120af9471c7ff288794e956d15222ab18980fcd0b459ae2b52311594784696a5c30f332862d8ba626410f156c2dd0e1df76c413ef189ff930fe84b8d28c2b666ae023e98a10b9b219c8fa86a589ada72ed209d325183325feefffdef959492f12007a0a64dc6118fb55e5e85d16b107bf244b9d6f056b7be11eb3d610aaa4b2c6e0f373abcbd35aaf545302b585556c0e5deacfae593bcaeee0c62bbfd9201195d022ae5ea81a8c97b9918719e279e54903914c1ca5a73a6ac247e4d6d3ca37f5804db63af09e050a488c8a882f54bab3982f3c15409aa48c9c2142428cf4d53600b8128cb7613afa66c3a774180d3ca90b2abb8968b57f3ee240be29d1bd5bffa0b1ff0549a2060d2c8f63b93cf088225dfc071426029bb01ff38361cbcaab4ef009a0b7a14f3900f87b1c91003f4d8e4ab3b556eedd9809939c7e326aa71962901ddc9e281f4f3da4eb522d70e2636adefe3737b3aeb8c9f0a71a038121f1e5c030c8ad8f6d35ca5d654eeb1eded4542d96b9b0a5e0b0c4e46c1a8908c2bccbb0cb120db2dc5a32978ae979072ea5be48de15c9d841fcc15395f00142a13ef3a0e40eb0c91d75b20e637517028cb42eabf91db7c2e129a0fea1053f98ba3e8c9017496f8b73bac805b3da60198aff7efac6378887109a9468b2e3e11c7ecfbcd0f774c26cca98faa862a97d360bd7ded0ed6624fca3c7957c3de26d40c4972720119e3d1bcf9c57d1ea54d9b319266ce61ea5bd5e49c5131812096d7dc4b75900dc6f426ecd7ed679c9ab3c936fc3d97e94282361df18aa1a45556c4ddd23eaf07681dfd3a37d1441b583da4c5c8a398b6749a50f22512f8fd7fa7451256a684004ab4c7267ca8837280d7742218e10e67741d2e7979c78bf598d5ebf560c2b60b831970c127b47f8735e8109c12c802a112c1f8c11378defca341ee16970cc10c4ed80189eb7ac1ef18a6af6c4dd4ae98f3a868bafe24236aea16bf0beac076032d4f006cabad35f4beb6864ab65c81ec278aa89fdb8e1c27ae7cc18d8fb8f809f89121b6ce60a9cab89d8f6f413fa75b0544e283d605c4267ee18d8b476b9a00af0613a0ce0e34e4f8271d85499ad170ce4a6a87c234c3e09ed5774b5421c0053da6bd3a7fcfd15bf500056399e458ab6bf2e342ef52d1553dd00bea16bc0b0c308d0836f494d8b7c7e8bcb3fddd37e8b52200689c3da91803a5ac9399c98f0af02e01cdff8626d75bec537f8466d82a24d460c3418fbf8e753643c4501dd40441c87d0e9866fe3104d77cbdb7219efb48e8b753abad2342681b754698f28b0f01af9c43fac46d76d5b0af7838f04899f7743bb60c23f9bbb133cba3b5b2af056fc57971a9819d52ddffcd724784839332558912d51fc5cc9d3669fe7c884d0bb3db30f8e9b4fabeffef903a9f176adcbf6c1f95733718926c929ff772ecf505470b30b1486ebff9d124d4bf9c8ba021bbee6872715f1d401335e6044641f201eb5011fac26b27dc79134d7adec5eecf16366a8ef8e9849d8cf977e35bfbe90a12d82b64d984d873f8727a9e046c0994f2142461e80832bc388c3f22f379770b80beccb066bd7f3500d9a46fc8764b6ea14dcc9a3e6ae7530795448e30a4ae0076ffcf2f76479b2edce443d25392e59d2832d0329d0fcb9254f957468189d307d28ef66679456e5c786771ea4a2706b3d4e0bf9d9eb962434d939f474d70060ce5af907983f8732d9df7b9cfacaa97b71143b3a1ea129561f90d8069eb7e340ca96bd83b8d334805f329bfd18ef66596df8cae4de38ef1f96db77e42d06fe80f86b638e3c511ca8b6920a268372b8431d563224a8b4cb33f3bff801d3ea0280297c8c151e5e95ed08a629f1cbf9472b88ef9c3b67aa57e2e1a271b62ac212a0209a349f08dd2e56b46572a2af8845ad188516dfd30564f6956ea3b212859d4093d1bd77937ff8db66127be123e24e383bcbcbcadf0ae3705bcf316af549e730bdc780226bafad2080eb2f25fdea149fc4efd13c65790305467da5496bff1e20c8a8a440fc6ef5e1364e53d639732773bf42e24c10aab4190ffedaa9d37c3260678098806a58f242cb4269a0a1480de663af682a34b2363f0a7c363724b109b0ed156b71c54320cca5c6b7f2b60857849a5dc27c18c89e07f2168c3436f5fe80baeb4d6bcfa7c3611bed452e08655fd7c10ea2bd5c595995f1837e939656c000c70b3ebb1468a5c515f3480f817985230a8ede1ee5b5e18ac9b3fa3d16694a20eb4a24b8fde3369c28671bcadf2aeff5013b671217d65e6295a0c0c024daf890b5e989fc2b9051c4fe786ee00e7e51b278bc1dc07dd997c2cebd235d9389cea0ed8f51b0291db5bbeb60b0b261dd847c572609910d36633c6b01005722f0d24067031adc84764c5e6dc947c3d15b66be43554d350433b71c188d889a8c2f6fb032cbdafbdae8dd4f2142f5ae490d8991cadd0bc9ca4255adc70720b8334527e0c416a32f62f197834c67c6cc7b318d6ced346a4e644f46fdce8a98524bf36f20d0bf930a5d4dac18cc43d8536fe159a50da8483081b6536815f76e21a21765c0e988c3425eb8f4ebab4da0665b2b9cebabc3db1802042cf61c8de4be71a2fa8048e11e3692eacca76ec553ec2b89e14259b4085aab1be64d64ea62307d9989d07ca029920fe6f02ccbd4a2b71b3d028d00430ed91cb33a789464eefaee4c4d50483339f5668aad84309aebf7ab32afa1198db9f398a19ea1fad639e93dbad870432d6dd008dce634eceb0d3beffbca404626e6eaca51229472769268d44e4c4079437115856643280519c866e11351c50a367f50d921a7b191c08b8a239350c07877ba4133d0416a52a5fd282b3f68bf562b53c662eaaec0d749e627078ce1400a92d5418f19f8877afe1c1d192f61adaecca3efdd17c821a9c269eae74ca6f05dd216b5753f11db2c63cfe3c83fbbb5f71ff1e412b347affe6c9cda68c50c80b0140d27da72d4758358843f4b35fb9925e4080ced9347c43dc13e07938b3b804fcc6b3bc5d3d5733df81095804afcc504f4d1e6a7c91acc8d5a21487db5d8a0d766db42f0bc7de3e8d3b04d5ab67b75630daf7dc352df1305c6d72e1380210004f16f22566de66c7ad6ada8be2b4b46cebf4dd0bed9b38e2e6859ceaa134c80cf2891dd31ee72fff7e12050144701f245b1bbfb16a9f1fd2d29db3dde87ef40804ba4ed6181746a536a4e47a2f608e2595dfd86a60dc37313877212c2d304f0434413ab6836ddeddec8a31b0690cfff1b0356b72a1df75755920d3ef4d447a0aabd8970880f92120479996a9d5a368c51d2567c55ca1420c199604ad9cf8650b94ebe9aef605c7d738d357698e9a135de5b3229dcb3847aa9ea021615432da06ea242e4b947a8d0c64c33b27f12e65f8e9dee206e21916360bb0f12453fc2f0e5069610977b34cd0a70d5c25fb62d9f22459af22839fdce97d74ce40108de30027a8010f7c1660b9061697cf8034c9488a175af4c4585cf86ecacaf2d917aa034a6e5c006997fcbacf530837c3fc7daa58e6af2a6d5069b706766d254079b30b1a85291b2c6bbde8f41a9201a891a5fde09a46e06fbfc1c4c4457134e2f2b1084313fbe60fff8a81347c9e698e538f6c95f5cc6916d9d47cf5a170783cf6010d22bd4411c16941759364b89890fd58cc21010bce61383b03beeb399006026007fe9e2fc6773cb0a26469f06888724a8532e6ac2ff78b812d0d210ed0be36570535433279f2ea61450aeaee3e873443c798a21a95d1eb503fe354fac7aab3300a1f0c2398dd7f6f32a660fb18d4d3bc55c401421ce0381e2e27b037d0b5a70807d396c8b1ae9cff6647deac464d269583a77d2daf7aff05e35d8528de7aa9f804ac0e55dc3f629371cdceb7fad4f0a27e3f5cf2babb3c7be0ab2801af78944c067ee05c6e9d437d67c8e1e3826c2288dbdf9c6888b18f29642f6ba9ccdb9b210e0066b5374874c48b59bf16bbb912fb03b59f452876eaf07c94eb31af369fbe04d0cba3e0de8037f0d006a4886206b6676b5e988efeac4dde3e9beb8887710f02f8cae13acf425b7cb62465ff098073ea3b0daee8570dce9ef666a89d0514c40d2f180ec8b57ef56d30ef28b0efc922abd41b96bd8d0369b9c72a00d2b63f7f016a3291f94dedcfa68abccacbadb3f85cbdc7d6921fa65d5fbdc4df026c42eb0c6397fecd6de242c4dd58dc20d0520ded9fea7766cf66c24d97f435fb6145e90595325997a01b34fcff5779d9090e13b647fac559e9a629419cf019df8cfc4b02020302aa3bb92e363e3c4c82ecb0cd478e235377112ca4d78ce0fa677779710f6cdbac0377377fc649945616518834d9677eb8cb1ab5590c3c4157e947f6090b5e14afa6a10d328e5fe205b619825260522c2041ac24658bcfc5a528be23150bbc23c7f269e97fdcbbb871a4d98dc52badac8ee756ca7ce18940b86a395e1a0881f771758c7a985b3fffbc4c4cc26d18b770d032aeeacf62048ae5b5698c6507987c747fd2cec2db4d49d937dbbab1c76a873a7942124200abf972b260098d0ae3815bacd44dd5f75cf9d8388407c20285c0d1f1804aa59150b2f88bbdfb8e0b3ef9f06997c0cdf879d32c1e995ee5c84756cbd1823a3f18d11f249c7c4ea206dec7300260b39d884f71fb68f20bd5b2ef5ac77e95ca0418a5a3d3b425a0d10ae329f41e1076e8e1e8e49852137d5be3dd189365a12bfc41729d78c7b6c7b60a401ee11ce85c9316b8df686353c45f36bf79df58612f6f2bd23883e369b9cf0474a8eb261b1cf34c2c0ff3df637f406b6d73285793ff92c0b1c429c5fd3eb40450de437fcbc5daf84194bb813b3e7917a294f898619b16ea7007d5423f5ee8045e844f6305e89768e1010c5becd9bb53a8174a2856c12c22bc141afcb2e2320c3525cb593294094420df4e7bee3d1d9edb73e2431c21c0c544b243cf64bb8400cbb2e7e862a6cea606ed04145a56871d23d1643c1ec9f518fd3dbbc9332167000542b73cf57cc8879d683485314783b72f76ca6aa100e8c1c95f8f0fa46e1e050826677c48328532e1789f09420bdb98bd69047e3af08c96e2dd72d241bd3a0ecd8b574eb3d04a288c2d5be0f6df787629919d3845d57155309deeb214baeb08b79971e9c4ffdac689c657d7894ca2672afff4260d22e4b924bb92fdc5194a01f7fedaa86ae5dba8d516526de6c37a6d86f6593de06dac371f2b43410a8f7803d2aca7ddf9c5e74d5c138a2f770f3941b6e61aee48eeee9996421138d082f505cbd44d286b0c160ec3efbc7b9a03952f27da6aaa867d27d64464ae6cba91cb07079cd9337c2cefa83767dceb573770b012e8b1d2c3134974aa386c52f3d195097496441896177a74d11ef3ea3450c3cc836850084a883b194096eef438405608bf7cc8e4ecc76829dc12bccd13706c78d041efd3fa325f3a3c9dab565e2db4096ea0bd2dc2a1cbee3b58ba8f592034e4fe16b0aca2b5489f09a3b1341cb0a40ec57534829178f8af82fad3f127144c72f694c36c9b758b452ebdd493ef53a50417164a5d92285592376c46eb9bffe2cef65a26ba8f7a54239fe70d61cf238d081572a44a7c74a1461f41b7c3980c660354678b8850c69f4a2f205d20c7160401200176ec262c053347dd8aadb51799f9fa944baeed7bccf0a316f9b645bb210d37e5a079da7dae1103d33731472b4ee4df4d9bdc582ce157e3f286ebeecfe904da64ceaeb2c529470abb4e70c37570a81761719bfd7e50825c69b4174690f40717623d829c8672dc335882e672e726f90956143f2e4a1ca38f5ca4f0ace9cb05fa322b3c725c94a18a1df66f6515f247e75937e0916c6ae4c5dcb5e6df47060e280bc21e869df5d21591cc0409e8d56c4f590e2a4172a3ea45845393b1714e0410eb0f7140786e8b849b04fa2822780904a9116ff1b86306f98645d14b8aae079bcb0d4da3d665da0c9823f4391a4e0eb35496ceb2b845def50f2c49bdb54e071f8912995421af23185bf2e42709f7bf80803be7173f164c2f789a28fa2e0500b70b92de351d7f9ff10ed5b18f292ff294e9ba8d818722da6e71635476b086027a9a209285c332df1da80c6bf841dc98c98ba923a43a5c749e0e17a9f75dbc084c9e7d4b62cc109e03f0874ef39f217076de39ea8cd09b0a79e9e02d8d5b7a0e3fda416f6ed3be6768405cfc392f51e0a4d1974b0fbc970a59113037bb80e10d4c747f76fd39fc9316f3630b4a6e5d0559983a1845c8e3d1b5d1101259a7950993b9a399d411135733c8539e0c839b4fb8ca8dbe9e218e6dd938dee8a583510d6bded80b90991ee610566ebd13a3d8bde542c1972e5348af19fee7b29f84a60ffae6f1f18c2a6a2bb659c6b445296e8af47349b3db10ced45c7c70794e92fd036b3c95b9b66f130cb395db0f7598796843cb9cae717cd4d9a525b683657f560aa312add35106d5fcdc60a2293d3cf609d7d611a3b218c95a70fb0205c4fd1b061abe7ce03bade12c86274e56f1de4836586ac41599286fe3ce232121042de80efb400aad6516dce397c6d33e1e0cee529e002b75e076a95a957913505e54ac0b07241b6c298d635890058455a16e74de60776b6c1e4a47a67e8209a5d0c85900eaf292a7a5313f5c7ca3d6a7c4ad43887e04a08510333439327c34ad007dc10e0be94484233c1ef04971a4bc54be2f04e9a34c13c460853707f69bda81a8b30e472a4f8228dc121df51dd30bf45625a1b1c1351bc63b043a295134cee7b33d071b01837c8f90f508095b0748748b0670ab8e8c25b60a956569934a1fdb3b560954e81e574a1445dc94f9af613dec909dd0380e5bf6b2ce09c8fa300390792e08769ebc46a6f4e83c65070cd11fefa32de81426aec3340040fc1113ee98672709f4049f723b62119651cd35a6c4bc608a896b538ced1cc57f1fa123a2257c0c025c8d2fbffef9b555107cb5624d517917be394f964a946f7419cdc2ab57433c054e4ead088a2191ac206d1c0255b1dbf87c33361d7a077b1075a6b1a9b8e9bb0dc790f871c08d7ec0d78c9a16226ee211316b3759b4c21473011756217299d400f4f4f1734dd1f67044cbd5c96a22fb033f29996f8237b6fa9e1e6ba475b47507af321a47cc0400e5a2fc44f4037f2ace7c6b8de3478e44812811cb87bff40a09aa88bd5ccd6892896fb396b1b8e328bd934f0bc3b96b563c876ea0f10ee7a5021498c19dc17229cadd947f64a1d35eeccae5aa093c51120506232bc55c5dc8035d12028ff0fdd71857f19e62e1e525d4514388f9b7ca6f14f74a4780ca626a023079c475604e35f626933b05226abc07419b3edb68c0302afe3013173730340fd1bb8b8e566b895b4efbf9b63fb94d1b6cbe1d1d5bd30c54f508e42d2c0d59024dd9d46946c0f433c09c394bd318040da120dc4b846d5a566ba617f56bbf8a072c54f8a1275c091df36f9616eeefb3a0fe51bfb0556d74dfda9a1976f3336903bdba3e20793502d06b2414640a31190eaa4c2e175ae4e48f54fb360fd234700cbdde350a69d6b078fc6fab292ba7e3c9694c5707b79ee25a53110e70ff39f4016c2dffbaf236143232c4c891be98791c5b868b953eb1fedf877dbae047558802b9d20aa1cc665915974dbf686f31d1af079bad1f7e4b1290e5d4fca83b6cc20b8a9aa1b41f3e372711e98104048f4a02129d7367045fee8f19e209fe5ae9a70105f4abed16c8eda2352cd28bdcbb5b82e75dc7f7bd0ed981c1b39a5c5cd608039f1fb26a20b1b6cd1585be460cb5d1aaf5fc59c93e7a934d596c63abab23e3060e90a7b97ee69b23a9c4801eefe377269357db8ede4741974e26eaaf3b8da40637a5d2d0439793e95e65fad1c514d9cc96e3363b6595295551b19fe2cffc8501f1f0e69770910b0907b387c485d81c61218a3a95ad74205c8cc7c13f81fa500133362cf88b605d2f5185f2f952767a3d3de77b840e00b80cde72727149846506461c8a38d7c06abd75f712a6ddfd1bfa7503df3c5d4f7a08a5964ba1e1dac905a6856839a8d61dfc729af74dae73bf7b322ebee871e53c15fd9b14ec28ac8a05a5221c9734eddf5dc30f3b997ee8cc6ad3805280a6b9622821df1bc9397ebe0e03662397bf072973095261ef362faa593c01c27f7e7f6dc6fe90852a9dec0903fcbf2070f6baf1c7288364c9be18b438edda25a3e7e06e1ae6b5c3060a89410356e555845494651c4e36208a27b0ca5f5f70ecfe90baa6032eb9ae537acef301103abed2b98aa4d8367e3b492edb9520bc79a745765aa789aae51cbc3e48c00285191ca6d0ffc7510ad0b7299c9344bd37997a608d8ded99e2bf428c1e54050fa9c113bf57049510022b4094c95acfea1267b0783a4a23b2dd3247824bf930078a797fc13cb6cbd2ff4ca88f0f38afc17fc0dcf05f8eeede24b138404843d30c5e1da58ea6c4cefd29818d2ba068d229082c6f08176f1620587d279ff2cef7098d07c711e6539e0139ef445214c837838dec2f1162c186f645b2728484b11f0735fda4cd935cce928323d06777964ee76c2d62ffc37eb73726b8bce5b5f1b102534581a5b443351b0f1968f9b690188481e4767cc98d5e5da80ec4fa5082ca0d2b8303ab330819df7241d3f691e7c75ffe62c8922545914433fab35ac243be09572d7d3be726a887626330970c44a7b8d8bb6a45b2e13d747898ae21b4b04d0c07ce44b332c29ebf00e832d5b371f287c03e8b1e60a81c68a07da76a9d60d403f465de13e74a0f90383a53995c2f12971fb9f4bfc58d6b8f66510a525bfb2108c5cc8abf4d2c9a962c43237a9c65df9823b7334b69bcfe29cb5a41b344621c02ced3f849cfc08ca5c09e6033eedf2919439e1ed524765adc0af0fb57e998220203f71be8bf1f734bb2ce0b5645df3b546b228194bd98f72cec85dc88b7cc8e0890607a111245c6fb1b373600697950bdd2a46ecc7cc116b1ca22e42a7ba3f30c92aca81c417cbe13b445b90ff7603fc3e09508bb46ec4cdb8ad515a472ead60fbca3bc9d302ecb27be9ed413f2b2b34a8b715702de2c2c27296c4d611d96ef072abff8d8bfb29b0abb20c1f7fe94d457d1b2f8777580091619b5e3bfb632f00798a8bf286b899447db6f62909b5b4ab2d097f5066bf763e0ed6c40c552cbc60ac0da8b2045d5c734aad026ab83aa1fb76fe0790e314c27a323b5fdaf239e830c7aa4be30947ee5500dd5fa6d1b582f533be4fc571d06794ff8b55a7b6e007a0e4e8cc95738131007b304bbeb7089bbc6bd2ecb6fbe1bf5e347e7d03ef245e604c72b9b37d235078b25777fbc7290b6aed51fa44bf4786587cfb9435ffcdea009fc4749e58ff9891aa4bf6d474ac1351c927abbd27df47d8e203f29d24ee74a06e97b6573e3da3a491a594b7db1a0b623d9445c05e54b0cf98c330911d243390653a8f7f66efbf399b06d6887ed2ec37c94657786ade0f58448293cc4c646990864cd325b939de47517bd8e51c8dde64312ee7c67447151de5b2f419932b1d00efea788c899bfe2fce771edd7d062e04906870e999966a6a6e2bc1855fe32800ccef03e5dfb00ed18412fc98db841d5cb8ad853f073e7a6478dbea01701ad290b99ff08b407b8e8e10941815c026bf8eecf929d12e9bedb641703c46219d8d307f6f8163afabc1b7da45a44086813ddbcf2af633016349b3612ed33aef30fef0dc0be93dfc2e2651f35ed62c173c3a3aaaaabee0d2d77cf979eb63de9994d4f0f77ac53d1e1ba0a0df8b8e36d442f46ce63fbc3c3fe409e353e43e768646da2088a6e607dfdf88910342c84af91691506a1fc047265c20275d6de52bfbbccd00386d5526ac8f94ca13cee4f5ca32a7d91c373f9a6b81a830b6606c38a4cb3850f422a5d0a523a005ab64ca58ee01a1149e65766db443b5b83d1ff803854671207289f281fe63fe0e4e56ef6af42e2a806e6967ab084664f27cea52ac1d90dc30b5941f25bdcce605547789817430e71a053698b59adb573373e6339cd34d0fe0a42b1cc36ddf5b6072aebe931c8ad3e82e1a7b8b5aaabda8bbd7c4ae91563e7016d409b7e1badb9d91780b255f5da505c48adaaa909d8dd329420df0352449205d4a1ff5d1bbb1b72c2298a23c04c287849cf79dfca7c4549b5642379f64e9705fc6872f3a10342e55f3a753a8d694db90910fb5b68e03074071d64cb8df99d03358ca0d692127a38085a74351d93a4f82bcb2d2d8eb381ad9256306941931201c45510158bdfa710b1033f1265ac9d414e7f113199e9b68da0bf0df691a2610ec274a69afb537a9e3b220fe0142c79779395a32b810a6d578882e3b4ba28b50b9de31dcd286726c9f5c3610f38d6dc5265e2cace74712d57f4d9fb591e79990a8a112b74dbfa731f22a01fe14112bc9acc00c88b909cbd6229aaabdc5efec403916295be7866861ad2b729291ee430af4a891ff6e9f1597b5b2041cf60b398054713baf2aafe51e847353002f270ff3cda7760e4c429b5465d046c6e1e26e90a40512733fe15bb8cdbea4a59eb602ce7bf0d3813e59bf3720fb8e04085c73f0f83c9b1e1fcb8aaf35eaa28b5cb4d3ff316cb6ec4677fc045e74be0ff38b6b40e86b30d907ea5c7256a33a76d85ae203b1c1e8c087bbc76e1225c0f1e723acf0fe8d88c3dd45c967eb2b98e30f4c59595c497ba8ac6cad220220ca08a14d6c50eb742bab4e2a4bd518121301bae6a85baabd98c628e8ec9e73eadc1dd51ced909e1d195dc4f8dcaea5f094ee51ba67544b98e0a073096a16413118fb01b82af0f473284d56f14cdcfa82460cf02059f72d71dc32ff9c917254964fc01f680e50d18f4c75319305fa41af223c745b4a7b508b73a6e6c002f895029a285f12c050826ebbb95d0caa4b088dbe35aa8e13c0314c027234313f7d90f2e0dbddfcc2b0791873aad6de0188a5cef28961ed3782221e78b838f1cac2319f13ccb746cf00655c2cc4e5be20772082b97dc4a8b97527396ff766d2b1e1cd9a5fa2ec7b67c00f46f160880103ac268451ab54c8138a6819f7370412568f3f8954ae51ff5c5066ef3f7dcab8c1d66f75251d9b1c9b5aa44fb0f444d09710f14492b167f27870b74746b4eabe2cdd0bdbc27179d8b6e3859eaee97743fda58ac4cfbf11e954d0eb532aeb3647d7b65c96d4a8af89b8b30bcbdf66d0097708f31a38af1dc7c0b02d27d6bf3cc46eeddd74aaa66f95e3cac635a2d276d74735119e75c2049239c0e631f4f4d937a8904533a0ade74027d3503f2e2463939c227dfb28a93ab62fe054dd48451513484ed225b4884c51509e8624f5a7b8a462304551e6de7a115d50035edd3d81207ae470376434475b0c49e50fdf7e1a08e5ab6c20b421f37e7ca0344c710e1463f41264a03e75b407460e12ac842fe65b8d8ca0698df40fe04e40192a41fe0b8bb1834d9e66d75151292aeada01232f4c5de138ab6c5349c02af08efe6c34597ddce010e62d88427625669bb1c87436f08cf920b002e70de0c380bb8f725b47579542049a2238815fa77be9014129cff57d607dbe3742d8cfe9d0f4b99c7b33eb0c580b1b2cee77fe0869642f2b40a50f2cd4114390920ef7197035a4db92e5265943036912b4e3b2cc79e2accc2be007a1b0897ca33f771d4c201 false -check_ring_signature aefd97da646b183a051a4a479ae020d0cd79ad88d2fce7aea4b18742fc5ce5fd 6f01f25b7e6d3b6c8bd3e92eef1aa4c11fb8dbbd70b63bd4e33e08e5922740b5 163 14c9add369f7c9330cd40611f40d7f5ba4532e724f663bbcba342ad627b21854 cf49bdfd30e5e3646b32f760666ae93a35e9853917fa844cd2cf2bb867f1d362 a0c0c94fadafc272c4c06bf405e9247a528320189dee2b360b29e1ecba6541d2 0ed647bc7dbd2e91b6ac7ad513f3a61a652678990913cff9f50209f12b11635a 9689b8a34172de25eebbf531db16b9f700789abf923aa4ddcfd6d0ef1c654805 d8d58a097bef8e06264635237814f4287480316e2c385ebd8138208e461d6f64 e635277846412f6cba54fcf47fdda41eed8837cb215b8cc9b56949353d3161f2 179e30acba43e319a0437c17f392534c21c452f15603082d0c43e85b76c9ae0b 9558cd4488f4b035bb68dde2c92f3fe3f7bdeec59414803d994e969dded25641 8626a318a53a1e83283f132afc8bd815642b1ba5d36e4a32e29900681f90c9ef 3fb7e226b5c0321e57457f1031b230940ecfaf087fc50250b2a326b6b0c0843a 8f18db7b5d86e65fe12af0bc529b9d5ad849c5934a869fa9a04a6ee3f9961bb3 84b2ccfc9c00fc66aa4356205478df69dbc9f6a77bd3b34fc10f985cfab20693 d2315ac80dc2b0f801019b3cad9fad82f2ba01f1b662c742096fe949440d5da7 733c8190dd45f6a450a7921101b608c490f45401ada8bdee9dc85acfd2f4716a 0e04168aa43caf21852f02e321e9562ace64042e1ef7a3df221eb4721394d2c6 09f94e8c38fa012ea5d54d651f10da0c3cd9effcfde37e2e8a8f9b92f0ce8128 a8890380895620396b5b6b2a7092c5891d143dd606f2a6960fdba37696b5ba2d 5c20f81995f435fa6edab827d75ce7d4c7f78775194fb83e51d6d2d35fd4c8ab 4fdc964710ced1663b348e85b9287163afcac5947a409cc1482253f28a34724a 2aae334bc894a4c0777460e9c8236ee32dac450e6cbd743e68d8c283d3fcdf53 c5953532ac352a38a36759d0a4e829bcc8a99ebf07a8f07e68e18d888aeae323 c4f49c654816d6007d07e50bd503b3891e43349f851ab15c3c1ddb644b42ec37 239a6a06228253e2debffc32d99574f35efe9e481057075a5db2c0827e57ea66 0d925eb7db80bcf4836cf8de3ac4d23ebb3d80e7fbdbfdc16bb18c5b5b77a43d e55649fd34af842cea16a3b082158936256a79cb30c93cf42fd694d83f12c986 d440be6e29bec2f6b546cb63cb1e69bd5b50f41fcddec5ba4aa9acad445a7dac 33ff4315cc65b6b9a65ae7097aa662f67f5f559ffdf5c1eee238f36231b4d191 976a1339cc331618f1aecfb7a89b0ae6f5870f4f72b7c69c717c0d3b0a07bd05 7a2c57bbf78ec0c50ffed1fc0a910f82c56009e2e3c7a64e6bf65150c2d11a76 0d4395b019aea3084f04969e3ca40267f332e0a7d1a4ce5844a9e12abcfbcdd2 403f1fbc0a709cbb65d5dfa7570bd27e0c5c6fc8303052dc53aee6c0c5316e71 8b02d274bd7b5a7e36dd9441f327f2bc1564391787671d6e7c62f9bbdffd087c 5e6b78d142387dc7cf27a658f9c3ade07dd22b12dc391017227834e49e129efb af9b2545fac3b2524d53f6834f5595b05448a3e63b38be83b032102c7e9ca3a2 e60e9ab972fbd0fadf26617988c816ba666e082db1d613b88994b15d2ced5f99 c77df10542851763f9c4425d9eb67304dc420e7f860d384d0b7cdefdeced86a3 c2652e787ed400b7425f0fb4557a22d41079a5c66e95caa5fd864ee86750b735 4276068d3d8ae87952a674aa3cf9783e0cc2853b5692bb7aa85d3a6264444e24 a3b95d16e42ea7abb09c2258d6b1d59e65a9ed7eee188bbfd36f6320ccf3c9c1 44e0bc663e5795e9a7176157d070138b9bb80a6cadb3dd6e0db071c1a439f4b2 0f2601da3f88e7ed57c611a4dcd9681b13eaa7d3ebf1dc5b007d757ed7535a27 ac42c6dbc49b5282e3b1aa9c0abf3620d3422d5efa020cdd68bfa6d81018d9f6 b28538e3824b43efc4588b80d2a08cc7c56fb31b846a80199f2c85cbcd96b3ea 4455458279aab1d9730e3fa466eb18644af4a287cee684972695142e751a4dfe 82cbbb35a98288a390aff161c9289444e597b516371160d1480ec1e25291c9a5 9648740aaefe15c5108a0be314821e495b3ab0785d5976346431584f8943956a 4c7e351d834472644a6183fdf68879db3baade82dd03c8dcb19cb40e8e694605 bd00775efaefb699182e381b46a3bb26264c142da6020247a0e85a01c3177d0d b2bb3cb76433c1e0fc90cfe04f429bf7a7f4e009033a60e272dc384d7027f1aa 8549c5d43102b547da24b7ddb838c644723d77bcec9b4a6d3b3ffe7d3f515678 16acc80367fffbd752f8bb8e761d8a095df1553d5da88164e840ee8ba6c1005a a4a55f33f380bb3ecbd3ed42fd792bca6074e72306b1be21659c60ce6077f102 320ce9b680783d22c092a25fe04be4ceb5e75c7eca49ebb9db8e2d5aa99c6274 0b93ff899fbe95b43015790f8b33fad694c4516b785d48da015bd00c0dc45c76 3d66328059c4cb4c3ef11401a5d568626e55c60bf1ba266f45e1f88fd5ad0ed3 fa53cc4e468c2868351d1cc57793ba56bc17227de2bd8b0460a127dab22d8682 0251918ee19c40613dd2abddf6e12cb026956cd5bab49a2afd41e57fafa9e067 46191b4cd721d37b5d48d8a2e15a24f09f700f6c1c49489db902f514c68b1c32 4ea8839ce3c777ad05db0750bba64e9fc69fb4a69f9080c8ad8f32f81854b920 865177eb4ae0e388a433f568b8c92642cc2592d69a11972b18631acdeffee021 0538fdf2c4a275d7461953f43b2bd7477d6d92a81c5b435dca2747b6ccc53ff6 a749f3ddb91f682ff74cec8aa852d7e3a03934dfa7aea02bb0b5a2267e1e04f0 1c0f1a67590acc0ff6995c0e7c1e1e2b14ab053de5daa52a4c1d3538f83ae85f 035c6e5f82e9e76ee289683065790f5aad05d5a82cb670a3cabf44dda1fe4549 e161723aac5149f4b9b42c6f92b89d723bb57d830bbe1ab392d52db1e1cac414 5122321cdfc976d19506cb7013d92d8e1870468994949a76c25df19da6b1cd01 8f97b7ba637623a53129f3513aba1165b90d9f42291e0e51df18737b2a249b7c a1e6497fa3d494e423cfbe52a4ce2bda2168706feeca491c0a73ab6238d333c5 411cb181224185eb23d1554f200c3aeb9354ea6f555d95ccc00ed4057041b01f c64a13b0645d72165155c85a05b324648f0fe41d4d1d74089837924c8b41dfdf 7284442e2625b0e56433907b90fb7c3bcae3358fbcd4ca41165b1fcd2d6e1693 20c18bca3645c2242b9d89a49dceb35e6b47b257816444c3e50bf88117089fcd 2ededb23ab8da3e472d001c09d5583fab53069351eb45b9a5ab2e68a60f4a7ca d2e058d949678bb20ae8cf6be890346d149090bd7a7ef292bf6a321c721f8a96 17108a2d040fadba140160b5e4ff0c6e1560706d67b93c1c24b2181b6019e9aa 14db5de366c8441be09b6e118b73f25163e0666b729586f48a2e8db1993f3a3e 3d348e2b27da715a96a0640f85bcd89e791e5bd69c0ea511c5851919acee18c7 8f3d8469c39d3de1b6b7b7e2dc735b0408242e7e84745430d9d281de6c090f64 4ff71f464348ecbbe3721a576e15abb530b2be9b74d8ae1183aadf2d90d100bc 6ca8480169da517448620e16e70cf45f1ffed06f09653246db7f51c491558827 022c9a9191c89d7dd2d862e740ce7d728598e9cc1ccfb8e2acec2020a35e4d80 6fd1ab9ec050236815319a5faad59d13d6bdc085efa4f894252bef706dfaec2a 2687052620351d0df4f5b07ed908526db33e000308dab46e2fab9f2b80b5b66e 1749d64df10b2e4fc3492f6d3963d42b23eebc00581bd7b171b84ba94332dc6a b2f4dcee63b59d7a60360d44daba8f57f60f9703c401bf7ba500767aa7857546 3fe6bb103fc2c72cae21d34f8d4aa18106666c291dd78ab2377e77b435296a7c 99ff4ec94a88d960493ff46aa0a92f61a6501c4dbc6d94b6d6ca2e348f1ce77f cb89bbb338915c66f30e1932b3ed6efcd7dca9267233278587f041bb4ecb4372 d09da425c8b86252ba56baee9384250375965e24070b8e1dd5299ac190b1fc56 115704913180787db249dab55a7186849f4f82fc1503195e8acb7101b57b4ffb d438c0cb6c282166ce6931058eb3441322a50be4a34c480d2df788fbcd099953 ede0a6f6c352b5637ff88cf2a2dea2d41f601557e00b4665e7945be367105053 31b997f7c5a295dbf71afd78107c7c2a2bbda15f06ed59c3bfafe8241561ac93 011f7c7371b8b5c5320e08b88a3391bed0ec973601bacd24fdbbb0394ddb3c9a d0be91cd0b7ec1f457ed39cc20ce2ce61a8bbe0bbcc89f7828f16586c3ebaa92 b0e0f72d9e9a565e25412432c820ac37d1d5c2ed439bf126a46c057dd1ab1fd6 0304ed035d3547417bab9bc4de4a8d32a3fa0551ca493e9a24ff5b9f61b658cd f9b54f3e715711c35510d232113c90ed7ed973731c068aa2545df51779ace710 5bfab4b4471757fb8200591c80d84c62affbbe4121cb333c7368cf5aa75e47cb 6fe6088e3cec0dadf1b784a3088367c1ee12512d2e654ffb80b225ff3e461ad8 c5607f07090c7fcc268479cb17ae290adb2be5556cdad41c46d71a5c6a94d157 32f05923924c6c2575e99d0bfecfa8b2ab6fcfa7307764f0e90aa50bdf4a51f3 d6b7cade04983f9bf779f8acbef9b28332b9138f12dc70287f85b5a9dc8a6ccd 89304767ef2f5a77c73991b90e7b3f4bc82d7c1a3c6afe07319da4987f530f8e 57e914908e934f36d87b2d9f5467203408d634afdd79aaab89c7689ebe363a68 a0b7ddc82b80c7adc198b51a32e4032d124de1f87cc98e5823c63d34a381d0db 8966153fb21f2ff409f5ce64ce13ddbb1d5b751f0eb9e0cc6a89c0c459a183ac 052936fdad2b3f5286e0b2edd86beb73b56e0b885ec702007aeb1fd3cdc91cf8 f21a9860a50f73cbe3cca07d08a90659fdfd24df0fd672a8df1f0aa98016d614 647636db8de0de3fc4203fde46e0056f33f99f5a505aaa8ccd852f174d2f4791 3ac29252cefc2cb43944a430c90620e6121954a23b514a0952a155ae3f99d0e6 6908947507727060415b3956eff4bed57a10183b71c83d88ec281bcf39df3942 25d980eacdaac12a753eb00751d82149293654efd5d32dc69fc814d32d0774f9 94b9aaa4e3d803e84bc3180c9753b93c3a8e10befa268b5de19a3e05cf2cf48c 6e5af04799bd80a3065905d66e85ad9b05cd7d00bfd60700c93609803185e882 875c62ff7489a19bdfe97a408ee9ef930cf43e998b14e084aa3d0228aa12c91a 9ea43baef31a8ac2f7c93b26dd338fbb23e56ccf1f259cf48ec1166fa2bcba07 5c5c0f8444d3880a8f64222ce2e28ea2c5ad3441fa4a7acece38dc9cb94ea72b 3ba9102a0419583ea63821074712ba649b6ae220a2ce5f5ae665e01e6801e684 6d3da3c336ab7cc150381922d83ddf090a37d0387a2411943247ee08f0c55e79 ba4a47f7753ebf1f48a530b13426145389e35c350d4509ad2aa1852364a7c89f 7e70582d95278ff1a223a98a925493d4eeca84812d8321f8d575a4f440c75ca5 c3ab19864907a0a5382357a8d4df48147b1b6a38b48648c6457a26231e313b07 21c87ca57d493a4d0c0f715b114d66302db3afb01bdddcbf9f1d27e870cb6922 be5956523ad3709fce6f856d2b4d7ad546eec1d3c06968c49ae539c9b8fedf65 ff21e41bc6faf2e2a7596536c1bdd740dbc1b312d8cd7792842a617dd6ed2d07 177d95cda80e0e52d657aa1467f257c550d4ca94229936840d485dc407ea3056 a1c4e039b2483fbf59674b6daf52c051d6dedcd8359734fb4cfd393080a33dea 97d5497795b46e19ee5024486eea64ad98d6b04c0a0e6ddb4e0214715ed7b749 d4e06dba5959036dd8f1b1484a3356b93888da3a5a939d793387d2c5d7940099 17cd626ce5d0a00c923fe2c2492fdb88513d2373bac14e21fcd93e4282c4ccae dee89ce91bc827787aaa6e7ceae1e1b888e7f53ec3053d7079beda2e3a1b1fc8 2c05f436566d5c51f8e6e73309996e7b0c78a829e2021463289bafaf9742d6c8 b55675212309a06de77cdd51df8829a126dea34d2c27bcff75eabe1cc14d467c f61dc8c206b5dad027b76dec41ea3ddb9eccfa393f327cdb82c9d51b12126f32 a0daae39f4b1cb814589f44537d954d1b31928cabf503485c47efc2521a64c46 b1eabc46f19e310678fd74121e33f19793346026ed81aee7c34aa609437e9368 e1afc04d062b7b45c0d852cbe539d88b6cf7730618b09a965d4d814ffcd7d71b 446b1ef51e59b392d009b6b81029fbc0c4edaa43a8706fd57c03b549e4858b8c 6aa63b7c58a3ea968691b5f90e85956063dbc39402bc1f64781746e7aa83ee86 7820c17ce855aaf1d81b5bb613a12859d850a8e190b3b68510c8cdd865930df0 bddcecef1a234699feb11cb2d7ec821f27815425ae8c6c91a764072f8c2a0ac1 de50c8c22532ae87f5efddaf579178072299e11fbbec26efd5fd324c2d14f34b aee971019a20520672111e093a9d689196f8b3d2a9fdfc46ab49574f7dad3c9f ffe5516bebf791609ec534313758c7984918343d92a57b175a8eba1c2c6ae853 6ba497c6c01e9e87e05c44b9c61d816ca4b457a7620fc6801e9dc9ffe656bad7 5c708e09c3ac95e10d1938d450b3989a9646d6954dc7d28a9516c26f1a56382a 4ae80dc0c5909b59800d2e541d29aee2ba22372a4105b2e0e0dc9d44cd77b8d6 6c8dcf6e40073c51e7fb6a68630a89d239db6c8513fbeba4c45817961dbe93a6 9eec171e19192ee3b0c6541921706f7ef6f4430da84789068a1270bb04d54f88 dfa841e9ff3eae7bbbdb0eb1b95739b514299609042e3c6a5404da054ce1fa5b f1198c078614b11025348cb70fb359670bb6081c7f965269c3fcdc4135bea53b bc391e6a8292e0559f1b847ec067bb0acf1c502879c81770931ad4716b2920a6 124b93225bb6486ae3c071f1fed3dafb8e2a9d8023003fccbe224654001d4957 8c470e78842d0d41b7632ab06fd5fe1475fe91909e17916e6bb5a27168ee2bf8 2eece7c52c3a677e0db1751b837ae8272730f046bc9e6ffa8f183ebacd362eaa 6fe4e22c59dc40c11412b3881d58707a5ea9f8debfa916b201a9254e1ba637df 3936e8badd3c07ac3078eae2570e9094312392de5381a08b09ce4033491f2841 ccdf951f871f4396f9eac4b27ab3883e22393f4b3302e8da13e277bfeb271fd2 a9cd71cc013d651991fa33fdef34857895a6d32229936dc7b10cfb112bb8e24d 918776c6057122f827fbf5525ad856e36ea11be8a4854289b52bd478eaef5b6a a891071c5dae8c00345aa95b174973e6252530380ddb96ec16f94c717a56f3a5 ea32ac65618540dae08cce9e7209d80e94147582b2f6c318b78fa881cfe7ba0cf92d30a80435924586b5ad161b2fa82bb5566c0f742232260012070ce52fa10a39d3a9f18dfc41205ad702214a069e61d77a4be4994fe22105ae431e89624105b974f10d2aaf7076979dc3101ad50414ed08a313341c56fdbc05a0c62c29310b95f58531ae017e6474dfba2022e3b136510526d1ff58fff3ad10571c5a41da0a439e71b18634e2e61d4fa761a8091303964ff7d86ea736669580bcc743050d0d326220822f89279c85cc3162579659c1fbd4f9c22b9811e43828f24d1ccee3007220fad4d930896d3948b046182785254553e7314e2abefb83a03423e9c9da0276624c3f4c70dae9c4fac5cc80e2db44cc4e933d9b4d3624e3b4438ee691380fbb81f64132ca0d27f0e35339d2ffce93af88e913d5fb88a467d6dd0ce7e40901ff0c20a3012e62d0b220b3c148a6d4ae63b1efc980d90c0ce7498de06a8da50dce2ffd40ab142b0687f4926a261f2847460049e97245d80d2e0b7981c93fce039de021ddf0e197f6890f2064ecf06454012a426388798254fe88330029b8110ca7ed391ec5035ae56873e6d0a7426a8df50ecb61717500f0915632b4d1b3880f46d4edaa4cf5b75254e152d3b08a090bc5fed78401cb21937b21bd762f2acf0b87a18f10b43e444dd183713cbf23ff1296078b35dc1c06d43468e17731481f09072a33182dc4b19596a039c0eb52237978d2516a4b450bbef32f3c7b5476290048a373d2d9cabf713b0f27630d6b88ee21d15b2dd369ccb54b8d53c781818200d400c511e56197a5011b51b850b05ca1f690af0c14026934c39e183c994011023d43d1b2e0773b2dc99385b1044b0ed4c039f68032552f32302bbd9dd7a8ea0fb7c80ffb63199b5f29725458036c784993b1aae408b599fefe02287c33509707d1949d6da0f97af8730f0b91c487283a9517b7998d43a8045e81cae31db63b043cca99250c5f8c508b0c7d4aeb603699609f84432d901a577501bca010d9110db66c2b2a1e44fa493b4afd05f84773b2e4a0226c7f90039d029e7f400d9597049fb9e1b43ace5d48285fdc79824f4171f6c21675392dae84abada6f2081c4b0f7d221b8a919ef77a8728d4732052bab0c1e40116011160e887bb5918cbbf3800ab3a5edf87b5317f4dbea74d3309ffbc24c96ae1297875a855bad4332e86f4011864af8edf161085826cc725fa81302a6297023e221ca723b211c18e83eba30fa75c6221205a620a28991837d752a3265eee0d320567ce50a067e1b69b78cc063428253fe1aba41cd08bd223a1044588e9e673fd4f5086b925fb225c38a4cb016214da0d898a20401e42e7552af669f89ed77043add65b9b9913483cfa43af0777270be4afa94f0cb9cf4797035f636ab201750facd98de48889707305f4770d69255e7a7a44cab7a7dcd58711dda1892c7d65fa9895d99f03c8b099e4224a05eeeb77c139d27d59f55aa0a4448414169c8bb6df9c721065454989ae0b472b01253e7571d5ba7925b6a5399076cb2cbccfafc019192529e068b4836b6ca68904455f6be61dcc339e56407df00c5ba408abf54eb1f5f4958818d05e474ad02a093b6049d2ae27d76a555147195e6e8c2622e251a9106f9fc22813e2ca1c4d060f5ae9294659dcdbe6f7a6f671a3a0c63c777c14d3b988533cb669d23064060309b2b0785dee3585ab6f59ffab6780a6758b8e0f509ae657d49774f2e67ddf820961737e6ef2680b41b81274be968e9e545d81305098c648190e06dc5443d36a0b68160992db210b8e34f3af73f77352c3a1a1149be44c6eea1b057a1ae31cea0ccdce470468ba3c23cbcaf08b7cc903f570e60f87b7c9d4e72d61bf8c4e4eda09058f2f803bcc95f2dbf8bcefb3b929a409a782e16dcaf247eb3f0caa4888160a3eaba46835fd576ec94404deb943ab58b5ee04f16a448ff3a3e0259e7386650b27ee83fd77b1f0f623c76969717e0f4dac18710ae32350a9ad5eecbf82894c0e54bd2d089ff638d8b6f8796c7249c1bd9291b3b97128c347754e5dae53ddf8022326edc4834021aa9f9461aaf936f3a58d4f338dfb03da598b0e46f2fe8ed60a3f0238700671fb4082e63ea2712079c3796cc151bff22de6e41241b05bd4c3013a9ea2d340995a5da51d449eb945d052ca4ace3b3d495e8ff149b4c7bb09560c0039eb94da9e020fcce04c6873fac59141408fcc874acce6ac242bd1f867740fe6ef9a2214f7cb4aab2ec5b5e98f660d4dda713e3cfe7d48871c3b42a2fb2e023f257909c65294e0f6ad93c33b2ce097a7d677c935d10db7d90813a14aff80089b6564bd6d407d1894cb6592b805a1b2df6409b7bb168024f25e9c11bf46780eab9a0f4fa83dafa8c3ad4ddf795917723181c1d7a883269a9a7fbfb7cb7f8c01b87fe7743f2d85d8ab87c7f62968d9413d993783b0a8df37b4df7e1e8533c80811bcb9637b301a8085c00f79d2f9550e284ba76c77211f4b794b472b7bbb8606298b06dcede2020717b938a2080f96ee294cd62e69d5d9c1dfa9c1ce2e5d17046783f72be59f90437463eddc301f7fd0f8e9238235c53749db4d106fec03dd00f1f4fc32c8bc7b90f6f011780e0df2257724fdc663de42cdabf00327cbf28d051305cc2458b4f1870ef181e5b32b5ed0dcb1bb0bbb791d31c995010f4d37510cd525c1bd6baccfd61f2a71b821479fcf121cdeef914b84a4de5f66ec58fcf70d03b67aeba89f6ece897645cf9cb278a9792cf6bf5aa23cec0b42635554bcc40d2a7139c5c02f44cca6eedcb3ffe5f38eac690abfc5e04eb6007b45977002db090bc494358acc1369e219e25a60c275ae120de51db645f2097df83edcc547100388f1a62fa57b90e443a2a1080546875db877d517397580f92273d3ca117d6b0ef7e28ba6ba730384d3a0141c3eafe30f9928f1d2940a09a42c2ee0576d65bc0b91770f49ac6aff6eeaf9d3a64e4bb9af32a36c1b633026fb0bdeb76cf5871f090296a0bd4b6207786e893290c99806c12719680a59df46a36a03bfcbf4b08c0d816d3f767a1c27d106457bf3847a5d8c3383a40a55fe89d781af5b9c056d0c0a4aa06132d152f45ebf536b79ec01e173e3002b93667d20cd9c3ad71231667c0959402acc42a011b9ac30a5602a4dd3a1869038ce763cef27565fecb3350e760826be072123b8b9899fae68f717bf9e898f6b33e2a683ab9769860a2f72e4a8024c88a2a1c870c11bca2e1e31556a197dc906cddef3b196f54009d7030f3a5d0ba59f95daa3e8d2570c83edd56657370c7bb0e262728f51f418625a06c9bc0801d12a6c2ae2d8d5a983f1442dbc51a073aab0ab688aad9ff63654d78dde9381054cca9c29f8d5cbf94891babd972b4615733e4b9263870fc0b128f1fd9e62760c47b74f1d17d768a181499f601a0a61636de3782785cf9e554407eb2f1ca4db031f82e26264fc18117aae13acd9db7a5979599080979b7ed40f480c218871bf0d14d19a7b4612e363f19a8a9f93e10505fae230bf827339dabf1f3a60283be90a203a432c2aeef8323c51730cf838b55f6b2fd5e2936564506dd8f5ef8653ec0b4d4d8c8990b3acfd41daf133cbf8113d7685cdc1e1f15d2ae7f54b9fb12fa809f45404130535d5420651351e8546ae12039f1b670bd13effda55dc935df86b03447e273f9b707375f8eaeb1b9e9aa922d13bb6b1f08bdaf8412d27a8f5e1d4011a38a8224269446a972cd1be893edb9a7c7c64e72d509442bd8b7a8e91a9120dd8f9fb256b2f4ae6d82b1df7fc70883b8642858bd4c9e7b9a4843ae3500a8704816a6f19e81c9d32d8e0870d827aa60dd546ae7546f7ae898acae3806fb7800a703679cb426c8a79f18934ed4039cf88ab118279d8f82c8c7a1c443eeaa4dd058d43c2cf2c3d4b64d55ec3b3bec6d1bc40c82d20b1bddfe738616e384079ca04cae851d201dc5dbf1c56557dbe6763268eeca7fe168920dee9531f1a91b1130c65220733c145d1dbedccb9d6e21036fad477b155d235c9ebc78a64b86ec0bb0f65782c4fc55ae0283778bf4ed3957a0e120f18a2ec29b58c2d92828aac3e0a069fafb7a48effc2eec76d428297230ecb0f32e52a9a5ebbf285b697cd74b9050d060e17e4fdf9a7ffed201059a8bc3630c81329d4782e5aad188f16896800cc03202b71657a70196357892f157dfd400d598da8e320ea2e4d60bd75cfa44ab90ef81a5d676a0955a03293def703a85844ee8e56f997a41b51acbb404136820403ccf651096b19d5bab049bfa2791f8c0de39462cd7fb3b408595f05de259dad07725db9000176bfa4002719170a2313232049faf7ca4dadd48fd007b7dea834028eb2c3ae1a51124b8dc9ecd32e31fc9a6584ec7d04d59c3b507a9f87354542061fcd1f589de5362860989c1a65bc4f637b10ffb11957be23827a015cab331e071308719198003a7f43f6159e00f48b4b267dab119bd15eed528fd6a92bce320b61ccf8f82a8c42a0caa08c199ebfaecc83c7f711101b1af5707ff0ef8169820e4e861373a57637d1b1d27721638bc71e1178dea781dd454fef7ac3d222b9790cbe14c8f7b0e43cf1ccfe85a3966b529d0c7d855dd2c400c65c2c1983a65f3806e81fb0e2f7ee29460c04079f82b47eb98d53536fd2ed527c6b83408e8dbe2001dc2a4e32012ec63dfd7224ade62fc8949a6315af9d31886a339eb89c6f928d0f5eb05cf921bac40fc8d6cc9cf9da218959ac7c47ec5952a94691e7a407e93f01b00f457b25864bc226cf0546da97d9bed8dedbfdd218c39dbba4aee3927f720c87d140a6f4fdfe82abac22fcc2e33b15363950ba30e2b053b6f5dd6fa3d8230bc8fb7e746f11d028d4cca79e476bab34dcaa781562f288c05dbebad71270f907b1be84f38cf92ecdf35c20fa1abcd78f7f71f0d804d80da68c3a5db9c611b207637a0515b52a2f726264311d9e2d037c9649b52b97ac5552c359c5e1c507220b7b904ad5337998f667dbc205aeaebf7e697f530e2384eec53df0d29428c1040542174fcde643ace2639eb8216265c246f15ada359bf90b5ebf64dfe23177570f41cfcebff1a23eb9fcdf98f118c5e6633c7601912703e0dc41e7c072197982091822083c067f30691e1cca792eaa959a1f4d7712d0c61477ade04a8242f2790f684298dfc8599695d2279712b024325e874dee3745fca0a28649f1a8101c9c0d2f4acedede144395d9304c2c715ab71efb84cd3e5cad9b2bf67ee5e5d4f61109717b33569f8583f90e6a771939d07c5256127b2045df9ccbc5dfdbd9c0f30003505263ba75234f9f8d1f80ca10d0cd4c051cff748c28c8b94cd80bb8b9c99f0e52ba6d9cc4424aa74f9b86b8bf91e3e1f142b1da326b69e349a83701fe5721076c4332c4f159873e62e572321ae7f0c8d5a316353eb69850ea82634384035c06a3bfcf76cb812086c5903804bdb3c1b0bbc5f41b42e3b99d254407c6b028230974595613bd0ed96ac5573e3064e6692a8b72af85be75e4070ea244308cb1ee0a7bdbbe299c48c7c63df33269a3631b0295a8be4c8e7a76ce795deae0edc0b500f7c439ee706f1588a85cee63f9b044b3859cbd6dc2d9aedb51774ccda565ce0b7f8dfccab458e0f4bad684d768ba229e33366c2773e23156a5b0239a0ed2f70e1a5c3c67191d4005de7df6ee876406afa85d3fa6bb836da5e5bcd17da8a3060bf9f8c653a1e7660919181978fb9af6bbc9720fe7edd85ddbcdd2054a88f77c06d2ee07629c98b368212fa43982a47f4ad18118bfdb058646d15730787c871b0af50bab46c3c3f40d68bec658c2dd7baa9c2f5e251140fdd91b3ba8ffea4ed50ab5839c1d23fd15df7bfa55e8b3af1b0d251c0abab6145cfeab26181ef70ab803775abf15dda14e24ee83e593c21aaf738568ab2fd48abf96c0d5c6bb12f48602deb8928f1bed66d5d8ca7d8318cdb5a3a9eec09bb0c14254f2edc8f8d4174f0ff10ee241d418ae5ff63d9144d20665f75b237d43b11414dca41ebbcc978a2b09350b5b7f62f066284baceae2183fad444089b5405116b877160beb8ded15ff00e2dcb3af8c8b0507e96934ca831a6ca9e9c41cb640a34667c8601e0e297b58098ca0ed01075bee0ec85097ac329e3afbd922d3c02a3b160d2a67a7ac1507f60a660da39308801c8bcebe3cff518266a9701e0a229be2a5c5541496e1eee272043ad5ca32516e944fb9d36f25d760fc6ca7957c21bdcd312c04d994cc7d0ad401c37507ef7118036b1e7055b792bf8c9dc39ab4436122b278f32c0119fcf3c80adaee3774a85f5fc48e496f2482b2629d1b99e7b02b5481303451b77d7d22840740b29f357258bb083422190debb46fa9714d7d2e34ea6c4eea0dc29227f63d0efec7b3d262d6fab597cea1eebccff240ddda57eff51028beafa57409672d6a072a957fac25f882e09193d745e9acbec36d101ac3fd0705e8b82c3d0696152305c3b4700468a43dbd57b61249fb9ef62c6c65de4a123f5732fe103eda70886a098e748387d6c0a07211c2258c6013a78dbe239773835d321bb1888a6444ee7200bcd5a60c4a1bc21a4bfce3bbed06f705cdde54f9b07cd41a596a79cb67c6310aed48d60caabf7207ed87d34d341d58689621b83ab75980ac92e439d53807c402a794757c52e551eb1012d457d7895cfeedb0cd28f709d3cdf98cf368a444f3046ca96fc495f524a37e8b011c894b0cd29307fdca574929dc1223dfb755cff306815e2e7e7fb8ba5923a94261cbae923d45cd0aca7bf9e28e4ffb7c6b1b001505dd4209546562b682eb768d06f7f2d64e6d49d4738dbab0c27f07673e34a628015780b13448e5013ebbcedb01ec0d42e5b37cbb9cc73c0f48b75f4d5aa08bf90e737e4ee97a0a3d2bd167dc181bd4e8f5b966fe74acd355b7a0a05b706a30910455574c3655784b301b529ec2ea50c803e346ec012fc1762d270ca9a1de91150c916d0463ba4fc53581700a8a63ea474b5735a2077b82d8b9fbc73e3557029e035c537f31aabcafdb4359e32e84fc1ffdaee143a65504207326e837036e305e05a3f72c9285f4fa35fc87ecfa78eb94e32baf773564c659ee071194778b1825028f94a733ace8abc6499122b07950163f5bddc9772899c97d5eb6f88537e25e0cfeef9f97759a86b5df1c22d04d9875f378bd1fc3e04eb3990b0be460bc095e02e80c0fe82fc013a8ffaac58a611109129789a105268bef0ab06d04d95eca500e07e691484cbe87de4e71c399961dea3005ebc38f485066f3cd6d461d02a13405f5bdda7377fa6e4153c0b12a0949f1c40213b092b696d20a1ac7068505c2e502b7faa63065a69657f7d27329323ece49e986ee38f12a0b8771556eefa9705c0a9e2e043eed88887c318d2457a2667e159bf281ff881b12e08314d0573a46b20d52146f790e4adcca69c2310307768bc8fb7ed4637c0b17407d70ce4a16f3a807812cf7a5c7aa2f9a90598f094766ab5335e0f1f4fa5bce6c67df0daa1c48b00a43c40c7534f58b727f0aa2d7d8b232f062b40e5a976ad16eaf4bea708ddd9204a729da56d96067cfa8fd5a73355d0190ce944c84b664c3f04a6383efe7ea3c0296a55ce7150b6e54172f2ba2cdcc8730f6666a8a506a383f15bbc07c0b744e0be227e2036a3b913de65e88505536b094e26b419bcd931fbe1ab12830fbf4a0080c051f3eb0ba766c9ba9d15f400aa57ab32717bbb3ea660a525d58b69e3cb80a9c137c1ca998414f4ea89745b1ceff41f118b8223c54eb508ef2debf0dfa5f046ad38031c516e85889064b1667dc23a1e68bb07bd9f70563007c63d09db1d70875a2699465bdd747ea89a844adcfe907fb5b2176283ae1b8c70f2f2be72a68018ead6d12a3f901ccfa7f582c938aabbaf99e797baddab9ec8a5a8969694da10725a99b4fdef0e5d2c89ea2f18568bf04271741cae51c2ff1a1ba93b69beaaf05ab7c1dd476cce682d8d0fdf7dc66bc71772339a5fb1508129f0ea423a305c8050ece307705ef0c06f66d8af4acfc90d573066b078fe6a6f96118226a798c03086f392ed5458f8de2eef7f56a42732328cc6517899ef4f02111d643f3c042190b9d790c904435628abaf93eab8305265173ea351d5a74cdcbea7789db8bedfc00518df040031ece9f91b6a95663de43265d148434ddeb8d963880b3ac17d8940a3d2dfd680130a9cfc4a5097d62a8bd75bfc8a694cb624961b40f5498267f6b0d843bca23b794786a98e5d7bfb6b9c371822bf4aafc8e3b268ff6eeda0df730002999d87bb30d3c273f1689b0a873859ba70f2c1a78e8278a5e3ee6ddf237c10b52f2d76e3171ce6d53b810cb79575ef3d30098d47eabbaa9934992ea1facac0e23f5f1807ed1de39bbf5579a4cb0d4a3cb3cd7296992c587d247668b7ec7b60361396d2576b4abcd4767be2a8b4e89674d85fda5506273a4d723a05b192216074d81087203b799d7e3a6eff8ce3fc2350cc9433bf0756d03b1b6ea89213c6600948608f19e2fb919e419c230162308153d64d005fa80a053f7208d83ca01a00133d5e96ede50af0580aebcb704cab51262e4435f64f5c21666e20a6c7d03dc062d14de299010c2781730b01b9d36e28375606eff5777101fb0ff3ef3943f59068b65d3357ae7f963364feec2b5f4c562e2c12eac0a460210e87f0bdf7c0b940dec1146dd6cfee953b53d37d691edc13aa57b18479368dde52d355798e11aed0858cd337f32bc396a49818e5e4851a9983e2f46f325acbbf35f7a8830f166df0597ef20783b1dfcdff1d2e69550fadbc13202baf8b15ea3551e611c6a55f7df0215020e89a5e903781e3ccae0088da25a78ecc826db3e87fde02de02ec3bcf00ce1020e02da1c40b56d5651c3889ab8574ed428eb015d89e588951efbf737b00a282add407ed9b77cb86b1e49f9f6c9a5af11998bb56c6e8d2032083dc3d3eb0d303fa78202b8e3d2a19b2283d5cb41b191cd4a89676ba3f13754dcff7137110dff154231db7443df40010da3b3aae9e35e0df7a46020b825c0fef1da8801cd0a54adc15210ec7d07c31d0b4c1cafd5f48723d07f50dbf687332fb77a38b3350ea7cf93fb0be5d860b32eae37d5b6b580d59e7c339e652dd7e64d97fc429d2a0bbe0a6895b2dfbcfec5eaa3aebd772c83493b042c61421496d3c0eb164f252f0442591880e344b82a88db2c7b978a83f9f72c5ab9f656756f4d52ce5dc859a908ae22cb4f06485fb26f9f187e1925652316dee826a15ac159232dd6fcea5c73063ab08aba928200401d21b1c03e155d751ce434f881aed7f6a2eba9f9aee6bd06d128561b6603df59529f4fde10e02c197977f1b99518021060beb23bf897d60f53de5a9b9b3eb3c8c181ff4157fa275e7f093afb09f80c7f26ce0eab8f78f4076a2481a050ab590b6845ad8c1c44d5799e2b68b4ffc36eb3ae22eb1f91306e0b6f4b713e0f7ee6c24565255acfdc88fce8745ce2863df6bdb00eb1a30eeb6f05f23ad99364722a36d37f6e91caccdb30c71f704fa7e4bf62f9224b02822d920a71525481523849f43950a782770383edf76653aa56facf657caca35e30309a0e582e8536b5cc1160803c27a7ad86c9dae77b9d0b0dd9cf11ef7995d6f1faba057287445ab203e32ce36e4e80f45e07f8f53424cdd7dc05ef4de2a9c156e71307f5de32eff9270ccf10fbce32005cbbdbd8e36ee83f885c37d870db2b95d2b30cf5f0df5749ab3e9ac0267e3dea1bd538e188cef68f921ff0311c59f4fcee1c00a050cad9cbd570c549b271f40163fbd08e67e52a97e270afff595ad25d408a012360b33d61ee1c1ec3a6994b1766038fef07144ca4a83135fae068de54ff5005a61cb10b4f68ea8d27db400df197952877b9d20e571cc05beb1692c46d14dc0f8e0853e248748d1e5a145f82e86b85039c365a24777c66bbe6f1d99959a5be0d0c8f346ae4781d5f67c7fd37e220e6b4b0065da3d9ecd604d13ed494b218110f84fc51b7ed77e52f10f79ca630a54ce103b3efbb674687e2879e94e035f95909380d1ef14ee45d207647e7e587c058d6e5634dd8fd011b8ae649abeec7f47e0a9231984e718311d701837fbb05ae95162aca914e0ebcc002d9c2a886024068081a5d0bbd8461dd862e7b580608811c108011e703bde9b170789fd615b805ad0c7768b097e300e00484b5b36c4c378fd28cdb9c834f8e32d37dc3967aa0f41d080bdc0f6d78532fc2cc971601a8c2cfa160e9eff8b4caccba9c5fed402c9f2d0e616eccf45c9aa0fae22a48c03fa379c4ead4d71a23ed30f9b094f4611a35c204be2cf22e40edcd5f1cc40cd03b049d21f1a2b228ece5dfdde70d783032279e0818b09712c0797f890fe00dc00a2a44289b1728588aeb56bd831368d808d79f06f1a4550848cfbcc2d58a89dc8e5b479a03e7bb58c12b511b4dc90ee8d79e4b013d7280f289da9654b9983b7e2eeccfab8d583e6cde0055461f894409ee32450a2fef970307add0ace07ce8fb3b64940a167fda3d222f024b24c601707fef4d0b3f0b8c472228099ee40132bbef185a07496e3f2b0f3b24110b981a1e2506be04d363c9c752cf84250e6f800384ef4d32430d84e7f74f69a9ced98c5d4ace210f0ebe39dbff16cfa51378987d584ab24d8c0630211c3b18cc91f5a0d881c69b0e7bc45c9c16370d19bf8f82e74183f170b578a9756a07497c7eba8a21188c5507f3104d2802a559f824d76c81019ad69831d816f4eaaaddb91da0bd57aae53b00cf1bd22bae4a5741108ace682d7cdc417f8bfcc4cbd37c41b4ab77324ba58f0fd9e341d298bd2bda3db2715df72cf0167328d1f7fec85d43356d8e8dc1c7b405910ea73fdee5d980f9e02449bf2bf39ea2f6527c23b2a947fdb101ff70a02e049333b4335ed031338acfb2c97c52208a35e628b6a1886c498a2aadef98f4290f32c60e0760c7793fe803fc04bdd702ba9e4a479c7786a095b6e38bcedb42200245a4ee3974645578b719efe43465cb7de9eeaab9d18fc8b16f1eb09a8cdabd06086ba366d72f64e6de561bde3fbf4b34a573f62eed30559ef1283742c7a1d601afd2ca10f076745e99e2a57303949c8203070071597d06f5854ecf17b1dc7d06bea07e5595d42e1b177e64d8f2d5fd9cffb6460f114ff0b3968d2f7660a0b30af1108a8879b681e8307007d353322fd4abf9f0de13f9803f80982334a423400b74b41ec8024e43ca3e2961694a10f00548068ed7127a4884fe3c78e9c92b1805519c1d5bf89ab4a0103e2a4dc50c7d1fb2a8729b52d11281e1b664f532ff1208ceca53fec7a04f3ae61b611fa1d63bfe3bfe949fcd00e04d532b243cbf42eb024b806738918b90a04db3ef5a29c5acb6d5e3897d593bcc3e4ad14599d896a0080008f06dc8bbea1bb8a09b4879432115f30f22727472cdccdf4862e5730a840d6c12e4b1b84709a1fc2fbb678432e19fde8d9efef5383f2e1df6db486a70cd032746cf77ee73b3c495fa80841f6ff3ce5bac3702483096e83f7378ead1f6cd09593055867859bf7d1b5114976d3308c0c802873d7aaef645da7223859aa8e50c3476153ba055dca1796ea51f0bb074e45d01db59767a0b3f040349b92ed5c704d9e509d26b7e32aad43550b90d30704f1fd471b0d800263a04bf243ffea6c50a7da5e32112178409d0d0b465b1dc22e71cbb75491f1aeebc4331e4f7acde1f0e443f89cda25c5dc6a97bbb33bff1f08b0c42476de649925bdb3702a459718d066ee98c2e31c721f6c4eb4255f5a8647a14002bc9fe26cba19c0c4bb7a204b600b74c9a559d93d20e110bf77fc1d5869321cfcecd7379474bc4f3170bf3fcbb0b4fb9d98dd5557859762967de08a9b6fd1118a2f2c127c9dfb9f5d993fc7fd306887ebf9f20f5e1f5552d9d880095757a37fa997b019f4a3a63be345e1b6e600c1286ef5af14e807e0cd1c8b45a14d4584572c45879d218ddb05af699b2c8d608c716d53d6db9e56f30634ec46514b160646ece2ddb96eefacf4c2217fbdd150de6902b166b7ed7ba242f443abde4c47535b43a8f24d6596b20ce6fb797606e05272e177c879b0a0da4feaa12cf70859592e9f4731e339a18df31ba4fa16f500f8859f049dc3675d02f6d80005abe178ec5e889021a1bd7150fac8733a73ad009ed57bbcef03bdeaa7a91e862d3022ff4df78546d9f2f85ff1dd470e06403130862d26c80c193fd3f2dadb66b84f905b8be8f6809113fe5eee1c80574e122930ecd910e16ce3a90b258d50ca2a32439214b8bace2aa01ce8396b050bd0190840426c8812daea4a5db67e8d61e8dcf1b5273c17efab76fae91643cf268b991e50b53ac425091e3789a5eabadf189d6396066fbb5835680386bb32fb1199c07e9076ed62ef7ea776dea693d7101b62a913feb18922796311f742fe66a9407ea7202893c9a4077f24cdbd5f1e3ccaca4a18aaae3a7e46c745747e0b9c7d281e3a1031dc0ab8e8877d19512805f0ee39e46c44c841649c5f83ef174e933862d0a3f08e7ea69662857722fe00dba6e069a03b0cbed853e2242a55071acf090c70ff6014ce454d497c90c34ba6e1fdf2b77fcc2ce9c0c784451a4a7a2f13ba3b093ed0d647a5db83c364e666924d749e742888f353f1af41b3c7180c037aa097f09f808480a3d966bedf5241912c5ad691b1895e4b85b1a48d9ad4048fbd6f6586c92005a199bf26135b25d373d9a0bd0306133e608ad828c032827fed84cddc62d6a00302841d66297be96051da77e0e582d0607ce97f0e2bd160abfa5720187d6fe0d1becbfd47fb34b1248a0b9bbea6f15ab50cdce9b6465922c85f16113eb08e50b961737dd100f84dc0289102da7a19317b7b548c5c381c2b114408de1117a53073bc350536f8c5c29ebcd46bc203e41cf92e69cdc0e00058dc9335fbe44fe0c0d284a86faf832d55cf5a2d3ab1db241c773a8871206a0d63f98aca504f62c310c9de804a5fd380d03d31bb428f0de9009adfba71d6b50e861f4c7c096af152b09a79bb2b3762e169c057b2a07e14038237634cbe70d5240767dd23d0521c67f04e98ce9c85099719f1d984d62a2025cf11b7bb676224ea6d98c7411a165c8390c58ab8bd5e0777320b73db7054965034c3348cddd4e2afe9f46f2f17e0d14d30f49b136eb0607def7df5c013f77342a108128fb66905eac2d823d6315d286f00f3ae40958cc95c7a26beb62989d5303ee0cba566ed03cdeb0319e829923513e0a6c8e6f55bce5904c99930b75bb969e685f223be28c307a43de042d286d9fb80ee41e5c07e883618963536560979612bf39719fa72223bca659a06df26755160173c554acb4f8acc2b219b67bf2274b96daaf10ff69ec21e233c7929c692c5e0b1248abd0f3a181892a51184acbf7dfcce7cb1ee9d8dbb2263a7134e5c12fa2030ae22c322cd237aa24bc913d68b82434dd8686ea1df4f7c0660fbe8361b042064f9c051a86de9dd982301b2b2e6968b062b2170221069e2e0c7ef37f8a49290cef73c4cfc1f45e219802804a55e1e5014ad4643ead9637d2052c988cab1e6a00bed7e9eec5b687065e366d570fa92411af57e3ee02bf2eda68585365793ef706e308f44ce72dc9d9fd8ad65a99598cbdcfb7a6328a780c52d1ccde932c574903415e91b6f861ed2b8ec371948b6bc5b6e7f999977e2954b0ccec466d853a260b18bb675101992e19e37bf54dceb8216c7a5b222486a3571f08d43d8bb7c7fd05bf61a7f3a845aba43a985cabc4a8a90ea20689d08f356b4fb22a2eccc0b0210b0af655543b4d6aabca7c930cd59e9690c9606c3f6c04b7f0f13f992b314b7201f5efd416155bfd8d3ff1305d7333fdaa78c825f04bd0954e1344b04cfd18b2010e2655741c2351ee14c7aa4b1d241156f141663adecb6c9a5f41f12434252e02b30216ed340681346479bc721a1628070b2ce19f47a97fa886961a84d85d670b4c60579394fd7af586e775964b87445341e91ad089b26a1c1a8c98e468646b06a82ab10f11987bfb8469d334a210765ae3025fbaedcbd0fbedcd56e68e753a0627cab4fdf9ee6070daa3ab5012d65d94017b7b059e3810e3ecb028cd1c68ca0fd64244fa59bd51f48a1483365f28df4a39948f9f9e5a43b89e0b3f87284788088ab973cfb4739effa657fe38cd87cdad9b1ef47b5589a99d1ed46e2e231ffc05dc3a06c372dec4c9dbb3e91c93ff72860b4cc5640dfd423f8663971aea4dcd0fb407975553ecd0099b2b1ef648ef2c6dba129d38e0a4a1b4b3b5a548587fd70ff3a354d26acaee272647d98f7f2da4e626c4870235a620361f690d8384d26201720202dc9a784d8c49494efa09fa2675ba4f7cca2c3b31018afc6dee4bede607bcf5f14774759d500a567515ee234460153dbd3e7e7b4db7a1fc9790f78e9f0e0fd8bc4c167a9201b6b91babb567027cd98f2b9b122f100b07dc67d7c22f5902a0393afd5a353cf48c7ff7eb0c2f937184f4b9fa868983457b65ba053af63009c2d5c058bab23a7fecb286dff5cd832ad33d8a64d88b393b384befe1f8fe11066e2e44391e69a2ae289054a732cd35f4bcf65f9b7b32d5c89cfc29ef82978808f664b2ca273cb138075a0dd47d98335f86110e36cff2c12490534db2810a980ad55e098d79eb33019182d12e6d07747aee956679b968ae7959acb4aacf8fa902 false -check_ring_signature 066ba7506b50d3f83cddeeaf1549fcefbca918eb7896acf591d3e16729e05019 4b72389472bda3fa935c375f4da6324423dc400cf4a4b1fab03974720d64c4d5 1 b0db587ed4eab58b8149e2dbf6965ddaa156dd9fe7b4401ec18419210bda1f92 2093b2e5ea045d6a8742505769d60b7b9fe55f50541c39d57c86fee3b2843007db3db8351b01245f8b1bc4928d6df0d0b2d43f1826bd5aa26beb1cbd0d758b02 false -check_ring_signature 0697ab1f801e6d94bc307e8779c44845152e0063c9977391cb844b5803b149b7 ac0f0359ecdd8e6e60ef345fa0dda041f005c374ef19e9b2db17da5cfe1fd5ff 6 e293e8d481e88898b31bdeed14158ec8c48bf9d61342d9fb44e255fa0c807834 2dea559d7919b9d4b0dc9c119c34f0372373d548fa3953d6663194e347e97d9b 17e6daefd4566390209fa22b2f316a699a2615b8680f0c293ae2c565a68ace6b d85e16ad874b01a11ae2d979ec8e508cf13b06057838a72df181a75e03b3bb88 808c4c323784554fd93be95e2d89ebe23ce868a8ef7ac48c9e620a8798500a2c 9ff208d44a24f384032527f3f987e4d9b14ec7d009f3f0ad755a1e26c092c216 ac5e2d20ca1a128965a18e90939f56ba608da6c3c38a2c5d0dddb84dca0b5005ee4e949d7534fccfad10257e3118bab37b4d740813c4f4a3d7e51180e593ca0c3332a7617c567211b72ce278ed7ec2814eb0b21ee88e382676a0d67756c66b05a653a4a141febdfb79d5a6ea84eac0ac32912ffd1e5f9b3b1ee6b91c147bd203fe5e9cd0a8bc69cc99af718d1dc93f050bc15fe2bf523ad2e2eb85c74d29e401f45e60a6011dc0185843aadc0d618b37a554c3c6ae2e94fe361e770b0f504702ca834c26fc93f00d3fc8cfb83173286f9eda71be61eed9b74fd8b842d172d70570151375ee9d14874aef6676cc2bbc1b1a1cb567c785773caacb138fb231eb0640773cbfc68c9a3fee7a242b36fe94f9f555caa85f55a3aab40499970a9a7a0b26e2c497db6afbead811cf4af01f9b44a8ea4314b218008feb19d07195f96e091f9e84b92fb3bc89024cda5f3748aaf4901b78ade5dba23d574cea88b199e00fcf11af70afaaf931beddde189c4e26ad7a75199ca4e99d1926e152f700fcbe0d true -check_ring_signature dccea384ccc954032ff52cb92bfd3bd6c2c167b940be1ffad34fbe738f98fff4 2cc0be94edaaf0d9292b0026738301c6d2628d960c867af356ee00261313a656 52 470f96608f0cc5bd50b141300247c68d87fc1e3366661e65e327fa0f9957b8eb 2ea4e8f386b003017f5eb897b532066b98089dd676eb9c9222cce1aad192005b 5e0b2ee5a1db104b142b5d3c529ea05e40d9edc6e44994cd43942103e0e17826 644bd55e14f1c46fb701fe7f9f1398cbd018ecb7d65c455432f5b8632f6c1f16 d84899c86a9c5878a0cc29fe1719b0e37aa4c58bbdf9117374597dca002760ee 95160aa1d247f96e04e09845366e95308362342efd400cf2aabb45712578461f 177685e035c5d5c773e5d10c409f6261803280664b323c8d8bd14a2271e3b8e4 3893f4a4f61bb5ed79dabc68d5287c8a62cdc2ad0b7cb0aa352467fd6c7803eb b9d5c72c6d70aa66a926247c4fc5a29aa227b98f0e4e9ecf08234ffc9faef20c a034bbc1013e313ba5a6ef8afd45908fa4e737b5be05ec7fd95fa9dcdc9d6714 9cfd707de09f12444e8d47b4f1b3fc5e1b7030ca4c0a6e237ce758425f794571 e5d716752ce7f0370db334ef0c5920c64946146805e3a08c41da89b2bce6acbc 62e647848fdee156fd6bd98e9baac81612b5bbf8b73472c78deb87abad2eda5a 6395c6d6262da23c880dda1f28783b7bcdfbdd6e8af6f3cb1eefe6b362b20dfe 8e5bd6ed2cf87ff6e95e3415b5455f43c7711ad7c4d7f005fcb2ddb98e30810f e324c28fdf5eb4ee38a8fc8ce25d4c076fb1d6868d0d1cab67d5ab4688df7d02 4c0c240e581f42e6f9c39a375312e664e036a5c203e2b7eb2e266de8f5c37ac4 5cc7a204cc28a70c8aa749aa5620dbb9f39fd70b5611f2baaafc03455a66faf1 03ce6be69fe9dd7b680e702e1d6180b8b17081739befcfb0c2721de5c2478369 d02dd66a20a69f877120c9e70be2f7c93d3098c73179bf7d7da6f7addc89adf9 49de98fe19dda022b9166a71bca72d4c219e519dfb8508750ed274326b603c6b a4b99f04f6928e0894e9d91add64730b6d1895a4cd0ce10905c04ef6b1433f03 42f1eaf47646646fa0af1e6bc4ab3b94c0219e55488ef5c06c71d3cd4fb8e0b7 705b621ad9bfb05871f08ee0723e5c9c7655dcf9a8eeb5cfb6020347b1dc40aa e68e045fd189223d5ff46f2261fd5a756a9a5c4d24f0af9e793a69ed2055e622 dd057abee1fb70387ca34271a2efd3231171fe248e59ffbbfc51ae0cc4f111b2 556cf3f0891d387c05a40de1f6af2173d8f16395611f67cfe098593e043dc1ae 97b3f91d12c3d4e2eff132ac851f2edfedb457252a3ece217999de0abf5023c0 d1eb464a6064227dee3fd35730c70460b8d1ce482fd0ac4278c3a6c7ea11d988 63514749815c11c6cbec472572460c189ff8f253bc3c60c9b81abc61e419c953 05a025397495581185989957b4bdfd8fada3976bc6dcbdb97f9d6518e2cbec15 c1df73f9a9966cf1e59c2901801ffdf141a8876492129d5379e9614529a6bc03 9f757a3383765773dd73c20270d98752423dcd539b8573d7421de265d51998ea 74200bc5152852b2a491109d8f1748f84166dd0e3438b43de65cacc8e481b139 f1510d23f980360a1440bc4d1bd34d747e61ab94a22d60166ca014c45abb36fe 8b9cdda1bedeb8d492042f9555b937e1dfbf53b3ff36898efb9c4298cd1e1b4d 95e795e310082904da6df7207a7359f1dac00031900545873599692c388cc575 bc3d46d3ce77e0057fef8c0f99a7c87b40a37224f6e9fda35233bfb10e66509a 17342e687ec64cff3a3400aec4667c79d6b22d6b85cbcdb472e00cfa3c46e709 25d9145e5b165217466870fb83e1a31d0b09087653a3d9675b93116afe46c1fb 6489fc8af846f6e701cb4ebd1f4d298f2c8af4b1c2a51114f4e5fe485468e0c7 5b06151cecab180d4c9099af228522a9e2c22200b1c5d64e5c7f08f268f9b4be 8e68fe35b25da927421b1eb7f09ab011197a2c2eaace7376f0312f82b2ecd084 262c096e80f703641067cd782554634d7f131810c2c42ceb374be8c340877f6b 5befeba756852d69695c25e2b95194c49f15f25b938da753d681301475266519 fc0c933747fa5a133e092c329c9b8370e2893ee399e1eb8fa30e646b3433ecd1 007e6f140c490c7d3568b47b29d92dc606a7bac736f75be16df7ab7fd3dd7144 741befd96569c24e7840baff1d2b105da24f574673b6adfda842306a8f151788 1ae5f640bac0b2e16381b2034aef1dc9aa3fc97bd192e3d12f171c345345b001 d5bffd4a17fcefb701d5f5ab532427c9ffcb81539f4db1b1f9d01c0716147113 c52cafc082ad829305d321e9835036190791e73e1c9a883ad9284c00ab4de0de b5341dc4278afe1483e58bbfa83d1316cb5d54dbdd1e36d566a1c3d92b1ddfc8 9b8c784df5f8b115a085075d814cdbfa03c7246c72d2b53f8b800d88d55f5b0b567659adacfc1b45545ce3e14fb1527268f6d2f3d0db4f9ff4d2bf882a06f909de60f9b2b2f71bbc2011bb46a5455aadd71ab0ca814adb5281612cd113100d0c3c9207b3bb0af8924af896c5e70a0775803cb1a1670abbe7963f97f628b1ba090d9d79a1b2dfc4a12f705772853baa590ade57335eaf92f36af684ed07d6af05df3066e51301237b566b1eef0aca40cd15278df6c6fb8ca317723abd91daef0df0069b254917ec72c638514429eb350bb3feed5c1ade0ca605c1d405b0cfe203b7e56ba72beddc68f36dcd17307c3ac35867c9faf82fe1fb1f10fe19e0ff640684067b4f247c965aa2352a51bb841a5092357fff6228d906987386d00552400e319cb75d8fad5d4df0b48220b967b0ed0f7b5946e5e441a24895049cf0af730ddb223daf7a2ece3647c455a14a416d45a656febbe960e1f6074c3ba625519c0d109103d6abd0ffa4f396cee9e4d7e6fbe622987cb6900fffc4dca9caa2c09c02be03eaa3956ecea36aa459b3247eb507112ac0900ee9520f14c6fe7abcf30d0293ae6edfe460b6ada6c47fbedbcc5cf672ccd75d7962728df25abd989c707b03214aad02a8a1a9c08a17a61381998d7fcae20a81c7c6d1b45b38e1bd1af71601ca9b95f47faf8a0b77d8ce6f880271d3b8f9556ef453e4098974a77b60641d05d92bca9060ab7348a76e0646fc5a6fa7461413f0fd7ecffae884ade4400b0203ca1c657c086ef77678aaf06e45ff0f4a0e997686b39481633facc9b523109c012b5914084461ae2b716f51c3aa8d18f5daa33af00f9ee9d0e06693c6fedd0a010bbe7a1e3d0fe4a61d6c13c3a8581df9eb7dea55e7e176467d5a79dc965e3005273c5d8e1116418a2e29f6417611fc700b241963c8bb2edc7d61edc824cdea07f211ec0416166efb343f43beeedcfde52714c68d49c7db5739f1826bee0784002879f3701cd4cbc307153889148329ec6bb3bec0c0f3170426002183d892120d22fff29806d117553c830fec19d910d60816dff60a1ff361814e9dc6bd90a80ab5bfb842d44047ca5526955dfb4c02b280ec5e93d439c7a5b09333916383830b6277ffbdb4afd1e875e9a76462ab70f80a3403250dcee78a298a242764a36b08b0c911b3beffb8914b0a2a2d012d0b9ce6b645fbef2ef84657459c0bf5357e08adb0e2bcd9419bce9a286dfb8bfffaaeafa9470221700738c771edde32136108077912d4ef7b700b42138fc1e15137d1dc235e155e91db771c3f4a7001498d0991033f17dbb650447ca6cd0e4bf653398c01661a82ba55f1f98d8f883c3b8e02f6512becaef0f1c6db0633b2f3a7d5f723b9f946c9d3d0ba467cf266723aed0e57e99d0b661900a294f249b72a14074ed8f37b624c7cfd1b939a69a02f88030c1ab7abcdf5a508163000c50376e8cddf731df214b9f5094fc6406b781a25d709c17506a3c8f7057c71b797a999743e6bdfeb48b42bc04a511a9a291fd185bd016ba9ef3dc80b4bd05a681dbec0b287afb0e59ba8241b90b70338046c9614780bd69ae76ba93cd59d2c5b8a67e181eeeee394864855f16cff5f9c3d32e9e8420fc3d526a0d29f1746dcd3770169ad46689d34983045cc25131f5d8597a0c5360ef71fde70b1fefb4d80ca02f84a5566fc5ff3c413028169e765bf5b66ec9f1d06460ca8842007e6333031eb8e737e54feaca5ef46aa7dd7ceb6795a22d932bc0614ce36cf665135ce3496370be65f3164cc6c7311ee8d7c551a104940cf83f3046593b788ac5727404d2edfd8b5d93e9d26f0fd5c2d4e466c87b310f72796a10c3e56d3cf28158458b463779653d72635813ea70632fcda498a7ac7030f07300b4ece3a2cd5495af56df0c3e385e981068e85204a7f2b156ad468f66940c19f03080a7d8622cea34964c78cc95ee9f5693ee062176d8ccb0142afb8be4dd17100f03447d7712e8de30c13d27d412cba02d475733fda9244a7d9c6a91c7f8f0407c6f82472c9a27c22e63b1dd19f6a6a749e22f9009dff11f367e7bfcded58dd02f38b5ece5fef26f82b6aefac49588cf0644627d910a24882652822802329ee02cc64d72b23cf17cb4d3d2bb04c20eab81f4c3446246994fc91f856229f69c4037b191729aee9608b17abe98ef488c13bfd0b4203ac723683f54248acadeb400e124e61a9bff4d0a61b423b5103f1126801a82b185ae93d7a414bbf3cede4fc04af024da52e60d2643bbe2a4917440f67759f9fb38190fda0109fbe7578bcb706822a08f540735bb6aa161ee5b414e169808f4ded8a39b457f28bc787a3e182083faf5afc57103bd6ba153ccdda0ce80eebd199c4794799aa3a4cc9c01466cd03d87e71ab2b7a8c02c30ced44a652160b454bd7d900898958f52ec2c4d28a3b0d6c4c1692a4664f24097d5fe269c68c1f9ee7c114beaf521ca51923c641728201a45ad795a0cfb918f03a230ec8e70586a216453f0da0ca232255745128f4ac03b1a688f921ade1144623b6ae852052194e5810b6cbb0190648d8a3b511abd601ec45a3f3ca2da4245bf2b8a623032c0d4aef0411ccc158a3af6fe7c8da505d091600fae75a1602cd155a36a66f4d8c216f6cc416b6d33880afe413fe5c31b5409a9e79490eed84237dcadb3c27cfc7302165c5da40ec579622d3a10304d6bc07a276eeb13cd583abaed4c1d6e8fb2669535eb3971ec6720e9fb205bcc9ae6d0046e34ffa1e78d6070ba152f8fe844f3c4de2d63a6c7f0f50457f27c9a931d00a8e919cff9fd711407a6148ad80944cc0b9d69e8815eee1c6425788f07ea19704bb53c8a2247f96699a65168673a43d41cb193c5a83469d071a4d813e34d8250aa9896106abbb36f05f904584ca75b0d9b0836f4e590d6a60ddd75c3aa951ea0f9d503f53a8758e78d5d749c76836a595642f0723bb6094697d95092b5c42e208551ad1b8d8bcf125ed875f916ca0b25d81976f2d0fd2e09b2790fac82271460d258660e0bd00b8577c94126ff5cc7c8648eaa129bf07d543a555f38cbd46ff07e779e03bdc7e070b1f4d2be981c86ead2e2caa96eba5a402cc2eadb0f3a6480f317997b99d7cba6f53f200de1d158277c49a40165536a36117c978e1aeeb5705ada21f855b9ce569a300cd4ddcc8fb39c1ff66f4d1f5914c862bd49105d7a70084f06e994f8e490e9547ff22765ccc9c6eed9ff434a2d629458f3d7e94e99000b013965425adef7c0a6bdb5f3a7f16c62cf33cb589cc2842f619f24e1e473404a7c3fea76d2a05f45b47c49c1895548a48e03bfb742c84b07b70fb652f1ef00554dbcc984f788354f1c226356f74879da84b09a7fcc88e3af67e0a1b43a4890d2e8fbd6b2d2147d163b783f0f352f920a277ae09ff72eb7fe5496cc5fff7720b2a7e36cf8b832df843d3cd0b1e88c926bfd00b7437b4aa32aaa7ed8e7ff7ba063363851babdd73df27cea933a6954312a4d75f8cbf1151997babb3a7b37ea20bd15a452c38d695f421abdf23076d4dc248e4d67ab0aeacb6819bce96248ff40c52ae7dc41649d6cbd79acbd0af5004cddad1ed10d5647a125a2397eaf0d1d70e7f0fd7f7719d1eb2518bafaeb2213d0422941f56f18fb782d151bbefeb5f190df93c3b28b7b9a605bac09d8dc50474b29a01a6eb4df81b90724036cfade26c0a51d696b07f133b204ba5f0b4e628479decae75abcac78bc9c9c5dc5f721b840d0b2a7339c7d996a08753399143f90b897fe24498df2489146358e975c0fa9b0e80fb2b13f3a9ecdb948323b754479b75d95e28794eacb883a710e74ddd2d2f04566000c54fcacfc0ab5ea77fde5976b18429ac50218c84c1bfa1f88487ab690067fd36a04c56a93e041dd8afce845cc9a94ab835e6163feaabbbe205ed33f804b4f16694db44083525b8037f3cc48fdbc95f8480cf61106a207573c09fa15801686cab68ea87dd68eccde82b7cedd26756552e660ee0cb4d6e7499cd7f64ee00582108a3d14ef64e1d3135d7d86a35ac700078ac9a9394c638cfe9b76adb5802a511bcc0152a8aad708a330ffb0adc904a0146d7abf4bb5f7027ce0f52efc701a446cb49883945731ca7907b9baf790fb21bd9f0e5e6f51de89fbabeed74290ba09d0d49ecbd3e1b5a210ce8138e3b0d7227648fc05148801ab9475e3393bf0cdd81859ea7b99f1f5206e5116f597310f3dbcba164682aad4331bd2585626b0bf11364f05cf28d0918fc9b4ed50550e0b364fa7a128c435477a10d47e6ebad017e1be59f6068283da694c8677795b66fb755e7522a59fd59dbbfed0ab284cc04e478672a784007106b6055aeea634687a368cc98ef5264b3c95c42eb1422ba0098d086dce3f5a5f3dc50ec9bb524c97fc3e971a68faca16601405c75d652d909092ad173f8ce6bc28a317ccd0e25d951ab8ebe86b483935c8a7e5b4b01dfbb0a1e3473070cce206f9a9d091d02aa672347413aacde7d659af5084b3e2fcbfb0e6122eecc66fc1759308bf1d3635f6a92106ea9c3489c3242c23b9eacea35a9071f5111dae87365841107edfae0374c004274453d48fccb19f9acd5eba46fb1056f200c5fe331fdff446ce0e303c878bbcde26ec45a1e886a97162c9db40e0b0681c0127677390e5707cd5fab789e57aaf0040d2a1247949f83eaea859f317000 false -check_ring_signature 5054b8df396d9bcf5e25db96935839d07714f5b8a5d1aa2d362e30977640ae71 f5bcd0d3e8771372db40825b766cc002055bff109ef8586e09f4e5723b2d0b2a 5 573fdef1b58b1193527df35e5260e954ec0ae4045ace1d23f380caaf32d98789 f4a5f71a4d822ee551fb94415a7d10f8eaf6879de3a859bb2c974fda1f39212d a152e79d55cfbee8500d354a2efb7e30fe9495f8d255bea8c7842077f485ec19 5a08e85d5fe98ac97e2bc5a51fbdb6d145ec3d389714fb131a01b04877c85da6 b170588c14bb4cc5c9e9d56cb689d7667fc3464419c17bd2bd693082f2fe9280 fa09aa9579e5e4a44db30e730b22264b4effe960d5df4289684cce954dfad609d08f9f4db8b529c3a89ae9554e5647b8f068398d969ad9f82a25661f2045e503be92143f14cbf583f128e076f88f3e986320e8a1d9bbe0763dec43a1be04110c94982fb99bd7ca221444ce9f52e0c0e8ef2279a9566398655fd09e38e3b0fbe5923a1677ff3b443e2f8940c069e795cc4e62a65731866ab5be8daf68d0dcc7034d159cc7beeac355a611cf0fcbd4ed8ad6d07680b47c8f90d9d6c3af7f08270755bb58958a39fd301224aaa098ced181a5ea9170b94cd2054c69f93c897c636526aeff49e1c1fdfc389b06da504154e21be757d7ad42cbcbf2d35314d721ae06faed71bfaf7f6a883e95f584cd70234ae6327d38ce949d2637148d77f90c7c011a7b29b29dd5fdc60e19ca80976204df0c3196348a064ce51495846b70a13108 false -check_ring_signature f6ecaceff222290a853ce2ad4b7ce73b33eac0d1ee52fab652feed0311c6eed1 b47df97cac3ee781d3694b0cb36f86f5b585c3a8a5f136c91f1363b878722ed4 4 1eaf114b5bb9f49e7d28ba6ba6c7302ed9c551016809aa58df517d9548a44790 db92e994edded82d7a841eed3c9e3ac97a018bc10fe5944e90e8d1e63cd46cce 529a9f98af6c16e7dd881d32895a4777b37f44d8cfbddb76a8dac92753602a84 a91543ebdab5ef5435c85f3389c415fd3d71972a0e29798e7d7c10f4438a8f1d 7b33661dc28a0332d3cc835195288bbada8b429e7adb76ab64e6ca25786b1d071eb3940ca7560b3fa52b9e96b7dcb00f1a5b894d8d7c0d9ec4ed81525ccef007db57a1a4f234efd225488aa2b2e45a1e64fd4b296838bd6e18e276a9f0ec0507c101285df92faa796bdab9de15625ae9ae899c4d4c4dd93c21b9380ad226900f624f8408f149fbee9f71d9baec4d740b967c173a478572e53a821465727ddf009a6c95b5939ecdaad283d80174329b3b8f5951cbacf1b3a0ddd4727fc6042d0f1d24bfd4a8f04913270e52fe23a0f785d5ec80f430cc724a13f6ac77ded1370c2bfc6c82aca676b84deebbc93684e8c63081ce506bd02378143b74c03704be0c false -check_ring_signature f58ef9f5c694c2c7a6ba3a2c8491baa46dfbca0df40c348ea6331386a3e2b93e 00b86d41859f948a6fa7cfbe6a209c71939941c60ea92682b91cebb56bfc89b2 1 59471f3bddb8e8447a3c396123475bf96a5eba62e8b8eb7ad1985ac52345f38e 7188ff44e715121ff041a95f1a6b8fee957bdca69296eeb00ae9edaf9d5656048f06711b75f58d6132c31835e82567990b6284b47fa3ca55ad8ee067fb480a0d true -check_ring_signature b927ed1c13d7084b24d46e078b41aeddc4cc23b62fe8037d874d1a29ba5c4282 098fa2ca0e9243ee14a6530b75f2246435fed467a48d1ec420ce8d9f5ccfe4c2 5 b0feaca3dcc8d74ff79b32238eb88d1547fc3679f980d821f15607b27dc5f02e 6f42bd84577215f7b9049cecebe032d6bab923b1c500358ae229a93528b943f5 d09b198cce802144305371443765e176887b147c74e8d637e9745dfa5a1f7b6b 0670289a464ce28e4cca5774d9d4212ffd3b7940d38c920b61a811bd61c5e7b8 e3abec9819b96e9fac76efe0ba7d743274955d8e4807fd1c51d6153a6fd51bf6 0e7093c5121a3ff78fc901a00c715ba51e4540d7eea8e6624b5beadb4aa36b0ac4bacf55ac2e648b70c499702f53b7aa164ea4c41017bbaf44d0ac2726a88e0032ab784cd0f8a3931987d9a0ea518e80d5cbd36c6f160f0a6448b7a4f6f6ec079839b087a81e058d3670f2e5f6c40fb806c6d6ecc8ecb73de126a634b097070f0f56ee80e22be9b2d6869950502c65fd830d1d812f8e83df32e13556af60a90b9962d80187bf2071cc5e67d19d574d1278e9fd27fb7ca8b518736ac34e7f4d0e42cd8ed3cef3da3f74dbfd534f571a522f9577fcf38d114d9d3199dc1430de0e4e943fa9d07e3286a941b0cef125cfe9bc124c28f9a6a36467cea840dc7b5f053ea461476173a8f3189e6142e06906254ddc3571d443effb1d15db5709b73a03e1ac3b61566bc09c75b434790beccbd956e5fa9b06abea14b252fc6ae1f3c603 false -check_ring_signature 041bdea077a377e6b569f8ba901161d38ebbb8dc21701a7ef7523d71cae8fc78 ce6458321730646bfb610bfae96ee7b042a8d3e337e58484ca71c34fb7c98baa 53 ed78d9a2e57e8cdaf0dcd08d4c9e759f340035d1ab469a413cc450c5e31190ee ee044da946da19ba161439b869f24abe11fbd67b5243a68c1a918315354c4e4c aaedde3f2fab04b8ce4d58cc6101c1b59178b44e66ab18cf145aaf3180df7e5e 089fe514c7c182f6f6f38200c0fcd81fbe01a107f0ef5ca25cda92459652d1c3 6555ca1daab5fcdc0c75acb05d26b5199a166cabad794db094fe5cbc62812953 3580640df0f77d72ab32c07d768221d8b9fda1645ecb377eb9e8dd7a094e5fd6 b1ff716cdb9cc92dddca7e9e712cb43ca958c71e363e3afdf8ffb8e7c34e88ef 94ff525f3c3292fad1f602d6763c9b3a81a60ad7225e26d6807f1ffd9b9c8eb0 b735cd14dfff9c05194f63fae07affe20a8b2547cedcc5496c0cf408720af2c5 e0e9bbad0df776d68c5c62910dd9de6822a86f5fb81843bf9de0a0aa9c404314 ab092260416eda1396a1f5689c212820de2ba4df55efd3bf6f3f045ff78858cd 094fca2776f31d2033a00c9010e1e61eff415e34ee2e8a5e1b1061dd0330cc56 6a7bb62d753469792d28558188e6e42e2507587cff55754b6738a04c21f44105 69404e4025213a0c3a78e315f454959d5ca8f1feed2d2e81c737ec66dccc9f87 3084aadad0ac52475007059aea855b06ee9096c30fc231f7e8fab382a45edc80 bbb88a0bbf7ad7d638b03b4710d9a5a27b42258d42ae3e16f103c23cdf8f629c 6e55d4ca980c788cb60b91551910d579286a10c04a4e1f73e7fd03202f32dc4a cbb2748cad5a4c80005e4c85d11d9bf8cc24b063a040cfcf1ae3cdc98398e7d1 b582285097a9abac81d95d836753c02c3a8d2bf496af75e3eb06c09c76f39bfb ab9247662a5863ae82ac932b90d3682bef49b7ce554bb121448600173b22828b 6076ff69d5921c5db8c9e9cee7fc078efc49417f6525ca10eb6739a3fa19d419 298310ab2e4098cf67ea1cdb8718e3f2bb4d9494210edd6d43ea98fb6e574625 dc87946e44f64c3c993e94d2938b46ddcf43025798d11a77894a08faccf59e85 9b56e9a99bde2b9e93cdae892fdfde852cf75efe3e549b7c7f819a67cb457408 e66ff89251db3528b9c9c85a8e065a0b9dfff81d9de36e4e092eb16eaa298ae1 52a8187330d8b3552e6f298deed7647acca1c24d4a63fb65391842ba7f115f70 83ad328328c81ac1e1d9ce82805dbe444af939e6c40a7febcf86b65da0bdcd60 151a50b0c806834441c48b8d3f2b53b264290f872f10a914449b090b4f5d3682 0df3b0e31b682f8292cf7d6ddb5e1f0c6b02fa9881ba3e3a91dacde5b4abc762 1bc15092a2026de9309c9cc8ed46546bbc528f2ec99f0140d13c2dccf9e01186 4089892a9fc143445e0c432af3b5addee5fea7bdb51e5456aa8cd1eba824a4b7 faab82599aeeade55af68f1778158511b8e4df28cc8f55c2fcec2cd60c1007b5 81e381908eb5cd9d497d5340a820bbb4f750140e4e01c896759e8a308e607c44 dbda89b2abfddb998befd97b97368d3e5be0b5b4f6388b6a273357dd91722de3 2d298506e1f443eabd7be7d78a37504d16f05732be2cfa7730bf9396a48a2f95 cc716b45a4d06f9986377853d134fe341cf17e0363faba15781e3020824cc675 0f946bd182ebc031394737629f0e7a267cf6897cc7c647bb9f4870a1ed40b087 1533fe6002aced8ccc2a03883be59b8c61c5df638a81eed7ca5c141a4ce56f6c 17002c47d92bb0e6a917ce59e7bdbf30931079daa6501c0ce620f66fedc9789d 559f97ca21852ddd607cd7b23105ebb3f02f5d94e8a983964f5e36e30ec2a568 3eecd495412d44c9f109ea6c4976f99c2b7a5f4a0e7a344f6799a17fd73db7a3 76caab548a1137a3644dc4dd60cf580c6e244b9c0756ac8db233aac6f16307bd 0a547f7f8cdb19e5f7f94a4d204bf08bf989d975f1a7f59e7898568bf2987d9d 8106bb858ec2c5568d1f4f375585ead8410a68eba11ffb91081b7e58190d68c9 c18fe5b4e50a8fe9ce1fd475da89d0c9e89d1ac042c289dbde7b1b7cdf8021b5 b1d554fb39754c1a6e0f8231f2d53160cd94e78faecdb1a0fbc67b0db46c4382 35330b441f1276a4fb1d75523c15907ba2a4b728664e5b923e8bb70c2d5e50aa 7352f803a37a93c78a8fd894e89f207245ba65c1f4ed8695d57d46041f7f56f1 d27c2c96999f7cdc95595714e3d13515f53ae0df9b637570f8714350d1697d7b ad198f8467144c25db302ab30a80e019b7adae0a40410d1aff515acb2591cddb c6d454d5093eae5b3c4ec5f8c911bdd6eb2e218278b4b201a3a19607b8196607 304a739ca4817e364976f42d2783b56dd9a53fa36cbe1c323c3d7e8763a5c442 888ea08caa7c00982563bb2266cd780ad0e963f3fc99120adc2da4a2bcf96b55 6dce67d10566ba53bad60dbce8f0f0e0b1077ad34f8b9d013293ba43524a4a09d10708378a19b56ad420a7b00ef54c047db7c68339be17052caebee5ccc2da0824a63a0dba01a27e5824bef3a1bcdcecaabcdebb8dae454d478c7e4f09da67063c3f6d8462c165fafe483964714ee26a9d99ecdcae05591d35a11d6d57752702c039279f28aa96f885c40b117352ea04dc2e7a5f5c9fae06dc63c9ad838e8d0bcdf31111a271298104e95ce24bcc1c232e829e0deb6ff26e24aeec8f506a8d0315c94a8f10ba9d6e4b50d3be7f0d5eba43c4f6b1fbdbd60272e8a894cde74b010c667b8332f64afd16d0e8bb0b9a18dd7856977f609cacfa3712c7be50c9f10ceff55e173c85ff2a504f0ea90d2ec883245a1ff3d69d31200d8852520532ca0e24c3f2cceabaeb9d424a30b464e2e87f776e5b865254daea6fcb1a8626c0bd034f97b272cc13772f4477a0aa535cf0483d6dbed5e80198d370f593c23c08ff03423f80c3170f2ace161215e8fea185b8b507057cc00c1655276ac1baad4e6d066c164928871332c57154f923b35c63679ccb9fc17f4611dd33c8610a2ebcc90e3545d2ebac645789569b57a37f210dcd6243da967a4cad55c61390be6289150e3b4b5b8dee9305f9a7c282144c943a7f221ac5af85acd415693ad3f33411c2021df42497b0689c84511fa6a9e82bea9b8feea37a70c1f3fb672bb1f640916307bac43517d431ea43616376bfa405e0b3ee6b3b4ebe14c7afb08b98762d20a704c5c07a200693985917419f3d6a5ac0038267f895db2c64263ea70a5c661565019d15bf4c05baa0be4a03d355fdcd769c2c21b25cb8507baa0d21fa66a7be010f904909968311d4af3e98f1a1454684bb3becc32550ac2b5c0ef64c892827a40e25bfd7b4b8a1b675fa681a12de7a9dda8c9f55c0facf6a672d4ca10509d2040fa1d3e6ab2ba2cef26c9cf8108715d8335bfc3f6f415d9e787cd469fb2ab85707b9a9e9141f2e503a476eadc9ad7bb9080fc2c9774db9e7ee21fc6d162162e508b51ace99c89148f483a3fd698b82bef39e13de74ec73b47b71edd50d7c6f300786fcce0b32717ad82fa650d8fc7b92ad88f510315c4ddbf4ddd7920c1b1a930d24acda1d920e8d051ac0a508bc4c45ff7df88fc8b0b496621671a361bdfd880894cbb45904a38d7830874e9dbe789ad16155470702ff05e440cb23b7a0176c01a5736d9f3c7dbd836e98f384094eb378aeb9bf5ef76acc43c9e40c06ac3ea20f0bf00311f0a1c94b22eaf9d3f7b2a8f5b7168aa02bc7ddc4cb8e5578384eba070d1943cd96c3e167bf61aaa1c1fd76fd62f2ad7b4a07e60558019eece379a4040db9314718c489b1218c866d0a307a0b53e6e327fc11bdd8cff96a47d27fc20aedde9010385640f536c017bcc1de92c6062bcb63ae3ce968b2d16d287074e50302f79f9e9feb2038e87bafc22aa27cf147f032ad0f07a66d5358559b5095c00b3416f9713fe1e702cb96e7f623fd85adb64387060b7caa434b81dd64ffbc9d0c866ba45fbfbfd8e532ea615324371d97089d078f5781acd3b7080e6438976c03b5b65d1d02201a4b2c7e16cb74bf974b6ff245e1ff01190e2f7e83c535b27c017e98686ca29885543d3a1951d48082ac0a893c877695d0194141913e29ca91082b99f61e3778a910884513069cfd3b3baa6723ef455cd38bc44ebfcda2057c0f6b6fee30a6de446367c3b550733f0d80a7a2ce557fb33a0400f6004b912f6f0bb3f1c0be0d642272d86975ad2a2f354ea511b18d28c90a9ff4af4a43553b0206ab73de8425d4dc4eda12317c14bf44b33e23197634b67c717238e76e142f320f6d871cf98c462fe8c01cefbabd6b901c8470207d318f57970c31b19e7223cd02faffd8096f18c9cd91577fd55a7d527b52713804e38a7f420b05d41e35b24f0104ffbef2da3055f259a4e822d0cc8023d712ac0a3ab599cdc24396151f969e0f794d0f068e027a50ed887a154d452eae18188beaa7f855316c4f9754909c3d0bb15c623aaffd45c4b0650a186eeb2855ff6fa1a22831646f3ca270c12a20d30e3cddb88b953ca6da6d0b5c6c07186a5cca11ebe0c70dab26e478efc49e6ee508006e0c05440bfb887e74e2e6d1c4036ffa1767fb88390626920fe3d74e70dd0145481bf49d1eca0372943a121dea1cabb406954c7a310505896e17c44039a50cd182e14c625bd3e231514c61fbeb6549265aefacb1a551063ec3619c6c1726095eebd51469e91996e76f3297ac59872d0a575f82fb29527f46ea4917a5a54b0d6f477ae083d4742dec44a74748f5a61ebce656748613663a24076381c4ee060418283d965d4d4850104c8e15d51ef6b5f7ee2ac40bcb61861b4298a49748450c4851fdff7c65d1dcfb82b6c9cbaa8bdd0ac27816ca2332b6d3d92490f684af0daed000a9ba27b10fd7d0781aa4f44921719c9835733c67ee11cd5a6c1267340542477bf734305af36879c71418c04bb8d1d9d2216f250e5095a4b03700c77d041593eb793a70452aade533ab9567ea43816f1f405c75254b9282bc905175780fd0e087b5faad49893f3ae432347d75a4e0cc1a4d13a131e4ec89018aa0bcfc0a040dbbfb7864898f00c60e079dcd6fb29bd761c66862e4b66d04f3a40c9fe504f29b1e7d747ab8e4fe4b433c64fff5e6f120b0419005a8bd038363ab5555dc0f04abb95fad03d20f082a5090ae2ba1431c0c2768a1f5db3ce3680578e98e6e07055133d4d2c0e1ed9aab743d5be8025d069215bb35fcd0002eb40e6d5c6d0607fe3a8354a86d49b5d441e866122a7d040fa325d8d9a93d5f04953dd795580507f75fba7eacb1812960066f1763bc349891021ac112a4939a737751bcdcd4c80688f602c8a4a3bcb8ceae1b04d62e4b5c9975aeb74b55c4471e2b16bf2aaeb10650155709717078124c9348988756b9456210e67f3a6e45fbad19f207759c89098b2ac2163390eba3c1e88de2590a90120049d10909c6c5c8e142fc445d9b150b27ebc245e057716738a6daea5d2aad2ee030b79743ca6e34d914b13dd11b6508f19e9a7bd91e2fe95761651bbb296d05eebab33add13c45092c602d7c5f6a2097fa053b83e975b8f62a21ca5e376b4a8abf3469e5c5cc483a0933d09dacb1f0efb25f226ef3aa72a588a3d9d582a5bdfb1a14c0627c95f89883000d06365a5069bf5b816150ef1811191e0b97a1ad9f9f75628f6be98ca6bdb5e7a9297c8a70ef774d71b238fb384a14d2cfc65500d50bb7abae7132388b34b5b3b336dc57e0bf1fafdb06b8f69da245380d220e9ca0b9dce1bd9a1d5bb00fa0727730f4a790d701b0eb7c89fee562a05d92290b651a9a1c77db7645e5035f243d53e1564a408c6b37b6c2ed54054ab5a3337bcbe69960b55586a3e5f61d57b1eeccf77b5c00e8afa900bea8f64defc8c7128ef91e4d9ce3755c39c41ebdd3cc21f101009fc0e1aaac9cd9083c5f0983e4a09a9ee2896930bca94fc311f50f7106f0bbcb7ea0fd80ac02c2bea89a0be02814d56cd14496e37e10064314986ebdc219ee0da3600c0dddbe5b3ef60915c333944820e5e3eaf90ce5f31e59a8c9eb7483579f1ff07451981b325aff4dc03ae472bc9d2c2bcd652f9d6e37c8614b203a99939db65002a095ee9fc139c582ab6efb24f585c1638b99959f2a0fc9a54cc87c66a851001bf04923393a830f426c354055f622357ac3d581ed52c3e87f2a3520d3e87900926b145f28aad3e6a3fe22222cf4dd816172068267d8b83c522143125b3f41b0c25fbf4594a3a9b8ebf51798df339683640ee11f9e847430007c09669c8533605cb934ae2b9b5db826bf71005a2e60928e0223fe22989cee35bb8c15569d70f0fa09f1152234d3758d5c6a5d6a0571046b8bf8cfbe5943c8b8929b867e10056051f79286d2924d82e4df5ed7cec8832a8f94c73e3c188899916d58d4f7c861803874bc9802bbcc2bd91e868ad9bb6802703079c4a18df1b55b3f3f2d0e08407066f158621b6d425a2805059c6b3faf2d7b2a64acd1c03214b99e8f1507596dd05eb26d6a1cc9494150fad519996e30e8678a1b4524a9a4b7e5eac1d758f4eaa05af6098c5fa8ce2a3380e3a6fb1c516dfcae1601005a3dcb27eee911fb93fb20ab7cad5be7b590c82f434773e09996985cd5ff38daf02d5ec6838c6bd7b00d8045b62289f4feff6d073b06b4fc68565c4ea51b6c5be81dbcbff0ede4970f0c408d5e88f17ea611a05f672d4ddb8249406665560d4c54143f07ce8545192331c0bb2910d5158908bab842049f04210f005994be1bfbdad23732d7e98e125c86a05fd82d1d15e1480499c0bfab607fef7a8f5b660e4d6e75fa6e8a758ffae7f18a239f0ac621a2554364f7e3986f28c62c016db9def6168dd16dbdcbb96a57cdb0d42d9db09550a2c1d74af38f857e319edf44dcfe5a694b73f2bd7fd1097054b0934fc0470de2febaeaebbc004ab74598793376f72cda38a88fca61891938b22003e4bd56192231354897f799928fffbc91ab079869f4b4c36f96ec7216541e907fd25718777fb25868460ace710aec3d43e12259063b6866fc40e616f72987103d9e22b6b3c662475e794f7f16af712408cd083ea15c2e1573ac2e0f311679e001b2a13376f0aef1008eeb0bae289e6efd222913dced88b392995bce97310a20a4b389f1e9f1486555f77de058cd69bb6404af15887d578ff0ca287ddb1915d011c1c8df995defa839047dbe23692526457bd6319ba79046298a10584cb9abb09 false -check_ring_signature 442938fca51f9eb0b2e004adf4a44b765d7bae468b9dbebb41e1fab94e668c77 f78416cf7697fc9a7cc12cacdab618e7c7b3828b43f402c4b961ee4dbbaf1cd5 3 c39512232626bb2ef03f49c906626c71a1c82592dcdfe52d8bdebf58a6eea242 9ae9d9573a311bac645f4477453b7c3df42ad9c759a6269c9b250f39e7bf0101 2966ba0a63a1d014f71dbb17e13cfd24087380c9d0ae088cf698b412a46c50a4 f024d21cec72ad38d52371bae17193dbc0a8a96f6a6a8b3bf68a7c3e5aa18500d23446e716c14e2437f31a0da31119b052fe9d6fcfaeff8cd35dae6a8b66af09404eb945192a0dddc69363c5d3f01798e9239b4cecc6b7a364b0ddd5b958c3032aa74161c7967f9dbd36b1003d6a7fa821e7a514e0730fa342133d4aaa9f0d0719fe649947c2325331af854caec49be193f352a54d5271ba9fc9c957ff6e7706af001dc3d65dd1064d7cf582b9d69b81ce1ec70427611940e2cfff25466d790c false -check_ring_signature 377bdcba1f66268b83729a327bc9eb15b342c28c7b8d19c35de3bd2f5ac4496f 2308a71ab42fc81bea11393545322f3b01f0646857d88bf5cc1472b354252ed8 7 8fd0f48dafb35b7272d51945106f58bfc5e48de600c8d046e473148d479f9fdf 1fbc2a356a82d914ed17fad16a9f58249651ef50f10fcb43e60e297e0ee4eb08 015f4f927cf8e4206414276268924297eb4ab63efeddc57d43cc7fca05bc4983 90799c7b6f26713482de018aafca65538e574023d1501c198f61dce712130dac 413b7f384087bfd6ba820360859898a176c1e2bdb409995e1e88bb4684578f1d eb36d8696c86e748b7ac51c7bb6500ab0dbdb8e87e2f8d2e64cd1136e7fc9238 a0f4724f5030c9a1a98f9dd733ccc93d0dee9f8549c1c629dbb2eb2020d17c65 a931a8a5ab4bc0c54bb549017c426409908d9ea7671cc4e6b2381c1110127405c019442e738eb37f12b13b7b7e1478098c80c299f642abb01ba4b3d94307c60c70bec5d1b44ec4bb22d4b47034f2a9e9a6d31e8bc156d026d610a8ba381e5708a335204b97d51d286cb364585e8c95b9ef4f65150f5f394e1fbaba02d6ee040c3881af7298992774b78e38f9c1264e9cd3866a0559323f9605bba2b946f71501dc10f2b2f49808cc055d26da0fd018fe5dec68dd7fe84b59c3fc7fa719d8dd0740b39e78099e7d4943ced4050c6e45ba30ac835602dab11e5f30ceed478bca0ee1653fca4f4bbd5a70523c6c6247e13069b6763bea6adf81d8af8c9a81f8e206d154d0e61c4d1f56be2b26e7f29b6113ddbdc6f2f13e1c617deeb2792facdb08401814c113bb8259924ff4f315a56b171aec1a92269f329fe5230a9f2246ce009a664ce717ea04e57c15b7faf4a594384cea06b29c05deb06df800379d087d04c6841c9fb85d66c9a92e779708e6fae86542ac111b023c995eaafb3962a08603e82bf13f0e2638c7597d0d620d7f2c05c0871ea0caa45e2b95c3d14beb082b034c9f8c34bad639119515641b5c571c853a6b8d556897e6bde2aad0fcf6f50e08 true -check_ring_signature ccebb8ae7f687130b93916a7d7afea292ba266eb377390455ec3ab18b55e0b38 050d0d6e0052176bc5e6d4fb8e489c43e22eac722fb69cd56d32e3648ef6bd3e 9 492bc87efaec907ab81c01026b8707ff5cd7015a725b38dda09edbfd1ef1a18a 4ff0344fd1c52d3aec9f30c09e41fd83dc1a23d27b9a3162b089898eb487a988 303b25e2efc0b2d56e26eb1e71a346e4a49d6e840744a7fb35eafccc6054d8af f7ea060beb5015aeb8450c26e811fbb0de478d07743f5a6adb7c72fe4fb5dae2 bc2d71a422173b07fa41b95a2b2ce851824c130900bc6d42894efdf46add8d0f 96eefe953c0d2f632c7f2500f932153d81078d376a83748edf2fb081df4d8532 d6faecc3696e62597f72bd9a694216f6bcb5ba98230e3f4fc01c892868b133e5 8696a43bcde94485b68852f605049661a2edb6511413889b828a10393bb842e9 6560b9f769564f9e88ec2b293fc485cbbb8b266d70ce4c6339320db7d3ad46f4 34e905faf04b2d52d50f454649af13e3b7b4dc65ddae9de903ae8cca948e0d05953670fd7e3c17bf47b693ce26e9358711c8ee6e7f7e23c90c13c10a583ed50be1b4634e992afb64091b81f194b3eb79395891686867b55275c0264f7a2baa065940d9e8756641e9941dbdbcdbf3b86e875a5f41eafb0430207355c6733f8001f661310bb1a3316f7346d669d56447beac086b58661779b94b97826f413d26056288833ea8e7a1ac92cb7c9b6b75fbfd8f1f780a10ae25cbd094729e8fc5040d6d78259346c52ae6e78c404515b254770c3dfc8245ef6074ff6b717ab142ec04d24aa8aa5a8afcb190349fcbb6ae14744bbc05af765a6664090ad118bf63ff067a75cd612ef1f701c505503bb06fbab22fbbed6484a1be3863566184e2432101ce27432e54dcb8b00e8e374ae8ef1295cd50b22dead79bcb9d93e2aeb0b4d40d1437bc71176ccd19d7141b06ce550ea1e7a488d952e7803905e0949f238cd0036590469becad77d34e4c765f602ca42a6d827bdc948ca0894334da10a890b3043393b3f8f1060bd8b8617409384a6a6274076847602873e1b8620dbc4f8bc50eb14ccecccfcace4a252bd57330fe477d08e309bad9d3a1ac46035447ba3ed40a45f56f069c0aba1c77df6e9120c8542085d811400d2554bd8703afcbb3f6a809efc7e8fbaab2c26a85788fb4c36cc9d86f88aba1dd2ec0ac4714e3995b918a0735cab6ec295e9099132ba91b7e196b44f7d39af46f1da35398caece72a55ef0ffe53f8545d186ee42297808b145687fb6d3c620b9207a2ae8562a282623aac06 true -check_ring_signature 484d3d9be8d4e8a838c00a87036b6227f28d49083bcea49f46caa425b78ba9e2 f3d9618ad1c54698a644d292057fe01e1554b2f803d41eb214e2b178d6a3b5b5 163 0dee3ae8a833788ec12c2ebff271bf499715cc4a3827cf7230870dff767491a8 97b85e7a50e86acf2d925b860fa82cedafeb032121569bff1c811f66c758b466 f9db32b5813debae2992743e116679ebeaacf5362f1cb9ad7a749c92261dab06 d3e6eba95eae01f0522327919548523ab092dee9bb2a2717714f6d6e4fc56429 db44c817db288c6be3d93326b43df0bea36e3605731ded0f8512a6dd7de3d06e edd7a166977d7ec45fe7a676128becf587d61ab6321d2b50edca7955093eb398 20f605bc5ee1855b1aef3e467faecaa81323c29b6d67abd64241490078c09c31 26ca0d84fd512314da2849766a7adddaeb5d106ccc13d9496281349597207993 a287c960b2a0253047ac19978ed4f8df09100b68bd5152244caa2d0e6cb90d4f 2ef6b1a26301690a2d4fbcbb0045ab6990d2c18b7bd8635bde13d64624c9f5c8 1b42b7c78afe45b80d6a57f5440bf674ccf684a1a8574c8add7eadaf1df77956 dd12f1b9285ca10987ccc5a1bc53406bce9cc5f646548e11fb5a144d8e524ff3 e062523ff91964ea860d08ed0c0e571337a9af49f3aa72d2f06c582de7397156 15b31902c003749f9a61e20fefd60730c8f07f8fbbfb6c29bb36ba003fe41273 2f265b3ffeb4ff87e25072281a61cb23a89fcae7aa563fe7c2636b93476a3a11 2e11d0180b17597c1b97b77553652dee668fb5b3ea2305b1ed58d029fc7aa10b d81a547366834a8c8d3d65cc1192f7d74dd7065848267b88ac709ee7ef834a0e 7cb44d2992433ccf9dacc0708909cc9fc66f19527ef3db2bbd47c64cd7ecc06e ed6da27c7b8e394bdd7042602d734267b115d8f76ebe5278de2e4325ab677911 bee9d04176576f8dc8ab020bcc79e965646e08ffaf1e1593b7888a14b33b2d75 328446230a26b6d1ee45085382a752dc8b721d8dee4ed55ef7ee62c540146d3f 81685cc1393a98dba9a094025ffe672b18c9e21cbc0bbd93ccb85ac08b4312bb 0506e5be05e6678cf84de018f101e3182fcc77be0344799a7ed2bbca0ae3f8c3 61c1e0745e78f5e662a9e4bfefa502fa926132c736f8d51c7774d76be3037315 19c44a1a6794e8cc1ee62ee6a32c970c4bb742247216aaf0d0ab29b330c1b483 5c42d03c5e4239a6271600971ab4939032d95b76a5b73ab11a7acf74c2c48a2c aff182b73b6285a43897204c1d69a571988daf542192299f41a34c06c29022a1 de2e920850f97e677db9541d2ba487e0bb55cedf76f92a4290855bc71760909a c274148d1d16dec0882fb3491d8e3ec677edd19061f099482a696a0be842883b 5064f168117df50f475a6e3eea8d73b436d1e3c222364fc5d82268e6652a7195 ee4f36025347f2adf95f98b4f3c568a8df7dacade70f96dee631a1461ce0355b 7d217fc52f05907b7ff3df6b146052970c3eb6edef4ff6049468a921c5a73c1b f5c70c58c1e72883cd05f60db323d139341244e011f91b6e9b9954ad1f702223 3ec251143a8d6192827e514e2fc1e5d77c09b49b5e0b6116ce61d771ed037a8f 8a6335fcc57e6c0d32d33bac820eda6e133d8dcc503982093bdc6d28269a47d8 5ad88f5e03b73b1decaa86b3fd3dc78b471c489a2b1d148b15ba786bdc38ea7f fd5f9aa198b918315239da92733d65d432517eca1e742e3dca5c26899116e41c baebbc743efd3c7713a513a70a361955f4641b71cbf5de8b5ee27d531f905a8c e0d039e4a9829fcf4ea5ce7a473efcee37149b378633dcd7f3bc994c585579ad 73ae3dae84faacd2d07ec835fc4c7bfe02aa7513339785e04e5fbc8c2b83d676 3fa6125f7485ffef8d130e7fdd2c5d6710e7a1d11dad64a052d21c1eaeb07aae e66919c7c06583aff9d18035521ffa96feb8ea91a64fbd7923131fa56045e47a 532a21e7c5ce7fe4df004e2e5df436aa751c004e3744599f91610e33d64ae310 b09a56741ff482ea7a98a565f2e91bf0feb42dbb9e7d948c31a54a9fab8b312d 566c2934082b03d9fd345f5dbad1d3fa3794f1709468fa1b54f981feffe69e73 ebfebd7807c1fb74e0c8fc3ffbfb82e62b741ce03364ed314a72855099a17b26 7f762471267aa5d29d0224747b7bf580a49e1df4a4b6918ab85800c23d6eea96 1474238b78afc5fec3147f19475cb54aeaa4f888a4e328e38dc90fc56e767639 0026ca733271c42f09e9492523435026afe3e048e25a6ec32fadaf02f9ef618e eb127fbfb17ed11e6c4e41af485f58320a23ee69cc57432902fe43acdefbd839 f4ffcb9bd1d4ad93c109b178485b9b28d4bd5908afb3dee0604f7471e281bfda da37bd291fc64bd8a4c9a512e769c8fe4974980a69ff69cd2142dd203d1713ea 4fc04319ff03526151d3fea645930ab54c80189a139db211e2ebbb16862de974 40143f037593c1efd354f37fba79a58431068c96d9160b34c7dac40d3560de59 465a14376037afb184b720b36bcc850c4af0301901632ca0c7a96a9b92e26d9d 0e80f71109e577f4561edb6195021c25d30a976de88e587efd158d2305591b71 9fe2eadfd4d26b2f000738d6d67775c7953fa74816d87a9791df36f4897194dd ffaf1f1cde8d42568a5c4d6a2364bab67cc7eccbe169ee23c2b5b2168f2e91a2 fcad8435c5bdc82ffa3ad71b578d45ce266b3374ea199dd7ce2c4d10c7aa3dbc d2dd31bc49355daa82d6b24ecf1e3b41f7d2e052b34d833c90bad26cd2dc7fd9 923505121361c025d69ac947a5ea0fdedceb0f234c3d406ba97c81337cfb0003 f1469a1293a45f44a1c688efb029311fc43fae2083da50615b6cfefa49a00277 51c89cb696f19b9ceb2a265eb10cf8e3894b3f72bb2ce20d2b06c486043e4a36 440b8389768927dce7029163af8e855871386ee3b6b222dddfa28c16007dae63 5a3cb1bf8d69dceb2b690558672633dd2b934d088521b99d4dd7a0589b597556 61fd0727cdec203121b0c9453ebbbcba29f37dcf64848d2a220dd477f23ebc9e e5de68d241160ceeb64061ca6cf56085b2383ffcbad023dd18e380a34e66920d d67cbb39a625d631a1b8f9d924fc424a9710566e63a64dfa78ac906eee423dd5 41c5af8d80cbe6f783a8a3431a1b69c17afb9f2dbb611f908970a1494dfd38e3 7d94df49364789109c25f198eedbf218c44ec06468225ead0d0e60e9fcfaa3bd e0f51a70a68184c2232b19709e48d9c4ff6b8c66a2efad8ca25c97358fac0a8e 9870b08d01e0bef3e743ff1770a96f6091203aad69e6806edd0a596e630f5fd7 be735cf1833b2a0469b98673d5e31d7ed4eef5b363e5040b1a778bb3ef178a9b ffd0795e10932d94e33b0c8bb98fd4d0e6cff1d8a3cd7676a398ff64b21a0ffd 86dda7f63d5236bb70aee143adbd33a41d2bd3e4eeb208b79bb8f1c7ab63d01e 079bbc5044d751808a3d3039b7929d9ca1121e9fe181635ce1562a35d05c5cf2 0d7c75025a09691f682b4d40944b2e8d4186e9fc7240d13550f9761e4d1c5b0e 4d579a44dfbc200356d7df4539ee64b4e7c31ed7b40978dcbd4455e0c154c789 8316d67a8218f29148f6dcb98951b35a911ba16f44d105a71a96034f9a0c984f e74a9c9856aa851a562d28377b067cfd9511176c197a04d419c944944e886fa0 7f78e150fbb10c215ae4e819c754198ca9b14a1bca1dffb947641363b784e139 542a3ad3c62fbaeb551a758025c24299f6286ac01744c5ecdbe694c796b20458 702f7f2fda9ae3a96b1b70da766bb0c464dde084b757a71deb26584e11d845d4 2ff1982b9850d36b29e948ffe2ecb91aff4a23b7803a4d12895b34507457a1cf 161d3fbf059cf9cda0cffa271904dc47e1acc891ec92df0f8cf40f63555d06e4 0d77ca222f74cf35b63d9ed7a3688a30a8ba5091642ad49953f5656303acac51 6bf6605f3ad27bbfbf8d33b9466e09d831348a6a756194271283d1f364904d9c 2ffcd2bcaa4c40543340c62edf5860f53c44c1a10e96a743f177d2f12f738cb4 44e690cdb7103bff0a049e0e0f75e2b86542e09690cb4904d294ad59e6e42519 15113ff262b32ceacc03ae2cbdd2e70344c46486f0803c19283a4aa5a48a2ae5 cb040c1014f39ab34876e74a0c888172d8422363cb62d138195f2493f7270455 b10705b64738d2be2b8efe7300a09b7dadf17bfc8f64f59cc93209ffaaaa4d37 b11db3377afc709e634ede70c57f09d670a7152c2ac832d71ae0d6039bc59c6e 0e13551fcc6c8670972577603271e25a19f2b33b60c661f8dde85a3c878df6d7 7bf793e308240262e8232c32835cacc6f011eeefad421baa7f84132e19c302fe 6c79b7913fc97c16ff18ef3fabd89c191b2380188d70a1d085c6895eb8dca3a4 67b769c272a79b045b1b38339ae334a0fa49a452f5b5c2ab2117afd6ad7bf67f fafc5040cd549f716eb00c470ee9e82fe229d5e44625636a827268962a6f34a4 218d32728173545d0d367a8d4313888f7da6edfbac9bbc615254b7d6c0984058 c0e26a2fe45e870c7ea5c2c33a6543e845570ba795eb71040d82c4b0bace9d34 f2b83a1deb8276d4f79384a35a7d92881fbca613977d3726d56709b4e55a93c8 2c007b706af49ae4cdd2f98ee75d615af32da7f60eb48c2116265065e873446e c0b84883bbd21b79c269bd78aa07d37050a4b317f56edb99523a7f77d02d9914 fc00bbccb9f7945dccb1d50440427af139657e0fa0fad7a15f954c96123618db 70c1fafaad27935ee7d630c564ec053d62bd7220d19442ccd33d2e3546810950 df430f53d291d0b62fe79097e3af506b36882bcbc5d0d0e6d1723dfa959dc6b8 7d0709e0e8851853d91565da0b3311e039a673771efee69c8b83c58df6bac51e 0b50e77f0ba77bb25e48dc41b32851e3822fe5fc5b9ac47a3b1e98320b20cdb3 b2f76982ff91be2e93aed336f051259191767a19bafb87b931b409b15a65b328 3c126d1d80b416ce26103bd6dcac3e99a3022d250203a58fe8b502ac4cc61437 11e860f8aa45fcd03bd5e7ffef3849c9a8750cd2626fded2e43d2aba617176f0 16059483d38a526125d190dcb9abdca581b83a1afdb72526434a4aec18978bdd 703de81ed15ed9a3983505eb5d2983a3bd2b20a4195084e4e816bd2721fecd9e b3553fd712ffddbeb5e6709cd2de3f85438b0aed3d47d572134647889d73971c 3ee087fb6b2fe653894d9b723515ee84fc3faa09dfc405924af1f6900426adb8 1968c58ff0d69021765c12f990092140a93b0663787b3bcff2940ae3473deee7 58049f46fffd51f2b42833fe58f282f747aff14bce7b4295702418c2f9fd6062 f635b3be39833b2f1f4208a8d15250249c82bdeb119e83744c4520136b018f38 8382176544a602062723f40986376705f91f9cd3c302b5f6ef3f5e3545df21ba 3878dc819939327bca4eb7d9ecb996a88a1f14c3a2be2aaa44a59d6bdc40224e 50dd62ef02d735ea29366d3249e740e15391c35d320c209677d67b60b4dc4c2c 22d4befc6647f08c85d0b50923b41f20251aac6fd6469e7f5c79c527c8ce464e df5e80654325b3051cd2a80cc1c71be5c25b5e1de2cc980003b05e9b0007f26b e0241c843a0876416d0ad5666afdd402ad98331affb3e360a6b7602bd9d8d0cd cab4ee6ec8224e54d12307e7b901ebc77381501c48b851696c79424cc2d153ed f8c83929f561b9a7daa8ac881d03c555ffd37a0bb68a7a63b1d5090f2b45163f 8ff704bb89441f33268f5229f720e6aba421ee1a41f1798692ad011ceaf42f14 c0c38e8a6150edae09d5fd128390ca17cea5e257f6c3c1223453515f4f00c02a 583ca0f37b1087e2837117abdcf0bbb5d1dbcb7652f9e7dcd22c10950cbbbe88 6fa5e58b0efff67e0c9ff75520a4a3ce831d03cb6f29ce15bc561d5ba5713bad 481b3477763db6950579b18aa2b184a19dc865cacd6114b732688d7507d5cb10 0f8a9061339489ca44bc84af246ae24f82056e7c72ae570160d27fa9a24eabb4 fa0c04c1c1cad5335ae0d88987f0ba7a1183377d81f46b6f1311fa9234273e84 726826352bd5fe8e1355955cdff4f7e3aaff39bb32dd9fd1dffc5302f61bdae0 b56fd96b011a4dcc2293e3f4a4bf7a52dd4791b946dba09b8fed0d78bee2b3ef a77c58f729f0609c6e0399e203857d689260ce0e9c860d490dfef8ca32470f03 26677e69b7885b2a6025faf9c9f1a9bfce31e39bc93ca86eedd690b6f013cc24 aa4a735a806a0915ee1efb7a88aa83296d76b09439eca97aaf6a172b1623d7bc 04d15e070afe1bd9d8e02180a6e074bdc88b74c234968efd492da61a9a70e84c 30d1322dddb9ae25c25455a50de1c3cb2177a62835de317da1a9d7152d478472 7c2b8290855607fefa2a61f595fb58fac96f82a2d39516367b82bdb33d4fab0c 1900f939004df9fb0de385631b7b87e06dce3ee019b807870a72c0b80dfc4cf3 a1f16cf8d2a6d486be9bb326457009ff0b9878e1d88d2850ad3079325f81c843 492a0dbb97d77165432076062780e846a03ce34fdc57c9634208ad8b651e1f3a 7d01212b47a7495aae1a0409d84d92db632b33550e806221b0eac33490b2cf5b ca7a28a5e4c77a57a670bc3d9802035739924b2e230b266b267d72124660bbca 5a5fc123184865164954da37c1547c460e92cad2d8583a9e60a640539973322c 831dbbc5b7ad3597fe67e6665d19ff15b171a9c6ed6d96c04533680cc4eb8e10 49e0e5ddd78f41d683556aaf1ad1b1af5c8582ee6c47634954910eb69d011b2a 780e6fc90bcd4d13fe9c213f7337f855c0dcd16f50b4541f93e3b9f0c6a96a78 4ef385428df91e947dce8dc2219fa417f01f3d36b30f5b28dffe3784d3d6205c 532ac88efce553fcdcad28dab4df6e2a4793508156ef86518da378d8b7c79ad1 d53f5922035267d4350261216270bc765da4613ea331049f4c90b99c7c6ed5bb fc0fac1a2dbf2b1b44620c988571d1a9c75fad56f6f76863dc09d9b660a2aa13 449335842454bf243d631239ec30a253f10747f8f99f79708084a4a063a10a37 b199c43c2cadb5815077a24e949c695ad86a96e345d3795636cec3509f81db31 d762a28668ef889d689fd0c1932755f7387fc09560fb7c0435bf3465aa60af08 dd8fc3c3dba9bdc0bab86bcf3f26585444c6347d2a9a94dc15997d9b56b172db 4ef02043ec6575248340fa51a12f37fa87581730b39ee93fd060ae6dcb53de49 8ca05d585ab3ffa56e8b11f4360a251b9f0de659f4641e52ad3feff55c6abfbd 92dc666158bb8173ad3802a4a231c410d72cdb29d406aaaf626c74abb2507783 9a7d552d680a0a5c395887526fca9447737db9aed6309fa21b4b28f9bb0b0f3c cbc9e3a7111f732dff9106a54eca28efc91cc2bc30e04652f657e98cdbf13e19 2c13015d56179f575e207eddc6ba51470be326cea607d9e28ace85921c3bbc0d77b02f3ea19d5ed44b2aaeb51115c472786cb42ae0182577d9436fea1250cf0ad2f0746958f1e2f2187062523ada4b42eed5d15df81f9ce83c90197225f46e0cc2372bb0b5b69dce1872958722380a1f31f3f025460b65a2058391670ebef6087c4236871f09e4a6729e8e397e2045b108c8aaaea3af5874a74a278beaedc9056562b64c6eb51ef91bba9fe89b6f34b18b25419554d6cbcbb22288aa7fbea10476e4425625f0b3a3e4c70f63ab2aad614222f5d60841347aac4b9d0a3117f80915f1cb3b9d3aab080874419e7a038cc0242818ad8539596bfc8db9a637b36a0b7221746dac8912a10c9c0e2fa0afc73e5bbb2a14919819ef3e2c826df00fc3073fd715563fd298a5a57794e3fd1dc1ad60b3f2862d961b9e29805a1a2ae7150d114a4c0ca3a928f0a43e85dad58613930fa609fa4e518bb20817cfedb6043a0ff889284d057426e4ac222790baf4fed5b4559e123d82f32130bc6cb4feabd601c991711a70bb93ed797ef608d886c879d7e57aa201cf5cff8e047d4aa55dc404b3ed192619d815e954d8434d276531c957984eada067df0ae772b041ee2f990181a25de838e31bbf6eac1e22560113958f15abf33aa491ef4c14101500929e0efce07447013a1a61006fe27f88065128db8224eb54321313e9fd998ed43aeb0f87af8e229693675e72400191f6bcd037f3473290935ebb1329fcffad9be9f2044322bd6d29fc9b62d7bbeb85555da8e3f0c5c11f8c44296974f96759ec22f905f4f74c13cfdb90519798f1cb0fa45a72042c1bf652166ad68489628c1d576101edf008313887c1768c79e88bd502c5c02d7079aeab4fe87ce60cb1f9f72e4107aa60f353a6302542b2093f5b07a077723b51e9f6e8509cf959a2ffb3e3ecdc0dcef5e660396b9f387caff2e9fb78a49f0dcbc92b20bf626c5f45ded76c55650bc6bdd36995e6e8144cee7a31f3e34807f77f1ee9303cd8408626e221cd7b5300796a9a667265d26b6bdf93b8252bc7697df638429c1bfe9cead029aec591700314a6ddaef3a51fd7893ea608a56e94e614d0f256d2c8a8b095efd5d45c91eb0d9cad039cba812d9e64823b924200a0591636dd67863d036f1948649b5138720820787293fa55c9c57a8c453ed725f93407461b2b6f6b6d2a09b1b0fb85de7b0f37343db8ff2a131d478f82546eb2bc122f398ba620f6140c8824165d408cbd0db31971ef22e4ecda1d6d88bbf12833c7b9c3d238775459f3fff4bae605eab4094b327ea61055b891caad030009e50bff9d8a85f3d1de93e22d47a2b5d95dc00d550fe098b9e32b83cfc830572d3f47efc7e42f1ef04e73dd576c6c1fe955490b54a1b387aac4a04ec1d78cc281a9f25fdd21497c421ce1c6d6c557fc514ca70963d3676a19bc0d2ca91159008d99a473a9ac9a170ea26136b615ac83c91c280536cb24f52d053b12f3b594d85b0cb2302d007029b7e774b9c1caf7a397c3930d90e0435abe2ebf4ff4d16d0e7564df8d62e1ac18d8b218fb6a58a4f2b345bc09738536e15b76de7ee594b811594bdf31b33532a4270551d8ddc0c9a8b20d940facaa35b643d586c8fb2257b3b3879524ed9edc46aa89ac81957125eae5f91000b41e36e14539b644036d8d819127d2057cc8b3d6665b0cbef21193ccd26fea01148650631f3b0088edbab3e5fd6f6ffa436255f41ded73e39bf617788c6df606b5d0ca9053233aba842fa1b8a7703c7665a6d2908a0c425580f1d5e49cd59c0f42518873ffff660a428bd3d316ad4f87f5919869024bdaa15e635266917d6507b988b61e72e9b2a1cf2aecdcb098d30625c21b8e48721835dd96fef9e8d4e8026973ab53ea500e8024f1efcf1cea19f6dc1d67b857fb1b6814312b133d0dd10b9aa86ff999f002128fb411bfba672513e57b6f0b8c698823c6721aea158c7602944de897a7de5b7ded27a967eb1273d969c11adf052d6de74b7613547023c70baef40387ec8efbac88720c0869f3d4ddcf1a0b543a45c22cc1f7d80aaeded30fcec5b4ccc154b2e320ab509206111e8afe5a39c10cc55fc2eb720f2173dcee02134efe3e3cb8af78eb4f24db365b6eedfce036ba9d3ddb8b9dcb8f2ffcdfb904e65a6401a6437bb6c5249cf25c5c5081e1e214a50a883b54154f9c666842390bb8aa2ad73edaa52eb4db09ab8cc0d4627164a45a05641f30f22bb7e332bb2004397054eec92740332a5aa3352e480fff1ca0256c08f5bf6b0b563f1c576b3f05faf586cb75dd0f5e896a8f4fb295be626188f1062b7f85e3ccac1a67b6d74c0ec7306a2d241c93c4890fc9af95aecd5d98abf12e0383e21e685dc2ed4f98cd005be4d31099de0ef94649de83340540dffcd0a89575a67b2f90a9c2f1a4a8020fff54f6cb7fcdef3ca92c677c783335c70f5a0ea805bad4870de32761436e250bc17d415e5c98e5336904d358e45db6cbdd8cea43fae17af64434abd9d4e9fd0b33a15899cba1141f821c6c8542fe19cb2fd29fb962c58fcdaca62c9569666203c01d140dde7125f106115179d83cdd1ccfd4aeb8805ef8c7c0ad72cfeac1f70b1167dab8e7b3e46d86b2d818a283857d9952e8ecbd57cee889518cd34944a6013776bdf5c2020f99796362d2bb2f1996ca52b0646d97ebeb2a6088a71431a00c44adcf6e208c559d383951b2938a2318079751260446c9d543468f5ce23fe400b7dc25ab23ca27a0d04ecf8e0ab325f1566a5af5f9393f997241578ce332c00af739059bfcc26a7b3f5b7686cc20416c3f2363f6b41f95847431f4387f57860cc5618369e019d379af13ca8b73bc007b3581c5afab1e690f43053541f5f03a0364a3553f5b6f557fc0de4eea3795003d1778bbb2837e7da0fbc2cbff1afd8f0731ca19d0572b15da34ca5589b26b8fb7a9227971237d8ef4886f143804fcd500bb37993af20785e16da87e6ef98a22c34c914166405688e720b3467063a36401a51dc3997f6a79907ff7ae6218543c8912cf395498014f7fe9d043dc1471e10b532e13fd32e177f2a59d3f982983bafaac3f5db10a0ee2e3cfb1ed6faf532807c7232bb05b3a8cbda49a612b0b530055fc9eae0ae994de1903d557675d3a7901412bb1812d695eebd673a8f05d2863dbb0e8d6ad316c40cd79e9ce741e761f04cbcb4647217471623dcfb9828375122e89d0d5b5f2574d2f80c45d8dfe63340c5667921b4f42d891ea989699babc119aba0cd9129e809349e6c5a5c605074c07d0d9c3a3bc46bd61719f660e469c9d8bb89e7b4b471800dda02810a165fadb01a29a22deb3aa20a7c3d0e19651dad46f572e0970078d826661b93de55397b3061676d2a014167eecaa2cf304b2f379c42b076e8743ea4e194e96dd80c63d30048ed869f5a2b9a2578a53960d921c22127ce02572917387a58d13a45865f5b308a2f63037bfcaf921f71ac4d2769e7050318473ee3259871329e0239e4533d007eca349efd340a2de111fd6a84a9a43e3b9dc4c27d92a6180ad760a19cb9698046310b5eac90df3840dfe38a1022b0c89ffef018181da89fc996785642b72480f7bf2ef093bd1a1420638d16b87119a58de3e0fea49729227f3d871fd0704d702ae8b727ef1bb753ae0fad19334c5edb5363d352f475209fe25ade1c57e1a6703e3afd178bd2aee3748d26bccff1ef69c454b0e9a919e1cf0d036be5d2625480be19cd16746ea632e5ef165a45f1f2caeb7b71bab0c0533e9c1fbfdc72b98e806d7d7a4698f59ae744ddfd414fe52209a16b085ba5f7880747f391280ab11360b9ff09aaae24c8bc752cbdd419bea74895e393af158b4e3e611d43a5a62922b028424d58fcd0db9645bf92381e7258555d538c3ac7f4fd9b4b9c280a78b61c50e0dde9efeb9c2deffeb81eb5499b1c02e81db084ce78be7b3730cccf655ac50030ace1ff48f03f277a8676584c5902f07556f91875367e8a18f9e38ad210b1f0ca2fb379ccfbda70a24d9bac9532d0f374ec281651c6edf97f082c193de598d0579e5bfb6a54be4f13e1c050dedd7f4d1181216ca3371e5d3bcc2309c9481cf02b0835b63eae163c834d5bf26b33d61af7030817616ad00b1edc2e112b51840e8da180b9caa3dfa2754ed8476355d1069b63b077873c639200caf681c5c2e6005db6a4638c986d3053886a18414df3c5fddc25347256f5f0856a41caad1b576090dbc9909b60157f559709d25191bf3c0f13653749c8b3892d1099a5ba5faf20e9b04bdbc2bb223dd06d99c93e6644c6ecd1374e6afa86f15c52762ff425e9d0015c0eacfc17ad721b13f3300edf6b540c0a77539ffd2ba36b93dcf77507e4d0f5faf3c4bda89814ee9886c9b77fe15e019f365ff699a36fba2d95eb8e0c21c06e5ef30300eebe198f8aed55d327eb5ae67fcdf079915fbe9f15a98121b1c5d0fefd06d33ce5cd11b8fdfe80efc866ea3315ef36267c8b60b3cc19850d447c10f6ebb1cd80d4dd65d962a03b7af903a4ea3127ca03604e591a9a543747668980fae69669d3f4ea0a80bf15599005f9dbb7b00f00576a0aa7679c1a171711c7002ee6a222ec69919083a1723a3afe3ef558a2022e66b41b8bf3f623d80548f430d0e26ad6ff284134f5a6ce2954bd8a3a6380b7089b6871244e1c3d3133a73b3075e9e235b2333cac3450341e524e60a735975979067ae21abf2ad9f41dcb49506cdf557e1b1249fd52dd795431274372394406417238299142ff68d6ef9f2b904f747f69b99aaac1cd22afdf79f68313e14f62f7cb5e7654ce2afe9b870bf9500de24f736b9c9e036c651990f6cb6bc98ab14957d969eb6fca43419404f3bdd0133f4021ff5c4bbbd2ae0d5a7ce39c48e7eac03e6d3917d7dc77bf1c2d477760aac87599ab44d461d3325dd9a7295bd2e951b81776c5bcd5f70a5ce0a8cde870835ad4c8497699a3f4a1c96da573bf578abea3fb4021cacb4164edefacd36040ed8acbac6d0761b72ab35676f85edd7bb8b5a8d6da59243ef00609e6e9e221b09e1b22557db9ae755b1ab7ac65ee3f037a4bb53d47e85e166637ee8c470e13c0c8d24650bca3b3a4b608aeac59f7c51e8fc73fb0e09a1272958b8f63da82bd709786c1bc7e37edb08d3f72b05af95da208382d3a18eadad537f790f0306fe9708e64f8cb7ac9360dc5f3694940483970d6ecb46ecd30272aebb60b1b477128f09199a18ea210a190d6d43633e9b862988f827920f186e7ffebe14f53388cf850f6e97f04c4a692bae8b089e78e6aa093beb696af91d81fa036a881a0f65be850350a27369e2bc4adb4b61811ce5f04e4f77f2240a26017119c83ae97a002c4d0627eb46af6af0668363c9d818ab56830f9c34c9f6ac95d0a19c7955420157b506dece3d2d4646b6f47d876d02e68684a0eff000281a2d992f00cf2c31b87aa301e0326490b8451ad56531bda8c0180226ffc3214fb99c11696641f02a182c8d01dff79c784df507615ab6d6479d80484045200a3978a90fe2068323819313f40223201f955ca0bfabfad1d833c021e278eb9bdb5aefbbc9194c99262129914c0f49a456fe4e16e384054195a2a9a78fab23c3a20658a8a004bd6c15debc280b0ab2882d235742b6f08fb4d5d55476587157b58af2c185b51fd2278f3282bfeb04d73979cabda75ed7401b35a92bbf54559ace8b10b40bf07b26f3d36ea49aee086c2bca4d12290b4fc586bcf8ce361824fb37eb213f6928930d915ddb9cd18605f644304b650e3cf2499f39f3c4366c7f150e85be334a56f2008d2cb76684da00afde3eb655eac3583ca9e8f763530584b0d47667568f5ad89622a5ba2e1637023fa1644e274220f10b0167fcad0e2e40181c532aaa3bb98e64b8d5c7b01efa0fbaea54e776ad2ef1365edb03a0d8f61bc0f51f12f7b1c0d66d1eef318b99c7060924991d6b9d317a9f4a400939e59d96959a4ed9dc3c645b34ceae3d04b8c7091e9a3b62f63f8766d25c0b237c0212652d96f5f063499bef8fb57d1592542000902bdb7733b6aed0efa1bcb1726a43fae08cd6ccbe0a27c1e9451ebc78ead20a87d5fb939e5e740be30967ccbbe410b59b2d5923863aeaec16222c4e2005df0c65ecf5bb3fc28a8f1bf1f734e80bd03cedbc7029c62bfbbb9d12e8c7b118a10dcf16167faf44196c3d720b1e7ea8d6feac7d5b607c50d50b70772b655547920fe26154cbc815f0beaf8697620c6ff940121f423f9c82152c1a4f17819fd91e0123e594abe2629a52b07d58ecdf2af1d3b9d4fb8f6f66c1020f783baa60cf420c6a2832ef555569e682df36fcb0fa48681ad51ea20882a98d389951ad88e00103187b980b02c1b2c4fd94dd6d587a7105618222a301207d0a40bdea06330b740fe0edbd473d0be9b4159b84a183c6ea26d0144dd1811cd4f0d61abf197c7a130411db8238f8a779dcc0eaae130883349e16280b97fb2c0610f3b2bc1445e3ee072302d04a78da2ccc52a78eedc2c68e35440a5732ecc3f2f1dabc4097851744032a979d0101556b6f608fa28f5f004ea91ae3ae2b34b4abb1e9fa5d2af708c904b2f3ae102ed0ece89946b86e32cb73f63360c6ddc30afa8acf84ac8003e35d0652c641f3f243ae90c4807792fd9cbde90d97aa0e1dfbda883a8e83b8e82b0f05da9a0f0bbedd3864195ff94a2c3696745a3e80b4414e2289f7b8cc0bfd66cd0cc9622f10deab3d5e734db15507503ef0a21437e99e23d5bffb9d139c9a2cfb03d2662b480dae9d7bc48b277a13010f9150dee35d5e81373992bc35045a37470e798f3bde60754fa659b8bf96c8e0857b36e6ec2f9c0dbf445b3a37a91d365404fa147ee463fee983343cb5d426df872df8a07dd466331e4a11fef960310729053b680a9c1fc2ff206889ee3de5cbc91f5fe64a2112f952855edc2c204fa02d0d8f88761a00ec7ebe93bc90b33f51bebd870a85963c9bc8b8c490e9a068cc51070fde753a818542948502f8e11f1c0676d763dfb6ff7e8b56887a028b3dcd6e06ad73dde07e55a1249f3cbdb450e932852a45c28208a74b643114661d39a82b0f1f82e607edd4261adb193d10357e8fbcc122794d75b3bfa4bbfdfaf15ee646020d718ff9c7f056203997d9ae199c3d59f9a0031b63b2e2c0a1104b11ede39302323754a2f74d9c32461f15bbb19ac662bbcd1366c46e46dc78b0b502cec37204dc1fe933a8f43380f8fbec7c5337c18d9da3d937f651f43d873497e1d71f7d0c336065867bf134df50fe6056b269e35252b3ffd170aeb856f55da8c95f9ef10c3103c77879f4a940bd9692ea8a276872edf51d944dfee962de732768bfedad01e4c4cde8fba25413923d79475b81a8cd6fb57fd1bd8067ad27c3f1ba2a8e600308c0772742bcf0b395739fab194a3abb7d59d8e95d435efba6896bd9ffb91d0b6b666edeac31a0fe97010ee942c9e516cf2ed5a8d74897ec90334ac73a29c50ffa380e7ad89d80342dac3cf42bdb136771d81b24ec5d5bf62effa3398544d40e7edd53ea3abab5c148acdcbdf47f2aec986183a1404544875562d0af7abf490142ab0364d81f90f5bd1b2e600a14c1a75a63b8fbc05e75b2868ff83f15ff34018f06da7578bf910a30ac84e8aa5da2ba757518e90f3ab05cc7bf542c72b18f0633bf0d68f564b52c3a59a3f86b6b4a2fe19d77e6e9e53003df7c3a00f11f51078db0415dc4046a0b4209e18f69cd7a28d88f7f364026ec8eaaaad3c569bfef0e7be3642ab93b2fd490e71a6c51088d83b24c613a484fcbe08c25f1ed630fb107cf3dff48b2ca1a7e69acc7fcbe6ce20fc40fa943774f12363c711ae63c93320757e46d3bbd5abb3daa141d8254682e58496cb17f0e6676f6fbab9f8e20acab0027da93cf9891e43acda238fcd2858903e557a5ac9ea7ce0ca1e696b73cc9ab0eb594b3ff88212d8e20023f20c76e371f2822c19f4ed7b91503efc05dbc81fd06f083af6f32bc5a09a4eedc1e18f4192b1c10f71cbad27f8b56ae28624d90ea06b973c175e7245301188965890093eaf0735ddb558bfca6f84bdd797f51e34d0801a1c673207ce0604ae7719c8e83df1ff482bae80b5f634aba8c7f91335f3907869f535dd5544f8046779e139381c114d01e43bbe62bf64de332603bb033d407117fb28391cdc6299eedf1c87cb592cc4ec2ab5eb07b98a736043adc3fac8f0c323859e72a4605a86d55b3e5cd8402b7e4e895f6322cd5285890ae6a392b9e05d6e388c08298c165810544c3b152c9fc43f753ae2df96be6a20ca31a603cfd02350643c9754403d551e63aff6660baf27b9e74b62ea969efc2ab68140ba4440305e972f01d8d18394210564fffbff49c0e083cf42af9235d1605198c36698a07bfc3ea88f8d5ff816830828bef70af6edf381a5c25697446c2f64e4f186689067034dbea6dd5e271bd98776fc29bc7caa9aa8e72f3b69432351179bce6c3690a7f2a0a92f9ce9cf5aeaf1caa3ba321fecc054936b06a6daaeb4f2c592e8f5a043511b3bb3c6b0e5079831cb0516ee61d9065e4d35a5be0dea37e7b7f860cff0a92f7d00c4ae5b0d2a89eac9880f859aeb0e707fed517975fd3011f20aab8bc035691b86eff08326050968adcef591b619ad1f9a71a60fce9f50c6a72c3b8800456fac51f2069ee380eea0eb15e8445ae4959b7132606b9c9dfdf5470b1f3cb029ece104667782e04436ad2b4968bdbda957b62e52cac279b75417d988d54cc0cfb38d2395b01231e75bae25246f22c521e33aed96112ce856c50d2b17171bb0ce76e8e2b128b8cf1c7c80ece46cfad7bc1ca87b1a2e2c1c33d69449b7fc34009fa7c31a2647681f3831530e7f5c51671f7c58a88805af58b7a54f6f8fdfd18036e5727135fe852d3fe4805934b3e356ef2dbe754f1a4218352e3efbdf7f7200528fff64bf3af75e90b9ed80b5d778f25e233b087d6ee965eb665369cf970c8055a1162d9e4ac636ac82c69d932f6f1c1b2cf007c08523f5cc2fbce915552fb0df9b2dad39ca52f0bf8faeda4cf94f8e9cdd23eea84584290cdeca3a03cb2570b9004945e2e5d85720c8d5d9dbb5318bfb1ddee7a57d4f6dafc43555498ab800f6ed8cd0c85c4caddaf133cfb904518f6188538aff601af2ede547c1087f2670bb8ef3d1c2c65d347c9c737b6b3073bb0b44a0b7e8e563c2cee8ea2028cb81205d79e69413bc4142355e3b8f23cca753dbc4ff6c941e6fb5a2322649f2d8a8e0f635918e12068c5ddb913f8d5f7f3af2c9b5f8df7c97a6bf858cb6f87eaad8c05f901397378806bf24e35853a0f0665e51d3d24a87c75b0d685ea6ab353d34e51c0df52970a9cd4d7aad0d57a3fcc52694d66103f7ecf4976b6011566ba2d3b0b3f16aebece7ade99e2829f061e40b8d40beb524d6483091927b8ed810484c906769e4422825efbb83ec9db3086624444b617f6622e4d1cbbb3da06aef25f000086e7bc54a6bf32ac700f947ad951f2116b45a95203ea691caa0338e2831a0b0721e73cb95c6a9a8caa335c5731a50943116df86f5d6742c367b3f1523e647109ce1b9eb31c2c915eb440af7bb3dbec1ab4020b25e4c49a1b175b9034597b8b0da72d7721b3ac4fb38eed2c08b7d56c065a0726028e4d6328aa8ad0e070e6eb0b15975d08ee055137f35efa1e345b6d1966fb3251d17dac9119da620d18ba5000185b8192a66f4b0388856168ec22132fe8784e32731fdf45d4b27346f0136c01b1fae78e9663f9f4d4f841278aab858a7822761b792de966152ffc673935a2059896e846ecc99776f4bc305fa83e17ea905c3e5de13e3d9208b0b5afc6ab430013b90f52965118feac57b20de90579b1c7a1d65378a8d7345ec9f0a5c31f8737210fc641ca8068dd4c85e831418d25e4f4371791c606ec092ae8f1a3967af006780bc81c54a6ff649159d91ccbdb64437df399dc8bb7001226b58043ff7daa01065edccbed1f561e2342a43bdb89d68dbbbca4835eae88ddae981bd645ac1d07c114b88de75732b3cdca3b79a72a29ed0551cedf166e89827d36b6b58f17c20cc190dfaa46378ed7adf84910b3ea272a1868edf326addfb69cd2def03662bf0388fbe5480d85fa80006db6d360e5766a0ff094d5b3abae4d9f7cf725af4efc091ad405b7321470c309e49c9e439144545df70ba23797b1db486f856ee322ba0c2d9d6432a5a3a9f7eecb7f8e0d13c611338a5600aa973d2aa478535d3b03350bca26899598fcdd9c84d4349e207fd3c3e113440054d9ddc6c1f28b977439bb0fbb6f73a0f1b9f109a313eb3b8730d4178fcb3c6ca286a5dbdd95ca6a9c713a014611201c59767b8215decb7e389b49cf8ccf5b187b3dab3d95cabb036ac24d02c4350b5f4394f19f55f9b3d74866901ee1fe425254f0f3dd8bb0afe7612fc800bb1293f726660db0a308d83eb19aa4c3367a14f382cf9b60972aeb01eff1fc04c1361aa119725e8f98e70e16948618c52f2409db3db9db856231edcaadb71803ff1d86861fd9fa6ea6be662d9c8e4a1b95e5b5f9837400d381212813e5900800d8e22bc7d6b8d23a75d8e2eba8ecc3156a15ed619ca2cfb79163b4d6d80da903247d0cb1f186fa2b958da577d193550ae0a23b17678e81f6fa7532cc80db700740b17fe815f8865b76e975e1c566be2e24bcbbbaafd15974f332be5e9841d7037fff20813fad181971a0e3a73725c3612d5aeb5636bd715979776894a2ef670064b1d9fce2cef11571d880bcfce657244a9b0c6bc288afd10da2818a9c7f220a91f5d274108ac985d64ae3503238555ea8c141e19f9faecee6799595eb8b0a001932213d2d2a41aa74683bd2d6741d139bbabea0281a65491226aa3f32685f062f176e9eb5d50b131777707f33f6ebd5b62eb3618334ce025ca84de577880403b4ee5613156573f97d24dc4368d9c2cdf2b7ecc0e45b0c35354aadea9e0c640925f892a5b6e6fa7bd804758d2c02b7a823263a64783c0bef6fcd30815d6a3a0d51056f618e9e7a9cddbbc5c5c18da3b0aace00aa0b164acd7cd4ccb6544b6c0413a050b0a0f1bf7552e105d6e10dd0aa7c1a0e2d82c62948ea5e44d43cc282088ed448ae9816b351c21cd9ceec612b9656ab898c33812afeed583949a5025c093332b1a8f8befbe976320b3cab611a03c584603003dec14d12e60c873ae23e0ec7476781515bac717fe7d640e51a091c3a936ea6d9cd49dacc29445c22797607b0955fef8f7c700cd99afc687300e2b441372f37ed4d079e0ce42c9335bebe0024d2dd660a4a98712c0093fabad907bed77463fd308962ec82ba8ce3e08449047d010bcc3aa097ac863891f3e0494cf96c239ee2c82064050699cad5f4382607229559106e4439e8026237c9a8f5ca3f66ed24e3efb11ec43634892f1fde730c9b2a6ac24f68efe4c0c9acfc53a167e13660fc534c9451c408cbfa0fda612b042c33817e6d8acd2fb13ecb3a2dbaed127a0fcbe411944bb77c43c155bfb4a606cb48152c4a08ef3a0f88690c69a109d9e1736aed3ef35f2d30f7c9c41dbe220d759245739a38b8229a6eff85b64d89c412e92d48aed5073c51344cc9eba45605f13ade467e2c96849ba05dfc748f287de3a41da1adb7e0d207f890788f4fd701d8e192b6ac4ad8f7a0254ef69b059a074bf09ab335fde1f316c6b72504aefc04a13feca529dddaffaa15315ae7eac840ec710b44d038d104524d8ed3ab8c990443099517990827f0095354d8291f8a0f1f5b3587c260b839ee639ea89d421b03e7b210a47bbf2bc5b62e173d5a56e431145fcc3f593f837857ca4b0649e1670e4bf69fb0c21b9d956b1d77b03a19d78b0b480c7a29415fe3029e20cc88a2aa05ae843225a421d48b6cfb64f053dd42e7f05cf1fad0879dec9ea6bf1e8d7c8f07d6e7783759ddd8bb07389f31baa799816f84a69f4c306596459fb33e6bfc63090a2fb8fa9b38ed1b496528e460170a25e6304462a3df9fd84a2c6965c18fd00f7bfcb9257c8024104f996c586c06094bc91fa59ee7a6dfa94b87ffedf0f37200e7aae511ab9c69f292cfed9a5f8bb2dd880f3f2f9cba6a0b4a2cb70e4d52a80915cfa6541d12394f8c1bd5a38e73e40af3c7aaa5dff4b2293d3b28e80e018207f61079816f6bdeb6a13100aa36e78a262b46f42a805d3ebb11b2707eba8cdd02753ee8ab698c137f4f75d294a7fd5c72e3be5a0a3a2446ab53d575498d058b0d29358842fdc01f63d6749aa75ea95cef446a40099f8fb4628dec373d65d1610e56cd88db72e24867ec2e3ff15f1775a9b605b4c205b1aebeda3781a50861bd0c589f6daf47cd6449e6d06c36166697ac8a35b0d8f672516892526857f862dd05e92d7dc80f58b7eb965346f95333276e3fe1d80bc1714931a15386d9d1944b083054dd91acb8722fcf09f9cd858b384b8012c26708c70fb8d17697d9ed6ee50ddb2cce094ad12078eeebad23f5e53a928040a01b4d58267b7b5275ab09ed8a0dc07b6439633569ad2a53a8d7984ff4b15dbde14d20699fe48050514001fe3905b0a545ae82296429545c2c0dfebdbd2b11970b01eb15b4c452add923716027096eaaadb20de582f9270af557dddcb615a49c76382900e89f06e5c4e955415308b47891e7dd763087d5e7d2a05dfbee211bac96348438bc52075e7d307e6d530fbef6ba8b9f0518856d7355e4b07e82769e1e40d2002b580b53f1897333092c09089b2b81dab68ffb6787d3a3228170210a29c58326e8331285d11911d43460023bab8693c38694b8fa68967f5f33f127bbb9bd62239ac5162c435314ac60ad030e2d60892731f71e01b34ac462596121bfb4ca6dcc38e44b70a40563a3d2fe050564e6c6d0fbb5ffb335ad1c9a138bfee791b7cc8f127c53838e4365382a890d7c22832b6d0b9db15df65f9eefa1e0f6996374b2cbdefb3d18e86275bb07dc0b00ad7569351666934983048c038084f9266ed2334bbbfc5c8e38c747abecff0a2bb8fa5b4141b7a1105a262181a9ff00d054301e74143507ba3bd8081377260b5fdecfde302f6425e9367a540dedcdf18d0cd08e5b77e08b100f7bcc343e8900a9dbb5418ae682933f024fc0abd98a5ee2875dde6402b7f96ff14b499ba6f80aa42549c8a52b13a44ad16131bc490a3a7bdb2ac0fbc21efe268b8fb658b2db03b4d0436b7e5e2be9c6cbc120454d1d51633d6a9b110e108f1e6642f9a4db7206721ee15e43395507ae7e2ec8e1208fcd88b34798d34d0b943807f8d9a5831c06e3878d4644d9160a5943c0919388d44145275aaa57b83950ab40a39ff893d80504fc2b3e563178b6c334ddd6497270442eb599f0b57680d9a7b8af523ed29d0978cfdfe08b3d96d20adb6ab949e0c133dcef0011248f5e43581490fdc915d0027b4529bd1cc62f7a13047b2021cad70f567eeee2f1085f882e9c0ae9b988aa019b663c0dea12751a9f5485fd271d97add8a9a8d26e701e4414567b3ea6414d0c71f0b09ac44d3803954caf1053db584a6da4c2d6264a2612e65143b94b631c0c108b9e1da27ecf5fd81a328c78ea8cd4bd440a4fae421fe336e79596540cdc088f27405d1db1594a15bfacd36b54bebf1571972e5bace24ec008ab7f418d540848d0a8f3bec2fb138fc345bd9b156477e1bbd9b4aefe722a6651a8bcda926a00090d9ebaaa76fec8cd2bc0b76db711c9ee72774d3f2550134da4d085ffbdd40d2ee395a592786cc5ef77eefa9b603218534f5653eac53b715cddf16da9bb680e2ed282c55178cbb472b01c46956fda5b45c9b2c7359a23a06e71873f9d223003c8f2329a7856442a6f11caedf809cf55dbdd70c89150b78faf736217165e8c0803dd305c3545e28f919d3eee2c0e3a72bbd16a5cb04eef167ca452d9d2468a078ad25195403dbb25d7392084a099504af03b961f77a9b91a30b916e6d1f46406b49711dc74618a1d341152d5aa755ad61d9f7b10f39d8be20128bfa641fafc0812e8050a9c8d0a8f06896ed3c84f83207ae2a26fa34af9c6251e4f20382fae0b00f436652f13a42801d344e89edc5cc5f758dc271c47604ad21bad6f3503ba022e150585fabbf79b86f43fba6e2170b5b7b917d1b4aca55d3031467d2c46f90d5c772f6cf1c5730d0dc049eaace78a303515bdeebd4e8e44706c3b7b31033202c794d8c495a5a10fad67182ee2721fec4be58754ab1a12fd3b8616968cae440167d03c980bf43f3510dcf99d37ac3f68799133b395baf0d802b48c4d089578002682a87ba6b166a54a63b5a2eaac7d1bf946343c486fb9d36e077cf264f864079448fe97c86df24ad4ff06996521951a716ca3ff137a68ba0903fbc4a8e00d00dd9212e4e473e4558ac76b7d7c013a8210b6511861810204c7647a4db539c10f71ec2c94d84a605b128c1ead5c893698c512da35ab8856ab0c9be043026e01029ad52afbfd8b2f5d0bccaae75f94ea13ffb217c740db6f4da2ded6e37c8acd057489f80c2621e158b0d3850e7dd9e2e83b5b64d091f2d188e4997aa09b859600e157ed5235fadbc38384ed6b526ef9a0eb15476e24dec716034e2692553dd40d0307160212065e7ccbf95956dc04be73683b273367d30cce1a80502f0e3fee0b9c21b9e013fb476b38f06a4c7de1d931963de614f242217e9516c920ab6b300f5827a3fe0f7cefe0f13e4bb00a302edc797cafe7375737fb6583a6cb4c0a5808 false -check_ring_signature 8c97e6516f8523df6d78a7d2781adaa4cf2126e8d10475b094c139221d3887ae 37b1fafd9baa73bfe9956144d89e7afeaf4e67d0594e337ec9e5a050baa8fd07 3 fdd7974aac8468519e4dfe1a1284729a41a7ffbd3dc84c8cdaf1416853f023ac 3f5e2748c13e12f62cf7e5f3de36ccaf23f90cea4912e17db189941fd684a269 ff4a19fbd5f19007c9c9d02901571aae3207fe3f50d97e839b085835622285ff 6f802131d4704a85cd67bdbf503f96c451971a6094962698354b51c8b4634e0da173e6733952fda0d5ce1d49817ccac7ad9c07e5ee1d65621228bbee26ebbe0684ebe821349658279fafc94ba93103b84a8a6e6b1a321612790c606725be060fa336042faaae8bcbb3954638c1ae799a34f8a509897be40d768bc4780af2cf03c550d8929d95b1a2c527ba72d12c61ef477b93daa5857b483384299152d5330e11e23c26e9f4fa7840cc53a949a9328d78c04d85985b125253574c2ba9b11a0c true -check_ring_signature c047bddf7088fd681a86a5c7146ea387b4fb285687009ecea16859f7c61691a4 94229a0d2fc423f54dc99cf3d6a72a27c8344f03f10769734dc1e16c497091e2 32 b51e57a4e9147a3ef97900624525cf4fedde385427ae98c89ffef03643d1a78a 9c7120f012e67b541fc7561d64c6046e87e8b5e5d025570ef53fb82259ea0580 d382fb84f49a30f6e7c2ce6d5c1cfcf4aba400530744e2fdf739900e51a7a3cb 839df2e2353fabcd448985210a86900b110a2c2d14db4dc7564e5747ccac7646 ac6f4425071c69f99bb47c589a426a5537a4931a78f8817f36fc1bb5955f7d6e 46a2f68ff4f63040b8b96ba348cc4ced3b236507b6debab78fcee6c7ef6837f1 4503f1e1dabbf876ec131d2de9ae00f8eedf8a9ed996ae68ec0e54562c05962d d9caa53a4668cf8ee0af38c301993c0cacefc1ba9a63b9d91dceae602047eb80 c309bd5f2b59ecda43eb1118fb9b774c419e5bfbc25ee2dfa3cbc4e252b3baf2 53d83d1b18b82b19cb5d1f68bc80c708c367546841848b4e5f13ef5fb0f412fa 583ccaba2cb29b10702aa5cb2e41a51d443c741fbf8e71e9b6f3aaafab0fb649 97b137535b2f62e61df21012a879e506ea4ef85fc5be7d2b0c58985695ca5b06 aeddd5b5d14abc5ab8d69d744d6303660b55c96ac82ff8d4ecafc5db594ec34a f79ccfc3d7c1e9e32ebb7db1924918696aca5cf52ec8602894bd4941352a8301 d39c4b465b2829b8843f45e32c498015836e46eeed3f7f49faa2d7f193803925 e2cc70d19f6715f0e2bb84863f06b48938ef541f159a39de5ff2c8d0a82b0dff c2c57cbdc6f175adc4c894afb289ec44f0a510e63530975b543cd7087bfdd891 aa25f38761c14dde59212c6d591fbb42d63ebc758c0b2adcefc46490d2804ddb c63debcbd0ac7e35db5a56e79440c6743cb40ec3c8fc4956378e89e6e3a57a95 c985b016bb06595888177605dbc9a18bf3fcae51c9eb747248d994957790bf15 5c93f501a7b17b1ffed1d97a547d549bef879770e649ed3ee3d15fe41cc1ea97 7b1f26bce7649d3cb3e99dd91c1d940c2449b3255b4213f2545ed8c63862f11a 0103f880dcb1d21874c4d83b304bf2b6a7eba351c99ac1eed759a4d6e26ec6d9 088ba7cc1d2d19737a3fc0334dd39575422f74b8404c362e14b348225000e447 16e66fbdc2e9805c80ab6cd60194da62c757969d0e908dfcf77f81fc6bf79182 50756c315c3002d8a1424e862c7e73f7a372ad1e44fe4b2448ae2e422a20a5ef 981fd13de564d5cbde90286e01d0e292fd4674b466e83817abb5e4fff8a5e6a6 0e463033782d4fa9776601a3bdb0e3ba52718648e2be587e98921cb73817fa17 2254fda96b579c0c370630e0ff108f14ab3d9670929594f609c89265a5492f4e bec4bed318e90c85e2ecad8764cdf0bc6fa18f106e760ff56f5f73eae93e05ed cd6a489d3d88a233e2a212e3d63bae8c3033e4e0224a364364d869b4ea6ad8ac 92eda3d8b53a8c1cf8f9c43ad3e1f4a6a85759093110dda34bf17296d4a6d2f5 4b3e3682e515abb4349429ea37d9df35b097af32f35e1cd25ddcf8be3814f90044b83dc6caa05f1a7f8819ef894c26e1f4ea21f610f150665259254cc46405091c32d564e0b3de6b10db3532b6fb12bd21ed8bf70fb3f078d941bffea418e906dd204f8d402053e752af39731efb0fffd925d9617071ff05d92d87800a9a03008a62bb18c17656b182a1951043800f8fc0951c55616ee549ff95fba83528230608d4293ab14c7761d463efac93fc26e015950a88cddfef2e51af99c7989c5501497784006c5e9643786b35dc96324b73bd222e0651392799f810d2d37ea690055e8fc59a4c992773b47fafd759e1c18cad1bfcabc072e5a6d8553459570c400f93e33b2cad70f2182858691a5bae7bbf8cc2a7cf5ff0090c96bc73333ea75d0ef203bcf8dd59e7624bfc144c40351252ad2ce4e00b331d34029b13f422c56b02c80cab7609010b3ecbf51c646556c6ca87bb504cf06792b409e26b613be30f074a7414ecc9ae4ecd1fe26d0e2f2dc830a94d11e0a8434a25983d67633630ce04ad57df6dcb0604a0db602d1b67628c0e9cfacd77670d988edb52dca80cffa10575a34f5429b0d946c6835b70b3cdc78df9fc9509d2fab01a614324dca2d43103331121dd70127f10809d68935803285e763a9266d18e9b94d727ced62b929202a21f4a9764a0d1cd96a956bd075a02e4859e3ec465b1a238b21371927c673509b9e328085f6935a5ff3203c5169e043494cc721e130ab3ed770f08401d2bf30ec9494454d66b4b3b5a616632bcaaf3db90cb828b245528bfe40cd343ccdeec01cd8887373c94ef93c7224c297523abb41fd39c7daea616761529cec44a651c04acacf5423d47b10c5e5d6d435350464003a4aa30de44ca85711e385c961f7a03da6fec58b457b25864d5c141c76a31aa7ab550151e54f221b068d1c50205130b84a48e39777e20e23a3a0148ccb9560ce12d6c40267a7b8bf0e775824a8e3d0c68e2bf542a9b4147263541bc3ef9e0a21354ba7b4312114649cf800def36330fbb1e2823054f2faafef91e7824503ab8360c0287dfa38d6a80a16f643beed8073c5ee8b218ec063ef13baaca9ad65eccd68e6bd4e731037482521982d8127d00b65f53fae37205548799de556dd5b25fca6cd1a42d82c8419c4833a5e307f905c5013de2646d662f55d0079f08fce493468d58b5ce023ba6c809a9007248580e10477d29c8b49648a7dd656165a72dfa4c7d158d68f8ecc6e4e69964f52e3109a99f934abd070f8315285c6b525ffc0c2ad1e929dfb6b40065612bf8482da80cbc91e10f41e41e88e8bdcb65d65bd33eed0379d41862181e6a79638bfaae5507ff2fccfc45cfdc63253de111a6830589776565dae23134d404ccc16aef00ed09b1d480ee74c8eb499851266e6b00010cbf5648839490d8f24c492cc31e4dc903af3be15c8e9ac5bdd4c2c65203c549e651a102f794c6d7369188d566ae609d026472b6210e1c60617dd82e88aae220737ee96a82182a54cd5354a5e3cc327008b95d96deaf090146ad840161b35214f99c618cb3c27ca1dcfa1a122a7ba52d1e8d58d0473929d1e4b45d44ee9bda85a2ed0a822f110ff60a6805d95612719e01bc8eaa946fe2cb46dc0db4fb8404a77d295e0cddaa4426c7beccaf6a9882440a95c4fff0048dfc9aa3444877fe327aabb0cd3d419bc28f4432db88e758a7300a951faebc714dfa1c8dc6646071eea464eed6921f59145bc11261a5b44f11e002e38ce8d016aaad7ab3f6b878a5be6982dedb91110bbd4778839cfc143a97dd041d2168bd99309b509d46ccc02fd72e467e95292c71f46322ac6b491cbf31de0d11a74d61a809582285ad24fa7b69730a81583b37a78acb38954ce92947f3b100aae1528a3cdb977e78b19ea38ce144d4e936f65d858048a9fe407f4d52553a0baabfbbc5e4bbba3ce7d753f89de4a7260f34d28173489ef874c4e8885cb44306a8f0d66533a6008dad203a6de213f29f7474226c3e6bb73b8dc7b5ff08ea4a04990c77374c6ea0e2bd479a044de535c116ba9a1e7d3f1416c7aeea33cefbee0b215abab396547af70c0b256cc1c76ee0f5b8ac011673b1bcd9a9baae3e6de20b387697e5e3a97834f1f0f70f0a3ee90a21b01cd3c81250e05ed56673fdcf2d0bf7bb91fda69ea5186f3055590fba9d92cf0e7d4e2d3246561cb8752be5ba850542337685a19ff59bd60c9cd05c88e9d12fe5a6415a1005bc10e648de21865e067242ca0c9c86bdf1705299923614168d2870dd3285e5bbf07ab520314ee4c7c7ca55f64eb97a9f0461029236d260ee687ce80b0b693fff1ec6584e5d5b668d03f936fe519c638e057f4d1b5f638124b9120e12a1d350dfc0844d41b21530bc0ec86c3775f983d9682bf02558035e7e8419074bf49a686e7b500cb82c20bc7401f180006bdf26bb6268f7b41e9375076eb1cc73576650108d0e29f5b89020440e5b7540154db459db89c361ca2b4d49cb85a3a3604a05b322b1db413547b107042ea42d60083b60fc3d08edf4f1ab749f7c53a2595510dcec31644cb2b5de0102a75ce1ed9b915a7b5d7025a56054796fa7ce83efdf81596b50b25895ee7da40566504d84825caaa0e337a2ac4a116ddaf8e5c978ecc7da40824cb137a4808c09f3f2808a00c28c5b1c9d41b89cefa4dbe1f2e2e389374bd1c1de699ea58419009d27137c636fbe0c8ac57ac030d07403ea3e088ed56c582f9207abb8779b9b071d4fc6ce6f5b0cf334e60f97716d11fea1d168ef5c366aae79393609d5b77773c658515a24b389d32047e157baab97bac4186e0ad1fedd04fbedaf4b41e1f502bf50acb1f539d5c51af2428841489f5c9f564248fc5a4140fc82e9ff2fb28500 false -check_ring_signature 0e41905f9f05fb2e7aadb14b35f48463bfdde21c47086a01af0799292fe5244a 547f5594a664f60c08394e044faf475f2faec65d9d91ac57646ef26bc46aff94 115 ba0ca321e092baad389c9af1f7f5a7e74f7ed95409dd438e2ed2871207e7abd6 a89f6d9c97ee1336328287da0faec4181a7d78d87be885715fbe0f7bdc1876bb ac86a62fe497bf517ac0b00ee1bb76a6f6659b347b8693b01682cef465ebf137 9f696821719a0965dc18bd2e72969be2484078defedaa95511699a741afb71f4 c30def0a99199dff69171a8866968adaf261f4e0ff0462e055b39a08224e6b87 934efc0644612c98940ea8d4edf8c5f023de18688154f507b920f511f25fe25f 2fb46b296a8ea768f97980515b58a79ece6b57211fd71c3cfd1d420d6c6d81b6 68792a5c82d6ac441da1e2389aaab5fe28606cc1b607edc80251bcb91877dc13 9e945432e9da97ffc5c79da3c6c056612dec71a45c3f17fd604ed8239d1f9680 c924bc6244d933fce29fbed0c8a72c0027379c65ea530197afe26f678f0826d5 c5f95b669be58312ccbf628c9b945f72c10604898b2a6e30eb0d189294a9230e a423d6bdb86ab6368d248b36c948fde83ec8a31d2ae8b0c77e9da3e46fa5fdf1 a905ab6244508c369dbd063a490a7b402e33701da1d15b30aae5e1ee0fabfdc1 0693e8f162acaae307c0ca68273df68834be6fe1d4f0db6fd8198c134fb5ca08 1f1a78be2a61e6c25bbc0036c2579393acda2b6eafc177409ddfdc7699d78891 e99d355c1ce08a6f82fadd3ebef02f71656029d974f2a8242be37b6c989ff6e1 d7bb2d5aaa839bdd80961f31bb56799c7066a8a1c9f3771dcac3b3f2327fc2ec 1514a826439ce6d6e8f8d7228c69a045b631a13ece053954a768afdd1ae0cd7d 0a2073bb6b771a44a4c754be9ba28a65523d8d94bf14a29611227612554a0a93 4101d963bdf5a7c5c27ebdec38c4557c1f5a9c27d048b8e9780628bd339cf35a 94a4f2135352319b490375608cbee961dc91606df2d729a914cd847334b5f42f 4929045beb2d9512276d457003e3ace663a14760195ea9822c1d6a626316b4e6 0845ec975a7a7e495146f9f7dd53c6ba3f8fa5941870452aef64ab442db45ba7 dfa8ebf2874da851e2f621331a845dcadb668175f0de8762066420fa2e394c71 79e1db7e12ec6f70207d7bc2269fc39d5326ad1453fa7e9ec71c655fac118b9c 5dc842004991a2d7850014938b47dfe56446d770851987b95bd61e4e1b20df73 972ea0a0d4a1d3f1830dcd607369c354a013b77483db5166df91aaf53590fdfd e4385fef1ca2495404bbed6c932473fd6acd57d65b10daf0d18515f759286a09 ce7cb08938a2e08e39635d4ba2d393bbbda6b8046dae9b9bcea06f88661af3a5 2d8200a95797eb629bfc17bd1febc2f751315fb976ddd47d699997915df3691e 33dfcba11c47b695d4668d6662ae6772bb4cc10c2531ef42655ef50154140706 d0075a25a48c6e55a235b964e8fe3b06fb6e8fbda2b83c37b6a527bffa0ef86c 477180934602ebe9cbf2e079577e18f2ee0f50446d2b2e2a80dce5f9cd24adad e3b2f734400c96f77f3eff92f97fcaa723b592a30e48c1527c734fb2e84a9965 894c9fa24f4d23caa0fdd74d566d68f5c222563b63a77b1a2ebb417c83e2e4a3 964627625c11f84956ad4882e66b0acc9a267c0bd49bd36125416164c57460f9 7badd7980c0525864a32e50bfd31b6262f164d3d8c320464ec732946656bbe8b 37da588a20f0a9d4f77752ec3241c3438e4f0592bb49a5984c41120046e9b468 d65b36d46dcd5f46b94933ae02c75d222ae54e97565a8096c1f4c2941954daa7 42b5f9046f9d263d8a84f5d7d490614fc4c2cd57cc7209f1049daeabdbad062d bb071efa6bdd7229ead8682bded6396746685c971db85c649be5c5ae9bbef757 4163a07596038be17dccd60d5790fd71b097c277ec4356563fa7a26082fbe032 9a684758b767f17f0aa0a5f7f32786b45843d2df254756794dcb7c5a8089c302 c17c41c11a29828fe06d02ed0cb9f81de247be5872b53cae1be8995b8d61901d 8b23deae38fe82aa3d74aedabbe0c827b9b788c8c41fcf45c83c4bc08bc9b235 b0c08b7bd23667f4e07a980a0d587ff193661d1284839cfb582994e75844ef13 44a9401e04cada5893e2918df9145437caeba12a0b4207c93fe378e6ba5a12eb 529736ce904421b33143dd5553dc8f96266cb34653d5317c012458c87ac04312 c38034b154bf783f2b88f8e7c60afcd274851eb73c58f398f2c475fda56bab2a e9c03947e98df37b25ccbbae19a92828251dca01a77dfb3abd80cc2b35a77d00 b62a33c1e99da69f9d8e8297b9bee7fd40fb59d56a6e4207486438b44c89ab51 0793556789e196574cdbe2343b1c324b3e6265f5dbc4363827e1f0cf9c40be03 8e4232f2b32ac629d69eacfbe75e54559013241ee649ba3f203c7e8e75943702 dc508c48c68f878c82acaccdab3c60cfd19a2be8e7180c8e13b1b05901dda2e4 899295bb9a618c44318e03ec4369943890ebecfeb4abff1c7c280f730078a85f 64b11ff4399944978917546e6c6c6e02d9e907248cd8bf09c247db7ab21c11e1 4e6aa7e33d261c75e2d222674d7e33f74566d4735fae8a67a21629d3f60c9f09 ed3297133485ab7be94e19474691c80ddee1a2923cd1d88f192e772cac7061aa ff3df133baa88ea9432c7304cc4c6f636646c6cf5835baa5975dcabef8b4e753 4d32a353799f02762947edc4950c232b1009ffd6d43766bdb5929daae8106613 5aedd6d6d9f662b0ce591582fd4c651b4cd07214b56eae27ec02acb0747860d8 b15283474ab7e096ef37aab887dae89dd38b030771aca2638c6c5c5132f84a82 3bc238bbe8643eaf25a9281fdaefde939143c2a8ba1604248ae6d75e01e41a94 5d276a992c695aa7cb86b48dab10df199e41fe52a4a9451efcc9998f880ffa67 3cdf928f1aa0252246567db17457e3b9d57f547fc2b58c63f0100ca6bc4aa45b f3d7f31c662cd52692188d746894e2ee7a5d32b3d370f396412f9bebceb776e0 b3b2995b735692dba04c0a953a22e850b64f55048063f7217143a3b6d1897fa1 b7d235fc950d30d24f1f178644dca077c90f3c029571991dbcf1957c97b628dd 9be084547c58371f5475c13694438a43833192e043dfab92cc988b21ad9bebd1 e528e25a687a0da85426d3b404901cd6af73339b46e8eefe99bdcbdbceb8d048 94ce4c9baac5d8786f97403b8083004e4cff8f9005c34f15e908a9dd5c1c72aa 0d4535fe03fec7084c35a0f9bc12bde5b3bddb08afb15051832042b32329582e efc106fbc52700ffa96a2510dde25f76f32bbf3b9e61aba18cc753b795697a61 71398c2f10b9c93f4fa61252bd6e65167c6f3fb1b0be1794da9c1280fff67efb a587faf299024b25f1e14309ed0dc5194832e3e82b5d86617281021a5dc23727 c1f4cc548184289124bd99b4604f375020f3327737d85c17e5b5248792817626 a596c11426c2658768cb7be3e2941471021be703635e4947dcce631850981ca5 a795fe3a977c2b296be158b329c93efe040ce841ffb9fb94a2307c0201e8cbb0 321d4a61dc4cc4881227e641f01ae56499156e3e0e64f4524c0c0bfadfdfbb99 22deb923f01bd7d1ed5fd847cdbdb316191805cb886a52cf955a616fb13220c5 0410f572c873547e86a6fda4fb27af9c25938620c50e4707e7ec58fa3ab1ac85 7ba558120932ed176c3ec3eacd8d69bdb74ceeb38414df2ee2bd5bf909108284 f40ae3b70338e881dd919afba3c219f4ef1c2c07a0e6c9aa829d866fb87e3328 dc135a8fd03668a5b0ebe25dc7f0ce9ee5dc876c15ef5d7a1052d1463f1f2078 ec399bbcb3a024be6c0c876ed2551131735533db43775097dbcdfd060c18f5a9 68be827b1bfd23f2cad58da0ce4a9fad1aeb01abfd4b79325ba821bf036e51ab 5a7bdac68ac90864d14c438b1875e56ed5aff100ac7bd0d08b4b80bc0b96b976 aa1efe9eb3f4f4f83a9c845a13ca6f0f39ec86ac3c3e22fae2addf5b00be72a0 cd501ae31f17a4602f826ae1986f0bc7a9992c28137f85f33f8e80c0a2910c9c b78922e9b4d0688ce2e44309e47475d8e2970d652ba631d6b31a069312bedfb3 a7f7f14bcff65c40f57420ca9b904fb8bdcdb9927977b629b6cf5125fc5d1b5d 7673d5baf7c5f4b646e6423a9071abf9c7166fc2deae39d4f2cb5512bbb27ae1 02249e386dd1952f7dfd491684306943aeeeb999b055c3c29f33ad40e4555327 23b9f09a78f20441fcccc4e933b6e18b8f60f2d52cf1472db8164ea8875ea9d9 d01091124504f2af5435d4dfdc433f614403f4cfb8bbdd2a9b3687dfa4d62258 55af1ff4445e352b9918a30e919373a9689e4a0472f3c86f224439be0ab2e8d2 19ea0600268611c166a9d45a4519ecb23028f84bae555ea1b9e3ccb7a4a9ad38 7b58b9f421863252b6852f510b27a3af4832e68f326b517f3de67e55d2a6c71c ed5db97417d89a7306f2264ec160b75cbc7020172836a1098a11a487fecb68a1 545a79deb11876ec78913ed2089af0d964e864f8ae091ac2837ffae712586bc4 075041de2bea4ce363b23f66887bf112f10df6692dbeb5e9462cc29ae657e257 c0486cd39b395d33fec0dc8824bd29b6c576a8fd472e8b03d4da39731c72cf7f fb88faac56fb263da5034062726d4d5cfb8acf5e59954942bd29f10b97e2d36f 5aac94fc82735034ad1a372a6faa25b6e7416d225132a938027a1300f460ef9e 0a10c778910cea22d929ea8d6f9c816905ab65b155b0fa6ff81c9bdcf5782dba 5013e5ae35beca79e3ec6a9ebbbaa31f6d66f853951c77e263533019b703ed7d cd04891dfca466890afb3a1a6c55b4ceac826f55af6ee99dbe7d8e9298c87a86 e95f5f320fc05e035ff2b5fc9a34038c7a235d2a07f8a22e077fe6520f9e7fe6 7cd3042f2133681cc22f4b6781d90fd76571b5e6a87c6089641ee862f92ed36f f4ee768da83625f280c6731872a2364b79885f6e6e64fbcf2e44ea30cd28b006 58552333713a1584c2708d47eed32beb13f366d5d46ab4b66166020aca7b6011 4f377393b5d2def5c7aceb9bd784267d085dd60970722434b12001aa6d173d2f e660a03723bddc8a0bb445fc0e615a0a70ad77f8a9f363ce47fcc1f70c1dfddc 8ffca5b58a48a9b384bdb77ed096d6353ec3a01706679eaa7ffc907ffd05b629 a99a8facf8c995dc7a3b9b31184c280cd06987d0c39db9fc35144f074fee99fd 7157e9dabc2dc3a96d8ab0684d402fa8ed5f74fc11cc9eee3932889e6d0fe50d6e4dd189636390d074ecbabcdde1d3d803843443c45723bf6af813a10cda1602076c63e3cca76d51de64a29c2e6aa7bf7a4b34016acbdd340d4aa24e45c9870cb09f3b54534783cb7e5e5f17a368130c2329a555a1e71382ee36fa7d2814dc04d90aeacd7a050772addc56df426850784230d73e40f117ff58805ffdeedc660abb8c429f0343afa556ddc79e364f4fc55e250fa9bb18b9a6937d2fcba88a0c06c915ffa1b5aff3668c809b49283b2dcbe5a8393fff925d6a781eabaacf531a0f3837edce7a0fd53d035ec276ec25303bd6d75a25d8a180649b84610f9204300390a68ea93f0407fb6754b39c608a3926c1b3da85344527831fc1742ef8b03404ece0df2062d96889619a0450afafa52bb652188fd00ee15e8ec6b4e8499b92031eb13f31584f9497d2801124367666618a2d324070196c4319350b94e43bea0da63bcf9403c4d45c57e6e3cb6f3c279f33ee443feaaca78eefa444ac5e3cbf007b46693eb5b4039157a564186037692063098ffd6266798f371a6a015ccae50e87ec5c0bef7c412e5b2cd4fa93d31f889dac64601eb880b0df41f7868ad3d2009404efd37a0ff34539455287dd135f11c1d5cdc93bc7b0dd7627e87efb6abe07d9b8f4db435a35319d1d22b2d0e9b3d9e2e3dde8225428484e31eb7e58093b04a3ee1e5e7ed533b1c8b8f4fa895e0f884498d45424232b4446742181cdcb230619bdfd8b739bc9a83d3034dfb0601eb04b564329afba9b7a348d3b5582d4aa0a3de781e384f6a9a7c459d6ec90f3416ea8d63f915183693ad712a506e301d803a1a945f8617dc13ed8b6e01e4c7c42bddad9a908cdb7d44e52cb4c8313a20f0bd634a4857e6b7fdce5bdbff75fd31724467c3d70de46a3f0fa642d326f179506cbaab3a0860a2c16ba707dfca2ad9bff44c224d9408db27595e827b870c6340b1c15f9d02007698ef9e9b6d4367daa21b7d6b2d429b3e051f5974ccf73ac5e0a7a9a8d3cbf5b82cab13d10db360b128f617e98d56db3cfee46e1c2781fb139036da8e3c97f3ef2d0c7554d566f32ebb3a1c8cd4969a445269a49f886b3bb1a0c1e9c1b40273109dd457587c9b5d48d2725e0b926b2c9e1907f3c37973affd004af29d1dc5fe136230f278b8455843c64b68b79ed48d212f95b4007eba5b80e03eeafc84515406c4076f09fad4b39052510b7f14745e53710452b7bdb8c7b4e0c5eca3fb22688ad19744e0338a3ca395b8053a6881e4f15d300469d9367c444016b0495c183757e4081c091fa23c7b985bbce61fd9b287a2800635d7cf586610f18887da24593899805b7930193e1e5f347280cab3cde43229fe7e4db9a6103047c8ffb108f8499adde519a4d55180cbff08b559a7486b8e9ea7aa72851606e009d3c2e8535caa1a733289c5e3fd67168ff04fa1b070c0c98ac611f047a88fd0f1966f603346ace4a637e16f79cfa8d82ba25c4725fce2d9f76ac7362ffe393065c3821bd0a6ba85ced3b4e1d56c14f61ea0d229dc72339223c440b86f4a31d0338c42fe3ac4eb91289a3ec457c81a3fc32aada2bffd743aab04f861fdd50ca002750269a1300e6f5297811d504e0c09bda1cefeff9a254e5b510b266a9b51607b0601a0252d99ae694c7a4191ad476f539ed07b45c7f124a0ab8428e1b8d31002e1a897d9d167a65be598c5431e3e40c6a9c12d59964d1397d2ed3a9f949420e21aa4b46724e7acd97105d08013bba281ba6f3fc74a9317ae2018186f7d84400aff87dcf65c22395575f4fcfeca0024e77a6ec42260ef7e7b1dd085fd6a4e90ec3126d8a43c6680ec0b75fff6cc6d99cead32912519a777c3344528c4ffa510b977cd378d732ef3672acdab7e37a16f48a734bfd061276ea689ce4f808ec140b981d0bdfc69bc437638887360fe8e3427ffe6cf63e3bf1a71f45c31b20948e07d7cc2d92c1a2b1ce1323b867d7e6bdf5d6c93c428a3f9fd76c44528b320006064867ab0f5720206f06f50d68cfa62a9ff4e882263d05a56e6233fb9d4336e60cc327a52dac9c4e194452960982d83170ae0a0ccb058accf68fff1a9c155161094852b08d63b90d611d6424f7b40e91dc4037d08f6ecf0cd407e525ad564d630cb14cc6e4532ac6d77a3481ebe73c2559dc3c6baee5490541969bb23ac11d4e0dbe562c6f5eaa0be2460adee023a72c4ec87bac043f5a88c5771927ffa73db20e27ccb433329672c484789e7f385a42496ea5971d242c0cd6a368388438084808602e5e8b68f64b3caa40271ec6fa6cfdfb910a95f3386eded43c53e65ff5e60a4eb89069b733a5ae33a29f683908bbdd37bc6b0462cc3c823189db844362e507aad89246fab70ae9a2d4f60ce057fc1f0c4a4898fa443e0e5912032645485705e35887dbeb0ab002e09325f3cc45e62d667ab1aa0b8c01eae4d0912e0a908100127a04a4d884ac2b2dc417ea3d36ff3963e9ca5f72d2316fca8983f4f1654901c5d8883fc185a77f6b0a12c3f20d8931e0b58993477fee5031264da29900c1000ce810e1a51716b3532ddecb5533eef59d44977b6bfbcda9f670d1407570f60c1851308412bdcc313169304a0abac0973d0b5b560e8c514009a0f4bdfc057205695ef2d44933a3fc08b59246179451fe8872913118052801c15c9afe085f290f152295bb2a27eb34771f49218553bdae42f4a023df98cc7d095dfc16634a9d049d3729409eae177211bb4e8cbd4cc576ee45523b8627606e680d76c2336cd100daee9ce59f54ddbe379a44ebd7bc85f4efe06bd2da07e5b34ad33a03025ee400e8905328ed6251ff7ad9278f2410e4e982e6bc3f219a11436dfe662a37109b0aea84dfc6efbaccfb5187571bdf1d0e57a3e176ef0d46d4c0ce74561be987a90f6cf6f4ccb1fd42dc760f5ec88bf2225bfe68b0610f2610803e75057b40ec2b0369ba871d89fa07471e4baa5a62d2cf88f677d4f75dd1223f8fe7ec189e297f07dc73b23ec96adde15f80b44fc371155a10e6f890668bff33bf92ebd98a9cbc0b680b58dbd922eff7baf54b2a013fed31470b8209a586baa4f33155b2e37b4407d040c4e05339e02deb9b3abf3bbf7920b6c159a75bfd85f9569a293547ce1a0f02656f65d6a6fdeb2251f5997c71324ffffcaba19ee3d33d784ecd2e93cabf0acb8f37926a80ae580e3710493b717d36a1ae8982af4c5d45a5f58ddf117bf60dcd1cd376f6aa66375c88bb918a6b1e4637270bfa0e1f6a8eb34788a2ce1b1004b2d484efa89ed2dec75c738c7009f00e5172e6f5b7f2a21edc84fad8bdbf5a02e94c83585359032564a44115e575c105be9ccc7dfc97d5466b435826a8f77d0f1e3baf2134d7814799cda5b803dde6c50e3031a0fafd2f5aa30d61bf9557030998ec58c73f1b48d6a62534446c8213c964be4c60c4a89862d0a3add996c5e50784e5c14226714c04581306e5683d89e1323914b71e96c803d95090c2986bd40cbef91a19e645b077e5fdfd6b2a6870aa5469453d353a4a676dffcdbab23f1b037b7f121fef503ef34a40ca623833d4f363e1dc4f7455b0a94dae6e87fb4b58092f921617b1eb80b4acf14b3dc846afa64ff6fdfc22279d501cc01988a54dfa0369645c11001cb4ceefa05c0f2b224c74af31a717ab917b368f2320e0630d93086bf65a22c1c449a1b3f017d823e31c49464849e706b089e69c7e117a96781b01f176bf19832bbb8048edba41dc8332cdfaaafe997d61909443083de27cbf8309a570e83963b1d0270aa59e9825b8716fad8a9407578b543459788531c59945042c03b02e41310294a3c293a31aa7bc333065093664b94beec8fac88368fa9e007f2c7a6ea3d54424ad7b3d788da470d8c9005743c480a1fe26045bcb49930e0366b982dd7cce5344e5fb869de7c149c6d6217e78b41d765b8f698f73d0c90001c24f9037c0fda626c6c6babd0eb98e518f9f0acc8ca9e43ccf8f18414a8f8604f6deae5abf9b5b60588bc2f3193473a45d0626200943463191f251270f4b310698ef5ef81c2942f8fba12362b18f1eefe743cbf7314920e4a21714365a45480599bc145818cb472b7bbd74c0cea223ffd90ec7a002a8358ac948caa17a88870944abb72b73715c337c584212ac6a7b6d5432b3d07b8715b432821b7033166d04cf059ef8dc28755b380a276d2dff0cf580822aeb93aaf6a0cc61fbffb75466084bf0d2cf1ed50b85ab08c9f2f23d799d7ae0eb0a46369d3eda139cf022a36f08e61c826c5c7dccf4eafbc7863c52dc188f076134c96f4b30f165ce75ed52ed0eeecaf0523d89760748151fce2f831b17bf9fb0528566dfbbd0e61c77330bfc03687e63ea47465967d514a649258b4b454b178367ed4a2b8ec69c173ffbc7e3062f236840799d153e0883ee80496fd5ad79c2d13a22a15d69128eded79e69910d8b009f3f99509998d1b55055a62054c57339018ea8a5eab886527e2ca0e9d10dce071a81214a14dfd3d37df00517339e7d87cbff50bb753bfec5a2db2a05e905f71c47519c285a3add49bb44d59e7fa231b511ececf016706cf8ab03dd013709967d7e5689d1cdca7368e2fb3c7a1a81437c85b5d57adf9ed4eee95cf6a26c0af3f39d289a4dfd43945b624f870f66b3879d219556c0c432945d2681fb940506d57fdd4ff723101c7c304c6d1e6e4ae2f1d0b8712000b10e35eaac2794f1080309c10ac66a799b83024cf8f1ce7e5381d99bc546393c4edbcf883c29449849090e6593ee30d699c61a329f5680d2ada50d9da6a76e7f4f9cdf3f655181b23a0a1f5a65e9e03413958eec02aaacaa89fb042a6e1c937c4b53e7b9fbde9de1ae0e1d08ac133bfc6fc27cfb60f79f74c63a1852d6550d7ecfef5fef4af086367000691ec00cb60227e5d7156ac8c042728d0c4cab953c9e571bea872fd2be3b6b03a44d5931c4b368fb24241edca7bc3f764059d336619f5ca557631a95d4459a00f02cf6314ae52b53bb47d6eeafafaa4df3054da4a3f836af76b1104ca5f86106eeafbde185b468d1079da2c9565781567b498f2553f92316fbb0346600c5c10a7f91443189596447b829ae00054bca88d2f9650c57a9cf7f01a55c2f04f5050c876e097bb2978c495bbf362b43c9fd2f1833ac84a2410a71f57c272b64b1850e6ea81951a76748161130cdba2a1efb6ca6c8e5a14795dd48c62e66d0b9716f09918d1f55df12942be488116697a1d8709ea59dc2e0b368bc3d71dfc55bae7a005a0323adeba0bef30648d9b0f8d089e51a45f590767043485e35efbcce1ae10cdd8a379658c3ddd559cba34605a6df8114110105492fd170c61d4e0b10b489004590211dac2795bfbaf012b63d4e3c64d36a5b1e155a793ca4f33ccac0345d0eacd986885868fee34f5cc65bc2837e24253bf302b048ad58dcb07373d4ca0e0567e44f731adc6ad85079604f0deb16b8427a22164930ea0f4d7cc6ffc6c2fa0106e1393c6446d9d0275c072974fa74273c0819b089153e6a980b8e127a31d80fbb6c8934a1a1ef045e6fec61a57d7640bb1b4c5c41273d68eb9feb75178f4f0181ba2f35d384d66e2a4e9e6d7d9990588bc5e1ea68aff94b14198ffd6f57a50840cbb75263c7ea9ccc432bb1dfdb9a265b51232984df41f3f0ceb5b3d2318008361b91028e743ebf97505e7e5e35f458370ddc1f10a9d7a4c372d9b13c3edb0279d6140603869e1cbcbfabefa902d1a8df693dff5129dd92e315d906e2862109b520cd0b81d20ae778756306774899266d6d7ec3d702f9f54c9814ccd190b905514897771adc8e97c90270e278bc3cd37ac936a4500076ce15c81a3697c7000dc1518252500cfe0fbc0df6fbbc5a4a3d43881ba9ba0ffc5aa9aed5244f8e8604161c9d3ed92661f400c5fc8fce4f3668b6752531a1fe93c03d492438cb4bba0e33c865f75ab1c7510ca3750b96f37041ae29d6df130cf12c3f6ba66b9b94c10c4ec3a6c00e92ab3c65f8944dfade6334f84000fca2355cdef42e560870a5920ad261337bfbf5506b471ec19d8ccd7e57c7007d492cc661b091836a8f36efa1005c3d305f275963078f3126e20062819d0e653f4a253a62f28609d9f46354db0b463d196ab7706d925490f2cb5cc8257f1ad80cf09493448cde237c6cf4d066099e294c0f0648df23c4b5d8921b4d43a3648c7122a82d08749f7b395e3f8efd0ef4967699683f7ad3bd4c2f3b4399f2d7f8434a6823df86f9b8b00ea0b86f8b09536200fa84b418203f2a7bed55129dcccee567bff00ad32fc5e65896cfa391006c5f00205ab6ad0652f2026bcee4a38ba774b95b7693b50f4ccc3cb97febb4074fb81d1a2a5f0595be8b21258f1ef131531e666da6e965f40c814d6b218b8b03de04176dfb4704e6b38fe9920efd1210cb606f0f4a6762876b25483b654a9d0bb2ee515644d9b863e82fa31b85f8f4f0988014b0566bd7a12381763e7f458a0c4b0112c33446bc84bbff5f61db722a6079ffb1d671e2a79349b3dd0ee5d400000656638fb3907e3f4e8fd3c684c3db500cda6a98bb295d039d474ffdce3cdf0aaee19e330b3e99babd212287b6fbeb3899a66fb204f74d14fe9aed221c7af9096cfa0060aa51f08bf746515249952bd72da4dddb33ac28743513b9dab2ff690f19b51f2010eda924ce15a0d85518df27ba1a4487b348bf09dcbeb9213b40de055721a0a7b8372c840b32e8cd1276afb6f2dd219d404dcd5fd023baf7f350ab0a68d150e2bdb250fb27f72e552cc2d2232cdacdd03967a81f993c62fc91b5870dff3c77ef3f7adf9e54072a7ef0cb475dbfb7b369696580005799b4e4e186ae0df70ab9f13415d3bfa15d39c576a9b1a26f6e9c2186e44e78823c6f5cda08e3034ba25be7dfd23b0be2f474bcfbe0ae5dee941bb4838ebe217674c0749c6c520db45ec311e12a0d91f9bc32356f8150bbf61e5ecaf59a87dd6d671c91b2421e005c19ba74d52678e0906dd780e800ca49db8160e5d6de85a2b92bdcdd0035ec0fa73d69b01539e7c720ab8f11b3760abd4dc43e22a74caf1d9db8ea4f49147407abad20ef3c4f377bbb8b5f3bea53b768c84e47cee5243b5141cc8d66ca740a0dc7b516abeb915b6d7d16bc921085f196feda7eb9f463bb555bf94395ec92650c8c884239b327a41709ae505c9937ed7e37711d6b5639d446a2700794058c9d0174ad8a5fab4ccaa9db3be093fbebdeae44e1d80df60eefd652da1fd6dfe23e07f4b7d2621c0d864d69ef10630783baec7acaa7accdc742aa311fda99c8575900b4d2cdfd2d56ead4f0d55b369c6b3013919a88c098505e0c832a77810d933d02c7c951bf290a8a48792f2b87baa3aacecf574c295df2b3226d04e8144f5c060253d82beda859885c8247785346d6256b77ac65bfcca957c46499866d7200aa0a7d0b1931e6ddad64b6db071144ae0f573f4bb73018607e61e07fea19c828940751c3ed099205d14dcf65f5e99fff9cbf592115cf98a5c9d24128215ed7ba7f01a4e90c05f442e2509029c5b5dbe5832d369eaf94de1a095f80b3c8780f0db45072845a83bc4b5f1f36e154e530c5f798e0cd276c30ac4f06364b694acb70f207617e35223dee9a1a99318267616e3b8334f8b9a2a3d1c6f817de402ad5625908a1ea5487ed7f56dffe4bb508e4b7c74a3ecacf285c6af4bbb52a9fcab7c3f4023d3ca113ae3dcec59687b407ef32eb3eb83c201a890cf92a478aed7ddf5e13085f0c45053ae82cbb24cab2f40c29b11a2ef17be11c48cb8eb78d7e415ee8030321097f40e60aa325b078a1c96521cb502716a8b9d5d3902498a46136e9ba40084b83e0ddbe3ca0d763baced37b24fe65caa6687e292e435ce0189b480c98640a5f44b966956d0cd4e851314b8d493f56284c0a4c27a7b780f03cc0c38c02fa0758cd8f02dfddfcb0ce83862a3f61993cead971e4b6c30786552a87707a53390e62cd55ad454e2e9330994a3a508324fbb5a9aac5f871e2fb9bf01f7fe1904a0e5c741806481322dc21db9e938f5cd364b824e901a39b60173844c66c7dca12082c9fb2ba57074aab582aaaa87c3ba3634dc72857e454d059992aacccc2a9ec0d47643b793795ecc8fdf6eeb529c2f6eec8fad6a45bd2b0427e63d918e2857a05be9708e9b902e7c7b56c87d0bc518894f0e3bf6a34f34493490f1483411fb505df0903a578601f9084e143620dc86e82c146f41b21a666d58464b21273545e02dba6d5a98915d5a2384769e2dc10ba93ba8b2249a00fbf4ae49ea46d129d780dca9a34f7ab9d756bc24babd4b9447c5df6c7fc8484fceef72766594e70bd7506d9b7f32bd762cd8826c2735754a93152247c7d5d9ab70797e990ef252789fb0a05f52f87c67bfa0aa820169155474b99405ad66a87ab050f8adec22d9e1700002bbd072e3736fbff80e63eae6d2bb40058f30441cb761755a021f9bb1149fc08d5649ae312a1c62fad11282ef6f18cf01e9f2b580651efac0aca0c98a550320dd696ef5614f9c682cb44ff69c25791a230a83d43b001ab2744e0b1ffc354fa02c251e41a18f25aca36360cc48e9b06a165a9180e895256cac2c3d3af4f2ab20a8ba2d10a4c1c209c4b4c714ff40c09fa1f19401727e66a77082dc82d14ca4e0383108654248085820e4409c115078a5526fd5858099b78146ed91bdbc3d03a00e857065e525714c4c67bc21edbae6fa974aa0b700cf8a42d98f24e5bb93f9e0eba0c45861134dddd94e73add2867d6d0f0a82dc504da1c89f4ae8e19fbec8a09700df8db3db12688fc41defd1066b8d20ee5773ff3d5474af9281b42f194a405b5f83966b42017176f001b5666df2b37a60032df1f03f2ac26e4860d3614380620d8bb5247e4749333073dd938b9238c851f65dfa2fd83b2a788ce52fe4ab80267ce662fea19ce35dedc8381e67a3200d4ce4d7c025c8b43e006395b440b7e0ea1e9c23ba5ca048e228690ed04d015f6a789a82a137d5fe113f4e39fbcf7d00b7352a17edf00fbfd24e095a2a87a1c21db8b56eb87e71c90f857576f1e540f054230a67095929e06aeaef278fd4b6b8acf24a5c64bd8037b42ea9abfc24e460b513bff248fb863e52c73ed00a2043619c2ea70b84523655bf7cb97e2413ec304cfacf79225a1ecd810b46a71e1344aa4b167526019884c6799cda9f59b65b503b0b26304f4106576abc9617e69c3d9b7b984b42ddf7e4147c0e32426620b380462f78e65e482a378f4117eadb6ec51275072400852af21f787ff394390b7c40f8bca2d56df2bff913ec824b5e9e6cbc09b4bfb4e377e97b5dde8bd8d5b894709d23b53804226e920f19f0acebef851c927e5152893cd88420662456b0c3e4a036a5e252ce19b9183defe2a95bb07f65d148a1e031ad2381038c5ea3a5ebc2d05edfb48dd3aa5caba8b77ebb2bd77caef679a13cd4b6235cb8401caeb0b88ce03f270b251914692bd9d869dc7398b7c2097ff511a398b5601aab56e21ef21460ff092361cd5840d91943bc6586fcec4969b2157143b5d64ed679608d860152f0eb48230bf643dfb8f0d8714e41bbe40535791aecfb4ff4803019cea1f43f0c20342752dc3a201186cc322966869639cf0ce97bd888c04ecadd96971c6b269aa08d58bb63373ac67a5b9297bb402aac926e714426815fef165743c6a4cb90135033a44a31e4dd30a64522f7bc04438b6f20173676db291e0c7e6b75e9f62ccd30695dd339bf24dfcfbd90fd8c81afcd0ce1042aaf587977a2188508a3f9d703702d1168d4d198cc06d0e9d974cc70f77b8baf8df8cccdad54be1443b7dd3d2e30c2d62e78f64cd8783fb7ca5550b8e2208ab1c914a13986c0f1092fc0cc240bd0abc0aeb1c2a997f67284c7d7d455962ad982d21b7747b1666d6019ca8301de20c3e622da47aa2d1e93cd6dcfc3e0a7de2cd5485c6f06664e5a6c2c14796089f0188de522f355956bfb05cae110c9a256426ddcd347867445865d2f824dc5b340cea6094d528ed3265e5ef4c05c1a80c45f5da2fd37e255b30f035f00a110f7700061f04e121c75d8d183d94caed5c029f19dd5f84d6ed0a72fbe5d7037f6d170ceb28f50e04167d0904b8b1ed3ba491bd3fc988513eeb84a395d3a567f4e8e103df1dca68d23a49180a145be723c8fe875b0d0322b196ac7493cfe56f01a5980230b3ffe5c1e9db7b232dda065dfba9062c5b91335deef294a3c6eda633f9510208d29215f203e7f1574642bc03ed5cf95b1a9a0a36475824dbdfcb64907a9d07cdd1c0cd17cb87a291650f84c7f5f16db7384700f973a000661ee565d1a73f0f533161b303d563ecce8e4c7068f77988df2293ca95cb7d95c94a438ea5cb7d0d false -check_ring_signature 85f70340b32baec9875a12bf6f31cfd7ec5d55ba6237cba083ebbc42e2f69a5f 63bb777d231e415c66188f7c5764c0933391b5b14875be90f799a84efcc66d73 23 1744e7d9df32c21e03ed82c87e7c727f86b89e7ccee38ea199dfe2050e373746 884c63be4670bc133dcfa7d61acf996c04173332e9ca9ea14d23323dbd31eff3 cc60c70c8082ab3c52bc706437670b97480c14f14cf9b3c0c313ec044b8fc93e 069673ad3b8e35639b59e8458fdb12c519258796198f24afb2cc563b87b8e8d3 c17c9b114995d18894a3b2e83ba6c78b37e24e48045b54f7d27c94102f28053e 09cc12a86e45bab0e6aded0f5b8c1aa83a1a2940d396b3b59efcdc0b90875f8d 804aa2cba46c8514b6223275b25d48d5ddaa6da34acea2dd94820e610001b7cd f2ea8364d890ba2cc78583602ca6be716e4011f38db99bf12a4d66b58662cc2f af6411b2a9f9ae6afa1bcf21545dc6666a43026cead9818d8fd85e72e87a7b9a a227de8d685a0255e60ce0952d0387f6c966f3e2d0424ccc6b32a13b0baeb171 c88128bddfebc048715ae4e15b1ec20909d665937f72123678c52045599da20d 4e7d8735dd827fc44c83aaee9bfa702c2e20e8c4a69d657457bf3800d7c069cd 95ffdd0afb599ef94a94be26f95219d2c78487aa24bfbd658039dee1434341d7 77eb3f07100b593afeb6875d70319bdb411a0c40631d70fb335802b4144d5b9f c419a82130fa11a12931f4e60123f5b193c893205d95f165d411e11823e0b9a9 52ea9a07b550952b9a74bb339e74be307fca33821263f5f547968cecd264a7b2 c12e213880efccbd4ebc3308f894cb348a6a01bd9d3f3dfdb8bb4936f6360844 75769920cd9f96eb68f1fac538bbf86d88ba87eb281b288d3c1f7e9fd0380426 fc0abf89e762e385fdbc321389b9aa57eb48fbf32e2ed023bc547081f901a0b1 83a947ecbb61283b8a46aa72a48f9cc34ee9de9f54a6484166db3a9cebda1e28 ab316122d46b97686e5b5cf864027fceb6f9a8971898cd4ae125c98ccf3dad42 a2b941f8c6b1bd3a227cddefc4b0095801b0a2e0453dc9e402203fe5115848fc 2d076f4236e6666f2fe6f9593235f1bda50cde35fc1e20393f2fb2cc22c5b99b 90bf47f873a88ca3007c741942b96ccc6b3fde458a46ab1d0355743521ad740523ed8eb584f01ede868cc9f31ca694bb306b1b4a89abd2a706bce131b39b470a1d99371020c01db6dbcde7f336cb60248c0e5753e614bb0750b5bc8e91725f09587b478ae0aff578e63d336c4e23b0984f4ce71ece0f5c5917beecb9828a870a7cc4224cd48c9dd50a44e3359664230e7ed19f8ec5a6b2a91519cbd0c563660247560d59fffcddc4e127885f143c41d362d5b43cbcfc22e993f40a07562b9608ddbca1fe5e1b560c29cdd95c5ff3d1a5d40c9968a96d215ac43804285e1ee8069fd21cb779b293a856e7f07d74380789468e218b0ca41ee918f87ae8d0a69c00b9957e6554d9aa146618048ad2823f6bdbcdfbdc1108d23ce856bde3626c780d355608ff07fa67baee48c6cf5c9e16d1eb54d4e2edb1d3cf86055f935ff53f044eb3d358b3f8019a8dd19c7d98313b47ffaf4b4959c996cb9774fe51eb7aa004e92cabcf8870e7c60a40e94a14e304e598378490552f8be341a485aaa009020ee7e0783a16fb0a291afbf44b11b2dbf6a176a3ca28ef8ee57047a1fe02f0a60d1be1cd66fc5b4cd915bb3f3d969b9d2bcccc0e168ab87d93b1d95e4ac4275e0224585c5ddd32ecc85a4d451ccbd0e3d215e17c928fbc68fe58a3b567c5e4c606fd9f92709ef9c5086f98f6e74465debc7f54b7af142a36cbcf83cff425ddf506183092570bd0409d9dd538b5328fdbb5e8144d31a5c93c6e01fed5fdf297790bd740cf8aa738acc092edae58945dfd5c4c7fb58d1f4b23d135433320e7dd15053b53d53ee6cc29976a5f815d281d0f1687ed4b05689039d57d739c77b3947006b9675800cf3873aeb1ef7de63dbd2212fa4311a36658216ccf6ab497f9186802f90d25c17d3c5ec0ef22cccb0875c5d37ffb22a7f51d7b12e5bac815e151560e16988879aaf409484100079f82124e89770c7e394436ae072112afedd7f73e0e8e0aa41fb48da9506f6cbe1ed05b510aa0ff5d44782205272691867b717341068fdac5e2a81405efaa24e668b22f04a02aa7b71d49e8f309143719a215a56f07de231fedc47d7ee021a838624b12f48549142402329e765b7cdbaf056b7b6f0eeea8e2ec9fb1576755f7acda2419a109243da89046c6670a70b7880637b3b8053258f340136a4aba78ec84581ff0599a365314e930a7f0aafc7f883557216e01ff7eff365d768209bf51aefbd41d29dd33bf3568d28ce35a2b936148b5316a0b60a3350424597ece418eb711d067017697e6b401ec0460a521b7d4bd70065802aacc64ca9d27cfc671998e8c279abc9d2cdd6b3555a98cf1d8036e3a2413e20e1f0f92494ffb08b4830dc593a17f49409e42a7ec736a2141008c7c812ed5060051e8ab83546f0dbc4997b80d1e015bb55ac9403b84c105fbba26909245cfda052e0a62496914c15af53b97f0e65e684e52d3074244237b09c91db72eb6376f0abf663cac83addd8201db5cdb0b649dfa17cb7d82462ee3a730dc64710f89ca096912fad3a9dd79c88c31d3b839410d6ee70d73d791a02fcecd3dc2b9facd570155369aaee8c644f16b3ba55c189515e125470b377f618100a0c8eb1a24f08009900feb84c0cc593d45b713f4a44c29de9fe193b2efba578334764ea4077e50020d9a1d6bf7c199e378834c2fe61904b384780c948ca7a12183dbbe1fdbeb710d8b0063ad4ed48d2e0391931640be0b42ded76680b2683c7bd5956f5aab729709306ff53182773d7bcde058a221bbd79b81d23bdbb4620275ddc904fb98626c0bd3eaf5e21fe33d0cea822560d9a1e672f9035413e6ba83e2097a6192f237ec0b5bc7f5e0b079685d32e6e4f3f692647c9259d01480c07e04ab53e785ab0a8d0cb881a5c4de52480215066eb6f7825ec1d94bd970986090c10bf20e2a3d99180131ea0a5641d2413e76cfaa5afb035a7f14fee1285afb1df7d404cbfffce33d0539ed007a0ddcea2c78ac5fb3aba6ac0f95c88ad00fadc4d35167afc159bc8803d3737fa701dff5b4d82970827f2bdb6e2dac616912e79750f106c426dcce8e08 false -check_ring_signature dec64228de78ddb98b08269e1032d9ced99caf6c18f5739e516d46434c515eb2 91ed717fb52629182d8bcebad7c4af93392449032768d706456141998fc2d5b0 4 f7d550cc5ff8f9e09460b9c4da9b683fcfd88e08124e13b3d63a2eef6d7ac1fc c9466047742865abb0bd18cfd3093c079d58fde63f74eb338fd95a70a3555645 122602fbe04c4e1ed7e5560c72c7473fa3a0070dfbb9baa06500db1fd317ed8c d07247275fe85ac7de1e72c8fe00208bf116797890fe050c8ddf68a77d0583c9 51d53f82247286682619edb64a6f3f6d5aa66e13ec44b8978872e24fe154c504fc02aa33da1c6bab642370af9965e658c6659e7b61fa0195567b09d18d1238074a7426a5487822e917ba9085134e481f73e6ca3e8490232985464d8c31a38b023980b2ebbcb6bced8788b6feef025f639bd8b61de532190bc2f8187f6204720da7451567eac3c130e802a148323db62fba5bbaa400796776dcd25935b8ebb607741529500302d20304cc37e8e11b5aaeee963b0b08cae002b50933577cf06b083dbddc1297f69b88f0106893ec53a542d8f7f4c1c50c00b8e4bf42e094d5a900a498ceba2b2e27598d0893bdab7f63f64d42e3292a0221c9335903a8f218ef0d true -check_ring_signature e96b6ffe89cb7fa8989b93e892d2435dd49abe5462416ca38068977e3922a86e 4ff21b9657dd09d98ddb1f0cd9a329bcc44572b98ff45007dbf07172dc55a7d7 2 d38a3ef05acc0fb0cc04d02fbd557f7654e549a4eef100c6dea15e5410b78770 83f6f8f2c55907c130d26bd9f25eeb4516f5e7fb6b405b902d3d59bcb80d1490 c22971c3badb85d54a5a1c34dee528429f0e96e6efec86c5a2349aa71a3bec0f0f4701fe79ef04d14f41faa120c49bf14aaac037b29366b625f9ca87de534805190012670dfa2494822a98c3627d30b36bca5efb8f93ddcb24c406581265ca0232c30b3188a2b8098202e432e0301a9640a1b42400253964b8ba6cca4568740a false -check_ring_signature 6746bbd3b121d924bb6b72b5dbc2af59f67c656f6edd09ddca4bcaff115a4f99 256596bb6027811b5d6f630ebd489b337a42ff16540ff43539d8a1dfdadd14d2 22 72bcba5bc321c27e2fd2c1991eba7b7ab5f69f900db030a667dbba1cb66cf9b3 94f5a8ea048b0965fd1d35615706a78e690a8caab42a2b3e0ac388212aaab592 c49166a2152b0249109048060592728a73a0376eaa8cf92f6d6a56637c3f1381 df674285bb6c56018f38ddac7370260c4c8644377fe3c594caeff22c7f2481aa 6d8977ed46b1fc446fbb010654f2bc47f7c6aae73c65e2e406822030cb6a329a be83caaf9d1124555ea2e58791b3e1675a6e8309174dd084a86ea14c963d1075 ad74a78a35740f8ea0caf5ba3cc6134a598bfa192822658f88b5009944f3ea1e b2bbec5079ac8c618ec608c82e536b07d39dada0c0c2f74dfd2ea88f9221a69e c522623a02b6e5bb695eb638c6adc806309233cec56abb83c4e3db899c89107e e3748402252fbf31de87774abb2c09bbe0630b6add42171ff52948847a6f13f9 1387230337e61b4e047a2f0c93a9e7bb7b2250536a0a192399adaf3e3b49e343 2d632ee65bf5466352fada6e97708c576d70d99c872deff1fb3ca51f51f2f72c 3e55a856ebb0b0c95aa4b8a5f1983f040e81aa2853221a7a79600d94454ffd2d c29df508c3a8e2864e85b97400ad9e60b15fccb5384b8085e8fa5cdd85f3d58f fb67ceb994dda59db670b2c5d518c683abdacb23629f02be4039a036e1e6a94d 9b3dc0c09d9f459d0a4e8987a168848f384c1113a925005470c0119dba298a50 10b1660a1df0c4ee8a8765b511a19305392c40385cff6354a2723b9f2820617e 49741e8251c6ba1d30884c1440131c6663d297f05c97a87268d8ce280038bb34 130cfac41dcab383a2052d407a7a3c9074a420edc5162c5af599c5e7b750fb39 a2d0b5b0f1265d6402a8092c31dec5e48de1f6fb12db9f2db41520f641cd7ef1 8ce7bb26cfe77806084b861d58e1a737bc7d0d47b9d51a7b1d32c49ad120c29e 14e00bc08072a0ee425e94e860a68428a0fad48614d4aa4ecf3e9a5971bed184 6467b97c9964b67a61a439ed27bd9b81df7c45a86356a8f94448f0f7b06cfc097085897c2880f2015dd2db483e2b717d70ab30d18a13467414f09e8a03f1ec0baa7b497e4eca0d507b645e6a12f4a4c59adf11b04d8229a7ec98dfed56cb2c0c4c397f4d481ac77df26a6e39fffd931df5f068df139a6eefbab8bb5432ace5d05e3dfa4e51e06a4f9f18e99796a14b34beee115d64cde55c73191a5c14575309b7306901d9d217a222c0fa68fc292144bcca821004b4316cdef575247540590275a9d2682c1222fbebde791e79b824afc3cc4dd6375f536fbca1d8a782164e5d3486a83c021ab304de6646db7a553e61d8431e95004ae0aceb2afea4537816081951f250ec9651ae36265c16afeb1c1555e3c8920c6ec414f156ec8bb4d01a03c51651bd37f91fa1f6853e11738ca0d6cd9574e90f034d778351284000b29528e0d9b8288bb7909725cf399d00696b9a7d1e6ab39a3ba201763c7e2dc3586900df418a52ee988c4e92980994a6be63e79629f63f6b50620ba23b00b7c7540606bdcee943aa77ba635172c6f86d40da3a42c3d8bd138f318caae8745f4709bf01830adcc5efffacd555d420bf50d2cc55240a0bf76d3dd905c4f0aa96a48bb2021d24cb3ae936d85bab13395e2e97e793cb14ca3c7ab8cdc436c4993045785f08a04217dd7296588eb1c223f0f42bba8f6b5f1978b2315998be77d6b1d81cdb0ced278d8b6ff90b0eff1699876b77c7e721e15ff331dd0a0f93150664317bd10ae06cc5eecc4b3471c0dbaab9a4771453892b2ca7d09d9b1b2ffdc55c1516f70ce3646b88a99bc7729fd3be03bb520245efff91f6b65737d383ddf8218d9295073ad03373adf2f64156e795d60b105bc50ee783d599869ea6b6797025167a5c05af729b0b15f6ac172797b28886bca15c70b121a10b1422cacf0d05cbe703c709aa9471248d516d3ac0f3198ae5917b5ac318315845933cb518172490cd00020db6f674bd61793482bbae8823c329ea92ad4260d1c51dfe0725c7b57492e23007feca413c6b0d83ddde8e3de9701dc298c882b31d3527037bf0897ef37c4c6806a273c339cb1d8c5b685c56477e2dba5236bee6cea015e8ace71f8822a4fd7a0c8a2d9865d60c15e9f7e01c14bb4c149f4dcf25b2dfec238bacc217e334f0c9010ad17fb6c7c7ec819f05f65695ba0098a3a954f5882e2e041fe85c837e9ba30daf5ac11959d2098af38cc21b4841ea6efec89ddcd15cc899ce1015419c199c0dc0546ca08bfc43f9a49402ac5ead3675691841785aace164c949a3672aeb5b0de4786329e718c7696ac1d2e237e11be97d8df0ae5bfbbac8424d1159e12e670675e458cbd8c212683b9c16f33d82bb23a4f55a49e27f760ab565761021d07f0ec703b0769526d22417bc895d405d30c681260490132d5691b6038e9b4d7e38079db6fc75afaaf9bb3c41b6c96af7711db69fbb6a2bee5dab5ed30843457b82042a1add825f92cbf6dd931ca847bbe9aba1264294444f5ee375e1c8654c7955019802e138445fddc8d5ad014740ca2765cfcb5aff966da9b8d21319ce4ced4c095456abba60d8f4ed56ebbdc2ee381277d2682be3800a6daf8d1a993bef17560f233acdd7b2e4219b01c422c733401a0aef45c633d738140749102131d0e8970be359e8a6a005756d7ea759a6e25424405fe4323be307fa4062dee7f4a8d0da0ac3c122c8753f319ee71c1966879c71f0273450010d935d2a3d4840979071500c9d23889709b045fbc0d80b63d282c2c96574ca03f922225d95833012338d18035ff91cfb62693177f3ff5b907fbbb86e0851a00e748e3c2c359cc9ff82aedf0f6ac1aed17c66ffe5a071d60cc52f2bbfbccd687f6cd5ad7f34f3907561d0580848ae9d38b17bb143ad424005f778601583a5be8b17c40e0f4f194ffe43c88802f1299ad71d3426988a3b5605dee12f32fba3e271cddb8b1ef3c47258c6d08009 false -check_ring_signature c98991070f7718fedbdcf92c1ddc8f33cb98ab6497de3dc5055aba558c21682d 702fc4fe6235679566ff59e97bfb34391c2ca4f851f70b19710effe8b562eff2 58 763caaa87cf235adb7c41df8facb167d7752e0168d2cf86e4142395444db85cc a3a71a09180284746cce08f73971d4df033c000aa8601f0e97a07f8386b89118 a0e6bf1068246c4a37fd7bb00cf9371cfc4d5c406ca9eec328b010c50fd20684 7ea74d08c67c459cb5b636e48559720c132acd225f3ee4648e4d4d18238b7323 91ab955c9a45e81fd9ef5089db0a3822718054dcf789b4bf4a5fb5edd79ac797 122ac214cf32ab27817e1a39c4fc3faf4975a53269ad62c688d5eada9bdfbe73 653b3af115c5980d7b5012abdef6d9cf28490b751ddab6aeec7dacab2c07c05a 2528464c127058178ec4888191f7e3dc691710e25eb5a8962ba59845ed5e4db3 5cc66eafaf7b13fb560dd82e4c900733c59d45b206e864733950786b453d46be e2429fa50dfc9f94d80127e5bd03fa8d366cffb9daf3c934f0cedce304d04990 7089a0cbc2b57b8646ebf966747955c1492f0d9a155c183098d077b9df62e2c1 89e756879914a97312f72a191f9f3e7bc671cdb2c75ebced2fc9d237a967d08b 12fa575316ffc42c096342c06392b77445a96dcfcac2575d81fd62eae6743788 8842539447c1d3481346a5056db3413f065e78656f1ad0b82d2b6630cd82a232 04941d2e833d2e56d0e3eb465a7008a488e41d34724c22358cd89441606150ab 9c1bc86a36d2f28e1bfd824d332ddf3f84f1feb2c629ab29af1180dacd1a7c5d 18c7e1cc765d5cd9b7e61643ec4bc524fded0890ea95e8e464b93199f49b311c da6d3c514a68b22eaeca42df37c69040f70bb76a0ec4dcff44240f784bea1f58 a0597779133bcf4e1075328bacb8bcb92b51ec5f4d229196694697c49dc75d02 058f04a8bd45f723a1e14a0c9a72a910d95b199845ab12605d2e300336c378dc ae407fe66c3753bfbf542fcb94a02f515a06f0c2cdbfa004b0e870a4b2a84248 743985b1d0d8f2a13230c882b73e3082f76428790b3d349f33ff8ca1179c2924 115380fa4577d71042488463964a765e538111b50647ca13841759947fc21c41 0defd50f2eb955dd288a8135169f793f44a05a1c7c3669d83ee68e6a7b1cdc1c 9d8f26efad9d62a2cec1cf7e5ee32c3c6399e3bfb78b05c7bf80a9763a7c6633 a6319da23715062f79ce7e3eb10316613991c80ff3ebec4b2fe1177501459410 e84be37e11b463f979fde5cc83068365c007f7371d52c238b2ca70a65abba62d aecc487e0e40ede4d2fa5a6ed40b5736349234fd27db421b05eab6030f1cc223 32fcbd287a51eb1eaa8df5894a92958d773a6966e8959551c94df4209325ce8e 184ed38839285a1a8d7fd0dc9dd755509f595e9a6ccb6cacdf9e593e2e12a888 61b27ee07ffc5595ad1126112a4cc3cdea58c3de92393edc59bea0423981c7ce e99825ef483904aea08e02dbf78418417476fc4c2a0e6403eaa760d638ec2f92 bdb3c6fb4b29610e9b1e074171e5d89aca18960dd71a03ae9d9657ef3b962cd1 4d733f246e04c64dcea379bf12a35ba15789f52015f882a51267e464ec5c5726 2a4e5f3f5d1b952fb9e7fb9b8fcabfc82a384aac2ebd242fb918726fb6002ec7 f41c95ddc6fcf66a6778d2ef81450f54bdd2b51fecc1e01b4ecabba96a129779 73b79acb81fa3af8aa34c5602072c3e4a7de40fdb48247f9adbaaabf1656899e fd8e96071b10987e629afc37b5af2cf2c6b655d3629af38bc8854042c79ba9e1 cbba9e85b89811a8bae726e8bb7fdbe800eee3236bcceeba71ffa4c017f9f934 8b64b3d94859cf2f527e49dd8b07f8ad009f6403fea88fe7d5e84c0e5f6b6359 c09734574e1251f554c503a89293a7681c91451fac06487b22065e1d1d622ded d4c9fb113cd328b3296ebdb140d1d56775ee36230c8770373df4f24c57d1676e cf00460946a706231cade157da2d2550d6c9683c49b9b7dbe49bf92d01ef9554 43b6e8c5b0db9b61d7bcc14491ad536400fc94796d9fb6f11f2bef3d6bf4b97f 44583a7cbf0ccc6fec3c13e8dc1d1848d50cb4e729db60dc33826ae4f8111e78 89c156e693e9baf507f76854dcbd8835733080ffd2dbf757eb6b6803947f1a0b dc0731647d7aeeda00281826b2ba80b389f194470df668fce4f20c0c8b52250c de93f0e3bfe158e18614394e23aead62bd87527d8b1d2e4c19398092e084f1e2 72225f90ba68331eb446ebafa279dc8fd84743cd1b8350d4ecfd8d863567e0a1 af38cc58a9ac38fbba5eb3e07cde4d3fda27875ad9b44ad7530546a0806f3ca9 8a17571ef3f4bed2321227c5013070177d19a0c63c556e607fcc9f3726728a61 1edb83c5fa7d25ffe84e0c262fa965a4be1e6f111c2ebfa7136c25748395b2ad 5c95c65ce85b0ebd19061c5689b1a5124c85c91a3e9964c0ae9ddaaa19f1f27f f382cefbb2d4a2440badf8bd877b57af7f113758e7a1bf8c9a32f4b7ac296663 d78b93d6622b464322ffb1f6b3ad914907f2dde20875be47c1ca26798ad669f7 0862eea45e013e22d5cafa4ae93a4ef7e13a46126e751f0be6a6dbdfcae07d36 9f0285b8eb5aec8891d0d501fc3611f93167fbe03f4ded977e36045e9496e655 ee682de5999a941c8cd9f13844a6a2f9ccafacdec06159455679587ca8523ffc dd06912f5425887225ed18c6510cf4a3057f54adaa2222fe1da639e6d3e53b0b3b1efad65b64c71ff5e85995e242e29e4d06212813aac81b4518321454cfe802eee9afe2f4e2a8db2a1961e8ca193bec2418d1db050150d3e544ed55b6d2540672ae94fca70a953df8da1ff06f652f3ac6ea10fcf689f45c242c8ab23054890acff5a8dcba03b45f5f31893dc900b0d63ba491deb293f2e1294f211765c736076d48788a10397e40bd3fec35ac3be8eb6b4d374e063b8d9ea774fedf7ee00b01a6b2fdc703b3934c42a7492c51765abb9b33d0caab5d82aa82671bf522d22c09e7bb996060eb263e3129008838bfb322803a93b53a727c0b0dd8bd6e6678e20b7bf1e993038184f981f10c77046d685e8981e94cc3557a0d3219a41ef50cef0cb4b22919dedc66bce2bf7fc99b313c0f11233843d52bf50d6a3b9418c714710b5218c2f71019090d50294c8cde37f6f4c94fae58183fc63ef2bae5d1f7ff760eebff125cdc69286fa005877c405366bd03aed7cb91a1920f70a461d3f3787f0bbd0595a935bf1b40ded29fecad6da325dc247f9b630fbb24a3f8d873f1908b095cee5a68eff9f83fc63d25f9580fe9ca1abc27f9ca26a4b935f722f50806bf01f2ff39f9bb92a4d2a08f905618a60639be650000f8acbc3c4aa23966e9a72b0846fd0e8de637f3294abb5e715e53b0fd4fa0a79abc757f8058496e74f7e14e0539465bb63e1299f5671ab74995787008ebf4b555a431bacf74b1a24b2b55390fc790efb69e204ca60118d68e59fc232ebeb1e449be7176b03cf57481a2aa9e08aa6281f946bfb9554d25dd72f1d4524f5ee99ac1acd05294b0fb8c09ee69390e012e495a64e038a0176b3a505e7d53e9262cdb8a9314422a9fd90d6437d27f0e2462721a312752e4ba5db368d4b9b5a1eb55a5fb91f331b1d382bc36745d720a0c13d59528bffa43e797daff184a75cc7797c130f58278d47374137e22e2ca0018de23e91a5c26751570275ad09d4f713b8c3c0554e19af1344eaa95a9bf3e03ba29e7e2c69e7a5e1f0def2be56b7684df189d48f3b5d381ceece5710a9b960b501116b4494c00904c93afec1347ce2c27ebcce4e68e0440da8890c70fa32a0df94ec248318a18f90a19c4dac2e30a02da40cda74a7a7db4a9e2d672b3727b033ba3a64c600f4129c72388576704a0cf20d50c1785d14975787df17d049a9802075ac2ef525fd37dfb2942051d7b52334df3842e61c90b60bb81f6bfb1f1e90d880d764a3c692011e14fefaa2b427b95db7acf73f8c2b3efcdfc041acc3198031284dbd729ed09c787f57f3c5b439b29f7b41683bb0a097fea3db5e27a0f290d695b5ffa2c9fc40e5481432401ec9c0a4a15df8c9b559e98a9d90219d2dbc6083320d59d3a781b2d0914d7020926452e45d4a519c326528c1570b536e7ac5a0cfdbeb74703dfb31777a011c68a5ac7cae82094b6ec4b40fe5bfc7eb21b8f8d03cecd94bfe60fda574c898863eec270885c1aef2712a9895da6915bdf2ec69e04f2afb38c25b16f930e44b34d604507905fbd2c6dfbe2e33871ab28ad17ac71054763dafabd79f119026bcf1b5f7f55af744379445e7079367a16a20b89bcb104e02101609438174682dfe44632ee325267f5d20aa3ac90dabb30527cb13fbc3c4582c39caf0df3d0cd67497945189e320f42f7ff04b214bab347c5af0a74300562dc98464eb240911a840908a4a0ce7a03b371a5c6b233ef62c648fa73febb0ef3a6efb01e124026767ae2950476015876a112339ef286ddbce75c029ec7cd0dfc41687d15a1baa144f8a22e5d7c5e51834b8c462fae2cf368b6aedab0f9310441f35d4afafe0c5651f4ebfe9e9720078352fc518b76c14b8000652d6ed3b90d176cc7a5e62dcfd92333184c0a8b7474db66f166f8737ab5d512ef32ef7606b6fee952f96081d39c2addf553bc57b9115a7ee79de017ad2f47cef56891f24d08113a14d52f7a4d7b3371f78bbd7ad2503a82ef6f24837571dfe0d8b50743c40be37095c5262234cd455ee271782016cd5c1ae7c38f32ea6675e008b566930206c4394e2acf3a6a7655ce04877a8e5f5d11c9d4a4ab094eaab69524b909857609b861f54e0f7bc0b73017d715748aa12ce7e27cac994bb05bd2886863b99a7903d0652406d6ef6ef1457f47954508bf40a77bb4b976b3d110e3860ba009188f0030154817d2b47cb5721a471d36468bff29ce808d8bd4943eeaca37873fe59709d6dd6a812ee8a76f03924efdfd44b487f6189e3b087e7c399adb005855b50d0c03e9c5daeec82f1a4ebd88b457fcfc77a6dbe623dc41631259d38259fb9a2704e91e6ca0e5de1cee09b2058ef35c90f12a07dcd28f1b3350043c67574f68f60368d54d1b40db0b723f23424cf80ceedcab66ceae9b5a7dd50350937bd967640f6602d055b7a5f1a0a23ff01ed809cac37aec66b2fc91ba95d52e5359f308b40871bc07a830efffe4046b625f8841da99f07a0b18507c781f983d416939531e07f37c70248e96393fb0e1fc907941bfe4f4f9dd341e1c7c6c21223cd3b1d2220d61efccf85f29a8cd5e68db89b48c7a56cc5e044292e0f9fe5dd1235f6680bc046cf58f6e1b3db45319735b734f7997fab34d6e56bbfccc987db7fc723f609b0858a1ccdf7f0532622b8fe26890eac8df6cd3ac779d292c7622c009233c96f00a84ad9f4ab1f1dfa9dceee94fe94af76b28be59318a4e6fccfc9829fa70de0203c192a382392b0b3b12d85b9cd696712e875cae7099327a6a4f33498f32bbff0e2d9b4c4ae7536db4b48cde1b860ba0509d2f3cc5cb9fe0e04d44c5c4ea43f80ae8fa8370b44efe66f8158523c54862f23d85e11b420834a59e10ceb7c2ed3a052cb0529cb395c69c6d49d53eee975cff9a774e98a21ee02358ed1459bdaf5b09168f5ff5583cf9363414e4625bed2297d43e7d066e5eccc0256e76585658bf08532110b7e8178e2c9758423eaf6b1f1ce8fe33845c34bc1dc5482b1d7a1ccf007dea42c99416d6f6025547ce9e5857b1620d9671c4c2b23b12cc5be8e8d8010e3cf2a6efa7959d41eb26bbcd2014cd9a5b917b46cc4eff11f53f9bf04e7e8109ac393609e3852b62bf7acd330fcabe502cbffc82ee31a94c8b835e4bd1c309051c7fc02835acd280a8f8a1beb1302c794643a369688db8f2777c592d50f1660ca76d8eaf4efc883a96af121ff4f1446f7afb8f3cecccf564aa48f4aea9b0ba0b7066495e7df521f971bf5fd8d8bbde8bf542228ab96e1c9fe19e1ec9652ed20301164503e9373a0be04b6e4ef3851865e80d708168ac509e10883bc40a39960a4b077bbbc9b109bc5a4bf12589476486d3f9ab527d03d78ffb40288d805ebb0ac2d8dc9500b575a77f109c748dff0919c0ff8e3168194bcf74f37a43e0df810d183891a2c48f71d664bd3bff7f8de0adefc7720e4a14a464268a30454941900c15f80127000ae813402e330f97d0dcfc22c5ce8a56c7b1581347c55bdc7c6e0ec2953834be713ff3356caa68ffcf2a277fb437a7bd10e09a487e266460a45408283203ae6b9eedb5bc346ed249265bcfbb3c3ef8478b335eecc52a0572545f037b92d4ea764d3479d7b8deb182f10783339a52b5a09778207150af23c063e50264f63f88009e53e3a8d54a5b490867d0308999f6ccbcb56b8f1829842727a70ebadcc002e73a034a316b5b55204060461d36bfb33bac18000c95412011be940cea52728ffff3e1f49c3c1acd37cfdc46bdd9383dfd678786a97868546d49560e5f1e0dcbd5b4e1f09f7207acbbcbf8ba9b1fe59c7705cb6b924837b6ee534a07146c08345b82bd306a33c851b27faf0a176760d16e16ce66dafb41f8f775f103cc15936172327aca39d27d698d4fe4bcddc6b69306aee1a3c3f36e08df3d8f0deb87ba36142c13d811d4800c12c45de936beed80bf5b9733dab0b4d7dbdc1f03776d2ad610f3df1f8c5a03cc6495641dc2255d925f4cf0f02692476621bd9b04a8c0cbc4e93b39d0639be3638fad34b41563e1dc9b247676a6613fc9b454c902ea5a2a2787fd54b41b66284bb9e044367afd2018d23d4d34db68606ceb2b580aeca73bc63d4f33dbb4ec3dd3601afdd490c644b8980dcca8f93c7df862e74c0910c5c49cf9112b5e5e4b19faa3323e4081a17e5c952ec8a7e355b65b41876604fe316328c453c81bc2d034e5e7071d1599dc82e2f3493f45148d79e102653fed58774fad69f13d8d1de502ee954032c2a5acd80287ee43d142117590c51d0c0df6d3e577d7cf1df55f44dd565c7c13b931ada81f45facf8aee9527e7ca9d40003c097ca2e3594bd143c8270440613578852c148250df0bd16cc7db54c2d72606edb3596221ae8c14b68e47d8f3e2bd889de6a7e0f442e656ea59bd6f7b79160ee355cbbcbb347f2c2bf3a8a5a1e8b2866ab552deaf3989bd1e4815cba379d2046273e97e81de67c1946729d0fed250906c42bc0d64ae7b8c326e9889edb3860a81a79cf25370e16309a27d3a209714c3b346f599fce7c94498b4db5876a9349257f89650b966c21624ceb89f025f8bc193954e28617411e10a646774cb495c06737a32edc0c8d2293d10b2835a88e19d27f6d6712a56b5f631dc969301e3af009b62b2bca7d9dc5dbe7d917c27be314ce5435ee3002771d98427880bc91eff010ba0ff5f3c06bae4f729c4f718b2ff67f9fdb901a9a965789a3a1bd3eda0110b496fe08309810cd4ad3eb769257b595dd9443452b9326042d630874fcb48b60cad9a6e4b5b7b3edb9c71c57427e9e0d2e0bbc66095c79686a71342ff7ba2c20b88d71974b92c48c62a17c519baded19213d0606d86f15d07e19543d9e3df750feede5103d877c53fe2a0babad3b65ea09a0d99344cf1bdfd90ac3fb6ec8096061250807efb11b0820b3d789d7c311d904c3d2c3e8eea7db5bdf96f285bac0a0bd1585784598b0b2deabd2221c06cebf40e37f1296c2759bd1cf6da3ebfec290f9065d53b2705e4696ea4d4afcb71af21fb63dd3d45ecb5c689cda40bbeab5d0aec1fee1080ceb9b3d955700e367fefdab9e195eded10810a5151eb71e32ba501cba8d0f64d2dfd053d1d8987a6294d0d22c1e4ad81eb46530db2f20546ee180f0697bac8d307c72b86adc627c4cf48964b149c37e33306851927ff9e4c90c0098cc35eaba55c1d369871a0a6101faa2f4a7ffbd6c529fd18a973c6594937ec0b false -check_ring_signature 489247b9547df8eb525489a2bf02b95a1333a45f433fccc36b9308d8013d4d6f cc1b5a296409e1f8d4a1fa451b910f02d2af2a85ec3328b0474c83aca51e0a42 30 a87e12463559d85d1286ec641905f116829947e383cd51aa675311ec031aec93 e0abef28019dd5dbcf0be06c2df2fabccffaf9287869f1211549c4864d94e887 8bb6433499babe82142b6a5d60d27dd85a7b45c97c8acd26c5a15b343a80a89e 7c662fe57e2db6eb5a2d553cd05320d72faa724960ee4ef904a0700873c69848 49325ce67214c968800cefbcf7d27b8da0b885dbb16a1b8cc2040f6ed6239483 555dbe57a04f051114e631bf9a2a2074bd285faf9fcb9bb826476538f4a8f86c f770ef15d1dcdbb78ff7be3a383805a6a60beadafb9e14bef34c5e3fc006ab2c aad703c25d91cdf187d06fe87237876e21cdb313f8ea7c8a2faa231127802f12 9e5c9332ba303622a8b56f33e958e6904f77a51942db1073d4f746a0521076af 5805ebd03f7b394177e3a373ec5cae875eca50b213f3c530f6e0d392b71b3317 5904f80b509b57f761563dee334aeac78d72e5a5c8cb4f9b75b2dfc261a0076e 775454547225604ec06f6f4520875034c1e7970e1baf1a16c494453dd1764913 c884c363c88cf6680d206e2f18635466f746a0fcf96ca761f171d3db2a9a6f32 d015be1b75c691c4eef30c5dc0bbf50fcbcdaca52496496445ae3e40ebf6e952 295d319edc580c466c1b81e05d6fee3dbe9b31e061457fd4b8ee7559426bc35d 01fe31e204d1c0d36914bc21a3a03398b57c1d83ff9551d1deb043ec101a1334 0837d391549c42281e284b531270bf072b52da003a717186685af18b6332c6c5 0b1acb5cca730605924bf607482e83e5d95ba4aab5809ca482f55c1769c8ed94 56a4c70c620c8674391e6f1015b0363421517319ea517324121f4355b0f5cfe9 2f9a5fde9817ea20b2516592c7d7cc0572031d810bd75b9e1e972f3b8c3c0fe4 fc9053debc360b2d643ba4a2a37048dfc9406defeba136cc5f26f774416adeaa 4cd44491ad2c097f15b86e1038c0c05cf8e12d063b0206569ae4d457c60114ef dab22ed8544cf380b1f03ea10204cdb25c958b7a6cb5e4ab8221bb5609a0566e ebfb55cd1f03ace054156f2288621efe454c45ebf3ff2131850c636f7e5ad0c9 95159f2afdfc623ea8fb06079fd2c19299a71f04314b592a0366fa39431ad097 0a67b17ec14c9d2ae912e62ba6fec1b0cb2cbfe08e9c689251e3d16c2b245d54 416f14cf89df663414ae273a0859a25df615cfba69579225330a74676e9c5ba3 16007d8e1b5028e80605a06b4dbdb15864c69f519111e31e4e76efcd25ca3580 71741915a336460d9f2d5e870b6c0768ed4b548af48345a446633f3cbccc5924 be6657a5e86fc05d6f043e108662cfdd0bd282d3c0835711415873755852f804 03e82b7eefe777f0316009566501d0d52262b4a541bc80284cb33bbc98b70b08c02dfbd99b8a1e12939bf9fa81de53c27c4b9c63cc8e68d19c5a8e9edcff8f0665a8331ffd51c5576b70d165ab7f30a73115306204237e5db073dde6c0481800d54f1d81e78f100179f56fedea3e3bfed9b7f8e0cdddf139100aae887bef430505465c8724f3f7577d35cf0f43e0f91a8a1e9cd4786d2591e9da8a599ed30806dd249fd56cb46f5bb08de7881a846d9a55422c55fa7ff685a4caa1e75eb869079a2c7d7d20d46a3f5f19a1d713bb5fc6d3c00f64e9a9684430f6a39bbbc1ac0a2649f00984cc98309121a4ecf55b61c12b63422cad195f863020d0c852b81c0f37a79036f6d0898c0e4ce0055ad9a6550c2a5e80ffac103fe69eb007b1b00801b5d077b5930a094fe836a6737a78da8fa8002d395a8e31e379c8db81f48eb209310b0fcf9e5e45f726091211fdd1a1294093327be85b696d9c7b4f29dd1789066a94550b67bd528edcf9f15eb412008f8fbbeb68a42d969bca182931813f1707527f849f6e717b5f4d7e88b1b1b195accce0ce474f95a5d2af857028c3e35a0fcbb7070e57a682dd3379b1398d350739a53a5bdd74de49203968cef9e59b4c083bdeb8ddb7c69308c84c1cae9a2e44ffe6134de9313c5c5da4c5b627d2d5ff0792bf86160a3232d9613e8a1e917f5fa68d7ec645e098c40814985220790765052eff74c104958bec60fe2406435e3b9f6f9c4ff257681e1393f89acd35253802b81f497622b1bdfbb30397d6d087a47c81d5939975d8be0c8e41397688048c02fb3d3f77f975d9f9d42c447ca3f94cba612d640493a28c89db655a46d9fadb0e35d54409adc0debb662c6d6454975cb26fa1e909971b4d9e281a47e4cebe8e0268c3938aab02471bb651f722650881ebdb91d16d9bcecdd7fb077ff22816380659dbb19bc614db3829d06299af97da742a9d31852df41cacdaba4b4f363b900a22e16cf9b979b49a7bf492f81aa694d0131fef14725126ec733b2c3e51667308bec2d441b2cad008ccb61e5d39e1a365e33a9117b4fe7247c178f7ebfb3dbb091009a95b55ffbb7b3cdb9d7d4fd1dfc50759414acadae01407e430fe00d41b0d52ed04eb95e71df3fee224cae1c328fef4a40392c96e12e514448e415e48fe09530e0549b0d6b92d921223d85069d62afb97d1af7b2fd84f46cfa7353d03b80d16e8aff295ad343d143eb3615ec29f10b6c8f338cf04059e9680e0ba9def3701408fc887eea467b106f7ba02e1addbdeadd4aef21f0d5b1e1f27d40e88d45a0e767d1b7720dc559892c097afaf673ca1a44f26d55693ec24c9e88d76d0b72800d1d76c4421422c563ede23c8f37adee347b3c740405830b29ce92a3e4fdde6088117c8eece20c4a43cc087be46a46a5dddadff9a3c5174fc46f23918c6137208591f946193e86b2c2970dc5ced1a48b2ec1c16b60a929d60cd4b93ad1a6dec0a96eaf16c2f24a40972f19a422816edd9ad3c26225c848806d32e5ca503aec80ab1c4b7c512e7b2f2f8cf1626f20c7f6579619f828876225baa2cfdc34773400dbf128833deee3261bc257eb2453422b1ee9590e469c425f15c53ecc3c5537a00657b815f906ea1ecd1e79f969ea671df990b25424289dd10d09a442e6fb78703d955f76607bbfaf264ede224a38fb725b960c39a85e61c4fe7d9f51f98358200d76ddad47db1a1ba0cf53dc079bc6c55b5e778535da9a09cda95bee95b00430d9a8841e10e4b55b1451867c45eba4eead1d225d1afcb0fadd2543d11d849c60b1541e8a77ce81f586749c628d4e7e1d8a9aa4dda1a084210de3a2dad42eadd0e6f5bccba52b34d12f49fdeb7898c33ddcb9d939ee27a0c4c39e716009ab0030831d0b76fdad52bb29e145d62358fb9b53fa13609302b3ae6a67fdacb5bb6e70422b4ae6e8bcb01c752bb4fb3f96f1a8c0b412ccd8e2d543feda64d72d81e200925856ec7e52501f49abafea3ed5cd4acf22a85b7c2092f1836cddeee702d4202e89fc0805deab964e2f898d487007ba66f18c55d6cc81a371dc96b611882fc0a47eaff8a976668b44916d8c316982b5d71b2e761b2e8d4959b7480c03a62ec0ca9d4a9b3f08e753a36e73442ab881e055dc25ba2e63a9c763ec9a7e47ba50905055435f0aec9433ade5eee0bb5024a4a18dfd165cd7f94ea297f6d8fb6f8020f6a77946121f82a08b4a99ba7f6819b7bb87fe931688708a1f184290d8218ad07a986779eb294a73505fb66ba531a052e55d62a15243fd9d4877cb761fd85f20ea540ef36b588aa8dd408b9596e9150306f1222c00ece0b35f0a936ab202ac20c4d9b5d768750e05f56b535fc1031703c41cfc662ebf86deb6393b1f693cdab016d680e301bb4cd6c3611a835eb8a7c0ff46d70cc58cc8d1f0973b14b33a9df031f462dbe62a4c31fd2d35b14636b69dd4eada06f92fd7ed1a26307796deadc0f95d17493cb0b2967db31f26eba79dcff692b75efaed4f377d45ba715441d830920d23cd67ad0231f48f0145851f97c8f865c3c24810ca40b184ae2fdf99f550026d68e67f1a66420726256ea972e8cccd93402301bc1576c3be52e789feda207d627e2e4e60b0a9a5332f7efaae842e271e49d8deb6279a6a7c222499fd8600d8028bdeed96b4a8af0599cde605a89d3704ed8ab420adefd57cb602778153b0e true -check_ring_signature a8cd013daed093f3806b1ba54476a44a6468fbcbe9f537b1db70aaa0e7e03270 782c481c352ef41250b69427a76c6cc5fbe1f830a74ca841bfb90cc14a5783ad 2 7df3c07af233a2cf998edb804881dfa39550e79b8829956f8824b639479e9cab d80653745adb9c79a38905f05525d3ee2aba23ebe613f8018829caaf15d2dd95 8cf753eca51f03290b2ebc61e8f4a08b29d66a8d590c23cc525f9c3aa227d70f2eb989199eeecee24ccccd73b9ae463b8ec1f353ed136477123c4bfad98e500ee86e93cc0dffb3e06cff2f07aead461bcc29440d60e4195985485e8f65ba6107e87fa57a8a2e1b84677cee7251ff3f5938e80e16c8baa62e1d6c897a80c2390e false -check_ring_signature d94cfd5f2d96b845dcca8d7a9a17f8d5efe1dd04b1ece130a7858e05f02e52ba afca7601feffeb674b6a5096d6b39e0c7e4af12cf9b2f3f9ff430794e9d88834 1 152dd267a6449f2c0b82f0d5752c9dfc496e07f58120bdb569f6e4af27eeb996 b0d207008df81e39f9536ca40b82179b3b36346af17da578ef904bf973683008a52aab1b60b253b9ab02d512d67056290776997a6d06455712590caa10143508 true -check_ring_signature b3df5d44812f1e65ecc52cc6a46162623f33033b54a7723013b2c2d48de6e726 4da006cef181367794195906110cbd05060042c0c0ea9a4af6f7699ab3a26a20 1 891c1cecd072053b603d42b8805c1caa66dfaed70d26add8c022f8d4f3a58349 b205f02fbaf90d06f2dc1b38ca319be0dc712d4f613fef993ec651ac03aa1f0db08c0c9ec7e51a8a6d92544d68526f60d7b0c02aadeb2526b0662f84b70dd70e false -check_ring_signature d9308773eaf40413b35c63ec5925ace8b4a05ddf9353f25b3b895adb6459f1fe 5e4557acb135134441b8e9d39ec561e995e8ad24eb9dd05440b05c0f376526ba 145 d447cf62e7383bfc1f0a46488ef4bfddcbe6ead67e0151495fba2d5402799bbe 16a306395827d1c1a5b0901c67e69e1e387fe5899b90550278f4a6c2e03075c3 39a50d6ff8338be05c498d95705617563a9fa81e4474453e89290e118216e75d 301e6c1c0a8cc20112034058df038c1c8e663990d8a6e8f8fdfe249abe7be550 b85cc046d96614eeb4f2a9cf422420a9a9224404b8503ca8028f87cd9339bc2e 80ad9c5007c4f7a574eec8f693f084f5f0a2fc996644bd05dd8dac328e968f4e ab8a631586a27b18c80dc44e01ff53eb05e03d0844da9469d4d943b1c3fad7ab 515b5bafdee33e0f9a85d5ea99c22d6802591f6bfd2e13ed5daf72bde5b92e1b 188c2faeaaaa2ee4d0b843cac3c0f40e3a9c50e0481beae2effa049c45acec49 bf98253b4b155b5c2c93769bdbd2bf360f3c240536a5a0a9044f25a61a2af73f 58674608d4ab37f45e5c1aba28eda3847411120f73caccc98c5da7fd29d0347c 19a5ecc35e06217ae9ec5ceaa644f67992c7e94bb47d1d0e15fc3b80f8d27526 b5a1e68a02bd02d9cb709a1263afe8d1a6517e8a32846923cbcb6d5ff972dbef 0ca66e8bd8e6376087acee7a945587eaae0bc6e4a5f6bf44cf7785b050cb53ef bf19dd1f74011c3617876ecf46bc560f075ee31e0ae359e6101c680cfa5a3ccb b72d8a606dc911d79281cec3b444b160a1bff37a2b506370533ae87a34b7b4a5 c3c5d3fbd8e4098bf1f228af1b0eeab925d7ee61ff1a331244578af7af72d2b7 a914251a5b97ec2a8cd3bcf5771663adf6fc8bcd203d60ed95d10115a3f710c7 08886a0a1113b21bb6b53478caf04c2433b1d22b3f0fc3b9fd446c539957c058 038286bdd844e02c21807db75d12d56ef12f1e787611f64826dcabe813562dca beb2465eb66334bbf64a6f4ca4bdc44208ca27f69ccdd299851e427e912d2899 79debe73e5750dddfbdc7cad82696079a6a2356c2fb131746412ed3bc9974802 e5f3c173ebf0a83e3a0d167f581aea31128923c3bffb5717c0b3ab4fceb48b4c d7f24f6258b6910c769e1b0572fe30a56fa81260eb719a092c7f208bdf69de6e a17fa7f7e991d169e5b01b496fe26355ea03b5acce5ab8060fc958a1b8fea050 433879b49de7e7d0dfa47bfe0e8bb303e21f7e75e046c4023d5dd13cd081f693 fb85d159be2f852327c400b805edc25f43b14efe1d7cd47f4af2e30744631220 0d65ec6103774c05181b41193a71fcd429ee1004194b73584105fc1c118f0db1 75874900e799b3d155052111d8d9f9565b0e4dc2ecd41009d51180e273477eba b14acb3f2bcdac63ba93b5854aa5a517ea03e15da540d767a9ac2b51708a47af 328010c642817bd9a3609523f5475806dfc103ca40083ea1a2e34bc83da8068a a5cd7e33854ab280f97f57923f6ff1d570d961156ca6a181a6808b0b6ab018eb 11a2ba73f3d609d6358694adc4081035878cd02fd50a6ad2ac4e6d8e8352c2e9 8de33594bd75d281dc86227cf47f984c58c868ce43853a20701f5ee5277ca01b 09136cecba5fdaf43652cd9519e6c1227b93b1def94be830570d6786e3159835 591e20e7c3c64f3399f175ef867771a58e244cda30e8c2167b19c915fe71eda4 f135de01975338fb3efcaee3fe01829cae34b8e1490b537969e266b330bc254f 2b0b1471545cfdd28c9b7a218ce46c0bb90e4ee6a5a97086624fae9821a95288 8c260f5039ba5a4969de38615355fc8b51f08230aeaf3081427096a0bfed327f f56a339ef6532110d5e37c83fb1b184c6f6c8e175453eb3ba30a62f40af331c6 5ff4eddbf68d83c20bbbc8006a26d396d6276b6b42f39c895ab94b403290f785 00bf3bc71c24c510263267e8e437c1f2c0a423532f15db0b96ca7a5a0f64e21c 8fceacd3704c030e1e20bef22d2cdd4c2d696d1ed06a68ddf7799126fed6ed45 e47c6726fe22facf8e90ec4f5cf77e2785253209b62518482600cc54aef0fc65 c48038dc99dd9c2d087dc7bc2df1916aa62618378b1fd0ae63a7282fb3c861c8 068adc9aa1f46c0f990f3a8620e1e7c1a3fde6f0126cbadea7aa14d471f70102 b745d86936327fe5fa82875b6fa59c43780f8a90a26eca6cf63a38f0d84730da c832f277b433f2b20a7d6801eb8c7710e0b68d4cbe073f20e210094c4d5cf278 e5e038f6bf70fb7ada2a8a3add4d64ac573e62d6f0176c11a45c273d294d6fc6 57b9d2c9dd0ff3f1ca6201e7703098794dc90b8046ea852fa0f6998a6974254c 23f3cec4a274dafcf4151d3c7e905b5746265623e2ff5a1caf8d7ca6434d0dce 6c4611e89ea4ef06cea599d5622bede1660c8747188ab44974fe9dcc5dabaaa0 8b6aa5ba1c7b2f05e2c557c00deac1a2a67d610a5d83eb4dd8150c583bd37041 6e72d8e860569822d9def64378b83f4c58447fe1b73d6780db48b4bd8a1fad29 6404c21f95bd03f7369da9250b9548cb3439ff17387e4ed02178bb15ce6ebdb3 fe9d737963e783c0fbd918269e6def09352d85fca55becae8a5d716977a6ebeb f0600105fb4ca792cb4848b408aa7417421ab370f4e789e3ff7fa321f4e4400c fcb84529817c699739434533ba2ac896f0c7268df412204e9c717c1e6f2d21cb 3ae4e33effbe971d1fe84e95d1074fb03e1dd0da186847b21ac07622f9ffd84c b46bf0ffc9f05f91199d725321892b007137f260039e82e3b1ab3cf7daf3cfe8 989a674aad7ad12f2da8caa5dda3b72746118ddc817864073dbf17019e845984 7dc01ad2ef7a34714ed8ddfc3dee1d459c3e4c2475001f69550d765b80ca0574 c71ae284a47b9d596b898933bc9a2ab5d13e6bdb6f29ed9f36bf16833b5e3aac d764820335c71cb3461f4eff77d92fb9372cd496d3f190b16de1820bcd8d0030 64cc5b3a6601533d64d1354de5733ea25fdde809d35ce65b00bb5b4b4fcd12ec a44f991b81117f6e776e58c8c84231da1c5c204a707d4124521b23a8630ba282 e6e9a7580708aafecd0e24f44c843680164c32689d1a2ad7ee4fa501f59ce384 b33fd3ad6b576f1549250e05c487e479799df14bb031b55a77385c9d498c0c4c 38e36b1c744f73278732a21164ba8634bbca4d0df8a6f06caac4367b2b262c7f 534f6e9f78c28bf6d6dae82d890e7445fe83f8ea4f43755bde776ff4aa1bf7ee 993ccc85e20000aec71ca4b2114d9305bea4af5a6792bf4a489b6f4b4f0bf67f daa9119d2306d5631146be9fe95b74f6a28b086e56c46effc33958aced9c6359 2c89136fd87a36844cd3accb0cdaec2d5875b24bac16bc1e0391caa0d385f258 aff60f6f8c5dbb3b3ba23c1fdda6c8d662a960dd90752cc087d2099e99778661 3a7a863bdbcf57e41fb74b8cb7d021be9f1887ab8f51c83c74c24a4560c417f0 8d1c2c3a755aa119743c8864e03122a5225bb410cd8099e126f0019e529780fe fab15193df8e040e4e956194c7fea847e2021443f659d2a1e3b3fd0bce0d564c 88da6284176afc9c1550ba7c3299d5efc141b2460201ac6011decc3ed77bd2d7 5104f33f20287eb4010412d108933d9c9814a8ae35c33871e870ff164f503bf9 6b7dbf9bc9ed060f909c5df128ac4178d874148661f69c15e7eaed8bd4f6dfe9 c726b3dc479ab8df8086b0f0588d17d949048072f617bf175a8147e43e186e7e 8582cd98f4a04e0bb6a0eb020d4d2da1c9e7bc59268e981a3ac0849c214bad7c 56b7c1f4e4497367ed1f8cb24b6a44acedb61e67e4ef49ee19c5c59c929ed37a 44c9a8fd4cfda9b8eeba1efeb6a5e4d4a86b1236a6be4300266c10c1f54625fe c2f2d44b3b9b14eb2cd727b23a9b89f319c53dc0a22735acd265007f413091c0 4f4d49cf3d2fbe2c652d22d1d99368f2c5faf1b16468a815d5f3fadf377f030d 790b1adf98727964f1349ecff285d4f52677e962d48431b0a116693c2c6a3e51 428fa7efb7f529edafa4f435725f652451c35b11c1c50a155d1be8c4b5474cf4 77bcc409b198b863449bc23d222a1101d36823d8fe8c352b6d97f8bf1a80aaad 6de98fb17560e654ff07abf8557e90a94644e36fd3c2eb6d0671dfb4d0b4ddb6 d6ac74101f9f46e548c2c4f10de3f5b76025a1ec9a4e8da8c451639dbc951c66 7883cdc318387157b471a852a09376717ec864e6246015a199e0d60ccabb20d8 868f23b48c7011d377560b4a2dc07c2b7c73aab2eceba22ccbeddc1a6ce58af9 7c00a223919f82f34feba66faf9a2c54d917ee577f135a2b2d038d668c580c77 7f15327ebadcdaf23c82d9829065e61c5b1f4104846a5e87618dae4d3e1feb06 b7f7985eaef9e31bff17512ac14d992274fabadbb23d8fb095644d240e0aff1c 242d3d76c337114ab1778776a41b4ccf7fb06a6b73a9ac36b88700569f06d1ce ad6a09fb4fc9c13559524d3516028c17c6496983718d55a5924e98ac3b8329e8 8eb88f7a7469810df9b6f77afb10be5f6a4e5b11b648cb75f8b1842d863ec974 0eb90c610c49ded0ee3ae0727a8718a2c1dbbc4e6b53061f3a89688eba7ad535 7cff27f204a12ec941668dac31d092bc21ba20aa4df929dd9ec14a3f4700c780 13decaa28c3687d25b958199d8fe3c4189d768f476df53ab783e18c103fe8e2c a8d46b0861ae6182c299b19d1985cc128dfbd22fca0ed6c63dc1c5a677ace24a 958a70e4fccca15243aca16243e1b6e2e70c694e81915408fa9ee8bb676bdc85 7992bbdcfbbea8ec5c6d8da919754252e18ea803178ca83fbcef11fcfd47560f e95d124f6f66ab25c3692b39bc984fbe393bbdf1a67e695ba92c62c1ed6b5788 cc63cd4771e95fc2e0d3747b5a083029233d2c2cdb671c02449f515050da81c4 896772c1e0a5854d4d5a87ee2829e354bd9417d33b4d0eb519f0a9062b5d2140 76697679a88bd5189197ae7c5fe289fff2375fd1aed9b302691bf258417aadf4 c8b28f1d14a987351fdc3bf1bb6db12e0150dd68e38ed891295e4aaf5bcb758c cd703f55c20d80f0873bb9e8ad67f3d92990910fb3d3f59e124b572d2e840b37 416e90662a101947d7aeeb85a3b5fe66dd0ef49be65898b06ee62d7cbdcac3a3 747f75156de70a00d18abca2e8481380adb1b1f9ad4a91dcad281e096f63bdc1 561cf0e1671f44cc5cc2b3e2a24889dc4ffd1cb29315f797742d619f54944d8a 8e2840d42cf07958bd071a41ee642e81cdccbb2867f7869a1bdb77113510b27d 3acb12c33d6313ad1e01f923c268b7dba0721db9dda256718846beff5ec4a720 23bc2ff740e63dd8f642a03a2725028522299839147192812525aa1d95a72ca4 0253f4babc4333e6e4d6e3560368be6853964a96c4e5ffcfd763284e8695c4af 0a9ac8027a014d2d87bf0801621f66cec08ef21ed742f87ed7380e7c11b8565e dc9da5183a8d51d963cfebea0161a5b5a81e62fc2df682ce835c3b6d51db1b9e 5123adc4da56eba9a57e150b4a1bb8a78c1806a7e10e7299dc2658c94403ef5c 4b109a0228ce630be9cbec09352f8e5f48a7c517f6e6d9f6b73b9d0d0ffb7fa1 9bd4476f9e69dcda85b6e5fa18484198a969abbc720295fb7719adbbb75801cb 2e2c17ba3b251292f76fe09652b4ae61fd5a8df352f9222694068e719eda82fc e071f63cef7c9c56245d31624b9550a1ee06a5c60a927bc6a8ffa8772514cc0b 8b727989d33022fa2f009b750bc1e0c7a3e4d3b6abcefaec66a747e6af60f836 391c722b7d2bba71428bdc71d80b622d36c861016fe1de52ee764786c86ccd78 f8e323864fb0af95c41470afccf4070bab1c758ec413615fd3c08c48eeb069c5 6400e2820178bab8c814c48132640e9cc78d0b12fe20d0329edce01eadb32a84 56f1ddac063f285d44a9dbc6d0abf1a04d6974986e240623e25ea8d33ab10e16 20f79f2df269a29780300a5004637dac5088cb12c2513e73b287a82a11ec4780 c4bb932a48df79c436f95963d53b56051313bd7084a0c34f4f48311e1393cf6f 7e1ebc5374984f7a5d27bcdc7408b6855c5f49ac7ac649d8e416578a31c6fef6 93c6575f616961496a33de6d597ccc320617d5c87ed0fa56c447c2fa5f620cc6 6712eccca1d08955db7c2bbbf9704816847403334d133c35faed9d19d961f310 dbb519e629f67857c72eea89a938aa271668c6c4d8b465d38a6971374793a7cf 5dddb032714f01ef11695a727e2a57b291aedab49b00f44c75e13ce5593c63a0 711735f4fa145bf81375ab0512da5e79ccfcb7f424b84a4cce67925571e9b10c bf45b35154e5bf558fcc87227bedb1d02ed679cdb04a140348758977bd587f03 ffd793992dc707f414f86afd3f427059adede5e4868270f230a2ab7af3ceec65 0880e873a5fab86372eb4d68b30fc660cd8746297f5063981089977f9f64ec48 4a747fb953b9b65e4c3fa85d4075b994c85c8c6ee2044227655791c6a4840ecf 11376c19d7d14091bc99038da7602601a00775c1bd13dd93f9bcaecc958bdee5 ce6165172ae0363268314debc1d89ef85331e07a05a41236a5258c834b80b452 48a34de60a9117416ca4a4f73e84abf5be2ad57e7e6ffa3c309f8c169c2531dd b863d7006fea463de1b162c43d07bad67762496e6a8d7a5f81160911247976082a01fdb3bb433f3ea041d90f6325ec8fd61c575c2f66b9efadc84c18418bcd09f63ae2eecb4fc013fa928ee4404f960762d2873be1ff580ed4af2e6e05bc45068d112519afc98184148c13fbad7c859d56b2e98cc2ccce35e1f1e4604c3d4202931a4b03e23a910b0efbef64a0b0ad64681246f74b79138641a9b722e36f870cc0074889ac4e65d51a6e74d363545b89c4f654e0e3a74068e3c355cb682c80045ca4aac35985f32948a05cb4ddcf7b8bd3ec2bf064aa7596a61780eb5cbaf8025d56422e5cf9678027e554022e2ab7655c455f29625e4c5e1da5c805fcc75d0b213a80b51ee00a0fb639b89008a133ca5705dfc4f888e4011c57d581ef32b10c5c1b7be7fa479aec943f211730ceb87d2adcdac190dac3dec8c022ed7765d40040adc592e376a3cc88edb1fc17392954326abd83677c0aa46721b4993821c40d0e910b1e2891f9d081065b908d005c63dd197e7305d3aec920e637ac5884310040bb7957b724e7a5a81e43d672c4aff73dee8f7e018fde5ee8f6a533fb6bd00bdd31b7919124ff557126884d061ce8feca65509745f8a2bfc4868b434cc58a0b6b3454f03d30682861d8a2efd3456a68932b966088cc27651b37424250442902a3a83b4eee5454bf21684ac1003c42b196f7e1a23bdb289b6e7f1bc0586bf20d8697e30b07e54d6d1b696f86c51445566fa716135dcdc35b6547dfc423ce800812f79d0d37c503c8d09372c2a88fe445f43b6b870fb68af9d178247aa23ef80842eb3cee40ddb2b2c01fef2511eb16f15d907c5c26f0fa6cbd78b469c5d9cc06d8864e1daa6e168860c442a02360ac1191ebc4b7fee080969456de45f3421207d913fcfcffca72f20399ae61b6b3f0aed9716e375f21909613ad8a68e5a6be064c58c8f4864d1c031fb6840d46ce26d3ab5f4b58a5eb8604baddbcccd7dd430001c8d0016f9e800876caa51ba8dbbc887f746a198d7680f78af50511bafc2100a237e5561838b04f29f9ac554780940abb4c23472b80f7eeaf42287fc741160d27df05be8007d25b24e18a4dad459a904071ffde4974f8b014546cff3d82ac089f4c4a177bb56214f31148aefd100ac9c100fb36fa8784d7bc399680f5360308136bdd34ce51a7570135680130b2c9391ebbe69ba91a3a571bab5951c1a1a30f566147516a943dbbaeb93568c31951bfe946ec3f2715c74952c85167490cec0760c188bc40ce12224265aa46b5e1e3b1c3044d1a4699ae6ad18af880c4fa420a14f771a379fe015badfa1897219363b23b913ed06675b93500450658199aa300aaec939344e905af313025394d180321eb81e4551f45d6516ca23fdd25664a01b0bff595daf92031a3c1bd986b8598d27bdca9d62c9dcfe2dfa2265c9dcfc704d1dbe0f2c58a58a8d8c5fef7e99192155f4176be7944a31ca6be8c39d902e60919063026acb79a460dd15b0380c2699913b11296399609cc7b35eca2935a4204f259753a2d77d475ad9266e60885a5f4b74eaba9b9e241067ec2fbe8223cc1083607e469be198687a25481fb0fe285c6511546542ce823e5eb65a3cba2b90601440ecfa2001f55278a228e653113cd2efc01814dc7b45cfe5d53d9d8598a9c045bfae7703e6bcfa9f8e72a6da96c9da9031cd6518cdc7d1ed051cf3d4d25080661f914c72f04d7f3fa370e95400f5609f75a65b80a658dd015e16612b34b8205acf5796008a2d632fd7ca5edb5324a501ee9b0e87afe9da9397ab3bd951c7c07028b0de673325052f405c1470363a38111510b26cc1fa97c0b00a3c7841a3f0e7457c20ab1f7e8fb7d0de070b35c1dee14600a1a60e895d42abd6d357ad3730398cc0ebfb88f506d0b638165e9ec9048b357aecd4d9f851d4572ffe856c1c2068fe58048d006ec90f58220466543d0666b7ab3cd09b9f01a9c9c79d2d4e34307ad2293dea005623e7c30ae68178f246a1b911e032aa674396d9afe3ba43de90dfb8eef928e8a8d94556edbc926e2f79a05e528a960f0662034c95aa290f29b02a2a4d947697e551d23554239270c80db251625b7f72ebc2c8514cabf88205904f4f7b7275010f37d12da0c798a4dcaf6e9d918ae6d31bb73b3c9f013ca37da0c5933a9f1117d3a4ec488fd478c29496d0aac7c8c71509328ba1b64fc5ecd070a2f7c3d32fbea108978454b95c3b51a2678133c3a14fa4a4afc79670f9584a60aeba3aa73472de00e50c33878c67c454e2d84175586fd1e07c5dc36462be3d80ecd96c1a1e90a88a39ed5849a49f00c239be352a6912ecf630a9878223065f802fb2eb5d0724f36943d81824c7e47ec51b8a91d12161ec53bf7f8a23c1e632d00c439db6e979549242c1f590c1beefdc55771b5c3beeb0396cd855af3ad34150e452b7618382eb0ed164caaeac5e6e68130afe148ae22c09ca78e6796f209450e5a533519e1c9de302acb47ffa3abeba72dd94385a7d24e5f5c03ab72fadec60ca58f583e6115fa75e506b1e61eaf8a0d0905550a65e18346941826b66b8bea0b30e83c16227fb9c0d70c0f4982f56ea3533e92e7d4cccd860f1a7857812bd20a7dcbf8fd997d9610726b1d49a037bcdcc8aad23cebd2bf49be84f88680fa6f0c635a5f2244fe32a9656d4a8932b436e18cf3b0747cecc0c5078123cf4611d80bc6ac55c0a301e7d9442fe33e877c4248f21de586617bd9671fd04da86aeb26026ad74e11ffc6eadfef89780e0d9625baa5e407124ad1ef86b17603cdcac33e0ac74c039d5437c1bfd6b3bde148ac3bd48cc49032a30a8cacba8a365c717db405c5e13ba39f369ae90fdc369e567b016b3a3e9a0a9d2dc7a6128cb8f9669ea90379e43712d0dcdb75faca63372cefeee701cec9eb3fbc3fa1d74225e8ae040303ab1469a340ea1c094b92418046c966691082752ec3b481b542d0f3eadfc9a70390507985379ac36fb824bd2b32fd59db0ec6e667cf74a687827e2b5212c9170c335e5d190c389aac61d0601f757f5cdb3b23ee69354cc274b6598b8ef87da0030938a99c7720e5c38bfae8376b22179177c3e1c732a29cb5d0728fed452e51096da91a0b4de764ba6c9784f497c1435f7cbb6dcd9dd650e031d4b6c52cad9d0946aaa4df35c26ac4ad2ebd105020eb0db8a89584bbb1e22e99b604ea3ec3a307d5901af4bcedd6dfec6e888fbc8578a405aca72c1ee56ec999b4fc070dc1dc0c415f9691831570b48ee6b80eb6784cca1d5cda1b17f32fc363c17a0a1669db0080d4de6583dfbf1c2ee94a5884032c7da44029b02903a6c8098d56a884bf4e02a63c46640ac4992ecd6b71c260c7e039a1e398d1425c6c596271b8be6a28be0c2542304019c5fe3539fef35b62f94999021bd1ff6774d8fd271e1fd32857d907f1eddd3955ce323fdeb9437fb1369ae31f34086a62aa27d5e45247199ce2a40cf9b3c44f23061dc10cad2e7ea67e006136a20320ecf6a655c82cb69558867c0be1ec0c66313af33d31ab8d45ac216d598dc2120ed94d08608e2c0f105f73700da0a6829a91366dedaa5658d48586b4260b434f05f634a007ec321cd2db422a08385f17305ecab57d81752a8b53b8ee0cd56527a9fbc08e77280262572002220284d0b4cd7384263d1004c862dca07abb9e62d4f8c5e48c3064252c1dc05dc80e08fa68eb390786a3ae9659cb520c33670fc3b997dc3834349501c7e94d525005ade700be000fe56730fc5353ba2ab46feaec77119d20ba58b1449d39100b770a1cdf35c88a9fc8de6d11ac865accd80bd4f37bf9f1200634f09353bc2ece8304a5903ec66ab0aa1c94da81c8bc2866c0f5cbc418b272afc1aae4238ae0aaee08e62fd7e2b10b255b7970b5791568a45f1f5787a0b39210421fe64a7f272cbf072595d15b90a4d340b59c790d1a846ea416af11c1a7382947a18d70eb3ef25909aa76fb65157d4b3655171eed6f7f801a1df8e92400497fed2a832ff2961b6707514116ec986274f18c643aa03fe54bdee8525d26930fdd2e650e3285fb68160bfb416376811ead89761be5746464aa0df9089d11b093f8cc7b3b01588951aa04c1b60aea3eb6ff6f4cf65bb4e1e76a8427304d9b84869470cc3f906c1e49360acdfe2ff7789b90dcd31a0878bcc369b28bfa9155c268ad3ecc8eb1179f2f17085b9dfd1b5cb4a516d4f414f4ec953e03b9a844e5312a11288fe1f8c7a2cbf806664873d6834b7e93c6bbe8b8a47f7da158bc19238c952aae667ce983f8b5a4002cc4b74b3daf10440d4beecddf0803c4789b231ccbc0517250dfbfc927a4160548e9fd9c4588f923829ae84771fdd484fe561f528021642f3d48c6133d34c500f25d77e7d908d64e867b21ae4ad77b797c9451a50f35a8febc0f67f141b5ce02644b22005c9cf546130231982572da89e435795f5e07bb7892b4039b905b230cc82a6be3366b2326f38d3d05f3b93f74b7ae9033ae78b621bbc45514437bb20c9f1bcdab796d5e2d0802517196623f140ddd25aa1484734164b364a3d1f18b08144955a2c75d99a2f3a7ffafde74eef1ee4813a42933cb0a88a6eead2577ac0a506753032efae614fe649d6898101c0a979d9877160bf92c3f1c59e7a429a800c138b0b5b5e12a199c24fed74ce66dd42ca4c6d585e5118cedb9622c89ac790dbb4b8dc8fafeed8354da942437b1982a9d565defda39987fd134fcdc8208fb00bca101e75564c689c75ead6354d72aac9387f6e5a21c857d3bbb24a02559100e648423201f1804c93eb2389d5e7a2a9ddf15aa21c36322a9a7f27f947ffe3b054e844c41a2d2f48d32984bd1833456b475f1edb23c072024df4a7c414566b3077269f2e9c56074a01972fa4b718ba8ee8be5618efb3f8f7c5b7e5af2cb5ffb02766a62dc799ad382b489ca933ace36686fa5be2bab5f46ca998dd8ff81c8c00715e5b58f311e9a7debda35d2b3004e959d4c0688f835007620db724810495d005f69d867e84d43f518e504a11ce2b025095deacc7b86306c7681ae18e9035609470a19147f563ff38b08a7ea46244e745e70a57944c3c4208b9ec13ca51b51069cf77baae2472f56c87badc20cd87c76fac4437cac30b542ccb9e0b39b7cc10aa080a1eb1893fcdb03ea8efe23d960caee0267a19a6a6214ad5e01d83e5dc70406e0592d03a9981de8ab898c5d21f0ce6529fbf90295c25c8a9758c9ad06d50e5cf0f38d59751c22081fb1b0848815267d0808e146b57ce5c3db4354eb6992081aa4dd878b8c01e865a3af8cb245a8504fba0bc58bbd2d5765c976a56bf56d0973953dd4a37effe81887481a824fd1e64d190e05788ab76f46fa6ee7b5adf003050289bb18f63a7d377fb0224b3fd334410eb506217e389add562c420cf52606cda6a731765f926198e4a3205a56ce889cbc1b101a082bf43acf4a16351ae70cd2e981f4f9c6a83b95e2afe23103f04dfecdf8c190a9f26f59f7203b08d8b70fab5a668a32ca542292b25291c9cc52ea8f9cc2011949710d7d076f261305f809fd3d71b4e883c05392de0066d3c8b0e15166ecb4e56a65135e9fb174ffcf0b04672374766df8afe2afb6d94c1804d5bcd3780ccaee491d7cec8643fe3aa8c7094ce4641204bc21d47fdc7996fb91ab2e88b750e9c6bf1e32853ce2f85ab5710165e03b5a8213ed9b3e417398ebdf2c56a81c755338a127850b7d474e8679870269973291d5aee6af9e538395e0c443eab6ca95d60b7cbc2f2547a5d8d9757b05b812ee6c30e9b2136187cf54fc12d3d30215aec9306130e79f4bb4edc8e35207a14433c48b2ed3c179d74522593e42f7b1b20efe2c7c3f4a1cecd9dce6a9f20be2beab55180df15c37f16a2174e1fd2cd4db8057ea11ea9307b2c735c077b707b56401e6b7aa3651c36043c2cb9346636965d385428d47d0a2003d2ce1fb5c0ba648131a09b101de6ebb79e5be755d510039925c2a207b97c78266f08e77110e76adbd9a5b798eea5b052f0f9c4f4e13b3e4a5d272d9575bf78aa8282e6523096fde213f733db952874f8ac72cf8fb14982a60c7cde0d3134b54a9a484bc1308e152ab68add431b5105eacd4071bbe0eb953a20a4ed508b53865e6686b8b500e7ec5d945feb4ddd4c439525ca24a46c6224330a2c65a406a69fcc8946475600330b300ab79fc8c45aa9016516b0d6cf139312c24f0e0ce2dc4e9c0c88f9216079b78d2b6d4f609e68c4c4d38ca62d1a5eed0bb52729fdf9aa9700f197730620541a6161dcdf3dc5bc665822f3280df6c7d8fc1d41097965e1b599af7682f76090d84e31751b9cd9c80fc972ab19e5cebb313b664314a0cc0db9e9aff31fc100e9004c3fdd681d49cece3887e54a9a1c93f3e0de1b954ccdc9f55bac540620d0f8e6f6d78e05928d14dbfddd438501cba957e4c40ad8e9b6a78a4a68e515f1a06ab686b49e1b465b2d5adadaa57181dcee7002bd5b12679321542afa3937efa0ba1528106aaa1ef0451e24a7e772f7aa068af6360bbe22fd6d71af03e25e2010a6decc28ea21c7d49b0595126c6fb99ed7952f0635d36dbe213a5fadf63cc5b0cad7ebe14d7ba3b1ff23eab00c4a7afde7cbf3e061621a01f611f3cdb20959b0b4ce5a7e7d0864f877e7487be190626b46f8bba7497cb2bd159a78ef4d998d2058d2ce6be296812408be0a50266fdd9e8a08e627274663573101aba88e8a7b50633affdb0edcb9ae03950bcd8255c0b6098b6be508e66975228ad941529b2210d59308d2d7d0db4beda08fabd216acf5da2854035fb93dc23eb2b0f982cca840077b4f3ebf76650ae40a9069ab232a146b982faf2201b37ddd6c3f78c7bf2440dc72b73873b1e9e9daa2c5e49fdbf55e15275997994e8fcbd7ad3466e62fa4501840724902271cb77afc33785f8928aa98be06c8220d958e9b4dc472f88a4480c59bbdbea6c6d4421ace05d2445467c2b9a40d3346a47c7b22588444b98ff6f06fe1a51be822aa7fc6dce7363834c0411fadf73fc91898d3b18d6f2b3f575c809d0e9286fb8d0a5b8822efdb40e88b81755d464d9233bf2a223729d08d803d20f776a74494fd9c6b2dd974d87fdb1917da4b5bac05a6160f9731f895346f926070c14c086787f6e735f6c4e218ad02365cff1bf0d9e0114eb3faa0d9a33664a05749af7d791727e5c323d93d3b6afa7c1cc32f7484073d650386455eaa5dcf50fff336c34c54b08889ba1873a13f84aae93f3a4770e92ce86e2a42deddfc7ce08437ca9339cca52a4762b05c31cbc77153c6c006f7e11b6201024bfffc738c10d684018c8c0eeef9a66f15d14c164e8de478e0801ed8ba0494fcf170317a95b0af2c05ad969c1f87bb8ca1d2d1af2521b2db118887c3b9ed16b2def7d836d120873049c5622bbf6c39cbf23223e7ee19ce007c0cffccd96c3800d6c23603ddb0933b58520a7305f33cf41d079c5327a8a657f159e78d5801aaea27f5698cc570ddef4c61c6c1259aebbf996fd7ee22d35c0cc9df19d21089fb1d2ed1feabb4d00ee08a79df3bf3be82d84a09433f7ff0b7c01345d8a5d4cb1a7178f5dbc78be02f249557834594e5e3d71e13eeb31c8139cb3fabbbc51c06cc4d0cc7553c8640f3fd621febba042036837a39cfa05820625dcc4ff9e7abdb5478df3557d2323040dac0cdd1b059729a4511df34118ba25279cbd070b813dba3072f0bc1665630c42d7e45ae056dae16bdd822d8927059eb326c52cd97bc7f0dcd541dc98624e0e50f9f4437904a6aea2a50a7f00a8ef260bd2e873782b5a897c7ccd79da88dd04037599675a9be94e75cebb8a03eb27dd2858509a7ac8cc4aaee8bf7be288a100d1a1f5b5d84aecf750ae8625888e64018f281f79032df77f0c7e1e0ffb80c90a3657ec02cf4212658d597fd343046fa56ef228badcd0a22df17be4df05cc3600e82aeff65003665b26f5c313284d32503e8d2792347f42200481417619f0370c818d66d3266d89ed73ca48692bb65f55d42000161a730aae03d12a702270ec0c618eeb387622a67bfd2f6f292ded626206b5da568e81b2884ea0c884160abd0a05923e24221a21fc0871c7df9c77a59e26275acfe1cc0be4b050f7a04c9d8f05e3c7000f70222aecce72289f269f9734397b66d5545f2df95fd818b48abc8b087ccaafa17da82f5c353df459fb637908ab8312aef8399c032a747ba21f9ebf03c35017def7536dacf4a698bf78816011c3374c33f7f324c4851e2392d22c9000fdcb5224cb941b82a188171a86f7dde4fbca23b0ff97e4e38b708c8469a9310b659a239557b126c36ee8c8925bfb206aa6b8b69e8d5193cf0c512c9644746100d6730dacac4216f96ed086de4bd648fc0336534041f239c036c3255033c0100290e644c9393b251d7cc9fae2a64be5812504967f1ca0542a1dc7625efb33e10096b22f938785cad678df170083d21937b767b9dd5a1cd588b4c4cabe66135d01a8230de1a996997e62bafbc06dbc0438849400014a3ae601987a14f99c73cf0a6148d4455c7aed331aea8971df70a556f05742b78b0111a1ae78f6fd61729d0d50755346a262bdfcf4793d8edeb61c1d6032f420c1e5e3da3dd03acbe23333076ca786ea1b5c9dcef6b2498cf789814a2e5449b3f345457cb8d75684d1877805f6fabd0592f6282faedc18298f05f19503fcb2c46ac52e6057d1feb9c7c0e9070c137c1e0dee20074f5e0202c6ac5e96b8cf72ee44e9e43567df8ff2b2ac12073b4b761a3c16b818c87c1086030217fff702ee97df59f16df1e61fe96183df0534731a1b605c5bf15c189d99ea4592f82279bf5f23cb59e1a691835f66a12c0c4409e50d142219c3b52335360d9cb17f46a41d9ad3ec3bd6528d1c7347fe8f0528fb6e1a2eefc54b07f4ba53a1447131984e2a38e248881ef88ed9bd68ad84007bbd2d1414d524abed172c1b7d0b5b01bfabb92d44af0441ac761416367fd30c5825aa99a9cdbdbe23254266f6e0c86738545c489ffcfd6bc87e693db497010c5571665750e1e9bd9fb8dfc731b84cd7f6a7b4bde379c80a09224cbc893cff03e0dfb8211478c8aa85dc40affa261ba980a4fd30706ae2d0a6e421e5a790ce0aee5edc6f665d1938a8b08347743f6dd7399d4eca7307a19a2f28afb55cf9f50a3766d43c002c01fd8b4529be2e9fca06e656886dfed271c66534d587e521270f713b7870834ba168f5abd9cb6b4805f07a5813a886edfbbc5113ddf47f7780051e2d66ec0412c983d8f3aab42e54894edce5509e7bd9c7df9fe046fa1c5ae4037ae48f7a4fc3c1aca6b97e9f12dbd445372131f7f54805ff513a86da9029cf0d2d8043096d55f956b1b68c0a4d250a63db984bf814ef5931cc3214cc146af402c8c53314a1e35590210301520b13ab33a0664cf4fe0b9237c7bf183511a0d902d31d6d32432be73a0291561e24654600be42dab61283a8170d5d8d11b82b340ffe1fd40f5e4ded27174603b64cea9e7ea864fd437cbd16431a5144d299b3920ef8e32de5b5659590a007f91e7e4d7d067fd7a06ae174b9da1dd9f3dfa9bccc05a538b7361662cff926db9ee1ba0ac71d436ac39c7bea2eae28024fb4f57c7502daf3cbd4ee583942ef77c6174d6aa87108bc6263a3f4b263d032d602b4fc82025917855db13a8aca1892ecdf46d9cc54f5e42a28fdeeebabfb087964d7131903542832d8c9dd2d7ce2e24eb781cf8208deee5265aed20f0cc89b268ae64d9f05e405edc61325ff9363c52483b5bfaf51b3209a28ac2c3585ed2c6d85ccf9e406f8d3b23ad7369199309d52e27b4b0eb7c361e0979d30b801ddd7569c60a75e0c4e4af3a8fc8aaac1c82fbf90b61365bbbd7f3451615ff16e5e7e9142e889b50f7885c7e6be8ad01bac527910717ae5e191efea1225aebe32eed84cb58640b70a27c3ea3225e115f83d634cf4e4939d4f004070c46a4923950c11585a16100c0d8a1d2d58442d359be47816297b9f8ee86dca71ce41c64bb4937b72b8f17065040795d1f459ea72729b9c18df239b5ab6c011f140f8999541ce71badc6cdfd106bcfac50b3da6001d8991c2a8b74f1697c09832c1ae5589c9ee7b6ef82bbef0038f048319e4b61d9cbb73cd0a518e3d5813f1cad507f93761ead3d26ee44b78055451579b0b5f57117d88761cf485c5032a9530e87b7f5376c42971f542eb620e89f6b8d3078d61cb74030ebd6b97e020c0a88a68d55281b966e06e200369bf027a4a07ddfec68d2f87309b99de22b7efa24d4184d255c8374a98c26dc7960f0fc27db7172709def6300be278059c5f5dd47c0608d75fafa2af3680aea654a00e0d3c379ecfb778230f8b5c79bc8c500ef134e0a8dde9c7a0aef6f41dd40b0c008e1154a377595ebb00c734ac1b1c03a265235a291318d1d0a5c62fac34e44a0f13a31d68a116a324d6ea592184688a19407a2a4b1284bf4225c781e3494cde0e62c0e5532c186f85778dcc2c5f353f21eb26bf5bbef7118e6edca6188d8fd30c13d88fa68ce22a7c303a66d32c90114866149b6631a2b66bbf7e4aade3c9210c90bdcdca6777b2229116913e8317eaf2c22de857558a0fca69c244c0a4698a0a30cdaccbfa5d1b59f82b07644d23e5ca7b02942d5a34d3cfdf4e0d6fd90e590533fae6414d36f5a9d4b969f6d402e2d6671ead57e9cbe36ce2bc39f85db8e20da28978ae57520c2203259ec20d2bb9c7ad90a9771894b7798efa7040a52b160d21d724c3e5e77fdaa86717414e4fc24d071c82a568e2fb3d1eb5e9eb55181801211e171a31910c95043b51734725968440c619489922b30d24af9cd8c202da0fcbf2bfdb0deb2d91abf0a1f317208cdccc656975bb90e92284a58bc89530610777bf308c4cc2836a58f10278ef7f40f9c11250b06fe6adaa2612f3006935b605aba9aa4e71c505230280cfb33c4f9d43d1fe3ff46a8232cc8e922fd02e4ecc03391646ab0e38e6e0d0d257b7d4ccef84d4da1b74a2b2a973c9b140f7e16b470c8d9762f54021c3f5f3e174abf661d9ecf035c4b07139af8fbb506ec9997f240c6d618a1d95218c4e6d096feaa7b11621e64a5199b0ae47c612493742db32690241a54a9cbce058497ad2fdaf765863966513c5f4add80f42d168abcdbd19fd0ade2c90cf1039f224667654df2de992199cc54cdc8307745a2cd85ed89c52d608b6ffdd7f153c56b5ad1d6af717045fa3d9fc70c1576647380f15637fdf5eac08e8688a4e2f61915003efb7661404fc629c22fddc6bcdf3ab9e6270a4cffc6608573cfd4bb2e00d975708d25925cbef55ef560cb52532b301f495fa7261782d048e30563cbcd10f5cf686f512c7d3751bb8c0ed590ba99db13fa20cc2c220e2020b61898f745d4393665acec6f911f3f2994646eb585742b57593326c92a0db0335cc2046bef7706fda3cb75bba9a8e0f04a9106761c2d55ca32ae598b632ac00c74101ac1021cd0f6cdadfd17a0ec1c1674f1e0a20e1833ed4a3612b58a7a308e63995271c11d2d1aa05468b3791689d4921732cbcb18598be5aa66d0b003a08d2b2905ce456813f92d17cfd5775122ada6889f08b53e037c657d987311ac50399ca30172763d14d83b019e2dc02cf0879e0e3a19c875cb5bd4d431b6b9e220b7e3e115bc88c090ab2547bf2155207e5d441386efb94e7e42a9e3e86bc0b650007d9858388cda07d50173d55e3eb53afc3ac3e6499426a4663581b99950dd3041b2c4b78d50f6a58e7ed0aa3099948b8c8e78c9682b79217ab30828055c9160486bc5655f121ef61260b0c70f5ed0f0ecc49499c389a52537b3817afcbb85605aee499f3b4ccea7dbc228cf36bff99b8aa14077213c58b6ac5930dc4084aea0c8ed5ca08ed17e4197656acccb1beacdbb6b085d6e5eda2459ebc8df30bfb330626a7b74ee68e7eb8de0befeec964b9a01ee0061572d5956d7b23b8e9acedea04493429de30b638d1b7956649f4e32d5b49903281f62202dab2f4e8569929970bdbf52ce7ae0f220456f746b75b3ea514653d46546048f4846886b3db1c85130aa0d868c063339c2e4527a6d4c1af522e679efb5f045c4de8de4d4cb4e7cef50f7aa6d32449caf518bb66fea7304096af51d1931fefdcd48f8a38d8729401e9091635769b127c1f3a1f735c3fbaeacd7aedad4c166f0fd7fb2649922b0605f402c8fdd82893be0310ef61532b7e151daa19ba1729d86bf7444bb81c88d9c73a0be767bdcff2b844f4ab321e9650f58f88fedbf6cd3e9562539c81ec8a07149c00ff3f7db87f48967e0e9de3c1edcf27d12e1944e55265e5e04d17777a05f24602dda2d98791bff2986db41a625b0449eeac2d32eb53a90441f464367f29c17c0d605b1a1c96809734521a1ed5b8f5a5ef9b30189bf8755dc05c90766e4c679d0786b7434228330cd49e12508ef4fcdd215d7687caf4480c7f2e45262638f726004d3871195e1396b84f3d48df82231bdc94aeba02877c9d13229b94418de5a80b12710228511ff829e49e4630904cda6efb7a427b65c07ee529d4bf08512e17079c5f39fbb34b55babcf68fef3c4e85f0f29d0067b666b2053536254447ec910560f980e09b2152a8ab7fc358c6118d1e7d896d821f72aac97467aafb89ac0c0c4a01f691b5b081dec7b01beb7b8adeb49569f854c87eeedfbfb93912a86f7803774c5962eb7ddf49fbe95fe6e4934fc23275a43afa05bde257eaae9fc6a3d00cb509d96f34c293867d7fcb41240589743401024cbf9ec27ddd5e72f2e2d86a0695eecbb341548701a7b1d740dfda8e980c0fd42f682960713ad8a24f0e54f70f88d0f55b35382e2d184fb08a960f14a7fdb6a13902033adde246dfb32e069b04f827d84593c4fc24d3633140a900bd91b81d638826b8220974289ea11cd0e2060e9c7a6c838a4fbe71bb7cac749992f169e3ce2151214224c3840d104dfdf200204c29099528cb481c54e782fff8d98ab359104132b5c7806cfe2255abe5c1003e288c82c4ec98249bf95b951c0fb9354a62c6235328fe6ff50dd94eea85a202ba7772360ac00c28e27e127f5d07f9fe3b6f5808bea2bbdac6aa2219f8d7010c false -check_ring_signature 29cb1acec1b78ad5d1db686a433f8eb0d6415ca78fce6320ec6cf82fa2e04404 26eda6ed4cb114532ea1989e445c02d82432437131d3ddcb6bf12c43c33556a8 30 dad9a4d8c15541acfb2506075834c5986214eb912c3b39aee1e1c2371884a9d3 d948ed2db9633a77ae0ef3ebdbbce7639bc9b8fb2e0f1c005669a28171285ef2 2e7896540f91111bc199f14d71637e9600a1b897ac6d95d614fd182f9818ecbf 889ab04ce71b8ef48f32f9a40a4df7cac799e47db524cce4241fb059a3816d1e a852ad60f2ff338c21addcc02d0f99c434d55c792eb46a9b1cb515dc5d7e89f9 a218dc5e9d8adeb68ce33eb2e1d3c891e01d98d1b5811086d55b11c3deaa404a 1dda149ca044dd9ba75e65f356a13a590318ea7528957f87924aa1d10d366ea4 5f810355f195f8af32401bc54aa82d6ffceb547fcdd4c2692848cf388f5bce12 db71d3d368fb5ce149992077a4d303028d9bbf3a436fff35dc687bfb21c0ede6 3c3e08b456673baa09575693b51f1143c93ba99776ed5a0129fceee748214e03 3b61079d6475ef1a48d77591aef7880806485c7d995a85664d18bd1e2a8e60dc 8f3b44b68aab274207093b9d7e93e28d30a2acace5c857095fdc0e84c373b975 ce7523fb61d77be6fa5949c02de41d5ec47d686b449827f54a3bfedbc2f9f50a 9d30b8c893fb0a7a8431345625ebfc0ff024192b057a29f89045242f999ffa90 a31690b436ada1a9ae73e7aef9bb8b5e9536dddf9a59ef6b4e9494b8be47ac89 ffe6f589b46e573771a44eafacab3cb43c93aef2f7363b1fddbf54722672f229 5515a4592dc3d49c961e48312413ebe4390a9154ea714230a05da2f9ac74b0ab bfd6b23b44741a608db142b89485467c8ead49c6049d0900e8880e9da0a25488 167e42e9b99a14df7e74eb7d1d6b3ba8c1d617946b8a6877974ce08cbd2edb18 f62150db082d70d10457ff579988018efaa6bf0d3cc9b3fb58e359a9a7f61156 6f15730b3d00368af6e1d23a0dc6ce8d1a8da13420e10435812a2ba6e10a2d14 7dbd69b941c5925caf8ed23d44c9226b18826014eb61ae86b227c7618f6643eb e1bb50fe921749a50bdb6e5d3b59789ca4f474f5884b868e494f1050b5ac3932 f2c9fa0cf429e9fc7762adbd496f91016782b137cd2b23ebf797e28263cdf02e 2ad905aa76c0b4247559f3725886c70c6e82fb0ff999ed028649ed94f9d73213 124009bf1707451e7cf4843e4291ff267554ca985a29d8e3fed50be540da40f8 f5a6827344dbb6aea000b088e5935ac46945f22871b16f0f6efdde0dfa93061e 5256ca0944b5e7eceace952cac1305a80d0b36c5fb5afb88bbf425cbae1eb177 727bda6e244b57291ced3e6eba7431255404f3f394bece0b0e46b0fa2d379a03 7ddc533b97d6fa86ea591a918bd830e6e090426e91ba55453cb57c604d796917 66182e00cc9f8949253fd80eb06730a79fcb4dc6715416e765045518b665e409bb65b178aedcb75c225c2be3675ea521fbd743a588c943bb164226353e1e8206eb24f781b3c4acbeb92cafd054fa6362d91e9b860c146776c77810d18c2e1202948cbf73df99949d9cfea4e2348f6f29f691fbd9bd3a397250f0b4cc2a024a0d0e9354b133bce0ba06a168d4a944033b759a3530821a38000a778e449cf79d0bbb6dc8924c3e14dc77cd9c8e33deceef188a152ce580938652a0f4a4484f460baa1a817177ac1bd98c18668f6ec828845108fa41153cccf7e9699dbdbeef520363df02438675bb133292b26f4eb557c8f9eb90c6d1ddfc39fca126e8652a8e0eedcbb36c1108ed07d377ef8aee22e8b6951ee23bb965bdcdc9d84e0b09261b0a3e45fad07437a07336e23b6c8f1099e7f566b6e9ec714f6e4f3a03cfcbe91c0a75dc0a1a56787ed5b928ba57cd19682122ce1469c81930af6d6b552afb23ef0a1cc047237a41810bf54cb66959cc0d1da57f0d02c319d995b6f0a718ee542f06dd8828278cae5f591fe232a14341dfdd65a32a3296a3d3467a565121cb762e0cbfe862503cec15950a681eed5e53cb0067d9bad61741b47055b1e82228f0aa0a3602b82ab486c44ee6bdcf149bfda46b3567ad506acbfa41d30c66368e8f9b0b93ecc366de90f0a79dd5f4e7344e1003b852b76e345ad2a088137fa7cffee2010b0c69537272aaf4641581cdebb7e5bd2da175ce550ac541b5673c9f0802f5074680529a2f55ed2ef103e1302bdb441b4edb71851caeb970df1e6e6f716f250f6c6c7b91e40a5a0d8a500ca51ad3b6cebe6f6555b4e820c588871fc3ddbcd90ab874c20522bd36cddf1e6660babd8c690917c7b7eb8911366bded543e075570b9c88aeb59a7c1c4d52aa0fab56a29a1920674a8115fa2ebe6a7c7ab818ae3f042ea0ac910a7534162022f53673d4736911275824ddba8adfa43190958f2ae50d6e35dffeca5f1afcdb409ba2709799e2fd1a4917a0c3b1fe9e48db53bc482b0d3bc8d9d7456f6d5527ba91e4f7c31ecd43d541fa2829087d79eaadb530f87506c63e3e7ad45b3a4255903588071c0a169ae6d82218eea8a515292abc41be080e0077e8d3d7bd154aa34ff34b681a4c86e94d01160cfaf355664cb2a4919f10008d0ff9437ca4ab72652d17517608af499821df19d53c493279b8e41103907a0a8b0a96b2b43aca8421e2f637a4c3f22e8d25eece53ed8c78d088165d2fc23409ea4661c7068c838cdbff4602bf09ab3cd5fdc2f8a79bb270ac87b255fc62d606c378df7136a25c02c39fcd0816c486831b6c13754eb68b6d40caaf5e58094a0e342004ccba74734715d33bbe2b45e9fc9534c6f87836a44b1d39c8ef8134cd05c94fb9685df2d1b6c9148edca59bfc8747cc07ac724ef9133ff3e2683dc5e4026ac6b127b853132ca410a4531beb39f6d68f4df6b60ec2e9c6b68d6b0597d50c853859588bfcae278d27b7e27160045a367c30bcdbc04e2c812fdf6dce6b5d069bec3dc4d3adff8ae8cf212098c03ff885cfe3687219e1c3c86ddfd421224809fef4aa6fbcd059eb93ae39d0b4d9c8756a88bba53c603d2e84cf0cc4cac9c90d6655ab4bc4c2a97844d4ad5dd74af8b8b372105faef58bbd27d11616bccde0065a053a798665f251d3d1cc89e8fc6875b7b7eaed24c455e2994d465fcccc1c08f9dee50db38c7bb6f4ee358e2d171cffa6e3a5cbc1d1c719ba8eafe57c91a5000906e02e5b36db09522c00a2b69bc9f93ba2be2cd36578e1c8df73150eb80f09fde3f67d13784cf5b29980a05b4528b13992669158091b4486cee2489b8b5a022989a8c985032aa44f0dcdd4da88e197199e293a25c22ef43b86fab1ace964059f3b7df78564806d5d2ad65ddfd97d6ac39d4ffe0fd3888dc04a222afc8877026f0a43708bbe0b0becd38f52f4f73d41429adba44e7d3ddb2f3c914860dbd800129655d1284511642ecddb114a56aa0cabcf6ef0858b7a84af0e3d2324bf1e06d33bd8c4dd195d037837d94cd12e96d04f686093e37bea0230b2c6cfcb3a5d0ca5435d83433023f8949bbb927c1e8c1af982f36f4c3b38aa1ed820c3632aea00302fe98d66a1dc6ca94d4f0ce74c242dd52e75cd6de0e8d47cdac6ca29ba580b0b8e6c9323d538ba57b7290e5dbfee74f610398803e9b1483125165ebe4e97031549b53190f462f254311c568bae1d3954fc15f651c4c51a8e9db3c05dc88c083c7ab8c8e499bb23ec8dac0058a1fc0c59ba92936a8432a2e669954565077009144561abed88c9fb8c1c48c547cb4109c971898e7ede148507e7c35deea6b00bf8731cd49f125482bf59f026f04b086fdb1d1f95850ff253878f918986901f0f1c99291362a380461ae2ad77a807ad84288e86d19030b0fae3d28ee49570e501a72e12b6000e3653b0a87fcd2d0fbd9587e7df294a2605b68e94d0c2af9e590ebafd7f611dbe246cebdad3a272b7d86d7639db45a00900638fcdf90d9655180336e372725267b1ca122c2a09c9b1d6618968cb9b2fae37d0e271965d32c8460a36f3ec11f8e64323b15094c54e75862d7a7d94cf0566101077495b4f6749750d97bcacb3691214b70d622e0e9aaf2891ac82b17f25fb90040fdf14f547ede50ae69102b2e9b7f3c392f24a655740451935d56d69e033e35a35135116c2ddd70c false -check_ring_signature be99c0fe0eec7cecfde67bc8f305f76b039cb4e6492bf127e07abd393e843f61 aeaa8f2827927ce293100d29a7d8e86607ceffa0fce5e0fd78e7edebf5934f8c 10 0a34e4a65305244a961661934aef8f894cccbad274fd94b5c790246f89d131d3 24c78ae753d122dffcbf763d5616bbc3dc7d433a497b6a0277d33b05e33ff753 c4c912015eb815fb598ceb3fe369c322c7bcdece98f5d632aca309ceaaddff89 35452f5bd7f04e04f6515c2d81e0ec9fb6d3fa48ff55c75bf4e81a35bfdaaa06 33baca075025b52aa8802f23e59374b7373fc8c36f35702adc0f3c64c39186b5 31d38f8689cd037edd55ec3d6cd48c19ab741005755a00f41638b4acae331c7c ab9ef86fdae256ea7aec3158f4edb73df76d23344f247ac6d0361ad97bfd47a2 67990ccdb25d579ec84dedc871265ca5491fdf58a07f4a526dbdf543b4f67740 77bbd9d3a9c46ff7e7578214c5aac666da53907cbe25d98aae030ab26336a262 dcbd8f9708847dd79a936c337717f1ec214ff2c3c010880d00a6f9f62d7b0e2e 1f7687c9113fcbcc52fc86d01a8570814bccad6b4ba4960c35c8321cdc5e710227fb3a66d1096546034006dc2d5d7150b7fe778d85e5ee917e0bb531e6bc780f525fd6b113e957504d830899084e97758e762a559911c36e1d00fc6c943944060b737b197a397d50aed03158c3e171773a788b967ea16a72dc1d8fb2122d380b4f19914e898f516095bcaaba4f8ed8da12682891d23b4ff4d3bd2442eab3f10b53d570e2f0dd8d5ccaa6d7ded45307cdcb02b3b70bef9f8dafa561c14448b80fdec6d188a556e5afcda6a1edb8c08ee313c9a7a37c1598e1fa371f0ef650540fc4acc3b7e9dcd9d0f7a7a428fc2a4a77eaa1a994461ed9d04ac8d5d46ce6910ff67207bdf29d5a6bd83e517b88264ee6b3086edf13a00f8eadff7c3f12f36e075f1ce6b8726d0ad6ae0f924617a415c90711437282b1d82c371faf40ca5c1e0718187ce2dc1b2fc8871cbdb8269d0deb4a82704371a4b07688f1e1be6ede89049d5cc4a3f82beaaa0873aae47a5362c04bf548da0006bb8916477ae30fa7d603bf7c4869b55420d9cd8ee4d13443af5ba517ad418afe1f8d4d917cdb0fd0070f4663dc6cf753b1fd704a115a707ad0666881607b87d2d0ffc664a80a077d330fb2a448c0a9bd1411a8d620fea89a3007f371fc49ff4caf317de92723070fce0ef5aa6416996723a3f67ef87a06a4ddc76c54a9fc7bd02f007393254aa15f960f9fef7d7a0c892198ed3df984719a4ce684bd68cca39dc1b021411a2853a2340f104338fe7b53c5f85148a2007c625ee7aa54483f8d5a575185fc9615744b8800edbb194fbe1c27c93d7af095919fbd940a84feedd04b6128c67bec33b38ab50b6cf54a18373ff62c2d730327701813adde909cbcc48de116b03113db1ddf3e04 false -check_ring_signature f716fb47a15374c921e2f578be610a91b5f6c7b404513cbc502ea7ecd01051d3 f6e1b030c9ad59297684bee42e6ebbcbf5536916eeb24c80117926e0cfdf2a4b 6 801fd7de390e6031c9af22d1050220bc154010766e3ab26e748530d604c756fc 9b4d58d7814c40f26e7be24f092d4965de2a2ed5bd43f7cde716bce31b2ac3ab b8fd21ba72bae2e2f5c7c7038ffa73b5ececb18b458f90f8d7ff295fc06046b6 20fbe543be5b2d37f9c6af7b1ef0c6422a07253fd6a361f8008dfb43c14abd47 2f4a6166e85ff967309fc016beb16f6dffe6dfc20b20a7e5c4d47d0213ace99b d55387ae3f366bfacaf6cf6d10c089d5829bbb1fb73bf9ba2b3b9b3451e621ef b4de0a8baa4b02a96190342b387902180635dda31ae3b57a255a2e58007c8a06988d918faaebcd90acaf58494a48470044215ea3fc6e96d59e32e98d4d73500c761f6b824d69ee4dfe61ee9298bf56c41c5ef4aea19310387bb30f7a92ecf60fde16093790ee6d8bc8a5656f481d4315c20ebeaa7ae104ddaf345606ec69bc00da6c2eea98fb7de7e6fc45f3b4e76a286bd912d4dac83bc2501c3c0d8413570f6f34782350aff48101f72dd2c1fc8543bcbdd9d1ebc627c9c54da5b13cd41d9f92b2fee63a26af637f0acb324b941494cf6239ff8290c2bb1adf05b69062830afa0581a20866d8cfbb6157d9ecdf2af304e4fd591fd2a30d6081cdce4df03804fc13886209d6e46b8051954091e83b16af80e95ebbda3e0607c8f25f34caab01abbc105093bbcf5b3d06960f3036658862360e9867e2e4d8edb2f56c228a2d0ca98ebb516821c154b90174dbb414298977cc1565b7132f1745f49ed0654c0b090b102a4cce3f657ee497db696bb0eb164de747d24c1e3dcc8ca7e39355abe7a7 false -check_ring_signature e183ff3ca131a4ccfe319d6df93452151265c41ed1c2c30ea12d17639d7bde7b f96fad598701777c7d98a806430b542fbd6a35d24a3b9a013e2dc0ba93c4299a 2 2eb61a76b321a3695292fda7109755f49d25dc294408ea491ed7c13d5fa1c46d 5b7f06b7a12d72df6f2956a82928bbf139acccb72102e40344bdf63d377b9ce1 eada62ab9dd2798906f6142f9c31f94d236b70aecbd4f04558b24da10b2ab10b187404b5508777b8588d10500f938f331fbce8ce65a3d5be79e75bebfcc6160e6a2dcee31df66dc9af5fd1727e7708a7d7265ca0ab26cb4c81c5b70f67425e59c9df3a4a637701212416bea1004e0de0a410a266d71c9a43eaa4ef1777083a03 false -check_ring_signature cb283182808f49d2d0da49558a9fc64d9d6cee1886c982b5cbf3ed0f54ad965d 00de308dae52737a01140bf6a63b420be4281ecbfeed788af1a4fc30afc7429a 61 eee658549f2c2b8e1fe5de4cc8ebb67a0ab5cc2132873d93c3bbf756fbd615f6 95102da66ce8d5f62667e8b17a9c1d8774b479a3fc96b2fa0dda029f210a0559 bd41d813b6f68c8430c8e4bdcc6c7e820274ff99fdae972803c770bd7dccd5a9 5b2e2015e04e7c042d75e5cf1dbd243107eb1b473bcb54705dbf461d33ea6040 bf2f2a6d476d27cdbb9f1c82702858a73a91e5048818185ef85ad073332ed710 a4ea413567d857447d0d6630f1f37db39a03d3c0f24a3e3f7476b23efab4499e 53e5d780cbcc9f23e2cdfccd0ac0d0490c869bcf908a3083938c814e9ab35171 e89ac69f9b6cc93c7ee732d5aaac33b5b65a367e31553817057ad84f6caefdeb 4e4522a41f739496177489e4b90418e143ccd2f0b0a5d8d244ae79fb3e9d2163 7bef8e6e2ffb6c2fe458cf2edf30b77b5d8878d116e2b9b903c8c0a582e98524 5593a3515893e77f1f6c764af932781ff95290bb8fa665adc1227e9dfcdb0214 df13ccb8b33e24a7df19e28d78418ce69b26d13bf6967c3c8c7e378f80301fe4 23e81f529394e60e690ce5b07ca28ce1465d818f79391542783f412e3fede527 ea04923d98a5f143330b093c98dc8fb8504dcedd20713055bfa7f3798056202f f65b5065fc8fda3a117f28eb61a34d9124ac546c01bb82764ba0a50bba02b945 bd758462243b3e3b534db06a1811847e7abb89e7d9d2e286602043e31c5b6729 b5388eae0b362e024bdab7d3d0b4f2ef243789595155502e93f425f143de6fc1 21e074e80cc15b1f2dc58c4c8a356b09ae301c78bc830f3d81ebf605c4b39f17 fc7db01677badc255e4d6c0b447f65ab41149e3ae176681b914183ddd954037c 353d46641b2a4eec8bbad875d9744fae1f38169a4d9e57631f2de6fbbfd61a82 567e34110b106d3846820e6df4f437c352052a10db1d9eb39228929fad9338ab 174d262b188d9eeb665e9281c9d93adf6d849fd9ee269aed2eb1124e039f8db5 5a7fb9cbf73c87c17e7bfbbf1a5b53fdc5bc19ead3012dbdd75b41c0a5901723 1ef48ab6a97aaf2f62d1b6d36aa853007d724ffbf0cf208fa4b162f6ebc984b9 67bf526ea584a7813543121f73d022efa6d2b2f173407d4d47c4109b90b5a2b7 7edeac31cbaf454fa84f2db32750e3f6ada89c1b177405c6522cfa75cd2fc100 dc43a11ee4c01b3d44e78972f2ccd1c94bb24a5bd398d12ecd81c81a12f7d785 96c4ba6834d50881e4453dc1415d14e2e3f7efde3ce2dd617682981445c8016d 2535d5388efd8e8f391f448c601cd3c29f59492c23e626dbf2d06529af26dc33 587a0f1b1118992e1f20e76f58165ea39fddf34fbe796089c4be781ea754a5bd cae61af859ce2a0c3770659341563e04adc1b70f79debdba1417ea9e76642edc 86c6a3d132ac33216693a1acde9863cec06d654b8b79f5a4f614ce61407d3d6d 0af557cdf5d70a74c3af02ed35797bf6079c659fec53a8e33cd48bef99a3bb2e 4f912c6150091d7ada34101112d5b5499444c3ac5e98daa214638d388899c27a 6135115419863725a019353106d43171e4e1424908f96c39db504188abba2d67 c9402d88074b575ed2b985eeb74308b58328101814faced3f759ae05f7285a92 c98a77ff41b99ac3ac47e1c3e1435761d53d62327c9e5597e494edd5bdada626 875d2f47dc7cad47b30899c237f5e064038cef6f52175b8c548517015119f546 095423aeb0600404c3cb47ed7d60ef286e3155d29aaf446a6be9c2d2f31ebad9 6141857287b3a717dd7c9f87abeefb60309508954eb49d6e0f786c1fc8e0409b 75a0de8cb29c4455fa83ac80770455a88819e0d9493cda3cfac91ec547b384a4 63b8cac61184eb74ea7e9482791826628bfda7144a7ab6eda7d97fdd52595086 f6bcac6fae20666e646e34e941104863fa38c3d42335bfef1827cd033108eec4 d75336caa5e776e527bd85291bc314e53582407c9ce13134ce006eb3ba07570f 12ed777206c5056d9ee718da2711a6bb578bfc8cfbfd688b243a2fb4c25fe341 a437cc6c5a23751dc4546de26085c386152e189d33b3cbf4a90e604289b40c51 e44c771a92f6abb3278f23b7396c6d0cde83127ca724e146f670c7b47271c86e 25f3f2bd85693e57668062b806675ca5b6cdcbc2b263b39791135fc479f25f49 7510b09c4709c8150351e4bfe77616508fba74006a20263f995742b98dd47c5e 52274efc6fa364b7136e7cfa27a3794f3364afea69f79628144bdd543924f0c5 3236652405e157b7fc85010be9a1806d03556f845038c47360a685935a68dddf 6c45b2102ac58a8707d5fd40f75c993d90f6e173b350aa4ce351296a85e3814d d6baf743ad2d0fe8e67e589b76dea72d3ea2977d3c3fff324f3165ad083d6283 6b98799a1073f70debb91ae7566540b9857ca9ec70277f60f2c0e99c97a6369a ab4297416429a468b4a283f1c9ab55240aaccd177c2f551536ad9197561c2b82 99629c2c4dfbef9203d3355c5a6fb0608960386e54cda88f28a4855e9125660d 7f48ee2ac193904c71f7bba02a8c31b6f02167d7bf5dba703f9d88cdc729aed2 e72db28479098e55caf95e1e91ddc922e95c5bbd478eff5f7651e15d082388c3 141c140b5306509bc12f8adcbb34e923834411608fdcddcb3a69e475e8371b2c 016cd4459ba5465b31b999870ae974db6ac87d579e9441c81f41cfd10e34d9dd 066852206dedaca5c70aa69b0d334bc88d5eec72b3b1805c164d9680675f8147 7bc94f0cedef55f0b392cdc8225e18950dff00b578946dfdd66ba9a09d58a80331eef4131a1250f81078a4e4ad9ea055294cae597da93daf7cd51c0a8842610c969ca7a318ffd3dc4ad37af847d0b460cc77a48d68f4678dd2565a8306774e05ca78a593a09142a8b1d1d4c83622ca7bb5d95e4f195238a21d8ec9cd0be7d106b156504e428709b05f9add4cebabf3fdc6f472b0f6d297b031b61e2b4650130ed790c7638f2c1450bfa3df8a6bc9bf7dc3e79b3ba4179904443e5609e690630b259bf906d158e91aba77af37bad15845d2b17d3bda69dcb1013211db3d3bb508f5f76cef1df73561d5aeb82eb477cdaf13356727e37143de7247c6208c79bf00f9ec2815553fec344b6cf94795a8d70b2356973094329d9355bad3358975ce0ac0d0f0a3c930d937eee9f393b559023bfdcaf4b4ab849e3370f9086c064ef70d73d3555f83d675fd20bfa765472e60fa3662cbb0e32490bea7bdf0a596d769058011ebafabcfbb6b1ff7e482f4b7cfb71284d2973004fe0b717dba67718f9a0fbf271b15bc20eb0c1e1c45d054891388e922ab489c9664939f46dcfc3192fd01bfdab4c0590688f158c59ca6cd5c039f83ffca8d2b1e1a719bffc79042279001e1e5426d876ae7558c43719c0c15754d1c5959fbca19e362b2845d3c353ce60381e1dc7a0bcf4401bc4ea2d065a5c9304fce5936707e67d508e7b61596cd11046d5733e6e9558a5d3fd7b9a9370632b0091e37d00e94e0b0630ee36c255ba40689f7c3ef4bcb0df5320b58a0011d5e23e344d1133c8e764f78ddf723620f3605124521ff3d63aeb4d7901d020796eccae72620c91506d29b4a41a7e63a48ab0d929c51d713f0b43998e9c1d18d7f5b94685556125c14ad7b3e355f4e3d4c1d0ff3680c5c869523faac89442b80f4dd8a6660d57db7c40e6c885d0e9f14d09b00479d426f6fa0a9653d7cd10985366099b5ebe4800d1dc8ae85198f52a10f0605d701dc0c4ec118ac5627e310fd845fb1c83d13e28aa2f08e03da387d14ca78058b8cf73b83603713659e6d9addab0727bef19c687766edb4bd0af7dbaccaba0d633a0bcd9b8f2ffb716c8f0f8b45b3121d879d1f21b3d1a43662152f8cc2d1057681f2541cbf9ba9b0518bd0bce4908ec0e43ce464ed3730d1819cc4212e6c0471715c28a3682c984b56565b23306fb528221bf370bf71c16d308dfadde48100ca256a81e71da95d09d7a20ff00de9aabf4397104d386d7c6b63ba7fa8743a027278100580f3ff2ce8ba5a8a9bea4f6e6800ef2766e6f8a354d48af57e481a02da42858f74870aaf5a2db83151e4c87f844a33f7b594660c53f8505bb559af061456d01199949a71060f4be301bd2e8eb642c4341240935ec3ba72bed59a690193e6f63ae0cff5630f8117b5176ccbb6fb5fc8ff9db418a8f17b8983f2203d052624fbe1f2e9ea161594d1b781ce7d534dea0f11f654b20fdf00700b01894605fe7484678c1081038ee735409585abb218a7d738885384cd7bc8f4433e8e5700ea6e7d739afd89eb2a82f4a30ddedd86e499ee5225345e9535ab5bd6daf4220fb93c884b159a7319b47b30c88a63b393692bde598294da93624324b32e37500a25b4f091aa8f47ee057889b4e0214548f09a7b9c7aeb6e56805ebba011b65a066b879795c8b9f58a66be9b7fcf7e1cb6100a3573dafa4e7a2d96751504b28f0d6df924e88d2e035a3ab8d8c61ce72a8840223817955944a621ba18c4105f3c0df0c1a7d7a9ec74c249538af1e7f732dbcae14fc9389b6dad1bdf312cf0a585044a06aa1cc40073f3bd34e6f2b94a7681789601dbf4ad8d9a959dfe58345daf057f9247e11ce002e1b0fe1867ce5580b4b2a6f577ee94ba67a5040c3cc572680c4ce2263428c27e68308bba7c5cac07b3651cbfb8d20702b1b7362623e58b100c3edacb6422d0a61e75cd9e2550e39e011208290d40746c7fd1049eae54f3df011330541ee7f62c8c11da64d3f1ea0ea3471ce412c996cab107084a50ee52ca009070d1ad681ba1fa599800f9b853d3033c52262e34ba852d1bd91a0c02ce660a45e918b4f7ffc9f5e9c5077f0bff9d84026cecbd0d59e339886b47c6b2446206f3275218c292be598b82c65f9d70ca754fb7ca5e87fcba5b9e9d894418816a01cd4726b8438374a0ee06a52d5205999f3a69f92b63934a85687b739e44eb1f0ed1f0a001150649dc7a8cb159afe53fc92833ce62cb1b88a16e5727a5f6eea30805e0232fb7937975c707e5905410ea63a070edbd82d253c45cc37e295ff80c0e541a9e75f982f48ac8d442025306ce9a76f593477ac4553b273edca05b5b140b1308eb4243d6b63969f7ea90b8a708b145d8ebc166981cfd64e3dbd100682e0de0a93603cb430342003120fc5b2f7489fb803ec933dcbf91036b7998b598cc0bcceab2780701f1f4d6ebe1f557c2f62bd7a6cdd7db6f13b40d840751cd67e60426385fa58a432f60734cd560b58c90a79f97a649949d375bddcdf9345722a70242d7f14b3143e68d74805e4601b7a5ba69ba541c9634268d43def30e73795e0009887e673e0198de9928756e9b0a33da3ac048387366c5a7c6d5f413698c9d0ee7f0a1087e47d330c63d7aa6898f7696360654a7e125f3accceff478224f3e011c995d9addd17497012a73753ec0113b416f8bacf508cba4bb918ba20b748c05d85119b29bde534e6eb87d4737e07dd13ec487c4eac878d2d4ca6c74163f770d8b57ffd1194643bcf211b97a7200ca17b53654213afbf2404304dd29222ef100eee1d7243ea3c6725f40a97a3511ab55a6c200b2d86b4e5a48129359b87ef206981ae4265b54048a433b82caddfc66501d62ac726e209303ebc3a7fcf853ab0b3df0f1c6385b751c61e5e4c125db9830ad2f4b9af0cd311f3218cdc8447dee08905e534ee5e5cec5bbe8ad6def59fd84e1ef0e9de0b4a82f926901438b91090ba8a6df8d4c42833ee858e1c83c09997703b6d52e16f9ea424942826da2b170097e1b1f4fc3fa7958cdd9923c61eef8142593eb9e3e3a24e24281d783bd31549d22bc817aac90d4f8cd9b176bdd2dd7bc01ed5253cd705457d8a089471ec8b907b4c6868896a86929aa0dd280694c228d698a38d8bc377bcbcde2e7088b4fdf0f48119fb6d12d3bcf976fb3fbed74689e92b6046b8dcd6e9e82dfd6a7e9fec109951f448bc39f5c1c4d73c19caf1761bf1462cb111067e4ff1e1252d4a0e66a0e4d37f32dc0c8490426d794896c833a740095f88644d68aef13e68b01d30e1b0fee5d3298e1edad83a1347507fc9282e016a52bc4f4b591fc415dc8a33c688a0c153460c404b6a3dd0afde8092cbfb2e0638f91c70d7bf670de878f389158e307bc044835f029379874588893749da6a3ba52d8136ddcd65349d2fee092f7e40912b53639f5287a9b7327baaa408cf46307790849a0d017840e9c75dc641ec4077b12f49481c9b0baae4e5d5318f69fb72fb85e52612a1434aeb4a03b0f3b640eb072c5055f4f5a51666828a942a6a5e48358456802e5af86fe6adb23337d5a023de7daa10f2d3a3677110e8ff9aba607738a7cc983d4e748dcdd19490162f3039abdddae99fadc65ba3f8a4f5f1cdd315d971139942cbdb785d60a35ab01df0085898658dceb13e6fce33b37368bc8ee41de7818856956984339614d94dc4b09fdb94d860414b9913e5e19ee3ada0fcecbecc105df4073757a00eabab9ab68083e03a6e482a8d84215652ea15264d308180b05aa8231f9bba5d886007ba2040147589f11bef0d6155d6ad42e749918c78516494048e7e918a95cb0cfa7db13029c10427ab7c57c34ebb63e0e66f38649b38af1c508be06b198c255a24905cb006d1b4d6d884815cf2c52ba25cf1d5c4ed1bf1638ee80bce83b0a426d27099a08948ee1584af55850f302bb396c04ecdbc40cb70da7e9c467be95cb86317b650c4c184d4f7f1f7eb707c6433028d9d3e3ad0b25cc5e9f21bb39805a7b9741580e33203ceca4580512820653269fceadb98086d45df5cd3da926d708ef5201580228684025c1b9f65ced939cc204f3b9c3947d043741e2160bd6604527bbb8420fee16dd1eef251d6c862b9e3b2c8947d512cbdb6656a2e68b0078765537616008e32fd021a6c8e41a14f676e5fa7975f78b8bdc9b32bb17cecd27a4070def340c4e70bbbdbb1ad10273b284e12b10e121a9aaafa5c4a0d1ca6001f87f21e35e031291338b013c16c3f738fdd6ae4510c6e93eba24bd414c062573b2fa1ceb01056e7486469740a17362aab8f106d8cdbeefeda5bc14f0af3221db23c916dc25040e26863780ede19b17c9d696fb8ac8e70ba12390038e9b1b61b30eb24f565b097db98821dde5d4f926f4f7d5c179e27fbbfa46a107db12445a636896b337db0e9cb04b4d8d6e0a60d8be1428808fec612dd45e3fb04ba9c238a8f4d978ec650312450c9f7af3ded96595b7e6cae754fa7bb1e80e29e3dbf2939b674c92ff4701314d7cf7df999eb78192c48ee10dfc0fe7b48a212a717244f4b535906cd9f304306e7873f6afd6ec15a04b132fc94ddb509676fbfbb54700a646ab221cce6f08fa250605dbe7a9e00ad8f23b38e2afddec89d174f8b07777d02f4b6d775752089baa5bde7b56f22dc4a55c0a7884a7dc30a0a6df084c69ab731c5a4e8ca113088f6f038045636b65524527f84b5455cdb0e5214f1c316c154944fc7b8728c30d72d0b7175f4a540253dd3158061618532edefc664525ffbf8d254ba88ee00000afa694ba599fa42df2dee342ec72e7dd58d3c8606146b6cd4b15149f98709f07ec749304c3560843a0b3f45af44f13ac64d92f2ecd526d659b774ccdf2776100a282b40d200dc01567d693db195add3166d25f5e05a0ffc90aaf08adc36dc60853be73a389707d0dcf52bd3caf918f53c777a079345b7136dfeb51d34ebb7a0c581e88e09d287ce36afec63ba2e9579d9cd641a4482caed471436597ab25690e1679ea330555007a4fa536763f9f10432797d0bd31ca42658fc13155bfe00900b124729a73daf799675b8ea0ba205e43df04ac80d53a743f424fdf1f0db2f70fece996ca62219edd1c4bf6c6e9d46dbe33622e90804321e0954f87079b0aef032f04e37f050eb56e5f287b5eb679eb1dc95450ee7e33d211a2b0c38eb5ac2e09ad2b10107ff3253b35b521f46ff2a825a6d17dc13db3a33d53a59bc519546105f819cda7cdf7cf3fa5f437eae4a5abbc24c1d9fff966809262e62c0821e369012068761882fc7106705fd22af30fc2854b1a62d9edfc84484d5c74e2656d62016ef40fd8bb64327971b6e11261a326cc65bbaa167f264f3cbc0307ab5bec1704aaadb199d153d2708cdb711d68d06cf404d55b8d6d4185c059186faea89f08018d630b9321723250862ac0bee3ade4410364be33481a3c1461e6945a00818606c2ff644f10b7101d2fed70f9c2e41bd9cdda614ec5d89ae572f6d0a4000b0409 false -check_ring_signature 872613d74ebbc9ee2297d2cb56b4df70d2f5b95743b8d6272cc2f74d024b1df8 482c9c9216ca57367a8efb5f77f4c23cc27e3c9bc4a15df3f4ed4114eb1926ec 60 a2be4cdebf7ccf62e1c247e1ac8cf96b5a1537fef0964482aa4f328bdd8e31b5 5b9fc44b1705c099ca095e95d9988c1d8d7672e2a5583517b1cd9b4af25c35e9 06ad83efa44c9e142667badef2fb69de4182e80290913e392960dc9550017d41 072a28762fce80db566300d61489309630ea004ad0a8c7314d93fa9ca1524a2c eb5848e0f1c02f03c9912e80fd2b0a4e968bffb405f8b0a195a463ba76e8ed1f b213b167a57106faf2985568cbb18b16845a5312dc3980fdc94688d9a845d756 e8dd6cca9fca588f9f0b6dadb66c3c173a82db9f93b6fb2766ae136ca257cffb 8887147c86b31042b15a18ae8a5eeda84481c6cba3fe9f8fcb5e9ead1ad2aefc 5341af8141b332ed4a9e6c3c9e4fdffdf858bae472887df4dd6afded02dc529f 271afcda1b0c8bc231bb06e4e7539a3ac3690bba8cfa500eb245f8c560854e39 ab7eaa80d21b02a5314c1c4cd13f4e18eeaada9f5a0ce588f0ee52a7fa191b6a c97bba2f84ccfa6bb3563c82cceed0624fb33674fbd301e29b952849854ace68 1e3a7a2fd8c0417d787f1a1c1516c6cf785f1724f9a697402246d39cfe79e076 ef81836704ea6700b02352ddb9b377f1f840c940230d485f0910c1edcf9f1ec4 b6c77e87a6ff260957657d20f7bda35ffe3b770c60a36fbbccb05212e8b1b857 95a293455d62962a26e8e53f82f73673968b5ca9720a2b419cc601261040aada d179b063b52a043a237c2b0cfd58b7fd26db67427a2452e0d0fa472c3b1beeb8 7ac9e3af2cf32adea8df3fa5bf3c718e1df25d5f5ac25391ba34438a7368da84 66fb26fc996486077c3e74d0966f52381ff65a222fa42d8a2250cee10bf8d93e 1310129d7bd99a4849883d95cc99770896cc92d172f55d58cef2e8119aac8b65 e0502d700825f828987b3bb93e7534810ec3253334f0cfcd12d31c3a6a6ddd6f d68f874a8849a14d624cb8d41f30855bf16f32397ed119713e0038a1eb010de4 f29a92026ca2e53f297307b94213af24cc5b95eac41dfbfcfa201bd0352f58f2 bd75b99f3a319c67951d96e9766e03ccf7883969affa42ff5748cfa68c0515f0 10a62309f8b721f7532c9a8ec52ada05e3e939c160ff9a3803293b7b8a2d97c2 c2db2d4a9cdbf6a7c8b8d74f1360f16da50c9eb061ca6ae745732ebbb16969c4 378be9227ab7f811babd66988aed462ffe207e872cc50026b6d56fa13d946098 69209d10f38dd7ed82f67e5c53bc102201216581e320b983e7e68dbec2edf55b 2beaf59d59032e07ef1920bf69cfb2489cd4db9cc2e12ee3348fa7e6e4af342c 45c08db2286d19f098e70aa235dc2c4684a608e0b10137b668708ef34124357b a1a54c4a658814841d037f8e0fa38c767519cf7014cd775e5815806e632b4538 875f7c02a2c41250528178228c1313cd9e0ebacaccdd4dd33a8f4d2fd839d476 98ee4a1487769108b72689a1be7a91ddda423b954613d4ed61e3d3855e386b3a 99fb68a965b5b4bf705e76706efe49c63d84c8c2824bd7715476f0a59d7c9ff7 466f7ac4ac26188ff93ea2abc9141769e1749fe1616e686f5f35714dae14c4ad 595ce1f47af28df284ed124c25155256e5709d4a9ef3ece82c7eb00ea99a2a34 0a8311258bf69309f826c1b5050571cbfa7b5bd75005d7cafedd9d95c6686502 4d1f6232d02b3a30c7f024b940ef33139a2589fb3304208ffd0da5d3fbcb6178 29a4e0abe87a4337debe1cfc81ba7c15528fcb4c177b64081662906f315dd2c3 17fab0ba2ab41d7196e9da9c890fe10a830c5a04889cba9ef713739a52913afc cf554e67645fb22e8d3f6c37c5f4016fa116e93efec2b8bf723c5f240606f11d 189269699a60e7a21147780f5e6dae400afe2e72d6a614f44fbbbee844d9b72d 7b6381aceef772863d55f329c4a483a155c0fd2e0408538f6811e1f2c640929a 6379adf10c374cb824f9fae2b991bda515bcb5f6bc971ecddd395dd9397f39ef 7e7b71e64f57b57304d39e7c626637ac24e1863dd8c483c5a7af12a8f290d2fe 2058f74f6e0d3f106622f7ac328d39a2c3385bfeb819bd27bd75cd1c2d90d15b e193989a2304f9a74596469f862ff683e9eda4666905e9e817d51f78f2ccac0d 497500aacd78bfaf55c048b2fc4d11c1896008b8bbea7d024a17565d03106a54 eb9d6b65b9cbc45db45ef5edfa7143c5dc4f2c4a2322bb11cc1591df5268d932 9ebc1c00b909a25a8df0aa03f6df6af7d40766f401f247f81aa0ad039fb18ac9 d82030f4000ee25eb51be43e7c2275db45578d69b53b5384cda2ddd02225f11d f381932d0b1087f5380effa1bcee0290ede2170af0d6c89737915547875a4e04 e84d297313e6488b24e418f86171bb48d9906d4ce69831ddc19849347b9b93a2 c67524419947083405178f51b107cd090e1714738a06a9ec4d29e9359fe7d28a 1d1ed14e16e14309e5d4b46d09381ae8f5603de7bb9a9b2379a375c6c5ac15b9 180d15421cf340de6ed8cd962a1cb6f1096e64b28b11fee282d81d6c47a644cc 9b35f4794deae56a5d73330fdfa85480b0a758bd6b705dc95fd3a24bd2a3d1fc 38dd78b325086e72e99c6d82433005125a589922de85455b1f8b8c2f9580e5fa 5e33aaa77fd2fd4bfd652be21b2cdc885f588e1c623d216009808da15d3e1bf9 0a0fa4d0e1e020688bdd64616db1720b1f8d14f3c265ea8c2d6ba3541ff1f3aa 1dbc028b136a01272e497a4b772643759b850388b3e30f8ac5c2d170207f0c0b1642935294f38788a6bb263c0c5b10665bec1a267de052d0b46cdf6a1a13400c70f495e9f1d295860c1f2867e7aec8b072d1e6b93f09f6a5901c3ea62fc67004af562e60ccc880fe5f414fb5c2e58f8ee8495742a11e099e2fdb5c3f622e4303395cda04bcf60378fff0fdec95d4beac87ed1e3cfc58b13d168af062b2558b0f30d8fd89aa1c9ca238d812aea30157e3bd6efdadff79ab20dac00e2fd91420029fe288e37ea59c6a02fae9773b1420e714107cf740a33d85fbae8ac628f52e0094961709b876e5880d95ffa6693cdb6e86df1aa8e195ab2e19b093923627f801afc4e342df499412e38a874b6444add8435718d7be94d6b77ce785f1607bfa091ad2ec12e64c41bd09edbf13bb58ee290bfd3fb9e98b9321bfbc50e48e7b680ef3e3d3e3566647b6a165ee6f814c6988ef0c1d704d83a449a791c59321a69a06f64291db909f889d327252a21aeaa27418470c6a1f13988a1eb255a1352c0b0145750169015d83aeec8b48d64d9288b488fafb9793a88f872843812967d6560fcd226f2191b4c882e40fa0e1b58e606facd6903cac31ded64e8696eae8b22e0c10a2f5eb2135a03e1c4038a56979540b6228474f1d7cdb7c7518baceb6ffd204b471e664bb75a87b2ebeac314c54722e77b770613f4bb49ce1f55e928a6ea0064f9e92d826283fab79edbcbfad300340d26248a266b84c7a34601ee2b80852012201179d8026acfb346b23ebfdba788b9a7a48bda69da9d14f988071a5c6310200af199611796621504255ed234b4937a65f9cd0227b097c3cb186bde497aa05976d69504eb35cb82df9e35345d9f175239514201d7d4f0b7b186dd31c2afb0b525112c35c7478b2a392dd21256b4ada653bcac1b2fa43ee9c409fa02eb89100ee31fc0d3e4dfed2af5100878d8091fd6a97615f76ca3c5ae6cb9d0cb9a0ee071cb5c1191c14e72136543620e7cffef8d3fb3993098296fad7f4cb8afe443a0fd84aa0209516ccf3cc05d578173827d7d80259cf1ad1bcba09d64b0607410a0189b9f0a5e209a372972e1b44bd870d0f070a27b1431061a774d1197548f609035a96b21e4a943d087d2be8ba60ecf2f713c843c670bbc9de12a31f9602cafc025db8cccf1105aa855467f778dadc722af5b0ecc152f8a1304b546ef636b5be03c8c0a31f38d82c5defa1f34d18dfbf68bee4e372e2a75e562fa0af24e73db907ec11d55a9e684f234686406080c939526309419daa7dfce77b819bb44645aa0a6c66d568d7e59ac47094947bbd1a4d56c290848c1bad0c08a45bd9200c7c720e5e4a6dec14d03d83d6ab8067b5d4aea7094cc0c18423bb14b523255e2d2c950241a1936d1c49125f1f23ba853f08f30c750c089c63403ec660055458f7bfea0efef0d7efc833d20eb0b13026e5d991d7e9b4b244afbd5ed5905b732f7d755801a2ad1a57bf8a2a858f5a14575edfc99de65c021f37297313ff583981b717ab044cd6eb2fd881751bebdb89555543a60cebfc1ecdf7876e7e82ed75ce4e2c3b025e3ddcc4dc5bfa78af995c9f48a379b64caff81b5e20b3e5c3637f4503b11807b259a500a38bc7a64193eb27181af391d6ef9ea918ec517f6144a07837f3230a0a8d5c8be1b1b22f3e7455fab9a8c673fe67d52a3a7f8ca3f1394a3e69308507e7db30d2ae195ec914a743fc6c10f0eb26613d632b08451e43ee66ecb8849e033c8093875c698292c8f8df105f8dad2ce6ea08cbe60b87a266428e23ddd8ee025167ea893e2eaefc89d5cb5517c9fdc747e964c5645214c00b7e38e8aa693700994f2a19cb9c0e28688c58e6813130204e5eb7183498ba39aed2c9241097c50626995cffe41618544c43e78772e0ff32f052ce7682e93ef5218c0d0eac68870b7a2a8f74408813b0d93acf0d7b922e8e58bb8a3f1d35d5652b6999c4b38fc300c2b9b85f27ec01caf099bc9b16231da828a1db50ebf54d12534d66545ace510b388301a034e552c0b9c4366ca9b2c51b334ae40d5793419025c03daa4b7ab9045522876655451f8bc9f9accef02776bc434e0bd0a8683ecd9bbc1c0bf6c7cf06a7cf3c0c9a28e61e5b01e7f00879bd5eb9a3901757d0d226abbc8f84c542950f19ec2f84404de0f4678966d5fc83ac5dd6a71d740b3d126270be515131f94b059ad78992f179e4ef79b8921a218d0f8aa53daf19ce3aa817d1d7652179f354088a89ac7555ce400bb9f1673fc05f89161a31276227394938a872b957ecc0880abef14c9fd17613399ebe0deb47728f27015d8b6341fd2a65b9eab94e46e4440efa48495839e65ad96ab01a5541330878f0eb8b54693fa525d379090b646cfe06ead7e86015492f6bd97c3f6e57fa48fa6438b725388d0d74ac30cb444bffa702a05ecc41500a42d796e956a45e554c76b734ed4348f1dc965958abc32143700b24648c82bdc37ecd33155afbf578bd3fa154ffb5a23e949e86be2226a593900d7477321e7a57afb8d43ba8a89fc908613d3f361c54b6c37771bee15d926ff50439b1012b3151712f877879aea6cbd07900f8f768e25c917e0e18b8484313570d1c6f721d64445da4307f282f44b320c2037fb379e97618ac2a393af5501ccc08d23bfda494408420f8f1ca91b86fa9c43e99a667cc378544e3423cd23168c70effbb6648b590d83cde5c379f423d4bc188e303abeb0fd5a190c016415d5750098623bf37e9f3e504c11887725109a985e8b63704313367ed70dfabbde14d1d0e23266e967bc38361b7f91fef1bbc2017e416f2b8f1377e15d629aa914dd4bf0cf789c133666448e75b218977ab1e603221dbbfa91bcf2f9ec29755922cd3aa0e1f30b2e17176b0b04b9c89dceb6a4700115af0df452a1927fbd6d44f9129cdd641b365c037293728d7051a377f5867ac994a99a4a7a5a08e1460b261f32d4e0007e432ff2e6003a565eea3e2b886139d2b4e3364c0dfeb607d718ceac6dd0b0e935d77e354d32078a6f2dc4bd8c817ae68a6a81bbe375da50d1b0b474519680cd8f573a39ba9fe758c767e8dbf791d5463ab377e0a132d8c0e943af53eee6a01eea67ff130dcd2f89fcc4a73dc3eeb02551286f9bf87915e5780d81e61a6a00d47f5fdf2a4150107e86422ff0ebb64ee948382ceebe8c107f3314eee4ac4ad017f1c136aa836aec5e1e228654dbd2c9a6c0f14c2d842daf5a8750a1ec5388505f42142a911e82b43c7cc971b3d5b67ac619d4e221be08d890d68eb8b3c5d9600d36b7682296725135b18e2f6fb2c0400b65da9dc51a87d08c78f6db1405bed0f2a84b136cf0762901c42bd5078a327a84e0bcb00d8f95b9b695c4f8a59ba5d083fcbcdef7edc005ac6b262374129743aff383e22f6c3cf4ccf6b6bc37806de03fbe60656f3734b1ddd48725139c626c3ca115955621d6deba7971747ab895c0089c9d7a2ebc3b04f9b2bc59acc5f6fdff7c16dacc33437d3232355eda200a20d55e71bcdceaf6e4d9b4976f80b5164b09952cb2c6db82c05676172dd2c04d8076936f3bf10fd824097bff803958977f5636cc6c747f3253c001d4f8fe62de70f773e7bf742d48b15140c2cac48b2e77e47ece76f3fb39eb4582c1805a09a7a0b8b5a953d39f89fc0a3a39cf5e13e5a21aaeb288c3072d4ff6624a172c9282a02c6289b07400c1afb471bf5c5656743e7385cdf0bc07e10d3b8c9efb709fe2a0eecb7ff297f018849a6b225e6952a3f1400d061264c47d0956d09d227f7293e00d06aabf4a1ab3feabb96cbb5c8645f4554180f2c0de6e8bffa6b06a618c0bd0c3f6af8d77b233693e14e03005ffc137034b5fe2b4d4dbbe2a33f5cb0a43f0c0e8318a75a460ff2a03b4cf3cee7aa3641db89b8b34b5168deb32f6cc844dba501c914cef0a72f382ec4962fd03ba55b76bef57de387cf449fbb71aaeb632ef80fbc722ef24ec137c5b88e49e2926e3fe1ccb6f3d59586b10a07a56b9c2f86320b9543f5b6a749da7a993893f13ce0cda3fd0200e0044605adb1502ba4fcf2620b5ff9b768723a706ba144a6e64a8bd455d9e4f0e6739d23441ee1a8623866bd09ed31324ae64453ccafd2ab050304d87e86118c8bb4573f59b3571f289633580162b8b3f6352f22f2bffb7e99f668ed28766ae411b4baf5f3fddb750ca1075d0887cc0f4c6a706168771620b351bc95c96814965f91e478b2194836755cc6a705cc5a627814d6fe5e7c629b40b6e3c1e665c88ea2fd829996bfd84a62e4c7000b767ab53f357ca105688b8a0746f4b438f3b2929309270442b8046a697b61020eafa69f20721b17d9903eb867b6f4c113705741deacd915e7961e0c8c0f9dc20d6189613e142df1aad03a3b1e5f382c84ed4ae889d1586028a2ef3c5cdc67a7016ea665af3fffadf6188b813f1a48f09eb4c0b6f43bdc62ddeee12ae0b0df8302ecceb474734e6b3eef69283a8d2e4738a7f727e1298b7bc4e066f8bee4990e0ae4b8aecb529c5631ea447bece64f776ef9c5ebee46878084114232ce310ad10ac15e1919082a098550d0264b81439b336cc3ad6676d9dd0ea4719ded2f433b0cbe2863cb14f4b160bb8f391f5c11645b01de0211ce0b7e66f66ee51be923c20aac0f75c6b4a454ba7aa4b50f9f04e93835cfbe03ed0e8fa26bbcdf60360410086cfd86b3fd81dd85e2cf80d45e3c5e83216f4ac6db60dded6a5abf3dcbf7300b1ccab79a3e204db36c2ab131a8e286f37f0016e8a6a0de0b5d093e95884107086339d505fa92bbe63dd577816e9d38ad495f2409ad30f792747cd352be72fd07979b27575ac76696ef193fcb8a7abfff8d54cbf5ab82313ca9c2c995afdf5f06047addb0dd437dfde61c84a28ce2ef402252c760d9659afee02d2ac824f3a906af897fcdee44738151cea5e726ae06deb9d8347cc22eeb3bc62615771b754108af1168c7e908d948ccdc7c56a571ace5cbbd573da685d620305459de25801d02aaea94af946f72d5ca8651ea0322f55e6e4d6e75d8dc93b3e7a7cddfa2d4ad0117cacefe137360abeb5253c203d395de1518a8ee33f9f0daa2e3b5503c89910c5b3f025b67517ae1b483d2a733548c0124275eab4653fb6760fac87173a46809bce177fa52064a9be8e3aec5d3acd562c7b24d65d5c310864ec399acda311a03e8a28e6b29eb340384609fae70cb37c2faee16bb123f115f7147fb9e6db7880795cdcb3c894ed9aa62d4167a8e18dc6b139f992f39581b86b60ed9d80bb8a005bebe67a098e990a64bbe7d8a465fc816ee8c36b744a08e30e7882d933ae4e500215191ea67bb1d83c655be35ae04f3bc2da581e877df541625b5210237b7d200dc8cd61b0c79709637398e6c59041503964135e42e1a362675458120e6b12b05 false -check_ring_signature 57d97713d1cb289efe6b5dac84993442a5f1f398dfaa5de1ce588ac190f9807b a1be5859dc7043cef50796684d5943cfaac12e40cbdd3793fdcde80b7ed16b4d 9 b93ef39000773155f38f13aa2ab0deb1c67efdc0aa1e81dbb14077f54e6adab5 8c01cf7fc10ffcd9e1f2f095619d51150b5bf996c488cfaeb7e6b6e74b955dce 1900b6170ebc08873e2651e071444cfc9f5ffb0ec49b78bbebc5618e5e0b2d2a b78c32a56b4d63a4bcb4d8e932a80046debb84a8fd39316391ab5922ec115727 56abad6869b707a276d33f501d303f165b2112e97831fd83fdb583c975e2f66c 7600055222a08ab2600d21d464105060543d2a5478015cefcd7c8417ac3f00df c44ce221f8d8543935212cdbe0936b5e754a2bde7bd7e43b1644c6b19c299e30 fc34c181ad10637918186522ac9951fc8b06c8407c594f35eb8f43e62aaf6c68 ad253e44155d4782b343b0613606b3aaffbf5dfc425d41358a4a945f4dcc53a8 edc7c6662529eeec56af36eef13dad4df291fed7c5ebd790b1f779fcf6abd50dcb297db20956101e3fa0fd705b19e24495e3a751c86b871117cd2415623776000ea5772bf4827d47697094fe1f421530fb44cd43457b990d0d2c490bb217010156207566957c8beb279496119cbfc90ebe44ae74dec1547dc2e294b06ecb9b03108aad78262e0f3fe7962e3c980d04fac5e940ea1bc920291aecf34540186b050359a8c3793c540aaaec28db568f82cb24ed2519a2d19808005594bfdffc060939cb4a32ffb12b9731213ee2b9c7821d6a09e09891dfaf3e16ea7c05ecca5407e11c186906ed3484851bca5ba930fcb59c7a269eb48442edf153408bfd232608576fc90be2c38fa17725f78f3ab0f3f8a6c29e4d18922715f82256f63c71b001111d70d2771b3e8b2017ac6c943e02d5499d4fb7dbf32b462b8062a63a62d50a1e13e270b8f9d1808e6836e7eb54a7ea43a34f2c98d22a0d867ec01df2f4060a24b017086f0e4908687fff3e2cea6f0675355379f25094eb4183690fdc3753d53529f3827f4d8372b034d4b35a45aa98f66969d7290c3f9e7762801792d6a20ebc4fffedb09f736bb6a6c3c3a36982df567848f4a42c3af337b11a7879ac2d092d8880287f1f6e290abdbcd171cfac926b2c430d96ca59e7cea5255c155dfd0eeb11d19049e824e6fa1fa72f3fe930c072033aa99e3fb932cace720f83e86e024d095b2770f17b2366e7fbc8cc9c51100ced92ba99245844b8712b61086157e3061e628e78d61c0e77f3cc74f8fb88120ceeee94714e1de8ad4b3543219be10e false -check_ring_signature 578e72f8e8684dbcfef3c4863fe2fef1ea5b3edd1f89d3665b04e4360902203f f9ecbab29c28e22c8466db7610d61c975d50e8d3c3065aadb1e7902be98162a0 1 5cdd933574bbefac4e9122c9ab462841886eea92d3b5eee8e4cf2b56a3969b9b ff9533fcededac41eb156ca206945da7d432b1809ead83e5042be10e4d8667abd8d8a9a5b27e1908d3ac76dd76d3103ea51c1867c72ac32ba64da4deeaf6640a false -check_ring_signature 53ebb7b5843b08c0c6274984a323b9137448005d5814b2c0de1e8b22cbb257dc b2194dcf6caa6a63143fa9b283e0108d9422e2092f9963282623f88f3fffa0ff 8 7d2113632912234bb62bcc1f5c31c9a687b041cda1cfd4f5ed88ab9edf8d7d17 17165bd7615e087d2b24c54e1e65195116916bd4df15dcb2c0eafb8f41cf2d35 f08cde49f8606246d191eed0ee4b48bb83b1486cbafdfd15d3368532c19a1344 7c515354b3041c1dcc724ad2170687b0c8830e0d86abf71c8f7ccfdd88ed5d7e 164a35cd423c236098dd7f7c7d16c72a2382e2050f2724bdf0ac9fba5c1234c4 d312cea612fe4c4781f4f64d648c4210278d3e6683e57018839a06114a2c3edc e1df0b93ae30d9af4d472a990df5f8821d1ce3738b2e092ee407618c544a7ca9 f452d07d483d1e1d87a2b4f2c4c4788e5ec8627252941098475e30c8781fe99d 8e6bc5895ea2c11179656bee621c0c459530f2b22b87aca91be8dd2b103016043a72cfbc64b729b30f164f5118032d788ff41a3b3502086862e73db3ee5d5509e59b0a8ca311ad4125b5a779301d8c05e42cfa5ee4ab67327cf144d1b2c4ca04cac647ea4daee018a1b108cbd5e454abfe02c051a8e955b9646c130d39311c01021ddcb7e9085bddefcc9857ff9182e83c8b651e20287a643f58e0ab2e7691098b2840e2a3a97a861fdc750a24ce50935836c9cfeb2b73dd533e195769b12b04d629855256f4f7e02dc25d7880b5a1eebc25c3efc87ee1efa91117a3bcfc0509599d97db40e7394e758e91c0b5cec45cc9b6f3741bde4c7968cda83b45099607c03a3d2a9f49fd9e18daa42250a44d9c7ee68d63f9be46d7c56c3fdd4f6a4c08429cbac35c5b29de1f9960842c685c618e279c8f7facd9d90cb50965cc1a73053b60c8421ae5d23a0ce0df9fd09745c26b46dcf3ec6d9f5452ceb7c53a390402a9bf84b52e7214afea9706cc1ce090a02c55dcfc544b83e8e1b884167f5e9f0a396542cc427cf1dce6ec530880009b0e3c3f4e38e41b78c5ae16f0e51ea3c8072e2012c2102d525e39d0dacdbc6166b5875efd4525b7a11cb5f3af246028553562705e183b5fc88092a4a5cf018f7fcc25a68261b7967456cd8f613a1ce32a03be5a10af2ab8ddfebcb54c211b76a6d3761514650cbdfc541a10b588a031b904 false -check_ring_signature e15d9c1b89c954ecc128d13a06694a52dd173adcf93efa0c3fd1c316fdff8780 1b1de762751d7046a9516ae6e80988527eb00aee5bcb4c0c8ae6b8339178fb51 1 91dfdc7ba06d805c7e02f2264a75ec9b62fd00bcfa8ed6370cfbf3e793213cbf e84a942896419bc74ec70ff40339fb53c633d5f9ef16f87cdf6e756aac86af067707f26124a573b9b7d69d2ffc62caa019d7137b18d5454bb185c6a57f14110f false -check_ring_signature a54d715940e0dd0e4b1e902f6cecc88565e03d86d5f7ecde05da763554cdfb5f 8da3c07c28e404468d605cac07b70f091786aaca4f19eb847e0b58133d8f9915 2 30cedbe07b4bf8eaf74d7bc71bf660fb13af94b865be9bce31d35eceb260cab8 ad11076881c82446706a08f5d7adb1301b199c23a53edc9cc4bf03860f832622 d2027c550d821d0229dd0e63b7b4e83f92a6e74a8e7a28dff39cee9294deb60f0e7a66eb748c12eb914d60a452fe1b03e15d5d0640a55aa556e00371ede5d70430ca0070f678bdef5e1ff09a6155d0bd9d709ea9b151631266e680072dcacb0efcbc172f5c30d900800d2b1801c5ee0fb2a7603a4f6c7156bbfc63cb44364c0e true -check_ring_signature 5bb677a3fb94e593203b6b1fefba2e0e82fdb4fbb151288526ebe0271b1a03c7 58fbc3a4432f4c1cb5d17174ecd4eabe1443159ba18e14179f6bee7cfe1eef85 1 3cddba06724498ddbffb8752a589ba250143e1cb874214767a8daa43a7c83ee9 2a8a3535efbad122791c2334c48a56ade4b2a88726e2435e00825f5fd03dff0b792f84cae935e7b0cea7ed72a27b369918be5a23c739b4670e4f984a6b11f103 true -check_ring_signature 9453a44b6629c090ef565896790ebe3aa281f0daef72c9f8f7ac6661df5cb798 9ae6c1490e956bcb2b1053da7959b8473f2d644045a88d6ece03616f6316e045 1 cddbe11395106dc27ed87e782282ea6b8b71495f8030f928a4278940daf3ac68 41ed26c7bf8fde24a7137ad95ee8bd411b0580a502278d7771147b9ba466fc04d360cb30f80b6fe6756a41562756ed0929d918b8689f6a754b65328c2b4e1409 false -check_ring_signature 92fa07cc8f74621d4329af17f9176088f86d91e68fbc8193fc23d2d3434f9067 c0254334385f3146e2fd284780704d863b6feec3b55734eb30ae32698bf037da 6 cb4bde311c6393b2a35246a56db1e718b585ed366cd859a91fc8b05619dc2290 7dbc1a7eda07ba7a59b12f7a4ade42057d5c998175f7389cfc91057b531a7940 108fda4440c9f06c2fdcda6682777f5bdfde5e68e7a401fc55d82c6725a5861a fd00aff6f43f896cd96eaba8207f674dfe4ab9ea835e1cb14c6b680c2a0917a5 f4356f6f43b60c589f7d39cbc075d25f019462e42f9077b9f1136a6f85d874f1 ee7ab80665f10c89f6fd66fd586a2fc5dd177d966ddc3ca2b7c196f34d73f522 5a71c6b16161a317e9bedabb2da4df7e9fcf50d18b0a833c7c4a166df1520f01b79b6913ced6ff8c2cad52654b23d0a1ef4c2d49d76559601f5c3de8465819252d9ea456ad6e296d866d77b316a3df7aedabcb3f2f6193f9627ab6a8d8d53207508a8cdca5538b8b5ae9f504f7915e360b3653cf09c83718ff076b4c3b362c09bc43a78ba3f0ccc0079b0985e0f5a4baf3401346db6a17e635019facaeb6250c914096678332bb208b6cb58ae492ff2e6078f94f11d02d22ef601af5ebd196019df78a645e31d73dc977d1580cb8083257ce7121f7f05dff98715889007fe80c84c845f3df8617515effd4e9aa24cec5228daebf10afb85e7991ccb7fa0ff20f8af7d08206aa9fc0075209804ac5104caa0b2bcbaaab8c33337128230d1e9700faba2cfee512970becbd2b599b2654476435b8b613f5184fe56699702e971e0e65f65d63f4be15cdf3b855064e38b2752cba6c236ff6286cdfd43d7352cdcf093a575167b8a433162d90f0d114bb7b3b5924ae24fad61c1ff5a31de2f6a83202 false -check_ring_signature 5279cc5e5a594e95a988321d766948e1e4f873953597606bad1b190b1abf91ee e8e0b3a9f5e494b506923261c4b3db91f028ea75f96af56694744c917131e5ad 1 56ba23668db4bbe2d9e5503676c9478b6a2f6c8cdf983ea652041524abb2f453 996180b3b95fce23d6b7b37f7ff39f9bd85079049f8d2e2e0d9ef526fcc50409fc072b102ba4fcf68cc60cc3b8da1cb9f3fe9e39e9952a1ec0a4a867d7e6af01 false -check_ring_signature 28c09fcc17f92aff0e0359ac63630e9b105e8e99307251a83678abb0a7bcee0a 99f50eb937ad2757d9770e5172223505daf7593cea069c4da0821f090af6a146 45 5dfc99f3a0d8b91b7309dd1248327e1054aca4b93afbdcd177260a945cadc7ad 771760ce7364e15b70c5857379f8d0f622a4ff0eb6874d1ae1e65629c503081b 670305323c00ad92bb0f3e6b7e8acc3aa3fb465d42a6b6f5542fee6b981cbf48 72489679239fa9d2a09f219bb8441b87365323a0ef23e7ff0b948cc4d5b449fd 6220989b720c79ef0355ce234fd61738444d4810b7d81b85eecc711b022a1790 30982597c81771eb0a29b4239317ebc8e43f808914998b08bc4eee6651f79d78 40f0370d47209aba4175c640e6a80d64686a9f08bf27c1f7773bd71d2cf0bcbb f556f652c47390b882a646d8ed77a75c82cdc5865ba92a493032681b89694f92 c8055744fabfa614121ffdb7ece93c03f2751a36d83f9b1373d133db2af4f123 732678e11802b78a32b26133b6726f1dcf56df2c008ffae517c2c4f7036d776f f3bc3e7ece44a7ca6666eaa164322709572f35154f6f7b299ffc5a3ea3ea8e1a ae5b67a8952c5322508d891d94c9557bea602ffd99567e43e636fd084ed39721 b33406a6a2331d6c1e00dc7fc9fddf7eccbfb7acc9d237e10460626b8b2e7679 94eeec69e06ba1508ddcedd6f99438d6bc9c52f486bf10a7b50c9f7e2b014523 12585c5bb9e0418994e0b7d4da99a9654ff72a3ff5c46633feb084c15d091fe9 584ccf32157dbd9ee171c7044cc01fc6189f7e13ba1ff946eb0373ed331f52f9 246412013b9675085b847c5896d481d27689ff69a0d0c13112f7445ee30ba8cf 78ce4b0ce6607a1edfc138065e3004c419b01d4cf5939ca21ce38f8e94e3f034 ff12a6ca353fa1109aff2aaec1845e95f77d222eafa2e1462ca40e05d9c91686 3e59512089ab4788a35b3228a57f7351085afb8e874223968c551fb54f512d9b 04923ad027f22dadebe0f438cb35b0fa66338317d39d2dd71c3c58531e2d40d7 89e8e5498397402fd203089ae8822b161a7c405fbb0d805526431ff5743afbbb 93f1858e308be5a3d64c29b8008189d0158f5a1d0e23809fa807e0e38f651224 dcbcf739e1320447de615138e785d098cf97917e081fc7115fa34d02ea0cbdb1 79830f9fdef0153a2f0cd90957524c250699675232a7f7d878a9fc7968cd1a99 5eb62d0226553b4ba909d22c77005bfcc22c7015d4e97d7c548834c4b4aa97d9 5266a7c86895fa0481da2dbe17f860dabcf8680519d4b28ce4e07abb54e89657 571a8b098fe03f620f80bbf8d5ef4a4af9cac427041df2f6407fed49d6e640c2 1ce91b290f8b11aec0f8619af1396c9f2c77f8149cf6a5c3c29a1b41089b2424 be77e01a7c4694d89a9c630b922eaeb218152c4435da709f60bf08f256e62999 fd6df929361353b961c0b825b3ca30469fb6f3195bf01ddd4d9a096485ecf053 386fa3e531ec24e53b40045f43bb9b590ca98d355d4620ca2e345cbf815ae9eb f604e03662d328785ed97cb9e9899b441e64d65c8c988487fa9388f242df75d1 d7273fe723a926a88b409e1d5c565ae48e1da3a2bcc5574a1ac5a8573b998171 76cb708ed9c64046915307900e90d7b27d0f3db6ec1f5147d886c720ecb8711b 62848005b92090c30a1949186b7cb57042e367b5b0a2a3fcd5fe51e2281943c1 8f0ca27341e95cbf2c781dd0d4dfe444008042c334c1c4d24b35c8030a67040f ca8553e384a174fe7052a0f2741f19b986524b84d9de2c6b3cd3d5932ba2ccdc 68e2e64ae4e81f697463c08051e97f3191cc68573421ce81650f14321fe88c48 a81438908146a4a8128e7e260a12418db521446f257c55d853d1403ed85f34f9 98c187772e3187a715dc99dca449775497c0dddec48e7080c06115bd7162e5fc e434625df824863632150ee9780623b9567fad66ff22deee9b1a2177c4e0b528 3adbde52e407e105073a2834b0441029224f4ef6fa6145e0d4a20ea279b4842b 4a44b0a957247a9e1df8a82b9cad83ebd14c7cfb4fcc955ea462e6cde03ec702 1fc195a1f2bd1f7ff3269da544bdfc3eb9bca2cff2a23526fac8e0b07fbab300 5967cb2494517654b97535cf5aa8fd0c6f0ad74091fc72e543006da4be59b307cbdfe416e72444f1dafa04daba1cddcc72519feb03da196392de75264dfd41076d0b3794ebecb8f86ddb0a7ff54d7a6f8f1a0630b5357c1cb48b1eceba176f0045418c0b0a5a96eed534fdff063e7814169ec5e6824ec14084811a4841a44907604c8eadaaa1ad45566a398bfe1e989d1c9ec41f3d52eb1ac14f97ca7d9ac7040dada5aaabb96789fa469639104c0428b5ed7ebcbbcbe16d1d4f965d0a27010495e1561d5135c2ca22329b0c911d8be8a97e7cf3063c03a5ada2fe021ca8450e0cd0b6dd41409b80a06835203c501c75912ea655647bf9849c9aaa8c903c170c2f5f092484e3218373e9966e70c50ccf4ab53c0455cf3b40018e1e4b1e7bcf051719eacd42229bbc6a19a395bd55ebd3f8cdb4d978fb2d2eced3548e3ba2d1064cf1343d85d475a1e6e6ca9ef64ec27388e3588b8cce27e67c26bf1dce57d505d135dd070ce0596e8190b30ec002c161c7522bb256765d50a67ad5f6b171bc07def6ad15f004199c2c0acfcadfcf1a5da1e7425e32ab9587e0adcab30274de0cefe72256ca59ac0de44a3e47512f6c84b4d177099fa9fc555298e7fabf4f8201fecd6b7084922d71c0fca7c38a7d305cf322e0ea4238eacf93b25be9dd073303932df2f1181f10a590518ce21908caf1547adf448d6f2b3977a69b45cd13c50b4dbf4d9c23b26a57a9831d3adcc7b7b34f1df2b868e5596c0afeab81bc9fed0df44b84c350be4b4600bbdcff44767103f41e9e2b01f8a4fbd99fa2a290c516039f0e356531c0d25125f6ab988086726ead7d7f0b806a6df05527a8ac9636fe0eda36e5b0d71a22a5ddb350e46c00f2a4dbb9a1a2b37c4af3bd1173c1db8e9702e40f78a5f785c067e262fb4fdcbc7fa09271d66d1a683188fca8f2982f7d0302004198fe7deaef550941ba88a65b85ef32d77cf3425db784aed29866f848510447223f1069d076508bf711cb00634473ef68f9f6d346f72a77cad31e7e2ef50ea40573c7c2d9b6964c9983d45c26f4758b5c3769cb4b1bb35071d1eb4e1ba6067f75bb5713071dc5c6b0f0cb39027b1e72e588d58a7b059bf37a530784d88503553e1ab5999dcbc9ec3c11f33d4b5e0997ef81246dc97d360f2f2f48c039be0128fa4d110846671f7c0ddc89a81263330946105a1467e0e3153ddb613c24b10739c6d1762181f736ee310d6a49d51d0fc5c38080ef9fab5af2f55e37a317a10fb555c9b75cbb6608b212ec843455d32b5b62d235f05f8483a4856f7f65293f0fd66c57284b0ec0633cd15a47d19d95da6fa21bd4e9587b808697c5e2fc2e79080d45344b6f9d42c64c8d0222db65a85ae5312610e872660cf7aa05436041cb05d4da5c482a586ac3d5699d77e267965ed845c9e7c79f4dcec700444e80b0370a6ea550fc5a1508ca339037e3e77027e28ddba6e051efd13d0ee713948b1c34060004828ec1584282b9b221569f0c01438b0480d554f511dbc36ef003ba850f079dc78bcc7358690e1b25e6c735bedd0ee7aff67dc14fe81e94db1a1292c9f006de789d9adad64331bf0475f74fc5f953f09295b7bfdaae763bed02cfac94050c14418bde61098d54c195af19ea026433edd2c5342ab1f4a817a4340edae7ac06593cf9b2b0cf4bfbd873c870844c571e70fb8a28105729fa53077b946d536c06e900de61e6aff1860437b4c3c7ada9b7ae223535ee974b74a83ae032a150720c37da6645b652ea7e4b3c12cf17309ca3169e3ece3d0856dc9c0e73a3630d960695f70a6669358425b62d4d18c56e636a43ab458de3fb6ac9f82bf5de53bda5020ce26d83548d3f6d67e513a07fa669826adb1e87b6d9705315b4cea93a9c4f07495abf04909ea9c0a50a7673b2dbc7dc6b725f5a41d6d3fde970f553b6786c06f62e4c5aa74f79de067af7ae42b0b5341a625e79ec3f72778d7e7db204f0c5046d2bf92456e7b1739ab0475799c56f846047517dc8fecc1c75d82b608a13db03b6b2f0f0acf8f762a954c0a7552b49d5fdb99f714ca11da33ddd11e0acd6fa095dc62915031c1a1d9918c365831f01f14252fda68eadabcc0db2bda4fbaf0a05f14f34f35edb472a8e430df5e6efadf75bcf73d7c029fb5cd79d26b401971b036af5c46e582ebf6667f7ea2e8ff18c02f8a385ab5a2d06980d56da990d494d09bbc6499f89765c2c500aaa1c3667dfe86401d81652e122148738bd6f0a3c1f0863a47004a129f4d2a0345204046d698bf5411b9ac642cd01e6cb757b93e39d04e03d1a8934d3cedea80a221182127697e7540bab51a273ea5bc7a7f38fefc90f95c981e900cede124f80fad17e34da93f6035010114290506be37c1e0a87290f2d958747852870db8b28e7d47ca151043d7e9b6859b8342a17f17bf875b5920d54d1455de8809b8347e7857125bb92459ea2bfd3bae8fef3de72127c969d42099b6f6212335053676f441bf1b804810d9e539f867caa2d0558ae54ca83da2b3503e002597783e32dee99897979adb29339d0d748b063c6d725aaaa9cc747bd0269a9bf258dc016c9b3d518225f36ff086b362c11baad9230862662d664135408bb9a4adc1f41f8aa23e1b9ae8835b903247a3f1d4a357e49e388d9afc76ad20ee49a5b6d15a0e4e625c6986b0885f5eeb13eb0b4f2d494d52dbb3ce60af9490c19b49e03954d0acf49edca64c4c7993e772ae0882bef1aba94f29628df34310b03d7e3584c0ffe1784cd74bd2d65dbefcae6e35554c3675d20ebcf4acddcbd0350716e383028942fcbf624d1ef2d9f7025c76b67b0250266d80c66e21465bf0bd9ec734133e2a11830a5f41a50715f95e0e321a30b4cd22f57f9b4513bf011075173d6f74ae31912258a98cdee54ae2d4a435c630ab1e67019069b344f94c80c2141e343d74212bbb86e9d9ded5045893c7b4eca115283bfec00da6cd7d56000b9a2cb24a3737ba0802a179ebf5959755ac13995f9a3997007be4a1b22690e0ed079525831e4573b8a16f13371d4fc7ea5830a26f25a069369e7ca67f984610183661a14f2327a9047c871b5065e2e940f23fd3aa4e7424026da1ea5e717c203503aeb52ec6ba0b9674bd49a06e2fcac93a6a0eaf3654c3b0338856b10f62d0b1c872c1e6d4607e79598b16e64703ef5c5162ef746d9f168c1d112ba13adda07d0de7bde998766c46ece5159fb557e33b4a3e4b85c83a054ecf13d03ab67400487ed94608662d049e0f2ea02f87702e4c387680b351a09cb60743bed9f639e07ab6b6e0b9b4a56c9ec982093f8181d60f84fde79209d4a1a3654a6de0bb3760f1fd745d9eff20bf7c748a46264b4c63e0889d3950500654b6942487175e3e10e909f78a6dc5e35521bea01b5e9bcea15d8f67f00f2857528540c2545ef9beb04999b43227bfc8c1b676474d38adffc3b481d6b18277e0529a17e121a1d61bb0828ff5a40623bdf6c440a3ecebbf860a1f1b9e044e6dc02165ed86d8eea433f0c3be61fbb372cf2a32c940ab41cbb1c31474084002acb3f75048a56013ecfe70eebec2f0e0b10a3607a1d53b4d2118977f3e2832b288cac553f1a3017516aa30d4ffb28061d70b13d7be20e0874959293392b2e657202d0e1284e393a6869f4074517f3bd6ab255d37ef54721c1cc9e65124b8af16542f1fa7a872dafc2a5a40c3b944751f20179f0b2b405f4db74a4563fa9e89aa257955d0ea9245728e19607efac3c3306f268674ce82a2c595f1998a0c1f24a315bfa9e09678abee0b9a006b052390b30a1ab1a23e5346b6864175c70adee27c42e5440750ce21a75afad07a0ff7cf53e8ff761ceca3f047d96e96af120905a2dea81e5c643933adacf6406b1bd8092d8c4bb78eeccc322489bf7781b76f229f9b1948427b18a8f5ce7230cf6cb817873ed5776a9549f26a2b46496066835570db4676b548ee2ef9ffecd02b5fcdc2e42cbbb76a36a4adb5ca33e00c0aa2929b9510b0409516bb0441c790c2544bd3ba373f2649a8799410060aec2f0df4143ec6849a4d7f0f36749fd0800 false -check_ring_signature b6f5491f7add9a234f143af52c0a0396548c3ae39337ba2fde823527ac6d4849 4fade925a0b5d6936868e8f1cf05fb8d3841683755ba24bf72b3a5b40b4f8a1e 18 b35a85c1fe49e5e4f837abfd52974b37e5c30fdb5ba945cf8b387fb51295af59 d69260de89debc52398076fe2161530c281e019be3c0e3d19c0b95d65439b65b 4e6ce6636b53cdbae8ba45fbbf2d48db817c2de73130a06c0f7e9a46c4f861ef 8fe1b372fda44677d947c3ac203a8485196d2a42060e2fe2228a4458070a9988 2a53b6266e8d5d2901ccae1d3489053529f05f3b1379025629ce6099ccc852b2 42944bb15e8eba607b244faabb356b94dd0b6ad6f17a097fa2e5ca7ee6eea30b c36115be76f4407c82586f722e63c1d91729bb34b0726c00b60142021a116ed5 6b06c030f421d51e585f32463c029c48f96e3910e6852c25925cb8d22e3d1f45 e55e84a7a5897f3d87fa5d304cc0e2706e255a1c6e411082572627bba76c1200 fbf9abacaebdd9d5f5646eb03ebb264be0f91f5fd0a45d3380e1727d24558926 89fe7f830c905c2a00990536aa1ff02d3846667192df7ce923efe7091833c6e5 d139f59aaa3c937e2903306e7b9ca8033a7e9e8365ff7559de7785d7dd8a88cf 83bfd590e97c16dc2cd82875794bec2c9ae0d35c95d8e3b0773e3d0ac57fcbb9 13379776821a85fbb2fe4fc265d2b8573db82778edf0b669458e6ebba901c7ef 9e5401fe845dc41033aff2a69cfc22b134541291768021d2b48bce8d8bebcb35 b5b5ebf4be11e58d349edc3b2c70cd7fbfee5cd943e4e13e559d10a618223a57 824f6e5d531793bf681041dc8040fc98973d819da8f778602662948055152975 5fe84eed042c274bcdb59c27960dece3a8ee456ec539c7905555a5e4e92a741b ff118864472513eb5ebcf7510cc76f66456e3c0df9c140073fa5c26a3376a50e054c95d1285537524de87ffb74179e09e87159ab5e5980830e757e5dcc8eef054dd477007a56fd0d8d90e0b13e11a381fb7eaf28ab898dcb9bb33bbeb0ac190f5bff45dbd48bc3aee158f8ca8cacd4a545a12a42b4097c4b9b87be385457580c04cbbc6ed4dcdeb2fad7001382f220a9360edf9cd9b477e39f9463de34846e0904937573495eafc561d4aaa9be7d779e9c5580babc1412649d917d09a8caa20478c68b37604d761a05af888ea9838f79ad24ae257a2d1a3821a709bc4c6b5e01a0e9bf8d9d133ed0f75a32c461105aec61d54dc1bfa468f0430715ebda60fe009c91217f9ea00be1dd8302ef74920f2401ec9ed05ac3c39819d9bc9ef2964c0f58df90d1e652aa7c2b166663303b505c21d0fc0f00e26fe4727289a6a80c5906b6260c79440c034368b3a1f59a509e0f6fe308914af18a65e64a4b13e8dd070c626e6b954eaf35622a10fde04a1b2e7e0e5119f116861d4980f668c52a07d107155a75b87dd6226c2f03ffa997a0d0e11de7d8a4e538d71db12c409b7dac030126333b7854fe61707bc53f14352539be37d5fac5009c4eadc662ddf8cbb55a0b621cbd530b0d71d6b30ddaaaa365a786af836abd0f9e9c0f51ac175fefbb6800989e2faebf7ebcb2a44b1c95b3d176a102c79ac6564433e0b74b502b76350d08cd38db8d1c0d1a158e7f828cf14c83c0ee3ddcc0e52d899bae692f029139d20a2acacbcc5ae3e97dcec25cfcf83bea2c74387fc3e48e04ad012768e2207ba9018d8a162da44a16b783f9ddb42191dd4e28b1fd727447be72c8d5acf555394e0bdf2f969558e19d9364dad58a2a682ab23c3b32622893216ef07e8c0f5bcbce0f2dd2fc9a71c1a604e1aa2548be03a6ab686690d20b367d44ed1860f4ae930a07d4e8b91426ef1c6560c86e16415f4df32a423c5d0f5fa287a57412ee97a563087833262787371c8f9812507c4725f0c95aff33b52dcd92b4e3f4923d57a02600c19a6eb5ccad5052e3d573b914ccbbdef75127fa528fee8c0ff9f57e855f210064d5f0ae5338b69ccc779cfd9e5b43cae1aba08c5bc871399b6ed77c85ed52013bb883dcc2bbdf6d8514a6dda7a1dc4c0a98549af57d66528fd27af5c50cd80cf888c97cafdd71d0d85625f2745889d8a9da2b9a5044a8cd78c8f4628cd0b80f6e1607d041a899322f79e33c69322c958151b03b957c18ef8dfda36b48f47f0fb0613fa542f20aa870ab9191e5b123b9eaf7e41ec5e03a3cd86a66b9d2499a076ca2c33c971d74b42fcff4ad4254e3c510b094e6e9678f2ee75dd06dd634df0352168a88555610f2dbfdd621291b6d21c0df91c88791580b064da2b5d7c2ce0cd5c2a6c2dca49c2437aa4497cb416d7152940e69a4dec7f9fae344094b0c35068f86cbb9ea3771de2ade4169017129ceecffcd57d6f4d8c8d4bc15871cd1a2053820fc49a1fc6b7e4be6d7ba5c4aacb90ed8c2c796a5b814cf98f6c0e02d290e1c6d4516679d19d2aaa8673c26e89210dd227dd51efa2d033adb98c0ee18f80a5e1bccb209ec075f6eb7fa806e0265f6c7c8919c8ac402540728409d77face0b false -check_ring_signature b0f85d059da9391e0c43ec2abe90214d39b683b9ae42643023ec656eccfb42ac c324f0b01b7121324b2ae73269aadc8ff1addd3ef996eca7ac8f08dc4d1bd388 2 ae909302cd7c38c82c96b1a32e30566ca05feeb7585617b1bbd9a4767065c0bb 893d7232a7d81910ac66d10dccbb842392ab4cdd2da2dfbbeb9af881f0844762 21c18e74abb8caf9afd74af2a8da2e9a7cd31472b8e82783b7754b8b2a2591a216f2785a3c80cbf786ae4ea94cc1ff0025268c6e4300df1c7ebb48b2b94f4ba59a65d394c3352aeeb00c70f90d5e67caa56104e7cd12fd77e3e5b5a72733e20c14033709002e7e6ae40c60fa6ab4feecb819ef4433818a2a21281d8dd9529e06 false -check_ring_signature f049df4b6090d539cf01f6782caf0a0b7103c3773434dfa97b60c00dbcb1f317 af8c454bf92c6664aa0ad1f3d82a53efa5747e09a70eaa1be9163b2cf5764e20 24 456052f24a9b9fe69d1a35134dd517b09c88f8b9af053f6d4fde2b11acca0665 30c6129fa0d902e3b208e76377912cdfd3f2a44d7075c7e183f46b73809dee4d 6628ff091396a5271c3e6ada4c9ee415d1e9ae76c1423211c337b36cb68a6183 3e3b166c3f9283fa7fc17d70c6badbfe425ff73e1d80ffdd07c3fefdff5ac688 a0954b58974c92ae24878660e16b9e1470c1e9abfd728c6ae9813cf8875630cc 145ad39c5e4846257482f1079d736c3bb2d8ece032aa231c398b9f381bfeb419 14fb5cd38ac1308f489841ddb02a73b0a769d6afcf9caf4e5ffae7db06da62d9 33a2ae5712019f5b53a6f7026dd24cd355dec66d3cec791930b4e61d8f19cfa1 b85ee0a9b319134168dfd3e6d09fabfcdab0a43031e96ebf40bc0a4b41845d1d c684f05a1b61fbdd87aae9ab7728f6e16c4e681f6f09e1c2b34c01383d167b7c 64dad94af3b4aa20a47f2c7a52c1e364153f3a667e4c123445759984ee32c970 504f14064d9f59b73a307e73abb00ef3fbdd0d7d66ccac703d6e06bc3a9d4e80 e1726b0ae3f9d0753b9d67d223fe62144d0593cebf1491cfd0ea38bd5c93fc5b 730119c5eb5a11a3569ddc7bd6bc553a91550b88a098370e2a072d6024325c8d 501551b41bb99e75ef67ab91384e93be006a0fa6a21566cbe3634136c74c4faf e037c63f2902cc013e8f4994475345dfba371094edb41eb4d79e90a472db44f2 7f48ed2c59ebc4c11318c7bc8f28edb57df9a4c8470356378029396be904e01b 0091f03d0e9c5a087429ab051517b3a5baf4012f08cbd8c5a927cfb948b3b26f 2d17fba0eb0946ed984da6cfc094b1ad9afbba3d35706c7b2f00fbdcf839172a 2fa14420d072b349eaef4208b02ba689fb8ef98abce6743120a28c30d72ca656 45f8bdffa1dd074a18c901a297c0984de07fa7c7753729eb0eec7941aa85349d 2cce874d8ff5bfab08772a58255f525bb6988fb1b3e31136a14083b5371f39e7 05f7bcd57537da0e99c74c323a1357c5ecc6e094e0a528d48907323be868e0c8 17bbc07d00e23b9a81cf294d304676b15ba0f33ce28547b7b9f2812300a31a46 ec5ae9259fca659d48a554165d9d293d8b2e40495529de4004ce1fcf807f4d0c730d315847d3bb760efe8dc69362e4ca3b84bf86069d0e5a9166b0f27caff50e078d4f757269a7bda46242dfb357039412786acaab6df0c4bd6134de5ee9d90f01bd6e40d2abb9562f7128569149d661ee4fbb96e214733455ea89b672c33e0303977b888663d4eec7b2ae1d194e48999114b414b679be6c795ef9c4ed795d0985c2bea4331c843380d354f63f3df4c5aae9ba737db7ec67341f4643a2a07007af78b847131f0c2265a62d03ff282876575412f2cb89918d3f286ac46a941106ea07a4efa7beb2b01a9ce2d1c57dce078c67d13b452877003dd9b1c199092b0d7b4678f4a7c73cd60a3dedfc0bfe557be68b219d76c9acdfee8e6466f168b90eb1ecd3986f0812216165888a584a298c02baa5492eab2111ae7cbf207a5b870c06afc15d3ca28a92872e68bf1274aa4dae5107a327f5ff4479e1a98be2f9c205e345bf86cda00da5c20f5bb20a0605b0c8075ca04a3fc04cca0b7db6fb6181028ecc77f026315e6c10a0143ce1d9e18aa9b35699a0a43d7ae33a2915de7ed5089896ea85b33a88abccc7db653c895a8af4d69434e19c085e010a2d78a2bb57060c8f7eb238a1b6d137de029eceb935a6d58bff4e357981a05e09721b814a5d017ee852fa41150d7307c3af496488a18189b1032fb684a66f9f324ccb81c60b049b23d37590eb8ec399a47b33716b1edc959b5d993c7cd854e596b4a18e15b70b8109c9d82c315ec2746db9a0b15b557402d32e72b27d1b72dcec2ab79e919b04d65bc91204c79f7578bef46d4bd8e4845f92f3c3bb67071cfc1c32f15259bc0c085cac41fbf3c4211151606c6db75a1f3073eec3860dbdb7d7a4f9a3fc400707a27afdb0ed0d3a8ef5e8ab1470ddd51a57ff498cca2a10066fd71b9246455c05c99392514ea7ef8422d31f1449440da90879b146e1dc8e68cbcb365e54b7e9002615c93ce77644bf2d7fef8aaa476c1c335f9d52d735b0e551b0a4a5c0838103d0903fabc1fb1809492da7ef68b6916a607e4695e6f9ccdd9352f01ed7715b0748777a889abc14c9e6be529976317b724b5ded4ce11ea3a6ceeca4d68193d7048feb93ff32b3549953cd41901bcb365c972b0d4b94b33aaaf2cd8217b88ab90e060f8f65c96cb0fbb834d8816ba62f0985d8034bb06ea7da5524db788652b2010942e3cbbb300a02d5e6d8c1265a8a7a6bd077864d65b2bf288c20704f762506fd64665eb00dc83d559381df6eb349e214f79ff38af94cbaf269f9830fd66407d4cefbc2572f0770b8d43bad46ed47e125733ba7f9d2c8753b662c6d632fba0f9a29b2890e9625ed07d92fa889c9d43a223016005b94ae680d8e367ec01989060e1e0d11980415de404db71159e1797c8ea1c4f069f1732e43f20b48e2caa60fd3d0cce42362cce712e6ee547306ea504080cc97a1f030dcdfe990720d5f5704f3461738ff39347062a7b0ef9c822c6b8a449300899ddb97cd2bcfee72dc8805d83151e379888e641c09e601786ed753f35a4762b21828df0bd39ec6f827590af985a8f10ef57312fa863131226d1cbfb156f79ccda1640a990b8465e9beda07dc04234382235b36af05afe6f1ae5a4cba5044e4d4d15a5cadb03440d2374509fe3feeb0343008dac067daf61780225d1ec16bd53a687f334d60fab08fcf1d0d3166b5fb44dc98a22e910afc30e8132ffc3e105ae1ea242c90d8bb85532779cae98b22e36c13dd9f0d7096c897d732b3ebfe22d3115abe7acc1b2b53e9dc1408f83b4fece4f243400ad555b0c9cb0514e63f379b43cc72937f019191c23b0a05e2f64d2286df86ee4f44364607b545ccc9ab8ba02ddd506bdf8b32491c46110625e067d15e466b77ddb5396b76b4c7aac1320b5458aea50e041a29e36232ea0f05e974ce9781b221a23a83234101991a8665f4eb07743f68ffa86a74eafbba0d4a4350aafe3e491d74e709062db176b4b860a7d8ec59f9df2210f2088652ee0a5eec0d772f7e1949c711f64bbc439c65edf5e6ca4f1476fbf64d48cff52a1505f7602b7ec4478b87af8d8e641b38a309c9257b09407831578232415ce27aa10ff00b72684dc17b7cb0cbabea5a720f54637f890505b2090111a1fc95f9832803 false -check_ring_signature 727d0974802e64f27bdb2ecdd1d366d13bdbccec7e0d841eb28d84da86f9a729 10a9192284977c814a624661e4bbb8c0fe3301b0fa94e93af1a73ee19d73aebc 6 e83fcfa53fe4d847ef6ec4a571d19a13fee04ed188385ffe4c0455c2c0fbeb92 8c29ebc72da838905be44db88413c4167c07461c5e1f3acd32628d9d68b37b61 e59cf02b8a170bb4f0853f3a6cdbb22ece8854acc834564f029c577e1e6062aa aebf2335094d0fc7ef7753ecddead43c5796ab813180880574393ff7dc5dd2bf 2b80c0b1872237eced204982af4bc2ca9dd4df13102922f748d975b3ee080935 4ede14d64c622611c771431692bc076f7c3975736f2a51197b32cab4a88a8501 475da2515725ba06aaacd280d542f7382ddb439c03eb2209f7e6f45d3c68df0572748802a0704af7d52da96861ed5192c17f4f11310aa970f3dff235b02b6a0fff677b4638a03fe6a68acfdd5ea1ac1f2c2220ed27a723543b82a9df31205202a8b46759ba807785ecb81633eb410984bd4d3b70ecb6abcdcd12046cbbe3a507d4c221b8bca910b96dd360d628e1d3814cd10b6a7d5539114f7276f3a00bbf0ce64f7d833f4c0f4ebc3de60dc3f17cbd70940a6cf3bed225a018ccad6a84f501e0308315114417c71b2945002a6373fcb662eab174627d4059d6b99597b1c80cba329f14dcffeb1d2781fa6ebf17bd8e7ad62c1967e0bbf5694032ed3ac0e202ab3d4d207278d83c5ee845aee3008974c9fd2c4a4eeb99c7b0aac212d2050a0208204e944603a24c35d8edc163344eb60938c26a8363258668b75a64b88d4313475527caf48ad07e5515202a078f9d9f255725752e6971a16189ccf21d02600b023b727e1f2915aa79034b8b11d08def735caeaf4febad43574a01f782c33402 false -check_ring_signature 8542ff6cab193e5a0012d058094e92445138ee55b5711a32901f48fc993f2642 53895fa6fad1c47cf4c65a89a4b4312bd63504be7815221b246bef1ed9c3f082 2 eb413b1ba74c05d66ecfd7a53d0504478b2bead67b09321f6420b6aa59adac16 c485b1bb0add61dd52863b045cb06cefa528c35a6162cf84d0fc3d3456b713aa 0ca160e15cdc116015e4021a20720ed8b0a3f345e9fa3afa78d08f86ac03150d1f6b588ecf3e41ac759fee6e55660082de3c6d444c22879d0644bda0efcbb80d0470c7d70fe587fa35429dc0c96d1fafa2faf18d0e66ad3e10edb6c7abb247008b3019c45614f7dc6f50b6b20bc7e9441ed3d239feae9b8e0014185422b097af false -check_ring_signature 43f91b6f2ce9f7945b0f12f8b37423546973308c8a17e9db8edc6b39e96764c3 f5b455774c597160510594ccca3bf9cdc1a56003d5302c29c584ef6b21d4031a 28 3700bd709393fb62082e87d58c923de84ffcefbdb68c2e4f1d150221fd8f4e5a 54a07e0c6b65bda278d0c0a256804358a87e25bd26eeab50be955cf08db6ad28 6692fedefb10aa662c891e92470a1b0efc4cb8e0c66e7270fcb9546546b479e9 279d06ea0c9d0c3ac1bbbf2e743d5ab4566e93691a0273090a7e499ebc025542 71005f1c5ab1759b64531e73a18ed7a741b644791a58b11a0024b44a3c846c7b 3bd251e8830f6ce441452cc5dad94d889f0283e3401fb272189a09d735ed45b6 b1239fecb3f2697e4409c73745088752da6642c680d9497c9a6a5813d47597f7 bff26beb49c5dc8c2960cf754b73e34d966cf8bd55407dc1bacba64989fef275 f34b184982b68fb8bb126acc05f481d8ee2151b02a8a98a3f7a60b31e2f77e52 a7f4f98ba5635a4545c83366729586d1991012fa58a86e8c034d3f5ddb8ee1e0 fc9e7c44a1acd0aa3b3596324d45bc8a0e9c908f4549edd1e361a0f0db66ffbb e6a754636e8f1757a6735ceb42b52f9d8743c0bdfcea5a2f3b80bcb390a352ff 06b849d8f19233ca165cd7ea16a75120d037d5b31673833f6eb9458de9b67cb6 8916dacc894247452627b5b86716809a3d853cadabc9809bdae3ef6d31a6d01e fab7f5f75404d95f75d3613d3866e982a9add9eb936ebe3621e5e460b3dd206b 65c2c30e885a58bd7bdbce755b3e8b31ec5f4ef1fa6602594f744c6b6d4854ed e4cdc42035b6bf34ad06fbecf7a7b718d8f815272d5f50459fa57526ff5d40ac 4bc75a792116d1846d440a644c65d30343e04a5f705b40764d1a3e975c20313b 49cedca7237a5b5b9ad27d05dcaa58ba8176d6dfd1825e33f27eab28686b76a0 c1ef52a0be8fd9bbe4974083a0d5c091de1e89f178253ca81871828ab0623426 3c27a091ecebc56e0e71fac634bee55aa6d838938eb3286b8c6a304713b58fe3 6a650d35ed7df60c3d91c3671e61bb2b3c841c6d2e50e27181fef5c86ee65f43 27d7c92d7bf8fec47e4ba058e2db3fee8748c764922c3e91b1e4059a659b3f50 721d70ebf89715e052b35d753ea0185f2dcdf37d40b7a5c3db810e6355aa8851 63efb7c3cc5a3393300bedc36e31554182fa1213d47eac06f2f00dc442d13a08 d66d22e1fa73196ee82155a30fd7f58a48f65f53e6cf1720ffd6088e2edfe0ea 4b5166e02bc35b65e1cc88d2aeac2fad0d924295ea82057ea20d89defb547342 a3bc6858ad30ac40ef10c6054a73c5c367b20e5383a8ee6e643989985942be90 cb6e8edc93cbf47495bc1a64b5ba872e94a39fcf9b6b66e538728ff1c0c6980c42a1b0cf47f537a63d48e95066b37f93476791421fa456ded7c830b877f02e07528b1cd327627eeb7070151ed10ec911497f02f06c09d619d99bde2df08ade02e62cc0c8723a18129f934f277caa2be024bceb7e6f4e8357ffd86d872edace01e4980bd8cf706ad23aca081bd796b3861a479848edaabbe3f8813859bac5f00b89b3259fcc2c65f087d3abf1eb6f1f26e7bc6b3d39fb21ea166b5a3495eeef0611c7b66becfc919016a0ee7ab20a1e33f3ba2c2a6745c6cc69808562ca872404c30cb5021a515296b84441f6369f71d45360a5f44c4e1621756902b0e00f3704949a2403b20bfb2a4dc95dc0787488be5f5415a0e1df0c1a1a848cef5ee5100ecf0f4b0c69421c5779068eb6bc30902fc2b19336ebad4067824dff6a5a30d80981425c6c9bb24bac114581a5996cae036518516bc2ae80c55f49035e451b2c0d6deda1a1b798356657733381a512dfdec05624939fc5803a7acb3c8b5eb4f40b5b1a774a0d36afcc385129280f7b8c77ecb0145169bfdc1aba61e58f61e70102586c8e428082f6cefcaf2e52a777c7ee1f420812feea537ec172b6d6ee23890aaf2942e29137d6e4bafad8a94b7e817f261be9f49faf4e943cfb4b081ce8c00f57ae164732eeb1989495bf8a1ebea49c7045a4199ad4376c684c1190c760c8043c940fc7d01e112a64a6c13b626666b5b54541836eca2a451c401398bcb5b40176f7c8254611a2dcd8d08363410edcef5b285e7e34919f4dd2e7ac22691de007a3c9249e148b04e4e41a817c68045b519202d9cc97c57fbf71ce27db633f7a0672513ad9250d5f8ca4f727ae2fc80c062aedb8cbfd0e603a6f9b16b2b53ab9048f24681e4b1b941bd6bab2d22df94623f511abc96c824f6eaf72a1dcccd01b077df994da8aa405d34543f19116cd718cebfa3b578eb66aaca830b95801312959c90d8b2888bc0de89b4a731953c59d9f69c0935a072c792627bdeaf17c17e2087ac2d964056f4070aa30bbc600d8002fbbf1a2de133f09d6f47c85ae81fcec08365934d3674ed2510ee38a05c82a56138f192e0124311d7ede1417e32fcca40b3521ad388d1070736d10cea018102931af33bbe6568249b96ca396a574aea5068088f5c684402c01a52d678ba80b7be18ecf8b58d1871b4938eeefeb933962025d605240b709bb4834a8fe1bd81cf3c9b69f2048dcf7f89ffccd1e54711fbd01132ad6eaf65dc8c4b3351436aa0b33ad2cbf439871b32c8a6fa3305e3b45390ec6907a4b18940bd88c2ad1f03ebaa269a164e55f7d18cd97b71d9dd84bdb3706851c6de1b620747e91aadcfeebb4ceccc54a0ee01f598e1859132ed46de97c007da29219201f399e93f362616f33e0f04c04b12dfac0bbef378602505ddf430b2ad3f1b9419df82045065008d06abaab77962fa6b66da9ba154394c578e2850e2491ee2f62e480d9e54801529607dc4eedc1c762462114f655fd1458bca526036363c8005a366b7bf7812829e84e81a82c69e7118fbf95e8a82b1fc08dd3f6005014fa7dbc7fe0766038d40e9fc83b95f3074cb1edf6fca6d1745fb34b8549000b7ae3efa1ac7fc4e401d3737a75dfa258d6dea7d04055f7e745b6189667fc08eb0d6d0cca63a2cb76917b8498023a8b74694d3e07a77b9de75957e81be1120308e5165cdd2094bfdc5bd8a52123f1627d373e0987b6d3ecafb149977f2bee0ad4b522a8127f5e0d76f6a5dbf9f68cdc7552b2dff11343bdf57ce22681651a0562c4345e374a177f43051d860d9cbbccfd2408a37fe2b183d59a9468e9ffec0ba2a3a0268a784c702b68b964a70d222ed3bd0fc55e6de79cb76423a89b589d0002d7b9418e309d3bc4502e37df6310a0b57b604cc2dcfded1ef1a7bb4bec5a087e0f0da72ca6b77e0877b323add6294cd7c38bdafc2eeeb4745e47eda420be07f23c30f208a4af2854443841cc82cc310570f41b39827f938ba2a5478d5f7508fff7e0067f3e1b89371640e4d2e0f5e378cc5922be489817dc90c92985bedc0133dc424fc6ac3615c0db5b3a3f4944f4eee8ba0fdf12b8deb07c8848140cc80ca1482148311faebd35000224fbc6c7ed7bf2329090c5c3d07f564d72817acd04acaf63dd66f74b291a9fd7c4b8c7a7d2b901cd51c3498ba2dbbe1d12df960a0e9644dbe845e9c99b3c494711e6bf5e579a115dad01ad99c521c70e6f35c7f106221af96533034989c7c450dcd03bfc2a1f7519978818f59c36094715546f310da236c9faa4fe4c1caa60205dd5dacfe925326a029f4b44912f9210ae165c2703724ad9b99b249f0301642e86b179fe8673b12f0ff4badc7ef3a733e14db2b902ef43993fad40c3fcce3df21e87d1d0ef8c1f03ac88d7d8e9a04808bc5ea3240195c289c035f7264a45cc14ac1a72deae2404a61fdbd19be956a3e21361eea503c97870750a8a53b8038600f74debc7de2a1c18b1217d5d6de4365ad88aa80e0e false -check_ring_signature 2ba180dc783b70ea06595cda1c83238f25c695433d01ca442d64d883ce1febb0 2fc26e0b31bcf61338df1630166a6401b3846b803fcf25f2f4eeb54c2d3b3439 11 f607c8e37663b3d2c0e7523820e0333f0e281069c1ba3e34abbdf68f50e46ab3 ff731032d33b8d287699d66e2606150dc32a975cdce08ae5bd2f6fa519d9a217 3240f0af408a19729f9d0994d04f8770d7f2e9a1bc6fab9a9633aa951f6d41ae 2919ba129f0b45f4da890d92e4087b5d24be96b35ad368e36d46cf30d64b123b 64fdd6a6478c06a4e18dedd2d0b30e2b3393994c94879d244785c642f72506de f4e416b89d369fcc5f904597b40e4bfdb4ce6d3957baebefb3723e3944bdf787 f19aef95cc941506cda8fd26ee46aef010b8131e092616961e6238fad1d32b2c 8b31dc684a9c1e18df17471188e237ba34d728c34b3cb96fc6a1bf54ab9faf84 4103c2534da5c9afdd4b24459bcd84e3bcf7c1e318e0fe007dd313b026a0a48b ddf31737e72a28f8d99b6df88728b0a5245b222dbf64901daab4919e755ac484 b85fb6bd14ed86b7b7b01285cf40bf855339edb31c5b3c297bfd1de1de80aa5c c6f5d98634fe0f8b5883f6b4a8d2037792fa80c79fc12e2171eb964297e90d0d20a8982ee65fdeb984381a7ce84a34bf03b9784523883e8a924d7af85c832c0f655b13b7a4bb870bb437508e1b6018ada89a63aaf6a5b2312af15425a68a6504c99831c55c0f56f2f52a16e1403f6b11fb0756cd46db45b588d3f271d93aee04defbb0ed9241ca97783c760662bc943d434378493d9c7d5609c1ea3fab9c970247343b44dfce48617404cdf06f0a0dc605922ebc7a64b282c39f9138dd07a50277dba6a83937d2a2f26eec92b224b7eb0ac58ac2ad981e336e6f707c34f7a9053fe58bdda2ab7f7b89fbe7382d682a2db466b41968f5187d622095e2febb3d076182ba6d4ce18000bf4e083c4086283773555675d8933cdf23a5c07bdc8f6907c10d6c2278408c9b1aaf59bf6619d0c825843f5ec28851e66642c64d10e21d000cfde90af921f4c3c14cbb05de8e66ec02b247509bbc946813a0c98be0e9fe079a1564e53801ce4013b00523be44522e6593a65bd6feea2b053588162fd1110f0d5802a5d6fe36157a99f800dad0932bc0571f0362d9cb318b025055ca61200341dd60c89e80d2682c7d4af0498106d81e9de3bc04953be26fac2ed8684b2b0a9c18a23c995a0da0e490e7c7d8f63149e121a1f00284495eef72afadf3f8cd0cff605b25900233d64dfe669ec5a28bb5123ab61373aabea479115c0a98fb590ef433e568c72cd8c39a0736779053cb4497a51dedb6811a7544440742ad14c009110279fcc4d07c9eee0f422e723634e6d86fd9601aba169f012f7b29938fe10509e2b551c1a16b55c37f38831ba7857949244a71d8c1ac67073f11a957921805d368fc17bbb829291b1fce5d251f9c666183b7d0704983fafb6b8e2251b7740ba367299cc71f728e9c4adf57d73f7a46d0f27e4e1056dc41f785c43953e5c8042ee3892f8d4eb1d31ad80c6fa2a8465ad4c620e0886200710fbe61a985963c09 true -check_ring_signature b7f592da0bdad848b36653034e5431c42e752ec724e959b912a473b67ec9098d ee3802a350bce56526d81847e5210aab90297407d69096e5bd07eb9a8a2f0648 84 54f4ac4b767811d2047ba65360a62f723f37bfa2df1cc33a509cc703a19d95c8 e6c091bcd079d4a6afeeabdc05f696e7040d1d4d7465dc442ff085b3833f9b6f 49dda6263258347f07bc27f3cd743933181b5c58410dd8f35f45a039b5cceb19 76f82680c64f8f4136ec9912a1ceeb97dd3edcb6e2fc29275680ee4f38b789d4 eb8ca1e9e061e89e72a1a690119b0a3e8167490b1f4d692846dcbdb416635e69 d42a5143b4d4eb08ff481c9bd001deaa6253dfb71073dc11ede1b5ae786e2704 e9c28e4b40e6ca30a2a4fabb84c1d4283582863e6c067b800b56e10cef677779 6d647ac52b0b2e5907d513304030a81eb2186b73751e361bfd352253e97b06d5 beef501f38d49bf6b03b06b22da0ccfa4d88644102cbb0756df49856a0cf35cb 36f0b02431d32dfe54240b2f9fffccf5ae51d9b75699c955ce13abdd209791e5 5741e1d0a5f9ea5c02753997a39a04b2983e9ca142b903d4b11c9f8104f9f320 e1a16ce155c69212a94c6cb89d7d64a1d46250984fa6c5896697e3594c143fe8 80ec6e66fdca259b2c5471a5b1bfbdd85b716e70205e74e5af9d715fe3688969 a68f1a73dbde9e32c7ed5202da3b96ab0c7143674d64d447b848a96a4b23c6d7 72415ea5eb3460aa3ceebaf97c44f9c731c497d1c3388c6fafcd28adb963cb10 c91387154c0328610f33878d10be83387f7695101bc3a18da8ee42dbbc038ff3 eec10f7a82c93f2897964f1af4e640af9f81b377a91b238aa0b43163f0049291 7afe49c9627b09fa57db794352a4778924e2802a34fb92b2a9174d98086e4b52 effca901edcef670fb9a9fdd6fd30f418344692378b3519e9d36afd3b2beb7a5 ce72a73ddd20cfb7748202a425bbafc9ccdbd158cc46db832c857d10e7714b5c 41fb47b205f3d257507ea6d628ff4735fee53bdb906be1919a44bf4cef10e7bd 2af9e42c56734ac49cc51d2b563d1b90126024bdd34abc03d429804c62f7f1d2 9d7752874f84d1fd41ebd8d028f0f855888a30fef31de6775b72ea9c28e5ac06 9667f8f437c250f4a0c4eb56677bbad141de57f81221947e149f96f70d80a747 a00a8d38583268b911bc8a19a5449378ead3141f46194e3cf6780a01c6015435 7cab2940020e834ccd715aaa1b742f2032713b899f0cac39b600e04e9c7c200c 1a7ff6974dd93363f5ecfcd2749714d0a62df55f0735ad5ec8b05fe87dbdf375 aa17e16ac700415db0c6b8d3d9bcd63b9c4733e4fadbf21c56f2172af3816951 32483eb4391d1be507d4733cccce1a0087cb6243f5306168be44d58578267761 94d4545c555da779d72efc58aa338ac97e5a71d31fa62fe9795e53553894ee7b ea749931329490a3d4b321869ff418a40c3ff4caceb180d0191ef2f98ca3ea3f aea12a2148954795e32bbcf142f912d6f4f30181848e71c10afb16c0ae02da99 ea7f012f332ec6ba5e41eb4c1efa2d64affbf66f49a5e55fb8ee91a97375980b 9402f29ccc05be19039f539e73ebe8392d1d27372d6a6a04b82d254bae9255fc e61e8a30bf50c60ed16396fd97603c3d7694cc0b50ef79fdd22d1e48c1e13085 0bc1554dee7122c07b1f1703ab13ddcb2cc81859cd74cee65b3227c34ba3cc2c 5c547cbd582ca6c3cb356a99b1f40859d2b04c5ce49af4ba435ddaa833fc07aa b87325c2f3df9a9a9584cfe91e8c33a8bca31658e4ed025856fc61505b853be7 c4eb807c1cc0815683e7641172421f9f56d9c00054b1110be25aa833313b64f6 4e307d119717a1f0f673a4a5eb3df16f2b97f8a44aa3aa7efc98849d022322c2 48ce219114b077b87de61ab253f20fde670e03777cc230f7f2a3f8befe83221b f2c30fe243e70dd986f3738240d147850bb07095aa53a60b13600d0688c20282 e3e16a8a38332f53f545feb53414d6671cfb5a967e256412772617365efdc8d5 ac23d4b907dd961914a0faa98b4619f86d1e70f5b6c453e361625525d4307e87 b5acd7ed79c54106d0815ec6f9e17c195c20fcc25fa18ca5e8ce48b201148c17 bf54a1dbc59a00eea85dc8c3be319ef64579a4fdd8b7cc20742cdf11b3ea66c9 702c374e5d81e01c17d8fbffa2ea340506e3ce2f2eb54f1cc533ef83858e45cd 3b8bb3b4e0689cf3a28d122e4d20583432f77300c717ffe6e680cd7a3109a25a a8067a9d07ede35e3e374ad0f5981f66dd521f7a0dc8974fdc23bb3724f62c8e 434de12c8e0d096f29eb315912eedd2a50b7b05abb33ffb5b4c7ba0c08152dbd 4795de0c5f0c4f773f9c8a79e0e277e7e7ecf71a84640d016ec9ec8793a05002 9dcb9288a3cd8ccbb47951ac2ea2188ed7e91ebbdfb8f9b6d65f54806d2477aa cfcde4ac4ab6ae6e44d22f6c0ef085ae765e9edec3c8d4045d3128e89f681a28 d1970dcf3260cd072d550c9fb48fbf5e1b9922c1760d089d78ce20cee69d9d22 c60dd2bce50ee8e581883d0f1f3bb867898e5ffcf803a2d0ce35466f27782a2d 8f65127fb7fc1820759799b565474a6bf84d7a95c5271a4e1c71af53f7d3db45 04806d609ba69c83c5a6f0720127b8f4990acab98d692155cff4c2c757a3f3b0 1ab0254e339cc1b7e0871c8d17aa1c75f09f3f2e2790c22a1aae4c8fe1dc5949 103ad7286e26ea34d5af6dd6a94478488e22132330833ce131d1c601ffb744d9 7e85951de2d5f53be02c4b105347d859c44dcef7bcb96c5617e48ce69e4c3b65 384f8cdf5c35fcf2df56c38b9b250d5916ee5ca00f12a3745fb8a0a40ba1193b 6359aa76f59617370df695de9d2d37753dd70f23da119db097aadc21f426ce71 09f981398b830f1bddfb5756beb874d70473314788d59e2d206946b458aa4899 d9a41c9c629b95f3d00fac53da3534952583f21f45045b3464849031f3607e19 a427356faef3d124e77c6d1167a67049023ba8bb7f6de03084cc5387ed712489 79c7226cbce82036e49aa093ef9e22a7e4763be1df0db9406b07ec9bbc3ea8e3 d161c6ddc1bdfcd93fa8900e06193c085244ac688d90fb1c125a6a654ea2c653 0831c60692337128ceb1122a32c871e417319054a6e24c290de33a42e0d78802 e5287fdd8811fefb817713020b29e8c3e0ba70b6790d9f6e663fa546a0cfeb68 5c7a116f27dfa01f8107d40ce912417a5a55261f7c745069db5527cea6a5aaf2 b7125835e5d7c702f6fa6e57f00504b2b49aff72e2f06c1c6c817234bc85d657 176b983c17f1576679d618a7a95396a1b976be0b030e692d22e23b527918a9ed 7fce838bd26586b7093faa5dfbde8417fd4d914c01a416155067509ffe69545e a631bdd4b5e4056a50e1637552c0d53874901f14054fe07c2445257bf0ab8f4f f76d25380f5de7cf6885ffd74bef1192ad679c9b76521ce326a596d0c83d5cd3 1270d41e160f5c30c057866f317b4a280b9825fcbbee07f9f8fda2c540eeb084 6a718e1f74385a9f559b4f53905b2ee2056965ceb0e709411ba28f3632ef9374 0f995727ebf7c0a575afa7995e79016132abf080b28ab0c94845709e13bff75c e43b08d900a11c695697590a78269694682021f498747b0afd08177ea05f6a03 5d7f370408e7f6e9d15ca1b1245e13e4847b8d2891d3a5b792bdbf6805df0dde 44f4893d5ac36c9cea76611b4ee6705d3cd299acdea1175054115f5d927d67af fba46422377d57c37f7607e0e6b4c909459b5550916571c67a2400215ef3c32b 826752eb6d977d370bcba68b9d4a5c2d9e7e5375b36e039bb443f2efc0dc5e45 40894b257c81807763a748bb0de69e3327ccda32a812bd5f71edf4b8a1684784 9b9912cb2488b226b85f2937db38cedce65596c75263948618e4812830a4940f369dd4bb59eb7028ba462a1ee86acf8905b99a1f4799d6a5d44a69b577e76a09a35b9f645973b53cb194126442b9d382f66ff8e6a7f2473c920b6de810ac840a294b622daa1c847bea4aac99b6bd4c39758f7b506753c9bcc27d1df2a988a306a7c9b608baec44a838a739b1a00d19fe2399b06fc1cfc265ac6f21e1dd303602f9c8a3a22f3a4fd676e49b61560f3c04b6b8b40e63795f2f9077095dd2057f097dcdc7b71a999ecf56315bc8fd15f2fe253edd534f1680592ccde2385011e009e7ec5c32e3a6d3d5d752f0965f0e7d9169be8c786c2698b872ef60ce6641120a77e0c0a60984530f5a8063937f098c2ca4d2c5f0add43afada02c2f8b3ce68039085d700ed5bb1a8258abea6aa6eef361a9d4248c78472548887075b4427d40cfa0aec80556b5a302dcc7863322df38bfd668d056f84d7b7790c20cf4f5f8000bf80935135c03cea342e402fdb7b1e4e3d7445b18ab15a953a5e1ae511b60e0de39b55fcc574204ddf270a97c824ab25971c29a34b23abd09c65137c9bcef7046e8b6adc5e2c4351c7cd52140a380e79d610ed08f7fcf9149a9764de4c8c6d093dd5fd5edc5ed2f298a7b89efc2911b1db39c99efb8f9a6d2c18654d4218d709a5a3cf102419e975de88fc7a5a6459b99f7c7c2ab33b73992d8a224a86f2880266ef635c1cded9dfe172d7e0adc68465bfdb8e3d30c348caed2fe76a32df200b9750a3ecb6668fd20533612fe1d64fa0277f0b7e01ccaa8ae1291e9700140c007918f9994d43f9b5845a3faa8800273af054ee3dcc7263ccf78f023fd4a53a0c608c8e70bb578fa381afe0bbc26c23698925ca34402b946f6a21b90a15a86c0e26193a2b8168d1175687e21eca1ae4633336af68530de406dfaf8660537e430b171d24e30a979ff69a3949cffbfe7b76ecfb31cbebb6e08581aadc1e08fa170ea17ea0ea5f68aa6cc7950912b67310bc435ef56ff2907b7a9362b806c6cdae026a11faec798c15898590658e2b7a4b0c2dc56be6c315a734bffd45c01184de04d189e611cda29540f110532e7482aafd1e1862a33c576512893a5fa8e4fb7a065efa7aa3843ae626e4d246ba2b895068cdd2d879b75ff2652639cf750aab730ed07e392e4547a6999f1e3d6d244920e9d9aa90ba6b69f8350bb5d8c3e8d1fd0583680a2ab1cdabcfa23d2e82b111b257fb9a3c1158defc66c3f9988e69024004fdb99ab42c3d929688e61a214018343688c224dc628921ff8d533be39062f00ac65ba0b513076048a651218a6db5ca64ed1d4b324726d0d8b1e73a5d8aa19c03d459e1ed0ff05d64450173c75982c4e708f20b4503351c03337bcb8a935932061141f02f12e993756f64f7fdbf3ab54c8ef473edc395c35d7bb5c5abffff430741dd8cbb371345b0ab555bbbe8b394527db1922eb0b16cb82cbb22101f624c0266a7333ef68971d788c67219d4cddc7b7d8453d9331c8e5bd25397f3933ac506a9f1f34bbe03fee1d0d311a5ef15249707bbf9e031136c9283c1d68fb6dca90b982bfd69d091d08c54c2312ff492a054f6023536d70dfdaab7f30789bdb0a2058caeff2b7fe2acab10576436b3a09713b7a4c420a42dccec2bbf14fc7c14310fefe5cd916bb90d2ce44a33945b931f5206ab39d450681280b2186ed0948ac00d2193d248b9e9375336acd3593d57dcbbb009755ac9b2a9d69d7a894751fe03046e12042fc19ac4033da62ff2ce258e02c1f8f246f67ad82da73ce48a7ee497014783871b43e4f59b338bf6f49e065bbf88ef8ea3f434a95505b1c713a8bf360e795267d98fc5f0834436203f79216befe642a20d4890cc794f709b0a8f5b8206d946bcb0e443e472df336bba326453c6a624f0531070e5488d1a49bd5677560c0ecfa435f28a6d8aa7a22718e1013e44ca5a88dd95a7679e60dea98b5bacf80a1b8ca5ef98d97e69395e261630668eb4407616322bede6cc2cbc26b181f81700bd3901314ecd548e962cc255c5fe20fd99d9623b6f73814e44cbb4858ad6780a3d7eb971acc32ac7bc2919b8b66419ead9ac228cfbe3c3fddb9353922eecd6029648aceb5e334a14c03c0363fffa32bcc9f95d120d9e974c3d02541e0fd30f0ff7eb152bf35e3ede18f3004facec12f6f06cb52dd596f067f28fa59a2b71e908beb45d54e28a968876cfb6bf747b43c4b2033257af69195938d8a0e6145eac0ccbc95427a73c92cfdf5115cf4942eaf1a125016afdee69e33ad2f9185dca0f0b9b9eb6ae041c8ebf699804f99c23e59bebae7dc3235cd81826b3a4e63c639107ce8a9691b031f9487ae35c7fe527050fb7b756062216d8e4425efdaed2112f0764f87c33dfe12c3d7d555dd46ac6784e789c8565f036307fd504a2b8faa49d06bf6d92277491739d05d15bf57e1064b85d0ae3b32b2fc0d71c72aa10db66d40402113a69aa6c1a771ff9f3fa434b95e6c0360f858a06169ecba1ef1add768c02736d6e2a648f505006b90427cfe9248a6ab06f3e79c00ec09006e4ed863d5807f613035ef3f775983188562d3bc2bc07dc9bf3554ae7f061be8fc19cf6c43100d237c93f562e30125d03388380662d4bc3d282084e385a27289d525189882900cd4d09784d0519158570795683bfce236ea40b6545b2cb67c89d25da36e303057fa989eece5647116a374b8a22aaba4e218287b03e14066866c811426a92fd07d2e70c87b00cbaf9368ed88fce65a461e8c7636c1f913196039adaabb75b5e0823331f616d3defddc005c687691fa04382d12f1bc007c11d685356c0738de80d2a873c56e4788206b748a853c7834d0d7f7c5abb0d4f299deeaf2ab9e9d7760ece85c7bc0233d930a98e441c9e09b65b5f471457910ce10c2816bc0f575b6101566fe464dfc67af4fdc3ee11993f884b9dcb45f95b99835f5d4109846feeaa066e85901b94a43c41be61689d227e77292d4c87629219138f7650677e1fe23307b02ba439a9dc6b856087d18a6de3bc991e7ecc07e5722df63c8ae7aff6f2c50fe460375be42801810ff56e13dba9a369c4f018d28f0d4d9f006b12af4fc84f0793f71210af4f4de59cc10281c4755be2b5ecef30283e8457c603d642d40caf01601f59cbd461f8cb9b625fddbe104dc521dbf431d7408c40f135ad19ddf4af04e11b8ff551b7e6405878ca113644cbc35df47474e1ac129ab6b97351eb6003089657eba08c0d2c5df33e5ad063e928f461111b640153451a58505b528b9dcf0591b3750e94a289490dbda64bbe960c12fb694c4943e8c673cdb54b48f3abc50912601f885d4a1094023d1548bf3c57fc12cfe21c35c3906ee055581a201f2d0fb514049d3b8588e00965c6652a2f7cda899272df1b9eebbacf3f9213a27b1308405ae857bad3c4c882d4a46d1e7c2123f376419c86c749cea091e11777331a03ad0da6c0d1cd2d4764a47cbb841e91d51988afaf524ca9f4015c6bb4cbca600d2b7b1c61510dcd55f0ef80729dbf249492c8fc6c426c6d49610513158ced3203ae3e0ff845371f60568bea1622de87358b1204161d680927a2fb84bcde0d980143783bacb46ee05c893b6716b44b9f52405b12098daa4521c610e3521be6e305a7ab5eab4b247453e64112ecb7338aa82d8b790a2a9e80daf5b6ddc1747dc609eed5be0ca99937772bc53d9745d751b6b42512453bb85e76c3f80b1ca57efa04b5f396899f9c70a06b26d73f64d0a1604a066dacfad269b6edf8a12a8f082d0aa01a5f9186ed5dd4a656e4007018eb47171955146911e9137b83b04d52d1a6022ec4aa1cb3be9f61d20551bd6c2bbb4b89a09e16ffcf4adffbae9d702ee4960b3c75190e0ef79545282f10d03ad50b80c367f52fd95c939ed036e882dbf7be0acba88819f8d6d2e10e837946f360479914b68f00d14afab6f5c87e0be8437a00bbcc2bdeb8670fd00958719d679a954c1c169af63e1924d023363a120462f30d1cdba4cbe8974c742327e56ce60a35c715a56f41d26ffefcca7a76f4d15e6106508bd1cd8a32877b436f2ba552c87e9ed7b268f3366962c43cbb3780473a5d0d607ff550db9574d8b6b28591141c02ed6e2a4ffd5bc684c273578bb33f762901fda04bf49bcf49423c435d6dd3f86c14794f4130df7774a7670e45ad72204c0a6423cc4fb375a3fa971563500ebdbd3d6049d0c613c1f5f7fc3ae473a4b6ea07ad1097de607461110157bf3817d9bfc349b3dde382716b5dfd07b0d04eab4e0e955b67a4cbbd2f7ddd6e4ff996907bfe40fccd9227e551fa4122e959b967f20641972cea325e633f087abe16e4b92ede3daf7c0a9ac897656c9a1097d31f5a08e756c91bdc2906faddcb2ebf015474126f3eea833151aaafbffd49bdb47b210b84b7857a9d4f9905fc04e489fc9813d36c1c6fdec5f031f68c1d0cd98d3d0b0cb11da4387986234076373a6333a08d89b28e290e8fc23201389661ba1ca63103981f77314f2269a287e48c8631ce804268b431d4ddcd22dfd5a32dbee662c407b63c1b501e83e4776accdb8467b1074fef6a15d24ccddf9e24ca87b4f200430c743686c08384e63e4de98a31a431defc3ef577fd4570b2dcf40c06da0eee2b0bf51af49300ffe62256e642fc82d716dcbc5d982ffbead4ec302f49f224da34053065f3ca3c8aa5889875967f6c4c72810194f03a0653289713d4212fc475b70a60c348c6db18bd3d5c03a7cbaf14427a51ababebdc78fe72f0a2af1aeaca5a0729bcc4cee610b7f6f73aad80c7758f6738c3b53c4be475b8bdc7bd550a7a15091d5be7aa43b5196c065d1e6dcb61943debfc3365a7d7970875b42fd19a82e00818a1fd5a78583f4ef4fbb2fa2041aa044c3dceb9cc05e1976c09b16915b63c0c538ac43e80304987799ce948645ba364baec6c2859c765f91620256be3d8ec03fa4944014270b88924f03346ceef5835016e111804552f8ed472bca12f642f0d306cc76c4790e67d1886c19fe16a40f5ec5f42e4e04a66b14863ff43dcf24b06fc2595d743a8e45a32d990cf1b96ee4546ca705f233c0eedcc72ff5f4fad930f0d455e238dcacf6688fc116afa8197a70cc5dfeed3e6dcdaffe049dfda220b0c493777011cb4ea87b93cd71bc848a11e05cdd8dd0c94e56432d2dc9285ee30010553ad69eeccf3a6286163c04bfda98d6c43a7e315a6613dcb99cbcc2418d60d63c88a3ffe869386eb455edae0e1560c8188468ebef2ba5d6ced561fd140740c3f66a293df2b1ce264e807e181440cb085b67148f951c9a344bf76bab92cab0909fd9e771b9baf3f47fd3bda96b46fd124540372e78f32c9b664ca2b4676280f073a6167bc4869873e47ca6e32ccae31b6a0d39e9abd9a13ad88eabe1295510d2d37dac63e736d575ad583a72b80e4144c01acfb1330010f6b6a5b450a7407034727bfc3876f4ab20517e7afd397d12e67c89382f32b3ea8c4a9e4b0cc44bc0f1a7e0e776735dcf733fb79b25520dfa0d34804325f37033c5d969f6f774fe8085104f4270c4d2af91e743b36b941a4f9cfae1771e2fb06d1392913389864ee01fbedd83cf5b6e3e9c969e403ff00d95da075f5de71cf68695d6865c7abddb30f120408f30c2a05a4433095488e243c699d0a46945d0278f5a5298870310d0405422af78aa24fc434856a691e8bd06e6c259f49f85bc68527609e369390aefc04ddb14baa725fa7f2545f7b0f1fca9f1b30b648b48ba91b20c2d031f385d0290606c8b6af0543206fa91dcf407448928e5faafb4ce81d2bf10d4184df4a114900056b6c48d01f1d646ba0f6532df74b7579ec790a15981f5de4a975342f211600325968532acb0ee4f228a87a0a1cb936a9c383b5f684e7e25b754943e25bb30b9cfe65b0f1983981a34bbfb2a27fbed3a4d03dbef2889f5dd01ccac05edca00fab1230eba2b152cfde5bf16f88e371deee4c4eef4fd8e593a41d2e3604000a054056a2321da08000dee7a4aba489d4155af28b77ebd8c2acc98626fcdde9130bedd422b0de242d428db0f428fa3d5223b9700ef98f38e091580f45e4e759f107dd8858333c91c9ed6b9d40d13c9ae45c9cfe47ebc2f0c6a207aee8dff94f1c07ec7ef62100f7f15b071814dc274af5bbda07554d42313b561300fbcbc64f5b078bc5500dd346aa53838594b398ae2a083b0e9f84c6e5383cc2edb7aa34fdc104548f3b03bfe3ce45caa5ac8676e981147c6e9306b17b7a012635c4547b63cf035b1a7cebc4c39c05ac88518ea0f77270e4f629a9e04ccdd80571a57b65768e024fa40b5ba22002a35ea19e21d965e29e95a47544cceedf3fc3f3ca04d0d32609e00be5da2340767b288dc7de33223f47db3a3f07fc50923d43d81ac7d6ce4c0d64df98f7af46dd75af3d2b74931354f91e1e081c27b836fe3cd09e55bde4750bc3de647c10d460b1a1024ced4464c63bda3c55da3f16135e87b873b0c08b6305d4a026ac6a8e3e597997a0ea58ef66ec2ce332193da5ace2c910461dae21df0975fd362b14cc59db5ab97daa920bdce84acc365a56966717c41dba67a0ffd806a0e721c7fb5a80af52e26d08470dddbe94a9fafe4980235bae3cf705c06cc807b81c071d30a944b4fb5b6e403f9a9b8f15e1dbbbd9158860788f44cdc6819f0ea58108b60bc043dbf66ea89d0a0cb141bacfdd7c9cab981bd62f1c0aea680900a6321314158e1b280a95687fa2313dc226ea6f27ccb0c45d8d2fa1c1326d4206dbd6495d515681090f387b4805791ecfbb29cfa22fc2c5b9d12e8091b76b3b05576d72d7956a9afed4033a81087e0f6a25f12d46cecae9076d196c93d4700c082ac684cd02efbc6c843cee6baeae82fb4c424546eef77b3562fa13a024f71303596ef86d2239ff4ad33446ad4e3d36a3fbc77ecabf3fe7e91532b80970cb1a0474fa8017bacb98be41bd159bc8e7205b0177ace8bdac44f79161202196b1bb0a0e3bd179e672243b7773b5463205124eca1a0ddd4844cf44818b45b6e271b50c83c53568d0727873773b153e8ca96f5e39b96f77fbf368d6f6bcec367c1c0b06778e9f022818dda27155fdaeea4f0e8ada593c8b1f94146dda6ad06533725708f8192610826c062435be4384bd32e0371101ccf1b51b520fd8359246be572e0570d8a54253beb069a8d8459f6d2d8418114bfffe34497067f513515e76c2990376b4fc5f204cd567f1d6795d4596e94bcbc00b3e73d1a982c95a165d4e579a0b2700dae92111c1a29d25c41f39a97af861787c056eb0b7a7e5f0804b8dfee70c0448d351790f5995fff92e223ffabf99493100c52a4ed1e0be249872ad80580647f082bdd1dcc277b3e881b9d1fa75b113bd74b82a97d128a5fe7094d6c04a0041624644f5702d75166152a0a73de9fdf058bf10727aa17a5cbf2e0372856f08ecebf7f1cdc4d907887e2841a853e2211fec668072584625f33ce30cec4ac1039fbaef9ae66ccadc0ffb494c5790937f6e5d26e403ca6067231d62af7714c6079cb27f4b527b45932336e561ed43db9616ab68f6ccbec0446cbaf8bca4bd7909 false -check_ring_signature 1e0817d42c76cb1ea3778b660b6ad9da317f34e59f3361ad65a75330d49ead35 0795cb472d5c5ab690e4d5f3c46f29c68fb83f8d7c01db27e49ee672cd0ffb7f 1 9a014d6ac535f7bafb27d0d3e58f1486ff3a8daaa415d3509a1ee02c205a85d0 53499d53745b9776ee8e74355adece4aae3e10bf269aa9425511b6914c746c0473efcacf3551e99517fbcfccd89c2fd72def122456c6185a044f0025653c240d false -check_ring_signature 99722d5e5bb10f6c40f41afdcb958af76fdb08fab0fdd1d3f42937eb9061db66 76cc4bc878fb0c172c54d052ee70fc7eeafe94e4f4e676270f9ee9aa9f3e9479 1 b67d72fab4c3087a287bb3b0e643c2f67a7e322269e2251b29b68bf3c198b7ab e63bebc00a715aec12a17a43a781cea5550c4f4c4e1382a9d1b5c20eb3ad08077d671dfbe025565d68fce9dbc6f36273e6101101aa012334aebdd230d6dfb304 false -check_ring_signature 249e52934f966c8acd9998ba6851cb4b76fe6371c916920a89769be0ea1fae0a a3806839e271b19b271f6d6e479c6fcff5781e6bdd216b7b815b2efa1b754447 13 0c107aabc441343a98704488b295ca4605d8c38f9b1707bf48a828ff5410f09c 20f42a502e7302412f7f4dc2e2e4e583e4ccc2856dc96ed2237bb2d1384c96a0 702d478fe9cd4ce27d4413d9b6c66037061adfe14d90664bb3f903fa0150900a 32edb0a3c2c550bbaf8b3e95949d2ed2087498f7756b7aadc9c4b5eea39f7661 1be9c2faba29374a370a7bdada516dfd825e1966eed5c4294671989d551a35d6 22ad6b5306f07ea0a3d5e407ad161068c4040ebc448d0739be4ed42c36cfcbf0 54d7798cf5ecead8b0879d49fea3617b63c3e99b15e004e49b739bf1ce90d38f 9910b83ecb710715c74aeb180e9f080f4900fa97ebaad6676cf6b68d1fc472f9 74168ae4d8635647f857e800759632424ddf5d455b7319330e1d6a5ecc50fd83 20114a7dc38034aaeee17cd13773d94dd3a2029c1c5a56b7d273a7f68462736a 0c4f42f290a9b824c864ab4719b5a0e3a8a48beda8bb00f98c838bf2b29245e7 6709f023875f5d1c3fbe2146e68f2355e8b54018fc1a76c91a3c720cd2436a33 de16d2b08f884a4064f321c4374d122098d1c6a57ea7b128e9017366dc9e031e 319a3e7e7f0a013f0734ed0becdb5b2d03c2eb54378ccacf5eba0541d7f10002bab4e8161afec2220b74afbe772282605dc4dc874dbbc50495e095a83644030c0bc92feabdcd198d61d91bbafe3559dd5d99925f640757530010f122e53f8d05070e748f6e8f63303c3e30f25a19db51b34ec61022b2ffb9fd864d94cf3d1d08ce27b71d402644d3ec15cc552002184916b10c0c1fefef016cc27bd9c6ccbb06d893b656f314fbcddcb218ee740f0bd586087e1432385c65331c4fa89b1ed80aebabb0aad0e0330d0134668523e909bf686a6bf7126e14465e70402ab405850c683092858b245f0b9bd2da4f7048dba4ff4344f73772d75e267e1e633ca0d109f3703a44e982912e7a23c2f5c7d66551e23229eb74d4ae562639b91dbe08dd061cfd16081eff4d65aec69d2774a5ce924db311bfbaca84cf4a46f0cbfcf0b40ee52bf69daa51126a9997dd02ebf3172be41f9cc06becf8af1e2742951ec077008a38bf53902c8967526e77b8af9711dd8f3c3c83e44f68da3a63140d6baf650e194c106cd921fef175bd4455063718b01f9f7fb5cc077c20ca644bc6353f9b05de5217293fa1648b0860df82eb6fad55309c02b25c13eb191b45f718cfc204028015718c7679e812be6ea324d5b7082f8b7ff00913e27df5902dc9de3f18c901441930186adefabd465d26a1b9dfdc07bddaf68655123c1c2204876e5345430af21e423a765edd801df0114d238431665ecd24ca03bd17fde27a64aa9b625e97b57f7476d4c14974d8aa590ce83599d790afcc7629c8b8141272ffa8593235089f9d4ac48f0460de8e3e5d0e7ea58971bcaf5726f7b6cf06783dfe313223400b083dad3338a78b4ec804fcddb73da541dd4921e012e198834eb4df09b608a7020c13085a2e97f4cfcac831d57e45b7c923eb6f97f4614b2c69ec5b0f78521403c602eb153a2a22c74c5f487279867c4d3fac9d09fb6624fc23d075426ba40e019991664946213a894e6ac16715255825be42010fb64c5e1d97de74fc3a55310341280ffa920ec2c01c9da2d149bbf2e39d602dca36a83b68d9c2adb0944e13037e0a896b8ebe6ea892f6d4820a868473defcb9821165b2e746500810678acd0b643c36f9130b4c83edb5669ffd24647647e508b1174a8bbd863e7a5b918e610b false -check_ring_signature d1651c4d1286ae9a291d8b002fe540f1302b93099313de3dc725ea219fe5c49b dda2f4450bdcc0f3ba4289984a2a94cd274c55fa22cee04bfc937ef81cfd53ec 2 a986791c58a93f6aef6080d5829023fbc0fd80b43baa56e1909990d6aef67162 70ea3dcb5b1e5d31761b6ce9031a65aba48f42d5be0fd9ef00c0c7c5f198bace 41abb5871dcde2bd629962120010ee1181e32dc9374e13fabfff809d05144db5b436cd1be3eae39544eabfa81f59e0af7def6e9c69119403caad3dd8b61dd50719ffdae1d719d0704acdf5991276c1f69eaee94d3f5a1b4bcda45dc5418116c7b18f18514db994051cbd9a66a3f86b98a42da1d69e0f4c03e1e1b3d051e8340f false -check_ring_signature 09220cabd5c6caa15cdfaa9b980c575655ab5cf5722debdab348baaa2c3b21fe aa1dadd86ce230fe79574f13f348a7810aa20523f92d9051028c440a2fe4bd04 1 531dbc2eea802cc19794bf63df8ce02cbbc7292f69023f5dc7a531c65ea18bd1 ca39c30116486f7fd10797f6614f092a3353c4aeb1b43a6133d822bba993cc07b5324f7f96363d6fe1ee7f7092cb38d210950770848f28874852d25614183302 false -check_ring_signature 3670752e7ec554b1db2ef2ec9617b6b6bdef1e01a9d2f4143cefc6ea7d8623d1 de2902695bd17eac1dd42dffb3636d5f67b8473ae0a39c2b3512486b6fd37043 9 9143cc828e9767d81e158fb378a8bdf1822e82225d4defe55098297a2504d80e 0ec7e153f881de39f4bd08a0e0b1098a2099e0c6cdecb400646ea13b921a7c19 d52be1d3a3709b29c93eaf9d0ee020c6a0017af478ce9e36a646de297a7962db 8e1f827abbc614bc9f0ca07c9e677b1d5ca327f2450304294087d453d97ee6e0 12f5b368279d93726c9b5b555b6fe6bdd96de53717d437cd1a64348c4f95e8f0 66954fd8db581f04acfd5addb3574b6c3f1283a69a9e3b1ccb8b3815b4730d36 e6da9c342501d5f6f6c35fa07ed85d4cb58a854d07b339fa4055a84428511330 567ce1644368f7b47293ba5f9d5e1eb07c44ebf32bca21f3b949ff3a8a7d631c c43af1a25ed92750bad5571637fdbba4648a69c14873112e56ff570f21530a10 472774c3f0da4fb0a0a95eaaeecb0ae1733ff42c0fb0513a04ac10ab8e22950f99dfce3f6388f08a33e3a0f423911a1580aa43af37558196ec43021185a3290a5de131c9d7b7732ae58f18dac320764ac9f6579791141a45d55e3d96eab41303a4822d828b18cc6cd5192db52d489e4264c6db5fb48eac8394604232f81216053c3328f1901665483f9103f97f30e168ec0b996b5ad3fae7a95b03633438780af93d0a1cdaece2661daf8026eff97c664efccc55fa42d1e7c0d681079cd9eb039ac30f0fe5cff2b3806046b9521c8c6d2acef28525579b23625e313c3dd6df0801e1d1b62339b6da48d57f70ef8aca0e42aa0e41cc64717493c14fdddc1cbd0ae70e29e3414d22a6a3fdcd9b0ea9c3589443384e18cc9ba85cc337817e09d802226564cdf44056048f6f5bb77ba4c1fb7652d04165822d8fcdac2da9e4f20306aa5ca62d4c72eb89ca8191e04ac910c27ea45515575b5422716e3952b6eb4f03749c4087d632457749f095f3989fe3610c4a2d7c6a73b2e489f0a105cf01f5068b5896e8d17b70a5f6fa7cd472a297b27c15a7f60ad21aeed96be11c44fb540f34b1fd4c03d6f24e38bb2fbc0d35fe0b9799b891a21f342334eecad2636b110c1d39793d6932b93d8c763f97711e1a970afa16e068eb1d920ff8a92f09d887096be095cb953d323568f6ba924555a61daca5ee0f3d8895ac201fed52baad21055b3036d7f0b2bdc0b45459110c54ca17ba1d8baa5c1fb39f94bfa141c127cd07e793b19f73359052ae6bec5b29775caf6a3a75ffd8ab4f0ed2b02e26e742480a true -check_ring_signature 79b2df23eba0adfa4b15356a7c6faa568e3b53097225f2f576a6cbf157f4c847 6a7424b7d1885a6de7b0fdf105e9b67138327b97ef26c8c68ca137dce2834513 1 405e6526758325ed00e38b0811d1ee60d5d1b947efb41b56abcf2f20cba56f09 a786726df15550cbbe4f6a434c035badf3db5b9198ed15005084dd5d44443e61c95a2269d2b7bdc843856c91f334af5444d83432c723382d653415ef731eca47 false -check_ring_signature aecce1bb75d81ce568445a4acbb97187aaad19502061e7e8e310ca0efc7b9d95 aa71d20cebb776afcc0456dc3082dedc47a583781a825cfc966b3dbc43beecb2 1 3603681780013e1ed672a942ef0a0b509e3a6786ffbadb4b3bd096bc329d3a67 8dc11ee37981561bb1dd080e206bd6609cf1106bd2179f4a73fdebcaffc24f0aea18a82002d6a83eb4e7794f12532a95cb9990975ed3d4b9bb34889cd7724e0b false -check_ring_signature 0939e5ef6cb19ca9da1db49046c31d813988b6dbc2d7c9cc44f7d0f93ba14910 ce289a1f979a1fe8452a95b02de54c4eec44409328a0b487821ce79616fb7315 22 9c026c412468f4777fbaab8d44c414404f170cf2bfd92136a5fac3e702034606 6e8803177bcec1db0270219537cd08b0c0ff852ac61ffe1af8db328ce7f53dee cef4edc883677925d9715631bd2d7547b863d040d56b2fe48a2918d360421678 acc1ff52a832844dff536739bc38e47e1ad3b27f5c420a8712e0e5a13bed5913 3d733100551a8cd044d0f0ea7f32f4a7265db4b7591ac6034f3ec475b6761f62 cec1ca8541c19a3edd0895a6b0bdf2a9c2a98add2f587b0803e6a12896816094 e7328b4eb6926d15e882b8244e95623950777d3cb3ee84c470843f8f896e5c3b 42b5205052ed4c44afbafe2c6b45cf6bf2536f5c9fa5a6bf0a80f07dbb98c04d f01b0e3251518bdada0b7038b08fcce8376bb914b6d914e1c5ebec46ecac47d9 38eb94034d679d5d5b74a8fd3530e58ebced10ff0d1b214745ce211409f9b7ed 8205eeadc3cfdd242efab5875f4c7f944d76bc924727ff51162c46d91c82ed39 0dde6889ae492ebde4d0981ba6f1f9923d11966d5e70405e03450cf1d4510129 b600cf0cfdef8fc71ed5eab65a10f6d910c9b94b45d3a3f021373cf988f41b60 6539d48447e63ac6dfbff24317444d7cdcac2de05bc5e7fc2a849dd34a4ac968 18f22ebc5cacb8e4dc4ce4c7fa788be0d8756d953b882511388220db05006710 9cffb89332b816a224bc8c9f0e06fd3745fb05c5e7ec566d9d72b2205758c343 4388ad22fd8b1ccd970a8da5771531badc8f5c528b051b20c6034814af45540d 4443877b150926abb0d1c08f1e23f4319089dcf48c0ad7db989a71f83d7c4622 ff4d5511da776173ac3c72bb529c41b9a295bc102ac2ab324197acc669ee5ad7 2ad9dc8aa69ec012070e3770e612fb76ab5676c034827a39ccbda3f07a67cc8c 2758484a9d787220ed7d419c309a80271cf72a9b1ce32bedb2a535640e4fa34d e72351985b4e4f1e47202c6833dd1da9035db3c4f7aa52bf60025498e08ae766 ca8f95e5fe3f580244e0a13964adeb920e9808df05718678b38cbda4f1bff60369fab05927b0dc7f791aeb486ada0e2c744699bf417a40226dcb98c5d585ae0d75b290300c28bb3094d0bb27bfaddae2a437006738c5889ca2a95058c5f0bf0de238ea77d71966eef492a3137ee8579222e5e5c5158f1177d2b7a515ef9fb90d753a4665167fd9b29303169a5561e456a3638675f6286233d18a4d72899d1107abce35c74aa584d0b79209754be3c557acdd84858dce3852c9e1ed6b03eee70a15cf9f7b8f0c92cb585a247025669fe2984fe7c7f505061672ba5c6ba2e4a20485e1b6b0727668e707ffc24766f8c030452de546f9a076cb385490a75047600528f1e1c2ebc89af0d35c972120c4ccff556cbe495e053433f64d2d9e5280240f5625ab062b1d212bc80a15309fcf3b8e356bc57a89417b0dde8a407ca9f59a0c958e1b44ed76ff261b38b7877c0a784694e34074f7f8e0b55c7bc07168f09b0204457ec66405d4c905acd389d503a7f014ff58ea2c934013223632426e9960026a1384c00d8ae4f50afa58a4c523e13249bda94b2555e9b970114a8bc6646d0d43be9b767a767ec8b195aad1672df398847beccb862d7c35c838e67a5bcbc60219ba7c322a1cf16dd3b34f9effe54fb2e10628192848ff99e08f62efc408f10091037c0d1abd5f1962e0528661d4701d4dfa74ae0d4c6ea50d4c46ee289e7b011fb2cff5dbaba65c1756bcf3c25d15be68e0bf0bfc719c2c862ebed8ce39e206de8be856c9d9757365ba9b2fc83ffb95383785e58f84dbdfe453ea91c2807a04b4ce29d42c0a42f6d068614c20ad67eb77fec7b03e53b48b718629bb75630b0e43a246cc33ec083164f8789f97487f85bc967c0a3e12a96d0aa49bfdd6c28405433193e82125d7d1df889866a94caaa0d2a2e98b1edd7183c020ca869b8bf90dbce94ed3c9b8f825d09d10799199d47f378a784ab10924227b0323bdf756b10ec96c51979d727d02b0143e2bb894e36c46dc00e3f9bba86d22eeecea5bca1408f631f8e9ac260c17c986c515f42d7645959680c32ee999b720d93f684b795a0affc954b347c8aee21ff310dcdaf2931d4769d2c6043726cdc4656812c82c9d0579e8737f7aec62c819e5bcc2e420e15e0e55fb46b2233cac2c6b7f3b71fd6d00ff7c68d7c3d03108c61bd764c71b5d2cc8963a9f293f778eecd8fc1a2b63f8076481ef03cd0a0ab415b8b474c3e0786d0991654ceca11a8eb472c1e6b04b6108baacc7ffa6f4b7000ad333544468982577813b0d6f06e6dcb207d00a8e3c00006f615286f6fb06d35002016434636f3536b228222db6536e3d5dfb0a3db8190f93a4cc2f33f275a9e9e0a3b91c523e82915d5dd7f732cc4b549b76f95d37840c6371dd83742e1e6130e24a3abb59320197f23166cf3881a79d7531f67277910e9a17e2c5ab9f203bb4a184b9f46cd9c04db143b3dc050fbe0c4b531d4d0aeb0184ad2caafdeaa03f53ab5a248eb87f17905f122e62f09e1d8da7beb1ad996f046e5aaca344979ee3e257e8440bdb3f041ee35337036122d701ec15ed437cee05deb06eab65e3f15dd4591ff58f9a3dd142a196bc98f134c1e06fef1e7422360652d6cc7da570e1fcf94363e68c9b863eede2f1f9325fd1e2b237de5e845af606368bd4891f6bf4bb1b395466fd140bf793448a89a712601039a5aa3ac5cb2e00f94747d8a2dab531a39cfcd3b553ffe536ff3ecc6baf6cd5b31e0ca006b2db0601d99c706e1ba4909607d043ddb2c2cf565bcef6b95be048e34a6bbabefb920992661c7c9622fa0210154d411f43e23f54a7668662d935b7d2547c2b4611f20400010fb7111583143c0a1dba69a455e8527706a69e72904545bdbf5a98cc71c1edaaa68d170a830cb5db96d58e67316aff5c06071325a8268529af57d58ec30ff03458dab4e4d5625b0362a140e8b5a5ffbafc4bab677fa53757c26a8d6f4a0a false -check_ring_signature f29fa3fe95ac76fc260dcb9dc42b95f24f85e2b8dda584add132539c45ff1be2 fa1cb42626d8cdbfbb23e79e97d62baaec39190823742998fca20038e4b927b5 2 dfe02ce213a7baa79e2f45f96acc15234df8364c389e6406b42fdf72687333cd 9d1b3bd0973b08237849f07da4d74836516e106abd0dbc7115664b9c699aec0d be2c7c3bdd5cb24af801f9c63dcc49ef8449d40c540531eb254642daedf8a6007948f63b930f155add09bf256d31d4cbedd7ad1587ab2e836ddb2d50afd54a02b9894e81d485df9b2ed342b552f939d008cb514d7f686c926706740b1b5e590858f4ddd7948b3c85626fc8b09b9879ee098ff3e33acb0caa75291339fcfe9c0b false -check_ring_signature 3c2ae7faf5b854b22df3c30ec71cf2de2fb04777ac8710fa45efebf69a0af269 8787761327bc2a7f8e582557ce1b88690dc4d87a6625e39fdf874ce17fe287ae 1 221b5059e1395e2ea57bcf8b0e232874b086a4df748e72d6baaf6b720ce9ee52 b367bc659e5cde3ecd63be88e0ac9ff894df001f9e6de8a6172a2b8ed0819106abfc5d049e9b3d57d00b0140c41baa2a9dbd9306ce1a13c86560a2d37e2a5a0f false -check_ring_signature 8cf0ec916e387c32a716c98b5e1fb42af31f0beeaf9465cfc026ad7962c269b7 847dbb3f10d592479c2c8dc560650d0a8f2e818ea2a5f1435c7f9414a0d965a0 6 f99726559f0f3d5165dcddafc55da7d9a16ab26d944f453eae3b14d8f1a89078 1edf07871b2c5c906de4b71180d197328245c1a730fedeb93836654f53516695 73a2f06dfe636061ee8f5bf8ba41909c4d1fcd2d2d0e6cb11bcc0042161dba8e 675cd59701c131f0c3709ef01fe7188b97dd49e7b3497836bf14faf73c0e9da6 51e7499c6efb88523bf285858cd3000e75ee8f09030fc9894220247fbdc51793 ab6ef9dee93e2a1047c754ef042cd59212550f0ce1b88841e013e42937d133fe 9f178f94ff9d267743c87134d92946b0ee1dbad1998f71fb0e158e016e324c08f8e3cafd36c144cf41f3639a949494324925e43dad6dfb7c9adc0e58a1306f0f1e69bf263f2097b4871c63e9cb8b00916fb4a575d0281afb318c8e04c3845f075250afb84370fa63278b59468cdfbe99a777d9cadf79729de9101aaebb95620977a30f727c0ee808ea405248c5568a71e159733ed23ed5140414576137d12704c694561af1b4dd3bb4b8e0e7d10ff074749770bcefac9dc262c2cce47a171c01fc027ab7b9f347ce5646b32d70275549c82a8b91f4306d234a7ca066a6cd520efbb6df63341f211a2db8a543c6bc3f9a42cabe8ca021dbff8bbd3c52a0b2600549012188247bbaacc3178c20d2371685016e1985f2f83305dde4270cd96dfd00b6471ae82c2836086385eace8444c36c4ada4f129e7f726a928f49dda3785b35b6bd01e3f3918774bff0fc04188c31483e02563b050590ec15ab8194b51b94098b17f2436c55b968068df24c60fbbd411cf156646738728f55e7e4d4ecc0fb02 false -check_ring_signature a14c8c140b27c7c7c3cacf90b8b5366ad86cde0cdbce36bab7914d130c25152f ec6538b8c838aee6a492c1b8324c4a79e73fb593fdf8dc26efa3c77133491f3b 4 c8f8215f9fe281f822a260d9a7cd9afa6453348a3d353dea62124261d905fa91 928898b6851bd8e630758cf0d54bac7e42b547d2add3400200b9db5a0e3324ab 63cc0d08490a6f0c10fe2daf3593541b5ce2d95195a8ae56ebe8b8db5f44d943 c49490d23b741bf9c9c016b43b7ba0a8f0038e447a7b90ed8dcce435f851b682 a9128e31df69514a7339620bfc025fc38f11c420af7ac5862b8f8dc2ac06780e3cefc2bde165068f513f273fce0a14c1ed56aaef1dd6188dc199eab5a9cb93449e1791fd46df3e1c6fc59cfc45eed32f7d7dfef0a7e6a646db1cec6ce9320e07544010b0163a3b9022bd56bc2ae32461a0366f6effddf69edb29622b0dfee206790e5b6d2b278359bfa5700088328cf7c865c0587cdbfb324bd35c75f4786a0a7d8f08c58759a97c10eb0ca74762ec5af85571260ded1a493bdc74d5edb3260ebd95c4cd3b0211b0c81dc1a8301da5b9f644e044e1fa0a613bd93d5d91669f8ff2448d11df7ff7f0dd078ba9642769be191b56271b8d6240b56142a70c119703 false -check_ring_signature 54b5c45d102d7473e187e8d9747f7c2098064c8baea427828959de0c728f7b25 63ab43aaab68d15d76b97153f4223f1a752d19822eac6c6137a5172985be1a67 20 aa3d592002debe9a2de484951da4eec887bcc68f4a1758558787b17e3d4269d6 052f2ec5fde688bbc5dfb74fc77c5427c02d4676153c844472a05d5c62907fb0 128fea9cc11785bb8df19f73e16cddf60ebc2acc1be0c896443d07d066020c4f a070e6e6873d0484e2e6792459c932575383410f8f41707445374405605576c5 35ce7d101b2325141cb60297cbe7eb3b210f8fec23c2f86270d425eebdc7723b aa2d7e9898e3098a0af43d958ed901cd662a85878b41bb302e24bed82424304e 8f0dd5a553cc2c2d51ceda1d42215aa87ce1b7ad36c9ea03aa521615ef9c15e3 e20250c52300ee3b527279940e6a022ca1bf5050fa1afe5007eb16480c73dce1 5c340059d0396108fc850658a6dd8e708a1d87c97e4106c633089dc65e3fc2d3 f31b8227cdf2a29e2c677342731de9dd038c7a5c1c5112726f85d760dd770dcb 021a6169270e0f51538f4c5f678c5a636df7b15588f10f1004c5c24a8da5ea32 3e4359a4cccd5165381d2c579cd3be2cd4a8d7ff57c8bb813d86de6cf5346cc6 3d3fea598225c100a120c7f813c67ef4d507e01497b532f1b9a6c39c1f989cba a743a9f03795d2b35af52fe910f6ce34583cdafea479f8b86f29a4e99087499d 95f11efc39c0cd7f0a3c60d54913938e3e481c9c7248558f1258fdd9681436c6 fa04a89553e4917c4fa52bbd6fe67a3b30e6f1a36cefe2ec961f5cd778d599e1 fa869abaa58f35afd8af74d39e0aefde20dfb1500039c95fb5440485643d56e3 395fb18b2eacea79e799a48be509d27197c9d18b820a6234c01ecaa963ba0734 07e3621e8646d9307f25ea85db9a64f690e6698d876c27b9b0fb63576b5ed3a2 59b937359ba1176e7c655075c676a2030eff57bf41a637d56f034afa71f9598a d704990de95c7343ce99b826a7583c86ef852cfe1e77a26075cdd0534279cf0247cd4b4fe3988d3e75dd0b3f93be925f686c1dbf4b8def67e6da5a2a464b3902ef5cf21c626d127a3c25de89b056df4a69301fc44444799a1eae3d9349601c05f655f763e095ce791b1118bcfad5176c499168b4ebb07e7e5aae7ea14a59ab06b89d652dd7997268f7f9ec4afc32c9a56f593d9d00adf7f379208a28af541602ac62ebab5b2be4958b948c8a52cec30bce322d306cc6b203be1da72906bcc7043c5d820e328a88fb1b841c45db948597d7f241b847c859e216c2235334a808050daa9a3ba822a26b8061555ad6afc2e27dafa0ee56a554db49c25e445f75890b142d7565355957da3ce96b5e9fd21c41e688e2a584713e00c899072e2495d1096bbd3537662f944f50ffd17cc249c8750a4ec6071e6a20b54217961fbeea8b070f93bfba72e63333a625e45ab1ccb322ac074e8a917f2cd494b3abbedc057d086136bd45dbf3679352fab61572757743327270cdc484836c7790abcaca409205e0c522ae6f1b12e30c4eaef495e59cea99d0fd93fddcd724b6587e4e7d3e1e0e4f282fd58e625c450530d3bcf9dc437b91966f2c5a0c799b02adaae3c1c1610f3badf3f5954b077c02188a3e9ebcd7b4c1e3d1ca8a7919516be1530a056f850984cf7baf75286b0d775d588c5b8b4881580c18a2f75c596051c27bf95e4a730226d09f292fb5f074e509bbff92c16d557c184e2f247343e733c8b162ea6c8e03105f0e2de95d9f376e3d0c6b276b68dc0a978e5c5c1a3b59a12e680a9544fc0f86912d752618f1984974da4a9144aaccc113148509773537fe57e6db2baa7408cf705eacd3faf0e331b4a65bf96d14364614c0e74de092c5fb258bee2919ef043d13c7ef37137cb9241c04c9ffedc1e884e0dcc43081ba04bba5c274f40fef0ff2bb8e72343f431a723dd9cb2fc2b554ab0ad8a302a6e072096bbc788b95b5071d91d949f09d0fafbca6b1769710c508b2e82aadb72fb99a145cda92b84c2a0f06e5c7bdafa91b8c913107d8347a89ccf591b16825baf9da2cbd8513ad6e040438f3270c7b45b542897d688f75f277f560376fb2170db1038efea956d495410a3137a8628cb7639b018ffcf98c8bee683caae2970b7c9e96e22cf3edb007650fe16ae4d6db37abcdffe7449c15ac8d909c858842c8da2911fb287e5f78a70f0d426516f6d2c52f164f8f88b59194bdf8a00b4e2d8457044c057df10f133b7807be54e02c722f404dccb403f27b01fa216a0030bf0d430d9b68b6b89d3bb6190c1105ae8a37fa80a3d0989240d454088ccf3cd98807749ddab9e26532b473ba0d4d2cd17c3829f79a3d3a9f95b8aa5d17ce7670eced1083aa06f17d949093f7051453699f957508bd6fe0bd28212b8082eba2269cd9e0e7f90644a1cfb3d27c0fc429bc99e026a54b794d7639bc03c6597af62756bcd2a61bd8fa4ba609858e0be4042d3edcb6d6abd675c0f5ff96413461d675dd1ba230fdbdc18ce9f36034070d87bed5f499354481687d1bb4271fac34fe4182775c839c0655c518135e7e089f9f5250bc76db50ea57800682e32808ab735c83c07c39544e63f09d2b3628077a8f99de890a7b797d269edac574bc0e842051c50123cc9899f60bd0c7d1cd05a7077e296d6c943f0075c13231db9328f79ad5264a65d156b962257692018f06c9ffa149a5a5a36181c5987bd44814c0743a7da41ae481cb854ad75dcc589b028ea56199daea7813387a29a18106a68a82dbf0e2921a05d1f711c6b8630d6306 true -check_ring_signature a563fadb663bdb1fc7b5a95891d6df21d0857224adbbaa182248c28cf6ebddb7 3ae3112acafb165e9c3cf4dad6dfad842f789b2e607f56ea19fe8697f0ed2e2f 6 e8517ab255aadda9e2dba728481efc6da4de3cfe0a3c3415d0324e259589749d 90f0f8260dedbeb9acd0dc37863e9337af29bac6bf3918f71e91eef93590e2fc 838d9929cd5af56d4933f955f01122763adcec5fb1dc068eb0210fda57f116c7 4b12cb9f90e4fb2171a56cafa8c711f0657e1199701f80d92a412a8f3cd40207 910c8b45e5275557dcee85d9827f68afe73bfcaf262c55b1c5154d08adf6f79d 7a6f4ccd28fe78cc7fa682093d7de3a0af9cb4273204ee0c1adc16fcac19893b 07913b10b2f6886bfdf4c74efc0b0081c0282736f45c191ff59f3a0e8b8f67f1c2d6bc798d8506ee0a62d6aecc1b68f43e0d60585405dd912072ebc31d85ec0d50589976f95e6bd5cf797c676d4c8af5add088f4a607d91d43d3301fa016850130d5ba6f639074c0f67530e72b0c9c77feefa05baae15ccce131916cd726690f2bc3ecef96a564f52b6b1703df6cdf6075a296d98b962d8c80b3facd7682370f8bb2919f0de70681320b5c819e8b832cca33870d69cf6b7fd2213e157c68d50256641b554d2c475bd5bdd42ce2a0b3955ddd01646ed21f96a31b2462c158890fb7efe88bfd176616fb82a9171965a143e282d320dec6fc0ae4dbf69255857f006faf11a30d8c18d1a0e82168cdbc60206411c90bc129465734705a528f476409e01a52d839e95cf862497b3940c8250d883ae3feace0b98309b1e88b2c79a203dcf447b809dad81d40fd6110412876f5d67742ad44718be5b265c41c2575ba00ac470a2dc0e9188deb6a2c3015a710df318a2cfe996c74ed705564e6bb017502 false -check_ring_signature 492b1a8986dc1a8f04ec434f11e331f3850e7b7b7e7ba7abf31e91095a28820b 472fcb37bc5a082930d8393e6881aa9f5b1cbed4bf95d4b417ff765a07835467 21 0179312deb47757797109ec5c8ff79756e9532a8eb581b36b90cc87401bf8a46 6120a0e06d597403c41e6955e28a2c9bf9450e829c8ab90b4526b1d668adc6d0 6316be06dc04030a9bbfff6ea319282e3b05eab51c75abdc2ae490ab4df4ed2b 2860e20d963fc4439fd1f466704e484d12e5898746ec7d15524b86e05f3c6f5c 96c641e00901085d4436c2ceee06633f97511b2679b49a707e472f74258b339c 23c8bf451d55e0a60f1197f40c2c4fae7d3525d127984a9c037da52393163638 0e328652f226f4e6529e14a6d5da832b8c1df1e699fef3ba01fd61de556866f3 4c8cb77cd74b7d32a8edbd2b05f6b26ae03c203f6b03c2745b711208c6a6b6aa 8b61671282f987461f384d4965c561e62089310e1cda437706acb126c8bbb4b7 0045082967c359f0620bd458f9c0932528e9e1a22477cf05863dbbb272340691 ff713ffe2a1c0c311dceb70daa6a28e95ed7594c676a47c82195eb77d0c11723 e161fc9f0b2ce31a82a183d6751b54b65ed49273b52f4b68079eafbc3c3fa36a 6447fbec0f4ecd8525580e54653c75744c2da36d8829a091d65c20cff160c5c7 b885c6718791c11658133d2672ad901745f20ebe7f384f36bc9f9ecb68ad53a2 171332261895aa6bf4901e9e4930dcd1590f494229691f93ed3fe9cee855ba6a 7669c2c7ff999dfd9a76e8c6ee20404ac6e138a8ba135cce5da3cf1e4f9ec0f3 19e7e40fbabcde1b677ed97ed201c953e2e408fbe197140b25281f32640d2cc1 c3b82ce7def5a47d256fb5350d9f3c8de49ab9ea81a4817558fe0dcc5ade2988 bb2d9c43c0ff8a6e313f29be75e73dcf83d6bfe9d76f2583089e33499f186fa2 1765bc4494dbdac4ab84587effcc1f98f62cfa05319a4fed74012a9cca8c5198 87a9eb5188645badb8490c8a34fa1106cfdfaf84cceef457011b10687d3342e8 4e2868ea7bef1d53b40460d58361cb163d75c370f6252f4dbadbd34c1a60b0045b382ba4599c0f9a0beae12dd587bf72301fa2b8db750fad5269bf645417650e685762ddc32d2f37b43b0a5b1435fbfe4dafa9079c921b94e9694efe11eeac01a7427ef04bea8aef336b477eb8127c2beb6e3351b6dceb0996c20175f2d0e102ebc34ec1fa88a709fdb208c25cd2f6698bbcc7fd3ed78607c55fbdc1f41eb60c7f4625b0e8511c19cbec99d07ed07d1576e7a7a62564485d41adf8453e39aa06b822278a47bda5c5b7dbf5701ee43c224b5a6a9fa7d3c5c8406380cc8256bb0b6371815d68dfe5afb95c51a3555e315de4b1e62b209bbeb759ef3539b637ba058346bce38865355047be6e58453fef21444dceb58bdb58a4a9e080a8f790bb01da51061c07a9f6813865a48736047a1d2f3e5e96bad512c3ccd37c41a812070c3ea13ab630458d3222efc05e919fbeb9080c6ea7230e745167a88a8ede6ee30045f640176b1274ecb655ad990256e123b36e760e1ed2cbe2cc1799a313ec2007b44c4a1fa8069afd8d3ac62578bad706b394b25933a83023826f69110d8ec10ca23b422f2fe4810fa90cdb09499eb9b77a684d6264827d3b07956167afca510b6322debd4270f82e62c403a5a8052501ab07616e8c04dddb71def4c52a0a8f0bc88ea08973de50edb7f306ed10cb211cc03e53745f328618b120f2f2d45fed0e22f6fe03d0f458e97b63b6bb1af95e2eb22cc41ea2456638d8b138c8343ed807dbac61c15ab9881effc3d96a83d89ff0fad5bb210cf1e378cb3840c4fc889c02710d87407a764f6cc5176cb75c0decf5c9d8bc740900362c82b3744ef4fb290cec1bdbc60cd38fa2c58f04b05cc06c264296d4f7b278d2923c08cf4be93c7b0f601377cb319aa30bf9026430fd9c0bd29d541079774ca3a54bcf858d60222a0288c157c3470fef26ad10e5579eeb1078d85ea3f02ebe3f10887f9ca621f9cb06b82ced0551e88d83eca263277d15b5d422640cc5848edd3be7ac2def3c79400827db41bd9b2cb6b6d5db2edd7ce3620c26552b1b1e82081ff0c066d813462709f064c0aa0ebb19a4f9c6dbdcdebf9cb240e946915590e7d6492d054cb17edf0bbbac6dfc4ba24c7db9cfb51952e5a7a640cc691edac3790948d7b65069b95a0306800a08c1e12637700ad3e840d04a4a3ba020466e7c34e30d00a897389b51021b8c689031041a242a6f09725175ae23bf92a9fbe0562466088b7af7630e0e0dfb1e88c8b996c3566444aa9dff85e9da6ff45e7409cd6b348b739dd143e0040fc1b4bbbebf3538098cdd6caa4d24d99ca48e439c087800db44ad56a3fe65c201ebfab1cf972132d78e1c0060bdccf29c19efb856c9e0472d4996098d0dbde30eb1ef1b1689189f2bcaa39bd7d4d2c02fd400482809b670c68fedec744f0dfe0abd2d4a4f295adab45c163f3e2c17d6943b3604bbdf4fbf4f57228c9d0d684803193c85642f0b7481049e72db8125610e6cf997826baccbe4f50b75fa25d4890abf5962732a066d2414943ceb239db880de7cf5f5bec31cbe9ef04fcbdba0b907e6cb3834d91e8179acf21a6e16f14fcdf539e26266cb7d5fb0476c4fe9ecb20ca972f46b14d19a33c5a30eeec39164f18dd1c4d07e6bc8b9b02751a83632c201a547b64c7e4441b9e92c97765447c0f992c3d0248fff479be4fe676ba2714b0fb6ebb46c8e0b6a9a4602b441a176f389119982d796669d3b5350470bab65f0084bd54c899165779326e86df6a9354da66cb1c616dbba4375ff59138d10491a076ad4fe89160837682fbaee89ee50a6b5e6b6bea33183305794376db1ed56980f5457c3729cb51c6e7dc9b98c78f6809e28c469101f5988c13c38bc1fb51bab0d true -check_ring_signature bfffbc8a8f0210caf8cbf1b206aae28b197fa63e2bb803ec1aca6df446c9c72d f671e9a6b13370f0f5086b2af342842d8768cef2b68b7618c909b7c52893bde2 138 6836071c0c57f9abca490ff690a1c5353031b5581a1ea312ccfa61ecebbb07fc 4393d7d0625b227ea588979e1d3b75353e20e4dd08fa610e37c3509f1329761a edf37a42c26adf7186c67b2f6d6174a5a8b30a29ed17af57a9859f81ee3ac43c ddb63c76035844e8f5338c6d02d4618b5d94c30ee6238ca06485bbc7695d0622 80dc4e7e55bf1783098ae02a2dfc2e0309345fc79ad1cce1de87c22a5db79c8a 3b4bb9659786fc26795a0e4277926139bdb5a4e04f199947e3f59a5967648e08 2d8b7ed15ff8fc042f9ce6e58810866ffe2c38fe4280108c6d2c809ec3afd687 9f186cc6557ce6ae607d4f6f2d86a1c0c4366243234038f4207f09fe44168957 bd9cb62795bea49231df8a15a5d0e9e2fa15fe98c4197800493b09febb00c383 28836e2a2ceabdb9865e21377859af36a0e3352f0479ece41ed7d27aabf9ab69 3af6b297abf3bfcad45bda7da3b596a980417eaedd9a8c789b212b9f5546ce5f 81750241afd54a00605776b347622bfb1e2bd92391ff70f5b77cd2fdb2b62033 dec00c081b1f5efe3f61faa38fa548468d2a05caac0de5b571bb0ffff5f34d3c 226239843e11af4355a10ad37dbae49187a474f77ae038b1a8fc3631c17c3d7c 187f1a2b4704d8594bf5bc5b6b9913658840de04fd93ee7fd7e6d24b82f0000b 08fcc7a773c1faee957228b57e7ac95b9114ecf0d092bd5b0928274a5b321521 e8364e7855cd101063bd89cc40fbaf84ddf58745561aff0bc1a6060f8a6b7777 f7f1f39efce6dc2dfef4ed7a937628973584649f4aa9cd912825a458d7c25f42 2f167630e29e03ed810781da67afc1a3e9b814d13686875958a681a8af09ab8e bec0f8429c85fde34b4dca46223a17f88e7dfb025b44b17a3767952ac789cf62 263c9ea44bea8ee4fd109c9065e752933a9b6454a7504d2d2cee2acaa7a7dcc6 b2628d8d0be67cf31c244855fd002bda9f51dd88c57901e9fd643cf6b10d30c9 59e36485655973551754fc8e40e587b3bec732e438bdcae2931993447289cf3b 06e061fb4a3224cdf0c589259cbd7bddd0a74f0e592813f5b883a8d6b2e79f75 7e0ce27e87e7b47dceaece751c7255d3abf2d363c426ad4a1daa8314ac93a7d3 84a504c740306e648104d06f6e320f57cfbac4e8faf3d3bd48d7aba41a700c4d 1314d3d97e264dc24d6d0b8cf683349c02a7ed6a75206c13386c9462911d773b daf059e58325b36e32db97d98b787ab5d16275a64ede3f617ed255b9307e2fa5 bcb16be8aefd59940c3f179f1b0bde28584e5b3c11cb3d849845e7c995e626f9 ef5e2b0d7539b717fb5878eff2b6bfc4a004c73b243cf7fe942174fb6ef7aebb 11086a8d6cd586a8567c749f66103e0f066c57649e36ba5fc60e4818bec2f3b8 d161cf20ab37b16fa52050567f4f6bc47217b0539acf8b2bcb6ecfbf371e9d92 9a48211f3ed1a27d6a6c98e77f9b2653c8a33db08442c2babd5c4e2aee0f5c65 558cd0a9e417df88c7d6772b850b4167f1c8b93275c271e4eba6e28ef249703d ec7a475fe531cfde0985839815ad34c8b81bf63586f23e71db0e8a7fd2b4896e 7d3a32ce20b31fc254346b0866ca5390cba3960b4c90e2782f3b7df16790a1c7 5bc756299ae1c5f0a76fe4cf5f9f305484f8d8d2c9f543a43f7e371b01765d14 1a54f22aca20ec9ae02b5db7e36f913f9070a86879433cdee7e1d4fcdfbeb43b 6fef5fea36c21a3e4837f098d4466e22a690804163bcf080040358f110b6a7db 0dfd6af495c13629ba583a805af3dab867ff3709f88612e33ca114fff4017814 459634be7fafa0325e429e9b090fbd9f6a98f40c5e5bb6ce95fd4cc9c2ab458b 8ba0e5aa7b70a4ec6b7b2541d8b7e16565eb558a96873e08dfa99b98de1af371 a2373872285e833d1051cb46ae10189d6903362dc83027e0f9cdf2c33968f692 bb60530451dc2a7648851be75bd958e063b4f1544d64b18ec8f5af0ea3dd61c4 9dd859f46d36134b478114f0eabb33dd1d87e5bed77082fd446bc10ec1d2e38a a2901fdaceb4ea755437d02fa36af83136ffcd30667b4395400ec683980f0a23 69516e76022087e44c3f182f855630e19b471d1ecf9ad89eaa1ef6588cfd2a8a 16e4582d6fe02fa13391786145ee8cbd96bcb05cb90462bbe88b6ed3c24d55b3 52b28c842f437be57959c73e71c00d9e99ee2661fe9bbd543ef70313bbd590a6 d601f35f06274b892fca5dbf095052c52b616eeb31f8dc3b5a05890d8fab6490 de7f348e04b97c0f12b424795346d2354111cd6bd0388afab210bc0ac211282a 29ac0f4e2e639f486cb82069d068651bcd155e14128e0d05ee9c15b9ef38d301 37b56865efc11ede20dd9979684c75d10aa18da78970e2ac265cfa5a20094e0c fb1b229e4eb5246eddcc9f0d68f392a933bfc6461caded4abf1f164bcb0b86c3 f8bc9c384e81ab329f1fe3eac1e1666a4b292fc378876fe4cedbbb200cf09d17 5ea7d24a08faa8b6c1a4695cd8c8c26c987e0c7284f743fd4e2c487d9f5ae7c4 34ca3bdd1c36eec1977f3127d06a39a8082820e4bbfd37883ccc6c0da91ef3dd 73ed081422e61f9baac5fbbdf86855e879978830d4c554701ccf0f50ffe0436d 3872d17415c0b2bfcc5f6981ab490533c1bbed7b513c3a317d1ada6926ae09c1 53d3cfd53370df2247a4f81bff5f82aea3ef7c0cf884c8ddf806da668e876de7 737e019ed430614ac97c050100dec804fe428086808f0af643c858753b8bf491 a43d398b13fd566eb17a7d766d8f3bf8d0c429cde09d6097151419d3274f9214 0a85e8f27caf5b31102dac86ee44e5773a14f7610a87686bb793727e573d1db5 645766ddb1d874637ad04155f050d6a2ed45275a576fae5a0070d8b1c9297220 4a5f5a2a9db1c46c3f13cfdae015a3c657db99524c8d7824a7e6c6b29c70f7fd 07e2532396e40adc4aa6066804c24966f8a0a0d64c1854dc55b7311fc2c40592 3ef15a6054b023fe8fbe73ebd6b0344ec636e07e7a15fc1293fd2f17d3f8647b 63e7b977b778ee6ccecc971eaa15cb6f51bbb9938cb6f0c0166a92eed225048b 48eb9fe7240b21081283e0bb8b36cf5aa0a9155c8eb54de6a35f81576f96f389 c93f85f03e427ddacd3dda9a6cab93a647a7c3749804d216c4ee33d96ff3adf4 cc7fc059f816f79d6000b829a35625ea696a80ff65684b0fc172586523c4dd7f 8019bf3508abf3ec59e481fee8cd7f7c06879460fdf0dadd0a02fd56bf5ca96f 2bfa250063d063c31d551df90540c70a63313bebefadd705f0b01b8b50ba7cc2 a56f86db8de47052764da37573cc6d2405477759195efacbe7181c265c1832f3 e0ca2cfa478b0c4ca7cee3fd416b3af2e77c362f13b5b940e5cb375570db1246 f7dd9ac6665d0f4d5b44f5d352cc029bd9295b63e996423ab092122156705421 2593c102c7596a2fd53c0ae20a8be056369dafaa9c15069bf8541cb8a9cbbbfa 65b5e9076d629751ef69c3b9b5b36316ebcce7f745a1de10cff0011ca558f77b 4ff0154fbe2fa74cf8b73f325bb050cd6bc3eda3ea9ec57bad757eca6761d3c7 e378bdb2abe0e69d51e00379b323fcfd6513863883ffa5acb20c8c4a2b7e8b8c 75790992536a0e9c2b5bfb99228ca1bd2f0dd7ee15609b4dc206970c285d8e40 b5dab53752b9562b0f0cc5d01dc54042f1166c55a279949bab4620c851fda823 e388f5a659615ffef809d70ca92cc17c542f7c423fcd81f49684d7de0d5f6623 a9c06bb26937425dd665e0c87662d4cb147d0db232cd184ed3df5d919c56154e 280bf4c9e227b329ba728870767172091b15fb0c04e4d5606c85670f72d67164 c8e858891dffb2b511cd7e447e6e21eb3cde36b608b67e2c339a3e7f73663b95 c77558ffd2477eb69023a66644d92a941f61677b45b6ffa586b47d9aea5175b4 738a3a521664de6fff11ab8fb91e9b978fe72daffcf9f952a6a382cb8d2f4dc4 046568d4d5ab01eb3549eefafe0f142a6d3a7f5bbd54d3a864cc06b0d8850d20 7aa2b6f178efc24f124f94f5c0630546c72c533871c7463f76ca51d59ae2e708 20b0ba7678a965bdf5f15831645a75306db228acd6a5d1cfa512abd70e769017 2207062c96cdca08dda977ce21be783c042a50352b3081434183cd1242d81adf 747dfbfa76e0b36a8486c792b7056a45a41c4f5c4e0f0cb4104c5b3388a7bf56 420f230ca54fc9074b152b6d2ac01aad98c888e99d77395064f5d3c0850d5a6e 0d2c8c152904f1037e783c6fdf453ef41c238fe2a430c801291a220144489c4e b28be4d0298c22377dedf629ab00e04020272ab892654c107a5b0f284cf05762 b555b40a6de85b6401fe0ad333b0d4012f4ae25259631685d2bdc1f4c795624b 2b43384cf7adc16c3f3e7f7be15f384245b04758f9de239c4882adf2518a893b 1e8aa9fe0602a754a920757bcf856e832045792ca71e10194dbde515336211a9 3e2782d44b53be2e187f60bf9e0f0287f4129d48d92c2c28b93de4918e72d0da 60656a0a754b4c88077cdba08ebb6dbd89a3a19e864046efe12f068e43e48fa7 d1d5e7dd307c3ff7336c71d96c64183c6cc0f413ea3aaef822d967ac8ea9275a fb48fb0b8fca342df1626a4259828a48132abf144ea7173c03e95e7f34a6dabb a7c84e0d44b8e93e89899b2282c52d5016a4df21698eddf521f89db3b869c125 78705c8c97491c8000f738e7859e46a1f15e4e54dfffd40ed6f1a74e99a3a841 12addd863645c495f89cc0bc3cc84cc14ab5be17e298fcb688d2951a739f4ccf b0f3c280a46ad81724c2f35da9e1850855c65505fee51d955c6874331c618cd1 f758c1270198020230cb24d6f3a28a8461edda5cc3fbdec32d9fa4afb0c00d89 4e8573cdaa14e17b09543856953d13421beff8bd4fe03c595709114b400cb0bc 4fb9b3c35a570ee93e03e60949fe9c095f21dfcf6a98ae2aa96504705ec2cdd4 807c1d4948555fff4628fa960e865d8d8ed8f4849223ab9d5e209b1a1a67048c f0e79150293d93286abb446faa308597104deb13e1ec27f363ac48f46f40b963 eb39e1de6df29529a63dc4c3677c58adc337227755e874fce17ffd34037da85e 42afa542103eb7a3e6016c24142765c817182be2c2cb6c806f064629ce284a15 f9a9c1bb6ed7a721182101c8bf2eb0c58ec7426893ec38f43a48a362fd2e8474 68aaf7f50ef4477d38799d21ba175410770dbb97a656ffce79ffffdb5ef3dd00 10674794e8666995a59756aa8e596cc1cb05db3362136fddbdeccd197b02f270 ca309701a327f29015219c6777c526cf05c900c71b6e71b621425ada8daf85fc 3eae8ab644b6f83e571b77c9986bc7477b976be1dc6fa77a48a432ba8c449f80 667e2db39bb3c7e2ed3c5c45a7e3a6954c9f567a74045762984c3ab0e834e536 7ce31fa195af0a47708c49c3a159091702cdd21383a81450ad4c4243cd7d3b29 0eebf97047d60bbcc65d65114b7ad59249e07033b03d61053b844d6743bbffb2 17cc06d25dd66c6073dc76b9ddc3f7153d8538259f8e4604077ef25f45015b32 2f0af208fac5626648a464d4134229d2741ceed05062ccd2b06bd1f6f5ce7904 362070de95930c7619e227a3ffe61a0db7c6e311d11bf2d584852a9e661d4521 cdd469d25422a818cc13cf73c34277f494dadde50b96216798c1d1e8d5c5dad6 1a9461bb1720c0273e19d33597aaf0e4dee52a07ad109070ae248086f9d6e61b 907ccba428a489065d842ce35ab7d9d75d5c2e3b96b0da7755ebe7e45962dcc9 09549cde74cb6aa3626d2948dedd54c19eef59946eba028efa85d225d36bc357 d8776ea3a6a9d5579894a0474b90925fe31cc54267af42e35dfb7b4c6ebfaf71 b75117d3ea21b1e8ed785113693b3ff55d2b571787813a08695c4b06db079ae7 42fe322f48758b6bba5f35ce02747964e4b581cb15cb1f0553437f5cc537d55a 3bd42ff5dbb1ee7c2510fb9f1dc811d8ed58ab2bc41fd0f1e9ea3db9dc2a309a 6a356470f66c5ddd364dbfe4f80accbe1a594b65f7406f1266526928a1dbf73b 95b5b77df664d4f21602ae31a661b49df3061d20cd529894f335906a5763733f c57f5d4cebe77eca47775fd31dfa3b3698026a810e7b313039135e080cac5151 72c011776691b517a448573a7901c5b733d3ad83a0b265147e4da79fa61815bd a929875172f278378790bf0ad61a22b47b6c821a669f2a5089c2a202fb9b1564 9aa44f28af3127ce096bf32108ab7fbad5a16030a1de1f4a34912899d1261a06bad3d7d34cb48224abd4276378970a94cf11dfc3e2622cbc12b5b4d89b83b0024a7c3e0486ad7158c403421722cb75ffcf354c2a242d3a46cc3194ff129c4701c10b997e05a672151a1c8b272152efd3abbb8d8a935011855c39f39eedbf820bf541969d8e3215c36bd778a9d722bb5053b9e0d7b7f0acc3ef485137c47af301f5d11bb731d10daf511babe24876f6aecb48056b4e1dc4e073bfc439683f320b0e601e614458e9e1076c72892c1884919c5d8dc16385e73d53df872e24f2ac0a1daaa40f8d8f4ad4364b859f4f588a108ea89cedd973803f2e34e7e71d3eb00f9e57628356ba9feb29b6f17b881e3522e65bba4fe7e4963fcd64d292f26eaa026d1d0198a816d3d5cf66e5d67af3d55787b8bab0a37dde762a7f80a1f95d460f14e69988f04483f01151a4574ce1b1ab0613240822091292c54d91d62d53f307ca4e414c6be62164ffb9230a7b4c18c22be9cc7234e68160afebc3052caef806c617e33b7ded80c9205e1218223f2dd49bad21bc281d5438ed22d0983449d204b11f6bafc63f10962d995a2560a9d651d0b9decda1db0356c999987aab5d0f037f81d58083905bd5479710b858ed2a0dff2cc72804e91fe262bce642b7c2db082394a8f4bcb0d19245fd84fddf6f7bd6b2566de8c3de1378cd6655a5f15f0d0e25af558d63090bced19b303f23e6e4110b7a01c20b6a6291f51608bfcedd5e08a7839a9296f57075e8c243f1d0b12d7dcd9eb7a35a1a77930cda810a8823e100c138ed3c6a3a06e111144c9be9f49bf95beabb15a95ad752b8eac1d9eef2ca04bf5cb8f2b9efabaa5d4daad53aa0e808a36aac1f35e3a9edd087ce7dbd17b40cd4c74c8915d551c0d9b6bae1a11a63aa90c2b5a49067a14f593ae4568a589902a39162a55b81f9b2d2212a57fb5cd3d8fb1979bf9a3f0b63dd474ac5907758081a93acc3979b74f20ab9994da6fe34ec9460bb70c5c1138352844adf69136c029fbf0cfe7def6a81a863a3e0ded1fe3030f4193fab22b58bcdfcca72ac53af0dd17a7254cd655ef7706e40d2eaee8f974adfaa9ed316d42fbceaa69f68558002642f28ee43821d26ab2937a7018acb5ce0bdc07bfe46001a0213d4f2e95a420745706a0a950973be0a89229e8a2d3a81be409e55cbb2234585eab6f0f491830fc164c69ce4c5bb64e2e0d3d98c9caabd3154dda901e38fc5c25238d2120071014de88589993652f446f7ddc75baca47e51822fec8a75891346c689f6cf2e430a2949d41d55a24ead408fb6eb93efdad3f9849e7b17204b112702742e22e162098bdbb0b6330579e8319e92b423ff01fdf825231537397da4dd99c7074617ac03e39b27d031ced80f10ace7599295bbeaa4fd97aabfefb77e8253a37a6812be0711560611ca1dc543569c4c2a1a33474f79b2a93b8d7e81c59c20b4ac28cd6f0e5078940f429b6b79c53368d2ba1b35533246ce48598907fd4aa5c8fa282d120d2f0b6aed33c1e47ddc3b5326493d893d3c67a310f2c6fdd88ddb852106ac2e0d05d0e2371dd6bffbfc339b8af2b6e4a23326ce74e43f16083b58dc4e9136cc06b7438587ce5ffc37bb3258747a9983988048d00f4daff43253eca37fe8a031051bd95c44a4e5f236a111a0a6f41ee48d65f06799179ce637aed75eeb8b24ca0456091a0091d5ccf273d39977c38d33b30a36514cbe9343841926bd786580e00bcdef64d6764b1e682d731924b81c3c6ed877e3fb717212a3368c15e3f0d71308674db0ff866b27fb297ee55c6eb0baa854857691d61e4532b6a5b316cf1e2b0348be18138282e47317b302700c9932eb7b9a267857ef40364c353d9e500be603bc5a73d58af11326a37996f50fce3219e4da23db8a4b26aa3636beda52dd930ee82b0e6bbb00133d2b7e76083a01a1160d169761d015a67c7cbf9313044a3607211522c59658d1b533f5c59f3c7b4e066891b47d28503d0f80feb08a96835a0cdaf2e4dd5b14143cdf455675ebb3b8ead0d100e7c3bed260421b158d8dddd10b85759d13066887e455adc929cd05718958f38aa35c1276d7f0cca500e33220038d093dbce7714048888b5e2a5bd57e0a7ff1cb58c90b35eb8c7af138fc39590bf99078f68efaa09be444db3a91435174b7ae7282a9190776fa723f2ccf9f98032930a994765e2f28f303c7eccddea2f9d9cf4e868c7bcf7dcacae4a162872800b134f8a99ed1a689871849321898c6593e56449be1c64af94d450a7774543b0acdcd7f909b72009c09d1752bb8e9cda30280e79fdec1d37656b24d37252ad309cdcd7ac221d1bb1170eb725f77f2947210828fa68a3f4b8d1d7d24502bb5e60b184ca113665a5dae33991e260633758d4781dd9db0b89a1e288b210d8c45ba01160ee93eb4df4ee69919700e04738ff91e74e8f244b38d42a370afe1c24d0500437c445f3f796658ff4b1ab10c7e3185382f178944f1aaeb722b59735b3a040438b53b71349494daf9126a29e1d2bb9e053725b4bc5e6b5dbbad8d2cf64f6101534e6bac8872e909d37e8b4417eea3bc563a84ea5f76a54bbfa13ccda044b50be537fa030a162de380a6c162269937374b24fa7764b50da1dade3a89691edb00154d8030d7e2a7ed39e9e3b05830ab747999fbb90a294e52a477e4161d2ce4002334da8f97dcfb3ac2644e079c36ca5fd7acb3f830adbf6f5e260a16fbb27e0e7bcf2747d79d68fdafb2def84e5c514ce32aa0bf317633562922ba58e007a603383f76a821116b52f175ec657c777a865bb7fcf23b532fbbeb5b990b15c9c400d1be9ffd463c70e1cd8d3da163779d1b7607eed5ff809eefc67a12836fd73f06e3c8455105407315a94464763402e0e8d17911d86378560943c65cca336fe902aa77cfe22efaf4ebcb31a4bf7d3e47c5acd1d0654b653641cb663d6206122e0c283c93740a4fc6c7a28ae693d4bbd9943b17069b53d5954a5c634f93548bcb0b22e17ff5f2b1520b7748746717c8248ad42e0454c5d042f86bfe7fcd881d860d76247e95c8f087fb6fcfc9a7c457f33f60082358da5c15eb8b8e53b778a2ae017dffc121c83f9ece8be2af72750370a26f95f6481ff8c824e893bd06011fbf008253a5f81b64e0fe516514019fad8dd63d8a2fb58a7f76250e026d36870043000e55115f6ae68ac9ba1020d92156080ce6554b27eb6438aea7276b2311366b0f9a6123a1e35e16611474d4950869810d554f2609653b34127ad6989cf043920408cb1dfbd1e85709ddcf52f987a616b202ff9ac3bbb16e7d9d4a002945a23c0aa25c53fd5614a4eda8cfaecc585bf486c767be0c0580db150cbd324143054a0a933de88e7e0b0a5a6936896ec6bcc055eef65ee3f16d40b0ad35289f00652e0d4840c586a64cebaa096f390b9a58b3513919314e9914b147768a4658dd52040ddab3ba0d9426a514a3c15dc4c89d5cfbc8868586f0379f109c3c06d50d9d2d0396824510177dbd8eea915d2e05b9649b68f409f66172812f62f833d4b80b4405687e41f11be564b13200298bd5897542076cdca7586669417dd035649739b007141773d8ad4eda982e934cd4a35f40f8654358276723df1f940f06d79618300de0d6421285b15ae70fecd0bd4b31bf2ee05d6a7a3886728d38201eb39d84170044004e9d3877154c3cb43f72d9da74e8e7a7971e2da41a3ed070de1bcb546709f977df7fdb4eb56ebf77d28e2705c0982627827bbd06f29c13a286dd560e0702ffff304e637cbc375031e819a8b9a07401ebf18d212e324374788e44a6382a0deeee7447ecb9b90710718039e0a2872bce44ec0f1a112faa3e1e3975475334094c82111a49bfc179f67f96f74603383664c578a97d54678a5d32a6f7173a61024d8214ad543ecf7f855b1f3d4ca6c3b0c131fb8dbc28c3d899f5d5afbc2f4204ceff5141570d95155efcc4f039fca28d4baa3a0ee5e2706357b20b292037b50f8ed8724ae92920528c963246087ab98bfcb2a6f2f4e90bcb7c1186b8ab44000cb1d266395845dc0f2ad7464a9567514d9f4ec889a9de05b0db9eaa92630f600aedd7a12b7df63faede525559e89117b4514faba40474117e38eb0fe3cf242f0a250a4354b22eb4779302ecbfc661bf185b191a19d320c9844bcf49906331e30d1bfe9e5e6a7ce0eaa41ee7afcc1d8e8ad618699b477325935724f5c4764605063cb7b81448872d71fee2bd93d70f84324775c71430d423532543cbc7f4f3c900d9b69bad858c1faf2e33d1d050d6d95f9b96a16b16d82eee8fdf5e9e44f76e0b6601887b2a55d2a756730d136f3a68b2835d4705f130ab34fcc6513dcb589903c97b0448b8f6a9c308d2bbaea67cb01d601cc59068c93b88beac42653dea6a07df3a2f408015eced5d43978d97f7865e8ee44ed093cdf95d39724ef6ee36bb09d3b396ea3418396105730058af821ca5b16aebfee0ea21b35743c77ff049460a913c997ed7fa285af0eab71372c95551ee1926a91b0a61a1271c5113438e9f0d652f3e05ded77fcb952df51a8b66f9345f1f110a83b9f04771312146a1090006e407b6b3491cb5a4708c1138c68b62966aafeacf8d94da7ce96a963db7c4f309cf46404f65fbe82d746bb598e69a73d11355fab29c02d5a4db3b66b819d58f0645c9ed677de944baf08f602bcbf8e0a1c6a92949b0f192cc3c8f25c859e06b06bbe53cd2084db4d5a8b4f1c9ec62682ff4d7feec32d05eaf8d8b7441ee1bd90d8dd766ed1cd7e93e696959839e389189a07f39504f831a4c4d94b33d5c03b4047a36fc7425353948f9e0b2de020ae7b5531ca4088be9d91c0ea584cc2fc46e0c1788e4dcb71e42c0219393ee4836ab533db85a02c0613ca1da60001bd3274a00bb09eb147abfdce95bc861448648142d12b042060fe777f127818f87167428018a92bbc65ea11589de30bb1e645cf7944642fa5aade8ddad0aa0fa806fb7940f047c825c44ceb58be016fc930a3b96135e73cb70f298371995e1d3be19fb72076ec5f16109bdc84824dc5fcf45c7d57ecb017a3b20b4035aaa83dc60fe8d810b5cdfd793807bac6d3d40f26f74c626ac51389a0d9b73bed010f56c4e0705f007ed92ca444cb0c73d6451ed93cb516fde28eac0ced3a7ad2d8622f3e9061651081c9705f6bcaeddd80dec50d2b6d873c1290f23864e9db06e47e171a40403490a19ccb909a9c9349834869df4bba641b3cefbfeee509ef28b6e3f9e4021c60b0ca749cc227352da45c7d43daf678a95cbeb9d5b7982e560636929e298d7fe0800e3fb9d570722273b9a6288b1712b07d55fa00cc10b734b6993e53c51e793740fab439feed7ab145bd2a71f95806d24d0e8f37418f0332ba7382f8e88564a4807e993b24b4571bd3f3d82824de4fd5d0bf0a7b013b58a6a81301490f4555f7108828f701e6dcf84fdfff32eee8d93a06b9d29d9586d17e37308878fa258dcbf0db8b4464a3ff6db70d8b2c2b6168ba3f290671007c83b00f6e51d3f7b27eb8f0de50be9f4d8bb9149c6a9c4d8e6e4d4ce79970bad2299bf41174bfbebeb46750a512cc3dd11b9991133d50ba48e10aa95a06caf3918317ff498c1aebc3ae5a204918d8ed0eb6f1cb53848e85cd257ad74e2282d21b4fbcea35ee5bdc511a4ea024de0c84aaeed204a935c715d11e6494c3487306c00107cb14ffe57e2d316700b12239bde683f5bcc39ebf8efa5e49d027ba4065ba62733a6331430d20393d70ec54e0fe00f30a5471e01a2adb0d4b3635dd20d539f634f91f50b45c7ddd9c902d113e75353d5fd6cc34d5a8859ebbbd120e027be48949453c56b0d0155e9080ec8f721290ea3981e39a5d1d35a3c6a2e7ddd0c7430505ef3153e70f33894d301f463ba27203694d78ff58768bf30ed1885d1396ec5adb161b4407e3e88c990062e49bf19614da65e5201e42cbf6bae651e45376785b7e19c0db85636f13afc04d56f3f351bedd7f484ee03c3a7d5b22e6a2f05fd9620e789e429bda83f648f022be314a1259b6bc1d48b2761b2797bfe72dc7a9fd14bc1b87edda11e9192540177819147d6644a32cb4bf879c081e09c2acf5e5d7bc19cb77e84b6abc7c6940b8aec2ca3611ed0605afab4cc631ab1b182bd10ccf8b3e1517c3a7647e2e4720b71873bb6920a1e9b232b3ecce0ba091132d67bc31567abc0bd5d2e508080bd0b1f20fa621820ec786a815dfd9eba554b9606f4d58722cf868fbf28a37835e307fa0de5d23aff8ccccb216b43b93f72eac54b7f4254c4048bcf2291dbeeb6e50fcf846ce6369eebb444adca7aca016d4178a4ef143c99cd3f523e74834f257d031fdae3eaab9d72078e1ead3800baffd886b8132c5d2ba0f09afb9c293ffdd00b53506af750f312096c75bd31addd11a7fa221fb28583cd0109821bf927af990cda79bfb423ff07ecd9e421e1063572789872535265b818253da3066e152e92048e4bc411a369c9dc6dc3832e38ddb04751cd4c177c0f3b49c867c1473d7fb90cfb4aa30100dc2bdf5838221daa3a6f955f21cb25cc0d40ab9dee603ab167b60ed2d028dfa30e62a400881fe7ea32c64393023f75b203de1de7dfe5c2e5c92402e70c8be8c162d43e91b9180d6b36650d64ba111838e8e2ab381d2baf0eef740aec4036c6fe490da504a075d9b9a60cda5186806749e0b9b82d7d048a44168b0a784697c211a350de03f9cdb95516ba5441076faea5eacf9fe5f0e8eec041f90b7f796b9b9cead92f72552a2a0af0fb5e969d56e74d0e9619dd82cba98134190c1869efeb69a79258de938a2e468c1032f05b971c23f3087de2dbfafe4030fa0fbeb9b637656b93c8cc16269c8876db9a853cb81714ddb07b92c8f4c3a114280bcee34ea776b6074f5d5686354b7917f4c3abd3b2080e3f72b6d6017641218308bd8e60837b3d0e997dd6c243193c074911f2b2c4dae6dc141133204ca27a4008518a521d188549110707927e7c4b803b66163892dc9ab7346d72f5df2f7c2a04c49c4a571d7aaa7f4e6d7a414ee95fbe03d480bb028ae4431182c3b97b356306185865d1cb3afd289a4e03eca8ab7ddb5365fd2e8778fe1aa940343b9579220b65f136f02eee2e62d99eda79121d540ca5f31fa97c65724e90ef8082109b03016f684f7903c673c22ed800d107f21ffd3a104f0fd5e6cf4856229a61f0f1d40b4219fa76cfa55df9a6f66c97c3e51f23b5e60a3e4afc2d904cb9c83891dbb802bbd91b3ff1c81b2b596ec2aabaccfb7e73616824dc871a06a3a870d40e628507e6032c5d6257cb1bafb60a133e8a9bc4b1bac8c82f8314a91f8ccd21cb8b5308b0297a9be0330c2b9ec88f21d6610d5a48f827b477ac1ffb508499fdb6ab550170ba8cc51ea16c06ae96474778307de2cf6703e5623ff3d9aecdc3dcec7157011b94d6988f2b00d64a6993cceb81643398c78a277aaafdcda1faa97b4355b70bd40932cce0c9a3a7d43932041b488786491e94e15392d2790db050414e5bc80621070623c9d65cb18589e51b849d40cb1b561a206b60b475955e0d9e207c040e4f2f100a677b90dace6746343a438fe30f2538a9c8d235c293e747b539d4d504ffc12c24a6fa1a289298a1a1c88596cb01726128f24928d109a9aa92e02f690ff7f54869dc09e0b59292ab84911f5bc4fbe9befb7d3289319094dfec09bef100156f0f9a4a91f92f0f9a5ee384c834dc152704b72fe4c5b99bccac3cdb07ac07a3b2e8f6be737930d951e07cb5d659a0c806c8407b434f739c82a40252cc0407fbd896664c1439821f42a54b6092a2d912bc8c97efa6704a314af61c0b2dbc0e99d32349d0ac6c917768273345298c2b042f81c8764695a8e3e8b9d07fe86c01ab8260cfddd3df6e36a3737ecb815f0a601394c27ad7c4c3c1cd5b017b2f320b4ac3096de7c07e399d54972e7e1e0828704d68ff134c2c1ac41dda183329850080f22bf6c9b58d2aa8ea92cf4092d01d48a352a82afeea09d90ce99245e0f70ad85a831f179336c802580987484b78ca32a50f3790bb56c34ae84f2970e59309679272f759f8e69da1c6b3a557667fba2155b2d7d5aa1faa481821c1cbe6000a42d549422c819d8a043c87f793984205da1ac774f0f9b9aca9a36e41774d91010c3ba97a0ee158ec2874d0c0b080ffb8e70bd17ddb18abf51d672dd1503ec503ab2ea3285b1d57be09394ac97fb10a22528da15c3c46833cd0c9c52c0a41a10ef327609c27d18991d2f0852e7da316f773fa1ab646e1cba53daec880d6c5d20c86e177aabde941077879c24c90f6bcff998edc31b6cba9a38bc5aa3e5583a10bc3d79c773b690b9d59770b6eeabb438f72c867819f7b29ae396ea259cd45cf0f7f70fa1ecf69feabc05d52f1f4caa45312cec7b507f602c29233c3b5fe28000cc70e761e6fffb9693182cce6704994d5b24c2e9bfa0b376c5a8d220871c7c10284901c81bf6b32f133f4b200607454a515a7324e7ec8a2760e8b087f2fd4ad025f933146d81650395d9e03a42774f53845e6bb07cdef39aa4de99068b73dc9093f15276c7e6dbdd3acca7db0847f044f560362e6c4d1ba34b42c1157de63f30b55aaaa0dcef77ca435b49b6efe3446a68932aa3e35bf26da93290136d62c0a0b57380a33490fdcaff7ae62f030476da0915a2676f793b765ebd43f34ee2cea011ab7709739cd70ab883e4f8bebcd8ed02b58a8344de1d087d7e6ecb5c38ed80479e57e207e1e26eaed7c7aa400c4cf2d0a7693cc667205ce87ada89fdb2bf20da3e973481e69d77652959dce88c27dbaf2a754adf73faff115714de61cd28000524efe0c72ff2583c993328e130be2385311a26d859a72878005e7ebd5993005250c3034dffbce2bdc0fceb082d4efc874a4274fab4437846ecc85e1239d490279b561a7966ee9fbd50fac39047f79213818ae57593957c1f05c14d0f7b7bb0779043999e3df6ff2556e16ab2083b085c8093f3f917ee87dbcfea4df95381f03cbf56f69eb9448cfd8f6ee76d6c549fb1f624b7828dd91254419f5d55c5eda01942b96e071105f9a3d9a680308ba7e9d030008220d5c85d96f2e45ccaedb2905767a1e2dd7a5672283c16ebc3464381a409eae4152b5dfd3b58b654ecfe9a204643b78a19daf175efe95a171429e3ac97bc5c78e10a00a8082aea36ad1acab09ea0813f36c088b90474a6f7e9ac0f113ac1d76b69851a5b57c76b9a2264fd60a503d398fd8281db00f2c0f0611322c40c163190558c8675c595a4f20655a750ff3928bb8d4d71dbcd352db5ad183eab13494c4ca4e3d2bc1362a642989f6bf0df6f5bf80339e42749d808d6cbe156648fa4e34baefb73cf2b8a7bd36bd48330f3a9beca132ae4cb23de1ae12cd0a80cd5e5c4a5f0ac9b5f7f967edbe5aaebb03502cb7122550d93c2c5f858c4a65f3ce49eb5626ad0902032164e31c6ad2920f1df5f78ece4bb673ec30943ca87f3d5ce59d77faf4f0d380411d90d27bcb0c05339518e0ba0fe2a24dced08cbe138baba1cb916035d209e3bf93eb1598765a061c60c74576c6e4fc6a7a941e7971a906d7711314af34dd17fb84feb9565a5209c1877649429275f77f7d7f31ce2908af35da7a25da96383e35699c32d80c7808221e7e748f5452123f835e9f86adc60fbb15b2941564929bc621fcc630cda60b98360c7dc9d6098804004988d111f385749aefce1d4e456800d93ee7e0f2a10fc62bfd991132f54fe3c8e2c14a631d7d93e960628756eb43ba506c83049abf01a135a99bae53dd8a03237d8e1e5ecfd12d84c06623a905018a8bca74f5522b0080249a2ae424f39bb269725791ae9f1dc3a4f2b391709265ef62d9b36e8db30d443ad4006c7f18e2dba1a5275559a68f9b638eb3ebfaeb287589f034be2ae80d4c618988df4c40e5b16cd8516b6d1df38fe590a7cc881bc0e232231b63d80f04101ff0a0ebe9490fdab6f4ebb3d1bc976030656649b9b506ed7ae22842186807e809616f0b4b1094813da644aa40627096edff6ca6773d052b973a41fecba30381083efd0de0ca8dac31817e05e90f12c93bf631e8e6e27ec9fd12862847bf0d50f1ad4b71f98f364ebc43ed1456096b682a571201d7434442daf3081e1ac108d6620e8c1c8e3287730e2e31447bfcb6ad555ece7ac53d414fb2832e43c74f03a4047b682166a4dd642521ca22f2b2feeccda9d1dba731852478d96fca964a00967424e2fe298df4b70e1a414b566edf43391e2ba47f7cae2ccf3210eebbe60ad8c45f24053415e8b2bf5326a97e9d00664928393b3747d310ed76c04243eb0e1b2a9f64965ec8d565c319d5bc62189b9a807a547abbb44a2704519af38455019eedae1b26fd90f2d716aff20d9c305b9d839dd6dca7cade399fcfcbd0434109a85a4315ea018ac532ca7b9dca3aaacd5520ca82acc95789bfef18f848e62619a6f6eacaa04784fd63ee6210fa66e58b19e97bd62ff84ee593baa345da79830832bfbe31efccd8ee96db4d1d3c2e664943626f22e7bc85deea1b1765e85a0302286c6322e2f76bba5c94de0e4ee7e14607e355a0e32efc5021918d9d0e162a0ac0aab3b9b87379e8952db70b9f3bd6ab1f6da97b1c2b38e2c469e594f2d9e00474a8daec5898397ceb38d4e5abd0f03188f2ae9f9ed69dbf5b7ee7925cb5c20ab4ebd238a1ef14538a3278272b358f99b36c9e334158eb0de5ad019793b97905c8c8eaeb088c882293abbfa6ea94af4b6539bc2e445a31236a82caaf8dee4b0720d1df59d7a3d631ed905078964ad91c76a177fb5f666550f110b9671649a100e87fc7c8d5429a0ecd2b88f7b441ad467feb043eda681a39742dda370697d2007a320d906c0796619a69c5454dd795f42faca55cd8479298a1e00c907275eec58f8afd71880ebba6c7831b5e275d978aabeb41f48f32cd5215cbc0bef600a5031725c20224454537e5e96770e99a323d7e5fee9813d05e02a04aecff835ddf0a2dc63e4018a6ea4944ae18a2c51cf11eadb0157a77c1f696fd6a9f22df91980c5828ff490d42ceff700bbe181432ad707e0f64693c857eeb647f7e8d9d812906997075583efe51a722820d9f8f457e8ca7678d4de6efe3ae61ca84a0c679710893ea4dc2004e94eceec8f609929348533fda12d04e32bad8a2dc8e46c186fe0370d2a9a0f44977e93fedb2661d5ec16bfd7e483a13e96a4aee10cee422339109400bdf7a2649581bdb41369dcf5c68ded214fbac1eb8ba3de6e79d44594946025780bbb227a38e67fe08d980a50ec2b5378b6ec04ce4c0143efd679eda5ae10d77f95cc5ecc5ac6092ef9ca992386424d9f43e1e288a43f1fa06ace472a9270e9bfc112558fff7652df23dbdf3c608d69548589555b8ea99080ef364c2d6820e4c4ec1e1253c86deb1c7754f7ebe7231c2e5597ce44aa7c077737e1582671e05ce9df94a47751e2fe362d1675a947b79b35065992a9760d3726166e33cfe9c06800b1a732d5a233d15f2300bdbc03db8e227b24fae18a829ca3defedef0b7b0357863ad3354d7b048df4e2469589c5e307edcad237470889714b43b0c1d5750158379f6618f159922b98974ca382af88083ee359dcefaa818d709817e22ca800505ca109148c3fb0e11e3461eaf696cf8148b915c6373f2d21cedfdc30f2fd0bf5d101fd6a5fbab74618e8c0934973f9e90fe0677b309080e1e01813ee0b060e8c88e4a5a3db824ee17723a2d00e7794cdff3349f9afc7c0dc27e19ca06bee0ed83cf8053e4b9e89ef013cb1a6fdb2f1219495f7e44bae917f7f0400610e800e2113d1020d8d491898bbda7c37915981071ddd31afe0cb2dd5be6838ce79f007a17e939864fd2332819a7cfe20c5a81e42a789dc596cc8b7b7ba255d8e734706bcb9e3e236d981d3aa618452777c413f1031bdbfeb39df2132f86b1d405eed08a685f1dba59a30b6e2534fc3df83dfd3deda412a181180dde1ac4f7838b85c0ebbea9dbf9485e583a377682a4393b7228adc7e5d6fbca798d42d7b7f99a7d10aff0786b0ea419a16f22a5c43d3b449a0bff8d9e62ffd3569badbd74531e5360d7d44791eac20b28f9aa858a58ac15e9e4aee0b4955f57b0c98ce7952a534570b033e3023f57f6771eb67e28e9683f490398900cbe6134e523a6e747f2528c6082f32181946df5f92b81f31abea002c39cb6acc9b1dd0d49bcfb3d524a4c7fe0ff49d84826b5b3d537b71152cbbf7feea5a2d475c8ed0f56003be7201d269f609ff8718ae2d182ad80b7ea798d91210dcbaae99549d2b4ca025e9085b9395ad0330671f791dacf3b0c6e6a621404c680c8ee5fec1197312fe0e72367416d91904e11abbec4643a001ffc2da22f77012f0a5bfaa2623de2037f72be1df8a389409c33b0613bbc2318c9c2b449cbcac9247fb9aaf6b354bb87054750a01555cc004 false -check_ring_signature 545219d0c68d651af80088d02ac62e873637a78e6f8708116225af2334029243 6fd28c557a10651b3bc14ab85804f37b12664a8375e21907bec0aebc37d957ae 14 844a93cb099d1c10fcef404c7599bb65553917247bb7d210d282bab09871fced a671f572bb3c840b723b37500b6d43fbfc592b0c5551951ce496d80659e9bd56 91c5b0f30cab457d5e605720ede636be0f3c762ba9a796c8f6b71f7dc231d835 8bd6c3fa9cc4560d8a4d624a83944db195ca03238082cb885e7cf204785a22db ea0d206a0bee20e96a8537c74036929b973f025e2052ffec58ca88f47f60ca5c 96dbb90a6add884f299f315fe5d4936135a99fc0112f32d8508f7c762d81c678 9517db2276363dbfdbf71fda57fbd24c88c248b7ae00dbd2882e3ce2e36a27ce cb24079fcce599179f226527221331985e9f12a6a2e8252179fc70988ddf1b42 132712be6f3a8620b8c3dd175bbe2cd605574756969e2ba10e42741501b26c36 33d4248399966145ad00b3019491df8cd4abe76d0630bdf3b38f3313b0623bc2 bd7a23c67bddbce38f3a21d61647a6d714d5c15507502861bd1eb51ca1b5f5df fa3acc640801905876d1fd09da80668150de7870698db8798ef86a2a7855b1a7 5aed7e9f711d31401f1c29726e2636be928a6811ae1eb867b02235b9519f7ef8 7e0eef2a09ae92ffe2b8fea3c1e5e209a10776d2aea1a1683814327090936371 d5c50e236f505b97587b46e342653ffedcffb47832b306249a90bcb3c31bf102fc01471e2a04c759f28fb9aed9b8923c847564329beb5b65fb50e22ffbff080a0bdf0aaa78691a95785f36142b6b492e6d5287979d8a09539359a4ee2667d80a2f7374d81ab4f803c27854efd71d2ff92de27695d78511e51166433f51b33d07c407525d0d0ac58af149b1faa9737bc428bae26b2089f9215797b91e9f618f0d94f642dd8e3dd471271e42e360a933265221a981d60e16ad1920e19bea2cc903ac2a8e2f728c4bf9bb4d2c05fa559d267460e5f7660ed782987839316d094e00110ece9f32a67dbbb3a6c64229947a2cfab0d70824ed403f0d94a09c2ae65c0d8972fd343dea20eb624863bc21517d0f405b38e48fa81e03d50b2c713683a60bc6e5a889a4dc68f674086a4174267c2bf52626c63a972f4a8aa8b461d668f90567e580d6c65c7b2780e96dba35c2bca515b801c97bb83077c8618a08ada7e40349d0dcd483f66401a4bfcbb6ac72dcb216c4b8e86f31f8412182d121103fa308f834c05a581ccae7c28f638d0615d1255d2da90926905de960384980844bda01357949e0c75cb4ca314bb3b891a7a4fdbdfb921fa7ff800e602b4a4982839907368ec712775b99354bece5feb4c85d880234e38d4f6c63937f60a2da51ae970e93f72d3eac1ea23ce49e9a06edb9d404b8666c6a69c57c0183485cc3cb3b1b0cae257249c275649ce622785cb3761d9ed40ac14df14847a477a698eeab32be0df5336cdb399cb4d1b81e6e2d1facba70254a89bf18fe99c2deb4dd6dc141f90dbff3b6fc51408a293c11e8ac01ce34fb73cc0b27ce0ee33f76fa2e15df36340a6e5de7b37682ce8bb8f8388a343ca3c1081f784765f45549a643e9c828c5840ea9a78043c0a9c1f3b4a2df30810ef47c6194f35c8ade263ea03f34c71d992e0cbaaed0365176276a6a263053053038b2506aff201c5a56921bfec5709c5f5d0d3e6011787cb18f8d072fa8ccbee794ade87330d79df58e0c161de56cb29d7f031ec5376a8fc213772da33436ca09c9048d8910fd71ccca6890a1ec6c25d21c0874d93febe7375b2e4f5e6b659a9ac8f766e0a1194eb72d7d8d061138e126d3032b3a9c8180e884026651b529484cbaa49adb385037934cac12b99ec8eb1f6404f07fa14f5ec214b7cae8361b135f38d6b4d6b8e4defaaec60d798962d36f420b079862320652bc0359acc1ed7f2d2e388a8343f37231d689c6e612b347830009 true -check_ring_signature e67339d0d8bc7565dd6c3048229adb6e57744120d461e31a3bdb1c7bd18f8224 50d28889a61d97a1dc8eb0e454a250171484017cb911b54485f38732775cabf8 5 4335fe62f1284dcca60fbb2f003d1bddd1db1e48fb83c01e5c7eb4ef59941c68 c1ff9130073a08d6c2dad1a16f27d1d3fea1f9fe17294b722b85e136664f1857 f221f15acc0f9d250f0665c6ab14df83d74b99d2377aa206dd5f86f2f794fe3f 65799e40da674629148aae4f494543d177ba2edc7f1e2ecd17b4a42780e8f332 e56658729412b38f96c69555159c39e942251608edcd73f5ca550cb55a34df7a f1bf509737584a29d4f30e589ac601123e66d3ba132b2e74e918eeec019bae0ca2dabc8211af101a1603014b640b0d0dd05f98e62aba027b8e6c680f62c9e4016dfad8fe19b1a5bf4d4559c777cf2646f79c82b22c52c7df5905448c1bc4150cb0e5020320fa83a812520be9f50b2dff2b868a00bf33324c86e7740743cd32082577c39e39aea2d7dabec174722040cab675a21da16e4ee4d05d29eefb5a900bd6ea942cc75483f9550846f3f2a8b48b4f2f753b0606976d36485c22c1c0a50a9f277b0c81d676ff63a8a40dadd660ce8026486d0fb69158aecaae29f890940b05267545277668205b5759b48a46c6a1c9646cf30e28db0dedf73e136a785a0d2c9fe257d6df53cbfb84e806035accdddcb00f22e410ff630f5417325ccae50835a18f64cdb28e07d58a89e42b01d0591dd207fc6efe455e750f031f3be47208 true -check_ring_signature 0c94358ca3d10eca60e65db3109b81655cf9d92f462881803e04c1deeae318cf ff8581b5972bc075dab40132cc6ac15496543f8b3f622693b1bb6c17bac87a4d 69 31909a1b71c9f5794ed4fe1a112ce645c12e2973e6e6184fbed34f77c9c00100 db4af36f6c9eccb8faf62a2591c605fd87e330f04c9e954f6ed73bdff108c6df 5ef9b4744dd990c8bf408f1dd6122e14a32efca7b64c4f98636a620512e76b95 e59929eb918fc2a3d2c2b51934c19acff55652f0567a1f84933f59703c8d0826 ff1777b824b1c80e6f309166673c917114aebaa769992e71d38c19fbc13ae017 ed450c4bb03eb811927bc9c0c567b72a56a642f7d07b1a66864eca04b3251c18 efcbb2631c90e69c58eb5170a8c8d50c0c710849b5c3ba7de19d45f5c64d299e 95379b033f835081b8c533de8b9e73b60c245382b1443c8e184a9e3f18357746 2f164bec2ecb15e26d5173516926a8dccf35472b7ebdb77a70d0787f7117b6ed ec58042570c9f20310eae82f3d059cef322c9fb4ad49fae6b6e887c9cc6caa69 629fef988b779e1b872665c14d8dff4e2b4515df70d5f223140eb28e46150220 50cadb9071e09db699ff13032bde1f94b392920bb0e107b425aa0848b08582cc 50f04b5e17754a5543f912c1bd1e2e21fbd3194050e043f34f1ae93f8d1462c8 f2778f8a2f2eb0572f44e1023b836934b6e012dee0386b4bbcc64e6049a542ed 258de5dac4ecf47422ee72c940ee0d1f74d343d6802b4e03ea19a1c0596d184c 65fd0a4fc2529ee06da3dd4818338d15db70b37b3f0a777b25a9fa2902bd2374 0913cbee2f6b9362b11c8d4931e01e0f0d1a3423f6f981b717b95f96cc24c31a 87d1975f8eee648a35849280fe5b53667dc76a4f565f861930d20ddb3f9ccb8e 02e9bbc02c96c9e879258a68a37019e5788bac4a13eb600d47052293fce44319 2d67513f7bb121878175ecfbe613f73c8ad2d709d1a637a7c71f6f748c77f48f cdd60870f756ac9585ff4ea6c002beccaea1aa08b795bec7d101f0ae2196d1c7 88dff1f0a2f3fe5f8688ad5b006005ae03d32e692d3352839c8834fa9fc4e8c4 befa632685a305570a04f6e1d2ce164706bcd6ba0a82894e82eed2685a327457 038ddce92cc99d0fc2e085a6354e9e105156a55870d579ec80535916af3bd16e 590d484b474fa9ea8b3a1657f39da64385cb9cd54f5e64d8c7bcb48649ef463f b54e6cc23c29a2d884e7070babd89693ef6856eb80b49ebfc9fc1470664c63cb 48491ab9cc7ca0996deae2b8c96ed2c4485fd7cb95e213815d7cd6aebbcf5622 a056738b1646475aa77b9d26a3f3f166e1de86d5171909d3c526e5d38aa61f58 299be0db3d84924e003071ea5ef782920365e664cd2929871fedd5a7435cd5a0 52c5116ae8d7d6dfe24d8aa951be524000043cf494583c4b488031df76183f21 4cd0716c13e64f2cdf64dc70b7861581c73ea5a32f9e746a0f5d8284f0386002 b53dc32ece76e23e5373e9b56566fe6afa3835e5db495b7106ecaf01cab9699f 67e4b28542296c3ba458b1acb388e6a02b245c1dbe271ec18c9fc410992e3590 5983153a5a088964c44572690a9f5769614d8fa8c9e5df385a4ccfde0c1f579a bc2654881d7b19bfe8bb74b9c77563de6c019f5ec346386a57f169fff3033637 efb237c870ee0248ddef5b4a332ff5064bb0fa9d99ff584c3d82b22dd2d1f419 8273daaa8413410da179bb68412d03244fc11bd60d19ce58403f6c375249d21b d91819ec0668dfae01dfd1b5e32e71f90a675e780f69981882b0cc8a1d5ac04e 6da9d37bf380b17705280776974f70077b8ad99aee587b41643dc58753526cb8 2756947df9911492bc11439d69edccd841a343c99b10cd973d46bc82bb152353 bb4db2da9ffce317e396877c8eaf1a50300caf82ff39bdfe57ba58a0fbac3893 ba837f07319727546f33cad97622f6aa35c3baaf267544bc300d1a17d0d5eac5 d0496280bb45d6df56504ae8efd3d4887a8d22fe21af9108f579aff4bb22be40 b347f23959769a944a908fa6ece787f5aa6b9e3ebf1d3945d49125a86a41c3d8 51783e8220240e150b79e26de70a875b0b8e7a8181d2ad12afbc0bfc5823de56 205f04c414fba66d16098a47a334e2104feda92c02f04f8b26e2e6e4f22299aa e98150f5ccc879349553572cc65ac165a90b4ef87d53afee4b182b4df769ddc1 252ca5a40da23f550303c8aac223ba42bf2d272e2dfe47479c640f8662bfdd06 365d809f9fc50d90ff1b3c2eeb120e58a6f68d1505a581d288c44dab00d7a3b1 4b1a437f0a636a510236fae78303bb7885f2fbf1f465a606ab9b697502748132 401ad80ad0eed631a3ba2b63feaa030eb719aa1884cc4fab5c7bf69694e4cef1 31c8c5a07a8adb25a2950896c0f5c902d783cb520b4fb00d3af8bea7af627881 18525f55a014c2c9e3e675d35ad694fc961420c43fe046b9c315528c462ca6c2 f966643b224950f51ef29210813f30411e54d95402f86cde15ef27234f086b0c 3b6cb7bf9cecc9ead1017b3da3654a575e536f52760d8250e3171b96464a7919 64efd0f6a3de198ce1bc366a5ace0c864e289ba81c449cb1c61e74c7080bf9c8 f8fc9db523cecee4c37f214568e03070bd0a55f617ee209c5934e9a51a2e8927 12b80cb2050a04bab27674e607f321dd1c61c0dba3cb473955b87a5ef834a9b3 38b8a90dca8be962d974c45cba51a56fd858628a475fc26236aaa4bb7407bed7 28d800c2c12a138db89ebf397c60ed129f6387bf0166bb023196aadbb1b40ea2 bf9986f12f2abfc4e4f8f985a7e661712eba466b978d1e0e88a83bff74a4926b 36a3622c7e4bb43b5045963877e5b996b4271c0149d39c017ba08f9ec73569c4 97ba39de9ccd31172ee2cc868cba5a4f1f6060c1d3fa53d1bc71f2561ed5061d 5445f7d57b1b235612b7985ff0713243e1c0c3999825521d1633a0ebb101339b 45e034d403f731f9ffa3ab78517074f22f19ffc0690f634a3a033003d6945a5a 72a7bb215bc848990a4e92a5abfb79cda2dc92ea1985fa031672a4cd2b546dc0 1bbacb8af85fbf864204e175cf85bb54b52e11a1d13ec2361d5ac98b0577606e 83399f34f3034795a8d6b0f7b6de1f3a8289305ea5a219385bb0c2c94852770d b24d324ae954c55808201a8ac4051e33c6fb0ce5cb4f61af47e71b9b7e69209b 6889dac9a153cbca3e8ac693bf458e339a45150613270a231feb4833afd4e709176df5760c0ee8fef7c7967ff6e519b86428ce955b34ffaeb37355d6c90a9403afbd7c7c757871f2414bcb3e82218239d365364a48ed0007fd8e6463a10f250c9a1307d40da9bda121807022003a299d6379603b3afac763652118d33065940df9d83dd2bdffbaac90875d5847a721bc0969787a004b6d4ae661ea28ab59e00b6ff32603583a3e786e7e9b0b7c88b0c638d9db5902ca8ddfc0a60ab10e9b8106c499f1ad03580bda9956daf27f3ed3e1dc592fd2883f1ea4256b53ae285deb0ddaeded56110766dc8d5240d5e1b31b1baa870555f18b89f586dc3e710a08130cb2a9030eec9538ec79b38b63bb427e44458d6113c5a0314b6f0790b94173100568c0c3a36a2d9e0f7f3e2b44c8c9ec75fafa7a961bc34fe445017174f3aabb0460ecb1952ad0d3ce9b929077b11c20624c112bb1293713953418508c140097053f40c143df72789a5f68b669068c4bfaa63dbb7988635387454a8978e74ca30a9a094599cba91606f67978bd9f4406092ecceba36f1bb24be0602261bfbecc0dd5a8cbaa3a9929e4ccf46d4cecdfefcda094662373b9c4adeda647799f7ce102ab664f51e7a21ffee3eff304cb135ad3b4ff30772d9fc77a5f6c27efe016de093e8c04b20b14b0379abc52c334905aa5f606738ecea9c3810db538fefc2f8d068cc6a94330ab26565a67c92cb1efb4f8972b78c44e511775c3b12b596d60940d05c03f2b4598325d05ce1c00ccf9d71b3abb02ad2d3d84639f02e37fd3fc9a098ac0f9e5d4faf477ad1902e0db7e191e844fae8b500925d61a28411a21f8020160ae39ba10d5ea7d98e2fc6bc483a63779f1409ec95f652d71c6024f2974f003f391c87a1f3148a6c3de2129c2b41a3554b66f075cf29615451f0f0df4a96d06666a43a4cf1573adcdce72278a887e6121722835e51463f1764c26aed8095e050d25f69643990db253a871252dcc79cac498dd8888cae7c78561bc46cf504406eb0fd08db173fbb68bf4ab09edf2835742433a167adeacc48b438e412ffd710467854c7fd36ebdadf3c1696dbea333f412a39100a123ae2f3d1006e015c8370089d3475497c51937c903049e12578d12a674c66653f02049c6bf487e08a6fb094bb940bb055e1e457ad1c6ecd0d8f3b284c1337de379088fa4e3dd6450f8cd002152e3518310e61db62fa29ba1f07788a2366a52a47f824c4f2412548258810c03d0dd464701d0805b3d609eb735bb6014ce374b8ba6234cf30073ebf4db5a0af945df0f5b3a39f6593621f94c3cbf6196349986baa7e50e60f67cb8465a7e0978c958d6edf0109c7466b1c937d884c9000d9e296bb6451bfacf2f2a1e762d05e1d523a68bf3fece5d453191310f721fa937502ec41ac452f376bb599e06f90c04f05b5104ab5447f68699b424ddefb9de2480defaa181419ec79c14362e9702477078d5b108922927e6778f2e1c681b441830eecd61642ecd31d54e4dbb1f0a6e16126048f6b482b258900ea36457f31e351bf5da1766d2b6f87299c922e20d00954308922671b4c1b8dc8601604d3e822ed6910fb7b97929976f29445b5f067e45fc8cc05210e9754b5323feae933b2990b1847acbdf253ab7ccf38b8076052c75afd746bc6d1ba51ad637dafa6b808e3fcffbf52c195658dc528d5ebf450c6b2149036af555e9de7e48c1736dd7266abae4821b7c189a76ddbd135168b307725cdb81c464b7959a333b5248f0f70ad4279dc7ffe073d819882c90fb5ff3047f64940080bd0f37f7079570b0982c027e538615eedd8a2da527a62ed18cb00642468567889fe4da2645f60e39e94bf71edca5ca26760d731d0e3fdb9f0caa07f81c1572a7146415118c1c937c0f8b69b64c8d484d49333686ad248465e7c303845374a3eea39c58e729144cdd04f9a13b4f43a24ad722ff8b83163f2af5750843a599f44e3d608cfb2c32e779a65e8e37154c2705c80cdd5ea009e2bb0e810102bbb2ed9c019fb0ae789ccd7ce0c71b2ec1e9bf0187c4f156fa123a6dfc510c6b4490e1feee628073c30f525cb689735d1ee99c9e18e4f92318adb91aad510ff99c57098506af189fea1b8036aa126d87000e9442990237037b782b4d0cf207c6470bcf40b0f37fde3da91401b53e94ec7bd65e3e5e5abd9225ce414703f60de54e742ab266b07c59a02a1b3f19a3d91467cc3e1e2950326dd7ea4bcd006d04fddef54cca11c03beb434d51b938b38a2573477e9688143b4a1d7dcc13ced005caf6aa273e0cd2658052e09b936ad0a576be98d8bc8a4aed37e15965adb88b08e73e4b2a61f1f5575c17f11004529faef897855d5f62f14d42c717200649b705a99234f3fcb87520f491cda1e684a5d1c54328c793bd542cf90a6901f3c77100e94ce2a6a298e9dfebb4f26b48599925aef4d3a9a69e149838f0215aaf0af503756218546d585a6be0eaa5e96231668f6bcbaa10d3f9217f90982a8f0e879909158b161715b0ab2ec6f62178eece677ebad0e1c512f4d1945405424d446fc2091325a260a3ef6b7c2cb0d9e6c2e5f4c77f21753bca5ce23e1f371a62d80a8c04732c5192426aad8e304d74d0ecb47039c2666487546ea8726f97903619114e0701ef144dc7a615f20b3e5cd3b499eae269e17f06c16194b90b22a54bfa1a9b0099631218ed3bd0877fd8d3b9d23ef1bb82fe526f541c6ff80c29ea0d885048071864998c1f7595b04b2c52ae9cb5010e654c0f0281eeb8e6493aaf1a5ad1a90d2521d1e3d02e64cbf57058525b039c923d66123699132372c1c92f7dbbfa130a19d50e67fa7170a369c31fd41b93c58802c838c31376b8e17cbc197c61a66806234e6b2ec42bc4598fa429ee4123af54b4539e5f7f59cc9133bf5d58fd90850785f7c41df458f69ae0c90a7548ec63a750ffb8a14ccab080a8aa4e40cbb68004734b05a1625086cd129b0baff004f5414ce4cfbc310914871589de960baf7703fb5c2931c5de9153ad1c6a0fa292c55ec09a42591ca70d2ec96fbfa8f4ead30198fe5fa011f94faf3f13a371f9b8a92a0f7dcc0c236a154f6706d93fa7409f0b6f601b081d3be0af9761f8b22c1305544037ac28b6548b5150c08283fad1e70c89876630a6ecf77e4330f9aeecd7d85ce35fb2534a663151e138750f7b91810cf03004ea2d24ae0c26d7cbd21865b48127a1a8149c24d93e386a1e7a38749f0b275bad38fab7ac4c53ceed6914778c169cfbb45bff7ba985ba4c7151c884ee01ebe4a4030e8e2bcc8eeba9c5f4d0298f286f4719419193cff92707617112fb0ec9290e214c88bf229b1efe80ec2003e28caeeb28c090b6754ed50e31aaee8309724ea9c37283284d9c81d5b5ed0dedc3ddd632da9e6e3667841ca4f91e436f0c936e8f5f0701c6336a10232723a5d5ab270856959d409aba3a90a13012768305a0e93d2f3c6a36265fc466c55acc69b1adb1e5573b8196b1ca34bcbd50177c07277d5e2f4992f1ddbf641f5935801e2b36cba0a5786db858a73b160ebc88ed05d6a0664015b8a817d1573e578a41ea825091e540fd7e61bf0e2aabe1de49880a88797aa96f446684003c5370f20fedcd9d842c0d9d17381db9ef1b5717944b070cf2764029568391c806b1152d1f8f6fdb85bb48122e5d49f62fb8bc38614d01208b3f459ca47a73172d70dca5f8f8a22421e7569a2481239ef4eee4931f260ba45fec4dae9b9511b4d61e30a31a7093c40294bb1f7e5ae8eb33535f6aa33e0e3bc359f451a38af80453508a4ce48e853f2b13e1b7f80eafb978e49f56d4aa0147d50e79f5483d13309b29aa41cf9f30af4995b5ad469a8042f0444fb85127048efe60fedefd4fb89f02bcc07d5899932410dfecac1d3d0fc305d5bb298a780d873e66c6d82ea9fb30616a5090ab6a1e85470b5d76cd4a89e33484c0ec59290c7a9aa0bffb74f0a08bdb9f104849b7a97c572ac58bdfb80063cb61eeca7b19024bf6dec2379f9713463f90ec9e865f5a0ea7270480c527c9f708242c628dbc09c0e9adb244f4b0d41c580222e8bb857f41c991c729c63c616c9ac78950a33f0becdb5427f265907b9b022acc3e3a0b77d9fd715731781722edb804b749a64b07e5a74358225d1ebb24dee3feb7ce1ad0ad330c35dcdc5384c25d04e89cf9320be1f667a3608625e635ad4473b9e5680b085d4f149b773285ffa22ff23395d90501429e60be4bd08d7a8278255f6456e01a1ebd6ca42ac467f83143cb74aedf0563056b7366ccfa4fe92aaadf1737d1a5d1c5e02b220d2b7a94fa027150fb020efea850f7d4c1c1920e5000c4d0d4f3b778a64c60e013b3aa082d1fd05d4f9002a7e298d0e493aeb43a98581ddc9b3f874330b55b59e635b16b1ba07b839f5c0fa865302780d6e8edf08c5e358b290c48c67f42454e2ec073cea8070c7539fd0a070db72ee884d7144a0fd7ee9eb85af01282640ebd3e23615cde1a9920580b09102c4933501bea54d462c820f499e7136a6bd0b7b1694e4ef36cc4061db1960ba1b720e262e7ee88823c89555e82a728cedb7ed3088ad9e7f7a1405f36df680826bcb2818c10344ee09a34a859b84d300c7e1a5311f8f35312609a31ae56e908aa1f847235ac2ad4e4ed093a392d2e201216f220471d3bdc10fbae1b497f5701d9f0379cce41f9a19ec9559be99199d1620bdf5999efd4c442604a1ed648490362180e83c2b276c4b2f1f3eff8ff920348dde3443c4bc7a43ac9980dca0b210bea7489a4e7dedcc352d8ef1f2277d5ff904c43502bdd209829f2a58dbc0f5f0e6a6287a31109a7097bd0b4ba9eff96473123c2569bf32bb834c718ff308ec90f567c11673c05fe6c3818e58d6622638da7477342ae92722c8a9f1c88363ecd0dba038fbced1ed73a9bc573853197067509a51c715181039337bcb381559a2e07857528813128936098ad7338065e359ce964438945d65351a34096d4bba2cd0260ffa61668a3306545f11e062d99ae7e87a1045595e33a0ed1cef83a89303e02664248c3130d284a120907bae41b40ce3d8a8af91bfd74b3d59207ba1220650145b6feb5133bdab66ba69e5e6f3bfdb67a199a04898ead8aa459b0f5070e030293007c984d69ed518e97854c13cbd5cbc122e135063576918a52e0d903f6d301269bf47b896ec2b51a1c9bf79d8a5745f6712b2d3b1a5331060522f1110d7102ad8dce08c2b1927b7c3f49dfaaeb7636ccc404c4fa9e580123d50e0d72bb0c0e44f2e80e9d9cb959b860eadfa120839dd1e6137f75e22eebddeff721fc0e95061f40afc45e91865f3483550f4ce6d5c678bfc045d76bc6bd8b5334174fe4310598409e1389844de0757a6a3d082a717f48bd575a6d89e952ab41a0a90e2ca90692b09ca0a61d8f6de743b32b9d89446cd87f1c8321ee015258631c8bc63b1f01a69861e2e57ab3c368f4ca26ee33a756ac226c23b3df50864f99f196413cc70a230289e1e9d5e41ee3adb38f0bf6972accb43f7afe5c71923cebd9d677d79103f48fc203d89adc663a14fb545a8e6537dbf0eb860c5db681dcb44a88bbafc5017dd9e3abc8de988ca94543d3c629e9fb489cb304dd84c7517b21301764cc71072cef61ecf0f335e43b830266160bff2cf84de01774a779b1905e6c1a291adc0b9c7dec9eecc7aad182de5d8e4adb3f681964f1bff67cd35aea364acc5395270195421468d7911f15001a28bf15648d3534d846d61269ef363b15b5d57d2e3f0286096e80e1496180b87326db38f5d0458d2a86bcd8cdbe8309a378f3ab6a750b64c6224a3a859653f0331f6404bda558fe15e3d6e12139b14de2e38532bc270a042afbc8df634457b3bff70d09a2683dcb9787376b6bfb0a95444b2d316eab0f5dd9eda291033531bdc9ca7c8f7ea2f19fde5d8a2b815ca94ba4595a9d2273039cc7423057f3c560afda043fd793acd7fd8721ee404094c0f1836e4f2993e507b37c8b076947613ecb22a6c2ca51fbc493495802540f692c92509ad0c6a3f00574bb15cfd1acc4e5c63515282b1b8df99e3a44921c8a031df1ea64378595bc09bf63a01a00985e459decdbe9313dc0acf919e02aa14af9fe2d88228fa81b39084cabe688d6fd02db819ad96a695e9c3df74ef46e45a7ff21bc7f86e1c8dc23084aae968319c7ec19688d52b1e4902a123af3c34c2407f8ad0174dbf2d63c8e07 true -check_ring_signature d4c105f170c412c9c3ec6b320eedb4256d27f13abd0c0959a6409c33a7b0b40c 8e5033d3955f38db3a9ad3d6b53020fd8df3912adc71ce0bdf4991bf38d9933f 1 5552b27f86e6086af2a8ed5ce91502b6571c8c3d037f4c5f8e4b1e1bc14c9b55 1f1073f937b2a87fdadf26851dfef2e850bbc32bb4c02234863a22290507f10081b698c6a5de5a06a4df724ee51e1ceb988d866a23eac72cffaf545c1db76c0a false -check_ring_signature 5e94da9239059270bd630435f0596bdc5da149f10989a1a118995072decbeb45 c953fbdd349f940efb64f02a57951eec23e8e9373398b62c11bb08ec230a6a88 10 63a22a0943feab372699eeebdc78a4430d15dec44556c075271c3eba0e1a9b92 a1b30a81ac3fabee6cc6714da7a7aa0e112914af3c0daeaf1dfe26a438d50f7c 630557e0a456cf1ef08c05b2ee197ad20c92f895a60709793c28039ba7a1eee4 966ffd6594b9583a93dd49d29c4bb761cc9a8ed2a7d85f38afecfd4d132366f7 8e9ccf4464cbbe4f96b9a5ef1618456f4480db8969e48b222d7f1e2e86c7fbaf bc7fa9c79d4cc0b548e8fed6fd0221fd49619bcacbc98385ae28b9f1d845a73d dd03bde038480bd98f78c7ac6e9088aa5bda88a046c8cd4103a306c9b3b3e46e a5d8013b35ffc7ae8faf4552bce5f06c8759aa608f68e89f4234b0b7a60a3a63 00e8593968a453bf3893e138cd6ae02459e1ae63f755cdb9ed74ac21871cf531 97f9db47b9fe1d4261839f6e92b28dd17919680f3503cca32b34e410bd49be95 7481474be7ba98a9e5e08a05e25906dba20c8b612918955158f1814b17a5950ea9f29bb0454052c23ba37f5d36e096cd8be5491e0be77ca88d1bb8567479a200ea5899cf47f71ab77c738afd582d75bc1abd63ca630ba49d86ecc99bd824d60867c4fb9468fc3387fcf1e4287aa12e508ea546ac4d5fd2baac4844438d708e024d4773698576fd000ebe88bdc4a8f600e8bf1d052ee5f9aefcc8f6c31fc9490a94dc4c877c2d44860ad5fe89c5eaf05f29eee1794ff6fef496222163d307810a062413d06d3af2b0fb4ad221a14cb5094ecedcd346a616210908a6dbe00bf009069049421981bf4ccddab3e3b7e09773304d287147dc1aea6cd440bbd2156d030a1ed4342ff476cae038916973bb3b5e0a6ad097bffbd867839c8d74efcb941e1ed394b89e72139c2fd7ff5a0e3fb256a7fcc2bf6b02edfe46106b53588e9f039c2bbd0ec12c5d9924694a7af9493f82d8c4e6ec193cc923ce5adae3e214ec028d69268f702bd049de4599eb8540cd7e4c898ecb1c1a473a27b7838795a9710c62f1c67e72086f34376986cc1087d07676d9692ce0a63a71f3509fca8961c501c1b74d3f1e5d21e2fcdf7f689cec3b45e63457f79ce96f12eb34d3c60beebf095150e76a685dd4bba14457ec6a1f860d8dd2f54f4c285eb56c94cc7a51f57d064c39cc8601147c1aa365a9b507cc0a55393a86746831ce28c6566fb322d46b02fe9573b6a86adc87d6234d68f62b2dc312beee25e1a705b30b3f1121b39f390573e18f152b2113d87c8f91b9e348908d410a06f38d2f42ae33dafeadf75fabe04944faed928d7d604e4c15526941d3b9d394e5e42d01d4826ef5138b4a67750c18e3065e3b08d32eac332f87e0ea07750c6881a4575f0a2eab352ca429777609 false -check_ring_signature cf22a4288841fdffb276b922f58c233686ca687e3ae1bfda9aefd0e4af13b95b b8a445d383a010bb0262b8bb401405bbdab6865213a2d64274e31921fa7e9007 7 e1dda32a98fb9f61f725ae516454ed0c7a5d1a5d05a21d3c7c112088e4209a60 b5c66e11c355df89a7193863a33d6e00a6a74f23a8f27b1f7b2e0b0a4122ddac 521d216ffebd12e170cb6d6ce7ba88f2623a51298fc9cbc4edbb385f80ac5d7e 90d4ba532fa63dc4056fc5ef07d8e973b5e8a6c1dd562f77206a706ef9ed261d 15152c5b8cef870aa155303a40d85d9c2210abf697f60ed6ac18aa1c197c2c18 41350b0d410accba7675be08907b060e7b2db826d87a8449dc69ac122ed83757 e8ec2496623d5c2bdafe53b98a174c35269029ab71347bd42832593d0f69c5e9 f8238d5df39c9980f0fffbe1c0793f93324ed10328409cd4b0b65f4eb0f2ea0cb35dc98ac70a484d6c944e414e5c5313fd5e776c43e5e4f6b62fd9633aa39001b7e6ad3e4ca0b233ae43659105b330db474051e1ab00351f687c1de39674a8006bfceca4a552a810efd6380b9698db349b708ab1d596541177bb612d8f4c0f06cc9bc85ea4ac83c6baf9213c12f97a494cb4bc25056fad298d94d829c2f2880ab095a61ca92124789ada76dfb89f9023a919a35b9708fbaf6eabc719e49fd00c929830225d798638bc1fa799e978230529b4a5e66cffc9bed7b5972d373b7d0eaf710ef8f2dac3ade2e2fd37c9b6ed350211ec125e5d834e91c97af85ad5d402fb392db75849d692e1fcaef0b30710983a8226675ec76e42dd31a54b98dd120a1a854f6ec3ac95229b8a8fb8904481c4558c6102c3d23cafa6ca96cc1a77c309142a4e8bfe9f595419449cc22fcfd5f23cbc9d9f2c7def4aa66e43537e650503a984edac138c7088e8c2dfb45c4523b08b84fea7bc2c184ec34dcbada530ed081f7443144bae074b1be4ed3ad98a81458f3b90db5eb7c9d18f8fc87bc5120f0acba393df32c090c49138787612232a1014b9223ca0f79a6caba04b9952853b0f true -check_ring_signature c2e25fe2cc6bc53e5719f7b129e0c174a325181cc0a4324aeb0b2432b17fa777 e1875647b29aea6a6e532a74058e7baa1a3b3f085ce5177a0ef38dab25189b6a 3 21fb774312481ccca02fd73be76ed07941f00dba4b8faa3f79e573591bddb03a 44b7e0a142bd4d39b9be4abdb11eed4f8083fdc1d490312afda6c27715b7ec12 c0ac47cbf428543762456e4bd6f983ce490f9d748f2711c73457175556e01ac2 ac7c11a5dd43361c815e02f442a0d916c7ee97687e4315abcd3150676c08a50756a842593a730dade54837fbb0001511818ca6a9bbc1677562f5fa280a0b0c0b2903167d517291655e26e5bfe4e87b0839e3055b8a6b149b32c27c20e4f08e0bf4e99373a31550c13975a5f850624f093a97103e4f599cfe9ef2f3c6ba288907b3ee8f11a0c0bf9df8d46dd66806411e27036c8d3484c5ad3279f3ecac363205331db2c8510f068fdfe255e98dde575e68ec253d5c545b621d4aa55b22e0ae0b false -check_ring_signature 9a9f29d9764601a1976ed690e4daad16d6bab01d1ffefe6d2227919c830da905 10788c097b8b7b2692bb23b60cfce638aeee3ced44b2d51354640825b73a26ae 6 c496d4f5e7f48caabf2d51c13825e6b6bf81a31800a5ce2166a0584e4f63773c e8cd9b00f9291e0f4af694f44b363bdd60619d7fb2117231fcabc968419b7f5b 8cd801caa74e30b906dc7e4bf75b45e5a64ddffeee14e7012404c5d08a2d71bc 4b7e8bd6c83e7814c220412a20d21820d7709156c100dff39842235823ca5c7a b5fd230fbb6419b4b4eb04a94f7a8ac706c82be88931a4081c4d193731bf0b01 51693d056db8e10da648a946eb5810aaf0a33336c69d08d43992332e31a15d63 c163b579ab5cb3fe6aaecf387981d552e44adcd95faedcbc24be9e0bd856790d0e73e6e3d209503c0d723e44d446d486a35ba9ff6f45d1d7b85e8c500d770808f76215c08fd42f65b69b150c8ef5f21de2f7e1167bd35ea0f8588de361a6830a4e776156e0797dec6532b7bd748a9842f0e4c0fb03d4b25aaf77a9fa943e2a07f78e421a141406001f7eba479b0b219cfba5a860cfed46b54f1121ea4cd35e7fe0b5fdc8a5ac97409cb57bc59152488b9f3918c8ee4862de96547d4e52b78a08fb7bb4c6f10c54dc48c664dd2b95590984b7d2b721b8efee84412b50312205004d4e8534280808c876d87a4360dfc4c7ae8ea4c0922324c932a1868544a1cf022f51aa440ab8edb7e86e61ee71efb3e83c198f15d4a08d5ab88fd2b92badb20845958e5b3182fb1d7c844112be0acc563c236e34bffbdabf1a4bc5e2f0d14a07ec6be045d67ee0f40a14eccebb4277c1fd1809748c9b2f9850350d95d07c88ee69fbaaf6749a7607363db840a33ac0df13f10e50d541c1a5143ea69de0c3280a false -check_ring_signature 86d39d08934de5275d5937a8da8a9aa1023297da33369c97e42af3c70a5de38b ae3566f4f027faea2d4f60e5b505c0f51d40ff36578f7f347090513772a3d789 125 cb601b0d8add3193d35116d916983337a393519a2d9f3e3ae68865d69dcdadcd d8745433ac42fe6d636e16cc59b460f915678b02cec4b1ec96dd2dd4b93da13b 218658b3d516ae9c1ef04a7cc82b088935b97f894dc1a633053decf6cbb01e07 4f922039120657fe5077ffb8350c60349bdf1ab4c25e95fe9674e13cd0648284 f2c80e7832e3215d4c8a235c248e51a9ff2be54a278b33466ea054bb59b60b78 de2996d3b54c0785b00b7e9dfd659b2dc12ce0f447f8023f2fa4d219d4873c9c 8f324e0902028e58e102cb92dbc6d11d4125bb1099d87773f22a2d570c81ef9d f6a3f95a8e12dc1c2bbf6607365c4799414f60b4678bf03ff9803e4a3096a06a 254aac8d0da8118425791f1bbb1ebe58dcc6f7f066bb6e714f5287a17da52480 50172afa359110f7b953bed8e7fdf789c5ef1ada5fad0b19759c80e2a529cae9 15531f51fb9703c67ac3e28bc0b688262ead1d39c17f10f4eb019c6db17a0182 4b8467f0b016a72b7ecc1b3177433377652eb6afef059365111ec891087db320 76fb6a71fc511b8333c42558ae3a044460b7796cc8d79b1c8b630fe259671149 32c4cc3ed43553ea6ab4170a9e29dec0bf296cd9533e46e5c5b6a7b2c61e5e6b a9e77dd87a6d4632803c7408f59eaabf77dff137e39e5d2c35fb79ad242ed105 8f69307cbff1e3e7465dbf8562343cde20d0edf25621c625ce792c1fbc5a4774 a9d9c193e4c2a8acaa2edd0370c9e451ddbc73d739b23f4dd2c065a5c7538aca f4e911f99b8922d629cdb72093a17ebb32e819085c51c9f1bd73bd8d986859a3 844e0639c3013e5a84bb0e13e5f49c8dd259166103010a5fe1d1b1ecde3d748c fd5edb2deaf9f8661b4c585fb3ab965df32536abf06152a7c57810409bdc8925 7c8ffb049e5bba75fbc22f80bf29d7cf767d73b991f2a7f670399b1398aeac9a 44329b99509a2707421bd6e597c932d02b110453a1b8bdc8142bd57d2d695b5c d977b073517895b12575a2b51449b427768c3ff9dab0a9f2cf8d4f0f23d5bf43 922e0bd12f3b7ee6b5793cfc621a64f4e6370d976195926ccccca4d647e64fc8 e135a5fc74f884e82b3f8eed01042a0ffe05e6a9aaaba475fd0f36ff513e2d49 9fe7a762681ca74ce54f50f04befe9dc88ecf373fd964fff1b29006ec19c1b88 22519b20c58bd46963c13cc3c29b3d43c9505456fe8942706fb58261fa9e736a 34742b31d2fe802acc12da22fd5eee6fd877a344848dcd0e9cf4e31982a4c280 444957069ad6b21985cf5638384402e2ad353e6bb5551ce080a0e25c46b1151e 89b4966ff5f3fb8c8229b4f7667ee83de6b0308a6487d4f2bd994fec42e44b7f 7cbd6d0937aba071fc158a1b88f27ad58aa17be5a7e10a5dc2e0041611d07175 5a48fb6dbcc4ffcc34500b6f8f2c75e85d6588c4085ff22717e7fcd3f2b8ac42 e632a527359b47f67562496e6bd6f33f2145e820f017b27da63314e5489c2005 7a5a1be41339b0a12a99f2e133f9197d23166dcc84422b96af0fd56150ab5ab6 2b7ccd34ab05313b2af444288c2431fd7303b7c8ea89d82173234b9890a3e7cc 5cdb4b528c9532c3f00f0484038f5da79945826977616dd4012ea095b6148dbd e27caf8012dac3e74c43cbe63fece20fb3dc1af26ad95a7b53100904b665fcd6 ae9af46c1d4105d34afc4192921f762c350fdf2f876d0a4e30cf9dbb69026d91 ac810eaeb3eac90614fd1a2234f16586df1d53b736cbb01a80d759b2fe38c5e0 01a98923a751ab3b696cba42724d7918095d35e7225681b30465a8f5d345aa75 aa4687b953759d7a020e53db210257eeedb2c6e9fda3b4dbdb03afbdc764c982 945c43d4409f669c5a64e5bb6cd70bb2f6289cd92eadef862765c73cbbbf194b 13def0d7c91a59647f3a7271bab60a8ca48b8d79db09e6d585633e6c518d4040 d16b28d8eb859c5943c1232e1f0e25bc9b638cd52a1e91158e903bacce77e8cb e61ba298da201a2c065f87f04af4c1fdc9610efc88cf134e0b1da911efb3095b 3dab3e8113eb194ae122c2c33a0c6ae2e94de71bc774a7ecb49cd24b30476ea8 3350ac6d721fccc4c7f1612ebccbd5a760a2eabd13185a4d4afe82ea7dfbdca0 2ea7d347844882ff332712fae23bc18b5529c73ade0c41b8298c9c6610541dc8 be28673a110af8f203c038f59816236911586490d58998921f89335427693393 eaec0368105cda8d59573576876792f3f045e489d56e003b879d91a5d5c48092 290bffb5b72b8eaee4874c866f5d44b701190a40ba3d8af8fde24b48116395e6 29d3e8d621ec5efbf576a0c02199e82d450437ebadb2178e306c0ca550ed00a0 74b291b89703fe61ee1b20827804ee17b7a97f06562eaf08a4568485ca2fa2b7 7abba5eae297db81f56b60d8986d91461a94fd13dfdabf91e98c72e1c052fdd2 b8d9193b4e6b3c1583fbcc418a69240880150c5899cdd96f87677e824ba7b07d 00f4d39cd430ae2fc8a07c97d8a514afb4f80fc98852df2e0f1b6eb1018bd251 a75001e93d0776cad0ebdd9d560339ab5aae11d7e80e7812eda1a88bfafeb964 6a392f139d13913692b39ac83ea1899639530b296624aecaf4f6452fd67a7b56 93aa2fe7007e976453a89b96b5ca4cb363b7161c8df30510025163ba238dc715 64e08aa7902cc3841a9a6d53708008370dad987d0e0c9340644f3bd64c5c31ae 94d5dcb86c67e4c9feb7f0b121010c502dc3325c77782e94a9bfe0ceb46be527 5c40d37ffb070b9f3472b24a5c2a4db994cee82a7829b8b6d53342ab92823a2c 3c750623235aea0713d5349e1371e2877d1dadac31b123dd38350738eb62a703 5cad4ffc7091d79ed9f80afe9519c95ef97bce1df22cc715b40e9ba31882cb80 bb2716cb6d53e18659805685faf712ee344d78eb9f82b26a1069d3ee95d8ed3c ec0356d2328f905d029740b18a65239b2546d7ee66c513a1a87ce53e4b64929d a46ca9c90e22e3d067e068c4fd8e5be6baf9450b01878e2a066eaf5b76f16522 04301f4975e71879f3e03e7e1125ebd347928f9d4d8cb87d186f49cab793fe97 d8a2755c1a25fe73981c65a72b29181be89e5e931596a196cab152ab21b8249f 6a887e8c7f570772f5dc0555d821a2a357dde392a559ee4a54ac57b2edd60108 07e389f307076a2f3803fed2b0ddee27ffe2986d4f852758716827f373863ea5 5b145fa86b5a4d2490ed4371bd46b5cdebdf8b737a1252a185b4381eaff2ae82 b8be6a5e9d5e89509aef165c05e69e544e02c5994e5517dc9c694402d9ff18f1 c1210a28e0c1080bcd66ac4521d88477c90e426cfe8df5d47d896cace7880afb fd85e671f1d33aad1732cd095cc33b39bc911c1166d5dff2a28d8f27ceab5617 6e1067e1522a590b5b5d68b0c9b55693163339c2d3f8280dbc3bcd8a8611b9fb 5173a45dc19f0d6f1605da31b618390134be837eb9e5efcf51a143aa5a09225f 6bd6e7b151464209baf8f5a8a9628ec5f0175a5fb0e918a7a945949ef78a6531 333183787db0732dad6219bbd3a04d7d827ac5b8bbc7325c572b5072120ff98e c9f824f6d3396fc3f8a1755e0261c50b835a5866ee967aa80579d24b5b4ca2d0 58a4998ccff845f2101b60b9f2f6bbaf67aa06e56b2545bb987d6f781c5b3371 9d3ab9210e91c0fc16433641ee84703b535af88a0fc5688ff238aa5cee3be6ed 2b2b08dc7e17b2ff25a96d8a04df519ea0daf9a7cca9e0bf0b643ea409f4e8a2 a6c2aa7c2c3c68ab08ad4fb6f7b222a408c4d6836328bb42fc77d2258cb500fe f9a4989c61fcc3aa0cb95cd9ca2708a95914a9d2e74ee00d335b1fbf31bcd9d8 0b28d7a6a6a44f59dbf33c890029c747cf0fc971e3146d7f48e1b0c4a45c637f 2b64cbc9fb5dbd49d6be1039455bb04863964dbad68c68a01cc0832743b2a747 0574b167a5af294387c3b4923d4bf33e586af8723f3dc455ab5f5bd8a732aac8 290f6d4404e533de44ff960137181b8f322f5142dd0e786235f7b813ae9af323 9a25df56c36e5652ac15dbc18cae691400959d457d2945b9bb7822c2e1df25cb d493273f99165820c5af9aa459395e104a58cf09fa99e8a60bf7bb07246182fa 7c8d3a6bea445d9e2d5915f945f15166835736f908095d1a3adee29af7cc5eb8 ac2b376462f70189f2483f2be558972da528928a4c3ad6992259cddea93b8537 1652f1182aecf43e1ddeecc93512c7f3427ca97168ad5002ba39b2c05873df26 5d950fdfb4502c404637b7588367c350a352fecc984c8c96f41cda872a0f55a4 a63db6715456005c1a4be308b80314be19425e78b1d1b56d013ea9139c4bd044 e60b16d04da04a098a029cac31be04bbaf97ede8ea9d6282d2c428f8fd324acc fa1a99b145fcf7ab4124437759d2fcb6ee2302bbf0d05de0d2cc9cefe5f3c1a7 ea23f77ef73f9c4961c13597420e9a59b02dac0534e7ce698d190bf243512d5b f152fc39322ae2f1f8ac6d23830bd373d39b4e53fba499fb4d82a61c88fa5f3b b77a6b7fe80a8363c8c6d20eca298a4801061aabe1368c8360a333ef22e780a4 1942f15096d00144430e4c0946846acbfedec54bbcba5d36472653d6798e89e8 7f74c386c3ca74f4213e085be7ba323a8ad9713c0b46c2185bb20dbf8e88b3ac aadddef8032ffb8bfd9be2ba44311319c94cf7ba3c1bc14b6f8c32731801ece8 657c8bd709e306f5290bfbe78b6129b57cadf32ed0edc699282314d6f06d34eb 6220f93597ca15efb3728a31a34b36c9986f97de311293f521bb02870ef4ce5a f1ea004b5d5cc9bdbc7241a094c6ddd003753bfef014d9dbb78c533bccecd733 9c3428e7f0c880e8c1816ee42b1a775d28f1c7e1dfaf95e3ac935cb56b19bc2a 5a5381f96cdb48d1197ffb884922f1c6160cfcc67edb88168bbd340e6766cd4f 92e43f64046f3fd38e5696d8198dba26c3665d9bdabdc5c95dc97f6e167929fa e2451a25e6cf25ed9c5f000c9ce0456a04b256a8dff2249d6b7eec51746b67c5 2fdf03f129f0c607488d99c6387dc9348bc2576ce78c8017a43d83c4944e46b6 c52ee6f5c7d25f90d3d50f575766d150cd3bfe449f9ab46fbbcdd90a50176d20 b6d92e5c807b4c73414b91e56d5404e4bbfe795000c0497741299b0fa6defde6 d12dd3324c30ec3c88a00fced434bd47826f6a70ef50828e0cd6672c65358ebf 41249ae693a8e503762bc1754f2cfbfc9f61fb26ac15afca0e699437d94e97c6 7b3c1bce2f93b6b7a4d2498d1720d6514a377f4d9f861e939dfffe441f5d3f4d a5aea003fdb9805f07621d57d31dd33d33486a6a41e99cf5bc22e3afa1bb31c2 fbc788c102753d651f5e0abda6985e8494074dd7f087f97e5c46893ec793becd 1ca99530fe832197b63bf18b517147599c0d05ef52c87c5903bd684c6f994c76 458e391baa68ba002d0f981ebcd760623fa15f7231d2e3d40d042d71edd91207 24c8cbe827bbdf732b6d8e0c6910f243560b73e44c3afebd115d2ac4a2a851e0 dd0437f6558b6c1fd99b80c0e286839bbe3a95dbc342abe4d441b728a8038eba f691313bedad5d87339ea5b149d1df20774d044e40b6fca00d700cf7073bb92a ffb4986677121530ae9605237087ac8458d1735327dba33be045ad931247237c b0877e410867104fbfdd1c66d5c62341a96fb8b848d699621de7ff6a75d83a0734c39530c4962a55dc341f8aaa31cda646d52fba333e76b4c32729e8521d2b0c57efaeee9409dd5e6b78625429295634f4deb9cfe6dd04e69106eadf7f971002779845c9b3c216385801a7fed5ce6ed443dd311636e59fa7fe6b53c97e9ba103a7b398a30e55bd6d3de346190d71cfd027bdf9f74ea8f89ce25b2ade4cd6fe053bea57a9c02c813e9bdb69c9e3a615e7a1367a3074312db2d6d200968633dc0c86148431eac12176bafd6ae1c91b30822b8a425791416994d51be579ece60b0cf36bdbb863d0750caf3db938e38edc6e424c21b668c92330d6dba920c0491206550e3ca62505d2d09ffbcd4b1f2f0f03a14c01f30638cb296b8e4f9452012b08406f51c60519c84720a91e4e5d58272d40da079c4eef0c00df8b703cf784d70247cacfce1d96ff029927e0ed8375dc2e2da05325950abae2cbc125e258735306e99b17707ed9e8fa699f9cedec8809589b7408506f72ba29cdcd866ac8826e048d22899b8462ffe868ba7b29bb4acade3bdbc9e4abe99e3c027eeaa4e2764f04411a3772e69b75bdacf265b861681e14ff65f0ebf17a691c06d1db1a69e6650d12d359ba6087209922999100f7d7478fc69a124898b98428e8c06dd0d661a601170b2f676272666bf09c30c45cec1d558ca4072dd72685c34f946cff584b9f0717f01ef8a0b6a417618e07837a73d3149c157f78a5867bb8fc31e55618ce6904aceb9bbc0f0b527206632d3e3b92486d3805572d9436a0639ac02efd5eb0840b1324da74cfd7dc0805976c9f20d172c1bdd1d99b83aa348d8e5e3eeb14091901d3a4a096ac1682216b9569c506b762e7f8cad4ac267699077796b306e659c6080957d4f86048a4cc8aa7398a48ffbb13b18001904ae2374b0e63fdda88be940261d1fefc2984c782ca0157aec83db89df5743560f7d399f5e9f081b8ac5d890b549a17d5ac2de567c42ea1087e883ab4c27fdc852aff31f6ccb22dea0e8ead0601c086c154d2a4061fd196a1a9924be8b1204b9a472c37dd7f68ab0b1185740f3794a1bcf591f0ff8d9b067b5ac1913fb28c41b8a4e15178959208473cc6b70ce3a61fb36518f1c0a9fc88ab76243fdc52753be09b91371b6291297298503102fb75cb50fcac92dad29e8c644f662a3029944b1c33e8835c8a0e704173e8ca054f9b00c0a179a12e3f8d98989861b2168d2902d8d58c3d0b0e8886a128e49509372ab5f6d187ee976551d38e36e06d73da243e4747920220e02f24aff130ac066bac3bae4d876fbd7c4d8ace76473776acaa0afab1599c4ccf5481a90f541c0fad829445ad19915642087b44f3e13f61c93149fac0e22494dc8a1431d1314000a9b70584f383a39f85e850b86bf45e08fb63b2b4ee5714fd1aaf058d96fe9a013843c005372eb88a886491816824bb3749f85fc1939e6f390cdfdc13e140c20a4d49b79defd6e9033e97570fce43758aecc9b66d0bf739ed8db06b277300f902b12d7494060dce442f9623a5236ba544c974b6453b0009656feb41f876519303c48e909bf46a84011483ff2e306e8810ce2cbca1a5fbc6c286bd219f41d08c0fe182ae2d674e4a4bc9a2f231f581449fd86e0e95f3aada7bbf9ba7310d4ca70394b1a69dffe95ba10284496b04b4fbe8bf4d43d919534d62484d26e07b7ffc07283f39032deebdf91a90b4183340c13f64f9b8bb2440750b8aa674fba31a580998a166e2a9e34577f5d90dbdf44882bf0898e85a96e276e7c79705574e22e4034331d1c6ec92dd20fdeb76ebb1f064fed3e7597946c59bd7539a49677525980793a4e840b8a51b66c5ff2533e98eba621a08ed51112f81445784e7412772d004f6bf20dfa21173395810fc1e17a49849bdfa3e96e1508936b2b359639be77600a2637f98ec04b916e6318dda307046727f0a56e102bc5c587d389a0a09e5900ca845610e1467c0355fab4d70de81a5a85c780865709aa20d4b9dc056be24cb04330534a43d7e5dd4b303ab7d65711cceb4952d778a78e9656f0b0cf3b3c8ca041d76a93c761b397f9d7d6a291d3980f326bf5aa6d71c88fa24c1953e258b860ac5ca325fa8bb385f1576a809a08ba7bcd520d642168270f143b07c0dbad9bb04fdefe5346e5dc2ee3db372173ae1382d81d6c21b794d88c79b4395d890fcfb0d6a56d74b7a33f8d95a06241f99fe132d837967d8924a2ea606ebf4e8f365d90bdf04078c76d25bea7af5a9cf175c7705a7578bac0296a397d91b1b588b9ba506c4c3cdc45e97034f14fb06b36cdff8141a25b245690d77fb68104527d49fca04b3301516f29b9749efcf9f565f275a7b4b27885bb1db2813df4d2e7e1fd5be0dd00b0312619a633b92850eef6cdd6cddf2cc8dc75aab0b01e1552bb8eed89705ad4d4ae4fef75bf29aeda161eb418c022f44783a246125de4f52fc8eb015300aa17b2e278a3e76fd2bb98772d9a0673b981db3a00558d422b57ec330e90cf10e72eee35566e82ad9830d5c888733a2945c95c4fcf59e4e8cd021143b342a16073e8d3f54121d8eee8b1e8de7b55e6ced27ca65dd89493afa50b71b7d53844d011c2d6ece8841e4ed08514c565f5b00f6856ebb0603886e7bc58baf4b3315e00cfa471dbdc04cddc180f84446bcfefa664527779c4f8a6ce77b62a2ae6ab4aa0c9695c9df28b28bb8a598f6b64c12c871df6266454d1ebb8dcb83c54a116a8c01b57baaa2dd74369e6c8ac170276b3e603c95c120f0224cd81746760c06dbbd01898e18651ce43d9a1a18278c7c7a41478167307b7536ac95603aed9c72fbf705bbebe4be376393f89881e83fcdd8b00f873bbe021b0b25a5ad118daedf168502841af68c3f9b69a974db44621e09c76898c12ffda7aaa8ef58616addb3e34c098429fb403a45efa807776facd82dad579995b470b97867246a459877bd99c50a09761b6cd833def63affce40482e4756af6261732beceacf7ac0fc19f4b87b0caccc09bbb6b9a6b3dcd206b68c31902d5006d02e02a4e90688e2e61cfa1e350886b6929973eea7af88e59d82da218bd5048bf75e19586c670dcc6143fad2f204becda1b53e6cabd323bc39905d7ceaa841e690d4034c445f55ea2c2fbc576008b54c8f593af8858d650ff1041b88bcf405ff514fddd6383f9cedbd68eb887203274fea86357b945221e033bb4a0c81a21b04a552260892290fca8d90a3c0cf03ebad26ec4da69e9dd9570f19f6bee95b5331924f755ffc7c04a33eb698ae3d09bbb88d41f226f57d74e6fbedabe66737be8ef026f1f45f6a97a31bd76df6cc05cb9783a367d281d68de39deba744be40872581bd953b92c5d42e4fdb376bdb054b9b3484788c2ac2d17fdcb15a8f403c42a2ce2427622fe9fdfb0d5962c933036f6c6b4baa40db7ff59823c17e78fd0789c1cf564164373616f089e8709cfd0e692a06fda04859c6f700a2ae1cc747a10ce474fe39d31aeb330047073685ae006df8035c5627f7fdefd679a6667e95bad43029ea511e4f1fd5cb1d2ccfb56707ab4128e4edafdb16eae30892f02304760d5b497eb49da4b1c201b6803024e00be16d0ae04a5d40eb8cf8cf0be8272cb1098828c0d60e5f4e3134adc373c3f00309aa1ffc02bca0370ee491b9d17aeaa554075ff1a06a920dfd6ec75ba4c28f040f3b202c2fbf1d47f8fb4a764f370dd7e18bbf91a58aeb52cab4adbfd059e509c2a72d0f37724da22d3b80473ae908f50faa4b11fa9a0bca737bc56c6a88080240159895ee36e395e025c261003f1d9728c8e1d7f40a39e1e71d8bc3d1e5850a72960a4ddfeba620687d6187cd513464fd1a9587a600148825035c0fec11e70ed3d3261d0e689f18a57514d5029e328c6866fc7428a4105aec1a0ee9019ef0038d1417cfa724d6b439cdf08490b17b723948442e84e44f620bba17b35a9a2704a720b4edebefaaa3bff9470775991876f52fc337e227c1916ed0ad4317671104ac46e7f9f981b12ef624e0643b2824046f31d62a53a8df943d69a5bdc94b0007ac61f1436841a7b1a69b9a37499d5f5d5e6406bdea2cf1bfa700e9d8f6658006cca3e2bc3d1513d029e96f27c74c5a4a8937bdfa9721d565853000d0b22c7a09333a0e8369dfe5d2b2b0ad48d979cd710c62a0e591621c62a067779710930b0ba91afac37fd4523a2e648a393513e4cd9d33130bfb9d45510d21f88f35efb50f3e36f7c9917cba49d2122132e5ff450cc8401061e72a68cb004eb5e4628e4101c01b4115d6a4816db3424895ebe282c2e5f8359032e7f6cbee466caf9cd8190590861e22115acfd44689fc23db08ba2c31f5d4c574d68aaff7d1f45ab7e1930009459481491e0d7419dc3af8b2ec0287c0b2dc5a9e9ea170463144943b67a20f27addceeb5896e140959f3894331a011ca13f9c55602afca0ab9c6a8838a7d058fc60c61c4068710f327232f876bb12ae3d72a6a5d1366d4aa53d356d8eb6e097a7044b3b2926272b168ddda90895a7ffaadcc168531291716af5142fb1d8f018c1c20c746321d5d960006a259e872bfebe57b0f1e1f6d4676c22624197f810399a9832d3f326ac317461573c5d712e4bc20b7c5f895b45d1b29af13bd9c080208650d6b1750c01f2e08e96f0a828522d2f3747e06327f041bb90ddd304ef20221f2900e71a196b0a707a82223fe39ca22cdc53ecca54bdf0370966de3671400af69198b31ade364487242cc6244094d882f66d25379d09b9d7d623bd56e140aa36aa8fa7ef023c8cf6e546e6168e05befe22f11a23736ac3aeb1901bf7c700d8f2244465b143804cfc6285bbb644e8818f99207159578861197f84b2fad340ccead44f885cb2fe9c7ce5456ddabba77fd8a39ac215b90423b0193f51f3b6e03e581e2ea0a896a085bb7f7094b771c9bee2a6671c550b371fcb06648f7ae3a055914e01b7d4bccb4877393c371800a09776753985a562d59fde264bfe4f84a0d513bb71ddc8b6df9a6d934ac3c5696d1089238964dbcd36e626b151b9cbc940109fb721b036dda07391685b4456f069c33662e6ab91c5c9240d790de6dbb78083665ef005f30295e09a2d3efb708e53acb4959cda3252e84a99e80dfa233ab068cbbd15bbd55f70937655948c368434d84309ade395fa9930ee38b0eeb47a9032621f11165cab0c43386b1fb478ff0cb06ec511f997eebec018bb99efadb3f09c9a76212614cd4cca053817e09cffb219b995dceaa05377a13c68799fb1a04039e48570f86bf604e7360aa280a5e477041393e28970ea3a748001b1d2e802606ae76f7756a678b394d96a5f1a18f6f5a286ffec180fa590c643aec136b8f890b9427a2a8e212f64f8a2c4ba1e9d0ea2aee1743a3726a2e30ca244cffc50bac09f8648f75f132e666898ae6f89e6531569b47cfe77532bb93d47096f88abd740cfbcb110cb3cd37f6b2ed2202340786231c75a4f14c3200c430d35d00cb0fe60d00ba4ff7e4d106937ae68bd2051d625a75b85f7d2f4766ee1a7bef6997485d0c7ebe9b264e4c4a9b1b411a8e998f2834d61c45ad49823af381777471f4bbfd09bd63b8720ace3d453b181815aeb47ff40e0b4d96e42c5a4399ae4f4cca1c040a85b59e550ccb661939fc3d9f796d84ee351657c6d327f2e6e0f95135059fa209bc0ba2499951eccfa93cdc510e451f557d454bcda129eac1ae12a7f5c4198809f421301ec3026526a0ca590513f6f55681964e1622a87875b7835b33e296a80ebd0155634e6967baa32b1b55fa0f4fc4138251a97f47078b8c1afe185a2e5b0f4b0a0f0453e43da116cd9633b9107871d8eff136e3d7fa3daab0534c81fd36002c299f2be0f94a8d1db88c0e208dfa539113b8ae2a3ceb3b952557bf7e22c009aca74d9cd742fae557c8460dfb25b1faf603fa75da6d61ac36b2518d72c85f0a9a361d017a74a7ca7b617716f3a11dee1837a9673a3c110f3d2f13f36d8cda0927e3c6ad49486333cae0408389643c7c39753a9f0b4db7bb3d64938429969200e7e2173d28f15a899bfedcff87f67a0ce24e23bf68f81f5f3295ef2f0feb2a0670ada855c2329079da47aa57dcdb45536941ef446b66239959ee588ac631d902489672a909d52515eedab2ddbe69d72fee7af1519a253cb9be19a7f53d39dd0632637f420b8decf3b9062e55369e8b378b9ad52dd83bd3db0f373cd4fc85fa0014191dd5cfed20e3e738750860ad1beb42782c768ebe932b2b1d08eb0589e00c410741319505e170723d72dcc7e94a6e14908868c8681ebdfd2b7a2a0f672603b1f76a2acee4a79ca20d5d94ce9b960b43e62807dcf913b66f30f2f0fbde6b0ef3ab866b4b41c1aa2764197f41ba99033b0d2cb24b10a0c6de54e290a24320094471a036210b321d9eaaca19638833dbb710a1448e2f81018506062a2cec3c0e4f36099a14a61050624b6d614e906b2352a29be8c54361e92a33712e227cf90578f8d18dbb282200e202ea3080f51c5157c21a1f1ca6db47855508ad76eb83089c5021601b9884404c84dbc04b08a5e87adf666ffb05d01a76276285a57b3d065a864efe24c8ffad9f57af1ced040c13f61d13addeff7d5934b6023522fe4105ad26f54aec10068e6dd212967dd1aa4d4b594ccd89e5d71d8006cde3efd5e00acf9e155c2d54ba7f6e8951ebe69489e8084a465f37073d9172feefd04dc5e2092186af700e2e5468f88e3ea887dc14b7b1f2965bb315a05d5830e18bf19cfe01a37fd5e3eec97e18e04c530a89b3dd61d65e22cf7978129e019329dc7d30640425d3b0801e94615130ce8bf497d93d9eeceb8a68a957ca3285d6e6d277502c0de4785ff75b8472b34aaf0d90c8ea0303e041c048f37e1ae6df7b2ed16a2c7309f465534465569ae4727be4b9a0081736699a083df3cec7247261edb9b4943109b3f6991d97c54c18153035ade431bada32f4720b4851a35b4b704f85a614fc042766151dd0fa48df2d75a4045bb46e3c2e05fa1cf424888759a905712e515b096783c75df96a6ae71b6d9d916cbb1310e3e95e1b3c0c84e1aa3e908c7cb053006f0dce8bf7c7ba49fd2fbf3dd45e109db10df42971175203cf9eb02e678389018e197bc27cf37cf492a82f69842657ee3369dda9c2e229ea3fe774512af37c06f5f825df39e1c3ed66a47846c8739f744f3098cd97e58106646e9bc4053b8000929015bca91dd05a2c31a4592b681b6a8468c1c0b700568e8c5cf2c2c0c88e0af3f14c7d7e302dff43eb0d264c1535fcff44df8b8f968a33fbeba8741401aa02ba20b0df4ae72c5366ed1701cf3cf52398355bb1319e379228e78fc3bb91f80e2eb3221ef3d2ff91e70be36f09f5303553e0e97242669a4a59af144e4b0ed10269bc303352bff01435bad22229e97bb53e9cb7594d4ed3b76293f71f4ddc3f04305a8ccd83f5b82cb3e7a118803342712c75a0fb159cd0c8296cd59e5316e3097b9c3e55746ec54bf35deaf3030fb1280b719142a034046fa539fd6bae3c4303867a1798e569456335499f91146caa56705d6b803d9ad8474bb59978667d8a0fe53a923556cce6ece30f3be0d7a6ddeb6588121129ec83cff5000b44d459ec014d8217e885f4794e2adcdfa955b247c4a27972e42c07e64985dfe3850f44a3017eb74eb29809dec337acbd48b285cae95b8e848dab20e9646cc49a253242590f41bd75060cc6a9e95808be096632cac90c09af6a50a707203001e9f674abeb020c4936769d32fa2f23eb90fae22ac5217b65411a780cce9f4907d57cdd0c470b2fdcf538203663c7ffb9890ab5781624a68607a032ca37525a68ba134c25210d2a1d3bc2244e2cbc19ec18cdf04e04cf3d48dbd1cdaaecf8ba7359be975a6c00c5fd6728e5f312d44aff2dc6928d6f9ff685bd89ae67293222625914c3bbb70e436c28c1b40f8985466d7dc9640738c66a7526c15f4f98a5fe8fb58d79004f0cd3c8cc1ea540c77dbe35b6a57ac187aa1dc370256b541731eadaca054b94a202302787d13bdcff05ca3d3eeaa81cb55edc53e2ade101eb90db6f566be5ddc8070e377bd5b20a253fc80ce0385267b2a7e030a6c376b74f871ca3b14141677f02f6e9e5815c9421ff9bee68198fc130fdb2c6f8e9d635e56b5cb86ae2c86af208afe3b5fae8e73d844c887a71eba98ebaa61fccf67fcf331b0f1f65b740ba6202df83190486766d1d0bc8d42f8b875e966232553f2134289e3f35d614c99c3202e4a4f8b6a381af09a170c60c080beb53c542fed5be26a7ddccd10813a26042006532be9bd9afcc8ce5290cfaf2e16049358375ab65e8b2c66ba56ebe7adb5a0c2d49767371ae99f0a20a168960dcad2b8baaa75edffdbc05c8786181dbbfe10d306b9a4489980ad5ec07edea46b651e84f52201cc0a990057b493f12b752d70cedbaa5fb5b41336bda30f282d5e91e529c94f6eded6ee1f11f03dd60ffd7ae05ff794fd985e1cb80d5ee348fbd545ff6b25fd1c0601ec1e9623c25b2b1d7500437d365ec108d1ba57d17c2765f3d0c8b798e2a4b94b5dc3b1be0e98cb6a2650dd46183469a8291e3e6d65e2798399856694d5652799d15a3e01daa8f1280550f9414589b0aac54fdc24a22dd7a8d65bc7c0ac2d756b5dc2f6a1eaf41ca28d40b31101fbdd21c8dacd9c8d9a3357a6930766c1e9337e0f915878c9d3d0e4e930c40e75156921e76396d01135e43f69ac58aac5ddf3631e4624da3d520eeed5f04c2ce5e1edba0c6aad30d59413643f0a94506c1e2fffcaf74e20898a9c66be107e84cfdd05e9c74628007e0d86b5b52d492a6fb45d18ab5fc65b170f55b00ae096ee76f0ceff2b435e50d387c0671bbeba133175bacad0833a0741c87db45c8067c11d3355124bbf7ad9c93012350d7023c22fbe3a443f628475acd0849588303e36f55c108cfbcf94d6cd78a2b8b1187e0b8f81f454706c26d9b9b9801a9ff0734d63c052ef7bb3d2616d048e803195b15ffb8e98a05281792e9acd37762e402ae30711f9d2bff704a19f38a1012aeb4876126156b0c504057553d5fda1e490b7870f2bce9ed0c0b905988da77c4c41cbd843c0e9d6c2e52e8b829dc4060b0022944529b5e9e133d81a88bf0e894460eed21922149b809ff24007f93a73c6e030be6594c494a5eb25cdedf79d0645d94567656435c591f018529c30b08484000a5bca7d8834088508155443c0426de72a80b2716a5651074f2f60a588ecdc7008405abe7e0b9ca906f23c382d3ff73cac70563f5cfada9a0eb37bab41b039a0d003eb6fa090d3dcbe4ec218fefa8fd05472422a7f7996168bc71132e77fec4034758e1352722f353728dd49be9521542b60ebeddaabb15e2d32c81f99c7e490d2282511498d706291f890cdc5f4bb37669c83589013703dfa755543f50186803b34b27d71b114c544b0b52da95702efe93d4ec7eb59bd46a63402527e60e86008457328667c542c4a9c21ba860517076ed3828c768deccc0e40d150e59c7f007feae4be7276ca17f9cdb94f9266117b5160d7e0d39a2cb20941f87f9222d36035bc1b543736aac84665b609135246f1b336747b0fbd730b036a3fe218dd732053d7a8ce75a70821bf15025c867dabbb617d76aba46c4b49e7a1b7189c93659043d6903c182fd24f2de869dc0844bdab84359a183b315a5648ae3c5c794157d01df15eeb818fe38c11f7bba59a5113785913288ea69d726851fdac3d7240bc8082da96680b75f4918051a6a46656dc16f8c76f5b65683d45e57d6b8aae047bb006bc1e8ef39efc7ad846d2900185373d992bcd9c413f20d40468d85b569fa5b0f9395b6b8b0f79ea0e6b5ba7d05d062da7c9ae68838d8ac24c7334dd57b5d94074ed3dda958570724f11c080169b24d0d3aa74112a8f93db319b42562470bbd0650d12c21381da5c764a9a9f1fd74c56869e59d3f2132a6de246f21b68150030f8c8762b1a6fc13c3805b94fef5f1a14e01c13c985e1c8b57a34e099a8bd020074fe7337b7d558a6b2cc4210baf55cd7716a2f7bd5ea8932205a5e1aa71009f02ccee8748d200dc065576a7087f7334613161aea279bea49be9721e710dff9e0d62b055b083cd2ff005c614c73e6fe755b69ba4339758abe288a095bd2b61d70f8c62305582cc63aeb99d364cccbed3fef820a2001895966301b22f207e5fd9088c4d73f9d7cb361dfd500a47476df5c81b898cea1459ce6ad5053b34c66f510ec91043e8d4d0af8ea1cff8042303e7d5c5a4ac61da4ed6e3cf824da01bd29603563a64d462e7d7d04e4bde44751c09b4fa36bec1f4d8e9ff2355584bf20c0b0f1b97f6e7a2713cbaa46e17dde5b2031ad021e1d1a00cf06c55812e2bf80bad07c9071d511ad62c580a1bd27fdafb1a13b4fb7d1a55f6beed28a4f37cb9eeff02c3531be95c6a89b3b2593e91562bca639612d9d72c6c6b48b39dec12b13c6100026e88430b53548ff48512936d1a8c88ae00be8e221ff1dc2c3b5c49959c4d04cacd8dea39ce489c5a3098287f558db89d873d8660abe95c0682fede5dc3160015e0cd2097da5bd814d8ad5611cbcaa5e6907c03f6da63ead03b347d6846cf0eacca2e89cf9414daddb6ee8bd9575d1af1728f2bde76ec7b609244307cfd64023df621f8efcc38a078e6c9921e43c450a5a0b94d591ded2e492de9c0b8a54405f002b1d16c56b5b3483556e9dc700add3493c431d0a2a409924e4b64853be709927d08fdea482180580987ecd6abd3f86066499085c1080cbaef98999e7c3f0ce31a5663111b8ed681c38590ba50e6c0ad8a0f27fcdffe629ae481ece4a25b0bcc88ab64b8c236c69f5a7a09f27bc086b22774c93cd8543839d03108b230c90af7ca91c53507d7066fe6e011a33b07dd792d2c51203cff1931cf3a575019e6018cf18ff601c25a0c4829fc472b9bc81cd21ab9eec5a499ff5538cbc7ed10ba0a48d1bb0cd6b07ed5cb70ec465e230d4be25622e6e5d508cf264ec06cd86ba202eba8dbc5674ec900ce3b6d976559a5a5ac58df9a0b20f7ba654ccff7d4d2de0aab8bc50bff357c2e8815a8e0b337a35f47bc78dadc07f46fd0bb9f69debbf707943b2b545e493e52ee456886107b37eaf1cde77f75595c1106f2780f848fe40e12b22cb4b835cefd8bb4134d4c47939341bad03ec89f2f52356ca2e032c8630078637e5d50ad10aa40d9febcd49815cf429f63f043a9f4214d3c952fc282990ff93ff47994a7ab7dc567089c71bcfbbf33f9029d21425e02ce6c715f3d4c3202 true -check_ring_signature 9748649bdc06bdda34cb99cdd2fb934ae20cf8067d774fe7ee859430ee0b1465 e3ee7cc2d1887e202939e44bedb65350bd0c3363e56aa037bdd2f9ed3b0ea3c0 2 9e651a7358b06c531058b5968e1c44393a636198a08d5f4e0ff6a096f038a3f7 bf0718b0ce43d709c306bb8cf63b6740fc25dd2c6d2403083ddcaf81b31171c2 e96e691ba5d8acf14d0f89fe238c990c1f04fc159f8038a33bd37fc9a4fdd20eaf1fafd1e214ced98c6949c11f3077787ebdbdd53c5a861921eeca8ff1dd6205acd8a039180863ca987caa9d745d147f1e037f389396cf8ef42f6b32e943f100abcef7c7eb59afbcbe421bdd566222813fff7872b1f823816cdf6d7cdac43c04 false -check_ring_signature 7e38496288fdf3052d2eda465c396ef41a6fcc25092f51e0c5731cf1e0bd31dc 7829d434f5ed9b554fa8c445e283d3f08e0f04b844f5ddc3e60733eac16da6b0 2 0d590772243a35c34de913a5d41e7554f517fb3d6974889841b23c2ba1d695fb 6dff98e7d47656577f95c1cbb7819a8621c034eb3135a149a4b05862dc8fecc3 8e5c3fa03b10dbdc80e51b04376b9efd6b1d7d8e0d49db43146898013b1faf012fbe359a829971e44f11e92361afe127be68e259f159f90dad637b7acc578a03220400f6bdbbdb8681bb1a1538bea56153bf9677503e25bc719d9486908ca004fb89922a05d3fb2e6ced41905aa03b3e645d2c350dcadd3897d3b4abb15ff604 true -check_ring_signature 8a628fb172a7519fb2f0735b2ad1271c4f9e0d37a69bc30f87a6d25527b1cdcd 47571573a7de7ab06ea1902bdf231aff0f3f79948e50c476beb724be56f71ffe 2 f8267eec7777c81d2ca6889c85ec972b827e8d4140bb1cf7bb90c4f571a6e0f6 6ab09226cacb36b3856fe4c23b49ffdce13058231bcc48814ef26470f101ec75 58d71fddc0dffa74c0ea4193830388d959143f72d0c1ffcc7686a3b8782e5f04ac0e7a32612e2f0e710d2d7f03d0794d68094c35de781e9e2d7840c7ffb8c406e08c26af54a0ab47ed0cc808dbbf9480753369c06a9060a8860ee9c69834850ba32299a308c35db2d8a3bf26078c7ac43fcb1624146b6d5ffb5d580cb823310f false -check_ring_signature 037bf00d757c77751af74433830465f72d2fc4ff738df0df64b3700cbd55bc65 00f5e1a220ec7e4ebfa0337b682d88ce8898b725c7ba80c296092dfcfdac297b 21 6d875ad407903175520a2d4be18d41269329373f6bf5145de6e7ae867676f0fa e2d80fcbc5c97267c64079dafc234f59ffcb221eac810a32d63e2c38421d523b 8b28f71153af6867b8ce23cd48ae3549f05adce12e0a0beb45b96284a73a2dc6 7db0233f3012d9bce45724bd5d1161b2b4a2ded4801839ab4470ae9b49452e62 57301947789e3419e14ed90ca77487863c3d8da3af48721c73a9c17fe9f5fae4 e52d910af1cc36076c9aa2f8f153b2610606c334385ca9587b63469237e13c46 b45e173073678c66c11324ef518e7d7c8c41d44e4731132311446091cee9a665 408efc68392c263e48376c832d6bb56835a439c729fb0ec3697d14c7b8f107df abc7b9d4bc70eee4f14f0159819d035cb65ea254d86763e39c4e6d5af4552d70 33c95a04af506f2fd72c3a757b3b9ada3f3de8cc5720c38d61b576878fc834af 0753e0d1224ab2125cbf53ff269d3833d2b22ef59d4477e974fdf566d07e7ba5 b15fb699a24add8b04e29664b5e1e450a98f8ee4b847048d9324d464caaec34e bf948391a8680744a5c0e12688854625e1ac686649c6969b99c04492a4f7e505 9fb4e43907acbfd0e545956608e89fa1d7bb9e797640747edac8bab088271199 4fda0cd3825d3d9de6d65287d14ce8e42d292e03a6e7be37ca300409eb2e6e0f 0a9ddaaa766257d0a1e831d166755081c0f8900ba8d28034fbc6cfec56dff36a ff0a97cb7e09cfbe37d8fce796e947f2d7ba7678015544299446c76ea8971dc2 75d7d8a4ea0baaf46fbd1279961ef096c64752278f49d3ce790c83f42b6bf36c 53b7a1992b27c262d924d806e3ef14b0f46b5b71e81f8d5ba5e41567cb56b2ce b81a1510ee2d974f496ab74dfa2ed356a9c26d526d6121d8c27929b4274ce333 ee09fcd3c709c6d884169665f0c17365c9c39ed4b48d9c72724318d80fea78d4 4cc026f6c09a1fadb399d3b02f27cc48787a5f401efa725824b990a3d1d1f00dbe0ad2d36a51713eddd8dbcfd5b5fe52ac932e829a6e9205ce02837e4144a70eba812ef6ae514608638bd04a8fa0f3555e44e88cdaa0843d6402c518b6ecfc0ef45fea5ee2d1048062c7eb53cd91017b14109de3ea5de8d34ecbef648334ec0f0650dd829e990798c03a50ad1852eb84a92413e270158ea34fd0d1a51e512001a7b58f8d77d01ebc67af3a20ce4d589d024c51c349d1b87609640f374c5b8c07dbfd372b384abb1ec7bf1e3ec3c9406cde4b31864feaba9a962e06f5c4ae5501234546839b4b8a5da9fc5f09d5a655bc0725681253ecade8e92fa3ab19a60f05dcf7fe68889d38a72c79aa8fcb9b9dff6fb25fe9ea4271752746a0d9a669bb02df82e1ac6219909ec0a76deb3a229a3cae55d8653a0b4a9cfcb550e22cf0cc02e748d680888dcc79874e517240e3dc91baecf52fc24756f3b87e1fa7d0f0ff0ab90ac3c01c522c5bad2bd15511dea4bb63cfdbb5d80c2af18b0f0bb78244d70736c7b2d57b2bd30f347abc882b9cadfbf656f3173db5283d174ebb6330a5b30fb42fb3ba562063bdab366fbb46f47c7b8f5b073960ebe3e53c63d556f81b0c0eea6de9e913e2810bbad892969637a6c1dc6c770ee69febf176d375ae8bd2480d68a4cab44a3093207011626e7b578eee6dc53f97d3670b5d3cb4937c4253c4097d1e35cfb8ca9856738fdfd4ca4d08665014864a3002827922ea1c779c288309a50ad5743e88004b58256924c66c984a80ff76329fd89565b73323fa0e06a40269579e52aa68f92ddfddb9a11f4fab96f1c3b240300e9b77403c0bf2135b7c0c6f26ab7fded62b6e153f00574b76de8bd0d972bfc904f8241e840b4ee6477e020da99fc8b373c1b6a17bda2f75810e43857af1880e7827c30c79567a1ca854059901aeb65b685ac405c48aac8d67451a21ecf6f96996cc9e8f50e438909ea1081eb5247c2acdfb8bf7274f0d481e7e79860f7f60d4e602eff7039ee5eb362d02541fa0a5566d1cb638e4943abafc211c58e5d07f02b9f7120b709e98e2908d0f49375fa71838a697ba28838a18dd1aae9ad4129f1cf2504c9dd2d1808c7d2c084600ba1a4b6c37d25158d98ffb13c7bfed22b46db93b68dab72aa2dc97fa9a0bd447e2e17c3cb95f2b00fb2e9de12ef4ae1a85b21bd6280348114affe232000731640d5f08615870f83bf2de42bb0232e8a258fc2ceab6b047d541db7f6d45009b3b782aa62c1872afc597b3419f3f894e51efa0516d6ab99b1ee9fd63f9130d404085773c6565314037f7e7da58151f1d76a3ece7bfdc59b05cdd9c246ac201444fcea19fa992d9903b14cf6331cb00cc18287e831357c10c2a2b104da9450e752ad9d5c99d6d88c0af95f1722b9891f5044f783cc3ff70a9671ce067642b0a10ae7d71d375ea9c8c548d26494aed82cbbeae2435c4d83c88cd09048bcf96012bd5e927a7154c2134959dd55515c59c174011e3b11e39e123eeed8ba4281e0815a25e664909eda9fdf8bfbd8f2f9d56fcb4158cd8030994221077541e5e470ce2c07790274d8bbd961ea1cf0e72bffa286208d4c8a77350588c611378c1bb0192daa155848c318250990f1cb8b49638a01ecebd9361e0eeded32cfd9bad480175c700a09c51963b7a8af64b3006a97d2f8a55f8ea95c089d50b8664ebf7a20221934b16437237ed954a0b5a483646049e3bb5b6cb5bd7742bcc1ca0ca98420d1fd0cbcaf654a48ee2b7552919661451855e29b84fad7c4d02337f256754980bbf646eeb85cc694e3f8ca245682a6351c27959b48aacc46438801a26cf8f950003a83befdb1fef751ac61873667d3fcf47beb1bc0c5d1f062657588123e8c00e true -check_ring_signature 2b069eb451b00255b9b9317e0a36172c724bf84a87bf82dd9694f54dffe08e37 7741887964d63bd756b02086239c21fd3d5883ceb637c64521f8e161769aba92 2 a630a52ef7ed98dbb5995474fbeec3ff6942d40cd3a4793d5d2abb863c1841d2 be0b47fac9dca267e049fcbe5797358e31d44f9121a6f3b2a4de457f1932db9a 6e83ad4aba62d3eb10d69abd4136fefabafa589eec4aa7ef2fbb8969c7342d0d7b442f2f6ffeefc36badf05ac95af09a7a5990f8f12436655a3d9a81e38f9a0da24adbd390f7ec4d2b58dd62d9df9a03226326efcaf3e81a404181bd84658b0e0e380ec24951a602c54f8539ff1b841509d952c91a825eff67ddc0b37fc7b708 false -check_ring_signature 15c7f74bfdaeb388bee3e8118b6968ac84c4936e0dbf0c501de13290b1735ea4 5214c527981e7d304f088d3ed37ef7dbc975ea3b31f979011920596765469633 2 a2683ca083295ac4f4d7c3bfc5f0e460b229874b77501689f6631d3384bd41d6 238cc72c3e58bf4a37202a82bf9d5a3e78f117d5904808852e7507d4754d6225 a69dacbd40119819f4bfc519e2b68a06dbe719225e9a58005a561151ed5b880f99dc55a1c7e3807309d517d1a7255d322cc915abb09c23f739ad08d53d5d330783c386beffc1eb933b729b6868e728a63e896fd91b8e8a02e1a6396a0f2f4701566666f96960966fe423d5f1e07d84617317b6b0e8f521d5ad2b5fa33e1b5909 true -check_ring_signature ca45f298d4926005ba26367cf1773176f54fd1cfab65c7055ad577bef9d56f23 4a032e40669c0bed596a060a6b7313523ae161f5ebdc18b44f2ac3ab04e69f98 31 42db1c9fce36ff07682d3a837a9a1bf5ee7d997e10d4304f6488963dc4d663b1 9f77cdbefeee162ad63610f3a8afee5579bb5fb3c052342fd0adc19391e558bf 5854612db94426d8154ddd8d3606a8202cc415456f413c732c01067c15b2cc33 b3378704df6d8352f5348fc06f66f1a05612a315758f7bb2fb23ae50dd625be5 8135e04c3ac441a84eb4016f5cb159c93d5d755c33e5a03bdef43ce1867aad73 50bae002c867b1a104f5fd8fc1c746feb91af5ca18cc5ce4c6e75363485656d5 f866f014efaeaea45c4a1363cf5d8632cafd72e8afe2eefe1fc91af519c3ab50 490043f1315b845aad8f857a62228e6c333d9701ad833c2de0b76d7ada308498 72e851b7a435f2f12594e8ad1e4e76c604ad88c97b9ebe035867ef407a32daad 35786700a1469238cb6b1d64ed1b0ee4e9fa9ac65bc932863d7f4aa8d77bbac7 adff41177daab13d09d23d34c673fd541f12d5e60b8c765af7cdbe52ff1125fb aba4fc2c08a9f9081399ba001bf3445bd44375682f2a7c5d1aabe7d0a6b8b026 6ad0dbb447debf713a8b4adcdd37cb1268f2076797d790bbfb31f1c9238525b5 0725554b3b054b77c1d18f786926c5166244e5028e5fbedd2c8cc12e666afc25 64b44b1aa4c5a7762a6d3057f7727c8b538d36a0378c1596d6a44a23c8f7d93a 9129f403c21f581b8b3cd24378bbc0e18c79f37b5d8954706332ff6bf013e010 21108f0d511ebd2b37471988b685437710d28aafed73f76e4486b704a0e1c936 747b308367da3f0b1cff293e262ae4ec7987bb9d387b0d8788db96acd21d0051 a0c44fb43c408483067a5bfb1ffb4c2de524e81f02b3674b57d7e1ab0a4045dd 3a478e4629827bafc22eb4f6a7076d751e87e42885b17dd0d973a5fd26fa30e8 f9a3938ff955bb176c26d7303a8e71f203c907365115778323f3175f83870cb1 3d5937ca9c0f70f4225acc3538eed56e000bb0f9c99f0caf812e6cd4b829e23a e3274b199aaf8e7f6c2678f23e62c4b739cb6250e431652d962bd45b88e0ca5d 6019e99d5b2445f0d7108cdd983cff810b53b449aba145fe0852aed5e8ba33a3 60c730b994f15889c6475698c465f81f7e1a138b6f87a67c382d947143b5b0a0 a5a3468691ce2ec4fbb440491a1eb92cecea5ac1ae8d7d2df33771cbe1787719 633e40db347d50106b6aa9c525fde63619b072f98e32587cf89cc100855e309c a197bd6faa7d935fc3baab507daf98892591a24aae3a3ee0abd8ef62396b6df8 e2b317a5b5b4e52c476a6b7267c1e667004ba4285a7a6e8c7adae969f269519a a7b85bff8549a8c6e93fe459c4bd1b93c8cdb8835dca2adb07f67c502caa4c07 f9317e776ef4f404a796af337086474a6af23de4b9bc3c52008a243b1cdd3a60 1fe428a75ba86f1537197ae635ee45d59b1460b6e34d6aa48dea8413a105db01bca5e972d713f8743d14014e619fbe3498112f7d4d15fbcde7d40cab3c80260da1897280464f97348645f0d7c0f16c5646aff20201676602578bb036b79f5c0cd80e3addadff206f71b7a702dd22dc4ff8d2b1ebf442f1003c5213924a44940422ad5628f8fdc4fa3b8aa8fc13e2aaea17a96d6df143d79f884ce9add19c6903f055879f4feae51d7b958eab8bbcadee22d795518915e9b11fb328fcbefcfc0d0248474aa3874483222cc0a3d7929aea091a3825ff3626ee5afe86e535eab4008c472f9b3249e3707ff68d1b266a5c712058370f998737937086c72bfac3bc05dfb55309c0a84c345ed3b85d954fdf245c7bc46d239d2f68c65e58b52091de0b6197bf2b094cff044ab8fee48a5c7724c0141924d8634b246006ea49aea15105029ce349edc7d44ed2ff5305f765202fb6449eb474154739a19e540d4b4d1306be089d8d8d0885f38782855dd951d6b2a24024303f8f40237b930135e4e7b50b86c3af6f15807084969829be224b4b55e360c881e02bb964416bd6a04712bb081af10ecdb62ee06677c9823307d810c4314a5941132a7b30e8ca22843b212700d835dbf4c65a942afdfd9a160a8b46c876ae491d6ced0db2bd5a9af1cc7e990c447e920fcf75b3bb376c5c773bb4311220497ef662f6455b02d197ca5ff6320e5b35cd1acce95ae799e354fe4eff25d530670e72980a09fdb4a708b6cb3e050a788061dd5a466c861a3a577d008ef5ea92f9d9b51551a10fdddcc4abda35300f6bcf6b34ceb7134bc743b2a96f3f9560beea096954e1fb86e05ff414c02b780f305c2426b5d9718f67d98ac0948cf98dda7dd50ae8a219bd002319371364ef0c323b72068a2df05d4249c8f5615234437851ce7feed7a1b8109922e42599760c6f17fd029285ddb5ec76444d20ffe7bc3bef15bd3b5f4deed6f93619d6dd6e0c1e9a97661a7163cfbcfb9c734b977be9da5116e98e4f7d6316147e19a2c9d1062887f56fe73f824bed4bd4c5763036a4cd89159299396f99f92b55c9400ee704f310f65002a59f08539965cc2b179dee915f5396df3cc346f0c1e2f005a73208b5a9c3ef1a99559d8a8062969c6ab6dadc0469ec20c744207e946ae39c45f00a431b009f2253d195deddddaa6926331356fb3e65439da7788551fc27b063fd0ed8345ebd40010a6ffac992f72f672be022bef272fd278bc22e2cea40cdceee0344003821877cc86af6bb211b2f5ec2cc4a5d91f8dab79353db990e6c7fabc8004914c0432d16593424e1ed361208a3d6c9d1dfdba6ae16912f72e5ed117fae004c6a7ffefc7c317a39d507730cc546cf525d86fd1fad16845c0ba826ad28a301395f8022c9aef0f48d2b6c88c4ea14a17d4c7cebb836d5d995829cad077cbd030b8d65144afa3585d2646a5f86369fa8195bde29f2256748a3633aef502ec60680476e3200568fef833c317aca375355bc19d77085f8b9e27f37ec27762e9501e6aa56b5d5bc5ef10e5d54d1997ec9f5bf2d5dc323b84850d16a13c68abf500a0f76d50ea2b04121b71950bef837912033e7beff30730f1c5d5d1d466ef5490f6ec1a1b85d85c4dcb2a66466c6a3095713297639393423363e624d6edb8ec0075eb1d7f87daee702237d3fbca8b22c565d725727107fcb17869b430409fe750211fe8afd10c03c17251a2310124f73b631e4a6d6fad1e2cadda45982b68b3807dbee02d0ad20f48743338c1d6b87b17d178efdc55aba10433902aa752a3f190be4a8af06c4c1040fcff0fa091ee979635f41c8c77d103898e7325b001006fd064fc5ed2e45bfb90fa05319f55ac8a874d48d69781ea016ec9341ebf8f883190083997cd553757171ca6f2b93f4c85451086294c2ea655b89d56a0b3c3044d303d6512e9289281888ac747f2c8e48d3b64caf45f37aad423072bb0a673e08480c6e1f666581397aff160faba3cf10ccb84fff08ee496db1b2bcc7f7040c6ee20616c486671567c7402a03cf327ada19e1b9bd769691d6f7356b05c249420f5608c5c1f4f1e9ee191ff9d6b11c35108b8ddc92421ceaa76e6044679d43828c00036fb6c23a31fb944c0f701f540e3a0de6708d16ead63705018af37f5b652f730821e2fafd41ed71b50d530c0cd4ae24e8a854feb43fd7f667087b12e9bf5fac0996a1ec148492674351e58f3e887e519d66e2b225201fc06d36c5c302ad82b006c0470a9b4b01e2185a692456fd2db7926acae3777c710c47294f7db75f981c0ae816de751249fb33add370c34f55e2690b9234edfab4c43d5cda185a26faba04b7893f1e570404eef6e1f2bad9a1ed2b662e5f15b6b3970b704a2b2f9763b30e27231986ca5e92cf0ca428cc59bd85ab2a531f32f859d55a3711a985994e13049fc407fd1ef0ccb355bc450799a1a94e5af3342d6663ab1c939b6e44c13d4d097199cf03f74a870b37c31a7fe3d23c3fc196e5bd02e7cfa476307a655ec53905607cada225df91c756b5fb039fa9718edb699d70860668f9e0394fc1b6507b001aaa76bb9c54163dc2b739f0b6268e6e78a1482209c631b91edeebc71a27f70eecbf2e85b314a3816f90501f6b4643dc8b06e62b9c68c274f14aa30fde963307db6f7362b15cbcf8e0fe7039d91b3f4e18eead63b44842232749f6cdbb0d7f0f91720537a033c0710e7c5fff933de43083f96d398a16a8718496b99a0c7bfa01a5497d72ac5e0d7766d73e7c662b8f2744c9d7f4febde032dc26ed9e76ec2e0d false -check_ring_signature 27e042a1a4379b9d13601778acbbdf791b11d7ca0443f006c80012291921529f ce460b45648fba7bcbb2cb7a70838dbac0fc781cc26b1c05ff7d790bcfb7c8d7 12 2e2d0b17253e428a0f445f465129ec4cabd9367cb381e2572b870227881cbd7f 0b2eda04180be96ca0fcee00e750cf4aa4311ff4aceb0afecf58bf8cc1eb55af 9ab0c3b3d50fc76916abe4c232592d996f44915374893f9d16e2e1e9ab2a520e c1898b1a1ad791012403feeae85d7386aeacf4f956444273ac2f5b9c1d0c31c4 31ff05057d32fcd935d94e8d268ac701e4d2ca9f5a14d1165519128617fe420f 720ccce1583b06903a56d75a278fe34679beb1e773b3f80b00a0bd43cfbb74db ee9a22fe2fe55a582ffcf7632157c8797e7b64cb39d5b8415269fa91def68756 475a61de05fe1c5e7c08e280e466174ac7233c6a734db33a669d757d0c0c723b 14f722ce060af3d73fc167f189394301df31b93aa21cb1fbc268fccac1f7b030 e78997ea882cb8aacf9377fb3081a590ad3e84d3d5ba2f6957e2c8c50234da00 19b0551506734bc62b8516ae743d876cc6c6861450b2a600f6debd3fb54dbd38 0f509c1ce4513d91c9c63c92a19683290939bde4989fef8f692178f51d348299 d7e69a6f5073506bf05e3039631f4cf190271b88cf6b4d4bb4c642e593b5150d67acc94e7fbb70bd589762dd338310cef250e15581a4c5d63782b67b7237a501af8453d9dbee9e8c50f8ec8e947d4314204fde23b3cebf54e5f54643226bac045a9fed5d83214123ef362ad0a4571f4bc54cf86d58b5d99538e214b8ec98b50f454a77ff9e6f84670b6116523f6ed27e810d851d640a98629895f13308088307da28f412bceebbea7eef91cd4faccad586ef55b82d5c3977004573c60046ad053069c4ebfd91ad0a9453b2319fcc9061917341d106e339d44241e16dc59e3a0e43114eec55f028b2fc0e8dec629d8b51cb07a34d5ed787279804dd30e80d000d2ac1ea92139800e466996c1793a756663cd168226b30ef0019f8dc460b1a3907712f722e66319b18d06534bdd6d58008de2723f1136d63ed05f68bd34e46670748a46e637865221de1d4c53650cc577938588cb34e3f6a12cd056aba3c7cea09aa530ad5135b6e5021d346464e9120a2dec9262215bdad714a77fb8cd5ce3d075d6f13a42971fffa15612a538c12dbc5cd62dd2b18cfe66e8696c40c7650d4009f84dc3ccbfb2d9feb58253bd239fae92d7cdf5991d1416f7dd4a7b7b8328f000af93dee85efef36ca6fff37b41ae84709774c5fe2c141979dd0346a3f381a0378515bc608b0427968e71898756203846922712add1bfe294a2084e7e9dd1f09bd6603d9283ee357eda529938d2c6f218dec19028129ba7cf3c7b62849bdad0a669c9ff51dff775ed5aa0b362c802567e2291d924914cc6e72b9dcf6baffca0c19420c2cac3606eeff9628e2101a0a8129c993f2cc49f43828e8396827860d05d0f1cb9c4dd3d33ab93dc8e0468cfb665fa24762ce636347145943d3dc7ae60f327692daf39089caf86c1f7d51bcef01c478c81bb9bba6a76b7f8f6d3ac7450861f26f7eca3b68b3c7360f7c79d3cc2dd8637eaf152055961e12dd23144c610fa6c8cde895414d30fdf78a3e462510faafcffebc8143dfc6c13b51e08e448a076429cc0bc21e305c4d5470722fff07d97a85906d1f73b925c70bce7f66532602 true -check_ring_signature af81ed502e0cb82f846a9608b4a27dc204cc7ccce277607137ba71053eac8c34 8bd46ad85b910d53065063f8cb26d74977eac62fcaac37c1a9d59e1af88fdaa7 1 1ffa8871528b515bf5bdcf13a9f628b3ea1c0b0001796f9b7ff21794833ad73b af3a59a90f64042486781fcd1f7c1e45c7cdefaddd6a90f02d0c0e24ee98a10916375ad7f20c5430dcce32fa3bc47d8041657122fcb651956e1ffcafc65f9c09 false -check_ring_signature 8dd65c024807aa0ac55e08bc6a571cfc32f4b9cdc0232e7c50ac5931fc9395c3 fe37484ede51cf63a465ba87a2f0a5c39294967c8e89e36f3479ec5461d8981c 6 a5855f45cb4472eb373f82cc3ab483e369f4670ed1ee29113e9107301528bcfc 9aa23d891edce5faab0769741f31add0cdf5790b599608d56450d52434862791 5338c9df44e9c2c00e7e1d45e17b21a512203f7ca7701dc6b5e64a9f83f35f4b 715d53b7a60cc673fb14a994d1757eea9094dd5a7b8904978a8354368b52d970 05f94b6027f656149c318d96d67dff26ec1dc651608aad2237146a70a32d9ecb 3a0f77fcecd51871b41da11e652b251fbaa39335c5e3a5bdbb0c50302a36cf86 d98de43da08c2bb004472df48aad77ff7c5f7a157d42ee997cfc1e2e02fd7d01b3cd0bd2f9b74f204e4ac87cdcfeccfe4f030cf6dc12d5a3788b3b900a7c4407604a271f3ec7ec0298c086d68852ff30d42a28c86e3b32fa63086833cd985f09d2d65a009819dc6b7d0cc634a2a2265b43cc7aa0c663f80ccd4abc46ba6adf065f72810fcdcc54d8df94de643c672bdaa3d103a79f3de2c822bb502258468401a7f6333ed2642a4f2c77c32bd702bdc59a14143def655926e30735947b2b770550077f87eed853a12dd98d68005dce40430a39b3dbdb12fecb9717ec7dbbe50574134c524985ef6065c0451ecbe30679f7e00b375ff5105f21f46d6f1f38c00f0e0ec50d805b6727fd8515c1169b2ac03be0f399d8e39bc6ee271caf1b11f70be969d42692711c20f46d2198331fa001d93c55f366eed4e4d69865793d394908dd937461f21a056e4d6cb51d040a78b254bb8c363680e1381c80f7b12e178d0d17b3e7cc0bc86006b16b376980f483bf68e815c428050380637f8a9f679f4e03 false -check_ring_signature d0566b2bb7e09f4a227736bc3c55ab77bd8fd2252a6e7159463fa177e8eeb7f2 5b6fa9d42a69cd0b3579360eb79d9f81b8f0b2c5c015653c38d62dd246bdf18c 3 e51a17824833a5d67080e4b016391fa66fbf6837de255473d1b0d364fdda9904 351949fc168eae6076dd78cdc1db283649f50abbf76ed7aa120d55f4c0aa7def b224ee3e5c0ff61724bd85394605122dd90b012a0c315ca4511ff98b4842322b 177af74be7dd0bdf417e3f20d8b3da7fe62a7c0fd671e40d2175954505d33d075d5be12010ec4bcfb34eaa70d66b06a4fef6b9c005487ea623246498fed7c003f28b8b72e15b272ef7ba8d099a55862dcaad0f5e2b007453b20e76c07c27a60b2f489250d462d8c02b7fea886250024b70dbe78b2e78f8fd23fbe0b75e3e1c0c6a5c533443bb483999b96e1166fd98fe7ddf7adc1b7c711bade3e471cb0e7b08c6969361ed498654808d50c369be8855721b13817a36c12061ab750a2b36e505 false -check_ring_signature c8d8a6a8a6f4d9891df2d4c2ade986a89c3a16b66d6db6b129bc6b7bbcd09425 c27a1aa68930250cfa26f8514dc36ed969ada3e990b4bd10de9623af488d71f9 16 9ac82ac664b96df659522792d3e3f868b229a8d8a91446a14a210108e8df0eea 778724f26a88933e2eb92b71df9520e5b0c7e52e4bf36c1ac397900c7e59365c 72f01870b257fc310f117a8e5ac7221898325cd87754909e85ecb8596f4be3a8 356fa5a5b5c97256c4da227dc1882e1430410cfdf71d07fe9f35665a71f073ab 09f8e355f6fbc9f57257719a6bf0cd6bc6c73a43980b97393bd2f9ac0b0dde0e d1e58bf0b36eccc03d1c7b847ae14eebf68bdb4b742372f845ea2371994d2366 f1bba06ccbab68a30c67ddaee75916299ef030d136ca662862d69439ab7b507c e357465796ab4178252d5a396c38cc71009739e81102a74bd2b76c8e983714e5 e088727b9aaf98fff111eece48077c6123695e0748a22df3a3b7409a40da2060 3d2b002fe6048ec11a65b92c92f64f0dd5abb02cf7690739bfc56cbb3bbec5db 84eae0dd04659a5cb2e24e8d3ec464687b92c45a1362a8e9416ef7e1539ad5f3 8feb36b820e675e438a432298fc54fafe71e580c250d792a2d3adc5c20c7adc5 8127d388a00f3879845e7c6b35446f66196f9b74266055b98d301d790230110d 15275b67c91d390846b67ef568ebce0c3cd37a242334a0fd4f15fc73b46c1721 bc0a834786b1b9bd37c4d7a4ab6053f82041f130b98453a90ad1608dd72ca884 84737fe118d5d352a955bf87e77e9fe0a8d7d5c7f65699503c3f437505eb061c c2548420f4e45a7d421152ac5d2ab113ab73cc0e90dd605dba31c5590f0d020df625e8a1762b4d7e6a3e55fc8a3597d0fa24065496200328f8c219133f1d7601b43bedf480a824d011c019577ae4483af792d9b6066d4f513df461ae680fc807787d039c9eeb8009efa830ce5dd6a31256267150146c327768058b869708b60bb0523c9368550d44cf6598719ac58d98df76f1225f96ebaf4de2450fcf84fb0ec226444502f20a4623143bed7df162fbe02d9465a5ea51a316d5dd09738cf90a70218daacaa2c4e6dfd4f607da970a1994eb391b51779ca9ef72aaa5f1b4b10543ad1cb23e29ea7d8a5fc5d80ac1a4ecbd43d1773aaa5f3ed2c61d561be6f502f73294678509354ae797a8a1c972e4efe504dfd1d60121ded4cc94906ba3a5083638b862ab74faf48c7afd5faaa80408434f210d02ab12c0ff5636c70f3e9a087a47927c59c181e125b1b08ed8dd81ecea90ba634a1ba3caf7c8ece3dbae980fcb6537dbfb08e6288461874000288d301be54c075e60266717ecf83b68ce37034baa1e15ba979aa7530590ce9268454c0739a062af84e211827648664f2f9505c143ec71f45695782cccba9508ea8d25d96cc4f0fbae6236ec5b63e12287b601783247c9b2bb7233d9a15b6b5fe0f83ca99da74e37c1840aca9262755ffd4001cbe0f51df4b972d7d3b9bdb8e3367889335205455056f17ddaa53ba30313410c0d6caa80e2b982568f8e66715314bf13927eaa990d3f6d0f6532e5ab69b5b604f507456419e8ee42f4dba3b6eca6a3644c2ba6e959d1b7649f81dd03c8f32906237dd767f1f0d5452904c0a143a53acb0e1988adec8f3224bd3504e80c8c5cff5c3f7b8329cdbcd521de3ea3a7dd244dd6d0910661ca83e3ac87af9e006d1403cef166d6c57e542bc068495e4b9abcae1b90b27e8efdf6054fa2a8a50d74ae05f8a30e3e8acd66d5986e4876186068cb6cc4cd875c582c65b578fc7262738101d7b6440afbdb87cb6c96fa278b2623fa9149d1c68b9eedbcec994aa0f2e593002fceff36ef62b2daf6b1cd26de4c38cf1511663cc7d7a737d3137da489503d04e57bc40ab8de9eccdb8dcc3e3cb7723e6c9bb4176a1afa1cb7b9379d70eca70f39ead56dba6e325db9dc82d304b46da63840d7afc03a9ca7dd598314009d7c0989401888185a3ae4d6951ec9aaad774ef97b041ad1ec950c5fa4a86c4516230e90b97c01e5a915ce0c4bedd5effffb4924e827673d62bfa2e8e2af5b36ee000fce3596d066334b33096aa0e7f53be6a39c0daee818b04550a67c970012919f037fc48c45a35be8f4400fc1c94f440c9f87eb027367e4927f94b144717806d30161f576feb84967468fdeee87f8840539e86697621b033c7f45d48f89fb24fb0c1f3df2294022e0ea764c224cbd97c9337ba60588d73f9cc9d0c45c96c4d7ae00 false -check_ring_signature f4e967491f41eae534dd4c00511387ed6062937698fe1cd948816dd6242ea638 93f91110d4a294af82dc14b401882d8109b6dde3aead57c23425df8661592bbc 1 9d244cc9e49092352ce5b2bfc323177498acad0444019f9ef3035e47354e86c1 907ce805ff2639fc455260210fc6fae8b088d92a35c439eed1303acff65a640fa87d48b2a2330cf8dddd61624110d04a4f323504128a79847666aaf8a067d507 true -check_ring_signature 40954bd53f43360d23fe8fef527c61f7dbda22b318deaaa96e29d3453696aead 3e0acf9784fa5d133a19c53cb2f08f3f93e3472b0b2ea915ed55c914480e92ff 21 d7980b95d5705f77af5bbfdb04f438a1730ab17ff0336995147593c4796d5c02 d7607e635db14b146a10a78601b39d8fad53a1951c4139483d3cf445cfaad41d 010b5edabdf37b63888446d4a84c6643d9c9b86d38f0c7b51d2f1fb495e02a21 105f215155af80cc578bae9d87e134dfd7e075dfc7f9311ae152a6c013dc6891 507164715fea223368847856130e68cc9aa843c385f41f0eaeecc8169a199d17 98965121e9709dcb69dabcd96b0c27789b7a22db5dc24d79f2138f649f1bc9ce 6f570f7aa591cf8744acdb01fdad78c464fc2a1f920f39e90eee0b98c24bfc2f 9d9b22a8f437d4ba2c46f34b4f0da2d99b34262417fac8486cabf774c8902bd4 83ba6fc300221390ea83b60fb645ebecff42e09f986b5a521f9f05f5906af72b f17296be0175612de96e9cae382848873430257593fecc412fba1006dd93c61b 0ac6b2d849501623ad431520b74075db4cb952027986f06e9e39af799833f020 790ec13a166da35c6cca66de23b4b95a8887b1b2c001079d717a1d51d40bc847 4907aa1e46d173ede36d079db068d623f00f49fae7202290119c73ffb1fd2724 8b38205ed063945aca02dfeaa4e268134859e1ed69908c05551f63648e7be2dd 0a9928a0b948712ae095c7ed12f501c19dc51e88654bfdf8aac227f48b6e9cde a1b59addfd12676ce893881bfa43f30b0e5a85b811f8a9ae1e0634b606f3a252 7d7250cfc21e1541612d3f7855f144b0a0207e1e72d8df677b65190cf02e7ddf 5574114cf814016d0f7ad980a9e9b8685095cfc5260b99811f180a864cc3f9e9 ad3121f14e781a125ad7d5b572e6ca53b15cc05f930d7d78a5abd5829fb8c45f 4932d1c99917b6ebdd624ddc327fa93859880c46e618de70f317bc13249d9d10 8de48011bc78ad45174ad35ffae35d5346aca70444e3c4e68bc4e15e684e81c0 6b552edd6412cef464e6e2d61b74da4ad83eb54b2c7dbfd0544510d3ac42110a1abbd2224e277bdc8c81d426a4eba6614c1a9092bebfef7c8bf9026246dc480de9b2326ce4a6538ca7e601d4c8dd59f6529cef21da2482730294dea728913807cbcd7d4a7ed4fcac97a042ec1858351e6e4dd996533337f717dce3f7b3fa210b43073c39cf332b47f2135de21d6871406e9dfe44c6bf11a64d13140e7d97fb0989d4768c4e7232e2a814c81e1ff037bc5fb572f3f1844dadf0b2308c956f9503338e6d1c75b23b617793dfb5b4bba832781669759b7b16a657679ad061f9fa0878e019fa96b9f0b019e366f2b31137d818931a49d50ca96a1ca87f52af0be10ceb8c6284e5a3d697e99d348e2169714a92f4f5e833fd5921fff25227b3e0450016661b803c4c5948052f7b76612bcf90c44b5d0a86597ac0b3a398dfb3b1f1045061f5d6f912f8edae6cc250afc4c68de0eed6435733b71a87982567f6eef7055746eee4ceff0bd1300033e965fca228a22b0f0c1f382867e99f74e7fc885f04cb48aa5eae3dd71c964dc0e87e365deb277ffac1fa111d9a314ba01a574cc700b6c07d73de4478bd626f862f0eeddbda41cc907fa326b0fee0c382437c734f02cb8e11826e2a1aa2b9c997751e398a3b63043df47e05d28274549f4546ca2b075016120ef1faa15184939841daf330f8d49d0cd6e28ed53003f84bc5a1096706e56e35c40ef958db76eeb882fac3471164857ecc204b8d3c1b5250c3ce088c09b427b2df01e9f154595e65e7b71a4482020bc448632cadda248ca726f46a8604f17759792905a5e86449977c1538abe4f206c18f7b8afd8f29ab4fc4c96e0204e4e6825da691b35c883b23d6404009a8c79fdb5e9e5fcd6de7a45668787f29028ad3de2eea89ea820c35c46ae6734b062dad76874d2851f334798624b104160551c973dbe8170a47ca125f317bc6aa19bbb1e7f039979ef516372e914c758f0c5e4a66dc42f57f893749c1f35343faf4a0d63c908d142776e1c8c07718492ee140b5e426b260829be6d77e5e04b090f3850bb8d91ad8e13624ffaca8a5f0f20282a002c90042762ec085d2e6f87b4be1da99efc98123ea5d602805a35c5d5e08072804219b7c1f53a31869a45c0b3577142360e2f3ed1163d4922eafba49b003c7e9849a3bd949d6311c353e1cd511ad1b7573711e9e2e6cd51eab09c1004c003616a085ddf3c875e2e3a280c4a25da038019e66d84dede492e0b9a677ecbc0ce6d8460d0222a7aac1443e5e9d296121ba35e7e1bf87ac98f3c7a0b45511550e56078834db66a4e31563b8eb747f029e2fd1fecae6fbda6fc982558d92ea4a0b45df98ad6ccd8d9961e4219670e552f5c35503c775f5766a49dcd2283f36e306ba8e72020c76169fa65dc655024df07df7fb65276bf9c32069d963bf0716b904573ac39b1a3e780f1215c3bb9c275f8773ce209af2fa70f92672bf92aedb210f899a8b675b4d17333f6954ecbd28b66500a0f36208d6d7a08f0cdcc53980f4065d097a0c058c34620578128896ef142cf9d2b6002f1687aa07622ab599fccd05e280cd6aaf46dd34e3ecb9ab846ee8454021d9f55272c0ed0d7526b19eb36807cf60de5b25314e08bf96c65dcda9f821ad79d1da0f0fa3356ddd69afe4c416065d8c789412c1509d5b520c99a267b2d55142595c03e81cc3a2927468bab35d064aeb9a663d079efd645e32de8d96cc913d371c102c2b2480cb9d2be9c011b30509225fde496022df201dcf948fe79398175c824f85728f84b588ef73b8737f9d528862712adc72c7b38e8acfd22ba945a7d114fe0c6d7010201895cce8fea10b0abbb033e46cdcd8c12afd27b9ed882e1fba4626ee3e2e748a2245d02bcf9a03 false -check_ring_signature 310d6ae3c782043415f4b085bcc6e1bee6981da96a213fcac6658012112d8c38 4655f5072925f41d3ea72bf8485e8d0ad76bcc78a77797d7218cbc2eb9242d19 116 7807eaad662177a554d4203b800354f4bc3c8de88d359405b25ec6d01bf6acb8 243f963d2dbb7c90b2a482c3f50c64087612f1f6443888636c023cbbbd459ee0 75f3bac208e7e39e469115bd252522f0b51f79b815087fc5af3ac690b055fa10 3b440105b596a27f7f68e96372c4f23328321bba50f2b7ca8cf4f0f5f1573b65 426681962a10f5935bd863e9fa34d8cebaae83293282aabe0806a9ea891fabd6 2661411a67598906942a954f9d32314080a4ec8cff37d0747638209301e83f26 abdb73c4e6a14617ad25d8415ce6e85ba7a71f51809f005f38158e3b061018e7 0aa5045422b3eaa124bc0d1a5b2012097cecbe326de83ea10aee2ae88247e5a9 cf4a278c316a49dd8d725196420d8927c9127d946ef809d4e39639ff67fc7ffa 307ec0a8ca3988b3b4c19482de24f4f0c3ed306d9306e82446bd5bbab333e3f3 9b79060b6ad43fbe1aa1657c88e13528252b5483d52ba70a37c46075c1287a11 0ba5b1e5ed5efe1a38dd018ab70783efb16f8793105e77891b60a4ab5a8db11e 18c8de65a46d3d5787da3e24d6d1b3a08c3ac20dbfd725e60600ba509d57d15e b0e2a872213d7b5c988c76985384005c8350a6a5cffab786cb96a749dced5484 b0c69e8eef215af47d30263d683ebb1d5fb6a82bc2fb17c4e66cdc3b8d68dcbb 5f233d4312b43419693e83b49fc61c1e636437e5111d31c46523929c301ceb3e 88b4b7b34251b862c667575b8500261452c7ae2e3616500ffad9e28167fa8a3e 32b99a72e85973f02e42a86612266c844ec7d951190dca6109a134895a83560c ea79cab8e63834915f956676de6a6ee86e1a5aa54b4653c450b550342a31185b 6f7034b28634582c35e8eb78d023d701e07686499fd7992be02061624a81b41b dfff99a3046b052ee995b1fe204ff4f5848326fdfcf8c6cb621eddb52a576f26 fb50ecd29ff719550f2f7a8b361d1ea28b294c31718ca7e5568625bc217382f2 a1b967b74e9d0cccf61efecd2f7fcf031f497147ebe199b9ea9829bb6441da86 bba22d2526ff859190422ab652894ba8e01bbe7ea73ec0e05ed8193a3e282781 7e26df47de96672d968f523bb25f70c0529c71692d61936754834d8f3e805bde 196720d41c9b03b0bcaf3c4aa22575c92c9d96d9efa675ec9dc9204af95c7ff8 7c89452f01964e095a42fb57a6e984db6505f467941500b872198ff5443bbaf5 afca79a03977da951bcee8d0d1e352274394960f6467c9f36c6222474f86c24b 6499818b9a821095bd69a9a2a558563564bb8c7ae60fabdad58b492b20a2fe66 28eeadcfe2593a31f515623dda2d99b9b13c7152b1f03fc335529f9732a84062 d6483f0cc625144b6daefc9174b0deecc4b8defceef2676db19ca6e0a4e71fa6 2a3b68eaf64024e87c7a517da3d53e629c5d52e186f07bf71c0b5c0ca4ee3ac6 d0efe0979ba88efafcac1574b450c67287df9678013b9954235134f20a0991e8 b1ac8d5fdc7349d3256f9608e2ad44434e1518a4e029fb9835c8e96785ef1699 f23c61bf170b59f8226180aacd909d7f980bcfa82b7105ed8602cea24461c971 cab2fb00e4fd79f106d6f0691af0f4d6b366fcf0c43e7c13cdfd88a057c324d2 c1dbab0191310d3a2049d6811caebad3aedb0b03bf115a29397cbf257d9732ea 2cf4d6eeb523b8e50e39491a60d4aa1604014407418e69da6c89ef020650583c a73090ebe177943f78099836245426fefbdadfa897d0f16bccaf6a0c655db4ae aa68658636f6e9490744ab6d437bcacc8e341c3de4a637ce1711664f3230df5c b1dca726508f79e19eca642b16c45d91e5cc243a9da5699eaed5ce033f2e5bbc 064dce1f4218673c2dc870f68686d7edb2a4c2d1ac5653152fa0a4a8e4663439 dc04842faff17e231790b3e620e13cc7a8b9e9357a172a0ef313b541dbbcc8b8 88ff4293253d34d283c614a759ca0db49c49116be293aed9bd61c6c3791dfa50 144ad40d237041c33ec46697b71169fe7ecab10eb64b2f9e95295a3dd08da974 90fdfa1d1cbee0226380d54a42052d953b4d298b62200d10730cf5c1fdbe416d 3f4a39088e9eacba197cd2b04e7f256aac404d03932308d18272f157c9bd3e04 0099c85d8895d6d6f7862ee3a3e7aa347980c06c638f429ab1627729c7114d37 e02a9a6003077e2d27a1be9637a08e4326fc3c09fd1042f31b726bb85a399f34 b41f513bda48137483a891867bbe4a8b4ff7755d5a832b957aff7b53a4bcbd1e 52e94e34a84f923e1c64e4f9f59d28e40df1bd3de5ef9feb96f9630d446d3848 6f2e18e1bf048efe6fa42784616797a52df0ee8ef874f66b9dc22df70c7a8a48 9afadfb6c3891074a70c109008357405aab0246e17566bd719f5c3e28302e69c 7a37f7f5d26f1e0e2fb4737be23313434c0f8f38c3042c4c5bc513c2c9878457 fa19ff7448533b7cbc70328d1d4bc60f8a8e35e6b8e4262e63ced4881aefe307 2376d71eb07f42b6b43a14081d964b522b6a315cd42022c6b2a543e2af0880aa 5d6abf4d43a5e3cd12326e5138186ad88bfdf075cd875a0dc29aea2070c50af5 b5443d7f7944637ef8024751646ac3b245dbe0763f11ca3d985ff1b2119cf212 34aef0b5e69898f3e6befe6c1c478dfe0561b3192353dfc690475f3ead9aeecc d90b7c619b39df1778520430bbbd7de726d60349dbb8b168a07fbe3f30868a56 38b2e86d0d615080b457ae7e53688aecf57979af0d7f43d7b1f08600ceb8519b 8a5198b3e06abdc3ae23b6ab1ae6f124d1f482f7ccf5748ea08a383de008ec52 bb6db9c2ecf463a01ab6944f7564dfc59b5ff4926e5adfe2d61c796d6e1840be 8c0bdb356e786dafaad32e9ab1c9584637177ea339429a3eb0d54fc998a5a8d3 ea7b49409158cfe86ef7a25865fd690a2602b9834a401f654dabdfb725261a0a ab2f6f10735d3a88aae9bc15f86a2aa672d6998f7f44df84d3847910e8832fa2 7a2b36fce6aa5a36c597c04233dc1747ecd2b4a7eb821aae6364f95e49d21dfc af3b5d5721057979fd3fa0b8c7bf05a9f4bcde1d16b8bbb42d6fefc29297b296 8ce37871c5819daf2ac91d6110e85a2021d344d8749988c529a97b9efbe62461 60d6f0d40c7fdcce050bb9d243ac49108b51a77a692921981873811dc758401f 379e3960b1794be13fdde889a47bf76bce7d46b147e95e7169854e72282366b3 4ff8aaaeab595a10e0d64cee1c7c683b87996be35ef9d618929edc7a0f147e2f d53185e8d7fa7fbd8095e05bdc9e920678785d53c9a555a747eeddc4f17479ad f0a9a8e29a825e469180d85f1ad9fab70abb8eb2e3233f3d0b8338dcbae089c9 f64caa6ecdf7121b098a84782c67e17bba07873f33dc349ffa402f5c8d3cf5a8 cd1d783c1b3757c5fdebac0ecec5b658013b5ca5650273d697624241719ee73e 1f9f87a86a0a443e4b04c85df47de3d3667e0e310ba273f2e104c95de0240126 373359034e8df608e1c2d607df4038df5e200548c0eb1ac2e4839a7764c6b316 ddc74b8108db902ea7157a251d1f91082a5bc6d3d883bf6ce85bd7dc959c7df3 bb0909401335e0a0a323658b2291b713c2c5a1183400e926f632f1ff00dc8956 2e3d07dbcbd48a85e0a6b6eb26ac98c8f094c29b6ab4a1618edd72caf66a076c 4faa125f4afeb30bd7c8f5f124b9ab393c9290a572e7f2e4b8c3ec3bc0dbebc2 090f95fe51b6c80a830d645752ea65f93496d08a44baeb72abffa633d8f6bbed c5d01ae2b6e8e01d4bce71696c7177b2685abd5a29c5884ec7d049494cc3a963 5d80301404837b090ce9b13868b8cd9f15e741104d08e9bdfc62abb16c250391 8e729deb0c68f60ee34c08bd209f031a8505f939264bb50818b4b8f50cc3c7e0 bb871ea3855ea09edd134ed241ffcdbee7a5aef8625ed6ac3f0de23cea07ec1c de7afee7982ec30b0a54a105aa9e96e736e92a0b9db9ef6b5615d15ee975bb3d b37aca0b6fc23749908ed86d6a38833765d7ee741e6ed2177b507ec4e89a491c 1c46917bb4d35609485905b332053c34e2587a374640f48180581239c64e462f 4814d2b538a9f657c76e3223542fbeeed714054ff83d66339e2a674e471d60bb fff3d13262a472102c092d648e2bab278a6f6b1f337bf03d2929a005521cc1c8 dce34021cba4d96695699a962c02c861645877936ddd77445cc7314322f3db65 39922b7a3cf9921d3942567e98244be13d8883831f928ef4b8732cf1d1c7c7aa efc52e8d2734c19b3b592e47575cdbb2875c2ffd188ed8530bbd8b7892e1d6ea b2b8fde10a89a646596261c90b1591d895d2dbf38d7c9f1fb3c633107ad8090d 78070fd850d00f74cc4466ac6770ed783ed0357bce8d43c4296dfcab58aa3c11 c6037036aaa75a2074afddc9b8fb582cf2da23e68f26aa9feeed6f2cecd87cc8 32af5cdbe471cb2be8b9da434fd7b18f07a49385c25cd2e9f6ad8e09a33b6339 03204b77ed926205e2e5e63442a618b6fb009d6d73274a40ae69bb5efa760954 5b9375d7494ff5b1419c523a8d9650a80945d7be9c9b718ccbda6c4d4b506b92 555b6c2c1066f4fee1e7e49696bca4767e12ded606c41d0a80e6b04c1cb450c0 f6b5fb999795bfa7e11ce874dea099bde7f5e7dbbb00e3e9bac9c840e6f5e7be 11b69160c91fa243f82d80f47334352bb93a6b656ee7c12d2f3b53efa172c439 65f0da19b7c34afb952aa61249b1237dd4b7922623bc1ed9291cc04879981864 1a0b49e5fde43996062eb5aaf2689b6e34e90d9d70e99b7261e3ea79c4d1dbf4 a93716c541db323ba60066b9ab4b6b88aa874c7ce9486205da161046e02969d0 6e4541ce6762b2679ece8b983f278d6368343a8472afd197c2dec79436357edc e7894f77563b3259028de5a7fd38ffb888f8d19e87c2f400d53b737413bc6241 4e229b28f42ec0c1aba8d69401610ee13f2abd51bab8c8ecc6ce30b69beba5bb 187eabf0032d9a5d656a5ee1a853b8ee7d5775a2b406bf6950920ba6770a7498 d6ca4fe3dbb37e0b63ed5a1051e863697005560dd650afdfb0ee4a149a04af67 714a1d186fbae02fd71f45ca2dfda9c1b9d1c3981f8f6c3107dfc382564afc2d 91ef61ad2bdfe75385597e803b2aefed3b1131739364dfa4cc4c1aa3fb571ed4 2e6cd6e9491b1fcd10e8276706d342bd0c1f7ab2e1ab16402ebd10342ae46b2b 5417c80b1642b1a3b77a101636d314c82ea2f0a2375b275282e3d785c9001264 0d884c62f888cf0744a16359fd3ff998d85cab05e0b889981f70a1f55de0fe0c84256c8c44057d3fba3c46b3697439064c8b617dab08b187f8e8eddf478ac102ea9031513f90f2404b19c0348848ffe5b75a8f27330967511fbf794f23a39f08b54941aac405fd0b769b581c609176fed67fe8638f7b710fc1bb02e8bb230a02b60c5b7f249eca49489d4c9bc59e24f02d6d3514bbab93a0740da89fc8b2ef0721f9ffd83035b3298e933e1d36b1b7c76cf0742af8ddd7b2c2d1b9b11d5e3f0e936aca598f8bbe5191619a343349bd127f03b3d6e8a69f11dbab92b9a78c9a0a5cbdf5058846cdd162617ee9db1115eb09fc4af5c45b5ee9068074d0f9d5a308e3c0a99a9238e09f1e655fb0a34befb2045edd1ec0d199e66e487a7b0d78f20bf5acb6381b37ff0a4a200057c62b6fde001b451a460b70d40a2cef05abeb0c04e9a00bc291f751b997cc68a8cbac387983a992357bf9675577a984805428b50921923572bea42befd6a94ca83125013c013f928b09dcd5c570f5d4f5aae23803e64238c1033b162fa069221371ee48329b48267e0e586c9558280d4f22a29b0b996b129c0373d3aa879746e314d551730eae4a85b315bec6ff84fc70d91ec30c99da8eea01a68829cb7e1409fcfa6db503fd08f093124f14c3ca074b4e791008b966e210c7d2c64fa93d3e6094b1533684024da4bdd72bd11e4f2917afcc230e85597067741840bf937d539e7451e4e2bf07d4b7cfb32c56617574ac9429d30f527778c9292e11eb28fe008cef3332663145d5f973b9241063218618ed89660c4a67f5647fe823fc10d1a6ff4243f8be909b6f5acdbf749560a5b0e901eaf101d2f816e5678df3cc1b75031cb4197b7295cb926465992fbb3bfb81011b097a011a53b1759fd92ef3f1b6c5671a5e30d2a5aa71b690c681aa3f7e6f7075c4050ea528e8976026d6d05e93db8b2608f0ee35a76201a10aac1a461996fb9bead40c7579befe784ed3981ab556d98550c84101132b5112ec98625d92f44c18cf8b04cb8e4191639f91566ea7d78fb72db0d614b0a12231995f5c2871befb24a58b0703f7ddf8c1af68d6cfd81a4643418526f456811caeae9c3a052fb737310fce043db03bde48c78281041348b180c71f448153bc6684a90b0b19e1c0bc37cf8109c819c0f2ce3769b1f5301a0292bdeba2c63b5e4d86c10ba3b2473b6a63008309db832a73c4876d98509e27bac3cd045cbd8d2f245558314406c3aaced4eb7203bfad23600e90d83b1f8d0e05600ec2f6b3d4d493c17f282b7ea4ede63e623d00c206d25481e8623c0eb4d33e9e97cbe922f744d1478ea6accae88344e56897059705f861ca23629829eea999e27b87d6d02e9c355059bc0223f2b30118efd50bbdeb1878add130bde0b6dda2c9b184278bb03af2615f656154939c7a76305b0d23288ed70f4f440a69c04bbe1cb111045a50166a3bbfd6fc40941b6a8aeac303688a5504370190077e2ae8e3d1194ab1fc9d9374f3e6c208235507e22babe70985af93831dc0ed114cd6a95e66f3d98eb95899fab1c6eaed7baedb9e6a0d6809ff1233bee2d7e3b68975b8efbf0cd6e448c9e10b35f81415446c76039909ea047aafbf323b11f40307161bec672866650cdb5c19c5fe1a050f9a33a9f68ec003bfc500208745b8fd2863733634e39a82564dbf10e52c8708792149fbfe28d20dacc45c871afdaf232f02d454c331824363431a43e7577fcfe26137c8323f870567f6f07317f19a744f122ee947b7a21ae5e2d7df31f6e785f1076bcf280a3b00bdb6d1ff7c192a7801292617cdc45b685ecfea4d9996b2a65b1211cf78924301bcf769508b63ef057b29c896a4a89053af39bc230130392c1d27584f4a75030d0f359ee2175641401dd9678eaef3e571d4e26daf19a70b44f643deea15349905725aabb301cbd3c0603e0be24443aba37820738c52561b3e451ebf6079d92101a9f3985621d2d6dd89fedc7421ac4d7c101321cb69e1b1c492fd5da64698e10cf6073d5962fbe3b1f0c0806573224c5cf69420db7c5df275ae2e6bd97596f709a9dbf0b9500d4f9c43adf9fec6428c796ce765765d471bc2f4ab5bb4876c9d02c8c9a5889171e9bd55d32cb2ed3d2d1eabc15d638db19e2e3ffe1f05ea64e704be0a6ca2e5ed54296e3c345afc3fc85b240ef5f3d257d29bc63e1ac8c5f0000206055a9ee0ba89850aea928b94645c323f3189edb5dda06b79a18e917c812a0ee8ced51df171b37355daa4201f32fec0059048bb98394c87b30d97afbf16ab0f5e798fa5b2331e63012d89494ecb5acc31ce2dd5232ac60a8445eb6e01a1d90b453dd86db4d8604b439d09e26ffe769ca55731cca1fdf6dc800fc8ef8b093c0bd61d574a5eabe044f435cafd9f0b0948f87763805ea87ab532d7223bb17a72090ce1136a83e5ec396eaf62e80a1a265d683f5719680f0cff8933f10d9326ee0a45f1a8b114c950923ecae2845af3b3cb618da00902b826fe17d1bf40e30aad005ca10be80155eef6e7c870e7bd8bb0cb73406500db1a81f7537778e0c9f40b0ae608965cc011a8309eb64b1e058a8a9cb6e045ca47747c84d7f15859427c100dc7d430ab886a337b4a698aac78f5150a9421ef8c496a9780fb5d9f3408b86e006d51aad3b49283de01dbe8d2ebf0a7a860cc743b992538d3bbc64c818c5ac401bad5bb073cd75425fe71dbaaa00c0286bb4941304d65a2cc4c687f9f18c6c703f6a19cf2e728a9f8ef75b92f0abb54eaf93f2053d136f93a21d85985ee12740eaa05ec07d86f6c72bcbc23cf55fd79ce1523c59fe3ac046d3525a833c25b0d0baa1a8aaca74a31e16e8c7c99b6c1989a5497e7649e8ddefa0f138121590434042864825f2f1c136580da9b2970d3cbb167317981ef1f8e9c3d15652c3aeaa80f37ba08458f81dedb92a3771f2e2c041ce74d071d2aafbbc091754957286b38019eef1d6a10c094c4a3a14fc580024252cacc9aa06d2b47a7a6ec3913b49e8b0f62ca21ba9e9b60f95a42e6fa0b59823ba41ed906d0ab1646cc97d457078d410dfa46293246bc579eaa70eb8933636c2bfa06e8fe935ae93c8445b7e93483da01a386dbb14da4ed01282a08103b9a31fd2c1f008a49c39191f56507ae2253430db0033b00909714e993d0b9e1382383ef33d18940df382e61258ba9c4b2bebd0b42f4079f79c17e4ca900a88b2ef56ff77a1c9d70b89c47b238202f3d25d0a00138f242bdf17b2b8833a82441a511afde07496dabc1e14955ab8a17c9321df802c6a418e20d0ad6a3d4d3f7f2e0a3afce3f60e7f6fb78ff1839bda3c2aa545a05e339bf0519e555638da01c8544a598b79edd90565a9229de668b0cb00f19e10a588a4e693485001235b321de96a016108f1661b16717472d2cdbf5d737c1b400a9ac1b0747e244456d80b1b9fa924f6d4f0a0395ef2c67eb5cdc3bcbe032ad0447947fe5913a11f0c2c30c11a41fb9f5706fa96a013a6cc48d05ec8a5ad97a0a4c633dc022215039f9270e9b5c1ac5b1b2c3505122a91e31a81845dea3b1ba0f1bef54c2355b730558562ace24d8b2b3b7b01df1d8d4b192a4cd14cfb6d6840d4718395b8c882c065623df167e1ac5a9f343965f6d15d8b195deb57003e52504c486b19e88ed4658d8a5fb24b6b22471b92405d7c8925066ad9da9ce519228083b59723126154b82eceac89972d7ed1b2a139559376d075deae8dff1ff4e520d4a1847b60b28848a7cc3c6584a0c833780bc5b0eb1ade912d86dcea96e6d390ee90a3755cf4c1b2245a041aad1ffe8c17bba8069b36a1d733a0171c6d4fce90f32959a3974c2f30d90c6c166d7b6d0833fbf237d2700983e01ad77061cdccf0e480539947ceb664ef3c5c26d9c46afe5b8d8e13f0dcb2457beafabce2572b30965a0f406e2187a12871a2a4acd9c399748cec59a1d9c2818f1591cd4834f3f0ddab63325db900fb98c9e338fbdcbb462ed045b0fa22df3b25475fab6ad162207ad0ab620a5c4ca35b655243498fdf449cca0c1b9bd38c000db86966f1722b001c6a166621cd59f5611023a09678206ead3f286c0c232bc247e00636d48c6770f40304880ca77f6b2c061010c7a6abe8a6623e4d2859a00cfad55c2a5319bd40295162be240e272be67d09985082cd92cfaa2c2371dbbd9aeb28f2b9d65f821053241c4321c1e878d9c7e9e7db364cda093f13dba5cf8e190efa14d64ff17cf06c4eb4bbf385104903582f019c8c932ad49595a72b14133993c72ed0fdecc3c0571f0db80988a122c5a218acece85d94fbff82b726b42831424db37d363de7f0564e817b93d3ff9d384965d799cc3ba8c21a9e2533ab56605d261b3215efcbf0ac13110b7046797c9c9762fe33ba47c636140c1aac224649457a99e405a186a0adce6dd60fe540a9766d5f6680554342601dbc19a8ff67a21464b63b8aad0a60390e1e438c2ae3fbff1c470d026454ef3f9411e2645a8abdbad80fad8b26d5f08210d19375c629bf6f6cc749caa5a258e4bbeb60b06d54803a9756ff4012e8a0f00c8d0903777d1574c12c618874aa8482a34ec36f441a290c2c0950bafca20053d751bcbc7ff4905e6f4ac5fb003c8c7d467ceb96ee94fac1bf1bc73dad10602707349fd71226d5c78922496d396ab765b455d40ffdc850ad017650d972e200c74d59dc15b0858fce055bb426afc595706478a1f8127be16d7cd2a4a78d39b083062396bb35f6b31a4c0b914b5e8f4d8a4d20b1670b321a36f31026b27c9930d469fdd57b791077beff0bf18330faf8e965ce0d87e56da863c167190403fe209b741f5116aa7626f0d858abb020ec7bdd6baf39df06bf057b16f99ee07e34309113a933b3ef98b30799d43975f297105c69d37bb606ba64743aac5e3424ff5086e1524835262689af64a78ac064bb52d8ecbe733911db804dd5d5a45cf5c6a0d15cf014b883d40a98cf77bcd5e6ddd14e397026d6a4365513e17bb80bad87c065d19274629b6dc450608b4186ebc46625c9b71480c9152bcc29240da7802cf016c090a02c568f98f5a5be7daab07b64926678c22b5750214500ad89e59122307d6dbf28141106a0626a468ca6cb3c1ab4f5fcc0a605a40297fb9bf7ba148a30083ca9f5e6e4ed150e9a73a13e9018765c848ff984bfc0fc216304d7fa92dcd09b9bd0bae871a1b85676b628f1e3d5ea9cc87d9584f19b22590ba2239390e3e0de942c15896fe8736ecc59dd18e20878303baec83dc37e234c7e994cd467c660a2a696eb0c3c138416331b601943590827a32635bd480e0ade0c6f0b0ae4f110347bbca16d93c3ed9438e3c813960e67b7c2c4f010c13118be80165544718f50a23d426a322d9a04ef207c7febfe2cb56b3f1352298ef3373b81b847c44249b02e315dd340498c98e00e31bcc68e02f4f1cd5257f91fcec90b20c6dbf4e0662020e4cefc60f766ffb6aea068edc0d2241cc336de822d2ffef523c7d163d6f70092f404cd784fdc08e7064dc1f19cd258d547ef66a0ecc91c4f2e7e704f389870d5457656a1562840757e4d91764256f32c3339e2502bd1de963686c67b30df602581d8301c0ea847d1a4a72251b88d0bdbb89667d6e46b0b6275b7d03505c3a0988534eaa0a130f14ab15e20ca3232a41b13c5bc3b881438f50fcf9a396383c047e196e55cb0fa3f5724bf540591805d2665966f89efbbdd9ce3018f2a85e920c00651563edf14705391dea975e0856369168ad434cefdb232043dfa436e2c501bfae0e2e244c96a735bc66849094cc30b8a17a50aa699dbea380a523b1e22c0a20b4ed135595a5bcc57de549d2e458b5d6801dad5e7011ae3270f7f425de000625763ca0ed10eb723d261c8a6a22b3b17d1d017a73957472e0896c96f5c3540093348b1c822b6abb93d4df53529873f2c2fa95a7f80d3d17593f7ff9a9f3e10d7f6e34cb9e020337e75b1194363299fd3feecfd50aee668293c8405e4981e20429abb861555813e699f698addbbf76c5b6f66080947e187fe94a98f36bb35d0e9ef6a2730c54c803c062803f6223776a96509071ac496767c79d54a5ea62d906e5b55c76859d5bd9e6997991808849ea50150d4505f76a68eac54223052919017140820f75c7c17938e3a5b129a1b18fb4700a860bb6b0338aaf3252ca8e1209bc4657bf0bd2876480cce020ede685d342217b6c139dcd7b523287aa75361103824f60ae7808bee8bba23a3a013c5b8b2037af21a3be948568124fd30d7756032a29d9e4cadd821c7362b4d3c5a798f6b3fce05004ae2dd62123b2832f01920685fb521a2145d4d3e460991f6895450efdc53a890e8c3729e6af94d28f12e60e1be538e8de2024afa5c09e910561bd615647df55057ec5c638d74fb2af125a00dc749e223d73ca6508751d00652ca9e41639bf5859c2c347ec3293120e8e97029b175f56ba125588de21c8dfab5abd5b8694a742d18de541b611c540a6eb210a7df0d34395be76ee4ac975f4647aa1212d5a2b71977844be2330c8208c077d0e81afff4635ff45eb22aa944ab4bf4a7001fb622c253ba23dec671d146cc8f9065dc40108619d5981d42883fe2897d8def7163d548155da7978d522f0988325046a5525e80527c8862ce056613bc68596ec7b9f01fb8c3f16c9e4666cd424640f2ebc1af262707785a563e338b3225260286b8a5dd54ade716ca6b9b197e0690ab45e516720e6a102eebafda64e885dd6919a6c6e46718fe25abe61982fb1ba064cb43f8afb4e332a0dfc98a4ba727ace33bc5731f4b2f50f38c13cab07f4f0066dcbad7d6595d89067b4696245fbf00996496970321cfe4ef7dac864b1b5390dfb0177146a7040ab653df1fc6dc974b4140c26cebf0d786b3feb1c3beb0f3800c1119e1a8ac79eb454024733e94afd9b6481ce32f73b815056518caeb2da94026c4bdaf07f88b7ac88a0f1846464a4816c2d722cc8112aca467431837ec53e0df64485999a23df24a98b5cc9b95e8b8534aca6e9ae2d59df48c3a40f70dfd006e19547f9b71e35c14e9b8d1c3261106240575194e443fe785dbc43d15add470abe5f5f0752efde89be37e7ffe0fcd339c0b25a521d54ed07077217b2ea76ee02a9286d211bba8c040e5150c1f46d968397dd351b914be78f1d0ad4a3cead4d062ff25d947aea27e4f2ae72eee59dda0eecb39c5c8259f153f05ba92cde0b8b0e8411cb62656620b93861b4b133eb72b192fb98bfaad7fb477b435c8b18b3b80bba08438d589de68b34d906774e99dfbdab3c5917ade8bb821697f1b30ee8ea02d4cf316af9d73c9c8c1ab57739b099e0a0e29af969c0b19d08ab7b6ff6ab870327469a53dfdb80f1b5b8baa500309419cc1f8949c61106cfb4d8402ffa56990b52529d689ce8c3041822b6bc292d5df3a4a335f3f0ea30068f670bd34b1c6109619715e957e9ad75166ae6ca389db24eae8f2fc6687d7a2824f0a9575db1960d43dc15bb06fc655aa6ea89170baf23fca7dc668be074d07b29071df0d8146e001bc39e732135e956f22e5e57a1ea73a2fd810b649dcbd1e268d1f5387f2c5e06a6a8f1d4c27df8615051f4292b14663f335a70ed8170e0de8dd582fe72bf340cfaa05fbeca543cfb19ea226b95b43a5cc0b174efcca1eeca8b7a71aa55ab83073ff1d606fc048dc0ea032b3df51ff0e4c8d2f9824bac685c97a65d436ccb7a01fdac9f1eaebae650c9e4ced2d2a1b6a2db652364ea9af8fb9e807191911f8d0f14f958c5ab10cc4c172e0c3dac6c0fb6ee3e9dc586dd668c047cfe87926c9407fb3a03c4607a2a33dd7afa33607bea6b755f0ef43f49044a864a3350287d7f0e1ff327df230e5026390eeeff7ada3ba3ba41d95d5b7ebd4f055ff0ca1ca290075925b84f4520aea8143f7a2a206061682d71121934ce07317d492195e3d8c503053745f27de8ff02ddef4fa02550e404c39c3229eec254dc59f571c9c1f0ba0fbb1a41dfafa49ae6e8c671c5be55c7d6690c7eb47a089a7dc8306e38c157be04c9e164197307135da89653f52fb0822d801889b5a0c131bbe0ea49b4311d71062a021115ca5825eaa624cce1a9d4f9e826fbf50eb10c64ec52201d47f5e00b07636214e74e3039b6e4d9716d31596beb17955bbe26030daa16ef525039d42b004004cb8c8d83f78e2a978635dc2ee8cafc59e9bf7ab5e77320bebcbf95f9a20c235a7e58e800970ffe333122e48900650cbda41a86df1fbaa3da9d4f4cc9c802c3186dba1f81bedfd59cff7d1a8719c16cce2b667f71d2b78912aabe8077e10ad3f5a2b029b80ea4f5b4fe276df29cd8c4592184ecab5770cf72e887bf21de02834b1dbb9c5b46e57ec701c1d1e5d304ab2f0fc65f4b514be4f4ca2757d6b90ac5f48b2fbba90be946fd48f9c8cfc2b327669e747642876ced2d498c23f02707894f837b6c5cb562465ad66f66b3c560c204576bcd53a2ed1a95c8ca9374a70d20fd91a01673f3ea072357f15d4c19796169982332c52576c34156d46e83da024a3b3be3343c5e6b03f2b74741cf319f749abdd0c189ba697ebb3ccf112ff50c4ba313830f2ad8846ae84ffac8876daa495b09a8ca9444625284cfc81499130d45b976f28b321fff7019f68723272b6c40b52c7089b7f210dd65c7ee76b7ae0e0e719cbb64c0d4288b94bd770114ad912b24525c24d0149789fde99d1c1925026327fc4f3525868264e108b7766263c65b1314be92aa589507f07c429a4fae00611f466b5aeefe4a43726b0b8b675ce8d0beb1696cb016d16830c924e5dd7a0399160215f732914403065ca27fc7f7c4afa8f50b38831f560a837d6dfe11840ec61f5230363c870605f772a765c7c5ab87459ac20905ba2247f843a6659bb60aa0407fb39165081c1defb38cd752c9b4ddcea69e754af7d0207de52ced984e0e6749ba8d9f43fbd35c306651996ec7fe71484783d7f9d300cb07b136c2247409f8be8f51142a64eda48d85abee7e908937973f77fb5159ce2f5db00d27b40d0a1b6f81dcfdc4c4c3030c58866cefecfcb8fa2b170a7df1d550059e6bef94ad0b4f142b40b3ccd9c53edf26a4e7921feb548024ddfe1985e952bd23303a13e90006f3e3e11fcd00b14be08b570bf4b843c26152120305ede093ca92f23334460271025a1cfa7de35c0b9d7f9d7d2e247a1feb70d7d7446e4df5bf953c90ed420593b6835badf38b0ec8618110b94994542596794695e43eeff1f2fce485034c0403baf4223975d90a29b4e4db7c0db272b5443f683554e78037d8e565aaf4ab09a92b15f70864980c7656d8fcc88b58a7e8bf96e87bde1264f636fa97317c5403d51097b27fd539ea39c63d961a28db2384b5481d360c19321250906aa6d0bc00c6099517fc6794058cd9beb10f30020fccfbdc4ce639c669c9df34006cfc90097e5faa18ee4fd24cd6e2877e0597e020b3760eaf44e5032aa0eca23028d4460d3903d6970eb53d94b74eda2d62f1383b2b1c89acde5e0fa41b8462b340444909f8ed23272fc79eac2f19568bab31d459e136d35fd7a9c7df7241f93089c8a90559d7625c6b08107413d0af4f4203da59552448f0c3070c1e69c11921322e80001503e684578728c203ab6c1adc12695a84ac6e3e6fb40e237a4bbadce211300ef728586a4a12da4286318f25cdd8183721f055ced6233d17fca9424047a4870cf2c29b4194e590881c789f18e94e7e605f9aff1718db89b0c2f6b0a3ab315108a7b0a91ac314fdc935a2cf73b639169f13b50ef319376510a254ac743584a7075dc948c5aad4acfda66151119014d469df235b0755e35abfd719737978a5a604613ff8cd5cd65260734a9668ce33550327d7c5b71ad8b6ebc3542c814253b70e46cd625c6b1bda11df5229f553a6c71cf138ed73d753abcafc348540de094f04e4a4682564ad3307556ee66810aede34536093640e53aea37ad9ee9be5802001eda0809bdee414e2a09c3ba9398f6af1eef3a66ef5d9cff68acf3a956514cb0afbd38cdb45f2af77880dca1b505d2be925d0653bec9fcb7f380dfd8e9c2ec30ba9c96bcf0075965269d10d8a1497975c0a54b7c02da177fb29bd627b039fbe06f61967bc03c45bb4a0eab399a000ed0a38a056baf536671b751d71f83f5f44094a8f275898e4b6239eb76f266a50beb977e8654d37dde6020b5bd40cfc7441083beb2f8c2c24e2db9391d202c69ec0b885e4498c953ee098fcc2554704c876096d46b6ca1fa3eb284d8f0d5dd7ed0e517582b053127fefc4d7a3b68f21acb506707f44585c5af4358f6d40e27eee51f79a6fd365e9ba271bcab21ab09a858c071d739d1ad7b4e2b38b24e45cf8268b4bb8ae99cdbec3718bc562648cd6164302439ce3dd3e27c86f8d5cc06d07672bd43fb23a55aefb25a4a46e453db46d360b8fe434b8072d0ab6752452e043bccb78bfd43b660d9cc99313619ad8df35bc0f true -check_ring_signature 1bee2307cbae0d70deb765ea6585a98f2360ac5f2312d53b30160896d22d7e69 dfc10e8ef7e54df0abcbf2e37d6e5240242831051debd943f2c97a44f18822a6 22 c45d485befe03df7c8073e4fa034cdd6a3b5d40adfb67840f3f919796a681a78 1b8c4973a61cb07fe9599d95c82476b9aa4578b620ef20d7b0777ec52b3af0b2 35c6fb1384be87a117d29795a707d311db29a4acd23f5ed91944a7906ef3f2f7 eddb7b2b8e8de4bde4042b028915489ab990646eb09ba7efdc6a10eea9a45416 5ebd85c8babff784c1ae931bffc2de28676f0a017cd94159afce10b9814ddd9c ef816b04ef9307d5b6778731b69dd6d466bd0be4f8de1f2bc1802fd54ceaf60c aaabeeb56e0af61da30ffe24674d808726a77e5dbf1378019f998b196e1ef95a 0c54d3a2c1d24bd9a1731a44bcab632e51b6daa5dced8302f636c5fa1613123d 471192b5ce65856383a16f4dd5f459ab3702a005c1500906034f46dc5abb7bd0 7940d0a9f9db9adc473486273108fe5485e933466a50b239fda165e100311b2d 3ff46e21277f805db51d846787280d617d00dca1e6053c000b789bc46acaf9be 5feb804d9bb8ee880e3ece47943d1ed5230a0240bd8212dbd51801c14ec7306b 3a0b1c8bd722d56a98dbbee40e741b857825457cf2bf5caa9a8e418be24c999c e3b7344146e4f945b1d37260bfee459535adc472c6fd30aa13804ad0b0e6770a 4fbaa8d028d7eb59312a4b03932d4ba627be85d55ddda546d050c708456a60cb f3d51d875c3248b571964b92e73ee660fc43f943eea989e261708d49abed67d4 c18a3f55e81463b0aa274eb57130f9b72c71de4f9faab6d3b1a1bf664904fe8c 93a8aea6a911b402292f8c0475f314da1da8a2ffdc7fd3e8bb3e6096bcf25401 9597f623fba19d46f1c10cf99eb53fe57ed666a11847cd2ad4b708d8b9d58cbf ff3bd3417c922c47f72603ff5bbe698562707fa2d53d15b5dc72d490bbafec33 dbbda6f78f7b1a3db04a220d35a817de3589df0f52b38af3c7c0b67f5d7ba501 8708cfdc241c15b8f906f54ac943d64a6519197f0016cf65b8c06668059f5e3b b56186156ddd66c41fd56b168075915603dc79a3506fe7515da93db2ff49cb0a6f1cf84d1c273f4083d9b01288057bd98e298936c62c365382681c700f258c0919caec23a5472552e3b8f30c70665929b875b5dd63e82ccb31d9d8596d992b03d22557852fc8823f66a3c77c0a1e0bfe22fbf52beb359e89a63d9c1f8c91120096e6b7aab41b779b7556d540517558386b80581f0f205bfaf0fce6c7300036038d87ad67f74a58757de877d91a968e070b772e74c15325680d4288efe650df02c08bbfdb2035b8dc2a43be7b3f725bab5253bddc23acdcc61f7b063183c7ee0b7da40bc2bff5fb29594967c2187f21a22972eb3c56139d3ae1333c383adda403cedaad74e36281f046878424e23b43fa32327fce3be3b561ead37d9c43627b05453100c42a00e5f68e92f7101d47d49cdffd6ac9681d4beac4d4f57d91a3220ba3872ed6a5487c6d8b6323503967db43d3d64673adcaacaa4a643f292adbed071f17bacfbade56afe027ca6e68a974d397e6663f4c5ae459da3d661814d5ae022b87077e44e1328bf175c6e3e47190a0f2d50d0d24a4e7a7ee7623b434d92508fec9add44c626e2f71be8589570009d7a75dd3062bd4642f30ca0977f5951207e0eac4972d949358f7a800f572b2c007aad666806f1e62467d9e667783a4e503a60097aeced8df2cd966f1c8de1e69ee2f316d9ec6d25014367daaff02c1c80b8bb7785017ddfc53aa92ecba114ec305585428a27e7117c7c0ccddf7b7ac5203adf82c0f0ed2c9e01dad05a8ceb523bcfdf4907d8a46079b0ba7f6ac2422b2797004ab75a0bccb322d861805d73d905d7dc4ba6f7775e833a7389727a928ce05e38ba96a2d388883de471bb3dce75b350bd6fc1efd316192719939cbeaa6d001f425dccb7d81401ca27c7e03681dfce38b07ec8b32e333f776717d9c1c16af02a466ef51da0a09bbbf2e8090596629978e81cdfca698d174b8efebd941df880d84951275f1d6b4c5acdd018f5bd49611314613a3d8f787b8e989b5d45e503d030ca8b2c253603eaec69e0d03f8a07b0a08cc0c8be03b4f48de90c32fe8d22a0c863aea524d14f4d8c491530ce8b672bac014086b29af1ca984f8fe1cab93800c5cb42a164371410f053988fcf484c50570c80d92b9715fc3fcd887151945f503b519876e0c7e4bdb7e4f0dc1bb534ff4a1155f15068423a79ccc294029fbaa086a8286f8765703f7f39e885678278c3cfc3c293dc98ced06e434bb87832c5303b7daa81625a7ef6f28b0a4123d23ee6b437416a0b17e6c8eb770b034e63d16035ea357ef9ee54770f292400f1a8e8ef83af638772802846d5cb851e83143ec072387b6da9368458a44a431891ccaf5e706cd3a05c29aaec6a6e0bbb91bc7180380ae57fbdea2fa0bfded11f97866bb78b09f474f28473007bdcde7cc19b075045f278cb3fc87d292090c5ea6f8dd198803accf963a6611a03f8b62703b6ebe0517165083c438125597a5fb5e6cb322276d746591a06d3d3f063c2a8e0927890b243084a39109752a50df29a32530d5ddeab48f962c3d7243c22afa89ea14f30864d48541aef11add3aa02b7b5297de4341c9a0d03b8916d5f2f6dba731dbe500c0ca5bb9c31a4374e5887f6511b0a14ef96d90c3071a9cb019a1341a01ce710eb2f5e1dfd060705b445f328c9090ebec09ee03d084c38c7d34bdebb8b0bef70503e932302e3b757d9c9ba8a9508923ddaf16843e0bc37b9004053ea541b29d0acaca4ef0a6cb558509006939753bf8c873e8cf894793d7c650d108f3aaddd40ec069723b3ca6fe894bfe6a020c54c23a12f9e5373ce4241f8c7f8700918c3b09c85ccea5a50477c7833b7c3f535e01edb8c45b3e8289158ffc5197aacedae50c3e3e91808f46f1bf61bc6c2924cb3dbcc31dbf44ff3ec6143316b74f8145570a5f834e4485bca12df1acdfc352e2f6b347fed182d7f34d9df49b33d2a49cde02 false -check_ring_signature 21895cc59133ebf0004c36193e05c4fc127cb5bd555dc63d621eaffed8be71d3 ecbafb4b0edf27dcd8eee2990eb7c9818e2d41972fbec6c351a6f0bc43b0c85b 5 37d69e45e5f7e7a1bfaf81415204da2a813010f1156bcf423aa2d643e73cabe4 78a4d36d922388db274b312324ae0237ec22a97abe3e0005864a99eca591d767 569eaf9aa27c1a44e00771277742c802f4756f5d2d251e410010bea0f3c793bd 9c3aba914a25e1d8c60415f6bbd5cb8da1f87ee96f0d66a0eb728e5ff2465fdb f135aed402f9675036a6c57487a49bc82b8867971b154c58c688c7dec5327107 e1684019448b2717d4560115ef905501d9cb996cdaf5183f8474428d7dd7640ae8036d6694a1dc8360f0d5e95247c0b255774482caf3ee158649a786dd34b80974c4549639a7c496cf134559af29020eb5f86f199bed4956e8005294851c83082e29da64449099f459a9b4cb41ebc4fe641d4b88873fa99ff127121446874703f8f6becac5ac81433b2e797e8c06c5695988597368fb7cbe25a45afe883fb402c520730927e3789fc3315a88c77b5b1f527eed371ad35c248f1c748cedcadd4d6b2e8cd1a683105b3d3e96c3cca4b5eb7722a7313e5e52b37ef294a7749a260f5a58496fb0d255a44e9b878a13a7df801c4ca633435c2c0ebcfe5b8fc2de0704205cb65f55485cbc2ee4acde551dc9e6d63f06e9ae76ad204239b96e78685700c0bd5749607b69522cee9b440e1ecd90286afef5de1a978ededf91a951994500 false -check_ring_signature a1ceb1e28cf7539702f29124201902f89c576a9dedc08b532b6f32f4714bb5f1 9215affded4f3464d39c3468da2170b92573ff25f49754b8a276c783cabc0fef 7 4592c2b238b2d62b85575544b2537548b7ac136856aa3a4fb8c61fad355e6856 8062de0f6e58bf5735f731930b04c87f0f972b40575b37ee13df8c6bfe3d3f34 212e3537eb76779f2d59cb95a9128d98bede8ac721381434ff6721c973f91d76 9296843a003ed00eb5f0a4c38894adf0d573d6eec2894d455293c8da2e6af29f 49120cf0c3575650e3c1ed19cfecd85bce34a9bdc21698f073495a9cd495243b a6c3dc919da447bd3fbb1ef1bf398d64f40a158d669a65d47857cb20635bdcb8 b219b7ea9550fc7deeac250df0d713975074116921ed26d6f2c2af0dd72def7b 8bc3428f3c8c9d8e812e0bcddae48876e0efc696d9ebc39d0c688166748f72022c99772a8fd69ea904af3905637e78ef69dcea09ebacb9d241672cb6145640072fca8e6e28dc4678168953dbda6f0f69d32f779c4d744ffd2973e4d88053040c49b2643e60b0517922ef84312250ca2650dd85174582ffbdc22b057c243e5b01881090479c92dc88075651a3e0b2bef743d37cf9f4469b27fd3f4f698886730f9bf90de572444ac2ddfa169eb32e357fef5a286afe0ddcc4c14251453dc6c80ef21fea6df568479345218bc235c41689460ee06803755b7da4ecfc0e63309a067478ac2bb001bf2b3b58e9e087c4d9e8475b0d0d61074c9ff005b3e70724180e81050c52f2e5a4a39b7e63b4727cb8cb6af33683938f8331d2c59631acd2e503eb566339f6575e034371281ef3e27cbdefa09cbbfd8100d57ebd5b41f68fbd09cd316728038f6b59e837513f93be96082db6d59200e84a643f5e004f310ea45b4611f12e15a95f519222865dc3b557ef0c9e94a21920291b11e5e3c6ff64bb02836ce612993dc48fd652f7bf64e3f0c215fbeb15a5d34ac45b9e87531ab2900fd3b2d970942f61ec3e0db7feb4b21e3caedb7d98833f0cef00affbab7175300c false -check_ring_signature 91c96196c234c8360a561f05e5da2dd0df8a985f551e177598543c330597b9f5 9c121c8fe41c45ba3ec374109c7ee8bb3ae2f16b9cee0458300dcba0ef2f151c 193 e161862a99dae815c0a5217b0a675d985d24cb1b234af8c5bcdb7c1b5b4fdc77 7121da185c9e7f255d80df00d815f6d05190465dc1291e28b2d88a09fa79e91f 868b93a12a9876467229b1ae65866de4010fa91597df8e5dd76a051bef3e4fad d1ac8e9547501523ee84dabadcd54fb4e28e16a8152d558634df32dee382e378 67122106e6c1cb08b9efc15aef3ed942d1c901b1553d0e2ae350e82768176cfa 2515245e7b7d2b38ccaa8806b8de0a08671618682db8a7a93b3b2c457fa43365 94e9f5fb2ef1fbbf74385bfa5beecc2cd38dae1b68963214c6c77a995102563d 46e220cd8a58d8cbe0db7c1dd5b8115d5775abc484426b8ad197badd0534bcc2 1996fb0da456a5e4f58ca01db9bb03e6e1fab0671fcac965621a556f4770acab a4225350f4e0e9f647289db0370949b64a0aeadca3efc27764ba846c8d3ffdf0 718b5e839de4a375afe2c75eb856e8927dd409f76ceadddfa3ca530fe493a01c f8bbd8593204be709d2bbe893a8db100040ce0eef159c98ebeeb6eea9f7221ca 3bafcfa2817b887b77ffdf7c24b058bf0017167fca57e1357fb110758fa1fa20 08cca9f73cc1ad943b1566e7790bfeb62f67b282f5dfc7191a41eb8d08bb121b b70fa8af1494810e3b3dd725691660e72f04bfaf358fa68dbdc4c219459124cd dd82ef68116ce09705bacd369b3ea9158ce5c6c804915e788a7f13563447fa37 b8524e8293c7d7b22d25bc99dc14feb769f2bb7beb75ae2274fa49d95d0d7774 d3850c2305837f933d2277238418b208421d1493141d1ab756e056fa7d41cf6a e8db854c01dfd1726886758dba64cf0d153f272a63338924a838613c9a06832d 16e8ba39570340f24524cfaf101027387bab6bf60326e4d7bc4be72dc904cf79 a5293e175287fd7a10a0f8bc0121df46380e9efa8e80ab9644a6d1a220e9472f 8588de4a93aca11147879b5332e5a634fa8d8ec0a0573550ffcbaded51bdfa66 b4779f8ded424940af83a6841b7e9b894ac5759a7af25258311f52c98e3dd509 12890b61c8ceeaba6046f19e1090731c72a25c4563438e8d185c874407f65256 fbd1fdbd9fae56a72df8d02a89144f374d1ec2345fe6d87a7ba9717437ba7b19 5809a152e2a50d71d056d27262687d28502fc287c5e5477a43f07995c7754451 d055af4076b2ceeef2e7359950fcbefeb63fd39eb233a004aba976fc4ea0e77b e82b74da77ddc12765f219815f89b9b67417515b26ba0682cee0142c9895d1c2 b7fe388bbc75eb37da2c4336bb300db8e56c7d60d43e38c117c4a66d57b7c78e 3f9bb068b366b1b8a3f91dd4a2d9efe76a3c42c5b59239e1e804a761605d10c7 d8c61f4abe890c25e7b564c2b119e033f86bce9237334953be942a9cabb599d9 407dc82419c3ec2d8eef6dc0b30d48bf3e52e1a60c094f1f704f19154c5f39b9 9d76fa5bbb8c6915177b98cadc434fccebd7504dfb78ae7c193f480f2a6de9a5 651f507857a6873ca14dfd136efe28db29fb7cddf1914cd98b4f12354f7771c1 4654f0e017bba46280efb2379af5a4671ac096faee347479f1fb177c06392d0a b4f29d1eafe89f4433b18ad6ac9b99203d6c331c86db324601d9765327d9c4ba e74ff089967086c994f648369ebcb39fe3fa6753eff2cc6553a1a84186c701bf 41f12df6c52d96c7ae5b58b9e334b98a4e39db6f89459ba39569d1192bf664f5 e8d1a2b82fc477d08f92272630c88253f3a630b6f125fb93652363577bced915 b41554744802375e2c0da4a46d8b63200dbdf80db23e11025348bda3419aa1c8 af44db4b23b67edad57731a85371b057caa7dc9fdb1cbc5c882cddb6eea3cbd5 3ee37268715f4b2e7959d1fd9d0060f194c289e1560ce9dc49cee3f0d3230a3c 2381e1415f6888dea57bbb3235dc75bc33e425d29e5610079724a9ae62875ad2 6ee002dce5a8e5d0d52c24b1d24dcb600847917cdf31152ea3a2361b4e300205 23ae7949734cd7a92206e7e3526a0aefd0c57942370b5d9e5d2a0efc04ffe832 a3d13d67723c4ed153d6e80c00f68481d78e804c75ca62c08e1ffb21199712e3 493be63139ee40c22b0b4cd78b20ebffd5f1e33c4feb8c1c8bfb6415d9b74745 e9359b412f4bc3ac21eb1095f50efb9f37e9f8ffae075459e6fb3fc9c0cc0dec 17b1930073d4d52366076e94a841577fb2bfdd0b946f7e37d32f9a657e17fd46 7f17f005bd7866df24cc4931283d94ffd0ce6be0d8ad6ede768ac472204cc05a c41cce248f29ea63f7dda29c5647b3a26536c73d298e31d63cd99fe8b50eddb1 296122c8c5f534080b20f361a7e845ca954bfe3a7927c318ad4e6b83c07dd1d7 5b3df5b707756b3c833d74a0c25648a5d130e30530280e7f3df53b09cd6eb716 d96da542b5da7a0c227eeddd2499e5e754d96df6d02085ec2bb486d1e5a42163 7be9c67253d69b68ec8f647fafcd269d4eb0abf6b54b564553e0a6fcd6ec93af 34027dcf2aaf47fb96bf715bfc1c5c5882442a136d4d9ee613c922edccd5a953 d05765857c4fbae6f8dd06f73b76299f95fe6b4fcfa3a70ca9bbc91a49a9de0c 1e72257db52cace73bc7979f06addce710a5ce34ac62c46f4e3543ba4024b4e7 a51ca9ad9b001063a07de8deab3eb0e10e8acf78628687fe26718f90a19ad05c 7e501131246a3567ccc0d7ca21741d55a54411379695c188d7036358db8077ac 2cf9fb0483a55f8d7bd2586b4e9471f319f259d453a776cbbc3f5445ea09ede8 e48913dd6cef97282dbd49f901e1a9cd6fc0c87f08a8d4bebd1f40595ae1cfab 1a2d07d4b426a69052a4976a63387356140394570159a51d6f52fcc64694d8d0 c74799705b1b3a25759dd5d6ad7cbc3a2e52f9f5d329fd3e2abd43d202db2412 0e191d363505d184aa67da6c5f16599c2120daa59b5bc328f3a24f513fbed87e 85022f199b2c5063a9c1deed15d761c837d51252f7beabcd93e2c9e9ebb067f6 4fac2d2d8e55a6244730a2ac6b642faf27f20b7a8f59dd21e4fd8e4ea9a0cc9f 4cf8659e5f7c920f10b7f027215aa50d75377cdcb2316b89ae88c8ebdc9983a2 f3fb3eac0a57ebe0531d1cd506905a7faaccc3ceca80c19cdad326df6525892e 46f413c203785cbce8db7924f75f5d90907c56a6cc67c89be04245a651d95fe9 f6621310288256e3e799793220a9b1c9a90a1435ccfea0be7d06d2cf385b827b 9b4bd622b52628480455d694b9f218c927e710921be5f01362458b34b3251aee a214ccce02e0da7ff54638697185d5dc61c4480ad55f260c8cde16114eab948e 2d3c7f5c6d45c4655902ec442430ebd92336f03b272c5e927b51093b7f91a973 ab67cbbc528fdb606eccdba9f5ea8271ff61a938e9a43055a6d25bf8dbaec37f eba5ab398362838fa05fbcc1847834d5af81df74100a33f058b2664a511cdd5f 696f0ac7c74f9972ecb84b4e3b35e15845ce6a3c1c7d6829b9eeb670b1d01f2f 7acfe1dd35a458a98a76015bc9569267b33b3b2d775343dd7ea02cf50887b256 2705f7156b4caeadaadc47fd02e3732bb728640ed61b9c1870afff0eaef034e2 8aa19a27e43d8dd0990ac65e8a51b1ef11ef2f40eb83281ebe61cc81c2874489 a6bf939c5c529abec874124b71d24ac6347f77b1c01f82b5f11dc76c9c0ccf4e ddbd80ffa8967c400794b5d91cc874abd847d52263238582c2752ace236f9241 fa64d0830fd00bcdb2023fda400ababebf39af7c59e94c2d9c3e4c18d1bedd64 f65ef60b181b161d7e602a01d839019010b259e4eb7ef359e6d5b538d0665740 b38f94c5dbc53de06f0048be4c475caca4003507cf648377ead7d34b577f19d7 e15dfd5a89f20a6d36d165102bb959ea024541b29f56dc2d49c919d7516d1170 c5fc2a9ed79c26099659754e3ca1b80315ca57892f843a578340ae7fa73d3634 6f5091724ad56abb7972390808f6fe142e650c5f9ba0de8756472c8e5f51be61 2648c79d0339a2f60aeb263b45ec55f2bec8a30bce8f1a330e55e31cd283dfcf c8fa210fce5b69a82e9dcadcde20d7b91eca89c7103d0178bfbf88c11d11ec49 33141beaf3621ffefb22797eb08d9ce9551424b1632f825fadda731d73c4d51a ef4e9c88061d7cd88a2380de59a674a1c691d187b0d992b28820465fcac67f99 e4f20cacf1c9e8f05ccdce34af65344b4e5cae9bb9b36984e2d8f861d2670890 958c2698e83219646f07560f3f205f3c49fc1f4f79eb8bfc4525b64beb9821ca 0d78f83e62808774279f5dd6e02e53389468028fba4eca15a1cdc460410a225a 0c674704d18814852e547a7cec5a67d3170638355641be3a48f8e5d00045cef4 1ea41e2b9aabe377a04a49f199dea7e61d0b9778d3540fae356d329d9052c5c3 dea86385c770f40a531abaa73015bbbf7c150ec44f01c74e53c11c5645a90a34 4c73127881b53641bc501ec13f8bdcfd85af76fc33ad6abe46b54da5cd492dc8 4d1fe371995472084ab0059e5d11188b223c17282fc89df118ccd8a702973f81 75ce45819d1a62b6f619c30ef1e1255754438a2fe7f76fd4480ef997ab50a554 5b984b640e4b9cba7d4e9b40002421051ed8679c0cd0240c614025768b551cab 82332c7f72e3d98872b06714203483af929fb06067bbd3458f6d8b75b0d550d3 4c29590b25aa2457c8de88dc45efa6d55800e0ab5a233c02ad26d19b5bedffc2 29bcb5493c220fa2f1e4db2ea4d022f2130c4fca2fad45fa5e151e376b5b9daf 8d662e8554f94a61520522dceeda02c8da2d56d682380437bb7e7e7000337c0e 012f84568e6de3bbab05be822f2dcd3e0019d873c5110d2c8a8329c44f56da33 621286d6ccf00e35785e59cfc589b81946359f82fb6195b5fc5199edac11586b efb019c49b35c135c095c13863dcfd0e8ee4eeaa4ffbc0a2dffcd24ae72fb266 978bb9d5405f956463ae75b382e997450ba089a8136ce95ac1bf2f07c86f40b0 db3b3ac107629e8993f38de924439c672f66b4f4626aca460e86ce4d1b05a9b8 78dd68c749d267a8230a6688b4356bc8e7192e67afce456b4966f38e268580c5 1869040fe2e6372ef82a8d4cda43a49bbb5f8b29bd7075122d25aceca048a7d0 6a7399db2ade2a40bf94c2c3e28a9ac8972e18f237a77364242ad7ff808111d9 1ccfa9cf961e12a0b8e220eabee2166e1d5e03b1b2ff6acbb9342c3e61f943e7 e3861f895559d30b28af8730bfa776c6127e0a98d5c378833dc0846cae1b5f54 c13516250905ba3760eb3d9cec410a37edefe952d97f6fe9bfceee544029ccd8 ae7449e1fd0bb6b4ef3941ca74caf6d710e5b8bc0654a8b740bdee26ad2649b5 d8e59f284cd7abd2d20f9652bc54a6dd6f725795f19f3701ffa4845d613129cb b22bf5a0c5fe152380c50e6f155b68b0971d707baba2bd53e995d2700facf3d6 d3f477858df1a9ac4d44227ac73e38bb6a318d29ee4653014c696108d05713ef 7dbacfc64f214324d135adb0ad7e20fe63aa8959c8502c34784635adcf672b4f 6dfbc709bfc859ce5d662cb3c284c86fe4cd2bbb9df777e55cf20503d3eff1c3 66ebcc577c3de8c45b31ece1ccfcb6d1411c40a27859797f3b5ad0a70b2e6081 b63f90395ed317237d5833d424abd1628a7c799c0911847576e65944249e8670 604cad976aa6284c18fa49fdb84d5d8d80a54c4740896b1c979ad4c44cd059f3 b1b85202902801287f67b5890bb1ced239d97e2311ff8b523cd51b6ff11dee46 32a7caba85a3cb2f442687465fba4b99402a2211bcc60fcf3011e093e0d603de 2652ee4ae8d9444d103e92a138e85737c707169280ae21f2efcf86a889c44576 7b1ca852eac13aff8342de958d6eb66841a432ba45e4e0c0546fcc25076363a6 63d964103c6680369a99ec33309b7a4b28e3168b2c0d14dd6ad0e047d8a98ac1 300da9fa3bd62dd3317c034e5d382134f295cf52f5a3a15f9b7149a085ab140a 7a2abe067810f916735f11fc3eafcae30c7cc466e6c2d3132ecdfd86fef2071c ea4c30ce57f207ae4fc18dfbdf32e515ef8aea1c28b9dc3984ee0cc9ec45f41d 981cc22979a6ee32d9d052209197d819caed76a573c0d98d95af997d1a31603f 3ff6934795ff1bda0af9a6b89cb70e4c0ce34462534cc9e41f86956a62f35029 93044f44329b2f80bc79788e8d216e1b6547a36c6675ea32157ac9712d83e078 f9630497226ce1f3e1f19f5a00482a46637c1f0741766170649eb4ca50d1a6c5 9bd9a86894275e31205158cdae4ebee1ba90ba2b84a0b2b7fd6fc2adc014b651 68b487bd1b15792da5b7bd1855b780013733ec2f46c72b8d259286df38f45472 4baf89810b049b2207a2e33df2131dd7089c161d61dec454bf29d207f7890afa c63216014b78c4c2582f14105fa45a3fc2b44cccd5006e717d77f4dc1fc16cdd 2f88b590b978261c9c1f9d3145bbe8010acea2b5736425610dc29816900bf917 5f8e13f81295adeedbef8f8d611df75d9c3d607524d3a2d48395072e559f9867 9be16bc0147b509002ac9961c8092298ff930f3055d68bcc9dfd54c703470043 478f7af7f6646765e498b9324b0223882c0c050c36068257f59e9d69c61ba60d e875b111f7254650a6a0d58c2bbaf062451d9274ea48e2e9497d35aa72aeb76b f57b9b8f3c4d8d9c03f992695bbc410d1f66cd94ff1d9926871f7413ee767525 1024ced84e85c932c83ff6d58076ffb76355fa4624aeae93ee92dae866c89c0c a74744d6178daee546654b5204bbd3733fe4227eabb4295784090288db2bea03 6a80bb76ba087cf97cf40364284534495f5fc65de38acd2d537d0c3fc99ce46c 334ba343a14bdc95cce0a72d8dbadbcc5f59beb2d9b0a777f4ff64ae0d4294f6 a225481439a23811489e99068287ef24a0bc8c91aafc39fe06d5511b9521fb4a 04ef78c6aa557b0a7b0c7419b59d8c0f3844d563bff6e7a210882216a358359f ea624d34206592e2c45f7c9ee7d74de718f051f3b4a48a61abd85e0d0c290d98 a8820dba71d62b35e4717aa8c4bc42ceb16e32a2dc856dd5ba3bd72870de7abf 7e9b06dac3b7bb9eb053858010e3a18e77fd8c0fff6f0395bd5b8452c59d0c18 d742ba47c5ee6dd0dfc960258a90f4daa7faa91809baaef005c30e543674b8c9 da6963ff7c64cdc58fadd4636ca45f431bceab8694618c823cee20f7e188c254 ddf4bd50ee7b3d5caefe27ef54a3f4b732333c558f4e7c5b4aa874849ac50944 e647071ea38ae99b53087edf06156ff85a420d35455d5cb68dfce219975d0db4 e90c1ea0a57ff79857e003528dba4eab4ad98450617d6f75bb257ba71169de62 2a4a02456239a17fe7f12434629832d8139e3a883dcaafb7c82d14883fbde442 179f62b40c704c509046e5425dd9823f677018eba18766621192cd3787f956e1 be99fa9b0ee10d5d16b01419372dd8b27ffc59ed268fb916fb62c007bc03c4bc 8a5700c4b06c4cbbe778cb573b954546b7bd50f9ed0bf68edaeb1c62c7144621 afa2806c478f8eb525c3f6c10dedb57463e266a7bb74fcf4a398242cdb373645 dc065f497840d0e64bcc10ad55d537c1c0063b543be94bd194b19c04370d6ed7 22fceb6e94eac5560cf080b5d4cab3ec7629e44faf999dab219910755f65fc90 3630889f01d6053111e6b6b81f5322d48e3cf802653271918afda028fd610dbf 7e0590a0b6591103b696d6191cdbd734c9a298ec806e445520e138766a79a6fb 95c795a0c79207d607c18f9b4878801b51c0343121d2d88572d854373ff0f4c2 acc301102d1ca017efd7ff48d1e89d5798e2c0316a1c8a2426b725f7a32422f6 512fe979cb4ab14e86e38ee7e91ce314bf41bc8545b8fc9abf50f243ddfcec17 3fe18f13a44ff9057dfa3a8d991c1da262c70c85d83f6494323edb2a0ef115fa 4fff7ca8ad7a8da147c26075da5a612b7cd9889b2ec89b2698ea4cbce420ba09 50a505cdf0dd91f3436777d41a6ec954356309a339b230b7dd2884ed3e5c6ac9 59c78ae9f94a520bc32525c70c4ac5856057dd470435dd7dc306163f8ffe2a45 4feedca1721e8ab967a9a811d3a54d8d87702b7191e7806ccead4f28b63746c8 d90248f2402560e288d52118ee92e9d25c62ee37b71a254472089bc9f3d556bb 47cff9595acb876c63dd28faf929ec0dcb4611e1e178c3ad77c3db1cd10535e1 28372fe85facaec13ffc1dd7cba6184e3bc33bcc7f47ce8b45dddb0fea398172 07a524d12dc6c779874953230dc4959586b3af0bebf5fe35414e0b1103c127a4 11db38c2d48746d3ecf43c84254a3cd0ec3734c3b3788c5814ec34edf209977c b258e81d902b876b749d4710296188f2140a5ffa7502595fe84193f060d6e60e bcc5dbc52d34ef550fa4460ed0084f68643e7d53124e24bd84f4a2df86211cbb 27bc7aed7ea316ddf5011f4e888183956c78674d2c81aaf4097cc1126d3b2f0b 673ac5265aea52c4ef3bb9f183930e274b9f6a85158089199bb1d6359ad7b946 3d918c37bb71d96f2cd7994b0e374b3bc5d1bd8c37dc4da0dae37fa03cf0286e 190e5082988924eddc9b7b521248d098ca4eaa5e3bcff3061d0a489923f4cc3f 755acdc3f0b93afb5a0a328a9dc292f241ae42385b921e487649df858513f58f 4aea64a5c881de6f7ee664733f109b4c0e3e0eac0cf3015c733b477f29bff980 5ed467f8b37afac922919bb3b756736a6e6b68369a12e5b3becc1da98a9f0cf9 498ee241f1df20707d21041cd5933298a3e257eb25f4d19335e1ea95153c4400b936190b0089898b3fe556bd8a670912a3d1f246d4a6bac37b457e9e4ba00e06d25a47c4170bb93690a09cf118014c739f71293baf6993600463f6d0810af3025a58b3353ceb55678e6d37ef2aade03c17cfcbfcd89e4824a5319d32bbd4da0593039b1be7cc80ddf9b3e3ec6f5fd48a47b25912acb767735a6cc6d5a7bd310644bd96b87caf804cefbb20569c3ad4f56d94c2bae51cbc2b94d0ba6f4398950d195c4f8520416795626750461d2eb0c528500af83febd6ad6ab304781c8de606691e8ceb4e2b257e60bbe61b46ca5de6e47c4333c1ca34b581cb2ce77bd4d90094c620e7c36ca2d7c9a9494a2422661ce4341f33aa698d3ff5d3b05fc71acd04ff146ad9700e0227af838dbf5fc47e04791c83a3e1f83063932369a34fcdfb061855cc75de058c935ea6a84ebb8076318ccd196cdc2095a64bb87a62ff4bad02a643f53743fd8879e5fafa50425be96bbafceb53e18eda56f71cd71492eac50b60a14808248e061f65c9308ae9b2c88a1d4b671436f886585e13a67ddaf3370ee105ed8856b499b1e4dbfc164a7c3bee8455e3a5717173d886f8b1c9bc0b890e2613a5903bddc02fc4015a2217befb73e0cda476d7836b04a62f298933fccf063ef8f7b885b60e0a83fef6668441aee6a1f240e1e1f7fa5ff7d93ca73a8062083849f971a2b02b8bf39a60c50ed0a4a646048402bda396872d3898b959aa3e0cc541e2995a50fa64e013e695d1e6c937377b9d95d3d9c565044833594d6829034ab0f668fd72b2e43b510fd9f3e2035b807d2dc1282beaa087a76da871f124075807e0e9c6a579e950e32a1924703fc7978050e91df7657db0abce81fe27910a44b4eb5aa1da15dce2dbb6651978ef39bdc54a435fb72e56839d8c208fb26c02df3e7fcaad39be9efab6a2b565d2ed8bb019de45bceb019826d1fd7c8abf740fe66df21ef693bcd9ed8a65f389f82b5c3e3a8acbb93dbd48fb6b4125586a0c00ad779adc7e7805116fe61ce2cae6341942b02768d71ddf10716bd8cbbdce110943a4f370d3c5e5164113bdc958efce25f422e34535169fd7e2a5a119b73e40061d722dc03c4ea9f84fa71f0afc2d5da6048ae83e4cd7a5d4903827b94e824507abff1774d9f9753f319d908378e4109737a7f6c8518b06f9f1359a1297692b0d524badd2633b4826fab4b76efed2afa5c6ac17995aa2eb4a7be98da1ad301d00908ca8bce4c5cae46e90c513827800b38f4ca322621352c1395fcd546a9da0046c96564b1d74180a945360040eff69f50b0b6baaef8e78a3ced1e4f69af1480b0c25baeba679e5113584cce1b50d3ac5328e6e8910eecfc9398995882dc5a60b95cdee4ea08aea67af4d35f48fb487e4099b3b674e80927622840065984632083fe87ad4dcfc4cec613db260457caf18dbb8e8a5685c395fd75052ccf62c010c1b10e4697724b8e08f55da030dac5cd5a8772d4174a08e1ec49835f15cb3ab06b0a29fdff269efee3ccc5ef4f2dfbb6ff93feaa7eec2de71505b73fb7a97200b209e1491644d164ddfb3e851830d3807101b789486aa3d521d97891dfec7910dbd635fe111e3b1f0c917e9bb94d4a9c182dc234eedbf7c485ccbea42504d330be994bc751ca24e5f79069cd8db52e7bc31e66aaa5f70d34efa45c5f23e066509079d72a1694bfb59988ff1112612daa9a778005ccae7ef230c363f314e9f36039b6c6a32e2fda434e67ee7917e0006f21d63a2bd453c98b358be83d56f965807b384826574cf17847e6006f4cebee4c79e32fb830cb1fde79f3c1e1176c86108c0133b6e2162ce57a14f165970c81bfb896fd67c19669789885d2329713a0e03b3f48f5536cd40157a6fa3bb41dda393e7cf6fc39b5266c31db35fb9c076210566c7359110fa0ad12edc7133ca124135f2875159864d8ca6286b007c96997d062a06a2abe03a14b1d8a91d22ddb8cd01800178c61473e3b4814666d0ac9e340a901094641e0344a80093ac679c3a8e60a1db892ff7622eeb534399e55ef3c10c1f09fa61a4b50b265f1bc3f8ad4d0b782f197fa4415a9330a353d7b585ac1f024ddd518f10c70b899e8233e350bfafd6fd28b98f408945a75a9896a49ee68c064c09164b6c6f9d370023501c8708654cf9d337be531ef9139d74c9826260de0affe767674d344ea40519e3b5c3b04e2f9e2c57562a42fc6ab4b6371ff2da29038516fcddf9a1050b437ea01c3dd16afa61b501f41a09f2eeda7d6fd0f1786a0861cf55e4468b7239fc95519d53ca2a4e9ebb057ff40310ba96d915fe626aa702069f3f31af24c2e180faec0a8d13c2f19031b09cb50da34328003b478f18ff0f797ee189b7fda2ac3c8e0968a3b1b74ce4a22eb3f33fcfa3765c3f747ce23002ac975876f91a5f3c370842d1e21b4ee4d62f73e5b4154b96077581a7d5a847006494598d42d5e16fb06eb4fe032eca6a6d92e9ade285759d4d26c722b0d43b003248591dce2dabb81a79df9125713d1e30f9825278a98d00337581a3c1196f0aa9a1c81dbde1b9990ebef65cb06bfe8372870c89e416858408b450ce0e01bc05c62e0f418e208f5e4340bf863460d3e22be8d0baa74a335f9892fe0fc6742102ae96d2ba363fc359c02ca57f9be7382dc3ea6ca1330072910b08558f0638fe019068a4309d3c4d8495dcbb6345762178153461b292cd66b351061773524944046c9c8bded8ce6b638ab9176fa86c4b78707d17c21db37182cd2cba6642371c093cdf7a3c92aba8c47722dcabc8f652026270e79f446552832e17cdbc1c827000e34f6ee75eb8f5d17fe2cd8f7021eafa03667180bea60c9511bd4e9258461c04fbfab48ef727d2e9164b7bd679bf915629a2a9f417e8c9d7ad2a1f43f54f140a4f17ba47ffee7db0cd7c875e2c21be72e06a71b9bd9e3efe1b6e4690a0fbe70c4fce43d88385f9eb00057e49250e8c0dfffd4de51827c99ce4d004d37f9778036b54e7e554b6880d904b9f7dec89daac5b5ab47c274ae83308e149268ad088049ac8b60bb64660397b50bf23f8fc9dc9c3b5c80e7678bd3143307fa07df9b606e5122451af148dca833efe44a8cbe2ad00dc66228218ea790a37c97757d8a2099231480cdd4c815bfe4b67227dc83e22aa3afe9c800bdc41971d208a3a9e910e9b95c79a36b1d48d8e7991c7690a3fecfc26a7617000422cd141b8fa554f2c04d86cedd63bd61e66e8a5b69ee057730c3ddf887e2b2b5f8094541e0bd034220cfa08d4b1e7cc69670f9cfeff190bdc0012dc370dda70b05ac1471ef7fa18b904b70dc4ef3f6aa3e3cbb71c2f86c98247214dd21d9715d6579d336806ebaa6403c55e51ae6c0d67fcefb33c3a4b06e52ad1dc43209de3736a5469783d1d52100e5cefc0ec27f369055e81f84ea52a478e638b6085b3b5c0a046466adc0ee0b8062084ccd2bb68ae7dabbe0771b03dc23424a69a6a6e428ef59cfa5ce1664dc60e0870f5fa01d5805c043e74f83739aa514d55590b81639d26dfd069f07bfeff07eee6d707adf4aac2ecdfe7277e0899dcf9309537fa64649d94ec091f595b9008342576c1f9f09b2b9eb2ac317d0ec8a83daf8555cf8ef9a043dd8dfcf070450f2753a6ff0227ac9d584bc2381e2928e7a076e7af47770ebe9b3662b8151e5a06a64de5e2a7c1d275202d0e87c21035c31b0c10bdbad39dd687436d0d74f29c02d3f269bc55c9d67a88b4801506ab317661b4e48ee336117b62ab4f664bc2480a7c22f3962c5dd441d139d8f421e648f72c1d83c9a7fd6974307d24f018e73e033876e5eb483e7e8ef7115d992fc92f0680408f02ef396d79376a32112cb0480b89451e3d069ba7c7fe1919f23b976dbb25a15920e01364bd43b500f43951fc0bfdf4461d7740af217bd1e919453620b615acda9bded9cf6ae287212cf9bf840fca7e493ed758e1620c7fc0f7bf823d5a953832cf7ef7927d6ba510207498770adf1566db6d6af8030323f6b67708a651e5c4839afe3655721493b9f6acfbde0bd2db26292294032bfc61d167df0d1e78245aca1b43d39a8e264cfe3ab8f794040fb6642c3f05700e35551e7532fdac70b6d2e1fe595e927818f95f2aff49db07b5b4758a88c62964de45de3c9dbe0c6b42e4bad19ac8f6f40b1fb4b43b2f630b694c773701e4f6f1c41d3a8c5cb2c34ae4df02ffeceaa9f9408468f389aeb2097549362861ed6534395a20b2372ed0bf571c7d9e3f8cf1e5249f2a454726f60adff7ff223c1951d567a4b36c030da0c80a1129ec077d6bc9bb76bc2f14e5e00077e0f940debc975415b76b5fb842b238ca34d19ed94db2810f47d630c28a2500bc1405de6067cd2899cede9d0af7d7431911fdb8b5efe203988cfc749fd40d06471ed0b12a4f86a7c5099b9938d3afcb821bc3f7dd849b2f01ecaa77963a0c063802fc47de4e717eeae17bf34a322a0bdb6fb19f38cace9892f661a49892cd08efe624d58c92bfd863aa0152890d5fd9551fc0494c3cfd7702e6b63e8001980d1fc6ee2bb29c7a2e4d4803b31cf9ea67861caf6e60658161a16807b9cce4470e32c45f526532a856ebc56e86226f80e0e30202515a3f0bae14b04edbcc392108356ad91f68b9151f2400d239e41e39d1f33677cf4972105efd7ed049b1f91c02b9e4e530ce8df216befb27bbbca8c91e115fc26a3127ed3d30aaba3c8307cb0eb058254d31a900fe666792aed14ae1dd7c0ea263827efb41ba29131dcc02ef0ed26f0174d4ccfe0b4dfb22a77c4c9e13ef20503dee27127509d5bb389ad73905bdc733d78b9f7257b54fd019dcee72e785a7369d5e427c12c7b80dc8738d1303efab5c5792ecee1820cdf6fbfd3bbab03d3dedb0011756b22350e5858acb0e06b96b9e276046b535de54eea47ec635b90b2500f79f8cb5c167605014b3eafc052b5388017192cab3714624f9b3ded8441f4edcd217b304a28f02dbc829d64a081349d7af7bb55f2c7902f5d0b6fc7eb09c6aab0419de923e95223d7802d7d40e92ea37fe6fa176d66e1601870d1bf7abcae8c727858a896db5f4b5b331933a07db7d7da31dabe3e995a7b8114de1f61b8a3a29232a087197913b01d26c102e0e7dbcb42cf1fbc6d2c82bdef646b83e425931243078ba10c84e06adf26d28be0dd008e414baa9c8d31738ac54948fe2b9dcb1e490bf189b8d12e6d0688b69050490e6c1f63f185c68374ac276fcf886dff3cdd2feb87dec5562ed684e4bd7380caade4a60a967585e574951c03f7c693e12534cfd80ecc54e692f03f784419403e08c150b650584361089a9334cf400cef60770126a24272bca95dcecd4f6410cf3a824a67e275b306725aa7cbd59d2ad425f7a620eae7fd8b8f927b46f6ec40b7469e87c47472feb30d6ab2b84b057e7d21df3508d1f666e890f0a634b32df06afa71b0fa39620bd88f70ebb5ce716a423d47f8188f145f3343895981704be03c709f67d47138410a22d42c4a50d074347a9c74de18cdd0cb3e70d45cafb6c06e530e085966a51fdfcd7e92292723c84e65b3138a3386cfd4ddc83dcd142660dced36887f2daca5c501cd4685b131951e81fe05eb5be964354ec5962b52ea7073ec0701c37bcccd57278b6929b47ea61df4a67d8b62e30f876031c67b305df09fbbd3a2e482dd8bc519154c11b755a715d2f87995f4889309674269e3578420781f8806756832ece533775f6a4ca2bbf31f240dc53074ca2aac250479f04c8099afaf61f75a2551ff4e9c624a7c0f1d246b3f1deb29bfa205dc8589b5e2a880be6b19910ef60b667139f6c96336b704b4b9d6d002b86c1237626086aa1601307aab4773d033f45b0f74564ca566eedacbef659d4f2c5d3c27197d513e9f2710eebe086602d91ecac969abb543213906eb522f142934917932726486f1acd2f0748c0874aa6d098c6ac9d23e8bf55abb3f4725cfd4d803d9a07dd100f0d09020cd5d268a6c45b49cc26b2e8befa2a786009f2de966438866bdb98f28754a3650861ffa4f0e1849b482932a8e3f504ffa469ec3280358391ed640d177bf389b90aa91af0cff0e0114a69ef1a8ac8468ee922fc8d4de4f4df0b2598a89c44c1f10e2c4a9275cd21f76e559990c460243143e9a7814a740f408f7cab304145cd290f06fde022959d120b2e544fc30b2aaec53f87d32a30e7a1d8fda9d406ef2db609a171ef0f9ec783233564fd3cf183ab236e795e9e43e6f6dfb3b070cd2428670e9136095d166154a733f695c9601b270b4acfd5e78c51d4179272bb9d351fe801644b6ac45e56e410c6ee8b6242068c4d7c8a8a464a5d189c1dfec6968a8064007448814f987cf084ce5669192403209f7f694ef9ec3cdde69ece24c36fcbbd08e346742a02edec722a89451ced0704e8e5037f0ec8927b418e4acebe24d01a035b70830cde14ff1072649edee86b2a81aaaaa0ad36259f0e7a37b20c7b601d06e9f3c00482ff487281b7d34dcc14d496428628716df86d0ef492d6588229bb0933efd381c12a8137d66ca448645b05e7d491080035a5ab74cbacff4434da5e0511974646dec1ff0af0f5a43f8f8a106d201b1d414eff1bcb6c2a01a72e2d480fb9e02c07e6a30bb2201bad4ab076d5f2e8be82129a5d5df65ed135bb3a931f0e15ad8612e7e77d4cd80d46746ddd79fd6a8e8d94bd8ea3920079a47b63f47207c3a6a3db2e198923d8d58be3c91d2bef132b69641cef3e3e73a49f500519d00d3607288104cf3db075b8b465f0663e03525e3fac73104aab2d4d4f745cd37c063b5504adb682db0225589646a2c860f31a09f183b057adb58982114763c8b800e4284dabe764d67ea08eb3e05f4bd905678081e9543d437473d269943741ab0a54f2b01544bd3394df6672ed8732198050a6962f04d9c42fb864a7a02726f30a3771d4b77a85b51ae35392c40a7ea847b5b9faab4788dfa5bbfef0b788df760aa3476b540990972492af71e7f7beb108e06883538e0aedd04cdabfb085d0f10443d6b5761c878c24930a6fed1b0230fae741d3d7c485f088c0c4d9cdaa5b3e023ed225aef574ec21653a5fe66b870a6c3c044cf2442453c9095d2503b5dc3d0768b1fd41245ac697ec59fd84a7b41323ab59f3560131b182df4eb828c64b2b0b4f02847468a7b2aac953d49be0c31b2b57b9ca2dc49235abc6a65c05439a8001360e1383ae5e232c8fe4da41c1b9bc76aea4c307c1bfbedb7fcbb7bb1bbd0805cc73096cb8568d81c90138440f35acc6c01ce10d7a18a8585c0ff8f30dca430632d87bf61cb8010279390d8a852aaa676195ff5abf1211b3500167eaee0dc70f67413f227a095903b7d827fe4423c13ee72686e20de12f228a2054d33d597409584bb531e50640dc6919f55d197ef41a4c8664ee314d40833bd7112be7ecc905d4f655b14c89cdd3009632d192ff399f4d1c574fe0e200ddb62c4efe53b1870d4075a2c2feb7f6b6a35159b7bd4ebadf843452734357c20d68693d2d10d57c07822a0b18af8dfe0722ff1365989b49c2c82a2b22c6834ceb7b16ed88748216050de17938026b63db6df31534bf3b72371566272f656d51856cd9b8770a29a007658e4da39c49265e07b813f00293e422f64719c60b9bfdaea89b5f32fe276d08236bf5b10920504fb8afcfa2c9dcc55a395f7b41a2eafa8ad92e4da15c9a6b0fb0d3ad4aa2482248d6338dda1230474cd2d24d757896c2a49391d80d91ab800e3f7a0c15d04c542f9767f2c0699a952e6ce3e21c77cedca95ddae06c1c1b4e081c9a65f3d7990fd4987205a951c540a4f11443e5e0ed6c6778f772f15d94ed01a50ee7d84d3330147d92d94ade1732e2219b5d4953a4b006d80ffbfdafd3b0035377b0decfe8defb5f0bd3a75e9eb8082eae4de293e940a740e9498d328e7a071cca8c6de7f213364a3081296b44c0911d089ed02c6fac286f730decab40eb0404f49c3f6a130d8dfed0d2c80a740bdc0d493d7c6c7374f64fc2789947bdfa06cde98b8131a4c1ea9879559fa06879b88e7c5bdcd75c27e9303d9c3c22b6c70644cd72891c0ee1f50ebddc8f277147dd162fbaadc25d7e2be609597364c1d407c9eba85f766d4e67d03ec0a03a7310db4facb35bb5720b207376913497419204453ba4f60e8bf851c506f42178de8211458aa4f9415b1cea9174e6747dede502764923c6027b8d612e9f010896dd4d788943f71a7a8982de07b32e8f30407c0c9ba9b1a45fd2b2b9c0c57e1252b7967dc34bf4e834fe8d87a88f70bbcc63fd0db1f0e44865fc2c957e05f5d9978eee5b461b659435464faf75a54a9258fb33013acdceb830b58e9c07076f33bc1a5dd819e0427bdece2c08a21f7df614e90f0bf84b1ccc736f8c2b3620538bd6199785b09eaa10fc458a5c344a96892415250564076c5f3f648d2c353888f163e3fa682cc93355253092a3c1e53d50923e1700861e2b6cc77dab48d7bfd94422d62d782478115bb3799cb24136fe98b4b5e7028a1dc625d5f6359261e86d18fef0182d4200387e7e40cbc0feb8ee6a95bd370d0d70aa52e677e2b0c76bcd57769a6bb9568cd58dd9d567f0be6e1c8bbbd30d0b5b9465d95c2ec97bce40d1b84a1fc79d65f31d75fa07abb3bc58f3bc009da60004abb14855aca7225d7d225a4632a27a3f0d259acc057f6fa81bb4f6ba83880a7036350640c5fc0e3e52e015b791fdc5d1f2807b26b73ec873df58df0bbb240bee59736a364147053926e09dc33edb36ffbefd86440b06aa8651b6268b776300b81c7cd795fbe311f4c11e8bc1dd8819f6d7c3e28de89d379b339505d142fc0b316bd99e81ac6a07bea91490e3b926add292c6cd131c9b7994fc1f284b57e708f3c14b60aa38cf49bbd1a81fb320115e2c9bf19da6ad987dfe1c85c69d32b005b90dc6d7051dbce5be0fd6b64d0346b10e626e702c7e102642f41dc8d722ff0d3d989d2e96f2ba5d9ad66c60a192f1509575bc68351c2c6ad34ca920e8b4880f9b613247526aba3bd058725b816eefbc0007e6c78b4d3cad7fa7ed09babfb3069d79cd42e96a731cf8eee9bac4d760e3a2ac1270ae8fc717ec40bfcb43a10f01484df27b20807fb9618c2d63ede72061c85ac95d9317c61e0b2b12cc87882f0336d2884a2ad21fa1ba8b063f11a51e28d50858cbf440ba85935bd83b1a2a730702ed3c93a90172de8b711973cdc3d76e9570c21b2f03bb91ff2ac0bb12f6bd0e8b67605b5fa72aaf4d61d2d5c95e8186af05c23ecba9e1527d4e4f5f5f302a0746403c93d28d7bca1ab46c0fce111466aa8f8831f663b194ef4affe57db8630925075d5073666938cf47dee932f1d100183d477b20d8e92cbe8c129ca41d53028c723f434692413a2c9281a4ca8ebc301a44eb80134e6db14c5d4b45ce09a50fb71026ad278dbbb0d1d42cc567be136f319e5036506b592028dbec12bb399401450b393b8d7d201e61bd389bace60f47a04748be4e157ae567b4029f3156e50d9896faad5c2ba17e2e9498de8f0fdd886857a1b3ea6ae865a214d4def4cef60929fed65cb1d648593ee1846742e096f553de31defc8e738ecaa916775b56eb03b67de798172a791d7f625b7f813756eb5797069ce83b13fc6324c083cb49de09f5a3f2bfed81e4ae993b10ade40b2447225c454f441c8dec2ef58aa835f1b10fd48ec3be929dfdf587c666adc01c78dcfaacb623041e17d9b11045b1fc432c00b7ca151b2229ea6515de45a94766370600b51a54a29de19a53244e007970720f1dbff66c53c998f66341b833200ff07982e3f78e7516e3015f9a3690cec0b60a134a88217cd7496d2a9c6c62485220a0c043df1e7c8bac63c9558a688d767d05eba3dcbe5d9a1e632d70844a34d234723bcc71cf4db08224e0103c66626305099c59218b8dea2aabe59decfc85c4bdf223983525e66a7947758882bd8317380a2b0202fe4501ea34a22d12d06c3b65749b319be2325356cbf56b5149eac0200acd5220f0470702972ac6238f8f60c5c4452d793e4a7f63994719f76cd4227d032bd7d48bf0dde6f82fb3e8fadedd8501e0f74a9dcd61bbf89ef90777f0a1da066638c17ae6d219719ca42eb9f61a308647f177fdb9355aadf329abf13b451a04ee5ec029b01bb6f2a884566cb0b1c6b04fc8f0d9626ee540f72f267e1a62a20ec69e383c80ed2b2a8862d1c822859b0623809583052b2b2445255a5120d05a0fdb7ba3f4c3ab8a31355b496f53fdfa205bd5d167de6450858e1e0a7d32e64604f9db61474e2f6051b840e1d0c262988e279a582d1d02d074740b7e911eb20d0abdfb663f59f2510b4f86c82b7a6e12c6415987b4bcb00e9a2da9d6e7149e250fd4dcd442f1c30319045b450a4c5426f7bd4896b8f2171270e4a4e59240aab4000496203353299a7e8a926dd56d5464b105cf71077f9c628d80bdedf4988de80db7bf00dd31a6df1af1372e9105d0b3bc7bc6b313a947aab686dd6a64e68d1009fe68aa37191e2412e5d77dfd52f0cbedbb2961db74b37a9cf738cb5d0c702905ff4baade8ad5e6beea61132e30f6992b7242b17cac8a4d7704e582eb5bc1510186b136ef0b192f2e9e32e0d7e736fa0b6ffb53a301b026bb0dafc3613a6946071e53910c8f53250fe325dce19281b08aa02e8ba2381fa791baf05cc8f3b0820bef443cb0b2210026bec622ddbdee5e90d2e03e9e58d5e8fc8c9ba8fa495b3b09a2beca1fd3a1f048fd39c595b351a744e6104840823bde92c750b23a0e35b3032bd112a703963b3d6f664445ca2e28d1ff54d1d4b52061bfe81984ab771a100d22a12b59e57c02a5f4ca7820e9a3eafc52df75854385a8a453e029fa227b37069466c19f842f4bb74693db57a5f8cdcceaaa2bc342187715195a3676654b930f843835c4a145efa077a2611cd57a9209a4dcb0f6987e783d73c26f7ac98b07069fe07ef02c660f6edcbc1ac82a6fbd0ff8c13bd4dd9930642cf9c4d26667d9003b90f7a7c32ff61366c5c793d5252ccb81cd95547eb8462433ed1524083e9f0cd7aba7db439d259adac63a8508b5dd07d7b2cd66668c0571d350e529a5a88403345ee4f39824cb6a619cb0224c801fff335e05fad69c1fb0a10f9272bc99d90b4f592204ef5c527c453bcca83f3c88329ae0e6cd1875a8e3452d3ce68b42e70e72362fe884220b7c1fdb26e8bcbc74be97147ae2c24a6b41c21733b03d6d9a062c6ffc0107f2a60d49b8333ffba7692d2d56d1ef78f3e806158cf1530850640907748589e95d14c15a59e4d82c1ec586caeaae022ed34b211da5bf71a916670b199616512e075343e30d1371daa279d27c5f041ec015500e05113c03e79d6403a77ce5768ae82c89aaba9e4a31c7f6f42e710e4924081d1dde5336888db069081d0b1c31097e990bf99a6d4f37f3fde3e3711abf486d7a8cff82f109fa0bee05d187b7785d72f46e3ad32aaaed3f1ad79674f1175906f5c93b47b20e456a7904899596be5b7d7911fefc07b741b5e7acffe27e56c4df7ea9c3508f6f810aef0ad94ae2f4ae12965e8447c437b108f13747163b0f4eb3234a31dd2dcfdc6c630c71c092839d5dd4d0cb0620309308a3f9e8b13dfdabf77a2826a5b9c6195187096468a31ce2bad68e497343a29cf8ceafc62e92ac4818558353ce4b8cd9ebc60e7a8938861739904c83ffec79fe6c5a538363167bf8c97576483b463660c7fd034afb30120c3e2f758702b875341c6f815f025bcfad7c5de935be294d2433ad0fec34ab503c3933839e256097e35c39d96198135de8a2869af3c7ec02c51cf20a9af80cbd514aa1f93f6dca25e149c1615288e5c68cce7fd1c7700de928f6b9013f960da24ef0ba21c908858718179cb1e1fed3e2735ed09cf395564332961306c37b2686e7a3829a58bc27e6935dd72aa672bc0c386e70202171f529d29f4a0595a030a2cbd46ac5dc307934575a8e7de78eb64d736bc22c942e9725dac370017a99edff34c9526467295c82c2f1ffea2ef64fed5294389d96cec669d1be00026d1ad70f85c9c8105373605425dd8a09852da7165fce5669bab758fa97bdff0ee716836a1698306d5a8c21385ec05ed9c568361b49b6122110d66db520e36a06d3706475176bbb5847d964a9597568f227ef29715d58a9ca163a2dd4d31e0700087584a602e5cd8a05e51b20a33ebfff19b8e1b443610c74662c41d552a4d80649040744cb054f12e1da174bbf57238378eb12984dae6f07e963e599d8fc7d0d4d83597623d451d92b3b84dd87023c112504ff4b1de9ee03d325c02e0dd81b00a7a1e4b04f552971bacc9116d1822ea3383116af3d70bf215f8f2db91be09c0479829bc9b7b06b0df35ca97f0509379976bc644036c14ad940a798281eb4d201679989447d49e1a5159086ec3c4337db706dda6e235f1a73d644c9b9fcbb430b9d9960f89ea50fbd79e268335f7f304cb6b003a33b60c98d7b5122bf97effc0694784b2c14d8ce186763ac6b4e65ed9f21d1dfa5fa3cb69bd4da7ea16d0c19033def4c741420d36fa6f1eeb1624ef444358e012c427fa23528530304de240f0dcf7f9b993668561b74ac0544c1cba911008d939d2cce3b13b6d3d1aba960a8094320e00a37fecc9d3a21f15d3fb85e25a8dc0c18eb7c1cb7b617ae74252e340cb81862165c74e3cb10b5dfde35c4eb24d76aa181487765809041d27acb88f30895a6400dbb370795f0a9f477752c4999ad99e0202e9e41e15b54e5410fe7ef00504ba5f16eb831239901af1cba2d7f6ea47007776609d7713748f78c0a8e350d845c24eb9bcda4993485f73a6a4c1cb117a74993ad42b8770a2946df3312330c0995e8fd41c2170f2373175182cd1598aee5018f8a842d541185017e358aee02e71b031fcce4c8c7ea67c9166499ba089afb86f20dc381527a53e8d44cb91703aba551ee977514d9dc7c6757a05b975f6a62ea853f9bff3e7f4118ea1ed60e03ee6f3118756eb6a19e770aa403426c0430e505d16e46864e944d072f7334030c72e670c79d7140606835bc40d1b89170d3369cc03cf4a49e3ea8411bf76cc40ede9d5e8e5182d2cbb019564eada0fc9394e67e7544ade1581726cf71a10d4300feb2341447c037aa9c6129be36c0672585fade2fd6f8d3fef34888ae1fc6790723aed021c02544a85773ec2bd9e9dce321de5208fd36a542fbcaaf41f49cf90ec40be04e0d5937edfa1b28a62c04bfa9107cb25d7d16e2e8becf8794c9963c0b88e0f2d90743202ec949eb5d52d27e8b54114f5ff0a91694b913f740c73fc30e0eadd2c437fd2d168515670c8aa7a440c9f6640e644e80552d42f120afdf570262533ef4bc970c24369ed6cc7cac829cf7f29a5ea368d537b0cada99b775da055898babd6297365682ee21d028ae4b6bcee3a72e043c0b8ace00cbcc54381f0bcc26bb8d1c176c69068fd1f93b89d76841e47f70229fd27ce2484a7ded202300155b6b8dc2ec2b249d84e56942808a7292dbc82436fe0b60915800c252b660026c15e2c82641201ed97f591300137ddf60ce8be1af44504b78109f53ca98ce00a777ff620e5d9411799393c6ed781f3df136b54ada84a36665068b4cba6502053457e65fed4f19f033307ff36b29d053c34e12d0c2d52ee074f476665294420aa5ffee21417cbd04566db74d7094b4c2122374621561ca8b5cca3e3f8c42f70f766a6cc30e122c63c447335b3f6c629af9e86b9f47a2b885afee8dc29940a4005b511b654bc57b3e499143c3091195a8b4f51856d9cc0ead9ed483233f94890a64c36a2c60a39372dadd29ebe0485be8a3f7ef17cfbfa4a7a17bfd8d9b315a0cce9c7cc0765e74b8803c3308576419c8ebbf0a3131b8f42686180eef8651a90940460cfc28c92edfb43d45166fac0dcebcb782ca1480a84080ef116983cc82054c2a25325c38549c581a588b5966e35f4d9d17c3a4d534241cc030db195f8002b40e3dc859e1268d60464a82b717780d81f7a92f8223f2447c746db679b44e05931da023b511852a64063bc16082696015a8504930d3f8d0f578ee1911ed71092a4ee8d53bb4047a7d829d3a469c459e8861f1f32281fbcb6d7187c5ca9b780bbf9924d5b4c104ba5f6831681bc5f9153f7a31a8ef2ea920b5f243f3b67256081fe20e993e77999ea1c155b7f0d53221e50b9dfd90282716088a11511a3e080d517246a536431cd7a40bf1c1b7218944e59ddd99617d40c3ac5fca983112ae08cc91d7a044761c267cfd3c02d93e8b9a6cf5ade3b2549b6fd1c8f925a77cf002323f6fa61bafdad3b917af988652b85804961d4328bb1fbdd891a82952bea606c7a36849884f0bc2aa51fa7d6c4210c04d06d36d93d5ec80d537c3ca86b0dd0c1563109ed3af3838a1a08c7faa52cb185ac783e7ad9aa16cb29dfa40a10457014cc65f3509e229040f9d8a99471d34c1ef991fec98b6be915b442082ace1910fb257aec798b1df14be5368c4e4bba8bb5122a09a0c883d5d94179eb190348706d0c67474b33e1266546e1c4255bb97a2526fdd19a1d7e9bdbb697f4061b96105a40ef14680fd0db5e226ac3399bfa97b5a3eb2366745e18f7911c58175f26a0f0aefe0b0405ef9a82148377fd4b324f96f65cf7894703a933f4c7546b441fa0f39fc15cc2a31747297f08656035d84e86cbb2809cc4d996021101bcf452ddd097c4b428be12027becdf34410e617768adeb851f2c55e57a514106b760f5af406e934af4a391dd6e4c36fd6f0e2e1d9109b3a53f3a56fbbcaab650608a1366609c94263731932784fd6ce96c3a96af26228c71ff794ad514ccbd690b19ee98a01e018ab11d5a27bf936b57a7d3f37d1f858d7b8f745c7756de4a1f5d2fa086e0e8ae3ff45349021bee4e7f1fa713c8ac2a6ca87d018618bd4c4a2ea2dfaa78b072b8e7059899c6cfb3744f5176016d2994ebd1fe2d68196226c3539bb6c36650577d79fac0ce028251c24430fd42b70094ac0f67a1e7281b5308baac01cd4670c12cf51e24b2949042418907eb7e871303bd52ebf0f62aa7d28d0661673a45d02ca83c4b625e4672be045072be0ff67bd926e62af0a2035825c922e43765441067c4e39ac73f4d5666f9a725f782af8aed5714f6ac024f8f0e4bce407b47deb0c2b87f90e84dd2d53575b4d5811aa894566a86c61c17376f5ddd1982304c2fe09fffbd92f1e813fb655177105d84e595ec8071a92a6666bf67c4254667960a9049d0c11daa4b48df837c3400c3d229b77a7a3e7dc2653a587fcd048d42de7b805f06b111745f05cdca884941017bd724fbcd97dd008523c89837d4ac0851a83014dee859cc652fcc7e50709d0ffabec3cd3346d43fa6af8e0280c3ecce86eed09376d8c60bff37284fda6eb039b97a77a0aeb0e2c5c10c02161db070e8464da0b1f262f2e4661348f467c83b3f964fce08245750a83893d19d88d93c1d12fbc01b3166bf1ed540b884bdd2fe375b43264a59042fc0d480a079802a7abf572320c32fa369d56a3a552ff842616ef3535e65d371eb0c8a4c8ba22c72dcc3a3a6501b1c3d9d5c20b66b72d743eceacdde86343eb20eeb51fd7ce5826f96ebbd7e309f7351dc1aace6aaebccbc1386a586db51ac02de4793a99eb3f123eb216ab010e9e5ca24c18338f76d82d1d039c24f25a51b7e0594926b70452c5eb0109ec67038354a443102073eb4f488155915d52bc00764183458cbbd02b493b0ea6f35b0166669e45382f1735900279b3ebf8ba04727bd538c92135a1cf1a2be835df150a2e74d387a7affbf9e9bb47f7a5ea8fb0b74fbd7f019f2acde479dc5a11051906ed34fc56b0f4368aa21437883f9c66793d1196b927f5ee4f18015a5db2f9bc01f7f9f6d83f00ffe7339fb8b474f017d1653d00238dc92637fb9f26749d586b07a313575b08dfd1eb2d0c50f57e0ca7a18fbb1a482a28cca9b7c41497493feb06d8afb171ff9641bc849c1173a02ec7868ea66ce23406c31f06eb301e5be5840f37423df2b6a089c90d2e1cf032e581ab6267fac12c0a99d12d8edf50e8f496056be9fb12778f5f714a4a219d68a20cac20703b2f40752bbd860eebf4902b73009eb32d35199f4e113b10a9bc90f1a98e2e6a6c82ae8ca1407ca78451f72b85083e810bff8fa51cf4b3342c243fc6950d27d609dd2b51db62ad3c4e6b26f77f0c8afc6917b629696f3514dbf4a6f4b0eede7b5c5080af4a8defabb1b56b6c1f0eeecd99bebf108d68b911d27f76c0354dbf1f3c7425958a013e8ca03845bb070386d62bfca462e5ef4f5a299981b64a1cd24132ccc5fe22de3379f7990004b906680cfb4c339cb3396e3e45b7777aa1f52b0c21d1b3c27a5872584471abf7340d6440a987778f37ce25855d36b24e7728cddb98d407210bd39a0784da4916b4035c450937eacbdc8f1de51e8a9ec9bdee07fb592f29da84d352f73d3db549db0ffbdf29fca6f55ce766f25d9d88dc720142671bf850038cc034ee923c56bf9a0f4d0e8a8fb207a3b217a675016d0ead340e6267df845d7f14610b94eab04b4e051af17ee97d9a932a3721ff1dcb3d0428d3ef407d417f4d2e350de79b8c5d6407f03e2af76ef179f0ca9671e322d0c5f1297966d2a9d3d7a2db0516e8ead843026e85eb4aeb72ac6fab52cae39035fc7298812760aa58f3756c1d470286ac8f075f2a2aeb8103cfbc1c805b62b90a22d5979210fb55321937cf22ce852d16b801682fc4fbbce6a73fd5b04fcb3474feb8953688dc9d34a0eb06c359883b766c0246539eb851cf36a107b246fd305bc5da54d48f08c44a766659bd3b5bea5352090a7e40f433789b42c8ca0375d66ae0a260b8d33151e21626622bf014ef8f1c028789b819b3196117635b0405901d5ea74a595aa41f2ad3638674c3e475c94a03a1f4afd2f1e35ea8495f7071ccf8e458424745098c92b5ec3d268936e5615a0b9e82bf78e6a84261a6a68269a34720c462b5ca3563ce4595f59d3435d15bed08c8277b3a3094be7a02cfaf12586c1ba6b8ce1daa4d66f4e25edbcd54504e3d02149f2106b8a694cd370646a899f85169a3a1c56c4517bd6264575a3ad1353e01195218706d1526977be79b3102516eb271c72bea9766025cbe4b0d07b6ac94033487a5b32cda7e070023f535b0b11e32ba0c4dbd62608103f85d47fc58c2a40d061a2a10e22d2ef0a20afe44cb3106b0142a713d5510cd17bf6f742d648a850fc9ccbcf80c657dc437187c9e038e6f0dc99a0a00ed17ed169b0c6844ccb5ac0d112fb298f4fb2c8a77b7fefe5460cfe6847af4b7f47859719ae7fd6991365d09a4eb66c77eb698a42f92bf663aa1e4fe0d2344945a48a994979de0758634e90f2adb927562850c0cf91b973ea1fb2276758621771db10c224e4740b5d821c00c false -check_ring_signature 5a9a0a48720a3d2e72605becdd28686fa12dd934bf895f45b7344a9a2872a00c 9ee8a069c362287ae2d0d99807ecf3b575dbc1a80a71e50724a0784ebb74804f 46 e191c54b27b199ae24ef52dfb588542ce2298d4292f38ce789a13057ec07c95d b7f9e81e7da0611deb90cfa9992ec3e2192d97abbf3e1ca7b04e0fe0e81fdce2 55faee4b1a535e7edfc58f4a8f4623a71fc941969a5f57a9435dc2d0514814c3 4302d297996d35d929270ab7cc615d54559d8553adfd63e61d1d3975502b0801 fce7078d786ee0d8a6b42f2d2836ed5e9da2de251dc5127884eb508818724009 24b5e7e4a276f71d904edaac4151a653292ef81727580ab128ca49fea65d511a 64b0b6b0fbf7b33a2d86f5e52db7710381450aff22693aa637471a7420481e21 2d7a4fc11d17b27c99d6ab8eccbbe97fdaa4b88e00b027fa772ecca0f784d1a2 a9a8073648391840aa216176561638f58e8ef039bd8c536850a7cd918552ba9d 5840e8b047e890b2cf6ef1185198d0efa96b620e12e116a46089d7d3539ac4bd c34b311182eaba6866aea210db26bcdc07fcd1a850273e8bc075768ffb4752ee df4bce9dd5971a685086e11cde6a01b265aeebd54a0a8b9ab220c509b66535b3 4cfd8a5c10b69c27cf0de3517d51fad60c1550c271fa59e6fb7be3449a294387 be8ce8d10ae6231f06299cf96bebcf85e1094a4243fec4e2be1851c45e4c4f1d 19e1063f0d942044e371b16e5c7cbf5c7ba2124fa381ac3933dc40d0a6c4c198 6cc8b55601d126ebf71492b960d99af01699eb8a8f0171ff8be9618971c129b4 35e7648c9904a45de453046db0047491d3aa38671784710a47713dfddce5f7af 0b98d48d90173910755b1b699af582c208ccdda64908c4d77d69d0fd388a3fd8 c6dc380c33073fc2515f47e71c33ec0dbec961444edecbf5024e9554c6d24e3c 51ad43f81689cbea9607157c790491523b19ea896c2249f12ad39251cda4b9e3 3b3b52ed5b6b0c8aa9a3496f85a1509770838c42a66e3731ccec97a32e8254b3 60f655f6aaaec6becbbad2b8fbfe8bb41f69eee2be8d4449a5abf4a868b631fa 0870c0f0bdc83c42ff58892295e7718e39f8fc3e3229051f1a23300f9cf3c609 eb7c3f1a0eeb55cb8389daad37a70bbe084c27c5e80fa4dfcd32c46b519edfaa d80cedbdbead4e1e8ad72de6a17475f966009401b5194217b358e608fea02ca1 f3a8a945806a2714a7e44fbd9b336b9e0530c170710b7dce23c68735c1c4c525 a5cec0e8362f73f90706dd56686275c3778d568c4a674091c66c92b13a7983a5 54214a593baaba174d53ab1fe1ab7132e957f7bc81241a48c4eeffb273f26ca1 cb46a71f3573454f7072209d9aa8789805a5e9900ba2c04fce736b265d1e45bb 71b5be3253f51fd8b7483b21c7ec68d83948b34ddd4cdd39a5b6cf1dd935a524 49c06b6d54925233ecc9ed4ab13710874c71efc2598beb2594911ac6551bf4d5 c3df0055c8b7a91abcff238adeee052770b1fd34ab63c57f09cee7ca9c5bf4d3 4fc65e2a08ea70938e940df321d9f6b98eed474e6313266b455c9e8e77063ed7 c3817908c2cf85ee69efc46929c189a9973976bd215c52dc2ade3e1dce43cc00 fddbe2e8eab1ac4ce8feeebb91ce5b5c2eb4606dac749f6635c662bfd8721f57 c50d812a5ac2f86cb9cbb7bef853b6bb437243596b50f0a1c63cab4163f5857f ae446a653dcb5eb2be63c364f267189b780656032690a917cbad02418dc40532 b5b7e2bd1e7854c56c6bc085a52a659a13487384267f914ae748b0666816fc4f 4bfd340325d37f48f8ae1d35f3fe97f01cc378ed878c7f601be1d52c220f8c04 319d51fc4b07ab3dba74c6d4d2e820a67fa0db49fc1452012db179a21a0b4e32 906d040b6a4e314010a938de210d87f2b2f5f8e0c3d99a60fb8f26b98ed9928d f1f8cc100063a3355eab12bf7389674151dcac1d354e5ea6e908ccaf58567917 49d3aeba4df28ba63917ab193322564f21e7772a0b1fd00ffa1469641d839575 95699fea13d1c3b2bd5057bd37f46e62f4ef1315e05e7bdb670d03d9247ec1d1 42c2205718f098fe99b4a976b345080279263bbcd08598116d21360664d64863 eb35107b6148d1e72f565a28640e95cdc3124828d5f95bc15e88086da6ea0ac1 93a9e77b6a85466e84145aa32e5d608daa604e2774db01f99f2c875b79d98008f58b95e7021f1fa293834f11d074efec02a6b1d4628e308a12e26c4139e3570738cd97a013008c1608373cef3487e8e1dfb925c7eea29720793e7100ac042201917550af11c386b7b034c885c6063117c62f058c5d82cedf01562c0e9df2f40e18d931539e19afe0b26dd26d20826883bf78e896f2d75d9026451004bb6ba0058ad3c764ac86147f9de72672d97912c0f94948d3becc9d9fb0fc84a65277340f1ffed41fa27bd1ae13236138ef85222d85709cf2d9e34bb9eb15c717e45f5d0aae9f778d51191acbe45e281097183881bb31cd4ff3a53181c48b5157b09fce064685de93545fb9693865c3d0fb5ed502d15c8f973ffc0590c57f59c7c14fdd00fc6c6f576a5da5d702f678691a0f9ce2db94d18305ead8662540d7aedb5dab0b5a9908def8e9ec410b0b9011cb0a1a32cb96370e48bf868ff7781aa992c5c300bad7112d2a0c89f55ff8b077662a961e36d2098b9df9b1b8efac99798990b43a63ef032967f80ab5ff23afcab911e8ecabd94c917c7550c125a3f731f28ec007110365b5ec43f1ca15454f4f1fbfb5a29974a33128bc835ddae63d198bb51909205c4dfb63b873845057cd708953053e48d32f7f8ee590495e066ec7ce74d2029e43d19f42605aacb8395f2be5ca0afd4f05c38cdae3eedeb7925e2b3be01503a9009e625c04547e148fefa13e0e3a644e033aa44b9ef0f8a4e0d93f40c712098418d3b5bcc60b7d1c432b7049a6ecb94950d4c4424363ee3ff9bef27bfbcf0646a9eac91812b662c5053da851fec623c1fb36537e851df67a4bce11d4db370627be4d478ea8207b14cb54129199d6380dba3a5f099f204f7f582dfa5072cd07c5701a0d075eee3d256a6b3e18813512724cb5be865c2b204754fe1b43ba7f0db44eb7f0790cb8d6842dc509f25b1b5f0f9617a66cc47389a376c0da6a8d1e0dd761674d5c1aa73267376e6b8ff4b6c76695767ea2d319f9bcfcdc07f33ec2057434b935532ffcaa0473f1c82b63442f8b960105b02d94dfcc6006c9f62b5c074790447a57bdf5c6f6b1a2345ae54623aa17a74c3f5afabf50320797ca3ea40af040611dd6cc7b92456d13d55f7218ff6fb2827751ce3e2b99c1987f767da8042798fcc7c519cc9530cb8596d09be53e7ad7f1c542178c81d4433b4e91be80070fd096f82fcd015a309b5d47d949fb9c2b30ead4fa3c64704c63d78e36bd420bde6465d531bb305c0711e93c0e9b0ddf626942f00e444e550ad00c8b53d8cd074f8ce50ee9aa770f45c2e005094ea44f71a6d137e551a107371947d0cc463603bf3dab408f049181d09e9c3d0b8be343ee1e2da22ce550fe915f726ca0a3d5078f05bd2578c0630b5a29e16e02b1b62eb5b72bd7c17cda3bdffb97174a5d0d0a7260ba5a42ed951001b2b8b1155b464e6ff6b6e39c9e07f91d485bb3bf740106343ea0b303e53de8b27c97918d9bd5345e66a44de998517296cb7f0e1344db08d090b9dbcaaf29cda6359cd01025876fb005286b8ab7659e40b7f57f8606db079367f3e8efdf2959ca711b467c8263d2d213539b5b68c14ee8dc8e87167b6e0f5183f4f79d49c7b84d23de35074a47908066062ea72329caed692ec9c974c70978da909ab54454fe670f468ef78c5a6491273786ebc558f63d643cb675b9400615fb279a94ccaf7afaf8e57813f39418d229b6c133df215c683ab7322e38a1086c9964aa9be1eed6ec0040e3a52c1579649b65d19c7eaa2a174c7325a48619017c2e6dfb39d3080c6c2805fd272cda6a7eb221a1cb3377a5fe82a71ea356a0080dda5aaff8d196f58c8a457a4fde439351aca32b8cec7200cf0031017b6b9107d97760960ac7e58a49a16c430a5db6b0c7d44734f0df7901b2af5d07e300c40e7e0bfb850ce2da1c53e55a0a42356a8c09574a610b2965d03a0fd0dc64e0160434f2cde3f0c167004d894ce5fa9a5457a06666abd29e0eb78ee233b149aea60696f0376b29ff98ffb3ce4b083d26692bd6f52ea8b2676cfecb47ea11a8dcce05138cbb75e25959cfefa0191a95847d0c02d8cb7cada776b4c75cf771418c55035f04870e865098bc84d4544bd709b3284841bb8c057c40cee26a4befe5ff1502856ad5a172ccb265845b0ba1384813eda456a7815a3d55b364faeacb8a509e0ea475ff1fd2fe6245f07e37ccc98063c18bf8cd25ad592dfa2b4539dc76f2e300003c5131bbee3b5aced7ebc719f95d9f9bd4e101ea293c2285ebe5a74683d50d6bdaaca55ad6779a2937633c69a761b484f3260a097bd3ef1c0ee9597c2cdb07f47a7f77d83a4c79c992b1bb2a528caeac204e0d3d1d0bae8aa3f1a6f86de40bdad323f56a0f21c7aaea12069e44cd365c13cbf0d001695d191b85f32b9cd609d5ee1be2bb2547c473842f9fb1d081cb11e7731cc5770fd47514ffaddc13a70dd1a1579626b8edc3e95ba050587338d4e67bd06a3f6b8768f963708853f0fd05624a6bdb317b5926dca0c168c991bb57b519269820104dcb561a37c40eeffe0004079a2262e3102534a3627905d297af421b2934bd595b61627ffef2af8f340f677b7b6672af6e67bee8366a46e57344a9427b5395bd456b2e8fc30214f0360846a7e6a7ae92736bfb05d7a4818dbe0e5173d71b761c39205c5d648c801c550bbeca1e16ee312d0837c04d5d4a3760543fc5a772862bb84209f9acb850f3c404d9fce19210cdb6ad19adc88ee3fad9db75efc660499183c37336da9fa76d28065d69fb097823b09c424023d444d7fc5cd063d10abecc0870dc257e26e965d40cf57a9eec7f70c3c436ca96606ce4b68befa5d4a3bb5c17388be002b0e3638e0142f6ddfddcc45ac78a87d01f8efb9ae6cd6f93544b1d15256f9bdb4d5c51ea037f7d9dc0b1f33f0058a5f5ddcb4cda62ce759facce2ac4bafadbce30e7ba4b007555fddd1090060129f1e05be8a42212764d5c8bada97e1c7ce2c14bc205ac04c6918bcf0343d0a1803d46322607acd12903f17e9b803ea2c317bd6dc1000b0ebe44377ca064de7ac5fa66022179d29f6654199740db558ca36c7625ffc26a02e9df827cdaf5fe49de95adc5d2f1794e2b7bdbfbb3906354b8c12ddcf85ba009cb549f19335f16f87619188a3098ca0fdc1573f9d515d40a6143dac2a98df804ecf882e96f00f5d995f9a76110301a4daf8026a5fa7eef163bdd9ed3df27430c9e7ace9596f1259739430e43b4b998a4d85a126f97db2cb28138285b085269074c98a53ec5f16af472d2dd080f54a244e18f84dd3e66f261430f54c45ef50c0be7e8835456846ffe47f8bea21ba8f439a3fe98335cd62bec8ec5fe92fab0d90a3a662fd5713e3f9bfc3397df5536634d5103a5caac269243a61bccaeec0b940c1d4a6de96fbfaffe720768c842af2fa5cabfdb1b13febce1ec118d5b5a274d017efd143c09ba16640bfa3a8f6222ef876c06640e7ef890991d5bd1702422cc0445fdee18c04c85e8a9c35551924a0beb8879486debc07a34d6a19ab8c322a90a803d87de097f933de4cbed0856e3456a53588f667ddcb950a5ae594bf7dcb30bc18ccca071ef4c4b18c5b9f4e7ceaae137f9703b18b07c41cc2aa00834199a023097a1001561b4a98ee9a31e908cbae42f6dd367a599874abe604103bd05070ba4a429847dd3cc89cd180b3c5616a87dc9ffb25741e2093a6e87a835aa56370eaceb5d3cea8dc5213cfcf1b9c2c1210aa973b9fe029c50b8ac3df6f9e107120f10de9331d6ad2019c910b03ba9b090c7b62d2bd68e8654797d54ea3af3a2ab0622a252885a6a81dfe66dbf207337534df11772002edb544107190a5117082e08e2ca78809e77f13d13e67e85ce9afddf9569117ff5c6ca94ee91329af879ff01b466b3442790b9847c3e15934423b772591613236752a0d1782f2bcc340ea803660f7ddd3d3c8ce8e6519cb3f13a0d84cefd71d63eff4121aeabfa5cb75e5e079591aa437413c9c7f873f1d1b4e8bd5fb3de84206ebe64fa91edc9ce975912042521593aa2c9068b817f386b39ba8c1dd68239815f45372dbfef06dfce32470d020d0f8ae61bb15421386da5e2c47310975ef06d791f9284dad74e57c6ec5f0f false -check_ring_signature 3f51c0c3e2a19e5c4495bb9a8ba10d07665251c6af8d4821e752945a1b924f68 bea45901c7148d2352714a21e22fb6d74f0e0e1d74a3ec0ec792165ed4aaa13b 27 0b66089349bc93fdad94df770db0810b1c42a85f843603a966978ef5d7c751f9 a91e7d8de911502d381c017dfbc5be5996bc6e20762660e0e03e49b3cf60aeb5 0324c56f6a20a2c77a7a03d1d76299ceb0e83f53ae4939a39bfa5af55d594ffe c92fc42aedab370c1ac20aa57e70ca96f12be72a5c8ea47a69b8de207fd7349a c58b0dfc4c3b9f542a410fdf83242ec812b5c9e0ef33123577f5c14577d7b2f4 7fc16a62fbe81c4027736d1c274639701d82e3de50e34cfcf1ab1c885aaf71d2 ff1ef5245821826c3ce0e2c761d3e5385d5e5e02307b207e693fd04d7e35e2e4 2b93126ebe7a2745531e783ac6658b771aabd1e756c2d15afa44dfeacc902e1e 1faa2b100319d0abdf8bc95c43d868cc5f9c5cfaf4c11f9fe38fd12632c36372 c081af545b974e10cd0a720f72f5baf26dbc1afc1a070a446c1b5f8ee53cfa46 1973496732de9785350ce24780c5c200b0555ae2ec5207a2e999e4072129a693 38bd06472413a3370a378d95e2fb0a0ccabe9a8f17e34508417e0cb9c262246a 8f28ddc65c47f7eac6da6f780141ffd3a80c79ce10bb2c1a170e871f9f6c0e75 6e9b0409cef5cf79bddde9f2b51d0c60b4c05a97227f94cf9bbc2c3a10eb4f9f 920b00a5ee68780ecfc1f1737722c35a6bfbc5d5ec249db2134c017e9bdb4ad5 d1a15a43f2c26520fb99377494cf9bf54a2e4c369dfaba64c1785ddc77c734e8 4e7f14c182611ba5358508ec4e0473be4e044f5fd69be31037ddaff252a6721d 549ee2f3ea9a5cbb52a503062d90a027714f53c831ac805dbd879b3f57da9c4d 43c3b9dc78bb98541d32d29104f158d76d0d3e16a665a4b48a69a5f482a8d5e6 a742c68c6ce9fd698772f50e849f23366c6ce950992944e5ca46887a958cbcf9 59d5d44bd3dfe72a7a6712464becc21a448f0d13385e29a709dfbddaa0f2d9cb 53c26a66ac8f551982394a74cbc52e957d19f1cfabfed804c4a4748e30365b2b 327e1bb4677b5e6e39d868f52ba7ff7b054fdd9d3bcb5f3ea4546187062c8fbe 28b4ef87b509f00c668a2be04229787000c2d986b75667e0d0d106435561bf47 07753924e47205179e015a135b56703f1f97c87951d093217eecf82721232019 7a876162b93e9967d49984f9f6db9a9e2b5198455dab011cf882b73ac7fb656b 2b3423a7f4cdb8bc3128211821f153bca1d67fbae5e6067e3f689bf92c01e9c6 108a5bc760a29fec38381aad61d9c9bd7358e8048233b5498253e1064972360cc1eca6d2e3479d65d5049a72a5d86f3754b767b11030f2b0f65ec6398a5b4e0b5aaab6f2b5cce85321c454bddcdc08293c143da13eb1be9ee86ba38fa8decc07893128617160f72a64ce18537654abbdf84f63f8a46556c8ed0ba660d16055096b12ab1fd5b5898c1d24e6629a45aacfb2f3c5db7faac81e0e921e2bc4622e010e03a58df0b79935dfea27843af4d49c1ff3bff6106dfe0b7a1574a437fb0102a042447999863f7934fcda71662ec3b4b89902ebf57f44d68de412a503dc4608ddc510619e24c6d8913050e3712788409a355134ceeb029020cccd3396cf7f0ecc8b25f39819488c6c10333cd09f9193aa0444ad6d173f66e4e161920c272e0d3ced873059582b9c218afec0e6d0c8d324f74227b4ff14cee760fc5b3e0f1b013b158d740467424abbcd363d34571a36e27cf7cc8209f80ea46f19adbbb7960f5d64971b7f581f2308ba2645afee32c4990f9a4bcb075937991e66be45243b0824ae87b574060fe0f0134197995032eca701f69e39e518b5f1f33817446d2202c6abf7060751fdf20c8fa5a30fba79c53b5bb6c6b60295121c833fcb02d21800f006c46ee50f05916ea9a6977e6c03761464447b62d1ba1e376e279d90d7b10f6f6af5902533099218f55f9166f21a8d0d6e2dd28f08a6a1fe640382db7d59090ffc539de359f2b1c4d01c27b35b886dcd00f4efa0cfcf1d6ae50840c51fc40bcfc2ce00e059acd503c8cf1f6354445606d7cd1f6e2e0f94f4aef162f567f5028bf9fc55d9e6035be4fb4792d8045cf63717dc907f648c08e4a47e98d314380f5dccd7cb8444b328239c10883861a072560932634541d932f36d2f60e6fba60f52fd52ed06eb1e67171cb758418e49872271c7a2be55a35d64270b81c9dd6905286095515ed7db5ac1f66272986a703c31173593e1fd74c3cb24f142466842029fcca1d905a6b8c5654e030b52b52c8fbe1326db45b19b4c0a45f2a552d6de076aa8eee95913efdc078d89c6a7623852ee87e06aba272f10cdc1f05561c38d0988ce6fcacc066e52997a5d3596b63e18fa578c45e4ffd8b70fb004c76d5f550bce343cab9344775cb32b4b07d190517bbecedefde860cd8e77e353fa80d84f0496f1d59d7a6331313406873103b7a2a7eaaf0aea10f1a8963e647cdc249a8c0265d321b74e78a10ce513289f5555b38b690433fd64c4d2a7237d28b4a9fab90ae31e5aea7e322d333205885f7c57aab1bc22b1ba29d46bc1c935148cbddc100e669bdf636462a12e98a23106c73fe9d5e254f906c992c9dd0de25aaa8a665d0acd6e412ac19724e0034c603e5e3e53b8d9db61b3f05a32c3dd27f691125d1d057cbacb6534960524667d40fe1c9aa4748bc60310d5ea0ddbdc9de09fd5de230653de7f1543d3eb9ced8e5ae5a72f60ea62e324c209d542506a5479a4e1202204f49f179861267b4b7fab1f5fa9c2f512633c5cbbc78e23b50a244183a2694202155aa6b3756b20fc0bf758fe5a8d8a7cf223c00cb5607c5174195f941611a3065cce36511bf0064230bd363391ecc40e480c5f8c901260e7f81bbaba0a64a002330c971bb77d943c88afb5066bd02724854b6547154b1fd6a0c4e70992bf92066a7ab20388e1bda47fb4d873651d3d93467175b979633c4782fb94a3aa54760f6a0aab6461115038b572c80f9c342edc1b3380d6db513b92074320a33f06430a69789b5789cb8e889516bf702ef973ecb53f3d6bdffa03b52bcf66eacafeec0839070af23ce3e3752ec68b969e5066e2aa7b7d9c33a190e11d974ab9ca7b90027b80ce4b22cb30a7e8cf9a09d95957d77f223453155d9e57d67c5389f05a99961d53298c5d4eb4d1f758f36213fcb55ac8c972a4d680070ec3f1807eb3afb70a4daf65cca32a9042f08d0a81aa1a394532c0901690533a31fc21170fda2ce203e877d2b4dfff42f7363887a8b98fad1cc004f7dd8ec6f46bb4660f86bc1b760a21a169bd1d1ae417194c8fdcdc21359be82c200e65d1f549c623bb0bb15df80e87f47f6a1e488a630ffd439ead48663858fb94b664e8be036dac9414558fab0b220c23783fa35b4ea4775ad7f14a135bc0711f6eac0b33df218d3b98ed97490f67854c3fcf23c3abd2ea7b30c6caf81c55e77bef24e1939ec7ccf3b00d6e2a0ed6e1e7ef6d0f68eb7e7fd118c9f1df4c9460a9502c2470949c2e88c2b6f6a909ef7ea81fbb6115223c533a3902cd883ec9f6a1c867cf2f201763f2527020090193b6b205dd30b5b9e83a2ff2ab35ddbdd85d16c26ba179cd4eeba35cf6701f0bd57d2f463e70afd8ec20f274db428832bf8d5fc42718ec962fd18e0a3d613300ef34226634c999ebb29108e25158f51acdc713a9c81a4829496b2e8a2e7b6801 false -check_ring_signature 52667215c9bd75812f50a500509dd705259732ad1e27bd9933178a102f8f3607 93181d2d806106c51ca5a586dffeb00cc5242185d8a06edcb32bb385eae56aee 4 21640f1cda5d2f968ab1900d5598dbbef51d60341d330e42279300c0fed8f06b a903a97927c14462469cc6fad84db9f0536fd79b3b12e4c73751476c4396507c 132ea488f37618c7e0ae4e1ca4d4a7abe756bd26e3b7bdef7b0495ea6f22fb88 816d3c0b5989d7a7249a9d2fc5e0faee5314aaebacbe7691dccc3aa36303e805 5f7ae473fc8becf5641786892243f7d7fc146bcf0876e8297ec3953b74fb6b09e2e4f5a34ca41674c68b1d465274678769baac18fec4f162bcf12e4362080464e2542e79d0dc8d51761517515459d547a92bb62c6a51d68545d85bf8520620031696b9a2c192168fa716a28d0792ed7ed21bac4facdd10b14e22bcacb13ec901e99ef7443d773a4d02b972026c399195a566aec4e592e59d1e51e110632be6082408bc96a67b6205ff144d6b149490d7016bc8850c74be6677833e8db62b7106fb630fc8e4eb829eea7028f891ca944e1ab0bc58583ee053107c7e5f9fd3320e81d958a8643b04a4e3018655959a7a5d98468a7b332eb2c298b8c1d478b1990c false -check_ring_signature 99fdee0994a83f97b0049e52e5d39dc642abc174ee98315ace76fe8a04c98744 20fad0a05d50e22f191b156d9988d26c39ece02a1c68f8e11a56ea63c9d0354b 38 c361d426ec07b493323b2e7456a05b133f5782735a53d5f9f94c406d863ccf8f 4f2de3914f6f7e65c1bfa6f3db5ba8ec3849afbd44744761c7f11f44e113647c d3c8beced620da1b1dc8a829d168e8bbffc3aa229a17b9028b6381e8b781c863 0435dc4613ead371d90e1ca9e595957690c9e87722f701ad754445f637acbaae a28b7a602dae9a1a8329ad3cd5fe2d2892a0025d1a62b79e72eb8ee2a4272d21 16de1e4ca841a2b3d5ba1c347918073da9975c6149c8502b583331790ffa93d5 c07aa37378e3c65857aded57940a1e1625281e44634e46f59c70f0d8cf270c92 5440322d2a524b84a9a12292d45ab15d5ed4a1530030fc6c3b490ca4fd4c1e28 04616337882a2e2bee5cbd55faa5e8f168680b5bc941fc6bf0ed35103f077df1 37c56ca499a995a1f7c09003b000c9f8acaa4519aa043eeb05d40427714e6264 1def03c93316778d4a7034204331e7a80143adb69242f3a08719d440ab190bbb b7b123ca15ada8e30616c0a7be14812179e7bdcf4ed8c6794fd1bc5856488b4a 57db4a44f5ff9f0478a12989bf3c8909abfe4af003a5ee44fd84a99d1c939e48 9d45a7f952ab6a190df2b9298258560fae0ac1d82a5303e8d845e125026dd01d b1d58afc977fe0cb940c39ad689d02eca06c1b499930bb2b1b3a33f11051ee3f d9bedda75ca7024bbbd14169c0b5cee39a41167c900812b029ebb6bf863d80c5 9ab780540dfb2619e7631c7073b035403cfbe8d61fc5d276feb08499800b9806 32648f12a4e6b1603d0c70a57633de72a9dc81720936d0d22a7cabbadceb6405 6dedead1134d597e93e1825243fbface6277280a3e36a788c2d6c774185a60b7 f30066d6ef1dbfa313a9f7f60a2344d121e1340f1fe132a528b68a2acc7cedd0 a0ffbeb081f02a292b10cf86d5e1e3b0206e98bc4b14542164168c000e9adf3c 7f591248bd15e2c4ed506a14d5aa27eca75bd8bc21d4cfe5dc13f71a013549f9 8d6ec64a58d9fc3157b389b9a4c47dfadc76b58ef8f9705762309fa922c36edb c56a8a50ec9b1b1780adffc03e4fc7efa61c18f1086d0113253d307d584b0e3f f8ad79bb15590646acf7c43a3e1845eb1130666e0c5d2f12d253c91b8e37f1d2 e3aa24c3eb3f8c542a79991694df14435abac147a163c70025c438c72a44ac37 8000426e481dce9d2b564570f752e40f41346220ebb34910668bf29d85884d1b 8797d1a67e57125793370273fbb150501a12103d6af52816a374823c2b5c0dbe 625e20bf3722a467edab661eeeb5136ab3b21ccaa8957acad335d8e3ecd624a3 9cf09748488c03ebf1aae49de7a8cf0607189a31e8209e54f6a94c22b5779e9f 0f2ece87f028e6c703c7d5fb6816f76954e77a08705ccaed72ec9bc9df739849 42d5eff368e018e457477bc2d4e3ff2261e18583fe768161c48da3c9b28e18ba 44f9923c7d09c85f5e47ecba184fd7ff98ff0339b1ac6ee97a3acc944da8b7ed b8b8ea91ea94ba8a09f14afd29e4e93b968b462e8dd8b5a94051d8fe800c9781 9fbf118f96fb0cdd5ad6431fece8f093bc35fc9e1c0ceb766a949a76ae2bc388 a60aba980165328c8c67a8829a631f379c84b93762a44b71a713cd1adb6d2257 978e82b8520d147be8d4059c7daf257b055d0aac7123ad2a2bc7574e21415896 baba23eb0db04dcba5ff18ddde563b8e8ba6be8b3bbb357dab13ed8edf06d539 dba84744e22ae6f1ccbb8f966895ea8ef7727284774e6e1e9e18126a293b8b0b67f26073d9bfa16d4e29c925d29b9300ce07583f70bccf0745cf18c187bc6b0e0bd67b1567f6c4c177b6460c5c2f6647adf1b3183634e217590c1a7f7202cd0e0b5f01a5fe31697f68f478a868e27163806160a49a51c4bc3cd11a7cd1b6f00197dfaeaeaf9c65ffcd61172f6fe72884b9582b6fe777e40c0b7253b98e23490d7b4a3012bff8e46e6f67e9dc2cd2a94686b90f4ebe13417fe4af1f496db9b8057a48db3335cec96ab9906e92437f007b8a17e731a6f99d2167b68bea3eae6f081530a45be37f2a4fb4af4cb5c0ca47f263089564920e65fe5f462684b6ffca065f27bc06f68fbd3a5c5a16cead0c77acb78a1d4b0aee574b26a7e21fc974c308d63844082b0dbf6767a1310872590d3c89e0d30500f98f91f98ef38609855a0ff723d0d93d3ab236a578ff4957779577c22ab0a13ddd3ad2ef9ef73739c1500e38b330f59a64a5b767d20e333d393f464cdf1d3d2907a78dc9f6bd05f7d0d60cfa2d30e33e578c778d13904d462a02f037643430505248096e35b184af2fda0f60e77641c1619542fde43dda80b2560a5aa44b5b1a18e4b6658a4c41a40cb90480b3ae9e04655d05f5f3506a8e801d617774d02ece7e273c261e172b991aec09be190a52a521d6b7a2371e8a33ad84b2be3b5f89eec9ba7d2a00acfd0d12aa0dc4ea62828669a4f821526e92d4ae81e712523df2b6bc1fcb1232cc90297736060a2fdee4c109c9e019ccec000b2056fc1aad3e3413bc948f9eaa4d5df6aec905f66a025d9979606dbf797b46039853d2ddb340d49c9a7252f82e7a380795480bc23914135306d6a4bdf859c20b59d53267554c91b87be7efe4d46862b6180e070ec4e2b96f3ff22529eedb679de1a15f9443207c3d35e9e79f022899598c7f0ea1a499a337397f1c671b669a0e7e336ad886f5200ece48b6b4e6d7eb59f63d092799e3e68532b82c4df3c7fcb1ccb1b7c24c3e6c873b004b2568fd267390e80b48c85cb168343108e30f61a334d263fdffdac8ebdebe9a0fc252d5ce1b699e0953146fb1c39ea00f54f18adf67a8335d88fc6b48e95d557d93df9b1dd1d9df0e784ddf2d097866617b29b4ce600f4c6a15971b1833c3df37901070c1b193d00308ab2d67f8a002dc1689dce77c6f0f62cca1376ebcece630da15319dcb839f05deeaeda2e34a74d6912b7d6dedc352233ebf747373379c37c32bd08ea1666805336e6be777d27b30eb82f49fbbaa780131ad2af8433ea9975f850b1f450c16061110c5dee70d43293e2c89ec62f0cf1ed6774559334f6f56a61ea028e85d940d1e2357aa14344ae78c59fed2f2e0fdc73e821b055f11d2c95d61127d15a4350d33507948bb94d3ab6fdcc8c2cf664d55ce895adebbb08a9bfd8cd8c4e9b7f20d2c3c8639af29a8b5d7900022d4bb4261a65d2edf3af7f68672fdfc1e40a14001b9c500237ca1c220b6c41b817ee235830e1780708cf73619c65b0f1a5a0a140dd409ca20cea638029fa3944b41a83877368e50942656214910760f07d7969902d11affb1d3a4e49c02ca3242741dc1fc54010b3167a741b9a8e2888c3466560e886de54f4e3d7e03606c9bac13bba8faef02dc52630fec8a4d9e6a6df1dab206d6448c0014edb75a33563c2e1534658ce584a7a967577a3c290198295a5daa04be32874b943e8c61d70aabfb7b70b41ae18b7565bf83935e2f1fcc8f1f046702529812e50a7ddd4cb5303e2d3dda95a9d52a6245d6f267512f7bdf9a72e64404ab3bef18d50f825063b009e3e7a48a4462fcd7400c8fbdcb50a23c7a47e35a037abcac36f1eb6d5657fbe7d4fe739c9d495ccc5c25b9bc6b28915b2cb5b7130eed3905c6e7aa64acd90143b431f660feda7fc880238d98ced9357f451fcb630c2a893df97cbb1d362f5de9f9682cf345743cd38e3ca5c2e3733b8813af673402213ccfdae36cd3392ee1acda820062075a770cd101fa54ab455a2ab6fcf2b60197251de11b8ebecc7c5394806bc47c04b6fe54be15851e0d92189673bd1bab0e2c93652b145efac431df92fa83a52164a801e60445f53b94a54b0d199854ab0bf312b4759dd36a11d5895b4aa671797d95e1998351c1107f4fbe1bde62f2310a56fb7ec0c079ce613cb086732c2419f035a46f1c7ba71cc95c1f8d3a81247d0db9e086110cf2a23e22893b7f6e5b2953827ada17c6889cb0856485fd8894160941c8163e36faf5fcf7aa411ce67aff2b357ef4db56c6e56a958d1e6bbcb4540d50103b023247eb38efceeee71348653bafd1709783928078cbb6a00c7d25010961f895c915ad059dc445dc195a9bfdf214805aa4a96cbe8373d98dce549c09068b693842e4c5ab1003e0e318a9f48a54dd152829d3c8fd02f0dcb6edd513d30b26bfd00ffe098e1677492bf55ff18cb260acf2f1340275f9668997d060b77b02cb05fd311f5e5ab57439978cf21cc5047e5d8434a9c3f127ca7529157fc0f709e92bd939a563d2a5dfcf92b40cde7639bcc6babea5f0ef5bf82e71139993fb0aa7b02b902684a3470ff7f0a0bc0e6ec57cb49f2557da83da5be04c4d3c37ab0aae19cf62ca4c741bc48770c12c0b6d9b72a5f970ba2869e2c02ffa0236884603fe71d9ca0b3fbecf9f0e7af3d0ec61283aa02500fef3382ad488263a3cdb46074d43d11605476b597f3186f7f6bb485aa5ba8a086e29b9e7b6bdef55e836400afdbd288b76ce521c0b60f7b251e9f5dbc171d2727a380172a2c280a98765570035dbfbc64f04e6864f0c63329102f520cac147f77eafa830af24fc2473fd70091f547061f11c7c2ed3e50cec229e146871449926b6890e28cf3ebb78659b770b53a1a56237634c40d33731808db59398ce8c058971463dc2e1a5b62d688e97048c99dbb892fbceb65abe1a2b1331deb6b7c759e2c96ab7afb5e24e4694d413071a102da75f94de2021388da24a67616aa8b92f92e21cece8361122f8e5f5c7028728c9312fff9b8803848db336eb63bdaa0e652f4ff95d7431ec52960f018105d5967dc1fe74bd34c0ea12a2b1b9a8d725d391f9e1564897259615bc821e170e48bbb6debde9dcf14ed9838f2d439c2c0ed02640bd76d570401f2b0aa7f2950fefd361d9a44205eb2bab71bbc0a5e62f887f32130ded31ddb1fe7b01e816e90631d0b1f0084191c07fd0d012573fecac5af0c668ce3213e616ecccb53164750df9dae35acf276223500db3daa079849163a440dec45e83438ff9fb5ff7c5bd0dccdbe4b558c1bf06ab27200745d987c064869c720c9425b993b37a6f047af80fec6bc0f446ca2d90127bd7f0c9c81be01d112681e5ccfe52a1af316094e689039e9e44e0278cbba180dbd577e1f953dd5188326c17e638c9b39416370a406c01 true -check_ring_signature b61687f92ce6c59a327c7e68f91a755ade0f1faeb1e2bb06e0e472afd68c8216 91ca0826388bbdf3787ee6b2d72798056c9522265d80ed79d48701c361c8221e 69 90d60e339026b0fbc324ee91acb9ebe38f38a764b38d9ef681ade8bdb7b4a4dc 6e12c634a48219d3745786ce49f0aca2c5de8f05f2caf1774b8897c37e0e9411 bff2f388c0657028b9b8bc36ecdc4033e4ef1bd22ebb81f39f492fc6f1ebe242 0ac8722d191ec083afcc058e0f65a1926d7ca2f01017561efe5ceb626b19d012 546b577f8053f05bfa048bd92871b1d50269f065803f5a8e2680e6bc0ca69666 46057ccc2670119e763183bde857f4bce57fe45a9a0dccc72aa4251396fc783c 77e08e8f6cae445a844d8eb6a878b1c95b6cc60b846099eaa82447bcc14a3bf0 74d1aa319af0a3ef6e5d7bd30984b307567f5e80a89379beba7947800bf4e5e3 f042b2d7fe50deafbec24e546250ed889c46b4fafa8791a1ae223ffa58424950 a243058dd09b1a1ffc5c8f2cb4ca4c74d22b39d2d4fbe8e6ddf8fdf396a2e62b 275a9e03fbe5304c7448cc2d85ee1cc263e16b2d52ae66bbda11b66bdc1f9c01 4509db6239122690697616078fe49557f395b09f49dc55b2d12af6a2f0b42365 ef75cbc7a1033e331360721112aab5d401a70429bc7d6145305fedfce45ea24a e6feef4041bc0af6769f2b809502467cd79cda5429e403034a32502166e48911 d4d873a9de76ecc3a7a089092dbdc27af53300290b4fb48c0a9443fb15f2fca8 6e82f2d3141faf5adfffe980dcc3add4ccf7dad9beb5ee677c0af6c7341f4551 031c21e6e8bff0d7e0db972da1f3104949affa0c43f92849d93dd28664945d3e 3ab10bafc977b3d6817a44850399ee683549bd0056e8b76b5dea0a2c21d83ad4 33823d1fdca8c8390f2eba3d92c5cc90841282aceef56ab98b7a505ada09667d fae13f3c9ac9fa86bfe52760719a66ce5f18415696c3ed037a7aa348f561e902 ca90848f7e78ec78287633dc62c4781d518dd14bca81d14d60ea576387c6ab9f 59c95524f4cd7e75e774fcade7250b041d80b34354b79cf7c562e5441688fe30 7fcd466f6f7800d2890b7a631b493488652f93be2f610d7247c33acaea725cba cecd44ceae11fa9de46f3b2dfe0e8bcab441df53936eff38323077ddbbb39e11 2d52765a3f9dde57810bcdfbbf20502409c6d1680d7de3e335795b523605e1b9 1db9d4860ccaad2d4d60f12d6df243c14069add11c3518ae66454c61da947db2 535a251acecf53228b0921bfb98ce9230201caa7a331c7beef3a755e5fe9b8b5 676d0074fb22b135bd05214d81741741f0cf2219fa81950f0703515c8046958d 3a623df4413093d8cebb6b37314be5a72ea21c79b34552c2f587f298c0e2614c 7bc29cb02f9c24f63c55ccc93b269de25ff5a9256a7d43f69d8975aca97779ea f087114c6983d71affe58552b0b2466df2dc2a61b73fc75155a73365452172ff 54cde206c4eecea2a66ec803c8621b357752033c7983ebaeb222b2fc613507fd 5573aa57614733422c3cb87ec4ce3687ece903cbb0c3f3af7003f37ded1e58d6 fa4144ee149e4a6704c1db05d3d12ecaa4789aa771e690a094baa84cb66c574d 0f7217da70bdcbfe26c68da0efe95c7f3a928d94a0664997659800ec5f41aad1 cfcf9efe186e903e68f967e024eba4dee33e36176b69496b5252220f50cdee72 5936a9100da43b9d61601ff4bc61722a192e237be1aa826189258a10f28cca72 9614ece1e13fac50ac7e0913179a12a7c3b8f6a09e504b6e52361704c7d3a713 40506bf1f7196349213a3e63e06b14d62dc66acff64f0367d80079b0616bd325 f2a447b0f38cb1817b4526a565e0af83bbb36452e704fc8c83ebaec140b0111a cd9c8995d57ae746e7074b4ff2a27cabf685a68ba7c87e6b6ef88f4bb574fe48 07a212e4745d04e14d86ddfc0ef5b0e73859fc1de911e66551ecab0bcb8feab5 ef7563f94a82e40c6e97feb72592e8df7726c4438e9bf949adfc1091d463034b c53d747f4e13df1e1ddd2dd2f7dfcb4bc7d6316351786a66310fe07a6f7e96cc e6ff84567de7e6d8b9c27b2efbc7704250db78cf3853d2ea48a4ab6953ac4880 f358e0206f2533e5a33bb95c007632849a627976d3941607f4f1c34789b51f9d 191257837de3908b2465c3192b59fbf3e366190a8e57872a9af63bc02d8cf2b2 c8fca8fa1845cd897c4389b589b2bc69f090877b9cbc17273efa23f3f0e486e9 46424c2e90a8fdecc2af795170c9857f2327614d478ae8d53acbae29ff2488ed a276d8bc462f494707275d27aa2f3f670d3ba9f69587bc71da5f6edd40504ce5 90fde19867bf8c3c95ab679a2284953b2297c4c07fc68c0b586c4816c39bba70 faef31cfeea36c9002253c07bdafa522e9eb4839a213935be4177b88a8070dae 9e159ea361d21d08b07aa3fb056405d4e5f76190fce9db93c62341a8608fdb8e f6c77ccb860892ef72139069405c8c2d7365dcbc521dcb1c1713d8debacc3a74 f82e777840e620b09cc5741541b058188e33236d12e79685186b93208f5b65cd 8b01a56b2e89ea5acaa7f45fa1c56cf06f18a07e35eacffb6b0076aedeeddb2d 183fd8b0b23ef6716993295962982bb502e4430005eacf7dd82427e0bed16852 ec7f7e98c7f33b7e99e194404843371fa3921a2dadbe15ad09061432641f696f 37a86b4336064b3ca40b4f801412c64747116868422dc3a7a9d000839afb3bcf 15d66e0a24c89e3617d26f654220a6c91ecf071e8062c0f55c15b68c562463b1 7177db0093dc8c7d8588044fcbfd1a650b4f0a4e51006d0b7f1f39b248b3d7ce 8e8979141016f4150f4e152f78762f0b6c7443afbeaeb6ee6e5b2f6cbff8e843 2a6470f7ee419356f798ecb1afe9a34830d328ee107d4c07f78d3c70adc75570 a37d64486789aecff731b7417308b468941a5a6287e39454c07d6bf4f1721e0c 86b124a6b3237c44562aed1b76bc88dc20d38091c3bab50fe9ec045bc2ff416f 4fcda4516870b870f82971dd1793709c729a17d8ad1cbaf88655cbe5bb85e2d0 06cd54e4326adf2207de87e54e49842f36432544d74c5728397122568187b56e 6bcf55e23b73f5a96ea76b6d22d71d2c70dc201cfb2b1f0e6b25ff1ed80c116a 90dda674f801bfe083acd7751d1b5fa9d4ab25874a3d254138cec31572776ca1 4e894d12a2aee008e59252525a579c7bc056434c5c84c1dd0c17a96768ac9c003ca28a688e4e51b36703035e92c8950786ee81b986952805e872071859775c00238947aa42346da5bbe26730048684533dd81417735989c78463ce7d3ecf7900120416f9847501bc6a41af7d33446c3b62b2b6dc5928f56a438e142956d6f50240dc5cc28ad3cf179831dae3f0b7311a8e7d529377e055d56b2fee67990d7206e53639e716a8ea30951982d6e0f280133fea0ad14562e5a4a8fafd7598f5240e26bb145885e9ec41a5edf29c93a211e451e4fbe99ee1096fb66d0da9b715b506cc0dffcdac7900befceda078a32f52a15b270a79b82af77335524ea0f232f10e16dc513d129dae1cc8098b9ed38e09f63d1b21655a41a5362c492e2fc1622300ad8b56ac0d3b1dcb4ca6c349acc743e3a18427b57274699c66904d3944fe460652030dce5a1d9162f28b010e65a69a673832e11a90c4bd171e0267efd854aa028861f83fc38bc0034d049df1b7bd46f45db0997bb047f6b5ce29513db1aff906fd79d219c159d89cc2d744575d339d5bcfa1564ba57ddd9030a7dfcc465c5108173937538016d03e4ea8506d1fab58b0b6131b64a3898b1a923dbe55c3e89b0f276bbf724fef3d96d275a1d807c71115e8b51f452ffd1d1d0b85165ef1d7da0b04ca45e9c6346983ea7b7a587b55b35f5fdee3558a57024c4ce8bf40d6972e0386c5b17edf733cbcb9a2eadd5a8cd3f7130195550966d15adcb8de4c86fbc20fb0b39d3884c3dbe19be40fad38b7c807d02f5a7c6cdcfacd28e42d109562870fdda32a2b2a9b1c155069d8b6754399a663c56cb119a921de1a0f38ba8c028200608509da8a8b119516e061c5bbd9f116fca2e85ebb1e39d2d9adbd25771fa60ac0d2bb92f0294c2064fc3ca05ed5ed0a3da965c92416a6854adb0d2a6afca70b769923db50bdd22c8ed6ae542d605d97b965207386aaff3fa822e64bb920fa06a1a5938f50f7edada244015fef9cabd6e3148f1c69aec5fc340b8390565bf1057c0ddc27939a78dc2f94d2ae92457e523911b25779d8774dde5604b946e3d808a4b3f5f3a5b0c2421be9ec798b1b0e9bd889cef29bcefe5597c44fa60e2d6903da04061708b2ca34f18d9e98e1e6f661634fecaf296c92f12cf1785af0148d03df2f77be7f300184628ef6a922dabb5f83ea7eee0e876c9db9e4a4bd89e8d10438a6e363a5ec0399ec60031e22b6eae16dc0292cf5545ca5047c3c1ced29990506641fffcbead07db6ec1f69bf0411c71c99a74a673ab04025bd6b77bf7d960a71cdf243e06cdd3e660279be19990e0b99adccbec79df6fbfb6900b0bf2c190a3f25c7f27ebe852184fc8b645dfd53ff6cf2a080b0b1fd5bd71189197d72760bd891314b8d175dcb9e285f53ca307b6671cab619cf8ad3f573cd7525f04e7e023fe5543ae90674b4b518f7ab05e6f622acf7de46a5e37342d1804464662bb601d9db4955acaf16709b63c4d19c9a9549792e8820a091d1d1782094e559f4c4006eb7d800a45bcef6293176024f4ccea13844808fb1a44556829b7fbea975d90670da5ddfb9a9ce289e8a44b9ee45c0c7fbc3b0e463f797aff5ecd02aaa878802df24ad4691f185132b01bb3308177ce8741e253fa02a0754e09f9cc1e338080098aee17ff7fe4fadf5a45cd0e7e06b002f34cfff66020c8acb9cf2a895a5ac09d0b0687346d25b1c2535f724019a30402af1e41400242ab9b615b20f43d8330a7737e8d0f7eefa20ab753e4776e2849201c259e13a2c6cc3162ccdf00f8d3e0d6792dd918917676c9a9e031bd3cf4ea972ac19e4fe86cee08251a8e849a60b0475f6070e984b7fbebf6bd20d985c7b05fcd73056345c9c27e9b4848c72ecfc0902ceb39083abd31e62dcecdd104609bcf6587b5588bdf13a4d6d4b7400e28b03c45bf930c859e03ee2c592c66833541da454338ef9040ce606ef621e022f4509625c86f8f210cae0133c78485196cc9cdea4da16fe1974e6e07e56539d09fc0d9c32c5092e6c73ec87ec2d3992a991f22cab72922afc20f45ab4dce3abe08d0d1dd22253d8467b64ee0d21b77a3005e8c41c4ae8bfefd56e29b7bd990f316501c8dd75e8b70dbb37c7149a40bf3fe9030db738285c74abff091d54df2c230b08648d298dd4fc709a28f8001b89b7aa44bec53067bc992c043f671a7d5ef65008978393f0a35787c3c1bff2a715f71d80b855aee09c3210b508a5c0e3b99e330f8923cde7dcef25947740696899325953df534633eb98505065b100d5f9219b024e43f34142f57075a567912d6e979fa89f0b60b1330e281de9b21adf02273804a6bc220224fbae2383839a49fd3230d9fd26da0e8ebfa437a5c38074165a1e039531caafa0001dfff3cac3fc63523f4440c5238d54bc1824518988fb0614e405db0bdd52de608f6381d3b808f679598733c3d0b2aba27acd80961364266d52065ee5744bd5937fba9d1bc1563b4f334e874d01c5c54634b6bce03dedc645d4015840fbb645b578810e93ea9c5013bec32aa71ff6b4174d1438b1fc354a3b3b01401c73ec137799ce3493a685304709b98f056783d51021938d38101acbdf010b9b67e614e61673c03837cbc2ea3311266550319696356c874622cab5225415041aa25fb9e3683809a74fc597c1db1a201d4c7d93a95060b28b6584cf1e70530d37cc93dd8b654f7a5c9eafb6b74b94976c051a37f4a481697933dccb18b86b05170e1cbc9c521620e5dccd7cef2a98c4af64f955d49edd3261155bf6e1a75d0d935b42c0a12b5f5e68a46846c396a518f49cd8c8adc774b4f6949b163c0df50543a4c63b9e5ed75addf2e3388f9aa7651ac2d5e9d90d153e38ebd01bedc3090134176be2691159d15b6207d62b2e7aad45172fb24ea424813502f0d2a62d6706f53494a64a48d55bae760abd2bd4dfdead24fcc043ea4d6f540977f5c926d108e9e1a615eb1acbdf6e700c7f24e6a70a9505264523338362ed4389f5be054c06dc133346cfb5a0e81bbf990d04cb9cf287ef68a929f82522f7fa701a9fb76209b4546e96b536d3c4a041d570f778e79678888ee4c87e15a78d33a79bed9a5f07829cf65fab004a6b539f92bfa35739ae0c2650a828453dc5d3bb3deb6e79cb0d8926102be52d816f53e941b5a655e83c77dcdfb638f99ac05d55ca4bdd9b5b0b802ba0a9b5872928b25a6a041cbe296bede049306b590a9c1d45e00b079dc80e6829572ff244a958279df9113a8f61d58530ac3ae82eb486e0368d4da47aa7024759bfa0c3b32af78388fb0a9abc82a2c036418fde2e556f10f6bc67e360ee0869370134ab277ffd9b49ada0ee297316cbfc8193111987d07e3c0f310996400d0dddfba18aa55749d2ec710f8d6205784ba0a9d9d9eb900e980fb1d964cc180e818489281191afaf36428ed0664f0daa7c4e3c4a5f0c2e9b1178a5f8dc83fc0ba766eadbb1cfab7dae5ed33fdcfec02eedca21d2d988b96f667585bbb7c1b70aafcbd26728bd1b6306c83d68700c8ef9a30a608f066d489566ba6633d65e270719d763c1cf463f693f54eb7dd8fa0620dd0f7d67963cfc10c21620bd1b5f84095b564e474de355c2e5cec8e69dadc6910f7e2b6ff0a6443213113539b17cc6096d208633e6c120e8ea57bdc0f6b57fd9b0970add0c8fa548b8b1999ce06cd00ba663e8136927384c64ee0d79d6363cd980b04714b4d57fc29df1adb1b341c502f2950baae66f985439c560636da7313e70666c5b1c3cc0e94c859cdc746d6307e697b3d8397cbdac6a09dd349100293c840dfdd5c48c0d8a82fde1bad035db0e71bc752226ed31037d40f3b3a7376e13b3b242e3d6c07d74dcdd27312279880825d0e4ec6aa224c871f279e013d28bd4b847b29baea3e3382d47b6d7c8a79c0abe9d1ddc43f1f5091480bc7faeadd6c3fa5a9a54351ca1ca9e14b46c83460709a251f383823945939bc0fe474d2b76600f9bbe870993197e1bd140e22d399f044eeb6e2eb043281ec808d86ddf1f78d42fcde258e95d0c5a1bd3e49a372ecd0ebaad8d2b3e561e9782b69c9da428d6829e9435cf2ea8c86902b145e43bd7570d0e2ecced6864b13949a5ae67ed3bbd22f47a9a9771a1953002297840a08fe90af13772bbbba6a674dbede1d2db282885a5d574af68b9defb6a96fd39f916270ca617deabdd4b9459eb72b22b7347220963bc0c928441782d1299be57135bea06b3aecd2f429b12467c7ee89f926421e78016426e403bb4ab01c1a185b56c400155e7377ebca1325723383b2fa38e762764916efd3ec58fa0079c0eba74266d081d381670b0a08490d4a96aec599ab42cecbf4410aefb0a5506336adda7f5680666e1b94acabfa2fdecd17bc42efc5e2b16b71439068a5ee3a7d898df825fa200222ccccc8f721435ec86dee643bfd682e788f0c50ccc12f6e5366198dd1ef4060111036ecad3bb132e0510f2e0ade7e85ad9964a1b4959f183b81de35bd5f10cd716a740f680d23de483e0485794f1539eb152f4dbfd267afcc402780df7a7064f152f9ce3819a849255dc9ccc5a6450f8429ff5ff34362ca838902d2fcb670012bedd3a10519db5fcdee6fe4191a597ac6da4fbf9f0f0af8ba6f1a2f4ce5d0ce8804cbe6c4712fbaf76ed1c997e18bcda360eb09b040358fdd9edecd2a41602dcd556da23834f76d2fa66ee346473f8e32480004f4edce19368466ac1d41003474ea47ea8529ffcc62c9f8f82715bccede183f49fca2b65e679c3194c1dc103db26b11a9a7429217c26c4e64c28e583cf44d6c127341d6b9ea81b9f2adfb90178731d2f6ed9c32beccf6e27a52176cda96d4796bf9a60a7fe55a1530a68c6099ba20bc645405711af07392bb3485d0ec46980dad0bfbc76ea16e583357fb206f2fcdeffae7141759c0fed40cf9515918210f7d94cc7b0dbffa0c9f8912bee05d0ab7c05edf5ac6627dc4d2fa9248df3ee7cc936546a73bb1f60f871c9571e053144df223dc8665abbf3c21aa837318d8f9255e65e0ddb02f7ef3a4efe75b2020889d92bd6c5b73a1f9ef23e62cf2a5e9318775679c21dd3811a81a307dff400c3ef8c8ed6fb830703973c9b24a61e9689d3ca6bdca290ff51c9fc0366c22e055ff81f3167c2dd80abfc3966a1377ff92230484a6d79c96e7f7339f415ad620e98a935a8ed3d7a3dd6b9adce387535b60583705146fbe8c23d0dcff60ec0fa0bfb596718cebc7c6805ea7faaf4ab1a2fc8892d87c6f7c4c9d22c8dbd2392040b37038721e32edb8f1429a12e043173df9d2be547556a7d5fb2f5afe2fa449f0c2d6ba9a40cd8a29aba1afe655ee487ebc1e47d4ec44ac2cca97edfecd513ad09309d7755a6321fa783c9b7ae394a0d8487982df8800a8ab6672f4c2daabeca0a7bf3c961ae35fd315a1cd3a70d416e1d43d89172c5d45118a2e0781e9fc44c0d1b64ae5703d6a38a69913e523e02376ff4674206d73d72c73883582f007039000b8c040a8760e79d0322ca10e747e74a3875349a5b3ed51a5781b2ed8307e10a6a837e1e969a9bdb2068b7aeebe5abfbf0cd49001112f13e93ab75fe36ca4a04ca48c9c1f1977c5f4383946c924263748483d1241e03009b6b3985d0b9aa1000387b5ef955a592eee543af61fc52eb877677e8228a3796244e39438a5e62e109f7fac6472092bb0e4c56db77379f328249cbe89ddcb01d89d56181be6f6eca02d415fff40856f8ef2ef3ea99e72e7445db13042174351a8e4b3e0e6b2fcdf00d29230be825b594a6441b2edbd1f87797511acd56bff136c63280ca7bece27300924837044c340ac1e03b2bf6d75726f004221a691777b176f18113a11716b20f712f3cdf1d64ca1c4c093dd5a789fa40e9e61feacea96bab4397897ba2238dd3362e05ae72d2da0fc8c16cd614391b9ef03f5b86509062b8617c3665553805033ac84507a7e19e07a7507bfc5ca85368ba62d3292fccd2f08a629d8553713f03f4c75039234109bd723c0227e43c198e450c0bfa14ed0ca8707ebdbc026c6905c6b4389b704c8e0af083d5255aa77a8dfd84366124d5b577044f425dcb528d03818054f34e52f2cb7d3684ef44603003ad79f9415a5182516f09fb65c6ee3505079b25d165e060341692e0f3b8235d15004e19fa431ad148cfdfad0f59e3550585bf794757c05f5eb06871189f11d2e09bfec8c2dc03e75129370a1913ac7b08 false -check_ring_signature abae92b9deabcf9c9ad0c538a34a35c43dfdc5531b826d2b5510a15fb39add20 6b1c0fb3c76c3ab6918b276246fa331fdb5bbdfa8a2c97838c5edbd9f7bf7157 62 dbde5d3aa4e5e8cbae98f33bbaceb09a22f80d4eaffe2e7153fb3a376079e159 d344270406a91177c42a3fe3b5e3993fc689cd253ff9ad2f031672f44a8db0eb 52aa4f714bb2da31d5dc72e12cee735af4d093d2e566a9d914f2b20c5b34f98c e4924d7103dd523312d9f146059d2370715b3eecc6782394049fa9d22998b061 67d7fab36b42124b91660fc5e2d6646681b647a48368f9727910afa5198dcbab 2cf70fa5854e316ff7c7ce96e9fbf60861cce81ea352577c01dda78715fe7e97 105d3ba37c8c405c38d7ec5b30a464c5d6ca322dbcdafda67c6d9c4c7a0073fe f0e7424623c84e6131d639ad1f517604861e0425ff902b1714c3ba43235a1464 e1f5e5fe0e31d66974d0a402d0e2508616ee301e8fbaa3d405a720d39a9adf84 e865ecd0012544a48025327317b98ff02192d9cf34349837d736a1584b2d649a 19b5249672c76521323f316f480d2fe34fab6fbc4033201a1c1d133f93b9e90c 5917f0963a62fc79156905cd92ba26d0077e92eccf7cfe07fa6dae6f7ca42971 225ff849c2fd5f831d17a307e620b33842d0840456bf05a74523477ea016f1ff 976263551f073bc9a1062685aec332942ba032c0c0eb70bb197a232267bdd20c 8ed5c7b1e050867657cb091434de6cc61d04ed3773520ebc57bac5ec769c6714 7ba0538856b958d05f45d16e2317bf501a5e1fba0981f3900edae7132be01997 10569c3a23e51f1cbd5fd68c5fe6f944f7aea6a62bf34a70036d5ab907c2a0d6 2ebd21d6eb144f6afd94ad7e3f9a7db12db7908d2755814bbd6e96554706ea28 d4cbc8eb9da8798c6e519d82c462a7b74f334a67e8824f53b5920db9ff3d0d0a 7ed6dd9191b9757b5afc38a390f6be01dbb8135ea296820d101b4db73a8641da 28e455b046a476cd739bffc50fd9cffb2a4a39b2d9eaf9d893e3385bf59f3360 ad1456e0231eb24151d9e2c4624067ef0bc723d281b948a314c897a6b0293600 cb9373ea96f4e77c5209ff9ab0261aa49370d3495125dececf97baf429bbd69c 959b24cf76bfec065d3d877c5ae8dad80131a2920ea3c4de6daf86f1e288cb25 b530eb28a4e7838527969674b5694e3d1dbd104aff7a1be9e1240d3be6841ba4 09aeae1ec8a35c6365a1ec5fd06e43ad09c492de7faab73ad52a1f14160b9245 8047eb93b05b8840c76b4aa35094ff6924e35503e7b5af8626a06afd2d10d79c aa4d12a7348d9ab21bb1d99650ff32e768d214c98c10d6e71d8dcd36a9e1c645 e53c3ae7cf8fe93d89f876693f4d6566b3d8a7e6d07bf957d7c402523436b2b7 3710c39480a59d9432d96d510b8fae023c2e8a283ff7dfbe8d38440ea3ca54fc f1b6083826ec26deb63f18e7c11aec1f1516353fc74935871492d29680afca6a 05aedfcd1955fd95d9c35d7ab07ad8b05e4464c0576c4349ca594fa8b93d2f5b 4ea324305211b80ee314141f81c44165bafdb01d4177b4e457c1b2112ea9b0fd 0004ee32f7a134739f5b09c944e5936169208ae277b3018c212a14c5e1fa8173 5de96137070551122ff57fbc7ec5f914bc66a2699215ab6693c7d6940937402a f3a005c6d5a4f74a99fc330c821e3bb0e4f9deda9d41878aad037ee4dc70c5ad 89626ddf42788858059105c29decaf4fb68d792ec5383ad170c4c9edbc932dfb 33db5abbb95ee5b0d31eb91ab7692f4eb96ec156597c206c735f5da841e7e301 5358bfe38b6bd60c9b88e4cea68f3e020627754732d98dec81b888bc91bd4a03 62c2935751ca3dc996f14fd83bae3f5076ee9697a133e0b0ee42581b6bb232ba 4e45b42fd1e12471929c9ebe13c393912e14cd0e491de7f8d3bb22ba93a9fc60 32971932f9910d1e987806f54e38201969a1c2aea5d755f4e53d10ff12b2054f ab662029a39bc228f4631d2e35da2eaf64ed34eaf7dec5c05eeb47b56ca46646 899c92e3ac949b170446030cc3fc62df4e19e77a3deb8757b939c7cd5fbecc05 ff33406bfbd7682375e3487facca942199e83236a37bbf3da42136b604c21d7b 2cb43248603798a208635c5c7ead853ce58b92994c318f6c909f49395b4eb97f 4ed7fcd9dbd4ccd9c1838d0786cd9afbba37fe28e74549dc1bc68bb04755a186 0ee56adc6d2f608ae7ffba79dc5e40f4230eb9acc57f062b6213496074091a16 5aece29e9f58b264c24478c4f8c92b487f457facdbe752c9d62f24ef438ccd61 a2774e11195d704a4eb2dd19476082c00e71014f470465752479ba7c0b74d3ab eb93418a7029eab3cf817801aaa0e7c5d259895a00642f8b69b316d1bce44ff2 d8220debf4611042d340a2f4a170651cc9c465c4109e9254e9b67aa4b7720362 feb9c2b0f22bd033b8f379d3c9f705dcf0b1a18109c8bb3eb7f08bdbaec39af6 2f41f85602e388053a2b643e068478b1221c0bf00ea2140862fffc411bf4f0b2 46205287fd4671cb8dbbab6c7a8fea2cb47401d8c257da48ea18afef67c45ee5 18e5e4d429cd5c3f6549281a9d89f306a08745611e47a636a58c6af6951bab5e e4a08903ccc06db6628b6cece8e92ceca0f82e83bb1b8d37095610d1a71ec6c9 886b51323967c3b4f7eab34b41aaded9c8165fcddc6bc7e71f37630f926007a9 5e549fb764984e264b8f91df5363a02567d161cdaafa768fc5a5e0f033ecd0fb 06f79b7d1884c5fba6a910921f80759a17fb4d68f90e8ee6e9919402e3cfe880 71fd7f6ca99dbaab3428667c84988f7d37337eb3e5314a71a649d755530336c5 4b7a38dd32209c6bc0caf330b04e1c5e1e481655b8ab9e1b5cd87cc6e4278b30 0a9511150391300b86555904815071763ba46c5ddc93190606a79306e917d50c9e7a2f7ea1ddb5da1446489c91743ce0e7d6b75c05d90db5fcb4d3d79311b3085cb5f12d3eebc5a878ca5ccb276a97bcb322f7ca6627f377c6fffc27814a2406cc80be7dd057270a474b93cbbeea443b392029418b4c7fc693d1d7ab467bd0014a6febae38341d35d79c0b9cfc083ac8b8eef8a8940965d3b88dfbf9c682ee01bcdbcefa80499e97306831a6bb6a64d84ebaffa741d869584e40ec3ecc5c160c8f1913d88177299ed1f699a9ff365039551c008d53f0e1ac456203054c871e0b2ffe6dad0f2e59548ff9694c9cedcabbf974ca057136c5c887c5d11670771402a6b81c9ef08b13547b8258e7bb69179904399c61f3c15158777259d5322675024280a71f4c05071ea8286ab193aca25a159e8130a6db628e00e4432e37a52109bd0767ce291808fdbe9f85fd6aea491ea9d7d85504033c583844ee6f6d121a0312f467e8a48d886a1a8e58eee5405e27e1b2909cda9e43aab82ebd8443aa1d0c9c3f45f65005dc54eb742c0a303b64b70fd3a168f114b34b44fb973cadc4f90004b7488d424c4ab8fd3e84a66ceb41af0b1a7f5679c866a21e80126ba653470ae159e813d4f531a12e435a50aec361547183949063e1db4c5ea4ac4c17f872036e073800b7c4cd109bad505a93cf644e16e5ed1ef24aa77d214d926bbeac9c007e0fffdd2eb2eb8f177052d6751f721b8ccf4e047f0437802c0705f200dbb70a57f6f9dfb24ea2e26dee4128c4e5b2c54f09b30d9c2168f1f9b5955b7d45ad025e2183154ba9f9084beb68f667d49c5a31d032b490c64ca3bc19f69bccdc200ae1bec98028f7c141a187393023b90d3795aeda1d5d0d76e35944fd3eb975b104980d6fc2503d8775c579d3602b9bb0d2b03e5081c61d393fae86ee5563e4410fb1475bf1e2cafb18fe8a620fc01a6540ee8954f15aa0b2f28e2e1c1358e05a0c1dde0b5ac5cb57c045847a447e508ebb90eca293a273b888ab7d382eca7bf10e7a73b18d2622452105a2ee42e72e7cb8a62abd1b7d21381e96e3a9181e4fdc05ee7799749cf14a3c2e7c19bce001bb8db901bd90e969cbaca52fa6eadda4b30a18a96e20747783bc8147825f8a5cffa5368811e70b190b8770aac9c6f850680cd73417f26c7b403b1849fc3548bb595a3a5f135fca0477dea1a328d4c72d9d0991da74de97c4b0fca68994b3bfc9a7179f1cb788ee0e356a4d1d7b89d3ffe00b4a5ca27ef9788b186d971822c50c6bb32995aa73131977033953eb5868b3de09a8561e28ece10a6ddd25233c09e638b17ad035c58e1335674264a341f0455c07d541699f354e1de566609e68c366ae478c6eb4184ab0908b2463739a0149cd0466e3716b71f239cdab6e7f5d5d07209dfe9f40f1c3cfcca4a2924c798a203f05893330d287eb03663334113719ca842fe5324ba44f6bc5da2f5a7b4021026f02e4b3c977b7dad36838c87d6025a5b9e3fb45c5094b832fe6f6c67201bcfba90e364778bf8f0048d721b51e7273fe52732edb35643de6240ee1f6b4b58e1b5800f16e4447c0dd2311053bd193e44b9ce29d0269c41562383f42522911830b3d086e397ab403435f704611064111c9412a163a68c7e2c8cc65df4f9a9c80cedd0954f1b8638d444ade99dc16b54cc6efbcc11d315ed622aae967792a5db0c8ae0820fa8fd343e84c03876f96899c7e093d424b697ca8374aa0821429cea6faa00d29cfb4226c14a6553b4dfb5f6685749abb132e07738159fa3cc3eb768cf1d909f9461cb05c9232b34ea52ee6ad22793d5d20a0f711475dd7fa5ffa8ed2ca620de3a2ad6ad94cf941636d51807db5ed33f2650b7c29ff7b55a62af774577f3905d98b3e924af2c127a5cb934436bcb51c96eb727727bab1dce18209cc9468ae0668fcfa81ba9c15118fd20295065d49b37662019621f9c09f7b1d2889a6944101bfbd6909393f25280f224be38d85f7b385cf0303e2bad58f0deca4c87bb7cb0b8c6b5640c20232c3e0a04f174cecd4fb7b9a61a577ed96951f03b70f8396c80ec9fe627112517de07203915b5c888b123654fec6080ecf77a72e3076814e1509ff313c694a16ae3e4a8a7860322c06e2cba3006e7751fcdb8c1909a20adaf20d5e688e67b7007e407cf6aff0c4e652043883f39dc3fa6b38db736d4bd3266c0dfb7409dcf518202223c653f5f1024d6ffa68b13440a86c409ce53e354233340ed3a1219271c02ee9d00802ccbdfafe57a31a31783e15fcb990370ad8e08b630471167fd3ff2631e6c781f8665a67f19dc8c5ec8b817f462f10e4840e9e36ef0b1bb158844f1b2ca0abc694f1c271cdd7687c8ada2cac102b20dd482007461b0cdb23d69d9703b4d9e24916e171989ee9bb5ed322ff7e0323f7e102b8a0b27b0e3d11915f720ebbdf44848b0ae219c0fced044e9ccc4a360ee6409b7a0875cc07434ff45ba1fd842ee7bc8820b587f3b5abe97348bece3baa68a358b9fda6080dbf2fbbf825b9b1feae6fbd6ed273ae7c11b72ffdd1c6d2427a75e91c49970308eb9800781e9371d867aa6f9c8ab377f1ac5aa2310ae8ae6c593be9d99172760a8dd955194b18216a4ebbe3bc04e124541fabac9682acec47507828f3c5bd5604cc43d38ff92dc0260bb8a46adb459a1ce0ab20987ee9fb1f3a5c0a923533b501d5165cb459564888d56668fa85434e676f1993ed35204d1ae2f4f211c109c80dbab55e45317c6429db38ed62318c482e57fbe602a36d15d5e0b58383280b50066e211faa6f61794aeb9e3b29f101f63b3833438e4c3f7a67a76c58dc9af8a30c7a1372ee5cbc2d7401f5216975d9b84d150d445a45dd9e50fb5330c6e26de606b07ae707da9b55132490fefe1e9fbf4d892defd929357eba677c57f1e59c4f0301b6f4676c43b1e2cfe8a31786867133568b8d288343583c30a15a24763b730c6bf309117f397d05e40730cfcaa2e8017a9dd0f143e9162a50e2fe52164e63085fc7e18f20f5683793fc16399c8c1e739d086c80fb74a9685aa0adbd72f7b303e8b779fb8c5e4b35557e01fee208113f81f68a0103bc577e2d69ae395956530fb78e5d4a8855c973ee12f8212bf2f2792d8798aae175ea42535bbdc2022600049c509c28b9653e5036f9324bde90af98e89307f024d9243b4c0149c69a083f0cedcaa3771cb158a83024c453c0085a85a5284ae23042f21cad4667834a83140ce76ad8c16c40fbce71981ab364598ce43fec0a27954568629fd3296fa60fb309fa3b072ea6ebb80226ced0106d97b008f0d5bd9f16ebcc4277d0bed5c1859a0c2f61024998fe37c1d7e47468e1012664803f85033199d579dd1a2d6cdfd84c079dc246659b96a71e392cf04a5e09773724d7582d58fa62124e7c2401dc31b40ac11124fc17e092e8439976ebd29ddc51117dec465af9ff9919867ac0e0a86d0951287915ed2fb0f7067f365ddbcc811f64a0e97292417ff3bae969679b91780839691c4e9dbd7b8dd2a81181e61bb99f00f0c0bd74cb877ab042509ec1f2a609f096db5a4c46fd1f809c9b585d899117e7b72138ae26dceda9d001efcd28b704bbe34f750dc70ad7b40b31bcd480a44b7bf70000b202a8ed6f6b693a8ea59004e17c81962580ee5affa30a1c90df203de7c68bbfc8237a64b344f10d52a90200a3bb29964d26f4102ffa5e0c3da90d33eadef283b53db5a444d5537cf77f780b3505418d5aa36de80353adf3555784428dd78b96efb6e9e368ee00a73dac710bcb924a5a6b3c1fc6dea405eb24e151d93e74f6ccbf82137cc82821f559ecc30222a0ade6e358b17b0e0eda38632347f7e56d3eafa3b7c08feddc2a322b87520860fb4b206038e0016eff8d5d038382e5c4e8f0380a0fef4649bd91b20347c107ac0549424bd531afab7e3603419a5b67587dce653a94336fb1a30e166195380b54f15ad6ac1030a207761d307b92b534eb3e6718c44a7938c170b6ccb497340ccd842828ca27d62db5016146be66e55e1396fe21e8087333867cfec02a5467033a19d42a084811baf84a7d2898b95c283c915f642dfdc00f45e9b0522d73180a66426c8af86df599252421f38ccdc7f5e8c4bd33297ddd5125be81cffa25fa0e00ac85c1bf9f26b03bdc9d16418826ab1b671a2ab561cdc7f9d14eea5341c709f6926d3b50b2e6480c4eed35ed50f93049cee17ffad99a0b197f18190e2185001442323a76cc449776c2a1a6e61a239830beaa8802171c2ef145ac5b920fc306b6788d2be9122f668aff53620d4a41b76806ca22816a0ed6eb768892ae1da30a70b5afbdafa5b60da71f43c6d54a449d410299bf5b06b17bb2f1e311766c36070e4b45a3a783e2345db32f56b6fc72ccc7cb3c45c5c71c5bf4849ab9be066a0716befd07cb4e20cd0431be29ae99215ee1c4f78c24c0f8eb00687ff52a823a01ed40811e32932c27810134c70416a89b1a534dcddc6846d3dce7dbd1f0dc0f0474e0372c27418be4ff4bc9f008fa39285a9b7103a9f8a6ae4d63c4577952df091e637533874db5ed8e1dc663307fb54f47b2f7d0517b1e71c02484900b14d9002d38bb440bbf752f5b76b4522aae0ddfae770640562b75b7cc274dce0503e10a05faae7594441e183a30cba6dd25651f309af6c86cb6e9a0755da3a02e695609024659b74d8ad45a03489edc1fcf89fc18c654b70c62ec3aabc1109a5a3e81da20a87b5be89951aed82191db4da3794c20dd70c1621bdb448aafc962da311402b09202e725455ac8a646187f6c3f7017f4401d2c5507f57850ad2928b7d0540ae3e7d9f98c03b7584fe94c80b823b642b6a5d7bd23a184e06d831e20b1d4f809d579605f65b7696cb4216eb2e783c42eb1789355cf8fcdd7869252183710350d79bcd2be7386a62fe158157c672687ed3ecd6b0f1370d25b528c57d65345660f64dd88589ef315ba282f67fbbdc6800b83bf008021853dfa4f0656cebdf4430a11699e79b813e37cdaa81f6a60085cf745e98e86e521ded78e5000336925a50bc93d8d336835a3722d96cec0e2a8bfa27b34ff6638e87115aaf4b01d778cc30dde74f5eee893662300d0500ef6c8636ccd9266e2be9fd93a92fcf2f6c301510492e63971a8761e57c7baab5ce0ed7588b096a06671799f231148530758a94c0b9e11027e54ffe4aae8408e18e9f04cff01149587ac55bea1a0cf73f137bc6b08fbfa37704952df340ec2bda767676233fc5ec802cfab8fce28c299fa4894de077bf7c3ea8a21dd97d84da7cd09568a1696d76cb2158e896823607c0ddbc59701a5eddb7eee97588cd667a75ff26e03b9e68938d4b93630d74690a3263b5a2c01346cb3715e5390c794395e0ef15a38ebbe357a3cbf99a13b3c86f9883a6d080a9ef2d6dcfc135f2dc182ac1ce97cf3eb9c0a33bd112a6d3f434ecbd1b046610d1ad52769da032eff09f051fb6de90d3e8f271a771b8a3e062000caae9d093b0ff194130e95ed8e49aabd308fc71e6b483824bc7134604751b08306fb78131d07f18b2daafcae4006b2f4f4c828ae04a149abdec74a7c317035d39de818ce450d false -check_ring_signature 4537e57175718d31f4dc31e8af5f0660a5f92de8f0a3c2ed11624e66809e6ab7 dcca4a6f011ccfe6fb40329bac50d1b3e336f45502ce1373ddcc5e396693da5f 12 576736965be8ba5ac09f0fd2ea4636b36bd6bf242e430587d66cdf708ba72817 39cc8557623a98897579807ac32fe82f35a5ef3fcfdb3c8794a8d5383594d9af fa6ea9add4b352db55100fdd6ffb386ddb44b42a1724a39cbfbabb20d17cdd63 7eb493e2a6584e4e8cf4366b70d0a9ecbdbab2cdbb56e5272243235323c52e24 a03318a1711a42293470b812df90b1c9835fa548939884c25444f9da74415f15 066b8e9febc929cc21abba169b5f9669a0f0949ae9c6be8f2c5cc56fcf177e30 1c60101b48532e9c1c3d4bffa5c8d485c265062bddbabfae4ba13a68fce1ba98 ea6d56ba0bc919f6924e162c8e264846aa6bd46235881999628791e2919e9f6a e22515c6134013b5c5c9b1eaa2df476441baab9d88957fda02eefeae745375db 6cd55ee16324b28b634f3a90fd8604559faa61291a38b1f2b3ec088ee7bf9387 850274ab729e20b995c2354bbc636ce9067d05847c5351baa8b08e00f1a02937 d2c591ba3cbf3c998632f0a08bacb67787562ab8f285e8dafaa7370503b5a4df a595249669dac3f9952c2a3432b595b08f45106547a393f34f3c428eb1fa8c0767acfaa43e0e075a038e413a816a7ca535d398cea2774a63c6ca8f8b4ae901095d0732f0f17627cf5f2e4ce5ceb6db0b4566f060a64e8d40b4f67e5a0dbd6f03e7a522156d411229625338f862343eddcd97d851ed5c8112d681d634feea28017eaaa1205462a6182807dc6a3cc7cde6d4efa8a82576307da426f9fe2e61da09278ee8d3b5f4fa6d5404a765a38a4b83acd929574406889d7ac0ab32676c6f04f93e5fc93d1db453f0c35e2c550f89f0fcc2b15a57f01b8fa58a4260cee8020a804162050d8c782002aa7c52a66fef4893823464a8c5d91e2b0a6530a3da1f07f37156eb0ee26d6ffc897d116ceded66f11504e0578442630a952490f945e80ff16ce46eeb7757c924440350c9c6dd4ce1ce1b09dff8ad91b72dbd6d48807a05136917bf66610b4cee97619e0ab16d31958649dd30316060ea8026e09da298e1f8b0c5c87a833c199cae20fce341142b47b60b11f415661c1fc6ccad1522540d57e09ad617cbaf292cd7f656450790b78f30e7724fff4311967a3b4fa7b6860a6177b567b47b5193ea727fb2b507d6a244c2467b6458a5be570c317b0447e80ee6744e013ca63d2a64d1c3210c73057b148f4d9b1b6e6655b4d43586988a6a0d7ae99395633a14500f2d43eec0ee8bd72e6f5ba8254edb232207b954cda8bd0cef186d51afc0b59773bd5aa0593c7e0787e799141d170c79487246cc6c2531078d75eca61fb31f2a5f90e6b7468008a47d20635574b97e0232a81c2f8272850a7bf0f4ccc0bdbf277a8abfaeb07636182d224f2be22e9a9852918f0a8ecfbb03592ca8a321a7b8aec4a06264f875feb5a31d0139941409e75da36b2f4206550845302a57cf3c02a9bd862855086f9d34897c8d054e014d0ccb67dd82a1f0bb03b666eb62c885873b2195f04d182639ce7e584db657b359c8778d4be7cd0c05054ac24ad225c86201db30d401409fcdabb3d12c810595d42ed157974d9cc61a0ba8f8020151b44ae0866d85964ebb66efe74ebb04a6c9945dbdfa9033b6c9aa02 false -check_ring_signature 2062fca5a14910aab22a80334e11657e74405b573b746788fd44d19e13fec058 8319eb95f8540b413448ecd7c54edb812af1489902a139c8cfa2bd816b02c9e2 16 1b3dde359628ccfbf0e74e107b14cfaa4e26daff66910070a93174dedae699ad 802a47ca25fc2644b50ade8d0038e002e0dbdf0601133e37c651df65a5cf7438 357bc9f3779ceb69872e54c110962ceadb5fdd500f21634fb7a5a807a12c44a3 a893d74e746e22cb0e5798030020211c94e58f586299f4e11956f1e5d8711d9d 813f3e85351bba4f4d47e93fbc3d9a3196f3f16f243a478b757b926fa8e00b7a dc337acd576846a4954dd1222e18194db5f868c4a1c1d9b4cf31b71a61b85b42 1863e774228dfa42befc01ce48be6f1f75ed4590c2edd4f0891dca6dbb788b4a 2989a1e0f64ad4b42bc63b9ce28e1ad084490094903aaf3876a5e7ac89467d3e 18a842af997ae4ee6fbd4373fec079f2db19ec3b52b96a8d204f39c151d141c8 445e6a9219ee61f63aa02ad0a52551793df67ff05c6a47161d4d3dd6105a5f9f 9cae0f899be60e0e427754a0bc7c53588f7211900c01cedcc364095679903377 9d98a898267b937aa5864ecfa6dec33e9df8b5cbbd1241598718fe66014c5ab7 015934c00a849ecd11c253169387e31a90f27b6ea12a27b2eded3e9114fcd7e6 464e9f58b273060007ca05c34781be9bcbba836b11953aa2984d09da3fe7d6b4 493c1e5f335c095bd81b133b225ec92b346518b83c4bc98e36476a61a3544340 6b06ed736d556be2f86b505f4b5f0fa104c5e5ed7acbc47058dcad41a77412b1 c69e27bd83d3db5974045ef29f5a89c5bde7b4bb1e43713eff163d20dc572408b3fa6dc2a7f4b3aa7f97e925ae6d578615b8e63dce1f73be3d884bcae040d80282000d4afa5f72a4fe3223b30a33f5caf8b5631f088e058e6c0fc286c7087a0ed51f0f673718316ddafdefe1bbc2b39deb6d78b6c828d0b8c4d98ea988b06d0d2fb4bc0899e350bc6009e07c6c6ed7517bc1a489a1ec518526d2d509603c76037d80ecf2bf6e5dc161a0165588ba52050263c60c3ec01838afc17bb614d0ed0043bc2fb750e479ec39b82259e1cfb228571ed7fe23bfa53dd3c4a57294a79a0a98b6cd4bd827e630bf70dceb16287557b52ddd1bbbc29aca84ff57585d79be0a4ae2d83315691017c240b4780ce21c0f5a09299eb86e1d5ece9f39825dc76d00d39e6be5fa0c0cd1a000f7698c144eef0eabd91aaca440d6ac1e0391240eb20cc10ae0e4e7a90015eaa6250543a583073c556ef38698aa94f989f0a922d029e995034b129bba910dd8044273899242941ac19dd354d04ccc2857b5bb3be1fa06e10287137102bb31af8586ea110ff3a477422cda1517ab23104e8e4abf0fa40a651ac254795f9ad9efb5a2bc6338a30ddff27362cacd6a50d55d932e25e7de025cd5a37b6fda240ffc6dfef8f0a943720810c80527ee79cacd3df52c3ff4060bcf9068c906c490338750158aafab06f2a7bdef6eb8fa09c62441eba47cdb680fa397d5cdc0a65773871e9cf351616c91576b59239ea4ff35b1efe6b2345b4c08f417b12d53e2c12311558261dafc8b27423ddbb9a978ee042dcf6744d1a6c5068c5642a9812dea96b539451f3dd9bd6f2378b221626b1b2d7cd5ae4b15d83c051927611081af8abe8bc7f079ab1d4d640ba34a2620a97615f5a975fe8f488f05d747c2e5d40e8f3017284cd96473a683c21bf947ad3e483569a8c90a76b6440ed433b4b11b47240554615e398bdb0f28180eb165910f516fd605698b220bd90fe28b7814aeb39c649d8658aa3b14af0f38554f657d5ad592c626a9ac1e04ff098978f2813b4cbc74bca1654f694a3439d9f506b1642289b836425d9e9629b70083385356402077a9e35aded4421b5f86f11dfeef24619e243d072442fccb01093c75a5896d0099d3d89a46755e322264e747ad607f884c3804da04fc58c4f90276af38d8ad1ce0e7cda4f73ff1b827619ffafd60b3be3a8fafe4b45ec5938c08df22357b1022d184691b616815c909cfbd56b3cccd279edf3b3563f58407a001b021f8804417b24f8f8327de3e4d7939b4c9ccfc7b280bb58457bc2e2d472e018f6d2da6fd47e78304ee6788a857906b2036ca126a26481fed3eebe173374a0de06808916d395d6e2f31bb8e487eb0ab783d0c43cb455ec6a0bd5d4a8f565706c5be04d3464f4401afe0477799de9f848a381ec25e607b0a748078b322e71106 false -check_ring_signature 1da524200980e7d7b76890bbe8c5b9e5eb43ccb0404e7c6435c8bd18f1c683b1 16b7cfea47c73ad358a3bc7099b4a7da8a6b29d48c089787cc8e8e70d12f8cc3 9 57fc878136a61eba33c3802397ca1412242f2bf0503a5928d85cbfdc38ec839d e2bb31d6519faf4b486013d266d6c03a69bedeea30bc87e9737c7fd963f4143b 2d13caa62ac77ddba0e2996c642a269684d8dc94bf275a980328e23df1c60999 057cf72846f4bc4bd1f194756f99efac503fa774bc4167562a6431f592309630 4d7838c8f1c0a4762daeb07df30fca724ccae80b51b59185bd74ac4892c86158 8a3167bb8354a9f1ed0d2257c512a5d1dd6bee5bc63e51c55cd89f77541cd2c9 9d67c3d2cc607ca9f2d6c79dbf605564a6d4289136618bc363fc0f4190233ab1 73151ee7c015fe711e41fa2dae46d6e136461c2199c70fd3e872db87466708f8 896dd833a60d213d3836c0a9e7cdf0b7c95fc3b731506cd897ff51e5b55c547f 31668675d514702d52244a0c8f588cce3fee8f6b1b4f8894c8b20bf0dcf96e0352f69ceb2e1ae16f830ad9e14c70b5b0a68ad5b27c7a25fc4d268155dcc24a05b34a0e96fdfa4b84bd4275de3300cc8df463dffcca435d962b330ec492279008687666c834c8dc8824cc3a4f42708ef66c267641247963f67f1e092c59d7fc0962a8c174858d8db73d4e471e0264de4c4921bbcc9e19822b81e44260a416f90116beef56aec2df9f6173aeeda7d15461bc54bd2420b3d3cedf8ab42ef1edb602ac958c4d826a5fa9fa1c0b17c855a7897450825f92cc36b402c11a5988bd740a5a3e4d35b5e639ce6287d89f2e20a46363a19b6c09f4135b0109ca8325518a052d4247829e067b7e2969ba1968141fb6947b2cc5c3a27cdd7c1061c33abc6a07a2ae71bb4cc3625fcbdb0ce128bf45e2da5c1e99dc423336bc8e058d0595dc0a48c2561dd7c814f9cc8f6bd2b652ceabd35b5d690936ba37c421084492c13509df98dbf889177ee3c3071dda63d45bcec304b99e2d61363bcf2e835d2d1cfe0026dea0aa7aa9b3917f2ece0f2a4ec6a03939b1ecc10b681aea5fe3df0898590ffdbd86ea11df21069eaaf578ec7b440291df512403932525ac4f1d75bd0b3103558661c966cb0d216b602247561393a920537b2160c8aac195b34edd95cef000554d1ad29381682f1429be47302283ab23aff8fe418f7e4ab86c964a021961042db7e7c915a42524d5f987b0b7ccb85dd1426a09651aee70b6e7b46f405cd94322b25e31e344fc7559ef37c5ef3fecc883d4cf02079ffa427af26340931a220e false -check_ring_signature 1bd8aec3aad87ebdf5c20abf1f65c075543dfdaa1dffc2bbe011ddef00dc129d d5a1d3bccd137a2194da8d98c813694d4856940bbc9c99c37518793876275871 4 816713218edb08244e7acfd3eb334a0bf8c8dcb61081d728c229c5c7d0e5952a 91f3bde11959f8cadf56b493528874a271ccc44f87172085de3a83004c5624a3 8dca1af0d78ba9a3ea8765bc90a2809d8c0c4f4491252b818e8e9096f1f188e6 94ec6d06acba2a13873b54f59f96b63c7bed5c5993d2ce424a10820036b10169 bfcba5d569f82d8c31741fcd2123bc700e79680ef8fdb1e46b0202e290f5b60037501bb4317c856a6dd378c1f256e3ebe87b5a16ab1d69dfb401195e2e48f200b0177eafc5142ce8f260aed50baea6f558f90bc8d3bf8d46415b813fd4a4fb0a807083461dbec3563e54d6b6eed245336da2b935b67a14e451f8093aa4dca20c75abefae5b291d48cb71e9d0b50357af94743892c718a1e0759598229aa3fa084922a4ab2186a48f4cd8868eb41461b81fd4d1cb6dfc2c18398e1f4666563007efd6499ebfd824c0f4430f17111057e8fd7836666d27bb738743b7b39a80050e982249e9493266848f2184860ccb99d3fe491e063b2f32201784af6d88d7f50f false -check_ring_signature 386b42fe55cdb8f63b43ec15d2a83bfb40ab0476a3990f876c5b8fffb9c4c290 b2c54387fb25612f16ab3230d01ed54a26ca067bf47915f72c06a5fc4dec3a08 1 065ecf56c7ca0607ccd8844a12807fca2d7bbdc4f7eb3592812e4cc499e9df06 5d9be95ef4ee331453a7d2a01c5471570a9a45ddb75f417793403924386e2f03db9889200cd7fa90c4abb8db3e4b72987c362da517bc1bf5f0bff2a20b098b00 true -check_ring_signature 7b0ee27833bad6cb7613dc6a4e05d3cd682c040fe7caa0c5cf74ecd68f3bbf00 d0ba68ff8c0b2cdba66486110a3a87de28b3078f0daf077900903f18db17dbbd 210 e9bc0eb334e8056885af67b0c1a85f9e3fac640295b3de3ede158bd3d236f304 024fe16abb8670153af3d2dacc2d6011a9dbfd30a6fe6751a3600f3c5a58a358 2934ebe587a693d0f41735df709a4e8777fd48dc2e819bd1bd4cd65d12238cd7 66453945d1e1039508739947f574635853817a8f441228ae5c121c4ea58995eb 1ef584303dc31475dddde3d85baa586c67d055906d4fe9654e00b0a205670daa b6ed8c90756d345512db4eb25add05ecc5ebee2b259044af656ca4f76e743ab6 d77fb59cb3f15707ffcc1abd7a65f9e503edbabb6bb12fd6a66b45e760961574 e9e0bf24923407f95fb85aa57540e99d95cbbdb21395a662b6a60a19f8aff650 e32bf49a6066a13dbf0fda9c34f474de5e29aef575f55447532280c794f85288 5397abaa4a037c7de18cb766cd26d1f9bbfe80ad0d3d2300abcb28ad61575483 3a26dcf5ab0fc4008a02c20e9c9f99831d423ce97da7667a0f20fe3a51e94352 bfe6615b9a16b47997665a77d5b7340025506f6084336ad7c7cced5cd17a91ba e5332191d688ee1ad580c8c0668aaeb48c3b628776125e720c5af09cbc98de2d 2a61d68dc1c84c4429ed58bd9d890e4d9334a3368119f27706b7206123e6a4e0 f8c1c6cbeb6836823cbab09e1cf26bca193a2b5693095ba272a28cf38c08b4ce f1623ee6c79f4cf2f66c2ea9a2b8ddaf073628e95904185d5e9564d100eaf252 c102000caf4a1b02edec86a1cf58940419b913a9a463aa8651b0652046df7229 90011eb484aef7810133b5028afeb918eeceb26d9cf2c7ca92d1b2e704b954a4 ab358cee22aff06b08417baecc30884e6a8dd003d16ba649943db054a5f07282 06e4e45ae4750771122b89fb79a5a67b805d672b2bdd4e5e90735024db2b3b83 8149aeb28918a187c75a6ad220f87b76c13ea865b2b821402e5d30fee45ef35d c07408559a17ca3042222e5defc63e5f7eeb51e1ab4786d71e0eda5d3c44613c 657bb71a98040b9d7f959cac993ac053ea6342c258a4fd85408e69e1ff92c285 5bcba48cc3f17fd9c71950c6f373ba887eb734dc1116b58ccc2ec68f3716c4cd 2baf67bfe3b11f6e75dfb40d3c86c0357e8be9490f6d4a8ae3204d83059cdcd9 a0f885fc5da8375ebdb767e20260fdea5fc0bef9907c4fc84335cf6c50dda67a 63ec203a17b9122bacbaa7a62e68c0b12b84db1c64ad261ef99fbb3dcde2f8f5 d88e4a3b6364d6d58722eefc79dd2b5a067f1877d88c7b78ba6c8ecb4d63ec81 18d229a0beff938b1c4f96dbd423c39de4a6f7f1b97fa02dd477268e380468c4 c94302aba0e7767870df5961fc6b2bebd5c3aaac911b242d8af878b4befa7556 d69ac826e72aa36b2d71831ced27203e38b227baa43decf8fe4d52e054ffa5bd d248d33d08e93ff3e3d1e98eb4dfeb12b73c08548b7188654834d3e6c064782c 468e411d6f41336f13aa3638293e4ed8c10f022571bd0a8ef24879e7c2798de4 3564e81d622dc204c4252ece78fec1efdf423b03f8e76d4904c1fd1d9c46ab9c 7e1aec912d86d9fdf26a9beeb1ccd84c8580f49aefd5428e48187377cc647f2d 7aed60450c85040a802fc08ca9d440323fbf8847f94bf1ead7863dd3730eb312 8598fcc7ba4f96e5b0780256674deada9b999a629c754de8d4c69a76cc18eba0 d0fa8f5fac94e4128754897ed0b2122a44a0008d09f018f9ba077dd2dd31b49c 801b4e68887849c1076965c3b77334f40adea11e4681c1f6bd0307cdb06f885a a5fb4b06f05544936a6ac489e9d40874da754a7403b0297831fa9cc97c7fc083 bd89a7aebe98f9d288ab020b563cdb96088000ac09dfacbc3b11b3bb96a3240e e5b154085504331ccedff125e6afe0ad41997e28046114e714d940f70b76d90d 498478965ac05aa067947bd3d9357308537f60cd0782f8e19727301a9a6a0913 3a53f4ecec7965e055e5c531b9e06cfe7c661402fb3a064ee0a60840d298b6f3 561390a93bc62e3afcffaa1b5421b438ceae5dc5f65e58a91346a3ea18adc661 c5253a7a475a6ee49070e716574195d47372355d7e3a03965589a1af90f48cdb 56a000f72b88b1c67098a7fd03860a33cd4355791c304af10f74f0d4995f25ec 87047e6db174450b6f7c62608c61ec1a6a5858d2ca3426361d14f94e3bb9126e 158f4180ea2ff2ebf3a78020f8d71b9187b3ed652066f49412abcc872f3e95bf b7fa74c4abeb8f01627e288527f681dbcb1308d671ed9b752e8dccd3713dc65e db938b24b32a0bff2f4c97cd2598330978cdabcb7167df3aacc16ff922dee85b c90b69d9e3708a1217824dcf316d76bbb8ee02c6dcf1619a64befd05319eb7a2 624d9b8b5b7b2b072ebf06c4ef0898d4cb7d01aef748282a8dda1c7a4a90b713 917d6f5a29a1aa35de303600cad06680a2f0baa045b1f0f8beb28338fdc3ec53 6ea1a902d73b295252e7cc5619bf1ffc7cc73e252112bee8971edce5f05ff273 316d357c3e3ee4c36221b5d240027ed44b1e000a27129286a1924a5a544f19c7 296f586066d7c0cc65de46b02dd855b8a42f4f9a7b354e401e19a33998f68c0b 6efac760e366cfbf15f97cab7eea2f67b97cc7a7b6bb06d816ff22db4b2e193e 3fec8daabfda6baf49bfacbbce45a307fbd82365911dc25506265fb0a973e2d9 7dbfdc93afa7003031f31396465b038ff72a31ba109a38bb788f85172620d897 7414184c47378f4979631c8b35fe5c9eb58a9d3c4443eeb8eefbc97643bb292c 562210d38b79cbc704e1bc3917579cccbbfb0e3f29c962b3b861f12982bdb694 7ccd875d07f577787e2e23aa448ab9a7fbc0ea8adb42ac915d7e0ad01ed20bd0 ae4197774107fc3fbcec53e08fcc4b55355a15e8f8278678d9cc77f78fee56c6 6d8970e8e76082d4d7e8239ed99f470072d3dfc466de40ccad83549795216c7f e1be985f501a4c1e76f1adc93f239fab36dc2e289efe4ab8bfa9806c3dba764e da127b1ff9a50114e80d0c28eeb20aa1bf6a2dc37aeec86566ad3f8b874e1dda dbfb70324a348ec36f2b91ea7ed5d637b7102085a9536f03c3880532039f74b0 17d98e7509d41b497bde2e2ba15ebc56764b6d234420de44d2740df1c937dc48 41a210efa0ffab41e587e2482e50f7a19623633326761a3d177fde7485ec5b02 530f9dbf6b2c18c7008c85121b44195c078ab87a5a734bbd3e0bf29fc443baa8 b65db0883b6a5168b59e62fe482cd19727896249ee9e49079ccb897e76f2bcc7 0543618ff15bf7a2c793f98c2d491e301a7eb86e392c39aac3af2c8c45deeaa4 eabe90cb2f71c70dd9f41f06247f7bc24544e29d1e044c8029919c7a668afe7c 357e3aaa231aa43b54dd806b317c744ad1ce55108b1723db145c5a018ae910c1 155bbcefeb47b97235b46c7553f5d79687aa6609a1191729d026ebc81ffd3370 46246a57a1ec18aaf8b8ebb1228a21ff3b1335c6b0a69d5dd71fce57124dad59 106a538d1596e6764b8bc9c055dba69a2755e4599a8d4d7d6b7eab6eb36df4b4 fc0f76564f7ccffec6e72dcc53f010350a6b7b4c058544cf4a135e6aefc57b47 5c2d99c6c2a206428a2a21e67435fceda3b276fb8981767cdce618de709c1e17 9a1fd96724b81c7d2b6f343c33b296b240251b21da9ed15b6a3eabdb7f2f045b c9592d3d38878c573826ddaed6ecacef1e9b8e2013af74acb427f931d5e85056 1ecd06d05ecebe2845070dd4612dc811862e3c4e0e1229f58d1565e6ac0eac31 61790b505b632e52d5f763db6f4a4f30e1a67f783f814efc99ef6434e4271eb4 7bb1a4481ce5898164e6f6452453f59e34a06ca8bbf7cc9a3a4a300e1b132b2e 2f1e0ca738a22773129ce792d59d274de05d0bbd4615effb41bfeced0bc7aed3 a54fdde178992c49b84a35a66beb26ac9ca9703e8a57cfc7d60c695ca3ae8368 7af137709ab78dc935692cd50d6f45f35ae4f5cfb569830363f71a9d14e9da60 6067655fad35bfae5e3b56777728bdd9e9c955505f36cf800280c75aff818a43 1bcc5ac047c2089af880ec0c2cf6fdbbd833bd46f4c1157eca3453684af5c062 1fd08b9f0b03e68d3c8fe7c5454af615f5e1b82481250a977efa7db78c90b8c5 7ee46e67622fa5aa0f24b8df4be7a810ddf8a61385ed470bd60d129afd391f23 cff979748b86229c6d4b7f3f6a67a334c02a43fd9d55ffd8f14b92d7ab056ddb eea9ca7133ca4036ab7c14b09804dc4391c15c25543f72919dd5ff58d5434783 b87b10fec882c35cd92cdf07b97b2aeffa51157cc87a2bb284f23b37bd9b4922 cd63b5be0acc158d100924bea6d05f7c337ea8024f87af5c30452d72a72a6d37 c3919c539347795df5495e3cade4806226db9ec5100dcf78998664519a739e54 d2242ada68c4f016e6d99a956a502f3ca8c713d47201a2c079aad3c2829fc5a2 7376021ead8d1841b84be9711ae96dc81076e1337b3ea97f8e01329800c7b011 9ffbef1eabca56be0a186e1ec76cacef074b0099b991abc49f2697f304410cd8 567a122128904ef41cb5c019ec0d3ffd4325f3d658999233314a2093e50fbde0 1d263273f9a04b4b4fcf6918bea504a85bfcd1f96f7da53790d4e6b258dd1580 bb76db05b85379a6ff9a2769639338094b35a411128f1cf70a76a4f616aa2c48 04e66bb222362cfc9b50abf6cdcc6e4c00a78a856153601b983d0e8067ae1dac d04c4dc3b4f41e7cbafe488c604c8bd5b1f7f456ad274c788ca5a0156f34beec 2520abaa447c04da732082d3a898c651360a20ad7cbe7ab3f555513aa817a6f3 74031a84b1c0121543b98052e5ef0a7211b757eef456b5371566e80cf829a593 f11c33817f6e6594773bfd7e2e164e708f7b5113c76a862b60e1377f4d2d375d 8d24d2ee6cf732fd01e369a67183ebb13b841baaa3ead331b6d86549bea78e21 c691d1f09d2d4fa4dea54798ceaf5e480842c7a9a1d07494d40e8a7cc62f7a0c fe2c3561bb783f978453234457f58552355be2524f4dba90425d94cc225a3c9d 480ef07dfcb700a6ed90eab0efd7de26f377582e0586c9de3a2f8f680fdc0e65 802f31982225754711d898beb5732ebdb94486d8b2da14f907a874c45cd7de3c c3c2062e9e8c30772c6a172026b52aac1fb3f341e7fb417b53f6e2393c3fa5c3 05c841499513e0b38b440257ce03c96279a6f7d548cfb6a7d1597e7badaec48b 95bcc541d049d59688861067dc280d99330d229efca907cc6e5ed86c12785285 31fee95e8805f3692d405a19980d10a912b130e716accf6afe51a2b4672014ad df1d2eafbe75d32c8cce57de9333b68068e491601a7fa12afe3011d60a1c5f53 0366695ac8bb45382d65400701cade641efb611a0e9f2f2fdf5fd7f98bc31603 20436fbc696b6bbaa73128a46902cbafa937b4696cb6eb1c70cb6724f48fd1d2 34377ce4cb5d2022ec3c92d92a364f647788354d8a718da73d04cc829660f989 93be8ca380764fb5da6539ca5f27d2fda1daa4b977abd6e55c43ea284ceed5af 8e870b9a801104880a1ea33dbad13d28552b640649755ee583fa6d6aebfdd41e c681a5cbb404a3f661fa8ea218467e228e0d5decc6fb91d750e5920af1fdc25f d0c3917964e9b03531336991d8eb204a3eafae2d401be2198da3b0b1c148e0f5 937faddaa75a2c658c74703decc2e6a324c147e62e2d0469915162d5347cc2d7 37b18a96fca31250b2b77a7e5a9f725933187da6e6fe94a98c26db003f921660 b8a4ae8f588d8415b59fdbc191d687f34f097476fd1ac08ec51b43c8cd7cb178 8d25c44014cddc48ae976e4cc799abfb121aa94ddbc660389555e383b6630bda 3d9f9a05085f4462bb6fc6deb5db632ae43b46f4d10fdb508930bef7b803e065 7e63ca58dac095281718b89ac6b82272625cae747838b8b3b64c9a67f3fb6d7c 44ad1ebf60a22a8e28814f57f9b4c8fbf4c95e1951a33e0e5c5dc7c2b9288705 d09c5ee1e670a0829612cee563af57b4a025843df0b7a1c6120fedc499533b23 2534dba6818190dba3b556e77058af3fcc1efc63a9f57d2f3799d576e8a07355 cbf07e1db37f0d93c5de4dee5d56fdb5a8aefc97ce2f1de298c66d75b542f73d bf312c63c512f94d7453d6ba9e2d247e2d411b5ad37c9677324582046015fcd4 a10a7aff2e5283713933083c1dc363ce2b9d83a45f004b9d018d48bdfe1b8cf7 bec9932d0003a8c44f4223a0b6b11cf11e3ef59aa690acfd73790a72fd1fdcdc 4c9243295e9a45aa0800a493b28256e2f611c6d752e2f18d9546e90333b9aceb f400c393844ea95986b290df95f129f0bb75f592520f41db36e95c83f86fc24c 5d19d1520f7fe735e08b9e6d31220e34a797345354b52172292547164b206a40 f90efe6445abbb732ef8dcb3b7f31cefee83b7705f39a241de6ef8a7cb883fad d6f1e5e09ff40d8c90ed3a4e37d273558ee1125c275f76fc7c7fd5d0b50f9fdf 369754017598ca0c32d6c2add2078629e1636f959234a6775b3c76ace16bd1b5 7f74717310ad6ae831b24274b20f70ac90e55ddd87562ce7fe47ee77df319809 f154fc2a0eda09d5a573fe2be39ba60b3a8d54c0544f7f06e27a8bb6820b6699 3916e2e6bb5083d8366fb85fc5a351bf371f5392c5b208eb7c322843aa6b256d b31a7215ed782ece007c436d581f3d6544262428be933d3189448a37999af19a 0fcd1f82ca1e100a4acc1df499bfc879b66f6db18bbd5f8b529c65dcb09de5f6 edcb90519c26d246b6bde9d5b3d24b59706bfef2d81761e85f83ff063176017c a44210ffd299a097c448f0d421556adaef1dbcdb5fae733edb927d8c732dc5bc f0f7067f91f511875a871bf11e5c2eec8e1d8a7468165e176a87c534f01a9595 96029f544547863868c315d253a89cab2d7102b64124a7a6d38ce95ee182f85e 0c02cd944e71cb0a7deeaf52f6a0523883a6df82e3df2556bf9e3cff97a90de7 ac0bef70b63000c158c60cb13cb0de919614dc66e0f1fa2e063fd53244dda335 1ad578441d785d320761c5f318d33ea4811241a0af9628fb992535693cffe907 2883d6448c7020d527c025e8bc9f8601fad249b67491fca9b933656fbc2a8a84 2fb242ca9725c160a49ebfac181219ab08da76d0f521dbd4c26edab1be22ac4b 4bba56affb22ff1e98aef344bea8c574a814ea5c90c9a528bf6677f1007cf75f d101d6e37b50d825c7793e6781adb36dab3aceead27bc555beb180c0b69aece7 308b9bbea27377cd4bcbf64a56eb582364e28fc21885f6bbb5f5c5a4c7184990 a7058ce3c2e286a737cc0fd6dff64623e94d4f25c43b2c107559e03ebdc777ac 7511c0edcead4d22f64d325d37d1ff977a190c511a02e25a4c8f3216d86b09e9 1a29c84e3ecdd292e5ad36ecfd8fa2e723d3bd1519d8a0cfa0bafb51cb31bc35 0f03429e804c0d9fe07567655cfa3953e02cb42e8552c8505034961da5299468 f321075efb2d9a64a1a806831acfeb5ed19435f845257021b9df8aa8d21da827 eb5bf8ff77a4b3df45b42f2e17461947053af8df8a1158b667c016bf9bb55533 f30c64d57797a3d7f82ce7b3268c9cc74856c3d66b267eef1fa61ab9d66e85b1 390b96f45c64211cd52284edabdd4e0b466c0464f4839176301bd04e231e43f9 cd19f8fce2878178260bfe88b201e0632751f088173eace3834adad568dc7f4a 164fe14ab557f3be29ad40f22dd6a1952c0dee6881a99415b7143bd5d51e5e3d d4b7ce11e7498ccf0737e1583a45315e461555757c7b0d0f9634110da095ea9d e374972991b3d69f8cd3104dea0b0192c21ba11f2dfbd562a8de2b50f2389375 cc829eaf4e0e4c60c4bfb1762867edaaffd9665d8eaec016bdc8a5beae130ffa 73e95c1d1b895edcb15547cbd3d7d7fc967386ab7c10d500c9c5ae2d7e020a2d 69d4c241ffc2a6866b6bde4e8774355121bb87bfe95d2bdc21fb01b9e9a4d033 7c265b9d580b041d52077127a719adaa0e014d35c50234c75d0e5f0dad8e732c c86116f2e46da59b8976fc42644c5fed9c2e428f12ff072847d6eed0f3cc3e19 a8bd975d96ab2b4f6e2b519d676be113a2df0384f7b707a0e7dd893c8bcee836 a54bab2b3b8f2ac6f7477712afa26af2046f919f8756178ee936ca868b4bd76a 1d07b0d3d25884ec8f3fc1f0a7c79b7edc88732043b672907342d0d3863d142d 370c90271989511a4eb28e5d9e87300a5354e7cb7b4710eb737096ee2e61bc2b cc2d221a614de911f45874f9ee5db12fb55ff06bc2c48f05a2e42da9fdb31275 0ad8e2db05abce7baddedb7a4a68da67075945788709b8c6f7b9116e3be370d8 2ff6ad3751749b5c393955a845f692b34c2490336e50d3e42dfdecaa714fdadb 2525712c27dfaf4c23baa666446f85ccc77c563281fcaf634f5737579fc3a62d 3b4a5a835c83ec6fc8f6bfb42712f4364650389bb3529e73917a8daa405b4a78 addd9f640232c7ee5bbbcedc98a62180de0fcc89eeb91c155c30b4d95b69244f d12827f603327e930417cb060837b51e40940b687e63fa59b4205650173532d2 5c67f213592f92223ca45490089e54bb63a31b7b21a906cca463a97889c7186e ced315ef1e3122f79f0bb2fe037c8730ff9a74f82785caea84bafb054bbc6c60 f895036eb8552502857e06f206f52c8925fce570058f416e832a2480d458d785 6f49faaec09b6ca4cd8d5d18fe979d5e3c989bd5b5525a1d2932f8fcae0c202a 4db4908f51873a1fe0ce8dc67cddadd609c4950ea9d1f54d6529083f6ae0fd07 fd0767f51f9f0af15d47f0a04f802ae06337088c149ede2f04fff3ba168dd907 68db263f78daa2b3657f2413c4d6e4ff1940773d1d04c0e7327f5dbc062d4b52 a7ca464dbccbf9146b49c582205513957121c6764e89b5c3515d39bc400cf740 3e3e477137644a7619b7cbb2aac065c63713ecc27fbead6154a859344834e6bd 539de1834907d44c325c8c16590d04ceda709aa5d0d2531c1f33d08ea937423b afc9caad0c0c542ff7c75acd6e26a426c1575af68085c4078c4fccb411fcbff4 c47484cc270e53608b31a221e7ab6138be080f01d28e5a3569da9e6029696536 cd17752a67d06646d93210d10cdf69d31a3ffc053af196f6c43bd9158a6ebd6a 1350355d29ea897fdb8f34702bf8371a9bfd61174270fcca971d6a7309706aaf b26e1549bbd535ce3ee485fa190bf836fa6a79cc493e94277e4048321bc2e28f 0467797e02441aae71089ec651738c121cec92eed2e71bb31439cba92fafb6bc cdff1c2f5185a0e8d8d141f054f02aca42a0b206974de6fa12527e35e1aa9f68 d30e1f843b9c1408658b2ad560acf450ff2805d86d532397fd451ff646635d36 bf74bcaf3be844d3e9fdba2afbc43d346aec5911f2e4f46e1f49f8ae9cc34c99 dc6d76183dcd3a7b356fa8b248f337243f2fe4516684131f1c94ce90332252f0 455b4f65caa4ebf4b3c5614d1787f1f327307b179dff307bec456646ac9c4b0e f7f10703653cb3b53291e81151d0fa017aef19d3dff2551a90ed85a281be9d07f9072c646d889bfd06a38a5dd062aa17ec7f8b06e8c6e2f72af4a2c62a31a4083b600478897917c451f0c9ab73a5661939a850064444c33913a241f943b55a0bcb2f03383d8a44874d878c61884b00fe147d226160b78a909c29e635046dea0510e38bcbf049e442762fdbc00c10aedb5ac84f863716f14e46f4edca71c9cb00203183896907925cf60c48eff4429666a1de0c20b952b1163bc518de3d5fb00e062f400ebfb04aceaf266dca1a214a14f8b4e8fad9716527696771faed3f2b0151fa418cddf0cd352e35e34584c5430b4fb5dfb991fec328eefa9e7aae75b50eef120f9aa3eaa5a4a248e8aef04500bb83f55024a10c16216b785d0b6407700a93eed35eb55b736595953f4bb653faffd183c67beb4e0e7b84ee1bb13189e20a4f45cd951cdf7c930d19427ebb882ccaa74f44086ba648439794716e2c4004059198032872ae44c2958fb5987d9600c2b68dcf9fa9ad7a8639280280769b060e6623a0ff3c7c361570a490edf6c217703a573469b67bfe8f7312d471d74015058e32b244d273e8e5683bc08c70f3e247c8dfdcc8151ec03d6bfe1a9f1401160397848a78828231b814cef18ef23a8db754268c97c382c114be4800f9878a740523e1802a732cb6efc8c4870c05511b4d1cbc0c46254bd918ded320bba9b2db021ec7d948622d46ed320dc3d891673893877aabf2d3746bc221d4d106f47a4b002a670fcd3d085e6f179d725e6e69319208fba1ccabb30642f970f54f725bd30337a6a08e821e9fc0209f360a76230e0fc54f058624df4e261537c94ee13ad709670259a70225f4ca08029c3471e1204926a61957745a41493da8eb225ff1390a6b9ddbe8eb3675683f738db364eaa778761f25c3f4490154a855d0e1a55c16022b42f9cc658221c567960e10e36ec001d73d688c4e7701126f8b80ec600d9000715ca40c04e49b2fcd1925546b0dbdae7013ce004becb637aa13e5e88d946b0e58285107e2c53a84195a7460bad985fa09335551e8ba37f4919d569839a7e30041ac24f66551ec752561a8854dc2409cac929e4670494f0c3955fe121d57cf0fdaff327cf923efc0eb73233fc1153c95c24536657b64f493a220986d4fb61d0e87f4f14e50221c6049802f2c12e2781211a312b19ce8bb955b7e36ba227a0b004cb0f330318ee1831a7dc5dfb4e90d99895b94ab896a1d852df7aed401ecbd09f28bc61ff4d4090391c8194ee3320edf1953d748c7b76395c9fb9bd479062908a30d841529d671cd4293cc883f1816b1de2efc8374c58939d77e64c04436350ee929cc21c644cd57451e0f1cf43b0a427feab040185a9a993d6257fc2db21e023670fc3670af0c5b713dbc0e6c9af1789f6d7b6dbda35442a752d33804435a0ab26b2eb3469e298cdd776a522849aaa07bd8ae8d0705a645496f7052665cd0017766ebfc9cd209d30f26f412136a6455bb07dcc65d4cda424d6db6bc2bfa150649b57e0ebcaf73753242985e081b5c3d2665b2aeec998bbfd5d5aa04128b7e00d6ab67c9f44439603244dd4d316ff64c33ac52fd3a765b62686a6c34f401a707eff18c674d224f979bf7f2bb17bc3e0c1dd0e0cab472f1b1f22a396cf2550e0a4c7b1c7e596a447c58c0540ce3267b56866d16946baf18d9b3eec2745e98bb09223212f657a8ec89a779063014dabe78009fae3f5fef3f19678b081f55001c09d5684171a9a28347469cf9ccfed5da63ef57c3f718a4dced64acd9f9ae9dfa0a38df23b1e992bb2a55e24d1a1d3bc9aae844c27d3c73e6fd27462ba9cc23540ce4c782d3fa274844d15c5381a560d14157b65385f1bc35430e0bf58f1ea5c50756d590b4bf152fe4c4bd631cf84f72f2a79419945a7f73fdabd7a84b8ca4a40fa373ceb496fde056a72bf49836922e5687f12e23499140a45dcef0db3663e70f4784b70728ce2eb2e09cf20b5b5b5768147a34be0ac1a547caf21379eb0bb900c5b0886b38eb685e8078dcd9a41ccebe3e903943aff82c0c931d195d65c8fc0bbff2be44739524dd5f2b6368cbac0c3fe2def1a06015c8107767fc9778d3310122939a6549d90483f12e98b037860542572ab82f4cc2d271a6913d2e20d5d10749c003505a87428e605abf6242f37fda2f2168a69d7b3c2486c6d43e2d02bc062e4d0738fecbc2f45ffc78e209ed6f60e077f349a9ea92cb1ac3298c0fae1404afd9691caad987785c8dd851eff18551e6677f9807f5c7cdfd9e5e9a288ef30fbbf617bfaa7d083f23a96b3391f4d89032cedbc9a194f90c0d19f5d3db2766030adf2b384b26056a52aa4cc7da01273fa4307ea1a000ebd7643c2fe118f7700e3cce83fb3f7a94f47c9609c1091388de8093af386355af013283431315d62202eb17d133454a681256dd1eed0be6aad04cda3b838b2b97efffc1b17f7d791e0b17a3893357ad5b24dde2f0bfedd3ed61f7715f7f43b95d384620734f6e046a0cece5536ea49fefebb29400cd94c836d9c89eb633bccdb47fb152a2c42fe786092556bcabaf7ca03d775c1c35ca443b86529b75e8daed5f63ebe93a14b52d0b08eb3ce7823547e7f8ce1d3511338d330fbe1c9a383739f1e12e8aa76c7b91c405cb32ed62322b7f04634adff88baf7499a8c11b1e77241b7e62af3d4486471707550c234ad74a8fb7193443f0e915adeae921c8aeaff082840f1ea59d23a92e0b72c9934315b41c1044321b0fb554968dda8f5a8baa9b8e5d5c332e4cf906890fee1def9cbc18cbf549146b77f94c00dcc306a0cfe9ecc3b25ea9dda10c42150e3ffe2e9c62ee6c7ae030603c93539b4a86bef46f62daa88b8a0cf65037f7530e3da622484a10a4454fab8f01dd202debeaa78eaf3ffe972c93b5d2bff450770e88745d627b685ce0732b837fac30a8632bb9882f6f657d4f9996225e1f62de038389d5cb0725f04511e8a5a6b3b214a021c8b7b9962e620daea5d6c79c53a90954ebaf376acc9e6d42d0ab6fdf7c212f638efb7890743435b319e8619b699f042f959838dd671fc0eeaadde22d073109810c7b7c4b021131c8294547af503c04523ae928b601c8a1852c8b174b830bcf044a11ac4bfe8ec1ca6d1a0c5ba1440a8846af790d7af3460f8a40a572043825769770ed3991fbd6d040df55ed51fb0ac7b7750845e656835b50e7bb91199bea876a5b0a90abfac3250d27ff414135067a2886f168abde9902260fa3a692045eb12144407dbab34c6fd026b552c78702079c838787c4ec8823c449a76bb0ba19abdd6d53f9f23a1395a519087eaa94017121361f8585cc38134766f718f4370e18e4152468ee666408792d227d585806945971fc04d9fc5aba488bd3cba2749b0c7ab78e196eb906a6ea82b7781192096df4d9fcc0fb76fc884703cb5e4a96d6b627a504767f67d00feb42f400ecb00e10279710024e8eebbf63d7f91c511b2e3a3e5969714bb8afe03b9d6b84c3990b09173dfb5cdbb953e8d258dc51f91ec5c04b25e8670cebf27239c1d2ab25770d68224dab686e9731b7b601cff91f0c84df5a0d1ad5c89f4a4375541fec31fa0cbea775c9be69d35251d347fcab8d053b9e38a86257a93e825706a09cfcd4ad0c7bf35eb8ddd1024d679d0a95d96a5d7ccfa005180e82212c64f5418395389d016a0175235bfbe26f1050747ede09f8d76f193cbcc7a47ea569e5c3ebada03104a2b7294948fe962f441583f27aa9fae76b8dd1f99e9c98784c75484b66dea30ee081fcc8043f2d52f245f41ccf8232becce4cbd4f0d54b5c7a7365ef70e7440e22952ecb05f50eef89ebdf3cfda109c25f59a764c301f790b4f0e119d4107503b6cf5b409b144e9b35cd0ff4a0060e02da57a9e6c03aa4f9646ab54045723807b2e02a8507c1e7b60c1150dcc542ae79e701f77539fb88c11d5512285a9789042ec98ba4d9ac99f5cff1dc78cd48e49c07c055bcd558e55c7faca47276454f05961f2d6d12fbf509da5941ed813c3827e79b4c8dd3017f13cdfd6184c23a9e0de1f36457cc37c3083fd9cde1c8e3f8c98a8da613672262a7665ee136b4381601e3828dbcc6c5129f7738d242446062ff0a029485ea32598a3c7e41327ecbb60565c9d0ca9527e93e6be1c7c5c7a98762d30c3939118b6f08e3112f6fbe846e063c6b5526baa822507a6c6b300a21999f98b083fdd706c1a9efd6f458956fb70bc4c29857cb2e49855c17d2e01830b030e4d9f158aea5bc33388b9c9d788ba00537726fcff793bbb7aaae04de91a123acf4580e0f4c2a94b4ab6d334debb4920eb0bd361130a99ec38803e5ce081932b1eb5d1c2c9a8d20099c941983b28a3a0ebe796f36018507fa5d630139365ce6712977dd63bbd23928e458f87e0804f3084a198cc7e8b0a225dd7c9690244c6141186d9e0fd0a16e948632af15bb138204538c3008f82d6bd4ec4b73735fbd000fec6725d9ba8273360628cce4114fd90c013a55b1539597cd44f628536a43d37ffb86b61b2c544302917091e538307a0cd1a7c3d610e889d08762dd31b54469421ad25060d0a21f52f71d5c642ccfdf08c8c80b45eb1c03128b618c2be71b3b94702385e136fed97d39d0520e7e2eba0f8a705530e46b14be607dc525168165d7618d36b168c008111132bb6cf4731407865910527d9c9dc91b2e55b73498e31e28c65448886de03519c47b0a05eda90ae3e1920f14d154d494d45400025e38d21e9c90c3b100190c933b00350c026e0daa99709e8a4ac767b9aed43dc1d1e3052b50fcb0c77b3109175447edf889ad0df850cbdbd4bef60332f1fff78b1eec890233038b91846775e59e5baf05058b0f89267d5effb0d0fe270deec9943e7c292562a010a98275aef6cd6fa3cb2a8909cd9bc7cf04edf5ba7095259ed1f9e75cf300f5d37301388804f364413ea319089f7f49573c10e4eb69e0a0bc0359fd94cf4daa1fb96ae8cca8881435ffcaab0061fa9b5e9274a810c63608b5d877d177f901b6adfc700648abb77266a09be50800184442a9552191d4bb242145507ef30cb963816520f89499596ddb13457501792e6a3c205c4e9bc308607d15146ae5f859447395d567d181e8a791bcc2e5041f6bc7c0c1633790fb10220d429547081a07c8d3a026739a9028e2553d806702b7aea2cd23aa52bb451d391cf41913edc9bb3438099e2d871870403a8553800b3f3f751ff8b304c148ea247392074f50c1f59b705c214edc37408b2f91352f0d1dd0444d237348d5aaec101a5e879dc8a858042746a39e88a380cdcb37fd760608e086dc12bd1a03bd8be729ab24ecaaa7f3b2f633cbbb46b8002449aa04ca0cb047f5047f4b449d0884aa47e921b7010f7f99c868118d324df0e99400bfbb0a38f8ca64ecb36ebd859546a8cfd5e57b47aa86dc063cf49794c094b5bb42a5036c1a1734e99740993583503da01710b1bbe013d4e1ba2510defa8aab0463be0a7cbcfb64b0d557edaefdd2dfa46c1b6e54a468aadeb85a3899eae0eefb5f4104b23484456822c304dd4072357c9e6dc0a939b2942e7e99f51e4f8a30351f3b0a700aa9a3981a45df00cc612f234333d5a5f335687b931f3f274c3e349255fb09fbb74c59078fcf37d58f908b2f81ba149f36d82b460f7ff363d10ef4c63ea506fdf7096018c7314f79c1f6363781449a2be9fdb9a0ed6e57ed69980557858a0d41b11dd7061c27d9df41c3545e399372c3c522ec8de6fe56555b675bdcc5cc0df64310520c80fe4f53e8ef66b57627b3c846678737cfe6a655cd245fad3d420167457de0cba1a1a8edebdd9645035dfcef182646c0e6118a53f0cf3a03a46b0decc1565b279cb52334cffbacb70a452abe8f7343f32e04b29b81aae39eb062035e022ed4a6b8a7496309ca736b7545e7cd35c695c8d36abff763a0f06739a500a4d13bad02e7496e81b1e1e1f2c08370a6758be7eec96b0299cbe170cc10050d2034fdfdf231e8b0b1f9fb6b63ca7bbf7e5486efab44f2d26af7190925cb870ce13d8e47acc7a677ff2220639b565e1147e9a7e6d041ef19c40457cc9bcbde0626537f860889496f91f2c87f32808fa94fc863a87663e2fe6e31fac8e5d56c058e78514ceb8de21f19b52f0a0675fd82efd64fbd2efea5591e86bb9eb2c7960e8a67cdb1748e2c7f89412c0a93a44ec06b760461af9eadb778f2ea4842f518058312cf2a7d69ae887478088cd1dc13061e3585c4172dff7731e92a4a1f76a60f519725aa767ccf3a98067ad8e4edfe006e915bb38b7f0b9476dead1851903006e0ced8ff864270912c26f8a7a151a863bed425f95b617b10cc2441a67ae0d003287372043bc345cbc2020b4d6d69d9ae492039ebfe9840e119b711e57852370e842a3bf8155d56c00e5f8c542fd32d8ad3b7e55fdc5912dc8f3e54136908b70efde4e6a7fa6b59ffdad331fc1a280bdef966351186284bc36edde75e9ce0f209a5a2476e35108ff3a7b67707dcf910c7bf7ac98b4bcb4e945229f42438839306dbbe7fc95b8c712323f0d4a31cd279109178180570bba4afbf6eae7ab85a410bcb8fba682e945f0c0d0cc9fb2050ec89ac7cc6d6b1c3b1d29fe7d3d76baab408debc2ec24e4b92229acacef1c5b873b7e525b3930da1e78b6685ae85b686d7039cf6db752752752768b4e0119d8cfff45f02ae64a2c9f2425665d44ae451e60a9af3a45d7f9c97fb410269cb5f48d3a115b358c34a137b1bd6d4231334a59b067138a33176cb5ab9da3302149fdb4d5d4c515e8c18da88ad7b949fbf5ac2840d45f7b8e3b47d641845d18c91249da85e6fad3ccb419216940e46d5ccc679d300bfb65709352900fd565f8529330e209e361d56d2e320e22e27f53d5ea2999e05907d3ad0966674e50d617f2242d1c44efa579216fe51cdfba91313dc4a8dec068f46925b987be6fa125e36bc1976567002e0d07ffd106a535d0dc1e54bac99055d6637ad4a300000ffb6651cb2c76da6ecfbbb0f696e188d049fda740bd4d308b6dff4859b5f5d74aa805eb10a6e646f237d482f3c37d7e817c0928237c0b80df8a512e01aa4fb4af44bb9031af30cfd5565e62a5701e082e3b895292aa85b04a4fb776aad91971d2e68715a21b68e5a677a88766dd3428f7b4336302adb9d00673e7c19075bca6303a5f5bdf5c357ecd28c57eef41c86b092492093addd450cf9a815df2a9e31318bb90f81f41feef1975ec5a0c1418e02690c455e1016980c3e05bddee969f6f41de20b17b0d0df3ddfd7c51fc31608cd0bed7fdcc4c886060ea78649e78bfbffe268f9c5760e501da4011ae089b2f07506a8151af44b1d0d879763905759389fad68a94f35ddc8c31c06bb33316df29815f476ecc96b440344b1b49a4720a772778836d2f7757f1766339f251e49697fbe4fa5b980e99604191d8f5d9c2af7adff8f381d9aaca6a87e6cf951ef6f267d484593fc9015cb061d86c413aff5d084fdc54c86266fd2e0ace09d7f91bf2748e18720a0437dc8031c6ac31a033f4accccf1b75728e2b24638788520a876512e75576186f6837d00bd20d0768939ba8bc27f5c140c55a99d834fd439fb49fb373dd2ecd344338d0856d5b572e9dd02d7ed1abd5a8d4a04287f66ccb6c5757b202f6f9614b9ad9c04193528be7c1c5550e91f28bee16c2fb22786a8feb329fd9ac7a8f4eead7c870ffe3f470af0f6245b3d9cfba1e9c2c07e492db5015f7bbb6e31b699bd1a603e0a867a4aa248c539287692fe9571fad6699aab782689d01802757972d00efb380416098f624140e197ae27ba6ce301fa0deb4e39b769d02a95daeb32ecc9243d027d97dc397d007774ddf676778b085d02a3fa3ff7261bab651cacdb11d3d6780eece463307f43d023450036f39aea44acb5593ec533af5965d6f3ff5b1ab8a404cf3d15d9bf4e6a652713c5804f0f8342938ef4187eb1a089188ab21d09f6f10217ee07b5cab0b51824d591cae5c2826b5ab5c61e2085d08fd1dba6e7f65e510da1b99d26c40e0dd109270521b592cd5e312a1c53931a75799b814831ac97ff06a4c8b594ba30b9dbc64711af833a713b3253a05ce55260feccb1838359299f02a2f173ef3d1dcff53237ce3b613387954c27c30d7f54c5b0ee43812fec4a560f177873a178618ec730a9bc958f9f45e5a9f48f5a6e4068d3493bd097d726d00a5363380b92425ae88902835030a07c4e5046b28f218f7cfdf8e95b65c921e40643b04d92ef435f69fb2f6adc1eca5378e6210eee33758e2d5dbf8b21307b190587f53e74d096d44e7f657bf92bcedc62f5927bf57fd6a8643bd92c0c0e04ae02a70e955a8b5f3cad28a617cba382e86018a1e7295addbba030e395bdb1e4070254bf38713d7ff4dffbc215e85135ba45ea226a8a8e40b45ad44a8ab97d8fab0cb7b88f017cfbcd86d9cf873222c339559bc724d99ef34b43e15892e8d496ec02a6b76f2a8cc9b21ce4546842ba6302f11af9f73845421315f3c367fcf998220f4a749dfc36b2ed19e45f96756cb800f2a4b55c90c4c2c4d3fa92dda50a3e42015603d828f603ba4e23503e0229c4b7006ee45c3e137e4e30f01c0fdb517de5060ff8343c3957409e96f9669302657bf2382fb2c69f080ab84736b089b3a6c80ccf3849e867c0e624d189cad712417d2d59401d7797036f4eded35423f3f56604b594057ab76a9a0c1467f0e163f32e11b5db45564c26a31b4ec00d39bb68610dc6ef9a339cf7d7d50d3bfb767c774939df47cbf0d3569e82d25b021fdd08a00d19fedf011b0d3abdbce15a9abc170790ddfd9822932882517154ab20941b7d090e19b7f68fc48399e38baa793538c37233de26680fe3c490c7eadeabd6ae230516893c3e8bdbfb05a6c169b170c406151ace3f2ccfc2b124088f45bc49fefe0f0ffe7553f215a57188528a5a1a4824bd240ce9b065b6bfee1975de0f17328a079d523289eaec82da41ab356e94078dfa89cc1bf376a3deab52841d1ae2a2750928c7b9559133f405576d7b2993776d5665f21e750f23c56854e7982a77f8830bca3f6bd4b8d28c89191f7bab886b9723471393cf91b64f47a0ed98fd7ced380f8d40e396edc56ad16750992fa808e46c070dc6275619f424cb7c5a8f1123310bed0936497a10101a849fa70530344e914012ae549b728be369bd9eeedf07d3078558ff969ef69d6c78234906e7c43ae7a5465ffdb15da87343eb70123e6ab50b1667cc322ee8fa3a1429c6f728034e098ccefd74a6cb1be4fac56e20dd18880731e56d903ebb39c146d8c9a8f0984e579303423cb3555cbfe0b82cb022aa890be7bf927925ea1c7d19995f64532e16de16ea2ed9f43b4e72e88216cb90b7850ac243a1ba0f8cc0e045419840627573f79104c7a57c6efe32659b5dac577b3d0d0604982d295112bf6df243edca05cf6a2e479fe617e188b9dae6112eb6911a0abdec93abb986f2763161ff3ca6b18bdbb7afbd94b7b60aa6e12d77e08c31110cfb938c881b63de88749dd0aed3ef1fccf1b80ef5d2b15c2f87c009f9d7dacf0c6e7ca4e04b628c05f5b9817b5c6a77698c5769494738c25f2221c6500ab73c08658782bf3da18299b27ef2941f11efac5132343e4607c45d0de4d840c5ef430e5692ccb70a1688597ee325cec83eb96311d5faf35877675a4da0416f15c3fb07ebaf4a9cf3c5ea65615eb5ddf9fd517a96e265c367bb625e488fb98c7b420801f60fae82758f473cc08fee68469c43cf850ca9896c1559b78b2d3a8ece540c0352f4664412b626ed6834e87666ea305d279d3121013019851436690bbc18b7090dd22085a10f337e5bd042ac0beec7c0c3ef6f5f62ff8f185934085b7d7e320822c84cf53aa974e0bee2daa26026b0959ce53bbf5ed58e560ca3e2c4b42fc5014b5de11421318ebeac58c393247b2d220bdc4009c3c9eb0c0b90d9c92e1e710b164b4c08b4ea58209d19460e5f2d3ace5ba3a7df3f84b6cda29e58ed65a9c40d30d4b4548f44940dfe9528d01942af8f892614b2e32731a69824ef20588fa800024544fa2d46080b2f6160af1f6c39fa16f04321048e96850c798fb6a2439a059c899dd2e797eb84bc0af337fa5646fc2cf88d3b43fd3dc81e682917f99b39028ae0e2c7fe39913716b59ffdbd8415a70b22ba67aeca3821aa98423483c70600841a9ed6238a157d112596c83345678d683a41e076f5803baf95d95d76fb820193de476fd8e5278efb32cabeb9fe09ce312f69ebd293e88be9e339d1926bff05d8e4bee54433b5fb7b641f90fbd63ece06efd8d1df35826e60adc943181ef60668956316c366c074ef1976139484f7125d36d7cdc3ee9cf374a0ebbf8e5db902c8faa1de8f970daa7cede9d9460671a4d4aa165024ae960348ca5fe707712904f75ba2e7085e2637d1442e8e30c5bf840adfc31baaef525910279aeb811d4f0d4fe2c6f504907bbde4c9b0e5296171dfe5327d00fcbd485c6403ea3e8ac6230a5869bfcec583a4464c388a847df792de96d55898eeb7163128e12317baea2b0bb21eb1d093e25fd9fa9c5534929c77fc94537cc84818b3cb294cdfda98753c0e6bb388a26b907a02503b7af1b53283062085f2bb8de7d5bcd15925f830c60903ca557dd14bed75771cc60158ac3346f333f6cbd06d15e66c815a55da8bbfb90ed52e01d7e08d37b132ae335bf98bc1d99fa5002d3209235d908e373af4c4c705d70d8dd8a29b6dff6bcdfc004f95bab0ed6434f9bf25187ec622bcd61355700474aff6a365b2a5d0c557d11d47503401335ec8d3924ef0c09e5e3b20a95b1b07706e2e9d4ba9626d66e6af0acf087db352c4a818e8e37a37777253dc7b87cd0c26bf841c899dd6bde52e65bc844c7c78774353dfa61fbbf7809135fd7ce7a30b647d8b9168c1a31629731e7a6e8114d643cb3144d123e4594203a8616e000e0a29689f0e7db5bada8fcf6a7880339756eebd06fcfd26cc0cd23c377d867b910c5abaa4d6aa98edef52941d4f2bb6207a138ef8e6082b09fd042d220ce4ebb801529f0cef72c0b830ceade84c974856bfd79cea4f0754dfe81bd99a00ead3db04406ae32da1a6808128d9a288509803351352a987394f2dfa32a22190a110230b6f336be9642ba8aacb187b2f20aa5155311018a83eab05dbc3664b87f14a1e0bbfce7f0ca29d2a586f96bca869406c22b2a2d0c5a4773cbf088fe3feada41a0f6c7b634a994126045be16f227e05ed12b9220c3f416e90f4e8800f24794bf60ca5be208507a3f2c230e6ff8a0e7bec11641cc205652a93840cf88334adadf404d7bdb52a5763e20448e8e6bbfdf090880610e3fc1a87b84c64b0ae2bdfdad40293c807f7d2f2e7ffbd7b1d3dd2d5550200668d9e3b7488d0f922d30bb8943304ad7d56de4287115110a92a3c8236fe4c4c1439f556b1af4aae5747edcd60960b2bd6a24e16a735ed9e60332a836baeea9428175ad48812a6a2e9940b6d226d0103e0e93c5b95930be78e7f10ac8af81548a64af1fc330831636f4cb2d4c46102b9db5ecf2edac945417c482ca85bfc63080b30f8fac3bdc233e0c560d324160666fcdf89fd7bb512563e8a8d7b00c9d83dbf10f84a6148c2a2f17d488ab7b201364b79c792e9c9d436b30b99b3f94a745622bdc7037d1b4a10b902617fc12605c358f0f54ae6f2b1eca84d880ce041ef7a870f293084a33a2e4f1de05b7b9704ab854ce3d9773519ca0f224c865658887de85f311f0f72f88cac7ec339774f0e56c2f8cbbb121db97c0af66c7c00d7261f89e2fb429393780b9d6684a3678c0fed0e979daa56ee0e5ad9e3720440ffa8e54ed474fbf6dbcd55a03d0b6aa7db09536cecb65e30d1ed70349a315e55fbff5058b571ff544fb429bb5517f67398029a9b360d0bf2188c6a39f29fe59e7114e260befed76daaabd020c64bcb409608f893555e1e4a251c4f07e58e232420de750e926e7802ff4ae8ef5068193cc40e65ea0b1f7c732729ef82847160bb68b34710c33360bb4cd9b183447477ef00003b26e3a816dfb189b2b2d23fbd29085bcccbc189bafefeb514d0f8441a6cc70476322cba78a8363639e941e3542134747b105bb5df48adb490ecb2fdd84cd50bceaf5fb6f9b1e87959b8b5b5d23b58c6e868d7cba7761de92ced6961f667750cb37cda8708cdb5d85b2389f45da62b646f4b51e2e60a9864a2ac847c22d4dc0c80d373cbd8a110c67c43fe66f1951598fe3991a82f2c7e89e31e69a0d6e08005a1f45a2546b3a82a47754269816e2f26fe9915a93d7b66bc5a77cc783273f507f9a7968d42bd94c3cdc2401283d07d02495239666da7f7d2ee6feee9e2c043026d7e4ea14396564dda2b3f336b5f8d38192ac17d72f0e0857b8377957bd10d0d461778e8c137ca29b9d43773889ddd1f5aae3149522ced40e9ca972d901372057f449c01b80581dd7e0fc0ce090007338a31a91d54d6bfc30cbf347ae1d65506392d7c6dd69940164d3b2f67eeaec4e431147b4e78b1a58d6103d72298b7770292c799ae7f8214b6c98eeb4b38175b5c583388e349eb982890827e86d4a29f0ecdecf133ad4d0b6d188f75585550f88eddd0ac2fda130a2f607abe1a9cd2a103112836bf781438892bf15381ad372eb13616fa63bdbb3c359e5242d3f65d8400610577d431ccba6155538432b04b926f465babf62915be9a1c5af4125d0dc70aae33dcedd32b36ec217bc69d432cef6a493bd1e178d7659d01af9185f51998072da2e72f0ff42dfe0f626487fa9fbb6e05f719c4d871258cfea5e2914bd1a659a229ea60b4c0e8c01790ebdb11f14ab70a5387ea997f0db982dca735b0d4d00eb84388f64dd9c64eb55a9f5f20e3044581f9720b614592559988d8966214fe01d8e220b31ca1c54167ee16afdc394d6e1ee6551f8b6be2d5e347d317e25dab08759ec3609bca2ddc20545d971a0bfb97507f435c41b39a244a6f632b2711740524313622f21382588c3f9f40bdba1944db419bb67774c7ca42be029257ac7d05f07968976a62b3335c0c99275304ad735422ef2dbe5434e4676baf508f18440ad73ce7226bc16a838c49800565299a6853eff0f2980a320df6e99acacd444f0e2d09368c1d72caf00071efbc522d6b02b4c5e5520530a8ce65307b71fa70f80a3f3b32d61550cafc7d2cdf557cd7c13744cf000c62026e195d4ff5e51f5020004ab404d4bfb77bbc53d7841ea1913b14d231acab3e3cff928ac6ecd7bb86f806652af89a26e3fc2f1cfee6c202e80d0e88deef37682f84fc0af22a3a4e4792022b721d5c66aa3da2af46b6bd3cb4061d528f572593141c1cddabf6ff49f58303ccd06305a8dbded6b4a4bd85dc8b4025104cccb2f12e11785148c7befe081b0288b201672a0d519afb41593626b9dfaee2ec6de7d9a46b6d005d0b7a2760e10bf0c740647c632b01be54fa83e8ba1d3421388f9f70b349404f162f36f794fd0066be601be12fc74f722225b6fd03e0c1f89fc732895fc75064a9c959812d0f0fc804f88fd2c7c2970c20d764e8193efe49906bbdd5cb91e25edef11ccb7cfb0b62dc104f186d1f0a29bf90422cc56958b9ec16df71043e46cfdfc3c6b5eae10e875b43ca286d5f81455b744479047ee80e151e074aa61740b985bd8d3b245106ded64a45960ef3d0a41b5da5919a94016741dd7426c4b8b2b7ffb6c5322349075a9bbe65f2165426bbb3e9cbc673e394ff3a5a3a07dc5388d2281ccc296b6705e9cde31ef38d529dca03f83ef8009f459a0fdbba2440ae07601b8d8dd295d00b7f998eb646fc7d49d6dd94b91008753e3ec7278055129f84701f7ba111462600735b0f5b3e1c8f6de5d16ac4a87a8890ff6e2a58f5232b6a4179eb8d8b10230293fc590fc921b461fb192a9214c89e67107e1ecfece87d087c4be098ab9250014564fa569bf27598df81558052812e1af47c43eb2262ff7fb54ad209a206da0a9de95f74a8d91478f94bf162e4a7b9b8df03056b03c42b98583b8738b8db2b01ed27252520c174bd797ce42d155c99697bcaed2db54b6664f0bdd16f9987620e00f85d51ebdce18c5f85b1e87d100e451379615f81aff42f306dcb5965b30c06009541f3766528c0769479d47e84793c0b622bfd8a2b6cecc9894de4c03dff08553dc7739440b20fac7e20affcecc2aef7236e99c8213b6bc5a8c2222320e7045b3c70b0b014efa41746f292f84bf1d12ce8751851192279b611521f96efb108a53df0911820bab30a750f5eb461aab4578d731018b3e0c4acf156e9d07c820accae70f282555774231c7d1832437b0081e46476d60c37ab7615355107a77b0587bc2b565936b9ff2f2d5b0fd14f02989f86d546cc7c71631cbc3b10945ca5022cd548503a28e95480f1b8b5c0d8eda94cb0f6e0ab83656d799b97c25912e30f8e81718c7809f85acdf7405ffe4089f388459d0314cd9cad57eb788b6431ea04852a974d43bfb37cefbcf1de095bbbc99cf6ea8f962417648691b25d1c51e90c4f6e298b6ed9eab5db1139ed77bc705a0be0dc5aa001d683b30363d93d783b02acc2996c3395cfee5223a9ea81a1bd9b00d91bd89d6a69b3a9099db5d8f4d3097a44406839c03b873033c7bf6e2702048989a3b6d1db1e8be221bd0fe262f003d42ddec66f76faa0e84d6e85ef6e0f7487afce0d617e3a11c2a02e44b3225f07a81fbc417b4d8f01e43bce5dae34532114ceb3b8dc6d847c4b1fc7155d080d04b99a9a00d0c1475e784512118e21db09f283579dc2d41cea61dfa22301c4e40c6f74450e97e5d4df0368b47b80b3b2b38135f0a79a5dcddeb188d725aa78720d3be8c64815ecf41cbe7ac4f10901bf1bb83a64f4d6b84651726d065805ade20a510e97c4576600f047f6b8edd3ff8dc423952d3d735fa0932c6f6d7fc019f508e393a9528541bd1011825d10b4c2f387c488ec132a8c37740923b6617f31640e451e997a89d6742eaaa86b5aa26f86872a05301d088cf91e8511dba913e68009f4288bbc5936b2aee589e94b487956c63b5c18f04824871345326532508f4309cae2a178c991b18396b203b12b40c79ef3c4f428089c7e963c2ed16bfaf0b20cae7ae79fdc4c101ccae0ab9bc3d1443e28203883861156721e82e213e0c4b00730baa4be8a6c91ff9207c95995307759d8cf2ee9741a3b9350d76022d97e2f0d73a45790501139c27178098003cbcf73564b48e380c06a24916279fcdc48a50b66441c92e98c569cb5a5828ee9707e302d7a6769c0049e566ad23a956f52ee08eab1595eed19fff206c4cf16b232f8d3a515c37826d2e1c9d05ecc90ba70d30849314cbf260eaf7ebe14d08e67903551353d8f5f66127c9cfeec8735cc78a400fdab54d1b6e8d06857be2fdf84aa9d295cb2ef8497c4136f553e1043ebcb2d017a747cabf023172c41f25e19aa8fbb4ba5ed0ca541797102e5965cdd73f26d05cf7a55fec15583e87c81e08f8ae360d73b0168403b96198a357297cd0245310137b473c3772258f5ef5114db907a106ef982a5ad31956309e10b2b6d72857c02de51e384ea465986799fc6352dee18437826b97444923c156deb74c962e6ef65a687a2f52eeb67fbee785e976945f9599c32ffb882f3b5561c907cf0cd1e5b091ba5996d1b23e4043d34790b420e97bb157a239d819c4e9dc586d1f3f6df3e0224d83be1cebce0bee0f1172f9f3841ca3b07f4706825493e1fd75605ce3c4f052294293f6f46f7323e1d8652ae1b9d49f05a17008b244bf5c187cfe2c246f40822870d8bb86dc808db7be5876dfafacd649c52813dc4b5455ea0793023210f0feba96ef020315e32852e35155c9971dc74508341edd1fcbfc2b360089e23b60a8ef4927f6b15c662d74481e54e6ae651a5614870ece4299da660476f847afd039e423eb35056389d3c8f0bad185d3743fd09ecb1909e1c6607a3d109d33f1804ad879aced3fdb4fc7757a8f6680c435957abb7e15f786c9dd06a94fd5ed94f02dd5a325af5710d1c1569e848debe819a61002050b026e63251dbe26821b0bb07d58b920b2dab69c6781fdb35c6da4ce66c9cfb425c4f191ec0131bb5bb28a2023ad5d3b231015c2d1aef3ad1c3326e887b455c06fad70399e85d9774c303bb05d361eadbac396bbbaa8dbbdf8280d9175a8af67e4d3cdd45030a5ab1a384770a37567b4c18433d4d4ee91bf46d04e028916931418b234eab8eb91fba2576510091a472783d72569f3891798693abbc5333fd4cb8643d1a327d1fb0150708b0058218684e3620f4c92cde1d12eda701c3e0697e058164ae4bff56cab341cc3206cae190a79ac292e2a126c9987efc98c81c0f54f0bce5ae99936c4cf68a2fb20b995d238a75ee958278db2ea604cc9ba83c6d3919602b09bda8e32cc6a883ba036a05061981b2b60e3d9a1a84bec858a5501a0dd8b28fe41db6dea767b27fe100fc04ed36849350dcc6d4b3d5fcd21f44222bf7ed8549cd9e9397163934e10509ebd1054f9d9d2abb8e34ecb84817a56b480a701c615043edfecb1da7a6cf4305426fc7c548006bae60e3dd98fa2a84940204092ca24e942fc54eb436d200e00e692729a204e658a54de1ea314d88de5e534743e448c45213e1b6d5d84434c4065d3a765d2142355dfd619e0e3c1c52c1dd7baf154e6f1ba8d5a2d33e2be90b078dbb0e0be86c6c80e984b8a763de30df5f3aa2bcef38226a77045885773879043f9a6eb9a60345971e041ba80583c18ea501d3f19936fdc293bc29770ba61a0d722e94caefd7267c89fbf5ffbfeae5b128127c02c75d8023296f7e4c54d4a50a6ec78a826c35a8ce50b09f03c0607320508344be12d602e9c154162bfaec5b0593f84127224699262f3cd0ea2473fcac0975a0e9c8aad845e3f77ff799503006001b5b2eb15c9a44fb585a0fffba2ea8aa4395f9b4e745c502f349a4d95da408e4ce4dbd81c5231e8d7fb88ddc11ce1c33dc19380b6b970c73b00f5ad0e86c0880c7faafb1c40b155a0facbf7090cc8106a3a860ae263f1c37a2edb1724a220160fc8cce23ab7d3532c493f362e84736554b0494d91b940db657ec3ce948020db04ad519c0b7145f589b8d7626603c36bc9bfb5cc149f7451306b0374f7be402e16586aa2d84d62f0297efc38902de0cf4082dec5189041563190a7f59274c0d611f606f4434cb8a6555bd555a233f1ade61c8ea25aa0290b5e826bf7bcb290e1ed40b4a02d77ec9667332cfee1c17322e55a9496fc25c8700b9c055637aba06c8534fca36c955e900aecdb171c90700cd11e0436e71fe2f57ac646d7b58a608c6da703f168103815b4061d6fae09f980e45fe340f7001116b173dfe8e1eb2000cc7ef26a141caaf7271ce4aa5c098b70aee47691b0830585bf4acbfa948250030701ada232aaf60ed0afae70641f33c7ab929bcc0b987a6568ca1bc40ba5a0a2be936ea56df4c4196fe2405aee1523689bbcb33ffe1386ccc95a8476dd6dc065d43a3850379beba7caaee3af971b65d3e05a12e9673f9a57324b0157dec3e0cf6b0a1a23fcb5389a19e5831ad80ff7d866c1b97cd9f2bad4db7446ab58d2a07979199aed1af85d9c5719162d5353b578ee6d9be759d14659d9eea520cd7b205608bb8b831fe81eed15f5cb8a45453b8a9b1261c0e8f28a1980abb4ca0749e852f09da064aee14923596be159c5c37163c4b5e97c6cd05e6625eb74df242cd0cb92b28d9ebd28207ba9608d67a7af3ce887301f60a3502252b9cdf1e41dfad025862c222def7d6ea88336c6dcb00794b2f38b7d2a36d29accfee36129b455c06d1278684663fb11bae2b04bb493a9821c10eff3597335af355cdf33207c6760f9ff5913194bac3803d80ecfe40b0aee05d6985971f0cfec8f277cd2581409a0d3b080a3cda3d0c2675c969b37398940d7f2de6428b1930cdbabec63123e4e702196dcac00ac2e1eac25e7a4265344d6bdb5b468aa22b0a9ddf679345c8efde0950748a5249bb3e7ee3c71949bab648928d212c2a47338beef253c1897a3e2d011e29eeaad6547717df105683e68a69cbc4a3dbefc1038565eaa73d32ae924502a07147ddc4003a374617086d42fc7cfbbc1457efc29a0f6f423cfa2b9341660aadb462b36ddcd94b6c0864816470ad8887eabdc51fb3842f5e1d76272b179002fe2ae31143f2d59f0552b8e627b86765deebbf76344e84c58a6dfb69451d760b6c5b8b96ddd0630586634be2d107b9547e4a07e8146092e470f58c25803bfe0d3f0fe19957d6829051d8bda0cf383c5c0149ecc4eafd70791a36c0dbb0063e0f97db553b196d062d9753c0ff472ff67e8741f1f4615af9a0507d7baba8facf016b1de511026d554733435bbf6c25051ee3b69e165fccd5c910a21987a0144202f07af4f7dc565ce41a4d8076d00a5c925dfe2dd953207a6e852b0ade2d1fa505615c363f69ffdd748fce02a84191ddda9f8c046ac8c497879e961269f472660f02aa9725a217ac9054259c29651eda3d05260ef5a7059bd70490d1a93979a304f22c97fa545e3140794d583c50720db81253b3ed00e80c940ddda78109cb6208c0b986a20bf909b06ebbcc698dd9b80a721de60a06707e1f3a907789897fda0d4719a6da3acd0b1a580efb149dcae0292d4b5ba85a24436128216b79f5fb69072a2ba7c84cc9ae3e75e427a6c7e461afcf744f62f301d2e314fa59d11749aa0e4a57cd56bc1cb1bb88101cc97a2f2ffbe97e8b7d9d41a2fee0380c0d3a629906d43649f337e82e084ed3bd5235dcc3071e26b44a9c83ef540738d832d7db1e0a781b4a2127586b7b40daf00e23101d90d0664d1c71761ac548a3463a82d24c0e757e623f7b5895bceeac475a7ddbab36b5689478fe7de28f5182d8052e80a900b432d5c887a160f038d5a9829ca468024836cfcb0c5c9c6eef4490dcb3ef5a0c false -check_ring_signature 818af8dec37ffbf033ca5032bb63a1c2ad935b21488695955fd4f2bbaea74ecf aff3a960f9cb6ab8f70959be051b7e11c6474a175719121cbe65d7d9037beb59 231 a19b161d0f05da8c083f111087e40d487ed3592c7739c873dceb2efa8a01ecd4 7f79c25597327a7b0545be7284f4b461514b1e1047e260ec41c8fd8cc81361c9 c9b647cdeb3ca54632797f890002300f970b505b66dff0f55d34dd26fed6efb7 efe4a553eebbc66218e82a72677dc15be6a6454c19d432eb3f76c1dfc535f2d3 d60163344be2394cc5766dfbda22c5199615a3c9c7381eadcc3f93aed8f757f0 5d4ed338cbb52f964ec8249529d8cbc5a8d0842a8a03955e7de31e9b0b004d60 7ded60c5860255cd945c598deec6ecdda82be93824bb4a8d90f0594603066978 0e50547fe9b90061465ffcaadfcba935df1f9d256966869ebefd3336f9cc3009 06cfbdd3e6992aff52be7c974b9de998c1652bf36b1d18b7e8866055d7b9a17a c3c3dae8d8fbd6eb3449c4314488c7b3d9e9a8280fb8cd82d1c384cef1397bbb 864c5c8bc3ecc453a6b09790e1aca943e3f9730d70df21d0ddd8323d1ac9fc3c a5bd99216a2d423e1e2e741aeb8570e47c85f525a8550b8b48d235f25604d07b 50cc321da9457d81744b16be757edf3be7416531ee23988d661b6fdd684cb704 85a225770e74d49cddb2442a85a0201497d70cb7d748bf14b1eb9695a524ea86 607f10537afe6aeb6b3aabafa8e1c308fff5186fc9ecadbbe1b47711ca46c5a4 ac13b1a7b4c8cc71233eb6ed5af661e539fcbcd3ba862b7eecf8da6af7db5f1f fdda909dae411573a527f7f4a75dd13d974e1bd08740eb007893969099b0c5e3 8964505c74c9358ceaa2cd323a72400c5b37a6199eac1eb2fb2e732dedb7b091 b76dd1ff7feebb7abe0ad402defd01bcca140ba133d1deecc970e3eccc2cab61 a1618ce6d9fb818dc0fcd1af44ae8bb807d8e0d580f1eecdbf27ab997af2da22 adf430f7e9080c139cdbf8d03b7f86f074228079637dcbc89a42cd4cb7c79d18 7395ec944b93b2a5d4fc515471a248bef0c5d2df023e77d4ff86dea3e135456a 5ccfbbd5d4ffeadc69f9d64231a58bc034c69c812d6563659f0621e7c700753e 12cf573ed3da56e63be4efd4a8b6dadeedbf6855e6fa5ecdd1e2dc1e3080a175 a73f33f76c954ee3e108c83b3ac761df55c05a6a38ddaff571522e8a3f696b08 e348b47a1620eb5285e4b1de082eb09fda4585667259c35630c06510ae7e31ad 629ec730ddd4a00b55e5fdbd374c968eeeeef299e960e9399c4680d21fba0391 88b97643050f0533cd35aec6f1e2de27659840fd0de43fe3fa5c758da8250778 86a0d070d33adb3eb07167d3af82283df1db7c9ee3c952969f7183c307f33d73 82289bda2c7a01927ee941887fd256f646caf14c7c7d682d0617d0a345a07cfe e7fef4939ba6b0e92ed087551d3a49387ed72eaad4584d2dd0489db898491529 dbf4278550d8b32510fe242f3795bf7d2ba336d97f37443e4efb2204f376d781 08f90019b5f3b814aca496a8ca7e7d6a2912e327ec43a8b1ea60f73363e5c80a dbedf10eb9931850e9e74a9973dd46de5275631580947061550f845989feb0b6 8e8f464e85031a6ebbfb9ad215e1055595f27c45b759ac4b82bff8a8da8b2fe8 99fd098b9e500b39aa0118243bbb73c59b577f01840eac76da5081682bd1307e 3f78f60020d2e56fb5595707d7b5f06d266672820c87c2496cd03d971a4ec721 d0868971500f9abcf808a38a0cae68aec44d2f5a77f823217ae1589ec9cfd4d1 201388868119f18c6686e530000e95d0e652bb7f05772ba794edc4e0a9702bdc d140445c6d63873dc6fa0a0443484559e2240a3133de6f8e44bb92d25aa8246f 19cc9787696f3c14af5f3e3b9a1eb8976dda4a375fa558b8370c9aae2e7fe772 ed47d23acbf3378a452dad54caea1ec7636bfc634091254894e15f98d6823a45 bc389a0c7e8c7adf6d1f741060d06318ef78518ca2fc0e6e19c44967d41b5d77 40b7f92b93be10f137f58685b30964003638710b5854015bc1eba5db23e56bfb a7f21a03c9716b2ff1f73e46be4412d465932e76a18ae0a6da1b5ebdaafc3793 16de1cac521b481345a93e4be14e4d65a1f4ae697f2549b9cab15158b63680be 0e58ac7d4f11498ffaf516e5deb5da16c67cb0e0902f182d0b49bc359258ff06 349bb01b3ef0f38df0d90a2af9d077a991d30ccc131f73818178383445d17ec0 01b45315a2e9c2d81368a2392f6771d91b8be0999609d64d4b36eafd52b9da2c 6fa3707ea0da66811960122db1108baa655f1982e0f01c9b9d6d4718a06f76ce 0dc50a69d9d593ff0f0782e0ba7291ab65ea5612f098a3a72195eefd1711165a 21ce28f26e027d418843c67ea892feee83e76ec1c24be9c1d22886f0e48a77e1 4683144bf224c07c8aa3812b7cccc0ba08f559cfbd26d3487ff7e1e72d87c861 95cde94ba5eb31c1d230e92d6f296f338e745cc4833fb7ff2d6b360ce382ff91 50ec408aa6581325b40c5ce3834ac3c4cda7b562820f97ee703ba205a23d5fc5 5db9d50bb56998b85a3cd4f776019aadccf5d89ac06fccbcfbd82d44869ff7df e30b5b32507c79d72a750de45e4e035ecc58abcdef0419bfa36f85b7c730cfad b79aec5b13267b074692c8948e737b979006e9c54034bd7e1d345e15dd875e3f cb5f3646cfc94de51463f951482a3ef3fd5b857f166d49cc7e930b32a1ce11b2 45a70492404066e9023e831229b334466b71826fe43497ab6b93ce7f79d5692b 7570abb82ae09e285754d1e4fb45d2236e1b3d4847e66987823ac0924c013546 669e1a0fdac417a01db5f9ae9f89a10b921464c1b3cb587cc8ead8eb566d1d67 87947f3082401e3667adbff2fa1022c8eca6aa8fe9c38459c42a9723a27816d0 c425e6c5de6edf4d6561795b1297b8e20e3fad26a0b812582f1d457440d9d196 450ad0832e11df9b7c1a846ecaaad6f3f3590cd83c2116bc2c9a67a8303ca1c9 e9b862c8722be2dd34b385a86f96909909d8ef5782ee7f597938152daefd7442 46628b6ec6184dc5b95c1bed1383b3cc81f696f9395c5f15afb4f9474d34bf8f 652a73c2519cf5c1cd5fc08e3fd0d58c599fc2839b5f85a2d111736c27a14d13 1e2a9dd26a93e03573fba21a780746226e81a15a895a46d0fca109a413701aa8 98f9ec64e9f48fbe24d7331b7889436c838c89ef0fba1e9ebec4bfecf76a9c6b 4de82e3b4d6b9569ea1d7a7451a88c33c115dc8c19e37b279ab433eef9edaf8c d9e45f02811caf631e842c546fc479e20aa56eb608c84790fa551908c1549a47 e818846748f347f8e7dfe3f25af580bf57a72bb37f6de975a137c3218026d5b4 c197ac12d5726e6e8a7c53d33d52a15cc54e8e48f4bff504486a20330946e74a faab7a9f81680fba4ce14c49a532042188e4ba8cda3bb73de357f4c6faa6458b 40662f3fea716ec7af85f95352527c6cf419e83aa4c25ecd7df8db2b104acfe5 d2482477c9307cbede2bf845eeeba9b39ad45ca9653fe840bb9c36f839a38073 147b1889771a975d6833c3bc391561a4297e04a4c319d90e24a93b65e06c3c68 b9d07c49c1becaa3f41bc351a125cd9853659d684b33318dbbe3a8d1b082e9b6 d843dab7d0816ae53b6fa84bd001fc78a50b8b753ade522d9bf51172da494cf3 fe735a81f3a09c51cbfec726a68fd34ab8d3498f0b4bed94a293cdae1d9f7374 6dc97f386ed6f1ed6dc50a60a89e51703fd26ba921d2765b625c286eb4383465 07f72e37af3aff67a1c7c1aefa4693468be753faeca1c8f9e2a69c3b3b67a7d8 728ee8e9889406e5b023c22b7ec0334078709b3999ade3dc126d60e87af82a26 df5e3bfcd53de215d5b3231d067a01814f0a0368ebf77819f84cd8567257fe62 cba092e1706407a6600bb0e78fc955a18ce26021bde790b6cb551dafe9253e2c 3528aaaed5ec99cf00c8bd8df9f0575d05f78820bee11da63be7982af3990dbe fb22ee89ef3f00209ba3d261ef756c7a383e494782c812d2529509e43092c65f 9d990166cea66ad52ce9f2c89ef53fd7f7b160c337c63411733c9d629ec46f2b a847d86266e95aa562d076439134791058aaf5ede749b32460249516585d3b0c 79132e4cfb98846a34c6cf3f39e3bdcca69fcda272cbb74ba0b746410367c5e1 7f27f9f16d5b2c3af85df59c0c20354ccc3fcee978cb94c8d96f0ebc88c3ce7f b6bc263c88764c2c43e511709dabc8e5e6f316c391919bb9bfaa9483dc62ae91 6288a7e6dfdfb97e5fb323c1b107945823aaf1b47424335bda79f3d89ebf11f5 5b8743f4fb13107cb090c9d642bcb041d3b0b421934a9afb30bcd896a32e7e93 e7edd3b1e9be4920ae3fd9236505aa85c3de95a36ea25a12dfdc311c565902b2 8cad9c3d5b7a14705d2b266dbfa22af1205d8cae0defbbc7741fa6e85ca1574f 3fb4125765edc2366948b47b3be000432493d8bca535c620ce54b07a1b456c1e 6d57d80077d0a80a08013d43c4a0bb7cf5411cfa35092518b50050947c8d4ebb 94a766689789ecaf11d9d83aab0ba8b6cdb1148bfe558937f00f50dcbb316184 6d1f52828badaa45386820f1cbe928061bcf556b2232132ad4b82101e9b5bc43 46a9bf98c6b043cfd712ce486f3524def622e03cde7b0d4a9ec41012be10920e 3c6e307b4bfbe7baf82db3e83ac3c4e2a83c800ee619c3dfafacf880a14f6286 36542e461ea20d712458727bbd8ea9aee3f971f82d54d91562f8c4d6316fd5d4 ba5dd64b8665b6f063589c07c8e17c7a5a7b459f786b9c765b66c96517f34022 6c4f56c0d4ca4051220899333f9f6bc995f5a684e02be7ac6172d2b5265263b7 a9ccfae851530ff4266d7d443dc91e39fb17cf4fa4b20ab4130d5a3ffc65cbf3 86200041bd1644dc0a93b19bd4790a58158f7a65f8fb462da0d265452191431f f793cf05a413cde43c6273c39518994718dd6c395352d0805b52f81d3b4527e2 112f3ae81a2d9ca7a151fe12f458283caaa79de83ad4a8674e045580b02b752c 3d4252613028032bd51ff5e7869c1e1d459deff6ba841635fc09eefa9e2364b9 eafe4c5c0f16504723344a76ecbcd72e988385b340c3b67948dd09e67fcf7504 6294e7b7860e05a779db87247773d132425a4981ae09b139c787ce58f39073c0 dff8ad717eb8156a7909c29b662c95d979fe19b028dab8332f989b9f1d405438 f55199aeaebead8abf70b817224d28e463358e5ba91a0008c9e509f0f1b0dd47 736af03e41a6b911f6f813cdc4fc26ee992390e961d3744a712497f9ccaeef3f 2a4627f23ac4bebbb8f01f2d68541d0541fa13d4b385719dd0a2a7c545e7da8a 6f9ddcc56c1b7325f083c56a5054c0c0c38f6b6839575ece7cafd66643fcb58b 361c4b575fc323212f8df77e0de63e6dd272c9442c8614cef3768baf6db7f040 af7b121e04acbaed3363b237002725eac77cdcd635e8bfb31862b3e8c897c0eb 078069303cbb61b9c92ee422255765f79dcbdf5ad5e6e4484fe6bfea57c2dc76 5d2c0afb171b76f9f93f60bd2403a406facb3542ccd3ed1987f269918855c4ba 1d18855490d10ec582a4a08e24ae5f75024410259f93949266ed9e14f1a77fbf 37e07f7f9ce95405918c4bd599c3a0ad0d0aa6aaafe979e63045bb96940431b2 8b173b6e806f43590a15ceb537160725cf2a929777504da58e8caaafeee75de4 c6b1086ad92ce2f6d37d0255ae57915389b2b7ed73928b855ba127bb98f29b56 2af3c2024f664f268242175db52d64684827a27b0bf3f5d9793c2929fe8199f4 ae657f459819d64eb516d136eb198cc810a02c8da1ddb04c14af7d703da40683 df5a0f4eb58adc10aaf6cc2bfca2327ba654d9015087e2cf892d1f72e2e6b54b b6dacb0b93362a49e63be1faa9370279a249d1ad3b3af2a719773ecd583749b8 9196f4703d629fdfa47c0a32bd61b58a87e6f3df5c9103396cb36b913a8a504d c2bf6da9288e45ac7a80f9c1054772b0996140c1b8ff44fc737545c977948fd9 aa4eccfac43c1cb85a40f050ed09f66a6b9b741232dbbeee726a81bfb5b385eb c42f212f51c95cab7732310e79de189b601f8cbc0714aaf412de0abf0351ea44 97412b75a6e731afbd5314531914d8945222d62c426fa3fc9ba8eb0a5c32c29e 7482633d27a2b9a9f55986730b814404fb3a2ba4e8a82a43eab3d84726a33af5 361645052d8dccb379eea95c5b2e58dbac30fc1fa7493ee2e9ae2b9083c04361 9db9d9dc17f977bfd942a3c4e190162780778fbffb8426565f25d2bdb6ce339c 643e8e8981e9921cb1147d4ee9199de42e2744cec16b8acfb1121d73754f67e1 89d8946c6ab51a1912a075c9fb0d30c70c4d5514b28400fe9a0d432f583e44bd 11cb3dac28da0371b76937d322ff5db2607d719c385953e68156f615b8c7b178 fbac9ee7d58838d660c33c4cb9fc153a24316157e23387dc2857aafb99dcbfe2 139f358b80242d940fb88d8fedd324e7ec84cd4786cb560c704cc2e577c1f1a1 fdcd65d762099974ce1247156627e9be8c6eefeef4e2115eb74996beb58239ad 19d300fdc7220f571736908ea6d6816a56997f88d9e79bfc7eaa4b94107d56d1 f3690241c8737c09f83cf77050fd1518c30030d2d070c5bea6b0f5b0d9c225ae b8090b2bce8dcae7e79ff05fec5f3ca1ef0c558e156d9396d99a88379bb26d3c a90fe55b478943970baf5a46d93323cfb0f9617fd7df55724a4cfa0dbe188724 ff3fb8c9070a9380599c1f149e2b94cfaab747e52ae4a4d07f51ac8e43fa2f3d ee44460df1d40ac16eb25bf2c5b91ec49199ceb7e1d7b9da8c7e945fc98a55bd 2b06fba082668d623922ba7f49f3780cfab30de2c439b363a53198207a76a477 c13566b8f89fa4b4a40b8dc58142cc74d22ad399c9c6b4c29c087518ba09d180 d2aaa083639bf656fdcd96dc834c80f3b30163c9abfbeac01fd4d91cd9637326 c10885dcafbf53c746eb31129787436d4a44807247d951ffebde5bd66f5a4e71 fe7626a260f03d5c255499abe39b377b8bc712d9a8c22a16ddd52bbc407a6cb9 5ca819318727d2329303d4d0582f174b5850d400b18216579899112ecc5cd90b 22cf3ce7314e14d59886b09d9a198a6ba286da5284474260bad6ef5b3603a4ce f26bacbf3eff8765c2b2d478dea0f37127fc7c38e7183f9fa60eda4dad954de9 5592d9f564a0a398e4c781d66781558f1ae8e5a99513a380c208a4a96e408428 1f66d98d67ad5271467675949ddee86518a2bb44465f09db3c18b5ceb83dde11 ed83f2a222563eb8133b57e0a40632d7feb8e9f75298625ee3cde346d5790628 d368aae656edbfa3b0f60cf62caea14846e7145585178acdac566d714921844f eca00d7488c35779b0b3842439598a6c0378577e0669182cf548b3c24d5dd900 aee7744d280c7016cbfc545925d707f7437340fb46f6fcb818421efb5ca54c03 ca7c77831ce42c2e9f934d8aa4a7f229e2d026c3bed3ec1e5a408fdfa6811639 8f6e6ef043e2a295a47fa898c67c5606f47685fff108606082606a58567206de e86fffbc0aa461a3aec5729c7abbe1dd8dfec9dfa873f87c9438915ff0d46436 d4cde197b930ef3b90817bfd8d3d74cfbed536be6df26884490d5b55a31df875 8b3da1981b21f58ae74ca68faaa4df9df0efef78cafa2c6aa2a2417010a29cd3 84acba6fb1fcc70a24ca3ca03f42e188d542a14003d6c0df640e582705dbb857 1687864a5025f40c11d072806f81254a38ba757bf245eb74f8d9dd1fe362061f e23dedfc5ae8683328577c286d7682e9052f65b79beef3b384abbb6a2feeccbe 8763cd2a179778acbdc629393f07930609f79256b41e980c2e2d010446554d99 399e455761ca85ad6b378cbc3f21219da25002ce94793c4f164eaca255d7198e 72b67a025b22ab3f7e82f0228069c5a7583e1f63ffb2c8323249482d2dcddd07 36497f2bbc07add59013f65e5053701e7caf130433653a7607b3f6599d6e84a7 3cdc5bceaae7243efd761f0fc417888f1b141111564807908ea897117be22cbb c622f1c5e5d8740d4f6c7fc94816cd98e6653e9fe322e7810243e309b2ea13d9 9e2d0334e13d1343abc8261aa3528b6521910152ea70cc6686a12c55b11dc4c6 dd449d46bbc9d2d05467a768dcefd1a44d3a2f2d60feb10a58124a9ef83814f7 32cd8ff28e06a6b78a3e4397731b9936962bdc869b38d2df09b0f7ce0c55a956 a46fd79d5a573ff6ce20c699d8acc8c452f65f6a34552c96c916c7d32bad76da fa17e81a5824558222afd6a8fdead0b84b970cb3370a246f023ca1069f46dc6b e44da975c1afbdc35daf9bd912b3064be8a7061f58218244141661582e510be0 77621a08e9b3d4cf95b81037d9ac0116c6ba97545d5d5dcf1f6991e4e4a6303b 49ac6a84ba22d0d36b05fe2a84cb42cf14a4750c246bdffa1bd6a12bed7bae69 c1b97a0a64d034bca4a052b1484a950bacbef316966d3209d2e8f1878f174c0f b2da0ef2c20dd11c266a496baa1ced40e39d51670c122e7bba13f214543220de c67ff49e056f7cab7a71d5e9efa014cb400fe2a7fa9302896949ff987e88fbd0 6800a836294b6e5819d2ab0ac88aa71e2efc8706f3c97595e3670e947a0dfd2c eb20ea4fa0744e9d52dfc44a3de362f474b65e9a97f671add66628814d8b1bc3 9f180ff2994f30e315ec7d1d87e6c8c3e242dc8f5fd88ec5851079498b83ebc0 3bc1a2029983c554e6ef133e3a221ca6bac5993acbcec5763ae38d86fd65c615 003c1f659ca049b85814e6a49e8c8ec8c5d6d2f0f39123e5c69aa8de9f18c543 9ec4be595cf0db8d8355604b14507054624eac921b95db0cd6e337effb0320a3 ca3a7389806ac815044bbd93ca91958cccbbc966a129b87b0b19c76ec6920cf5 a2fb3d5e0fce299bd0d17fd7307ff192ccfc565cb06438b5cf04e23710430894 fda81fc0e8a8a7e2a0c503f155e21239f6a963c987c38157e51388a6d09ca0dd 4c7eaff2370dc39bd856766fd314e8604411435b64c2090002ec5a412768b59e d4d5b169ed37a59012d077522c6f32a2916c57f77679119421050e0809df7a24 029fb040dacf93a705fd9bf3ff05cc960349b02e402f3400aa782a5f9ad81bc1 724a3de3427160ad16611b0dd6bf2197ec45ce4b0d16b2f9038a8b56ea024cc3 3bd46ce89b4e816348451a94ab9f867f30174c31efceb89631c1cfab45476812 0589b6aab992e0af67ac53b8f24f33c89f9af11dd97d8362cf9612da6bc77a54 5884fe36f8c38f1921046e099a7f1b9866c5ff2fd1ac861826f21fb436bc1181 5bc678ec8817b5ad4d83ee9357647565413458283f5e11837e65b03118df7e89 45b23663b601ce5399ae2cefc8269ae85da3eabc86ce6db2b1f2edb475bddbab 1a181164d49282091a94049f8cd46c1d859b8ac13face7acca2714182b9de66a 23fc47297853ad2ecd1ce2d044804d326f4c4461d57c51b04020164b8f69f074 c928abee1d4fe14f7b7a642582aa7328387beff2e6eaf62f6cc6543c67ed0eb4 fa28ffe6ec9b21eef27347be24f557ce9412bb1b86238f747d54776868c1373d 1b181f519d36b6b88b22c5de74a2db453cd476b5ff73f3d453c26b3cd824f50c be33b63a03c1e0dc098fcaa9f986f1c526d9435a5a8ddf2d99a89a6c2baa2ae4 8f4d6871a4471375b017591677f358070dedd8c6773b5fd5b8a462dee14f32b8 533a0cb7d1c73c042a42129fb44709e337582d30b8c351b570b7484478c20d58 0bc5dd0658246cc4b20b3845da2aa4a00dc1e1fb1c3351f5d86be63dd84b9d41 4165622cbe11257ae5652c0e40b477401eacea233eab9b41bcc5000b740fed45 55c6048ff65f45944f8585a304e43ed0344d07baaf6d4e0a6a41da2218e0bf45 1ea21dc9eb94a35b84703f0231df0588e001a10c054d32365a6be44ff7707f79 5f68523716f44e16bb7f2c1f25447a81c83d5420ae75793b8dd89d5039a231d4 cf43394a14378c275aae9d325ccfdc4e672eb8219ca62f09a595f24336ba481b 5b86b884754e55419aa3acf35660b1945b3495799fba131125f79aad0338fcd0 41ef305fb3d6e330dc888a901843110e99418f18bf91179e5442ffc3c602a08e ae7e5bcea37ecd12c914e806f323cfb80ee982d24f0b70e9393e9f4087a93368 4766c139636368b66970c988a87e48357b3f620646852d68c30ef6647f8ce628 c20f72cb8f563f0f61da312fecfc3a7e969c93ae522104191f6855391caafdb7 4a373809f432a55434b71ad52a80a09812278dcc712c6ebfd28e770749b55600 ff96e01afed08f9e0d3ff01ee6ba291d3fbee3898290a3ec45a5d1b93bfa7d9d 55ebfd1fbbfa6bcec2603d133fe59c18131d745c12a7145cbe3e31fb3783de05 850a4a31fc60803199e3487d762cc2414b1654ffe84d86662d4be4bdd6df8987 6e6356a791d546bafbda86ca061644ef20e579c1b704f44a55f42cc59887d9c5 4e5fe68150ddbdc96607de8998d7dc7ec5c3821c4c03645cde37696cf541bc05aa956f6fed9ec291460961b517a9bfaedc8168a5988579557427ab5e07cfc801962f71340e4b52c7e7b6616b93da4e0dd6292ef6e84541b39a195deec007ac08a00f18e733cf2a13317c8263d65cd7a60bc5a3ebf35c7a845c9d6327f38b400208f746f217539c4abd6bf3730d11cb9b1f0815a6869b7feff2676cd81541290f6b90f15cae7eacc5ca7cd15df10fab0025509a167b27bf92119aa8c4ca932b0947c15e2221501bdfa95773ca41b44cb3130e2d838ca8cfe85f08f0f19897500930415c6a1500e94bd2d8484a47091f91d7a298d28178ccca3deab0f066f822047bb2503f495524dd851a3a66fc5982fc347df3f83655fc69161f1bfb56dc0908b052020f22aa88a6d6737cf8235ca4ff3bfa6241f8f9e91ff977db1a9aab6c00264bd661c533de3557eba1bf6f39735a4ab8ff1bb677f38a99737a7e97b418056cb87ccc32d7914b6411109ac8b03e1a7d989775db85aa3b89ca622cf10e740a5092e706d7e3eb3cae0ace6c67d3080e6dfe5c34b522cd29e8e78a6769b3240e7bd04f1e4ded4c28f25eb98705493140f50795d2fd9f4ca12f9f097f07ccd00edb02fcfd93d766be10d7e30661e2830e61d6988ffc268a097d4434cb3616d007d92881e51017728e28e76e61c950b0298101ae27f0ba3fd4353c952ea742ad0a0eb883c7cd93cbc77412120fd7c0d0f54fffd8c7720e8eb1af595969aaf952053b49bbbc89630468bbe35ce0d920b138457521b3c06a4b93c3dd4c317465680e4bddc9063374d4e732361c4365e307e0712b22f214279726fb785232237c53053594143f55647f7bf76de8ac040c301ddc7951ecf8fa3b1c4790a16375fbf4018e9beeec9bedd23e1e82a98f5397b25d70902d5f403a7e09259b30591dba4e0663529e0a4ebfa75a118af3a91ce6ab23c201887407317359edab9170b0c3850bc07166ec79f8b629de9cd25f4346e8c32970c9d26594fb2857c35cdb1eda150d49958dbf476cc386845e12c48048aaf2572b6a148e2fc8473c1b228ddf0ee900afbfeaa50a9f86b9bc7a220d766db51b097e81eddb4bc67f7f4fbdf5e5b294096bb6566e9031653433baa8554d71c119003a639db0d461c97b25daf46d6c640578854fccf4e6c5b3604178d47ce6f690afe0d5436edc6d68b4a38409192673040168d1518e1630ccab45f95707f33862034813f3189613271203d1f2337f830e8981a212aefad3624410c2ef6658dd714dee7dd51f266bee849f0e8b815c860d3f6da2d8e7e2f482713f250fa2ab15469a913c53a0985a208130a7de23337d0347f6c9be57738c64d9dba9f9f6b38322030084b87cdc82544def42cb158dc903bf6196ed0db4956a742ee612523e1bf973538d5db9a3c6b712302b9476421f02b46e4ddaed1be828acab5eebb2058c97d561c04a97edcbd20004b875b75fb601fff0950e4b9d79716285664e383369deda737a5e7909683ba0cca473354d46043eca39489233e117e6b8a959b63a7f64bd57be8ff540437accc5be859aabab0b36d225657b8d19276f405114712951d16a88f6fc3a7699b14791dd3bd6ac1c0de7eabf5bc007301dd2966bc30b91ae9e05e693445223d933f71bd46bebb336049016863a1b370a489c35da6bad1addb0678861d15685935acea46469f12e770b09555137bf339121a3c5597b68d31628643015bde8ac227129874067e221130544518ff06b4aa2a2ff8a14ef34f587b8bac93d95dee7abbe0f10321777933f0e98c37a5d7d5701377a3040939d20fbe7e89a2ba8beaa5d7ab40246193a6c0101c7ccaca102bb93af77740510728bc215135ee723a02e5a8c3befd4628bbc4f00173ace0d13d1a5a99e0224e1d5eef1108f9acb0915594d596e1e7a7c9d4fb6025e25e158ed01b172c9826cf1811cd4a01396107dab10c81fb986c7289cea800da3639370b8c85b1e48d54f4dff100398cd4bd5210e9adc7935c722c04846eb01dda6ed627c573053e959d5bc6025f2edf851378b4e61e21f75b957d1597751022b6c065ae30c419ba15a5d3f773b06a444389fb7a3703f378ddacde5d2356f08ba836a48a57304e645a7bcf2273c98e9df0edb47dec5238a3404b92ab9446b0f0f2ab5100e87595db19b25820947afee3470ac8ef0fdc6c9921de1b56f9ce609ab636dc27820a8d52b2bc14fe97a5b58b35ec03bafe33e322d16e941b2a3600edb75572932552df4a051a794bd4beeae028bd2912c8ddf6a7918458ba047f2067356761cd7606fb7fadfa6b500ddd125fac345665ddae9890ab94ab16630d8070b62df15b0d08f006de357b497599b55f9b0fc96b41fea2d15f1b924aa469808d0f98a32fc61922c0d3d36b4ea082916ef206134d14101020c7f8f3cdd70620dfbd55ac27cfa0c52d698dcce5d7b3da460134b681e935442449e5118ffb5400bfaef8e9f35bfa0ced8490881a4ec2d6fa095be5dadcff21749b59423ab65b203f5383972edeaa76e883a25595311efb72ce43d2bedc14ee3ad0c23c27a18770b1e0596a10b286010c217ce2d71c412495e8ff6af6cb184b26cbd7f772bfec30635b0f42a2c8a049399e61f92859b3efea8fab5a214acfb060f4a8a41c962010db4a7390b49fe71cd9f65190af716f277fbad9215a435fff268bb243c28eafb084fdb928ac49882606473d7089d5db5f7d4a1e8a5084ac5882a56db67374e0a062b9044bce900595feba4ab23ff38b8aaa1e0fc60c63ce1b04502bad4f0d0950ee7faaed24f99c18e7cbeaa9f1c41114bc53c8052b3e81919ba655814e0b7230c75368049f17cb4f605bb3237039bae358192740c506c6d9a500a96b8942599018b3119f429c32ded5a1e5e3a4a0ee8795b52a8200e494dc5d85fb2bade45760c261b5040d16bb207187a3d8f332618a973b51278870c61514a431efa7dfa790c15cadf344fa87e71502d8f78b0f9c4558e822b221e1770c37e36194cc3499605afe7574d31d2f81396f824cbc97615de09b84241dc48f2f88fe6954c4f1daf0b9bbf837d85af661fd6be9a303409eabc3c54643de3212899a23872ea530cd30f9b11b94949ba9e22db29e026b62d388ebfb3f014c51c303155bcc0ae46de1901148d1152872cbd9c6adc0f733352880663de7fc4b7872b439686f95e9ae26102162fcf2bbe256f654354909bd975677a9c71ee7b22f6a87243d0300923ce63023b82703f4472448e469664d00429a34add9de10126625941caf5e2eb33e7b204fd7bd2f3debf300a280abf898ed0aa1c7ff0645ae14184d34921fbafc310a3079dc90a9bf43ea0c723749b45879da14722dc22272d1180e114fca52985db180f0844f9576968667487509818aca89c57ac0198430d0ba91b337fb217c512d30b59620da11700f33b9e5fcd8b2b5b8cd386cdf8b7797184fc224c00e52705c90d95c4debed87b1b924c350bbbb4fe91d2091136b1de9dff835575e5a34286cb083fd6bfdd0838f569e39e556fe356403c72eac13c0ff8e7fff3b9b59d3f46700bd1a878bde3ee9d9325cc1472faa8d65dc7e018e20416c71de8421278cb851a04cd174b4201869b6fe7892e292c5496b466a074cf29fb459e5e2a40839b75e00a95722efe2149df85de7d52ba73eb0f0b8a18bc141318f9367d98ea7ff4960f048e013ef57ef0133a60950e33875f4bd9a5a68e58d9fdf78cf5397faadf621409200a3c29fa4a9524f0eb93f10fc535e714e11e3ff51a1b99f8a6ddcebecebb028c460f5e74d1bdf1412ba99ed1acf1198b4b51de68f82d379a1b609b5da44f070ae8a111ecc295f9d8f8453658a537711c6f0ebc8e2865c2b7b36b10ccbfe703d305dd67c0a90c9f169de55df69b239c9fbbfff57f2c6d7a618532cbda6f4e056de94c6001a5c1df599059096529690f84498d4bc3d6745372dfabdd721f6f09a522f7a4dc58e5aee753de78a95745aaf424e8534e778364b92f0c21bef28f054d69d7ffb9968bcd34fbc530ba18b23ecb99d677fb7a34e2a848ba5814ef0807779df929a55287e80c0cad157cfa818e5ae32c76affcd05fe665fe087837cb0daab184dab97e2474b2c3a2a6dd8173cf2ba4110cd59126fe752042f4e7158d049315c5ad4a019d43d4ca80e738eafd88416e782923ad06783d3e870ea24e9a0cb708d067bedecea4e9a03d43047f62c7cf105389aa1bcfcd9c8941c2bf314305efebd767d3fb822b70f32deba7d7752743003f5cb11af2df50e495e5a89f58065e36821a9390a9a02919bd71fb0f2f2868817a9d1e415dd1aaac5bc39d2fd30e59ffd3f09c0336e53b5149d962bfbda44451214e5ae109354b48099d543a6d0fffe77d072c0f50f0a79f4803d3c35b8b29ae0f77dcad986d6432e9214605dd079aa5213a561602609fbdec8c465f3b0694e6fb97b25f6aa9fd85b1cac6f8fe08a87141c35ed3e9517c617c49eff6c1f13ee47d01040c8ad477c3f222ea83550dab5b92edb3ee83e6b1ccb7addd0ab69089cd03526359ad0559a3ddd5c5b52e0114b52da91d0353c8ec0445e1f2d4b3fa45056d675a935a3f01470b9df811e506a97b113e60962de9535b8235cdbf829d62143dac59d679b6a8836428829cd50f8feaa2ce14eb04e6b6334972f36ab9609bacd0e40a90e09418a813ac586f1e04dbb9a4f553043465d21a4eb69957985cafbce9f48f6623767920fa2094a99000fae9a89c050e9d16a5e871e6ed313a98e74b110c06f3682e9672fcb6626b0e04a7dbda97f5bc415be76cfec789961eace90f251dbc31c404fa85ed70939f790299969d2d0c4c10df34bcad928ac98953a25f9784a7256c6fe6007796575d25066324269121d025e1a88b5c96c4873424140d7acfbf81e4051ee07422fd43a602585bc657afea3d2f5ff7630ed62336a4ceb65d64b351d7e84df8aca562f7240c77c87a9e8def547c7344e286043473f2aa5b7271dd879f04c64ce59bc30a920ab58314d83944f27040ec1362b69e66a480c73a119728c87d7dee55d513c09300d31779ea892add03694d16a2fe01b530a254f8406eef365a7843be9e0cd28105a2d2e2aa4052bd269fde0fa18247cef6fe17369738e30e34d829664467843f00ef48997680c83985b528b542eee25e2ffe3f23f0812539300b178e22c264bf0cf6e48db3f6492b6a0f1b0cbf57ff2ee5288d1d95f7fe2e9b50f1b2889f70ff0056fdc9f2be3ddb8baa032ea56ef5af462aeeece4c221467aba6556a367642f01912b9176f27cb399db49c5e06f9fb53aca8b4b1d1f0e6c379a17b63ef3f38103fc5807054348af94e9df3f2acf23e230dcd931914c85a05db3a93cca8aa1ff0908aced7c32f7a3e413e47e7f9067f8728c055f34951583552420ef4f4fa5ec0110eaeac86fcc1c34d58b3eda1fe98a4860f7099da216a31dd61b3e7bab047d07414bd50041caba88fa3ca10526816a8afc0772447cf418ef579406d9c78896089768b09351b2cb6594acdb1d2d85ee8d4a99e1f36edbd81cd08e472c573a4d0005bd45f32c2dd5fb789d0611f9364cee741bd6d96b3539c80edb3c0dcc10090adf1ea90a7a011098b009913f2c110ea93dbc3602aed0e422cf9ccee527fb41059cc3dd5fca54c8f6cee8fc4a1eddf5663ecdcc21083d4d26469eaeea2e57240112bd154d9d6a2463036d8a64f5e9cf39df136758e6b6fc39de03434be7b8ca04d03c39e1ed0aa5dcc99d8c4e1771f3295e828d44f6b916e78b939aaccc11b00ffe833f116ad9c2bf6e19cf929d177085c41c6fd6ceeae392df2d41fe52581e0a3629f72cc5afebf9a530c2144996b388473270de9229ed55e0ea1232c644db0bd1792b41d355a2948232ac2326eb7f06ee87d9b9386ef9298f950bb03be6390e4e4573e5e248ea8a59738cc37ce5f25c6bf5113ce72ec6ea8c58d918a6b46b0c682c065f61f4073fd7046c4e318f71eef2d844210b2ff5e9f6a0e7dc7d6c0407c3e3b2eaf4d307a53e26da1e30e94a54abe33f9b5d7e428c7504941cf6dea70943c70bc04769cbac7f7c01f6be354f98ead6e799ff9868c5faeef5c3ea5ddc0ebdc23122fc3b057bb903540016ccd90ae6cb7282f84048b224e0b09c16797009e54088de8b7bea7b1e2bfe7cdcb96d571a3576a37314b15a90cdcb5e3c1b09079072b20e8f4f20641d88874dcca4f1369c0041e3ee35fcbd5174b6988a670e0901f8c68ebc8c58b7b99794e187545c33445385e8c1e61f14641070604a8c620480d8c7057ac4b99f10d930457a010c100d872cefa31456e9433b17846a80120d940a83a8eaede78c95e1019664e5cf8df9a89b56b8a0a2eb69d612647abc60032ca487f8c7825647a901ee416bcc3c9ae2f1bd70bb501e609d86de3d7772bd01e3f3827e53998d47a40156661352411a9ab689b980524b8cf88651be680b3a017c59eec729a7158f4db1d01269dec822a7005f863f50d1d7a3528e0238f4ce009e2f7bfbf2cfe38bcfae16aea1951d3e94cc3657301d7144a48eb042cd85da07825a23e55f242df1989f2df9fc6412f91c097aef134b704cfc416c1b6dbe8702eedd0ffdd163e3f8193a140cb76a0c5f294a4dd85cca27ba1f3ad21458e45b0abae4fcea46162f5fc135c1570d0a50cebf6229117e5835a9888ec7a807fc130bd15eee4ef0e54d00c20ce88f88784add3e2596725309c8a6476b668f43a8c9053eab9a31445c0f0690ccf5164e560e567c4f902a59e82056036a38dc3cebd00671edbc1fe855e3bb02a0250f20e321443df62b2ca635bcf5391b3db6346b8c075dfa84e5605f826535d24be2ebff0a4c22dbcc69342b1fe65df8cb88b45dab0bb52b202edaf6de82201e2bcf2d23790c703bfbb0d16842d4ff5e63b9ca4f210a1957db7c85ffb2a459d9dfdc3f35ea69676a612a6271bba3bb746bef2c5a3609295757a4f4271ff32ba8c803a7b30d9673b67395b26f140ac810f99bc72987079386d10fee734abd497a29f9f6c72bf98c018cb91872f0e4b4a241f76c860c0e5442a5818e0882986e86d2ea700629394a8faa1bf01b185e3a0afc08c1182a024f5b8e27249516889cd3e34fe8d64c6833cafac8e6de357f3bc7fd82e8a02806a664287eb5f0f9c2d4dd701bc15d321eff9de6e8680ec885d5dfbbf82db9e10aadb28a1ed20f2a239af5ca37e8ec53587336e01f1ae28b994eadc624f0c492023aa4e72491280a99b63f3bc1c00a5a96730aec836423c33829b938a3e4c8d80c7f98e36f06fb1a47d5afb89963512a92b5e9258d71d1f03077e2a5beaa3fd20a6c2d9bc81649ccb0496c3ebeb8858e1c4c16c87568eebf5ac8e5f4c43cc4ea05a23728a53157553a6c7e28a1bae63e63623c00ea932be02e45b36a9631f0b302ff668ed14ad590463dda39ab059bd701ff5a1fc441c4f158d2fe70140e7aa405402d782d59969eda45e42f532bcb6ad3f02912ca72a3e2cea28dbfa63e206b0ae3efd4a62eb82c4285a5cd3598aacb14e65c1ce28ee2ef4037e29063f163300c8cb81d42c890fe9479ed1781c4b08157dd8e446966c948e48fe9a90421589a0090d05adfb2b150e34c5fad19e19c7d2a01d4cfc1f1025662bef647548561520ba3e483db1fa4abbda7a7a29e5c19333ddf48147ea7c84ce3da4209d462608a03d53250494e7a15582d5997512cc877ce82420b58501998a1b45c8ed019ecf7004924ad5fbed73a4374acc96a108125725722697073279e56fa1b01bcf6ea3908850d9dec44ab32dcdb8b408584f32b83781fa25c8daadd44ad2a62b662310b0d3b2a54c6b2eba472cf872fe89cc7da5422c83a725dc39e6306c3f6dc59061e03d1ea82d5814a744cfbe29496e4d780f48aed4bb8eca4779af31d0bbe0ef01803f3b8382a95576a765d80e85e3a376835e5bdf04b2ac2cf0f72f9f98a25845400dc870d964db4881ec245c0be3a666166bb63b44c002de4295035d3b5d1a2780db0a3646e4a6a897df7615567968cc642e3301303f8d90630e33f75300e47fb0ee41af833a7ed859496337dc53214d64cd1d9f1899b791017976b371bcd948708ff0ae398a48b7f81e668a84297d2f4af1308228d57785a4de36551863b812603bef77ca2c6d12cb3d1f2e8bfebcb682e380a278c804150b59439348554e4490b9c1a53d569a19dc52330637eee5be369bd5cb5a50ac765ddfc31157285e1bd081aea1892ae486ae30058346d9b3c772dfc2b5e363435b2e35ca9bbc6b4f069090e19b5cbb1fa0277b05c9a79ee672ba59a17b02097dc2d4e537695f8e181480c4ab409df035a8622acd6433bda250100188af9cc00d93a4d00f24a6e9c0ef70964c4042c2644c0306bbace00d6046b72acb339a1d796d86ae5c2b5532a83d801b0047bac4802fa363d75d76b6c8b97339918fe5abe0d6480b5b7bd876285760dd63d0098adf498d68937158e439599756d03cef309ecffab6375c812b1175100e7ba7cceb1f1b8abbad5b4d48adf9ae0a7d5c42fdcdd25c5700893c6b7b004062bf82cecaa02ca6250527b5dd5ccb15357d077cbeb570b085b2434129b2196028209a074c2f08d11aaf0e01bf67a0af29bf3300e4b261d55d56b6eced87fa607da2b6d38a4a31db57fa8972649ee69133c78fd5f2a357435321c5657166c0e0f6eda84d5be8162ec74e684fc86d1f04c8b61f1d12d63531f7b5fb091a38be20f433969a62fc921acfb9156c1dec401ee25821d3e113f9cf48952a40b1f52e60290db82fac6b4907486893d478df20eb17d39537a3bfadc79b644d24838eaa2054323051f8e9b308c360465c1b8ba770c2242822b8c036fed8e2d73642499830816b9d352e5091edb5cf7359454e5739293a32cfd1070805840ce5cef100823060a7bc7762606d4720e550aca2e2075cee99387d673dae560bbffd014425c24012b4bbc9ad1584425f12883b004e5dade8afe9061e0164d1a677d1225590d240972a314a1049c6a8a9cc1c6b67eb4d8c4a4736cd63d7014762302b15eea2e470ba2e898dfee521b65463d0612a3e6268fe7c112067e885882e1c6c36e3429e70ab21a7468926c19d9779370c67a5095f3af7d79f8e1d9d5b31d3e48dd7c5495094a5139935419d1d86eff1dc4d29039422e2bdb375a006d4eaeced98e8fd9bc048b8ea680787dcc66cc14b81f5f25e3554b6a4e8a0c04a962ad21ba4a877c1001c47a7b8c338ff39e32d9541dc5027dd0ac845e1a75cad1da0fb3341fb2aa6301a56f25ccb12f3f5ea862dc12fc7c7e319aa425fb0c01cc171d0cc84301c4930ce0253106447587fe78775debbd4ce78d2c84f6f7762985e2e054175607df3e014476c03efc5d72d6714f588bd2e068131e32991b1c1a5eb19aefba6ff0a6dd05506d90df7502fd825d892150db5567af4112f4e0a4eedfc09948aaadee3d8b04604e6440df02b01547c4efa6bb8fc3b8f61bc3d88abf422d5dcb68e06f86f3098e3b3d22cb2978da67baba0ff7fee913bf1a79d02299ea75fa5ede79e985bf0959f9f13cec268fad8a49cf7032d9dc8c9548ff40a443734aa501a281e810a605271313521626319fd3120b606a4c6c045ca25802e7069590e3a99cb1c4c70b0382759b6a381ae7fa2ed1828c50302db1664cf4e2b19583d73b13c47249c4fa02fde66f5e6ae055c708028c039ddfb38366fcc4e7936b84930a1cfde5993cb80df75b209b478383561f0d507788f8f00226367083d49f6762db8bcdb22ac0220d515a2aaab0d0319a2d277b806e45b3c91d473be853302bd6a4b8a390eb6c470364c26104c35c65fb0b73e927fffc65091de7746b151c09793de0c69eccadd207c371f4b7e8728ce6c156cfe2b5507e48b53ea285861b206365d7d7dd3bd7840a1b132ea2dc92dbe49df120b0b3354cdc87a33dc954833627c7eea47210331a07a19099dae4efacd0200283fbb9c078a0e9be5f6c4a61502aa4b8e4b813974505299a596f8c0501879ee3901c4bb2e0e7749a0721c5822ab1a8982246d6afaf0bd6262066f83a03d113f7ab475b8c0ff8a2b3d9d2e4311407dac8448d035d060a656c463672db8aaa0dd871a77d175839c301177a4509dab137e9d3825d6a610a0fcbb93c37d85282f2ace825cb5c347428dc6006caa5619bdce9cfa4c71a150ddf3d4efb1cb6ed12f73a1c0660e33b4d127a5232558021c1eb9bb54e2aa5ad0f84936483e72f15fe410ffe45fc4d4c2780faafd828fb3ad2c6f606d8198bca01020a95a5816ab07722cd17fa091dd1fe0e464f7af319cf0ac314bc00aaeba3058b21741305c0c0d27a5b1e8d6c722630289d7a56bd69ed4c013b680530cb66007a672b30f1ece8e84ba1e942648b555311c306716ea21ccc12ce3cc960002d00af188c3bea9e386507150ae915406289b9d452b10f62c92eaa37a3136dde3702ff6544a086b95d203654cf08d3a491f8af50907629944e834f089cd0b7422402e0846ace0816d9e222f7953651e93ec339dd2f818d98282150dc78363484ab08b4eaa777c185b7299734bb06cb82ce557ad1dd1f5facd96ff8cd4792d9bdf40d6ecc9700a0a26865d0cd33edbaea624541eb045c743c5f799405a3dccfb56903c3d17017b5214c4596b0d629fa9492fa1978adf88234e43ee2fccc9eab777108adb8f887205536d0c27515de01044532fc953d78df68e05f9dd05ac86fb0d00f0484f662f62418aff6d57224891a599258f2f68d9dc8a44423815f6abcc446059c1f56a1c050bc8ad1ce3dd4ac09f9d6350f0e12c1c6774a1a58e34daaf5910643b2c65ebb9e08b69a5e910f76dcda7627d35aa504b515905bc3ae3c19d9ee02963b7873c5b1d52ed7e2a165f29822c2b189abed34a40769b1da2731589ce30263628dff4f2f20f68decf33aa46fc397dd355ebe159f8db26a7589f1d4ca770ea4b9291e7b7d16f99a3c66d43ad8c1822aaa383eb79e6e04aa5ea7639636500f984a6caad314b3782a4cf516ea03acb92d4f9d204e633eaa57cc8811a3ca1900ff8c492ada768eb91b1e8c6d1cfa5f0d970b9e4d89c747f2dfbeca435b6b1a067331a6c89de96353987e9e6402501573717d01d2aa9329b652c7b32248a4d3044d9e2f1379d03f1df04d81f4b930fa7e10d1c0a27e0d05a64685f23079137702e2639de22b63395eb5b3dbf37e1ee9452f412d751fb8d81b9da643659041860473157d12b6019ea7bb24f86c6beb541b7b683ac992d0cfd4bcb5db9c3dbf01007aa45f55c9450700fc34ee457c5180546a4e80199de679efdb241491a575d305d7520992bc784612bb9e10c206db49535ef498f7176e2a039b720c4fbe124607dd698ff95d67205e3e362b7240961aa61408091ed0d65abbde6f34b0ceb7520a5e0f9bea3d5070691281bda1578dcb1fb840a040508881fedd6395ce8dbaff0f135edff8cd1bde98d548d9ffd03a2785ca8e8cd0bc16753ef4aab8662638b105190722044f693e1886a98bdeb4d97829cdc20830579c1d71a6421f0764b4430e3f91e34f9c46bf5f05db28d7efe9d9dfa4ae7310c2a4e27fbda97025a030ca0e851f06d74c7c2f160e506720e752ce1b895c2f8057bceff8cb1159bb60fe180ddc1c46394fcf7ad66a1bb0ab87f1b49820342254d1c12a23db30514a748f8704de9f8fadf5c314b4b14348560b4040fc54c7a72f288ae39541c7df22f091ec0df827e9bb427d60c6570752da15ef398a85012f6efadbafbf40c8edcb8a43cc012e89bfb7870452b520359997bf9baa5bf7f8bff1d5621cf64f770c86b8910d05d2a3ea6b9019fa14a3ae4581afa9b36e4ecbb1ae90c9f043ec1fa03f7c8bff0976b7737c5d5dadd779398405764c60d20ce748ce2a313a5630dd5140856b560232047fcaddaefc6202c4669f3b13a4182c7a89ee5b0a7d8ce5b615a741697501dc1ebe621e915996c3b651bde59d12a49112ea9c526852964a2dfe0a3196880264b238576cae7c29375e90889b39f31e9488be7db56300a0ec4ef6f64d6f68015c9ee9adc971e39343ea98e29e2be88c6d5463ee1a22915ea6721a97b4b42f0c7a975e7831e43e0ea3345ab2d251252d5a0aa5d5b47d7ebdfa962da149d82709bc9f4e16efcfaf8d65849709d3630f9cbc234e0abda0da45f8392d86d7061a082159f77f810a17fde7757f2150bf545e100adf4073e06867f1cf7bc2946dd609f8fb4d32078dd9d6b89a49fc2402273725a2a5e3081eb857db4030077dacec6275d4e6f254f65769b74ce3904ce2dd8ec9b5f222f87ea9923efc0c117295280ada5e675df3182dcabfe2eb040f435b2396d34d9254ada44749a823db4a088f03e7ceb41223110a1e13b9d78136407b7bf62188349a01c194545c6483bd57660a021860939c390f8dd39b55c01477ac0912255e1e7fcec3f1fe40a1f46a311503f85048b9b3a1b4d47d2cbeb7c7a52ef112d57df19a9c158e60053554d9d6840313521a9c3cf3cd64feb7bfd43d9b453de0c5c6d555bb7782a5f8feceb5487b04b5ef77bf201a2a66fcba64e56a96b3f250cc223c73425f0d41972ddf6909a002699ae67d986653e27bed12ce4da2548693c66d728c900726cf8b984e289c51074b588a13958f04dd30013a2f979e5951de598ffcf3d3b26df72b296629bc0f05cd3d5d9ae4aaf4e8417c85fce3390c7cb42049f848648de1b0508dab91af9f064d7a7d0b4bf75b0368db66603fcf98b17130dadea274725ffecab7c58624bf0ef4d2ba0561e8b0966a364304e88c1bc2d80b2134382eb45d2b38ce7668ddb0025a56c241485e55ca7e47c8ca7fef9a1584509a0ab222d9c63b49701aed1acd0511ba57fc1fefa28482b8f2d7035bb3c9979c79df753cc3d2fc0dff3adc184b0861e12763dd12ba5c375d4e3e639aa580666fb868832ea3db7ba1e70a1f10fa06354f5568b6a6e70b3fcc0089ee832eced61d03eef64396ba387f863fb7fbf702394c7077e5c3d374a03d7c3ef851e2299a0d5371862ae2eef581fbe62db3550fad1642de75330c2a38c62e998bec626f7de8364c61e202299c3dd9f4cff1d701c1901cf5d3c7cf925df1cf01bda9de7e2d2dd7b8484934f3aafe662ed7a0510ef02fd919b203c5c68ada4841959391b8c20bf29992fe0c44b92a2b58ca3d090d97326033fe2f8d2c05262aa64a5274b6a94c5ed2385cd27b95c99affab324606e51fe1f0aba9e8a5e5bd92b335a6e03c691cc89b2717833cf94c2b9ff417f803b2817085ee72dd951e78d8b8c264c99ada71a1f348f66e4a0defe47bd0176f01172eef2836bc10bbdbe394030d9b222bb69ee3bbac0b8d4ffc029e4d4bd3ba056357cd42e9d6b3473ee218c7b59f80b9c63657384b80de0d6a4591812e37230fe5c5ab9c1461af38443de1c0e31c7007f6afd6e7dc1f2b950a4e5f2ada2b2303c981f7c3d0c22855be88af50f5cbc1bd3c1478c92aa69371320319e3613e9c004ea36a862384942006db77f19470de85317cfaa1b51e7a3db12515b4c51eac03e6e662a0cb222827276a22ff53a29f693498969759dd14b93d618dfc35332f0b2097876a8ad253055e88e5b3c85a7be26460aafa1e881e5c47351e2f79f36a082cf10d42a2855156eec1e706ac5fae589fa7146b65bb1a0c338e1b75082779059ff09370c6b159f1732e0d8522f39271b5f6c3f9254c02a27abc18ddb39ecd01bf52f908ff08840c8d126f9b26b13beae097c2f2af9df66039a0f35522f33f066bca0d1c334e647e0ea1fe4925c54ea6b8617e4fb447fce2ec64747e6c51e50d169b031ae268c79a04679dfda15c207d7386db825f133f333f7319a4762aae082b1b0a8fbb817a0170c7460099fffb010ce3aaafdf7ab09b2e9006af4402780a2980fa997bfc79decd244ab81c06bec05308a454f8454087f91bdf3a18e40d0a59092f2b33651c29b945722f44a891df437e64816937f1aeed3547cd8e7eee012b1f26d208457f194b2e971e6ca03efeb89b7a34fd2b51162666210e550f4c06d798380469325b32db098892cd4914e0225ce3b4063c5e75208fec66f84a840c2a46da83bfb6adf860bc8308653329c78a7fbcd5bff5b7b0b07fe32581cbd10ec606e534ec61cbf0b0c97eab652286c9a8a4eb8efa835562ec155ad1cf0a210d0aeac3164adcc295e09ea0e7bf9d23907975eb3666773b6e3429eb6d7ba857050a183a482637400b25bbac4dbb87057ad63d998a34e514dab1f3dd6e46f8b9084623ed8e37184d4ba91dd8cf48cd9f8e874c5dea450179a07b88da48a144df07483cb1c2c2d1c2881991fdf2638267772a2dc4a66edcbdab2608bca60a110400d5a39f6a36b7675af89a70eb8fc14237af0b7ea1fb32d78828b1df3f7d07e10875a15b6a4f426877431556734aedd4d2c83f0a0c05d6fb5e22f9872b2b60000a2c6a96f8ba918634065c406eecc685b8adb5d9c0140729fe7f6e669592ca8109642596d36d8e65a52c1323278342599a3356f23712c5dd7612ace980d8f4d00f3236c40d24acad77b50eb1c65014e57b32a73f6f78ae915af485b99c9d5cd90ff2ec107589874f704a0c7d7b9272896512a14fdb180a93ef1201eacf11e9af0ca94e416ab80b1a06f27bcc8590d22641bf0f934d245056c4c0d9b13be89ce80e50cda56387b6bb8d69d1343453f85e96f72ea4575893a9709ad1352a9ec85e025a1e7067fe368e77098a75bb11dc6219f5daa7020a436d151620731bd267d406666fe3d6744bf071906d914b096d19084f927c760247ffc84e381eeb09faaa07bc470c62afb9ec21e51d84ff92cdde198362a00291371f85b9437c3a5a15970005287cdf4c36c901c66f681d1d6203cb32e757faddb4173fe5f4107c3616ac042dee7dda1c39edd964854030e34944dee5911020d1641b62e49be6ca4905f90329639bd104b0513d321e9f6d1c13d9c9d84bb6cdff3d4c7642333d42a693f80e53f12924ce9d3461ed552e22cc832e5c2c34ae4342f9b50ae97abe13bdd92a0c66ed75edeb82f811298661b700817f884b5c4b05318d10be732b4bdc705c03034d89652b325ac817f9d9e7bf721b785a13c629a99d84eff99e2edabdae8b220e20ffd56b5a3ef4391dc73696c0b4d2b82215c082955e6d62ee6878ffe9bb6301843c0fab1ab9edb8ffe74eb6d96b1548f0a7f4009054c676a1871d6390d6de0264853baeed34acf6c67ff5545b9daef6918efbecec2724dcc0ebde9c8fcbb40521be6cf4b91519ff44d9b869da7c64f84a96ad841c1725d70b2bbb7d134c110284d882d2775d251f2268d493ebbbbc4548fd65a9e3023715fa93f86cf1612304d71a5b1a7b61896434bdfae0ce645567cd55d73b7814e50cbc75311675e2b5019e3c09ed93d6e40d2fff3bd82248fbe92b5d159fdd12815ab730a41a7a4ea40a31a93d69480c21c22678646de67b3a3848a168e6555bfe03addc72e8b71d6000aa15ec662c39dfc32aed4339ae0a27935c0dbf247ee2f6ddcb1aa9b3093fb9019044a8c0c8c260731f71fae1161e1d086ea14eba4077a07c55f5b742a06b3a08fb281c55d0519654dbe17a50a8496d7cea9c944f2c087142ec6e9da25ae24e035bc75624f400f557673014abbc9fd8f5bde51b98205f655536be8e41332962082b52fcf212ec17d23968a3db7f7f6cb1f5a2f1d6841f955c42f5685d66f60b0bda7354a0781dedbae7705fa19db382ce1e4686137e5402b947eb16bb41265e0663e26068fd083c250c818b5e4f9ca60a3cd1b60afb04b2c709f8e3edb5351806667b516a06f13d5f0ab934316c6063d8c7cbe38db71d59dd759c7f2fda4c0fc79eb51bea36ba6f3685914a46dba07a12a812dc1c3f4f27880e02883ea70f710ff089fdd6939c246575e816d87a1e3c7aac9f63d79070cfadd39d85c81d2ebc0858c45681259c493aeaa57c753f84849a3bb6efeef9a57a6aeec4ad479950b90a49e9777ade308f2ea84b82c7711598e9462679c56d18fefe614e96b472947904a32e8c1b445c8bfcbe4e813c6544e1a6892095e0563efe1d17173ca5c9ca0c039d2f24717820ea5a465b69f9a09e9c7ac273d37d9df9d3bf3a6f41e1dad6f50a452c8543130ad509c60a232fb5e160e713f5029ff26a21bafa9802ee90062b08530f387ee8b200e1b2643426e9c7b9aeac530d6819da85ae3431f2f9399a960f207f34ad916bfd71f7fd67e45609c28c0c8cf0ee1310bb168379afff3cdcdd0167af23b4106d34810639b7773aca18d97f855b5ddc3c2b50c3bb659ba059860f8ff2243a2a6dbaebf87f514f07a15f3cf631857c97bed9ff98a463ea04b5ac0d55c35bc55d0646042ba9acda08b4f96a096a736e9357c7918340d8322c8baa0442f143d5d2d488efcee546d96404500504ece41d962cfb44267f860d650025036a47bc14ab32064e5b8d055739479a15d8e829002f68260db2e2dacd80df860547ebbd97cb7d439123c09eb44854694b654cebc4b062d18344d393edf03b6206384fdfdbb59b39be36671c7310f0ddfaeef9a59e6442e94162070d3317558307d7b2869a9bb43980a7d9de339a11dc6af8b4cb5653f30f282bfca71e981b92068fab420ecb3b77fe3065fa03e6f12d87449d1c6516070f8447351a6b7755e6082cffb1ac56de582fe7e651996d710c9c1f6c2400abb3e1365f86c7c2ce9a9d036a6a161a942778871cd965ac16b66e5f098b1fc5e9bbe4934933521e06f6fd048d0f150fc26161ee01b1b32374f15691314fa21f6d43990e08fba34c0ad9970ffed72c71d941bfbe18dca38880830bf5a254f3e74115866227bb23caeb348c0e23546aba58b153dbb8643f338abc1a20dc718195bef5f3c54baeda293952d805aeec4be1fb69b6fb4971d760ce6f9694040ebfc37da2ddfc6c5dbf2af09a7602509aa2bd81d43a0afa76a0eca305cb7f96ee1cf4e3ac82506c761c23a51d2606461ba8247acac1133bfb68478b3dbe521be58278c3f8913e9ca65b1aaaa8dc0490902732be3e944c12789134b70a99682ec3b1fee8e99d68421e354278512f0620a5d0ca98396bb3ed3e1fa7132c6d27030308f5b829814eea0d8c7119d53508f5e123c6a4e5e274cd2f2ddd8378e0800402f5abd5fd5247a1ff0f3365b4dc0e22f8320db30dd7f1d56a190d0ac2eca34de7b26ceee58e5c63ff913e488cd00a95613c15cfc3f15863ab6c1fea9b8fe5110addbd40dec7b422ca055eed84ca03ed737a7d6654de920eb971a781d135435436346bc5f8e1e41158c62fbaff6f03202bab371808540fa30d8c749504287e80ee0b0071cf830fd8b28dba42d1170bfc79d44806f036d18ec050d8454fbe902aecacb8976c680d0ec9c17e8c58500bd75506f80f4898fd5c4a42a17f2e82f2cfcd4e7c2befb975cfcbc5646740d508ef88b747167d237d474bd6f29dadcfa05ec490da53450d7641b3c63ae231760a2cddb139a0a9eeb275ef2da0ad3777393dae302b1a74b2ac6cddfeb89e6abd06997b69ddf4fa56ac34cdb8b7141a1b0e0a11a1c8e32e1e37587a82345e9a570cfb6c13bca842484ceaf598b572bd063ee13a2efc7411a15e8de1a5036763840e3fb59c4eb807fbe2dd098172f79b78241ae003345724281878c0faf3b23c070296ccd28d078c73db29bdadad6e4a694a5a5e854f15cfa671e7cd5aa5c7878a0a508381c3dda8784e9c6c59904d205e8b10646b50c6ed08f034549b5000252b0b94a8cc071b6f251a1a352c8094e2906cbc004db4bbd5a2a049242d7f3462eb053296f61e36800f5ad5982d85469e0475010064fec2cc0cb41336af44adb1cf0b33ff54d1570b0451ad522c29ef919e849a399fcb72ad15e9e788502c2a08140c4689e38b5083be37c3e78117cf91fd268c8654aed1260e51a449dda5c96aeb0f8f594db2cc5a73b0b6ceafd23130860122411027c8bd54c6f8f34e4da582380fe3a73b85e574f4988f674096a31c7a28e5dc10da8b1619fb87ba25fb99623204c26803b831757e664f317a5a20cdd39d2b7e6720ea1278f618752c02b0f09f0ba25e16ef51e5fca7557a7a4f4e25667d90e07af68dd49385c1396f224322ca01fa64893b19b8707c07e848667298188fe0cf624c3da6ee282c8f91fdf233010aaa37d4a705d1044b949a66d9cc0746722c4e1dc93daf34c163383311915ec40735d94a39646f928405c215bb3991bd7c35e910f108eb26babbcd3485e4273005c2c69bf312560d8746b10579dada76d77f186a94bff7e541361b188992a26b0385e2ab25e08577eb5594828e4480d93a68db3ceb10bf1a79e2bd504494365807ddd65818a034c13fe732d1a4f93aea2e23dd3ce8dccd3fe9ed78141104ad4e0f2fc1cc949c664814b4ee571a6f91b93ea21f8a33f4fc48f3233fbc0a05aec900468ee63e19e897eeb1a75971f7c8da58a5734494e1e1c6d34e4aba6b580da70bede7b98cada0b3de17d635baff01922f171ed0f9960b601c638e0c2b4c3e92017b83955fba85272593a94c9a3d80f1873d41d011f8c95a73b58e2d154959f90fb908c1bf3e26517ea5be1455eaa867291aad6965f5acf8bbc474142582ddf00f51555085f03e371dadc582aae0070c54236a391a1ba2bdfdf5fe37a235ca270202503e012664f979ed2734fb68ec36c1cebe3c99b9454de03a5efe8258cabd0b8467de127614e1e03145db82bb6f1432ab7a83b21331f8709c4bff0fec0e390a6636e071c824951b1baf4e0dbd83a40e12588619c2c9d01c79f3ceb8b196130039258b1a304ec88e19d61e3fca72eb73fddd7de30c2eda50549745ea9e938d033ed5aa778fe1ab6410ff799909b0cd939f2561b690a2d7ba369c4ec44faff803067c99b84749b3a444bf5f6bf873899e241de5eb3e260388132ef70eb09a2f03fd95940cd8992f1e82dc2688f626935332c1b47865ac78cf9251e623c1d981027bc264414add086a1d8d0f8cee009eda04a1f51f9e36fb255067a5984155c30f7fa87b3bffcd2ddaec38b6dd5284112dfbec572ea62c06f9b966e205eb599409123c4cb409b95be728ea5dca6c8bee456922787fc78a7df137d2c0725226a107a5468d838267bf93cf8ac29a6d4bbbe695e499203705981504a6580640834e07f838a819f6e757f04feb686f63eccf3739edcc21e665039d6529c411e2473e06fecc6798c005591a044ce163f21d7dffb607c1cee90d46de15ca6914b812f2088758a8acf5bfc2430fc1c3ecde482cad537bc2358cef878c42199cfc7be47a07f42fd210a1927b261c9f1038778f7490bb0d40d108e0b8584d9783aafd07dc08eaef460d471d1cc080212c49c47319ae7203884bfe45e693a5a2639de88b0c00e8fd0fb322c823ed9fec6d2a322ef3b9386c4e702caae155c5463bef4181c2087322161186527354bf5e8e51e36e8709d2be22819bd5ed32a2ef706c66b6bc093720d04b9746c2e8aaf48f7f6ff509ac3fe105621274671c9da676f0bdc67d0bb49e6407e2387c584691c8097b4e248631b48423025e300a859c3f054941590eb4b3b4d709eb11e2682af66146d426fbf97b2e9ccd4ad920cc7af805900fe80e2d227a948235e410676b1c2702a417fc3590505cc5439f51e431ba3c27921a0540b685d9230c4261641b030a2a646fa785edb146dc135b016afc3795198d2403383809cdbd1d2cbfbe47d364edf30fe5b6fd4bc0ef35271226bff6e7cd5e13042164286c74720b1ebbe3e3625795c991faeffc83d388cfaeff4df9f3ce99980d4aab3d3b71dba64969b16683f8f9e6c9d8d03ee27e1db1e3c741a4b2b152d00bc63d99400e6ef10f372bfa4d406897cc4e14d03707bb7dc0fc07b7495cfdf108a07ef45112adcb5ca4e5ab6e81511bf573a2e3b2c02ef3b79f0e6317e5113b0290d9bb0ca4f229d1f3a470c9f07e4b4bf534424edc1161aaada599ecafaa220d4369df58f2c6ad681768363555053ccbb52d674343cbffc28d079dd6d159bc021891b40eaa5a379911a1a6f7ad32a85d3d48d62d8b24aebed570855bea23bb0eee50be93c1390e8003f7575b83c9834306d987815b8699fc7311927b3dd2170e7e170bfad169028fc9cf0e92bcbe995f5f301a5019e5dd4b9efb9dae7c96cc086c8b8892547ada281a17a16b856e1ff6eb97d1683c9dde1cfb9488031aa58a01ab1cdce831980c2558204ac3a7714ed90e73fcc3a20e63fde9ef94bd02ea340de85d8a968a90a4b658fce43d7d5ae682a0e264717ec3d9e10fdc65c992162b0ba50435b6367a854f71ced0cc56259abb6d42062a838703b3b12a65becfb1c809cbb2b57bf65fd382a4b8649c1a694a621aeed9d03937fe2da116fcd3824f5702b4487f8ce7b37dde953918689865cd0df9042272f038b5e495c5aba213dd48081bc9aa3ace8d1a0b13a05b5ed5327fa6aad024efb7cda219195fc9f1f1f70709c5c36e9c6c75e1c3700d5aae3c66b50f95c077afe4a7cbb0507d71ae3f17510fa6712a123cbdd7f81110ce2119f3eae5d8358a3066cbf086adc49b104697de044782b4495f34ffee861d10d8a5e5cc09f5e8607c717732e36852a99ab441bf03f183f209b53eb10ecc6d98d8a721d016f83f6e0032a3cb1480303d1275fe8d0565ff91a0312fdb93ffafa1803d75750049504f7cc6ce4916ef06df6cfa55cf05cba393b219b3aea1dcf3c8ba31e17c05f71f53a9aa54430da3e944866b960a070d9b079e694fb4fc13b52b4012af62974d39f285157efc4cf92f11fb64e81202008666f2b122fa3f455f3f1040cb248dc4248fb200db624935d912075aca3001e04e382338da8c53ed3064bac1daee37a1d9aac7f2ed00b5555767b48382a30de0dc34caa86b0bbd9dd3fe288afae94fd03edc762349d7c10182b8ecf42f5b0a false -check_ring_signature 1cae308e14c147235025c6e90c69226229971e126ac0464e83ca62facc4e943b f94baf2c43e53c9793e78b4ead812cd6ad8c9983ee103517da31bf56e9e8a834 32 6c4056dc671d75a2bd1f8e1ab37d6f697db56386cf8d1410844972942de3dd92 20cb16582ddfa3882aa734c88ef1635b1100281d096a9a30db2462aaf9b6818f a71ca9b06a33de99f4148871fa1f3591fa9a1ca4683cb6a1fe1e6408812f7e92 749dcaf1bf424ea43d7998767a6390cd08766f7ad7e781f395938e50117cf4a7 7329d3ba1afa598e541915155b127afd318e9bde2bb55fd168254dcf25bf192b ea4b657d5ed6fd7a9692cb5bdf16bff1a08e86172db4fc7ae9e0e1d012db6d5f 6453b861c007cdc3c8901f44ebcf72cb995bc67d57009a8dae9397ddbfc60e45 f3987ee2b934877a35a7a8ec9beae047a16994ad4d1b5196f4f00a90d791ddf8 42dfde55ea15175c3a59e2f9c8f2a6ed864626dfeeae36ed87b5f8bc5c5b42ff a6e888cb8f414681ef219f342357011dcb5f476a794884abf7212e97d9e39fe2 6d27e1b551d17caaca3054bea64b0e2171e2518c092b40779f31b434033fcf21 321642615cf3c5efbf667d3660aa9027a5c7930122ec81e77081d8a918dfe070 1c84fb1d4dd4d8d44a386520002eeacfe717506f303ffefab57eb0636522b980 f64d987a4c258bf680282c02969baf9cf46aee640a1cf1a6d18e998fa43be1f6 7f94b17e66f06d46ca4f4518559b3e30c6bf2130e0c3e9b118e5c0c721847c9c 85103b152141e6cff147a41dd75c7fd1bfe0404f75211f8e248b7020e71ac34e eaf6f83ed94268a7b37d6a8863617281b71835258e117da1a53c11e5d93fad57 7d63f61b4635911e9cb4081ac680fd838f01bbbbed26f05dac7c1e1e01710885 8b080766c9d587c580fd6176560e84fdd9b46b76a4b7fca6aba8ab26d8306778 546b94134dcaf0ab9a039a536b7a2c849eff6cb4ac42ef0c65193b8ce80fe642 36e4b5a48f16f5015702301b7141d6b7a40ae8e5a22735558e5ca0b2325aa917 0f08bfb9fd36998fd1a17649664e523c8ced7cd6dc09496c9e45465e79cf6c6c e706d9cb4b99b9cb6e5a79f9c8020ab56c15df0f11b90bd46cee288d317d46b8 589daf1ec465ca138099a0e3b31e21f3f4e3b9345a8da51fd40e22ad75d55392 6ce807b75338bd9a3d3d747155c99846c8e2991e4b3a02a9a314ea4c6d86c211 17d61cacef31d561193b7e792d4a249e35ed7e883863ef473a72ad8a26ae80a6 5ca01d8c31e0f72c1a757295922280d0e9d0cbcbee8adc4f93e392a997a1f843 6b7502549be684edb684a5f5a27b6abb6bfea3669a4ee428f5e140613a9dfc2d 3156b50e1b7c190dd0703d6a0bfe73fcf56618a76c8a48ebb4af900eae914942 c044c7c8b368dd336d9c43d6e280b8b13ba2b28269bcc79f3c9584af2e5a496a 8581760756fe045259b8559c06b9ae3b0cc33e1847ef159f4413bd4c1133d130 1ba2aab7847c9fa854b31a778113af5216b4ca696f4c8af11fdd313fb8bf3761 a20bbbc07e38f4b90eec5d700e319c3f34fcc7887bc0874b82a993d4ce767806dfec6a9073496c4228350ab4a2ead913d67efb65599eca8150990e595eae790f69e5fb96f7f945e7fcf1fe349dfe0d7fe64da4612d0732490dde4593b2b39c02d31383093ec732f3eb79a2168c4a4876e3af31e6c06aa5b45631ceeac7ead50d20ea8ce8c00e508c31ca315936d3fdc2a9d4b8d7f93ed00f90710df083930a05285977bfa615fb3cc13fb6f17d65a30e5d18e8f0b2ee561b669459bc4845a50cbed755f0f712ee377f50cdb9634cea7f847ffcfacee25e88eddde20cf4518b058056194301afdfb451310f6d933ba592ebf2410ac4efc92768c15601c262990ad5ca3fdb1a2763bafb02eaed987cb0b63affcc099b6ff6324e99c52d2e030703785ddc58d3031561ca2e5a152c8aab5d14429338858db7513f3ece7d43e42d0c75d0ceb47674c395fcfabf1628359eafb6978462c19b6525fc6535d3c8cdf00c469d0f918df0b5d347c068cb939303f245d8f35a63ba824cc29775fe93bdf00ba2ee138382833cf0a33a91c908133db25fbfcf8152298c0a1e8bd5d396682a09df8a4a6ba710e60efe3a45894450d6826f7518eb61b4b04da5a29954a70b2f013ec08540005821d7b7f78cab3b0be7d653c1780ebd4a1280e3626cdd8431c70d04f1e43aac986873e40c9d75c9cd0a9bf8d34bc91284415179c2faaba95d1a025895f150a7b6e53ca7738289cae2e864dccdab2e0599f216e8918e04fcc5f10ea4e5f6b8dc702b6c6bca9e4e71a1c1d0a75d2901b457e1c62cd6693aed1f450b01959bf81d809ba6c015d2f8a22a319b0d6b2a914ad5602456048f488959650f73be24d1a68eb80dc3831196539b0a3ffa73fdf172aa5201cf358b14c794330a771d75054bf07621b4b405242e79de6dc60b11a74d6559f4bd9c32ccdcae4707d682dfec27dcad6acbe613cbe072be6325a8d8a8855c58b486d14543c085c1081f4e61b32e48d60de963d724f8cdd4d90f488c315ff81b7b24ff249e731bd60071e1ea3fdf0adbb1848306e5237e559fabd5fad9b69f29c4695b5be7ac23a9055fad6c657d50105945344db09d5aaadc18a177c273f57361b6636b625f244d08ddbe5d47f650c19de16da6e87394a99887d8b8a1ab2968c48ef0bfb8fba7af09a399f2a10129ccdd5ac0e297e5ae705fa618a8cdb5a019d58f1954c5aedbad0af4f1284db6490b3ee72aab0767244eda282bc01555aa4b0c0aa8fad1844a800eeb329fa7fdcfa2c468fd19709f9d2096dc685a24bc23e15e1a3ed51db56f6c0372883a183b531a2e4bf03b2a04a9ee6f6ff1f208499e36985eb9d375e75e6b03a50aca6032ecea51871bb9214065229c78fff79e9db1c1c55db06045e74a0406d8a1b44e10dfa39067a18d081de157836c337fce2477266c7158a533a0ed480222434ea290a15d0bfc713279e84d14bea4ddd80bf34e2fbe0d622521bbdbf10499e984f50873ef27db397a86db248c4965da58e4aeb11c99e50089bba4f8230e5994af438626ed6f91cd8657dc3d8d883d0f3da5ebb77a0bbec510e18f7af206985b76ceec34fb07299a6414fa8dad69b79041262391eb70134fc5b0628fa700ac5b17ab899e3d763a63ad6b645bb3d9d88eaff2c8bb68a401e77a2d3e1bc60be2ad682db1a53e75d3a4478b2931da7ae906d46f4ab24e43dba356798770400849044e35e391ed5c7dd967a71cd8cbae096164ff8d055506c762046ec1d2af0a86190ac15bbd074d0aaea41db818921acafd788e734fc99abf197ee39707be0a3ff1f486c3a26b4450f43d7f424f671b8c609aeecc1ba41a5ecbb668a562a60122768f122ecd00a4cc41ba2e40dbbee62d5bd7a54b9ac2e6e03b2b79c498c60358b9ee5a481262498a7cc60a11438a83591fa52d38987925668e361171b1bf098989f5ab8654d23628a410c030e283705d9fe10521261b99a8e113dda205120796b6aa41e309cdde125e05dd4bbde6aee85a8063dbc1d4b11447257bdca10b0a7a4d9ab417e6401b8fe108ab3030a4642b3aca66a614b1276b541e849f31c80f3e94a56b060262e6747c8bd354f50bffadd654d73c6fd50e2858ef03dbc09d0628b562e93344a5fbe8bf721b37650422feae5280fcf747da71a2e927fe1c0f0889a5d1b024d2af88c0179ed29b820e1830ff2348bd99d14fd4cb33165d70b256f60edfa95c133035e14e561861ffea9b5ed218dbccb7d84f34158d89637f600d69b4e8d56eecd813406d149bed610dfec307fcb105b6e5b7f482d92f585f2806481641b331c73ba160b64e5b7549d52e3d35f5e56fc9c2be48a44dbd5affe1032724f86e1a8e3e62e971c5fecf7dc68de9c5fe99c58bc14ec001ac0fb169bc093d574f44f3c5dada8c3324d61b09ab0f800f6cc34e15d170d92114688e151806325bd2d4edbc3f4f6ead59e59199ca156944cd720f120b8bfaa831d44afbce026a621ed5c6b130a23b9cb26a9096c079e752aa179a6c484402a69e4636d17d07379ac0ece2a6a7a3cc3db1212c73fa04cace202917330e1acea44c0b8929c60fa1fd4eb590b1d650829bacffa093b4bdb2f43c7a131595667ac7e6fc1c3b7708a8ecb369461fc51c3942772e137cd20d8fbd921e47db2646b1fe483c95111b083914d2a2394227152f8fc04a5428145121ee97b03a4ff4e2924521e888145706da15655b1550e448b56ef9382ba54058b7d3ab88a1eeaa26820b01a6022d8705ebc6a35e984277a33afe9ff274b7b2e92b137b04047c5795c41505d390036005a25f1bf1093c22960416639902a9e25076a2fe825b28d8e937b35787a55d8f0e68a19e1343703979dd79f07ee811120048299d17968c74b84ea760514bca8b0d false -check_ring_signature 8f4ca926185b1644b02ef5d58669fa6667928e053d4fe59daaac50f016dad0fc 397dbe9cece430e5d6160077fc5cb55175ef6e6633c47c869f820293eb33f447 205 faa07bed8eb843f93a9aeb874fb06f3f925dee40af63bd2aea383979571febc4 e0f664f6db22f243bc156feea5f4a509f76e947dec323533abd0aca3176630af ffbcbcd4a93dfb46283a63cac30782d5225db55f329ee2c43a7eb8959562f94d bbef41ce4241cfe6fc93b90250980da02740fa5ec6f9722f5b3fc65bf141e744 ec1f4ffcdcb99c4c52a7926fa586776f348484a6674761beaba643108d2881c3 9096525d83ca25a890b63b0775973ff2a5ceca9ef75902ed86764211f7c6506e 448f7d2afb8705570f693a70394cf7422775eaa2120d589e43ca5b21b095f8fd f76893b206b26e4abf34125eb364d03dff44d394dc95b19514833d44cba5d3ad f79550e340c723c6158568446652952a459ceb2f9cdd64772247334a42712a21 d1b7fa86470db365156fbdef611de59499ba4e431fff9f3cf430ca9b9717fab7 cc914bce3af3841d81c14385bfcf4eb4f0cc566724194e2a07f805f00e0fc4eb 74805cb24f2bc22bf1d1c5a907b92eb319968ff4ff5041cbb9c138a2934f59b6 accac956f776ee1670ff93bbbcdcbf54029972c78e8568759a49acadd22f8f7d 46c845ee8cdfd7efa0faeda2425b0dfaf84610a9d13055066f0aacaed816a647 7da0312e698d2e4f0ba1082f436749407ad78ef67247ee630ab4890e9f8fc9c5 9c195cd417997221b03b17e362e834f5f4bc7f21753c44757b464d1ea22b8478 035de79b3225ae044d7c0f18f276dfa02e9cc8dcbf6f7dc2df624d32b3ae722a ea35759d09029404d104e31096d1f245e0b9d61a36e9f1b9276ef004bc3d404a a0229505f6f48feda9ff9490792224e1f890a1e6841364825d28b5dfa319de0e 56885d3349de823338403d7d644f1187162c7b31d7ffbf3de9501d35e134baa0 369a8987a49f09b72228533dcf08ecaaab326b7a22f148edd7746cc3ac7a47eb 546bb271df47deb4cf8bc7fbc883ebe43089182205350c7bbfbda13b41847f3e 89027a513788c727c60243e6f7e21be84687d76c01340266f5cfbf29ae5579aa 6138b48b4bee9fb49c464483a616cd41c73a9934d25b6ffbdd86748994d3d4a2 1296ba248c06d04ef912e39214f5bfea42f23d828073d5e264ac62fb8503e522 f315fad27d544ebd10241af57c705dbc61142223fbea54a297e9f968322d727c 501032172c56e4ba8a9b42f40aa763089d4b594d04243a0819063732c63591ab 52ec92613585ae0ac683ea5461e7cfea14bd2a32e05895de9ce9b06bc8b0fd70 2b2723710a617a8874c9358a54f1f65c21a96635ea6d18e59dafe678c6c18742 5b8a5fe325bfc52076c50bd31e077561b3902ff0d5e3f8621628ebaf481a465a 076267c2befe4fba2a313a6168160bb9853e43ec2bd49c9f6fc121ff8df5351c faa456f7ae817f26bd36c8e2cd4eaf6b8cb1e39dd07abfa4794da73f183dd194 cd51e428452f5a3a5cbc5257e9095397b884d19a0bc267a61ecfffbd5261f709 4ae1c01a499fa70e1b54291d7cc9cbb65415376736c5715c5594443da020493f 0d2a3423eedafe70a55f11533685f96a1961f59c77bca8d6243f7fd1ade1ff42 a4d6c54fffe45ac1009cdbc5bc5c39495f2205b61a0e4bb7c0f9cd9e6a734b1f 87a02fa664a14a808c79e7e86826e7e596c87be85132e6933ee0f921d919161d 3676dfd34f4e3d81577ec10f38c00859ea61a975784090b1f5233501950dc6d7 77d3f5d0b0d827e7b4b986442533f8d13042c439100c97cc23c033a5ffe731ad 6ead2bd64e5b3c28a6657010562d62624a79a7c6c681cd4a234861bf4b7170fa f5637cc3ee2c96a4ecc25d0fcc397fe4c8643089d9ea76a52dd062f1ec0b75a4 7192fd7715a2a24bcce48e3287a2f3e9944dd6eb3ea856f83e10385cffc555d3 f7b48eb464bc49d2ffaa2d00653613f0c13ca4f317805975bb10762aec384f18 db6a711e6a5989811872d4e6de0587194775b8af377ff6c7218f53d43bf21bcb f422568f4d73e96310395fe6c2dc52fcc43a3760a9fefedb447b22b29ff87bfc 544e711d490d8dae23ce40fa5f3230d51913823ecb6847b2d6a3d2a0e7cf73b7 d08d447aaa386b1c14aa3aa93d970ad2c9393e75c8cf1d9407db195fe794486d e4f69760bdbae6876c043d5dff6d07753da50e530830f7c873356556695f7a25 65895861996792c9217d5e450875bbdb7666fcf7e56bde1b9a936c2bf1a829aa 12831887019ac1e81cdd2318cd0848bc0b18e8fb035c1db0025f375f9d254e42 1f57f27a0739adc87c96bf57bc3ecce0d169f58f861d500e429dfbc05bcc70f1 2e6666c909284453ef23d3277fe7fec11e11e3258fc4316e4505e45d3386fe47 6fd5e446be6bc557af86e52713570eff775fce463a707683606b37d80d85562b 634ff668ebd66ea2cc3a0204e67d920f8bd6ce5306613304a1bc596d7f0f7c86 5e18cc34ed35f4dcdac079533b594caefa905f20590b3f753278eb5103b1855e ee6983322cd657736c3c63cf0f135e7af9d216622ffcf74d4e6d5563500e7bdc 1d8304240fb169eef810bd7e347de2a72043477e60edd368117836bce8c6d1ce c0afe08f0d52861390e961a383fcecea9a15ccb53b01e3b152c340176978f3ca 4a01ff39b62c861db28b29527c1f9b7f4441419380c0812145a2f03c4c9004e6 56602b50d850ae7ea0e9a2b86e32057cf59360f1550fd8c49395858392fdeced 697e95def772b3a3e67e99b717d74e10ffec5ff51941996b228d925aeddcca66 c8ae8487e1a3bd97af2ff7d1e3004fb05297f4e87bb62aca14f6dd1059b38940 97e209f01ba6097a2e9cc0ca0946dc6dcd3ae1241e4d97a7009d409324e7c2f1 3fbb9a8e8f0eed259ba62591cc12f6a6c16797cc20d8480d5d6a8ce24d843c64 9daa5edc63223d6093bf5eb08c14f1e39e672a420887123b69f56aea9d07aa58 304c8880348bf315235956d2ba7db69fe1cfd06c6b1f58901e7e201e615df85d dcc5ffb5feeef365489a2110251ee05c731d2853009555b45b5775f36ec2352f eba4a270d3b339771745082d081e93f95c6fd7f4ef784bdc83fd834d545b8593 f06dfe1dd1cc5d2c487b312fd7b05339688f8bbe8105a6810039abbc285cb587 9f9aca63a1894a2e031f8b79361e4d36fcbaefeabb9de3961d589b10dc06312b 02eb86a63ba0b4321b24212333bbca157c61addd5c568f3af3c691b6ecbad8e4 3c3606efb20e016ddbcaaef5deed94ee06d9488078d9b19ecb79e5014268e622 233bdf32cc68794b6d63b60979188c3616eb5ed9b9ba1176d20c2a668f75a5be 6d9c05e534caa5049b1567e726dda3b118bc43f63de6fe7e16fc4b918e63b462 8fc25108e6eb9b1177605d05a2d31106cc38b59dd60bf00b5d2a507aa3e64b1c bbb5b758b007e1a7a829b25aace41c5ac784a1ac06999de72f768be728be7702 9f4d202f5d48361b2f2ace3e6b9d1958e77aacfbaa82baa7e860f67cc1ec66ea 817004f531e4d191f80be7f22440939ea24d3433a7c7acef13d2d74d9cecf96f 5b5786c1e5660e4a0b5429596129abaeec6348ae826005923e2aae48f4073194 07a56005b44db6626b9c968b5929c3e7e4c63effe58cd7b4fc5eafd396856aae 47f65776dba915592cb7e960199b1477fd233c08cc1f54d326795d3834a3dcd1 04fa1b882197fe4f5734b16f14b2a55a5f0fe1b1d4e1f2a57a26b7a4a9865346 c719aee696cf313dd115fdbde78afb0e4a9909b90d747781a4208eb5eceb5f75 7c98cfa038b9b877fff40f3afa27c8daf3cb7e42fb39b0118f2fdae7538ff99f dc29718535eb7b6c439112d0196c200f2798eec6d942c034cb139cacbbd36b6d df6fdfeba803c428bd8964a18f2139d602738510959195bb60c879e04a17bdcb 2361c53342a9bb63dc72f558f65f552edbdcad672f559b177b3de76f713d1efb acbaadb9752c324144ba6ffea9b1594404dd70b297cd6b7ba9745229580ca5ba b7ba03d7efa3fe5be57f3016844ffee2dd24fcf3b97298163707491be05e3c2e f16ac0c32805308da08a445a9b4483c96b2d1002af5c4f0b397321cb901c4aad 02cd4ccca6beff003453cf9ad77ad0f59a465c11d61e2f0715bff97aa23c31fe e42d6a5648f34e9aa35859fd57909ac83fa9d47ca97e8aa9b2f282489b0e1af4 71aafe73d611abdaed1f754ff29e6c730011e858f36500fa601410471af092c7 372ca22e8fb696fdc3f25ebe363944dbb9641b6f783f1e5f45d9129209003962 b9b7d6cd1aec9383351e4f6e197508a94d4581660337b7ea139810ac2c2033d1 af378f89dba19bc0b4482ef94b06553294cf524adddb411840dae2439e3f169a a2461a4e9765b18143deedbaa197cf6780fab5900215904407bf63ca1390553a 97ac22392c4e4bb2d3a1062efc187a99a3c4ce1291b8252fc87d7b1ddd5806be 7103a62e037b1173e76196cc236bd5f9a5c16a4b548ae3aded7f4ea1dcdd95c5 d02eef8b5426b7062982af8b85c5115768b8d7fd50cddfe807f873aa0ef854f5 da606130559f6e9ac70661198d75d454843fa46c2e4abca093aa47389fa95807 9b91b482bb09ecb6439376b6852063ab1c3c33b449d2717907b5130b9e88aa4a 27fbd74ec0705b49bff90fc1c9c86590196d1746558ece62fa38cfd072399095 c5c63e6d23414bc068738e256f993ef55de404a648141f9a1ffa22597a9976e5 745715ef2b3b5ffcd4ced06b5fe53391e7c6694fd01c6f4fb9810a5183276942 d5e0d45574b2c9bb640e27a49a1420661fa2a5f8b9790aa3cf5259ebb9dd1980 50933d9a628fc65d877cb20d00ddf00c879b4af855d5cbc2ff895fb78812f3b0 e2ec0dadfad14ebdfa2596ce3ac49943dac492960581484ffcc05ab063f96aac 5e7ee50f4e8e6f05eb3f8dbcf2e4f4e74e44d877653c70c23871b7748b397813 c6f7f62120d568ad5b8891381c057b8a7ed71061ef6ca87dc3828dfee3754601 a90c899aa7ad86eee47874468c20c6257030aad361aebb5ba4cc56369b25bc3d bd08efed40203ee26b588a08f671bb4946c6b6b5ddf8f12fbf2b28e6864b2b70 330388c19aa2352fd10d6d0bbb9f741840be2a2d7233644bbff322958a0c8bf6 fe3c5c3927f9c66c25f4a712750a193790b3db93a0da01d2bb4417878cf40905 e0ffffe78a502693953aff83b127a676d26b75cfba8f7bd1c04e130bdee21096 feb846d6d7e35213adf617f6f284a6fa796a8559fc845fe3540047d96be77788 218d3dae45cbb42323aa7032a5f028f1153afdf67c56fd3da24c62552a99319d 0e86229e5502e18ac9d9cb0965121eaade739b099ababf67aee9a55214069ff3 cda853523bbe8da8b05e90c2ac32e50dee800d42414df96d6a7d6997f4d9d7e1 4be74b00d77074d611d996112762422a3acd8f54f219e3b2f0de03fe5a31ee85 1d724c063cab9871bc6209a77fcc927527ec4153165c0f6772436adcb6e26b00 57ff591e7ade9f76a77308a65303e0d37478d93264bd03600cf4a1934031a631 93c472b3a4db8bb1f71ddc05076e93012901e93bfd491055f763c65090ef2dfe 464afdd394cf72dab3cbfd0ab48ce6f81505a1ca52b6198f2703af5cd6329330 509559ef0841b6d724fc711b91bda94e8478609368dc46a14bf60b31a400f018 515acbd4bb845bfc232e1d4d861d86e49a881f9c14c241e827b0f6bad616a825 3b4d6fc97a48868f94877328e46c138dc1098ba297825b54ea64e509e2336258 6ac1cd1db8f7179c6ef212c8021b917f0742aac8ff9b34c67eb501e1632ba57e 6f6837f43d31c170814178f65530defccbc72bc83c4580fb5a80e7c868e230ef 58362409f99880081757e9170d01fd783ef59c19d94216e20bb3d1401a7b6974 c794934049c0a06bf25f63e7f6ab566e91d7e072f907b3b112c2254edf819918 90231b9c26075e77b5404521dfad01b223f0155b1bef6ffdd4a153baf1f5a5b2 938e6ee557593843ccd276818704b6d1dc8327c50ee4ffdb86c71a485d92f04b 9d31844ee3cb199ed5da6a2bed0a54f118193328baad46cb2ffc95e7d1c77e2a 4fcd595e0b38374be1471a04c789f674b344d44a5de1eef557c6c103e725c748 26ebe6ddb895515feb9bd4f3d4418c04b69469d5cf1f922d4dee80b7461a5945 1c6889c61827b4bae9267ddd422ac03c63fa3f643a96e0d15da1557ed74eb1e9 55373951a22133183552112b4a537ff459271836b2992a4dc51b92157892c1eb 7b768332f8608d3395e48d17cb003a678434af74b233d848ad2ca3393b2acfa4 f8cd4d94deca9d3d2214095e1ec11cd0d86dccd7837d9beb002621a4224e0c34 74dc7a8276c0b6e3e0787200c109c33ab29e8169f13112f526eaa3adaa9f5af8 97388402970b81fdff1b14f0a8910fee2f936428ffedd26a6ec13c1c00b6ed40 f13b5b3b5017932b26a9532dcc6dd7ef1cfcc40f4b81b6f0b68d9105dfacb54c 955ca46c7f549ad275f758d1b5dcb576806e620f03d71ecdfb6cfd65212ce46c a71150714ea22ca35102643c6d952e6b93e841d933a2c95ffd46d0a79fb8e254 3c54cde5ba11e16393e1c31b61cef7b32107f87ec70739617920192ce715458b dd44e56a5eef6e89414af263fa52a85282038e1dd024d463dcd400085770b1fa c09fa0ab5fc99a12c0568d538b4a2abd04d76b1d37e62fa7fe55289d94ba1576 402f9c89835b14980efc659577c38b3b39d728e7189029d10068585350514c57 7bebd50d97a61bbacb11f732fdc336f9ac6354767e29aa2c8dcf5b06bec21ba1 f12d5b67030048bc00d114439dd47521ca2df4f94cd6071131a053fd00ec0dce c70f143ab0113b872abfdedeacc62d09479bcbd65489dd9e21a737592414b284 3ef3f9aa0196de6c975021b76b367748843315f1e8a07d55fbc45a3055f8b083 683ab3aaf5ffabd90a92aa31d1e956d133215a888f089e5e3cc1cd1d15352251 ef0348d0d0205c9f2f9bdcfd5b2766bb021c86289d9e336a2aee89ce3b2d2877 f3e2d035bda15ffbae710f2b790cc4fd5b945845d068c6c170fbcfc87dd16aee a6b6515dac40cc8cb701bcdc6839d44f0fe74cb38c4ece279ab7444b358fe8cd 42bafff8342e25549956ced18aa09b127edcef71a17d0d7d79352bdccca3c428 b53e28c58701b3f5411f2cab86ed964ece1a5eee5935d64cdbdcbe6b181d48b0 52b92490b4a839e03c92ef058239b5185ba4b386c4fbcf2bea13c08b598b2783 75b5779d59ae1d0feb447dd361ba01e1cf11e5f81aa0c0217404933915a74945 72429bfb42f8e9fe616b28a12ec9fa3c9b9014667caf1f827aee11ab50e293b7 c013bc407782717b7d3b5980e12f6bfcc8b1be08182549606146452ab06802da 8b749afefd0c5a06a0226db76c9a69b7128a2e4a7eeebba8a8e8dc73432d13fc fd38589070f7350da42bc12148bbfe1be9efcf8b4a2775623883c0d7dc5c9a96 3008d0ca9211365917a0792c35e89829bf764777326a84e4916007e047a54768 b58766c04c081baa19cf639259ef6018788b47197583330829b1aa2258e6b2bf f1a466a0070f4f1a14a341f2a3626a4bb1704b3e89a6b03d253468ad924286f3 bb34d48b8c9418b2ccd0cea7099d2b7c74725ce837ab1b1130baf03ec798ed2b 7946d4c43e2b617311c0280d43a9e9c14c8847994748dd260f951174037af844 fb90a9155fc6c040899f752d65a3422e93bc27cd2d7f99c33dc5a4fda16f86f5 45a522fd9adaf96978d997c930f6f6996d81a7dabdc767bac4dc632662244cb8 6add423d4908944112ecd9120d17434d56c0fe911411b3d8de622e57cd58475e 045f51414978afeccd2012f0484ac217b83345e0bd536ba4c1c7e1c7148c81a8 1a45547d5cb18807120e139478ce9e85f4fb06314c715cb751491167d9e91158 27ca976c01a806af9190f4aca2dc6202ec8f46c981eec7c883be04bd9038cede 90ee73e86b8581739eba86e8d362f5a1bd6453d9ee3e146d85890a95f799dd08 7e44b9bd3e559c951e65a54f5606be01ad58c96348a5bca801287addac251cea 471ba8907b6b6212cc4b49e07f83158bfcdbff2dce62b393527635c28f0846dd 9481720a90b41e5a02d38848e0ba06913dff1eae961a473f81cb34a4c8381c31 8cb7245b9f8fad5e08589e8d46bfb9d7d909852ee130e395d5227970d5505aa0 38c9d0c36611612f3cc9f9a5060504230423c9e0baaba5a22989b2a98f783637 d3c1fb2e351f937d4e9dc62842c67dee057d177dc0789610b379b9f9b41c9dde 1a9759b98a7f06786c5d84b5791724e8f97dbddc06a0e893502494c5d421d735 ccd9b28acb1cd780fe7c0f0c38fe1b9818a79587795cf41f07371721b16e00cc be096ca7dad3b8cd2d3a573497e8f62a30ec65eb6760c36aed89b356cb327cce 414b37213a63daef2827bdbbf5715d74782459a0194706eb3b9dbfa50d52da00 42422f3b794bd03dde40a9658c23a523ab03b82b036f7d11bada20c160725090 4bd07dfb275a744fb77385b57aafe1f5f50c53c586087f890eed4679a6a5d39e aae5e924ecb8b6c671583f706c0694aadd742aace486c424b0bca9b6c8abe352 acf08a0cdb4f2a5681688e5133a905ffd0c7a407b85b2b0a5c0576ce132e4a62 4582ee214dd4c731a27fdf1b18b8e00eb3e13dd72c8a5968fdd020fefebca3c9 43922875bd81393ab15ad63b8a19f705052d9f141c8c46240d0f7b1e8690f470 b5fa64e942e037da4d39f484ad8d61da1b212ee28779837bc6f647b7f16733c7 ba387f6d68a9e6c4825e9ed82f7c5b9cc261bb7d50aacad172ae86baf9204ae5 99faa4e9097c6d96aa1cd7f0dced29c03171331a9f9d3feeb5ad8fe349825d89 5e358d1f4be52d75e09404bbdc90e0d2431200060e1a16f37a3889153ea5ed07 0611ad7b124c0d0e6bfdfa49d17307000ddeb7a55528fb7b25f015ca8dd99dd9 fbc5d86a034e97db3165947a3fe18100d099a1694dc4822791e3b4f6f07cd6e9 c4075a0c036bc008a2c23c970da6051d009e0dd71310c7e993ce03b40de50769 4738f893a73fa37a7e7a8aed248816855deb691f14ae4d03bd1484f579a43b2a 049a867b508d450b1168a286d8daf816f76ae0592b7f9ead15b65f88a926c74a de1e1534b99c20d25e5e662ee49ea53bee2670ff3e5425af90f0b0c6f7d89acd 99908815f184fe9a8d8f72fcb040046d7a787e32863111dd56bbd14216ae3a15 99f57341a1d92ac960f03a4eb908440ede4efe5def0628c7ae5c022b632c39ad 1d89cb5acce80fb7581411a5985803fcdb1476f586ee4bcaea290e38e25dd8031ce0ce1076185c517d63010797612b2d8d56c4c8571cf96287a514705a4c590d4678278e30f923d03f9621b429a2387564cdada0ed40cbc52611027420789e069eb41f0995a91a5933dae33775485454ac8aa943185ce44059f47fbd06995e0b216e48971292276349bc9145030227b54ed867412b03007073235049522ba4069cd954d975a02aee0f52ec3e8ba50afce55b68b0e9304f4519ef8091042a5b05755820ce7199bf6a55ad9657ddaa88f7c114c663901b69b53a2337bf5e6ff90dc325d274a48ce89cf7d6a7185929c07da03af8e173a1f338647c259efd7bcd0c0583bc5e33b00aad9e42da3b20e16f8bad1aa27b709ea19f3f58673ef8f4fa0e1639dba0bba8432b69a64164d21c16bd9086af77cf00b05fc60926da7be47e067eb5029fbcbfd3e9be00e9b57d0d17903657fcf178e74dd0136bf7c5e73c7103006dc44e5853a109358f00eb67e8ad1803976495b39dd338b6b00a1dc403f500818eddf3e99ea7d06e05f263807e8bea809f26932711f6d5b60b84ccd43009034d70c387d4934163d794052b294b777aa2c18c5fccd072b0472c9ca3e1107a09eb9dee79f44278c72326a65ab7d2b2cc1c87c3db3c8957a6f0a17b602973190b5594fc37d624b159407a645fc974c542b008a87610113ab21dbc5fff6baeff02a491b29c3cfb06801bce126efbb4ea4b6c31e00a0d2ce99a8a72adebfd6e5902fcd94089f439ca54861c3ca8075581528a993967e6597c9b4b0a45977cf2f7035df05bc536074a6aec935e15dbb617754348016868ea883ffe1c5101800cf307bc39c7cc0937ba45ec1c2a2fe4189c6dd6d5ec990a378410327ccd3f3c38c103fad87983e5d68364dbbbe878d0080321f0679a11dc0b3b31e90c4a0195e036082af1562b6e99eb9d49ffd553c7059246bff2841e6c801370c8bdcefb2e29de08f610e69b163a69bb405f7ea0a18cafbc4a8aa80442affca6df456a1721274507a8d24d24d4ff67acdca0a1e55a0bfcc56e384f2d9de8e3533c7b95b2db992c02aad840510f0b2b7923b4b17a22d378cb0a2807b067be7c6925bb79fe0145e60e65fc398bb47ff92218339e8e0a5ed06a1c8d6911dab4f247ecfcd3a05be5ab0c029074f9a7c6b52cd922e46137f31b40c906f428ab8a5e71b083eea672a7900539ed14fabab52a5e47d06907c7f474d85a67e9550ddfd5cd96f9c00e5aa19b00597973a94a715e809d185e98f36d50094ce9629a6192377ef2a6869b95f8720a492267689b694f98e36489f981865daca06645802cbf8a487c23a7937fb70d090fc86d5c52f26f19ffce7e73b082e3aca8077cbc05f36b0996c601a325e7f6028d2353c40fbadf2af0973eb1761ab7c1e6e83103ba49935666986921946ac60f755936d1001c6976d0a8c3bb15cf24425bc28d29c80d1546a6a3d2cc6715490abce46b5d90659bdfa299c3ba7ff8d8c057ed59e7d8b1e158c25e64804490170511ca41d9c59925cecc658ac6630e12e5d5f0e75220f129cb26c122ed39fde7011fea265b45f1fcd1d21c015ce90d954f363c3d2166d263e1b15bfaaadd59290e547abb9d3e4dfed068210877bace0dee04fe75768cded86e0eb533c834dcf908a20cd280cd22a40a5f8e9d5647bbf71528a915e0abc4b8dbb97f9bd05711770b7d9c02d805973d8b309adb2ff34d874687c48408f100a27dbbbbba0e7cdf0d070d5e672455da80d5efb9eda5642e709535fb154ae991f60b0a3974404a67640724b5a670fffdc3409f23103fb7fb845a89df064d3a6d52eb7da5297cd091a6027d401c5b7384c875ab298f1d16a9c8dda2e6cd837699cda1efb97be05909670621247f4d76199af7c564167af633877ae98b80dd7ec20b2b73cc20e75d745806f22578d52dc08bbb4dc2490a14605b3eb28ec621bc020d554c9ff659a8416803c20df9383f3b37bc9fd6f76b5d5addc0634b0e1fc9b0833fd171fe649cb51e0cdb8867dac3ff3b4cb7f9048c582593d89b0ffa307d94e4577c820403a7f5b80951b56bac109da5b0f422033065e9c0a36de1585d5fcbd8dfd4cf89526efa6a06a3a4247e1fc0c9647f2d8a7bb4f0c417d7c5e58d96f7df07520181704c77790fd8397bdd33763fa8f8cb59f9d76be1ff1df64c9587bbce3608a58d35ac68d107ace8340be872f8b17ce41673111bc977fff6a565e69d873e024166b2f3d46f0ffa89dd192abd29143b894c56367c47bb9b9d1826b35acb8865b662329f0d04048d2f4f6a2718d3ea4e780f498f450c32ff43b3e01e2c64bd1372ecdffed14302634ac0643443697b2f4856aaac9829e147a88e55602ec97b0a0573ae170c1508959fd39f4e17354c95132590de353798c30c42d1db79b9418bc9b9cde0d4790563c5c0c75a899b7c68c04330dde13c82dc3fed272101fef38412fdcb62fb550688e582a71ee2b81de356d43825b3a9039c51e7bc71607481af53f07f1b0bc409f70d57a5ecb398253c1086f8998407d7c7d70e39c8353e0e84ecf3f97b68410b2d513d7ce27ed24442710967386878b0e04bdcc663e28b16aa94a7eb040914092ba77d9cd9bf1418a4aee49291c6a7fd80e0ca6dfcda2056c66ee75ed469200d03f579367874ff7be9094b49e1509b41a5e027321560a5301e4d6e1ca717180fbfcb4bd3872acaaae168a366badc5cb5dd1f93ee17479e93a62a7ec52942970d7d736b5c318683d98af659870181c74d7e1b8698c5925b5d45db8cac85f9fd04cdc5634d867617c512e7eb84c62abdd2bcf46ff9fae66479d370e1b552527906086d6cdc5464bcb0048ae9433ddd417f92b8c36f77de80ff36ce0a8a436ed40d54397d3b9a1ab9032ad4c1b31f2a7276c550c55a22ff375ed65f0b1b239ae603c41c9b102c0f5b9a8ae04f2e2a0b52e40d61bacb5c5570a1ab52cb55a043ba0f31cd51a6e5ffb7be9ce2fa222a032cdc1fdc2a341590df6f8878a90513d24c07b379a51f00ba17ec36987933339b58d497f5fd2d4c35d5d76e43f0042c71bd0367ade3cef735efcdc432f192702af73c644c267a6e65471fe77abbed03c2e20fab30d8b11c5a795d91f0029ea00a7dfc5b7bed0643c30ca5bcb1dee3996b8f0ac2e258732b07b7ac0dc08b25805d396a13628c8b85ab4cdb9af9dc61d6ce540971ef9fb6ab83bd262e57a7079cd2f16f38ab9fcdbd3c501a9161589911a7ec053b18b19d19dd666f81c0bb4aab671452558bad057c6fad6d46db6cf609acb80e0fd116c9fbf9f6d39bcd986ea64eaac33582f891db1c37ce7c0074868677870be6f0d4fcbe44fb4ea1aff9ccf5e8d1711bdb9e12e14bf6e927d3dcd4d4b0ad0bfb5881272a4f69337acba3a125ea98f7e60e8efbead9ccb16648d3e1db80f70014773ae5eb9b30a0d626898252858865e66a1d703ee255ba1af5b7d099c03602e1eed6f9cdde9f558ad72a62475c4996b6dfa5b4a0b83983ad2f72cfc93dd904dbc757ceab91f1cf1d31ba00ca6cea19053ee86590c6b3df89f91468cb3bb505e97509460ba29ce6f00f76d10df6ae739e0cf4a8592e6c45b1ac80b52c5e7e0b38009d6c9016001631c944ebabef8ba35cd01faeefb94600a905f4daeb285a0c7fad515bfd2dfb04605e36cea51027fd6d24329e72cd9e85906d1fb2da25330cde7e7ba38ada34ad410991a59422c4466b3a196f9902a449138145465a8a910ac9d705c4148a5323228f61715297d1781bc727888d67860077eded51bff5570a29fbaeaf47ae76819bc367ca0576d3a3fcebb9ede0b1f77ac6a8251683d6000915712488ecfe2b563b8242d7a1f97d11968c3419db2e3e4cef5872853bd19504fe8788a27979fe4f81663a686c400cab2f007f359a911a4f110cfab1fc2c6e096fac2fd70c04ef7db23880a30338435b95911ce259625dd99e8b6d43281b54004e0458d028706ffca1eb1d9a8fc1497c692612bc486b105c68b17fdaf51dd80c8a60b23adc5d9f8f062a0735c1ea2d6be891fb326efa872c969cbb3199037906f339c4b5e9465826672bd2065757a4ed01739ddd5dbdee47a07573f5898d130154186045204c5936576c9d8bb30ac081821029918826e8fef9c69a09e9f53106246ea9610c1258a978cff7069e9a94113a92c5c2e1ded6504a1b1c0e7685d4034c6810d0cfcf7e74c68efb222cf05c7af9039021b8a4532366e40ca154a715004282c23fd2c15fe9619edb85d910ea9e4db1d13aee1f813b0fc8248c78d61100046c7076114b70c4e3505dbbfd9c67caa33d0f8115d92609fa1bc544e464af03b1ce06c392ecfc9cf24b669b0ba0bbd3a4b4c7071b887a9ac140b1cb7e9a500aea9c25660cba383692bf2e7d0dc2d9000fa5b0242a90346874dc41c40df02b0c2612f2beb5a1799c31c1d66a05c42b44aee67f6dbdfd13d8943ee0e1926be30611e09af7c8231210d954e13186a96d269890702a3b341764b8c35f355bca740034890acd2770e579e922a5412379fecd0f548e9beeec4abdb9595a7c7f58d909972a3a286184aad2f6928821e693e41b13c3fbe2cd2608bf2a25f11daa72560164efec14eb47c3ff7308a7e44346dfe78bacd3579073505ce74b0c1ee25f0d0b340d192b8296c0a79f136e5ea2d92e5abf533b98e32492abb24999e447dbd20cbeb269207f7a783c6c00719e48506daea75c0aa96c1ad82d7842926253becd0e7e2f851c31189640f00178d9ce97ff93085c75a35f50ab59755f3b8c04fc780d272012342bf879fdc9be34ef46822efadff35c5d2f574fd38a927291ca824807a91bc45cdd4ddb6fe6691fdec30b304b38e639fe1db962115f5a96a9ce69740915891a5e79d1589b47e0c6b333ceeec9bccd358f52f2d8499bd2d80319cebf0582d93ba8794b3225abc29024e3890850cc176bd63c028b17aa9c5988b82f2b068d2717b0058cc8784a4d5195eb0da663a6878c52907268310f63c459ccab1305c30d53c2f72cfc51a52d4ba671f5601ac429a872f867d8739190a561ecd7980fc87aa0e39a0f70af1cc6db0025ca2e91c4e1045b585ac1e0622f017eff8376038d25fbca8dc51506f60c791071972626447f704da0fdd35a6540e3219394b9011cd5cc4511e226558fae7ef2c5e64da3b5b8f68a790721b9158e4dea679c5f0e4284bff983c78a13e0f629e0bc9f47fdb659616b674f842a49aa5ece792a240d29cb74c2809803bbb425a0ebdf297236c6c594be020670c777284383a77ff30f24ed1c8be4d58162ea846b9b3bbcf1231e3fe78298dbd614bbcb0298adb5c20abc4702a6f766a6052289c8312d19629cbe428a2a3545aa743d18ac5cb80b0509fc9abbef7af573d1731c7c2cfaac5a50ecc336f2c0c48661b36bc8118a903009cfb3d7dd305664b9a62db2126120d73b4d64f29e3b059230127a3e4f300f860fa46a4ff9d3e3edc373597ff9dae19cb55215607cc63ef2eab19d2ad35948a503bf50d4f3aafa951f23ad11093f494fbe40ae4b9f833d0943aa530fdd7733d702926d031228c73d0bac3660f52379a242a9203dbe7f308f0841f386b3f888950817eb97ac6727300d5f4675fe7210d479c6723c2c4242b63cabda1a64d00f0f0e077e8da17e1fa05467c27d6745972a6171c7377ff9bad2ee4f44d28e2501b50af43671fe8accaa3717493c0a94e05eced5b44b9f9b52d24b678b3c089cd0df0ab70634e8f679ff28207716a07c1f9b76c74722562e1e223bb1f49e195d04220fe6f1041cb3385e525eefa364c8d6c0a8a990c17a27dab6908aecf1d5ce683c0448a4206705f4c4fe3d0232ed26f7c26cf8fa90501d9f63749e534d6c8d7e320bd91fc0cd76ee2f91af6959db2c15e6030f99a3dffb9ca2b345e291d415dcf30651a2a91ec337a2d19cd6fd23307e5ccc582152b50d2e38c3787fab3b6693720a9dde3c215cd4eae8cedceb0411d392fafec235e8d6d6e167851d3476e9d3d9020ab3564ce23007dbec4ce566c6e030ea0519049c8efacc15b2ab565c7f53e109d74030e0c2014985ce7728d28f244002cd365cd73e9f00cab5f73fde20a0010739f7561ef3ca77af2abe594954f9d0d53719a88225987c76b30ec29656253c0947f85ac1ad49e2ff5fbdffe207031226d791f0bb48c12ab9cf49bb060228b40d74520388565cdf19e3534825253fcd400d27f671935dff49fc11c57c98eb570467c89f7dc9009d40811ed7eb2da2555eb88ffb13999825561c45bd3b97f1e0060eaa379004ea31a0ee3db3c31099fcac1546fce8f6710e1b43f03ad3ce1711015629ccca0cf22f2c8f890df19ea6d492026bc0fbc97b301e9e9fa19fcc3a290cbe4e78affe249e7f5683fccd65f88d2d452608ba5fb32e18a2a3353b3ae1f304825c476162549cfe5607b8e59da26781da3b0b1d5b8a6c4ac73a7a2a5a9be20d85fc3119849ce297634d3edf11742579e9cb8c43187d655d9bd97877c95aea0cb78b9c263ba61c56e5d8a99e85d439af4785656292149c85f7b6bdfdacb9cd03f52df1dc3880575b01c35975a3139b4133ea0991fe15321f2fe1d461c8b83c04350e197b1d4257e124bbceb904dcb81d79c9e5daabc27dd87622ac61ca31000a21042abf7a7688f3759ad753accbf3415caa82311ca3fc31bda48d60a456a503d4f92c43fe668fd82a87e0b42a54a4e3b493c662b911fddaec1fc59ae09b480c3ce22755049d283738235b1a75ed3f0f7dd0c4d3d2dc8907180cabd794b86402c4f26f594da1ca38a4145dbf6d5570cd7535583efd9151177326d839820abf0bcfe12dcea97102a44e237dc6901558e9c02107f1306ef5a33d43192b3c7f550494a26ffc257f6872fb7420dc5db3dd1ab22b17ddf79bea24d1dfc9720914610442c36c417fff54994305f3028e4478f1f80d025e16dde7ee8019756c91bb3d01df1792945f874ee020c4b99f16c70f3502176bc0c8052451885942f03b3fe007e47409fec89fd38a6ea523685dc6f49c897b857681d97071f7df39b2a70818076ed300cf89c8ce57e5b765163f47e09f6afe6358ce4e030df0482f2cd293ca03d58af4b1d6de2074bd539966ac811eee6b94c41aa9c9224b0fefd444fc00df0221a1869cea403f2286bb0facc3282f40ddb611e6c15c576d6a2be1295152ee073d7d106440916c1f293e32603e5c28d6fe8a989d31822988a59df858d310070f95ffd05fce4199ee2e51191aa08fa8940d69cab4641785f58dd1feeb537e7302e6fc7da8d50df4303c0488d889bd0ae2d6b11ced79735a4fea2e37d61343922ec8a09bfbc4ab6054cd0e6e26fd7a9fe3976dfab333940d2c2d54d07a2000e90a986f35988a728b1066beb7c71697903579fb8aa10bf149b406bb941f8d3e8602b2452cc4225e82dace841140efb20c45cab8e270b2e31f6fc03b989d1c29ee0e2502413fba5c97a116c3c18fd5a4210e3a3aa6d1e4ac05c9f22fa4960a7e45039f003768650817c783a85f42246597ffc3e3af048e6427445be1812a0e35e80623c5ee2951d53181e052ccf9d40c91c78eb446da57a0140f32d18f1833220903cf7b7a618f11cd2cefdf88c69673d214202fafe2b1a8221068cb1be52304e40174ab87501e269d6d12db5d467e7144f834bf8cd40a8318ca975bfcbcb4a2fd04be669e48ffc01ceb1718c3a421d5309aa7ef523b22fa1890f5ac288686619f079ff769bf9c8ff56cb7dc0ffb60ca9668a4b1105d83e47fad605b0b1bed27b40a3a5afce1c0a9205b56e4d731ea00427d3d6437025ee65da90fd367f215438704a2f3a5bfeaeaee7bf355dc4c39cdbf2bd812c9879c3f7ed7ea4554446dee17012d7b183abfe861240873792c31e75baf9c5434bdc0df673b34e99ccaea90dd0e8ace3464ade0e7f9d05ee352141fb50c49cca49dd4c4e1b4a3221489e64a980472f60ff6c47397fe2e29309e68de8615bce09d19dd33a7dda57a584e022bb10e25d1833c0ff9adeddfda10f6497b2306e7d661412e308771515a9718998d8f0a37f2c59edcf77ca7219c0a5a57b90c8143592ab3a2a2bd1dcfaa8eb27eb1d00ba9713e6ea1f4be57d9ea7c2e313c0f4d8a46b8772c1d0e7f6c3e31b229637f0365ff0ac2c69eb8a1cde223872e99e57ef46bdb729f1ce27448f625a302f42a0ce857a330cd813ebc2261c87c7d9c3dd77100c2e71c02e1a5041ac5eca861190be030c2126c1de48fa3068f568e6964f2031fdd2edb5cc404fed20b1c680e3d06f425b534bd2a334812106ce02bc1c49a0a984acbb6e952e8771d3180e5407e0efe300571e6d5f07de2811493d12e65d8dbfecd6cda01a416650fc3e98eaae9008c164831d036f6a803eebd459f213fa7b3b4d8210084292ad4c99ce57c85fc0c286939670129273999e6a194c94c5866f29a00e96574812b3d8d826adb036c078123f737b5ce51f4b04c0873fb2476874344923c74406910386bc46d76dca901f7c4c9275dce3da3797ea3105ca14e19bb5bc9765a1d885bd604d83b3e3eaf0eec5411b4ab9f8e8241ed4b09f3594939d9152b8f0713452ecc33bf3d1f67910ae6e4740a773807a9e94e7de7cbd4dec87f6d4645447f99e22251ccd4285fb2017760603b49fd55f8bfde57bb55ed0623ddf0961b561b86313016b6e717446c08242259d41b1685b0cb9ce0508be6a2dc18c316a20a5ec92c9142f3134b0e8509e00a0fac02f48fc441c97cde2a3a0e6ed1aee2ea7fdf7ee24b92cc1fb966ac0b00d2b07e58ccdc49d5453e3ba665a05c8a3039d18e1a7c0308f534394974c70e82731e2ad3c4d9f6c0a7add26c8aa0265dbef1e737493b8f4cf788ac6ac3a10d55d077a668a51c2fb2e604152cd3ba24914ab8906ab280ab8d4b57420057820d9408f630ab30adb9760ed6a5841e87fce3fd58d1c1c0d6e04656723beb19d7020340f104bfdc55b1993e84d552cde18a0ade8ca776ee3bc3391efc2c67e50d055cdab78c089ebeba9b33c82ba5c9c74b8acde57eac2468040d07a42d97e49902a78a14a98617e2f83b6985afcb58cb9c5300a9e1f53d55d948759b64ce003804727f2f532424c0dc7c95931f5abc3239d079b6e128d4a8aeeca93284fbb57501ef990d5cf66b179aa9fd08e35197a2d61f52c1c648c35d8a61d037b9479e0d02c0636bce8db21333a75b60b7ac140adc350854b15e5a00d13c581b902575f20c41e4fae27a13145ca2f0d8232364f4207985d505860881ce94ab579939146508298b9204ac513261c390b5e7ac682a78684675d6fa50645f1cf6094bce61cb0b04bc967148e9f7f328456c2cd8667009203554bc782e605ec0b3fc427f145b05e4a460758da537f246f447ca0cab669b7db26de6e4ec5b53da33cc7eb6bb4b0986587661206c3ece6fdc19d9e3bae441353171f77bbe0a650df26008cb531809431ef03cfc723d868acbb2ecfbe263a782f97d986c5e0f184be3a1dc05c86c088c865feaff5a35adfbcae3966d8a67cb6768eef267dea7dc5191739aaab99d0b9956d90a3291ee1c036e8e2a19bdb48f719c1ac1e9b2d5e4b1083d5f02288d00272fbed6c057bd6cd6b4661714e57a7d83517958e197539b5626985686d31104ffcbec883c7ccbf6a680ec84ad382c69b7cc28e38576a781d97ff6dd6ad588017785e9448cb7fe1f40f4e60015bbca34226c0d4b383521f82e2fb1c2db36160f7a0fd7578537fbc172b2e549cfba061ee7c4525687547ca7e12a039812546c0bc9648f2acb73dbf6fdbd1324d09f17df997a8e82b810e4b49fa064cf70383c056b2c586da29595a0b7374775e2e578fb987e47f4585ddb0de2a93e180c28f100a13191aecfedccf3f9f026a3539515be5b6a62de1850c1d3ebf9a268f623050499385aca9513ad4afa340e47546f0d6cd09af4f7348b5ee022e811a0abe0b40b64ac23be836b9d64d053a08a1fee292380ce2b05ea0d4865780baf7cfe5c990ed356909d6f34ae7751413a49a03913a35c8eb38f64e58e0dd6a752c03236910175dee809f6978be44a63337bd8f5be6ded7beb46edc4bfd89fe7f73aef3c480f0bcc99a15feb0fed2613c4a9bb7082467198a17ab106ff27bf75b26d6f44b60374979f326be852f3ccd1503f9fa4e917e34546ab5b81663bfe1542ea30e2b30f2a6479371ff5f7be6063bc6809268e6d91985f3d0c2634cb17733b4b12940001eeea3c0cb6358f2b1ad6f7e9714e2176c13a63a6eb4f6dcdb25c29c05d26d60873ceb7cd8807790e12b1a3f1ae387cc4a5f99060fe87a75e71fba47d0625bf012df46f2603cfced823a674ef121f1374000ce836d7932eaea35c2cb7eb6315088c30272084f24677d534dc1ef89888b6adfb6629a773c8a4c29682fc7549580cec0b084c0e6224393bcb0ff64da4b16890080917d37603b128852cd6f1bd8407c72382c5ca8a36f57dd8869b11e7d8c29cb0af0d12c852fb1df0726615396405228bf7a395427dda41f9a75728ccc420d4a20823829dcdefe79c09a80747b20267980b61ade39b4fee794b0ad3070e9d0f30a642c05addf7adeb91378dee9f072aebb6ec7283d3bcb930ddd864ac6999c6469dced376ee0cbb20d89dbb7e6606b418c7d0828cf4759a952b9dbeb5ce5ad0f7a0d36e468e148300d324d83c6900ecd2819d66d35b72dc16f169652e5efee37def0d899f4f8634b7b3cb54fb180b71131fb29d43fb647af0212ba2be4acce1bcae5b6d1de9f49e0dbe683aa9fc06d085b16a0358b7376875c4b023f1fbb9c1e561d7a57894ea18441cd3924392047f4487d0c27e6406f46b30b38965fe1a94e84f439b80fd5cbf6b79a86531dc0953b78123e088f90ac1321b909a5fad9bad53c51043182f4fd6402d194929630fa398b0a01c481d649a146aeb9ec952c673809062a0b9500365a79569b5d441071bb8a2a77d6ee33af0509795684c6f1cb3a90619329421349d42b4b6172f0d0063b7acf747324534fe82f610423a20ac8971a4e39dac951a6ba1a7884d347303724b42ba1514fe57b26f9bdfd26d7359492549095b8bf075204e032b5a7fd60b82f066e5e6d7dde0ba249e493d5a4dadd3cfac2e03e5f2d2a190ef28917f3604cda10933928c5bd487ddf6ccf6d189bd9f6369f670cd5e1b942c2c02d52ef0038e3bcb2e09f60edc62a45dcb446b6ee0c615338f445e1248f31c295d2789080c18c88478295b7a9b1991ba7ab25342fac7b26b1ed9493831b4a357df52260b0b3bc9852bf5b305e686e74ead6b039a387d05b30e77b517fb308420466fe5840c0144a1fc1262e3173ec4e7ef428c0d31ddeaa52ce9036c17c00de47894c5ec0dc14760f928562abc6ed556fbeb05418ab6595466735c50921bb2961fe215da029b312c0c7e25010630e82e8eda6b44495cae77bedc035c9a2d5ededa13d6660ec592b4a2342b397c70b70750ed2d8a49a11b7f3eb08c120f886a7fc6872caa06f75df44aef69b0931794138c63231f67b85fc820bb3588a362c6437718ecd508ccd48a30efd6428d8c317ecb32d1a658fb8ffd7b4ae8a4aa090ef70d8127e6004fba03d8040b5221dddbfb06b1eda751afba61f6fe022e4834ee372330958a0d734a69c7d64855e7535e6ed8a7dd2be45e8b1e34349ea786dfff2d85f02f1a00980fa1ebf33806869a1dd02226229ea98f949529e2fdc6a601d7184e9d6cf1047520db062570dba5aad31fa02f4efb9fcfca755a69fd775af5dcc626e674fb060a9c28f7883f2b3b0a003b73f8e4493c0ec8f9b6c45b9a9d9f07ae0bc279e700b630d2ddee71c4bdb4646986295b7780c92ceb4a6749be5c6c3dd9fab179c00a9988b8445111b92980839be5efd6c88d6253836718618839a4f3d517d913330b32d5cc63c364ae74f54de60dd64872b8b097f32c7005e4645f8b78d9a0c69d075114b2e187de3d1700e1b3f5e2aa079a3a6c419bcf127ef4230d12821ade9f03ec3847cb51687358451196f8f500e10c17947c3a1947d7c2a6bd5e208434510010db36e578a5467f4b4adaa5053844b35edeeb8b60f5e394f7375b0fcf044b05ad9e19085888e127ed4f2cdaee7dcf117130cb26cf797e869f69b1af065ee3066395f3ba9a56c6c14d1266aa440ae3ac66b87a915c297509a671c71cd0489108772efb02e6ea2239d1630478f2e88bcab39334244c32038c6f6b7664692f4f0660b173f895f934b6a1b170adf9cbbda96820f7a380990cca1a77326373e086088040e970e4c1804e90037a79fcf6adad490b44edd3ca7dc4383771f6758d5a06dba2cb5b2f156be2e41eeb4d165774b0e37f1d74bbfbfeef8e3ccba2dc9ce500eeb0d22f128a6e3f9fba9d1745ce538e10ec5f613a74f1e9532d944785070b0e7416e1320a13aa615a258d788c0f3ee459d3a555ad1729264136db1a3733f109f3ffb5f5d156a7311f929192300fc8f9aaaf87743a64d514939fd271a962fa006dfebd30dd161c2c30a49394b9dd925cbd88447776fb69279660aeff4fb2b80e10b6d301d4f5e383ecd50ae9eb3d313cfd2c9e2113eb1fb0c55a790bb814f10805e094fdb7a88c04b2bfe824a91481902b9a2613ad77abd7da30c9ed2007dc093cb74632983d16199bda98645d0571f10453eda24c828aa18c73415da7bcef0c4d467ab436dbff685486338a8f68ba57cb1d7825eb3da7c92eb708f4eeb0dd0bfbd32041f0b6d1edabcaf97b4c2cea2a3704acdfabdd5b1257498a4083fb68001a838d8c12975d90672aa0f038b5a6a8f763118644f39680acabda179ceeaa014633be8c91e34d98384937c1ec12dc4a32b539666a3f6e132f9a6d1de346960436c12cee2b7a3bd03135e4434dcd6701d91630262394cce60e30903d3abc2009bd06ad98acd2370b51e5db83e6d6d4688239bc5ada572e42c880469a50619508aceb4d03e0621c055b38442f2a46cf3a853750b380f3b80299a9abbe19097d016d0561f681cd350ad96eb4521d5f44e2632d992c28b807e35b635edff8f35b0848163467866166773c3bef0a3f2c49835b7e7625a348c64041cf5989786ca709f90f8b23e4a72485f5909b82c2330b08ea2d1899d81ac51ca96bbb7f5ce27103fca58dba9da63d0e4ee358edb14bdc9814f5785c2b80ce85f75ca6261e11a80eaf8070848df182490c4041a01b77e776a92de756a3d399612f18283f7e7fb80e66ebf635b88c9fa623be99c8d38b18e61b86883df401d83710453856b69188006e1bf511429991864c8ffa1c451338e1a250bbccf1e41db5b4ce3f7578fa7109732de89814f4060a224149585212ecba4b8c25715e992a74262a8d4b4acc480880067b0f0bfd49721de942531cce7045998b34ddc6604b288beaef9dbf033a07713e6b768f2f44d6892d5e970bcf6117910d19736b135c14ba9fc723507f3a07fa5cdec5ef3faca1ef8b407fa7d4cc57fd343da0eef63a40e7cef539d881330c214395fceed5a346d33e0ca84c646796369d162afb64b92779a041bf2e61a20299083107ede309311ad376c4d90387920793b2de3ba3a7850224196e6f097602c0a968624b140a5e1438f5c117268696ea736585234ce6a0b0a1f3aa7993680763a71c8327c5c06929ed1cc81fa4c84c37150d821ea42d55f027a74f2013ba09326b75afc17fb9875db4516613cb7375db89d343621083c620af23cf72d5f2007ba4ec3a94cb20d8d97b6795a7993f4e2bce46d3f5501116dd3a93da46d06a0231b2d2562117aadbe6362cbcd0e585c565ed91c2fb32df4567d3653cdb09c802fa3e04540f236d9445560d6f08ffe056c7b95184cc1f0c109fc1bfa0eb16f803aa9afde9a830d089c02a5419bc9b7d3fd4428418a3874c2d7cfecd276bae1c0b8c74a580ae0c08a12e5f135a1a16064a34cc2a7fca6ce04fcd9f97baecfa2b0719cb9a9a1148cec946c4f916ee1a5bb2dc084459e64ee4ddc06d1d82c773f40c44abd5e83ea4bf5cda8dea905f5f16a1a529f32cbbf699eae9066439b844790f9f31de4bbd466f6887c1415508633244d5d52da45818875a3fff69c946aaee09ae9ef23a7adbc68f72335b14c531f916f8feda32fb729907ce58b3f8d9223808b38af9095a0ade582eeb5c0fb9868c6bcf913a1abc9159e66b87fcd2f7a5e700bdc8458bd01d2ac14d88a0bf73267596ca3e54f990e441baab7674f6d455400f8ce5d54e3c985cfadbe0c656eb2e34082f7bc9a94dd521bb5b58c5c3b1ffec0de4cb4c44985f99b4c38ea1e4ca03bc9b103b011bb010e7dba0b6fdb933da2307c36222ddb883ebc27ade8c2511e80f96d5aa3390becf316ecf4e9172e86a660b6f6718555dc1b94aa4d7030c14c22199de4dc625b7e9223787c1661d1962aa0d9fc95462668ef8dd3fbc7f4a4a8c3780874460d9bad71c63154f2baeacfb6a014989ba5b9994fcf168351b2d4276b625b03f3e313d3539574636decd4d9c6d0d0d1a2967973b74bc6241a4efac30419ee8c78304165962c40479ddad72e8b60f91bc11b61f732fc2b850099640672d29dc114e0d359e2cadfb225ce37b4936083c342eed7566e09c10a2319b82e7298afe596ca669e7858ee975adb4b237c202caced41c5720377dc4dc151532fc7cfbdbd498a9f46c672241cd0b892cc6da06dec5ba04df73a79f770249bc69ec84cb2178d31f56338837abbb6bcb498e090914568942e434aef0ec11fd16fd410e9801b5aecb2455392befd32ed642b2b3053fd11e5fa29e899ab6023ca622ccc2cd1b7b535f922e571ff49a5a2c579915034a69dd186d61c714c0c4ca23bc05957b55f50e17e5683ab11e927a145a65e20a056949a34bd469afa3f829f77062a395fd480eeb84d800d9987c5cb2479a240032191d296853f50b462a77bd83b772296e828b6a91ae5ff7ec4e38f372309f0148b1146b20755907b54add093bc65452a5c2f18ea7b14465cb4c8c1ff8af2b0e04a62be33dcd1ae3094fa59336c4e4fde3b413cb200793fe93bba6f1de62cd0ad39ec144b3e65cebbceaabcd84946b5eb872d85efb5604a65cd6ef8dfbacd907a6617c603e383952a457eab317fef607fee4f06499853f4eb2690fb123225d063b4b13b50c2768aa532317c60ca6a34c8afdc466ae77a88deb4d280b51bbef0c81b1e09aa871ccef204f9a59e95f2866b7a224d9c2be4c618f2cdf699b4d4d07dbe3f58856d0ad3e80541be84e5d58d630e375bdeaca24ae781de0d79b012e0f2f7f385d5fba41739eeba80b4c206febdf02e3f42f94aed9d485335ee3343b054e0e219e4c6fda1f9cb2c48d476b43c454f47ac896aaf54cbdeca847a72bc80237cfbce50534d81146c8f51806d81bad9087c96b3193f79fff6adce8e2973800732223bfd380bc60ddd16606a6219679896442fa4f9e1ff384555c70fcb9e801792d498afd4dcf322142c23d9c597fd1d15b56b1b5415b7444635233f350b00f8b542728356e981136068643e3e8f89fd8a32ac000734b8f1fa5f094ab27e1038db4a8a3b3d5d9eef2cd3e50b72ee404d0ea186b08b546df169b13b0294a3f0f8a14c67a89661ada6ee6de4530921bf8cbcf53aaac91bdbe08cd20dbd63ba605a0c78b23958689622d056f50e22783efd3eb879c371b92a12bb40047216ae9036e189f308445c0df1a83d30dae356eb0175719ef5143e2b90e7929393b775802ff9bd531a4492960698cba887c3bfffaa3d762ebc43fc89fbb20592188db54069b88f94aef42683d534979805f19d2127a05918042a79581d5bce4c37cbb0900f6572c38c8a73a2c4035995a172aee88ecbd636c60275e12e18693cb49e9ae06e2b6a7474a9f751b8fd97131f8d75bb478dc67d70b2a058409a6f3ced3ebbb045ae7245c034f512ccf5e416bc09efbcb1431752ec21ba0e116851e2537d62b035b0849451657e060b1677905f5a443e47f3090c380e64ac9d0ce6a82300f670990920c7ac0a8428bd7d8bcfa9bd6a5ffecff776e68de8e2c150f15890f24cc040060568d5516674eefd922f527b2f61a516c96e01d56433af4a968e6a5b5a8071530f1175e479571390a724a852942c63b320af3481eba463dff0e84896c460ccb9367522516d34d190858253dc2610ae2a6ad33ce2b9e15ea8b51fe1e3b1e0c1ec36dd6504f1080285ef916fa124994b817dbbbe3bfd1977a7362b9359d7b008b93b83344bcef7371966d40e702a5774499b695bc5bc94174f3b50c20d11e0946151f7094ed08119254a12a5a8eaa3dd644e8199110e88bb834da86036d310f1cd1454ad157c628b6b096b9e9b892e5da72e84c6238dac2bb9f7080a9aee30cf4c90b44eb0ffa4c97e89ce1f49c62f35f11368d90ccb5a48f9f2caf83b3210311fb56f2ff5839c61468901a248e57883fac0f682a1138f8495c9c726109dd0f45a5ea31faa4f2637d86263b191cc6f3447624557865c6be5b78ec8e38fcab08270add1feed05e7335bc7c3c186f17281c9a8f9d9c859c93ef2249ef7f58240d7ec3abe5e7f5fa1cb39056ffeb31b28be161fff02c7538f90e65e0a0b1c5be085d03a6b8e1c64bb154951645be825e878b9efb39d5a3f4d81df57c705068c40eab3a6b849a64fb0aa269363a79390381b0af640ea217af4d6a32f60e92e5570de54b36564ad27ddfe12b64793a5f959318e855d593f3ec7d7e4f00aa2f590000d03a5e70e4e610751a4e1b6a7f0ccc7d3223884c4033f51f16094d2af3cb810c23f143f89b88f7b3cc6651e2b82b1c2a2a8e27fd2c5a56d65f871976e06e7c035c60e66f56831b69f3727ca2e6e688336778c26acb4c18319c158c2356a52d09b59bf52b9624b4ed122541d7334afbdfafc714eacc92de823186c5175b95f1002bf7de449e0a093ecdcf520c6eea2a5e8a6425fe0b905727228b1c7feb6a2a08897631053741295387cfc55ba86570ff80cadba58999bf5878a2ba823516370933696bd3046d780fc8a571afafd0d9c711c2348d390c7028c1193c2343d4d20d80758461a74b8c0418c8915d716a4346a762da4aa60acf9b4a40dd1f8f38040fbea94f36d0bcac02571e74369579a5f243132772211983440cf3e44c76833a0fbdada3240b3a31e09ddbb97fe08d6d367205568a41d324a82259fb7810d9160735328ef29959d7a06f94a3313b26768e7e60a1b98dd906e14fd14ad98bf83007d61f34a597cf3301578645df2b7f5b5357ec3a549d850a0887fbd020ffe9b90ed6d0eca111c18056d97cc096349acad54c6866191c5a39e9be4736fbf930af0310d4d7911dd4d5152316e1329e78383358dff4f01342585e5b6b2f51dc0e9501b3d71469611c46a183db3e65152f4c7cdcbc9f22f2ce3132d69910410e1fc2038d9e29616ceaf2db627e630e881d696ca8a742a2da0ee8c9dcc10d6ebdb8790e2620c3d7710827b65b531cc63a3d2c58a9fe8ec8b6ac7169f01abea0dbb3730fb61e5cda699e9d11ebfa00595eb829ad31eecaa43603cb31d024fac09fe2630f9b3bf503c401c16d3f52a0b4322729bf03990a362059787313e690274b24b50fc3ceececcc19ff866c57b0b8df4b0c8f0b3c75e207136b8ba7a6be55ed203a0574dec2e1c53b0ad3037dd73dd01ade4765a2b29dfea36ba765601bd075cf630bfabc05c52b2eb8f5594ee9c1faa5979a698d037a5ffd99090af69b8eb1ced10873d4860abfff21b4b4f9ae74ab3088b254ecbf8745851db5ac9895c136fb2d022e8189cc6452ec65e38b9fc3e1ae266b7e4dc050fc282e59d74786866531ce0b80bdb9a98518207976d1aabcd5fb6cbe4214a7ce5646fcd7e5cbf6b4a9e78c0d3566bec275a663e25c4d1de5f1ecd767f429e3847678d21c060b23f16885670e00c88530ced8a250278df9d8793bcc66002491fd38184fd2e7b362ae5860430ed909b06c9a836dadfab84858132b7e9b42cec6636baeff16126bf10359df07012e04aa44f823ba58d4e50b97181e992ab1f13a2a310e2260ae59106df3e70a078d457860e0ad01fab6227a70193af26f06c5fb251d79721f50d060a52b618403a86c7fe644f6b4e38ae2f02aa356427d20e3ab061bf6adbad660ff808f7dc90f2644472a959c0900338c82d93a21f6e7093ac27a8fbfa6c4ca19865cda033c011ac76c9cdb6a8ebdd61321f24c5d2184ce97418e722d7f8a2f114216437bb804d151902527521ccadd902e8e66a09815f68055d0cae1d7ecbabd93cde7fc2a06cceb5fd6a9771180ce6c765de32942755e585a18bdd5c2df1aac616db9a5290321bc4c8f1505831521cd4716e679e75ba99f54e5509f92b80caddaa89d4f2301fa6e818a086d8ad37642f52e4ee401b3db47e2cdfb9c769258ce5c7f16f02e0bd2ee8587f16f9605edca7c2dc3f96c5e194fcc4cd01b8393abc0463bfc0444007b0546297349402dd926cd001c0828e1f1cc85379123b8c408f1148f429fc70c6e078ceae226dc24d094c815536dc98a3b01017d9356335d9e1d0aee361a90000b7337178e2f1be7cc9f72d9742eaddef1c0c7f6ee60fd2555f7c032841dd405 false -check_ring_signature 34e06ca0229a0fa63c7f2ba73df9c6ad305acfe2f4d441107f8b2ad383013ef4 9e31d96b9a50f3cf6e5f758c8e9a3d3a1e3d63012b2b013366e55eac66b60435 15 7a87bfe1d7b0a61965fdf39f2e961d6f298ad527ccd6575aec83106159c70cb1 b99a52a5a84c11b6aa31e1ed156531cd38a9fddd93fc1a540cf152c90bf15eaa 50607eb914f001827a3e2f04a9833ada4a4d3ef6acac823dc0285cec494d1193 8b1c2513644e959595da5f2e6333b209578af17775196964f7df4d8d56cbc112 b32b848774a7f6c91909e2b1cc8cc3a69cdb56a6c44b7b53f9fdd1ca755ec991 906a7529f6c0b8783f4b4da608b0eeb8e1794bc243f438938f41ec0594945b70 26654bdd00d8faa62e4619c5031e22db925e523943755a51912059dec638070a 43dfff77735e0b8cfe14db1e5b3b307fb87a65ad6a4ce785518ba9f4881ffff2 6bfb5270f0b1d5622a8450e8e84b79e2759c23ef1946f642d6d851ff0a1c4927 4618879d85feff4517a5fb215d8329cd6c2f398f922afb0831a4896df4f09917 773475f91b064c0ed13b434ac68c528fd22d287291c33ecd6cf890558052160c fbe9c0b8f4dc3ea1e0b4884299dd0c2d7cad2f71f98662b1348784214311e3c4 14f2b8ae34944362fa850cd5cf1ff477f52e0d9f721948200a1e9dc471e17cb7 d79391de3c7b51ff8a1da3b38e87e3a58c55d465d6a93d10936b6979999eefc3 b456814e7f6a52dc729ed1dd5baed02199b5e6d9b48adfd2d040284ea9834cf3 cbcd5e87b5b8a74a52d7262839b0a07dbfcb507344ff00a91e3e1caca7ce900f8de8e4b34fe00acada1996e269532c31c586969190aadba2b5c537d4ba2bcb0f1e4bb4b3a8e126671e0d03aeee47bec662141f36a863e924434993c6c439fb057665c4b34b064f7a644b69eaed7e121616b1b79dc43b1962ec219e2827f94e039e971d4994c86106caf1ab26a2efbdae5c864966aea33b625f772e66811cc402a80db89c47b7994e97ace179aba3e80c6b3dae65e517bd957d654b64bddd56074534750460b4b9a1e994464e66484a69c914fd38de8b4528130ae6e6f3370e02698a5cd29ef8ae31ceea564c3b6e4fbc343b0a9c15d09de47de62e8df9eb170840bc968496c321f5e1d4f50e61f6d2d8d69a2b7534db1ac0126303b1082a8f3f49f516e4c32fd01ac3f4e9d62d5fb7cb5ff029d5540a4141e82920e1a62a9b0439de8954784870049bf96cb5e0cfa4e862c8942656a462763ce85dbeb93b2305bf71f960cc22c3f0a2b8bee5d4e60693e6ac7b55d916b8a9c6a0c44089b6ba01b93c02aa28e3193caf09d5aed9dff01d9e554d4083117439eb475ee0528d5e0fdd68c48065dbda370328055f85efeb54fc48bd6623d77cd727b0063eb29bdf00982b452ec3e481ba12b28bfaa5e4c3e0797fa9ca2cfdc56f8ab29ee1df956c065bbd9cdb5e2a9dfaec06d705176007c2952bf0ada97d0d753a947e9b9ed658043696d9b3dd5de305def7af3d78bdffcd02cfd3e1a67b2d9a3736a4577bbd960266e1e0f2b0057980767c05fb4d3d6cc6b06f15729455aea2bf6bf6db8f263f07bd2df6c3b65c8243ce97c4a7f499728ff44ea1b0875359efe239fb29ca77cf0deb4b133c0b397379cf6a2c1a9221ca221fdc0a74c73e92cf3cc572c20e7ab20ecc6e4ec4898edbc261874b505fe7e926df3c9979d334a8cefea4cb6287e4a706dd3e5af58d782b08a04ffaadedcf30aaa9bc0f0a3f3d3ef5c7114be374b7a10edc3805db20e98446fe22a571adef7e048fccd2e8a1f7f6aaaafec5ca7d26730c1bd41eac03aa412de2af7b4629489a88033852bc9372469f28f0faa53f5ec50e6b9dd16c1b64d0a0b180740716f484ca4fd2f601ae7c2064dc06f122e9914704577f578549f7f4dd8e86cf441fe774b89709c18cf04709ca7860f6fdbd6dfd0473cea46d361397b567fadc58055aa4dc93f4d5c999be0a27793302a55784360fd0b018280b79b9445993356f03c4cd77a159d3eca3400cc477688b410a823a0c6cb0a68561431505ebc994e2f702a99eb5fe769794e911e933f5d88b1bdc0505631bfa3377a5b1d3f4e1927369d2f41ff64cddbf944116157522b4836752800b false -check_ring_signature bd9638108b3858b695404789dcec193c8b00569b771f8b0e23b64b292fbb79e4 9c5d1d4c5aa86e0353db69fc8147031e8e6c77131c880ad76b67beac8b28ae23 2 8d91156d1b11c755f14ffbca5a85b11a496e51a17c2e3cfb8a1bab0cc67803f9 547f9c840485da239604f4d72e7a16e804484399ec81824ff62be227cdf74fd2 36e0a87849884c6fa70ce6467df56439ec35f1e63795e97acc2a7f4007c9b60a736eb9629ca0875e88a178446aea232e24052ec3aa342f189bef115ed6fea40fa3b5e04feb3a647633dc41e434be6c21534ea89a05af3ca5fc231118d0d31903022d7481a54547331b1fa174f9f934718577a7cd1668571273ca1b1f3e7b4704 false -check_ring_signature b6839b1f52a7f7f3c8a2e61e59cf28da7cfe853159f43d0dc3e25a48afa27d78 639a7782af9e2cdc4dfb9173fd69d7f5fc7de038665144379c12e9406f981436 63 d2f4f701a7bb5a80a57c9544a60644e5f541d1678588d274eeb2d020a63438fa 4587ec25f723cc8571900525fc3b80c53641a67c5f146b2b2996264400211449 32fecb221e9823ffbd0799d49d734ebd192c6e924e47d991994de840f59c0f9c d6679c6130b1ee5ca9044b281a40a33bd0db707ea704572055e2c48a060b84e1 8081fcb1b2310875b87003ea1dd471062fadb0488f5c3d58f9ee2200812d35d7 f6ac16d527dc92e9c8e260365831500d39296d022822fac0390272f7c84f3e79 32bc1ce30cc1dc7e7a2913ba048ecd529a146d1898c500d74a85c25437372ab8 967a4cc007f48adb24d464d5ca23f999b857de868781c3616cb1623209a37240 61e379701a4c9c1219f694b2acb5f9b26a00999c33907618caeb035bfb78ff84 676740aecfc9e8b3e4fefef736ea80dddfb3f08ec5f49de0092cf1ca0a0c2924 e0fae1866411f51e349150ad84cb8c6f9c5d1f404b848e9fadaf1cf039a8cb51 b429f180d0c06812bd99b2cc47c7337fa54bb13b56d4d1a0bc789524a535a097 35ffdd7885ba494223512104fc0d026906608f3c0bb2f67f82c39aaa4150a5ac 201247c443361ea024408967e5569bb8afa5598fad59b2db8893b2bee576d9b3 a0717e304e10bf03a13be1a6ec4ee319d5901657056727932085a32674d28932 bf8c84766b92684ef4f80e47556bb18d41ab4ff24c8410de07334ba7788880f9 7894d16a55e3f25832deb72f2cb9408f7b49c3c116f96cb729dbf0b54d873fd9 8ffea8570e2d8b0fc11eec7145fa4efddb45c59b5aa5fd7bc52840acf89ca9fb 87c59b4d733192f85cd484772bb406650b4b0c629fd2923743f9524c1a7c2a40 e4e8d4f55fb8c14576da8bcc033c459ac07238fc40fca182d49ad94e264fa5a1 d6b475f39e48e908536000b08283e0ff215dd3983a86dc9b957d59307d56732c f976c2efb1edf41dd257e93caa1eed0eda96ea9722753b946505bb432138e05b 584d4952b50260c698c3c03aec819a6ee399f44bc5e55c4e439be6331cd33d7e d9bfae924f6f92d4969fa1088f3e45e75d4dd3c6696516d79fec15505af475d6 22fe550a3047134382bb32cc6009b27ee32368e908c0a510afda31d8bb56398b 94b9ed1adfddc28da4f896189f6726cf8ae2f192f3d53eb92d158fcf0b900c21 be9c20ef66482626543595ed4cdd9ab9edd348ebd7bfaef2134b2935179ca408 492ac3a29b9d8f1822814644b50d887904943e517babcedec8bae093c948b457 b320e4317d28aeb2c5e8c7c258c8759a3f3d426e86c0eba3c7c81432452bb84d 57993e9c004c7b49a0e24bf53bddec1a505d466e6a824666b0c2c03ca5a7c55c f732d2f4a79caaf7e5dec2882e2edc7f037525aa31a6156616c369c471d83729 d1fc58aadb42aaef12c6ae08e5699321dffedde7c5941a17c1f24d0ac6acf567 c1a353130cbdabd9483e4580d16754f4fa74694d96edafa67e61c45a1547afaf c370db2575ae0afc09b0dc2a212497568918ee1015ef519c33d420515b65128c a92e48313ef41b06b767d24db38c60bfcc3586a407414c09551f46eb35b249ca 74b86c379fedcda0566233d6e2db9d260657fb2f8857e0a14f65e29c60fb6955 20deacef9d9e67c2a90c5d80df51db91b1c8ad2d004fd08c02bc218feb72c230 b51626a915aad86c542c6a01bc19b4ea72a7f0b9b152725a5ae81294b89123af 8680a17c953c896d554f33168375f1e6407fda21dee1f3b33ae0fd8868f1af4a 676ad675da32d17b27289ce7654f61f7ec95a90ea6cc65a36e1a9d5ec235967c ce0f96c9b0c3073fd542e83b724bf5a10470ae8c815c668f67a230050eb403f8 b7cdbbc697db77974ae8183339fcadd455dab07b91171700ec492939efc2bfc6 3ba61cdad92a6ca8507ed4a9844bae6b0d78189029e72679f1197ed424e523d4 8dcd2c9d09b978856cbab8a1ee62ec4e53f15ecdd6044e1c5a68e923b3a3465a f9f14f8603c164991f359ec279265098cfcce23d1c3fdc2dae5722211a48ef37 33bae54bfa42bf112d48e96cee081d50c4ad21657d1804c6496a1575ddc1cf82 3371035ad339ef806e4fad044ff7e290f98a741ee4483f5d0490d5dc142809f5 504006bfa6a809d8387b000c7e241e3e34c0d3037a6000956b6e31dca4f8906a 3b27940b9f4a1995bbc1a49dcdb75c95b2fdc2ca6aa87a5ad680f262d2952fdd 15697ba20e56df46498d11530a518cc709205bfda3a8a859d5690520b1c97c9c fcc77894a9572bdea273dde33ef11a747db280bf259e2a2e10a37e66a4bda306 3617a7f7149580616aed00f770cd6e7ea9bc8d8db6d9cd78dcd688da40b03407 0c8cb907cb03014497693bea85bf30ca6dc7a681452379da7e8cf39a0850694b 966240c88fec6e506bd19c189a74302b8fcb53f6eb483e99b98ab5822cb79cab b55f43fa0509d094dd0f996d6019032ee4a7e48899d8d0d731b8f9fb3aefbd03 33608124ed0617e9a276e94f579a9b7ccad80fea1394ad9b723ef72c6757d2bc 9036f8ec02e242aff41432d68e2d161fd7d7d247fa49e838b4255d7c434a9346 44cc811041d1cde69f7e88ec6014930a44cdc9b406c4d9c0557bad6cd3a55e55 c9617c5e32d51d61cc1918979fc89f0c7798de41f029b53d8ea9648e76dfd945 534b5abfc421262cd4a915604bf2b38baa3019cdd285289b340b88ba5ebe2f67 3677930f7c5742086a484867fd75df40adff6013ef614bd276c2d4fcf20fee09 52509f3815457cb63469e8a4cc5650fff3fd1df1f739536bbe9a7bc34062da68 5cbe054e66426da8acd5a750e12c37444332b3a3e9cbb1a53d671a3c36620f34 1b6fb66edcd2de62e37ad382bc7e2d12f3ca55d8e4c8d92386273d77ab83490cd602e35cb71c8fa03acd9c48d39b148693839549e4bbe3a740de68edff49070c9d1bd4e0ab27f72435201a64a4d90e5df080507c818459755ceb8dff2e9c0a0114843afad83aee093608182fe51d277d5bc5aa85328143dc120c565f9e52b90401b8fb035b353a8ef73947b1fc59ca33cf5c9fbf39a5c07bd7fdeee93a65240d15dfe81091e8456c7cbbc7704b0eec4d6640126f6e516a208b257c1734dfc207d807a7f87333c094dc387394428f3d77a6c6b975537531647452c44f52a135074b32684dc7e35637ec6fe01cf62ad6bd0c06390855bad1c7ee1242fa28e7090849fc000b5aa048e28d6a53b93c2529568dfe69bac88a790c60cdea0ff03e7e0e3c1fd75a273bc7d696b63bb71f1e980da9f323e3c8dbab8110112bf0f4dd7004535033e2b730048658ca44773b872191319dc64b1583c31c455b39b389005d0b139ce7e4f767f35796a62fbe2ab9bffa646201a9cafc3d22d6fdf0311a6e4108e11995077dc9bfdf264298f7e48355a1906bfd5e9f18f1312ecae443f9ee3b05aebfe5864e737976a57a9ae0b20b5c71dcdb61878997944fd5dc2b594c6ff40a0ad9e126083717773d02d3987d75a7faaa074068de4f6e4319cb6f6bf2a3430ed4be2943e1d0547692cbc647e7505aa03ab816838a2c88e83d1cbaf37ea8a30c4278cdcebed47cae54131d952a89cc219b6ce2995b3f348f19c705a0a29d5903620da07cc8ce025849ed9697752d6d490517e87f8a7e19fabf35335e74cbdd04f28641d972e895b6ea0c8259bc67ea25d7c5ac743546695e3b4bbf1918a9c305d81d7616fe8104f7f952fb9ffcf0d061a0cbd2755f5e42ed60629461d913f803e3a37a8fc878232f889cdadb2c8b599d69ac523186abeff54d8458b7cd84210b15a47f0a1968aea673cc47c055f34135862d8dda7b0fd0dc1c3ec91213476a0c69ce00f0a1ded67020e8f789af416e8c7e1fc160cedfc7ed610ac79696a91105fd52597a68416540e0223bc4895f410ecc04103782f6d227cbf5a454b98ade0b2b3d5b88c23018ae3435d3017950249a72478db231173a103aff726cceb30305caa51640a85fd010e19d57458abad7437dc8484a8710a7b95c9989fd55cbd3038d4c4355669ba6996f16198ac6ca3002840eacfc025e696faba791e0bd99600384bb1b03049a62fbe5b89a15d6e5759ad7740c9fb8d532190afe489413fefa072dcc57197ac7f004966f8971631f2905096233db6c11ac23c23c2ea49b96800d82e622666fff8cc28b50139d0205621ac36fd8e230e55629edf69679d250d103d06e726be917303d45ae4c22a8ae755d70f8d25a289d1939cca10fa2e4ff170aa618c727f6b8f51e6050448f99c42edae13209bf766c99f3a4621882e0c71a027d68bd7ea24a671d71c113197ba970086c3922f28abbe9d1949bd65928fea909f1c81cd0a7d5cdbf42e7686b8e58a9dea2f221266ac763882c2fe9d90ffed40c43669cf2f43fdb1c4bc68036b86b520c9bd218d8712c514c7d0cf68e92891e0284f11cf0d3b87d87a001dd41672a30cbe8a4fe5c021c6062560f291bb3a35501391449f5bb9808f3343e3b26c6fa7c45442d5811126c0d97b8dbabea0a0a1807331bc4a37a3cfcc6bd3240b579d72ad575942307c40520f2be49212d5ee2f20539edfba8ce8d6b8810017757672afd74e3bb21212eed613423fec8620209da0c677226c68068aee1de0ac209d386b3a81be62c6f87ae3f17931ece910809950b59380a67ac24dc48601eccd2fe7a174559d55b2967f913005961fd6bbef42b0adf1f63faa790ace70c2e416ed1c61e4a3392b0319f5330f3a8fc4766240b8807af21888b2c42578ac4f99d5acafdb9524105dd27c3eacd6e8286e454a76da50a2b134a3cb9b7d8c05b0913f94902123b9819fa245b413238f256f8bbf57bab09a9105fb3ad2673a0d7e87ab9e398928d25bcd92da56545587050f74358818f04a81636a8284186ef04189adbf923d1333d8c72d5d2cccb6cd762a21299534709d53a41f55d512d67cec8eb62cd372b9c50193220f017c9b7d98fb919a8204200a9dd33c8fea3d5464695d47a93c834c01ef9c1cfe36969a7e675209aa4af5f01f4be639470eaff0fe1e023e77b3651341956e31af712c1bd5de774e618c3d505886d85b8f6d39e784da1f4bcc386f324ca39bc7ba4f26b11707d5abed08696059ddf25ab7d64dc853d654eb0be3a176b3f85d354487f8e6c2d5bdfd29c678608d8d57a5d860e8fc328aac81ecf49784d4d188781fc28e29249ac070337adc20f2ea4a7c56f4167bcb6273e4c7c2bbb67bbe17ee27dd7f3fed50406263c8c0c0996750ff674bdcd5f70f424b61190a9ba7aeac60acae14d3d88fd38d51c03970e9d16fe0112338d26a344ba38a52112e310a457936ab75ae37569c5fa1396b70f1d42d040af63ea2336382b73e909367d0955b13309fd5f013bd6a03ea7bb5b0db251f2caee85b7fd624a8d3c905a79261667c1d242b442af87a2b00c6b79a006fde1409cddcd07b134a64989caeb08665c698632fa6d48b5f55395e08c022009621815ac6b9efe04e5c1d429e5ef83ab629ed1ea9cbd7da870d305bc0057450db7bd026ccc2428af6b2cf52e66c4ac744043556aa80e6d14d93b7bf227a1b50f05f4a62170b7c9adf2ea5557f53f3c8c0df99b9b0886bab139e161f5b8847e061dd45099fe71419d7ecfb1ce63e225d53fc1a1af2eb258836db2e26d2fb64e07f3dbbd77d6033940d19c690e2fd35eff571da03d238d903fb07ed1a4cd9920086ce7f034a78a30e45ba26f17fdb36568302cfcd60b0ead4c3dfe41174f5eaa0c06e1ff4c4d1edfd02e2ca589df2af36c264fd2ed24060abb2c27d50b219a4c0d9e8ebf6f777ddf2a08f71ae5bf9a6db92c757d369983923ffc8cf5abbe029109793be4db593098d465643748c748f0409ffb8adf2447b201517b8a70c6cf34033602b31dc2931955209b2f88bcbe49ac4654b96fb887d0046a98c13b53e3dd00470ddcf65d2b2cbd68b4c41c77939177a1ce9114fae8b3875028ebddab8fcc01b54579233a1ec36c6f62c691c1e44a12b87d60f0d0659f8affe534dc64434d0a401a7d6154b07587d718f4962062042ede8384d5c206fd9afe125af2100b990bddbcef990f3884f758e5b2171f7e936728856889a71d42c17595c94fe8dfaa08ff94ee4c1dc93c00d2addcaee9ec2ee955549877a17879f5af9e19236c97f002e7dc94e2513031830b384ecdd6599fc9a9de25e40f557cec2f52cd15f2292b09f4af0d6aaf008e95e2e40cdd35893d32ff43299f9bd61ce0d6dae002e3520d02ee8205eca67c337fccdf2dc5b37355e5c6d36743c6dc64bb70ca4643c12416016f44f52d94af79a3e1d4aacfeb2ba963818229f41eb5c15ca325f5b76df9c8015a686a1c6db837e522175e301c67a17dc27982af95a3d54cc371af4a9fbab80abd1670a5fa00f1472da29d1c7ee3f7903d06f2ccbc7cb5165332cfca4dcd2a05b01457b97670531ec303bb086505350fcd44c3077775ea6a91cb9b2f52c693079b0b1045459348b5de867173e04f716e9760336838e0fdd5a4a544fc78937e0eb5489a23ab658054d52770121d97175e5330e075e98c84f326cdf280a3b5d50531711e870a540a16160b91c3cfa6a87b64a1bea57afc641bc7fae1eccd6aff077f9edf650ce7497c27cd82ad48bd03d24c5db9719161f11cd7ada65a38e1a20d302d18e32ada5b91fae3bab2f61b285cf04b426580c02b459e2d9d595e06e80db0c467668e90f8d55a1faf317a7eca9b86ab232adab7e043cde811cbf947af02f63bcd7eb18d3c01ac20757a42fb7afa9c3ff1ab91695e82b24db3607549f709ce63336fd6f5455cd0c65ff0c1e73651e07bef5018fd53b5eb4ca8b983e9b5021fa5052233556d4845268eca1060c95493368f5a233450669158e755c0cb6301c31575476c895f18b090bba6f0a03fb0e0bc12214a8ae956b2c3466627d7750901ec409e99fe05c2d20e17105e528a8f6939b833a719643525f25a7cce0efb052b703785989d47ad7209a1cdb9b4421f31df1371414c482c34bef1a8809bbe01c31d1ed77ae63ffe8e65ff447209686dedfe26bbb93630f2b766983fe799800d656b73e897ea2a7191f84798cd491137b16022b0b6e95670a58ba64cbe89b5048f26d07d41e53e2feb243bf76091002248656c8f2d7d86aeac0bf6312c0c3a0ee41628ebdc859014ecd48da3fcc2d20916fcd4d7d8abe0943a657efb106f5908188d09be9ea478f2f97d91e6f40738dfaf2d0cba76e88ccd761861c2da71eb0033480aed0c40a3f8baf5c068cb42ca3718d57572e66b5ef8d48ff4ad7183b40986fb9e55db7b3d94009fa67187b5536d7369aa5b5b0826b6e0c563a2fbb0a90781c63cc8ae5e408e6cf965c0950dcda3f5684348b88e577c33362affddd66b0bff91e09a9ab1d55f93f2ada510029cbc76198c2d4f66403a4290812e7869f70f98f363887b8be085fef6459adad22e03dd29b6230e3f640e15f5fc2329bce50f915c21590bc70929b8543b0ae88628cb56a2f6f9c75b2a467f5a34f2d28e3c069ef2d2954fc6d6bf0efb52db6e4059b819a05bf3f9f0a1ea27c4efabf0d8fb0c25bf4e5736d8610d5a69582894db97c26e0ffd05cc14524a38a0e3986055d30f0adc4b77be18e6eab73c887a7339715f9ea4b456f4602d7291311e6bac553d0a6b2fe713c3fa358ce8eead0cf8cb50cdd5e37daa9700e15a73f3ffb2ad059102fae616c22d5e5ed2a0c7f93a3077c7d1d77dc9dea7d39ce0c0a5322f8026f5027a53eef210ffbc6b40adf1868ecd6730893039e38a36394301a12a65f763c30288cdf9d25902b4da7f84ea42daa4b8cb9e2553cd3d869d865c5d3a82685b1208563216f94e572279462d3ebded56ffa61f5d8293bf87a74a52e4b516a82e6106684eddd77f989526955ff8ebfddbd63d64b024e16f5e96c174fe8c28b5a1d80e159e9f89f3c747e5aa19fc0e4d055a46a9a48aeb21c07654641d3e095da07f0c528ef3682c5e74badac7effb006d9020df3c780ac1f4e63a95aa24fbd199670ef1a4a7116cd21dba20158c38a7faa5392a8cd6d42691585a55fb49f7d635eb02aa853a79d439d281229b88ca7d763b28eea51d2662e6ba1b5b9cecd8f805ad00ec662773bea98cc35eff6f7cbf6ac3e888f9a9a792fe1389403d40e30841d80efea73dd8504c365d56f5abf6b3460ba82ede071f1915070c0717ed30a49867051b519847202f2cca954761d39530447a7afa3b2c9db3175457549faedf85290d32ba66c40b2d0a376a693ca048b9d16668793bb9c10ac4c9a4e8b91d3a3e7e05d1e9c7bf4785420c826c1e4060653e8e21ee57dabb57d03800561c70ae5cb300fe4340d5cd85f7f1e6b640ba48b284c84796d06467f64d504765f5b617be080b1d6515282c755a94ce32bdd2bdc8d35e244ceaa12aba4fb28ec578da961ae400cf7047f4ba7b334704f620106478885650a3b1bca5ff6c57293bef2c55af060781fc956b36a14564621d21f952191d99384e65bde29e3507f1217ef94ae2940552190bbe5fa56ee38aec4bb7d6a050de164c8014a904081f72be5caac132360d true -check_ring_signature 5e2a858810da111ae21ab5ce43e837b132a2c06db68a097260c762508376fc6c cd3fbec61a0e294636326e680d1fe1a060a7ff9b64d7eaa1f3f5eaf9ec0f9e40 15 986c82f05d514aa3164e0dcae6803774d55d0a5b8e7dac9ba74042defd3db42e c80ea57c660153fe3cf8067c150bea28cbb39545ceaa094fce2af6be299e55db 82b82ab4be9f53f9637c906a62fd4b962f49cdd70d2c9e81b2b7af5b1843a0f7 5b1e044fba4ec30aa05d460af651b38996f1540e812b073c2e874c75503ce7b6 2782a6f914cc183d1ab5c618a575d85909bfa952668400bb1642664062f1fe71 066da74e2968522dae74f328a4b97d767c636034e9fdac5411390998d6a52f9b 352c3ce4ebd238351acb54c1c79ad46bc93803ef5120734362c867c51783a1d6 6785783b476f6efc907a9db077eb57d220760332a7a685010f5f3fafd613812f 447db2fae2009b8928a12aa4da8e80a6fd3fc7be6bf573faa7f724f5c46be303 97b96575bf0db132b0b0b9ed755140e3ec97ccc6f158fb41efb38652b57eaff3 3982a459867fa63502a50963470befa8d6f7fa2714388d32ce77df54d6338e22 0e9ab7e1a6b7e7c17fdeeadc87f27005980a8c4aacaf206d95ae412c51573c91 b0664ae779685ae89fdcde71c03571edf228bd80a907f1f5fa3589d5f5ead8b1 eb32453cb34af4725d0137effdf92324eee0c2ae623395ae907928bca04f05cf f020aa84316ea283beaa26831ec7b84e57931b191fa2919e237324d18cd5e240 782442779f603b2af95605dd9da130dc9f35ae1f5f3aac0f68175f8b4bed3a09a3693feaa48316dfea28b92744fbc9160ae4c753fde612da4fe02181be7d9c0d7241b3f381fbe3062a774772bb17e54e43a863f3fa06fe917192e7299fda630463197a31deb8a9d26ca2b3ebab6013a43bfee082223d887db9aa690e2763b300540ceae488d601629f393aca17606de8b0a19a7fbdbcbc7dbeff3a579816d6059b4cce3dee62b67320bac04a2ac70f3f14104375e98e2c025fa7ddbe46703b0494ba1eaaf077215cda0738ef3629c4d853dd4433c9ce496d3b24969efa567109bcbf495268e26bcedb015697e451ab7b5ec40d0cb8873f0da6c922a2783c5d02413936a3e42c974705f4f4882ea6e5cc4b403d30bf5214ce49c25bb492a9cf0f0f7378abde15a7e517ddbd73e19f837d6ba8b9a43b8203d235b6508e9a519f095d7e0ce14b71142ec630a77a21035dc6741d25b60d40b4b71bfe78344a7ca9002ed5b894ae09082ed0ecbd7defb0afc54681bd1840bf27febf01d8f523ffda0407759be7e50fa88de9714141d8a71ac94f77f33cd80043c51a9ed1b8481e990e12156ee9ed1c3b7687521e5ab9c044d94eba9b6de457c6cae7521751f049d90965164eb837d44ee409cb05e7f406a3917524a1bc9a9526f5b13f09f5c723620a3bf13f7f4fc90741f1275e5a43cb78a53f93921a67de66dd58a0c9fc9e9cbe0b8191f402e59a66548affde0708c0a58ce2d7dedfb2a6784c3997668b1d1e8206591c0b573c6352a1f8b958e04a264ec4383f071de0444a8d9f365b130931e90a014b81fb48e6a73b92e9ef07542b5fba071d7849016c1ac923b520eae2ebe504af4d1b35dc30aaff2676a25ee40e974bf16a2798aa9398baaecbaa80e613d309d6c6f921eb482c248ae9156195db3f04454948610d912e770eeffea55919bb021cf3cf89c05d93fe005761ccf8691179ddb74289af8ec03381ca7e29b9374d0ba122fe49d34491bf8246ecfcbb1fef599e90ce0409508590d15f3519d34cb7085250b8d9d7436f23a69d66e6176396d1a4a44b4b4390f2eb97b9eb46fa87ee043273b66fae6f6a570671316534f7a0ae263d07cfacb2732d3bc86cc1036a10074f16d4405755c717a9f9efd77c4665ee7dbff725e07dbedf3a5ea6f8e87ad204816302b60fcc8668a683b7fea3026a6f81a3b6e1291aa1f6035b377bfbae6501bf9f1f85a2dd73b91eedc1264dbb2089be3428c6b8932983083d4f97bfc23105249ed60f394334315723a681f5b3825236094449a6a23f987213b37e6e050100f3e7375abe1274096300581d9ad6cd93fa13c264e7590b483d78149729653c08 false -check_ring_signature 1b25810017988ae5601209cd5e45d6b3034c103a18d62ff1aaeb458ee5996e5c 83db2cb516085edfde9079ac05480535058bcb258306367fc741a51bc7ff7888 3 4c86489998ecc0362e631fd12f9599ebd9914e11ab26bcce63b89f375fb55abe cd47462eb2530eb30225a65fcb4391ba2b44a5861d355ccaa9a9f2445e06fa7d 408e29c2b5ffd88e957a5664d09498f4f844495a675fdc2e792d00ecb3fb0593 87c713dd83613ab067221c16012d88081f8629be99c973aa35c5aaad431f8b0a6875e2f57cac488db083f9d6bdd5598d0b90f13d105d42b3f0345dc8f681070438f32eec2f11bb4f1832c11d69180d98b2063f1739a14f202bcb776820021d08d7dcf4260a6d9ab80949d199f9e010d7331e510ae6717693cdec3c074bbbd60028477ba6a3c4ad8023ef3efb9479d0bfbed7be94925f8c7147a3e326a4d23f05d70b34b2c380e2e75bb6cedd8f2ac84cc624a4fe6ba9117397b3077ccef7ff0c false -check_ring_signature 22335027ddf7905b19af303f1d09e6fdcde2fe8da7e8ee1200a2af16e75b3531 8c0f1a6d89877c0939331154890278aac9f45ca4a7a2846f6946fd9ad17bbc22 4 c5452d00cbf96d0e7151d02d54cc44bef4a5b5587fba1b23ed8775a0bf7d5ded 4faebdeb7501a30c484cec1bc1e709a7c34cb67b66cdde6d09a4d51b2dedf9b9 fc8bea233e0492c9d3a3eb422103c3a640b55c36d9908337e46679f5bd631935 710c229c1963460172da6196d6a24c0a1cce422c3df616dc28bcbe3763048130 6c83fa4c4f270170f485140cbe192d95d22f77f9749a197b15051df3c2512a0996c4d841ef1e1ae293403c06f6fbc79188c9bf9a2cb8349b8bbd33f874bdae0cdacae24bb95b73dd3e369f8c5a5ea1be2b6b83c789e0ac5d289f9de7e9a6d40eb6a26e6537d0aff4c09c8684ddf98bb69f53922e1136abcf31e887cd92c6cf04b197f8a72e3443bbbe5513e468ea3df7bea2338f8ebb0ed5b33e27647bf4540f85732bae4ffe8dd6955e9b47a3c31da44de4906a26ce33c2af4a18482855860e166982acc8ba966818a4b6cf3e4126ea6e48b742841a47e47ffcfba2608a6e0800764b84f43667fe4e6d6833645b7cf863ed6c3b63230832fad5ca6b88d29c09 false -check_ring_signature d117a942dc61e07cfd24e1f5376626e6f8dfe163bc73646759cda910a13fcaea 219666ae29bae64cda9eb430d427b5e51d0f269478703510a2dd19e0385a1ac9 6 1d313a594465c94fc51933ede079276c5bd5edc79c18ee96e690a88d44ede793 bd71250c03e0475e7d1ac04539a4aca35def74a88927e4bd7a4e46f2dc5d4f05 fb203c275ac072e341a86841604fcb2938d23ce8b8f92081ccd7546a3e983ca8 00e653e51ae7e8be867bcc83cff8ae9ed4095ac3495ce4cb223d4401eb5f48d6 32bfc3de0074b898bf20291b807ce9f0845de654c566b526f244d30a6f18aa4f 8f8ed3d87c59fbf0f7afa741333051c46876a46a1a395403f94110f01a371758 89aedee24e7a5786ed4ff076134246c30a9301ed7ff6052209632bc30f7ffc07de1e406e9644e37a87be0ffdd5b79307276c06b9d5e7166feae229167b061e0edc9ea366c291fc08e58c82e639fb5a9ec82f37be7cc4008ee51a25ed2683800c65469cc357bfcb8dc4dc604c5ff2739a55d0fc11d77fd450d53bc0135e3fa2026619746f130c6b21cb519874bfcca55fbdcb3fce1e0288f082072eff2c5ad10df1534bb8f2d2f0d179020f8ec18d7fb5b80668f626b5737a0f64924f6ec4e30393efeb831e565650ec22d1b44cd81e5ab67092dc0f8c45e4cdcaa17ff7f9a002e250e3ef124c35af082b68dcc70312e94b592c47a0dc0a5a5d74409fb7318c008246fdd1a877815fd597b02d1d813f0de12800baea16d652f554942215ec5309894bb713e937f64b43526dbc593efce830d2a987533a8ecbf8871171961e2b06b1395a3ff39a2eb1a047f0dd7c0282e1b3bcdbb88f68cc5cda3b3949c24e7a02b467b8ae81eda36abeaed0a5e4b31529a3141f1df8b82edc51c1292e01093407 false -check_ring_signature 89b658e4e4b688c81b04b59b96597ffcb20591d03db8c806bfbc534f2a8c3aaf 8b2ad5e682aa1a14fba182f46c84416e4e8b3eaa7f5a179f977d2a8a09e426c7 1 f6496a8c0d11a5be6ac497ba19dce6445032a17ea404b526a6d2b3beb6953b84 49c9aa923d8f8168d6ed487b1464f75447df5720a444bcf95ba38da62fd4c40c82cd193153ace0f5555a927476ff2d91b17458c00ae03030805c0d3f999fc306 false -check_ring_signature eceeb219426d267cde10835df1e60e1d5778a8d72348eb917712daee9680b1bc 68a2410f6b135ed738fbc02b343d1485a8d3c71eca33f068130ecc475c832a01 12 606defe655a045387958c80031520a74d994adff74aa1937b85d3e3a6418c69b bd2733e6126402cb308fca53411c911d8f8736770e0565ec92db92d1eaab4d69 f1fe14e33415d5ef2624e00dd9d37a9e53b6f436ae4cf30633d825d4544d3a35 7dce1d1750800dfcd4f0c912411a2008b76b8c427e9e987343c066466408571e 7914b16bb0df58225d43a836eef68dae1c02759d37ce1d2664bf16c185b420e6 6f0afb9d986a6ce94b4f8c45e91f939d9dd2e8321e58e15782b524b405ffc242 1f7139e827a9f7c0a7beb996c6c86483d603acca8a6d315d34ac791df5ab66c4 9e1e85157b57fd7dd4ec4317adedb3bcf95f0c54c90cabde57289dbecfd65f03 fcf00118b438cbed0de0ec57c2e7f147ac0f5ab2b92b292591fd57a8899921bb 004be59b560abc3882585299536f3e046e45aab5774f647a2df5d3fa9dbff06e 090e73e7e3d1a6309e93211d8646ff0af5f484907365c0e7ae839d9364ac498e 72b5778473cf32396f99dc711c389f16b8611622f2917dde8b9a91b5aa2e3c03 4aec01a917acd6591ee476c40fd56d848a0959a8bdf9dac0614e318875cc4406489a5629e794a24e7f171b0c7835c0665959bcb8e63527fb42cba1eb7d75e80886bd43b377ace8a17559edb50dbc3c1390a5f3fa5550cdfdc35c85566648900d2ec3d401f5dd6824145057464db898d0d429371e4413d46f910c2bb52b702702c621a60acb5c23f99438d11eb3e067e5ed1f288dec5902b4fe0b0f3f8f3d12010202b5e45a2e20cd312e8da3abdebcab5b537f6418a8bfa6e0e6ae11b267b00c29d937ce86bca0e52d62925fca5bb46bd1e5d1efe22f60038ebaae049591df05abd8df47780a43baf313da2dca65a30b6913952601b4d6ffc8dab2adb137b106dfcd389705f176376cd40cb6a0a4a6b02719ac4d5a9c74236fc763c39ba8890acc0ad200f8b0a9892f092f3fe9b6daeec09aac507dcc84327272facf8098b508ac1815fc93626d3b94485f833545cbdb93445c3b0008f851eecf74950c07be0bb9c38acfcb711d6b412fd043b6a9b1eefff61d6f3248f6a4160af51cbc494301fcad95fd9ad8c99ae7a81f46ad8be07b3030f25b6c04510abd7463cde773650989aff2d133f00c2aa5d029b58033280f40bc9fd4ead2b513cd7bdcbd7497a60ea51c5b3a8628f74ca87a21c3cbfce966098f9dfe898a3baa7caccbb41e08120eb08da12811f16a09d9856fb225d822b05cec5337328e963a49b23a95f1cc0601de5957f97e79f0d9853b0240140d09971d29c9a19cf56bf1dd095940147557023a3df858c1ca7ef98117d485ec2042f4577e6b2c05d00fb9bbc37b69ce42c90f9f4ec3061182d7ce8a512fc3ab4493fcb835580dcecc7e238d839e3d63e9ef0651a09f727a61c2166c4ef77c206fd6f2e80c54e06418130a5dacdba18f04b3088691f590285e00362d4e55da7bd1f6d0a8af9bd1fdc47484a1a766b5439e7a0c161ee33f84e691df6f173eb6620219eec45b97b3f4fe9c95a7995c6b82966602fe5a8f4d59945c533601a89956b19cb1ce141be9499f17813696e93904f7d90304357740afee7dbefbc1874c00fc364a5b175d8c1c319e2cda8c40cb6a5f6f02 false -check_ring_signature cd1f44c4e9c88a3f230a6ffbde170061f70d1d4764da591cf8ad082c72203364 4ce765c5a4ba4ea109033b7d81d5379c562f532f0a0db06f759b9bee92a707ee 7 689161627d2df722562558afc90099627139d2e0e6a3ba49f0f2520b103fb4d4 c30fabce09391a0c6eae748eebb6bc14367752298c347363f4b167107ee4353e aacc5ddbf8781e083b3b17e35dc8324dec0746e2f0a40e77ced8c638be1610d0 928ea4c9d2f7a149b89d59322e7f0fcdd54234a6f92ab503565f78b45bb21db3 1c2ed53ba24d3e027ad5d17a042378cb8279471ef0de431e5a83a4f499becb52 25a7edce76f0c18ee9de5fbe37c49a8b51525dc64d4587c0aa6e8949256b5a92 692a809f4d5c8f8449bb068d46e39c57342a82382b2194134c0a61ccc12cdbf0 7e0f33c9bfd73a8cea15e0364c595d2272edd4860567868cde75bdc222fd330b2b848c7a481241540ea8892c56adac8cf3c4593801aaac9b5ae0877abd241b035639907f5af14c28e6da5bd9c35c5ce6fd008876af20836a8951d8416562160f5792d77a8056c0d2106d3dbeecaedc21257a664e311afc5638ef1c0746378502068974dccfa712e253a4cc6f6390d7d7c7725d34c313ef6f5c5978b91a7f2005638dda4402c536c678dec8393f4cd9af7d212127f6a157739961fdbd006ec2096d1da6f7e907fa5fb6b290094cf9948b31dadc1df6e02967e173900e7cfa310abbbdd6c8097c9c6001b0038a655ac229b68d0f2301ba8cf714cee104ea691b0d3cb0b36a2486b976d511db8577ef07e66c600a7361dd5599a7709bafe4264000e31fb4f9a2a2a58f56decc1f1430f90f0c6b32f8ad23574aa7a8dfc0f2d3710f40f5b4bf65f0d6de08bd3a024d7944aae74f98660abc8e8ab836d698dd6742653daffd60d2f537e5258897c66eb53c542b6a13d128a6291aec52eab365bae303478fc37e6d9352a446d9bbd8b00f6a8f4ba3845c6457d42d06a589ac1b1d49075643c675bd8eefd76e1ea00083d0ba7bce5481e371da8831bfd65ab044fe8705 false -check_ring_signature 0c9b7806661df154019b7958a2af329d0a2131aec8846593c066209dd0492ebd 909e70180cb35865b601669749f7531a44c49cdf04ef740b093e4434a0b89832 126 149e3648252fbda3278e03c992cb823641bea586cac483f7377e647b2a84c20e e1cf0c0b62830e3b364a11e3f8c5394459bc072e7838f4d2bf3106aa3d1dc8d4 cc31efe4d4032c09a4ee88ecbb1662fa053d730f1b38dc5a9d02f085290b31a0 5aeae963d593fff38bb791389281e34ed40c8f658655f814ac4936c5a0f9c18e 85046b6f723bfbd65d7d38f0078f11b253c55ce1a46715913179f73e2dfa24e4 c7134b35ff3345121f70c9a2ff46b678c3379614cc5a33a2bcba7941c2062e1b 988ee641b18cde9a7796681640733b297192211f9797656ec190136fc195321a d4912c85b1ae28bf6454caf6f2143b05e2f6606d5af45573103ced03438b0c90 c22c3a6171d0f4b2080544e24141dd521baa383a0543340cba4435e816034afe 63f9cf6dfdb62f85a347d963d7cad9297c9dbd721c64be9a0650043bd10b6ea4 31be27a70114ff91b988af85475e86e7c8bf1a07e8bdc7f22f4fb3b182598ba9 67f684b2e850891d07aaed392b9959b97b8a15c6d013553c27e474aa2f711616 7fad45220d9c370a3206328da50fd19b9ffcf018a0a85bcaa807bf576cc1186e df1d45a578cde8f1f0eb24fb47af65eb3fb0d96a0c28fbc3a3b66de1bb1769a4 b1a81e42158364fb78ae01c6df70093d63a879419686867807a5f5dd2bf1cf0e 92925321afb0678f2fa092b40cd648a2b80ec40903185f345435bceec6277767 b3d66010fec754d8307182caffb687b7ee7ade0d4c482b3b32017da63753e992 180fa7cbb80ab4c315636d2895d0bd278dd6e5290b498e34156fb85d3de062df 1883bdd457eeaaeb6b9d3cd4c333cf5c3a0498313f07457b3249c85e88dadc58 f114511d8e9652571fd49d3e3f69c4f51c5c1d2753b74b74f64e3162856bcf85 99e0a5cfee1a0661294074fc268234f25ebb5ca5bdd236b88ace8750e0b58d1f bfed1644bd16bc8b9f9390381bb2de8709f66c527029aaa2eb37e7220ed5262e 3d80ee92b909b6861e3ef2b4e62367e77592c0d55168a237fb057c228e3ce49d be9085d2d2bbeed0d2fd154f01d18da594a16833cf9d295565ff11e745bbc721 7cb871e120cfa5f883e923eb1562b27ea1e5d905bc98026563ea3619d4f6e31b 6700d8a97a0df4f88725cee98afa2a25760c6d73476a12d4bdab88b5ccad0422 29ee169e421a5f8c6637837bf6b0c8ae406c92b957899cd11d39ddfa93517c96 53270c8696759feb538e9a0d3558ad32365acb6bf7be5d1bb7052317208a98ba cc140ee06acd3c61d658cf38aa3da5c0bf19d2bda7eda56062b842eaee973b61 bc996a294d1301aaa6c2fe3cce1e3267e0a33b3e4c4bb136bf6bb8f17f2fc162 9cce7442755056a16edd5668b7f2597fa01674c04dc428dc147909614e22e878 61955989972432e7e0c1d462729da70d321e498d637dd2de835a8d73deea9947 7c722cd2ed228efa432747788cc504d648655b43319f45f66205a890a639b3d1 eba0945051b79496eee9a441243505c4d6cd0da40dc8c0b523cf1241ca24d578 13b57339c067043b48f29179efef65c2568f383b1af180f16e6efe7df77cb6c9 22cf900dce346324cfb761ac215ae8d17b95ba9c900fa95e79a0cc5c340d48f2 11529a90a2b0bf5f75746f85eb3021582fb7513062c45de69bc610694dcc2b8b c9597692207c8af4909b380cc73c95fc0bde8f681af9d69eb2d410dad127f4de 9921eec94b8933930e08c5fbc6f986529d2281d5b96c811ae7f697e571e7d443 543f27a994444ef778fd25ea5ec6911f23244d56b89cebc623e3417fb67fd0cd 57306a231a35121c69aab35e570dcda365874d15103e4007c7521a647813ba00 e7bbe5b7bbb14c4bef10c65616ae8e2203bb84ed9fe0c3e3ad2cbee9eb97adaa c2272ccf5c1efc05a73324288a7b3c62839cb6a3e82d39d9c85926e14159b7cd ff579899a93f07db0cd70f3012164bdbfb99f7b8ecb1e8a52ca14005743e7c69 5dafbf20129e03172ab96c418bc1bf97f80aef19f58e4a78c8d18608eca7756d 1b7f3ce38fe1300ae183f79bdbd02bc2ed38c7414571d689657ccd4b26c64763 08e541a1326d27c2a4478e7367c4fe687f1b88b39e15945f81d1c454a7e1722d 5517cbdf73a5bec89a0104c5fc5c52c0483c6739c7647540ee967955a35bece3 0214252efa150473625f08114afe6ab721267e095f98624ea76ba3844fa5b743 f47e3ded9c689f29f8df25c14bda0e5296312e8e489d55c56f2a1c457ef34e95 4aafe2c5ebc57e1e3d386a55302bfa86de8f15dd157859773f249c0084cc5969 7c2f029e97d5cba8eebd0b5933076b06e95ae4077748e2f725d0138ec5be195b bcdc9b0b999d1a523eb9d4b3c3352fa124238ac088591f11d59462134a34ca69 ce1973ebb6f9013d75f794d8640b2044929bb44aebc15d11ca1c2d656561b6a8 b28d90b201fd9a8e8de985cdfac00051954bd4a906a27d85b454bf61c29fa202 f8f682e4fed6d5f5b5fc754db18830afc4d5872e387df96729a75b1828e19c0e d1ed964677101c79bbd827624c7cc09c11e075db6bf413ab46087e8454b4d5ef aa89d42ced0d0d2d65bdd81042d576b5b8c8324b2e34a5ce0c07d022b07327ec f5c6d9ec255672671af7476790a6bca9c62c62b8417919a6661db91302627fcc 09bd2fd0930f86471c20773383330b4f9e146e1e8aaa3a76176b958c87f85b8d adda604c9cd8a692c863ee57264492daa05b2ab05f8ca3c855f54b29b0046ad5 fbf6fedc6fa44f71bf4a83d23b35da89dd49849a32f7d983dd2ac9dd3acb64b8 fa9e74fe5e2c4b8f4d747f5b571e1d86ee8f7ba7c14364835b217f5c056e3cb6 cfa3d6b2e93b23a2d30d1f70f7cc3a53889c444321c56914f40ff486261838fb 7ac33b26f5932e04df1b84f9314e88a78cd323d491535b3b9a0770c58c413651 758adf532995d42fc01d348e92972a92663f542cbd48488ed6ac743f9c6c5478 f32cb5803f48cd834ff6a78d3aa73d6623a6a7554ff1275476fd43ce5e53c239 13e40b5f313c15c674cb01c386b45914d745100c570dfae05e1cec7adce0eb2b 1bd6b40824e8b76f9465c536ec6ea859f381b1afd488f0e766d986ff9519b334 2cab41d15bb6426787d90df633d1f22e5da3522d303296b2d490821539894c25 9a967364c158267d87daf2fe8f86c8286938a40d167b3ca9a2eabf1d959a5647 6a0bae93bef625bc4c33d376a3f9b4d64c048ff031694f5da3ad7071569831d1 b2e03c660f00eb8694173b5f7c22b3d25d0ce6353668406aa8d4134909d6be7f c7cd78e4ef6d90747df283b6628fb6db3b6dd172ab147f6762f75ca903574b69 c0589b1f773976bd711bca5629981575ee2ddab5642274b8b5ae345009be3e65 56d84f473861388e633fd2718a3adebcca1e36bbbac78f2415592b701b13e61d cbb8ac9cbffe150ba69b30bb92d5ce1af48c59f6ee979216fba580c4280e9a5c b5605840a7c1a7f6f00421c2e6cc6cd57974f3c3f2ccecf27f8debd243983a70 e4b11d088ab25758c21681607a7fcb193d2548b263004e762fc83833c76a3ccc fcfb02c56699021cb2fcea6fa1d88cd04c23213316c0772fc475725b675d0a2a 1649d8009e71192350e0be712becfc0a302ce359852dd333db300865b8eabb58 1efaefeadf82050c88cfed7e230d890e235bec7de58c1bb00e114c3dc86b5a60 4eff508dc94d806a0a9a007e301b53256269b02702dd0d9f16d3e0de9d3fd328 cb52dca774300002dd0025791a0e6e1f8a8f502fe7444e181808d36670c56e83 375ff540df88420186e5eade21b24b088d1002ca04f2b381e370c50ddd80635a be8fbedc896add69491714f40196d96757b53a00e5a87b3f433152700081cffa bf0301fa99dd678fb5cc13b0193ca9f45feb5e490b7987eae6583b515a49a494 37c5ab0806f33104d4a225da05c830b3d6a83f58e315f10ef95ffbffa71c7560 7b896bb7428fe3800ad0e9eb8e00b480ebeefc350fb48feacd8331dfe4b9b646 1276d18db76db45bb5519fb7119b549a38dfd72513683ac1ace1a3d6a66b3092 7667ec63d6c373e476dd90ab3049bdaf3d4a28397d41306787e2a9fd9ca97677 f2d2c448e3272f9f81841119c6d1f3e210930119b5c34fcdf4e65a09a12ac3b7 89fb18390df9729265dc0245f3cabf4beb1c98a0a9d00b90a890b7ef949ac467 5622426a8e3c423fc72f8be5b4119788fe50366c2fd223be3a98281bd7e6d184 440a4cec16345bd8ba7fb95377e148a4b17aed729bc280f225a91a063470d213 1233eb2ddd762f6951489be4e180120fdb7b183e71b15b200498763251eea9bb 312bc90b4ef5bcf66333ba7c61df12e370e8c8d608a2e67320a37a6f8000aa26 443bc846fe457fa35441eda4e1309ef887c6ca0f0b4de602a2c3fddfa5071954 83852acc9fc2e053ada4275330a5ffc9aa6e54b7a23293264940b9ba979cb2dd 6832514760061036c8d441136085183bb9406ee3095a482223fdddf479136e1c 8641d342cd16f1723df7aeae0c4875455a85ea587c63a2f68a159da7155958fa 9d5cf51299180f7458a3ad1f8840a02bc8b639b038298487eea4da57ecaf0dc1 ac2bfd5c06667873c87ce08b405daa804fb44e5b166b0cd9c15ebd8423788125 83e15224d1e34f00cb71fa6d5ed463971d181d39c457595de702158bc635f6c1 760a56bd39d9e5bb09a839c707e7a7be7b1ffc3f9641109f7e4e633513fceeed 708a528bda4667729a1d86db0b2db817beadc8caa80a614582996884676c2597 2227708507ed4cbf8b324de2f2010645f201670eef69c3b96eaf9f9c87fb6841 b9c949d7d7cee2b4f0ed9e5fa742e1a8f48d97e4b87f09588e166760720fb8e6 3940372902216ae5fca089391e31ed310de6d139b085183daad170513c6b017a c1349b16e731cddec12177a6efa5ad7b1673a24efe644d3dbd85827752e153a0 f02ee7db428e4d32b95e5dfa493730b101d270e7e86dc4872b648d79f52071a8 36cdaa723e698c5f9d29cd899adac4743140b5d620b6b5d82e3c3df664c69f39 6f16a9dbbf31db146fea8adf63b605495a0e05cb8c67f5bd22447640df8090b7 53f77ca86030f25d4d1b5929e23343fd33d4b9663925feaa03c68ea3bf94280f f3f66ec629568f907efc633817008b7f11244d21fc180721bd9659a56b4025fc 24d984fc44ae33bb93c8a97b93ac96ebbd1998c013af2623abe3ba250e2abd4d ac222b518f140d77a4a42a0b191782fcf3988f4448c93a09f103dd2c2f6213f0 18b3791b0dc5c92c80128cd3148bd553790d5bc1dba5bc967ff800fe43eff567 d67e8037b43a6e3f7a14b818ed2cb03331eae43ead307d6160dadfad47270b07 eb5fdfe847f411914835683d6e1d876c85aced3feaefe15baf9afdf6e817c051 c178dcdfa4f19c920d7a93a5f96e48953328d7e5af746fa9f6da763530eb064e b81f48586f18f651143d605d2e087d4ce44989635a302fa48193f3466a98ac42 ae59228aad954e7742a8557b0fa8da2a3b5d7f4d0bc6c6837cf6095606336824 0235c11713925f150bd97dae776d2199e26fe9b51835c6b6c349273eacf85cdc 5665a87adb27cfa9c58a9318b8a101b9800537a017189eb49c48976865ff9794 f91a4482f07265c376816d2164a0319979c1f568a5a95f9836c59e940670bc3d feb2a2bcfadd55e67ae2bc748a69f1f9900f4df69844c2fbcf0764eef95eea080080e4ed1f64b72da820255b3d614bbd45b6870978109b8b954f20daca32e1048d4796015174a1e168c26a48780466367a02b0d0ae30889c3e6815cce04e0107f113a12b339be68c0c19d13223c67f93cdcefaf01e2d52228d8b60577ebc98073548161778520ba90ce8010a4f032fb4ae836108850828da8cbf97d2fc45920f0ae8411d8437562f25acaae9593a47b1514a938167a0de8c668a412437963208ce366d92d942d92f206584a6a9502830f185a59cbd5be46f9bf1dc907355210e52dfbb0c2a3967eebe08b5dae87c6e5b1b6f69d97ae60c8108d812b8c796ae0cd7afccf9e85b804dc3706b1e2ecc5d5f4f14d0428ab009853fd8588c5023520384f82775dd6f2c95c0a3ecda7537f0d1d35265809cdd66a41f0b7c084a24b10243fbe29243a5e74a91029427da2e1f21bb702b0e77f8f6c48ff5c6245aa86003ed6506114d3b2cb1656e1ffe5f0139a2efab147028268ccd26a31e8a871bbb0c6ca7412a1ae4e99d176a49aef6c76686066b3f2e51cf0be3af13f01c50896b08413fc2ee6905d38ac13be4ecc8ed41686c4e43fd55dd2b35389d39a3f1bd6507870c150149187506fd0d9ebb5313763ec5bc383270add0f4d330fe2790b3070e278e568b3d55d77aecbf51911ecd9e6f759589e29e7b93de886734e9dfc235015511c14cd5ebaab3e5c408df3574635f3f8aaf142f2aad9d66051f5c99954e0e6d438715fb89ae5f75b7cc0b89f10def9f2e7f3b01f73a980cdeee5fc33ea006c9fb507e0724c51888c9fc37f5adcaedc63d6f4ff37453e5b4e3571750d738080facede4da262406d285edc4b7338be893f8d2cf60a548d11dd658e957d8f2072240e45660eb721a6c6b3247c40fb86da977e39c46ae440d2ce758bb363ab502a652b2ed477c583b91b7403503c62e1428567c33a48bf77bd7a6b3273cc5890a34db1b5e580d7822f05845e110c44370d6e431c8bff57932bfbe6ce65170630533f37c82a04f017c6b010dca228f85c400a259812a48feb1219805779bd7ce096552cd30c69e8a25511263f1f1091957eb99b98ea144c246f7c9fd0852ef080095c5f5607d4743ccbf1ad5c962494870e60853ec06b9fe1602323984998ae00fcd582bb896efe3fafcdeb6e9bb5134f2eb958b012d63b4e78cc0d89bf6f9eb097f03d7996e4ed89487f1a610f864a2f55bc85d590f0150571dfe02e31fadfb0a0136a068142c739532b076ec00531d616ce09f5e51a8eafcc58a0b432d29bb0fab2f3fe9eab42a7e5f46bc944d9cd2e913d209aee6fbcc85d8230febab5daa000a6f1904a08a445412024e07106ba0d1c997d785d573237b0436e31aab087f0acf376dae3b7e8d3ef13b5f53e1d3f9827edf614fc9463e1de9bab324bc77750bf283027ee3965bd7256ee66118d914e93424ee837f8a8ed08ede57d8c50b2706548297b07ca6ece6656251dbbd4d1c6d82b928ce9c5790d5ba7a7b2a483b37375419faae4dad3e12ed77f1a2c75e7b48f4a4d94929dacb6bb3a69f3c3e072d01ec50dc434cca95f3ef68aa90934880481a8966ed7855462c8cdf656918790c0e522b4da3530ee3f8a40f9d9a3b5fd38e5695ec02e0810ba28c500152966bf70fced62ac90c1da1e82e6b1a8bf508793a93104c1fe7dae6c83ac7b6d841e97402b529255b96293efa2500901ce4ac4d4f227722af10e18bc2cda9b266d7ce8b0899e50693fd1d91edce33d2c59b25d462918ac63c12d30766f000d52e72736309208b61d87acab6b663d28c9c199302ae02540df3c213a501c30fe461ea1582075bf8dbce508f793cba58837388d25ff2e703a0939b90e6f79ec46a80c79d2e0604f4b9e7ff32fd83bd26368dfe83795423c8f74ba70d951bb7c284c8dfdf6b0fe9da358148a750a0de38739a03b73b0bb1e81fc0204254433930367da852370b90465e2ee8f7259069efdc2602465d8d585051fc1674f27e046bd1c1e3a2be073b17006fd279546baf47696e419ba19617d7d38e5304423508be077714348c0eba2d6f066c299f65696afffec82050643c22e8ca6ac6510ba499471d6dfa8f0a442b9d37ec840a827544aa1e9d94298dd116935080434d554f04284e49bd9d07af0fc68d596bcea09391d9ea9431a28362cd42a2a03325c369e19a21e4daf7052d5855e63f248d18f625fc9694b7aebf6bd53b065e24c23d65d37252e0ce8c05ac2638d913cc782826c877785a6d893fc2427d18711daf05316d2592bf264b01654c3f2e6478ed9166ddcc2f46b409d51d090b35c8654855545c0d1ab3dcb70250ed5ee2d8ff8e547c01359dd09510544277a9aab314ee710e3e5b548a684d0c2ecc7eb46a199e39d7ac53033eb40d9e130f0dbb69874f4410e8ed2e0bb3c20439c724c76f0e14f77f291cbb6206ab010005f9ca17f0d62d5784057bead29f082fe62b459fa452b7f08c4b35925f43b3b86cac6112c349ab72c33732f22ad30cddd8235e00076e2c1931ba2c71e3765700840f1aede9966f34cfab41f95bea0d7d6bae9dd52609f52fc51e53a71fcd2d2c6ee73ee8cb8c252af26ea131e60d018c13fcaec01f36acf377610586288312e9b93a12237fbfea0d60829371b42406784587761c3c5f6cfa9868ca21cc49d8081aa02babcc38ee66136f95edc547055c339ca95baa71586a3964a7268dd7c5996852a21ccfc304610932cf53de2e01348515d0ed7a4053faa1caa9f199304c0a3e4bae39d553f9a24ac3e839bfa50ec4bd9e544cff9394086c15ba7eadba6e677f92085e255e4ba6fa9d540089830a630f939599220b0f1e81ef8cb2f2dcf50addb0bd4883ec6a19b0fa9428c18c07283bfea01afff7f78d404210675db3e26ff908eda912d858d9d5f927f0c1ce09d2a95e7ab8099d413c1c10016d62f9ffd09835c2fa45874a01ea1373b2a63b0d699c1ffb5a71116167b54afc9575953312f3afa8674637d563d42de301d9da0d69ae0d7aa0160ccd31c34f8287560c9c6554fb439376578faf9340517906da0f937cbd6a234ff69701d182445dac1c67f83316d118949c4df1d35bf9ca63770487c5bc2c0c5e264376b6f946f89b1d8bc048566af9506ab189fbd855f750c00317a309d8cf2046e0652eafff50dba5e8bc166ee8546b922206163768c8403f077a7db3db39e3603854931c233e0da07635146e362f3475e4a009882c1bd34906d7fafbd26e3ccd575079f9f8142e28d071ad0c9fa97f0b69beac8b4d52327304982e932918f1f0851953fbf94d0c067425dffeaa08ecbe3adaa8bf1b59b0d606f8ad69728c2c43c2802f974117f52c4318c7ee9b8c83a68199d3431dbfd2810d24d97090ceb1090cec0105faecc30dccccbb32f1c7cfb8e7fdd4061e28298f065329c3c2509398ff2e6dfda9e44947094b66e28bcba755d9696075effcdf8905ef66cfb75ef5cc9cc8471928697787bb5dd990aecb96e3ca2b58906ae07ec80ee9e7a53645dc5bfded5123e56c8820af263d600874a7fdad48c334c902606408907f9af0b69fc6ed8d44c6e7bedad0544de1c8251000932bca926c0efd629b0811e7cfb0a921fea23f4da5c36709a02525c1c5a97846f73103a4bfe0e531030cf653d168f406a00207a8bbe9bfe95764079a14c693fc9629af151fe816ade70e1fdbc05585ba8e65fd541420cdef5f55903d9d12f4411d78d85a5cd03a909a0aa90d957abae25cce4b7ea2d7ef30df32b0c8b4eb0be08f99b49ec1d434988c01278516b9b10198e8cbc156d1cbebff1a39bf35b3e0488e5fdb57fc9b59e7a10187769b0cbf17b31fde8f0fcbf3b5db47f16787d6291accca9f395837d13bd903c9390d1e0eafbba7fba1d386fd1ff1b2168d44e7de5ba83b14eed442f0931e08048a51b04c74d3c45a682fcd085db0744daf2a10b185fbf4b1557e6f7f13880a1bc036e7f79cc8e354d03308f1e7c9add925dcd30e58fe5c1192423724e8f60a7c69e8d3b4048b6a374b82a931162ad302007afbd12e6f20204649a7ce6c7d0f738e3cd55d4ab8f4d65c2a125df9f2ab2bdfec9062f2899ad5e5e8e81403140965af65521bf41bdfd85af4bdc74080041910d1920b3afe9cdcc3529178e63b0601e769642bd857d068845eab7c0c3e3709da80ffa2bbfb8f0355296791c3e005954d708a29377fb634bd4bd270c5f41d485a9e90708456d6be260035306cfa027449b0f13338de16d2e7edfe3da840b4b05ad30b38716af7c2eb9b55dd0e5b00e9aafed6d8958da5357d95c3881fa9a1cbf8775e4d26eade2d8751006c480f02cbd56007cbd7243c40e680fa586b4c4456466ec95539f3d919a1ef2f8f0de507a78f2c595ff6ad8f0072259605452c44195fff6e893520b490aefb91a5a087012cb131d6e8a48e6f4264bb98ff638c8a83462486edfad30abf19af3c7abeaa0172ffb0932679f7e8a68f61b58c88de45ab628bb5c587eb2a0f995b1d12fedb0432515449e9af275828460e17d0d9b17361209d43916cd88e0fd778054658610a688fc356a2b6f3d8d1fdcc42b2ec356c8078b14cb1f07cfc0055d2c779211d0f5b1abfe0df0419808336a44b8fc0fbd15fe74a816b8df7979d04d8345048e50afdd349218c75ce1bb8d50bbed20a253b1d8fa84704a12a0ebd984fd7ba29df083a6dc007be234ad60841a7606cc3870fb15bd035057ba0ab11cbadaea5a0ee052a1024802ee2fb8e60f0ad81c35f181489e334e978d0a98dc32abce8b1c20d04462d1e59ba5375a6b2eba4fbafb2474e19ab2bcb4781c0032c9025a89a81da02dc7654ec1ca96660f22726467449664b8d2f03c4bf90db168fcd32c43327df03076d0c36d9b574526279ded1af6c34e092d97a6fc79629e3b836bb40c8a5230c2341c49f33d463ebf9f2783a1542b1d87b216c85e72a834e370fa4d7b630f60831f6e9cb96f7cd198c3368a7315f3fc55f91e9622bffe7fbb625bc881cdd360779a17557c6913732ef766898d29369f9947ce8275964b8037dc67e324265f5039022671ad5db4058e900a7e59f8cf097e4b52274859d9493e2babea7a0d90a0932cda0d63b4cd33985404c5e0c95a15f5ac0ab6d4d7960d677107c3fd4a8e202201a16a06531628a51f5ac6ebc49813f9447e5de4fba54bbde9641e647a5a502bcf527822f0d423e10aa9c9f52802b2211e748df56f3a8cff96b587c0cbdf90f597dc363afb0df80721e24b045529d79f9260fab6a14b2a23b22135dbb924e0a58a0de5945bf96d2a3bc3ce9213e1641c7e3d882f5c358ceb038c53c1ebe7f0b3cd0f01ffffef52b5c0b8884cfcbd70fd9986066e37b6f4bd16c7024e5b8f60c98d6ad15ab0cf9e0a569590618d80ed4b985436aef4dc9f305f183bcd57d6103f9f3e5fefc305b700d75186e4bf7dcd7427e634986ae7e4fa30af25daa055e0fe70bcf40dd38fa02e9173c776c5b47f97c271f84bc7a5be79e7dd5ffb1b00d0a5c0beefcc9b7029d0f629d0c1300e5be1f585d9a4dbaf88eaa21ae98cde20b0b85c943cb79ba98b7319eca96a4ead9a24b693a8cd0201e5339751cde3b71b30206a449d0cf65ed41b8bc7db9f3a5c829d537e1c9bf7db60a56a7cbb6c488040c61511fca60e9268093585d96ba46f13d02edb5ee1203f44b1513e05f721297039fcc64ca472a2aace28b13a4e9b7d60fd617a69ef934d7527f310981406e290e65456696c9423f4699db234e416525b7a9b2d5cf3f0086d3651b8c6cddc9d404f0a83afa275fe8d2c96ae72cae5f7cbada86110edaee2d79d2be6e0fdff85508b616f58d36eec88b5a44d1fb61ac640303409f281fd9fc2ac532330fdaebfd063a7ec0ce722e2b864fca6b9f17da84eb9e6b219eec19c14ea0e0cb56886e2607292e086839474e071475e2d7844fa561b57e5a361ff3c22ec960a0fa52ee0d07feeba58d31239b9b9fb1bf0d9017285a2e36c313b0b91fe6c23a640941170a024fbb1659ecb1121cd5e438af4956c01de8f34f422066afabedeceaf232513d02e027237cd67819898a52bb219065e58ddf2f34039834321f57224e97d632c404e73d98bc1c1d0c739a94a5bdfb4ec91db8c3c317fdde51a5081d5f98969cc105405eb5d6802af4fa8a0c0f3a90ec8693b853706c5b8a824dd9d3cbc993a90c0283f5beba4c15dd1e04ff90a8ccf15fd8f4ed336d22a90d47f77bb888737ae80aec3f06af9e5cbddd2e856e167fe4ca5c4af1cfa677179e2a17e2540c410d5800bcefb2293806cb26ecaab8d3c4c530ca4a9a691e73ab78bd33eb1428bf6bcd0e47d7a2e22f10487175056547c2c48e3b1587ff67934d7b759606e219999a8e06f7c7e4966d11168683981429e6a71893edf6f7db4f3887a24f3916d7e8588c0cd4cd17af4df3cab5392240e42344081933d6db2c32faaeeeedda13681fdf990537d84cebad673dfd8ddc4229df718c3f3a2070a9a5b49f2e278b6235af21ec0e9c67045d07b06c7cbd72895c7aa7dc9e5150940904059106455f4b65b06ad60542a60f42d50bc020540229c5d03f53c216380b1391db54edaa13d68fdb2fe90aab0255dc9445da561d79323b27e9c44473311ab211bde5d8fb995935aa10f201092a76e63680fd51facb32713bc86a7443de39e814ebdd77e7df1f97339efc0d29289d5f02e5fe73eb71479e2cf143268e50a1c32177e596fcd4039ead4465079852bbed3761bfb6dbdd74896fdfab884b2b49069475deccdb2ab5014a6e190e0942ce4b4cc56ae91aa74f96cb528cdc7f5d8260da6e0492f55f02f22bc3b804ed1c3144f10488556e97033d97f667b650131225ea5451b97297555ee363930946267f86e2878a43f97e1a2aeb92570056e30c1dcf74244e5ff1cb1ec3968c05e30c824009ad2b56904d99477075e7a894ed96d3e8a496d09cbde4ad55d7240c2fb69a3b44a119c8d0f10ff83056358f706f889b553f912353a2a4d7f5a15d0d730e37132805fc80e517493f827ae852f532ef3cf2a77e8dd4f906942992fc024f82b2b95ba168b4103b2b3729a096068ffba1d17b66b58221c24caf24176904b967acc905b5cff55d3ca5eedc2d693a12c28ad9f82aa684f29d5c57d935ce09eae11f2d3dae024f902073d2a2db21c9317c1fd9dd2fd05190783afb6f01e90055451e6ffad613dadaacb1ebb54cef74c872758e4ac3e53523e047ade8d9ae08e37412129d9f2c66ef4635381071ebc04e80878dba227c441d4480b8da74f80f05d201791243db696ae229c9f741287beca8b60137082964df7994c698cfaf0d66866b5325b67af720f5e925f87051119b88991b918634598ebb80e114ab4505f526b80fc5797d692cb29a51d82026a694255151ed9d933bf600040e36c27d034f04fadd44a7bed743c72d52b6efc587649424343ac7c24e70af74f5e3f87d0fc96cc0fc75d6033a8d304c1173aa3aa57ca489789f281329d642c19dcef9990e0ef1b76897675390cd5601bca848d35225a69c154d2e23baf13e7ec465ed2d006c493f211b1842a82f81ddf257549704db45e93f4eac4cd0a09e98f54e57ee056534202c0a55a8db5a48b83cef771700f88d62747a5e728cace9ef579fe710032d25ee69e0dc6719552e3a1840a84dfde8caf85058804f5a56a9b8557a7f9400896bc1d690ddaa6f51cdaa9da880d3688f8fffde19678e72a02457e82ba396038956653b0e5c28a849de5ac21370edac90265b8e1477b11797568a13b4d9d1064b9517c6f5ddc1bc84fbc98d919ba6a24d17f2d1ff18091842d6cca69a0d6308766912378c55c9b078050f9dd5dd6379783670be80dffcfb0da5c9757039810e22f741692e90ce31c44ce485d03b063c23d08088ce2d504cc7da7077cd41930f99b4bf490bd84531504dbd1959c25209bbc47988a1348cd3942fdbcbbe368409862279d74b97b8008a08f57f42b921d15b84988a5c41dddb5e70b483c5dc76091218663a8caad95622d45974095ce6424b3bc2a7fa14a6485869ba13bb9bf207a7b22f6ea8936e0cddaf21e052b9144e0c7d596150e3b9e227b5435114f6d20b45e5c0499f0345b7722e1f1d68ace06cd5992e039697371dd1d685a7209b2e077975416c9d319e3b9e9fce508222b79d0bb2da299d5d7d9302950c8f3432850b26ff5eff00fc27a9f53e3c167902b4d7994efbcccbaa20c7a935670ef0c46603a20f1d0a8ef541d280ec5bbaaebc7c8c303517edf5960cba330135395e43970e802f13c54f9a95d105d20d1bf74ca6052cb70a382bb809e686ee6169518a5e08a2072bb0979f643605d0255b7141087f6e4f40a719ef3f060e3b4b36965ec00157339a6d11f87b64df296a6b60a61db550ee5c9fff8bed0aba26430613517f0d0d3bfb208f9df70ea0e1be358af20b2b1c6a88470a0e644fe4ccad52c0c2360c5e0ac729a22672687edddb7e7b40eb527a15f84845dc138e48790c8524dd180cd1bab079ae3f950bbc614b091f0f809aa99e83b318a342be60e77de401eb5d022964d81866ae313b3ad6b2695cebc09e710705e2340281a110819ecf7f9c4007547ea4a1e640d77a5bc0d0158e0ec075c94e324b10c3d89277f45ce7a1b9760f1792f91904cd9f5451fb2d2ce571afccd4cc9ff586c96a1c4e6e85234bc3ba0e15571b1bb96042bd51c5e5d69ff418ba193aea9f777492144d266bcef408710160d7328ef47f8ba11617742658c48e48105e66d3a4e5f95f0be61242d23f360562b8b38d3919c75a28f9737a699ea7b28541b62e71141f6d3a9836fccb111a0f9c67fb04400b2ae34f54c19aeace3fc8afb685b1793afecfff31430a737f5f07fa9ac18ae8c3f4168dc4b44c86c899d553d3610b72252d788d24dc04e049a5008d6c739ae5bde23a81cf3d1af9b035e4665c38ef703da25cf510e68bfdada5043cf9771cb144d58b426294cce7f501dce00643fcce0ffc59ed86fa1ccd87fe05da0b6233b70a8a5f10289bef691de00abf7e32c0f3b8e46dfa7c2e340fcaf9022a9e15d285a6838dd04b63133f57014ca6bffa8f25cf767f62f2c3d64c73e401680e9a493ae86c4717b52cad5141ffc3703e6c7dc34858f1f86523e24044320a704e8bfd98a0b6985a3bf4e302e3ff7201e912621491604dcc948af3c550f5057341974aa5551224850e9bf5b3b6d1fb64d723c18a0d8d05937f80ef0eb92e06d276c3f4f2a1246033e72ce3ee13f34fc89219b4698452edbc50cedbe39ba80fe19559801e9d98c089984ccc2a2e8bb8885b7cc90a4c073079c9f9b483a33a0fd9145984f13ff6625aa3b3e13548ca6d78dfbfe1c26c4e41bb4bccf591df2d0e514cba2a3c86593bc0a9ffda0b12d18708ac7ac9ca33b3ae477d251d743d3c0692fd68d91d30722c583760f07461b68b9d7eeb670cb6b4e6584d3656cda65608c45ae881facb91db6d3085febcee3bafad5ff9a17e4f9db62f8c25784b550f09d543a3caa3f46471cd78dc082125e22c466ecf54d4f64b292be1d00711f8c506154a094093d3156c060e851decfa688d703309f74f4e45cfc637d9cd157d7e030378938f5ac28e36fe39002ff5b33d1e44574f53c7058c754ff810482ce4cd0f3cc5c490ffc437aa8cac09f81669e0a3e066e0041f34feec0f3ace229851590d9920d653a16d3325eaf6303b96f5a719929652b9c57654957806c307bf2eac0f344d7b902a2f5c9243edecd4cf1a8937f04b19ca9b37c597a24a19161b0470029c61bcc498a98757f84768e9bfa14250a3bbff7aa35ef5e318573dbf782b4d0422f71500f27af65af38c1adad8fb2894619053b88e1cf46e5efeca00a30e260aa7d279ce3ac598b9e65e14eb7c99a52228f6b285af85eea25fb59047155c570ce9bef7554b79a22a827d3da3daea31ecc744e006e5ae082f1440ac3ee158ce031fe2023a08e993dc8cbe689c4fbc95f662d720089065e6ad90b3ffc955083407028344cdf7f7a6dd1e045c4de42b89c5f42e5c97e6e767d75892bed284ca1a04ff2d36ad2c58debd7eb1b68149c1be0e638be42c209c624f270dfddbd19be80fc829872fb6ead376957e2f2cdcd0b49419bd85fd781d9fd855952712a2394801bb16edac8160daabc463e58dd5a8a4a7d761e9f43e9cbb9becafe476ef6d670a412be0776c69d4b0657a296a3f062e12ab46f0b5366993a5d039774e9345120527bb89c73a0f3bac4575a8a772b6a9a04002f0fab849497b16aee9531b0b080f8e381d57ab2db30ed3c759c7646448d00292ce23308c8cbc9b61bd34115a260be1bcf08372f9abc7d7a0618b5cc30e96e66fc23b0ad43b0b5d70b442f2dc71015a59bd15cf5dc9fe8fe5c0e0f09b9d83152951d189e29da70adc8880bf18ef0358ac9aa8cc469a309d16c9bba70b5ba5003b1a1671962cd51ebb4b8f519549041f847fbcd064b2e7fa79b9b7a99f294b4df46f9630ff606ae43c08e23a59800e021e6ad472c6d9555f0979bc3b86dac76cb085753e8e16b0c4fd68aa8fc9410b40649e9e552de0f7f0614f74dab40b93bc81e77c4492895b3d148f22d5d936043fde5065004fe3f6af1c57246b8951934a3515306f074acbd07706c828338f06ba2fbb638838a421f320ce28401f82a484e5de72f651359526ca60c306a31502e0b3e58d684efa0a501a1674466e89b0831285e79b83f9014ed73135c2bd22039fe8d6cba38181e42cffdc5a1caa4c426ab36ee77165f4d56398f8bdee0e63021c8b1d3130619fd7d7ae245d7e42bc05ba66facbca80a1dd2d4137a0583efe02bbd4620e65b452fcfb8762dc57013b06aa4b2c2403d4bb1d1324147c6d393205c14bf53e7c5cd19aa8e1a5f95290e8e792f5813508fde1ae352af3fcba560708f34a181fe9b3c53f9f78b001eeea7c8dc232fda671d2f267f6f2bbc788c2db0d4c7c4d2202870ab2e2804a01836806f004efea99640c1e10e3f1d3d52bf2e3078ac38ca0ea8090b1c08c126843419fbd5b8b4f6a8729c1212e3b2930cd1f38014f3ec6e0bea9766b1c25c5d8dd2f5ed6e6f55253a7e330813bd786cec3cbc30bc5066f8a4d5b19a33c8c6aab8c38fd8c856907d9344a36933af920116afdc70bc1b0835c9c44d42a6d30ca01104dbb649fcba7abfa22cf9c799fc1310baeca0988fb2f9d0bcf218c85902e59bc39b46742eed113219cafeb5e1ca8e527e2cb044f7a3cdf74c7994427f13ccc98d990d7e469c23fd94a2a77a234e08bf4aa000350b748c49262be620b004116e5cfd4cedadaeba7db6b9b338721e3bf662fd803a64720c36831c4e746e1aa3c64b3fb07d429e554940aa14e9bcba9e45697c801c535124c0992122b32fd6c2b218bb8418ad2917aceb4e89fc027499c1cf0f70e false -check_ring_signature c354328df3622741342804ba34bb5a45becef8ab62bb85246c0f42146337dae1 5b9093e949b7cf82963dfaf1399aa05ed5d8a3e3a73966d553129af6667dcf78 56 8575d11950109c1348ad540c7bc8c0e2b68cf523949bd66e6616b146c271dcda baf9012bae1bea0a23c97072ace2aaaf10d82c19f143336c0ea36666ffc3d53d ad1f9c433fdaf3f4dd7d383d85c9bb892ec1009d63cc41e9e2e41c5b771b71b8 69d2cacee9ddb629cb8decca5f6f251ba735703e6a7b53a98541399eb98cd118 8ae75d006cac4d42002cb7ef19fd7a2f57e41300b22a3c7b50683c2f8dbc6170 406c1e49a9ae84661fd063cd5b8e09dd2319f1b9b574c56e50ef627be5d80e43 12ec50ee4554f406b6015dc3b7d0c07a0a9179b3ba36e7ddd9b41b652cf7615a e27ac84925e05a6039c26d2de89301980ac073c055122d4ff297c2bcd4bc9eed b7ce6eb4bc7d101d43bc66ea51b4bd1ea4bbb81800c706695ea5fea738bc1284 855313e196c8fcf6f99a41dd07a5812955d2f022b8c33fff6d637cd356f5805e 55cf67b082f6f506363ac6a51f8c1913118d8c0f45b16c3077a2f532fe623ab2 5e569e44cdba748e24b742bb4fccbf5054f66bf9d3467dedc8188621192a9324 83d7ef8d2f96f5c0b5b89951f2de0d513cd6c22bbfce85cbb81a3d2d7213b6a8 d0cfcf13995377b17672d79da382f52e8c1f3f68a675b573b52073d5023b861a 5a4c81d50698a2ab3f40639119543b0c335a7c5896a4adec208d230202f0ad3a 0df622337fb01734b2a8b4b747a24965c344fc1146df2635396a2b1bce906a46 cd69388aa8230adf7e5ce2a90baf8aff63cfd71adfb1f782eb2bf0cd999bea0e b8bf932f218565eab49b22e1d07e659cea51ac9439db124e67a03fa612a7bdeb 1e1c4de0f1a29b24ea6f144a1e9089052d3d4143b4933873fa0f00f555e4def0 da60bc48d41954dae8e5f2f2239d90b229483420862a1b06b088ad76e7969476 538e8518119a6ac8dc19d187a54bb421a5292ce8b63f9bf6cb590980cb68bc47 4378df3c015a752ea6598b74a81cc1c8e21767b7b53ef3398ee9b93497c6bc6b 30ce914527ca9d5d12848404a97bb5d70eb70fe55333f4a895871371fe69fb86 356722ae652b0dc88e1b958d794cd763c4cd2cadd57bc51450fb6640fa2209ba 11fea384a4591d68753f2b22dfa9d50b3cd350c7f3ed7345e82304edaf91f5ad 80adcfe82be138cec5f66a2e70e1835c5ae6f5bd9974fc31e64fc0f058845738 cd579d3444f16498e6a90d9c9b7b450fd2eb6a70636d6b73cbf1b0a311bb059c d25acea3741b5161e8adb101e1003b9b5e2d58090b46616665887acd63c5a9de ed8c62f762b306b85294684dd63b8b6c3f64ad473ec9171088c5f4b27add4a4d d6c3fc2bea48d65f9da404483061a05440fabe010422fe712d7aa0075b7d919e 93c70941a6a4249a45bfa2e398d6565219c4d0adb90843a698c2a07e191f7473 4bdc42584b966a1a6487414bed7d10978d138c3c0fba5a3f25b5fbeb1ca52ccc 9d8a97af15ffe4f6301ba26f894a6bd55c1f6c76b16382544509fad2d8093def 57e486c29bf732d229003a30157b3ed502380530fa3f871888e96038807d3576 97cd0ce33ee281bb410dde98bb012bb809b903e32646c29c99f255b7006c55cf 73f24bb397f551d3d9b24817aa021895868cc40eca29e6ea02ccbf47f56d074a aa48b84a3cfa59f5137596eaba1313388afe808c7cb2a8c3cfb27b753c15bdc0 c5277232c519e83bb50e2ff7a7dc151c11bbad5e27530ec965a86c051f94aad1 e34abd194d6938c0eeda5fccdd00eb8df2cd464eee49ca4c9f4eba06fbc07584 fd3c92f24cf5fa03e0ac7e6fe00afb8b0a87d65acd097dffe3c0e6d9a7fbf915 bd884709004509ec9c9db458fb5e60c2744e19d32e3aaea6a90d3f05013e87ae efe5db4151477b7c3d2aa99f4075c045f93dd7808e80f0b20b2fb362ea5bf8ee 9719e753b526af3cb6cb058e06c6cb3e881fc43c69bc16a4e14eb36a3d86be7f 21a89536b2de9c2a5c40037287dc3fdf280bfcd84ec0a520b49f6fe1bc8b0dcb ad4a6fb566792da5034a183df37675c04f841d42bec1450afa55b882b478825a 60a89723bf6ce07d4544a8bf4e52f1adbd92ec1fde80b7e692e5767de35496d3 2be78b98dbd6240e0e8ecc171561b730d8af53fd3f1a34e60ae80ea64045b419 fc7fa813753b2d6abe9ba4b342a5b16b605336b202fea6675d9d745083420c8e 1bd09bfc09d7003b2b2f39c52de35480df3400ae90501b770afd2479126db84e 434055b60dc8c3d4805da07f02ff72aa63706f5e21ea8a516bfd6c8f884abd69 b48f1ebeff7910ac6949b35ebc257d686afda7491eb051013581f3e8319146f0 874a3c2efa6f219f5376c1ec87c3bfb33827cb9e633fee306b6e2bd6c8da752d 3166156b35fb2ac7e3e521ca1e51ecb032f1043f456f90cf2cc3991fca7e0431 edc4c3d609765e1b1bdda61bd57c8ee4cdbbe98cb7b57d06cf71581179015e51 30906b1a9fcf72af7cee90d6e76196e9be737c28383d1e1f21938f139a2df9b6 cf5d908f99553afd19e004c7f75ff54af9a2736e6a2372e19f91f111c38314ec 55eff74f3033638fdef8218c8ad5cf554630feb4446c98c3cf3794e2db761e085af89ec0c8ab3f69d4569fd5cb1ca70aed18855bb35bde00125c27cd969c890595983e989e63164711a2d039990dd24d72803035cd40738c41ff6609dd19d50a35a250eb8bb2040aaa446b1b4261201d15c223e55d5c5cceebbe5f406ebd0c0d9c8e90ddd857e57d9b66c6f1143546f127583dcaad0fb6360c9b03630d460000527949a741dcefb623c11bc3d54961fb1b43da81cf7e1ef74443762675019af335f7eee8f82438a534322aa39b714e0cb7e237e5931e3a51a872f004ad59950b3299ec12f3392d458c9738efed6d5bea9831f962eacc5a13e731c25ec80a21063f83d5c58569728ff3f654b0f98aba6ac85fa9402df70435a08d39bd761f5d0dd1d5081754410c5b558066399c139a5bd53462ad59793522d4afc0799003c408c45e7a6d92c272c61731d75d99827ee15ab72b92ef8c55ba49c23aebefcb80026b76f1a3544cd6149c1a3718dd037286577bde6533ec31d4788a3ba8b04733086a4320cb78198447bc3f2705db04960cf165dc8ff574375473dc72694c16880ec5bc004e98e7759633c9f7d46999b326c3ec9de351e30a8675d6521c5fee2d0cb8925cb1dffafb9fb10876cbbd94f5e49e58376df2041f85549754740eb87706ceb5918670cc5c155aee6e3d70c0147fac3500f7810af34b24e96da157b4580d78eb7a7bdde6354b078e5f7df7375dd0497027cde4ae4030bc3aa40565ecce0d5bafbb0dce5aaf46c94a4e4d4845013157436a237dcd9189ad5113b3f155c90220ee3ab2174b7855da12ccecdb1ec4c1ded44075c2fe3e5f27c59a5351c9d9060c3a25b82a959af7f6c8d2ad66c11509a7b5221de621b3ddf802368f7e5f7b08a91c4248505209a2848062ea84f19e6ad3f5c3a2fd9cdc4a5f90cf2f3bdf040cc77a907823ba053595c774c3ac15cf46fe7d7e0a4765e0a6c39490b8a55cae0386953960f522780476d00307b7c1a1ba9cee63c72cc69595689b18f6aa5d770546a382f8e33c43828ead7e0087128f18414b94541ab1fd0ee52edb95936b27005f5d686576995777e0471b10c41cdadc6860e9424f551cb161ef7d7840b0e70331f880dafb20632c1de9a80c2024a0beec32909685296a7fa7e774ec8e7eed03ca488c75bc0257b0abd6bc6a40c41286844c7acd90188c003a004a79a1448d02f869235f38d474c241e2a5f9c7c48774e4c5fb546b73b70ad09b9d388215770ffe48cce4ec565f88538d2a7a638538410efd42276be39d704a1b2f84b5a6080ed49b64d39d498830d168ce09ee8be33884d9dcafb5ec2a4e9d131e26359b3f0b49cfd93955e7c766c548e1e091e5704e482c73055c5b200901b7c9e1d6887207be94d392f90cb98d7ee420a61a21331a5bb24b39eb2dd530d8e75a7615b9e50b1e17290a469b51ec805730c88e75b463af9f9fb4cf64bd0008dabaf5d5990509e9acf7b32eb18727b16d5d0b3ff97ea9a2cb57d44cbdbacc86de1d3681918c0e1d22ac05ea9c9afdc1a09284dca55bdc33bfa7c90deb1f07a86bad4258282e04d470df48730050e48102b9d5f3fc82665c2fa2b43dccfc4711fcb1149ffeaf074f04230ca9b3728ee783f702581f1f9cc62f7a212e15bf31ab7d80b390ee190c333dc06bffd26f6a53053384101941d6ac4eaab838a7766dd4a275af5640f109f44d735bb162ea2b9d3479d7cd5f3dc67b4a3cbe678d097a28a56e7131bcff054d7da4d1dfaeacc1545a84be09d5f5ecf682ba2f83a88fd19bd4a8f978e5ce054285b050e74641586bed44d5e7cb6f88e3db808d25e674ed7f2dbc5b75685f09a78e055e87ca38451f315543c923234a508c0be0233d1364dbdad3e087da46001775ed8c3796eb09e7bcaa490791dfdceeb4b0bcfe2c99ebba6a3142c1d10503eb5cac9c925c2dafec09c77baf37710a7a5cd28dd585dee4a79194fb1fafe10b15f8523c7dfe74aa5f65cd7b85f14a71fc6faca331d8a64f1c75165bcd603e0969d9f93e1cb86dd8db31ba9768bb00b12102d2352e58557a5a9e2316bde75203e0ac39affc90f1e37efc86981f74fb0d16f57fb032fc9bf4d1193b6f900ce50cbee8209b5b0f1163ce01475b458809b39d991015cd6b5d67e038ed573b062d02981433d547025f31f840cfb17dc4dd04c8b213d5ea2d4acf8dc407e032908b01d793feae336587a1b4261ad75bbb5539c770818cd87f1db558a61cf6c66489036873e45637ba5413830b50775523fe42506e6a41f03d6ad31a56187667139f00b0d97c56b87f6283ccfa5a30069cdf3da55539d49baa8beff595d31b3f62170a4c4d166772dcfee08b648195dd02496fe7ce8afa0ebae611fadb594c082dc50a8f769881df9e8a78eb5fc2e5c38c7365ab2dfe0509ac7bb2fec54972d86b9a04eb5062382958f6d71b87c56fab372c463aeae9887618e488205eb8b565117e0123622abef8da10ebbeedcd93bb7f9a014bb6f294b37a4cb030e90f367951dd0eeb7944ed3dd315d04babbf57d4e46a2926a2929ad164b9a39ab4710d3aa8ea02dd1f325f7335f8591d8119199e60f3132a3144849660de40bd514443ed492c08acf83abde9134afbd17147a272d04b100937acb1cd6bb3a829f47d9aebde7908f3831e2f32082bd1756cdedc501214471afd3d0cdf72ba55fad56fc1a7966b021b27ecc22c68d9d00a4f36904455085fc9eb05a5daa64dd1fc825f6aa197ce0a1ccdcf397d3053f08a18a181c0cb73d2135f93b50bffdcd95ef4ce90ab8db40c0e1dd99cd5c4a64c4a5376246472d45a92200000542b89e43d6990bf5a29b40b9b3bdf0d6e952fae660a1de6f514679682f81c5a4dd0c3d2b87477499a490a02a17f35480d537551091e63c140bb70d1a17f99f32d03a791e4d209ceccabba007d5eac7f8fbe4fe16152410b38b5e56339bd4b7bd21296c8f8576bcacccdada82239c92c96acfe1147dcf46ed9661a0958ca120a88cac44fac4990c3ed301b0d5ffebb0ab02c1ecbb69d5871e782c0bc39aa6fd12a4dc4ddf7e5b834f58e460d35ec1d3a1052920803a1e58dc8917c3d6f8f5e7c03bf6f6222961e41d71c9b02fa4abadfedc1a4d7ebf951dc62f85b4764ea4f70758b81a82d82e2093cdb5e09d76a30ffe7001d6fa0dd05d8f267cf17537054444e6fb09343712a565dd2cb0105e7784907443746e19ad2094ca1809f6a51716535c4d985c82be8c1a8ae7f0a8c60e6a3417795e6cd667cfb83a1e477de07b046a84fd1ae436c4749379e580ec6874b0964c0c3cac9aac49f8ddd8a12b5a5c35eba562bf723cadb180d0e2408577d0c2bbbf910a1a81354d281ed19194733bdac7d7f01fdd0084144d4ebe308058e9623d9a2721a47da39cb1518138b9ae32d29d592c8a7000cc75ae2604406aa79d2cc0714cf68ad19613112025434b5d9994f156d0a41dc25252c68db2309999f6ffaf9fdc0c37cd24756600b9ca753ce5837d456a31f15416300c9840204d065ab4e93918eadeb98dcc097f3ab1a35f3bf119c398b2e208163c77a7b190baa0d6021e84055da9c577c3482a6157c189f92afb7b188eaf18e4cb84f9fa10dcf13060e7aabdd15ffb932d2a9e3e624ff7ec94eca883b05ff5ad7c8aa4f2503e29f30a3ff82a920f907aa32f8e7e0006b7789a6ae904d9b9c5f15f235102f0728253668a86f32ec6498de348121574537ae2902fb930a1a8b52512182a35d0d5e312f76c20b44134df32dab5ea1fe7978069eee955468ed94425c4070b9110c1ec58069f027358c873400684cc97dcec40b9264b4298d49e9e40c053be0470703afdff2f0e6d843fd1503a317025bc149a22c2d1047042883453b0433daa007f612fb681255d576421cf060acbd558c01605082e66336aec860a4f36a1a69056f11aec586f0c05af18c538cb18d5e964ce594c45aa3e419b31e8afb23a5ca0e31215876096f8e69c2b06767b41c4c5c99f5576e5e8fa9eff814655fb5b9c3084e41ff629e79128582c6fb18def20a6c0021559bdb71cd2f5a0130df8e14e40937eed38354610f085ebf41fe9ebabf2e00d56ac9f8551b1c5fa7524a2626f00e1230a771325c46ac942192c3a07ee4fa67fe3e9392489833d5a429f5fa7fe40e5b5a3930e7c9cef5fb727010c546894de76de32ee13ae35f3ae16d5b4f371600bc649633889f7bde0e4a2798e8c8884dae1db68afd44abe9dcb3a4441464780f62453ba803f9ccdbfe23c939079a6cb7204a278fd690f03154375449cd5a300bec2d0a36d29bd4cca0b4c4ca513a25e0d18768b8c635895bb4ff3d5672713005d435258a7fe58541f9534eef6108e19f98de2476140061cf342ffac9cb19f90060bbfe5b381bf25223b6e27c0ecfc95726796098276f09e4287d3f43f0dbd9036fe95b7b2a5f5229094dc07841b6ccb42fc1b5ca9c44195a9c49286028376c077a8d2e4757d8d6236794d86f17c5c7842c4a83b12b349dd1e00347b33c1b1e0feaa2fb73611b0c5e2892671eead8af3434c9ce6eae288de018532ce21eb8490de80731ced94a1d8670c09452c527221030f6795a8125cd00e42498d62fc77d0ecd510383fdb964f3ee59c00de9f724af5a716801ca3bbff580b39b5c70a5390bf7fdc20af961639e67af3e9efa3dad7314c992bff4325f1bd12f2e15f428aa07341033eeeee520c6d37434fd4b45035d48f9c159b47e1c910687c90becee7e0bd95cc631e6072aacb58edecd7ce4d6946aff4abf014a13dea38390f3807e2e08d601eb74677b8f70dae8869b01c1f6097a214afc9f20ea723e4e80540d8ba101609b5c317317851f8e924d1448b3105abf6de17fb81e85ee1769d1a7c5a8de009e5de0b887f2243b5972fe32820a38c2003800e7aaa348dedbbac855aa97f90cf837ed41715902981762e7b84248a38c7adc3acdd930e90689ed7a73656e3a043d8f5105afafd52637278df925b992803cf18d34541bc379d2810396e7497d056dabcf60c8ad4e60ac6f851795e1c9bc807e5c08ef070bf88a176520c8721d00 false -check_ring_signature 6924656c129111eea1857d576a022ffd0d5a26cb3dedfdbd593cbb6c6ad050e3 18807694f5568834d1483d6252a5fa4f7970fc49310cf2007c2b72b6b38dcac3 34 08ab253a95aa576b7a253304e022a8bb2da57fc1023fa1bb535a44225fd328a2 9960f0744d57c235b87c4fa8e7acb46eb616080f09d39c6fa470c4adb6e0c383 a5c5a29053053afbd18af715dad35846539ab9f011fc3d5d90b045db53a915f7 b5e63d6a6a1cc3ae8c45fe2eb323cb8de8f29297611a4615b9ef5c5849da55cb 08fc0b67e1904d5815f7949dcc79dc1a48d639b18e06b0040d729ecc79f763c4 f091fe7f1f0c31f476b1eb372954edcae60f34092648714fdf23f3ad7b23358a 38080fab19e7e2eccda0b488b8263986b6ea3f5641f1f22e06f597ea1d0212b4 6fd84cb460fe5d1bd291052dfa64e37dcaa58853bd1bde3873d6c92ab41d976a 5cad023c0a3f09a524dfe663e7440dc1c6f3a4a5add3a16d8f877037decd4c4d 592ff8de3aca5929a37bb5f65850561a23a6c866f25e43cf3c81d096c38f615a c6fad3332720cd213abc776d70cf0e7171c615c42fcdff2582ad13ec0f36705c 2a05faf033baf040f5a3a3d0ecdcd58ae808acd857062ce825d8784be5a5b63b 910357f5a554660016115bf7ce601666bf1ff9861459ed863aa127a9380cad2d 5caa3439b42ca996bc545c61c8f5514f148e56f14dfeba3cc8367cc003c03e9f 73a87480e329824040194d38be24407009e4cbac5f0e9f5c260c7fbc7f125346 68f952818bf0004528d2a3994bb286439be3686fdf06f31da1246f74e8e3e13c 4e70147f389a9a785d7cd1aa32a2e2a82e7f6b21a18f2189b705ef9b1ffe8169 a9456c6e1d98d1aff975084458dafa2558d79ab8d91a58ab2250d6e94db05109 909732ea6b1c7a80d8bd82ef292aa907fb798e2339c018a451fd59d021410655 b54009aa9e6da817ea7c2f18191d183c2c4ca64bf98b19a084fc9b7ad5a98415 3a37aaab845c170cf7dcd43789c8ff8415752ea19595910cf32ca12757b05fdd 832f33ed65e9e243b3e9ec6f95e5794735172012eb6228270648b1959cb6cb1e 20036a36b096340d0dfddb8d9fb302586e5e7d0e88774abf81c268e0913de4e9 ce74cad534d5491ad7d47578d265c4d4691bdbe4028e6514545d2c5bd4cbadad c6a2efd5f7564d196655c12efab8333a5fbb4a9ce9bafe23098b5809d5daf4da e4f685916ff929fb707d88a5f4e824d6aa5608cad43e3dbd3f01b1d11521c66d 6c04f365f617c1ab3a144b1a10ade557f10b3b1c1f231f73a6f402e0f35a44d5 1d6c44f1f795c0bfa412a6a7792574b93d52b9cf020c57e1852bcd8e75c1fa95 986e2e44efd4dfcc1059244f069c5910a96459e8e5b8d9071c12b7cdf608eaab ac60c696d287602b93a1534eb84c11be3535c6a6c04e96cc11077d106121bc11 9c71726cd3455d6f705fcd1937f81e51f78202191b2b185151de5bd62f72ed38 55d3598c66237126a9d8a75333a9cc8250a1e035ab6801ea5d0d081198479515 55f6697007191102f474be3a2f7004d6f01c5b350e15f05e1e0ba4440abc07e9 3584afae939a3538d95c9e9b041b56634dc8968cd91a78a7c9d4bdb8de8c2a1c 178203420dec4b09a5d160a3d4bfadd04c3ae93284c528806d02d1b54a0ab8036b37b776b2a3540dac9642162d6435ad25f8e48b01279adfd6d70bb6e118980ebcf30da9323eb7ea323a53521f194b10e2961d1373b0b58366e835a9d7dd820f35f3a464919ed77a973fa7de14e4d452cf79a35a8095766ccedf70f669fe4806333b1230c484fae34f7170032df32a9a470d655bb15f1547778fcf2879968b0f073929abdbb324fa9bcdaefe1e62409be9aed982d5cfacac924a77a1b056520e6df54d13315183031beebde948be16717329c1cc2d988a09acbec3cccc1d180437a8b210ea071efa6e1de013c98043d985bc8b3b6963926e65cbd85b3ed9b60e3933c67e40ea9fd519592ed9648a1ea885e5ff387b02ffc5e27d5012fe3d970c956f000ba2bb000fdabea2ce39eb77495f3521c477c41f987f7745a8240a5e01da31c4a8c7ff1c403dcb542320aa8859cb9585cdfb6b3bcc1ff6e0e323c94107c7abf8097ab18f6f5027e7214ee613ff84025713c36ebae36d0f4162280d380ccf1026f1bf59ed3107a5446a6c3fd40134cc6b382e9ab99d0f3afb610acd790668430bc812ca1ffc14f1d757d7c90467cb2fbb7091963add983b519b01e5470016413080a01b3b7eff2057f5a7fb956c502155fc47d007860d4d4e353d030401f765dcd7ecec54472fe57d35f00dbc428e55db63e61907094cc12008b8df09005592782e7c21b7a63d07b355feaf873d68f0905fdd16b762791cff264aab89081280942bca21fccfd8a58adb4624d8b433038758053ed77fc8dbc2effa7e8507ee009c365462d860f41adfca691812c374eaa8b6cf351f2d2299f9cbd3e59804605704e68532bfa942b03aa195cb60d5693414de3fbe9c76da8b78e3d805cb069f476590127a14f0052b19d3c722445ce3aa27a56969616d482b3576845abb0f048d0bef5776bcfadeadd4c21870734c6d746c762cc8becfcf63834c74aaf30c4dd01a7e5fef88a9dc959636e7da2ef3a48299b26be23911e042ba036953dd02b2296bf410b70849dae584442f888873074b5ada98675a5f6632d479073c29085cb8ce0e582c66fd697d988b96a7dfa69db6d04d2d82fdc4c4bcb9962db87d004fa87d0682f0bcd30d9ed27d705a5584f83eb636f4ac8b57741cb1532931620417c3ff7cac2a95a597731102d38a670030de3bb2a97e5fb24465ca4d68eb190a68fade41874e03aba275a7af5f16f72dbdb008bd33d5b1f9b7e3fdb9b7d3570b504c4de3ee8aa1e62246e730bb8715a0d548bdca9d7816bf6600bb7df1660d0e0bcaa5b5ec1d81f37a7d2572da60c2e6492a6259fa0b536be508dcd961cb5800a4929d02d7cb59d0a7e69828eb4bf09f9d8abf02f01174787dfbe7604b508e0b766b3c7caec5b4a073d95088316ef5dfdb38afff57ef8df6eb3a3c1bc9f426052f8152de2cb68ed659a5470c0bcaf7622392125faa433fef6aff95a774364c0248aa978630a5b8236ea48bd71dc41e4b1529b531533e5f027bf2730e0e429c026a0ad3048d44a7df345cbb3abf8253a49070b73ff0bd40b4a84fc9455e5cbb0dcfe4de438ec852dc91aecc40411bd22b99a98a3ab6811fa51bbca14e3291f60596f657ba8b0178217bd2a6e500cabe7ea8e4754b38f558a7df9c44dcb5b05e0b0255b2519946c15c5c42384933cfc2c4229e99863b7df691c75a4bd980d8e608bd0239ef84a74d06557034d914d4f9e50e8fbe47d70754286674bff43601c30f7b06f398dac01dffa13b342f895e978db09e64db8c4e67b646d49f402e21910d0145d6229812f8f7cc08af979d8fa87653299d4dc389524d3c363a6f85e6a00090f0f2b3ac40a8adca56da89731978e13c387728d26d56e131940658c16b3e065a60e65d2593a6990c914526c5b9d86e12ed571206989cb91252c82aa8d2ac09f8eec480d41fbbf787f3e456154a82530a7371714b378137a0b34d8dfd900c0011f14ee29e287f070e9af4685adb81c229c8f21d1deacb80492aefd33fc348078af63d370e413c1ee7a7ce2fe52401493f3e79defede95e2b5fe3f283bd00502f97b7e89af3038e6fa15c87766d802131e728c01a470810005d1919fa8ad170c2ef6710b71f40ca74e989f9395d7c2fd3f7191833a7554bcaa9050a5a5e70e0b5b0be60529e40c98846aefc6436fbafa721c7387d8010af2663b1def282e61094cbd9d68258ad662d3046e9cecb8703e06591aaed7dfc269004cb0a5e7ff8405240d539faa38b9ba5e6f7826bc7ce35202df94dbbd22b7363a1b0e00adf08a0a9ab758b80ca41a1d19aaa6bd66471cd680f165635c3c7717ed4e59104fe331045169a804aa12d5e77abc428ff2376fca85e83253656cc886af709ada4d6371013452e490f30d9d0b6a3ff79bd9873bf46ecd6676b27fc0c02bec155798263d03501f0e980269f7eb29ec9292a7ef51577e0b32e6b20c16e0d69e14e7209a010653eda3fca23c3703350c19fc819221127cda1c59c0878b4bcda6044f587ecd014882867b0b21c8af53eef1da422e27e641463ac1908c9eb39e6623714e73320e08eb6a92aecfea47b3e784a11a04e5734834cd4cb91478ec39af31b7db455e0073c008dd05005dd4b85ed174e7d9ffe6294b22c2d80c8e58ae1bdc98f3bf4f01300751e9861b641ea22b65098d902caa1fd6746af750d90257cf926b88d9c20dc69b2994ae97f3bf6164f23a2219d067c023d69cca42b2490ec89376406dde042c9f17f760ae3acd595cd87790b50f9e057b158045df6eaf361a0bb607158a0dfb3300773742c09f00afd9ee7b4bac0427afd5e5d582301c1f6fc03817bc59070c82fb04b7c0e9080517fd3afc1b5ca9cbf57d28794c9dbb244abef2ab04580681f4d54fcdd008d38f79e89fffa7800e630916632adf0fa9705880360cf5270f9944f96810ed002c3849ebb07cee4ac582669d1732e8b6748e853899bbcb2f0ac1d784943d228b1428e69a15b9e6aa0fe3f2b187f2625aaaaaee5c5321c0150288e80bda256fd36f2a1e1f720cbff3a90c06860df5deac7f06708df5db97dc03 true -check_ring_signature ba5aa790aa88a565641c4f719ff26d2875b198ae13e5f2983d6e7eb28b2b0426 6ef9ab5a35f7a2f2cf6d1325499720a31aabc7d44f973fb0d83cf9279f781619 247 2835ecd665da540618544ea9fdf390dd90731c54063f474267d3be0c822cb4c6 051b66b46fcd592c5c1d5ee0c527696ea18499bd54b3b0c389f01e581d2c30cc 5db2fb6b7fbff81546277a7e46a8026a5e26bd6273bbdd2e19c9d56d8012951b 0189c280d99d6c6e77350f02db952ab0ee1cee6ce880da11bd984762a63713e2 69c6052edd79bef63c3ada57fe3dbaff37389f612c3f0215a9a69e07d4b86e68 834f2a997e46455f4f91490c200dc303b09bb2eb4b464481693fdd1a8fdf5492 2a0764472225e8905b3c585af1838741c19b20bb0cf14c8aebe54eba647ac886 4e2cba381854e71df7807a4e3316acc6789f049934583eb8e346d8189afbce12 48be5eeff2af5d30bb2318864f9ab1e2efb2091378e04f66b215a026ee6e4fdd dc76c20f5a4a27e616fb0bd389674ea82b1c1bb069d5bb53107f36d1e1a9a312 15137608dbb58511189deb76a91bb2fc7f842190cbc9b1a636cf600b2a807438 656a9c48a8b2d4c7c73f03995445042e0ddd2907287dda0a0c060561c115f718 4532b622578fec1dda7389187b3a4a467baaeb2c0fcf53b7762ff1c8547dc77b b88d9095cd9d3b8f6f6a05767a14904f950e2e8a533f8c88845d8502ad872604 674b21a341d6c413896b3ee1067c4871e37cc60475c4814af2349259c33156c1 f862c45cdd6cf271ad6b661c41c13b1a1999550add19ea27777d93421ada8f99 023413f86525b9042cda3bcc0799f16137099991ed4b4c5d55bbbc8cfb5a11b1 52b265e80ddcdc1ea46393e2c8c1cad48e06a75a37973dc908e4d29bc2712ade 60c0042d13f46338b8876fed414b80f8fa6a128c0dd668dccc26d8dc430fd7fa 26e835cf724ee675abfcfa6b7ec012d46aeec65a2e6f944893c58c834397be1a f124c9807d0301b302e8154488b5eff2dc8e06e61d18fb6dd71b361772ff3e1e 9ccb66589556d43b443a06d7ba9cb657bd9507d96073a80ef8bacf2bea4cce5f adededbada94dbc28b1330045af183cfcb68f7c5facfc387314603edceffad1d 05406f6a0de09f4aab01ad91cf7e1e14a1a67d3a53f39a0e58af93953b2825ad 86c900ade43c7ff935dac8a35510f3aad678771183995edc94700e0071d0396a 38054a09e15b194034fb88b01a34771b9ac29661537e939b7badb6cf92c7def5 a88b3f3a860dc0bb873513318b91c1fe0a46670b8621304efbf3620d82534a5f fce8ae0a6d29006dc2ac98043cb0e50282878ae85e131cd83d0f50c6c7cdd0fb f727f4ab94a10ab90d69826289650e06e8edcde782ffa41748dacd76a39da997 646a69958bc844f1333671156fc5c1f4bc5efdab41448d25518e1c2b822eedec e16ad4db13bab98d644594a4a62efbba6753b84fb66b39bff5ec15eef70a3bb3 b60823c5a412efaa0ee5ebb63c75c6f278e7fb80e38e6e9f15c4afc9995fc79d e92111409383e4fb31bbd15e1cd0a9e84ec5307feac24a8e15807ac93423f673 9c0cdfda4c2e4ba4a8512b8021afaa10d22c1aa733626ac21518fad868ddb5df 0a1fba9504594bfb3f43479454f527b277e8dd2dccdb86bb01158b6f41029543 25a336abc19f2aa8b5009373fb6e90eae818f85205b3083e1bc3421075a2cacc 0ec83e8d7270606ddebd330e85de5b0aad51e1e23d2440928fcc39239e9f40a5 4f0059b554be0353aaed1561dcc53ff5eaa9f8bb1ce42fa387afe1c2e59ed2e8 27dd1c40a6514954c4b60d0e376aa4e084b7357b81199daf06dd52e9054ec1c8 f73c68301e8f83a2592dbfb5999953ab29ed8900e638f402b19c4d2b621be42c 242831095d3bb7652473f351c80d8c15b0f3cc5ead5539b264b8d59976ae5499 7932008af18e7adbb7e2d23e175fc65daccd83716d11505874f5e2d80b0421a4 3c5b13a2339f198765d4f73f8044511d0bd4a8a4e039eb17f880d35708d30fa4 2951f9096884b7aaa4b38a2c8c7cb8288b12a493293d75b13f8ec5c9a161df8a 1032d3f8565eeac13fb672d8370f94d4124d6428e401ba2eec53f9be5b210dec 612747333f743e22dcdf96756377fdd18ede1639d9dea7a4e30e699263dffef9 2e94abbce88b025d1f5fda2a70bfec6e2324705d53dac2cdd928703678f8214c 010f2c0f67984029ca12751d89c82dfaab5602872a60d910442313c47b9d0289 14639bf3dabd73fe8cf439447a3c8adeefb92a9372b701939339afe62dcd5cd8 5e2c1c50c661a178c819571a830527ede08abb950053a2a6dc7394df821cc3f4 d408c133e9527a99224b317810a940df7942651ba71276845c4d3c9c8235a323 c5131b6f9bc02abeb221683154f14456f68431f4cee06acc3413f50ca3af10af cadd7618394a9f452f4d2b74e729c98b05c3aef09dbfd49cac4e8e284e91528d a5f9b9fb7e3a5ae9a6ed34859d1c3f3110244e8de9cf7665a265746934b06da6 7b2ddc892fee833af80a384101665505a9530cd289403c296eb574f8c536202b d9947fab4208c028ebf54681e99070ff3de4134fe741d99997fe5c0cbeffba9d c9f719401758f5d01a0b24bd5dfa92279f3a3cbdafbf9711e8da82bedd4621f1 104eeda814000ddeb60a9434ea77565338dd06072cf7391d0eeb3cc106a4aecd db574fcae69e3236facd75d822a3a1462a78b4ac9b8dd7c8e00132abb63915fe 558a2c37e4a9a49b5bc32549eb4f89c8f21e22295921c22f6df1586210e02e42 c446abca27035271e891698b08ba639394d158278190877678069737620fa04d 728c0425f36b17b020e9188a5ec059ee3f609659d4aa1f9c14371999a73b76d0 ebde0bcc2bf514fe24fe44c22b5265b142987cd16c91974043554296c78b24e1 d6ed60539a9291f74298977850e6bf46d7b0e8eba5090698b2a43e69926aa568 fd07e805dc8c8b6babb28110aca4b53562473f5cb41c491035a6ed938d788a31 bca928cf475098c1b2d68685dbfe88b7734b8460b08a7340caa26f4a632c2399 e0a3df653c6c6c8c64d548a1db846821b7eed10d06c82d8e930d67800edebc3f 8b421c652acb72d6be5b116e67c2a38465368de3964b3c6124f422e6139a86d0 9e30caee3a77aef0772e9408162dabec978efc80a4eda7904936371c8b2796a4 b5326c1939f4585b6a5b60ff706d8115574bfba042ce8380422c5d024a886eda 1913bef86a172e6772d5dfaca30760eafdbd9432a72ab107287882859b04e395 374d4a8730af24459758e8904e0d812d69c28ec8afb4465840ca93fb9718c763 1d5ca0580de06b66b4932fc4bca4d91217e04420030cbdb7d86a910fd5ec99fe 480011d4f809c7ef82697b67fccaed8c3f5cf5baa64177d71cfe0f439c46cde0 f8a495becf424a7cdc590507aec8cb9d9a2b659c544df0dad993bec47056dbd4 2ef44c9e3dd3b908ee14b16da36d99ba506b71c19b15e94d0442631c36e566e6 9cdb31bce041e46f211490611cfd65329248f8ab2658cb1cbc91bbea97177a93 1a1987bede55d1692e0f3dd0e3eb1fb353542c56d7490f6d275044f72017b004 2f3bdd54666382b4968093d9c20572b5783376a68a96913e05daae58307e8e1f 71caadb52f9a30b10023842f438861b418aa259a3cfc9c6f49b6722cf6bc771e 258efb47a8039a924e6b5313cdb89003ecdbbdbeeabef8ec770d61bfda3e1c3e 089779592f2449cc461db46ca64300c2055d62f58b09acdc42453c73bd4657b7 d1c80f80ee43cb9074979dac950ef878317853fbdc95c4213b305b660ad1f538 809443b2b5be8647eff6e6713dd65b159603d740101297971480e072109b4a53 6ede77d0cdd3f2c3d60f85d1997665549ca7fc91036c68bb5a3254732610e38d 711f064e362b1f38ef31aeee01c07047690ba57e9b5b413c6b097044912fb89e acdeb5b4a6ee0f2f4edf0b27782a6ad124d7afb00d0b3898c6ee2ba36f44faff 85651f76e9f8b4081103c0777fbc1c2f9af56f8845c9548242aa02dba67f2dbb b2010e5b41e57841ef8700853a15f6cb83eb1d5f984adb82382bf4d09112b3bf b4c79309edc691b4bbb538c0f6a6fd98e4096f2a0e9619c7f125e7f90df590c2 86ec0a063a35a397b7d870242318332b1a0377cf4d2f0a655f00d9a7664e0b37 b061ed88ba21b7118758741f5a0db39c9573da76f8d7c9baaa0ed464d50ccf55 e9df66d2ce466bc30e3e75310629c04c6380a2303b9dbf004c0ed2d1584a0313 c7305e4e0c573af525d8d1f8298eb72a99a84f32b0242b8eb3adf9fb0c9e665f afab03a8dedbc7fb70c6f68115230dfb51117ff55206cdc6bfa56d30c4ea21ca 5829b42704769a6baae67117b4fc1971a2d2b8f9b55a59e6f6c511a731665a4a e23ba42db760f545899c9b5de63db11a70c2410e5a4709e799c5b87738ef7b45 8e5c9f572329d40cafa80d02912b0d2199c6fa584f59bba612b7d7d468f07310 b7dc89ccebf51179c720049a075dab19e6add19bf020a2eb2131413c1c53e25c 1d9482e9195e2903b3ee6ab02fe68ec90bba0702807597b312c23097142c659c e1e84f24577acfdb1317a77d694143767eebf1d7301384147ee52877bb67d22f eb9be4fd831d3712ddf86cd3b03056fa560eba36956cb42b7efad41b6f54c022 a1e4d9b803678009217a64091c444c3f188e964860b407cc7a728bcad6efb3e0 58665316dd5bc1b4e4a85b36e2e48eb94faf63d24dcd941e70bf1d754f64d3f2 940c93364c8f4f36de6d47fa8c2b43d2edcd82c0984d73e717887dec53f02b4a e765fb2c5bd3ab5eeba07dfc505a7a31ab66b4e515ac8beb7054344f215ae0ce 902e0f4be8747332398633d58a01cfea2131eec09e626f3a3b94635a8ba0429b c44b313e522b948bd8a3c9868c7996f3e0403834465a98da5fea21be46a9a5ee 8257a5a1df89fc62cb0eb1c0e5caeecd7771687db7f310e71c2cd16acdd4b3c0 047087a44ab39ef4bec857c855240fcaec5ff2b320e35c1d26bd1b4315759e84 95f317247bcbc0a8732451985aea584d18953295ccd05249e1a2182c269f4457 823eba453895413bb7e0393becf96068fa40cb690fea61fdfbf894f6c8a1dcad 5a97f1997be092594d790c54ce0d42c324990f762ba82153b115e9188ebac432 557a4e69efbf4671897c8eb7513e9f81b969bad3394d45d4616b74edeb495c8f 9ec549d50b3abea31ed7a44ced596fa2414f744c66a157a50a29fcd8049c5a76 8977267b70729ea06ad0e016cd13d66b501bc0073afae1c8a0d487f54eddbf3a b44d6da2bc5ff1e562d8ba9bf04f76c4dd379316a91f67af1a28c74969b655b3 988dec4b95a00058188ed59488de8e9e2a22359b82bc41391145cb6e9463f005 edaece319f491eaffbe0f8bf5ce55728b59f93c52022f410655ddb598b98247b 4817caaec89420e48d3342799125c305db1c51a2661224b7be17cf85f5377935 2638c611c6c5b1a87abe95109ccfa9527acc55d2f7e75d532d0d7d25e31c58b7 32b5dbb2c79bbd4dc5c88c250f3e2b9efc2adc78892b6ded0a9054345c1f765f af98d2efd31b5c955d2142307f19c813dee1a873eb230036d4a132a37c5e38c6 7121a6a4350818e954cbb03034671fd4e4bf863b91bf25b3482b72553c7b9a14 a7af9f043e49c734be80fbf022e71a504ba9832408208e537ea580de5140d724 57cc8fa7de90039172a1930426643c600beb02f836cc0cfc396bc9f0d86facc9 62f9323631a8506b05ad25ac5e9f2561d3546af9012f1fba8dbdb3348da94110 ba538d476315dac3b7b2e139eb2a3a2e78a075278cddc821e1a6f2e955d503a7 feb3141637817d04cf54a6face9cf51a59da24c3a92a0717f268a7b1fef7a091 0993bd85fff2e3cf0b45439c0b537bde2cc1397020710a1c5409f5a91a799504 3f49ae378b3c0d39fe3f186571894fe599a5bd948897035767b769e35b3f8c47 de1162425561d68dc8f055ee387ceffe4a8b795019dfbc764b95cc21c141c708 0a9cf71534728c643ffdf5228d222e32fe1f28742f68e5f4a2e82c713fd77e28 c5897f4e5e563782ee0bde941c13b0a4319820f079d02e5a5ee0ffb8ffa2bfb6 f24e5fb29e7a74a3bef79e485849e607edf010a43715feb4092bbb8db8a80599 66c09bb3e5a24ecb92ac0c27ee04b178555278260fb088f7b9940c264181d39c 70fe379e13d4768553b4e2bd2e3fc1db72d04e73f83916023cc74ccfcbcdde04 5c4a29c9c10618fef2573bebfa02b213b2fd3697ebf9005fcf7e1894d466d62f b09c7439b594162ee9d222a30dbb8450d404a108d891552526a9b72ca3aa77e7 dece6e0e0317e0c2733e2189cdf9f148d22f31ad29f1940922953b88b7ea4733 2b8443b7817a15db574afb9c493d41a97978dcfb0271d88cdaca5e494f2f9e58 da59aceba1bf7d41bc0150b25d353c07f07686cf5a6d45b3ecf0067d61880e75 d462b59f4703e54fe7d227e5b71034c763a4d0486d5d266558497ab9a86d20d7 4b85045d952f27ecf22f3ce6e8254a05f669625f4ba239b1c40fbeba1d132d8b 3218c7b344f98317dca978e92b7a1708845a5afc83e7d7952ae55847244c8132 3cc379bba4bd9ba69fd24f2da16ff8edcb6ee56c0b53215ac1453453ad5e09ac c360f1e979cb0625a5fa903228c390c7186f247a42878fb237289362a7604c0a 7d32b77c2c907d4f6dd858741544be5afc7201f6e4e3dbecf6d0fae21788b344 c2ecf93ff86824988c76d6d8832ddbfc0a080e6739cba3a44c1f68cb1d8429e4 21a51916c45114a183e6c87297092856d2579d8190c02df04eee1ee51f235919 901684b36c1fb32245416bc63af4582f3ec91208fdf5e05dac5d954e2a238ec0 ba9937065455e48f54baf7fb922e941db7fe7ce80628a648440cffc837fca389 878c1475b14281622067220f24c7c0436a7a85e933644f727a2d02c7d400f13e f5cc54c8b146479883fac3c6723570cd2a0265a797f2c3b3d30f8a2d6dfb8df2 e0943bc6339cbe0846f1f3943b3edd38402f05b5124893c36f089b7d77530684 209357162d9da21665e1a02823dcfb8cacc288c8cdbcc02f34610dd0ebb57efb ec502de294b8e97b0fdeb584b5e77912f3a0d38667b2e7a9d7e1f3fba5c6dd56 0942c2074cdec8786b62adbbbfc83f982e6d55b9f24649799daab0380f983064 bde8d14db9c66f3581675125449522a60231894cb306b69c18030ca6f9566480 6d478441781d1883ecc8059d5d96ea5ff2d3514983559c3fa1bd9ccb0164a011 7020c64512771c5efb5dd5b63b195cc5f7f3db57171862795745e14c67cbf39e 38736eb747804ad5b9018584d77ccf176448b1d71a6bd40fde670fddfcc3fffd 8ceca35e9d95c405bca19245e0587fa949697f83121ad279166e5113c11851fe dc4591d5067314f9e712307bc831ca2629b2d2b99090b4a591b3a11a52c5017b d39451138ee61ee986678f5c5f88d0b4aa88e97bff8f6615444e8f3af13ec6cf a7a46f90ad654be0a9e0ab51f8069c6e5d7b757f6dbe1d78620fd532bd5fca3e c090ac1285da99ef9c41e102c0df6d7855c170aec4a66c0c54763d327aa23ef8 0dc257e39d9374280deb9f62a365edfdc512124295c55c2bc058905f3e09f658 16f3e7369d160b0af51bf72447a8e469dadebfa0a2f1d1fb373bf157ae0f2943 d6c3eb4f1f6156dde26ae05236c6e24294c8a2995d14d4300ddcfb3c83621f75 87aa73f8280df9167250bbe4b447e865280796acff69eb60aed6b0fc84fadd4c 424713db09e211a1dfe7d4bb500f723b42d4ba2c80af822d3e7541f2dca1dd5d 60c2f13fca146533878fb27197079c081aef782359b03eadaf0fd058aaa73228 c7d72a9057c8b926fb8dff86a3b5546d49668c15008574c193519f9d36f43ba3 de0ed3affe10ff37e0642b269efdb17e2ccbeb4598bcb736762debf545d98adc 4220e6a1671e2999794cf2600ee5f1ea2809268224a710b25a5ba4afc845d37f 3fcc1692998775c4d1efe0a036f7189b7825125003a8cd210d60b597ea121cf7 4626f83d20089a0dcff965da674264eeb9ab0ea6fab68f2dd9075fdd16ea110e c4354ac2cdebe31cb013f1b43364a6bbdd45d57d1cf20c7a3cb402c67726124d 74480a01b81f60e064a4d39d30e15641f193566d32a4d0a422792a647d05665c 1815ce92ac0fbcd0e9ef5176d1aa6dc1d1e45062a8d4909a7d10f58d5a8c637b 901f36f9147f8d98b5348d70517a35d3b0bcbe5da07c99ef04ccb10af8ac5311 dca792ac4ab8205a890199bc7488f4423617347483c727f8a28da792f011f928 e297b8bc5d3831630b415578e9374268cb0d0070262fec80360e1e34e6a4cbca 2b3f79342cc087e2a953254bad2c73104c1baa1c67b1c9005794fd3f12f406ea 5535157392302edd10c307b98323d3a55a6449fb320e43342a47239064aae552 9c7d80e8a6e337fd054db3ea8b240a799a9bd590f2a70c3d23b57e0a888bb7df cad72e250e2b4608cb27833ffe1d8b8a8e16909f57560f9f44f8c30f64f7ad7e cdc47188c7de8748d70496e6d05daf2a7a67d88ea9a29c997043ad2302f7b5cd 94932267e26ea7827057a5afdce7b41902cc076abb7df6c435e61e230d8df45b ac2448f62adac9eb04aa5adfbf54a38d66817eaaf37b2b508490997401ed202f 41f0df15c35c67fc1ad16b9b48e7c91a7f8aa23268f7fc30d1c4d4c5a7d7256e 6564da7b1315d1ec426e1809f5520eb339c209f37bcbb7b757f8f926e688dc0f 1d89b455a1b82f41468d86c9d3a9c6615fffd772628bc44827b8fa962fce6da1 f725a6c357ef4ab54dacd98347adac9d040a2f99e61751c153ebf230c0c0786a d62b032fc071f23f8a1cde753f0a5d7d6f29352f8d90a64285ede18b9b079644 6bb0cdc27f719db1aa4d69c95e6ac9e799653efe5d76e37e1817552df6393dcb 5d554b8b200253b33c182d6fef9798dd9c63d74b3501aa2d248c2695b1d3ad2a a3cc222e9a208683e5b2295a73c691608a46215f2e1769b82c7c878df51892d7 3072b5c91825a59582209e9e7b75b0d95163a03bff5fd4a53754b181e9fccc87 c6443c5a423ee7a6b09d83a85f3eb196ac328d8ada604edfb705c7008b327529 195580863445070ee3e5ca71657c133ea57316a816c5bb09deb6d74f1ddc44f9 bbdf319e1154fd05d3808da2c10b7de5a0c730d8d05e94c3b701596a5a0ad98c 5676de856d9c0ecedf0f8445222635175b467e540107ad9ab9637d45d22663c6 1bd2125da88fc57b3760eca74db17e86323c62c4c8a7629d42f9135543f06ee6 7f6f8dd39d8a7fe3613a0aa8f485da3ae89bd5f3cad47604ce685b936b6be4ff e795c89ae1f4fe3030dafcd6bc21ea91ed42105fa3e88f9cbd5046dd6cae3fcb dcf23d5249b74064bd25f42b5ab083ffb0322f7b6a67f2f185f253b0ce334a86 bf4b1bf8b009fda1e803375ee70733399b580ddec8191aa7778d2b87a88d27eb a02140ff2535921cf01b14cde72bf35231a537974292ff4de256d201893ce47d 60c02aa9963e480d46e5e7d2839f8372db0828da901383018f44c8a2a52a139d 109637f2abe262f523210054f6cd7b230c3a92e0b5daaa4a4d1dfa4769fd2f31 cd70ac0674c7a2474b341dbea038f426ce3d8c2053e48adedc37b605ccbd4472 5122103dae77c0974eb5bd6a2a6a5fecc0e86ec850c0d72b685149ac68a3a5b7 444e5aca2ad6a0233230f6df38d63f3add2de336363ac3ccfc656d1d6cf1e0aa 36db035754d371343930cfe61a6b1ed6a3be254b6f0845939df1d925c0db4af6 42cafdbc70c3907098d797113302371a539fb127488b73771f2787af119d693b e012ef7d6a4dd51851e3d304fe7465d4682e608dbe23fbd663b7c16133ee2cd0 fac0ed72e1c0edd5b382d8bba2c4144fb2ad51c1f8f27e33eb07b755dc5ce905 664dd6a274b837355ceffa50669b468f90e0d2dd0ca24e1a0a3d0e4051f7b87f 97c5e0d417c4cbe28d4f69cec58af1ca6046e0c2f7833cc55a1875b1ae74ace9 2065057d224f5fca6ae734d867dd0e1710bde567244e3766700ca817e5079e7f 490a4bbf67bd8afb4f47d2c0f343024496232aaf953cb436502a146b3ba862e4 7907cc9d2bcf271dc8255fa1a53fefed1b3a29936775f2886e90df6e83ca9192 58725915509b529e72ed3029ffb730d5e6ab868e787bc921486010ff18c10ee4 d4b3d1e8b00af1a11ad89b2f846904009bd2ba4d1c105f68e57e2ad99276cbe5 e2288531d95d62e4c93f816df12b771d6e8ac68a1dd094896ce1d9db582e937c b9aceaf4d5ab9ccf20d8df1ba1c7361e6936565a5c121fa4c5968bd53bf160ca c4a4d5dcd7a1923a2c8709a7f0a7f30bdac88cfa70f27648a02aa0f4d785de93 9b27f8dc4960c074e97a8eec610546da8bb153aaad089ff105d18c89a42cbf21 6959640403eb49fce511c4c6cf260c591cbf58370cdbd8842d8e655a87fc2f32 270653fbbeb96f95770278faa8d6b517a40ef682f0444c6a6b7eced7a0d06b58 0723daae4f938b3bc7a24ab970670a1dd8289f4dfcc400b9ed03593dcc80f67a a9ad6d359fae941fd588e6e0a3204d06796aa889db30bacc55b8385945e2ac66 acd894c6552b9b3b1d1b6306e83d88d2e390d6aaaaf83d8b241329b66f9a6cb2 f06163b64189c13677dd5f2410fde5e6ac6897c8f9311b33d4c8b689337b98eb 8f191f811b86d4dce9da3b6085b4155216f7bf7d8d49290407fce327beeca5b8 bd7c4aa5a20be0359d4ff9368c40261788a9026023386efc9b174aa9c4e1dd1c bc9d642740d10b85529430efa591c0e3ea915faa8d3fd0b4daf70d8d0311d37c 6179cec5ac37598ae27961bd8e898b24a1f770ce0b98b30877ba199ebbc55fba 8647337fb762e7b1b54ce083b055a6c6af6088edd102bb5f88d7313c8bb8218a 2c0805a08e82b816b946f7e7affb51b05c6ae43260a52dad6e54f1eb8e01f113 f5aa41c40f126d48a29cde7ca39ea558abee54dad5e49b9d723df827f5d0a307 e07fa4cd3a1a503fb03a6867e8bc9471150b2b28dde140b2af9b69ae81bc1f0a f51bacdd2282cba56a0e0c1a0f377efd29d9f85ee353632388e5a8415bad59cf 6bf1f293d1da442409f573e36a0e04b226d5d08c8635fc69064339de84163413 21d99d6217a9feaf86bd70d0804c917ec71ad460feeba4dc48536582298b6a36 04429b3a01832e4217cb974db7d712cdfc1a010d0ecd450305ac03fbbb3c85081f559c6bc31cf21e8bd6749aa7c5c8c1b74ba0cba243e45af1e531ee033cc300391f0b80cd4ec045a25aa6e58b72776687a0f74b6dd55810922b8539b71b9f0ba231d2b3c55ce0f94c4523c8ca57f2a0505181554792a347f507b6de26120500396ca276f53f8c45d6c0bcd772486e94b80f9426dca15d920cc143f1359a66011848981b995b7152afa9fe178bb560c9e6a8afbcfcbd73a4ef26ae76f785110cc141da8eef88aa86c502425920895bc90adfbdcb0345337c9ff0112ebead1400f3a4c1bab126a444ed2585d04c8cd7ed9c1cf4a41c447e258dc037bf953f7503b818c94a20680093fe0873002f179be61f2fa141227795eef350fecd9c5ade09117e20f2a7a22d7e31e34a9b938b67ea4f770e9036481f12b3c50d4511508501ef41c65c542ebd327e4008ee0facb21bef9ce3c986da32afe07db27ba323170d54ac767f29132874b0f435d59f5e8f8fe09dc370be47bbc1b462ef452a03850ebc6016614e63e4a27d54bba255b86aa41b2d8531fd2d8604253ed227f5a4410448979214cf76761b4920bcb078cd90d88fb90318fad076685218ae92784f760235b05b58ac3657707c8709a7a81eb3118d921af0e33e1468b7b6bc44c6a77b0238cd0fe495637113e6b1b959c7d36b3cb207bf099d3b0bfa53b9ec03136ec70a2f8098559794b8701d6127412e5afbf11f666b1a38d0a74806f5f60ff1dd7a0ff3a8b127a683c44a1c8ebc4cdbdc4569203aa29558d1944eaa347b0971c9cf0a96790585ca3aa8198ffcc445586a86e332dc7b6d7b0047fa86295308a171c1061f4dec653346703ebb0bd28f3b85117cd0f549483c6092458f93c83ff630940227448516ced608a84e411b5912806883a4da6306173c81dd5b6a8f36c8a4b70f4e8bfb2bce074136ce3bf12f746e53029f43e54a1a12d1fea2fd72f4cbdc450f9476774bced8fe68b0648b5dee5905d79b1bc4a8c86fe7a558279879ff6bab0a349543592dedb0f1dd97b330d49bd37d9e9e3e327966432b79d314b96ce4cf0334b129b3cad9fd1bbd58e91f8397f41aeee0880fb1e7e5b8cd2e21010abaa90b3d63f0dbe69c4e751b93a9f2fdd0bb3876d1aa26ce25c72462c99d296a87e20fc9206b0e2758e0687d52c6ee7c5706d2da4adcc18af8b9b7c5ca3584b903560cc09bb0bdba08bf9e8ece24efe1d3966541a376438126ae9a669f93d0db0f9c0172f48c2542c1d5c1218d3aca541bf1e148536889c26551aaf18ca182d621780e6014a1f95ad766ff873455557349f5dc2fd237d485471a93f5bd9f99ece3d50c201db39c47bf90de6abd63c0836b244de93227fdfeb8843f9204bd4149675f0085f62b440f1dd496efa924fa4c3733a1a8723b8bef94d30b128e5b928c800d0ecec49134850565a94f9421e1119f9dcd2da6953844537e1b0c893bead8500f0a7493f58cd3a0f5a21fa3907c11d58fc0d37341de97b9f50f03d4f4baf135500c0c81bba774597f8ebc5e17fee3324feaff01519887dfb3b31fed8015e0540209c51b1c44387a4b74d59312c5eac3e00a16309f78a4509fe75931e243d2ff59078bab13fe263c3c6a4ba89915a36f1371447fe177a8528ccfa5ae121c3838c50d6933a3f853d2bf8e51734b474e7985cfe69f4303d411c1f6761587fe1110700d8aae1a560fafaaae0dc9d0df50c1ac259482a21b4e2a0f7526eab30b7a3ee40b9433139920e5f45d1c831e7820e4481ae28df874552652f7819178212b72be0c3b8be3ad55f36b04378f2c475709f7014a7c60eb4e699f6c29607babee4a0a0fda2bebfdd225c7d2d67851e33df78b7e8ecdad2ea08e5d38256e5d7299d7a6081336b136fc9e9c49a66f337f948fcbdab653b3b68da768a86ab7019c0db5ab0e8b76c3619e8d662e78ab3c93879d7b949e5fb7a7587ec152a105d15810b031091c1744c819af54d459702e393c57401af6d76cd686743271f00f2f10f0660903f3c32a200146c93162421312e3861c66520b30df69326b740e5294cb2ec32c014d6f9706d6214daf870a8a018b9456eda97b0ebf88ed02336be58b2e6c88ed021153838597ff3d2d5962620e436ca46a685480d7f3962480aac8187127a7ff05bf5253b0828933451c36b183013ee3a55aee870a2eb0d228e5ed869306345d0401af4680344475f68a49f24f6f635055ed335f373e3759327c86c9801247960e1af27b5a3ff188dc5da9bb7db2fb36296d9f427ddea867c9f3a3ef6820809c0990ca5f7f07870d23d28ceb637b03f8523d0bc91988c94b5a945225c5a3ed0f07db0b01ba8303a89ce058c2d4fc933508ebb99ae1f06822968db3b67a38856d035cb72cc3cbe75065bbfecd704271c207fc72938141d4e0df15970783312a2a07050ebce1648841245d642f1db78abeffa2355851050b8f217aa562481238260a35a81d85e01e4a17197a133f3e2fd6e3ab1e41dd9b357220ebf3874c6a5fd2050b1003e6d773a9db168af804427343470c9cc882f183cbedfa4f208b0650030fc63e74a4251982d5f92e7d42121fb4ff14488a9f3c60060fc66d342d823cd408eed83ef9793c55e697387b06d61ce64470b7c1934553e998c222308e22e3230d291683fceb2c80755d21d0f6db590b35c98f7f9eabfbb18fe94540609faf6b0298b77aaa0d5dd9dd5fe3733c0a2811bba967256fd39652b8c3561fb13877f5029200edef50a47e0af20d688c5a4dab4649e0cf1bf4d64845f4eb9abc5943f902ff90ec29ee159b466410444dba1b608d977424bd63dcab1b115f5530e20604012e9a29b0aebf3ea248c835cf7660791c35333dba6a820c437c45d487155e5702598c93cf1d4c45d69f2c2851aa2d98b01baf7767d90cc37081e93b497cb3470924b628bf834931ffc1fee9b17f5fb463b7c1d6b6284f2c1ae32abd872ff33101d9b98db437f4005caef7875063b3f7b03255b18a11948336f5eed5d1ff8b210b63940eb28d024e5f22e157ed32929188674b63602770800fb77dc09de7117a0b034d231a9dfa1dfb155a67c059de3cb3f4fb40c45609d3ec449b4ac4d16b3d0b41578c36e7aa6002f4d2e1bc1d17ccda1e87866960c7efc1a925d84a1209660161bebbe447007e020c6689216d3e6801e484c00a305baee529af867c404d0407c46d27e7bf7e633976e73e25ec5e95ef019a87cc8de4d602e85b280298c55c001c3ea6ee9602bda3b30797041f28bd166f4fa5b5175a669bf7a8414940a0930967fe6e5054e8f4f75935c3703868c5c20c0ec7db9371a802173805f44690c80f809cd5f3db71ca8da2584db4b79f2d3cb42456b491ce964b2a3d206db28d990d75bd3b080edb683d244f885d1bb6438cd1a8e48ac4212846ffc4a04de02bec06b05a1c05e6aaec8d10db1a0c2d85e22df6457696e0566874eedc69f45d7fcd0e25f127d395faefac9e061eece709b7075f87e6295eee1748d2ec82e8a797c006d08fb632b0d20ebc0738e088fc288dd0c99f156805affa98eebfc5c4de9602086da7cc10143603cd31ecc8c6a6698f51fdc51bcefb2c14c286d52c33bfaa80088643560e2553d269b56ae9ece52f1842ce2ca58a44543c21884d085263eec00b8a189115f67941bf83363079c6b74dbe4ac852d67bcd4252d0c13df5bfc02f02ac7deff3fa754f15d926f30d15c60dc963d102d32630993d44db389f1a3a0b03127c3b91e46190c34fbc4bb89cffce0583deddb78e9b2e38e5c9bbb6a320b809215c378a64018e8478e117c89722c7095be9d436720abb43002e170e3d53170a665542f2102869e27f5e26453cdf915fba9249b6f0670896f9851ee553fc0505a18cbd61b58c0a13d431f50475bb0b5b15ed49b087e3323436aabf3545a5800f3747be9413c6f5f1c740a4178b697ae869e443d8fd2d4260f1d61fedf7fd54003f10741fce5bb02c15e2aea43037ce8c03cbdfca1b18844e2138f3666925bf0fb5fcdb2faf0e84ba2cde4c62184978be0712abf93c623dccf93f072d48d8af06a70466bba91329fcb7d0fb34a521991149210f09a55a2a6bc34439a6e3987e0e1fec0a91b01f5990e6134b418896f3ef879955c16516119a01727ca2bec462024c333e4dd8fd6fc4aeb4c56f584db7031bca1863763661559a14d5d94db9fd0bfc41fb982655781a152e2f2e04ff46361e59aa405a56c56bc987319a70b8aa04fa2a2b62c1a97cc989ef6d6318b37fee22edf5bf220864d7abc36fec9b7d460532e020be7ec6d5072c123ecafce36c01b8ee13da9207043510ad24f39c210b08a93a764cd40e65cefeec14151eb1ed0c055e3ea64aea5a874a21cf66a75a9c0f68210b1e6626e75d09888a5563a4b3cfbe74bbd58f5dbd7e513ae31f20b56b0953d079e20c81defa377bfff144a5dcf7a26df5ebecabb89fa0ff8f496d48c006618c9083317f0cf62cc9ba1cc3e7fd7d49d83106be72f80ddfd88d04fa6eeb0c7714a5e0d3fee611b400d559f05762c725e334c2660de3d249d45414137f6809d03f6b3b32b2baeec6ee9863119d1944dda35e69123555109e6d31f5b056ad0023b4e1f6b594a81544fc6eed606b1359865eb035912549a84622c78535381406c2e7813f5287c32173a2eede51d16eef03981b2fdb7663fe49cba4692949da0522669949ca78afd942a37718da2c1a046b0d4c9b888f8e3bd601f5caf63ba2056ec7d481146d1165df7c4d981f1d22ec8b592ef944a4e6d3ba42eefb63f1ef0832cbcc69d99489d29589225b295e32ecdf842f2a647982505974adf5cdd1ad06d1b3eef0d327f5ef38a1f95e882a1c382d23428a24a6bb3fb2f72b57b4b5bf02a59d4d45c179737ec69988e4682c5dce792caf920ec447fbd417758d7ed1540a4cae43543f7819613718d031d5e419da9ae9356685e51f1c32988b565780d5082b2e8f07c243f027984984c25be391541ca705eca2e921b8461ea8ca31506b0e7539d2d5ebc1ee9a4ce7b8eb6e239916b9dc79884b71c07c0aadc58f75b6540f28cc2b5a649a653e14fe7d70cd0afe6102e1f2ecd848cc25d65f3161ef54080d8e970a19c5c97b00bf47e7be16cbaa1a1c0f6829d81058b0f87caa12406ef0027e4a6855ed3d3df05a2f913d78feefc253b4bea622b46ff41e6b69bb4fe95d068245fd82b3ee9e0a82e541ac4a144a5a1f55c8f4f1aebad54f1041c707742c00886236454ebb409b552fc1fb7b78138b61e4afd7b33e11d53eb82d6b5d5f2208622b12593e63fe60ad3d87378deb6f6e586e3f00503d25bde8af03af93bc47066da6a124fbee9bd23e59b70d0d2e657b67aee1a9fd87d9cda94d40df45a9f7002dfb769d17bc37b059462ced686ea28a4b376b1719c9f67bbfc90607aa1a2e0ce3dc8e668935495584db87b6b35d75cc4ba93e371bde4adee5193323ff82b4012547995210d5ec434ff5cb7863ce6acaf54676481caca588c6c17af9e8fef00318d44f5c278b567cf42a652bfa084c5c4a5abe021f4d7e1d616887f68a6ed400473d1aac62c6e2a79d3383593307f170b1767c2ad48ffe0691370ac74ab8af08af8edf1083a9a80b8f460acc6a5ce1aad7f277e40b3ea4c5cfb49e5b105da608d18e5c567c26cb63f65568dc657b063f23528d2f8e46cc473092367f9e3d7b0fb41226fc5f624680c6b2578d98c36835b75fa13fd8914dd0bd9f50efa35f560b009e1e6f183a9c20c699a95eda6ba13dbdd028b78ccbd5567ed58974323b260e0d92dbe3ed93e1b61414e93362f3282085e09db1f990ea181a58d1bf5cae3a0062aa5a4f4379e8b1e9b4c39e96b09d1dfc030a608ad24aeb49cd0c281a84850a58d04463fd46f22789ce6c2353b5a28f3556c78ad0326e3a3cca81b7a671380a03f7f4cbe844be1d03263b6c966d2af26676edf3fa2491af2804ff476be6da017460363ff163b42ed815c6fb1fb48a2ed4cdb1c6874cd75d62160c4f05190b0454545aa6b650dede0c6a7e10e8c195c52e7556beff7aaf13917fce8c2d0eb102514b5ce2107261a2ce529b52b31ce7d3bf796deb2c90c1b8ffaf650bf664850c25ce97e86fe4f8e988592c5651a1cde95399ff038ab27addc9a59b9380bf3f09d34687d229342935f4462ebb5adbf1b873f59e445bf517b779b5bb50baf8290feab870baf39da03e1815fe78773efc0e634db049c458a96f3d5c45d0750ebf06ebdef7a0ad399b163823db1d7a3968547cef28402a2aab1cc33dec927144420c7206f69d6ee3861303462daedc8347e82d81dbe33e6a3f7f9b1735cb3a2c7f0b41d325e8ead60beaadc48e16572949df349c46b294e4aaf08e1c94ce4f639c0fbea6550af262a0d375d47e15e691edc7da46892373837a9b4bc7b5eaf43e5a0be67d0a7c4410e9af532ec5070107a696f4bdb5f71478bffe596f964d46fc5a031be12f924368ea90b8f9b3d6ae61f668ae9ca4ba2422d888fa79ed3e4527770415725378ebdd6fbe46c761da56a63331578665821964520af0fa5feef33ff90beff27eb1172442a08f21855644724cdd4fc8de70be1b91df02cae13610c93a00cc640935e41a3f31de69f152cbfa1151a8544110a5a66b316478829e4126cd0cb4fd5989c0d51e4d7b8eec0bc002881ba84d423a5304a3a1bfb358a32a528002f8e2ee442ad7a749dad36f28387504eff270421cc9229b2da799b288217c96003415f2724a521ff04449c316078ede3de2a740c4141dd234ef8d4ce09d8db80a84682d69c2951094cdbcd976da65a8352127e649fd78b96599c214bb7dbec7017bfad3df3ae396b2bbbf75ae3b81705456426678aed4d9d4a903901d96e3c60f77b45fb4f6fe5854c1a8cccbd82881df02410c4af89245f3c07eae24e3a2060845c01eaa713522524b6808c3a84e22da3c756821a754b5faa4143675ea34f8006623616c75a9983d344418180676c9a107b488c2a4023ffc2200764605a56900564aa6a16bf1ceff91b151a8e9f789f267f24b36868f534c1bb89de5f9e84b0c13bed8307829f27518f3e55089b01d2c269df2033f363aba80ace27e6995a30d3e30396890b6e7f17828ac676c353651c2e3ca787769813b4f57da9b6dea3d05597bd0458249686ca652cf0c4f2acc96970d2f9f13ba23c780db1689df944e0bdfc2dcbb68b72d033b75571512eaf629e6f01da41b765c22bc3a0d6ef447ea0eb3302e3870586d69d28c96c971f7c50aba4c68ee247d33adfc8b6ba481362a0f56d361b8bd21863df0aa8189d7a4fc4275f887790f5373446b867dfbc3c32b0d58735e42405a30453465a95f1ed54a796248fbec0078c11c9395a64532cbcf0c0a73303ca394612da353c270adf72f537d4dab5b6d05e74722fc7dfe9552f90b45cdcf87f977c9afdc63c38d00dba41fcc25ca17d247865b39be47be4abbfb02c23d2449edf044e6f0909093dae74d6ea72a3bc827758d08bf28e4041cdaab0445ecfc1a1b172f56fa2dd2cbce0e6163bc7933ba90a64c88e07b04491aacfb0c73b60a2be2f02b85ca3a92dc44761808cc1dd259d818f894dac954f112324101e128cd2bacdb699f36459dccf9c3bf0f463892201c6812d50d52ebcbb8f58501880291b699bba350418da905ef344283b9a2876b470d67a7838593254c19ed048940fb060d631ace82cfceb1b28dab324c7efb8803429b57c2fe2396de62aa047519ed916eab98371bc2d3ada1cc223acd07ca4633a044f4c584e3c566f8b30420c861f102377d35b955b37541c323186f751e6ebdd27517340811def08f9a0e2012746c2ce0ea4850a5097806452140ad9f9cc678d7b1606462ea188ae557007266fdda1e1436c7badbc3f567cba9d7858efd005fe32b74e7573d5913a91202ea2380ce2d4c4a99599b7193b6ec7147b6b7db624d570d957094268cbc48e009e109a455ac4474d0f97ab2b5acc95bc26566e702acd6dc3cc6007575d2a2930a878f4d5ff53d8536b50b8d83fa2e4ad7a915bc4a6726d14f3069aa9055289901673a048a2566c30e1b5c94fe10feda3e651fb69508f77f9785768e10eae1b00aeab17ad8d6eb4bd52a82f77065875278197df4abb559612727a2d626670348045a3da98c11b65eb43a16e9b85b447f16562bbb538de1aeee5f8c8d45d1875809933358d72f89788d1cd3043a079e96b2c2d970ecc776c91200cddb66e675860479f5de3f33f6bd3dc68a6da04dd8ff30e267cb6eda5685a050a2101a36f3340a5a4896cc9b8d169f58a477b07f827b083530dae8cd5a39e25a385d342cc105064d2d828be37bf6f9415d83994a2b7849507eccd7caec1bc133a45040c59e100ab3bdded9f16d1669ae9bacdd268fa3de3f08f89b41cdad79683c3abda8bccc0d8af0b2d11ac52ec28e21b5826fc9089adae20c46043c2e910b3cff40a49ccf0a9b580b1ed6494b35b2940dba9d2275ee019f9844b95ce9f6ae1ab951207383047e256e865415c8350030cbf4946579df51f1b2f4203f3f70823419220a5249090243fea78ad2abbec7985af23bcb359c248bbbff5f503a8bb4a984342baae504065fade7c97ed88dae461b3c9474ca4f3d3dadada04ed3589eff748f3d14b10031eabdd0fccb698f726d0076eb5fc424b2a23fccfeb6dc032582f589d3e16a05baecaa7eb7e27e8504a48c2acbf87c3cd7dfeaddabbc192282d98e18341241092faac05740c6f4f9ec79ce60a32eba44d6147642dcee56660ec97f98b5a1970275a6d41f6e89e3ccfbf20b8843f58f210b1cc25f6bc0dc393c27790770f6a50dc8d0a497e37579b02f6c10753cfbc40aaf0d79489523c21ec0464c1621529e04afa505eb82e61a87ad4b7eb41aa6ea571d2180c56f1d4936f414e6c61763de0928f0489055ae9c396c275bbf25cb26a2fe6df8652d91a5e5f72a01210096c008b1eafe1d6955aa6f7e25b739e380f8bb313f39dc4fcc1628783503c802006608fc138e1196c8483917739d31ba8be363befb92eaef4caba108681d5ca6f6060254f9667bcc4c78573789dc71b35be17762746657291862a4e511618db938330a0e050341f23ac0b1bb960d788ea505eab345bc53b14a5169983ed7ec2411c80fd65b43634daa0ae2edff9e2abf94125a458ebdd2baf54a4b8d99b785c11ba40c2eeeec5be1f2142e6242c12cf8aa5de21a4c5879b79987ba2bc50e99988cea01b31f440cad3aaa5edd3fec7d34414997d4713f15c31484205e41b66dd9939a0e1bc397395d1d0413889d16e4292e77b7ebc3d22266111de6372dcd6b833c8a0f136ed57e5832034be42e449449c2c22081f14ce71b6f431947b2796625a8ab034d123aeb28c0b0e2e9ccda66aa1820f73bd0dd255940b0464dc9c1eb5094aa0f41c2a2f9e71c202cbdd49274322de5fe885b0842cfe2f8ed78697029280d2301d4b7adf30690195f205d4a8e195b3d4885703d861da43624486c2e098f0c990e8cbed555405bc297d4b35ba62e931ee7afcb154d1724fcdd4f522178759d8e0aa0faffa4b57b2bf7b14654b98bf47611e07c0fdc2989ffef8b731a8e8a0676019871080f30c7e1aa4ef9fd5bc3fdef13bd54a78d52a86a80d3efcfb0b90f560044076165d07964a084c7464c8566019b050797cdf99440e3ce805a86993cc70f37aed66d2c4a16a337301d85b450c597ef5b7ee73892d1f22bcf65559683dd0a0bcd990a34074fd3989f2be64f178ea3f332c05ea6f2a9484d6f67141d3f620a2b1ba305b07cf3f362080847f6b8cd78f218972ae9968a030bdd205e87f0c1007930c6f8b62667f8b0065626642124304de4556cf37dcfa5f3ae90ed434df4028462adefadf6bf3937f26bf9f53bc2750a4b8b4187a946401f5944ba11fd1a0b8128384fa6a84a35988c9b596760dfe1ee4f988cd87209b86cfe4fc8c3e5fb0ea6544ae7190084298c94dcb4d5af201a1d09e14156d940f290d279f6b95f7000d4e35c2c3183b47a75d43a4beee7df44fb1b7f7eea2d17c99e6987674079bf0da795b73fee0f0fb3b8cd3574044a291dfa32383f9d3d6f4155121cb1223d700bab129fe0bc3fcd4b689607b599b8e9300d4c0412c131112341f2c046cbfdaa020f19cc980ca7b460de773823407da3cbe94328bc9d469164a4ea23dcdb0c590490fabd1af3117efa5a4c2f8a281a9134cefebb7b5f247658ad1114f932a0b10405513dbab0a7b95d7635e1d32d760e79fb1b78584423b1eccead0ec8796b21026671f41874f78f1af4be5a4d6ccd278ed9794578d5fd89fd65623ae9b2f1810ca56624dd8bb42ef2c4da375844ce15ed809995def07a59737d20448e174464071f007f64e8b1915271a1255520407c5b773e327eeeab4054e2562b8f37a6ed008f81a2c36c1de80cb94e112bf77e8ddf8a2af5564ccd52203c2bf9e1a4c3d20658cdf064e2e925efc3d323d53d1d0b537364adf8c9fa0f203d9fa5150122b408edce8ba0b26b5e3f2218c26645e1fbe23bdf6eb1665be66532fd2293f810550ab2caf122f0ca47b69c352626a648f16ff8653a63b259b6159b5d1f3c12f5cc038b71ddaa4897cb55ce8f1a061bd0332ff03f4359efc06209ea40d36217f3ef0b5fdd4f15768bd0beb43e5cb06373adcdf15c4847c90c61cd3b34df497b9ad2093818bbda5389472bcb3243c574e09e75c1166d44eb41b32f61d293fe29262705c8a25601700f09af22b8e2d1f60c8058b2bfc982b43138e05973939446127102d1fef9a4f9709a779732481bebdf2eecca243728cbdf5f1daf9a5f33815b390bc214b989697999829f3d79350971c735bdc9c570498627ef331f8e8e30031a05c0f0aa4f2737347429aad86add2d6c1f468319b1b2b1261cd6263e11c458ac0073784e2a0f997fdfc83b73dac58aefcba9a0bb908a11a512e0b7beec68c7300f2d7e957d52f930717c3ec86875ae0b176a38b51569c2564dc63029d39117dd0a6597b29d05b28c838fd8b1f1b60eba8892b6f08cec268d62d6792a1f4287ac0814151d5684a8c14a5b5a6a52103febc131477f5428b3a8bdc63632c589b30007d48bc293767b5c39dd9c9c11373350841eb6741d68d8adc750bd6f7c23321805a0a5cdfe382d6010c9646a2bf89a0bf0d7c17b0b06b925da2728041223d2f50f907690479c47f2162ea03684fbc0cf0eb7815dc0d214a2b74ac44073bee01707f3faf40a7b9297553f930f6839420dad52f9783aa7b67df9354366ec80c36b09168a38e1902d7a7a20201a611464a5baddb7772eb581d03d2859a151bc6e5e093adbabde1ed83b16b19bbffaf6204b39bf986bc2dac250f41055f0b53bf99800dfe808a6a8f9cb82b4d859a2c50172d28f6931cf120def44082968afe694060cbe07d4d04b7209bf42ddb99b180cc7c4e44559e833603b2ac48b0b7a39293609af6a4083d0e5ab1402bd198d7126bec0ab4404e00ee89f44f84639f687cd670d3e905652fda2cc0d58700129a9790d54cdfc00b09fc60f87a145a80ad24b0c074a332856f7ed4d67ca263edd97339204e419c7760d676d582bd55cbc98c38a0f5b13fea372d333e769819e201e1016e288552c2908098bf507e242dfbf5246019f43db756cc42fc35949572c6ea939b4264e1464e1d9bd8fa2056edc7ac12403511fdc2bfe2cef58577d67e46cc315ecf313e08d39c460f1a94c06eec195530dcadb07b7ef195131d91c169881d5454aef609e9f4696b97b4a89a7efb605160aebdc7da5cb8b4be990b59336ad387a473284b9bf64bbfaf3743bb1f85372ab0dd9985cbd2a7448c3e183db19e817651ba851e6c52e404d1d4bac995f5d54fd089d84c5bb155e9704c6eb974beee7a8a4e3f742eb1ecd3d677ad59e2873ed0300a5d8f1f8437a3f5e3a76de14a8fcd40fd72bea3fb69226f16bf9ccaa7f0f2e00587d78b3c500ed75de037195fed5a4e7c51a3538dbcacb410ad5f053acf64f0e9e8792229117920eebee055657a1fa1f05a0b6bcebb33cbbfeea8f1165c2c80173a39f34bbf96eda5790eb79fb2aeda01ad536a56baf9844c86cc07560e0cd0b09ef6b990de4fcd4762cd7e5728d8f0c51f57472a7881cbbbb11c7b447335a0d2cd70f6415dff6dd7567ba2c09d13dbfa9b6cd6452bb51948b8546ba721fa00351189e733f9a8009bbc3fc330df34cc3dca05e075e312dcc7b96522b8b68b9017975595555d71e69a1716864117f8e647d75f40f46181d04a398333490507904f449fa5282ffc692d711159ab3167ad411a7db2a530b809ccbdc63af71b259059b37685091d835c0765287fded1f1c9f4c8a9a7b3372613bbf587db0ceb94e0e7391af31364b5b2c157be07e17e978a04676d4f82e2d75298928c6a0e9b8a9023167b5569eab244dcbbc7bc43698e95a0568dbdc710555690d46fba64c4984015c52b969a826cb41869acf4fb269c114521e78f6dd88f020b499a04efab5a405cdff137eab692e88d1949a21b2541f3939b9221e7d24a1b15ac0250439d12004c2d33495e34806b61a540bf7ececa76083311c9d2594b421d9aed5fdca9f1e04b34a3ae833cc1a7e9bd2c8acf8761f0f0651f7ed7e0f8b7f4404d41d55d1e50db6084da5864fe3c3469d63efc2953a08ba9bf26bb51530beeb82ece8b2d1f70db02a450edd2c0c8c804b2a7c1f7677834436c45faa4fd18998edd0662890770ad1b71092c00fb8cf48ef0ed3a2dae709d5b3ead6f5bd58b852699145103d39058e543d5f2446b3036064219d8df90a5ae47709aec667e4d5f4162eaf62d89803716890329e914a680b8d57e4db332b1214208d33ad71c4161d6787433583c006b6875d98a86b8a466a95a8865f7f38552b30b7adfbc80b8b323c11d45c2a7e045aab4ab102db4dd13103bcc1c3d49f70392341de531271469700c0eaf4808e099bd4114f75d3da96a213761b532e1d7ed6289b7efd57a74d13ac77946061350d515b47f21bfadcdf74682762bbc36f33a82b939455992fd916ac5fa8f44ce50e866e3e3fb400d2bbf32d02d37caa0536525b787340e60a8e7d6d9f574945950ac37325203b76c923b62acacf2b493d36f1228388b6521811856937106c936003434c10688f2734a4a0ac23823de434903da0e3f6c979c820ec44b0dd99c9200e60c92a602853e1206c489a9ab8846484a783af39a007a3c9c6bd3cae77041803cfd9bcf1b6835f5ef05ec87e8ae9d2ee9630a4e0213524963385960accf5620fa183a82038b2e38a6e2d2a7f8f6ca91b29972c65f0f7774b13dd7a77a13f6e0abb012767ca98c4837a78de52b2d23c48132f4511b7459bf046b725e82fb5b20b3478f6313d396a1e756d3124bb7ac04950ee216da5aa8273b2a23124b3a7020e17752af63c5d01a37e28029e1f42b505e8f00e31b2a3a0dc40323b7f59a9bb0c557203b65ab35a844505fc200904716e855e84b6691fab89a81451434e089e0db1a4f00184320687cefac50fe65736067ac243e7fd6005234492cadc8c46b50654e292811aa1a7bf133fc078ee56d2f6359b71ef6e9ebf18d7cac23533e8c80ed0c51092306c3125fda76a6a7a5abe866f834206cd93a7611ee959338819880320d968b0f654134d5ab9885c475cafbd7d272413a236ea218e85309db7c1da00ef6680db8ada950ca1badffc14bf74e4b3cb5544e531185df2be7a81a3ef2e07d34b7a13afd4f67470ef4cf19d822d260befa8071973ec991b581e02d1b2230b2b578d4d8120f4547bd3d32392f9c528161be688e2230be053caea0ba43a2a032f0728ce8b8b09a6357e1bcc8177b8bf230157677dcc4b3f875649ecb67c320f9f431ca7e50ed4e3b8da11337864a564d9eb004eed843e32869163df63bf3706a57aa13ee105dc426fec974ed20ed812035748c96f1c430e056792fa70bce305dd24008e2947becea471f7f98e49b6c88761f42d2a13678575f3e1cf93603607f9566cde710f0e9bc2af6a5650ff337cb43a1c636fbf338e636880c8146a9d06e9c64accf09174f3edf0841741807bcab477e0379f2d4cfe6061a4ef6e33ed0dd994f3f81bf64ec9209f5bd4dd709f8ee151fae0a7eb9ca062b24521cf50620e67e111b22eca81cece432204c2165de748dcef63379ee2a1a828ca19e4a31e02491b07b263a2014c19abe25aa3f2994a681732d5eaea7096873db0ee862fc605788c85d9a8fcccfe2aeecbd4da6a4a732296c1ce3faf37762c064b29142458073761666f7640b7fe4ad0b01677170237cde50d362466b8e415e942790ad5530592e5d6c6fab373ef26b0716c28838a680d1b43404708a989a72316378a37a9066baba55036d2d0afe65e37214efbe03455c4d81ebf00d39866d547e2aef2530a91d5f5a5e07de2267da29379b89d42c0f7aed454d8176a283da8ae6e06717a006b97b210b8e652e85467f9ead55c711231fe0eb7e15fe52ffaacf111ffa8c9003935f580520442e21421597c381f9ce5961e275d599f6f82983504524718da0b626eaf03a12f61efefc764198ad3d58bad84cc04aa01d87f447b2e46ff229a04964dc55c173754fe4fb7bdfde56e9ec6963729f614f11f4c1cc662d440ccb60252105ae12bb060500344a8b2b862007b46b978c263a04ea1a58facd51293a60780916edc57bcf90031ead665140acb518acd36c538883ef0f542954b56fac50a4e4a77a23f61f8628e14685adc3834201c9e539912d7f03b931ea107e03c600f633985aa901d3d0f471f0f90de5986bc0178dde5a9840f31d9f5ab0d5a4de50f02dd50914096f8c6a4ecfa132cb510e24ad5e40bc43a73f7df623e7380b2c50cddb51ebb0cb836f32bcbb82ad44b7bfdcedf98124d6601a807aebd948aeb7a0b9ef7a36e509dcab6b45fb23bf6a063de51e81922fe8db8a7120e62a04499e20bea17ecc1a842b7c334525ce94ca10d281ea8368af35d68605a4a7875b4d41b0a6dcdfaad998def6fb021563ef0d539aadf42e99387d275e84426546406e20a0e202fd577784c50cec4ba4ead7027ef019bec5c3d01fa83a2fded61950bafcf0bfb0d38fc254dd76e9abae7c596986af74a9edbd531ba8e316b6200b4b1f34b040a1cb1863321470f0d4fb431eb204aa7083a88ee0758b609b463c332d422250c10951dc2b93e33077b2dc7356a4e189147528017fecda2bd0713b19bdabdf806d5dcba47373f6dcb5699e6c27cfa6393fae3c87ac33647c5085fb1f6f66eb603214446580501135a222de42a204985b796fe61708536c088ec6f13c58c06b90d9227f160e13e423e4922b7fe4d6d12d509623bc302cc4642cdc18ac93083f908a4e0c358294a228adecc96c7fca9efc6cdcb2d49f33feb0a47128d12973f3c0e53b13ba9dfdc4749521a6ff840a782eb3d2ee519f2c0dd19ecb5c40659e87705546bff3ccf8a90107eccd3ee3dda149ecb55b4ed7b1623bf99489388004a6204085a1611b0d7850bfc899ecff0ad50c72d72a4fc21c4c427625e6a376838a90e1dff1af84853e4a3ad143c56f53e224b8112d2b719332f39e77b757891a923025d44e201ac1f746cd400c37ddcb7b0b154701643d00ae1d883765e276bc1f20daf2548080f5d8119252a5a920f6477ffbe36948b45d2123c1ee9f95b15f0300131d82eabb31829383fa2f78c033ce83da02e47ccf0d5a2a131d81db1736b9d0962902f849a0f363873b10d31ae4721fb39c33188364f45652d2185983f31740ec1718b57bbe1668cc1cc426e77ccc0888bd3b969600e2ace0828acb09101f809fdbdacb3dc324187ed915512aeedcfdb90842544470a548f79d8917e03d2970df7561092aa399a386bc44ac4426f096ac945ff18a0267402017fe77ce8e79b0591597fbee8e154addd2510640de58d176052cbb85f271a210c52219797930c056712e5ff133afe9c0e8cd14fe8d333f01b656534612ce9c7ae0c14a7f8f8f506471b0be8e432d99ea0d9918321434c6bc0865ca04cc9880d6ac278a631202900413fa6e3ce882c94866655de4e9c83303ebc1f063e644d6d0dba18efb77be706cadc958b561e99e989496e7e9ea10df710a80c2e5062c2683a39547e7306150042aaf85f764a04b93513e493753ce9235aa97e366124c1005e9a99a613b7d700103d9310a0cbc424328816971c3f74be77587b40461f343c15e4a63206e9f50d19ba1915af1ba23bb85e4c4cb7e0b432474e1fa17f5312166eca5478f965df07f0f14cbac333722dbeefc7e04608c983e007fd407caa6eb554aac7ebf4b7ed08a889dd8cb3a852921768dd99743d2dc676aa47ab15ee14038efdd3ed3bc4dd0b2ec0b41f0258165844b6925a2bd40fb3cd8b8e710f2c53e53cb24882d383030925134668383ec4d94d2cf0a81eca84f027c8e8c1109416a0e2c059d6e30a9c04156e405574362c8ecb7bfbe18dce066a6dd9cf0fab10d15dcc00bc82e5d15100c09aa6afddf4fdb053abb3cf3be9ba9d2ff22e427e3a993952dfe56a56991e0ed0822150d63f7193481d74421f0bae4209ead583ff65e0561d7dcc4a4057380981de0f312d43fe1cfc5e70c4bff10ade8f30a1a4ebdd1630ad6bf62d685930079a47155c2ad649c21dbfcefeceb21e836d19185152345ac0a9df6507229e430e3bed8edf615f5d69adbb3901255e4d6262a572e6c6de8eb6878a6c45684da7074855f36401bf2c065396ef8b56203461a9bf897e18ef765298a0dfb5427e75082dcfad26b58e5893abf555af76e6b3fe2ebdc897fbab0dad8bbadb47dbe12f084a8444fb1806587300e9950c1e6c8149d91d6125ee68627292a4f81d84b3180a1ffc58ec2dc316c17cc92bea1fec93fbfd5a494cbcf3d50b4bccabba6709a2055cc2739bb22119dbddef748a5222916e6782a517a900702a1412dbd7662464041a8d4bc9d2dfe24c0e76f42993375dd81f2076046d791a51b492242521dce60869f7306e5e8a4879cdbb19f70ea492fcdbd69cf2b3bd3fe5e84b972ee670d207f94c1b4fe062afcf89cc0575a2c4b0d42eb861046d80d8e5af5ad9481f0acb0a4004deadc80cac69cdb3959881e4e92568f037d38df2aadfa24bc9d9d9338008e07111ebaa0c04c6cf4cd108014b581f1ceded64747f915f3bbc6afcbfac2100b80d6e9ed236072d6104a76b60ee04195fd9ea3a2a3bab914ceb29e656d6bc01ba0548a2971fbc1b87d7f0ca900e1ef7a3a42ea8105f6ca8b4b0f51b0064a50fff938b6ce5ef72d953d9e67aa6802e896dfe94ce09d93bf696304850ab203105b66b200fc570985d1d8451de8f6ee6d63c9c2d4c2895c2449e48cd9a4030dd0ac8df6517db3f160a67a5d996b2a05a94719fe02cfba2ef4401d5249d73cf680147784c5d5c281cde88c4803a47fd8806f54645c255317da8ddae943e4bab3602684a7cb0f3a11817ab50b85968696e894643f4aea7f69c337a64bc586d6dab00e4ae58e8e0db1e3072ed2559744f1395bd3f4e49f88b4589acf8c0c525e2b4067806118f05ce9d46d744fc70d92563861d436ef652bf1a8d5b01ad376416a70f232bff8c8c77099c3bbddc8ac7780e9d9f1f63d84740f50314474887089e830715dd24460a4d196352242a244d32e2b8d3273b11fef23eadc4efb73a27ea720b81fee022d1d042ea19318346f86b91d264aaed6131e1f0a974b18eff45d87806afc882a0c05bbb93e463fe8d75ce58b01e9062bbbe4bf0f27a609ed46977be0cd3e0d79fbf4bbb96ec9feb03885d5294f082e9b736766a6253517bb538233c02571696a978d0534e0322b563739b36abb3d65e22ccecca74c070ef54d4e0920fd5d7227f73d7d269b437a1e3dd12132f9e3093baf02712b6b5bff937109bfc0267bb9043e04259184feb4c41f01edc6f08a73bb8d57114ac45cb4256c6a52f07d41fef0419af299a9f731c3f6da1f49836b7035e727583182ec0b388e8b6770d632d398c7ef3b0ba9604b71c5e8f6e5cb54279d88bb18d09b126c88df60013024f05d2fc8fef33e159d88c85f2f6c95a671bbb7616fefdfd30163a6a1c0047020ceea82317a3a9d0526d9217f20511b09cbf7330637454766656f01f2ae0f40c1e3866505e57e63ec6d8dbdbd49637ed83ccba9539b8f6807d60485f659e5a03375cfa189dc0ac43862d1a0e929baabd9417df83fc26f583a1190d2ec3371c0bc2a8ac184ccd54d7192b208821b9e57e26ca63293e24041f509dc25d6248e00b3cd38c6bc53fa6687e755147c41ad4f899b4e4939cb457d88a02c7fd0d528c0ace67513b578b9c73d20f005e8ecdb6fef4196ac3c4fd5836c95fac90e1e74d038c5b3d854bd919adc219bf1960a92b6955c802bccb738ea18839b8de75095d07634fe71b99bd99353fe4c2afab14faa0bdab725d2d03a0ffe708ed067bd0a906b5f31078d0cf5f0efc02f76a407679e68f9d71b821ec38369841f9bdfc204103f2fcac59748e9e7e50345c413724d55e5e6d4e5382373803bd673e09d5911406cf77f16dde3f51bc38aa52ed96658021b3457a6370df83ef2975234277ac460f89288fb202d527ef20b538ac84cad474135ae0d2ae186cd92e08e204f3966d0373b1c92ad91199ca79f162bd692f32952a4b63586c2f1fe4f7487b09b0b5300cf90c48f722fe9a89d8b258ee0a7b79535b92c44431af543f87653fa719d1fa01ec01f3516f97f28fc6510e6ef37183a0e252837e2cf44623c57475fab14ef7009b9bae776aaeb8955be08178baf91005ccb0fe5a89f1db869db10c894e438605f0f59bd025c1aa5e13cd9051b0026681eeace61b03f5e98618fe0406a42ba900f7a67d80095136ccb56dea80b5295a07a660532a8a93816a1c9f4b03bb54e50c92ec2f97076fba0802b2f67138747dbc6403755704aed02f90b9be03ff9ba3001874bc9728b820ea30401dfba7a28bab2b46bf8322ab598ebbd3213df4feda01a7ee74a931c70babc7121126252bb18e7ccec55b5cbd09fc5cbbe3a9f43a1d026493db5d214092dfa6dc3f40bd1c785239fcde7a406b0b15b6b5446af06d920b831235d8afefff77a24ece5aa0d59a30ff6eaad89303700c0a887ba04b478c07fd50d89089d37bf8ce28ad04bba065a4c315bd0ecf6162d705321daa59bef6056dfea3b6bac248d3031b12e8deacc6255573a483bd30c60566c81a51b526100aa3b1583fa2dfcff1e218551eea7bb46a5731ba5dfe81bf8b748cc1af662c0009539286e657963b6ec6aa544d3296c6a0e145736edbfce45035971633054a1c0be11501fe1fe156421050786818de061343f0dd1114baee5813728eb6be77070be0994de519177bef014e058951c9452cb2a16768c7c596563ebf3f94d304580398acc09885baceb06f6b4e46a7e0e1e1d26a9c534a4e174c2f57ad09db767a0ddb7a2aeb08ff40c25ab57c260822bb3ea521966ffcef95b2fc859d60cccdfd0a226e26962302e5235942d868aa1d3f71bba90b5ad50464229f0b5681367ffe0301d5a2e6edadd8c6ebb2f32970393d3ee0a488d7d63d7260c7bf91472c156f01144f1efc8305dfaaa3594597854e58b5b82db50d1317d3d54ee76c164ed421049779de4b9f5472bdb64b7e229cff90be68f4f44a0ab8ce33a201d194d12a320d1b88423c218c969daa4c28f89d2b34fc7c6b113255ccb96cb59bd2e9ef90a708fa3151e5148782aa4caaad6db716206e59add06d960abe16d0d9f45bd4dd0e01c7b6c13ff95c3a2c243009be5f6cfdf66b8e487f7b80313cfded2b0b10c43b07b39cbd1e9c23a58128a9df1621330e859e93557e063de1e1d3b602e87058a90aed27121ae8a69585e71db79dc7a8480a4c418f5189258b71c7789208e675ca053d0f338be0d7842bfffe34a29fb382599555af2aa9d47f3d90af7724259942048e35c35625370f19e8af84c888efafc4c80fd5311b49a58905c64c4249ff4f011b51b7ff506f49b198bf029d11dccfa03c82c591867a1fc977311fadc371d8016f276572308590477d0aadb1e8a0c46d2b9c85906852bda7a22e3e62903f8b08c61b8f6b532a262c2107e1a69aeabb138965ebcf18f2317563a721bf56d4c8011ff6731ecd3d18c5b087374bbc570ca56bdc39e0e754f15f0bbcf14db9f0530d041e7f278c7d4dc0495f1ccf7a5f2b1f1789a0df6afe67f81d2ad017fe019a062b3f3cebf20ee8e365eb6bfb4e0c13737240563b47c5cdd6c0c2b000cfeb620b29acf850042ad92b94e4f4ce0a02b9af8cbad7c4f8e9808454169b106807fa0c61c6ba485a48f72c4c4ca9fd8fe899590bd53a822cdac461d4dd49ac97c08f0aa7b797c7df3247ad94ea19d5e294bada9db6484ea1691b2bbf268935a3fdb00c28e3c21537cb55d8eb338ebd645dbc6dd90d219909ab84d40578503a8067be04bdadfc60673fa215c255deccf5d62a21c55a5d5409005d6c8861c177194d2709f1949721d852b3f312af4a90df8c836f522ab1a15a1f52e8dd2d5af232ae8b048eaa11aefac655741c1c43e409b056cfb7b57389dbe5757ebb257b1e893d77053d0cc72e57a2f09cffb95d5e5c977916d55fa9bbc0813d0dea461d625944a10790ca5ac444adf7ef5bdd11f80410cd72edd0009edbe8bad937e31bface4dc000615b86ab6ab7166373ee4876e2b3e045c63f3f38f73dcc626ab7d78e8d119009b192268d0bca199fb9e9aa9e79b290ccfe68bab084b3ed650863264b61468a050ed2be1f95d50bb8aef980045de3e890df09bc99d1d5626770909b9bf30ba60ef218db361ce52fe35ce465e1c1bd645c562f4283a34337e66ea59fe8598f080fbafcee3143eb63336bb922444a8c5f01524e5e7ae124b756e269ecf7fef6d005d05d779ec3eeadbcb4271cfedc362751ad1d29184391b781a97e8e13d5dc6c07b6920888e659a4c9d7f21674eb66302f691fceec18db56fcc1ba05244512e309d72d8d9b69c8147060b35a4c37af5c837e803055d39f8771c409ef0c98139603768425e938722205422aecb0ef8ef16dce1db6c7c524f2b1d477d28392a5b2021a4bd0db9ab94ed10e93edc2c35fd9e39920d25af938999a2729fb29a6079e0bc01f83efe3b573b20d36245691448fe42f96db872cdb961330468c5222d1f0063c0649397f7a9df9266428a8bb3bb9f9c7b99903f5c1bcb75744d1a72beca904b95bfef8865133586d57e939fe293ff0296eb33659e21d0d8c1d60aa807c6a09442d621a15a90d72b3ad6bce7ea8583a082b9a334492c1277023aa033979760eeeb0682068e1dc3a88e55c67fcd3420320905b9d11233d60a9acb35c7a24cc04a67ce0516b9755ab98f30317a5cd2e56c96c2fe5253966a69b8629559564450b75dfb1bad2204dc83e31f55f665f0941e2477b995a10c140ecd9fb2352003d02054d21ab5605ada8df8cea8cf02e1830db95134ebdc9e7d856107ea59bb5c306e0071bb5bbc8cd6430c26947b1ed84154ec748db3eac15822fed8e9202ea1806777227649f40b09e729f3d0f8ee42c4876a135e074e13e8a5e3b039f740d030f141eec2ca1ae79a341d55516d182538a239ac49aec267178c77fcb5b3062950a3bf23d4883ee196375c0f930e1899c74dcb59ab89cb7758e66aa15c84a8c1602a7b8101741d8d61958598ff386640cd80cd947c91012f266e038c3ee79e74d0b548772420cc6c5780609fab7dc8216f63f7c23505c46cd34f40f5bb188b6d905aa9809a4937bbec9606e03bae8a870c89f40bde6c739237f281ae1451f75d3075e60148fefce384997e55f12724e73ef42bb4ba1bbbd571c7b4b46121590df0eff1e04550c6d3878de253c37a3324ecb4105bf77742a025f40c74753a6798301803a30bebdc52a3a0403776a79b296effbbcd53a806f30dc0aa8ef49e27f530ce32a2cfe58d96494498222a3883ccfb3ff42fa7a8b890a8c37b72f04df5cea06c13c4c043c4e5702dde83a2cc86504fcad025dab2095d1ec7683b94535ddd807d88a71b806f88ff2291506aed864e8e10fe127f428b0d516a9cee2a479930c046b2148d1ff1e02713557f8f478d63fa821a08779137f19a51b2d44b78f32c609212130630bfcf6de08344a58893bfb6ace834889311872f1777b0ef103688e0c00863dfd0a3996dcbc4a34cf7b4d500ef65ece78c62d32811827f6e1c84fe70c6ded2ea461d20d9b8ad711ba0941036faae6e557e6472d30be26dff423973302e14712c3b7b73c4c6f6c9ea9c402d02ae25e172c626ebb9026cb618917f440009305c1e60d210603cff7f5410217743eb5dd1a105a99b21eb8e9ba58286a5808fd07c9aee216fd0c32686ed4eb080b579f881b535860d8d0cd6361a33234e00f true -check_ring_signature 02e5a0cb838c3e8e672eb2b39d71117ee4d755ab7000ea37f9277dac2881538d ff9da19da8f14014a94c44b66ecedf645fa4b643f688c7ec6307319ee2568535 6 7e10f50fd05e3c1662e225311b9215dbdc6a1adc7f1051649e7209e697a0f6c2 41bd1289442583f2ea327f08e977303bac6d77f36bbfd912fef0962571591d4d 3af331555a34b78dd21f18dced21836eeff48a89caa9f302bb7946918df3db88 ea237c0841397fda972c4962161913f64e42f1c9e4095d98021762ebe849dae5 441436c44135c6c5042154d7f7e74bc84c349432906cd53fd0edb2d4202e93df 1fd964ae0d324b4e7c16e858dccfc8a3e8d03d58e3b572d07eba1f0fb1a8c3df f498ec3a8a12de34e88d78fc9b24469d279a2f9f3e55addc1b653836ff0f740b6e4ef1f79dd27802b511ff62027187d813bead31fdb7c95adbd93cb749eb23088eba964a2127be94c8c0296a4958b31b91a42481ee797859a316fb9e776f5c035f1a9e613dabc70e719471518e8a1e4f8dafa25c08e4471f448e1cf56191760e1e815a3d78b4698df46c4d3b0a2095b7df76e9c30c218db608e1af267797da0d1c03b2df95aaea282d52b6208ea0283bd78b9bec23bc3f182f166eb2be4b1a08bfa4370ab578d1baec0db3204b24eea3054058f7a9ce2ad0a668774a01685b03abc816b1417bde51011019b1ab4e87c4d00499818cd36799f05316ee83fbcd08e5a80990f2431acef7a2ef6f993443cc2df2a34487c4e876d528557d621725080819b40875f7d59453acb77a8445f234bb9875a28fae6c0fc11675ffae5ee30e356c77a3fb265486df610f0c6d589deb71b5e229617b2daa1c9b09865f1f1b0357e4f1af3f1963368f1d794e9d11e0fc48a476b05dfb7d4bd3d033a0a6958a07 true -check_ring_signature 99f5c5074937c09a2a7e4a952fb47d1592f634f0c075fb37f325e691a5e40b5f be84aa7e42a17e2b9b89a8c9803026088ef6cf576a839563f22b4553f4713a63 14 a0943e8f1b94116d49da3256cdd6b063abaee1b3f686122b7bd5b4d67f4ea6dd eec962a2779719236c2351f092d36dd4505821ecacad80b823059d666a2afe9b e9c2210047b9800621e20fb623e146db62a2454c607ae9492ee8ea7b1e975e51 dc00d40fcbc184f480762d6b88d1a47304899b4663ef40490dfc38269ed1f3a1 f0842bed3ed550e02ab44468c6d9d1e7456c5e2d3ba160fa8496d8480eb49e7c f7ff392b77ead34359164bd6afd5452cca49ecdcc8a4f16403d3cbdaba772efc 52e3b551445f8cf1cb509477d3c6449ead2e88d98517e6db80fc50d0fa220781 d9c1116501dd329476a62ce4bf9af836b6682b194601bbc21386a3ce54050eaf 9468a8e9a42c4b19e1e14e3821a5697a81fd22efa0ea293118a547442ffdc1a6 e60ec26f2f740ae3261418b733baedfd1dbd9aa883a48b581dada1bfe8ac0e48 136c87cd493f36cd485c98bd65dea164146b7cdb7dddcebae5bed61cd9e1bddb b8a248fb3143992c26eb1dcfadadf211ae61d62169294d5c6932e8199a568b3c 97e33d0933b6cca933adf8b4d41b3d65e24de7f4bc6b15c0bc86ba62f26b4076 4430932ac184e57332063bf9b38453db595e38a9db8a13d78ce732cc55facc3c 5f07b2225f54d5ddd1e7e1ddc610f7939bafeb7dec92c227feb6743b21b14d0067e10c55042703683e7a5da98eac0605eef989c0dc8754a079ccfd7196cba506d6a553c265f869e3c733c59a45c94dd6816410706d82fd895ea55ce9085d6c012dde5cd9cae968db7707f2c88d7b67863926b10e4203fe2d35c42c18158b5806743d37eac3d9b446187b68a2d1c50a2906eef6b823d795f7695467dd5a14e003ed49e6ca17b2f9ec05240907fbad0b512336793072ec8f2c8dc09ffb0dc76002271aaad2cbbff00809591b023e69b20d10b3b6e313c966e81ad3f43185c4aa39550c56fad5dd6104c4d790228706cf1f65ce0673e51bc1b35761b3d681f61e0ffb094741d01ac53b6692d00f280def26fed45af99b369ed0e31c19e9e8420e0a237b34b627da1d1d3c7b22d7f6abf8b0b5c7cbbdd15b9ca6a166d7cd188a1b05ea6ddf4368f6cb38b50cc3c1e4da2ea904fa2f86f4f5e12b98ddea4bb09ce00ffae177a0cf6d3edd55c06aeb255f614928d0fa4a909bbb038782f8e02dba4903b05bd0cb9da7b97d12e2d23780049946ac24cfea125328003dc57f17af308d024e55b46b7101013c38d895c00adad9caf934b76fae87c6dc1cc73a07a08fb90dd7acf27a30b11a7514cc9c472a5dcf7f5cf1ccd0414541a552b163c6b5899c0967147a99dee20fd998e87610dd470f5ab994ea039dd307fbf57e6fca0f85191f6fb271b6efb041a1ef0f7877c6bf09915acd057b7e8d0ab4578d35e9dbdb8e000db94f02e1ff6ff0d07aba2ece2b72fb9603f5c900b9d2f497709ff4ce87b5e45438dea29f47144fed67f289f798d287e739fcb3f24e2feb4b2e26fa901a9a0145e676c373832841e48984499f95ab6f8daba6349b4ec859f5643eb42aebdf0a1a16a8bc6fa116103e30ac810b0160cc44b8f805e9c77c44c2b76309d17c770d311c0c5aeb9a7800beb89fc27111e0ccdde6fd17420db72a22c0cef3f592dca7523bddfe95781f84c3e6fd6f49113c05a87c0004418f592571b78459de71d909ea0ed55738a5d95e8d30c74ab5bfec6b2b44bad668d711b16e16126f74738f0fbf04022e542cbaa5dff9414798e099e03b052020215708c164dbca882e08080806314e296a2d0b24ba7e82e18eb1207a5e81ab698e4507bed51a70ad590fd6047c6fc21955d8123f15f0d2cf8e0b0fbde05252d53979ad8f403a220ebbe4c8046f840ab3b0ed37258d958caafbd83243bfd6e30d2da58c86b20d4442ad1cff02 false -check_ring_signature 5826388cbd3ad63cd719e455a77324f752680dfb415cb042deec9b8731bbc10e 44320f9d8b68652addc3755dfce78e2410f8a06238cdfb556cced60092e81c7e 1 4b57c4111f38e7b7efd5f083707ea952b51adeb52b9b110cf9288b8b5fd5e54c 52d2a4708fbcda411c998ffab90ddf47db8c4b330324d8db11e40adbf1667905f27c365122055151fe7d34fc519f21d7936c70814eea401c3fdf8e81172b5905 false -check_ring_signature 2b328fa53569d69ce8bcbde6a32d71e226d8e1aa39866a67074808def8f9a345 9824c30c6df2760a4444f9a5dc5ee9738394e72af9f65086272904a1a49e2a6b 1 59e65f9be96ec4eac61be96c876c14b32efc87e02dea8fdb29d015362da319e8 86402bc0c7edb82f13ab74eca869b956d580697c01b19917b1b35438a15fab0491bb7ba75e55828fcef00cbce345e1fba41b940ea442f6d66aa084f6938d6101 true -check_ring_signature 960f0b1bbf1406df89bea926b63315fa2261d4880e4337a877d579e1eb571f65 0f89faac68e644e45a194cfc605d552d383a7bef86045d104f92a70049bf7133 1 bef5e6e714854e7862886af8ba97e2d9030a79855e89f4e09215ba034f3b4d53 ed2bea658e8f1372263c839b7887a046f5b6feee394d16d39c51d7edbea9670af07e487f7719911ac19f7aa9a599fdd7fd385fcb156152d0c60637e80f224007 false -check_ring_signature 37f1ff61130210b49258bd10a06ea2cfc10c5830b7e5eaae386173d01e5fb3fe c6f6d944a4c564abd46a10e6fc98b3ed3bbc1b08403e174996d09644781d6b79 41 7c8cba033f6b10981e0f50b87ae304fb0ac63bccfb15d803f95562e1886293aa a34b0ced9264d84c807b013314544a189b493ae9f16843bbdc31ca28461d78d5 8d26c826e03f5aa49e040c22e8b240787260c33aa761f15e6b626dae7ee26838 e4ed551bb178edb5c7c2697ce0e5180309703a760fcf94403cf53804b6a23efc 9dfd91280a17a1f20ede08c78c78295ba49dccae55b10916c46dd09369a371dd c0a09f95895866ab3fe121a9fd451492f33f4a83bb0e4f21d596f18adf125aaf 0a76b4542b3bd70605e2cd496374971f7f9d30ac5eb6e6a05603873d75aecf38 250b9ef17dbf2247f821b31f64967fa5a4bc97fec91e3d22e5f8cdbe1d96710e 957e3e8dc2fee1980897d56f0d76fc7fc54b622f2af2af0511907eda36a61a3f 63ab2e0d155dfbea0bc4bc918348c4fcd43803e87bd361b7c11fb3b997d9f4ae 35aa7b6c171af253e35abf4873f225303c85835782f6ba3925b1153e27c7b2c4 6a1117340d9c9d772e319d6a25f067adea42563e176834f3c083495870d58521 b6a91c9111c139a8e8d6f4ae2602d694662e2fe3a251f6804526457b6f18f819 9f3d0929b9945c33fb7dc590494dae159d521b1a2b58b1a1afb3b44f7f095386 7502b85ddc669deb82096225bc796bfcdce9385c3226964d3f6ef751edbcd846 d7d12d0ce1a64a401db3d996e946d02d4229a782d9eb4f6f18fea73b22188e90 0b84739030209fd65c99f2a671a692e9602611a79bd4184f88441b29bb3b8490 a688e9f24411f3e240f06d0854fadff7a07e133e3c983f701cdd344b4a0f1618 cef3e77619117c10bc08496eef7c1def70d189bfc2fc1716d3e5439747472050 216a7663842ce845110f3bf02142e909a1571442269d3d9c7eef41dd0700ebd7 5642117dcb92ba1a823edf315194f495c75d1a7cff3f5a0eb58f67ea29fa4487 16fec02d47c71e47937243556f0af5c8a02b5391658efb227f270a7bee5c0b8b 47265c01b2999cc56389ab9ece1eb6476883242207c1c70d7d455eba372d6d31 d9c82520f823627922f2130daf5c89cf8894deb33e8a5df24e30c7b9257c258e c4f64e4f7809064d7ebf78c72c900ee4ad8f38de32741775a7ef88d1851892a2 ba5e06885597f3965751382a74cbfc5724bc2b65762e19c7a428d996e5305e22 6c1a4e817139af52356a3e2d90da1d75a47cb38e575874043adca2c328a5ff0a 5cc6d7d99d5bcd2f98ce140d77f85ac734595510c62f246b06eb415f13251046 e6a9db7dc897a0902ef014a2a58f5de6b401b7d9a7ad2221f7de4fc20df23da4 320cf3b9ea68dde3f9afcaf9d9b0938c78eec3abfc6ab732808ae6e3c20ccd70 a5eb65dba9b7f5fc046aa5343034e269fb0951790f292224583d9394e68744e8 abdc673528f3b000c6dcab43cea12023daacf32a60786ceae74e57a2e273577d 9461fc5c12e6655bde20267ea06fbd7e071d888897a1e8d9ebe0dcc6d7e2a3dc 7a07d57d8d553dab60dc8f8b884b2b61cd44c28570d7686fa26712f13a8e88e6 2161c85add05c551fbc2db04cf8c087f49a91180aa6cc7ae22004c8198468cb0 e3c6149381eb098a8709cafe0d1dd1b89918d04e6fff244ef308b68ffa3f8c0e 2a3848ffe332e10cda3d534339f55892576434f18be9118dfa98b24ee2ba2cd7 c45b958fdff1c882498e4b88bc7c5359473e1d97bb017acda1523b057f07dd4e 456c34cb0693436871d5f0dfa79304b34487b5866278a15c64bd234a4eaa678e 66744702c24638226879614783bfcc369b265a5b4f334f6e030a58e3b3e0214c f5dc70b735b71bafbfba96d5c466a4b463696897e9900c285061260e610c8f54 c180467f62391d122fd36bde7e1fa1551388ae6fbe032154ddb0ffc35017410ab732dbe0f2e2ac16b3d68cf7d0349bb313e1844a0df3192dceb6805308743f02134a066f90c941bc7839b92008e9ae83a89e767781b04c9cc5b07be3688f3a0085a63da1e67ee95e64b227be293be32a85d21e1d2a7b2017c67fc07ec18a1809112dea735f703e5329861fe4d575dcd96bd7f52ee0f0c2116d50d2410b2a1d01ff6de2456b0920ae0a164faf5c3eeb030d2887f8795934ac58561b9ae060a50ec2fbf8f29cc1562775ed1eee20b2c3fe4647b89712b94187faa4337d904108089845d3a76f0a80e1532e45fefafc88584461f516d47bea72430738eaf8c6610f411554adb5fc6524d11db539c39462806d149511a016bc996c81c9e19e8a11019461afd76835b48e1f202af6080bd4130216bc22e451301f7be8322a07ece105ba4887f132b603cdd97b58da987da998f4eca6bb8cacf12499c2f12ec40a080ffabab35a67f66d8ce0e74a8bbc18597b945e868bd470509e455e0c977e79620736df5deda7744afc7f3ad937ec77995436fcc5a934df359fdfbee283e2200f0586b08a8acc02256fa5dda0ef071ee20f419f72ccb757ec86c4580c5cecab73012ce59e7818f3619b31b3a78c67262b7a3e65ae6fcf8cfe2b5fa462db5172eb01ad3056f17727a68663b1faaff379b59a8bb3cabc791deb55649d30bacf48d80fd4b34be272430d8f063a5ff3d71016eeba21a39b16006355e1de80fc7d79fe0e4f785eea395971496c3f7b322602dd0098d7430ea35c70e72a6f694d5451770944f2e20ac1a40f2981ed7d5ff593c9191e95875949a39a4ab04e1dda4646fc0882fdfe48a219694504851f6205c58728d5127fc46f788362324016d0bfc46602c03dbf5ec816f876d1859ad49d240d01503f22975abea2c4ec6b9bd70174ba0b0309845205f7f6c0010f60e1be274d4000b0f39bd220c4fd45ff063f3026bb0af523d07d39103e13784b7fd788d87a543b14a06a7814801cc0f4fd75cbcf7d0a326582f3275549bc56e4621bf614901bcf8576554888c19dbaf35a669ded730f473460509fcbf534ba2ac1905bd408760786fba25d2a194823522bfc8f122d08af1c7e14f165438ec4fffa2793d42b1dccd30d91a7c5d5d97dcf80be49da7800bf1604f94d39960546269d8712b2f88bd8476a4e0b54ac879d49a6093dcb9006cf021a2570133c867075e481f89cb269e98e7160473eab9acf7b442386184d04243e4bd19b39f79294a76d4a7ce3df44991f481495ecb536a2b6ec4e0c32fa60621a0c8583713e4898aab5e6d029f966f53c3f9d5c1263298885414b88e09e0e9266f9724d8ac32c5f62a669eee8e74f2296366be785b7e2ef10cc80d5ba490e341f6e1a2d5e5137da1d365751d339d714f7a2d944470e6da6ae1837c1d8e70d39413586a03f9b35d60eb9e63c987ac14c993f8d569c4e22e05737c2910e0d0c7ad1f81cd7f91be471cf9c6b67fa0b372b728cc2116a696d3448212f42aa1308b1c5ab4981b95422c88f91b730904caa4f4974ffbf7e1ab31536db4a48911d0879de19103fe771a195df3db1872a7b119b0b28d4fa42278798127cd7a26f854b893c9173b79ef95421d027784ef89235261716bb5d4b042d5fad64bf4c9e5d017b7ac6dc892b4ccb2233bed8f89006ee16057fffa7d29bd4f30de12a43c2a20e8d084e27b24b75292831a63e40671a0177132bf3941858c18af7a9772471430f7cac483abf52263594e65e7ae27b51c306c059ac8ac58ccc5931c6d09427920ebccec3d86e36345c9c12c73ee14a7ca9afa331615925e80e2b468fcec46f7f01cb28262c8f50a1bc926ae5edffed96e1a781d29694c761d25a081b3fa1b39f038909c6c9b4ed69abb189a51eef4d889eaa3dc7cd7044155cee4e88f30f559e0f7d69322329bd8393179d0cac38c9964a999ddad849685d62d3ba2bc222ed5408675bb566b992cfff163e55ffc21af17730b1b3ee351fd07c608e7ee7ff35ce02a34167f5314fe1b90e40cf016c1e8ef2e09beebf2aae634b1d75554cf6987e03e47be94f25b6ce6d625ec240efe71ab674d703d544f14ebaf1d968d5f8fe9e099f3cb0b4fec7ab301b7306936eee79377076196301c20c7b4fa06a593a9e250514c068c06fd9e4eaf10bf1e826b4278d77ba2e171136e30d00ec719735730507e8687c10794fdfc9e53549a7d7d0b91fe2ba42c9d373e5536fab02c6d58a120f3508ead2d975478fd243041227ab62b209bc75b2dde8f833961f51b69149570ed2d2759f045c818a445ddc6d5c9bb8057ba99193e5b45ab8c07176e5a88c7705b6d7536d4aaa43af4cfcbb2dd13cf8023400786011d751986d993dd313b231058d0c6403b310746032913aea5faee09e846e6c2a6ff1b441c604cfdedb924a0996ad4a39418062a1b5131a38f71c0235e0cd01f2b89dde53af9358e2ecd89403be671d6bc1e20530867ebc800c62efdacd064992122f96974373bf66105d5907f4c636a11708bebfae9406a08866c95f871a115cecffa1ce6b258377cac7a20071ee730da1b24efcc9982021a5c833d02d9c0d983c2b2d7c860d9dc8491047026f276ded1b1e65b013edfe8140026c70f93d9d174d285e4bd0292f53b58755099c2594eca2843eba85a11972e0ba214bab95d55d8b88d3a4513db1cf3196da08a493f09e199a268499039b697e735c583d006d23dca2f0404ad908cb1e5149088ec33d6f40a19bbcecfa2caaf2ec4ea9264460d094511c3743d55990ff6bec08e4142837ec9f9ea07ee31c369b14d89231566fffc603cef093a67aaeec5b3807fafc32b1d4064e96a8e4d32a19a7f6a47c46a1b4de609101681456d4f9f6340d2039144d5c695d81522bd6001c5493e4f27cdd1006999108a598a1872ba154042390bf8602420eaa88185271d0b46ed8b206232619c0db228a4d12313637442f4b947cfd7de67f9bab98d7f06eba2b26818c6761bc6e1cfeb599316e05bcb309e6a6cb09b23492599b9066d5a9a8292791a9f31ecb6d07aa90c19db1fb4965097e8852bc3cf52a379febe7b41c8b732fc8dd73283df106cc4f43f1a57b2889091195e33805196c88bc78cc4f5e4f8285f5adf9da981cf8b7d77597358a6ac42d7b4fb6679a5eb5e0282e2d184a6afb91a7660ebf08ea8e4179fb7914830b6800b246bceedda9d29523607c23f4be4183e4a5d322537b1b09d82b91d90e6ba7069cc8250005bc353f9956995c6e2136a7f9788bad29a7da10e173a376c5d63e02f3731601b61c807f948306dbaf44a4f044b528ad1b53f4e105858d4864f578022d36468af923ea97c3b19f0f16a1a7fd68ae989e06e17f1d0d4305310b7b4a44b2ff7732dce469c77d3cf9480c6778429ab37d5f10020c3af104a921566b4304c3caa5ddee67e2003804a84593181abddc2e994ed642a80889ae08dd60d81e2fcffb3faf057af44ae841f8116c290ae92db43d1a567da28462d2189f69fe6c05a3b1cd6588395e021f4afda2dddbc11dd5341b363bcffc73eb3d45ad53a5b8ac60a97165810d1900315b03911bfc51e7d42ee5068bb7fa13cab17b853d79d50408d79cee7f44232086ffd538c41c73922209a2120999ffef116d9aac3a4cfa09166f00f71270e39fee2e34cfbf468f1c28dc7e50f3cf3d789a2136762d74ed09 false -check_ring_signature 3944bbecf38169d73f4407bd00f243709ace7401d66efbff27228fb410bfcc9a 0d58900b050d30e20ce7ddce35cf3503ffe33113e3ed6503595118226cd6c17d 85 03a461a63ae96990a55ede19f05ee70e3adccc6e7f79fabab8cb9815b01ef8b0 05d82a7b26eb9424ceae67026371388b209029c0dff89aba74ca31a0d6ab02a5 9ccb91004019b462041721c36c78dda9837074c69b9d6258081dd755f014bd6b b025b8529e25098f63e413c97cd2852decc9a237041a4ddf4e991cc2704a5a07 2b3074d79094f3481d0a2977755f4c614e2a484bc1ca4787b83dbf302fc5a73f 67df945e62ceb6e3b485fa63d8a06b79bbb8e31ed9b027766b2a700c82b89f6b cb18c42f75d07b0b7fae7793bafee3edfa89fca4c39ae7ec8b72bddd9e7a6568 5526bd7d594cb7e6ad4f0762ddd6c27a31bca63799650326d50fb8adb2de2854 aaed7dcfb6c34f906ef7d93544c7ba1cdd7c8f669255593e04643409602e94e9 d97f0eba7faec85626dcee1b557161a4115bfdae884099b4ff75a46278ff8b02 6502a823cc9b40534e29fc754b6a7c463a05f49ed8e0cec0950138050f90639c a56296114bec470c933575b7580e4f85f07e678ab27b7927b36269cf98a7ca56 352528e77f8d4078de54c7a61eb7686b9a794ed59a3c59a1afe21361395494f4 d0ed7ff62cb44439a4327d801a09a334402703fcbc054b3141c8729379898a14 922d4476452f01f2298fad25547be160c7d38377b0fca2bc0ef14e9ec2d31d27 b86ca676e1766d6a86c2ea3c3cd2553fad9763bb14826591c6de5fbd33f0dfac cee6af7e09703bfbf7a8788d444770af20d30f62a4ddd6548be6c4eac7c44539 927ac8f524127b753a21e972d50b43059d34289f68396a0fa710a0b6b0c93255 81211e5100f99d918f6d9317a1bff6aab4b11e5bfd3fa52200eda5932acf2ecf 107bdb16f6a6c7b24268e82aa4f7cbed511e5f9d9ec967c77a9820720707c6fb a211f5360b1df3aae657ab9245db42fcb1f5dc0052c6655bbcc3cf7c2a0e5ee9 765119cee51c092c8ff9789aca24742c5f7453049f913dbfe3907a8e48c14c61 1b4e6f7422d13bd7e2c1ff99e7a9416d3596783d69ad106a48d6191f0e7d2580 b3c242995087b44e6ecaeeff24dbabbd4e3c7f64cf6a8da3a0018bfbb364671b 1d85c55eb4089c73c1d7d091f6acc3c4b57579cf24ab671d676b774b2fd92d47 3f8c52676c482ee1cf4f443fd688789a31b619cd3b30324ee1e65c70b0292efe d6975e89ee4147855c887caf4f53524bd2dd938e4f59ac7d2204448f99a30ef0 491d6b218abced0227165105e43c61ecdf96e1d0071283153b89ace3b2812379 819271bad851e7f86cdfc40e7bec2af465b5bb8a029c098193814c19e1c1d4a6 a81c95c513ad31ee11272817ece878e04e1a7337d7f6f9a65cce81cd887c9b10 066e2050bf39ea974691c64b27d96334f6b04330b0c297fc82740862a31f4e2a 670380121caa95a4df8504478964ddbc66df990e029059e3a6abb0974495d116 0048ebc5194e547b19b22a47b7ae046f9dbd690915f4c22b04c57c941645cbe9 f3bf7d12ce43538a0572a4fad3db2e4a30c58ac7dd53dfb71d77cbd523c3b572 1e6cac73161480c16f6a11ab21d0383a0b55bc431298bd1ba87c65d7e6c39108 75193664479f2dd1ac1a078e0118edfa95f45f033ba68d90aa2e0118b6007fd7 622b931b8f69cf49bbe2adb4d1fbb4832aaa88e5fe1395819b3ae0744dce455b 8d4c56a68cf64d00728bd0ba6aeb0d3f049536bc328bf1d7adb4bfa932f0bbb1 40bbdfabe81658a5cb42d68fca8cbb0e5fd74dacc7b323f96f79c6d8197b1df8 4a464398d11d4e441ee37f949f0e7f389c3f617fd4f126266aa6ef82d44cd9a8 095b77189f780e5549f75bc7f6e31af900b66009a34d40281893748137732af9 514c1f0e4cfb94e339ea4889f63851a6568fe24d16b6e5fda30ffda0eeb9d4e7 c8a7c1eb3b13c4b472629c068df76586cb9346dc2babdc670275372c3a3fe53e 394de0a0054afdb7c78f6dcfeb4c068784c7471c877ffe8e72ef6ef96c978831 db9dc0c745c5b52d137a09425057fe302ff8701b4e308aa7554419a5bd10b7b0 3195eaa3b9af64e6fb2a38aee617ff80dd376ab19c0383dcbf975a6d3bc6f887 a47eb1d0c0b31bf43e82911252ce9f0d0301546f7f8a9508b30e0769acebfa42 405019c040282d00ac22d7d615cb6e876347f2736085c2e1d4380ae3159ea3f5 202372d85f6b22928b905475ae6c228d2e31e62d2cdcdc3173c4d830a39b006d f553bb04d59fb51f6ad05b54777db7705cae627a70c7c9fa63945e07fb1579f4 319b616ac33ff3b33ea2951cd2518ae15856382956a3e6bed9986818393662fb 02a80cc440783d0ebdcc70a5bcae00a6d5a7ea12938436dc9f8df50bc4d04e52 20424fbd796c4d1dfe0f1f4d90d4f9bb81ed93d18006fbcebef2b7f7c3195062 6cb94a2f3cf266527a8dbc04f1b72cdc124874cd25b5289c799f2a8078ab3e46 72d854086a073d89e9489ff5c8e62ea6b7f1b954abdbf6e461b38834399fc341 bee46d0bad0d19f14828f93e87ce268eebac443afb9511f7440bf320addaf7cc efb92896d63fbe7a550c4732098ff31b91ff33d917e24ca6ccae798672e325fc a0a235774550c83050f0c6d07e46f1db9666cd75ce78b16b0383e26297206e3a c561120714696d195e43b4483e9988f03281b1348198666a82eaaabcca2608cf d33367a8f1698e24262396f5d3e829509ccfcf48248de20ee8554a07337eca05 93cb9affdbeb64c76109c70b6afe3f9af8c0bbc672ad3fe693eaf383992bb031 cb9f85e187c729c329645eb58ec82595da32bb9312996452db0d3bbacf21d42a e5afdc0b5ccd4a59f667c21b3af7f8119a506f509e9f805b3b5b820b992f27e0 8b91dd2f3aeaa21730e52a12c38a7bdc34d27e7d080918a31b3eda005dd3b75a 5821a70e5ba67d7b6ccf3e48a80e9c01f6acfb59b0764d98478a84598f158c8b b24529d539934ac8d2aefcfdfa3e7b224b1f1f68d535a3616a44acaf62fbefab fd56c947d33ff54e423d8cf8a97f72991678b8214e24c70d70c97ef4ca8c8640 e64a78b03f5481c2fd891905ae03066bca549bc025687372579e2f572063cd3b 8a2687abca154a67ddc7fc568835b041381a12f291a4b4c4628d929c671e85b3 c0cf24c61ccf6efc4e40bb44871cdbca20ec045b8d1142869818c4eeeace2a70 8dd32fdc73f0340bc5450da6fdb332191a52bf21d411a831d89433a05660fe0b 5b00f4c63be65689a7306dfc077aef31e62982f96340a314b396382c278d7dbd 00dc4ac3ecff6a717691e2e0017e1669de34fad195ab7b051b8ea82f18932817 c2d310dddfa2cc43538f180d5f275ee2cc6e49098e42504a9e41e822c83bbc81 a433ba60c17f3146bdb998ee2e234de0502eff3ca5673f46c3169c3fcd8dffb5 6f14a1a841e0faa8ce5402ea342fa31a8887e2f42993291cceaf61ee29667c62 23715f4a0db2c7ea7b308eddd814fbd2443f89d096bb59a0c33ee42b90f57a36 fd0c1486b6687e043bb733d3f2028fa71ea4440b4d62b9d6d8e5e9222a98b0b8 98154c8251d720ecbd14f260345b458c15afe36194207e9b2e478c6f5753f239 cce49acc97ac309f5618cc8a2853afe2230382a2a4a1ab84a3002b3276627a16 e2d0a2202f1a9f31dcb14951f7f7c517471e60d1f41a55f406d06420369faaae 7ef28138ec1c452f85211f377b646cb784ce6e65f4f56ccfdafbf674ae29615f d9a867e4aa26ba59d9077154195444470aa149708f4155afa7029ecfb2d9b0c6 3dd8a472d38358ff662ee26657482af045e756b52e7aef5d1e19ae87e6fe7d1b f67836db419a7a23fe35bf1f9fd664b48d6c43b3a975e6e169f88f806a224c69 17230f985b41de0f899f2eed400893bb4ec09ce9ae40737650e6f843179ce9073d70739df01b83365a1d54cc17bd0e352920d7076270dd48ffe2c060ebada00278962564560d281b25e3ebfa85ad1d9774b876d98ce946c0fcd17c399ed8c206f732ee13fa38978db75ee37744b059a06b000b4aea1253ffa00ea04906640200ff8d0c0cd2ce73ad5228f4107f12f38590e017edfff3f64825d72bea91f37d03c8ae01aae37417607ef72639b03d5e8f2961857b71b675ce015745d9c3160807bd21e292e3c5166601a2cd73d5c4e82e009a08c82f244cfb6fa45a38e0dbae05e5422de66e78d3bd215ce68be9dcd73068404216daf9b668580cd8901ee43a05da76cd1eca761462044777367ceeeb2ed2f3bbce349b295f215c595976cdde05b004a69a12ee0b9c7b7a97cfe8f0cd60f579e8a88d64ba37cbcdbc6fabeca00c0dc917fc29e5592bdf196dc61044302a5c6b4be635fc38945c317bc6ae33970341363c67ca5a68ba3636c7cb114445d08bbea1c59f9c5a0314c29222beadf9000ded4fcbdf26f725ffb32fc83e73fa091daded4698e914d6eae6f4ac73e06c03c87ab367d7e3c5291e3701ed297e2867d5f75b6fe519b254fcfb0956d431200e5148026655f34a5f7b48ead7f31dbc826ab5ae00fdaf45ff358867d2b790f3061c1c3ed2e29c04ac86c69f086139a6ebb68c80c271ca426d71d40682788c7102d429e262e67ca2e43e6079c8eb5ea3efc88937be736e07a2247206dafa88be06f11e802db5d64a13dd3558ed2ad442f2cafcc38e4081350c81be60a8e163020296d34a2585f3475c8f003bd443a409afd10ba424d5a69c80a02bc0652586c30712e20d12d05a6f5868bab2411669d96f2020f0a9317d1788aaa41810b2a5fd0ee21730f60a1a8d23793b47e9b252fc1c8cf235b8283db1568e51f01cc1ba78a869a00e46c0e9f478174c3ab517a7544fd13b279cb9e69da909b76c674619d60b82e858b897c4ec0d20f8afc41ebfdd431b0d796c52fee94f009dfd1784b39300961e917d437d712f087db67bb727abf7e014bdf4985d23f5b2fbb422a35f7606410f30d871752f0fd4f4a36f523f7f57ccbf7d88118017eac63266e7945fd40c4c325f16d90de1a024eb33064d3c5e345f72b52e7f9820f277d10a6ac98afd0f6886b54f19a831016f249cbf73ea7253b76f03602833eaf80676fb9751f167018e29285446f47cd37965772d7e6ac56e55edcbc63a564cc38d36fbaaab21e6003c87f87e60fde59d81a0b689b5434638fc14b1c68ac4b2ecf0c6584a312aeb0bdf3d332d159089f482b830e7428d309ea30c257227bce7f4295da3355ee87f038ccf03f43f556a48903b38e220ea9cab7f000cb706194860d20b5cdb5570a30e9c08c6bbe4a35f97a86176930720a811ffbc5b74b02d28f9a83b698bf3222303a6d4b92ec04cd4835437095cdd4120d9ab9fa131b90974950b62c567297ea20573224d9add08b20b243118a7aaf6d98d90a89c563135c9795ca36d1355ebfb049448340174dfe5a7831cb14ae33e5e5f943f733c40f6b0030e4387b11e82b4085dd74ce6397984194908f6471d51bb9b129c51fd225973b54fa674f1a35a1f00811e0e6ffd0c5561616a0318ae94cb42130a9538e3bf82b3de83fba7f5a8030230055165c476cb01cce5bc3fd8cb6f39c15dae402a8c77fe453547ee29bdf6085ef231dfda0bf3efbaed207bb34eb00a6a12e16ed912480dabc0dc0a84b75e00f7d47a7373ac24a07e2e4e7aebd3c9d7e08592bc8e490925ffde720655369f0c429f1944f5f4fcbe7dd572eab1ebc103615028b11674215aef5d19473e8b9004701a5ea467347b7476e8c9f5cbc9be29994f59cdd72724a205d79017b84b140e25d020cb33925244f06190770ad0b21f2f933ba972ed0dbf1c0807967446d50de3c3709163847421f3020f41a4c13de3927734341055ab2adab7309d03498c0bb30590e96fbdb72364012c05563cdbeb7755d3861316f48be382c2ca4c045f084004169d343bed634b94e9610c7f96c1cbd7352eab778c84b2287d4034c3ca099135d0078ae5caad51ca5b0d15db20950308f55a563699dd5a35485d49c50902ac067a4138c85ea84dbfdf63fa9a2260f9e6749a39881ac24b754e407ae7b10ccd7855e5bb9c1728a4d15e755b3b15299b584697eb079ff7e40f40be97aea20acdb1caca93dfabb88b292f44de4a4f2794d4c0f823b6afff8a86b6ecf614270d395fee55b6a543e8087d2c33fff5928b8ffeeb832682bef558b9544c5ff8b900ad9aa783c46216e044e64c644ef6db5185f2e744c8cfa097db1cdeb697fdd2079d4380b0aedcce4b3a987ebcadea8372a8654b692fc21af840babc60b3a3ec03d62f0a3d04ba30442a43dbccdec12bf84149ddea0dc55c805f8912bc935312064fd0f07aa953ae16caf8c844ffdb0def386d3907b8fb7bb670ca4122a5bf5f0826478bcde89d0b458b15eb2192077829434e907214d66b60b48021777108340932a0586b625d81588a476ed82f7b204e2a3fccb03ba884f4f0c175ed7f53370fcabf5c64714eea81a78ba8b3e5908cca94454e10c8f089adb06ff1ad210b480b2d6326d68f7158935a94b475b6e89d2199c7d2338be02e4729ed7a3a5588a408fd28e19fd918a52489780bb00211b1da59388cba16f40584fe3edbe482e54c04951d0184ba8e88364621e90c8e2dd9d412fc102bc2fb0cccfaf55c54a9222608c21659721abb1155ebb29ce370bece3775908e79afbe18c5c7377ef71d94830bd0926cf45c7a4eb5797f4814beb8c64d770d502cd613cf9968ab58823fe0220a4afaaaaafc1b3160340eac0f6f61bb60c516582702ee31bec6fa3afc87aa550dbe228e43bc57665525a54e8ad19c2d7eb505495d57f14c3e2580b4a9c7e4c40dd6067bd21dd587f9a29838df24454066927930f70882faa2b822a58043ae620537fe517d74ed5bb700ade9ff4259c53a09a246d79b51aa3267531b50c3c09208f2efee6330ac5bb9f32bbe7fe5002cf78ac37ae1d150262f371c58a50558085bd972d38b5ba07db4336bf3f1f97e8e0fcd420b90e496eb7818546c25dad9f60f393f146ef8c40bd7909c8c2e88e3808907e3899edf408a224645b6c9d0932408aa63c804de6b6f5a25eb5e3f4bd176b0e00f86b3a4c0426df122e8a9d2f0860f05ec7999d2813a2694308561c68e84f187cc34b21ee581f24f01e83520f42c069dd60f7a71b1b410604acc8e947fb13c173b2c4e67397489b55502aec27e085d87fcf45f0d697a775a8b4d3f00b796df32020e57223c3acd30b44932ac8aca0239be70e2aa6f8838aac7b1570943ad4721241d92023b0fb277aa01f026c8c6044c6e5215ff493a60eee60731db9ed9648a61c15e738249161de99c5ee30fcf087029c72e70a4e5a694bc47b7e1b92eb453c96888ea333fa50e74627a1c9c6607840ab225e95ac5e2716c0427a78849ea83c09b5143ab676306a6ace63af2950a9976e1f155ccf2c58003bcaca20d1cd77769e2d7b45625541d6568f0d367dd051a0d295961625954d956ddabc668be33e5a8fdca4f732d2e91b625879482aa0dd62ce5e26b390a290e7954da32f9f3c3d8ab386eba13c380a8f3e2a7029e470d0ecefe84fb4fc952bad64ca7e5ae8cf9f96e7152842efcfb84bec0fee89ef701a4d4067c7cf34a64f20607bc242c5206323d7de857e4d9df70c44935d7b9ff02700d40782fb4fc01ea3598496a698d5a8d6ef9a9a7d668c8d023f191874d1607aba572cd2772e73e8328e837bff984ca39fbdeb9a5165aa814e5f300f9585e0d5db4656283826546f8cbbb8b3ca5fd7a13ff307662c59d00605ead39c6576808e7bb1b12e0ec2d18fd142859b97820df19ce420c39c6e43dc77f1848ec087e0b0b931b648f935461f3f33d2cd2497b45cedb63f143d59b8994e3032cb786110b7bbb673195cefc6bf390a98ab30ad1cb9931987e2d3ea649943831b5d932e60f920457b2a3dbfa986ae048792f4188276ed6baa67e74d10405320c9b68d9960b3dfd0e2b623d6c3c1510efc4518eb1318c8d573af792ba3846ba8123ab1e1b04bbf5e1fdbd84300b3f72b8d57fffe63892cc3397407c61f21c52c6c5588a340128e70b1cdf886e16496fd37c81d326932a5b1c604929aaa3a050a484306af206c85323e799fa24cdb530960fb01b12d5b9a06d45ae440e216d76bb8b25c4fe05abad74d555f68e6533fcb3bddb70b04b83668006702a1ff681072347e822930fefe7003b6c23c2d516a49d033a3d284f883d97d7e23bdd58d8185d16accd510d352c7f5bc4c1a0430dc32a44dd2d6409bdb484f986a088bedcbb766368db2f04ba89b7fea50bef653041b3a631dcc8e4a35655c86973e928cd698296d1b8bb08b07cb681486d118686abe0989e6dca11b4937c29636d142c8b18b83a6186ea0916cda3f87302528a668a695ab78f7515a3c58dca2e0064fd7f440835110984016e2a484181df0fa8fed204694ecd777c9bd518218737b1298745b5195a69ed0fed0d89834ca548bd1c47de45484e8ac10b8dcb00f3d9addc99bd7cb3f0c3a000199469410e9398c109f5dcd36fcfcdd2ad393310bdc48dea341330e480dfc30e92b562256d30604345d8ea1557bc90c4fd4d909e5a5c45be33d4a96eab0ed50431db8de48ae7b6a2262fa558ff3e601867620ea63b96227d09bd8728fe56bf0ba57c9817199411312890fd36251e6e1e12a164411c5d900436fc86bc2b69ae045ad223a25d5aaf593eec7819228581ebc8ae2b0e4cefe18f804ba2ca4003f206e465468769239541c25806cac4e3ae318355ec6c45032294fa45cbaac444240cda648deb47ddd3589e15197a010a9ace09fa97752e4f703c5bc79c112e08c6095b5ffe70193e1ebef23cf2ee04a92f7ad3ee94bb23e6ae756cc5755a87b4240a3ec1c5c153f9ea65301fb18ce495d27877156995bab8c10419ea843a5fcd950b43be371a5dbdaf989ab384e355ce96d808a9e0eab5c2e7ded29da832fedd8209ab85553e0b60ca1b427254ad9a874e77e704a1a4e79b3bd60d5a1055fc213a026df15239068362c9d7c5700f142dd468b8cb17366d4c7a01c8ea8b2c45503f05ba459f69a6ec2f5b9da3716848e6d6011c7088bd9ecd716e1cd5d337b0058352ffd02084af8da6f1f9441cf09edc1cfd5023ca3df351ba0c6b9cff8a1a0e240a1894a4d1e36200dc1068096a7f492679d130bb37e5e9bf3968fbf15932eb440a11a987d260584fef6b9f26557a0a0cd97d8c16e90c2310b1a1c156d4d3974b01ce2318bed668ac515912200e2c0a52dfe9837c5604a9d6bf75296b46fbfa5c059726f1da49bde9cf28b39b21845f4e1bc0d9edb0a51c4dacebbd7f889d406206eeabf82d2cc556749adbc7d7d390e969bc661db95d46720d402136e55aeb230aa057cd8698754ee3b3a71954d36e6b4caee5077131e7a45585be41af47155b02fa5a7395c341a407ebf51410c3ff28ea29bf274e13babe67dfba6154926046033fa205a20b2f6131ab94da4ca0d318ad7009da69554b6139ee53f88ad7f3ae01a06850060a06a66126c57a8a9ef90bbd29bb8dd62fd8458a05340d753f41a70bdef011104990ddba18aaa2981800c9600c6dd152cfc7c07b7690eae8b948eb0da71479dbfc4cd2d9fb1b6a1e5caadccf2492edb365a457f556219bb10ef6210ecd0755e388d4dc2bbdf114c98255a23865a0b40c6ca9981cd88c575eb1e25b09c7076df1d812235407ca3f841f8232cba7c7cc365038ac72eb00f06b801b64021a8d02f48ca1cc1a30442d710232d6e3347ccd025dc7ff0999cf0208995a0c06bd814cda164192877f49b05b579ca010f2e7f96bdf8651ce3f0b04eecb0e4c09ffe40637e7e252078d513442e432ca0c0bf55f45e206d25bafebe04b2b854d01d36ecb90fbba31348eb63c343f6a65d06512fd53b109dbf9bf046fdb9d929d0ebc515227e60b4c0e5cb0adf9e832647c64a0e25e8fc7283600813953c4e45003ed0062d84983e9ea16d35efd9ed8fbd30adfb5b8cb7b50a797adec5d490eab76d89b63a9f419d71a02761599d9ff235b1c30fbf080101c3f35a50180c141060118cbfb83f81b0ac19d1fd2e194a127793e5a9bf25b34061c47818ef7621cd10f07ad7d43cb8b55c5ed932e56b7c6b5d76ee2e510c7d2139ad9a43bfec0286a0c0ce1d6325f3d8f2ffaac9a16be6bc5c06d8d61556ea07b3ad20bdfa175a54b009da85fc3bb250200a57577dcdc84e2d4a04b8f6bb6989ef3c94a61b0cedf0b0af3c5674fff13022771b170dda634fcc7c024725aa05dedb76030f7793e763d02a345751ea84cd99842293d602cae4d352557a4f63e01ca2d1f8eb4d699c837003a0137694eba125d54d24f1735f2780266150ed34fcd830ac02be0688fe81a027c2e8b9f828a8e9b11aa61897438e7db1ceb82e966d2c8cb45d342b98b9e91020770f61107eb366983c98654d52584af846dc5bd58ef01ef870f435966ad9901e844d22e1c25063a4090efa8dfe4fea9cd36d2b1b9d73645f4d1fe9d64bbba0c05fd386d6caeaefb2ae00d793b844790860a00186a0ed8cdc84dd8c41317c100e3d5b13fada1cabee4058fb2433e1f8022603fd308194efc536752bc492e74016a6975afa65f8f53bba3d34b7964b196099536b9f773c66ee71368823fe3a8043c1ae014155d5e64b1e9b1316a37bc6230785a2562fc85714e1170c8661ec70227143270baac6b54573b4d1b92e85795785e308b027971e0e9e6cd6ed2451c01a5cf64e08d35900c312ea4a7c56a93efa7adcb936ddb76cf7f01ccf5c72b4006a31d893f0a7b7478739794950c2896c9b7be50406f04347becb3da9e93c6450bb22b00a2892a8e0405f4439b92e162e6e7bc05a594b4f39c9abc46749cc5180533a4467393dff2dbd1fe1fd1cc4aa9a0153163d87cbe42447cf84b711dc4db0a5838fb296852629c07f63c5aefc863b4819c9ac97c0ccfa0c7ba865296dab90ebafcd43da804b1b952ab5a68ec506aaa7481abf6ffb6f3a79394906fddda8c02fa38c1bc16e7b0a5a88e4686695289f2bb49eaff2cfd8f75bdd78e13e82f8509b3cb04784038751aba832c5465cb7bcecb2ce1e8469a9287c4bb282c22519e09bf4fd349c8d81309d4673097ec859188d87430c4c4d3a776ff8fcad836a196057039c7c03b3b176cd4d7a056a93f61cf515415635ed32e3754d1506db9d194093cd04fe2701402e8fa23c676a633c033372f1652df2ca387523bbd3ca566d908ac77c40e6e681a8154e72e0989719cc5aeed0232774fc9151aa5c541711c3c02f19a0829c41e89f155b790c103e50e39598c37befee895164c542378d8389106441ee000edade79ccd232f70df075ecd3b595c23b7912d935e81811168534d0ce700038fdfc9e90db14aa968d543649c94f60ad7efbb99f871840e969f931a0f9c80df3e0775c7914401f95ed2e4045d1402f466847c2a46e66ec9c303493c0db43c86bb048857946a522a88e6ae9b8b38a07e742fd0f8510d41870d172f5a034c7f9d09739414f51dabc3ef424d7caeeeb47d341211a8fe68aa855837723506155537fa3500f1432db19f12491eee7b95704c9ff0b5de7bfb7efa8f0d16180b false -check_ring_signature 3068613e11299fd1dfe7d30c7d5ba9f368dc71372502a61cb04367a1dd6bc4ba c172eaf1b39ebcd5c0a3903cfed163f30417e87352572596844faa243cd49a94 1 8d07e3b7fb6ae4d152a87e7b670f2e2a43b6832c056705151a1d1a49ddb793ee 3f5907d33dfe023e8fd032bf7be0abfa1163842b65a8f654c202940ecbe5a1082feeadea134a5f083e46610aa940a2a1471d9618bc33d0211ee02c413c34840c true -check_ring_signature c083ef344e985c780fea1d18c2fc363379cb6df3853493f3f56f4c19b6db368a 7b74b28f6f6b4c2129380ae0dfab434e30b0d35860edd4161f7e3b701ceb6bd0 4 4f7f5871e419099e8c4142f173485d36af8a414a2e26bb6bcea436354c1e2261 a7b3a8c485debf09f3c92503facfcf66ff4ba69c178c2e918bd7368dd55aa225 3fdf2f66b40eeb8f26ac4f3a655d3266cd9089106a2a72cd817f1a5aece858d4 369048c0d6c6104e0962d41ce8bb23860e1c2db23824938ad19219e5560c9d69 7ff58f5531f1bc74ed0f82e0294655bebbb363ab4cf49afb774da940828061056d1c662c65ba0f43927d79b66546ef4468603d04626c5760dec71b08e6829502b66f90edb64d32fbae860d40fa1bb10ef583ea6c261ae0ffd2aaa5fe21a2c309d97939a63ed8702178720dca1e625cb45ed405537a3ade9d7a814345a5e4060ecadb3a03c041d1de4ca3a441c173c5197bed63e67b1b88aca14bb66fe497940ff407d3296a4415cf585b26db9d338264cb7d66347aad6623ca0f4756ea90bb0c5ad7395d103bf753c588877dae7e415b3605bfe92c90673481321026f0175b060c6cb754321116b77fed1fbc138e8323f233592a13c8089f92ee4b8255595400 false -check_ring_signature 7c59010ad89e4f7481a0330dcac35fd79a12241acdf477f6b1ded630e4dd6863 aa33cdeddcedee7e4b7174801b08af64ea444c20aed01578284f92ab7a11f155 4 24a510e916741fb2652479932ce7745009d9ef8a2a0c17566cdd4a7b9c1ace9d 8d5560db86fa780645c8547844a7109e99efe8faa35011a0407542ae44a9992d 3e78cd38338aef74aff43880524d3f9a83610b0a8234a848ff4f0997989d1fce d3e41b35b2aab384f3be7b0fb23ea5da3e8a94a3d716dbf1c90c6149fc59fd2b bb9acab3b290f906a000362de9414e4579a7ec968de4ec54c144e72943cf5e098e2292970e052604b46d5051dfdb080fccfcc0f4fb3920131a0291ecc06cbc04933554385e55479544c067f0297bd38bf29200275cb224ddc70b1ae41df3b90161153ee7a498c76ebc2194ef70c84af4802ad9e858606290d995e6962ba71b01810603a9882e9de04900a38ebd5c38c7686b7d64546a8a04e15c877db1f2220ba886fda5d9829a9f67974a083f339c2e7fe4e6cf71383bf74ab52bc761affa0b07e27e6fd8782f4977ed87fcd19a2b1142447ef06c3425e1015d2fd858053a0046c9137911df01f3df6e89b1bb85d8788bf3f4e3f5e6db218b92c0e7d6c23a09 true -check_ring_signature 04fd431ce3869b3d44442ac263fcac60d586479ac06c3c20bccd627cc723dc9c 0c23a3e26945044d1b6c8c0e01f0d90110132bb3bfd1b5c31bba657406a16538 1 1dcd60eb2fde0d9717be8ca958bc01fc064b04ccb189c25158b3fbbdc4fe2907 60800455962c9a577b4e4033e5f807eb65ad5247d7b9e420ec4c7d58d3448d0c1ec2877ea63a22a545a8827f426497ba63e348cadd570dbb6483e03425be8c09 false -check_ring_signature c76a8cbab4cb3c88fd028e141a785df094dc230ed712ad1e1b534a3353c7b248 dbd7b96797317b06c3d4e70d97276177ab48b4289a3d090b61d8bc1f88e1310b 252 4c00225af924d45a0d7dc3f62ff47c953d2396bd3b16e0b9b17a0246937e8867 133cad508c37640c77200207a4677c155bf298942690aa54430abcc01fe31402 ef6673572a28e017ba527ce957820468949bd8b1dd4440f8f6a13726332c4780 555a45b864f939a310d8015bf8e455a99d2cc941de22b343033df41ed33c0682 ae09ced715f4ddd4be7416c578ffbc771c538185b903290fda87ad404ed7e267 7d1d9884495990957228781fb65c93ad2b34d9a7a5f0d66b45aa1f395b26e0aa 40df0c36f0f3b8d0e7160b443c0a2d09466767b49e53f3fafa81240f28895f35 7906b4bf8845b83ab073917fb06b2f02f48e9dda5c14af3d759ec99224330f3a 5d6eb6e4d14cbafa295bcb9166fe484fa6d4af0039030f7b14a1451aea91c37e b0c8717b50727f4f6bc7cdfc91e5c2d2e59ac4da85b73d5f9021bd837435f81c 37af2e98a438bd760e7ac50030621d88128d9cf14cee3e457618f93229295654 1d3ca7e6d3698c7b16194e1a7b559af38c466ce7d18a82f860d0925030143aef 46d2b7b658b7cb98d4969341fc8e6f879e142f44997e11c9837eec5b33995c5a 313250248e4cee97facb518c840656604e65730bc11893e381f43d6d68af4df4 1bd0a3c29565284fc6675b4951180f3d86ae843ebbfcbfada35bae18f2abdbde eb2f1c79ec94907454bae15385207104e87be399193f71e3751e9be3eaebd3ad e6f2d10299f7d290c10427ae4d6eb58a4d783e5404408ff58515e8c191ee9565 f867d65bf391c14af5ca2f6e768d0de3c84c036359cd6615ef58bd52165720fa 8f2be18ed4e96300217151d70674d799030a04f272c3b98812c64fba18491fb2 a5533c1761cbaa18b322a9ce105132a8ae18c8031a938d2394dc0d7968ce2d17 f58084e5b0fd8f54d1a870bb532ef79a300aded4c30c263d9dab8e05b09acb5d cff3df6159a2e4435a2e46437ecafa7ef705180499fe365d4e853d0f37feabc3 e3526ebb127e0b2e19efd463e40448b6d52c6ec05b81047fc8b73db5dfd8cb77 63fc016206d80bbeeb16eba764985176c065662dc36cec3207cc8f9f92bfb162 72f923cdcb3bbf6947f3da7a49e9259ca188157ab56e60374f351a6189c0dd77 fb0fb6686cc51de3228684161fe815884872cb146b0ec40fdef2ed23e739f284 8f3c5f9c7d70c25a1bc32a90e10dee985ee5760acc570a23648375dae24166b2 5899f9e3104101e4563ba60aa7b4351e692b213215376f21e9d9a60d55197225 4c0fffbbff67703d0fd6a5e6a5482d9b989563fcc51d9baeb5aec7beeed61971 f759ee4356cc7c00d0ae25f33a724713e5af525a93222cf8446f795ffcc338cf 2183cae9fb8b6d566312e29f27177e2cc12e1ed02c619eefadeb3c7e2dfa56a0 65a8d4e1edb8f81139ee747c0e0cddd3b7a37f35cde62c1daeea804e6a4c8dbc ba6a9e8ec1228da419e9d9f96d600b74084e5d47dac05d510de480ad2cd6badf 7d4d2107c30f384e684407d235fd61e282b18e9ac67470e57be9f305a649e2b9 06eddda5f877bd4506beb6d9236625cde4df9684d8499219ad0a1c74c248f2ca 8b51306a87f3fea1bdee12eac76de9ad85f0ef46811d95782844a12f245e61e5 2f3b34c9cade1e547f41d14e66068273847da1e71713a74f1b8be6b2ea139ad3 e287a3664f1c49c8878b59b4dd9025aa3444939d554c95f5eb541ae1171590b0 149de0c43cafd6fdc6ebcb6d456afb20dd0dace9ed588a7c21adcf247c92c6ca c238271d900af9cba3f2485450e561d0ce66a599a814d779e21086990c50e5f2 c533f624b1d9915e70d789e743b0919dde3ca712ce8f7ece082319bc3dca8477 b14211220674252bcb6870b72bae7f638b9adfad1537b467ab3a595d6448f13f 2e60fd3351d1fff219233029dd5f4217f58fbd62bfe12329cb49aaf84d624a95 0ea31ee2931113169f83c75a849ee11343e0d052b427b0493985e7d121580267 c1ebe6b825f0c42d8cdf93fed0ce180bb35ea2be26aa028b957b8e11eb4e863b 31d03d78ef547cc52a8cbaba7b6b134b41a5dda09673628a2bd1f82926a64ae3 54295f8944306ff8cc5e1c9549f71b56c5c9892dc85df420da3369dbbe6646ff b0d17be91c52ee3e62844cf13b588d6f7ed7958e1b7d0f637ae3f9896face854 f994748393dd6f3a0aecb7cae69b556cd1b718a5ae0545ef73366d195cbb6d62 6b2e6aeca22c861872f399696b0d0271806adca873b4c98b69decc40bef57b2f 4086712528dba2fd95a4dc9acf3907858a826559809fc464b2fe781d6326a53d 17882d6584d89e80977b0f1e6b6df80083e9da87888d8615ff3e0a2d4774eeb8 2ef26ff68c619ff15728a6d90cf3f6443354af81c06b3e3c3b16f216f1ba4f75 3ccdac9769fe82a58ab40193ba3896569fd249777ffcde0f558999c3dab26c93 a531a62e2d7ff9d0f063ab5d30fe74af8a72ff91611fa49bc344ba94506b507a 97544cad25fea782623a94eba9e704fbff17ee90c829110694d8e5d7e74e9a01 6307e69f672265f5dcdfb0934ce2b90f79641484e4d84b3739cec91d1f754cb4 4be1477d2acf4a691faa75bb97e1857cd4e24d422660cd3ab30a14e74c4f11a7 1c361928cfcbf55bfc96add35c3e3815606125b8ffc320633645de1c50152602 e33403e8f6494bf2c5c607fbaf55f71617b776d70f86c16175cf3436bb88cada 3216402d7662949877295f6a16a5f5ff82f1053d23b6ebb052f3231eab3c41cc c2423fed7a04f4f7a906dedb0e754e77464218abf2ffbba755612fab33147899 85fd728482060b60a57a5775522c89b7628a8c3361122310771530cfefb57fcf 6ba3f44f39db4a39c7a6ef8a5c25a437325b985a742e01d650f3a192b3b552b0 2260d278fa89298436baf04d4e6e09c743f62c6706e0b23887d8d110c93bb16a 242b7505416c9856140cef9241d4bf1d56a02957fbdedf0d78d8906c05712cba b6a6f29b10597f764d713a65ab8ee99bd309aea81ceafc18597c5e497ea72f96 b7b9eb771572937cb131272821965cf87e63b8beae5fe53b91995466ae5a7156 f29c2476fec021d618233124458771f37b1abf55f1ad050a079f9e5365a1779f 65f7754d4d03758091b61e1e7e75e799ba6ea2f905c2f3f6059f7bda879f6fe7 fe1cd9ea1c2acfed52c1e637916c9b49c7ec22e7617e026570b3aaf335ca0348 d1ec52eba0a2d1b7992be14b6cddc71dea59f10b375a7766379ba727aa4f18a9 180b0e6dd3791be28eec158718cf6310ea800f018010a858783c55f8bc717b33 c26924fa360646753c3486bbf14ae1afe5f8f3f6cb7a1cc9bdaacf40d8dfc4b4 5cd672eec0c2172f0d0fdb6ebe9ca71cdbbe06f8d53f6d2166e31f0c3ddf4002 d7420c0fbe130ff78abc3a57e217c326c8de11e7b3847f3d3e09165601dc6f5b 57485089b17f9f895f4911ee8f9f0b3be2f53110c457dc6743579cb851ba5755 79d4c9449822fae9abc4b31e59236647065faba6427382afa20cd0fdd1ce1ebf 8c4b0b26148b5960f95190a2b2bc864d35e6757c31e46e56faccb85318f5e6fa 1c11e3e8c9ec860ff2a5f4c7b70db809790089021fd385d7e397ad47ad982f96 83e744e528e3f49132ebab2e63674e52d590c8664e08777f82db2ebb935fe70b 187e957b9d423395f3098291562b0ca87dddce032e0f8ae600bbb93a45d8ffcb 376bb42f93937e92796ec6fa7ccf3bc524664cc945bc221bc19d3c4556e3d5fb 84d4798e3fd6de516699e40b62b229d797e495b9ca073597b6775ef9badc2480 bf5cd670c72f00a37109bba06c2c5311a8169d3128703909ce23a2ea03adf305 a0957ab0a5816984460edf6aa28e649c31e99339b89c99ca2ff808ddb783a4ff c5e6a15c8d44df9b22d2f4d4bcd6949c0158791dee947308f271ba31eb3ab960 b7162ab62503c9bdeeca05b186e59371e6a4e8b64c781bb3e8088862910ba43c aa8144e0dafa016eef4b774913ce0ce9760d29eb61b31cc4fb18f97ee2be7ef5 79638f3a55b591a1bdeea6053f8dd1bb2a201cc851c4febd92a8d9d796a98f4f 2c630c62421a8b978e3ee7dbf00bdf78bfc3f5bcd2493876f922188b1e92cbfa ffd764c99936a19c2bd0d2bdb139e3c8bb3328ad663cdf874590b51934bf9612 f088137c558366f70dceb0144633637a603a53de272a48fae93149fbfe88d734 ebbd5e73f2ae1cc713241981fee7c313bbd6bfded7a606a61cf2abeb3c525a50 404918036b0b20385938673eee9412bd1b41188bf50f4c15e48a9accc75ac380 14a922dffa7bbd49c33a402faa79144bff8edf738becc3b7fa71ff865b3535ff 787156326d05447240dfdb97f93c7bb5f3b43dc5d85259adafb6584e8d1c5331 28b66ece2698937fcb1737cf3e3696a9c23f55aa46fe46003e2fad697dcc46af ef9149814afea7001dfd1d4a8fa842c34609340e252d7f3da1dd7be812aa1df8 76aca91330a7ddac7ab9ce9cf520685c01f7154dea40b8e090dc379c7f0dc889 de9cd551fb4fb49556bb0743d20383fe69b55dafe9f6977cc034a726f00255ec ebea9629102bfe72e2188771cab8e7b614fa4d0bb857c52c82b565bfa08fa2d2 d6457eb7b9d082e94bdc553ff4a9932f757592f1dc0004bb3ba3a849cd7115f0 ed62418b98688d74c6d236530650abd9da37ccdcea34718025c6ada164d6c3ac 91de7a7e693107cad122f804de277f6bfa6b0e123f58d17d166cd5669dc82b4e 016e2077c6b87ad542dd37540f7204f4e4796d051ceeca0d423e3427debd01c6 c5567b52c9918b501441b397a778df6b6de7cf99bc2c8c5785b692b8b9d2f011 82a046bbe75012bc98473597cb99da6bebbdab2156eac1310f7433daf0d80348 1665010cf6abb1230f7e4d99eeb7b36d4150146b422b404c68228624471a9f8b 1520a4243798e6de141e735ec4b759a0f1a356b465339df8ec797180a1573a1b 0e924c65ea52566b9a8de62dabe4cc7e808ec3de45d0029f7ef0b911c614d6df 2d9009f1148f92797e45a9939cff52308c4a7d736132eba4dde0b2f923d6dea7 57e43a9bbfeab8da7ad496536f9e177e29fe622e28f064941b1741fbfa1cf08e 0657b5f36da4076386705f098a4aa4631f06d4764d7ab4b3059099cf795f89d1 56206a24a34a16fda834b909dd1c5476aaaaa2b977c6d08b601d2fa2bcbf8726 ffe85e7791d0ffdc594b47e0ee42893761bd5743a3e6cb1b05eedd24e7291712 8d34232c145934257dce3f4236a589f39f611af68e429e3f9b640b465c150049 35d809891d2a42a7e7135eee0987e8e3668b50ffeb286e9803a14e6073e0d35f 87c939bc246d42ac2ee83f2b2057a71b0daf3e43f943df6e13f6bc11c7a79edb f594ad639248a3472fdf5ce446aec366dd2d77e9e8b40f44c253b607b3406e82 938300c00b58ac6767395ab99dab26a96c61fe6da21eb9ee7e9b88c5f1b6e43a fe738bf7ef35ad963ad5fef0940ca33a33cc01a1e41e22dfcb397dd121a3225e e676e9cbf04e9183b91c0c6be28a2b7f218408f304b3e0a86988a83e5eed86a0 33eecd0eaf936bed0a8aebd9e04e04e4e4c640ff31b5ccf575bd2a9824c1d573 b4665925f8c214ead93ce0fd617bf0fae1f1a85b09c7d484fe3361ff0240ef49 7e78e4d8aa9a85f1ffa82a74ea905a3382f570ec20ffe258fe6db4a82fa42052 d59c80af0d46de78e2038bb49669229a6ae5a240412e615161ec79c183c88d21 9f906799188c7825b5c3fd15d7dffd2cefa874ffb5c719544f5096d360870afc 5cd23836ae9f6fdac70c594b88d16ea4827ce48aabc45491bab22b519fafdef4 94cbe8035778851eecc0c49c48ea055aafa83582b4a5f00938844a28327f44c8 dc3d71e2c917593b678bb6103f121fa0a7c9e65d896c41adfcc56b9dfdf45a61 a67cb36bcf70798769813096734d1106a91eb6eb40fee5c60bebf46e11ff235d b40e99aa1d67126df0d9cfdd334ded05f1e378bc4fd3502be52aecbfe311dd15 34bdf8e9c47e56c8664dfd9f9c7b336c97232c19f83fcd67573fc694eef97be1 afbcb115e8c53670eaf7216e1bb0cb2c8e19d8578f724305fe104e13963bb7dd f323957e806fe7e5ef3dcd5bb04a2dc4db8ab2f49e2eaa7914d3aaa882196baf d4472bd94e092af3916b46d19268d310238ba904046124d64d6531e1414f168d fffbe794e5a9aabff85422c22c92195b25bf3577f4f13c5d364b374f133706a4 90a2ed35418eff3c7a1266d1db50df54ddeaa1d22657fff3ebe19690f9a78510 c795b664e6eac0e12ad05eb879fc768158ba481101e9360033d048444ceb6723 228f11880b8b02da878635b789ae1788c1ca7b36abddb8acf30a5bd72724081f c3704f97d5a9508bffd5cc41b4cba02e1f2f886ee839f8b628fb55895ea1676d 3c14429e0f125c941057d7564e2968049e93675a2c7e5e58cbc861f063e3c33d 24184f1483fa7e39254c8dbc51b81f6fdbd62d8fd3384ea1e5c46c35d5a58e25 4e7dba475c73bbdfd019bd531cd539122426892d319e6daea10dec2f74fcbe64 8e8fe24480b7e33ba145f198b41a635c6fd33bd15e3a3ac0c49caecb64af77db 944634222c9c4c08318de7c0ffddcb898752e9988592a6acd1120ecc48cd266b 70ed4794dfdc29cdaed20ba988578e227e457ac1fbdb999bf6b6d05481450417 01da4ad41ec5eb3c69af957975a473866dcfb348a0cfc8d022b9c63adeb9f30e 2a0a0fd275d0f18866dca3855687baf51863aa40aa3f8904d72a2e8fa34ad28b ab7a876f66249813444eaee14e57f485b73c79f564f41a5e6ed12806007648f7 a319304f3b5bdabbabf38883bb1a466525cf87bb3581cacb441e230f96fccfd0 5eaefeb89d47441336819f402553d784006623e294ad9db2c84809ce10dcedcb 9db3360b6652070330e2f9a9792456e9d67c759fe9fb9b3d3bdb6c39343ed888 edd323389bf21d64c99ea4111e4e71637dc55c741f9db174a7151dbe0d8ec55c 7830a74b6d312904be9f318985af7eef2f9d9001b5d2186cff7e56f00cf2cac6 6e3610203b4ba6ec590f07e826fe07fb1ac696610f81b984cc110358299f2606 08850fe3d9d31f92ed8d60355f607cb91f33b98921614030827d674bd0666413 c13d00be30cc57aa4d1c1d9ef31432eab612624ed7b8379873bc25d2f765a533 41987ca8ee46b18a376b9860a5491312fb33d93d97b8e3d53ef97444d1370893 a39b47e38dd5edc1d7b94aa3971ee63200cfc3650d65ed5d84b2808ba4f9339d 5f1102411abd91a4354a1fe1c664768370ef28944e693418f2ee75449639b552 8153cf3252e3048e65fd2d0642734426ce76e208c049301ae440bc109457c9ab 7e77f45fbf22831114b6f8f439f4b42c309d0499927e139059fd0b142b36d9a3 733739bc4b8fafd5b263cd8d94471a47c952cff09771c05787e05e8c1e188eba bbb82a0555803aef14e519ee3cc84eec339c4505e7b1d1b38fb91d11b409eabb 14f94b0c52a0b6dfe46f0511c1c41f29e01d651e8d6ef9eadfb94718bf0274c2 08ea1f75445690486bf2ba14fcd22f7bf434c67540c7d882f78b26e2d4c4b9a4 2eac8cc275d24af1535ad3ef855e78f1c016a445585de4afa22424a9628f471d 4161d6a7814f17d4b44615349af82dfc2b38284bcd1166d290fed75259e86285 66a8b43c14db92b92d544543ff7b14a3e4afb54f21718151b73bebbe5d011715 4343285533511c2190e9b1602a03bc4e3dc1ff58a156c7e371f80e39cb89f183 9d82949eb0c5953c0e0f13b69d2be881928249bb77cd77eb09b08692ec6e0b2d 4f0748d934dd497abdaca0cd441a676f969f64844da242a0b52d6bdc0e824c17 90926505b4135091fb9b83234ea193b23d86e8b1b00513fa3d42fd38729615ce ff12a8fa04297bf9e42b8f864ad5cf6ee5a8ef9cf3c8db1a5fa44fa1e1a33261 e24a2b0127781202314eb5180fe3a1e736533215ff3332d03de4d29ecada82c5 7df347289ea082e23c9ad7a160f81e8dcc44eb9ab557d696b6d3025f178e1a33 aa5248f83ca4fe00a408b357898383037818ea8ddbd977f86734477e39a5ab4d 31f910b2394e7b918b6bd3d020d19164546da232b717a1909ac91b8c6cd74bbb 02cccf9de09fe6a54d6e1ce6e825f18e20ef2e0a97f8714096116fd47e487229 8d8c3369912e7c1f08ef81937222f90bda2094f6c69f701d1cb7450c1f6f9967 2ab048f76da05cab15f5946c12b5c0547863afffee0c83c11daf85fc74b6ce4e 0c4006aa7a13238f80ec99ed39c49e6cbf9094d04ac2bb37e45e248803bb47c7 fd827443837bf528aa91e998fada0e482ffe55fe4e3ec6a570e0bcf36a893fa9 c3a6a450f427ad3ea1518a8b79bae7f8b5dc0a750f24f1169f5ea67ed587fa86 af06d54844b292fcb151dc65cb70b127811e1a794e3b5f53cfd13d2c644d6c65 34cd72755a7a43368958169532c2121190ad4b7f2420b59f4d9b9005081d3620 fe4aa3dcdf61680dfb4df3f39311979a5ed644553003212c7c32747693e3f8c6 fd524d67b5a7e1f725d52cffc2cfee6d36b26081b42bf29e652e4d4e83752428 ea2ff33f08188d8451297f18f7578dbd473c70f856b021aab5ee6c68bf12d267 b502b4a270836f0c3660aaa781bc4ea446ae3e6a9d8536322bf6b39ef38e4a8f e135ded12c390494a4e752c5fd66e1879cf95c434c476291e04e601038541804 b483b874f352fbdb8540f1e7cc6f1aff1c2035289e9626f939679d439227011b 5624b92cf65c5928ec138a24d485a1ba38c15ca694492b32a7b6922af96a6e92 5223d75d86e9c2dec001c1ab5b76b07097d95b9ef732a59a866c8bb6e113ff2f 7126285017f355f6e5b345e3b53607d5e0094274cece96aa39cd7d1bcf3b5ae9 05fc638b4a33f66744e1c1eb0fb0fe684b96a5128235bf40c60064e735f62426 0f3172d00eba8c6ce65b0dc1a71f75c89e0eb007adda2ab4f16e39d4188d992e d99d4d675f26eb3f25a8034a8e86e52dbd18c570ac80219f56b37148633a5707 ff7e4303939d326d95673457043652e86263dd640bebc8ed0475e09278ddf562 5eb40f6be964003efb0a7e494a4b2a41505b86adcb45412f9ae651dfee4eadbb c4c3e0a80fbea2107f69a989df27c6f91bf51ea50aa40eaad569e74a3007fe2a 9a3558ade6158f9689d3561b1b8259978bc178c1ec4f9fc5e5a4752cc37f52f3 3114638958f10f177690794287d6a50da3d845b09e30c3a9fb946e127d92ce1a 0c7b2900406842d41154049db23f6dd0225dab4931f801eef62fbed0a175d60a 7da8025b8d4e5a6783106a8e734088d459f660ad232c6b6310323e4e4b49877c 2547e7a9063980222ba7c46d90d1666bb8e14c07b3f24eb7c6ffbb4d781cb2ba c570d93b62b264adaa82708985bad65ca3783cb2cea12851d43bf9c86b77e5d5 b84e08f95d4e4597aa129847c6454762c2942e9d33adfb165eb5e0bda77ff565 52b68d1cb0a5d83efed81cd171397d90e12c3f7164c27304faa3350fc0397e75 8047e5c6e2d5f108469f130c856bba19eb80fc9d32b4e21d07a2cded64dfd6b8 d21d32350ad61466b3af99f72a7e2dab6370a04862c2067c3ce4695d61e09580 dc21c6171f4e3798a0cc44d4768c4dc5fbbf38e0adbecfde135838612926c8c3 df1229c15c293b5ec7ff08fcd76537e87d2b3203d09dc0b8909a9e6bb7a2e3f7 8b8617c91ceea5fabb0da50bc67c8cf75a1e68527a05db559fe48de9a5ec7844 f73a488d5a5ce31166034222bd23a7cc2271f6196365a18085dca6933d9474bb 86cbd861850eb03ad21a27a5600221cb9fb59226d1aaad25e443237bbfb53f3c a707a602f7b21134da10de63d23bf57a4120d69636791fa4ce0d89ebeca4c1bb f71c259fad434cdeaea41c4c70f9baa39c4e45027a5e6df36ada26233ceee4d5 42030c1bc2cec38f16e94773e623a342d534f0468cbaf97d70ae3899e6ba129e a9913dc27d3b96cd9a759752e489c4996a4ea40eba64a1775efd330ea95da24e d3d7d4eb171cba2779514da05d90209eaa1727b52c9b8650cc8b8db57e46897f 0a68b58c79e3ad6391d53d67b471138c26f00bf529327ac2839c755fd96d0658 ebc9495273b4931be7077b276e051fe7e8250ed29d9aeef31373a4b03641c289 3b8badd51c7045dfd57bab5fb9d8db13a0eb7020c290ad1ff47d5370cccbb068 b2f09dba2068bc955d463f076db8b6221e3b7de6275a6eb4ef9a23d82a181544 6e4361b0f7c4f1fbcf76201eca5b30a92a8f722d5cad9a57beea0403b0928df8 3a4a6ae4a131314c10c7384c2a58785a6325f4a00c4706fceeb6a4d6ab30ec6a afad36e990e6e041a6db4f53f880b268ccd72b72261a0c9c048567e1c1e958b0 caa48ebce5899e2da0c7814b0e696b035be952553d062f8829def5bc771e45da 89406d314f4e39c4a033d6a4e4c0037edb7a3a31b37c98eec6c8d720f2c48f01 0b035741c3fe08c2dae982809d9ed7485e734d5547faa8403c1b77a9da609ad4 1a901bc127c3b84152875736adbe0ed69167fae31e181406b1622f6e3bf71df6 a7c09e32c7ca28087797e11804527fc83232aadd2ac4e4c23186f02e99efe7d5 4ef11891656f600a39bcc5f7f7af21e3bb26e453fc2af030a2724a50776c4791 6032204ae2b379496d83b9d2240c7d063d6259ed8f2433747cd105ded18db7b0 a63b0de0182cc0b97de44031c55d6e5c2337d9f6a0c2992c82e3c1aaf10edd22 a790c12d426ff35c0a37180c25ad5b2a929f3c268c5c91b20bd5ee2a202d72e0 95c56ede8f9227c93d02146ed6e57eec055b62af9c43ad2bc5fc81651191b06b 56fcaa83985e5f120c841b50e7f8919c76578e21d247e7badcf03fb8d1eac4ee dec07c7dc8c89bc941c8223d62d94043acdff9b2517602119b0ae9a3a252cb45 c584631705ab126f070fec1dd3e8a48b69b12612d24888901c737231ef93356c 4863cbb3e8cdc1faeea1abad685eaea9fa63284b4328c59ccd1171c115d7f781 729569ad10a1dbc8e54fe0ec19979991b4eb9eda9cf88b2084f0e2ae270e9cbf b66d3ecd02a26407dddc554c689245c2675c055f0d9bf011095dfe5ae82d7554 2a24417557d9aea24b7cbf499f64cf0d68cb5c624a32dd8c5e43b92f9e83d256 e09ab5d4c07215051bdb16b97d2076e10e8d160a7b966bebfd2e0bd7d02ab6f3 9ae1dca3d2405a2c2b1673eea8e08c4fe9b01059bff85c8f4d6daa4c10b59e74 69d2ab5cf7662a005d70270be30dbdd3523e56108712feedd8c1acaf9183edda a8035f625337c31b0ae545c63b7993a65dccc55e4bc17094f44fe45c2ad04867 0ead6f438804330f41a356b1cea31e696b837f1cc839772d2aec226aa942b6b8 3e8aff5dd480a26cb119974e59effc52b1371c8662ebf794d8254b0e59d68d0ea58a3d54b8ce0191b323fcf9e8cceb8df6654f39f8e4298e73f34e9d61e707016c630201aa7c9ba4eca5f6904e9439b176a4e427102bcb0c1333e6cce0878f09ab0ae8c8fdbe6a05ec06ebf08de574c137e682c720fddc589965ca870885e00aed41c15f6425b865a9720b7e5725603c5d11b58b8c64bc14f4055feb41b17f0d3c8b403bd173b9336a17c34fce651438dcbf99e9ada19e34474b660bf7430307e43c5a15c74a1bb23f57df5e2b47747be90425bb064022775ab2cdb776ab3b0d2684ad48988042cab2e61a0c83384f01074a9bcef17ed5fa42fafcb4b9ff5f0e6e6c8af17ef9d8a1f298cfde46286e447564390b3bfdd00b44d5506519a7c90f3bbfbcb30ec7dafdf602e4362b3d01a594b6b2a973cd7595757cd5c73c2ddf0fd0bc2bdc25137b3eca00f3195b96bfb3a189332bd351494f0ef898b49d732608fbaa0e4adf4839f1f8934309604fe1ab7ab2e06bc8960612481866e17744cc0ff5dacac7a3cbe5bee6a769b6bc2674d66e14f3850ef5d06beaa5da51abd5a207e14ade04d82355b0ac3a6f7dbdb0ffb13f96ae741aa6129dd5499c578ea8720296da2148776454961afad0e6e3c2152e2e83a9baf5d5bd276e96f2f4bd82bb0e979381a350f4d49c35bd03fc663123e95cb3cf025c2cdba40d3b8969f759a609767b6b59305197f1fb1f76ef6a58406813a65e97b041b0b3ef228f7029270406fee285ff821dc05735c59a152580da6e752a90ff626c5e197bfabeba031c600bc8972246e6ab8d6d6c5db515446594c853c3af94297fc0e72807e3bb4d51f70191ba9a77108c16c13933aaad9415c8737cb89fc82c4540677d80c8d6ba7c4602f75cdd3b930194c0f8c70f0db1030df27a56cb034117c07e784560e31daf0b01f9ac723a2512b290dd64133c3ff98c862cf540d98cd49950aef928c292a19d0e717835469b640d8463a534232c018b2387ee640ba40eab7e8d60f6db4e8973094035bf1e5bdffd4b67d34ff6fc1af5a097cb455cf69f68ddaa5e09e5fe0ad703ef98640e24e2d5a56e34d9d9621819ec932c752191a8cffb83588bd2069bb40199b00f2008abde98daf8f0d9aeea1b09941fbd79baf2881ac3a643706088520b93edd0b446be3d786ece272174c11fdeeeb7846fef6af732740dc69c4976770c2787542a1c4f58ee4b5e44fc527de1d46d52ae901ae430653ce6485b0b224f0124b6671706e8b73b159c817f8756e9d33d602ecd02a42ec79b95d22ee8346c01dc3dab98f886fef501c2de10be5e42a0d9a982a35cec6e4850d96ba0f651210ddc0899a58b138f0e934580ae548d1da6485d1f353fe3cb7d39ecf9cbf03aef0c1f1f1ec6655917a3134dd3abf9306e224d5a2b10572b7c9ffa1efff280b81b0ec9793c6b0c75fc13c50d0cea6df4a5d690e11ad7df52fb4f5ccbc59294072c0fce76dc836ef97ca4a227ec38d50bbd35a9e1fcbe8e1ea6abdca264f385f1000b653e7b9adfddf1c276c7566cf33745a9092a2455c83ca8cdc6b7104922e3ae009f375ce25320c6f9b9b2a51fc6afb333458ec73b9c91200b1afb191eb4f26105bee658bfc3bd52749343cc6b0f723b69a9f49dbaf7dee590d81d778558caca04d418882fb5cfd7bbe7b18d76ada517f501d2b202f2683f6e5fa733ab772e990121e79ddcbbcf31ff5a63f135445a762ab9034910aab0f00f1f54c507e5f1d80219e80d5f612e673a7b6d7557bb21efee4da9aa1f1e5f85b81c568c0b9d3dae03c67f8392ee68bdeb44726d5bc12f771b084803f0e10cb360c4778b5ab443760af4f994d1b53be9e6ed24a1d2a2cc9591d08e597e01462db547d05a3370eedb072741f903d7b61d92d3da4970c06a0b2dcb7a9bfd5d7865ce533319cdc996290d28df818007143a84c562e8b36697f8bfacd8c97bf2c61df8ffbd79cb5d1b4609a1f79c9479dcd7f9e7b568521885a71536c5242622be704c4dafe7e1f25ae90afc742ecc571eff8d824a79c4ec179e908c63b29e40980dc21d66339380c7d4024fc2170af50b55ed50c825cf8e7337091671fa00d1336a683a3609981407df030c4f36363e296de0ebac3c44c3961f38278786f61ec70c712b9d556f6d44a8055ce1ba3841cfbaddadfcb2d058d22ebd8e32b3e7d244f3c74b01868d57b06708d2e38845b5b5919f31401eea46fd27fc07ee4b6e6993f59f8e225827a92888061621d17695a81f528c95c56da54b9ac3e50cf9d6b7105c9faec6c0da35a7440126b158b87605774bceb63711114e1f2993944532ec814e3dea34ecb9fc6d4308811cb34c6155ab3d1f3871d6ea9cb710a96b076aabe2937c203d25d9818dfe0d3f99f0c9d56de8d64adadc9409a461cf14e1953f9e3bb6e14a60fdb9c9553b00c92d04aa42cc3c20ed7d0192b048d4c9dae12160a115e575c970a0df2528ca0204ef845d31d855536948df70ecd5b8af94253babcae937b8a68c96b2ac9edc030372065c616c569289c6192785d290a636fc24ca5b2fd7949f62222ec835210a4eae2ed1659c27070e33de7b4b9c9afdeb644c1a649331cd9700bf05148e07024b5d3e411e9ea31fa6fc5be8c5e46a5766a3a70d2faa001beff6e4d4363ac408899100cec08c72b89b1c6b099a76d94467a3f1e4d4dc70e2c9a3fd9a5d50f60b9e60a213da14656ad664f594469eb46e2c528fa7b949574202eb8939b98e0c0235878f5ffdc6a188465a3c9ff5f78ab72ab023f73dfaaf8935f336cf15b1e80f4ddf316b928ac1db28c9d6b0e51b66b0fb5e8651d0a242e15ecedca20c00a90474219678b406b73544caa0d46ffe1ed5351c14a653bb6d2c254479c47f03460d130ce01264adcf7e0638a8fa574c570df875ab813bca9597a47626a2277ee807ead3c9f40a3e28d4ae7d5f11552492fb7a72ba754710c429b83bc1d52725d50e92f2355c6ec7b4abc9bdacbbfa89ec14b9da0835f8296196e1af7200e330df02ca2d5f0a091117e4b104159033e903733f169b6bb62e13686beb0a609f1716055193f87f4e781b917414abc4300b8ae54b14ce9cb4341f664c7aa08fdbe0b603ba74a91e0467efc521c7e40dd6dc2478ab446d9b698e34b648a28f446294f10b82c88c89afcb8e3204152779923a32f1aa380bf397ade3ba2eeed0c5cf285805694e825320e1383761815b836808ed83074c901f958dc873a9b61fceb0ccb20994b4806b2e0c19b98d954d221c4304858e7614bfa190140b07d8071bafd90f021520d78d575cade15e4daac8063dd56464c25ddc6e4a515d32286b55c5150a082a348099fde90e49218f235cc3b205e107f9f1b1ea4181c330e8298a17c5ba09f843edb49aeb069ab8f80b153e77943f7cbf154ac963bc4467171114158d910f0e01d1a3e83ebab08ab38c1e35e118a9dc15c72a12b1ce0da53695d5e2be7501ff046e5f053a5fb1fb4ef5a02a17d9ca30a6e04e3247841f65cb15026e25cc0622a62823946ec39cf05804f25465cb6eca4501a3bab553994d4e98865e72370174c7efcf080fe194af15e5be9c2bfef42cee3e3baca6c5518d37fb73f9dbc207749a1d28ea1ccbc5bc27c74cb5da342b16f630404d61abbb0c7f3b8ccae80704502822e79455d22044706d820d890d39f074c1980d8ec3ed7733c19e70f5a2077a241ee5405dfdfab3d2bf3f4145f10dea528c59a4bc99f255f2912b2b752a09c791b17f961334be63972f7901fda1bc87914eda9a0491c9247aa85e1fa08d0104ff4f2cf779f03ccbdfd26f0db3f9cbd4db6a33d9586c4fb242feae9dfcad091172bd6a68f13c06a8adda4c587e4bc3e301a3abfc4e860053e420a04061ce0495a7b31a2e873a07a2344e60cfd5f345dc999710320447726ed8a662230e410af300c4deebe75ed72fe2b4199b5f19bace243be4c98359388e92ff24dca6250048ba820039924f167af1faebe9861f8acd802c27f26b392ecb552db471496d0e40af5d21617f449cd15f0059ff35a91e077cfbb0472bf184fb1b234c5f8e250ce0e19d556aac61b002737a38708567fbddefd92f75369bf83f560f2a9ace1b02937a64c9cfc6d959f9bfbc8d762479400439531375221ca11bf656ce5cb75e048998b8e1453ebd17da33121d7e977e95d85761da351bf1fb6a7afa459d26d306599ffd960251bdec82ba3f5779f7f2220c5d6d7d870a350de4a008cc594dcf0506d4a84e1fc96c7afe5be3bbf023b29049f630b82d8e315399f2da886713200cf74d84fba08416ff96c73e9832a670019be2d299652c3e6b05cef1aad30a490f6e5e18adb3ffcf5b6b526f1b4e9e000d9f1177e782fde3ae932a60fcb368a40f4746d4457d2602263bd7223a9d20d6c0a290e4505eb664c1c1bcaf4d8227b70033f2f61d7e524957ce41ad8caafcc26ad50babf32ddcab58e6700d280bf7ff0ae8c15d7191351d3231fb0681cb6074f2ceb83c797d4abf7c46ec6a95a230b60f568b5012e4f74e4e43c6b1ae8aa3434dfbdb2896887d3965e0299dc2646a9003cbe696c864d501940114c0541eaf8aadcc2105b0c83f7f813989a1c42dea4101e951dd8a1b34a74f36c99182fd73b53fb49fb6715df08ec7bad8b01ecf85610038fd97a4a14b1cfb86a9886a5c813235c85fedb7bb230c89c01b1ca2455fc404fbad8320547e92904d7fcc8f5b94bc1d6e67399e036fc3c1c62bf3316f3ee70411cafd932b8d33db27d8a5ba646ec49a833bd5e7d516672653415711f04d5408d92ac6598df330883bdf60ecd34948d4f3621d641dfe05ea996b7fd0b6ec2c01f975554ada5b10cd2a908a22b54d1f7b96830c9dbd966a1aea9b2328545d06044c7485910e41f2c3ba3e3332c09ee11661ef588a3b69c3509faa9ac74bf58c0254b6e45acaa1d3f7a60450d4b478d9a5b6b397a6aa871d6c8662f16117062c0a879a483c125aa55760f4468a9353929e20b316b8bade35ebadc87f570e515502e451f49599685cc35695d303468d91b4300e7bbe448d63b7521583b4b0fc4b0875076a515de0166aa71f81dc0daab54dc15494da34bfcd32d7a573627be0f904852e4f688860bd28d2df05573e5dfe3facc825d140c18e13f171fc93f22e3f031304b213081eebd6f11552f42e6e547bfa02324cf49217c7cdaa2b611ce2df0e633aaf3fc16e5546b9080ef911873d3f97027dca6c7c5a6b9a77632b06c2d90343a480105fea008bf8a692d934e3e6d78bbf78059925116f100ebefa1fe5cf096240241433be9df49d30d17fb1dbe1057160dddffe6f6a80381ed860037bc0093c5cb5dc019e26902aa772a51ccbfc2def6070bc603b0a6c8fe56a3b32b1e00cc403650aff56f6717eb32a71e7ffd5df8b7885fbe97fc76fc0007815c791f60f238ac6eba156a36c93c4d654233f8f79352332bb16e625d2f6f447665a664a088316be138c0212a94483307f299ce009bb4370e8af4afe3f082ca31eb343d10ebdc41d4ee39eaad97e2734f5f71ecd24629e2e14be124bf7cf7d681e9d43db04a7520cf3be0ee401c5ad54fec765bf427e84435babb47106699d241e95bcf70edf94a9ae9fefef53c0db551581806af541273f6c7a48cbdc7c7a0a9a0ba10003922acc374dd81efc43aa52ffd03147291d628b41de19be288dc89bdf81e24709b08f3df8fd6d902b4d3acc939ea0baf404960d2af382674e155149529b2bc005be95b0cce14f266fd969f442b9fe8e71f472bb59d9078d24885ea27fc0aa9906a2dcb0862b1dd91dea758c78275d5401c76de772857ae683c0a176eed72ef90eafed773cfef6669e0c4de6b30ed3bbf232190a760c92b887479be48c43dcf90ddce0d7ef98e9034844c40537e388e9b36b3bca012ff27599957af9310544b40663e09bc0e1f0eb4cc6ddaf0aa3c1a447d103804a524fdcf11aedba8c78aafb00cbf4772ca740c9921bfc74658abcace495b7a9994a2eee7054f7d9ab0adfcf0a6e688ecee32c23308521a7732eeea3cf04065e57c8a40041d481428bebd2d10685d16cade8c0ba36fc07d73629a988547df630a0bb713b5f0c73ee9d2e4d5a0f3238491a06955a5591b579ca2883991ce6388dfb246e18602fa48f33b2d607049e7e1177e5d0ef802028807cd232730404f92b686fb7e37b2a5b2d7f1bdb16070b315301ec50fb28a1e8397188d66416564393227a668b9ffd60427840ba710038eb56f2b06e7f715cf8f36050c5cc65b346665d3d7644e860cff8228d226102de8e29c835a3ee69a8f1372d104f936182a3e408c379e9455419729891693305fdab762a6c1f146d33a6e3ab705f539d021e48012568e93fa2e208ee6ca0e90c951ec458c1943e578101a878565f2c6f1a058469f5a037a5c645321a4ce6bb00fecb67a73bf1dd2051239b89abfe6a2859b1cc675ed03093f6b68d426b47a704df9feb181a6e9052249ca73bfe9952f5752012f7de78902f94fd891eb927b904b20b99663cae004136452d077ddc84dc0d154a4a10dd317985bd3865ea892e020175766585abf32cd90ccdbebd2a1a96831a4fab406be816bf92ff47d19b540a1e0e545704838da4c2b70a96fe82b871c6090c780df1deb4be8f531272565d0442c1189b5882d7728053ab9b7cc9625a0463579d9b2126673821fa8000e2df0cf09ae98a8b7e73db9497bb748f6bd00c84f1bd49debc13b2964aa0ed18881d005cca6441d498c1393a65bd41eede64b81e10613a3f57fa4236e0504d381fc700352ae282cf8ca6be8087962d0e92d31ff202b99649fcbb55cf24508c88500b07f7d8bfc2722d4b092d42ce542269024a48b6f2443ab53027f23db886b3a78207cf4bf1a02427139990eadf285d6824af5c969a8ca0a1030ebc3900985090b50fe49835501e124b167d2de52a0d752798267b6701169fcaf8e35d49ebc3acbd0ace8c0a040fed1310477542b135ddc60831b3879f5633dd4262fa3c1f0e74a5064697c8845d932d581f522063a4aed1b6ee9193f1f6b4331a13e42bcfa40b6d01f3a4ba852ef5faf67676d3c3b88204bfa381ceef57a1df6e9d363b9ca27ba30b9def8886e344d4db869071ee316eeb12f3d0be4e395335e934db828ff5d4e30181b0f85cd4b87fb68468bbc1528b4cf85a50cb0c9384a12cd5c2f367d3441a071f3a16134f9d50208e1415371af4affc08f0a009d1365849ca8380c437f2b50608af7965a489c02d97a76b94b1ff6e113f950d9f488ee911acae39a3882f820a35b1dfdd339e7270e97fe72653c3e67aa719545287a23aae017ad7c85307bc0fbf6a93a32dd666cd78ebed93f5b710ebb6f56457941fa1581a79bbfb0cb17a03a2634864a973135920d564c45bd478253075987b73543bc9c10f7874049b540117bd36a744caaed115e9c7fd4b7a9fc9194ce8d25158d0f2761635ed887ce708ac887c8d213614034c2c03efb61aa3006b26147fbec16b757df21a3bbb7a450c229c9b64b4a77632b6cf80301cefcc67f8636629cbd63fa1f4f29c0bfc6b2a0979c3462e8b4d6e97ade5fd9a3eac5c7f1ab01974091ac355f0cf668b9f154000637789e1154d502e002e64600b4f6125210fd47f04b1c308aeabae17fdcc5b06e870494b6f443b65bf6b5f998c27aaf7fa25174337d093f62ef6d552048c300a169f7c9dc67d0b70dbc7825119b6168a7abc5c1f6a9327d688cbd3511a58580e57da0e2dd35d5947b2e04daedb6660e56a18cc90b5a54ece73f3a70c84517503d7ca94060f87c2a5036a2bfad7ce798971a192310c1c040420a344c454f91d022c80f2e4674b02f2b4c50232a4b9d8f9e8cabd392c78e3ebd511784db522a30d6791f989aa91bf7fa81d9c5b7d331482fcd8a5da83440478bd733b32dbf6f704cc8508e0697f761b004f74873f089d8d9c4b2d2d82074e44b94037ffe1c50600a5cdd79ca124b497ec21334f7b40f09cb85f78b14dbc4f20542e4b5aaab54d0261367bf71a1994a9c25e97c391d30a9c0c516efff8b1a1140d524651dadfd40d1afad05ef69fe08f4bd81f3e41601aa92a673e8f54bc05f74b86f5fcd625aa07c85edc7fd7297a0b6ccbf1d596eb5ab1fd9ca5a4fedb6a8445a2b1e35886b5011c5dbe5e0842dcd7cdcd6fdd7f599448655b96e72c8c42fb65a8102a394abe06bbdaef2fa83e6f963ae908645172cad7ebdf2c00d84974b632e64f2074c958043376bcd0f8101902f1f2bfcc0b7595535113485cd46a264a9084e2390151b400630cb2185669c3fe265d58ef907aede0ba04b92c4eef136566e225fccafe4f0caef071a893a3896d031b96bc5be1fbfaa72284061cab07b479b48eecaea70502df90631cbb095211622ef6c51b5a88d8710ea1d23e00a25918baefc6c6c4ba03164645c4fdf4550e73895a894830141dfe372360bc5d38c904626b384eda7a0494277fab6cfb74a755bbc131a2015d68e8305aa3910788ccb098a45c4e0020010fbb907607fe72d27dab14e08d6df1277faecad5fe111ce4856e2b2b86324009414bcfdc3f7c6f2e7650ba745009ffeb31dd0c77654e7447a19b63356f86a40e4dc8174c57859378838629f55bbcec46b5299b7b9fad556b7a2114d15d64dd0fd1b56fbeb8bc1383704a905bf678888a46929af3af5eef71488d3dad673a8b0cc043f3c2b484fc2c142a816ab6d39889ea76d5e00f9519891af744513a4e7d0781a9a4ba0bce0823becdc43dad2d594a978fa8a43c66502816045c6ac4ccb2004783b64782268517801eb643cd00fe66d941bfc833cb68fc557221a1fbdb8001613d23d075c13e9ddfa514f1b9d9b55f432c4597ab6b5547053d1e56e6719a0d701ad2e83a1df55a06885bcadec4ba221f1eaf5c179af20c296a0e5089889b047d2e7e74abbc5c7949a387de4ae805af2e783e8f03439331197068d5c53ace059ae22cad5407ce288903bd70f7f72d43a4f8aeca667436777ef386df95553103e12b88a1296391e2a23b3661c3947235122efd4f864109fd7513ff0e8f26e4088d5021e13c69b4596e261dd40ac2c51c19795fe5ae9d30f2f8cf6da46c3afd0e3c9bb4ac4f3fb81bb08de117ffe1e19b0fb70326a0038d027d568a82bda42b080373a504d962ff06eef3d419f53d1c1be7f9c4e153345d159e076f9cdddc7b0e3a94a34c3d6b2b5982b5213b3669eb6ee7444fb715454a0d701025937041310f91959deca88cb4c773c42aa55775eaac54d21f8836973928770590a0df691d051960dcee6122f75c5cb92b1c82ab3565dda46ac1d8bf0342d0fd43fbd104a00d0a387ce0230604c80e24739f898ecd60156ac7b68f15dc4a5804bf65c1bc910d61d2e92ac03d5a8346f5e9c11d135c5f55fada8fff7b249f95312fc2f7687909f4ae8a7440c754edf8ef42f434d472a2a9407135d0ba53dee523941a1f30b6015a970db554412958066e44e3c3fac897e1ad165f4e12f9516944efe4f63b190df248c0eccadd7e44bf21ead90c28e572f75ab479a4ba4e96de6d2a7e66ef220a2c15a4f3f9fbd7295c97c3544961bb7c073522d1a16af79849f4b137527fc206d876270e7f7f5bd4ce4c0ab5d8ab444b2decd325ad828c050711e8832658fc097e31c6b7f153c4d99aa89e13d69147847caaf2524bcd145a7a732b10b349f309060fc0d1f1aacfa51d65ca8346ea7563f9711dd12af228126605b25e70069d0d33ec170e150a896c8e4453413ce8e9ed52c233782f50c09695c444ec7de7250272906166ae564b6089606b37eec8368aa5c7bb54c2acb7cc4b7eb836e255d00d8b8af0fffead97586cde31bdc0f71c1655ac84c2b463881dd57e2f0a66d15409059533a40229aa98132e4eeeb8c04a7992a2dab9421ed306cef88a2dd885310f01769bbd3081f9a1c801f533a17a2c56ffa9fb72e4ff8ffe67a05889fe6a860395aed1a33640ae788519834b72a0a8d3d0d81af786e1d69015ccdb417eeae4011a14485576072dbdd4bb56e4350f30d647c9e5462a1ae167006b3d42fd4e46065b39eb74eda16ec99617c3c80e41abae04835007e2f80f4cf8d5eb0cf01aba062c885a957756fa051c98bdadcba593cd6a2d04bb6616fe44daf6c38314f438073518005f67dfa5f4a45fc3cf5c23dd68df10b3e4f60b1c67e274a9593733fd0778a0d66886bb1caf1a857de081b008a10d83a9cfab9f162c0fe79dbb008f65051d79c8439414dac71dcf42e33db18e4b084f37440ea3edee70c121040e61460469f590686dc57acd556c51bbcc77adbe86f7344e64bae0e7c63ff6addee307012044ebcbda35ea091b794c72157739f61b6e416202178af32f0ec0425e6d8d0290b8439a31178fdb8f4bad4b5d0b720d54503595e2642cdafcd1d6550c20840063d78d27fc7532016f63b98d13ed41e79232b52763be5c62616e61f4e559c6010e5d672b12490d0a670e458da6ab361a8bfa91bab7418480ee6770f9c87db50d594075059a5949fca0eb60353db5f781a41dc808dbc7837e7ae301ba6944f101ae38998d8f868aa981c16f61aa12175a923077e95c215d7f461d9866084d530d0e5beefdb4c00fbc20c8c7ef5fe27a56132d7d80b01e27682b27141b0b932800866014a15d22dc15ca4a66d2046dde79ec7c19d49a3d7388744e9d2a1a2f92000cca631ec240ec1110e84ae51308bb783af546477787fe85ed17f02830e7e609909cb29ecc54db42372e5577c3770ed0d53308793d04e026d4fe3dea208ea2054df328e6302a2d9c797de514921fafe89d0eddf7b701165a2cc619adfab90c0be7c37a85542687e06ce46038d93a22608275a92b9aa666389d6ea20f9676ec0279528f561f8fc484d643920ddde380b6f740187e9cc5c9af0b4fac4f72ceb507ef868fb0299f4ac021dd871a570cd6da908d6d8486b5f9d0ce46e6ea92ff3604012bda35b017d56cf3e9d90aeff705804483a88609328e635da5e79359dbf3037b250ae44e9e032ca2ab4d570ce0d925eee965d296621f2448ab8cbe458158074c34e2b9c3729850cc23d0c9c9427c26977a5e5f82d49a3a119bc1369569d7043274aaea977474f3328d4952da34e83ba77023ed9a4570967766ad1cc10c2b0fdbe0b001c45cb5ce04a3f888b96bedb01e95a91c84263347be3248e86c602f0108b03d7b156ac48eda53be9d638a1acc2d041588d1e7099a858ccbfb0d55560ae1c9f2937af02ac443e8ac5721135fc6a55a75dd5f8f69b46d61896a5a075c00e9ab678ab1e92281095edccdfdf897025d1b7d8515e4c4a113ea0162839c010ed74f4ef18512785855ed63fc4577fe8e607a9349d759f11b3ec01250ac59660fccbf672babc7341641ff11cd46584cc523274b4d1de9549313e40043044e3b0e7787055d78e06e1235d8c1a42731eed5d958ffb06cd8d6be63760910014dcf0ee34e0855dbadc625b2544bac48f087118ca34f76999236485733640fbb98a808d438ee97a628953eb9b09fadf6e6a43dd4904baf9a71ee8aace207cbf195690987e2a72fe753c09b1fa81403212da11ec1c95cd12e39fbdc694ca86aaa988807dd3c4fc035ec9ae1fc70e97d1ba05eab9548adb74fff5e63004de418179dcd0b71898376ddc53743106dc3c08c7fdc4ecfadc5a97f01d4bb3249dae797055e06fd8479fe644a7b6a68c39ed623b956fd522312e0e70f0f4cf5fbeafbbb279406a6d7de477300b683be1c2d8a7d1930ce2ad301e750e19dfc1cecf101e95c6f0c73628f886498f1d86ddbb7ef70ab0c02a2a7e1617695606ff13b310dbb82c9081225b79d97ebc5c0ee7a25fd27c20181ef9df87ac8b76cb74e0d6cd911910f08d49606bced1418c1579fb8dd50861c33a75bde91989d1eb38672a54626bc1a0381ab229b3610d6d2f9787b6b381c8b9a2ee39f60b50ad2e80764c6c9233e330a03555576c35b327169746682486f64af72657721160d77c09154408966167b00d2b294129d872acc05da90f587caf2b1d682e4db63795afefa4dba0ac8a0cc0c500aa041b52437f09c3f257bea0bc03d682eacff8d85a8facf59d5833509820c98736f3d214aa9ff1ea88e5089669b806e18d492b292586097bedf5297cd750f3574b808ae7ffad02e2fef32b8693938e830c853f04990cc0ac51a4a91a57a06c1a76fd01533a695ae641748ca0258425cd30ad505a2c85c676c0a82e5e989093e005854e65b5e72a1d100088cabb24ad6dc063267bdd6df84169202fa1b1c0a5ffb7f518f7aee28334cd4e91af97417d8940ec0c60ce91b8e428857b0b4ca041d1aec24461b03287fec207028a614fe64f8389f5850ae22a7e053a9fec5f00849efcc989eaaecc1ea235555a771df947d08cce28079cf5530fa4b59f015740aea99ffe53a98d33667036cc145915960ba313d599737b005df29fded0bcbe10db829eaba53fdae8e351e6d560f74b981d13dc3c38ba06b63b6178646d37d4e0accb5cfbace126513901ec26da9189e1068927b4462c07d1712c0904277bec30e5590228023102d8b2101930984cea38952f34e6058e7072fbfd85afac09b7307daaeb26d7482fbb9c70115ecee3fd73a182baec5854a31ffeffa453428d0810b40d932a5facb186d1385da8c8724a0cc5b14ee574628b6845211453609c7270574c03123ce0e8d77dd6eee6fb4fdc54b18b2b55966a39ca6882896d5c5c5910ecfff7c69a2f18ff3b23b7f4b7de940b26be034aa62809f675ddc1fefecbab10158735936558022b642b1f04a67f606f0c42e4600573c783b7fa28619f6324708e8cc01ccb82afe6cc4b191c5d7938b0a875957e7a91e9b21eb704d50eb97530deb73e039f2e765f3cb89fe0c884648e9ca646071a12003ebae134f830efe720207d5f73274035bcae425d511ca20de2b2aa840ff80d595b5dd17470b5105b70a45cb0e3f40b30fbd56f17d0646085f16de4ec8c277da75dd606cb11884b4a70d8d668bf6883ecd22e6975252ba94e7a8308ad5cac1e64d148db55663a560050a6c010c1896ff94f751154bf900287411eed5d7c9e6a5b29170b2c2b4912a20046695af896a8e512cbc90a32e2d5a91c97309560e8a4678e91a9f9bc180713b01b82e0f91624bc8d8aa6fba8390c329e1c56d06651317e6984994a2f604514a0c7bf519dfa46f5e41331c91671b7c7335f0f75ad62da9e5d7680ec37fa3e9e50d9dc793a760ba6883d242a9fc4a5b29fb9fcb80ec985b92cfc32f528214723602a3b89634b7d06b01e671946070128116959701f7cebac44756a1f0047a480b06105cf2d4ed62a82c2094e42c56d1a844e586c726a69277a1f138fc151c09d90118a92bb56a00234d8407580f35d3eb67a67b6dbe1c05b33de579c7a0eaf8700ac00fea9eaa4789da147fb72e48035d5bf21152d654150509963b721a4b328709f8240d8b173b902cdf4b506db58398b669f2d5ff30ca3a1d7a15c6901c68290a7ec65aa6949c8d1485b02f9769f883caec5e7e6b1cd9cd2fb768bfb4cabbcb04d2eb1da0ea60e7da36f09fa4fd032f7acb63f82cc5101527c58555a39051fa0d7e8607c5e2fb4f6717f98d8ad80e9927ce1cd7d03a177f9abb0a41fbf031b506a82f3364d51f72a583c4181002f852904a2dd406e97634ed85a0c3c03773db03a3ad77dafcfa7f536efff5e7785270d28acaae0d22da2ea43913f31bf9a2e10e291a90326403e1682fbdaaf23555fc68258d2cf22ec0fb32e76d6bbd5fff7f0fc91dd8155e3094bc28042b0602cf77da0fdd792497fa6f9e8b423e2b221e210d00266f99684f3aa0ae6c0bfa92ff7e5d94cfb823c2f3b894d8e6355a65fc7f0425f72b07380858e00e53fdf211426737c8cbf0c5404c1c09bb09155be75a4d04998d723ad0462a8accc407b8c23d7788f79e29d0c5a91fd15d21fbb90d4c46095178025cc93152e98f1c800c039d8e438462d9d92561c04dfc7b77a59544f50fe62be4104a2bea9028e382b3e692917637225f935370cc3968ef16c78df7890f3854d5ce906ff7103bbe50452c4a878fab52cc1aa6811f959467fe5c9a5a110695dfdb6bb30e642bb56477be370b15f6c53404f6a29b39e596161a03de60200ab41b3d7ca9ca7479d0d16bb7b837c8a54730719e3668e2dcf31d083a3331af0703b896a2262ea2a1b592cd6dba302659879bc315d4632852e5164be3f141dc06c290f1add724f548c26e0fa7923eec80e2b9e60c8b690fb493dd3104e991c503e2779980d54bbdfd2c1b2054c602bb91fc2d2b37999034fa6579e6e06a838f0e967f9a71b85c49d86a7715752906294ca6b4c5cee636b41a6231826d70bedb03d1e49f4943b1e821a85cedbb9fc1970f2a604271f89298bf50bdf4011335b30841e43bc9fe0a83afcb062a4a4b4df60138a90318fec2ec25f1123a19381303099d3d5e86f2bfed546ef4f52b236bd66e2def875f2de3677b7dbd413cdb2bf4083c6c9d19d20a435f97c5fbb3f701f5f17b7a364690f064758c463bc87e6ec40a3788482436ea50579506287b06068a004c5138cd1fd2ca3355c82623eef84e0830ee76be0f5f4ef30ab44e4333069d5a55181742d91f3f6613c5536a75e32108fa4150a2f6dd0893b2df731d9949989754b00939e84b2abf6ec3285ff161f201f6a456f88ffa238a3c689be7686712024f9ed93d4586bd099d0cfc99476af3067b643eac39f603730530f238e25f7ce63230b9e7b28b1bcc8189fd3eba357303ecaf561be1942974bbd20944563772157e99f4a63bed27d82c3343577185250b95fed1e0bebd4a0f76f71d238b73a238561226524f0ee095f7633274cb18d4023740641dd033069abb2e85ec275b41aa800f8ed5605f7b26d0f3f27bd18a030593c508baf7f3291692ff38e0064573ad48412e4dcde3ecdb9098259c2e66bc014cd07b7665195f0cc778a57647adb55f54bf5d2bea89605a51b1917f0ac38d014e5e8f0355f7f9e07723d846390523bfec9c9eb1653825490767785e1b08930c1f08b5d7fc210dedd62ec1e276265929fc044bd718ac1502211b0fb4c55bc604e11b709dd71838e4abd8802b0b439d5ae300a94c88446ac693a83cf5f373c5032ee86dfde4a387d2fbfe7afea6dae57d5569f8c5c764e81e498ea8d307b0ae08066468593c39d96fb49a4c049ba472091d23c4ea24c763dcb076515d55f2940ff3a888b049570d7f7f6bc729d63836d50ac32adb2dc6165fd4c4950a95c5cd04de6d58c11d14166cc0669f12d48d5966b544fc9ec8ee91e349aea0d50559fa044aa67f5aa253d2cb2a6d15f90c8218ba050570fdb60269a8c2a27928a08d8a03df490136f716eee487feb016ebf6d2b22ada5b82cdd39d3bafab00762a33b60ecc24628e741be486106770421359583e4d2e88be183004516f00d40e2843e10c9e466b060f46ac43a73abb1d8fa57541b709ba03e325ce8f3b69880698f652055f44d058844dc01948ad6d8bf987fdfec63ae6c531856723ccbf0b3762d6730dbc7b5dc1ebfc1e226338d20380b7bd802c2785e1098c4e611959b35abe61c9098edc699d0a0101c9065501d117400037c354b9ea766a7fdf859fcff940c5b400d56038d0ef6bbe505b30bc9588ed121b30d14404ec8e1c33934c228c102d07005547877ff4e6cc50ec68de496306fd5974113142139514b8cd567c5b882505000995f1ae4170984a5945bc7237b38e384baecb5b18cdcf14cc029d01aeba2806419047f7226cfc5d056d1921ef2980464b761d0895cae7b0163b63fa37a1890c05098701f2bbe6de1c4395bf946ed810c41d0141301a5056c9a17acd2917f5045c6091821fb250f1ee93a933a45591a5dd08b80f6d39ce681ce5049f232f0d0c05fa75a4ffc39098ad7f2e179a4b34733c4d33d3e1c1ee26b4dbbda50b35d20fab86936e46b48ef795d07a18fcc3690f7211ca573b30385765d1272e6f7c81006e081905a44fca251261d767ee5d579eb54e7159f6f1a25f23cad43f3d14900e78f18f9bf056efca7a929131f74ae0fba96c993b9738983034a1ea44dd2d9901fff16e8195142bc1a64bd526557ea3553617ef60116be9b9c28bb6227edc6f082e8c9f613e5c566ba5dfe715c5606517e63ea5a4eb258c4e5bed10c07207bd0dd07a0269d12ac516ceb6b7bb55aa737fd413db60753cdfd4916b9fef571efb0d11483744dfd2130b201cf407e5b24e3feb97a361266a3614a0fd7ad5e5ef5906ecc6fa3e2d006b80da4dac7a6890aa1dbe9eac196deb1555a18aff31359a1106247b740b10b53595eb064d8a805b74f129e7c7456cfb96da987b4cfff8c5fa082d368edd620cb2090f196925425070bc5e166a62171b7bd1378640457a771c0a6afec642a582cf076d301105eb30702d53c6df6948165bfa882852ebe480370230467c2350f447354807245854abc465697f82f13c4d70686cbddd261fd439016a93392fb5163750069c9d8f4f363c2b511bb7c557719966a3fcbf30c94dae098acdc2cd78834810bac62812c5c1c2b7ca55356cfc73b3ac309320918e3f690e65b7ef804036948a42a31bda5fdbb70594aaa4e3970da3db843450123f9f8e0ddd507e677f259098d5f7c571be566ce23f7132ae9a92d4ad1dcd32afccdfc70904e31e4a5423f1f448d084a985014083d8144fdad7c42746bef0e2fe82809904179904139d5fc27ea0ccbd3602aa66eeb773668a5e9e932340f69f290877ec0014702645781318867010b3636b95be1f558ee2d80be021edfc39d1383ecfe404ff85955f6a76fd57f5e85818b876722851549237c0308f172968589d2714ec07198df3a14d03559c3d22f9c8a455ad9df172c0094c501bbc627be7acf9d0f807a1bf4729e29c5afd3635ee1feca87f15face60043406c3dbad71e1f5d13a6208a7814dde82b4f68bdff857db2139dd587a96f407a94e429434b4db29417bf9056ce3f5936615da3853577118cb72c66051be07b3d933d044711c88990f4b990e198ae67debcf778a8cb5b3bfebc65d77d0fb7a000f50b057dc3a664b5fed8b08cd56481da067627f8ce1269a7a61b4f0ad8ecd0f54cb7297363e5ebd55285e06bdff1dcb0f2a0cadb232e37932cba87f508ff5ce49267af6d661a1d2843bce03fbeae2821153a89b4c449367a21aa56c2dce4a3778ec53f74d9a8830d5c8d5003d0619f8a63b4fd5ba7e7e23ff22f97c10d9e5715053d269be8f92bee9a5dd0ffb422842b3b74e992df88e466fae3ed16db6ee8fafc714d00edbe546045390029b524e89990bb1bdc156f2cf4a182b7ca077bc0b4e24dda65c7c0d6d754c6c026e445f70e1a319c4581b95dd87a74a8901e5ab654c31f4b8349654e7c6d76b0d4430ffe262357b11827eaee70144d6f661d6f7f79038435a9c7814a3713ea50f7befc1f694f8432a2b01372707b1fe0491567078a7c1bcb323df2ae4ff8cc10f24b1d33576899498dde0ef007067db1dbef52bc4bc4bc734ac68a616fa5bac0b654128ddaccececa2442cd3e0ff3bd5eb7548c1dbdf0ce05c4cd7897226d5e04296dd8c25cb722969fbd0ab9f62b98950a378a0de7db4a4e963abe34ad2dd90004c6c1424f2403a1011aa410b04be200ec256c5149576447e194966496dae507e3cc1bf6c09f6ca45cd55de78b3a9bd25f8fa318fef9b39eabaa7e89b9ecbd0d5fd014a450327da39fa6f5ef8deba8c8258faa49fcc83285cffe2292d8ad640631279a880f5be3f3f310c0c65de8c15ecd3e545c723d917a44f293eb377a83047f0d42fc867ade6cac249e46bbf741dd02b0e7dfc5833aa7827ec8190c31dd0bee3a021c1fe76648ea7e54a5cc7b4e31f49a2755d95f5c97bfedb97b9b112302e93ed5ca25ca5a458b1462ae4aa45b3bfb9aa90937b18cd1342d6cd56a668801b7ed6416cce10a2db8eb384f36225150a82afc47078709da8665e1497957ff0b0261704755b7505f173253cd74b1ecda89bf6f232bbc57ede55a3d9741511c045e568295c0952d0e3a619c94034b536e0865083ec4e4f5fe8eeb6ffa349f7e0bfbcd973eda93abd6b83e4b19393c7f5380790fb640a81ddeffc6658fe2bc100fb1b79c8eba56131eb06e6bf63bf7978b74efdf86879caf25721b6baaba7c5a02106c97f755e36d55791818b4fccb9bd64bc150ea7bbf32deafff336abdd0500e47abcd2721d2ec41fd7e86c8ff8be5daf4b6e89bd8850d5a54534f501ef88c09e5f112b665b99e1a67257724b4c3b8ee3dddfc19b4a2ad92f9fbe00ad3504b031ac964a93ce40014eb7f13d60e7e104f0693779259dff96b2a003683ade43405b78c03f04875c4aa6e76d2c85a60641473b013986d8cf862e9d59cc553e68906a319544ed897c10b302b268f5229ebc00c6ef5dcb7e5af11e1f55504cde4110fd4cc893fe2b8c17feb2093d369c327b2ba707430fd422c6b5b3c4f4d6df680048d83cb56a81fe9e095c63f78cbfcaa3b4a27937c898d23d3daaaf2e974bc130ab97080d8345d392664129adf5c8d046649da46cbc8913ee364b72adc1dcfde0c7e33c4903dead2c34dc7b3178acab16ab24fdf16cf1b35d0f667b2cb1d03f10230ba38227ece3741c587286ab9e5d850dd4a2fc047d8f187be443bb47788110abca3fbb31e7ab4ed8911d7a8236c6706811fd02f2781198a53d4b7c128f3cc069d53e6ac2d887b971ff49bfacfd1c65f4a2b346ae78d3c04cf7e0f08506edf04e50e29dbf36e1ea6ef469a6b7335c53ddcfb4e2009757ca4077ecd25d958910c2827e4c637f83c3043fc2ea0d1001cf767805d382e8260ea9e74b727c1c3190de178d5ece74d660505fb83b45764b32d0b3bc1c126162419f8fc9d91c810760b67e5e20a66944a7f3b5eae8291c2f529d9cfcce41de8a5bade84ada44c741d054a589a2b7511190dc5590913744100972ef983b11fefe7c51e9ed87b30102a0b92687ab256d1f96438d7a348461b29691cfc70f21df4732cb02286b6434ce00cfe5c48b756487ca7b1e30042fdd75cebe596277b24d2c841155b0466120c1c0794d687a0055678e0c41b820fe92133e3ea46f033839fd16cd227784d65bfa30756ca96be4a89add6ab6d7374261713f02b9494435101fc949f286d3cf39b8a023e7d8b300353df8cf0458335c57b9ac8060e4a911725455b14502f6f1709290e2365c51f8e0d5e1899bd407bda1912a93156aacea732555eed351a776e9e1900fc81fcfd00456c981aa0413695ab149638f95dc3e0f9b47a77bb164d88efef00fedab0504ab254346f73bdd010895d407a8c4fe2410364f1ee8191769ff2e9022efcb49d8242dd535834e499492f09d222fbe841a1241147588bea76d962ca0cc1d48d2d1377d12d0886ea4ff91ec71d4acfb0049275f9b430b620f4a0ac5f0d7355d7be24ebc2d83c6f544360c7c25afb0da7d8f97fd582004b9281081bfd0c3a3af1926ceab4ec07d80672b84ee01cc52f78a1f1cf46ca69c4bb8caf5e20080795792e9d8cbdb4af499708d92be1f56e2b7495b0667cb02d655b9d21355f04942b855bf6c9d55c5d0ec67b8ef5bcc62dba5d0484fea332b78f3cf8bfdbbd06ea0f5d4c7ae9c96e402dd3f407f8094918e606c4735020edb923d05fb06be8006df0bc1648f1f78f16b1acc62e472865956fb1757730a08c6394394f7c730b07025d631ea663095c6904f4112e9424abc66e5cee6d96bd4f7e8056da06ac020291b7a767dc572528b786f86152b2f46d3000017441ff594aa7e6e6794635e906cf5b1943e28537b62198a7bde52795d298efcfa85402f8b0f984b44b6a74170387142d576a93169a2347c248b0e33ca715e2a02a0d2663cb71784cda3722b009d8bf5354091f2e2984ccc5c88517c001b522f70af932cfab8540a94912601c097a763ad32ed046e3ddda06a8a829ec104f15529e6bdb0d62368c0fba1728ab05346a12a018dc2b3cee80d52f2dc81acd69dea8eed5cc664fee3b666ecd23d00408df8a59c99dbbd24e80392428205f1041e28be6c98114d40467854515ba650a7b435cd3774b86a859469d8dbbe648c11b394661ef4b1ed9e3860c6184d64009498e406132a2de480049953e350bbba8992f56cae87a2793a95993a166f73f067510afa41ea7ccfdfca9bd066ff0fc05d8c25eea79d3e2e667ce1229141eee022b36400c82aceea5df1f549e55f06be6c52d083b799ea2da940c0c0064f2e20524f2c2f0470e2405622a34957aeae0c2779f94f43295e75f2791cd02152b440dfd88ea5764eeb5adc4047fc12be77babe0d8166518f6786d53969200f4b43b0401b8cf386a6aa6fb78a8965b1a8dbd35ca39a91cb517fb8ac0dd93847c45ac0cdb71cbab57df1a2e5a3211f278cf7cfbb1835d701355b8befb8b5eff5bce1d0a822beeda6ca08676b4b1b578444731dc5d86dbcdda5c4c0b36dab7b87a6e0206208fb6dbc2dd99a9bb7efe8617b351d478b608891845d6d38ccd509445652f0e7bdc503d9e0d21b92eddb72e5c6d0695047cb0b75ecfd374deb3a5b9d6d726058b886dd6a52cbc5213a2684c2ea0fddb7c0f5a34120a7242e72e0a18d3bc490b0218da276abc07839a68782ad2745357e2cb36138d4093e073d8d8058b6e7707048474c0ed3f7df132abb7f963c5e612ff4fe9043ea15fd6a3f29743ed446c0ac3d47c1bb3bfa01c12b93c37f6e9740e429b39c0130f2f0b90d6023efd9d43047ae04eaa5a85e95529edfffdc6d2487599fc29337e0418f3389fdd9e32a6240ba31f67418f068698063de71057043e47a8f8b2a76c71562fb8ac3b705925be06037f57cb29422bacad00ba1ffe7900b1e10e8eee51c0d7dad78d3477ca8a10087f951c268c9e29d92ddc9d1d7051c96bf4839819b1bb0b16836ddbb3a5a5880c0b24c8d92c6fa44e7a5f79477772091ec969970a141d77d9c3aab5899192d00d97855ea936296af32ee98eb3d0a299136c6c00a3a2b567379f23eb87ac56c40aff39e88917f54a6f78e6bba0871073c8a25c5683f39114e39152317c8962e00f3c68a71e1f7644762f7ed67713387133a395795fa16a1e6083010a7d97bcc8083515a2f37f84d9cd9c1dd5700a885511a7710516fac265315c2f625f6d42b907e9ea1904f38bfc58c8aa02d514b0ceb12605b96e11a6970ce36013eb1bd16a04628ff295dd97e0bec2a08126b49f7e4fa8bc9d0717f941d0e88f79d12878970a866ad07f78235657202f744b87735c05c08f3963218e53a86ee30e0c5bd69c058c620d4da205533968f31f876409fb3f1b9659e428c6496a4397558ce6f4ef0a9f9f39edc05b052b13830c160aadd3bdf5e49b4ffef8fcb1489d8ce54a1cae036d63faeb0c8ca6350f2bfa54545f499c5ecc850fd9104a05ecb8c69e08009602cca3b695d7893f03bdfa3f28098c183231cf3b70d85ff450dcbc9f59fc308f03b1d6f50d9e68a1fcd78895de62123305d6ba93ba8dac5e1e163ed3ca807c590211cefc90b149002ed6c1e916584dc07561c826cb39d691cf5840e7ea5a507f058d9c6033863d246907dc684fcec74dccd3e07459468b97c8ab60010a135551025cd6f12852f770b051d82f500ff689307cf6143f4960e63c6d05cd3c4e2d740b87406da564bcfd75911be0282f34a22360b7517ee78057eab8065b167c33360b45def3d7557deb45dbc101aa094ac85a39f1856b916f606d783b302e44c23a0896fd6b0c2cb2a441f45f357702a85ea3de0b6f8e5e936b6a57e4ee6cec695f09eb5b8ee606c920a23a5e2b1cf9c924f37192fb48396ae1f24e4613caee74fc055d3fb966864fa439fb428d4bd90d5f8fd91997169223b23a4464b1bd8d9bcb01a4212b12f2b0e3c5c76490d6918365b97c9897908f51367e358fc2a02617c901849d7bd07454948db62689714ff5b07fc41f278bee3959a7d855e6ae37791104b71877b16d8c8d59b14b4f64f38f7a3c920c33ad7f554a35adb12047e16b0b03e481710f19cbae7f2dd5cbe2649a25143b18b4ecdf512505f87afac9efb708007de64333f58c904545615405690d365ffec1b49b6c6a96c39e9395427f06df0b03c71efb12560df39e8c46cf1717efe9441e44a66c96f6e7cefbf73981966e05c5c48750456e2974f3c37d2a11564573786f627750fc86b2c10f0a13f8901d0f39a93cb4dae5dda88122e0085eaea8e2de489391593a385bfc2e2bd8f1def00d822865072ad918f4a6a42263b93f194e5e0e03e6affa5a5eab97c89bcebfa90bc65d8c1976157a9439f37455b82ad9d5b46736b08250d43f9f286578a5bb4f0120d7c04a8116661663b248a7a2aeb1589e3ae116acd8f5a2eb36b01f0d22990077b595b0108011efb648493a7da8ff62f525bbae844628cb3bf69a1257e00803cd7256cdfbafa9f508e1d219f2a75c03d09072be5d2ea32ae52130a5e3523f057084c5f4f79eca3765564271ac1d89a6e06fbe90eccd6bcf3fbd4695b0c5cc01dfdc4e239025614fe7ed8dc2cf5aab719f4ef8d6cfafba784fb2d3469b8578036d6d0e3b2621041594559feaf35571b7cf961f9a615d238f6f5f697193733d043542bb2795bd1a4e6d8cb393ab0b1903488aac8fb93e75c5770f713a11370507245d1ce32029a861c88e87ddea2268d249b74bdf1514ab9cda5ccb5b42a3ba0e2d66442347ed002040cb28ffbcef7c564d483dc5a783b26666161e5cfcfd8a0887e00fe2fe23ebe5d85c1d45771ff4ba1cb7b86f7542bdc7f8ebfd5ff450060c58937adc321c970470faeb4432cfb35a8a81daae66d2e5c7c17a36db6ef43006 false -check_ring_signature 4ef3e892bc0c8fded8ff0b4fdf9e3e891642f71fe3b4d2cc9dc3f59737c4591a 6a04e15ee5573dca0c840d6a700ab83de4dc387d978b360360cf9757a7d2c21f 1 976495616de20cf71beeb2e01b23e4b850d7898948157403a7b8cef7cf76023b 2e89fc510442c3a598c51362f4d3bc49458cd6ea5f2e742116685675d346a493d909e610411c2616a9f4bd9dab2fbbfdd26cd57a8a13669d1f622b4c0620df01 false -check_ring_signature 0084b7d78e1174e113f47279e493fe5ad73fb10625d29e40908cb8febbf47f7a 8265c399305824f8c0699628c27aab6e080b896d9475859f0ad253c1dae7b745 105 d9879fc25594e626015962a6f371df7292fe0b4979a8c0115cecc625b1bec458 24ddca57655df91209eb8062c33d0003be77db07d05c7e8b0b9921c893178805 d63cb4b8e219146048aa68aae5f5e8f65f383ead3943b9e28856b3defb1d008f af489c9113d2988e68de68102e4721710a517a047039618916b16e4affc44513 51a95e1943c4fed089548c27aa6d3ffc1eb0fb4009d02b8c4b35244a19ef4da8 6b8cb347432663e2ad0d02e500c717f0d92e1c150291848a5eba36dd4d4af988 f90a266fda80f4a2f048884b95ae95444689c3e2542191bfe1b10c5ce44738c8 61893090f49069689749ecadf5898d21273cacf6664779533a2d664618286987 7b95d0103093c212692dd17fe71cff78854609a88235af63730353b228e6cca6 96808c03966a9e00afc9039a3719383e377961c0ab61ce312a117db82ba83fca 348420e28cd8f8c38418ccdec8d891b43d4ba6fff97bb2b6007ba82fdbe90f2e 62257b7485fe15ac1f2075bc88151f58255a2be5006d23d774d4245bc1371bea ad3fdd9f00e0cad13b1bbee178ce1b64a2c76a37fc9d6c2d1601a51d7c7fda4d 28d2ab2ac2a9aac023c913ad34704b6a2a0a3d1d8a1de78b2adbe9b3b986ebf1 891e293bcfcfe0f8a78b200563e1b58c0ad425ecb3565031127c5bd607a0ac0e d7ee6a13e5bc35b59075b8a6d7322e906f515a37ccab6e63c4b815b2a64e23db 015140932008da064018479a43690ec49e949c3c71376d6bea5cd03fd755102f 73256959c04db44c84f59e50c1f129a4344e902244900f5eca0d34b33408d57d a0bc73505f3ebdb92d657cfc6843a78840250d9aad80397c5721455d55acd812 2fe6e12c10701605dfcb9021ac65bd368ba1da028e1c8df50482db6dc613c0f2 2185320889155d2a0f1802b5d1a6902037aeba534d1188ddf92ea306095300cb 1cf324fe8d45a5586faed246c7b2df27b579578c78e361d1e8e4e61bc2422b02 7608dbd8137272ef35c302391b18d2f573c0a593239a71bd4914af9436acaa9c 217ab41efa04f5f72a7e62ddeacb0850d7a47c952520c1578094be8801689a7d fa01df875bdc8746b669d29b0c1cfd7798cefc8e6e6ec8951872fc7dba857685 5aa952ca3f74482ecbcd33796de831a2302406b4d18cb9d2be3b0826eb0b584f 052ea16d5417e1c90b90faee3fa096f1f82c26abc3abbd2a081523d20707f882 55647a6a53a7a64485f460f7e999c62abc4bc51e084718dd5ff3f03f124491ec 64a6b8bd7bfa0fa5d7ca310fe4fe92caa504f93b42c63eacba824f05f22985a6 973cbb2dd8d713ce5435db6a8e2a8eae41f2131f8f6fe8cbaa2e0426d7f97517 20399da256daada4220beb1b2b4cbb84b9baf2aca1a27c36695f147eb75fef84 3b1a4339f4864dbb4d1ceb67975cb304968c033f0f3a0db8e6ae80d488165c1c b9a21880fc056774737f4e21a0cf99cf43f6513fa95ce1a493547f812e23d013 257c02378718df4115b4f2dc144f88b0226305d09b2d9302ce34f5521595b384 7b13be741eb3f6a043fb77f734c15e6b11b4a45506ea4782909eceb31beb7bc7 400f910b604aebdcd1adecdc6b3be125a5f36f962ea2140152fc6b58b020b14e e4f99960dbbe541d6023278632165ae143571136693c150a368495efbd7d97e3 dcc857a90dfac0741853ab180fb12bbe9115f60fa7038d05ae519f2968d7b376 e80917d45a7d7fc8e2a6454fb31d7a7954a5f1d0a4970cfd0479afa842d6ce4a d3694f72b26053744c18869e5a74db004dff96ca51a6f58934af1da99d375946 851191cc47d8a4689f7322542b687f7f95f46fc1a22efee96e034f9cd6273e07 88025e97325a55df3389181ac066b105e41a7f257da6a551669474ea8b5c0131 8ac44216077b87fd939711a0cd27a1980455230b9670167bc25b4dbbd4387f0e b8c5ac085948095495d882cd7323d4ae899f8d336257bd235c738d083d4c5527 ff322913087bffd551411a743b496f8ac4cbccb4380b439c8bea107433caac72 feec68aa6ebae178d38d333f7ec1467c6af70e46497d16625be9bdd97ac807b2 3d5715276ac3bf474b77a9fb69b6d90e2ab52d3bf3cd50901bf75237cc55ba5a 93a3661c469615ab4d2964277069a464ec58e943336cb3023caca11e3fd1b091 8b829f37664533a2ea009d9616de281f18127da71efdeb31bf3b101bc73a036e 2daf8264ede517e19536c241ad83d86c4fe2f5fd2be768ebfcec9ddf2b0e5f6d 4aefb08ee19a7aeea4c8054b5cf82561765ca4c4b803c7bd8fe8aafa27a53b3d c4f3795c2a3db79f90333c7ca5566b502488eb7f8fed75046094f49726ec98ad 023fe5d2448c454a9cc6880645eaaed62ef7526213f006a477166051588cd91b d45678996cfce0d8ebc1310851c5ebb8eeedc0fb4420b63f79cfc5eeeb689e11 4df30beef1f2b67ea3bf96683ea5e0e4c166e7b566a0f03de9759f89e778a9b4 f28f18c02d0da66b45f50e67762adca5705cbd44c92b1d3aef38d9eb7beb0dfc 37dcadae7dab54a54763a6e316c4750951673eaadf7a5620343c9700f111be23 d553d92bff408cc73394d297bf4033a69c6459dc1708c098402923404904af35 1bb777d8b42c6a7d2777ba89c7e2d5a8def4ca353a1066ea636ac69aa0f95b6b 4b3e3e09de6b9bc90a9fe83a744a557671409b553f05eb3028376411d333608c 8cf76b958db65dda10d1f973c96510acab7f7d985fc7a9a8b51a8de0e2f99d6b 8f16396e0dc2b5abe0b53ab8a91d80285b8a936006459ed75831cf846d839d08 a046fba01bce4b5f6c74395173eb073c9e08bc1e841d59bf44b55c9874738a6b a1229a0051a752041f52b15746694fae56d1f47cae39a10aca3728efe884ede4 9db65a567bfec4593ea4e7d020d694d3c896d80889088263a91f1377c088443c 6ecca18615774e877b09e9a63f20857bf15199495fff92c3189c44934f75eceb af9d5223df070391453de0be80196d845003a194cb11097a6e63795d9b32d524 4b4a7e4c84486eb33ec15b6b7950947d78360a7ba66c827fa4ac2fe09a35fb36 25c35d2258b8a70887e3093dc9b50f663a2cc2d93817cb4d41c666e8d078f486 f3acbd54ff0b37bd2fb55f51c669ab44ae7f05a82c586fb8168d978b52813e56 b57eca012d351704cd7df74b17b9344efd37d455685812901350dd2a70b4c8ff 38eb34b779e421340f87d6f84696a1a984003c94caeb0e9d485e61c84c4fdcb1 3f32ececba58caa7e14c6495ff8ff58e24560e4ad27859848a069f8c4497f364 3ca15b0ff296c5dce691bd3ae86983a681135356971e2f08463c96132e25f46b dfd1196f5f4bc1b538c1f32c233b78e6aeb39e6e2147566ed4ee72c7d47988ac c63a88b29dff3dd7bb275bae19a4bc0116f57b4d63929715582be13eccf5d35d a7f0aa3efe6427e2db8e7920b96a880fcfba81ca477ccfccc524cbf3ea1e2340 f7e646ec7af73e0b9ebe71552c646b4892136083c7d5dc35f927e896b697f038 c0b56759d0b042a1cfef156bbd6aa3067f433d503bb2621e8b091f4b5b3c3e6d 65855f5bb0b74c4f4b694dcf891d9b336186342dcc8387a68684a7570b6758ab d7605ccc1a26ca9dc88ca945d736757b4a480ddb89093d6a479af3248c1fc3b0 3ba5c026835ad44a51f7c9c72199becf34c6bc85093010000d942269cb7d696e 34ccb458b6070b7102078e354a6d6ec123b8248b8ab9f93cf72d559d4845ae5e 53aefdf6bf07039aa295f81edf55d88dd4b9cf3e24521d988546358eee50e746 f9d426847121ed85a011aad271da868b27b327b01f10456001ee7b8d01599329 c2bb5d75068a4240caa3cdcb5b3bd7fc2ef6fcd45f39c69149284421edd1eb53 acdba561ff335f9368bb18bc3498b90c0604a65b6dec9196127904ddb0c2fd6a 508e433dd909a603b6fe691efe88eaa87b5b53b086b4c5c7a95afe6bfe12a2e7 0ad870e8d531dcf397f85067ba45064f2843448692692ff4fca9f7fd8d2f017d 110f4b5759876fb3bcd6f6428d26c7bab86a92606e1e9d8aad0d9d2780c821aa 09affac0695710cd63980ce05f7e5d9b19255c72c477849305346a9217b30dc5 f5f3227ccb1d882c6422d8645196b9ac9a571944bbc0c2fd14578e02c0cd3355 19ec75175973d3fc6a5436abc71f06f59b05bcdb3c3d8ce6929f5c008e309a7b 5c40412bd4df71a14042fe8b6eae30b79a44edec0815a2e1a64e479b597ea8c1 4bb9198cb1e230fb37a7f2992fe042d6e14c8402e2d069e4a35e49490f4fe3d9 18382b090ecb9e6dcb9d75b0d045afcb62335aa6f791d698965b6ca5e8109df5 a850830b7520fafa655e959d8bb8678e91a4665350389ca19c5d1d9bbf04df40 f9cd7472a867660cda68833b4e31475402824ad8bf39bfc12201231c356ba1c9 c8a83329fe61f73dfd4175ddec619ab552217ad46128c94034770b8ff40f6c75 2b1147c4fdd1a25944725bf2ea898e5a04468772fe63cdc9fd5a44b5171dca2a 7fd2cd267f8f2d1e6e05314be1ff2c52689294c4b75b78bc776130b75e203f37 55b499077f9e84a05ad60315c770991187aa9978788c3e01456968ef23c67fa7 5a6ad91e163c0291b38a7fe107f8999ae34c5514b7abe6c1915983d4e6e1eba0 5c584bb0329ec21e029fd1d0321a16960c879e00d9333db11497fb9fc5b6e372 55f93ea227377d1bfdcc22cc5cfde33e01afa311cce5077ea76bb41c2fe5bed9 0fb493af8e624ae169dafa2d749846673120a558b6a4d6d9a0c9f62c5241990ada0ae201f40cdfa5a92e089f370f4c156c20e63f24e952b83ea7c3c65ae6730943657fc210e619472104f3b2b05929a4f6ded1f22044b32c99aea9fb55981d09b042edded03a35be93d982766b5364905af05c22162e44e73347124ac7f7b80b208b6ef6b08804a8b34efbe02e560cb3e3199880dc6fd3575c2be129db9cca017e11a12572d5ddca77a84e0acd648958af8385f11fe4d572135ec1fca7ae8c0070f45e00b7385fc419dba6f91d591562c51e9a056ec7eb89cce50a6687b0fe0c27ce64306edfc1bf00b8087d9b015fcbd48a152f57c98f67817e42c41bf0ed03c0222bec753ebc151a5df035c5e0c53d069a796119e548e54c9ecfa31b721e0ccaa01d480b0355a257389d86ae42e1f1813c25357575d7ab3f9b8ebdf62c1208fd3c7c5d9d4df49997ce0552b2a7af64acf00d48fc3dc40f2bc45964b7b99a0d00c7041ac39d7b60c6096b4bbe53633ea0fda9a0a3e7b4bc40b2f602f53e4e01018e06a37f8e3ab352d8d2478debb42559f5956ac2502dcabdfd9cbb208bfa0b3366aeb9e9edadc7f39008a7a9e2dec7a6c0e37628302676d2f6c1b185d5a505b01c3be2217872a4fe8f9cddf355cd5f5a4bb0b78277178b9457d9386ab73c003f3957b356eec213302aa5d67348a5f6ccc19bc5c10a7c2388478ff00310ce09ccedd9bc976bd54cd7d75896be004149574f2c4e8497b6f89b568955d0d7d40e2ea999490349b61a9c4c6f3c9e1d96f01d66c4c044f3a5921d8bd19037b36e09506e54c9d01d258974f71181bd01bb7996231e1b62f0983517d85c5ce78c28017a3206a2754baf9ca90d1626f70913d0f8d96ecc07b71203d6ef985a6e85280de2077486c99666d6b5a0e62ed407212cb63ec291424411fc867788629086b60519f406b0cffdbe7910d2b49e582250f3b52a3e3071e97be3cc257f82dc1af905b1cee14cb769823e2908ceae332614017b2349063b050b1cba65b9aaab87e20bc556438e6ce98dbb08fe3c544914e699d5a9044225d93fb50449c834e9d6e90732c56d5f270043f7cddb781cfdb35ea37e31b51a4b860bd958ee17add307390a9655888ee017c2ec18601f64c6ebf24b793670486a9eb6fbc0bcca3748663d0d7d2c54178d30b394ddd30e474721fe12ec323158839e4b62d137a24b94f5c30ce96f2f119e76223e5eb0154e6786cffa3a07fed2b0b035656590031f534e71015a52ea4d29aa6db6d3f53edeccef9c61270b78b39b5afca19917bb0fdc8d300ef0de26a7411c959395614367b11ea739d1b49bb6f9318055473d1f779f90ad0dc867fc1c197b532b435333e55d3fa85ad33c4e1b67a03c0bc67fa95840fe75016f216c82916b4d6772714c976f2366d4c98492566371096d84edbdfadbf3cc0316ee07cfe7dc409aa728494ad995ed0c76f10af7f2d562fdc7c8dfd2d01018070893a54c45a8e78a80a965972e230a0715b760f36611435cc7b1541232c8630a28cc1289b5720ac508065525edfd6b5363116ec83ed2588ca6b2dd33d9979805f95f7ce103ab2671ca19caa52b26ecae19d6892b4a0d2b7366aaa3e15b6eea09395dd03c7fba05d81cb99ed2b30709d33383ae8b9f4e20bf99f9200bb14f120229cf14bba26c3ad4e3ecec857974d1542ba801908510cfa62ad53b8fb6f21d039ab50fd4b5eff9e2768c1861080a2af68b0fc21c79b5117a4273422eed7f6008d034f03e6a205bf8747c66f440cf46b53cb21a1b790c120bee1222dbc9617902074389f6133a91afbca792ab4fd3fd2695913f3be59d864463a98e69397f6407efa2354f90ec4fdef0dec207c8f077d1a77d2f6871a0af60e7781ec85c3d3b0144e2e0740fec2722775a63f30ab44e858381bc3416c86086ebfbc0eef034e6077bd71929ffd58c4e028f449faf8f215ae1b3e2f238fed2e884da7787d960e600bfd08ffb15c4de4b8ccaddfa2fefa4be5840edb071439e1286cb0b8714ec8e0e4c47a02b5e8015fe678ae9e9065268910fb67b310264697ae41af458de7423065046779e959499a166ba73116ea842a039093457b03aca70d55743def6e0460dbf74a80a2abceb714a71d70371f8af0941ab913318d8a079b326f3c896c92200c15d48c0f1d45a98e0ea538c79d0cc1293365234c261e786c3c50f3cd77e2701afc7fcc2f2e943b18fc41232ede47df3bb61ed0f7ea7ce4b41df5ff3c0e146061f6df2d8c5b7eacd80ca042a9f85f444c9804d15a5b686e6eda37aac50495d0ec5cf16858e141dcc3dda3022b75b018126a1325a0bcca6a651e78abc8c4823018095417ab0e2e9d0bc02b113e33c50231ed7f80c0e1ea0e92108c39653f2d104009ff05d47b49238e2ea089ff656187c1c720b73f6c73b6a037dc1f28d942c0257b4a9f921ca794b740ec98d63820cd42f0db46f82d00c67f1657df9fd54bd0e380f836d1349eadd9ecbb6b89a1260ce2578260fc305a94beed6325565a85c0ee54050a78bfce22b49c8b26b2e1034451691d41ad5a3d341ed921634fe30ca019b815d561322e8b503f20147e1b6e45e81eb5caec20001d0b7d6f33f088b2707c96f8af2c5fbb97de29bb8384bc4587f813edddce95ae24b840d4fb1d124a1017857041bb06735d8eb38203a48a8a24c719b3be638b27b1807b082264f0d720cfe3b55e06cfcaffa272d2affc0f3eb63786f3431e92af9ffe896db20d6121d043d482a3673b4a93567683ad687b53426c6101efcd6f34311584c530cbd872f00cb0615b303870f5bbc89aa21be172f308c3d6d5486a37acc8a17f6db33e82303cc9644a19486bfab69c27f9c4b424e72fe93e27f2dbdfa21e6d795addf8b4a0b8f73d6db9c6762fcf99677c5e9eb053dddd7b318b86e76446e0eaaa137463e0ca1455b31639b407d08237655e45397384939c79245f4d0d4bc59a9bd76b7bb0580f17b3b18afa1b68669dbe3f2d4a92bede5746ba7a01f4a6188b47919c29a040f859e604de80def83cd3118686fbdc76707c7dc246a1c48dff4453127528c0f96fe0059c30931e3e548ee2caa4d74b9a0c3edf2596a626db295ae5374ceca07f0791758cf9da8da091ccc55ff4d4a74b60f554338ac10f2d5b405889749b00c87d1495f275e6178fade730865884548bc2dc8e8ff125d5c329196b51ea8670ec4330a61aa82a615b5e6945c98a8c1d23d8a447c563eb625c13246adc4eb4202994a2782d31a22c3000d28a25808b3c3433036106363a376cd70755a00735507a42dc6dcc3fa49ecec4f85a6046e2a0e0918bac696368c94017ae810d73ec4011a0161730b4c4bbfb718b0a08288cba944436ac7138a9e1a8ecbc3e4120e3e080f87c61b98014ae0ed1cecc325c5eb72e0e26eb2908a2807df2d915d0d658d0aefe09dd37890a6c4bc26164a0a74a5e507770be2265f2e7e92b8d54f70975d09ed970a3f81719125f66e4fcb74c542ce5f841d56aac53c4212646758f98d0107afb0002cf17708cce73ca5f225b5a39c76c2d7e4bfcac4265fdfe1d79fd2570fb19f139823269c5000197ba69cda53e0542c29475f39e973de04867ce175fe04e762dcb27df55189b0508d656e660d2e9a179b448499301a80733de0b330900f6f755b9c5f76ca2df7cd012d8646c0d5d4df5e03fde8595385ed6b70ebfa91049c3658a5cdacaf20ed9d2c3d3c26a40de5c10b1e26185d621e32b24dc4fe880e835b934b89587f0213deec63a64620f98449d8fd1bac1ec1cefe4828740254085a6566fb9245c1022b2cfbbba3854b2836487bd4a71c23d09640ea6116ee760ffa7621466f4ab10c99a52df0f5dcd2a2f323c1517665d97e6ee3bfb23a548c0b2bb6b7c73f869fbe504dcb5a60d366950314e1412b1c9aad1c182dea95509b0e919ecb3515b88c5a27d290ac8fb51c79976c555f5583547f1bfbabcf374c0500dbf06ee6b6b6a8b1736d2692de2610f93ef196bde37e7928ec9f230f105e2a08563b8b0c13651c012303e943d456d0f8770024900163a2175e6c73262975360cd96a05f5f5815b68adeee74424c754be8dd6a07a02bc1a7bb00ae0c4446dc7021c2c9908485377fd1cb17afff759008f09da0e17f459dc97a3743d4edf1bc20bf2df602effb028b97ff486c921b5f70d9b93e8f0063002a561228b8caecd630d0dc449d64d321ebb0b104ffefd46513a7ff17490c405d4c027a1287b6d6c6d055e53a50fd71ae32cdcf5f5e2cbc1dfe6b711497b19d5412c8d82d576f4159106931c50e403142cca9a3a7480bced2e14e9c72cf801017f53ff69a8b4d13868043fe9f6691d79c1b93941805059b0327a8a2df67b4c001fffb14edf91b22bdc89714e00e8ea693323f1207b14cc3bb24fa43ca2bcd008cb12a6b1a6b96730b60b4b7c0f9b4692df3252360b3aada47d023ec774ffeb04a2ef00b42074a3c0b601a55d19b10b2fe7f6d587bc883f53b47362c3863c532b72596b174671f5a2ea0dbacdda22fa49d0520fc6eff4a94cf5e995fd91cbb57cbbae9acf8f4dec2518084070ddd12e4a8f1e472b368c0ee06f7b9a2df27decc3c7ef9eed93b620f6e10337ec40d4ce007cb3f5ba3fcd7df36150c61d3729d7c5d2cf5b841bb4ee9f170f77333a952adc30e21db2bf96ee6472c04e813f15d02ff0e3a7f011f44283730756499b4da9a4654392fdef54ccc2ed2ea03bc6fe5cc0b6f6d74f284873407802cce5cf7f9bc0555c471876784e73ceccace51ccb2d4e47ffb6e4bf8b7ec9f10adbbcd5be55070a22a0a9ca4044a8ada3b6824703df786f5d575f51aadfc8740db907e6bd8b4ecdc111470a9fd8ff97306635c22c8994c87c1667b8169af63b02db6fab527b4019e3308983bab92ad0ebe3f40a4035398f256a4c2f5952072502120674e44d89dffc83bcea405f191702f4fb629a51ccb05d870c0587b3b3ae08d33aa5fdfc11e8341253394d1401b7415b3b48cdb8547ac5c28b03b9d4474a0bca9c7ac49cfa9a6f2f42cb1f541b0a0fe9e4c7e6e3675b28703715e7f104140417e0585b9111174896b5e1af9da896c1a93086fab75444282a01b54c776e650ea258fc6317d4dc80014287f1607efe1eaf63b63445e03246bbcc231ce04a20011b7c347bacf9f7d2622856ccb592ed81790f0781e2c203dc20f28c3890585d07ab8f11240c96ec48ab207f6136bed1a8477f8db6cca9ee148f5ded85efd209001991e97a94524c1860eb3f6e9255b766ab8ff90d6fc69487ac478bf3d1b6d0018656e7afac9c348547e82cb890902b551ef9a49818a1e895e870c4ed0b7201041dabda7987480623dd005658839ffcdea36ba33a81456acc32a13e4983649c0eaa5a475ea37ac018bd17ae3a902b31b13ef680a17dbea5c967e598879686be0c04216dc8a3955b15a0c00fb30a208bb92b2272675d728277e8c097966aa3e4056dc4f04fe62f451ac3c1cb9b1eb452d1de50be7d8323cb721df648034527d80aa23f90da90731bf1b6222f291718a0b87b45059e5aa6655e701e507c14494c0c14b4deda0ef4c26ebeb030824fdd53d464f9bfb91a61533475d9a8878c2ce9044494b3574529e77de61244d863f744a69df95232f18a8d6797178dd218b0ce0e6cd4dc36472266b87aed1f34999dabe17fea2ef167c6bae7f9a4c31a76ee2308cb7efe03895081f84a4152afa5d762b3d2a1f0f8dc880f74df793b5838983b0fbd784fc142de6a9ef9029663c64476a7cc248123e2d4dd472081bc136b3c370e5a453ec3bedb89ddb45b1ccc1b7bfce601503ac6bd9f90ab0ffa87ba09c3670f78c20217e3a62d465fe11389cf28ff63450bdc3999cbb1c893bf0fb5b3525b0494c908f099437c77885742f6939f9232299803a359fa6f1f7a204dcdd513b40f7ddb1fd0a3731babab96ac0ba6819e768ad0a920f6153595c62426f4a03d3a09c166702697e1ccb06667beb57df3ee4cd4dcca741a2a75afc1bf67c0cb2375008111b9430876884af13b416efe1f781d4d261405c31fb1791d285c2be66b0b02b2fb48ae6bb4fbb7c1482aebc38d34f106cc8842df408d7a2c8a4f1179dc100d9b37d88458f6e7512f475f2edd5d5ed45abb9735e9cd73f85b2ce1522f3bf803da298bc7eda947ad34d838417be6385396ba23da2e35c9cf5b7abd7b9278d10c90aa993925e8c1edb9d94c2d37fe1bf3bee0e2f4f3499c356ca03e3c00f5c10faeda31c2c4e3e1f1c4a79f6db7f7dd1c7674ec74f9bd8a01b34349aeca517702884654b709f2d582b61f784f5279eb9dfadd41948a3ff86f8a83bd38ee33e50ad017015f3f98bbb5c9d0f4b644c1368919e58fe544647117e15f1766b6ff3d045edeaaedffeb1752b558c6dfe56cd4ca3a74e526b97cf5a130adde29c67c95009a0dc0005c687dad4031d704c4c8f6b91128771dfb68fcb5b30ab088568f2706ff4b282f5d26626187d59fdbe376305f45ca0d15614248f70d817b6c628fac0ecd74feccf6271a9deb18628776473eeddff3c0f5260a55659d2745fedbdb790e748c0f6ebb29624b8f55e0bd199d6d46f89dd2781a59dd1e023dc0507e9a100d405301cedc17c7a9fe9ee09cfcc2158b2c4e24d9aeb0601c0a9a6261b25b3a088cf2e44997d73306faa0981a33b1babdc7e35aa85494956dc1abe1779f6b4b025e037156c5c039dabe8f300df7b59f324a4d54895ae27e30527d09aee46dbb06a5aac370b7a320d216581058f316f5c1975499675f9f7b3ff5849cf6f532030bd325e70a14d74c4d82787b42336686fd8f75ca41dda331d64a38947261243906c7728e0ecebb95265d92efd4411af166cef4f64a66602407825452543128160c1b274bf81b7efa5a6dee981c163560244875ddfa8eb39490ee3f148730cac20d770417fb48ea5f1be5d806a75cd527e4515327a01f87a18e1ecd58ff349cff099144045a5c40b1942332e47aa60e53aa329e76d4158a9f229c49823d1747910b273377bdf47a94d35366d7b732326b0770946aa454073958b34906d0d9400c0c903145b4e988d513e8ec42d1f3ee899acfb06a83a014c5465eb1318811f6c000cd92e7bda197211a607d3646bde2bf07311be8077d58b7160370d5def4bf770ac50b6e5f7f876373411c56cced2f3d912c9082ac407ee1285608011bc62d670849db3238ccddb5eb020a6ea69b7fabe57bbd5b8f2f36d3044177dfdf0dce8f059aef956cadf35cc1aec7301c01bf0615cfc7202059ef0e6322cb1cb69c9ced0fac1a490c77ec15b26cc7e7e591bee9b1a138ce6135e3e6acac4cc8b2056c4c01d7e31799822990157d09e7b1966e50dc09b34db638ca615af9ca6a115217c20fbf68cbf789710ee7d48c5a09e5f680cbce77f54fc8967b51aa369decddc10c0be4c93f0eceaf222d58033d84cbe21723d615b6d4419c0e8a612d17c733777e064190d1adda447b7bed450ca61f5434e9bd8a6e99e7cc16b0ed30eaea7a8a68041fa48525a6839338b40cb72cba97e5a3173e0d34db6258a8a75815eff7653d01ca38eb1aa9806361e8591f453c35cb82fb938377c8b93f4d00da7ecebffed70e257ce1559cc41a58c0a9dcdc3b35458ff1467d4c1fa2d140763be3e33653560a503f9d708ee75e2a28e4cd045c0c46adf15bb172fa6d59e4ef74dee30d44890aa416d02133cb8add0726ea8cc5a67daca0ef39feb5ac79294d218732ef9e120bc3febbbe94e6e07849d204fa8101940bcabc99d58914df7caf95510615b88e033bffe15c51570595c5dbb6ac050adbdc2776e3f1f925dc2a35799369701c0202c7b57a5e2c1984504ad660c34ec3c0a349a6e23e39fc78285d95d83c691aaf02007e62b5261aa8a467c1e0d083d75a63a20bab06132f47d9d14dd6ae70d96305191ec8687207ce4897318eb83d5e5912063fc7693ac6c293b82c63ecfb813003e9170c738532183613aae55a2b2f119fd8b63fff4e37ee66fba7370f5fe9190a98b7aaadbf74dd02bda966edd3d2f15d5a3763e7242ab32134f57ce2048a9b09281b3456c9cd1f0bfd81f2be43aea4f29d768560d149651d387ffa795c3cf806797dee018e917f448ed19021b0411c662645a8209d8799ad5d3cac591e22ca0d1496c8ab744650fa842223b7804b5042af69185467dd265612d6a2d6bdb70908c0af0ba1beee9f5aa26744749d000d62c8316683e2d7f0544b510fb24578590741eedb6d36d5b19c830eb59d995bd7215565a9340b91b8a9c3811671848821047578eb239cb5c6286a66988450f15be451b0af097fe255bf76d4f78abb25270097e05066b8e62222ab341ed0a657dae95b2f6da2c727783b5a58f4cb9f93c90717ce8f9226f0a9502add50c015b66db3b017a6682dc5e5e1771af57af405e10ffb9a3d73da2d7735071524da17579071699a6649d89a4dd0920114aacdb8dc094b3fba7bacb227ea69d945b3c043bd76aae4b6cf1877c0e0bd04dda93891290ef8887c0cf147ffa870e66d01e1736fe52345bfe350316d5435870394c54ca501fec5b021994490eda273475f7df183af676da86602ce8e6033f3d5471e949709995ff944fa7a43530fc0b292eb7cd5b5c0d5e7aabbdc3297db6da444a08afb018a8f950ceb23aea964f0619915b95c6dc99ef0bb53e5b06d360ecbabaa8b000484db7b83d3ac1c2369feff8b8df2285f6113407d40b3f3f1372104759ca8ff071763151e7ce435ccd9ade02664c7e0d1eb8e7e5dd3ec9c6d282d0f1c3a73cd0be8639f9cc147459d1cfc209b9a456fae0dc3c16f64670f858d42c92ccabf890fc83544af2856d0afa0f8db925d19eedb3f448ab6b9f7e530ddfacd9ff57ef906a28ec125a2338b1970b3ebe5bdf25e094e230e05b09775410101b25f68e164054a8ded29e145454c5a8759810a999ca71d2bb1527f208e64af24c5b966d61603f3d8cab17661b9ec0455a63b96a0b4618c2c20cb36092c824616436a2a074d050f44f04f60e5839ef66d95db869d9da5eae149e964aaa92c5d451eef3f580e0e69dfc469879ee3484d176f25b349582a7409a94be1c29ff9379ad4d3675ec1041ffb24a4ff69d735baec1ca77a95e736adbf5eaef2d51c6ecae435d768fb570d70d1dd40093ad1abc6423e2e2019dbf274111e104cab8cd4500b96dde75f2e02428ccc8f9b1c897a93b439e7fa6d6bae4af07abed7cc6c346128e04a9e70d2071ddd304dc2e4eb9e43c76472ea23325c194925a8fe2fc7066f2bb2bc7d54d00d124837fd949d425d3623ecaf769acfed5e07f374910d3f4fd7d89da20d66b70a38e6ca0119898a56ea7af66cd618df4fcc7a5550f61b6db30e5e9b62eff8eb05955b99f1a093bb3522e1ca779606317e857b1d50a3c507331d5ff8325a41da07317899e4b11d2e827971123f194450912d9008bbaf08926c5163ec023d60250782292fc062d7767baf6dc13b1fae1f292c0894af73fea854d96c7b6698868f03 false -check_ring_signature 55466b6e3fdd3c65396695db8cc38bf5b202b9b5250a2d0a036d04b92df36add 17ef635b1b63b0f7d307bf0fae90946fcb187cec48a31930f166cc1bd58d1115 188 174b506211a87b9d4f2387ef6217dd7ab4567e7945ffb571403c11dbf0c3fa27 86bf503b7012699134fd4709e235c6113aaf001ae38235a6ae66eab4fee3a89f 7a26cdd5d01f0565d901554a1a5f4ed954991adff1a735cc2cb8d4827dae5eb5 cbd09d941409d78963ce9b0fbc74356103393c7498be97f9d09a310928f05ad0 999160a57ea074275c2500669ca556f914396d80702a135bae6f94914ff39ae1 4903b24466bc278daf304ff7807fbec3415688ec9182df2ec5004b330452548e 2f5b7db9f6170a0fa2340af6c72177afa4722b20d8f2956342163bd7fc2f8d80 c766f4890af50033f0798354dfd3613c1c7346a9247f69d599728549599a8964 7922f79241513a7a4905ba283c82912a994d44b899030a3fccb7c77d98f9fafe 95322f7ab579a1373dca3fa5a3be4a1cdd9a39996b52d3f987e0e899e1f87bd0 e044563452b45291cdf6ae49c1d844fabfd64739d5965a15cb8ccfd83c85e7aa f44bf598962173bcc57464e0ed3f670e82dd5b8cc03c75de8d34784229332335 91aa528f9ca53d922e70a3334692831d27b5fd28194b12248bc226a625b110a1 161fcfee5b44941f2abf4ed59396ea95bc8fbb6258ec56b20d943b8db0e83028 bc6fef0f7b43b7bd2fefec102129473aead7b7a5a20b51421f47e5b081d09705 e898552d858eddd625876c1944af85dd6581e572d8609d5fe3eca575602d90d3 0f595a52962c9eb0ea4bebc46e4778b787f727709ad2a1f2668a601c37f54f95 4781ad9c9a744230ed76cd614b771f743d0f719081162b97c9029f1c371612bb 2db52b8e93945d728d32e7e3d04605c94c4bd4f63875ea4ea7738c8c57ed84f4 f6a878ff1794ab273ccb31dca8dedffaee7ffefa3ee9f861c46aef082ee81ec2 ab4e06d49475bad348af47ffeb81a34ee6f5cb6245516010769444e469a14f5a 5c47b31250824c9a0ef053345b9e722c0235febebccd8826a5889ecdd24335fa ec3f319e9976aec3ddc16dfbd5bc11df1fa9e8bcbbc54da7f3a22373586d3d0b 187d3b597a4c9b3b2de015118d7e2e6ecf6096c4855ca9c4189f4ab0afb76882 da2660fb8d9d72f8d51a33e792c769e99e959424c5c21b9c3cbfcb5351535cd2 7bd6b3755e476580a7cce369023052382022f934612fb8e48ebd91e5abf446a2 339ea73eea3ea78a9c22ef3e861b04199c0d3dab2f3ee6ed3f77eabaf1b5d111 3ff792be1c8f4cf29bd5666ed029c32638c161d67a6a5b42624ed8a61bd4e021 0bd151a53fa9624e9227cb99fa34c57057d32719c384041cc1037eb200967c0a b7f8de3224a0ca8bb2b6723546869acaa992f0328262006c2790be1c5003f1b4 136839eedc3a0aa45dca0dbddf15472599000956f5f36361c8f2a9dc6bc18a97 ee2de7282fd9763538574f2ad687f0ca4df038a3c266955a0af44358960e2d6b 245ccdf706f758be0d5cb92a14c1032005ddf982999d114699d8392722b2eca5 806a7c3900d847b6951e9dee218705fee7d1b62d11c0ebf7046e06358880be61 8e230db8c161e90bd1ce595d44816d01b39c51f60800271a20c676676cff301d 7ea0b5fa27cd33f642cc08820459cf00e9dacbc2eacf6eb5c46897ebf86ecb1b 9bb7786770bae19b498d39fa3b998909f8bc453c54e3e95e39b3b13eb8af0b69 5b384f243ad88029c080aa66cb1fa5b3f92dd8c65134b49d3d400429c2eb05ba 3e9166120a4707cf42c0ce21b2d6a85329d04aa28dee9cd39fe47e4e4fb4eb80 34b7debf3125f72db2e9d65af52ab80726646fa45df192302d068d11567bca91 89dc2532f4acdc4143fc3b4235e6b9de94b9162f2fe4980f03aef33f3a437325 ec25b69c5811466578cd13627b7d4b998ccf93b476131070d1ccfbe0ef1abd1a 4d24384cf8b2ea7af20c4f968dc36f9b8644c3104016dee0327cf14e2b30ddb3 fc98e78454ca973d94718cb8413a03f70d799158aa16d938376fdf9eab860dbb fb90e3427ff3b12d10af4f4c9e5f9a2dac43adb98e402014af68e18c6832cc44 67f8f1b39770f3732991061c2ec21e5f1baf9afc4ae8ac69a3338296325c269f cfd062e338f3e8220a94e30f08224cdb13a1cd24c7c73c6b85b9de7647cbc0b6 d3f19c729e1579960a7c6015762dcaf38c4ec8e975550dd1db7c13d16955cdeb 03bdf0a54cdc523d8251e0add834c72dd85b504b673903022ed24aeed264e5c7 48e9c940ab6229ef9fd386eee823faf817ba329dfc0cb618588e60440b96cb70 164b2212aafa5c2e7ba74cf37e254e16bc75447fc5207d6cafa4c19da6bc5427 14904460cc3d8a74aee1b2ca116a8cbc24f930c2b75126f9d3e399ff2966d56e 27e7acc31684b4e3c97cfbec21c7287ea3aae7c72133754c8e2237894970f86d 8ba7ced39227f9671d102f7f387d66b3a537871b823446b88cac52f28ece108e 3f32e178c7b6d9faaa3cd0d243e8f0d3d947938ed5d3b73a916eea0b1f3a0dd1 2a573fd4bef527732b42f013015e093006668353a7a361c47fa7762736e6e737 873d64af0599d5824a5f1e74143fd8a19fb171c7455997f67c7a59dd524dfad8 b57632ae5ebeb6bc84993466819bc197c360d17b505467e64a9b23c8afa9bc6a 599034fae26e25bebc68a23b4eeedcc1cb090fc88a2b39a5d7f89b81b2f64af8 24f7283755f8ddc418790257969016a499111f96189269035be3bc9971643569 6fc415136c6aa35b986a3828e30af01b0af118e7d02cd0fb06e6aad1831cddba ffa3733e6c2ed001dcd2e58e3e5f07408220be794a0eca567ef59bcfb509a9a8 b652472ad3fac23e0fb91cf36b5026d0dcc95e81b07ba2f328f6f304126162c2 667091be03799e4ef96406281914cf33d29f7091929f44e7848bd02edb5125fe 8f0f6b1963a8e67b3f9c57dd59cc43a1a2a3c2f51e5a3fa9703416cbf321440e a57dac92763ecce3a19474efa994c1f8830788e758fbccee62028f4126c598f0 c257821d89104ee54896583d8e8c9d8efb8ddb35569d7f3aef4dc0300c5bb846 4982883ac5fbfa6684d5c4b8f72b41e5c233334a29c33939659ca71aa942d080 f67f72115f0fe982b735be82c372d51fc6ba6174da6d2940381cfd50cba05dcd a9da7954e5eb09bb1df9e3f71eb420b3266c2bbe87aacf79784a583cfc81afda 9653503dc689f19db10e92eeafe3895e469adde098158d2fecb76c018d26d6ff 59016d7f6f47176d41655ced5bd75af61e1e972cb16d70c3e03712f85bd73646 d5e78c7c6e694e39f34fcede4899957e2c743e0c0e7d9d5184be823eb89e8951 776cc513e209c90f8ca987228435a89f04a816b30a875824c4f9b89540b13790 179005d1f2391e08f71b5abcf04e93af5731bcd1e1641b8a3470db10f2958735 f82a049ff600bc1633dd96388b778ccd3455758d4eacf4237df9daaba4dfb93f ba6412fd7049b22c10a97cc6593fc66ff39f7d2e3b7b5c4cb935c4e7b81e4545 a6feeb5154053b0e7be4746d21e429fa48616c64734202c0fbe96d0239a5cf17 92a1c8ba615f89e53234897ee3de1173370d4e13dba2ec1566727f60fc1bc913 b1efc2b8b786e2b82ab6055229ad5f391a42e90503fd72d25047a2fef5baf004 d75cad81509bfe86941d9a30461de8e9a9a90a62ea2e578b217332a344cdf3cc 0b4580129f8206a325d17213435ba02462369a554bedb9694367ee9663bd3906 69556063127046f7f5668de50cf50131d5dd690143a9c6db7fff2fb7d7762209 b328edd77aba62acdc7d267bdaa24226a9da7625696e7a0b683c8a5ef6fe66d4 73078f5c2d60db6449bf92455cba3a2937fb08660167ac162e9e784a07c450e3 aaffdcad6a9d529c92e14959bdb286ef37a9663e8dfe2c98d13a4a84fb0defe9 2cf02d99f93845978440aa11f69604aae8cf97a873c8f6d506d1793f7bdd0a42 b0f96cb98002c1d7d8dc4357afe3ab003572a739a2ec63fb66947f0497cbd84d 81aaea97b5a1be300016e9e2113a5cc2c490c6dbcb29b1956d2d9a1251941099 6f80f04510a228615f4ce395394c15096a7ca34002846fb5c88eb83eff627921 b8332dd6d50a481caa662fe8eb9eed08c79aedb48f5bee6acdbf69eb02d465f9 7296aa746e0f9561d9399644ca66e75680d4c6ab0b52b8b6a3f43a39eeacea4b 2b5a065af745f60ddacdc84220ee6c52e39f8dba230650641d45429e8d69dfb8 31e54a5acde78dd853a77810874496d059ebd00c953ef8bb731b79407091243e 3f1e35d0a32534c5c0b8fa95f08377d7561b30c15fbbc7942e0a8e797875bc8e dc4b32d2cda60ae97ba58c57975d623b997003b75d55bdd09602ca1feec220fd 21a8514ca65450eabc9a2a2b90ff790567189fda942d119ec0e105b51921fb2e 43d766263f6f0ac6f4dd3e5cf2b2d69df7aa4ef6bee2d3e0e96509155028ba1a 261bc36890dba5bc9b3eaaa81a900a0b1ca8b1c4850d819ba8a25535ee00dc1c 667c911652cc4e9480860fa004c1201a681f17e2cc1239a1dbc3c03eb0fe6e1c e823a7dc36c4c1dcf39bcfdc74c58de9600cbbd0b019398e93a205376c28ca83 b04fe2c822027fe3ff1b47e9f74b9f0a830434d3dd4499aacb3bed1499fd6ca7 ac8c49c2213421ddf254f1e4750368b191fea7d44fb256d22a74b12f1f00adc5 4cdaada4a4596f384a66aedb1f9c30781a222e3dae7f3aca76f2356603d9e8b1 761fb5fbb8f5d085db4c97f09ddf3b96863d0cf351db4fe07b68c9436c4a78ab aaa429d2f78a78e444a34c5769a10ee1c38fc16c84a099183485f655f1d5898b 91455816fa0b2c8c146ff2150ae599c046d7a0df1f795cca953e20e1609e7aaa 0cc93b55f93d4e502f44b305d9b8b9e499b1688d0edb173a884e06e315ca0bf0 60d5441986f0c5357c9baf133acb3263e5ee6e20e4c62e76f8500bc36d96a540 f4e1ecc0e06f40b8920780aafcf7dbf5a23ce93949f90e213d38007e582cdeeb 390cb6128feaf03937493050f92d0db9d6e93e4cb2910a9233a9251f6f84bfe5 4dd8afad5d8255b49160cd64d33002a711d9b7ae5cc39e54e00fe133e0418304 50910e572c7dfc662272b26fcc626b5b8d2e2233e868fb2366dfe8d49338cb38 d7910da3cde2b9a48a69091e1b92ae698a8736d92e55ea00568e8e76c30f5000 53231652e999d43bc134d2271448d1bb102ffca51cf5bc3933feed57ce917842 2e897dd3f9266ef1073ba51e46afe445366f581b93e183585cafdebd8aef68aa b2c795b820491f3eb3b49a4b82923a2d54a8d55cee3ba0caf63a1827241859f6 3c34114ac444b621f904e1b5b04b2a6a32160406f33f9d80ee9bb144e6f197f2 34f14f1a6b0ee84a49276142652aab903468fba4cb230201626151ea6c6f5e55 d458936b93be498fec6d8cd5d79022247ef21452456199da98dbd3634197656f 43fbfd2adc3ebe535fc10a90bde627a27d1daaf64286b8adedee959f32bd97d4 0da6263936cf07165699360652cbf0dd7967fca72b5dbfd76ebc27e5537fccd0 f7ea2fb7d32a215617e7fbd0d992e8aab08d0bec9d65dad2289ddcd9c29e2d2a 01ee9bffb8c553366988559bf9f0ce64ffebb737b6a42ef369d7a89f5c62a8af f4e58defdcbf5e6d678b0c07f2f07a27f7077709d5b884efd5157553b664320f 50972b3a75c6dca3519567901cb7275592dd384e287fc5c5360f849fc3e7d2aa 69b15a3c565c177f6c99110791e32500a4c907f578ad147ee2377368cc0aece0 a7a153681a133c3ea2ba2788d7306c2c20d17842d82903f658dc2c23bfea7971 4494b0d4cd18c2511243003d9b2085cba56f6b63bb7f094ee385f66388d7b765 3230e6719b39fc3972d77f0b626f6f1601f740d6b5657995ce73a3226dcef889 42a7a66832dd3487a446f5ad2ec98df4b54f5f5d04d9694a1289b8a6d0228bec 38ac3515008b7a3b9a77776a18d0f08848d7c3e7ba620165e3d22df3eccc8f57 c240f5a5602160faf49ae0d5a9eb512883dbc29073fd0a5f782b99a7bd7999df f663b07492d3177426c01c2dc2d6c162562db08aade3f3d5a7f164e56d20929e 32361d593f0a0c5bfaa89f9fa98f8dae32066d8fc9581339b052b5dfccb3f813 0ac729dcc0c8620cb757e2205deafedf0e1e531a00224e1113b715c11d78792c 403506b6c359f000d63ce849d4a172f3875c3e37399e31ca58e4eb9976de0c8d 4237a32ecc75c72766e740b4edb4b6f3c57f2afa0d11768df845748da4db926c dfd42f2ca9d0b520aa32816d207a0cc76d25d41937869e8aeab9bd2fb90e9d89 338fc1d79c279f1bc15eb0d1ee7e2b5f6b4c9971d2eac2856df612f11be968d9 75f140eded0f1ea2ffa0fd4216b709d6b5f607ec2ce71a9448c61c223fce139c c9ce364aeb4cdda00959a439b0b9ea5915875e62fcd87bebab811b5c43a85108 ce3b1c3ddb7dfd7dd93b145b2dc7f28c5f857c99b49327ea85f45ff24378c610 d3c66f0178c9b3c695bcb40c2de892ef1022b06bb7f9d840a364d3471d0c7130 4532d52478c2654d0e562cc963e2e6048f752b2f00a66b5d94525c84d52931d9 2e56f2c2fe45cad4ea997f3629db8042157fa5881d35cd3910a7bae0c902ea01 f644536aa091eeb78b4c68a4321bbb0d59c611e247ea24abb36a3947c3775cd2 ba401aab7ec1aaece35a77cb34e40b7c516271fb2f1c9cdf041a921b5b2cfd08 4468e65c88d1ece78f527f6fb5d3dd2ea69dd913e69b50944b2980df087ee942 7b24d092a4f0153e49a649b7a29d9b83b453cfb3d7c25d6bd316c36d58621445 ae731e806cb13c8e792aabefdf46b6ad1065e7757ae2cacdd94cef1107485e8e fe00bd282de6b12edf84d97162f9d69b6b0c887ae752600e18afa75e42bcb5e3 47bbb7bfb410f452cfd61178818b2a670f901fb3ec2c7a1f5dc84ec83a1d8a75 77229257d5cbf9f8333819672e28d8d710b1e1182e5a869d794b3dd59ce0292f cddf72814456dd5214cd2ac099d0d8a6c25e84cb30ec6c44fe8a3de8d5c98de1 65efa0b5a33391fe8830654687ffef6fa8d8b8c9b2d58e9062bd71b7c64812ae b44027dc3f4a4a7731803ebe252d15183c34ddd1728e602ea0c70e0dc6c86ae9 6ecf9be75e25b8166fbe5288721d3a050eb5c4b50e39358d6b9945718fc55ca3 13a88eb993042a398be573a9a088088b1268e202aa27892dcd9cd54aab190025 6c6265f9781fd8da2fb75c06f032d7276b72c1b868f124263178fe4985c272b1 c54b31812055c3a0f77eb2475f3a6c5bb79d445276e13b9537aeff4f5add8a08 afd4c016f69ae762f920f908a38a4bb6994256b7605227cf2487c452647e891d 3d76678d57fb5ae61df45f4d044989f299bd8dcef3bde0c3cf1edd23b873c33c e9a1d3a6370452a5762d90dfc4388cbb30d06fe33ecb927e528b344fa8848d7e 6828d9bcc542037e64f076135b8098125c595acb72ff27c766aafb0542db2382 a16aa0d0b235ceedfbc1920a1b4801841bbf77cf447a327bbe41029d63cb5f62 876a693845ca46e135988e89aeb6834bee9448e3cac9c77f46a1958e3fde02c7 0b46c3dd1ebf98ce198a3bbb2e78f9615b252179c875a1db9a630feea30855f5 6592a56db22460682896457fdbb0e0fcd73d067ea40f551602c44598d482a38e 971b2220b28ea6344cdb67bbab99082e5c73b20b1813d278c6eb6d56795f22ca 18112f04772bf05445e6669e3acadaf8c1540531f3c37e6db932160eedb45256 695189ccffe55027a0c779e70835427effc45f05d334c34a61fdef75f200cebe be4ae24732784384e7a2ad51313e8a6b2e6f2b937019e2c947b54eaf93d3db78 4887d622ab77cd0ecb5863ec4f87247daa2911fde5e302673d51fa809c2387a0 1d64873f5c135443d3fdcb13609ed7e475957a17c5d307d2013820c738689238 e783978d5e6d59c7bb96557d161dc5150994de3deebcac8f09d8a2932b31432f 767d71e0bdfd6be37cbcd3c0085cf845c13bba954fdf5857be4f777e55a7ffc4 a012d38ac59dff2d16514aa822325b63d82c4a96e5eb7352b44536954cccbf9e 0f161f07bbb5dce120350f6e1f482b9856375a50174a45285a80f72d45b05b8e 287fe259065b6f29541ff1d0e4dd5addc67fc07440fad8b35a0670e0ac4fbe10 b6129e763a8f3a01f52e87f5063713fd8c015f9bd7d0d0cc7897bb1f9c65715f 5bd9eb3501c24083c50baaae91d5f2ba0dc77571d327a977635c7af874a3ef7f d916364c4acf7fdcb6df4533ae2d3b3476be2accdc25c2739f2bdf01dcd8fd33 c465e15b311984c05fd3db54da9521ad72aa417974844ff24a5f3066d0e3c779 9df62c7d7e641a5ef49e1904c639cd9e687e19d80d0c08ce3c0c2699b538fef4 26fc3f27b42426c4219733f08b99bb522bc32aa916f50f3c5a2568c1a7da5302 9aedd058a20732dec1d50a3fe2e8236f9f6b30707f087e6d11795dcb95331f21 6e1ac21e0f5e881ad49d905c8fb1500dd08af2c8a4ad1646a32a72d0242469ba 7eb3ad88d644e65d3ef6f4ed51b49d23d219b6ba79e4f9d05f48f7ebdc4fb505a91e449d0231fe25d862074cdb53b7465f4753a3f2126a74daf16541c39eab0b3e4602ec16d360b17d4ebbd17b1a14c559a7a91e780f98bc5b094b323401ce0f94bbcaa3f802cb69d7c6c3fa56623853f22df31c1854c1818578ffe8a9476207b3a71a21ad90e676c504303a0a45433221a7f42dca02213a05e6d15fdc19c80609141eedf40e5ca83d6e75943197003e4e474cabf567f6e805ce70903d8f5302ea6a1c168b4e54f8d4cd8d9b377c0b8a03a49ea3d5ef71b94008229cc7cc35067ca463443114f6688431ca9200f3a05e7af1abc375627b2c3dce88e8ce9d4503222870e594bfb9f655044f01aa755d453871d8755bcc0833b1bfd5a71b21c80f688682af5d410b9eade06c2c9c1b9117ab4529786783d62c06f2b32478cc7e049b7290302782dda1005c78bae41cd439bdc8086af7dc4a46dc791930fa6704028c4b4e548acf1caf7997c6f99a859b3c6d2d874fb36a46a5fc6ec98e7320b10a74948415efe25b65d80c2b4151c67388bb7c346a158e3687a8fadddbc199b40f0d190980230b3a3b53eeb3c6b9c7ea774cb5e1827bacf1325f74ebfc6d382d0d2e55b0f001dfde8b058292219053f3f4888c9ecb17c2ecc47eb73095a577de0b7ea3c57dc35b3c922653f37074efbb8a57858037a1922ff5caab9355b6b56505ab4ee69423bf34eb793eca6e15d08d89c7822defb40f21f1cd6d7a8f81c2e20039cb74242ead21c9633f2be8fb79b525c6222f74a1bdd4be9031542647c0920d05ae3c05713daac0533521f349222cda87e2dd9c18701c0da62aadecdf925405ecbb2fa137d9c12d59592d5371003afc7115d3a24b36e5bcc614fc2bf077b2d4664364a6feb42bd1bab2be7c05431af89c3956d3414e0ff950fbe197962af30df30a33b4aba2bbfa535db006dbc2cab04f099075a849597b6731656aa9e86c0c7d3e193f9193f114f2b1df10eabf8cfe65dc45c480539651cbf6452898d6ba054e1ee93098674eca8c47238bdafe90e44f0d9fcf46651c73e7525992c2cbcd0932906be713f2e924742095156d87d5ae5a01c92eef6e4ad5f08026f26e2f21010b9a6afcd0ba1a1d99d04bd504a30a9fc5099bcd9b66bac7ddbc6bece85e260762d83f7b724e08dcbe165b4adf38434c9528f914e17d71b0ef1754e259b0d804f2fcdee9fe8c2ac9a9a777a2e1efc5c61bff7bbf15754a0703fc3fa0bedaec0f9a0f44c0856020bd44a9d48db7f37fa36e16f0fa3fbc7525892fbefc0b5fb40dc064e47752fa5c7c2fe3fac185fd8acd0d43f3a4ad517a912cd750f42b760703f9dfb604951b17b637df87617905efd81760c5ff95a96f1dfcae69d3a23d7803965f55b677f7d86c4798a55e274069633b4cce4d596e4aae2187ed828b4d3f05c46604b41b962c9ea06bb8a66985f4bafb93989df15e7e168d48d6db6e99a10f88746e77da401274fddd6f28d0a8e1d5bb48b5e46ede074c3bd12e3f942e3b0c6dbe9ebfa2ca21330e6eb78a32d807de21e89103e57fadf66c7c20b15977960c9c1a6da11198ff2a570afa1457cade1d315c6cb5deca1d721b2159c494976a084af2d50106ed3e28317013021f4d2e0a4756436fc3d3f84ca4f9e87a3346030fad080dabf50d8f032cb8f26f686a05371d49b8ff447ec83a827f4e6afcee1904b1904aa50834cdac79fbbd56c1228bdbcccd89da270f7e834834a733a88b0f07fc7d4d386a47a91130b9cfc490efcc403d709dd679cf4aaad950fdedf52ea7071cbdf6e4aae1206a30c2075dd3d339eef1653d72004225f74fdc989f5b152a0f97b75a9cfe3b3ee65914a1c4cedfd5649cb5506ec8d23c973a5e051665ae4c000ad423e1cd9820044bb0225ae2c40b7778863420ba4c019b87fe98d4261c5800df52947f9d7be7eb90d5aff9b23a22cb1eefb5367189d263d8ab7b4b62f41b02c14add9b39381614de12dc0db0f785b80846a222b8038c8425d1667021612a0f994781cdb5f178da7dc9b8a0dcd681c4e60883372457022392a01e1324e1120d1846431fa815420467cdcb5ef513a0d721dfd0c5fcf35f9bec8db5b2f128bb05b667618de743d40229ac452c58fc6c09279c5bbe468a32edae6445460b4dd3090e37aee35b2afff537d79181a7bade4338ec7b4f6aecf5c32d19ec41992a470cbc9fd18ce8bdcb99941ba1f4d94c3eccbdc5120fd045977933c96e1e61765f042d537c4211ad0eb546a596c45f7eb808fa6f4a1ae7ce06159915a77b4830c00a78367a61b870061a6d293804ee67dc6d801e4a7942750b221865781f7f7bc6013c40732bf2294688d235b3379eb51ff3ae53d108e98c9ea70d19e5d2cb7a47050beb769a1027c7b4c4bd7a3acb6a0c8cdc11e4e5fd140bbf078541df3e6d43066eaf96251408699ee88cf89dcf588ca87c2c612a40f83f81246cfc91d124c0015b1628b34622e2ea95ae9cff3fac14d20774719e80f456e0a27d911bd332e3043fc34d75e56b383f3f2bac5539601661dd1bd4bf4e388ce56e16a36d072be508d0331100d51e07032a190cd8a0430574d2da1582a1428cf05dcdab285925de04adb34dc773ae1b9989db7df120db77b481474f2bc5cd7cbf3e37ab61c9ed72056f9d93987583995fb6a37fd4b8f3dea29042e57ff246a72c33f353f4632e2f027cc0ab0aacfd054831b418ec8c438839b4b63cea8a875a0046dfbf93dc985d0252d1f188ab677985927d68275f3cb1b459457c05812af3df9826764bc8299f05e40c7b24e11b11460041923a42ce2ce750feeedded38148fe56a1e2adc36c10b2bc993cc03eda6e4d496aa1d76ece2e301f8f793f4ed62ed26f0012aff5d650d311b6ede21c94538d0148ac7493e36be5dcd0f8bf52529b3c65e3a027aa49c08123ec62cddfd224e3c7a5fe6d114cc110f459594305a8c53f2a77943b8600802ebc62e1c02c3b0e98b8e9da73a838ae1c681c103a9fc1c3859473e0c5825a508f9439ccc560c055d83f71f07043de390f094d4b75470e7bd8018c67a6b2a360708221b65211fa5cedd28f523a7713b21715e559b2d02497377e4762b14ff0202d4a8c6a6870449212a94c1a1b71884f92d460b1137b7d72a5eb78c0a5dc94d0c15d8daac7a35aa8c45cdf9395e4f4d8aaa83da45ac3119465ff93e7569933f03ab39b0213b627596ea0ddfbf41f6117f780b90aa2c92d6fc65e8cf26b9d9e10fb5023a462f93073ff7a48849c5432fbaa98c6b006350c5d5cd29204f2c96a708014c574769df786f691b28645976a8e9559eded442e20a0057a2432bb381e40969b77d7ee118a5789590c12dcbc30a0685100b61bfa46cfbe1ea22987036bb04ccda682e012cd5521fea2e9895c9131f89a1bce5b43713844b74d1c7115e9b0a5de836eea1076e83ba6b118342f0913ad7601bbacef91df540ab8a437e219403120a60d3333ebadd10382c706fd0b69638a827b68b4c4157cff5c7799ac428064d335a65e08d6baabc33bec2b70a38690c7a30d7f64c1b0c2e89d8c15fdf9907b0ccc1b878654aa5f96f01c09ece127823169414c6315948e5d9eed61fdc290b9a68bc52fb8068d8f1d1cbd990f9b609b3b04cbc9a34a33038c03e93b3c8a60b11bdf0e7e44787180d0f6f0980f1bbc68c68453e7c731253aed36e74403faa0296157188ff095dd49ea12add9935317c7c14712996b95cc27f60dfb4c3211f0876aae32159987e547c3a28460f2654a9a65fc4b2246df5d1020781d2fce2f70db5ed75b3d8c98f6b7605356d6d40f754538bb69c3bc2a637cd36de9a71367b05c9061128ea0977b6a8f252c2d279a781957d8e094c847ef4c859af6fc26cc30b7ee525abc480b24285879118a1f888ec4261e3cd95bbb5212606bc3d8522300e73e215c89b1615fc80b25cf2aac2148b9ebba8cf7dca2612323b1770cd10e203b85571659283ca9a258f43859d90386c607c989437b0e11d31a81d133465f80d8ba0b74a295fbbbb46c6bfcc0ee44038e2cd146f4b0e3aaccc15762288048702bd638c13ec6b855d94eb34373363b53b7d1c5aba3be19e1de28de52869537203b1ef95e4c41dba7d7d1686aa05c3651522fad928ae6fdfdd00cc9355965bec0a61fe06e2b0d08685622def80b05a49305ad402c3c4e5a94d9c541933a1ed8504855d007d9d3a878149df72bb691a279260cfebca6d1235f3bbe156a867408d078b36273a4c5560b74bbbdae03d0d4464d5d46dd3a4d62708b84abffbb1a3ea066f2bf6d3c1945c070927f6ac765dde22ccc42215efd7ee900922a17df16c8d052ce0b52c682996543ff36b80cc906aecfc0e4e941e16467aaea1846990b1e60155c09bfa983fd0604329cec26e53083a57981ea1ab83805f6734429ef4aadd063b1cdfac05fbdf7ce3c20399858e306d1cd5dd59b67b68350166febf7fe009033b91d89c447538cbf790dd4fbb06793697ec1ed9fc286759a7007ab780441e03969608ddb9526327a0944fcf3282b2912389d5be8db2d1acefa8a01a2364840ddc2329d1c62b1f8b8df56b19481737e636c212aac5b8401ebb3e9f778173fb009008151a31ec38411c177059b49daa9d7980eef2b296e2b77f8ab3770eee63087e1dd2c34352a17250e384d2461b4a1269cba3875e6787a3660576d422741f083ef46a0272b0adcd6b21e7c60c1e56c44bbf57a40e02d4f8c7ef63fcd24e0c0f39bb5adcd3b33d095c09049bd935c7c665727527ed94239d4dbab90a071e5d04e52cc1a30ef0f4652798ecbc7642f041c76bbd38ce7bf5d69a5d13ae1b09c808bf301bb9a4606a269c334d83c7ddc99c9d45bf500188fdb8b3612d44e58927086e6efded6250adb5b5ecee9f8b6a97ddba1b7256a14d3f4a1243c65d48e02f02ecf9bfda29f43f3e6e356caff1101f1e3e42bb3911e5f0946e969d55a250d1052e404e9b429bd4fadcfd83956c600498adb9d8d6b49715f25f4884e13cb9a20e13d9b8560582630938920f842f8d56c3464b6cbe87cce0197ba65756c0e8a100e3bb4d69af08d4c93aa19929969ef7294ffd4e3c786965e0c9bd4541ad733c03708f99a98bf70929ad3b227c3fc71b9677f342496fd98936f773f9cdf51e950350d8d4f1240dcae28db650d8b81f7908da5e4e7dce4a52d92b0a5ce229264c086b12f0c64e902262ef64039f19af4044e87b88dd8def810009225f33c9a31009e881257228c1136dfd5e08a71625a65fb5abb0d4f5c96d85bf3d7b4aab88800992e26c2e0359f324de8e878819d7565355e9926769dc561003937792af8e000567ecd84c6a098caf94a4156e44b5be75b09645a8693aaba102d82da41a5266019f016c274d2d93fdbea11fbda9de89d722dff3a3e7a5b5c4f643a858f1dab6082dce013e4d06669d6b24940b24044d95c3e1d78bc78c0ccb170d6c07037e450768fd7c5548eec2b4ac80fc064efba6c387c107d4046cee634578153af28d250e714aed569213ef714993baf43a782a1810dd6b9050b946f32a7b2a8c7d03b509725b080eaf3b25feae4e5ad273dcdf3874a17e680a3224066139da77352b540e2b6040d74db5f278f85e4970992119b5949e3b09619bf09adb303d8bbede7103a1dc8ce59528166005dd3a14345b24e3ac033b2c946738c2c06d238dfe8ac504a4d5fde9dbcb8976687fbf3613844c36c64c055054f20b296fd5f3e9af73740c69c51bed5334e08a582e2cec51bbcb2ac853dceac4f95a58c6bfd912152def0ead27da17a4d7cf83205dbab576dc83db88ed075a96fd948bd745076a4bf0e10fc30ac71b64320e71b3749a4b34b8f06850c0933958a3d090698b41dd1ecea30cc9da40c78086944a256477f696e135ee3050ee36a89dd52ba4fdef5d48e3930d2e70eaabf163a74862d97b5be646d60408672a9f57b29c9e605ad97405326703a1494edb013295443bcfb740ccb661b6af81d3934ee12e3e957fbd8c8d91430f1ce3d777183cf85cad5af06da96391a861db392fee4d25b363b5d64e2a077d064bf0f80250caaad11f027fb2d1d0766693642d1eaf733aecbb3d175a7743b6018ef5f6d8308190718542bc07ba5a6c950a3695712585766c52b517648ec329018dfbf7f58adf943a914c0cc9fb8ba0e1f2099d7cb9dda505b04b75b6ce6efc0b3fa7f13a11db45913f0327f67da9c5df200e257d78118081d239f740098f9301b2d14d421f9361ecef992ffaf3de6b647be5dbde8c3a8815aed436059e8ce6050e148b2d289dea54783ba88007f203655f4baa2c14209cc89d1c3835b2989207f59079d80eaed07c434007ff17b9710e2805e1f295d2a6514adeac82af24630a0f7fd3092f1d302e3ac94f9e57f2ed8b1c9c62bfdef0e2794872b2a8d8d63e05fc77fc3e50a95537f4e22ff447495138aa85594707a02bb227343449503f230fb1fc4a621c2f881f5d24da6de9393eee395e0aaed8cc945bba9ed916ed0eb60aaf83ff1ff5a07bf2eb0a3735a69474deffe64a124cefd516aac3f484bc70b8088f9a3e29583b68c42d05c91edbd38bc5fe9fe5c0384059fe4667b70300b2700bb21ec606a1c8d716d53f9d4cf129b352a0a2b9b8f16b7a61e9e932abf93cbf034f1f4fa20d4e40f42c0eb5fc8d5873b38a853d1171c6cc606cbd7e645d6f270f2df4f7e7f581a97c9d411b20235c5ea610ca6475ce2acf82227dc05c03d2fe0ad5dd5a4199bfd2534cb2a2f9186a53008a88a2e987e6615e03ec0ba1dcea680791c046efa53fe21b57960a9fd1340bb9e76d6941d4a826bd34890003f40a1d089608a21b82372e78d0546e5be613f4cd5c5993608246c9736e25fc966e4658081a675c04234751a3624975ab274b8897aa631e4809dc3de045834ff9b9d85907ef065c9bb7c8c94e7d7d8dffc911c012e447821867967cd5fc93fadde857b00bd5543f3fa5cd859c610782741fcad41526ac712c39f5cc870ce4acbbe04a15083e9b5896775dd99d7857460920e1f997ff96915ff932cabcb42bf0c414132f0a3fe6f97829662146e88249116d4e17d4a3d55175bd04a863b227a614e657d00fd82b01d95df43f86993b546a8eba21c85aacc5bb4455e2acc020b59e494dc20125f1361bbd0ed68849c355e4a1e20fa01f9b2a8bba5d03c00f4de8565551230242ac5057be9c197ecbcbab4a5a2182bc85cda07ba8308ca8ca46298520f3a70b8b8d5dcc5b46cccb858c8ca4cd56c5ad0d2b3b01c7665dbe232c9afcd540d0026841385a942f7c345fd365036c174b82b0664033251de8437283c081486b3a0b4ebea590eb3ae1f30529dd314c2763bf2760dec4c18c8081bf6691cd41078802375829774c3cd762159537d76e89f055d64aa407b2e22408a010a3e36b7bb20e27df86a60b58d0434240e7cb993c060471a420aec8d041e701f42480cc6bb30766c47e8eca6a57a8c2ffd2cb876eb929b6791dbbebfd4b7c07064025b57a0f00547ec60f61885f057ce022f79e4f35dfe8957324c664f4528ea327abb086680f27e28d83486ddc5985da43f6817499c5e4efce177b7de1d01994d26c9be4bb037d467957652df480b6ed99a2b3e55b8514afe8d64b15e644b4c8d033b9a8820539cdf54f596175728c209be30cee6ab8384996cfb3bc5abfce26ac61506c930b928db3c79c1fc9d8bd3e023c218db4b90ed650a1b805d357462acd6aad269401d658450ac08b05a5bd3b17268c584ccd33471a3add38f7bbd00f762912c28c086ea8ecd3e5733d81619bd6ff766a5b3153ad07a821255e4c91ac9b1842184a03ad23f6f06963202dcc705724e184d0573bc144617b9514732491d66fc39d3a0262b60be9ccc3da9cef573072aaa7434707b63718b934c48968691cd01687920276df2337525fda24d08b94c9a05df1d784580480237e32704b4c1ec879fb0e07365f8c250ec5ddbada48b5d843abbac0c16a818a4ddda011713129cb12a2ed07de2c6c1fd798b54e46d04c5e76587d2fa20c29fb01bc8b4113602dcb51081d06d6bd1b20fc0fffa05c15a4db92054224108cd5512a68328caa9e1dc79027db06cdf0979a5a5af24fa892d6dce9de2f40f7816262e1e7621829db4af6240a0a056f822b206f8a5746f53c02103ccf607d43b8a668b42ae2e1068c9eee8879ad02ea4afdf9acdfb04dfc954afcc00f9b108b9d1efffa879a5b4569c34829789a09f44bb16d7efc258ed421aca3ecca5e9e0ca47672f4086376555cc8f0c58cc00b232de3f1a57bd1d089510b278632247f307c5b5fbe8499b38df3aae93436ea0eebeb7831bb974a65f4bc9ee56439992a6a1c175d334eb683052638ca51d3f30cd0325256ad85a036d3dc5e31cfaa1abf9ab1ad6253a42cf7645722b57b40680af42de83b46a4098300e3bf38e94e2c1ea02f52a35e03c5bff4ad38dcc2170d041d4daaca34dc6549840258c0d71ca08d6515ce3718092b7d86ce98fc9ffb4a049e2256f604f1926110d973efdf5ebd7471ee40520b582e032b8a88121be1760b18e6cc5591d8f2a1a55d09bc1acd05bc5574ac93455ea1f2bff80249df73a006ba32743d5af75a91771244cc1239e606177daa8f656698d6123e2e9aa2f0890b67e7ac7a1692414463cd52663159560ce57762adb0a0103ad3800e90b6ffbc0b73fd380f9ead309a7c6fa1688f913260782a4e74d2f6e0c225a718eadb120b040c031ff1080a842eab69f4c4bb9e85fdaf60f21b9ac878c48183e9337973b2004533b20f15827a1db2111b976b4fe4c66ee5d5a1ea6f742099637abeff7f7606b3b14228081544822e647bcd85323d767f1b05928e048dda4677cf5c1aa8e20ea1ee738db70e3ae83aaddf34a908607dbba466b619446a8c2b8a0e00786fe60dbc10cc1d4a8e9009630fa9345621cdac1ce2ae19962a5b76c9fae124de576203454067b1656d9de459bd3ad28e37ec432dade7cfbdf5af08b0c767865a97fd04c92e7c01569d8c2f1cee5f9cd1d3a883e062379831f18d69be629beb650207076c83d70bdd9a37db1157920c296f8a553f5aa518ce60ef3d3b45c6da8bbbe7062c947b4a7196acf0424d55969968f9b5dccaaf25de1badaa2badf2068bacc80531b08deee83ad75065fcba23e582a74202766a948fde92d8a038bd82a412a40accade247bb29cce1d0c11965822da15ba6b084c1ab0fd43c6d74004709569a05429df3fc62550229e158a3f2d1116dd46d7e1b961f52c657391c525ce109f40dda9f96b6baad860d08d3ae49ecfcbb09e243ef8d20f96d368bbcb4ba0f02f705c4c136be1cc85166745043d0f14583ba40e93d6348cfe8ceecb2e949829ec0080f08b6f0e6ef6032ee85ca980299ac91c5242ce21f6f5bcf59be9a8806cbba0a5ffaf9b8964e545bf0310a22ccb66c03a7c459939e57d541876b2e69a539110f24763df755bf7842be8c4d9b6e2d14fd5d61ac9301cb54624a100f5379245e0536e3a0a103f5eed997b0364bcaf8acfc4fd9f52d91a7c275f65e1eba6c38d80960c0de2f71aaebd3fbe60f24dd685306aee19074f41ecc32967442c8cbc6c80930c758f07f8776aab6eb95da469d197a9d35f1884beca7456e1d5726b7a7910cc0e80ce21ff5bda169f0140c01ebf466bd57939f0d8170a84b013746df0cb40556fc4f0c166e6e59c502e12b6101d98d044f3214e662c61a74a5d824f7b28804b54b2f001afcdb292cb3d3c5aa8437d1fa652dc3a297888d2eec9c2fd11c8706a209cb8cd33f821c6c0b297e3d15154941df6e414e118dc4f92517dca0fb8f0ee17f7c3bed9272ef85dc85ddb8da0f3ca30d7535ea6c3d3fac3d21f559b8b30ad0ee62f20647834f1bb23f497728b09be96a1adacb30294b32d5bece711ce10a160f25dded351ce65af8e6b6524dca7d153f97f6ef871df52a729e72ab9b4f0a4297663e1ead0fcb1a8ae0e73151cf02a54158ce9c080fb3b50c08b452534e05cc6bb4ac0792c0671f860187da86970a1516852c20062cb8696e9ca886a95108c2d7a4ea362b1ab631085f365cf045dc638378556c1cde21b3d1048d0d9ad903646664c59a37798eee63a06270cf09209b7f5bda6c8159f636040e4ab8574e000a8a9dfd89ddafb55f30fcebd51b7fab90bcb12ddf0c304f117d8f50381a2704457b6067208b157c421680e2901257dfc3f779cedddd6f3ac87bc8558aca2b02e165718a9402b01500bf7b7ab604e5f8cd1369bd7e5e945e0e9f7837745b8b0b148f179ec72c14b078ba8e98fc540845c95f4aceeb81f05c93ae52728d0c63079fe9a3841a446eb8d1e1f8e524145281b6159acc64c64d0080bc55cacd02880a255c72de1aecd1713418c43c98384a120ba6313dc79899caa8439153b794100dd41c0de2e792e46ba2387abbcda043e9cb179519dc3fbb06537b133f0ad0f5040e75d00bce4ef17ed345eea797cb443624ae3651c4e60339133028b34108e1047006ed9a19e40c915e11d7846e36a6e76b9e88186b667089fc5913c52d70ce08ea9474e615ae20655a6188389873dbd5992f47ba161de2522c85e3bd7f2db00e2d14caa6075d328e4c34af757d71d8d9e3f9e5c46e4b1101558c64e3a234d70b27adce847e3cc04dd199df547fe47414300485f517e1e49f386b40dca8c1f607109a0e0bc1e257ca83fa4624b7ee4a4f92df593d09999e9e3ded2536c9244207f3e85ce213b0674be71025fc9c3d74ae277f9b72983db3ad0d3409b41b47a3009d496ace6c6f04a8b212bd66afc0d91cf309468a18fba0dd5c55cf8c7581e404ba84e4dfacb468fcb4c3dbafa1f289eca1fe938d82e388609c441bdde746b4033da3fae5d0269a293b8a6b71d4fac4c9b2c081291bee4751d1a883e908644d00c3508642e62135d4c025b8dae84febe7e17056288cd7021e6751549a9fb08006354767477f9fe9932839d3a676f90b47547de012f72ff6a22e27f10a9c4dac0cdcc015cf4e07f8ace464f97a0da9cc2029fd0df8b7d44b1d4de719bae21cf90b275b3aad58e5eb3ae58aeab9eb4ae69f94780d43ec81520274334a67f3896509bd6ca754208a00ea24be9d96c2e03fc071e294d5f49343c8f706a78d9a4e8c0cfd96fff6f1a0f579c8522ebb4147dededeab2a7fbb97765fd88ad7621eb1c60d968e8c7c3e26dd48550552f778406fc2aca22244162fcf6ffa5a0e5e012def019cae80e9187162b0ca50047003a9a7edae2dd680e95a359c7e598903920f3c06ca6e3fbaa6fff3f62cb9084b09f2366d9662381c6edd135a189d565d050e5304fc0908f5b02db1f4cfab6e2b164c76fed44002fea6a3fe5420af03b5adfa3001c2bff662e4ed5344d5d4f38ac9c030a7f26a2bae87332badbd27c43e0a1a20097984285dc7e8362ad4081e778220fb2c80968a078da33abb0e0a5d0b2096a40f9c70ca0ba446f6ca2f70f28359fe07bed8b7dab05eb3752eaaaba6b801713801ef8cff0a067361a8234a60564a45f2054b522552488b1073ac440873abeab80f8c13971c91c8bff0b928c82240b2f4be06d1118b6cd325f1e9a4b189b3144c0b0d3d6350a3d69a2743ef9acba85084a60478b2f87d7a376558d6e371ac1d0b0f04af19949252b25d7ebf7cd3184eea7962cf4df8c00ac15dfd9d876ebd340f0c7b2be862a351a169828b16a14c7c5ba761c8caa05a22df300dd88f6135c90602753d63636f324a86d6d5b17234788614df61b49902c6d644e0e57987458fb30a2881d27850bd1c86d49d703e5848a2d61350bdc0146db922cc34707635d82209b15b991b096598ecc57199505c78d92c133fb873383715c4756824dbfd5af107acef31fddaa61d3a1c856a01a4e1b5a14f3f44c70e708edbec78a788a61b7a0bfe35f5ac0ec50046b8ca21b903a37f95960261abcb91c2d409754047ad34aa069f039ced41ae759d04b48ad518c8a2dc8582cc1a4c2fbcbe3d3e7174d5809b02b82816103098559a65dca4e51da1ff83d1fff228872fa053b687f8f8091f09092088070611b80ed99ea7f2605eb720197bf2dd4dd84d914a7bcdb2d21c2110044e23eefe615ab17413d7b5702df3456c9bb3e5d7f852a53ca4acd4c34bab720a27b6035b61afaf074ffcdd8e8c469c709056b53617b346db56480186e3b0f6019f306628107749b36df531fb5bbc2e4de966b5450b24b390985de95b055a2400d79c1a010925fe97ec383fd3945dea16251828183726a5dcfb7e9054ca49ef02b97ff2a53d942399c3e7009b712ade721998ac3ec21bf0cc285bf8fb0a00d70e68b43c63413517a5e7738164c08e463d5b5bb483720b0f2b8c8441cc7a910b0cd615330caf6d258874266e68378e1e1d922d1913e90bd6a525e56244828bec0913d11cfd7f162ee4c3ed0e5b53b5fdaaebfc6c3b6e8a8c5486584d9e0997c805fb64a38557babcf44c33e183389a95a7e95a71337c4d059fe4a52aee86d614030ffea5519511a112c1d2a51546dcdf3b68edcd7674f22f430e800a1d090f1d0cd76920f0df8739d47de74df3e0e1fab8de5b8a1be1c169c055b02777b916700f3b124de21b4eff3c15f594d3a50a3c72c407aa76793ca9774a20ea2a8731360b726bcf14145e1b559d5ed8ac79b5995ed688d46bf9b07df8c9cdf1ec427e7708d39bceebe17dbc4ee0ec005b02de6982b8749b4835afa42bd974f0fd7c246c079e41ea92eeaccf41f8441e41726ffbe13dd5fd54a138adf77ef2972776766d02b504349fb4b10f2d6a4f697e9c235684f5046aec3364588c75656f21e5b9830740eb21396941b2d0165b60d6baecaac5e9e1c4c8600407931dca87c4040b70095bf46f36b74aacb9c8655e067ce99834d3e0b6d481d8c91e00cab693eb12290b92f0e68b84d65cb39ff10fffa228d88dd6a0097993a2686c4d98ecb512fd8208ba4d5ab0458ca219d353fe67501b45552fb34333cdb02be5eb9a5af62ff22202f6a662b9797e5f49b06857763390c25ea50543569a48ecee0fd5d65e59021a0c2ccba2970eefd31489cad0ba5c42b67319ecd1e73edec4cc1e152cf2a45c640289c8c2012d317f4d81f620257aa9e983b8baca101c251d03eb22507b90db5c091127808484c167a36989a7eac59908bdba517e068fae0387a1fa2106ec500a0e3592eb1e3d9a4ec3d92679508b29de19ee959aa4e49d9ad46800ddcbd9a2b90ee06a440411dd7380e71fa3d5869e463e7d59f7de6f246d6d22de15b81a2a2b04e4a247523f3afca2064dfb54fb2968f9166486f1e3155b9942e667a6444a9d0ea670dcadefdd6f677408b158688acc751d8b0dd98a799c8de24db568d87bb50354ecc74d84b2f95c2016649c12d784aafd71511f94d39d513d237d144dfde3048d45f322573c26c20b92c1332a2c0d4cf899667ca6261bf17424bed4191290089f7f1a61c609b4837e3f5f67eb899d1c2a93b37540d3f2cfb7b611a012b5060522433ba9b17794518f9b181b32ba1d51623f8f7e262770ba70b574f84996e70521aaf642e6dc99e269e660adb4af2ae99a0c2b9ad4ebcba0727f2e92b222f9036d361419375775c98a3cba6c0e7a370bfb0a7d25a8ec57ffa9b99c782d57a802d030b10069ac02fc5524492697021591fc47c5520687a83cbd5383ca01ad2b0e1411ac7f71b3efb43dab742c49cbbdf3af1cd53c4c601fc6a9ab18e7b68e95039045cf0813d16e4937f991fbc6c29346c0e283db6cb64254471ce77efd45660119b33e15b378adaf43ede5e40accb931825283a1a798f601f3d9bf4bffa1db0adf8ed57aaabfa0c8c401ddbae822772d4ce0fdbb4ddb3bc76f2559dae01a6d06f7f44b67969b175c9a8268cd68ab118574edc4eb26096663481c365a469847d3ef2c7d746416a70e978c54b22f1349b8d04b6e11e29e3e7b89fe9a014516410a4b189ea9aea6aa94d73c8696c97fd035347fb0ee7312d281a836f8afd838bc0f94d16d4e63ce4fc3af67834993bd5e1227052885eb48ae7a9cb11c00ae87850b72e5124ab2f57485e183a796109f940ba73df4160cc8c2c7f5edc667511637015e3ee174876d3d46a4c3b5fec9ed731d996ad6a3c79fe3a4ec38e01f761b05004cdc80db5b40f822624088eb2427badc07d5862e0ad84b25ec1777044be12d0341a5531f0d6b3701c4dfeedebf925d04e2d6bcd485ac30de601408d53213ee0c035dd2d27d90ad1a2b3f855910880940ab5ada4296c8747d0e0cf6dc2b18990158ce6a6b71b01ac3958537c64046127e5126cfc194d289647b67886972f0460bd5fc9bc129c860b1c53607ec88c4137fe9abdfc0fb39a658005574ef96f3e9006c9d8fc80d3d8102715222ed5afffb5a2c3efec115bab0a1db435a1781ea95053d38a40dba1262c2ff97f6324e1918f90d9f4edc61b7905b37fb28980f72f906f80c3dc3e410cc5875007f583e40f902d8bd91352a09c80eb1b90cd47b8206012b47e7865249cef93597a28d2014d822766335cebb1b3e8d460a68cee892310b6b19072e407340078cb81f3a34f11e3c138cc40ba04c2bc0e849504eb3adb70c449ea5e82b0ff80c23f39baf846e03f939a6b64ce279b02db0386af78382d102eda9a04c5e63d28a781a688fef567bc20fbe2f447eaa48be284b183ede8bd201cd054a0bc0c3449f9e7b4f89fc7c40a3efdb6768923078d4fa117df501372f03c2110d224168a9d9c548f67923005d54ddb7221963737b3201c9973468ba380f60295e77a09a484ee6f25983b855d9b0f56155a7a7c76203d2372727665b900267cbb993a86301026ae552da5b626855ba8cd43660ac68043553c112b5330604cc13a9d8817aa1cea754f1b4e41282ef34c88410b97e7ef3346ea10a9cd2eb0febabda419b6381c6ba02f2b70342a1782415a646e89068c6132e4a6ea760c506bf4b438149448ff6c1a5f0bdfb13b088dcef97c4fc2cd7005e05e0ebedc8f40f3201571a0760819cc49fde9e9b2c17a70403e7804c5aa7a26d7e429c90a7ec0023d238cbd6ebf643f6ba8579fceb7736cfad4bdf0255c2863b734b5542358d0fa8240b2ec2f4095c50b426e3d6089e050499789246847969e1de8bcc6f1dd507a61ab3b49bc335839aa7826ca31eaac59c51e0f1e64f45d731c8158548541b0a2d6c953d815bf11c0c0ead830e3c3a90daee59e782ac5c92bae55a900895e30e97623f9e1a5cea166ef5686659659de4f8378002fcb749fb99f7d603c3fabd0319c6a7233955bc3fe2fe20b05e30a3934519bc2f51288142c310345ff38cbe0bb8231bd9b15d7cf9d7ef5c489a792b643b8b37d65e303e6e82e845615d0c0006550fac5a6a46437af2425ea8157542d1f9105accea6fd0c6964d05bd58082505ba357684506d7ad6bcfb284732a75152e48e75d851fc5714a569e2afe6b37b0dc0805101bb6626797d6f11a182203e97869dbe7eab119b8e40d5ed778deb52095b38e4953fafa8c16e1070b7082dba6d7c5334383d5e27c78f5bf4c891842a0962c843e2745a3aad08fb14d59f5887b4d331fdb8dc35a579eea801d0c8929400b44612ed9183bf83469ced35c1360035322e7e19712c9b83efba7a2b6741620a607b9ea69a688054da29e660d7966df21ca7b877ed69aa02a9886ddbef2262034713798501fb6a453e02f1e43e231cd1198d20dc98cd6090c7dc15327a03b20d97b3a6a35446a8d59e6aacfcb83d3827357db853aabb4c7ef3db1d5d01810e057af8152ac91f0292bbc8f0910b266030c26f5b0c4c7b7d2d2278d1c6b4f1720a674ab539cab5e0f3fb85bcdf8589bbb85a5da9f4f7be6c0ec3858c26c2a39b08def3ba27ab3b494b85654aabcab989c21f5a1579b8c4feff0a83ff19066d600ce9fdbd348c5a4d894922541ee53feb50e204e74ba19bf53674d564c52198da07caec6736d1a008c07474a8cf47fa6c8743270bea64cff1b13e223b62502de20829e60ecb0b44693359fdb7b779f6f13596468481c0d3277505fc8a98a9ca540708405e8bd2f9d0d6eeb81f2398a640fa0298e638f678ae350547ac39f15eeb0d5d17b6513d30fd34f07756d748c283da5ef9621782596cd3efa2c3d01374d704ff1231959cdc75dff16abccef1fbf52b5e14aa1fc96417a5245a30282cabb40ad8b2b2985a3b9c060e8c9598f949dcaa6fc417c3dd0a95b19f5adb09f524a20984a64b7c3a2a9bd1a1d3a014ed4810511c3f1262e8145ba8611827a9c31d65011abac18f1375f6c14a9db3beb08e36d781bc7771f9aaf08b5044ffff57991a08c41cab900ad14a54e97d0df2a9f88660812bd98bfb5035660c2978512aa1a20cc5c904841c882ce19a938d1ff56dbe768b39b80ff773f851a0e381b32a71740b70fcff8179a2b132071cd63aa87b0b343eb7616083d2cc6c9cd7bf434b119907be63b5ade54c6294ade642b764a61f8443d0f3ae01ea8711f3c23125d290f205ee5f952583046ef9b062a1c699f016e597fc4e5556105c6a1ba5fe5ff6a26b07bb5f370c470a73f20098c1cca329dd5aff1fe81ea7acc4ba79a8267df79cb601acb922a9d86086b165fe4489645bb062faae406ea5fb6c405ab54fd3ff8c3b0c2e778d2c51a5b6e007382ac9b0ea13ec1f4a139e5743402bd8dabc6ca9b6910de63e963355a813a6e28349f0b8320d9b5a8bf0aed877984fd64bef28724c51046f8cea6014cc9cd03d3df6c91ab52fe6a395962d05be76e25173e7047f45830d07bfa9a5c22ed2a9ce2cb970055f57be48893a628f147fa7ffddcc225663e101e6c5a99534b01587fa1f9cb896fb73843a5a01d9ef721a2054d956b3c2042709d7c8da14c97cffaa1e0b1746db47541acb905f680da2a6d0c452201921462a061038959021bc5313079cc4f497d3b4961d938337a755f878262fa0a03b1a50040adfeea68bf4ed24dc6ed78113a59c293f6999e6cc073a8f59b0488bb89421029a24e1839c29c6ca1bd471d82eae236467ae0e20317b0636d36e12f3fb41f901 false -check_ring_signature b76c5fa70da5302765f86ad7d0e1540a7459f1a38ee2aaf8280344133d33e76f 3d427c219d5045f4254dcc007657c39828aa3bb85eea35e0c061e3d539e810c9 6 b7c48f046847430188ffeb89adb6595745cc1fa5cbb80e54c99a99018a676942 2ca95f9b982ce0971826b8fbede352733a7838e2a51a1fd4fca7323fff5f5d0b 29fa5b9df05850aea84ebd32a5a886d3f94c96960458c26199425589a15b8ef4 c524e2d466e16fa39859d5710a694b0efdcf09c35d3a2a7a96d86c0b6459252c 44535dcb635ee511469c670ac42e0e3b34e0955033650c4b75423cd554ae8d81 1e5377c2130a2f148bc3c54c52f55ab32f126bb3442f253e72503c7a71e3d275 b11486ebc5560cd93de2fbccaafbda2badb9848c42ec1aa7a366f65a8fe1a6066ad50aab6f28ccb6b1c62492d3dffc7af1e3eb78c1e59614f4af75be3def762c4324424b4798a722d9651bfa5da1cbc3f9b05564c84f2c669551a5e1bebfc604832a27443d14436f755e6fa6cdbbf64ded6660318cdb8dafcbe57048692941009baaf953a1e087aa3db0955de8beb887595c467f972b8b116fea45b4c3de1301ccd94df636990de7564645fbf2cf596f328735147d5e325754a1ebccbdac3e01ea4ef0408148f3515845576a71252ca2662026b94bdf66e3c10d2c7f0c757c0d86ab7fbba8cb74222c46fdd67d13848f8c86e7307b672004f36b11c5ff9fc303c64db3deb1b37892580a9f5fe250d2f345ea2f1c1f4934e0ca5e4ea758e74803ad36804c36fbd7fb95f8912e7bb71b07d4ebebcd516defa968d573815e4d3106fba851575d40e2d2fd1be6031dcef8a905f80f0403a314120b6d25feda6cf70ecba4531d07476a32ddf75cac9fcfc42a7e12287c012c6b56b22001f4a252d10e false -check_ring_signature f66628aa258991283d9cbd2863f6696d51d867dfdaa6dd307215a7ed011f92d0 193b442a24d8a4cd6e8b54ea05679caa6e61b52822a220c6ddb3751d7b66d909 1 cdc92b67421401ddc76050fa294a60467cfaa3662e194c013a7bd8fb3f0bd760 1b0be1f127b683e6752877e64e62a9f45dcc0846f5d7e31defa2d3e19c13920b6c71b42d1a01f746506240bb2e746b31591bd9088904e77f66119f97bf12d906 true -check_ring_signature 0443054ecfc3444c408e653d099a66e01c22591b9af550a1b664f2036974bc64 411b3882dd6cd9e8c7867debe2363bbc8ef05a46bbac2f1768fbe2bb08d1602b 121 55909b067b7acf2438166d88bc8121c7f8948b378c177516b4c41c08ab29a16e 69fddbb86bd2d4adaa648dd49d92e0321c60c04f25db8d1cb9b00b92770dfe90 fcb375f4e7203afd70034accfe1f4769135f57987a1fa6af4b233c8f1d879631 9d64da90d656fce3bda87c522fd5669da36e929f65919ff2a6871b07d09743ae 8709bc33905a62248b80f252efc397f7c6cda0a5c7acf5764ec7410737d94dab 8e384353ebcc6303804ad40aad5a99867da5ebd599ea45670ac4a1f77353dfea 88a487b4fbdde4fcfe1145ab14a8c7cb4c97ba7118c9c4275749cbe40ce24642 735584857c9d7dcfe0119f592e198a6daf21211f53a52eca0da6b779299afda9 eef56b5dd752e8a0a10aac68489948fd07ee351b7c1f516a863c434aec85545f 2381dd0ef6c30ae973f454407be976113d19e7c7f7cb27ea0037efe4da6e513b f819ad5260c14357b9056e19a538ae596ff0c8bb05ff8a1b25362b4bfcc75176 aefe1d7b47fab04811eb604ff64316d3a8e432c8dd381084cca31b4a7fce784b 3be8f078e049fd7b530f9df274d7bfb8c07945fbceee68f36459d019bd447615 e2d67b524bf44504f5a068e354f2916352b66e2b9f7877f4781842455fe41fb6 c81c389b4050d272f1b5bd543d6a270b3b8e8860d66fa3c123293a8cc5efa73c 3b66fac49b004ddfe71172761fb238cea74dcbf3a5ca38ea959dfde2579e9d22 f6af722ce23cb0c5510a073ab7616fbd84d5696dc0747e26bec1b4905019f3f7 d4fb218b9962b1764b05acf3bcb1da382206d71d8fdd546e35b32972952441a2 92d962cdcb95e4f80a25dd35cf85cf4a0baa93354d578131891bfb8271fb98b8 baed46768a9fbacf45d6e3e428698c1b39d1c27512c9ac5b2c33393880fd66a0 b553bc7b380161af1b180faeca126b4fc87e087c68a41edf78bcf5e26beda1f5 31e99b9cb775a586a89b383880b593dfd6a4b4ff8be3da7e70eae30691a34f06 892dd908bd7f30b8446ab998858001e8abf78c0de0eca8761f44e13fa32a6b93 34ed757eeb440082eff2c402caf89494f900e52743d2a28284383c6485bc96ea 03fa9cbc5b21ecbc78d357ad7113bb60d80c5327aea7deb987ef6f8c36bc7881 49ec88cd8fcbd7a1f4491bd0373d00351e2a47491b99a34fa5850fae99f003da 34ab88ec7f9bc8b55fb42abdffcf4764a309094481bdcbc8a3703333386b8df5 840acad5816e6b9f7d15859635b118abe8171f111ca15df84afdc1640035d682 39f69be5e4057f1722b8d1bb22432f55d062872aa774005e26755098780012df d8cd8d438f7367d7f395c1468375a1715d8a23bfe55383062f56e339bec9f9e2 a168c4fcbb643d3d8d7f1f759fda60d22a92767bf59aeb1de1ac56d6fcc7d77a 868299e3a7c0e6b3c009f15f5fa06f25cf2ebf0e8a5c839db6ecfb6533369213 379bad8218076aa09a6176e604ecdde9d6c5590c85655fc7fa90ac077a67d697 9052afa89e0f95427ebc9548235089dac595d0f100bd59e2809bd95ae0a8e803 ea96618fd6647e0c141d0b06eb808960ea2031e44a84ac7c06e3ee1523cccd04 019a3248bb6f6ef81bd33c3ea246c794176aa57c6ee3e3f63819884a6c0cf698 ed50ddf386fd9d34500f8381c4d4b97bc63cf109f4029aa6e7d4b8db28f088a6 9a753434d3c2daf70cf752077493aa01e95fc984a617d32cf72712a95a66ec42 234515869b80731100c180f409604f4cc2c41848ea812fe43fb19cc0dae34a1b 00b7bd3590114474c70707ae3b7fb97df6b9b8fee97667dcab98a7c98af39b70 584a477e30bce655e506c696bb1a06958cf3ece718f4a3dc1a8d3ba99ccbcbda c3f84535b7ceddcce4c44b94994bfa9e98270d40a34928cf044b78851caf8771 047db5f01b8a5ead8558aaeedac32b46c0122005b163a2aa4ae4f33ddfcc2217 7269a29c4d7fe5c86da657561320f4031a5ae8bd24e36ec70b34d5102821d5fd e5a39bfb9956c12df687839477ff5253ea28c23a6038bd180d821c08a2b0e032 479af578ca302de352a5557f30d508dd351c2d7e48aab82fce26b73144de5237 e04f5fccca7b224408bedb13c88dd318514fc8f58ce0b7595b94643ac3c9a04b dd3c26ff0e936824ce0135b9d8fd3ed02a3b23683504b7b604bff55433e39e6b 89caeb3fc4173c72c7b07ba0516a974ac3dd0781d50090518f796e9538a7dbe1 4b76c3096691d3385dd8c47bc659971796a1749b637b5e8b8025db34a8d4e3a5 2e28a9056161566130bca6f930970a9f28bad80f7b31c3c7ceb2251e92e78c0e 4b005a03754f845e66da462cb5a723e9b4a954ddd81cf1870846794c2128cbc9 b47234eef621ab47841ee62590b3ae3df2c27a8f43c24b0fc641f805192ce9a9 fae3ac01b70638850697745ebcc2dedc5e7c50ffccd9e8a0e04f6ee70262877e 4c19a584dddb42a38d0b8e753ff973f86016745e42b135cf1de3da9fd8363536 42e2c474d6cccd703e86874d0aaa078db760bdb302f08af7781cca03be9615bd 9f4dc8d482884f9fcf8b40768d805785e852548f52f646ba892fc9104490b8bf e43adeb094ee08384cf0e81174049339a32ceac5ed2f9965dbc8071d96ff991a 18833a602131f5cc01510eaa0a0c40c338582753fb149158fa9997e403ef7bc6 ced865e82445857ad6f319a9f1807cade1fd2973705add2f4c6b655586a4e0ce e6bbd67c5c8df864f74906f24872a3e6382b7ff5651f48630808a90196db7f8f a7afdca5ee5e0b5df3e21933f636f8ca1b52471aef11ff67af1c69b88ccd4adb 576aed87b372d1a5db55d7404d9b81069c3cfd84bc787b43f4b684c17aa38fc9 3619bec8712e9f227a66ca6e2b6954d4d5d399195e7e086faad091d166e91e04 ccf0b3821acf830a920ba9da89f26db1fc5f9df5ca9ee007d37835ffdd3d31da f1f148ebef60121cfeebb62c6ab9cf4f906601f2f0bc272997a691c5b845f78b d01e44887e833c63b3f8143b3022e73f1721f7ced698bdd7dd4911f827cd504b d9c9a6bdf693a18d9a9f675cdbd46c182717ceb8e84b689032056b191a54f820 8b32eae1d14e50950d2a5fa4059daf51b8a0aaa54e6e8cece229a5451cbdc5f9 2226649d865a3c121060a1f5ae16986ead178b16a03d98f2fd43e7fb8a47eb87 ee369ca4bc00350efc18defe0a45f7b2bbd1e52005c65891078dbdf7bd4242ce bb611d0aee7dd1601047d483a5e752d986395ce4373c1d479e0702ea814ff116 e3c5e2631a648074cfbff9fdd5cb3865877c8ed9740060d8ec19a8c0e68b4a3e 6273542736995bea142d51bdb12fdc67c45b99fb2644cc0af07e7bdc6174fb1a da3d1948dfb719ea86205cff64314bf94862fa8a9fe8a055e28a84a4eff70d1b 18246393725a72d3066bac79ef8782417daba4f968fc082c6b19e16321e31988 b6c5d4c56ab48e109480564ce1f7cb3f3f721289a0f94dd48ecd33ad3d6910b4 3c0881232d6fb4fc8da12f3d0ba6507eea126767395f519d7f1af417d21b24a9 138056d6e4e94273acc9f27245a237315af8f753572a90b919f285312af7a995 dcf76815300a5bd560c7746ed5fb89adbe5bcbaa055768cf704e439ebafa212a 57db39131698ff9661c81df88fbc0126d5ee0669b812f2f8db452c18152a6088 0e80f14c50018a6141dbad54e488ca479900c992236106b2769c2ccc3cf081c9 e31261e6925b1286f5c7a60db74ca8824d9e2ef98d3743d7466d95f412c71716 3edd4dccad4e686c54d2466e512dae283321e3e3c5c4dcaf4ea7e8486d3fff91 ae42a94ba31b98de52927763bc7f1c2bafac4c1a5773e9e5572e9755171bc92d 7333b1ad259d99b567065673deda8f53a4ae1cbbca73268b73428f2486efaaed e21452330c0d9bf1b53754355e167189c9fcbdb299d35811afc7cffc5a094503 d4bf4dbc4834a29a9b5ba78bcdabcf5d4ed9a0de00432e345fefd681b71dc0b8 1a4c78dd129fc9348deb71ce085ba692204d26e49bc97a50d5c12e998b545dc0 cddc5fce2013adc289d033d5783bfeb8e0e0009e9a83abe8d8c84edf965041a0 f17f54b00f88894ec8e9d73a54132de311a8306bd6a18ea0813675f9185645d2 54c6309fe589f7fbdc1d7f18bb9a48e37610f8666488e72d08c8bb84f9cee567 417bdd7eb88aeaf9618a6e8f6cf7928ef3ccc4ef6d2dd552484ecdab72a261cd 1c6188f8146410b8846f29d8077400fdd6935ccbdb5d22d8d1b78f2f676c66da 87b9a5cf1dfe10adaa9ddcb67747bf800c40971b65c55c0c09eb6673ed7ccdb2 1f60291b939348bf72256c78a71b4d21766390af154860ca8d5d4dddfdc4df2f 6d691c1d52a00c455480f90b84541d56cb8b2e7be4d21422089bb7869664027b c6e0e2822bedfda66f858abb102d28d2da1368d4735a27ba20c9c7ddcc5946ec f325483bae78a70ac4ef75c4a96b4cfe4a24499a5f6dec1e12e2fa07922785f8 6d20a655e03890b9937b9f1a15516572ac4bfdeeeaea761e48d8e96a3df9355b 944607ecb31a1f9a43dcb213c5700055eb05b64f5216dfdb568af69a9b1cc749 5fb4676ed875af4f260ec950c7681ec8d40ecca3b0dcdfe56bdb44c856e0b9a2 f49f4cd110cc0afe50523dfcc81f2a532eed97ef2eb741ae3ed10b11d0ccc804 5bdd0bef2b3438519d11d25e21d38ab327b44b172f6233d14a6692491437dc6d a352b6eec19b4387003677c1514d636da8e5f6053f04fbf1c284d04b5ea5e358 45123c1bf10f1ac01a7710105ca46fd5d9dd20a2a0290ee8bf96a9af38b47a16 f34f5fdbeaa68a7c0028d6a7c8ba7fc472ea66f1b3095da63d3f53fa0bb98caf 0e5d7355d6e5c5b73fdcf0ad172a3a566fbb3c71eea510d61c416c199c59426c 0fc5f4d5240fef62c696c0bf8977661e8cab6b2ab47d3439a990e401aac6727e c6385ce937a51c2c7a9045187c81a3cb7fa5f421c83fcc0c1926b08449b36f88 814833b05cc60d512fcb8c68e02213564ca5ebe7ea68c3a22a1a3f105c8ca7d2 b244db6cd9a91184f40a6bd453b0c0a2d52a72b3e11199489679f4b9f536e44b 5be3dc56db7b3b8457a7c12ce583e651d8d20bd665eda32aba4478b44f0d849e ee4e888b54ccd526524894489c8c69725a9b8424b3ed41d93dbfc429a4c975d3 1cf9f35c2463b134af064148d9a1403e017a73408eb68042d31c6a9f3b92e7f4 c171eedf7c328f87c7c9c14f2288fc9869f3818d064d44d5d6ce6c9c42d296dd 039f046ca97fe2b66fb9e186c3094b61c13fe7992dd36d629302a63019d2043d bb51eeb1823442b107d5f033b001d2a4b82753d5137d3b537ba344b7c212ba13 68e700e51227754cae433ac999c70d22571dd24b4c38dd2698856ae736615ea2 18ce4d2bbaec3546769b231cc286a5aea920ba9be1f0d976a66ca4918bcb4323 f7ec7687aec6e134cd27339e061be1b02ac72d173f3076d6a8abdb1a339f9759 f32a072749e742ac509413b04b8a514d3eb587625d68df3519849f7bc92dda0e749b051a1b0624c9fd2a7a09e75d60678b32f2314c933e1edfe619cfac5e440bd10824a14e7b66f8b6779ccf1587d4dbebd14cefff8b6698919c9c9dcef9b10b775d8afdc3697132e43ed5b1d8299dc29a8b55e0e1bec9873e2ff0438774d60c9b4288c595480037829d39f00647535b6375e1f6c48fde7ccf4c0c9fdf4f420a2bcbf729c28c6dde7d6c57827f1f6e005181d165b237bd846ca6305d3261e90767fb1a7fc85b4acfb97d0fead72e3123a80ee660c9010cdfdb47fdf5d8fc6e0ea2665ce2c107a7127fb89ec2da2f116d7596e35cc0132300c76b5104a3a28b0a24a2e97d3638618b46a266f9bd4a27177564c473f7cee7da4da1b92caf0fd80be55cc10a4a2a892f603a2891c4b54c7a6e91d25b6923682e0b26640ccaf0c40a38ed3d31061ac96c02b17453c7e8a49336a306d5fdd2c4e84f4912741253eb0d4c184768c16229ae676b0456a335d8eae4b7eaaf6843e1308b2c9011aab7910805d7eba0bebb982ccb0a0da17b6d6c149cda25ecaee3dda67dd79a9743a1510c681bdd6cad422ebb671ce87d228a6929b16b1dcdad8b102692e67ee06bd1dd052d025faa1151a6058cbefec3f7cfe0a58e3d4dd70ff18bb51794f78de19c2d0777c72c499b90babcf9b8c77ea0faf4e9a0ac91c9bd19220695dea41ddc5880049a3d5b5cb5ee6dd1d98cd752761903b68077af131eb5e4598cac48dfad90110a2527f587f9670fe674c5845e55c901d9d8bec3eaf142fa76a6807dd433e8a108bc637a45ada650b949bd57aebc1b4fc8bbe93df384bb93127934e3400d19590ee36d1aeff6bbf3df43fb704234534d8aedfb71302e5fea57d37c92020ff0af0416a8bbad97e57abe237da7c1e9e109d63babfa4f03826aa4082a5db60f83540de15c06b5e28fc4dbff38d8f4a577130e3e64f99b89a048ad0289baaf6e4d1805da49ba6ef94ea96b97b86103bfb2f7981762f4dbebdcce1a2136aea44c2d3d018927067f726fc3c6f76a0deaf8725c899435af4b43e639383b55a0a0c7ca7e0822257c75e4a9c5c5eaa16de1970c9ebf65bdd17570b848ea4bab3bbd7ba6a001ef19e4bf679583df2946e16e15000a6ba635c994c6b2473b825ef1aa44ffd904e585ed76428a6994f4f1efc843f247fefd7ff830b1a4435d439e2e14fe7fd60a5dd6e93c2049a60ecc6a85f83777e22863162eb4ae241e556228d88419d1ad07a5451ca579c294dc0e3e1c563f1533d9a9e2ab4be986841dda7387bb0d103c07b7ce0a4d15777d7fd7513fdff6985dafc4e7cdfd38fe59ec2f76e015b882070e13bec8d49d5b07f31497f5e459b9551a20ed02c042cefcac1d445422c16bc80ce2bbc0a08dac279ed06c687499de3efbcf7841c72381f4c06aa3acc99bc39a0d91e952a78afa1c5166effe567fc29ad301cb99a38073d9e719ebaa535911ec03f1592aa831c33709fb22dc34c60258061df16baf79c55d4bb94ff330f47e1101afa612c029277b9d0cc3f382cc87d75e53274bf70fb9fcde2d78d172787f5109390163705d67d8c8c0a3e05bed3d998e06445af7be1089926c948ee0a060c30a533335bf03c338d74e963526b33caba1214564ccafff380574892135ac561b05affb8695688ff0f6772ebbb50eba959abc2b0afcaf01ba2bb6c4db5796a45809185fc3db8d646b5152cd2a74d8a56e992c2f95caf5d975af7c92bb794f66020c2b0adaeaf5b7c3231554aa0b5a79243df9709475aac9699f3efebb96beba280d3ec9e7007b0b3b9a214a07c084d1394c0062db476ba939230b87049dd434d80cedf9a5a4d673b6fe28d1744308107c9a34eaaa265ad860d6f1174c41063ad50fc20b3ef83acbfc85dda9830df267340484f9053b1fc1bcf61d9157b8d94ec80724c5fd23e690c907a1451c09635dc4d7d9f89534ced8a8330030ed0c329bb70b4462b7b3f1c19cde880f33b895f36d9ea981564ef5833efc171be53d64a51b096b470de03348487a89b7f8e4aa2fa8f2390c154ad40442fc690b7e5ff0e88403fdbb730aba7b6e7fecb5e3de73244c8d40fc45043d86a3309af4e02257d0110dab2ac5ae388a67140d23b193a56aed227128682ae25de66f60e5a0c683b2fa092938ca60355722ffa135511facb51ba5f3fc80e6d8a34f72a86a26a64d85be0502f0395bc45e563c3846fc263315b52167bacc7c4402503bd33a69a4a7c1930a5d9edf482ecde965b33c1c8d3d448b5469641d4ccd1c487226cb70b5ba6041053aceef00aa1422066976a41f80e56dbead53a0e24d58e2fbc733d1b1c19c040386ce38975ec7a1acb2dbdaa08a2b09fd31fa84c28a5fb12d2fb431da6e0d4802e4974788f90335a583f2175845ef25aadb0d845ecd58f3b8721c1ebb3816e3062b08bcab4919be5cb97d6cc9e5e61364ea5d70b500e032f9d6f18301761b670f0ae8a30241a2a0ac540ed1e433dca5e634f6e89c1380a12308eb27ab54bee80290a1ea8a68fa7ef4abfee7325887be59e2ea13afec81e0191eb5db5ca724500a2330e23207cccd207d05af8781d000f9abe21f1a839851b70327a48325064c01f7bbc82e4668419fce57cb13bb3b106ea147ac67639c3910a341a173ecc751042c5724610c66048dbf659ad867cc76d45709e5693f510a03323e21779f0b5f012245c52f36a32075335098ab35b208dacb70cba19cb5bb026041a557f0ea140b312adc64ef7060e2980aca3e43028d4fa9e3ee242e60d579b5bc42899df86503cbc668b5508b2c6bec06a992aa3eeda722b67a03e17ffc677b3371920dc33b0c34b3d9d89225b80c03169b463678289aece3027892c357aa9e09a011d2e75007ea2546585f41e2a8988f41cbfbf8b651a7b3ab87e5dff898d0841595aab22907cb29ec2fef15d7f3fde71ec08a7bc62112aad7bbbd912e15afac1c8af1c40f06451f79f423f823c7068816908ee89b83378b04301ba72b65c4c4095687c888013eaa94ff0960c37601ac51c194f6b29ad028ef0d6e932d1d6b1400a8e5d48e088dbdc4bc7d6300ee0bbc91ca66e91ce8306b93e321059527373408845a4f970074a423b05ecc97697905e0f97547073a61d6fc310d997f0451e1b8a174e1b002f29d92485653b52555341a3fa128b096d77b9ef1307fcac9a75452b09f85fa07aea8cf55bb15098a325ab7aa8d2b2346d60245dad400f43a51fb85cce5a57007e8342a421e686e219fb5c050c08572e326925a26d2b799d05375e65b873aab085cd4308930709dc9140ba298f5cdbcda0b0b763dc8e08069aaae2cc38e75d50232fef2eecd88dcae9b62aa440d86251b16d51cef63cd4be3277db3d87ca95e0ad601dee85813f2fe16d8c23ede868c5b0390899192133150d7c7daf04fac0a091fe326af8df2c03c3daac8393abd01f2ed4cb36517cd28f965f8507ef5966206d8ce88088d13108dd26cbb89723b1e5bc2955344af7f6f5aa4d09770adaa240ad87e97b42c7ddbe9292c218d4c59b9d231d9d9496eb5c040c51cfbba5747c90b78d743876c08c495a227e7525fd9424d5dff3bca9b76ef361e7b273972d0760bd376a36e74c9765e6da1749084cbc02c3d9221287cc9847efd262c9af27065003cca28b419d3e621365b2720a4afbfc02b1fd45106ca5ad87103e565671f0d0e05df8b83c1660c6a084a9c60d4d95a24c3891d327e3cbddd48087796327f500535f282a4cbdc2497f7d29053e4e5a72ba977d0a7c7f85f754550e8ce5e54b3063cfb5412a1b044d5a6b49af4435ba23b78d6836e03e0fac5c2c45182a9fa08019ca311f96669877169393a6b5a06681662c3b439fabe9846331abc49a25287077d507a0b20dc35bb32abffa178a500d3c4f126327f5c3ee473aa4f04d503360846737a0b180cbcdb06498dcf2047f369098f7aca2453f8cf51bc00924a706004d9da59d4064333d9a80db577a225e465472cddbc99ad20e1e9e110647e9d0500691b5caffec65dc7819c23d8f5cb43edb3870fb4c2c500f01a9e6e32af5016044921c7f6746f55fb81b07a9f460590f61deb16d3755f134b1edc2be3b68ffa0a79a567a40dcd4ca1c84b321e147b7a154a31846c7c574b1793d50f8059c52206ac8fe7ade389ab08554fcc576457f6e70fac07a61a00c9f5a827344efe2e830a789dfa477d20d516eaabff51933ed6c3fc43accd072f03807eb2b2b0f14f610d44788ed8bd1294ae94a37eb19eee16ab464a0d9e228882589a0351f863ffdd0b803c99e51d8f29df6388afd05690b6f17fe9e799e846ab490dc2d7449ffe990d0b1d6885bd4804e67e518cc92213bf69f6e1ff2b508c1fb61123ce50fd63a408875103376aa3ce0d3a7b8f040856f67e4c747817eae8192e4a41ee54b2e19a0dcd134cfa690a0cee9ab4d1b5f06dcc45443cc8eb2c55deef680d8cbbc1fdce0db37e0c0f5b6e4e6f675415007d183725fc0df660f8e6c8856212bbdd8b214e0b191733d3618a22734b426ed2ca3ffd26cbd4784caca044af22e89baeb1fd900f9e5256684eef266c852879f0f057c1c5d247dd84db3b614ff762c4b44aa9fb076f0d1ced5bb8dc105dc3b287c798379a283b71914eeced4cedda42920b013c093f30014b068085f64cc86134a36ac663b38a99d52b42f90e379330a345411603db7e6622981c0bfe032b6101bbbba0f5cc8f59c476741fe4b88a799b1c2c000c17d20139e99804de51faad786445f04b4ba4c944c6f6f215a97ebb02fb20e1094cc63dc851a5fdc6314435d4d52d3c667f9ef5bc7da5120717ccf4be06b00f08f4b76ce38c3e66db467c018a924bc4ea6bb86b0e13170127ac69b466c5273507e0e75d75633e1246ba2b3f2ccc48bee3a131ac1c45f36109d7e591247878320b11379cb55cf166ec16e2e110cfc8a80fff1295665f74ff1d4e0634822f0ce2053b73d6d1c54fdb6394b88ea824f3be99807061a3f5ffd11baba03c954be0970375a172ef818575c35b5d3abd8123309df1c7d539e5f669a87d4662f09b3c73032fc9b7ccceddd3d37b7005c52736ec8e977d5cc3e823a126f176cc753b08d00a18618d742ee5f0f5d11088965f6aeca07661501d6c20ea7b93f85083901a4508831bf7d69864f8d0c9dc69554366d3702984ec9b4f13321a0b1d7385de2fc9010a874e412778048954b5b806c6db14b2e9913df0849b698302d1f26be2f6d207f55431d08637bfb40b386bbfbb02c587ae82c6decf28a036a694e90734f88f02c4df0b36146ec767cb46ea2ad90764a090af08f4eef8cf95cf5a2c0ba4c3720f27b067707a67d8909ea3dca569e3f0fd43e3055514b91e5cec3ffc9a798ae60c6e34acce1fda0b91ec474014f76f618c8b35a3522ebe5510e6616540c269150581715a7c7eb5f6c75a954f3c82859e8e6820bc63592e13c4ef216c1f545fcf08bc3419948005dd771f31a55a5d7db80437b8f3a80db754867e2691399d4d7a07ab58c49c8372055748de4356bc4a7ae33f8b5ef079110995f503909ebfbbb304fa5c99507c923e57429215e908cc2622cbd75712d347d5af219d6ba8256b4d0057f325291b68bb6347844d651cfd3afe9ccd30f4e9c8f933e46956ca3283ec01a23c6d093e4a2b91e47e503274a5c1d4e9bc10629d60557609058502f11bba07b418c115e6f9aac5a9cb165a39234db5167718335ea80bee61b396095d703c0626637e5ded0dc9490c2208beb9d1de64022933e69463175c0e10ecff4f4e5e04d27dcfffd769040d57a0122f076fb9d0d42fd32c0b3c2d5b493df99882abd90c4681d1a7813419cc267bd78720a6ed57e1e433b8b5405eb73a391066d59a980637c6e8e920c7d79e5929aaad5f6fdb72ba63cae690a589a1e877c02a7b3c1603ca9989e822dde5275736c6e8d3b7016d3a6f21b2bb370c133ffd817a0981f90f4f747710ee193924e492831ecfb2ec10501e179ae2db170c30a40854da31e708e866ba566048d56b3537ba8f1d1795fa5574c1d38b0227956b8d83020efa4700b536617d1d60654eb9bbb3efc2564cdea2871e778a80a67a54385ad199a8ff00537f61a95390484cf0c3e95e9dabf3cf0b2a98deb5a72b4f94947ab476746c071f89c7f259e0ee2cf93000b87c523835d018e8424686608c86fd3cc347a1a30d5f2ff4d7d18556c8e18e4f910cddf6b8147928e2e0ab4287496a5202af582a08e867f197743ad043fd86b8126ec7757bae73bc1b853ccacfe699abf568d9580d5c59ed36bc051add0bd07511b621e8b8cbc9c32340cc02b86541affc4ddf1c0ed628fd2e1820c17aebc913b8aa1c35e87bc416a99712af728ba6d6b01f5fd60ae48097210db32e6a0e60fc428588b21e049d46c6889a33c41534267e166b41089f940c3e85f78febe9e571200b1e22f0063af521e04de508394080408754590025ca84d4d1820c6a56cca0ee71f98be2942ec3ce645cf65ab926a56bcdc2170d634b69612c412b0a7f1502d181db43998e20d816456f167bc73850eef1e3d30fa14276458a2c7f1bb7f059511cb39e2a0af241b19ce5ec8d36c53690f2a13f0e8101c544829f406a1deb34c293e0adf1fa8fd2ad533b4d84214f13497b87b2063da5364366945e99d527c41622f6e483cb289024ca57b940f9ddae7026cf7b061c74a586f13bc7ee8ad352a93b09b6a01ca4b4daedd04ddd5d0ef58eebd9730da047890d51ae37e678e5d7bd2279e74ff4b9bffc7add690f47e10f0058331c09464cf03dc8f5fe81b9063d7e7901498d9771c89adc75c3bdf0711d0862591500c5e63a213265012665a1b1dc479af6975b2bc875ab494bcb421e03ce4f25c205f76c1b1a3a24b94e7efda296888825a2a6fa99dc0602e6ca0a55b98d8db97005511cbc03e2f83d0dbab69ad4f016ef3fd40bfbec46c891fff8d878c173785305c9123c6a74e9335fc93694d74b145dead0473d622a11459734c78092ab10400af7614c991b4517aecbb50bc89e9b77a609d5c1d8f94132ef720eb82bf8b0ad04abe2291fbf3ca7fea438ca3b67b1708db5505ab99a938488886e94fa617e12056ae9e73e6e224fd9836e97f6dc4769aa470126f6a2ee66bb5628add266ef930a03347c8fc0d0ea227eed1a87d9dd01a6a0aed48d18b2840377e7795767b63e0b1f5c1f830fd7dcca29838c9569a96bf651df81ff745296a5eaa85e4f519b050cee5bdbfa10e60a90e015c3a9d1cda77536e2452ca068b0aa8837b7ba7b27ea0e7c22e4f3db1e60141064dbb78876480ae0b0646f6c715a521af89c630ee1df05480ceca293f41855424a51304efeb234114e86d151a034acf8aacd6a5730430c42b1495e74ea7d6e694417e819e3c190932d920fc7f68de72ab9066926231703221d777cdaa7187477dea4362c1024f9fdb0788a4be35e523c9678d99e9d090370bb8bf174c13dcdf29c97108a59ccac614e9d1b1ab1ec34acfd0f12e1271a08eea72a9ddd04d073daf36521d29b1eb7758f9513b2f06c3301726e964bc79106d725833f9c76c2f5df360cff44747c3f642fddfc1f2bb28d2c0d8afd0cd44d0066c437e6ceb1e1e2341af39c172042059e505b578c58427690fb4c1ec494b902255e8d0e3800b3b9f1e5f31d4ac1d554bcd1ca04b2e5f4d835a5d4d062095403c1c1dc615e56c1fcc90c83b67be23ec897c9b3763b1f7d7fd0a9f7afdbfe6c0569bb3303df148d51aeaaa81f0cd61fecfd362e4eea65f14d7e2c1f203b18f50d6a92e75f6a214005b0a44ef16df484a03bea40e187d177eeac2c195f7186970e69822a6604038485722822de753cb1e186b3f31d57318541e61f785593a0a30748211c7da88f2c7e02efc997bd6c3b4e54d8f8fd0d07db38eec563a5157b9d04a71357469220285e96f8a6febf825a3abb1efeb993e5ef2f62b282f47bb59906b5e66c62dea39f817003bb3b0f07dfe585ad97057faba7df7f0d9977115a1e0fac9333d924c71730f157c2e8bc1e5dae27ca70538a5310d2741a586cbdbbeb0def0e912a02ed2b4b50386560ee6a38ac7319b525d09d47b755849818109b960f8232a1742765448e128d5ad7566a46d3c67eb71206f9f38443e23610ba00930f47b9ee99c9edb480861cf62a8b1bd890d443a387587d340c8ce6f286378bf40e9055896958a35f94db6963a22c5365ca4488d5a197778f801ec6579e00c63b0f907e8d068ff81d73af3044ff13693a53b8a01961efac7b02328eb2dc6173480bdce41b64ce96cbb404bfbbae187b5bc0d9ce668cf68cd6a96d118c8ba7ddea035e4cc64c4334b6ea6e2c0921f300ba96aa0ebbed632285390a9ba0c56dd50a003683af5bb1bf7921bfb18c0341a97b8a51c7fcdd653c4ed71ae8dd035026160a18beed79b49db93b5349a295798de34a8d58c303fbeb770946dfb53f6b1c1d001df1d4c8dfcbccda3886a6b46ed2b5c4c75c112149454caa8d3c368e757b040c6c9a2d4ebe1f0813de0cad5e1e9f3499cf0c0f63407573f2ff79203334cef70518a163027a10396be0a9df60d5f42be930fff4aad1578e80c0abc6005b5a1100603914d8fc34797e98b3b9163fce08140d314ef6de44f5786ef97f894eeb53078e534125906d7a4c314ee921ceef290ba0423bbd56c988e51070a00ce747550439bd299b23d575f53867fbbd4cb7ce4f57e3882044e43df91343964feeea920893abbba056c8b70c6679cc9b51431f31b7bc0eeef42e0edb905171c9ba44a30b932527b13b03a176fc5eb205e47787a4b6347ff49ea68c65f426a2dc2f7b8804cf8b96195cc9f9c68cfaaaee5537ea8c6ab8e8f7ee8e23660e485d9c48fd790709b3470783802e9c1b94f24cb0b103077437ccfaa7ab96104dc0a53d04943108d67ed2cd9a2a48f2a8b80947ba8cfe2e6175e1bdc0ec8507ceb21b4eef212403d1ffbb52248302ec2fe1150b93ca6a149b00e8c669eb80b9d0d7b42b022fdc039c6986e8d9b5483e1ab9249bd854df84c140161458a4645e85b11a6340d7fe0922da07955ef374527fd2accfc5851e5058a89e7caf6438685d0717e7349a430b95acb13aed5ac48a65a94ef3af6647d6e75c70209f526627c8b51a46d7d49d03c278a619d8ed4cf7105b8c1bb7280736449a6bf3950827c84c56edc96846b30efa07107dca8989db4e125114b8eed3e0f937ce6a0a952d559545baa04cc4f60ec488fcd26387154cf128aec9310e3bca45e1e14b80247fd638150f14b4bbdf0f1af01666db74eec7fdf006fa3ceb331ef31e01a225524809b00405703f1c2f0cf1170598db10533a68f1aed48b6e62f356e5d4fbaecbc24273896c7840e810001ed2b32eea03c3e52059c54054ea5332262bb898c822a1d899d3c6eec89b6608ebcb2d16da581bcfb90da4d1439f3690a691f271bd5e748cb5e65b12186bf4065aa83cfa6724968f29f25962147544b379a1e943e883451288c90f2ae5784c0940536a87cc23dfef24df6b020ec53cb1bc6a5ec4b63a34ec6ba25a0f46492c0a6cbb0089783772457dcb70e471a7421d878c22b98e68ecb2fef904e53d5c5e0d3f4fba8c025db6753be1b443ece9fed498ace65cf8cc192caf4106176700b309c1aedbf639a59e090ccea3de1b1fa0f434ba79736cf9005820a5cf19810f800fee8d05f6c172e160fe071f254950dfde61b6aadac3a36ab3fc9a4dfb08e719073fd3bd43d05c239b60aa9d6c21b24c08b75796667e01c9a0ecf71282b2983804e190dc34d16e15b679822156eb47a9a6132628f4b6e123a149a7d421150b7102b9ad87708cfe046c5312590f2e387ff0ae2437402c5b0c7df2b74a3d6da68708ecc858770eec2aae8c54ce4eea1713eb5f669c8030044c00f6c6ba2001d75d048fcacdd512849f42c0c17c4dad1493b9c8227762798ffa2775151861b4a4d10efd5169a8b3e3aa22a7f448393f69e5410a991a2fa8e5bdd9d6db49218f9f74086dd72157d482c16b679d0913b2ba2c4f2c98f376cf4afd75607e4025c95af10655a239277e4ad42e9e82546f0d749b1954fbf422a4fb48e8ba3830436648640aa05c3069d8d5d16286047d7dff92a9cc7b012f4c689649a0b9840078c29639031ba6d0001e194716ade5898b8b68195dbac4722ad51a5a295fba7e8a20fb8f02679571c3f3429008bc392ba23aedf2e5af70ba32e8ae577ae387e59d1854c00133dccb2a65fcf842fc91191a34238e0eadb615a2f73520b81d3be85bc63235010377dc14f3ca01034d3d1016e7ae61cb15a10d9acd07749f228394e236ff1807b9e7e4bb196daec15d391a286f57f6696ef781932f303a5a81a7784fd09571060fa10bd0f6de0d25763876f60bd7059e212c9274b5aef0a33e78478fbc730d054db46d5f0e3ded96fa4e6d64abfbabc90beb62b9c309182900715d0f5b9e010a862b7b7b782b500a52a28771a5ddb31af128439ce8c7c8fb330875664fa60c03f177bf92eaf722b337f1581fb50df13f02dd9977ad982acbbe58ff2bd44f7603be38c931aa74e3e236a52b8b9dff20754f33c9865af25b135a228a8816837f09d52cea692085f0b254e73f1af5284961635038132f6016d3f1b84fa39c518a074a2b716d8a3ebe63ddf86a018b2550f8c4bb05586953466ec5990b274fca6304e74f4411c481e2f4fabe1bec3dfbcd54fb376e27645555f0bf25f9a88e879a0473316e065efa99d01d18b627bbff3a53b16efaa79554b88982ee04267a754e0b48b9954007414484f554dbc3cf2b45ca4b1555b21feb75d72fa620f4c7270f0e31df18bf88b92c2f94d8a9313b00cbdd70f9a242fcee45c8138aafb8ea9ff50faaafd3a00291f824bcbb4aaf930e32a53ba1921b5a2c39789e259fc64833150341b05cb129b266ac5ce86541b23a80dd1e937ed5ad0551fc2cb03b72aa452f0d true -check_ring_signature 6664ddfd62d4db52cc3527630b8e4bd45941542d292cadc745ab27e3b6de6446 a4e16dd06309db9140f560577c1ee7c36e73b774cd028b6f55609bb66a019c73 1 303c4e5b2b8acea9bffb433f117e0b1a754453e9d68cfb98069c40e9bf208bc4 7d6d2450bd9cbf647ac677eabd62194f585098575be0f8df58c5ef9eb687eb05ccb1837d63621815df56ae9c421630ae443cc25c04cef8e129f68a74bf5e7607 false -check_ring_signature 098c228de28779ca4d2fadbd8973385caff9bde06161d192ef86f5351ac99b81 a88a4b0fb7eea101411c6b2b7c9fc235ca8bc8cc5ee740cd4c7123910038d745 40 ba78d8417661a6b9908fbbeb4fb50996042cba2a9b3ba52fb6a71727c3590f8f 24e97d1aa5157f56c5a7a8f5f21369d4453178f878fde12f34898ddbc2440ec0 bbef0068d31ca15ddc3a6238b1ec4cd8b574fb596da5024fa39a4b8d6327b986 57e1d12f326a58c17cc7e764c866ac3a3f074c6c0c884ff151fd1c697455ef6e 6e8a0f2e35c7cd71430694f054c3d64022d009d71095b183caccceab7158d07f 88121de2616c2fff3e5afe6186d345c31438189a94b751d8ec96e641cb72ae92 ed8112f8a0f21f8682274db02b123ea6c9fb26b2d26f3735a22e14c268a7564b 25b02a9d36f890c94d1564af9e5dce2d767d9c72557cd15156ee03332aff07f7 ad4acd68a59869c7bbdb95b1782148221dee4fec095682246627ecebce38579a 5926b6b97d3162d73f29c3dac30ac8cd3695dc71fc9337ec3c85aefb232f2a03 47055f963ca42daa585de3721f746c82ace2e98566b924c2c3203295cdbfbdfd db2cac4cc971d3456bf70a3db302b1f597aa8cb1cdbe02f99ca90c81d1997f45 5e1a9d0c81de1a3e3781b7ffdbf8493a8ab7164f1cbb37bc98b85d6753989724 154abaab36ee0080c8c74a79582b0c0090c3466c056fbf85bf4cc88143caecb4 ffdb6a317432d640e7a6b3cdab48c2830aee129112e0085c822c231fe638d610 3edebd9180be57e9c34645821aa218a8832ba4c7c97c9fdb9b2c9043ddb5f178 1556f39491dbc9a7434954038ed59868ee6e4ad6849cd7e8ef22f842d851b4c2 4698993a4657d61b0e0b4ba2d77d0a403ada31bb55a7db78a8709238ae5c94f1 d424e27d49e1009203aef4143042b49a744f1db406312bb681a26af7b807a3c8 b12a7498aeca083ce29d99c24e349bd35ebd0f3fd6706a04456ac387e5d77275 b493b1e5e8bd5b361f0eba292a81640f59b64409173a4eeecd5353b3c740cec5 707d531de041d91b8f9e4f7ac53aff5c2a25937dc4c1d6010bf531164f46a718 0b687467f5f1bbf5bedc63a70697f341745546bfd5929474b81e96bee8316d88 3d80728181769381c7571c16474c1480513e0fdcbc0908f5e00329cf774259e6 f0fda5cf17bfeadaf49ea83e5ff530d51e798a143ca3e7fdab900e5ea6e17ad1 58ea3a99b04918c0e35297d68297e3f4dfaec7535b704d413fa9bce574773388 ab60ad352f222ee1c4fc02ccd6e1d3bb435580add45cd7165423ec93cdb76aee 089a9ff4f9cd7cc6d9fd6f2e30772b768cf2e116c0270b0829736dadd8a3110c 23179bb6ef5f50f5206ec4b58d9834ad92eed4789a0644137be990019efa9889 2fd13df0f048919ce7d6a03c34b8b11deb5bfb6356770cc97edfad880365e585 75197ecfcbe5139c3699000851b4da93148566e174131cc226d085bf1b4c5f1c 82abce2c958cb869c0134ee849ec75d754fbeed83cb21ff80d9c168edd4eb3ae c0974ca50ccacd1dfeb605fe8de16e6fbd1ca7387191e219386047bc5db76ee2 f13bd645d69ce38fbde559eee64b90872059d11b0dd087f26bd8c89a2d289417 e625973f2e39d092707ee540271655981520a0995f6a702dd8817a8fb99eadd5 f0f6c6bc2d325ebb76a41b0b7d903c315bdb197ff6110ff2969d4987cf5f7bf2 53e52b65b15bccda2bd1f7cd32509a7bca632d5b86c69b508ccb564d7f0beebc 0331f6ca9a79b8dac04937e8d3dc7a27116293da863e6847f1387a4a9efb7c64 6f88d23f8e881b315f4e99a21b2387be15047d65033f26769fdef3109a589389 55114566ca20bb207fd063f144b280fefa7da2d34987eeee3db184e631074eef 9525928ec8125aeb884ded6188957beb14389fa025a81862d5ec7dc2703175017e2c3ff7bbb296da79594da6dfbace6fb212493a0a02fc96536b10ce76c8c504ebfb21f23e0a27a652b2aa23dfcfad6ddba73fd2c4c1dad850c3de43bb5cb303ec6c011e95c1e3fde9bd82c79481c38835f8e2e6bfe15f1550a238dd765a3a004d0b19fe51a5699b27b23f400525bf4e77948d0fc3a63f525666bec50add0003b81dbd0a41233fe7a7e87d692e61d0e4f6261fa2d65a44b4b50a458144f90d058de333ea63721bf4821cb2c5d4cac4cf97f317fb1c51b8a3bc6e7c42b2eaa10b4b1903d1d9317924b9e0d0a2570c1c7459a8b766aeea48cc3d2a5c55b7f1500756810ad4b90cb1b244e88ea4f3171f44104dbfe0e6ab1a0f82f0dae2ec0d8c09e908883422c1354103ad17790fdae82be1d4d73fbdd335341565ca7f11c4be079209b9d8708494415760e0fb4b0c71abb55a05ca2a3f575be93f1898011f3e0ca02e6a2b696d61f9e15a47d7fd526b0ed23f32f30a095fcc2f8e46cfb5c4580d646f795522753ae7e17086931c57ca6db6095a9661c18251b7235885dc6d4000774ae7feb8e33c3dd30561e6b2315ae01ad497204d2f3fcad1a3d847a82d8e099e33547c307a6c7e7c8070d459008de710ba16ba015dc15a3d03aefa51f4bb099ea52ecee230ae7fd76e64dcf06129675f0b4a8d837fb0693769ddb1bd77eb095447999fe0576b79dec8da4dab1483ab6edc9146f6f79a3d35936fd2c033d80215089626c329737bd97406072abf84fe6239e1da5e05c3030038d44790725002da63b78bfbc05e8a9acadbf2de8d1198137ff62469ce1cd654448802e5db2300afde056f7b6cbcd4e8400acce6c987fcd079fd28e0dd754defb6cec769125e082c6a900375977aec0584d477e492a620e0d4abf613bfd35858b9417339260a03c692d25d1a41a95e37ba9cd0f5913d41936ab0feed519b0c5a04966399a2190d76195e785091cd133b521a3ad03223dd079b6e1d009cb8d7c6c06cb59a8d4c06da4e36bdadee4202acb938c72f7ecd09d380de857f5fcad2fc3e50780db7500089c7b3f60c267a0d23653c72bab9409f3d14a3e0ad7bfb06681adc84fae2480611d9c284c0e845ffdbbaf62f29a8d7fbd274b937f5ab2039a2c17799fd8c820ea5c4f0cd2040e3b18efd9a6a9be5ab581b5c6d4477a15c8f3893973e865ccd0ed6cc3d617d88a73426d392751fd42788edc75ebe4164e67e4efa0085364627005c66b420c7a940d7b9eba182321b5bc401b01f2decdc6647a28cd21d4c0cf40be1b12e16f4ebef05960c83df3592f956f389ff9b00b956bf9dc8169990bd6400a6f16d26c6116f23083feb535a85d7dc8c804185aa1095382a248c23e7beb80026bdccf9cf2f53212266606e86286843031524aec7b0424264263902287afc0847c1494a007d802d1ec401e7b62241751e552b0734b466868a3fe6b1bf3c0105d211deefee012996b8b68e5be84254d5d7899e3ee75d8bdba7b2762123f8c502542a799e51c8d3f017f0d75d3279b4940d819e4555c3ff601c1c5e1e6996e806f3cb9c39e2448fe09d78bbe83b7c1c2b854b11d727368526e8669850fae46f03735943d8eec33ef68e43b5d229033f4ecd35f90d51091a7a1ebfb5a61358ff0f056e6e5600dabd2f79adbe2d1499f6a5d68628b5f08dbcffd15d2fed6b9fa104f583d2abeaf8d48806055239e7f83e8ef5d7aeeb7cc8b14be267b8217bd56b07301499f49b146e20084f99c56b4d3ec65bbb6d7da161a34a56ce808b4a94d402593406dd6c6bcbed0ce4fc4c3ee3468c0cea9021ea289a6914e2c780fdf81004df24155dbe4a8a2e0ac8afd03346112c8dce39863710608974be3c66f79d3804ee0fc58df04bebe315a3d81a0d1e640935719a1738c1cfbdb8424735490fe808a84212defc6f3884cb0cf0f639fffc553b17ceb2285a348489bfb6055e81960f09db6eec43632724929dbedee2fd092892c67da93bd0eaa6792048bd24de830d247f5ec0f0ae6d15268709f363bc512d896d1bac2d1d3d5f6450722a77eb2f084f973311d4f2d824b249eb249bab21619ed504b8619d7e719193f61d1da15c0811d6d16ef945e5c0ec9dbc279754a94de47698a246d495d96ea8a70d1d712e07f41aa04de9e2d5daf078ec072e4ec7c54bc14a19b4ccbd19d822bee9296cdf05e79197899482ec22e5656dbff1ce344c6cbd4d2c53c90e73e5dabd4caf09fd01262d1e59ca0d38d5f7455e1786cc2efd2437a2cafa44b3cb1df144baa5b5760a4ffd5686ef9b69dd784f19d4051608f1067cce740be6fb93f73a429b6f28ac0e7292b7fb9bfb1443fd3270f8853b465a073514bc886016e8a9d8586fe4403f0b9f4fc25b4138b9e06fe1f9163748d485af2de1e6bf92b81e8b7602728a17f00a2fac85defa3ae81e76753890317584322e223636e14d68faf84706a5ba9a1b0018c14ae9082a52d0d9b6c87dd55a7a17d0daa166e00159559f0bcac75ef0210842aa2d4ab632c546430e227a1d1239c07146e4bb8144cd2e0a425713ba8358066b4c2c2573616a0d7d871e3b75d4fa011d0c3ff602c8906628f6b0ad8051010a7a78b03f21c716b7ef2dcf8f1915eefcf3390dc22b95c1ad3bc5abfe627e3f0a0ad3cf804fb9a7d14550af18102399bc23cbe4c23e393474a2966a88fe83ce062d1937879f7636d2bd308610d18d7f2767a9e89d71ca25ac71341d0fab07e4016d43aa8d3b324564645bf3077103c1ff0340d4bb74e9bae1cb25cc0596b5dc00516aee7bdbe8c86dbc217cc2e9675c4a9880e0dac330e3c661d63a38eaf0380f69089650ecd20ddee5dcc9b094a17fc7361cb2585400ae57f6f4a41348b1200eca1a0057279eb8b7d1fddd46a9746148682fc29086a93667c21ae78cd1985a09926f88b0bdd0c3ad39f641dfd49bb10418b57cd7369f079949ebee7c220b4000127a49af3ff5e9f6dd2c28d7958ef2bd5a722f04c3ed023fc0984c6cec93760b26866db8c861bc6e233f70cc470e7900d2ca1d7720f01986883ac52e4a0de70eba5af05a235491617f44ee7f56a7a5024d2761b3dc3f2a689c8e4398bb1b22090dea4eee0d2855ce747be3655f0562764564a216ad6fbbcc09c398b577860008a5b06d4cac65b0db304c7cb3459cc47e790832acc3797a9b59a59bf25e24da01f846a838ce9baf86760b0fb9c3357c97fd9c3ca8986f9d9c423227140748da05da0f5c946321eea5fdf3fc9ca83f77337a9bee831a92bfc2bd88e8983db9e508d03ef9f4f24d8f7dfd289f8db27c86b84cd2fdafbde797f530d0fcb1cf586108c30b1d345e6421e50b16d794d76edb22314839df6609ccb62eb99dd961084900fbd96b140d23206c8df1963bc266cb6a6745b3a34fee4b993fc66d284fe226039ba53985acf68af40531e94f37007127be0f8e8d0ad0a8c1712e13458880640c1bf6ffad3db2bc653ea93482a7369368a1df18f90be1e40a568acaeaa5c7d1093237b831bdb4ac9cdcd69b143eddcee7c40d23355cc0f5f12f8821b8c78ce40e09499fd3e6d76f65418f9c60eceec5f84f8c82f3bc61306e0c185851a4b2100c false -check_ring_signature 5e60c84f90470fdb92feb2993e79db56e496b59414be9c1980b3d94ca80bc03f 8d0628445c5978c8cb25e3255c03d61dbda99374aada6caa125269367af2783c 14 0697078ca6d45936627ddc714c69ab678d05de4c5db5d42f51a0f4b77fa42580 47f4a3e6df3a1828f2be93eedd2275b421370b3edeeb539f3617436d46385a82 f89f4fc8cabd9461333b82862088c36f037fa8c6fe2530c1b1856a03dc0d2347 feacff0fd68a78bb8f407bfdcc42db5096dedc30add97fd1c35f00b350269183 e354b55d368e8c1a32b079f52abab69e4da4ce335a7ea5a05bc7172fbf25301f 2efad00457dbebe5aedf96dce26f41993980dee02b71e33bde6f970741b38c12 14bb7383c4c14d18074e0c6032bdc28e5ae1a9484b0e60ac7967bf55b033656c 40fdc3060d8878604f7ef1496f069feebb6c87f5adc0e148014a9cbf762a23fe 0d05df8eb710336cf486d5a35cf07cbe222ccdcafa10157cf95ebc01b6995d4c 87143e0bfa2412a4452fd19196754c77ff7c4e9e95722b7fd43d374ec96b7f6f 3769a2243ea7ee6f49aa28cb40e60cabcd559297f4f8ecf8388210f60972ada7 8c645e36f59dd5df6e61998562b4b27340f812413085d36ce3eb2b67fcde77ae 800d5321ab658755450fe58eddcf334188ce7a31c3684823d128fb4ce9bcd32e 512b8321a5dd1dc130b79bf77ec14c5083c4b397662bd4892fdf4535ce7fe8dd 493a609624e08f9b7688af5de1198d79e2507eebf09bcb835f70faf9155a7e01b5a10ef5aaa8327fb32d605b76deaf96ceb972db54cfc3bf19ff77611fdee20bf41cf223fb05181fcf1cf72ec3fe0dd32b452458f67d36a84cc5a106d11eab0afd9bfe1b76fbac0b0b0b2afca66af4b9e3edf106a1ecaeacd3ea880b53bb030242a297322520b33f0f1285832ac45ae5217c78d2eff74231fc85d0718ea8530f4937df4c972168dfd657dae8dace2a2bb3e19a071d03ea6c31d61de6531c230b770459d44ac693fe37ef99704ce850cf6c39bc9b0e967d615c6be152a0f80d0d707727d9fa5c9894897bb7eacd80189e2a5c087a754362b7dfd7bd3799ce43017ea29a0f0d1386b782f73a1fe2a45132e0244dac07f73f70bf4a3e0cc949bc004a063b8bf0a6eb5d480340e0cef038a24d1287324b86841edac8eaf7c6985a07d5a463e304bdf819f2f53ca42e400def95e8ba9cbc60723ec568950f1db67b0c4006d5d0f3075a780ffbf38166e9b1edbdd65279c8f4a06b70740eb7f5ed7a0d8e8120987ba6e72a177e2978a2f4ff00ddbe51888a6dec3edeeaf49d338e3d0b409d41fed1dffe31a77a3d3ed9c55a7481e09f0102dded6fe4305f001af5fa0b483260ba2c08307db04ee309f0144b57a169c6b2a3120b89ba5c7f72d5f262011e3ec3e80c8be0960a9df6473f74265bed79b44333e8e0dd241193cbc2bdf0099577a12b4c8dbc2096bea311a80e55bf9bd8428cba22fe9d385e796a93665003f8c8cf90b8685f06b16e56fb75bb5f940f761c6e6e5ab66064e79fdef225a50cc6da6366effe7df6e8595ecc155560feaa14be93876feb20ddf780ba6174f2036598a75139dab1f8ebdcea0853cb81a2fdae7f28c7549bc78d490d9f99c26d0d016b2a4791dce52d4520e0fd788a1d3c74f81fb8c1d2661ed88ab488d501ba04eeba5da4c7d6f0b8d8906404ae7bc9f6d9cc9e51836ed60f7527170689be7d00197d5b883be8a9213731c0424c8082f20a14572262e11c50cfa63d35bcfdea0e3daab6faff77f1ab6c83df5ed3c4b41ebf97f341e77834cc8985b75839df640f8a104fdcc4990e6ae6b12b6f22960b2deefe13ad55ff94fd83a7d4ab0de38c0e9d1098d2da3c2e96eff5a5783caf8ee3065b1524eac2f5126d6d8935938e26006fd28858ca49f4d5cdcdbf10d491093d6c0c0a64bda84ac94cf11e7cbc5cbf0201aa5153704a4bfc27cd3ee454f02d064d4bf0ca27697ec87dd2610c0264b60c true -check_ring_signature 73b51f2fb7f2850bacdf69a71268a87f98703171346b499ef0b416fc0161b104 a042a66c3a5dfb443f6941295faf053319abc79d62feee63a801282035b59b3c 8 951be4743e2aff30bae6578993fcbf40169bad8d768e14bca934c3ebabfbabba f6cd6506367f94d32263c29ff7c55df596260d19ce3681abf7b94e0fd3c87cb7 0bfbce60169d40e9f226302f6da350e66a3909f92cf31ea9a015789ec20993ac 9818891d40019f6c4fa9d60c53418d27f7432e5b3e9051184a6c07a421ca02aa 5d9711d1cead9661ada60219c05ee60b4e0dab9c974cd530ea602c34f53f0016 ea7622f433bf6f1fd6ca5e2fcae08ccd926c323c1b3d8d34d31dfbd05ede2276 429dc689f6786d28d64f5297e8b42f9880df0edeb33c55c91de039b8b89b2652 fe432a71a8bee3e1d8a538d646d61b50679a31aee27281984c9759e20c9c5848 dae85a093c04ca677ad60cb1f558a857319a9013d7a619143c952374dc8adc0b416f21095b5976c1f7a2a822c139dad2bba9765374644d70f8efee77deebe009f6cb9ebd2bab6ace167b5160cbaed9ac00c94f03101b921b101d4635f110a90eff2b661ff4fb508bd7720a4355c98926e81d222dd8cb90142c4fcdb415c9a70bbe3dbb843a0a758f80060eafa842c93c30a01edcc801c64e4253e92a2229aa0960245170b1dbde27dab776a5a190b02168ce53362c4975e3e656377822bed908d9ad99dcd74d4531d49fb071edf666421d82178a15d9b26a4d830c4b9ec2f607ba979ba546393a86258333680419fdba72ef5f6ade937bf352ed05bf678afa0acd76be46b01cb10d9139daa65cc7b43ed5f84e696ce840a328e9854b07c50a084c6c5bdd4866cca3f1d0d7426654db91ecd36093c07efaf9a681c2b4e0d0d90c9f1a485659fca867335956b6347e16d249c3ea0f9aca5b8f7e0f2294d4f68d019f5f1166081032a248b165d3186be59c086d34c8070aef6b257e94897daf3d0f62b171e61a76ee2833bdcca43783f7db14f5853157cc5ae8bb5c8c4dc0dc050094acbd81a4978f66a356567036bc16fff50ea0b5f0532bf57074a9485f61ee0e7663229a90a8bfe0cf52645ed65db0bda9bee7fd9a8cbe4b89e06fa6bb533209d44867063c825c80bb98a06bd79e0180d5d5c09f9353db74995b757c7da348b0 false -check_ring_signature e1e8c636af7726f032d01682787f88057d5aa2b345561899f56ef2406a48aa0c 76d82cd5c5abd87ba15417a57c3e2328f083de06cd75a4ce62397e40a471800d 3 7bdb3545fe8cc8cf61d0d14b7a35c6c6cc2e30a6fa5362f92b84fa7df0275991 e7dc7fde6292256cbde42f1028509856e97d505c08b5cc389cc3ff4cab66c1a4 4a63077291afdf7e31e172855db908c523a5431b5d136769ac786573dabd3fa8 6a6882d5dc7ad5821f37cec0aa4915147d0270c1c6940c5fe7514a482b8df901ec3d3cb484d41f8482b4b4b9cd819812dabe1182d09899ccddccbefa39888e03e835735334c7317a8f56a1dddacc50bfe2afca9a634b3f1ac82d1d9263e0d60deba45b291067a5f0234e25eb412f0e0a060271eef1ae484ac15f95e619614e031b9e8187fe5d9ecf6c9911361bd531a8140d0864ac7b066c6473c9960eb1b90ca11f376d509a6dd3dbf0839f17da669090a1ecab430b4bf91deec17d7267f906 true -check_ring_signature 236bca93cb9c75611c53ab04deab49693de2d33ac7f45daa6bbbd4fd9c5aac3d c9ded5d03811aa4b164d9fb993f602a20ac7b574a4ca3232db3eba9992892fe5 109 21b633511119ee81e124b14882e39dc7e039fa97e7bb015d6cadbc6a135d18fe 53d8132e04ef5b930e5343cf609bf2e2c2d1507ff9363e45574d486c1cb2cf25 c7c820dc97f8fe10c77dfadec249a52297db609261688b454b02ce97718e9ca8 5d798b982252966809c20f6bf0e447e80a04e82333cdb5e3a2c9a3c6cc8c57e4 19c1decbeec68004b1275b009d9be1521264d57761c43b2b40034ef05dd6fd1e d5e76765dde2758c4a13911580cf6eae972823e1224cbf85a60abaf6733d4c69 cc6257761706ddbe1e6dde5b954997d74d34ebae4cf76bc5b317e713119025f1 2a710636d5cd2d05e72acd4427f49069cf12dc3e7273c8242db47697da4ec9f4 67da707db2658509967c733d3bf7646d0ed25d38dd4cac67e1fe49fdd0411cf5 24f6e85647b35b51ca12cb836720e8d0e60fbafb4d2ce4c1be087f3581a80f22 1f7c4d12e307e7b4e13ed01fabf81dc8b09df09982ac490e51c581372d6192b8 5bb4423873a144ed5f47bd5fe9434546705cf8d611ea7ffe43e38ad1c7c5ce54 d6a5a0d9ace410d8c48aad1782b4474392b063866727463b2ab349d83131ccd0 d85a57cd0c6ec21dc3402f8c786fc70adea03c57419dab993c465eaeed23de7c 33c570a29db5ebcdf24759d08a3df691f1f7cad76a01c1c0cc6e24d5d11b22d3 52bd49f1ca331a64edf11548c0a9d5908cbcd177293db415008a83bae273f4ad 99809dda7e4fe2c9a1f0e68499ce813a82ac6b13d346e4518e8124af98a1548d 96171d8846a79c8f9f95b7c09f232f175b99a648e1041cf72f052c4e76025daa c797783bce05e9d56b3b351b361faa64908e8fff8fdb0caf9e398f0afedb710a 6377872f1aac7046d696e57261ba4cd454a91cd3b92485091856ace97e03d2a2 ab8325f22fcc6cf9d9530fa22515b33e317fe86d5c9100b532c212f0ec1b812a 639a9a3a1b5e381038ce852948cee0d91f69ec10dd085a750b23893a39c617d5 48747e760cad1eeeffcd3f528a33e6e9d1ad051625bd05c9b55160c8ffc4df08 ae20b4eb6ebe6d5def7e4c4f4a6af10ccf97f3263080b64286db83c351f54ec9 a083f0a00a027d208ae49c2a64eef903e0d6dad16581900d02d98653f845a720 e3f854e96e4b9091212da568eb84a90c175600b67d721302310b34e0012c2f31 c3318dc9c780b74162815da6f055c0c8206868e443b9a07b9fc9d459e66bdddf a1666df44f6f4d334aa0fad8039fc7350a497fb1837c22b0b92d9e2f65916379 8414b88d6cdca65192af065e48e527d8e88dd8ce2610ac32f9574d18e5366e94 dc09b95c8e75d500f839ab18c608c6820246ecfa7eaac8ae89927143ac16d1ed 30b208870247c3ee3359b800abde386d371ad6731175fa50e924d92cfa72d6ec bb2fd84d0f5f5b56239ca3bd912765e62839e4bdd70391aa1bf9e9ba3ff42132 f24113b34368e89ea491656fb25062b0d71d40670aebc96cb4bd3cc11cf51993 2aeac6a7dd94e20d38bbd05c197483752c1ee21bcac2d7fb8805547cdd5c2ab7 56ed1d81064f9a85a7256799e082fc1cc9fb70506f6d2e2dc53aa04766ee5b9f ac91596d69074da4d24b1e2e6d2fcdb0407f31ae466fca0b315ceadeb01e244a b212f26c1cc2be4b1bbd4348bccf972ef9e1a900f49b2fbf355bf46f3d316ee9 82c86b9104cf1af4f1efb4c46a191fcd94f2b9e1771977f25b59f7b3ff5dea24 33adeee2235fe120e5818eb4ba4d5ff98a811f4c767ccbce1b1bdffdfbcf908d 1ec3cbf49397536b8b44d8fab1733ce08ae9f5c7e2b8fc9106901c0b819cd658 fafd8314ce838b9ed3b33cff096b6242dea00343d9f948a1de5aaf637906b395 5d2e23ff140bf5beed54faf850f15ab2103a567b47984179b8ef8e227dc73c12 b1dcf8bc44be11ad79ea20a6225cb9d0210134a713af34a9cbfcf6ea7c1b87a4 f8fe6927b88cf7cabc75a2bb65cdae2ca21c29f3895c0b610de98c2a033ec03b df2a896c5e4d11aa617f11d4a88a31971f88843fae40fe4d08fbf6a9a2cbdbb6 1425a3bc0ab9517ed05e345e9f7aa2440f0128255372c94cb889ea293ff1d63c e65a104010bb11bf68bfb33639b071e77fb59f600788ceca24bf2f9bb985c285 f22c211b288a1dc3414c161d58ab052b13146771a6c22688fb86315bf72e109a 2feac0a3871b2e165d9e18e768b543fb7f95e4848e9316781d5de7e9c9469f17 d26b37cb0bfe267d73cb39e3d782f029d1d235b5683fae1e06e1bf175b900cda 34d7c7014ee8ec21e480022bdef286979fb410835a4d0ed7386138402999d7cb e2da0a66cfc92cdae96b519c60fffbb0aa9292c216813e486d62d97693fb67be 2968f721f49e8ee29f138f1482a6f8af35a60bfa7669808cc379d6c845ec7899 bbdcb4833d73dae9cadbaf959f798ed222bf07422ffb36709b2fcacbbf660a08 d54f324e69ec32d5ffd58b8c2a98075327e2cbbf29c7fb792c6f404ad5322a59 3f122895f4e3573646b8e3aff5200425aeda593fb485a108f1975e26ec60ff9e 140707baf4982dbef6bfaaabef434b1307e1857919887a8d57a432dbb4f5cd95 d24b9d7f44884c39eecf400c77529d7f6ebfb6ecc7e4731f8d5ff9376b5e1886 f3e4403e3f1e3646ab0373ba93eb0759aa69c5f710011313704ea6c447980232 cf7bbf20466ef7c076db09449952e92aecfb888c647d69036dab97cba77e47c4 30de23a6564e1d89aec3362728fc38f886260017203330d34bea292a707b491b 8e5c56672dc3b7a0f309dfc3d7bceb1e16f1fa32bc43fe0b770655a6d866e7e0 9e40d9911088c28326ae6699ef4ae2f58ff175e96028baab289b13af1f687c00 4d5af321d71289e966ba46f95326563bf4af0876c3c8301978a38fd9b37f691e 57dbe69abcc19bf5d7eb5370e37d04538a54d9f7d73de6b45ca371775814be2e df40cf6594ca6b1d9d9cbfd676eb8c1c8e451fdceeb0f82d291e67a8b5945714 b9f6bd3ba91fbbf31f323d696fe25a7206034603c300aeb1eab79328b86aae2f fe12440886011e93cd34ed78d6a8a163caf2325e3e194da98e9b75ff63c8e71f ce7803e8880eb38de0ae044405129177659303f0a7f02050e24ef4dc17edc16c 922f48ede5f02e1b483aaf70bb0330c65ae0c3ce4d237cbc8fdf054717699962 bb575827e25d6451dab88d0d5d3743d23b3dae08549b28f9ec4792d853044d9c e37d8f08129eb183b9de21a8a1f2a9cf6dc1a88f47981604d6c7b9a2055b41a6 d774972c215d2b71888ceed625a8ffc8896b4eca2eba7bda37e8e54160b69745 5a949eb192c47a82ee29305df440e7eee4fdd948253717527d7238cd4ba370d2 45b08e47d6babdd2c659758ee7c6cd72b6d99777c85231f2c808271c68c6926e 08183fd8009a120e9e2c6518626d8368bfec3ce7621d0be35d9b46668c6b8ef5 0ab20c03582388955393f25eacd2a91973cad437bc6b2eabaa7d307ffc740eed 131e153d4d78069a62f08bb5dcb1195cbc55ad501169fd597e985ad102ba78ff 6e2baf384b4403c3cb1a2bfb955f348a8ab27ecca4b0e6ba171116023696decd 3d6a81aab6f9ef3fae86a1f09a3d8f78adf849d275e06aa19f5fc9d4d21e22db 670931ef4d82607f7ca977d32afdc6833d6024c4552480096e18d7c6297108e8 c0675e7158b35417090a89ee8d369d6f6535da900fddc694d5a7efeb7efd57de da4f6fe4edf2b9d4aef7eaae1aa40f3509614061e1e338c1974e376e11d8862b e20b7500a127a1e03c03f683566c271de536403f67604f0219cd2741c35d8624 780e660c024ae1752a4ba45bf5a570627b9214e1ba4f0d6c6bee99a7fc9d933a 82032bcbaab568cddad0a8d12db0bb8d2e8dcd9607ad2a9afb6de07a260c4347 68fa8a309e58d7a8ee7b38800fc29d03b035bec0fee4ceddb8b4548f145a60c0 1d6217ba9c059d5bd06b15d46d56d148541f0c317c3088f80b378b3cc72d9286 10db2c3fd6ef054cf10bcacec7c8c8f7739be7f9914e83fb792a2cce1371aafa 783eef8fb66112ac85be0697298b0706b5f5506bb91290a54b62ccae33b07d73 a6c13567a396a13f6ae732bff6d40c19686f5037299db6c31736284dd5e02d96 8bb09eb89409158be50147f920006c823ec4a65068d303fcaaf5184923d08c33 af63bba9ee65d34ff4afb24be315ef0ca046108b119688c550e662969e244e5a bed44ceb2fd787304edb9e570b4c52107b5477d72939cb4e671e4d6fbdc569a6 53f1e5304eacdc3774ad07841bf8244ae663b0a7e19ff958bc207b38436638d8 403b14e4ca8ee47dcdc351dc7a568634ef3d2ddefc65a7b30d4ef64b137c413d 1c93abda0c55a2b48ead529851f10b4d16998016a5ff8389c7be5499b2a5044e efb976d59823de16e41fe22c1f22335170d49ddd07008301f18237e523eecf32 04985446cfed21ec59b7539d432c4486218828e5fd67f6c01aca6ac504f25707 23fed667865c3d4024a8703b0e92323cb3827645f60941c5caecb57b9f0b1364 5b5e1a01e87eb5f38476d44434e1f7ac74119adf1c5083873cf222e7e4b3daf9 38887b4f5e94461342cc68ce8dcd96e31f66b1ab0bf1afd34ccdf657539dd2d1 d59146602cb9a3c3978f5fcef49e1432ed8fafcd512f87f69189d95907e6f8c5 4513a01a8d0cee77c4066942092f68907636481d431eaa6751561ca60c51eb79 a26d1efe213eaa8aba5db71fdf0c400b3277fd1110bda912835d8ec35163ce47 1e3a761734ac9981315f72c74ad25dd0daffe4bd58475fd50e612d61e4e2fe8f 03b7c6107a257bb16bc2ac805e692b0d8f15b7bb119b2467899680e747076554 94995f20ae41f8b8903ea05f57f84e6f3107c52b76f7450c793493f02373eab9 2e29b43be0cb1b078c2f9eb58a244f0372691614828f9a82c1b50ce601ec6b15 7235be07c737ec6d197d281e024e4c42941f6b94393284336878330870a46b077699d63bcd522fa161954cce89d17e41519e55b3f57599afb4d0eb6ebcd8150fcaf461890b8668c116c21e78884c27bdaf0158aa84e9b3536ae88c80c1c3bf0512d2502312e3b36ef77865591aafdc2f37fe9e7c7c4835a7aecc867df25ffd0356d904d8666aee245681d59c11c9d81f3add79350d6ce31bc622f92c7eb6850e04fb0726fce591eedd79c44b84680a981deff1d6c5a6cc5cdd49b7d90324c70af3bd2551964627dc90886b881675562548b4bb7961e914fab784fc76a81ecd0a597917974d0bfbd35217978c428f4eae89ffdbe7dab146658691f4278c70320653c103a4a9080579011a927de7a90a01bc2f3a61d04de46da3d882e0c486dd05674c2a31ee1742647056a6d73cb148f413b54e198d3a74a371b1fba2014b390a5094046376db88e52bfbe4e2427823f3773855d964474d27de2e546240b5120f33a0279c6b17a153ef1539b4f0ca9e1b96538d49664bda4629f7d46f4723e60f9a6877a84354cfcc21780671122c85da06569053cb0f1ca44ba09906f38c680f3852e8cc2bfaace66230e5e10d5fbf41ee4ad156139e575a1268638380e7ad0ed13f7f928ed27be47157908c4294257488c37ddbe588ca96d92c2bda69d4cb026244843f860e64dd549d7213b43618d0b5ad25a6e82405894a841a5035fd8e0f94b6114c6887890d7c3a5a072c7cce6fd669510e01b44d3f5b7039e49915ba02edf5e544e70fb6a5d0a994b801b29f8108db8b04e433639e89814fb1f485ac0a4d7542ea8b3dff29965643cac27c1d6764c8facab89087a73530a45c9643c50e25a834d522b83c42276c236a54eed7821e667df5004b178807811c93cb91da07283dccdfeead84a5fb8cbab7994453c896aa386f2fa2c4e07489ac1c7502ac0eaee46f43e4389fb85e05954d56a7bad74dca030ab9174e4c7ac0eee5690dce06fd1061d9b8ee89d3259df65037387d238f6d78aadb249396120ab921df24c90e4a9633bfb1c1a8b8617ee4dca9761821b1d523bc74d00f5cb5a9fcc52289850a5ca66a3a1dc79984303059594c14c1001e50c6976723d0ca67468df9d383e0074bf65bba06eabda28612272c5df9dd76bedffa8f21a4f466ca5e09fd2526100b1da0b7ffe40a066c8fb08605a366cdc2100ae1084e2a0719b3b1ebe2db1473036cb5e4114db273e5aa135a198426723471d492ca2d419515fd04fda810f11500743d7782735f38e0ae13648e3e6448b9ee069fe83a4c676fd00694ea3144dc08472147af0c029007d2a85600b7a6d0307780bd30dffb4e1c4d836faab7bcbe05048eff01f36fd2e7f7159c8c7082d32ae8c2cefd2b2a08008b38a7ba37f8880ac1bbb952335ce5b7c9407856cddee40e9dfd707b5a63b8b8b557c6b93e73d50b220f6be0a3c20da876386ed7b593689583464989497145c33982e2615d0a72095dc53e58694d521a69b1ab49131bf0637ee73cc0491d0b020f4627bf3801d202027cbbefb81407cf03e782d4141445d4a2810329bb578730c6603eba5bec8405f4e7ecd0cef3e79c2f24266643e1d710aaf66bc2f3478aae3ec529ff9dbbee092de2c80fb2927688d0205f50e397ae38987499fee44e3a18d09b5ed367300f00152cb0f913dc3823f1ef429c4bdedb34a0fc745c1d3742cbedf83032c714c30349fc88d5f30abdfc3ee8369c52a14bc865563563c3814bca01f86b87fa17230464ff3d7f3708d1083cf429706f4851855d31236ac2d4aef762efa943a9b9b60e611c8b18aac1ebd2f797bb295690cdab1f3e90770cdbebdfb9965a25bc3d3605b8d5a7bb4dda9e4b1cb3f11338914da02fc4a63567558ad8083f3a08cc90b60f5ae2719a2c007ab50b9e8b101652b8ed498c7100f3ac26927736c3ba715e910549152f5d90125d6815649eb0386eda0ba4431230cc5378ad03919d10fb9dbc0c6f420a7cf6682681f11aac61254cc9c2d46cf63ef32a7709c2886fb46ae3b304691fb9dec8d870e29f973ab2600c0425e21ea70bace4d304f869a0804a3cd305ac5ac86311a6c76eeb50345bc142da44f6f5b20f7f3ae0a397803902d4b1c6003c81f22fd0e1ce75c8353cbabac32a9d1d7ac9a2f63ec3f70c6ed677b771fe0e1fd4802ed7cb9779c4489ffe4fb2661bfc4c0ad0d92c0d79e396c59ab21eef0508fb4f6421aba609416530a238578f8184935af006ef5c2d80c224393efd0f0276a7b7ea9b0961c5eeb3898db43a53ee07f8c222c9c28d851a1f88c0a68c9a0eaefc47c255f09f051785a89f5dfa50fa057a199fb4beffc31865203f56f7200a1aa0e078bbb2ca9f60fc99a1544067a316761c33ef08a62244664ff1fa0a550ca63f2d91ca1089a12be048f02fd0721dda22243671af7012683ddcca6b3112050e9368406823c1126753d91e3f5f1aa6bda03292f9f3c6a9d24e8479c9bf6208c0c8354d217622472783da155c5c433a75dd38e9fbf56d2becbe43ca2638dc07dfc8e3e392f6f4bbf31ff0163e526007ecb1bc6020e23329abd925bec81a4b09cad5e344124b19d8f78c70e9f8b4ce71d6487a387091cc8ff31b9f018bcaf10422e4340d1c368016a735011a4e920197b7a4fb867d78c4229ce48bcc36befa02e4109d754766308379b003c63ed08c30ff72de6d12e81c0b2148cfb2e6828906f06e48afd3082805aa8c9b117eb1669d32b683f347e85cfaac5196eafc465d038194d961a18732420f16068daaf8ca0ec4cf02bdbc1e275ae777efd00c6efb0a6f001c27b4dada5de240a79cad8263b3e79c31e03c131dcd118c4b478efe2808321535923f80f872fac336fcd93eedbb3c1c83279db69ab332e323319fecbe05ffcf86340b583eea793a87cc5bbf5df1dc797b46d3b54ba4905a67052c6d4f011eb248aec4d733bac1750e4e61e1d2fcc398767e4a80f40977b29ea2cbecb3053921caf1067e644d10e9cc951e0d4cfb94cd78b0afb54b605e0c9ebd58ef380a364a889d9f47182ee7b2c06a72a3473ffd34a731c4e127e24bcf971964b4840da83567f821f8615c07d76d1414732b73bc4a6480233c2f3cccc448fc0e0e5b00367aa87400d6694b3c9ee71d6a99cf624973c18bc5cfa3276a355991508eab031a10901a77dd9885e526da5261b0bc882b190f4566eefedc4d06a1f94e561e08927930e269b499973695a15fc1870c08fd352ffd13e3ca398912f0ce781ea2024449292866f273f6c284a8f2a3cb9b3d80279a4add0468460e61d0eaa9387903b11b9de4348fce28406d6249663afd7bfb35d51a46a6d858306ca7a157d99e0a105ce347d65429e9aeea0c85ef0841f722caf485922a8482b8ed80e6071a9f06ae9939c32316a50501991a832288b23d0c99163ffb1309ad2e62cb53c727a80dbbc9bf0622ba4573455c79d7933f21d8e52f510d3aab66103ca350e1e85e110a223e00cdf4800a66685e4644e97bca9969b9faf8ca8975edf87f356f2e0c2a0e71a48dd5d218d5c93ee34ae0e0cf740d7fe1cd71ae7c1c25aec8cba7b2389d019e0848995ac7b97f80516cdefc2d0e43f7e3ba020ae66732b395706b3d8ebf0ba493dbdcf73a6aaaaea515c1a163160b45f8e5edc63a4311befdc82a76b84609d1c46857945b26404cbb510e95eb3980b75e7b5c6c3a8afe4351f9db99cb3b06e38b81969f096ea789c95bf35c08dae9118cde2ce0830b99b2af0f8dc8041b00f2fbb35a851b26c408ab38654299c3a4f33a2bb195af0be8621afffb4bae4a0901a31255f4cc7b535ce27dbe3c7bd6a9dfde2a8c07d10c6fbabffc2f24e38d06d70ad4851dbf7f22c838000899a5ba259e85bd3422d97439e32fba831b8f8009ffed94bb37bc12d3ea04ec139209926798203b8c78a4d18ff6396008d1783d070d98627e63c80e00a726961dec013f9f5714531dbe4380e55bb4b36014f88708b13dd6dd8c0406c14776a934236d8111066364e71bde83491eb835e712550005700c1b15a473d40eb595ed077a43e00138f15c51a81be66024873159c232b00c81c0af8b12e376d40f0d0289727681e57390578398ca97b92beac7b6bd9ee904c6d861b5776db8f85463526a6759b35adba98e342c72de1a5b0df00437b3d109f92194ab88757a23daafa453e1daf37e96496c7b9b9a673bd642647116907b0d21e27a54d72459ff4132c6e6f73a16978b892b9c27e7c5da337df39adb4eb6094a3ab21af2f8a297d814f45bc86c707ce7b894d941809998d8d7c751d2373701e5cb438e68abdc9df7ded644bbc82d7a5139f1f7b2f8b2fc21434d32acca4d0ca2b2045814f692f90eaac5052a17854f4b85325829acaf78512ad9e1534b2805d281d0765f93db714d9af9bb755dda0d5fd0348c7eba38b76693946ead55960f51bee3cdfa8299d2a8f399833a66d9aefa0d5513110b107ab132aec23c36e504b2ebb6616c4e5ef5eecd8ccaee1f946be1244e026ffa22b4a04e45133206980e969e18928030653ad4d4a461e938a07c684af62ad992b0655080e46e9de8d80867e1f415b1b3f410d3d8eef54685d025d8db4b6fd043f6150534065833942305cf16af588b2edf052bd5c1893236ecc688cdfdced8105464f02033fda436ad092d9484a6e4240443fbd19c8ffd39d02d90901588a1afe74cf884e38962993c02bd86bb3f11d6b8f7c88d8275b2321e998915d31179858c19365d490d21d7780c83e98c2ad15b9cfcd060ff0304d63685559a8631f80aa0755ceed225702b300f656061e35e806f63661d62d5b97ceda8a8d30c106c0f207c0da55ca79db9d90a89dc127762b42c15d8d060a0ad1ad660d35a3f40ea9aa6f109fdeba40e71d90a6f524174682e674bbef0d34cc6b5caeb85464d1cd92327f213f613cc48b51e0c090c71e0240c76025426f9973132ff424d0b558aa647c6de2b7d1884fa9d200bc2c18c47db5aad8adb5f30784c682bf805dad97b4859e04510d90dbf24284a06afdd4f1e086c8ae01e2a2e0e6ab32c2f99dec3a7c4b465110e8efe7834da12005bbc970b9ed3e794bf27faad06eacbcd9cab39cd3a443b8af678f9b44e371200f1301a42b9709df07c7a604acbd449435cdd44530561c8c9a7ca1045d666f20c06083e87bc2c0a8a4cd4e77ab13e9a98d7b0bed48eba43cfefd964e2e5a5b60c612826a0e2f2347be44cc78532d139ed185513bc68679cb71602d2c7efcaa80faa019278bb27ceb72469bfbb43f35bfdf87e8a1fd6381a35466ee4ad2986820186a33fa38002c4914885f9d5729cb200aefdbef666caac65a7638b1c0ba43e067db6a4145f8e6db06d2895324681ad9a468d8116ed8bf73e657d6cf44b7f710dcfd1909d2475098701dad3f42b6c831b7ca724453eabfa4da2bd848d3c35f402019ff7a354a3b9974d2582d3b93e078b2d90c6f0772d53420f806f9c3bf7ec0147306456bc9ced08354b129b5719be6bd8f31760d8fe9e40024f4aa444fe0509beaa6ef4b7e586a979a284b8ae982635faefa41b8f2a1078a172ba72fff57e0c6f9452232e3aeb08a9d582cb6113ac1b633e5a79be016cacd71b5b874ad20801d22d2564d2aa7347e3bc4a8d4fc57a37e8f80c96872c47720db5dc3966c84e03190a387f6f5ed4435f57b44bad06db092a1b53b0f2160afd71793125e971ae0a0784bc2d5f38a8d1282a3a5b9d4f3e8d64c18325174a09d24d1c8dbdb7786d0574b63f527f9e6cd1eb154df9a37c8a2be9e6842db981a4c61ebf0f0d7451cc0793d4b24a4bc3c97ad77d5f608dce40989fa2727bc4b689b0e69cdbdbace10803ba64d33acf4e747cef862c55851b86ee4d3af8f97dfdfc682c770201cd7f2d07406538f33ae93ec375d256fb6629f7332b318bcbfe5b33c062adcd96a05e4705b9bee0f9d3828d3c5ad4d6980b3c6fea5a54274ae712ab90a0a4a4f54cc06c05aec225c3f85f16f1dff1a66a7b04a6b242195872df7a3903eeab038f813be308f972e28fac56add2e2b35df415eda1e794ae7c98b8e8145bea50277b41c2600407cc72b3ca1d94709a0188475c9a58cef4a29ed93d768bc02479c8bf1c02ce0de1c07e12006469fd9642effabe484f335f18e82dd78a929ca9ee81b4e9f64a0fdacee1f0b090447cd92ae681500a6a805d13d3b4197013c79878138737019f08bfab5d7e17cf509874012a45842512f9deb6db7286697f638baec0f92464d10cc8a2d8957235c53077f065e172d83ac1123b167ea9e6092d056f0d2f76c3e4098e0841a1e58bb89b7167998c4261692e796a970b56407bfd7162e215e547bc0c0b74266b6f5fc9b2b0283f45621500a38f077edb5b3493bb99979f60ffbfac02295015aa603b55abd452296bad62b67c73953f4375796698b47ed77ce42265075c302974af6db4f642960b4f21eb04c7d3e835874ff376ab1a1ea2803f511f0171db6f072f5702632b114fac604c64a98fa68344a8e8d38ed160a6c818d48501a032bf446b340207f3896af1afc80ae713c15c3cee3b79e8f94ac1fd0622950505f4936eebce896f2f0fa340fbc3cb6c4ac2212fa759127518283d290527a8009822defc76afc302345501039a55b6b8717584225aae488176b53f9f250a830a70e9c970d9fac103c1cda1ac3fc2c1d14ae09d25880d64ad04fd8f1a9ba9980f22b4767afaaae13f966e05c7523358a70e3fe69ed9599dbd3a4d34b66a4f280887ebd69df5d55aacb8bef4c8bfdc02706869484f69f12a445d7383602422d90ca7c524f195ffbca8097a09235f07ad358e66d9f04af49c4b3c54683b9ebc85070e57651d4b9e53c57cdfe3626a3876d18df2e3fdbdb780ac2e46b8a1c186630e66b0d67fa4abf5a7e93d76bd67c1c84dd6d40759e042d464eeac687b23d42c0eaa0585b884e2af4b80714a349c20ea96840042a24c7aae3718375f88e15bb20b3de9855d10d0f037761b98953bf9fa834055282a44f439d1fcad96f048027708338ecf19937466f325ab343b77f3672dca7df9bb1a82f4393790ffdf790cac04529ad0e0db9c9f99bae6a371d01ce0059beaedc0eacc514ad737602ec2b3a40d33cb49b28249cfbfb238acff5e7b25a84e83dce7b77e360a1dccdfb461b5130f0531d22878f5f918e795beee3b3c7f33b8c9ae283c367ce955bfae6f4b7e190524659486e6ec61d542d5e3c2f0b764163415e218d4e1127f21fb7aa3884f930bd1e654c1199af2fa2810ba874fd462fed3ca790bba973b96b208397a8893cc001b7a3714fa03831b011c8494a006b3977b92fedf8b5c77478ccb4f6fd7b34600b78818ad865bd55f0c429e577bce26726b388c9ce43709a8061d055e9c7eb50733eaafe3136605c475dc347c54f27cd521fd9e422d6e99407570a9c38c7af302217988921c684618229e6c4e659853e3b0bed0fe58910b070407653a4714c60c2d0b0f98cba194d34dc2e371ff7a35a20c24e5c2493383da15b9097854c1de05e6e80789228bc6efb1fe6718b1a26e5c947b22a255846b4831a24991d266f40511223bc61c46abcda08c074776234fb246b9e7b52bf3c00dfe7e0b8924da3a0daed2ea5eb0bb2a52c9b0dce4415fff07e083df806ec58ebdba40f6449341780172131da8b1e8eaac3f239a74f8463833b0036b33d119a691c4700209717ad10c52785fc34cf03f69ca47c694f643429792735dfb12fe38845ced331e1f3e9d07dadf524e9e375de455c0178e95ff6b8d6d2d5af75220451a6d19da1d099d4d02cbd4c022441b4e255b3dd131ff607e62595637a0fde3aaf61e1ba9deff6bf3084564a3153721ccec7bf722244ddfef9343f3a6178a4390d3664cec7a6e8b3b0fc9877b4f33219b77562f593dcf36d786cadd8312b313093bb809e19f1c02620a670becc1b26b123c91543772e6bad7aeb39b2bb5cf1e4e6ce629650010fc5c0367d002be3708a941e459efcb5d9e5b3566766973e6cfb510cf73de9d565822083b16ec4d0c96d64d3f1faaa92154049742b49ba4de7051355605335cdfdeec06ce7ba0f43c1424ecaccaec2a0bec9379868b1e85f6e0e46488cfd4ed15e72900b404b4c3f76f0b770a7229b25402602af7a54aee9150e8349e0a02e9be2740056987e73ee41d451bf0fd470df92e3a370f2fbdbb8dd1c2b285627b107e1db905ead02a0faba8524bb87788abb573f967615df801275892afeef2847f4ef46401c993e25e026a2c1731d05fba093a6edd346a7a1105ac0bd61a0ce2b75ce0dd06b93dd8d12d4f8b1eb76426d3f61bec79b3ee7826c2c8a61fd1c7b1bd8c3169066166c39efdd83086699bef9b18e9f61a96da09830fc76ffea8f536c507670f00a1a73d8b23fccb0c455f783eb195728b1d8787f523999b7f056751a319843c00d14944007f56d7772c4029c1f62b1ca7c511b21e1fb0111457bc0f590850b80f469afc6ebcbd5361c1a560702ab0a09e49f4e1e09a853244bf5fbeded0373309c5df2ef5fdf0e170cc68d54d6a400c9bea1812a9bd5f45c954931ec84645820a8aa0dd0487b5c9e7ce2f7901497f9ec02e2bb5b9c2e08fedcbd447153ef0fe0eb7e9a34200bf3f2caeb0f1b56b4197c7d29d43e6886966ac08af301b45e9e30adcdc8ea807f01ac3dcc030c12c1b807c2be15413e9ee4192607de57ad706f50c14717f585a374e8650e6b8abc3dda1a7141b63ef4a866ea9464283380dde5d08d02ea75cdbce782e6f6860f57cffddf782688e36f107aba8248ac7f8edb1a2020f0936d5bee4e65353154ad972d78d8ebadf5e44e549b31d8a2be530614f2306e509ec4793e836343236c4c7cc6befe8e2f4be6296c94db93d35b9d16ab5f900d615e3a505422e2d5ef15ecc7936963dfca00c498c81f722773ecdc82449d50ac0ddde8e99ab02fa04bd8eb2809f2c609b109c8a477c25c90741539635e3b904649427be37c838d8a84c35e64cdd0250e9ffc2aa5f2174b2dd463de273cc8e013c4563623a38bb1b698289e2283f61d862fe555272dfe000635d79920b7e6e0fcddc2df9c9d89c14c9c902e0c4a01a3a0bbc806bb9e195ba0e5fd02798c43c0224e6cad06f7386fa2db9c6e7921cd5e93c30139d091d1b95f9df457bdccfd600c84446e056df906fae17df030a5865c57d250bf817353a4fc612ef2028413b0886faadcc83ff0e0ec4df0662593f8c137cab3286ecdca7f0968e94d7b48c75012ead64efb2a159e6c99e3e7fb4b9e21b5db5083f0c8ea37e4853d83f23aa44096ada44272af4eb3309d729cccde6cd719877cdc62df3a0b8085936bfad30140f628125eb45458a714f5d947c586b0502ca341734b7050c68963b416cb51c770e44e704d774a0a9bb32cd690413babafecae31efd6d02540343360a262a14f20271378bd4db4f101ffad77a6ccbac0901353e5ece6cfff69728ac8306ab05010f372a08d3106bc1b6c0d7e981650411fbf04aa69bdf7f14a1a4504f4917d4d60ac30e737dd9e3a369b45e65c2c8993809eafef292a32310b428526a5da6b62d0588f6d10ac492e0280bc62d38834805eedfc313ee7f356302987ca7bbe23dca0497995d75911c61b4a9a6dae8e7f640e6e6b20f4fa2ddd9120c67da43c8e4a906d374979de3d8c92545d5383d8c21e5d328c51c9a9d23f97283c5115049adba053f02dde89331c76c48b5bc1af676ccbeaa2960bf393ad675a382919815105c05d910811982b59812bc4fa4cb6f8b8cf8c2ec74416fbb0ee65aa8d44544e3eb03e0d3c7905a24f6eff6d7b365c6bd406b49775d9e3cda765b681ccbf83a6430077a6914bc43797012d4a11e36c1acbbb7002d8ad5d46c2ddd0a956e9d691e2b09 true -check_ring_signature 7d4169403946e59e0ef5899549b0b246e22e8f932bedd1b3304ac44cb20483bb fa432be34e3972778a6c12b87d6c7f0b1408a85700ff03df632f401b6ed72981 1 7ac25e0b19c9934df5db43be40192aaecd902f2973b8783d483eeaf53b47ede5 1f38fdd08791a6cff3a671cc250ee9db9cfba1b7eaa7bd6fe2b148860f0c6b0fa285074a3de31624aca04c34a7f79b981c07d1715b242e3d239ba5f13755220a false -check_ring_signature b3e0d4eabf5789c157298e2432126f26ef594b57f6c6e0129f7984bea6ac7bc1 ea6673a762308b0947b9af24987ec65fa9b27f5408ad17ad2ba61573e1d2e124 4 b59d19e1bd087b5e47005f59603f585d59aa782377a52844b1dd8b6fca18d95d 7f995c1d28ddc416443d40d59492e0e98d7f15aea5a0487f026dd97f6b4a9d65 ab51727c9256ac228cce399a7b8ef41cb0b91eee953ea7f2c8b9e875d4529321 1fcb56dbf4380fffaf8dbd6bcc80c0a649825c54e27ac32e865797fb53015a4f 82bc001a933742cbbcc1bd4cfd82b1dbea584a030c29f0f6c2094f41e2463209dae4fc5edabb23d6bd1eb520661dfa3ed0f02c452af03989642b3b6c27896e0ca270dfae10ec07e9dc5e3b02644fad589f318728c69bf86c6e3e8823af5d26063ba68f4c9db008d7e818355d8211fef5f751541db9c2621509c0a1c06c682b0132b022ffbe7da25981248879f0b4b01a481e2a652697bac4e6604b96924aa20c3e8fd598d76360f74f9d66a5592137bc2cc5f5494e8d62d398d6840d7e19570bb940093c451d4d7044e2bde626405bc2f893261538e1f577948c1574d8d18a05a81a5c8d339aa7a6ccc176fe5e9b2ba15295cef25c27204450077c9e6ff4ec09 false -check_ring_signature 0854476fb317fced0b45ed51652b792f05a55846d2c96990bfa325b2393a9406 4dad540eefd57439d00d7c350a1e6f2008ac00b8274b94013321686964a99f11 3 efdc15ba8b040b2da14205161fd088d3aceb6173588dcfa04766b0b92f465906 d7ee3942814f0b5e82f28eba8a358eb47f4be99bcb685b51a7d6c2258acadd64 0a90ca681160f6dff3d9efe62d3ecd7e49cbf8f7f409c86e3e1ec2433d9eaf27 e9d4ddbf8a01487c1161d034f7c843de54a3bb9cc331fb5130dc5a591109cc0e66061116d294bf14424a127831c51fc2789acd74b9fc4bb6e370b5100481130052320559cf812736f75609a044396c776392592431a92de93a38892758dc3d0d31a2833343395eb22b0fe843bac30681dd0e17f445b8b2f0c61db4460963f60d1ea74e5496dc0c87754f4416de14aff0f7b4d45f249e8eaa47b971855821e605b4a78e66373d3564b7fd7d4f831e5a74604c918d982def6d587b1ba1ad17ea01 false -check_ring_signature a8fc9f82570e1f173f9bbcb028005210190a6e5e2ad688c74fbf27ad8f13604a 61d721986c851c37ffe74707450e31861b78b02dbd6da6f9f68b18cc211243af 26 ba06250d12aae879081806f9ef814d572d6b066f94c8061d659ddeb6f00662a2 26bd91a55edbbb1f2b354aa22a9abab185016d281e37553138ed8b1bbf0929ec 9cf4d769a4f1d1a9218240c7fc9b99dcf08a7aef882899ebfaacbd67def3f506 4a3f584bfb239add1cd3f5cb1ec983bbb82952142cf99dc36814ea5cb7fa009b de11a90c64068bda54132de1a0547066fe89d6ce39590fe01529a3bfb21e317a 5763f08cf38cd5d91e3dde1e638f9e47b66004c020cee73ce2b277a3efde649f c9e234d45e47e4d53ecf83cd905851e44ad43f502d2548f6c2bdf40e25288b4e fd353e84e0911b10d0799dde9c9d3ac835c563321f42427cf73b67542c661b0c f9cf029b191a6db87fbdfe9617f323bd3abffa534420d3abc453342886a28b52 dfa6b3f9010da8866f3a7bff5a0340ec9ca33ced410c9b53aad592dd0eaeb9b0 9445f5d745156f5d3c59c690ac8ac1b96af5d1e630f6e3f6f6c42eaa960ca1ba 0125ae4eb7556e745acbacb5fa9ef67c24bddde2a29f98a22728dc7ccd4b5bce bc7587cfe3ccb6d9abbafdfb0e675102f07c632a11b71ce082a8600ef2d3f5ac 54863a1d65a33601281fefb5e19c00407fa85b6efa65373ad10ccb7fa7f96e2a 39906434ae7e63b39e5dce195e63c78ee5cb515992b709626db2947e82f97435 0bf1550be6019056b7538c05b99249747c13ee78c44add2a6ec798ffdad4c881 4dabee2af5a9ae5b3f1ea7e2ca5654a353075b9b89c25712c4ce221d6748942c bc1b7a86607332bb164830a8acbb0ce6cefcdabb6d8c90c7e720ed9d6218a61f 3f93b84033e6146b47cd8f92ca962b965d48b4be0a5710097a95d103394e681e 84e08aa59a70dbc97611112e5564e2fc63954637fcd4fc657c7740acf0317f1d 5fb2eac2b0cfbf46a1a3eb2038cfec86a8a66486d520d4684aa1e68db718c1fa 6d96652d7daf5de2efccfd7c78eab370ae19d1ab482adbcf22f2247a5c84f80c d5ba47004267f9ec2c66d76696a65d1031633e0a32b949b3ccab770cba919bee 4e52431c5ceb7acc739766df3fdb84ebb627f6c4249eaf7b308664d2aa40f12b ba1877262eb61391b36f7a18aacea950a4619ecb5b77c3cc404cae4e01f7135e 3a73ed0b5279ceea3fc13ee51ce6f55cb9e101066009cefbd27653948b655bd6 2e32a6c45e1871770860e0c086a1b4be9e39f4dacf79d9cbf18edb70511a2901a442c05519d4069b0cab8b70cee2d1b6264b3bbb551abfd1ed664581aae109071fac43efb883becd81f14ce3073dbc349f68003c53e63b2dffbae47d7a9e980313a8e3c755dc924f8cf3535664919994ea9d780ad84695247e2842698970500999f007d70f97766bee6b3b32e8700982dd1801a080c69627281d12f85a92f600e8b64451e40b067fed83a58c63138f7b7f40f33372394d46a276d46a5f273d09e8bdcab774a26e4f4d592770c9b004af27d9631bea17b9ea9944327dcea23f0ca787558ec267e8305f3933b451d34e8cee79800cec06da5173796d0841d801072c84b26b9f3edb8f1e79b4bdd16cdbd19bc7d7f15e63d00bc3aafaf18bd169063e0d3172775957da4d978da6e55ce12f3c050e3b020b7836cc6ed37069601702dfa30624710c7e45c1e418a175dfcd77815890b36f0a051c0db56ccffc9c400981d0cd6e63081ce70c8fe468b5592da4be5bb3b23c76adceef671218c174480db53792facbb215628426c1589de9579b046b63ee8af46b4994de93d221b2c20c8da6a330630e2a1a9fa9fd95659e4af1c8e0da11480fd60e028592cd013f58001710e0c67eb4044f9c5dd9db7e8252ae636cb657204e8a0a8bdab2e651ec9d00415cb634c689e5de5e3f86af0c239d283a9349bc7414da70114a98e58558130569f6ed1dae12c5cbacb22819ed35b58dcc20fadac92059ac5801f69b5426ba0bb37674652fc1542676564b80ea90f0c9344b3b0259b43ebb587680820971e80ba8cc27695359a08baee693095755b6cafe6c248229a25c06264d1949e289cc07159c162e481ac700b6091b6511f3eaba604076df8b6bc90e5ebaf949c304ed05617d914575a567a89d43f90030bb49cb6f0a70c3109122c7c4e267866e46c807f048ab2a5988b070bb0e1a1a2147231e23e85d3d023a68ae02f272fdac877704809368120b03604f72494a7d715b5d366cb04f353f1130205ec98891bd98150423d50a802c718a3d60a8d58389f3ddea366fc8e7ae6259b0803f6cf824c034036beb211e9396e45dbd540b21083c9ee4c7241ff5e1a4a320040e0efeb916ad0faae33f81c6a1f4702e6c3bf92784f8fb40e97aa980bee49b4c017214bdaa8501c9e803e89d694a83fc81658e1f912bc75bdb2fc7cfe931d48b5c2c4d4b0ad606ce14d70ff712427673b0823c770c69eed1825cde1458045507258ee710a5f1011b2d4d47f233284e7a66d38a84dff2a2be387ee3f102be8e98ae32ca663984084c7ce607f883ed7e4ad3aa1defb0e1a36c9918014be84365a27a760201210f0a9d8adc1e11e9415c42b8d58d3f373c71be6bd4b1e54e749418027ae9c063a70af3e40cb362a4108fd61762cc931f3f341b6080c4a717804d4926eda709de890ed77de1515bd4a317f25e556f3336bc870bb82abdc746ae45a199ec755451f205d91ea97b9392fa272bf43478831421b67b05364fb681a8608b50bc8f5fd3920414972dd5a878829bbbd4e06cc34975bdf0633ff961f9df12902d2125cc34880ab509996b6c516bf83a9c9df0f6c74587cbd655536f9bb125f928ce35df86a304cbc71f502a9a02ba7a32bfab8f400a4d08db4ab8cf384baf8be41eb2e1830d04204f100b7d1a542c09cafa815e001fe98967ad144bd52eeb0e4806317971b406e37d24a93d166d722ffa6e76f6871235ee8e4f47714e26dbd830c1bcfa26f1028ea3f53feba7b9129caddb6ac1a441c8d238322c93e15f2379d098cda49cb600fe3231bf81995738b179321683acef5961bba71b05b46c5ed595648ddc2b5c06e497d4df813ba0b88750c6003b56ab854074fdd5f2f87ef64f9a0589adb20a05201286317cd2bc9346eeebe949fa4d2b15531b338d317fc601d7d53469202207e62c58b754ec7a7d1e15ae22cfb0419c7bd00fe9c2c04ca29ff67ef168dbe30c54f683a909925cf608225cbefa81f6e0018aeedb83bc42844f689203d75b250a8cf644b440295396081b1d9ec1c69651295c8f8a935cfc6ac4ceaf78a448600d64eb9577b0d0a8f87aee2ef4822d0fd91fcd3c105c82ec9ce76dfbd2cb307c03a83ed1b2742a1adcfd3fdf79e88dc39a63d6de276f92d39be875efd79cb31705e185cc164f8640c17d8b5faed187e17e166300ba433a606494b88e475c363c0f98da9de14d654298f578982b9754ca44e53c18324d45de6459471523029ff204721d63db808fbbad8332c842b4626fff02b1bdab95de447b711ee6341ebb2d09de0b0e1943dda4993ea6dd369328c5ca11950c087e1d2b26542740a06a76f108 false -check_ring_signature 9ff33245c6d5bc362eca17934c470340dc155cdaebd32d10ee8eeb3c27ecdc3b 13e132c97f3726ff537fafe931c1076db732a2e94a4430d6ac1517fa4249e8c4 19 54fe44f25800661e88bbda1a4e90aa8f16c07a7027c6aec87d5ccea1bb315ffd a3bd87e15012a501eb8a480817996deaec5f59f2d7684fe4f4dc2248f6a4bbef 1e7ffe3e8631bf22c9bb0c75dd613b92e3c37e6ba85abc96e7ceb54a798fd87c 8ab32ab0823bdca6d41ef595b8f27dd6e834389d9f1bb6d20b219240b1b4f517 73f6e7650c67cc2de802ca87ceda955d2ff7e36df2a4d680932cf198240fe29b c7c0284d3e2b6e90b7c5dab2e6742626ff77383f271da709ef685877b5820705 e84097859de34353d1db10c625a0eb41b090517d784d0edaa3c4a2a9a668a2b1 7dc2a363c890d1dad657cb0645634cb38a14161605286b2c11a6f31dded01a01 19fd2133482f4a7cf7a8f263512ae9c3c8dc6908c177d174e865d1310b4bf013 ab4a1f389c52344b3b60b94e8f24e3b6f5f3f8ad35ab975a9a442075d0b8e68a 72db5d7eb86ede9e113638c4dce68983cff6da33f9f90c2896d2c63b95c29a1b d1d65bfc72d92f10b0d7577b7c438e7e2bfb086feafbcbf469f978ed1e211f56 953518290fa37e89e1167dd62bd64133113cc02d9a1706fd76c5be0abf764fa0 0d25fb25999bc9dba9622a46c96cf2e5c1fa7d931b790e7b58ad567659cbe5ec 8196787aab895bbdb262b72a28737c2681971b1c8b6191dc08f55f7024fc71c4 03745b5b79253086a2f4467624f6e3e85f5acab59e4ac48dd6f95bb32088199d adf6ddbc9cc2a2e54109ad405516937adf2caf9f054ece13f518ce74e9453d31 e721339935f210902acde3c483075674ceb8d82de10e9a89ac60fef0b829fc2d 116c814667d9baea940d812c5c4e4694a193086b3a0f29f4c555128beec06c73 e719e69b62f9201ffe0bf0016cdf741ce831058387c907cf8ca073a9c55d7d0f84ad3ba9e8116098299125299ccfc000a06c92cef9dbd44b2d60c4ca60ecfa0cc3418b747a2b7d2561e690d6640c033ca87415f9ddd60784205d337871d44b0ec3562cde8a9d626372bb2fc0ddc2c084e7252e6dd7e88a0bf546ca1484a7d80fe68aa74e0e424f5041be00a2d1bb044da51b7036a287bb28c91067a55a3d1205d4e659d17afae8a1ee57d66356da2eb5c66149c68f1759b576503b31bea45d05d9c5cc01a02a68e6959adfd94ce5e6e163373899c10749dd1054e44442f2f90b56e132dd9cc75584981bef34ab117f403bc5f70790d302e799e123bc7c5bee0f6cd1d5e2d45f7ebe957c2216c53d4e9f5daca06b1275a84f79c19861b807470783d07988aa00e3474220ff7e1f8adeded7457b76c242df36064d628776ac5f01080100eb62e4e197172784507e5115392beb4556399171a88f1586eea8664b0056af29c153fb5e22b6a3e3bea6b14e36237a6586f1e1e24459b05c08033dba0ba1e6a8ae41ee96422c3d064e841cc05989f242d902f51a18c1e43fdd5f2ee30c9d0e7093934723eed0d42f5669be92a248d4d8c90dbafdcf995c63a88e67c109ef3f73d2b14b96344668641f760e746975d2e7eeefa7d7f984eaa3825cf8d30ef7c33f08a12e3063897032f716f717287c63b5267fbe942ce812e2ed4151e20db601a8a03bfe3fdf039e31ce2c131a3ced52913665e30c45898cacadbf11b3053b1f26e83e68be4b458389317801016250af2597d6990f22cbaaad578b97e007af238dfdf5e4bb5ddf953629e9e049750b43b9d6f4d98c79979dd41c7569020025568e7c083088fe4b700c1ad5f1268cf53cafceb4a928d8302540159591f608c3c35c4db17399edf3df289163fd77144e4ac97baaffc25cd51acf5dd67864037f77fef77afa4a661e5d12cf258895bf65bd79ab213029d1198640957eedc1098c48163412f4e55507369bde30a9cabe96bb76d0fede34c8c5ec9fe94e23160bccce5d3cfe3cb237c4e7c20fba1799b6e03e8be9bcf1b46a6d1c2518d8179008950af46c7a9a63b21eb8dabff3c1d546c7fa55f72cfbe50ebd68c395bb6d7c07bbc15eba5b84c1641033e1fe9e9956489eb2d1d9e9ed62aec346f3a0540fab09df88fb6e34276f6e0ab3d447374798324a4d7b85aca06798cfda943943c8f10fea0e0ed77a7eb544eae93d4ec174cdae8ea1daabf4d745aa57c44d05102d780ec135d4d69c5f0b29d44025e2da8ba41ac7add38f779bd7035327e18118532a045005ab9fbac8342f723ad328ce6559271ba876f236bfc9d5fae1dbf6a701960270fdd284a6aaa1f1e83d1d0d06b6533f4ca004ac840009a494a75c8a6981c203ecae545e54ad3dc908b78d921a26ca99a356ffd8f5e17088ffdfe0af8043cc0875752a64a4f057a743629a7e92ef39f9b14770fa9a353ba85144eee26143e70b4a0ae2598bb1999cf55e6ab6534c63b04f4a7599b4cf9e40330b3d8ed062ff0e1112c70d33e05c9675d431786359b2e05e15a9c8d6e6c06e86d0318b9736860b2a5c320f0178ff8b5b4c4c17c82f439086d85e64d586ddda4cc500a430128b0eab2778f7c4643114f786ffc0f02572206c6df94e19a58a719781436f6dc9d203bd5cd0cc686f45304daf22cf15c00752843e14beead0d44bcff50bf3ee719209 false -check_ring_signature 6cba43a09396cfe248257b93019a5ce8b630f9ee6f8219495fc3d90191f29817 edc3e17fec64012ff7447990e01a7afaac23c5c01deed20d76790f5097c76810 161 2ef3f9a23eebcf7cc45752c7e112f59c50c1b04bf8d03b31c7de6e8492b17144 305319f524ba9d1ec43ba75d02d0b1904a2b7c0380f442abf7bf9fb55ef5b7a8 50531748a434b225650a4dcd35ddf3930e21c141682dc10d3841858faa786079 ee0a118077b7bd1e0187ca1053256e07b33b1d5d81d1f29472d4969c76ae6821 7b40a4267283a9f0a3ccfa7698e0547984b043b99517e9abe967242a49d813f8 638da1b3106398323fcee0c3316f6097ba311b28b5a99914e27df7505c1ab11a 5ee700f30a17e1bfbf7e65546774e68c3d4fc527f9523fbd0e738c384ef96b00 f8726aca1664d0c68cba2d0803103be0cb040a8ec80e77ee2825c55742cfdff7 78c1b5f74f769b0427c4c29afc3480949cb3249a95ac48e7e3e1d78adadfd4cc e79e9e473037fe992eb21dbe8d31b42e37497effbde37bd672963dafa936e785 bd57383a968e6539182b3647983d499b15e218d9868b0feedd247093f625850e 4ed6885706f584bad95ac0c73708fd8ffa273e68769de4e36dc67b5c51e406bb 76be992a111f32549c555198dbeaba79b5f047bf4848fdd1ac84209ce27faa28 9e07d998b50c0db2a22691f8caf1988d557d7bf820444d0a5b19574cf1cc5aa7 3e2fd07b0caeb0812ce8b483d684814ab79b15af003a26e9651444d97bb4d941 cdc3e8d0684755636d3100cb763f90f5e6f51aa3064a76a0d1499d72d8eac0c3 e213d99492b9ee2d7123598749ceb2d26a8db99fa66c629662e9247512096d33 2f72002cee6e99f5cfd9d01528ee1aa1d25c0d86083ea6330a7ad11f7a32ab55 1fd92bdb4c1d60b87cb7cbd796a3c153c756f15c6733ad45a16d7ded5432e335 3111ea84dedcb6433c5634c621651f5421b1a63708de691f23a1d1ec50aa74e7 8afcff5032edebe0848e836a4b742331be7a5bfaf341962c3050cd7f9798a1a1 0796a484ab3608abde6a2532c0ab688e6fd05394ad8580a1df76cde398ee71d7 ee5c203c8a8e7f1ded65d96b56a978f1b07eae8cd2c43407e05ff29025b8a891 46d6993ce2918ead0ba630bc675f9333735059d22d681526faa82d62e0a4827f 6a25e12a585fbf0469536307b924cc51346d2d9422391141c88893f4595f6a8e 43de62aab4dffa5e188bf276ed7e4ef468bc2569577e77b0453547c3d4dca471 a664c807ec7344aee6a9bd28dc284c1cae7e06d2e1e592787d0eb4e873222b97 7919ffdb59bd4489c47b84396d6149b6619712340cabeb53e8dc4166dfb07b51 3a0035ca5e98b52820aabdb1c2a20d6f47c6a50ef98b10a3aafa545b2b350931 fe687138e45e1477c1c6edcc965762610aafbffcc4c403baf0ed2e8ade8849b2 51c6fccae0ab41163d925f5b9526a6842f5bf33d9f527c20b11cbc9045740014 fca8cdb3950f78bbbcd0568bdadaaee2d228803555fea33a5cb34137dc9267e3 9314c18cd62cc7507863704b057602f1242656e37451c4f3247ab3e5659e5c82 acf0623a4e5afebf83775e5e07f6f421af722c17724099877c79d83b71621c11 b4cd01bf184b7daa008ca37b234e8f26ba133846db80dbef389d948988bd2f7e ac0bb95d53db7af2b86529a9bec39be1898ec99b1c9530d6c3c7ba37983692b0 b9ae9c0f31d91de2080c55f82f17ea0480def8304f98cd2986f6e691384124e5 61874f7d4443cf096fa09bc93ef2f8dbb129ddb31a023cffde4db9d9d57dde07 830e7b866c3554180d979cead5f5a02856bab4e48ba0ebf20e088fc0e607d206 b132c5ba6d985d31a438834e4837d1ebf99cb0aa651464200899d943dac96e7b acda751ac2bf355223c6b7f56ba2917a08c6ecfac95c1738ab22ce46ae8dcc4f f02402e3ab2847cdc6f5b3dfac29d9b45cadd73ce3e35299afc49d140758dfc4 cc7fa5c4a29feede6355b7dad63ff305762b56b720c6830ce9e761e9d2899565 4ebbefbc1d59fe6ac1c28976f805621db55f46f2ab917c66fb57760bbdc92e33 5c3235f57d18617c1042eaf42ca9cacce901b2ab635a772de727c3e83433db0a 408fcfb1f89d0b90d5b7a9119df036b88745162085b53b4412f648d91880d3ea 522cf36cc73b1229610e7e4e2850d3a3fe7121ede3a7d8db64a947ddb4a9135e 7f8b03b7d227d4e6c05502c775361f9ec690a212fd46a42d9c7c70dcaacfd64a 4495b1b61360f442c3364e615bc42c893a58534e30156693049afd780b6e0bc8 641c8f050ba6c304c73854a0ead4bb56fabbd6da3ff5cf57f4fd81ecf4d170c5 9078d953898f8acaf5e4910c3d7321fd0718cc4c767f5babb4cba90243ee56e9 9ff73e726884bf99961d8b0f698edcd4b5e30c116ff0c5080ba5956ec84fc81b df29ea8683abf66a550a16d374c20344c41703d2a127c79f96336f7ca1f9684c 7b8cda818edd0b44ce3882ffbcbcfe995ea0d6a84c437636265ef38bcc3f898b 1fc91c860f9a6c48ad3266fe3b8c8c22a4e4fca839ac2535b354effba347993f 261736fd224f5764170484db4296c999acfd332f9b3fcb4c2ca18f8017c25441 bd0305be085c45f3403b4968a7896cc2cdc5bbfa83ccb966e72afb1d6f6bb961 4d88733a854f7830bb6a59021c3e6b92f4e5bcf782072cc2f16c2b2347a54655 fa3649a1edc94699f326f66dbc883f3f259bb49d54783c116db838be47eeefae 99af9e604e8443535ec191c311231223ff743d723efbe8df95420a8ab1c2e432 8bf695d85831c04a4fec8edb8d4746a6241923792058686684cebc81368b4314 5be41b65f6a3fd319636ab09159f0acc0e566d2f6a129ab273e3b129c153a742 3274ac725f8c392f9b3656c5fa5e0f923ef6ae267f09e4a2d465400991682f9e 1870bb0534fecd6da135ffc8284b69f6ee147c9cfc345fa6f2b99074910d803e 3ac0eee73dc0dcdedd95763d298b0e896ef145d7cc29d1478a8cc7ffc15e931a 2ec0898c98293b69305b2874c369bb309f85ac352f4f83f189dbca81bbbd31b1 39521955eb937ec2c9461169c0a08ae578945685721e9c9b06678a7d5c054d39 367c7538e00728f92f42a36890387d96fcece9f97fec74864ecaa947756b7801 4b6b386a5183fc6680e523b2e3f6d79bbec85017d42293e5e0256b2cdb31913c a8b13eb0fa0452dcce9b60533ee147e8d0c7e7603024cce86d463581a2e7dff1 54a139c2102abd74256df6b513005d1ee115548028f3a2378adccf4b4dca6f0c 484ef2385add1c42999439bf1de2ac7de6afcb10310a1fed671a57744c1d0d12 480552690b013248eb6b5e9ab368820f2d77d377b42045ca96e21b37a67773fa 982e0af0174bc91e16cffe347112d53d2acaf7b5d9c420adf1cbb4db318236f4 c06f00cbcc07a915772d46d27a8d81356777431fa3d20bad4aea6606b6112f8c fd3c40f0ae462ecb7ddd87f51a99f4a419a7ef1852374803b28f5a028ffad306 3c2d1e0e7873533b5df4ae53c3380693ff0ce2107e37ee2812b5e4bc961bfc91 ecae6d1185f01344047b33baa1063af28f7a58f4262ca4d07ebc256453cef506 473367759ac413ea8f467497743c141a00972f674cbaf85d7b0d95c18193e682 bba8659a6cfe6cd1e8dabbf8a56b9805820a0b17a96c680d270ea3685944699d 780d31447a082f3649f0f062f5e03acacbbf3f4dd95d31d7e2f14e663abd272a 5e77d83998b223bd21fa1bad5d43cb0a11d0f7b93aa3003e7b19d6dc9d8728bf 513ec2655cc88fb9ee61d3f1c473fb21420d4f7ba16ffa535dfc9beedbbca86c 26353c541b0efcb7df4fde06330c3a2a9d9cba32e9038a29ac72c7d6b3f0eab7 a78fde441ba9ac150cbfd20b34cc8464a600d279aa2b524194eaec7345a5aeb5 b30a80d3356088c86b37d0c09d4d28932951ad2a1656297dd07bbc96c3edf1bf 1fb1a5ed2d8892ea55b4279c5892e220423c50a8c69b06c57f77cb6809f9d707 d75183552e91c5450603374eb19a027477ed521d44753f767f206718c3dfe551 31ae359d82b2fc59e4969753aa9ebb166226309813bb0b6b711fc0dee9ba75e1 7de34b9ac7a1549f78a3359625b4ddcdce68a2021b7f382efe6fc70c36f753c8 6e764922c2fe879d4f721f4876a5844b5a1dcb2d8dfdd25f9dfa1b3aae7fdd8a c3f0955337ae8f62eb9f58867f84cf20170c40e98c23accb4b24eb68ccc1474f 7055245ed916decce06fe925d9a36e1b17b4236fa76dbaea6675c1b535723f61 7f0ce33214b5083e2d05adc8f4acabb7e265b99b531d7b869d4611da356b6b61 c43faebda7155b821e9fcb01767384826c2a232da4634b9b15f889a31aeeae2b 7f15437387d84adc6a465739ed303448c4c8dfefb63a14e000585f0ab9ce07e9 ab7748644c19d3e90f3301084dccde5ff5719a10490a3580de2e21a36c77df68 355f155e356f1b9f159a50fe34dde13937f7f3fba6263c9af88fe14bf953de22 9ebe425e396064b0e35ccc5682066b6d6e9e96e733800d33090ce7501a1b9f9f a201bea0a7ec2973a820f4b82312e14cb063b3b210edd4de901fe17138c83442 da4d49f103211c84e64dffefadd5cddab3ff9ef977869c69110f92fc856ad277 b11696518406a90dc66a7200a67d8b85df689fb49cf46258a6ba3d3a339c8b74 14d73c7ddf88858501df23a73aeb1277932389d60af03c1556f1708eb0d51782 fbaecaab401ba32dec414d611842430296bec53291e97eada4b12b214b0da0f6 7bd2cf224a2dfe381d17acdeeb6baace6ab2269fbd6124fae3ab951b9afe2f15 886109c9990cc8c6231e8b139e79af3ef8baee02c2b3e459f35b2fd08ea2ca57 949d9d7e74ec4bc6137693d644fb92c1a4ea491bdafed691ab615cb3a3bdabb3 667d194d45767c3a19dc8f5f6ac2125ca3b156365e631b2c42fe54d7903f3019 b91bc099a0189533d7295c16b74a9d7c322bdd6c2c4d82e546bd17fb0e0ebe40 27657ba04389739da89432cc43b7cd774b2c803899724b79747e2c3bffaa06bc 2122e9d361f57f59910dd8ea2000db0ba84a9d504e0f9adc083693321c4fb220 7c1716e716f9579ebf6dec4be869683fdca3d4790a8e77a042a87c922f66efee 3ce33f58cd7fc2338e1c305a08dfcfb51a8a346602591a6a3c6e580994ce8b6d d2f9a2c2cd908ad330e315b0ce28b0864c277d449192979ff833967ba547f73b c509c71ed586e1b7d288dae65cb7bc10a06b185be11f7c31f8ec99c1629fada1 c0077dba543c4caf28aa3db30db40ef3a33201e31d3733cc9c21be1990a7f4dc 96f385385a952bf4692016548b6d1b6387d0bc326a790b8fdd4f9d23704c7c02 f3105ca52412c4ba7f4127596ba2593d6ee5e63899e93e6e2a8db8679fba28cc b03778e3713393dad50467fe5b2d82d9f39ad2705885b41c10bd356a217f09bd a1639bba38ca25ab2bcbe89daf4593cb82e5fd3ea151d951367d4cbc24f6efc1 a17b775caf8802b8eedce721c2146b0b10c9a7c160ae83760c3503eaeb8fd542 3746cccf9d749f3887fb31a6f7f5faa759a002492b1727724173c0ccd00ed7e1 2efa7feae369e424ba9f305141789db09b49311fba618c24af26a4b1c5bc9a87 cfd162c43ef4e0570fdd19ab2b631f5bda782aba0b9dd4bf36f13fcaf4ee9e38 573067638553d946bd27df86f774563212c5b80226c22358cf1e03f530e664f0 7a0a46009627b57d0d646ad5e90242f5243907b6267d43cbd456aff7584e1a10 f9249836f740297545152a6fc17a86192788b9235281e808c75809e7d89ce8e5 489c007f392b1a88bed6efbcb6a76702f2ee7d3de9e371180b4007df380f18ab b00d2632614f2af463b5ced6b10f29df75dee1fe9027d8759ea1838a5552c395 d49cb97b11cd75ec54001471d419d916e7176f428e804baacc167608b38eb59d a697d4c1b768b7c0239ce0db3b9cee5b99c3b6e4e595ef35a9a6b9ffde08fd05 5e01229ec9e9952fb30d75d34f7a30298cad196234d5f2cfd3a7f038fbd9a93d 53d1183895d91fa4f031564f1d42cd66e95c852f53c7120ebec9d8c938f2fe5b a19d66291953165138011ff89f6c4054eaab53de08b3f9c3034edcf2fab8e09c 9dcd6e1f7a2cf7a3c760baf620f2a8d1ba12e8f1849fe7c2e30016feb2f366e7 8541f8f50160b2c0809f056af4148e1f55dd19faf96ecfe0098eb4b60d789b1b f00559ee94731399886cf08981b099aa244c5e2e4d728cb4fb7ebfe569b3747b 77f082c92dccc6cb66edd35220b99e8336080b2af8423421129939a035bae4d6 e66628fca5e4e89ec90e7a373d2e963bf8715d01511474320366b2c5c9a3cd94 684096fcba7c4afcb7dfaa49d922c93e9885847d6f4c85c027488414209246e2 e8fb3e4377a0fd05244dddbdd740c5d23c1ce4c0ec430b9ca47d5a7547da6dbf 833d92362e2499f077b4620c39782bf9c8802423f919038ae4a301044a78d3c9 4802dec1748c0a8738346c409f25badcfa474111b0b304a85924d35d202aaa22 c511f894ef9b87e334e79fbced77e3451d8c1ce8a423d263827ca330d4cf1536 f1c1800500f0889a246f9ca53223c7ea3f10baf8e3564d6b6a8e08161b1a163d d72931b6be829636b0ffa66bfce1f396551f2dfacfd48e16a22d82b8b28cda66 5a405baf6b73014a40e44b5864d5c83b4d9c232b00ca5cd08c3b13e7d181f71c 4735e1583b1cb5d6e7eb995e40cbe9a00cd3a8715209a61a7c444872b1be4128 77420a7566c381e6d5ca1b78ac7e32a55fca3152c8dbd8b690d8868d27d07865 78cd6cf7fac6a94819d3afc71150383e1b3ca75d575f29f4d76f92c25ba63b79 ed9549cc77c95f4c9daee1c38d19e2e0019a76205f3cf1e45c1ed98718ce94be f1fd0c024f29be42424ab43c3fc25fdfe2b4097e166848ad78f26ba469270143 c56065ebd5d79788e21b8c32308ac12a17efc99efa2e296683838953e53cde87 e3871682b7317d3b0e7dfd82a1fecf4d559347107d2a570bcc2a0de3a809e75d c361bb5b6a2e700f1a09c00ba36bb6bb09dfb1c7a7b80a2126bc9fd1407a0166 bb0b55502ea6226457cec9988ad14eecaefcd5f2372cc8e446cad5d632b5f9bf 55c911fb14af5ea27e8e521af5357fc4555035a53330bc4eea6f237e1a330040 b57658f3ae81b2e3f65d7a9bb4ef8e14d12b1a149764c1b7780c3e91612c8502 3396d4c1a025ece5c319d4abba5f03a650828f6bbc1c946bc415f6020ad50fcb 62fa6b134e1e9977fca68025a2ac17b4f9048fe72494c13b14ff104ba1a35ebe e6e4a2657ef29ff6039d713cb5cd1efa6013440fba631b0b7a2ac8312abe0b0f fabe6724b7fe7320ee2da9b845d916abe3a42f664ad63e1637f4512255d8b405d6054e32f35cdb4bd4cd6b969f2a116d732ebd42c2f18436538fbde29e72c60cbbdf58535b55c840572c45c1eb39d43a63a723810f25e29c7e545f3dc5c22d049109709e549dafa1fcd6cc0894db632cdf1947fd1d49cc816d06f9981f3953022dba3feaf8039715db061b3998d3fa29af451efd4c233d951fa15073649adb04989cac67b5258584e51ce5a344b5cd43804002523e6ee1bd88e3419f903a900bca5e766d405b268a60e6ea5b542b1c3a59c996c51cc708daf16f7f83472f4f08f961f17cf2f1545bfeacda223aeccf754ec2a7e94d4f2edbe3bdb14c3ce0250f24ade4384fcea733789430920006a5389ec1d5f242979d36f934cfef32d1bd0052511c3c266a2f6ff3dd92cdd5780e900e19bdf0408dd94e6a2de0a1591b1f0fa5a7a397ab9f4348cc07e2aabb87dbbfe195fccdf66fc2eb92254a784fa7e706a07e78092c0cd09a8ea0106ab6ae35efaedac47ca11cc8882abb9e2b1b4609059113c211d3f97ee6f8e166939d49adf53d69a482a56570908aae29ba251ea40a33bff776c85d22d1ddde9925b420dc3c9a61e85a9846942ecff99e3470c98a09eda1207f655077b206622db011c810060052e70f9af4bf3bee7a27943de9ee00b43a27853b56aebd89d3bdb516372fae0745a7e3d524b1d65e74e5575fd1c60687922f59a83d6c9d7799939f0ba3a3909d739293c6a4ec83e45bdc88856e0d0e15479177be5ac18ce7efe4b6d66632d1359922d35d04d955e4ec2b88e1d5c70b2f24e286b05c0aec0a84326ed33024f979c8c2336fe5b3711f8c1318c0683c0ea59cf13a5c0f0fa795531982335ee91bb8517bc5d5cb98fa9e7b9ba8cac28901c8c53608c74f87951a999d5873fd266fad4c5f0a38c7a0cd49d6bae41905df0a7128dc912e9f34e9b7fcae1f6f01dcee655401f45619cc74ccf72d0378da97095155d812b8c12eb648ae30785563d78869fac0ae324af0a43649e52f2b73b60db41dc68678009902a5b7491691bea4704d1699f0e5515a5b19619baca0e55d0cf8f14cdb536a393ff6b4ff2af9c22907bab0eb146b0684922b8721637544780bee08535114c7cd5623a8aeadf3cfc3bdd9a4617df6bb47f23b67a6232584f30f3df95cf3480150de3208ab2b4bb070fd139364e7d3c9040b364bfe65b0a72d0099f7b65b7a9b45c017e8654521b77d73037d03b4e427839c50adf85ce51b300969063ca6f8aff1193d9cb425cea496d33df05dc84f1a6774bc5e046c28c2a80aff41f9f8acbd1aa37307b12b4c498902d1f0aa60a033adb2af4604d9359df705438bf466dec5c39dd2ac34a0273bc5366923a662782ecb0dea63d760edac02084f74862eb33654911100baba984fa9ea070f9836d8515b90b4bd25972b1dfc08fc25e54b6114a3ecb2bca32711be44a088f7fd4c8df010b4e26c2ad33bfa9b0f25ba950ba030e9b6dc1f10cdb7e6a784f1bccfec55e93f36f1287c3f3c1dd104f0e71dd099be5fd2bac2ca992cfee7be2a40ff1f4da0de6687e11a59613584084b1826d6703dc474a35bdebd34c9d9fba973e1fe202a1e45a821401d86e19a08643d8ef893c54291e9369aee4fd0d0e2cf77d853731154f7ebbd5faeef7ffa08b7bd9766c6ca0f444dd090ebe779c1ef1c5c68c7e41430e881d386b1d2aee70af329dead3a39e6de46aaf2f6ade732e9db8f9e419d6ff8e13e10314c3bd4d602c6e550d5c44e8d905588822380cb52f99085780fb5b6354eab1aeaac5a8d62007f83391ea32079302f6a17e6ef95d2d74dd607901b701c791447d75b36b3380a11c1e94eb33cacdce09abb425a077c447577feed21c8587debd4ce94adfdbb0cccbc07c535cbd82fe63356fb9d381155b7011e7f3f91bba6a4af799473849207fa171a020a405360c3b1f0646ccb1704f1674b740ecb8d2566bcaab6e984ee023219ecc791d1edba994c5953f5e7f9313eb6b697b4b4a2583beea21e8393d60e08aeabab88a4cfa33eb1c7af260818b4e6c12f71c1fede96a9c03c99f37c3506a76dfacec92da1d02a88419008ef05ccfce8c45964d0f4312ebcc4330c0c9f08f611e8d848da0d46b4b0cd82f7309b92a61247b71a50d8a920abc64b5718780f2f1dfbfa616cd2f0a2808878d642af248883d76a7c25dd2148a6d042632e9200e7ff399b7c4bb17760c48d73a293e32cb3860b87d21df94e593241d707748e0a0d1bd44bbfdba6cca4d8a2bba798aaf54d34e69078db566ecd03c8044758620fd5f6411e25aeb36ee412e6bdff2ed30b2fe9f978da2b3937ac728ce27905140eb455b94cec27e6766ba1daafc1087e70cf2e2d17fb3f9650975c1ad8705b5a0889ae41d1b634c1d7b6bc833d7c998539457f526d3124289d33464cddb77a6303cf81bd9fed5fd8fd63c8b1a216133520e28134fb572ef2fee78ed0f1d555e003c627c7bd0558348735a6f65401b45c272c5c0bd1a67bf4227590a196f7ee9401193cb917ec238b239f811a78b5a9b3c35e41899b1b421f9f0241fa100812e30906efde258bdf11bb89cf0eeb80cbc698645ff82bc0f2f6625f5b57c4ef2cf005e1806152a9494725b78ec0f67cb8918e6ffc81d9d5d57f5aa02b01a08633b90a8e5dca09556f7bc5db4a22934a3b3cfdc562d53b425ac59fb373190675067200b226554797d99df872c760e717d3e346e9b5d09143112066535d83c3e15fe90c48be37580e14c65a1ff97eff88dba8047d50983362b696d09c5841ac7cb19503a2e38b1e03b5e78389e650be7ebd2c2b716c7bd7fd92fdc78cb76fb5eadc6000539779f6709b0cafa0b667d65f74b59b199270720d0ed6b81811b89552ffca09b6212a5ff9a0bf8e99a369c3653ee2380e08ef64a9b587f2b24e043027af9e0772442ab25ab019f5252a787bf93299f9b5e8a4f814c2a4508489ba465bd2470b8dc665e4eec776f6b684caa856bdb87254c0c51b0814a5874596b57737fb5f0917445fb00464785bc6b8637d66d7ffa03dd7e5890378e323edc1a19da3c6a90305fee28eb28789138072fc481881f16527f8b2c1bee1ed5cf17af2cb36d71303a14de0a5a317f18f3bc454e10d44ec745bbe4ba6bfa5f4635083195aca4891041ea33bed0d5c32a03256c335d77c9d5cd855e4d6e2c493f331a99092d18af105789170bc4bc8c58f77c2145762366d5b4b814e80821260eca5aa34c51d17f0031567991ccb26db5cc4145e360c820bef0295924fc14e42f5165802783b0ba0014e59d5f3f57b37dfc3f22018227dcde0fff255301f20b8b83477617c19d9f102ab19b88c46efa433169b6f7aea7d850f22091e261fea86aa3c4bea49800c0503a49520b2f97ac00083842957ace7735b0fa52e7eee0276907ba9624f64785807b80c920810a055b5892ecd23b50be83a1949b005ef2bbc2516cebb999a76320c3bda4590028f31f82c51a9463cf4a2413f9fa5adb7ef73ec8e3a8ed9eab12b08d86ca87e7b6b52fd11ceece00bb6274d225cd9a77c9bd3c2a36781a2d94c8504a37dc3c769a0ed4c50877d2ef7f771af3e146f4c892e503c51e197395370fa026a1643d4f8e52912a2e914826df2398cde3f9f211fbfa4028a19985aea3eef040312ea10dea190a84906c949ef4b87c1c8a3a474654b8d2f8e1b0c9f199af50befdfc97ff93883856abbbc435a29cb1cddf877fb9859d907b370cab4d646ef0022c11b532d85a9f9f3a0ac649d0e826e15bb876e7fccf582ae8ff3556deff8072768ddd302ee8e619987cc924ea12ff65745c7590a49b1b11bc3b0868e659a0f530e83eee0d7cc90050806d5101b734f2d3cbdd4d2f3e31333cf3c715233fd08049b4ad5f60425167ee498dcd7a1e664574a2a0d049f4dbba0ab5d4cdca70f0ad2ebfd85ba5facd6d31918ce411643363811717086a589dc1cb304f65f4bdc0a5d3837524ce60d4c40213805ac99b2f9f2fcbecee4a55daf4b5cd044e47d740d7ef83f30dc14e81b5a13a74493c90040339bb23473613deb282a859a45211903db7cd2f40be3b4d3b3b4e04b12cfeec81f3005f35588db528b6a6318bf671a0dbbd8c65e3390ada21b577572d60951ecc75791b842e248e6e73ed862d556a70d08af3a1e085e4a37e27a4715169916aaa64cc8ac8f31140c513717c7d644230545b05f5e0ecad426e9eec7e92a5b413116080a6b09ef77f512daffe741a2c00421d4f4687339d2eb858b6308b60f82f67bc8978147285f0b847505857715a10804c2169fcaeb53c8c1162dc3974317dd1a00c8367f1458e62bd1c4dbbc5d780a6ecc8eb7b4ca671902ef19b0f315b8e1754a31c5c05350cc9b16d761b2d1c5086d62fcd1e90fd77c1eec5453803f6ff9c51ff18679d461bc88570ce8542c4f08491cbbc695cb4788b700f126128618d068924ed26ede36d6ecabeb4b728bf50e45597b598440c25484d5a0efed30c43c816fe7ebf3d61740ee414134f1ca870e357942d7b6070c4a2abbdd5fdebc401cfa60022cddbd67b811a80cd725e6c105b5332b296af84ade0d5bbe6dd9b6fd54144069e5ff047f444a1441a445eafb028fe9347ef79b1e29f39d7a23cd0a44539257dc2d339df2652a6b084780590d0286eb6348b345c249dd4b25fc0ccd4ffe79fd2f6c461eae48cf030281c0e33604eedeb0990b8476e7b0a46a6229687ba53607116d3e1ac062d899c805e1ac3e015f4fed1c144f6e7df2a06821630de9e59ed1de34e55e266e3e475057b3dc85011eada1fc42546ad9ec91c53bbefdc7605bbe88b45bd1d0e25ee1a461f4065504c3a95933be21e50af5cd1c56f01161400c847ad1ae0db7a3f6bc3afebdf6a307752041c91d2c69d8175644a926327000be34fc43e9fc4c0dca0c09a76143d902fb71b2b2a986fcbfb6e14d90a3a34f866cefb2f88715ffb9716f311fc08f3508a440c95e03be750d03f23f1d271739fa826e9ce71fb7ed7c6fd301b5fd3e4e02c26c08d6d16afc4f31d42f0f4aaccd842e09ef7d64ee3e5c7fe33caaf2e9d70be271fefd31b4088648ae1d951b1ac06cf46ee95e652169c96095930d17e5200309c0a10e7e7d7acb0981deb51c7fa10f09c796cf6e9e5d076d53d437d3edcb063b51e591bbf8c0924b441f9f2287d69c033bb98f4404eb34d735ca4d88636509b92536e870001401219c320db3b063e2aa3e1704e32e36349b5199575992990a4e0bfee543b98736c155a66eac7f4a2e9809eb1140a0868a96fcb5bee9530f0ef9507e22c18fbf110f21d85dd84f5c5a775ed98a77704128dedf4e78e4b4b50186bea26c6c15cf6370ed9dfa322c93a03eace307ad2acae081e2046a34c8ac00a8ae6147d6762ecbb0d7300d1a3c602850ac8af2ef819dc22497cce6cf1aae06581fdbaad2d465a0a098d0abf155df9c49aad558e028b28aac3e5d21cc71e50837e4ac0de25786178ec13000c04688ab0f8ce2f1dee4f9be834fd504c12fee084672d3e3e67f3aeb77b552177cadec1a53c7656c3da11fcedfc7c1565bd0dd0da781370f95d4b80aff4afb37014d9f89625a55fbf8422e39f411f2dd463a35017fd0150b9ad35aaaf89cecb5a7bc3d22d4ff436e440d6cd435ed27de42979d0b16d2cba6432e8947a18fbfbe271c211e4528f10588a004ccba500af07f6b2809455130127b964fd8ae087100b9c18ca3c600740b4d48cb2387df80cc489de70012a51268c6224718d9fb7e6f4d4547b84b077883be180930355ed3c7f8b4ed07d3edea59a37ffa890f79ba6bdf262db0bf548d64c90419e8d360f1def5e87403433fdc3552fbe164b8240f57a9170cc65c41d166d1c5c8c26f40377bd544fd0d96afb13bcb593d54b426380969b76ef23265def4b78e7b4fbb8b13020fc9aa04cb6a8632315f9fe7eede1b3f9f30028e7e8da184b19df1a1f6b4d0def203bc0a9825f61e47b56f04a93cc2f343b86bc8c35c63dc258efdffa83d177e88b9b80bc585d3ee99413aa7139b052e7e5eb97447426c02b940caf5b0e0d2fd7b40230e81853369b758369c374e88b28db90513eb631c5ee04d0da8a1eefb8a61e1f00bc1df9a7e64a41ffbd12e1f41b0cc028e3fd41d1b1070998bb3086399be537e093440008d10345b8ce33cd8b25b418836952d3bf67f627ca290ee44ab23ede906f47709489b090c43f74941c4ff221d67e6e88c351537ccecc95acadf3fff2b04072801c59a5d0cf04684b780aa1c961b4c38ab7c5bbbe853d277d5ffd16a8a07e7d89b398e78b8d8cad39b81d8f1bc0947317f8c5c5c971c0372e4ff12e4f207596caaab9504d1ef9d1912a8469386710958e8af3f9a3aa88cde87877a3cf90c2ce693c1599b87191cbc35f2219fa0234201b2deaa475e8db051b37c6ae9d60e44be48efdc72e6ca6e0a4a9a06f7f85e631d721f9e829a3a3514d8a58b89470de996b9f647f628ee1ae3b0fa220a8f9817678d4f107f5938f8293373c9c61c0d751569b690d388cc30cae687f2b6f0605e92cab18eb17a37a8dade9e1f064a0c6021413d8d6dced292046c50649a873b1d8fb96d9e426475b6ea8f63ef08db0075c41d3f16da4365e38b0764abbcd01944488d49e79d7d24eb4a05480124a0060aed7b52a895d863dc0d7f97a74adb513783fc6eba78d24be07cf766612ccf01ad7519022b00420464e7ea6281168c1b3666c914d1c9c458e3377f053302fc07a63a6154bf6e77174fe12fc7fe29fe2c763834864bcf089587d30eb6797d6404a81efbab527a0b5cb0d97b5c769dbb33601b595d282368ec8f8fe962db9239063f733c62301fd0d62bc0dec707629f9e9b234eee66065bf76550e600f54ea00661a2133424155cbd7123d2df2a9d98b214031a6d3b50f5104f8f5bdd27df0a037f0c9ba99d8efa8542d24a09cb60a9896e052582790658de34a44a59b636f40fb6b2f7b33fc466ae6af4e5e129f669e70f421446cebe68ee3d68828cf57bc901aa73b2d768bcfd01efd892d6077f9c97aa2ff1f19345353e9c4ac6d6924e7401ce1c15f8f75c70d0798eabdd545771859a994277249ae7f84b0f62afa3d4c10ecae1a454e1cf54abb85b893dc23b37bbdb56411a210908dd427e5f35453c2a03134541c5fcd1dcd47778f4ee3b412f7cff3511a8eed9e0b25730a55ac371c2026116e13102c903d7f040f5b9036e74923bf37b7602ff83c19ffacdfb6f57280273671273b2d50a6ef5b3e05ddd1b5b4bcd12ad7dc524bb2f8d9af77fe9a5990bb64421e98d0cf4f87c9499e393bf98bb25208e53bd0657d2013c27528c2fd403d2760da3fc1565c6f6c8e74171bd7adc27de807e691a392bd172ae8d12548f0e3a115a07c930305a21fa000d37405f5722e07b66da338fc7505b0c174952b801f4a5b310abd7633dcc3e32d11f566043f26397a6a69b34045734730969c55200842bac0095752497825fc1309d32110bb020b65548adf9890c427a64c2c34c034931cc2fd5042d4bce528f027e35c0b4eecd841aaa0cf99ae8b2449bcf23a70c3e2b13a472048e6678ea989cac4179873fd9d99fd7a497f5ede1c031cf233c07b33b870efad0c87c04128c7ad8d93e6b42af3205286aefda66cdd28c5c0cc301c465d6973463451104ba9154b73abbcba35c1a00dd7b000e58494f9284c41d0bacfd30dc1ef3603d7f148a564fd5b6cb6ea50bcb25db1b33214c1ec5a4f98902dcacd302dfbd5391806961f0fa99d61db2cf585f22ddac67eb062c8d8d556d05a1330f84c3eb249eb27bd1e1df6f5146e91cb61826657aff5097e0a879c5050e177f0b0286201eacf707042f06b574dd01d0802cfc7afe1e57c0e6117e17300b8bde95f24c702cdcf0c3f7bdd2699c7d835af7c048c75cc6181be5cc42d2bb05c378bb66ffa2a65d159377d375f6158f34d179d82926adca35b906977e3b800c5ebecbb960db82a27cbeadc1e2730f5c3ab8d42813b89ccb1bea1ec994498d0e0f6b11459328bdbe06f8a8945bcf043b134a667f44d776ca5c3c19b1a94282000e76ca200c7d85fbc8a0200d23d008e8f1ce0cb04da8b44957e76f295e0e1c0519732c59e297fc5e017a5bdf3bc8c8e3bdaf3752e82c93380c2d812603fa5a0447b64e94752f7a7c06cd1f2c14d8c9da51654db47b5518ede1812fd0f3849d0c38d84d9543ee1cb42cfc68fc2f908718033e525d08bd60bb1742fb6018996c05dc6da90b1906f56d0c8c461406e2d391d90aea18263119031539a320e4c5de03c14a709bf8eb63d7373729d51624227884199fff09ac3b66e3a79a3698aa140987a8eb0aef219b3a8b806d09aac6a88aee1f2c10d602c8a22cbdf4cd9d048b0cf460b7a61f085ef07621fddf679c1efbe55ad0ecc5da1e38201f44e0e98e2408c748bdbc8760dcc73eb601e2778e1557b0fda48b787333ecccc05be88255e1010da59d9f8aab0c4792d1639e5b0acfdf89f37ace8aa8ce0ff736b0883826da0880665885a9fc9f6eef4f540cee53258f2e031a739b53cf269247159a4d5fdd0514c1e448091739e96006f9b251210cf9f85c89e57bdb3b7cb3667aadef2cbb09f8a938321e688c191506a1287abeded4cb2d9faf7909cff45d69ca30f024f4099f5dc6ea6add833c04ec039e68f33f152c6881ad2db6cf6bacf14738e13d4500c6b6c78fa8d8a1808bd908ee4f6e162aa262be6171a0a5d75158c044cad69003906a402e9646419174c7d312e5c61097f185bd4aa2317487a5d2c0fa9534ea0c4ac10c471397bf357d3c75eef8d44a493e444e30b90ce28eeea13e7210b93a03b71c7c46a19ed7b134b23c44b5f1c7931e3536ed27f26f859ca92e836c6a6f019c9eebc79fb3bdfba1301cc05e9cb300473c6fa1c6e524609671763b3710850ed6597dd2773f3b35b15bcdbb1fca737daa8c6f4c2d1f325a11e4e15d44e11801f6993345a51ce3d365ef873828ab1f97ca7bf393442bb970e8ad3724cb981c0da0ceb14fd2c7124c89be0d159e73aaf53283ec2244fb5a23582f822bedbf840809a4f000218ae275315e311228f4eb3ac2910550695ab3dcb52b9e1a57ff04075a594fc1aab3c499632583d3dd649473ea1e66d38b36af78069c2969807c9f03ad7476915c2fff3224432abeb618f2084942393eebca232225d8a14bb672860e3676a92678d5c1951fa8ecbebd11542c17dd32616256439e94a3dec134ac9a087657a9311533a35eda50a4d46eb1f58d21ec41f546ec322cfd6b89af5b391c0b6da897b9a9dedb7a25af90228068fa5a5e8162a8d2c8b0df1ab277d1bef8e40fac593ed1b18478532a055f96c98ff69e3827eb747771d26eecd4b5855996160b96126c694c365fa649fcedcdb765678627858b19a299935607519769261472038a4e1665dbb2632e3ef641fc66ca254ce16cdb609d33ea8b809a446744ebf1049e07b63eed748b08001ed63aa91aae9644b11dc268ab7fdbb48193985185c00bce131ad46d56f273a1c852ab1ed7e436ce4cf8eff51ec3162a9ae428fda05d0c000f069b8c904ced33f923a4a295b0359b15ab1a03347b34d31714cb3fb2a00d55b431ba54d3ecc596d6945dc359764e05ecbb800d30f1a3e1b08477b5c63a070b5d56ca883a9693e9bd014bfd20a814a0d47869c3296078310e9c693bfe9c0b5380d1325a05c02c8ba5bda9f6d5c37844362b64bf6c82d59e3392305b436602e7ce8e8262308c0f87feccca2a3a73d5778898615ff437e49e3e9bc18ddf4003191dab518113bb9a0d0ceb169be7ad9c57d9b5d0f2d44c340d0deefe5be0af024699acc71f20817371aaea419ac4c373e352110b686b7cfffe56caaecf90d809d57bef6818f70fdd998f9e76bc700b4252e522da41e11a6164d9f50c5ca0b90adf05f21c5d846069bc8ead9ca8d54fa5010661d8ebaa480f56f3a845f2938c0d5fdeaf5a35fdea4f3bf8369df7995b09777bac0b9cafc7c9c612d92682fec40152b6db1f1fb17645715d90aa8868b7bce9f45db30dbd10768fd12db5654da102dd5e1216a154df561f73e3b56561885e1486fa434c6379978ee952ac61cb12039353bd06e0a777e0caaaae1533487076cf5d3963a47f51d4bdc94fef174c1e05c292eefb594da97cebd49116a941d555392f017fab53942761316ce0e945950ca4a2608cb3178e1b3e5d66ee64525c4f01c86f3581ed2c94bec3af899eda790bed74d8515e856be599ee393ce58cc353b215f2c8f3981051250c4ad89250d307a89ebfa271c542c7f615fa85da162d4c3180c99d2b4da633c956935bef61000b192454ff9789b86dec75b1ba0fa01e23a28de766936747ff6d671ee512b6050b49122bd067314093b18cce7e67ed9fac4f66504676b76301564e38ecb30553053d51af29b1fc43981a170ccf488cf09f12ab4d0485db18b33a579ced6d77660a5addf9ead06bf35e5e9aa40a91ba1178494e4c1c1e1394d633224029faac9509c437cc6247acb96b572338e3d74efa1f575454a2554b3e91581a65510a9cbd0f8f2e8026359a614b0f7874f4d917eb8c1a9cd5c033def173ce3d1e75c0192400a3a6880fb0e0687db5c4efaa0bb18617329607313d789f31c6d64588a754a20a0eccad838989b54f085ce1b7cc0bf80fd60bf382a9232a06c9e3e7d085dc3e0e8bac4b54f3c16c46939f5d1047e97788695e7e6440400aca585fbeb823ac4500ddb66b4d7a847980f7faba124e5a90cc982fadfca1849a51ed16d5e049168208e0237b32f105c2288f0acf15ea9aef140d1931d802d287345ee86c633710f40238b6bfb3daa2b3f53a15911516dbab03f33b59b1f19010e3eb458db9d65e0c0f4112b2f4ee3ac7a539b20191c826d70b6b7411a0160e722a7743a6874a45ce0a3f74c7b6bb1f2c6c2f761cb06d9db681ddd8b1f8debc84948e977c4fdb3f420f5c5b05a04fb765534b889a6fac960ecc423e7c7c93801645fd4db44e71e0710d588400cc5183019accb99548214ccd300a7bf1ce6ff8cef3ce3de79eb02a9c02351c1bc6083174398f42d4c81ed636b9d68b3e92317c1ec0e5124a1c486bb2072b279215c30aad449869b3d98652fa4b236c138e495ae86fb37b7973f5b5600da91f5272ac5d31bdcb237aefe55de7a9ec458fbc2715ced69af9c454ed25d10a5ee27d4702523d39ff80a78cdbe4388bbed357573ff4bd778e7be36dfee79a089c6c8a8ab3304a2295d7ef8177a526bc29ed5ad6d874db8276db44c46dc6ba06eb3392467881213cedac7933bd548ff547916c091d6582ab04d39dc8373e7604acfe8b0fe68ed82bec7c387bfa2b4de0917aebf373d14fca365af829004f3a09ec3bb8db42596d7c8abad5df08c50818727ddd41e594fcad6b80ae87ce06cd0f3cf86a6c8875f6b21ee02b149dc2bf68503bc210bc4399758339ed68e04a710c0568927e2547e85b519cc1e4d519093434d8c1658846ff79517b92425ae0ab00f67c5acca9f3fd5c03c6eae301fa6a437ac0573bc00af456ed3a1a041cc77d05313732e109c459110674ce1a25ac64c506d733693f58a2eee357da07855acd0e9b95b68ed619b03496de989798287315ec8ca286b583992e4ff967a7762954010ed036ea477fd89ca2f5e07de852ee063de549a7498464a02e87256c1ce1f80a564df1ec14f43e3782298e5c730dfebaccc4b1b3dfdcc99e7f48d0d2c0f4a1081d4870026d320d9776ac8b8b8a08af9540ea92d0199195bc7ddc25917b552d0d0e924831abf7cabb4b2df39501388d9971bc307be543dba74d3752e4cd1bc3043c62ea7b08050406d4016c1a93992a9358d297d05ffc916c6fc526fdb7806600f25e79b019b316bfa1a37a0f7f3327b1f2b7b8bebc8ea1c43be55d46a16da2046f330541a2e05447698e5d55f38ba377c70c8cb9512eab2e6825ab93bef9380607db60f6525b8d844070b659612a204b71251ce22ddf9b131771fb7ad245bc058b4d5cfe0d4006a2d4b1bbd4ddf44197b8d60d38392dd2660e6b0f6677ae7b051ffc64bcd8c2be2c5ce15df14d4aeedc4e6bc6537b127af1be39babba75c0204d31fbdbcdb93beddb441ed77fe7b023f1284be9ff1be5d44646ef40cb1b80402e8c5cc35fb1901f0f2d691cb40b45955e483d0ed05286d644d0797277405740d3f7d0d7368f67ab985d2d70a3b7ed9da881843b98b6691de5168cd5baa765c011a96f48ca158a1fa584d61ab34ec6d1e51cc9323d41b0b6a24ac3e9a2cec290705ecf730791429a02b04c34375c1a20b8d956b64257379add77e054174dd48039a6c61050f089fbba4830285e88da4e69a1255f5bcad062030243c1473e64004ed9876356257cd8d6dcf6ff8fb47f5659d77dca87afb22201afac1515a558a0cd640d37c47310973aad628fcdf362edad503be6cc1edaeaf41f1a43c106d540ad0156c0a998fbb0672a254295d3e0ebbe6fdff611051a3bff4c1688390ed1900806c6e899d0de3834da3da41b3dedbb3d7d39cdd40d4f44a26f7efaef84ee407367e1a4cf78660d064bd96a07b6f72bcef687db660774f4b831862ff3a29a3074cc0a62aa7cce435ce9e5a90490cb4c8a2a57e121161116407a52b7ea7d98d023c74193e1ebecc2334e7496b14b4207a2cd471bf4b8ba04067a9a19ba85ce709c81f0241bee713e596eb97f49d79a636661c608cf810c5a75b1601174c003e0d99eeb2a67611e05716aaa80c7eab20e7970a8e1a0fce14546f15e1e59eca1c039247a644d8edd6e2bf44859d2fc576ccefb4df5e687921dc8157329d37b0cb06fbfe44a8822c2fb9480108af9ed619b0818b12470fd531a03243441b8715d502409acec2654cf0678a5f9e41eebab2385d33d55bfc710daa5737794056d5560decc05d4ee47262ad2492869cf338b2ad0240264805061caceaa7e4d9667d7e079b59a156f132e903e95f4b90c3a26b77e1b5854e003bb9f0a881d8bb75803a072bc8362f867ce99a85db0db41e97b0ef5f2b600a676bc438da69f247afbbb0093daf9f56db85f520963275b6e173cc943ba299b9a67133e65f5ff42fb7e4fd0439b633cfaef6b6f2fa10901d0a5a182524ebec97588525693756588f9346d505700160fc5d47c7fe73458708bbd8c8ce498a135b8ff90b8d3877b61cae7c3408838294eb0b278bfcf5fcafa96a6be5569dee09f6f593815e05655b67e3c6f60aa0a190fa0473cb645d13d724a8022e1f16bed2d9734aa5697683c6948155b30afd7c4c9248c1f2fb8875cacb931c6acb3e63d655a8434ff93539daca05eb780801e782c37b394e876ec6025fbf8692937b164f1697c25838eb33247b7ac9240fe3fb446688d6559a6f3c3bd96fce22f17a56331bc8666ca0ea368fdf8c555b092bfdd4dd417c61ea477c2743578f5826fe67b4f9e45f9aed39128e820c82da02cb2b6c4c44448b379f2390b996d04256661f6da86b896d42702c704e81a35e09c6e793b61a92710fe974b70b4f79c5b0746ec6096620ac8f548f91dedb6ad6034b256f5db51e8c73b22825eb00c1f2bf4cbc9227386d5f4d88e0e7e7f00d2506bd7cf186c14da7efaf6ac3425b2d8c09ce59f8cdbee11f356d9d3cb730a71b059db526a9b9ed757162ce26f9a33330be06a5200c5cee51e0083cc771c7102600c2faf3b77d99fbb87be43922143cf34c457c97ce05805fcccf92f2947df9220717df3632b19172c51f8f880b691c485d82fe2aa084726af36c12d91ccdc4030d6d2f037c9ecbf59eda67a6d744511af1539cf132aeb72c29c98c265f2cbe850ba911ddde572fa135deadf9139c499962ebdf7912d7d1d85ef814cbe956e63d0d419b19912f4f2f0e09826146e75783b9f082b7fbebe54cec1a667aa2e514840003294ee617ec4cd79b5b1652b9f24b6941077f8605e71b4d0eb7f43410806f008340ba4120ba2f4fba0d225e5aa7fa9af543f097cc0f999c43f8f475a814ce04b5113bb57992fb62c1ddd5e49f6f54bb1859b347c74614510459a6686abd12040f18c9a19ef0e553d0f53bb329e1549462b18dd8841ea990ec8c62460a06b60a98dd871458d1d5d4b43b2cf7e413f69e4b5f61bc816a32b029852da9bdedaa012641a7c49d436cb06247bbd18762bfe7ad17669bdf7405eb09f4d01721933306fff98b38ae43047049eab6e0b7567f71a557b2bb86e5ecf6a66de146cfda690d53bd1e96de9ee45e26b8d45dec2bf4fd5fd023f3274073f4ecada0b34babd00f9e34f3961301af6bc2171ce77d7f323306ebc863e6989339552348913621fd0259515de46f22e0f13fced9bf68c20162f424ed4a460d13c5330e50a8dfd4a30166dfc13259fa16a167cd441d7725d8d3381e71483a0d53d393f4cc8c5a9e740dd47c71a775559db5831bfbd54cc7ee9ebe4004bf5159172877b1e817960be7077fb1f4f85527ef1a314a057cd989c245dfa650ccee6a2cf5d09a2e080a486d0ab769025359c42e47409b3911c9395d1593dca97c08d1ea0549ee8b70f6146908f570f2c563f502e3deef0ea54f2444c1c0865b7ffb704bedf360749b8ec3150a true -check_ring_signature 8331bdab8cd6e412417f5cb610e9bf67615968f36ffbe7b3f38cd958c6c6415a e5a8f24b7efe77431897b0234714fe8a6c96575234213bb56d3ced435e253f2b 7 6bf1dda0d9bf25dfe93ddb183f1600ebf4bb41469166b62225ed7c0be8bcf013 487b281ad9717f97a41e6c50cbc7806eb63718991ddd837582b4728df8d5d201 596978286b670d529adbda4a60c1702ada066a1f1c585a6579f32ea57786c865 062cac95ce6e7942601fa92a54732174db2393bb6e7c37cc616acfb3d767f528 d258988fa95c26d0dc5fe991d311b5cde0263c155e19bdf2aa3129424705c282 6994feda217421744a9365876dca7be5c463e1b723fb885afdaffbdfaebb55d9 bae40345627ed9e2d1cf4349c746329cb5287c6e7bbb8091a6c5485ff06869e3 62de0d5834a15672279e9da2cd7f09914436da832fda46dca8f008ba86feb4c649dfd42603a359035eb5f5c2b1eacac5a5847b7e80bfee1640cbede4615f9d053d57e8be6048b77b41421af0893278d02b0fb831565004316d25f1f8a1a45e020ab4d83a3ee7f9367402bf70c23b3a9c516d66787921fe7fd77d8a2e07f20ecc30503c0c69732d9e4278c0d3c30c71ceb52fbf40f071532dd54cc87878b7000e8d02a60c07ec2a183683d29fa5437afaaa5371ff37b80722f3151d5488cce20897b18429bc96cd8412d7991ef167669d105e03088b4ed09c8098ef6034cb2301f4b719a3875a9c3a1cb2513a7af2439dfab588183584ddbaf59da49588bec007d39c715e8129226799c84c869228a0cb3f1dbbbffe85cda89be3c3d154047e0f93a6db0117d7065b137ca4525dece03815e4c7bda797c98a373dbbdbb962830255184c6562091c474494abe875663e08dafcc5e416d6679462d0cf56b6954d0aa9453ebf1277b934e8604ad25a72612321451919a4db0ce969f660c44ca18f01f291874300793a3c864381cfc6f612a6e899311d19cc00ed05571404063c590758ba3811e5516b7ddf6eae0ae34f6bdcde5c70e1bf899a0ac2202fe2d2bf510d false -check_ring_signature 82e191b543080e19c6413d17d2711dfabb00cae7ac9829646ea3c185d209140c 7b47af00c2b0e0fdbec786bebfa145ee9b585e7f8fb074b1f90a290fb297dabc 4 5bea2e1836f7dca8d63959c9b90e07de78fce1d4196ee540cec6f4e165e50dca 68523ef8337ea587d48fc8a2f46d69bcec7dda8947fb311aaa0917325729ea64 a269b320a3a2063a92aa641e32c9b29fd20a957936df21b14f79b0cfa7851792 367341992ca76035b4a80bc35e72ff0b2ec3b54fefb39496ffda76f8bd871754 8ff9ee8ecdfaabefed6232bfd8c92576009e0977e677601e5a3ea3e4fbddd405fd5bcacc86c0a3b0aeb33af3bd4afcc5e305136ca064f0a0d093a5b357ad8d0978c6fc814dc4c0097b0d10a1e34aeaa7c8ec3ef107fe8b2fddf90d477ca4f600ed84e950ab42a5366a9497c47b8f9ebb80a9584f7e56d0814d1fc5fd082ece063ad61ab57646dd092aa4ed872f08131bc8c6bb5fbc5509d7c6a6eff0008d9203597e40edb519383c0681282df2617ee9126ff8efd9705d600dbe3e35b955f408aee9bfae78f7ccd57b74604715380c14a20955d460f0e89cd3b7083212fca802c5755a09b33cdcd5ee176c970524b5fcb5eb12925844bbe8f78bb9b6ddbbc802 false -check_ring_signature 6c178191fd83e78807d1dc2d4854fdcb2f41ba57f659a2235a83e334e13b5f6c 25756651f2878910328494a669eec382a4a98178fabc16cd0c04d508a33cb892 2 e773fe63490e9584e5ad1cc7386c846844f69a24f2865a7d8b4a9ac0e6587692 53d03db03581a6bdee45fc43d19d0225baf980f462338dc4ff713d78405ca6c8 a67961e889998a4d91e7ce427622495926deb384e1c061843a5505e1cd60570252ee095f7230e39bf7d777962f7c556f99b887ea07590fb5e892f418ba5a9c04727924d1a39723c1430db9e9596534b7cd3129f246e497b4d21edf70a39c4c0c7b0f1c30974937bc98b88e2c7c1092a4a226fc73e8143988b5593e281f258504 false -check_ring_signature 8b9909bab6c7f7c82cb539f4515ba42810970a49602460b432d4f484ca97995c c407c4cc1e19eb76094c841074fc34db0a8166f2411969a844c7753ef7a62ff3 3 2902391d1045f41d38fecbe9456454bb78eae45f35a8902b8ab52fe53d0cbf22 44548ea7a015f0c775a8bb1cba7eaebafe54b01a17bcc8c6cc2e480b8a5957fd d2c49697977f9d9c16323f2accdb2b93dec72411d86c47a65c11e98d8eba57ce 878eaa04afdc6feff8d315d8a73bfee6c9c57dbadb196f23783e5efe1d365c0297a2b7ef3c4368672fc1e923ceeb083399115300640e5306fb591b557294880f1912dc4a69b58f08d76348b9fc4a23a4ac336e935c8fe8f17f4a6a3d9257ab0bf56bff72aee241a1566fa7c89cc88655ffd997bb33b7d6ac1e49d2a91554a009b6db492f274a2fa0936c7229f8415f52376ee88c40d4208db3f5550538df450ec028a84ebb7b0e2e469ad923b06adcc4b814f388656f68d7aebb007e5437830f true -check_ring_signature 3d4227f3d1bed720c65a3b327f123e7041b5edaa5f7f662d6b8ccd8506246a50 ea5f57d6bec54ae0fd03f9c91565d767c092d0e4fb649a705b577b8cef53efc6 3 f36f6be96997f9f1db91d2ccfed4217da9aa09a09d7b78a383a98e6ed94261f6 a3dffb8f8ee36faa35f7c57c79ff8db4847a000efe640cc3c24b1e3f0dcf5145 e097cbf6610ad974b675adb2e547dea3c88639673688a22bc5b6c3e5ac8ef391 3aeaff6428d1e99396cc0d018b864232c642a8da3fca8334178048318f99d20776f37d6e3e1924a26c30dc05ff93121956249ae1fc9d0ffa22a6e5a286f33f0133102d8cfd1874cf64aa478f4fb53eaefc7b1d89e9424ca944a0a9f1dd10bb0fc621042e96cc218e58e2fa1188cfcc4511078222e04a2e2a71ee9fd8d41de001c8c29cd0377eb453baefad75c88607526978545138301c21bc55e413849f580c78cc6c121fabaecf6d725b102592914e21b5efc9575d4718f71468519cec2e05 true -check_ring_signature 0f0f07ad2cdd46f0c6d4f344245875bdbe9dc6bc5eb773eed39ce01f69e409aa d61f6f637187818d91e86e10e14c190a056d257a4f04200072b9adf0b709d353 43 b4bb5e0f004360747f5520a1df3dc47ad73c074f84d4187b19b5002bddc2f4e3 efc3f066d010591f79c8075c195b73a7b96d7da6d8be79f0e8fbfa4c9eb080b8 f7e26ea0ae4ade4748a0edc949d9fb755e3017b281313d347713306e135cf39b 414ce089f936dfc20e78fd7539352cdbf5291f03b013cba7dc7e024f2c8e6a4f 0a4d630c44d2da9fac7b190f8d89a5400ca7607931c2c80136f79e27aeaca003 3240c390b74f21b891d760bf9de518551fed0ab969875b3e4c0cdc4e315f6293 7738f92b6cd1087f8478b031c460ddbef3d43e0af05a4282df8bc162887b319b 3964cee5ebeead138df403b2ebdb34354d1a567dde0c857ddb6f4c9dc286082d 278599fe82ebc0029c4cfe4e8d8e5dee66c7d12117869acf7cd5019a0df490a4 a9f6b82cbbc39b10e26904c0e731eced370b68cf7044f79065ab6cdd1d7e81a4 b4579cc68937c342ff59ea0726ded8307e22a1947b0d0b2c160f74e06b9cc498 d109363578b6d9762d248eb0f49564635982e9f3bd1ff1893236eaf508d9be8f 5d707ad6170230ffc46438d0556fa975b0e1de141b7a36b5a25b4b856a8ee5d6 a414f664ed79453f285353ccd746250c21399779c509300ae7436eae72ca7733 046b163e3da8fe9b9a90fd3148da8eb3bf95ea015d59b1a21741a05121404cc2 bee278ce2610002f004c27763c786e889bb87b4e93c9d069061eb13bd8d3a9ff 2e6d03ca0af5dc41c8a88c9b45dca6544fd3e2366795a7625559c99a3c38a08a 8416e516dcde1588868f1654a7fd2ebf871389fad8c98015aa4c9f8b36eea8e9 3eb70b782afb970a42a87725fa991dea0777e8cd96d0c8b7d142e4f178c6f7a4 33783995b178e2a0acd0745bf5806b11b66fb6c6305f0845df68478417ca7a80 d1431c00f34064c009a1ab7e2d12b843ad6b2d8181a6ea93ab1222c309f3e904 809222cc8b14aed26bfc221abcf6f7dd28f11ae0f385ba83ed5153c9d4dcbd31 7ad782df748efb2b546874a291bd6783e01bdd98693765abd13cf6f992d53e0f feea44fbcefe8413e77d9b4475f9fd3f3f2e0e4fc5758ef4228f5bf87bd28d6e 23f4da01c785da20e9a1245e6b7bb4a5f817ffee464ac8b9e262b378527a6cfd ae5b21d9865d108d4b14ff64d8d8002495473d76ca6fa90c1c05954747f9df44 1964a3d7286b630b663b00a65fcf920fb02f23f1dbb52ee99ac66e8ca074e6ad 34c3c2c2db97653ee0017deb0d928d62eec23c2ad6236419a56edfbb56896dca 000f0f39cc66870ed22bc08b8ca8094d8bcd77c09cf7c6c0a8ab1971c8194382 0fac5a73cf61f8a4939e81b585249d69716e0adbc8cad92a3a4cfe0296893405 b4db6f0bcdb5f77a13dc0dd9b5457f0663da681d2e820daa1e05ed80432c41b5 30b8e6eda8a1931b7bfd96f2821d878a3a688c52bfb9593e13ba9c583f6f6a8a caf2567e2f699e43bee4bf309a596e875b707c6aa8f5916759f3dd68a3cea3dc 24995a90715faeaf2c96cfe13d904f944f28aab8afb03148c29f8f443556837b aaac49d11925782c4dfd3389b339f7d1714ed279e1d3a0157b5d25fea3201e91 9537e2b3cdc76b1a8b9e77d6d4710bb098f35d43bfe2ebfb64a24c03406b9baa 615ff841b129c4c2a406c71dd3fd2a91881c253e95be842062c8dfef084be8a2 d3032df2576010d6d444eb6d8b595d8e2acd7260ef9ce3fde8804396747c77ae cca2b0c3bdbe343fd03a3d44089a9a55e6f61523f0a720dc9049f5ba4ebd9e1e c8a14e3f72ee010f11032b655d687b18f32d42bce556129b2720a8f9bb75d106 2255795a35a372d7daf2c06856e97e5cf1496cef6d546cceac1d73c017548e67 e666de5437f11223a209c9b105504171e8a417f14312dfc2f6bba9051bbd2515 766f58844ddbef42d704639b8f65e9f4f55e97ff70dac35c6f386493c4e0551d 5fe93e2933a5ac95e8c2956cc574963d971f0a46923f5ebeeccef4c39e2a730c21c2c49b164e8339cf35460cae60c8d6dc7168d23f6a560f5080d2186a9323042214bcc8a6882a654bcae74b7b9630dc3e866ac7b6fae2f85f2605b1e85c180154aa69bc7a4de1096facdbcbf99dc554dc293a7e41be61a860537eac0ce0590e26cd59c5625b5449df630eb9dee501215285dd08d07d7470c6b2c6530645ec0500a7126bbb43193cb9f72bd3cd59b8c0c77b0f58605b01ecf107ed2000afbf0bddefe3f6a64dbd734c81d0fce93c86aec27748fdd4ca00c661d20b1c26b6a8073fab212fdf15750e4a0879ae0abff5cbcf80050c376d9fd962eb1413508b150bf6855627378392b484992dba0005fc18852babd256d87c7c75a3915fb1613f0a6e25ad190128d366c7777f4a7363634a4c2336793a51771d162f49177e24be0e99596b84bd49fe7d7d8821cec570725aac0d78738cdc5c5f98d42f09c44d64014ae1a095dc0be01f4eb24cefee3e060b1e923f7c41e05c9faead4f3a4bc1f70cf7f9bbc8f64b66e92f92ee367794ea8801b5c7023930e3df61bed79828cb7d0b1929bb5c6f801f07c2792dfdd297863f2bf1d7b34aaada3eca057c998bbd2b01ddbe03dab2e32991c82c2f7d58d7f57ef0d42f91dbda907ad650b9e0fff60d0a199028c3eb3bcfc64679ccdb54ba740436bc326518a79287b9a526f8b64afa0f132047b42fa161e44bcbab2bba560f2bcea99ee69af69417bf257c49a66e8f0afa262b0394beaaf0a23d5791bbf845961caadfee49957f8df15659c43618965bc5fb07729cf786c6e2f92bd19432d5bc059e4f9b2eddc9e28ba029cdc73c6b032b662044eab8e267f0fd7fe42acd792d53833adf0a7ad0851a6714462ac1e7094928988346bb8ef9ff7d957ce3644f5202aa1ec976188416a8caf093d91c4d0c425be571caae9f5de6636603b0faf78dbf49fe5c2f65a52d0c08e6f6647aeb04d24e797a6cfa39e78e0e074582f204544cf31eca2eb4695c306ef7b043a81100d81f2f9885cacebd8af5536bf0004ff44203d83e7f424bebaf1d4d8a20022a037c5a544c21af98fa561224f9a3e81083c431ac32e09e7486a94e3bd23672cf0ca8e6a1d532e21bd6546fd73d6b5e2c27ba1665f9c6cc2d0282d1b5506c4dc00a71826824ada4c11f93fc192e7c2fa6a563017ed97a3e2d4d7226fbc67b0d580ce99c465d65120764a443d63d81625000d7a637b7cc7e7b709502d260d7d80d0285deebd20fad227136fa4adc459f6b7b33b5e493a0f39196a0c4ba1f002e8d06fe786180542abe37cf1c2872cf875ab3f3227a9acfb96b502e5d1e0766989805f42bcc5f614635d03d62043bcb798025f60d651da506bf3a516d7ec80a8245035ce28b1cacd5d1277d9427fa8ce0839a1ba21f220c4c42f0a8bfcfa3ccea560f9fe972a71ebfc7b64a0032273f4b3325445f4087572829eec82ff9034973750e4c7f8a24a14daf6d95648468a10c3d954609a8b5f09ec8858edc65c2450cc70ba3e334d9d05597f932572f13acbcfc66b0311bc8bd450f22e99044b513a118076e500fbeb5f78a9c0e798bc06e123e2bbe7f175a4657e4b20435d1729815050f3011255ab2b6a260491488d1009d32fd7e134d903efc252ef5fba844a51b030d5ba4cda6cfc10e254f47715c2581a427c422c9e2388f7cbe43f45c3e00913c09c5fe4643b97a2d85993daa05583b74e43e42851a7bbfe42b88f83792ffea5a00b639601d30c240fac23995b4340b08730caceddf597f412cd5ec27ff50b15d01938158a9a5782045b25c1f6340031a90f1baf117e48c15918dda7c779f565f04c61e6a7d1cfbcb1cabb2b53c337ab90092faaed73a4e4cdb2eb6698aa777260f16c1ac9657cea9059abb84683128a90f34a7b7461a7a401847dfdfd8fa362d027f422bcca3358013a37391bb11c951804c3d1575bd99f866ce74d40f36c3da01425f56ee055b6d445b9be8bf3d74956487fd1002b4b72c6c993ac246412acd0534c46ad03eb5f5143d794aa83cbd94f4162ec6c75104bdf008d21ee88e15030c9f035c44553b7396880cedd53e0470178799ac0cf0885eb926e4d249e866bb03712ccc3cf8f159230d45eb50c39ae82b2719e0ef3eca54383f964e40aa2520062aaed073c5319058622feb4a47090b6042b310c5b03d38b39239e2c0f1ecdf0c9275eb13c7bd8449303520e03fbbaef0fa9bd9a1fd349a180765fa2c707177098b7e52a2bd8d8ae978321fb7adca7f9209c0436bb83442d986fa43f8dc0ca50d558db0c528f6f87a973ff7dce72735bbdbde62a6da1f3dce1ee1b245a550a105af7b0c6418903ae00815757697f2ddb1bc9bf8708acf19d21e3e751c021f67073d4ec75d943fbeeb8ea1bf11d23b4629824132e8d04991735b44da0a76d6c602296401dc86cbf9e323ed7d0bf7ab914f847e8ac5c6d5dae83a55c457da3c86022d15c611ff997d830c5d2d36cdc02047f1a62c62675ad3889ef0e31c55374c097f72aa7105b1db7de64e19658ce703a82b185de6f2b06500c2a83196ba729c0c99f75346f715aa0a39a544593f692fff9fb9bc096555cd1937fe266d23f30c0cdde8a2ddeecc2cee5585e85a627fc47d5be794600053d5ad514202102b831c08fe43da7259a4e68da247471381b9691b76cc5c15757a51ac821bff1a5b877a05cc05cc1e2699d014948af84d97fc3145f3105efc67f64ec9ed9a05bb0405380f2974eb7cbd40bdf14cf6073c60bdf41038f7774126bcf4c87455d0b8ba55230574c3b71a31b68981b559070107e1b95ab23df3b9d5c89d0e78b27deeeafe6d004ec29e04413fe3fcf608e54a80ccedfb2266fa5856b9a0061b973d1b43dfa30fbfecddf96377e39e6d7a46f796dee49a936b099626b282b93e59582cc53f060fd2b274f48ccd943ee5c6ad6d4492b6aed270bb786206793a1dd22c47b719250822af1db3bc275f072e8585e2a01e175678941d970b20f3eddbb143996328170a82cc63093f36d970e1f9649de69fb4d560ea095d79828aaeb2ea0c624d66550aaeb24bcb796a19c72ed5558dc4f942ab4c2e27a6d9b0ecdb2cd4eaaf2105300cb3e9c33edf327ac99bc54caed5d5ca5f324c81dd7ce7fd0ffa601856be20890fd8ee0d9a67b8809aa8fe21473ac864480fadc058943590da9e3c6d9c30dc70080ea62236e38338f3a2fcbca750141c711d485c5ffaa0671d0d31e9b5714f9c02ead3eb8987bb52e6b9e61fa761eb31a67debdc9fcda89e6d9d64473d7f234f044622c4f241d394a96732925ed71f51fc13164d5375d57340a6395fae4ab863042c71788559a71aca0d9fb1206d90d56b2bca1c9550dedba5a1fada6478b8980767ccc99e7f6e2546bc48df7a31024144b06fb7659bad5b50f0f3faa38224b80c4ee25595f0b03e17a8ca2a1a05cf9ab39d34cb4b117077a8d7c11d4738def10583b2c6dcab0ad211a9033d21cef028c562666aa1c82b06c6fb266e14b1d9ed0f0838f4a7c99158035b8fcaf1cd2d23433c0d38d42ae034bf425ea4bba76ea20bc86a7563ff8a821ffc5d958b2a66353a918f1eb36f8d8303f4fc45fcf38f830898fe44be224fab6857680a7c82e4c6c994ef198898304ef0d8dcfb913a866b000d887053bfe3d9f53e1ae1b7d65f6f5f24c2c2a3c8dba94bc64483dc49e1a30895b0fdc1382053bbe6b583456b8757af843519a50d71a0e2152d8429689c13000e46f5e8ee3ffdf9850b7a5a9b2f7be1d2e28aa2646cab94328219cecd9ff507189add675d306e53ff9b57497cfd0d33f3db494bfb9214a993add7dfcc2a010e674cfc5210ce378efadc6573b66fb6a072f20f33ce88f8fc23558a216577a300 false -check_ring_signature 965214e5f8d053bfde6334705610e43c484524ec10e2cfb1e54c0988337d5465 c5e817cf1ccec823f7ec7a5fdceb7f4376ec9bf9e40ed58afaf34491a991d82f 240 a65ed8a13c0d887580ee74901b8a19b9955078513361c7d324f014d661c6133c f55383732de4cfec12631ebda55a9bc2618a5cafcae165938efe04dffcbb562d 5fab1c87a4fd8d904a04a8ea78be205fdfe57b3cfe1a1639d97d90567c04db9a ba1db000f4fcc466015fb90bdfb8cca1e0416430ef81b96ed3e4adcca6e2d9d6 b11fa2805daa4a95ca1394d21d52054170a347605fb9531f4dc29468a4776b4a 36442f1a955fa1ac6d1cb59cbceb625d259a2c63dba6e16e7a7734b8b3216f15 b25c91a51913cbc4b99ace299e3c67dbd3cd238c7b25255815523392e18aee13 34cbba61a487d461a562431f0e287ef8d6755879a91d7c01a750cdf34f949563 e4925dd00a423922c85362433808a9bffdb27fc151a429513f7d3f38dd2cb239 c484be7ab08795c486dacb4c61c0a93275811efe2e334d31e540a0b02fefc134 eecdc82aa71e8fc0b528bc2f9af26e6c077da027fd0944f898beff588e659986 3dfd82b8264b29c9d5b166f93d392525eaf3935a0ec70ca8f2bbb7fc5fca1bcc 5586e0f3c099173858abbf32f1991d44c37c58e5b2f44d5ec467f535b2c034a6 c6ce8ed5128e5d7362264a1edabf73ade145089ec17db8d049317eb5074a41e2 6486537de2c0bc63bfebde20f0724f3d3525798096719768ce73699bd547c9ae 2e3cf99a1a25e2f71aee1f0eae4956c266c1ab7c8e402ef34dd24c1dd2945ad6 3f2ee05c3961f22026362d20332b2b26fd544e84bbe486f90ecf8053561ae70e afbc754751983da1ead41e98125ff28cc14f7a57400690aa5e1b5c63837ac610 6d06e406f59116b78e1600fde276086cdaf159affd498f3a340374919bfed753 7ab1c46b74719ac689328e0deefb9e2d48962ca71fae1f602b09d6b1e07c59c3 2032dad0ae3f600b5bd8181b0bf3fc3ca28eef948fcc433e213b95907e9c0718 75754a977d634543ace44ebbf6e6f45f27181715a6539f3c5c39b0319f618116 0c3bcaa41ed343e3e06ac735468372e58125e4bcdb3bcff6d56a8e57288e7bf2 baee8f40a9b0714875a838b75a64d92334a83f67a66af0c70ca31bb5aa85d785 f3c526e12a6c9eaa80f4949d692f96d677fea3c0e52046e57f8cbfd73aa96114 d3e47f51359ba7aefcc3d299377ac15f6f6d11e456d1d63fdb10f07c55510bf2 637975821d29b19f411675d0fa2c6a09b7d6cd4715f095d718b488bda0b7f77d 92bb022ff12bb19de8a1ac45a8d41c852601d300cef4f4ffe71c5bbb8d069999 87fe05c9ca903c4ff3b924ac29c16fcbab4426f0eec6fd272204b3d982270e33 edaa92f6c9639b9b9fce8f9cb14e0272ad43a5a836f345f55e8df07daf18105f 0acc6665ae061cec5cfa01f870d6d9a1e8eb890bedd75e1f61f12878e9e088d8 8649552453538f21dc148aedb85711c68f90624d5df5c237751e6f364e7eb4b6 181de62a00828d8bdb8731c8d66bdaac6c07a77ae03841c001069aca23470924 b4b563299b982d254792011ecd5767c08edfe4565daecd4dd9653b6f1d789390 c8e7f027856c86602d3e7ba2a2dffe107109c35fa0342ab73d8856a046d83871 176e374eaca73847a813201520f7e1d8006c8bbf4156fed26f098ddccc152fbf a135afa0ca2d8e3045ddf682cc1eb1f61ae40fa5c2cd2efb3c2f9e608032578b 77a13ba6ea9bc338b9cdb03dd510cb0b7eb7f54a5e274b7ec48abc85cc595855 1040a823e775bea9c1a8e9fcacb1040781b1f6902a13412c87c3d9a1299cc10b 62c4f838a3c9a38343eb1d26b6369bdae7d3e8fd209cb69d75c80f7f466714c0 7ac961979d392a311c48206f03f3d742321887ba3a5cd74001a470a35107e550 1cddfd37c3618abadd9c60914724312b89b74afe024f9a1f20ff560466b9bfb4 18ac41fa237757c8689f8632adca99fea0f1a307bab5ebcf11369d493fe9e7ab 933fed92a5872fc21a62be10a49abe1a13762d03fc0a566fbcf5594be6b32726 a1b8412cf1adab469a4411aa0af9afe34495748cb28cda9c5da9a9212d73ebb8 c022db0036727f222f050a7c2aa432faf2883d3c6b997e50eacd702a75856c42 2b1ff4362e0dcde0ad93e26b61741a5b1e4244fcf751598385be4a125ed8ab1e aa40e79824e22bb819570b5429a2083bfd9a06ec6636973d45571209544cbde0 8e53802aeeccb6bcd278239921505aed7d5130f8c28f9226d08dbb560f2c1daf eaa68f607a8ff1cdca94bee453b5dee55c6a334784660e92f601c62dc4824586 3febb2d46f492ded355d09d2dec00aac9b9b497a5f5e1b6bf0578b90234a227f 6c5178e59d492efec16de40f801d945cad0095d77238377e61f000e095d42f2d 3d1f6e1cf51155bb69152690709ac88d5f05e808a894069404fa7362dca0aa6d 0b0a83601c87486b2027b205462bc7e97db0ee793d86892ae224549e2689a45e d5d5d2691090871bc9261635092dcc47a2a65b64b59d6ec22bf1c93cace319b9 d05a99e76df502ecd28df4f7aa1dd7be94fd136ff10c749b7711aab4408179a3 b35cf9cda5254262387286f0b45fafb16da1da0797174b862cc636311081a5b5 9dbd3a7aed4cc15f603d7f2875bd720cb050f5fc99bff23702c04141c53cbd15 c8c98779009e2bfea5f3e5991b48f0ad8be6b666ee0ecf03fd702e48319f6f35 02cc598e5e2a3a739ca71e22554285b7f673ae14ffe008129f4447b0711af645 c7b5ed241f6ea031504f4ea6ae217df5e8ef4b5a9490633e229c8e9a89de4b29 a69319a76e4e5494603f5ca58010b432b1e3399442ed2eb06c1df210a7a5e491 0b19df1ca86407de679cb64bb371b7f227c9b2f8ed416677b2f2461f8e0b5d9c 592ceb77c143c61ac51040d251ea4e5f29ca7449f5fe94d9a71487510a53a576 38ca9f77391f2248ab02f3b098c199002011dcd32f9548cef5ed5ad1daef7af3 96b714230a717c3aa96dc98be2f21da3d78ae73f318a240050239a844af47674 bbd02bec7162043cca50b40eec39a4f6f935e3e6522876e335e0b5c0a2b96929 404717feab7d0bfa05b0d8c07520a98beb118d696a8a90638b5a9c7262b353b5 9f273f31e96202dc05e6d0935f45805b67a5a94db43a5272a8e360c49f67147c db61a8747614385b0f8ee99e4a2e475e676c620bda6a65be9b2893cee37edfce ce6daf079ef53c57b46adcef5daa6f85028ddb2b16d4b6080f69178f31043fdf 49643c89cebb65c13e7be575a03d28a1b5d858a38db849186691ae16fb1ba392 1844f20cb6697cc3b2bb5d03b62870a8ea705bcc8b78f5976bbffb3d1aae68e1 556cab7259ef74e293406d1aeb096a5d91ce7f5525d34fe5c3b077b42bbce7a1 9f6d1d663cfaf85877f4d1add9b87da4262ed9080960c35d48bc541edb93ce0b 10d3a87182caeef8afc5f4a1d0e8ceb4f7e658db614e5be575fd6a8fca8f58cc 56ee6c3809788e73f5abd12539f78b794af6314ef768d08086626a87205cc6e7 c5be6288447a3720d9a4599f225fcbd3fab8d790ace5ed14f3515048250fd428 5aceb25d6fbc3d54370246fbf3c0629fb9637c5ac021acfece1dddca6d9831ba 0d078d8956aa952b77fcbdf73ad643fe1ba5f9ba1ada5000f181e3c176e87b45 7098ae431dbceda3332627061ace8f6a28d3344e4f7ea4a40022b6310a93ecd0 6ea8c7b7c3f1bfdd48d6f87bacac9edcc68330a5aa90466deb32a666a4154b18 b4e8760efa45c8dc9918281185452442fd301dbc159a48dc71759934a4f4ac65 780105b46ba20c060b83c59e78e0171ce0d62583ebf278b5625aee31f5a29eba 91683d8142e4d6886bd526b8d308f7c74cfb5a226b2d9f0024860efe776615ca 08933790cc349cf3dc2c85365d010956f9fa42ea3566e5669fca6d6d81fec503 53f0762c283a5fe303c5191c6826825bf4a346ce3e10b7df1051270efff95d9e d71304725e7f098da2005a36193badba5a84df778d69906a0364cd3b8f0b05dc c85659ff8af1bb3b78289d66ef801d7fe63a5d1486268161f08e42d54c03ed62 fd3f6dac720774bcc5a9b93e1bb778246a71a3215a4273e287e1a70bdb524a70 9a0d3f6172807719d2fac69219983a0b47f0f433cf94f8de47888465da364537 ae81419d3f4883e95743fcb0ec1aef5221d08ad39111cdca7033b50f33507301 df9cfdc0a4948f52049b262f26958540a46a8e4afe95967e68512d7dfb04e451 eb9ccda75813672584db202686f3d28a71c5b35ecf1ab0dce501162493ee1239 b3618fc6ad014bf1e1940e0648ed2538f596bbd0f5b6fdb3e8c02d37d4e12f13 322a10a3f2a2ff99f319d6d537cd4ccd5faf0a1654b5f15ccd1067756ae23e61 cb7eca3a1ad40000f81607f5bf6f458d6f0208a7fee78edbbe10c0ae4f99cba3 9d9233c8f3799c22e175a12e11265b18c014224b054233104c01755acce94eb5 3afc849cf26a6d96186753b0c69b3698cc23074b8041a9db338fc9fb2f8cc9da 23142d03938714a2edf569d471ecc55ad8dbfc0837c133ac6674fda285611754 458e40c24591400629efd35d5004edb6faff096d4b030625bcb2279fc65a8a7e 76ea667014475cfe6777af23a81f4ec83a6001ccd2689f4eef42b2a5be75398f c47284d3505e1280b6ebf1d61de96cefe22fd00227b94df8bf0d886deac5aa3a 2133fef93cb95e89c3907d2b56c8ae255d8e4455b148d7a40f3a6f33c16a804e 8ab874ab6ac91ab5f4d2666f68a5d22da311f61f361c306000c7038eff97e9b4 7ee340342a7f6758b6912bb0656b1c297859cbf1fdd84cb24c8ab387e54e79da 54c8d5ebca5108f9efb8d5fc796f35088dc553ed6c7426c221166ae1ce28b4eb 9044349c08b3dc8e78ccd105e8a97117b4a51b7acaf296ddc66b304221659b85 942785c7c2db9bdf90f7a6b6be73ae0670c85075e2bbe5c613e4c182e6f87844 e962211125cd6172e4bdebcd93c9f9209372fa2c19f7a72753df70ffe2e35a1c 1c7ff647dff4fc897c764564f336fb737ac60d90ef0f08ff87caee4f38b3092e de669b6f97ab2df1fc68f134b606d88949bb4d4133c0ac9caec59a215155d380 d1853dcb2e493dc8cdbfd8a362f772f2db4263e14dc0499c3ccdd48fa2f338a0 5c1dee475937dd4b7c0777412babe377d294ae8a4dd22735a43790e6b741c094 f0ae69fe83dfce23f9e52995b89533227a5d8438ca3e50d67ed31a8c0e7f0fd4 bf558b3f03d2d4f08fe97698d042d24004a55a9688fe16b1eb8cb0b6ba5a7567 d321b8d8782f1108e38d1a451eb6d2e75f2a007460baf92ab7d81ab988947f96 ce051eeae59b0590c53471790ba81862030d6d66f712ddd24cd109530d855fba 4a08c6dc74a58bd9ddc9cdc2cccdd684a4aef4e65e6a8574e0f3b5513c7a006b 649342642d558b5c1ddb6d8835141ed99ecfa6b564ee24876642f14b218aadac efa0e9ed412f021524b3c556052b43a0352ec5469be68d40a9ea5460d8079223 26f1a5c1b6303f4aec4d431518d7ed5dbbfb5070341cdbcfbcac03da90d22dbb 525d58c709b0a2d613c23bfd6161584e0632603c58b51ab8f2e827162cbd3972 7fa554d2e6795d245a5e6b030b3b88a0760568c77b8337511584fef6d2715bd9 f00f41d88de12909b4d31f5318fc8cc6aa3ca68ade32852f4e2d130851446b0b 71cea233b10fe02dc5e73681643a59e42ef0f2af9808fef1ea34b1dfd95fc661 fbdb08c886b3b19d879db4e807eb017be6a77c03302f093b0dc3fc93684983ec b8188dc8b0cc3ba4bf15e6a6e9fd03406d220701568f4dc844d1d6145585596a 31e7925606f68add80130ca3e84ab5217884d48367cebcff712f9700d1aeb04f 03cc8cc924576aff89c37bbef4d9bdc951ebe92b1dd8934d48a9df4658435b01 adb59ee0372fea16ba01d142ccfe411ba8ea77a729e17fe0204f47b52d08c4d2 18f6339569f6da35a58b64826e02ae94d9ab244aa3cc5376b6c8f3b6c380daf9 a117ec705660c217631679915709d252109abe4d48f46dc17f9b7391f9005504 6702247c9a584af8cc28c4a058440e6c20687aa8d591aaa4c17cf57992d9dadc b067008932c5e99ceba6dbbbbb7d7595a8eab29f2623f503eeea09d2bc443a4d f00259ada0b69b34ce72112c26c528095a3892b951b3bfe22b96841faef8a4fe 2001bbdbbe24e17c1ddf54ed8fd8e2dbaa08dbed8e04c416b25cddc9ab817e30 83e34ef9b65f7af6a298b0faa03e0618e160afae9fb85a631a4ca0a397762545 beeb675134e3b9d98c4648b2d9b8abe59d080152bce534590f3053d90d21b226 9dcc6977867084e027f7cfc0418021af31f9dccc4f158ed9f5b004ee20caf2dc b3b6a5b444e710ade23b4d888d61207ef62b802573da86f2800a5e6188912b00 526eeef9a7f3196e7f8b986b70de99f4744782261eae99716007b896834372a2 fb82a95ad4ad6b32c2cc29cf0700d9b6110e79559cdba4b6f8ee1af38263af4c 15f5c63ed0ae22fa6804c4d07e1b49a632c7bae10798ee9ad3b4e7c2e562584c d05bc2b87b11eae535c3586a1f4fab116299079964d7fea27adeb2559dd89c5e fbf57ff69979a3d2d3e2835e6b09c14a2f3cbf10601102d67a3ebc2663907ca8 cfe082735dd97467432f90c1ddbde796d7892acf9f74084c293ac720b2876dac ee34f540c19afad546329de5559b7be4c30438976607095e75fbcf03f018c7ee dd4debaf8055a84dc6bfb95be09f5c2eeacc62d62bc39a8d7af54311dd443d22 e1da1a9505064d8ec5352c2df1e97e0b78a9a615064bcadb35652adb77c964f0 5a2fca853add98e2bd6ca39057984ca8c13b562d230cc200879a1d84f3ff7573 00609e89392b69a4f39a29f16245aec74acc38a1e47a28cdc760d1c7b58b81ef f56b92498bf34ca73fa6dd379f4803f185034418df64aab9041d5d28d06b9682 dfd96dad4497e734663e27b886134842e06a5b67c30171eed057ebb45c6d870d 9a80b84a3fa541ff73209415231a21d8cecc5d8b36636b61afab66a2b5a27880 0020c220c583fe635a466272e1354b117a14da2d1b610d725590f53493c75d28 50072c38b115365258af029f8a0d09c364705b277b6e22d4e161cbb5dfd94fb3 27585cae29226daba2ed8b512eaf76792c205309654a9eb803c164f71d558606 1da93a09c90b28ca5223e057f8600efeba1a5e5417f999d764a49f1cf9e130f2 c604b60e77ffb22425339810279836e9d9f727a097017c30f79b11efa1056510 d96c367b6593a27ddfb8bb3203f4f231db999bd3228bfb9ea9af9ffd3512a634 1df35bda04906c2135b283512062eafa20f0582956570ec96e379bfcbe703e0a ec9e17ea6871e8fa875d11e3776ce2c613ea86a535db058031bdb09139b2bdeb 8b7383ee37a79e0f92faaf51bf65bed69e532cdd7438acd4d70ca49ec0bc0462 43cccf7639a6129b8b53aab97303d8220ed24441594e6ad3ebaa6491706da146 81d067c37b21b8e5c04e5e4fd287e2e352e147be1901c0f1773b371dea82472f 6e27039c8ac07602ea10f226052dffd0c6551b2465e175e6b4bef1ea60839236 e48a52f4293bf174c268fce1a6d8d664fe590ed9254f4e4b614f78bf4c8c1ef0 20d55c49ef6df56ff4374bc9ab785f8f74022b38c303d408380206e2449b0a7f a06decefe45c8bb9f6b1cc65071d24de19e56fc9849fbe808b7c4bb3e3e47287 b10cf49ef6ac31c4fb55ac5fa96db2a27e343e33f236bf5ae3678489ffc1d524 f893ed1b6c42468700f2edb147c45278eb07cea8df34782b7b04aad43f4f0b99 e41ca7119b0b34fe55a7574a445e7c8f606e1e387e3da6a2d5cddba5f0c889b8 b3fef6ea6f3e562db5e7e680b680a9e888e6e263a0d6558787d029631e243799 5f4bebcad15517b7c243522c87891d1e931690bddfba243dcc3205a8dc66774c 34164ac8ed888b94cb2d09a3b770026fb83f04a1b383e3b9a1834b61bf3b9f6b e2de4de415b97b51d60e7df29b3574b630961c80a88cfc41a5803eb1a9db2f2c cdb7bc7cffb8a2cbb5d0326fdc033a4ef4d964b3c63988ffc0564d8079fcec0f 3f4e968e9b7f503cced2e4fa148049470e8f7dc00b56a3e240561c5936cdadee ef1e93ff43812c1aa6c3a2ffa065b2d13782489b8dac97f791d04834edc9ca4d 340ac3f0dc455e03f3b3350bcbf32ec5c0a82f9335b2f48b3960cd06cb2aac51 7ef662aac128015512e783c8abf2569778b0bcafc8dcc517f9e2cea1343c7c92 9002f467232d5daa57c3133384529362fa3f11e442878ac04e023064c0c969af d101d3540ac92f45c0830ca63b0854e5a382932dcfe9ed49accc3f5a79a7bc8c d196004e714dbead95e7ca40a672a86f9f7ebe58d2e6b9d549c4394c2ed46748 e6eb386bc04d447b30e0a64bbc1a9b94d75752871067b12ca6a3714786f63f49 261c225c0471f8826fdae9b9b6b1875cf7d1bfafec1098fb248cf705bff94e2b a59e9f38c890b6f70d914fbaceaae1a14a7667741260f615a807c7d8757f7355 a994c743d5074023bebe0b3b5f4aebe8183a67738ddf289dba9a3e6a47d45fd9 daf6f8b83e38a2f6ba070beb531baf76cfd611d93e75a3c324ef8be17a6870ad a54ba31f9262eb99a3303c71f6a837a93353e7bccf5fcd9247458ecdcc2c34a0 47d827a080b2a4e6a1cd473e7044a02b3bb50498a1b50caf5846604cf572861d 1d2d3af66195487d788059ee287ec5386c95e4738511ee1be79ebbdb19ad28fb 0dc1b706f7ea3c2307799528286192dfbe43391412af4fb4b36b70d98e66f0f6 476f71bcac2b5bbf1f796514f0469c39eb3e59281a385be410d76be41fe238fe cbd209101d48c14bd2a3cba74fce06053632f4729007df26caab4a5c99fb4dd1 1097da758650f71c69bfbeb90f3fcede5fef00ea919ed2e5eee2ec65d02b7f31 b3e773205eaee40edeabd7087d31b308224818164c3df2a3edf470fc04a37b91 80f802dbd7258d31efca0941692026354c37c379b9610ef6acfc382c679c956c 3ffe877ae038b1d2d39952573ccd0aa5f0b14effd5541441f680452bd0667b3f 9ae0835f7a2ba39599e13bdfaf9e050adb542c3e0a9ce325ec29b32878355a94 e0f19aee81781d30a690a8a56eb628f53b0f29f1111ac267e1560e671292d7ab a878f9aa1e111d2c2e9e87fda087597f7ecae1508c1020771ec589be42977d0a 36d8d17dded6068c1043819fefa1588ca525ef30857135cc70d6549b6153625e 5e53add36fa3123ea0ac930c2fbc8ff775feb4d1429c4426fc76a0491b158786 704bd11edf49f3b7f65504cfa15619f052b99b043175932221e9da2f4855da61 d35da09b53582e2536b3114fe34f9811a99416559c977fead7fd9f0e17a20a77 956229e0c6642f77da63c40c2480e59505b7b84a504c455947b1540ab6b8c31f 440932fc5c0393fea2d40bc23844765ea66aaa79b4d32426880eb3140cc7af69 1e79c15a4167c5e96bce48740f96fc3ae2ccfc3e354e732b44904468f3ab7cd5 44ac48822662db3a2bad4492743279ed61d51d9f326a82a160ff1ecf3ee1ddee fb74348c6c786c20a403ed927b6fedc32b55e1fdbc9db4b8037ff164e128146b d4d20d6d363bacd5907dccfdaeefd9cb1dce51e8fc76a9307061fed3ad2ad263 e361e3aa3fb72b15bb37e2a61b9bc9992c7eb3eacca73ffc7f3e795f0f253162 beaf959ef7380f6ac4f7609916fe7d52814d567bc8cf5b5fbcc8c88d46d42c38 fab280dd341fb1dbd35a7595a9b77b66f640de7638003e856a17621da6ef6efb d064c96b73266a6e32390efe44201f285b7f1bc330d97383df2c8d5a09f13581 9fb79f04ff6be3f07f347304b528b44ef2178db5832db80cbb2fba520e11f71a 6433bec9ca0a1c7048104b8ce235f3422a248238dc0d6f6e9423d58340c21722 37b6dc7cf42750aa10ec5a3fedd188ec6fe5e85f28370788711e01d044f28267 889f5875d77a0ede9d2b4a71c05affadb96ffbcaf76efa28bf61d812c6c689ce f5c4920edec4eddbda1c86d39ae8435a46ee985da2bbd34621028b1f4614daae 8fede9237a0fb7dabdb884aec30ba9b93383142292978304fc3302b43ee77117 946c4f49a62e590671e1e563d3082cd4c14baf8f4150497d7f5549c0cbdad1af 295d9bf7a739f382685d621cdc9c9dc0f3a337ed5bb097c4d128f6f730e12f01 f494d699e8395ee918c848d962a516a557b67cf6a29dc13a029bc4964c6f3e09 0e2c52fdfb46b0b5a8a858b49e16cedc4bd13df37fe4058fdc93eb51944f1380 fae35e235831124cad198902d96b32679ac9ef99ac93be3aef88b4545c918c27 6dea670dd3a08ddcc5c51a21c33ab061fccc782a26464d159366839442c50490 a0a4643ac369b23920e8285849343a4be79d4520f709baf53695ce03ffbac274 11331dc3da1ec18cea8d9e5f81a2eeaca05096d060aace7b2d95dd1b555fc776 7b1ad5656ea714e0d690013d31d03fde2f9038b9de08896e2c4d2025ff04c314 b4470cb6004c7a6f4153caa77581de809cb33c1f495da70fb0d74ebdd7f685c8 c38b1a155e9ede3c2aebf3a57243a2d73df23525f3062aa96e14200a6c56648c fccc952842eb0241f9e4aae4c1a837261bcc664c6eb971f56be2029425f91664 add6f094c828bc3f794e2677329e59d3b8ef3b48ed19397b0d816a05b4c9bef8 8846f8e58957613d9d9624f256870a19fbda893018eca02396c68445349c34ca 786341d99163d5ad083c36b431b810cb8043d0bab3332aed8783946b48e647c2 0fc0062eed5e991b60b8b7db0bfc32fec48c709e539cd03faa00b1ac370f1d71 06e58bc577c9a11122baa1c2cf0b147e047804c34b19d4688ae1f83b8ac0457a 65cb3df470b4c75b7d90df1326d586d14ab33b9ec0acf5b546ce391a4674610f7ef3dd885b3cd12857426c3482bb985763324023e779ece72d721ba9b31d590239f13ac8d1dcd800d9077a1ac69c1320a4498b6d3362f10465dee840424d690decd22c4f02ef85fbd05a9f8a760da021728360f9fb765b97fa69a2b02c0d6e08b5cce62283cb2c832c96f2a57d7fd7aef136d02a99b834796f6dc9080272f60b4a2b23449f2f465ee20ebe762e8c08f5e7ef44bb416ed2dfa1bf8184b893b00a19cd5650f8fa059af9ad692d41502230698cf25175e3b4bdc2e2480e6c7c7307b68b822874eeb6d4a9cf057d5749d84e9aead13bb88b267443ad710020e998072857c16bba54ec448b2c3c64aac1c3eeefdf28d5c1d1828e58800ee5808128088b424894e6f6a6452091e8ac73d47b4241268311fd3aa158cfc8b1a7aaa1f4084ae83b4f80a0435d69233e94539e5eb1eeeecb1e6e09b9ccb3d04ada59c7b703c47aeb975ed19ec1efa37d13d3a06c8fccb8c484f71f226a370abcf92c49f70463deffa42af3c18807c2c1a31d09a1c19b6d95c861048f2b112d2db4061ab70964cb5b6203231b3bb9189990a38132ed0e1618e0b2b8e4929eadb92e2e3da206e0fabf0c151a3c6ec701cc2dd10dc4b2f921bdeb2a1ff41b3e31aefb51ea760445e22c99be65c1a1af83bf80e9e76b876d87434962ee2833fff510d90393970a8fd48a7ec7fa1c00daaadb696fcc81714c93874ac939dc218dc71fd8c99fee0573d546b14fa82c36d97b27086c0a96b62a112a528f1cdc3516cd17a51e2c9d0f87cc895aa30efb868b562a7a8ffdd84cef51cd79430d935e549204b404e4a40939ee50323aab3696c2a6f2ebf17e143967460d2dc36579a006e90b42092a52065df9e10be5b6e92e5b24e966c0724aaf5cacd5578cb79074345fcd98c7148b0e3b022a3b9890709938694ec6408e0fb0192fda1be9827539e6ae130e850cec0ce284279b574dcd66bfe458851a26b2ddcaf834246b2864fedc1e8f531e78b00270182d503a2e806a4e0b37397af111c38c1d72b778f043864465217e7fe76d04882e79c57e93e7fc561ccd4eb3793ccaa9c0f8ca1ed35c9d7a1ca70c473d0709f7e2314895b779124bad0b6cbd6ca97743d7e836b0c45e124217f34d7c35530febfd255f24f9e24d6a25d26e17ef0632fa475cf76d4821b9c7975eaed0048b0155c3ed02f4c2bb3ff31c8d8e257aeae1bb37779547ec2183377b14bbb49c9207d88d95cae75d3847b0b2f06979aa861587b878c2c6cf476fb4c9727db36c1f03b7693848046f947945ea466b4d08bb060a1ff32845fa58b02ebd1bf36ab7a2050671fcf70f3ccba9f98b089f1549a733827521574301cdc0d5fd7b0624ac170a0d28087227a63fe1a46e3e3f20afd881ae8ffc4e4850adb4360a1bf20dd9c80ad07aa0f85caa9abe5823f3bb543b2d5113f6ebbadff1370a0f1e6a39b2e35703c77f7c1b6ee715e5f9173cbeb328aa21fc0e24b79a756638c577f1759609b2042de0622ac8b5d05a242a147926987d07a35b8cc99961ae3182cc7338ddbec60c179fc7567d6e68d6c373d21004f2db7ac85f57377b17e0a6e2ceb8fdcd615d095fa905c96c43cf0957ed0a2cb4ce3c8b7913e83c89dd89f7ad1534651d00b00d98f8874cca830480f7e0867c58832097bcd0ecd97eff201196cf334160f15b090db2875e95a3c1e99456edcfd38be08456d40ea235257d965b70b3649d329c05c78b4ae8fb9cd53cf89533c971824c0fde7569be3fbf15442147d7cd23c97c022e998289366253eab3951208be74a423dacec3bce89ac6db809b66c859411803c1f34d137d99fe53e88d8e904bf3ba9e89926aa881d30c912b91f65e4723ee06fd197ca9fc4d5681a6ebf76ad9be4fc936cc125be43f74c9036263bec22fc40889001c95c6b086aa6c8458bc507ac0b16ba4183c1459c2ee1cf305511d1a9a045b0e7b86e7e89948f95e179025d776fc989b6deeb65c7e49e9d27ccece6e160643d359f2e80f6a4fafdbeb7ee48a2dbf748664ee91935f8075f706ce71c8c909a31d8845b042e8d3436e2077a987a7fe6172f3460c054bbe21b3081b785e500e930e2959482446f4ac8fb8c8c3bcb48af1073cf7a0a7351bca0d7ed811a51a0632588d794c011ea5d9d12007be858ef1c94cd646dede449e1b449eb9406f090bbc6967edf1134a2028259113b902f121b0c685fd68ac379bc40559ab2d441d0faa2a490a7bdc2a270aeefc6032162649a10d0a4f86ccd7854f03c750c38b380ee1756eaa0fc7527f84ad0d10a68b57a943cb9380418eb888b54c1df922dc2405103bf806e2c60e92c295aa1076b0f4d17351f3ce4c300409fc54fd8d46eb30021eee04d8935ad26370fb68103cea6cf4522b00c98ec08a413763a0c8bebf4a00c82619bd7a1c36edc73bdc4b899216194994b9925c4607d2441084ffd3164909613f2567050ce2feb6278378c0bd65eb7f47024415a5b56ee12a0aa4039b4f0a09ded616e6c48865483e1a8f12f0c60c604f8f4cead6b2c0164a6895bf517a0ed10cfaf8ed424721d30e099c01def8e9e8f64155d020a2f5b1bd64dd3bc7780e8e3afae586260afbae19daeb062a5048cca821d40349a0f39fb108dc8951650a3079c546f5e100c0fb7fb7b4c302a9fd88787a2a45b75b3181d928a7b686a8061895bfbf24dc7ca10dc6abf9d85a372654fdef0ce15be0d4da7d3b758826ef02282c52253a45cdc4a18509838b4c7be4545971cedaf3d698ac6ee4615cd3a70389548089c98d60e4a16623f3f86b457d5671befb6fa23d5588ac486086d5fa05697b000ad52dbabfa6fe5bd2400884e4b5a187d7a011316eec17727829c2aa0c3a1960bb0f358415b94810d77ab2f97f42bbaa7c961542be303b2f2831f6200d2e90eda342052fcd7787af5af2ef381c16776170af230d4941be12d9caacb20765f196a8fd36f5d614bbf92b71f0d59ed65ae909cd167ea986f898b7c09bcb00039515741a17d655ce501ec2866f579b12129064aaba744e37c5f8a476490f0e5f1814b2521cadd2222486df19fedaaf772f1585f5232bf986a8b8f21d926103fa62bda19a540401c666527f6a2920f1e60fe28f43aa39ec1ec9c1c38afece095a5320707b6e79f95919bfeb65ed8b6b75a3ae4011249bc667776ef5ffdf82054e73648f5581a25c77c6cfa837a4cc488982a0140996fe77699930c09118ca0aa8654d07a65ec1fb081818a694935e54af3a790b6c6d15acc6597bae54ff62071bf80771227b5ba3ecc694541d5547e8bcc7986840ce780c62480cc9309af70340ad7b390584a252876b7a3ef5412c4882e0b3583ba60933c5a12c82dd823c063f45ce6c70f39f36e25f4326c64a5220cd6eac21c594813ee152b0d3b4f7c802a4ced6ad653c2212704589ac3d76d5ab5dc51e244e1fae7afdbc52f6a12fd707a11c65d3e9a71a6f5d6e050797158084da580d8c3892a55b9b7da803488edd034c264e81d25ad586b7eaef84b2191777ae8b140eee74585bd10eb33e81e0bc00454ada6101859ea2f8a5232dc667df286b5beed2e3114260fec65b59b4c7a4036c1143acc4d22177ccf86bcaf12e1419e4f51b7d6e679d58aaf7796e4bfc3d027abfa54b0ae15e4705655368b109050ca9fce3cef64ee81bc6a0715cc3bfc207b3c4d66254186d3f23bf855b1975d9aa9ca4dd51f901ea3634c27057fab5210c3f50ee571c488eb1af2ff239ee9b85907e439b3050ff45e07b2fb9ba9977330cb0359f147b9ab6e9484265afd0559460e8e1f3764785bcd0535334c5df32ed05894b34a15facc4a55488cc4ac09c4eaaaa61f889f41f88942c1a9f4ce451a10c2f5fc988a019c4cbbe50e625fa387ed04e63f40c15a54bf981144a9e1ca26409a315e3f51448e6986aca278972b91a42f21e6559ab012d4b34421408ee778306b69a0cbab233214c20976685ac9b410241f2dcf910cd096d09293996d46ab8011a775bb4ee3faffb38cb50894ccaadef63813f15d2c3c9ffb88416105e1bb60b7ce89a8aacde391a773bea6dba4d04eeeb3defa9b5dc0e04000b79c1d6279a0b3f6ad3a1a2f6b657477475c3feb8933d8b5f3afb5436c3982d901b38c8edfb07759c93ef99887df3a069d86f031634bede876a31cae25cd13cabbb499c159204c6f550975fab7e4dcef647b42442a3e8b5d3e4d15559b9720c5d68ef84cb2d057e0dbb3abea8dc563b1534e531e833a9439909212c1a9bdd968f42d56e270f0d84cbb380c7d0c020404a68f412ed18935f1cac5f4577138a79be9f0cc591f20385417c6ca07bd20724f39f7d2921e642e4221fd3b29dd85feb2cf7a55d4b26027b01f382879b93dbc3c25e1ded07da47c350508b9df4d57f849bf7c33e5be705043a540888a13476217f2f3c41428de3bf13792f0b7237d1d2fa4a504c1f1a07026718657e510c6cb244e4b5a49586d8335b804417ee9af161477ceb594cf308b96e94b2be8dfc3b377e9d2269c2b24adbee460627570ebd5398ded7805daa0b67015285883b7339b028a9c34c134233853ced64e9c209fa4cb679371da0bb07b041fd306c36991663cd43e17b59045f56dc7b3024bc0383239d66d66ca60001733a487f28bd6139e058aad409f8f2d0c3a02dadc5a05f84afe0b26bdb8cfd061d81357e9bf7b7c7e39a16c6041eb4dedf223712b338f2b3f80956f7f2ff200295f5a89f44ecad620611edd41aa9703dcfb22ecd06f10457ab172db1ab88b300ed56bad62db563833cb37d24d1f1b58c3a2c9c2aac5949f76cf4fb7ffb7eca03a0b47842094431d24200a53f7c1717439451184a39cb60cf62e5b2a2e4ccd90a848a9e4f895fa8457ee884aafc76aa306f146dc865684a8ded7826df0ef2ed0605e9b4613cfa69cbb3e08110e9caefb799f0acbfd6b845c34b5e31a6b6c3de05bed369f7c257c38f39a6ad3bd65290c5e21357ce8bb8386665003a34f4080304e3b64259325ef67b8a907a63fdffcabba25d1f9829c0d4fdda005d64de2bec07845b6e50e7c72b7f87c0c1ad83c40c85b904cbe415ece17980eb07343205890d8bce442d60b4ebe9010dc358de73d71316ed64fb9c81fa95839100edb2121e003c2a9b6c1fefef98acb1db4c03410382caa47d03e2a8bc794056c6fd8e6d75021987f204e048f597e75992ed6aefc99da41f2ae75f8c3896c71f50aaf40bf806f428a8383e19ef15fb3eaae0948d0c2e9aaae81e5fef6842daef1367f826dd0d1271d2623adcc2f4e095483138fd0c7c89df655e111338b8fad2a13c5605630c69f8e6d5b38baec40789ea29d479c0ef6ced0f246ac13cdb97d370121175280e71de4309984bab67dddc34528776a25c01802480792b3f1fd933dd5cce6d9405f03cd20ef8326a992b4de41a32b95741dcd38a07061b4a75d960a3746335b10c4f43401119fe5509e3e45b312d9c96ba6b3e4f8d3f7bfac2f19d31983d03e2043849ec2289fe684b325eeb97609e9b08e5726dcef25578ab09a5375d4bc0e0082434dc1134d21538a8ca2424898c4f2ba9748b93c4af88acf4d745d2b4e1dd0babdec31be82a078ccc0222609a2a5bbaf8ffa87a7fd1148d833be48e817c220170d1bd730dd66cd29e523f917a078b132f49a0abaa29a611514a2c677ce7e70c23a2046d3d2fe32a56a8daa21da9b87369a1f99d6e2ff5b6f50d2a5d948e9c06d9a0ce67b9fa235d4ba0fcaabd63276eff3eb52b69404c9164fa17d2c043da0880c11703bc11d8eb65727d55f375e3b75e54c85c00f91677345da43b838756075080bc1b49fc81183f387836dd118eea6596876214453c287bfbde2a46462507ced752b5d1b6edcbc552e77e5bd9e6061a84031387d13e33ac37d44aa3939d0a54372bca4462e4c8ae8113f9e2d8996d4d6732ad92b6e76bba43a7cdbc4ad80bac642168c9311854cecea979dc419fdbfeaf182273280749b15505bd700e0a02bf9acc27cc8f0f771e2f703e7552ecd5afea993e6811fbb37ba8a87fae42fa0a8878cda425597f14bcaaed40cfa3f33be6a4b1075980692c37cd0d9454ae460e6c58c67a5fa69884a461f8dbcb81118beddafef79bc34b6a1395f68fd67695063962596543180b1ae9156d49489e27267d9dc032288fdac9d86000f6f465d1048468bc91146d45f17afaf39e47d28b2c744ee3a6de021c97373865c9bdb0950b7e97aa78582b4e7bf788800e0dbd5122a3f780a6c188acae530b7c5b0836c60aa09b09c8cea6f3e8b65025df0738f292f18563da414373ff7ef016f60a143a0857ef5c1163eec44600777dbd732a6a12a204d01c90b09dd5db474fc82c106e04151c12e12dba98a6aac8d33df8905afd103f5b42761802667ed6b78dd834e40acee06deaca3cdddd3d7bca3abbef92612fcae339a45a90209b103d85b54e0c03c2b1252e47c410b04b53319ac5b97541cc7cad24408da814375ce3dcd9e2350209537acfcf1e2aec40c5758e6ad9d8ac8a7a193f4f0aedddd48630ceacce1e07375a7cdf8c2ba51a4008761f198da2f713e0b9b1398b8a614f31fa1d5e787f0ac0a8c0ac0e8f1a92daa1b8841fe47f97efb2cd382e367496ae7254c9f612e606d6d05b5f98eaf06814ee0228b41b275a68eb5955f6e22e77c3d2477ee7a1e50f5e22d7ac8b10f206ca3c3c0ad4e3c231863bd8be360f8240a9a0f85e2919bf08a124cabc1416be270998538d0831edb0f72ebc0a7232a509b08df8024d2c7a0f4d309ca2159e62993e0c5a6d8a4df52c6b8108c24fd9bec76988644356c8370b7542c2597f700a852825ece37d614472676bb66f2151dd7aa557ab981e35a10b0a9a62dc87e52d398cee470b26eccf28b552ea11686011a958395c5e27751c0bab815e9eeeb20488bf3f193c38a4dd07aa3e20510290f4f0e7b98df08ef545061052e3b3b91f042aa2d0367981c9ad71ce43612833b75b360180ecb8d598b90dccc788d4fa47036794ee147784ca0c566ae423148814bce16df84c15e63f170e9874a7188ab0da26df6205ab775cf685ceda902b662aaecbab97373b6b7478068eef84722159269ccfb14060b918d5f6125ef42e8b88512797bd9cbb530ed901f69984bd02e779f851fdcbefe3f13af9551e2c4304ef9b55dbb91578afbd5b0f0f7a9903377041f30306ba91ce3333c8e558ad1b42199993c649e5a74899fe031eaeb53ea5a5cc51ee68647985c86432f830df9487288226a47ae714965f5b049b423c063351daffe3333edcff9728c3291ed6a7d4524f6ded9094e81068c804c947f8699b1fad1351cebc8befdfb56a78e147ffbdd33833e712af279549cb0b744f2dd884af949f5c22587ce711f6b2126264dc455884f53d59556e7398050bc90fb0619e14fc3297e411c7c51bca26aa55f8be6328663db19ef53dc4893e020916dfc0ff80d8f108319127467cdd09b6a23bcfceb80d32b2474d82b57f180ba853fac3ac55d7c43de763a158ca2aa3d0df7e51e5e994c899cd02ed87ecc30a0b6f5aff29c987f7ae520bf28f66cb623a699dfd3819204928386ecc159d230f6d9149d2c7d7303f150e535c68cac11f43eca3dd226bb2844281b1dee886fd0c919f7638c0fb762353dd69c4a80cfbe92ca4065e9589bf938ffc7ac31398ca0524a2eb2404e0589528f71cfd4116712b87ab61abf30df3aa47a2079662b6ce04ebc49f878472d66e888cae226c7ed371bf25f3f76970aff927636a651e59ed00d4c17fafe02f901a6b6286a0b3cd14a65219142191632d988a28c27db4d44703dec8feb603fff36c9c74fe9879248ca54c47f3d137485cfe2b2cbdf434a057027be2efa2ae48afc9a34cab88ca99ee5481ec4d2f88f39759b4ff57f9b83ac103d06c3e18d5ebde2a0e32610c8227c1627218331ceea9474e26dea2d3b39c900891fce0703cf0fe1d7f2d0d25eea6f00f87bb43b78bb939340df05c5568a27807a58b94354eb0801693357ecb16b58873b70578f3a798c8650990230f4480090317435b6f409f1c1012e59ceab80eda976617ab3ffb7c329e320dfd019c0d05024f8a551cbacb3e8933c2bc8fbe7b555eafc74102cc241bf3f96bcb65a3207a0b3da01d42e9c13cc30afcb670a16179e4c88c6212b80e4807323710b13efda8042878ca1a1a56d97f5c33909d4afd9754e24e1e177fe535f6d5f214854e27f30271c90aa7b23487a35c5a8eda19417698b28f0ce0472d5614e1c99a8f7d383b034d1b4a23f5ddd69dbf662955fd498824ad281a5ec9c0f4d96b7aa86130622d02e44ca705da0c3410c5f5302b4e43d91f7561187b3e515a9ee7c23cd7a72ea405bde6fcbfa943f373c73326024d7bb6e89a6030778a5b8cbb8c7f0bdd12f02e07df2c5ddc3d0b229c98b2ec5dc517a6633084d7c559b10956697f9446bc3fa800c13b738f8c7a5b41ad366c9348ed1657b974bf9f2d8b19e0f2356766bfff5405549468427cca2e52dcaa2268b0f4a9d9f439ff870569d40d3e005fd60f278908b46d39e6438f3bb19818e345ea4ea9c233f3e21e47635f7d3f90a85340586805574d0918a3eaa06521a2133662de97adc579fbd4bfe1b29857f8010ef296b3089be91d9162449457f95583e7f5e65a095eece2c7f2326ee7d2837b953b6caf04eb8acfa381f9973c715e21b18d8c023254864d163c80bbeffe4cce067d076908e7f95174dca88e6e3ee7e3b5046bb9985ccb086bb8d7e3be48990ac898576302714c32c2a1f0c69b05c78da1c6e265074d42ebc9f3a1c26de47be28ad038960c14f6ae1781ef967416406d346fa37de092d7cabcb7cdf9dca33623bfa6e4090f8e78e7b9cbcbb4c6c176b6673054ecf398f839f4db6bd02ace9935c960316c017910a1d67e86457825a00dc4edd9caadb1f8635df51eb7b2871cf3ba746be90cf32e8a23ac9ed12685c3ecd3f2fc8f7458c82c3399002d024c5e6c66f1f8c6065729372d88c860d1ac57e6219772f1d103efe996c6cf220593d251d2f66ec304e981e54362f0938b001011be316a0757387f61b7213752bb7c9325705d27cd01b687491a082fdf452908d4434055fce52b79e2e68457414c0db820a627283400ed470e354aa0e5e0ef8b14961c4eea3f8b44c49870a7829e8698c15bfee2130fb36d13beec2e8494eba96fb08b38125d563c75610f1091658c60bc3f86737d0a77567b9ef2c0a698473dbbabe2689596b7b0b8dbbb58b80642b21dce94f886037ea3270b1d30f49160a5bcab195fa243a015f8e0aa6181b51b52850c3ca7b3084def36dd8d56848fbd5542ddd55a454ca7e20beddd0763bf8d062a91fb304d06bdc8afca03e73b87ce86fe595f90836663c59b735d96d7931d8b96d56ecd2006e5eb1917a4a6b09326af228c17e402b41ae6344f3d27120d37104b8463b0500b2df3599a8fb73072a976ee91c213b8478ce4c2ab8d7d236d2fd9c2303735f5049ed9d609e09de74b5b0e1f12551f8b6d531f151a5cb8e0825361c3a155ac1305c102d2ebedba39d335f630802c4ba21d20d252d5ec55218e326b28b0b3e53f00baae5095772307d7c0af51a952cf1632a4a536a95d468c31c75872ad8087a6033e4542973eaab5367940f22caddb96466899d9b101375695c39b460c5703810ce7345d24a10a08898a7262b98d6fe1e3d624cbf9f5a2bb1046a7407e1e24460311c6e0eee055097b341b58a20497ca1a6a53f023082ee08af93f94ac751cec0dc0e2a431918f475e97725ea83813150c62eb04ef560779617c8443bf54c9270264bc8fffc60d16b8c49d12d5ac383b9f93b3977b17fba7a0309fdb9d44cc7b06f3d63c1057c4da5c79b273761a9debf93ff15edf4203eda54818e9928f5ae503e13986d9abb41b34ab3f5163aa42173ba07536f463f16d0c4aa53ecf8993bf05cfdf5ea2feb3b540f1f2d68c4f0b9e16369fe39120318192b69251c20648a00e93f8cd837172cb94682a9e0075c1f3fad72fa49f001ccb27111d4928309f93094a514afabc1dfad28d3d16faa341ffebb5b9ad7250b7c1018243d9d92bb0a105a4ffdeeb0149cf43566b00ae764fd7c9ea4b8a247b3d75491bd7b48a87160d018938e8eecb0d1b37f6592050a15dba26572608abb54f2f9be11d7a7b63a85c0c7a3f4de13030fa560d63bf22d8fb5416c82d59dc186939a4ee1856e115c7850c0448e35aed7b0c140d69ecdbc073bb36c81c57208a986b5c4496fdc988a8b303a641d4d7be5211280f252bdc556daabaa0e12ecea8a902d32931104ea3587000f5170d251ecdf4393bc5903b8051c6d5c40ea488f5c8edc080d03e1753a1f706b06b7729e59c84d321ee42e85046bb3607d0b93fc15c9cd8850ae169544e370b8df2a856d55ad8cc570132367c26ec083cb2efc90abb73b6186e1e1b477d150f556b093ba7310e3bb838cabab90fe869fbb96262bd32971d13cd412a6a036b064a572a4a2ee7a1af588fefcae55cf6a73578f8c5a7d879da8a011162d53d1108b1ec5c636e4e31b32f48bd156355668ffb7f6963f95d04f467818bbddc4dbe0be19bdd15fc1e0a1024b51e93eefdb342ba8fa097dee4628b8bcc40bbca98a3061cd34c6ffa6d9046b177a24b48cf14af49ca1f0681064b1940409bd96d2d080474666181017e15a2a04f1e3353b3b5efb64bfe4a9a7f73ca7d20f9e269549302864b9321f59b2c80f2e19b5f039206a2ba1345998c8abe6228784ac0ecfec30ff87cc71b858f81165ec16eca9b7306db3c42e3d527d85334ebe53a9bacb81f0d7013b3e7fcd7c449ce0b30716bb91857403e719fde378118853c045883bc4b04d9ed2f7636baaa29be730d776517e78bb116aa87a031d27321653ce93535220819a046bb553a179641f2112b6ad3e1a1bc131269d54e45751dd89941eda50d0cdbad2f9b89e3a324876fad867feca528923f7dae69935a4f98d5bbfe052b6104bb81f3f3ed8d2ab97c8bd0cf1021e2c267765622a58465214e9213a5ff8ba20c3d53b1eacfe59dbea9d14f372fb61a5d31574cbddbc9767ea4b9e74c2f49b50a3b161d796a8c44a6fc4fa6a74f22a6c15dd2f187a8785182a1020c7070de4002c3479f07019a456f96ddccb7f4b3150677e9bae0d414a6c5f9e70c2fd87e2f00b6f4838afd9584c99f479b4d462de20006634e4ed7eee60b2837a859a7ecc00e94600ad01e7b576c507f1524ec06c5d220bc7c2d05b077519e7344b2fa58f901d7ad0aa6df3967a0064a168f42a47462852123e03dc4df7da73b40f866dd620f88650dfa70fd0d90a22352b326684930c74ca20232d25d08ce4fe8c0f26b5b084f078eb2d14f378b00aac1172432f9bb76b4a433b01af16fcea42f5217bbc708ca294a97e725f45e94b471d1096d8fcf509a9969c72cd081631ba518c4f91706c4d569455a7e7671117d8c932f29b0e0554ae663262154775284c2fb6dc86d069f874cf7a0edb0cc9decdabfac9dc3460a943cef3eb248070a4c7918c089f702f462606caac5758bb05ccd2e9448b92a0c75d0d96b1b02632cde9194f444470ac4db83409da3babc8164c39a83173ee76d5b8727b56e92c8c6ef98612a844a0004ee1934edf050594f618ccea7080aa4eead974f98798f00928ea7793d8b8507517614101da90fa3e4da3a24d845a9058082c68912dbe391c47d26856c04a50aa06603ec3b54e5fa3ad94c3343788a4109ebbf054b2647b10390a64c9c532b0140bb61b753e7fd3a8eebc3341edafecb2309d1e42993986183b2423eee75340f06c2a652ad6e7544cb05c35a73b7933d537db756d595e4236252689e0f78c40d2ccde72e91acdeef2328bc912d201f8508fff0882a0181c4d203d9437e9e9b0d191c185fb27d4a7815e3645fbc3313b7dc784e2ee2b18e5ddd196e264c62c80c49ade45a68657bd94d75e2d499e83c19eb57106e785e64a65faecccf32ae71042a23a5ebc2865a9d6da62c55d422b68e6a394fc8f74bcf20f71c2ee9440b6d0d1bd1a1a213661f356d30309389210817239f1bad7429304a1f529aeab824d4063cbdcb93cc417798a25f8bfcf4546c94decdc2c82354b784f043246bd47e220948192f0b6e7118a2466068fd9c737eaf5556dfc369af1389ef4a9a0961389d05dcab478004396fc5b08d0d3787accfcbfa15f2c69e463b6aecb6cf9514302300780fa7cccf7d1bdef1a6a979dae24c5c5d5d0fe9dd6b1037ed63d2d46665730b8f6ad20789cc9997c609bb25c455f17c3d873c27c418c616e61c3f4edab5320935a73a3afa103e1a1a00de1d9a2649a0c83e667f9c63206b215d1e045e4b8b0cd7c7cc8487fd2e0c5e694adf02fe5711a1344ae05aca706c9ddd0d4dd4de590ebca17d0ad8a0d3407cb460531c2bb0435b9c5705c95aca37439b36fe1113df0d70420000f8337726077110daba963ef3de0c7de634837193ec0ff05131dad50efd5fd6c54ad9255ef7cb41f37f0b3e759e262c50823ac7d36ba1eca23bc7190ea2eff381b7c8056a26b67e4f62723e3ce9ca4f8d97e271c97e4cb06bc34ea6044528b2c77407ae89e181cd94563b25d08d4a2e39ebb6f5438ffc310f052c400776d2f7c6b76f9c0c2fe702720aee89df7141aaf9245a854fad9575d5912c6c0730576c6c4ac0796ef555e83c1c7c31879f4deecd18d58e5c6cea14a875b958089247841f04ffb4ae6064f2bc0b6d8754d824beb36d5f7e904d56800813aa5908e6277488d9dcf2c4015a3425cc59bfbfd1933de4d7e9a3c11ccae34090a33e09a87c6b2d06fce6d82eba89a8a5cb37a5656bfbae5b9b282cf66d00214791210527662bd9e4d30370484a6b30764c0a53abe4a8349f2b926f6d8b7e296b1c61075070644df0dcda8ee54cc0c54ac499334f5be12c81aec7686f8e0bf8a835910375680e6ac96b0502b8667ed6e81e462a4bc3032494ed68a257f2d05d99db2d0ba7ec6fa0180afd8e6f8d0c458caf170014b5889b001ee35f8d1a1c1bc7e343042ddb9dce58ed9da15912394f1564d4e88cddae54c0ce26d0fb00ced17c78c30ef052991a1d11cbf0436e911f7364b7d5ff267c999b710be476ab63cf02027d0ebff9d0736e202a2d1b0ccc388860a6162b56cff615cda31165cebcae03216208b3dfcad56d16b5e5f7c5bfb2d3901ef5904ad2da2f5b73fdd0598f0d3f17e30d6e7a89147e317e2c73f0d6d3c79708308a0234560da5964324060a1c0d9bc80fa8cc17fde40db11aad54a0fd568f2ccca9e8ecc0cf4f73b15e6a759c70ced605f873336034baa89e45de9541ed3069efc7ce6fceadc2014d572b076e42743b0d0b89c83f0209055f6860a03638133700974919e5fe34e5e793df3e98cfa6f10eba34b9d2a5d56bde1171088d6a255314158397ae91484334980fdff41cc3010198fd6867ffdc1f01c0a6c0907c4e2b198f120ef1e06067bc6f0ce2fc011b2804998dab19bfc228ba93e3c8246229fb3bcd965ea62c0022cf48b8da461532970d15df919bb2e171936b4f669c89db9ad422ba22cd002f3a8f3ff64c01c829b7097929b6ad787b24dbb108493b0d0d15475b3496066d826f4c071ea315a92e9f092659b60c75a71b7d1a028a202925a3f0b630df5e0de955bb6486e0482e4488086ef52eab903fd8d5b87093c8580466a9bd2fbc77df841fa7fbd1368a9bcc9b060a7721152685f81e56fcc61fd62bb405b029a16663bfb84228a37dbe3dd4ff00c7d783d6a31a24acbf133a02cc29b7778cf6eaa4b8cd211724dae874e8b369006913dd3ed6f7c8f5422978bad7c416e9230af9451dcd075b205614b4f499e00394a12265b446824a0423ea4a9fd3fdf587b0753d00b062074f594b07d502c603a457f3fc36a4e1b17fa908cfb0bc048ffc4a5c65e62015c43a984d643b46f30172827892efbfee772b4cf842c3e4747fd70f3fa080c7dc8ab429e5fad0bbe50bfb4c0f8172715f9f87ca76c9fe63dba43774220c01c11ef5df4f3a7885db660bec3e20b555049d58a1d6586fcf9eadbb6ddffc6785ec0633bce4684cc8f64900f5d6c486c34355ca2d4ae4242e71a6c1d2e12864a6aaf92eb5ebdce20f3d2b0040b5dc50041546e89d02148a97c0803653f17775a78bd0751dee581b1f7319025e14c931e23fea19defb2dec73b3d4b95924139ab47a843c1be28eadcb466207544d3d6ffee982da7aafab8bb3671e4bdee431515a2497922ffc8e15810cca0fbc1e805a070b8ed306bb6d66f592f5450139a045cfdcc26ffc3267ea269ea80320c6484c581bb78bb32b5321f4a06566530c3c8755870a6a7e79153adaa67f0c73c5b74fc30b720eaa466625c04323003f46450d730c80ca03c3ceffd7a06805476153b59ccc38e6e1601a27d24c3dcf42d1c8753796615810691e65d3dbb60f0b9c4a31b923a0e804d12b0fb50dfa451cb8db9953ddf89bbb0ef58d9e6f6c0aba551e95d6b6f49d8ded6369f8ab250c079e29b2d2dac156159bcb0a2e989c0b36987fbef9ad52c3b238e88b20a62edd0093a628add8e8f26f662fa9311279003906d3888dc33792d32ac637970cfab79eff6e5847b1ac4720e35287e34a920608967b7bf3b0d5e7df54a720a74235918cdae0bb0c712954dde8f00f1a98ff0e348237640497856fa6bf030f54db9594bef6c3ed0506945bb0530091293d440baef08b8d190df604e35c75a26fbd6a64bcaca4aebda6774b266ee45f7070070339f56e01a0fd5951a73fc8d9c431d3921ce52b4ec42d99f678b4896b407090078f50ce53f06fcbb2878291b0ce4ffcb494865495c3e7d32cf1131c0abb53f104248cc776ba19b9f45dd4b4c1462fe26c5cc4b8f3c629465cc2979c6214728d0131a6ebe5bf6a800ec33ca8018b98d94e8da8fa287df153043d7886dea691290bbdf8976f209422a4d2b74746077f73e9678d56c1554edf525d6a40979e933a02300da5e935e61e6cce3664efde6b8ca62bf15871368a5c8ec16215dad278c40eb80ffa81598e498922e067cb9d7b4a8cf22d2f001380de8f67887be03c054e04f6d0e0b154d24edc00d2843e648a63bcc51f178e27e1ded0c167326df4ead3079f55f1873fe609671ae3b609b08517b706b800be60e43bdcbdf20a0bf5e7c00233dde87425fd97b3e9660dbbb1957edfc6b3371fc2ee60315a4e1391bc7a020db071b6225598458ca33aa9c23fb787b91fde7523f4917ebcc3cb9b1e00f3e80f782f6b93346b90fefe32dfd8a07c0940d7618bfaacebaa1be73c1dbe67d83504e6880cc549675391fd77d313b3a98d65589a40f5507718a6bccee3fba229d00de0c29fcd10055f9ba9b7a41b0492f628d2abe6623b87f7d42ba2885f3f7d0100eddd3728f0e923f7cce05506ee372613e88bada5e6d24fcc7f8e74d6f3b8b8084d10772405fc3c922f0f76242e0d63a1ec8806904d16e545f8d0fde93e1dfc028573b291e46705dad8fee40e0595e3d3060da9e0523668e3982c17046a95990af5e12c4e1a725b72e56591da0dc85d8bef74d2aebafc56c10e60773776a6f400a00dab007d6ae1f3457759e9a58019ca58eada12465b09f219ecf29e6fedc6015dd1f4aaee22c7a7edcb244b10279c348f4c9c76212bc339bb6eab8bc7ee2500ac05e6694b58f7668a1ba1a79e17dea5b96375d04aaf4cd0c816c2a48205070b65deb58e54c3d062de0caa9431c98c6fe46d36cd04c7a67214a0aedd5bec660b094413b45621b8d7353898626610dbf5da152b9e98037885331eb3c37c77540c14064314f363d6e6d2fd7dec0a33ee845681eed300fb071db23ff3c6f8d45a053047c4d1ebda59e9c586a062b0e2402b3152c615ffab3ec7446ace46348486076e7373d6095563ea939d96dac76039ec21cb755240e83f3fa0887db9a9675c0adea13eb8d48f8c3d302dd61683b65b659abd4b4d5dcc2f5a5150c125251cf80a7758804aa29c75d1b336e99a815ec72539ca6e34300bcdde8de7c70b15f88e014c3d3c5a148c1b8ee22acf5d2c5ea92280570f784c25d122d5598bfee28b0f0e9a09fbd261ffe872c88df845237eac296e590a8289b41af3d68b721106cc81086c6313ab5673eafb8f359cf2c1f8cc963a703b8e6ba05a239da13abba4378105daf8eb6060ff46a1a6606c880aa9c3816fd44e2c088545da4f5a7eec01ce73084436b0a0a0495654c95c5bf372591d26e9a5100d531f494012acfe179cd72c040372976ccc83aa872125492d4f839b81e6d8dca39669746d5b6ace889e6b810ac6e510122cbcfa10e623514cbe754222542b5645738bf77da0a798e14e847a07f7eeca88edfb92386506a95f809fa8c1c7a194781310de6f3ee3453592ebdc03b17b14b70084e71ced82059cc33e0af1b493bbb8446eaa9f00e9be10a4c6d60acf111bd964713571b42a8c8dfe9d35c3557a6af6ded1969fad221f713add43032f0ff532d972666d130b130e4dc34f5b7fdc0423051020ec1b209f569076c70c00e263f9af2e4d76a05a8daeeb4ff52dd6577e9dda8cc3eeb0d74491b2304b0b4d30b0ff60fc60e179cd3b3ae9d3c6aba09d27c6e86a5876f4a35d399d689d0c308625cf05841c50dcecf7234786a5e87d50e24eafae2c380474fd35c02d040e7b43327cf218be97d1a436607f0e75e171c83694ff1e179d68b1f23188d2df06367e3a24af8d5cd76f2c450b04b1a62bb00df438d2bece02d46723cd5200b3095421f966106d2f49d149bc11b723982c0ff138fd01e078cc0cfb34a428d64d0c06ad058539a72fe4acf5e15eabcbfabffefeda93ae9a651836b91420ac393f0c339f71aeeda048e4099b1bb5b49fb3ff63a9c8b1b3a06a8b36d261576bdc2301c6f2461bfc6a641a1efd58b5e57272ed8c575f91710531d41d269e2e5974800d95799e73f61f4e1908a8c6f10a3566432a9a35b91fa200f6b11aad9b08c74804d7cb6c1a7207b0b95ccf707ed3e0a08512ecded180cd1dd21c954c55a425f10657dec23491fb17d81099d9189f8c234d8cf0fc8dc4c6c8290dbffc7fbe82a50c33637e391dbf9e110951738a0a5bee800de981c802cea2987c1da920e97c3106e05194d63cd90e8e8342d4683a7c453628e672c592269fe4bc8668ee7e879300787f4f4962fe36478400841673d7f0326278f11b61a88ef178b56dbbb32ba80f1c6de1d4e04bfc02de5b64f2bdd4dc20bd8ecaa705481d495b204305deb1cc002f69e75a830884d299a1e2d01493bc890efe25d6f0de1d1fd0ddd91b3ee5670c7820ed20ab2928559686b7f3841d21d0a4445668584b039f0e3203dc9a6c7a083f006d1e4d299eb84d5fcb26d4f5fcf8947c1bc23d002408cb8bd0788606c60a204716712cbd9ac40cf54adf8efa8ec85abc87c0db3ac90825768e1223058406b6cacdadbfe5723fcc6346b019de3abb6c64a3d95b58c4d1930c3e175cdcd50827d5db74c7f7df1fd0f66d57e02c6ad53c0f366a6865b27f09d3bd0cb1e7d806df96589aaa1e77f42e2549a964830ee92438f196f824222b81a3ba3b7d0b570a1645e5ef96582f39cdbb05c682fe036f091cb646eb23df5ac6b74510ae795008e8545cf63549359ad362004fc7fb94d4069b9355edc323e04057dcafb908cf0e3c43126c9007da455aaf6d0f0b90152d9ad6342441f64b426e696dabc005ac0037740027e68d262d421f43ac02c593d4f0d58c4a1b8b5e3c1dfcf2e62c6dde0322ecdd0d38d772a788a02a18cf1009461907433c5f361f458c9b5315c9a9de06070c6bec540d84962dc13df27eea47978811bc150a4e46f558d5d7aeac9f040855168140e6cd77d4dade1eca51755f2149c65484c3d9869fe473c39a0852b10da2ffea8e9658daba7dc88664c6d5d3bd6eb8e00da3fea9b8e948bbccfa54f308eea5e61f1bc69f28dec7526764619458f1dfb541f21c78e5a98d6452cd03b200098b5ff64c59649e284fd038e53617120842909dcbf9a417218e996d89f9cb071a918207ef9659340ff138f8e6bbbb2acd8cd6100862958e76d9c7dbec51fa008ae9bae1dd81f00af3ab826938ad4403dd8d043f82eaa080c88df5d9e7570006569e38585148a7700835f475e4ff4d6a40638481ea0d9a96173d10eb9b84f608b8e632b57f6e703d170371a0a1db26677d2a80e91218d1e006e502e14257d40e243c9c5cb42bdbd830ec3f117e738bcf7c238a10d6fdc8cc08ddcb0fadfffd09307ec299f1a85c292d6665796a1de75a41f2df9158fa236f256ca808210b3609d3e496742de1a58e6f1c8a20cec34ee763d918d27c0d7e9e1c144b07908e17025293c8d410880e8c45cf35efdfc7bd0962c0509102a054ff3eafec0ad7a73c06484a3517542ad9995e148defe591e020b3a4f2be88e201cdc74bc9b73fefc508e09c48e26a1469c9449759cc5d0723040669773477cdfe035a3a56ca7321730af9cf023255ebce1abd55996912c72138bf2e1ae5d88acf22e1caece71dd1290381aeb752cd6b5bf08368f876ce4acf75a09d295d9ec22e2c1b4a8d0f0b700b0965e91b749668431c6ada7c128ffc91f968d80c5ee615367bd3bd02127735cc0cb104ee6cacead03c296a790461761897619a62e8682eaafd33ef83b78392270f0f5631d692a382c69b1114c2b04add9b9141358cc548b9b7375254d241408d01482375c3438b6089f6f06b320b5b3f6fb5610640b40b10a1c88a53bab31844063876a9bf874930679244c6ed7d13c8e843eab6422cee56d3486aa6d4544d5903c170a26fe8b600d5d147467a71012c3d93c7c10b1368ecb62d14bc764e154801b67bffb7c412766481d61f9ded43f38f541614777e1c5cc92c80f6c97bc5900a5848cb533abea186aa64309cacf6d64b2eec8aad3ef5f5d81e01c989110f53063b30cb5916733b683e3035ec33ea152c9f55b3b50b8c3a761c6a2fa8d0cafe018efa6307d230c3885eb24b09f9db293a1a03f86df3653761e4628a8622bbdc03eb2bc1fbfb0d8b2f879587210d15d60a12f026aadb0fdb8f8b3d4b56045285011cbe6075f49868fe79867601c76e8f29770ca8ddab06fe8778c1402c92c7fe04f74fdeb5429aa03311ea084c94580ff9354952d5e4b4b7e634a36e7b663c820d3e5fc0b7eb31ec205576ea18d3581934cc0d7c924e06f1e91301f093657ff5006703477e4144874bb148583267b3daf440553e2febdcb18109900cf668103d0eedc1950b41a629c248882a578fab30465a77d5f45516216a5739cdfff83a71043d787138811d24bf5142e490dcc1c97d15bc84a7d3e5dcbe684aacce4d7cd90072eca9ec67bbb57d59dabd97aeaf7683458612fa237787742f5b190c9172c50448aae095d33e8197fc6d4014d2290757f7a31f7a26e8c04bafd82b51634c7709c15acb534925128d13798885aa72fa98c7808e2b18219b7d5de2563ff7da1305f979f840c0737cbad5f2dc4b66d63efa6d13c3eae4c9d8f9754275117645750d34b39b4ca21b0b7644b3000e2a62dd539e87b90d56df4aec3cb15553e70d67095a58d2a93d350ec6e8c6611ffff75d77a47469bd27bbc54e453be66a3c4eb40a54417ee030f0636f929d43cf4a01586698a903ddcdc7423fabb9016002045e0a0d84feae639a1693feb341ee7665c537b210c63a0c213a60faafc61b31fad40be20e1d24800591079038c2cc37cdbcd08d0419ee4c897cf32d44dfcc5e00ea093f1758c56f07cae2518d3da8caaef384d52891ee0806e9b99beeab7b08312404d484c5a7ea15f1a73a5d0b0fb9fab0c329b8f5ae594f40fb3b1b0e3e03dc4e0339700edfb5915373770a57adc0709efd6da93252f7078ee43113a6aff33fdd04210fcb42bcf14c815d156ca794b8274e9613e7c8e04a706e5cb63383c84612073b6b32b3ecd6a71266fdd0d5d3e6f9bd18421dca010b73b632b6b51d55a64306e6818060736c64ba7bfa8d3bb33358a06389d9c3029f43bf2cc622a219e915040e6e006109747826edf6bbd48ed2d15c641f4028cd8bce6f6a6b21a749f83f04095654a9b80194b529b25f8ebbd55226bc743313d9186d8de42b15986647da0962c099852ef4c96cbf5d0a6cf77023ea9c0377535b6b3dd4f0539696a7194a089b59a1feca125d5e8c3595c41af489980066592c6c23b5ba98b6cee64799210daa073c917c642b668a5bb10ff533cdd28c841f5dc52953333933b6f21b80cb0de074d5a5bebad980bc9e40d3ded73e839ff788ff0fb57141c62ea8bc7a1f0c056fcd0b57bde28dfcf51f21aff28ed6fa4a34a621c79fe356e83cc7b979dfcb07ab1ba5d45d2c182e7d6c14b1cd67fa6cc63539ca3edb19875d4c120a227ac30053aeb95fe82672bf7fa7b98d28ea1221132b8a6fcd11069cca04df1fb601e20df1fabe02adbf1ffe52d2558fbab02635ceac17600a0830707c14a2462e597f08373ca6eb5cfd4b115d565dd09cd97ce38798c0a102070aa1ffe07abc01b2ab0f15aa6fbf1679655dc3cd6615aa3efb1d480b45229cb6f23365ab1c94d152a700f3e6d3c64c8a32222ebb68ff400f2a4dc961d0c092d392a1e93572952c269a090f4396d33a567d146d79366a50ef5895697292644c1a35e8b6342b3971b5c60e314fd539fa7eb6b37852c7e7837d15ffa59282645f2119dddc16b47cad04b40ee2cce40cdc49c1110979f71ac6e5803080b27c1d526a7a13d543e9ff0810720b2d1ca47a73be0b744a282dbb01ec8eb0f09e636be37d4770bb16625e9b00400e3df2a138288cfa10de5631684c1108bc0389c5a25b97500342c036c9292fc408957adb0a05cf2661bac9c671bf09b16a36d41959df62058557d78a6e48cade0ef41be0c099fc70e4163c1ff1acc826ab1d845fbeba99c3f0467e4dabde0e5f023320f44c8874bd38a3ad44be659476d61d1e227c7f9cc5b4fbd41de0a59016060eadb1370c8f0a58d211eacc4d6c9ea4c0e0f6f437fc12bd52141ab8743d6506061507c570123f90f10f5623733a380b518b80a0b401d90723a1ad273741d2047e6a3f6c00e1b244d54fdf8561d2277ba8293e4d30911138ff3179ec0da272081d2977c372a11c633e69ecdfd5f5af92fb31ef76f834bcce1eb50bab6a01ee091021022b2844816670a90ccfbeb0026021cc4ade2fd3fc1886b694b70cb6f50d28359791f5e299c6bf7a7a191057829fb05d98f132a4ec148734be40a20754038f4c8e8dd35ff2c8fe767306a9abf03174389e5a5aac066d5583882a4add0a0267aeb5b0767813a590db3618e820120c0d37c5426851e64c6c2009f10fe04f09b1ad074f637bef6266dabe1d2ae81bd234a4f8e2a1c6241e555ffc621277de0581f270489e921a705f86b3969134ad2982e04ffae31636184101a0c61090c20c80a28975d6800473fa9855db0550bfad202b8b68ab6c42cb3ae0ffb7b617c0035fca25da0984768883653004c2ee34587e4ed3aee8f72ea651273bedc09bd00ab17d709fdd1fb7ece909324343ef2101dc087cbcafae45c65903e0d9bb16260888e01042cbe3c5939d3abd88ca9cf1dfd917ae520debbe9b29e115864ec7b00d2606e55f1d40e8bb9e30c3671dae7638c01d8f45ee245475c5e0bd68682c0c0c8e52633cb83000994f58059cae9dae8bfa8a3472c659a4aa53f68b6487e27f0670df6bc5dc1376e79dc09c1a53384041d8709b6285a422901325b22232d14303 true -check_ring_signature 7f16c58c82c1172f913bdc61e6fffd2e228fc874435ac8394e66425dcefa78cd 243db35d89a40fba0408712511d702a90f58036c0bbcd4167f32a2427da2214c 81 cb22e98ee43137f31fbb3fb66832bbee8691f965122c508b271683c95b90a3c0 fb8f2fba3fae75020683f36e8ee69c80e645f8f81c0e3c254dcb15a8b6cca78b 15cc3d2e83eb5c2f188cea4306b4fa4527d33a49e4ac2ecc564964977c47765b f489789cfcfd7afd51898223fee93bab5ea9ae7381ddbd55cdf7733625757192 77c5584bdcdaecc267828ccb45fdc618109fbea971b070537a29b7651d4b24df fbf4e43f5b0c366842c760d678a49dd04beef3860ce7f4ef4b8268d7140930be 305596408d312f044cddbc8e4fdf7f9759d0ca1f9de73e050a41f60dfa027e43 08b60bac329061040f84360462c296a862392d9d6953936959b9b28033ff3af9 fa9ebdf5e4bf1d5f06aab9ee66218b1aab33a5b8defe98104f00a2e51c44a18d ca665c3428f0d6e761c6cfd9686eb85e96b7e234f02b6abf20d452aefb25c059 63e7c98b378b11fb2d1f52b10a9044ca2aa1f971288a1b8ed5d2bbf5ca3ada33 644e9448b1a646f200672a296597a0e58b8232d6aa482a2fd1930cad74dfffe5 de41be75b46597d7286123b2050a8995e77fd73bf3d43af0a1b0388b0169dc3b 708f38e88765f3cf343752ea0d27c0bbfc738e12267e231d4a5f7cc12bd5baaa cf57667bccba351479597750a9cac9dd71709fcab30dd7e222fe65c162386569 64a8cc3f288e0985d396949f97bcbb205d57c030666d40b6d6654a2ed06a00b0 1c41f072ea27c0f57b4beae3bc1be132f0bc0606b4539a8e501581da0eb3306a a1bf01227754893d5bcb2f573549de34016454bca39df0c732c00173aad1e13a 0002d5247563c39d4dc1e2e3929061667b0c1b6a515ff008a2dd1adb36d51acc 6d919c31ababc034c319367d9f240695888e0c70bd4d3212aaeac851e39b1c36 c51eb4c4ca5145aef3bd789f4c126d7ace6753811100b1f11ad3e8a7f0e0e7c8 8dcd5ec88d717b574a2c3ec16933eb6069d58df7b7ffa5a32cb8fce1f9946670 138a789834438310947780803ff6fcf6dbb70a8c9d3fe03bb3e7a1b3a7f5cfb1 b0742bb3a6037a7c0bdc9f44ed76c418120800450ffd3b4a673fd52904579129 3c9781ba2d2fbeb43ccb5d13e8a768de98dead1cd07741e42f9fd47babd435f0 11b03834672abe1f61a1bafba0f8898841c701d7eaf37646a951a65a4ab34db9 a2396d3901c1dd5e63436832876d8d992fa088fdc6fa97f77dd909bd3f1d30e6 6a82c2cb4575c555a42fbdeed985c1038134ddab73e0264ca5662f7b09514a61 52773f9b0de01bc19fcb3c1141d041a4937089f039ec0889a4638d485d222870 dd312b43009cbf26d5c58b894ae01def85748b38c946613e9f2a46c06797a558 ba49f31bc295b2e4bc466b13701c9001f16189c03b0743655bba73aa30ceb0b0 636b8e1ad447e1d323fa211e73e20e6797d899c1d7a00291f9bfefa2f29fdcab 1b045f16113d2b9d6cd80eae611b042b9e864b5dc6d0baddcff2ad78c92dea63 e74902121539ee5138d79ae6aef0f93bd52f572a1c55fa6262b178fdbdf3e9ac 97a369da0d4c4d0ff4270c68c1798d9857d7eeca4eb5c6421192db92115b935d 1dd1eacf6c6fdc5bf01e7cfd865a5e30fb5202be3dd04ef3d1e6e03d0dcb75f3 41d409c2bb2150030548b02598bc894c8c58310175ff939d742f469de3579334 6d873e2a5b79bc0bd7c17016ff0342dc5eb03e000f0b3b5318cc396246cabdc5 dbf99603999c021889e95ec345ac8c32f61a48840933384327b6e70369d7c05e d3a9ef5dc95f926506a1793719e1ed8fc46774f7806537beaa52e08899d4c72b 83abb3856f242bfa74915a0b68b2d2c85dce6755f93da4967e8d42d76b942119 6e93699e370817329363a3260c221baeffcd8f1f38c49ee4e2b6b0e504db8976 b21ea0eac735c6fca96fa3cc6172271fb639a0c8b814a8a87319fe86bcde5634 9dc7eb28b495bd91f91fc45785b25e20077190a72234f5e66cc2b9fb09763599 4eed8f1efef303787f3a0a05457970efa10030f1d11aa05ff3f53709ad61bd80 7da7d0362cc2a82c2bb02572003431c65ec07904794de1045cebad3ef3b297d1 0f73dd7d58fd95a9d8e6544e4ed4e65d0fe09b1acf3267b111ef26ed125c9e3e 679f6cf72af9cc7159a3531f453f47836d20ed86d13be7156112306dbfe36c55 08776a04c34e984495507bfb255b89529f804302537688e6d6e7a8560dc6e211 95b28c5c544f380ee62ccce9a809680901515861c0d678d80f4bac516eb9586a 012f3e977a746f4b1d7ec351f3254d294edd011008ea977c8b3c4d581b065f0e 5dbccb85e84c86957070cb713a91c378899774ec432dfb9db01ce85fc9fcd486 8706ba1da0316eb41bf7ea0bb44a208b1137556b186a62e9d1c81e4f21016f15 d33c29fca539dc5c6f1f43b3b85a51144a784d1518291a2c2047fc4b55c0d214 a455041265e13313e690e713fac83f952b1baaa69b69f948b29597e4b7a098fb 8fe4c65b96de012768bb1dd99281de38739fb5b686c36c8db6a42dfd773d7d4a b2a0b90ce64a31ea0f95f49b4ee01f88a67828f546a81c26d48cc29541d5a7b3 b49f8ad7f14dc582c88da2944531e28487f7271d83b365c9514eaee8d9199f26 99b5c404dda7feba0b417c6f660127885c0af8019486fe25f33e440f9e42c221 81c3384da70f6d8e3a1f68ea14586c2d137d5f4a4e0a673ec47ff9ce39ef58cd c9a808729a7e8e4ea7d461baaf89a0768c566c6e3f8cc7cf4bbada70818f5e44 163580dca6fd1c7e6e476cbf4aff8a90a95bc41e8f1b16cc519cf128fc23ba26 5b3196e5009478addf6bbf6e8808ae3db997e499a8f26e87f0a97de7c91ada49 a8a3bae1c19fbddd6da2d4a5be0d31bda10e06606c8f763bdd5aa6e2d2018d4d 26688cecddea3228e0462e85362b6ca482cc0eb053ffe80f2fe2f97f199542f6 f3b2a7d5e61de61bfb905b99d2b23aeda0df59d5ccedc70dfeddb66c7b9c92db ca722f1511810cc64c3e36c6619054ae49c754035691149ba09e6e664317e983 77c2d2f39a117b7b06234c7c2e82db801ff26be2fc9deb441d7ffa87f7df491f bdbb8e1c3774cb5d3bbc648bdee6b277350db11e5d3cab4c198467d1c890d701 8f47f1663c348fadd98dbb7d1ec4a6c8dffa2aa0e5ae8cc7edf7ddfe2ae658a7 d7bdec61b1e60b77f7010e72be0869906975c6b2539e742ac9522ad311875015 6036daa8fdfe004f2c158eb8fdf617a9a3904fb5a9b32a37856e37479a89896f 85d4ebdde443b69267f2c95606c52469d2245fd17ce79c00b60b79f0eb6477fe 23898beb5aeaa1db72745f5d7413f64a3495cb859fc6d1221884d85830c53aac d2f1ebee534c570129148db308373497770b11aca8ddd9cc72ec4bd9d3b0129e 9cb3b3745d96d8f722fe64aad1a1f98a4b331a9babdbfbeae143019ad697fec4 a78175f24308c3b354e6dcae604b1808154e42b71414b1183fcc032ca2f8ada4 15b2660ad0f8a65e4b4f032735c75810f415030965f8b3289ba2d86a96229132 21588558bd72ddc8968607ebc0ad2bd37495a96fe072e9f7d570c0ea7134f6cd 2d6128398d8c3810d59c96461fefea5dde555d0eb8149a49c653abdd2fc2524b a6e7dabbf9b571d0d512e95c7c1dad11f2320223dc386c7aba30b4edbbcf8e1f 33686784dfbae5e69c7019602b06db87197429a94a34384afc4f54833623540bc559cc5aa7893e294ce6deb07170a8c6c310dddc537334feaef71f9fd1f4f80d7d00079ec4031ea089bc045e29e036317a0c220045f1964597995b0d20652a0af70d307863348d8ddfa0b90d6d4f4078878e9a550f0af464dbcc7c45b32e6400f55301b5e15616d9ccb6945309e92b3ecb869f960330bf007d2b1bea16748007f06e1d5c8ec8c4c4e9e792d17cf3930b944e5163658bc16c4110964bd3e0cc0df029a15a5eda91a16b00ff6c5e05c8946dbc13af3d3d7e6f58fb55210c06ce05d13778daf6404266af11ddcd2fbe3552604c8da5270a04253317ac79dd8b7a04c86f3e120c5cf7bed5749b7f622f7f60bd9044293536b2bfeb441b74768571045ab299892273dd5fc8259e2bb022dec2666e80376dc4b231ee974015c56ebb0e5159c3de30490080216a8db8ca3dbd24bb29b5f3636409bd92a8954aa4a8b6069d34981b0c48608c3865d8061c71d55e68592daee4c9b89279b4c8d63fe7790ad012e74b2c0244da000e7c3a1c8e542d2f413e00f888c0f67d434ca8867eab06cb45fa0d24760ea89359e43d44f2e6db1ce943bfb9b59d7e55e54d42da35b5099a6db5a6c726b3099d4b1f5abd0e764e4082aae2c50e40d7776d4cb70eb3c60459251338907ee25f040522d27874da7e8d696c9e9717f9200ab6630cee1b67086f235703f863f96b0bdf1dc08f987f36bccf7ce0e8069fd7e83caf11c8eb4c004a59786d2f1bbb67999b2e870018134ae5688e0019d63d8ee16d70a1e84cf30e3edae42c52e36bf0dd17a66e525c9095abcf40b8ca85c75b226ba12fa9cf960bcafdf37999bf985ef13b072e391cf38ebaade8f06abd13ddc1337976b2c6e809005bfc9774d92f12dcc59306c56be241b3847d836a6528877f3cd6c42a9d4009c835669e729a0c7538629d4acfa515ffb8e414acf8051c9d6793fe683853d9093e1e06ef05b75b2f71c64445ab1f4daf5bf4683f8a69b7ab838cbba44e196c0f6974f3f33114455fdedfd99c55dbc400ccc54711c1ed0a4fa565f077c2ec6a004b199f11d79c417879043e2d30a5a45e3c4fd23eb39909e8c870dd1ff29db10d12b151a91b31b576074ed40b0577a07f20927f90b45fe8929d48c8320681c0030dfb1c1e9dd90d64a2fbd38d406582f638e4fff84e62dbd9e446c71c96883808bd7e797418e4304ef95dd3942537d3d26400287dd3b6f768d87ef387eeea4d0e92c0e71943e1a77d7c1cf4ac6818ec23194b3ee85c8aef0cdf8e5517f44b4e079cc23fa768e459d8a17cf8e304574e41fe6ca0d52fe894c53ad1be646c7c290f7bddd4999b785d631095979b7afd126353608e5321298c804bb42ed9af56530c37a9b3911cafd58a53211834fd045cd5db11c3cdd010a931c13d4d535bd60b06c7283e1ab022b99e81a2b198f7e1590317ee46308326e3ce9516a866d506250b2f131a81c48399ac76ce569264d8edcf78ce7f2b9df981a4fee276cd2dd9fd0695266709eaa1e7c76ae7a59ed4849ea1e963126a36c447f4babf6d24986d8e0acd7d29b0866ee8e19dbe12a2be930552166df6cbd03ddd95c56ea27c19e8ee0e296cbd7065281f972aa64e66a06dd14035d4f717df1cbb720b786e27d0ba040a02cf4c77445382fd5451eb524d9a8712baac797f47b121d3a7bc47563c749306d86bbbe65767d602bcffd5e709b31103ef698ad76bc0930c76fde77e8255130e5f489db3b157817a44a6eb9f8941edf64b06cf65ff60a0d391f2f203c631c60509034a3183bdf3363d3b8491d431122017dd59e6ab101c4ac9a173c5d3d32303f8ef0cc8132352b2d767cda084f53331a14bc01cc9f44ebff461d13d82741d0ef1e70f4cd4fb6032536909cf5dae7872135a174cb551c5f423c8a2628017f102646c6a558088e072200d62da8ca28e13c6a3751a1826cb724b1190190d6d2b03021f33330324388c0cbd3d4057d700d20eb77d80592ff34ab350f39c11cd690118ae4ef033028b9ea1e005dc8c27557e3464a3a09e7744f5eb033f81131ebe04a80edf26c12cd890ad22c44ad095396211525e509d27f61d599cac0697156f07df532a65cba96fa5f0abf2ddf088f36c4922828be954f34419abed841e51460a2595ed6ef20fefe5f2e68c4150da1690c6ee550219c388d41122818770e29c03e68a8dfd09b85d33cce5855d9d9d30c206c2c4ca6042786eab19a8ebf353990f7b30f7ee45999dac35762344db61a33c38036d87d89e4c8aeb776f00fa91680fa804e479b3c0b119eaecb1152b582d05aacd47720ff2e727d49696d0a534490e69ce16d534def076734156d4136100e5909511ab002223814492bd71ebc435977ea9bef203c97bf0cf75197f19813496d94da1c6b60438410b3d8c40b9566704c6a6d178ae459ea475e4b0b896b0933c707e265b512aa8f23792f55818a80002dac24e4af5bdf37ca65f3f81f5e9ebedfd0b67906a6cfe73bf8912b098b7590bf68bb6922042e35da15ae14ddfb7218649e9787ed93ff212f56c17efeb1e080334f7a8c7c65758dba269c4b3a7aef77a62d5b9921339ce119d65b4a71ab0cd0e855d3aea9a2a51ecaae20fd181fcaef87537c9265f47e26d4e0e0d86b871180cdfa4361ea62cfe80f5a83194ab65738a93094ef3a9acc97fadb7249d113d3b0e51960ac31114d6e86bf58b7aa5bbcbb25dab0c6c19245956404aeb3cef51680fe66e4274a0ee768489bd4d9eba3944a342ffb50367b00f4b5aa2d825d9391e080c1c08db1ae1dd54c9bb1b2f7a9840b87119cbcaf04a8bcaf551253141f1e80c682229281bae2952e0948ac1ff032c21efda17ac56748c56a46f3ecd1178c603640af9a0dff0183291c2a3663ef7579922dfec86f67ea6fe7d29df4d0f3a30059b14fb32963a09030ec729d50690c9b0c58a119f1e9f1449159f50082129f402852e71285f6ea3ce0e4321058d236e42e3d6aa14225bb363006df970280fc201a18dd2f81c034a44c9522b0fdf90ea6ab5cb4275cde3e1fb7a54308a1eac370c5faf5e068c0dacf8715ca297ffe3b8806218d5c31cd561db16060977c78aa40d356ad982644792d2f04875eadf0c796eebc68d44861a9f2f4c736a42a9f9e50253a4513f2e98c72e708048e315f633bad3564fccc32e9824bf2f25c5e46b4305f72aa8540eb76ea0860b6cef53228066cf159ba9aa1a47aec610c79d3e43220d66b6706cf85d4ca84908fbd5d9fadad3a2948c5ff6db303d1dea6a0cace39300df4272400d58f2bd532bf58703ae1e0b28fe36ef3609c860a8f7476c39007508d75d9496c84a9abbb6034862f35e3cda6e6b3e1f043e9ec3fa49fa545ab7e50bb95dc5cefc3c1b320409178e43c596b649158458ca9784ff709b71fe69a99b0b9105968390f17c99949a42c78ac29d69eb731669e8264cb799cf199e6b95880d91a46d9df1cf40c9dd2e673ed1828e833229dd786abc00f169c3d4fe78284e0417f6b8d640463c7d4a3c26f492879df58621b26b779db2cea8819db03c2871028b6c90795fac6f20d4ab31eb4602acf512225191ab6ef963e579b8ffaabfa70da5549a34592d12e393e7b1cc8ae75d3f54937b8bda52fcf77c57562f4dafe10d1740bfc4759775bda947bf842ab789a8244938419b82d854ea36c6b8a6455b0a9b53c1f0ac93fbe3ed3d70ddcefdb78af59f9f8b57a27d366b33e09774351900cea21c2dfd159ab4e55bffcb53792c6d3326606bfac2e4f30fe8592342dff80ecf5b08ccb0251af8791dc788f0fa159ffd1e5a6b848289206c190ff9f275020f5aa44dc0c991a8e0e03dc1de8f9e69ef03302b1606a896015dbde94be6afd70f7a52e47969fc2c65667edcbb7917e4528284e357835e4508a40b590bb66ebf0312a59d1391b870ce4c26ee013e874aaaaa39dd7cc4626580c48d97b0a6acd406e9730fdfee17588776286f4b436641cec3264f32b362541974d7687ede700709e021e5f47dc27a2620dfb55ed70c2fe5ca27938590df0b3e73acac8eaaa91d03581cdf781f89236dd803eb034e3cdf0152fee6102f71d651b6e81a10d03f79033c52edb6111808853b3bbf3f9d4176d7c9ae7ef3525d1099311edf9ba0581c0e6773c750c0b4652d0e20850ed1082a6ff5eba60ac7db039fb237ad0a5ed7660aa9a4dff734137bfdfaf8ba5e257492111ea2ac732920dbb3ade476f8652af8066b983353b5ec498e2ca2d37b98cd3e02ad52f617df7261e4b7367ea65041c90da8b989183d14152b83ce755489d159d92f7c1bf3344e520d2a1ae52a2375e6055c5266e98afe5a59e80ee2bc9cff440a987ec87fbc3ed462a673eb754775940f7137b8c30e88f5a59507545721ef4aac45a7dd8cc4549e71ca986e17e5e88707005963ea40f353e5c897915f5ce036ece63239d052df68a5be5c0f7ce903830cb0ec7eb0802014daf1bc45570fb30c24d0b942c26014b9d6de440f2d1ac1380a9ef56e3e4dc29ce120833aa9ec0bf6b703019e0b2fc4802aaf935f936410b701e873d1b3285f109f679f226546c583d5de7f8bab73ba82f3f40f80f49f0a1c064c8c823a90f7e78eed26948b2401d6c633210d1321a75226d0ba0c12bcf449024415c38a7f12f9292f57004075d0ce4277ab9b62955f291c2d3a732eaa877108035799cc0eeb27a8c156c4506bd49e65ed3819b9d87005dce1641464ed09ca0eb28f2e17a6167750ef2c91a12d5b3a8c7b0e13660d644a2886cce4020367520c4494236b7c62d1c594018cf2289804a361b1d1174c780b4705b0c0a452dde30c00ae716ddff446b15b24c16767f9f1b25aaa5e0bfef4be2d9ed9554edea5d7014353a7a47e24cdf231d9b8be3655b031ac8b8b2cd83a190e68570510a9a1410d135fb660f232bd745be6b127d93eceab6a9abf2cb79e0101bfeeedc875293c0029104a944da3b5c057aed98cf61947b03021d715e03a2563edb6f7d59b324503735b2adce7987212dffaffd76bf127714cbcde8ff72b527bf7c8b8d009d8e801e34887eaea586cf6eed81d396929b1d523bb0819993b1f88dc031a34dc87bb0711e9ad0c6b2fa3c9eaa30755498b059e925256953aed3a55208cd0a034cef8095523b44848839454fa1ed2f133d9b87cb4029dd829933e02bfc624d3e9735100fe599e530f4f194095645504fb93cb098d650c6486ba4fcddb9a8b64bffda1058abea799917211108ef7f6acb07aff1be252e08f23a17a550b46a13020c0e40b5489118433b221b0c019498dd8a585e24b61362ee64e05f063c23296edf2a00d6965538bac7e6368514412c13e5b46d1f48a389435e37bf47f1c913a3117f904546a6b0fd86594657f8ac7db7ed834a619f77725e13fc63ae53bb17e0e52d4061b8ea4d6740afcd7a6505eca726b63fbda0a094294a24afd45d3b23fcc11080d933839dde657c8202a4e2b6c65157f954c60042899b9a015e74aaa68368b8c00180e124153566ca6d36e21f9a2f5c8388db197ce89e30aacc12adb5987b3c003a5546161da296184af1dbcbd4b6b868042f23bf15f2f15773a59d4343347c90e3cf14ffda031a9e7b391dcdda1f4f6f9bdf295bcb6f9eba62e9a4b48b0a7f1087811e2314829d7e1c2aba57a2e28f277f2f1b89922bb44a498a3785ceaea7d0b42efab5c003121a89de275ccd1b9e1b28c6a6d9ce8e557dab7ab382e8385880af1d2578f6942855496c771cd8594bc7d8ea1d8bbd4d177acbfc5313742072a0e438bd925188f85dd50716e998b7231916f4685405c6a761284f6f6a6652498024cb52126f7359327def6b9deae1203dd36a80013acc1aabf903729a13e2c4a0a630aeb32349a84a478e7e97b3b752aa024435252e63b0c15746987251d05860df27134cd59151b238249f4f411283ed410317ef75d427b6e3cde12d20c5af104d18c4ddaf2d3df128813a28918bad60908e5c638496e412e714c6bf3180a620527825a26bf28e3c7bed0ac08798a0b62fd577713a867a31b1ff3afece0fc980c08d2af280b569e136bbefcbc71df4428a974621581e26e35ddf48092f71c210892ccce1396df3b9d5782763c2198f1a7c24311f801013a1952edef2d7d0d89028e1223d7f2645d3a61dcd68b963d60e17a92e5ac12ddcc7e2cb0089abe2365042b5adc74560c6fa2a965d56ca12d3417d76ac302aa062f613a2a905cbcfb6b0456f6ad6a8f4c776e9e5a56938a797756c3b0252f03db96fec98a5a655d76d20701cd6a515e2eb1fe2483bad281dbed689e227f13cdee4266c4b8e093df206100a300cb4a765078127058930d9ef5bf07cb5e80d2ea2da1fa76d9e6d689940a0e8511264b8b983af2ab6b3051ea69b63a143418b77c0ed214dab407f335d60a0a2713ff595fba1358c888fe722eff1e8e4aa2f8dc3c5c3d466c24866b9fafa203a8157023df74eff90cf179276f32e6972f457b87c0f2df05ad2c0232e240ed06e0daf491983fe588a9ea588f7c734bd4f2aa19e10526cd5f3af063cca573200b9b9afe7c41529b4cde46effe62374af065e90e403125f195c0aaa97f58ac500fe83f672d5c10e5b63688604d3a6676e7782d22a59cc67eaf2bea365cea7fc20998de0b9459d082dc194dd333a6885a54c7aa313e3892f29c7ea47e5653b8c408e37e4a4f5eebb74a5477c09f5ca6c9ae35a5086edf18b76ec749324cdbe3360f8929d609ec643bc8379b45f377e081bffe3f25f6719698b040f85c52737c87023d8cb505571c09143b09cd61b553a7bb5e89ecdc89c8f0bfcf586ac76fa1910991a340248ceaa636757128deeca0a58162dedb90d97af81f75fa1aa7b3df8a05262216b3c9e1166e9ea06e4576804c65bd32c34a86fc2f480da689623c9d220640c2b00fda5fcac9dcaec0453f32ac4841501b1ff34d99a29249e9edd6d4820bbe3bea729f1abc96ad0a8d3f3449105d84810df26c18c6e508533ee9d2efce0ae8c398e2fd390470d008e0ed72983376f6beb6202bd1f0229d048c3e47147c06b9893fdb2777544a377c927bcd4a6ba66da69d66c5880824c2f7554dc4b4770ce055d437bcaf2000d6da6c9ecb837b7750498657730b5fbf9acb2de6639b1e0a567fa98673558b38f8829cac2cecbf4e1f384b9ba6134f2908bb1a4585749d0c3212e66054a30370d2bb7c0371586e49bcfd471fb037867919f6be38a4f7ca083a3b21a41e2d02134b1171248e022e2b8441f8d424d7debde75d01e04f932805cca39cbd52ae644c5f00ff42bcc3ac74cc5e7d2f667051a7d8349c6aada38400 false -check_ring_signature e43161617d7700802e02b35eb44db2eeccdaf8eca1ee4738eeede9b84935dfe2 c131e541d81f218b49e1fe74ba2d4c307dae41ef9583fee30412f58627b35247 64 da30bce8687094d53f5bfaaed2e5b64c18b2b822342034d36dea8e2136d729e5 6035ff4ec737789af54eccc83bebc8d1aefc643ac098de89255654abe06fc8bd b2dcd5382761844418448bf8e637eac4729393d3024b78aeaba087eb287c47ec 09ede82f371a042d888533a24a8bde510e45af926b7e34f32ccd13a36745a7ea 2b0402feab4d0c6c0ed10950f3ddbe2028e5a467463f802492d4a82caf61c80b 7d353de277b6ee3afb0e3819a0fb5c0b194500c5ae31d48b5018e75de0a6dc43 f8eada9217a6250255458e9525a3759f1abbbbe2abe3b91569646ea7d1004634 ff95be8b0387e781839fd2f0dd4c890e8a8939895e97088678e7123b52a58a47 d0741ccb17fca89294ba42e200f2fef07a0de46493e95eda0591656f237d00f0 8f711aeedac6f36741a86f1093f4b00ac4cab6c767ae1166c6dc7f99b1f8f062 508bc523019c1bd91052da2bb2d2daf28dbafa2151520280cd71b2ff0d8efb2f e63dec4deff1c88251f78ede6fb36b83774ce454408c66c599797c4f03148dcb aa321f3466757888c85f87304f9b129a589f9f0290ff7b53431bbfdf6ed684eb 035fbbae7ea650a11a386fa46f83b11ecb821b8eddf812be48c3adf00cf63d34 685d737c6d6b9be4d03117cd7125f2bde475efeee85eb0afe757b17f24308747 39dae9ade2a915f0cadb6570b89ae5cc0d92697de1df277f5ad4a1289f9abbaf 54a9af9490ec2a39ba862f671b3438fe67a913f08e5943523694c6878f79a29e 7ed746a8adbf685da09e7fe63922153160292b072726a4451413b391096a95de b320de473e99b24d987512eccd98366a94023cdcb1fcf11fa8013657cc4f8962 45d6171cc37629958bad5e4bd3e6179c4262ad0172877a451614e175cffcdff5 6495a84c8b2338b2f9cf8ef3f591744abf6f8f61ca583a91fe89240d13d8f3ff 66e61e9038eab3a62f2ec31a8534a9fbcf15c97eaed8d643b26d0965b2ced8f7 86677042ed80f3101230508aa5c00e31f4af4c7db166b89aee9cc39a56acc164 09197165bb14d3fff6d483d4e8139910f5645182f9d9383d1c2c54a12f27a994 d01f1793926597cf2a11c4274467907b52a3224c5d963fa0ac812aee10ca74c7 9234b26c13d4d977e9381615539a2c0004fb9b8fedadd0950b30ac5c6c14786b 291f698f76d754b4e03b5b4283e2ed00ae6272a7922b714538008f6ab53e9c40 1bedbf6037d7798f877dbe78c2fbc7df8b0587b8b045b8e5ba4c99dbd68da93e aef32bcfc8b85d98d1e047328e427008f8f40bc30d7f43128af8e0126322d582 7007bd0212f5a3c5b5596a55f1aa1761e38cf6c02d594bfd9dc7be19a3ecf9bf 412fdcefadd3e9c8c36c344e0ef341f314dfc34e637622c6ddb1b1f07f2d2740 3d6ec45909bfdf5547a4746bc2ed9c9a10f1ebeb4dc818e3572e4973d4969a93 3a56c75e872c6ab84c9564c2924958c9221437a2cf160a604e6ef06c63e24824 0fa9c7575ef79d3b18522e0380536f5a9ec8fa13116b6d4b8c2ced8d2189155c c9bae09b735fad33f31e22525f4de9ed962ce18dab5f6a1bb1b8574b14635755 255f718654075446063062294c08417338fb6c68ecf96f1df8ac28d8577239aa 1f943520d31c1b349f6997d6a2a502215b2431602fe3fbb792873eb294d161a9 6ba0129291eb534855635eff20e12958fd1a4bb424222cba24fd13f76e7d10cd a3b94e736e6f646865c702228e9116b0fbe2c3f1c5dd45e6cd1653ecd7516e86 8aacdb0865134a812fa02baf0b067d19d95ebbe14e3cf0ea8d44e950ad08caa9 1b38e7d9d4b14e77cdc741fdb8e647ef7c741ebce1ba7ffeacc8778e16803304 d9401b09db8c07a30f9a1416bbc205b431f35cac5cbad11f442d7a43461d6574 fd78b37f9a92de10508a7fda368013785e3da8a61c3df9cfd7dea250a840c146 bf02e2a05229d9c3804993d001b058848be5ba09a73e2531320aa73acce0031d 9cf82d81d39aa3f315895bda8c55523aac0d7f3b35fab79b59a244a0ee53e058 1321f2e3fa8a2d28a7361c276ee71db16dbfbfdf35dbb4bc37a6a9aaa14e75c9 53d98c885459ddd06823e10f0dbaa7bfa4ebc922c52aab727894f22eced4f7ef f77a79ce93bf9000035bd42495441cb41ce542da39b769e3bec662143398d536 14b585d9f272e84106cec6a047863e1fa40e09990729a3144f57d93993c02523 fa0e557691c65475dd91cce1a34f0bff451e2fe7e20a4a8af7e19892731d41e0 9c1c3c8184986321cd4ab03b6bcef5b8c9dc4e014445cbbc15a04e9e9ec7a2f4 d6d161a4b42b299f1de974db4ea38bc4d7aba9b448e5d30d7d18b8b256ca8ec2 6dd7508592dbf116285d167dc19c503a9eeafa101743c1939c5e5a1f3c8ac9bd 796d5d354fa72404a9be7e82a20bf89e1ead804f991bc28fb6c9ee48783b8439 6d220ec372dc8f91644483d008244c55402fa1c12048cb5b326ab18acf27880c 08f24e8f84f993ba412f6f4fe993f4ee0bd244788a767ba41d2581ec1b5d5780 e1ca06eed4c256735b8fe5fc13fc79f4ef9c33ec2510534fd17c62f5626cfca1 b93b0b848cd272c7ed37d35f60002e3744081bebd8318c281a8dcaecf32851ac 5b3c3b4194975f5bfb6a7495ffc39d0df63af00072893abfa2616f70d86497ec 30c7b728840724d1b1ab7fb02dd961a6a9224d358d1483a709ca693ed0371d61 51acc725d4652c793aced466d3958f1f6552d3baa2494cf3a4a8efd47c8de90b 9d127f7c859580f547891020efa05b29dc46260d3b0772921431b51923debf80 449403949e054f99387fe0c9805600ec9356b7dc9041b0830f60cfecdd4b858e 74eb94fabf235b37627e67b2e8a7798b64258aaf0133f955d7819ba14ce6ee02 ea8a5f03abd98edf3ad57c5044b5eeec723d10ef127723dd5c85c3f656aa740ae71aaa48ab511a200186dd6f3e0cc44bf03b02c96bed890592c52f274d3919013059b1f0e133dd7ee190b99412632aa170b93d4ff12db0a0bfd91c3e4cedb4099e1c1d55092c80766c85867d0aeea4abf985254a6c37597a3ebf8e9e35e88003549bcf07e37488573bbd90eadd24587d5e942f65b8704d3e830885279d01e804b9748e3d22d5ace9de504f5aa31f06748fe10e5fcf1ae67287c19f7c3c560a0383a9a9c52e413d34430e284f17f818a69dfd2216c6fa0f1070471f8f468cfe0a3dee87e22b7a8bf20644cbf6d3cfb9ed890bf47e9f92dc0122cb903ea9e6930f5488d347dc983fc92d62ebbad5de1b4fc8b47f4e59ed2b5a428237242c9d040ab2c1389ccdfd4b564e063bf1847399029b3505102b610a3a6bc555265cf85506b418e5334e95e5485a40a8f87e74632a1cdc7f57d08ced6f8bf15e739ce65a0e26680fa6b41f90164a1dbad64df4b1e46d60e77cfd4f12c63d72d4db4a0d5b0616d2ea0fe34054839546862f9fd804a9470f33f7168374efe69f0c9a49519b022d0d851cc80ce3623eef1ff9baff7a8909e3980cea5b3508fd1d7d478510a2091b3a8fe2a4fb7986417260dbc8e3cab2ecd0f25367d8df9ad2460b02b2fc010985d3ff9bcbec5e0d8d37950d83e667eb2d0025c5c51bb6271585513f39e19109893c32bca1760b27b267c72e28984649803d29c5182516734e85dc404bd35a01b24f15e94ffe18320d78c0dc509b23f15b01f784d9b1c404c9c99b8faf4d340fac87c1aafd5eb2298af06f86d42763903553ff49baa36799011c62f76b43780ea8d4837c491509d13dc9f559a994e137389da47260e57e4cfda287b079ebe7083e129dcc1a9678f455f83cb293653027880bf8f0cad6d3be4b8bbb74658488053175b605409f4ec22402e2ad88eef8a26a5d77693fd101a926616465d4240004fded25b84070053e1726d19318c03ced3a8bdc3b27c8bbc90830a4fa98db24002c100bd4dff96f5410d1d419d7be0951f904f07ff22983d9483e4f4391c819036e98ca017b505774e473711c407f030b7328f5c8bb698e6ae3ec87b6beee160c3e221fe8402c5bf63312b9e4537435846a49018087e8f8c5e0b214f2a3e1370b5c32902e901028d8b180d929f221aa55b60348f9f626b8b1e28eaac0295fef04748bc3517e183001d302701e64973d420ac17c73234584cd7aca57b566d9ad0f672b2207536132a1abc26d4ade7d5b364f2f8f9eeccfbc8797ff5fbe08f1d109a538346ad91628c1ff5d71aaff475fe6cf969b1a93fd9cd2b4b9286795f67d0b68e020e15f3134a4fafc0ed37c36f51403350c390eeff586262434571ec7830960d72ed29887450c7e0b3fba6fb7334ab1e0ef1cc5dea92bfd5b64d16003aa02916a357832d6a14783c7b6e1c1e9318f7357a674cdb59d94661da7525880ee0ce67d7e00df38a5eac004fd5712bb3eb5c73ad38cb59733bf4055475efc5de30d1f81f4006c4c57dd981a61c456e975debe7dff3e98d174559b2d6d09267f5d0e56df782cedb929986817a66be3318fccff19e3db2f28d6de8f937f2e8d24ab0505cbd5ad36f4c13d3be126bf52b5f2ace52ba39ab819164f8e87aaf14fb7870d2e005e4c0a5a0a465fc68138bb7a097a8b3d95667f181d521e20cc6f5d383c0b4951222abc3998230ce91504a67caa731db4cc89fba24c96d06ad0939519820333c276f97675d68e03f7fcd8267f349715ff1528425eff09f5c6c26bc7f5f4040f5b1fa46d7d88f05f7d95951acd211974480b634574b121bfeb6aa68f26ba0a277cfa178eb230f2eef48d961b5842ce4e4d7edaee4ca5f90236d075e14cfb0897013273126e000d10cff7e619f27ec80de8d1a03652296fffeec316cef72c0753f2bfb02ae66b90d29e9ff3f38b3f728124c26fbfecef4b55131915ee05c50582b3d90d223f9536b0813a314b451065c208ae61d2b8f44864e2293001b7090a9341e9dbb792f649d4e7e303e72c118e3b09108d28437d226edf24372106c20b783fcc958e1c34d6091f3ffe38e1f71ed657d11c8a94fc3c7db09bd25be0f60f464ee5d3e77af81c4e4ce51fe0d43f26395d21dda1f742dd601c20f388629c0eb84d9c0be0fd9a96b9cbd598a4e3fd4618d7578acdbb4857ce1c25dcfaac2b09e0992058c95791ac01d5f2657bddde6741c7d5843abda69d36b9cf351ebadb042ba304739bef0e824a7f50628d59cb1af13e630c0efdb858dd40b4329e76810825b76f99374d2e7169aeeff191320b85ef320f0522e7a25f177fe39dc44e59067b78a3cadc92b6187471c508c74cef58e0bd1566ec71a51438516761b2f3fb0f549ac6699ffa2ab1e84d7688c9bc8a88628848992fdbe4963818dc98b536f40ec34043af8a4deaf14353d06a90b0f9e91af1bdcfa08e69fc8536ad66864cbc059dfacbd07809b9a0508ac64958eef6de7bc3e0540db1428e88396ff86da7d805558f0ceca86ae61be858c2020f71f5866bdb3a3a415eade5017574aab152220b693474d181a5ccf8e375f85c7c2539d87302e34d3c5818534b4ccfdf0ae02408ad419c62921115c83f6063cf4c328e5dbde1f5f4299b94c45e7243bf808d0601012ec3810b118e5ff8354dd62a041b7fdb77964be2277c042c98124c07ab76006cf55134e3afb071c73ec8ad8d862f1ef0dfca01cc128b74408bfa4dc785e00fae1eebcb489a9a6a900f2288e9cd9705d50b594ac7103cff861de9f547e8e70c27262cb47fe91886022b2c3c3580cf9b61ded07d88628ee521812d0c5f5f9406c1f2e974a9b91363e2d578779e5ed34efd557b384ed4099e4a94206d6669490c96c4a12e5646ddba1d139ac9dfc6643b322bd65b7eb007934d741992b3eb1603b87cc012f70781e94b84bb88a7dc6e97a64225c0503568c173ce98b45c1bf60bb2c38c7be96cfed0788efe2ef50c01be633b431413348998f6e945981ffcfe0c36e197c06c7edbe19f935317c39e146c02725ff2c59809140a2ed75faaa5f60d7bc167c930adbfe56ffc85bf51b579af1b9876f2662abd94300470693851550f1e30794f1282025c07810e1f099246b0055d0390d9e343113747e00754149e0c4bd8ad898eb4f89d1a83bced91a330d5cc035ef2744ba127147bfa63eca23606e6ab117da20ad115034aecb8795697bb34cf9b021f1bf07d898b673b5dd0b40f3a9ef77453b7f5e48b719b7f3d598e171f82062835ca826c446db8fd4f8d830f5e7a866c0acb4ffa63e4b6f9b697ef779bac5f60ed62928700bb1f908338e20e3ee0a66fa4e3dec429ea63d1e4c4f85bbca3a83116f47d843955c4f9aff8090be10052af94bca4b003dba9eeacf1df8d14ae1dab896d0c132a35eb1d8223fb08c1970d321ffff80106b93695d4a71a019958ebf70addde7b84352f27d673530e5060c31906671256bdc33bb717fae2c617671a92f15c650ee2fd4c6342d62e08c2636c4892081015ad7a31b52089bf3e6058c29f94df4a27557e0857e591c004255fbd2315c37fb48de00fb475bedf5e4113bfbf52db5327d48d331e29599c05d657bab669df3e15a5a7d96ea5dcb9f21188f5af62f8063cf127d527d93e6703b7eb0f70f527d1e496eb07f085b2fc253275c2060bdfbc3c62d733ae68719c0d8743de89d4e3ee7aa16d06489c0c84883dc54a45f5bad14ad0f1bed97cbb140d39ff5658e86215d90681b5a9970de6159af2076c651e644315e00d0f71eb9d047fcd6ceee61ce9127eccfdffa5824508d2420ec25ccbb05fb513e57a2df04c05f4208948fad7c0e3956d1b254228c3db1f83de662e348ddb6c6cd6aae55a0a0c994cdd1eb8ed4d04a89f8218afdc24731e4dd2c13c6b011701c85fa3d111810992cfb4c97a5e05acbec586710c266785eabbc09d1762d06e961901e0fff2b700a3324bf084409ad6afbd781e4936e4e0a75cef0399f19fd7fc3d28c873eb46035abe4dc922a962fe9c1f460cb3a6adb87554dd90f0b7820f47d641c48b50c406035b8b95d179cf0158640b3ceec418b2dd93f587b35951c9799c39274613d40e0d3d2a4d073627575dbfe116493789eb04bbcf877d56ac96a61c9efbd6edaa0b541861be40bcb8602947806ad46c8aefdd5ae5fd5108e52acd26d7e85d26b00f111b1139b1253d326deae9b0fbaba93b1d5b07784687e7cb2b18ad667ecf220503f00fb993ddb7271d84fca7910b9bb1514df1f0a3088a50c290bc1b22e4ed05560dccd57448eba2a4c3bda34f1592023eb1d0f9c11e16a2a88ce8620019740dba1f765cd9046a85b5e6ed42a96e978df21d0fb3c73627438f4921fc94824e0bd2c81ab29594e86e4209a7eb714b55204a757cbce8e4a7b8a1ad4222820c1c036f2530f769edf122dd9c35265e7464ef592fd7f4b2b03ded1732ad3439507b0d5c0a3bbd59194440f1145abfba31f9837c9808e6e09800650f70fed395151d080a38e523100dc7f70d2d0c6d91302d943702fd25b44c002f1bbe5ad42bf11b0792cef6c84871aa6ef636e5719b0a06540d06d774e67678b126d17b13960e920f730a5688e1d792b76ae979c88f53982a9e0d8e62ed4b0f3818b9f1d0407af7015dccc0ee6691a77e514a00f2e7998ccf30a7525473cd8d2630d4e8576a455f0933ae9e0fdab0df20a78b8f3ede87d33e39383410a17f728e6ae2003c196efa05b420ccb8b71f9b8120bab993e34e6274eace8167dccee49f07d34199e2bb88006a1008251b6ab489526b3a3b8dadab9abf195fc24b413482eb360cb980660808b2479d51d29987238c1ff6739831376bad3bbe7d15c20926e3428984e58cbc0394eae3ee5f011c225fbe3367693de469abf07c1cdc09fd19e7c42e5992c52207323f0d79c2062601c2fc8312fefd5d9994557ca520abb108c2f7c03f3bfc980176bae94361a095faaf7c01d12def4ca7dfff60893bd8968dc418e6203e24c708832cccae73d71bfef7db597d4f19e3ca47900387b2d0151ff8d4c257d038f0031a26ee635ed95cc4f45bff9beb9e31e1e9794a1327657ba58804c80643745309e1a4f5ae606fd8c39cf3bdc60e5d9e796be2b6a7270489c3a177f36b1a1e0f0ce47e07f9ddbc41ddc0d85337c801dee6b588f798e65a92e069827c5ca19d220e9b3f8d7aa63ba135f70cfd0ea4ddf2d788cbe10faad3df8a8398dafd7d18930dbccc19a19c3dccb790e680fb27c3f9a51ee8296ff79a997824259e7ba0b5b202af6f3dcd01c0d8e3a8d2c017282340d164211c8bc8f5d8f412839fd63171d405bf6bf3bbbcbb75437a66310699cddf17ca256f2319f51ecbaf0077e3a613060b950d40746bb931b3edfa7e7eefb7b987961d0725279b26fed6b361572ade090b2482080a52802555b8e73d7ed4cbb25b33e9b522ead1915c3b0d61d18997c4019d564e156c3ebf6202fa10b52f7ca786d4636cfab6faebbeffaa6fafd4d93a09b5d054b0b914f5f09e219ff25c589e35aef095c8c0201580c83d03592744b301533bc34906a8c187b10252c463f331f543459e6431d9981859562f1f5165680ef08fe82c93784e54eb0bb668ea6dcf21387c44d069225978bae8ecf7aec85b0ddd3189f0eefff47aa3f14c1b76cf393553415947631dffb1e9353a3f04802300db2eff1d5959aaec8a77beb000f717150cfcc48275c829de315b4070552e520343d767de0b128f724c9b7459cbb242b18c96b5b74a17ddec76a5dd024093df0a false -check_ring_signature bffd7a8721b2bd567df737466528863af8e20c861e3b69540d36feb86fec4032 13bcf237a83cedb0102a25dbc3368eb01bf5ce1b4a26c93e26efb007021e1b96 217 e4f871039e8fe93bd4eeb24e7cd814b6199cad0e5c6d16786dd072dbdd1d2ae7 cde0686c0237cbe6b811848ed250ebfd4fc8885530ef60ef52c21af4aa184cbf e651fbf27eb8c4f20abda0b22e6e80868cc097611b99f56abe2aafcbac8fab3a 74f66c7de3498a8e85827a4828ca7903f750d995fc138980b9645865d8dcac68 0ef77a84367b57656b1ccec3998bb38e973fe4490a89bc3dffd65f0c9e882c33 290d6e869f449b9cdb5e674d33a9720c3c67246906a98b87f04bb027513cbfda bebe136c702f8a083ac130ae89a158a75257b5b9b5dc3a020d94a1780e8ba863 a5df3de315d91bf27c9ecb057ca14bc60e0af9b1e14b1c548481b6dede1c2aac 2d751de10ba9228282c17a60688aee52b97e10f0fc89849242ff216325088cbc 48b569da16c8a71ba8ea1003b797f1dc77f16a05d086527520e8af441c781885 8b5c0ac4725d94e4fecf9eaafab8ef0b7dcc1bafce47beae96bbcc3beef98b83 894526b723a03608b95d54890967a4ee9384d22377aba09df6a731e02b6d51ed 6a90187ad209ac35433e1e58d4f1c92524cd2c74491ef03fd7e6d0a6697275ff 679411dbc6d7e535cea6739c5a5cba0e94ccbd87e91e9632a157bfff4ce075c8 09f459750540e2f7935807c0d03e78b8dac0c9dd654f6f103a327568c3956f3f 7728d12a7ccb8cef7917b185cd057d365f496ac326cd4524df2da76051d11ff7 9a04c200760ad8b8b5b8a958d7871de21b08b277139ee4cbda9fcf1881efc894 e8184dd8483545841faa4042457d98b6f88b861d82693f21b756fa2c7474418a 7352a59746075cfc53eba8da644559b231cb522a59b904315130053c3084c766 b7f348b905ba047f5efac2683ad97e289862d22e01c24ace85ac0f699b5e09e8 ac51efb9461adc860aab7318cdaec2fc6a50b28a85614d20b6dcc34002278062 6a36b30f2f8ed1d0e72f4701a92a62179cb25ae03aebab716d768704dac4e9be 74be1f7b6f356a061b3cd42b4a0f743e959e63ae544c33da2f3ecb04df4a6a90 0d1bea6caa757e5a77aa4069c383eeb09003f80bd9c948af0e499bfba9cfe35c e2c19539eda333b2d2f4bf231122ba4654a691a2826df6b390dd5837e9e6e286 94b115cba9f1fbac0dce62d2ed827e4b222062cc7d157c80f0c9f4eb4946f350 3c1b4c992699d46be28e286b00640017334d6d19da7e3548166b76370b4747b9 3f14ba403ed3b1c72aff8cdb6f0df1f7a20e319a1b66aea81dcfdb88de3e8bdd 9f3aa78834e4a14d92c560526c48f2e3b2dddc24aee27c968db21d20ba1ac3b5 9fede8de4469cddf0c97b95aeda66750c8d6726119c9efd24778d01284ce1587 224cae06205702e122249aadcdc38ccad1301547aba42c96b5ce791dd3050e55 017523ab11a3b38cbd827e0914fbfb21aeeb2ff6b76f6419b66af9e01068b5f1 dc7c8adcbf6801bb1e52f82929a92431c017109c3b89e05235e3dfae6e98ba3f 37b15fbe15237001e6767e6ec6e22ad092d267bef8a2564b3cbf88b3b2c67931 f8281db261cce40a5c3cba2fd339ec1d6e196b516d20787ff5c7c1361c4e5b3c 1e136db1db94331a0d8cad2cf4f0573172fa00d1a233c92066ad9f6213fee3fc b7f10b670972ab1bb24489c32903bf962ebc430c9598cea3cb8e42f589179636 897eac6bd3d3ac271b3be5edaeabdfbaf1b8c715a02b17fbec5a62525317b6ee a2fc476612692c5de7cd5dff17a5f143a0ff025cfa0945d01130c95e317b0a62 c2d2aceeb7da1dde9d59015441d20af3012948f43d6cefaa1581cfb3449f609a 14b5c1b80270ba76644f90f9fc7605fa1d1ea63eb1676d37f29dd2994af927c1 5da46eb5d2475e1bf03bb9d43d42ced01c11938ed6f25181de4b91744ab95f7c 25bd9acb186e9187ce954fcc127fcb13a725ec16dff4cb8b5543728269fef995 5085d386f2132ec50bad30b0eeed3ec782f7fec4fd84d2cc703793738c061739 42b1ab8ba3c76c2bb7b746e01db108bd9f455edc180254555204397a8989e8a2 94ac54e669262faa4321b1003c7aa4171bac88d2f35446ef62a14a9d381e24af a7c6078c9818938d5e1568ecce63205121be096c60c34cc7f4ac9e9a3ca9fdbd a3b59bf5d5c90885ea4224f34a3cd62db3429200ca23f79c18862af136d8ac63 90317f341a3b6e1b3de390074fc4217dd78240cb67cbd71d8744e0f25f70495d ff8eddffad079903c909d7b6bd27c5d30968713882d450a190bd9eda208ba8ff 4d07dacd4ac9abcffca4c52fda88049fcf316e97fdbf74d8a8a2db2f12283833 8d03de8375ea6dde79747adef5197c46a997e641c2b9124cda67ec7d1fd169b6 17bf80a74f3a1ec338a5809fd05cb159c40efaf261657b30437038845859d269 de64e880401aa8eef7d6686d6d99e9a7c5378bbfe883dfd0c7ec85a1387017f4 be5a81bda03ef69355b05055c9a262f095d0d4b0e3761361dff802d8c911da94 b33a1b2912d1be98a09d084b0278bc406f97ea2a38bfedc40ad929f7769f0363 8c164fe6f7c3896c4b8ebbfb55c4385a83bd2d65af32e8071bc2f9966d4bc0bb e7126e70d27769fd5299985cc6329df599504aa8fcfeae27b5e6d48d9a927b66 7284accd3cfa2dc8cd164e69730fbf61fd0b9e57c5b7758810c7f0470b8b17db 7245900fe1081d5ed57bd7c89d7b0ecb47cb6dc4859a4e6af19036c142b1060c a4248fcb27e291c6423e59f2e84c7fa187e1b785569c8f433d4783962ed0c457 1c372eb030d0eb273efe7fd3a641b37186cd52d20816e6d95f44d604a84d3b8d 38fb69aa0705687f2ddbf20ae01761939ff4621a7f9c4d94ca800277e84a807a 39fa6999db9636a5f4468f1602a42028111e4f61e4914ca36ad2b3993f17c271 12efd3e6de70f74db58194c38bac16daf00309047fb4a21d570d0070fee3a180 be46d2487e2eaa99e900dc7c3e75326b17e8c41832b16f4439e20e3e34de8954 e3079aa5e2c0fd3bfb036613215b2808ff48f05a77241543dfec00dac13e3833 15ae98700c55db4bbc4d1f702ba27c7833c94138cfa874a27777e01c4b1c1842 cf421c08ac3e08e5176f26bde0268635f5c5335918ca0978d3ff05921fbe7fca d2bc6665904d9c6fc03703039e47ad2820c1e66eaf0026d813b6716f14e39a7a 43f564d71a1da94b2a02241702ee6cb64ce4c567fa8f9b30dd50998a8fca60b0 d206166b7b2aee93cecdeb9368c38f8567ff27842b006956f0e0a3b946137bfb 8f6ec5b9d58542494c84dd60983837c9166c2bd4fc1a4da3dfe1313709537b96 0ac9b3beae64fc586c8fdb4f0fc1b5d313662dab7ed300ba9488362b1393e525 31cb1fd1a0df97042f5663d7ad2bef147fd071ccfe2d78eea58b453b955f4f32 9628bdff78b114cdd1fe81737c70f75c2a7edec0848a79bf0d86a0df046d0a8e e815a1e660834f38c05c4edf21ffe962855dd42268b7eb194402193e3065083a 51b641b6423110c09de470cb91fb41a57b5188881cfe6d46a29b8f929f9d7353 7cdb91d00f311661a01412a406bfc3974bd1d9f420b50bcc28d7561dfaffcea4 c178e1b28d234b4f194e40ed47b70143ff57e0180da3a339ff33914c77bf8ab6 3435742a0e4025355bdadf97d7b7c94a59655c8aabd923048e41fba63efd68fa 033725eba529009a260fdea63994c07905e66a172c48f1342e42951a8952c69a 7471b33df8f7ebd3bdf874a8b0bb4d11a9762f25d2fe38236be344dfd11879fd 4657e17f195754481080e4fcb2a94f8ce1ad19e456d101311ca3c4019948968b a601590fdf1da0ff66898d223cc48519a5ba21732e9adcd46168a1b73bc82580 26d84eb6d89d89ed83118802e3ee6d389083316e6213d84c320941b20a35394a 781497d94d44b3a1c2354e89e01e83c2141c01be80c2800d8d022cfed582cee9 96bc56209417d6e591f45f9258567b5d688432289bec280cbd4164a1eaf33b86 695ba82e9b615e9b22cc244bd71c56639d772cd30530e93827e76a678ec0e3b9 07c6d7cdcb5caf6113ac308b1a206ef5e6b76e350940166e11dcb6831ca5b4b0 9f07c2d616e028ed1a55ba2759852a71046c42bea1e3c68ea0e92ff79303f83b fefb63966da84a9b489a23fefc096c5c88e1c5fe3644490e8a5a2445ec6808a1 a4ef9098add84f4c4f5136ab34a1436b7ca9cfdc8e0d31fae0973abfdad2d226 92656753c2e80f63af05cc5d36f52acd241fda134ddc1517dab786216744343d 0176b844a43faa4a207151a1aa1ace69e2379b1ca1059c1680f390ad4c323d07 a7d4bb87f2b3c868f0518662ed78c8fde2fedee76bbfc3bef3dca237a415816d 698d0d608996f33f4081c0406c8020a648cf346bea405d0780f50afbeff6a724 74fcc3adc89def77d59ab30f050414e210bef694d6495e5cbfccb87bc45d7ad7 b91644f2dabbf177c62b831deaefae431d308c7c44426a4defdc4052cd8790f0 0d805c4d6df6a9048b8833c149a8dfea58a1ff64c5ba60d8794fe4b807037c70 13609f0d42387224a5250a5feaee8de5dec0a3bbab5dbe2f1f310666112e7325 cba1e0f41af7e347bf9fae20d412c196f815683ead8123c123144abeb270b7da 30bee60d2e909cea388dbf513b083daac4e4f7e6f39b685dabe4271b0671708f 4bb8b2b2b5ccc04ae2561e6ede503e216c231d0d053b79f3b93bf2fc08c9dfc1 1848a398bc5eeef98b0e619ceac148a672f11910e619ce24bde4c12ee199a8af 58f4bd16b4ac13f27b55b8f28b3172bb1fcfe9ce6604e8c34a16c2b243223877 a2e4e7fee634b9ae6b86eac5e2f1344826df7f31869762797b1044dca8bbd50b a22e5039520c1fffbd86ba3ff5894fc8734f50ddc43bd227ded949fb0b8bf56b 2a712b9afc4f957c7d3eeebd8a2e069f2c845c82cbe8b6128b70c6833a0e5606 4f50de3ad5eb1c115eb567bea7b2b649f60d2146431c51943e07531f279af2f1 0a9f7c13600ec4bf27d7599fe3682e311840d2bbdede043fd36e76ceac06fd55 26539935e5e7896e3d0a3451b92d86b618c97a14f00a08eb77a05e5f180f63f7 dbd26aae8fbd943d6676ae564ad867b20c2304da2197da3ffc247999387ac34b 9c290750d86d64a867500f2783e78c322032be457c50d2e8a8b222a9271f935b c09aa9b8a17b4ad7052ce02871e549ab4b451c9098981609c9878200c2ff5921 461253c31a8d9767a115846078014482f7f5e77590e835935779efa6ff40dc9d 96410bd6b243a6f25f690d40543714295fa23d33ba9c87a04000617d17ff309f ada0837bb14624a57df9627cb989340a5d93f9b3db1f6863e1e5ed7627c40675 ea3a6130048123a993ba76804f50ecc5a91a8429055d39d4062b558b5048e360 b6651469d9e8ad72114615b03ec15bd815bcafbecc17ed58bf04d81ec22efd4d 25450ca2c19b1f9f4950f9945ffb6db8bd3bca786d4700bf48ba0580f85deb6d 8a543df93d7a3cd8e34863a4b55e2f837473503d77331fd3ff76873c0f81bc4e 66b339adcf6ad5952e719f18a6a41e10cdc15f46511a743547dddaf2abaeac7f c4f8d40d3817200fb2d5da1cef2309ded30373738468ee2da3d8eca678a2f0e5 7b1895bcb3a6f48aba6593a70d78dfda60fd2d82f3c97ddf56f423bd62f4e705 cbfa7529854efdb42fcf1481c856dc6e4e5388f448f6a1bfe34f19dcfffc1f27 be6ff45150c24cbb6a2e21ca086806e6afc98222eb5186b7435977fc405f3a81 809587a11538fde30d447067982ff7062c180f1402a89ea0335b7c1452579a8e fae9c26d2e4b877e66e848e2cc8cc5aeb54f61db23c0faed660e22398049b144 16aba82c82ee8f502bc0778428bb2e472c52d11b20419feec06bf5c97eddc943 05fbd05bd6d21f111a599c0e945fe6e7bba4b4389051e86ee1344fe919db59d6 805fdadf64a521442f64826bbfc7cc25f3e5e6ea0aa0313a14cc68da3a6c8e2e b2f51ed78eaaa90e0a156a102059afeacce07213d92209d5c27113a477f24820 a0b4392a50f79bda87539c9171e7bcfe5ce7798be91804133eeb19cf1e26b510 8f7eb71d025b597e983b93e84b906933af703e93018afea682fc22e2b36db2a0 4bb9e5d896366712287393247588c8d017286ad5ba57d9155bdfa977d09ed925 a923bb1b7dda51949e47956bd5a11111d8ff3a99025ce20f59d434958e8d7480 ea001eb6f50725fe183dd11daee5ddb2b3794da823d2198b1e7e473d872b26cf efbcc0729038a43d2202bd5c1eef7d83adc3e2885e59363538e9be6f6e36c4fb 8196cc15405bc5510d4370de2bff2fe0f4ee5e03011fbabb0c1d08943b0d05c8 9087dc9912beff997cb2ef84ff2823350ca667c408a065312aefcfae5214f2be ae65a237ee9f434804928553608c7c7d739dca45c6e35a5069c3224f38f36698 75de0047c22181bc7e014cd51ecc34561b6b9bd3e61eb1d2966893cf76865d22 42cb74696818aa9aaee241bc708fcd072ac307b1d992a3c39e3df27c360a9924 66169e6f1c3a55c5424b0ddf5380781239dc833bb119fea0d26cc1ad2a39f3c5 ee7122657d4c777f61b851696f1ec6f0004582ab6f9338aa4c37a5c38dae287c aaf74e486d38da8226e46a75c32bab9199753783c5c444463b430add9fc421f0 7e3fd6edcaa84f4e53d81d48fcd46e02c629c6dc7fb064518a564ecae0f02b87 6e88e665c4f437c7a4e0cb3b88fac36c1d48901c3a2d8414b92edaf812716067 96d659dd965b852d2258fcc4757a5b52226778580d8c148a9ae723ec3e5a91b1 c88e6c47354fe247f5b6918d51af95a145cda713d2e8bb252a619b9251f4cd4e be344b500a608d0b3415cdd80757caa2a776f26d95c50b716345868e1b7f1371 43f96dc99ea50f3ee98fbbd963b2e9719e821d071e142a788d7401cc906b73e3 1d128d95f26afee9c0a7fa884cd80b37141909f7218835dfcbb83f305f5c6c52 04ddada77e60ae8e529721ca61570b11c3048909b4d7b4e04e98ab4ac772dea1 6f1dfd9b797ac0d28afcdf2bdf353dc5ab28c5e00232b522b4d3da795477038e 35bbc8ca8bea7128f2de50743250c8f415cefd82e4130fc1a38dd7d5485eda08 f41ec9cc6c276273b5df2b88b0215e4a9ff3c0a0ac7cf4d970c5a41d69e49adb 08079eeccb1135d743562a37a745eda2af7e421a7f8c4e6d713b6c4f7a559cd0 6ed238f9476f47fa07ca435a611330857511ffe7c873b2adf946eba5c366a010 4151d657d839daabd6129e4bf08eebf483c269ca6e86f554b6652a0a64a7cc3b ef548c5b55b795545b63925340b460a4751770311f29260481b101279a70d57d 16b5b5275443e0c79ee3910715e03cdacca3a10dfffcf78127a978e954c8d047 52a66e6415e496bb6a76f51183c447402b9b6a22c1221115fbae3682d1c6cabc 15e6a7801665fc7d6cbb49ee27db6fba576e5ce4bbd479d8cdd7f0a39a4ecfec 1a1a79084b5559cbadcd95ec909931ffb39ec44c87b9179414a37135c5a5909c ec3b9018fbe03a86edfa66c39413fb85fd8b4dfd69adbc2b6c5a7e5e001f05b4 c81cea7c368a81f4f177ceb66cdca96067f4f23532d315b089bf5ddac55967d7 6686a192b7ec0696103fe86fed4e0311d4e369f51b11c388e80537cfdbab7d88 234cf988851baf73222e3d060dfb2ce15b496b87f6bc6eb8308f16e38821de0c e258664f9b49d85366cd96f17c99535a7ea33b9cff2e841b567757d7b1aa3869 bdd3898329ec6e0bf78191ce153c0ba7bdbf7a3e91615937053ede75bcef3f10 3c3a0ffcb2d31466b78a0c5b66b73041645e041a83339a24488ab7bdd9a4aa5b 76c912fd50e2fe8dbe09ed03aed7665ce830c23df0234b2213b78cc587dea600 1121433e5dfb3cb9e92d75945c5676eee6f7eef6291477bc62e41f7e8553347d 97131e2fa5d29dcf7cb10d5c19149c1f005e2db666024c257796bbb3964f7011 e978988852907c74a3df0e9ea3f0e9cbd4a7cc0d92e36f2048d54248f91f5930 25a294c2be33751e147a3e67c234420fd9eb09409c4b27b7faa645e5cdfa58b5 c9b5bd23846c614bdd4973b6b92b7a877167f29277a793eb5cf7a72ebca81b24 6ea2f8c08bf47dfd4c8211eb01bb49ec9448de4c692de188aa7d338aadf1b53a 4fafd07d4b0d9368cf15c53aca646e58e305ea8a3b75eb92b4b17144d6a4c8b5 264f55023e445592f1ca37244df347359a4fa4d8b34346a43a54d8001090190c fc58a59a9318fb815f0b34923e4c82edbc28a35de42ee53543f5973d0f576b84 ed114d377bbfc1c69b1eabb41bf835ec032bf987fb5a1e4abdd548670a8ba2e8 3561d59a2b2e57266b87493719346b7afdb23576b54b8bf662048edf6ed6defe 7c30300689f361f3f4fd1cf44fd26655526f3dca98eeda899a1d6dfcb3cad5b7 086ea4ddae80791a7c5f0f74d219968671e8d52724fd6eba1e2074bc0b268a20 7cf79d26e1949d47799beac35a92b1e523d0bda63c61facf7073ab6c9e773f83 2e24e33dd4b41b594124f8b88b358357407c89dde04b6101b5d2e48ac29d9377 97980941bf7372332e3eb99707f2da8910eb289ae567fe25362a9873cefbff0b 8912daa939f4e1adbd7c85f420fb3f85b68b34d14890f309e101a349e61f665b 23c2371b6781f6546647c9270dca71df2814440f0282c9624a304c59fbdc33ad 4f1ce27b41a8d63b507795d7c2cb24444b473606036e725e0b8e8e6041709974 3d2530277cdf2d40de52065cebb452c9a6ebbf0e5667acd8ae36a7dd32f67caa 4f2904649b8c9afc4d81559763b62ce576e355a6e1be73ca2c128bb91d0cba93 b485254a55a526cc697522a86fca1f392deffac730eda6d199918c1a48d52359 f69b4cf090627e6ec62971fc6c5bc36c87a92993f173165a7ba96d20a612ef17 a6fe63fa5162bc31a6c676dd667eb8a5a78dc9e966addf3bce24f1b7095c4c1d 2985a2d6209135638d5859cdb843fa9a8020ab4ffaa207a47838963ba7e630f7 f3a250b01b08291ee496ce9b0cc3c4f227a7814f2595d241aaca979057db9496 a28662c2f85ed8fce0f2b528297fcca460b6c8b651780e7bc60e4c85eca56083 eca0fbd632aa4a241b568b9af2537f12effe104255a13ffc0a36541bc697e414 2c7c5c397ac344df2d48dbaeca0f01b49ea4b5c8ef887311cfce305f2248b7cf e6a4a576eea3aa00498da05e43a129a15ed2eb223e4ebe35f9b7dd4a9fdc919c d05837a052005756d4fb67c490a5914f9333826661130bba6c081c3cfa66ed33 9152af99ccc76b484bde333110b3dc5148adef85a54d6ff0f392c94bd7efa54d b273401d3f49e6ba9c78cf0795bc3181bf1ddafc5275958f3ed95bb6f5ba6b9c 15cf8cac6493ad89c4d188cbd239112a27962ab0f0f6937921e4f17af0c47799 24bd90abdc4fcf1cbaa773a3e77c5bfd1a8af0c41c2e4f4229e42cb637874d75 845e9d170ba6f370af4aa2929d41d897c0d5573463e4ae46bfc637b3ed21d57d c48785ca945132a6834eb918380f2ae8db318211d076ce9006b212bf7c2dc386 a52e0f978972c1b436520cb3764dd0654388d607fd81f3badabdd03e88f7565b 6eb66444f72a040734211d7c80bbbd7b6dc32f4648772e76e1cf7719462e4ee6 e673b6d15e10ac70d2a78097c3cd1a675216c333ad73fb441944970c1e748918 5e8ac899e73c0801de613ed398615ee333273dac00d26c72958b2a7db8e19691 ca5cda4e10aa2e8e435c1345ce7eef337c6f1c738f92a0b5a8be11004ca0f785 bb4f223a27b5044e6e93726ebe4cfd73167a4f80d89e258fb0ff85a89c3c9773 2a76779165062f06f26eafe7fcac612cef2e49ecd74a048092a98a0d23ce620cc968a9d6af007b684d54f49c5a63d33c9aff9b8c68140c7920e6218d10fc8a042d525f5c174f0bba102efb95a98d41b66a6da44ceeddb55f1fdd03f6ef245d0693c1bcdf3b7f4ada9a0cb30e55944b9ea6c70b38122746a63660495865143d0ee83c23af6cd372d6caeb20ef89a9bf937c62d9c8d8187f4a16f3b6b3cdbc720bf665388f91908472ab069947a74bf4a2c556af5ab642e320a6e1e73554ad1c0a3ccd3581578bf7df70e291912fcd1a3ffa31a36fbb078d2918ae41f34ef634036a4a62497723d87694127f6cde293a2556d683d0ca3c89b7c514928cc051800f8af6062bf489596965c55dea441e3f7b4f621669bcef3f760ae42ea5ae80a8098eec55b998da601871694719010479f45d3a7ead93921cf05e0458b9f6be1f0d08efcf74c4a67053294e50322d9d9f61e4c17a589b5a6999f3a22e6ba74b1b064f3bd1fd51a59774ee726a79a7fc1d76614af243b163ab0c3f161e2378f22d07c19b3a2001caedff76a93edb23cbffe898b2e6d674584ae6906c8eed362b800985466833e8787eaefcc16f3887d6cc0f834d2cc0da214f6a7515449e933b4b02948c6b33a7b09059edc86d0929d0ca71a4dd7c4c9e6e52291097ff362ee3e304f8b4a3d0d755ad7fd697b763c32602ba16c6c61b7f3f6775c7a6bedb81c4c409baee7c601e09db76323c7ca7b159c7fe626d0769e99e2a125f3a1fee4f1cf40f83ed0178db3f153f65bea666dd3c717a1bfb24983e26c05d0ae676c99e469f0ff76e96b751c8db1c5b2cea9193e0ac147f7125089cbac7deb7c7d8a3f3efc103e29fab33e0717442d8f03626df45ac13259fe5be1cb7455a26477b9b79a36f0a28ab1088f32e8cdf44148dcca14df071e7162d4bb78fa47c91537e8571b64f070570c6333d01004e84888c5a0dba238d697471144ce0307111f7ad36157af60b1ff35f09256d9b98d1e1cca425e7b4bd1f8152d56b5d0aec119b9762b6765f0de27993e4896ee047d69d7e7127e50d6722171e4e2df5aaa23fe788340a50e60e376bac99e58f0ef951cc1fe084d601a910f80b0064ba7a17bf5eb93e6260ec0e4b457f15f45255b0f714f387729a8cbd0ad3e16f1c0d736a78d9e5a674cad80c2daeca4676bb2296673a52b85f09596755bfa7e87c386a68ac8062d721baaa01f38dcfa2f19a08bdfc2193a6c6c6ba313c221f1d984e7b10e1a734e295e57a094f51ba12d7dd536294c16160a55061def8f61e2878ff5a4d96b79c9f0523fd00b99a066c950e4aff178528e3487e715d590876c1a6cb8613e91b257fbad4af00da21b592cca6f013817d662e3c806dd1ae65de9d1082ef5a0bc1128e631793057e33d4ef223cf90e4d5ff5f31e8678684917f9c64535418fed4e5f25c4a8b000859cb9ffba93a89ef774f19409979e8a29cef1f3b8b59c94606f4af90d19290bf1af74351cb6520b0855f1af7ffe81d93900ddc8dc4a5257b4916acaf780f30d0b4471df8f4f2a9d82932092e60ce3310bed8b37e52a4bd43c00d13d0b1b8f05d2a0846bc530bc80ecd4274f52b94ecbcf18796b7576eea9d0f0794da854e309dd72e77b94a9f46b2dfd1979c3de54456c7ef91c2f830f7fa0044c0525e74803ae2348fb8dd64ba8aa9695aa91602283b414cb9d653b9b28c0b056703c06f804820076ad9aededecad4fd789e17acadeef4deb163ecc14eb7ff1aff5251aeb04a30d920fbf7ac8d41cd22086479b9e3438334bfcd27364f997edc0824804ce0fb267f46689ef2c4966bbfa319ac148f68a1461da856ed4133ab451179865e300f5a69586b8398926bce37471f5e28c88cf9dbf45b1e2e3ecb4e4eb201f2b610236a187cf9b7dc3408910d6270820d6e8bb17cb74a2f1cc9a22e55847e3ccff0b7495eaa92a75fa6894fdb34d607daa841c6e3f3f9fe44e402d405415e3c05108b6b84a8ce0ebb245c9178deeb7123e336514cf429ddf01a336c99985e4d1930050957fbf692981558a1c20eb3b65142abfbb6106a3072fe434494aa040e7fc0c37c770689ed3237f3247a5d7f75ab09580565e565f9b976a43b775e754cff2034b2b344e7b46c63da8c429e6e60bccbdcd6b948fd1eff9fb5001e78c6a95b80225b50d0148faad95cfc2c051af91163bdd9c5abb5d9bb6093ec265ef9b38b403b6efaab202c0c73cd075371756db08a2296b25f24261ee76e8172b28c5686d032d0e9a54f045858e8731b3bc58414312eae7ce1901a9656cfaad35ee9ab25804aa30f18b79cd4fa9e1ba8a2113c5cebc53598169f337a359094e9e8ddbb30608fa847a037bd4b87d611b2842755e855d7c9ac922f6c47bacca6a7a78c1663307196719db9af9a5385820a73ba4f0058e3d059a19de8d4e2ab85cb7331875ad0935e1fc8154fd28e771a458a75c929540fd577b22484559a30572e3a8d6d1090de1edd41ddd7f4619f2bde9c05e245874081e0eaf272a4656f7cec5922048c304228aadee2deb34a57c376568775eb4d19d3831c7f170adfcc4e3f091e650ca015be428118b1567843bd8c23f631936f2b0a44d9ca59e92343c02adda7746e2099be06ea04e161f80f3eed1554581aacfbd263565e0f3ab333f6066e80a0d9307c4aced38b2605691b64689da1e7f0c48128703d97ff0eed4b6da6d7cc7b09302f0f7627ba4b7cf142cdf282fa419556763adfefbd7e4e00e32b3d3fa7e0fe90a7a16e151863a8373cdd28e9c9503d044796dc472144a580075faff29c11b01039ea54c62f8bfdaaae16db552b537ddca67837e519e5f5068a7b38ce5753eda0ebc73b309c392e59eb2ea95d35b48075135af2b46b1007b9061d2e977ffd40607a9eb78fcfbfaa6027a3b3d2bab87ab98ef7c9cbf297bcf14e32b116daa58c1052ab09320300db96daa9cf0ef369f73493fe2941e89a87fa53e0ec639835f84057fc27421127245c5985eb75ba6fcdb83ff9a72e1d950f8745c401f735eb41d0bf9ef04ea6584c762b05f30d7efe2da9fffeea8341e5ae387d410246fbcaea9074a7d18b124fe077f0bef40c9a4eef546b99cc2f4f26a0433bd5b32b056db480e2d545337fbfcb1d4cf9e14b4befe6614ea915b46678159258a9914bdba19ff011ddac78028ed53cea0c0fbb5820995c8a9e002bbc6a48e93619d6313db437f05baf01ceacae69558904c67528c5ac4aacb89565fbda9a9c8ed4aef66c7dc1c09f14c029152e1edf6d0ed08b137d4657c4254c9209df5cc4665e93109cbfa7c0dd238be976cacbad7bffc7212edbc539641232c361dac48646c651b79f59ba70f16aba0ebfd87a6dc36e4569db55b22de6ac42a24ebc5319a8b9e1e974fd2e3079397c82b947b27c0c8b1fc38f7f2a0b102c43b58ee64ac7ce846b1fc7388da012f48e06f2286874f620f290c0df9e25f49c921ebe6832aa2be68847393ec390d35129191f04872860853d93ec92d37b4c2e8d9bb0bfaa8625a8a3c972991f704549577af3b23c8f0c8f9bb41f333523643e91f0ae324cdc61f0f01c896347c038ff3a9e464241a9f6d5141544b93be80544f07316fc63fec7821a0972224850c7b97766446861cc0488165048ef27aee1a678e43d902143a0c72281280a3730c0f083d88d165dfe72093f4584490dab2e3670d2cf6738ab3c77e657470f68c03b63d883e21d1ca545737114f600ffc0c40dbbcaf4a749bc71ebf46cb3e58590485fed533beb80adbe639764b8da35ddcc96746fb465babc18d7cfb95ee9b930f16799532799d6c3fae3ef89367b1070959d8162cfd12925a44d61f79d1820202ab08f1132f3dd9efa7040c1102427a61344ed79880e79ae17db72426eac15d079c76334e7bfbd1cf7fb997abaa2384b71d14eec0bc52ec43f67a3c3cd5605b0c2702685c5a0d1160186736761d771e4f5f1aa1d704414ce4a85228ddacc56809058b76c6fdce3520abd73c600bae0e370715cfc36e412e8f18707fdf8d44d009569213b01fde552ef3ce340393120680acb9d3face066a7631a2252a2aa1210c39af143ebd5372ab51cf857dbaa2df0dc2ef55f6f627a325fa03fa676b064a06f3f9f5ec509f840da490c1a5b6ed03eab0a43940899dd388d8876e244c192a07435a4242e6a3433533f0f9980af4ba9eee31126ac0e8176ac980fe1800319308652be8804856c4b58c9cc56c756210874934feac883e2324c3469772cee4ee0838c5bc90556f3e21dd9693d86b02446b559cfef066273fe12ba55cb493924707f083d38c920feb2c490e3d800b9d7800c342a181820206cdfcca22144c1b5b0a59f32400446ca52db86469323ff25e9a0ee6060a9689163d002cb41d6c4af30e033fe7b5fc395a5a8c137df67733355303bfb31a41b4e665245cf9f9455ce20cb8499d391e3ca69b6735397fc1895bc27bf42b9e6932027a04c069a68bec700c4191aee36f6f300855deefff4bd732b847f64a22c3e58ca1cb171e03cfca450bae7ceed289d458af9c2f57fd0c21e2d9e9a3fdc7a8c414f0010670aab1978d0bf5d6cc4d02dc7fc2e3107f418f096ddef4be82de0aa37459fa64b4a23c0d1809979a7135fc27b57707b9f5b8e8c9f03c33ec1c94c4fc22491adc362bf6b6720337964868efb3ddf42d9ad0b1b103fa5a2b8aecc2d1649629e40e262c042b240a0cdc7dcf72992b82273b84c019ba2f80226334d4c2608775c27d2b40cdabca0561f6fb1022906b3bab91e474d816f50a89a4e5f988d9066aa3f93a20f1025f06ef7da7999c22338dbeaf497906bb8785359aa0de6315d230ff0106b8ab2c42004c6e680218a50db89c6f9ad92b73779655d527ae0a801093df5d86a624189e00094a4ab8634a0ad478370fd7cf6559704b5b743528598fa40dfcd5fc81daeb0a88dfc4fe40b100cca56d7f711da4e81fc357839fb03c18119e96e63dd176520f0dc99ab86ab93be20f44f4451647bd2cafc81409c6c8e5ba3b7c4a7239a8950d6284645fd085c54a1a1545fa6a276bc3b4e9e5a2c8e4314388a9a2cc68f20502678fde8ca146fbcd8b8eb9dbc415bdae26c97a586f50afe280243328b6a65d045634a00c25c982918fd0af15fa3bacf451ca14abed04d79a0c6b36d5b4613b0fe6a89749089c98a6f78610fd05a5c51a2a793142bc87ddd3dc92c96242cc1200f5c3b857c5adb8645e68e1071cc46ef5d0c491b1a754fe3796b7a5d7f3940e0b875ff509086fec5958f05262a0e9f9dcf261588c4980a14c6c70eba7f315a50af023f5914be625e4e08a19287d78b10670d0d9515d22424070760f89b614f40621df5427d0c1b14295eb0591df2ecd5d9ac42c214ee11aa2126f8cbf7b30d50f7350e4ed6f4a226e32996573c77e0b6ffd5de4ffae7c35f67888248967a68402d4b74645ae8274759e8f96fa4009a457eace2f90d5244d2c8e86788d42a03102ff3f60a123ac603b38ff086af4fc30922aca40dddfac3432f4fcf451b4ca1407ae44c9731f64b2776ddfda6eac53a6aa8c46afddb12aefd7892b9df75eefdc0019d34bb1b76774f76e4ee0acb0244491b836b5f228e06b31dffd5aadc3c2b40b270bbe77e1686d309ebc08fc6bdf4df7c8f19379c9da08823650cabf032f1a0a7ce58ca0fa65b036c3d4e8b8c7504e95b85d4d34522a02cd0ff76590f4b92c056dff6a96831c211363de17298a1a661690ba8190a23f49e6c08c0aba1064b207042caef049ca6be48210cf308c40e94bf85b1be2c4a6ebca5ac9850bfceec802367b42effa03ae17427bdfb97d9c023e447584c6e543c67d841e78669b45ae01bb8b4889ab68a167769abfcb4e7027eb2eb6b71264c9f5da556d6f7a29566c07abc17e7b0a624a1485e2a2d14936bd120fb9ee764dc23d6e656b428001d3000a4a35d9c335c6ea2ffc5c7c175f5aa801861ac07d5eed6f35d2cc971e36f02c0de97ea5e7d29f76ee719e022b6f93361348521fdf2e0129a29e1bb251012359026853600764487cf54f9e926cd616a274dfda66f7ef2b3dc195496e448cd6c80eb73f18a7cc5cdb0799877609dcd54682ea24731fdba96a83e54d16d852f57507deb03016fac1d75dacc8fdd2bc6b7ba8c600f4f65ba1936842f168c94b009004d4cf85e3c5d1460202869f1d54a71adc48d4dbc075dd9c3644400b1052176404574a6b7d9adfc6950cd628b73fe1a04ad1bf5d09f41962628538a4c1192d9805c5845eb01afee6debebad0ace8c619cf3840fd30399baabd8a8c8738fd0bdd08232b3f7d78629a5cc63f2fcc9d2c67e4908952e93f1b9cd23ce640f5db97330906831aeb6c00e5687d9e3bdb083c29bd20d09a78f090271e598f873a3ab9cf07e015ac5b1133bf2b570d3ba9af38af112fcce760f35ed0f1ac396d94e1e2960e5ca7b5969a644c9df7da7368c5e6c907a6381697310dea71ae7dd0cadb8deb0fceaeb4d716fd2b027a6db9e598f77df2320d0ab83b2ec2fd9aa24a127f581609753e1d583c712d80efb384c7e101a9ddead60525aa5e362bb77eefbfb8199a0bbcd15b44c935dae9114f10f67301a1f41b37806fc5fbe98994903e539a0c53056303bebf858130acaa3422f66619d218b75a5a7565a032004248bfa893b1c609aeb8ef8d6d5464d7f450ec360d601dbcb88c0991470e18eae5c3af4e29975c03e57ed253066fbcc7b8ecd85631beb4dd6495ed0c8e6035424c44f0f7d477b40de9c6f09c136704bae0e2b55355ee6a349814f1baa00f733b9818250407f36f0afce11ea0fc36df521e3c174f329a36fd07721bae0930b1e2644e588be6839b0ba3d302a2e7c6054085c26c37bf8745dd85561acf8faabc97dcf735e273de7000137156357167f86fe485f030e8f11446125ae90e78f8b54fef607d79162d760547cf1eb3340e96381bcd78c5c6d731361fd32b0a45f9f39547b50b46b0ed0a09e250d825d588ecfb26061d3003ae8d5293ae29de8b80d35617db1554548dae063ac9f6374d0417e96445c6e6645b0a75285ae1d7d361b72f6fed581f4c0c4401225cab951e92083e90b5d35aa144a4fea1307c6b91d32d3a032a8f70b9383408214dd0a02baea225fa90d7f0cfc254547ff58405f5d1dda165767784b32501049b530156070323de9e042e189d916b6ccae7174c2032386272d8c78140dd7f0afc0eb4d0728ea35f9063c08dafb6eafc849e9b316ab6378fcf9811009da3770b767c5c6e879b9ac7e4968cdc9d74c10584fa8b53418eb4c70f4142cd298e5d00df0bb72bc9eefd4ed040f23ca6b235d704e8482979136bb8741fc0f99fee8a06dba68602ca0e0762e25d82338c85c9c801704af64fb3e65397fd764696fd5c0882e2ea60754546ca7da65b5d58236528ba2e7165247af31c9a91bd65976a1a08b010956c58e92420c2e133e41c2c75019dafe4e97e3848bca7d1bf8020de71050fcba2621dd92266eadbdf541cb9d971ff290671665c6fe8381fbdc78008420fc9201f9f50130d79bcda4b89d9a372809080af9d1d4c1e79ab984ed7c346e30fa3f4bf09219902eee75c6c1110433a6cb6af5bda766d36e60eef3bf723f5fb0e550d2cbcb5270f836a87c0bda239244c4bfa95a6e940642dd83376788fbd4c0b0d1429c764cd5b6a069ab5f47747755f5c70e7cad4ae6df7d690f9d56fbcbd0a9227424843124db9e8c16c2967fedff035173934ca17fd7895a006d50aa64a0a1a39c6c3cc766b25f722f10336db5616883f9f92bf99a71f98974e7ca455ec03d0f1511eca345718ff027db6d4483ebd089c020a1961455c85335308be9ba403ed53ccb1612d11aa3ec7305b9bd20a1402839a216a55107a322c2e5c90198d0f9cec4e8ec62e312f6d35cb9c16d93fad0224d9b721768baff13ed8d4b205d305d8dc4154fc40ce81118418c8da4439b1c395f00b5a051951f0cdff5e5223fa0a15e52540ac6c3e040466594c8d3b5f7bfc4da1d2730e01d1a770b53667f3370a14643d6f8c17693843a16c54fe39fc9b93dbbbbccff0481d6c32bd67e7473b04152a14d7586b997977f7461fa1e4a99ef890504854585294379bf053a2596d0d59824ecef7911591491f724c50d02c6f62e0095fb7f319b1032cd1cea0f0c707a2df049e43a8f1a996b0fab35773e31cd10bb32237312c3b3e814e45d5f00b03951a6478ed2076a34d0b90dccc93a1099b43d78bd865ea0d082aa23e0e75f9020d282ec91725843f0f941b4c6d14431a908d63ff9cafa54bc34752ed207f630426ac3559d78e3c685932e7073972515f50174b3a649305c3509483c691ae04065140499235dcade73a0d9f54d7be40981cdce8e54a3929b0921a810b38da13030be6bc3256d00df348f3df19dcc5a742015a69193b17d61ddbfaea120d62b40634edb0e65d967de9189fa868f7b932d26e1bbede6cf00cc5ec443c3927fd590a5334a2a7351bed9d6fc94f685737ba57f47988ce0612b01d912f55b6118c940dfb10a4352a7f3a1a423b9ad3edc07f68d12c5dd910e9c0fd4bb2abdc5cca9902d82ca53f0ac5001ce571d55a1939239a0503da10a3b4ed56fd45cf83cc50c103be4c6ad93e2d780d31ff8930c739f899ad5a8dc05106fdb874779d964b68a90135620eb2461f6f1502ece74e957f572644c00f2cd736849bba4eb864063efe06382b8a0a334b07565a28e250adb9c87d62ab5f9b78d4dbb63a9f72dc22835e0d8c63e99d8d6704c77c40b83d384227e3c24a28160174fed84c5cade6f32450040c69386e8a69a0b10e3087ae1d5ddc74969724e3ebbdbcc0b72e27fdb235080e86feac3cb60f5d681f9e5c0fd79c253e79991214e4e65db0cbd09fde24f07a0037694f025844b1a2f77fbc8918badbe60f4cad73547f3b192342f92c5e3a370c909f285aaa6a12f4839fda4fe45a2f00c197268e4477dfd9061db6ba792fc8017a54d96b5368b1c7ddd1d21c2dd2cb49bed836fe9e7004a1f98fa03043145e03549116c22641293ddafcd93ff5c6c04b6ac3dea3ef9cb76c0fccee304b004907f389ea9a92a49dcc0cdec21d91c5c67a5c9a35822f81c16de640bd466d000609743e4ed147e0b53073b143e64eff1d592e23164c9b8ea90d46fa56017819fa03be4830e4f0d74b0b553462ec20800bbf3ad7ee324268c5dbc2a2f9509a844c09e8a75358ca597b1003f0f5881c122bdcfa4bad45f2120ae66069e95f5ae3eb0d1b761907265580a2c163a5fd4481c7e0de40b8c5ff2ff0a8e1cf0415f61d230d46ab6e0205ea956a6d65e7f273ee88eb5ee5328432ea902bf3fe7ef153f01303f2f285bfbe74b8f32742993d150570536115a113270898ad62caec20fea9a80ebea157f0a30792abb17e4ba56cc3a562946601b3fcac8a6130c77c8c7036e90257ba8073ab41b7b7f90dd97729be3446ebc6208ac0d16f6dbd0c3d98c74a9107297c5b6f95c92c978cbf73ec44c79e6d12baf0600bd7b1e595a67d8511bbed01ab59f8794d3e858bf22cc70443fabf22764058f1f421a1f28450f99f1bc8dc0a05cba530eb22d8a8cdf3bde504ec1a1dc8f6f414d43fd5de1a38a4ec61c8120d4f3033c2f41e988ecb6e015ac8f6cbb202f73702b87ecc73c70dc220378a4d08ce401de41e426913201e14470ce6230cbf2b627fe624df660654c2ac18191b0e83a37fc7421b013ff1aecf571a78ab7d9e7a461fe457d32b132e2fe9cd891d0d9c4e91f8d5e308e3aa3ff7438576243ee0639d5104b44d041c63b7075c2f750051cb3576813f8e52bf3c22c56406d68bbb978ac80398aa82b92edb1a6a71d70484a52390c9c5ddeb206c752496c269a4c7963e4a45f4fe08ba49d5fbaf60110c3521d9f759e2f1a3718c5700001eb7f2a3ada37a59594600613d3a7a18e416033ce85d0a27bfbff1794dbe93164cd044debf7a21714e308bfa3234a10ffdb603c61d12bd3119addd6c4bfe8a6ea5768b65018110e22699c22245ce5252372f06b1645c52817bc70c042b5072051120a15f1d0edf4d05f4311aef75aad1bfca0f16b9d8795ffbe500573200ac897e50ed144fa09231db31f5b2f86f7eaac8a402ffee1a73977034952d9e1125d3d1a8b02654efb25989c846216c58bff1e5e20cd393f2e3be2a2a3b3dd763a4c681acf300b48570c393722f112a55106d0d7d07732014b1c30cfb0987147ca946595e01402f989cf219c8f2097ac0e3dcfb3f0495dc8761d2aa0534efc0b7e16df36a7d4b97177cd1a3e1225e1b5601c5d6bd01a0af555b1d5cb3f9a3ae9fa0c2c794ddca2ce017ccaf0b938f62d84152590b07df9aed26501eecf6d1e270acf7ae57938a99844f94719599a5cc985c28b4170c4ee90e2a5f41ec7b5be95977a3f69a54486ffa32dccf90558f83c810a5d2af0ec4c2280fe7aac684b1f8381492b4502d9db21666c75288b4e4959e36b8c4940f5e3d82c0eb39a0ead43e7359b00c1ea4bfd3e049b95a079916ad00213228970f1228e9e7e93a1654dbe88890667e5da8148fb210583b3ee684f1c1799ab2140d502089c2d6028e5080173a8b4a773c7269c6732756815ffb24ceb9da45e0850289cb7f12e62ec377d65dcac262c43e98dc42d9c6ae4fb1aff744fad1fce6c006608f00176b2f4d6a42cb74f1d0a1320e3a7914e6c8806e44e8d4fce4ba5ee301cc628caeb9cf1d4c13f10afa3e23e0209b01d8d819e8cd5ad20405279cac1f07a8bb38d399e0a76647680db6986b69192814afe92eb1cd9abb8141fef3a7370ad82f85aeabfd99afc487ff518b136464b192c59c082b3b419ee5b9144f3b4c0f675df80c6402712e3547419ec6cc204003c0c15ac649dff17abcd1e32eef1a01ca2fba613ba9cd92f5d707a51ad29aaa7d807ddfea96f2329df08ea25df0c10ee3ea6aefc812f5d29fbd8f71d92d31745ec1f9b52391b337f95fbe311e5cd10dddbd9c4444f4f0b3344be4466d5a7fd29f61b2eb628b65920ba2643dc917610b7f2e7a5cc6139b8fffe33f2fae4b2474f41ad15f6208aa5075e8c67983d125050e64bec1d12da98803ad8941d0ca2950c5338518a1f00563e3e18d66faad8b0e6000f92c205b5f6a922c825fd41f01b5a500fa5f1f7c2ad1af71dc3bcad33404c2902313c5ca5fdabc2f97699ef65e26ebecc8ca844fcc8eeaae8ac76296b50aed06f7c7338fe3a5b16023507e2695f8d21750f13627eccfb025555ed251c70daef83a23ccbc395ef9f634d5753b619814324a007457235ac96581b2104eea0f42889fb159ec43af5f7207e928bad4169ca33c96c57b17bd05c238e012b3480c5a10cb52e4fae14bc1b09bda7be31dfc19b7483fbcf9439fed0a838b9d4b680be0d17295950e8e29cfbff89ecdb60139fccedfeaba1789940930414c83caa607b7740d076f5e5aad093df4c86e56e1980c72de9eb57c33849ff752b8731f0e0c556aa22b50f0f8a3a723e04800962b6f35ab08ceef1e958f842444b406efcc0ed33a649460a405614972a6aa9090facaa19611f4bfcc7f2df799f8399d55810bd84361c31e87ee951bc88ec8299c17562f4c21ca8ba5241f031fc9e187f1b10292d95f77491a991cd85a72de656b43c3931e08e76239044f3940190a6ce5fc0f0acf679701ca9e0d2ed3a7af391f40fd2f91acbc398ffa2d72efdec2a263040fb45d3db9de65fb0d9c50590e18425fe95c314dd88cfd84e6c318a90047001e035d246cbc1950bcb1064294ccefe488a2dfba4755c4408bbba1942f3e58c8220fa2ada639501a8e513d536afd15940d85d74e75d25808010c0982186e06b51f01734b4484a4276bb17afca3e812a1d5475110ecbae8352854388171dc92d5fd032d98ddc789525d1010f8dcbaf433b6e33a3644a6f71836b90b7e83ff8219320fd63328990d115fa19fe484fa58744de1e0e39da15b8c0ed0b2a817272dd0b00d86ac4980b6a7fbb5a58fc3836eb72ce441a29aa6d1d4207af85a60473441e10342bae10ddd0ed54b241089d642aac66117e836879fb0169f8bfa0baa1faa1e05b7eb1fb2f4f4f30fe05e51e42383bed617f5c2bfc1bcba54a0712cce4d187408f5b9d48e0d21c3abb733156828a905d502a9906843dd0b89ef0684f384baef0f2dd6d1bfe8bb143216fe22c9106c5e17b337e31c500336dba9917f7d9f55bf027dd1c21ddd5cbee03b54b63b7b0db26e18357050abfb50bd3f1f9e2b48ff8c0f302f3acfd8c824c7d4b15ea865c920601705890fc6c23c6794e2f38de63c87095ba53f5385d8e4b2e8c7a4424c033ec7f5098b8960b15fd7ed76924ea7fc3707209cd38b7581d2db9e2a2e931e4a9e5a970cbe7fbdb1699477738ad479568e03ee35ce82a22e6f39feeb6d53e28fa9e9105afbc3ca904eb5c150cef2731c290c944f53bde3ad7a9f87b22fa8b785e2d75b8b6cd77df8f51c5eb08eef743f5207cf6b7be109b2564ed21bee74ebe7fdd6f6b148b1121b37f1b2d417dfd553b601f25ef2114d499bd86e1e3ee2a7664acef05bdd30af58b95b5774ca20422b2a027e74d68cd27461de3ddf47f952da968ae4338bf2763241e281dfa956aa48be0473bfadf3d57cb934fe0275ce01bd4460f10d402168d15e71b40e006ba895050e443dad43e74e6d27059f065c857ce28c80f52c909ddb088e6a854d5074553a09d3747c11a07351ca014694f9856116ef09a295d108b08b80181715e144bfff0fc0a00b0d7181b0c529f2e846353c5c9689939d025469c72c2976f6628d772b046dafc0fb6a811bec81536a91b2d6ed8e51c56900f30c3a9f3b014c257475b206847cba4772c8956dc8b84fc53da532a2a0aa05309a7fbb76410ecbe1822ab90ba77574e970c3e3b27e22385ded9dc81e887d3a89ba5edb3649ba669f32fefc00f4643a01c12ee9605657a8eb5a996895f75d9288ccf850e4cab11da51b14580e05ca6cc7e6f74cdde94a9897eabbfdc21ccf6bf89a16b6cfdf7a7699b498af0fa4d3c9a085ab216d64ea2c91efbce848b9bf9f8c51e324de3781be7c47499d08462181a9e207a47f484f6710007798b0f91397d5ef6c3b450c82ca80a75a380e4fddbc5b321d4ae14bbff9fdf982820f78074ca293a150b8a1db34cad442b3015203a5aca2970aca90762b93084c6d08c753d915c6321ed6c775fc75b9b72e0fdde5dacbb61cd765d58fb26dbbc61748a86946850916c7a8899ae47b84f24605a603b2d0cf2b8741632a6325e6d11134d22e04ab171749f1303a4e8b78c299004539aaff52330f863719d74ae263470646120a65e8828a0021bc9f8bf22798073aaa0c4f3e74c2fe60b1a5721885e090a189398d1f8bd0512eb9cb087329990a5ba132a80670470792138a22880fb1ae6179b0a3bd722058122673d7039fa805f14f34343ada37c43150031e048858b4811c668178d4e157410d9f2159ac6f0455973b003cba319b1a294e01e961a928544b18f3597bf369db9575db46c99e0afa5ccd679e06b95633932ac8b1887212a23e62c994a701570caf8e33a4925205a9810b7a3396a5703df6e8e7d3d96c8d81eaf7642def119cc6fd5c75536e300e3cfa474e8ff2beac86c3d5a84accf38b987fd7a372041354d082f8c841c8b802c2edbe1dcda5805640fb425385cf73e41cbcc410c31b26e22ec942f3b492030cd0fb83e904394b2068d569113839b7b3dd4dce936ac00907612a5f9fa5977a074a76657afb4a0e1e3284dc1fe3e72c15eb1de596daf41705a2d84e7518963a03228512fc370059ae280b9c56190659e54d5e1fd6ac2cf6f00b52d587bffe33054ffb529189472ed704097fc0dabc34f0a9e6de703de2f8d8d4921fb774ac54005d5adf0bc2d2f6c8c3a3be6fcb6233a238ca0005401bcb78c7637d7f7f260d05531f3dc5f999bf0d54f5b8e974155e9266f2cb57d578a2253372276ee1f57f07d28d92e600a1c24074624fdefebe39a114da7d7802f2ae669dcadd6f6c28e70ac0db34af0f992539c7ace17c2e12a8d4f95a70b47e87536552a1ec37b8b0210cff06a32cdfe976b3b24d52fad9f05033be3ed59576fb194777b675d28c9d6b0e6adaed70069ac25c7a8ef185d6830eb034e46ef2164c3c8d7a181f68a967aa0e362ac73e476cd998bb4e1dd8755fb6e0cfdff0b17c66943273a11cc42440ce008b3e68a3a4d67f5f78f72badd24c6a24d669d80fa43635fd828ac98f9860a803147af6e9a67a947a9a4eba788229aefbbd04f1fe940eb83cc564df1370b7db02786745cca2eceabbd4068537e02fc93ef1ded14f1a6eb4675ec14c8a56bb010984d6e0d7d1efd7d6a0476ccda5bd6156d0bfce99bf13573dada45f403d3608060f0343f0c799788a110c955dcc98f4c451fa8f5287fbf4c20627347067a79b0373734313db2d3e3b55cb6e94011c285cb053cd4ea9c478094a49079103831b0e4f9bc1f098933dd62a2d477985852caedaebf700a76c5f4a5cd4185f3cc7c20bda118e5a466e9cfa3c61b3dae753bda09294dcbe0015405b598cd7c719593307c98d55067324db7aec128477db20c8903f846f90468419cf59a164374b6e5503b09768c6d20ed84e76793260d3943e0b8230d68914eb7524a9cb28f252c5b00c708a2d8039a9e4965bbeda056f0b89cbf422aed2904c47eab5065d91c0ddf302f92dd0b6d7d3086942ba8484c9bad7960cdb94a5a965390b0daad784a9fdd40e1cab11a74abfdac3dbbc64d99948ccdaea7b68b404decd7a259e4368ade4310049925366e3f50bc50882b2f5c7aa0c03d79546c66ccfd37f77a13639a37ec509640af312f7bb60d91e265c2d183fde91ff31745f74bcef1343c3a0168fa2c009d13e3f1a85e91c68d3d2d8ae71e921a29725093dd8a0b8ea3ad11735fced6201311b5c6822ca696e37e7cabb5e121e7c250aa6596752bf71f8d7ad4809a8b0017d18411a2bfc67f9be5e47ef4dc224c19c684753fedb39e1be17ed5b905619055708e95abf04fe042b8710e5e7cd0055105a21699344e10e0d24329c0181960cabf7b09d18c6dee5492c580115fb174d2fc4007bc6ea67ccfa1b612f9da06801d956daa0447d1f69001ca0270420cde969daa1ebd405dde9101fe20382661b0ff84eb753c22cc5e04b0bf80930157e0f095704d677655cb16559bf4865b477015d94e918ee0982d3e92cd30186a100c9b6f8e35daae853772fa619f47331050b606656be403c119579c15f74c17fe451b150e3881484006ec10f13a100d46a005179f7da75bcbd6e967d6e87bd7dc7c08de43aae6c6c0052d844c9800456b801f7f0dc8bbd80a51157bba55bac82043a65d06f845fb47f1a62d14af49621970900f7a4ac95f68f45e1c0ae7ee5085f612ad8b9769f9690ff15d98372eb25a90f722b7678c6e83cc4be916cb229b91c9c690b96c1b237eb401fb4961d731e7b0232fdf14fe4807941e70878b712269608b940acc7b5019fd751732f576db0490c27e0c6e18138125f73b3e56d3ccad955c8ae8bedf65d4f9f83fdd4440756420728aaea329b2360522bd159b4405200ea5c8f44db864fe50272503d278cfda20aabb2285cc0c57fe07a930e73334dbffffd3323f00c5e82eb0d25018ce225f30d0a1990d5367a3f60e4a8c3a9d7a56245159e34f67b221ec1500f2bc08d5c0d09a668fd4ec951561fbc3dedf2a405229ff04cb9251b1fab236a58e8ab7f4c8f061099a3a60ea325c0ce9bd1c76cbfe9deb63dce960302068902d3b5a8358ff000dc2d39ddcc4e952a01af203eb8a5f219b03a419c865ec033d2700aa27950ae096c7e8b417de6be918c412bea04de8015c7a056a075178679c51c80005e40390c5b7ec42e9001753a73dbe0c8e8ddf5a7f54f25068d76c0a901fe82786bfd5a0304ca9b386a561e4e36048cf0f00b2e231fb54edac476887d9bf462f93430c301d90080eb829d04a0c05e25ffdc5d86bc47539e8ca0f52d4d2b6f81869f28ec0f85c1a6043e608c93d2e28dbad7712ea650af72727f63aea4dbf7517514ca050688759a9568fc774e3dc65179f9587ce3685ab4ffeafef1c040fce29fb19fb200cb8e4febdc738455bf6a2c135b6aa7ab3b52cc032cce5b7934392caafe9c8c006939d02fd24f13bfb7f4f125d5b73d572b87c8391e46a9941afead6b48ba48043f8687b977c26972dee8c99dafaa8142ee60675220b828e4c27497d998356e026b9e68331b2188abbaa32119dd1d1866e2f0d0dbed3eb37d713c871205807d011f2a5ecfb5e0e3e8deadf3b76a142fbd69b1860b8e6729cfc3afee1c0bbe6e06d956872888b5243b9338dc2fa8dfd7774c1b9538d92ae1535cfd291e519aa602cfdf11d6a7b1fe319d4d9bcfcb1a534a18c1bd4cd0a5a130ba18008d5ed25605bfe63c14e66d56af304a3f12d10c7d43a2002b90827e1fc33df40d456a0b930f4fd80080da273e768c2babcf5529150bd116eaf4a0fb074fbfca481b8cf2870f8b933f56738a6ec8c149f50eee8bac0f5c435e8464de94d4b6d206809883c50e94dc98a077c466cf8eb59bb151b115798820bf44fcc892335670cd9c87568d02c1a0253d6d7538f2c96e406845a531a739439d275cad0fa280bb8aca5906aa03a6a8a5d46d73da84d69c5602696744ba678c078cb6a14609777216b128a39f0fad388fe11c2c7cd3e2b6709ae4035404c482346019d4bf16f6848ac0b506480350ac133a06a9a402a2f8f176a9f194f0ef2586526ddf0eaba2d193a22707c5020203157329c09357b6b2ef937824e6d814ad4f1e8ff7d75cbf524f3c2f27c701d5cfe6eed934e85b8045b24e03a5dd52ea72d787bca449472249f56d0af36c0cbbd4a02d2fb263d7114431a053125ad033b68bd5118c3f677a286863048f320b4bae97335d5f1ca2eb820c4317660068ab81d61df846f39b40b864e63da6f004b094c7ad804dbecb108c56f14281910b0666aac203865055c5f7cfe016e24f0fab2795c12ae9a1ff4e7150998f257ea75078ff7eb39a841d2e5439bf7a0a5f0abac77e1b35e638bb82c8af07d71e584675d597b9a4168f8065cbae80cfb1270b0c0b8670b32daa9e5b7dad131f526d47635dd251a50fcd3b9af7f1f1acda5c0d6adbf00ed29abf6063d5c19ea024f44e368ee7ac530c7ec9b1b4a49ed279f4067e92db3ee17e42ab52923fe9ef6576d4ee056d6804f6ea86e8912f6d505b30016681872e0b581b707dafd9c851a2739d7531b73c95578bd0d70dae5463ffa20794ca8df0f13fb45d69189dd343a589aec623ab23cd0c0ad79b764e530619c4094644433eac6bd265554e9556c10f5864f66f51633a5b2a8f2b8b4c5a8d86580a07271f9691169fad8ad18bf6852d4f5de49f319513509b53f7e5f558ad651106ddd2a632c28ef55c8c55b3e55034aa21c7270206d344de499fd2cf9b172b08017681d2131ca327b728d863361ffb7955e897a5373c78067fd2dd147902bc310be40fa795db78c1dc133bdf2a7ba10f80c327233c52ec602fbd7f27f85608b70f1855422eeb9072380d642181f59b45293dedf9458a7e67a82c5e08bbf2836409238189f5b66a8f2d7a4ddcff686c600d14c606d5b2ff1cf7065a5ed27f67fa099bb68e240efdbfe133a2a1b6c9a0f789136c0e397a40e9e41fba12bdae332f06b656a309f109f4180e5432ea1577632bc3b546dca63810b2f884c7866f444c04d77f4ccaa5f59e3e8dd692047641f12c9576fe4f0a09fc1c6df617e20533b808ccc48eb0b809d18a4bc094f27b3c13727e0093b0e8b1007cc85ebabcde3ecb069d3e91827f97d800e569f720fc82e3a9e38d6229fdd68b05f91eef553b3a4e0e1bbddb26a517b3c6c2e26cc2f35bdb7ad7dfae84cf0ca87e60fc32f860f1f9072093da36c5ab8d9be1f52e36e87bb46aaf2d185386835dcb017e882b4714a70f035fde252c5a8c09c6442b638c8c07d5da34e1bdbd8f7dde3182ededbfd8f70f8684595c07aa5885113ed517f971a49784115d403a3f195a69c91c6b38c37c0f874ea2f98a9bbf3b625a7d1b44fb168f4f591ef6c0a466bab2ab14a1606ed00881a0ad069d5d24f2bfbfe1b18a92cffede3c01c0940cddac005f358b2204620d5d44f19ee680c7d226c312b15be00caf5e30435fb84d84711604ded0bb26cd0bb3b2c3265669adc90171a06e191c10166328d15356877109f0a471ab71f95f09e536543c4cf08ef78edfd0629dc037d72e5fa4ec7ae7b887dca2d57dbdde09050eb188bd67ed2a5daebe053c5330ca72300942c2644c2dac99bc2b5f2888880974aeb42ad48fbe3a3d31667909875d861ada9e2cea4b0873b7a01107c1d59602fcb022a7eb243249a3738bb41ed065f5ca033f87518160c0658b832f7ceb87068b5f7d78c56605ded8205844e4d2e48a2700364000a2cc4ceebe7051d2ebfc04a3b2531ddb572bb10f8fbb3732909f11a5634f6b2e7c5cdab759c34da1b7c10cfb724dcee3f3e827d63d149d4ca63971e8f12deaddc0346d75d9d7e488f91004fae46ab78e589727f83ed29401c3cf40e705ca8f57716cfcaa5f9b03fafed6038a0a582260235834620f07f95ff0c4975a4b5a16622330a3ccd5800eb0266203db503e22e83dea52d85fcf1f8412be06c2ee60f63f153128650d366219acdd0a7caba344ba82c295f9ed5e738cbebfb787dffb3eb35b6667028d120247dc2308fef5a79d845fea0ce85af11959683afa37465440085ec249db852eefd2216800bcfb877058d1d0776b209bbe838c1149e5a67bde97da9bb2c3e33b05a062e1024ce862760a5e14a3466c57765596e2f3f4e5f6cfb818f865b3a8e591bf57de09bd506c646a2b819eaf46b6f2c66d35e4d020cac60f9bdb64d8ea989235e468042411a5357a260e33a3b00a04ced932e35721da7e5643a0ed4094d6a72a73ef05402e850a59ca622df7b205672f462c36c982cfbcc00959e383581eb8c08d080010637bb03a221ee2336c58aad73823580aafc5ff0ac216a95d408e09a6c4220e1224bd5da1ce3ed07e3c64b8a334973a36e2108096d2050e5d819252aa1f3004fcc8ecd3e1b28f98c4fdfc9502a76c0ca10292e03c1382424437cfa199ce8102c4ab335a9fb8ffcd1607d179270c4471ec275226fd4b1b13d064b92dc4bda90971f0bb314e5ada64d27afd5aeaee84b4cd7478e1ad9bbe9b75bc14dc7e9ccd0fd34b7b70a8c92a085b433f06f484b905a930166831e1d98f38e79615cb77f502b531321ee1fb1f03b83a47b18ebb2ab7950bff61de7e8f1ad55e8272f70699054ef36b006632d92682d7db7267b38862c2e695266f8732e1aa261ea8172d9001383cad239d773e268849c64c343f2c1fb3ba424760e577c8949de74ae996a407c9a4dc8db32b65f1f056901a93e93e2c6f0c54637e231dc7cc5266823eff6d0698222e548e578b5933da99f20fd5f6c4ed72ebe88f1921d1251fd3bb32f2270744d6d78954693a297fcde0601df19f22d5d22cb5a8676f7a7637465fa463a60f036c4b8292d48e79f7b91080bd23c39621aafdc08b5d9c8a493d5a4baa66ed07f034f1a8fb5440e59b0d7f0351a604c744cb134f14e874e4b7ad0322b78d1f001b11d9877366664f9cc18dceb2f11925871b8d0f04c12e826d02b5eb933e0303 true -check_ring_signature db1ba2edade48eb2b00affe18040fea8ef28ddff8cf4a1a45b19f30495c1d00e b01cbe33aa4584415ed2069c6655472af629b15def551b1bc159f222014985d0 2 56c16fb88cb75d2a24bb5f31bd25fc1dcb4daad8a2e6f18308a057406ac96c99 495483757f644b1b9c2bfdaaa9d1f3daf5deb6bb4c86ce9cfae9104716bb6021 5b7c95db3400ac7861f8271203e15fdd88ae408f9037db48855a8a5440e9c50179d6c26cedc7e452fc02e289e574256f7c4542e9e8d2b077443ef88c0fa9290ec4a7134a039eff5eade516128e7ae450c73e23cf1dbc3e2fc076109080519105070c739c422ac6bede44048d40e8fcfb15ecd20a86a9199b5c0f4689d7ef5509 true -check_ring_signature 11b2e27770e170e035112a9822a6e6b2b6853c65a7904711b3fabbdb33ddcf14 284d321057b0f41b923cdee7a1e4d232bdebf401b2d7ce86a26b18be5ec17a41 7 374097478fafcf23273497c500962c4236682a8d330aa55717c1f42dcca98884 28dcc78964380b3c788b5e5086a4a136c4593ee5ce1c079aca825ac2dceb52df faa72d12edf72d29c13d3bac623e31df3609a2e00f70c401d2f6a14d1ec22637 12f87cb4e1d526d54a76a056a25fed43be2e71ff55c7555a3761dbd2ca6691a6 b012e3377e5285f3deda8e20c717dd4375e498d022653a096e085dabdb7bdc52 1a888d58bf23d80dd2b73ee58e891affae96e83d159ef0c8165257086bfb8ecc 092cc10d3d61507299fbc04ddfae647cbf2ec8e512df8277971e352d793cc867 7f37661c278c96f13c3539b822468860c5454b8afee1d655deeb456a587a7500ac8705531ecf48963bbe71b7a25a386f8272e52748c6396cc676e57af847c20360e031031c79c3161d29e06b3953ea2b356cdcb1e9991548574fce9884ae5406d3bada083e7c8d96c3f1820d5186c095eae35d856951a50de1e4c8abfe0878066e4d9bdcb4c0ad59104855b9792ebaf8214aca5b0aa84bf9259dd3d60f18f2052abe3793fe8cb3947405d4a464878887765ded08de0d7782b5292de5c0753a0a561b6bc7f74eabe08e8829fb782bf96a256cdac75ac42cc98b926c0a761bbc032ac36c52f1ad854a51834310b7eb4479080af30e74f20e5b5e841522cede6407259ac56f862dc61b1e9b5f2b1782ec0994aaf4eef0083f4544464da36f374608bfdc53d031b2b66a60e11536db628e43433b351f489603803462a6c29784f508774dddf3e8320d0e5b048353d4fb5823a30a6de94945b42adcc4ccf61acbaf09db3d80a7cdea0fe3ecb77ed0596113ac4185524c1fc229cf0e8c7b428a411504bbeaf1a1337084dcbea36f5f64abafdc43c3526dea212d2ab80229e74d649f0df94129c1e50251bafd1808b7081bd26d35a5d53764e761fb81a0ca9ce659ab00 true -check_ring_signature 7c089d04a1ffab01236bca15250714c0ba7b0f5b307cc69e7d3b0306ead8886c c6e03d1faf0ff638a24b02d423d8b6e0fb405b0c6bdc79c48d80d6f2965d4e9a 3 6eb7342736b3a3f1fbe76e467faeb3e67e1814c1ca6fb9e5e7b135f7678f7e84 37f5b1873a8d839e380624c79b16ba3df8e07e16f4f3675333c641168b7d0387 f71276168a62959ebce53578f7e7f3670a341181f2ae41e23ba565f62ef28e3e 135f6d8640552ae0d2e94e5a47678ef771d2b44bc43ce12b0c36922a42dfed02e31f9ebdb69a0dcf06d997910f83cce92b1cf6720eec5fc70361323e3755e50460e46b09685125625532e5a62fdecb18ba3897f32b8bede60cd065ec1127410d9d9131cee5390ed791fe5c5e3c550d4756a476230f542ccad3ccb9578d6f5502c2db3727cee47eca553223d80ab3fb93d8cc2178d392fd3992925a6b24d5240a84ccde6a1baff2275ac55628d649e62d04eaf0fe168eccfd8fb6e0b648332000 true -check_ring_signature d082767f7435551438ac9d53affae44faead4626ae0d53da6f10754c51160bc9 f7ff6ef72643fed6285a1e8394d2fb2706826a8e09aae9fe58124f13c7514657 6 2ccdf24ff93e70c5f3538bfd907711dc4f13e25780ab67c00b76f4a6d96cbcad 823b7af9a0300baf4ecc40e84f73dd67210a693268fe6e67e5a27de30d5869f9 7edecb7793beedbbb1cf8bbe7c8abc1b6cf21a30b188e903cd491060d725f54b 00507971e990056f95fd00661979dc59adda98f43959bd9c65af496d33145548 2c33d42c34877abb5e0f0d5ff1e6bbffcf59b20fb162be10cd1084d6d02f1472 978f1cf298cb158fa4406922045a423da8037dd2c5add675bf9b54d95de3a0a1 6653153bb1c9ecd1ad37ba8f51bc97abf569d36e728a21ed8716be83b85a7f0f1f093f6a4cf4d18f8ec8d8b518e0942a018d02fc399777e910aea0b2de22d40a429452baa2de1565337bf071c4f8979bd38e6988e8b009d9b45288b5b9961d0d1d4dad28e4b335bb2ec09ffd68b94213a169e5941d65fd0f0e925f2d8efc3c027e307cc2269c24fa0268bc1da5c885cafa13c76cc727ce535649800f02367e0715cd9b4c8f46c605146497c16dd5c469b6c28ed952b345f70ec84bfe85b85b075d0bd6cf34e1912598a9522c9f9869801aeb3aaf7ad04a8f6a21e254d0457709db6c31be01d156faa3fcfb1366c7829a7427da86e71b9591eadfb9942636300552840afa4f72eba3092bbd9aa2f62727bdad0cd50820a384232e2e4d194dc80d8c214dda4b5a651ba602f8ff8e9127e5f83c2abdb60e21f4224045d927b3040c497f2d7b36a6ae2b1dd3e2b4963d2c04be6fe346240b6bf618b0d48dfad17406685966af0710437d0c4cd94f792d5c23a2bb2e026ad3b8051c3c667415d91a00 true -check_ring_signature 80472731aa93cb8039422d09ec93ee05ca0959bc8deeb7a4d342b117827e7f9f 9fc2c23671c274aa0bda03d0ce7959331e9f71f81a1722778d296c16093d1651 191 d665c6a9219cb72f16550ec8f22f8e0c77c8f285a3f7f7cb0fe4df8c0a63956a afd8f2b195a67118c07f18b8769a892e9e66f4b54d9a27ca2349a761861c43db 7c8bee2cc70aeab9b89cc65a29a0fd8945f9aad5a5be48875d9b6880551c8126 f603edbc85d837d562fc5d6887eafb31bde395c06211e9cb278e4b006100c195 1742f4f492e64764f7a1c12bedebcf4dadbb437387925bbef6cff3a6dcb410c0 b48976dbfb436345a10fee9459cefc0594ea68fdf6e6f7176b2d7948442dee5a 59aeb08e6cf6cb89a801808dd58bace42463965371966764f90500fcec5cbbe6 be74efa1974f9628f6ee8dd79900ddcbfee4fb3dd424fa30bc35d2b881eabebd 69b2e1f31993d6d161d44421ba3afafe90457f528a5aed8db2213bf93c9b90ca 545529fd242fd8803fc758725206331610fcd9ac5d82242f3454ac8964231f39 ed396913b2022fc4ce7d9540a73651c8fff4136c4928411abb366f8df5d20643 3e6e954795a5babf2ed9cbc14c0fdafed053ecf7cdf21c28fa96c1cffc6e0c45 a109d525f44459c5390777941113c638ea0e6aa1acca1da85b4f2d1b0e180c15 9db9c444cd00089c81d8d27c1d6492e6e0627dc4cfb927b7267959863b0c008f de573ceab15f70ab0c2d9170ae05dbf6c3cf66308154d3a8ebdfa6a43f9be002 4fa0593048a5f236a81b374fd52cc00ff00beaa36a0a8918befcffd01651abb8 1930497cc33945e85ea276b7044b84c4a2df1a06c2d89080a6ef0c3e113ccc58 64ac2a51bbc73e9c3f5bdcc63a6b486b35100e728f8dbe141d12c458aaf721ac 48f571c07f585c73340054930dd2f256bfec54aa1b1b07a006702a75cc1fdbd2 3d4405c8542a5a0afa9f339dea589eccf46a2080d82881120257ddf204758fc7 2f85798ab9b22e080e44b7f29b996c105a494a8c542ffaeab20f7c7c55086846 851ca8d579ac46faeb2ae5583c0cdfb7c416e79bd9fc62f3c614ad884501f9c4 8ea58ce6b6645621e9cb6789c5b8d152ff424e5f988a95d6d2cefc966a8d5b55 dca6f44fc2745443a1cf7025e2666756cd8aa79133c93464cabc78a9f5db8e08 04f7f9fa22ce2958bce60f64b2bf480b6fe47e8024bbf7ab8adb74102ab546ae ff3c6f25203c13e6e79ef4a783e2a88f0efe18fb1e1e8fdd560f8e73dbd59c95 6074fef964cc16eb063dc68e21e4b68cc05031993d658d237610674ff43f743a bb88d44ae83fb2c7e44f833aebad9e27adde386dbbc7e91e16295298a7657ed6 ad366e1738ace9349b8248ebdc57cd9366249cac83a40f72761285b86b6f3618 9872bf8beed3c7570f280e7a65566577625162431491aa1b06dec63ea406085f f25a4cee387f869eccaac755f925d43935eafddfb18aa1efc8fdeab18c390cda 02bfe510e0eed668070dc4ab3d5a4ab868ae1f7312d067e90a8a02e3f2f50a9b c8e01be7ddbab3161703d28331b5d789f09f6f5859aa7c787d10353a08452823 63419a988d01ccb4163adfbe282ec76813b156fe35da0a5b17daf3669bcb5417 2a62a62f1e7fba4fa085734686812ba100e4cc3f6776fc051ed78548ba172bc5 e35196386a1807c4fc8c748abfe8c0b18eda4ecfd52213e3304b6436c51b09ca 31030cb71958b052a805e98f7debf2a9ec472b21ce754ec7423d809eda384fcd bd29219f375de601f51b42b55f1472dff7e92afb9f7aeb3c56e17509e1f8a8f7 e7a05ce7a25f58840ebb7215cf38f06b4ca586b3750010b69d3dbacf9d11683b b1070c3d5db1d01d01fef6fffec2bfd69ccca80359345afe0d360cd5541452fd 75d97dc9646beae9f4976bf45075be447eeb459da6af71886da9979f4e2d4245 450166e62422399892378f870a4505a4f7e7eef900b60e8878a5f02d36e68a28 efa544c4e125da60fdd40ee27f25b34e2059313f2199025a79e03f627c197bc7 eab535e715ba81e3342b4cf1b690acab29fee902aa7969df3d7b727f58e6ef49 3c5e442848fa4f45074b86884d246dc179dea11b7300d35fcacdfd321c1f4b74 5833daaf319c85de938793bef31c70e0522e2826750ffab8e675cfe6dd89a9db 804cb3a2f9c427b58ffb7094bbc313d9f3742d32b60cd83d87ed00c1cfae6f73 7b0fb7961aaa9ab00d90376fc1c38c9292caa6603ebd49a67eb8aac7b193fa73 de5c3e3600fe36db3bef0501329f67c73a5eac1e19b09439188a7e45bdb7321b ce81846e39a823015e63e57b290c648abded559d4fdc1dd449638a60d91c16f7 1c92f002d19552dfbb2fb1e7fff5346259b393aa1d7f00d7060417b24f50b547 83be7614485f13177d997c15dab82ccd91e729c7d00b5417ac8284b24cac8336 8f13ae75a8ef8a796e5f489138b0ba0b4a9fc2028fb3e328ccb05cbbcb14714b 19fbe69566a8a4a5697f000fc9875fb74c9c4b6420b68a7e2e0af3bb79df869f c49c234323ff5b56a3de61c1c8beeece4d62ac8553d1f772f83a5aaf70b6997e 597bbe9e9a344a0f28699986bac49c324af131b7d6e9beb3581719726f538a1a 7c3c848fc66e8f78b56b7a938cf3ac364f87622cded3de08eae818fe26b70de9 410d20f55af3b5c4b155dc80d278c4b839c671ed38be3b97d7f376a7bbb4d840 8093d1f3f5bfbd7eee8865fa2dcc7dce597c71795241a6cb0f975c259b21b211 1c5080e3c2a536fcb6a8856185b52146f65b38d695210164c53db5a93237389f cf36166fb2c84e4f5931ab90f1e2f02d0d2d9a575e37c2f0c30460aef8181fcd 1824aa474594ce36b8a8d3209a226e78e2620ea077731afeac5e630857467f53 e2d04bdcf5f2fe0fe4c21836361d5d21d633f0110aebb79c384b702fc17bf4ab beed3115a03fd947f3da1c67670bedc09759ac11de754b6bf52b18c53c6d0472 d9d5e0a847a5bcf292d02a2485ac0f496ca92205d16f0d73cf3752ed5e238fa0 34873eb0c773e3960825546d4f8ef4cd74be1c7704c1ef2a486cec39e7e7e049 ac59ce19d910d1426c4ec031f93da6ed388be13362f920e1ab00391f16ce02af 90c03cd2134fbe9e9c3f0f6cf264d4a80f1ec949b2a06e577089447e63d2afbb fd5f2ac956536f77908dae7237157b4844d3525610196db58df7073604318923 917ea920ecd0bfd81b7087ef57d78efbae4ff910bf5aa1b97609f6afd824cb25 374e8a7e730534f10de553ad74384df7938dd031fe38f9376ad247c9fad3324e 30cf4e0088767be728d4f2cdb35366f463924f544d1da6cc84e796c0e410b2db 679f223e222c0a5281931f436428e57961f6ee3f8281ea3b7248d113aa105f74 91d4ef8058489fb0791cc4f4f0c8e9c1f78bed47804b4597f06acb5ae2eb870c 0dd308ae6550d4ed048c55e393bff079c2d7c80ff2b6557b7dc0a5b34d404223 1555fa7c397bdcd878376021c98b18557c27bbfa395cd7a8dd8b750f0d94783c 6bec12e2cf90563dd0d0e4401a079973fae5a9bc81e6976afdf0242d2e46d9f0 d0c4c20cd8f1d422875476e33f71364f38047b4df7436a7c40f4bff7cd0b2fe9 5f6d027194680da3e7b032915c031ed304c528ba45c39eaeba32f49bd8266d5c 9d4bf39ffb952cf696d088bb163e22f9de637e10dd2fe5b5cfad02e5bfe7d9d8 a3c41f3703322fb59a962b76ed577ac2f8a1d1f168628f97f3fb10e051886c83 83bac27850f3ed8698bf1f8d594ba71aec4d0d70356b6c25887be1a071ff151f 934a05b8fea0a47481ba59beef88294fdb8b4e03cbdb843d15202dd4cf4d26ed 21233075b210b8d4bb20dddc5c13a5bfd86f625c506a434bba886c05fe62f082 7f35fbbfc0768c3a6c815a3223c225494b9dbe6b97c40a7fd598552e59b82d42 db8b46f17c1bd8f0a2a6e4df994218bcd0341d6544c9a0099a4e89fcc92f4419 4720a68c0d4ad8d724239c2ade10868254314820c1918ca4f41ee60ac34ff9dd 119a568b3b0bf65174ca2d71a6d475fe86b5e805bff222ca76b8a8b552f2360b f1b341fc87e456cb82542e72d491cd70bfecc18d43cb07625e299ea33e364251 406bdecab9d613af9f27a52b408dd8410bf706682e6473ac1effb9edb243529b db8b21d8ce959d41013f132379422f4c8e000f986e93dc22cc6d7cef1e2082e7 cb08b035911eb95414b42465182ee2baaabb2f2daecc4f51bfc9fa7909f2fd6b 7ab17cedde0df30f6e1585143d2c295ceb7b6e88b189eaff3656e89da845538a 705da5106ffb9955252414b1df9a76bf3e23cdff2afa2362aeda81674f59f744 38300c451d584a46fcde50feb028d235c25fbff83b1fb7786d41433ae2ecf216 a3297d76c3bdbf20267a3308a88c98570208022feee7b16fd03944959ef6f229 a9cc25954f662147ebc883f77e90ceb3b8460d9234b59f6f59369e6b559b9507 f61c632d9811433ba0b07f2a41fdfa1bc540265dcdc018bed7f755e341322c36 486b3e6f823962095fe727532e2d77d5618d696691dadab45f053997fc617311 e47aaea6c7cfc3f8028a80376f06135f3d29c296d226bf57c35bbfded1498d26 60133431894ffa4ab48ed513e58919691c835f2c4e10271b0296db584286c4ec a0d850ad4d9b6738a35b3c63a1d152b4a35f04302f8ce0b0e6675289654b3ac0 543583754a5fc1098bb83031765f1310a0fdfe4763caa27b1d2b6743871414f1 c996958266548c00033d70c9b825e2e16cfb1a8928882e76c2521a44ff912a9d d71cfbcebebd1d7e050dbb5c98486197c046e20509b887302720cd23cf2f349c 17162e040a76c7ca15e3f89d130ddd6f5acc9ab95d94d297328b73671d5e037c b1f2ae198989fa631dd6a3e62bf96c42ed8ed64c6d209da9397e93bc91d81368 9b52368728028ad1c2ced7cd7dcb1cb24c71c70d003872d7c43dfb1c34bd4630 f4c875f42acb4cda8bc4bd7d03f16977754dad82e375911453d94b124c9773a1 f2ab45b0143fc6108516e3c2f246816ce61203b90c83babd490ea55f4574210e dd619fd3354aed48a0ac9b50fc8105a8f4f09d7d261cde11c3144ef50ae6f8c2 6c9b2d4086940b94fa15bfdce53d01e673001b423b0483b3df26e24fb538fbd2 a3dc1f8b0625ed73d54cf93efcf4952d04128095d03211c07c4741544e7b4c6e c52543469d26a352b69443ad1a72ee9ff85908d6dac9019fa3eb565beea1771f da865965463f535265c85410d3ca8c5a650d81586307e2ca03938ac3b6bf2dcb b6e66aa9cec34813064f9a48ef6614439d659b5492afaea2966e787ba23e2f5f 33fa2c3a6556dfdbe76cc93ee51982a60658d9381b790707c9186423ac7bb0c5 da9f87bafba54786e1a45abbfce6f3cc6904e5078ee5a3e4d6ff9369207e2e88 bbd93d8f841f397eca82e55b49ad1a074f36c4f28a61a6198f60866a817a87f2 27065c3f046fa6aa78f7e315952f754d8e6144693359eca219892bd4a33255ff d672cb030c41db84fdc2b85d954345da1bf2d29a9889b99687ec61ab9e770a76 e2dd107039161f98907e568b56e053fd5e2006e67e59c74f846b6f824ec6baa1 23367a754d9339af2bd4f0316f3926b39e9f5846f2d8029cb5d7a3e07fbdec64 71d4eaf71e3766c8750ecb78649397280de74fd20ddab17ab1b11b80a5f282a2 a32728f0c61f94647a52ffca69569e46aa51441e578ef2819b4ca97b5a02a503 660476dcc25444a2dbbcd65e5711a0cd661d54b9899740914bd44119c43fedfa ef18955528c83e6e3dae60b00d03dc84a7721b2010d5dd05f23a9e4f93be65d0 57934bb32dd8caa2eb63b93ccaa9d3154d7284e6640d53f94de0a9251dbe8406 ef022f22489625019078ba99fd90f57d0003eb5fb01222e2b4837ec07c412513 0ac45470370b50e096f41601cd2ff8a1dca18524001419a1abd60365f82fce31 e687502823099a31653f590d27068e42b3dd128d011090864bce445b103dbca3 9b4a2400e0ad570613e597b0cf80a96a3b70306ae54fa493d840cf274bf86b6b fed32e30111ed14feaa69aa0806095d4edc8272f3a081733282d604a1a6c1818 6012f98e2cc3fc92a1af9498e888217ca946c9b76575a0590b801ad34706dc6a ece304c926c2ca80db31ad0ea968004684d881434d45d4a4af8bd4a38f913263 a40267121a1b2da7fbeb441c287b5994262a7169d8d4275d7819b0c6b79dd913 621c4e6cf6956e569131f67d256348eb5b4722c2567c4a2f0e23a67daef7bb64 d547b34756e50dec42de08b285bd3b2bbdb5346a60d4773e2eaaab61ba38e647 14d68e4afcc217a6bd8150633cb4c60fb25a57b32edd9fc4111dcbc61ee8c412 465fa91613b104dc9fb219c450f1348f7fc2dbc1c1c5b97ed63e116fc47fb343 406ee20e1674a41cbf2499a7ed089e77e51cf9d499078e8664ffbf5f5a4311dd 19b9a61806a25395d9a69c99c6faf04960ae67058d16f43a8188c35d6f1c9368 8f9a937b4bb38b57089dd4db58037daeaa4e2e2b3bd7c36db2dfb9eff503737d 94bb4b9cd206c8b610152aee41cf051916cbb9a74d52f6c944878ff23f3a1c32 3e291c8a292831d1c69b218a6c80e9c183f756f97f5ad9c3b6811b190dc890a1 160e8512cdcd83dfb299754295da70d3eeed2c399e7ab0d48f3a6a78dcecce28 d97c08ac33fd9f1ef7ca7cbcc24c1af010386e2d24a2f31b74cffa006a57c8b4 161def7a0a516c4a1f7509461a80600472c4ab12baf7e408109ae952e5d8eac6 c658d76736e98b919f4fe6d0fe4a6bac5947903cfe037c1531d9767f747b6459 cc780cb91713a1ad47c97a92e2368f4fdd15296995e44eac435564a317241253 9160213065a04568c1ea5054a66f118bffab1c50f9f400ee55f2ba104b0b0016 93201da12a1ae17aa7eacca4f717650ea081cb5c2c947595858eab0a08fe4468 05b1869e9098b832c6c2a63a8a64bff9e1a3cc3a7946f9e00a48807ef41c1a10 01c387c1d1934a00981d3f58d8b302c1a334cdb2d0a1fe1a9a10c37f215c42dc 30b911ea1a558b1acddbe29287a4ce2d13680e400f3876a0905b40e7913ba14d cf005decead540b2eb152df4b236604a7b0f323cbf6bb8c5ee983780acdbbd90 ae11fb0cabd979c0db772dd9714f15472897a57bd9bb186ea3ea34c34c83c44a 06f5a61e13de6aa8d19da5a0714b790242b281645b485f831789122e1ed7cb5d 6a0609838fe9b0b1b1bfbba6e79bcbccf2e7ea35ff5de5278b90b7a4cc523acf f1aeadf1fb519077310781b43666534ae5e2041614df3753ea5a2f4079ee91fc 8450e307504f503288519fd25733e411b6d0ef6f96ee61c9f1c34ecfe2173258 821cea818ab6d111fe73128ef6b11bab0967da4b3aa64d952e1cd6642f66541c 4cd38a5291665de8fa9870ced6c131ce41cd6333ea279a9063c415bf0370a42d b821e44895af85e54ab844cc19d1496b8e20daeec387f5bfbb2b01c07a68312d e7578b07927cbd13497ca05c013c19886d8af38755d480026e735560098fd9e4 e0c26645db0ef5481e17b78c5e48869c039fe66933fc1009a5158c6bdb85c7e8 808d55f57bd44c4c545e59142699ff4918266b724300eea059f666e2d15c3673 0af25d0151bd4b054ba9263178c99279c03977fd9401bfd755141e5b3a0e5b80 e64475b3a4d277b0d24627ebfbc5e97e0b39215e9d5283f3c43195302ae1e09a e5d3fbe47151d8db191d2a93318bc30b7ed483002aa0f29bd599c10c4cfed91b d06ddae131d19f25130c7b95385747de41ab8c30945a6fc798cf6109c3f96f37 0c1f5d54edd0fd3216519944764bc1ef803d4886c4593428bbd8487b704a9873 06fbdf0017ca07ef4bcb4369ef8e9c51eb74f598563ce1a155afc78b183cf253 fdb25d652900643c020ddbfad36f86a94d82dd15e87925a6cf857484f778c952 999ef517e0e713bcbef659f85864d0a7f12cd96f0d336a3c64e180f620a965f3 c19ef32cf307317559283496fa329f55024c502a63417524154bc6bb104a4526 96d477bdb6cc18a4e17dd8843b204b29d4327b957b63bc8a9d4c7561c6bf2731 64d0dc7d8f12987a359a1f817a5197d3de07668c5962543498f96aacb6d68eb6 3de230d5feb06eb5ff8fbdb0da15ee619c74e145b42e2c6e2809f2ecc4b10db1 00012ddc9677d35ddbb737869ec0d4d2f6661da5f1fe3ee0fda733aa15ad6607 fa3c57d0c23f9d12cbdea3da74b94faa9d00de84a44cb823f7776b7d8220ca20 cd3eb0cda5ac151471b9af22dacfd4babeb8b0b4bfdd8e526a66595e11ab6b6e 2b0c49be0a7c40d793196ddf73cbadf6426a191a25708de07403f07dd591d8c6 70cc142ffd15c91a57df47cb8739a1b1f0319346253234dc82a09b70be29b57b 53586d9bb5308440ca010b3cd77ad7d28acb93c2a403db77773dcd6cef54a9a2 8ba16bb85cc15f7001109ba5f902a9b17b08bd3d9f2c820d65336aad40e54f45 ae3b7d4c9b574d5aef6941a3b6181a6495321e866b5c81e473ec898d3908de79 ff2029425877e277d178291370c55c4f894e20046fee343166c1f10cb543c973 4e1135c1055d21ef46200e0d8eda13f9a588d375ce1b7657b6c70db9ee89ee3f 5d455535795952e17e0cfd4658608f317433d56d266d09eaa027ff7f55029fef 458b800835c2a15ea19c8b1cfb19281e1cc39468149ffb0ffc16ccc407b6282c 1495a17d1aaac91d6dec2beb9eca0af2576c095e4553ee5d01fbecae5ff57e0409c14d5898aeb20dc47ab98bb3975af8437d90e9e24af83ea07ef518df965e02318f299c8dcbf7225cc9d7a07d686fef14c95205742025b2d5af1d7f8fd21c09ddd5a5d2f63588dab5458dbe1883fe5fed6574ad15d463fab51c0651f285890925f2b4451468e8ae8a65445bcc9e2a8fa1fcae42a307e0a24614c424fcfe1d09fec41b353ce3d262db89ab1ade7840d177382ef2d27dcd3a8b7720a79104080873d397c3a9c9d4b95bcea2f3a47ff7c599fb695b9a2b52a6b746d43c2987cd0464b940a37aa0c99afa7c166a0a0e32a9825da33036c32f59611afa4abf2b2807ca38a45a6c41d5ab780f297c44ba138e8ac82ba6d865f018b684353211c5b80324488cdb8c2ab5e7306051674e228fc971261b33de4b7c0865643ceb2d225c0e40b1931f33e7f095e32ccb6d5cd5b880f48d320d8e2b5096433693a839cc86075a68758f53a0a24c21f96686c7929c05ca0f5dc95ead391d0e317fef7c6344075ae917a23dddce3bd123f4fd7612659e35fb84af7c1608ffe853e93a8d4ad2029a506a39b81b8c62fe7a0e4a8a93d7aebbe60503da7c10364b87275a715e090b1360e5d8639f5d477c8cb8bad49535b8fc1c41c7c6ae9234174c19356156a20228ca5244cdb56778b3edf97cf2e03df391c8787b55454685060f618a20c43403d625d4ffc8918651da1c58134febce6ea54c3fa7e7333bb963595cc97e029d096c1c4d858f56bd623f29ee0ed6100a4c26473c583aaaa847e1ea21713f310302e0151f8d3747e842763ef51e3d5851d4baa5e80e440954521a3895bbbd9ee20c77f16834fd9f7f6bd280689eade8e5562f652fcf142bbe06d9adf2ee123f9802b39dc3e8b3d4b743dba924867837159b5b09a828cdf7beba2f0d8976d4df0d0f59a3b6387f4347059f48e1887e2ad0728e1ff14b626a9192b87869a3d53e600bb7be0edfede32d6c90e3ffcc4aeaf1f30d95eb94744932852e7fe2416def5d0cc038a410efdd835f5f07ff65e2fc6e7f2261530c18cb1efea615352a0f6ad40c7933d95fa86f505971dc8110fd3248c49cebcc366a6f950acdbc45387513840dfd76018d9ac1eee3ad7ff50e543e643c9b6f2ebe630d3119da2d634ea559770b07ac48bf90219efb0976a7785831024e599e5da740dbfe4a85f779bba3cc060513142a47fe5f2f220a7974316da1fd5afa246e030ebe92fd7d7ff203192f0101856739160ebcc5d36d6c41b1c8c0c33a27f11c896e447de4d9e200c71857d905d1e2dc421a09b71ea862608dee6f8e38b30c4d4982b3814dd471d477a94a190002adac5406f6aacc91e3ead3357de10d43fe8a2ef1e692ec24d6bca04e96500bd9933e18749eacf0e7924b13f8851b20f10eb40a95257341f006dba58a4c09044dffe2ce551f094bc6d1dc6746606704b7ccebc680aab6d54fac3aefce0c660fd590002b3f50b2dd495e330ab3e4ac5104bad439cbdf98383820aa5b38fcfb002e546f10d49416cc2912c9b8a37bc957a962abd56e2ac7a5d1c1d607c2b7bd007b5e6049ae87e1022ca0de23e1f1361bd08fe0a2ba27448dee9d40d5ccbe2d03c42b46008dd4c42860b740e28172e44ca96c7f63d075b403e3fe4aa05a1efc06906d7e5c86c6acc0369a7bfa1768c529e8555cc32ebf327b6819cec1e45f060330c741ddd345f9cd8217f598661c2a4dd60efbf1a5244c518643c5853c86460d7d69636a424d21a5dce8fc5df9a7962af4f81bccd95705b5600306213c98290d6111c62d65864f9e4ef9d706157ec415a47fd168014f90733adfe9aa47f30c0472994ee97dd68697e6aa5d0a254fa946d5ebd511baecf375e2d766524f970c06b35c7fa5b7fb24c15b6479080e12ed741c27dd1d265e35a36a0843e38c08240f603c86deeda31f01ca66a8f49d79a3c76c764b5d7b9931b68a5b852ced223505d36d2405ed53786925427c942e0bb396bd00263a6b3c69b5aa46223b8095660d2c7f81e719a1933b62ff6c3a73b357645193fa6b73a2fa5df5e35e0aa6e17008812c907f8216f8b950d12d7bb5bf59e63a02b670ed6307dbcb114e7b52e43b0cb714c92ad3b5b5d545add28c7c58f1baef1dfda1fdcd99eef200acc249da23084d08a089da68ad0ab2316206782184f9791395888028e50e8e78ea1cdc2f9d02638e55642d15ee604369e5e1c5849a7518985778e091823d8dfc7fde8384c50f9c3fa76eee4463d6efdcf33171aed7f419795650fe221bafa86c56cd2c7dcc090ab81b4a256999e90fdb506f4ec2aa4b30d6dba6a43df787caa05af6aac96b08b6d607bc3b4d9c6364c86d5ae9029fbed5834a2b2c869ff69a152db31c60f208d7ffdbc2a3831a1b07c4a5a00c4bcf81c52ec3f8981203f9be5620cb592ac60df6804e8f7cc1b8fd343926d1b121abb4992a2e6be245ba7e2b3f40259d9c2500424357b7bb4c22307121f46c20191851f1f268ae9fa934c8ace8e9ed9867e70025e840bccdbdc8d6e9b6bac8b6737c2bf584b4e34e96348d0ebfbb0536b1fe0d9289ba88eadd00801f33c9aeb42d96f71fd5fc647db9aff6b670c087a412ae051de0693b6a109375b4f7f7d05c25cbdb2970d153fbbc8d41b87dc84a056cf6076fb6bede947eb6dd4d5c1d6153847658b3d9e693383dc3460637c8dd5c8e8d058e99cc36257b00a4103702de562a49471f780e1fa1c8a9612697ddce45d2020b1433146ef0f88fa1c7e1257a353a1cb6b9f5f5e4bbdeaf99a86e83830481c8093ef85d698effd843a4c092bb0469c16e5e7d33b28c22a05ee4bb9b071870050ed024922e78f5734897c13e8dd69b48b55764b1a790bf7c29b577174ea8bd6e00cb42514918f64fa908ca0da30088ceca10b949dbb13bc4a0ada0ffe323dc5709e0ffacd916c05369e2aae75cd725cbbe5c20eba956f8cf6ff8b06d3ed60700020ec8ed2cf1731c34ceb4006bc5ab2a14a952851478dd20b8c77dad93051ad006f6d4f5a23085ac675b2a4d0b09788d1159b8f9428862bf438147d0c88d11fd0dd3d1cecd1fdc5956dcae6bb55b6523957be341a4f72ad7d2e82324f2af26740f39233ac06ec3e7cff4afd591af455d8d94662d1423816a522800a63170628d0b8fa9b11eab7699c90aeff20b0bc696d1e79cedaa429ba4aa1b1c29b754ee540607a5ac745dee342022b6ca6a09e8f16c22cf43ac7dff76e465407d78d87b440e35955ba7f2dbb12f85c8b56cf4592dce7c0c3b3d4a3dccd5b2cbae022df52c08ed769affb95b9992fbee26fbd292685c6afe4255a51008e396234ba0521b6e00d5615c76d3f43553b2aa488b84cf784de2bfb62d125bd3c10aa62f2684c72b0d4cedd092f215beb2883763663eec18d9adee20a46cb9471680d998173afa1d0c342876b89b84676dc73025e804fc2a90b1bfe1eaaf64d23e91e17a6e250ff600b88ecab5a27072f94a2aac7c51e8c6176d0ceffb1ed97640656a8dce93b0310130c276c95bea4691b0fc2317a2c98991b635f3567efa2476c4f2f67cc348970ded3e90c51df42991a67e8e6af09363d8dd874ff8ee3cb70f74a07e3efd735805c23a10959095917b33f1f88ee5a7ccbde812a2d4204c473692c687f5927f790a1bda6511a185e945a4d7feadf26d4968762c49300f9a269b4cbd19a81610080a6440de20baf06739af348937b69e20cd7aa879b52175d92a9502c2e5cd75110d3e6a3dc0b4022714de00541cb165119b7c8de69868d2a6e05c1a0be2754e1c0606ef77723070872d0e861890cdc07c0d019a1707db8497c060a25aec1d45f70cf1bba72ef9a000f95f25c3ab8f91dbdf7b43e975d03ba3c7ee9b319cd90fb10771da69d4d379b2a988181926ccbde73c3da5ddd26dfb716c8a1cf36475d85c0b6d9e1978b1215aa7d61feff832c416d0c84799248762fdc86c0267250a1d9a0bef0e4d91d2d1fffc541a0645a3d496212bb99745c129fe3fddc7dca443bbba08e57cea42b8de614b0a97b2e26d6de7f2e1f05a78f0190a0c8b2ead07ad693203cdd72b10c2a221fe876fd230eab592b4978e2771ee23e29fa5b1978312c3be047f37f988032118c13f6546786cb7b3739a3579c357136e3da1fcc44f59741a0282e890a02ee177fa2fec0379280585a0a50bb8c0aa4a180cd577e12be58d720343dfa8d464f07d71d67b5af219881e490b46367bf1882ac86b4e9e05f4f00805bb74bc12ac92504d7b0fafb26672b2e07adbb64f61291d7b400557e33f638c04260b8503cf1e979e779f4e9ba61dd0be0371cf41a090777edda476accf5ea3044ba9bc55242bf502b65d59752d2fc912985e858d75e96d73c1edc6eeb7b3f40e79d44a944d21c84bd33638913d561be219f2b2b6367836bb7fbf52125f3d490887e8797fe1bd6ed04fa254bedbb881e907725b16d31c7f1c390ff498c941b20372e359432b0ad84980f6a82fd2aa2242548e48ad7f2bda3156cbe7f5802835019704ec65f419e952a1acb6b97d8e254b776030011cb0db5ada24961ec9e7680d447cade58f40e19f696828b81131993f22613bf30e957300d7a9bedf838cdb0b66386a79102768479f73dcb1694d4b45eb418b4c84bfa43477151d2c3561c8027ec8500663e648385cca1b0e1e1409b9949bd45dc107cd049db193deab8dce0bca6cf69f8b4a8d05699c1cbdc71061b354f3bf3250e619a0a5c86aa7f499e406ab49ab5e976186be9b2b3360a54107cdbc2f9c74fd1764eb51700496cc914b04ebdc231864f3ee408775238538a3512ecd9f035a84895dbfd12640ef51637302256d2c03fc7e4bcc615f6c2083f67885f8fe48f028fb166a5943a83ede54730ff0fec1c898928dcf4761c77f6bc9a13fe157ca276f0b3f51e7df63ce1ad55109e5d0f316caf831e4f8c6f8dc01c48a2c5aa297af6357a77753ed124e6eff8801d67fb85a165e33a05edef145eea8eea64449247c6641e2e558a8bce786fcf00a9a3e34982e5467b13420bec1273cb3c8e385665078fa329a5e7a0402d9f6350c6142a6e142bb0c58e31b37331216466c9c7ef30063bc474bcbf7b94831ad40056e973152b6132313d740f9c9b26668bb1b330b1ac65f962521c58c367f7b63013def095a36ffb4bd9c63fd49ec2f408b5a318d00b72cd659cf23802e6a9a7c0327e72b4b982f4617115fbd3ab6c998a66de8c724f7361af9c6e16289b57dd90554ab62edef8e800cd3b8db81fc2e44a3d94802f3b7042b633663763463e381084f2509833987b590a3efcf12c8d36988d09eb40ac5a71e3e94cc00031054b002df1a9493a7dcb4167dcc0fe3a2ab407edef5f0a8c839fac54a82514a533f880aa5ca87106d56ab653cecfb1da4ebae33c0c5fff7c74e42cd566f0b328d6a2d0d0e1eea1e061ff864343f53fdbe24d2c5e890a8fc8d5a290fb2d39c7b9cbb1708cdd972bfbd9144f30105d39e34e108ab72140accc6794ad20b109a12e170160ac82bdaa52d879a7c5220b327585682e1e423d8d209bc601af3d87d031002190545e9f5c1820488961c946c89b034b6793885cf013150a1da5a4638639686f007003b4a4ec9000f11d2a238866ab28b1f012a7a19f39c36e9344d6b94c173fd021efb996127faafd920bb10a87d3f3e7a0337bd7e32521860e351718e8f2bc5007fe9101f68adccb4962f48e49be909f64b1abba26f33cb5a287f2c22250da30c6b50ac59e26b2940abd032a5b2c44ecdd1ea6d63731a938ebe63f972a104b00278e1e7528ae40477722eab9cd1fed010978d0496f1d557eb87a28d0aa9afdf0a5b16d5dbbe8daf2a12984a89a184a3e5ab51b7ea9c4c9b47b1a2c88acea115094ac4a46fb28b16021e9b51f8636fdb7cbeaed20c7fd774db81d014ac790dec055c33d39b022c3e83304975862ca733e786b8050721fa1515c4b5e942ffd11304928db2f79aab2b56d127ec80c3edbf00bc2d646e4b0bce87cfcd0fe36f7a130fcb93c0b0ca739cd24a145f4c20832ad8050b066f38aaad7d12f23a8eac107e0b0fbb96fbe1a25f8bc6ba5689c3f400090798fbc8f2ae4a66e26f7ed11e3dbe0f88d13334fc012a20f258164ba3b2fdae8e333e9e3e9e30a4b97e44a91e26b405ae7a37b2d1928c52880695774e7191cdb9f01944ffc5404663c183a17bd34b0348b2ce4bb8fc1a7729cae91553d0e9c2240de7b86e31c4dddc661cd7b23a5b0c45f234b24e025a5979f6444f0ac8bc48c08000125a87d97cbeb13b779d2a2d010024eb82859f70493f3a9c62ea043dc5bcdf0df8bcec827e608105d488ef4c06818fdc937d6f00234bfabbf2d6296bfeb93cb62ccc55dd8bd0276889f841e9008886e0d42a5bdab3a122a1a38310d5627027c682d026573b35751983c59c640be2d02a9893ab8e892f6d141ce45e392f0a9f104b76d990ce9d2d7a064e16260a0972b1bb11bd270004a5db2a3522cfc37eecb7bb3f458501f6feb2b3c7dbd508e4bc33b1b29979a57e87cec7c3d90eefaa03ba44c39d4bf702cccc06b2dd040d9043f6296e27873fe660405f720ead1593ca7303f3f5a285b89002fa04168c006e58cadf37aa2a15d8c77ac872f3cb7f2b5e463d6364fc491869b7ccf5833604701b5bab0ed35107a998857f4175b09bf6e53185469cef08cd607c6ecd7df6072c944dbda3dffc865e3f9ead86e5fc866bc5f1e277373dde1fc5287fcef1540120628bf9859ea7c4dfcec847d5b6a858fa9b9dde77e19fad8403c9e200fe010d886f2cdd5d670cc5f8984f6124e02a14d4c85abc1e138a940a53d12b1cce7405edc2052d704bbee6bd65ebba74acb235e4bcea8c8fef6ebb18617e25419056053d28f145883bfe9ea70e00f7495c1122e477a897e28d918db688d993d4cb1603952b35006cdc28cc76da4e0b2db0be355cb9305b7021ac8ae625c63d482c2c0d09319e405dad707cff12126330315a3095d4e1e1b0e53fc43789c0febc43f20d2387df55ac41a4f8f3a6e232b90f8279c3bebbff66a202967f637df85f2a7c0434febc09b7429eb1f66ead08e214b6f972cec0cc9466fa56c18836b975963f019f0bb9d114288ba4f754fd6fef1a86c95010d40d6dae02e2aade57f274af1003ec78a4efdd423ba3d5f7d4021c565767b6e1bf3f4b11c84430ae268ccdbc3e0e0438e0cb483968a113d1827056df47ef2e4ee756b1036a153fe82f943c9f0b0df9fd2292c1710775a5f99b184f3dfd2c0f4fa6427860d23643ee4185a9d647095fcbfccb9dfa45007b6c0c771427074f7ae3ae902028e1d91406d723a5b5540199fc911a833e86b5d4c186551cb5b13d1b812bcbae42fb9af80c520ed258c40d040aab477202173ad59c2773565747f10066d16c109b1bd00809a93dbc629f0df1cb9e53e4de62f1e7cf25274cbcb527b16ca0c7bf5b1ad6ba4bf4b617756708b1c4f8c12930857e0e98f56a40bf04b9d8597dad8358084d25d0ab997304cf0640595d77044b1e845ad276c722e3a5541fa9192fcc2ccbe221359000c4838805af3339d38a42531dbe16812bcc341b1ffb10e09eab4300af25b19c29ad866403f199f64974334eefdd048f27bfcc0c7d7e7486b764f877d0596f4290f3c2090789cf3a8845b162a88754c4808d9579f25a5b30e9ba164c8e1950a0632acc860c991f3d7bc8dff9c33c4b08cba6c95b9381569dd33435292f55458fdd4d8df50b2c0ce047a442959e9cacc0daeb11de15f8d52153dd047762de67d8c52787fa01a026312f4bcf30b698fb3f63cd0cbbf45513c8e51b31817ce55d44985d25d705afc78e0bbc77883f6ab8ff58615ddc7b1b960dff7db6169ead6e698fe1fd0a00512575d234276131c5945e4ab4bf4a2ccd5b93a9f39a52f1dccfeebe3369420ed859035596ac5c2f32b23ac312deb16143a962ea383915215a4079b18eff9d0cd71d99cc569d2bcbbe7555f4aa179c740c6df0d2d3a19a5fd6ba2b2d4dc0cb0f7d2acbc3639f9135689997cce360514cdccf99bbdf5fcfedfac83883043ad104e917ade2e868470092708556c6c981b8d7e35cc9bb01e4a86bb7390a4df8910e48dc323e919419210a1b10af2e2050f2d61e5dd3a8854364a20ceff3d976a50a0fdab44e96e7e0bca9527486e7dcc6062acc52f5369b751391ccd56c531dc201fc06477b6a2a92e47db7793e6bcd45c35540957128fa5da709be032334f96f0855b875a3252b53283168be8391fa7ba24409e5f9f792d48c6672b2fffb4e8b0703108b350d9820eb9a3e1fbdb487efd88e717a7fe7fe2653bbe3908bfa4100067a1e98898f2929192c82914d093e46ddcfbe882e9f7435fd9ecf5e844d3ab508dc3c17d46778f5b34714a54d7d04137b82a2397014c6247fd918454843855c07b3b36f6ab2adf13ea82c0b4ec1939c72f2c2a156f53abb460b29ea3d0d97c30454d9048d0400cc2d2b5740c5b9b0171bddd055ef1e764f4ac0851ccad9181a0155eb031b965d8f6aac9d485805c81bc622930275acffd8d83e77afb83245a2012078bfd8c3c6494a16de715a1cd8cbab061b5a6937a5c68ba4f5ea49109da90adc26033446dd8fd1fde6afb8c9ccc6f45f11ec128637fd8235cf416117536c0eb8d705f14b956f096e09be0d32ff6dc9b338e2fb99a051405c1d72de16248f0d00dc43fc323350cd081c57f621eeab2503002f8b3b95de0383d0056076c6ff0301a4033364f9e9b5bb290e104a6126f77c93d47455bed8a877a6ac1e8436160dfb4e2e3ba9a529b7ad07a9b08ce1394725c5cfbbe585f66c7fafdb80c24e800b2633db83e1e23ba18335b9cb032280fbc5e23259a14fc4e7580075539ccf030e70016b4378b15bd8300121df965c997d119cb9c2d6d66c9c8ebe8853435bd80ca53fdaaebfe8d1676824ae34c28cd559660d63e91879ee1582543eed2313920ebe1f6761b4bf1e1813333f5f1aad9f56074ccf70e0ae205540f725066d9ffb0b5dc34e621eade2210a2a07aac0d3b59979e12af8ba99dcb1fa4efd9f44584400f0b1c2b0bf5843c26b78e55e608d8341cba4f4a79c1f0e977a3aa15b6eaf3f00e1b5b2af8df1b4529dd7ccc6f8e779de689dc94cdb68b61d0edd58fd24ddde00df7b3dc3176ea0ea30d9e76237e9f9fbf2371494b07815f5b9aa32abd79f470d0c518eaf8d846c2163c5d726a02ebde2b1ba16b2d856591b0b8fcc6d4d97a90655a7709e86388456b77cb8afd5c247da7c4089192ddc975990310be050c2f809fc3412c606d4bfb744d7ae09a396fa16523849b2c6a8f6df0b8d5497af3b580cd36a751c519c8c86edce0894139bd9cd99a4026c8913d4d2ac5979e3562ce10117f408219349022ef570df91a8aee2e7c7636a495f24e817dc829e386cae9b04dcd896edb3ef6e14ec8892ca5e4ffbfc12775f1bcf64a19eda9993bebc14c102f35355569b58c2dac2b64711b7c22fd730ab7f28d1942238aaa905e338bd370b6a5031dec2aa3396bed4f18f79b6018c13a08789f68295ee18e88f0f36ba0f0ce6e6339e86b71dff93ba03d1c129898963bf96e6b2949907801c782e62ebd107333d96bd4b04efaa125353ae5f333c9b3a86337ae25762facb505487ac6eb7035aaaed2cfef3d5942aa2eeedd38266d98ad586b518c47dfde265901723088b0e74df795f9aded0b0a4dfd47ffaa07674d4ad08c2feacc2a91cb2dccf0931530f385e94d11e994c0d3fc981f17419bf7ab2f8fc7dd8d110f805382ceef035ac0fc98a123fd65e8d39aefaadd552423a5e727e3fd7f49be18bf861b4bf13c0f0008ad91a9e71a400dfdc39c8a2c0c244c639b352b54e05fad89568c76fa7b8070002149f3ea35ad9b600dc23749d54a1df261936fdaa4959546680b670315f3002a23437773eec32da5a8398b03ccb2cd211551778e983d52c1741f3f138fd2e06084d1e77eb91dbb1e903d27eb415a716ed88188f63eb386c424f8594b6c56205d5f6d085eb00ff13dda93b524b04404de23cc2fd9f3fda77998a87ec4f846b0ed666416bd6a58df4bbfbb466700e36556517e9ef96b5283d70726817746193039e07bfd4f5a294dbbdd37e2be9912683ce882cd5f825ea60c1cfac7c06ee2600e16ecbe4ffe2fef20e87f021c555a96ef51c940ea4e0ad2bc3faa970cc2b3901d52f61cefc08e54b05fa5c38a835dcbf6c353c2473a2b51fc330e67ac9f74f08c294a529b09de9249a2f03ab9cd5524cbb149c8bfd23519b43ba496963c6e40f89e7b2e98c8a78b8263a8b85715c2b941375dc76ec8d0e3bbc139f055d9c2204858cca0fcb77d361903bf800ce021f2895e52ed0d6fa8ccca98ec62252e95d0f16eb8d395cd76fe6f35a5a7d2aa9408454026c0836345c73ab2267aae75e6c002083c2832d3132b94fb665276cbf4aa85f372e1a5ee11173e0e91d273c2ebd03f659ab2d5e2199338bca6b060f8ea7a7107e130e0ec76cdc72ef67f72e3c9e011a3d030aaec57fdec9b632cd9ba94ceef8f09cc568f4909f69d16ed7acff0c06f297a215691128a6d521d2e62db6f6421ebc958acf3d76bdf7b371a8b63a150bcbfc16e0318f0cdba53a79d86c97d21e052687e8a603e6cb813a1161ba260c06cb069bc87fbde9de5f96c07a365d4809560c81fcff560b48bb4c29b9c6d1280129d22346fb04032f9710b8f909eb94f1aaf07b1cc34ec06038f588428d3b9e0764278eeeacc9056f9c99cf498249d234bfd2a9acdb7f75f66130b0273df3cd0f4ea39760a0b2bd9fd7b5577deaab55717956b7a4cbf15e165f6daf13c60d060488f91de29a0f555679799c2982087672fe9bb1c06ed8005aef0942236f06100428a5619753214da994a36b201443c45df4be75fd61a90d52827044f8f09d44034a94334875369cdaa2523b2db3003e77130bff8a9106ab1c6bb0ee03ddf8110811c656ea4af2395da226db4b9f98031b4fb400bb2fcd70ee0463adfa5e5f0609a187a39ac5b5e0cd40fe3f8af9cedd618f3a19278c57b38f11548652fab8cf08d4b821be63e7496b4943bc759518887d58e8c39da88daa9b81fd1c8cb7a6470c57bc7e8cce6846e67f43267de08e3883d339f2faaec38d7befe631e67183fe08a47f79b252d0e7fb1a840f4961bcbdba94388fd09efab4e994742deb9afb4609ac8f073d8edb3f7dfbbdd7b33aa768023ab725feb47057cb8b03cb42cc82aa0bac3ce0bcfcef1320d0b366485c8d85f2b8a7a8df9ed907e3a52d5eed6327950cf0577a7cec21a528293d4c19c4d5e54fb263b71df9dddc1f482d7c7912846405630bfb98ee8ccaa60137a3d2c1dae5707167fedace28b766cf4c06129582f906bc96c3dc36ccf84350472041e707cdacc36e8a8f7fdff69978af42bfa057e30539f5bb699a08245a1033264aa81a6b4dccea86d8b0c307ba4bbfd2553650150678a2d65581f2bf9a86d0a6ef70050eca61918e79e28ee6e95a9487256ca53b0c9e354c23700c8994f91892d4fab6a3070efec633c5ef85ab5c6e41d29627ca0c2c53da0c392803753a3abe64a3018791b8763700300cd289dafbb1d6922a82065464b7c6eb516081375bde5b551f396d603b2c3f239a03384f5897c987c99105e4a9a3dd55eeceffbbe02d4fac880ca57d9d1bc02afa615a15af1fcc2d351005cfa314621a3bec185b01cebb97fdc83709f2edf7be6282b979480d551b65ee06f0fe7baaf96aa64e967509dc14b6d4ae66814a1e984e8fe45b3d4898a7ca0d0166ec9167b4bb129d7a85d3205218ed258906b9efb9d442519b26ff2c3f499a019421283bf78d3a306d1b101049247532f79d1c299c6a8f8740bad34b3db77e0064a7f28955eef72ac5c18873e69367cd45baec5b3bcd243068244ebd03de5609fa262c0570989bd9ab573a4606970b5868266085ddfb60b9b3bba9ca93f76b0ded26d799202b92e1c361ff172b31695cf7f3bd564edebaccaedb5b4a73c9860c8fa9faa422d0f1e2269fb123dd67795fd7eb6490eb383d32245c7ccf6f46900877b6bc0c9eed8ccc2c59a5475009a674664108c15766d4eabeb68cfe5390560e62eed05e8f0e2fc0a89c85fb933bccc749c3aa2c5c067d32e7f3e4241ef77f07ddbf3e111d1aa1a7b460c55d827fb014f86dcf3dffd3157aa6a43287115c8d016e9d5e241e63088701d8d74e72261a4e2a3ba66712458fea4451c236ac6d700132a0ed53f8cc466c142ebceb756ea82e4212ba3da3f239f2b28a29c0fcac7e02cafc6c4429620d91349fe82af4b4049af5096d55a375df1143eaa4a41046ec023b3c3c16be949151e2c56f26d56078b275a80c4f7214b08c014fe5e5f315e00b032855f91241fbf3e15bfe1e0d9c4068d23f3dee8d721b27d1cd01f54984cc0cd74cedb05b4d548d6783e4e5ab84aeb11eab085ab663a6b1ba78ae01baf3b9061c581108bd91006d16ac05bcfb45083cf75041c0f1a57156751876fa9c51260aabb8d6b3a7386d100f6ea8ac475f9e077266c87f687f658774a1d360b1c56200d634cd753b9867fb483decb8878924a5da992de02b57a9dceecc38a0d41e4207e146e2e9a5217ae751d3aee51d8816865acfe27747b8a6b273ecf33bc3720d05c8a59aeb5bd1bec6f877ae2fa61d5ecb922255fcfbd6f1a16d67a6c4ebc88c003b7f8ac386018e7ab8ed762510afa7c064139475809dacfc1d8d350f0de1ff0b14cb72388e57acdafe14eb63efaa6267144907f620ee0072be6cd4a8dd88ef09fe8029e5932bc702481e0cf5f3eeadff15f40f420269f7bd69b05ee89c662100bcb9967599cd4425ec64a97f62784b6e60c6143e710970f578c7666006c91302d528d403ca89bde4c0d6487e13ce3804a341c66c744af7a8909a886b97d6a80fc59c8e4d403e0b44d6a141521f278ba04d5917fa4ae2ce286f541475c712990f8aad46ae403f7940f8aa457482fc2f506d01e32767796833129cf1b7f268470f1371a8aa405d195636855bd3655f01169bfc161f97dbb78b6eb1e81c2b2d410fb07dc88580e97750a99597f10a02d4e3e7d58c70237c769986f23ae1a569630b684329232321b51db1210b28e1742c51cc0f949f947aece94871eae4de1ec2074e04c370428f967f8a1fcc4946ee09345ab7d7583319dc1b8aa3af3e67dfed0148d83919437dcda52d1ea3ab8ca24d1044e851d31ae88e4c9e159e55a3cbbb0f240a6a2f37c22bdf709ecbb8d8da3ac99016f916ed965e19a0d5ec6d8baceb08a7917b8a535c9d6ba473046ac6724e0713fcd79c240e54fec3fcbdf832cdb80f7a6fb40d8b6b82f421bd61120fb9bfa27c2f3ac5b6d2b083ef00a14b762f7007fba21cf09b1cf5f11b85b883cbdbfd296dbb17f01d7eaa47a9e071a7fa0fa8035f64484322c74262107857fb82e671d5034a8bcc333403ba6915b2cdd8e5e202edbefc730ea466de244177f4c90debd3fea70e24954496176bd515590657dd0a78a5b699bccebdc3b3c052c5611af5ad80ebdc5464177b2aa8b4df22f5a8490eb7028252da9c09164605b6e875786b91e4f6dfecd398827c52e250ebc54c4a0c41b6469d15a9d995f37ab9fedaf46d03ff7d8e2825f08d01ce7a9f019d59040a97842678433dd8c57a6986dd8c0d8f875cb56986bb7df3a52db0f384f22f790cab945ccb719f96969a92bcc9a9c0f494e41fec08beab2b8fd9a3344b36aa270a73d3fac0f899e65b055d95cad63c31cef6159adc9bcab1631a717e47befe8202e24f5e4934d1fd900df882653527cfabba677cb02ef355d379c4925b38d8910818f8a283a776824995e480e5804aee1614dbfc4e8d981d9c1d6c74a1f58a710f53d51cc1f93f483236acf84ee23a05a9eeafa5f602728102cc287d6b9069fa0f483d8b043390c2f91261aea7463809ecdfd84c306336349d4b1da5149c44820441103c15402aaab53d8b3def7170fb14ff6fe5c89371280b5eee22e754a27a0e2b11d2ad97de3a59f2a06ef0dfeaf345ad06ab92e110dc3495a4b55a3ceffd091b9ccc7167222f113530e1425468cd8bfa78f158d7e215fe7e8bcba268c13608d7e921a66c75c322e27fdcd9313d1f5ffee45113660269b8d3fd11a8310fae003a43dd725c54ff0621a1662ce09ae45cc450b00eab53e47a83da75bf907df60935a3e6d277aadb18a5697a416b927306b57c7e8d53e4cbe2fd134ab7da59120af22ea6f68a1d8e0ab694b3e3f43b4ac4c291f03835c12ae926c32e1ac5d2650b3667149de417915b423d93d8e24b8702028868ba1e3cb377ad78184a6005330a3badc1165216dbbbd2f6f09715328c0d986b0e2b1f47430698ca2f5433df38011b6c3a5ec5cbd7c72b7366b134cc8a1d142a5db86598ea16559569303f98860c9e75a4a851c757edd5238a3797327ed10e22246e47a5d7df8773396e078b6906b7b5a1a8a530308d6fd07dba601e7f914b7b240a48a02da317fe58307d80b6048a24138b0d1ef20a87bdde2892ef712ddf9782491986adf09854804b7d737309b326b2669437067556aa5ba815b0208f7e95cf7d88330e9093aa06fe561c3800ee2735f2740dde53e2ce1c8f51b4163800bce95adb01b7aebdb74fbfd69ca0054c60aceaaf34daf63058fef39500318ff0be445da180cbab343e3a1e3a8b390d9940d044136dac8fbf56487925d4298c9784c19b259e4e007f593ffc4df120001ba4a03075435336362ce96aa6fe747aaad5ab3f18bc78fe4c41e6c99782ca09282303dbb2b1a9e9d9f9d88e1dd5b067e81ab6003d3fc130c20aa3acab9c1401aef46c68e723f6da8a04ad746ebabc90063e0d58c5d4e2c1cf89727d760fe007742b1d89dfc7cf1ffa707798ab332c78396794ed3439964c2daf91db5208850cfe597d90068dc543ef0e0a2d4fcfd0644b0080b9d7c755f6cdd0d2c96f5fc40bfb468d12c05830fc6896447d36f51def1b38ce3fd3fbeb69ff6ffe3ab969380e131c8ea48a14ad356e3dd0069abf3b84a0e2be73c0e4516d27b02d5a8e7412014543db894fd442d02b544b4a3b00ba08079be8319b0f8c1958c782fd7aa5570db3bfd207c30689a506f318b4b4621aecbf38fe93d0b6fd9f2fdf5dbd6032ac001ecc6ed3f9e0ab4e2aa1c8c0cee845ca456d2865f9fec58a9f9d1606c2bbb90c421072e85fc780f356a1e56b3e01b66393aeca7d72a06d171ea2f0b0d95ef009ccf6f896443941630837b1b87727b36a246c65917592d517f492ff1b1dbc34095f1fbe03dbec3d3a05ed0ceeca6a9c128324e7c4d6cb67d7021fc2a9bcc0590c8e46f854e2ac8baaa98e993a28ed6ca3b412bfad8f27e54ab72a5499bc878c0fb650270de4dc8e8eecf3226bcea4d9561dca8d518649945f70ebef6610c54601360e210d75c3377c7a4556a4b4482af6c242648fb98d98f199830444a9b25b0b54d718669446b702475ed6f762c82660233246225611dc6bb6c69c3e3d4b9d0ee8c3d969d56f85cc203b10678e1a557f0f4595c7008f13a6f6083afbc7c3360bcc28e0648f0f47927d9208274250b7bc4a0343cfb3277a42e7bc17f08b491f099e96e70e3dbd06e4bedcb20252c5f28075fcbebb4dbe4d69cf69d3ff1f5f7d0efde3b1a83cc289b639b712c6a972a95b15109242e12cf18b828ae1a143997402e6d22de05a3fcac8908d0b2500578e0fb5ddb7b613247311faeb78bb1dcf3701f20ed0ad795ebf9d3b9ab21e8881fa571236fda6f617902182bb8c0d96a65206cbbda0b3d03f3bb91a8a625e64ba1e241c9a1640642d19981cdff922a15c2f043d1096405661f61026469ba1081cacc5edc43d3d342a85bdc019f0904ab73701aa331351363cff86e323bddd804a640022650d51d0cfb669fa822ec1c8209e023901bc918db162304fb384c70ca492e4b686f0ea6852fec012f7d486c6c8670db4b7c1491397f62fb0047df36b872b0279865fcb084f14238d50df1c999bd9044ae93a9df4b61dde0ab237f13fea7d28596f8347ca1de7867a60cc2dd5a761099ae101212b77259424f5f48a6308ed1ba075388887e18e335452593fab089408f92cb63c7865215b6cb8ec0591ca655510dc2a7c3e6e03d5dc1a47248fa5ad0d72d22d5fe3854cec5608998ddeb8fc2f2646bb281f201077099a11a5c7e92e0fe669496dabd5d62db2c2f43efbcd1981142e5c4bebfa5103eec2f014d0946f0abf499202d19beac0bde6d0fe365c4d1c0b28a30b4d257edf49f4b502227bcf0b573e581a8ef9264fdb4ac8c50717317d1aab16be6fdb68866bf39a9669a159064a3710c202a7e6ca71dc5d27c4fa02eb86c95d0c48d49356c817a2f62332bf0002af7b759186cc0e14d7986a028b3148856d6756642d4b63a270a8441a99c10d64e285271e4213a88a5a3a0a69282c18fabb6b43c0db5a2c5a92cd5c99da3504d43294d45e2bd4589b4dc37002b91b012b04e6d15ebb20212a7c96cba26f1d0beb4561dc6cfb0f41b1dfe5d3deb7317fdcf1d2d7c80699208918def8d89cc40db6794faaeb196ef4488ba0d9c61ce430edb7d4582a2ff0ef63c4055ed41d520ea2ca636090425287ca00cb539c676a0b6d1ad022050f99b04247efaff84f910b7c5265967ad54378a5ddcd06a4775533f3f103f9349d216157a69905f2e13f03ba07e39795b2b5d78f199104cf759d8a77d1344dd47b3fdf565bfd1679c847022b0b64193a75640183b3d62a3a685b289020dee367170b7f33778d02e91b3f0ba3fd924a80b5d0b4b92f571a4c047e299cfbe71f6fa98912879332815fb8de0f742bc5c8bf67337dbe0d3e61c79aa962ea6aa53434d9baa6bd672e5d96875c05fa8824a0b0f6e3241ce42d4e36257535172dbfc7b9f3b9d530b0b0d7517d50022789f19f07b2b412dfc4bb06a02591bfdd549f70165c4ef6e9748ab86a720902f87cf7e4164ca415a2b34d30c420ce458de1fe0bd15ae0d7302c2857bff2cf0ea1c2dc42595ba0d20309bc5b0f62e7e101cbb89d1decb058a9f1ba11df943307f3de7ac7f1d9b347fdb52159d346e65a6d96351ad9ed986fb87673590630930efcf704ac078af529198ef8d6c316ba165b3e1ca794ff132079fdbc9744f5250495c3e0b2e73ce49f6c22d1fea39b60495d9550dd67ef6af9db17394750c52b0a7875af5a283d303cdcae70dea13989055f87b9ae45b3e1506c8e8860f455010d649c06f9fca55d92e9b892bcf39189db83df8b2470378865d35cfa67486e9402600615cb727a8415cef2d144bd9f7b19468bf32830a09ba2cf2831a024d4ab00 true -check_ring_signature 7528542aa7824637547fd7677fa8f4ef4d5e8e128756b98189943d03f5c6be80 af1dd40203f4e8363e0791303fd5e3f0a2885b7c805ef80675e3b7a2cf7e30f2 2 80fb6f95f962e1f75cee05d34639ead6bc0057ebb44fedc53286df90f8727f67 6c325cd3e060cd3e2bff6c2589261bcd58cf50daf27dc352d7b698d0d7d4b3d4 5d440152d7d497980039e79024153b4d9c41f32fd692a9621634644d131d2703a326785d1e2505d0802ede93237607d66bb696edaff4cbbdd8d1116e79900c047cc678b1c6f2347ab28c9ea5d89cbd9d349a461a05f206c4e71cfc412133f408e827ddc49960f114738926d7921d127f71f78c14ce2e033a5c5a5528acaf1a05 true -check_ring_signature 13a94a5f9dcf71665a122be77066e7bddf5d150e74d66f2a516cfa0cd94d712d 181d6219cfc4db3d10adb0768a63575b38b9e5b6f8115d32edb71bf5b3a24f1e 3 d67f6f68c86bffbd4e00d2c416ee56972dff513c6bcf19bc3218e7d1495ab2c1 8ae882ba02301fb18834735e22a6343fb72a69c86d339c249a7ab031920f9ec4 ca26e2e2339cc22de972a1b9367900f64b830a3daf0d83bfd0fe1c2576ebe81a 4c0c0707f2b25808ded8b7239c4e449b9e83fc9c21647ec5b6fa56ce59dbf60f067af6d60192751177a58555da5edb0ad5f691c74076b6844fbfc80b1b63660fa0f94f640c96b9dd7db295fa844df435f3a9a13582c0116ab5c2a92d766ae806f4e0fb20d5dd157727700b77319d010a0974eaadc1d2c4f47136d76c9eb5570c602fc00631405942280c86b38de5da4b7c4b81c06134d0c71e2cf3d3003f5404d1e45491ba005bcc61414c478726504b9ee8ca21b7697b88af9d904a76d1b203 false -check_ring_signature 95ca51a8c2f7a7d8507f350b278034f2f56adeab3825e02fcaaa23686df643ff ceafae2249d93b9d94a2d4736049885905722472637f456ff570fa2777db95b3 219 03eb19c00a3c932be3d55444817b7795d25f1a2ff9734c07b4cd765f92c3943f df2cb1c4b23cb5d1cc05dc6cd01fe597fe695d6060a44d7df94667a8ddd7d0ed 4e3a3e1465381fc7dcd3efedc75e55469a6375b0494304827150f9f41d3ac238 637041c8b379570963a8f0b0486a8d316ba8b1e7fd48bfd6ec08920e0ac2d053 213048b6f466468175793ab8e93128dd8f3a30d7ab3905f6c2c47eadf9b6c7be 0a6640e8d2e2238de9d546089e2dc14cc5b398e69aefe29bd8533805f2de8326 68c16cb7fbe2179c0f6290cee73335c31196788cad91716133870206ab0e3d03 ec3c0d8caa7542a1ee1c815ef90062b13e78337eb17c5633cf4832d69af0fe65 cb0af78a1384be960210e93ca46c2c20427df7a1dacc4096fee9f09130ddfdd1 c9e0dbfdc497a16cb9f67e4b7b21ecb4f5d3652321b5b302a3b6b0cc5817ea70 683cf7837d93ec85f2ff9142d85cada1902591b98b67d29a1e7115d50537f31a 2aa05aa253c495ef70ade53bd94094685c7a82ab41c9e51d8514279ea8280e1a 1f94924a7e4ca36c2c0233c54d8194cf89c7231d54593195e8b753ad0fdc1a55 d0a03b49426e897c2f1444ca0539192b8b2a0c34b88d5e0b68fdab20d1495055 4341d0378b1855fdb9c6e9bc4fe85a2299786cef2565e127350c53d6c8303ab2 ddbf6488fe2de45cdb47426426f60767cc4e808cbf738d327bf2432c64492562 24a23238531412d373452e572ee12f9a0e4f3f5ced2d25a4b657926b46fec246 68e1f4f1c6edb0ed2e25c3a9931a062aad1693010b78a072fc6e2690442da41f a6e670b656e52850686d4ae0c6879e8183156ad58da1373dc1cfb30b522095e7 cd1eae7993d5b2a941eb75e575b124ebd6d977392ab97738e5cb82d3a40a73e2 ada00ddd058453530457b1a5da9a172432989f49cc43c4b354ae72c3cc4c27a0 302ebcd8163eea85179ce8765eae761bc9d2dc1435192064e60d40ae5ac2cba0 0bc23a4eaa22e0a84c86258efb75dbc3cad65a78d90cc290afd3d6caa0d78914 a668384ec6c255f37d5572ce41b9c412e083181a49c66c2a202d5e24f35238ed 56cd48651c4d178a19ee2c78144a11469e8b16acddfd6d85d0dbefdfa414b1cf e25b988551af890918008f17913ac2f6bffe04eb8646a5989a3353c664f17468 8ee2e4c471eea436300c0038251c92db61f9ce6c31a06ad2e90f6c12040b466f bdbb5b04ad19e83ba712e08f5842a41b05f5d76caf4272d833efea78f8e641ab 9d7702a66a60c546a32b2b91715908feca0a03f8e1fb591bbaaa721feaed9f0c a81912dde709db48912ca617716b647c386076374877d06464109eee5ad7fbca 00ba33f7d899b36b252ac7be6142e03cc369739adffe4025db800d133e1caa68 28e0ea5174dba8419cab525308cc1afb6724a32a4cba7208ac42b1adfd69ff33 c395606fd6ac9b66889e65b24d47ea435612529197a40437330f8c15f8d9538d 32df64984ff3b0f4cc10173814062709115ffd6be255d09a6b42a63b31faf6a8 14e70c55d669a18c6128e135b5c4926182986508efe952d39da184e2d47d1432 54bd48b7c386cfa27a807b4f92b8b76407e642c6feb2e77003a2bcf2aa7e83f0 32e949d21b5318f240a411c509f660f99daac32ec3527f6e0fcc095c7d5ee22d 99f1acdff2fb9c293eedf9edb9ec4e5b5ac9621586979b899531649a900a5778 de929eb8a5a0bc25e6436adc23f9f755ceab238d91ef5521c1bf42ca7f65dd10 afa63fde32a434773fdfdff8da439173dc17502b791e05e0b70d1cc909e0e95e a2c4de43d6b260c421bdba67edeb2144b7da44604e713e7834cafb2bf24e2262 c99e3beccc255df375eff9b85586d8a53c9064f8f30c43a14767638e2cfa252a 6ee27c4c81f89a8d81368a031cf90f1a7da150735e5f3ca41fe68d30a518c9c1 98dc00c63fe88c0a88f0628a5772e01e41ac244c2870c0866c2f8eea717d70bf e0afbfb7ecf8f6437997ecb04112c69034a75bd7bd46dc00ca18290f724109e0 f3e4e8ccad0479801df279c4aa07ff5ad535223e1f27f335ab6d3e2fe3320bfe 1ffebcb926b032f601563dd3d77895b06e08474e89d9c88f6c31d66d49e792a3 e675a29f33796a91d6975586b65257bbb0dbb7a7c7be10fb7b1901e362820107 9e1aa70d2a524b94da0558e6e696d05a914cbd14ebb98b807a23cbe6c3387ca8 f8a28f4b8574006c909d56240921eb00331cbb4a1046c9021932f3e80ac81101 30aab1e281939abeaff88b9af95d49a30b84bb0314d2ee4aa647ecce7ce88c0a 363b1114b4dfc7c38b13e6427aef8b690cadfbc3e9a4ff1e2e4ddddfb322a82c 487a5ac1474540102bdef44937bfd8b1e24dfbfbc533d32846be47c975b98496 41cc2d40fbcc7aab975ffe2d5c063442881ab1338cb21d0302dc84783c16f396 73600b333ebfc36416f6479e68aadae46e9f29ce8149a6f73d89762e18408db5 f098fcc85d066c7eab87d5bf78e47f12854d7e4ec839e6a7d8534bf307c2d6ac 85020dbdabf80f5e90860cda0fa2e2828862b24ae74a73b6e1c697987ad2aece f76e7e95bd644c025090183bb394d2c0a07b31f8ffa7c3ce83114ce99b0f4439 2757e5ed33d49531d5d6dcf9a66ac0ef1a66bda73715be3e96179fcc3920a94c f13d4c756575a6e14d20d28385619a0c5ac0e2f93931b4895ec513061ce75be6 4923f01962aa02ade489b9339a533548b028d25d2e60349c2606714ba283366b f54b5099b690206d4bdab5bb308109f332c49d83ee80e37c8e357944631f50b1 b78963bee1df83fc7fc61a4589b0a70738e9161d5c4f01f337a0552203b81fb7 1d85cee2e2e765b60da11e1e1d29c36307eaafc940a62a73ce87fce2a9ae381b 17c7c2a840d3263969429e0dbf54f8d0a75eed30cc13ab033c791814e6cfcb29 b1d05204aed42621d64dde1d15e83af274a6606fb300a0d68fcfe8fe6c1bfeff 3ba7c05c49112d84e3a188a03ece3ca6c71c3987154f833471730d6d5a60d8a8 95f777d7c97daf6943f73542541d2f037ac5900fc27d83d8d0c65b9737427751 baadf7129d0eeb7ea1658d4318ec717a7321af52a334d3c98e1573411924c1b1 dc9ee765b51cf38cbcd329234557f10e12c394bf6c0cd1ae451fa867e61e1fd4 3097fe6944640efc4416df2afa5f17925d4cce8b187386db0d45cf906eafc0a4 21e785f56db22ce09a84dbba30e40ac589689f5fe8641a1586acc61e3fd5120e b1b394c6fe7d3af18ed38d4b4601f32c924796b465f6b4f7a1350717f507ba4d e53ad87496327ca77e71e77302fa8c1885bbc9a9dbe34a7891a2aa1c53bd751c 8408ed2dc04a0f001be4909879ead159943aaee81b77c38d91ed7f05a3b18057 46869db5ff04e021dc7c3a15fe9c88c5edc4936d5d4a330b61aea9408c2f7a74 3335220c3a0c97116b6edac1e77b318495ce9d280eab1d3d102418b3d7fb4b90 470cdefe426398fba392e5834889a4be3b6570aba54ea6f2acd1c67d47570130 70f55ec1ff84ea2519953b2e2f9c34a35d2e052d066be2db038cbb5783ff465e 6dac8b7aeccc9ee3c57ec1abb4d0858ecbd6782a6dd1fde36e4b1492f9f17b2a c6972918fdc92d9197d8c795fb862392cbf942568175ff2242b4fd4c365f0d44 46b3a96137abb2b64f4d305a7527e2fe87fac048123f7989ede4b4bf09cea4b3 09ded486f7616425f6029944ae7507e5ee0319f91dc19eb0901bae46c94cb79d c020f757e8240c0cad34f737b3b317152ea9f7e61ef78b37519a9306df7dce8a 2a85f11e10afdacb1470e85d69c488012617b9ba26ba17ab9a995451c5dae9bc 895a1e62823137808d542d5a1375fb598c3e8d57e734647e553b4b8e453bd7f4 7f9548fb1b908a49c3b155718c1990b0858f9b88d68a8fd96dea1202632d6d14 19bf2cec93ffb4dc3f9f277f79d19f682d04aa8121c59926f30f280dc7a56f12 11374284b8a772a4f84915e0afc191cfbf2c8cb238ec3dcf405e930629b037ab 456cb5d4809fa2f6bd5b81f1b8b8e4236c3fd170c3b132dbf8f2aba7510bf5ef 885a7457048763eb2bcf705ab97b4f4ba33f56e9c4cb97707acb0bf83a1273b1 f7d29212f509e49b2ac9b62ef7e2a92b7331a4cf25dc36f366aebd90343f79c1 7d8ff28f8938a2f0ef6daa57a2e0c0fa1c5f3ad52b8886cdbb14c5350ed555d9 865857f782ff2919624c8110982c9471e5e322c026f31a1f374eff172e1ff34d 39df71ee4922c10317cacad9c73d24ee2cf7e3e5c5884a34324ebb603e94c1ce 820b59a51cc6181bf11588d73dbf5346fced0ce16cb39ba30d2538e89de9c2c9 dbbaaad635f89d33301d5557ab5ed84531f620377cdfb0f578612294d4071b73 7ce2c61dc029c099e50b7fd419d4e9d092df54a005c508a007e9bc5d8f5ee53c c8016c58b5615bfe5be8419a3a5a65308fc2de1aac8e15e8e68305c9c3fe5d21 6f34c89fcbeb1380c754a3f511d98c0043e3250706023c5da756eb57e205f480 e2d2ba29e8b53d97a837305d91cf94f0a7a5a6a83331355cf3dc3621805b09d2 e82e49fd7699b2cd21f2925c8aa9c27d2892d0a32eee8d9c3a019ad14828118d 3950ec75b260e7d0bf7d20cd8ef02b733bea743d46d0a457f368d84b85970a8d 8afde111cbbe8b0e35ae1ba92fe072b625f21cbce2afab22dbbe435843328572 dded27946a7971cdceb4aea0691c359c2cb45dd9e70fd0ccb240f1934e9b0905 f4e501fc7b09111ad194d28f7c5685499e110e3d1efbea6f79c9698851de08ba ba273ff57050787360c331e7449945749cf9a2767ed50f8c3572b3c7dfdc9039 d3014f3f23ab88b1b83dda241f19049bd80ca125f11be9d67cbd5b8c5e777add 75ec0373d85c40a35ad4b2b87ff606284132574ef180c150ef4f9253e6aa1938 709ed35b6dc2947658f57b6434a631ccd73fa3e44ef1d422b84f3158706624b4 c627c60939b2b417f710a73ecc1f64fdf71858e7f15362099525b29ce55b84f4 9458a1284b3aef5efbe954d1efdbbb8d24da3c90098f9563c042e6d162bec7b2 963c48e7a47deb5fa2e1deff27d93f677c6b073d88463bb96c5934b1344e7fe8 3a438da556d7fc45324eb0501131465f9f30ab0be293012aeecbd5835bfae2ff 09c20d8b72f259bc426bfedea0125e6443b66cb98c62653d2fe05be84a5de053 23faaad2d100e81312e3b2ee728edc737aeff37604ec3ade241cac7e29a9cd0f d1ff1eb285561f6c0629e1cec5b2c3650a8f241d79c29407db59d55c24962a34 1c9cfe0aae880aee76281649d84dec6a4bde2984fbe2f5c412774229d7974684 89f82cfb0c8acd7f439fe37b1a669274712df02f95b32974ee2b3ec1a36b241e b3addd1fd263321226625aede3953ba4672aaba600eb745e2ff8652766a9d557 cca17a7bac9a7564be1242b3ff56aca66ee7bfe753464cdee24e34460ac2af4a d700143ecf31e30121d9ed0728ea8b91135c22505c9df10fc180c8f4d02744dd 3d8c3a5610ec6645b8f2b4228cb9c571626ec0ee8516bd844ba5ac2a26d34064 91f469cbfe33b4b448a9028b2e769dd1c6b9ea26b468295ac41c1ca9157a968f 086d215240dbb593eab64889ea7848be2158392a6d6dc7bb5aa4505a35a0d21d 51c5068b1558ce07082e0bf22191d8213bb0d6af9272f3ebc9cd63f46c231478 8bc8f7c023be50b4e08f4a9aecd510be8cf0acb0407ea101c38b1b18be5cae0a 6022aabefe39589799c520c0507885021d99a0be75df22c7e11a6217c8060499 83a167f2590973883011ef4d1707744ea0a7658dbf2a8e39987d0bb22ffbb830 751349eaae8dab1075a803d185080561d470931610703d12824c23cf26b4f0de e42dcdffbc2477cfc5dd7b3164d4195d315fc2c8801958ad99ee4f8de7e3544c 11042002c8105506ab384e2fafae0e0d802621c941cf34340f4a7034f053080e d920842ee6d8d8458e5c0dd97dab75fb454f7b240e3320b4cff3fde7bfac5dbb f603c94ee2073dab38f127d914a757655747d0cb2ac71c2a411f01f2f96affb8 7ab3492481b02f5826d36fa9d74698b0d386c77e9ad8c3924ffb47b4d4066929 4bf4aabb164c2f1e2562d9f17939515f4e7ad896ffc051f37a6b2fb049aafd10 e34c7345b8a3fc0f73acd27914bc855ec820569698c29f55f261659afc5f256c 54f651a46f7d6b6fa9b783f9f488dc77a89b81a222d09146ee454d4f6e1d5088 a15da4d114fc29fb03269edfb174070e4dc0a1c67900c8fbd3e42c054771b2c1 be98c91c95fdec9ca71f21ee39504041b5c0e19d4151e5614a94affb3b22eca4 f251da3e7e51a60dc31a529f529cd84acf2c2c098a7c04b6a1ce78dfc6057a52 5fb19d9e62a587fc436eb173f8b9b48ad4f2f782445fe91bf63b36a0fe3bbe81 5b12daa77f40cd07bf39aeb932cc98fb6f862d0cea8ccfd636d25d111579c916 5da9849c4c5d522884be6e0d7033962bd963bfcfadf12630671d9ac15dc05456 1f2a643fb2578ebc68645d1e33b40460cabb9a4b05dba5c02af2596e9b52b257 5be3486529c36e80168814fc8284d596c22bb30a3b2d0f2c933532a0a557d4d1 e5d3a952a5f3f2a48b57c655b05b1743714ff679508a17a8951013ab22e6a5ff 75bb450728aea098d776fd54b444b1a8ae895795800d8caa5c4a8bf8a878fcae 842dc6e9d7ebdb06f23e8b6dea7526094a417cb88ae6a3f676f8065b5e47a484 05943fe4cf943630833310680bb4559f09b3da461a18363496a73b32c96568c4 32c9aa7fd6376c0a54aeb243ce86e450a0afad4d9f0902635ae688de1bf765a0 10b2678fe887952ae6217207990fc57d0cf848d16809c842d38f54d6d4068b57 1e3ec341863600457cd1754e3bbf55365082d01162bcd5eafe30a94aad484063 9ca0ce25d38d5e8c864fda98af161b331ee7e726805adc37fed5a09e712515ee ec535d8f19dd2ad5da907d62b4e027ebc8ae2562022c5ad66054139535de25c3 13d117598797092604eb2486a00423dde8cd92e9940fbb8a602e3aedc7835be7 56dfb9a590dc5b90c19f0a008f7b70d5db9f74aef1e70a3c512d17a3b4612185 47307b00a7de3bf85ca83a694ee119586cd37d02c0def54f9fc1b49c9c8681e5 3f62be9cb66a1c2a3019d6f2b7fd9b33c73bad04e5da97cf9a037552b6cd5309 44aa640b56afb7aa75a5858c0468c45a59ae23942b205a6a687144d206153b99 8a5a92934d4865f153b2094072dd5e81be18f4455fdb0ccf3c735b1993c78116 bf290616588493f58dd66272f60110fd8f6f026a7754986eefd5018a993d0bf1 5247cf73cc126d2942169872d9229cecb00aa74db3ac0831270fd3533324d86e 7a5505223bc16fb01b99055ac918f9c3a44edeaa5db7ce9f4fb06891ad0213db a4958dac8e7b8be9e109eb75bbdfb0605ae7c88f56213d9d2643c748168b00cd 90f6c3b11e9ce6d7ecfabf6c731b231cc14ea5f798e0179c7af29a244795d0cc 5510aa566e3522b877170c053ac70316f1fd5fa5279388c12419966a91a0a05a 41fddb78a63e5a0dc9cf962880dfce34d47b830ae876230bda2420ccf8198733 4b4f25d2c0f0edf95ea1d735fbb0ced451ce544a37199ec662c302c7ed5a00fe 1fe62404f79a38e5e5afdd768cb937387e88eba91be8cb6e3dc8921541eaf7b8 6dcd67fcbc2ea11297d4194c280d82c88ba7f639952e395808e0d71138b23faa 352c42035527381e7d60ae2c6796e8a45c35bee328231c4e46e172bb35f0de88 4e38dd2a4b15ff26b6d4723ada304a408bfe5d4ead563a76f572055792be6ff1 caba8ed1d4203efe406577148edd7c1d0e5be72fc2bbdc6c38c3aebd1a2d4af3 732987a9350d9a36b3afa79c53b9396a09eb307ed158cbce3b69384947ed6ee3 a9fabb52435b4c087acf3ecf1383448063b295ee2693089ab2bc2d91729c85f3 45c9899c70db52043c14d263a222657b9fd7bbef5ca85242c8ddc67448b715fd 22d5553e285760591f035fe34c15d6d5c5290287bab8ac292ac9c709890d1221 d71e71669629150d2180c09536356e793d5806d717d9c5d098f070747bd2414b 14efebfe4fea2d2b88deebb976710e03c7bd2720c17595a273f788c1597e96ff 05cb3cd6187c3a0d3900cf6a645c8fafcef3e9387b23e81564548184719c2c67 823b4ad3029a8102a6697106f318b7375ad05660d108b7ae233bc2dbf737f467 fd2df33217a23847167000be4ff8872aa97b3289456893bf6b101fa138ca6115 1ecdd78c0bd2e9e7770f4879c67db2d5d9b4161bacfb28911ffe5face0881e7c bf93da2df96c9d400c59eabf127de18e8704d96633c99bf4835fe5179a65efa6 00e9898a9a4e891f6ae2532b73a17bbbec9c2605a5e6eaf2c983ddb060bc6964 df3a9df04de5842077e8870d37a7604ba939d61338f68260a7163f8e4b2cc51e 84156db72eebeb1247a3c88407c3274a2b9b960fed88597addc85ed3790bd62a 9a4431f3c571883f89cd3b2a9313679b1f306b03a0fe7abe59b88bf698756a09 f81f594397eda1bd7264daba576182c45d3bfca139decc85e533bc230e7fc62f f0a4b4e8b96d49a68132976e804fb58303cca84814054a9b3391ea661047d36e a2f8d5a5097f1b43c787cf591823e0695ada9296d541070559a3ea88a9c5376a 82382ad13257b3edfebee63420abf8fc74517bddaf63f9b7bcc0aee851f9a138 12af1d69eb72023624af1f1738258a5307ca6267cbe6e3fe5e851f80f77faf9b d769ca9df5e6830ec8ad88c4d728796c6a6a702cd803c48cb96bf892f16787a6 5be0f9d1144327e53c64efc559f4752dce19c54d8157094bd2a4f66565c05bb2 e6fd04cba17970e55be92339a0a72a9a711f591e1fdfceca609ed3cd7069551f c1d955ee0723d55ee486e634bd2cf3893e0ec78f2cadd77d0e1d95eb133ba697 b40614f28b1de4720bac79078550655412b53eb33a5d23544dee39a59fec04a0 bb99fd768da20c7e1a4587e3926ca390c8186443c9ff0b2dcaddce9c28c1c670 8dd4b85633b0e9c65c861d3913eda5a9f43c9b2d1e6920930013e1a83f66f04e e49f99bf794f1db122109606d1c4b0c250078fa5611eb1f9882a733119cf6bb9 2263b7813ce892d76bad7d82c4ad12eaa07128b438d239737a36526f0dc7bc0a 6b2e8beec380bd9b3835fc87f072080cf68f83bb5d1da0049edbafc633639f7b 4fc4f5e9b254e044a0e032c2398d7ba334f0c2a88f4d2b44b27c3eaeaccd1b98 9d0d4224ff08751ec15935406b2b22c2539ee8f3126394b86c32e332ca92de37 c5bec5ce6acae2e9c32949517e79f61e3584d76b4ff58dc99fb0ac671ca23610 1fdc3daecf4575949b34a72d89a8288eb95dd8e5c3ec403f9566ad75f2b1804d 18e99baa8fec0e0fbe782bfa3794996d029cd97b61268a2c5037bfd010fac6fa b2886c6266f8995749bcee50e8ad9c068695533c4437aa9366924cb43d8ae82e 838cd1d6ac6f78408b8e69a0c4ab7aee4de1961d42d43898c71824e80901239e 512013c2021dab3547c483d8329c6c9f6d8e13ea8c26bd8b22062041cfdbf09f 04642b7512f9bc83574643a8264283c0d5babd2268d0cfc0dde2a0e9cf485810 73f1d238488bed3dbb6dd2b17cc2729c37a856c4480c7659df73ba720e06af66 dc689ddcd35537242d54bcc1d9c8702fa7052a3225bbcc6099c630846b86eaa4 d940524a347587f8aac7c625a5f2e9c09964a81b3bd23d9935581f481410c435 c13db87926100d59333503014d9fa86cb571fdd8f06ebd18c49ed81dbbdb35d7 fd6d7b3cc4beb0b35320193c2321e5a701c102859bcefc03b59c04771c9e7dc3 41aca81c3610c710d6b4d50ef7ddb6a16e5a1070f947b6b992aef2154aa6116d 2a335f036f2be190d619200a4494d11377274225660ea2051270925f39b0800b5fbbea24f42e23ef20318a301169fa0b33150787bcb774c0e7165f6b645da1028ed153ca63fce2e30767784ae65bf1aaee2dc3d58f1b596542ef0eb6fa205a0e064630ce3dfc724c35c55e6c5c06cbc5406e2ea2b53061bf4644fc580a444a0b3a14ef1c0c63b97862c41154f9088dd9e5a048ff579abf70201498d3530df708d18ac9803499cd4f73275322f075dff5066ad323dbc284a070eb6db42675f4087d9bc848224e9d30e921c77658aa3a4c239deec9720bc725dcebc761ac54a8026fd3cd07416422d76cb224a10ab654b7167172cbd41cb3df8af5f144209f3707e9dc0ead60442637e9d21d03bb086ebb6d4ff6fffb122ac09f867569bb136900e46267b36a4c7dca7f81500142b43ebcdbf3819013f107a93aa36c699d505e0c7e9d4f521eca9032cd00285df847b44e72384038e3bd23e808fc6cd422dadb0f7f9bc5f7492e806e72fdf074a059fb5144bb3e41fe63b3d98f9d5df0e01ad40175183127d227cc86154dad8229a34ab6eef7feae6f409d6eb85d076d0483b60513d97d59629c8c88d1bc5e13632d62f5474912fcd64ba1c27e4de4a3320747072dd84807d658d3586e6cd234c16bd7f4d6f719549082d6b9fc3bb05cfe7a7f0bfbf5045a7dae4be973b311bb147be460248d34cce6497695904618aa33853c0b8d43ea60341896abf3f005ab92497e89ae7665187c7024a537b4c35c9f56660f4c9e558d5b5bfdaf5ca3dadc97991bc7164799cbbe76d114f148407817969c0ce8c80a05f8bcc5a17df6dae0b9e5c6a531d5cd395dde2d211736bf9d8da4620e535b3338e11b1ac5f1a37d57f8d68b35fc9525bece15d7e01eb3ea1e57bc2a0d81a06f7a75e60c9b3c335a8be836390f76c02ade53bb997a0d7f90718a355406aa7a6b0b2c5673870c6d95a243800e745d9a7a0590562bdbc16695120a93e6032037f6e0bde5ff33545170f7fef30c724ab3eb29f6db4f05018d37637c6c1202a82cd85269eefe5192e40d69a67c9e39391d52903c3faa9e7007bf6b32e90e0128c62f3ab9455948b64c9d9388af948596ae93d02c3afa4aba4fd1358755bf0d62b22e7ab9a24085be55b01d137e64f177a13e43824ebfc94704b1220a59290afa1eff4838d7ef7c79b57417381e851194e054747cc5f86ae114ca556cc1320b38867d99eb1d16866402dbb905c3a4dc7078d0954041a1f58f6c47c6a87892069ba900f3ba48d15b8e7513f51631eea50ac21c9f34e7c8766f1641236a9fe0033a0b279fd87257ba76585b94cdd451371d7d0b3e77e02bc2aacf766b8b478502b95010df95517847065a2948e4b40ceefe3e326f7617a7087184b52e2c63ad05dc9bfb2d9002d8de57dd0d91ba69ff6a340bb5ac54d93caa139709f68786140397f839057b958e5006e9c1fe00226e3c7aec189dfda28ff922926d985d542505db2d9ca114cabc9c6ac195110e3eae7ff9e4eb0efb35baa73c335ea866b98504b2cdc86964735ad6922c0f7feeba09007cd0508527c205492a981efcd8a3b40f9b78af08baf09c68c090a8da033cb5438f68b5154209f7d27c0e3b2e1829370a00c84d53389f0965eb18de9fa0c00936ccfee8bacb76ef718dff2353797f270f0990ad0536210ea0facdbc7aebdee3eb9a5e5b5923ff361d4dcd5de43dfb60062e59a6957c53afbbe6b56827cd90801a2096740f423e23082b8471944d12390e7bc960bd28e910d0f74f274c5b4202df2c11bcad4349126a5e49dac744becb0c6fba072df9ac78a0077ef0381f644318da4ef5374567ae9c90ffa0972b38ae0586009823f626e5238b503c97d2061e725de7baa7b00b15c922452a8a4bf7950b1cb548254d44ccbe0a24f9ff9432e37489e1cd525b328813fd43555847d71907f8f86b3e3ced92016974b14ed7acc5c05507d9e49d1c4d0e22bb3356c2a4bd04e798fd10308fbf9f22505dcbeeb2c42691c9a7501cf725489a4d71713626830b619b3f745f30df274a1f38dc7bf467b2a84a7b9fd674ebf4f95ee2eae97b820894cdf5e135895ca3e0a350d84502e1f6d11f64df299371c0a4e5643a2c248106fd338bae8da3a376b12b39abe7d4e4f78b1d8906f30ec239423c193c649d5b0107da4c0ed19fc6c072fd998ab621e3ff521971a5a423a8ceef3a774fc5012b0f4d198f1fc69c53107a00412b3ea1367dddb8bb134544a2ef23dffe356d22cd0945c059f635d9c2ebce160c09d98db5bc80be2e38bf2c8adcdc60cbfdbd6eef087e9857f1dd4905b0b89e4c7d594bbceb64c5a1529988f709e89cd600e42a4c0b5515a6f8b17216c6640a10c1f1cfad00737c90712aa54d40f21e2d3d3700720a54b92eea66d396dc1597277804e311096513548a1865497678e0f15ba96bae0b3414914e7afee8c5f54efdf808a391e122a8fc6c882e5e0295873b684032660addd4f32187c75a28b9e384c4b038124b9194ff7300cc505a005339a6033db6085a472ff1b6dc5b6485c126eaa7c85d1ec1f9f12064fbd7d5ad12ac2bc9ef000cabc81c1b2fd8096a4e743c59239501552208b6e22049e8afea8caa72e87a8e00497f1dfc06fe44c3ef04a5625bdfea778a8c09e66039a5b5bbb4c7b49c95be0905eb9cc2a0d3fc333767a92de2acc4318394a7860113af34a52f6aaecf75910e2b443e2b87aaad3d005312aa33a1cd97d0a291b3c407732fd82f7d275740060a3d23c8b73b83d429124a2904308c6304954e15f24b7ba7b1bf163c8a6fd08609dd265752eca76b93243998ac133965f4048c2b773dbb503d0a104a3e477ecc072aaa0b7fc4a55cba9408559208e53e78c9bf618f853a08c11149d922aae5f20b76c7b279be5288c1c997ecffef90e513460d93188ff40bc4e886a63ed70a34016e3805f588e11c648651a3b16e248c1191fe8743e1a03174b570cc89304a390b24a875cfc57736f2fa2d46cf338003920e8de51e1f239f506ae76ca2e435e20da030d1bf38057d1f5775d257f26f3b5303a0de95e6bb968911e7787492c30f0dc020453eee525424e1a7c6043008bb1d979649e6341d76362f03fad4dcbc03008de0206a80f1a9c7e69362aab1ddb8950cd1ccbc7f7b9192b8c94a88db33fc06f815c889b7ff92a2115abd94fd986ac74c69c766a313807ed95b06f1d5e66709eb2ebcaa6543e3fd1b7d5c4cb42666e33ab98eb21c5b3722ecbf32342e7287068cfb3efcdef165cb516729934f9ef1b75805c611a14489ef46ad4d43eb59f1072b59228fe36b0bb1b624458d0aaeea20ac86d4525684f22b8bd894ef9d6acb02a9f6e53b34e3b42d35ece111ec7cef9516fe1c7a2e6fec6f86722ff9fac15503e360b091d8725cfcc63a9c9eed615f7a43dce612efa288209e77e1a74d1e850d744112794ca06b0cfc2eedca2edf3d6dd99b95d97e4ec93fd6ff91b0021cef01e02d560d928a5eb3bbb0ab28e89bdb74526de6b8b484cf677a6ff9ac687877077cffe04cea0b612964b138029c9f1e91ea71e018ba72f1ca95970e65a1c9940ae5ac64adbcee155d08fb7236aa58eb5e0719f2b811b5a529ddac91dbfb963c0d4d3527491e105df430b74ed807ee7f4b7dee4d874c6f45390e71a5b14ca3e10bd49b75d425960a7561d14bc26620ce5e98df164091b9e89816c9581cd0b9e8055ce4acae9b50a1c0efa0fa695cda1246bb79681431b3a2a96efdc4068f07af0630abd0892c54073af0d0b433e82acd10059927034122a56e38235e9b33e7bb08d661831d65c688ad76b617607c0c8c8ff8e7740f9eb2769b962a804c9a0f540ba8138c522a4e86ce8034778ab9fa8f5d7c699d9d5ff8dd63c718ca100a5fb804d130dc23d5f9ef170eb93d7a10ed78d4228ede2955a5318022b4c39063c0b10b76673d3b17cea406be90244db2ded76840a0d2a5128a6d251bea852d76dadd0c25e5e1a3197f8c409877629e6ad6241320ee5c933d18c5fc78dc66e66a15660105614b11e3142ddff30f6eeee0f8f75566aed9f292aa43484a7ed6a1c20ec00a58d9a53c5e490c8b9d37667cdf2439e860fefc761a6f798c4f91ee88b004b30029d21d6997b376c30ba706812a8fcc003da5e2d1477c15e9f3d9137711ab050910c1976235c66e90be1cd046556922bc383dbcbe5d217637541cc4c881cc2a0f3519e922d7130724059fe7715dc20b96d69f113c4abb39653b3b48bbfd4d9b00280605e4d2aaf15bdf69f9c18e5079d15291a5916e43b397dbbea9f7bbadf507a4d4e869aeddd4f90c1c8a34b6cb9047c0b26368288e4904369e3ba5784d6605ad302e908e5e6aabf38f9127a4fc41c5bc267fde7fe12425a833da7494d5ce0973ddeae92c90f32b2e1389bbc1d567e70e574d1b28254c12a496c8a25c6ef90a33b4e003b9728ebe342588641792eb5facf6f0256259f5d4a2132b8cab605f0c5750a837d09050428c1b37a1b87936eb7cf180a4865f45eedc93dc460476180e8b5fec7a64a3555330d4e674f8295ce3d61de110d1a366d92fcee0229664710c9cee99d17b648a5653c1d1d07584797ae9e5098cda6598f4c1793105e6319b055f635d5fe25210fb62dcd172583058c039fac4a2686e371766f11069df02c90063b045e2c6b538c2aa26aa822eb328e592badc98ec93d64efe4f0e3d7212440095114e45c9f654b4230e8a42480d90c7233c20552467a69c5a2d61bee36c9803624b75a97ba189414001564991006464dc2f9e383d2cdb3cccca02a88b834600c0f8ac272f26ae222488d2e59cf057494bc0d1f3999c9fe1e21275240a7da10e884b53fdbb72685d6ed656b08bb215fdef0fffcd6941bb061f2b5a638c02d90ecd3a82b5c862a344bc93932cf4ae3e50ee57ecad6dc8a70eceb97f5ebe4ec10cdd2129e0a554f23ab2515264ed04eb21e84dbb5344aaa5336895ceb8a9ee870860f5e75c470551776c4d847c1568ca94b2d75fb26942d7fdfaa57f73e3a2f7068fec6f796e53a5d177543ccbfde2fc22032f19257cbc6b0b038cc1c525f5680b59f7ae36367d28d3c1b583fa84891d8b67a1aa9f1e95e35929908e73597c0a0f022e99201d203ba51559cb25df383b524b20fcc06eaa33e686c0e47188504b04c3d3414360a427ba39fc1a850725f80d4bc4ac95cb2498cae4727ed63aad0808a42bbe1e44f1243b7debc221b29bf10cd91d4daab93fe5aaec2f0ddd4364f300c039ff270866c6245fafb7aec5677d060868b13be3ee5a7c1205a071fc309b0106015ee785bcb9483ec6ec82e66f85edef89dfcd45f3bdbe26b8009f4ac73302cf43b4e1545cb6cce0cf9431e18f28f4be84201a289f75d68d0adb306766740bb889c0154d4c23a0db8fc49c979f5ab7f52543c53237dfa6c93c500c2bb88b07e0472ff1702818d8f972a30c63e13c03865183dd302b46fc5cb97627caed8b06f1a3c86b129afb61d7ad56452f22810f59338f82889ef8ac1505aec68fea0b0268ea0a60d45086118db97349ada12698399fe82237d88dd98219cc813d5aa30497caf3ccb33b158c96c164ccd0cb3b6979e1e0e141373d11bc358231167dfc0bbd98499432bacdccdde810534d04071c52274811012121885435adee3146880ab74b0b8bb43aa8269422bcb4b0439ba5794a4f12a0804f1dfddd577065a74909290e3692d5e18cc41076367e673c7efd2209afee628e23c382d179d497c3760673e427e5807a9564a4c2b927b01b3efbf6bd55e1adc7e04b55da5be246415105782eb9ab7aefefab4ef8b8fc70a06380de48f988f195578d325dc2345946d90ffca3f97d2f055fab395965a98e8d7b5cc280917c4f7041f9a4ff4f0690471c04efc75da21c5e7e781709dd89e21bdea635618e6345485c0b67a221f01c251a0f3c0a14ccb19c532607ce740c0a39c1e6125f540d6683ea5912f5e7b06100a40ee51c81355f28be47d3592e2d81bfd0042b1c3cad0bbf48e73b6d5b801c23f609cfeaa2d827ff5e97ab8fabdbc1dc0c61cdd924495afd5f4178a7463ca963fa0934f02dbb12047fd87f3b687130991abe7d206a0d58c7c2ae8965087e519a650d4ea6b05ad65456476083631af06997eb13e7cd4fe01e0dcacf084a2de64fe20ab4a4711dffcc77bbc438a28931be276d52974eccdcbc03ec4706db5b9441450edeebb175a863b14c47b84c7399613d2323ee1cc73692ecdc040bdb3095233b056a2f57423cc8526b10eb03bbdd97c07124d4ab90deba1c266d29eecbb3efda0b481988863b1b3a24098c82da2f4d4b8f89951499c5801dbaa1bbe77dcab05502017ab14fbd61459bd7d2f1c7bcbeb3fd786656a69f19f6c010e3b31f230f18016220e1e40a6d38558de756bb4474fbeba56863bb3e27b8d76ada1645bea5f605a850ddce746f5f838c2bc34e599f7459176578a4419fae32bc693515d6091707a22d7c56bf3bb24774a75c6352e9ee0459ac9d5fbe6e078021e0b05b908eca079b483d805477ab7541ddac564da4a04c404d558bec38b3c4691083ca94222109461ed0387f663db7ea43092598e8a76cd05c87a7023437d7084259d44dbec00235d97b04de11da33d6c8624ec8e260cb2b8b1844fe72bfb8327257056b0cc902f923705492e0f8eaa90f6737a61f7fdff0405f151a86de0a13e8fd65b84145054409f5b81bee368ebeebf4a8073bafe34ff62cc21126e6282a9979e5c702f8006635afaf34c1b0c65c98cab197d1f8d0dc7971595e897736a7152a095cfc530ca53772c753d7cb33b0ee4081cce0ce12612bb38d03980cb1dee53d2a5755f00c7d286d89ab79b8871c98c1f9de07278777bf48ae897da6c4a8574762136ab40a07a316e5bccfc5ca4b0989b4da08819c038f037b3fe573c8c9d5543948703a0937d34598841b8ffbaa3c5363bbb7b81f00d94789312bf6c8ad5dfc58cf5e4e029b1b480ead30d42e60ad4b2c79cf41adaadf5297c3f96e9f848aa7320087540aef660edff5612418dda465113ed9c7636778a8e0800457d1aa8df8c975622c0810869b9342a6d399a19278dbb3ef9c45e33162fc0999104ef578603269fcfd0165129923422d5d3243aeb4ff499f548d8bb1447bf814e56990b8f6f5f7b57b07cf5366afcb81f2b77c2327bc16d8dbb294960633864af4c3743bd99c67b8f40abe5cd5c82bbc387eb4e5bb3d919789bc9dc188f3a2906d64b4a412540d58a90afa6095ffaa3bb818c558064f071ebea84cc27b17876fffabd4648d51a273140b7dbc1132753bcbc8f38d6686804a9a6ea9bd7980d73ad16a77be19ee77503702743dbe6305def508d5e69fa377e6d07a9bbcbe316b9028b8d4b7ac8f043e9803f7957b016274eba6fa07a49ac5bf31c14dba2ecf8ad264d6cb5e5d48a86e6c00351dd4d64b7b0a0b09bb0e3a648cf1d5a16b4087ecdf454b44fb7d67dd83dc0762fc24cfad8db745e61551e3702780e6079025f1120f16e67c8c92c1500ced08701dbc914bde6e44e820c86ddd72f7fac04cc0248115e9d55c7e495cb20ad80e083c26aad6117a5470d4f056a52e84ea2499e6591858425dda36631f9a91290ad343f75ee789737f86a3d088839bdaecfadfafb1b4485135c07b886cde4ed901c3a9ad0f72ad721544c38818d6fd6ecf673a61ff9ccd97eac3badaa7106fa30482f865cd3bff47c3d800f5e72d6ed96468cba56a78da6e1fe25ccd74b10b2c0462fa46a1d9593371817bb571bc18beef3677beefac7a719bfeeccfc8f8e43f05ad97dfa8aabd3251875d442d65e06c819234f42d7ba703ac47870714a79628070e9ada8f5c66d9dd44dcf9cf5d034cb86f1e5128fa8611a28ea3fe3b3f79cb03e41bf9bb7f14f5231bcf15979c57606428aaa0589e616bf81f840edcdf2130071a3bf313ecad9b7c8329a4f1e69bfd36779a12ae6aef5f5e3fa558fcf745e9042599f966cf359c6d17485a2016b00ebae667f28e26afe51025df7abe4f479c05d179fbd280af76e4d41975730d24cee294e97c9111b84c871700e546a67d21088047ad41afbc66a502cfc6f10154cc2741b758aafa2e9c25b47886b0cfc9390ae683acd083fca78d821cd5314b545dc8e4ac2a9cc4b836de9e8f5c411b260703890aae79ed26105dcb704d894e355a83be34aa7911512f35147d17b387f0ad0d1feb186440147127e289cac7110d49a24bda2fa72dbc38b02d8f5b94a034dd0dd10a4c81e537055243f9a29d312458450d705c459b3d9a9b8d84eb21606e3d0eb5cbd038e24ea1a2dc684e0327656e59e157d0f823386a89d58508d5018c140269c978248c4df0c41aef47c18a89e01086b1ce7ff1e892b47e2633581adc530fcfbff066fb3b829effa850dfe9edadc36271b6d7ac54f76e90092485b7fa640094384bec23d7abd16aacbfbd47121463da806c3d38b98ac2d28fc934cc79950578201cdc43afe23eb9ad6ceea23e0d2991c32d28d38905f07147b5186e607c0f5386f80111ef27abad6ca4357cf4c3b1978d984101372e4a68d4c81d3372c205313382e8247d00e7d3e314153cb2d44bf3440988521a60bc0743a0d67b58270205901f8c2c340e9ba2c88db57892c7f110d3d63c0fda3a5e80666be66e44ab0ccd6c2f576cf691bfcf9071778ac7e4a72e13ca773f8df8c3ee6546557cc5ae0171ba84883cecb53b5dc4fcd0c4099b94eaf83c275185a2d82a9c33dd445a390b14d044ff9bd8b13dbc509ae2f0b98a0e73ace0d71281bc4f8b4ed25d5e2e060672e67362e5f96a81c344e8abcce4a83e5e17fb093e342aad9ae57927d29b7908870b53b8ddd13accffc1f0b89d22ffc4adde9d0db48500d08cd0f8da4ffa0f04b996a023d7d570f794f664aef9325535e03b17712132d5d6d00e3c0c0f88620e899aa8ba7217a04a7586fc160c3799dc6af770338a824442f6ef6cd041fcd601876e167f93457813b27429e3a82b741f3cee87960f30b8555d0d8edfb57926078cc8a3a43968c33c3bcbddd3d6dee065fc0fbb704d29fd3bb27e748922fd830e9f2c05ae3bbb7aa0213d332881b4482740810dc220cb3cae5e5bf7e17b42010ddd277aa9cede86abd5af5c5c744d11c9a5ba66b9f34684b695b59a4fdf6050038ef117163d86babd879c9d48495988a413948a12197bb25040820b37a01ee40ffbbff516dcf7e8b5d052497c554fc088cc2472b27aef31419f8de04e24f3b7041ffc4d3c2ef6ed5f4d06e962a55ef7508a4fffbf56ef7ed478b384d4502acf067cad8d859ad4a63d84f79e7decfbff329298c5daf130170f4e3051f8ce01f60cbf826a0a3cbd298872800ca9fef8ee4a88993224add09bcec759abdbea365709caed3e66d5f83b3e3ce9e5f3ace1cd426df280e813a9550de32654f6821d690764cab9e2c5612f2a47db4abe1c8a13d7151cc7dd1225f72a1d9821dec8b0c90cd78097bbc7e40f8c42ba4430abf88d048d0d8d92ddbe77025f161515b4a2f80a6b1659f923c624210597ab7f5f8c6627431d8ea0b69de931d5ecab18a687790d03128506700f0f23fdeea72b65f31b79ea2e5a7e887773317093a1638ab99606605de55b6e971befe94bb84f2e05e94be4e58fff3fd847437df2685d1f22d003d3f263c72ac9030deac0a8821d30c43149383e4a15a22be981e8763db5669b0f8293be9288719d90bdb33710534902507f66f34f23060fbcc9ff9d0f0064f808323028b1511e06744a5a797b6b2d6608f08c56b221dcf135ae5918550131830497a1cf37951333be23633dc38869273628b8790663301891c8cb0a8e32fdce06bf6343137705d7784efaddc7ccf98a04af777d530bbb1550a5ac9e34d0b37003c8956f1e7552e2b9c0f3477b692538d6e483411753e7da58cba54c3f154aa507069ec170922c85bd8e18d4ca3caf16b0143d7a6a352a3a9349753c742f7e3a07d69326ac5a967a63ba12de5abca9565967da68c607a62e77bf711dbec2bfb4049bdf785f309ba388f71f96091cdebebf5bc81babb464489be0f0f705ffb2420e1a6577c1ee870a09c398f39c683d594547325def2bf05f9f6633b13957673102f5e04ca468589b948fa046a13df21143020134c4e25d285e5443abe64310e40a1558fe1bb0141927da7dbf9a7c0b274901a880b6865925196be881d1408a3f0e90f59f8d135d21cf8ae57246e6962980e7642f47a9983ed396f92feed94fbf0a02853c2729155dd6516191cd3fa6f7bdf64ca73c927f2dfc1feba43b985a86024f82bd354795ae5c54f0dd552f3f9aac1afe621808d19c7fcfa9ccb60a0f380b0c6c597f5603cabdb8f9d49d1534654c943a54a02f12b8a51c694c388119080e3aa13dd5fd06c1537ad293060f9b9a9e01e90764151ee08751a11ebab18eae068b019b18c921f9759e0b1ddd87b440896039e7205fd8df7016d615f736e49e0d32ded6a349da2610241619fb7ef2cca4deef10861c9eecee9c94fb630c8ab8011b4898e006586d8414288a1610249c90cf2cadd7f996a0cf901ae869f444a30a114b049926f04a38d9c842fdee70ae9a41d3ef44cacc18fafdcc7917191955084febcd2e1c66561d57af3fbcf530bb3e46411295a1df5234868b2490d9b74d04e0731257f8bc1aaa555da277e9ad3d436e0f61316b9f8a3418af7668b41f460fc2575551a6946b56f84b6b19e51c6b12bae86aa459692cd44a913c5240eb0a0a9844215a72187a263f1ca4e6e2f309ccb505b8e1225d6da94ed603d02d680c031e7e55ff1f5037c02c2aed19fd56ba7fddce9f50f8e891deb47e13c2d5b11f031e75cbcc96a6822483178cf561aa7f29d6bdcaabf8d1c1c2a7f0901f8f4fae079673846dfa93bc4015908e271d95075f49b2d271e87e9db2725514400ce3b906e118d7085847e9979deae0fc8307eeddc39c0815bfe35af6702789788d60d005462ab6a71bea038cec0a7e1e0d7bce5e1581639e4e32595a25686acccc12d20a162266098e3bfb494c2887c5e1d46fcc6584166001491bffa3f7d707adc90b0be4df56d8b6446a1fac3b71dfc0e23c97cd0808c0253d4e935f4205ac367c2407cf890fbf63d4f479af7e2438552a2ae1870f4d322729693b669b72d37e5f560db1395dfa6cf74b6536753e26af3b4c5407733e22391b79553c2b2ba7468859089085429ed052728b131d8e7b0362fcbb8dfa4400c9b5ac6ef1bbcaf4ad0b0507699af26bd7230b5052c44651337225c3e7cce47ba713a4a9a75cf7f27ba65207d09c9855584d523269f4faa390c3bfa0db547bd135a85c08e57d5b4b8ed1620bf6725d4bd7f3bc7d90076c28b7cbc5e605fd4748b04e0c1531284675fc717b072abe1b8f3f03f11ea0440a29469a10c5ec8a5f214c58b823ada8ead9bb7bba01db45d4ab3a7fd493af49b25f641a34d3cdc8f69c992c4531351837f2bcc43d024055642929c063e9b1d0887e246979387180f9457e8b05eebd0be68d6c2f2002d27fb36a8d1d061f584d5e001d04e36b067b1368a1201edeb805d86d38a2f20c8b49028460bc803add96d20ac5871ac15114d6830c3ffe76ad1050929be8ef0bbcc963250e6ab0feb9969a9be58a7fb510fb4ac835c2ea63c139e3f705f3480590db91b58882a7debed82a3940c682c731cf1a43a5b9784ed5d4007753f24e0578e1686e207d64b0ccf725aaba7c8d9da8b3df0ac7a7cca8f74269dd04e28e0e1be586cc8563f6df2d8b9e04cd18b24e919244a850d83179e5ce8492bc82b302a23485df07f9673e3d2b25e60f8c3f3f7757e0b7309d06b67d1ab67718869705bfad11e6a69b9d4cd9cd04cb7c6e1011c79a89f0ecd256c4a781cc8ee3b1d30e706418e2ac2da2d827658d9c7eaf41cdd40bcc4aefd1c77f0526a18d6f8ea20ef613ad81fcd9cf30a1459a2ad7ac2b8403327beac5d76d25a0682672319db4097cc7cb3be3c7f7ef7ac3a5e4be0464d17f7fa3fd66f04832d5b08912b7303708ef6fcbce95da340c152e49ae70a7ab397b4a31ab61f542037aa7bbf41cbc8c0a5efa075043d445ec6220775be2649d1a849cc7de9fd5f34ac7d29ca529af480ac0b5668b3bffd05a851ca2564c896814d2dea840df19b9189346661eb197560380770b4a8aa89846b22d1e25f71e1245ec04df5d381210ffcc0f5a840707af0f8707cba4f3d08bc2a449b130ca2a09a95f2873f57c74ad1b100da90de605ba04ed9e154c3baf6ab8a1be5344232429c171da41833469c8ae588aad802e2d0b03aacaccd125ccc3132abdd4bcf97f055adba4d11d10d406cea9240fadfd6fc70299830d32620eb150c5dcb06322a97c06e04d1555e0612b9c8b403873a9e5a1050f27a9740228817e860d5a000e2e2d802b95d3e82d80ab81956a067414f8fe0aa3ced87edab7bab21c7198bbc37c12477ef31b0dbac0e89d0d71a8996664ab08e4622077ac3a24ff4292d9299c948fd8b211f7a80ef291394eff117cfc848409fa4f6c786388efb188f92caf1305b015f496727572ef175fb70cb0b51d12ed0d7435f312757c73afa803ad105429211bb6ea5f1516718a2d8975d47b9972410464d8222e084af99b7aefc578df944c8f057fbd2986a0300eedcb08f3dc12930dba22a862bd6597c9905a4d372f63e2d05512df34fd7eb5b513bd292900868802112f46464bd62eb589ca56bd9fb0679164eec2b6acb15a81941685bdeb9c480e9491f0f4a353b4d67d80704e4baccc2df8a888e50a4293fa4e74f8f9ca4d4d047c1f92f975121a10febde5c3221349a08017ce19d8231763f23fc15b7e89f102a091b14776a0d04293bb2de5821e10d71a2d21371ff98c52e17276458dfccc0123e391898138293e8119962b34e5df1c80ed7c05fbcffad24e4b325f981b880b893d67133cbb6d651145516a1804a3be21966e2f82a270da96e96e9000f0b305b44489d78f66726a83e5e615d08a0d23a6bc3c278b824d69087285b3474f0b09169478a2e60fc59332e9d337a9db4fc97d379f5c2112415aa8800f76d6cff00ddb5f2bf8cfde2d321be7a973d619668f27b07928cdef218d6eb728a5f648660d081f8a6b0541b053b3996d6a0e7abb6031c6cd81ab9fecd0675a3d8fb10ebe01fc4c47b67b11ea8045a84b1ee127888850971117816f6ba2b29e8c18bc61970888b4c3d07679d88da0d4affb6ccfe11dab598043a007b0cb431057050f5eaa0dbf6e1bb7a8bcba1d6f37453d8d568172d59e1041cb2bb7067d631ea11ac04407fb12e0653d84d1de9b94d0ecc762b9371b82675f15656e7bf9be483ef2de2d0c425facc27d87046c07734f95fd647fd806e5188c6326bad5352ac27cd08e44031cca3712d5841fc6e98ef185e40c8a615332ba53530e25b6dc9d969fdfc6b70c0a857b9f6107d22a82f7151a9b7efe7eddea9bfa0e48117720ba8bbb335d52023841697744004941dfe1692bb0f76f3db2508fef4967d334cc95178e6c8e1e06e8bc2764b02e5df9f8107b54eceaf6631425774801a68ee3c45d84cafab3ed0223887a54089023a6032fe5850e578556617bbd22d9d4b8cf36c63cd4cebea508f4fa86cb958fcb541f2dd4df4c8646cf05472abc00f56478905270e83279e100f166097416be1ed0bf4c725715be7d01144a028bcaffe9799e57274c5bd71f07618e7b1f5b4201e5de22499180145b1ef61077668a5bf5662060ae1354fc330650cbb76d481eabd7a0e954820e63380504ecf8a0a469e9e3289290e84e719e0b8917599af33a1568506538e8489fe1a7e279d3dfa08c93de19242047e00f240a9561d2211fbb19a682089c9d24128f7abb98470b2b9721d45c64861855002c05404ba14d13bd95da31c0cab7af3c7688f6d5e6af120f77099a7d7bbb79f5770c5d86b2afdcb922ec6a7d2fab2da4bca2d9bd20e1ce2136171944dfc824908a043da47098c6782acf9288c1ef1a084afe7f67b816f451918b16fe7458e3ed220b28519544f09c973d306ae8b45fae8cfca8f3826cd41ad85415537ba9932fd501d170e1d4d250388e9a145232903cb7f9173d1016e15468564841a3ed9d55360b05d84e7df75a7841c498e8861d045d95f5c05403ec096ec7bd4c8e0160c5660bd439a739b2b339055a71aee1947c7f71822ec54d86dbeb01fc95af6be5b5860c5a5cd069d0338e4efa5e49cdee40c34286af5391bbdb75b101cd77925dfdb309275fa85817c9869cb4dce20ebc4be0d49394bc81233247ee3f2732e43feb0508cd3aea0ac322ef058c361f08504bee8d10477b150b94cdf44ca342aa597acb003dcbb573820f5fda1b8b96f0057bc443f6980739dcd1dcb89c716906bf4540035b02ed2124b9329d07d10306f5b3f2fb7046250767c4dc3698c34f01e52d0a03bf573b73ffc6c0b78545fa217f01951ee69e6798d655f141d73854cfe049750099f800a37bdbacb00b2d3b3ee7600871eadfbde05e4b3cdd2f78816898758e01130b6efacf13ae6b949358769b54edd860583fc3183fded718d8897f8c90060648d65e536994e1c57a20f84649b37cb635d200ae67682b7359ea0575a590280e3c4125dde720ff6a58de4f0caad8e468f8fa60be725fa562c0466139d530b10a0881c3e0f9860b3f62b58705f0b1aaf0d974b12988f581cc5a3a869646b6f6065827cd3b905cf614414a259e2d597d30c370bb73f7a0e9098f6703c5b74a190b3e60a66def6603320a417ac39ba6410646b2c1ca3ad435a2888abe234bf7cd0ca53265bda5af0ef11562d5358e2dd1cb833abafa40d9db440fbdf3b026196407bafddfd7fdc5fc4428c7b94c56dc1da49d13b2684ce61065ef7859522813c1059aa24c2d68e6fdb5369b06d39ede5285cbfe23c55da6f1531709e6283efd4c062d633bd81e21aa3f88e6fa99b207ab949e329c0e58183bfc4a7d6e54ac16b70bae841713b1e915ac99de428ecc1bcee266666deddeaf7b39e91242b5543b13084859e565247aa8ab8e41df6eddb27fa65fb48202f01481418f5042e1aad90d002cafd1da38eb3eccb76970352a38141093efe408e3ac3983af1e09d9de50ec06a98abf12a19238eae558589a15d1e2b74ff04c910910d018842c57eed191ff0d82521131a5373b26cb8a7f149d42b7a90cd10d2ff27d4675709ae16d8ce2920f83d077ed6bd1e49fbca4a24fe3d2ab8cb96b6be45c573696f463bd0e8ce91103a5f0cb5de73dd895af9eb6ed3daea13cd735053a0b768718019f97de37e24a02c71e36f2f8e550a1e2107d3b421f0442244a0db798ff64a023fd05e8ded31508415f3ac08ca3d0fc61ff62e3ab587456b194bababc51b61ae74915ce6a0d3109fbb0dd6e564a106089bd1c8edaccc48eb8ba0f230b0974636c6d568e19211204b54a55d5405b658b1829929b70a42eb2d170fc3988970b23ecaa6ed3e13afc0289e097ee3fbf672bff426cdaa9abfb549a0a5af15ea322b74792b8bd62db6209a79871c57655aa67a20fec71c3e799dfaf0322a9059c29b10b196641dddc090d91abfae17a717f3839662e9e7efaa3bcc8a28debb3c8a42daa21941fa7482101ad9c3a11a0c8a7a4904447b73e9c25005c507598e6b742a5af1f14dc6951fd006582026626ea0c7aec01a0548d8ca7678fa6de3bc10f6661b288b31a9b95260034ee9858544358424507ede32735b33e5c1103bac72885e74a7f07fcc8abec00d0c2fecd5a655eed69fa2c8ee12d3416024091cc31873c063ddb9900f66d38021d4cdce3a886edd488d134947e31889dd52ef342ddf7f050fbc41a1ccd1ddb05774cef49ba87c67ec4fce84412e9fc63e6290a58d4c4fd28171d1fd86de1f5041e19d1f9f43dd79eb7f24b716eee0f80bba6e0a8f9d26466bf3d7ed2beb41d0eb61919f543aa5cae5b47ca783da7f760259b1062ce8512e36671e15cc0d4a20d83307d8a737bfc66db61cd3973718df141a4f083cb5f05827d803889911e210cde133a113c10d2fc8c85794a21291d8203f70cd8ed691084063bdb28d6f0d4037fe2a8123c506eb3e1f4749d1824500aad69816fb07fc8a2eb4fc25791f31c0faa4087b97daa76c5d00f2b8921cd5602b18f3ffedcb8d0980a5b28e5503ac10fff83c98ec0223d27f265cf5a3a451804db549303deba248f17ef0d4cc116ec024b48e0032a2771409f07713aa6bec578ac0933c0cf9d8ecd726018d36b409b066cfd1d2e25655c2110ae4c9349ed63eaeb84252e56fef1ca4bb61e57cac8b807c42fca6d81b71e0c0dfb8ce2439efff90e3d3880d94451e53a94cb74c867bf0b14d7ec03997dcf61cb213adaf4f5f768b7d995e694df8b210a3d9baad348a7089a719d0a90d6c23d16f5d5702f85b26d03d89304c6588477aa75894f28cb690e61b0cb88856613252c2b4c3e6f6c476044d002213eaccfa9bea99f1ca55bfa01a8dbb42c38562fd2ae7dc0ee80eb9e2304647ec34f89c4b99af58b8e06b3be0c32fcff2f3d6c32ea44e7f6549c821ad9c8fdbbf2143f14d5c838fd710311e8031e469d6077915247acd904380debff6a093f0528604117957f2e20cf8df6ff0aaac2a6a617a746c88a4940638255c008acb9eb09cffc8dcab398935436bc6b0a6f6bebb95b81ec3900d9e199cb77e8160e45b555f94a718386e8aef7259c0d0c3db31a9d98039131687c2639fed47ac892cbd2e9641b973107c6fc1a95bf8b01767c7b20fae87b10cc7c336f4293e7a1c8605030eb419a9cd5183bc20749c60907156187206e3bdc573f7b88dd6276b5cb9525d5a2bf516e85e05ea187b91c0e96663b2eda12702681051f2fbdbec6908c438c4f7f6d407a97d78c2b9a1187041746ec665dec810b5e2ccb4af6c40830447ecbfd7433610f63933e2bc21df1007cdd094f34d2828bf1cb31a8bc96df40c8bac7d904c2b756f4222acbaedb400cbd30a432ec441dca560b049b912fc4848f9b04eb58ac1e2755ce20a176d2a7091a3c05c7fc63cb37b34811a6080616cb9e9b1ef674ec70e37bce031112f8da0336970a72c57cc387fc80440a48750a4ae2dacf4657a620dcd5de1cfe16fa20073999da168ed2854d488fa4a46bf7f57c21615bbe3ade7daea00f482c9431a10298cf928659a2647199f8ebf7dfaf5d24b10b769802bd9e9b6e9600aa26b4b90b5c5f3a403fce89ee823d44fae8e2c68258255bfdb6820292b9ba11669e2ca10479a7d177ebd0c13078711a3bed023b25222a7c1c67a52aa77d13d440ae392406f03712389fce3fda327c06164871b281b0da6667ab55ea28b02e137e5ab41b08e5fe2b589c3e9d6c97110387111487ea36faa58a1dd49d93ff18680349c62c043f1e340d85123b96ce80ae82c240845c2f9284e84e2ef2e67ff7dc0d048a91093e76da12ae1d196f974701cd36828160524333b65249dfac1fc2ab0129538902cf42f2cf30eaf0163ac26ba48eca5f57620f9695def5b28f12cf9afa8cfc80070c701e2a86d80491c42e4a55a13d59722c54b74d2ddca7005acd0fd894819206597ae8af857b38c0b44264672ee3b37a6851e2df9b0a731f3dda20c52d1d580eed3edb609ff00c5d8d7a3f82e297fabe7b8b4dc48d413073f7ce2dc5fa5e110d967a6bdd747f14364e8e9cef0486c231a50ee694f73b3c65f87999cf5777ae0d038c64c6e18591a153c87ffc6a4175fa300649f4e035e0dac88e63bfc4ae05066d7a7164b8981485711b8df62a0d49522d58ba271ce69df139dab055ea9f76022b7c8c969208a1e7bf1c21bcc0b9fffcec623df040060f5beb18ce5a6772070a0db76aa56d1cc616d61e1970b71458b4f583032b751d1f0bc93cd7043b065307cb1710a1dcfdeb7b23403058f369a8101e716b862a182ebc69916eca1466210ae57d07c2e2e1fd99da10262e83d0d1130d1394ae66302101a711616549bd0301a8155f4de32de9e3dd34d75b70e9c9d386f1fe777f776a0d3b8df20f1e25d903795d00d699994ab2632b181ab8e641b0fe25c1f0659dc09a35fbbee9c795860e31a73fbe6f298c4f159a7289bb3df1b00ce312760f37d7d2e622abc82ff2e20c0445d673f2c9826058be314c10e9762fe85210d9543ad008346a8df29179980e7e43b1da497efa91b557317f76ece3933ccf9416aabe6e345d3dbc4faa989f02eae4a4502a9e8daf09a1a35aff15872de9fcc89728c02f56614f96c640134b06f5e552b87b1094ae79e4dbd92c6851a9edeb7fef58836824d9bdcc36b35f5306f8e3aa38be18b7f49853fc2fd0612316162d514971fe6704b81f2bd2af8eff09c73294726c30dbcbf00534e0041205823226c85a3a3c4563d91b7ac5503ae00c389971d41b39d4b2a711fb0065e8653ca930d1ebf4bdcdf70f8275f712634a0a1b8a2c80d2d90e9ecd8d21b631a466736223599d542bc848a3856414c18bd90c567f56373845caab5d6c8a3d8145e83bbecf01c104113ac1d32f9f1233e4340fb3ee9b7554b936000cacca52f90a4e8a2aafeeb9ac5f5e04d16964869887af09eb5aa94d859db1d7245537a2fad4c7eb4d00d70a61e520b1324c33045bee90016e95a3c70b3e021ba08e423dd2aafd7dc1e52379a35fa4802f3d30f5a020980dd06ace6c7317d06b8adb231a7ed38dc3420f4ee3b4d9d234cb2af5ffe7d24f0a8fe4e55e40992fabea9a2f78408a0c26c5d78f2ea9053031cbe217dd5e7ec10d5b920a9e7a455dda8b0fd4e8296f611e41e133fcaff086a716df3a02436d9c0257246628fcc6bade19f6ac57034fb939e67694876d3d67908bee4e5a102b3b0ce384a30c7800af240d1326d57c6e8501972540e96b7cd6fbe78be647a1f4eb0bef82045c634ba0cf2d3b29d9f25a4c4f9170e261b3bc83e58905cef757b04603b0967ffffd477c3177b6091202744b1b07cf33fa744bd9ac4ae523578aa9040b4431576bd6a1b56c216cce92fcd6f3e05090ea9dd8016352468c640bdc67b60989bc0a9412a2aff478560130b853e4b61ce9c95cefeb3ba02aabf587123b640741eeff5b5f1879d947559ba19311b147f8afb6b109238966ae683fa63b86430b4469aed09a98c097fa5bee486538841e412028020206f5237227589ae3dc900734ecaf17cb35ca98831ff1c5d79f0cd93c8a89a9e83968c4255e8f946637f80a81c760214715ade81e67b81cb9d4d68fd19d7da826a7acace095ecc89c272f02c8a7e4d505037c39d44469b222d64cf12e96c51571593cf003aed681e4422e01b28c986fff1522200f430a8c5da813c08f9d89bb760b503bb6aa6914855358099694d613d5ef0cdf891515d65748c11e3c6bf60b1608392a6c2edb8b11a8970f8a9e2375ecc16f9cca94977f95fe0e3a6681b2b400e2860c4e3806734245b30709c6f8b923ced1e8edd6e9f9fb2dc2dc8223c01585192fc61226fb67967c430851d59de5129ecaab4ca98044be558c08d90b7bc3765c5c64ea5d5ff06ae6f50fa9dc8d807713d85a07b2877d3252ddf5eb5c91d7f584bfba001c5c9059e3c10fa12ea02230c6d7c156b4c35add61d7f5683b4e0baa786ad4d519cc8714bea002918d966ad37691b20e61553c91b4f1462a4a09275f06112ee434a057d7d03a015ae19e2a62843f4c7ab0d1f54111942a5b369250afea5974dd85089180175f00ab1974cf9a1d7dc2061a8291cf24cdd936dab618c36213cb56fe7130c218af0b1f9491e9fe68cf3de81436b36b0e6b59a276d8d43b41cf0e1d401fc51f0f2a0e8a8f4eda4863b178f85f159420f1f880645aa63700068f430b7289afcafadd017d229e85662ebbc257acaea5f98e4ab9869426c7af0c63932b8bb048db8b4a05 true -check_ring_signature 648561d2c483bc13e325bc7099359ee35a7ddcb77097d94f8ae2fd422e4a6b10 4f3f19a00ab276ff7a30288b31d85f7804efa0674d483fdd5a097f6b3eb49f4b 66 d1fd42a5c5fd6c6f913d215f90268962b394533d457be3b3d7032ba1aacf89d4 31c87365534f2cd641bbd8f0efbcc2bc261a4a7e61751f3194b1dfc76f945e4f 2ac2827347635cdab44135c21383a5f7a27190ed36bd0ec16b63dffeaa91983c 7df36d43b3d16a216cdf0f6a1db6c37b9393d92de72a4a9f6ec6c0105a5f2fbd 64053c5ecdaf4795d7ce12aec31b1d75618726769b73687c2c3942311d6dd7dd aabf4764787d59f4ccd72ee9f7bde31956beb01d613923bfd470099940a70576 89da0768bac765bc7a97f149e6593e16808017e73a3f6f2564ca23e03f9efbdd e8e3d18de7c40a92b23e85a28a412ac97a19becd85e2d17c4ff0df148844e5c1 9b154e0df88590c549b81ec2ebfe987433f27ea52003d3aeaf34229a3550f892 1e188016fc7bd03277b7c25a2647a8f5e40d57312f5a5704dbc5f5acb2ee2328 afa0940e0498049620cce150f2232b52424b36198f76d7f3f6511cd3735acd50 253acbb9a37a9076253afe7b2db8a80ac3da35a71edc9d6fa745fc18cc267777 62584d2aeab4b0ca46aaf9216c36d509d330b5aa1424eaccb2992947c551f491 c2e30b6dbd19352aa2b450b6f50c1db0c8b56a3cdec4eda58e45e78a305af908 b746f824bfa6dfe9671510f5c3ca70fefabd47f3cff9eb04c950bd8ceb52f997 4a724949f3458faab51d3d91d007f4a501d5d2595a22a024d762c7a5ded3956f 0cd9fabde2b0698f8c1e2b018ff5d3158b16a85a54123bba1b25e5910d4f0943 b8e64d56513eed54c5355fdbed73b995fc4f6dfe1ee9891aec65a68f54dff491 dea90f1d3e491d4fd631517062c0daf84f9d6d52cfa14b6db3566eaf93c0c694 b6f0bd9de7eb8b3e4a0736c9bba7cb6fa15c17c78b0759947e05bee305baf877 c4982756db341a24537f817b00767249abbb2c3e68b45ab75b81a2d5b0a3b132 b968c40f04a9759304ff2a4fac36674971019ba7e3b625c7439443b57e5d8c29 11e2b1360fac4da5dd7f60776bd19fd4dd8976c7e43e927b23be6f1936fe6a83 d8de4eae1903b19dfa984b3218184999b7daa85aeed37ac61b8a53bb201b6fde be71cfdfbaa5e51513437bc9a3bc2a3f0c30f68d52ffc728feb4e9774407fe63 f3e14d3a90ff1df9bdb56cdb09a83868405ac36571aec83cf35320849989784b f46d9b3fb03ccb8b0ab9dcd2e3c5b4ad0cd16c33c8522fe83ecf1c357f0fc50f 843b2134413e021cc98a5864e7cfe973123d2e126ac7039d14c4bd4cb7122df5 5a4ff16ab2e0618382d0b89331fd07f9288ae6eb6dae1dbb900305ed2fe777b5 a6e9103cb652ccf1ed1ed0a85c76a0cae9f5a20677bbb61a21aaca416e2caea7 6aa41d317695de74640f6a4e1572901817f55cbed26f69d31e7c825b72e1c4b4 586c48cb50adbbbae13df932051686cf54482dffcef75a1d3470e58cdce62cc7 de799886065ddb026a97a929883f2257a639b0a4d110f61df9661443fb62705c 740a4de28cee0c77909ded5eb711ba74c6c6576e7c7a5918e733f47d11124318 3a651856c4f8edb1c6d358af8e83e780552dac14e9b987fff689255d48297d3a 2f77dae23a227c0e861ef15e5f06418c94ceb903b4fddc3c1099c596911dd582 b5dc799a72152546a0a5b355e0b445e88f761bc675cbb6b7f46c4f7ba87a6f6a 449f9c155b41fc102d9110ed5ed00ae461bd0330aa529313015902afe68f8ea9 3739cbf06bed5d90f5d4ccbdd732d1253bdbb0f2c0056da659728f36b8e8dfe6 c82c15656a7e779bf0639a4e065b2492043833e3b64c46d26abe7d07099ff547 25ce07dd285814299a25dc863fbae981f69ed22fbc474cf32a7cd6c5c50c8777 5df9bf9bc122294775dfc15c3aceddbebbc1df7ded0912d261b788345d42c573 415c3ad229e801f03c0d29976f2ade06a177da3dbd66dc695995273c1dd8e571 ebc3ba1644ab323e79a41dff8c19f8e4c59287313b7586add29e413de1b62605 a98326feab4d89e77922d4d7ac90d7661f295aa12c4f62ce1679c418058adbf4 1d6cb3557e406416492d7a5326a59f3d9499cff8c1d6ec57dbe931d67df28f99 c25a8a0fb9c7ff4a25ec5810f1fb2517da8f68e92d724f2447cf9e18369d00cf 061dd9238ff579691fd93951257ecc9ae8b763d08c2df2c2b15024fb94fddeb5 baaa01f92f32d8216d9c68f18da6592118f1014f58f2964678c07ada8e15a655 8c0c7b049a770a669aa5de93d2ec10a26929e2559fea5c99d183c1674f8ddcaf bdf32157c483f1c16ac29247f1a7c1ef9332356c17e6c30a8e057b7f6df924bd 699fa2508079f6d6db58db3a97bfe91123968e0805338b8166e4144d6f5ae014 bf3fed9f7de28e1eda911b53752ee8875e9afa7deb5ee57f7cc3262981cc3aa7 30e6e498d253cbba9384ba1dbc86bb899d19c953520b64c7dc6cbc302c73d350 0d98f277ccd6f453e7564281b06542261cba05ee79483f81561ae737a9b416d8 807840a1625e553e4b6a819ba4ea149debd16dc6079972d177079ab879efe39f 976bc0d0ff33b308dfd3396254d38490f4e2762a79f11c06968fe24d8ea1902e 11970efee92dc6cffd4cec140b4e55f9ff6b9e5760e95a5c036d2b450762040f abaac248f8fe2137adee85ab86b369a847252cb3bead74ed306094513aafcf3e a86ba0a9f48955677d6dacb77ac1468ec39ce85d5026beb6bddb08a90b17e5b6 dad6d4be8ac3ca5a8d03fc1bb66e1fe09007335a6e83ed3dea592e61c0750319 a5c7536f5c784256a75cb279fe8fcb865719a7614d80bec33702777e8fb1ea70 4f5a03868c15d9414d42970c99af5c339ffeacf39e3172ee69c936d5f99d97de 26d337602a6b5220cabacc0c00c40843634e67d5c880b00c6b5748b638bcaed7 e82b7d2a9f1bbce940979ffa61f353ee2f6d2d68efcd45b31a9eece2ae46ea5a 619a2ad5bb7607e5dda36ee4f07c1c0c93fbac6bbf960b099f3b70d5a3ffba18 9cae41f5fb0af6da4ec6332a230d571d62e8056950244774c97451b5e4db4a043354e797eb03383773867871b6145cd305fcc46896c3c2f159b75419c330da0a114db598760fe244e7ebc3c649c90a87a9e4a30a2898f34ecd43f72515ede907415d1fc44f0978f0db4186f6177e44c8a00fd9a4e27a0a1bf882136372ce170e9234f0f3603963c74903c2e447fc5b9bd9810425ab6a718980b17bbba8561f0160adf7724dd53c1754d0e94accb2d8c0f32e32438d34f3d719a0dfe97086a60f0429e72c8d312c4c6b2f26ed8eca3bc4eb3e7f077f7415db3774694c5e4da70cb0acb6ae147ca28be9b2c8882827d182a4bd12f7b8deb43f9bb860376f7b7a01e23cd7c97f907621dfc331dccd73f2dd544a719e07211aa0c78d7ef722c5f6003cc199336c0d8f668f0445d3842848c7f7bd4a938a3613453dee62fe35aa3500517f6e3c6043828bb014a4fa78164b271b467370ff0531d9d5caca45160d310f04e94bbefd9a71d80c6d62d1615adcf4635b1edb208ff0237fcff6c9bfdfb803e1e41cedbc959b0b4ff69dd12647841bf742309f1f8ba623def7f7d4d90ad20eb769f073f698c67e1165f4f2ee53c74a588ab7c6036987366c569beb01d59200dd03205c93c9a8695379532b2e8d389ff9448bcf193a510ffedec55af3ff320aa8c45687752cb9396436f36d4b77780ec713e7b910b7c20ef383259a6b793b04416e94161b10584cae40ced8c75faf56745ea444ab67caf1d0c0c8db311ba10263eb9d92ce915298e152ef43a924372f4f653c677750f2b90fe869778ad2e40db83d9a143b870fc47e116b78ad1459d9052a22b64710fad9d734c0eb91ebda001d86149f3a5c8413f5b5bbecbdef9ce901bf6e554c5ec6364b1ed3e59d4bfc0aef310830ddb3e724144ac77ad5f1977f99ee0f988432cc472941ab73d471e103f21f7bfcd61fd68b36db40b4893681c7331ccc63a641ea6af888855d5a257c0f9f2137c0e2eb2ff9adb02a47d7456838d3f2985747a1f3b211a74b37b93e7e0f86fdeccc494c7264df8fde0ad87dfea06ab6eb72207d45c729613ceec412600aa196d3adfc994ae0dbac1b676ea289f8f5e1b7d0a4e3e36549b5a7bee3456901d2b2a7017cc2d38d0c6b17130624a67c766ab1ed4f58986cb28e61f67c958e08b2c1e756474f6d90f36377c967d0a8610710096c23d540b2d2d07a7acfeaf00b43e4d7721cbf23d8e53653049f79548b2f61ee67f1755f6afd8f18e83e7d4c03be967b433b658d6315488d7f07c14778d1a3927580ef87b7fa344e05f8010c0fa1efe5a98592fb72f42b64ae436fa7ef142f1369432a7437d79d7769f966b70014a12a906b06c97ff26dd8f1eb9b3d5a24da600b7a753c6cb8ca359942307e06001e39397a83f7057f864e58760a0bdbf842112bf25bb14f73bcb181cf2cc5050c4aecebfab02552cd0fca78539aa7f25295e9ba528cf041c97e4e0e432343078f561d9839d01325ff32bdfbb679634580329643127ff6aff94af8638ffac7087f729e907ac245b73f8c035d7eeb4870e9e8e99b62697bdae39f3be273eecd02df9e37fea14753125664faed2e7b5636f22a5982f58b6821402bbd064e19600b40487b148add058cb6cc06c9388fccf33ecbbc4cf041f6150a16145b26e11c033f90dab76118ff082f6afc383e62c0bb7519a8a2578c910230df2580755192053d5bfcec3b34f5d205d16796400bec4200f37e8dbfb70ee608c2bedf32159803b256da0167e0870541ffeb0b7d64fe520f1ea23a11b5c5c400b86794ae9f6a024c0dd9f4f2beb2a445637fe7f481e08ba5e33e5a3de826f2a0bdcdd85f8cb2028db57ab302bd480ec02991d0de81e85d4bacdbe42b022689bab09ac771ead5000bed2fddd653d19be8eb98935fd5d8587d809ddb15a6fc3339e6520a69b43f0e7b67c3fd9ab813aab7a4df09e3638788e562f4b4ce8ce2b320edd02785db580fd97425972baf49144b44c1c6294476a745eac35fe16716040d508537d34edd0f8f7bb4716e5bf38fec56fff328906f52da194cc959d19f0d271738d26c771b0fbbed357d09f1c4339b4db615307b5d03e9f2c16dd26e63f835fab15738084509e7f4dd0d40fb15ea4c4eb81073655051b689a00efd4aad88927bb1d3a9887802e9bcf89d3ca751c11b0c9d06c52fdcc93afbacf567be3c076e97efd4c0cf5b093cd2212eaf84cf988e690f75a8130457a5e6a8325ad80e111550ad1545dfd80d9cada53e68a3c8348b438085069e76767bd030e7ab966e684026642bb0b5a50f12570bcd4e330f91d90741cdea51487c6533e26c2ba2b977d39f257b5f49560ed572091ab644ab4c53aee51a635a9cedc01d2008f503e1eb47aaf8890d72f309d49384bc7eb5a931802248304dbbb23abf5426f1a3ee5e0fe1395f4ff53ef30deb1e15cfe177c48bbae54eac2c01bf12bb4e07f2309c3eedbb071ae13a707f0977596f0479951b65fa49120091b2862026c669bf4848ec87a884790963c29e0642e9fe90d49d674f308ce734f44aa22e15c7334654cfe95de529f345dceb53047cf3f9bd5b65ca222d21d97dfd85e3a6093882f31f275483426af8cb50d8440aaca8935be87a013e414cef7a938519c5f2b8f3568d1b92d3309698ceb394bc0aebbe1d43bb65edca7b25dd22fa9b1b8512d8a9c21d3bfd3920b6b10771abe200d2eb772cea7f0bf6aa4b60174baba9d2cc76272d0aadaeb7e6c8860274b2fb019f976438f9e7fe99fa6b10179308460c163c14c347dc581c96ead1f3a684a60f05fc51fc88ab76b1cf52429b44fc51bf7acf7cef653cda405ac9a32c5a6cd9001658acfd60b820647e5011afcd41607407f07a2fb973596f8032197deb70a600bd3c3130a2be3091cadf3af563d0c943104ceeff3dfb916d92c08cf3d3b5c9026dbdc4425304e65ec0315039c33dbdae91f73a4e578f49e9edee1ac2d58367034d561f3e131d0b1a0886793a86be31498eec352aff8781998c3bd00f093fa30c41f4014092b9d9257f80fc8c9a3a2c4a6ed3864a27ec228beb7b99c9f8d0af0791a97c7b53be0470f6e70c84d647983e0c59f3fd687e5b410cdb81b510933c0068bde15750f1b0d329e1178c359566f92122a43d4ee2deb87d1ee398b23dbc0fbc3e31d5d4111f36414437d66f065cdd559c30b244b1585313ede7c80dba410a9207a67d7e989c5d4d73c93f0545b2ee813771bfe0ee3d7882d5b04d4e24c807b25c7a4dc8c4957778d96c645f7a6d08a3c0827d065d0bbb78730e5c5e794800e939345ccea99c90bcea370d42a72c24f25bd35f1165fc39aa4f052663f43d015637a13a093be7919c7901ac020af8af7ebca972f8f9fb2270e62471298b290e3011bda9658dd0d6ef1c3f06e61eb40e2097bf1ac90074975247294fdf7b3607ff55d8607e1265fed46f9d417a7ced8303b3445ca67dc6dab629e2f98887fa0b94f8713a9ce821abe2d274b658b0b4869e36c73055edc7fa6a369a33cad2570afa60e34ee5fa8711f680856428787df03e6f809fe628b67c45ff209e75c19e072beff6a9567a114fd5459afc1aae57757a8186cdeac872edc9377aefadc55f05a35fba49b81ade30292a4829b50e32c38a16a408bf8d22b33d560cb55502e309e63d87bf165e2f605e6db016aeba42fc1e027da09409925a968874e28bc08f083bba95b168c4087dadd6dc3afdefc27b82f7a825fa65050eb9ea1a0579240f023a0c69fd470fbb31807be9ce3f3233cbbf5d1c6873a5ddacb4c7950fbb2b8c014352392492923fc086b4bb6bd849cf658009bf4233849f7a3091f69212759b0b765e38d6235916b6a51652383cbf025d1c51b2b9f6b33851d2db8184928b8e0a744865d50970b3a1774d0273ded72b92ab8597a94c40ea5167662702470f75063dac17081bbf471e4e7e8148057a8c051d0c4e77b765b5f88ac0a46a0915f60b1da31673aa5a6da8c2e9d7827be22924ca2789e9a6616e8957e68d289ba4700a06207a5793cd5f1c54dad30e937c80f0cf3270998841c2b2742a287b25310502a977aa69b3ae463f4ff7e76dbe29e138ad8972a84bbf6f0948a7c0a13c1f1f05c197d54f4c2d0c152d1cc95060f6d064541d36560261955e75c084b1a225530dfa92c206a016434b41da252dae9b850a3dfaeb3d51557f139f28ca87d9c416012958d235dd4dad5895ea4f22e14c471f4f506320043c4878828317c1911e0f0e85a8fa4fbb9eb942455202060cf793520b23c05d9ec28af6a1c35f855cba9609518faa0bb5cc28e289efa4f2e9da23b617da7361a8921fb4655452d75b22f400351239ecfe4842b2e0c1b763ff09303faf3e037f6c78a502dfe6d711969a1e0eb40753e1aa881c6ff71db2b433af0be30821ea1268c35c14ae59a4257f260604fc1edb9a85bcf402d2d3f4e561f4a33cecf32f8a3ee1e0fa8fa6cca35cb04708a303448aee9c3849fc29f43c3470ae7f0cb26ba630668ea29501921ee248f008c02db846e6d3e23a45c6075df34a5f5d934df5f259e9624db97171bbe3aa940de85af9ccc2b5d81e42217e9f01fd245075567362ab42c1aecee164eb68d5f00ce67c8f22c66be527868e6b920d7c2efa6b6e9d37b8dc066881caca823ee5dc0965f67300b7b39452651c694c6b591ababbcdbfadc7a65d55b664bd05f9bb8a07192bbbccc2209a5262b20e408f225f16bfd12d46da88695cfe383b04161a9c0b11e1c8bb0b5560ccfa4f2229e27f1cdce76d0eaab0213ed3946e2b62328c280e24a73a79a7a5e3700b90aa93aa38db587b2f90bdd7e4b7cc85ab8070fbbdc1073719b404c816b519f107421499e4faca4cfcbfe825607efbe4e95a0f35400b08a1157e2888d95d099e8c0c2b64277fa5e12adeea04fb620f3fff3d9cd39fe309675a3939914148c3249aa1b482dc4624057061eac87bcec89dc4e82a42435c005200affbf9b9feee74d75f28f14a84279e47ee33a5cbf5f8d505407878ef57091472203d4317377c1a8daef8d058d1e85dd823ae98cc1df1a57734a6bbe55f0673f6fb0a5701dfdbadd60ebe9249f24aac47815ba3d3cfcdd27482a5e951900e9973ac737d4af88b9b371848e90e415b14e8ec97a26ced381d7c8bf62c97770fa5cbdd00820949dbed6ebdbab8c3af3824001f540f437f172905392fa237040741d3fd94f55b396f7f64b0b8c55dc17691f01bb3ca24fa759f853c2cfc110104d82f94772c8c67a86d2947df3a2741b940730f4bd2429fc87fd92662f766f509a925604ea11b7e3a0aebccf73c674be4f7670c3f6314a635f166ea316b6a1e0c7e43b0c479ac33a9c3956f71dbf4ee0fc66279501926a780f7a3cd435a563909d8cc5c6a09a4d56e54b8fac78b82bcb57e0a016a4ceecd68951ef5f9be668a09baa4ae74890ea2feb373b10b6f2c3695d5461750486e8fa9f253baaccaac5a03a2093af3a7cdc5f4dcf171d8c4b76be70b22dd21a7b6bc8bb5f9e462b3cda307412996b17830e0c4414a1ebc9a115d0c6f30dfa36c000d8f29dfb02798316d0b5338aa991ac1e67601bddb53068dffb4bf24297aa691db94d56fed1856cfc00fbff8da880df6c5822cbb11ddbcfa333ea87340c684bc925cae39a916619aa406529409aebc024fa071b0f93dce64ac02a6a6a5d20a418ffda5ba9f51f70e2a057cbb08404aee0e2121a99da2f762992d39bf37edc51ed2168b72a4525b8b9808ead36ff709b3164a0a380da9e08ae773a76349112c12bc1e07245430201c1a006c329ee8d4b6fb4571b9c79fb84a27baffbfc3641cb531f934ffb820db8db80c45ae99bc48c3286646fd50a51afeaf3128e5a5a1a47681af41e7841d5fa2d60ea88748612ad06c33220822b993252f98c257d4abac7eb3c8c5f5ad554093d40ab26ea86660c1bc9886238a10dc9d0929f70c53089da841a5d0246d2363bd2c08 false -check_ring_signature f2da8044b10b359b22acf1d611da4af60d01460b65833b5225c2b8fbd90d3a6d d588ab399c6e3dbfc8fe421f75f45550d53092471fe576925bc983ca1162d29f 2 d86b1f333815de17f671ed6aa0cc78d506a5d7dc92319cd361acbd353f5ffb03 aa61c4a64c8e8eac110b9dcb5cc46d8683653920fc6593548c0e439570ffc1ca 3a888311aa8a4d91549391f5b010f94eabdd649af04419483f8b6390b6d9442991ab49941bfc6685a278f3cc057d21c73a85ded4ba67088fca9a6e618ccb230bae0abd5d10ffb3c0922cceaf184c909a927972d14514f5d55d4d2835b0d961e12388767cb806e449f626c7e39c48e145704c3ec64db249d81c3605e059406f06 false -check_ring_signature 61e9a6e7a98ba764934b8399cef2fbf94b3173c3e4c3eb62cf74e9c210fb4c96 50ea28051aba901a2135cf18792892a7bc0dd606b1e058903bed6f51cdfe5fcc 1 3ea5c349b575d446e67d984002246a9ee55b24ab07584406989da8b8b7a555a9 791bfe228d9c1c1788a0238dfce844b5e19f5526c57f4aae397035960a45330ad8d015332928b70bb262237eecf084f01dda4994784c765d82c0347e60b9d306 true -check_ring_signature a0a97627648517e970c16bbd591b86ff23ffd0d1a71875f1d324a28e9082e347 164f3db29d1aa6cd1ea2922f6d00859ea956e9fedef923659eaa78681ab790b7 1 50ffb118b12e06973cb31ceac937d27cf69ad09bea847cefb282bc4882290a21 51ea086d3e05e4a26a6b5087d74799fad7adfd4b75757d541192bce555742407faa638674254c581eab6fc82c7aab12cc53b3373be31de1aa04fea36db56f2bd false -check_ring_signature 4cf0c99bc452401e7c603eb0e250336bdfcf0997d6213149a46082966dea7db1 c3a9853339e9c6cbd69316753c3962aa5a75fe19f97c09fe06ec869d67845331 29 9ad95ab7fcf01df1aed19be7984c8018a4e84027ea88194bbe777dc2347e2d82 295e82ba2130f7617d73cc5fba37573b2d373a93d4827cde441f423c7d7f90f4 b8b4fe67525037df45c9f85f270b7af06526a53ccf1cae660026f90022484f01 5002749ca5a1037d97eccec8b9bb3c533461dfade026058ea14f4253040f7854 579b0b38d3ae03c92da4f24c2f19f08ea445b604d0e22027cd1d2dab858ff6f6 6eb8a98196f36bf8f96dacde853403d65172aab3a2c44696a072541fa7e5af65 f7021076b63283cad883c6a696f560a88b4717caf7962a8707cee6d376b5036b c05eb133546dba2e5a43ba829280ab7cdb9c55f49e152ce133cde13e4fe4d17e 93f836add31dca6bfa300aa8a26b89e3e56ae3ee83748dac14c4f0f84723dd8e c73018bfae23fe9b4f4d54d4469478d1f3a29365a3c133f18d26fcaaef9195b8 7372698561e081813bd62f8fca191ce366790e6665735ac1105125f6f96aa522 b87e49bcaaf0f7a2b28ad9ed4b8eef53573c573581b11830a614e5bf51ba6ee8 39dd823cec91c075bd2b92695dd904cbfe49f5c0b51ad4f48c9513fb7326cb82 84c2f527802283ffc137affe3c5c48610f4f99d88cade23b4613db688b51993b bad137477d4df1f72da02b9d0364806c2259a8ceffe97095c41acdb4e1a2d542 9906d7400a687266d7dd2137cba7dd266806f451faa5a42bf4ab6e39a41403e2 f4d853fbd1def92ade9a5421e54c5a59a10efc13c3aea9b4ca74bc05a640c485 e3dbe3c3406e56c2e902b78728dd7f7361cfc041aa3e2e26c48379f6e93014e5 a9b9c6964ede91b8f0943d12e58daec5ee21214dec337968c8385d0a6f97158e 8060d2c7c86533f520f2fd55eb2914233b4eefaae7957ad9c848c21f9b497432 3833bb634070b6c1186ebac255e7ab46948220cd81a949c5bd4794ea1bff61f2 28e43258f12c81e6fa40507846a4f719a818fbca77d734216a7026a9d01e2a71 08ca66aabdd438c388f32036afa75ef9408ae371d3d55486d0d3c7c7e4331eb0 890e1aff6c5a3c8d6c775f915f40e24f7dd236094a9311414afa21a9bb4d920c 509e0d37042dd94fa727f203fccfa6d2191d255583fedd747081b023f631e7bb 2321667da374f109773b0cb7b7adf2bcdf6a85d2c4618461e86d34389a8ced9f b4eb2d4bd1d0e2856920de2ca2bb7abebed662f9f270129e61d627d07274ed14 0c1784b8d2db95226afc89eebd52dae9f24dee66a2dc34d07ec469c8c3485442 1661eb4ae1bb2dc9a5847905e4bc4702d4a32edc3cf27c8b562b662e23721679 41dd204e29d994fc74ab9fd1a437e1a116aa5e457b7f323e33aaeb300711260eea4b3df2d53111e5eda2eac6da669b375ed810504636d974e8a7aeb36ffb110a804e9caaa17671a9c607a6eede690aa6d9ab07ddfeff65bf7ea5aca2055176089e4e7769a7e3803972fd97527359e96ae390c3b0948a8c09a0d83342f9291d0a06c1ab2033edc8bcdb4061680869329f4890c10bfb23c84aa09100764827f60dac1ad2f843f41c9976b6e0b9978d1ba694b484da746eb195bf73025978cb2b02f0fd62ce64a02f61629b921422c25f0b7615ee268676a5f27fa352131ea96a0d948e7a0885d7a192d74a2347e7599fe055951f385afb37eba03149f64ee213038121c2c887d244cf718927d5e2e30dbcdff7d84ac77cb991d2b8311f311f6d07ad936224d648ce563efa08d716584a5628d4a90c1e09669d3a73ede861c02a01fa0d3d7c14775953c67d32df8875b9c6cf3aefe80110215d22e36fc8bf3ae508b71dae973a7d1cab865a6436e016b627ce0e12e958d4a2c29ff6776cb08aaa0b690d625e86af972a8e8c909a505d0232dbd8aefde7faf866670b9092fe0e6f0632897015d2aa207444390c86ee4e7f8902a8148efcf506a8c1b5a033a7290a05fda569d133b0c1b82895812643ebac491d96eaa42552e734a0b755d48f1d040ea1946f05c2c8d99ad51aa3a18b1e0eb4f81f299eae36c156a352072870bf3c0a56ceacb72f579f9c92e6e70b603dac1ade3572c840d10144ce6247c142bcdd0bb7f3353ec500156b20773b7ace070ea37831ca8c35f704dbd707bd839be1430f502c64ec2dd5499a39ef084e59b26050fdf119ef77585d5d88ab104b8e5cd809acd90c7180215c1243c2abee6344204097b465817ffdd21aac890038a58652064906d3fa4d3e40c6d861441135c959cb32d3e01447ff927dd4b43a0a6c10060f7a7ed3dbb3a4e955cdabea4adfa7deea487e31eb93b79b2cbf1edf8262ff1a050ceff45102ae763d3c21f1bde81b004fea0b9eb684e622982e29f6e0b87e5e0400685d25998485d5d617be6782998ca3537afd3a0c391cbbb358dccd926040010a7a314a5a5deed030860d9de60c5eff3430805a9bb34033481d0e79e921d405493aebf4c6cd032f767b6f870e675b66c34dbe84ec0b3e23fbf7c91eaf0de10363a73d429c290bc4380299c74dfb213aa60e7706d56e9fa1de9d137d61ae700b3c7bf44955bfeda02d5dda8c3af282bb2ec01afa52fee48f3c6818d28f5cee0749889ecc82f2bdf204520896b278ffe2b2f0f0fc5565e2b83400cf098105550f8f4463866c7b414278ea85acacd398b16d1d71774f25246e5355117ecfe4450c6ba834304bfee5dc1049dc35897c663237b1fd5753becb4fcebdad11555d860625fd5ae30f11f6e85a187fd49b7a1bb131e71ac0c7459732602a10b6d8b7c003e35463bf9a3265a0e42093593c453ab20162393d2e1fc249fd6822a49c108607485be7bb4c9714c0eaff6ddfeed59d48a90d2b010470e06f500a326bcfbebf0666f99ee2f8e675ac12066b5b9f4cba19778b99be0bd69c8c0fa7bd7d1ba2bf09b459a91cda2c38eff79713730e79faa3534233f6aa51fa47e82a650777f14e03c5e1f57fa28c71208e1ccff56f29f9530cca207e64dda1e3a7d8313496a1960f9acda5d0e65807a67ffa8f4661bbd0ce9113f2b381d44219b72a0ce681214802b14172105126c69d5b62814c697130aaae3265f4765a69306db4162936cbeb0a4cef7d776839336daee1412b356306cd6d23ada7aeec3e4bbc2f3288dd636a008d9218246b6653854d5b6d7609376e2cc3f61fd0f28a612caa3e7fdbe1bf2701d32976c1521edc2fece81008b1039562f0d365f4ea34d7eac085bc5f251c9509daebb9410487f1bba8ae4c8083e46c549baefb90665b56787a0dcc4ff23d220a9aab4a5febafe756f2f3c7ecf2528075791662ed25a556942ad6619be270db002ba1cd441e86b8614697ea3fe238cab3a953fb2672667da225f365acb3b1c00c2bcccb693ead3f2c28ac7de727ecbb54b8cd2c2c1ab461eefda6f999fdc0750aa071e164c0e5d2046c6b4bd30efeb76f487c518cff4ae356a0834fe735f95f0cfa4c09a50263a979d96ab2bb911081599061d01b080524931b9cdc7d9dd9d7089192b8fb97f77f9e4b9c941f06477be3a7af97c53ee3a37f5755a40934588d025cbc7e751fce1fab0c454c74489e36a1c9f14c6cca043dc30f09671e9675420cb1d254d9d57ef90e3b57bcee09a07f66b8b273594937f1c51d8961f87d9a7303610619cc145ce4c60c0f2caa92cda37a1bf8000ecbfdcc3520a768b5d8a510041f5dc6e76278cc5d723364b1b30020fd045c64bedaba78b77552873c0fbbe70f315f7f43b30f2c7698c6589d6c9378c861c14dd7d3d936746a25c139b4c7f30a28af2de6e0baeecff06df06da9fb1ccf5c1e85d5f3fea9b507fef4d21a9b9f0e60ffc0a2f1bfd48d2295ecd5bb218d4e1b1819e989d3989d15bf7d556fd0eb00affda80d2fe7e91df8265ff2529daad4529c53a1905ab464aaf08eeb6de41701758b6da12bae32bf089efc1191bd59e10c0bba6ba4815090cc3fa1a8c449f806 true -check_ring_signature 17f59fce217e716e3a57008af350699440e1a7d23e5d0ebb74c692afc5e27b7b 26a89af1bbeacba12158a75eab8d35cfc524633ddd39fe8074c2a2066d8519ae 236 c26bf72b3cd043d4c2f6ac33a008caf00d1220434cbeb4f4386c454184290fc3 50d846793e4ac9c27f5276c5e562136936d3f19045cdd4e9033ce3633a53281f 5a7dcbebf400a7ccf3b2974f7539dfde6c83afa14559e534499a87d1f71972d1 17f38e7e7adcd3d62f7ad9057bfb02170a354e14be1b4a4bb3ce0b84c59a2cd5 1273e2a98a53d7fee045d17f86fbfad1726fbef93b3534d11e26a72654320500 7eb35aabd9ee1c1063a361342d24782694369e219e510a01f4b63d0cc1ee34c2 9eedf28353794eb2bf4cf5bb440972db8b48e5b92e2e7d7f5b625bc775f3220a 9c5f391abcd3dc79839987175680e4f29d7a5ded0494717c3fdc120ba187aa79 1cd9bc35347bdf651c66abcd3c82330ed51e5bfe256e39144ad17e32cde1c152 11a7086d1450daaf7ab65fe1f13e8e1a88e4bdab1cbd3b13a0b9c92349d284cd ddf36402ac554377aec0f9293fc7bc758b418b0cd5bdb26595ee38a4610c59d4 128b15ecd305b0c8496289a1f4f8e1924b559d82862c7d588bb69c1797f3d702 9bc0a1836486f0713de60eafce113471135d833d2d327ffb240b97575126c34e 655f45e719da74b0662305113a02440c8136721c7413b7cb459af3aec438aa02 178c661d446b2fb20632ba57561ed25eb67d8ade978378243824311e84ffda7e 2f92654ebbc4789a8259ec20b12c344c9fbba4fc7343fc6854d788349732a904 0989caba3533b57efc5cb30fd53500cd245e18d8039f9b5ab156ed7810e2bbc5 cb1fa92e4e0d5a38bac9c4f877f68639af5bb3bca674fab4cd2ce96c04545a2e 92baed360fb6d6a0b939ce8843cfe220786e8d1c0636d6bb71b09a721d834fb7 6a4b16fbb6b963cbbadcdb500956122999ab4f03235af133bf58abcb0600903e 5cd62dd626b2349021dcf9d0d1f06862d004e95ff2685ce931e630029a211435 c5a9e2c77b27df422ee2838f4c1a8a8030f28f02cca90b3bcc88dae125f531e2 99d207d9d8ec8622ac499882888b7f8b61ec6cfdfa06be6869a16d225086788f a7175b482bc651f78c47730498c9bc754d2d74b8bc8e1420a65471c9ef7449e5 5f0c0de71aec604416cb521de667b86c7ba56fdf868c46bfa06c570da7fd4e62 24bb8bfe55d8403b8cfe7ac4b8b98ea3c86b1d1c2db80f9567c55f967e514c6c 25bffc4dd0fc374cc25a65ca226f0955716f05e00b5d6cec5612c997d52f258c 1ac4b81e222658de8e4c47381f1a8fda2b46cb6eb850abd38542192cc075c154 129a5e76c20c087baabb50c8b82822d3ab8621704fc6a103aa8220ae71f28766 e39efcb41f8daed0eb96269eaa10c65d2435197e9b07a7b1caa11ef20fa162d5 204de019824e1d549e304e954af7fa7dcbea8d8042323cb4dd163aa007ee73d5 29321a2f59a2a065aad0af295a93985157078efcdc960c75f37ff4cdb7df501b df00e9790e479f697bf835869018373afd4db4cfdabc07f000c3e4115957301a 9d8b3a4e321b69f289b4dff34dcd78e0c23fc111b5a7c3ba5aef05de5c6cb1d2 872e2caf8ff566fe6813a5f54dc9d402f5fef01f7ff1da22a8eae40ea16d088c 540989394ac401eea584b9fa49b9535f843d7fd952873e6d9e98438e5418e670 4c59b2250c3dda5db3b54912e29837f77320b412e1b97835dbf8dde4bb21c399 499cb5090011c871b6b65c8367493ca95c8a7a1317abcd8aa1d9b7d1ec61cc9d efdee07d6cb6d0e892fc4134cd0fca595e1d0e8742c71abc58adaf667b61ce18 b28f6d9fd3b58ea5858ec1d44e64af846b01e7438e1e2cbdcd6722f51e6653e9 ecaae8479ddf0253b59223c6ab8e7749d422fc0ba1298ea0f1e3a3240bec43f7 7488dd8a7c58fc18415d3119538d9ffb7b48563fa73016058326bc70c7e37a00 e603a9c4813483f159f2514ef7f29ab943419fd2807b3121d884a0f2c297e7bc ab65edd3d0528a7f9b08bf5cbd9df2b37edba3d1aca6d8bbd558b9250090aa88 0f03ad9e6eb665e0c6c3bf719e3ca517428cb6ff3b89a914c21877bdfc4e101a a86401b71dd6962f78c68b5892c8fff7f4b80b8c090a4395547bbb1c894bb0a5 aececc1b2cb3de5da54e6c9db12a47296ce034d5af6650ef71c02876d4e5e335 eda3d5a421d210e07abed55df0d6ead78d9384401cfb31f566995dd1af863259 563290f621a402545df8590a5eaa78992fcb9128d34290b1848c9ff4f7adfe21 f9e9198a5f87803734b9a20f3320fd3c51323488169d3457f8ca7f35fcb4df93 9c49550269394bbb6fce18402e2d2cbe94fa8a655bcbb70337fa3a51466c23fc ff10d0ec73e47505e284c799847fd2a4ee0cd4a7cc5badeb85d5712bbd8925b9 5886a60ed41643a2a35e28c895db7ed20095cf6d1c6962d22002b437edff89f5 e9dae1aa4505475e2ab9134b176db99898bea2ba80fcc5407229e8bfc9d33890 f31e8f66b06ac72ddabaddbe883c554a1dca063e53a3e3c7794d2585fe126f1e 8dec335fe7ece73094b6b8d845f25b9ba45c78ec94f419e212bb7c13fc35544e 6fed01f26957cffee7277d2404ff472ef9e55f80919adf8017ff1307d58dc6ca ae139f15dbd62518bb0450418d41e8b154ddc903511d102b5d03e9e790f83938 60237d0937188ffe77a38bf864aa85aca7839f31ae148ae99578d0de45584396 c1a8feb7dce1f76965d38af983e05ab5b4b879675fd28f49c20237eb4f16028e 8b35d4b0415560d72e5bffdbe562cbcab5c5d9b0c99388222d111835eed2ea3f a5b1558661d96b76d989b93fc0d469082cf43a93ed8794f868f01faf636a3582 d7ef112a2d496efcce0b7793f1d285a78ddecce9ffd997fc3ae66eca3e898426 d9283d173057f2f74eb01e71fad2ad1134edd4cb523fd9aad10ab429e3a60464 2479e4b555e7d4524ae9fb36f19fbbfb9e8d17c5c796a0fed49ab9fd2f9470d3 00eb085416ea4961466ded3f35e04d715a1227f512f84b831ffabf0c2e3d39b7 c5f843545d6a2a985065b7d82a9c08f0d049dcdcebc2f978efecb858fa295023 c7fff2b921c3e1f2b588fc6ba6c9a252fe035bd0be9d96911c7b197dda7e44a1 6d30f43e71dcc335d09732a32392528d45ff74b34936df17504fae342bf8bfb8 3c958a36eb41d50dcfc40ed4130d372c4b5cfaae552de786e93e12ac616abfca 49bccdff6fa1515f85937b6041a9046d7cb6eb28b4b94bff013278578ea1e526 b5e13dc9b6682abd8bd49d5e12f7d6f434782c750f19582466acb996d4695e8e bfbf1b80e0635eed3caef5e724a1a85b6bd965c90d8862e3e382d64001fdd330 f7207be8974ff94e45616d04b7a4a9ad98fd81def0798840134f57b92642fe1e 2748dbdb6103876904d30fdbc286de22c376fd809923921c251b82a611b3595f e86eef5601cd8818b34bebe94912d93fb055323cc340991a6cb89f842bfcdcf5 dbeda139cafed4d9353b12279de380e6e71d36cb0e533467410f4f4d5aa16fc8 df1741773870e35d8b79259feab3a00f73fc5398900bc61c87a85c81ffc4a1b5 eeef12f2faebdbc07865e0678a10bca4f992d77e0db93ac6537b6c9dd6943d73 a3d71da627fd56a6cd73ad86d3eb4b3af0695fc2f101e67feda1653029f23e1f 95637575e6bd1de8a80c35e0913e032ac99ffc04ee744688aff151bf8e742744 c62ef74759ebd7f959e91985f2cab2a7e85878c58a3c11eb02eb8aba8e0878ae 321e69afda88d06e067b924a618dcc579327e5e7dedcb1a7d81b1e1c8b6ae2da 0d220eb8bfd081ded5c564ea6446d4b0cd7d4e0202cb7c72785b5066896aed9b 2195bfa14aa4f84cf82b4be9f85e1e143d7bd2b39a91510cf1de7af907d8dc3d 35e80115388b5645ab2a09d9c5539c86bb330927c2f449f177d521cf02e53825 fee07daefb71f119d8186699f0db1db2db34f658dca3ebc6135e9276594afdda 039e7e92b7272424ad45a09b9a17e4823d17c857cb2d1a1aa6a06ee593554923 d992aaf55e9fdee8c74e98d4e34c196a7df9b4c43b8d5ff5dce00c7f2c84f048 389d5d195ff16d2fa36d5f484294146bcc117f5765f0422267e6a0072330ba33 1292d20ba92e2e428ae4794e1373c9e0c711dd2ab01db16ebf229b27f064f7ee c7de9ba7193d199cb6f25f7fe7c731503d68203f6a4cbd28035f8468178a0bd5 05341cbe320419814205dbe2d2cc24146cfef0e15c498d4bf57960fd7f6e6efc 52ecee5b7440cb95ff9efc63086a64ccb476b49869546d87d5c61d929d0230a0 7066228371dfef97ad160f4dc4281a9360e9c53245e97be4e22d8bd42010faf0 50c37f9734c97af7a624beaeab588a4949ae2d7a5834f72f3a00e6e6b519ab31 8fc41c0e6fa6cd26ad14dc612f9a0f223dab3201708d4e6ed2d2d1249e9db44e 70da3574bff9f340f1de1cdd4dca001aecf8be954bff72d0a9c476f8886b6119 db436ccd70c423f55985bc5dd559045fdc581afb909c5f0e2ea82bca960009cf ddc1eb44e4a3b7a8ba2dd899feb8308bc5fc763f081c12988da1bf64376cd1ee 48e61240c21abc236dc7335c52493c54dd90aa81dd031d024a79ff8566ca6997 cf9d3c201c2418dee409fb5c5e9b6a5f6e85587ddd1d2b831c44ba58d63a2c6b 8095d9390c38ed062ecb4e78435d86392e607a53b9bd8c97775197a29bf8867f 608678ae7eedd0b9fbb6c9645b8c25fbf7d89022ce92ce6ca78d4ff48a17484e 42f201fe879f08f939ba9d5bffbcd0632152a5bdb4212ea4b0189b305e611961 246e4e7a2f295b801dd47b3de65f4c1fb10c3267c6b33219df4780698a953a9e 8844b51cf869733817c9fd03736c7b2a6a91aceafb8a1519bc0144c82ec94540 aac56ae7f85b7d429ce0becaec5e98c553777f35bd6bffc6cb3e06758fc27314 187969e2fd34f1941fa1d668e5b1cf7a7fa207896bed1d49f2fd8b8bca26ed38 83bdac9b1118b4a891a2ce6cd2946f9275c6ad6f72fac14357d1ed948a98c183 2a1c40f0ba1a797eded1fefe0bb536c13495fd7f423b49a3ac160790a66b985b 6d8341464570eb6b83ce2c5ac2c0e9992d4f03f21b83c5a54cf96124df7a8374 bf3e1754675624b5fae634700c0be3ab5738a274890517d15c240e762d339af7 aa045cd1b2603bb9e223bd11b6e86a5bbd8e0bdcf878193fb726170e5cfa2757 5364e2048195dd8ec36040a5430678d74fb055d2e3b1d612d6d9f6fc49614a3d bf4227f83d9d6437a41adb847bb61a22211d62dc2d8ba3430727e33b7cea5c02 1ff62151d170a85b60bede52dff337b6250abc293c12ed2c026f7919bb622f5d 78bd54a63b69b38af18644c0c5b8249543fc5d054361fcae629ee5b10e995bb5 6d175bc085473874f057d0a3e55cec3eb2ac6bdfd969d474b9aa5f1fdf4cc7f3 b38c126d5b28be60f0b7a432109a8f99a4ef4fb525e3ffe1f8cf7825342cb460 fbd1755dfb05979264348d79edd417a040e19ec1b7a0e402b5e9189341250567 fea8073e8a77c2333195513b91242c113ba0247b49081afed10b11f8f2c136a7 4b0f7d10209e8328a32d9f158c8cc3856ea0e25880a0cada992add9300a7c993 279ded90d5e960248648f7baf61cfe77a2ec776039b30063f626e34ca283bca2 1c6ff526d920b690b59981bdf28b1cc5dea3093e3f60e2acd660c92cc171a358 18b487185a5c8c255a580a6900af4650d8e3fc7c441bf422c36c68092183ac2a 261ccefa0257a1f4f0fd264274518dd26d59ac2906bd0c7e6eb36602e9e7e666 aad4d260ae4e5f057ce0706312ea44acd7d8a482cc01bdd7af62fddf0c70ca83 532baba94a857fbe458484d8297c3a5be76e6ef37ab596d5663ae25fdc1c52b7 2260cef29241b664b873f06a6c927a442e4ce8585510b8a395c911b15433ef5e 4f5f944c97c75306a893f4ca7b10e4b4bcde40dad5ae50e410e4f2514d5c8e4d d35475482284722f7e53093978f8bd430484a51776cd18a4ea2e6e81f4a34807 6c555fa9a971e8d01d7ae94c98a2bde0193bcc7936d728ccfe69fe1fc5256648 6c8d8dfb6244251bf9170fbc99bba7cde16e65635eaf6e4806119bc792c794f1 ea7652bdb2565f6865ae9a844dc7d2e22f95bcc0a3ae6d51b0dc7e6b90c785b5 43579311e84e8e8e7ccf726d67d4c1e54cca70eb15692b58e472c1c464f3f116 9338deb5f3d49897bafd1c644c5a7c82b070b5e7c5ec083c6f62d821f81dbfb0 8ee55ab54d37c15d041f734998093b8235fd1ab8bd7942edbc08a97d2b69bc02 53136deb88d3ea0899851ff1011eaa5c572510f987c2547b91b332aa4d0461ce a3b5afef324e7fe91bf72d8002335f1878c06617cb593d2c7ebaba6758988046 dd07368579e7d8b0afcd1e2a674352baa79f0d01861bd433d883d51dedc8c977 2348a11548282df88195ae8e384cd05efc2c933f65405b0a1059df6f2c4d0a94 8b45db3148d9678abf4085dc6d86b55f1f7a611e1884b5aaf695bb8afffcd4aa a6d6ee2186d210cb78dc92ecacbd03e500c6f68c7b6687131189cd399f710e49 55d58ba41c5384f8a5b5be3b8eac70fd963592d4b9117f6b04b567dc6670ea79 fef2fa9520a3663deb72f57f94f76a06700ef553b8251772a50e6e2a2bf8307d da7481792b5dfb4305c4394089143e9f81220de2ff5911355b2ceda5acd130f1 621767354bf336a83ed2e117fb9f0f3c40302a254d842efd9b018d6dd7119f44 8840a6bf330089a73657e9d0c4bedfc25a361949eb858817d8d7347637daf698 8a176c35749310adc330dbaadc352b0724a3eeb2ed85ff62617981ad1418bc4a df95ce444278a2df82823455ff03aeaac0cb76f508eff966d74ee34c95e78fe7 317e5d4fed435cd5b69a268f486109b5fafb87615eafa2c4427f5ab3f9a3d481 010d34d627e7e327871591e89c2df2b583f941dba9ed8faed7ee7264315f275b 681b4d9c5516fb78760f0e9dc1fcdf7a516dbd2819501a5e6789bbe4801c9b7c b2344db72bdc5e86e75d3a607fe81eba98bcbe1f31ede4f8ae03cf0a09f9a325 b71f4b941ca4cb8fdee169336d9419179e07e79039df7d31f6ea154e279e5e1e 098d3c0b6d30465cc8fc396c10b39abc313ef7262ed8e7cb237daf20068600cc c9907f129caedf5b9f3c03c1769c848e24e030eee7387445c27240773c41aeb2 8d4d72eaba9fe1767f60edcd4f00056c0c76473d95cab4f9cb7acf5f6e23e4e7 cf68f70fc59790eae1f2c76f7c68c0e8029da784a572575628e68b749ef44810 4ade9a306a2858f694e7409548045a7e8208186176b5899bacdcc14556a8e82e 1b4bb65b113221c289e979351cd73128c1557c73065cdc0bacaea6e24f75a60e c3ebbd6894d73b454e60ef0742c276ee62b74d9b91aebeaf5d6cf51faa634f0a a45eeab6b5cd0776936cddae0bc0a248b98ac061f4f8dd4847f6a725e13a50d4 23aae9b4adf8a0d294565bcc72ce63fa20e0313c5fe8c2d1b6cf68f7d15d3b2f 7d612c222336ba7fa4233adcd213b226754cd58fc57760603ce1b5e744d42d45 84f7d5cf37395201c2617cae523bcf3abca78e25854d2c8b8f238ee7c7bcf848 c265704695234f07394253ffe2f4f2a6392c1a232385ee7127d4aa914b984b32 ccf1abd2d4317410ec8bfc5fb142b1f65ce71cfdc22652618a12d46259db0a6f 13967758190ca91bbdb9b549d5e62e9c04cfad479c69365e360bbfd5969f2c4d 86138507f1c368fea31daf0025a20f291e39f5fac9c54ac3461bdd0d9e7b0ecd e54ba7e16358f2df1119f7d01a139bdf4a850e2bddf2a69315c63db8b44ca377 53c7017b1677f9962a3260bd6253d8ff1930de3822cdebb007d1de8e68213174 e7c029a6cbcd3eca90a73f2d0283fed8ad0bc23a3de25eb815dcca1bb7e5e37b 842f04911b40570b700992e625c75bea98751c71af81c15d8b65d950fcb55a4d 7232c69c55c457715063a2b50e5fb42960e5aa120e5d54d9ff9bd379296ec7d6 226668ec8a842e17ccbb129e31fdf1181f2f271425489a43d400aed9c5779908 c445101b3ea11b76e0799ebec351cdd3af64b435301d427c9048322984675b76 b4a4656cbfee2145adaa377aa4285e28e42817875c215c4340ed93e181be1c10 73e3773b17ef3ec8086cb5faae7992da315fd9e6e86bdc9173184d964636cef4 072dd4a741743af84c6b1461f153488064a79271d281f6fbfb85c8c2bee771a5 083423f0732a5de1ab7993d7f5eae7576288c47091913bc7224a95b8995b72b9 8d74e51a876899a93ae35b8ed6063e8499811d261d7e0c18c236ba7b5c01b9a4 cf363af0c90abafa18fabc477273d3d891c1970e4c0803393581e9312e964383 12972c0b0764930dacdd37ec6eeb1a287a96129f1cf30f9c5a4e85b550c89764 e9376b08129e26fd07fb765620f871f0e76bb74419f741727f44e742a8b016dd c16a2843feb8e50e713e717acea0be19a9a267f7352b6c361986f0532621bc13 3d597cebae93b398e9ca5a0c15b58a58337f1508edc7e989e4dcde57be120d19 7709dbe0f11bf4753f8ddcc37b7c1a19d7c29a100f2b56b75aed111923d0770a 81f1b56d5468909958ac27f543087517753574f1784f208762f485c533df3709 f4d93de8b273202900fc95be9674bea33a6316a0ed5e1d7bd1ee2424be46ca81 8ba4ef8b2d7d6800f2e914acca320c7830f3e033860162354c8700c6a0bcd0c8 b0fb71a96bde0a4b5ab8ac70d11bd754a52aaef5e0f9085850c8c56bb467e3db a37f7bf9b65a984628d7a41ce8aefed693db5175d54f2ffedef2f24363c8938c 22d0df23c29fa0e005e8bcd9fbc58fb253cb04fdbe910492b029815c75d89610 6c13f520035545f6a3d81010118414769bbee5132be903fc568ab834dbe69941 1199a00f48fe98c386bfb8fe65af4f28258c01721babeea589a678d0e4a09502 4ba30b153d0d673fb299c01d640916b416970f94767cb2deba393dc0b4ea984b 2ec57a7936a1cdce8abb31478fd34ee350f92faf73d28e2f83aef86c9233ccb5 18a77d429dfbdf144049fa04a120cea013c75da21f186aabd1cca5cf7b8df2b2 65bf0973d8890b04e06b0c6360bf99c6acce2e9f204872c6278eb187349dc570 f3dbb86729e866ab00b5aa295f3a2983a14bb50922e4134c1d1522c1fb2e9e13 4493cdc49b4960c6f2e6b6e30c3267b285a9370ece29641e1cf7b0b1e1a5a7f0 3292cf1537c9e6494e5c2ab4f2d3384450a0a3f1352b6fa8ad3bce95f78c4d0f aa418134472d775ee36844440ae5d8a276fa4b53b4661a24faa921bf4782073f 2d4ecf315762c2a34ec11d82a7e29c83587b4c8f3749c729b707124c1e005e26 eb9c9e3d0360d3326ea76fe39549e81252d1bc160d45c5def12dbd414d4f6cb0 36291d8646b27f58399f87b332d225316eea47b3b68ccbfe2dc3a645ecb5ba0a 1a85497741893b439d281c41feeb9de023758161aca54f57ec9fa40a01ca4bea 33fe8bad016efc7012e56e69850094c1bb13887e77f5a9a12e497b660a4adb8d 89a7c45f2689b33212dddc75c6fa34dbe343bd31a86dc3688995049ca7978da8 ca97326c3b186af36e71ad54395997370355d340c7b21599269178ff57850a04 d8ae78c83fb24c8dd6a5c6eb0a46096e69602ae0882491082bce22b12473443e e02de02b87f16ab79e790028ecee6043bb1033d4c34b2c474e64c837419ec0af 7951566a83b26a87fefbf716550509354b47ac889049719c0d57d5604e7bb93d db457a8590b2fca0b2700a13beb331df7a58f3789a6b7a4c80eb02aa4ed778c6 661369f8792416b51a2df9bef8e1fe05e489e3f3d98c5ae38b28f379fa6dee20 fa6f58d64a135b815c653d1726bf441ffc298b09c650558854c3f4ba7d94bc01 87641c1cf1206b410c9c27b4283cf19873e06377cc70e7aeb2de50ee9764188a cb541ed79a5eea469f1b374c18e0a8b12ccc800cdbd364bce3daf43c7fbf6846 e3d53844579d83c2b9c78b79169b2f6911d7b125b94f89ce4c98f1343e255295 05aef36fdd1e3a6e17875173faae4c9a2456b77c9e78afb4681c31c66784f590 8ff43e7d4d6c82b8c91758dc3e292af3e9344dd621aa18e97a3b3682e3467e52 2682b3e2c08992a8f0f7d982a6c6364277e9ec213de325537fb2d827be702556 83f6723c2f4d443f5b4b308d3c8095da6f20647d95930a8b5ad765efd049614c 438124b602a4805cb0b44782dee63394d9a8442fb62318313807bf030fb2103c aec608be9ba863aabf78544b1b2ec725ae83ac16cd15dbd76d8cd8207ee2a1fd cd5598039f1b650120294184eb5c17438847ebf8f9eb50b8f45dbde84c4eb19c 0bb7409ce09441d4bd60aee5385d2181bc34e98c919d689b4dd65cb47bec6610 89d54eebb2e5a8c5ecaaffc2cf70b255aa2ed8314a30aa767c9c1a60bf80a158 07f968701b8d9309e659c671d18f856d243959c06cca1e0b2b3852c4eaf146a5 878f353f0454d048cb77552ece25bc542904341943d2adba5ec5b04e5cefce91 d96a16c6bff9ea54cd8fd81344ba0014fcb194a497b838215c2a1815e02c25c7 da40eab1d4369c130f1901a9dc3e1c23d77b0dd4b1204748c222f58a6fc475ca 12ee66ade2fb782e68a0cb499d464c3a7b7f3cba757d653c0791ee9579ce1f92 b5e14255b92b4a964c0de98cf1644543363cf07eaf66caab5f60cd52ec373ef2 d4d5f7e4b0fadd08debd9e081cf34a0275d4d804421799f9aab58bfa1121020bc33dd4c5454048f95b154734fd9d352443b2efe6e629e068f7c4e9100dbe550e8335513e13c866fc841dddf7b87f58b352e8e2a5f8b9dad70c7e7d05665e1101a53b998ca6f0dc735af48b1697b92ebfa7a0a30d1f52fec48310a635f4e0650a3c88765873ecd02d604ed90bf226d59757f15a0834ff6421b57cba0590015f081f237d6f5e997a897a7efe69ac92cf08b1629e25522e8d6cff2662c103c60b07f0aa772c1cf618e32ab61530488b9ba063a4ca738926577b84b78953790a0b0bb710b76d83ba601ebf0d33f2220b09e7c987e311a37440c6aa1ab15fb899550886f490cf24113de21854ab83eac8cd379ca963333911581a853cf7e1089de709bd03ea0a160f02a81dccf5c9fa5a2770194cde0fe96397d05f3e41c5ccd17400d3be698a42ab043e332b50eadb2e2fd13aad672c1283a02f057f77f33bfc210a1534d65270aeeaeb10e7dc1d620deb772a10d6f5bdb4b9fb59c48dae0c98260dce0400bbdbf9e2680d1a5fa43bfd204abc6a7d3977926413fca8de74e10c6b04e139b7a6b6de4e878ca787ef90d685a4af62a0ef8113bbe99a5311a7e0e2e7003aab4ac6b279e58a0438cb82e036e8677020d5395062a9168e194eca3f1a5a06bae357f4f42c24b3bea0295fa79548cf33497967ebafe9d1ea4223b235b1030edddca2fd3fe5937fdf92f6f46ac63f9ac7e3659cd2c7cb121761b18d2400040f9530964ae354d1b5d40af1d79bac30bfe694cb8dee3f83d7b2df8d20921cf30ffed764ca3e9aa2dd94a3eabf0167212fbe59f19da3a19a08d2043fe906c3e70be5c67d03289e5264450cd0694de0f260f9a437af33a5e27a7c3fed8efe96b80f81a9648893ef32844c7e554bb55ea160be440bf5b62e832130a95ea0ade5460ba50a5559938a3301930e22bf0b6bc5cbd7ef054b10e2e569baf7e46da47c9e04d21933e4ac786cd1f3b26acbada778ed6c64dc23dcdfb99742ea82e721a0fd0743927a63c0b02aaa432782b17abcb90e1b421070401641373f93ff883e2cf803c5e2e29baaff5a3c8b7ed3328ab9406c7b2f9f19d82e770f57d144242062440b8877b725047d9f0a031a0bfad44f9913557b1dcd1b0080cbb819250c8807b502e16ffee3d72c5b6c4f7730fc910f85d93c4a707df07ba5df338d6989b919c00b08e96215e5b65af3c2f77370d0580d65a3999eeebc542f923df23369eb3de30f534c6539399483ae95a756fe661dcc5680d9738218f6f17f699d580596f26a016afc032f097dc9d0abcf165dbbf169d156d69bfcabcd778b01835768ee807504f51ec23129c7bda5dbb7b195c259dd1bc257f90d92431b32ef1dac2d1fb281085223f437e125c49eb39ee4194fb856a3e91b7398b088dd5b4eaa18b2aee5fb047f9d15638964d2f1464582ec21f4c04f5b7a8d26a90f68937e1b562cd09227013b488739ded262b0477990bd0a535854e9acdd1eed285999c2eed3bf3da16c013693231d9201187d6dc64fc5392f9a650cad5d10e2ccde8a05c0557279b04b0e1fdd14348e88fe08ea2fa6e9369dffaff35a8461b459b32c3aed13c0909e05031dd16f3b69079c59d28999e5b3b25bcb3cba49ea079f565f6cc187a29090330bb9c2c2da7098bc2fe2305fba29bc42a02f2ab552acd63cf0eb0051e3bd804d0419bccb50630c2afd9c0bb2920ac67c16e0e13d0bbc183d3fc77e751597b10d052aa5a2b2e64bcb99260d10d33b1b2586d071ebe6894e97ff848e5892186dd5036990d84d89ceec879dc4aa7424e5334210ef042c289884cb71b385438efe1301345f73ed78661c9e02c27d77616465d819dc2139aa8e8da6a2b870bf960ae0048981c5d1f9837953b808e3d6bc1fd158ee8a789bf4a1a6412c3e732acc6cc40a44280541c50cbbfd315ab574697b0c8f2a1cc1f87c2e9e88faee8bb7d5fc5309b9dd9c21d65c1d8b2cf79eeb98284258252bde72b78e6c57ccfeee46e4b1d306617727ef33f45e87328be10285793d7b21f061d8c87fd08f8ec24f09307132027a296f8be868e3d458a0877c4587d44ec470ece59c68705065bd1963846dbd03623e6c55f3da01c47730438bd566986f24c732dafecc9aef2cb98908ef3ed80b409cb3fb255483b8bdb828e22857cf9ef78c021cb5201afd449f332af12321088ecba4e8ebba5e70fb131c6f7ec71af31c70b90ad5aa579978a310c9d8cffb06c61d065ccc360ab1606b70585e4c1c9518c66b806538b0eb68117714eb443f09a24ff54b53028620b42a78f38043287c7acae89dc88d892eef5604be52848a049270cca08fdea33feb32c8db8d6ff6a0255896c10cdea4bb86572ded59249a0e8d973411b0dbd4ac858efaf412a52ac72f12c4d041dcb069a91ff2c7075ce608682ec8b6bce10b23ae5430ed71b0a7d61fe4490da2289e9926c1871e880a830abad70fbc111565fe48d776ade8534c29891f3869c21093b303f23b15dfbb3101e451a3a049381befff2e07d9b9ebc13c9cd12790f41e48db40da8efa6fcb530a41ec366de03cabc9e4d2d225dab44d8f16cb2db4476ab26f3988638343b3de0900ea3ffc205aadb6f92e9df8ceffcf70e067bf438f6da91a3943a42f625a3303c32e9f5584f390517ccb8bbfb24e59c69d3ceb3ef799476ab7d16275455e7705de6d7ee71e9d0c13a1729652c655f58c4fe4a350f31a0baa13fe28eb4f92c208fc06589bba38d55978a83e4435138bbb59c9d6e854fbffa42692eeecc0286d009aa7b09f755135a3ce0745c46ac1caa6834cf3f7c335c1e888de8f6eb767d60a87c0a803355b6db97b88a09b46bfb75e4d264746628bd844b13192e8569252091e615fd54996a583b4e587e3a07cdc57b4e53e214daa83240325b6173442760428278515dfd1bbff4d6b7da79a07221dadbaf5b7d5311aff1170fd71e03dfe0336ab8ea0e11421fcc9c104d3fe35c697e52e20b0f75701f99191affb8db8c105976673cd68181ecbc9aa76de713cd6d0f33894b9813c88f2b864b18a7b99cc03f584ff8ab5612f60779720f671051665a920a934c8941d0b46fd3c478f7159054fa52869c57ebf23ebef8a5e120656c37cfe455175d82507952e4a2d3282c000511499d613f7014e3b46d6cef93330f7e8dff50de628f529d3b12643ff10f6081c2c3eb48ebefc8a4b23fb3f975fb40216a8b7dfd86b55ef680e7e1425264f0719de19b1489b99aa69b800c38d5ecc7e67dd3fe35d63ec7ab29a3959cd093a0d4329b316c77a9195135bd0d8466a6cb2bd4d9f59c920789b01292526f334eb00bb7fae69b3ce1ad21776d75f72e4dd17048e5bc5d36d4d5ce7926cf5edce5f05652950a8c3615af35846e3731b2b72416e8661ded3674afefb37e0994f6fa20e3e626aa45b51be2c8b9fdad545220d54247c61f28d75ff38c0e5bc584ef42b04a83e54e92370e5d3677e4ba638628f489b25b9a28cbf1be4eaf768ccecf40307ec58fce8d05fc9ea5e2923903824522794eb7b18882fd3ac362613e02ca1f50c498312b651462541543932de18bd88512424dffa30bc8802d40dbef44bcb170f7613641611d32e1e55b8a3e67099cf1578fb1edcd7787faaf947f27872adee0c26d049c348eff75fe2f20fbe434dcbd48db78e4e9273bbed99e26a32e5ef850c908251b6ef9469153bd0a81345db540f484fb16ec7d7482d00a9ea5daf73bd0827d405a0029d1c6f3051f918ade7d8aff67b46bb6e9995ceb329a0acdc3aee06a359df86c0b8b4150469e52701dbb90b56629c3a239393c1deabbca147d2b80d387ddb96a26eb38bef7409a882f155488553e56557eb1328a6f7e588219ae4009b71163a8804176d014fc167d947ccc7ef239f308e2a788f276dd7679288d108944c4d1b8b54bde91500946f65b5e9c3e078476e6a37528ebb19105a320ec00045962550e089ec7ff35795314b9b22ca148406f27a2be062aa7aeaaed2af16075f49c6a66c8bb7635ce5c94101d46c54610e615fc1b7c669e1b5b30bcea19600f25f6e2c7fb3f3e902743991f3c5b7b494dc930994f5dab30b0e6428b3dfaf0cadebed45cd3878d28abc4cf96e28a12e7ee01874ad81eb79e679d65cf339500c38ec5b15ef2e5b3786328b98b2cb16adb1f6c1a2e4660b891a77894bfea2df088226c75cccb276447a9f57cf7f2d20d16b701ed2d9aa130aea5072a26d9dd008c78e90bad1f186096cc7fbda6a4e2e9c94e99d829f552acf7fb59ab40e2afc0243721242c3fd4f511ded96c022b925d61f09c2bafd086dc4be7bfae31891680b626e1334156679a1b428a9931f147a7a1c389278670f931c15d15e54d16c5401669edfaa00207badbcf4568e36df49ec12ef67feefdac9357966c2fc0a27be018d2e2259d54711ff2faa2732b0236f9e7a5a4a5ab0b72bd17bd8e3f978295a03254dffccb8d8d3f883f68e32cee7bda0c56d91aec435eee41ed18ee8f9a87c0a780e35b76c8cc4eec6a3eef2f825862671c8afb1009f718819dbcad87f66140ecdc4979b2dbd5bf1f7ebdabdd5af08e74e705f1c2cff3bbcdb49ca448e0f3e007c992941b72621c1a8f3800dd580bc229b3ab0d669c1e51f30eb3cf38162f307fd50b40c0afd65435cc72d4472f06845efc99558282bcd9985a6268b21e5d400c7e7ec1430556574c4415a18e78fa75a65108ec1d462d1d1754beca9bdedfb0d6e1b4cbd33d83879f0ff01d82303f6d54e8c81ed3a6efe65645b5a5686891d0224e3f9e7b37d13548216cbbe99369c7faaa10d21195dad6e89290c95ee8cee02f1c9715f44d653998c9e8b516603ddbed155d62cee3e34baa1df4a6abaa5e90b66ba43d817abbb83c3aa1139e8e5b22280d59945631456810fe73df098dea20420941e8f426aa89a25179745f0ca4dd04853d4a67f6871300265d3f28b7a6c0bb33640e8a0013e6281888e449919edb169951422a9d0fef25c66f0bf44f04f045a5f02b13256c052019bdee71cfe013ce8bc745cc926afe4eabff8855476140c0501ab436bcc04cb810508c90823cecdbc4acddca4f779b0f46b85708bab5106b88cc5864ba98a876c265a5f919713395faf27ac5ce35f0353fb87e449ce4b0cf80d4a93dc6c6a62967eea3d2400d90328cb5d6e088c97a63b63a25c81a4620d60113621308b3e781b342a799e8e3a0d29dba3a43572e2dfd94b458b6a13840f95301a4f58753fbc95da40edfa5ca0c4e1291eca378bbc379a89d6c282d53f07c61273e1fa03419f47da1e29a45ca601a9d215e7504d9d232af25609e9cf6a09a71634c4daabf08aba69b23ca6e7046ada36c79b91a1e9a23f7ac2ed4218f10ba2e975e39c67bc1448fd614ac6f4e9c461076d7adcd054fe1f3eb4d0b4c1af0665fe3eb40cc17137f283948a4fac048357d3d365b7fa81fb04f467dfc1e6f90fcc0a9fd9eacec3f9ad80441c8756ae30b36a7c3eb48a89b67393fe03dec46a0a31ea3f267033705d5e4b7696bf263fc4116864c52a23680e0bcc4ea7f3117e0b77b5200d5a62083c93790d1ddf16d4e214bcd4f94cd19e1f9d6f343d06a90f09c6b1b661cdddfc3a4584f674ef0d875c8b042b8275ae10e1c960c038e46a3f08607ead560b56c92b47ec8dedaf589bb94c6b1b55dd89d790ece9e88716dc0e06f2f23378ec071662f70e1283a5b4a3d31bbc7f4ff54b0d57afe03530c7d6d308c92304af656fa24ed340623dfda5e10b116140fa7242301750a1b0b976e29506d87e61c5c3b7f66ce3740d6d4357169458b209ccdb31c40b9185c48b6f4ca20840573a88779dd7b933e08476333f89cf60a6cbc97f16b100c85e2d1281330e002df8841dfd63bd21711a92ca668a61bbe9920c942f1c4616f897058de6735808da2f3bf4cfb79a1c3ac98edf92aea58273051054b8ed8f220b7f318b1ef7630884e47981dd8982e1d21676df969281cb350851ab72f3722eab3d36f63e01780cdf386618c06a41c8988162cf85a052caffa5e326681381938ed7a97ac9d67104f51130c014d825dea6649e890fc9a42e4271ce353c9e08e3ee6039a946da30003ffda1f4f440d81325acde5a1f89e1be794056949a4f5a4f5612643e0614630afca5bae36cadeab25ca588a1d14eab30cef4d36a2c578e51c44f0592164e7e0d1038a44afa0719678f6c25fa2b320dde27a42a62914dc9ff97ecbeaac9104a0f6f4f82c52e00cca48dbf64deddfe05119b0f2806c236c2ea078933b98c60180584965a9a3774b98e08f9e5fa9547cf55f57dd70ad041e7becf5a64645c4bfd06fa368d11bfa2c0c894f92b6358c298f06c1779666ac110e3c6b92615ff5cca093af575053ed6f267507f03afb3cd64f9645d53d5df8fa5aba81c19fe90edfa0a07f3d2c9bbb739b235cfca3e3c51fe8a89020950d579bb3ef0fc560c47267304eead4c9fb30ce34de96817074abdb02fec2ca87f0dc41d50bb851b0ec3be900fbd15e66c9ac22a9785ee6a6ecfe98968cc0a50b31e368a8a5d5ae48279eaaa0f715f553f06cc85ca22106b702dc63e6d22022e366a554dfa8bfd5724d1e9fe04b14ac16bb6503365eb1a8b89dbb35c1e8e4515deca6ad9b9b2e472c902241303b351852b45b7071c8c808eac91ef491c7b14c7353d56d3c69e67c94cb030d40a9b167a707c055590daf9ce9817ceabbe4d35fe3dfb25ab286200066e56f5d306110e875d980597aa48ed122fef6e8bd53e1f5e903a5d61506c0f04602bd97f01f752cbbf96b29b38d9b54419c73e17675ebce0e1bacc19a9742b9bb70b7af90c76c43f795de96bb1a987cad71f0621654dfb6ae265ec5a2510aa93de2e2ffa05ff1bfd33d651d895ef386d234da6a5319806c4460ef99aaa3f4752d55959e7007b75684a268b9f0f853095cbf63eeac62e96ce3da013c89a9a0fcf9c24bd670bded2a7ba8a768d312b8a664b7c0c211008091b8502775c9bfe42da562aed850807f4d173acfeef84e825324f548c2864bea5eb98e33a841835e4cf3c0cd02e0be2ce651b4f275619ddbc5e13a524a5d795b5716963ac2fcd41ebd812610cfb0fd4ae552a471079f68a5b4dcb9060a15cc3976cb09bc2b7c5cf7dfab827a8fc06a59ec44733237911a5f2818e5327d265df4cf9aae67b8a9d17c217028f18f908ff209cbbeef5b17e3e56543785c1ccecf6211bac40ec29fa0185b4eb8eb1750447089bcdfc0c5f608e481dcbfb9a4adc06ef566c62b6099afc4160f17e80b605422adec83b999d44c937e3819025e64e3e76635db2cdb43a313b0f00925f8402149db6151c6f1d8e70164928a75b52596a5f429a63664aae8fa1a983d3a085094101fe7902ca28f95d8526cf1e9d3e6b27e5348583272a0e7c9530589c5e280af94b9ddec669eadcc84ec35fceba0f9ccbc789700d90e963dc23ca77afdab10e186b9c2ecc8705463d81849179e92a66c1e13106b627726e7071ada23faa0d0da78c51441eceb0e969952a7f9300b682f66867e8d3aa2b476ffd4ac68c9c9a085ff94b94e2f355b1e65207eb5a7fc8b9005e6afb31ddad2631a5d128475c1a043e792e934303cd7e09b881ce2098681b7cbab73b606358b60359091c25135b0173bcb7a561687820478513a717eced166efe769fdc47a408892f7970c6a22e002b476a9d83e753eb40a537842f989a97ee614fee4b3801090bf1a7c440524a073afa26d21ff60689ac6ae80c4bb7db20dd65ce0e983a9c43b34fd330cfa549063d0ed3899a02484a39f2bcd6d20437faddffb8b7b96702a6d8f50f835fd82e0aef616b5e891b155cfb3d4598466a05f77ddb1179c5067e02b380e5d93cf92700dfbd2f88a943644bb63e74964ec07ac284e09b1d75e1ed0330af8961063d2e0a76eec79bdf19f5454b8296bdb8c499653c49a891c278370ca5d6ccc4dfdb660ab71a0c110ff0a276bb4ea0b9d4f6f77a52e8ece12e6560450e242745e473c403cf9e70f5c437aac36ca600b7d3e5bc03e5fda6cf711d2adb07a0d6b5fec7590cccf8d4545b607ecad7372b3c17f91df87339503aa6398a8b3e68287ba539090b726fdb9c35f03d45dfdcef0d4a47107cd2a2b4b4cf7d7b5318a9d14d69c20403e06759c160e3259ab22d0f37755397c732a253d809557abec870e4a21b12e30c1ea33b9974f72eb1c2fa80fcf33cb11c47cdc18c6d4893d0715a1db19759840cf537b881aaf9313844d75db67199e71ac9e5dcb8c965ad182662c057c7cadb08f753e7400f2e206af958f66461fbfa2608a4a637fdb8b3c8f7c4b063bf48080a32c1fd6f6789a5fd3ff57271520cc75d0361e2b7d7850ce19a21edd1f49be50225537355edf70198edd5bc451dbc415ae1eb91828675d1ba63892fdb1461d10ec2dbe8bea3d4218a223757bc5105a201b2147b5855a6006870e0d11175a49f0ef74ad14295338bae754965ce098f3621d6e1e72d5ecb0df5248ce9fd8dd7180588ca0ae46b0fa6aa98c7b7c5f753776bac4dfa2c199961b8992fdc7e8e9e220685afc89ed774328a7a9556b5d467752a88a243869eaf5088ef4605e418be940ac7ef010545e841f9a69965345e1e1a74a1f201c163a7edfd6270b0387b8c31092cb14916d2aab4507e6cae8e67dd0d5de832cd8973fdc6226669395514c6cc09c501fa0880a7fa0e68aae448165fd75a59141f7e8d42d5c77d72ee21cd0dca0109d66dde07f28922ad56f89c2c77a355faa2acb86810c9b5ccb8374e6d70f80502866d884364efcbd7aabc96fcd5b4affb7c106840fe08fe2a5e404d2efcaf0b0a21c839c89ba73170a824bfa1eec75feb50ab2d19c8011353d451934f2ddc0a187099353a7b00cff972a1d045b23e076b35bbb2419419538986ecab2583e6069310f044bee025264fa5457708efe5f571a4e5d56525bd91f5e759e6fa6e070d95b75b151d387f2777db0031148be09878b38df8e74988e887c4b1a82f1dc60a3b9f5fd72ea311902e725460cbae9197f082fb211a5493eb6bc2f240b532d60631fc9799802a8d43bc82c22e26e1d1bee82098bf42428d9d4f5be2764bfef3043bc49b42abf949e5a2b6dcd28082fbbc41b6f0c90e83796c895b9d2af4238400e504304c93586acd21e257ba31a66305f4154e95c764d788a08a90b31614ad0bb7d7bc19df45e183795fc21cb9cd5c752615222ce84967096b97ce451b64690f5300b2b88bc326b9ffe89871756e11e042d0abe6f5d205a40a499e233f75130c3c66fa614bf92c5f80f09781526ef769be7691c8c3eb0f587887b1e7e4db71011765efb7d14b97198a39c47951cdb7949bb55b50438c878b49a16c890826410b19bbdb6dccd6919a3269cf8c27d9c9c3462e660d2093b596ebd619005537930bfe591d7f1a9bc65252935a8d5f2cfde34b3b2a532d5940d1e9234d9091e8230560bf3347130cf0784b003f8763d400fb5f6bd6509189aa586d87a93e9996730512b38dfe9d9300260c03a623e5bcedb4e418f867377ecc9d22035627cddfaf0dd62bc8a9590dac2685b1157700e7ebc3c3d300121ead8621a91bcf45990e8a03bbce3b06bc65d6d6eaa33e78ccc330c14950221dea6a7ecf554d04cede93f9073531e55e6641e57c4fa5d966f19312ce11ebbec7badcb2f6359ae0c0ceb2a802de8afe058223fdd3c0320c2937b0a262e7ef7ca7827996703e3c64a819aa020b06c8892f25c7903be10427d6cea42751db14c2249011fd604287ef0c8143bc08ca5b2b67d306f5773fcd29b5f3cc3587014063de5fdf369776b094b9221e570805737ffc6ef324114fffb14634dda1f25f2c908550ceda34ab45e11b4ecafd0e511888faa2ef15539f9fd1baf201e59cf7667a4237030b8d6ae347d078210d0cb161c03e485b57f0d98b598cb5c6ecb1e752430533e2099a58429eb1ad87eb0cc76b4f7dcea7384e66f574b05e3cc6ce87275e23f528f59ebb01a18798cb93082d7fe33560eee025828ee403bdf46224efe3a7ffe7c3fc7dd2e660385a12c802df8bd310def6e26a67c7b413a39725b76d90c35425b5182c4210987eba01c50e1ef371675c3d45d25f7fb9f3235479afc9715734871d62bfba1b557395a5bf07dbdb3ed7050979fecbe360f70ac2f1bb79af39b15d779fee8391359cc508bc05e0810bdcfbc879630c4313db8fb9c9be1211bed1e8f51ff867beb5580aeb4808fd947faf084d44df2124450ee5c6236a0887aabf5d01a7466a82c2c9cf044e030c97b35be2d7193962fbb8effed6743fc19a35a42b08d22de1556a2e6870b503c73d13ca96fe7a8647e0de940159e43511c23e95ad3dfc0abb69f3204a061a01f649d72323f504bd93f152fe86b657edd5f74954db12bb18e1ee940d81b4ea0477d4a30a5cf0304526938e097ddbb3d3e487b8d1ff676d6d4ed4cf6758bb6102b7a6cf996fccc2d04aac2418d45ce32c4df07af4c00f68aa758f283b11866f0ac57020140ce4701bc7892bd8dd667cd36a7ed275c1417a895d5c07ef6526180f1b927ad0c2c680236f82cd6d94150b47aaccab73e36492321eba3fffd4a33f04d9cf552e76323ccb12780448a49be842fe769b87c1f7c35183b0ade81e0cda0389b1a9b97a68f9a5c7f0a94aa9f172af950104774498d8914f9e80d5b0ff6a0f32402717a0c0a5ea997967596eb6d33e812b996b5ef9cacdd0a897ef4f836306bc3493c20e2beed7444e9a2669661e784d3a381aac4787e2158aeae3ae36a80894bcaac70f30da11e2c043db71014b692740f2b98741ebe75dbb213c28c3d5027b57618af35b39ee416cdb697242ea07fb4aeb27cadb7533abf7329b06387a080bdecc01c0dc8daf18dccc4d300519a5a8fd460fc80c3beea2344d4d944d9b09bd9557f70defce3353a0f019417714711625659ef71cfafaa85a870e6c84c309fadac7bfb6be8bc33f700a1cba3fbda276fd6844cf1aa3a8ac1ad0751823370f9448c9778895d51be11ff861d0375d1adc39e1884ec3907221e5dc64af96b10b847eb6e8d06726662e643e36d548e92c4ea3dd7b1f85ffe8c6be127fe12e3608f78f65c2ea7f98e98863687ca54aa536ffb1160f69c3dd47f6b2b262f739fb0c429a299e3dd6c761e2ea13ed61e235c2bce644626b83244151bfb0793024150f78c6d79406b06e3646136b265cd70d1db8106087a28f52b6e1dff55107acb10b7bb5af718e6c4822546fa2562de7e22a14f5dcc68f4123f3050745da95b2d60fe188add7a7b8e312a3acfd5ff6d460267e00eee05887195e6298e4f1b47c3505bb54c52dfa25018a5950abc69abba0a444e03e5d7ac7833ddc92734d0d119b074486ef8cfd896e28920ec21cbd0559d534351130ba9236f8c86c3830ae46e606ce7cc25847697a36ceb0909284f47a84d9159866f154b17f9fd985fc8ced1102234f15bb0defa5c8ae193713aeca99c99e2098bb55b1b466cf1b2574179b2c0528322da4fe3d7112cf6f927ed9d2f7610cea47c0784a934f73c146de7c43c901c0f7fc729f588c0f1d0e917d7029c2b77cc948088c09dd12b472d211ecbb66002a7ad039e65446874bd4cb453b0159350a2cb0f33e524f48297c2f724bca96049c093e88164d134be85184a0c9fd255735639ac3eccb94534b821743e933000b6d5220382bc2bac46bae0a91cf321498ae5284e6ec1b074dbea57fde2cfda10a2c82e67519b67f3014a6fcf31d27db98eee3b9f892dfda092d8b6b886b7bb0080e07824680c207a7b1b35032e1eabbf5b3b4f867635256d1ccc5599367fe7506937f1846c79e6bdc0d0c3b17769afde4385b596b8c1fc9f9c4bda9d71ea2e40122e873ccedeedffa937befde92ca94c04931e2ba3c17aa9ed73f21ed1a357d0566d6ed9e1444aaf747db1512d2b3f8f211e6f73c8a818983f67aabae9b36f10767b9982e0962076efdac9946f749f248e9b663884baaf0daf421be455ef6a60d76236bcc2deb2856ffccdd8a423b31df622ed73a690bf7efccc3f728dc3ecc0a391e903408729ffce3a470179a03a73559fb673e05f3923a62568466961d410c2a7fbe2232dd0fce7d6635da8e04dfc0274d254d9bc2037b62cdb17aba7a9d06e20bb1b2a12bbdba8a6ddb20463ecb43e1fe05b981b8f29fb90421ab1ff7a6046e8e3b8c795e08eb6cbc5bcd963ba3afcbcd6df89dca2bd041dfbf6045bbb2017636ca5e54e83441ec28ddfa95503ebbcd2fb4bdac7cbb35cf5b42edeb99480f319457e7a2c0a5fe7476d9eb77896055844dd2501a6abf133f1f474afa02a30aa271f6b6a0dbf0b243c59217369e6fc6dbed37c83ec07add80883d12f2576f0132458605f7f66721cabe997410d3ee0801e3d51bdfe5698bdeb4f56d5c75c30610e4a28c89173b9b07e70542f2ab5b193ba32ec63db39c76fe6fe0b645c5100a4e27dd47b477c1149130b1df25615c3130b8173c194bd9eab0259a217f9f890199e9ffb1035f0fa35e4daed7a12d8325959f9b167046c01242c933506d9d5006a4c4200f1893811e803a2238b48f9b459a5f774576a40666d9e9df977cb4c90afe45ed1c71bdd5d6f7cace819dff81872884e4d4e725db2a60507c58b7b672094a5c3f00994655454e2b10be6ecac1d181abf556194a3e4066abe4bc482bed03f175eb4a6f9aea6136a822526fb763bfaa7b2afb6d31aa03fd9824d0d9ba610249d0b37314c52bedf77164703f6b374d09aefb532d8e7e88c54e88121d0348089f2ba36c1d8f4ca0fa37068f4de1f4d6f9a74d09ddf271efd799f44ebfc19505cb61e463561c9a1e7b24b16ea2178c18fceb5798475258d316466be4cd205f02e37773e7e5a34a4ee1ca725a4502f13a730dfa3c40cfd4dd9820d61d42c4340bba2ba4d4804a2bc4c6a6dee84930a9001093c2907371e2ffc8e2b190b5c934068d2bfc5776ede4789061c9a52858516846f6bdc84a25089aae0dbcbcea29670b937b42ed431b5e9ff0484b97007970079168ec7fd6faeaa410555dde7307130cd0941b05585a689bb8a99a38d92de262f3e248b6095a598d8cde91382a9b73045659658f367fe9448d6c91618ac57a23b2688629ebc26cfdf023c8d9eec81605752f09eed0d7242ef97bd88d22f7a74b42393e18f440f1d0ccb7c88ea6f1580768c04c7922148d20ea28d4f37118f732e717a6ae26f6ae161798e524bfb02a0a19b7d526661e8437b1bd319073d784d3df9fd4939ccaab38736fb2e4ef971b06e8434a56819fda1d6cd3c4b58b696421d3da0eba36d4a2395534aa27138def0e39f1aba923527e056a1f09dc8d3e5ec3ea45afdced4067208df8cf143e56f90c97e75e2e53666cc44040ad7e5b42b2383a27d9da36c35f1ce306f107e48ed30a63a2d83aa12d34fb45d9563a065a5cec152f10d833cb3a243935bf9fd06f310b9677ae5dfbbb9c0f8e2b8dd85bbc2ce131ca00f095e6fb29aad376f4a6e9f40879253265cebe178595b8c860bbe320c97bc660dbdbefb273f820ebcd1bb10709abab3a7f191595417843669e362b3b39e1a29c41be67fc2033eaec97a615c40c932f4e7f4fed5cc7b239c3ad69de089db11375907f6599dc27b6e0d4d0abab0e1803b57cb1e166c5dfea035de87fc30eddf4cd9233bbb608f8d560ff23574b049433b266e957fd6d7159ae5e27f9d85c0e14b21bf77fe375df459cc0a49c1000baac14834d07f71fb36966bf7faaf720e2a7a9d75ca0b65b127672cdb0785707d7449110ea22ee091189e4e38767755c73b318903a046cc77f47f7bdde3c7c0d18086ed7ce02cc210abbf9dbbbabbf82f8e16c37ef28103f4dfdbd92215c170a243a816355d0af2689505979a5be5becae5dedc527a7411b6389ee4a799a240ba3481222b89cd8bd753131b2de6ead1e1f94d146f44839555f6f69d54a98e7009d9adf17b094ec202585de5fd848129299bcb0ee93211531f24f2c4d9e31e504506d7b02cd9ef96bde0e73074d18e31e06782823711c24e99533b475b63062071452a5ad7efcf1a35d94cb1805f43d5caa212e15eed010eefb3963241749bb0c5e429e81c431ca10407dd312144b5e5666c0d9e989a9c57fd959a74116a15c0ec974f8deaabc1818857c81a1623849e8cc9b40942180e0a91734fc766648e40578bd4d868d76471c7f157ad12082ac01a03a8d1d67cb600b0ef6dfffa63d8b08121224beb6f67e4b73025ea4120eeecf4be1765b3f42073b5d29e3dd8762250d5db010409eefe809927d06f90795edd59cb1f4c249fde67aadb0689d00bc1e0e74161d2d1899c5bdb1b04a05b7c0aa2f4542735a8bcbaa527148c9b201247c0e77dd820bc6c9897ca4688143fb41a3d69065b9e4878cea6c8a64f21ecd06aa020ab496cf511926ef48d23641c4f2b40c1655c68fcb5fd5aeb6389daddf68f90ece54dd107af333589999d65c08c7a58c14eba93fe844d71f6641f133c43e180b2c7d27aa91cf8602fb36861d06372dede6268d9b227c873450d9787633ca460c440ee348d8bc99299bada5bdf2297cddcac4c1bddb6d52401c9035b92a14a40479681eded5bae8b231d73f0c8324c506a4123c8573e047a178a2c53ae721f0031ba2b04da3e24073f1a2f2c82d049479a7a7e6e4f699b8ed21f2eafda08cc8015bfd9466463e5c84543b2a7fb7d31b6362fbf3b1c94ae8eee35b4c09800f400646da6cdf68a31e8ba0221d3934b609ca8468ef12bf6d02acdf4b886b1aeb2f05ff1d73d3017912999924d36a1407bd5376ddea39f1d9a531128ff3972e9b0e0ca3809aab40f2818444e2350c0a54ce521a445481cbedc29b6617e0f75db41b0ec9a9d1d59ed210aef3d33a551aed4cae5b3ebf495df83eaae355f9898fc7ed0988e9f0125b801ebbd1ab3db714f270fa9057d1a076bf07818af3e21c262b0807bd5cc8d53e5f300c7be031860c336baa81166f8cbca517addb15dc4315ef4809c9523764560fe954cf96a5421eb43aac5eb069681ecb3a3f8c7a22f6be05e50eea756b98022f62a6dc7352fdf762ebf37b80daa3b0fe647e8183876ec135d800ca6b6eaa7d7dadefff60f669317f2e82e2b661233f9f7d024a58567051f9580b54fe79a8e80d45e98e4a64757f8d34ab1a99de1015b8b2d497b8a186a08f2c0dc4a2e62fdcf823d9975f8b2c8873b7a15ce1e32710fcdba22ab5afe460599d0797e1feba2753e73acd682827943700d1eebf516ff89ffdb515e784907814df0fe267782c0a4272529691eb3821fbe0a4d1973579a65c2bf8d25830f770556800dd50403ba817d70acdffe44587a514fedc0db1888b9a4cc79651267b408ccf03d04b72393969c02bc4b9d954d862006baf50910f1fc39abd1fe2652cf851d309b09961d1e164e7bdbcc25818e75f026bef4c12be89fe13ff90bfac56ee5d8c06182569c1e81229ec50c2c5dea953eadfe55b4cb8ed14b965704efb090e92be0526e190413b680f5affc13bceefdb2dde793cc3a422894cc5527168dc7f39b10d3d80622ec0d8e57b8e88bad2cb332dde2bed3fb487cf77e320d97b89b74c980c05d0177021909014883d9a54a58e64faade0673a2e7b4beb9886d7dccb756d0888a09e443f1476a5c20c256d321a9c0eccfeef5b74e1cbdc24917fd5b12f010ae64a29f758a7e33bafdb6bb7032cca98acb92c78afea03612818038ff9ce190a32aacc949e92e94bb0577d17d60527343fd457c2426e4d9e06983a3b5186430eaab3359665209a44418eb968e3b7ca7272b1db100091f8a19e32601bbe818e00dee98224e4714c26117d55ba9d311d22d9f92694556741c3d8f48c794ac2ab0aac3944b4378481c2bea808b30de7341e22c667d70644c248328e410ddd8e920607b34f91ba2d714fa0c2d022a52db59118d0c9ddc8acb75bd9b88d626fe43f0b4efe4b98085ab2eee0445aa14b56be3e19012f7a0d350cc8f1a412be1d5c630d6368a6c7d318343f790b557c2c9b540e960c93b5bd30cb174ce6b186368ca702390749f3493f53f18167e8d71dfafc8fdbf91fd7e38721f91f1fa8fd9475cd003e20919d589f86a958212165b56602f5df162a28d9d6cb6ee7326c129043dd06bae3026a1eace2fdb8a8e50384932a1c97ddce49465928304e8428d91f24ba01311ba079afcbdf2822bcbc5cb04e8fb10880d8dfb7baeb35024ccf7d51f1670f4b10dc5cce61cb0cce5e1e4a477acab5ef5db7fc305907b3675b6f9323bcbe0da9728eaf690f116f709ba4ef9236cfb9581917456180c476766462fec8a1060b55024ee755ee67b7cfbccc03140a331353129131cd329860655c28eb3280a502d746f2ea8ee079956b5a5c0706f6749e4db242b49b97d244bdbeeb67147b460b6b3a05a4ab1e1b2dd29bc37864489a62c9545c7a2a8c1f481c9465e50c885e008a5db8e44793d963cf53887278134826a3fcadeaa8c37d658754363bbc6caf0703cf33b1e9ac0f10ab01c1477c7abd297a03baeebfcee3db7c8405097fa86f04d3d882a20e3a201581705d11a04b380753db781cdf8bcac60625a0b2f35356097f7c713cbe7424ac336ec84e3640b4683dcfec6c513af320e18b62a62fb9a70904224ab3cd548aaddb0d4022ff6e29747cb53d8a34d4fd3d6755ebb0477195099fd181f2c66f014f80ba7b9a14052189f2407d591078fc4b243f57fcde74580bb0be0ede0cb205f7669640fd9a96342644eb2af8b731c899792ad82cdb087a01d2b5eebf983cfe1ce5eeffc3cd29ab6273ba3a771348394eac41582b2a7d940a1452d7ae144bdfc779bf94f18b4a8f4840f88490a2456416fa7d883197a65903f3dda0664b71d18465f29f1aaafebcf5a5bd30245d0ef8e9a0c217c48d230102548e330bd4cba45ab3a8acf4fc1d3e242030dfec96f8804a8f3c66a50435e800d7754fb57354fc2da2fd6168214dfab4cd0151fa71807f1bfc095c5638e0dd0480a8146a99942685351b155b2c8a58c4dd4f903d95cd81bf6994575c95d1d90d8b28025ff32f2468e42de034926f845efe50214bc5d7c8ad563fd34ff71b490c3d98448639b543b8b6f0425601badd3ef443edc2d5f7e95bd8f3cee22e378f0b229eea5002a82b32c162e3ec4fa78366d395214c400adbeadcf984b577cbc306e71053a219a7773843d64f7f75db2deeea18c5957f0319b079a4302dc61b260ba20691ac62da8498b0bcc4a45aa646e4088b1e3bf8c1eda1199d1089b39f16008ad09a68c61f6bf4a2207b0f3faa6b29a82797543c107645e31b78ccf0914c0d634b73322c087d001458c48ffdf45de510839cabbe002f55053e554156d9f5002c352622f12d941ce5d32ee43fc36bd39a79e1611f4798e06a2de9594a494f041e203fbbed975b164e85da8e27a19187b66adc106e2b5367260b6b01cd859b0dcc75c7d2b00e076799014ca166e8ec4d97a27679c88def6c63f48c889b4fb307f7e56facc299f21e52d65e447de78725db3dafd7b1d1f8ec05b5c46e4d562e0de6cd424d862dd2f87c95f1cf69a7ede64d7c4fc43b835ef8ed3535d19eb38405a58ea158ceb44792cb0557d8837d77a82c711022f3a3f2ba4c22a3f6e1e8f70af57ee2f139ddb288b3bbfbf3d7539e9e52120ff3c1e2fe37c031bb1af78b1b0b4e76ef6ef08f8f7ce2af41ba95b2a939f93f1c128f4d9c9851233ce0eda2c303e6ef528ce307222565e93f32e67e6642551e443d4a0bd3e81b605a41147b6d0ae9b08f14e0494637c63c224f0fe540ea63b03b35fd4284ce360046b4645f2108d5062b60b599834d5675a763e3a4d6d621c1b30927ceb089b6446f7ceb142f0eeaa25599c3ba66554fc01156b0c09b82218b7f8929db58242f4ba94970787b0425d6e0db06db8179d40e0beafca04cb2b37c782c2a05a1d94409473547a5560899140cfb7f0af3915b58d4d02de6147296e62035962bca10cab424885694c1003384fb03024497af5a36f62fe74e70ee6c290c8f53f098d96bf9e3971221040ed4860bac40d43957bf35104d7b2b54d60a2ff79f925b1a637584eb0dc8155005871c91cc6cd5ea44a419b514fab43242513fdda97c7cefaae26c806554f83f0a1c363dd4115678eb2e8edac2a3302418e9876070a938f029f454f8efd5632106f52040e69f1420cd865431a593707a9a0165bfab8fc860f81d4e5bd16daf4b05dd70b07a8804132c35036883ce13f942d72bec0e23494bb2743110190667f904e3c233d5ca443d7e4dd24350f3f0e1501f8468a9352e195df72ef3bb18af4b08811957a42a2f3b3c6b978c683a946ec7d859b978bf33c8d988c24f5b44038a0a4451fce7017d506eea5f50126cea98ec70fa1e99969375f1c79286c91578470bf7dc1e6cf36453e4c6eb4e93120ab7ca5bace97e1dd4af32a7794889a610ce086730f44b3f98d1ff5f4a84213c151dae90f1f0a4f0e5377ca9f73f9f02a101001cde0c28b4339c323a19a8efcb9e657a56748357f04b42de4bf9c690d8f95d0a25b4a1628fe5fc0dab9b062eaed69621aa89b1ac38198cbf0d75edb71096ca0ab5165b6e702abcd6ae9acb402d8fb95d58abf3a86ed56f0b22505a9e0bf2be0bfde6471e1f3f52e0e9d4cf0c0c6bcda3d9c6a9bdceb2d2c969d9ee9247947f03b368b78ad4d879f94d429b0e64ff61ce99fea7bf2155c56d51eab08318daf70c49a275fc13ea098ef243172a6e635ca04c9f21f74032aa38951ed58947ddda098962bc4fff2047f4cea7d7ae2ef8fea25934a187a1ede118787f691de50e2c02e0264eab584b22b32eadd7b9bcdd58790517165f79778f4f3b03a1417978d10f75983862192a7716c2d52789ff367fa3c841e4a71e2da8e45b42f5bfdf2be80b382832ccf315927fd5d8bc9a3fab807053f11c6e256e32517e28c72c20d73c0ba0405431a2dc78710dd9c3e0a1d9f27014fbf5a7489d95d9a76de7aabdc2ee0698522ebb4e9ba786fefb346c4ae84321332dee578fa28d7c532ee1f14c109607e10d67534def48c5d3409492f98b4989cbec6a7fc5a0917d9d25749db8c5e407dec95419bede21b854b87924dbb280a69d8a00e44844dfd39488b0a94c757e0ed268ee3e1f03c3115ac96bc68e1a752392f2d4892e4d535d60088520852fde0adaa39637c0cf1de59334da1650c8c120b6ab2f27a846dee7ce938abdc381920e0d5ea9e1f94c016d46e979e2ca335973e8e44d6f2968daaedda885838fe9a005f16b4bbd39430dd5fac2e833ae1d86e15a5aae3dea1d2b56dca4e61b6e9f4c011c8ab9eb5defc27fbe7a9f0c2ba901880a0903c354933b2f2c7ccf4a5e548d0d5c67dc49c61bec9eebf752259ff2e6bd12d11b9bc3df15e9a5e62e7be86e520ac5b4ac105087deebfece72fcacd12032219a71d473e5a7a76542e966e425b60dc6f847e0b4dea1cbe5603dc46b2481e3bf964528103d0451fdb78574a160a104e4e89c35e21c02b2b6bad1f9557edde483f85c940b80d732874c349f89b9a808cf6928f321c793fff7fba1a18007f55bff77a9bf12f3bfe01d5b801b71e6b50f69627f3d97821e5e08ce22e0c7c4d85e0548ece8b332e2dedf4809c5589ddc07dae1438bdc0bdea072c9009624cc3515b92a8cb9a2d67bb1021644cb2889b3090e2dd98a22829e7f155312bd7edb0a2ec890a1d723a03f878c1530510867a40ff6e2e41b20532ee0e1c33bb43556eb15f56f085e35dce4f6c11a5bfc21aafa07ec4ddfc3a4591f82f91e7ca1fcaea58edb49d3c0d5668f186cc29ccba40deb004a27243e816957ad0cf1db0859769daa3db9d4525a8f74997feed06d5c12cd0a33a6ad1563bf625dbf0db008cb6f411d1da23c073e1b303bc6c28c4806c05007b0d1c02d68c99fa8bcfec4734212e2eadba88e529d24a6a4fbe509a69fe11a068e75fc7442fd0280f0c768bda49c4f6513b85ccc287a0a02d500b563fa64840b626d4ad729157bda2a9e6c3e9ec0037651a6a65a150c646cd36a59adceec1b0452f35ddb92e6527c4cce5499b039cd03cc0ac9ec21f5758ae35d5ce6c8cb9f0a85be98b4aabb3c433ad04d3d312c70a9615fd5828f8a021bfd3eb51d196a400b2cfc324c48502fac533e4a1439963203173ca136e1d8da2e3a437ccb86cb7c0c3218540971962dd735afee9ba68d8fb7bbda9e16811b85f0ab18ec76d4bbab09859a608f53b9c2a2d108799b069074df81e595040b0b86b0995e62e9af27db05d322ef69c23e8a4fe326bf0aa0556530d55170c0e6b13d8dc522ae340a7d7f0ca293e255d105cc80b3e7e23e8153c531cbd98f2174d828ca414ffef4943de809b052058e4023a5878b199ac5f23e7e32aef8dfb3c1ada387663a512438ee1b0846b9c311216361fa5045002c41dff41c107e9a2daea02bf9fe80e1eac286b3044ee7e27520d970832700a312246b0525c2cad6295b4fb85c98998f740bc67007af93515e956f3353a053a7cf25fc7c0c3a61b814a33513a8761b8dd3eb03720a2dc12530975ef0ccc7eb11f4421824208a170dfdc88f8cb1ef0e3139240efc0b0b7fad742f670a41405cc09c7842c97c01c8908a4daa259297fbeaafcd4a800915dc484e67eded9e02848dc8a9ee0d5bf9df349c6c785b032c88f00e9123150c67c761df2b20f94965591bb0864726cf34b169373c73415fde20fe7be3303101d9377cea3600c15cd489df0b3c9b37903f3300cd09b313978f6620615745cc0cca236153049ee6b99784d36ab44fc6a2f0dd5d2e56fa1585289b318810659c0db170c21eef54c9e7d87498d68b0b399975b3b01a17e2f28344d27017a0bf9905dea79138aca676d9074b44ccf4e5b00d962d16574682f9602d66082b98466207b1896c6c85abc413c05b981cdc9a3fcb1b2c8c3a1e2b3b23ac2c5d60851bcd0fd74cbb101250d9a7b1983752c2449fe3f9a4c0f4ee06941aaaf7d55bf9096a0b7e1ace132bff711a295be818265bb59f39aab7970b1fe02c34e284b83155d406f9fbd0a6d960037a7aaea37d8af8eb95e88ce5d8769854c34a19e6539225e30fda69350ae3ef3ddf97bd491eacaa97196c04e77639073cb6eb3124127dd3090d220786a0ca5b3810b4a8cc2b012a25832af5f6c9c0527e62cfd6e3622226670d7ae8291e27c6910c32b16f8e2fb32d45e5e81012c1a008f968282eb15ee37304fc5420a2a87930416181af17193c9b0b15aa31092acb67fe323553f73460320a01b3fc64faf80e4024fcdb93823d478372e1c4c3a5db91d448b3c8b272820b06ed095ed42e274d4678e6e48523eaa724ea598e3564c2a4deb816e5b0b5c7800ea4e8f3dd50a5975156825d521c5de1a7925ab2f76cd12ca044bdee91bb3f3904 false -check_ring_signature 5ad397d2e525bc572c0d4fe3d6cf60a7b0185cea4bde180b69ca1218986b0957 c150fc926b8b54fe7e06052ffb74fef16fcf918948a4dda7d7a8f4ff5c584342 78 16c6ccfe049e992fba67e0833eb42b7355c97b0a565783f9256d7820f18bb387 9f472351b5d2ee50eb1b395260908c8e439679ead4043c89fb76f7ba130af641 3eff5e23c1421fb4651ba50ec773d9d254afc6fa89ae689384bc884bef49e18e 50dbe73508fc3324190ebae2b975e67c0c01e6036bcb88f9c6774b77f17b71fa 767ccc68acb146793635f963608ee9dca3e817beb2ae8162eac1e8f26c45c0ca 344a3520d03f1014cec4fbd91ce90add58392af7a2e17b81280ea7eb2dc44482 a2fc3addc38b5d6bdf54c8720e010b7111742aafd4169ff3bcb2c867838c612e f5d67f1fb3ff03d04b9196141a7a3d1e316284010868cc6631a78eaaf6a636fb ef04c150cb399ba04fa3ea76382fac5506cb8646317e6390c7ba56f1412b9881 199a9d4db6bc26f84018411053c8cdd26233a3c852a46bca28be8d2ec95d0932 aab2cb540476808feab0f4955bbeb494c49b3756c10ac4c4890a631309dd70d8 b5b7bce7da7eb519e17c6cd5a4b7a0430dd16f6e360353e800a4b85e8707dd0a c5d1ad0ca3f1e7c692c985afc192dcaf4a0ed8d8cddd3173e38595ab570f0349 130978d57332674619ce8ce2696850ad35dcdc4592c7b72b77b02bef992a6025 f79c2f033e4f8de6c9d81823282ae5263bc1923a15b14fd56b09d7a38d2791e4 ccc67a8fd26abd69cf7099c3ba1b4c43e589391c4363ca323283da5a41027c5c a186ee7b0290f3dbb78d2645b2bdb4ea3f5a5182645f0d0d8eac220161271d43 72729822590715857fc7d8c917eda3ca519e216947265f1b2f16826d2efae6a7 111f3e68394a5fb87220bea0660d89cd0a5d2cd743cde4d751fe550eabe2aef5 d92885d7fa7d4878759ca684beaa5a1597cfe15adcd7691fca68d2672de727c7 d98f0c6110649b6ad85ffa267c8384ecae3ed90d65397758f8163bcd21c9e3f0 31ec4bb2de4734c4656dd8a1882f71fb46bc9274d0e2ee3fb0017b1bc4608927 70e8a3a1794ec59acad71d0310e628379e0207f8bbf2b4421263e79c2d4190a0 ca92dfde69fd579d2b1ede20533607535b5dc3c4ebc65fab3f5816093b904312 64f919c775d46ec5d4fbf21bbcaa7a01e164ca11e6ae1556a5c37ea3fc5522a5 48a36026ae1a43f0d093ab803ef0e4ec2e5651549a8e85df43a968368ba94965 d2b17a632f30f460b800bf5c8d7d7b4981b5a4d19bd3552b216ae47a09f353dd 47f7693ac45edded95a8f60ecc2d78b3124be0dfc71eed8ea4378af1b1e41664 488aa8989d96fad99449e0072265cffd6d6954a301912075e2e750f9d02f4a28 2df6fbfaf5cb2279fb308c8081e0ffc4a31af8f9efe8def7e5ba4a74022e4f29 adf2a736f444f9da6f58e6ea50f97d9e5ec9a54c827a0353960f5b71334a1bc3 ae331ed30ffef2ff4a9b0be7ba16fc2a6771a6a2b0a9f615e6dfa2b2dada26b7 fea897f78f28f6847950c7e9bad087bda90e2f1b60c4a0a8fd52e0476c27c723 52138ddb9b1b40c6ae900abb970c43683a4f7f446d2db5e8e9d648583e14b27d f4df8f97d2b31d3d8a7b0450aef3b02e242d0bbcbf18c2f051192f4b8961748e de2407075f74f1e0a784eaf0e28ecb75512b1b90a4676164cac22858159684fa cf84c877a4f04f14791b3f5dc615f09d879de2bdb21f5f5f8028daf334f8318a da2959de85316c3bf2bf18c12394c1a939bfa0334e12c46bc39bf02748b06b5a 3e4cbab5765fc5d2e34d511ed7d85a8ed607317a889b56925d853070187aded1 26bf44cc24ea42c0d200f0bbd30f80a4c62c67175063d752b090859a25605f09 13c45543aefeb712effba0754b835c91bfc5a2d48a01df9b4d6bb7e4f86f28ec 4463bbfc4ea38db4a4550604002079ce3a47f3b333bd6d2a9dd0a58871edc146 f93645eac685236ef13e56e25c6039a7e2959d4b955f95ed62af4fa99803a4f1 f4ee329e2b8c8a64c9a4f02ed14d248add98eb45c322a6bf5d9d233d952ae42e 1656b513820e15afc82278d8918c7d4562842ac7bb14c6e6e7817090c3fdbf28 3e3854ab0edef3aea098441f6aeb6839a1cb8733b3d28ba6ece56cde65d25e67 0cb51f6e3ca13694983e362c9292dae7749f4d322c0968c3750d0efd397d476a 1925b56dbbd4f33c2998b42ab1fc4d6e6b24ff649071548d0ad99a1a6a703f76 891f54c487b39f1a4f519a8ef65ac249ab8b5107b2229b6bfcebf6e4e62e05c5 cc8d344cb262c368825b948778b1f98b6c50d40e4ea8fda0270030f58cbc3e21 a7ea7e4ed95be5dca5753af9a8a85b1871f981392329c023e82d0b9d0a3c53fa c8a5b573893d9a257c62e010224791a3dbc52552b9fd54c2a88ccb338ac27fda 9f431124b1fbbb70602398b0d0af7b44e6f1b9d7c59b6e01641b3be3c9e2630b ca15b6af5fbe14f21bad06cf4e0a16f4c71180a95263cda4928583c18b1bccf8 81318e527a277d0e82cea396d1173232d0c2c33a0f6fba31fdcb21831533bdf4 5fdcacbb8788fa950236ad0eca32b51d5d6d369bcf4afe82314ac636ededcddc b9b917ee218753bc02b5d7d80800046543a057ea74a898fd0481b758023b9a5b 0d91888f3c4a7b5e00b4b7b3553d5ec5d114e85206535ade5ea911b17e035421 0db929f38c04b8d8fa04086ffd787c983cc5a7fcc6f7c09274add50bef7fa0a7 77a9c149ef218636597989ead52be021eca71255767c1479d76182ce7fb726a8 03f959e87ffa3190b36c563d7c0cbb79c84c665032d62ba65fffeb8146da98d0 0cb8e52fe9ebd66e95d048de89da566e91bdb2935cebd6215704095b35dbd2bb 8bf544a37b3def46fe0536e359abd5d4c2a1eee572c8ecc9c9c4d8399937bb54 01a00be9eaeff23ad4f3ed10b331c91693769b37c4da635fefc815e05f883ee0 e7b8b6ab9839f9011df5585288380c6f0c6f4e7eaa7b239c3566b0fdf50e2bf6 d876d750b84a7101b525f2245aca51ad0389c4f09977b63765ef0f74ed92a6a5 f6791da7c59c61513bb1334375bcf9405d872e1341052afc525b592ba666a89a 4c22db6c874ac7199b693eae44bbed96d5f759da0a63119a9b7dc937c994d341 3fcf6028a920361aa6e8df0b280cc3c1a3b22768874bc521f4e1ddda6f6cb2d2 744070c6842d5a88707d6a9e6bea4b0ef6a3f2a049ac74beb1142203fd23bd10 40ce9e5cf18ca8478661a2837a09b477a1218c30793d0899a3abb45a1e2739d5 c0248c521b474b074ab0e3f8bc6bf31f72afbc98feb695f96a23caeec2d8c7a2 479f53579328b78eeeacc32f0487a6c8d6147a3e2066958b62a3c70472fa017d 64dc1766281439647f63d12a83786f2fe5fefd4861abdf3d3c313575a6954c51 2681d60a093e3fe80c5cafb9932c364fe4992e746f86f99f74c4a8fa879e342a 15d896679cafbd3646e9be6df7f7af41d8fb6658bda209a3838787682fb263bc f09859fd5b818ea756cda3cd7cbb7231f192b4ba4f468eff69d09d57f1819952 0949e826cd2421ee332bb599ef5c350f8a2a2774e66ac6df5791180c3f569b04 8a591eb3d69daec16b2df7e199f1873cb00ece3f1afc40025a234cb3bef71806280711001bc7be9f9adb5c80909b137949f0abfc693e8758810e915c70af9d074d3c5cddcbc0d4a4eb2a551116a400172a3263053f58a74ab92d561cda832b094b8c5838dfbb4ad8ba554ff467459f0529430a7d3e55a12938caf941b37a5e04a417d7ab7bdee009b0a1dd9cf091414b71f54a113f4089d615ee19a4a26a750996750b651059e027dfe736233f4b86bf21c95c07d6d77729f70534985243800210869015190a8d64c2ed44de174256ea1e6d5dc72ee578957d0f8f5064329b0c979c186a1ee80eb9fa8f70e17fb44673b6fe2f74cffb01b1eef6374489005808a3673b51302b226939b45af31923b41b19a5fafbfe2bbc1cccdf327d82a7ee07f11c103f9c8a0ac8be658ec1cd0dfc8a686caf8d9d870de7474ff41e2d8c8c09b7985c8acf51a040b52f8c2f1efe0008ce3e16aba53777d1e2e69e85b6711c0472ba75413eaddc1068bb99570d276f626cc19bc673ce578e2764e635a901fd01ce6e2b8e3edc879953ba0459f834240396e69a6c6949819db4c3250fa6761d0dd57308174d19a3875980b8d28872bf8819eb6ffe7378c72e235f3a7d75b5880aa1a2a44d787d1f325c03084e1c76303b2f09375e00538a4fcab0efdb71d8680a85ed928c0b2e53557f4a4bd16975c056fe7fa08c026310f90aebdb298ecc2f0f5b1775b30486f7e1886125f7ede07bab9b454404a83747dad875748f33971e0e8f45d921d6451c92a76b95b6a41d23b1d8d608e77e0e495f2ad345cbf6bb38098863a7500b2a7901615d6c54dfea3936b199c75c5d3d5da85cd0fad766cc0c0aa53dcf6ef786fde9a9cfa3e36c67306a04432995b2042d20103d095ebc278708fd707da9c3dc5d71cc0b5e56fc5807b802dc2e48ace81189f0a371a0f423d20a1cbfe3282487c6e4e8c4d5e2479557e1605f9f0b7d6cf8fb4750c441b1e422030d630380053f49773ba8f72026d3e6e43dd876b994fbebc173e9bd6426303b0f1fb93c274ccdcecdce9b9956da13feb0dc189790f61ca72930256e3f08037c0f6c565a8e9dd670ad267008571cdb84fca941b8e2dcfadcbf69de1aaf74e47101873d3945a4bf1da669f7167f079b4c8d507e2e9cfcd0e2d46e7f2b952b95660383a47a72e419e827830c412aaf035dac72802e81dc1a19ed7441b7f26c44d20780a9eca90033bd065ca7a878eb6bd248161f3cfd14d14f1f539fc0d67f06a50fd3a9e01d659f6420a0f301ebe1624bc3916c035aca01a9579ed9dca0fe46240dc0e47dbbff576558f5fb06fd6bb77fb468e19794f14c2a5bb00637bd9bd4ee00c2545453ada8d156110ab85f642d343478dcae330a690e72b829f4a22cdddd0687f4e60badf622115aa34ff1971ae376cf6c0d7efec4bfab37dd422e91edc70d92d00a01b5751072650c2bb85bca0f488e4295c136945e90a40801ac96e9510fefeda87d61052e6c6e5251ad5206190aca6240ca7c37e3ebfeb300447a8b1c03f7f000bc31f9b58fa48b1beb17942012898a1b049ec6eb85f0407fc54e0c8006d28eb46030f697aacb3ab1f0cc317bf7deacd57fcb6cec4fda0507025a65090e3f70924b42f26023b05bf03bc404f370366b434c7c13a5b875e68f4c4cb97e095955d40cc0b6d12c788169c85524be4c2194f69726684df609adf8e466a8650c86b892f37b703ad528163d9798d45234828d45276044c2ba8455b2be96064b01d075f2a9c211b460d6e8eab83307a91eb1595eadc4c63fb3664ba74eed024406826a75418ff7f0b4ca3a39a7f41797022b7208e73b7a858f4c2036e977ca6600766e9993f9bfb979ebc19c8ebfe8fca29a4ec07eab00437d26c15a66a2cb3c041406c741a21c6350a4f336b7893f407ebb256abd07e4b7df579581fca8e5070adf63fe4bf3c5a73a0813bb895fde8782c60d9e0ba9172ff546211896ddbfa40e41d3944ebe412661b8a941736dee1c4029523ed6c798ee28c5869b8a54c35509d02e65e24aa8070ff349957ed3aba176fab79d5b7bde624568d55d1eff621e00da89418e51722bd34a842026fd28746851106b834044d408787563f4b103d9015d7aec584095dd2f3b79c8d359273c223bcb57dc3b01a486bedfbcb28cfb6b0b3ea9d747e3af535270902cec74a6e7f1c4a31ebbe88205e5f87058e5a4cc2505bef7cfc1cce634f7a9e983a4f0ad4ea3b616dc576b321d9685ce2c3c973b8b09f976d0023d81b5bdf374d59fdb02b725841164980701b02c087e44b3392a7a09e31d61b81f3c5064537cdcd3f35ad480d88c3d089d1fd7688cb5370c1552d2072a8d3aa9b675126a10000000499543cf9551dfe1506af3cd0c4805d842306a0c8900f07cb0f203ff2b4ac4c0e231c25d60cc8c0804377e07f9c69601afa2820321ecdd669c3847c0250fe61f30c378ced2de086e6083719b00c7dad92fd1d201bf5b8c0caf14df0a521a468208a571b717190686daff53e762b0e67b90ad320c95a364efcca92cadf58e02f12f2868d6d0e279b11fe2602865b9fe557052b8004152197953a21a0ff5181c612c33f844346109bcb5df89c97d3d750191a08000b6b37b7c2dca3a033f290f3b256d9702313ece283d47176542f7ab79cd1e2200228025ab4e529c4c5c4fc997ba4084f1a2ea314566844ac357963d41cbd3150bbe1949eb86147a5fb9d0818c8aaeb9a9584c71f110f170bcae5fc1e9f423d90f285664a2e7bd79ed38087303919ceaa691c7853143529282e0229ec1030bc20d091cd13c6cea0c5ea01757dd8d65a59a6b6c2c8ffce05f142dfdbee8d98a080bbdc8644cdcb5d9f8a57399efb0b86d7553bf39944e2b43e28132e300f463460e8974c116df89db8552bd734a987e9f4ca2eb4493e3fcaf8fdbdf21a4308b170752d8cee22779d405972d4d521e7ebe57cd72a1e83c80d65a998133e90d3e1d0ab8d144a8e146c196e9124523430a3ebf28dd7d165ae952930106de1a1d45560724e586c16aede68a2c5716d507c13348772cc46c42d88987f184b1790b9db505b4149b479f6a1d6053cba12c1625b7f3aaa01722c0ec2dc33a083523ce30e704512324187479d4c7f10b917087e2c5105a45b0a23a6acfa8172cfa0fbf8de20a300ebbc85b0e5493cc6bf82c930cabc53a76ccdc2d304ae9d7f83f0060abf90353fdcda680502c27ed4001dc272e2019cc81fcd69cad6ffd6e945b1d0488340bdab904e31efe25ff7425e83daca65395dd741f22c79e0206a30a9193b9531208ee64600d203d57857a505136bfc659fe6d247f17dcdd9005dc3906ed9e440d004f1f461e3a59b99306aa583e8cb4c09c9693c7a2ecf6a9d310544a232ad67f0d4d3024e6e724398b29656f699e170c284718b05349d1349e04e6a8f9b1d9db0af5be2fa70d0c3f651742ab4a61597ab3a6754f19324c96be485a5432e7ec1a0dccb85aad8c9a3f2d817828bff7f080adba0d0955911012b3326a855c1a33570358ca992d43104e5b540a05bca32595010f4a757f4c293c0aad9bc1e55092ad0677f65e8bfda8a8235524fd2c15da911b5fb6470667020f8331d8b176eade140eb61c456feb0d5ede8df411ed45e3cdd43dc5ff886ad844307022050d4d506000619c34fcd1ce3ecbf4c2a04ccb0fb3fe2d923137475c4107bbf8801c8830ee03e777584392a56afb13d7a4040b1f3b3d9ceee82785aaeb8b6c729f1a06c86d0f41778be52fb4fa8684b58f31e6d8ac7481938a56ef22cb6e8b8ba6230f57d2097a27b7ea5e80bc5220eb8d29521a524b6224d2ea14b0893737ca18bf343d93027a06ee3bdcc91ea3a911fe4c6ba57e5d2669c686d2d2abed793db6ae8e57880d80757ef305fd2e0a6834e4dac6984439ac38d748cc0c648493e61b53b1c55d020e87feb6a7ddc2b85e21d81701ffbc15597055503c1997bcb48db07bb09ffe01431c36cf314ffb9a8a22092e04d33423ad9b37555421d01b275767e57675c50a31f202a57f8946b3cb3fb9d2cf3c0b72750082939c4a0c5f6a1a55a56428680294892dace612354d6378eb16a8a1e26399fe8a32c5b86f6b86d8a901c0930a0b3816f18a62ef80196eb24df9cb8c6811b01384ab406d3496a92b556c1a31c607384b240aaf88a59010601a12d7f6496e671ba8f5ea2643703a356078da7264050b41e5b0d749eac5661545be9638159a9cd3eb5c7be9c1fe886fbe6a14d67a0e7b6f065dfb04de1c99def1a423c3402482e609adfeaab2be41db15be2c117a0397f6d7c4f4b759199e04be3abc42b46d1d7cda2536ae1c95201d3de52305c40ada0608f9318313358fb5d2543e05311816bb5db063dcef64da09a4c8803acd00e393d051f7d1cf31654bcc5a64e05fb3bfc6e4909f2b39844ba0219cd6eb3c00a338dff59f9f25c1b2d8bb48954ed3a0b2c40d2c51007c8ae4878c01c8f2d805b22d9106ace6a606818851b091622c5622b6d6aeafa3726ab5c6a2a4aabe890a0f9fe29461b16b6b7a0c4d1f34d39c36963d65e70b6646389da5788f2cd806076936dd5dbffa9542104502438fc7dc7379ac84d8be4e4f74076c626a83239707e613ab08d198a922da7aa9c2d65a1a26594fa3a9b26ef90079b6c5173847050ddb591ddaf7950fd3dbbacf10c910dcc82b12fc74b174fd6982498c3b260118063bb6a3bad99db0fd665e149a9e44d77f47ed613f8e093a1ecfb8ba3e29c2c3021b71418f738523de9ea73e123d1d47d5d5f07e8559324f04d12df50f191fd305c9a949056b83d64656633d4782f2180455f09c8b3abc9039488ed103c406bb0af20a058f323e899681be337b85ce69fc5b1478fba48724483b0c04877121f20c95a83376b8c15e7a5e57f579586d7f5d1d82a894516e70dc4cf8401087333704f935b7d6b5c31c36d017422c9bb776b5239f3430586a489f6d530a1f34695d03a22b8e156ef80c8b381e7fe3aac48f730144276ddb013154d1df47175573080bc4f91b1cfee179c7a5331065a421c7871edea6952f99f1774cfcd49bea7aea0e63aa146ffb540b93d99dd999f5d9dc7769e4c361709bed16bb211a5f6bb0570f6ee913a635531dc4acf1cd1047e4ee6f8f2af927ceb0f8e5426a1016a7a8da0f4ddec8e52cd1f36b913befea7f2188bca440a99c7b95694d5b441deefbd2060bfe9ae6601a6b28ac48a952180d503f9db023773133e61fb8c48ba632dcf7b00f4a100875265f0bf688b7e632ff3dc97ec7d471d9f30b84e77889eeb58b56ba0b8742957ca0375259d0eb4dcc92787fdee75ffb1a2bef913dc193deff8941900d28d19c9edadc49096b4707abc9d5c06d35c738046c245967ad5007d8d32c790981d0a113fe0cc6140d5ca09ed18cdf5bf690ea461e946bb30626f1cab447b70f8527e405ce270e6d4a8703c3634596a3576ede9caae8e89a4e986a46b0c23704b391aeaf54b69a2e32aa78f997728925ae7df442ef08470b80602799b9d07a06f76ff5e398b9097af50010e752f2e5a0e4de4243cab4ec16765c05b589fb9e09b612e5a4065cbd485896be96c70752d9c1fe31bb68a7c6543749b6cced68a007740126df388e23876b82e127ac2c4dfa0f992d9b77e69fcfe1f3d00c0223e00d96f9707cb0fb31a0baba8bb909bcc896beb3de3cc47e618fc718b24eaef87206fcde76a16db63dc3d4aa6aa3d470e690f14fb69c817154694869ba4eb1cf8a019c72947472d05c39ff5723ab76fd76dc4586a6f26aa60e4e3fcac2cbd992770b55b8bbfd1206fe3bbeb3d09fc62d6d5051b02dee1167f29c8cf35156b810c3047b478640300ae3b85553b5309cab9db05b62dfd69cd7b9dae65083aad0f2b0033b5c14128ed603274cc0c612297badfcf2c2b86f2b31a2eec0b1358e52c91d020d2ec585eedc020c12395e9b81baedfa95a902da05b61dcc7a412e23b959ad06c7b18625c94b0e50eb6a4101dbc8facbf9164bc81e6599b19525687b9f4a0f08191bb74d89ad89e72c66a4e5c9eb35dd8682b8f89b1dde95d17bd071e31f020c4b4c85dede8936c8e9859e3ec6d2aca9d4a939cb4ff7be12540c9fae08873009a13ea2648d714f5beae68fb967e5f6514336f802d4e9afdae1a9093fdcd1ec0bad2f70f41151dbed2f6b0669c596aa0aef589ff83ef94b63904a32df6a3fef00a224d44bcfbe6b9cb40fd017f33637f6e509890ecd6d0388bab5bead7003de07b0207d287f9a53bbb8724866adfd7c4e22735d72a4ce1766ad694cb8bb790a04f71341a1aaa2b8cd1b10a92edfbb6906c01754ee2ff1751378183b9e6abfb20648e69a822d8bbc81c450ad1d4a85c53e8762c3d87c68ff079a395817ef91320298b5d4d83597cfe2527c29749c74d5a639e7d99c70fef4deee0bcef9f8d0770aa66f374bbe7874f789f054a33f42cf17a6a2a587f9611dbc199ede332acafc0a1d9e283b93bb551c8feec51174c23d829db0423af07df85c2b909d874dc0c503c22b802112ff6a8902888848c8842ab0321d1192dae42aa772f3e70de447c50e3624c66ba7415cb936bf79cbfd611cf50514c9a1c10c758feef14a7fc62db40dd5443498ad155caa3abafd22839a410d166f2b439e44e8736c65c7cd40a96805c4d4f84514b45c78084865fab6c98e29c6e0ed8ca0fb6fc3a5a8c515690b0e029553943aa903dcfe96f17fe48cb1721c517692ca775377744d5eaf3a50901d05c95e412a75f08ac26feb05f150c3d847020664835cc9c54cbb625f588f8abc097f075e07c08a6d6e4fe514079c84ec09c08a8760912a4d801b4eb1c14b80ce02731cfe487a29ec8aa9c65340755a63a083f1a007235c3be63d2c1bf81216d001f06098e32ae6244641d18b0b735dcef18760703ab906d783b09d5ee1533ba60e4cb9a12acafd1db8b8af33b59e61029e2c81dbc3c27da50db0e9c7d149ad0b0f850a4a653b8f175497ccc51ee1fd45fbb803eac30be0126e3de907e5b44a92083e85a9f0781bdafe190d152923b28d34afab1bcd08dc3cb211377035f27fd70d true -check_ring_signature bd948a9d20dd526ed0c7ea99e07879c9eacfeec8bdc5e011724238f165fdd361 8508ff8b97fde2f91e25d20c5f044071dacba8616ba923dd37ea2128291c9d1a 32 3f22c789dd3a17dcbdc5ea87c47ac99b0f58df20a851c190f1d37a4911fa01b0 fb670ad0d35ba957245f2728e0ab855160be192642c467db55aa9c64b056c8c5 83e48cd84de97c51f2e10205785617297a95270dcb7eb323b7d15bb7fba936f2 132519dbd43b978288e709ce2eb8b730b94b3b2e785ff1acce134a66da7f3dca fcb418887fefec11119e2f3df1f7dcd6f5d213454163a21a3fe3abf45d7b8d61 3137c98abf4dc5fd7035002703373db6245a1d22b4bce1c2936045d50c68567d 7e457e83bdc0495db118cf215777017471e853cef8f22a04454ea5cb6bb188b4 524f5bd1e5220b90fdeef5768023e7d002672133da0346a22db67c79e5ef06ad b0afe2aba41edcf2a3836504ae9a000c2c8908128d94dbd0891e84df8c86127f d4acdaaf564f48b45a0fdb41bab36fd283003e5dd87aea6a29146d1835bff615 5207edf25efc74ce533012ec1178628cb479593708fa8291acd886239577a6ec d3a5fc9cf03a1b821bdb8da26b0d88cbc2e48a32961d7d3bf0cb6198db522396 e9c0fcd91a34971663f17cfa32d66f45c95678c5ecc36a865766f7e407a3a9f1 03715dfa15447de32e6cf72201768ce40835120f351406e2d2480114a5e1b94b 59ff5bb2101efd0a5dcb62f97ec45c0050152010c932b98dfee23c2fb9d70e1b 6d08c992f46be3426d909b2cab64cc6d7e8781938b497941b167f543a6bf0c9c e94325c2feb3a4ecb626979f322d88db46ac973056a95c92d3f52e4e0547813c d6759babe45c75a13392205f898999b668529aa78baee1949267fbcd8cf86108 d865e11b2383c4fa3ec0f19f8e74e1df4bf73bc6d5e72707293a6fc390ef7987 227320a2984a0602d62b9366272b8284099d5d4c9824e7f727aaee7f78a1adbf 8d27a661995d9587717c61355dfac1cd7a5cbb447b3ec7e7979a2310bc887e9c 60c6e289b17b4ddfee3e1709d382689148a03193ac62f1fcd84eafeba1ee07a9 84ce866585f0d49a6d63e62579230222f3e3dfca50a01a717ab5805c5be05459 5ac03aa1757a819556b01011e17c87793ae5da1257c72475f510322d1806c80e ea58bff2c511b8db09b8e311cb351e36f0c24ceb13e1eab5c95bcb180c0a0ac7 f9bed539ba49ab6233c6f43c2cdb54c6d4158b99a49a7eb8c24dfa5fb3f9dab0 78a3eacf398f449fc3ec00647475af44143a5beb2e2786f58dad7b4d5da1cc04 dc34c01eb6504e4f1c72326f2376ae9bd687d726e19b1f988ae368a32115b5ec 0e620e7244f7e8abca5c54d82ec661104fe93331384b44e7a8d965cf83bd0c2a f1e301092a2075cb8df09fa10e2b1bb626890987cc933759cfa71aa81b1daeae f729e7e5c22421497c3d077743360dd09db79b70f3a1633e7325dc7a36aefc07 9fab0dc213aab71dcc7f3085a39e0c6c5c162387af7f76bf04bfa5cd38b7bb8e 6caa88287ce345d24dccb47871780121a9e7e8faa8f18f7065b8d8fc4b9f790da0fbb8f85d5f02c388d6ac3ab6ccb73363822658d64af197f23f435b4a01150c6677c49a5cb76646988370ca78a94c50a765079e9b10c143248dedcb30480d0caf0090601f747cf351513bc17155e4bee47857f5c777cf49a5ea8db869f28a0dcc507716696f169a81837caad148a6cfd6c79b303ddb62705e862443860f8e0595dba61f97eaf63113667c698264b95897300f69b54a7ed9fed4c18301e31404e8974cac75d3e0db03a960165871c3d277ea38b6395627c52bec7405b865a4077e99947e04ba28e2902020d00150391c6ac38f6aea5d7ab34d140edd73882e0441f156d195ad560cff925544d0fc5aa973c0ef7dd720aac6dd3cd339ccbbd60ff464ed81cd0e161112e5656d036f2434130059ceb96c11ee493f607c3e7f5a0e55bef26f0be2e6bbd68b1b37ee1e6e1e0051a94bf950d31f6bac375939c68a0ea3aac1e7e1f7362f0437c5cac0f98e4552be1156fc331bc1724966843f064f0ed4947cb2ee926ffd3bbb0b09528815f16a3037d10e6770192ed16cf777952d0f2232f6dc18270863c6587508631ed4a2d85563231e6f7736091087e066cd510375d6e37daeb535e676b91e65a66f62cd03ef00f56dfa98b79c6a8ae281ece7003ebb34352b837ee353158ccce690dfbf5ba172534a634d15121b021d88c5b708c63c92e24cc89bca58617eb13abe32218604e003a67c76dadf1ee4bcce1b6e015aa14ee273043675b8969dc95322e583e5faaaa034ca419408d0012f2d5069016bb3f181767c9966238e0f72bfdd78ee8dd06225625a77a12a7e322b364d060fd2aceadee6decf99c6bfe8ae9961fc16fa55052fb691e2a32e8c8b70899a970f851b90af193d4266b3ab337720b8fdc03c76cdabb68528e885005ce246fe870f68f840d4394e37b5bf7427c703060d64291584a9d4b0e45dfa95e9d6c4b35801e326d63b493ad501501523ec32415d1c6b6f30f1d6a83628e371e673f680ab0ada3aa808323fbdaaade3ea8100a4f44321020427e78bda2e5efa5505595aa60d209c8166ae3be8e744b7c98d0278bcf962bbf841e6085adfb0dccb1d3bcd7b0a2fab483150214fa9725a3c1a21bccffb670404365fa5c1dfee772294ecb6b401aae98df347056e51d42e17bf40e9157edfe4e4c27471a266a52639b92cb12102e35ae7267dd9c29e9679fb7078814a72f5a6ea91d27b538a4017017a5eca270bcef2e3bed0350729b3309a22c54d1686e402b216d8d1de333489c04d67f78c09e685bc898d3a08a5a5d294b4d256c60b930e33422cac03a6e2c1df06f6fae90e86c4cce84f4ef69c40d1012bb60cdd7c07bda17a3e3610c61ca13bdda171680cb592ae8e9d2cfb87647bd385e2a9fbc7b9d571014b93e75dacd2516dd4dea10b39b72009b0afd7b87c60f6a48eef98551c166cf7c10724873e4b135224cf27001f0de1c76dbbcac723645120f34fc3b4001b29f277eeeb56e61bf8fa6874f6082a84272af91e605ef4a3a634cd4e66a26a088084e2754e28f035f8ec7a90f70f953debc94a699fecf2dbabf376507ddce9c6e11ad766617f54fe17119e6c6d003e2bbbe51dcbce0f7a05d93cda65c33d3012017d1efc3e16bf2bf480b98dc302df93102db4c3d653ef8813fa421b0a2ddba17afa682ba34b26251ac4adf310053a8f5bda08515f66252a3970617086996d0250797b6ef3d5955a4a44d58c3609b264c06a5da482e1351d8f806c2faa9e63561a47508a169f283e4e58d6391601fbd3a20b8418a052f073dc2849390654af58b569ff7fa2371cbc67a0c4f88902426ccb1e6ef86526c26d537f995235b894e963e89f5018ef4afbc397f31f3a09c6efc24999fa74920011ed5d54490e49ee02239bc3c2878ad688351d8060860e57d8f8654d0368d71a86dcc3e7f5ed61d47f1d4db5d8fd5a12573d33c23e34050e36b8efc6f58b740297b27e2c3394fffa30b06be868d63b28fcade2831e1005478f04fbfb25494eb904b32321be88005a9d445694f5e157225f1a20e7525b044bdb08a4a78abfa5049a6bef03ad230c261adfc6bd899883fdb49f1e1241ee007fe50398f776bbe66d7906881b3b74f57064de407e69294ab1b24564dc5baf0fe922bb78463678c3d6b0aaa3384ea14cb28846bf42c59da3aa0cb023cac06f0c0835ffb89e6120c6ac0fc8c7c70ceb1ac3c2b8aa3228e19a8e794fb3ef56b30b755af98173232d184a20cffec85b4c0cae3c072577491cb87cac55dc370c42019cbaa37ae98e032263689ec06add47a9ac4d5d9eda8aacc0648121ab6502070bd6bc19ecf20154fbee21bea9bc950243bbb42334cf539f7415cbd46718023f097aa8f3bca72fdaa2b561af67894b1f061207e0ca8eef0725d9dfcfd17e22a60924b22acd63ceac8f1a9b3495feb67157dc2ef322d83d6d44c527d575746a1a0f6ba75299e7f3cb022cf27bee01e575b0b4b97368c3a807533bf4417d5374db053497de0ef29ac33d15a277281fd785a1525a41938caab495da0fde2ca48ba90f554a147c5bb1fa0a59b895ed91492bd1e3c25c80ffca4f088d761a4991065000c67d71ff31b4e44e20688a9b72269334fbec5e266b881cb51a0d28004d16270b51dee2c1338f5b952a633921bef35a2afa3cb526fb3937ff91f350409acaa00df59051f4c5c53ca07c276c4d73f5b0a1ca45c9a7decc2c12c50324fff264c6067cc0bc29664e783d41b030d0c0337d17853ac6f71f3507d44a9e5b3d15e9390e192eb91b6553c8c129331371fb6d742c31a7b2f9ef41efb135dd4b7377bda306934aa08e351ebbba49d973730e336c6b09533e5f95e13a1d007838dceeb9d40d false -check_ring_signature ab0f1abba5d701f163059cfa007d2f676442f89b742227fe7798e93e99250c09 caef420ff7162211134bb3547c2128c7339c9796b695fd158eb5b22d70f2aae0 1 f0bd15af29a5787e0c79c39f0892d27669b8e39c763183f6fa39cb32639de157 c7afd11d17f6c72033100a8b133d656c30c4ac4cbf756c751587152450f5f00c08636d06ddb035dca3220a4f5e5990b51155f6fb0ad921ddce290fd51ab4b60b false -check_ring_signature 65e5aa463309a8bbd4e460b71cfb1ab0d54ab443e1deb0e9038cfe5ddbada757 64639318fdc5cb264112439ee7543cb4b64483abbb29636ce383a5b5bc61ae90 2 11a083f2a26ee4fd1048de35de6e139807702010fac8ce24634a9b391e1e8eb7 f705cbdfda93d825c4401d0d43b0f64b824f34d3ac312c2bafb8ff94202c080b ca1f7b3efc72d962f5d48004262dfe6dc0b0fdb0e7fd0289ff78465b7ce18a019a94aaf30f605ebe5bdf7ebd08ada5938eaf49b7145b0db8eb514f33f61821023dbbe35c60d50e05e57fcc4a3a7e191a07ae599d6ae4d81462a4fa851d4a590f5dc73cffdfa7dbdc82f6f7faa6d9a45936b6472e4e0dfcde607c8c8e1488680e false -check_ring_signature 1156b75df99fd4ac4170ab157c11cb2df4e2a51ff244ce90624269a303ca25ee 26abeb8aa7ac77602ce3a1f20a360d4323dc2f8e8a4a1f724f6aeca5a0b130c4 28 9717f2cd42d77006900aa7367072ff3cf34376713831e98774a2095b5d94c1ad d4133416ecf439c544550cfc5b056d8d04ad811eceec6fe7818dd8a1228258b6 e6b92e5ffa48c0320f24f1f00a603d626933a9505f52f0b1140bd46c69e38ac4 a513f684b9adefa20d58211d36effafb5a5f6313ed0d3ccc910d2ebf167dbfb1 88e181511ccf04e185be0854c9c7ba6fb43b20beae4b82fac6eaf172a870b750 78adc822c26e1e2d717a074b78ce98226f2575e5db2c56220032916d8b93e426 daf9293694d9ae5dd143e667dd6c2c069536a1f5e666da84d36be345287d8922 8ad1eea19d46c7c17cba367b2cb50dae28a1bd41148398158554d741b6a45652 51c399372de807d8990d514138504aa24a066d8946cf094ecb47081e3c10f80c af54e68f6898b15989332e6bd91f5146f3976cb0fed8114acaad9608bc730c2d 79ce03b01c0dc4f257a9f49dc9ddc76c42dab109c1cb1dcc81a7befdb965f40d cdd9dc55a91a853c7d40e77e8d0304f5968c4d38033e8026b436f1710cfaf747 e9a048c7a95486876a9ddff5f3178e84f6fdb5b0c4b10ffe40fdfd14acf33316 08edecdf21ef4cbf3e2a4ed111951dc2d1f81731e6a0545d92c27a8dfbcfac0d 5dd9b2ecda3546b0468dd419c586420d22cf7b705ec8fac16f05233076631c69 eda2a5d1e26d3881c07574183f14c1b08fb01cdc5f393ecb9e243db17013a1b3 05d32b84d13454f3fa2dfd43ef126ab7a3c2b0ff8c56a137d15caae28f7a5866 d69d401e2dc000b7e8bc22731be86d8f9f0ee7c280bde8623b0bda5d30495d32 9cc22974d155e78d0ad514f77aeb402a117fbc8b8e704e686ec6e54c3b754ae8 86e44ed6dda7684b2584466ed17efd185d3e53ce8f46ba69a935c9ded49d4868 ce2cc8dfdc4f6e2ff04c372ec1dbe647c7b58a91e98579b9685e57fa45575cb2 645d271f98183226d233d9dd75e177619dd9546d6bb0885e460a00fd3c049640 43f14ad7f620fde181a8497f68ae5ad0451301a9c95cfeb578918ad40b99d8f7 30452c91184b04b5342329b15ae1f326eaa3674dc02a04c6e15ccb6d2359dade 02d002433955ef8510fba4c7be861caf79a40948d3e1b2305ae6268d7eecbc13 747ef7532232fc1fbf525b9b1b497ad9bcbdb277d7d05037a999557b7e1268b3 980d736a5c0ba71150c12b68af368c15b755b789416977c7731c368dc5ada5f2 9efec0c98805714e00f2b254233a45b9465964a7046f00b078c668e5d000a521 d2170033a7eefa5f5ed223c855b9c54762033f12831b3361d7f88de2bcc1c308ca74f6f9f76bbf832540de2aed7bc9c697f8685a8a5d198067cd5dbea009ab0e8296948b366c543084feb2718f0cf3d21551dfd90e289a0dcb42adad3cd0530bd429521b5ace9a61b94b66d44b25bff8d11cad3c515325ebb16ccb08c6a6f60ead988a296de1b049d9ef44588ef4cd5567b3177cb7b3a1c8a258b7717285a105d28c77b77691e2823b2086522389eb48a96a490630b6d65de86f564ee544d0006edb8bdfec92b5d02026d80317c3388f4e2ffc4c641f5720261812c58a51d407b2ebd3ef947676dc91b779e4004f49db72257cdb092343487cc1ab244663c10be9595f77c2b159ebed3d21ef916e1fd8a074164744d5753d89d5124855b2fb0857c6be9bd8e885f0cff9464a5e09c224200bfe45c55f3b2dcc4be36fd1c64d05b05c6cad17f27b6be03c753d69da197dc98fce9bb534aa2f00159cea05891807c397878b8cc1e5437ec1316deb5213133ce3da7d1a08c38d95b317beb539f7027bd9d48549fc1c2545aede032eb3430a8531c9c4adc0cc6a1b9d98e417eb310669d369e76b7022ed3037416c25bddee277755e7a8d78dc3d6d0c931451e43b05ec7264428ed680b4638341bc314fa4333f05eaf306bd5a62ffc7b33864ba9b0d99c26f3fa88bd994e782b51e0b439ab46b81c5cebe208ecabba9fdd49a31be0e8405e8bbacfecf139a79524b042d328bb5453ceab0dc8210a4a8798b98a2c604bcc7cb6e516baa89310ab7445a1e6d7f7b1a1dcb6f3853e33d1414ed550d420bbeffde663bad3aaf99815e8b065178780ce632e2ad740115f7ccd5c956d0430a0e047e648b9a45918dbb2fe228aa273d5f04d9050db3fadee0944bc2dd3daa01ef76d4f33d0ee65b80a774e72ff84de2358571bdcf06897e5d8876ee99fcc4003d6fd141bf5af5f0051e1e2c2cef9dfffe883eb0e662bb4e28ce8403bef3f61077dfabe638a64c0177c6341dd35a2353957c056d7ea5b1d248984f2851619d07f8cc39862259a884cc52683831a4cfe4527b5c05d281bafd12ff18caf16ee10c8270e676ba8aa51befcff22b69ea3d5483a778e310878612ae4d42e2a7b74c0e8528d64f099f5cb6c84bf940ea9192f41f1972c6e1ec929fc3b3d359c452a002f710ec61306bb3303ec5b329e81d463adf4be0a0168af0f5d65a64c768207b0a3a55da47fd5a60feeea79384ee3aefd10fadfc3a6461155bf615865cc73eeb08c09ed7a0fd1039c30a1049f309f0e2085a72430136fd36493d873b7c83f5c807e8f7650efbeac2b3ccc611891ea49d927790975fa1c83122f9ba4123602b5605688024fd40add3de403c2578bbfdf5dc0ad88051250624540174d4a55bf7f80ae0e81c3ab6af41b94c9644b40dce4dad8ae53ac6c0d123d88a30ffe21074f207aaaabf260ef9bc9feef00bda77c5f8e6eeebb7d5bde412d6bd4d6cec0e556d09309176e8c58fc025f14881fd0418c0e152ca087ad87edb3952162678c33c0e0e8662f6a4767a43408236cd7a1add969b5b9f4ba61547e75c731b926ea169c302f893863815786286c43925ec5c49f173efe3fd0d49fef6221026a7380c83480f109e746d05cd57b9f8fdaea42df74d3cdb6c71aa7d9cf558c8467fabc4cb5b072398c07b68a1cdcaf0c9003feb1c6134aae5356eed75fadf8ca9e418daecc80e2396d609a236e366996c54b90ff5822669ca2133975f6a0318fc4267109d2d0c0ce47bea8f2379dbc9de1c5632ee921861297de21110f739b5910027714ddc0b438e0f5d0ef20f80994893853a04078ca29f384a262c5686b23e4ad52942430c0c29893c64dff4777571cd00addf0ce21a0a421798ebd3b139a8e362a3d59102ed3c0d6f3eeaff3e74c3ec747342008824c881427de0e45fefde0ae4e51148054ae645e9d55b8948bdb59235d0db47c362d58b5668dc01ae7b214ec0ec82e90d085d76f459865d3ad309b76ae7be1e087e1948968116a3ca74a251f8b7604a0374756338aacad8e506a791fa1aa5b63ed061bb1c02e745974d8a6a46adb963036ef3cf854d845452c3babc3794707ffa8b825b2ee0d079c5375016165820c30c96c54a02893a67f830f969b3d80b62fee19b044cf4e7127fe169a7dadda13906b84786ecbd1f3d43f9a8f86a4f7eaa8055870bef9194a960dd50e0ba56831e0032a22c2d3bb2b0e6d7b50d05faac97de58a864b91074efd82f93693aacc3550d9b39e13cfab879250421b0d64207dc3de7197505c1c956a183e8850d06ab470fb7e2848d613880a568f6880a2ac7610f281e2810e46de9b11a0675e6ebc62b0ebdb21f8fe3b9584b60fdb83794c9511d344e8392ad1d9152b65d102309671a0847b837f89159732b6af33a99e23a4b61856427e5e67aedfead69e9857de3920c7a60fdd8e087afc214b33d068de08009ac9d1b40419d3fbf40b184a7de09ed02062164d2c32e5ebc1ae476eb3f03345fe6b31ee8dc02df90e276ce4e58a7c503 false -check_ring_signature bbccde968680defedd1a38e09239b01a28400747a33151c8af793fa26d9e49fc e56cf2894f0f963359a6eea781cb564a13f0fb5ace5c9a709c88a5f1fdf61905 29 6c3a1e98b66cda3b94123ee766a704cb061108b7fe3b5e1684f0d5e5355b8a82 7f7af7fcf67c0fc2e0eefd6bbb1c017a218a7eb44667dfbcc9becc838ccf2e10 52b1a946f3571c6d4866790062a12e400f51fe8acbdb9ec64b306160d713bf03 00f91d12d73515d1d773a49f3dd85bf27e74935b8745fe0e50b37337644ea3de 5102beeb52cad0627c5e5396ed740e24fe8d5a0d27c105d58b4b9f254f87d99c ceeff99a3ff66dae3db324893296724c34e593ccb83231588d5d9de16a62dd8e 682ed0febdce3c65e24da2bbe4972e3a9bbc683052b79155165cfdf9bcd10c66 c7594bbd4e607aeede309137966003f08698034502124f56928965218e5b3e5b d52b7ae361817ba351b32a331637d4e56422a53e8a5d02300f2bc21d52170327 4b9707c2db7c6764ec95ee09815ce19da0c9980efcb60a03cc69c05f286ebfcc f6313e7f55ea429b9263c91d69ab56c449b03666282ec633d613b90b051a3fe0 a757b95d7b64a784e15b06a3414d525a2c29ab532c5bf3af0e661defc3927651 6f305dffa9bfeb7eaaf1b6881a385e0dd24d3d5164ae8845e56cb299e9406f42 3cd50aa95ca7b00f2b917773d52e9dc72a8bdcc8f5d838e25c4f126705625f02 bccdf0224db3550a0e93041f5123aab17c9d54d36179732cc1728094455e9e46 fe99f6da8965dd7b2a5dff15f148d1444394711b76b97af69ab90b070459c388 f5ad15892a3ae4f2ddfe3566fba5646674aa13cb26ab97d6e1048bda83679c98 4b7d9550efe912fe700f396cac3ac095f0af00b2b128d307ba431cb16dde605b 41c802ccc33dca90859c743f0b6b80b363ea832e2a4077c1c3b8c18f45ec1805 2a48606542a30e291094cd0156d84ea5f1a73bd003ed3727782413df2f977a36 d6bad3fc8b9f3797f814603ac400c50632d88637c7d767b8c99a5a28276b7dcb 4065396dcde866b2b68fbcb9a4937a26160a960970b2912a259c5e0ea7727b4d cb5898b278930c5f1f2f3539aceaf5641c21de3a418b1cf5524d31adbfd78fc8 0280afa93331c63b9609e831a2514d1a95778d281f5e8bd4b5ec84f014ec0fa6 1d896267db20219e7443266c57ed011e62adf2ec8b6348489a94526e53b9ee7c 6cc442a6f99838cb432dfebdb8e9213d38d324a8b55c23955cb84fc00df95d57 9f7315c162439eb4a89389aea62b85c7ef4e3ba5ef6cb268f36f79240aa01475 31dfde1a5324f6031312c46e00e7d29aed00db9937374a0f11bb1082394c5985 ece31f1d0489d87331efb90a58a093bb6e15494403320a52c9a622b410e65cf9 a8b2d84d1bb65bfb2b419f43a0278de5ac48723d995ec17e11fd4a0dce21750ebf7ebb934292ba34304302f1060845d1ae34369dc46be53edfd8ffe5ced28e073d3ed874f295e33f51958e31d148fb6c7f0a26a0ede300002d75651a0045cc0bb66595f2136e8a7b3ed4102a4b2acd06cb76c4abb7fd109fa220d6501a4bab043e8358638ba9d63916b0b3c009b574b69d7e5bfb5bf774b5dd687fe604b7d40a8efbe41dd21eee5e8dbe19cdac0b40dbbea26157c957968e3e4d66836867330ec41dfeaccc272e74e1cc0276216d3dbae0ec4e6fa0ada75977aa89fe4cc461042aa31da8b38f4aae0b22da4a9d1dfda674e4fc87f735eedffa2f2fb4ddf99808ce92786caf887df682b4b746dcd5bf7a7ba3eaecfd4476f5ed2cc045c51eb10e564302254b0735f57db6fee64f1eb6409219c4c754560506c52c16897f2523044b17f4586bda477c71518355d204874c3e90c3eeff937da9f99e4ce9f31ef40c88fcce05c477fe510e92bec51552f9490e36709bac79511ed922add4c2d04b0b5a623ffffae851854fb96b7f9f4e3e2d2a449c765aeeddaa55e9ebafde83d7077c55c173a036bf7791a2bbee7cac44c30ee85535cf37b9eedd532426fef4190416a3728ce79d9133f56ce1c225fb3977735dc70cd817515bc65a8a2842b8e80ea4280626590c7ef41119337e84f3d8ceefb6f4ed8b6726663d6c6e4180a16108357896f006cae76b9566e1e29d32762431b69665e905dcb7d5f2610184fca50d2c33a2fbc30bc2704b099f0fa5dcd52ee134fa15acb7ce885bfcaa88c710e40f796c736b25db3a39f34570dbaaa2c591be016ac9e7ca7d638301242549b6e80df9a2d9dddd298bcf25b84c9ca2d94f09604db4c945612a2858727c2aeb60e50315668c8afe37000b85b031e6dae8b7295dbe4244f245746eb1c75168cf89b4045c2c872c757491ee8dc3acf93970aa68170020b1fbe5be9684f0a951877bac05800ef98a9bca77cdc646adb7dce6ca41e69466a6bc18a39b198dcfeaf465e0010afe2d0c4b48bdd12432ab4d06052e1dad97a7188becfee896f3ff6ae27bb503f2ce59bcbdb3e44da59c6b6b6584af2c84d2cc869f00ce522f3443dcfe175f09d15b11b7ce49feb3bf6ce26eb7e74333691a669442197c88a5f98ca4a9a6d0075d0360ac13c484e0126c41f8b303b99bbcba9ffec98f8707232879ab7c6b0408f7b481f90773848c9d4602c23f44dad9ef184fe2555988be7eb3f9da49795400459bb7e3fae5ac37f04a334ed47492fb25aa1645be196fa63ab0126248ba9205eae0e583cedf6a3f72bb194460c0ff3f8611445b4e98395bda94e52108dfb5006f24f3dda68c51812b66f217aefe2c29c7be0c836d64a2e651707693a87dda0b98eea24e5f6fc7079ef434f7dd848f3251373dd5a434a9f899b265e4cdd5e10fa15a743fe88f44d7610a114c215ef48f80cb3cd3977f01447a7687eecc434804aea16d6e20acfe1a532961c849fff73b0f637bc757e3f8dbc0f324cac544eb084c68b8d6c4be6b334c44fd95bce56d823957a7ebd1f2f5fafb23d8591af2ae0d86c8289facd946aac3892db75414fc46255b6594b95e4be27ff7fb8cb30eb80ff8088bee959026ceadc8c3c589c336830c53c2bacdbf423d1cefb51b8f7ec20436cda1bff8f1c0b205cc47c2f12f5b987ac6c58314b08fdd35659f5d093b060aa7a9b19ab4e803a9d4b657eb8214560412e40b801cc9c099d4818da154cf680842e8c2dba4bc601571e8544eb541e2d1824b8bb93da54cbda21364e2d84ae4007f9729bf37d857e0635b50608bd57985532c0b3cdb3842a45ccda894b102d20fea84de8a90e80ec125ed74ee103e041980a9ecf8fd9d2e836586cd136478280c8eebc9565b209c8c49e4804e03fac51e019975c90bc5ccacb60f83023a0d14040eaf6ca2287ff3d8fec6fa68fc6a366009cb8dab486452cdb2242b0dbb40c806885bab641d32f1336065ff3bb4ef5a412ce7db565a3ccab274d0296dfab3ae0624678cdc547e80367c9f2d62b2050e5585fc7405a5a2895b86efa947f07bbe074c7fb7d1c44e304c1f00c845563e95293b5cd82df0e168666d509e7b30d3070c5d5b06ef5e8ea17a668b22b2f046d559f8a98ecc1b7e7216681e1def05c39f06dcc0c952a5e392b6376cd00b3af1e265143221009621c85a805d95d647cdbc0861126357c0d9d9321a91ae636c3dfb8370d7a8c2d6a1f696c75bf948ceffd20e5244e38e842ea94e743f9fa369e5a13c3dcd2658eea3e61c43decff843e45e03ea84026dd17277d2960081254a7adf1b5ffb45034295dc96be00f2c2898c46040acb75f022182319506f6353af18dc23e46d8931a30f40ef4b9ac650ea43c30c957d545111fe00235631341e06718ba3690576b19a826f46933d5ffd41301a0714a1e335b5fc50f2a04c036276658379760e7e17714323cdc9db37ff6b09b9014fea4ab74b0c37eef848edcbe74e07cbcd1bd65a93f619da91f60f806c69280770ef962327c817feb660f3949571d41ad59f709b891b0cf13fb7852cf5c1a4052dd213ef2d6f7bca7701625ca45fc3390121eb8872516c0d0908e7dfca16f603 false -check_ring_signature 8ab32a90447cc4a37bf016de7c2b496ee12cfa5d4ff74deb833e8fa8144e3ccc c5a057e865122b716c4f9697fedf3d5d4d67ef1cbf19acda12ff685c9dd85b88 1 81cd61615f31e4e0627331bf8e82f17f8352e2ae2d37072cf6e0a01c6f35d4af faf26518970b107a472020b9cdbab711ddbcd406fab010385faa3be0cb090e0c6a58e8578d7575c59badfcd02425ab6aac85221d52bf0b4cb88e71d11106150c false -check_ring_signature 79e8c22bac02d73d3ec2f316f16ddfdb818ecf3d4293106f5128f30f46414cc9 e6dbeed65e82c32e44237e0e3f723369675aa3b021a4ec0e2b943ab4fcbf15c8 1 c9df6753038e697e22dd8bcb6e6303342cf2170a3198c085316bbeefb05045bb 360d06daeff4c03d4c6d4c046e5e9dcf561c398b7e89bb3f466ed48a895afa07ff20406035884a68a6972259bdb1c31e69ceddbad5f76b6ea9a05da2cfbe07a5 false -check_ring_signature 7312bbb57b14e0575e3f45f074f9922f4eaea507914a46a96236c14ce2066e29 4e77c876b674f994e33b535ab5fc6937fa101a67070158e876538e846243016e 17 cd1ff7a0b99aa597f3fbd09ec6bb8fc5a2e7aa0bbe80e37b243d4bd31827f239 1365813ea00faca92b392f94d003c84a8e1adfef165ed94cbbe3a25ae5c7074b e05bfb18f1142b18d478fee3f64882800d0dac92081aa1c96cabc28f7f2abf6f 26015a34d7c459f6549673e23e2f66743fab72b81f2bf34e0c0610ae5cd5e649 ebf804dda844a1c2c43b552b16e56fe41711e378925edf33a173fdcde7e3d1f3 e5427bc500f7567d039eb09b1486bf8e6ea14dcfaf68290bf85fc589c3b515aa 29d2630416e8b836f83380c860e383e2498d5f2fc3c3d9e8d5f69e07d18af76b e62d99191501370e7d54f4b08ce7ef1f4d9ea19fa0e102fac2500af126d87145 4319a9305e1edbb90c30446134ce637b47ac68cacd921a96486afbb5ed70baee 045214c8d3b4c0162c5149fe029935f856dd89f1dba77c8618247ee945a8addb b8b273fac86f4ff05dca36726e7ef70804a2c4671186c25912561fe358362cf1 ba11fcb0f480874d8f4c39639423a5354ed543858e2fabc7f420365a4be99f1b cd898de668a1bd005c9aac9173c1fe5dd9b107587fa9c97b1cf728300bf63371 3c659dd10ec5aba9963fda964507c9f288141465262e7e91b621d94ee685a4e0 48d4273b9a08d5ff9e755b4dc5a38225bda570ab7e41fb2315fcfdfc1ac791ea 39e5ce532fda90ecf644ef912601249d250c763defd7c71babd06fcf56ca1d5d 2039a513bfafaed4812e2af0bf8a1b68922127303ff8af8d3c2694c425cb9f31 8e3cbd875f6fe52d73259e0cc5baf1c903d3f32559c350b3c80eb5b4375ec30c34fa6763d86c903fb3e518f642f74e1cf3077f833c08d4d8cdbd5d58c141d304134ff53dcdbd95f58b53cd1ecd52a09553bba21f636112b7ff9e5928b449320727a73fad3845ca371f956d18f0c723677a2a621dbb89a1bc2a2d7c9b1f01dc09e42fcc5eecfbbbab967fae7091eeeeead50280a52f2d6798092da6ad84c7eb07fe46b790098d048f5a692bdc2d9fe599ffa6e0143119f1c6113bedeb409e7c0a64e0ba4db71fa285c2500c46dd5b3c8cef75dfc274693fce628076f2f086200141c209704bff1d525cd3fe9ec16069127ecc2a55484e39c711208a45f8f07c0f8030ed0f7b9c04ed72b049b2cd213c39b2ca4cce6f12e256a3882f6b0cd6ac0fc34a92e5ef64388517e40bcb57bfaebc7d25909b9353bd32a65c563977476d0e02dd27596ca2d92394c3c2ad4a614ccc1771f6e9a68bcd166cd9351785a15a028db4d1fde70da65fb0ed55477f09598948cfa0c4d39d1ab177b48f8f7693cc0062a25bc3cfa8231e51f299fa48c62c49603234dd330f46c3d0666f7b023c7305cefa0851579c457ebe12728f777f407635fe0d66eee16c2b97ed2becd5a53d040c2ef2dcf4cdfdfe2a52d82e01d41e54e6930d61e4ab47a360193e32b708bd083465c018c4bb6f1c12a091880b9ea09df9da107a4e09316877fd26df88b97803ed1287c990037468b584244f601cbadbcc0b2faf82856ef6f735acca8af37e08e843bc887a928ead7006c206584a66cf11ac5cf2f3278352852b5b1293c33007bbd0e438f36147069ed169506a7e65a5f761b88800b37c04cda9f327a322860c6e0c8d77ed8d309b33e9d7e6ccfe29f9b49ed5774d552e893f81b663e11bbf353372d7597162c725b4284c0c5ce8b9c405640e331f658783b640effaa55537083f34579a0a9ea7ec2931a55a944f5ab6769a7d81bf95398a50b2d56b57eb8b08580277861f82d2fa33353168eec25031847146934dd33092f2ed6270bda0ac0bb05d1f8891d2e951c40dd800b7978d600437d109d3d3931850a8e0df798ae2061a2aacba44b180caddad8ae291116a585ab1dbf30c9ad1ae33609cb8ae63800c95c808cc1f765664a746fd700b81532d4d1a21cfbad816a4c281759bae7e2a0c6e8dd5c2fe5a7af11ddd234951c5049049d5c59577e49117939ce3773d42a50e1a9fe9302ae694325c80d26fb9c389d872d86e53db46c58434358e7ea3f6750cf309206aacc615b0e625d15151bbed3f2e7b46d9304bead41c9bdc4e9abe8e0c15bed7ea0c9f2a909e0c2eae36d0009f9172adce3bc4cff63b7fe4b5bddd86007cadae4aeb2cfa29b347f4aac7f79f807cca9f850ad644e2dbf7a6a867f3810c3f1864963fde7f250519eb0014f54c781f556325540ebd195e378dfe6aae98826f0703c30396d94b182775fd4671ad38aa4244557fffe10f682ce3cd9832100866087ad5adba1509774f582d64ef4ab03c5dd67e2619d0922bbf85d2fb819f0b false -check_ring_signature 03c335cd40f3f01ee9f5d52837b97a6a2d035a71ceff74db426584c385910433 d374c9a8d3ceac52c743650c4970c6111d112d5fa2deb9bdc7712e80b77a5865 4 cfbfca2d8461e60a2288860695db810695e19588a5a62f98b4cb580e78324286 074ace7f5432b2aaaefc42e55c44a4d38503678c839b354d8bff4eba3790696a 3a5b1933b1d0a638faf4b2a57fce389dab79dfc9223946b75bc2aa9e6b8fb16a ca23be8c75c67b91b29cbc289ccc25bddf19fe69b3fbdd3aeb7c5d65258485e1 523e0e0d6cf6895443910d753ec5e7b6962ec582393aa6334677956fd3e59a08500d6f9c9425c2c9c44b2087baf781a13df016bd1e7bfcd51a91148f3b59f10bbbe242659ece4355a3c71195a5c663a7a3d2ca17ff9375ed9f045d15fd1f7f0093221a6ec5f73fd42cc3aeada210c9ab045874fde07ba56f221819a9a279760c7d08ea8148f0fa6e92715d4dc15bb886c52ad4f42ca5ad13c9e52dfa7113150ed9db3b58e3a286c519f3d401c223dbd28b7a17bb1e9cc7a3ebc5da0497ed9d0647685c758d72c159e0ab8ed4f0728061c62c11644ea78fb141369102d43e66097229081360ced1adec54a3651d7b17fa8afccb4fcde8a2bfa4d6da4cf318fc02 false -check_ring_signature db4a0f96a2359f3ceae83ff0b5ccd8cd9097ce88d500e711da71ac438b397a3a 0a8d9347257599e365c73cc27fea8d206c62f27257a5caf31a01d2f76dc7cbe0 2 1457bb22ab84c4d827c39ebe80e5716257687d06d4606b03eecb7033d678bd51 84810725a97fb349f82669b5fcd55d3873f481295c210c1bee7e73feedc96c72 677b6e0f359dc37f43401ad49cc480d3c7b8276066d830e05e640800ece4e30f57203761e0ea1bf99a0b3b3aeaf378eb3618423d556ad36edab4c129a719030bc62cbfda9749518bb908fb525e11818ef760176cded200c08108efbe2595f0075e8f7d5e0d261411997ed435c046a0edf81fdde9510095f945122e06f4e3b90e false -check_ring_signature 9bf10f9126cbed6daedbf2295e13a753c4e3074bcc08fe29ff1b1e048254e1fc 6a64ca0b38418e1b46f3c3ea34a6600d0ff39db88359cbaa2784bb0c3cac03a7 1 3a0c366c6a5cdab282bccc028766df2c8f5d6363c3936c2f434dd8986fe1370a 50d934983b99cc10bae18ab53d2b3232b9cb9f46adf567853a2ddfb920d279053cfe2d8a260737fccb8b7240e41331a459921dcbfa4a95ae718cf0a7a724e102 true -check_ring_signature 6f35614f772a40a61a2cda4d352549a403f2f0db107a3522918e7aa39e649b00 f373b134b898be7f9c74b127ebdd2ea9dda553cb2b61f8c8511c5529ad26d333 4 b2b3a03c417c4dc514a246c950ec9fe7cf744ae77180a0f4de74a6909cf9bf8d a5c71f3b6b1f509d615ca44943d59bd2d39db6aad67da5f9e9b5ef0a6bcc79b9 3106109ce483792c58f577e85f69f690ab310970cce89bb49e8e677a0da07d4d 69ad71bf7237ba32907d38431d72bf93a12040e56361e2ac3b266efb2dcdfa11 d48c8a32893c3809a39dfc12f52bc36ae79d431ac30467df4bb5d024c2a0f2044cbeae25174092b5027183f336c9a097dc13b0305d867a7daa15a2d0e5925c088361ccc42471db0a0beec5f1e63672d28279ea87c762d6ad854a18187d9bc00496b8caae63eeaa103d0e2f1f21c885ac128f2564bf47d4723ba9d5b7a3aacc04a286615172bd4075a45458b970c49037d665f0097161ef83fcfe2149701dbd050f5ad8cc0721f61e70853345eedf7e51d67b814ba8d41cbbbbf425a0a0570001ed78a832d416d034641431d4c391d3c4ce1b4d6e0dcebbf0169cfab7520fb00d5ea5c255df525b52d7437c8328b62dda47f3178771259ddf1f4e6ab484eee00e false -check_ring_signature 5d60a5000c53ab9194b018b7f90b02c04f6425d4efcc430297e3bf721267c7a7 3a832001fa0e0e587f1de65a0415a708dca22d9eb9cd74f4c4817275dbaf2dbd 1 bfceedbeda871939c014dda66989eb5180f7bd3c0cce3a078d0242e2ac579a65 b82e5fb7644cb97b05494e5c03db1e7d2584b5426cc6ceca76cd0c867a55ac079495d9bbc11562486d4a595e2f86ba234d4229af66f169bf375a896d91131103 true -check_ring_signature 6b53b70801555bb0b8da7b6689030cab4d0b02f1e8ae94bd595b6afe3f83d858 b32f3f6e77da382d2a41aaaa37a27a478b37cf9a6eb0b920d9160ef3b4e13ee0 4 13798cf4142ac1fb7edf0d8840ff9f3b78a66f28d1cbe6e39ff02c320fe12f3b 49b0b60715cc939d875573842b86e39fa5a517b02d49bad650a7860fd046d59b 9eb7f33a19112fd2dbffc5a9b6c7ba27f18a832eebc4feab1d5013ce9fd143c8 7d9e3457cb8eca2040e62254d15d9783f79da56b6c3c959f4bfb6f93acbe5442 5082fc3e1f43df60b37bd54848ffdb202e9a50381f4ad1fc14ab4589b11d140bc90362d4bfd8c69a87eeed249005e42574c3062e8751f936ce0165b0c8897e025376613b4abfae77d8aa8336cc77e3ac0885bd9a8a79fb08a20a766183b23106161ec8b53f42a8d5ba0588e18be29a5914fc853e2187b6edcad2dfdc194dfd0c89917eb98eeb4956a4a8999a6755fb97cc5d009464d47a876dd8779564c3630c1ba19a2738296fec90a812110af1edfd319040a804f0296c709b47aaa498e400c3e1338431efcafd9cfc192509a296cc301244a732d7ec595d622948900a6e099e6f28e3d5ed6cb0d5f5a5979d27a00dfd774702035482850fb42b8d67f3410c false -check_ring_signature b99fef68da0aa182b938d7719a0b02bec0b02fe1a0b7da2c81782d842ed1d5b2 ed93e28f23b17f4bf08fb85747d2d6fd31be103fe0d1b9329ad497d83eef0822 11 f7b1c556c0b04cc83f7d9c820dea9b18bef29b941c970355f50279d6805e4cce c7487356162db734a4af342baf99edf0ddff27d628f49df90a8ce34b9d6a6a73 d587a056b91c5df38f3bfbe1f9c0795c2508ac01b743efd0f268888d6b933c94 4b43a886083d34f849b35e27da0aeb4696d3ed52e97fedbf3f5710c5d70e0792 8e243ac9ed7d0bcdf276b103927f9504ca9ef69ec54ea70f60a0f6b1f759a0e7 8f87c43d67de6f77f02ce3cab0485fc7c8bb6a1b7160e372593c29934345e239 ea31632976168c0a5bf679cdb4b1810c740bf4f8985a49635f1d78cf6df64553 f8577933f10dd5f0457a52984d6ea7cef9a6962f2142425e77c42c9c741f0c3f 2626b9dde8d92b6118f4498339bb7fb611abb93dd0e4a931c620f5725317bd71 663c76d6788f85d26b4c6e42eac2c92d833b5f24a508daea38943f99825a7dfd 2d57e4ecd39bebd20e9d2bdd6a142eed54f19c472943da665f06478935716d8b 363b5e99948f01a42637b9b9aa145fa972dac3913ecc54341ec9b999b8419f0fe16bf1d1a7acfb6cb0a5c66ea6df67c55be7500fa847e19f1239e25491ecbf0763cdbcb8926eae3271a983c20e263a8a7b452801bf13031c8371b94f6e477d9589f8fc10bd0d589c3122288c4ba89f3904fc9ae94c26308d292dc1c36c24b50cef42c521818a7642a335bb21672e867ef7a5d24f79e748226e95d0f8087b4b07ef6f310c3952a32643d9a28664a4d2ada3f03fd858aa753e9476019cd4c4ce007435e609e9218f2866531dcd4f7f15ac049ca3ed4edc0c482529f79d93cfb0021b20a756e33b0ac1de7eeade591894241df91335bf5e514955206b279495c703ee53f0c6c8def78ea4f2fede3eafc24f210fd823936b512f7424256b1bcec60b584811335543f04db89c8ea6cef4c8ee26a377341bd6cb3c8706c1b6235a59080f53edbe1adac641853aab4d91338a18e17acd9f3961de51eafa340f9a18fd091a76aee31e8f5153ee925183722a6a960366ec1d964d8c66d4fe559b21b68802e56e71bb390b3e9ce157eb22364fbbd7a44e128420819d128c86f25c92cb7f05c754cd8d96c86f27627c201c15e3250f3120ff4835dbd54af191cd82be82f80f6fc32d3a6021ca3d05f33a705cc2659d5b5aa50fcc1636243b5466af3b183d06f1e0654d01d0fcc6f7917872fdebb2068af6c1266d4fe3e5ad297efd21687f083dae1a4079db7b648b31efd59656aecd960c60adfe5337aed8d761a52710450901037069f05caff680cc3c4bd674ba163dee57c27b55b663be721d6ab7c4a75a2a74252cba857e44f37e28be23b2631bb8410a7e708540600385877bbe0cfa05188c5242b4b11fa6b9b5bc4dcb02e0fa63a11b3c88dcd5f9eb331481f77199517438b878a5eb94eca1ee18726efda59756e2d9e336fc8704a3213286dc54d80cee22b1e23cb22afbd710259b562d456417941a5abcc5febc9978dbaabadd2a03 false -check_ring_signature 4aec06172eee22edd6cebf13875433afca3184b8f038efac6541a6d9228d1d12 edb7ee3ed2e3b949ec92fdffe0ad1fd7d2a01104e092710c8863aa1bbe53ebef 1 8d0a8e24eeab26b6772db88d67bea3a3d0b624a41e3639c9a0edf1ebeb6b8bdc d470837dc519be3879db286b906d13d22b9068ccd67c64a472778bc0d06d030eff5e1e3429f37f3f1bf1c341c16cece63237e0e7882e74d5f8544dd511721002 false -check_ring_signature 8ed9da44d94cf0c52f8a4ba4cfd16da7115f080a14e3284284b0e58f7b262f09 c6846557c0918d28161c456bea1a748c8ebe809860fa8dc4ae7d277eb575dd38 25 cb4adf5d744930469ced223e7fff5213acaa5f2928b6997d723f84b900094c4b f19347ed5e8cae62ff112df41bb118ed21a0b772cdce31cd5390260b4ec49cc9 3dde300c1ce11643f13a600e0748f133658a9afd9327c4002655e5fc8535a017 d21c9727ab84ec8b12274dd0efda5ef3c33dbc851d499324cad547edbc93dbca c992c25ae570d8b3ba89fbdef5b8d4c6d33f24257f14a9eb8205a04ec3b0c2dd f739e62b4df00409e3275d4da086a22ec6e4bfcedaec89a748013c8c68f721f4 a09d23b98e6382c2def107863ab731ab4f25fd6add8ad072c815c26d330430c1 3affcd4c25ee4e06ec0851ba42e7073c087bf5f5ee93fa38e37d7898db47c402 ab9b4a3913ab4efa8c2670b5c76d913e04fdf68c804add58c54615bb165461ec 235b151e47b211dbbed4e706c03124a59e4c8ec5d5457ce63abb25b33cbd4d9f f2d8d0bdeedadcb436aafc39bceb308307eeba41cd1a4097323aef0b532d378c 6de970da09b4499fd861ebd07023e17ba519045428759cfa6804b1a1e5b3f4be 01aa9a6f8023f2e87e7f88ebb7a0acfea6916797613f7ac39a348b3ab181f7d8 1f58e06c625e3562441e33d8086eabcde7a7d38f71946b3a89ef98dc2ca62bfb fccfa6cbac626142faf939ce5bc9c230c401254451ad4b17abe721adf9cb0d4c dcf8315b93fe2edae6fea6b017f040039491c08318304e5216ffbfa37b69d33b 7ff7849ef67cea2171702c58f4bd3a758e0fd3920d6a9546ddd2588632a1cc60 2d2ff4592be837bf6452c33aedf7339df5fbbecc1884fe95428d9e3489f5d332 3c8a14c06546f75662519f7e5ad565a4c214900f56ec68807ea5da161d129628 97f39ce6e909f35261e00b033d144595332e2416ef2965d7be459a4be3b58ca1 40bcaf1ed57e8479f950c5686ca2fe562fbc896cba4b7798134bb345c061bdaf 5c955d764ad5bc5c68670c7dd11ab98b4f8270b98a80230ca89c55c8a4c5ddfa 183ac859023ef8fe3c290217eff14c11183d8d87561d3ae730160e0dae6317dd 5df3e896aeffc4bd88d2870ec096823c0bcd8b2486d94c1ffdea135883ba51f7 18d4710cf4edc28dd0b362ce859ac12804c21c4412562d51608f61a649b8356e 53a2a2d84f9f55abe750d6cc8f2a62fb95fe9c9aab9e9e2a5c1842d04a792b01b1d0115887320638a02b8edd8c23c919af899ad5d75a89fd6859a9c17b121f01635feaff5d74672ded1744b6c5667c569f89607b8c60e41d400b742a38c0ff00639028158f8b539d9a4572b8fe0011127f4ec0db0d1d989c6c4156ac7c2e9c0e48fe353ce33346db16f07b110b113b91b96e770913845f77361c796ae1ce030bbd94b63f33968c1aab70ba653caf1c66775352c263b4ab7f763c5a82f0c5eb0f977f95e7c451f2f765092aeef4df08f96ac17ce7e7b6d5a8a3e6e24096eb0608793ca0cdbac9997e25ad57b9afd28217a8a20be1b5076ca4c99140cba890000c4cb61baf3546c31d0b78c2bc239d447094ebd85facfc2b70e8d97005902a350ecaeba5288a30e28bdc6391dc26c39237b3e7c4dd0e302344e79051785ee70306042f15fc02dfacf81b5db35b8a9f1d5b83b0819acbea8bf3342c797c65d12f043e62e73138489f21f1ab9f852edfd7501acb1cfb33365c64a686eb2df46efb0f95557b54156b00fb39b8bbf6421edb70d4deea9fffc45470d6a0477c7d066b030234ce2c951c9c986e590fa3806c7ae2f1b9480eaa5aa29aa90b5babcad9fa01fe4f567ecd1cf539774db73f4c14d06bfe378c9e38749abe06b39452b5356d057f563f0cffdffa07cb8ec922c2177a3a2614da92090191b1a1d01038f682350d615193e33060e95f12e9d89aa8fd09588683953bb8b19d3cfd181b848beea306c7c135ebc6c3e9408978a404052e5b6ad5ee0a210940a200e8dfce356e8d90b6a888b293e4596d256c004033d0b6be4d9ebf4847ca855696dc8f699c306da90be85b8136ad0ea1dd7bdffbbf49373fae9e5e874a6598078b15b534c91a49ce04c658970b79b05212b4f80166bb03bf31d8bedd8ff18ed5404e9a479ab6a66d04153e48649101ac3604ab60c61770382c194b147e9600c9ee174431894088780936e8fa8fedeb281acee5a691e0e6cfb3b57bfd14e90649cd13b4ee6599c1a2013bf7cc7f77ec6cf2524f79a028258989fb5c48c93598f05bfee425a8675c2601dd473620db30198fd100d802805e23fe0af12010195a11d4455f212f6404400fdf66ce35ac04cbde38b81b3f449619392ddf5eb6ea02a949996a1396d370fe06dc1cedcac42f11ed2094c214322f8b09d76ac93a6649b5ed1fc1331bf3e29bbdc35a1ad5820d0394e1026a137cc7a88930be89946cfbabb3764a16a6a9de4b0d4a3c87310410529f9394f38265e98e87831dcca86018446307776b1ec6f96900624353bcc70ded285ec094197c2ca32df641a21efc0c3fe4b9b6d65f3df2e90dd40a63351b12b131ad40ce8689c5d650aa9ab8925a143b3d1d9c62d7e54772057d1ffdb1bfa84a93287225a8fdc2add4a2dc2689563fc7a91c7ed11a8fec500743db4bce17be434648c9ebd64f1178d159b50cc9ba51fae33b3197052568ea0437a540c3ff226cae57ced08e5ad440ebd74309a329795049223d9002bbd0bc015799400fcd0494f6c48e26e3d5333e0cf36749d4dad0e412cb078594ab2ad709c02f7b85a153dfcffd01ee5aa356cf56559883075b1e3ffabb02452380d2e0006a3ca1f45b46f4df2fd972c3355f8cb763d433d881a8aafc2b6905ddbb07c5025eadab10d51f3248e5247c2e128ae971ac8dc820fb8b99d4aa5816926cd97a0a1afac2c40080b86e6f840d5b46f419ff7d3f9a6cfdfda85fe5f06cc45570bc04214801de4a5d18181e0c1d8c5940a566a16b2a18f861c1a221eec9c553273308500bb637efa804f18ae7addb46f0786a3ed8258118de814d0bc54ae62c5fe5016ed285d735be6ca1ffc6e7bf177ab2cbc13d0f8e8038d8dbb0293806cacb300a4e87dc12571432864f6eb2b88da28891a147db6dab9bacdaf9a7d295cf79970fb667b60722c363654e7be6412f87ab49372932ead6e982419c03f43654f9d8068fd59a9829fbee79072e976804c3cc3ccfc1fa78d4b5e01e3b59e7c4f0175702b09f985d44677e9ff2ec3bced4e544249492db32722997053e738474393789095a2ed52e02e888e67502d7e1a5c1e2ce0fcf59feb12dc7bbed044c9109e14a0d59f902f873f468ce42cb26a4c19345da41011d037e692ea538aebc43f601270ad4d5ad2674361163c9440243352bfcf44c1b9e20209cede17a418a3e4c2d850e11bcdfa2ddbf6402d7b45241d44b8f951b35b8e76ff5d9db90f228789c631806 false -check_ring_signature d52f8d8772eaa8b4daec5506511bbbdb35bc49cd263a54a05d6c98d1d5ece913 20b1b3e3380a9adf5208655595cbb4c632412d48e79bde5ff982a3fd8682e522 1 c3b5cfbebfd83e85136ecd21690bc57e19cc93ff6383c3a35318b7d20c15323d bee5fe6e8b92918d6fcd86c526a5e9651c403c0a89bc07919c897d2e5b59780da548d2103e33f89ccf0a3c100eafb2e788e1c2d69fe00d5b84ebd67a28b4f50a true -check_ring_signature 9c5f45dca925ad73c1c2fcdc69f96d4ebfa4139d744b4760a5b652cef036a5b8 6bcbb97a072956dc87ccd860f2d5130b00160976532c640e8ec3ff2a8e4c7c73 7 cb0a62143a2c9fdcef1c1c0d4ab4be7b642f7b7395abdf2f77f0211df4995e5d 01fd170d9005c990dfffb230a2c4b41d5fcb0f5500487790ede9f02e45149ecc 53d26b5206ed8d61d12036e7eae508d6fd28fc7bd14c2395767b9b6d58b564ae 709b82fbe2c13d281b817f34855547c0a2097225dd2ad41e5a26aaf5fe0d78e0 1d79c221451514debbe0952ad1bdc57a6629c962575725bd885dbf887aaaaa3f c6e50ceb7df4193d57e5f9362f57510a8617e7cfa8e89c19c4487828e0828af7 afeb621e79c2846f5a33cb466040a40b7466d477320fe70d99d6bf934b117381 c13685da54da6b88a4302950154a4a048ee5c7a3f4fc0b2c7b5dbdf16e8a6904767bff0f8f0ddf90a3fc9d82af7f49785e20a49b51f7eae35ed2b73d94820c0a7014219da8bfbd4993d52f2135c53860e18974d35e4a4bd98e0cdfffd431a9053baf3670fd2d57ce81b260c937d6f6eef9e6e9413fab257b0fb87ed95fafe70550e1c01f2cb52023553a7fca8a52b3c7398911184bcd37750a7ce7ff22e2720c34c5da76b32ba7d811e9991873f16dbf3393020b7195af306bb0067080971a01b707b06a3cdd1b89c5f2ad0e6913e7fe28fdb144b1229bd69527a64bee72e4099cc9e66872c326f1b3714f377f5a971d7896c982d868c48a227cae89095bb70c28a43ad7bb442160aefa361b2b291ed5ad4e0367b1818faa28c6e02b744d136e6f99613027fc37df66783e23eefef1ff9430cc16f25c918257754cb8447f5e0195f3af4943ec08d22036b495a5db7e44210f9661071f59afb0a633c9d18be96ebb79306b144fb930b2ed58e1c6980f27bbcad10a571dc128270dc8158b61cd0c5f98a6d6a266782a59ba945c5a159294f76ec17ec8d7efb672775f902cdf1e06f4d243b0dc1db7ce7204223c5df1fed7020951d879daf43e87195b5545aa8b0b false -check_ring_signature f0818b26c587e033beb3d9142c4f7077626973b19c11eb31e9cd6b7dd2b03ae2 be4fa0cdc45eca12d703ade7c2a8cf9e8000dd7cec54877f54f7baeb28da24f3 8 783a9c452f8160b7a8bdf7aa9f532313b3682d9c14283d3ed50c1b27e56e33ff f87d0ffcc5efae82c56a3387bb78260c7db099bb7cae81f32d85ced9da5bc8c0 7aadff2b5e5a3dc9958a40ee876690353344d6ea8cd953f7dcfe426bd38bfc00 b8a01a73d4e28d6434b5e92be61801f59193c077dcbaefee79d5883738d34d28 c8870df21fd49948f70e1dcce3952f60ce7221935cfb6b79f8f252e28d6ec1ed 25d9988f684455a3d7bbcf3f62637a9365b072b2b17ab99b4ea6d57950cdb07b 0764980a39c4ea9658e8f31d78a332914b7753fa249179843c8c220772e8ac24 7db988bcd217c16f72c509aa8ad9180e5f36961e8c3ee90b3b596ce1a6a099f9 d6ba0d6b129e819450411947203054322c2aae57049aa130771289fe22af9205491efd73be9860026bf231639273f2357499a4fc95ebfa42c9d93fdb9e3b529beba0da16578e49060c7dea0a8fa23d7c42cb25c6ef7c577f29c2a6eb049c850035e45d98186e49ab06016afee60e5a3d9c9ed8c2ed30de4349a24e9d7284a1024342cb3f18caf70f293258ccc3d86072e04c4a91ff83908a4790f5df3b564804b97a7ae0a1371286822bae46a34b6f1b934e3f738cb4e214048961ca4f589d091dfe0dba5422a6540f03f060f133190844c82aecfd365f890d9e9451101be30090f009f8dd4e55acd202ff750ed8258ec4440cf1b5299c07605ebce292754f0d4a6eb984c7df3d08a423657f8296c43e56c24fcd4624a8b2786245d7a682a9016684ec5bf5307e4f0532654ecfc3ae80926fe05cf452c1bf4869e8d96f0ccaf41ce534f2afc9500bfe5e2fe294344ef8cb00d5661891f9509f9f25c7a644830760830ffb33c320f2e5c7ac2f577682b4fe45a7894234d54597ad845e84112a0c1cb931e3717eeaf4ce78cc2ee945a55453561f7511c53c8bf203bb5abe86420a177976738f4ac80eef48aaa284d0caf8478b7430a7b7ebb6fecea9d8dca2070726be50c55d009fe7da125aadee69a0f9aa5829bfdbf01ce34a77fc6a7b13350bad95bdc90911b378991292e2ffdb3fef6001123edc1c4a53c6179f96f2bd8b0b false -check_ring_signature 7c7cc1cec69d0952b0d7bd1047b5b5f07451620cbd7ac42c5948e124af8f848c db9a7e3278128b2907bbd68444e8b56ca3da61a782c63b83547d7f3e693c5f3b 72 c6c8923bd0b67c5ed79b6b658327dfcba579cb6a567d40f21d5bf164a91a1046 19dc45e178aa70b4a5b6ecf5dbfb76671cc09f202235a8625873f37835840763 a61d82a5ea3b45760e5a042455a32341d3b3f05d8087548ea703b881762cad80 d5f64bb5ad26ac91c13c24900ab8dec0065dce6f41501e45a113b4f2f2302207 94587506e42b5396789d9f5c765d7e46009a7ce7d8920d3d3d2fcf3aa979e95d 0f472688511c3d5930396881fbe73ef432f9870ca8c5d4302c6b5bd6722be6d3 aa44c9d7a57472af16d487821afed31270ccad07574e14318b69d0a1fa11bb29 34746a4f8711db1be5710c0a143292d7f224a49c78d523319695ee0d8e697a86 0e8dd73d28eed6aa3879c28388fa7a96574ac5d7aeb05bb360f8e8e836da2428 3950cf6d2588b3c21829e06427ea97b25f8d4fe595e248f5f8f8292812671ad8 261b1fe65bf4302bd915d0dc4cfdb4ea71eeac0b670bf21d920054528673e7fa c34d7a26c2c6350651b665df1734619eb9f8af4577fb61f7f75fc022f75b3fc9 642dad95850edcc50a6e731f54494340b6d340d727af7b4b088eb30662d5f73f f55af920f3b87b91eb0bb5ebfa4f9418a5dfb50d99d85ec3c414ef33bace7382 53e933ed4341a2bd750686537eb716857b5efc957c1d17333c8ac01c65df037d af74afa51541aa02afcee9fe3d5de2aefaa9953a04e2a3772f1b6a78df7a1c9f a5f6b158ac72b38f725b1b094c274574a8070f525e54d641ce915e9e0e26a425 41f6bdec3d4849eafd97739b4ae2706a2044df48e328b3dba7d7252f64693c49 91cc8b2516bdb9adedc30104b03b034611daaa712e15bf0d707b2e483c4619f9 6a1f5155c474e19bf083ed3b619b2037b1401bc91eb76a40a7c85f9db53193e3 b2abc92439b9aa123cf1ddff9d2b85bdadf140f04d775140237ad7d9a74df599 40dd66f9d7eca9b19700874d735736b63d8414bbdeed5a1b1b2ad764be947a7b 8b4f379bc71e1527c0c90871d7d4fb438bd841fa6bfc0794a42a2a8aca25c55a 7a68b2e01a8c1d9617a00ca46442721cb02bcfebab5035f9f3de0a073eaeaea0 8e1957ecf4a93637aded8bde672824608da8b21b54e8793d51b0e558720c78ae 28a633ea017d915b3f9261cdfbde2ec0ef7cc2c9ff457a082b5e2c847fa23bf1 0fdbd40b1d1b26653917a1c4d362e3e28a584a04d54869b66eb8cf0b28052529 f9ec01a2830c675e64e6d0f0136d92800d0f4bab5c0da5f3edc27b4cd9073665 c5461db05236cc985b8918971fa02e4c8380cfa290c5e493caa1bd14bf5a0791 fc2e0b72908437528918e676b6b113fcdaf71625bd82b3da2fe092ec7556ce95 a85c99ed0950c165fdb5c6f4fe614a2622112e3d04fcf47edf35ee24085a79db 5a89ce615e7162f4a946c3f122de49d3a464ef0aa33e352d13e634ab4f5b54c9 6145dd206512c091820ea144af0e5f7fc387ad7e85b06cbd5edc5bb05cc61c92 c3e5ec8e248f72bf964421860d9a61d9e40c1445b7f1bb667a45e0b68aa25d1b e6d79e7c79c352aac08756c73e5fb08e261e05b1eb47c4e8c7c12133ba074b23 3c52e39c2674794d39cfa5c60bc96b3536b8eb1d71a927b84ce67b8221dfea97 4bf556ed08393b0b32322494f522183ce498d7076427be06605b155432f8ecf3 5d293d673bd1b769c0631f8104e3f8255251a8d75fb1fa5f14a21bd3354d6ed8 8b4f2aea800f896f1853e497a593060d572e9467299019f6d3b751ab05ab4a7c f66ccaa3bf183895805bf6a5c61d239a2724ea9e670f717b4ea0a3a4b275b498 eeb023753455ba11424e08a05d8382f27b74fa21cb6cd88a08002ee379dff08b 5a58a130b9f9c6be64996269b89eeccee83acf3620b085621b8cc7ccaf8e4cce b429441a8bc0ff1929e711ecfa5d37323469826385405c72f2ff656c8f215b80 43f622e81691f56dd6d4099ce6bb890ee2bcfe0ef42946694ea57019e81736fd b8ce003b781d04a2dc998e30776d6ae4dfc8a3c6946f22d3eda52dbe41874c60 88212bab9ded363d1939d7c29ed81750a0d069f62d1fa4f509583ea2e4225598 113dc9149ef1e28bcc3a30972d1be033cb6da15bab06e1d07b921a84693a5847 d9e2680bde0ae1c99583771a21f377b6f01b29d9543a2f82a10346cf9928eef2 eb040119cbba9fccb0154d8d5f5ef187b7e6e357fbba61290b85c9135c37c6bc 7614b35bc7f9872593d9e610587da82a4a921dfada297c565a641f3a6f73deab e872bc4934a1f816a4c88714f2e3515d82819d6395e720799e7b0f8ae1269184 533f3adc5a866dfb52901a9c0bb7b38f87b48d3b71ffc8ef407448ee9f99b7b4 4d1fa07d1a791aa9a6309bc810d9873add2ec6718b842badbb30f30c5842acc8 0261478f479afd7ab55bdc29a996f12131dbd9ced4358ce00d784c314ddfe679 992b917d12579099c8aa80b7751d61f5a3f5d90eff11a441a502a32deaacfe74 10cbeb715421ff98ec2180cea68bf95811221a8dfffa5e9a99da16d8d242b5b1 221c3713c7f9326288822ad2bff837b89170632454d67f3eb8b90b700871ca84 abc04212d7d1975a651496a7dbe8509970ca15fde7031744e5cd2bba6eade540 0ae99a74fc051ec18e20b580cdf8d9333dc5080e5df07fbe0ea0d658d75bd6a4 e2a7ef8e626680c9ce2cf6789097570f0546fcf8cb6cd0f8c19e2300691b455a 76318f168052ebe25c061791f7d0ae09a31885dd35bc56c19484ac3c3be29795 1572b656ca6cd0340f2f81df7755ea12b81945c77cd301334e6dee6736185d9b ac072066e889fa47dd884dcf061aa20e11076e1c5a489fef6c9462d4ec8e044c 3689ebccc5d64ff54771d3881c1d708031980beeb865b153c40debf16ce24944 ae65fb67f073dd23ec9263bfc2fa8212371f5a486c8e80eb949d0dd49c6b64ab 1c2c2f224533efc51701cbdd991d56387da658399dcd225210cb88c33ae6a4c1 46f68a06998ac773c7ea23d2de3155c1a77dbead1f49261cd1b18866ca7d8100 304b7436dfadb22f4cdf4a2fc62ebb006c5d84945c3fb5ab7a55e381325a8506 9ca5daf4626e18fb83d3f61b7c191637203e21c602478c3510c3ab3d0f76fca1 7aff21674e2b22bcfb6de4ff3a6052fe7bb6d95666b2a8bae8629dade3d0175b e060fce3f1a5ffe49538a1b09dca4df06517f53f06b5676b9d62cbbc95ea51c6 8f03aacb2be2efe0cecf7524963a0255d57117dc8bbd3cf6fd2f171138c8be32 1f6b626748b815bbdc53ec55c80b06f3f2ea6f4fe5395d6644773821f9aba6052f79d6f36a707ce0f3f283395177a8d1851a4d365e5eb017c10f9872d59b6f0e5ca11da3a12f3ed6a817a0881ac0e522649e6be2bdaa804db8774747b5015800a3b77fd4756f60d14aba028f4810c50b7ffa646a7e54de20c379566f8d95f807411b7ee9ef614ceec10e19f0613c135bbe12b29fb34da7e746964bd1e0dfe9032160e91ca81c44a59ecc1fa2d6e61292106d397f7a9ddea0f70efb7e1b16e603d27d908e9ac7ac915bb35aa56aa88feba122656229636ca1ca91ddd103023b0e628866d90901c95ca7c86994cda03d09c171ce38d58f8696f34f4ed3959b910f1ee6e347600ef37e620311a26627016a784cd4a488c4306a30bf82f99972c707704b9f44f52415b366c779ffcd0454295cf6fbfefb580c1fd26de7bca860130dc6de2598f1afd0dbffea46bae33521a603fa59a7fc5005fe852168a286bf420d3cf9a70166cfb6576c8e0cf08ad9058f0a60596ecc917ad38e56fccd688ea1017a832355b5373bf9db180ed2a6eae2600c8e4cd59d5bafab551eb3e5afeb74037ab329a1b580d90710e954916db18bfd5a368e46f84e6f0f5b3ab8bfebf9b40d838c32954ac89c4069aa68decb0231bee082678e2fcac7a66b90475978293a0d0f62c8b744568fa7b946401e9f93b6cd7e6b58788afa59adb003efb63f68e0092cf7b6037b187d298878cae26004ce02714c84960d4e3a6869d47f0b323839016e8a9673f26641dabfd10d7ec7d2228978ec77b576c6957d0d137615743ab408ff33b0c1c3dc97c0d4b3fa2e69fa64dd4ce83a86f0d2f5f0dda621b5c9dbc70d16703900042a9d40594ee40e9f7f60181ec4acce9cf6c54a702fd6315c77a10094782345daee68959236191706c4ab97e24d7a91cf2b880c4d82ec29e2836e02d7d619e6ce6f59212104205d79b3705b88a501fdce65a4fe234ae42e8183b90fd764d6fafa43c55ce311882b1b542bf33ef48130aecf0c32d3d89064ff4e53029e1d978133644c3622423370c8a054f87502c911a7f999ce26ac97d18f01d605d123318143cf490e7def8f7c9d9b61a71969ae07f2eac3ef181f1a34bfa5020ba773c173044b56ec62ccd023cb41ec9a23019948ac2dc96dd5b20082d9142f02c36a7c34dd2872da800f2aae79396581daf001bdec95dcb5109c9fdf03b84903f7fb3ffdceb61942c7722f808f5b8ef518c7078076b648f1bda08fbad9f1080a5d1427b56c4ffb899c5371c52ee272e7aaeebaf58354d12be4646685d6551b0bb8d3e0cb88dd4b67e7484e73335ef8aee22463dd63751035ad7854a34f671606b84e7d21e50717961bc98d9003fa37ab2d8f9963b8b1d14d83c6bb2a09d5ed0288861e191dd56828cd47d12297edeeb41f05c8829dc6d11154746e91e0b9e601e7c359e0ed46f272561f9f5894a9fed3e2f28f1022ec876e61d631bdbbf5ce066b2c0e7c8bb0d2c826d82dadde506b056ffd3d2912800355b11002ab439c930ef4231497f7b3fd3c90e69dbe20d82ba12d07cb8ae1601dce9de47148d8f8d30ca636061db5944ec03509b1eed356d3820e549e76c2e6efdaa41579120435210fc2a5a5cdfefc832091ea4ad195c01e01f8ff461c13d931d117a079524f44db09adaf89c0e26d897a4db9b2f5b28597446822005c2378c53e3781d1bb3f1d260b1ac34757cfbab8203c869ff1dec3e9668a7f2c5fae1437872efb085385e4820510f26ec64faed6887113ac6d12b6461ba93244efe196f83039aeadcc77777f009b57ba2f41658bc20f9b7e17d92e7e9a205054f97dca9f4a786f94dc88314e00ace8f2d2ddf0400b578c33b631cb8495ca71c80439dcbbda166b824d160007024fb505d15d8d8005e9feb3a2487ac4f732580f1e217720e6f2d316009cfc4e024861a1f85e25e7539fd3dfc37e1d511f4e33e4dd2ea2bfeb68f22663b5ebeb07999ca5cda9bfb7139e2071d8f6bd3b1fe9b8f3162ccaa1aeefecdafba9006a0bdb4f5ac484fe700e6dd665a3e07c233c1343761fdd927d802d58a88bf9b11c0ed8caba1be165844244691d22c2290d517033e1687b3095b056d769c4718516017f572fb7e9fe7e8377e8c288182f7a6e2424dfad84482de5e32a18e407807c007f2d333ccd744c9b2f2d96de913d6da2d5925898af011fca7f40da80a56ab30b57cf50adbb534d70828eb3fa3cf084b1c7362c00553695a2776e15e20e07a700836c956f76153a6eb3ea62c87661c421c77e8cb38508052e6dcd0c4a3893e006508aee0889082f8949a7a6d0d16903b47755796f3bd8ca8b357721f04d54d704e9bd085bf39e179e678870b07341bdf278b8edbcba85de55e3078ee50bff3a0d83da9fb36cb544c2bf8e6d95254dbdbd62a3624799eeb28399aa0e3fd5c30d0f47c1fc71a2438d3405106a5346885de651e2113a2e3ccd36148337ab0d5ba10f91053c9a7f6d16eb3bf16cde70f7b20588f42381aec17051f0e0571b138a8f0cbe99123599badc8aac697b871b7d5a515ad2fab17ee6c7e42e8b99cb66a4f50a8d0b566f669073d6fc630b3f8b24acd4813dec4d3136f3ce8afc1fbdb72895071ef0110ca19a3fbb3b0154412d34a9a3a6170d4038c0f07ffbb23e9f58295a0185f1b4a0de6300994ed2b44337026059fd7cbe796306b0d8556c0abb9ec9ba0cc2ed2dc76d6091187541cd6fd88861e7425ce46e7ac1b373e2ede14c0998720bd1dfb5ba263867c1bc305f149c880905c6fdf1a78df752a946d11129dbb8e0038383690084847f6e6229c7590ed0278a08d488ce835ed93dbd32863944ab940afa9398bd5293fa9819149d2b8023afebf8f9b84d7a9819808c892061b38018097fe2d34986eb17a375a2888b50dddea0c8389acb24b26883d9ada71f47d54009600e4bbbc99b7c1a77ee234d5ce5c5a51b3b113cc4f658920fd0ad4deed31b0835bc29d6c887a362b5729c20b0fc30b1ee98610d95ebec764c613b68725a490b365c93fdda0b3de75f021153242c1d79c65b782beced177a3db958e8810f22094d747c0ad1baf5b8b9b1031e8ccddb115546341ea3a2ed8e337a87c8ab92a4042efbd2efdcbeacb36adbe02dcd553049633606f230a9f8de58db232a86f47c0f42f204c1f4fd7fd75fbc62c1a598d9b4392cd638c8fcae939e4b97b35fe57f00201fb26b147ed6f8064187251901271dda49a9a6cceaf53601950fa5f6f0e10019dc5d44d24f9c0562fb8f8424b03f353a45084a11d65e7a548d782010ab0108dd071e349fe61fcec392a943990067943e35cfda233a154456ab0b1dc7a03d0074af87ca4bf249a47b93e304791ba0b19c72c7b25a8ebf393658f42de15721074cbac5c7937b57b9fb16a0976a2c296c7505951cf7d6bd710dab2a1ae8d8d109a13a538f465c5a7781f14fa510c75c064cdbaf48bedc6ecc659dc0d387c62608f406556a8a6cb47e14a7fa759619d3b68abcb545143ee746453b438aa0c8e300a69d4d0aa1937a93e516168b6d59d100a70bbb99b2643f92ba4b161d617b0903c8678284a3d7fbc58b4f0673f63e0f3ba2bdd2a53bf4db7f801f40de7dc3b90fe8a3f2352052f8889a0f4823671a40a595349e727f4fc8a127c429cded3da50ab57978fc05824e4da6a8dfb60279f76d727c937213c511d4f24bd178787e650e046221d14ff50bc69dcfd211adc48bb659f462ef0e6cf23a396d11a19199ae0b9c3f83edac1deb7315fb4a94bf305c6935edbc493257da253cb10ae5a922bf0de9a2f321ce1de191ac6df2283e9215719fbd5b8d84ca5659d4054f3410e3540685cae0f19408bf2b7f3de3ee33a63bb28375a134e703a7d6589bb545f1d57d0381668e0b69ffa5326ed3adf77cbbf1b4a70023fd54a60f70c23b1e3f6683ec0a163e97f8b2d1019c0b31edcc70f716b356558b81de33d1d5136f484315f7a20efa6ecab10eb535bc9dc9cddb3b9f8f43846dafe372d16ef3ce78b7d6ed3d880c56193099f1f4b3a6c5a91bfd4b2d51f192e5644dcde5a721f50de3941034da0a3a3d7e819359cc56f3a3c88887290ab12315865cf9a9ebee005ff0777e64ff0137d56d4ac8feefcb17e157c4a7b5c16f6cd97f7f4589f6b8bd3349a93b0c6000af051f3556b960046f6acdd05843fa9731ae6fbe35c32035de0ed1155a5e5e060cdf2d1238ecda33b5fb2a72f0a43b8d6513301b7808f880a3c715904e2a010523585d0916e38e79f1703712d01b7b3a377aeffd3110ea5e0cadb4ef3b07570f096a2e7b32e825d902f0b3e50b5722175ab6ddb4ce0dab19c3baf6e1616ab800f1d6fa545cce303a0c80950fc33c756deb69c234129440761aae9328b852f7088c47dcc9d1c5d2203f6bd5851f4d8314909d2f4fd6e2fc8e56d2eb3f6090d6076d7de8d8063684f1502546e2929dabc2ba00995a823c07d13932d3be3f2f7b0b60feb660f7fd72e93fdc5a7465494c6814b5af00b3757f35c49e61c030dc4b0b8eddd9502d5c442c20f4cd4824cfcb4e576aa2653e8317428422c39745e1770bfdc283e8c28c748a0ed7dd1300ab2f34e9d7a0bf465a95284ae48e08de902400cee7021bf75b23ddd277ffc8b99af787b21826e9675cecd82e6698f1e78a040667f4d4aa0b4ba5e6a1368dd738cc1be13c727d0a9d9f0ac32babff1ad0f0ba07abfb9509c14cbbfc32296b2bf9f98a0aabc12fa3a3193f48be3843da6bed0c0a9fa28a2ef97292ee35108bf457aaa4f117a2892c253ef4e198dd18dc6b191b0053dbb61dccd6d9ebd965439d3f68c4cf528bebbcf700c1f81b1868046985b705b77d8cc0a4724e99b23ed33d5aacfc0aa4870c893793ac77c56dac1570bf960c25db38a3b56da9796b34f1f87c2d173df83798d75c3c8aedd0bbc8dea2adf50bf086daad474841d25750c1ec89a07d396c64cad6ad3c7abeefad905fbfafa20977a64718b0b5145bc177e534600b78664b802a5f960b6e639c0c0f2b0851de0b055cc422e3489cfcc3b81982259eb460255de4ce61b6337ebd1ec357d136d20ff83a9cbf2813a7f91489084a5b599d2c6673c91d5a5a210b1d89260ec9652906dbe7b69f56e02e434bc64bbb4d2502b3e7a1c2c0075392d432b69e439da63200951e7031688348701cabe4e42e405f90eb17c96cb85ac40fba87ee2bcc344003dee65559206dd21b9b16e25820d5b2e681b70422f828e2c7bf5ecff24e264c086e664cec3401b80e8e6afb32c38cd611047824f1e82b4191610d76a8395a050e490b201cb0a1be2c2a5ecc31b1d0afe15d8ef79692aa3c2c9e6ab806008d740658dfc449d93ff91235c98cc52a47aabe56d9144245b90e8b4285f432b150420391664d23d3c8a19953a3d82ef1274a4942dca23c1e214c360c1b108a20e58a0cf127545fe9466a1cc1115a68ec8134c4c08174f5b76651883a752e80a4fc4504496b22d0c2e70fcb93c5d779b17f199cd14b9380e0c4c8397a309fedcb012a0de3d4d8334a926b10ab2f4cfda630e718b43a1b8178d425640b5df2ceec47b40717a9dae12462b9978ac74ec01063f557ed3fa6f2ac2fe61319b294b07aa17b0cca54ce642c5992b5354a9333b2be5d1c7d23f9061cc2c844fd16b597c6810003f0298b51e12002ee5345c87e4e043e53c75af0094256b5fb640b0f2c3d53830e30ea34e13d16284a7cfe1b9bfece575ed7f279fffb87b8aff6e601a9792c0a095ad150c6dd1d3a2152c5b34d59ff22b5785aa72ee9418bb0b5d67d912c97d70c0ff5d46ef427c73cecfd2ae18cae7600c7a83d139c9fa216db03683fbdc0be08ba7efa1fb3e204f8faf121e008bf02df4718d965a18923b68702ef560a666b03ea11b5fec6762074b5e2798fb389a4aa57c1a771b7bcb97a81376decb03c020cea4d68bb7a38342595fb86fb8b0c657eba5ce44c172aa472fb805ebcd03ca602486063bd91ba12240a8f053d46033dff4e76b87fa48fb289d79bd799b1dc770111ec6ebdd7bb772c8396a3995d154c0e2940be90488133e84f1393053a1791029caaf4aedd6b2b27eb40f8eb265af5b84069039ac4b9478ceb1fa9b9b9534106533e05d6ab6401cf8abd6b65ad007f2aae4a8d9d03f456ee117401534a295e06a62b0cc22c104852c5c0d3ea09baa808c960a74f6c988038ca7c088279a1eb01793264de060b60576e4bcb4742a05f4fe344a15f0b49ca17dceb82a13a00550024703b466e26b3c3229ccd22ebbbe1c13de7d86b9f8b0545d3ad415cdade550db3a6dc9b31f4d96b714966dca2f8974e145d2f7f0eea3ab029ed5c2fd958cd0f36f3ad1bbd1b7c8fb56f4c1c3824f26f6708e73fdfa94bd1becb535630fbbb04b5bb9e00587ce431e7d3db20891e68a15adc445b88982965d035f2e4c9b5170b09ec8e4c3ea9e6aa2f4320d3ca40702c97a72236a6a6d6ad1206d67a5807d70650700574f916e7c25c5d2e204e541dc15cfd74fbaf7fdbeb8af24b9331007b01 false -check_ring_signature f54c74a1b290d237a96d3e1a1b2ef03459700152786588d74e4ee0abf4918f52 094d30b9d3c751020ba36844e893fa9aba008ce28e51b371b73fa64e2905f5e1 126 5769b5fffd6551633318a4fb918d5aab6d281288286186eb80c9ecdfb05b7784 6d853b4ee0249c02645d0aad0e6e90e2493dbf2375c04b7e73a9811c10c00792 0fb98170e0d9cdabc2b62a5e737bc7cb2b235ec88102c55b7c71cf3ec2f28d90 49b86a784130338aa20a624f127512147ce9c767269f84f9f232a5970bf47e15 45ce203041b0124accca4a5eddcea2871105570f2c02ba087be8870a77bdabc8 3e51f9bd7ec2cdce795a130c63a76ae8ff6e3263e4fbce5ee4c9fe0ad80a3161 14910797a28104b8dce365f5e557a737d05310dc9e9f9040953746898e229e4c 517d16ce210ccbc14666a2169063a4c450dccfa8474a8cb505463a92c6ef07ea 88e45917d7c6b8f25941236bded31296045c7f24bb3eb1d2fb3f9b537f45986d e0af619a0967c5c279fb5147dff26e49c0361c1fa3a321dd4429974870850bd9 803d3bcb732bfb3195ff1c40247b877e90554a4c09bd2e759f537875c4033e75 3c64eb0d86185d12ce9db45f0e12696b1bd5aac80880b2c66eb0ec5961596499 e965a6015280b51041744252d9275abdd07dcde83813286a18c871a11069392d 552e5a473c459329377f894207a2eb316ae9f78604eee2b946ea5f64937ca416 3df1e8646a575b02503d8dd028e8a3be36235242552bcd7f69b934203020365c aed65afbb9cb32ae61a0aab0399c5469d5fa2c1f901cd33425052bc3cbf95f84 216ce093e405ae119c49a527205a64438e7a2c8bbb74525be4cf845d7851e705 7947b535d40fe3a0e1405f8ce2571618f8d857d69d79d6d4eb618b992f749ed5 79dc34e3909b966c0f6096914722a3943b8b8f5f6dc753f441f441c339ff5b17 36c0b7b41a808b876d95fb67eefed9368b7450abb577f1576e577391e41f5193 096c59672058d80658740b0ef1d9df3938d902b2306a31585f9b4729f8bb1550 3a751930bd7812666e31460d6221970e6d492a9aedc33e168784a9375e554d87 2e56a99957890b44ea3d845fe0830eec6161f7a8c56e6eee27e79a109455a91c 85b8fc7c0260f3726c8ae0bd90125f4d123d8aca6a3973adbaf68159d8f3e3cb 7f9ff848d788312eab291b8237899a035025149227c48bae984db75124e78bfb 9f966710a62788e90f4665ffd7503f61680c4e3e7979210096105b850b21bfb8 fe7f1b171f76b0054685cab0659968267a3ed7ebfbde16f629a051fac044c113 0193a84984559ca018b6d8bba29b44837282e90c6b4a590a31d9a6b44cba4ca4 9231d70dfb7a63cea0e7ea4efd3066d7cafd04d07f81f012cf3085aebf65f190 97ddeac0192d7283230f98471e81ce248b0d520787f150f4fe09b5bdfc936bdc 824d137f48591eaf3dce28d49706f3dc557c520dac1cf6d09c8f294f123d63e1 3a3516b776c7c1ea22fd6069e95bf485bdb4ced3103c446ed527ed954e669f17 faddffabaa1d5343c594662141d14bb990d75a31cc7be55c8a74c03061851c5a 12dacf412800bfe8bd68e738222d5f895c73f6a5f035f6115a9916368516962b 4f0015dd0dd0127d7ff0abc3390f9ac275cbb351d54a487ab58812e08f8c168f 74183e47218ec6839a82522c4b1916f9dc01697418056158553ca0ed65c172be abb9d06682094027813e093b59400807e266cde47f96a485633a71245c8705dd b72d3a3950f08b0bf29c9d91fee65c8f4264f0fd70a61903fa620e400f76412f c90da1d7ded1c5e6b748f2b27776da5d9d49bdbce32fdec81571de1126637692 0ad4461c5b310f56b7fe9a5eea062462b89c7e8666d09c14145276d7a682bb3f 2cd1982dbbad36c9ca254f52c925873396079622b29f583647799a89932a8e61 c5bc56e732b7637b7b9c36d405405a6271eaa7c9a0e8eba4820e2b87e0bde3bb b8ffa781552f2afa4db5e090ac41107d06ff0c8eeec34d968b2d99dece884e13 64e8e315d7b5b4dcd525dfbb320a1b38688c8bcf188a587d04f8ef18f89f8f84 f7bcbec4acb7e22856efb1ae71bd4819942c72dc43bc87cda1eed3aee9d141e8 cf29942ef2923cd99ce64a6c1c1a046692fbcc59a8c80654ace885a997ee772d e095af7ea8c1171684cd55782b9c9c4978de07a897faf31d0b36bf4f94e0e4b6 0a708bdb344aa4bb956c31a0a4157e3dd6c56506e1f851c2ede1945c2b9fe827 640d3f91fcac0f693d9c4d7c887aa80f5edf7d26e4840d39050373f0f0f11403 00a51c09ed8625bef53cb19e2eaa02a9941d5ffb27a043e25cea959569e5f2ff c45f5f357aa1df0fb4a74c4ee853357b8263f17e0bf06913d213e23e174ac401 c09b32c82666528018a1f0739c59819ef7654fca55ca521c1e7e374b4bb7977e 72c46974712caa69c3dda10e747fb0830f822d7be2457fe8da330bd78f984a57 1187ec9fe284424426fc7795ce1adf8f65a148ed744fc03648a01194abe3a0b4 1592710a21e831965c96d94f20f457ab75c12c8015a7e41df298132821aec935 ae8b29c92ff80416d1c4cac01c3dee5e116e439c33f9b6136cd592cdf26ea2c7 ba5376b3ee253983ca358566c209d0d2eca4d30cb6588d6bd036cd72512ee24e efc22f64986084115f90b37cbcdd3126babb921fb4c3e555b9535307b45c6395 327e04534a4b70a2e51d290c02e5e2fe5ce6b586dfcf811dd7ada4b2634c7c44 43b61af2f0368c174f790916de6e6660790544d324b9dcb4dd89fa455ac9290c 5cb68429ddd7c39f666d792f547a8c89caec4a8050333af36289cc52adf45ef5 7f9280fddbd04b4be1d8e838737e3b1ffd63db49ec72a0b5728d63b0875a25fb 0523c68a6e203eb84ddb0b795877cc35fddea756bb8340fcb62133990ac48409 2db939e93ba67085c26ff3ad67fbdead69395619001182b8eac8d21285f9ff22 695b7cd1124bdd03dd39249c96554a9e5b409dd10cea6a7504e842bebf752e42 eeb81ea3b7f1f2ad83acba8d770eedf000a4fa80987b9ebc5842f862897a5dfc db5c93d8f04141b1543f8adf2e3f3645a6298cb4f1e4450a95fafdfa2cdb2fa8 9a2fed37260f704e5a4981e5e4b430b1d53a2f5c448c847700c8c7528c02c7b3 7c71d944702ed31c8b0534e893e64baf6174b482c896bd704208076ccad033fe 18788212ccc7cce95cc1b6ccc616af9c3623da2920682c6b2cefa8140136716c 5e34b12e624a41451e9aff2847850ef0995f7385e4dabf5f4cea6e2f088f90c0 25381c2db026c0d2e388833619e3012b4196e2dc3d7321931919b047d44af7dd 317725845ecef0b20f100d37469fac5fd301eedbf4070b2a828fcaaeb2791a51 c0521e6193df91764cb3fa659acc34c18c1140fee6d8ed242e3de54d55e80836 de917a054122d75036b935bef5a365d2fa5d4a724dd6ea1d9eeb57b6d8be9d71 961e6459560affad3bdfa35cc24ceefbe475e62f5f837698e3824f223888c332 4ff41120c6a03f59daf612421a66317057c15f9b2599da3261c4f4a42c53edd1 9ee764c35a5eaeb4861672cdd7874ed7ca1b8fa9606ad8676b7aac374cd7a2e0 2ed2a7e0a5aaa9278674796f007ff1c30ac57b17582b97a3394dffc466670725 577e59d003ab5c21d26f4c85e54f0cc64cbecd21a7a48d2df6f95eedbbbd16f4 6b4c69b2a3cabd6d896de2375a6f0e0e952e3cf1f6bd0172f742e64321e5c0de e276b466a8fb44daac7027aacabd065334a6360a43e3eca45a69968efe18ffd4 1bf260d638f2bb19308a8ed4a7e85df7b48eb937451067448d784dc573251618 8afe7b7856057dbc4b201e9eecde616617f1c8207a36985013712f082642ed22 88cc5534ddd7a58bf2770067f788f44771f6552f27824b12d94189a314b5fb98 29f958909ae2d5607b37b6cbce2a7e93477d27b1f51c11327034e2a416ae57d9 355eab89b3e5e525dd2fa48e6b95eea735063e665cb773ad352884fd269017a8 ee7281639e6c372db2b818d11be887c3558c29d3028531be9cc9ea736ceff2a4 de7eaf374e8198bac046e6b3dc3e54601b8047fc29878b57f694d3692346676e 5fec45641c2d2cf36c72b0ab4294f3c16de029062dcf37441d81348dd586a2ba 3eb7cb81c9d72b10c4f6dd070e5070f0d3a364f6367433197b67cd9d4bd3e1e3 72e59b96f3e5598bbe67a6ef5b70860e613f08e70bc29c1b2ca7cdf1fc49b6b2 4ea7a8ea454f0a8614e94baefb9438ef4e770ef020029349e57e48571dbb28a7 c7f24d87ff5f70bed9663a2a069c41c9c5d137d883c50704c6751d8311acb02e 765dece2716c33c4fa80fe2467f2299baa1620e6d0a187517fe0c2a5f46750b7 1899305d0cd7503ceedc309506be46d35b7507994fb50acf7870229c0c435c94 580d5177d0847dd6ec0a6126005d7299e00fc7467616c6090bf1c21aa8755378 326d44a6ced9d9bb2239eacd6169b2556ea0eda0ddf676812f23b91ea6abccb0 fcd9afc8a4c73c140d466043961ca16a958daffb2de01a7d387f7ebc23d32b40 b5bcd74a9fb4b20bea62123313e74b400d17a0776ee15067c88b2c932b89b500 de001fe84c71eafdb501bab097cf33edce62e5acadd512b2cd40581f2af23039 9b29e584fbde607fa3f4d3c5185ef8a070fefe49bdffb49bf29381f28c25fe5c ba03d52039cd2e064b5e30b00a607968583402c7be92f2eead5eeffd647f48bb 9aa9571d3848d8fa2dcc961403493af3efaefaa093ecccad408a320139a1a94c 26f2886a3d69b0f67b77c4df16a72022809afd6e7ccf08d5d0b3f3c99391828b d47098a0589861105edfd3daafe06165dfe7d8ce58699c8e4ba81986d1f3ebbb 691d04cb909e3cf02d120124dab66fe0949935aff2dc7e5182d1cf96e5b52c88 3470c5a6ab542b6ce08a0f776bb7b145bb34623b6a3040c267b780703a79a374 c41a32d9ace811cd8361273fcd7a36bf922b3927a1308c9d65ab03bb9e1b6e78 aa16c090449c5eaabe4ccb131dd1d03d71a6eb774ab1bf7883cc42e317d73575 ca8666a802d30ff59d924693cf7293cd5dabea3a7ad914b54fa6b96c7e9b1a8e f4afcc9321781beb69fa6817bc0268d3914bef1e7ff6ae7cd4b0f8d482d8e5b3 e3e707d1a2fff224720a50508e514f70ba4611177e34bcd663be29df00f0490b bfd5f5414cc9f04507b3605c62346b5fef9eb5975882583148ac824a8d4e38af 34335fd1acfbeb5382489e4bb05a20446470c697ae86f31b4e46cecba7de8b8f 3ef32c37ce1f00baea84db8f231620770f7e2c2ff8ed7fb90b6eaf96fee4f9f2 07cb3db191dcfa1c3680ef45fae82b3ac0d8774392bfd3f426d203385f6d55ff c2ad63c1f25d83fcb272b123644ac7978362bb42e6e0c86efe8ecd0093d0fa98 74582c13639ebb893a80a13a1b423c8461b681b8ac57d1caaa79078ae83c0947 0f266cf0e11a0499d2989e91d07d4abeecfd9e20d699472b4f1d5fe4e5f6a267 d5eb5770a16d07a9b35f51be65d47ab958d982fc6227d1cec7902c2eb42a6479 7cac3c05872d9561333942a11fb1308c22568fa864ac77b05392a8ad228d4b4b 811794ceb2f3bb18a921dae1b90c8c4a8b2d884c5b7c7996ff73f44263bef6d6 9e203781ab070dfcd119e3bde2db056db1e6226bc37388f3f9694011d2297cf8 96fd0d54ee56c7c18efa76476703ae3298b48173406b4713f4441308fc77f183 7e83d1954e7ec37febd4a1340c73e333b075ce100ab8c1e89f7ff32cfc4084c9 6f6bbda26580dce8a006a46871409ba18fcbf1621676986d372bce130eaff0083af8e50395fd5e5d0ecda5064270497b9c3a35a43b4e7341af4695ea1790ba062f09cf7adcc4a08bad742a318106a604f5c6f08730f396f78b12a93d9907150e0ee029d63785a1b2a26d77dc3cfa942f2dabc3b92b2746b1570229c77bb4b80769c862f618e492ba49dee1df7bd251704c00c621378630f66177835cb4491c00e88882c404591b5087887c2f364fc105e371c25eb76d8c1f4e955ec58961bd0c7d8c643e5819ffb8a73eee3fc93d4e20d02adcbd35878b88f9b3d61de41298057bddceb134b35ac40e59cf0996c5001a6877d65bc5376f3a28967fc72626fa03c62fa07075f830e64aa54f3fbbc518f37547c8cb468321b12997cfc93d411b0ad5811d99f2a910101691b2aa2fea1371090e760b79e65155dbd4864c615de20565c27106170690c9e9ec84b6138cf43657e1cab8222ccf2c07fb17eb3959b7097481059928fb4e67184f8e6364fa6a77f9e057e48ade64bde3955610f96ed80457a86096623e84e84b265b883f54dae003c0d18965dfd8b2fc16fc0f3ab6f409d0e14a51fbaf3337e01c07f7fb8bdb175b00ab9f50af18d820e0c73665794100935f544e8cc189a0d9fc464b1570d8ffcda26f06dca159c305234c7bbb20d60c1e5267d8ada6feb01d0402eaa7c95e88828f01e2a36fc26ee086fc655ca42d0379fb6b6f84b09642070b5e0f56c4433b58dc51d10c5ef20faeffe2a8321d2002c69bef76042759a2adab206baabe74c68d7d8f72a485b095ba2937749cfe4f0add3c20f9979fc13e6b65c2d9caef1d6d8df367061d9a8a47949d8654357ab4002cffd11f66e5eab91438770db9495b7e22bac99da8fa5c59bdc22afaff86990bd0874c5bfb1ba7e24b588612806a019a609066b4fa42ec41b508cf01b2588904e950e8d018ca1b15f1ecd7b78cceb5858e7b7e4c820baa48ccb5718c12da860644f888c52181547e7d37f87d225c1f8fc19a973a2423f7cf8c142b2955c0ff00177a4396b09b15b05f9af49a07865ee8164b0b65968ee8063dfaa71cc4a4560dd2cc9f6d9eb7d089f6518c10dd9d5a54e584eb3850d9a31dd180bd25374988021038c1bf54f0d29ee2cab2222e0a34335fec7ede1d87e905b628e58cfaece5046e78e8653df6a76796b4bc50e25201e04d713b3231673486db58644fac233e0e5e9b46164f74bab9c5fdea49aefe0082f4c4e5e9656c0d8d93306bed215a150f4ab906408c46710c5549c43bc38b3a243afebc093394954e23bab13ccdb6680006a53fd0f839b220366a03c58a9105c08c55ee170ea6499f8d5accc92f79725b71870e3d1bdc90339d102a0c47b4ab29be6f0ba82c66be2834eab98140d2e706b0f5f77dd32ee5f56431777183396ff9a989980071a85ffb7e3e9794fdad9a0e97755b9f98702d0ea8e9b623b997086dc584a2ddd2016570469069d175430309f9fbb6a39b5b38e4ed0047d82284e1f41366916ae6460460a30451146334c80fbf22f673eaa3cf75e9b64884eb4c2d705a8a2cad7187ffc1bea2990530899800f4178b60e4b8a01bd530e82808a6fa34db77630220fac6474c1e7d44f726960da4304bad430786bc41860a211d82a6a44062c1e5fb0f7e6d222fdfd4beb5830d325805909a15fd7cfcfbfd23145dc0969a525a403bde1e82f764f543b4cfbc076066263bd4ca210935a81e63dcf1e33d4dc0de31a84724dcddcf791241b2700c08aec107d2cff0458613d5163826884751c66eb1858f41a25dc8a61acd2032090190a71c2551976ce4c2683576d82d3a4a64a54f7be718529733da76a7699f04fb46b746d2bb7b0237bcb742053062b1a9043dfdceccc9e46a3cbe066572020bf80d1138b049892d0288c377a02cf9b1d2598df8dd6ca945b02463a47976130ff9f13ee12a0adec254bd7851e1288871dcd7873de1e80680f6de6b0a792f26097a1dd6e370c5b9902e6842053961618b5eee08eca62a6c28ff1ac09e61de7b079d3922e58303b860340893ed0d274fd4ce60e6c7b1c68af3295c6aac9acdc1005b8a6add8cf960be51cd04529ff0435ef5bf83b78a07da1a6df547602a9ca900beb05fb949c282f3995d707bd407fc3ad6258f419db037b5da735c228f3181040f5642c683d9509b0111cb46f0ab88776ed765a952aa9ca8d5b22f09b13e6b0be3f32b8fcd72b8277ec56d99db7aad95ffe3370fc0028ca977f07d1319e8d604552fdb280e658000c1575678fcde28420aefb1882ff70f1c9cb404051bb31408630510a2efadf95d73d36240bda302fa4f443a65445372b31f5b569e21d2a90032c066af6fc128ab403acf1644bca28b27c0103217ad17420778391ba505c90da3c0095f34185a2490e06efb910e6b40cab14463a4cb412bb8ab26d3ec35110e76c010def2feb26526159a2b7410c934e2aa3e5fcaedbf4fdfe8f1d494fa4b0e3d1447cbe61e47e5326323f7fd059c8b93a45f67f7f5a2382387d3ce665eb4058edd9aa0284e6a96dd7562fe7bb66d766f0e2118c34e6dc4f7be03d302f04801c2902bb99398d18885f446b209d68a3a0cf7fcec317415a9eda35164582c230a9bb67289d6b06bc03c6d1a760a42a357d59a535dd038f938ecfc05d70fa5ef00646242e4feca68732fd45f81f6a72323e55c341a67ae39473c92087fa2d2c40feee750b170575dfba4ab9831f5102971b7bdf4b780a1ca0cd7010ea3b1a8980786a9f6784d97b7aee860afbb4d57989d86bc11c9246359d0e99b3ca13b4cb10a46a2ccb6bc99c81a4f414090d4ffeb807e1a61817328a1c2baf02f372fc25300bf0fe939e8a1135c8b7233e78094355a32b70bb640054843189f89e1a379610b762c541e5009f72f79bc83fc25847a73827ae4f729c0380f5c16f8474d912804d8ea34b89be8f2f9f71bb0ffcd0ec703bf05e3daadcf99e1a71a42161afd770425d073a6d610a60856ba85ce7002c7795c46d7393dd534ad6f8acc99fcbf7108a4363c05a1b1a66eb0c394469cf26a31e067cb2d8369186c6d9949935d220807a2700e74aa2a98dd15a2e6df29fc03c1c34c8bb2ac5569a4ceb682f1167f7107a9d92594255ddadc67f6408d57bdc30e93085072aec4266927e4be7d3be5d2098108e74c5da334bba589339229e5b1f7cb3181eee43cbd8b3ff76117e352cd054f51efe326abd8372be041b1d29410be5792fdc82b84f9a93d2978f553d83e0461dca2c28414d0f1ad6c567228fd4e4381cbfcfcd204a074df4e66848db0bc0f1fac7ce4ef66118456389885d105d048594427dd818441cbac9f9e081bb51901bbd66d94b4230112cf726516db164b774d97fb602ff4a90da0467472b781ab0f2eafabf46a958b25107735be08ddc9ed62ead30c9cfa1195bc1fe0039a46cbed4fc0272c2f48305b178cc0d14de107a2de3b21e76dd25403d9defb0427b4ec013b399b67e3070afb3b5b218d1bcfe8618db8ac93521d9c0610bc9c264165590fbdacd73bf31965991bfbebd647f211143bdb2919ddb7231728e95fd104406409980a263e2133ba8744374c947899a26fec4dde421fe3b604d56ba29d01b1250ac061c66853bca6271a260f3d559958ca3ebffe202d417ec76189e39006da9600d9996462ab6905650560e40cedad1f086fa80d31c95386993f527593b976820028b6108f02baa80121f5cf30ce6e0b4d43a9054eeb11928ebbc7486dffbaba02a9601fe767a8cabe7503571d1108adaf0034816f0cc363bfe0fa9ad0d3dc6802ce219bcc803b321648d8cb173655298c12d48de5cf76140af99a39ea17c91d0b8874c01573f8e2373ea5a8b64330799577a7c0bd16319dce654aca6700007b0dff1e58b302f15c3aa9b6f1f8373c1585aa104ac6694f8204fc0db25f4d12c10ebf4f225165aa7c7a2eb7ffd79e3561de83b49bd0fe48c61c11f4ad1d4b7cd70fb18dcb2dd6bf5e1b4139bf39adb8c201d03e0c35a685700652a39b710edee30abcb19d1930859cb4fde72fcdd07b3e499811dbcd7b173ca6174b990762e0dc07dd1e5d7146fe91aa7888ee43051c779a378dad4f257395e5a67050978c4d30041d555cfd6e1505825b5a079352ee1f01c626b8ff79eb55fa4999352ce00ad20ddb17ecf6979e02437283a7321abd3f992d1eed6d43e30e89a72cf36e18ac7900b18d66a9d07bffc88ddbd916e14ded805445e1b318a14ed5ac7602308a11580b699a9eb48429285fd78ad8b7ec0b09db04d9ec2d003ddc3b1dd91c83f215180bf8b3c66c77c9952c001910843c6ed69840b91cf8b684fbec8710c4f0a6fec104f793dc4ad2054e12c8c09aec060192c1df5296d76d165f37b4db3b2305f3b804fd47e8cd23a126af9e825f4db46bbda68a386cd6aa23fadb28a5da783024f70f17d7af91e872d8b18541a691ea0933985e9fb49b953a779d21b84f0f461562050510392eb54ffd1aab2d4f2b16ad6b38f037079d15e526d48154ab1df41b5b05c3425979b73615fa814654c90848a6cd87ed5e2fdad4c3d9a8d3b925b7969e004bde443cc98cdf21218c3de827c21cce40ebe9521bfdeb662c1d7b0fbf10de060903685ea8ef864d32152667008d07faa05798a5277485f4e0c351494b4b8a0ed0508a8c8a755052b51fe2e93bf648af12c20a80eb95e454c721e6e92216930df676e0fa5fe7da899ff15832b73fda89f4b02f411d20ba2b1211c9e75193f20d65f5faa69617eca3489c3e4b2714f0957b82705ff77dd4aa76dce7147960ff0df214f3e19874f91e39bb60b944ce61240304336216a3548287ce0f7e299a2700d637cec55c3901623060a462ea8414b6b6222edec239f225e97fcf2844e35e09c279169318132194437ee1d61f1f2793d84c005c06e45dbe89ce233dfdf6b90a188bb14f2b740e7cd7487cbc8b881a91b7972382b9435153f14527a5d16dc70e24a687f09d2dc7c2674e2348a6e38716669a03e3b17904bba81efd0f0a35020e18372cf242bcc9aca6fa078f39f3b6b1cc294c96c64e447670a7a429e930e30996fd1271d7acfea614bb2ac76c0b9de4e75a4f2d84fe3eb9474f45fe1f557a06ab63ac0a79424e799b197a6b1deac472322267333e4a0d679214996c33f079090b840efe59e47dff10479f573499cdb017c26f7a450f18f7b0677d242867fe0110728493a5f654d6c9aad28c8e068213d95ab239edafd9f23c289cd73f583000b27b5df2d858152856ebc9f75191aef295ffc747c3a34a44d404bbd673ef430ae294d9b3a00ef53d3cf98ff3759ecbb806e04365ae10bc3cec771d5d4bfcba0450afaceecd438a69d4b1498b6bd4bea1df83f3ee22b12268f8c176fe5dd3dc01464d62e16d99b2671766aea3bd75737b37edede713aa3e89aacba8186a438500ddbdf40a3bf309adb56e6387bd0fd1a285a194eceb44a85d1e38315691a69c0ba19f26ec8c72e31e283c12f65d3aa51b0fa5423112e291552edd278c0f4c98071b9ede4d8921e8f468dd90ccb619ed295b4baa23ce81e6ea468028f8417bc906a63323225daf8ec2a4d5f302973a5fb3c4987f4b6c51f1ca217144598aeb920cc3dff023257c778d10968f32dbe4eb6c712e237c2d9ebf78b9029d61b80d1200cf415c60a30e4ef6b9f53f36f2e5d740520547ac458a396885525c9698706609149eecde8bf092dbbf6808d14a7f49f565f46d635f55630cd73a4d33a94d1804fc5a7c84f214659f2d81ef05033fea61147fc8fc9eb43acd33ba203534256e0fceb5d691dbdcac7887ecf069c3f04481aae214a007f02e128747ed5e1daa6700c35333d9767a9131a3cd50232a7f4ea50fa39cfcd519e8a6c3988de947b2a209c25f12b6add1446f798560a156f9114148ddd94d926fee5d96c3bbe1bbd2e5094e621aeea56df43fd3a54ea68f0cadc67e630cbcfbaed0abbe3a81ab10cc770e55816aa5ab3d31428ea210ebf32a8d1386844cf715305265549aa6da83c03f046c34bfd7081b881ab5d2a416771fb0f5c3b8d03364c52f4f6ff4e84cd0153e0ad97e4854ea137b4ee7a0bb853f43d746764eb2b3412ffa1c269190fe5dec6e08920d26a79d394b0420c067bee3eeed101628680daaadb615a349a6e5ae09e9068fbb3235c2d682bb4a41c2c02bb16d6d62d907680c3a84c50f3308de971d050f615813d3f17e4ed0dde678426c0c1bdf278345dffb016c3b4df371bf007ef201e74bba5abc00926119e920972815908d76bbc460e80928c8b11ca98bc4a61703e7020a3419c81c8bdd12c030334dc8f0de2bad6df0cfc62e8935716030701f07f66747b5836140b7b592ae1ba39155e413a297ce001f6b5990a5541ac24ade005f01cd9f5171b6cd1d297dd48a8c35150a648c7b391bed3106a59c3c8753750b073a3540a0177aeba6bc827b1cc232ed33f9f14f6a6297b933d3b3080571960f64aef566b6570e9e31b1aad1a1b72aab9771f0b8566b91af763588ec0249d90ec43517f46434ed7e1e88f945dd5c153994b3a76eaf0abfb9c3ff09889222430eaf3da658c8eb06e0215c290846c2e2531c5b8a62a3c127b15fc5f9d6e8ab510c8f65216059cc05bb58b56207f643dfc9707fe904cfc0ab7ffc12a6390607c80613f1c3bf9c5a7bcc51ee87d520b69eab46e2b0eb2a9fb3610b3f530d39f735084cd38c465a68fdbdd69f72ef6c1da987fd826a084d286d8fc2f9a751c3bae9083afe2990c9d2bb3e33ea879318e77ddab0d6384e32c2a24b5dc02604477c6f09181cc5022a729978ccd4d236242360e05309a0b47b1ea2d31f82c4c9baf7910ab543402044edb109b71029dc87e25345010bccd9bd886b351e13af8e05df450ad264d70468b88fb62381d2a9eb261b73b3c2b74547fc23f4f5405a7817a67c0feb0cc25425911a2f855e0121881520358c9599fa9bfd46b294e01d9f892b9008a8545dff4186b6dedbdfec3a9476cade473be638804c2f947cbe6bee1b76680ef00df3aaca62c8dd5ac08fef29cd1997cd06515f5f181a747beb40e43a0f420d77ab1985693989e4c6e934cbabda70514ee079b8455bd54c623a2c4bb14e76031a8c9d2cba6ec2aa61dbe484068b75ffd51a19a5c98900a0740061359c57530c5e849f03ee47c5e9e1060ed1a3d585c436e83f3baa9570fde2e72b818e177e07580bd4411d3c35cd8463f8861732096a92b6e40d934e8da78bc6934bc8e7b304cdf150baeda3469d510edb03e4ecd6196a024923430fa8782b2261ab645d04003bf4fc1e50f4d10e860fdfb53b043e645dd5bc1017cb3880be0aed1e09a15e06de64af7062426c57f28b9ec6a71e92836159f5b8cf0986a896660580ca349d048d4d3f8db06dcfd330c634fbb013157094d99123efa100beb15a51d37d1e890ba43d387247c39a9cb967e775ef2af89734a95697f210983887204875fa0aa102c1a8c51b6be622c63b95e78e867a851d84e1f804bd0d5a4a9a8de6029aa73c09be76c1693c161d62d654f5184f25cf7c45339f90275e740142a95c7892580c0b8ca351006eb0858e8501d433dd42994ac5b82a514f55eba7ce8f184bada8990ea335a0a746ea25aef86c108705578bda6116748b311bcecc3aaf797924231409c1b96e7e453ac2b077d5ef93855712fa89fab0b7145126c07a76bd5fd0bdf302186054a543bd526d586a5f3ef3146f591aebf51839564cda33e79f423bd8f8034c1318552d1d3065ec5e995a349778fc096cb37b853d2333f798671101771a02bab726aba960f2b1df69da39cb9b85e13b6bab734c4525d7eb57fff8857d5c09918e030334ccee476e33b15949a0c8fe53c9a87bf25b6f88cb7704c3bcc95e0f36cb44ed1882b08506ba847130975f4a319274c95f19744e5d7a858254895f0c24d00bc49ed3ad610dd6b4ffab79330f86a9b9fb4e4cd3f20b17304d10753906fcf7bd22bc865d9ca2550c6fc63ae40ac5325e6722693da5b4dc4f45c2d52e0dfecbfb6e8546a1ccc67db125184afeaf806a2cfeda7593a87f7e2873dd9b9d094acd83784f6f239b4c90a718f30bed4c4009808e4c0f5216e9b24dfa5af3c50ec8da876842651f477403404ad086b42ba6a43aca5c87f461e8fab1e83b715f0395677599e07aee9ba07631e0768b537609dc62d2f21262a06e8dfe38eebd750d81e0e9d4c6ef20a1bb970145ba53b2b4df0951efe31caccda939b9c89c8b72092da7db963bdd2f1005be0892527806c6841ef548b1d86482873896ea20a1390ae0a1b2e74435bddbc72ab510f781d5594d5705eff1e343b4f4d0a97d14c7f50e43f26be6ff00fe356815dc8f6bb6c243438388339f66e971cc3c7e7f71800407a2d462eefc8948d9f90a16e4eefb902fe6256b43571c23cf3d41db69c357e80cdbe851b102d6b45b1d93efccb87b06afdef6b15207ff780ba52c32c28660ce027d188caa7222c39c4e207e25b333985f1dce9bde3b2a2b771d045a40de659d02b0dd7e57bed5c60baca6e4562389353bac513f8300f5985982ad84b64ede0501ee255b5a72d7664231318bbb2d64cc85ddc2551601eae2ca223e12a813650502d62fa0a0ba03c4e151505ec422034e348f425d6791f7ca1f7d629c515d087f022049bcc0ee8be25edcd42bc63c6fa02b7e4844d366aafe4b5b0c1f21c371e705a1670d1c3c1b67cbf2ccfea5445d7f03aa5cc34ea8e229b3f378ac59b62c360d2e319658b2e12430003c4b644e016891ed3ab8d028a978d82cd38c7145861d049515ae0f207c405cdee9d1189e3485f19bbb359a226ef19bf4a53f066429d70d121bf70ff57f95e76d524e9c85573519c80288b5aefd7fe2b9548d1618ccb00c38619b602408c2ed999f6e10121b422711bb269d4e5419c540c81d47f5b4b40b263d2e8d52c3296c41d9bab596ade9f78aef01510b3d128f796196b425f53902ff0a561b5193acb00ff1f1490fc9ebfa36bc00aa8bfe63b746d87f1dde5ded0922dae8ee20dd0d2a7008dd7d233fa30e5df367f42a8d18d408505347ae1c370fe2b6e328536c8b6ca2765d10b5ae98e76cb2c1a69fe5f3f2bceacd3af5b3fa0fae5bf831876723cbee4f57f6117efd7e3b6ed5cee9d4d99987e78fa9ff15bd086168f5ef8cf84f5049cb8053f5ac74b397ecd3e8e7d0bcfb424f195c781b350fe8d181247212e9453823f6e7c55874ac2600b203169343d2634458d302d32604b0e1e9f6111778a13de6672041217fe5871f975e3f7c1911d1cc3d786addbf04de739730e72fb1c016a8c92021706b5834c9063b0cfbe339c6b799d94623eb002262dfe1b8c62c230ba159aec69fe58ae423795aa2cddbd6098ff2088953990cd031fc8b13b374825767414a08a752f3f2198bbd5ddab71d770a0dccb098bc081444d1dfbeeedc7cf0093edccd9df6f8aa449a4996578ba1b491bc21cedfbf02461bbcdbfa9baa873d2201f525ff5506dbc736d9314d8411112302e00bd6dc084f75343a0d02b5099c87d9ec59281ccad6db126d3f93267c19c515d0dee35f0c58f1f08b1b9db44ff220d9c742c100de5f8684bd30aecc4ccb1a633d3ab20f097f990e440386a86a35e812d6dbabf4e7866230e5fab466f21da968bfef96ab0fff1572dd81d3257da899092cc147f40e890404ea46ee4085851005ba726df705b9661fbeb0dd39c6d28889745a82896833c6fbfdaed5334bc9185c5712e7810b1ce834888fe8ddba9957535826291a7db1a85fd1a66257553a1892cf5f1b080253ea0d2d303b5c1ca8942ab30e312698e95c7f4439029e032473874db8555e0a565628a0b244b9b92fd28a9f727f06e3747574084001a493f274709b1c533d0ff93b55ca6de4c7b4de8e4a43945b131d96a7de8b7c7a358197f25658d9253b0e9db39d45665780bce603dfaa148dc2f83db470c7604566bacdc788e42cdb92079314cf5da67087ef9ab62d88f1ad44b5fa3d9411181b6c636b80368f416ed30cc5fc10769868ad5c6592597329fa956d9c5ed3c734023aa7bef582a5fbd3d00e348c4ab62f51560d6b8bf638c3f17109223d0a4d1c38bc5e439964cbb7cf9400fd7b4f765a2a79964bd97106143180076080974ae57bde67d361b6511d5f0c0a0561e79fb6c6fcfc66f5791ff1adeafd86381ce2e3655e62e185513a0eb77102cd4b3decf7405177f237570102e9847c275e3aabbb658dad0ae4f2e68f93960c99acc617a5ab5759e5dffa5e38eacf37b96bfe4a1151d0d8b97327add242cc03721a5ec594fc237bd694026b546b67815648d6694ef6ac54d63333a71d6ef005c83a2e252694b92d471e66e1bc387b45b69f4138fc9c77f53efaaec0a141770f40f28a0d69501bee133a4d84c150f76aba2b2ed80133ea7c1ca03221e94edc029c9fbf527c34e6a758e6cceba013c0d9d0324c6d2e5f3d6fe9eebf22acf78e0c492d86e6d99d34acb4e58d3e237f98f8ccbbe87f225fd29138f63384c3dfe4089b1ad4297a00f24f4b6b8a67a8fc7a6a277f5b38237419d4f68c7019cfbaa000d9e3812968d7f05df8ced33c20b5e0d9b75b5efbbe4935890c9c9006ef4ad702f8b62ace6635a38746f821d97208103c27b238e70fb59a191f6b4d234add590ea7e68328c79fbffda77a0a1dd3a23396e6a737690b1b09ace0447416df8cee0c099962446581cd62ec357c24c1cbae0120d6bf0a881a3186ef4e449e19d54504f4792ccdd80023fe5d2d55e46912eaf65220bf65a6b32c3f52619230db7d72033dc8fa6c559ebfbc8db654b2e3f95f443b61573d3c2d7ee845d01950e52b990998e82df72b3832447b1510286fb1e2e260c37e94e802c9445c6866a7a3f32409a2aafc69b90601bc3bb873c0e8a8cecf66a5090fec6e87a93e17a76491016301b5c83f9ec21fe6ef61c7c41b7c1bb6adc745ad5d3a94d31c273532ad2b367d0e9285d19b2bc3b39ca1a6de49cfd0c028e39dd44f71837f1fee1daa26b5c3b60d2c6e0b007eb3fb44b573ca9f5deb28023225a5c592bf14c70e5cd1c2b9253a0c3a041d2917d11a47bd7bfd7d9df2d00df5c1dad5ce295ba967bb214fd4790d0938cc404a0a3797fe9083cdc1832fb9907bad432fe1fbf3796398814dbda5ad069eda7feb2a91fccb553f3744a364262363e07eb779958c7718fe22d37780a90f4ccded1779fed035a2fef93947d505db275c4d24bd03f3a46776359057ccdb0065539768a16843ff74f17448c941263baedb2db6d520df8e666321388f2c8b02e4e6d41e61fa89ea76be2d6308c1e4bd365104c0a0586d15f1f89179d9918c0336ff2707812e2219920b31554ac325c88529cab039ea2bdf07cb7ef224237305bd44d0796fb6497abaced60a9080618892d585f0e76e31b1f6b30a8b6be4130f false -check_ring_signature 4edc4c2c6231dffdfa88d45a1f4e5c272f8012f34b8f052692a52fad33c452e7 a97680bffac0b7b23c52fd7f432306f0b6ffc7aedd047745da29d6276854e37a 2 b52f614dcea8b216eabac634fc5c5f6696c21bc5d6da9cd67bf7478fa5b816fc 3d8f5ee46492ea34a33b658d132d7008dc57471aca1ac901df88febf6afccd5f 231970a2b566e17e6f70d3cb99436fbee7199385f2a542c41553f89786fbbf004bbe9c9834cb95753cfb7703bf7df10c1a9a14fd6e0851eca630f1f9153d4708336bc26032ebaef3aad1ca1e1a469006e51b0750ff69ed846678e18c001bdb41bfa935372541cb76b9d429c75adbcf1a4eac94547c0b950699bd9deee5c1aa06 false -check_ring_signature 702f8a4fee10582bf097f3adc99e3742af12ddcde9da66560e5b365aecb7fe99 f2056a5d8f94925888346372a50f0c6547bd00e3222a0b39ffa15dea61fdb263 22 dacbefdff2881edca0d0126415bae6c6f4c9940ce6a49f0487705ea7ef3f9f4a d7b720a7fff223b64f7357625dfde92a4aeba14b8e153d141a96406cd71b95a5 c815d746f1400d64caf61c68ef9b7f8519d2738a3b843256c2f7c79e8991a69d aa750f7dd67e1b84d0984b57971ba19455e8665aed6f2b48ec9a0f482825d789 53cc3dd46ca56fc1f2a265293be2c518df76d19c013fe589240a193dc0408689 581e24bb5a1b593b573b3765d49a31921fb535207bd6ebe04e3caaa1739642fe e828a705c3439283ae9efc90b09658afdeb756b5da2bcd91d1c2ccab871d0633 2315823c99c74f8fcc38ed4ea5a4a35d32d12c1ff204ea1a9a30f25b15e19e8c 637496e33cfdeba11fec907014502901880e6429a60499c3264480da94432045 09d67bb83e7b130c75116fb5196c0109c3e2d9f5c0f90c468ef6571638c63340 752c48799b55d570ff2b8816fd971da49f5a76035c5c6e6c5cf196bf9aded4af 39097ce876c9c689221eb2ebad4f0831e25196605a6bf2a27cf76ddadff05c0a a8fd3b8087da9639379bd4af595db91cf6f6e10040b7e2d9bbe746659723d454 3f0ae66b30d3352874933a1913c730967912fd14b7abed34fb69bce60550f974 4f5d5d79b38910392678ce15268f1ea958957b86f1aaa074d063b06e6f44ae43 0095d60fedbbd5861cd33ccbf6c7fc04c84de0d54d628f71ffb33b0b3e4dc975 0c86790f436593f847503246df91b161d16b03323a8bb8df4185558866c4580a 46fa015321171b500efb9cfaab17c8a5b9b485fb7b9c140cee3f928793359426 5ae8b3697f0e4ce3ec7c5df97174c4de70efb13d2c1cafeb991b248463607070 5f49c03305e1c37a5a149fe39c7781c871f2cdae6ec97f02da625bae987c15b9 057cee88cb8b42ea620c81e5e7803a443d505af7ebb69de14eee5bd2ed360c55 bd7de6b145d7bee1dbdb231a854ab99f89ed58f3cc64a62bcc74c5d96fcb84d4 bfacce8b93db66d1194b8579a1e0e64de8447c537d53be6e131e14dd50ec97010bc821932fbcabad94326a9c6175f04be99e773304e4dee34dbe9bef6d1d7a0dd4c01bc7dde05a7460000668cc872a569e76fc917dc0b9a789ef53ed72065b0d485fbf077421bba83953deec4340b391c257666be2bb9b1d7c766c1ff5cdf30b76751d96a71f337ab3535b5f31a07eed6538b10ba924db3c9278c3cd50ba510887df13907fc73abbe599f1a9f319f14b4121a15a41848219df21f38d4b96340f0bfa288fb8dd766b675eeb6ae69f70408b7208c00904070d9a6bf778e18a320efd3c8db5e4dafc792e8f998701d1d5b20b5c395b2f507ae8bb51deac9899cf0aa894cd3ccbe1ca1ed63dbad8e0c7f10da3d2310dd159e06fdd52f85326b3ee0ce3b2aae3e897b0581cef9794eb9227084385150a2a072dfa758d6ff1c49dd30a02abedc54a8b976f84f00ae470d4bdddcc98ab88431644bd795dc9d0d119bd01d9398d21dfaaadc4b87cc49d5fe6c3495505b58a40b4933f140d8e1716d06d0eb69c15631072f2e3ef3075bbd2b38923c03734f7a6086fbf159ba7cf1564920fda2613760809c1258d7ef4cb52482ef410711adcd9917944384e8367782ed907890761542e1da46dba92cb77eb8ca435e569aaba657237c053c1ee403cc60d033f0d11b2fae89d9a0bb0ec1314cc16fe9f4f2bd60d936dfffeba0de33adecc0c62b1959ddcac7c651dfb8194a31aa2ba265cf3aad7b3688c9fa8bdc03a1c7504f131c648893f00af598fc4bd4ee2604909789c96374c1724d1061f933e6cbd0a30cae51e14057b0f524a50591d8b64705718ee6c7cee8144fdd605227666190a5aa624b7c5e5ba5cf9fa57d71cfff4959dcc01df8273a4117cc25d6fdd83fa09ff5931759c2fa871ec158a3f408c451f1054d518b13b583f569eb07f92d71302a14cab9fea064fc2cd780b2882f46f848c9a21c1b3d40a3b2b4464e5cc47ed0cfe3bbc89f06eb233141cbc8440e3b1a229594cf82a268ba45bf52008a4e0c30946dedab16ae63ea1c790bb9dcc8734f119970530673531398940c7e56bcdfd067444bffcacf7d2cfb48f5c0f0e67d8df77f9c21ddaac5f83cd1633263ad00e026d96b1a78c9010a9cd9b461855aff3aef4efb97de925ba074b94f82f9ecf3a0152e10655eaaf1535b97391ff6e4748b0ca4c1256f32dae2762fd35ec7c33ad0dc323a6ca9831c7116ae2922325ba1076b6f1c0c191ab541b3143a6e69e736f07e38bbfbbcececcf268579e9451a5fe30efd36fa142b86483e9a1cea0f64bd80ec2809054efb660032483cc280483988d2daae0d6b4b6cad9ec91bf29e5a4670bdcb975a62562791d0ed27c938ef3b0b605c594917a5ec65a772e9075eb0ae60f75a70e19977ff07a3f7d25e091f4190011c74d3e1167d844080d575fd89db900800b88ff884b0e14e1fb5392e3d6654dbc1c8c901348452a0b0c271a0b1aad0d40dd5bcc61cf1487c78dba47dfe442374cbc0fd19cde5315fd42a3dfefde1a0fc73f5d5ae529a3d822a504a1b546db4093267841fef9986d46326a181417070f604545df537359eac17296625c264d8f89237a07f9a093cac392b31f2052b10165bd98d745a59a43e5a4315c7d669789aa07c6ebb8fefc4966a54ed6c867700216ca0265c46797aca7a67bfb0498613e225d6938e153f3875e7a52813d82fc0a52ffd7b515c2531b59be7cbac6ba16e934c0a7d4af199e481a60bfda4eca930646616a7046657b7d763e778ebc823dfd95e71797b2f2d6b2b03b527b4b80110018fc208b379101d569ddf26a27513b07f80e453c6ef10c050277416b1805e900d6db7e516f053aa0f2aeb13f9300e0e36849297b9f13463124bc659bb6f8a10a67015ab848e15a56121273b12c1a0090ee703b3cd4995e8ec19e17388a8b680a785ae68255716f8356a4867f7ff924a49d5f7fbf8adc59000a8f9c905ba2b90c true -check_ring_signature d863bb299f1ab2b669938cb35c4450ed88a944ea1b963ec80dabc92ffd549839 88ff6d8416e78a6a5548466a3cf9b0eab9e12e99856276a1b740eef4180529aa 2 b46e6f6d46ed024c7ae1380b40491d60a270dbcf167cfdf14a6b3336069b94c1 a3d0ee7e5a24cbe8bc6c59b2d60df052446d131dab724e4850c660bee16f7f36 cc4710b4ee2ffbf0db386216f19716185aebba90359cca4a8c8a0ba54a4286584b894c0e10578c2a045cc196a2dd8fe72905fe01baf6bad60fbdf0b6dc245d0dbb92ec46ce12ccf4f3c1efcd85646942cf49e9ceb99c39fd59269ae4f92a5809859746ba1255cc39e8a2e49b7e5a3416d716e9181cc14cd4a7dde94786255a5b false -check_ring_signature 030b2b6564471a725e0b74243b9eba679b5eed22a6bdb2a9371aeb35be377764 9420229dedb5016306a5834e8ca1f410fb4f75be6419f70f9055c4d1bb667d04 49 04dd6531c275eca5f173e1506fdf13487ab66dda691fe2952790a95eda1dc24a 1e06594f064cdd7bb6f70a71d9c4555f2cd2508b1fe271340b5a6a65af074d2c a221c774c6901346db1acec3eee5dd5c5d64fb7f38ed931f99b01e73fb4239ec f5b7a76296b99ba4011aa2e2da0885e12e11f836272ab1513c2895108f92a962 50fe03d93328819cd7ecafdfcdde85cc9bc5b3561e9e0c2d8745ff866a317356 9fe5dbddfd681579d0484e3b503f9eae8c5adadc3544d58218f72909e1218dae 5df6553184b988c01288075aa3262d78fef4d46e4ef344823f55c9d49ee3e8b2 cc1815edee397e3ffebf5094c8b827af3a7144d1993d8ea94e74c726e2f7fc82 ef02720ef8ee65b8673e5e3b9772daf21712784a4807790a75d8fe4308d73771 4e2431cf396f29df2f3b5875e68888dcc2cce60861cc09c3407a4fce7afe7c5e cbfd211e02cf3c99ebad6bff7d2441df21c0d2af33251b1fffbc4ff8ec1d8cc4 c7c479bb33798b35175c0af51704133f7c6199c466beb901aa24b24536b83532 4894bb9fc879fc134472f5c060a0898dcdef4643b042d5ab5ad9a6e03964ad49 cea59ffbcbea026278cd2d28ee48ae35a031eacf33be287ef250666e262c080f 87ceb730719ddf038e9e7bc33db71ee291e62886183c3434c40eebd52ef62bad c832ab8a99c1ca09cd9c4643758f6dfa043080dfa8e91b9ffe3bc83b44db0ae2 3f771f0c4f6fd7ea1fd7deb748683b56c7229cce9f4e9f8311223e546234d687 d87fbbad52eac1787ecac97e84b15cb80c62b63cfddc0d6467fc437a71901fa8 b38fc40e4657178a72845b595c84b70f9a1ba90b94ab8c907f5ec5fd0e32754f e8a122c570690f710b1b1f47e4a8c4bda9c508f8c6968a17ef7315ec97af0f1e 83edc6be6706ef2980f6fde52643e16ef6827dc6477b69687c32ef43b1bef0cb 039b64c5418aa75be354b8ea9eccc082ce1d1f25dd1c589dfba8f3bb3515e246 e0f576edb14989b9113de26a44fa50d0d878f80bf97d9e3fbf6d373c3e24902d 4621a9b4a8b8b7ec594df8980bf7e61d46583b9ea5c1a116fb082fb091e8bbf7 dea17c68c9ef284293db1322db657b5c8f8130e566b5c722ccfa34529fa742d4 3b4918f8b60e44df3ed99afdf38f4446cd1abf200373c960da7c3a959bb38da8 5d22d1f49e2e46b67dd5a30ac3b60c62a9f3db99fae8df8916fa28f83c06cc63 64025043c0fe3d0c6d8fa278b9fa976cefbbf5e3581a668dfc303ed79e76ba07 de2a6d91071d1df1121ca7256610c5b44e6ec4ad010f922e2573f317c471f09f 6d346ee5e1a5d6f9fecca57d5ad5c70755ea7efc00637af093ff392cb516f80e 6a29b584f1086f5cb955d0ac4e84817c9d3e9a946e457abba10cb9298ec3c6a1 64f0e346f69648d5ecfa40be1a4e4a239ae91a14b00578e58feeaf7fcc6605e0 8b16176f86fc9b515b2b89e90cfc9c80d9d03f27092832a2bcae46bad39ca7e3 b6c083d2bfb4033d18f110401fe44b68a3c20acf6989f2493fcc393dad38187b 3bb9281b72be663ff424c88c83f7729dd6edf6da0626299daa8f476d8f72b30f 20f72d2a59078473ceba85d1d49f7108f2d1e7189e4e660a35eedbf8a4fda690 d23fd090ec48dd013d52e780f0072c2a6c107e37de7efd28ae1e79065643815f b0ca8bb65efeb6e806f53ea7f173399a14bf49bb826859a0132cd2092fa3c164 bab9bdd9c0e244477a3c60c6b2fa254e21802815948ce84bcd2f3743b5466571 cd1de044167a82c80b536b89e7aa4b3ed0cb917b25b1fefcf56050fc9a21ff69 0f5a2b5e121f8bebd1da0b972ed70e6b0b5c715752a71e4380d6895bd0887da8 c61cbc42da256417e88b0c74401e52f0e67bb97f3e8622c136aab1db24276097 10a724e86798ed26d4d0b38f9f04f538a90c586e9247855e59e8925cb6ac7af3 463630e484e0293532f461673e2497d5c2bb80016d02e8b5e1980aef289cb6c3 17ab44865c2c75a4ce72a2f663af0798298748564a5c6c3ba309665f9138764c 066b392fb244d19d45c44754001c272e4039df676094c5d7209f725024e6e991 d6380834846fe02ec511f2f8331eb30761c7bbf48f7fd2f662b9629827d73f95 152f23afbabdd731939e5e3b375a575cbab2dbe782f7b2ef0250a0945c6ec73d 99722fa2cd028f92d01c6a574e1c6170a6fffe2d63a57711a5cf9146007d9bda 171b3c4046465cddb51022eee9a0d9268d79e806d5c6870a4a027da1b9b7420d81779c66155d4b1b4611943eeff665fe1271a697df9df0b45cc821bbc63f750076c37320e730d135fdf98fcc0a93f8c77e3890b98158d0872c9dae00de23c90f3e3008302ff47cee823b13324e30755725956bea9bd9f0b4a142be7e4b8dd20aa846dbbe9894d1a9bc45639232951f8f29192217b58592d7f22a65a2e9888808ec1188f26b4514ec75bd6a3968d2a48f60db82961e4c28e770c3095ed1ffef03e46ded14792b21da161e1b41862200c026a48b4965ed59b3bb9c6d6eda9e3403d601c910d5f8b30bbb4f6a40b6843bafc15a0b043015a772f74d69c353d41f0cfd1773c794692c38d5d73a58512e82aa7b013cbd976da21b02dc815954194308ca596661983b67b3f555d3de4bcc024e2026c35c258bc113b5b453902211d105109da76ad1c59b2ba97f75e27ea62be7b7d220ef6e8b0ab804ecea416936ef07dd5d8757748cb244963e6c60f7a19aa32599013c7b40b50fc8a4dc96e79d7800e390a1962080b58b865af39e5b779a0d3dd53501a44c2b31c97d74960b231401d8212935e0405d4c1e1d971e6229f2825a654cdb26648ca5e1ef5a557459230b30633bd8d2eea62d39cdc0043ed779f9ef0e8381578b91665a0abdc2dd0ec10e930701df2587674bda4154d6a91c3a5515f649724d6b4b3c682b69e0aaea3e0ade1a5753ed053bdb396d23cd93a7d6703a95212083f9ef76d2842616632a650f3c69fea386a81f4adb82338625cec24db84387174e12a9f52999083447571205d4ccbce614dc1c6c79d48486cd2cafdc68a28e1d6ad5497183795aaaaa89d100d913695bcbd882e867642836dcf19b7280fd27dc55985bb35039ac0c0464ce0958f5b3ae6e016e3089f30a4acb54bee6f8022d9f2463c27e0014d13f4526740fa5ced9f659ee78bc6e708aa67f44c7c1e32e4d24fafef214900eef0435806800dca2101c56a972d99c834ce0f6548b58cae2c41cb535ab81b542eb8e25397b0fa9352717902c99dd7f47ad2bdad2e37fb66fd1cb0759debb3b9cf1bd2e68780113401b7962ad4cdaf0610dd269f8e09286fab7bc4e7be15f24786c2e8339e00041bd0ff6f726f1607bc518445248eb2ff40879530aedc9fcbc25422407789f0f7c07ec3beca58724db54e78b52e27eb5a0090cf132c2f86661c6ae26fd27460507d5130b8ba55171993ad3bcea40eb06e53ec31aa461585b60e39d89788c3b0a248625ea5b92ffd61007834f4acd549cdb00fdf93358e1faad8a88c3fc2eea0b83c2ed5200b0ea37b819ca4fcc18e5ac6deab7cd9921d65795ff2c8416e9e20d82fe7aec6c88dbd57f35f8978150a4358d620a9723f93c9f2b698eb4814fb30229a797fab4332bb4034fa9de892fa5f10865946a0aa55ce557db40920cc38d03883e0acafca3dd8d56e799c2d10caca8a0716a4319b70997bb9045354c08700f2087dea18e60d69c0857bd1544a469ed9b4b9585cda8e8e7501f6918560fbe079aca54a68c44864c6f161b69f3fd94782deed7fd9e59513ee0a0eb4518115c05b9c18909174ec91d69b5f802795646674593ed14b2f714cef2ef721e85381a0c3222840311cab79a2775b1fcd3268185a19feb70c8a52b7ff564fe06a50a9d0dabdf231154c4fc66a294d64d256f95d7241394d7359222e50e78b3176b5a4201c417693f8720b7749b88ff612fe66517011a3bd6c1b73defca9b8fa5c977820a7406d00b7c21a634d3840b2e3361da7afd354e2cb67faac05da409eff22ee408027dc97bb6a95ca0c8949bfb9d6adbbac5b278e50dceb10a4c6f6ac05f5e4307b3706e806b8de1a14056c8612816ab57a85c051ebeae2f7e142f4c5fde361c09056a2c19a9cf960208bb1332feaa5a57dcd262b9e62e6b292b1fde9ea4489f0d6d974377aeeeadd4b81b4118407f2bb896ed22307700b88d22c87806a6492e0e5870d265ccaeeaff538ada4edd76153e7cfdd0add2206586c5730d6206536804fb0f80c4075c1742ea1abbd31d7335b1114c47f0feacf10ec314d7eeb76f5701a9d67c15d6cb1c313b4ee4e181d3d129c46a235081aa383bdb22309668b4e10928b55743e842a1a7213ea80d3c1a8e7e09165ed91e9064cf2dc417d9de41800321af74368fcf81f5b813d6a9e8d36594a87390c3cfef18dac1ed5151e3bdac0e20d23833cd5273074efe0b9f8e25e2503ee7e3a0a5fe4dec1f2b6c282b778104174d69f6ecd9877fb14f273056b4f21521af6c0bfcef82ae08250e37f0cd350cbb014a1eedde2366b1e36fefe8af88de11b9593e26787558794d0b93853411075d66dee405f5825559b7601b67c41d0397d811026f6e05095ca2a7ae5764bf0a6a1a66d60499c400b0a787db2cfed0651b2c7caee339f48338e61469cbdb66087c775c2331d4e74c6c8588ee35f29e8a3581a122271fe7cbca8c07ce36d4050b9561b149d78f27e6802329b516a641efb9cbc03a4ce21fc82cd534a177fd230e694674a21daf5d77745c3df9785fc70baad27dad98cab34a3c5db47a6c6fe70406da3dbcb3848ae3cb0c5333c18b6187dd7ec85c5e7ccc95a8b846b408994408dc688d3298b5857577bd57b6c72a8766118c35fa502e2a352081a025b938d806b3d3a083b3b256825266dd26732a7cb72dbfaa1740633a0c65a14b9eac456300c228fd396be2071de60bbe207a87e62645516866b11e9cea171c5ff642574507def7765f255ed666322727b1b68d0fe72a7f16db370cfdcbc70d1287b86493077e6c245bd1b44e89c2023118361f96697b084188c9aa813d067107199f38b9072543ead26d55314ed71eb7bc018bdef3a38d5e0174ca08c96ca9a34311176b0081a8ac5d4a56aaaf8fe64d4553b33f15c0c3c9d477c73a49925cf003ab32ec0b33a731336c28c2122df8f8fb0b05500fa088b536f794bc42219bb6c7bd267d074c2ee5fada4deebcf72b7fe8c30a40b474b533c138dae63886cc28d8656f2a0c08d538ca0e47749b6a059ad73e639b8fb67b126ef08ce220ddb810eb88fcd3076640c979b6d62db832ed694509686ee01353cd1641fd953fb85b9a31ce0bc207ca7823ad28871040f419bae64e8102603b64b97a79078cba36b929791d625004ba1b224c223c38b18a94ebcd2c0bae8254dcf62208f74513fe101042a2570307ce2adb70dafe91f2b8c5c1a99a1beb6b7ae716b07e6af6172a3253fe392abb0a07cda88651d77f467a69668ceeda81d8c39225923d9afb652b78ccbdc444700433592c029c34c11ee0687f84fcd40ce65d525919097b66c7e866c582f01fe303d7cde820d30dff81d46397a0ea8d8c593a080b28a3ec3abfc51684e8e4f9ba0192b060f324e24284ddf6879440092d1812c51c76329c76ca114ddfbecbc4f008c2831a521e0cabf3bb0cc30b38185b38710ea3083f599a4575e36b9c15ebdb0833f6b6db41b61103fbd46ca2252ade72f17a86e44f34e8d9283ecf7dcd4b010e99fbbe77b2c7bc99ba8be81f4c7f1d751e3aadec085b855c0b11d61a3202800cda9e0de2a89e2519a1a14af2126a03eecaf9836578fe3d0605ff6488f261690f16a68132ec3f2e068b9742a74cf501d78df97bda8068fb502dfcc2333bd14a0423f6f2bc3eb77374ede411c7883062ebbc80d07337577057b88662f4525771056055092b117924823bddac1a9e554b7348569f3ddcb6f45df6f098668230d40b324953c4709335c0eb3da412c947c18f9e2382d352c3ad933b203fbc7dcf3309cff3ae4dc011e29deb8d6105b16a4727d9b37e482c27abeaca1cbed4a6f49c0ebd5b90b68e0189cdad0636757f4996dd150a9a165382eb0ab0fff9b1ad52d10e67f462f247dd7bfde87adb7bbc4b47a33f46d3d187566086737e00ac533bef0baa9609ee88adf2d242af0daa1082f0e60495c5b6661d5903128bd6beb535950f6aada22202f80a974cbe0fdb1a56b214f2bdf123e2cd1c8b759ea6efd3e1290f447a658e6899481812a0d6c9df8c81090e92eba08eddb84e4360705a4651a9060dee8085457190d6b183e0efd1411bab612a59b1620c7c2a104dd5bbad23580316a434f16b01c478bec38a1fbd113eb23f32b6f1b799a165c9b25fb01b2b8305af5d88c9f562aee45c1c8a606edbb96476f6f4df03beb7d5e7b9442c093c03013b67b54a94aa1e5014d428dd2360bfb8d812dc9b7140b646b4f4919b2a4ad50fd8f8809ed7a240ae0d0fccb134c173022c8e95fc55d3ef2b27b6b021a028b006c2ef39f5f059acbd3feeda15bb3a905cddb2c3460f38854641589e24be07250286f9e2c93667dbb498475dc113b6723e20b9ceec8c599206c9089cadd7e32108cda12ee59c61bc1eb36c6e868682be6041f10e253dcb2390d612d97ceb62bb07 true -check_ring_signature 885394a6f0f87f772da0fbdf57f6195532c267c5ded6ba9e9744d6682f7bf003 e23aa1871c81926d13688bf566048796e255f412872f0b22881cb8025edbd2bb 205 c1b38708978a43ec5710b08de2db059e7d1c64e9420314d74681af7d5669770b 8423f66d61fc4ba7100ee8bd8492a412f007687b36276ec28560fe1984a7ecca 9dd4484221af2d78947bd3144d8b4546a2261adc8c97dbb3a6652bd27dd7fcbf d5fa8b112b71ce82bd90d99827e2e54af14e5cf3b4ee7b13be691fc1704f9210 c2cee640157a2126a81b0065a7100f98cb8bb9e7854891c7e649b9cc5c79660f 0a5b76ee956606015b7abfb1ba4f0ee941f52664145578251857b9c51a327b76 6d8e90fe2b662a39b1dd9d79f33ab2627dbd87912da9cb0b57f98b2b09788003 655985b16414aee22eb87c76b890306c010f47d6481e92fe322c3a459f5673f5 f0b3c6716787ab712f9e59f53be475fa5f2a436a9e3a5713b3bb47ef868c2ba7 fdde49494410a4a654393dc67787095bf7576e877061a417d85be571cb395eeb 47f7507867aa188635bae1ed06059bee8636ef8bbca5d3732b57079c1982e4f1 6c576efdcb493945fd22a58e78b10d2e88a2589265fd8a0934ffd2f22fb7ef52 6db988e4faafe56b529c45bf427cc25743a5b9d46fb7fd79141b610f57524c5c 578473698051505e58cdbfb33d6a819a9577ff366d9d9856258f8a99a9b7866e 59018d3a720a37ade549623e62bd4d9ff7d0a361aa887568f704f38273aac81a 05da33d653b87b1b13d1502b6b3cfc1080b016ad2fac9672828e4ebfdc4969dc 27800758c200cebb06358ec2cc3efdfe591e68788d90400898584438f7a7046c 3d4fc3c3e96abdfc2d48f47186e094eba0fad679605076f88f4c980f69a8ca48 9f35ce315b99fed5804d039ba7bc154332812a752f765e51d52d82ce174b75e9 2eb6dd95df0eae2bc145ed6a27b0a54ecad53982a3709a8d0bd10b57c6873a37 9f7c9a997a3e99865a2e04c029cdfbf1e6329e10df6d465c7a0dc6fca327997b 2174e066600e299c28c39d333d642cb33f37b1f3d3f74ffc5a7cae4fd4dc3cfd 22613d5f011bdf9f484029c05205063d5b5186945d119152086ed0a221e8e0d7 f9f9def2897277cec312a40f0d67262a768a02640998647b0508f4ec445af0ba a3fbd80a6567bcc16ec680d45d04a16f81b5705bfabda152ef0e1864de14d977 310a5df64de71e145d916ca7edd53cf05acb030b29988002e03170ba0e99adc6 06cc610d07abba4a7de6d904d7cdfc6c7bce505b54526ff166eefc8948915fa0 065603fa985763f749717b6e0d6c783780dde109f7097a0818d088dc47148c85 77bc9cd2f3964d9d0b78008a77eb1bfb8be075950f5b90d366456e7666833e7c bbd9f6ae3c5843f6388e04d13404796952f1a0e3f993b36c3829b22af62130dc 088897084aac026f63e031a3f38c571b127b204acd93bd37948f8f645ebd071f 463f78c0093b7e592662cfcd080d2f7efdbc1a59bd85decbc4bf2c7665a3e1f7 d3a7e701679b76684e10221333c18ee4d1d12541ce4db6b3cb4bb5e578ccb0dc a24c9cc6c088916f7f73d21d3a7da6d0161247f73c34b6a40c4e7f29e1e501a2 41ce11b673d15da2b951162916e6b29e42086228d4eff251f97cc8ff92910423 9db5bb0193339b03e41b597083b0df1142f5cf0f705698cbedb1e29c40266fe0 7bfd9f79e20a44e4986b548c54567136f074efcfb06b5622bae94e36627602e7 1732a7c717d5aedba07f10c93fd0a16a7e575dcd859c78155dc5f631118d532a 3ab460cbbbde136bc06ec29b88bf6d36dc439891cee08d3a2970cc75ce8740f7 816b0f3e8216406f8974d99f399d41cbc7a80a2cecb84e14fcad6ce2e0149501 139eaa7beef87f2c2f1be65ccbdda3c00d5b0e48baacc5a20abb862a9e918051 e267b03ce387e5fe6677a20ae2f9574c8ab37f7a7646d718987775f3d3eb9d26 56bfc31a90f2a7688bf8df17d1fec4a19db97236797835af6a204c89b603703b 55bdd2cb8bb93f27c09dc3aebe9e561976e6101d07695401cfd337291cfb4317 60fbe0b3d3c5cf90dbcd211e5d605ac56aa143d3565f93f173609cf3a7c06c14 2df32d12721c75c67dd53c6505d576dae956ba1fead6a8ace62464287f655fc1 e430eca66f393df81f35c9ba1a9db10bd1dd7f547129cf99d0590c535fdeba9c f67d413b5dd435d69206d4349e26333a288366cd62d28180e15b1300d5d3ba65 ffdba4e55219be22d6887fc12d2bf9cdbfe3912c8c58c61c6b7f19e0200b11c8 fdcd4a4d768effd2bc3c9bb314a2d80a9067f7c9b17123beb835f422a28e5535 429dee08eb2ab684b31fea248c155f5494a6e615478c8e3e1f4cd8eb67ded47b 14f77be99745961fc26b5a102839f593733912fb36a47afc9c2605e83ee2905d b46d0925238e286b3773ec60af83bb4ba92f816180e9a360be7a5e1302cc4a04 f26b8431b8f839926fc1d538e975b542d529a6789d8e25cb177d5226a69f3dd9 3837c0454f6e2a5ad86c6dbb12fe4386344328359fb2dc9fe68feb3e496df527 671f188197ae5356cdc91770fea5b951ef33a0d56a4b31259ad587cab052d7e8 56e2727a5b08504a58624b4d7f97aa7249f46bdede78907f95e1c317b15dcc51 931630783d2b420752ccaf951c75d3f3aff9581f94815a3ca59289c13f9b9197 37aada10e9f5cb7d901e9aff9c8ad0dd6fdf17e37b5f02f26869d728534747c4 d9a07702b8ac1db3d8ad8467308fcbf33ae9253e347e74b378bff533b52b85e6 9a50c740bc5d7d1c52af0a35e7015d77fcf1de7cfea2fd05808ed784df095c11 75fde23b3d62bb015bdc99ee591a522cc7a635f55d4110cf018f3a1e3cb2579d 6ac4aae22967b70608f9ddae86265188cfe96d4913b21cb073b24549bf2d55a9 2641af07c7a62ad477e294b2f63c9a626dc988fb6f4857571ef90f95ba5475bc e6b01d28363725d39102b0f183d78f32a51653959b076e36fbe8c16aecb5df82 f041f9bc4df73e30d0674f054b53613141f2d8a0d1329326b376cd1c2e19708d 2472c7ea28dbcd872defbfa2eab191316fe39166352e2ca4e5af04f09f52e0d0 75b1fc4ce8ad69cc6db043825cfb8d2eb57ec2c2033955277dc86ea9590f9f68 2e41702a5719d8a445f3ce3ffe75bef438c0578a07219a63e72620f66b142e8c 997813412f1f6a2ca963c6a904dab748154a2d6061ed80abff35d30ad80331b8 e69103b2cbb852b735e7fa1cf421448a4ff9a3b98d4ab503c68747599e844cd0 0610315e4b4306f8b7e0606bc3ba6a0b60fa03c2f4d19176eac3c3ffac6ef2a9 a98c80bc04e3f13676174096c1004794e9a4ae539db2d29976bffd0cfee7abb0 cf9173d31cdef6d1f68fe89262415f20abdf049c95f58733422d4f0f311cb977 f6dcb55c0c8ec5a625be1451d57fab3fca5b78a2596b5193f2347dfdc346693d 7d0e17aa413fca78dcd44e94e159a24c6a823f6c83d7f099db80b977a7406f02 eca4bae7096db33bcf6c9ce9e3653d595f4e8301e3f0cc29abc0b1a419314467 5c53d341128d5aa8b2dbe99a29f5dfb8f84bac0a97b4558399b36bd586fe133f 6f895ca5e7dce029a7520dfddc8fbfeff944b36e44cfbd0586228b1855e9197f 227d256cae3a3f849361b2bd28211a433d9afd48205750f3d7db13a9697fe375 698cb77217e36aceb9849d8259413668a10a38961323f6ccec6a6ecce9ecace8 22266c458ba0ec63b6333e5c7dd905d7a04faf48914a80a59ffff7e4224b485c bdcb47e9f3bf9cbaa9c6226ffc5169357690406fbfe09ca3ef942264979c8b3d 0762cf86f438e733320a3dd46d7a455a18638ad54e0abe45386cfe4d1436f17e 26632ad5a5431c2e1648e7770cfe98329588348cc1798d9e0352ac53e8626d4f 3c57145b07624782fd4905501a39dc0d0de7bae5083bf32312fa78b87a50eeb9 02759939335304dabea672d3aefa008939ea40269eeac5e489f5bf9f1da1b60c e41370b9b9fe0ab9696c1cc427516c4f0d3d300dff02f92cbc15dcc9d2652b5c 0acff6ea59483690c7921d67d3f08f1b06f9f28215085b320523dd67313671ce a6aeed789ab1cc022d702bb01765a6cb2544bbbb47fefe140939abfc70f6d99d c57fbaae4aea1002e3091f9e2c20f6a000fb4a35cf64edb821266fa10eff3c5c d45ae49e03d0fbe2181ebe4ed2526f26bccdd184c56d9493ee8be344f095c06f 91db6353ca8ce60a88b1361cb8cf8ca90d71678bc5a75732fd7731efc70b81dd 3699c43fea7272d836594eb45cdafb77c486aa75fc2ebb112f3bde19452acc69 ad04147db17bef15cc3939a33ef7cb64c3c9a87ce055ab0e1edc301d6e365292 7cf6f5237820962dab491a507d46aa41ce8428ee26fc959c7d81945f96cb0e0d b61922a383f6befdd486f4302e6ce81457e7dfdf704302aa2469a1707122d477 c7842979df59dbf86dc7d8c02bb00e75217d0c72e24108b5d09396926862140a d9eeece164e8b517be3ee4ea2d5d5f16704ed315b13c70db3d939e73848b11ee 3066b2789936ff937b46991415e97fa9924e8df4073b31afec027910731af8be 27fce5d25fbb36b8fcadc9b7eda789d42679c109bf835c0abe9234b61d1a0091 bbe72208adea6d98521f8d439e7fe7cc7928e0c4bfa2d69eca93a342c7c1c9fd 3e20cf13e06781d23fe01472760dd26d283ba748417c3acf135211c34df17c9f 018a6bef14d80e37e264fb1e1cb7c39bcc0c51b8059dc66a26a36a441863230c e4a5aebcc9c9e4af21327579faa45f4ae601e1ee0917fbd7c9b082460d9a1307 3decb9452455281f105261cccdc3eb413755ed795adb4173569d207e471c5c03 f15f99f38a0af4d6cf787b64a403f7c4041aab9463322719ed57622ce8492f0f 0fe0d166d46667efe2834edd71e730254c7fed65a49705dec913fec76d637465 42ac5240b5284f114e9d30a13a83f5c84aac3624de331f0f792ee9a8163387db 596adea92f40e6aa2ed7c7e56dc5c40e021ebe2fa23e8e28bfa3c8cdc94ec140 a2364752d00b4ce57c1e6a078aeaae3361e52e9e40ce485406b657a1ec49c03d 1d32995da6bf1f9a2df18e17bd2260b6b69fd596cd8a9e16a4868e68f3273093 d9d73376374fc24a096f2aa92d738d074ba44b46853e381ff7c96e063622376c 0adaf76dc8ac5982a088fe9d4af593a3f961ba989a74e54ec626ea7cc5811397 50e9ae79fdf350a9c733bc0e0aae7fff8c45490d67b17ce353362e26f7e8cdb4 dd6775c8f7ec1a0f87099c9bba47545db72ec375996846fcc5ad19fcd68766b2 44de67b83d9c516066523e6768cc10e907fca1ab5ee3e561db0d00785c712dbe 4e40a23af4c67b1c21bff54285c32bef33a7d877555d4dfb83b9be6b5c223e59 68fe7a63f815ff04cac04daf5a0ea46024e47c721a4201669d4fc6873ea55191 258f0e64d0e754cb582387f9c1fe1c7f7c1e75d5fa06db4c1a7ed67424c11ee2 962321559af4f56a7839c4eb9af408cb2332db6a1fe86057f33bbf5bdf5837a2 f4ae7b1b8633646860cab6168d02e313279b898c8e1677a4d0346fdc295c98e0 89717b5e1999d53d36dc353be45136e9209c3ca46a7c07cb84526ba5c52cdba2 7c78a41cdb31087df5b8c5ac893a73c6fc278dbdd7ca849c8ffc538bb809e9b5 bd9b2ee21a29710bdce5fd874c20ff12290fdbe71eb8e74deaf7541db9a58043 ff5eda8928f32ecd3c992b5685dae650c66ec0aae7b272d468d7dfb81248eec6 e450cdc5d534b1f29ce74fd4a4986378ef0f592930b2c4b480905284e292b5de 46dfe811e3670c8c79b0ef2d2b4ae6fff481170a69fcdb9f8012eef180f233a6 2f64c44873d68fe2639144eafbee4dd813bb5193f628f99a16f3a466fd9a8e04 6174bf2f0eb2062c58a6e90a95fd3eb23d4144f76bc9beed31c75dc584e1475c a5b54bd56567942654fc544d113c508f4e4bdb8167a896c72837726f76aafeb1 a136ebd6115c471242b22ba9d7079457109c463cfc651f1e6ec3999e0a282a13 680b43225fadf89e2d4e8ea302651645607a88e7754202f9d7286fce21b2a69d c53cf817307016cd10cfdb3ff1a134f8b9159b941c79194be16d8abe8cd051ef 949cd5e23c083696c72d0c88cd3bd5a1123bc7e2bb71f83f2fa64c556d05e2c6 e2031c971c0b2d8809bccf69893bdc53237af890efa1879ac2fc3de7a5403490 9b105bc2fe21f8aaf5e862b4b4724505ba33f67278e12d7a25df63517d2f8613 be49ef18545ffa40c5faec59ad5348ac6bc4c5ab34391b184e098e0781fa8c59 d51030e8f80c10bbd438ef8b6e6b008d3f85d9795623fbfe6ad0e3eebb945031 d3a786e182375f7cc03c7e2c4c020cafa0fa0b5efbe51ccc17e6e4c8406018f4 aa5f7e6ee560a3df8bd230772135ecb5010751f50db40e6d634fece9dadee428 0d008d04b30810958bad4765ef7850075d2fc8f3b62d7891881b19fe5ce15072 ca145fa1bc31f9f6b6fe95a18b6fd40020b11391e14f47c2ef8b3a834d30105f e7b7f58fcc8c6fb29c5559dfcb6ef07cd6668d24a7c2e6453d201d83471def32 840aada207b0aa8b2f30284ac2e1e599012a82fb1186961c5332809b8435f16c 17e515c87edb38e0dad476facb270a26804f2e1b9c98d9d4bc925fc756670f93 a324c73603604ca64daab387e9dbc852b4db6116b6d0a8d2efa593cb83fbeab2 76765ae2e71b361549dbc54a655d5889bd6cb4ede01f1ff594e7cfb8c7de7242 8e2f4c491bb077eafe2da3945272de0a5a9433b115062cd4c6429123040f9e88 9250476660850697611bf59c7db85fc479565ad9d72241765e7333a80da94a7e 8e2af8bfa5320dae2f1a424810dbb7d02993ca3ece10d09d360d42b56e6f600c e6aeb16d8a69a3d43b1267f14de50fa571edc6c581aa1edac9991317cc54d3a5 b461c0b75c804e4c13a3184f0257d9f0f4a72a4b11d1ef71f448ae0710774f64 bab9c83004c5e492c942bf28f87d0389d473b00a0fb32300ffbc1e3ee8a119ee 468623388bf989e8434a4942576badd33eab31f0a20e2b706a10280568dacd3a 9f3250d27090139eeb0243c57c48d49754132110fd2269b1354897600d90be0b f5aa553060da53b54904755b657f667a920b23d1f12eccccb7cd795bdb82db22 d7c07e090efcfe3962d837e27f142f9bcd9a982f5718d4bc8331bfd24f1d9109 9d49c7f8e6b100d97a9e762b684667c7cedcd155148318bca3b4b36612872b4d 8ff2787aef988246bfd1417254277062f86b40e1210e48c9c22a5b6cdbe89d83 795e1a857f678c10ccd10f94040142874bf10e1207aa89b11458a5cacdb953b7 97ca777e778df1316ad09c601593c6fbc2851d9b06120032b9de5ec1c08b8b43 6bbdabb94c9bfd0f69a6fba0f6becf3187cccd68a6d53dba70bf874561ce6c8d 5812c2765aee384e130cebd229ceb43a7c9c9f53b153941774200b2960997d28 e1e71c90945f5e3f561ef9df85fccd31c99901bd9a5607ed31bfce23208d8135 4983b06bedc968c937b789c4fec597eb3be0be79bc3fdc6d164d1cd5d4af25ea 1f80f11b8cdf2fe77e9a6dedd5fc7ac134f38245fbbd0d357c6b9a2ced011714 e10e7467a7089a73ba66bf0b4bfe21d9808f4b3540b39fec4571cb815275b82b 43848fdf5a969570bfa2ff24c26a5f1cd16545cd7a099b8b13f2fef4de3c5682 3214bb86d2360f3ab64a986d849b3c3e8dd27143c09029a15ef8d748d6f52dc6 123e2e68618475a84240a44059389de50d407517ff22341da8dd2244a26fd37b 8b8bdfb743ec6859c2ba7e1776d600bb6d5e77a507029fbd4e2a67ccd9f51af6 54af94bf86bb8c36d43f171ce2e1a5f24f572a6d1fbafb8ddb331f443f2fefa5 b025b63bd71296e1e8b441ac2de1a1f6f286a49d0c88b20e14b0122882157177 8730211fe8b9a2ba08be2fe4a7ee90fd63366fa3505745aec0106b344b1f341e e5ea07695a2746f0d628cc6241918a5e3f2dc28d315a28d7f2751e2fdfa6962d 76a5bf82e8fe910540e9152bd1e740b5d7cb7da545ccb07da614cad9df66ed31 4e17d2e0f1bd87703d4d54b1e6f2164670600bc94d6347e02e539007227d8536 371472ed08a679342b969d6144f0d1a18e702156ed642734eb8181291b762637 52730d99452c3532063ef45183f0348ba0ab6270eaa4c504e88c3a295c30ce79 ac80b9510b7ab050f9f0e149a3ee7a26fb94f283cae2289f3a80dd0b9bbf6fe9 51bff3c3c35a177d2cfa93edb8fbd51cf1978ed3ff111362544732ea616495b8 0c9c1fb8b3b5a851c35a1a96b1cc50f962ddbaee3761c0a31e8326d5de6b6f45 5b68935b5f93ae0477f49b195b68f452a945840ad57de3d9022c70679eae3bc6 d3c03cd2d471c307cbf0bc2a6fdf4f58289e28ce46a5d147418546915f0f4de4 aad821dd080b7035fbf544d25e952172dc87cf15c3dcb0dff277724ca21fc5da 97bf66fd42019614fc56bd3e05bef66f7d611e3612d0a41ac21c18cd743d8928 42ce3ca9281d2ffa200e6a0392c9de1ef5636612d20f718c3e102c75d5a6e2af 36dfb35b52980bd3d78084bdb6f5bfcc554c8db2db888d01c3eafe684692b238 88aaab52a970cf68e66a1de224bd3803bdf9412f100daf3bfd9faf0337af0939 5e6a545b0a1fc210e2638f4758e333f72f7b5baf39308b2c449e03730d25d93c 68c66d757e44714a9c5c9670fa97637ed363790553f01b91a67dfd1dbb703af4 b63b08ca19a3fdcdcd391bfcc93a2cf754addb463b805123841d4fb77992cfd1 c304570fd39ac0bcbb5ad8631dc6e92b8ebd911249f02d6b994f914ac8a90fa4 5d75f11ceb412f1b08408fb51ea897287416fbe0f7ea086728d3735d0b3cf643 8207286e1435978572a8a371b2295269c0bc951e78cef44c2d0d7b1364074d7e 36442db76b1a7ea41f004f7ba17ad0be9117bde1438e850e0d30b03f1dfd7386 4dfa95d173eb8e5976b8104e168b6dd720f6361e7ebe05f46f0042cf5d7aee52 abaa9dcbdbf3680ff7459d93aa56a15c5b0b6fb3c2cb4b0f08448fac69ff5f51 96a6677f967fb20dc0de159264fe42ba55424a54b660c96fc205fe25ed9c303d e29fd915a4fcdbf9a542bf0c834b4ced98a22178c1d75bc1c101ac29f01979cf 760a8217bd7811ffe6ba6a435f9d9f337e47c537318b77e855eb4a40905ca2e0 c44c00af393cde170188a29a002ccdb96a845d2a0011375d8c81eb8c71d92b6f 5f384b8ad4abb2539bda86a471633d9bc3f05170dc254d7772f4a548e3102685 a3fe15a5109ac321a5a800bd945ea341a3d6e8771da5464e8191f5ed31a369fc a02025410465bbe6d0fce1fe5484942709811fbab068aab75a7330f1d14e9b0b9c4e4983d9987ce7901167bae6eb97610f8504d1331f7aead3f9af11d071dc07785fcfced80d17b7a701615c92b78699ad4e4563480efb5a23812e02c212d70e80da375fb5d85ae06030702ecb436fb61833b27bb1f47489efe4b70cf4337707fd0312057705289dea7b7883e6f7de5f9dc6b8189f4b6ceb6bfa62eb70549d0d42fbad21b060f81e3b88d59899be001b68544eac3f9cfb5966d678c201278c0b7dc86395a8d7c2d219cfbef3f114993228e8581766323306efbb433603db9c000e67c1e0121667343e3d1db84e7ce807d269d67f86a456f2bfb72defa0de42066e1797ccba50247c2dc0b4fe12ea9f6f4d0d82d28c9927eb4829c7e70f067e02d502816672576bb716a4c01df87195fd7c17c8d047ae19783d9b326ac4f2160ffe323c280ae56995122df4c0871ad59eeb431795a201dacfe0cca2806b318900f1bc3e4b61c837b7498ce65f5fd6823cbb1adb85c57c778f15adaa256345f80ea7aa80e17dec65b95ab727b55c02e13030379020e6d36a3a9e068fce9663ce052cc08aa06137636e65a6dff0d9354ff16c038016ec5db92e1e027145b943d9011537460bf0f8fa3ea7a6c68fb84f74d3186d43980f85f1444680f85330e75a0a2cea74d7a98071c3b974c33ebd89d4b5a6465eba0dc0c92c50f5e3edf53ea600951586fbf624b99a2e578ac26b8877bb292e8b328193a75c691a93f2e32d4f015fda8b7d9c9caeff96395057a4864dc4159c254036f5b3fa44855ded71d6c50b3a336dfac9bf9f79c07f72637d060954f186236aaff72aa441f2a710215aa201d35145631ecd76fff6df7e0f0664c07425d7046d65935e2781ed6b1ae617b10645f9062f4abd0487d46e0e991726a253f055ed5d538d1f65ef92b4789b8c450b0285541cd257575053728f47a649a247f83799e53f419764a73fb0cbf3d5100b080a5971708d8484fe4e8e791a800faf75a9feb7c20a02e15a8387aa8255a50a88614adea5578c60f7ca7e6e2b172846ac64bdc0d4e62f2c76307de599f10a089a9283d64aa3884a664b4c2b521eff74544e030015144fa32f8b08872418a70734f838c57452a09263ecb7bf5643993c77f5e32cd3f9686e9c5c79434ebd220b104f87de33b7356ce46e174f17ddea75e4ccfdc59183c1acf1e8e4efa18cc60642ffed42c5d5282032319eec49e4eb4e359096ef64f93bba3c72ac96a458620379ea5bac403c5416422e37f6e28d9f4d702bd2c1c424b85dca205f5fdaad880d91a7231c78c2fa2331ec90272792b4ceb96e289d27300258b94c64dc0ae29f099f048b8d29140426f1fd0ce11193b66cdb1b901ece04473ea52c6eb1ec56aa0803d5edd2a063205b77ea409991e54e11d18e694740df45781a4a3667895bc5021b6c13fffe99b1288bc08fa845c20822c6defd3bc48a3540042f1259e82952094f0092350a416776e6bcf4b7a73a7579e147e4020bd66c2aa12ddd1ff90af60a62c843cbe407007de766ba2e27bb255d3063ea2603d6c0be14fae819a6bf2303d85ed88f3a70892b5f6a099dcb891e167b58e9a90383b0892bac0c2579104c0866e4dbc9bdfda574da9089b6da3b22e7bcd44bfd863728c617f0d2a03fd408011a8b84634005dfd619986f8e2e78ecc6db8b70f751b2353dfb7a2a5a8d32fd0444a34b32adcaa4fa6b29fa1d009355c96574a87acb55de2d5adc0da74189ac00759a9d4cd2a77eaf579adba8dbdf434314e6445cf61c97c260a60abf20202205b6abe9fe2fda039d41898ab69aee32371ef03a18ee564acd2cafe1cd11965d0ff52ca4d88ef2013451e6628177387fc470d45ebb1b380a342681909fcbb93f006e2d17c22aa385442847e6f0018a50d56a61e7c7f57d4ac9b0bcac6323ede30597d0535e1f9059a61cf53987e921eeae0e0c86df0b82418b4bd5c53d291b110d033fbfc5ee3503534b93a0fe7a86dfc27875f6595bae856b34ed0e650df4aa0f2b6e02f1fcd31a6139983ed7e0aff9deed69d6237bb5a2f774fb471ab34ee50ac45ff62d7bcb2a2bbd574c94b19123a984547b9baccec9698a81c5c6ec3d5f02bf76fa0c8963db77b0bfb196b21c5e071b241077e1493414617f822ef2820002293d068315bfb4a92d97b7ea881fcabc2024166d73d4d452a83dfac87a7dab03283252dcba51cf03849011eca59b2e1f40f0d439fe5ce78ee36b23978ef7a80f47264280d72b10aa45ecce44d5f0597f2c99cf5aba677bd422e2506e15e7210b2f70c299ba5122a76c05d0935a8b5bd920a38be370e38511c0e9993dcbf28a0f156d92258c47c78cb26a5156305d5bb47601e6b20a6caf6cef92a299fe0bd7083532a5289b609f39737028308821b030ae42697ab249c679dbb6dcd61cde1f08e81a9cb656ac3c529277b603adca564c26e5bf722dfb6c097abcab16712242096d41c7199d8fac469245bcbeddd93fa2cc75fe918e6018d05bb562fe252bf906c9224d65e7de1ffaaf86848b880e8bbc51345121be6fc95b210c3dc1081d6208e14a8854e50c7a0ff72bc2912cbdf76acd839b0d31a13bb57070d8b8364fbe07e7d0429b5f6a8fcb33e0deb3194d9d455e3ff3e07b509ea7034a70a6fb708f07cf587641bb599dff9416b8088f0313f260d3ad74c718035830dde7baddcc8e09c53b3f08d11aa0f752d67a2b1b7110c114fdf17a25c5a01dc541eee63e2465001d70152627ff73a9f3815fb3882025eeb6021eb122ef7f79bfe99d2aacb50305a1c9bfc20deb9e3ca509e4f68c2840fa2b96ea70702b54ead08ba9a07a7daf08bc914d6656b3ccd294c642f2ca3b0e58238e9872a35b423efbe018fabd97f40c94efced76ff1d85d3691ed2e0d339ee1dc321cc975c61d0b20d3cc7e71c9160863e971bacc32a8dcd32a95829049b4f95c627ae1b7c3e02b3e764edc668e79074f98fcff6f2b1e747d9e45e72af884d072a3077c36e3a98544d195d1126c6d085357ac7b6d0d0c1530406eefb40b66802137a24b4555c1f688e7d6c565cf7a04b64ab93dd0dc1fdb6d989ac59cbb4550910919876b48bc24c5c6deedd53d440011c091fe668c8dc390c0990def71bf845191f1026f73f3cdc80323dd55497e019fddfe966b405e1db606ce7ecf9592ec3c4bfdedd96c32f97a1bc24f88fa9902d0dcd79fd8e1a2ef203d5757b56f24c4c382f926eeddf70ac71bb4dd968b2b054d3ab9046187f7ea148ac58a42c7a25cccd14461e9dd89eeedf8e3400bcea104de6191540abd0e320658fdda200191116fb67c6b1492ffb193364495638d970b0058b86b5c82da4fb90bea5a616c71bd26b34f6de6bf7b81ce5d498126f4a500c609756bae47405b9c6fbd43894f659b40e39a92bf317aa8978edff12402dc0210e4b82bab37d57c2b20e80dc08ca3e7104ef248821109658b9b39e95c344a000f10e306a5dcee0d14f0051351373fc9f3b9318d287d30cfe6dc2a5e08ef260930172b7ed2a9f93874e3b9da6ef679029fa18dbcda74ecd9f988f61a62b25708efb9967f24ff904326ffa69ffd1c3171e2cdc937a2fa3f6cb72d4a349cfae50f66a4e78608975424447156f988037235e3030a7eb0a790f68347621a27f6240e2761e6145f022633556324d8a492e222d55f21dce3b4145e81887e753e0ba20297c6b7a2cc673853836554f8695e54c5fa61740fa0d98216742cff1ae5e2ca0cedcaae2135186cc491367d755e73ad7425d12b731973e095db2b4414dbec5b03456d403d476e0e1ea32f7f783d9fbc012c19dfede284914e503dc1bb2a33200816fb6a20a581156de2e4796820f5b85f11b58b29ee294704f82f8d3eb7e91c0f1c0821a6d43530888e42b76a0b6967072771f5e99d22489ada8ea3b4503cb2036fea7f7c7a0d320f1541596fc499b6dd12cbfe3f5698f89828a6b9066788d60b18fe48dca29ce6985796cc21fce2adff3e0f3e3401ad5772d4bdc5ec34119e0bce677784208b675b6f78d22422fb7cbc1120538cbcd248ba22d7385f7840f60a1776d4c04e59d789f0823dbec8de9e097f62f820bea214576153f05a2c44fe03705e56f7a1ee27aa57cd39842ed3fde742719cdb57c4c96e3920a04d50e5b10de89f775968cfbddc97ffb87ce2662b9c696c9405358d02031becbf7962c07f0b2aa1b070309ef73da026b8c5218d6f924ec87333d65c01076c33ce3264db3b0e3f214d7b1a4e8cdbfb303ad6936b889e67574c20428a92371cc25c836dd46f0c5859e80dc342d2e8e19ea122ecf83a8a9065c99ce8321e91ce1616e8371c7503a57a7f542c0c6d112ab761321d3cecdc2112b84d77e5ae803153882fcd22650b58004f9aebe8de4bf954246430e7b55726d6dbb27ea3680ec4806d4520209c04f2a8d798fd6e55e14dc79ed8678158f53c7eceb6cbc7df30dddab179f9bb720d4b914d59297262e7d8cbdb88fc19c85cacd28755d2ee05e8026896b5b14bbe0a27da8417a25629ec4dce9debfdb6574151c40a26b63679f18a93ce1d05f264060f9fcf2e1d0031db5938a689f9f0c5007b6b32675b7ece2560f9a6b79c638f0933f876d13d74394d6306631fce2b12e21fa34f937a5f7b4b99af041bc2c259023fcc5fa04ca282db5350101812fb0ff7ba830d176b36c3bf1a41bb3a494fb60e7613c196d1ce86e64486ac755b1b7ce4215c35387593cc5bc0abe1618748cc0caa86b025ff9185127630a2c7472f07eaac03781c92279b65c9b05bb1fdf39d0b870225e1ddb7406b55aaa57e879bfa3251358d360511ff510526970a1a8d5906b8e0a479e56257473cf6c329bf2267819d5cea6cf491c4dc0ebe1d775e8312006079016602c8568e62c585f654f3bb5676348addbb69409b2639d003672d1b0c800fbfea93ad76c0c0ac2cf752fe0201cd4a96bfe779e7d110de7d80463bfc0a9f5abd5d82617cfa5603d259afed2500f3dc5e4129b766408c0ac39436a45909a9b3c458565f6bce24ede1d7cf3e03a55916e8a78538e419e42ff3f19b8d030b4e66738cb0bba5ca2917a94cf147cebf5a0e21daafa93c00d3455a3160ac500abac3089eddf5b8512f8f20e203517ed00604e5137f8ace43e1c69b0c69ba970eb1c33f46c27f979930098881aa8abc209494ace5f7168507c6349ca65615c902f9815802bdae540add05bb2cae88b2069d1eaf1c0708d046a700375d18ef870dbb6f68c5e6a50a3be78a888a1ea6c14e723e643b4ec37f1d03c8e30b11d4d3053f002f5376f9da5189a4f4e7e7bd301409e7a4d78c491d171b71560020066507af64f7db45522a8848e944e0d60e8823c3cc49e5eb99836bad5d35d1319f5900bc5a7cce18e084f2059145e281b8d3a2b5044682fb45c405c821cdef15929e0dcab70f36e9c2e26f7f6c027545758ff60e2c4ca6b8bc288afb5007daf637ad071d630385e97e86fd2c0cd98b02590c77457451a48b964678a83f7be4d073010aa478a90ff707c5bc8f1304e963138eff212b488cca99dddcdcb454e64bb62001c28a5cb0313249abc864ab3168fc6a6f91e50307f48ecf18bd3e7a4aa0e9b4032b74d8a817473c74ff2130b42b9a20452ac69eec90dbf90d3f1f1b7ecf913c0c65d60abe275a9982ad5c7a2fbbd4977e7821a9738bd6df8c2ae1e4a1fee4a90e78440cd91b974ec2b3edba362b04f031d161db103f0e839e4b9f5598457421062aa39b34693895c28d2117fbde7c514bbc9b7840c321faa03bd54ab9786c8a03bb2a65ee41dd74fc839799c427ba9c6c9d3ac17b3350b0e8ae24a8d58bc518037439e5324a4dc1a606c2216e753509fd352e0aac58fa9c1ea51705238cdf4308e066416f05da4814f0d3426701fe951735a1c6c5c1befb294115c898f4099900965d0e89b5f3718a62aed57e15633cef18416e49a4db27d6ad49ecfbbb04f20469e537c331e05c302947ca99aa98d64a03c7751088340a492099a673d949a40f3c31950a0ad8d297ab8392136d1ced547f163917e2f6240a0fdf9cc6793b8e031c9b5a646b42e69fc9fd13cf188186756d63c8659ba2e359f2c704f9f3bd850639b144ff1cfe1913b0262342c9784de9e315228d815a2db82a1b9f6985f7d40320bde014c61e571f0371ee086f864d79b8fbb259cd99f6c20e3eaae88d52f30e45d98288a3d7588f7821ae131bd81fe2e36639d8ead813a8c69973fb235f57003ac293b6baae8ec894cff4764552194352fd03ee494394885681b35547dc580d0b4cb79b8d4e1f7f0911a16b1e33ce48cd3b964746d479d858df2330b32bd20e6b92738f86081d5c239221bfce9d81dc7cfe661634b87732d9cb4a9dfe51eb034c573678359dde7f29929042a097c0ca622595af15ad4de8e62c4766385afe0e29293dfe3e30ca13edcac68671c955ec3edb97b32ad405ce8a1d4f60a7d853015185408bb01138ee9c17a65566df01c53d567bfd8f7b3cfa11b83356eae8130ba13937f56763846209f8fddbac3d6fd2a244469328f5d93dceeb0ed67ce7ad0f0a6705a62c15445993e209808d5745be127eb705c5d47c8c4190c91dc78a3a03187dabc99d2216750d30eb9279347683817417cd0d2e70686e723a3c28f4cf0811eb5fab26ba817aa642d30d047c158d7bce3e9e2b044a2772d36bc47557d606069f04edb35c81e812591da824aa857749fe8ddbf852544f90e924d2482ea80e2cdfd61fd458656c44afe4c20961582bafb00a29d2e8d6729e981c2fcb78f004bd86abb6a3a553022945dd9c4c32d7832b5166a0dda8a3043f23d40eb994380f3f0b77426159016b4cefb5e6ca578a114ea739dc2d8f9d395b21cce27c86750951114d4db87e35d215e0583ea99a614678a574a2d1ec2719bc6638446f6a80002f9c0f84fb5cf60c14c83801b5298eb7ba207d091d603a306fbe4a4a894d950b139017206a964d9dae45e8d9b58e9139c0451d42a69af5c907237e0cac8b6106317da6dc7c499f6ac34d0f5949d7ce53270689db2748d94779e30a868166b80497d9d2b3bf0dc87cdb6fea9bdb5f9439adc9a692cfa19cfafabc64aef8846d01bef26e2d3f75cbc920b154246ddb4ed2a8626fddaf2d84982f643c390d009f0702cc444464a996dc396bbf233b20b450f708ea715cbc3cf193e44d0cdb3961090f6c5be50f6e7b0958611b067d1f481f3dc01af9ee860487035801ba4667ff023f2ea625bf70815c7b721f497b89bf8434eec87969c6451e10b4454839ab9f0f5b4cbc524547899c3faa2397e3b3bedc4d34c2f7e9ada313b6fac9555e08340cd627313f3b3ae47fcd87d4a5acb683935ab8f4e5d5f15168beefbac35625bb0e2cf3429b842ab9db7e165bf47c93347972501c313ffdcb06ea8a1894d023b609966ac616ea3f24ea8c377dcf70c1d07936a820c4a37e072ec9661e40fa2c370bcb337f4850de45c21f4e60f853e5b8fbb20d71c3f0e5d4a00b21b4141bdf22061cf6e34fbcc8a2463584ccb0310f8ef3f15946f0526986416534f747d2e770029c3852d9c0d46a8160d994e56033a5c499d25fe5366241be260badc6aaa621097983aca6650732f63e851516d058f79e4c85b1497ba7721d7c0d9bad8ff61401c6e5113fc8fe02558b80eb72e7e4d4285f9b92c83548f68a3591f8b37b91e10200d5c212c72f84b0535c1970513ac5ad360f4fbfc4b6c87923ea77ac51022909bf73e9712ba3fd81963730471242aaabf0f163f1abc5c36b13647a3fff17de0ec95472b3319da442e3280e2e9411c20aa497214994def3d630ab1f0f31c3ea0d3cb8b7918611f81c5651d8b9409ac8d809cae1c5cca7d100240e5a2fd4cc7709c8ee36e955607923a2354afe79f319d12b2ed89e45e961d1c42098ba9227bb07c763113263ed4bbeddd4161907c6ebde091352cf1202c6cd43125e83b358660a9c21ba63f6d85c887387622dfb221a75eb61d5ded6002de2159d49676e9660024cffa8ca3c16ddf17cf8320b335e8ae6350a805e33aa689a8f49d5dafef1200250aa4d3c738525bfaecb4e91c66fea978d306b72872152db47f1d15df8ade10d3352ac54d394ed3eadc08270dfd7b0a0ef517c19d4c301171b0cba083fa7bf03d644c57c913d4e55581eca048f1b6afec58414ee0123227e8bb24c9774f4eb03240389f9b6bc36f5285fe4aabff100fc6bf4c4d8b461b5b8b53925aff922fa0f47ff94a2c836bf4afb078d70880176dd10271ad14de14d15ab51b79e0ae7dd030e1b3e97272b73d5388f5c6db70fc1724dac099c7f2b35163483d6d39ab0ff0dede04e610bcb6c93239743f71ce9178bf43153e7ca9542097e66ed991f1ec3041003879127ece1a88389ddad9c2f94a4ac9f45b7a2a0cfc04ad58b679ae43206564bd6d505585abae6980105ed4cb8d2860f02b12ed6b8edd7c5e3b9dc70380c9ba25583b3d413b94c92f25d00a05ab1f99ef0ec24b0b7af98be8203aa824905f39acbd2274d79c0e6bcaa0eb3875e0746c9e0615f31c2fb01a9269f17004e049c47f81f6bcc1c24bbdd25eb483badb13be9e3a985f1e3ea609a140f2099ac05d54765c49ad17dd6ceb89e0482a73988fa5ed3c4bbe9368b025f2b05789cd703cdb97cc34264c4fcc74fac10520af07af1bb503580f7588b5818f4ea768fa60e8870ee9d83bbd655f6a4b6c88c8b265ee27923185c60f452b5e05d391a987f004ceeac854b9b69220c3df7c1c7dc405fc8e38bee1f250ef5d60dbbb00efdb30d4f3c37e101b802ee9056afc9d11b925125d6794ab33c163725cb79476cdafd0c94407107ec87808f1727737357f96042e2412cee6914dc15f01ff41ef9a04c049efe30eaff44487476b352507cf6465ee65565caf7d9843d4b3e31eaed48150567abf281a39146b0dc2ee389ae4349b872447a268f865d1827664c50f70da304ded291a5f9178f0a76aed9ae8448615eadce1bfaa96dd237309abf994a53790b9a777a420335a63e8aa373186d4e16602b16c7a134ae0510f17213dd09f1050b9bd370720bff305383fcd6e30122771441c4afbf25ecf3bdbe7c39433d9fa5065fb21f0fc35d55ac4aa65d23cfe4f6dd45145a629052557b83d98c0abd22af0836c0e6ae1af1d5b74c03c6175a60d273f5ded23ef27107c4d041443ddce72c092cd8b0f89040c46b76f362188e63fb3bb946a96cd902000ed8fe4889fb1b35018692efc8969a11a73e7063423bb9fa3e35d5c1a5c73d4793e6d279a87103e808594b5f9840eee191f4763e68063478bf6671fffc021937524402bf36581a740c847f0a38ec37b261868801e7db99582666556dfa34ae8c10a6d2ed56fc9d420e9aaf2b6bd9f99dfb755f30f12ba62782f6aeac6fb82a146a7782febb11995f09caecc6d3afd73519afccab621f1d74ea287f7aa7f680e69422f33c5eb33e23055ca6b0176040f164df393f95006c27eff93b72cb4af3ebaff41033ab5022b100b59a4aa020f2b96dddb9e524d82eb5148f8e142b7944602a40a8cfff785f4f05efbc8a7befbab1983bf5bd39da2f24381a39c6cb6138d8da5831114bdccada00e64591df0d0d784388b17efdf5eec16ce5b05215cea3bfb88332b50d8525d303964cd9e428dae2850a7b52e616d13610fb2b73bde3a36e474570276573f95505497336ceaf07aaf0344ffbe62b6e6ea7242c95e859fb8680a9284b7aa93f13053e8890956ab777cf63b8a33d50c5b37d4e8d1d1b816330c35871c0e1cc31cd0d09ce1b2c1c211e2017b8902d125d5710b3bfb45b4fbceda289068962bbecc50fe37ea72f6076ef61a1cb656c7e9507444ea36ddd8f33bf58540e1ace0983210a339e516f9e71d7a59fcf3615a7846b869334deebcc1bde138a059f6483f6190cc7f40096af547dfdc86445edb21f030275fa395ae0fd3bbd4478e77f9d03270969ecf3f8d8a69a9346a9bcd70186044b5530a33d613de2f9116014b9b23fd902038bb563426cd017a136bcfaa25ac62ba63b95a5157c084b61cc67eca8076f0a62044d4b90a7fd02871de4fb818d6f511b18fa1895519784decd94582f01bd085e71586c15c13e2c706ff2333f38d7641ed384a4b131e5243ed93658a0682b0fd5f7979aacb2adaec79d6cb489ba2b9c655e2a15a1302547ec8f4f1204c1a609f6396d4126ea5f215d4429fbabcefcc5f869ce1b1df798c93ca77e78092b740a979c837b66f24a57bd736a439838720b40502c133284fd1dd7b18380302a720ac24c9513fc6b1b40a2ad99e96355f7ba6cf62f7219949c15ad233687b782e5031ebb0b17ffd26540ae82d9cbb519c9d63689ab0080630237b27de4cface4130d421658f7ed02ca0ce1deef9f66a45104349cab7641836ff22fd49556cc5f7b0d5a348850f01109ec0b252ffee2730171ae5b38177df286e0006d00f71e6ff206610dafecd060e32bfb38047107ab561af20e67a2ada588c4dc0e41d5b906120126007e4e401284b7b89cc1b6057d061ba6b58925f32a2b9734972725e46edd0c58e390f3d3b08896ccdbf4fc3d354d9667be637e503320339ba5b29974b43d06a55f540aaa5b1dc97dbb7119acd357a5eb05ec832b28b8d6faa83b4d3b935301ae6e9c904c88d129817bfe6acea92c0e315b614a81619510fea182568e8f7f069b34830fe3dfb434027ffa50a9240131797fa9126a9c07d5cb112beea6d1550e1c175cb80219c9a0e2044f5cf306ef5200c046041df7586bf4a4eae5a12ff808348d332396a95c9d577d532e180b08d60ea7328aa3090e18bc5d406037743b0ccaf39c08f044cc7dabb4321b624e5f35eaa47332deece5c916687545e6c3d70898114c9b809b3ebe597fb5c51476ae02d5bf1ac3d59d21ea6120303cda68b5042faf93d37cadf59b51852845d6ea91c097de0538dd495f6d2eb39128ce8bd70cffb366d9b4b7057ec31289c4a791662c4e43f56c3c65861a584a96b7c47e03073d2a7521ecd556bc9d5719d3f87a9729afb5174ca089cf7874f64b48ad1cb30ad16382c2d532fcc82a925845b8cd73799a92b806dde1ee012dd653db1027050631673a1062f4e3c6700299d22d469d1b45febb52e0bc63cab0a2c2d5c1d8ee0c27feec12ac21e8689b7d61065ef8627e6a4887f456f039d70a219de3f31a350c518bd64b9e4c21fbb5bad62361ea15d3cfd17a1c8b65a40fd3633d2ab05fed0db816797c9f639215efd11bf0a125c1a77cdfc40c8c85456d073562e204e0ff0e6e4b28eef155110b097647fb5e5391b47e92aab49a8b7fca5e2c9d83f1ea4502cddbc89158266102b63bf373fc6dc8d86da9185d6fdd6801900810978450910d29f83c6e82b89465b32b7691d0cfd29fa02e98cef8eb4195c0f746d76aaa5701f20625ad3adb3377560ee0f99d43fb8f0cc96bbcb4e0a864ab056c82bca90f06f4b0f78571463c5ce2f38d45f2adea7ada235853202307a0c24fd4940fc8ee0327a5c9014146524d5b691427e8d8521a1589e6d4b1dbb28b0c9b39f96c005209421197d6fc37b876274dff36387c1c0aeea08a8bf0f48ded8e528d4279d3c70dcd814255b1a1356814695c1f014e3824a493f0cab89a4010534c9678abfa550a5359d97da1492b70a84233e13260539912545cc7ee6a788704bd8748b5f44c003bf10d892e4001ae98382f207ddafeef70b1053353804449e7f800ffa29d450afbd4c1954a9fd8c9c75fbc67317e0be138fd98fc92b731107e278c3facd9200c61357fbcd0a7307e842ce0d4a3a74b98d67564ed10456d894ce0a25f907ad2031719bcc0f88622c6d6df47e745a2401c4106e58acd234583dfdf42dfe59574009e0c6e6843f09c44638627ad2ce04a13d430cf50aa555e7f1763a9d6408d5e0fce2ab1dbfa39b925ab31389be828c9374066c5b4c1f5f5e305d811a6b19bbd0af968fa1287407b6871eaad1a212647454de936690aea678f5afd6e3329fc44068298e2d0a446018527f7f149636002702b274cbd54c7d85227d227251db34d081155f3b8c3e97b110dc65dd948c493978255e11e36dd0bef469a18d50e54890a50e64a70f97fc8231d0e2b0292768c16a36c716dd27dd50ddaef618364936007b2575f05a96fb4f78ce8f7700b55a7d9c0f435c85167575c9487b575e78f9d0e00d00d7f296891845725141dbcde9af140487fa2a3d289c05304905f18d4ef090cab4bf25b36be42fcf661a7dea2774d40e315488aae416d79c44ccc6a0cf80f2f2e24f1e7eac3d9d9cf1c815658e9c2a27d6106093d1cc99890f69d25369a034708762ac0e17181b8706c5157855327e45b6babb3637f0e90b12a98ef667a08489bf45360b8421aa766a5ccdb48b1222d344533fea4936ba73e4949c48a620022e1d031dc114669d9f87be1fd64b33f877b52e358be288cb2297a365fdef009b7e8bd1de8ce77aa19e5fcff2f6e8fd903d9930d64059d58cf3fd6207507610a3727705296407ed7c6acfea04852039dbe3082e587c194c4085bf951fb4c01072895a632393a2e14b7ca60705cab42c64bd47ceaf5bf7f4912af8173ed10750d8c5defbf9308eb277c58ca4a16e8e9eb341e0952468d9cfb17421b188f5620038195e422a427306eb2e57c3578274dd9c3c82ef0633f4b9e6b0f096edb5d46000591958004f1dccb72598c323ec685786b1fd332d36fafd16349f2ea9569640fdd2f8ffcba6e1bb334e090aaf1f867666dbbcd2b4a651de4044aad45c858d1079269c0c7955e5fd7ec0f32d053de30a3b7238af119325c0520300c2219ed040b5dc05c59b892e88444a6ab61d33330c54957ac54dbe2022d43cf1e241a86350e0332caa5a29246294f908a2e15c1142a031d6ad569daee6bf2b1fd2f6c868e01d27bd11bd22b68878a9fb0e39687cca97cc85020078570ee54e8dcea1c3ec80ac54b9a9ab10b036376973161ff5baefcbe6e0e2fc8981811b941b78c58db6700cc896fd9065d035c34ac16560e487f88acd52f04af48f62e6247367587c90e047034d23328486b7a3f8ce38bbfafae5b7eb2912e0331b3c3c270fe6aa01adc084378c0ce8ca7f248471322c1fdec2f8302669055c0c70a8de0e18363a8957600fe5684c6838054808888a594c1fddb92cc48ed986b7b26ea3ca0c537d2a8f10c5aaf892688651b6b57ff72773564c3aebc0644d4f9f342e8aebb240b8a55d404a3789f6b6e9976b0e455d6d4dcf0555cfdbaf805842807c8ee8e67ff7cfa8101bf4d4d010519d9b3ba4b3d15e193fe72ac470da50499cd71982efbeda0a3f506531a2ea2de9fb977fb86d9717b40a5708b3a3f57d002e3aff938d976c01ad00be152ddb18580522f8ba610a0fd1cd11bf81dbb4e018ebf8d4c5ed7a6edc9da0f1b779bd95215aecad8bf79d4bd72d6f6497e5673d51f1ed1a6f4bc134e560a08a8310519d9842e8f19d0bb864c76bf1c916276fee717fd9d931b9884017e22054a955d109f0d9198e1079bb9b39c9d10170bc487660f8d2d4c3f48b02ceed60ecda16a2c79bcceca001940b03a6abcb1d332b89be5128ea151dd8229986d6d0daf1d6f6597eda90526eaa29e972fb152fa73da3adbe6db554a40901a97f211047bd62f0193fec23605a7b0f49c45c77b8e1392b8c4124c6b156aa520af3c9008bcd6f4e2de717f65737a6ae59a9a09660365316c2aafdaa18e4a6991da1087033ddcb9f03f489b4fd4a6d014cbf904ed665cd574434b0e9fc0e8475d0ae61105eb77681afb5c6eab4db74ef4003aa80a782a99cae561fcbaa6b9c9e8c619a902166c713da372cc0d179825f8bc47b149a724a77aaf80da4e00ce6a917558f70e42fd8eb53007daf1e4ad5805e509c6e82f229d7b94908762e945cbc1b57abb04bd11bb57e6d731b5389072d1eea66a2a27b9dedce4ec3bb252025796cf6c2f0ee621854bb65ebec90488243c6111347d628ece1e8f648f37db7954c27eb88409562ea7763f4b1fbb300e85a9308728e803e480dcca23ac1947527c5625fc210d1f34274e525fa5139360debbebd85fc29533c2e0bb183765142b41437c4f000cdf674cca50f054994c55563ef45248fc9f7dabdb65e0cf907e10e7fc03fad902bc30a2c1429858e0b417d49e82a0847680accdb8b4ba7389f0a76e1e7c29de0faf8c60ee37b52e6b8c25ebec4baa6581af3b469fccb1ad193b5fbe63019a3f0824fef748cc3bc1036ba85f7604a78ee9bfd0dee7182a38e613c8bfc5c3c5c00bb59ebe52334d60fec0ccbc53838451e7c39e47e43f4eb2c63cfdbe938c6968042ec71010b5dccfcc389641aaa6f730763f5bb861867087a7217770e2dd6c53045fcea0ed6cffdae0a9a4b8d3592a425b9bfa599d398dee86fdcc14baa0fef7bccfecc7a568202f82f1101bc4102736d665ed75fb6a032941d82bb3a993f3c00b88dcf700dbb1a370d93c46cfa40b7c9b9376e10223ea9655cdf59d7b8f655e09d60208aad6a165c073246e40edf98919fc9756a60a91a4541846b9e602aa740d0661e22159157dc02adec11a299f8f813f0ac344ed66c94f871cc51743aab208e910336c1ed7e2dac7e64739dde05c6f3d1562c2a5cdf082d2a5c239a204c703089afb153f0375edbdb306b14efae1017ebb242199b7142bd01a4507376bac009dc3dfdebde577c2243f519513444bb644825373d8f43b33d6ddfc234271b003e7ab4b3b41b5f8dc4dad49378f0eb6bff3ac1d72f7fa054fc355c3aed15074089eae932bf62187cbe20f2d5db721a679d8537dfba27b741d819b958c8bc8680afbd96791767fbae33105343eeefd53f8eab15b5f1356526d432de21c5600e600b66030d7a688ea0e0344185e284a9b9dd579df8001b7115514e20fa25dc94e01b6b88b8f38bbe27f61203c416ee8f338aa243b75b249bd3c9e88a3ddc1b22400ccf70b97d7a0839a6fcf05f9fed09b25a69870d02f262f03bf93c21a1d5a8f07107768cb53bc6efdaea045297c92e54f0945748abdaa5486a4b5fec1ed146a069f15168c703a19143d8ec4eb757ac2898a6a0fb6772bc99a5a693a704c854b09178db1ebf4ec6f3ed77848eda1749199ff17f08aff565fce4d2ddbcda334a20823423eef6878ae3749aa2de64c1c8a806dba10d46d41507168e60b1b3c2d7502cadc1635745f06fec4622f9bfe457c8e3be383dc66ea3af07471fad81d190c05f96183b1d20bc84c28934bd9116f185ed51a84f711c1b1fb297ec77685954d04c21d9ef425976bdaaa6ebb70955abfd83de045fd3497d29ff57690cd462036077c17004ee4a7dbdee574d39f67ebe369621e35786116a1a91f150cc3c99c2307f4599544464c176c10ad48ac0be1dc5163c24614ef2c960dddf49c3028fa2000c3867760e2e5b0f8e10fbd77bb394c5994e3721a519f82d8ff0d782a77ee410764a0ba9c30251c167db386cd4a9c80e2f07c83cb23445ed0e5abe10f3e272408849b77dded9f98d975de7484f80287062d9b5531304149c846e3ef53b454370a6386cbfe33446ee90a3bad83428d5d17db5fd9418d95e5acd5607b08d6bff40f3c21fae8543f15f012eec9b458e74dcb7ba6f8de9ee273fbb529d0437fc6dd038282b3ff083f956c56ab1997488707b117cbbc600300642938dfc664eb648805e72366a7f8e1c4ce97ae430884f0909e6c0a6d3fce6cae2b1ca2cf75dfe18108e8d9a6a42047c8151875cad48e197d915eefbb6a73294d6ede523f24fdbed9046e35e3a9938fab5f66f04a443b7c5a0e416c780ce35f24d3faa6a2296198170adbf0ad49ebcb2a1b6cca71aff3a1f052238398863d23009786416fb03404d00bfb60d016bec5aa9db2967b1d99464c3cf633f176a2ce029ec726879ad2442a05d16fa3cb94ded53f5a2e93646d7ec005d31c91c6c7cc177b8346d74a17c11201a8a06bee06f1a09cdd3b6b3d9f7c0e35aa43b98b6ff63de09a5af0954eb5300d79e65540285517e4a1f1f10b1771bd3c73e40102edc6103461c5aa1d8b1ea9010c3b2230bae3447617cfbf6134fe0f1cefbdcaa9be58a0bc9b5c962734054d03a6fa97559bbff63f7156bc681fb7ace61ece5ccf88361a03f78b22898bff53073b6e17e4ffda5ee5fde11b245f9c4c70759783cdc272d50099dfe037b2944d04150edf3b7ddc197bf85af8dcb1b6c68cc9bebdd336c26a662c708ab053d9b006e2678c9a90b50680a8ff6b2c3919ff0982ac561cb8c5b8b19a7d771de9d5da0ca078b9c620049aeacd0ca665fa1bfa3fe8524a2d3e5f564bdb31b9f57b8ba80a79560f4eec8b6eeb648d42b09dfb06429c53cad2d43cf7880045ac6815a5150b21b6242e753ce5e515b321a1f34cffabfdf935cf8f81a3d77c65608efb59fb010f69253c97eae6df4564a7400a47e5bff165310c0ca41f38c9bcd01809c8080332e352e345d906696d00d92178c90b5a65899d3172e9829a57852d96d0b8880eb40f95765f957dd9b371d178026eeeb396544c32881c68db58ddb01374cc850a6d3214cea7098cbbc79bcb742067d4a69696e5718940637cab3a7dda1b39300aef61ec1af91961f43bad0e085f35ced40c1ef1590e12bb0c35c5e6efa60b01095f1a7e2984bf70cf9a357ae1445373e45f33e595445b1b87bbbc625f2c0b710c942f832a0fe75cd0e97d1439a078e66e004c8ed01bc4632332594fa6b5b1e30ce5955291e43ca8e92e039eb6ab270c2095cf6c0cd81f2fe4b97c20d330aa800de7ca5adf345a196cbca1d2600d996d93f4eb612221dc28d33107b8c98071260bf7a6bceebb9500c0ba1c01ec201531128e7703084b265c2b7246be7feb793c0d39a11ffba88c56b9ed5c9eb4ab4c660223138ff44374d58d7a3b2d884e4672080a6ec8fcfb2836c6a2feeae3c3fb546985054c0d35443d68a921a191d3c8400fb97e6f17f51406ffa25314e8d0848d67b3d2a778898d464b8c2b599e6ca7e707b9107edbdad5ec2591d51519afe0f6fdd27019622b12903f2d9852d4075b1e0056c3480078431eb0be8abaaa8c4f0557aaec3db7696c1a472dca5168189b710e3a5d08ee82ecaa13b12787b9a1ae2b50d04490f51209eb56563ef6d67f102809ba061ddcb871caaf40b51f6d1aaaff73414119dc8dc479f8ade1a4c653cbeb0affeef2572afeb90d3de283324749804fea3eb4d460bf6e44474b1884ff0a3f04ada18f38b64a53d902763a0a19623eb307884641def96db6d68a1b70851e0309be0f992ab93541d9315d1062b973e0180a6aec096dfa39a5d641aad34c961507345bbfa208bef1045ad2929de34794fcce312ad590f60e8a6754d6782ecf500b9cdfc04a579a2e9df56cc5f39c66d1786ca6232e02f506f1d36aaab2db5e4b0c07975181da407a4a3af16ec13a5a7701b20599ec523d28c3517a4b8c1654e00452b5e5c10a6103c92a21719b994980b3c5161105ae61b017f59ae7a8f286d104f88ae2f7048e3b8f2e92482d3f6e15a4b24a7ee83707b8de18082ad2de1b300ba20c1293d189e5aaf6cea7f49d715defaaa52d17bf1d89c022b5fb0c6fd2790c6e020d1f2418ff7dfc1544c92e490a0caca211feb22ffed8d7bacddf18fe860295c84de635245b23e40aa3d1ecfe64d3a620cf34eed5bd68784eb146b924040c47a668e97b42975595aa048f26059b8a8a2197a246ca75183da886f0322dad04acf96bca832e1982daa1fa4541b18447218b2b7ebc539047b214166ffdf58c00d18f4b33f1dad3af5a559855a27f2223078d0e7868a5a0b789ceb56d43520e0364f4f99b739976772566593322e383078e37cc7a73dcaead6ce6a18d435c0d028cfdd69c99977f8a2f79540c8222f0fc3c0c956373b585b48b7b86169ffe2203258b7938ac62f50a8fb821f176104a2c0d019984b4b0245a815351ff007fc801e76d6eeb6842732188c25965c31892a22727c06294668f7ac637bc7526a2890454df6c9d27fa818fda9db937b2a4eeec9254d753616d31eb14c5f3df5d2e460dad4e563fb9ade5cf60195e27b261438a80f5b675482de7e8159c3377a8218609d1bc589e89c05d5cf6d30d679df0fc5ea32d91c5f8c8b258edfef029dd187807dc5461f51ea5917e5d0df4ce11a1618face15f3717d28de57ca50dd632020106ecede033512411a7af988e4cdab48740dbe9a44fd8405706fbf581489045c900e6f6e89a6778d0de6ebee5dccdbd28398195e9e1d3b50d911ed0d43cd834f00cd4bb1a2b5fa775efbeb7803d46787fcdeea5a9f8d58b32391c85fa0e66ed880abcefcebbb8cc85d3e1b35bd90fa430fe3d05c3769d4cab122dec4ddbf3fbc8026d2dfb132cf2720575f20fd1826b63be66eb816280bec3c50592f3d6204c670fecb09cc387a771659363a5cb84b22cc4940790ca8e7e21f53a2e94dec6369b05d913355df42436ee23fd066de9f9bc867487bbeb5908264e27a8d6280331be04fef2da05e60d38293ada70f53a12157aa0edf4cddd42d51fe1b6825971808a0650f4d1a3d58dd9d426bb8e3a0dfc5d23bcae447f0663addcb5349588dbd18a0a false -check_ring_signature cc3cdc034369c01e11ae4312fe56b13d1d5ca1a7f143a9ef104ec79bb2dd6280 915c932b2e4a5a7390b7e75d54704a1919e6a7a4509ae6edff385c6f23ff9a0c 1 6d6ceac113e039efe698f993c6b397531c5a684afc530b45c7f68cc5973fbafa 11003e2c2bd9a4f96a1261c33c98ae56c374ec505d4cabb47de7176e8bc350030c9bbc77bdcdd7c271e7ce6545218487f078fbb5327b73ebdd7b9fffde7bcc0f true -check_ring_signature a9014b62583ae54abb4930758fde197249d88a95418bf34e95602d165ddc6738 b38204bd1b876405f8559b9dc592f464448a7daacd446f50314c08ad912f4655 7 3cbccee9100e7928ab09eb4d8fdfcd5b2f6fe5b42010b805a39be21ea71cf03d c91e96b6cf52e1ea441a2606605978f79459203ca6e2c32def43264e29c1cefd 8cfae39403d7e2408c52a1e617e068a27cae7a180cc062b499211892a01e5f48 9ab9e18bd924676716a3cbdd0105d98bce1a081430fbff332ac74e9fcddbb1a0 43f5ed7e0f4737a2f70d7b46b2a33c62ff9797fbb3bbd9fedb4efc79751104e7 1a8b33c30886e67b7266107bcf1c74dfb66a3318296787c1f6f3503adbaf08e4 45a42e920932104286b51a08b89d667278fce09db9d4a362ee0042d1905f9705 94621edd1e3d44bbe0759d1bbfa376b24638faff033092b3163196f729455d05a9402cd6da7c4284e8ab4bab1b6f887bf1d466c6ba71e71141c4698d74132d064f158509daef5d97170b7535a9a3fcf0f61cb41406f010ee645778c39ce77209b527b03848f6c2eb9e17c807c21d94c4f32f5fe250d46074b033a9d757733f0e6a81af51210a6aa045cc8c98cb6e23d8b0e9f18dab36f5eab11f8f731d0263064e5221b32de0d571a9835b5f8e7487759a655a78b30d9ab4736fa8d28c78ec0cc1f4a4d08a209f09a4de1244c6590cddb37660d6197cb62cbda781142b07270c40194ab3a3d3d1fd3f771a7b21aff43962a6d8a46fa96e75c0d6ae1e821895035c8da1daf116d6388abfbc61f94fe7d78ee2ebec51a5e81605bdb5dc217ec10462f99772ace9d3e6e8ab17a7dcab8495ba9162d24b43dfc088ae39fc3e9e7f0e38f2834a21fc17ee05a3aa09c100e2157cf6d74dbe76bd9ee8b2807132778800b1696dae2a56788a7e5c78884d9190dc91d89a28c9143fb0de09e3b84bd0db0fd5363de5959f08605fcdeaeb2fca2d2e840ef075a6aeb0acaf40b11335851402443190644810465a6b18df279addcad5f664ad38fe70170d77f2a1def390760f false -check_ring_signature 2a908f3fdc60318076ed93f18c1286981d6beafbc5743706a371a354618f29ee 779253c369f819ac211c31490bd4558f9ad58776a40c32b6377cc09056ffae47 4 3225b973394c15b9004228c77463067d0067614522371a35a8b99005c9b211d5 90a776c6ecb3d52810ce4d97a7697074d954e05f1d4b73525358036f72323610 450e0a81539217b77218d479660e8cf46d458099cff8f33bcbcfc3196a3f244b cfff9beb02ab13977cebf3eef2003df4f179b78e6cc8e4db0e83a63f2fa94b6f fea0b2017901409d9c526c06f7b31cab33f02562de3e028ffe69f997122ee907a8da3699afa492c618bd45e1226989f9434bd0f87f76fa3e650001c32843360f687d2d4489de20e5ee8017aad4b9b5567c39e37d60092e388ece1a4c22d4ec0c65105343c7f606796ef54b799e4d72f942d94417195d38184b419d192b405109ee87327e8720262c91985302f73f8a3f13ef1be6d4511b63a85c81a536117a9a4eebdaf32a9af522bdb018034c91835c021e6770c065f182f3559351c63cb50b325035a5fd225029f415beeee972308a505421ed94ce8c76829ab6691c9b99078240a432499884a8e75a9f7cb25ba933424f5dd8b918d57a53d82d5e9a9b2f03 false -check_ring_signature 1b2fc1ff75b5f21907b9991dedf3dcf8e7420321be7ae628494a105764ce2e97 a001d4897e2bf0c880da98f8f6fcac026d8b63adca63eec5de7f1192c118bbb4 1 d9b8cd59c9061a8d1e61a0424914509660358d40c29a3f1dcc9c6ecfc6c71403 3a46a08e90295426683f137614e6725835a124db3a40b734ddede088014f0704e8283e35c3b07cda873585b6a651e90b4c96def3833f97a35de8c7f1cfb53712 false -check_ring_signature 934f62799b243006bb7f5a806e559c76d3864a9024330ce7f4e800397b886243 b73909dc3be452f5d9425d0ad3c8b98dd224b1635ff34f30a3e5c8f0af7dc5db 26 37fac6cdb0ce1b7886a52d33ac3524f3e0180e13f7d5161e02ce3fe80bff05e4 4d732ea37159547be5ab6053b56e4a06c2c9ea79115d97c510d603d6b6fdd820 a4b6f3032941ddd24f6aa9163fcf25dc4cfb9bb87459fb0243d232a45cc4d45e 09f82a701571138095ae00e4e5eeaac7c4eb5ac0d1f1a8af0ed1ad195d3eec87 5a54ed0b13fa76bab4ebc6ac57529eaf23920cecd28e0c23702b1a8fd888a825 e85333dca504c4cd004ee5f81784210d94cfc0ddea0aeab845c453158b035a2c e64527b380477e0e75c67f7fbf7fb918e19418e05fa73e32f9ea04f33c21e3a0 39615f3f5d70223b02038ae0bfb66b6a5b229194a56bfc4ba9330dbc72a2c95a 1bfe92426c61606d706b64c7796c834a3a7bc9779e83771f1aef4fb5b920f2e1 745c23e29159dc3aaa988209ac3aa5f08e2688a8fbd48dc008d53cd9382ad212 9de9af082a7ab10cf5bdcd7577b4118667efefcafd098a4f3f2ba99958178eef 3587c9f93fa14f4187dd44e54a0813acd6770c435ee709064922686d94fc6308 95ec5abcd04fec9d4eeb912da1fe513d3a814dc734895757c32930858d92f182 d69831155848e06db179a0518c3e42819e6b7a41046309147be2eca597ebaa7b 32ad77cb3d16b544dfea57994b43445cfb11657b8c6e26e881efc61c61505184 16b8a7edcafa1be617730c64955592ebec8b6d84731c64a821e986a48d9a6859 6814a13231ff3dbd4d677213ae28207ce03b81db814e844929ee2b588da3d116 8cf8af9d0417e32cf3f04bc51cb006859d443e6a1f2c67a4652d59d65eb3021f 222ea8aa7078f40eedc3c36648b94e1ca57cf6caa0f19e76c64cc508ceca39d0 eed67e78d19d4d253f8473c19a1c01a44fa18a0b438fd77d4c210f55cdbd96e9 f2f08b9148cba186fc03c0b513a41826565ca98549b96baeaf9061c1c1301449 b97fa8541f500afe103e00485074b883ec3f49f7b5e30cd9f049800c31bfc687 a7914c41cee0494ccc1ec1b3ead1cbd3adb66844f09151de758879de35fe5a29 3313e23607ea7aedc191fad6f111b12ebf9bda6ee19930c2123464941869ffde 3586e837afd375c5608f099f6305f399b441597ef9c33f1980bc27465f9793c3 7ea2ba01f74b2c8877185093f7438f804d7ff748f7093a51888ca20199eeb8e0 91bf0fb06b09d1ad60bae2778197740c579088b18e75f882dca7cd3b2d2da707b144c9463dbac07c33571b08a49a4ca6c3a025bcecabdc83aed0bf0bc60adf0803d5265e7aac22934a3365bd692b7fbf6b4fdb7a1c45b1e2e315cb7df0a0f9099eb233dc81b40bd0afb2e190b25d11a970261b08e4b37f210f6af1f83c171a0e0ac77de28e86291f98724260605bfa780f901756e7bffb334ab0536bd3c80a0efca739c7c7972f00666de6e179da361adf66cb4363840be6f132334b0485830b58493300020731cd40c70b14b5bb2f2fde1cee998830fdb38c8ce88800751e0e19397bd16d48c2b3a8142adb2f5b535771357d403c082686ea68996e9704b00750100a369f7d0c08dbd3a349e84bbcee9e7cddffeca946099550b647fd207f018e9d9a6faca4620b32fb97ed78052087f4e38d3d4b23c1ed33f32b8c93411e0185e5aa2edb97f7fc96f4ac60fc8a77483af4eae764a676bf63cd35a1e3bf3904a53f142b3ea9f20f0e901628e5989b02c23273d3642078b63072970cf088c90cf489a5ebb6ddbb537f2b5e12d3921cd44c3d35ab38b865ce7285248f1b9a3105a9ba0dc810a61222cb8756f05ddbc8ccb6c7a3800f93bd1682a5101922cf3e0bc727654647a1b13565b5d1ee804af30edc72a20e14e673c16501c8881d45b90b1f5eda7532917031b9d4806e1dd99e861d66f16f684d1d6930c347715771dc02a4ed7608a4d0c3c24c6076daa0722f22be60d81ccb8d0617631c50a6d8ff440fc957582c88572f0229f7f74e531ad6955e19b09cbf805b021840cfd7e0e61a0cf4097c2d5c3cecde4f7297d508de3746f063bae6235f3ba58ec0521359468108625e1a20ae5e9becfc893f5b56f471710659c80df46c87efa7cc5a2fdb7c7408f03606bfe3f526bae54f4226f1b1bc68cdaac141b9ae60fadffafc08f4a2d709aee948584adbd11c9a5e392534c654a9749bdf64cd80c1bddf8c6a907e90c8001b6e30e414e8f7fa12a2d9c89e5b6adc6cf2475731d0b84f289ef03ff9913a0661a859972983e13fe7266b2f59d7eb6e0b176976972c1c6094bb952c712e5e0e37080c07d0e6562fc03c9325f0f60713b1ec5ec5dfc5dd8b91df9eb284733a039128613d884760e8fed4ec4888674a7337e9c390ae9d9b9148004eef52c8f80a2aaa23a34012fbe91753ff013ce376fb5f3119e3f2c9ed082aeb70acf4f0b6099f800811f775f972d4e272b524412341ae6d09f53c24c00c305971f1c5321a053cfad891cce05a915456ae16125bc6ec2c05c8872711edb8aff4e896031db00741e27514926cd3eb2241a85ab3c72303c82414ef71f354ec03cda13cc652d804a74703a8b380e6b131e46480f959a66a11f0fabebab3f8724ad4d35f165f4b083afed0076a846970861e36d0f91eb973d44d7f28fd53bdfc63c6267a402bf70d1310578f878bd5bae0c4a7f2555d97da9442c7f9518d727d4961e1a785f1a70394c0b0c79710dd734ad470229e16aba6449a271417a153f81a0a0d00872a1e013ceb131b5c6bee84cb4d755def779447853fb914ac9090960cfc948b1bdd2a033c2b8e10310478a96c9a2e03f1ab2e28d3c4c0422e61007f0b13b92012f30c00de8357ef21bec63ca01e9d0bfa6f3907881f4d57fe56a1c41509a217a9841e0d58bf355d82b94becffbf63bd052d972c5de3d9442f90551bf05949d353e4a9058cf80bb73bdab0e8455407ac0fe17d1bc7fcf8d24e13ea904d3ba2b70479e3043701479b76084a47a27efce1b1a6383d626c1b5c4ddfc7289fd37ba6caf2ba0f67320ef7e68df72fbc8ea7b39aa06f3c06cfb519267a7e4431ce665b15d83a0a2c0ac020d6d6ae05b9940e1caf0d63fb84c0c07e5e0631554b988364dd9957049bf05bb2fdb66f946ed979eb4c5c4c17aa7073b779c9ba6ec8128c3f63e3b1093dccd05820cdbbe93a5271a15cca535d731c9da1c1cfceaf78d3bb7e457ab6025d7eb1881e73f93723a3d37eb91844db39f48e951871bdb240004b32ff42be0c809f0d8431687eedf24b4d6aff2c156b22285ce9671469a4d16ca3d1c965ef056a44db2fc98271171326c95c2db536ba49aa66fde6b1cf615c0ce85cbff80a0a140e0dc97805d2da1dd5173c2a72c7aa3e3ff234e740395108973f02c508c40856c90e5972f9da0d1bee432c6197852b2ebfdd462126e4fcbe10f3aebfb09a0f6ab705830968bc9fe9c8fb3fc203e08e36b4244b5ff6bf6eb4287a5966ad3800319222e23adc834077c719b8ac00bf8f4d499ccf3ebee9b4eda2b1f933fe700ea3556317615e5bdf12a3bcd23801358d101cbdd9cbc63df5251a069d93f80d02 false -check_ring_signature 87e729205e9c5a0bc54a08e91e401ae7b99d4302ee42af4800c12ba2bfc21abe ca54987dd236b170284d9c1a1b44b92f40296ea88ad9c60b656ca65c07844b67 2 93eb88599d4c8021c37329f69b9089147e3d733748b8779adbdeed4ef3fe9923 972cb49183ac96d78b9f7afc8e1f1d0c3538a5d6fe48549043171b23d3d45aaa efef7b0e93e76934c0254b32653831a4d45025a638f8f50cfd995d9e66d3e20f774ffbb34be0225cfe604a13071fb10f01607ffd586610365df9ba87325579032b5abbf0b66293c87bc059d6a0253674ed93427a32e69f66e0648d5d92b2470c8b3dc77ef3febc26bd30ca50584fdd21dec69a424cb250c238b9250d948f5f07 false -check_ring_signature a6c51cbce5951677eecd398091af0c1de2dc03b3334101517a975c0cd0c8e52a e0dd29335a889e8f3de70be7ff0e85d96579d95c658d65f16bd21fe7e8480ebd 47 8569e3bb8ddac52b9685ffcfee50682c55f0d5ef627b5346c72d1815abeea97e f063362a44c799fdb91ccc6e7a9f19975c250db25b74e62a30324af182ef31ff 568f81157b408968b20ac24fa15ad497f23575a7df6d14942361fa7fa927d14f 69b8b97688da1c07158834f81c8b8d1a13cb6c5522e41714826b9c87e09980d3 dd13e5b5ae64ace790306b65dc3d47c9213989ede90b17ff17ba030c7e85da01 27169a7b6b658ced99b5198893bbbe9f478e285cb03547013ddca7932c7675d6 73665dc4f9bc293b7526c40f4a344e03269c2f4204d748abbd370560eb34cf63 c69ac4939e0cd5158a2b4108b01c723a065b8d15455001021d146523b46e4245 ae85868db6b4e57780d42d6afb8d9138874746facfaba07d50a479842039d3c3 193f394b7f48d0c1f3535423a056536b1378386aeb84d7a4da62b3fdda0576d4 5dca30d9d6c76cfa4e018bb0bd9b9a24f7620dc572a8d82f6ae2b31831312667 012b8c4b77677ba67c822cb2ba37f04766e1ea6e7e9cfb4e49fc936778515cc9 1947f66d7069aa3ca4992d2cd046fdcabde1c301b8190accac0e9f76fcf2a510 329878c13bf86e05c45feb28e0795e485efe54265e19d39aa48aecb8c9142dc2 43ab62b7e66cb88527b78b0af796bf9fea487c27c111d5dd4ce205a3c4bb557a 308472b0156204356a36a755e55b8747b87cb1a0d2fdd5e0755c0c75eff26437 8e93e7705bc065b066a628fa22a21750ab4b29bd716fdc65a40e0e09a001008c 59bb252a5dcb517d20eb10a261fcc113eace978be33b8b7d1d0e1030e8b11116 f217ede3a3a4bc7c92e0555859d2c8605f08c461b26f95f80b4232ed7b2130fe 39f7218cfbb11f0b4e3439e8324861071b8b4a5aac9c5d760c334944447f0e56 e7c448a304b7bc6fe0f5e892ffb90642a0b68bd730845d77f4ba4dd8129288ad b280951d49f5e55c541b173a09424e6b12d9def2212a8f2f1b5348a177c18f96 06ba0818206e8582f6bcd670ef2e3dc4b57f074f5da1ed6189182eb686370092 af8206a2b3b665c75e46dd2602ed7b8a958f748df969ee0e8d8bb701e30e1539 75313c7d8ef7563be24f0c546641f3ca67613562f5509800fb9822cb9ccfb7b5 2b8d02edeab6ef5dfff5d77a05e914f44862d280691dba73070ca7f59d9feb48 088a68c866b340d8073051dccab2d9c3e07f224c0ae617fe1d53c982cb58ffae 5877ecd59370b4b65d90a0f7f5b24a1afa4f6c692fbc4d033b143bbade505a8c c83be95d27f2b432e7bded5a4bc7c9af63ee0be79d2024f42101b1e1afc2e189 ef15d7ecdbfc6a377726863f692d3592bd2dd4d1f1ac8a4b47e008607a316d40 b0aabe91b85e0eb8323745fad1404ac348058da141b69e7fddffd035d3160439 0e3487169681b8e2f0b7c62fbcec4b94311fab417f077058e6640e805f90422b 1fd97e547f86517bc41011078a1bcfc2f61887f020bd2fde28a20e95119937cb 47a1172505d0551634a49989b99af3dc5d57dd37be260910f8c61efeffc22c20 cf2a134080760e98d5bcec1d8f4ae1417ea8c9fcad052569a4c721a1be803bbe 17efa91ef5b630b529c28ef7f79b33907521b6d4987fb6ccc0b7182adf8d7be1 a00fd907d2800bad5b7832a0b5296d012c9beb5682c4f1c036149b1552c59046 b953cc42217d53510ae69eecb6a2f7c8edfc22e1d605abfc301a7934de2b13b7 c0c584fe4ebb110fc0150eab647e5a0bca85cd1f360c92e587742b6c8c9a8323 a457fcf681929475fd0ff96e359183ea5c95753c3642f617c5ad1f4411807abb d80ed6697970d83a75f481734affe8632ab0f65e49c043ae9d7f65f889ce278e 334af62282c7424660d68aa7e63d2b4f2f690a214c709bc47300f7c16350a0e5 3d249a159f6c89aa520cf1012d082210f4c0ce43eed8f3635f85385e406366bf 3f2e4391ecccb2b543b12e12e3d3206e676a41d63d5c18317ba68fe66541f197 265cc776f2d3725122f35f3133d48acabc1e4c631cda5f4dafd5abe0bfda0b87 5e9b2efd0aa805719284e04e8e46d7abbbafc402f9de171ca7652a75b9e346f6 bf3bfc3342af917dec0edb33c858d3d2114f21a8647d302d4d8af180c5b78417 f019d64d89fb2e7ef36b85acb64dee0ec20b6126f0b4f46b9335b376839515088637735504b6d3c278bee07d8c394751973b732118f5332207f78590b360a002d2b9bae3d4df6b7a0634c4bd925da04cedee83a5e4b41868803fbfdec738a70699d881a1696900470f46b925886ac73a1bbb40474c9d43c0d8db8e69f32a0a075886e9c50a5486cf4611f43808d27adab9388c2b99d6ad9dbaa42084f9258f05cd56933fdd6113102e33cc53fb2aee66403462c002406edddeefdf23e26e1200cd41494153f298c72f01e316b214575a2b0ae1097a01fcae0cd022ec38e6aa03561b93282f5a05b90cfda26227c09d391ccaa8149f5de4980f59e74f738ab707cabdce5a34e4b8970f5171b59a25e141f3bcaeff3f673ae13e0c11b1ac211b01c5c98aef6224197d19879872b8320f9b8702e5c86106d0ef81e2909088e058021215c40e3fcb755380562114cdaf4702f3d90104ddab04320f51d467050d7601b3689b4f6b1497881fca3522c406c3683a61b04464dfba011ebe3088bca3990c82dfec493933f040fccf4c0fdee5e7729994c5b1094b14d1f03026901601310455c366204c6e8c28c906669c4aa6367a9d197538108ba18fb9f9df106652430d02c67d90cf193e14ff7169a53f5f10f086e932bcc865274bdaf0b861ce4d210a7cac203a69cf6a40bb7893c645f12ce53c9637d4732da5761d762140fb1d380f74edc939e50a1addf62aeffc76b0a8b5ccff3d1ff762f0e911138e04e7a0eb08771ade04bc180593dd26b9bb95575d1ea4b2c7f20faae32e00e2266c97d87e00f79af2126a257b7ebc810d4845e7a469e632b0d7659f447d9cc56d77b9acd404ea537755e910632be210e1d47bb977df01b4e7ac7c0c122acc360431d1ed3a0b14db62e019b2790af8f3621b8d833c5843b4ab041158a16038a9199a68b92d0dec7db57c79e05ada54e8ed2c69edbceaa8386ab93f383e21adac173024fc900dd414c75b3150d025d80ca77c991c0ac24c6d83261761507acb93b4900772cd0f8e232c577686368894361b100816fd80dbd27b4d063b5ac1fc43be57553ed304cbcb34eb165156a47e2b552259965358bae42acc3eb7385cccbd3e8188cdde0d1c541aa1d9e444044306e33cb204a55e150388c97416374650fcd77710cd0a080a2109cb237ed7bb942d06744b90ce4bb70c99a58da3c4800427d740b63edd08bf867c8a150c0010ef0ff6158f4c578338d4c1f1e9b9da77a848bc0773a49e0d8acb3454c12171b91681c713887d14237138a07d9dc96d33b6d295431b142408e26af3cc4f278f9a489266f53bb4644ad9b1364db45e3b13d4ca8c088e6d2306d1b1ce0848332592323f1f50a4e14983e5232503f1ab44ef5a6c9474c9649f0e5e25d717337be68958b53adcc22545de46504534c6a4cc5e59529be5bb3be70ecc3f3c723f80ef58e33391f5ef70b5f5bcc330347856c0597374917ae0f4d807719e5a9c2e4d3cf937853169b28cc7e4d302962e92f1e5b4f01b682fd2962d046f282b285b611806bf62ee5a4c54a620876e355f35db1487bc4484c3aa505701785222108df9cceb05f5a2a7e0468771d8efdaf285fb9bab96687f9561912808befed516056e4ef2142d881fe1d63c93ac37863a7313bc531785c280ece93b067b3726339a65862d0ca845b00ce14e13b711f21942e1ddf40a3449d3e7aa29081cd981bf8f47f2438c5b681ef6face5d1c43be802473db497104cef1df49f00ba16cc3f235bc10dd5337427dd055daa5e1cb2d3a1907b66264cb11b209c41f053fcd59341c5bc38bf0909f2a6c330caff4abe3833446ad837ec107bee4e7d80629e0fe66e9aa8597a0684bad772f51842bfbf1b060aecd5d7a4ba3ae6faa0f0560043d33f3d8b7da1f12eadf2c8829dcc320f11b0a7c37e6f59fe17bed14bc07f5f7b1e8daec07072bc36cdeb3700253a9cc15aeb28cec3aaef38511882a4004cba9ab98a2c818656da903c09e71dc1575ecff41519d9aaa7faeaa2eb202a301927f93782f93ba518549713b839bef60ef1e95415fd9c6599107312590ffbc07e4e72b7e82f87affacadd32cb91208db9da7db4ac62bc98d7686b58c82a0bd0561531f0f3b148fe0b6cdef183236146029ffa5403ab1aa0078b08dba97a63f063052a27cbe87bf5a029be302853e39e7814ff402a3e93ab8c2f911734e0d3802d542cf1d67ecfaa8253e6e110651a2e0079f20c6841dbe2fe0cde35b403a830ccb10f7e1971a1dabffdcfb7684965e53a3b3ee5fb628a5ac54ce1f1ac378ac009441578a94cf5fb14514b8d4c47fe7b24f0013d1b8cf66d7c8322f672fd1df085f7f2347a0b6b8db57066f9382f87f59f4a2807fb0edebe614db9259e0ed270470b3e344c571c6c2fde86880911b72044d18ff84ee91cb4d57c41d676840f308a9aee68c18cc6f581cec608a5d109ee67cabd2b155475064f2c693118efa5d02f211be692bef2513a16a0a10de7c5d8a3d48ecab75e1c108f3f2cb614f65e3049eee5c15c88e98a56a38db7eca03af7022fadd180a9ebfda7b15ce511fd1f200152d8b4091f99eab876dca31a03f43c254a79a038fd7e218650bc2f4e2b9d1007cc11cd263215e28cb0a401dc67b13722476743b7c61e166248eb7507aef870a239f974f9ec3693dd54eeb1fcadb93de138ce5ec5e7b7af6dc0b113216c6e408975b9118b9ac8085288ff67149339d0d6a3fa4fa8c7306eb6f755a32aa1c5806e97f17e88324fbfcf41fe69d09e7e6a303267cb48858b5589e0315a228b598008cfcdbf4252b4a2b6e984f294c8735de1a09378e6447f37938bba70e76cb5e0ae6e67d54ecc64c63f4969428485c6bb456db2f7c723138af6eab6b66113a0101e7dcf935bec4856f7ff27a42d551ad560dc015c473fdf89dc9d76ac50815460f08b00df8cbaf79cd6f91f7e3beaf56e22ea4599c60dcee129082d4d213638700a30f968eeb2c848bf458c4c7ecf3ac1f1f4525d24cba445c6a9ff53d70fe750fa282ba50153dc5ee93985615d53740863ae545e4b8259cc72d8991647f4d39029af135a8c526ac8f5637509c616445cbd2eaf7f67df912e0268710ba4e85d70e3446ef30bb1edd819a620c026c3e23d42abcf811d9df8957ae5df311da2dc10c744c4c5bdffeeaffa6c1d7d7405b7d30f9ec14ab496569491bdd37ff14c0da0c38d03b39a84b9a398b525341d629e143c3c37f8266cdb2604eec801b53ab2f0e0e5124ef250ad3c46078fec49419a159f0e53edfd9d3478a1698e12aed93a909c6ea727ee6f6db0f2cc31b0f783e05b1b855facc9216be1ccc24487a86470a09f8bd13331c173a96aa052b9cddda237624baeac1d6e7b5675cbd1098e4f5190ad066b8018eb7a30ae82636b23dcf6f607867f8b0d0c236c4e8719e1b148d61026498417adac92fa6b243164b999188a2c1b10985b138ac400bf23b30d841ea0c9578548a3bc5e3399fb294ad17177efe13f923e0c514061f39d95b073b64f50842c788e8004e0d260732164935bf7fdf42edcebde65642b67c46a4ec27e32d061bf7ad45e075eb75ae83002792fa3b4a2d994952212041e040af195ac042b00840d3b6fd82429dd5fe3e55592e960ba7124d69dc5bb1130e3af028147ef70602f80ab0221a8abd451ca60ff0d748cc4c20a0dff0b306fd3d42a8a4474fc96b038d16f3fb55c60619e728e2656e3ec9378451f53a29b8ed5313457fd7eccdb503261435dcc8865a13f80fcbad6a22dc98f819eab86920bf1bdc8eb61a713f38015d7c71f6a5b058cab7ed111201a604f4f167dc9b400a5f21655978801c31e8065f9fce3a985601f4a5face9e5576b38385c5cefebb459bf9fb02d6c94fe1aa0c398918f9dcbf269b0943707914c4b2d57d3e1321cadd79ad1c7fc8fcbc036c0c425148b86954c8cba4aef477a7e3573886d249a9bca74f8d236df804380a3204ad3547a0bd01c9696b7cbb06b36bcf96da44f1c638f4b569b2666010bc95c70de83a1935b22e64ad2a601aebeaeab8229344ff4958498b600522a7ddeb548200a874464fe90247e477be7afb7f85a5fff2b37ebcf5457fc70a9bcedc92ba4e0cea948814d40ac1c02d9c700cb9cd269b3b6240458825c8319f8b7418e467c40a3e962f8ee8194edb774604de8477ac201954d47ace9a5d93d8896d24eeb36d0322a51850892fa8ab866449838f7abf9d85ad029c0a92a44daa6fa4779980f70c true -check_ring_signature 04b787991bc8021a59d56e444ccf29f7f9f123904ddc56c034e119ae492b7efe 9adb03bc64daac48e1e77cf1ba0f1d81ca1fd92a939e966434a5671677a7d0be 3 648d5861edad5312b5dfbfad13dafbc4ab28d24b240a232ccc1ceb37c924b555 a6c4a63bbd07cc4c08d2f20068ebc11a03c2b7d94a085af732cbc034af1906a5 33de8d260d6fb249d5355b3cff91b10dac5429ebbc1e19727a90c6d37e48ea63 d3432992bde1499d48b1528bb5ae2e29fb411ccb0986db29f213a8272310df01c858a6d05392901c24f8094b85b179783b13f4efdc59d87fc836ac8c0c52c9024f229c0d2638de0cf91983683360acd87517d4156a014a547b622f8df4a3feb20e527a0800fc54406165474297540e44dd7fa0391f8c816909cd8fb0910da50398c4405194c3089a59a0c2cc150a099380a78ab56fcbc4f5b8a04d215e37780dc827b35539f1b4b1e0b159e826cc3bcba5aa4dab2d7e07bfeb4fcb93c4ef0206 false -check_ring_signature 9b45bcb602d21b21ffca8dddd55ee725cc37c0185beb5956b75c28b4885eec8d 3c61e6c7b52b59c24dba8404d69fd5844538dc1624c8ff5314e875e35e75c4b5 1 106234baed705041bafd2205b7fbf911359a15ede31b4aa2eb74cb75f90ec5da 5949ac73cb7e04a8c424185bf1524c7436fb63b9af2613fa824da4163da5b709e2c817d2e06f5a9ae142cf2caacf105574eef4632e4519489ec601a5da1b030b true -check_ring_signature 975eaad62d2cb9d17f03e9c02de7f5f53b756e4a3d33e1ab0d746a10a784a1b3 f763487eb94a474ee62305effb15e8e9a2ae4e96d982ef8fc85e1b60a996ae10 14 bb080838c13a858823df76fd684d087e2d4b3f8e1c3f0289ef12e907a2f86b78 910359f0a0260e9cddc0d9739afb4a901f4a9a4503111451b9d5466af32c8a43 4b4d674588256bcf9c56cd536fdfb3272bde2e533e9ad5b4801b5054c7d1c4d4 ef2a81053f5479e8f09dc57fa8a7b648149573f7ede061f3b1904a1b8a40d457 ba0deb2c763972f7c92568605e85cac9eebfa22d759e937bc7fdb68a8456bd8a 08d7c3eef62a06b0b7c1120a91120bf2a8db6756a0b4110f20bf3f6cf7147284 d8298ce820783eafc01abbe0eb8d992ee074334b43cb25b37847ffb6773699b3 097aea15c1758cd52fa38ff7345bc5e02a5fdfdd570e2d9d6e7bcd2ffa3f9c9b c2c35841d29657d304ab3ca80bb9c5e486a2db461c0079b638babb1d73447f38 cba2d41a7a987c4d1dc770b354983f5cbbca47538e5b093ee557db75787f2de2 e5bd35f57870d046c233f4e07ae0d7e20dd1e6d24634734c4145a2b1e80c205f 957a7635de0f1d047793a299e5dc2c142afd0c46394b32e4d5a49feab205b09f b8ec2c752e916b4c5367d1b8a4ff79e948f861d656ad9a09752213c0a0795e03 2d2f8199aaa3a7f4aa5c782a244c1488b6577af1229376ca9719bd51e5c2455e 7659867eab16aaba884590ad1a26ba01a4864ba8116251b5ee54aefc497bff05bdbb0cc69dd78e89fbf100ee07c40284d791fa362c98b3b12b69c612e64d2b07d854428655c037154c221db56700d22955d63a509baeca0d9dc175c399a3f5078c4c9b60882db507fe51cfb676705f6db2f8b297d013f58b3117b5a0dd64930b2465cc563f31e5744981dfc1a70c7508e4978227a9592d603bd9f57e66050d0857975d7d78dddce61a843f95b65c0bf7f858411041f22eca2960f4e74775ec063e0dfd635c5fc54246cd176556b496edb83e12f73bf4b40251bd6cd6266dcb086776d6889b90473289d8f455faefc4384c53ff40dfe7053de5dfd695363f3f06569655c570eb6dae6fe58a7578e2c9e43ab00e5ad8564f5d60f88bbdbf894409b35176279e36d4ea85244381b7888262da2fa455c1539b3e0063f4e4f9b2bb0dec13990320fce6330691ca7304cf76adb7ddcd1aed312c511114363ff869510e4935640819cba899c8904b505f2276b09af8bab87785f61c0e2a972277288602848614ac580fee8d9c01251ac72c5b75e924794295d1f710fede9e41a27a9d03f04c7650a932c16ba497c7f02c95566f668e8e5dcdfd56ad8704f2fde798baf88b6959ce6fa6d1beb34902eb7045b693d9b1e59ce32d3eeae82a7e3fe3a36c07ab558f4bc0d645736740100fd4eda9026719b7c2d55119ee7cc5922deee31d00831a50f4aab17331532edac4ba430412aba4e33a2579b9608716943ab1d25f088420c62fea4b23784ea38d67fc48576c1691fee91904f3be91cf3b1c23a35308c4b60f2b9056fcb85d36371426a3fe804516b1194d0f68faabf4867b2f006c07926081eb77a391bcc89c4fd2724083d072325cadabf0e09e83556d57c8b238002b961d6b44b9197c20854b7ffa1d57a36859383abaffc885b9d1ca715cdb130359718d230f205f4ffcd2b478c7a1aa5f242e6b04aaa6f3eaff9b6017194f9e039bef69b473098034638406cbc7d1954a9d025ab82665e5a61ddfac7486aca809129b826c360d577278403166d48a4b6a770a2bd1bc965497301c40ccff6aa9056da15757e62a7526100f1175857136598fbe0cbdfd518d1bd925d2e7e29d4db2627f586b85f984b48f573fd05abc2360a73c15bb520481a1658440c2202f300345c31af19d9e00bd5daf64ab4d4aa6ce9ff00dd9556a5a66b0960034f5a697052baf9af97f3baef519608ce3a11ba4328989eb16e2ced64208e2c0691dfeaa05 false -check_ring_signature 3e267796bc50007faafb36a8ee7780eda5bf4f7e412fa0457dc67aa13f71dbc0 4e331942925910ad379ca62579d5f319bdf43dc6100fc6ebb59bec22421a56cb 16 ed26502f0b479b288d603648f920108d1fc8812949ce0d4ebc2a20dc792ceb6a 99a3d93bc5d93639b9ae2256f41f9c25c89dd2c2881d637349b135b8e010c470 9c647f823a3c2d9e8c6926cbe887813e2f99ee0234f1cb976476f527527386b5 60d3bdd16cf539f9a2070f525215d00dd75f243730f15fb06f6fc6bca41cb167 3df24627e57414457bab1eebb5bc340086e07f85a340173efb60e3a619108e8c 04bbf2d8a5f791650f753243fa634033c311289ab0b549302f2297e7a97ede8e dc17a26d6fdc9c8329222c46e090e03a472ea237c38c0b594e9282f96789473a 02829136d31d1a0ad7e2e9ae2f065b7baf7bb7122fa2bf3e6d2c7d9b4f997beb e223534308c844fe8a2ae4fdf5720104e3bf31b4bc42a8f58ea4f49d76ddc66c 5628fd2faba6894b3e22144fae271aa3a15ea3ed2adf400c40f8b0fdcb7a0240 dd995c83aa9b8d80c7d298204ed83abc98013c0d4a5d43e8460c6951f6edf211 d4a71fbeeb5c8b4954e0bb61ccf545fe4b01b757ad8584a2e117e662c60b8496 0c1b95ee43e98ae38a6ee8d171617f991ee28bd031371ba469581b2b442a0848 733f0390cdf0f9bafe70278797d682965336509485deea7402df25f1bdd5ffd2 bc162adabc7075ad3673511115c978dbf31a4929b9bb3be5fdae2857adfb51b1 f302d17a9d4daabc93d785b3ecbd542b34c9b3f35fd7ee73cb97be787de4f862 d43c3e47d1c89dc16a912805a6336e29e51fb1422f6e50c70c6e1bf2a2230d0eeae07245794b8eb74cbb7472546afe8e0285709c9e78bf912c5ec55466b3190d45569c911c8951c013e568d482130bc18abb519906269de4f9f6b8a76f4f020b72a8dd1156cac5bc03ca5d9434b060e353edc58087d2ab725394a8aef5d87200e30c460e7d0f61da7247181c10cb12befbb5f300208044e0b9cc5f7ec087fc0b65fac86e47e2a9976f2279e90c123ac64233b943fbe511072896a7fbd16ebf0a23d49972c06d0ae63d26e869cb4f42e237042ebd2cf02f265aa97bd4e050a70a027f7f19080bf9aa21f5f299afec9d76388ca7e9c554f9fc9b877aa26a2b6e00a92d9233877fe6d763d48283245d778ffe2639535e10159deeb9d3c3355c4f0e0da0a8a24f58c836090065035173039f2ba43ab68240a27993167ea2b4385003af1872a83f60a7131488527dbcea4b58377698f190732332d40cdd34da5d89040c450222a9e9ea60ec7591ccbba7d7eae3913dd8af1fd627b1811795855cd20b9e93fcbdffde036d0a07a7bc4ceeb87063e01af30dad8a43a5ca3b60b798fa0d971db11da4c4136c906ee0909a3a806e5bce632f4376a9aca5bf63b0e2e04509803413ab8b939f6c4bcfe1f93321ffbc5995396e1e2a8d13105f124ea3ca490e610d7a7c186a4720de04fa619176577416bd38827f83cd8e08f6c56aad0e810395944f900c66cc1b6dc395f952c84d30ffa4c2ae7b5ad97ed387371a49c0ad02be6f1bd2f2e77545187c3290c1d00d5be789576efca28200ffeffb2199cd1af1e4e77014e6396cfc397d017e423c602e87960836994f649fd5009e161f19dbec10b182250a0b17cf05cc15804895e801996c519c89186391abe257ed09976d0ddac98ff8c9c197e8ba0b4d4fc00e02e980e64d9c91ae6c986a47c19dd7cdf0099f55d9305c64768e50d21d4497528ecc83094e468fa073efa76e3cf6387a2700c6cb0d1825722ab1f78bee9be9babafccfb0ed76df40fa918cc03644e9bdee03fa814562cf965f33f8b5f19de5463906fa680cc67a8152076e45ce187e682f0ff6ed1eae27b62ef6afaa390f564d7a8a16a51053e3f23306002e7ac9afecc1002144ab1d1fccdf6ad310ae2f74f0082a8b36760264ae575d5caae4aec08d270a6a95502aedb1a0cfe34354999105addb2e0400b296c0b30e16110c12261005029d30a6b4452deb8d2efcd0dc61d46a3cb8898f542f4e84632d8125fd5a607c0fd43a40d8cce0061220cf35e98c83b8936947e5f3079b7adbc2b4395365775b0a3821f4d08a7fa03938df5348708cc743947de59885c323b78fc850074ffee108839d447466da6e8299fe0d17ff736ee060a1db3a9e86c6a6f677738543fac908c94540caf28a345e6768617c6a89715dcacf61790743a246c598fe0794398308 false -check_ring_signature 6ee05c303241e5f2efe1b18e4d177ca34b7848e6a7e5d6844b2a91bf10e2053f 13a21cfabae60384fb3960ae6c776e3db5d535eeab229620d105c4d0cdf5fa6b 1 b4b080d2704a0cac7da8043ba02cba7c46c3de51946a4611af11cb6170ac5f25 001dadf87345c77ec33a58f232db160fcf17bbb6da8d72f36de0b6d28d31a6099aab8e4bdd4fd6c5b144372585039956763ceb9b3551ad4f4997f8be4df9a907 false -check_ring_signature be2100645ebc642409d63bfe78a46afd8d384869fe1608db11a21f76dc7dc6f9 640b83e305f7024acf4f3d7cea9de72015485a0b3c24eec488280ccbdb5d03e1 46 3d65b23be2f31d7fdc04cebee591aa9f93450b0a55d0890306cf53998da06f11 72ed7300f8fd118fe0be9cafe8a86dfc3a203c1ef51fa6135fdc205e53d3cf2e 9a9f24d0468eb90a6cd90d74cba92bf80da3553a66e01b23aa4f033fdf6d0dd5 982614069b49daba62b8402cab7cc66b92b50135cd3f0d20b6e2ce6c21b58fd4 b9c34228225155d733372d281df7c06ae4bcfafb16dd82c87c06e7d8065914e8 2c97a02c1a7ae307967a89922219d5f2425175d5ecc18e964c9c149fcc8aecb0 22b817d741bd49a0e126b646c7e1363df19931192531faabe60010a784ececba fdde45ad1a9faddccfda09f9e126946e9fd8ccb6ba39bbe14eef749d30db3c7a 4e7d8fa7591e855a450cd151b01189b57bed961a4165c73536d6e52e87a64392 833b917a977bb47f449e623ddd9982862823331c772476af5facc30cef4a446a 4251b0a6be427ad89d834f63e733f846038dbdf8acf7368d599dd9fb727b9975 a1ad484f5eeed848094f6bb24f81f701437e07abd8d4f857d818b770f875175d deeabe52039ccaac3f1c7af4b8285920fd96c3a5a670b3799cd52c23a4e46e3c 3a95fb3011d9a1495e2dbde8b3654041a3c1b74ad62b918091b24840dcf34ed5 146ce12649dd120d64ac1c7d26ebce5be33333f57e51adb62c65cbd084609e62 3017eb6f4c5a6de973dd22157ab2090701737726a668b4d8fc7fa557a8c5dfa9 9c0da5ddab5802cb3b15d9084428ab5f7bd36c3cbdff258bb9198dd9d24aca89 a0c6ef15d03b9fabae63f4cf810ecb7b01a67c0f20a3f6702e4ae528b82bba4c 9eae46dfb8edf0a56bdfcc88bf5336403c4e6837bbef952721185955890a5bdd b97f01ee06a9bb1982f93eca1a96648f8979e77f3b1627fc1a3a280eacf171e4 3f5950398b97d08f1d8380d41991e33d8d44d9536c581e961ed5d35654744931 35c393fc22b7b0c1f70c7421d6f37c8061ceadc37c8989fbd8d73eb978bf1bd5 b5eaa32914d74b81d8cfa9b958fae7c40835b7102cbfd210750fd880d2a9964e 77becd6828df0b290c6e5a11385224b4852b3b5a97dfb79012ce785c634250cb 9f212717199d89641c8f9be79c30a126b2d6f76f246feada54866ae21f7b7644 3eef86201d8398bb4f45d93ccd21d392663620efedad59f90cf9ad892547da8a 2f75f0f0a05bfa58c7cdbbba9ba5473efd7237eda5c128c6d0f971b23ceaf355 c7a25cf5cd9269ab3dc3865062bd5bb2479220b00206f6cb7386df534df0822b c6fa7598f0bc54a6c896fdc47e38b05dd60329722e448b038a2f0b1bba89bb5e cc8ee814144e520365444868fd9e5612eeefc22fd1d69c9b1cc417d7ccfcf79b 8918c969ef151a783cd10c5b4d6394743bf3d33255adbcdabfb878cd6078a9f1 b670731180bd25c8e8f4edfaf380139891d16bd69ad7e02098ab67370a268d4b 9cc3d5d532221ac8aa683d1f1261a3193d22d25193fc794bbb5d3163cae55580 471d913eb9ba56cfaeb5e2b1586d27d61c805604383eb1f40eb0c44f6448f456 5f6de603c371697e4de713a2e8282e063f3dc338d82dede935b27d09751414cd bc3bd2a5701ec97a46e562be50a337914dbe3d138675d362f7811a579cbcf420 d0bc3471c34141b5c07887534d5b5233a5b93c4737885af419664d0d7f9b873d 90e65b27b17aec4424971b34e75c5d9d4efb53aaa88dccae52b25eef92cd7cff d5d8f0ed76fbfcf1c6b82d372e2c50c1c48ec1899abec3aa0bd5d75fa0749758 18d3164e6a3e5fa5a4d84f8724707b31d6f3ad974ed88e417835362150248c65 36fa2a8c47ad1ecca09914da30b1299fc4604df0d66e8b5a432c573d8ca74c5c 73318b797db8bf374de2208fca346931800f066cc61b85a9d314365b16591f0e 6ab2888721b0410ff16a926c86bedc1dbfc6a629d83a91a37c9be0fd29001668 fc0ec36830621e009574a33ab1513e0200797326410cc289c13defc474df1c7d 78e958b4a79ba80bb30c7424dc89d6385615014a09f8ad3e2e408c7e0579bb83 e531e20d5503d07dec2ac945e8872b6a50ae2f567e5632085e9dc030c1b1942c 3d621f185d20a76acd218e9095277aa7dc6facd1e07e400fb483577bc54ae80b15fc2160374946ba6f3cc873cd62f92526d49be3b65025179974f9fca1250e0ccc3e48993b5caca7bbda2304d8eb7f21e5177ba8b391e424e804978c645767027e580c86995b96c784de9e1dc83db9fbb9bebeb5e13fcd7079b2a797cb87c40979971b37fe58595e7d46721f11f605afd3c13c046267080df74fbd3a003b3205358b785b3c6c7b45f49c26343ffae2b23a721e6c69e060da7c106aaba1e15a0c3d53e9391ffc77093d0098342b5f43b47445d59023a3a94f786bd665cc6478022135dd055ed0c1b9196926445525b7e2449e99656cd98e02215f3081eacf8b057e15332f7693a21d7f6de9d578209446c61367c11ad036e3c7373406d102e30664f5e21f4ff5517987f20cee905ce44da213cc7b0cf9663b8975d5d2f28e3d073c24df3520c8f40bfc371b2445a9ea6363c45e11050d07007bac4e06b99eac0066dae3c1e67a87d39f33ca6f95e613d149c85967c81749def07037067c20b70699549ad10367295973c3d81f65336111ced8f8c427f237fb1c0a0a50805fe60720da1f9f0bf24de926c37db1e3975d9b986ca0004151521e3083e0378bb6e404db0bf067d316d62ec8093b9b66a7b8e35025a4cd9447f55b96a8a00d99dfae0d79f4970168dfc09e30837e5a09b12358db7be7cca9a0705f6a9cf16c6fd3fd083c146bfc2f7db753cee010967955d735b6c24b3bbfa00d1942e234c847fe2f000d5e4c0ae2e5b8baf232a7473e72ac46487132d5c326eff993901d0a92378d04de4294558ffe99ef785407d8781f4e7a989f5a4846e90fa7f51e28c6374a2c06e7b3683364a8c7c77c5650fb98a3f99b034e401ba6bd1fb2239986fcb7a1080b339625d4cd66bc0d5bb3e45e151ea566f92ddb90a817a6e41b904d80bd65df0021e4f291ecc31afbdd9511599bd52859aad9c82d7d7888f2af9a16e09779dc0ba1bfe7c2fd13de85d1a0b8e87558ceee5de527851724772763ed123c74354f0ba00a3748aae76e9bef249da52b0a2215fe87e91867959fce8ba8d8822d11fb07edf3247539123bec4e3596375ae482c243f546697427e5b6d5a6b8773c48c109a0d1f3f740e9440e1930c0c4ca2030bf621e7f27a59fd969fcea2d5a9a367008c42efce5df98f1f6f521a8bf94a0cd0b636eb8dc34fe4b423b1ebad794ff8d0c7138e3c689f8fb14ae6fda0066b754f7525da8bccfd7fe3173d7304f25f56102b4a6dd4c2587d9dc7d0cade23e1e2c7eca67b8b7d1794e4e9eafbb3b9b382208a7c33d2834792b55666eb3e904e28a86e5614c59b3dbbe4672f94405ddea650f2d11daf69aacbdb4344351dc55ec2c6b82dcae745f00815ad45b6959e0888e0542cdc694f86f9e0f9639219e0685a9c99b6c8207d3c3ed8c1fb43cd06addf003ae6325f104782a46e60ee8043af82f283e92ead929f49fda9dd5ddd361593c0b639ac993acb2bf1ee4be3ae2c5a1b8f28e334f4c94d7ae54555d630ed1f91c0566c2b64af264a4f06f23557b537cfd71af4d8ab2ecbe1efd9427ab44a7e33806269731a1f00b5db5f66e758a63376ef48f4a6720653e66abd5b42bf310c83e0ec72b9a8f6c17d4fc51aefd957fc1d5686e2df601e4a08874a26a87bc0b6ea203a2e50a91f3ba6b6755702464ce4984c877d60ee9f6bc92c07893aa132f02fe082e0afcb6cb7da739041b55cb99a62d477117ba21ce77121f40d5951f526a580913711fc17d462522e34047d6da99af5fd2f9646eaab15d10e0931deb3d8e5c0eea244b208e356cce2211d9d6a034047175916ff4268e97f6a88b7696d8ff620e0fb032dbf529f09dfb776b61516131361dcabd10771fee5ada44626341621206ec4851885ec072d89800ef50cb335c7c96c41f312857cce7c5ab8ec308e07404fd5c8b3d787a529251979856c95ed9bbaa8f79aa0282a0a14a00c5a7f84018077fb11f7589ace8c0897396a50229bc46bc70ed2d952ff479fb33cf83f4c3f50f11f142b4deb1b81bd3465b9f2f26bb9f5497b1c5f74b4d1c3d02de75e547370f8eba0a12e73411707c3c8e15dd9b68449cd46e6b3a1e0b46fa68188e82f2be0574b1f38a9b6feae53030fe934e2b34ff25289d7016a6ef7882e2c94d45f6ac08bc87b2cdd1bc7289eb4815836bf1e594f9f7b6946f2985738de54a1be226270abc4adda6d9e958502e90a04ac41c4ffd5b2f06540bebd6e8d00dbd34b694640080e0f01596db0d6f96a80cb6b81ec3a5c82ce0e57ab2ff81ec91d3dd725ffb0cde0bc292da5c1d00476ce2bbbe2a285196c8851615a0a2ded8f401c33bab2b07fe8fa8aa2d17762d46cde6927d313de940dd45ba58f5046e0218232b400a720cd901de7483c36cf0cb0548e4e47af42967af4f7665f768211f42008fa475320f5507d0527ea528d90ee3b70b77c978ab03856713d7cde3494fd39ff6bd76cd04c9d08a2c393197db4470525865ebf4df8367f9d1bc599cc4193812378cbabd0a5d5ab9c5c8b1fa00d380000c023b4954b473e39e4b30fcad0c3eff45feeb8505f44fbe87c3e7cac9a1bab3d28ffddc183571f24b736e5e581d2664537cb0840bd73d47ea649076c8e95c295c000416db9206ea0ce591e443c40c008e371aba0a96c47b3cb6fadeee696b82b9ad0fb83dbfa40ec13d46db1b0bc37f5d828fb6071d75098953d56ffcfe37ca9aae8c62614e76c399e2151ecc0cea708c2d2bff07b033b914015566ce2ca0086c852fad8332904e49adf19b875a7512b006116d03d4e690ce2d3d914f684d9d680487bd570b1a5e5e4dc17930eac46f77d1654b06f9a3222c34cda893ec4155cd38bbf31a8002dd9d99a3cb148bdb3a89e9214208060aeecc48c9ed94878ceaa18338ac20c95bc22f77d8ed508f464beb1a17ba42c4f053d927e1c7c113bcf2e37082bec46914100d4573b30b5122c687e1b4db0a96df80c713e9355d113f4b928410acd4c74cced6a4a7b3a37d1139bb3a353f001bad894da146750eff0949e1201a929676cdf2f52b1edf94c9cb08d3bc50bc0ee1893bf81e9e42e416a580a8a45f7d12cbede076335138f3135b9e5c134ed20951eb127186a53e5fc6363e41d50f1ad3a6f335c58c092a33c3a7876b061aad0131a7100c577088e676e57c1d25631c826e9f92364a93ed75b40c2c3a940f4300b2f0126f542c561d5cabd2a56a3e7214f5cf1ec869022a05fcc9cac171cc4e064166d3e2c7302d53484684b38f05159126f4a1bf6a5f7763251a759138f1d70be2d1afcb85eb688efcdac67cd6f3f5399d2c461df9de9fb8a7ca2f9c67feaf012291ad606c1ed70d0d8da26dac607d3425939a8b42d536beb3996a054dc55702d4bb88ec3d21bd16325088216bef06e38369791c0e6a25d25ad0e181735adf0eb31891c40e7d4becfc7d24ceceb8801655062b631f8e725eb09e81c8044fa80b985435fc31f784b9dfdea5c70496e6cdc1f274fc565c96067b08457f6bde4f0fa4019c5ffacd0b6f42dbf84972f12a902c0a54b555bde716896ecfe2dc39180e9e99126031ee847c556d5d7ab88991dfbae96d81ae942c84410649d8934c8202ad73b218c07fdb25e042a775733fa3017267c4017de1e8393b14763d9a05f70a9cd28da1b8d3016ffe6a0382e7e326d35aa480ea9e4b8dcbb17c10beb84fea0a8971bcc2fb08abb401376bb302f88ae4c7a5bdd5961fb41936859dbb9390ef0774e62224be41098d3b5b8c0e44b4fcef18a5e1c0838e6277ffffa6a229090a0c9ee4c3517ec7c5f1f054c80ccc286b63184683624393fb028de14490e05124045ecfb52aae47e59780d91968d60015328ec14edafc916e84beb2f93cdb32fd06246fcd06e0a4204c834dbc2999c91d4c0a975f56e48bcfa7cdcf8e3f802a2708fa033cc2c676ff8cb47b60921bfbb0cf49f8427c5537502e0dabb6a84cfe9e06d530e8feab9024ab37483ad1ad322dbc2ebb7bce88807a373cdddb1fd8885907675ed1ac893ee2b01beba1804c8be59a8a36e9ed3c623f5bc2d84e97c3ebbc06bd269630ed98298238dbd9db6e80b1524787d58c801c80f13faec0b2af348c0d68f0fb9b32adf57fb4c2185d660f81a5294bf9e251af6d78eebaad69c50d2d01 false -check_ring_signature 67ca8409c161a5fa1f71ab91a6643ec1c9abe6a8cec18946576fdfb3ee9dcfa8 c6953e0be151d955c9a8f17514b9e2593cfd0194635cb7240dc457ff14256555 27 7426dd96dc314fe08ea7e82f8ab7818a4af3c75d9752276b789af035ffa3ebf0 19230427a745345393a1d6f022567e51b988427ae3d21669051e90c8a8ed0c6f 634b5523b72f739a48595e839122c0dca46d6500ce380a52675a64ad9f6fa955 d9d1dd89347f74611b328894bc7a4e8beb425c0dcf0b4b9801de914f241fd08e 83fb2aa4a0a00bfc9c5be7194d096e2290e3d57582cc8aae3b1bc64016abc8e8 5759e9c1aebcfc83a1454fd18310758b1dd3fd0b52bdb486207526d2561b7e1a 028b2d40ac793dd548b4a2d1cae6b6aae09d8acb45e39b0bd9364426ec570c60 1fee6fe54a01af6cb7ffcf4b685f3c5349a60bb4e73084794b758085f079170c 868394880792e2f30542ada768ec1efb9422ea214be68fa5906e587d79e199cc d41d5f7be3675c74c21e063a09714522b2528b83473ced1c19fe658729f9dae8 22f3c72599a2257c28663a982350bd0c4987f0042b5d2e3c8e6c1d703e400837 9aee674b9bd82107e9345fb94d85201a85689cd5288797683637d12d925d6819 07a8727ac12d0ae75695e856ea76ca2deb5aa487b7f99f9e5a2e85b4a8c4ffe0 00d7abe3a5ac37a635b94576cb516964a1ca5e17a25a405943b70ac6a8a505f9 d9375925718df1999aff3c2eeb292c1a955448323a2d46ecf29e778d741650df 55d20d2bf831f90fda7bc290d0e6cfe1e2447ec47e7b4a7b646f681a8e8ac933 f9c94cc6e8aae342c47a2e4eead6b5cfe97811c2d9024a5328a766937ff236b5 ecc5bc0d79b9340d816854ecc9eec48e4bb33196c03dfa6c871763e154a205a8 a98f564c6b8e3fc3bdf089c1280d1c91f2645b6efeb0bd877bd152b3b920c674 368d0f3be0052d9cf8deafb328191971e8819ede070b8694c990c881846b55a4 3b8a9ff69578d44efa038e49ec25d86567b00661bec3e86890e82c8ccb52edf1 849d7e2a9cce53517617b750ce54c04841c06c660f2112ae860043370a11f1c7 8599c2f30c4f36d2424c572d34ac876a363889eb06504bdac6ec0d96bd479114 4f928e7ecb606c63ec4e1610e41ba3d9e776da083d526667106f99ec197c0f7c 736e3da2a76beae397b622c6618473b34684e7e941a9134f06fe9316487c9f72 308d3e9049f0c03d0fdeb722a8133004e5e25afc76091cdeca84b95091f047ac 2de58a98042e89e77e5c1006eed230503fabc81f69ac043aa85b42c6cf96fed7 1d01b0ea38dd8ab152f5e5896c4313daa05e0333758689272d250a3cf2a3770fa222c903662dff56e101c06bd7aa764c83193411e61b87a92a6ee81c2347340fa71ca1ddafa35a5fce5726296b99cee1840d2b116ea6533cf7d2df8ba31ef7045daaa061771f2c6858bdeb5c0fc1ce7c61247667bab5db2f4ffefdf1fe78b603e8742fa094c6d2460872b5bae384efb98c4285a9b43d03350a52781aa5ca1f0ced680c56f4b4f7d8f30ec9baf8ba0267941a22a80bfe0cd80924e44ffca60304985ebac688dac1291f366d7a0834793e6d6b21124199146ef52139560feb410d600433d6d429335f427281d3b3c20329a1821c1d356e8da7cacbb216ae570d0197e4ab01cef0fd5c7d2903ca0fc13ff41cfa81e7b1b3472cea74a64e6962c10d4958e266e0494f48cfc47020a0feddff8bba2908e2a3a107fbf1c19c20e5e3033003eecc4eaf769c54464bfe273ea2d0899c847752eea2da5f6888140416540568c8dec4940c1d2a6baea0ab4a5210df48733bc1425d1c32ea11823a3778c9002f6d53bd70be473356fc3587fb27e4d65ab6805be636e312f83b6d5e2b8c36032715de6d2db10a3d01ed9226676e5b6fa06132a688baca70c4d3b80e9da705090bdb303d543e838bae8fd6ab54d60674c1ea6c351ed3935bd2f8147b4b608b031558167218871a545d9a09ca85f6def0b56cbddb5b846c0d936516ab08bbc70c1b1234694e100542087cd40b4c11dc3cb23cab5bf3fa59183b65028f1225c3023b940520a68fb4b3d3ac9e121fd091ffd1b9a1ad25449ad7e1230cfe14fb6b0e76555b46e90980b98e073a0d60d566d4dd64173f872472d136ae4ebf87e54b079c41021975d17e72a4e67410abcdbcca3ef0bad8eb13f8f9ddcfab3163306305f3cc71fc0f624f43d5dc9cd9f9a900c26b30591189a79931a2c767eefed3a8006b5501e137d00a32693f8f8f158ca51619aab55a024858e267986558b552580f5b2ce86cfb24686dd6343c19b9590e65ae4d3b9625c3a7d4f9085aee052c2b02719feb14c230ce5305aa72f2606c9d37bda88fc9718713341c6039d289dc5208fd17c8dea3528b7303bbb2e5e5613e16c99b17b4a84569546c6e568b4d31930aa26efcbb3fd6b64961c186ef68aa9a341a9aed0b337a40e60c7cb2981275270e6508828eca14397a4274037ece147d96b32e5dca2eac96ca57bfffad00b87f0931bfeaac8dcfd6240bb8439ceee6eff30a2345b6ac542ab2d34cdbae8b31c60e32d1758c1ea2b741390d2dde895169927850da3f93c227452d9118f2e6ba120a290f32d4407cf9ebc4515c8e024c5ec39b003371d30c49b2fbf439b2b57cbc0dd7a4349f99f71beaad680286330653c7a74e3bfc0ec5f9ab4ec45001f6c0c60ca035714f549a2de1f48c4cf68f106a800ffbf48744a2f62d7128aee55a930d089e58defa0dd9495614fd3787f477f87affced8c4cefbbc0b137afc0ab6e71d0ff0dcd01bac2f7ae4af62e309eeecf5e8aecc96cc5d96420b58bcc4b7736e6008fe8cbf8a995b46447b2b88a5aa240987524f21dadd9c1aa09c2873a19a7e0002b05f05f38eafbbb7c535e809783a3c6f67cd7281c4f45f834819462def21bb02e9031d5aa5a498ba4af3eb48093e97377d1a504a9ea493631db4079facf1d0014fb23f407b80e98708c0af30c58dfaa11fea6854eb710e61d6a6bad913af3f0456898fae444cebe10dbf8619e4f611e70add353cc3a2b204e9f844a46ebee90464c6e49274e0130de9818559765ee69deb6ccb223a7389002d6217a87ea8ef0583b09321e3e2f46dfae11297d3f81a1a77fff558e2e7967aaa36ed631e1137084e592defd9d0912d12f9600da71fc4efef1a78c277a39455205a54377c04ba0527bf98138e13bde644255986cc0d22c1f519079a7f885c8bad4da45d42cb1000fb872c68716ea4a372a485273c08f9db247c47a32c2e6eceb836696f9926fa06ecd1028d83cf05f6f6c3fba4aca88b23e03d0cd68ba46e80d176a2d53b305a06718b051378db7b872dc2fea6e76be4cee821eb25fef48c91820ecf62bd57ed0661872767b378f5a74e65ee03c501c524c522b8d85e988b4b56240ea9d476110c0641815dcd5be34434a959f33d07d308f21b0860cec81e4b6d67bf9afc2871018b66364af5734339ed5f367cb64c25e96d2c876e692b847485de95c763f062094401a4408ed5e83f1cfe979989f7d3d0c97d3759ffc92747ad0a07568bef5c0c6e68d461b8e61de251f95da925b0ed12fd1a9033d2d043bbd686b980924ca704beedc1eeadd1e93b249a97dba2721085e2711ed82bbed7ccd81af5013a1c860bbcd866cc33a11106fcb2da203752368b12022b9056cc55af4f76b8ae6d56860bb6bab16f3fa9af466687458f0f41d8fd6e848546c6252815c364da794b8cbb03 true -check_ring_signature b4a464b75842fe8cad865901e9481dbf72085fb56085bd62734993f6b8f35f4f 3ac686b317575f986506e1a09a529a0ddb506bb981aadd9d8aa0737c1ff484e3 17 582ad9475df0a198c4087efc6f75d10bfae7074977bba75cca19e9cf6b5c6d60 2fc7e44a3a4edacd89a857c98060aea5afa8d152c8043ce2ea814cd0b5974dcc 7b38ede5a8c117cd87cedbca018f7f3394f8a50d03869850537bd464fa50447e 0ab968ccb81d774b7ff44015af007a989629fb67b4d5ea12192ddf958ca926a7 4a02fcb39507f649fc53ff19f9af2e6537c5f20c3ceac93b917e0f46da520f27 9c35f2942bd8b28b11dd5804730d5949227932c5d7cf62eb95dac5ce231af524 864a401a8e188543517059e48c6ae0b2a82f2eec1e418255e3cc55a33e19c441 8d00130b88513ce7df2584a6a7a1f47cf2f8e3c7003b3d0ea273e01e80af93cc 3f2efddaab7944e603cdf6758fb90f74e3ef35adb98e1c49ff868b6014314eb6 7165cc7b0ef414054beb9c72c0e59c569d4f3599d3561288ee39a05ee6538d77 93145a1966fc97af27774d47eeb428f84851d3b4f7f6b5f61bfe9b364028c8f4 7e90414c89543bf6b3bb9632042252a28e7f52b95109a67fccaaa66adddcf4b8 274fbcb306c551179ce8bdd2d338f372b3d92d016a2679fe393eb983501d67c7 4d0b1fc75b5020ce5e396f34cc0c0e67c050b05b827592da6119296720a08a89 3bc5703a86aeb8b69cd2696b35afe235d196ceddd7d35934a92e85367a8b2764 44b27ea1e3034a1d1743c7f46c85c6d0baaf63fd87923016cc20dfdb88460b40 e74219e13d6b7aff93961d426f690923e10a0520271e9f605a92e14458b41bec deed095ae410e14f338b141cf60fa66523475f3ea8df1ed71876ff3924261907d3dfcf1a7451224598853c2436acf21245dda2b4de3d8582ceaddcd87e81740ffc92b67679d36867813d45aedead98d40c1f0f7a74fe1e8200e2b33a997e010475a0b8fbff67e6ada1371f7248104f89e23d43b787b2ed0b94b1e7abafde760d81e4b3c46c2c8227a857f39ee2e807faebadee3c3adae9553cd0b72a4288b1079e40b9aa4f200064beef79c578e3638cd9622d1bdb3b1c576dab6d9f1aeb670423d6f5ace31c430e39e2d0445e62ab1caaf700197b948632cc16e820a1ceba02dcfe7488ea5f0f305094cc8c69410e814a4ca04e71d0586932725224e81e2c041c8326ca3df0f195114f1365884146738f6910033659a5a1a61dde2cba80e10d48b679e40063a2defdfea7307a3ed346a30efd95f00ea7ddc777ad141367b20de97eb8448961326cfa9c4163ff680528b41f276b2f16c8ad71caf4aa55e7d201219082f91889736b733b4f8c0797b8984daafe1e3bcbd4c1e82c8eeff4bbbb0133c290a367dbe1a02225d2a721cf15355c053207a4296e113d4658dbfd386f0aa119bd93659707cc71ffd8b358cd6a8aa97000491bd4a8ab9fa98e7b99f1aa0ed02ce251a39711d5ebcbb1176c734c4c7c9f535cf7ce1d7b1db114447aac8e0c31059fb5fba88173b900ed9771a5a483976b1283bf4d95b971f3b05219a84f0b4b4d7ebcfe159230d21a55e4ae76a6eda5cc436e9172f6e66f239d5d3d0e5a0cf7259c2e861bcda5ecc0608e42068f8baf55c3a97d75422632a2e2220b09fd076b444aaf37072df7c07ca3bb59445d5f4b72f26578ab487931ae4651d220a40963dcefa16e26486780da27eacea7d50692ae8efe4a4febd8b97703816575e50df06c55ed1f1879ec4fab6c15a82f110d2945bdb19ae6ad27b4ce3977652d8c0f51027cf233670db5bd40d85ba0143a63d469a741b27876676679fa309c1a5806bf3e52d043e235d8ef6e22bea858ce4978b3784b276c88c5de0b03af950f4d04e3227e74bd3074eec0480b2b182d61269bcf50efa36a6e08d1bfaab86242130980630f9ed9136de3f22bc875ac2af0b081e8e54399176a4e50a8b6ef53ca0f0850814526ff86f588f8a954dfcf7a16cd321f7f246cab99b40514e197d938150fa8476f740060d9bb8b9689080b0f304dcffe135ce087a157d71c3818ec66190fa5f55650d4529dbf8b08096e4effcb746b0120cb1cc28b28dd290482c2b5860ba3a28f1b3f4bbf454b91544fabeb3aaa279294f27cf6933edeb0c84b0bea7001a5b1608fc856e29ee4d1c680667339c4b07935e06d9d0747942e4522e0177a07e6b0c7cc4da129c98aee08d89bd33972c9d984911cee04911820204bfd7ef90b47cf1d3fe1cabc8fb920d59c4559492beaa387ced1f8be933e739696d15a94098549991d50f844efbae76572da7b55184d6edc9e405702c09e3476985b70f00055bbe35b2e95dec60f3e3d1360a3e79690d02d0219eb727c62d9f9c734595004 true -check_ring_signature 923f26c6a7cdfd806795b4522af5ec0deb7aab52b97c2cf7daf9ea525fa71fb7 bf7746fe0aab45f9efa7948caaf3a36a05ced27475fa87f11b13ae75e3c10a8e 6 3def33768a1437279846e16a71bd5f81c4f8e17b1fdcfe1f09b17e0d1065e033 790a3999fb4e9d69b251a388cad7a0441008b9c7c2eb52162a6e3a4964d392f8 232a63298fc24c52c90bfb7877991f53a77079c31e370646d52e05a0b4528832 255f1545c8c1fe601a864b158025e2120363c090609bbb0b33f95792907b2b72 c3a386f02904f7b6a24b00c2f5fd56052a0776889baa1ba182b8457418db80f0 815fb0aa8537797459579d2d2bdfa879b8afd64429faee10b2ca5b0f0bfa7bb3 4f3eacaaab3552896947409edbc44a7ef4674cff54085189457586434c88b1049aa183586c85e80992106b7dcc8d273fb8ac6d38ec299f763b865d816bf7f303679e8ab0a74aaf083b1851a0342565c7d7ea81f4412ae060ace096f5c44c600fd438d951b7b2ab1553314a78b7b42e89c36e8ee49057acaca183977b6c1a7e00a8ea76a200a6c98ec402e7a8dce7b435514fb23c992e279d4db62e93464a420a840ead6936e54aeab9911a871d7e658dce1770a090b670be546084dfbdb1ae07b94879a98c174909487d7f57d36d3e8c1c97376453e5e55142fb211590bda300637ef4b7a4315e26eb8ad5dfde374369a85a540aa4f3832f1b2e5c0c4ac5960f047800b0b6fcd2088c95d7b6b49afaa602bd07e1f0feb1a315a926a7efbc280d69d0c91bc131473c9ef9e439a5d2763684531f7f2426867575fde03641077da04536319223196078aeb2d8924ec0c1b0c3d23a84ae6d1a52632bbb21f6f4a40296cb5bce19d9157f0e63c6f49499c627cafb26751d26fc88f4aa9dbc499dd10a false -check_ring_signature 9643b9170aa3c8b8265f603ff487443a084f219e6d69ed7cdd02186ade78a563 a47f07db2b84a44f6489f02a5c0ba225a6bf80483b343e5b5f575d210eb07580 1 7ac7920e31be92605735dd5915de211bbec0f084b4e4a84a69205e86f72ddcdd 4f4703bb61a884895c6f73ba14ca4930f0b5c241326d8a5b876637d55c68e208eb6d87c8d7351ca8fe59012346eb921a423accde763b8e82f685b60cfbed2d0d false -check_ring_signature 74a1e669688598bcf8cef3aba417985c3141a65ab7005ecc4634e4245be2b050 e37557a20cda2990455e1af2895866b800be131486b157ed5b06d63f82948564 1 ecf497908dccff4adc3f914dd11199a12ca9b378003cbfaba86495e9976be1fe a4c50cbe2ee8d0664a7200ff609f6a9fabce000715f974148499aba4a6a2ec038735cbe6864e6f7d17d2e17db1ebd385ad5813389e41ee48dd888df6bb2e3903 true -check_ring_signature 09eb68a943358d3da4ba0ca34503b64a7e917ea968c5d7b79c974f855142f518 08162384fd803e66fc431ca3e1c96fd60f25ee6933436c6d0eb9e0877a5053ac 8 43ed8f944b02f477e82631db4e7e070bf4b4bdb86dcdda96d5ca1660af060606 cf02c64168489819f3f4ceaeced75848c309b3879c367dd7690c85942dfd59cb ba0619b21cf4dc5f617fc6704e2ea68dfd678b71864621a96b80f6a4b6efcb2c 0075d4a46ca8c3d42b606f9a3b87288d0b9132728fec66f052dec8d220acd8ed 8d7b48dce019e558d58f8a22bd3fdc0747653aa231abed26d4205d15e44b8fa7 dacc2604c0ab5aa3cb2c8a67e08cfb3cb93ecb51bccc5cbe1b7cf737aaa30b43 ff72be9128280df116cdae50e47956b65e3d67aa33af1c860ab0195f958c3684 ba02222b617d1f1d540fe1a3b1cc60da5beedadd7ea9e99765caf04ce1e431ae 6fb3b79f132fccdad270d5239378d371154a73263f75ce8f712feb1da0d6b10fb29721373e5b2a3144ad24658bd72c28097781b49eb7c9c75062f30c8c37c00aceb4d10081972ad884f111f505aa426ac085d2998067502790c0d36c21952205a4d54cd61544943b583b06ec663bbc841dec5b74ab3df238ec2b9aa71580340ba2687f11f421c99058cc7c611442ac5400358ba5ec0ae0230ed89ff25875cb0ddb43b97170f206279ae554a8b62714ba20753d0fcd9473c924c3f11e87ece706f917c0e33d3186d3d7a9df6b3a378fa6120608b6486a9713816a83322c455801a5b55abab338c9c4e945cf0a29ff01dde891fa38d9d525cd2d3f24cb661126057e7b31ce4a00f68b8a71e1907b8ebf972910503e7c822b838411dce70a4049074c9b1f15589b0670cba4568e57709a27ccefbbcbed4e1acf914acc3aadd7580a88092e9285dc10d27759c83d0cd293917366ef124fe26720b5dd95d241d47007d39997d68b2a091cde5bf8e32d4d055a67451de13359e3c608f6c1a74bc4cf0d78edf0251ebd36a60ed65ff370a59e0575011f0ded6b526fbc8b16a241dab10804d1e4289e24cc12e9fb6c9983c4697a6ea0e892bb95f3d4211d99e909be260fcdd633fc53d6ff7c0cb1c3bc7e5a6ccdbba9131613d7352e7e26dabee7e1be044e84a849852796b8f5ba31b8448cc148271c617f63a00974c04e6bd35e4fd80d true -check_ring_signature a578518cc07aadde1ea688180b5bff313070568c139cabad7d675abea3fa7a3f 9fcb186bac81d4bdc70101171db3e8724467eaf39557f156aceec39b90ec552a 51 42739b76a28aa61cccd4112165bbc2a56e93f8a9f5f102f3ae38370415a1ab6d 66e58bfad42ec3ded560a433b859408c4dc61187d7251cba026890b3b555cb1c 5f123053d666d8fb771e340f43f43c984f5de2107d514cccce10d985f6a2ebac 174085f2c190a81aa294fa290602a73797ae4d8441190231e2e660e0dad4e75f 4296a9dfee0865371dc618a769f98d47379c2c86b1f9fdb9b528b83dfaec4f55 6839553191c8271aec28f5075a0340cd05f6cdf46586a56d5862ec0c553ff40b 96306335673d7d50714430cdad59ddfb899c57b628395f0f76ac8fdc9a7c1a88 98903c6cff76228a83b2e425dbd1e7115428bd8a5d31e21cf5a4a7042b098308 f77a53c8624f4cec80902d31a3be969f5ab4cbab15e9ca5b531ca6a985d4715a 6a49cdd7f60f181da5f25374dd008c7bae5b17c3f4694a6913c26e3af05e25b7 95a22d1193ebc84e78f16e1fca06df908ca5ed009adb9772d2d548b3da86b10b f994c9ee41c73cedd60635748e4dc950adc699196b73ec381e92d7c7a2497d3d 46ddd53f88db214b8c2cca702f9b31f3e9ba4a52ed25d6b3dd08aa7836ac1428 99a1d5fd90439962657d1995ad803b475c5e3286dca34bcd98903bef103580d4 b2ae120acf919892e23f32d5f65730d0d7b1b792f41163c84c466bd664ad9ef3 fe6cd104f670b62f3f23865d2ef49907bb5a33fc11281d03b869fefc3cf4b04c 816085cafd2fddb82ffad7be6b7b4a3ff4a1ea379c78895a18f5f3c5f4b86484 c104971371a1e2e90e8770a01b01493eec256e3f4c723a54bcc9f234795d8240 5b2c24cba318abf50750cccecbead77879fb5d7d32fc69f89a7749891d7a60aa e5b5fa8dd4b83a1e9f5cc539472255bd4550cd5e1498f819d6c71f49c9635e83 1ecf1d06ff9168d0e79860aca757444abc6ba43a3bb7bc4855df82c80a906d91 14d1ee80d734415924f19f19027d3be8f1a5d35ebac15d2660365b4a1e3449b6 71de01ad2197fcaf83aa0a88f070f956b3fa272680ba7b2d5a5d147188968088 e5f318c9ba0cde83cacab7524ab432a083dc938425b2680cf252968826ba7b19 8bd68d8059afd927ea752de14bee958dbbae739989c5ce4262c0efb82d6190a4 724d4495c2606a928303c21f8aac5e6c01cefe642c67d02e95fcbbb76455b273 f90796a050821d8d371b44f64e83fb084e562930bffd93ce3bfe3660ce51758e bd4b0ca0e0700527d2ce7e0009947ed6d6d8cf695448c789195eb39afa6e6823 fa0ff056771cf173146e3c215f9f653f28eb8fe0e940a2cb2634687e43044f1c bc07232af720d67ee2bd81bd535d568482b8daafd7060873f376e6814f1bb41b b495986db2e7b09ef7ca8c8dd66955e30bb3c7d4cafbdadc34106c21a633d26c 6d46a74464fa65fc4af8154986a73e3e0ffe847cca6a41c01dbbce7d09c9acc5 742da1943e472cd24e091f5903436617bede5519e601860833892a6eb4ee87b4 a27bd3b822cee7dcaa87f9d7f409b52ef9daa8a798d3dabddcab5d6607373213 e7ef890043b3fe57a294ec11b702fcf7ba026090dce5902195b8e31e5eeda99d 4d5bb0347ca29621fe7ec9e94a18355a1f61d0c37673b7c97a9bc653b052e8cb 8d4425c9670d1f814de43595ffc11fcde60ffd35e9911a912de9c5e26e71d704 8db3f74080255f33366f64bd3674d33da8a656cbfd410ca1d9da607794bca91a a8c0f7700c416e1d633a1dbbbcd83c61b1bc08de319ffdaf7a48d250fe99d3c4 42d5ca0dc8ea39938ccf0b2c814c4644fd2e4e4eb2ea71759dd20c1263562941 5f6883f3c6e0f2619c1dca76bbcf9c594e1927dd29a7bd22fb0aa1899e51d21d 6da6f36f85bbef6710b113626127b0d01c80842b79546e0728fa9f9593e586dc 50119ec286c11b617730939e3c5a89ef52854598499ec28981a78a4e6862700e 45181ff9e7e1c066f1e81d2d469767ab33e461d902591f5415819e864c5d7970 09fd71d3c54382accd9ace15c5deb68f67a76980b0725b31f3fc231817bf9a77 e3f753b40b72bdb96abf10c51787a6abb9b064a813ef80ef015d90f8de9ee79c 98e8853ca82ee6a8ded0d8bc5e7d75e6f12e723919a9982129ab20044064920b 7ccec3bbcecc3134ecd68706ad4d15d2367c2a2bd094026f27efb8e2bbeca5fd 0d81ae79db68f7db1e14bdb3c808fb13ec7db13e6db189f7c1479b7e599c4fe5 9e70dbb77d06ea3ff2b14f078b68b6413539f6e1365aaaf3127b2c439a338673 56b03519d53bdd7ff774d2cb158de33af0360a3a3c789a9eba545d10fd21baf4 b6faf64512c3a1422450a9a6ddc05e0804c39b18baccd494c536bca7599c5302638458a0492956c3d95e25fa9cea929753164f35d0c78b6d2257bdd6623e660357db49feb6f9a12ea92d77c04d33d9736d2a0aea83bd3c46ca4b9007c1b502073de431d4bcdcc3838ec896e60ed255b7ba920aac91be0fc333f3fbbdc0c17b0f146d235b16047b56df317486f078d9869e0b8cfc41786c51c2736853bf1ac90d6074a925b6ee1d754dacb8a5a1a693f247ca91a8b57c4ab3784256a026e1790e53e13548c801e2b2b77d322718957f7b48d4d4376026ca19ec31666f25f0c7046ec1d236ef83109b78bce2ed432af4b8df741bca33dd1a5ad4cb31d74210e90af10d8c6e8668d592434b35f65b927cfb753c61c56ea43206bc46023a5f0a2c0c92eb374d46de57c807d1f015aae2ed7af99c70c585d2302399f96b5df8289b0f098c1778b3727d9ca9a154f6b2710a34b6d104177e50d196ecd6346847177e032ce83ad5feadc2e413ef11a61320546b5630f366b48810037b5158803ef9c10f18133a47a7600259b0a1b92c9b978dbdd2d8ede8865b62daec2a05177d3a750a4744ff39fb3d3f2f5bdf655658454e5807ab532da5af4536dbc3620348c7eb070686147d149080433319d67bc5c7b42560de7450acb5b7f945c8be59df671b0d09c490d856fbf41ce3ec9200401b9563e7f7fa2369707856839edd18d179e70ae6d807eccfc44b925680f8f97e32e872cf02f5b44eb17eee07bc4fadbb62170b9eeaa94f88dd6cb0fcf25f452d2b7d94942bc488297140de39544eee6cac720ca86ec7c60d98d99a880825fbbc8977c0e6811df3a9bff270217f30344d07400959f2d305d219f594535eea9c38e4353d088bc91ee833a60ff75861b939641708e78e2a918b51851667ac7059bdb9d7535570ab16a78df8f8d0511d5f2bad99069c45515d3d7d7cc7ed8206db7b34cfb8e6e58cdbb3ed08383dee62dfc7d27d0b64672c03f4ea90efc1757f71e6f483b04d436c1da02c4b41152f6460ab1f4a076d1827fa4e6ac92a298f5d7f470a0c7dd10ab1b171a06688a3edc23afaddb40e7d8d29d4d16c258e11ceb4fecaaa5a14720a976690f3ac9e6da8893aa394b506e34667f55ae4e2a46d0401ebc1e8b6ae2aa8cc346fa66234ec5ccdf97860580af864baa0f82a6684d278f9c4332cfeea65b402f3eefdcaf2c03a5174c8d51109a5a4279713a5f0354e1d05c240bb5eb8441f34e8dbc5a461689734775efc4b020967f23672594bc6ee8254008586c33d3e53a79406f0425c33ed7aaa673fbd05229adf5bc36fa00e93572b0d131f3de35a9c29a0e9d8c74f685a96a26d72bc0138ffaa5e4a4ffc2f737c8c2aa4b2459f1c1187ddaa261eed940437c846978b0117d56606e379cd7d0b9d8a8b750df326e6420436e7621404890cbe2dd607450e160b73debcdc3804e4d4a07aedcb8636d0cf02efcbf0a912c06e687929e00a06c578cade93adbd6b35ce3980db3c9947c5aa5faaee546fc49e81794a9f810a02582810a6ccf889be286ef18ba85fd74d37f8e20c1b01f9167750f0aeabaa360ddc8e0d36006c0f7d4eca39571f81152d08606ae7f1d2ad0856f84c174ac9ed0e6470d81275d7d597f01765cbe968c4bb336e3c88a7f3cec1ceb10aca50de8f088e8da3842e61ddca265a33b25cb41b6d802607262ead0775bd2b857ef767810919063b7198d75d63c8512b48b1217ab629f164401b6fb31ac6faa07fe1887906928725ba22cf5f5f4e03fd9ed4206ba2f0ea44ee2a9dd868147ff69898edee055fca543542366d31489090cde05aa5efe75c9b1074cf804cb26e7e41e836790f6981893a124a859151e41313b64c86565829744a972f54b520fb47f84973e00d2d9280f12a3e3ffbac6f59b11bc9f83e13786d8e218f0a0dc22f1887c879c70e33327aab099462d4df0cbf7ee156e0a74e179284b31a32545794c6c6e1354a0a257d0582532bd998a5d2b48893ac4dee0aa65b811a673539073805f2c2e58c02b0f759a69096f624f22a489131608b204aec32ddb130bfb5d6e964c24251ac061c90361369b7a94a6367e483f7801b91cd5cffdbc4fbfc796de08b726b5dd3009fbada3b404c3f4320a28bcd6393be5c7b69179f14e3d84263f9d890d74ffc059c4983b2e630685e17851667105957dbf92bc36f487594ef6e6ae20961e4c0057caf62bddd73d1acf8b79273c45cf5018dbdd040531f83d687ab9e157ad1d1078b2e69859f6a66048cb109267be6386e9c2282ee8383942a42841fe93b9dc500521a78308df914edd8d3f5af4545e303baa479155dc3a33433e6d96217b7dc0057fd74791b92760ef4703ac6b2a04eea1519a89c01cb0a7f56c143d0e07fe804a8afe56d2bac677f48f53c30001efbb2a34b43663d7a6289209d21b81926460105a19fa0b06d9e0fbc5801ea40c3fb8f7733d71071cf0613a119327163802a02210ee40e4f19e945d4e3f3f58a1d8164035f5fea5f90b188110ebc89ec060c0e495cbf82cce59ce70599e200f05ffd008dc7a4731335b0bd004666bf7befa9023e4ab14c0b53a0e22cfb523155d8483e8b716d9e0779d57e81ed31ee0018bc0a947c099c653f4c239a867d56454e11d2c9613f7c8b4ca375f1548167e2315801ebfd94db5a8d1ea14b807f1cb3158fbe7db1202ab695eab078b029e2dcb2140fb94201f0001d3f93dcd33595a34c22cb4af3bbd7d22acfde69805b63b239c304b1ed92e64088695b9f5828d61eba3e92b0a4424f5a4fc2dd6a1f75a0142f58096bb5dead0d42429baefc193f04382ccef0c35160f39c45b79bec7e0e938be70fc2eae5d804db51496cedb751cbc797d258e292e0dc266cf4afe8b51ccc3e6802399405b9f79b18756c6109f1174d2c466b6bb5501a1f78051e75f3f152850c0d5389fbdc3048aedeb6fac307a417e362758de2a2b9925eae3debf78f7575cf045ffad6e5fd92773ea6952115a4656b73ccae8502c8ce94535a08d25e99773702a0dea2cefcc06c239d8fa1123abce4962aa1eacd75e537613eb683105585640daee01bc2b514adffe2a08373a359281e4fa5390c4b8e2505e4da660bb208160a2c70bb8503daaf0fc401f9ee87c35eda21bf77546feceae7ad07d2085de5ff09f9ce662702ef657910ae2cd6c1e41e75dc38fe5a97265ee683f301be89a88e0002e83f47d1a9947e3ae16d29afe2d8b1d18b2f2546e2c33c95afadd01fc0a20c5ed59dc08a06b2103be052b6e476e3eea2d24e8c11854d2a89e721b5e314e90b1071e010a271efdf8252525b7ffd483fdd1f4e1e5b922b38e19aa83e37749e0ab5461bcb360eb1e8e63c1f828b78a41f0923f2a42c0db980e55b404f7659520db7c37423c8cc497cdbda7d8f20ec37e4f03bee82e316406c6517815b5b4e600877f667bc9568025f227aa2a985b6bcb9d22cafaf059ea95ad277c0237976590eaeea8218ae1981f4e5e79e0f9dc72933dfae1bd3bf27aaa8e51d9668c9580006151764de884b1bef855a76f6d171fbbf109bfce6dabfdd79afb60b7338b01b0c42c831520597b3f396d36e65688150c1a1f4a58cdb4b84e1a3845f5bd9771805858f936130bfa9122ab4fa53f51d93ff0dc1d953e76c689717708433c9909e014df1bace321f4b7ef92e025df3416bfb802bfe67c96ac151081b07e145736802e4876cd54043e5c46e49fe5ef35452b7e8168b240df25fdcbe49dff7546be9054144af71155144a310d6a0440b84fbdfb353f69f8711f607f8d095ff6d7a550d44c63ca204f06900981e1bbfcdced1d3fb6273bbb9c81c2c3653508dbeafbe0a8a774470fb1b2babd18624023ec8e92b17576ece2cf306854778e78392dd1c07f1eebbea869d8bd20b913dd7014bac34d737b5caa4aa5f6aa62ad0f8f816a900f78a95508de5ea06dac52c50fc5bd9286f6f2b534e899f1c77ec27f3bb2e7e0cff9d880b05a312dbb5186accc17e7bb8b8dbd785cf4a75ce06b19470979a6e06ca00ac0781ab7312cd43ac4167eb31167ad8ce40442be794f2151ac542c12f0c7394e24fda96c3abe42d7dc2d056a7c1deb0cbae4f28ab7ccd330afd1351b00ca47db09eb93a3389258683208b0b5fa2947266aa4c5963dd28187e87bf48570733a03606e26e1574e2765461cdd701ad92243a57e380c67c579754c09754510466c50f0ee99b7405805df694a69a0977582f2b31a2de0768c6df7f16c9da8708d87b43bc68ff93d598006f1adc94f5b03f06d2965d0a60a560b425b7d7d23703f159d130b6dc232f4b23b39d7de2f9ab04e3236a516d5e4f718ba6542994ac07383528c3d500830b5a704b4814b7bcfbffbc5041461e0d2cf893de57842ba30fb66f68a60b368ba2d93e5933486e924a349531f3dc0872657b47d3e6d13bac04b554373dee35ac0c84095bbaaf437bb77923c2fa6fd269fe756d01f8d53567095e8bee6b6c5dc307833dc49777833a8bbeb9935b57b936bee406bb50a67f120a66ccb649f1cf76bb6966d86eb7959fa0ff3283efa8cad208b76e4bbbcf387b0792905ec8ae020e399728a80a077d68956a4d624af7dd0fc8cd9737cb9f373403 false -check_ring_signature 87ab27cc6a980cbf4a374d79cf3482c0bbcb5dc6c9cef726f45e4629235f9864 b2e6042391020dbad70e5cc1e30735d06b53556e5ddeb478cf8821dcd67cbbbe 22 d17219bbd50d2e5178587101a3a94b32ab5a225aa73d54bb1819bb6bf307d80b dba3f40dba7252bd3886407df65e5d18c7d8f09144fffca8ce2a295c2902ac19 1809395b73b6aff3d92971e00beae032dd98eede419a28236d855f62771017f0 ca7120708735d2955f0631c48d45ed3ef8e5df57a95fd1bdf1077d32d3d718bd 9a97104912d7f5c0bf8d6eb85b4991dc138469be6a5abc395359097574c9fae6 8ebc2dd55bc9c22d3a6a353fb94a8252e4314ebc2a4c268e122169ac9216a44d 8b61e7ebb548c0af4e4e2f3ddd48090d96cf231aef578b93960c28f25311514b f59ec8f25b1950bdec64c7454896519dd0c892ac793b2a3d2ceb8c06974307b3 52ff00cf1ef6ca072a857cfc45a7c5f9d93c03a6d653885d062c7627b08d2d31 39861b9c8c707e498f11e3c8ff94793ceca7ec876b3a40a18326681f2c23c927 3dea66e978dfcaf0cf8a5a70fde33297955e88a750ca39a16696ca79ff6fbecd 3b9943b9ebf3e4b05a8f18d4d80b06acdc30f263c335795c71dad6d40f384f35 111944ab6457d73bff5247f2932f0c13299763b694cd3c597c86472beef2e644 e6a9fab5f2b9fae476b97413f7027dd0bf22ad487febc1186812913d4e484d5a e3544bf48792023abe59cc893e1890945e5359c1b37f9c2d0b4955022dcee99d 2b3fb7e7627aba1af6b8375d3363d32e70091831bdb680e18f8a3b25a1c2fa3e de8675d2523de939efbd52e9be3dc0d8d1fd8fd2c468ecd6611336aec8d1dd30 454f343069e7ca860e37673f0713a06488a1695e0034573fc6d2d1ca6037010c 7f90105d4544caa4d2365e7659e0dae3ec70cb13ca696e9bc508c93a1f0defa0 700dd5caa800cd7ac323edda07ede082991946964d31ddf07f6d1989436fd349 7bf17a9f0942395bdd58a3653e1fd07ee4bb733d1a6934be01291954cc7ebdbc 2668efb01418655c7e372fe72d174b253049989719df7683d66ba7a4e9ef8d11 671bd757f9ac28900690076dc9196fef88ff8fd0c4e52d386429f8d84eb5ab0594e6a135fee86d85ffe351508c8c401e1cf643ca8026cbfa165c0a43eddd6f0337a274f828f7aabfd9c4e0dbdba6f29f18400d85e87b9c541debe7be6421920f4553e9023d76d0f92b115db3a708c9fca6b9c2a8a4436af72f9955d4abe4e60b85eed7aef7274b6b4c7d4bcc30b5cb2d64cd4157baa8337e097a884ad9a96d04ece2511dd14d697db0ca77e3dfaef1c337a4d7d094b45ff4c921c7722e71120c455454fda2a237101e4360d51a84fedadb10e8a78cf2fc1042ab0f1df8229e02740747f8a58b802a3a2a196c8f502fef95437387c879d5af86b1842ba626e0006771cb533973fb9eda37385738e2939bc4c2e861a31145172ecb66b337190e0c3e3b386ef5af2c14865b7896de96106a3d570819865248696cfbdfbe03539108a917de03e23c88238b3132c6676f7a6257d7afa94dee53e81d73026edfbf84037628da420f560ad24c091fa7059bc07965aca4104d8938b45088a001ad83310051106c37861f5d3694ca7aa2d2aaa303fbb7e276e55f93bf45486286e765890ae8a6de6b867822fff32f8505b3090edf038b0c3b0b9256c9f647ca026ad97e0c42ac4eadd6498fd5137b6a3b7d2ac75f453906e86745234fdc962b6d6e5df50250ab3c3d6533e7f4ff1b610f0737a4267b0839a6a8ca63b61e23004027bfd60eef6fa1b4c8704aea7ce3ea2e02249298b277e48339685c314f8346ca1bc17709c6c4036796e56cd1d1a7164f5b843a4118288cc5f27dc31d9ad57a13be37b203d2e075649f82d422312d1aac8030d2b31b2f9946930be707ca03a1041666a401a25254b0cb97e98888a2da2add939bec159eb3cb35688524905f88674dd15e07b293c7e22b65a6238f2152485b9b1c1055b49b5864f91f57860afaaad6ab0b0172caef323e3c75905a5b498ff90ae8069dd65873117fbd340d3e7fc782a53208416144b49f9a98e6854117f9c16aea69cd5f9553881b5ca99592bff73a7a5d0a6e39c943f1706afe3e97f7449e76f8ba78a97623d5e397868a444095dcbbf306c65e71d312c9b29a91afe4d8688cb1d22d30bfe1c03a94bfd92c74b33fe9d60506b445441bbdf372842028e37ab1ba46686c07501b9af64802b014f32fa1110f64760aa66fd72c9f0a093c767d53219399bb86e0f8f17ad3fd95dbe10bc4000bedcb5b611d676dd4542f38696730ebf61ef344c7fbdc7bbb8c92016bac63f70b87ae21bf4ebb6ac3492560413f539413d57ff6855e328129c4ca6016df3cba0e7e435934d9ee1ce0be4cedeb1e614bb5e5b450fd1ce508db4999ec73ed61db0fdab54a52205f43cf8024758b050c7ae6d15c2e515922c18c273bfb00aad4be0eeb0bf1508bb0c313dd70b6c350a446641ea44d41482529621c52f5a208b2790f6b6a3421d6c3c772f85ca02add0267c2d27abe84a8a2a9b198c702ff5ada0ffe4e583bb102e8f95763212ba49f02668023a97bfc755031481cf84f089f6e6f085dfd215e342eea72aee101564bc2f10e919f73534ed2322d2514bb3dc051420b2ffa72d7d04420aa2ef20d8ca1fc0020b63f01cb7578278d25cc19e9a48ee00e3f7f0c974f415d16b983c6e2c374e8648fbcc56408c69e38dea899bce4767508a0237ced23002712d67040025576d62a232e7d3072d063b97bc84c581b04de03bdfe1692be9372661cb9d62f23b91953635f04c82a6974aa670ea0a7ed98be0181656b76e23d153829c24c82425683a3954ad6b88682c9e0fdd929c4c473710c2a6395119d22d3f1c2e9849fd7a05bef2dd8f4a35478d0164269e18ced112a04c9dbd8501e74a7bf92e49d7c5f83a759fc54b6f899dda55968d7d97c29acb70089b13f5c7253382ae5a5f35e26a8c42b72ecb9427d90bb4ae1ec0120b710a60c19d504a92518d3fbaab3bd7fd1f7d11f8cf71203021a29256696217314e2bc09 false -check_ring_signature ceac4a97a872aa49b148ac94878b237f2995b52e421c140cba8df756e799f648 d39348a73281ba8011cbdfa13dee780b243ae79c8227ef6cfd92e2e8483f1009 204 f604342599f3e0ee59d65eaa3c5f67ec87d5bfb55ce578fccc4ebd0d4728a53b 4ebd4b915c31e0ef3d8b9732f2e28906e57c3db6f20e6289c39ac97609de5bb7 3637857321a9c77e729e8dee946678e80fea9c8161219a56abd55fa0d7dbf0f0 91bbff988fb8a5819bafbfaf6979c54bddcd0ee3a8d14e1179bf8036fcacfcc0 ddc534212fc4e4c05ff55779b5eb3701ace527cd002e818edb24a298814459e3 61669f64a804ab1cfd29f5d7759647379a26f60ae33368eaf94256147b270b6a dc114a86fadcedf8ae6d212497dbb3e5b656af5da4eb84e179abee2ed6d44dba 02c790dd9dfb4f572f6ba38999c0331f896e0390129b4dcea7f56d2fe7a8ec79 8cda6e03e427bd835112648732a46fa884cdd6d6d414d440d245e7c75a81b3ae ade002dfd78b0e1ce46471fc9cc6db6f69ca273499bba1c7653c0311083b1186 07c62518d5e099b59b0ea235fb4b7ab174298cc1ca4dae1cc60f917210e8c7c8 cad1d546c54719c81710b0273bc5fd343e02b8fd5e4d1d8bcdea8b51a8967813 60fea69d0fff6048fde8eb2492e2f0eb375662e7d647e3bdf4fe66e26204392c d6bb38d8c0a67f76004707189a982c1dd558e9523daff93bb5680018f121312f 665bb16d688303424f02656f46912ad1f744ffc180a28d54ba6c479ecf2ad058 1585570bf5b4510af914ae2a8f89e71488bae39bec0b01d77e37e7da932b57cc e5535c5d89c1a2cafaee58d0692d2b4cda063821e2434e28691cdea973a89652 0d709707eeaab999a424909a48314161d6b23341cfb9165b10856489d45236af add7127dffff6dc2f8731c8c6b1e8f439844970dd179161ebeed7d5adca7081b 7c60480b2e18c94960f4b43a04a3d1f3d839519520f24c0100733e76f6be8011 b63d258af3f2927178e9c3426c696a5a8d13d9d6b3b8500306ee7c9036c7f312 0f5cc49648b7b5c5b4195db5d6fd20fe3185e3c980937a3b6b2af5fc38ee14d0 3ac1502e969a529d6a96f2954566722c90ccfd3c72304edb4f31070ec99af149 fa7651b7a656f70e9eb4ce348b09bcb7b49c96baf8cbb84a7c6844272a13078c cbe3ddd0d78136c99a7d26eeab1fd26732f0d211dc2870a0d5a151f95076b4d2 abb43e59419d092cb266a71a3223707dfc5e85ae18dedfce817e1c4c26f148f1 1e7b523d840c1bc15624c4d9cfa7a07cc3fa5b53e48f5b73bef887943a997332 bfa7ce577ec67e2e4e97d9d30dc98257de842dc1835bf33cc9adbd5715ac255c 193d65c79f25285ebdb48e8f492944ab7bd572119c857970916a09a1c9b79e1a fb36c2cadb81144c959fb390329ff8b3b0ceeea5bb53b8fba07f305f3fd33b08 62859c282f23c7539ec47122eec7581e2e8455ecc89d3aea8c5644da9c45cc63 cd41f0bdfac6dc3885193bceebc49cb017c2b4f1a033e498f0b4275f3270430e eb8a3fcbf458c00a053475791d29af19b8f4c4faa0c2f2bd392a24eb3f185271 c01f7d31d9cc7224c4d18e8029e17394fcc9623ee5a2dde3587d87d0150df7b1 50a6ca93734a03cbdddb998bfa7961ce3db46947e3773d0cb0d8f7431b4dd523 fcb98edf8a89e31a5802c6527ac5a0fc68c59436a4e08632fb367b2f56146730 de432c1cd3a3be7b594ca81a4994d60b49c8c6d69be3dd46f5eb039a06e72373 6bf8062432d12a6aaea4ac66c6e5a245cfd412f443c521011b917597b207e015 41096aea8310358c3a97fe9fb7fbc31d86d0e4c0d9206b9f439aed2166c3dd80 acc1707a25297db78134d81a7aca14069d9f1b436cfa12501014a035b946fc71 8024c19f8b8b0870f9d06aeddfbece561cc8a8caec7d95f931cb7a35f494dbbd e95288b6fb90b07dd66c2eb51621b2385be55fd1d894d36b45a1b0ff5c9efcfe 752c432c3e235bd43df35a279d15ba97a4061f01128e207c3db5b3dd9724f47a 033af94490a358b571d49c6f55ea34d842b2f6d8fad6fd0105c5246ab5880b09 248f78e913e62f31c23203043e696b1b538d241b9ae9c683dd6a6b934e6f7ea4 dc3f5c1e8ddf18eb41630b7aa979174022a82edf6cf2988ffafa078e08352a00 06c925caf0677422d09190198b17295e9243750e6ddf87657df3d6212a67f358 ecd5c998bfaadd6469c599a98c3419c7549391d2e1aae567f1af7464209c811c bbbb3240136065ee552ed093a2d331cff08c469a7911b94c88613ea1a5147201 da9ebe67eea9d972d36a9a5a67fe9b0639d36f778d790817b69bbbb49dad3869 d0f5fbe613e18f4529c4455d09412f91a58e098f7e8c26265d1da644a498f101 221f0a971515d5d518fd08e70820caa6810db1b2e8e67bca86fa45458f9cf6d9 cde433dfe5b87676a81584a01619c9de70ada418e17ee884427aa3a1b726af3d 2464c630f6f06c679fe04eb102e46c8a0319439e31f98099d8c178e6299227a4 4403f38f36bfcd864a738daabe408ecf2f7d4075c9257ce27cdbcf7bd1a0016f 17c19487c46ab6e5f362ead0bd00aca736e12cd7191dc4450c1eb4d07f5ce613 bf3d54a179175de1158ec55e9818c0913a89cf1979d21448906ef6086ac38e21 dc04a58105728c2c34af78062664862e47f348fa0070ad9000a2435a27ce1588 9236c89bd6ea81c7520f3c9880c8fd7511f71f91764978d4a9075491c730309c ac9f87cc839c79a9f875139e664cf136643c735b6336284f432c41897a13415b 0ef714847fc67bbe8c58b1b29b96f5a3cb93eb19249bc222a569a3fcd824709b d8a563982a1ba85941a5121cf2afba8c589ab988260e4ac1e8a88b2cb6954f0c 71ba2c4919f404bc54cc1f95b93500e25462664b4ac1194b7f7c1d08f6d1398a 193271ac4c4c3402ac438d5413ec76856f16b7a0371724faffb1f9d9758b90a9 2e314af8f828ecb7dbadf56b15343bfd72b02331bff02936473ffeb01b6b5e1a 003a50abf77a8b6e089a507d4b10202789eb2c29dc7c88689978d4ca512356c2 b9d57c1d47fbf98ae63a510d77dd3e40c45156007e09f3f367d2d62e58f47261 b51e0174abde7122719f0f798f61630ca1f2e942bae5f56db2d049724135eed0 eaddeed669421bfda0f26e6e720ee62eea7ad2384ef83749b653bb8cb04e5948 29faedabd93a5c2418bea1610aabb26fdc9d2b50f29912715900abdd98952831 a59d1cc09f3710175792f8bc1e4786c5964c4fec3a38e3d95b2850988f480ae6 bc822e1ec4c55e38d56a5459819c2b8e1126f2bcb404bb8256aa49ac8d17cadf 70f2f77ce57d5928f14eaeb64ed1e9c5093a680a4ffabc615e28232e2ad56c81 9fbffd77186320b64c6202e2a3a29632005221c56733be61c87296e3d3ca7640 8914fd6ccd6e060dedba76e50c8652d6bebfb08089bebf1e547379564c62b027 9fdc384549744d0081fdb4a8471c91a878c4b70218e169cb5fbf6bace619733d 3835f26f2db998249972219f9a1b6562e86f84ea702fba24bb4b45e953235e99 41973abfae111a90e905dd96ca0216dbc582cd2d8597e87507ad8586ea9d9b57 1ddef820025fa78a4c27755101612852bef0f52fa1e35cc69910d3d0cf79a89a 6d7b814ca12542f6d10cc559013d21bcabff34204ed62ba40c4d648a99661a25 ef9251853872ad2808a7926f5721e6be2842668d7a0acd55c8b9fac4b3517b68 63cd4fe73a3e2162b6cbebb47965852adaa1622010725cdb1ec42408ac72c008 361e14835719025ca9d7f31ebd66717dfb16fbfe17878b52cb02749e03a144af 34a4011ce5d3748c716c93776a723062dc0998bf25d3f995cadfbaf50228dd62 0c81859f11b253b190999baffc0d3536fb7642994727170a941e2efc6e06e57b ac07d71bf6ac2888df2bc5f2d50e808ac7f66f1f4b8ce8719a7d367e05b69f22 b238422861e3916a442ea07e25f18b6f426863b2f4d3fada33114cfadc4983aa 361ba6c5ab2ef9dc4c2bb26f71bc19fa277bf311e256a5cea71f56839c6bc055 ffd5309c39bf603cd929b79689141fec2c840b83168eaa29b591633825951e7c aae000760cf008c01adb7bd23b1ea4491b51aaff5b1d5c8718e39e1761b51971 3bd88d32ad2daf65ad26cdd25033c3f5ef5494d0e0fe9eb5f7e0fb4c1c85e1c8 3771c310e4dbb161f0e99565268cbe237eb8beb48e685e83cd679e5a21f2bbb5 15f9a7be56b6a0c18ac9775cf2c19247afb3018d52325f9eb19d65716d5f8992 e921437112f677dc3283e55ca6ebb5de50d4987efbd0698c790f531c5aa03ec1 7c31955ebfc97dd6ef1ef509d4318a69ad828ca72e413aab0229c0c01dd475be e275f2630ab553c494ce7c1c5d64e2ddf2d4d90697616ece48f45e022cbe3ff8 d602471c1abd175eeb55c6cff99f3e4c632e86f953be6cec1d7ccc5842c42cca 63a12a6bf9a1dc58f20a71d18785d99f75eafcd938533c0b2fe6d2c1e5ce80f9 0948dcf0d912f0ced46ec4ed70cda3596eef6347ac48ce5545d7a7f46b07cdc0 1a4f1025d613c2caeff8f552d108cfba3425a42750f991ee649324e59b0c6fbf a34a1169544e6e079664cc1d1f2ecf6f3c97d423d88f584a5360648e930eeccf 50c8aa0aad97e27dc4a97d512577959755267eef3ab499efab679d9e240f4716 747c2d6185c237f5ba12cb77ea33da0f5c3779ba478e7a13dafe635a87bea761 be07f3f90a5e693d1c63b8f4a58530ae0b624292fe19c39029971f3fea463999 f7087a760318d4e2b8793efa39196ed0d11f67b34839fc29a523ec57d88b0218 554799e79b3cab6f66c094a85e14e7d43a9465ad8bc693881b06462a21a15da2 a444503ae2097ff626709df3ca7d14a8e5dd00e314ba997df5b4743148fb5e85 47f9d3e28ec206b4d4276401005d01f0698174dfc5e0308b712f0a6389f39e44 8f05b118940cf436421cc518d33d802e92c80c5cdfb7e61517c24aafa9807092 c259a00bc88d9583957545e026935ae9731a41c626ed31a681341804a12eb8a2 9c9bab624dac55eccb97d691d06c117e2f22a64fe2e861b3d422456a68b2670a acaaacfd17399b4a89ca19c0b371676a81b9a17b75a2faae8c0771e7d8af9307 59c43566f174ea96296d6f345af8aca00a152e9a8964621c6d48d8a389d8b9c9 c825e21052d083923b3f3eafdcec959fd92717dfd6cf79d883a4c1b2d55e703c a30ff15cf24eb21f1c910d6461bbab58e8d136361addd8ed619967ec55639fca 64489b197067b176ca4aeb6eacac2fa048eef9e891580982402d270c4cf0abed 5d8bf115de3849d0bd74b203650449f0ed8294d07743b4542f27f2be67fea0ab 3a543503e366952cd81502b356038bab89ec19222e101351899dbf324e1720a4 28cd580d43c881a267153a17b997680e306ac7209e99ae38d10346a2a8f00959 527ff468c67400a33527cfde146c27cc61a6bb0a5398b0b28dc0640afc636495 c1986d84fbce4b1c13def01e8ef230f4bea6ed55eaca5e70159e5063cd7fc096 2679afe926e81cd3bf8fa02c4b950b0018c7c373ef7216702e374c16a3b84440 88d82d5e5fdc1da061de8baaa2bbfc85334db5ca29737b668b9c743a27062505 ffe72c5a559099fc7857afab7497cc93017497bad377cf4ac53933411c710d6f 5e3dcd96fd8b817f5991b5d7ec2174ccb0c14311dfd70e07eb1e8a9be2378ed4 b2bf4c1733342eb04af501aaaa994bc1b0d572dc85199c0981c323c0f6f5deed 82c14a622068cf54888d02cb54835c668886045246647ebc07fb090a0cd8a39b a7ae49ca6ebd7824b9a7e9ef64ee4c27b644b8684a7b15f93c102400014f6058 b85a0236e00f36f54c8c1e2df4d46d768547678524351b1afadfb444964b6f28 eabfc33d2dd339fddbb1008f20901b0e86397d2095d0201e9651c95ee83a08d2 2637662741ae3c79417e9d0353eccdedff8e7f77c8306218f9133f71b1295d9d 01d5b967c47fc58bbc7d94f0d444404c4b8b947d40a0a29f9cafbfb62e307d7d 34849c4fa735be27ccddcbe46d0ce12e14cd711665c5415c7dccc78a9b3af7f2 f890fa4702ee235929f5049734972847afd562b5163efc3415a8d1cfcf537ede d634752c52939e2acced35013e8e4f68263ba92814aee7059c568a017dae280e de9363b46f284d8510f660d4c86264881a6fdd2497cdb61312a4d337c37480f1 779c007da7cbbe1f37779a6770c64f3cbf7c2db04d880a944ef3cc47a11e7fa2 afc549dd7ea15e8e7bd948e3e3109f3075b323ba5b77e325b3d2368439e3ee13 acc7ba62cba93d196604234839c590ec2a9f44e8ea2d4ccc95edb0e5e6c6ef60 ffbc311563634702e4fe3ecdcf8cfe791e44bfa842c5344ac82807dca08adbce c93d9183bcabdfc758dc761ffefc01e5ae931b861a2a92f17eeb12e305656d03 73b87ee6f37448beb5e548855154ef20da56ed2efbb7eeee430bf907c85f82ff ddab3ace06fcf167151317820d28a4e9d9d63de8ea2880f1a245a07e03de0da5 73d5408ea0e11495e204004feacde20e321785227f0810b756d228caff21b728 2527e1dade4535df9a42e7d271fb4e12d897cd5fa266032f1c46b0dabd2b0d67 b53b3c81d0d39c349db741ab2b9f8db3dd3b598d01e4ff038851d6a1a3ef26c7 fd939c0031505d05841721fdb677cff7a27efcb0753f28db71bf15b16274c390 63cea22c4ed719c54ad9800b79055b5916399dfff194568f139ba662cc9d4f15 b227c5219ddc580d6a09ac24812adb648f943d682b6044a77049e60d41e53aea a307fceaf1f2def6a822cea0987902d7caa1c5057ea145cee4cbe29d3ef678a6 ea72172b3ea097c8c2be7f922bbf0e0d59deb725040b73cd13250c828755cd93 bf7e46168d9f0bdc4d88d04bcd8d5decb16427bc768a0ecb86cb7c3a31efca0a 0e2020e31c6515ddb61b20bb4679163d8b4f1c168b3879116c3fd8037ce3d25f fc9f7b31c573e56da4a1576c97574bb4b08dcb98323f0c868e4c96949555d0c3 a69836b2bdf06fd14f72dfbfa243f3efa4e2ad7c886c800e845db2bb9313ea35 fb66cb548dad9bf5cb457ec32c1403dccb6e4c8a4b1f0cf5d4e3034a4af89402 ec604c9247d419174a6006ae148caa0ff2771f11bceac32264e14df5f9f49411 49a10bb097cdffa2b0e685bf690f713391bc322b186a1293590c13cb224481e8 cd7309e3c00dee6238bd0829db7f3d2272904119b896d6802851063aef0b6648 e4bc932e9f634977cc1acd7bcd65f879bc979bb5dc63fe913d0e2d23eed7ee3a 25b8382714dd2822cf6f2f63144a1d952779435875b9702787e442fab2edcf36 68d03048d1b1244ba2ecddc01c2848e50328ebb9f1bfbc8b5ff6465f786fcd63 2e20cece8a0c8c4022e17ff7882f7c3b61620c5bef2d7b3604c139f5684eedd0 97944ac6849b8af31cf8e460abaabf1019fa33e91af74af06326c32161f1f714 572290f9937ff4205dd7698675bf57866acb27163bee85b4d53662a7b3321adc 4eb698b8d66de693151907d57906379ba89971be1df4c3a0f756fe945f1375a8 473ea7544aacf320b6aa901661bca8bd8d9e4eae717e4ed75ecb919be738ce5c 8892b696d88c8af8a511fb227850201ec63a19c5263b7611cb98bc6a6e770ab5 63e9890151fc06371dd475b709056eeefc5451cdfd820fb976423731d77a471b 78f6d6b3789a652bc79719872aad8faa67241d69cad65364d8736f6e09816bec b92e5f160b7076a4a29d244a425efca86284c927f62cf72ba8080674d9c65f14 401171856440df887038b5128bfaece79ff618802b50d8492f3973c31d8f33a3 c1f266b46bd303d8f5a1833790a10c03b0794372f3312827a05f2447fc49ec05 26aa894565f85b408f34391b587f58873253e11f78aaf2be6d13666081a9041e 634659d439c506cbd982a6a0bbbf5aeac827252e1e356f474c5a572b4a64e20d ec350d00e67f78fb1b0d74cb130bcabc8709e1b9a405f092c8741680c0713396 fbe60943aa6bb5b0a1cf250c85b5c00da93a358e98c7dc97728a59c61cb3332e 78f0e33104b3de6ee8501273e14c52be68d68dfeba07508c4327bd96393c36d0 d51d70f3ef3dbe53def97ef36bc06512713981dbbe3ea13a22bdcf7038650e0e b8bd5faa9177b93f898f2769f31fe6afe544a7dc5debba6280437a56f26ad541 0aece4d1123524e280db6af5e033f7c5e0bb39cf2097d477cd2f6261ba29c73f ebfa4f6af74d1774fef336c5c5be623b043f066322c9eaa075a675f03c97762e bf7f416a637a803de9c2ec68c7113f4b3d0ef66cdffebabb1a7aaad68fe4143c ac49b3701e94cd6dc27d0224afe0fc0b3dc7ad9b18b21a45fe99f9b113623962 856db3fb4e19bb2c58088b3478133fde5a7c6cafb59c2d7f39ad88d1a3548562 16f3815c4b711d85aa00442075751c9a474d220476be7aa0f2c91333b54bcaeb 046b30140d57e61f0af2ea577881ca1e1dc4a98124bf9ee5eb6b067268f6af21 f8f7a43c9d9063386bf878fd2e56f914b1a63025ba85e1bed833aa363954ff10 3e8422f1e04c6386c777e37a8895306070a0e908e8efde096c96b437893f2b3f 1d6f4b9dbccec8fbc0637a9a90e10e60d3ff01088a4b1179fcab53b6384a001a b8b518dddc9f38e8491ffee38437d3ed896a821933ee364b53a0159e957d04b4 41c5b82014df1ae54a9aedac095df2be4fb068f3f09eb66edff7b723a2c63f0e 5c036958a90b8d7cd054c04861668a13e049a1561764a46800e37258d62a980e 365f94bb8b1a39dca3e4b2980328c9cd733d6c7bf6d26653950f5f5ce357e9b0 40a49a70d051d614b7b95a717bed58edd8ade4de3557ed367e88c517464a1a5e 97487382def088cf216462c0ccd9032adaefbfa358478d7731ae5231593c1ae9 0964d84c40f97c29bbc1a1c65cf2f21076b207f101618ab7fe4de907b4b39ebf cb845e0ef05c29af57c147076311b2da2e120fc2a5fa77f861273bbc6d2736ed 2e6abd048c1691f7e8e8b3c01e06ff94112401fbe3e0cb02e56f27ae3c76654b d78424ae1b71c5a67e15a4427060b8da7354108494e14654b9c6b8c9f97e15d3 bdd2ef0a3172617bd43ac1554f1f7715675e6d3025dcc82b8f98a65925e89154 a8dbdbeeb13352af5d8a62c086046ee4c6725a17ba7b8151e876a01375ce1bf8 28abba0b2121fecd55134017f75dc321eac7e654bdeafc2ee6987689a89f7a0b c8252977c64027240943430a38b6811e774b76ffb6aa1248a10803992e931136 82cfb321ddaee74d942ba3395ae831a78e03fab2db7080919347cc78cb130107a2093d79ddd8057feeb085e751f2de802da893f0adb43b63d137cd9c43eb160732fc72e2af127a48cd5ae3fb6f515bae5ba2d6d0bd6c18ec9d03b3a5dc9733053bad97dbf81a151dd6d163654e349ef3dece9279e8742dfbcbb67b5c9e49430d65b3a809a5f3915434308b16b1e7ce7d06f673765d2b159e68e8dad8cd859e082ca6743f7c70d6f9efa3b5385fc245e440c001448f49bb27beea4e52b070d5009f428043ffa593930d0bb51be63099c9593d04b78e250285788aefc54879b5062890820458002466d27d01fbe750cc1d5ca4b5f1f66949dd2b0dde472aeeed06ae6d9b3aa007d00511b69a711e436df29378754234236b57f886c383dc3aef08ce014afdde8f3e33685ff12f91c209aa22d6892a678d6bab4a23efedf6337c0d6c91b6005c89e084150c80a655725aec96473afdd98dfa05eb7bd2bcf637590eba4b601eccaa0866431e4271e9309c0cd3b1ad2b508140d5dc64e352cb0e340c8812b9a93a7db8a26b397844423b47b07b9c08206280574ec77e6904a7f4250d184a448be7946b0871de417cadd7264d54ad4a2ac836b6029d243ba5fa05bc09d7bb081e1afd9c5c445c6e72f74290787e0ce7ed9b7518422315e4fdedf7c603466cf1daf5d82e2780f405dc950b56dd02f18d4dcae6ae67b012846e0d1be7021d51e4a7d77dc2c5102b45b733c5b9e373bab476ffb2b1b584d1bdc9da67710d37ace4fb595c0b46d7d63aea3c830c4aab3669a8388e5bf847835ed17e63ca0ee9d4b49b530421bfe9e48f302416d87b1c0252ea0be113f6a8ebf69439dbce0145e784ed79cd63cfbb1b41295116ec4e71bad2e641ff0441bdd9f15253020008dfb5a9bf43b9964bd8764bf80e81562bce1a37a0d147cca933b6ae86c8e48e0ca7845da3e159294f9b5d6f339b055f34ad9f4ef995216a21a6c9c97227a70f0c6b6cd89acb1e3b207dc692106659d7642d9ff13acb60073a8e0eb00a230d1205d557be4abba82b0879add2d39d21e0ea30d5cf08d56dec1f63b970c84afa750804d500b98a6e2b9a7668242abb31a23e1afd422920dd2a74556144eb4987800b8071651e36918385b84ad1d2b566bc1391814316e5cdd74f02f071b0f95431011243f376cee3ebb38acb4f35df06c7bcd81605c224111bb381141bca7ea4070ccfc856784a6e0c6f5adf88ffd841eb1bd7707a3260a2de06efa6a679a601c2015b156a60edfb669d051685e1d79a7685a709dce572463e8d9bf37eb4ab2af00a1af05fb32c8329d288fd7f0469304d814aa2604ddc32b0a8fa49b25ac1bab303aa55536828c3e38fee97fa645001489c1ef128f0d87bab207a976c419d3e1902ad89d90fff6df6081eef4806faf051c1fff47b2f80cac3b0baadd3ad4fef4e097377caac4b5ce40b9bdb06ef2f8de00d1195e8db081576ba6b3feb643668d30812409dfadebcc4738c0de03b1559e686d79cbc5c0dcb158c3440135ed9b6df06c587f6a1fd1801f107bf30315c7c974016e8c5c1fe24a92b140e892184aa33063b069ba9937ba0e3b1b4b256b6d8315fd816b5db1d99eae0f2cbe73620070802bc2a4ad3fbc32ad648b5be3468a3e6f261058f269cbf70ac5bb4ec870f906709843e517b3a0c96ea318abf8073f5f23e03202fd1588f87db8ac591a2ee7a6e01518770d2d1716e6fd978d2b997bcf60b70dbbbe86b2c08b8fe6ceff1cbf7170619bb5db305f7a4be0a4f2634ed9a71fe7bc63d6e9d6a8a34255290d0e054d406ab287ce496f36af00b5f9d937824c0deb51f31e6ea0b56a2bc26d424150a2b02f3d7f8d1e643e55866f6e3857f33a4bbbeeb8ff94bbf1407f90a0956e0d7c00586ae2940d4f30027c7e20c9c3b495fa608adec953f5383542885f2208bd499046b492fd134582553b25361ce854eb9b7cf37a86878b9831a66692a2fa0f107089d3c26514c0daec6d0cee5450b1444582676915122b38ba3c92ac0df60554d0251805e78b3199524dc62862f3634feafc07cfb964af6f7784994d89d4166000246b43e06009ad1107c6c48da4ef8571ffe4843889e86f9668b3746e773d2e50c9dc95f2aae0511368d8cafffa70ab31d9be2af0975b370f5419ee9712cdca1066b60ced31c00d6443cd05368a28b42c4160cc9be2add86ea1519747c224f1d0d80cdc2c3ff6be71169c4a99b40435e3ec6aef1653725bfc841c1a0d77259a207faa4c9d324161b637ff864bd529672091f4214465fdc5f8533d4cae05b2a1803be6b515a8428cd7fbe55b003c5255f820212a5e550f58cabbaac52d45ae3ce096607c11007c6a87a7d8fd379c8f96b88bd748f63dadeb6939808387d7d9a4f0b2cc7c4a32d8baa63278dcf04b394709e97deac090767515f51909852c5946303b0bd4a08c42d206de3c652cc02b60cbb429b79e4063fbc13c716ac471fde040ccf04163dff76166cd47a875c28e0dcb79b1a30f965792b826d0d78d0907440067a4c95e954513fddd1ede94eda6dbe387c0190b15bb22677e9515be9a845d508c7b7ead0dc84905956deaa9f8960d551fe73720b7e3b67aba2736e06dd6f4800a43a3ba68445b36e46ae99a677e095407659e1dcd2f19c1b7416297d5bec13077df89a7c4d1837553166ffa1a6d9bbd41968f7cdaeea1707bd64006a37cb240e7a131787be73b8b1c72a8a1191ae7b778ecbe953583dd1b8e9d325cdb857610c0d67f31a21029b3c77e173b61fb31089b197ec0c580e1d5f321821b54afebd062e7567ed4e89a8e76c6217b8eca773f328efc0dc81a1d1f7ad0e74319f905e0ea6b44a178f9bc59deea70070c6430455eff6ee49aa792dc1599cf9999040180f3aa347a9b4e0c8407cecf114360ef28c595ce56074d07df35352e3e14436fd05557ee358551abed3e20e930e23b0a32a7d4a9d57d08cef56fbac466d1acde40343fbc26d1ef0fa56f7fcf85cb4cd3c4830a18905f68c3048d481b814feeb2202427de657d6671088aa9c8a1cedd3192724548abb1751da7de59cf25efe4ce20b1ea8188b903200af468400682f2bcd5c67a6be734632efe359a81d604709bd0c93b17178339b8a74f331f25263fe3c03d90aa5d585ddf7f6e1bed8718c2d0b0b58ef6159f80782b18eea2c189e2e66a1a137ec4e033b35a52091978e158eaf01e7c102440f48571dcf6c0b062874ba5c1c3d9c032597465e251849052e5d9700154e194ee1e8aee8c31a58b1f5cbd3814b043aace2bd93b5c04586d4a2579c040fac137de2046d2284a670fbf29bf490c6563783d906c6c65729e8b0f8d15304fd389463d360bc24f1ebc4d760fe54093da7233fc14af6ebec0314af9bea7a022436e8f59ff392e69ad830e7c03c5f539a113546f17a32921c82ed9c58bb3a05d6e64a0aaab432f073aca4b67a3a3c8e1b891da7ed4e1626d6bf00244b0e9802db397de598266331aae23303cf786a787f2214a5a25175200a080ae707c1af0c0653f9af8d1d55d855b83cc6bb02c78ab0a9400faef6da0b898d9159801f4300ab7d8862206a9f98a24aef4a8fbb9f6a7b88f5ef7856421cc5cd0d69a4ddbf027404b02ccbefd792071acb0cb3e844553ebdaf31f98a41dd7a56378b0b39170b0352df30e74539579e46615bf360eea948ed9b9f1332768fe4c7fc834489aa080031baeb5b43607dd7227457a6f06f98e20529db4ebdfb07455b0c4c59c1e006d6fe1c61a8a55c53a3aaa2342e3af97783b0eff327e4ece51632ccce9b17c50eedbec5488f39f4fcb5c3a78c62241105e7ec7e9259ad392dba1ee4c6c3b4e50192f8004f3ca8c05677b403e68ea4dca23b65d80796add19f12531040670cd50630c8ac5ae77a33e0584e30c4af9aca3b6c9364ea7dc9397ada390a5b8095020f0eb0793cba123e40874bf10bfa27863dee5a1e020bc1871c3d7735daeb0625033ecb52d42701ac75f64ff6704aa1c99de121d9e0d000f2e2030b851243822d04b925636d4b42eead4e44502c7c1dccb37e78d4bda54995efb96f96a189199e0e07dbaf55b9de2cc243a6ec16859d7147182f2b254568d5183c0f1c0dc8638f0e7f91761ffb7e6dafaab4be0a2cff655e96795c644a14c0c49def544d645e3701d4481ff04373209efd516804774b42bc69fced1a6d03e5253f1f7feaec62a60bfd5c143fbeaca207a70a4ace8610ac36b7717272c8c197c31bca9c2ca877e10c9f30811f9ba90faa66dc26f616b889265690f98a9b9c9809f4124c639f1a71075c48a6ec272a98da0ea20a8464f16b47d89635aeaeb43e8bce3bfb5d5867220118f1e42fb5ca21775c82a63a24f79ece215649dda765b586481f2abd5a07240ee812405d805227a3f14acbe3725abd35385accc0e8b19223d37a54430e67040da8f453f170fbafae2dc436f97b745a9dfe86a5ad34b88224563fd7fbd023d20b28362ef145de3260a951894069d51049ff1e689838cfc0932649bd140407210e0611da4e5b7782c52a7fe8d468881aae694eb79f0120a3cbe23f64488ca58009dab723d31e7de58273a0693f3b5b88722768186fc4bb47657e42f2c127b8b502d3e7814d5e8b0c96716edfe2e5e5c6707ac6a1c04e48eb9983bf25f18c595f056997a46413f61fe53e58d3881567131e6aa1efa5413bcddf22c230f86db04e0a6eea9e6db01349b6b43ed46c1492bf8a02e2d32d1c7144ea24885f7e33586409098ee8d9f346748aed882d67badbb4f38500ee556dcbb23ab86b8ef03876bc0ad96a63e69a28a1059bc8bf3fee6c6a841b293e1ba83592e3e263df2644b3a80b7b21e49bc1c8e8ddd6a1f19f5f81e514baa8b947695eba1b8e7c35789f855100d8fdac898d4a2ded914f88a62f116d5d50aecd86ef97ab9bba78f9659e51be08d32bcfca21f6fbceb3206827b9fa9a5690bb71dbbb1899ebcb463fecaf3b5b06e90f2e61114f3d7c64b370bcf1e9bc84f649b69533b5d1ca3d59e8e32537590b71a0e0d5fadc35acffbf9526562096d591822e43175c4d65e3a7a429e903b20b4d550308e3fc93b7526a6ef73a2260a3cc8c7b6b70bde8505d664be4f03abd0b4774df7faab5a6f6f5dead4043e745062871e661eaccbbd2b203f8734fd34c0856db33569185329e3a173e5fbdc8b96e17a0713ff7da4dcb4e9be54415bb1e05d5f0a0ef0bf61190072e9a3739058dc6fc916bcd1da03958f714f795bdfa800ebbbfcd8891d769ed2efc614ae052c879489888401934e52040c2480aeef10101e40edc70f150c80968f7eb5195b1a38b095f4c2c65a22059aaaf02209d11070016e40eb75fb0a09a37d6ad10b0fa7c4ff5c04e4313a0998f62752e17cb00a707810e1435c561347d44a0974d5f17b8e21256909fd910fca16640462b53ab9a07debdf1d3ad49fb52e17abfa5b2c6c1ea1ff7d5f7fab4dc2d948a666847bc210e62fae8a6ffcf3d9a57652784eedfbe29d2966e4dee9e7a72c4274c6e992d870ab5c497a872357ff2ebf99850265f98a0db4762b041ea5a3f886889a5707cd409b79af591e5d06063bb63cce45b24c7b0c007a352dc2ad0350a37000f73337e040981872285ec81de313c8d1505e6926b126ac6c8a1ba2ba178a48fd20ede730fbfc5d49a9d343d6e9ab9dc7aabeebaca677b6ec8b581234dfc578d6b277c830a880bd3026f7cb7094af2121351c6dc099a04bd4457feba6f3cb4eba090b63e0236dcd1479b0ef4a48d1afcea35582819880a567409de1b148c104444adfeeb0fe148177faa5dd4942ba2b7ecf642a6cc088317e2add3e6c6adfedabe860a1505d4ac0ea7dfaeedf9089138040d98e719b668398d623efb29eb83b9c03c7e100c88eda1627ac488346ce38e1556978d311eb21bd1f1ee627f93c7ddb496d8db01853a093403947bef3136a1a3768eced1f92aff510011d6aef36655d04d06f90458a6458d6b1c2def5732bc79548e9bc74889e4a2e7d6fe672a925522ebfa61025dbcb223f8bf50be96523564e0249b2419fcc46be20d197fc2a963cd6c3f94080497f1f3071334c231b8d2b4894d276794d75ea18470dc5a6290d43af254170c5b98988c5556fc2b90a88d7894e1073dd8d2148b255d9dd542a80a7a513cc80e6656a17bac6175fdc48a93af8110d87778b0108c2f61d7cfdf4f3ddb52765905d5c01774c171a0657015c2a6b35e515845f952d3740952ca442c9c0e02ddd7030fa9dc5abd8297444868e726862f21bd3b742cb542b46b11617c47cf7ae6f20ccdd28a3667f7526642f438f281834ef847971290833aabd768f333c7507dba08b20a36989633d8829bb9c638f586917a56cc2a6cca2afe8a81aa2b4e51d81f03435bdd60fbcf24cfa386046dc08fd270c30d7c4d4ed19f3e87d29419b4e59a0ceffcc5bd203d669d0f37bc1239bed6ecc8e20898a32b1485b94e73158057750d87a8baf9f5a7c8fcdfadddab0ad027465a94c68191404a5a9030a5cf1af111049697429c11e81e6f4fbdc814d6a00bec6e8b3f9363e2c410524741f5e6a9580d69bdd40584f4e63fe0595f56c5e2de5498599bcee8969658bf5c780e4e6fe600ce2fdca194f42e77ca24d109c5192f2665589fe97a1d0a9f750b2f1a3bc60703c72a7d0703d928d2abb61f17078c80f40e9fc81a17a364cfe0d7f5b4ac593407b63256ebed92836182b45a41ecbe2fc2e03c1c2357ff28e225b80c5902cc7307a9e63ded5270551106efd04a48e5dd0220975d26296cc16a48cd41016d455f0a2f8254ea1c6fe29dcc770e96e93f9f24f5b7486a291fa7e3be9b98114be67406e7f433c68521bbd4d1db12fd16a001331387af9ace7bd3a657a5d2116679040bd1f5485aac01719ca8ea867f8dc29a96bd425d1524bd0cb56f9ca7b7ad9c16058cbb04d2acc31849d6c66d410737ef7f0c31d14533ef700e6ebfb7bf8e78ad078c309df8d62eeff22c0537b2ee2bf46e0f2801f6109bcbf0e5381cc09704020ebcce74589ac7bc5465d270a1ac2de4a532a9fe0ff9b6bfd77e01963e61dad70ebfc77ad3c623d520b8e7b9f00535385e4af2b6f211ad160dbb4f85f64bd1af01c8d9ced66d4508359923aa700e71f97e4b3d88ec9e30404bbfd8bd6ea79c93033a401a1695b2c6ab5adb552c28d0da21b7510b8a321ee7b2b9fc4dccdd2b0e09a4d37d99245fe761da5f2982730687b54ae4bf343f864bbd5fce95353117a40a87f216821793c4c42e79fe9844b117a43e8983ac42c7629d2d8a41350979b3010ae49efa55e3b31e63c47090e6496a06b3ed1ab79f78aa66707370e99d1c3f0e3551c88ae3e539370dfb83146aad6e2c96d7409c39439b8e7798ca8e846e5209b04f6597da6530f9497cf76a097b758070d9fa71fe4ade5a9d43682a3fe23603a60d6eef26b22f19e79bdae216ca77f92c7b92b6ab52a540bd734db512e5b50391ce9f95cb7ee835503ba33faf0f3b9819c8ebd2db2eb38a4450a9f7907cd903a8c78a195cdf712af2941d2ddb892ff065c2903faf461a391d1e0ebcbabeca093a7409c1d06a00d2a1e8ee761bf1634c59ac8a4d6f76d5816683fc1bb8d92b013f853c3c675be176de6f5379de5b31b0206e68610ce71e794ba112ca82bfe507bdcd58f9d0c4e858e7d4318085aef32a601f6de0ef7630ffaccd864f7c9bfd0a545fd3473529aac34b8e8250e6b964156f0c767a482b1e637cc75d0a418a2e06b82ad8c4d5903c2dd259fb33bc6f12048fea1b7bb915a3504ab430926feec30c166556260d7832a1979990c42e118b96bb3e297406dbb0b400638381732124067d327cce7620dec3210d7731bc255019c8f6405ccb4e6263f2745bbe1b2a93056bc363e1da22995e0da778d3a5e9f15f4196db4f939d656ac0fc41d9cff31b0f0eb2432690c3ec6672e2f7907f88b16ae5922a2fcab7ce7cffc5db783045c400b70b62e2dbb9f52fb2e4b8abf24c8b87a4a3c1904428f8d5f68d8291e90247015c91221e21303b78803924022c3a79acad784545ab2b08116e8b7a992b6cf10435937e42b0b8167d5a9aa757ab6f24249141400eca2d5053daeb110f366a5208bb63892c2c7bbeede23d38caf591fb2e578c8c04bac8eaa8630ace0ed560150ade81db2016a5911c58ad50296fa4f900ff27fdbfbf628911c91997a4a97b290a79fd650277bae99afbab62b5fb82f40ef904b743cc0c879f9b7a92d8ceeca8030d5c3e61972922abe35c1c7a6e4737f5f5fc65a891d3c0d8dc94b266b9294e0194d15ebea2d759d2f2139753b7f96f3dd921dac2e89854a7c3314ef5e2dfe50db4b74e2bcf35c80847a5b6c22d5249e5ac42347ad129eabf5748d7e40301f807b1c2cb96277feff357d72a7606a54f5c0befac0095f818054843bb81b8624106ea2811f5a18b6c71d634ce44061e9203ea707f90948f97e7409b39b9f78f3207c1c1e09465d71205fb37f55095f9016f5cf0ca9d6333eb31d930fc9e155bfd281d4daf6bbd5602ad35cd5fae416bd9b01b77dceedb618cb942d7d7768485420d1dd2cd137b173bca7a169987eb1cb92ba0d6393dbeb3dfdc58825602d4c86909bdcb970d5869090670c778141066a8a7dfd7f62247bff7d2ea3d967aa348d30d6dbc6dba62a555d4dba1b3af1e11633b50cf1b3396c82f6180fba721df7e380d3a8a335c1617bdece36cd2e91f3ae9ae732a9848cd1c537dfa940def1ef9a0028cc1eec638d19b467aeca57666856049731dd82dbc18b4d0b0927a3e32b573028465264867de443ed8b33441eced30525be5887ac7649a77863d7f8c0f047a0f091cb007dd701653638cf2bd3eaccdf0c4c81caeafd6dbad23ddc99212e21e0b1ef2ff0c7408f1d95c782afd525957cd78d7326a0e11beb053ec4b5c3151ab00ae631f37f7b8d1b4cbde3860464fd370a9b13164c696439c37030a33d269f00f8a36c99100a5d14169788b628268e8f53beb10cf2f4f9d77d1678f3169fa7d034b3c373d7da7e0a9419df335502e47815a34bfc431060e4c87167003c8776e068c055c52a37af68b4512ce77e034e218bab53ff8f15d019f08d371a14501460d840382cb8efae5fb8826718a8af8cfcdeab652a2b9be73a86368ed37d1f3f20cb1610d5deace656052424e5a2fe2e977ff787661779c8dc4cd0bef45c208c80a392985017cfe749486534e05a78a042b84ab74cd2a674ae19b207123b8e91e022245e289391337bfbc13c4e34af1d67ad6893eee08293f733c7eca4efd553602ed0fb708a21722092c43af6dc61ea25f6913fac4f0eb75d6501c6e17c3a74b0f5d7caa16043ba62155e075734bba3e781d043249d329134fb17f2b750c0463070e43248a08debcc5fdff187dd31b3b4f282418a0b7c4745aa58d00dd3490a60779bfc5d3d00dc3240d1e102c39e8f000fbd9ab5dcf8abc5bf0cb2973201f5e059444368730f8ed444154a57a88c6286652085f524ae81ad719f1532b333d8e07f4b2799ff2f9059f2ff87661001edc9119d0ae1fc3174bdf959cb2472332940da58748c03503f6cda3c42882a522a1ab7bc3cc3f909ee41d22a4c516fcc2db02322beb626d7459e5873d5729885364315c83a4d0fe48d1f7f11eb295a124a002bf3aa27524693d6967910e948530a0dd186ddd9d90ca9e553c19149f93cdfc0b5f07d9eab2b4e397339b728ebf045600742d2f6c1f3545ebca01af992fc8940092367220be9a3a94d05b153e3c60bb92eb4e3801fc1da3b33fe9ef24953a4a083cb1d228abc37a347084b8a8453d941b5f29a91918bfdd4399187a1b93140b0432b9a19d14b894ef7c5ee47be2ecde54b41295ccd58f429cd581cee3452394021c18feb2ed98061feacecde5b13fbf62a76178eb95d7db4735dda4730b862803c9fcdda0b809e833f2cf310d08b3438d9f21d21774843e6080bc71d3ee807903258b8c6076658352b106df479a30bf0185735299f77228aad0faa8979b01860017aed71e2df87dce2b1d9ad1d6d05525a143c2e943662fed97c9a8e32cb14c06a26b119c03c206437cf04805f08fd720d23b1d3d9fc514acb3320cbf71dbe70d14a54aec78d632f1a1991a80d45b434a45401458d051b9035277ab85d9448808f6ecd33e4c4bf23f958f8ea7981a7c676441d0f3dee9af517b1bcce898b84d05ecbb3dacc3511cf7312b2129409ccbb2c81f5abaa9f40fa4094c8cf83c30380e985ad5a0768ebe00d06b9bb6a167e11e0eb9e41ef91a39b03e8b98953d55b30476ae63e53f18b681f74c2a92064ccaf1094073c2218757c8336f157fc0763d0ab8e26ffbad8160e1a1b73f01696152dde91ad24ec9617b36b33e6f52a0951402958bed5de808450863ec2977a0d8dba109ed81cd3d3a5e84064ab514db4499099b39be21f6d3cfa9066eae3a95a4a49799aabbcec268a3f6ecbc6d3ac0c48d0588542a4402164535bcd9a92393a52e53d930a586750c91e622df8a58eb358106a8371a2c2b597e0c91bebb8ca04bfa9a3169899fdefb798a738c4bbdff9af602055e12e2d78e87ae1d3fbfb8904153d0a3a98b66c10c1b7fb73a767ec239120602d1391e9c3d6ebd2b7f126636ade6174871b4714c38cfb0656f6c502360cf097ee9255ec0983084a956c986579c67049a9ffee1d7d984fefe56559608a6f00d115053d3eeaf2873ea1efde2cbfcefb5d7565bf1f5482558696d1a056f4fae05a17707eb627633c2751a997bdfd1ad7e6722adf248ed0d848ca0e0aa7c563c0bbcce061198bd4c030734f892b35858ae94a92e7106b10c44e7d21b0c5cdb3603e879d204eb5a81aa9906b9bb75b1b4982d60573480dbb9e1d166e134b906ed0173d41b4ad348f346fe05096391f13de16fef8ab31b4fe0afb6cd936387636600a48012f3c283ebd0f7e78c4e7aa1650b0f3b27673885c16a347a453d92092c0f56f54aae1b5b3ecc29d58063b40ee3551e6c99ef2dda34e292fc9318e3e3840971f6d227ca29b4716f1ab35b45d18e37ed98ea78a8a89c25adbd5f259e558f0e685b961901fc4d25f0013d9f092ff329e355ae3814b67a7e9d873778206c930c75b1d16ed5fe34be3000f7da626ee7248299af96c2ae22297dc382bfb10a19063ab188f3c6d8a578442a6e0b33cb38fa7bc9b0b8dfc2bfca1abc161567db300c43768a372b678676412fe760bcfe3b65575642c2e382c9dff729f3041624a40101732d8542eb30b1f99a3545dc1f200a8bfdbef5647570631d5717f82248f7063ce0d198cd6288321a05f39b72f2a03fa0deed3bb24e6eb2b95d67b92f0d2f0cd591a76ffb74209df3a1c09e8b54a0a46f3d9a648e84002fe3cd866ad8f7700089840813aa01e7fe26dc0a1747e633fc0ab8b951a933dee9ca38e547f5cc3f07aea59a8c643bd99d148d3ed68c0cadb6585deb20ed9de990f03786bf55f98304e5c2b4276af5166d826a4bfdf5d83f2c543ccd1bf94e5a8791feb522581df60fe154d5cfb0e0fbe1e925006c45216592bb3e3ed3ac98101adb90d9ab95b24b0629d13e45465e3f4c331941bb72d35b164a571489cda507bd3457deb248f977059ecace0ea1ea78c363112e1af1f481243a335315dcaf7d80d342600697682204043a25ff7cad5ff46911fce2847bbb88d63c9a9ee92f14db4865a3f51587a30b9f5f019b24382f09a65c28bf36a3b443b85f962380de6e4dd75c70441aa7d203f7edc1982528de364c2f8762f2367db1383ac4eb43fecef2ff885933bf4789080b2e5686f409212428cf33f602ed6299ce6952a9627fa412ec82a826567fb10cdbf337d0f4304891c98e4d198dee54f76b6ac322f2ed5e619ded91e895d0bc0b664ce0622109039c091c8b3a7c9965699c8d2c016a2aa35503d5edc90d6f2a061538094f08a9d9bac45e86d420a4f53c98098de6bf5c8e56f7988637a8a80a0ea4721676bec74a494599fc15b58c62352cf7e03dedf60de13f16bf01a9d6230943dc0cf525d309384505bd63bd1ca238764aaae99fc59030cd036c00ff949e089008008aa34b6a723a4a949bb27392d2b8b29f44112c2a677f0ff70c9ea4b202abea28f86bf12172d2b4b0487894da0fe64dcde70f3b2e3cd32bd51e93f07f062a14998503c2a26513eeff0651c31528572a15277505b0bbe0cebe18fc4314075121c0dd76e03baa12ca6a498fb7d95a0f4fe14b1d2198ab4e3b7a3ae8563c0cd2dc034e1a0f1160bcf9e063fc56303830b079dbc444a0fe38cabd403a9fba0cd217775343e8a885e5358787520eecf8763db0917c8c47bdcb65ae8d315f970b461fef986a1688fe747d299fb953d0d281f8b1af1ce31d9f6e7f8a5128847804f7129033cbc1a35524280cb09796f8e70c37739d21a1386826babe8de58ff10ee5cdd1106a16737e4de401643426cd0e12d1f1ac2ba044739270916c3197f4090b0e404ee59a51fb35af6443184abd42770f37059779e3be307d16fe7e52d0041ab977620f409f5b7a72337e69ed9dad0f6651017f1bcbe53ee4aac945fdde0a1fde87dfe90388d06f4c81b4aed0a3a3ea24172bdbddc206152b4c10d8f27f0fb54659bb58b6b8c6acc2baab3dfd970500a970569fc636684fc9c4ec40fa0e081f3e2f165cec8f3ad77af0a82e74c8c79fdff3dfdc5fd07aa7e5f9ea1d4811095031b897a3086512158a36e842e25a6bbbfeb07ababaff7fdbf892e39b258500e0cbf28e3c6e63e4a9b8e04c8f31afaa1af6761f125d53e93ae9e578e2b64b057ffbbffdeeb51a3496a945f86085716420941564c0a3ab517e2e251eb294ee0f0559bd6941aca5d6134252223bad3e8dee1aea0bd666cc8a80c4cad96adfcb0c296186383cd59f941fb0282bfaa70b797b08226b8658d8d9d766aa6e39714d03b0e73dc7965d3dc23d814db666cbfea132faf99764274df9df2992881820d00aba2600fac134dfd14a09e886bd0aac6c66a22720d23128b5f8f59766e54440013fda2ca4b547c9696f8dae10418ff7df981e5631ee473311cc785ebdd18ea40dc00f8ceae4dfe3afa1c6fed983a8f916cf30b777aa60b84b52d8b12bdee84509ddc8552e6564cc3dd6f725387f20c5214b254f82377fbd18fceab9cbc4c38d0e5fc6663f4ee393374dc7120c1c2321a0a0d35aece06e5bc04ac61df1c7aaae070e59f2f562b8e453b742087223b68e16d86ab9dc989d0d5fbbe9341baa02d50bab76d33d2d90a13c56f6f667cebe6cbdc04ea9ac7888958bbd5ec2ad0ac5d305e7472b0c3f5a5208f3772189f7105f03d8ade6b634ca4d26bd3f3e1fe9c8b407a14dea4a746c75eb9239b6521cce1d0dd443543f2632783b68a99eb59b660407cfbaa1933e7bdade746640a531ed7028c44fe69cf8804207d44aabf609b45807769af3ebd2cfdfd7e239fcce6a8d84f297b78534469d720c62d90f20d4527d06acbae6b0118ceaa662c9b8ff2ac2698c1e1df2f032d9b36d0722483551357d002d51c50452a1fbc9b69288271f68586a85ba13a848d625c9e6c0f7497ea36e0d049c8b83d1ec82b17d3d11c5ff3e1f7731c557c6be618c62d775bc927bc34f0469c7632d074ed01623846243f62d3fc6c34700172a56d0a4d868bfcab1e2e404682456917e70c56c81da09f80db7a010b93defa5871a515547ee17d1efcc930ff6bf230763aa3c878c35be8bd10325f33d83a85bedc7c390ea80c9db3ce39a023fbdc012814923b6925a25ab97b42ddd261987a07dcc355bd1bf764690222a0795734aeb6894ba0889f8b1973aed28731c67a7e4c44621b70a05c71d0b6c020c868f59d89350cf2dfef178f3d08a2e854c38ec077e20cfef336146762783de02d4ce3f4e5f261f77392be9e5cb444088ea2db3bbdb2c5171d7a0226880b08a0c7c587b2268b4285bf6a4b4167868e73551da9f2adc66d82f5ee02e9aabc105098872629440e3761e3bb10314ddb3967731a018b6b5c55dd3f917db2860e87805603648d6cc1f514ea7cd9bc681df5ca59a24a24f09c7e9c19712d104af1630067d1ec130258d890be4a3c6389a07f95a3ac48cec76145a360bb2c6bd7bc02400e41e889bb92e5382c8665a6123ba520c8440479d8a04edb2d6f0a920f8d93809ffce401ff96e124033a8a94ea16ef34bb8b99abbb7895482b97bb32b4ad3ab0fefb27387e99e53e3060b71f9d9ba0cb74f8429771ed53b8b25a1198c61d6320285d178a346583fd6aad6c71fb7a92cbd8142c82c7d51ccc16fd015b57c3bba001b57476d0ec83e0057db92c4199d26ab5f0b85c4715793bf9472edf3d423d406f14ee725bac0e5a041718703bf896ec2108c521ca7f56bec47db62aea6f2dc04ff8b685fc484b9983ec3571235e0176d0e7ddeb4010fbdfe2fdb17be5e3e9004b7ccf503ee103c0598c320b1e98fe13378eb0cdc16e9b8fac1829f2fba79060a4ddccc89d63ac98fe2280b2bb2adca01492849880440ee0a5786db955b9aa50be9c1df3bb593cc8d8b9494468526dd3b3a3c9af03df7721d213972f5baf04801fc122a7302c005a19d3d6d1fae3e8d0b8b3bd282b3d6d451909441d059dcc708890cb1ec47cc8ffab4d9e624e1f3898faaba70ec782f3ecf73d6331bdbd9b307eb07d9bf8ec618f2604e3669a984f014faaee16d9e767ac6c7b8415f3c3ed804ba26b4ad0c1ebe88cbcaa7297fb437aa8ac8f393f2c33d1dd95de677e49a6608e13a3667a22db2d88a3d80cc8482a469095bc7419508b54dc067f144f89daf0a9fa79be6314fcd0792c0390912428d9d05adbe6779cc594940ee3003b958a006e20adae8638341ec66f2858494f8c52b9dde7ad9db4059e847a14452bb696c019d00292ecd4682243fe04406ef54eff9b8e7e994e03bb630b699cfccb25c1d049e94140d720d14c7fa939f6c628ea3d1157b340664142685101df37ada4abe0ab462a4f78d33d0036949aa02f4fd48b9389f17378d87e2bb3b44e16be37e4808295977edb6479c71d7800b15c702c2ad3c1e31c35954f16d774011419c8a600cd598c79b510e8ac917b12c7b848b4dfbbb5d1657b0330297625a61d936fb730bd7b97086efc6973f1a93ce933e26cbd9f0a0cd567e09630d9b6e4427fc38390baa3c0021d80651122bd85aa6a0dd62bb05e8fb7dd8b2ce29915eb83af3a6a401e437f3f862c537b02496d10d6408e609b25dbf57db44461c9e12c41292dc3808514e9f753a61ec1c23ff30976a9a4bf3161245091ac12d93f908711947d2830713f2f8f121e049e9d1579e4bad156614fd2109a9ec78391b0ac9c78a980d3405d7ee9ede3c0c0e5cf1fadcb9188110ad766f55b8f06a5773ff1a104e838a280f32d85ede4967a4ccbc61a68f65013c9f9dcbb8c622a88724ea8841b55c49fd0d74417aac9fbb7b6decd6b0102020f612480483a014e27b5d6a8323dc448abf05eae2637a9c0a27fa8d3a4a6cabacbb54201c08d864a7c77577660f7219d495006b170f35765fbc113fad4c97dc188bbec3b271823f5e25e1ead935211a7bc7003636429e1d6a4d886f88a8bf438191aed153a428e1eb3763f49c86f9e3bde90cc1b77180cbc1d30b6e948ce16cbe7fdacb6013008d534b58f83185f1ab62b30b8956d15c1784a7f6775361ac5d30c0b7182fb6d8cfe32ae2009e83ebf3d6c80040b931f569c851370d0d41d2c171e84f349199044f8a2ef436e09a08e9733f0c7cf1134ef655d1944e43d7e217f724db6c8f9a34df65b09e54fde1b9c6561d074d3f8a082a447aa62d00aa7537ed1222bb7eefbeb81eb6a4cdcc90aca3f63404c1fd947730370feb15e4f63b689f6301fe3faefd1e589c559c063c115e0e9a020bba171458a8d01a8256e7e2ea5c31c32bc7c6413ecbd55a57f728f4624a700c04f84fbad9eb15870f59fba04dbe721998d48ef01a47e1a20983a1fd6ae1ff016b54e2dbd65abb02406756f5f42e16ecca4143e00b3a24a8e1c28ddac768a5028b5db6295508c8b36510d43cca2b5c4011c2c761bca0fe7cbc45d5c83cc6fc0456a7e1809320ea5e7b74b09668c26010f684e5c28a1f8b3fea7175064c51d600bf41ff0201e5db8da136ccf46533a7f0901cf8eb62e3423c68cc85396fff7e033b2dc1098c011e005d7ddf14dbe835f1ad14bf34173a274d8f3e5af37a586c0d6911f519fbff7aad56bb3f3fa2a169b05487e6ca808884dead4e73904ed76a0f066c198166461152a1fb8ca251e93aa84b5d01268c11c8d51d4163115b8c25050c99002db6fb19a30a53776dc4f4e6e903b2868a201918098a72366297b1f305414a584929fc143025a53975235637e49f1f80bb64a8ef94d09ff660a7325a068bd0595cf300e2d3bcfaed48639a0ab4c79e5b3fe2752d58d7327899da3607022bc7861c95fdf1fad8f639e92a25128af72726c015b5d2bce8e565bea32442063f24d0449be056d17b38a5e71cf70216a5379ec22dd337f2ff531cfdc8283e056d305a2243eaad27e0923daec1d335e1f5375f4c547867ec45e27dc81deaa0094d274af6d435d1285396ce08140f1c41e42d92e26c9b80d72524f84beb1a5f0320a5a08f917fa4ecc5a95e3af68f8aad4a738e65a8f0dda1333df2c1a489b409bde6b68cbda34771175ad4aa72a25fb4ff565d439dbece0aa957edf941f02e032f1498b321cb4b5fe38ea3a9037a6a97bb5cf6b3fe694f5e9d55087236646f0f34967ed57b10f0217baf830786abe399c96dd0fcc86c7cd7efb76de6d9639d0bf92de72c82e5810cece156dd64b83f65e9a7bada832003f07f5f1e82d8bc0c08fac419b10ac6e30e1f7bdad7e944540f25141a94c79ce2a73cb146f478e1440520deabf66f6e6dafd5419da44553fee099c68a0943bd0fcefc0374bbb58f4907186068cf7ed2f5cd0af41a2094331cc07318488d2c3eb2f7b2d1d46e5d8a8905a7873a6beed2750a193058cfa9cab2030ddee8c56e5a78f7b30f701b9e2d8e0484dc5c9348d7ccec2f279b7b7f5b2933db339e4622fd1ea3f329cef92dd724019f651579526fd01b6254f48a034a537a921254e574098cc46835a31214c040046d1cc49c3a916ab976b4ec946c2b89952305aad3ed4a06ee8b8468b896627a016b914570772408c265d411d50eb2bf32491add311a53175549413fa3d09f45057c9c1417ab1ecf34643d461c240236cc9aabe67323bca7ffd0062f8a0118c3048ea051a661c75398d422f5261d4f43eaa23bebac00db12ae47096f052084f00f3275c100de8de806a3f97858a6d2cdc1fdd0fbb0a28d9ec25c79fbaa5cea2900917e53e609b1f3653a4e846f8afe68ebdd1c6a87c5dff4d8c258850f15a2d40b0def014a3d4d40bd6f1763ce135f9bed3f7da86b5ddb1b223f691182d98fde094d16bf2b3f1afbbd84c56c015cdd0c681e4485c31ea94c5425b8b47f46fe360b449a746c78f377b2b2588af7bcc62167af343afb2659d08919bef51111eb3d0f259527d9f79abee01f1690383b7cd2da4eb38719de954796973d1126be0ba8063cef5b8cdcdafe1f7adac29c2e2ada23e98ce957763f952dde354140b055290392395bc33a46c0876fd7ead19c8bf6a650f39f44c9c396cbf55e238498e22f0a55321b05c249ccff7280dd224990d1f05ac88d272e8c6377068f573b9ecb190ac274fcf19ed042cf8741ebe774df5aedc0beeef50896d37e31ae6dcaceca040b23f62d01c01c867154f745d01d5942b4af0f001df1c233ebe34c67674da6f70aecc865e8bfa5d0459af5fe297fb2f4da50b90ad5ad7f3431312f777ffe51420b1afe4e93e20d6f661dccf2b2648842d28308ec39a9c59a640e4e42459393e00a5eb43fed6e08af6a75ea60ea5e7bd7fd10edd45645bcb254675123f4d1667d0083bf140795776e31798b9f98bd310c5683650ffb143ad7f2fdf6711ab88e57039f7ab4f1b78cee479b1dba6fd162c86b2c9365e2ddf22b4d56be2b9e7a346d0032bf9d439e6272eb13ff4d8604ab8d2a309faf98cc2df68279d0564fb38e3a08efeed6bbac3ae557841d39f096eeed11cd5cfbd43f3f8a3677b326a5ec397403f846ef091273861b34a72f183e41e0921fcaa736ec7b06583590ff69e649ec06f5bb9525115ba9254cec8a89b3441da612fedfc0e384c185fddbd3cd58e5590e4d7b0f24abd936d78852809536f8082d3f2125b428f76be614f0a173cfe5590394a4287752466111e46a221b4ad4789575cf828e47aaac4f731eca3635e4890df43b238724b413825d13d43e87157e646344f5d93572bde432b9fa88c9050f0c155cd56d47c1bd96eed2038b805e4e530495fd8904a77630d475afd0548dd90f526261f5a2e1d84f69ad8019a34e97d58aed873a0a7b2c5f3897a18e2f456f01b68d494ddacd31a9e30ec43910052cba2db20e9070f89caf2af40c4330c8c906924e938f47cfb1bdd37bf3ca185eefa6dd45f191bc77f6d5810232ecdcd8a50a false -check_ring_signature ff8c9ce9506f05ad58ce636b972aa3da201d8c673ddfe83f012742c52f85f70e 930ff5f225e005ef7a4b292c3364fc74a5ff1edab9f8a4a62dcb6f86b0f108c2 74 693b8f08056be66ad8b325f94b1ddf939863fb297913069b568bf9f2812c7e8b 64e0ee1a9fded86ba515586caaa7f9bf91a203dee9be86c4983b591e3770d7ef 193d73fbc615b46e2b2c5a8ce5af36ee111779c3bbbd4bdc4a64b4ea17881294 6c5d8cd4ac56576c9d0cd56762bda9e7df021106bd7fd561b8f93b96d17ee5f4 6e7826bb6c875505b98c00cb2da08d4eaa57a546dd86c0368ea6533b329b3398 904198591e8d043ba0b7e3cba74492912432c2904daeca0812ac5e646e266d98 19e10ab3500e835c24cdf3d4463eb78a63938feea605ccd716ea038f4d9fd491 589d097cbafa02a928049d159c635160d65e736bb85ff3d94ad8dc3cb38bc927 8ad1c032fdf435dec7fe5cf08b531e862447794f575cc1bca18a37aadfb56609 ba682e6a6d690c195afc2b34dc56c50385796cfa0a8730979568639d86ea9bb9 ac84e262c1d16cb20f98b8ae10b6420db77964715d90e165e5a17e4d1cead1f7 f8e4d908d3c37a1983886c79c48c2521a14806108a2cc0b2e8945319eded7871 8d844d91c6474ee8e862ec0dba4cee3890baff0cd8757e475cedb1f7a8bb8624 921456162a5942162c97006d8c6e83e094d3df90cfc3372de5f7fe7feee90cd0 05c4d10220710851dbd97feb365f9cfcb62fb6efd220c0808fc4a382cc1bac93 ea9d1281775a95f14c98e9bb5ba236137a8a8bf3e624fe5f19838ca56a9034fb e8bca275bba7d598ff6c88afbaa918e3623a871f83729350ac8956b03709b34c 1fd158d3740ee1eefc10155cb36b786b211135c9f4d0859b2006fa4fdffaf703 8ff5603f83b5ef67b91ede27e7e0e4b19dc729149ad682ccb5968c521129294f 63043b26a2569231a7ad54a3406e433f9f094f6e20a3deee4b9d86d3697eaf19 031b553f77b0e1a7fedc5a8feb5ecf7d94d98829660efa54f51b1c9f5095f208 dcb8417d6213d05cc79ed0b7ebfd33c2d2e9631640962e4d0e0820757034c004 698d929036b9c90a566f021a6f5c49c56d268a21c3c9be5c6f8b6d4f684c6a0b 82f7c0cec87e03f977d593d1a44efc531e57f72782c5960374b757caae9188dd fb0eb9ab27dae8b20196cffbd43218199ffad5f872d4bf3e69ec7f736565d2af 28cd0565036dfc2c43d1747d8cef1392fbc595519ca410fc3d7f97d74fcfbb13 4f36701e9d3f2b55442f312e63b2c305a4de22d7d12fa9783df1c5cb1b22facf 4f57c5f1b7bc7c8964fd8b5d8b94780ee6db1377861ab20aef9c761471832b35 ef7f3d4e38455cdeb6b800ae74373efd916f8f69a6150a605a9591469300f817 3e83a787d708cfe83f6755c59212d86f615b2ff042447ffb22d9d91f01953ae4 d75f767cb56b50b7c8fb84fedbdc68c28fd03a97fbf25b8d670f72b1f9f19a0b ff81fc3b97ac03fcbc2b4f2a60c03eb3cbd8fde52148e48a7ffe845ad3d3da3d 7ced3f046c76069a9cfd132e46753a27f39b22ac3b83b56952ddad9934c1148b 693fabdeb97e21349445eea7406f3f97c3a397edd77cbaf1777f7de890d037b6 f069074ed2cc1dcc7d09d889f9724c1d965f051a061052f841a0f9c21572a1cf f372bd3f6807cef7496bf21f447ac64de28635ba0dc8c1a9fdd67d5b012dd20e df8ea5a6cce5fbaab2325a24d4e994f83d7aaa844d8e81d2cbef21733b0d15cc 6959cc0abb27d11198627bb52c98ed9b102afc1b7ad12e336b0fa0684dd3c212 6e613d3f83f554185f4ec7d947e7f296b4dedabdf2216b5efbe2a423ce539e81 4d1073cc1e84d9e9bacd38c26ffa61a42d60523817c36895c597964de6679850 706c1a2c9d73c202386e32bba07d686b9be1c344543c30cfb3aa093ecb244ead 26dd162d71d134c1ed040c7d1d9e515fd095c8b0ada1aaa5a20ac8ffa1b65010 4eae16fcdc565b4ae48f8473db7f12332ae09021ccbd14c2d505e069c25c10ad 62de42b89e9c38a0884275dc653796d9ac9f24defbc894859ad2dc970e819fb4 ff90c6271da497c0418b970921119dbb834e939677a0a8ba920ae5d2bd589885 165029c549a731782b53fe1fdb7779adce43dfcb00137ed059ca11da92e3d215 f1b6929c23beab5ca67481b912179706a8c8b9f5283c115547d724f29f45ba43 b18cd9d3f8c9b2293c166dafc9c8a6f05164e64643ada8bf9e601619803e6145 5179fdf9ababf31d3daaaa62edf78d8f7dd79a2b60c083f9cb0341873599749d 0f8ff6e100fa5ee43cfa442c598010518520cfdb8d80c41d864f03f00633e324 f80df27448a68b4943eff3289fb9f6b236a61c89c6a89a0ca0509d34246904f1 6c6fcc3fab22079b459c5f891d82a0dd8f7a34a312d210c5de32a3d8603359bf dae084758c1ee7a2d9435e2e399476d47d9615665d5b538f452e0fb778bfa4d9 7628f20e56e5a20f8047b954f814d0271d4f14e1b02792cd15dd2601a774e06a 3bbc6a4ce4ce778c83af389e4876d8d6054569f515dea8b7947bd01a88ea336e 67dba0420586c0515909993d712b4137842cb5c10f9e584ca7e64ae8e133a65a 0c3af95b2542cb683da3933aef6b354abde16b6dd1fe10b4e952d5fc7781b468 8ff8c830d8ffac06728642ed2e4dbe2c13a3fd340552cbf21cc7a066923f0e75 3670e6dc5d576149fc9140aeabd775d3bd9d7ea43fc0d48a2b8f4b88b664b55c a36ef15f4f2f39650b2d9e41cc2748b6cea32fc56d1bc0658380340acdd1526a b1c2e3741a452fc334bbcac3e2c653d2613126f65f1a0e15e6c2298928585a1b 9c6f323eb272ed28207bb2ef3a7a973e622e20df8705e110acd1a034059c1d52 ff7f41f3d6e7b9a556440ab1d389bd17da53520c6770cd8913961deed7328476 b21b4c7540c3fbad7bf79043db3f2f9560cfba0afec55d4e9a12dd145705544f 6b0910d6bcb5266de6afb593c0b357827f459f7d735e666ac67654d3f035d843 ec13ea37ad3d4c77826beaa10d169dffcb560e34a96fe3dcc7e295ad19a0af51 d13fbf837dd79a15afc14c105aea2d636caab90a9be779b491ed99d7f7aa2a15 90842183e0e2781f8c07ccb012e4460c647853f71d0de6a3da333fb03d60ebd4 24637f4416bf0733148e44dbf1836902b080e8b06337dd09324b9e3f9259fa02 afce7377a3f91ee03ac2e1e20b3c4e22eb2f4493f22934017267994dd9e08d40 91632fbe502efd187f3d91fe0e546dece87bbb6f81bb9693221f1808a58370ad f62d9af46381af2b88942ada0cb9f6c0fc633b416e4de28d1ea116d2ffa1fa77 78cd29b14b39042917893a461da1d8e70e6f3907fdd521a34ffa5f00f6b01453 49ea0f76e74c57dd404f675e4cb1f4928752de23896d0940d1c5e5286b00dc01 3c32aa0f2fe357c55ce546c835a9c451fd024779aadcdd8b5aecd8ace5b0fe03d22972980ed2a8f078afc94fe662e3df8bd9ff052312321adf96fdc9c63b330edfe339b3802108a00d2bf59db8c7e5f6c07a314e6b99e045d0d433fe5154ca02e388585d3931e6fca83727af61cbddb807a5d4554a005428d7e579032d4c7f0d807261ce782a8988d068e1a1a207e82f1a075d559c57af3c152d94fb095b2108be59facf4ad1070132bb5361b62e31fca68f31c252f7034b5d16d0e66b27d90ad122f36bae9cca65534a033bb5f53d4628fc1d4c7317f28cc41b0f83b4d99c0863e0c01b964b3bcdc1675bedaf49abad3cc48df2492f3b7ed0ab75dcae47e303e6f8cd606bdef47f3d85d621bd1144b2ca22697df608ae33ca99079c10743404f418cafdcb0c6387b718d5739182eef15cb355652d82d623c35b35438a47e501b8be0bcb369f0970219a38d22ca099d3cc1c0e320e9589ecaca30e20fe114c003bdc6cbcfcd919009f60bf9f3cb9fcc5339d132f594e0c3cd4ff84b0737c610654c4fb8c2661b769922fb8336aec92ac0c1f8498fb4e8b2ff9d13497ff966a0601991581876318e08afc05bb49ae9d778418554b47d98425eb7fd7ab5e60e005d121a634fce103d24793ea4dd961a6f3ff89ac179bb186e9764c401623d5f5032d562f1e48443f6811b1727aa2517b94fb3a82e5dd5ddaecd839826b0003de0a9bf65988f857912901a5bf6837af17be687d94d4913d0c30e07a8c27a7a71b0c6e52f26ae92bd34ce7ba3836984b3ad300dd703c77ae7fb0513c1352d796a106a8ebb4fa822b72b3dbd51c080785dabbf8ba27e161bcd3cd68091ccb47f0700904feadd04e496c410e25dd87864e23df8bb4f32cbeb95fedd9cf0d365fe97a02bfdb7dd3950acac90e8ddea63f5d16980bb7a05609bec6b2d80ecb9ba88000002fe396980cac59803cccbee010d7ecd96cd4dd2370af24db31b74f6bc7acc60ddc02b498d67da59aa5ce91db1afa060b6fe5677df2c1e45a15a9f969013f7c06b234553e2062aa1c50bf55515978f3199c06e69fb7c12eab3c4ffaa30086c8061d306f2562c209e808fbc624133dbe8011440ec10904bd39bd28e0547212aa07843cd538b31acae0f20e3c3e0a5e79231b3a6c631561eabc846046e0e8f80b00df8e9758e4614157b2d0235d7730fd81c775001270f4c7a7939ef53e7a7968014dcfe2af886db37a9bccaf624e7466e735c4521693e51366ffff654fe229c005841c00eb9ca8b485f3919d899491860c9ada167f72e654664fdedce2e3768a0469471c911b3ba1f09737b117015d13a39a022c87de6c10f1ea5475a09154b30a02ddfdde1d60e4ea3fc3a126d2bec2f27315874bd4275c8b2db2119a160faf09a226b4e1297f0f34a2b53d0aa281f92f02bb290f19c59d18d084a4c4919ce40bb5c6afe8470bd50217d42457d1087161d91c073601ecfab325cd910a8cd7430f3bb1aa918aaa18428781478e511f9923f3bf78b0f4c799b64846e70622a7ef0fa4f6f555777328b1985bf0a557f0e3ad6103045aac3f22a019780198a96c8d0202bee7a8934b26962042ba9595d0802529231c1a357da5897f1beb5573f33b07e666732a77f056e4444efae88870bafdd5f2f85fcdd915f890b80b8fd08052066b5e8f9ad0e9e4a7eaba997437b598dc84c83645ae7e788c7ea5712e0dc2100729d1ccb94c66fcfad9ae5983b5b23be8e91c2bdb585fb1f213d3242265cac70497c966326879384f7deaaccf48db34f3d0e41ddb869bca94a2d184e1bdb2e70812953c05500b418fd9f7c82686e6fa81e69ac442ec0b1123c85046eb1298f30968c4075be9f2c3693e7392ef4b015709af5bfead7cb4bf6f77b8b3be936f8308a30c88d0b8e5dc4c247b42bc805785909d6b23dc3acad38186acd0e20b4ada0541f3c5c6b3bebd41a6f82a124ff1e3c6b312e836d02cb422d687fb400092350b5f675a24f75a7e1e30f585db9c3e3198c6411e759ab4b18d12b9dc81c635dc0678a989a20f263d3a6b9ea71f7ee67e396d3f31cebe3f1c711d8bc5d626e2460207055c4a74fb8b1a0deec5a14a919f869d4fba0e081a69e3edf5f8849400af0f3ef626261025d1e8f6d17185fb5d45698a8715504342d512b080e4f77bf61008559a28a170fce8b859057c423ec9b0c185a44881d8a5037928d35263b8249209815f76e7f8af2a7be8b24b21f38b10086f4dc5e9b4c58781176454fb19cb4006d43a4cbe2994078890adafcfcf8fc755f6acde77c7a7dd256d611eb7eab4f4005d6bfdb94e42f6ec5044a6326dc2875d74d778bee33c96438d0b6ed95b90bc0cc9d64c6710e8fd19b97f053e5f704e481d7aab0db82a1719204965958c036a01a256a18959d063132f35e9c2d21a12916302169edfa433921e87c69d45d70d003da56c534f46070ce95b04aa5fb8dfdda7bc183b4647c4058513d06e3760a50dd5b77936ad847735e5ab988e72ed2d54ae40ab656620547698d3fca0688eb105bf6083acb96d0407d60aaa21e9a930a3ef22be2773a85802e91e1a6fbf212201ead388f7fd7ea2735957934e37feae393ccabaf027cb194ea7c85466c202c401902be3e56ca53c22f2d0382f89bada8fe7af002c3ecc08a0c210f6987da78207afa34d6d6b19b44e7500851add5b9e9ac93b912bddd96adb1342aec86c20330ce0a234b4665a8f163cf71d10c5b2fbabbb6ea2e47a5bb3012bec3f5f2e59780311fa91edc9606dc166986e8c912f654eec2bbb3b4446ffb2383244f65538810b1609c76403a743096c8ecfda5f4a913ac4c0830409898ff9a9615c3e2c6c390f707590d74ee61fae2bf56fad7533393c8c0bf3b346ee391bd0eca810753db10219bb45728cb5b7a819fd232eab5a24fc91a5ef94604d5a2e434380764a6bdd029f095b2520b0e031677d1a751214d2a96a3fec9e0976b72992fd1316ef2a6e0d5c8655b6f10f66f09d7ed328837d260f3ea489ccae83c6463e733b4863d6320101b5cfc82621ef907613d9c5e5b243d9c62dfa04eb014683dfdb23aa08847c0effa860af1accd2988d88309b25464d4c3807a7487a3f26ae0ca7a870f61d6607e89d388c2a02051e0d7b4e24de91547f587522862c551228ef9cb09523e8cc01a503a5215dd4179427f7ce6a051e43bb2885ea40c3b245cd36fd088f5c67d80c834333e1e03230c720d17b7dba919b934b44ecf4d6dfca4a544e7edd5b752b02f07b625b6df794b7ba0da02baa93d3c1bd8d94d366083fc9c8b904617dd6f7088ae52cfe32846535b4dcc85c3e54acae89266bb1419869639dad777db4ebcd0b9d5e58f62f315fa1a768b2fe690f25042ce22c83bab2469398de8fe15376a90c88eb276741802d643e8eee37e69a30dc3a82003f1ae0a65cb933638af6f769038eaff4d0df700481cb436c9adbffe73b20e4ffb244b954f042789ec7fe0fa307a7c14c9703e231ff09b8d6dab0d7fe24aa74eb5c9d970e48f23c084fcbd95e0136d849a997b5c5f646be06acf647353aa90ac9997cd4f43fa78cc544b6df2601331a42fe45eb7805c648b82e4d765a34643d6817071974a77fd54a6357f2c30c0f957651ca3929d850344cf6a223bad2c9300063a955129171f81ef3431e830bbff1c5b37940a905eb0e418442e29edf8ddd3e38f344b59615ca54e4a0644e0e7eb7366c535f28c10e36fd2f26294e15a5089b6f092e7f0aa6516e506705610d31c54e6eddb06c74bb859119d4302d684685e8379a4aecb306cfe03466f3a20a454dfd3218afa7d44c7d65d81b5480886a20c4833402ad9514d8b1b10087b7099ecc043120c4b3cf5983210419c65be8011497473e5d555af7ef627a6e34a0019c5e707f31f9580475df1043192ae515e1468a00c4fc5aff25c8be3f6becf106fa95969a9d3c34ec2fb092baabb6f93391fd5a56fbdc38523e6547857b30500f9f25447ef4f66a7368e035a4e62c6d48fd42af96503c9aa20dfff5b9c6f92f02d16bb1598d9b1077802c62143b6d93ae1f71f79afbe1837d5faa45dad5f86205cb19d6d736c697fbcd478c8bc84b195b099e925333d48b24ac65d27a3d98b209334619e07141d61615cf8d073307dbbc5e48feae022f0466bbfd502974785b0dc73342e5f172434954f41dba2a43b84791c01d433dbdb6db9ec3ce59ba23c203149b669b82fbc26b700aa277d611c994b87cb6cbc8d8e7d78d146b84c1e0ff024236c4c877682afd5901c2fccbde7e6883fa3649da0cc5926b96cbfb55980d0f4fd5bb249cf8171ec36d8170e10220e734498780bf4278c5115a8351399b700882e6812dc8562c1807a8770c472956d954e5757ac5156c807a98c7c639fee70e57819094e732e717511674b3c27ec683707a0c7e564da49c889a34c4fed61c066f98ecfe97a4f1197c13476692c8f832acb275ee02c23e8c8eb608004b7d8b06e3237318eb5922c3661a670ed5b229fa6c4a897972c1fe3198d91a99b5615d0cf8c0ef181a8e5312574193d7d4f74493536326344746189cdd4ddfe7f0eb060d18fccc514f48ef23532df7dbabfd7e1c102abf16803cac139c17364c447ea4018311f3d87b58bd67f469c70b69ef00598b5f63515535e846e020bc439fac4702edc016ef997ea56f98913182e19741382619365fcdf50f444ad566031ac71f0af9b968ede5833631baf8f5078ae0ce1f8c6e74b54c595a427a11444afb2dde001e150d2fd6c8eabe11d5b43467e1de50ac2cf54a0d81ff4c259e789a0c16990fc3afde527f324e7957b8be3b9e1773e7b883cae7149cd4bcc426916467c1e3032177e603c8b4ad7b38e21d076d32199f6ba51f326b8cd80bec57c00688463001f8d6e9feb15272351077f5a2e7f2264259175d36601ea51ed896222175da230ff228eb3bc729de9f5151438d19bfafd853e1a654eb2c65317664f64d143a8d0e08ddd0803811d07f37f185932770f37a052537904c2de3158694dce48f09880be8504461c4df5152c5bf2dad4ef2732209af9668ea8379124b04d927621d8609092e3936836d453a787920667953d2b27808a17f433002ea340d82621fd13701e0158cd965e0ef43124a93b90d21c4bbd71ad56389ebe5a2be8793bc50629e05e0253bd975dbaeeddf895b63241540b0633369781f28c56bcac57b7094aae90ca8c7105edf271acd0da1dfa4092419723f9fc15b365ed714c9485f4952c199022e19885e809829ef6c601396f3bda4c614d43ac6658dc8b488f7f47802bdf00747254aa73479709d1bebf803e5ffc3062867511f04b0b5cc3aef5221ca46760c804b0e0161acd635539f0712cee20563b67c74f923e6ef5db5f3a4a7ebd70e01cd4b86a93384f26f0ea9327bb14c52f41a11d1af45604ffb1ebe9719fd10370d63aea216bb614d0fe797ce2f93cec9ce02af6b6e57faae2d064bea941070db0fbd975c83454d4e95ff5794b93c686dabbf97501af3849c6ce0ad0807068e760a361c1a730264e450407c63c385d543db639edbc24fdb41b98ea6116d32935b078c32a6b0365939a44d129f13721e976754242d0b9b6de2a5c377c42fd386000af81e0fad4836c5d1dffe79bc373015942a460f6a1dc4dcb8aaf1b793e940e70fee97b4396b3872a406203914d591c5c37b32279d181313625533e7e1b770ef031d078570384287aaba887d6280694c82ccd7c99c409a0cf76dbb010389871b0b4979f3cd1a34d12ee79f2fe36ce190ed477a0cb7ab7b932c713f12c4507e55099fa27be309542aa41498e8cf41a608c61b5524662a985be2e3b8c14fba140e04f70bf5f7a4c20ba04d81b46d18ea86f60c7493ee60bcc9913d37fca9ac89bf0881e58fb5a134acdd88ac886c07ecdc507f2b35e1b928f860ca18023418520e05f90f7309228f91bbd9927cfb0263ba920c854d704aaae8679dfc65a8d3996d09c109badeac5707b89199e1c78c19f4875cde1002900f35a916c06b845738dd09d33c00694fec6e7fb2636500539092b466c7c9db74815aac71c94361df720d09ab1be3b4cedb91446329c6138fb531dec684f857d2e734adc3ecd79dc92bfc0e454115bd5a67fdce78062d71ed8be048b5e15c2da6d83d774ad21044074d9e07e352d9f29a19841d9e7e4b91a7fa238fc6e67f8a0c2971533353f96025cbcd0965db8f203ebbad46b176aa26b7b625fc71b933bc57b9cdbc73724334e7b01b0f30b213c7cf52ee821f927c6ab09dded54b11c04015ce0c2f89b8995b2d458c0c78705a6869369eb014ab45b7a84244c3f77b29c241f65c6019de8abb02fea7013d736472f1ae40e6a96fb4cd6e40c065a241a069c0a4ac47225a748d1cef5e010dc2b1e664fd8d95cd6be59a8dac3d0697ce4355c89dc48554896956ce32e509e029d4eb2882910cf48b1057a6a77a29ca063af51b5482ff7883e719bbc68f0d195dc9a19f539f25e9d4485a9fac69bb95a1c23493d3602851520048b414d10753b32fcf2827b8e7a486a6fb35897262eca2047299eda7c782661d92f11f0001d8677ff040858d6246eedb22ce53755e7cae4dbeee415ef08a46d23c8abaf603119f21072848060cc6d7646818fa2c818db3bc89919f9b50b57c67278708f004b7a6a89e1f54db9753d4d7ecd01ca134623cfa2d336152683e55c01ebc060c03 true -check_ring_signature 599d4cda4c1a6fa8cd33178158178fef06c7fe1b3c3fce08d3006ba5aec1bd8c fac133f07ca120763a0a12eb89dd4c5245f5b99ebf143fd9d11c8cd5e9e505d9 58 994e57106f3b142ebfbc4eb85a8015d5f8b58988b92b52dca04905d02c0d1af1 d2a809469d95d8229794b09eb5585768f1ad6994cac08700c0d59451f4fe3804 12d262e5259aa7ab1866b7a8cda4475db99e5eb541695a6a7f41ce25ac9db18f bef6a1a1a371526ec2ef3caa1e797bf553901270c0d6f32c05adb778d20d0b99 47a2c221accda0fbc148e95fabd41b6ee5df9fad68846c5e7214ba0ea996ddfc 6abad1804c374782d88b70cbbdfb5615556c5c0d953b96eb1835c20238467b8f 57a6d498e6419ea0da5f696194787b80b6e0965d584a33062426fd086dde04aa dc6c363e82fbad93d737fd03bca1d76214cb10d9cdccf1c0312fc09c1a63fa0f 157a0ee5bb4aabdd35013617a76ea2be6dd623f02d52031071b5b9ab0fa9b6e3 950482f3805331c2cc5f30f2379884ea7c3cf26adcec34f38b00531727224300 689ecdb10bf811c469673c9fea9ceeec159e9e16e289400e774139ca98f45b5a 52e05ea5707c0a01adf313e933b48113e3528db65b06a5b6d0567a08f2cc4ff0 319cabd699fd667e515ea25278487cea8b42eea022f7b546015c5b29d601957d c06ae284399a48897b88d654a5d5e3ed3436d7bdf0be50553bcbd37773553628 74d452ed9b6842dd350ab6012b20bbf9ae4ca8439db1dcd4ca647d693f39d1e3 05bf57684d083f3ebeebb5a64f94d0d07b12ce24c3a3a5deffbbe94a2f64e4ce df703721974bc7ea84b97e355609601e681ad73a0951c37314d7675e5e6f95fd 28293cabb44d1baa014a2fefa4c2a5f8632b58bbc58635fbc4654dedfa6b2ca8 a749ee5e7e921ae783157fe871b3fc4c2ec7ee65bdaede2f348215ff3120e7ac 539270565645b9182f09bca6ab53c7d6b3a4bd5ceb7dcd5309df9dbc58cf31a7 befb3a88b0177f1b1eac68b010a7d41ba7d8c09009a080c5c1d84d9c822920df f09c82cf0050d149144ed83fc1518b0a1796958a39afcbdfe14ca3c5bf543332 a6d5ad9a5b02f23ed84b22bd121f089f3add29038ace47ca0b9f7baf25b89b0d 44aff964ee7debbce83beb75714d6dba6b3bb61aa1b52100691a9905c24a54e6 e652c9737181c1284114198d2e055b4e5bbe839346e80bec22e3a52cefee968d c4e9e5d14727bf8d264f11af850b85142031a24bab4d3f67cd00aa237730822d ad0b8d3833e39181c2459fa5b3234c48ae990b580348eb0fd8b9d77f5424d863 d828fa967bf11cfa7ac3bf89af4fa922be23657a58d0e22d639e610ce43a0b81 c19d1b88f32a86147f0f01c14466f077c6ded5acbf320636d3a23f2c2b2aeeb6 122a85a6a48d729f27f1bdab9924100f8bc994de37f6d3ff7ed2f0bb82c4df6c e2fe10f65d3a29f9beca51269a6d19a5f973e5aad59f82ebb2e5aa950fa248f1 3e479ca8494b0d421ce6efa2a37b9638b7b200ffac8de9e8a024dad86dd1152f 6e7dd056dfd2801d8e663a290358afae51eb99cc2ec123d2a299bc79371d6b4c 464ece86efea596ad44102673768134cfbbb60e64137a747f08fc5f9d35b2513 4958f7a748483730bf560495c85aec3f9103953950c1be92a16a2f7c77139837 636f317450b81c764332e69ceb027162521228fbbef45ed5827db51689ded936 4e5d8d21a92dc6f299def9a7895f73c7e0cd3f3ea471b5b6eaf9f7a5589c08c1 5f7fea2aedd5470504b8af64e27a3186808de0e131af321446823bf88c1ae7c4 71a773237f61786444eb213d0832afeee098dda67ab8202144a171dfa81bbe5e de98dc0b11a4633b22dc62874d76a023e0112dbbe3e201bd3f29b6eac5ed8abe 57348d46c604f69d63ed51d2484932d70b3ca722a7cfa2e61f552073fbe1eb18 151e3925eba4cd784842a241875ee2cb1e4a982519ee60f7d96d7ec6d2d4883f 8402791ef568564c4b6e2ae17094b725314f86385a5425d5dc21bad547987a0c c022191eae2cae4e15be7478640d5484998f9a563de5505cbf6dc2f9bd0c8de7 0fd38964c0a8458120e50a6ac05a45bd6656942d1e8a7eeeb259837e8f5a3743 9a8b52a52920fedd14c0c9b5555931cdb8caab168f1527028dae3a90e17a939f dbc695f70090942a6c71a2e79820bb01a8f9297ea966735633b2da3532c9efb7 310de280ea20ce89cb8c8f69d1132710e85e2ad20fa94a7eda73776c11370d1f 39bdef0c203b4e14cafb58d29c27b7ca13e29d81549ee5c5abc34a0258de2404 91c5df19c4df3a1a275258b8924257ab771a4487cfe224571a8a2acec86a1743 95b0df6a08ad5df5ce19c38e241167803eb871366f905a2a527f736051d72cfd c819c1ae8b5cfcd5b84bbbbdb685adf141d1e7492a9648266a77a43d59ba334e 8f7f619e179a0c9c6be12d2482aee9abc8fdd8f972bec9ad2da6018a2e51bdd2 fdf8c171e8d7b189e8801018a29f38eede1db1e559d20d3c1930b8cd485a1439 a47a62111cea775e4d2a82f8b200dc61db8fd1c5f3d8103adbcdf006dd270928 f740fe2262c2b40391fa78b46f34e56751e34d7ea4cdf2aeb8410392f5499fd2 ebe96581af8d070926e9415f8989568a30f48efb18df87fbdb924028b0feb7d2 a8ffa7b49d39a6b23af9bf1f62132256f17f91d1aa2c483d67fba4346437e684 3d348ed9b222d6b51205267a9f8aa0909a52c27318a68c0c86f84299cab9700abe3c57cef439295b2d269da17e67a4b2a1e3f6c03b3d26baccfd0b4a1f3a920bbff6bdf313840a152c372d1ca0903e0904c1108b212a2f0def45c1ffcfa9800205bce645e9720c8ad102dc1029025931e139b09c62dc9a10bdc41f3f77fb770855d57424d9208ea97fa2c816a907e4c677fb71f4a10a88f6eb4a786da4e0d50c3f0ee82eaf579481266cba279e7c6db83494eeeb42ed43ef26d4d79f34ef4b0412c95e4e12ad87b0b18483a6fdcea9d0c7ca23b3be74378bb2edcc82804d85091b87cfa7a5d57624aed0fcb07ae41e0b1c345ace0432c3497f92449b8b0c2601fda79e628ddfb1e6a54df41885267a38959d07d437298721679e46cc8a236f0e3a8594e81f3dc51f3e4d3f68c4cf879f161b4ae849dda7076026c40964fdb9073927ae53ceae7e93affbed446c744106fa68a176fc142c4e064a2c348e51970b9944433744b176473a6be19275e6db0cd0bee32311ca6100af6e494d031d360e3d510bf4c723d2b6ca39bea155bb23a3266f1c2fe7a9d90be16ef93b54893e00cb6504952d8d52d6c0cef8f59bf547bd7cf6b4619671f07dba3fc8759643050d8c621bf12920221b56d34addfc1995da22dc945aa14f1367c6c72fed90f0350085f57318ad9c7e7dcc96352163519b4e60d93977761f52ea9dc00c2806a51906304c7f1d1c8e1a375e15febe40c01e858ed303272147665798febd73c0870005b4817645dbd8fb30bbd90f4a819f1c4fd1a07b0e171aed6aa6b416be6d565f0b85612d970d223caf90225b58210963330aa0969e9cf1f181a6efe2ff5119f00895e5dc4a9d385a97b686a9be325280275c6f22a833cfe0be5e403daca65a30083dcde5696064145dc329a19d15032baef59f45da323a6145085e7506aa2f1c046d35ab7f70c5eb0c98e22f06063f4c6b36e081e539ec31edf6fd05fd4322e104f5870b6fa765ead7b0786aeab489e174927ab1e7d14796df38da1ecde36ff10564c3e54e7017cb6daaf0f642260ade0400d24aab3ee2852aa55bca5084b60003cc9dfd738953ac25eff29781fcb4f0d5d8e67c0d3eaf9a2a1d3231d200e4800f46e893959c4d9f1660ea8a8f4089267b898b66ed670ce06e2a6aba5cbc734e06685fcf34536652a7364b114acf181240fd654f5bdf4ad31fcc8b433daf7ffc0fe016191dc93f55b24fe6c9d8c4764b3099e04e08fe2a2cf8606e977f32ab3b0f5b66af88372e06f7cc683601c9b4eec413adf25b27a802f6f3f3da2075a8550d40ce45e486a1c841458c386b1689291f632cd34f0708f3c7dfdc1e60c2871603dbee5e993e157264994b87945a41204fb9ab4671fa94721c3b9ff6cc43ffc4048116f23e46ea513518e7bca720a3e7b979b1d4cfb2a26cef3e8496cc001f080cb33ea096b3eef0a7346c745008fc4494b66de4fd6a37086214e303eb6b77f506af6223b2547779fce917f089787117d7bee71c93c2d4c7a8b80daea2b35e0a0e162645e3ff5936f083f214197aa173f88fd69de16b4d2dd6ae1dd6ca64f0ad0ad8a1d098c27805b0e72983fcd44e125b60128e968cd187236fab67c4119e40022ee593c4556c0db88a60948b96e7606bdddc449eeb854f39448da05d4fdcd203ef35aa3a5528bd3f091d72db7b1af8b41174865618f40dc1f5273caa5694c20f0110a00cd42f61cde0524f4db0d9b812f1816df58c2d1dab1895bf07f5cc670580b7581dee08ffb4e138b8cbab613d829ebe05d50d9401a21390e158596fba05ccf55f1ab45ccb805f40b5b1376799069dfe7a954ab56428e52f6f09c1e8500654bfac7d2fc55323b7d823ec1d9372f041fed2723d53a0d4d048251756d66d0469d0d39c8d7fb42769d3d05833c91f586c0341ed46588bcbf532b7c503b29c0a76b2878c5e16c4cccc2c1621cdaada14c2c5b52ad3f192a159ddc3442abf59058141091a3561597166d73e19750ea55805d1ca9b42be10ec23aa9f3776d76900982812a6d67996619cbde7182a3a3562ff38ac9ee216583204f3e49b9fdd0b050c82bdab380aa1072578d159f8dc4537cdcf674e5ac9e9652d5ecff39218980897952abd5edcb413fb423b81226be235c87d10caf591f0e3141ba324d7f8670261ca49265b1307a5b3a6b49ccf32ea54326acee209f29785ae046983c2c4a709e2008db5d0d4790cafc15f4591749cae90e23eb7fe3b1de76e7475f813ff7a0913e7d30e3fcf380c1048e1b16315361dfd62fe97efe508fd039449e73318fb038a6eace005c6d18478f658896290fcc388d2b27e915050d4ebe77d47e062580e8f81a2d7306051dc7458f641beb91952d40426dbb4fe0f7ecace3df496a3b8018e3a48ce2158a7a8a0e42001f1762c88afb0accbb08538eab5e21b6873ddaa089dd9a42bc30228590b72a23bfc9abd18d3905100629e2104ebd6323472c21200c0cf733464e00ac3c09c43729a45450da4a30d8e037e9e1b71eb0a12c2600109cca40ec986bbd25cc4189b98339c828761d5d13b2afe8815611e40207783ec0821d81db8e199422d5f9cb642c7495f07be3b0cc6275c568acbce8cc61e68dd002495cc9fcebba7f5dc39e36d9bb6d83cf9b4cb5f4b83b1758a989de7c175660b17ab722f0ca1d57d2a2ca58908e0de155e60c05774b79cb7dd86578aa60f850720884a9855ce874ae110e48ef3a3e159e84e27d29da51e78b6b4f4139e76b2001fb6f42cc7fcb1103acad13b7663d9a25967b5e182b52fcbb49a81ded6e4f3027ce5ae04a68b7b4899883b2d94b51219e93e1539548adf7a334aa7016cdd81011a85c269d4f32ee73a412f70fc1ecf21e0e1a92cbcf2674aa32092d87eebbd07f31bc9a6f0abbbe08f939afd39ca0b8dad367f932e2171f80360698c8265e9058b91dd7140c1c89b9c7cb9ad6b39c2802e4f8b59bc1fc08fb1ff925a70dc540775e035b2fdf8ce986087144df96d365af65575df2ed1f3bfc206264f6bfd140e9419d5ebc0ab93f5d3ea3fd7cf5e7371db4d157f50f40cc4e0544fe39db5d80cb583a52474e56ab8b2900aa68cc375981edfb3d66b906c48426f75f2a6e4ec0e5bc8ee55b27b0c7795cad22711090fb6541f3ddc1f4830e6b10578764d429702a6bea02513179d385d8bc59cdc119bcde886458b18f67851b26f04f6b551ab017666c839cbaabd36ae528d0903c30481e620c0cd0c46a5b5dec30c8342daf00adcb2e40580a57ba82dd062d744620e5d85bdd2f1bb901ff53dbb89c3a2f8f401f1bec624069aa44d65bff6cd840d11b1acc1cb5a807d7dac2d7e23e70bb5aa0f4a9d7006e61413094cee9f48647f3c3cf7f8afc77a84c8752ca0344d220d9e025438f05063f8676ca5a63e4a0aa39b4b682eda64ccff92dd74cd9e3b232f360e4fa791b9943ec7346b020bff2f61df9d6edc17d686864a76c8b4471e612b7e020c0b68666fbf521b141af50c2d0814c13a5d5150d2be28ba08cdd95c7372460dca007a8a2d4384ae54fa7cc32f7a48a37d1502657ddc6ca449f1144fb215d4082ac8b4ae7794871022b2f0950b6f01830f1e5bb0d3e9d9a02af2e240eb8bb40e7cf493cde7b0521c130bd1902031ba1334c0a32ce87c58fe39e7ad117b1a4302b79b386a7ade3da4f2b10e71e93eca5fe512b729c873ff86defc0ce4954c7700399aa4e3a36e502eda983a2bd4309ec75ea4a42c6ca110df7abd80064162d104008730aeea6b4123d99447776124ea310ad26310277e71e7e809a361a65f3507843917fac23e524afe27a11dd98b3fe06b0d748ac8da7d1b0a61360bc6de3b05a7af5fe1c769038809f24444def0a3eb07966764dded4f2a6145daab24d07308022c52ad5558b0e1b3c276caf40bd68b419b245cafe5e9f7abe9e5f26b5bf5056ce3fe012e19f3e41d0471decbaa0c0ba68fa33e211a1716704ba73807b95a0c8491837febf567267014bed2a1ef2234a67a4d672378e64540ad6beaa415fd060b9a20b8139e468b2167f7f83df15f24fe93f4202edd6232a175624d4d023d037036b8800787a7ee81904caf206dca4971426343d5f92af4b03923a339943e01496edc5b3ac57289d6e6b3138f7dc023a873b7d19ddac480e58da4e91ed2c10fe626b80a428c0707d8dbe79f4d9e981f3db150012c64a7b5cfa458a27fa3560a574ab5e86d8351bb138541f88ef0a826fb94c7de0beee3d6f45c1aaeac543d0f8091bc880a4e314c4ed0f2e4da52afb1ad5f4620bd355383f2db1caf3d627004563577b5087fa3a480a376daa8ed54e63022e4de7ef00ea1d2aa8e719a12c90450ba6dba839078d7dfac1f28ff1b2202c10fc8de336737a92564e8603e26790dfe1d14f245125f85d5768e6b0c6f450ebeaff30fa48233882d92f629951a7705dc231573500aa5f05c5dc6cefdb48b0a825c303968c845bd0ad9effdd553f40df48a8f86b809af23d13bdb622e213bb168ede8961b8aff6071d4a42492d30301c5c05c67abcdd8538dd55ccb65db2868860cf5941f0040304d7318b745b3250b7d63b7bd42606139af252c2165b2da5b802391dacbd6d18ad7c3427606414f06babbb13597e48c29eada39183f9ae7311eb84e5f2540b5ffd19e636e5a9b060cc806c7b93b8fc1c312df87c27644f20ce144ec701a228b36fcf6beae11e0550707ec0c6b15526ca60106a98fa756ae7cb1ea3b9a2877fd22d08db5e815b2c40d466a37ef6f76c8b3d26db894053cee334491bceadd3e6dcc8143f54c3afcd009bf9df52e144b9b73e2d4554734b1af4ddbaa268038f5ab8267751750c60f2302415bffe08adc1cd8f779dcdabdfd6f74f25d70c3f9e0911e31c2e02e86b0e1080474e9b704b555127be3465719f05139d6b0ca2c33496aa4bceade46ca51b90f0e0422004bd088b42df31d8e9ec9d2bedc1efedc583d2f62813fe632cd73aa0e2ab831cbe4ec8b8f64575f48c76092495d17f9eb49bea62c19e22e539c4fb004f0bddcc8f9694a6a2d9ab97639d6895b94ffb56efdcf0b82ba026c8956014d097b0735b8ece4bff9e3f587a8c4a5a94b508f6ebe1d8c2069f8fe354fceae5a08248b4a1f350175eb8ae4dc3628fa254d3ad595b3a329e040ddbef9a1cccca40e547d1d3e38d21bc4a1a8f075954234012a1a00221e54dc9ecfb1486606f603028cd6fc70bd8580961f67677024804b0d7d01f154807a360379247de2838f5701 true -check_ring_signature 024953219a3a2b2229d2529ad02d7f5ed75d7f539e4c8182aa9f6b171dfcf4f8 ee4ea67f3c17d59ff6073d80186b26629a3bcacb82311d6e4ff95fa7bbcdbfa7 1 a3bc0f9eb5af5cb10fd1deaa04c51641a1fa954f0d11f1b6bd7c9d4c2fdd2145 75d7e32908dd6db2c4d6633eb9d7281cb389f0849e8878b8d60cac8d9652c60c6b823d91bbd308f171f648485da9f33641b6e868803392b19404d64339db2900 true -check_ring_signature 52d646bb1e40148c96fc24dff5c0ee95f76d314838f3dce0b4bf24aa38666ee4 69ea6e362f81b59bc9f0046f38add1767acf16d908b9ee52f8e16e322b9beb8d 3 91512a0f32094da2d97cbb3206da15e2b0fafa6ce089bf21381b336630f4e378 ae24f67c39234a92d6d12619b901b08f0d7a0b6f95ac3f9a5c287fa852ba3e44 3e74237215b2408a64b1480af48fa4aa5a7e84b1280600975fedd22f90288e45 c26cc89748b18f49e60359ae330f890a2d62b2890794535c2503ed1fea0e16032797a0d2cdbdee959a861f1e59ffdfc260cdbba3b75f827c3312c24c71073e09e45da2ac0573e2959d4eab0ac804a3c934c7b216b3ec40d825483c12ae4df9068548d02588c772980780b14cc03c0f9b587264c5276532c090bae61dcb5284ed0495b9097a11d87b2ce8aa1da022e211e087ff66057bedb39843db0c83844f0d43e55e54975218b3347137db90cfe85802b1aa80e631f78c3f856cf7112ca409 false -check_ring_signature 3f8e402199590b46561e2a7a39e18f1421ab5e02b88b365353bde802a597bed7 b057765038ba36b0198acbf95b68dac7b51220c780662ac5fdcb6258b97f458d 91 a7594e9b734b10658a385673d71032aff7b5e91beb0d019e543bc24c786b25de 10d5891698e40a23d7c0039b5a22dcd0963528154d9b1c7fa22de16d01da55dd 47e69779e66485fcb7ad8de05ba1c82024afac488ad08730567df6fe879ac770 6ee458d5f32094e2d98109faa7daf07f65cd8fc76a13b1d93586df0343cdcf37 f6eba3f5708a3b00bd7b588086ce80f47518edd8dbee9496fede6799a94e7e9b a339d4e18d2f0c979102196503925a814912e37e7ebd4dbb1522ed3610a672ac 928b7970e6bfdebb06047a5ade56f2802d1629e0147b09de352c3750eb06d324 17bfab12364ba832de03f7c39d428b0dd10aa9b56750f8a1ab2e14e9c26114cc 97f28205c41e2ebef30fd5131c464bb3e7b902f0f406479294fad6b9fad0eeb8 302681689b5d505258fd892efc7fb544b900751b172ebb76327274e1582ebbab 4cc447f130836662cd6025aa9af2a4b5abf2a3bf5c6e1143a5064f1b331f60e6 80cc990dd81ca2c26e34057b4d7d37a84db73255f57c803218dca0d2acebb116 375f59218a1e45d0bb7e8e13401a5df8cd4131a30c03eb9f7fbef02da6ced4ae 048ca7ee14f4f9aac2f3cac1c58e5656d03b5dbda78786c419f5e7b3faa7959e 8c1dbd37b320f1852867b69bb4197f1bbf0a3a93bfc2bf2e139e4ac5c4f602d6 1f5ed16b38425117909597ac31397bcf4b7a0ea5d145b667fd88df032419390f 930f7c77300140a0cdcee985ba43ad9a6e5058cad453a9d733604b01bbab3664 ce0fc9fb3a5c55a8d010b20a433ac669e250b87f766ecfad4557da99e3d8696c cc5b872315b870caa6c55d8e8b1db5e4a0757cb627ba63f2e8767fe666651c1b e8b7eacaedaed1f94d8dec138395d204ee4be9d7eaee010ef24e72073e40e71c 39ec7f84c4ed6296c13615115efcf3894824885d148320337e0bd234f48efdbe e642e760e60951d5298d5cff1391c3e9a3436cd662e0c3835d530fd598166ba5 1f51d9b4daf1816947933730e2e5dc24cb188e617e5bb8050775b347bd32a979 260ae6d3a1df5029dbbabb1d11fdd1a69fe392d712be410603bc2e2799386f3a ac0365f427d9fa9a136fa3f952e3e5664416b3abd36eaa2e8c75c276f4b5b380 f8de31f19e5754b703616ad82989abc34c6abf5e32a776c377c1933e1899717e b40865394119f8e20dee131c1e3550af5c4dfb197335b6af6ad44634e17db4dd 5d5cda50574030a962dd2ccfff374ef9cc1d95aae7740f89784555e9781d858b 3aaea439976302999c7ffa9a7affe92fa13b1ee140c14a73ddc77850a0a1e38d 8adc6ce9dddb6196afd4bb8f9d7c3f83903589f49e6983585a59a8fc41cdf1f2 cd0990c1f3e24a07659508357f645fce47c50585099a49260d3aeb9771c55614 663a20ad11778e9b14147172f22484a67555d2d3e9602b39cb7b6d869a9f2855 d0fbad9bde99f9c74dd6585f655adbc339b69f2d7172f7c79d13b0d0f3471677 bec39362fa58672e05fdef1d918f1f49ac665e5a7b2de792fe606af1d682b3f6 1e7918b696db817aa49fb7d7200bacd0858950254f81dd07cf4293a75f11b240 ac0a06023b5861dbdd46ce271ac04f9c7568483848fea2592a29b12ba8e311e4 6308db943953b3aec572a8cffbbd140233d5954b0e122a68e85c4b0407deb6e3 3d4515cbd260dd391e5431a476a6f4302cb8c783cb604e049c4f2c4d84a12fb5 e4fe139b768703d92c29172c6e78b4e99f59fcae6587b7040b3e84119a12a2d5 e0f1d57eeba7c3eeaaa131bff8f9c2868fe6fd1bca4934ba0948e6fa57ca1955 f21fd66c08d56920d108205d22a3aa1bf8cb009ca397c8e7749562a08b016599 ab2ee77ee71914b9cfd93e7c9580840c841a408c6ee9fee41722360e30f5304c 34208dafe5453435a11ff089463dfb1b7751325375f3b7588e54f6e57a2c3ce4 38af9cc160e6f880c3d1ab2f9f66f78a7538a700b37e148526e97bfbd6ebb5db 03a04fe7d4c39a1b08e0bee4ddf7f072428b3c8829dc33f8bd2a7b589414b75e 29e329fa814567b4e58802fba885c63d6bb3a281ac4cb8a3ba138d17f213f421 5244e7e6b576bc47fa509c9c2cd1bada32b4f5e947fad45893cee389e69152ce f23668da1ed3be15c1dc9470766643bebffbf61837a406e3d64918d5205c099e d538b21df4ae67d5a554f863fafeac323b2ef27292338cb3e0836867e4e58e10 e37ae14c93dc8d8b797418caa83463e29c1ce958ba08116147dfb053e2f78507 fa1f93d8a1dce32c9206c3f5253721aeb97fbeb5c4088f5b865cc96a1076d3e2 71f5d1479fa61832be28f05cda728a644e5fecf07636cce4c22caf6d7f9c5dd1 dbd923b83425d694ab0d0397e273359d7193ba770f6ae62250d8aeed59c60d4d eeab083505c06de1aa7a504b7e3fb59c47dbbb6abc54bf80651698ab105b4dfc 74f142c3a30e46af996d3e25ed9d7475b0424e5889c648296c06b54b8b604033 aaf62b4204fba5c131fb423e034cdafaaaa7c10d2d9bd0e00f2b12c4b24c3d91 9e2daccd4b1454ec0264f0d133513f0bc99238372f43090808e8a35a373271fc 1c8e32fe8a8f741ae412b4b9e2cca0f3ccebe7c9f04925b5f9143d2c0588a99b 0d5c9d18034951d01d9b792d6d72c5c84125c50d334d99cfd26979cab6faba37 a458e5da5f317f533eaea85dd57cd7cc94a33103d2bef5a36ec1a942e8716cd8 c0f7c7f912bb692b839b9257bf132a667b93a1c264dbf0cb60032990df3e6fad e8f301d352c45cd2890336fe8116ae58f0baf016471a3e90b0e0f0fac5406c0e 5466cc7711c1b35ce886c54334511d9cb73b085bc45fe9b14d1829137d9a769a 6ff9322dd56d3d6ce6bcec1ddacaf93af393fcb7b6e057b5a851bcb081005144 818ed9f9fcc6d2d86fc8f45db7050794104043153c50a55d0fce32d21e89fd1c 125f6ac514046fb58b9cfd53fe9693e6fe5b2dbc86c4627b64f9b3f8966d274c ec3dcf577d4f45ed8c671d507c4ef109ffd7ede383f0975a66f138958de7d61e 576e4adc73b9513497239a1a3fd487ade4677bdd32dc9f600dc91b6e60163a27 05a6f58914a21e618cfe39dc8ac15f938db36cf851e44cedd1b9ce014a2beb50 45feba0e98ebb395393d665e2e4b84d92b266bfe1766c942e942f5e8da551233 ac0c37b128f6eed6a0f538ca565f850f21b1dfce3738d719c25f51009a93c83d e5a7ef9f2ff4108887f752a95fff4dbc53c1d9fb2833c8946b2628338bd855a3 835fa61fa0b3e237959e26e2ba8b1d9e55ba82fea72225c269ceeb3cb2c7f938 81d73888ecb0c862f0af49a8bb684554fbe7f289032b5bc0a018ff3dcb39f94a 7309f929ea7caf60badbbbd02fb76dcf0b37011067a145d266616f80fc6989ed 92e624c6520b6ed1b87c352d4d53d57533af5249c038a9410eef32f1800a820c e6772481161db18f3dab52b3046c5df0bb5d69ceb5f5bb7aae721b0c1964ef01 52cf3adfc54aba6b77714a64373927c25f78e7a538ccd132b4273ffbc3ff7c17 1aaeddd3e22725b010b57b5646c57da87521c81a75ff011ae8d607e647d2a5c3 1c9dfda1364dc614a35059de152f837a7f521ad1c17bb8bef407fd1983a6f2bf a510135fa8e1b8f0937c7ef5babfc893861d905ceb52ea21a5a3cee216cedb2f f99a4efe6a8191ef50098b74586a4fa162e585e903cfb05d1dd3a30116082c14 579c217dbb8ba20e777a30676323f255fc3f7c6d2f13417edfd21cf5f9aa6aed 3526c1c4ec84f1a449ccc077459568053034c46f7f0afd49a939f969d45c54b8 6c8bd9d6d8e3b2e5cd3cbe944c7f3ed376e403c44e9e0adf2ae647bfcc09babe 8e34ec451db882ba39df63986e9b387ee63b7f41a0e81fd5dd31d092635fdc04 3c7f38b8c627ed60f387e758933fb234be63b9bdcb0bb701d58edb46bd4bc5e8 cce8f99608580ee8932ff3a2e1cdca7b22b310936d5a3186b8655c3ac15a4f8b 1cabe2a5f7a743a972afa05254c012ce99482b4d0aaef9d35df72bcc2a5c2ab2 682e2b6f01d1cefe180b6292b0142de39715212656057c17cf482661bccb7d13 d96c44cac82b4ae9a0048bd82b14df41f48dd620c10cc34ca2b2e4f6dc152b64 87928cf7f56b80b965fd3bc51dc2d9f7fdc09880d52c5a484e2bfb037a86fa045ba02235fec89a08382784f0eca7080a9da1168f233c03ec7d2a8aecce55930a0c14efdcf8d2ad87395d91d1d7249fc057228e19ccd5606bb20c2b2b48ba060746cfcdfc34e47b62f530f43bf61f5d28693c6323809d14a7e64f5cca64ee7e07c9254a2b7682fcd687a54109e0c8b91c0d1f21cece713bacc6f0d1511cbd820830de264313d1690df8b877eee9618d6b85665493e23c062e8a82a877215aec0fdb02855e363037940c97aaaf2cea8e4c71fc5e0d81a4e603466cfcff3c8109054efb2601b6784dede8d4737edd2bef3790be9a6d3c1d9fc1426f692da2678604d5a30fc91466724c40706a6d3221596edcb6e55ab55e0fb5ea9f70878f35f30677f893284634eea10f2a5989c3c3a6895d122dfb2779adcccbd5df0a352542070dcaffaa459eeebcbe683036854cf4e37e6abca62611bcf5138f09f8dbcf740b8c5c6614aad7b7c9f087a6a067c17b3991983b3a1d573a58da8ec55fc2542e04b3fa7ca6331a64d7e36344ac4a689276d7f0d4a011c0429cb96c4e0c6971a40b1a621b4744ec3e84501a20c0007ea3066ca8c7e02939d1b6200887c75620500e25b0d71ce4f23af65c3f7352a0b5a18c58399925babfca5aa26072600d7b1606ce14e998801bebc74717f4f7a1275343331c4b2fbe953a18966d5b9927984f020f77af8963b6fc035208dd56e450ce219188a7fa72196394665127d063d2ca0ba08a0f030ce45bd3ac4535ff2982cf8a33440364b117edd8f39105eb7b4829073c040c70c6d703180a998e80572455668cd3ccc95271a575916725f4aa893704ce5bd9fd93769097c6307544974703fecc9004f0fe1372e02a7e294f491e600e93ce6cad5c219ce4b29aea0342068b79eca5ba36728afac6ac95dd7fc56cc60ed7d23e2577164f69aad19bb9066fe741fce478f2382c8f91c9fe8d8b6bec1e0e6b2a478480bd768f286f1de66a1d392a095539e7b5312ef1f62378c2ce301f0aa9ec2bbc1b553422baf85649978abb7976452b7326f679ec843684dcc8f3ac03a1218117c64ce6940a29c3b2214aeb6cc4ec793b477dea85798eb40d460b830a3a5ee5f3368212bd2c12595dfe0c4abf7afe769b7150d83ad2f23ee499089a0ecad18786918a2a6717de769cf0296a825ba4f0df3e838f179551aa66d7bca20fc47b36a53b643c2f8bbe5d428472bbf0bf7b6cc8b73d371403e91f7bb186740394951e44a41edd2398f23d37b18df55de14bbaaf9391ac66aaa25912727cbc03943d88c1c145164c2316c64a9fd4978dca1695e1283cde1fe157f55049d1fd0577f8bbf815b835b96e29f08fbeb3af5cee654b7b1eb8c59f6bab80c0be48ff0073351c1a4cab8f61ff474ea94297c9738cf831cdea50ab4a3d1cfeb21102ef01b32aa7f2f420423d71d90d33bef58abeaabad43584d4352ab9c329713930c60bcb244948a3ce4b8c92e07e25f40cdb0c09bbd230f6b3c069b5139c191ebcf00b231990b258e65d11f76cdf8c35827c9f1c79ea2007ed3dc3f0ddd343f02aa804909aa22d39a4be323a08328e9e4179fd895335ff3241b8b612bd576d5df66b04a3186c8675de2989e5340470904a53658c884fb3b97469110214138e3c82d204b726d5fc5438aa1851e3d4b9480971d2b2328b87b0dc7ea679ecba95e5b9a90eb2c7ce706028708b7a3632d5b6bfab3b46de5b0c24999de37f87d005014e2803f9345558530e9200f5606b35fe299ec0d86b032cb71426127dbcc7ba94140304d7a53b5a1223bbf63dd365d06be1a26be832ed4ff60527bb6c6dde09ecc29b03a0c9fa6ed76a30a3ff951f9ed542a4ff2aa19efc55cc1691cac14aafaf087801d264f2c8dae625d8c143ff241c26e8bb30b81b7931bf09c8354612bc9a33220dfe6834eef2d46bf77ee3be17366a8f573c6013eb7dd1b97d81a8058247e98f01a964fed52163d15b8ccb4ecdd3e7baa89c0bebdb0ec0f87ef335152540f0cd056ca0a34e25fae066dabf35750afb3fbd749b5a85dd70cfb677ce3edaad3ec30562a48ce827e44718b4e0491068da747218cf184158366c6f3684af369cdd7b026fde3182279f0a417e242a3a920c47739579b82a693dd26173c976ef865e400b3b7b8bece7c454709dbdd8391de2911031d6765c1451c2952bf46c7797ebe00489ced7bf9efb077ad1f483383f14e579e4bac6f06969d96454129d3dc394a900ce974e593cd75bfef1f140bdbaa18e5035f1f22b6863c9caff1def1f3b80d604ec776544fd212ff673600d5ec5232792b30ce0d0920bd03e99efaea9cf5d2700e3867b511e8ea52730899f5b890858eb8c7d2491b0d3fca72d69c53a0b3c6d09874cace4f16335af43f232bf84b511b32d95f926f8df9d2564fb47c13aadb5040dc9a7ec105c6956d9aafd360df4ab0f2d8d381c1d10375fbcd823b5f13ebd04fdeb5cb0381adb227cb7cc5d96803db605b595a5ae534892c447d4435c466a08e2720c9d49073bbf3532a5b153107d307ca35bd5c8670e4d30702d656e1804007307ba24a01a5a3a3b5a2ebedd64c2dbe2a00039b471d8a669d1bf439297370338e702f033dd451ff6417bfe08947c8b49d15e71b1cf0c7167f00e707c56a302019092d88c106e0380379cce98ff036797728a54b2736e2a7a9118524d4ae30b5821f4858fd9ab30a941597b0750929ae1a7e95d54a16ce82a5d561e08c39309a4523ab157999a7cdf8838e60aefbebd071b0fc4ff704340a18a2cf262ddd1009114c460499b233cf36d220f8517db90cd1f47adde9f1f70a0787133271c6f079678bc76637da56fe920d996d87aaf1844ef19723cd72206e9d1e1dd9465da0296cc25aa2f58a465132428de1e11b87e629cd271e6b197bac9d1d4d89a10660e91ffc60a4a612e25971ad39c7af7acd4eef6c7fbb39e388f1a6e7a2d0be03309d08c7d16f4e914477ed1d67cab21affd59343fca39a7256391ab7f057ef5a005a8bb6e66bb078a4c5aecdb11260dad0fb8b39c0468d5a96c63d0383fea091c075ad24ac37ff5c8d121d68f56e8fc09c64afce39a87ceb8acf79f3fc5c0e7b609e57d21b97b7f223642d19b566276bb5757d68f7c688e2114753f8a1c99dfc80d148ee8f276374889dc724c13fe82b399e04a46903656d754f6f6bf6341a298017ca8fee67c67362dc29bbd4bbfa20cb9a274c4ae8a4d1964ca588a46702fc100981b4f81e017c70542a43b07542c72b13b2e94c081d278e909803f55d21d9608ae72688824f735e685497f6c225422a58057e46679a9951ff9373a705ad54304210d465f943f7e9cf27c657589deeb4cb7dab2e693b2a31ec5d37f5fb5c1f602d92d3dfb8bec0cbee02225d27464a7e415e61d95df236ff70c59e8757c88fc0c1b7b7642957d490c46877d5bbddc9d4857999050846bd58c6a338bef0bd60f0fcaf3df43903f05ce0610fa718f65eea15f657c0b6247621648405853cc87e50e83b37b27f8038777b46a08302b35a4b8546c5638392bb532e65d7e11c66c76007351d4f6f2adfcabab09d3f7e65934cc22d00a77090b5a5b9019432e7116010ea371122260343abfaa2eb1299a0c24e5653b3e10af93c485bc2e5fa26c3cbb01078e03facb011070322809fadb90a77fb982cb306ba58e1b998f3023b895140a7c46e0c737f6e2a987120086dcb10f14df343a86f69dcd2a9a7505dba0f0d70ed0854d83223588fd8eb0b70f3693998d33bce28d689f31a53a2821804459a302a178cfd8ca821eb35aa2823fb69bed78cb6a27218162a5153d6e23b073232b0fbb09bacd257b6c356ad1f23a90e30a0f11b8c9ec20e5066001148a34d9acf505ac65599d409933992589c1065285d4529b47ba572b0dbbe63e54b59fe3577b0e0677b9482f5d8a1f377dc781df148004496c080119282515a03df0461a46be0c3b75e8e664658dafd355619cf112894c160d4e92a90c011e829618a7563a8f0799f9fecb1293436200372a6bb1cc942e87653df260dd18cbda7d9cd909610209946ddbccc392b96e96dc29bd6b0fe8034c4e45d19d3f0e4a6b8fbe47a6656903b065bbf3e1ce5566dd2afaced99b88ced6d7a4f50d6487e8d906ecde8e9090003e76a67ad0cf3ba8d06737747fc39dc81cb05ed5dad64bc3951da498a0e87709b861586a0506f322d71381e6cde13d42ee860e6fca064773cdf8dde68ed4590e70b440c588d689f59cceb010cc0df417453be903e09d1669b5f619c2a16149037f28d6061337f67578455d8a607615d9131a3cc5bcb76cbf9f5c3c8ec6e3a7056cc6db812e4e9b927f0efa1cc0405e75a0098b7436711dab812ce316688c07054361279079b63bd1afcc600d000469eb0c73013ee990d41f3d4e1f181edf9308512b9afe38f3ed3f875f5c2c42f477e6fd4be7af471ecc5bdd03b0960aa2d00abe7e6f3b81eba148f2d2366f3d58297cf0dd53726ed2a2cfc9de6d82ba7edf03d345b3e84421a32cfce9955605f780b3dac59a1b9f83352fb74acab333faab0f05f4b2f305340e4d0c39c8be5ba4aff1608b47fc3bd66c7bedd74bbb9f3d6300826ca75564aacb9441e6853e33bbc5a634c7c2e9c8429d9f76e5e70ff2606f03c32eb813158ea1c0be735e385f15fd6481777a4771048b151eae786dd2dd6b062d5af04a73142cdbf3713cd816c3c6f422f240f939b4d7835f2dc983082c690076d6fd8ce7812d4ec774a68c71e251b45dceba003aeba31eed0c9a871d60630a87823d18c5172eb36b19326b387f5218eaf329dd7756a71fcedc7a56c7e26701470807907bf9fa7bac990253a7896e2cd49804545cdbcd3f6ed5fa096e76cb079100579f1dfa812b08749cfaa7a958a2073dbb65bceada4a54d11e3a91994f050623f5512db1631cc095d761c244462623b3f919d9d0d0a26f4f4ea01244ea0aa2f7b0f32a7556d412fc6c105badcd76b349f10cae1e8f7398dc7afe44883c092f29827275dc6ff288494165d00e99e74493748b209bc0f1055a7d0619005906977064ceb8d5903c4275c553c85026a21cb9e756581ce3791c48d90ba7a97f042995ce649c4cb83da94c245fc49e382a92f78345fe297d2c3bdc0e5fa248af0bf521f0e327f254d86c954da53f232ba42818ffbf42e2e110fe52eca6cf4dd203fca2d098234a550eeec8d7fde7bfb79d93275cfef55e2f7b6e9a8d53855d920bd8ea0b4ed6c02c2316c12715dfd4727c9e8013f29aa4fd3e199e218465c2e706cf052d3d5233a0406ef841f663e75feb5a161df58d2c2720c2e7b65d88d116060c596f2cac1c62d8b19a8fbd2df80094cc95909394b12abd70810cd12769ef02d17c4af1d51f21ab5faf01bcd919670734086f8bf2a6c2de5e92909e4381410857eabb38eb6871ee65cc08d759fe80a915e3b268fbe112ef615bec663ff9350460acb947b3748701f226f34a76a8a2f7384b401968c759b9c8493dadb3a6770938c20b001492a6159b999954c1d403750a7c029dffe435c4d3a4b3d70262470bdc7b75dd15dacb63cbf44edd79653614aa0027b0048018bd342e67af0f23640095121edf94313f07e76b248ab95276375c84497093049702a99f1ba39607c1020cad851173e920272886657dfeab5e56a7676fd33f542b683bbf578b6a25b70036b1e442042635eb54fa05cf621950e3427b271ae4ac475e4dbbe674ea8e6401e27ee23b79cc73913082557f0942cf04e80e0c6ec54d95b80c5b3d83d409750f76ed83845ff439cf6349156bf955c77b988f309c8f1be7e4b70e884b60f2940d4c3489836c20fd68e3d5d48e81864c35967dc180da6b16e9cbae096d7b788d0980a2a879b4e0c93e991af29c52409adf8f89f2ac2628e2680de9119abb1c1a05395c20f565ea05cc4a83dc36b2139785750985d03a4e545148cf41fd350c9c0dbcd4acf473309fdfcff3ad6407e18558c697badb55617059a7c5067b5398750b28ba3e0dba7259e74061f17ad3cbbcb1fcfc17eef62b88ed27b2749a54d83209566853c16170637c9d9fab8c2ac54ccc3a5e0466e4e37e3f2eef06d240a0300abb77d2d4a42d2a5e2d68de528b969f84a2f8372c5b0d348a0847631da9b49d0cdef62659f53d72605c8c478815e60bb1aa658aa1348c81527ab5558d857bc3063fd4febcc5ccab6a1db909b0350672d1aae52aa5e01a63fcd640a49edb550d018a9e8976ad29e63b3fecd5f7ead67ea721e9ad10f6368b3e39aff3b47702d6061128eede511f1d83d97b578f3479678a13f8b0c9cd27b181f50e8a794e33a40c1ac153edd81d3b8c5975a78178c8bdab72cd6037dbd84f7271409195e64f7d0fe857f32fb125447e31d7bf7005988341c45612757d0fe643cc7f8dd5615f0f037abd1f93bc691c0a55b07a4c79593004e974551370b3f871a5ea42fd92490a093eabfbb5d2dea5c0c02d70a943d70dc57bb869929be417853ab970d0ab31930c8141539c24e1b9732ef1f8422a7915559317ebc0413dbe64f9ed4a660cd63d045d4d106aab159d9c28870b4d8e53cac2faf7e882c762c0afb44cf7644e45f2031e8ca45193895634cfde92c1110d1f2ffde9454c443439727d34145b8d8dd404918bdba45eeca9458ee17e0964d245ce9443bfadb942d8b78d83066fab35ed020682a0cc0cfe7612152cd21260eb48d04307ec79e39871077eaa0902afed92073ca2498938a0a30f46f7951d202afc236b27e5ff8e5f6b2ed1dfac156561eb0b4a74d669463a3a01bc120e32bcd1b6cad021de310b92c7d65945a3c0ea305a05ca2c5effeb1a58ca55a6570068bb9f373221e2691149044df59b06f3aadc7204713674a0dc3dd7ec8489e0fa5cece2df08cf00d9cb92b5e598122a2a3815c00cb331d63024516810d56f0e445b40a2dfbbfdea7fb0a6b001f58e5380945f1e03b3f0844c2c1d09f1bde263b28bce4a16254de332ef4d3c671b0f54ed1d931e05668b30dfb64900179988c2475ad2e0a5873e9c226c9cb73b5ccc1364a34ab30fdcf9baa9b4aeb314fd4ff7d1a104da2bb9b26b3ad8ce42455186c2a20e2a1f00ea5cec61182517ea4b3c0750d76a947d77e50b9d24d33f956beafada038b5909fcbd58da84e1f4c1b446279fd5a4f95176630575bce480fc9586b0f861cc360ac0a0b6788b8a96b151e6e78babce07790986e69cdaecefd1beae2c1044fef90d8196971348ff15bb441c6d0863b40daa0318583b48a819ffd777d552dcdfc60b8d31364d199d0082b40d726ccdd02d8065ff5654965bd467045459d16665870686352d8ded99e94ab2d0c501121a9360a4d5288daa3fe941d946748fbffd6208f20a872bf3755ebf9198c83a6fb4cca873471ce10c4d45d0bfb69fe03201ff0ae4f28998ea66a146368a972a548b81ddf80a72b9a8cfbcff52165a3abc4fc20a0f37e91a7b32e8b2d500ad52bb6bbec46c9b0c73bc422ea8b55af5793225b50873be666a49bf29acae9008e966aa11ad8fe37695858bb83a7ec65d6b392fe3054b90dfcbd1b151e172beb1d24c32d7e3783892481630165b04278eed04b44200fb77a46a52a94d1533d39d9e9cec22966ec5df7c2375d155e0e050b82b718d0b6bedbd8d129b26fefa89b898dd6b862d03f8ec645962b81f4a42a46d2a31cc0e87c463e138fdb88239dbb753a7c589ab544e7ed50c1e1b8014e4ea5f3fde720c930705f76f6a93c9e1c8d3300e0b4af5c5c9b978dd646793f90a0740bc45ff0e3719f6bbfefae5eddf14fdcc40589cdda76d79a2406602bfb7f6e3cc62e83d02ba07072abb5674c5e0308444f36eaf596baab60c656bbdc26fd017e355b1be0600db3787e78645009eb694ccc63ba56b98cee0a37a5cd12fb1a9001e2b221201d726db488053281714e76ffa283e79b40a9f2f9736a45ff56e856bd433e0350dd214e736f60fd2fc95542aa9582a5f5b88b065382d727bca5e9e593555cbe3018041671936b17fb8f2ab75c175610c3099481acf7f531da304fb6d7f17c76b0ba758accec75e6f74b6e50f26ba9d95478d9a2cf05214741894cc90dc13990d0cb551402e0c839381f2a1cc0d1efbcafdd5aa37c0a1ca3ec2f34610d38e008e0c1a0fcd1e8c15e257c9758971a2acc33dc4f74b854150fcbcfac3a4e85eaa0f0028b77b7bdc5bf2200f500b77fa53b9a03ae4ad80f8f434a2768e2486ba536604 false -check_ring_signature efc23321d402d681a593b615558ee79aabd10f424eb2928297832eb8eadf465c 521c39cc482d3839926487a9ac2de16100561e0a640d1539300f1d7b1b091d54 15 a88d6c7f793aaa8df1a98a9eb427443c0b72127a9b85439b2a1ca76508e96243 a1479ac8ae799cd98998c746ee0caf0149f8ca9146d750765e67c1fe4a5ad8a7 daf185ba5d3ec95749444a1b49c1412b23f1534f48383779237596258a3fea24 472e21894ada260d538b23726cd82d3e97f649ac582cee0b61e2c4ae0e253ff4 52dfaf7e346d79c90bc29c4cf97ab9f809f41f24e9a005507f1253252b67039f 720973eca1ab26a57b2205bbf4ae4db83e52b8ff3f3e51ff4eec5712236a823c b3a32f06786c9c0f7e854cf81a9621a0e573b0720b51bbaee28e26de1b28c119 8a9540cec5a26c2888636c39cec4f86db2932b00b56749d7d09c9e7d9290e3ab 1a92693ac75b4c88fd8adfad31cdf1af11fbba8f1f62202b69de5ddc2ab51ccc 83e7901d623a7031dd4300355954c6faec082d85a2e5dd8b100005ca02bd44c8 d647345a9e5b1ac6ac46a5f1e50435b0302d4fc990c2005e4ae54187e5b6152a a3440fa5d1e0d16aa9b7ab6680a1a0c9eebf8863b3ac7ce4e260d57388599c88 5d496bb629fe035fdacd7111df28442949448f4a5e775230f3d278f3c32b06ad 36bf63139af419c7cd13de434383ba81d019a76a382b273e3de40b12cfd29a62 9b76403a1067abd581e80b18ec900e029be84a6a5d89c5c2ed3cd4d8f548c94e 149ae6e64b9592d9650b7266697ec9103f970c4aae99c0d5b58a3fef4a8c0c0b6329956d5811f782910089463c776d941685c1132d47d4b24b79570facf9330a7d6001a500d7111e34011d176335b32bbccbaadf7f1f18574517e793c82c67055bcc2b0449a41bc6c4f88e876e2136fd358a9d387b162bef34c7f509474aaf0aae293ddb5e5ad993eb236ed9212d537806bcfcf0206c20c09392126f03837a0d533ab891c1124d8d4b35eff51b1678c0ef73e8454fc02a3eab0f065073afc30a6d69d0a238fd9a24b9750dc7476eb5c68d37f06632e3a6d5753db28318c1ad0e3de5b8f3ead0b422fbfb901d229832baf1bb67ff3568d6ee9cd7dcaa7bac1e0581a18b260fa7a7978725b6f61a0627a5587067d45a16bf8e841480bb071e17068492a5bde9d0627fdaf7307119d1625bf164fb4d79ec059ec3a2f2a4effa7b0498e3a657060fb37705047cb36935f6cf35eca0c294212bdb582371eae8552a0b4f6d48163ff0501ec828e5b332ee91fe900a15b4ec4f0ad91be8e905e6c6192f0ab1bd9d23cbe21fec81cc81ca34168dd3e24e2b10a3d8729eccfbdc2000a20d89fea2626e3f9d026faec5a863a9e180df3275ae18b9f172aab8a1e5ecdcaf05fa3906c3aa0f6fc649f6b677806d421cf367ef6ff4f9b2f9b0b967380a8a1a0a1d0af8f375ad1b98d2aea187307fbc1671eb5b8c00efa5d45e531fe437b7820f2dd58ae4a113d62199e7247dac206ce2a9084c732c4509cedfa32771ae6c110063bca5f8bc004a87a715fcdda099b8ab13785be8a3522d66d12c0dc9a04e4b02012da7e24caf364c5e25062285e7da5fabd2af4c6d50d4099e9ad689098c9506e1dd093b7be1925582b61ce357aa6fb56a6f179d07c25ede839f20c192503f0317c2042e6cb9ce35d09e98229b2ad89bc0e8523d90309f368ed2fc4642a59f093f143197f63a66d3f34af36908367c7cb56c20fd5b5c400071a16a2403d8a80db029833696975b4f1278afcc2fec0cedc847236d9621c8822622dc6a223b7c013435fd6c28f9e9731ffa5ad2c26cf0d03d94fc17f9cc7c9724d7b67234a56d073efc9b910f2b9f04372401ae73e23c7cf6f6812907a625a0f9b1040529bef50af691ca963ff0fa0ad5657dfc61f33cb08561088972e7d46d0cceac2e12bb9f064f44b16e07d7dd981df75988a7d7b444358a41dde44956291f148a058317f00ec3d138b6b3b25c358a1644d73b00ca3f9aee63a8728862dcce3dc79d796c0b09c25f76b141804935cf9ed864603234f69b49844b11c9b191dd7e96d561090a0f295c7fdc4dd3f07e5e34e8b021f422a31880be079cc111e6ec0132c93625590c false -check_ring_signature 9c798ae38730f6b0e69a34fd86909cc07498ff73b80e84ec4ce541733c7a3996 ae3324223dd5096f1fcd8cbf68c1fa8049fde8b8168cfa8543790124e56c64c1 1 5d3610082ca0066caa822943f43f38214ae1302b4296e0a3464c86891adfd056 1e76379c3cfb42bb4e0d9d5704126ce61e97c7954af13c004b68dd33c4d50c00a8847bdc6ee3e892b42a51144663910eae384f63a05c1f9726c264d2b8fe470a false -check_ring_signature 5e1cdc84ded1e9a394b86b5efcef545eb098fe6555d677bb913aa33277d73bac 8a6239734f4d7ebab80fcfc75f33f8de82ce59208e9b278e88ed0fec4b66e7a9 2 49cdb0ab26bb8a508db83368bf0437e4d3d038470dca012fb6d3658b7742c58c 6d637bd91395c22ad4021f99c496425666b6a4aea0bb00aa6e18af74e81728ec da611f2c8366a5d90a4dd4441a977ed19e582492a29eee1cdce54f68179284065dba9572d29f86860bf70e6b24409e937dfcb0c34353605b2dc5f4ace952db0d13c7d6451150d0adb7612a6501c216cff6e24fbaf16ca24df94c375beda5dc07e8b0e2a7acf8881da5060c94410ee77af0ee390365a2afa5b51e60ad2fbe190e false -check_ring_signature bc7aa7ddf204dd9ec8e7f5e541d68e416e4cf4f034d0a4631544ca14c43d9b52 1f9ad835a17d23395645f6068a3c8f11d286b34f65ffd52aa46103a5070c40be 4 98171d0334fea8b400ea54f8802bb3d1d259e239fccd69f25e8a8a897a03b0d8 f17130b3b3304d6840d7a6d2f9500c9323f5b420ebff26ae56fad48625d0caf0 900b20e43c629247bf4adc3211b58e5220fc518908af4af2b0da487dfbfd1b9b ebf9dee7b3e2ae7911db222f5461ed062e6e6e3094bc5ef0c1cedc955787fc79 7196de11e78ea67d80d465b7a6b6eb6494d85ccb40641a88f19ed1c36bba770f8e432c1eaf3587b8841e370a2f9bc26f2b23132bf343df6008b544d0e80de8021142609e8b4752a8ee871f01c18ddb6f3c4b23b780d059096cc3b1211f3f900c47087e2310f31cf7b60c39b2e9cf17ef012ea7a2b3dcd1db6d18813436549b098ca6ad8a0129381dc85071e2458ce6cec62b1a137430d3395bd8625e0d652684d2a4bcfe680fe68d73c6a45011cc3b41fe9b61f1c49ee27851ccd29d60fa600f7a1f06fc94f8e7b9d6d7c5bca3816f7bbc220c8954686c5f2ce79ccd4e05a80a65a1d50f8dd1d70ef10a97a266e2afb6074e94bb0c050b8ec3cc9ffa56a47f0a false -check_ring_signature 89fdadd0e2b21f312c09e5cae0830e482cd49180e560fc5a7db6a76602027cb0 b60efde0f5a33cd80ad73b660d7bfaf1fb9cf96e3bf6ce8d5ccb2ac2c77b16de 6 23205683359f492cfd023ac88715346b07ee474cfcea657f7009b3b14ec5ff8d db87401ae2c68ebb01b1a10a67a1678b8064d54cadb5e0ac90e971ec812558a2 9d6344135ba7757ed760df15d9de15d374fea45f356be445edff6a5364258120 be2025ba8853a57ab5ba8c57eac9463bd70e36bfbde07c742976753d40ec06bf 870640ddfe037c4132a9a194567fee1f8df8ee305adcc47aa98450a94958e966 e23f29c3f3bd232a40ea413ff01e8618d4373b24cd8a5bb8dae0b5b7f0523cab 78958b81eff706031c2aa46b8cb4dbba9a656bdcd8357a9fa2ed2cdaaab3530c75d1eebee6629da55d965cfab52361b179fa2ce334817a2d5cfd29c1bd46d706e614af2f417c74892cce3ec81ed59f47242eafe1196d600f06755bec9620790ffc575d036f2921cfad9425fcf6a2deddace03d059752852b9e42fb5db5a3d607b269540a7c27ee99bce2b98e8261c8ff4a838c15299b66ca0f07fe45e57352e1a4bd4425166a0a99f80f6bfebb9e51d7d9c3afcdf98e6b1224fdc6e910690e0281c93778bb7a1d8af9e87d87397399ac404d6e832f3ca67ecf85333dc77c54094c23d943cf50115e595baaab6711bd77a35e5036c4a4c5d172ecafa4e48936007d1b72aa03af83fb30b4cb42820969bdea47376e1cabc5997daf086f54b46504fe6e008cbd4668955e1976ef8940183efc4bf8b2ba70eb9044a7f5f8f0a91e004231ee9b3e7930bcee10d91ffd0717c70dd7b67e58a6c2cafd83c7a9d4a528cf8b8781b10ad41b3606f60b47eff439de6b6c0e1e01af8e22b1c06bb17c4c9c00 false -check_ring_signature a13b1af866cde8ecb1b4b3ff9b8f87668edc2ecd53ce772e6f24e6d0ce80163c 3efbf5a2e3fe069fdc4243ac9414d9bce6ffd72c4979fe77e2354576039a1362 2 c6b695b5176d4f31475349c4173da74cdb747c84042488809f041b6228b46e4e db1708ebe9bd5288ff22ba3a79bab641c618966bf11e53aca9a8ba90a11b0650 533f8cbd7f7505fd41e160c989f138ca36fcfc75a001a528fa754382a665150a7dabd43d0e59e355a1af976680eaee728d940c1d1115828c44f1c7d58fda230ce3a7f350eb378099b911406329e351c4b3386f9ca39ec726539007d0b832fbb42cea5092a1b893ad8883670cad3e432c841737053a6cc6ab1b4d8465703aa505 false -check_ring_signature f323bee9182019b40d53701788a3a831850708ad0163d34aee6be350a8e57f07 3c503daf1ea5805291ad1fd04e4213227d1b24305c08ae095113bd3ef9be9b7c 5 829aac225b64bd8da46e97197de39982ddf03d9c8c5c4f48c826728efe3e9671 6439f695a168fde0fe695cfe49dcce30438670a65e1b1c5a4bab224ac6f479d0 4b56b056d707423ac8b5a4ab172cc75f045b334299f6e0f245cf8ea038849251 57584b1489d74d654c4c16f209db64ac8d7c9e9304d8e21f9fba6eea491c2adc 5c1a08913b3ab0d57c1ece42230c3a2fc48c979861572fc96a6964a2665b984c f4f793b0d36585bc37da71a937208caa999d67ee23b7cb325c3a54c49b8f8008be1e8f2af7a6adeb346dbdfd2d5058220dee83330ce94c1257a55b2bd6e6d407dd814a58f469955b19d22b2bb5a9a7b76dcea510d6871c81639abf98ec04a101c2448b103015d07ef53c170e4f9951a4db697a0f07c46508fdce7cbb1320d70a84af9521a79f778916ac37e395dd5b002baca1a6d22a7f7088232a01a175f506309bc6c017a0ccdc9e2547ffbdba900d938dba258c6641f1c63fe187c16b9706109ea19e5079c53a36aad39107fdb1faf12655a38a07d7116348b03ff91a02045b7a1c53a5aa8e323f8fd78633ba0a69109ff093d949c8f2b0cfab289039cf09acbaf25b3978c0de4ce05ad7459485eb939f03ecda41397657df412b7a119501c98d8ae03dd3798315837adf0c728e188e53a0078a7a63dfd77c9e7cce120402 true -check_ring_signature ae88e7fd56902c1d2e4e1a69520548b2a3dcd53dfb72a77a43ef8a06b5141468 2d65efbaf85986b9088e0bec23538aa2cf1f34a8adc7024a94305ddcfc29eaa2 10 92ee4635dbf0746b56a2b1655af77b9b9f1ce8cfaa4ed3f7b02e2a6ad5b55da9 b8d6a16c88fd84d58497c88b4b405208cf8ffac8382f2dc5d06044a56c99ac69 f2967c168cbab8b12ed4904d99680fce3807d9689bc2ccca7d31970de540ecb4 c4a58f615947ad1b91d9d33ef476036db5eeebddb46cd780f6211b2d8ba5d662 c96f1e64247d395e2a2a5774116a180027f581676a97e826c02043e92fb77c71 bb77ee5ca127e9d1bc7768c67a61b9ab995a71b63857f95bdbe5b45451bbe2c4 5d4fa36ad317143b0d76a38dc70fd545dd23d82fe3fd9c6279c7c4b83bdfd916 51d36207c3d676e8c33fa13212b6270fe6bedf66b229ab27688d9719554aeab0 d2f9745f99c898dac06315d84804a80d4c39b144843d5ba53a3da52c409b6392 ea3610de0457d48d7a4e1aee5664ff3b080fcd706fc09e9510ba0f96f4319f94 f858b678d8b4e93595770720f321c16c66e8636fb6de11c67a265a4902c77d9928eeb5a137ab3955fd9968a67f2163c86155549b55fc06f5a3c3e02f6aecb90d6b04c264187c76ce9f85af4fa589ea49c08039efd398ef763fc7a937b2746e756eeec1d72d85b64bc22329464cc2044b17c017fadfd00d0746c4a0d7538a540e86bf97ab6fbe534b2f573d296305d61ed3162d206c8aaefa96fa28dca3149406c176e2180857418973defd2c493ca4ad202598680618259b5939df61ac287a0ef298631bd143aecb52c4c19b21c8e2fe02651cff7415366fe214385add7b0c0967e194d48213db3bb64095094b14d656a4a9d9502e0f194546522c09f77231077be294b6f87875bccb0c8dccc674230f78f49ddbc9acae60c4673a50596f82018398cebf21f53fac040044bbe7846d60f85e0e13f3a946d18b761938f6f81207eda799bd80924617866e3b2f34198eed8bb5c16dac7bc0c61551e7d5b634b504e9f8527c9b1f49d9efcfa5d97eae897fc14e3d1ea1926357dc01a3f9cd047e0000b820ced0c97c140b2f6c13dffd89d106a4a47fc4debb383dd774773bc96b041265b0ae34d274f474a90ad650975a10e73d8a2fe48117317e43229ca9719e079705aa0ee1922a92970341493f843dd749567cbf416d7c6b9f643172bda7d20b749578fa12ca13f84467bfbd6c80766955c27752b28a1f8f656d83450aa3ff0594052c0249f1690e32c83a422a171f08eab01fa94ff3c47b8abc1004f5fdb400cb89a1bc67638a17f0e521ae088877e7fa790392ca445524c84fe76723b7c404f718df7cba35d358cbfbaabb197a2d5eaa516d4b9fc0ccb30557819fd7232b0b4c90bb3bdd9f5ed64737ee8262de6fc04e3125193907816653f2f6b076c9d001 false -check_ring_signature a221e0ad83d0ebdfe783b9dc40defb7cadc34b30fa06659fa272247465feddb1 a6c99a37608b77e4b8899407973d6a0133834927691c263cb6c2e11c515200ca 21 e286d6a00aa0e2c62b24c1b34d4422bf763f7c5ada3f046ba765c7425976f3ca 29dddcbd0e44582a9711ad83351cb6a0833f8270b59d6c3f15b97e4a9a3901f8 8ee0641e2f41d7f495d456355f69eebaea1c85127e6ab2751d896c5e4bd1681f 9718d0c464c96030677a42ac7fbc6cc71c07a8a5535205dc436072fecaaaa59f 00d357e3e9c0c4b7fad497dd97507d8cda22ee5c32c8a4ea587038ce22d6d41f 570714dd122c75bb75a8f4ff0aff54fe2b105b805436c73843dbb867fccff5f6 e514609c028073caa08f3373cb9ccd94ae266510eff2b2a60e56ed15fe4615e1 32fcc9807e73c509228f7a523022f7d05eab58bfc4502d967c22e55e920ceb98 dce65edbc6c63bbd300cd063d30c0d97b3cf4e44608826d8e5bf1a0e5c3eb13a 768b56883c5a39b7ea2b950696c6c0772840f43ea0c19a28a0d06c1894ad68b8 589bbf2b0c1d595829ccde3a5d6acc2a7e83e1f16e6d06f4aa7823b9f4f89a02 09f920c0123ff52e7e39ea120c7e01acff728ff577ff16c826fdb311fd10fddf 75867f5a2896f837e612c07855ae5287814eb13998878252cc89abd354ed62bf 8805bd8b4352a08944ca908f03e6cfab6399b67a1a8a9f268ea8ba02c092bd50 eab050c255680c8031bd15416018c30238b1537f177546e2b5c09797d5d891b0 96c888299022c60e493a1ea450b56116c835cd057abf3db815e24fd51dd85b54 116c807645d5d9ffde3532072d550cfd7f244aefd219ffa1591d962d8d51f326 d8c2d4ddb0ad0b8311937cf0dc82208a01a40e25eff7a2227614edba15e91239 7726d4572d5733a12eb2ca36764da64457e80712e397c82d3b357bf59a779ea7 3da0336e2ded4c5b51b3c5876bb43afbddb81f7a57dc75bc31e1ea9de42fed29 8e56857b1afea5f97b52fc87c88b5bf12369b0afeda0d2b195032daa7592c674 c2d51da8265fa3a6bf60e0396777e6e3faf8762398f64a78469d7ced7b1a6504ec8a7ff6454505efd7268c58dc1e4c2bf095f5f1e9576830cd99b3fff443c30bc0d52b380cb355dd1b8cf31f8168d10f11907f95d986673250dacd8843bdc10d1f0a08980d7f004ce7997b0067306fc7f850183d84e377270933a1820fb1430ca73bb61408c373a2b5431ab83c1e71afc106ac8cfabe373b7faf1c0db2004b08526d2de41ba275833c26aad68d0fcc15f186f67fcd635891f6c534b53296a607eab5c51d2ce313585a00f9ff9f425befb7dfe0fcf7e6e366d70c0477f24b7a0135c4fb7d0631756999ab857cfa137d3d530967e7ba4fe75cdd8214d0280be200b405fd19b2cc0e1daa923ffd56e5019cb9445e8f0335dbc5ca64eb3cb737cd0e3573b8b65f3b38b9a476136fa60a7bed5af8c8fb9cbbf974d703a38e7b223c0e94d26abacbf16c2bc8f5a219b03119b23dd47f5a24c2562ab3c6884662fdea0b8afc727a671218733e5b36b45ac85d386ce8e4029763ae1ef910c804ed074e051ba189e790602268cf8ac0fe3e5fcca6729482e2f44e656906e51ffb28ebc1001205af2d8fbcb8fdefdd8e412d8f3524ccb4b59cf9cac3ffecae361a077cf3076d18d591785396da849c68677a0fc3b47e2f569ca18343f122250d984b15b20e1d4fbc5ecf8c1cce3c8407b8a6d87a3ce5a3227c4c22190002c8da0f2de6eb01ea83ded5cb1c733b9e02d11bd1887f3590b4b61df818c4ae70ca1f26478a42001d4ee00e920735466a2c7640c6bf7dc155f1c55fed5f3a4e31a01a80b57f5e018486aae12662a1cb48237ac3945b60e4e40bb4041244587c7d519eb325c24904ab3943286e6bccb0e8ae55681c6a7dddf4a67f33caa5816fd0290269d4197c0b175d4108d18659c0db6775fea36472930301cb2325877bc02d827fe6bdcb2c068c3d37e507a5ef7fcb239eaa83f433db9b1c7a05dc775a7b0e52ccc3575b8a0b97b1b01f299eba82c0d54786856b04c4d48c7e88075ded5cacad70ec7071910cc2491cf02c8b44a738d6d350afb81c7bfa37219e650c39dc571a1c129c88930d332b1ab9dc6c658654571ad83e4cf067ef5e812ae502c10ed64a1614a1fbac0516696885f692086d197c362a5aa1499bbc05857b33cfe9893fd734bd70db3f07dce5afca9140cae8e2b79e0429031782f643242081c20eb130ce4bd10f3cf707640afa709658dadf9001adce45cbd702acf60715340f7fbe899e512f63ecf106e6827a9225870bdd9db7632480366aeb5c7a24f0fd3a24ac96685c4a3617870af6fb16fb6be9aac4b2f30028fe9b72c3b066429b4d96712017b335fd39ad9f58f4a59ce061a4be650f15f6df29bd88414867e16cc5fd01d6384d6db35509aa0f18c3af205480cf31bef57e34e35994d8b485a92d245c934679fd11c4acb335035713904b8e1604a1c25c4a739ea97ae423aa5e6d56d7049ff89bc7dcf515b70989c347e47e6f2583c31432d4364428924644516b864996fb8cc9db7bc61fa40a219ec47486969ccd985983596e30b39c7beaeb457b821ce7fc83f009dcf1f8019851bbe7454bad26b1f1c8a08af6b07335e0a50b7dd498141b1fb6a12852cf05dc9755062f23c8d7516870185577b7861a16ab49c46cf54240e57e6814f9f9053135327fca73b41fe500a9070eef523b6d9649970ae371989e43d40067baab0961a257e0eddeb88f1c6bd9e78e0bcc4931c23074c901080be9bb07d5a3d8c8074f11df16b37df02f57f4e2dda2135dbc77a99a963decf4ffbbc2a94b0f22c504cc0d4430a85d1cf8bc7a5562accf7975877fdfa82e274e5005005a6bd439750f1fb2e878692394d0a931a29e444397bf6009a23463f7c914ac2f56cfca040b0b false -check_ring_signature ebda99526e477f3e5c3516bd0f4ea7ee3b1fccf363b63f011e07344a51cfd35f d7047675daff65d9451b0a3ba56a97b4cb1c01b4f3292f3c99147dc3c5428640 4 13e4b1ba2ef976a7cd8aaba35299fcc96b112b8d365d83b6e49a872a861e2dcc 43fc9ddf541739d1d73655420b88e38437e883cb2d228bc445895801ebc7f762 7ce35dba8815ad70420d9eeab7296deac970d29dc21a24a3dca6b982a25e902e e3aaa73cf77b0ecd4d34d796fc2d6dc29876ae51fddfb7d97f4aaf5ef82e8342 a7e01910f187fda01f64f1b02cf9032573192cb6d8a2158297d3465147f8000ea20b7232826fd702086a1a5afaf4c9b4545740c40d28c50fa8d76a9dcd5fddc9db7ab3daf674ac953fa2d6ffb00a739d5445b2d25335400c5321818feabef001ad04c18b9f66ab6f9b0d7b82aba6d981b834b1bf1ef6d535b53ca6c2c15aeb0a4fdaac355e7f896824074bca5c87ff8fd04a06ae30a1aa89256d03b1affc1109819dfdf525ae64e6fa2cbc4fac386252b1f0ee03f791c2db40e9bbde5de66834c97ab2e35fd58abc838492c6e3ca562d3e571c8af6cf4a0f983f939b4481b3009f0a10e01c6f64bc45d87c4640ae0f4d3ab3e488a4f76ab005d77fddc621750e false -check_ring_signature f20d406b967053824be9024eb33d062316ed5790216f9cfd6c07d3e12c82d160 48b31a97a55ca38f4444ed4cbf27533417562f7f5784b6abeac0771ff2e5eea8 8 c62c84b9c8e283b480e3e38f6066cd29e65c47dd6be3a964b71f00e1a51d42b4 c007f3857ddf04614bc9be4652b487588bf4346eda7650e6dc5270661a1f573e 0c43d340c6a44ac33db3ada762ffbbee19506b6e5f11d92f3ed624f8d8b862fb de37d6f5c9c6dae4d42d68cef075371181a43593fc2ea5475038f50ad057bc5d 447633ece4f67451957fa681337a9e83812514a4189291435c2382bba116de5f c0ce0a5b09ee5d1d33b2a0cce64baa1ee84ed766d9c2f20917bd658619c19f87 bca8bd95be5013fd16fb86f1d86022610aaa24371cb21676f241c65bb7927ffc a19ed0e794db404a7d92512083cb81958e2177aad0c8e3645ce7b3b69e378191 f7551bc211683690778a1cb59cc84c8a2d2f0ed0779632fe15c0f8100ca74e01e7b5b836060bf41c383207ce0422b42219ceae2245d742fc8011717b154da607fa69982661f4dd5f64b8efd1d323ac61f0a4551b78a415848b2e04e3da43c2088d05e55f259f7d23c7a0634d199076e01e5504764f1fee4ceb0c58fe7e1ce00c977ddae688dee3192a3232318afc4bcadff55412315220e4f2a6b1c575deb70bbb432518ae865c714282ce54dfda8580c795b931c560fbf83f8af6a092b4ea080e5073244656cfc5aa86c01cca98982115a1d8b99539c7382ffbea1cf56db206338cbc2e9a0797cb9f6eeee144e99ea3448603b9098e94d32cde0632bfe7890119a69a70b98984ac041f8b01a463901f04a00884fe43b6664e0861cfaf8b3b0ced0a1b9305c148d86a10e4394013e626c047cdf2ccdf02cb07f083eedbacc4068d92072fd6377803d61677d65d8cffb2f2d264027cdc49fb5712c432dd0aee0bbdf7fed6387caeef8bffef903b4e307593fcc55bd2e913e9227663457e3852035d02b5b55c5032c7351528633680a8542c05f4fb1a008e11af97298ec1196d093b4a878d96626cfc4a5534a2a56a8ef8b4082ed49aae3310043ca5acd400e608e04d8eaa99666994416d727e743fb22f0e9c643be11c19e821f1716be7257409ee5838c6ed86121b9750152b2525ee4829ae8a2ac565d48701e1ebecd71e8709 false -check_ring_signature ac0fd4862ba8a3f3011ce22123febd85cff730402e3421946b3746cdfbf7d066 998751f951b8f8dc0fc267469777e31b417fb90b89a57fe0f2595c8534c99909 42 31cbd18eff019fea28e940ce5c1ee248b5f049d2bc6657a8a44c6eabaae75935 0ac30f403cd9a83bdc85232c7a4f4f9293ba838312701ffa8214397f87073fc3 b556d5006dbc1ac9767ab88ae26b6f4758f848dc36479b4e1e25fc8a8aefb9a3 10cc5add1b90e0a8768a93698d341d130068dda1115662bbbba877c9630b1bb8 a2c4021117e689c1b1a7428571cc872c899d42dfb074ca5e2473cb033140f285 c0dba0103a8ad12aaa08a5390bf40cda0cfb8b88a7d8464cebfb8fa64d280ef0 829ee237923ccad532c97fdc65c7d906f97fe110426a5feef9c1c4b0d446c846 3fedd3b7b454e5cdc935d0bb0e1ec5d7cbff43c5468fb86de05418ac81fa21e3 e32b6993139d4aac97e85f725a46e4eaaf0ca144c522e8dd5e45aabbeb95af37 395cb6b59c1d264ffce2d70934537cfd635c18e3a7bd791fe22bdda993d598e3 c499402122437eeb0ff197ba81ec38a97fbe41031020880a69fd421009c8fa53 51f21c96886699cdea5e08b2b025bd871b46d2d60d86dcd6802e2d9323a721ae d5bae357827fcdd8f0661d7c6848a070a9ff42ab2f2a164bf88d816573a36998 7e596424dd67a7fcbc13f4eec99d05542b6f1df735cb48accdc15c0b395bdd94 ebf2f607c3fc67caced0ed88b110004837a742e365ee8d7b00f5e71e6796ffe4 e4e3bfe489233758666e60c50a2f9bc844fb2d6331fda742d0628e97bab3f409 0f9183e2d281770827463e18633ddc368529bc1f0d1dd7fcd334a5f45813261b 90f0cd6a4b26441d23fbbd337fb7b2707b15365c328347561f7200a96f0e771b de1c63f873e9d7a1f62b1270baa06451be4edf2b1ace8337176ac2fec7b9d148 d3bf4d5949093e32d1cba703b16e8f1f1daa204ab27fd130d2e50d28c899472c 299da7256fafc4b1640316f76bf69eda75e3054ef95817c7ed9ecdb3aebec9f3 704d9f9a27dfb21bc93e75a3b3093c32724af58b50974f2d71fd9069bef9b9f9 443ea97a212e01f14bb03d665fec7cbf222a7f3b0594f5a0a05249c10ef89b1b fa8653d7887363288c1ce55db795490a1854dac68fad9b1c2b99590466907085 c004ecdd82e48b55d1b3bf26b80f21e516635e741683f1d396811399fbc342f2 bf09040af0d5ea8b10953565fdd2dcd2d2205cf0d343b9393d8944ad561f2166 66ffc36934fe9862bf004bf45ae5ff15294f80f0f4ea6d5931b9e00aa34c777d f13400b4d738d82261f1fbe10df0f7b8ce93ff3f72aa4c006eb82f9e04f575e9 d6f780eb6e255cd92407222fc9baa91a2721dc89e31c70ae8df3f73429d31e97 a42cab70d7cf4f676f01f5ec06c0487aa70b8aa37ed21309a62b5f2d18004ffd 4f579c38db00572bdab3aa32ea9e3881ccb9ba4ad3bd52e8247fa8c0d7fb6e44 7183f4f7ffcc89087a9998202ac9e8b4a1c20c91b5322cc507ee3ff7bd8bd772 57eb4653222d1dcf3fbb5600a6ec34087b720d8998a611d7943ab54e3096e94f a436656b470f80874fe23f527569647d6d7a2ea1684fc0fc795465878edf0454 f02923f143accfa8272658aaca05191e16e30d6144921ad7b3f391c553a252c2 8d2a5ad5d26c872f5e02bf4937889d30a85b2b7256eab505e3544eb83ee0069e d97dc8fdeb10dc83848d9c6d3dcc32fa68e1672a9d0c304f2890a02434c4237e ba51a44bd9b5b06cda5a6930f32bfa61ca0c97a363a1e23795f6fc26ba5ee801 01475ada70046369679d05bb74092a11c8a7154a9f9c43191e2c63d4c2e2de3f 19a533e2f496cad94b6fd0edf8575d7ed57e665cf2b8cfad1591289bc9fd2d29 39070ee178e56bced65f0e6b86941faed5e45faea8f7af14ffe0054260082712 e307b00b94c497afc4495f0220cb1b84fd4c728f7f0a9ba223110386c7320185 8e02b2c733360f0f2dd1dfeb2705a48aab3000d08b0cff16a5198d132c654400161006787148ca8c9ddf309a81179508281eacf4d23ddbfc0218031be9236307471cf8bb30edc600afd0c3acef477bfc35619c3fe0fbe0ff3df5b95dc1787c0c4129de685549c687334df26d422f64214a1038bc9304aa2c5b7fafb1ab63100460d82982843be847f152b8c8441bd12e3534c1abbfc33c3b5e807398c93f7606b194b7e02260a07f4f84db90f9b878a9ce3f4f6bf4dd3cb59789acf7ac2dda0cb2a8a5be597cb5d87e4fa59042e200a6e9786195765d61123e9dc6ba00d783040f531f408d01ef26bb4fd39f7a51b844ef641a4845cc7927a3173215a5f12208c68b03a4b8fef66a2a99065daecaff1237fd71051b239c363681e2e97f2c0100363bf511ba8d8f96b04bcd59f7cb0f305b066c077f6357937e80d050eaf74c0310315847f14eca6ce260abdea00a186c068fd264e43e1a03034feb46d8321f094f56b7143520004d5387057e27401f6293d26d14458c763147457047b8255c071f6f28528f60cecbea6963b91d2662247489e9cd814f457bd2714a02436b17006c644a6396f7aa08340bea05be1b65815634171abc840b0127c454f2c103e007f8d35d0e3fa66c052c67fbaa5c80f652bc52a4a85258136549704bb9c3e6a40488b3b0eb6c8d9f83a69d84225e3560def1bd03ac6fea0c2e53896ced37f23707b28590c3ddb328784c28f6437151e20af894932c83e49e69e907d9cc15a5ba0557d76ee90318e2e6f76ffdbefdeb02b5fceb87fe33aa33dab1aa580f5fa42801f2ad83db80ec07e7cc348d3ec5e67f4a91a14bfe8974e470ebe212ca490e86029d38080b38f5aff1fecafd64080adca0c104e57e448418d028f4c759d0a8100d0c9c26495fbe252826f7521a468db5e0135d0b5acf33f39f8e4064f48ce4900fea54b4c4a8ed6359367836d6d39a853f4d506e100e89c8c540d1bcc64147830442c47f24e84ee498cc076680045779318a0c3ca31831671f880c6721251cc4090b119fd482e986eb126a5d2b6313ba52a98ee7dcd8e86dd062c1ddc8d327f601f2ce39c44c042a347c73408e509f717e01315512efbb2ce09d2095d3b810a40a0461b913737e34a36181cecbc702a470ec542c462d268b6647b31f1d6755a30165ec605e013fd9cfd26b3f60b6b7920fc502ddf230bd124c65c3c9539c48db00926a087cc62f9b0da3f7aaec707f902d93a7f1fc046b2d5d8cff92bb0cad2200a1122286c24bb06ad99db79223b9ec659fbfeeeeffbf978bb8d4476a26c3060bd883f456a450deea809f33a7268a18431f4fc8aec719f3b8829ba95ad751b10cd7aa5572cbe49c2a0e1706c39b62869b1bfe89d42454ef93742e0b4fd40e0607a3b442c7416f0f549903f4fb971bc8aeb9e80d666079015d0f0c8eef5a21a705a92b3a2bc6ab1fc5054ffb151d031ca93b1f1c8aa3b3cf0aeb6eabb75824cf01c83f065213485c550e2fa411ec9ea59c2c4e4e7f4bddfd8c909a4779a3c6e80bf813a4c1f5cece919e89078fbe0f69f7f473ebb252e712a4e70ce6053238e30ebf5429caa4c151caf726bb8e9dd3cb74bf0726e5a245f0589abb71c4b3d910005fe64841c7d24c78e8d5eb113be755849e9b511b66439b366a3ad6e36544d30919e95eeb6c5bacfac43399377cc4164ace2d5d992f5cbc95cb1973c740568107f1a5d346effda449be99791c4f6087e1a02481b9ce0cce4a0db1812546c948087cbf201beb650314d258cbc055bbe1bce1eafdac5b206cd0c1f960843ad5e20f3d86694b6c7845511320312248dcd5ff75593be1fed36026346078f6a063010bafbb078ff5f782097f9f57b7416c7dfe22b7df3338fdac95a63e879c809cd9071287329f2bde5504dc0a4c24d1958bc95bca94998d25b1f9562b51a6ad61ad0188feef53e23afe580b13977f8ef072196305c429e60aa7c59473cd25ee6ca607eb73b1777e528ad7d585ea3fff94c9270b256a7de3ccdd268e7c321ce4fbc60fda9dda15455cd297459a249b5b4126bf4e687ca399848cdaa51058050d90800533b7cb6b9ea58fb2d682488d5c5921732dcde36c471607498941831d4d92800374bdbb1ca19f07773f014ab4b539b163676311c8198ef23eedce860b61799302afcd304133b55ffff21f3e0dcc594d5d9e0d5572658870f52cc0ba9f63634f0736d67157ccdfc64238e0800ac31ecfd0b78bff2a906844d43ab26b75a96aa601e2a7d89a703f2f712427f3d5e3a6e8860ea0eee528ffe75586401ad0c8f57c09138d7a6f2ab0d26b658d9c83bd787bb8632aeca9a177244caf073c5cff529b0ffa919b5ce54e8c37bed3cd06a7176016c8f489cbfd5078f1556816d957029f0f7dfcbee65c94d143c46615185bd36138e8ab50dd4b06ece9e23ab0ae09c4c0086be844892dd002ed86a6853362132759f82fd10077e3b950accf9b732c08470ddcd5ff5e1a56165600397130d1d455a3d47df50363e6e90f1e84ab28713fb104c8e592e370e4498e986baf7e731bde3365b4114e100d341a3bbd1fec47d9780e3ac035af7c1ad30256d2e5b5139d99ef698fea5d69fceda1d3cacedd0d89ac0bd102e5a48fa9cf0d8e077f28b45e905d056372040f2335043e53f14f1056e9083c50df5dd892811d72cfde321d516d716ac1517334636cbd9175847dc6ea530a7029bba9feb1b09599d3b04546ccea73050235edd612abaae119b69be04fbc0c0c94800640e102107e3dca04ecc8a2eebf2e73337a0f6e7a233e55964dfdb20b21fef39c954600b173bc5f1b3b0da7e60ce28c6b3553062599795468c99c04090583d72f6ea17e13dc2de975bfd944d959a52c6e54e737f8b7041bc6ff24020161e9a13cf4ae170110185cdf0d81fc57c835fcc011d55080f8d5cee342df9f05340eef27ea79981909a0aeb3ab647f61faaf1a50304dacaa099c0650e4930b073443ff98a1ca592c508f294023b35abe8a343774a7aaceaff07750b53380ad08f477ee67dc9ea4cb6487034fc57bb4667ffef2fca2d0630cec1e7bd2e1c7a9e6624efe92c0fa82307f72af0cb3ea07c31ea00133fbfc674776ea03ffc46981032b03474e19b5fab54b89f1bc86a95308d7ce08724089a83c8d9a678c46b35e0c31dbc7b48f8165c634f99d9b91ca8f7feff2f2e7fabca157f63aabf651e21503b8e07ca80f44d2bf68e4482fa90363d554b8e9d7d6f8f0bb156e302a9240735c6219c60dbead58779c955b208cee060e0d176770c82f6c29714aa18468902e03801b78c12a1c1a363295d8ed6032d1420da7048452cdde789e65a9f1f998710afd130a1c8031450e7265b64250b94cc16969464e032fb65c8d9e3eecee0a0603672ef85e88932d778061ec7cf47559a17d29503d7438b59770d0533ecb2bcf037232a2ad2b192a8072da280808999144a9c4de8e3dcfeb86e514f9e273634504fe1a1b5e0d2c32b3dc98c7cbade7250d5f35126b04bc7bd5b7f14c07d1992c0cdc8abd25c55dd903e306afdaf80c1a49dabd7db06af43299dd13c884fe67fd0c1553cd8e7f9a6ec218e9fb846fb8c2403cbfaa2268ebb3f006f85de09b9e300e1f0cbe0d03521184c3a86ffc58b151bf3e20d444f697d0690e2b06873c81500a81b6030f3435120b136c95881e08ccac59879d21786ed339fe3c2a372d020700ea01b283d44b8b2e51108755e03021c69aeacb69991f0a17de937838a7bdd40e45b6cbf15c34e407e051064dd2e970277b64649885429dfa965e900a4637150b false -check_ring_signature 7195eb80d52bfe06f5fc28e569af63be098b4147b47827871a8d2f93191c9e06 ffc818748715fbb4f726673bb5ab15abadf5767d7c7b2706807998a73415c332 4 0fcc11bd9ddd5f541f3e0751ba2aa12e6f3a7215c40c0f4d33f450f80a97ea57 11e164fdd98e2571d58aace3e4b7952b9fd5a9a35fbf27e563aba243bed29abd d5a046708c68638e74f534b37b9f1c9200663942e895c714064625966a1575da db5b9a4f56cb2c1e86376bc0e161e4badbce82dd14504c781db7e60afce7a434 ec586da91ec965997441823e4ad06ed3167b63303267de3f04b583a733f5a00c6a9f587797968f548ff29294c7744e166eab5d80518c77c408e599a515920b0fddaafac329dacf3a9c192e855e43b9424e5b96f4cd3ea8773702c1821317020be7d6a7799fdba25a3e72ad6ca6d59b80c736afc9060e8ae0381a3a9dca412804554a6857e3f9866a1095743467e00be5804fb6b860fa2872d7ffee4608a97690230fce89e5d53d54b9923cdd8a4920f3a70c8d49d4ea8a4712de1c639a71195cc39c7bc33f13955bf3d0d99a885313976b0fab31f45f1005bfc260069103fc045fda606ff7f351a06fb6b95de939924ee78d54a22fffe9fdfd13ba40dc9cead6 false -check_ring_signature 64910fd34b038cfcec5c0390def630744418efbbf9848702abe5e32f135be843 b1a6a53d66a564905360693494b093cf884ed01c48e9716b83c96f9b2a0175bd 2 239cf6d62878ea9d75efbf2ddb08ff6f4b2c0de6e117743a4efb1a57c1041c4c e9098725ce90d0f0cc949b71af7074fd35fcd2dce1bbb7b9f21bda62e9cce9e5 f391b274a2f5eb9e4f53892706fa08487d98ea5de8015dd4d0f3ab1036ce2405083a5219abe733b4fc0358fc5f49a7180f83e054417327989df5ef6a5d5cc2099d1ba209ce94773279df65d740956744f2c3c56adecf653f5eb9371d91fc1f048339f26725b6b7d6bd3f8c22434f1c3234ffbbb78f157c645220781f6e480b6d false -check_ring_signature 64298f4b123c62cbbd708b9a8df8295c14819aa9df1ceb9c765e66e2cb0585c5 3e23b3a096c33c20ceaa881917caa2af3e1b462d7cf9800c7feac9eab834d0cc 5 e46e6d3c1e0eae35e27f2cc51ba1601a7d68470046dd97af98cc2c8f582a18ff 826e44b0626f0a29208a6063b9eb6022c853c8b1bb578ed514edb52bb0e33111 8baac94840eaacdea77964dea424df751daa5b975e027575900ccb4211597c97 5eeba3828edc84d129be0de987bd9d5b59b9d697a9f4febe0e82c99536d69da2 3ec09273c1e61dcecb7876faaeefdbe6cc5d3337d21587d8b202b323b93354e5 e330b767b0be87d4c5371464e5adf0c4d46f127ed62d4b09ac29b544df73890f9fe7c11286c144b48d1ca43616ea99820609ef9a16ff9ccd863ab0e107972c0b7b58cde63d44367a40ed7720d55f088d046aca3c1586baecc2a96fb5f92d3401a2f8095b0f18fb68baa51a2604e0b18b1a43df4ec28e5eacfb99ec3aaaa4b90ab8566368dd5f15b9c0a9322bb4ead71a1ed73e292184b6514a51c1116557020fc8f85f75100c7b2a2bb9a3e6fa89c5373757419ba710cb074ae931463a58660ce9cc89227ddceb3fd6c961e6322edc1308eeba2eff3ea02ebb906b98f376a7051a1beae25769dbe1a6fe58686e6e1f8c55bc891ed3905f608b3e329f53b3a30f89957fbb2d5cd68735811c81f3701ea6fb277e3b62dc007c4a371aee19059a0b670bcfba828c0097ba56f898de3a2cb1d447e1df28d248873b3470e86157df0d false -check_ring_signature 1b703fcd3e5df33f898494a059998c0dc095ee37a3137be4f628cc4569d88c61 686d12b8e95507113dbb228fbf2bebc6cd36e3452fb89b1f580991ade98a499b 5 e49ee0d1d72fc102bcba65a0fa312a6bc6ff74506d7487b8417f5343abc9241e 3ee8973b36c21744953549763d0f4d4106d37dede93afad199d1b4b9d6f3002b 140a85508c00fb3618fc15ae7e8ceaa02e5feec62fc30ed1b2514dbd87986030 d2e9bb41142765f549d96dc618ac0078a0dcb89497b889dfab151cceb7766505 d5ae0750a2819db3566bb1e28ee7139e017c135a81ed079c9fa7d69152786196 ffe51ace44c36926ca59aad4c3a4d480ebe3ddca72b361753548ac1412d0fc0645af56939b2cb045ed0a3883b4d5380044acab32c8d720c781e54d85c1c92201e297d11de5f6280aa52b2e044e0e96fac98df15db458442120eb67565ac63801f49c4169bbbe3446493820190ac0994e79eb2b321a4b2bebbf20a6562c4ace00fe39940af4d257b723c09f7ceff768164f4af8cce927646b1e6b83197942be02873bdcb323b798a99cfcdde79b2c3a6d0c250c4e44ea436c7a440962565a0d0f1450a7873473fcaea42088ae4fcd5e9974cae8280ea2776255bdc57f0eac6a0abe7fcbe8b1cf1cd98db78ebc12046e3bd7a9f92b06bcb5d445111c486440170ae754ebc5873818e5c90e3fef1f7d99bc6a6b8ac9af7c8bc4ce3dd4f4b703af0dae208359005098ef51d9268bbf410a6d334a5706a28ed90710d266ec121d7503 false -check_ring_signature 999b1af1f6cf37af876c8ba98f30f671c4034827160b48b57599b8ea39930283 b8cab1f61fdc9d06acbdbb4f7929442c2fa07471d66cb75475b66ea994769877 13 48a8c8f7ce616f7a8e46db00110614059794c1b2843fb537dd06a7908cd6d080 9999aeb65b66efbe5b4089061db33f58c93b02723fda738ab00a75bb4ef45c84 66cf29b096f209b50b6fc7f0ec550701651eaa12e7ee06dbbee2b2838730e6d5 e7822fe5ca81d4006bb6fafc824333391c458c0045da9cd5c4305ee169a26078 a7e89e90a6dda954ba4f6437138aa822158207363c1de0fe7c91797c66a0c760 867fe5a55d0da7f65ebb3cfa705ca629b55d4d764b2f94401e831416738ec5fc 601acedbdcddc41887582872a7f7ff17b3b9e25fed792ab941dfd657e331b182 72ae21dbc86f08a314aa7b8b6deb3f02bfcd253411c375d618dbaa5a4e8a1012 a876a2281b1c85770e7bf45ca9e054e2949bf735093a0fd54fcf03b67591f916 9280e987160c702d5b95040a91a6b5d19b9803ed0f0a9140082d04c9441a6956 a8fa2853eac886d5d02dd8c8319140452db61a86381e2e9e563c4dd8c2f511ee 260881a621da869218d252120e8dc43d7756720d10e2299b39f280dadfb2ecc3 684d4315a1985c1292c40bd951ce72a385e644f1d5334f1d0fadb88683a9dc31 dca867fde9b5b5a33d5378a6fb0f69ca96f4012fa8a34a908b81fcab406ba809f56121bcc7aeb32e3b84b602a6061c63c2a12633958c0f7c4061c9c127740a0f771a58d518e737513ebd525a85f43a978f04a1eefb7a0e565a59f5683b4a7903926ffe9f8f0c1963c73bc35bc690fc54bc412c0b8bc0ceabe8ad2aa682797a088153fc5a35fe351a9a030db37e8e193e252ab065e92684eb9fad69122219370f0bd0d282a3b9a6a978196d3535aacab8fb77122f79c855963812c4fded007f0d496f9343271b027def9e64d7af00621d622ca236546f2169cbcac90eb5ba0b053b72d91ef4465aa5f742a62579b1143f58f1bbc2f2efa5f6ae673418cfacc80113ff376a2f5af34d806d9de2c7f3195503abb94cc01c66d4fe6f5336cf5e3502a0fb44e7f4da9cc4387b84d9035b67556a954bb2a727320a20d079930249d4020f5d50dd9422f55ba58bc60497361333c14ba955eab69d71bbba857c882d2e0e695135ceca226d9b8115092d0360a635768a55375cf65c998b855eab3013160251410b784e7501eaf46ed656b82294e8f3bb96aea8b600ced33237f507561d02e40e7f13f3ed72394707fa033be5dd36acc03347feb3d123c62b04e5e09fe80339ec1cd79f5d0a14a26ac92dfd73a4117addd52ad937f22442320bee6ac4980637b889913540fa8000aae0a7dbbfdf6380b3b21f8e7119cbd573a3ac9452930dca5427987d0d22deb5b0d22559c02ae9195ad40967d98648552a0c3c0164ea07dd348fd6abe37c5238ecaea9f03ff5834bc437fa391f7c75fe88dbdd7ea4700160b3f14bba7b5df6651ac163025b6166122228daebe3f7564c91ae21dbf73f02a315cae010e2a34b53a3d46b703781a1ec15516e84799350282a1423cecd050871e12f65ebbe28146974ca5acb7618f86b9ad7a2ce868be09859acc6b40859038c02f411ad4ab9398138eb543768f7cfd1ef8fbf45ccc19181b14fd7928e440e689814670b9ab15e4f4e91fef74d210caa05bdca1ef18a57147318d654b8320be60f311cf800114b7b84de73a31540285dc8696b1b5582ca75e058ddae42c607bb950cde6d8f91a5408083c11ab5a2fe698f8ee34f2a59b569a0d35c64ceaa0e2b6d02c1210f5ee8c87826e63605da6bd042af4590c9f03a122435a2d457840f false -check_ring_signature 00c166fde991964e6e575f55c9c1f95ba5f078b333cb4e948538cf513dddf5e0 13888fc028e6cd14c7cb7be665ab410324f35468023082d06d282482ad74f168 27 4b6e81a6031fdefb39283f9d78817244917097bc497a8a8f363a711714d78646 ae185bb0d945a69f1c2e8b2d0a3a6b9e04f9d8b60c5e01742f28a99f1ad5fbf1 af086439278da8ed42f173efa5b3a15dbb00313221606985be0dfea4b986211a 4f53a201c9c97ae232789ac7db45b3d41e87a3e15987b3131c8048e2a599c272 aebbe28176c63787358cabd4dbfa37aa2b60cf13167cdcb1ebf4b26e774a49dc 08464d00e98b902b07837bd5a83e675816e3f043242f9361d0b42f15eb8ed10e 2cf1c814076db692e2e44a7c41a433792bd6255f4fbb96ae18a93a391463d12d 9249badc730c279ac6701667b550d196fba9d10854355e960cb2107f733dd5a5 d6e3e41861a1e46388f3bd80b94696a3a01c9d8986753a221b7da69f2101da58 d54402d560ff303de78618d55fa06d1371126078f5203b78e12c3e19778d8bb7 f45178a19bc02d467fdc361d54cdf137840292d6210f94a85cc2f5df49cc8946 b2814e669cab92ef5ba8b7172ca685593e0d109fb50fcda3aea015d620a42772 ba384d5b5174f0b4387acec2f3654f7f1fdbf516d151cdff483f7c498883e1b2 a01a993b4654424969fd1ea1296bec02a4ced86380bf061d4bdb8141d95fed39 534a48e5ca27adb82b5d95dd1b0bc9bc278ec3a1d7b33d77e71ec783b22788ab b67efe7783c6f15b834e5fd43b4641920199fa85f06e45b871aa83b35c44903f 0fa5733a9029be83e72d06e566fe058aa3e7d81d40fe4d889f95f42cc4ee1a75 feb2641a479ba55adc5a11a6ab7137674e65ac9bed555323fbf0fca0f9c5c589 135a93fee00c16e4031bb73d5bfe7e3796f98d21d27988794fd4d855c354bdee 566892ef3418970de2eed6f126b239ec312524d63549c6243620e9fded957ebf fbaca07a57271bff130872376e75e3d52748ebdd8881eb74fc9fd1f8bc87af47 289b77edd5c4aa47a5834cff59513f62499dd3921144df502f7b206244801ecc 1b6198cf7985e21303f8d3602df025112f141547fd0e6cef628a90406e249d6f 99198aa61b4828075a39fa766476c61c2bae380e7a77cea486db66486f1b17dd 45301c3d2f3ab7dea069a323e0b5ab567e4dee398c2abbc1277dd5d7f03b6cdf b53b593f6f866f4676b38a25cafb7f46e78e5ec25c8bf21d9f53a1f2b43b0616 d249b79a840ba5dc970d4023ae92c6bcfcd211c3b1bcd3e83a4becb05842481c 7d22a150c31e29104a4a00d47b8813b18e094964b259a2b196c6e2c01f5aae01ceb07d796e8e5788689d9351bfdedf4635cf9247b07bc81ea6f4470a19d98c078d1926d836a5489b4071bb24ce4f2c5ad225c1ee1a1b085c6c3847e7fcac6b0ec2baab5d569759ebbc0db9cc9c3566987c253dab4d9ad458815a22da2250510779fadabfa83ca6b0a07b1fe3874523a4aba453098f6cfe95e7ea539556022a0c61a8cb140def4e00ab22a0266e153770afbf7bc8532ab1a114232bb456d71400a0fbd379b11a7dbe26a245a247fd9723d2c0e8a5a0c57230a7df585708f40d0cdcd8fd6a4aa47348403e27719bc67cdb7fcf7a315ce4b3da0710e55c7b44790e623013663bc09a6e46d709db13666a4230530b3843d875de1902da3d0a24d60611e854580a3ab6ea8c7e87804992e9a9e2cd76f5cf9e137fc19d65aafaa322006b8548cf5bdf577a3cd98346b5df8f7bad6ba0f3054339f3bf515b3fb75036062c4a646f424ae8848ae43246d7a8ac745e120593e8b88b57fd9c27dab9ba2f0634702ae13cd3f3259b4d34fb95e43c4242f562c2b26ba3b84e2f8fe85c86a409a2ea212c4ff4f9593101a30f6835829d3b17a6dfabc2fe580ff8009c416113080d4d60e0c50269f728f482eb55fd6069aee329e4e03ac7096c4712892d024704a35b5bd10adfa23bb3a90876c4957a244c74df4cb8dc57792e498981e07a6108af826059983e09fd022ba3661cf46d213903fc7b131b4594a798248ca4e016066e4ef1a1474c3fe4e1bd44cfad6dc1e02b15ee468351d1939db0901a609e0c09ac402fe63de7ff90fe4df8b3c5d7ce6c3783bfd827f14dcd9cdb984487ee390ff215ef36c2af47fe2f8d34b302621c0460675bb6368bddfbf29e8ac88ce2f1023c4a74265102f0b4893c8bee060155abe77974ddfc3e69eade9229ced6c8150ccaa10fedb029e3ed0d8dfa1ee67f7ff64167acb7e67cf80bd07167dd50addd0a2043e4941eb09f831c4a00606931549d027d52daf13521cd3a70c9d22afef20f64120fe91c6be39b48fad5fad0ce324b2220fe33d0c955364d062297a058b7010d76e9502be77de0daddfd01b0ca5a021cab55c9211ae25808c7f1253381610778b7e0ff20b4cd55e85ece31c141dc23103607e2093150067099d86bcad6b6031b7aa3ac668b44d33035a94727047d6f1969838ab3d2582d4ee5b7856bb5d20b0a6cd0e345430e7a60b12dc0dcc5fb8e868876ea54942c6b662bf9a76d86e20f133aed8f53ca75d74735aafd1e38e38380332bec8890f950ccd3c52e4d8663085d0ea51e8d76932e73a490f4118349a97c36dd8a888b4387d950181be01d110f6b501ac139e551b4ee840c39c45019dd67e1dbe3eebddfff16af99c0d08ebd08c0fff43679fab95cf217d187718957697cffafdd7dbf769b438d8ed1a662a00cdc1f7973ae418b2f9de8e66b9aa0205fe63c48c02b81e4d7e02b4a22c2b24f044864d1b7c2458b4b81603258b9dd81d7298b5152a8cfdad57e5d017fa69b50028820a8880a7de486e4646772ffbd21ab565cb6d5244c82bb2c310d4d89319e0d1f537bd24d26e2ea7ae50925cefb43e29675207d6b6d19467bc3ed73ed5bbd0821a805066fa851f007327a24c0b4b5d433f2db924b4ddba43f1e000bc9818d0a03640289b5ee7a7568a4d8d0f9e015d4190c15f9824d3056a7c2da10ee35180217d32a3c565302fa40164d16474a69527b1734512b08e395cf24cc7e3a337d0fc2ef9213ea10261447bda0b9208d8a8769fca90b2b13dfbbaafcc4eb33de820beb7f23688c2f018c0df735aa18ade79ae5b1f703cd1d6fdc8f0ec110d501150b86b2558d4366d9c365a8c8e4397c0ab36b320982b28f9677ed05635f6db41c0e9b54fe2b07b0c7dddf33cc2dcd5cc6e19ca9b39643ef381bfdb15c51e99b040b7765cbff4c38ad2f836f0646111c2bfb1e1a63a4cd0aa0c5d1bf87af87354c0cab5e13cd086ecc0ca4308a053b28c70d07923ea4624e67e3bb408fe77d99a007c3e4edb02e7bf0d54fa8aa65befd65c67c5aadef35aeda73ef74bf1aa91a5c0e7fe217207903915c1462aa570f178ed7b7d16d488d0632f4ab14ca3e236658061d4ff3c2dca6a474e78d16f22bbb8290a14ff89aa0cd4cd3296065d5eecc94060231e5dc2cc1c7fe22c6fb37b026eb59134ea022e88032109ed0e198a835ec0d1514c44146e6a3e106d2700242f66f95c914fcc575ef3e90b185a5498efbc808a66e42908f32083ebc8f225d1a6951fa88f0e10167a6a0eb70517664fffdc9004b3c40be4f2aa83edbec58cad067707ccedc7615c4230c4b4dbc64d6a7a78101e76b8f5e0723cdedfb34a1d7019eb38b09cb152c540fb8ab1ab10f96316074008941e05776b3f858f19082f05179e32fac22c393abd8bde9bc355129b9e2540d false -check_ring_signature b290c04af485323186f544045b77f13f55e1122272828c0dd1faea87cc4d7f68 d6d7299900bff94c54bf4408a0ff68bb53b219d5cd8c9e027a7b1b505e08c8ee 6 b3edc48338b6e95cd92d54c2ceb34e9b99870788e4930874fe2cf3af2537e64e 52620f44412e2f3c5e4eaa8de1b71d4aa8ff2d4480e89ba1a849816d99de9547 2e8ddf621c59bcc7589481673619c65931ed17faf8e5c6b858618092b7f0b27e 9563f108cb1d9d51cef978569c97b4db7f2905c11bafc44f036e2aef396e0169 15955d23d7c485674e51f5d2fa1b2405c46d3cde5ec1a6319a549f78f7f4d858 7dfa484e5ba723543fc5db19f3ad2a7089f58cc600eac44e67ee2df80cb949cf 5ed56f3a85681b126e415cba1398c46f015df031a335c0990685c8754f76670232ef4876bd3d7f8169c8357ca94f0c668008b3121a751ee7c35a2aac620abc092b0ced009fed3624e591b9015f0071fd24b83dc7dcf9bffca273138c262c900a87f216d3b79c055c6b49b4715dc3346e96910c5661634e5920bf4343e99914037c800834847fffd8bd732520f1afa4675936b7960006d6ffc06e68daf8f2db0d62290e434c6142c7e74492d41168831b7f0d05c9789873ab6e47a5a1fdea7506ccff89a0e8b706ed1775f366ca486f2d9c6e56f92ab7bc3e20a15719bd2db90bab5dcf279b055b24cc1b6a9845672583396b30d22043f1a105dd7ba6d8041a086ff1d0e6ef61dfea05048a62ba6790c028ea3a575e88054ad49bd958b52a620fda3973d2b86877e41352bf53c75c78a681343337637b9e0751f428fbcaaa2d0fb224845c81a4663746d0fe656b8a3eeeedcda51c304c08db9f7f5110eb0d3c02d6bbc397aa382ca46549af1784a567dee409472826b3c2c02be7d1255c5ccf0b true -check_ring_signature 10f452fddb3d0a3b9c684c85d6ee95ac46ae3d84a6af0d0dc1d8a131072b292a 70940a965ee2d27916a0c770d1a23b5aefcc03751e64573858b76a1a0aa2efbb 14 880b09c4fea08fad3dc513b9f5c406f63ab9f9a416b2a0d3023775d39ffe21d1 11d14dd2496d819324120a052e5d0a4f269596baa52c8b0bfc70022d00947196 e81d3a5e990fe9918008dcd44bf4706ac4867d47d5aa3206d39ae5959912070c e9cf1b99caf4ba146f02283d66a0044e051097280883fdddc2c8800614dd6e9c 2fcac311eeeee45c94a9e4d063f91ad14a42aaafef33a60a70a2e6407c2de0fa 835a2ea35274b3757ac0181bb8320d2906ebf52d2e977ae19c73c87fe29fdc0f f4d01b10d401e150387874620af6b79085cd00e0f286b1ee02591d4da924886b b553e492a2e1e4f6b6283e0ac9f18f4349f0166388ded537dae121714a97edff 7a37cf477f50a23afbebc93685295fb1a14e2f5b8887d7bf84051d2aadbb34fc b52f7beb84ee7bdf57f801db71cdf78c835a49cc2e73116fe3db890ac7640c9f 073dd0dc33b1a956efc1a524a95e66eb508533a3afdc50c8555f93e2c89275d1 92fd721819ae1f94cca6afa74d7a59de504b253a21b442e27e01cae3b1ab3f56 cbb7d37c133952316ef9d6882f3a410a719709aca92794a33c2f6c7a92a10e33 ee14823cca605fdfc2a154af0af910ceb0768abf494c09398632544df3bfeaf9 f5e1e8a07680a575d364fabf3883373e6424a064343c8ac901ffc752d4986800db491b1f78314de9a38c8e5279eaf40990b122048ff3b5c97f45f85ae4ec3c0c971220698e3641f9825c5a2f69ff1b402fdd29820cc297c8d2d6ac79439a1304ad0810b9426a5f2bb2f43d09ff0819234fedc443bec28c98e6169bb7ef78090827a63db10a153aa0181e00aa3be88b189f29bbffc40bd7541301df23116df9064307d388d5162e0e0a504b72e6dc86c6658296321abe65188fc5bd72be5b360ac243ba1537e7192ecd87e98dd395cea1e3c333cb3206e3c2d30a3f547be83509b2ab8dafc020b925165543efb23047aa32ba0ccd275f63ddcd61140d4c77e20162ce2a5c393da1f01455704365054e5925a12d46cc3ecaf5265f3e554b4d2e0ea608728f15c6b625fbbceb8eeee3dc60153334c23de68399c66882f4e6a4090b7d1a233e4df789c8c0cca9a8ef3db64ba3b0948306a18b327a7e86cff4e7060eabaf671c0f89b73c3433049ff53c50f6ed62afc54849f6c84f33b4d5a8a57907f82b1b379539dfa6da3019efdc506bc97c65b5e4242a4eb25891b90266eaa0031d0afcf4f36901257b6c3b2d9406c6d976a1398febd7e3f9770dd0ddf81de60db93b893eea1eba835310447c45696797a257afa2140b68f7525f591bc609ef01bb4bb2ae269f70ec719bb3c175f0aaf0e9a79f349bfcd43d992894f13a225900f3809c7a3972be7edd0dde23c4971216d7f244576f694cba0e26aea0f83d35003fb2b40c860d9b6b1ef90d794e435dcdf79fdb159f454476d0c4918083514501bd44a704eb5905a0280c7e0a947619d6e24765e24b445b6d0a41cf4f1e2f1b039844a9b7e5ac425fa993c5cdec2b4232a6439ce57e6f9484bcec9789765c320af2ad8dcad2f96add99c80e597e7285d5e7e083afd8b5c33a63d23672da88750e7c0c0d847f94af77c8311cb1099018e95f42bf34266a71a5d0a31196f3c13403b3198a8b5b2ab1ac8d4ad804a3febca212f70ecf35161a34f52bf268dd44740d2a3025648a61baa19dd38733f25a72af7433390dc91c97b417e71859fc79db0101dc7900b88bf2dfa89d9f462c01c15dd0b0528701d6a8a3f90400b29b67d40fbbe2ccdca793f1e1f1aab7896821e07777762503af814f47e8d2c9f1d7bc7409928cadf762c5988e92686ed31fb5e35276e6b5723cd9672b803ec796bea78002b0fb3549d1c5075a48eed362212ab12c7c6808faad4d66046d01aa87696a2a0c true -check_ring_signature 8b8052fdd749cd8829d04c5673f9fe93a8bed11f9fd8b711e10e39ea2732ae8a 045f99c6aee16e22df83d52672a5406a64b9da7da999fc09f8946845a0f789d3 2 98e776eb4d5f9dab271cde4925df0acef546918f71ad9f3a6c8ee44009aac8d4 12682ad2e5d23b23537853bf1d8954f8ccc856829672d04351babfab3ca3a7c1 9884435df7b55852bfe6286fb7ba6a22fc9e10fd688893b788e55c9c7145a3b0bde0710db0b95ab355b14106f7a0cb4565def61b88ec218b9f3083157f469a0ec09abf9399df7e7cdb36f46ec50113e17598512c2c040ce1e760891509cbf10d209ac842162e692c55da8205c994dd0a0d498d34118543468b552d9934183305 false -check_ring_signature f7bdf378c952d98f8b45d6c944a90029470a88ba352d728f4bbfd1752ec51a57 09457dd660ca181c80cd1699b0eac6a3770cd060ea29cb8b43135efd62699a38 9 53304409375ae7357191240ce60c7130f4a535eb00e2a907aaf8cbde79b603f9 c5a8b2b3614b564e64953376f6d59c5eca4f5f198fcf062767b50d7dfa692c3e 41bac0673f2ad39f7a7958b0bd7da19a65f132c55752a033f8aedb8f95d93b5f 2aa8c34d9ae71bd938d0454210ac3518b50cc29d78fb621611ad34a21561f8b9 f4543105d394123091c2f0cf21c9fb90930427c2d467dccf6dfc374fe4a0bddc bd1c763cdedcb4e471aa02a7d293328ec06508353b5a53ab5b323301d5140d14 93e3c7ee8b88f30a118eba9cb981e09afa4d68e0fd59c402c0c8c38975fa7b74 a1a9d9e453929743640e7f3746c98f9ea927c7985cb00966c175b0cfbcdff4e0 08f9e38f4487d6fc6d2eb66369ca20922de631c1a40c710b922441521cd3f36e c2de6434074009274bf3d3295eb565dc37efd6fb105bcbd44b6b37ba7c80e4088eadaa8fc016e70261af012a63879a98552ada0b76dbe5d74242e27efb72d9009ff3eda6ab2463d56ab67ab141146e6fd819784221cee1010371d2dfd6528f0c09cc11a03423b249f9acef5434b1b472484bfae909a984675db3c639f7cf18019d51ca19c87bdf7f63c40434bbb20ddf93796b21745943505c14a837b83c4b022a24a68f007a7c56af86d5e189e31cdcc83b742cc1245740b7c94913fdcd520edadf3ca10715bd9a3568ea6d5c826269f46b5029ba014fbb48c63ff27ecfd90ec5393a679c4624a60d012cd6b452519f3b80e8f84494c70de92d2c54d958510a564675bc3c7e3e10204f0979b95285d10f8113d154f155bc39795d54b9acbe0123a9433a4f683745b5044fbf7d136fe0add30469dc28e6ec66f36dc00b1b24084d1ada378d991c46037f9e8affb8193e9e4fca53c86ef72ef8ebb30761429f014ba12cb4166f9b75eba9049648dad76d85bd35d1b68da5a33b75abd6972db808dbb8cbd7851e7617f6d506b37d4ec9609e7333a7a11e8a9f30772cd4b8dde6003548604efdc6d42aa41af84804159954733f84d427bc9462fdb9a16ec7d50d0af9470cd82133b632ba143d7bc217107a03f1f21ba3f37f0b37678bfce1151d04b6da144f26f32b94ae0cb771bdd09514e1ab7d1ec49206ffb5c301e75a13ce0bd6da2cf034c95a86c6171984aec44d6c8e4ef52933ee4aaeb473fa40c00f3e0e89c9f7120b0d5de1b149c9ef5db9d4b0731613c01917c58849f0dbae72e87d07 false -check_ring_signature 0152908479776a953687a784d6554b5c9e91f66409e8e43c1ee285f2fc99471d 0bd543318da95132267d35d0e91af719e02173d19a9b9196351040ed2ac12e98 59 1628bcc1b4e6a64a46ab0562cb51011e2b16a0188667b2f346506a4c6d007845 42787c4d1202a0efee5dd69e2c49c7bc2125c39ee2b18aa8ed582db81592b9c5 ea990d2e8a8ac0fb1841753c730094ed03a427966c59ce7decc10ce0ce980a60 a0161df30ac046157bb094248071b5153961748b146bd016d7fd128596fa6fbc 23e71c9c5b454cabc05eaed969be1bd03ef47182b807513e526806cdc4aa8973 56769299aa5a0f8ce651f09827ebea8b8c485c9b3dcf99708384f6c74316fb92 f86ab994c962e0c8ec985907a9d23f8110f46d97774bee19f27f6adc950f8cb9 da79e8700f74b353da39a9ecfef4dbd7611b840dcf8f455800801a6c8b9681d1 1b3deebf2b739713465272a42f9ffce83e39137717b3380547ba4fe27d156ff6 d4f61846f2cd7b38aed5a15572d2548b7bbe79f1e61579401fd11852c0891891 ebd4d5ee8bfafab9d5221b88a4f0c2914f6b79dad4dc4ebaeb236422b8a74597 b39b8c14600a666f0538f2648cbf51a5c8a09db27d51ce377a32531bd81b004d 8df2c271f744bc2e4f20e3f93471d945e175e0c77458d5ee15ddda88812b3a40 c5f128ef5acf408e4f8f8338c302a4e1407597e3c272e9f00c28f7067998105d 87329e43419453466142fed426d2256f5431fc45a8c0cefb4fdb923d6476f3bb e346d136e7fb72adad417624a26c9fbd2b8b225d158cbc18b036da031c3f5b0b f460e1f3e5344f0d7a11b5ec35aa382ca7da89f821a8c72fcdaf9fc98e3640eb 884f5ef688a9e78433b25abd86c360152324d10c36d39ccf61661e67b14ddac0 e39c9bec3d02a135c5f07de18f857c1e16e5324ae513ebfc18fa796765dcc55b d16f3247e27ea2c1ba34387e456c8ab977a0d7103e10e1cbee2bd12a2da19019 e382ba49e25785909f36c518814934967850a94a2633332d40f27fd38f0eb946 3858b5c2c731a4dd98cb3656f2e505ad3dd95a962499a24709514ee10db44c78 10a7c93a8a1759f6b91eadd5f4b93a69ed55e178655fb30cd9a6f46c8760d32c 66130e12c3acb3af49e252e9533f43600ac2a2d1c78c9d552e72f8c44fc893eb c76747446a6bb51d6c2b06528cbc84b488bc95d3e5095787963459b926c7f4c4 bea180daf39a856912cb6276c51da6b104c3321c8029cde33f189f348a0e5686 a09d06d129f686625fb105317bcc1f8cf7128d65141f5ebd629be73a9330f0c2 1a2f694083d535c8db6043c1259da33b46537d264c42a08fdb9331d2c0d0c0fc 19addb527dcc189399e6457c905a8a05929c08cd9256523702c291ddcd89a561 eea164fdcfeaf9cc6c6c2cd29279d6e3f0f249883c2d5b449ed00fa54925c0e4 79dc45111168edb808afce2e330752c3f774f0c82363888fde912bab15b75af1 758404cc56d80c95caf36a8e72f3bbc7ff5afcebb99fbbbdda2af6fb524d4c5d 3799a667efe2eaea4ac60b4e29fc23ce49bc1793a08f85bee2a6eb216f7a93e1 a004d17ff260fb97b02684f689904aa40ef93b52aa916ca5c4c60dc4c75d62c3 41740eaf65b885907c80d0fffbebb01a9f9faa5de0124593284ad584f606ee0c 8cebc0a376926190f67c95b538b60baefaa785c5616f6b0d4478210a21d23537 4548362a74cffb5296dd08fc650c85c16efe84569f8a94551011e56a09ed9618 eee88177079c051cede5030b965331cd5eab796c92424ef2dcf7a89392a29f62 a300e956f2ad4e4b67d9947bc4f2fcfc23c3d9aaaaf58a36508c6cddff3cbfcb ac174dc830e6b88aabe189f57cf1ae851b4ba22154f3f6d5f37f3016fc4a4864 9d694ceacb1714ed7e879d571b14d639260bfbc3b61e6068fc2c78bbdb5415ae 8c73d19b6a6a96e932a0a303601c58e29176fb04b808f14b7285abd2b86f07f2 bca2d6bf4cc3b7ff8cf978f929556f53447e2ee52bfefb4d3c0fe4044b6baf50 9371f82555f7019ad4a69deaccd6fd6ab3c3d890f474060c7d817a7d8da73176 de212f99f4edfebc0a5bf6618b0426ba720a6c0ca900d4d37f9079c65c035502 583c0fbc3b0e24f2040cff9b6925c03d817a0075c77091d8d096076290db6d7a 4fb52ad360e689fc357adeb889cc6b4263a5dfd44dc1cb2521cc847211122572 f35bc2b7fce75317815b0f905472392c3d1846cd8b092723a2fe4699a09c6f8a e2fd5ea1fd9dc33a1f90d8ea1ba4c52e0c6b201bc7ea07aab69a98ecf16c0851 3c16585af5c424e968433624dacbdd6184966a5cf8c2625783824e2a7f96fade 5e8525af0c6e5533eff44c90c04d10c16f02c3b78e31291a4eb22a6ef24d347d 0efa25e30ebc39fa7c834fdc8977f51573a60cb8670ea2ae475e25f7801e03c4 96a14a00d1e02cfbfb86633d06ab2e72babc9391e4ef78dff7aa0601b196cf40 b107beb98825dcac701e3ae9d020b29b7460a4b52ead478b26807eb8bf6b292a 54327c7406b898a5a6dfdbb1ec1f9de740a487f061957e83388acc67fbb41177 46b3fcedad9c9ca9a0fdb1518a89c109958c0003bf04927353642d6937a5e07c bd353352a415db9dc3269b65e6b735c775f15ff2f3aed3da5ef11eb3006ccc9b 2a3e868c9b88e68373815ff5171212584fb732f94253efd2b07edb35ac83bf98 eb9debe6ffa9c219e078f4e18c5be0804c55d79979c542f62bef567f61513858 c13eeb61b1903c58c14f72b06c8b32c61321b93017f45d636bbae186ba2a7f0d300022c4b7620cba4a267dc6987a4107ef59e1ed7df51b90aa243646b312290d7ca669cd5d4026e3d3fc8bafb138281591fef507eeba6171db097bbb45d361089f9f892ea90d74ea33fe71a8ab0a6eebb5c9603a46a607816e996c56d38a4304cb72e9db021503a7cd0145a3c1e05791f964f7f7993656e4ef4eb4fae7b359088909a0d11954a91c39873e123b815b65b1640b54ea73bcf6f45b2e236c7bd20fd97e4edf7b86f50de6c5b7c20bc49dcb3f9cef447424d9f53c362e0cb264260051a566ff948ad61e1e0cb968f6c47d038fbc20b356ac87ec4ec0a7ee34b7b50857b095f4dede777816282ced67bd1d08d3f1e0e25e110394226fb848870a290466cc57a88e09b2c6dd53eb1e739db974353b4082773a48d4c4ddfdb230486700d436bc996aedd56e7f3aa37110bfa4e40935eeb5c260d3feac026613a045f90c59bea591a3affbdb727178ed748876c5a6c554671c7d050fcc6765e58b544c0185046f5ff3882f363420891eaa8eca0ea30bf1c85bfb834ee8d5a7acfdbcca067932f0aa80614aa16bef02701c4af89e635a2ada728a9fc8c2139a5956b1c6046d8a495ab1b72e779b960ee0185a828b369f23b016ee484b561feeca5692ad0892ce1b67d40db9e0d08badcba0afe73e12a827caa57a9190d94ed9241921e3059ca41502ac9ebcb369b7a4c93e8d3ac7a83ef9c57e0d97ddcab3531c9dd65c01b4efd9b29a9f2c95a03de90d6d42cd3f90e39fa19e11d8feb2448c454f4e14062a80cc6efd0a8020c895c394f2bcfc53a29d3fcf0f668c155f2631fc2512ff04974673799c9fd146977a5032e96ee4bd5c7c1f7a0fd0b46a19b7fc763bd4a30934d8da25ea4d6657d0fb028e3b5f8d13c4b79becb454511019f829edc5c9df0b3ce15fdaf1d5487d75a1d3946668449c3781c64d4791b5dec8bc289b4cd1a605fba5c3a5d9e02a9e5df920edc77bd78e5a8a91000798b91fe6a5d4f545a7fe07e52d49524202735ce70eccdf6b6f267c97530cc39158ea19d787557b5c0a560b6fd989830f6fcf4b3e2b6bf00148c13041da2941880f909ca07784e7c9765d0780d3769b3e4bcbb1bb41c3e8d16aea560e4b85288b18ffec009b6115b608be054e5d1f2f7d99a797f1531a51e26eea4c1e3e0cae860db46d70693401a6cbc60f7d5fc3599d0a95caff0af45d71d68af58339e217ad24e3b2556753c6d296cf00033d78eb1fde3861b2f73b6e8b21d201f5d060b72ddcd3788dfe88e7d1fbdc03a8afc71bfca5d672f41234244493cda17d9f18c1dc70723c8e098035c1b9fa01c13e7bd859c2952b8e05254ab415c4c41cbd9a19156adb2161294cad0f0ec0064549830754bf243780579b9f024ec9024f60386314e0dabbb077f3144e25d304ae7070e27aec712c3562b6825ee18438434e3e2277443507d3f3572332fd3e0dc51e266b45e7752d5f52a7bcc12c252742b698df6900a154449440d94aeb8f0801e531869b5ecc4aecb2aefd0a4d92cb92bf775fea08958458bb724f7a48bc03e8b1a6cab7aab06404acf4ce182a1dd835df9b2041ca5ab5838e7deda2cf9e02265c54cc13f9eeeb2526419856749c558188bdd6370ef83f33a444d29a0ccf06ae2042339af351dd8f9b55e793016924273db28933cd03e278dea45d236f6e01112d5ae6ec3f9ccbb12d4513c4f87d07d1278203fcc93390ec1887ea3451c8060ddae2c16724c971b6d709a36a9e682793e5e80577b70632bea3f319f87419032f61729622c289a449e10c686a3916c2c8e0d3bbc3677b512425966d12640d043c0d0b26cf2380de7055ad4ddd1a84e18a3c685897788c3470eba9ea04589104ffa9b6b4fda501631afc97b357797dde2a62a1e9d702e32070ef7e81e119410ba647f207e0de87ee06af9ec871c9ef3e5b433ddf94026edca296349f5fba24068cf6862d95474e19d0a872c1c39f8549276921587f1b257c850b962e1f05d006b00513d8c47120e764436251c291697da5c5dac00c4a641073ca79e8fd0b030a1118a9db891807f58dee79de4453a473e9f557923b612fbe2732b414fcdbfc08db4988cfd2965c0c57d45d807b8e3a2b262f129932b6f52a1a9b4c69427d9f0586d172c70704832149f2d4b84fea91febf3b97db3b4cbc92982db394280d250bedbe9b15389599818b9b408f6b009fc070a01c14de250d20028f1018b9b00d0b9fa122f23ca8294f298bc8216e751b586a6ddd3e30f6b30b0f2a61a7c2cf5e03359c753436790fee5847ef1be1fb290bf320193e1a1416390e0bdc10a6cc1309d2af65d7ff959fcf719c67c76ac6f60ab90d06e808899be7a5dd4b7b443c340ee36014390659768ffc4566ad22e5e53e9c92d143af5ced003ec6e2869009710a2f44dd857ea2bfb902bfdaf9c9689f37a7075a152f299ce2fd036eaae3a44002ffb8d91f707048585f3daf5a99fee1310067667433cd0e3b466a65d299b21701abf28dfc4a386ff026787e71be0c6dd3d0ae32574817acae975a0c12e2ac6f0bf62bbb022f213a0f1f8fad4f78df16518325057cef3541ccafd1cad8f7a1190b1b329c28432db24725d9ab9502bb4e310391a435cbd7a6d52cf8fb22c2a2910772e4d725907884f785df54ac1ef40bf1f246252997099a55b080b470f51d690886892c7f5c8ed805c422e61f6086145546449b24ad428ca62e1258ae86fac00477806d6bf801f1165c0eaa7fc46b4450fde7af0ef14add13095f17c987266d06e8af38df547f877f6675e6030b635e2d60b6bc7a817590b550757b41eabaf4008f168d7dbd325eb3f8bcb3bc5e3806bc85f1118aaa861f9fd4cade2b3018300b25e8198bcee8a46ab6ae78dea3fa25ff2a3e4455d41dccd30b3a80b82179a206e18e11444125b2cdebea016cfe77953aeb4b7d9ee9e07b86b1f088af7444080fa8672d4debb320aee1b2cd03a4a1ed8013700da2afbc705936524b001040bf007d104168ab7a2f451a53ff71dd9148b1f12ab13ba954c798292d309c3cbe380f4a41e7c05c4274be056336e904b27a155f26a950a0b65c4a5b7d4f80aab6090e68546d968163533bc8fd09b65a932068ec872170b6f6c4c9c907e10f333d790ad73bd4619e3c1e10b58c9b750ef23e7351aaf1d6a0d7c8f392de6ad492f2470777224aadd404fb3e312e2592ff1312cc8f1c6673e16aae0d4f603e6dfbe9e80deb2b531d400052126fcbc2c7799f241c12b419f8744fc72be5f82330c1c920072edae970f4d05cfa501eac02ee76edc1ccd2caee8761334fe3da8e3f50be6402eeb86c750ec4083454b136ba339252ccd8740aa8ca09ed2e206709a1ed93f309f40a9ae8b1aac61e551ad480a1332aaf0d827f9463198baa4e0740bf4e87a60e9515e80f48ecdd93aece8f08ef1ab3a8ed4e7926e120ea1485ce005efd3f31018ebf8f0e4a74c754e210c4ffaacd12db598eb8c40856e5862e95f5a15705e60bd95101cd0e2f7292c3a91b0ffb6db9ba36e771c35b20f811b58d2f3a32039201570f9429fa7bfc631b6d000920bcf03122152e1cf4a6274a3f2188b03d715c0684ac8a7187121b46c042c86a608f94545b6aade181b993d9bfd278df7d94b4026b6a16a67e868738404a8e7774c28fec76cef38183462a424034015bc542330c2e51a2072c16df8a13951047699755bf04cd4a59ea935fb3731c41c5e880de0861212b33f2cb9a200c1bedf4ee58e93a07fdb45cc263d379e151a36af522fc09f7750d85b6c1a23f7f85196efa955ba81ee6ca9170806b8bab6a1b39b7b99508d0621f7a5cc119df96688fbda210d586664779bbae2361f76b2911db028a590d3e4c82a8236b011f55a88f4b7b9ea55f6074704709c6fa781874285e2643f807020e2c129ba550a33f708b7b2086cbf959d226be4e49bc9429ac49613331bd0cad4915f4165b14026299480bf5040f9e3c00c2ff9a90b6799babb0890e320a000c51f74fe2a781cf025ea3d37053fc7c9048f5427c0630b2856439c7318d0306fb8f3ea025d10891598c52650305b5cb5c218484a81400a08eaece9d26ac810fcb681a4534ed133417dfca8dd1a7a30cbc5068b476f2d01de0247fed70e6a60f0ab7747fc1edf56e9c819d4578d225e3d50f5fc23b4b38483241afe135798b046a77f85d9b9a1179a8c2f2ddf8708cca41094eaf5e27061ecc4dae7413883f00c1bc181feb85965ebc9eec0fe0028e0e778756ae10e5342e07d3c7df2b8ea003104205a4bb1aff385d82a23cc0c9283fccec5bfa43aa13d65232b9aee315ab04d5e6874778a78f4d37a5e13c70f2e7eaa8818c335d53c7257d53b10376100d0b07240a37a5c74eeec634640515eef61568f9c1f429fc5bde3f3fcdad27df4909ccd213a4e28b44f4827b498102991f245b0a62fc827758917128aa7b666ae309225268406ac445c3afc85e179b0fa552b3a8135f09b5bd1f3a9d06e8503ac90ef4b895849c24df9f64f463fd776c9b1a47f3a2d5558c6fbf29eae21b80cde1042399b13d2c18cf3c6dc6e82d8d5b42cda846f074bd9680f206b51ffe0c7ffc0741511b7b26d19bffc4199e89eab65a41cc5b8d0df7216242eb13f3214af7920aebec68112f16792ec5c598f4064515966ad093508c6a912cbb5842f3074c860c2382ea17e6f5c0798dda7ff97991f6d300de108a6cbff4acaf418a85a08dee041d46ec37cb85e92d5ee761beba65bbc22bf36cc289a3157b0e186282fbaac60bc7c687bd6a8852198f4855154d954d68c1afe93e47250ab212acb2df58138102d07e0173a8b0538cf68270340b0c0ade6d2d2309baf5666aae4dc58202acd009d040352f4fa00cfcbc44d21bc5d87b421e7c952d262bbe1335ea672d9ee4860182f25f3a6b8126744001a90c7e7227e046914fed18da21535779b2dd17682e02926c7cb8bedf357c869041a9fc6ff603e6664a9df7e4c7acdc1186f7f18a1406d64c38739e8d45d565810c41b2874c58d25943c6d349ab15d592dbff4ae79a0dd6e182dab0507427f5f6ef0418a028a9248b49f2dc4b919e20da1a343c61a000a522d9dea20b6caf609d7c3c2e3073f049c5ff349e67faa6ca1cc4b3523d8c0ae75b7ef0158057aaaf7e47f3a3cc2c64497b9492a85e903fdd5b439211ca400e5317ed7a898134b7a0fe6a46b106f575f85223962fee1b5f6f160bc8794837053d849ae44406faf13b5e5288e1e95b77edc591605f2483198c620d6542f9ab026cac1b8b083267bce3d769f2e05c08db1a28116f474a14956ba03f21905a2608 true -check_ring_signature 9029d6c3eb54001c384fdf2eacef42f633d9059b96d756456e48ba23b08b097c f97d599ef1c8c9fe1dc997c17ceb9fa05732e83fab78ad3ba1fb143e83de66b1 46 396c1f3380ff3f9e8c4f4213198e5faa674c2001dddccde83bc1531c7443902b 925adb92914e7838a7874f7157894a0550caa28d42835ab2373ab0851f84493f 9a2c9cf9ba01965aa306d71a665163efd47e9e5db89476d59f942911922c48cc d607aec9899cfb6e923b188d771b8dcf8b1d32610e180ba6439d5f16f8482371 f6b4d9fe3c872f7caf33d0f6b3385753701868048d0f62b56e64639e045bdae2 89351c23c5f6016acbc189b86a3654c1ac8428ac39c9eb6f8eb8dab5cf445e4a 3a2967e0b01f822bd28d79681f8fc92ca6fd527f368f4a276e5bcabe095ae975 c4ae847dffe398ca68a2e9fdd546767492dc4b37f44e21a8493e73722cb5c260 b252d21599d72ea5062812d28f8ad89b703a07874d9cb912d1d278c5a843afbf b4980e66b4c6b7bef82df93da7eb432f451c1d804e9f852118c350c154b16aef a54c44d2a6795691762eb43ed0bc4fe343f78bd778400713386dde9a99f7bc12 43f48aeaeb79fef93ae9ff3dbb237eeff8c98c3b1ae6ec562a3b47c127f546c2 c931d4de2da0e028935551f7329105c5e22b0ac2725bc56d2fe0a54da53010b9 5bfc3c747290774cdc01ed2f4c71c1b9a3d6a88e4001d8fd83f29e5e1b0e6686 a9efdcc5bff8e5cbab4addf205f42f6b3af8d9ffd0130b8b0869cb4f6715db41 f01d11f7d3034aa6b9d40724d33c81e07d487a789d09c9e89c1cf43b4c500d95 45327503ecf7e8dcb9af55b93b6967efd893ccdd91f8141e9fa95ce2ae630668 ae8f817bedfab571d1a207dbfdcbb518db0530f1bd14205f42113a144c698411 bd4fbca0e2b4bf4d576e06b8bb3977a5e4ce716320898975c9577da47ab62cc8 a5b5a4e608acd368d5a1718899b59882b91d0e02dff515f005cd030a1279525d b9cc6f971b2ec5b5c58b239e2e348b04750b4a5d4c9dd76ea1c71fb40accaf48 4edff9234e1af2866ca31d401f219271703482bbdb201e22704353d5f140d191 46195fd413ca7c8a6bb4fc34f5580c20a994998624d392c028cc85c243a5cf33 81af4a7d1729c1a39880011dd50939de06f20f1adb8a61a72ef1a0891c323edd 86ae261d4a2c4ca06a3df46958c1fb854bc5d6806bbb578870e98d1d4c1aa0d2 56a6fd5eec396533c7ce505ead145a1097b9f25b4d730a4ab0fdc32d648d2fd3 284ff3c4acfc46b5f7712f8eba13168ab671578cbc14b67dc97fd429ee74debd 6b8cb17d33c91b0684a5ead5256182b5272fcb0f6197a0bf3c3d3f25540fb929 2a5ea5f801409a3b6a4137d0499e545735593061bd66c2ea88073942fb8836eb 66d5cf949d099e6c62da9441d3810261f4a234a0b1f3cd94e86c9c7cfbb942b7 e1a8a1ed8dbf10c9ebb5ce80afbdad637aa9fad60383735e000a0a0a6d92e065 525c0500fabfaa7cee9c015f652111de9bb22d99527bcc1b38c95c98a2a88eb4 5f2af4e0c0e2f7ad1005d8fcd2f70ba78ef82f7420d529e2ef275d694f1065b0 b49ee4c7c0110eb2f0bb381cd978e8c62c84381dfe0b9a380e43b842f0c5bea9 29d29d0862de39e4a25c83a177aacf17dbaa83f3fc079a86187954904fd5f02d d353943ff019bf4155faf0c19516af6438dfef160af99f2193c1ab8e39d96792 0fb63e510c694d961101d9b36308943e8823905993ee73f34ed746298dc85f26 c9738d390ca145d486a04dcc9f5cabb9d04b719d76142b10d99a3e7c52ea3ba1 37597c8faab3f91cf23a5faa15f2f8f177bb7eb68917c92ebf93de8a3a7c85f5 e365b1a4a454f968b417dbfbff90937b423380e68a4dbaf8840eda9dfa755472 6ad3876d6089ac18a058455e3be49c506a615290db6f44341b3a1bc235bec9cf f2f918dd6b7f233528e95ef7b96aa9b4a9e7f888fc9adb7331c45bff9e3be7f5 905d2ad1bf1ada8421c3b104105a2d2b2233f0167f26f96c8a425bb922f2a1b5 ae4961e3d4daf8917651f0f69c3e565bc12dca0ebe1594650282298285688a72 04939d774d7e80c312c801e0a77b2ff1dc373877a214d4922f0fa65d9bd590bf 02510fb3783a26c420b9ea3cd2a1794cd62c109c395561134f1a1909cb89cba9 1072211f430534f6e8ac0454382a03d438cf745154c2554874c8b6097b7925057421547bb2d74ad93bb1f10436729a5f98fafd672b9916269a71e914394470084d7715ee4538019ba587f4845b2cb5ce27a5473768f55ef4660cd47e8964290f21dc9effa8cad3bdaef9cedd168478239c478498d61a34ef8cbdacfba3d8da082f16114c662c2e12f145ddb4354949c9c14ab9018fa98da82f06f825ace1e503bf462d22217429129c09de567bdf358dd505100b550bd0d753651e6dff47230fe592ddc976890a5c53085b21a51e171a660ebf86872f10d6239cc9f39bfcb305ad6b2c87b8f58fdeffa8707a14518107b2ee20d2f63bdd19d2e417709496cb0cab0a7c45f80f80e9bdb2deb984936533f31543597069a3173e8f07ed6f19c400e32816bfd8bc27184f8155dd15161a272ad3a88c00ca594384e4bd620128ba0a61c7334985bbd51653ab03383242b390d94c4aec313273d7bfc94c8582140f041edf873aa3349feb111665c1749444afccd745b0722317a684281afbe6425b08745cde7b893be411948434ce2a2b2044f94616742d2579bf97ef90f2e6b52f09727fc92783514cc81f501d67e50336d04d429c854fdce2791cd5e00a35fb0b09181f9f0ecc12292fabb1ee6aa2a090e702da2a32d8c9400dc2b683dae1aae401d162716f058c756e7aefce8829e8ceabff6c7d7fd2aec2f3f8af8657be12f1059830985162ddfecdd8246ae3bab71a3bdd58effb301191439ad265c0aa6e2908102944a504e57e19276972984c2d241827d8b92ec03ba981fe9add0efaea6a0717db9ea8b690a9b95b0a38840ee549ccdf600dc6f534dd46556db811faf80a0fa9239ee9e7062c6c5a601c3abbf8e96102c79c976e4a640aa8a5926d66325103e50d16627ecea3ec6d362ff2c4ae32345f2ba456183fcd978534abb627162c0064fd7434f3c2dbfcfe7d00e48621adce67c81a155624172488e179ee179ce50b42721fe7296d09f8bac4f089da3ac2187ed4acd9a82f42dd395a6ac7d7fc69080085f0bd3017c4796d4e1493084800d97ffc0077661e50d4455cd3e854a5f20200ef938b27b3983d06862aa6f070fce862e07fe77773ff7f02675696e0033304acc9ec46ced468dde72aaa8a15d4943e9fb89a6dccf579505a56dfa61fabc60e292ee20fe77ea3e857141d6986c092bf74df19a9dd86409b008209ea399c5303d27549d45d6bd5cc397cd6b87ad63bb3a256a283c53f0b9f6c3abbb7f07efc0246a630f8996db1904cce87610fa2aadead2e874db4a350b715e7b806cea0b001416d8788363ee04b8da837dcfb13134b1bb7efd865696db223f5f84825d06f0145bc9971ce67c244ea095d2f2c1b62e994e9963dbc03166078fc64bfc8c2e0092bcae6383871832a76c588e49af2984a452acd50ae5d394c948cca6ba5ef5f09e84780e58423290adeeb0bdcaec21744d3a0e9951471d75f6443b22f91dab0094ebf1991adfa91ff8941b43f08899f87a93f5778027462adbbac612b61256e0f98d16213279ef403b8d67e49b31e65a0a38061a2357059c60c7edde331c1420eb59b5d78d0c90604d6210360da7b4a5d9c85afad0ead985c5f45354b21089407c7c0a2fab572c8f3aa222712a861f2be0ed9863a33dd8d0c5b2774717f5e34012e98685d71d838a36bcb28387231b78d135ec254cfda40f10ee8a33a6cd0d80b1f20889eb95c66b047ae19607d7c2671eb995189480392c93a3f91c19bea7a0f5eb20a54e0fcfb151f2030d8ab91c4cc2c7c9ff15611d6280b602dcb950d7e0d9f8091e888f8f77d422b8e3fe4308ddf5c3874ebd5e4d05f666d375588a5d90d5e8ec1e294933363ec2316130c6131a655b6573a73c40783989d3248a64fd50c433ae44592b06f9b041e6d16048df94dcf3a07741e76cc5e65afe168dc9abc0ef148629e110a6d4b5c8c8eaa30118817434b2aeab911e5ede7b7fb57975b7107b43132888197d9668768ae7f0617a3bd7a38e31e09a9c057af3531fd18ee3a0121fbaf447956479f3616e2c49fb33869b990e680cdca34dff95aa23b463d3a05731572df7f71c4343729eb1cfd08ce755faf1f50d80c438dd73d05311e999e0a53feb8b80f787ba56872a6a64a6400ed831c37e26dcc42b6dad033f018b7a30d389e246bc63d9917351814d7842838e62e7b02672c3458756559f930a8e854039b34895f7cc4f6ff80e7eed216b33301cc020c87c6f55d13cf927f99e1c40f003069a63dcad8b528642440adafa95c0301f8f6cd0f1d521e638e134477568f0c027a44cf772abb28cfaf03c5f15f26f45de8b5229393ac2e59ea6fa5833f8508d73fcc4da9bb2ee9cf4c0afdad7c7dff4605b26ef1afe7f1e071cf10e512b70fca2a1bc5f1f4c2589e71e00c05ffac93e794873f9b70b2e3f279fa7cc630e40fc5470f9d98fcd91607f3c300e5decf3185b3fc8c88094ba65775239f3efa580f905e2386f77308fe3c0a8bf32c3ec6cc6e88f270c8984f18aea793d10d0686057f00a731ed94a72df9ba1483ebe85b596d74d041ceff6d5a5db29323400f100bd0df4ece9d11d8568a3b3a7d99aa671430e3b54ef5fcf22fb830f6cc064d4a094db0cdadfbd9b2d5fb348f483d53abd0e003d3137c392beee1dba71c182ad403b62596990ffb834e44eced17084d107671e2364cf44e26cf5ef8ff73060ed805c582c77041317f0b8a6357ecaeaf5f5bcd3e6d71c80e457a4153f014ef5245082fcc44d1d6f1f409e5d6a11acadca1ca4d6fe865937c9618ee1c4fa85134110fd42548b8414e97ae43071c272b64bf9e760b8924988ca61e9754f758801a360d9a826d5d4547242e640b5256dd1eb848b0cb3ddc17bb53dff5d0b98861f0d30ef3e5b65298a3d550cb31e60c58cf9da508ba92c23b989b2d98fcd9b584f9700910626d9b2aada6fc35a1d0be03a74278cb3b406849c40817d64fb76609de9908a91972c8eaa485cf9e5e3da6bbe032c63634766a7a5f82094cc5f0b7510c170fdaeaf1aa62392d9b899a05f686cd15bb1c6e5e56b54f4df488caea3dd0b92f023c920a2a5b78e5ff625887efdb3eda9fb921297a4793def0f5e6bf72bb8b9602c7bd8800f9c1fecc8cdabace87183b5ab35d06b4347055a1d258ca12d05e70048249c0f2bc0237f9c6754575361fe119f38a6f27b53508e079d0ce47e1d6f104a57bfad00d680237b6d71319ddcefc7392632d94143ec88740dced362e59de0276e810159968c266783ee3e9faadfec615dca471e557b4c681c83891b766dd0875ee3a3a339214fa432e575b3d58079d1124341e063315e444004cf493ccf607d8987e02b06545808986790277e31f39de8d52f1560c576d1715b1be3abb1a0141b6d36d85b5dd3d0d7cc7be9ae3e0831ba00a8476276f5cfd393d1d0ddcf00c59c1578425af6cb60b846bc0707a94e53c7c55b419b71b61cc69c04308a5fb0c7eba79e65161d6e81dcc56f03cbea7b88ae3859071c7d66a6b9238ae29aa0c0620c2c90997d69f9b8915bf826bdc040fa2cc7a4cd283e06415287bbb0932c900d60e1916fe7784296009a1e78382264195abd07c2124b2240d438bc2e9401f097fccabed3918ad4c495f9a76e2599a36a53070cbc417710a6d64665e39cc040399b09b2ba8707289c7d6f7477994444dcd2af7ea577d25242fa1ac7227d15d074809af3d9f1250e5d53ecb2cab76a751d0ae755548bd6fdf45da38e6fe81140a913b4148bcc064a692e9f5421c4618578b8e92b7ac7061a1dfa69d2b9b99e101c73f2b9cd857cbd444a8f3b12f68cd40e428c87b8901b65d2c8188fd584f5e02ea53941c4e82be0835e8711ed6172bdb72609e150088d92597248726064fbb0417bf9ce58baeda3de17218e56c817eb82d33e4be76073257204fd0568b87820c3b2bd9459fb19d7f20e98726685a23059ec92ecd8be56c71bcaf9386a8aa450744a8ec3399dc4c608c4c49043fae81f2aca990dc34dbe54de687a2a5f2685107821304daa51dded9dea64416846f8cf3eba88173ebe51c7ea01736d28a962400068004bd98eefa3b58351238ea21b579f797b917483628adc90880f644db7d03c381467dd44cbacf8f1129e76b57974368cb95a123a2809bcbd9c9830d58790f false -check_ring_signature 6f4a353c98d17d690766152c7514af2d0c90b7b22a652c90187df0771968128c a631361abf7ebe5449529d94cf9a967793159259a85159867940befb5726d469 131 5a63a6cdaca831224a3605fee9d1ba737cd5382b53f99b6d685c99d59d9c7036 8ac8680db875fa26e3a9c0e56c3ab4a1b7d75444d5c5398a16e46c90029d71fb 54cb5918cb9fd7773cc2d87c0aed21713ae309de6a1a525d9130be747694d192 48ec122a3e15fc1dc97e38bd797c5a5a0666c9633774805ac508d8581304aa63 7caa26c8fad164a80ec7a14d22f275ac35e3defc4d2438cf03cd19580ba7e0f8 1c3b8d594626ec9261f9a121e9bb2753c9f33337b0af2df6e44af800c1b2b63a 520e7f3076804dea2bfbb3c236378f18178ec407fba9b8b583330ed62f313b3d d04df52d53b4220c2885514563381b3363fad15d65de935cf170892d51bea618 9a85ad8eabe7ea1fdb0ceac0383559d645086a5c197150bf82fe06ab3a2124fd 69c4fac644a9153b66ed1009f0607d88e8f83d47e3b963849c0b5681f29963d4 9c765ff37ddcff3a29e770987856fc10b5e5bbb37b69143c3882d3f383dc2ea3 3759fb46d89097ca92c19b017b6ec55dc58b966b056642736163a44f94532f4e a2151b7b55c200bc90e684f5cb5bc4623a7782449054c0eeff9b3fe608060dab ebdf6810aee38aa8ef1d64c12e481a046ce02d52170ae076e73344e81c74dbea 25b8d4ebbcc976f2d994c3dd44ab69d994c84810e450c1c96149d5d35014901f a7ecdbc7663aecd01a2212b25d2c0e5a0fb4717c830cc7143c24eddc24f5db51 a399280ec5a83dccca6255a5308cec781e99a69039ab1dffbe27a2920b0c1b00 06ac2c3a341a7bb9558c42e8e2f5d3210bef36a4f41fed5947ebb3407285b76d c376976edd098fcfd2de0c529d2d97d2d30a69e2a44ed98b31b7d23e6c45c1c3 04b1c371c20afa0e33e2f59eb90040980dcf4b966c8b44a234e7a159db7357fb 44cf13d993c7ebaaeeb4e51853c4e17d11935977458fdc4c5fe689ff55023c1a c45062d0cb44c031249042eecf0e69d93ac520124e046b1f14450d9512743af7 611531a984c9eb7b2ac037138281d766ec1137b52305e65689157e08558bbb07 697715cdc76c03de9dfc44dfb32bbcd41cdfdbb9425353bbf4b8ea8fd1a6d7d3 69c6d04ebede43bd7c0c759c4daceef5cdf9eca844f7f3997e41ca48c95ce393 addbefbb1830c9977e2b8afe590097bb5cab002b02ced6deb9ad474fa489e0cd 199755eb513fc04aa7b4a0ae5b78ae98f8848cbdee8d26938f1505dc507bae17 db27126c4c5a5c657d6a1cc87da1df63f5db4e1202175b5120a6a7b5c0d9767a 7e48fdd0c4c8ba4e857c7ff98eb0048544393eda6ddb8b779e46972582114a24 9dab90ee09f6be87fa7758d8e137b80491003b68f5023178851cdf4b51aabbb7 721254afc66aa65aa711c7bd0792fafacc3a5d15c54bdb3fbd07ef4a03cf0f6d f68fed9eb7ceb2c3f607dac93ceff2c05df4fa81acab368da22d4c2bed7cbe0d 5bfd80ee8cb9f3a939855c174110da0532689f8936dd52e1cc222b860af29937 18095160504e1940fb2b382f2f4972e50670cc2ba6d6ed730c7d678a87e4fb9e a406ad8970f7217cc8fdc42907f22cce0f82928d3a7fa588a207b70080a9417a 9af6a34745b0f9a2f88144ee6b083c73b7b6eeb6d507dca685f83ba9ac1c3a66 2e429d7eb6bf3d3d3bb6fcc0058775a9f34ee7229d57763581b08cb19b465fe7 66639884429e973d7586e9bd855ab6c0a21826d8f26363738e8adb7317e8e360 08e7fe1a4f190a16131044b2318b01d168c040c4d0214dc3fb64bf269533a2fc 75ae8010ff0a6eb1fb002b920f7b92080a578d044a5da3b7d73a626cfdc8bb77 9b11f45a9eeeff10d4fd93255784968b4d709a05660489b0801474f8f595797c c645ed6437125e3ad384890a11a64d75ebe629c4d0ad34aac14417affa050b56 1eba5600b3d0efe85628ff2d6a88e5961c48d3aacafc6bf5051172270572902b fe0c4067eddb22a49075c8724421b0231c1566b41ebdb67bce0f8dd5bf20902c 9296847a37b6dc52d7d3dcbe60da733537e3a337f5b9c846230c9260864478d5 54aabdd44f329c08ec325d05645f8d2beacbdb2743b914a27e87f16da3c434ed f3857c88dd7004f672782e78bbfbbfee2e5208a9ff6668f803564a71fb1c164d d23ad9948f0154640e1b137ddf90ef41533ae18aea4603d366f25a1c4343e100 a327fc978eeff82c23c810e536d75aef1fe3fe2bd9488036878f209e723557a9 dc9064bda2a1664fb6f1bec09158a56469f70327f8a8d63a9cfbe08d954b67bd 8bc2459cafcf75b41f08b44a6d9ba0ea68ee6a9ab8d69befead1e83402d5ef25 79b6a6c3566a8adcab67d0fe5040fe8a90a06543302c419696dcb6f59dbd6aa7 8a2667cb62f56bdfa9b7a4dfcf19207b870590493d984236a534a36f35f5e1c9 c93ec915437590f67d6259bf8fddc45bb5e40a5b0617f698ec342d91a974feb4 60c57e687f8e9ffd5563561a6b68b4de7ff83e90e82cb55d178e433aa97832c6 9501640141ed72a7523a3872e45e2bf097ca106eac29f5b2d7f913aa53446590 e85e4ee068f8f77f3e13a118dcd60a3a6b2461c036d2328d40e8fe406448b79f 4a3244fd9d62aaad96d7e070d4782ad1877bdd545bc6aeaf6a9dd22547e2f77d 003d5a3446361ab57233d6c96500c6cd17fae2ff7d1e47c926a25de8bf3d857f 6d3a430c5c84932ec2214147ca515893cbde9394a4f38229d356b463936e7612 6740a3a129ea7625ef1476d45f052e5d686c2072ad2b341c5b81e98c38f50b83 898ad045b8ca2afb8d637244f8b242438397d29325eef563ee976275e6d4beba 3f94b81f74f0b962d0732e4aa0a0a1c114325326c22f3926f5590b7de250bb9f a80d6f13d050fdc79467f011e8591a16cf8a386dd5286ce361cdb2bfe40804d1 bb8b413e894fc2a8cf818d1ede1225cdd130ecb470d9dbef8a42dced33644653 f29c84a613959640859a12dd967e1b1878ab09efea2ba8f8894ace206bdcc28b 2cdbcdd2bfcee8300b484b4c982d88a94ecdb47fe7ed3fe44d408c2f80ed4aa1 9ea4afe750d0df5a71a9e761bf16c7ff9d4c10ad0c29ba4fbe32e79a8bff2a55 22a6d452d15786c34e6d9d0618984836a932113e5046ed3c115cd1e140ba7ec3 a0c40a66488641605dce9fda22d251b70faf01d3a31a576f192a136b2446e7a0 a099747d6e82117ce533670f428a4dc424a5f3a2579755485f7448c315efb332 718f0d5ee32e74721e29eda855e7948939b389f42740dc3073b75e3fc9551233 f146879cc102ea4167ec679c9bc11f0a859bf5d0dabfffa408af83276726f4bf 460dee2a2d29d0e7f043007baf9063c5a22424a515c6074ed34eae2b0c6cc85e 90c26ee26d0596796a0f27d17a4884cc3d5878a3a8371f6838fddb4328040c36 6732fe38c3f5e8aba916987987c7f1fa4c3595ad61a122d5e75c7e254fd76d00 d65208d52b727edf133795c11c6181368a0cd5610f4db541881991c39a752526 ef6f212d8c263bd216b56612a4262611ee80e6c0ffa9efc4617d1b9aab5c944a 6b5db3abe7eefe24b1ec24982d1ad73eb769ec5b47dbab92e25793fd4969dddc ec417b9936d2a9eba7b971a9431d05128fdf0f064e6407167b15f2d3af0a531d 5563856c4c0b315ecc2e7e830319dd87d1901253cc3a02722a4a4e9f520971e3 1248ec4e18b826ab9c12532c04ab5ee139fb5cd1136cd0a7c88275f516352eb1 7b239f405208508958e9ee5465dd6edfa2cd1b2e236a05ba4b65a29a21b22f7e 63d8327a3dfa76c2956bc1828f28f88860758c5f7ac6a8f9773ccb95689f0074 19fafaa4738a3633b1e8af879464565bd72a59095a0196c5ae5f31b7e2b18b96 3f9b6300d5c6a8a2ed369a7ae9440e808ee85a3cfe991bde19202674ae0629a9 73ee8296922b7965e840296e96e90a2f31d84bb127f901de33b6ffd56d862e3c 49a081f9972eb60d2893f26e4c34f14aa4a78fc3bc658e41ea58675d97c30c60 acde4de0ebd65556f5de1d9ba134fc8662271503013f8174802dbdd468f275d9 1782cba9472421f1e9c82701a3b69acf1c98055126a72720c9d6890730dc342b 786cd1ab0717a4e62ea37d89a77da6bf2dc2267af497d5684eb829382a84ac4c 2dffa82ac4fc69e1bd92c750913603e00253c7ee108f6328e2ba442a2004e900 3aef9457a125babffdbcdf080325e9d1c85979cb7c5e716c654af5796a2b223b d40ef7abc0d5d76f4faad16a5096e20c435df9e9b1444351399d0d0dcd457b42 87017646760e5907215c6455f0fb8a257d8d82da4768b72165ad7eb6e83317fb 8b35565c4f50c6ce6600dee182cf259dbad787a8edc7216e7d1d3207d4466093 df955fad319e1ba515ea90635b6b068c603317bc73b42d104045f4f4972f979d d8c5232e2819058edc184a4d18cb44bafa8e6063ab367e5fa30f468579835008 69346bf5f225d5cf53a5e8f4e6ab3f99a7b167c3753ffbf438df5c5b54c4db7a dee18867f0c433f06158d12558760bf9f7b595fdbe4f695bb1be43b1d0540654 7a25907f55bf2e608b7f4ec0fa3ba641b0c854a0c2e8a481abf4072a6a1b5b91 f07514551ce7d7b0c1d5a2dc7de6a3ce4f7330a2b8e3591dd0e5b749ab6c4f74 2262a553b4ca0c06c6ff220de005eac7680a12521b57f5b2aacaee21c26507f7 4bd9f0e6f46433ecb3e5561abb4c8337d16eeccf7e7166823045cd0191b3b2f4 0e1cc89dd8a9b0c07f34b08b0e242ea4e63b7e10f08ca36be83da18ae89fd8b0 007a52e915e1e072ae84425c80b5b3d6d7a56425da630638e40a25498bff7feb 204e1d3711eb894dbe3b9452061535d8e24ed117b206e75fea92de7cce49247c ae55c56b06bf0ead2e46b8d1dbb5e91d0d43c9d93deb1a73e9f2c3dfd4784f6a ca60ff26e62c234cdc10918e0e1a55d9373d630b76cab480fb42bd537c0db5ca b1713604c3ba372d49d452dd4bd69e97f73abf00b2faeb9e82f454261fc16228 5c5777cd1ede5d8910dc89877f214ce51b402f4fde2d72c2715ba54d90e7e3c3 2fd374a77f3747deca7e61471b32515d84c7f13a128e8d4bf0fec74553f11877 7eaaed0f15b99e68b2e1943e282040a379bcee3f09714f3ebf9e37e394fd0603 8197388103b9c19612f3c82af0decc23576ab5fe54d789aeb3e81fe6232457b1 14670f134035fc8158ed7a9b73c740567de1067cd916869a60f57a66f0b0149f 0a1b1673c124d26d32f5699cd6006e029bec9f368654e849bb8b6a3194f6eb8b 0ebd1d8bf4aebad2bb2913f55cc849d5e3707204bb00290b19864213155ec268 58a1f1d087df3dad2422ca51e5fb5a568aaa3f62cc6abaf6ebaa84b8b9d6e16e 8bf5e7d7ec49e3e045ca39b040a40077051a51ec21829a831e50ff888c9041c5 03c62c01f13193993a87eb543733531e0be7a0253f3bcd664fa55856044ccca5 914f45bbed58a21e7c16421e2a58fa3ae31e9457254199b1260cdecf95bf7336 dc2409ef8d08f7da7790bd6bc80647fa4ee2dd3a7a12379617ec35d0e24bc649 963f10237c2e37d44c7b467496834e037b380e543c0f9b8ab281493dff79c745 af3e5295ad96ee6b1186cfbaf2d3c848893d382029d400495ed14da5ffa468ee 1d3b3bf7f07e65925f22bfebe2b71eb513d6c9e7121ceec5c37556fe1db842fe f2e67c302ce856f891e21631ccdc85581bf504a04752e620613b04dc7f3e4078 3f34f04af6b319e18224bd42ea93a12408895f1211254fbc70e9771804592c45 ce3e75c8a94622ee3178e7a1a7457f1ba5dd1210afad079369aa0567841e055a 36411c22b7f473dd9a8cd8712d7f61187ebacd3cfbf6294569739cc4e6fa57bd 227718c673ab52975d23cef5a11dee0008444e66abbeea70e35891ae9177633d 180ec8bc9cf8b201e545493fae127319436538b2b95a98dc7f321a095667f512 b1c52e73b1fdd263e045392528b4ea155d88c006a118980801d4648b6b398a069c10c06c7b470a3a711d159fc766bb7c1f22fb76861e7c76f20962916c069702223a556e75e9191834c9b7d389b079b3221d90d196bb808ce5b1d96a57e5c606166d6ea8c6f85e0e18fd468e8f0918658309c95591f1deb25936aabe2c0eca0610a9b09f4179b1ab6f067c4702fc5c586ee5bbc3d6a64a1833bb92775947670a8f032ca50fc24f808e40d8b699f8dcdb3d42298cf7dbe8502f08e02d8a0d52090f78493d263344cd6dd29ca94789a082d45659ed48d3f29d3356345d17fb460b483e861c5f0461c6e94d19de17dae8d501318144af1814cbe204b60db5441c0969b6d3be3f0150d8067134400f0a02523020969423b87b873edfe460a8e54f06d3a98f6ef5a3d7eca0af7c44d26faebd8286d41e602311697230d6fd2b2598044a9bd1b09311164e56b5273594274727414e8c92a497ff731d1f2f78a6cd7c0f76dc3c0b79396024d072e54d86df999e4cc0bdf7cee3325f2790369a1d203701df6a9a6c0405f1f5bfe5a9af46fb7ecb1d81955a6d5fa16d3dab44ff4da71e0258ff46d8f8a54cc48a55ec7ffb93773645f29204d7fb5b5a5af34ce0c7fe5f02aa008ac64e2d1c1e2d1eeda79fbda6c3b99bbdd0e7b2532f02e3863026a73804716a69846909c56d3f386c757572bca3011e19ba302460d5a46ff3d9f6dc6401da1719b586358aab434a78500fb861a8a51410363af20a7878eae2dd1c1c260d47253e5cbd5e38df5b617276f1da3d4f6db34396cb19ec6558060cfe5f71f40aa675b616299cc0531c894c5ea27ac711399d427200b40ea7d84730a99cb88d0e2c8c0f7c7f250ee5636b737456cd054cd84e2a77d101db40dce8aed2eb1bfc0ac170b1223b26d6814f0525b2be72b9f1ca81a3c137738979dc9f3a2129d568040a1bf4d3554d3ade1d8677de6f865f2dfe4914559c5ce6df4b2068e49a49360e8f769a2e930c28b57b565e6913f71245dabdd2e1131f038f9261d7e6013d900c2aee55f678d3f814a7c1333c8b076ddbbb373662d9c91b5c5d35cd0d78e81104673fea35bfb854c3e1d90fdc595c2f5ba3b9b145c6d9ed7e8d1ef8d5a815850dcf5a7161dad1de98cf789dbcd21ee86ee41c896d12abb2d807a7f46e2a24a400f6e1d1e28671e12296424f0463b456f19b672f7d92b3a013dbe88de6606cbf06d9034fe0a41822ec1893ae2ce14498fb081f5d91c1cc050cf13d76b7397db506f461aa167ead65b29a74f65231c804a4b5a7dab3613ea95a1f4cf5ea5ba8bc0ec956fc908ae31d75b70b6f0b3b2ea560d902db73f9716c35a7c9caee5af4980285da68046e9c509c315bbd852946803d687c711a5c0a5a74a3eb4c751b5b9f0a4d18ec4f438a5be3db599df89cf660c892189ae4380a15e0ce6ee5a439671f0061e36057d6cea002e9e198f4151a8ece7e482a2b9cada200748a77d9d4b7e205e8c72368554683c4dbe0b1bc2c14ac7efffaecd17b436f0f5f43ba9811d2b90bf7baa95c83d2e6095c73c40962e045fdeb7acf26c09437ba688e4a0977c1c208011b5372da619d17864b89c38d3394ff0abecaa9469cc12e277131d2fb427c01dd34dac4c59692ef25c82ad39eed3de910481365e49d1b3a420a97fc64d42f09f3e67187b6772ef1a63c5ade1f79edc07a317d6c4a9d4b4bd1532b141fe16d0380798d30754511154c99db75593fe7818154a9d5e74ceebd03a86b5632b56b073f0ff1f7242349bc95d36c2ae4651b99fb781f230b76029d98f23e07d285bb01dca90bae0c92a83e186b8c8237fdf7305e4da3e3b450e0306672bd566c03640a89a8bb801fd68469edc957cf81944dbfa832e2c158a37119abc3dd0a840159081b15424dc2d0b064a238eb135f8e762a187c06a23ddd7fbefce16dc09c41350dee4ceedb39ae798294c13e6a489664cda36ddfbdd6f6d43cc31bdad54b9f490fe282ba99d077e48aeddeb83d5296f4636e7126875b85eece8565fd613b4323055900c3bb251cbd32236f6eef5de6a34e5c75753f1384ecd8583418542f6c0e0123d5b856936b95aadd2b9251ab441a417347a71f12bf9a35bf62c36374de90068495d4f83a2ce1eea5fff9e963ba1a5a498ee68fdbf28400f8e3521fa19fc00f5c48dde340482d84cf836ab05d7aa8580eb68c8dc625d7f8fac02a3502eaad01b9a4ee118021ed7f6165bc1a4f656b81ca8003d4a11306f3a44a983be137d407a33323f0aa6d19f4db624cf9011b767d60e6448cd30997c17b2bdccc5f98d50475e593caf3a1d8ea7d4ae4ee6834437d825228f00afecaa3763ac94ce9f41b0310f929175c72dd058ab88597827c75c4774bad1db1b5a9bc75846e74ea4a830acfb466825435140e950a415a3498d7aed913d6fcde2582d5dddfe72c174ac10bb36af188bdfa8b377636b26f682eb064efe6e821802e2bf8cefa4a3a51aeae0791d77231092c5585713550be62581592244c1183c9202bf9021f0789f8e49208ccc0b729a1eec31bcc16b073988b7c5a583880133fab6df62b69c6a9d7abba04ddc3e7ddc4677440f43cf862768376f98d37ddd2bad6046fcebbc68bb9d129022d82c590496f49b4b719bbdbb3d30e0823b423979060356b9b07d821d760d40bd441ca409329769b6646463ea719cc18e669d89632076c773f0f7b253906b206122042644ded38989a98850ce1f2addaa2c005a19a73e92a1e2a40d0ade4e10a3faab945c981a2614473163a9fec622b9ee906ffb1f0375a1e27ac7280857d0563da4a8fde391f82d8e69f753a9542ceb9a6b0462934c9de908f344df373100f949924395f430bee98ac9803e2d4493905cadabc91618c9caa7d25966c6a22006d59c31d8df6caf35df8104a6f4fd3fa1ff8f0b20367d9427e2b49fd5e6da20a39bffa0c9a2de9b9aab70ea9ec68bbb575f4e39c7afc3936dc88b1dc03ce4606d7223a0e2afdd1f147738863cd9867f85111a7e315744dcb697fb18fb2090c05aeb92d1f9cd7d83eda45c40cb5247bebd9a78a15b599b1653bcd357aa3e1b70f3af6a5402a9acc3516e69321c7a3c1b045a6aa240fc456a1ebd0af3da4d1a7070e39ac80c1b1a7ca4cddecb9490df9bc0348d76dcc8148d076584183cf84660aaeceaf765c482c763e141912045657346456aec81bb45ed6ee1ccb9dbbb00404bd94349bc321291e9164df6bb1b147986b94d0dbbfb823c89c8e51273be7810a03ccb84275efcdfd784741f17f98cc2b80a118099acf56a96af535708c52da0a0aa786443cb2066e3f22170edf4de5fb82fc02d2f002911c7b582454b135f0008179167fac05fea97d49738ff0fd915ea5b53c24461c22fd9593df471c39dd0b9dec13bbbc3578a57554919899bfcb43bc215559bbc86e8e737d33c407706e0d00ba8d47320aa4fa194751f1cc01e4920e8a3af1c96e87f4b166b58d40b3360a4714612ae42a9985909488bf723238343f65b77ec268fefb7b02e41a3921eb0b120b5fe80b0e322bb97066721fb968b27ab40dcb93f8563f197837f66a9f1903281712b6b91d89b7eb1794d14a05e23d56fc5c8e5e323b6a29671d5ccd685601c45628a05649402dd8c9d5cde6727ddf35e6ceeb20edf46178c6911d276827030330fb6ac1a9344cfdfd475411943a45619b85983c18ae362ea3c9362e4d7807b55ba93d96c27fc4d99d15e36f3245dd897789cedfb96bdf93d24371a9db930e1d6be864f498e8fd1171100a5b92ac6995a45f4c29662d74fe6eeb1861bf2700ab44dea727b4820cc473c61d64f09276f57ffe1c82fe3a35cd04346f32af870195e95d8c1198fb337fbd521406eb90b57226962e89ee41fd53d2dc51ffd9880a343c7dfcb61f9b96d2fe959ae400c5c2a460db8fe10f7fd4d3c9d1d2b21f8f0e948de0838aa24711238e35bcb1e894aa570aa8ffa4f2de96bd0989440c54220d77e3284d3d58326c6d5b5c94dcf6ae32ea9329b04c2ac430de75a1ca91fc800fe6fd11d3fd845c3929300e0a4148ef6d419ca9b9e4acb8ceb0bbe42d74311b08c707dba3cc1207e430287c0b2ddf28460b11a4a3ec10ad41ac55bc4214f87905494db8de4c578f32c5e2ed499dbb92788072dcc8b033d8be7f99abdc2aad2d0e57aa7eb415dbdad7b28e15714344765095832a61dcfd2c324908d9725a4f9e06f2a3810c7199371f6b60f5fa44dcc88eaf603a634ccc1c5ff116cd6a23989a0ffea846e17e90047ad5da0c3bf477d358e81303b049465827facc0ccbdd1ae60ea8cb4b94caa844a4135a721d360291ede527e68fe56e89fed196d32be74c08024b32d3821056fb01052d0726bb5df9b0df7cf190197adf4c1a730ebc71ee150da7c3bc7d94d4e17f98f857864b44a95d7262fd924cdf90c46525cf22e864220b33eeead9eff532efc245954347d8f0e6eb468039666933de4d4d12bb7ea1e90f7a76a0980522f771d2c4fbf5989693e26c8c59137af7dd38f933df2add1b820df9783c4e52dbb559ab753cc89318a3a480e248efbb9f9827d512f2626c1a7f065378cb4bdd3a9123b1c56a8b59b9acf6cb8af043b28cc5cdd9343c3734e0350068681f60ef869ea716053040eaaa0c80de0eccff520d37a8fb4d37c8c7a9840d3e57f47f6f730c174f55d39dbf8933d23ebee7733b99b271254953be00859a02de4090d6720943454bd4e08a9832f7fca9f25d6cf1d1419d890e5afc2dbc1701e4aff2b25ae7bb3e09f1ae5501bd05f3c2e9e5750140295c78724294d90ddd0f3ecb0e97a7a4b49cb542df734e3aa7ef97c71a69450ff5d2e2fb604518da490f5800a3fcf40792a2b33ee693bc8f0a7c5ac729302c4821604998445f7d82a4037e9d9b1d883045d763f805b665b38ee75f10308dd3c48bcb2b66094898b3960e4fbfc838ecfcf3e1f8840f630040a7b8c28e3ddf1e4e99acc6beece7e7e16e0bb9ae642a3537d1e0c68f1346272d5004c11b8e8ce63f1da44c0e4aa1dc51620131a1909eb78bb9bd9f10e1f32fb5fa08a326629419cc3218d3ddc7062b66ce06e34462b7f8d84f4b247b30a14cd26f7d659335216d0aadb366d35d9026b7b501cd343bdd41546097b0ad43c017e86ee3b208add97da068b9c01b0a8b70ed1702d9a90b8466c54a50a028e4a69d0972b86d1aac740956e6b920c54cec5337000ffc1b2b4a1622a15e0f932f970b1a05369879f069d32ddc5ce5c1fbfbfa316e052c374f6138b8e6c5c373c7d37007719330bace031c477f8fb619b3fb77b4e60bd01dc949bdc1d30735aca28994afc4b5f24ecd7dee146b43aa538b048f42a1098693095ee25289ad787e8ba3d5906933c60f1e227b4a91b23b038ed58bec0d0fc5c0f13c35258aa6ce3dc238111845f97016c5c28e05e68ede68521ab71d430459786b38ae1a7f57f1b29cdd041c49ed0302ec014c90db63edca5ea641f8ac096458f5694dabe26bc1820a0ea70bcfac5f3c41f835dcf238b01b2751f6344209131cbb62f27e55f380d9c41d9308108be6e3f1fc65cb362262ba6796f4debd0b1c69d2fd908653e0774c9b97b7f36e5637070947359eeb080432e44766c80e03faf07986967963a691a98174e83291bd708e30b2a71f2000a17ce8968a2b5d0eda5450deac6d53a3c5fb8c18a08f367722b03e938378094299a3110547d6ac0ee96996a8f85a9fdff0b300ea5cb76a4f8392b0ddfc9a2e1908cf0343db816106dcb80e0b3fe060874adc35039e45f9364f264e76b643c6a02187fa278710f305d96b12a7590378d24b60dd92a4bc8af3e1d7a0209f45577c7ba5564da114d303d0c4fd6eecdd17d3176f2871759325e0af5b474a657680e1eaf9cbddf75e3c080efedde29acb4663c40be6d8f8abb5acd6557cfec952b2bcad3493ee976d6a07db52b0058989e124e89cc9ae1042afba36306def0ea620334f357cee3ed494062bef30cdeae2c3fc2bfed7dae4356a4f24cc1f1cd6c019ef64a4d78b04e6510d6f96d75e4e8d593318d984bdae59429f54a6a4d59f0e5c219e0fd442cae9d5093973c1fcd0c3e5b5ed371abe95e5999fbc7ad195fb5ca3f91d1d9293eac2fb08413ae4d403ed734d846c34940d6db2545c9d558088da7a4b4d4e95c5200f480751926cf20af49f77695739eedb0db1cfb52715acf2f5c56580dcc7e5f45d5b04e024382dd0574319c078706b231331c159825a95d795361752e20d728d4af20b89f184feadd3a6aff534988f99d8bd0801da7e6fef86234a478a49592696b40ebc4b583a910e88c0ebc00dee4ec5a69db7ee1954fb6a3235e3b2ccf0ef612400e2015eea04963d6db85d9383969c38851c150e25d079301c5fbe28e2cd70b90c96e96616d47d8f7e287733902460b51f430c8a1efc6dd91dae0a4bb170e16905c6bcd3a2008b0de1ccae13320605b623dc9a148cdd720e7ff0b15d020c171c0e5cbaed5bfc6075a70e51ac5695a657ac6f8677d5e89077163d44ac83295a2403d7cfa08939c29b02a21ab887647af5440bfd183a96bb8b01d81d536af866b70fd7228fd4870dd2e8041dac30c57a16589d4ee809ca577f57539cdee35853dc02d0a952205f0b3a406b0c201e88703aa8a25877510f86263ec28a06547354dc0f671cd5810ff5d28045174c8ed1e2d5175dc2fd8c502a1e803e75c4812f145c0b13ddec4fe24243671d6ca3edb000204c46c5d61670daf24c064cb743dd8c4c05c77a9f651a8d628a9648435168df0c4a3dff059b22b2e758b87f231b4194f209b2778e1c778389c91f2b5295f83facf5adfa8bd6de921a082b3a11c65a5d920fd28aa17abd617b61222348ef5f7ac78e52224c9d52f0620c4177a56e7350cf0c62511acd608dbda61a59ee2b08e733ca70ed4b4d759370c26b519ba11ae323079f9f33a1e9c716d9a6bdffa7e272de70173b92a9412848e9d3f40c1ce017a20b666194fa80f9f0af80a598af519064271b6dcd1ec4a415d3fa414bdc98490a03e0c9d097b81b93905c9a1110d84a9072ea121aa612be73d5659bc69ac100220cf7e763b1bbdbf285651c4f1535d8382006e0a08e231c42f94164fc4dcf83b00f14c4fc94180ce707798608309db8b816d20c209b6c4d037a587135fe2224680d53f551885bf2324e295c7fa4bb74e21ef8bffa012fa9348c2036d9326e11d508f9b20ff1c7ddf9092013ab802b50aedaa88b999b2f5a3669e7062395e681150bce19a63120409907e7b8f0520f7c1e39277784e911397bff8be55d2f992ee4033f4d66c61646c973cf454f8761f07b83801e0d81f4b0cb7e91da6628497da20af006c82138d794b2449c624369300232664bfde27510659d39f8246a56458e0084d5e1ccdb0223955ca6228f22a20abb26e09080e520406d2f74d6d899c89506e47f2bca3b9c7d663077b13fbe5a9ff494f66e6061d09fb3bb5e9dcea1682903c4b9845c2f961e205c611eb502a2e70ae66c90d811fb0f9cd588db8b27773a09a14c8b412cd3a21d17c038f2790d7f8bf2c06c1f7a4e268939bec1f68db6a403c8972b73c24c2e0dae839b439d49c5e6abd7d67c716772f81d33222507d6090120066e5c70c108f07c024517b81a98a15289c978b298dabd43fc5536b0b16a048a0086e03a3ee0c0c30ad6931d7e9755a9c63df5df63dc55e19b2f69127b22064d5d5ebf12bd64466496fffac4266457f5e0f4ca4fa65a969c0dfef44ab1ca0f7f625184aa5b83dc3c17f7540ab0bbfa64c18ee964d213955678d40e6c6bf003d3dcda531a9910f89486ee23d00e8bd72aee9ef0ea458e8b982b42c391564e09b39c1054406698219cdee96a7020325360e9714474972a5e253d6026e131690d6b50edbf3c1d6f940cc40ece87421381ce280a42efdf83f3d56912f241dbc00a479d6b7428a03549e2f2f4bea383a4cc8e1b193b6a5b9229063d4e071a817e060da4aaef6270ece5cef2dbf2728c27db262970141f39c9a1def061861345fc0c98d8cb4e835cb6f6803327917c1389706b44b8549f2b7ab894e3fd6b98fbab0a65748d776633b10f5bbe3068e0110cc2bd13828675d9be872efe580cfc41670688faeba31255324f572f31a939b62af1c5fb70ca566ce7b205b9778998662a06b595d005d8510ea199d96ccf9a6001d6406f1d58cdb84076926039c40421f40a16197905bfa7b62e96340e3c2a4a905339977e0b45772ff09cb672e01066d707dc8cf5b56691679fd42c434c1486202a776aab8649b88b7b831ecefcc63d4c00c4ce733e495a208641ece0bb3a5bb8e068c97cbd3294024e94de320122ee7b032a05234bf13dc42e539a6971658e24b7e366411a56bd7073868b807fe6034309584ad8a73ee76f0b57e0d002789db9897350d79699a36322789219f110efa308ecb2da073da55652351b59e8425e79c125078c8721b43311623d73fea855b10f33f819d94df34cdeea56e0acaab9f3938fc0f58c6610c9d8190f7518a2b2d40cc651494dd192cee88a43ec6db9093ccbe0b2edb67e9eedc8c7dda398ba7ec308870aef9d7206705a8b2a3a1740230eed0c59b711df3147594a878f6a9ab3cc0960f3c05ee6e0e3175998c181402ed59a5fd7dd1fc1acfad736c6889966d8140053ee199e08dbc4f8b49282e5eb4d509e5e82cbda5f3dc726d4851feec814c30707f0cde4d83dd5df04d323090c8692cafcdf50bd49afae475d0fb19863fa0c0076768eb3d6e0ed4f23c0a8b0158eb43452d273be3366fd985e77a140cd646f0de8986a3814209a9b3b40e91bdb1622550cde4846e73735fc9e4813560feacc062c557e82885fe52f30c2e1d56282176312495120137696d94f63c56345a9c107d77704e732ba85e05e8f5d95ce268cb3910dc03d38d5c40ec578d9c0a70e29015977a39129a2b05b412262f2f100f87ad01b38582666ec13077b5ce9a8b2b107c0a20be9fb7a636734458ad4ac29f76cf1db492215acedf79463ac3e37652404dd1aa63517bc19d7de045b9c8f203a07f35f9ac600ce9ba5971f34fdbfe6cf0901b8390aec37cba77e2b37097fc95cdabf5fa803e632a087b7b69afbc44a510970c6a99004b83235dc20f32a8ad4c6e4ba0ffb2dd4412540b59ab2fcb7f0e00c7e2717b60fc62a724734ddb9f8fb7ff55e4887da4061e825663fff3c54bf3203a8cc5d575a07725b586f6cc2665df6d106335129e711ad49fef515a52bdddf0aae9178527ea8657f2756410f79a1f97cb8e27fcf7ddfff650439baa585421905a841bdca4d8d81f450c1f4ac36ed9ce98c6d4b8b7ca516676a657c3f90a89f0fbe51d91e68cc9b9981ad155175b58516222adf55b3c262b9464f3b9c7ed6c00497359d494764b3eb8e18e400f94c1ed4c9c042d27f753182e832d68d388fbd01d8e9b5039e2fbd143306d7fe60d1ca0b4d5bf1c6cab2e0ec99fd6ce78acbcb003e11ea3d91d901381d3f76610ad40757284d2394f626331fef2129f58cdb880df1f16e80250852538889568a39287adde63aac8ae24bb82e8ade9ab9860ab4006abc535bfa8244e9f377c451aa43ca47185d4687c3121035bc0bf05ec1a03f0a6674d42c5f1730e826a1312e224579799edf17e4fb535749a22d2cfa205a9e097746a1b30a00c449664ef098ab92c89e2c77ab6e3d8d863bdf4141479b62ed0bec8f38829c42c8ed7c8e47ede0075a14d7297ae34bdf02e228c74ec00415400479d6fd25a947606c119fae3e0dabfbe67a4c021edc295b5a120efe65b0eae801042c6c472c3c1f3fc7717bbc3394f2ea62534d2669610f4cc67b8c11d2483d0c8b4f971d953b1ef6d252041467bc8fdd49a14cf703ca53d5b2269eec12f3b903342023b2355c82853b98d9bf98a43d85b9b9cc69e18462d8916d0f7251e6ff0779ac1f9e1d377c8d5d8aed0357d4e3d8a12eb894e5693fb5169154c3cd9a330a57cc63d84287024132cd4823c632f562b4563ea29c9ff5c09c74becdc389370ddece03cd6ae2f20ad4570890304c1c2ca4a2f195fa09adcc998405456e02fe083a774698eaeeeddac4e882bd56d03b87308076b5bbcf7c3bdb008b8578a97a0725294262f11f95f2e5d1ce9e2f559b0564685e31830e3b7132dadd6abbec4107f43d70ab35eae4e942bf7c1223a0a99fa4755c628a5305f1360252244a9c0a02971e9c1d8473cfaf0ed20579f2077d3560c901f31b2593014b7ff427f40f0c02c6037244c4604a2de4fc5b5cc7e9b5c647013d79c9179849a05b74ba60a3a605438c3ccb50a430d9e054478db1f054c9eb9570be66007b7e0313567726561104d82b47fee33d058dc3e6487b63f3db8394bec7c8264ccd13170ee283104e3000593bc97eb30a510333ba33cfe2df8a3f268c60586d14f5001577543440426e032bb2c4970ab05471fedb83ca871b5224c116d89c4dd232e47dd0b50944ae4b00008bdb534fd34daf9c113c2223f0545cd1749b4e05b594fa256680d1e3b87706a9348083a50e22ca6bc2700f0b12474e50889f5fbacaea0938b7550b3a34e209ad94c2920b6e22475828e77ea541e20cd3e6853db7ea1b4607a1cff623ab850015bafe2d61529aba38de6d2ab2ad4d503be7d0fac813ff39d35b9eed81a4330280e6e69c301c4d01412925a8f6edca4726830b62a520b396354d967661ff550c1e6ae39b65cd1554487aef9fce44ebe9d91e1a00258716797065fb79ecef340aec4516c6cb1285463083fe3b5fadeea78608bc9246223d7ec6b2d0308b24490bbf6957162d004314a8cf44c0c5eb9b5b4db2b496c9f8f7885e40f704fa173c04d02533642406175a6b6145bee3a75a53f0be2315d712bb609733a183e505a30efc6a78b5493bc900b19c73bcf2178651ef7b503da8d5795ecc91fc4bc8043404264ccbc3e9376ff629fdfe0a99b82b33ebcb32928884d0ec1526704d9938d1087928b62d0ea4aa4f477f0060ccbc2f9dd527a9cede2445da24f500d1e9f25f0e9d690f19badc6315948c23a8479fa93fbb8aa77df129b7e7609b2114dc4ed20d339f83513fb01da44f8053f9e976296949ff6c0df12c9f963c95a752b086850c2b515444e3335e979c9a4e1ff1a897b9260f6ea4d20462c71a5e793a1c3d770f0f9239a88cbc4e536be401311b5413f5ff9bbf2eec5b18ce8e1cab61d0a333065701b5c8eae7c19e9a13ab55e7c83d1bc0a59bf2e99a61789c133521b291b307106dd743a631fbd9135c5218901b4d503097cc3a67415cb5313d8809b740330dd2b0c19e51e2718b5dda7a39aa9ec920c2bb3f9f4b7b1ed4324dd0fd0e9448064cc6e7b95424069989b796dc7a032420baab531e974ef1e36195aa8fc8506e0afe1c9e57d347bea42d4106bf6141a564a4d1e740d0d49ffa9a6f8e50af6ae60686d86fb545cdede5ed6e1a4906f38466bf1b9d568ab93b9e50cf47fb81e6d9000106791b705ba556cd68250b2e6afef19679ffd23d64372805722b64efda020eb59c06dceee3a8a979b0036079f84de3b19a3059a507c7f6839578e207a60b0736e690882527e7c68fa6bda9f554d548265dd79f7680650f9991bc0f4d49fb0945722702e44de04e9872363b7f7125f4b691a24f61dd92212786dd788a202209fe9c47d1b7c38b29eed93c4871e8b898b087b929603993972905486c30f6bb0f39836198ff01b9f52e9ef08f4ced21f21d496ca41c8b0a082f78cda920b2790aa9d7fa642adc788b3c330fda6219f3f23286fdb13c2bfe5562e22697acd5690470082a42fc408d96745d2b73a8a47fff8c1afc16a2659c45f3b430f7beba7d060929d57e91f6a5027a0f052825ab8c542386a4bca40165dc4d6f8baf01c87107 false -check_ring_signature 7c8d05a20b98687ac1eccd2ecfb772b100e8525cca6d0afbe4bfd55fbc5e68e4 4b7d6979c18a42f6ee0a614ed0c2873f6f1ade2f17256129f5267b6ee1731358 2 6353b88da00ece1ed59a80de1ee9e4fa0d661310fe40f4f3adf174d482452e93 9d1f66673abe038ace2f95a2246e51a2b5933790997ca8c14f3d0cfe8bd8009a e5d2f1308536178b2ea160664ff65176ec18c01e7b739be8cdbfabf5382acbe42e9a91c9ece6b9b43ccc036ecc4b351854eb3f197c95482abd045e600d72c432f44235558157f3f36c405b4becc08b5db3be842160e2b6803f6936ab0626b63963363ef0fcdf22f700edfb0364c862e6d5db1a6cb01cb317f0600ab891fcec09 false -check_ring_signature c13f95a3027c107e1ac7796cd5b824a45a43ccee64e58beae39e51268f359b2f f0ab4b582132aa491d6af701e4a1a1708d7f033b4686df97ae755174bfa64ef0 11 88bf3d90e14c27f8607d1bfe9da5f0c38e5fa860dd24ad8b6781d504bd7b4c13 f1a7e02a898133726b05171c54caf654df8f3a811822d5cb0353ce19675fdf4e a5f82e339d375fa345c0b3f75b3f5644d8cc5c24d346c6c63b29f5fde6979ea8 28f1343655923184026161a86af8a4c4287fcb2309586718561c765c55d4ea38 2a7aa026a43f6945b9a63f15d76c701653fbff4f7a4e4ae3a1e2440a70f0dcae 49dbfdfedc52f9a6f4d94f6d948a42e49d08a32e83e5192d7676c671658e3621 e9969352dd8d28a4771d4ea167079eb00671c03a166132578e02e263bd372da3 1dff250931d97017864939c082c9d3d96b2953d7986ed5e14700d13bfc6971fc 66eba2c01a761c933b8b2fa0f86cf290e1aeab212b3d5a86363cfe2d5d564e6f 7dd7850374ba81d97daaa651e62d0b4c9b979578b89d0102eb93f69ce8f6442c 39cb46a53ec61bd5e733713be26f54543a1c86b372f3aa78765b171909aa81fd 32fd960ed7c704485cfdbf4bfaf9e0d8c66ff9eab92f0cece6ca414abf164d0c2ddcd357f7c2cdcaa4e3908f224f7188823951a79b639d4ec52e0c4ae105cc05d00efa84e1e40a5f9acd94855dd2a6ceafd189f27c334b88b9b22127b7a3ef07f5efe8a3f3bbd894af90514909d45d05de418915ec17116d07a64e116a7aa3047f0d830b6279162558eb2bee7a94398041c631aa39649c22458837966f8918f493e4e356e1a4483637c4124ce764b625268bb9c832af336fa8b605436b775805bacac750779725ca79bf377796c4a78037da99e2dc48125f717d09c9b4d7dc0cc9e586883e4b3ebd153b782e4f065347adedc69e5bd7235cdfd65ca1927f65003ce6533befce0de583760848b30e47755ed024a302b7278a5d556d59605fe0017057b769222250ed70650f90001f36c9b12dcea80831de6568c38fe83df1f407808cb43fcef0be649d7f6cd259c11c63defc91e2b9d65517e8df3d1e60484b075d4cfa539d09b3656f1e63238b0c62a78b61022d368682ecb538e281a19bef0df9387f92aa2516a246a6eb190fd2277a404d1f80a0542a07b576b24c587442007377fc56cfe2165716817188731a4ad4f21b7779f101005f69385caabe9d9b03be83463f9e28ab2abfd5b86b4363784c1c8768cea13da920389df34b4d05cdbb1d34654d0ebf30fd504a793e0ef0243c708dcfcce1437e13f4b3d97aeedb71075e3696144959e41f7808565a14020a4fb314edbaebb56484b37f65dbf458730927714d3056773aaf6e6fe8de0d346aa053f5514776220490203d575412f7c008a1be0cbaac10f6abd21cc5155ceb2db781d89233e146e82b4ab024cee5d3bd01414d02b1b3a327458227b3e1935b4662b9467cb983c8fd13b603bffa977efa3c6fed07e5719e5053d7f2e9651c6c04c088db68b9784008faf474046fdde96404420df05b8bb067f317ae515f4387727cca49fc36fcbff96d46323dd852e56105 false -check_ring_signature 638d9987025495671172327e91788c8a7bab0bec5349c4b91a6e4192ab3607e3 d1a8bbd8681325216b01fa4a7f07efef49df78344b4d910b22b2bc0b5e4bde42 5 5c88b1e46da45684db1020ecd2a09cde9604116bd5d5791373e6456f52d1db02 e69ae5346b52977dea215a79a81ee4dbb6b95792e5ee6b5c3af981e945cbc23b f816c0e067790464fe1dd02d84b018a0a2bf7dab60c92fdd664212e60666e22c c1a27272db4d4884b4e0b18f883461a924135f281a2799e9f729dec4b73b63df d71039e2759581741004941d09b19669cec0880e74327126f793db3a5fb1653d cb93e7c28cc91c6e4e0cd4414ca6248240496c96b965b9475c6d0a38326fb602875a6214eb63c8e0f10d45788e46e27ebe359a85612173325d4b4992e1a5410b6a3cf33ab0169b46bce746053abcac07081fe60e4f8862dd35bd073bc076ed0b3d83f65191ec72593cc2722390fafece234117e0191c4bf29ce5afa6c47dbd0b071ecdb04356318bb15032428848fa84f62067cdcef9d279274114a944756a7152dc48768f8b9bb9aa32c3c6a5c7c6b77c263cd987dca8df5eab9439fbbf9a0aed36ef6cb15f0dd7648c016c48778af4d7a37b4de1ddb718697d92ae199b1e0dabd593171cf56956522798f9bbb15092320fa8ed4fc556c7b8a072aafc3162081e0cfd411bd8c88082c668f9ae7c551e33cd37d988788e103f9f2f5ff6d8abecc56f115e8d862c8d3ea4a4b94829cb6bf37a1c6e26beb0da7cf61419ce7c5502 false -check_ring_signature 039e34aaeeeb37f0d6fa05e5abf36ba6fc9894dae4db402a888e5619aaccee60 d024beebb785152350ef1fd7c9cbb71301747fa6828b978134a0e6309127d9c7 2 ca090b700b5e609d9f2afd6504fe6f4c0db56fa65bcb251451014dc89d2dc41f f416f1648d2c58927083addc884be42dcfd18cc4cad9913a6aa37bc3d8139a6e a8d55017d747875648f36ce82d1b1425b97b0621ee62331ca81d2b4cfdadf1024868b2b16cddb989386d26144fe1b09867c88d2c91a1688b3deef6839e896e0d20a2ca9b01e09a971550f96b81231709fb389272b877f075b86de96216c40697089d50422ccf8f7cd76176dc4571d15de2ee0fda6ffd9f9c8d85801819b21d0f false -check_ring_signature 78b44775225784846ab81ba1c616a7e97afed4eebdee6559c5221aca1cc1d9c4 5055139f173476256ee0ce773b455ba2db2f61abc7ae08e45bce1ace53e88e61 2 61e98abd06487b5291b4a2d7718c7a0c6f9b3cb8663798353d4f43039f0d75b4 18f8aff4ab7061303a3bdfb0cd7bbfc2add6077d3b9a60052561af184f872f8c 6f317fa8f71e0a5f7780ee330277514aea8e059922b68b2b7ddeafc2dd47f90b4953c7d99ba8c13a646d3209a039493264f9a080637f130676e894bf26851c0533ef670b1afad43d27609d510df38a92a46ea53300c89f1e686dd32ae29786f16bc0200164a3cc0cff23f61e547dd8b5fc890ded9c8dbb11812900ec7897ed02 false -check_ring_signature d0f745da75d38d960d23be792774f4c8bdada38883706f7785929fcadab4aec2 5889d6c383c22ab9c8b37c19a6388d155fade0c2742d37be754684075936be6f 11 17a8af8e29ae5d51a6c51cfa86cf3693c2e39ef4b7eab92bafa84d5a650e1aec 64723dedb0a62dfdd47adc421f0d64c548adab408950a001e416d426e0c0b1b4 a4260fe85a13837bba66d90ec18c0cd477c25799849e85de1fc3b7902fc95515 20c66228f8a09896d9bbc3bbf8049cd7f4b19c35e01ba92fcc8acefc17e21869 91d9b114966647e4b968c931a9203318a04e4fc3b54b168984c2f947ff842d5d 7a717b8d3d96d3719073661a5163bd81189f27a0433d7f6e9ded7e9df8091ec0 d9e00d6c93910ae1fec189fa973e784eaf7de5a44e83f5124d0dc9b15652bd8b 7160ecc05dbb139673e41e5bc8fc30c5da1836055a6fd96d28ae30391c8124b2 424658e74c4830d18c385f546889847d953fd33fc9775f331903e49ebfe05791 81c068ce05fbc03fd20946d31d8544f56ce6fe0fca09c1bbf8db8364d1137895 4624ab0fc46c83a85c53487601eaa265027f7612045fbadc05d3a0ac76e0cdf2 39d66be9f38e325a329312c48363728c55810294c7d80413137ff6b4f00173057bba15e353d04969aabae81c8e7890f7cddd26bbaa1f0e64b93f2dbae4a3790e37faea2520ef2639329484810fae36e422e2d509b5c701f86a3199abc4e69b0681f1c1a7c912e319e24221d31379c94fc3b8f51220b96544a030707530bf6f0e584d6bb0057fa1f0361d12ff52147d1821c0f5318ccb25c71122bdd2a2268a01c8780cfb85ec30337b3590673591325f98b92eb6bbee7e985975029f0ba77601f7f3e939da48e512e82bfe75bc2f318fdd943f69f20adf3f0aa7ca8ede103504c4cedc44b345dad13205b4f35a1c66b87cbc980f746199b9dfd7fe81a756160f4f7b7d0fea4cdd077a5db2d69714d08f5c193e3eb8640481d07d10b08abfe20b8b49d5ad2ec219621723f758d6214158e5385e5a5c4288cc1b36c761e72eb601270923089d79b405a4694e66ded4ed6025beb166b93709070df905bf4814890a10950e589e420cb76f7fd67d10cde49681012eb4079238d345d5ba95fec1e002186721fc717f0ef60eb897d0489003e5a83c9c59c058ce0cd0e81b615db5de09f030ee2aad3adfd7634010697816066fdf2ff3dda6400b417f1b151ff576f60da575e9dc14ddee5d5a8f7df9d09722d7d4056208d3f80e4bf02ce5d6913bdc0224c9ffa72f32b5a9203814582d6e2e80f21864f3b2d4c266ed875022ca3a4d0d1990f876c17db5a02b88886dd57e7b6d11f41d4c08d2d568c81542bd5c8e5e045d96918468a6f1450a33eb350fff56a065ee2745582b017c4fb090efbb549f058d78b7eb8b273ff02d0d2a532242ce948aac57c47c6f0da940b15e0109917d05af8c85963459e9b36ea07c1b5972f3cd350a653721cbde9cc3c6d79c8ffcaf01eceb4965a0a57e80a4a0150eccd099b00652aa0fb232a332fb64fe0b37503a08249f94dd3259690481182beab0b009bd6113cfaa8c73ade181eb9682dc82960e false -check_ring_signature 39f3aa455c12622db2de343cd74cd91477969573a591aa2840d3da2023aecfc0 0b681c1668f43d0b705b1d4724e9e5478fa800daff16d4f0b4addda13867e141 90 5cd5d4e53f2a17d468a1ac0f9e68ad227f2dee8d6857d956511b457ffeb2929a 461aa7ceb26cc23433c1e67cc32366c7690fdaa3b5c225dec1498e0e92b65cd1 20d2344133f72bc03bcf24be41b728bf6410ec1a1682dfdaba0f01cad3b56f31 4e11fb6df68780a0bf0f98ac497dbd739210585faabda269e8e991de0288b84b 99ef0a0e34151333e337b18ac8077ca5ef07f8929921b49b86c549f3a77cdc89 314901e51e5352c165ca03b7fa891ce93adf87aef1c2d474ee1fc0412b848883 f9bd9fa5ddce73a2a6b04ef6c7d2088247752ced476cc9a18cbc7c7a12af964f 820fe89807555b1b754cd0d8a6d618b98195c3f72ad78c42d5d450bc9a4b230b ea89934af2eb5ad6e40a177aecb6fb844f2e83f3d3d2fb706219af276640b75c d9804cf21a1190d072b0a4f543b0813dfb6a9af5dfe4b71c575472fb905c954c b90961cf9a4f53b961e247357253f4c4026e1021785a143855a55db42418472a 703f8a2ae3e3eeb46388801087c93dc92cf3731a57976da20a23e93b244e1cf2 87679ddd986b1758593bd5ae38b6b68b541811c6a1f1bc80d9cbc0ca0d7e9558 5feed638c2771b5f18679870332e75e742cb2cbda5863319208f8ad0ce2188e5 7ac09c1cc6a247f145fbddec247a09c95e0be535facf5a5d1b8dd67ad4b813df 6137412995a3768e649982e631313a3e8d2782e497bb8b398cf6714af2c74c77 b3ee775193e20b274be34a2018aa1ec119f94acef645f6377bf126ffb967e2c3 916218d6bef2a9eaf4249a5d29ff60cc6f419e38c34b6fd5a7bc2bec0e4f4ae0 8c1b4da04cef36ff4705bec1a2568e9efa064dd3698f80de5f9d76296b82c591 a61055e0ccb353bfb4a56a1d79a8c8bb72dd1f269d39a395048ea8662868672a 29e48610e42e76874f45017925123622ea4689fea5fd5a8a8a9ec1af11fc527d 6f10ccf1adb2e1e93d76cbbcf8b08155e8b20f8c937a06f1cf58d4113196f0a9 81d672400d45590855533457904188f6a8efb686dc4b25193b8add788ae9f1bd b969e02901da79e9efda44a8bd3c63315719f61764b07d67fdacf21accc6a01a f6125243786e4bace9a1adda24c67feafa91e36ae4eb568f72e60201cd1568d1 6396d4044bb0b4b087f3ffc5e9d81fd8935d9eadbb57e539aabbeaf1ce4b5e42 ae9bec345534051b459db732f8f1613d577d7bd72973c3b7689d383b03b52bd3 90ecec0217486d5741289263987214a14d3d892a2e210acdf71ab4cbe32cd4db 0a2e236ee4f53ddf158e697cd0cf8dd0f04e8b12fb4a02d824d1d34ea24678a0 18f6abf1c3831ad13fcf9e15cfbb82e03a9006f9bab89f4f18e3f474a165f344 d7acb523ebd4488b7a29957c8eec10ede51862b3015096115c47b56451815a8c 040b8cca4352ccc6c68b94e368b909be000034688796ef962d60d5b6a4830c54 4b4f938671e73debf2be4f88ae09b06dc11ab71daa3d1a187945b44ada00a96d 8c43a1b68ae4eeb14bdfa3de99818285c9ac766ebf33aa2a0db677a048fc8550 5a5e8ca64948013e2dab71e73feb1d235c184eef31802aaf438826f84d9ad64f 5b0e405896b3498c1c0eb88a3b0d31dff2fbcadb82d4e7a30f2b476de54897e9 a6b684b54357f53d5a31542d2c512fc8d5fbb7b07a2907430d39130616a702c3 72dc02f7353fc7f1e31caf8648b2ff8127e17df577c28832d362f877b80b8e7c a30604921076eef9bba22f2a9052af3e33b4a133fbd109d120aee8f4319235e4 c4bc288cbbe1aa2259cafe95290318f02830faf34e3d4368cdc6cd72edc27acc 14d5b017ff05fa87b7630d18cdc3c19fbea5ac7770fee759344da4339ea39358 7c8127a4c79ab550988227a3db3dd219747ba24efe93557289cc1709d3476ce8 3efa4bb285697d9a657400d96774156294caa5340192d01d1a45286b6741d55a f7f6b81aabe6b047142b4bd871ece6389ddb73f39be5a64fd8d8a6c03fcfb40f bbeaa401d61567f5b99d47d3f4da1ba4f2227f778b2b723fb9f5382b25ec56ca c1ac41206bbb0cd2f4d2dc2918ef6c268ac23c4c013028d55eb2378e7d56f634 60c1da098e22c03e22cc7305ed759fbb497f3150179b8067dc317474efe022a9 f58e6cb8bb07a01308627a43afe3254cad69ea9f1e06ed04fe300dd493c27ca4 051d6433a096e35ef3a44d7d2be1aa673918c9ef2bde2480b3eb3dc7097e4e99 608a7af4b0f04ba538da8407aab7d79102f1566f06150d1911a28e61b5c6a417 38daf29b30e86331ec888df7cb1e704c7b46ab57ec0559ca39d0f0f553dcb1d8 992744417360f88b46c870d5cf37eb999edc27351c837377660687ff3729085d 5d19bedfca142c5b36b756ddedb6d4d4e121b8f6531d55a47236d5077d2ad6ae ed41510a7df10f9f02224d5627046a7cb975c5c9285de65e4f5a15a42e31a98d 780521a49ba6a805769142e6ec97fc1ec5671e364da794aa1697c15f40fbb5e3 5d4f34ae2bb25f27ab4e19eddd468edcab24a6c8a35ece44d547536abbb9b109 d9606ee870833f605a8aa2996ccd9c85659e6a4fc2e4fa4ac49e6ec83e1aa4b1 269a48763dcd2d7a63e06bf501200fc856b66e9996d87208686d0a5031de0f82 e107ca080e3b6b928988bc2c7f64644cc5b694ea7360b61c20ee5725acab18eb 99b35336cd54d709fe02928a36c58df73fe6aa26b5cbf84396ba20da3d12c4d8 5029685ddd1b34ece484a2346b259d9311492b01f26e21000f940511928b8671 49d3a9e8f668426d2020f3e2a86f3e77fdce18721ad2a3bfff8bc2ec87b1156f 8a69be6f54bf7548bfe8c57262e67433ce96ec81d3352ae2b439231f5d35e95c 4e1d9356e0e2d36d00847dede2d324d15fe950ae97ed0e591bad75f442915190 bb85e3b622ebad2885e8338dbcd7465a0c6363d7d8688a8d3d56a3a1d386ce7b c8d606214d48ddafd42b4eadd37171e8295a9e8e84dd56fe43ec5f23865edda8 8476af82890f51c050e12d9f5557f0dd054bc240fd71065b9aebdb43d289102d 9dd8980372f442e0dc74d4bbf7d35ee8d8c0753ac1e574dac9b938777c81b718 690330f8842e2278b7f7c55424bd6ab47bb225f24d9b0c1838e530122918d89f dbd3329def046c5ca2c8bcfbceb65fc6b1919e276b71880e70cfc46f4985ea57 3befceec05b6d09b6b8fb4505878260c3141387f17f45b4d8c42455dfc185c97 6c8b8fe3de69bba004157b471e64e1e4372c075bb5f90b6ae4cf0280a4a87355 c559105aca2122297dcfc601982fe8be0062687d56463f42212a3db2f87c2625 916dbba1ac3a6da15b8b7f39b2071ce26ab4745017c71ed5892ef8e639eb63e7 9651eb589819675cee3152a9764731ee4aab92d1cbf91dd48ec11f78ff6cd40f 598badee97d427b2f61ccd39229e458eacfc169a82431acc299aeca6d83f35de e6786c85a9b58c2a8a22c265853b3593ab19a5e92e59f30a3c1f0c5e9d52a222 a74b43da21c3d8a67a580ca02356d38724cdf6369393f17ab88b9c7df00608a3 7fd19c96abea91f5baef8b5bac13b0c702961e911cee031604785947451329fa c59836f1cf59e012052d184229744375967d11097be2baca46ce5869a99ad148 cb22fbe0d70d1af45e8e16a16bc597d1289d9ee8bfdc5ceb4b1d530b607a97b2 7a23ad6698328583a8d7b94cc9f096b829dff64117db045432a72dd89370e9c6 81cf1399aa202ff986ad5a5a46292cefefff69e0439982f7f66e48b4c3a22bd4 609d288728c69662a3e91299be1788ab13b3ec7899bd1d20df0c5ee1733e7c2a 223883427061c77f7953943384b9fb41cf8c0992d5af91b3fd9acd4ecabab936 893682bc6369bab31af845f6ab4e0505416f611159dde12db8968a17d028eb53 5e0658abc118b92fc7bc88946ed4ba08468bff3a591ea59b7b3ec7ecdbf3ded7 fecf57b5c98ffc7fc7e9ea1c67994aca61e481d1f802b4443ce424ee56d5c50c 3f25a24f9cda2e9f4ea9ad3067c19ccabb33ca3906c471359368223b7a247518 67bb8779a5551cb32ade8b5715f9fbfc99a3e900d8913d3c1b5508f249485211 f346bac80a262d968fd9d7bdb3d711a7db962ce287ed89920547738e60cd860c62f899913ca94c5e003c1afe35b65ca2dd99c084c2f9f53b7bc0dda96accff08e50c56768a74db1a99dda7f6095c44bf904f845358878fc8bdcd238388bb7804d82d0c53905ca816931aab05d8193653cdc31ebed8534a2d7bc6ed981e24af08ef04647d24f999ac8c4f5aacf93c05aecfb99c8132e4b8e29cc4c635fadbe204d160f6be871502e75d1f924207532e1f25faf735c9c89aa2960ba6f47407fc0b5ab276bef6dc4d3e2a2b26560fccef8f726c1f28c67627d0185c5b2f74503307bb067340e732ee6314a551d0a3c6c1afd53cb27c5e5902ca9627fda288dea406a960f97cf76ff7529cbaf7c603897a528b5f871e1a6167b8c7a1d5289dfd0a0d664875e56e91ae1aaaf26dd90ea9a41beca307912dac3c3203c7871d59b26604f80c2a6780f857a211c4b12fbe9ec7f3d55d40876a2e42d963bc616eb6feee0719e73fbf321565805004028683d5cafbf3149316f44638586c12ada0b3f97b036b7f2aa56883ffa879000a6766e348dfb82662744f416fd7e2cd44e7e4df550bf90268623c1ddcacf5a7ff6e68961a1135752bb2e9fe6ad76137d3b45e99600943d9a85d0f07efe526d9694f3d0aa380b76ccb487f1e679001082ee766eca20a2be358dc1f47afbd84ebd759be3a1c960c7213b2738faad57701e1cb50130c0bd8558866b72ae4213cb6330d21ae36bcd563c0990302ee4f17198b9f7cb6c10e185e04db7d795af47a3c2892619d5cd51f1c9c599569455b17f2c9d593e28103d929abe066242d665f444ee3b23b32c7ed23fd7ac831e23bc716f0fec4f5570ad9eb38029a5793668bc279539d597fecb6aa4ba761b5497c0b59e604db19cf0288a03d5ea53c151bd5e817a360e83d1a886da64cb50d8cd6a5b6aa65d2734f0229e734be08974528d2a0c220c08fd3d666ff1dea22facb9ace30fada88279602b749188cec99fac9d4e014458fc492b871fc415e370c84182657cf512090220cda2fdc11df8af089049e7a4a196f75acb8c7237abda5db8ae4ba3ac94f42fc004374b1ea74e3fcb6b0d6656971fc77dc350e515ae2f839da4dd66606c335cd0a9d80b3459625f1728552fbf4aa18e68bc1b1c700c8a727ef9eff2017c137b30281d093ad9d58e1ce79a142b692f73ca2a84b5858ae582d4b079d8722ca55ec0c4dc44bae92cbd720201b178848a8d6f4cd8727b94e9cf8520a6353ab238f200d25750ab38fa4bb569275c4d7535524a7ae42522da3fb61f14339b1ba81275303b2fbf6115083d8263c4e193f9e5b13ce64cb3036d815a0b91c695ce900dc020e8a189d8108cbcccebb9f5a6aa499d44c2741d1da3021930530ef8d7b12a8ce04c78d4e7c348688d1f3bac6d0fe49cbd2a58bbeac7118451ecef670c0f99c09007c4a141903d7c101a93cd236544529eb67d2ce56e9189b71c3fe13eb7b8c300931d9a12984a8271841fa4d3fa0d32a54a62af9dba0bb008b03d6c17b3af88902a42fc94a9ffda53f404b79e7e5f04e2c8f7ec78e3cf085d6d9b3b1d2f792710cf0d5d02ab1f8dcb614de09f171d2409ba6cf5fd91f1ae2b3ad364b30c4bc3800f52c071caf86576426d81410b5ac451385dd9062a83870e35383b3f586d6d00de67da14e2a00ea72b50652f4c6465513225c30b7540546d5992ac2c522422b0ef85c7f6ddd86aafd9f0bb909bb5967ffb6981da4f2e208f483781272fd11e40ab2cd92cc9af6b9f6ad75474ea6cb73a416bf446117a3f8fe2803597308b44d00b2853a14c94282cf757b97a185d117bfdf4141c6e6083b6079ad0836dbf7d10b127e1037b2e26f39e0887e17d967b2d7fb33d81983d80bd4d4a085b105c8c50e490d805372c2be5bd9a3a7e15e042ed7067a3b88ba4ac0ee2a721e7a23e8290cfc385be0ff3c38a3a91b3201d141fef53816b6092ed1fd8e38470a8b1a0ae10bea0a548df2470dd70f08b623ab3bc391d04ba278372ecd7b459530e94d82fa0326b50cc3645b307b7293497546e113f99c63c4adffa2685876ffe184ec68500dd858077fa0608175fe191cf6e2a63899da9d959069220cb1a198f4cb1a002a0b22a168c66a738c9f7edd4f007f6d8d01659b1db2d9074108616f804f52a055095949e12f46370d00d27aa1b13eec11acd0b6ad98d2c5f045f9c4e454f06a9a02bb590d1e693a6b404d588a13526a21fdbf758493d5b019887bfe98a10fd26d0d7af7b267b2bbad81ccde7a8553d38ed1b8f43da7f5f0805fae74943bfb9fe40caa74b22138390c4d47a0b03eb0fb520bf326deaf5618567a2938fb3ac0d3b106131541f31299c4ce3bb41986b97b4c285cdcffce1330148bf14f52e7dc698c0815aacc5d7d38111a65330f0c21da54b828e50324acf9a24abce174fb0a98ea05ab37f424c86c0c65e0c5b193cd2f17ed651cfeff2b13b03f9ef597ed7d76ae09f3edf4144a6e77ace8d4e18c3cf86f6252c1d0258a967e35c679a2b3e8e411067f05a090fc65dff10539db58b8b9e79a9d8774cce1dc3ea3a3f8539de65d250ef4408eaea6ec57e8b36a9a9c513a75784ad7fa4fdc002477ef3e6ef98992bb0b60c130ff21a3c4e579a52c3bb82615d2ac6f8a1057c4a132cb04363b598b0409997f6906028371d53fe841e3c03ccb7502cb3af10c93d427d17a5f771be1c70e67cb5a5ee97e5649604cc9037a68279dd2bae89929fb6d7dcb52f18a860bdc09f8e3b32597197cdcea38df55613c9da2630634a927a6e3dd8b64b1f0200b3f0cacb459091e8eaddf1f93f3d62de226241f4adf17f02c75b2fd78c02c1799d205e3486e7c40ca1aa603a50544baca16dab577b4594a5f1e0aeb05d3327a28b20c6462d278dca0d06fcaf439c84f2edc7aa2272416ae3adbe6a9faab68364a340e0c762cc9aab6eecec5c131552319fcab06837354da916526895f67748bbd4e0db2a1c7a7d18e19ef3c18c2f1825af556e32bcce7d475a523596c1878ab42ce0ee2b0677c3c1b69913f28995e02cb46614e3cdee33e2aa11a064c9025e0308f087fe2599e06449c111982f3a0b9e89c2a11514a78e815490e3cb9b9b59414bc0d451c12ff49bca006a19637bf1ffe67b6bd9a41e6cd9f7d37f0d8eac557dca808938afe7c738c71b69db6e149e1b52528e482b55dcd8a8041342ccf10c9dcd00e2c618077112ef0755c0e582fa4459233764dc047447d7639a9f2f204ca72f204f85f47f398aabaabbcdf0f5416b041d352deceea54708658ecb34fed1ed47b06052a85005710f32e6ff1cc54d600aad0982cd046c93c410eac8bb5e6b198e6082f99225313c08abb424198e5cb5304a3e4a02822421013d82bfec44a88442d01b470e030bdfe083036be5cdcdd24c52d999547e12b36970ccc67b78507b5f1096f2d9e98759c0d33c9a3251c8414fab3bfd2f8f0f0ddb40a83249f969ef00c0e9ea4a704423bfaa22fb41234f3e52e2837fffdc5252aa0bbd8a018f8d9b1160b6524da2b3e2beac87639aaf30aaa07aa515cf0bd114f179c982a333226c18806a4741dee533647df9f32e4d17e6036a17219d12e336cec78725d53efd44ce205fa24bff8cfe315d1fbd1e0b9d82a33d3ae63a3dc324147e0aae12963aef3ed09d0408e60cad5175d026ff5be994766956c09fc31a7be9976708d8b0fb77ce507292b51eae803beb9b07005e6eaa2593b25dfa87430850fbf2d1688b931ee570838993a6586dd58a4091818fcde0ef87c0df0b3d5c4a6de2556e624ee22b1ba0074d55da7d908be2c1744612d0060606ccce6bbeed905664937b84724dd27400ed3451fd0c95d5fbff3ad51a55ca257b575a2422a2badf26bfb60f5db67d70107c40512aa87bf51e5e73292ad31f77e63e07a696c5d80a5d8d3f6f91bf43f7f084303ad61b8ef0dba0d9b34e0aa9f5c6dbe211c3ff5673d0d6e9e1e17b8d48d0a33b617c749cff139da184fe0708421f2342f3e0d07f34144effa826e00e3560db71ff4c97059665aaab8a94b4dd7a0ec792542abf4fd8ef888b8fcb92bece90d95b764f097d48d86e58b108adb3794820528bb5247061e56762fefc94d027a0c5d6d4c94fd5e18b5e2ba0b368922662c08b80f4f5dc8bd8e7bea4d2df60e7e0ada3bb0d3c4920cf6408411d146c10f59857186c0d3197978c007062ebe01a50742fea676a693d1aa7c901325131eacd242edf1a47668b3e66e2005185cfe20039fd1b5e3ad6b0af4b64c62510761996a0b49ca5518df371d3fdfc1d53ea9c50df97121b1b07709770dd858354541c29d2d4791d9fafea8a556b684a44bf29500ea4afa88d35be5c3dc5da8d81a31431ef21e8422ac2d479b2d597b141027fe0bbc6785932bfab9b8eff79debdd7141a6f50660c6e2129d018cbccf52464d66004e4ecd5af0c11c560aa584c6079c3ae97b14e4ef5d22d4b1b9e1b1b906ff170d8a45b7b3da2dd39c17f077b5d95c164fc934bb6ee37b48053b382af8941a3b007865426fff2289af64101670324510bed8e0d1849fd4cbac196a4d4df856e104b98b45833d4196dd74897916248d581423cd35a38e10b5a926ce3a69c3247d08a8cc51396c35416d190d623370218a4e33d2e53b5f21e5fdcee490b7d13e7e0df0ac39643d360963cb73612215a9677d2a3c88dbc77e351fa19da2101b917d0d445481e55495df8ded0127145174e3466c26f899d7d72ad9ef551cddfb87cd0ed94afd041eefc668e5d8d21ba23e14e7f41eb7736752033f76c5b8f6a6e597078eca74599cae2205f048141cb6bf33f25924000d85ea3c2a5f02281594907b044fc2c80eba6d075946a04d2ecf07353add3aa467c470343e608d5970530bc10830fe87fe713a7a65e5230f27fc0a1416c33b9d11a52b6e101a589ad517c38f0f50db68e55ede50d7f1cadc20ea632231453269ad12213d78d4052aa1d71ce303583d97a03d79a57c553c5e7e411b940f67136e879667c9cf9793d3b1dbe7bd035f653a782fdd11f7273f6f4b6400898b7c6c41192afbbea4550153eaa6545d06eefd474aa6d66c104bba36594b39724f24b9e44fe20a73f0d493caeead457b020632ea0803f76aa1a0590b3ff79ac52ec339d682c43f7a5ced8068763d8eaf092997a9515ce747229f956c1033fc6a1386ff2f97071bcceb905216defcf84e00650e5516d4a1ba0f2e8b993b7e85c56422266f18a31aefadd5092bc20696f80e6d01b1455c425b97f83fc6de176935e55c4fd411c615c815c9980a2b32ed5c0c62ada3334cab9118ddab84a985694c6e2b23849196a80cb51e0b1799fc30130b515f0e63f63e36dc2ae42f7a07396869894e32c8913dd15bd8f6012abd80b6011c0044b30e070988a0015cecc341abe8cd740e0e4656a0e4ba3a8a95c4aadf081c2aea3317b9196914be4923704b50fd40aa4075621080c45e4ddb0de8a8720b7b65d5d0c6c030f94aec42e235c58c9223c623d7a5a131ba4677b5be1046990c3887914e6b83517732ba268e3ad6ced341c45f329c9e1bc70e868369c3fa7a0cec8585a6ee7bba364b59dc006ea27ebcff5a8266de972a9df300dc7fda38c407a5d4d011eed76a44041e0ab39829605e89b896ec71981d7576f29c760c03ae0b21be2e80df1119eb64f50e6f31342bb88cc306da8f2aee54b122fb9b2921ec0009f01c224acbb3ede9e95dd98693273209a690f277b70ce733a532ea106e6509b126b76eebb7c5dad45542afcad7af0991e40ca8ebed160a8609a38ad5494e0527893987794737cf2c540dc8df76a16dbef8db7bf1fa143775392addb787c00414e9b4850acc190292592cf0508c0e9bbdf0ea46ed7069ab7915fcc409498004ea64dd091e1920b234c0267044db9e0f00c794e1267ff2174b411deaa579900f67790067134997bcd7d1e19448e84c7e2d0b1887337fbcf3fb73a9ee8540820efe5f7424d41d778d889fcf7061cc74a0abd2a65a41faad05d9455bfc6b250402409221264a8e1e5976247f959cca8ae636d7bf3d83f1ce6a20c4530e36eae90b6f62a6b644437c71ff7ddbcd9d3879ebce5cc6412d056812f5a8e05395b16c0a460bb54c9a3bcc2e0473ea12b005187d06f668fd6d2250c3eed185f9f0c13801e9dd26481919518e78d6be592af6469834f60a4f90a9c54a5c76b8c0cddf290191865dd0e8e0760276e6988a2421c592b8b1418e78fbeb10e741f1734827b6279e8218e601f6ee847e894116150e31f8a3e669a88aff3a3e468ecf12b00600008fcfea1977275ff9fc470fb90217dbec3750f7af70bbae65b9a0c4102d23010c97c455319c2ab1900084a014d8c3c408234118f259e99baf2cc471ffcf05f403c8e92d72c0fea99d308794685afdecb356bb4f53c2d0a7fa7b3ff08d6c57f706b75a0de64b0bf069940b5a1327ab2e7e6366898a2aae3404cad8ae47a78149008fced287a131604540bf1ba3ccc73c177dc3573d32e1bc891a93ad23e0bdce0266d7191e86481303707e98d2a3dc33e78a18cc2a37aee0327f984cd511e6c60ce9feca962f7772c9ad78488d937badc2053b976713f936375d4929ac05ffa90abc8db5e4cc0989e8bd72ef338fa642bbd0136879200d2519aa2ed87c152f85021c246c08dd4a928b01abf96e31ed4ef987f0707b64ac56008e84d1b66f0a21033a44f7d11cf7e38afbb628cd574e7902dd6123eebb27c22f41f372a78fe02f0877b16683aa7d1a37e96470e56021c3673a914571d18208b375f0ead75b4e8f0ca3e226391224ef7f94407e98394b4b7541ff6efc437122aeb63491b3fe52710c2d0fa5984b9fd8aeafe2efb25a943d60464e4944efb84cbae4f4ce9273785103a5b88244a49b37cedcc2574e143eaa4b67781f7908d9bbe03de7cd03280b7b0ca5990645029526cfbfec34c931e76572aa4899e8d033f37c2d6b25901f814809841ae31c34417bb08c3675746659081ead93c6b4d6aa96292f7b34cd08347f04c6d662b3d8e50f8bace230ff9a3a40fcf505fb760405517245586d114e04740de3f22ee13aa4efe7be4920af214c1f82fa525302df39db40d6b584d89cb9a807abecdc7ba7b2847f43f378e6d7b8d0155ce6314ae4378f42837d7ba3bcf3a00dd582ab0e9dfc0d6b6e5b5cb920d27e57d67f8049c58b19bdbb9604842f37b101fbebdb8b76081da6f6f4c1bd87af50f1e67d252f3f2a3dc52ac0d050bf1c28051d3c1d11553cf4eea39fcba2304d6a556284146e459e35c62f8d648399fd4507b8b22bc3ab5ee8e48deec1d2da80240250599bed8928c803c4143e147d247d042a60f20beb91f9ca0ef08d46a7a7adca816220ade2e76933e5ac7a16cbbe0f0cca665a37a2ccef0b76b3c07980a2555e1c2054d57777b4156165a7e0fb9a380ba9c0351ce554c50790f107e973b38917114979b8b2a319db7ad9464006fcb10b3333ac652fcee2b1ca198b4d6fc0a2a8e1a45dddad6fcbd55fcbc017fa95f604546095833b440e5d3835a3b241bc1067555e651b0919e9d9bece75823c2a93022dae21769f9026f8e4798d3ee4ed1b37a7eca79b69b450ea6281c2307f7c83005397b61bf03cc81b8f5c9e30d9b7866a0c2457bc11a488fa6770c8874a37f0062678f4324591af21951de4099d13e7682191301387b1da39b8b0ed7356b6fc09986d6327836ee3295ee3a225a03cc6d268e180e5fab4e9e1c10ba3126f7e450af4b25dffabf184bc7e868961ef7c3e24e04ac22560d3ede54f073922b88f200e85064f369063aa8644a54b75b3362ead295d01a5547219d1e3afa98ee7ece60310d55452ba8fae94c288d60817706376f4fc8d111c3ce0f3917ab95b8e810c00d999faf68b497c239c5dee04fb6c4566035073fabf7d1b0211e0a92b0c9a420582beb26e3112238b6074657300d27cb03a5e650b295a34bd6ebcb10c5435740286463911c76c965f15715eeabfba2b11f164a968c938cb3a92c4d812766d1f0eb60ade47ffa67cb103e4e6917c0b4524cbc3bd8e655098e827756a87f7f02f0f05089478ae70d9026ecb144c3c31bf76bb0d943c215b2a3252f553848799ca082b83f7a1bbccca759497c86fedccde2eaa3a468be268797f8c017b8ccaf60602 false -check_ring_signature b7af0b7d502cdce1a493c61b753f397e5aec3d0a611710326152faf9f36444f4 e4546dd97e5ed855a96cef0d313c6aa0911c329584fdc345a414377c846d5bf9 1 0b9ab3e49f2bfbd039e68df77ac371a65e05bfd4b2e007e21685462c0f3caf2c b3dda473867b770fa34f945c20d08a5176bf10e40958fe695bee9fb993facf095e5167cfa0b20cb76d7e6f12e557b1ad134886e12b6b4f50c6633a08f327f30c false -check_ring_signature 5c3e4765b1f76080fd69b43b8854785c2fae315475841f0b69297642c9b124e5 14ecdfa9fc9801a9d5b4c81d52e8ac0887b57259731f2acb68b59ff3e11c5b53 1 50749bef2f3cfdbccdcbaba32aac77e3e5dd5161a07bc3627cc4464c0b3e27b6 51e7fbd0e84d9ed20756c77ae043e957cac9c2c217a0166a7873e002822a430ccbda843b58ef54dd85ac8e0a09db1b0d7020318a9e92f35fd1dd6045774a750a true -check_ring_signature 157f9bb9f5b72a2d22e7213832070c3d1a5266ce4d09aa00be6d47fe8125b15c a332ddd8a0a8486e8b9b6b93ab3909c7cb1359cd6e2edae564fe0237ed0b5e8b 116 0a6d1148f4bee5fa507a318b4465507c47ba56b1b1b4ab5765abb2b0e433f907 748c8949ee79f26c4398739e9f151abee2f174573f5121122a7a06fb86d9f74e b3e352f38496d467a1718b13cd469b6c45a244cc4c3ddb608977e7f1282f875c e10998456fc71c920ec2e03aad96bd5b795486488bc8578e25aaed8dd50aa7dc 9adee13fd13435f7ce907fbb8a032a93788c77b967c5a15fc9364221186ef6fb 7b4d405ee33a82ada90966915771a54322acbecb160d0885716406ba62001fbf 87a94ffb1447d68cd67370461b062603dd2a4d4275482cd1e62bad7dbf0a5502 821d3c0ee26dc3ad86a4b6418e39757825fef14f0f365c84bc5a6955cc62b838 2214cbf22b25558bc53098a74203435285629cd7c5c6a54f6c1866b0666a1baf 0e1ddfa3d1687d38492afdc90f021fb88a884850707a11fb875fa33055429c22 cc9ef5336058b9a3dc69696cea6e391a5c3b9918dee73d0d37e9e93d96d39a62 95c84526aa6fb8cd01e89d3144e34a986a2a103416e320f738a8a9dd393a43fa d036b0244cf897d3e2ecb8e20d76729807c5a865a3296d49be764e3460a1b575 f24777daf2bcffc4ac674be7606f3f16d6d4980b3c6128567a2d7c77dc07e530 297b40d35f0aeaa60747747fab1bff114b098ac6e9062bc4bea7c6150e6c5123 89a0e447cac093bac9655462defb9ff954b3f3de7c5a33ab4e2a03d409a25cc3 99863d14b425793be9800d9558f67a036564ae32cf4ecfbd6a929877fe6401d9 d8ed8094c5e0285b8a347a283316df89ae7bd8830f1917c38b623e7f8dce63ad 22633870c9308117204bdd158b3d955d50aeb54c2c98c04f611454eebc7e0e87 c6ad36fd3a5ad526815b6c85fca2445e85b7a74757309b0b3b8f85f80dd97bbe d794759237d296771262595bfd4d93ee247f8a8c28b0fb43d6597f0fca29ab4e a7c91489baf8ce752a74cbe1075aca6a376a750541a81afdea198d01dc7b97a8 353576e025b27d14d2e3921eb6b057c1b4fe7df073567ac9a227cbde4795bbe0 151a3c3cb39b3077ae9a4d3824320a9aad096f61b8404c0c0ada0c9e25808426 5552a10f8b5156a3d6de76644c0c07b9a481253c4a7878c6bc95fb1a83e0d4e5 f32b048fd54464ea732a6b0f1f1511434da0d531de2e72563eedc9987e1342e2 4bd18f89d24e9d78d2e736d9e32a86079067a8e0af2287303341409543562303 20537e3297310a1cdf920399a1fb1dcf6a1db5399dc74fc7c188757b3b3d9330 fe63185247fa2c5b64f5b9a82e3abad3da931f7689fd3d2d50eddaadc0ea1b61 69cd718172a2551323a6b99f0c41065b99425beccccbd0189c8b18ed7cae3678 79014f2a977537542fe276f29d37f3a9a1128535e1b30931178e82f9c349bfc6 8aa4451f89f9db215464fd95cae7f4fbd4f1f7d1a63ea0a26421c62a04aa7a47 8c8a9df43c919068307e706cd5bcaddcd702ac620ae63cc4ee62c05e5e2056d8 425444975ea57580edeac970c993e3ba0a7fa63f683a9b539a16f11d198cec0a fac9e0f58f7be76724ae0474c6298e29090c975babe5f1a27d0bd6a2f2fc9f1c fc1235f0762bae61e2c5de96bd32b32a9b4901d51b7ec577b6873675be659d95 074a55e6172cb234fca4ee872965f1cbd7fa38c65e0b3e2126611e41f3dfdfaa 12f1a3376ab551ac3a7c5ec2e80b99f7ab19515c3f769957a65517e6f6ff0647 6eababa3959fbf4500c46584a18bf958272599adbb7b361214cd1577a50a6df8 d57e1c0ba3c9257e1329353b57ce68fa75af341abb4ca7f8aab68f39dd4ae56b 342be81ee4a5b05c13f960694b62f0ec4f28983b7895bdd421a9f1797d0763d1 af891dafbdee0c84fedf5a29ddf7990e397781c13dc5044254c57f925af5f535 2c1357c1398e6df5b272d43bb4d8b91c5d8bdc20699dec09ddf9162fc4641e75 370ceb57232ba40550de342b774b7a7cbb1ec7baa82477fe7953db88c5ed2262 59bc39b238ee7d4040f31c0c665ecde2d21ac7b97e758e16231b2a0f0e221ec7 785b3a226d3177d465ea9766e56c703b3fbf079fe106710b67494f46acc1797e 04cfb7c5bc3928031b19c2070fc63ace3bf7454c5c6ebd5b878d3996beab10ad 2a75811030a398c8de5398f13233f4024689b3f7765e47a1436aee1951dca308 2160a66dcd06948f80612c6d6868365aaabff1fb04ea36261887aacd2babd2aa c006eee85cab046664732bd0e8e1b58c762e25c215e5541a8c9815801d17e8c0 1c4df29de4ddc68c1865e1c5450ee1986d30528d8526629dd6bae50f93f5c2af 5aaafeb0618c822848d8f59cc68e3b45d1c42221aaaba72d2e12a683614de8fb 7c07e378275539bd3f1363518066b62551a70401731313ece2995a6413725660 61bb923324a87de279b8727d1a4f8bd106488c8590cc5bff22505df77e273b17 4c78ff16b7c0943fdae2e1156568f227190ce3594b7491bedcab8f432ff72677 2a8cfe1c98e44547bd9c36f03c1aa484b70f8c8ef3915c989fa7d944437f04cf 9c0e890ff3e2df82f83a4f0c987ebb921a66704da3269541112bb2f208b935d7 93199f512ef039d19f9bb018eb5a523213f79ec05582c57767416c7a6ecf25ad 51a7ebcea353dbb033b70f8be37d3022b0748718b89b7fd5d5240408f4ce11e1 cffebff7fdc6787ed546d638138ce0b748ea10ebc721627a7e49161fb6ceeef1 06377a9d67345a0c2ca10867a8339f5d350fa025aa26cd109ca4858bc582a59a b01a5a2efe5a22ebf37597ccec9329371d112953b6b19f06277c7c993027c510 57d1c5fde3b7ef596dd1547a988a3e81fa6af67152bfb9298ae4235bb801dd94 55decafb9b4e587e41685c4c2d72ee4aa8aa6025f42524f8f28fe4e2ac0f6a6a fe8aec0619428030d797acc24d3a7d4bbad7b51493cd33c222ed7847785e3459 3a0e601ea7448f1971e01a75bd9ee63a796828deb90afb089c074d3c8c7729fd 4acbb707c52448f2d8e7d0321f317baa3e03f3fe86e591e7677a11bcbdd1e07b 9d9720b7d83726371780632dc2115a68ace21ef63cf9d3de6533da936c3e16f9 a3f18b03182cfd90d127516d86646bda94f82ddf1b274a976b2ae0ffbd338a56 543d6b8af09e00beb3e17b654f59f486d6db81a3ffd417cae8ba26ce2d1062d3 686c62c86a0defc0147bfa93590e7aa730980c047002e99bfd1426a6b6ea4a1f e7a4df450aab7294471d1b7e9eb00d6ef7e4b97b3c811876c5812fcdc8d7b385 3ea264e3a140c0c477f0da281d4dd6e43212e6dfc4c2d58f344cf75a30f773b7 753baa5f6f1192782377dae0d8639e5fd8451292485735c919d3b998ffae7f96 b80e6e0ae7894240b6a1b4dd5de3b6fc07fdd5246e8ee7af34791d5b72fe3e28 d597b2f05f3f0283323f57f1b12b84e8461d3bcd8a2c3d35300250d53cfacfcc 275207051276c5711979417964eb428c1a3ec7ac1956628d48329e62d1070e77 295a80dea96342a787b4b29915346639276bb882627374a0a2f9127234857518 5f0c93cfa667f8f19ad2f225342e0964f531b19aa2baf2ad83a711b841237fb0 89b53f8063dc7edc0f4260b0b9b2efd2cf1e97d0ae4dc9af112ee5d027599f74 76b3934a7083de299063cd20b58022dbc2cd3bb50d1ecc0454c895d3012aa473 f215d071bd4449afb971e26d03725c093e1cc07b55c054a04153a976033dd839 6544c94744337f526622b3e4dea3e7f86e57dc54a255692e8e72aa20ecba5450 e50a3ead715e4b571508f5a5d6eb05d1e274b32563286007c22440deede0d96b c6345d7f08f299ad93e6c6f7193ee1e3e9933f7508ae7bac8d9983b4cd84064a 4942929341164fb29a428bcc82233acb4b28ad6df4225199fabadceefdef17c3 055aa64ba339ff6dda69eba4e5e3aa090e917591e1c5a308ae4ee8399ac16b88 c8fc1af1b50bb9ec410414404379a6ce0c582cd5e1dfcc3c94f783eb70058361 5d3975ba25b2b79b06c6122f52e926654167807dfade144c25b4bc487a20c46e d67f7a4f6b343aeeb9b469da8fcc21141b44b2e581cbb4e0383ecf8c09028778 2f51929847921ff586a222ba3b787891d7e6bcb361845d895f9037bf35e1952c b0d93953480a9d1556cbccc3c9afea197b0093489026f9d96cf73503cd87bc4c 736d00800fbbaa30df2213a8af963f1f92f0fb8654f8143b179358d649d5366a 048a50a49684cdeedcf4c79a325ec9d1a13b5bee48cbc1f42d60ac5834a1c620 4528cc2e984594bca9171760cdef09320d10b02c5db28144093af41e541e867b e281a9c71a9be93d6a93e6e3a886a769edafa87e89b85aa6b87110a03a85c5b7 bb9e024082f7401d6d43eb31f728d9b1ecd5b9923a050b7bbedbc8b77c01c173 9d6f697fd1f5822e458adb716d78d0dd07f752899f62c490e399f25558e929b7 6c9e9a5decbd0e582b84c9b0f236f270f82d0f11756cdbc555b4834c2b0220f0 7a69a13079c3e9899eb57c64a47804d65b6e35b3a660a07a46bfa914c0a26d1a 7d7572d28211a3259ce5b23a91793499577976be6a6eb8e496ce4f8daf54329f 353df8a27081965601ecf28e6d337acdd83a4bec44ff7bdc92019e99a908d80e 3b4c41a25d9f8f37a80ff08ad3415b851a039954c50d44e3eea6ab7bc5aa4f9a c9beda27ae053ee7d1cf90f72bcdc3ad73c7dbabbb25906f176aba29d91efd42 b511e9ec7c45f2ea1748394dae46358152232da54605d256a9ff14af6520e279 ff7c690c9077e2c03a75e4d40e9ab426b3996c909482b249fe5ae2201b868f2b 9429843e42076f5e18e4172d9e9c4ad4874548a270c7c882bd83ab7023586d41 23e9188c788613b5ab0e543e6d88e7d4c755317c94c88eddef03eeec12e4b287 c74a49ceedf55d33d429eb077b5bb106558c06b9f1eff5a9e526add820af5b58 43cdd55475b6bf9889b362c9cf8de1a1e05905b387e4222e95d2a8cf1ba61680 f424ec15ceac36c415c06ea5f29e014f0409908c62d0dfc4efe2224679192cba abcd3656b28b454c62e20d2a3583905d7bb77293ab609fb5adc8422b981457fd 01409c8d7f2a107af9108f32b0090c10014a01456cb390f2ef4068c0443baa31 3ba172386f27c0dd9f4a840b299fd0a023d2d05fa9c3dd6b00ee102020200d79 56ff8d4a75dae0e25d375a80240b0f1ea1ef6c81dd76762a80ace8b7ad00c173 923d0eaeebe1b707b8efba15eee27c847f9dbccd61de662d74f0a36a37728e81 aa415a5c421c2dab3b1f6a61a1d14dbe57cb976dbfc62692e0915bb48513120b619210d8ae2c20ce51ae132ef16c6717ff67ce016baa213286a66d85ca7a510cf746b4061ef4f3623470157dfd5fb89dc6f1f1d4e37ecdec95a61c12bcce880e949b4a206fa633a561fdc5c2f07c9ab5aceb88a284d20a43b77e330be1a64700742979ca3694f4a7e828374a6482685b47ffe3ef44ffc300041fd6667320550612896e8311e64843faaae3df0d323cbe73aadfa984acffc35a71ce0533fca1016c9b1e54280ca00f8e0740629a4b7a459e28648b98e9cdc52db6b2b1a51b40021f6a34fa0b63deaa5d6a6e6739139b53de81ba681b09da4c093c0df0e1923f0004253f1e2069c18f1864c313f47b792642e035c298186fe0fa13658c3683ee0eac6790c6d054d0ea6047e9d905c2664c5e64668aeb98612604d31af666a6cf0109e02264b73645abac33744db6fb99d191199d579c1c7dae194816f456a0d20fda6b862528559844dd3a1d2fc8f1ec63516acf32c4cb67c130c477de96bd44078ac6dcadf66225472ca215253a01125ae559a1083888bdc12eb8ae1aaa3d4406ec298900b15614018c4ba29d6997ff58d565670afe60be4ed1dcc53f16739f08bfa020c745abf200db2b47f38b251adaf725e93cbee561df1e0fb145214b9d0bae79e6b6cc08d60363d643a13598f98797acd89f09cf2d133aa9a5477d1624094072b3d4f387823430e200a01e316a048ee3908bd6632ea2ac55392317b9fe0362728e2037a89239a24c0fcfc07cff4d6ef98dd091dce684e567bff330939e0f0b9193cc74588dd3b21aa1c336538d505c32f1dd55a19bccc9d05f27297b1e05ebff44933db4dbd2d89e92d4f032932f190d2d5fa9c2857a57878bda4bbafd04c76a38c5afeededf70e30e662a823466ab225f562a521a59a96a3423f6362c0be971c7b56e7d78a46458a5aefc2c021020010134a0100f825884990287ffab0fe8ab2bdf84bccb9bb555179ea37925f0dfe0bd86c1fd31eae0c5b7c3d9846c0e0feda337e8d01525c178d2b66cc8c17ed8703126851e928bf6a3056752f09b03b7adc2f057ac1f293fe25dfb431e4652f90d2b819eaa0ab1d6ec74985eb8760dda5b5b9a05252fd6d6a5d35d33313a8207a859a1de918561751c5a901c4bd50aeae7250bee0c07dd2470daa56bea5cc0c791c1ee0793fd3c87ac8323b3e24b0edc071cfb120fd497983b627ff8f5ec9c9a7e3bbd559e63fa51c03475677a810bfafcbf907ee5c5b18754d497e13165ebaa80d0fdc20e14b0e66663aceb8e1b0b4cdb2a932eb46bfefd4c0c221b60f763593047dd99e168df4f8811fd84243202a4d8f8543176d3a381d98588ee3b69789c24da40a340c573b08bffddcbf5fd0540e59347043004c56c27e3b084e5779f11fe6747c2734924f58bd62ca68c4906fa30f735bf26182345c320643e577cf29c79e08cf509009768574608fed3410f2d7512801e90e8fbb4cc33072d9a3b56c371b778cf552dda1fc5cd0e4286fa035ee4de7bcb2ed844197899dfd1b3fdf91b2d6a2b3bbaf302e3ac03cd77a48d005a0faa35850a51ca889a80e81166fa5b911ff188c663666b1f75a65697535b07c555a89a4aa1aff5688e4b6343046e5e1b8d5cd2ee34df920fd50f2489eb3b0f7f2fc86c513669358bf7d51c6523b98841ddb437c1c3475e0456c222531162047372b182832499535c1b3762078cc2857caeb1d29d9149fe846d771d50aedf007d87867e40a230e025ed3523c737e813e4714d1b82772f9a8ad63008f95eb402536a9ea8fbb2abfed5eede18094fb65675d7b3192fcc4730a9c552fc80fc7100771a2fc43d6672775220309845e6b7a53d83aa03328e4c86fb1ce46e37a40906c1afd31d2c06c3b68ec3e6f86d0398e75843720f875642a92e7ca67cb577d30b654a8cc0a1ef73a32df8befab905accb760529a8eaa0c3a7325c0a0debca7f0ec7bc2507fa238a1d1a3a02ace2f88e7d80be7efa626113ba384e613096f9cc092c943b11e9cf8bcb8dedf71b89eeceabd441f2e74b99fb621a4408c48daf3302a7e103aae51ba00c81a23bfdbc2c092a654d5b95ee0500b0127edf9906a52c037b83efc2fc496707cc056ba8b62511d2bbff369ff7e7e48fdf99e259989a240c8f78aa916dc6df18be930e10ffa43cac44b3686ab938da33de2c55b45959560c90a939bda425e8e67be7dd0a1e550b42fd36738b33536f2907bebc972f62360ff67d5d61c9334dfe60b69fc9f7fa7f49a9e08200ab101c04a1fe871bcc9bd80fc1d866599f7605f13cb15064cc73dc0c18c353a8f8cea4966279872f897eb105ba1d516836bde9cca41e4a4f3e0f10f95b790f10674c0b69cbe2c3235c00850f27dd823b85ff511017441873c2f6004b8d142d861e54251cfd92e9dc7d98330e3855fcc7c95ee930b6769442446c0bfb630af3ab7dbffdb3f450fc990f4eb40b5a5253c56fb6b2b2b293f8f3ffc416d81f6b81d30f5eeda851764ec6e23b600abb1d4c6c4e0989f5bb16ea28fab3d8c09e8e90214a438318728b6c0c8e328700e75721366583d212aaa2c48b88b8c044a063593c77e3ad33dae21e3fde1b4b0f73df380df642e816f844ffb454d5e8db89a027633b37ba7d3cf7c2ed787bb60c5e06a287ba8fad1d54fd2aa8dde9d20c8c5317d0f4a302c25860e95cf1639d0d972f712bf8ecaf5af6302dc3a28381c4fa744ed4d16dd1a20548173ad47a830bfa05510d69ac221ab8d214b0039a89c29d891a03bbda3523d02d9c16b36e1b01233e0f5f110ca3e3f1a56ae64eec860da7389b8b41d92401a9c3025e2d2ab20de3f39b146457c859a60774b9e7eb1fa458250c2374495661a37846e72a48320f148d2766208f9c2285aedb7fe16d73b4e678c710daf96dbf93fcf1bbb53a2a0836005d629f4005e14718e962f99c6ce7b247823c5114e55acf7e7a6081ffbb02fc70d0cb7d7b493c2804d9881666cc08e1a38ba1aa59743d55425105e809ef03f152aa94c89c3039ca2d17f70b3fa1df34f401b892c3c478618fd4163a269f0e9f9f208ea2b40583f65bd89508fd06abd16cde54e5ffe358e1b7e952301205039abfddaf580613ed09d9a6ecf341c8aa98cfe7c149df9c384b1d053f844ff70506a3be2320f1e9300ddd09e8db2f90ce33c22793f5af9f972abb7769a37c3205a37e911624ba6fc30d9cb4918b350fffeaf57eff40f30b139535825cf7f4ca30b548aba9531e237a0dc6e2b766b8aa2bf07e7037de32be4aaefe0e284557c0008b4d8ef4ab93dddbf2f711c3c1d28c8c0f7a68e5d33d301786f2e1072aeb6402c691e04fa5733d78ac4de406eb6ab8e951525f26e71d29df315747a777079202bfdc7319af1f31d7bdaa777cabb536475d0cd8fd7e458e6bc1ae8a8cce096009e6d63433e7c11d49d378a7ea3d5d461831dbaf95b307ca34c674c75fdbd1fb09dda44eb7180741641cf3f7e64ab808b13465a60adb499ddeef47cf9814371c0d6150916ec8f9bd30955ebea95025327a012d83d99074b1a065cab85f9a849900239c66f13d7bac8d0493ce51db7c66da856c07a76591babc1a7f9681f9aebe0069cc1ae4d88af470a48b0040fa5e3be2e792d68738a46daf2bd180bee555ce0bc78ba00be2b03512673f752c91154964e9f0636b0e30da183eccf0a1eafce608f5bb3dcc792ea69dd472a4b8b42631c7534e62798f52ee0262ce0a3cb9cdce003ba84ba91c8068740b650eaabc654514fa0a3a87a17ca09ad25006d125b60500e01f2396bda4c123756b62a36e0eadb581635e5a8f1ed96d7f638eedf6c8bc0226ee001b8ca7065402852593aca62ddb23b18f2d1fd6846c44e01c25d339740fb2f7d792c0cd44f2b76392dfdc81279c5b1d3f1d8f79b2d9593ef505e9af2f05d74299773192f8e3e4fadba97cd45cc2c1402167cf8af67ca9ea823b13f6b809a6073fb1378430e2131ed097baac0a281fb0e838aa087093554a0a346d4576054579723fdd8c9346bfe4bd640676f0ecfa0c08b972f979eb0c7288b9c643a708f6101b6573f7d81a8d9b7e99418d7c7aeee794d386ee86f7b56480972d5a9a012e5afe148f9ad3c069d3a4c48be611316c438bf971b3a571f1652e0362471609ca77c51467654215e9b870d2e7fd9468e751bba4181481c27f6e3e63ae40760df58a29f0c10880c73e468886334947e9e23a1f9e8ef8bb50191b2a1151db060bda750f05f98b98b397ead2c47c0a228f49a5b031c67c2d8a82d31863c35b1e0e336a5e318c36bc61ba98f09d7b76451aad4048c0fbfaefa713959a3bbc0efc0afc80ccdf93f4bb521956f369b44e8b3ba32b04c795b97918b7301c2ad80e7508482d328ec518ccf13a684c342eaeac9a33097cb1e579a1e8ff0ee8985eb91109938fc0eb5a8d8d366261c482a3ec03866b1e8616507666d26c0e8f429ecb4405c28d7533db8d33716b505e8227fd00d83983da642e14eb5448f516972e00c90fc867f382ef9a2c57c07d8b56199cbc516e655d127c12fa42326fa0c240cf060be4d81e8d8c1d7ccca16754c8af36f22dee42e57689150d399e109a87644fae0aa80b8d8bf174ba97c1bc6b6ed57ccdd89754cd8db5b6dd1ff4ca70a941c1ca0f7a207b9a159e8a7e6238be31d5ed01efdd5eedd96ceeb40672d8c0e7d2ecae0746f14ea0af2d71e2e1c315f2724d42cf874b5b08d10d932f2c32558d3003d8015c023a589e506953b08b763fa3e386ed9a1f842f6831989f63ab5aa297062d0394910f78fe65a5739aeae012eb4f35edfb4ac1c6bb3c57fb8c711073f4b34f04da155f16c150936abd46c791383eb0ae294456711ef743cce779366dcbab5a0759aaf01f4bba7793efe443fca489de2132334ae5feed9c006308b4dc65e82e0a13869fc2a1e8c75c53e4c1b71c9a796597ab3379199af5d8b209e15f9b66e60172c3382680c2b1d06ac60e09b7bad5fffe6ed53b02674f4a05742d4669b1a400c074009f4fb7ebca1cb7864eaf595fa5432854f4f48077768ba67623578a9e0988d33842c8fd00b07bfd9deac45a9332bfa735895d793b210492edad009b5c00eaaf611c09c40d83b39889dcc7ee74b46b0c1a4ae90c7e928ce42c87333adb0520420375e72b6b8ff3982e471dedf887134a60c4a5f29a5d987db301d8015d0175474e6b262e33093d67be55f60b9c2fea49374cd68686b405fbaa626be7360743723f55845b6716385e34a352a9083889f336a4787a724d7205297fbb22140edc4a3b48b18c9ddcf6e7c1c6d9ac3920015ba5b5d2e9b333898c2ac2703fbc0e032982f748e0fb4bae5c02b2c81ca48552022c14d960aa8ca17ba82c87f63608aca9606ca79e995812b2f6e9c39b72d6b2fa6f3c8c5e1c88aeef96b17bf5ba082a67e60cbbfebb321ddc35229fa335b2c864098d692e7a169555b632695cb006f5266994b9e6e17e961c54f9154c81d085a8a54f009abee26eb9e1303ef2130fef7f0d64e4ce6f9d0ec43172168322f3839872094cfa20a48f0fe5a9e2ea390825429af0e72b3ff9000dcc4107404d844500e3bc3b8295cda503838e5571ec04b0756be8e6ec3693f1b1e83b86ab2dd0e7a88ed0a6fa36ef7762631a39ef660d40cbc5b8fe44d5b908b1883b31884e30771470ce8699d2a263f2ba6f8961fd08bba0fde4770cb908acf9c5ab7c4cea6a26ebba294495551f3055a320ddfbe5024137012e5d772ac51384a63a22c572aa725334e7f89afc23f7361edf4dd55d066d4af96dc724741b5f39208123ba549d828061bfcc5188aae7dc7a1be677d50f24d64a8b61a60490b04ff052af310354e29afbf7f4f385d1f509b8f1269dcc0ac2d18fe947a6bbdf8897104bac73a2e8e1d77671c576039d91c17ee4f2b1fc0167fdf96fd0b6ef14c87f832795c7ede8cb6389fe3137ca6b762377c97efcb702a783aa2500184287b509ab01a45e1b04a7454c70dbf8574a275b135514a03d0a65973d4dd734875a28c012fac2999ec09cae1963390acdb8c96d36ff731ba50126734467ead41c119f373646c30b932ed280d09eeac145084762d4eb8c8c6803137d203747c14029f50a82e0f0cd72b83885133303131649b0859386fd10ea0c0580890f699fba75a85ca24a373804d24059d90a37346c149e878a6ab4a8b50d281ecc518c6200277851984eed604defe6a32a968a73090adc315f7ac3d7ff0657d4172d840988421831bafd0691a1156ea4db63bef17e3c8e133a090aedf70e40506d6f5f526b3c0ecce35f3a394007552bc6dc455ceb7152c521845513eb0eb5d7a0540e8e285df52d9358512f9d978da616671f9ae3d3d806432ac4626d05b6c44ac6b0378cfe9b1e0a37f7a93084350e461f814f8b26788f7ee8d395e40ed6ed22a030fad63a6b3619feee7ca0403228cb797c9f12aa220f6d789e1f78016def1e8737eb2c1bb90f1e1440f92d55cd38defcd6bd6ee14377fbba946d6e03de72c474a0e4e0d04f8d709d8bac68986a1e2a57f4f6efef9f9fac13bc517104e5bbed6438d677ed43242a640242920d6cdeec5b862e464b8bf48849674e48076b6c871b888deb914e655f36211b087dbfe89f038232fdb587ed0d498df1f80f4f17de7ae9fb95dfa4f7d64f0d4b6416cf410729854c6f8802899dde6e6f8c0da24992fa8fce1fe39648d0bb211afbc0b8f869885cf6d0415fd8da7da17e9809845f6c521aa6f0d7ee4faa4dff04e5bd5a9c38592a5de4b09398bcc335da700b0224484b8586a9f190025d08143492828d7284ff3b2c4de87f4008bd4ad3530742b79765feaabbe02b8092ed5b4a94b05f733e6cb72da0fc4309556c96b7e303747a5b2521d0020b0c03a4519857318dce7acd68b312230c1524103d3b2adb0d9e1f3e328c79378ff39e4574afad705af02ce2cb0e21f6d4ac970d8477e39705d37084e64781e6d89c6979f857d70458b76487086385572b46f23a674647150666c1c6b80d69f7216a8ed1553569df55884604c80a5670f0e3abce9ef439cc0d2f82d59232c35a261d82f558b73864050471369e0ecfb0d64a650956b1898b080e346eb01719bae7d5a28ae41582ff24f10675b52126a312f86aec202480d80f32203ec57f91c15c21c6e45f492324208490e1c9d9f2ae724b8907e31d99360159e890d01a14c67f56c1e8564c7bc67d32d34716750f2dc6869202059ea69a0869812b5c1ad73f6abd8cb7590ee489998d4ff9c17507bcc7be565f02803790098840c8288b06728525410ecb7c05cf5ca40e3ed314ae730a0677f7add4edb50659872ff4c69e79678485b05c5b7a4e94dcce43f1e314aadac906e21408f4e70d9ed460f7d3c801e0c4fd2d6dc0710a9faded49e0ad80b5ec181e1bc82653c9063f0ed71e044e5bf91bd1c2fd21f3110ca1e28c884bd14883ec718e46dfd6e00cc26fa0868f075418f7ba7daa07e2b99350693a5cbe4bf1a4eadb82a1131b3000a8f9b8bd4d34a0c0e14f8256f9aa122a0b0cffc02439be5814d2c927a4fb93043884169e3e59ebf80e3d090258cd0e2d5eea3e83b4c3fb2eda415bb3d068090e129b759789c122fe4faef628293f15365223bdf5cd714bf51d7ad88188c7ae0b5aae89c64dce38467265ddd0a386018660e61e5e078f5ea524e5d5c37870cb012911ca536e5a82b12b8e03e156628b5421fbb0c9e0c45112f53047b4d52e140340c860d703c5d0ee4d5f36c98ab8f914af649d2bb19b863f1dc319254bf44f0f4e00a521857215832d099ef1e48af74295ff3fcb3848634ae74c8b442d320c037f46fa1077df30d84df6c7164b46aec5171f580d6774e035fc35c18ff260380b0460a909ae17f8d2222f9327147de2c6f59b98431b923891489c8f5fb20a670c5eda5dbdfcb80af2a288bbe6f2ee2b536cfc08bc0576430cfaf765a8ca4bb804f9802c204299f4407e64f3edc5060bcf379ada647c39b434905fab6a0db4b0026a884f455f0bbbdf8be96082982a67887c143bf5b5d1848900209eaa1e5d7206cf0c758b86ec52cecb1f17c7ddccc8fea46bd989a46dc138ac480841154c410399a33fee392c57e6dc2f457816af57719da8447749d3e8458c23d67e77a68a0e5b7443082326d53f73a696c17d11d7244086a438eb45db8636d776f25282bf0f9541a8dba028b9f177d3fcd5fca4a2969d9e4b16fa3bd9e1abdadf7fc4c310069b274263fff53c2c809345b0e15884cc90234a3267be6e83775d3a8d7d91df0efcaca11bd13e19899b71e46d77234c0a50557507a2c319ada81ef36725a7720ab3435eb50729493e23ff774539bca2cfb449ebba489028ff0c07903ef6f79706af0baf217cf5c57e0dbc83eac885ac602d5c95779171c6bbc0d695e9d172ce0e11c20563615e92b766cbb6c1c5febfc8b8765cc61a80e5a7bd27d3aeb050b60bfcea79bfdf37d3ea54732303e1507033075f41dcdc20b851817ff2b184ffb201aba492da92bc72d4652357393d4537aa5c4fd78630bf2a71d49cc3c0f4278109c4421d8974e7d6677a93127cc93a56c2413e0393fb770d8e872fcc249917c00c8c1104b72e2e6c6bfdd54b76d6467a5befafb24cd850df21a8be7344bf31ef0acebfd881b866c5add57bddaa0f4191dc10e5250786cd85555567378ccb04560f7343af5b87c7b913f45ce229388ff48bd99dcc0944ad49c594967fb05e7e8f02399b2fb89aad0af02e19c900b512cf6ddb5f578b71f43cd1458099c30566b00cc61c4c87a7a2d2686ab2d1604f28b61db51841d9728ffb57ca4d2190779d6a0b495cf26f18ce9591e55f3007756197824aac4cf79a4a4e5e5efda582541ed40fedcabe50c91d1014aa9e7c22f605b6cbd31224cce74585eb455e528ea6c1cf054a6f82d803ed6b062536c0b02c41a9c3831807407486093ec18b569044268a0c84d83a3606453cf9c1cce6fece0f0e05b7da4cb8ce6e1eaba5cf44e8405aea018bf5854036ae93b10a9cab79daecfc6cb984f09c54a1e29acdd26e4848952303d2615c2b9c2a0acb8e07ca0cedd3c17f362c3d5551a650fd2bfebaa713054d039d130691358fdfdcc69d99daad7888ba98634d917fd5b3c74cc0562b518432027cd5eda1b2a38dd2627922fdb95048104082b3f5c982f2bfb49f7cd499a2c10aef180a1a929aaf36fbd387971fc99630a9f4c7ea6451349e062fbe4388249a040b28d6911ca9030312efb616878678d8c4a157c63a4dda0e49a8f611198140095788e760cbfde44f5988cdc05001c3b40fdcfbb0f06643df211bb81673396e078515af66a2c7a639b803567d5e269b776660b85b1ba12162552e510f96b4260b2aac88bd883b9a7cd37f603d54e7e26d0d171433e22f1a1646132331f2c9300e10076f76cf9445908cb3015c73c96e4482a5722c0e9d718386b8f74c67b13d0d5617f15a3afead75c28e15454a260c2b5594b346f5151ecd00b481e10c35460e6c0e00401c7d4d3a1d84e7508bf2609db3110ea3c5d5a13d5c2fb543f8d1460eca94991741938c096c6fa21da3aafe6c255598917758cf4060afeba518ddb009339a93c003bf0f692645b1b779ba26871a38d1a17dfd2f313bec8e84c15b1b03a42e07e76f7283112b6f28d366532a678a86c5777d9b46de8c5c17ac87d7290abe92d8c63b027f0d4d665bebaa85e374267864bd5031e1fdc595d7555596ed02ef56d0298146007cf6f79c8c22d262e2a5d2f21d80ea60eee926d9879ffeae0396fef63d7273234d313c7b93ef06cc329e44d0bd8b41f7f9797d3fc70ab3140e04376eba7ca287cf1f8ddb1e31da5ec1b59778dd37edcc8971417b9f12284a04bf7f1480a10eb9d7d7795aecaa771ebe0824a9bf968c64d42b971b4f51b6860a2297442a2dc7359669ff5ff01284125a133da283627b7ef286c6c3fa7ea7c20efbb470dfde7b1afa41043fcd45395a1ecfbc68a1cabde85de26bf497d208e105f237d2226fc99fc59e166d063de2bdc9a298cb7839ba81c4747eb22ffbbbdf0bd900845e1da5deaa99728f22d3e0c84cbcb1945a0d2992b14cc886c5e31edd0ec9829147c48132d734aa31c55891157c776c7ba88ea0de8e4bcecb22853b63065bee5356078955a6fc36f94d485d2f3db75e71fc92ebf18b9180933894e8ee019207e4479573121c89c99910faf84ead5e5628e9e1a1a852fdfe229314a7a006ea0db86bc87c5dbd48b05e5f6bdba09f616f4e2321c7f71e2076e0601930ef064596bd1a6321f79ee1e7c0a5ec6daa36a9b06c6803d53bec7a0efadb97b455093ace70c2fcbcdf5b7e494183268857b89ef06282b2a03a863ee1147a27c14201929fe785d1e3c76941cb47619aa352b0ecebdd120886c395ff26806b440d180e091e7408d0976fbabefbcb752d85fffcd7c9fe9f7606189cdf3de3d4aca9aa01ba378b93a2e8f5b58c816dfbfe6606a4cb6e8aa9eab218e492a5b61ef6b45201 false -check_ring_signature 0451d083bdf471234e8a7bfa47b8910ae1c9e6e112121fc43849d52c228b4404 9cb11e177558c72b3de534c9df95b2632473c3d6d3ef462330c7885e8e7c1644 4 a7d4539134556249b471b9f86c828cd3e13bfeb9df75cfad3fd1e0adf374a55c 96b35e3c21f7b8a1cc2e0582670a99d6777df3b7030f2f32e125a4ca923b335d 64e81651c8eb3e4ef34bc15535961d6c3588d7b5ffa219fdcf6a4491a1c0206a 9f70519cb1184abeccfadda2a5aa53a0106b41753d3addf588973c75a54c4d83 343483aef813a9af8e959ba01b97c4fb34ed7a95a71cda553da42657ec677f02f869bea53f0d206615e26ada508e52d68e07a42f4a74599212c10b5567a3d90d78deba42487d0fbdc279cddcc4c2a02f4daca6445aa626a10ab1ca4be609d70ca555dfd36c8115434b6a1a8b80e8f94e6caba23c1808d384fabad5f475de9c0c5f25aa5289e222f22f6c53d7ef81c1c2966a5591530f9b5ca38b3c735f287b00c4ab740a8e5c2381d2cfc0cf68ffd53e583de53992ac4b3db64310c50ffa4f0cadfcff06b0a56694b52fc87a9d972679ba83da2f9668686c0e313799b1003304648e8a4f87f67f95607540e8914086a90a627b7dd74c3dcc4e51cc90d1618707 true -check_ring_signature b51bfc2e56e39d769833c3e5a78d37d15083cd1c1aff59891ec4aab418321882 b3f93943f298b98ef2852fc7bbce9bdd8014704ba3f68f57f9198cbe785e9582 179 7559d1f05ad958af96b45a5d8f5d3b34b03fa846b34ee052376087101f9b4a29 4bfb313e539417c9515b7b5b1d1168a85596bd968954be5d15d65fef2a95ed35 e05a44c3887ca8b1dcd7bb44bcb1675d80b8e7aa7222708f65e807e69b0efc79 7bf4aad754bfaa0ea921de0318a3b85c754aaa1461ad8fedd3fd2f769c3d7a68 9c04e698a359739a5049bb561a01ab35240797f0d70425a8a86905d7213a2b1a 5284f9c7bbac529c5edffd6042200e794fd9f0eea90241ab06aa0f47a6650583 57739a26754c5e76c95aca6a94d90e839879bc575ad818653877fbec54f7715f f05666ae67aa3c1c4d51c45ae68ec461e7535d39a200dffd6a13f625d89f9823 55de35944569ef51344f548c236e0c6c4affdfd3fdca231609155e2448c71ea3 376e1ecd4817655d71a5abf3624533bcf20f1e974f2c69876d11b7604fd15eb3 0d88a55b9e59735a7913259aea1119eaabf38c45036cfbd67f6c510f31f5b785 c8337ee52f48789c0771be5568ee450a7502ce152f4869f69c9e138a153a3ff1 341b8e436786fe3e69ece71a9e7e5c05d0cc1c34155938855f263dcc5159c7b3 b7a3695ae4b81da6c4be5ca88f0ed8e1868e0029a1e61566b3d6b91db08636a3 1bff604617a2cf851848f29a2cfd1493ae6d17503389e77c8892d34dce56cab7 07a4b8fb9e294840f2ca249260d184f6cff2d1d6a7f938f2aaff214792b5d3f8 c4f0445247970951c97efc4eb65729faa9f3f45d9da3cb01b97db180ac7a0835 49271d95c6ed055a3366d3e1729674430e64881d587521b91ccb54ef7ebb7e81 079c7fd811dd8de1ad65294936d9ab1f26ec3fa5c9b8fb0d3506d47fdcc77be6 4c575aa96fa344b8cba93299640df1b23a2afbeb8d3a41dca08111f1f732f489 2b889364f921e2fe651dca752187e0cc9da8c800735cd30abc10c1d6d5a1ec0f 62d953d7a488ec9cc89094d0542bcb9fd670b4a026e102beea2f7be467c0e3d3 4039140a398ed7b0720a5b7d39c33164a00f39a9b53baeaf270b2203b8e398f4 9768abcab374564206d6f1041613b9e1aafd69244dfd05b46fa32ce0847694d8 29a7756114534465c2d8e2c438cba6631a96c718a77007b954878bb8ae237f24 4776d60c5040c0d2051d3f795d4763eb0b9691d1cd27ca786772ae48fba120bb 560425c81620eb792c80d3d7357b28d5d81e0e78bca7bdb84420831d37abe070 e42c998d64de0baf7ce80cfea527817b26ad7a76d5f06ef1b554f54bb2887b68 9d177de680ced823ae3857e264c50fc514ac9a3b43181f23ea6e34914b1c0b64 6e42e2363b94c1bfab4212ba02322c4d6a731154b42f88aba661aadb5dc5bd0d 2e12c313a62c99ab34638117e4f134777c2308ad6874569d900bba8c42ccfa85 595489b6913fe1003a8d9708258ea87f4b46c48f2c5bd9d2987661ef6b44bec3 eaee17d8bf69de36b90476142af1d5b190302269255858f0898faf8205438e50 f8130cf427c40eacd12e0583d9e4e4a9d83e49610ad1a663d4d44e474cf38484 7597f426a191d32b0bdf79072f12cd82b6d53284a62306ba3da6ecef16380dbb 211804bac262b13bea0ffefcd3032224adf4c39fb006a26d1b40f22a68224e94 5307449f218df656d3d643e50f5c32b8c2a6fff9d4576e17f6684cfe79828e1f acd2b86d1ec47c8df656ca5c7111030c3a09ab796d1238570e6a3f916f644dbc 6e44c0fc22bdea1ab80b90f0d1c8c803997cbb0d92907b707290829df3a02269 dda39d4c24613ab6720f21c26b690a1c4f2da11686f91a0cc114bba0d63a3d6f a1331db35f7995c79c4db8a51809a439bed2b41ef0487110688dffc17127c417 0725b496010a564e4654140b5afd5f1546d36587bb2f8df166963e0c8dc55561 698a27321850074d0ada944d957d62ece5a610fd8693764b64cfc4b75375aca6 e758ef759cfb8442cfee161f4ec99ad70823acbf26264d1643a00edff34d2cc0 abbbe3b88e7e2ec0e2c4f68feeca20dbee8caaefb8b34d98df48993194ddfb04 e634a8b92d054c1b49c5703c8c57270b42fa7d111078401569dc2ac7ab88040c 8a89d3f71bacd47758a3d869ae5163ef52ddec6d946b3e0fb584e3d88c2bfd22 fdeffb6a37d6055acad3e38665fb477c5a5318ec27910e6483b4081d499e11d2 f5cfb2e662bcf492ed4a59b6005ec8020158563225ce848b7511601ae5cc0160 21eba48e1d89338fc4a28d02ce8374bf27812f97b3fdbddb5520ee57cb143997 8313a8b0239c89d0c2f9de18cf4df997dfeb40c53436134d207535c0fa405493 0991c81771a76ca28eca587744f1be0a0a2cbb4d71f0f3b5673c12bce611b8c1 fc4bdeb1806cd69ae2254a1e09d6b7cf233de77467277965a21dd8900eafbe45 29459ae2cd1c06c7401f25f7398e9a3998e2e72dee7feaef74cf0b3add0576ca 40cdd35167315d809d5bd3d8e5b147a43830c2fa8821636550df8c3580c811aa 9ca35d65f81409598d3db74576c2d0ba9965416f01d24a9fbfb7f872913aef83 a86a38e22da94b616a4bfe0ce64d4b5cfd1ef4d63ab792e067e63b5ac8bdd676 69054f2d4c20ade5ecda71a99ce175e2010c293ee70b0a41b3f71967f4a000ce 15be4b8a34e9aa0d2b0f43b34cdfc3ab40cba54ec03112b8da92be5201d1a3d7 bb099d15de6e3e70cb5496b886ac719a31a60b87ec99750fe328acf65c5cb3af cae79f932b070e503757b559fcfd920fe6a1434d191f44a4b11efa8b6a1c3888 01c9a32ab4e4b847ac81b4b5b29bbc19ba731d3c7bbfcf6d990576618a0f2ec9 4bab67797701583ea5c4a29a7250eeffbd1359c2148ae6a871ac183bd38f2ba9 80e7d22eb7075a27ba0aa487272f44ad5e0b2a6062674182f8eea5eba6501bd2 a14e81dad1ccc4192a167b67e994f404b9b48d40110ca5396219d96498656f8a 7dc31e9dbfd211001e4bba384a72dcb2ecbfa2ded5723fc98a82fe72866519fd 2f4045b19beec19c29a5ee5a8f379c97e67df7f9216b45f6e57b9b29e04b6134 abae9c485c6c0204374710ba5bc6989b6ea3276cbd2ae45640930b265b914e6f acb72bb5932b5f624d8c741e6c69228c06b4b7ed4ae46d3faaf0ce05e59203ec b1328db0671ed1fb8efce44826ffe20e682f349d502e9fe9c66ea8b5e2ff46c0 a4f3b466eea2e22abd819f25353d62f9a88dc24a0b9cc16616db5a1f6df68c7f a63b62f9258d3b17b6f49d18e8a49f45e4c07a647d4496beca584b47eb897d86 d03e449a268d07794e3ef26659a7d88c5bce7327a33f84f5b54a0b88390acdcd a679abff99b6d521d6079b6317fbdccb243e16bf099a61cfb39eb4d70bdf7d2b 4ac8d6728a750dcf403290f0e12b81405faa2f799f8e62ff06bec39f9fdb78de 42edddd980e2f984e8c4f45476ae6d137d595253e377dcdb70e6721c9fdc1776 f873156efe5ee8b4c2f1ffd450195348cff5d6a76cd016f2267205d1eb12287f eee2bbed0e8bf5c357449920558bb7f472f782c5146c3ab6df51e966a4348d20 c01ceea4764e0e8dbffd5e698b1029e7c9c5d0177ff1ec83888b0283891d3d66 6091e486723f81df9b51d2024edfeb271239910bf87875e147cd7230966ceb4b 3b8bb6486aaa20444cbaf3543f228148885317602e6f72b11a06d9395aeac160 d673128b14824ab260e87ad6fbfc41faf4b16834a55d9b24e22d3cb964889ae6 87bb2cb69939cc9a7713ad49f7ebac2463df1b6a4c95f309eaa18e33fb314059 515a9753f31384c902d806d44639f320be6c1bc81703a6146d6fd34cdca304ee 5caa9ee6a77ff220f02e76226ef92b884a12a635f410f9a2d3ee00fb68111b76 f6da2f99501d21e7c9714fa129852a632637ecc04eb84107000eda2b866d150a 0330596061acba7b120897bdae9c801bf1f0fbc4b733fc334c3fa372db103e10 462830e89c7486b18cfe67b4f219fbf24205d0dd1289e2db0c8ac05ede8ef4d3 054507fa5625268952f103c936cb7ef93031586c325267cd38ba9650bbb928f0 984a8c50ae97fa09a58c848f7872ea6da34846c0e224c53a2a1e4ce27942da17 09bf4e21e563eda5199639a83d596bf3d638b6b5701bc390c9a37502e2a120ba c2094f1507cf817dc261ed9d81a67615974ff4e6a7c11cec12fadf357529c757 4439e960129f8aa3f21e90ab087402885deb116b7a74b5ee9ac351602fbe4571 34212a0089e4519e64963059d31b34275c82c031f01c02472d0e63c97b9854d2 de4cb8b0dc1b23ae413aad2dc53378b21e2375d9a039a92a250cf7849472de4d 89d1c1c20ea1e53a7cb5cf6cbbf87bf34454331e29781a96f08afbc33aaa5377 221b4073e1ed28f6a7036842d58b768fd2315a9261fd0ebbfe4859ed1c30af6d d0d77f70c340376f2f87d0b16f8778de6468256c64f5164093e06eccb8d4fc48 32f361b3c359d72496b55fd17adc6796848f0a399999f6fc79e53c673c219d3f c8d88f6c74e2a11172aa68021645838643f69bd804f247ab4fd007922869dfac ae4df9d333f9bf2dde1ac6815270ebd16d43b7298c0267a8e60c4ad678993ea3 6830283e625162ea036387120f0fbe555e1f52146f5e77e79cd05ce4616b3e0b 2d83c48716237ed8870e61d4d011732703fa7a1b7e46f1533e4bb259553d324b 6ba6b8190f121b28f4b8fbc9d7131c3d3233c1f90b956bc0f5d695ffc6c5e519 d92d490645cfac8ee49d82a08b1c03a2b75bdb129aa5a066874c41a3a7ed122e 4607858077ae50683159bb35078b15402883de00ef4ade3ad619a361dc5175ed 7752f211a214138ea9a46b27e36e5233a23eabcc95eddd5a3a30ea7fdcb53098 8959f676956482c74429b9edcf799160a94fc0450e497a94fd41d1ff37cfe87c 7b893f9dbd99353b1cbda8145a88fcafba966ee62653b8b5978021dfde6b9d11 e5b40e4e148ede83c5f3b341a8a421cb43fb3f474256b0f8b8e5d3cf3ea59009 9a3da58cb3623aae2b3d1e0617bd62890215b53fd04ac1092b596e9cb5bfb094 0d7d622ac11672543c8ecdf09cd737a8e4292d4478bf5b390cce376ac5407684 2886532d0ff3a5911083534d19fa1817b6d9c6aac8e99ef52b56ace144ed4fd7 07d7802eef870ca179820d4a885da701914ac14e2b7628f2ccc3309bdc53cdcb c9148513e725adae280f07f03140d08ea6cb8f65e2d6b86ab63b32d3724eb1d0 bf010b875bb01227e08ac3c3908265b4717d7c97b7f927a7a04610890a301d9d 9a5080307caec2b8ed8240041f63fa0a71b97f01db624557b035df733b565953 d0cb423a7ea7eb80bbe83eda791e58610e4a7e2cc9c7fe7887adacd66a853162 11a4eaaf5c4978d60711b4b7760eaadb076d8cd7ee303f3d2e09ac910fe4567f d87c3a85eb821acc0b997586720a9a31486ca9937b4a2eb6de62f85d8760f100 1f5d76a623a6915350e65d6aa14b1241e064f574f04ffd3221de90abb5416d18 00be9202b00b093ce9749d637456907fe2dcebdcb26ace6c34ae6ec7eb98a048 50e4bf42e454f5b0a5a4ee47bb276dfae11b263db8b8c4ed639b0993af3a84fb 54de058e2924c2af489d9f816fb2daeeed88906fb8531031fb034177c42c7602 b094959f114d786422744b77839184cfff0cd19bb803cbde01779c2311265dca 41f966cec9adb731800e77fda3567813b40164039283bceb8a0e7f4b619c6f19 3cc5f9c6d086d552887c4acfb07c23b32cf612fffe30918d2fda0c582a255219 7dade0b7d9f07b54438a8d786610ca33bc952e594562501c8a0fc4d57bfb9fd1 11b9648dda11ed27dc84639a23e6e7c8dc560e4fbe7f0809729775abdf9adbe4 17d7d522986bb39f7284e9a8b96407deaaac7192ae8c339128dd8d3c2d06bf9c 5d38264c960e59a5f8f8058e8e8afc106c8591809ba61bd4ee76deab8d10376c 37dad330cdd25bcf9156bd5ac465f3aa4988aaf1ae9ecce30b4d735a7ea0faec ec3ec149b928d99accce9af98498fd823482e237556e16d65d2d28d70f306081 70fd47692c192ff7b9803f2332d240770ef2078e55703fdf9bbc3cb2dcd8aa12 f4eea28c3301792ebc2671550b1b1d75d236729f20b45e8c2ac206a0b1e7206b fe34eddea927688f7b6f42da835d8b4f8182f0d8cc8a90385bada93b577704d4 6d4cae6401a1333bfc744172cbbcd0449139732a6c76f4a12b77232546a13954 db12a9f6b49815edfb05e17f1e2568232c6e2478d51993a72d6c831324261822 9fcfdb16d4255d5847663e9131e25211eee82137735f5f37b71a0c87d5ed9762 c9157313647d62fa0967d580e1225f32f179561074b22f4ae84591487a2b2d01 fcf714cbb5d0187b6287840e494f5eac3b55765ec81043adb856f3006da75cee 4895dadcc76b7944d400b520a883932659867ac764fa0b895550eddb2d2a896d bf9bca3dc4953a0cac4e7d159842bc66fa16ae80707da789852171a1c6cbb74e a0425d80a0c3ed2fd18c16ba3dbe4a8de65850edfd05e607b6c6ba25f971cbdc 8d4cd1e90396bdfc4f7211be4d6241188e13ac64bbe984df6e3330270887ccf8 2df16eda0e43285af892246857a4ecbbe056f4976d644ddecb24bdafd04522dd 0e1d0fe5888a1345574b073adf326ec4fa1c0bdfc53b1c7efdd63db1e4e7710e e87f15df03e60fc1d137b51a835f57699c91cff60c76ee09d9117e575ecade19 2510a2f1ff0b222843478321c9c87d7ec256c74d8be4a4a748350991de67ed11 9e8bd22cd61bb062f1609c00e4852be6d60ec36f1463d5eca4473e484d17a4c7 39a3418b65493af75520bad131aca55298648e548877587f2d81482852b90c27 ea0ccfe84c85281f6eb876d80db2b2a28872653369482ad2a986b01a1fcf2f31 ff75693ee337fa456cfeda9b5b23edfe87d2e20546857ec577d06197d9861aca 10dc2647661695a58ecb0ec1487538ddb63e8e21112694fe4b9f6fc3eadc5d85 6bce44beae8563801ff9a4bb6aa8226d14f11d30d5d5fd906acc28e0e2232684 a6f95e88150a096b1116bfe54a21cc14d8fa706ea05628405eb48e7fd83d2c9c 57970c38f2f0febe22585f4575fa55ae3bb3fc6e579acbc2e1da9978f7546343 6399cb2e92447063a84201935af97241ebec91fd5af4785dabe42d76c76de574 08170611384d8698d64047bce08f44ce2ae6e05cc5443ea4b6e1bc04a46e798c 7a586d2f86702b40b6d22fbc8d7ae1f254f6eb76e1afef11f16951b4ac586d1e 56aaeada86c10ef1f3a51cbd83a84d2de16391014c98ee328fb16f069eb3b5e0 0d2be2fa25a1de1d58b14c2bde62ddcedfa843423b934a6805b775913a2343ed 450426228bff91d1f8d782e21b7e1a239a7214ab94fcc2896792ccadde71a862 dbd5654208028809bc74ac224958f12a66e93f03efc2c91d42202a19ea8cac64 9827e9ef2161587cfbfa6b73fdb942c166fef398c99ed5cec6ba267fa6434db0 eadc885b11f941dc9d084a67d41409f6e4df20f22b605063753841e96b581bd5 f5777c6071b60dfa535b1f668a7a6b5f35f2c0924568a07060fa397570340e74 1708fcd3a5f32549b773818df7c4b8d2e59412264776256c9cf5ecf3de923b10 70a7052b60924e89f81c6a18eb1a3caa0f21ec3f27ea3b9ffe8d629f0aeb0c92 4154e8efcfc83d80bb7153c91eea07da05d05e8a802bf20a35676be59b2cbac0 b66dac31a49268ad87032404dd0328d8001a2a510404b83f5dc2cee793b3ccfb 52872d8c690fe7c8ac0783b01e2ed35105e18e377bf202045d37189f8b367dd0 0f675609ea20d487bc9c361c6fa0145e74d6d66c9df631b0a95b169416c7cb77 1a40dca1c30e45591db954ebf28e740072ef84f5dc86f1936acafddad3675331 fe2ff2e96d83f65106bd7f02e3190aaf1a1e31349fac0f39a15fdbdeecd8a587 62d8a66297ca69c0ea8e80071ba1d02cba8519c0b0c582b7a0a13c3b73edd833 791363fb75f2c6ffc92f13831df11ce9d0e76623041628bb31944650a3d2ee79 3e73ebf037b1ee22914552ad8d35872a811fa6e7c8b32e12a3d077fada30973b d1c083d9d844d9a5ba169d813dd4253836f60ac14a74ef97bf173f1cabdf718a 3ef857ae803b57bdf2a3dbfe50d82a8fce9c3477002ec1ea3c1e7a58251f14067255ac42f9caa2865b9d8839c1006b272b04927c13edecc5e4cf6688f9541c0953a2157ffae7a9b2a5f87a75c1882e17977dd3700f17f3722d1a574e19871f06e9aea324922f2e21e07781686475108afd8c5e586920f98112eda17e37030c0b653d45e081689c59d33bcd50496feea06fecbd0099c65389cecfdf143be00a0de56d350ae89e093c03349917e0db5a918acf44315a9883609de08a94ae3c270e8c651304a3f1b97e183d2c4d2e8a5014b46bb02733662ff0032c7eb484fe5205e25b0fa6a8e2a75f3cb72eb129ce90946490421afe9390714f83d200dcb94c04cc7c95c5d76dd498236495c324a4ff9adf27420f4c96ba74fd2f50c035560f044c98dd0fd5b9642ffcd040a11e4518ef7a9f57138e0bdb1d8d2bd34e269c4d0f422c09bccf84b620d05e10c28fc4e26f22d0fbd0c67913c5bbaefde406209602483b3244e148dbc96d9a03de5823bca44b7b5f6d24ab3c1d2ec78cde8f855f0d574b442cc76f01498abca77bd9afe405d7748657fe5b19d5ee074b0467c05d009cee671dabc9a41e3efb853b1fe6049f8fbb311c2310ace0ff26aeaba3eab002e48c160e6cb0f416d8fff3cd5302db803bbcaf6e912d3eba4ef2990a179e410f34f7e99037b6809e36c7fce92bfb31db5035958ad89b0b6b8110924e2b592804db81ea5d0a2a3ce8e8252a82a9380c76606e501a956219f9e3ad2727471b3b077bb8f1f9337bc3ae12761e4a817c5da9cb4776fcb060414b33a2a905f8dc7204ceb73687b1e5becc3aeabcad4c264a993c58880c2d27ef7782a4e09b9102ec0272e11ccb6b01171b733d6499960980b7a5d7e4efc33556212fd2edaaa68899073c9a26951f8a3761781ccba1ecb02baa0d3f0e4a845e9585a8dc638345729e0b56ba909783c732c281741492a7a2c30003a1014b9236c6f029d9e8508d83a80dea21d1f3e4829a732f0ebd033da278dc871fec21438b39d720777998eca368016bba14f29404cd08b98c0fea909100bd029c7fd832fb2231f98103d7358df60b8e42b876928520ce6f32fd34af6eee7b88d6c4f47735fdccda698c285dbd4c05d0fda2a2b1de1ed8342e63e1a87c2269bea114e27040dff480a839474f57fd0230c3a3cdfe0f4e9bad8e514e96462d0e7ac1c81eadbd8df12900162501915101fc53020172ca20c9ae168941dbaa8fffc6d057dcc3809364eb5de6d41125c10405d3e67f593a9b841f33fe9fd490bf35fc69409991639d57e2def3634dbac809a1c9761a6151e980ce32ef10795704d169097c0f5ccadb6c5074925ba403b40a2067cb3fdd7bde045932a9600b3a5287367207329372d922e15b848287a902021b83f3850df6e8f10e147a43c7ce9b06780423d538b63d19abe8cfc896e7c70f6816bd59c6b51ef11b7abd0870a10e3a8c79c2870e645e97a3880eb16d36340707251159c0087be29b31c5aab93d53fe43e6f1e736ca73279b9256328f129c0e4f01d1d5d39dff5210c5d7d41fa663d2597a9db6dcbe0f780aca8e1b77a1ac08c7b012f7ed382f09ccb6ae793b4e899eacc3d7784b72de2671cd08d1721a5e06e64e524fad995d49d8ac79e5a24eeb5e5a4b3f9a16224c8866048bb4ddafc50972932ecbbce61d6edbce02a46f1af28bc2bb07516eedebf8411c03d8deccaa03e525395f369403d9fa5565e9647df0eb59a285489d5fcde30600ab230f06b00a5cfd2fdb6e126463438d172313f5efb0d9443f7c7a7d88a4f3cfe7af1ad48f05fda17c5ae833c779cc8de137bdc231a70eee61178603b048571ac08a659f650c54df95a45a971d956b83ee252194bfce62a61ea66794bc61ffe8c3f5f8f09f0be407f82ac8481af9761b132f277a841fd05aa37cc02ab9faed8acf9815d3660d9f8fc076a30b1fdc03aa3fdea2c7c033cefe1c2de494cd7e4def0cacab743b06636a72c16148ecbdbd105021476316fc794d740724c76903350e743f9961460b80360fa8340159e582fca8cb7cc36213becd822375f34fd121000cee16075b0b72222a9f531406b56c7ea76e96a339a54f636cbfe01367fc2cac9d0502273c06d43772196b45644f0e0157b6aec4d24e5f8ced29077d3656641cdaae95b27607bea5900bfa8d86b58170863d949478b9d27e0b4a90b909f05270899a8bdfb6053492aa2fc4ef1699ed619de6c2cfab0fb50e547fe801899299a3547e977d7b01625b15c9b511ea88dd862b809c0c0e3e6db2b2768d35e77bd56974ced99e32018978563af9358d350b6218f323ff1455e9f50ed3e46d727d3eb644d7456e07052c1f4fa2307e0f105459f6100d977e23eaf4ec0d34ccc84fdc99cf91a873aa0dd564d585d2b7358f1d85015ec03b72c7c50258fc5106c7016398ee48e8ee8c02c708729d20619a7593029366cf49fde5c7cab9cc6b9f8a8ed6ab2b5f07196b01cea2d257edc9690ccdce7781f8e684fb557218c4547a2a0284d940f8c75d3905bf6f043787be93df546e427da83868c37aa5b5d3f436bbeaeecb62d31aea2a0c20abe415cbba6ea03a944c8a176938898155c2304ac3838d5a5377091ebcbd092eb4607478a518f4f98b687ffde7597147263cb998c589d69227eac08136e60f6f33ea5e3f88891936666af0492b0b3d1573aae3cc11da71e82c1ccf6b4f8b098ec6215353865bfe9ffe025702663cc79412eb01c654e6a79e0a2c854ab4e903ae46439b56a7478cf0fa358bf4702df10bab10b2dad785774c421651a3d92e078fc76f94b1e7e00a44c5edd1480fc00549e2e77b786e9d8fc131b1f889cfb806b37ec405297ed74b204d2303473d4eeffc963ac905844f88c46f3ab8996e080f5f86d08ac032e7697f4c3f9b01650adca4aaebf791521a397879bbe2bd024a00b3d532b5b22f8dc17207494863a20520aeba2f9bf84525760d3a0bc077efa904f9ae922e7c383b73172622d67b622cdac8e93d5aa6e7375704d418b14732880c2bd73d91490a5cf30a429690dfb471b7bfb49b88806af72f51474272def58603035b76345cb83261358461da237673fa9939d915bfbf59d5413bebec66567e074976f70058323699eb3bd4a4a48d43ae8f1466f69663741e26eb7abaf71511085ae0eac6edcf77df26dad14a8708cbecfee0b3d2ae090ddeb3bd6a22a8adf30bdcc9ddb92e7fb8046535c7c30858f35f7656ed0c50a40af97d5445e2223c7609ddbde7cb03092e6500965fc4b11412d6e62d8a788a54040c6c6360499b53de0436cfb05bf49fa01e34b0968de71c04e361c866beb0258e83394088ee9724ab0a42771d746002c1a52e68849c03d07f749171ead378ef74f4a24c8869c379b404d964bf42911560bee4f3ca120dcb374f12fc4c08d17ee532f6a65b8b7e3c280e2a6b7b7f9927701a0065a182ef79e27fc81db8f27b0452e594e2aa98f94b6704aad5185cc00449d358015ea738f6e98c8f7ecf069bdb761ee5c1909d9aa0f00c9ec172e806727f8d75dc0cae1ea69a932a14a61582df849843eb83af5c507c0297bef2ae90783647b2a48b5f014b2a6ce0eb0f58a16dd534da4def0c8ba85a025690c05fb94fab9c0d7496a1bb6f792214ef453e06dc067cf18b7dd4810d00075549d08d23b2f417e192a2b608063cc881a74a5a9c0434b9c016b5ffe90f7a091c6ab180b4fc9011af11984f4591f022be2fb8e310ea75b01cb526416231c100373d0d3e7378750d75aa694db8f050e72bc262d465bf942f261adc76558cd206330b8cbf9060f2750490d0c1cca382d96c0b68e1cc9f61fb4a508f7570f4290bd14e06c95882f0d92630c1abe28cba66e47f566290fb1432379bd46c5a07e90e4ae600deb9b31bb6af999b766539baa4bd4c7f30cffe1d6aaf05b0ab2e56e008308677011b1bc02412fac152550f667dbacb9482ec926fe4e67f07d96d5bbf08010bc82b76e31be0eaa0adbdee561d5095c4d531f307ccfcf82fa8bf62d9dd0bdf5762fa36312bf2571b6fdc78e8aa8a78aa8da5f9403804dd7410ec759f4e05c4731b69ae72969ca8e0893cb50c9c8e33c51d4134cf25334e62e9f6104aa808bdf5459ad808b0bc957193353833e5506db8d29e4c23c49fabcf55991225130452086a092ed869af001b2d7e8b7f074e0eeb01d0e3a6327c79ea4dc5db2a030aeb420fdc83465c77bed6c2693e56ad0e6013a060afa82b562406f9e50944d502e0f731f6dbd62769531b5e90ee596125ff98174a1a735e11f9b6c508fa1fc20a9ee25dc9b1b5b0a3deed08fe87d484ebc3029b3ff026b9e76643bc7909efa90966ba297780f88efee425fd7cf95e7d493945ef0b09912f231f8aad78c40ca3072b304148daa75722ac9a3d0f0783e9e2e48a1a5f2e433cb6093f4618ae2f67067a3f5ef26e1fa6766826617b9b905f53bf74cc34177ab283e8ca048d9906e20fbdb78ee8828b5cc598c6fedc16a760eb7bf50827b0dbc4a074d42de113208703aed9b28204589f1d6efd726897a945ab8087b8cf808be7c58a63ec14c99ac20ea41d66b318db65f2d66b3f05bf5ebb1c8bdaa2aae850b4e68a2fefdbdf5b5a0830354932e8247cb012c64d878e581d7514b8a0f911f17a069992a96bb71472098960e1262ea70d9852862ed8fa5575f16cf3e0ffeaa78ec328c6a940868d8e04377566eac2220dca94cf6db5748ca25c33c2587bd9b6df456520629237f16503d0c35fe359038c9c19d19697d8395dd888ef5c7a0ce9f0dc40e8e9809304a209c10f3d8d916884c33eccf3e923b0f2751d77be11b23c2a4c1402f4b363102c0058a23ffb5ad3b62a4d0124749cfc28d6f12cfff35910e725aec2457feece11044907fc5fa6e661be232026a27fdfe518004be896a4da3df12d9a376e1edd3d071ee5018eac2bb2e354fe98d70874e149141793da4b63608e423a5e1b8d61f3075412487e97beac76d8cdd72f9049a916fcd3e745636691f957b8778791f898000aaa1122d4fd61caf4041f77e89cd63e2b23e5edce43b885a53769b4d1fcfd0e8485ab60b058fae615f3f1213083ffe2a5ed24b39f4bae6229c007de32c9d90c69c7a569b006b917b42aa860a03850e881fc59ccc9547bc8b0c9da0180b62b01eeb6481f822f4e2c2188ab936ad4560208ef8968ee89df4afe3787b29e50c80ed5a9fc0c15f66987991ed6ae905796b439145d9af1142da8717ea16063f108089fe3b785f867230529d22ad9a614bfa1bf8edaf1df9f8b9ffd32ff395c1cb2088e606c3a18534feb46b7e6a97fe90017c7f1e15d3d0bb92304960c43cd3fea0541f3201ca1e81b0f99af383b6d12d8fec5504b71a3699c9da9b932eb3a6e880e287d2e79dbaa0f3f75e570eb98892791e60de489df65ac66b23a1a4fabd90d0551a65a856823759a711e6ceb72e773cdf4e71a0748923452c633077dd6321b05e3c0cfeca9d2542ac6a3d0f3c037a64f3c1998113e5ab81acde4951f411f610f246c84927813508b6cc66a85b3cd9ab4e7c91cc2b02fa05cbf4e6492c2561806a7269cfc7998916858da2c74731c8402ce0bfb4f05b60c3776a48319dc44d402d9bcb8919bc32514cf7e5b37b41a9fe7ea42602caa35a629ef8cda65d77e3008f561c7b1427f75a2f94ae3ef318b56b75cb658d326829ffe9a1b54a1adaf79083f7ab13afc6685d87c5a7e27f0bcd54c417310fb1e99ded3b495850448f75e015796629cf6d8651ce57520902b4b4ff402aec8075d3bc8394e17bc5be3577109f2e882b296b110c0fb4882770dad26ee7184d1469d8760bb8f4345a0dad69904ed72eb6b3249a953b1e7c9606a03ae3ab3bea5f327788b6461e8abd93e21ae04c1dd5100bca7d3d712f002753e082323f8be7f705bbada7abac6786aa48daf02a266e06f553d39faeb3bb51b1edb258167a45ff22b64fab2a3f60c7a65a8d801acc6a66ef59a4ba37d2183de9dc3a3a4f97862b6d4019a92b26a3d47f0a814015da171b989a70acb4507b34ba28e898ead79840651be9240dac6a8d66f0dba0a17cbc8d9166a93d848ab4658d7cca2c014beae14fe4e87c0747dc8dcb7938b0d7f7e557c2cd392c6c00c301a4a0eadadd5db1fea8053e564a78488418a9c690e82db66ff4c6068432df9c79426bfb21efc4c74e78910b33733a024d8d358b60f1ab390d2b9c2845d071c487bbbc5bd661e07edf49bf96d7ee2965c97f4ced60069943822f2af71ead66c9c73b94945362642afb5d90d479c066711e40bb33f0a069316c48f75c57d119bcc8a729562d1b5be0de12ada4596dc41880933068d0ec94ad7d83a3d59ee150a9460c6f681b305d8731a7c03af35d1b9d6c9f0b29806ae2a2670832afc430606ff8a390823455f5b8892e1b702e027bba0260e7851052a910993fb03fd0d3d7336c5c974909ca2bcd27ea72efbc811adbc27f0fe6e08fb2db9ab28f8e516e3d0400096301de997d5b94b617904bec7a84bb4eda561053d4936e6b69910aa1474865862fa8a45e6552c324f0a531e7f0b6e16b794e1041b8707dd99f542880eef63051dac887ac985767b8a648ed6c9675356df224d0dd48b8ad8915aee0a743c6881253ce26f427239c7f5e8c3ed3f2249bd6a57760ca464a8debf580f0d742eafc5e8883d5d6b7ed338d6c68f3dd9f498eec2f0f5056c86560ac28bbf7f52c317a2b55d23f29ec50827d56fbc0d85821b2f6ce83601db823a97ad780f4b274d503b06e286e8fead9c2641329d3ebd509d4d7e89fd0d9228e5ef0b703d14473dc14efc917b158fb6fdba27afd2dc7430c6290dc90f01aaa7121f1cd29f992ccf3121e102928c5d6578697abcef2fb896a3766d88360337dbc5c4a106b712ce725aed29d363ff7e5621ae2ac9f038640f39b552f7d50d4e515c7a61b3c25b3c1e44f89c46b8bc173307ff3641afbec2b0d6937b247000c1b5814afe613caf7d6ed5ddd720efd24481d3df52bb4d8f876b3d273c45750d93133139b299c98a5e40018edab2d2a6154f67c39faa9b4a409303fddbccc10eba8949d9eae6668107728cbd025ceb3f172b8a86c7fb35f4645c5de38885c70cf4952d75618ebf40d0f948968da6c6db99c38d98956986cc72728131894a8f0054984763fec72ded9df336e2b4d24dbfdf930ccc6cb2ebe4500a0384a3fced023f8e3f3e161dec8656c0f456305923e1e2512f389893cae112560a0d0c5991017111f37fd341c0619893ef02b85a13eaf208f34ce2d228ec9364a5a0ba837304a83d769f3f517b9ace7b0ffd31e8b08fae716bcfceeeb73f20fbfd6a337f450ed48d5dea74b3c4db03edce036a90f8abd06b644a43ce0a1bde70d1afc5fb0509c3406bc1e297f7f87e199d7e28147ac830eb972de07634ab904ac7983bbe86042513a4f81a0a9a5a8a9ff7e015ad96870e8c32ccdc015f8b8b6347b8081b3603f231f629a4db1416e4d14cf3c2df8bb193812e07e711b08ce88831ccb8bd88067dbd13db33432c27a66aea9e12e3b27115af3a0873cfc85ca0af2ee1dc1a4003568cfb067d8bfa8c74bbac22be55aadef165de77ac41322a6f0bf559c3c68d0fe5bf471bf24d0050eac631ac2f5d2da66c4dd90f9d81b4a8bf4781774b068807fab67fc2374d03396d2157ea0521ddfac42a6d63d7375e99763724ab206af60fb0fcd781a1c8f035e79224fdbc4917975698c96ea78eaa40ea9023dc53f49b0c4cb03176870ae5f348d29692368ff8784c8e3d550116960d14c7b62b54a5c80e1efb3eb4ba6fc85ed232b61e2392b9fb28bf7054691afd07f555a80f928a4e0e156e903475cd0245c09aa679117c394af76ad4de7f99fd03c51ad0d7d00b3302d85881365790020532dbc670ef5059ca4641f1a88b2958ce0080ac6b2c88970e58346e8ce3555aba8f0b0d466e2c0896a514bdb34b565ee41e3207dcba717f09a6322e6759d36b27f609520358f28ce91acff5be60a845fdfe348e3c1dbb5802de63cb8edd87a99d23ea2fa7b832b06d66915b5cbd0fa9b1dd46f2f52f0fc60baf9a9946d64d7c3227c69683f883e223af7c026ba2fa8fa86e991ae2a134210e109d9d9aba9bad760d912bdd997f701aeb5bbfa5aa5da2f7dfb0f718445e0d0f343f5a3833dc108d2e7265dad873fdb21dea182fc37df48bbc2fd9a033b23005706e694b15e8cb2088e1356be930a2fd27a999e35602904722ca154dd27c170897863b27cebb4dd9cefdcde6275b65d870565e29a2a4a8a612d09fd4ee276307bb9e98276649124372b14e94a44ac362bd013277442170fc69c1981275853b07a5687727054104ade4d2ebcd4474eadf21f62cb8c2274f2773ee12c70987900483cd3ca39a7a3102e872d074b1a8642ffd04b2aab5faf79fc6c905175747e300b7f1a8f69415633c3999d962980f3561c9e1de3a26254a5f532aa3a464199703325ac524d8e6f42dc6a0d622207274c8cfc9265361845acdcf495ce84fca5e00bea6a12579b87d2c81f651881d96cb041159d4874755f3b640874d7c7bb5e2065787b2134ec617863d32e58839f84b06141e37e288af44eecc06d94df8523508a245715661e2aab6e30017d07496ccaed3a43c1cb872e489fc2964c034891b0454ed091d436c75cac49e63b54746cbc20e897ef6b16faeb24660ce6c35eace01103243e8ef1daf49351b63bd0255c7d76d7e8922338205d9c073ca905fc56a0bc676e22b4708f435a9f82e0b95627398c58da3cc8a2bbfce9d0ca7723baa21090faa3c608c510af9c68a176918bc4e38f03f0965e66e6bd1e7fa413f3e626907265aaae905a9db7c5c9d196032515dffb96963a8c18fb322ad27f0b5309c3c05c266e9b31fee89c6de796492fb2fd9fc5bb1c970e5722378106ed10b9b91f50a4ecbaf0d0e28d536a6724b01f0b19655c12625f45db22528837ed7db424ec302da7969202ec0be778d48c5411ff82fd077eff22614e56fabf7fe1dba9cb0440467fd45d51f48486be07f0a4c6bcf5fde8002dbd27bd8ca99adcdf667c7b9ad0bb768ae3312b075f920616aecdbc0e6bdc4c0a63d5e00be4306af28738b34180cb431a9d68532c6b3cc58907ff4f8388124379f84ecf72fb1e9f6b33080888e01236d939d7de087b9412462e33e73f03aa9b04208d11dba0bdc23e7cf21928b006d178193fa6650bb2eaf7b9ce20b774f89c207a9d7ef03875f8fb3a6c8b4e9064a166425248a48f3d531bf0f95020768a1c3c8b4e590c52b40b1218a5cd16b0f5229a59fbb575cedf4fa36a5053ce4b6b3d7fb64a272ddaab8a464b095e1310f09cf6432e06d25ae1b10fcd30c41670fdd4082b67c6316c16cba851fd0bb6a0a711eef0e8f9f747e887c27b032e8f8c71d6acc07e6f767deacf34e44767f1e0ddaa09b581d16a2556d39a0e6bd8c464fe9afd20cfbaf7193fd20e8015bd4220ec24c9a299e1772ffc6f9749f8714fe8f72c4a11bb9d178b67d3feb9ba5d9ff012856ae7f84be8a54a9cf3df2cb489cac0a18a23315848838715c9b54071eb6044d0bc714e50850bf87e92b80fde4dd20a975c21b54d3c02b61a2feb24e986d0d890488b777fea3823af699e651e4cd5a7b83ef6e2bebd406376cd834ed59a40012609728375dacf571127cde3533d10810ab984f575aeb5e2a1e49dceead980dfc8b23d2df2838d4e514c3431a7e75ca020393f813b9ce1bd441c834ecbf060e2e7ba524c792567e108b0c3c654c45b70c51fb609b05e773ccae1c1de670be0800a23693ce30211d1efdfa1aa5191599c31f044ad41e31ef2e03fb2237af6b03331a602ee3362bcdbfd203f89adfa8c9a7d5da4979a0895b443c25226317860674866ded40ab71cf07e646b8f9d79774902513d403a5c68f94fc451697c2f90b3644b5f7f3e352eb6ac5748480ef18284d749385fbff26e396223ca3f8cead079f6a94ae15087954b41115efc9803ae84f0f65f4407667b3ac9819e590a43c0543c10923ad7976c7787e6f327188136067777aecc94f67d3b35d4f5f07ed980cb3fad60497bfe71a919e386e84a550ec5072bb8528ed1be1c0f4a5e48b4a2001c025cfca3015dddc67372d4cafeac0bb80be1824b47ac594759bcea28d5be60e0846860d70223cf3c9ace14fb8ac7a1b85072ef5041e4f503157efad0340870ff0566d6b87aaf714d420544134012de226d3e095e4d947fd12340991ffad5000dbf7fc018bc94c3657ebe570acda24e3dbb0e881a73c8e37481ecec3ece1040afa63e72114c8d2d25b9afa0ad4be05667dbbbb3ffcb78464fee7074c23db320ccf4455f646f523988415f137905e7fc973120f7bfc4e1bdf1f6ae67a7c99990f4e0b254365c0e509a96fa6ea7a7aa114a1a399f4b7180939269c64f6c4a5ff0453e4ca38474a58377de248566b29925a9c493611d174680b0b5539c32de8160bfa975c4f3e7d3bb33572c95379930f6e5e4cd61416039bbd61dd20913dcee000bea377b4b05cbaea836fed6b428a71ab198d381a1063b3dab01e1e8e0f3ee6081642156843f0eef87d5a6029c6eae45482c0ed84067e12b745d0ebffd00cd002f8ca68c5611b8497b4cbcb3488ca4def3db170a56c1113fd1c6f83531c435c027b4e0aec63dc0b7009b094d8246b4e2a95cae7a96c08a664b86a062e50a1c60935299f6e86d2dfe8f77eb5781981c940fd67527aaab0c65b3dd32d2984237c0eb9b07b5c64324b5d69ea09766ac4d9aa0ecba8f0bbcc36196fce5bc17fb4540673e3ab1dc142beeb809598b6eb46f33cb36863910d63093215fccca2762c6e0062b01210a92f83ba51e8a119eee0843d32048a32d671359713b9b36923d1ae0f5829f6e0529801eff7a16d5d95e8c38ddbc14e3d6bd6c92db318d48f5d110c0919644e3e84b3ea13b53165ef4be72f2a9c3bc44f20e96a50762d15fb553b6d07a6ea49630e1a5476fd0bced483c8f8189ab62e653a43d6832fe0a95fc463770f3464dac6c07e108812d398dc2c431b0c495a3157a9ff1e040611e7ab07c1d80227e18291ad2dad28d0f28aac1cc45db3b9e2c5039736bc5a75c41ac3a699a404b59a9e5b1667097656fb8c0f9499cebb320315e3dc32266a058d9c302a16bc06682fea86265f652fa9ab8498d2051fb3315181e6f5dfdc479e823892d5b34f0f0c0355417682de92dabfe5546512ee08f02c8f133e1c0ad60ae20cc96eb9430cae7ab4fb36e3363fc0bbf28c3ce7ce34949b3736a918b224af585981a53f2e0aebf9ed48a4243c5f565d279fbd3ca7277f2b22a5c92e211b78ae8f79da6b0f0bcb18c86d1950cd053a406f046adb895c98af4bb591ceb282bca759a747d23d0e368c5cfdcebb78e0ab3e73133fe5bc60b0b3360f32a310a2eec95e3516fb4500173d12361782303943a2c941170b8dd0be068442d51feb51c64acf94d3e91804a986f94e7027235b2dc83f713cf535b238387932a1d94dd69e96ae0194925008287bc19d4b9604e39ebf0ebc32b7de776a5018de1c6edc0107f0327be96e2204f2426e08533a5f115b9c03c93aa3c547db1dcde4ff572eb5063915f1da6dd902e6109e2439f78acb3f6f7d46989db58d43623e1d04844ebb40f359309d70800e91c7011895ed0e668c1448d691e1d13b2e46ff78afef04f3448cad0fa8c84706293fa395c0e1c1c0bd74b16bcf27727dfe879b43f94d69d773536bdd63dd2d049881e527fd6875c5dfd1869b67e4fb475e17b13023b7116baf5c0e281ced1502f188cb74396c54d0e5ca3394a6bca3bf777da21012db0abdb906dc99c83b430cbc37fc02bd6358b096c4d4f4de101589c410c5ca6e19074bcf9dfb091bd449043e3375cbc692f8f9973a2c082fbf26d2f64746380b5b37d538a5fcf2f3b0f507256a09cd90c12a5aa6a2f43686baa4578d0d75a74e7e836754ec1e01dba56c0c0ff05430f51eae8fe40aeb056d63c24afaa0104c051edb5feba074b6dc0fc40497d88d3fbb8e0ad50d67abab21ad4ea1f6ab0ffa2ea817edec24cfb89325040dcb43c21ceb4cf2fe0ac401dab0a210bde7dc4c4f95e5b611d7b36d3953adb008de78c4d0129aae7971fe5d54065e15ff5f2add8a8de0c52df60ac6360fc8d90e80878456e87505c0665cfbf0c6984439754a186de0d4ab6eda1b2940b955c7030871e1f337e4e2dab15fbcd2870128debdbf4a57e5f4f5bd9c6611fd0475cf02de1076ab803c966c14ad193df3570ef74058693be9d1ba4880890ed2e6ec9e024efdbf838ee31d3865d1f408e36d2c4ee1e55a5f182c815bdf83072986dda70c0e646a9b137c96d1d066266b138d02faa8871b80105e50d86124744e085e620383c50d210a389c01c78d51193bc2e48fb3b871aa0c6f183f109ded2894b1690c8fc14c6b8fe0468b5d25d2b454593b1cf99757cc5eb402cf3ca124d8c450a60d0a0685091154d6d1c1fefac52f5204f3747c0cf15e5303db73d90b708b4159033018936390fcee696083bd1f0643d7f324e3ab90695f561c4ffd1c658f129203cd15d47bfa84c3396e61a6c6fb68a35b940a8056d75ec8a82ebd647647958407520c34ab2a1dd0a26a1edf58b6395964dec1f03ec8fac68234e1cee319e8b00581b6fbf08239c0814a1afffcf30ff10b2f2810af3792c6f9060a5b49faa4f30fcc508082864394a09bc74a2d1582c39acc66624a895d86c394ad3d88c93e8c03707f1e8092d911bd6b81c1382699484288fb01b4f1082b4047d37f1d833eb9043a984e1265ec17e5ae78a4deaf13946e3b6c907c901c78dcbae493b7dd1bf603c8cb244b5e5438aa04fe421fd0cce7060e2e11500166acb2a4c6cc93afca060b4255909e964d8c2e6e0a48c6fb9a0123212c35b4988fff79d4cd527dd46b830ea1842e3307bfccf0a7aa9d6b76c5901a48582466da9404f9590bf066b46a810a94d557476226e0e62bb6a6593198fe8fbf5e8420a7dd97df55b801cd20ebb901df9ef1bbc1c29094e4e55e963328fb90552e2d704840cf722a1f160fa2838806dfaefc9ce6c672a346fa7e5d52e5112e73a9e36a72a2de57f8b5f1ddb0370c06f2ce9dc449b24ca0b6dbc1642a18822aada47809119bde591f1821ff71d3d60f41c9d63f04e90c3280e090b4b557068d5b87d973a6ff406dbc26bbd57732940dc2f8ec334d0febb34fdb2d4e1f8193d0062b4e151b41c988351be34792461808785d2c49f626275d5307ca1ef51c1c887a5b06285eab8af2e61e9b6b24dab00ed9864ffe9049d40f1a9560e833ced4d9137edb06a979ae472fccb582933a17043343a44e4efe9121debd86df7d45bce57e14b503983e40f3af8656b15172c204e9ecea3dfe11beb8e2a88d906e907b17ca778377f0668cfc753b65a7a8e4020d449c55e7486bc62efdbfaaaee60d035fe3ffa8aba2bee11785a179a5d642960ffdbab3062f44d79e574c6bb4600b177d1219fc7641410d0cfb1c6a1504229d0223abe0a64619e4ce92c02044e21ceb5d99dd9375e0840d3c143e6de16c057103bb564ea131e69ecf3ccd5dd88af6e60a7e1278a21dd29983f876efa71d138e0cf8b9503ed377822e8cd9b49c25c750f12cee3deb3b935c71ea7ec3cc48cbf10e8832e93b2835eb38f3a56fea5ed48e3ff478255d9dde14d62e2905ed2065780a96877d06a53099c07efa13ee4b0379fc2e18585d950d5a9ae3d8ee83012ca302fe54143c38b8515c369c712afbeee79ffd32f9bb43f1c41659bbfb67d84e200ff5dbefbc6925726034ec104694cc89d5c1b715e8d13867606f660c4866b04405e20cb1a1a901c472f50da399c6797461c2c255ef2d466e26278b7bd397258a05805bc46ad275053ca7744339f56e8e471517294dcd1703077b3b9ab347b8120b2f42eca939431bdb16cc4fb59c7690a39dbb98e76d10d09af80d1ad391b6920a359c79f52163cae766d8d3f3934d16fafb8462ee5715cc5eb988e06def07fd065b0da9bd02c1014042d8f3e86bfa77b37c1c512d9bc6dd3fef517dd3fff7fd028a1bd74d4e518e7ae62dd7867f9b7bc7116db61463fcccd245450f71b39f3d0bb61799280925873e3d6b9e2a491979a57c01d5c51d70a6e1ac3fd5ffa1bfde0cf5f178145b88d13de2b25a2329bf32154d9c7fd67fd6bdbc8f55dc59b9a37f0b22d28468a9d7988b80140c89fb004b0dff20ac167759ab2b691d974d02f58b0023f0b8b4eb0d85199040eb3f37c3472151935d16bc254a14949e07ee7fcf3a0a2d2078cf122b42c6d220c63bec4c16f3bda31e27b1f75e3d1f63bd6dfffc2f04e916c4945259726ef282114ae3fe066117d6f8bb40789dabbb48c34592afb3013a32dba0e01987fd255e7d49deb3d1dafd7fa3972fd9a12d019aed971344cb01da2d44d2a8ccf4efe05e19888533a06a71706b7abd7c2d018dd0392e78b5ef0faeb7b8ecb2bf6f068b805ee01e3d2760abfb712e7ad2b374ba6984bea3900d0ec2ba324092e84b603e3295aff231590d830f855282dd731b762a87ec4a298f09ae242180d6603b5a0464f1bc494ba10c6f91de249607513d7511b0c7cd3ec605fa2c67fea254dc20c2323cfe26078053846de4a41201f10537fdb64080205707dad0c1ee22d631c4e573d17064d79d70c49eac6899d79c2a9e8b084c41ae440aab57ce29cea8d5834796a06bc1477e20f9b354c3d4a21972e58593c2d5e9df0a303539238904e7deade5d2a677fb8a382edcc6a67c3395b3e45859ae719298069c3554e8aeb189135ad07532075ebf52f1ab9f314e6f49a442c83c8fdb81c308c8ff618f680f667d485f8ac3e267e8570b12510d277c561c2df2b5243f15880e7cd4573e02b6652288a531f8d6f2c4b9d65c7d2825ab63550fb432e388fdcc0e3b609526acb25f86c1cc77221818c2a72bd3cb1185e2a0516f3ab6f2b9736c09b1e26a9c3180b796acd34991b1f2417f2c4840187a5fbd6d25b79300a04ffa08fa138155021dfdeea7299e27703c3ae6721eaa04dcdd4b3b2f09c0c594a7150ab9e1b5916c4750d798d952c18d298be0fc18cd1431c94efbe8efdb3e52cf6f02802865c798c270d88268ac15ecbfe97b92929455bc4cd02299f282437d9bd80698eecafb433a8a57fe82805cdf31b953df7d1c6d6cf5ccb46c636f35c87e7d081dd50ad73a2e4bea7cb9c3737009efcffae92925adb692fd284f7b59b507e503e93a98ca68100878eb3f4339ad204dcb89edfcd6aa4bd0e9c45ddf7996168901e1faa8be55cd299efb7af198ac8a3130b2a17675169d013519624cfc34834f00475c6b7e71a6dc3a4907994f54b1dfa357db93d32d6925b8581841aa29d46302447ec7ce7e2c4b15c7d7c4643b45f2b1ebc15b69d3589358dd9d12916d69fa02bff3c0b450ee455da8153fc31ebef29583f3e2c655345129b32d903cad99b90090682241722e3c0ed17c2b08dbea3da9d86ad35a1a5d1c4a085ff39f9b4028035d7a00c91db888a6cd3f42738ad7a7a2748df6cc896399782af8cabb419fe50a11e4fd889765340bccd76da9db6ea127183e035e66094f985800c7cc285cd80c468dbb4612663e3419aa8df713e724e0f9a07f25e51f4e6fb1f2416ba3137d069a25fd5ea69e5ace87309992fef73b5f534502fd9adc974fd10d97e8c66b9401c2840a423e6df1650b70db2373f2bef5f6f957c950a27c4e32564f438e19f407d7f5e0e8a1a61bc2303ca529c19e4b2c1054d8ff36f802506358fa5510808d0246815cfaeaa19ff060243e747c0761dc2251c7337f2fcad0ff6f912a5fcb8c02bac8e7a41b47b31d3747c5cbd248a82a6f1e61299396fb5321f2dd2706e0f20b39663e41209f84294b8a8b4a57fd5fdee8e27768c3b2504549b354020b28f30a8feb8123a32773b97a4b37354a2494db32dae83e69996e5751f6924bdcd6ad039cecbb1d1652e1ed18f0def645503990d335503881c00a857960c1f2538abb0d0a847365f5247ede5005357e6f2826f648dcdf71b6507615c5a7f6ef03d84b002ecbb8d551968936bc34152ee0bf13627eee499dab456bc1941ab5f50aadb7026d8a2eeeb4872c12801fbb174e01e4e69a58ad4ff8e4f8fd6ed56990c7cd930e6e58cc1d80870f7a1bcf604d4151e1d969fe298b46d51d827a3110aa5d6ab20c457f234479329c9f0745a63254de74caa6990052e6f249aac8e79c7de4810903 true -check_ring_signature 21dae28320107f6a6f150a6b32a244ba67a35f61c1ea2e54085ab2abc7f8af24 bc0b2eb12b78fb4eed6f9ea7f5e56656a02565841056ed856dda84083cb08748 1 312bbcb6c28ce9dc4e72fb21e9550e06bc42b4761ee408387fa7f8e0ec51187a 3a1931718ff85cf3868f5309f3142fd993646ac011ecd29b8fe747d670eba4e47b5bc2c3cae43c2f71054315cbf59229b0f7b8541b6eaf5c5793011673477c31 false -check_ring_signature 38898a2a056d07f3cd4d8f22c621ead1b9a98b854432ade1d1c1bfa1ca8a2b8a 81e3dcc76c675e94f6d45174aad57c0c99333be07805a00d545f37eb36d3e8a4 14 1fdaa78cd76b8ed4f6fe810beff019d5e881948aa90189c35aecbbe5c04dd3db 68e7b02ef14e94b248b84fa46a46474ba139e578c3bc7ec16c0d916d832cdc47 a1b6e9f4feb7c8a5d311796ee5484282e6d2d30e270b753cf6470f41c76ce517 49e76f8e625ab3acc522e8ba20681357a047a034677f049fa2899c475aa19a34 176e550880abd876c2307b4d6eb42b0020d40225124f670991e261ed9e74b822 fc5f1706677ee66d985d18db4da230f439c85d82f808a2ffe3d22d08318550a6 82ac6a417bda719cc532f46037cef471dd931fa02cf9e7134413a2c611524901 bf26e6a9203067b43bb70a3404d94e047675ca7ab70e25d11141911bb2af2661 87ac2e381f57cc1dc67dddc1856f853d2920e05944a532345728725fb333af86 acabca20b109acb684e1f344a89c72000014929301fdd6a854841c7e70867946 e75992d74ca21a03e48780b8f29738052cee235434b9fd0deb78644c68b95aef da0e26e90c43f9a3ebc6dc7cff460413c17267f1b98fd79d0dd6b9e94f684a7b 4c31c0ef9b1fd02ff8d4ed89809ea8ebaaded7db9a1cac184b56d85d9e4e4f04 1ad8a24e43a357ff444b5e9cd03287c508c2c3b2859e6c39375d221d420df347 5481f5e5c31ae253ff91df45639f8d235b06467c15b2941ace43eaae597acb0b4f00269b119a5914ab28706da4b2a4c32be6b105d6e6ffc77630dd8f1afe270244f1aa6ad8f49f4f2bdf94b370d114fa1f54ec5d871e0f4e8ef1542c09bc100b2da203e7845f71faf0df79571d4a1631678a540ff63451aad04288d3fb7f9c0dcdfec7d44ef73a51d6ead8bf105896513f94d477794d702e0f8edf38c38a51052d25a0864025df343d88077a8b6a409826af81792aebc48319292e4d561d0f01542ca0d2cd0c3c5ae83b72e002a19e69f6f464a2d7aee2332a5134e5f7f9c80061748464ea5f1c0263aece6ba673b721f0a6d64b812d1c0680e1237e2d06d80a8947af0325c7b758ee1581b88ed1f03646cbb4261665fe68e79e4d2549a4d70448d9dc8915de19d43197e6ade922b4d730bfc7a08e28a314c08fd8d8c94493012ac6ad69d6fa97bd4e9630b5a78e1e11b891f2ae3e79a34d47fe3b7ce6fb960dba978bc0a3b89333444616670af52d1053cc96151a7e3693d3411db738640a06888be99557ad57f6f5d1c9ce6aef5d3482fecf72191c8ec8b5d5c97fb3864f04477029ff97dc300d522fb53458a5e42601dec5e9efaaff5d52f280e6fb47bd0b86acaf90f10064d0e1bd7ad19988728306fca4d9b9ee826c52f568c096e3cb0a9b64aca80dfeff237d4c3a646bf1baaba212acf8cbfdad2a39f9b39fc8ee3a0f2563439fbd1bde8a40f6e55fa88ab10fee8330c8242c13e1fce9b83cafbf1603ddf4c61052967f0729c399a1c9de4c877b853825a94b45eb807013b73cfcb204c7ad35c8d1f051d781ba78eb98c91a37e3ff945e5759b0efa763f9e90800ee043bee9edfb942722cc03fee6e69fa5f84fa9d714bdda8105d8530da8d1f9da10b88b9cd11144578618293a986d51422d441ad6a260e9368e656e6408b46e5d70bd393e1b2139879168ae80a61988db34232e3d41f2bd7bcd0d233e8f3a33c320a4ab405e0cac707706ac5d257d0b50ab934d45d77687b78cc269bfbb22b56c9009d17a3d2a4478708fd75f18fa3a90ff60316a92287263b67e13aa73941f7c10f394f8c8c4f7ae9748bae8451b68a6fd3c9c11121dddc0390a3598b373b2002078c8ec8f69bc5b44acd5a57eb684c83f74cb596c4988a6b626470aee672176603566cfee4fe8685e01db57113fefa9a525924e5be6e91b0d9bd8dd0d56dccce0524e13b9fa7e5601ae738e93fe9cab5d8e265547c1268118b716a1f778f8fd80d true -check_ring_signature 6c652241a0b02dd524fba79336fedd548bb200275678d1bb7bc5c7289412d67f 47935302bb82b174364cc153aae883383deb4d64c34736c8a682819af4e034bc 53 2ba9f5a31d258685406b66a036887307b0e56d84000683fe7687c9a718bfba72 972d172c9b55d7c732189756c34be598687fcf6640ba495166aeab4ed73b7427 da0b7959c8fa40674a9ebd5b5dce8b71d3a1357630138bd54b6b171167c277e4 ecabb32fa4040807eefbe578a4e4f675e83738c41f71a7605e345353688010f4 fa379507e1c0b46e5b06e4a588561dd5c87c4e8b45ff17aed2fd1a4a9ba3a88e 6241d5d52dfb642890c3f1d0954b8f46e0af74ee28637b64121ae65622731064 8ee68cf20ebc1975030cceba430b79d45a2500d4c387afd6e15a386878c90bf7 cfafef82cc7725a07f2006979a9b9395fd5c4b0db6c6f03d97f97ee6fcfaab9b 2de44056196de83c7f67fc4e285630e85da7024c7a8e99db330aade4f359cabc c5e26831d95771a475bad2474a0acbf9e47110f421a6c87131da25ca5c154674 d94a43c38c3a3cc29f27961d311f5341f1420a78ed5cdae8a6763d3d27443d08 48dcd7a616178404526ae1dfb14d3297d4c8bacd29d6fcba44e76b72ec0ade48 4cced9b42a86b156629ef387d160cc2a7ad888e43e8d3b62a75950261799976c c2d2e0de535a3f4e763658980a73979c09c79d2d78b82596684017cbb77f2c0b f1ad4b434cd35787dfc4de4ca6cac299376f08ab26ab55173fee6bae23385d98 a91e2391c42c3e74aff8f3cb6b4cf9cbcd59629e991933a387e56735a48c556c afd2b8990b82268d50d7e00676b1b697970eb3090f277787566c81c670fa25c0 748212fa4c0bdc12a92123f4c4aa11f76960460780d9620e7884360d60dd2cfb 2a22ca837d1873a851c472e066aa2660d224990fa4acf3592527bb4c6da786b0 d7b8c2abbff9f5c6985a878ffe9fb4bbf3434e23c1d3dedaadbf8059c1770c01 8710d156bdaaed74a8cd0811454f81d5dc04d8ca17441962f6e74f6c21cc8741 5b01a2d8189764b752ff5cb77c4a5e9620cfed5e9108b18f59717253cc77c435 11583795d30925decdcd446173f4bef0015c84644c1860b7c186ef1affbf8019 64f4bf255e253542cb8ab343eaf9a22a8d7ef8a04b43957921380e2c5cb4d4d2 d505f3221399b1ab3e0a81260db1716b4e4f4b8d6f19f1baa87a2a65e5a7abca a53c02e1f21ee4200704bef92c785e63b61e2e096f909e26b0f5643048ab7b74 6dd9b92433c5c0c3ce77e572416e381e6e577d028c376c792e6c2b31b6cba3aa cb1657ec1603b364ab38f668ed2a7fc3e807217a1e2cc3302974cc9ac8d5e01c b60689e792daffb80ac41be54bade67e47a472e5545affc872701350853180b8 bbd130f41cedfc0a5ba1fd88006f711191a4bc63a0ed59a29c872b74ed86555e 2c80a1ba2778d2a9bc1fd9b0db7deb5ab35928f750970da576a0a2d0aeddc165 b4b4965444f7e41117abce553e28ea7ec332a1c8ff0337723685bb8acafc587d 8a5379f4334a6cdc41b31e510f117d076dd8c1010e230cb48c90c8edee5c03f5 36ef710ccf06de5903bc5cf761ce623ef374b5713a2375cdfdb1fad8e1647e44 990a0d0f3e5aabdc401a046d28f55018bb8789a42a058ab2a80adc8f7e8531d7 570943ad8b5597f68770502d1405534787a85de867591c1eeab16c349f6ddd9e 294ddcf9c6885457d3ac7194a562c4a3b1079c84764fdd8c7ca2036e1b35eff2 4e1a839ad1cf13e808f729c82be19c742ddd68ae1f1f1b8fff5d89e475feabcc 7d13024dc613088ce3ff20248a52171addc6efb00413ea987ba2d9504bd17be3 f55a04a83bb00fa712197607dd3661e388d8562873deaf797a00000f564798d6 696fa8ef18b1b5d54b6d1d0e3e82a0657d715d9ed73c5fc0b5a7bdd364af2b01 0a8c7fd8ad218e526f24590fe861849643e943d0b326719735f10796298c488b c89fe9838e51a734ab0468c09c72e259b0a8a7e591ea8f5d44a78acb2abcd1b8 de0ce8c07d91c0f2f68e8a7ca9c5ad0c9c02a7a92a9aca9a6b715873b6917d49 c56d7f18690cb556806ab1b58b83793d0b4463fad66a1022f6c17858c94f9416 60e93c25cfe2e5e19a46aabb7012febe6d74f0ab681f8ed23db103975ed1154a c1130500418f40991a658c764d9305e60c609fc5eec3f9a6f4e816db15fed61b 162bc7d1c3e172965553552f28d7ba9898c34ee6fe7afa8a1e491065ba755b6b b08afac5f4bf847c24ebc808e43afd3e1d51af09f7bb9f9dfcabd57ba50d6c54 4d4558bffb699d94cb9dfda5af99532efe4dff103a8fff80f89ea683564bfaab aae6c25ef1ea5f5be4150999ae3ce693aab41825c5690bf977d61e0dda919021 d1d774048f0e0260fc35c10bbc337ac388c85e22ecbcce9c41e33865960e7734 5879b5332001449dbaf3578debeea212a94b9d56855a0f9ebd193e8c80ffabba cdd95be922ff84d67bb6af9068051c43d46aae3d185bec07b750f4f79ee4f60380bd3008215c0434f3ad3a489311793700e69145d8d50072e27561064991a20ed042b3ff1115f5c0f3dace49abb46ae364a33d2e400c069f7f08d4ba868f0805ad6d1d9e633438eeb04182ee41238922199da915c228e8e66a15926e116782020d84f7fd1bce474cfcbcefd962c518992762fb8248dd17ceb6b8d4ab5116490998921c2c6decffd9159c5eb55ed0dc260335bcdfb241555cae4f12f54a4f5b070a85c27d95a38196698e9798c874abff6ee2941fa694be59a5aed779a9ca4300dabbad7e0c9a53bfb3dd9981b5949c557d7b75d744ea2e9718bdc9aafcf2af0117f36d920d30d2dd2d9b62d5684eba4bb2ea3f76d4727c3f44373660cec55d0a7e460d62f9b711da70c92f6ac145cd148f27a08eda977e4b3b87b47c5b710709dc8407e3a7ac0f3ab4f02ade6157502a0c0caf292c3617bd49eb7a53e32bac0fc34ebe207cef0fda7f2635e7cf2191675835b32db4572285087f2478d8f1ed00f29a5b0dc6bd4bbaa12508147419f02f8f52e487305d4510ad878750d2a395010879747d382aaa97a6aaf1a2135b7bf6f292c353898a6f707c73f96b2d147a09b4fd239d667d399c7eef6db733b00888e504eafede1d9901d2a99cc0382a2c065d06e22c22a10779f792d05d2f0f4f587d02d6a945117e99025a106a380ae109502297a040422d4096eee658d629f75768bc28a1e482bdb6441ff8265199720ac59fec090412139bd9eb5382346097bc8689505f028612e32e65798e1f418d04018ae682a1c3c793d8b97226fefcb0385397801dbf77b699696405f54f4dff019747e3c33782fa9fd45568c4d4a6d6f7ac3633705a6782eb884bac2848de5a00eb6acd25a057856c015cffdbbba424e3caf197db6b2ba34e3d3a380f3bb5cd0efaba9d00cca33f18372ca059bb43c34797b8e2725459cbaa7c67c2dc7fe18c0d984d074f9ed6ce5fa18675f43e658f44b8aded8354ad8fca34ad43a73ccc5f0277f72772c1bc8f016fd1dcc2e1502300f4a5f55f7dccca541da4a9e9202aca06862233a6bcc1c52cd07a9210643d5bedf2d933e8f5263ff1fd676df0fb030f020704c324705838780e0b1742e5c62321a5358990eea8939056fe7737567c240168458bcf02153e9303273ff074e7dee16a07cfd41ec1d643ec1dcdc66239100c2aefccafbf0bba5476c166546fb3b0b524a1d5407e7e640a890613b9f62ccd0b3f92bb403fb1dbcdb0564bcebc701b196ea926bd7ab7a585168db39da2840b0449958d49651a5f49ba2de30fad0432b570f5ee7d515ce5fed541885b2b41e80e4ec9dee8993700393a0560d1fa00768255f82f6716f5eef5c8bf393938e7ed07740e6f100f6484d248bf7e419367dfda647757e69df39edc77e286beb2911f0a22c020dad6a1369df1637e5826f3d3cee8628c42d022fcf30a5e0285d239d7094896fa03f48528de4af35a3d48b2a71aa2a3d5b22cd920e302ce21865d02220c0506e4ca617e12d246abc28c57576e465fd748d2438797c3d173d7a2e3809103cd341ddeb4d72f2d54220854183c9fcc6063a08d0639b9bc13af10ac349dbb03a9189c7cb0efcb749155412cea23b834cf368fcfebff6dda0bbf0f201919700aa69f61483a58b57348a7e04bb52e9538b70a8082eed42fe94f71844381d5b003cd51c6ca32060cd7c91532f03e59471d5e14ec080dfecdbff2b504bc95c8c2045e39af39525b88a9519f567108d39d574a49c05b638f840e6a5517c2410a190b087141acb52949c6dd424d41749d2cd0a52076aa829820a9d7c720cb3e121a05824295ea8901a356df5f18d567c8bb1579cf45d8755d5a221ba40a00f0188702f5a465b509a88a0f00636df17ff2a9259c3bef6a4989d9333b384164720dc20b909db6ffad6b98303ec79e6bd46180aeaacf399d903c179dbefde7ba26d9c006d6fb30b700c31de335389daf62d966b0b8c1a64d76f15bd7b9dd42cd415bc7037483ebd6d9d0f7fc742e3435cbb32f49e3e70798764eaea842fa876aaaf6b50015a6b3643f9b8b83b9e6105af54b2e6c31e7e049e9a6791f20ed06ced8d9830303d9da8d9da8408b9c18b9413e3cfb1d86492989ab86ede03bc3becc3711020d7f849870d1debf3512fa56a1c9d444776e3268817bc67d6edff1796acfdaf605d1ce0c55079a0f98d9cea4d182f313e85c2a6098b2e6449424cfd31f2b2034078a900844bb79fde0e4fa1626110fb12a81d213b9b8a03e2e222edfc778fd230c493884e22e5c53dee4d9b19acdc20f3bac36b56c58dbc90993605af7f3ebb10d9e31a52cae9bf198188aba4ad4a5a6932014120c6fde005d16554699245b000c55f7cb56125552f71493c27c47b5c6f229751771e1cafe068eb17583047bb50d96cbf953c2ee7c32922537a29288f923bebf933f3f222f90546018ae895997020a4d6d713fb12a7f1d88c0dc709cfeba9cfd0d2db49cfb164904c62b14e7b50d405eefddf9bbf82fb2a125b632f525695062bb98e26cb73bc1427f989a026009f00cd7d04afb6df725b49e94862866d0d55078401cde3ef7c26e53a19d76140d2c86279bca2ffff45ddeb00bc656595c735d50201ac899c9707b71e7d1bd5b012706fe1897a63047f32f8b440a5b2719a1ce71a091438120721035810c11f20fb6435f2dcd23ec3ba29c0e7e204ea5545605770ef75d56f9c0be26365eb9e00e1be994673aef219cf2a0b9cdddcb4c0068eb94df9545ec44d9751def31b1030198ca7216e44de208a27fb2e93e2021b683274a2a58baf73912093ff684ef470fab8c80ee44bee2cf8da3793267e9110d981dcea627ff886992307c90fe012409afb9fd939510b5e8e83d600b2b553aa7dc56001006942348ba12705998cb420b6b6cb9b07cf1c4df76a3beea800414e55ea27cd21e2275f7921bdd56247863001828cb4bb4a2eba993611fe35655ebdf399521fe758dfdf31201ace0af04320200e03c0030c00c5982c58dd7912635df00599b82289a12a5868b5c7e67ab11080a6d215324cc7c0616e656037c06b26a586a2d89da2bae2c56e8027db48b870ab0531e74faeeffcbaa339b5472333aa1ab287b2ad86dd45ab1e371c726b89f06f70b85dda99f538139ab82cc3249dc95421552ce86caa99dc706675cbcb3f10b3ebf595363ac501300d8e2a6be516db437da31d1708d38c4536aff2003701508e9e206a4a13007db148a2e50d7e8b01b53ce8f8c26e1548b9f77ead22c45b10b35470bee76238515fe2dc00a933bb4383814986590428006e842fd5b9faae60778224addefa9d4ecad7f353f20d1580feed3ecf9cf867b36dbdd6128ecb08c0faa91267227d4d0f7298756940215c7b8924e10497ecbec6afbf10d0231ec850894d998bf64604edbecff02ff7cde4d8b720b01de022c0ffa7ffa6da6da000b025317136b472c4db1098cc942f5c8797d8eee0b3e24bc9087e2999d561e999d0dc806c14dadda13b5040a214456211cce5bfb0e401d5a2c3d1930e2570bed83071633423f094cba95f8e3023ab2869b4de9e5c646af178e3eeebaed14156c2f0a02a47574adb740a4c3d2231fe1f170bd05cb34e7e04ee53ac5cc8319bbe30c097b5244b7ef8c26a7ef1461dd43731be7dea4d8c60b428ae3006c5538d44a2204a0143ac92ac44d693e05232ffe7b3dc265c455a4a61e9928aec1d8df59512b03433b50704dbc0150406c22d444235b21ed7936d583bf8e78d5a9471ed82469016d708aef8cdba8379d44038a52e6bce93cd80b2fd2d9ad49b5d207c0d6046f092ad3220481d34c12f800967c5a03638743ea844e5a90c05de642b93d66e65d04c2361cb55bb3314701132ea16a66859e0f098ecee9c9c630992bfb00cf44570a48f48bead2d1c5acb5a3897f4bda7f61cf92edfcadbfb2db2f04d4593bd1670329fb6d2939a23008215ce4f8863bcc74c5cd3a8db21b20636ccc9e7d6c2b69069fd523d88564afc9883822675d8252c90af81251c9d46ebfbc61c58eb63d3c0437f5fb4861e0cb6396ee3e320244b56961283e44b016f531e4c39167ea31240cff9837aad6d27f6de4620621ecc97eb95d0d8c30d9d6d1aa1d8e082c1b245402b03982d4434d620b3ee48f669597e687bf2dac30d416818cb0fc3402cbbea70866601a7fd03e921640ec07b96727de6ca2694fbe444e2034c93c7daee2962c0d326484c476ad3b6d2ca2403f80eaea9fd204bfdfc8c5f8213ecb1db4a745340943f02265da4cffcb879e920cf23dda36c54ffc215279aab298ca5edf6206b80e5d5a850f1551670b4b93504d0ff81f1177351d8ae3b1d03e9d15082e404c97098d6fbff3220a9b19a23a618f05daf4a63bf11c714cd0e619b0e3420955706d0605587f58cdd3dcd35d040bf3e4f650744c937d78053ca1f48b6c5bb108bdcf03809f9c6eef298fc14d1abecc32d7f3f1da0ed5fd18ed97c9e543afd8c78a0a06c6f8307973454e7f7ed643574ef62c27bba8e81598c64796497d36bf9a50550241d420fba61353f418a57e800d23fd0e9f3420c327d2c6e2487eda1dde35280946885c8f10c645a48273ecd5428684dfcb97a670ece426196c4c625edf9bc70e63e50314237da0420b55fd1082dd870629acbbe06f018cad46fe285c49eccc05aff0b94afb381a73bde3f5ce3d95992680e3f2768f465a34c9439ac3c2e68803f6ee0df179cd48da840a71dc9fba50072f1bc15da779997bcad2ef1256ccdb06 true -check_ring_signature d3cf45b59130a7a7310595fd1eaebefc1959b14c22964e396fb877ef771980df ebcbfbb9b0f050b1d79e0bc726945b3e412c6f9e16f5301442842b8181a6c3a9 17 2b9114c6a80016864ee5fc7131ca03eb255d8e44fd947b58cbac4ae7a63ef80d 1aff241b0f57f51728f46a41b1fae63541d347b2c3c6a9ea8f92f29592745fb2 c34f923db1107e6ea18f19bd2365d51f3e2af5896c27c4876e292910ae0f9d4b d47c4985cb26ed6babbdeaa5b4293758c7007dc11a5ff24051c4e133c5c0c7ed 383caf61e4ca8a7bb3ad298019e8bd28b939c9c060b301facbe5f3dafece7a0d c52300baa5b97fc63b2a345fb391d3373cc765fd52605241af6264a8fca95b4f e6fc5a6ee2c1c6e110f21c7eef0b55e075bbb3fc6c9630dce88c3cba53b5f02d 4b786db46a1296579197d017168450bd80e186acf0051c2c9b6368087d278402 8d1c0d19093166cd209fe78d1755d0d1112f95121bd30f5bfe4f2605e7786448 3cd88d84a1075de2814c772480354b5f8a5ea7f67168c542f60e6e27e04da286 02462c1841188db411dd11d2135f7f542b897bb3fb0e20fb130e8ed4cdedc82b 7ebbf6736ca7648da1855f6ee05cd0c21a6f2573dd570ca6e8188952908e2496 19cfdb99a0191eb61d4843663e4c92e701b42908963d4cceeacd9ab86f5dea92 f2ff96d80c95a11db96dbdfcff228c26674d8d6bb511172ba42f18a29a275851 f59e6c9b7fb02c9b37a9ba7c889c514a6c24ce0afcfe44dacef385c6348d33a5 5a27252d6e6eefcb8818d817fe15208842d5bc11d49c25879e2fb86fd55e2d39 3a619702a0c0b1a8e76c37807cac1b7eacc07c9840e0e10a1d025807a4de7782 a61c899bafada7778a369253b0fcb9ab0ac8d82bcbe0604817060a29271554086c9baaaa370fde4d7809643fcc731cddc4a5ec322f408dcc0bac0df06081180d8e323b4d0394d5da28a8dd4116a59cb467b742e687ca1c1d49b4df7293165601d0a8f7dcfbb3c162272329fd20cd8e3192fc357ca2d4c095c157ccb4a2ee0308a2515e9ded7c6143c10ea08dd1f8659626f00dfb66b8e71b04a79f1bacf9fe0eaeca94599c8d4fae243cebae6ef9a325e38d3854f074482f6f5e8f99a2ad950fa47d7b5f12c72d210ef5a67f8b0fc02859db015c31a83bb243b99b11af61ba0a0197bc6c5da10d34767880295255ff01061d9cff451ddd3216f2db065e351a0062ccc53a7841c4acc92521a198076b345a59523cdef46c2fba60ffc51ad37c0432afaf9bcda491d92fe69ff276c2781035a78d0ae2158f2d2e6de90d5f56600f052c8534283e12a46c985b6d0a93f04be56428bc6d8cf81d4c3a2741ce317f06b02431a04a832df277a2ba88a86371a8d420ac289ea3ea158fa6efd46ccb7203e25a5508e9e614b3ae47fa77b5bb2db242582444458aecc8677f7a205f05c3071a6e870369dd5e5e4af159efd74a0115d1dd1c20d9c8e89b33464829f70d2a0c45f52d74b06dcaacc8d6e9efd0daa1483fa4f889c20251232583c3e6bd113f0a5a05e7ba8aff21eaa0c4c426427bd5a890ae21de9e76ec96cc5e30a5ebcd9f0ffe783ec1fdacc4f8557d715ce72072f42cbcd8fa57ec5d7eeacf1c062855ab0646e219df871b74ca50371335ad52136cae1c83bf4d019a992c6a8b6d36c97d0fc2eff83e5ede4cd6c535c301b8f47be3d0a96015422037b9ddb029c318b6b109e2e351c02b93787fba002984707f98ba2b1fe78a8c71c1e525b805436eabaf06b7cab6eefe53ddbc5cc489327db9408ff00c90bc86e6882a45d0891c1e7b0e0fa0f49112600d9433e64ad3d397483231c209cd796a4a6fcbf7f20f60d54be70513d0e181a08116ed18800948412c968d27e2992b148ac2957bd716d0acac6b05da7e66e1135092690a51ccf12749e0fffc65e9f19bed7c9b1fbb2fdf1abc290b1bedf1ad5ed6ff30375dbc978f21c2fc235984c281bfb981b9235f3481401000afe5ad20ea970df79595856bc58cc4a0254bb040e5ab7cf576cf307b043f1c01cd8839e12688d0c37d8a67df5ca7fbe8f5ffe6b15feba8566ba6164d0038260e0cce662e6c9af5d2884ed5c5e491ffb97370ad82e35e58ec6e6f7d4e56c60a0e7f87d461e000b692e113d68fc09415f1e06c771342e19affdedaab45975ea90e95570c66e8235ce633aa52a586f9ea02222f549099cb96735c3de4a37d86150fdea9ca7a63b5634b138bfec1547d4affcadb29419fa1aa0b3a0dc0fe90d7610d063a004d92a630ac4f8f548352ea8a9d0bf1c644e1dc9af179be9430320cd5078186905104412c547a56a519a6c1cdc90f0a2b2cea95e976e885bf3009ed0304ea95d37983c0a833f6de1ebb9a5d4d8de4481c7af9073716c26ab98d353d9a06 true -check_ring_signature 3433355d316784185cab4fc382a3ad7582e4917e8136f4c822197f67eb0e672c 27dad86f9993d50116ce6404f2ee1b22794b03b1f61ebefd717c33601a61833d 2 9557656059dba281368ad615c2a77c6225f571bb437b6462655ff394d9917fa4 79252d723d96e2c59b9828bbe1dbb7bd45488aa424af31acd3b152a95d0b56d4 bc25a7288f5d1dc5b271df927f1a80aef611e893212b3f5c9b320a4da72781038743f81b064a31eac0eb0e8ef0fbbaee0b31bdf64406f0c4213e6f31cac98c0f47b834d54125b99046950ae12f8b3ada087e8d3dab42a1a9bc85a6d5c49c07007edc975cebabc95065f75aafbb85ca2bacf880b1b3ed33c2b43ef64139c82c0c false -check_ring_signature ad9aa780723492e45f9fe9890a39383d651e51f564cadb2627a5bb7886e20918 2981b8e9dd156db3c1203f94a9f989ebfc3d50e6dd7c7b0bff32606b46a64f13 3 e6430ebfdcfe2b83208ed44d822afcf4a4c4fd0f305cb00668967d833a3504c0 fab2803cfd72a65c515f1634c010039db0fddf419ccd668398495aefdfe6eb7d 1e3b399ccdc97fa187d25619e9d690e57d3371ac5fdfd25727c4c6d9681ee7ee f8bd18068a6df365b07de09a4f2f82bbc1322ea31524ac6bb73988211c6cdf00ef4001c75f78e26daff33ba1f8b148f259fa6b70625cf8060e6dc4088b96fd068da598e13b046902c50a0d27c08d1756caa991d9413cd81d710c9e89902ac10a2109c829943fb54849baf82a08233b22c7a355cd7a63e0afe2ea479821e2da03808948f40c52a075d3062c4137585bf8d1479ff84a278f7716bef3ac1506cf0661ed71327af41bc77ed5d7afd8bf9ff54fe7127e50fe4acc61e7f84bd5e0080d true -check_ring_signature 4e84714c706df8c6c1efdbf1a4cb6deb82b5e169666dc8745686a2f1a7e16087 6f671d8ed31a66d9f08f209bd55e909f9ca888947113be4fd01f538d434bbbb1 3 f93e746a6280298b23142bcca2292526b7e8a7986119c251f435b3219103820b 5860678ef908ec3e4cf28573f7177b4ab2de6325d6ad82c14388aa478d74ce69 8afdd6f9ab6b202ff1e4ef57ee2f2e1361d36f6b80d2135cd8b3c91c096c81ab b953eef97e24a629c05b79d7a3451866f9869b34d8b7aff738034eb0efb9cb01b17fb95a2a36f759f46fa83d470769462054716b59e1180937ff27cff16bdc0cb64c3b2d9e8d94e92c225ca07d298e75bae20d0dc46748e7be5da79f9da15d0008d1b7a412b636291fe66e6e71d35aa57613ed028225defe307df720620579069e883ff08399fed740c2ea33ac84cc04f5af1fc46ef2e9de81be343e5a2c474a29484de63e667d8fd32327c57b370b35f2fe45600db53896b59e717777fc8608 false -check_ring_signature 25feb200ff701edca6f05577a6ef399e5ebefe4c30cd3d32fd8dc039260d7ec1 a0a0daa54a332805ec04b545e4a2c2ba33f0642d6e3f1ca86ddb753dc8ce81a1 2 e8d4554c2b3068e32e4577064bbf571b8b64a109c245aa914b7e66a6c65c1bc3 f78fdf5978d1800743d622307e6bf8167ed9d0cc93de262a0fb59bc525a48f4c 6846060b1d59d9b43497b66b30e93a400c8c1e7ddb6882dfbb53f08a6bdc150bfed92a1506be27f499d39afa10251cb0d20518e07a70b0ceb38c3c5f53241f024d96d9ec4856bdcf1cbf8914a22555d32283629993009aa450c9ddb030717d0f8cd7b0ec1f54f230ed5113ffe1fd3714aaa996f700faad06581a94310d9aa708 false -check_ring_signature bb453ffda56cab015cfba42204650422e26d715293459f3d08b4f50b3ac29c89 7cb83f6fef3d582f8bd84da32942cba21d04466f16005fd9385772699b33dcca 6 c869a0fccf8186de68b46338b470a7cd25a9bb7676f30005d1fd4c71107f08f3 c240f104da8b656df4f976f50f7c3703ffb3e9a5a8a6f498ef9b1e3576be8d01 a0382f1958be4a0bc7f20507f88bd81cb398ff28964bcb3be7ea1af94dd22978 e4f61d36efb761701a0b2d344c5f08430deb59bdc0088e181b31ca16d1d1e4d8 3a0333bbfe00906b7c2b5f84c442cacf3768433734807d82f14d7b9568668dc8 8f7fc2ede7e5bd7d5493bc8951b92167a430597d1865088d8bbe2f906981afa3 468525692a8304c984d82ad4a468b4b93375499b05c7d7eaa4834c7192ab5f09942ce459f3b9d9266668b50b5c81e0320d48d96a93a53de69c89304f6c22280c28d9e165bf2725c9a75f995235170b6e0a6bba3f4a1f014a71e1031f63db120c0decefc8719514623bbf45b54a03e797c821190b78a462c18d2d0cde087a9c05fb1645f7f113e4c1ec75ecb1e02348ffe60f400de5512a8f37181bc111f5c70d4a39b170ec31fb51667f2523a4f3382d2d60ca76f793c3481c9908fadba2c80a59d0d2dc6fbda0a221d9be17ba7c5f6318d71db96e074e2be15f530279e7810dd2e7660e9e2c00f513bd3466758482a5e4392a1a2a1766cd0b12b06fa0870a0ae4fa41a5293499be77a5548df5e40cfba99745b54e821c8cd90777a218f50e6368a8387eff6c937a9de1eea977ed891a50f1dbaba5c99c582113cd7e21b648005397b3302d73b477383bbc682161e6cbbf60c08245326035e09d2e01658d5c0cbb07f510553dd1e1f940cc1f70bdd4faec40aa593728dfeca402edb4d0f9b10b false -check_ring_signature 9bd8eb809b067fad780e7438821feaf7f855bd7b3254eb2c5c6c4f6dce89eb89 9472505bf6ce8c71d71912f1672ded22e10d4b079ffbf18f2b18ac6a23f5fc88 2 c78783be9600ba6ef57e00920d663a7786017a3dd496f76675dc1d673fd0fd0b 64317c35148071c5ba0a73dc12e54fd4a15937284cee09a5933bd5ec1dcd017e e19bdda781adf88dec174f4bc084cad19f308b19505784441f4aa97a530e2c0e52782bf3bb107d9d963366daec4e086685ef30c947950563704321ccdca85e0e24bd7accb150dbd1a5128667199dc5761d0ba2eecce6d864b039a1dbce879d009592f0571d114a84187f24611123e239aa10e6010b545dccd6e8fa68dd2dc50d true -check_ring_signature 8a0f915acdf93e40f874950e9c1fc358b02d41c0b05e243b365781cb40846beb d814d7e4cc04411bda56d28ca1eab20d6ba1fd07a5a558fd3b7b60b34ca23601 55 7c516380261bac0e0333a435d7b5f4c410f233ad9b03603eebd5c90ba7fdc019 5fe64baf0334271aed58c7e4f1880e7e1989664a34f09c5a0e9ff787bedd6545 66335e8827fa80e92f5fb665e1e6c37a1e567a8a2d148db917fd96d18eb90387 83693cc038a805fa8687c16ee717871f7f00f27b2894b90cebe0380763526561 0a7195255d4dcda5cc3ef502badb136e4ef00ae1e32952667192af3a58a6c939 21c3eeb23c6d9e804a7a0b99f18a147cb181136732f35510c4946d37ae607acb 22455c32637cdac916092b9fffaf2b5ad2efbf2706a2849c7abd124a81f2277c eada3ac9fa92f80f975da5e81d7f7b35f26ae50902952d6a852cdf05a7c56296 7dfd8bfde91d14b349b07c148b6991ea5dbd925c956a0ce7020f9c609713fbef 5dc17ca621476d52acf1a12e1d464fc1dcc68b20b7b8aa62d37537ca44b1cf80 e621a84b417045e9233ca9395af49863bf8ae1f778d73cd1c985f937bf7a767c 46a21d1799f534ca01f0c528754973254a89c8cfa8c9373f70a0b98002c174de 9c6dd09e90f65e337987bc362c796f98619d0fed3f756d7439024c9ee7686871 09099322a9359556ca43b429a5dc5574d052f5c82caadcf59d85769c6b423927 43a7242bee6f012c08b75fb1fb2fc7f28948a50b547de455a0422e4d9d41245f bc75cefb797590f5835795f64985c649511bd2a3c3c445617981c0392dac82ba d84aeec599d00210d614d83618e6915606847be0d39c73d1becee86991563831 608d522e1bf59f4a8887db05299748cf30bda8bb3e4194d8d3bf7380b73ea320 d22e148513ed18fa53ca1ad83caa982c432ce5d5b6dd7494d43fe0e7b51fa6ec 033717428316b611f70b28e5b4ae861307627885db8b49e0915f7da0b4e4caf4 9c3a6d5dfeac5058645a68e6be01ed7ec398d2bad97001d049dbff770f72c19c d9cfb5d768dcf962a9800b73960f7b9da892198a23a002999bb73b8fc52b6981 d3b3e1e8ba556ae3d5381507359d83da7d5b7f6cac4c76cb5251ba6429f0bf4f a536d23d70989a860b74c217b0a2997f1e8efb46d1b5325f32e6009bce07f617 9a7c2b8a80373fc7a27d8f9b16177d8f978cca020a849912bbc576ec4c6c07be 87271de8eab31aa3bd3ae07b2c4ddb39ab4ca35bac08cb64fe27ede10909ae26 23a35b92be6a6abbf0aa4dc76ed062af880a5ff9d242a30da92020c3be8bcc40 33ec4873e1f2f8c2ad69f7e9c017e20d890cd34029bb0b3c21fad6d435a4f03e db6b8fb710f83257e807a6c0628c132273b8f93b05a1b3fec3765c4fcbf07883 18db1ca2c69c83877a5b162df4b5b42b93bad09d8c3d10579952818b94156757 51e09a281eae003b04a31981497b4ee1f026b251f623835f2809e44c0bad0068 67e89aae7bd92895da5bfb69a754500b0fbd0dc2155431311a35e83b298d4d0f 356aa6c64264298c7ed48aad059085fb701288fa84f05c2563f41fd7c47fcd95 b10b5ba19a411302351944fb62ecea0294c750fd9543dc2dd0ac3c7d740cd439 8542da88a0dd22c5df6ab89145ee15dfaef982a25c6d14344691662552e96ab9 51da57faf838d2e85bde0f06f42a8271a90994f821bb33bf5015ec8178a2ccdc 6d0d4d39a841e942a57866794db1c4c5a5f323a475e54d44f01c95f69411c348 4545dfa50ea3ef0c294426146b549bcb8ee26482eb736c74508722d147b498e7 371a334d4ff6bb0d7900f9735fb915a453f7e4536e620a5401402575a11d5a22 a2c854d7dba715642a545269b9b93759b0e3997e79e809648572d2656898e3f1 b1198841ebf583c025043319e86fb717785ee4bbdc2a1931e1022aae4e04f0eb 7111c3a213d0a65396201d12feaf4651ac80294157457783079ccf6296ca13ae 16b07769681d49e34388e968a875cea5fb206335730a477537aa54d227cbd1eb 16c00e866fce2c2323d4dc4b7a226a5a5c66fa19b97ca08442f5ff53e7369103 c390f826a180703e0a3670accdbd42e34811846cdda8242c78cf1a4469608dfc b3a8b6d2e5d8a992546cdd1060ff2085981a84233ca1683b6472a427f326c5cb 7abd8efdf911fb46589cf908d76178c51332dd3de0987c284821e8b9b834eb9e f6ee51a12b838faebb31c95f60ed16ec13626a4aa7bcb47446c285e8f16ac61f 3ed312d82c0ffca86ecbdb342e39796c3c5079321405971535ec568ba1e68535 6951fcdc1ca1cdcd969b4bcc8d017f25ae54c693dbd119c9d20894d167e65bf7 603c0c659d1d8ab52a1e6bd30fac381b639c591e18848de2f78f90eff7816983 aaab4890ed65fb1191d4dea73f69c19e08ac1c11853023893ff32b9a60b6c706 d856c8353cbcc85d25a69c504eaa8f424400adbd484b281c38f0f3b59268ccbb 27d09be4ff7eb6a5d429b29c8b177d6cf72931ac40b36430e2e5ae71384904ab 849478525d8927c09b6930c05a5a8cac1ea30666fbd4d42baac76e80f49d7611 91a0b48ab64e5011c3d300af58df272e8158e54b95754632f6ec6a6d65e47d0078a2a978bd59a0aa720502dfad2e2305ff69d9e4c171cc7f48ad992c01dfc90593638eb49b1697ecb67bf767fc18367410e784bb6cd680ced5ad37746dfa60066bb8ee4c840799e57991d2fa9d7ce919003a580583b8575c2c4abf8eb22682094e77329734aadbb0485e37c001fb6958dfab2732b9fdadbf6199c486a92aef0b6447323118d71ecec254fd4c3df96c8a9bcb2f75bb1c9c10a3bd48fffda2c2056fa28f5f3db71f161fb103bfe5ce392ccc7608f80a5ba5564f9c70b60a7efe03f86d8fdb47414929d7c139e0cf48d226b078a875938ebd2983f570718b95fe0c2e9a27f59a36bc6fe8cf12756b31c82eba023a8d1183198bbe3e2241c6192a0c0322cd67fe1d7d3da93dba7f72ee56df822cb07eea9583b450f302ea3962a3060a34e556074a8811dbbaf61b65166bf98eb9fb2048bde698c262371a474d1405b1dee33c344d9174cd11991aaad5ff7e78bbc618e6d422ea5504ab149e603b0c8f3b8fab2496de8948603bc3f48d3957f5026f5e1883f612a7c11b583ea74a0396fd225ecfad3ca6b8613cf955277f191300aeb0fb6d0e230c5e7eae7dde590b9d48e748f186d0a18682da493a82702bca62defe913ecdfcd1be9d045c330505fe8624c77d1c404b002ce553d77054afdaf5a1dcf047cd2e3f5a7b11abe2a7067709f08641e498b262b731b54971093e288d3f0b8fbd2e69661d856697890504fd952a819a2884b646375a016a877aef472eca5f7c89c850d2c8511c2bf7db0a8c4e7b4175712437468b294d7ac9867b128e0c5f5e60dd3d9a856f4f0986ef0925cfccbd07c29b1079a64ce65822ecf2b80b2d89bd1df224734071a556b926042ab700d959e799169b9b580d23e76d73a291be8e63792b9c58a188617bb66503151195ed4bd0bc710a070ab739981a2122e33b958b4226799949d8f1958e1d0a902ae887b25f0ab650e8b2af0b01d2a8d076727f2881bf7d3254e94bd8359700fe37b46b52f10a7a1c6a30c469fb15a9bd6137af24899d9927ab9784dbec4607912c7469c432e6f546ca1ccb375fba5aba61ace9c26ac81ac008eb7629f8a602ee70fbc213b436930596322a21cd570b9dcd7bf73d4d52604be3dc89113a6f0c8c12643dd3416e5a907435e312a8c2ebafa6c3b36d6db5d135737fa5acb9890e3ac23acad9b2f589f34aff945ae0cb368e411b995f88fecefccbc13970fb220c102ac55892a1ad0f662a8548eddf9fbe2e62cb6c318324de30e0f206d76af00fbe9001bea2c72460ca6f97e976c04ee0e98a14cdf1b0c0f562781603056bea0011541ca9cb7ea88ee7a4eda21866284077e3c62ead9796da18e1388656138b0935fecceddf47995019c9c15885a5d9e74e3e9480fc5dbc96d82d4e501fceef0b1535b91cc20866dff542afd2251e4c4e780094d9f6e3972c9ec5989741b4f30de5a58b657aa8c4dd2175387d43121806ff2b0ed02364d691fbe2a3313dbc8c0576c67e3f4d3213024684fcdc4a9b2a74c2b91544990b712284bb3ac2b597e4093f4b2cf894e190a0d39c266e6866d28d5568685487d81af7a6edaacda5e0cf01576ecb8739e3947410731668938c1743dfb586db3ee9b8e8a02a1ea26533bc015fce21884417317493dea1441e8fcea6c708d5ef79187d020ab8cd0291855700663569f6edb3192ca1017d273db3e68c2d8d908f05a0633317e78dc7317ddb00ff0f9a8802785266878c15dc6ebbe7e06fe2f672bb332bd78a0eab554ed7750040cc0f5b6f6a77683df4be330ae1c5005a5f6247259fc91765bd91efe028f70342fb37de28f8d2711b321397515858e3e1da4530b2acb1794fcd571dba886a00217c35000bab47a117573ed5cc7359ea282ef40789798b2914d669d5f9e90004850bb0b7d1dd004e626fc175feed1a67a8219aec2e893f6e9209b0e42d483e0fccfb5426a20c96b61e9e1f4b44f5a192047ed4d62ea77360464ad0f0e2fb83099bf95ee8fdbaff5e801a823cd9675b0e237a20ec65c36993047fd92ee48a540cadb5ab9fe2d58bac87b02228cf0903f0041662754304990132024cc3de833d0486bb92fc4d8078ad64311366ee25047050c1d8c4ab0ed12b984701f2492f040ec46b9dbec7962b2653acf1e5c3ae7b7616646bf25dece43a6d35d5a83c7e76054772d173210ac109a205ae9ebd114b40bb8e29e08b1fed7575f62b463f3c4c04c4dd19c9695df8861ada1b0b0c3984e72d6887dde6d4193576026aa278b8d703b45635766860c315e1f4b70d783c1dfe219f411aee8701c503c9fa1faadfcd01336b79492db4806ec5e6982294f2e2f7a5056d932c982651047227aa02aec702012749123afa23167cf72c7f5b80ad16b5e2a47632fa10ea6178dcd2eb45d7038a1c0323b54c15690a23533b9111d4b56e68a4a55051d28ca27e80510fdc110b9660f27c6c6d7b759a7b374772b50a86adf70a6335b15bb5b7e0fd652d0db1013a8c5c3b664392a97df96cdff6e754775232706420b321ec4fa4f108c42acb0d3b50de4a0cc80b630d03e5b3795fd03daca4e0521cf3e756d9b5cde84d03ce0d82e7458616618cade9fb4ed739a06bbde12812bf207ee915c30a1d2edbb3ef09d924c1ac0403f29d8ec6445a03dff46389bade9233608055916e4e37748e7e06d959ef878ec3dab7ee321ca12722b942dc57f34a2321472449111951ce1f440f107043235109758eacfb050cf064e2f1014b7b806c9281f67f96201bdbe4910d7888c8fe6e6ec779cc9e0a74efde8d0b7efd8dec4c5e9f36c1d4e9d25bfe5a023b19e6229b246c1e80d91e11b01aa9cfebe13840ea27e3ea7239318451413b058f1c50ba2c575df84b36a1c8eff03595014226c6293d8c2cbe99dcbb36bba90158300249243487921404f107694bc27df3dc2aa244f4912e3817c52b2c89bf0cb349a6d56fb0d8c4a5b931785124290500a0ac2424e593f67aa0201cd7aa9b08b4540a6b9f52211b0873a19897743357b47222263791c42f93982675c2df640f396de8090606738b68c8b4a5c6e7ecf476bc465091b09edec1656ca40af2910047facfc9f52a4e83338801668712eaf9373149e917ed1d6ccfb91b3fc05faa036a673f2184da10d114cd0960c378a57da684596f4e5df9a5945f0dd7e5859e0dc85e322bbc0d175c4b73394b4769355c78933a802b38bed78812780281ee58021266337aab65e3e9c855f7d0c8f68b663759d0e3c0ac1453171b43b21f252100ea0fc271e555eeea14c0ecf486bc7591cf20f25de68877b7e1da7a16e3284f0cdc66e143f22982a6f3c7bc8d473271ad677f47d6ecce2994846fc00aeda7b40884c8f437d399fdda0626bd7236216a2ddd862f81ed0c1ead2393dbb146355d09810089f305f941e6d70cb693ec9198953df2a737e6d80e97e8a8d1654281ae00b106a20353f2db551f458065ddabde6dfc7669f0b940cc57fb0e588912dc1e03a4173e804f62d5a7daf511618fda15798c20dbbe035aa741794b9ccbcd1bbf03ce1896eeeda974d648e9e03ef88eb6e6e1e83ae63362d1f1409534b7c3df13058bd9c9137ee23cfb29618cf5064222ffed67e7e767417c0f8e78b598841eff042a9fc961fe79bc45bc444e5297a304801bc2a7b50a2f8f49c9711f7b9417eb05f89b11f4c9c4eb8839d3ae54372863002d091f1c66bdbc90b99147795cc9ab0073a90a1089c0a9027581336f1338699c78df44afaf5b555106450e64d259790063483e941db9899306a48c8de0acef8c638e130c93732485cf8c13a9c8ed920cca3d146b01ffb381d92bf5458f9a1bfbc1a6eae07dd735d34160a39b9735e90c9ba1cc5fd6868832f06615a76175d78868dfb1b56239dee543ba5f95736b4c04526a4d87b445fd10be8c470dbb02646da9a2f9dcc55c031cc60ac7608026c50bbba29bf32706d14c97a6b12d3d2ca3e913802e1e315319e7cc2416b479c8950d81f571982b390e9f6c9ae33780e164180de62d99dac304d2094c4c260a208b022b4449c896a5f99f4738bae8a674afb60d63c5360751ada5c3469f15fd75de046c907c085068c3170774522878c5483bac3ce9280680e864f990a7b81ca73a0395c80680777cef4dc2f3f1e12b6e99fce43c80e6908bfc4af32a5c5dccb0f106af1cea6a2744bb6932fae24444c01a75f0ddb8b00b02f5fc9f1cf57f85ba290350f7cfbf16040cab52265b936a6d0c7b136b3e51f3bff2af15a7bef14d56190ce21b7a871dd625a61c037cd4351dd6d60326dff2ed9e0c683908193d71d5fc051c96f4c3fd3ac00ff1a83e6c94a08c5be3619c51b138ea9b8edfaaf25a87540b155909f89593dc9e189c0f44d98f9ae3dd332c80242f4fb6eed8792e84637602f0ea1e1a0f2f66e39a323f15d14e8483e94763bdf5156188fc816715ecff370f77bcfaefc87fc1aeeedeb38e5a6b6f21516075af7bccf21f1c85c01cdf4ec4069b26dd67d8ab6ed384055f541a13ce5ea792cb52fd0653dfe8f7773662c6710d6a3b8c68ad96bb76a314feb3fe5ee6b084927b38f38922386a94691b1896df0fd479632f22cdece1de7d63b5f62c0c29f68f33cc9090021e8505f3674354090b9322699b4666b07afd7efa6c406fb67d9bf4e4a0cd02c45afe46cbca5dbadc0f71690695b7329a647be9bd9d2905ad968e99bce3dced7a9ce2a7df219c56d60396a15dde6cadf720b7d1c2254603775e3861543dbd8742c6e1f5170e2c0dd70f7eaa66ddccc59ff295ad5e92f7ecc9ed1b98704bf28cf876ffb799b43f7d16034c67d7399281d7256cc15573358b2e0c538c947cec2e73e1b49a2d2477353d037835bdda15865696720a21ef7512e8076fc2d3bd6113ac802c62bd1e584bfc08771b837713d2a47459d31297d1025859b03222eac174f25d22fcb6c27c475102 true -check_ring_signature c9f20d46a8081dc4d147ca8c908497e43d88a94cacfdce5afdb29f3e569ffe63 3b948a9c5fefc40672651781f36487288c2913cfead5689e48b27216c1f406f2 2 312216116fb3f6784094356a00c260b48ba1e0282655caf40b6764187eb3dbd5 fd4dba853b4683b38a8366556f1b96ce5192bbb1700e95eb87c429c463380471 21183e89cfc5da67e02f9ef37b7c2e95a57903dbe97edea68dd92e02d57b440ae3868cfcbe35cea154055a0844563afaa63813c790c2f7bf3ce5f5dc381a760ea41d6f985e9d8ab472a9f0b7858e12deec4bfedbe9021683bd6d1107f4733001bf18a8545ded2ac91edeaf898737b4cbf71c0ddb6db4780e8ef4b6df5a85ef04 false -check_ring_signature 65a9bc2dda76f945ead871beb5c97fae0558d6ca11b4122e271a2ddeedf1c8db 22a9755e21607f2554f5993c24aeb31044e4df69be4b4a644fb65489a264607f 1 e20f55cb85d4d2f70b5e6375921a9ccb307fb94d1a6e935d4275b60f87d24a0b 1bc9b77c0cf07a49f9924a710199410035594e580ca84b6c57a1609ef7d301365c7b87a105722f7547373509d5190f48863c66dda8f78f7c23e7568e23deebe0 false -check_ring_signature 66c167ffec24bb9ef57e8ae77e1c65d14aced4c56a51e135469070094d7dc302 e43c4d39355c662ac1ac47786f298a65514c6b03910e37f81ed0a239f9bb29e8 95 0b9fce587a43127b1a9257cccc96d558876a66da33d4f9ac5d4700c241f793b4 41c3a3dc2822559f7395be480a7bfe07acdd514446bbfda9c65458f08b9d4946 15116de2d7465fe26e003ce0c91b18518ca7188b98248c4db27020da343e28ac a5e80b63d9a700d45cf275f88c72f057b7ae52b6db4c332479b9569bc693bb79 e124af6817c4e8500366a987011aa7e1e43a3b27eb443b4a69ea367974c051d7 8f6fb3a1519e937ad4953aaeacd5ea0c765a9c6101ac6e8e23382f78400880d8 5080a5d8485b03c954a658705a97993cfefd49981af50eda73b3ff17b79ecc87 f2d6de6887fb4827ac64905326dde02dfb6774c38e831540df0a7bf2de5a803a 25e6d77f165b85b47a03ed652635e5fd1d0797be387b85ac878191556739e7b0 dfabd64df9299125e47837bb68ba5c2ec2adcb006a7db1b08d990faf6212398f 1cebec1f64ec4b7ab56201a5a4e276dd05e80fea103ecb41a093328aaf77f5ca 9d8fe09f648334c6faf5e77d9324ddee4f3077a7db5dd82db0cec0c99204b829 7fbb9e3091aa5e96220a9d2f0431b42d0b25b512cf3ef860cbc29757bcc54f56 8bdda1e94aaa26dde8724268ef8a37df0577b87562cb34df13eb60291265e4d9 86faa67332e0eae5a9bba0d3b6c2668c4596513b0f073ab88def550f4bc15098 30bb6138c125ffe620a9fe57648d93487054688fcc5219bee5d827638dc0759c 24b8b6d06cfd234e178e045f34bcba74ef9144a3b265e84ca0e06140da47a83c 4da0f7cf92a620de66fcf8aacacf9b09d948781ad1bcf0a2a66d8b67010eb6e9 b1cbfbc457832e4e89e14624a2edc0147a5179459080050978c4e5071d19cb0b 520dff601b0f6f64a4985113d64494344e2b835458326ced510a8782ed0eab3a 42c2c4bf63c996fdc387c3336c753dd616f3378f019184085be87d553d173b9b 86f624facf228bc9af4072c87f6d3b6d04b3f0377912c1009864b224387e2e2c ee51646849ed5bdb6b1021998bd715477036703c367e4eb6ff0fb1657b3c4046 09c482a5a00a06df3ba6af980e3cec9df7053ab944a052564cef5ac66c3f1591 e3fe6b1360ed472b82d3eb88094e8fa590f443a772d1c6f50876b13a3c08680f da2df5305f616a5ba3b4bb1fd93f5fc2d6394d2f5a7dd6f41bebf9b4bd1a9f44 2abb15b3a71b58ca448387a743fca2ad7f3ce041f3193d0cd9f7bf8bdc8d93f7 489fa20c23707c0ececc3c438bcdc158d374ff7cffd1cba4160d8a8dde2c6e67 07d409b984d2582c21ba02ab6fed9f9dc0c34e9af361a9be26c243c2ef8f1916 3c95c6580e4405b595eefcf0a3e721dadd4b8863105acb854c328793d42db56c 38a19815a1bdf73f9daa4c780691bdddd500099cec374b0c07537e510a1bb69e 08272292ef106c6510101d7db0ecaed3813a7f7cf6c03025207b45051d3aba72 b6b83b7cee72a8f595457c33d0dea9f2927276a3c569dd10e8307010027134ad c7d03f5cfff89c9f6b461a25a65e5f2d12eb10dcf26e485a2d371c98d96525f8 1ec1820b4c875b6d549c4aad462557279a667d46c9891263592bfa85cbe34eaf 49d195116897b06a51c5f03814900cade6f726c23fa6415cc9c460b607a7a98e 306cfaf43abcbc8524367ded0af1cbb10219131e58cda1651516817742ccb884 f635a55b01b3ff255904e72ac62d8ba0e714ba7d8e092280a468ddd57e26be0d cbcb53fd975e051b61f873e122524a4250a70f34e8abc55d57e0845338b794d3 922b743f263dbd6a2a9582340a868154984e438d27ccabe83c2d7f5c0f1a7860 88b64b3d6feaee38aedf3e4e687d6a07ca003693e9ee6a1986054ec401255697 e4db6e8e050d2ad411c2da19ede306282cac1ea838e7d12140537f4e176acd39 f285512e5d288ec2939ddad8cf472130f437e156642bb24a9fd01090c25fa813 bcb7a3d51756b5dfc868d322a461ce418a28017b0366764cbd4e5f26e5d7a51b e11605a7775e9623ba772448e7c2542461f01ce110c882a628ccfcea4c7ce400 f8badbb3235f790b2d479418fc0de7cd373c9e241f4fbe5e2484c135608ab9b3 9c681a7bac3442e9a4b4eac2e17b4f95b590d151cdb8f174ff7d3431c982927c bcde8c7fe118fa55b6bce6931b07e702c769b975a1e0cb4010a9893f6290cf4f cadfba5200643555fbcd230d6ad82ea2c10ae407df8d8393c35c06ea5c694788 f0d49ca6142eeb7ca03d1f62254d81791be143494dc05dd10a30b718e8a0a345 e65182a31820dd5be4832e35090edb69a6ee93e8622f44e8f1e230c66c954165 aeba6472d8d1bf2e8c668a38e35e58144d45c204ce99885d1107182740c0a037 7df37a7c38dfd7149a69e9d1e5461bc7a4e3ef1473a0923d3e9fd23b1c54d533 d9a9bc1f7dea44ba832baf68099d07471a6e979c065b7f7fc96aa8f90e1a2c96 9196cf189f43515e7fdb31f48cff40507de60e8580badaa1ad49647786c103f7 d14bae874bdf4c3ef6f18bb61ab3482797808f06e27f7606bfa3a0ca2134126b 86806485786145cbb9712dac37e727978c1e94051d26a1fe87ce3f764f595c3b 838135c69f71716ef1824b6e90be1e032c0e3c8b8e2fad1c5e7182a3b76b231c f366305683bdfa55c3ad1633a0ba7b6e580718dd5dbf554eb355202b4ce930a2 80b34507a87be6925f6076ef43ac1a308d7cef4e253645011f1750b0a75e46cf 64a8357deef2d65ec0cc3e749f418b437b62ab0dc3f3f150db0abc9ed1309f8b c0ed3dda04c874d142fc305d998c5a39d444d5fe2724300633b88094787bda5d 698b2e067ec96402930294ebfc71d4756c83870b2df953725de42097e8a1e4c3 7d54f21684877bad4ee0debafafabba0e579dfce08e28b392d0f502f1c15af6d 02866924a551f1e77d9deeebb6d31caa907271c187f1055dd4a1352a2275bfb1 f8f579d1341d2d173fd85e634336b63ed7266dfb6dc00b9e29322302a9ff1f7c 9beaf54077cf0738555648b575b8d2a370e24a88b4f9a3f877df316447b7e530 ce6cb3365137a1ad0e0000f97be79a8e0655d72eae9f42b887cc24f47aee0545 766bb1fd14af320764ec6ff909b6a178b0f3a3b9b5c48c6e80f38c2bdc105e0a bcdf958929d869a139152e975bdbf9fb7d0072e9152a1821c1b0abc736ae939d 2cc9f63b7a235d23aa62f00d3ac147e6dc7c9d84b2b19af27d5899ae8ac0f2f8 39ca3cded742e96aa9626edb9bd0deac1d99c6c0db3031f7318aaf92aefedfc2 50a69012dc38865509c1d513c11d77651a5bf8aa44217336f625d2f15262a9e9 ee3b1e803baf4da087b83bfe171a7383d06914cc081ac7990d0704f4cb819121 35df37b739660dc22c4fccfd92b92a54b7d0b4155c4b162b17d8a7d1f0c9159c 1f5452397b1598685904652e529f6528c18041458d72be556be84b6b3ab03891 9c9acf23a6b502aed977211f32b65f8638d3e4e7925452e2e0a5f0716feedc4f 7b30e4e085d0a9cdc84418d23963e7dc97e0c682cf1a3e7483add82c0130a5e8 4e458bf4be64892cfd87a390f6009544d253b8bfdbee58249c8a65e6dc24fbec ebfc009f647d8fa94dc5b27899e047a9bdcb89e2a83f79caf275a0ef8119cbf6 28f859f402110510e5fba0810b7d019de553d90ab61dfbcde658e682f7fa17d5 ae54c4afea0c32881417e45e6844f04f3237d27ab9e3caf6f9e2f0e88dbb0727 121dd7927c8c87a807fe6ff56b0f35cc494137cd8900b23068a8be4538f7aa26 84a2309e245d03bda8897838b4e46e811b602cc5c402cd4c6da2ca536ce66143 6250af54235348bb8c7f002fe67e0de8cc6d7d4a01d5faf308b3f16d59703e5c 708fe2643358169c99526cdc1040c4ef78bf0a7aeb121ae5d5aeba55fc197855 96f61439cd72c4879153e4231467c59dd2934d6860d055ac32ae477bd38aa6c6 a20f585c413caec93bab27e6503bec00306050391fdd1be5a566b50defdaa238 bb94114a82e8a53bb15e36c6a43958571c898a03cd6ee9ff63460918d623abc6 d7606ebd9c1253988d6899de2efe87a2583865d7dafc6d4f9c3c9c4033cb69ab 2fa242c9bf6962524b7bbd53a3df01e24d39564426c0fa3836dd3d885f4e7fbd c3f453b6b70836e29b518fdc7b0cab45749806b2edd8253ae665cdbb346c9fac 213cbdef740a2549d17f66ea7eef5e08b7682e1cde227bcb55399ed7967ada29 1ce08e428f9c49664a0f2c6c5724eb7ecebc28f7b07f21f96ca71ff39687d36a 2d22ea7ac4a65948dd574287f6eab6c7e86b1b4ef8ad4be9e2495063ecd7191e bc97f08362f7ba810f08502311ae10b8213b0f1e13bac3477a6dd3419b580d0d63ce1ae388de0f27eb239d8520638afb1089302a88f0e968b3f372a53efe800860fdfc9ec79804ff0fc5ff79aa9a427098d1e8f00aabe09c82194f4f4bbdc405c7dce9ed2534594cb58fdbcca78f49a2a7b72f6dc33505c6d8d02d2f24802f0c1cc1eae713ada236fc43aafa3b949aa7b0ffc23c9ca535d75e12c3a26b9f7301d717e4c239ce9af205acd87b095577474dae763f3b4896fd4abc8fe326c2a50c1c6ba56f6cde28626f77e45258e8ea72a5092e6a8326284dfe6edfd01ae47800638716230fc77a36cfcf6a49df70ced516388b365c81885301d2a04da15ef20a35611fc18afe9f819972a17f4541d8d02644b2ca637844b1a795a4f3a008c60675116304dc8d2d2ce5cc807dd64fc6f3bf1755d155f3b3e111e3fec3ca8f9d00935086a6fac08bba58087678e3ba0d22debafc49cc87a7b709845bc2a0a8570e4cec27575fb208b6da9f6fc53d2b8b1b560dddf197208fa7fd48652b5816cf06fb585c6a43fd5f9c16e4d0dd19ad9eb62f872008d1cb2db953873ddb7f443a0d5be33b2bee9b0dd2fabdb8ad23eb09b46420e14d436fb6fcc58040c5461225021de380d9d02a53a489c9ba19056e9448ad04377dc97e5f6e326a8e6db6336c07df271e7534d30862958f98660dabb5ff726eb2495df0ceca51be9a9ddce5fa0ce42329844f27025e714cb1a5e21ec5f85470f754558c8d7d6ef2b33fd992000d35e5362a86a72025c0d9b44ce5a24cbbd91d89178c9896ebb2cd04e34c5aa40bb541e223cce9378ab6511fc7c592f3211e081d3e96122913b42ac9ba4aac120f1a997781e25c622388700e7b1dbef94e7d336f953bea7e7e7bd24aa7764d5409b347191f3d631d7973c0919c696e1b778f9de177a06e887af5fc2cc951c90e07f583837a26add0161e59a9fe7030cd3adc8361e701303aac383998d98e8972069ba499ee5a8fb15fbe3aed4bc379975bcf1e071650226e174e3fa59b214809089b5bd979197b26d07529c2432f0248cdc37e32d3ddf02460096fd3754342bf0d77f3324dd9dadbde15c6cbb1fffa77895fffd2f753c0434e7ca753d4670e93028f93cdbfeea7ecbf4326bb3a4a866c0dae610d0f8f91845175f21b2f8e8e4708367d5b7c13a545bd2dc0281d9a2fd56822d8a755429bdb9dc4d2937926d6060ba928badf3390fb436f9358d8c6142c37d100cce18af57b1c078791b36c67490b1e78b6a68b7e16c26d8ba7668232d9495770adb88cd7901afc8e8547f3bd0c09a7e7667f6888e8598daa3ed011306db78ac37132652b3a33e23ff43028795a0bad6ef97eeab1dbf09d3bda98c0a6b88f6156836bbb2d47ecff9295104bdd290b98891a1b7702ae5100ae96a1217a295aeddd42a7fd5d0ff7003e46431a16930ea2c6eeda529a0ae00c32bfd340302d5d324fff1b66e3cec5f162dddb7dfbf70e42d1e8316d4cba5f06421e65a1032addcb68c16be267f0bc27688d286e9a3b0e6da3b8038b788836869a734d7ef24bacffc05d1b2c84652eb705aacd2df2b90ba6bb9a1c99c954e02173baae87f87d148bd9a0e7445d7c83722664c4f141b9045693677ba58ddcd8310fd9d5cea3ab00e5cbd90fb55711d243b1b532c7214b0dd513c4040dc154220f027901ab9626837f9f5aa42465c4dd1ea452ed5b1bda03758f1d1140a75ccee1d8f20d624b97f25cced500f5fc3a96122471d635148e08130ba82f52e3c7d7ce9107a99a7c0de98acbff4eb6a354ccd37a363f97262d02c8ff0b7410cb53f5d5c08c83ba4ac1d50134889d636c61883e1ae0bee5c19f0d77a51307575eb4800ce8da5f0417bd83a18f85ec6ed2c07240f2a57a8a48fc06a79c4793edc6a75024626fa912451e7a0b3fd4b01a0ddd6d76a30c5666dc350254a28408b7faaad8999ce341b7d6d59026e910529ee69890a28c17bd87374905e54a357acc453fd99eb195b12d12092b2d983a0ed0d488e763ecd0c33fadb40630d0ebe7c51c6b5dc7627f9e9afbd51f7c71ad092f4418a7fe145f0376a293041253ed49cc9fd59bf3e01bcdbca0194592d54e18c7735616b9b85812f68ac003b6fd89b767b85ef7560e83ba5c629f3772120a4164ef46caea2945c5de69730fe7b302b093edeb91fda27910c42304733559d2709e920a8e26d43d667cbbad00aa3d4f12761c07c0f02f350f3317b8c2039c1edda49612d27852240cfb22c40a896578c703b4a51530f0c07f9873d36ab1e6f100e0abecbf56ef6d669f6192030d92da67e34d91fe50745bfc64ba115bd1d9161f182156b4ee27e878c1ca720046282d72a88666423ae0f5e0deeeb27434cda023a9d58dcafaa1c06b610243051833eb583da81e6cb65e59714c6692f288f92793e947a504d1a1e49e6fbf680e94aa88b4f5d512cc3ee37f2e138490444e12b6a9d8f5f61677b1c25d0c33fc079df7ebf81d27a7a0740fab58f5cdbc99af757f35122d02147fd25f8765891702fe483a0aa7dac886725d3739624c3f1f702ef267dcbe87db6c2a6d21b115ca0e84b28550c7482f712671df0bdd5f54ef739b677f15d2259adb8a0163ee79ca0ab43ca884f8843ac0351ce19eb2e2f69c1acc32d8389981a52357a193500d690a2ff4e0285a801cc8ac3f8eb4c5b7de9bb908d3f9acf37f5d442e3837164b2a00b4c36a2617acb2aceddf7703198a6752132c7f3e0c752ee5fedb6395c1574e042b3847e21a30ea3dbb52683b8e7c49aa603b82ea40e8a695a96892c479011d09952fe635a22bc2933d97691353fde13cc1b1bf7f3bb52572d6a1b503d928900a6dae1568d7560b399bb0a92f5b1913506e0dd720c0957c3f604e55883e9bab0bd05280d9450081c72b1882c7321b87051ed02cd87f89643a0ce4dd3f25e1f007681be23477af0bae196d8e0dabfe8490ee7888f95ce020e2441cc2fae505fa01c3341cc653aad7c74f5f6dea54c086960e26723e8c884c3bd8f98aa3c5a5c20a09ec914495148983952e50174d78893064f08aa9308ae36494ee7ca317bdb50a14acbcbd2ce08ea9258324ebd16016be2d69d20853555e8013325605d232bb06c5ca218b39d8cef6de1872565fdf87f6021c9156bdcda2c58f4fafef10d93002c20c836004eaa71318b0f393e674cf875973528e2f4f653439b1b03d6bfcf20d106a2294243f7cd37ed40489f5bb2905daef399001bec4dce8fcacd54c44b00604c11afe384a73687fd1619bd43092d4e2cc50a88c8f6decfff396ad444b39011bf4504ab34645beb3c57683895f98ed0d10b4e73441adc1e741929b1b72390c522a6c673698979c2acf61b4c8a00813f572470af5e00ab4fc9bdd69e1e6cf0b5841b8d832234b585d374d30aa1c5ba792c406735c657f87cc505d29c64aff0d718ca37048f86baee03bb4da4772ef2b8b28a854d18b300830b22337600a2a084a1f37b8c81e4487d29d3a6c402f612015f968a8cfc5d1fbe6be11e78faca3059ccade479e4f9ee825ac4dcfd198976c47065ce73f263fae2763ee2b4ac5790e5c120d8ffc3c080634a13a66d7dc83d900308a9ca8ad055d2244f81a09191801c352be302740c872ca3b351905d6e73c5cc629d4f8a9300ca3bb959bfe6d5b010630d8d51843097de3ccd564ccb019472681e41b17b0a93cc1789c163efc00026fa645f754d1a5dbf5e84b0140550370c0b44ac241d8aae949590cccafcd2b0f33acbc6406c9c3c651d7ed59aea59e42a46fc820baf9dde6192e5590212b99047f5345bb9513cb9e36654db71f808978f02f90f914e9f3baec283421d1c48c0c180fff165a33a762bfdd748f4f635787e3d59ab917a2ad06d8aa3d7e3cfa560f49d9c931f26f5085546345fda23b750d637c7044915f0bed4fe1165f4d5d8a0004b63ede29599d6c630d98f4eca0bbca26a7d4ef5c2db4257df94692a2f9240a8a7a0f9f1b92904d7dc281994e2305766d4d26dc15201c482df6c503d31d5b055b86661780f5147475fda772c14da540655f6a15b6d2621ad239c27a129fa60a247edaba214eb92071f84544833c6ed3a9480c00800ca0792edb782116bc080fd05d48b3abd5efe1b2d7026a51b198830e2da900d8e529cd7ebdc885b1be700c57aab5e00185d66852cd64bda8e551d47567ea789f919a4c8bb8ee9684e99808c6ec5d7c7342a59c1705ed68aac2122935ec67b3d1219dc00b0d2e09c822290ff6c76b27ca0169579412857458c6e4871b080581c3d04cdd01d68671058a2b097b418f9fa13ec5c7b97c80b0bacf7c4b43d09556f020633855d469271be681077727a2462ff8ce2aca6e45709071d3c691ec059db038e6577b359855f867f5001c63438d34dff6571af31e05d7eb9e8b78c655dacebf7dbf9a63a495a63f6009fdf835956dba8952a088099f921425d017c191c5df7279d11cce0ec5db200f033e6dbc969781991b1598c3c1daec96c531e570954d46366c33f138992fe0040e30fa2219677c6e88724b0a4cbf4e003dd9bbd631364658773b77cd4f677a610a195ef09433f6dfbc2b2f34fc47f83c358d1a0b2d3e4e0f92660762204f4ff408f75a90e928fe0083a541dfb330da79c0747e3705992d5dfd2cfdf2813da8750d7f8219ff164be9f7508f4a6c9dac3d3907df892efc8ab484287ad8f7ed4b320ef5b7b11b59299ef17d4fae3ce6534321cb4cf7778e780cfaedcb99b12d1cb604bc601f980dcd303fb8b6593d67d2423d2ebf7e5824496a4cfada1653fa3c3609df15c9424481372b70d6c5caea3d0bbf8cd17d3b6a0ec43c30f8bfdecec2eb07b01d5e78a3ad1362d7625408de1eb16578b466420a1b27624149e9d137837708391cea45ac156ddcd5ed207a31b7d2e55b92c711039a892cd25abdcdde529200a4325662e60bff8cfba291d83ab2aa0f9a954eacdf9f880284fa9d96dd53d907a81692d8be48fade309808d5aede6f5346891a69793e3eacda82ff66995f870125625061d0b482ba9b8772c13adc13617bc8bd97ac9b146e0d0ccf50f645bc0c35f9c0eb402d8deab5b3251983e2fd62d8ea043806fef937c43a491f5531b103a17d36e1a9a30cdde6d4d5443427a311310c2de62e63bc2d2a8b6fd609f6e66cda386c6625900321665871160307c3df759e10465beddd26e46a827cbcf78a0e49896259ddfb897b3390960f5d14ec1a4e94c79fbc37fd838172ad3b87ad86022384f50c78ce40061ae0418d416edcf7c487cd52fd88495e832d29cef944ab0907d86e6a5024ab3f0ca9c4686dc9f1a531b814e2828864d5428fd3d504899903436b6c3c78c3de7b09194687851e2027db4543b606c0490b0d52b42137f3f303fb5d757569e1a1446d5587d54b670eaf8f3e4b230fa74f55f169596e7099780f7ea33fbf753803c29f30cbcaa8793ebe158907d6aaddff7d9c0c94a593f3c302447d786f0de78952a984d62be3ee36d75b1368d597ffb29efd71467f1600d8089f2786f76d98a5ad4150366c0d5040bf8b38db5b44bbb0ae4144a43acae85d06b5d72142bf3c8611cdbb14fa402dfdc8bd59adcc98ed85ee3212988e0d71da0eb4e5596a00e8ec553d7536a8875dcb1226f6c269d51c245db51f60b6798c14024a3c87018eee7ea271b4f505769391fcc67dba5a690b9c45ce14172bdfaca80cf846325188aef8fa5f726f0aeb6c38a661f1bcb56198cc6f34591c2f8f559b090cd978b511874a0657b8200d92d89499c05b8f7261c40c5a4495ff57b550f9090aaeb3cef5c2502304bc6c00463c5d4c89aeaa8011b024d3c5c97c2c2e752a023acc7ecd07fa0da9caa58de5b5a456b8939ef747b01af9ddead31c9fd076ef0c76883a2bdf65dd3fba38a8ecf35fd92d033b0493fc84ee94d8113b86a7fecb05a88093f855be3550eccfec80d2a1a44ffa605f25fa481f87fb178b223edcb00d0b4df6ef655709ca0defa8fa46d32d9e07066dcb276c5f1d094f7b10ea79d404dc1eaeb0d82891f96566219f27463f2a07f325c756349ed592b45b992415370f8c5d5723803a2813a04ffcdbaaf1cca3109d686d75e15b1165b7c7cebfb06400ab1905259c7aec53912a5e7af0c27b8c23e25d57e68ded6f9a0ddb6ab437c30147384fbab4609a400ea49036baf465f24d4dace23986a1b401a5eeb65f75ef004589efd8d6cb8840400da382a9d25acc4fa90ba1e6ffe3bb09f05bba005c300232e9de4a597d8715cbc97b25046a09aa2958db0c7ff1282fbeeddf80a06c1c0c7537c011be864cdb385f8d8f2f1e28cf7e20e9ebf1f722a05d76dc88f659d101048aefbb00ed5a1334dec21975ab2b59b8fd13b3e686c6524bf1f0677eda9c02045c21d73286d4f0ebc3318e8f0197b48e0bbe3296959b9e64b6c576d4efe50adbd5c4f5b2cd819e62f13d57aa26fb1f6c38bcd98908a7acef9b418c967a650968a8948e66861ec129861b041e0ae80305cf3155581ca2771faeb1499cfbfb063ac3bc923848aa73c823f5b7b12697e1032730aff63cf1674f4340b73cdfab0d0db20d03a97e7b35abd59e3c4255351a726f435c1d24823b1c84bb832be7c20f38571c9c9281512bc36f97174593b69bc425b9047ab4fd3e43011b9d95420402c2586716d05676ab231c7548b7a2ff8c0bceb9b2cd2b78be9927d6b8670d530491bac65bd99866e7ef2dc1e87ebc6fbb138dc299a68a36ace196f0f9b24d3803fb0c630c8b7ee185aafd72ffbd70a811e756eef54ad68ea3ac91425249c1510a52a7d340e5cbd38eb924e9fdd3a75bed9132531d60fed5de1f0514d755f9500e27bc7f3fdbedc96b0a4a715de0fa241341668f79088b80b185dc12622a6b6809f0d777812008b68e03d37c2f8f41b5e03ee247e885d6201cd3f3efc5829460074c3ff3cdc2486ded7119524bb07b3598a4a684d7151eb2d8d29a0d7cf016a901620c4cf62c740df34063f42503a64abdc16c1fa35ef92a5ee5540d748d6bb10abd51e97c590b4e619df3406ce873428fbf77c520ce4d4ec317fc3a1dcfb5580c18508db5e1b37d368b4bddb76060ef839f69c0848fcd5918079d53bd69a2900274d3366996148ce8f729b21199fa644394d2a19d26cb1a8707ff3789ebb962031ce30693da35e3d233cf6114404f2518467fc7343f7e1508d38cfdfd49ff110256223e8b467695c0fbc25bb0f4d4518e9844b0cba404f21f8f8089a96a9c6300845d74d4d1098c8a7e888a1ca84c65abd411ef459f3b6411e2c1520339d71e0cb08b0133e748895b815cf5d7606a2d55f773ddad934aade4e7e79d7f257f6907cc4efabc7bb204ae8a0339877e519712489072917e283b9270bd3897aeee4f0ae56fb4d1a408d8e31d98b7e834ed6c745ee1d3d28b10efcaeeb9b54a2a9e3205f331a982226f77e6bd36d9cce2e2d92ac9f4b3f8593708e48f3c8091c7fada0675271aaeae82cb564fe96c0f27d30a9e6ff24046f13df41ff47ff307618be609eefa1273a15903fb439fd467b7c4b56d39c3d516bcfdbd9875ae269b5b196808bb3aa26d89ba6cc30439b16962557f3281c9a200f4fb3d597ea8c797e967520ed3a0e532e22099e8b17c4fb1ff442edcb672d9bffb07a2122d38460288db7202ae664bdcea27f9efac6cbebaa578e2c1df9782dcbb9fc12638f2d71b12f7cc065eb3c00c63e55d766798e761111da45a54851684b213a5b13acb703d9716430761d33e422c8298d82149235c59a1fc08f963855069c5bd157c30615e6d9ccb0ce76a694ae4f0c37ed675ff2d826afd5b945d2611175ad7ec469a87385c6f940e0dffa9541b8fef14c56c2e5249b75ba0244a80301e74a83e610a6ef09847fd042c86246968c46459dd475262644330aa3ec9123e9143064b142dfb4e1ddf04021813bfda7ecbf7012ae2e8386a5b33797166bb733372aa44801eb5d556ad9a05238a893976eaa514ee310983b931f4fd74cec58555b30ac51082ace024522a08109c008f7ae7659afdb78eadb26bb4ba8076a47cf684ebdc5233bc13fc51fa0226590156bc8696c92d6f92553175abc409b6168182903cbe3b26fd6217c47c00198c96393273c1391b446648bb4249b1316f113b0eaef0a643983c35783b2b03189489d259cc88273a073774b9b3176c10a2682de0095608af356b362b6e72038425700398041e81d074505e961c3d3899c96494202512ed96f3c2c44430ba0a56844370c76c53fb359b5f2de4e671a921d327326138ab9d2f2ac180e2e5db0c7cca0050394c73f718e936d170d736aa49eeb3cc846acd87d9be958e13b9550235270239a593bd10562ac0721cac3bfe4ea6c2d1bbd829b2f44248405ff7d801f1d5135214db025d8c44d634be6df2555d1c007efd7d593de1b2c53a88dd4206d99245743e283be3a4d146288d74d2657657f65f2106e9a2b0ac6b6ca7f6e60e40b2d362a0568cb65bcb63f9599cc1674599e10013b859c24282b1eeb71cce02e1a949b59197e66f8043211845572502497c947f200cd8d755bfdf63cc56b404bb798fc5f1900d6da6b54a46fc71fa8762b965c31756196c4e933e43c49dc20d false -check_ring_signature c15277871633ea4f3d9c1e9aa201e42d42bcfb6108fc91878ce69e7022032993 16793c87081bf034370d8751436bef3a2614e7d345d897b8a706e787afe543ff 34 40949ab5c247a53bbf047d55b689924dcc5c320b1778032ea29661e3caa32f72 8b532f25a3dc5d0fb4783d98fc4f7d7a3080d2b6df6a89b23413cbad3ca627ee 39cb00d338775178e55462d7da12c4e075aa46c413f79bc04c295dbfe9d80049 b0fd318475cab2a31f9e681773fe1cba9955e629eeac9fbdf2bbe64c21b8ed39 4d2478e5b08d1ab02ff578fb9107dd664c9860130ac79cbfe502f1fbb9521441 67c6bf7d11954a68f7428650feef6a227a07424394996b2966d1f46e797473c2 8dc28e24eb284a5f0069639b202c6c9409084edcbc2b572c8bfe5faf3b6aabe1 6fc120c25c2bca7ef2754f11d5df0bcb538b883da60e54537814143f193c847c b9036dc88f7b070e1f9efcb63adcf1d92d988cc2642f8a313657f28cb89f1a63 4d45f7199a5485fcf509649538025418e0e5cfbe24eeb7a10e37e6e7dbb41686 c3fefd65bb30a92376d7eeebc971a6116522546a1964e9dc1c5a6a4d2858103b c858c4090e709c76d0825494af7afc73d48b08bd65ee4180715744c82634f428 9ce01cb1bace2846548f583b3b3acd1dc7e35851482224d1ccc5086bec82ee12 01c73bcae72019ce934ee10a1228aec07529f2e7026905fa0c16ecf94265f996 3baa075b51a459e937d5ee676f500e086098393d548501ebb045ebdf6765cb9c 9e3929bb84bf26b9814fe0044df8bd0c434d5a195b94d7b911684d0a3020ddf1 823a00c0cfd45652cf50a9103063b2e136404f20a89ccf21f0a4cf57cb72b9b6 005634b15e1322f7fc4f5f807e2e815a565fc4360b07cd332d03c2ffbc5f863a 0abaa75a0c1c17bc02b2a151101ad4c8b2da112c517e3b97a65dd40103544ed1 eb8379a1e37cc9bc9bfdb3edabe64526c308c9ef00e708ab9f4959ee4ba5ce04 5f0390a40424b6fcc1a8eed480d47c52f86d1b015ccc5b95cdde232fa4a796e3 c57ce539f1208da0aff50d01d3004848a7a1269925c4eeff3cc9a56fc68669d4 5ae3dcff040f36095330f5159c10bdcfec1fe198d3ad25f6bf190f4611e41b10 0d89dd656cc755a003281a6673a1fb8fae9663436d7ef852913df50a9cb6e1d9 2f1a78ad511fe4dcf2175ae03e894dda96cf2e70514fe84681388d4f2b9dcb1c 743172e1f301916eb21e6c39918446728ff7b80e99f655ec4172d03b5b17b8c6 6d2a08f72c052157289ba72744405661f19e8c8fc34ace91ab4b63646f3052ff f50a39c117b09d8d5ec857a62ef9f1c6f35cc908bfa07f6895c1d7b0bb70a4b1 eab8b82b9740cc4c4e705d03ff8023dc99f39aeb3d37f1ad46f19e72e5b19ba6 2283bfa001f0f1086401047481b90651f1cf1828b8e1233d23e9be460124e265 636dfaad60e5544383e02f739edc278f083523086e7e8885b5a2fc3088e23573 44b3789147cf3e4a8814d2b57f1cdaf6400c0af051355fb9a37c9a315aa52d3a d324aa7c8fc844deae858cba2804175bb15c78eb9d25f0236c3ca8e9eb3581f9 abc28f45a261b049c61f8d5546156bbf5eb5ec9b6341f714d85c4795c5e7e457 51ba6017e16b147c25d6e392f7845b3c1eb09faf63d057aab61b394619d58904ff644de45ebe89848aa7d03aec9489ead7250054637487177f3cc010965fc30acb9c8382d21975762351eede71976969b1389c6f2b27b733b2f04ffa33be500bdb538e980ab5710b278d6f66d368186d5a259b8ed5cf107b7bbf668fa2d4ef0305d05a4a325c50aa005169a5d9b3b6448e36654827a9412d7824b9902cfaa804134cfc4329fea2e53c342e90c58bfc0b9fec631692a33c2a486d788699ee5200d7b6a07a9bba7b2ec97f6f00907d4b0f138b2f0e8c9a7ccaf360b0481eb6670048b9df5d2b74096e81ef60e75431a198be9aa84c511de7a5de2d45b3b4e36a06ac3ff39b72b341cccbbb8e0a28093e6d6f8d57c0cd092a49f0eb0eb9468c500b3782ce684529237bda055b521fc1302a59c33a03da01dfd4ccbac85156107a058e04deead3c33b5bdc4069309c1aa8ac53611742145f748514d7a20e4f9f3b068779aed6c7a668da6606c1413355389520ba813f3775d443c24f71a9d770ce07619d9528ef5abefc93a9ca4494f04a59c2ce898b46c2bc67015e37470a04a903c3216445b0e36b157ed20c18258d8dff643f6c00d9228706e8855705358e150f8d784b6a384b187cc0d6ea050c403fb7925c2e27574de448d7d0a4002a30b108253f4cebe1400d3f5efdedf700453df3486b68740b5a6855b324c4f27ce06b0e03b6a7cf8de30f7de1b76411130a3f2619d509f1a30a39cc04c30371f029e0035895aa97b2c05d2ed990ae4ef817dfbbed20d89b1de7e91dce29073357114001d30ee62c80bc8e470bfd2c72968993ac7cb7a6ba70d7be0f763e7fbf4ed477019d8b05f3949612d8511692f7d291b8120f5abc1765acc572baab8f2065fe3c000580524f826a60e10f730c778f7502e214442f82e07b06bb0468edfd15ccf40d0469adac7d187f1fa80a45d2429dec668535e742a92dff08d7ea24b856e6250a0402fa0b28b97c9f502f3db369a11a1aeafdf0340e86d58d85742f166c83040f4e73e997d0fbb5382738ae56626a4403b6aaa62fb5973efd93afbaca57341d0479c6a856d559c01aa49ed11e5d8a0789dbf7a8c9acaa844b56e45c3a261e3e0d583195d7519f9628a81a78038d3eb9e6d7d2a31e7e54a807d0c73cbfa26ec6086c819bc84793ee370830773da85addaf5a63a47a63830dbc6b2481fc8e663a0540789dd2ed7101ad0bb354838c52bf4cae86cc6752d9038c912e1a5f81cd700802006e7195536cd930fe39a8faa796138d142703440f0ac7a21ab92374283c067e062725bde3fc69a059f725e44f413f7f80383efca019ff05a1179cafdffe06ef8b43f82d8eec840b1466c7da053d7290133699ec0014367f53f9a8bb1ec501aa4ad00cdd321d0c24fff9da560870dd1a19cb546616c3b6f6de955759870f0d33bb2c579265f0cb6f563d483f4fc8e61c4e98b19e5823db870f6922ccc0ee0e8ed1d3d51b34fb01a9207401d1f6692b5aae1cc410c1ec9c9c73469656c5870bc033922c335795d49ebcd75251f83d0834cc63bb5a3cd61a6bcc16f20890f203de9f7e56b74691ab988d10fa1a39e7dd1ab80d5bfb3b75b235b4bc363c6b5403eb98bf77f967688ba2ce4aa8e8a735bf449b16e08c756a90df340a65491c8f0df95dab7aec7dbe5cba13c4e20b05571e2b595e1930600eb2434a93dcd749ee0cb75bda75d9fba8b41dc8c6757428c30ff6b353233b460643bbab2f560f6e3008528c645f38472440e6e201494fc7623f3f08012ecf57cde8544841f43e882c00aeb863e32a27e91436b6a9f8de221c6077c7babd7b761de0f664eefc2bd6e30b9cd71cb1ab1e12c2720881d83dfb16e4127ca35b63a68037031383d184914a0b58d861ec8a2dff36f1a5c3839d903a13f8edbece182ebc1608182c430056170a91fc811828b25fc55f5d6ed25bedc6da71feb25605488f0b65bf4134706c34031bb02076735b7fef971b8d0afee7af979bbe9e97339ad57eeef81fa7e50bd502eaa88bfc44c63df5c36d746333207bf73e92cbaaa088feca0235b575203c3307aeec33c31c1f3954e965e8d050e304ee4fd731919ce6370338c7b92065b3d20a86e6797f2e04eae54c99060c138737943d4e035d3d0c55e3eea7a521d2791809c0d0314d5333d9e6bf5c19a3a03946b1403d7a326e9a76dc5f925f20c76d98081dfbf6ba14cbf1d982fa54c7ca73a12aaa73cd09541e9f1fa59dd1124d986e038df0013b9f2122ed7ac571b1e64a6cae5f86a5c5b0ffba5f8f0d1afb8c4de60472f7de9bd44114e7c55c7c573cc9bd8f460f664538fce256725270e43874a60a1e9957e071c8a074cae13e5a4d4390039b04b53ae6f882230d464af77016d20e39b99cbeb07b74d9de6adc0850ed31a0c04c30afec246d2a3c1da878fe63f607b281667bcc59bb8c150d950e262bff80be48e8b351c9b870bfd17069fa7c0c041cc4a94cd5a134db51e92e95c13954055129eaaf1220b8b5af01423099a01c0ce3312b5f61046b3227da8d9865c6e3f502d9687b15eb48cc608dc724860315029e8e21a38719687728a6382319ee464e552d74f0581da35dc1cebc964239f7056594c5daf7bdc2a962787877a34d2c673bcbc099fcf16c18cd5985964f64920e6e3ae5de06e7e5b4b053fe54f95897b67b1c935bc289482b0cdea4c3d924a70cc1ffa728c8a08ccf68113dfb71f5ba592c5842d5f5bce1594aa5a165f3e6a804252b43fb4a38e918668a44eda3796a952659609a43293ecbaacadbbb3e5d4e06c7817329b386457d181149700f13e799347599631e94c0450af9f66753a2ce0d1ea22d044bfd1764b9d93965a1874ec6375a1f38e62744b3d7e94e2e4b839a0a5d39f6742db4710474b6ab0dfdcbe121b0b87bda6a6acb5c7beb7e9a8e250f0e4418681f84d453f9782069ea18fa22ebe4afe915a11fa72c0d3615980e259905f714582280bcb7ca550000bd85583eb9416de70e1e650f8eebb11c596501a202f04288a5b91583e74a80eb7f420c632f16c4d1d39ae035c8da5d86b97cc07d0f false -check_ring_signature 50a431c72ba79852942bc7753c9488c77c85926dd57ccd12dc9a5a802daec799 1c2c6751b566a1fec469c69a99b7ca2b52c4423ebf79292b31020c329668c0a5 1 2371673289b34bfab9ef48b89e141a298f714665105b2e9525036dd4457c05ab 87584f0f3d4c9640d46c925b69645397c92f06dc943d86e158fcf6a2cde06600b8cd41b7f764bcda2f7c9f72b9f99f18bfb174c2dcd098f66cd46f809a0d4b4d false -check_ring_signature 8dd52f1346aa71dc6ae710af04ef2b4f2e1b46510ecac2242ab69ce5662bd19d 7feeddd00470974b7a76a10b8711039d8d247a5c2a4b35139c6a8fe5c286f21d 1 65597b8281972226b590c7b1c6489f0fbfb8337c62f19130bd722a4e78479913 090bdc822fc834a74964765f809c6eac37b84af9a1ffd7ff25c45c1fe759bf01b459445f98d0ee20b7a9f92e73240f1fc8ca24118dc4d78e163ad27f1de2ac05 true -check_ring_signature 617210729b255473a72b788ec5dc85428e92c03cab5b352046af3a54a3fe8392 e036f89e89eef8e0477f22002feb8cd3ad09d3ac771cc4cb09ff2e5b7ecc83bf 1 84c1c4b34e07d3d02d3e09abd0d4c5fd0f1bc913fd146a5c9f20e1fd579f08cf 4a719a65b106d8b4b228fe7d128fbc78f9b961ddbe9b4240120b6ff9c0ce2d6a0fb7d4cbb46bcf49bf72bc5208ec0b399ff6c8cf13023b633fff93c61a80941e false -check_ring_signature 4939fef9c3ee2af38f7d3ef855dfc5b26840e5a2a75cc0af7c2b7e6ec2c3b19a 23bff94b57d29aa5f76e35aff5b8b748060e52e4cad749c9dfb22f593c7287e9 242 bb993aacdf458d78c0fe6d2835b952443aae77dea1dea245c35964878ba9e50f f969d841d0517fdf47b7fbd073ad341d473f951538d0c2ad4b3aba08993a0bf4 ac27a3016ecf1f392ab7e36549245f78890ddf16f3cdda74998dc472c79b0f9e d906966ce26e0476e4c54a4995263ff007d363332b6c0793f4bd979b5c1229cf 363d074b6256f3fb9d199e1b900bff8c1cf76e76027dca93eff139e262888641 a3c718038e068a4aa5fdecd9c21e34469631c2270d454980f5d5ea4e6306a2d5 29036f5f5490b5cf2692740893bc297dde9ce7fb32c26ef1c0b5e7eb182d2aae 31d4c1fec453891d1baf53ed468266204dc46ad9560d3e96831c24d7bccaf128 7ec85aa6cc6813f20aadd476132a1d4aacfa95c21d9b691b740512150f558946 0d1bc8539b5766840810d5310f9046740107fc72ca61f89c09ec618e4ae8b707 b8b486952af82aec5f2b0fd483e94df6f540673045505119088e69e93cf6540a 80e71c1311bcbd31d56935a8acb6e8235c6385d96dffa397631db535dec59c6a d1efcdf9703569f20f2057cb8c454a510c058a5a615b484863b174fee0031a97 0df7f8ff55e5a37a3957e0c86e346a1646cc375e8628d436d7ea278665271e78 3d93dd0068e6c4cb84996f36f98ea3cd1550565a0fd1d5efede274c27307fb05 552193a2daf063d92f990724a98fe6c7d98b2a8029449887be7fd1739f6d6868 9638da7408cb0a8e0421e3f31bbe14bfe4c57b9c37d93a6e909bf0d42c0e906c ec51bfec1cb723fd421e0f9ba2de31af5e0fcd8ca2a70e19ed1d2b51abfd10c1 85a8265570c5bbfcba9fcf5a2042c4377e0d1f6e446765a455f9dd9580d61f35 16d74533e3367a3d46c877ee5a248c4f691a1e35ffd930d42865255d6c575cd6 0bebd48bc83f8d8e919b68108892562bd89ec783ce54ca01398af2704db71fe2 24d83646f4572c2355349de9ed3e5431e9c8513d518975b86b2451674edef9dc c70a7334ca1ae2f9adee45f08475701e9a1e1f425346e8aceeb7f30fb9f87363 1946559541ec5cdb140c9fcea96a26e5173cd430484e068853d5c163b486c59c f9daa74e79e0729d442620e5dc8fc34f107a2a9f895fcbf5a88f77512d06fc30 741bbe9314a19f4c85742a718bbcefc820b14d78b68ee3338b42c23c4b228b70 4e2e66db29d15cf54fbe1be69d6be62b938d81fc7c7a9ff63910a7c1906816b5 6a66f2c7201bf3df893a9240cd55d0f55c1d7db343753df335962bfdb3c7f58f 128d9f9f987a34ae994930c1dc86583952f3c031c353d815e8653e22f3907953 01422c906fd9c6359843a385fb616efe274318b8a17da19a730587fddd36fe93 a931826a80ef42b99e26a728ab1839f88dbbb4575cb7066d6848b3304e724675 14c0acefac76cbf972a207d740fc11e2312d466225245ce3910ed799d3887902 593a180d4174a39c7adf867fc2b2c366b38a79b8457707a9f13212b8ad212ebb 508d77d4409087bfa43a489a419669da65e0af452deef766dabc43853bf5bf8c c5944d49eb3d05e3a69ee1874cef459f70bc0cd05cd2e40671de143ad0ae71a5 32b7fcde567516a8fa7521b30a6d458465bfd4bd16623a1a5d58daf6387bc9d5 d4479e3aa99c0dfeb93b533714a63b2b459b84590520f6c231fc0df6ccabf895 c05bdbe04e49ac8fc101a2bb49fbdb6744318b54b07166505cde512788c23de3 6ec3e18368ff786b281361dbc78e3d7650d9c3d8a4ce9568991dbe060df6cc91 c7ea6c605fb8249a88403fabfd5d52c09584beb355627c1e9ae8facbbe7e9d8d ffb8bac8f98cb0ebaea7fc451ed3247f44fe6784531978049ffe48b56544d39d 8962a7faa3b8acffd3a2e24a7fe6e04241f284b7fe099f57464328674be01cab e4a21cbe0ab12ad84fa95e0342106e2c8dea05e350ebc975aebec4becba9d4bf 107f8754cd21b9bfae40863d718c9218afac197f42186603e74dc113e0290df1 d86014345396034eeda279bacabd36ca3462c41e3f9942ce19761d99f301ec9c 9315804660afd649738286a5d115c4c7ac82e0b9db34de7161b581bb9def5246 7219acee2936b9c47649dff1b07a5fc525778243a48f1b901a7c05afd43eff72 28aba96a459b5fe3c1cdf15a7c71b91ecab6b87ed2ac1108e8de95ee538f370f 867a6959f8ef7cfad478e39f0fdc85bba9a1d5dc12a03b0dd44deb989fad00eb 20ff66e74cbea326527834fdd399c9c6f3a2e38eed5e8c25b4763c27f9e27f93 7224f4c4049caae5277d98bfa421bf1ad07ba9601f8f0f3d9bd2c9f993e74f6b 31e00b945383ebec48cbb73580afc78d2b101c1f0cd018294fa4252cd9e30e56 0500d69b144b50e65753194d2ee9249dcd662abfc66f1c6b49d26205d88e6d05 fa6c69f7ea5363842b6056e489be66a8c9d5b226f757e25dcae8cc748041b52b 8aa275536249fbbac6e4c1f123bda19aed6faeb7af2bec04e4d01687cafd2796 17ad997d184ba9c7116c9cdb4e6a86a294f5f5d7c86570ac00e062c938202de8 0b2dbbfcff9731b3c6e7dc5f2f58363387fe461e790d57d291540cd412d8b549 efc094e2a64dd75f03d3ee808efd4468273d403d06e626f9b56e2e2ccdbe97ca b040a602075a0f5dd940cb3d27a4fa4e8b1146d59393467db4a2c6a5a2845957 1907a1493a176b42c7d386121302dd11905b5415ecf833d4e98573c658c7e837 db29a8fd3c651232b8803881d0c9b47a5d048d5ee55d1bf6b3254de75d45dd8e 0d22ec6dfce5f8d20095b6e37c0115740af8409041e3efc96513664badf44e09 1f5553a2fbde5ee2efb2ea27f1071a8ce804416c25b8fd33e7c02deb5ba8420f e5875c45ccdfcf96594f712387b7a48467070846f5c5450ec2418205a5a9e625 be29065f55fc663eb19e274992274dba50ae67f7f8941086d5522cfbf0152b6b d7c3f65653085595f144bd67d7409bb66241ea61694e6f9cb9d2bda7679d8a7e ec8d464a68023c1e2ea91919949f71ba55f916a9958ff04f079e6e77e4c30bce 9b736669a2a10837f06e8a68e41b1b2c62f2cbe496929a45c7eec687ff3b1ab9 f77e0e5ab1bc5d35b5927016c24b5f9376d608f3db4ec60a0ebf31da50811597 8e7660d430dadf75a1dc26ac38b45bc8dba3c7993939fe53481378be48d771e8 b239efb6df394b771aa3b6474c45711f5541b6f4d555d1cb3f271a161c1c02e2 451539edc253930d7848ea6a7f13c1259c2a6d1cd3e2df608fefbf239dc2dc8e a2ce86ace020ee875281c9131690778d09bc6571e0fb7487a5493f78f420b811 f4018a0518dc619771890b97cce0a2ab22e13a2b3df5b7337cd8965ddcfd7ad1 a3c9b86fc5d70823dac192389abf80cef7fb6487178d72f10ff2b2c5ed79465f 5a845a7601a00745978e2dde2e2cfc24fa26702d0bdd6b36c33c57651fdb705b 92eaf46acda75ceb04de8a66e867ce5805af352d6f40cd4256f556b7d9735e66 dbc4ec8592e17f479fff6977ce0ee8a7a003ed423719e1744c867b25265c4e6f 189d079ea744b7f6e24b2c932812c03aeebe340067d30ed624b090f35da405f9 d5dc60afb3958d0ecfaa2d0e5fe125745fec56abccc8103e241e5448408a8a06 1c194bb7882f2769d713786c0d43626142bddf70c433c05560fba674f45f802b c3af671214f3781d822368f37feda9c5cdc43b89b45f9eac3f723ee06df91b13 d6b889e71e4b7f22f2d9f30343757d48e7b7d0a91de713c7abd6753759cb3ea6 7f098775aa71a7901a3fb81c328309bc0dba857c05234dbe05e4de6bf3e7a8c6 3c7b6a9bb597bec9365d8d81f94d3c5ddee5d16e2b2fb4a1aa070512205917c8 0fb6fbb66d35ee28f926cb580feb5d4f109124f668278819779600016af5e7d0 55414eeb02e7682d96910d782d826ea18f3a9131287de0693cb173e27611907f 6b7a8d419041a43d45d2acdabdba6d0cb38b0c7fd9a1881ee3b799d59c153570 375f4fd73850399c38be733c12da7192d50d4f87d7c1fb0212a54bf45511b77d a511f9b27213ef80381895138f59977fccfb231f8df45ebad1dfde7ebc60922e 8b0de0bdcdd43ed75e4fb315b67edf53ef629e105e8ddabaf95768686479db0a 2c129979d5fa42766e920183fa79e3f442669ee8ae2fca604612bc97977addef 92be3c93f487bf5860787c02becd36bc6aa4f8f2d9e26acb735e9f42da5f0874 df016169d480e40f4b6d565da95bb9f2769144ed73675d965ac0c091ff4a64a0 e00e09039b8f70092cfff8556c10549dfcb6a5fc5299fbbfe59ddbf5e364128b 27f773f5f70ada279165e51849898a7f0a3af110bb4189721a7397d0f0ca9c85 f11ecaa852b3b18457a1e897d2ca382220e68311e552aa9658db3e1b940f8a7b 040dce8ca46f2fe7c256cb01a450ea2cc24ed06f6482b9945bde93e9c068ed3b 151a84a13eb1c6979b39bdb98e79726cb09b6b238fe1eaa336ba6aadf2eaf2cb 70f8fa828aaad64bb8c9a0614c4630b44ff960456e6cc5eef423ea5f145c25d9 cb47792ec423df63232e85f656cf9044e0c814fbc6b275444561db70280be858 0cc1cc31bc838f0bb76caeba9caf39bc84ac3111ee2664051d61ffc09c1e2424 d052d545bbaf405fc1a729beb084bc8d8b5b1ffe24c9a36542db693533186f20 0fe5e42213b16a24c9e35668a7b7dcccf7b3219a9e9ceae0f436e2507eca2096 f75f806b4e7be01f2604fe99e3bbbdc472bc567a39fff5d1a86c7f64f9866aea a33d91cf529ea547ea44c9d16dec2baf9184383ab40ef9ed18d2cb1c54b0a900 56ce16995bc801247240194055cbd95d9dadf1ea82d109bdad69cddcef7abdd2 f9f51e0c57141f157b70c85c7d7787b922a23617f6ce24cf27c9a5211d4a167c 8b2c95f8f045a9859e36ea71856a44d37ebb9b53221205c1c042b12fced16944 d15da8cd51863f960fa183d4a1402fe34ba79ad5d1c7fc38fe3f500790c5422c b6c44fb9537649a5cb8cf29ab8ab7d55a6d63dadffd628b572e131426a94412a 6df0b99e07a1a686868d32a47c6012e35c0aec71ec29cfc717a21536601a4738 3e0874fb7853816bdc3d59faf30c8f4e75befe4842cd085ac43ac3ef283b73ce 2933230528c1e50e54db78f565beedfcef4f56a39cb6002b01c6f473c5928166 1138ac154dd96951a3bee348c82f52e7bf1c984e6266ba43845db2e1f9cc41f5 ea9363046e4c157c853d5f9b474f570a1e577f6b06d5832c94f6bf0cb584c4b5 83c77409ada63196b404665397316c5085e792b8fd127a77d7f70b567f6f1e1c 04d05e49d4ef6f9b99a8d36aaaf2b058e2016341d4e34c3c406a99568184b834 36cca7a29b32cd9740f8221aee2bee35f31c119273dc2177f08dee70d1cebeb1 be311f238de071c03db91250e57a58e2773a45d9390cbeed6d9211deec9a049b 423f73a2f661099e9f0a1a535ab7f6fdfe33780d2f5855cfbfa1f19248af302c ce75acd0de6235ea99391d4aec2e8133e8596ff8d9056cfbcfee02b19a844b65 690fff6a710befca988f721999a7c542b4e68fda95ebe76fa5466ae63235eb07 357664331636e3f4b09ee201a17e34ccd5582f8f3b8df30f5549924dc371a102 632f725e288d57ec6f8ed5ad5cd3d616e2da3c451d83e1d0ca258b9a2298dbf1 0df61ac68f4a544650e37e32cdc0ddd572275c4a6d812f46331e43e2aeaec497 1d22f1f19e7e422213c46ab25c50dd955a0973eb8a5fb0d1ea57d5204c19347f d1faec0cff1233feadac8352608f19e2b15c460df409698636f9a32212a1ad73 8cf9ee53656f63cf521f5153bea1666b76a1482cc30adeb5064c9bb4ccfef7d9 5d73cbd0880fd6341cee16320bc01147daeec328d80f828878ef9acfa366b3a6 ba5e33ebed89e640a9f2edbdd0b11a49a03080e1fe976cb8fcf1a207d1894e88 f5b200886f5fed9e44b06eefaad0b4e8fea0ddb6daed5183d761644ea8c8e6b9 4cea1cc77c99cf37377dfaf7de1917d4ea440f963337c59cc5dfbffb03c40967 e8731dc00c1a24783c78d23a775efa3c2a7b8cd38b1598026e38f919f0f0b87d 3f49e190415324a6eb3d15b9719dd5ba7ceb93d678de149ffa6f9c3a1eed2d68 113afa195e3e6cfe79266bae832c3d86980069d32d1b82e4278a5fa75fb50eaf 419314fb75f9515bdb01397df874ae89596f21b39e7e75682f4b7b2f98624214 4ed89a3ca36e43545f9d064d960cd342c87bc2848b39c354e2da95650ce149fc 09e62b60ce2d26faff550f05be51fe377b523138fc608b8ada091497bc303a67 16c40d8a6edd7ca63ffd97f2e487543c0a68a8956a00186317485861aeb029d6 b3a4619a48ca138c90fac43232efc3375f990b46f975cabe26fe3ad6fb30e055 0751c0a6600c16201cdeb614063bb73115c5ae9187e662b580d715da9e9f73b6 fd8676e8cba754a2fc01475064e943e5d93b2b7a945bf2cf6e752c9a81369c73 545ec777a15f8e1cf3fff0f7fc7af6fda45654b0197049a78eb4432e4b73d972 434bda12c2e417d37df5ddd723f4ca400e499f57d972310f2bf0ae2e58704988 e0dc8ce7f1ce47cdac2da58c884c88edcb9d6636513558e9b7d3744c4b89c9c5 b1cbf701c2a1fd7c2d400f3210a4b7a3bc8187a8884bb87356fd3e0764d5fbd0 1d2e83d004a80dc22f9adcce778127c75de0d6e80e121cefe4cac0992bee6aad 42aecc6c3d4ca380696d7ad71121e1b7f7883943faf32402ac1add6432edd093 790b2b994c490868179f083614975f9b5c674ded71de2f0397f56f9a39b0e336 74d30e4127b22481d9ffbb989888b270fcd98c39cc7e0dd5c955df0af1fab0c1 666cda49806671b8e52da7ad3f4dd408e1e7748b9703e26bfce7c5a9e5a61cc6 2f81b2abb8a2c888636736162519b1577296a4d2371cf64dff5d67464d9ef080 4e1eeff6de6f9bebdecfcdb529361c4a91e7cee288d0cf64e6d9dda213233db3 b9086150998f58b64f3f6a7e6e4071cd3bf155bc8815a63057748e5b08a6dd3d 18e1351afebd7a5e708c9257aa2817d9ced565887865e83080db3e3d546b5e7e 988f944090ffbf57cccde2142c953680564f2a7b7fd8387b0645a5cb32be356d ba0e0a9ac0333191ac10420bd406e458aae8a5648b6f1f0d87f13d834934fd3c f37a6bc5a7c39e6a21293e154c67fb74d45ef6f66f02dcd2b9261c894b71c8ae f7c8e9e1f38bf6f1250029e63ab8c8352f44373063de3dd0b3ab4b34917c18c5 e18bd218dbe459a832d511aa2676a0f2824e61a2408ea17c7028d2b5103101f7 d701798ad50b06f0d64de613f8b1f97dde0d1d9784004b9137ba93ffc5a0b9a0 0dffe4ae094b95758543d18fe1fec3464acaa5d765123c6852471d0453e8ac24 971849e7b5fa5afbbca7f7b8cee26ed3e479c1cee8bc1a92e7357c4c340172a0 663e186f23b74806bb0287c5d36b27f38dbc2674d79275e96eacae4d11905f26 1842d798602da40368ace7eabc3276c2e8c8e84d3fd93ad6ee99ff11511d59f2 c82a9be6796af2453b3cfcf4cd174bdb8b8e197bccb169d0d98d9c5e6a071deb aa83a14347fb678aa6ba41c80ad7d01ded3550a50bcb4298602b1a18cc3103cd d3864fc47ae91de6fe57bd05f9512e6437346cdb42942e654596b9d94b462efd 60a83808c7c5fea7c91797f76c4c701bafed2eca741030fc201c4ee36689b934 35954c564986491a4fe27d85092ab85e5e668ca0b72ea20df121c63ad7fdb2b1 e2af6a814b84a2cfe3ebc96ee45e3f5d3ffc6febe90d6f4a04554024bf8044f3 d59b064d64f934a61819f41d5d881e7426450ff24865f0dd0d1a9ffce9675e41 b81f94ba994eb4848109e1bde452a9462e6d3fa2619197aa944de4a8a0e7a91e d1c2fd8e02055dc5794e92612863c7b08928a9a8343d0d0d81877421d648da3a 87646bcc369c6cf8529f10957ac3a5ef30dbd9f68552a0b6f511d8538b60e212 c700f0a1778a96d644b951e82273f989a970816a397598759c524ea37c8c3676 0fa1d2a2acea304218cbbbd30c7a516ff4d943b80a023d54b30c8e32fbc18890 6ee3ac77b326e09c6a3d107c2f0c14b098a4bf0d2fda1a6d27a1ea36f913c3cd b511e3f1f1514aedca9f82b450ee6da478cbf981283f9fe6eeaced50b4e2322b 5ce703b93a43a0827bc78ce0f730a3932514632b46597fd27a12582cefd8f0c6 b973db5ca93c5213445d03e0aab73d44976b2f38f93a66068f9d79b58f46582b 94f63a035a4af439918e6f045bcd631566f7e0b53d668d7f2dea963ffa9bbd95 87dd188376a2a1becca2ec64f2676a0a0266cef87e8d201ced87c35493f0ebbd 58763f32bb6ccfe5f3c61bf5867b998984a0bd56f211a6f3e46f3eea174d3365 cd83ae7ab10005a86cd4b03e1a241ea3d04e2ccc8d1ad2faf9b2438309ded3d1 142fd82e1d19db62f64c45483fe51e2e6f1aeb2d9f2f0a23d97c23ef4e45eba6 2a6ec642a9677fa429d1a4524e846211079ecb2148ad897cc6fb82b9e7e3f8a0 4c13c5177a0308ed1606a24f8f10abd92d7e53059936756bcb7bd328f98cad1e 6768e92bbadcec64feb07e1c65400edf68e3b9e1a3f755faeebca903c1c30939 8be9eeb27bbdfc5f3282818306b78f9647769c8aa78f4b022be62d0b4a2c4901 2671e1874130dd8284becf27dd8fa4af8159b63ab16033fc3d5b0d13eda62a5b 872530768bb320e7ae3da9b71d19f21bf99a47d0df151d9441f71c9795845ce5 d55a0e5d3a9d56127879a2d943b6d757275f4908d445f799fe8952cf11b359de ca0ca994162394fead958b5d5e913768c5c57b0277ef2833c7d1bab4816a00ac 443b470f8d69b139497d01d30f60d88690519efb4d42940d65bff86be714b79a 59906ba1de2abcb1086485a3a7333efc082e4309bdfd59a9be05408f85584bfe c6b2e5b32d57ea11c6459be5965fdbf37bc16e62ebb892451c8180a9fe46c2c5 247ddbd8dd34daabd9e0a869468d06ddfee4e9ca4e301eb1ca51bb8fb5e6bb71 5240e6436a56320a3375ed877ad10b77cd02901da06965be172577cf408357bf 7f25cc1338c3dd6053fc3a33363bc79bafb95489c4cd62630b3be1df7c8aee4b ab5a21f81c7fc4af10ce1eaa84481e49b81817e532c8df53ee33be763317c052 da5b6db2e47711543ab4c3c5d71519728a8e29d17ca06147d6c665ec594ff9ae 8a48dea7a4fdfaa118da6c417228a460af3be80454b5a6f5758ac793f1fb1ff5 be5433a36244c7a3ed53690e6f8c2295434402337a34d1401a997705612b5578 2a3366962d7fd65c3d3cfe842f6f659aa548bd27e3e40a3d9285c86c831bac3c 92cbd2f85e90e5f78d6ac64f87ef730939962d4d9e3c2db976e31ccc7a7a553a cf7b9c422be7d268ed7b8596fcb971d838ba05855ccb100acbb07fef00123e52 611f3c3166d306375a5408ab3122bed0bebc9a7ef8c76f48a442a19b71f0fbbd 05018146adf6f4d96636729f82db2c821628b4cd0d29b8a9bccfc04b9ee0ee3c 76f09916d789dd1cdfa8437daa5193ceaad30039979fbdc49fec3eea44409377 a672e637d450056d3b6b1a7dee47651de63f87e51202779e990d541ded44a259 ad0faa367ea38efaadfa2f84bb0f8cad9e93919851d5a69858df10ed19b31e28 ddd21299cb63cde6f61c1a6591a623341a422692ad812e441302e02b3e76b638 80fbaf7ba9178b5b9adea7f6bb12d0b0f77eb39e7c26a1031fb9c32f05353928 7ee23a6fd69ce52f78ecd64c48dc98b9fa4c0c9590ece601ad9a1f5049a5e1e4 b565511dae423183937515217c79a629694ca5c88e7e82243cd29669d196ba3f 54f0e958fd5484b65c53742a78658b987c19503fd7ffa31233c23c6f33209121 5112cdadd3cadfc43637400729cad247c7a619e36775df7598c5edd841209936 8c38ed90d0a615c8fb50707cd7e26faa2a9a70b6820093843bbcd1b6b97bdceb df4e28d2042b988f84f8efefcd2497bde9d6ccacd4225fd3d59f05c66fc78d75 97280a601512c5f2e082a6f36e54f41cf82102277d51db9a4d575cb2d2903129 74ccc9f17d227652003edbbcb61ac71f969101ecdf69ab307ccbc7d6bce1c624 3654d211fcc407144dda3384b2bf110ef0adc89ecdeff01360122b188ff4b628 f6180b4078988a1a46d60440074ca4f0e64faf66781938f71561f2c14438e0ef 55e94df9caf31aed29bc71ebcceaf3425be641d3d310e63a0a0df3f55649389f 0bbbd3083cd4eb8c610b73ba13b118f18e049a5ce1b982b3a95808f9a97f212e 17bd1a6b42a30f6e7723309a6ecee538bb721a6dd88e19f9ded503e85623ab92 5312a7f5f7732d9c14c9b2453563d5437999219e50a8bb1a42fd59dc09a9a858 8758375884ba530e0741f0f5779ae9603c35a466f29d92846db2e463fe2509de 299e01554dc903e926078507abcb46810a84f5848692d02f941018fc7906ae83 8cc8619a0869dc06add8000e240e14741b9551beaed0cea0f18032914b9fe0b2 9b97c5c683a2de0b03ab0445138f8599251326e8cb7c4f32dca76dfaf7922288 9fb88f4e9a2ec7994eccb3dfa6d825e97c4880351f02a9c4542f93162bf36204 ffbf35dc556453fb8906e02dd87ae32bf30b17b7ba4738f9bac7d1cfa27016d9 fb902aaac54fc93e3b6edc64d6149297d49e5f6fc449ba43e7b08564cfe45bd9 bce0e7d134a64b05352872ee3baa3e942eb06270513ec71da9450e166d59ec07 7c3ca1fd318c2baea063c73042747afe00ef7681d5d2ce23f58851af40503d60 12f8a863e109504ccc59518c2dbb072052ca33df9b4347fe801b95eb532e092b deb564b75cfa9f4aa1a00615a9dd55e24d4b3cdfee6228f0069059ac5f7b31cf 44eaa8c0499f2403189df29d36782b905cdb28dae267a7a124ff6668df0104e4 3e9e455a3d699cbfff0cf9b3a69faf612755691fe03a09aadda728fd540a3dff b8045360e58369fb0bd060297a65e981d1a690ecd5df24cef4fbad7c8b8ba40fe4ab80d6db15da83f94f9941a1dc9f2a0e35d1bd2469794317d7a08a09416008c264643ecacac09d0066834a3e318e3e35f776a737970f2245429f2577ec8608944793d94a09e95739a470a4c703ad41f45b839eb30eeb69717febd97bf7ec061b880f4027d66100b3dc2c984642933a9ba82e298af02637758ca807cdd3480dd4adba949c5f132f0dbf56859422983b9d6ca4f400f9c4b306008efbed396b0242c0c9e271e591d07e355c554dad4985af707a27f6dccd117518c73b75e8b109c25d0542c702fd26871792952aac469de72077bc7168241509e0dc57c1e006000e3d9ba145c87528af493b175b8dd4124302121e5ea2ef099ae60e8c8205c30705ab0dd5516915877901fd2f9434b4aa8f73216a8fe65a6ed6f01faeca0c2805dc05434f61d5575a98d200bd50e6db22c8bfa141ce629434fb2798a91263080b418db2ef33848064d8f1b80e1f6c1f93b33c0658be9bbc80cdc130eac79ab904e653ae4dc6b10a42afadf5303b979e9ea0f1efd92a00ca3cafa6e4c80a98640c533901fa953ee69b2f9f974922aeff2fcc0d8cd41a1692da8bc5155670e0ed09865b6832456d82e77eba9a8f135b59f8d08061e2982b513b4e48293964d878093b96e3c6a0593854053df8ed3aa04a3e97a77d2062042525616dffa0c18a3000947965b8b00f543ba7015daf5bed1f6754c0b622876e2d2e4ca7a38da107d80fa6dd1827fcd0973e4815234da254356fbb2a73282204038519e0191cdb6a87073ff2f20eef349492ff2f2bfdaed404daa7a7fe3138d7de17f7dc95634908cb0225d8f682d47f561d97e912a41beb1c29e5c97b83acad3b5927814c0bd58b6c01a74f4b8f5732ac92940ac408150949f4858fe51989a581bc4cfc8a03844c790b75aaa1ca86627c69bc538a7a69717392bb496e6af1522c395e5af015ccafef0b94de489b8fd9819e1aff58f2f6436f977f3eac34287e7c730699548830990508a624f67fd860c81a64e83f8749eab20771a6ec44f2ce1a8fcccea1fa92aeec024736f622dcf262757e2edbd1214279f5a93a247d6f7aec21c289c886e7d84d04034b045074e62ff9d56375a995f632bb993b8c1f547ce83b2622f83a98b3470d2bc4264c7d885ef2e58205a8d914d8ae9ec13b09e969f28e0b8d9912575d6d00c07d311c343390823ecd79f610cfa56b51f199bb1e4c90c3707987847f028b099fde4176ee0d6e40380e479874d805aab6c549ab02f83363447f553810530b0fd73128ea217b5b3c011f9791b7399c090b41a9be8eee0812da51feb090de9a0ff425f96c3d8b1e73be226a0efb90c544f4e7f9f36361eacee17fc5945f6e2900368d2269966de6f3627d61a413c9ee512c7f7a6a9f7551382a30a3747593cc03380223e250ff18eb08cd934c95a8ef374c187ee9a3f2ec03735a872852f15a05b9fcadc8550c6581c75a77087c00790240b23d13b30adf458c5c40c72ac555051c6c02e765ddabf0dec893cf632c98b6d944b39e8b5522efd01e636d824da3081e3032fd2473ec7dbecc91ccc165c381d2a6dca0371b265aeaef266bd2365b07d4b1002887dd81ef26553edf0da6478e752671d711a1524ef18172e2a29be10a8011a3a20b3b396360fc96f2eb30118dfe7818196a459174b512272698217c0d445c893d1b401789faf70797ca79feefba864fff981945f16e19b584065c9d09a10cd5f5a34fb833f67afc813325c79852bf12603e0480ce79072352eca1d701869592a56e10b8c58ed938227ee1d56265992f6ef338e381f3b4140ac2c2160147d1d82c0283a309e8b258f26bda624f2bb9ecb30161cd7bea2249cdcf97860812b9c2224af0ec5d8b484b7c262a134e072268b19e81bef3d18fc8fef542630819febfe82361b990a07b1a348b4bf53abbe2441e43e2b3068d3c740448ef860f998350113c62fa0959dba5b6a961176313a8ee687af34c28082c17d6f023990aa23c9235966a6cf21900570601d5d6204a26859dabd942b0e96203645dec60045660a6ac462bce66893d38f0a7c24e370b3f8bf1ee42d2a8e1653f9145cfd708161527d90d364e56ab0a8795c711f2061649bd65998afed3eeafe9fb5426e80b06b4f19df40846afc46487fbf526c644bf2cee30c4da7cba92ec4b102eb72d09e30f3a8659136fb694c1cda6ceb9dbb60e2f4f000924baf4cad1192d3b16e80ac56bb8a64222644e2635ca049f1a05c1b69409d3315b13a5160c17832cfe4f0f422b5309817a7c3fe6d631ce66f3bd6cf15d6d45ae9c7674ab74510f01c5d10bb497f1079f6e2e9919eeb797f903076e8a71866a5fe919c0b82f843ea594810589339c7a07c975e986ae3472ce5da0c07b6b9a8446c05ec9aa7006db380a1501c005c61551e9e7adacb5d552b64bf52f0fb03cce274cd33b79d0967516f5e20a8ea0f941cd9da535f0b3e389afb879ada57a7b77142e163d495501a6cb47cb0146b0dfd3cdbd4d6c1ab8eb2f5d20130eaa7c9bbe00533cce36e05898cd1bc2078ee546fc6bdedffb94bf04be2fa8111479bdc0224607cc6f60de181c7302f90d71e2424b53b7b658808e8f2832841b9614045146daeecc53fdd140616c7ee204c022c5c4334d304d6c14998c44cbe81cc616a595a76b7b80a3b81746b1cac206053c0ed91b5928442a984b994a0b3ec4fbbf8a5ba29f30cfaaf14b19706e74044e7a1c3fd6c336e37948b96cee3ebefe3e8587ecd49af0650babc9864669440b5589b77317b827c6b748bac2f651c3796bdfbb952f886006648d45424f46a60fd9b9d2ebfd8c3713c6eda39f6b3b661f550be2863d2711151d71a171431692018b3c3604fae4ccf45ab4907ad3a81805f94c9acb3d7f938f6f31c0bcf1c2810b79d4df8bb4d796d95b9df6b8d9179d775f6e7ca48470538b39b660350b9d77056d8c92dc9b1489347d5db775cbe6f3bc314fd7a7a83e4cadc1fc960e40529c04a9aff2cca54a8ce9857ff279c5da8e09312898e6325f3a9731a5a52ee0a83d03df6ac762c4e323d6a08d778783af08e99a6289588fff35270ab6252aa49880063e9fb9cc1103715b414adb29efcb87da33a635bd78227009c796e106b7324602ae892f0b30c9e8371d3b63e55475e522235f3aa180ea777443ad327907eb510e979c54293b0cf3e60d3561401c114f1916a274ec3ff5b5654f5d5dda289e4d00f0a6c584193c8e4b4d376d1f158b398c067654114ece07a2110a13b9e61daf0c54a2f7269ac2de8b1e615aef08c5bedfdcac7ae1a8c862e1f3ed5ee1ee38170d8e23989cbe27f0d86e196c2ec0f8ec0ccbdcfd840f59c26e5047ffe7ab1f4203e436013d790470fedce51451fb653cd9db97f79b2381ccc680fde355c23ef20ef3c9b8e64972a00dad57f82211298e4a5650c0553ee50ffbaf84e5ae1f70d9094c603af1dcdcee55a71f55d871fee089eb868c6388a88357bf35ac22c0c1b600fddca0b83c104cd6f3cc3fd65abce0be5980b6e737e56ce15f6b43c6cecc1b05f1a04f3e6f7f84103a380c4f383ebad3ec28de90abc910c15d1d398502394d0a22ed7e86e45eaa1d9eb193d94f6086697e2b25fd0c1fb560008ac3bc4ec984026ecb51a9bfa91d07ff80dadb755dec99e21c8f3932cdd1d05e85004370c111029817bc796271ad84b7ee8d47951baf95932f5a6c4f9c0f2696f25f67f2f4c70426ccfa2d7b1cfbea8fee7e74c422ae35536d26eedf80fca345a3efd8c848270be9125a9be4b2d066d354a86991880648198313a393257a871c58fad2f7ce9004d0af376589a974fe8700a4b49d01bc4a39e85e6db0a3e6b038d91854fd37be0d5bdd556f122f20690f47b3b995dd75828fca1102b1b39815be7086d31ba30f0f9f02169a8df04ee2e10c8622e6ee611b0362b540c8bc5a234feea4e6e1d2c7001bf01dfee56f1f4fa0bb159bd541fb7cca58469fbf956d1ee70e7d46227ca700e3b1627208f9c606ff6fa5e3b0aa1407f7723f22b8647d374c94ba03a8700901a6b50a9c5671f41b7136b5cf600c30f3a626fcda7da491eece8c36ae268b03084dab70f39829b11da1b10bb377df433af8967cbaf601c0d42e5bcd414d27db01616d16693d45e0f9667d340b18ec295dc0f56bc3cfb11a488ba07a40e9b5060da32cdafb68ed961ee0a33c1bdadf1f3fce69b0e003402693b0fd5858eb75730b53a8ee65a0a111a7068b804484c141dbc8a1a334afe6f2640f88c92beadb5d0663716bea2f2354e217f350eaf314cc0e7b8c633b4e748abf9dbbe1d16a09d40b5eca29820a11a6f18edcde308e35c02c5f73a0129d6373e5191b0f4c6e261a00557eb84906283a7741f09bcb7d580f9a6dbd2cc83f9f416634ce9b08f0cb1600102890c0389071bf1634a16abc4cf63b7353ef84bc766e57bc88c63d84029e01765ca5bc8cf11d67a44756d6860ebfd7b20603e6b2b658cc8169f3e887626607b705d18dda0e6ceb36e409009126eee25668122dee5a19ff657c4f8f951455012a83e2c6cdaf8d44b13e87298664f53bc2c0c758e5246191a8d76a9350c7390cbbec85adc8c39aca4167e7764b5991ae73e4d45ff1283264318ca830df7a3b0a988d8a145bb469fd61563a91cf726352a4c7ed3c7df15eed36f21beca204110344236c58ccf28d540ce56cf58f5574192fac05cfa68ff27c8aac7df05af8d00c0ceade9035dc5fbeaab670981a022392c7a6bc9b919a6469e245e495d18efb008cafc0c2a6091999086a04c153bb1b5ebfc1687ce9c84de60c4fafaade8d0e03b1ff7e42a6969dadb4ed22ce969029309b0584c4ba0881575972ed8cae8a3f0af0aa46f472584e8a201b50bbbbb913377c0bb673e5c75f30b64dd7237d23a70ba7754b41b9722afdf648782ed2d37be5aea5b7501c89b2011389babc87426d0c03e73a844c0e2224b50ca3b8eec7da1a6caf577332cfa80ec34791c9a1be210b08452445309fcdf22f2a6da6898db929673aa14567250735d3dbc0111ef45e0681abcfe90c1c28c5c643e1a1bdcb2022b2d7d5f84ee49068b189e0099442fa08da70b94465585a7748a42ad937def0f05bc645bde8738af5a10b8969be44fd0b9135a7cec7400e89546db5d8f22252387469ebf2403f5b5cb05e8833278c6a07bf51de9c0775f281652e1bf5248cd79d24d46ac39ad67914fffb8e7fa2ec7a0703a84428566d98282ab22cdc1db035ef70dd1dd34a3f66490a12bac9e8618a0ebe662e53a04225500707efccc47a6047fe0f8a38ab2bf0de66ca5befbc6d1804b54e40c718a2b3480421dfdedb6205b0c7a4e55db86488db4a4bed28d1706f0650ebd26b63115a5fdbdbfe4f67da842a1a0927d2191a93867d799eeb45b05f06881a6cb6f17d7d7c69f3b19ab83d0d96d01329c62d2e7e3e0366f7c82372c30c6e76866ab268a0997b5bdf5e9fd16e561f2437eb5ba6607f2265d981cbea1b09412b25607ac219d2a23c963b8f6954abc4465ad0cfb926091be943d24956e50a1545514b53a2fbea31ed4ea8325965b301581875d1b3a15cbe2cea32404e7e001753b00cae91dbb08dc1f79b4719cf54cd6e6e565e7a1739feda2a819e3474052de7e284e8b48f1a99f56929dd25bcb198e7325f257847bdd12294e7bdb1ea0006e889d7f9383e76ecd875a7721edc24189a3f46a1ced68dca8b9647c478690aadd21ef3d876aacdf4bbef7d9e6bd9545faef25dbec589eacea5e695e04ea50d86e1e4e78a667bd6ecee484e6219c205ca98fd8ec141ef2a05150df56194ea0b732d803f8ea4187e0f3982a999430b4a8896b2121f7e9c3a32fd505a21db9803923e68cd387454ddf8a8815919577019d97f62f8293e19f265804146718e55094fe6304f655d3363ec2fc6da56ee1ad49b83605cdb46a6e6f33ac21034e0a9070aa13e50ef9001f35cc5c608fb28a3babc9bfcf0af3adf9f9d76b75e96a86a0cc458e9c60a14b76737264bac49219b4a3606f05c9c559dc73b828ca12ecd7b0a98d5c49fd02a7a6fce25026fc621c890069655a3390deae952630540cdb96100abcaabaefcd7df9b3e15ca04593e991109481300f0b6557e2cb42ee498db580c3e49e897450de1bc4732afde3553488dd27536e9bdb1a986b107fb49a07974049e49f2f1207b6feb3c9177ff15575eb787d35ca9afe35d8596d454316e35e2039b85760cd1af0a3ad278e2c3cf99dcfb12f88d5cf9115049cb1d01c90d4d4c0caa17996a15e513eb1eb507a7cdafa76870786bffb36dbada422f7e356346a50fd77c00b9c345e43a5b18cc4b8d5dbede31ef73ed2f7a2e9e342baa95ca2fcd006c694240c37bb9b68d9e538f4f89a74975a9bbb17104e5570fa23f24727768098f5188a86f8de137caa09383c5ccc617202ac5910151d3748c81082eaafd530e310d4e8d3d04aed7f6d99b2eee66850b26a2026c816bec75628cd6c53e0aae0f6e2a2cd7d931eef78f4c2823b36a63d31c4c68a1563170bc8f39aa2048f5e906e2f4b37830f53c95dcce9f2ffec8983a4b1a69ed3e589421cf888b974d94c208a76dbb03d8b23e68e2984f85ba2081b14265768a9ebc578639152ae48db2400d5ee3092cd5b235b7d178daef8e3bd4bad1b01d7c64596bf9edb18173db89520199938c74eae99809816df167e477f7ece80468d6dbf2cb15bebdd55ede8e390ebe72f254200d73f465679f1bbd5a21dca3534d15d10c353813fcec884c9b190e93e7dfe430e7f6621bc360d4a5ac1c8a6c63e20f28afeec9ba2458cc5899ba058b992a244f7b54948445f39fa9b3e53fadce9e5caee4a7b5ce72aea453f51804317c02e16e01a851c8ad694f0151d03fee23854dbc3461477fe7cb963b965d011c4d22cab40928d47a471b51814e0c8aaa13af6afa6aadbb79d8fd15c146def1c9713f02f16d436a2a1170e9f6706d06cb9a93d855cddaef6f99819a087ddd0c9b1e75032eea5d962a23634069e47506ea01f80054719611bee44dcd219af5039e1821b321ce445de9adbee086b4bd6f7c688ee69b018f4c4d1e5270ef668b09dca3d353882cad4ed580e9c6da6e8ea3cd79307c94df9d2907ffa818c4a8be01188d189692b555305b170f5684af402066001a3985dc6c3f364217b349373f0a5f77f8bfd5255317de69798d6049d6e4da64819f9cb2e11922b770f35888850b040702109a367d0aad5af3ce51add08fbebfb6615b5e97cc72501b27af95f70d714c9450ef98d42bb4f797033de6dabf626a13c809762dbb145f158a990fce00e08b39df968e2482a2f7496c5971c5fee88a61b2470f672967749aa3c079dd08d0494cb0128bf22667f335f69a65093e931ba381a9b0f520963336b283490a069bd3bb6d9ebeb4b7b3b37480df6579600df5d4ab544e474078d49c54d5d9bd0780453604857a8b23c23279e6603ca7bd904438c1777738cc4bb4ea9656ffd709b897577e9b5b1b5ccc3ab57bbda1d4000d16e86bbaa4c69b197f748d15c33c0dab64deaf1b0bb7c003c68f36fce92eb158ee0b9609a82ff2e861d0dec3ab4207004a6044a91dfae8987f647220fb6ffc98bb723ff4f2d5c254ad8a1a20f58006371240901e7bf73fb7b74a85918b113da19ee52e11cb0e6d1a0f674de32f4204136c2eafcd9b98650caf30e34a279d48e7b327cda4d9d1831a1c16160534730da30a3d1ee0179aaf3ecf253823e625ac8fefa747da6ab3b62b7f739e727b620d5fbc62e6f9fc8f8170cda07a5ebec248b5d7fae03436fa28bb3f8f6c40bb790a3ce0b6214417247d55b14b109acdadaece35bba3e1faa1824bedd1b88ffd5e01bee7fbec764546dc175d4a3681988fbfd22ce9a46c1a1a1b0ae2e9e36498a1002c1befc6aee1c871e7e8ae8b824cc22b0620e15f28abe0ebd57adaa18902b90ff0d2bbbbd484decebf25ca9a62f5b633201413fd90e8a3c0381c3e84921cce0e15479be8728bfc3f4575afd309a98bc2585afd62ebd3fd9cbe9174b0cdb2d308d9b90c306d1471d81c4cbceb98aa3f100ba9b69a1d57989a350e178d11e4d3072381327888db194614aa58e270df069e342e08cdebcda94b524fefdb3aab980d5c15cf573e9100a5024c955295b881cdcad27ded43c1f00466b43850c98d6401a4f93e5398b7a93bfd30ead9ee00a1ad2b6b04c3f3a5fbbf0e90339eb8dea60448db9d5e47da33948a43a002a0f80eec12e2438e2ed5ac27de33076fe8c2f708c43034bce35bae1a1ba7aececcc9dd63370375991415c6c5afcc554528a4520f7a377150edf30805678a93643c9a565162f34ad6853d67921029cf8747bbc4074d712635d0a5826a28f76353968219c66c65767a92bf90d0dd758ee417dfcd0c7d94808a377741e752322f8a7244a6086e223afdee97d9db6a0401bd486113095d00cf90c980206bef2b5a11f50c78e52f9b83371f3c53ebf66f6b1922eddf01b4414316f88bae30236cc0f69946acc1025b030940c780c9a354d149d0f74e0dea62d8c9d0b92c3d29b39feb1d1efba11f6835401565c63a76249f45925a0d0f8666ebd8bc6fb5d4f9192d7d806ab94e6cf19c7436d43b48733d098c4301fb05dada03fd0542bdfd1cc8b4166710b8f90e054c63d72bafce989be1fcd1b8b504b94cad888267ec4b12ebe65e39c06257d135eb8d836f8a5cab7291f7aae7280b37f0f0d53e515138a71487fbfa95293acb7807543b591d4feb380cbef84511cddc219bad47eaf3d7d66a7bf46c6491b7db6f1d683f85ca72c196150e147e740a0cb45083356d2ac1c5fcc2de44a0b4eb89437d396aa3cc9c62472b77d2dcba0b9e5b8393310030e4b88a9ee12497819b52825d8ce93bf4c1ec4dffbc1bf36b0e37119096e41fc8fff90b0e8356e5143a7c892dfd40b03c42fa9de8f72000f9009271033f8726f146e2e2028d603c6556f9afea88b7d5afb6bf9bc77cee1e6660c1496851221932c0da16b2df0d63dc9e366b1aea4dd4a5d743327b2756bb1d0115af3085334b62d14100a5b430265f7f0bb9a7e0c98318e6453fe2149e0c7e03acbbd58e3f2b5ca780c172cca313ceb7b1daccb3e6e89f7ce76bb3be5c19e30c4f3e04ad208d880d2c0d7dfaf2d4883b4c4527fd566684c8261e27561edc7e07893517a5f81778b9285f05cf6e42db7a20490331b5e04d7259d839fdf6ac990c43e8372350fbb1e3480d0bb4278c29aac781390763a9f7efdcc856f80651a40f6e9ef282061b7bfb01feb4e2aba7b36c2817826477eb5c081df01968f3304804b92d980e04548451b2b93aa83b85c8d6b6eaf77a328cd4591b5b6ff13f2ab3018f334b2a1a8c8b5aa3e11a6e254aa18724159ca60576fcca8a36494568de910872c9a13ea1a3c8e03ba161f91934ecad69e6dd638c118d36768aa48e95a4f801bf9ce9179bb52d9aa74370e54c1221ca6fc969895cd56b8bb6d0910581aa460093f8bb5789e325951867317efd078931f12b23a7c650d5d04f6d8b0b1eb14f07a8812de48dbcec9b6cc9d1de2ead830b44c860879132c071a87fafb82582150092d2995cb0d9c15f4e7c40db8c7e75657241171b4238c1a2647da634c1d3ad064a61da3cc88619ae319cb143bf0ad0ed6c6a2477a4275b627debfaea9fc4b9080b16d8967e2efe57d1e1333d6279eeebdced88eeeda292a0a7bae4a7253cbe049438b9ebea3b3d5eef67b71d2847e9f8d81e9150b6ac31bbffc8534e6b200107a9bc4e5a20d54294b969e5b4f86db18fd7edcd16ebb19fb9d489e381c7568d0425206af04fc22b8abd3091afda72f84df03799b018bf59bc50873cfbaa707006b6f450fa01fe2de1c8915f2ed169449bc9ef1b6e0132a5e50832911e6b8e3d08ac5ec9928226ca9d4e99d98cd7d6c8eb8deb2929400d83c9b221b31ea60a060e126f3e872dcec965004c9c6d5a0433cefa8cab8d00f3b2a02a8f3e7d530d6602e30e850c217c5abf155b7d66e83a34d8439582218dad1e23fb6ed0b6d7edb508f2bd16b5f13872dd07ee9c9ce9d91023812b61267cdd58b8f1e0a53868a1b108c4da1b0987777461e324d767126448d2eb2bc1c7cb74188806a334547402440f80431616b4cf8fcdfae34a0a1c79ca17ed4024f76ead2b5d8c5793ff4d09020f86a424e4908e7acc6e70454b17ac6a801f8a4039bcf4ac92dc8ff217a68f9504a19e1435a8066016ba71588b9dbb396e1583428385194b2a9efbc52e4351480cef841a0ad4b55e0e94891086154e4c2134a0d5cca64158512ac0e3e5e439990808a85919c79fc4db203c4630938c9b4e1c3f5d0009e5be28922f262258caf9063ec1e79dae8f797fb93a13565c776c06a1ae0e0cd777be870ae664cb9b7a180ecfdc6771baf8c658a3ca202160abbecd9114249b7a278187dabe91023d11e009ec638656b7b0697dcab6c2ef007067021283fd6353d24aed5506fc52bce2e000bc177f998dfcccfb957f0fcbbf9f3af68a0c4f13ec86564c23553227e667f90efad97f5bd2e2bc83a26c71bedc907d3660fa578e6fc1b56b78bff0043ea8e1045e6a4cead67e18d9d0b6ffecbe8f4995e2aa945ed610638d9913c8439e513a0aaab1b44f3e1ff178dea687dcdd30430e2f54c9908d8a925baf30d46a9d219c06b33c939aac44bfc789daeeee5f7683b4614436e9aef26e07de3998d05f4e2406f6f87f5b0550ee69a222c5269ea3ad538e2f5cfc6169f7729b09a8749c29d309170519470e0bfb938752cdadf47de47c4fb96f644dc683fe16c5f2432bd7c1012b73a65ea6791789a0b5b55e8023e6910c6f1a3de0ca57e3c1a2e79edd58940c2d4ddc7988aebde4b7c5a05f5870fe5d304d9d98d45be86ceab1d2f73d19d80c2d9b461b818beeafdbb1e22113e8536c18dc0544efa3b98981746d5a1d1e330ee3d6c314d867aebf5e20fd513d60abbf9f03b4531e891f853026db397fcd99038fb8014e28c25c0dbf02e39b958366296d26b356a8ed5146139dc3b26dc3750f9ccfdca79ab7c9f558ef3a5438533b96771f2a27b39c86ab429d98c55b404808f7c27d389d09cd5dce0a7a97abd6613075801a5719830aa8f9cb895f4ce500012f2a8d58279324428e8b6d1a9f78d501db290eccc745fbb5ba48379299284502e5db0e67293289d5eba1d64376600acbdb546bd6adf341fa00e6025d9029040448efed61c9d6a8a70d00de83e728ebd173c5027341d46a4f84ceb30d780b01065f2b1d135785aa5cda24dc5cbdced170ea3a416a8a0f2346504a6775be8f570157e6abd7b29f141a16338fc09d8c13204a26176769381dd990cb1ae12250b103ca7d92e2048174194fb37ee56cbd01ebff30de8945ce572c12b83a421a5114085d8efe065ed0cfbd1b8565a7eaedcccdaf0619554fe3155afcafc545ad7518033ba7acad1de25b17a9a472d21d79cd2b7e8a57e2f87720cf7fcffb9210397a0b3785a2622f17c22d664642aabaffb04aee5dfe298b588106f057eb95a7d2140741c7c09815c3cf4d732a8a2b7013dbf5bcee2282d7224171f788dc1421042a047dcde2abbeef24bb404cfccff5e3409909ab326a7e374113c0633e51806f030afc5f5e7ce52980dd1a24a88d430e97dbc8a6a267c131ca7dd18502344a50b804f635f3171b840cedbe492b8d8cd91a985fc1a858a2c086b4596b8bc5836ef909c649a370cc0790d95656c1e403fb0757f367a2350b46cd1451caddcc9c9bc70208b2dea622c68fcfca1f024a9997a2f7719f76fc23c0793695a982131e6b5a0b1f96d652787c0519a51c61f58f201f3a6149d9bb2d331179501747845a6cc6099924cd4818d54591564909a0fa28c31b0ef51ed1d14a103e35d0dc1f3088400637e6f63fd5b78e4d476997d04d807570a6cee6d0837d301823624b6dea074106cc2dd2d4b013545f60de734a83b461e07a2f9aea64e9141cdef362eb52bc769b5db93bbf5848d32f84a0888837394097375b3867404bfadf152249f535bd4e071ad3b034dc3463890e89258f9516993e108ef4131cce6c9f148edc7037e5f20bca38f9e28185128c92643f3c123933c354efc7fcb316068a45d835fdca9326046938c49b8fc377f5f937809fc811e5519d47516ec404bd5e030d212e182ac10958c3cb03bcc8c70bd4e48f2f1c38c75268fb49f02765f5af8b7af0953924720f0bce778e1248f4a0646669157693854259773d6370b3bed35d41d3f9c16d170157370feef0a864b85fafa9a56d906b929a77f764a7ce41ccc484440a6dfe7a0892d5d5324d88a8f5dfaa8b1a4bd93ec326780cf642cbe21cb743e5fa80c7930d84ec5a648e56937a146a763511e7d49b23153ae262e789f510122e6fbd527808f641372845ba91c14032e330cd420680c68bd5d322372efe398980534c1db60f9f979e8ead393f24c6598ac1a04f9d113de01cd5ce1fc5cb6b9ccecca5430a0e3d03fb1b9b12f9a7834ba82e0a09b722053bfdd3a95a9ef671f1c65e8a7cb8044b6f58d0c3fd475e6b3e846735e6bf9d60e59e9922ce362b47ab930c51307908693a7dc012d37c390a9361267aed54859a2460d9199346f6b57249cca495de0a592a58d093d2fa701d48d420b36cc47fe2f2e118ce1ace349745a938b7a8520a4f8d47ee877b23b594cae47f054b450247c0f6ff901b7c5cdef34cb4af05df0a5ebd563f990d4ecd27571fec383660c401fd55e7b5b4f1ce5267335dd5d6fd0d60f5f9d5d211ae9fb26c42b3ac305cabb4e3e55867cd1b4b29c5f77ed89c3e098469b69c56c01f923fa072b40b4e9aa9c9d3c308109e7be2cc0447e8f6bb5e0eab1de8f200fbea38f9a5689b74d8d98c717951096af1f9c44749f71fe9f1b30f50003a2b12dd3dacaab5809224a0465232e5b48b61e3ae54b4ba8dd8dc56470b744a315bc609d68d17d26f7415a473b00320f54527bfa47be11b5dbc7563d90912ebd59abc003e6f03f767a2c4bdc2accf6108b3d7a95dd7889e814a7f9e990f5bf4502ad8da8f1136fc4f7aeb5f4b14e5b4904ce6c59bc732042d4902775e0c2631ad9b4c127887f24c499ebc7df21a305ddd27e48ab54785626389cea8180ef13ba1883cfc10494fa9c2d991cd428dabc6493098b12515216c61774e45e204c85eeb1a8f9518a2e7c5df0acb25080e7bd7e51ea70410731bc57960cb6a3d0e4632d0b7a657ca506efb86963fabb7d4c5471e0fe8c9a3a4a0a3c4a840b9a20b62e5a2ba3f0fb1e74987d4ad69206d2184490fa4d7c29cfa5ef2b373ba48aa018f92c5902473cbd32215532e883241482ac2f7cc52e10301ec7cbddaf43f7b02dbf256fec3f5b52c4f5218c63390c3670d6df5a522cbe2c43fbada4442b7110c21943c2f9bc5a83b22d0dbd3c8e316b876cc821c09a346c88ce8514ca18ff40a9783c4ebee5fb4000e731b1141819defab26e152b0eb39e00a4f752c757fce0971c1a443670dee95a2f09917dc628619a5756e38286a49931cec7d99ecc76509fe5f057a3139f10e51946c636fdacb13f7747d4f84b90da74f9354781e6dbc0eabb365f22196ce98d47af36be18c97a383ecf41a805c72626123da3816a5de002c06b489bcd6bf3a2d9d608d5ee411699e6180e453b05472e6a09b7acef9d2893f97d6ffd42a4ae0e0067fa01525e6165e485397b3226c72716d595a9c8d1705d1b4bba16d73263cdf171249011892e0e15340f08c0f11bb5096d866749dbc0fc2f59ccadcc47b694804d41c5423a87e08ce6631c79a435da6106acd74f8f20fcf17b282bc357c02b248449ab32a3d0cd14538d8543efca9086953b6048d620b7669d6d17a8465beb31079f95c97fb7beb9d0a167073824f42274a7bed40b10d4d7263505d7ef436251c0b5fc4a246ce87493503250b048a254674657db37d05eaac5890cddac5f577aa2506c3e660e211f7618fa05aae7d79501351502c54087dd85686f2d1bb1aab409aed7d62c207f05e2f6a20317772e0f0840abec3840dcff36b81de6e72d1c573a5604913f9b6f7d48cbf872a5c3b17f2db5bfee6e2025680abd5c4db72673999cd462cee1fbfc633d5f9b68ba80f747621470e81730233c601427073ad3dfe2fff4390339a297e6e422cf7ac1cb06d2283e6dec72f01fc99c1a2b979868fb8354431887e96d95c81ff35f61dbb5a3e19231234f2230ac55e5f303a1a467b33bd9f215dcd32982cea23b753766047d37e148d2afa1a08ce13241e7aa35aaeef9388f1df8e172b73837dbeea97bcff5c75cf169b321803afb5f337b36690b3125292c0a90423fc66b1c3be3952c0590391e4f3c724c6035741abc3df5675bc3d12d94202915e9de6cde1cbebac87576625159f5de8f201f807b9f688b337368baafcd5e22327ac3b9b6e9b621f7ca7a9ac7393f3b4bb017dabae7606c701e2963e5e2d55119b2c2f84550ab1cb9200a11388be1189f90c63d3c88be75900cce6051e408ee316b79766da049af02f3cbac47330c741e3012ad302f0e3201592bd85321f3ac804863d44f93dde2650e7d9d85ba268de66038acef7ae5f5a4801702a385cd9abe9eeaade6a6a71c142449eaeae2bd7b3ca063fc1e7256fb02932d785765e69f18f1e2a4ed8eac6d63531363d5f80b348e500e39a9566f61744b7950fd63e7fcfe225780ab99cc5126ef48175e49d2b47860eabb67439fc5e04b97b1e1efcbc70baf099a472d24736f7dcf162ea29287cd003b84220eb2a05d20b5bfaacb9f4b43c2cbd3508fd2798184f0213ee659768e40847dd0990ab409d1c6b7a189776e3e4380f9ad6bec40a4074b2eeffe82a56e50fd335b471c5768307d41436eb90402ee6d068b6873b51390c35466ab067c9cf0379f0c2b02f2e11575b48c603e93010dd2882eec016e32633596bd27686d4b6072448fdc5273bb1f312632760fea5362bfa6d172968307079dc5e909e0789450d506f75fe0dc335fc726898683398830ba3593bbbc45351f4e606fde9dc0f8906f4090ecdfd82d96cd057076d735199db74738223f4868bee1788ec957ec25d03037a88493b95824c7d7405b7f033564208fb2cea34705755898b08867169e306b783ba97959b68ad305426c247112cba84b187011bc37ec38f9476f0ba02c40d797ef3150e2d88d6ec236961e2369673988366bd91a4a44146e31e6ba3ec2e097b9858545d992050e9820ade724acb30622f0a187d7454e862a9e432362af006f57a24bc6114ebfd5c1c407190b9a05136a705249239a55230a958fe017ced0979460b9b57a05b028eaa3d45a55b9d7fc82f6deca612cd22ba83ad22809c9d0e619ba40a4fcf643f044366545fbd9d79bcbbce26799e07b19109dfb8a377060a07230c55268f774f178a8c859b854f61be6a01291ee3e9378d42185203358b0af2e485df12177239f74342340036c5df4bee11e6d50c228643560d9d144dd408928c8b34d24f59cbc71f81fa4c0ff20a9633e46067cb4e922304a6abdd8edb0558c928e1175d4a82ffd7d2c26e29902304a16b01f064e5e24157406ab7061b05e4de0379893e415bab613faa2414c2cbfd773854f6908b7804f95a45f7122a058d6d5cf391aca958de84787f178a9159afae16225165ae3a555e85c020a3b00f3951d98d705000c317a3bfd8e93b2a352f7a6f9df6ad94779332c1a9e03e950a8b2d9928d370352b7a0e2bb225f770717f6803b4c89746f6dda8d5cc374872080f5e5477b5d15cddde8c33fc234383adc91599fc46a55f6285cad66eb06edb05713c142a973e27a0f602360bfda958f82448482f2aac4a4bfbeffe68ceb1770782b4d7c17a12b0d6225707f507c5e0592783c7d4af10ba04da2637474bf9b90d55560f728175a7da09a5adf5e7debb2287251607a037d8e26d2fcec761a22308a152679dcceeaff98e85d19b6ea0fc3d91a4db2b1661febf25f813196782b40d2baaf093b4119d1f7f6586423525ffaad31de9e11da631db6cf28ffe915fee005f4cbc121f8f67af6380fdc2b55e66d6e3073f06a5fbaf50e84fa29500046405273451d42101344fe6022cb3472464a66cff368bc37d5518ffd5d2c094682805fdc977fd4f22c4845cc6ef2771e0fac411e1c6bacaaeb57972c4f419b43e8d02d08bc2a8853734c46dfde5d888ccfac784b5ef13e1ee44c580b286fd914e2503d386b830994accb87b7046a1fdcefab5711234a2bb1e12ad254371fc4153e80cf0136da4a9755bbf2c713af5638542cbbc6e553a25d9547f7903d45ffddf310ea21dac88b85032719aff117a145803f0b3b070f079e87fd92cf2085f282443047eb6948635fd806f59744fda96966bfe357ca02cbf06ded0fd12a316a9ea2303c6d9e55c9a364a6917de970048a9a377edca642bd48b0cc307aa01b86c5c09016baabccb21a8639a3e7b6d5d3a29c6ef4522cf0fe0ceb07858d140735ac63c0ef5765c414510e359f9358a48299df40fe420f68b193edc17cc0e0cd597a08b02a6bc83f3c58c9171d9773a886f9118593819e29bec58277fe97f90b59ae8f40aee0b907029e21a9ec7d9685fb3a6c5ea5026cf9c41682b21f468a0adbf96cf0afeb38c3a2df1461e00590dc4f143fc2f4f8738a3ece9508adab869cc73d2fb080e1067ae5b9744da57e7c109a3bd6373f9d721d8227197a85e2cf4444f85130d386c8e0993b8fbce79b18fbc3e81a551fc6bca9061304e52997b22a27694df0a59b170ecc2a8275dfcae95a4be458420b2761f7dd7671953988ab299a8988903301779669d64847000f8bc3c890e8d7db81e23470385b572f2f36212f2e22f0298ac4096e91c644a3e3046564d56e59924e7151e895913b70c24b99db5f9c3048cf0bad9106ec8b97e2dca0cd3c17d3694db4fc018a13fa2f42170e935b2c2011d39f229f49c42a49436038e4611f7f0227aa71050cbec25a2ff55318a566d00434ecc1a569031e45ce69e0984f04e897d3b2159e0853386e62e7ef341dbda0d219b09fe95df7decafb79d4d6df094037ee8ae5d7f75ca2006a9f9e49bc617042570d386aeb87183af8f524c51134b953c3bacd82881148eb17b1939463eaa04468b8d0b44df1fef546f153ded3e92fde76766890e708efcdf887ee102ef300960f44d6e637bec21e7d3372b6f1a14d04b9e8dfd6e479b5aee05dea8654229097b0befd4beaa8c520036762482c507a7dd1ad8dd9cef96199954c1f896908606515f991047d28099df15950f880edb2663ce70554040b494c5242200c17ded0db1eeb0e6de11125f65182ecd66ded77ac44b710cf9d05f93f052898f2736a00b7c12e39918aae7b7b2a50f6f7d684fb7b86fc46ec9c43fb8fb3866d8e8b8dc0e4dd32eb0fbda190790c580aa1f85c3ce97b07a9c118ae329d0ada0825b16f60afd729d8f0e0b141f16afd3fe87362e099c306bb5b5f8d9952c51f4116a5965055896581143b0f1dffc06dc0e3db2949bab098c500ce520a715003f406251980686c65677c9b8bb34966324afd2a005bcc0be30f4b64f649a49a7d245dca15b07a518442e25b481f6ff558ff9d49788d39e0b873ee088ea08cc993207d70ef902d35c2d1e1cb3cd1d45ec7a7ffa86d136209d5bb003d29f959bb369405adc410b72e642ad3110b825cfde326d14d01762cca5f20df7604e3fa744ffbf17dd5e01df5accb76afe0c30b3e1faa8397d8863d93f40252c0b4f7665639207e3eabf025f855c7bb344165446208746950eaf36e3acc79c1a298b422c346d97f8a73d035b39cfa8c40bbf3715d13ea08aec7bea46cbc1eb2ded009a1c72c5d055f9ef044668791eda0122af6c21eb06ccc735790218c6d13618664e87b5373f95a3fe03a23495c933adbd3f519c29e5521b59ff7763687984b68ff5ccb57e05c4729c0e0a54c61302a4e88edbbce5ee4828cf3c8a83e940cea979e942bc2f6fde99920a232209b4c02e70eb5e8b777f66a55aa0f3a1133d4182bd3f3f2a8c0fbf904f0c4352ea603b655492f2e04312e5b813ea21930ba38942ec08d9de013bb428fe0e8de4a5bc957b72f834a32face3b52ced2bc0241c366a5fe8f828cef0d00ee50ad5d0c65e7fa4fca9c3ef0ed2aa6e26a771566eec151d22b337726c474e776d0910596059b2bb0c9304e3d77e519e72fa29e7a08c2a2afd6e70993cb3f2ba210436648f86f627592c3558567fcef6848196802d30509ff702b1fed7a59c4d890b0ee26c7c77aab4a12b52364d208a59edb494fb40d9a4c2beda3a09141e7b1e03bd86c4794ed830ac760e5c06405a581aa1986df6629b6fe5433ca63fc5fd970e3001bd7fc42dedc35470bf5daaa9ab16e4d5dcb290c74ee8aab41eb9c4eed10f750ed62c6a6b668d8caaed62500db494f92fc55d1f860a2e14e4bc415b6a130ae1297857487090fe1f978c3251ad8521921ac044ec1b7a2d225e57cf6b98de042796926d38b106b7e3020d672a4a9db0e389d12fc9bb238602354314867ef70dc9a4f544fd93f248489d53073ceb35e9a5794bd77eca54e8048bea6fa9d042052c2a5344b29239ef6594e3b6787fdd1c1546f10f0c61dbb00f861e3781d83e0a92d8df746ef80474fe474b445a08010bcde6d478d33a9de04b854a18e290c80023c29b843042f0e9047f0e3017a35a64dad94d929cfbfda29c3197af040981032c7bb68bb4563ca7b134a1def916ab0442a3656c5e871f586adddd2eeb93d80f0bc0ee66683cc5bf0eb81625b9abc9eea09ab3a8af48c3be51e578cf79a4f80c34a48ebdc8144a2c9e8389e8d0588ef3b29c60bc21474d18070a9ac170f93b0c2a33bbf3985a766e24e7fa7499739b74542805eca1ccdd5bcd27f424bac72302dccf38958fd359d6da22873b00d5c9393be5b5f1e09d18048a82c374aa8b1109c96457ad2f342eb7877ee21af98d2b08403184dadc348f1476b39adc6dee920b4ec5007eda2fa66b3d10a557abae173fe7dfdc4da9e0cf18b63e703de8735b0fa49715757e2f35e30a59148d788e07e5605308eee079e14e534f0ed11a3c4a0f5e78888fc8d6bd50b013b22d74b3c4e13067f1ba4f1db3a262de62aefab7d10c11b1befd80334da7f554f32b08c5f1f0fedcab31974612ddd63d5331c824e104e509a0253291594479db15dda7b46cc2afc20d26e855fff2323ef05274d22104c26f33837a52123c7fc0d2cd9242cf67982cf020869289cf23a84320bd1d1308e42c1210c22338dc0054fdbfb1c0484bcc0ca75155bb7adcf143345f7113ac0cb29fcb8931fdbf9f226a668a2fd3ceda3f56397896cb96c6f2eeeb3cf6a8e106eedaf2470d92335dbefaf7b86c3377fe906de79af4882d15482e2775466aaf09fbdb17d1fc98f36a18825b8a11b4e2d4250a3dcaee11073c74a4ee9cd76f8303516d4dcdd7004777a27cb942313fac707f194e1e07b80e340a62ff93dfe6fc03a2eb94155334ae5a41c0684242af2d819d060abb2f89c725618f6b1ed56f41016d0a98d77a2c2a6c2818f3b34628cd588a9c3691d80f017021d3ef0560f2ff0de05b82408b4d951389aa2096897720f30af0f006d9895a1ff8752218e8fc91057695bb44544d44a5c791a80b4cd7b0ed989fdb133e643f4d08ee949fc542e50915e6bf41277b01529d89b3f3211b2f426219bd165775d71881abe2ed824fab045f17d05f5f73b0efe45b15d22c2e890c41230fe6e63fd7da2c51a7d585e3510e569e65e348f0e0426ba12fa8d50b51801a01b4697d75674395b9950e65f25406c90a0024f55e9029a7316ccf364dbb583a33523acbe57d3be19186f6d566710efc958be526831517b3ed2de7ba34fa7c6daa2620bd46550268274d034587fb0190d679ffee07fac7fd050871f6270eb2b9ff98953733448f69b6026726f4c80893696e29b9f47773e2373464ffcdc6893a3c0ee164f8aa2fed8a64093506500cf9640072b193c37cfd4b7a47f485f3c283db848c78e92c4c3a066ebc37941904c7f918a8a58a752025f9db06150f7dfd04c4d203509f25796e6097d3f72df906f8306fe23bc656bc1eefb8288ca4e69f67e679fdc0da1a8e3cf5ab7fa295250dc49063c851259fc6a69f40ebcf1ad56e0825580f5af9e6d9718962ed90c2fb0e4afff55ac5808e254822d5bb320b8dd4760cf54fba1df9b32e716fa91520e503900d2848e8fa1a31058b9b333e2346f6ff9a4b57a62826c9541a93632e780109d7e63a655248e7fe5556e1d0496d4f2cf25dd41e1599bd509e70d17157eb0d05c082a51898b59a3be3d0138cdc1602947ffb817ec6a437fcd10e57d05086f303cd87509752a83643aba7b16e46e5d9d814f64bfaa678a67f0f8fd68678c47c02dede23de7d12144342e243b0e70f2d54ac638e34b36d6a4609a570a9e2c97f00f9a3f15a0e43b72c0c4c36946eca3122657af7ed059379de5fbd478533eb9903fb6ab4b3db148f6dc0b36d2b553c3a60e5382016177bfa9a6bbb7e1ae98e98002d491362541bdbb3df9539c3ba67509a175ff9872fbf9d5b3b8f3f7a7a2d6603a27f9ab85984cc436be911536303c8df507578d18bbe8e6a16810dc095c5400650c79a0840fd13c59c8ea9c505f2927ca36a02039d74fbd3d6280fbb0a132c00ba5ced8e6bf5059caa403321bdf794d6c0e527b2dfcbfd7a69335ffc7416fd0df7d05ef484f0fddf19c57049a7d67c6191e78f86ebe2ec6de6bc632a661d9606a9cc2200a3a9495b794ce4f0a5952966ee077e88aa66ff2f20e6b2b1e2eb0b0ceae806ba41f0d24372f2afcb09f917f1a96000ad2527b8722e207f86d97a08056eb4af73f32c066a993c1b81b5968e7e28df8681dbe6db827662f8959fe6b7045e6df3aea611ba8e9d57f1e7fa0a7b46db30a2066dff8b2890ba8f3a3a72180cdde742fe98309e73973ff4d92fa8cf125e4be6493cf114c4f4e2dc978f6f5b033635a7d83921a1ce65dc00c9883b35ddffddbe71d2107e4e876b10780aaed60a5c4ed0b9634290f6e258d48e288b7e2730997defbbf2f925be8f688ffa7f8d0d24a7154e3b304a3a99a20db5d13001eb2889d9d2c3456a4d94daa13ca72ca000efddc09150e917fd2f70fa6c9d9115aff805e0489db6e91663ee5e7c6b770a073d1cf6c7650e5fd99d825663ea2f0afe550ccedf545007480516a5343760e3071a82c8b33ae1173800177bd214550bdeceebbe7828ddadc4c1e043a28aa7ba0af73704ed11a4875aef46adf6dfec60a11b82c0ec9d020fc4733c312892388a01d4c682c15d3580f5eec33906d6a7fe4e405ca96eb9406cf6dc85cae261cb240b45d9679861f7f0f4ee3c73dd6743bb8849dda243916106c51d8dacc4b6e4e90d09c224effca502127a6c2f0527e4824a4e17c73a2c0a4c67bc861e55f4760a03560331c5cceaef44f4a58f80041bd5fcac4a6469a866aeed66ef7c60ecc75c0446b49c0b1339f491dc03e0ca880dd6518ba28c9588f9584257e5da33363e4b06d7dc36ee6210844d9eae6c04f3e04cfbec30681026cc6e59b3f1f9f565378503d9456cfbad0886ac921f125961a8efc9eb470337f905d468179af47f8422690fc12242c0a0c84f699df0c6f066f0183bef72a4a96a17c692595c77efe9ef740ebc6deafc44f086ef01193659b91cd86ee53ea034b2ff9925c66ea38458c1f8054c4f51deafbfb95593b6245570ae5ab9e4aca7b29f8508a1b787fe32f903c407b9a37021402ac3ab70deefc3cd2ebf1c8ba662c65d9a068a8b14e5ec99bf54060b88a15e078bbd52201841d536eafb2f8ea9069ae26e6eb3374cf6c25991380a088e155a528ca381806eccbccf5a27b5ef6bff1c8dee0400a8d902e64763ca0e65dcfc37b0b391350e3890c0bae2396927b06ca662342e853bc0237b8e7df707 false -check_ring_signature a13ca2ba0538621ab08fe8947d8ea614dcbbcf738666eff80d2e92e0772ce79a 7d21fbeede1ec81fe0c012f86497de11d17a07d962c649b934416be2e3eca19f 8 914f44b6fce6df64b9eabf794f85c7f95492ee11289db749bb88a2dff56639db 756f52262fbdd1456d917bc8b60163e1c874101e9e4688afde155584140e7ad8 fb11345278a3986146291a2df6052a0461525911b391a206dfd2d081fa6cb65c 238aa5b011d0ca6197a4677940a343ce4c70ed882cced15f95c6d6e41a5a32a0 fa3125a5768a5788350e10431a84a7a46e55fad0fbe1aa98dd9b9d299bf394c6 19899c784ed69659d2fd0366467e5a0e05a41e68b78198faf1489b921ef50673 df13262a687f210630b3619680c77172a23f268687cc1980a04d2877f2467317 4edf47b663d61fdfac74b01749f580aa2340619a1ec1333ae1cb2d28d8083de5 9c93fd33427b97bdd63678be72aa176a27e99d425856b9ba4fd8b8d76867ed0269e19671d9cd17176bfd25c4eac1a434f71ca0441a63478e600a8fa554468c05e490168bf030404d9689f85363c3092d041bcbefb51b6239981882891be1d40c67075c0c6dc4c0347220c77af60191126b605e30ef695a7cb2d397535606270912fe8a9ee4e7bceb79ba82f2bd2c3395d0a40ddf31dc8b3840fac3eba7ce3b0dd0ddc6dcf80f035556e2b456cc8d1afc52c3ac93c1a4d4fff995d69695fc700bd0d3d7e64e94be945a95806fc67b02b5ab274bd26c6c7460eb8650a9a65a0905cae043a2859ec34dfd68a680ae37e35d946df867e52cd1ac38fe62632d8d9301eed5408cacb8dcf8062222922deb7c56752b3519c967f51cff0361c563649709dfb085fcc4b488dd703de468905896f8a7f37a7b9a2501f99993234c8875c10afdaa6cb85945364fe50ec34106d0ec7918e7f3c8da1a63ee0962f9ccc40c7104698746fd9214c09c3f2a704e51d1255a46a52c7093d3a34aa2158c9760e79c09816308a736e2c91f3086d3978463dfd06b9efc21845745172ab6f10eb1eff30cff0cde3e7b846b873ceb4f76a88a456e8d42e614b7adbda4c7e6ec2c61a12109b9b2f91ff3d43293a76a62d40dcff1ea88d110c96d00f27a6bf709187e17490c2cf8bbf8330aa0e556cc8414cbec200ea0d1ef80b3d4e14dd08c26a3b553b90d true -check_ring_signature c98ef1ef21a48414f985b6ce150f538fc0d9c6dcd0b6408cc4b8566adfe7ad4d 696bb839f576237b0ee5c7213115478c77d104d9f72b229baf1a3a4954146c25 77 174144d2e13cdcaa9ed1a39c5c62ad432008d4e4f9b2b35dbdebac9124e776a7 1d9a42a2d17cae43c4f9272a03b2847ac42b2f63703f94f69ed2f6028ca14fa0 4ac64efd0ccd5fd4c310cf9d8f949b16b44fcd3edf704c59137390d04f3c8e7f c8b2db867af423918f8caf8794bb1753805844f0b5547f859d31aa2b4da394c9 b3faf88c6174a7d9beba52d624a4e800fc558e16823855a957f6a0509187271f 8ff91f126dda6fde0b1a2219397d4ff7f3d3133d120ff71938e9aea84439b772 24df288a6f57f35c09f79e6201abfdeb0a2b2e350066895494dc64ef9b3f53d5 5cd0bbad8c4c94d4d7591bdcd46893ed451345f9dc3e03578669f69e17739972 dcb4502d8612d44b08573f31abf16e099de13d348df35d56f190bc173b14d76d 0d0e0e8fd3f4a517d311677928acbf332c047777756c87f02652b0c20fb077fa 01a5569ef1b3620580324f809226a43f58501bcf2537dfdc69db4e34f134110f 554c679d487fdf9d102c50227b24430302fd5b6c3278618b194c4990f0495e4c 0e631c3c22743680f96c29dd821d0c59d4ba5afe4097dad1cdd805d838aa2fe6 feb46718840b28649f0e0af3fe1098172f4b6091aff94ca270cd95a209f3613e 28bfc1849bc6c8a066ae2dc643fb5f68dfb75fc4fdcadefbe05e9b29ac771f22 209592a5b7242ad5a46b2557328b36c58815e8e4f0c275ba6aa9be7286482fde 6544df432b847e772e4d3acb33380aaf348f4bc3d9d581be843ef08944f94b1c 9b919039594d312e4c8277ac788a79f44d432f11eabe0a057df221efd504c4c9 5b179e90b5adac68ad128e2dfc688dda7bbb4ab4d7a9b370f74d568b0edfe0c1 00d839e6b1e6817f50c54793df90c5909e7bcabf268212af5da646d3e8ee48a5 ff550020371752890cb997e7a99fa60f17accf4d49f7b610131b05420121e205 705cd8ab69ab1b88a356e45b73b07ddeabd1a3f867efc3cdbc9d06d002508466 43b7cf3c7c2eab384dadf9258b105bcb88f4f64ec6d77df922e1a6160e0b2173 5af478d4e4620a305189b6c97136c2d798883bb10e140a957a47ed7e3aa990bc c9959225c416ae5f6016794ddacfa604d084615fe6dc2ce49357e92b25c0848e 179c0ea6669c78ad5511e6824ca471695d514b7717f87afde8b771a2b5dd7186 771790eade1442d61bae067b26a937bfab6b18a7e58f97f7a24676d7e6bde58d 42613ef66c464c93d645485bffa1a1de38b57ba77f302f06f13d2894fca8e41c b590315beb50c6b6d64361f10a2837acb7e944f9bfab56d0cd274d8aef1cf4a8 711449ea8e8536bbe43177fa8db9a2a97ee3ae99654c8626a0d05e3cdf1bb31f 247e3bf95fc4ba59a92fd0010c7e89df4c7cc9ea96949027e89e4a57bc6ad102 abd358adc41c5589e0fd4ea06a08dc879328b25adb9b97be1c029437c99d61fa 5b572850ee51ca14e8a36423f2ebed8ae4d0444fa6ce4652b9ba3ea2735038a5 9acd385e461fb7010e19341c78a741ab18e7c14aa813ad5156bb650417193189 f144b4db8884c9f028e787078df4232702f73e20514acea8499001b7ce7f6f6c 05647cf5334ebe15d5aca86bdb0af5ac0a25263e7e673ec012b3fe6c99fac92b 0805fe5ac222a8b34939ab3865483518dddab45498460e025be8d22ff9063678 636045ab6c223aca087066f441c2f8a00a5e54dd18d963adf9cefb943ef2d5b3 52d15b033786c93825b7caae205079c2bc02e7b57c64a255b2bfed646d78ccc5 006db202cb4ad57c340b687df350343db14f147811fdd5432cbb3f0719329d99 6946877a707533ceae915714522dfed00dbe59c1fc3b52eeae4aee2e1bc3cfd6 990da17547047889d9a7c4f91f6c5673996bcd9e86abdfe60e042eddf80caadf 1278d02941f5664442a0335fd363e0ea5d9d65eddc42c95e8c9be172443c485b 64f3cf216d30bdba10bf43e33b47b487e1db83ea48a0cc6986ef7732bda43f03 fdf1970d7d8f95e941ae87e6dffbf4d3178277d19144190483c44b779b2cabbc 1ed1f21e5991d1d2cefbb9ccce1c742ccdb603d7917859acf0d6798df3f03e0f 24be770bc8fdb0151ba92d7dd921d20491b05d4f88f4569526af324dd74a404b f6c84522fbf5cae78c89b4144db680dafdfec693e441e8b4dac8397210855fb9 86f00c16f0c94c2514a41d39d3bb02fe19a3b0eb7811ff79b0e12644e384e8a0 e0ab1a9dc48646af03c1c488b53bd8aae52fc4ffde28b732963c4065e299be2f a0fac10c6c6dab47fbfa46d182d09870fb1457475b0a071b137d9c738ea73bf5 16eca91a7b9a416fb4143998d90eb1579a6c34baf22e1dcc7be519e7d5d054e7 5be15940cb9eb51eadf55c3d80241157bbc5d76d301772031bf6e3e0f9062d5e 6dafb56d753434bd76a5116357494556cc90e32443d252be68163e552e023cf8 acebfe51431a1b1ea13bdc132c4df1f7d247fad69b384fdc81bea75627beca6e 24fe3d94e2eef96038e42bfffbc90d681f306e857002e1d442fe51cf527eb0ca 43e9a139e162a03688c5f8a0e2010d0be6bc70389519e7bbc4f8d7f5f5778a39 39de2934ff524aa3447e86976474688aa2fb3c044a55145f4e02347e9a8a85db c368422c27e99149cbdb5ab236954b30ee5cd8cb18100c09a872e13bc8951416 eccb926091e4ef11024a6f82cdf89a0ddd52c9dc1eb139e14a950c55c4f93646 d11705362bfea2c19d6d878f516f88c92bec9c17c813f3c3178448486e2cffa8 66c30d0f6e8c70c3019857dfe7e4a203e819943eeff3e615706510fb4c625721 831d8c5430decb6ca8f8a827995ec447e3a7a7c042d9dee9ebd2e5e43c1a5e6a c910532ea50b130af3e939c8ca9be7fd51cc57c28447387160e049f3eafd9fa8 302a41d8147f4970c588c2a9104b26f09eaab0fcdf934ca7d8eb84c5f1a1313b f173360f0b2176fb1cb7d28642d63093a7dc61f28df49ab883a753659117cd59 fd151ded7639a95d15094d34263fbfc47055c2564a7fa3b911dfcb112e797fb7 f56781f30593aeb84190438870b43ebce34f8922f5ab30437917d06e98a8dfef e7bbec1457dcf9dcfc5a031459fb48fe8dbafe182749fa3aa68d93c1b252c8e6 a7712d2fa423b4849fc8d9917c427b67c0bc8529cd24351b8834a8837287ed47 463784ac8cd7f1cc1383679965a1888b46006472b5feeba90825e6bf7f49f1f2 b8760ef9e718024d78b1ab52860c0b56b8a02aa3ed60004803847db0ad930173 6c718d0c8ed5e5d24adba50897590a9ff5683be41ac78ffde1a6c301e588a0a9 dd82dbb8e1c7ab2baa02bff7396df81a9b9614a9d4bdb4bb14b138e9d9dfe5af 48cc19784faf7ef22eadcf428290eacfde0485e8619a302c295935d70f27d970 fd777ca1aafe9367e22bd9df1b71efdd6b9de2d804a4b987798824900bbe6893 289bf62cea9911e1e5ebec24294b32be2fa808d413cf0e03b0c3b6da9b26dae1 c6f7ffcf2e648fe5e21942fcb2112877abc086441fa81d16ed58ae5039dac70ca50e42264602ae134d27e5a53d7773a6a5e8e563b200b26a44a4c521ad0be804ef73c128d5a8bff53cb6b32a2bdd22c975d2ba105ca1e4274bfe7e41d888cb06f76cdc808aa1f11885681b97faa87b850b002dadfe9b673dc91c367c9d694700c2a97b00f75b4cba162ac307d242050dbce37b4515ba504ce144465990acf9019f082b621cb0399d7847a38962b5197b20cc80962cb785ed3186f2d9e1722300dd5604532c6efc580b2664e0a6bdf08cc38ab9a73f5dc115f351aba2f349360231b796406f1ee4a4ac0dc09ec76271b05d8d4ef7b53beba7bac60d76dae4ad01bcd8fbc4e8f8d009d07ab805e7935b3170255a5fa8375e72a7ce4d9e590d350b90ff99abc759e093b73c51c71c2efef8d343e6b05e5a29aee8a0d84693a80a0609458216a604e3c7748bae7fb32a6b9810cbdd62ba9526aabf54c21eacb9f9054ee41a2a1b4a2f7e61a0697b633ba6cf6ac6d7345bc3dc2b353f5cc2d59947018f1ba8d2f37030f036f2abe6465c925284459daba688faf6331313dabc79440959e7fa576d0735690603d29473bd59e606da184d5556de05d1e0f3d2363f7b0d1b52018051293d080cb1cc1805cecccbb453b1837815ea095571c307d7929502a0eb90fdf626c67dbf7f96d6321caadc2206403e8f566fc66575c909bff365065cccecf2a6645947a1a73c210fee5c93601892666cfed3b4f77e56fa580b0503d7359fa50fd81c8aa7241375ddb8f87198f6e876a258a7744edeec7fcf2fdb01c656de63b60c3c6afb683d0e6329cbba9ccae9abbd949096d981d55874c0ab0517d3cbde5a18f2df4a5d0c991bfad41a10bcb3789f1038d71be34237d8b43a0b8c6f60a048568851ea8a378df1ccf850d28da5733ddabd696367ea7a14a72b00f5f3e7153ca521a7deedacf10d8ca08f65a47f47dad95985815ef08cee5c3801d37f4c7dabda7386b54d680c6475f21a75e258dc3386f335abac9a628a75de07a29711e1c8fa94a844da35bf81d553373a843285656859a07a9fef42ae7c16027505d323894afaa2a50282d7ca4dfcf6181364886d197e5e8e16371623ef2a0f1420bf391390cd6a8f3e6b102395c9181f63b4a52ef62e97b3b67f9d9d436a0b7e3832e8b5d1b2633890dc8f2bbf17d513ba71f6b2c89b29bc2560de7acd7c06d5455f7ae3b5b05eaebf602633a5729809b4ca2cd57478448c81fd4fdcaa6d022674b5da71199a14c47280c2427869de2e043d86ff7959a50da9dd81b886290acb8614b45d11b378cb159a05ff548595976d1a55ea751378534c764ceec4d407025170b0f90fc9a66c9e3a303f3784793446f9c7525caf8170b27a87977bcc09a740821e579a1f956e103731a96b5478912428baec1c8d6154a69820c319f101a24c0278e10eb43de3a7d6f7d47500acf1882d6eb58c41a71acbd85e110e10069d4b31e051dc54ac66b035109542b87ae6c6de8ff1e748b65e388855b4e5630cd9fd545a90bc720e2e1bf0f78908000dac838514363c13175a15ff4185dcc2056d871d173378ebda193ef78dcdc68c3d98e5a3ec3522d0d578beeac407fa53092d660332659f074bbb3ec198d864998a983542d684a889362122dfde778ff70b32fa5b767b9f2f95a3be7b213e4b64cdc46214fb52a20f7cc40f1aae1dc22d01243f272342cb132fdcd872c808053a5b850b9d511e679f0f466cd76deeed0c0b97230cff313ef4c2f4f20a1ff5f65cba39c46053c018309c888238f284879908318162f89d934b2867960200f03699613ea0ea6387762fe8e3eb8c6ec510f0067852f220db30a2d9b3ced934dc0b38da3c691bb9a8070becdd7ef1ba3bd8ac0ae79b2be6518c515e7ef8d91a7c1ef34baca3e60302947447936ece847426570ec736bad2963e459fe40516c5171b3a59e65e7a858b70045eec5939a981760507522e2994ea9ccbecee7351baf38b4f1236d69e42a18ffbd36017816f9a3c810e49ef7f8ba786fcf6b56132ab596999dd8401f168705618cb4ff7d432a4628300a4819b69c347e73119241c5e5e3ae8582c62fefd4b812d306f5e8c3ab345740c300ab9ccdbddcc180f74e30a1645eb1db2df82adbc14a6ba766759388050d30d01e8b96256481594be03ba2ebe1bac8490df7ec070a13f0908f4877c6428f20e1842caa30db6a45f4352460b7df1ae092581bac8828fc919fcf900afcee0130d551379c17633e6fefa071ce0b11d6560ddb8efc64480c156d257ce47ca09d40965154524ebc0494602aa65f5a1ed97798cbacf491ed5284ae516cf7791782c00038ebc1ca04fe3bceaef6826562cdf26f1f22d3480a6bb196b35071c5c1c4509d52b39f4f6200d6fe8f799f348d559e1564feb68aaf97bf30b2f3700a04b1c0d76a9864d21780c955ef51462fe24e3e0483319a582375b954b5d94887afbdf0f7c8686b885b7d4bf7b09b592f88cdc6ed7e4388307505bc55eb87fa0d7820304a86dc92c40f41fedd2fa08b6ccaf297ae10146bed3321d5f9e336038c2548e0545fa9e65f4909604b7f88b1acabf6640c58f1b2098803f30f1867088e43b3e0607dd642e597a0deb317c673a1b8fde29e444788825b3da4d3dd0a1360716d70c650c7298ab7ffe2febdcab5b46f33a821520416788daff4d0c31958f55a9900959bbb7e4e3bc7713bd55146873f52f18da0359c8bc622ce4c13ddda092b39b0ef9f36f31c6dbabfbcadc895132f55038f9f4ea3de269c5958d069f77801bbb05e26840750637067f033c65ba693c563d157adc8936327b27e5167579abcc8507453cbf8e21ef4e657e459a0202ecf5783e538a5c7a265666c53155dab0cf6507eb4660672e97019f75e7345b8db5ab7fe2214672a541b2905aa2858ae2706a04f86cbab1b8cfcc1f11928ab6bb7b429f0365387846b2d520da6c0955c7f79a0a1c2210735dbbe462887cc8b2e92e6eb6074e30c0cfe25a7c96cbcca9908cfe080052a1ec82bc28c21e28eb43773cc0bf3fd8073d9c253a4801fd2577eabce5024f2db2df9eb720e09f0e1bf04a3958ee6b89d0c8b5bf6937d2980099e19c5e0cf22f474dcdf2c1cd7cdbdae5cf580b61177c78640d82a669015d432833ff21043ab48a32485782ab0d0cbff9e4e9da09aad102aad2d4854b0ba145727672d00c30b6f63d3d06875c44cef74e71b1c6d1ada2c107b06402bb659b876bbf11f008cf587cdb59f1ac8871087572e5186d5aa4666e93488944c1e76f3a4af6f7840cd0185e958e9f7e2a39fb6c755d7d371fe9695a18f146b8c70211241197ce580a9e5f0373017d885e1ef078aa3cc1dd3f78af806963ebd3ff8e58869ce7f37c0e3fd8d0a620758c29beed33a0de11c8f62479af8e78d7368a50d5d0043e01440094a41ca19b91f3bc23c88a7b3f238d8fdc011bde6e73c9d7f3f2ccdb248e820593673e915be2bfa4fcc243e60413cb2e117b1ed06e83717fc8532124fa160b0af9124ccbafcf737a235855396c34b0acce2e461b4525721692f7dc22d08ecd0b72e1b2b602d5a79e9b4ce99eaa78cfb6274ce08ed1ff15297da42b00f83bd20d1d2b29b5d07fa6d65c5a6c23cf3611923cbf81caf2d3b95f09be331da21e5b0d166ec78e5f286b8c4ee3f2eaad8636dedd6e2a1738cfac5867db64674d2b860194e636f7ba3f80e1fc8ae0b1f74e47f186ef85777da1a3032d1e512a0bd68104027b07e9ea5d0a99f3293179d2225c0bb4021e2af794e48aa3591de559264605213cb722e1b099f4d6b72811edac1996a2670bcae337d21126f9f69a338045070890ff1faade726d130616155e8c302e145acfb2c6288075e63b4d0ce8f2b5055f0185b622403362d4455edfdc677a7ceb74b33fe7275d46364f0fae15d4a804b3d11d758b6d437e5e77f51a97e12dc32143b3c6a1c3e90d007ef4430f32820be3b152c144ef5dce9baab3b4940e739ec9d82d44f96fb772f0818931e190030003e9cecfc62555ef44e3e9ebc0c2ad0a7a8266336bdf3902b079f4f160dfac08f29d53c836d0b5891af24d253ff13105d30f72229aede78b35b13fd15a00f804819e1640b155550d76222dc4d022351c036cbf98a59e25f7a75b745fa42f49073cef15837e3b42ae622d1ce9f9ae7546054e01d754ff8a09d234275492e7460cf4b204517e1b4173752ec27730528510b8cec491397bca6a97133d17880c2707eeb88f9003796a925ddf32bf37455eaf5c90eaec2ee628b88323dff24441880b4ae982c4d23495743bfd3640fc50354402e9d81d34a5595377bf6d4983e0b309a267610eeaad6b9461d7e94695a0e349e22c3b87b1f4e05d6f19886928866c0ef041acc02aecf8f7a10332231f7b16ada33f935032d9fed3962a29f96fa9dd0bb503207108b3abad6dbb35dee9b49447e3a62c0338378980b972e4151fa3e907a4cf35e59a78c1381a22e7a2ec034da2b5b77823307b307c525148cd9ce36308433743917f4044899a6f703b81604eb952b934a6fa449c8deb00a0e7926b140258085bf683d0de40e4049ca1904caa35fc16745bcc6638c153d413afdaa4a706b8c6b30e8dd6221c538a437391891556a4089151095b7d8d15217b13c0559e0a02cc366df830d33b2f14133ef93a6999259d8263b1f27ba2514a81e5951ee8042a5a5a777d2c62dd57160e86139ca8e7e4cbf161b8ffff9905efb54fdbd751022ee3d682f039b00e4e5e0d561150ac9a8fa915f75e115984595dc61b4b8b8104149e98afef01150cb7170888d00f8ffd05edcb27d16ca775e053c50bdb90fb0c9c50c032f45ce52fdd311ac050322027174de0f7983e48f4e96df471b9035d02def397ccd2b158831a866b598355fb6418f09e3a3b8c2fa021c749bf8cdd130f126a3b98e09853ac618d0ee6a7670df21f40142b0414ce9d51ad3982fcd0b103301e7a78705aff63c07f5c0ef841f613eb4583de495a968e42b661f0e7b95d0bb12724608e6e147bd71fcab1eb5dd7f1b3a9469992f821b16aee4d7e93adca0a9c3e75fe0eb8c420ede5f18056ccb59a62a606a9f7ac5385cf7c11b7d1006000c19098538cfcd1e09398c4f279df61b680f804007dcc679c745d829e9ee18d0048da7df1bdd71f2d4b6d9abf5aac0089549d6d54b5a893c8353af7c930fe2f0c631c8fdf2c635d506d82b7168b835295b7c1c6394841fb0d57ed17ff96d4fe08426a680a47e669ed84647e92cc62332ee8e733f900fc4726e5a21f0f0151f90a1e06b4e6b69ab9399667a6f8a2143184ae29daed85250580265aab81dd7a6d0b59657309f4c8d44876399b4555f14b1e42d1b88a0c9d4c5a1692aa5e1760450897c9b0990cec6b5dbcb41c59d4fbd533e6cee2572231d59fe4367d28da73150c96a51464efd6b4d8dcd789b4f68bd39a09539fefd747b8c05acfedd2fa1ed709465b81f2177691943989a2c8bfd3a0f7ef06e6d15838c0f38aed5877689e9c01e45758c2c90d1af667d68b566c9028c528be5082ddda47bf09ce6a01d32e0f019d419fdd6e69476059e848091332eb1c6cc567ab246d45787b87c7bc9a0aa301605c131debe5036fada901334e4e76a53bd33048553ba2b912a56882abd03f07872aeb50c05e674157a857b39d5e070609b02d2157f4734d1da1848a8cb0160709e945a32b8ecbd67f9cf4c6e15c3037a502b431c640e026a74895e6eb74170abeb1e4507039ce10cf1899b947d71d6a7e5054d952b9a00957896234f8f95006179efa4f87871d917aeeda684169ecd4859e0546f14b163f501e96310568000579c36d03f8a9621c0e5cbed7d5332c06c299771eea5812096a20f9fe8eb8f10245681cb4b04d6edbe5ef4b7aad25af1547bdb2d460bcc44ba3a3c8e708ef7108baaf0cdae6384d038d514f720cbbe6edf627abedede9db50a0df848b26b5d801386dc666f0462f37f572aeed1ded25fad9e1b2777150cfa0cbe63613052d420b130c1097bdabc2bce2f50f463cc628223eaf55c0c6e5a34fc75616b0e4b7e80469aa95ea5e225855f09397ca5dae7ef757656b89f6f413552ccfab902aeee5068b1bf312008054dd923f5e626e9650fe1b163d865ad2d1eb8ce676bc2ef50e056f7e034572e685ee141d94108f2e844573ce6db472f02d74c4314d90ab0d9b0b7f25cd68c0c8c0850fbf05778bed632227ecfd9cab7672830c63e38f04ece30e85cb7c23733340d438f72ffa23b5c49997a0e8c62f7b4f181e1243101f53360f6c8a1f188594c5e37923f45629ef300a251e429603727d2b02197b48cb5f7c0f525b9e16ce6e15ed4b47d002e33417c25ca4dc8a3c3d2131453b445a15c3490a925cdda870f27f915f58be43fa41c9e547d10cde5b3f472c554364ee7670c506b44d1d944959189ba0e6d7e196a568b239c6c875a775c77dad2c4940d32e480d37f5fe3659e766c517630ae067b39246de5632314160ee9c3aa8ba25da95fc0a5083db5f6635250715e46cf5a460af77704bc818e5dc8da90b65d37514d7f50ce04e2fccf2a2eb6d4434371a6cab4fd14d67052c415f4bab4320a6a06d5d3c05e1e62353686e5650d95d57f50f933ee1ebb9075125a73e02152fe060748eda0b229d323b9644c5ae0ce01b3a8014b038be1a5dff334376c83259a3e36c75e106f651d6c976cd733072b352f6f5002cb814e913e822532686779cbf862f14d505c6a04df5957c329327059f75fe5f374c583244e5f00e44a224e99e6f5527a10801f354db490fb6b204ba11d4e88c6e35c2987aa847941038bd3290b3cab50b0505172a621d7e43d88eb69d0e5bec71aba4930e481b9825598c447002367e4507341bd947b14015f1e93b0c80bd95ca435b58df62524a05c44e4ad2700eb3b60cb52bfbcb430d730f0f7fe1355783256720e9cbbcf86c6a7c3630284672710a0e true -check_ring_signature c9e936fbcb6f9d03ee6ef18ca239fbfc24529aeb5f726ed115332bcd23ddfbb1 6acc8619f8116ebf026e1a6c8e7bb23bdecda856e2067124d31cfe8cf1df7c94 6 4d1ee5c173327b207b33f1b228717e104c49764583c9a30aab86c66d66b8f198 502b45a7eadf53e4ef48419ea95f391287b18566ec992dce56a2eed9688941bd 8e49f27795f3cf4f34073fcabe9cd1fd9ab4eff6673199471c5e3dbc0bbf9be9 9bbbf2c97c5195807d178459e87e86400a8fe5529a765f19f5ced0702d28116a 5c1987b07fc49c2042fd2d03e226ba9223f4a2a6983ea3e995f566e36220a47a 8f4b99cf871f449ea14228d9c13c4f31bba06fb447f991ac93c33d10088eccc2 8cb885d425c965710d2601f533202272159c4de864d4702bfabd75d66d03a002ea50be67774e3d71bf646ef2855c99f9189b054aafdec7a27a2427ab7c89350ae61f8f70b1942967c11ffd34e37111a84f5b819187a8b6c5d76da207de1ad906d28f632008986f5937b68530805358c973b126bac97261edf1addbc583edfd04679f51ce1bac9cab19967b38aa5bb0f2ac7c95579cb8026cf136058b54595803cb24b6bfe12f6fb3f496488bb9a2ffb2035dabbacad725f6a3c66ced8ebe3c09858a5523d8ea9903fdeec1549c33bc4d3f24b28895c632ed657a1754f10b090b6e6fbcd893685f3315e2eeb56004efa9272de12de2465092a0073c33899f010d0304388f301cccf2819fd67f1592db9cb070879a819c38ed77747e068ad51608d7cecb4bf47820d70c8fc016516cffa49ef23751d09b6a30ef5850bc7ca82b05103c3b01d4f4548cdba917703b3e1220e19d99253379e461abf18f4bb6557f0c5d975da70ebf9cb51778a065f6507f485faf14f3e5829f8a5c4525aa454ab40d false -check_ring_signature efa8728b4c73495803a2f40d8b2a90b775294ccad6f2ffe994c885ef91da190c a03e7861558bf20876cc05c633b32a279a44265f946d4518b376f1c4be526b7d 4 570fd537410c3bb1256a8636e3170ce6f05d50f639932c8bb9c93b9219ea892b 9e789bc9c46e20e58f22a8609b85256e05bece4e477511b535386f36339f6d90 9b33304cfc3181f743501c802bbef98cd0892d71c3aace9dfa0fc8895a7ac29b ebab5c88b00b183cdbb2d6d2e49e2d4c3bcab555860ae7e781e554e9d97ba4db c79e70a3a08e500ce3c13565c68abf26f416d6b832f830be9a0ee031f73c04083d3b33bc7e6b0d0ca6a0947f9520ff2072a9f5c17ce9aa5d6150bf3717c4d10f0e7f79c87e34937d24b2ea3baa1637462cd08f4a9cafda894a65c37772986d0da6add5d4a1ec6c7cee1bf2385d8d5cb8b36bd556520b92f7d6dbbc08bc4efa08092137e04b7e1278d64b4cb48111f0aa88d927bd68528ee495d41e81f575c70918dd9888c0a34d9fee02346db72913306a0acfcc8b255c2421aa6cb9b0988709408d69bc7ff7dd4700a5b34412c32b42bda5e3ceb769c38634d49ac32ab79a099c1ec0ead5f9502506683cf023ea0f7030c7ea247fded00826735bdab5ada20f false -check_ring_signature 7ef2a188cd6823b1d06083edff156c95671b3c99e4f96385f2c05a1e4f370657 7875c64fa57cbcb3ad6df531758ddacc83be916dab4580f2b2a635d409302d16 7 952d98b8413109d7ed04611ccfe0e1ca1c5a75f2d7ed621455698539f23fcc9b b3478964129d268251e5f2030a24afbf36594e165fd7a50838037ba2a0f84266 a04f952225ff31d2a31cc290d9602c33eb832823445ffa49f00c5cfccbbfd24f 8ab562f710538aa90b0477ae14bf01c41644f4af42f8b076d660b9c74d4b60e7 1592ef92643c9407a6168874570d60fef92f082db40dbdc5d240b994805b0c3d e3e28e3f949d1b65f27e57aeefadee893cd8a59b455be7de06ab8e1b2a3732a0 9b8e9d4ae630ce738d3941487937d2988b028ee03915bc6563a48778038068a8 d8bd424b457c43e7e24bfb5b20e37f94c558b0557d9965dbeb4e9be6ed8ca00b8f6c6950e0bae4374c87fcda40588d597c05ed2955cec38c2e03bdeac2dc3100ce4674ba26e234d6fd6f6be833b89785178aff8485b07cc8cd826dc9e8f0a50cfab96bcd203bb749a091d33c2a9d590a3cb1a94e48dabf27cc269ea773e7860da4efea6c1ada07670dcd2da63038f894e89d531cc0fa324bc3ab6e470256bb0baadee0ba39c49d2fedf5948629b4f28d74acbe603d26ceaacb1900491fc4750ee88e5a3094660fe4ee6db0e5b928dc82688e121a3fcbc968bd1a3a3cdec45200241500d50c52382131ccf211d8a8a801b845d0e5eda50c3288e62523ecbf300152063c1a0578eaaead8a0243bd970a57fb41dcf2eb77d13354f3154826bcfc059cc96528fff4f386fc0975755c86e1f56a37ab1a65a6d3ee99ad0394f9980b0dfd6df6ebb1f66017d253190b69cf87f853f2e688b425287fd720538b1ba9300fa3ba6897b828e8a97ba231152ac646c7b59515ae67ca1cd9687fab07fb4c2906caf074b8e8705ef727cbaf15e63a21a0689ad8f4608d99bd0cafbc5616e34b0f00ae7d5e8337326e5ec44e9201c598120c7d9eab6df7bf51990a2fb62a204a04 true -check_ring_signature e6813b49a9d6d091116bd4f13805684e651c5e573057eebd40e67738fe8b4048 d4d717b28d98217475f67e6306cfa308207c4f8b71ac0c2f9c357358082f7a29 1 c0ee3b0d4956dd9da9a2aed06674d495e9146ccba672c0aec7bfb0acec4af310 46d82373cd414e936b09a9d873006bae984f6d79c6d1fe6502e16e33397658069baa5a1addf0bce08d3b6d4f803db65608fabfa7774ef94195fcc60cdadff009 true -check_ring_signature 451780677468c41b8a80ac6816f4938d320c474118ba2f45bbb8a30b47438715 51fdc6cd99e5e4693dca3fea1142938e2905a6c355e586f21549b8479226f363 14 b0350c313b55ff48747e630975836da50913a5778bb21db591e83d3181cea3b4 d582c06533c625e5c5ef46fa6c018701c9f1e6cfcf49bf77a40d8055821d0a69 8c1441ec0e1b9d932ae947d1428013a7016bc8400a15beabbe5909178c07a403 e9176716be7db4e0fe0f32f1502a13e1e8238e44dc58ec854e8457a665219213 1290707034eab3f998dff43d150f5f8e6482852555ba524690173ce6a566a145 58a569564b43e98500392c0d156a0a510ac54af060e4673e912018f36bc36aa0 b6cae06c8e1052a0f6341a1e1fcabd85a6d387151f8b5c886396c73a3b1f9694 082fd0a0f2e33ff0630f398c0519206410906b0c3a9dff5f10adc25d21bf0e81 a8ce052e31be16eb4d062737765b6773951a4fd98b4b999231a55a4ffc8eae05 957c35d8f2b1a6aab066757737551ad98164cda3ac6a1f8e07c2549a13decd33 ea3c7fefa84cb7ca698cadd1542f1a274d51cdb00d9cb5445c82245feea0ce5b f1274643cb0a9f92f8633443d6087bd23ea3fdedf0b0f5edd461cda013a6582a 835763cec46f5e17fae8af7b0bde98ef86a1acfbdf8ef27d25b5c4e5ef33a8d1 dfcc47b6a908bd0d4261545b1a9bbee32774f8dde8a79a93621582e1f5fb3109 dc27e3e72aee55048ba49c63a8724b194c98ac116ce8639352b36879f3b02b0a317a1ffa59d7aabab262364d066cefebaf41c550c80c527d1ffa2f53268aee03021520270d2fad0f9863d4e42be4998c6d035b4f879eeb5cb0c487e08c3c8204616e4ef127c42d1723d8ec09d5f727864b25d9aed7cc7f87ae68c0bf3427df00a8a5ea2a7044ed8dfafcb493f9204c4178cf57660eb4af678ed70ea27cfa1704bdd8fbc37089a4465eab2d0eb6d2b3e49e4984530cfffa72d2226426a86c560857c490ee47296c5a10add9474b634f190c32117dfc04cb02e4825df60b4a55080c765ab63d86b777143c16015212e7f92ae1e6526f5ef4b1908e3414f614310752f687680b799043e99ddeb4a164ef69d424b6c60ab7c777922a144f3218270e1e3717b3cf8740f03e9c64f01ace67da70b35e47e8212d534675019d1e2859083df76a9f4f1bfdce48e6ca5d3ab17c08382c2388a8528b934c34213f6749a907772e714c85252b99b99c22e7c3d26b674ce996a8b6e9a63e2b261b7f0f03ae005ba9f1df329dfee7b00fcb7eccc90d418445af8f8caad4eca32b19b0ca7ec7062426017bfed670b888a626bf4e6dba5df8dae272d5a8d5c03e0942a19d1b0407780c9b20ba7125e53afe4c8d237fd397720b6c85e109bc8c17c22fec2741980cd375c96dfc3aadb7f7864d1a1887d29d8c5cdceddeb84c5d9ee16b5cb7dae6051964fdcc702c12949b54add8eec67f93a195b31eda2ad406930504be5bac6e0d827dfbf2c99b14646ceb0c5b2804fe8263984d726c64a56e01b62a1eaad89b0f28db0f14c01e2c63ae48d287235414ae9413264c1cfce9d650b91c1e9ff21607dfb014f2d756b16348a2d114e623072fcf3c0ad1b9a2eef391d54d20de6342017542c97cf868ebcf68cbb74916f5dffc9200e587be1f951c3006ad79b70ed80897090fbde36554e5e9b948b18bee971141d1d32f963057f3db663d0497a78e00f28c0ba94fad149d71e463655acba33519730d24f9602d168e23b201fd74db0cd0ead9fdfceab47458c77053888f9be064d641396e9f0ce5397c9109673e6b03a9f0e913d6ca62b861cf19266c7fbb112bd1a2a0d278fdf2ad75d06c40b0d94c33d837cb5955c93f3e7c433490fece75af51b324ea54c3925ab2d18aed9fd00f68bfc32b32ecc775b08aa2f2719b2e495cfdc74c10db2e6876bc240a6d927d0fa01737a04a136212c3aa4f5fd077a3b1d4bf87c8744a8338df792e4e4e772e02 false -check_ring_signature 58b0ae0a424c8295154f09c35fdb205df4dedfa6bc2fa62e95932218091e0ef5 c44087b9415ef5e814b8a1ea0016735649c6490f68fc7c97cf372116208c7d06 6 743fc97a477485918a496aef17a91547a069ccce584b96143152c178b812d319 3fb3cc31bdebd54fd8726044b77dfbc3bb80e9d0894b6f8c76b7f07f7b0a4522 6821b583b38c9ad4f8a4e462a17db36f5036c581c15e496f70b4475d7fbac3b0 e5c06e3d523c2f14719f8a2bf8a8c31aa05dd032c4ee10233c17b3271b4aa856 64c9d73cfb634f00e9f680328faf6f88f8162187bc45119589089dcda6a657e2 0a72326308e742a235afa3d3cf5eea6d11975b7c49af90f3ca6c97c0c59696df 8b0d586caed0f52a712c84edcb5f9574a264c0898e5ccd5e511ae629a5876b07c638166b8e2e437cf3cc7315da7b95b9561391a3c70391089904be9110ca970e0603998cd87c70fa0b1b407ddce2e4fa85d89d5e1a651f042f59d00e342dbd0da7513af2a7f194edfdac2adf246633ebf76d1fe0a7f4cf731de5637b2e0c16060ecdb60e516b94c14209119774df5c8b7a430e5557fc32f895d70a3e288e7f06250dbacf140d6e916df674d3664c7acbcd8d701da88525a80587f964aab57c3230cb73aa3deca98378c473fd069910c7e573603bf5263cb34e12b5d93d952c0b4f159ae2a004c7eb04dd13e23240316a31e5bfa62d2e4780e05e6e2361e4220a6986c975e44d75e80cb78f18877d7442108c322360359a2371e96e51d247f30d7a5892b8c813aa4f667d9e15b6bde3acd924017a6c83f62f7c64b0de0ee23709f3970c408122423ddc3ad25d1fbd0fa61c8094abbb20dea6d22336b87810fa0865c6ffee4390f2632323f20093dcb8049b9cd71c928204519164b49509fdb302 false -check_ring_signature f6f45a216dd65427bb171207776c8fd1e2073896bc7b4e753e9f3b46e46281f2 10027958980d3c12e52179ff6b42d06971768640a977a000ab747fc9e1222aa0 6 f9693aefa1ff7f631bc00dfbad50acd2bcc72810af6cdcde39f65fa531483f02 b3a2d78546628ef799823f3ebca4db8667f80508a2583c78527e40dbedc5ad28 cd3b704b997110b32b88b82fc411fee76a756a4c198b021842354bc1baa3301b 49ec9cb0c0d3b2b81c31cc89a017e561e73027a1de650a144ac1833e8f8cf32c 0a30ba714c30b85081d9916f23b0e306bed6fb1f4e7d3e23813da310a7d47578 8bc12be09b5e38376ed51fb34470ba58421cebe312fae36229ff3c0a664bf124 2644a3d56dafd4e4bb5f9ff8849ade6c563e1c2884038416c43c751c84017203c3885fc58a6c6e50963252261912586e4e6f0676df9e7c53ccdd723b0597610af4a97efab9097ff92023eb15473c0b1b3a74479973c05d6db79e927ad93e9700ab4f45461707138339d5075348cb6f7711a920c165339f0f1c0d1fe64859a10a4db32e3156189651095fbcf48c18dea419b4e710d5f6ce93b91d7d1fdbd0b50556bd2bd860b6835e2c83646031e36ec4c639d6b05f2f9d6ff2acda83d502b20ab3263032f54819af0dd1ca5d78744cabe8fcca947dddc86c551eb1fdbc72670d860e080468688a501e611ebe0cb5df725ce64610c7e62b19e1a916777c90040fc535f6e50a0823900466a02402eeb8ee4252daceb52b0a89656daaa74bc1d70bcb800af38740eaa6a7f5243e1f7598687c1b9c913366d779071e0836ead1220600247034c91013af3d5c8985323fe86f47a2802ad90f96d59c903b4958184b0f6c4fabcba225c27a78bcb19816c12569c7749c9bdd960d19e50c1900fdd42702 false -check_ring_signature 3846a6712e12b6f30679c60d2817ec5df21594e6554291ea2ff089b624480c3f 9a066b15c88157165710b97501f5fb5b3a82787d71492801041d26211d643489 2 f22660cc480d6225be531c7e5d80fe35447d4fec02db657fa527073bc99651e8 55d7cefaee5b8b478aba01d718a8888eccbe572e03fd41703644b782fcd7a40f 8dc56fa3176a5d9afa0d322b93a81af1ac92859cb09dfa29f566edb588c2a80682b442e67a42b43d8e3c0589f73309c998973c77c641c2380dafaf0d321e1405fe26b9dea33a740cb996bc759d6342c2c303687a6b9377c20c13602625eff40189fd081307dc6009ddaf235d44a56be1ba7503dbe54f78eedcecc4f6ca71f90d true -check_ring_signature 88538e08ae78deca19d24cb3192eba32d20fa5d5f013550eb7a532178514bed8 92ffd5f03642ebf7dc4fbf0f22c862a2b46a7e038539d1a78f3ad16f83d79a43 2 264eb1a6aaa73498e1c01b9bd79fa030fe57a99e9c9dc2c4c59515d85e7ff139 d3930ec1b42e38386a07a2d7ed83a9c94e740bc44a4a5f65199aff96a3cd76c6 f2605f4500af31c6b675f23341566495638bf2802bb5130439766628f0ce2d0a30b5d858eea77e416aac7e95e97fef65fcd7c0301eafabe9c74a2a78d6fe710101cf96318afcb77b57ab23269a481b2e26bf6746468818588071240514141b0ca5759ab91d3ce834adec55749f789c69c32af0fdaa27ff5d153ed0c8631e8306 true -check_ring_signature a4927eae0ddb081c39090f24640092026a7cb1fea81dc6abdce9d4e21c763fc3 10df5df67a0eba796ae639e003409d5a28ab0c19b2053b15b8d798975ec1f642 48 eb4a7a98c335c3e432d5a0d01eaea5001d3d2eac2d82385e37aa55f409873eb9 1fffc459349e7fb4db75b239acc03d24a01e97b9ccf7c23bfca4ceac968de7c2 f1b2af37196f7d2ba7769e1733b4c21e0d625be2299280a5297f9d0c989b8117 8ef7320d05647690dc527ba0a35d1a91c858e7ec348039f2fd80dd41d2554a6b 018ddab2cd67ea2302375c0a302418e868909ef2a73c6b87d781089821a6dfe1 87a375b383f119e32a5da9992495688f6207f3f3f037b0d7864920367a362638 2814c2d95e4b1fb4f4391c5c05f6fd0cf9523717fa3b095fc8eec85dcf8336d5 64efe3c3a58b46cccb82b47d1a7d63bd40937455aae137f7d59fb14a256c1271 7ee690fc0a3450870c1b25882df7e8ede1baf3528778febc7b7daea82e503720 914fcddfb8fdf9bc8a53c3b347910a6acf6694840931b0c669ba2220caf021c8 f6ade69c454b1092f6ab7ff8e4f1c2d25bc772085bbddcb4cc6f86aedd7b4651 9df59b35fa114e1ca51bfe98cc62a17529a3171c513961e3988dc000e347f381 04441ebcd90f58e87edd0c89f14a2e085a10f7bc113ac59679bbc05a59ca320f ea1a8bc785b9aeb5f370a2e160509e24c8654388a410d9a1831a7b009f233538 991a938497f69b1e361dc46e612659b6310ed5d5e8bf90376e6f8453d4e72ae2 44ef74774ba1a3c0308500b70e61297548f4e2f99d94e5d899a96140142317bc 7d2cc7e40f21c4a0d1d6b0e8a4fa6d80f5f9c8e71fc0e99af8b592417f278f4b 4d3f3b2cb0e44b7bfe6e627f0ee5948c16b671fdcd3de0ac897cd11d4cdce639 377b4fbf72d5f1ea61d0774ac57d20f6decb766617406e693ffbea5bb19ea6ce 56d8d6b40fc8ee5bbaa658bd44a93e8fd4298b601b4dd0a1ab6bf9dba807a749 0398a473c7c2cb3bddcba74057df285f6246145bbad91be10cd414737815d52a 47f86acaee2bfe97ca9062dc184fe45e6e96932c6277cfc09d4eeb6a45bebc7a 0469a6c67251ed80d28658e8b1a645881d834d6035cf65441d20cda656d1f4ef 436e1b0dee35e9a54fdd173f9ef7351b2ea1b5f0d8ccfe88bb654f79707641c3 7b781afdfaf203fe0c3f0ff3ba746280bbe1c50344cf2b4532d76ebd027e5342 38a365281983bad350b867428b06c4277108259da2052fbc108a2d18f7aea789 e4e9018093f5c28748826a65b753b22dd623805559fdb23de2848bc17ef14d28 ae5ac1c7b15c6d0b012a8f768068280dbf59f7b908be3530d6c8f65a52435148 b4e784721e2f0d5176e1fd3cc2a0cda6c75f24d4ee766dff6631348eb75a41d5 e86006d8d1be59cd2c82e2db50612d418346442cd4c72395937d600e7b2f33fd cabd100ccdbb740e790a44f2d84f881268d2404536975e9c6846c38c9b4c12b2 116a54cdcad4f3f803affd816bd6592281df1febb5bde46eddf04bc6c0c50c7e 2a698b9a8c3f5f6bc471f614133a3b620dc9c3ac7c71772ea41bb4a9963c2452 a9d0d98275ca3475aa5a138fb6d45b124d799d82eb84d756fba35d950447deaa 76cd0dc7c7d9dc27d9d7b70b42b226bcc9dd29e95b8ef1186ef0faaf20ca4825 4cbc61cafdf07e4d6f70a1ed3bf07c1384505b16542dc97c2a2d67cfe44d697c f3114e8f0559d3bc6c8088e44d8f3210e907588f2cedee794df80f9a009478fc 649133b77f0730a47f78d12904a3f3117fddaba1ceca85f17c7c593086d77026 f2bbbb977b0655fbd504e6a7f6b0136b27f58b3b2e7c3fc189b2ac127f48bfb0 c3fed52f944645001061558d6c45b3052f6f44f21af8408a404a69d2bee8221c 1486a840dc79a4faf620cddb44e9921c218f667df2377b975aa420a2cfe80b1a 4e2b70cb5a2279c8118a33383d6231796e9a11c49f75cf0d773d375ff50fe2ee f6d7c8308d3d6043deea1c59259be8b81acc105b22f30881b6f1b3f5eb0723a0 7981cfcbb82c11cc1d29045e85a3bdada074c08ededd9818cc7bcb8f79045b6e adca5425dd382ba22c81800e8573fe05fbd8deadb615c88146a0f70b4fb89b79 f81dbea88f8c509519d23b30cffe8f25c239c402e2d3873e87687f13543011d1 097acdff224a9f64a84963dffe000f47c4ca28eab8c3b3396d282c29f51a9164 500f6437474da6115880d1f7c2b8ce76cd95a1afd69bbce7a3dde6156f8bc368 71f1c8c1b4e9ac76ebbcd109c0d8a603bff08dbe550ac09aa33755c580a1c7062a6c24738762b597813f6dcfce64935fb8371700e7b6b71f1682a94d6c1cef0957551139eba6e6c34df90deb131daf78a57058f29d5e5df647a3d6268574110ff2c8eb357dbbac03033692c104cd5d859eec20256e5d9a5fb4f1fca2839b32071a4e340521d2de0b511105085ddb42671c012ce2bb26da3c76faa3a4b08b150b45c1764037cf198c6d43b9c10575064a964d2bcc21673fff55eb4490dd6640064a26dd53595228f1039b95a03ab363d48bfa1f06b0c5f8df869ea57f5be2ae05202057fc6f4ab3a539cc82cbba22f59e9a74424197d807156ff3aeefadd58002c894285aaac6fd540bf5dacf2a09ef3c66be20419fbb066bae5db7a22650720d29a4d4da5728e53636f64d73e39b0521f96358d07860d8a59c7546162fd10b052e50defe41bc223f5913480cf427d6a2a142db5d6038c055e1010ac073ed850a5d024f8462efac0c0bfe7395dfb84a664033176d944faee6ffe244d4d7af61054b149f9aa14a5f3b5db0c93bf43622b8ef3a1547f250797520807a29c8c64e02322b9813164eed6458d9c586de897147fa7555f02dc3369cc0114f40d19a080306b6c080edfbe75eee07c9d3f1ce10c80ca8d9a0f4e064ca40b2c4fa07e4ec0dd219cb6513ab6f98722a0b132eb068649612a8a5213aae4dd132b320e8141706fbb840c5a9c26901bf841911b477d1bfcd64c58cdfd3d07cd6264cc7c78c420d188559060cacc1af1457f07c80ed1d218b8c6c6e49fe53861de29492748bb90c8205625e1e47f6982df3272338198e2104f61e016a5bfceb1492f015e59c3d014e246255db497a00325cb1360ff6ab095ebc46262eddda5ea253027a9e76cd09d489db265b5d4fc19db625fcdf9f580ee67ecb0aa2bb333d9fe27f15c18f6905b408a42b3f9756782e12efa5673968809e7169cf9ad618e51e9f58cc0fdc8309233c7f97567ece5a1669d6d0470a76dc8130510a69d96625b5af22942712360e7a22956a5203a9dec349fc9caa31572e34c0c63dd133fe67777fbf077dcc790de62769773421be67fd42ea2c7328a4cacdffcf53225c15fe7c41aa62ab18ab0aec804d3bcb03965c7998f3e3fbbc73e06a803f8b05ca1d0d19a1059abe83a104ecc3c809c032a8b651a2abc4856cb89823af75dff1f09b4cfa2be7cd55982306769a24da9e33366d6b86dd2c39e04197ff2aef0a8c8a6a9e71abba485520710b61adceac8aca033f57fe8fb6ff2a8360059f9400c8d8db69d4a2b1215137ce054917b3bc74e0bb7fb30665170007fbb833fa2908507973d1d717a291282ce107fee4f01b62316265e0fb41b656564aa43109698665301e76ed63e277eaf4df01a2bb3099a96923be23f88e54cb7a1be9115239ecb0bbc68497dd45188819880b40ba0be3e0b0db3bae7bc095d1d4912d33c8f7ad40baebe3a3f5143cf284d10435557a557b318bd75a4f42e205485b5c36b66aca2abf236e21f3373852495a0770e08b5b0a1921aad0d181e438f62b5293bac497add04fbb702a9e5cc199390e77fa4a9772ef952e8ac14c47d5a321e014c7b5fbba8b7f2a2ca28947aa8a920068d12ffd85bef97b1bc4b46e162e9bf3f7b21158c47a30d1a88eeb6d50067906be3b45ae911be228a1d0fad4ac7dae58e4b67da6a44cc478a3b217fcf862d30829c730ad70f1fc7f4736c32d4a1bcc7974dec464a7ca7e6ba2c3a985858e710212e7f845617db943e834212dd4d4442a648fb9f18bcb0eb88b9298b678eea00f0e067d1bb13ee7e23ba30914862ee97525c4ade9ec4417d74c7000e6278b9907bda449cc42a597506cb90babf9bdded19f3217005ab81510acd6b970e2dfb5065ea8b9b4f5948fb848d9a822d0e02ee1ef263720c28f93186de3367138f7c10674d2316a7e30faf96a0baf3ec3832a8e65c274ec564fc938199067f70a1f53077762dd8ff84316b49a1396278e64d417400f543bd5af37b2e106ae37c4471b0490c11461719d6e6e26a818fd1437f565539b8ca1a88b3a7c12f9b940ab27ce03c5417abe63b3bc45550111adf70b39c2c90887e19a89a30947eb9f7d3ad1c0032812a8cab8e4e6df7cd9151bf5f5cae67d5fe0473b8c1be7b15641b1fa7acb0beee1d5af577b10c16de5e9abb57a4f5d5808ef0a1b5b8383b3251bba1cffc305750d68f448dd48c0c1fad6ef8fce24d8d2d8255312a8ccbf4cdaac9d85ec340e63098fd3b1237d1da613c638ef8ca3cccb7de500f765ed4853c015b2dda0d3033df55d87fcd8ed9a5621c8fafd8fa013bd9895680c219254217fa2971041ca0b5cbc0874971f08a814f517c5ccf59e669df320509a43ea5defef1c75f39d00011d7cbd61f6a37db884de4def3eb04d2392dd9a669270ef57a1752515f6e625056eb39ee95cae94c1ab74ae48fb59e7dd15925c23531edcac125f7da3cf623f0baf5ead031843d797d61d31c0e7b4c03b06f908be6baef4c70a9cebadd3f59d05fd3e19d0f4e14079b790096f8f4f8cff2e416e41a947c89eeaadc66869226b005b3be118dc0518f1892d0df6f4023179fbe65363dcfdcd6a014e2aa94b603b0600fb270bd00ed1c4a9ccc595eb86f1b8d81022cd114f8c9226cd2bf43a4ea907b502f6ea4157b33a8ac50ec7fec7c01f1ef64393e2e6672709d2c9624357560bb3bd7c56a53247af10cd25da4e5a3067d52ff11e5a4d81672bfe6d357444db09d5b897fa0ba6ba0e15949092363eca2572a6d4b265ea195e601f25f4afff6c0a3070606ca5152e0f5177f81c82962c55c74fa7c5d24b2153f602863b514d0c0e691dda359fa13d58ae68e5b8bc31a3d10163247daa32ca1af4d5149aa7e47501785996b2c3805047e2df31911ee24149de96c6efc72b72663a8d7a41d9311f0d7229585f367723391b6ee3227f2cba45a14a243b3c16107765224ff07369fd011b299d64101ff31e4f91bee0feb1380abb10a9c8910cd8b6a79b08a8e682670db0c2c46c657bbb1e9d3f103ae294be3fb782f22d71b6bbb3715625ac990dd30aabef798ba4b70bbca01ed1c25d67fd9b897053839893f3fb8165bc1ff98ff8077bc4799dbd846aad70bd2a1ba688f037e293afc9716525f8d07c1ae9c1d16903a760202ba5bbef5058ef73660f24960f368fdf6d9adc73da8bad7b5faf4e8c0bf18f68192a4e2a533895d47beeec951b80e8ecfe23b3258fc16b602c0f2d5d0324b155c48c279100b8bc7020c2027d3b6ec115e07fd687b35ad7fb61f0b6f30d6075731ff2d19d9c33829c9c64016c1dba1a6c1e2a407bf6685bd15f0002e80d4401a1d51d9a98bf3b378bec2afd1f0dc5e5885c90cb1dedbfc9a8c12b6a42061b4254c72befded69182257728036411aa30ed7554462816d1839303ba99880e16ff284605989434437f078774af7d00b39eb368b325bf5c9f5ace2705a7be0bbe0d3186f72bb79e330f4065658ec8726bd60e29e4360809154e99021b87a408f158b8bdac012dee8ade55fce3421a6b01f7184c19162517111f4a48a595ef0e8a9bdb0ce13bfba33459ae0169a94ad344a35d1afc7ece6cab974b880a56fb047900fd6dd6009162f2dc35404865fa4cbcda7c25d0cf71a8cb06cafcff50d50d9655c0aa80239b320c169c5502483638e3c9755ccc4797fc7ebfb8877daab0031d77037dce87e07d6abd64bb0c52e7278d0870c68b3a8c67a1fb8c36f959f40d2df5a43f9da6830f5416c721b9090b5ef55ccb2314766e6f196b8d37453f590fb2d503a0122c50a26abdb6804d904f2da2e9da28b3a529d3fd436b0b0c7d0408fb679e3ebb80804bc807aa97a60d8308a97daa459a0d15dad88243155cc4b00af5354dc66890d76abd5466cd85852eb589aaf5803e4be28f4b7d53e238f41008019c7926368c448c3c4d7d3c655594bfa6b9ae503c1da1e714bb81837d8b0a02729d8e524aa5dd283ea07d9052ed19225fb8a0b7c4bfb91f6ac40c140acb9f0ffadab386b1a6914231a4d430881611ab6f577d51c548c9caaf7a15a8c822e102915d71c999e0d51bb7d0c16c30b10c992fcc0a6d9e0d4599450ab72e303830034c939e8553c6a7d453df94fbb12a8b072460cef1e9a07d70672f9773ec52f50b33aeb948b9f72b00db43a8d7ef6a729f83cc6fcb1e1dba5dcaeb12ceb4bda203de6e631f6db184b2b06b63988aa9ea1ece4b427e5bede8f69d2af5d4904f7a065423439c4134d0d5564003036ef66b5508b24f5587cfbe3215706de1a7c43d01afb704a6546c1205f37850b7e63567f7137d93bc19002c2f7b29cc76188d180e false -check_ring_signature 2c1ced99f4d3a52c4d9c8eb4e26c8ee0bde43e5fc50e1fce0422d43c71abe137 2c412980ffd1b6ee90f07bf8d165ba73e0765055cf1fb4688f08068b8ec7983f 7 5eb6fd7f282834ff580dfdbb7f225f9681a4956ed56241c5af7e46b548a7e130 07f23b44e6c5b5e7cc6c7fc6e2ff656700bf2f5e4664afde71a416eb4081bf96 e649b1a48a246e1e33fdecc1cb0529ccf740c0dd1f90c7f92a1380368f3863c1 1606f4e312671b555a5f69dcf2876d29de7fdd175837100264d2aa7f657bb35c 9bcbababf11fbbafd29c8e6af367e3c8b2f9d21629ed90eac92fb5698e391024 9d2b248c6f6ac1c457c54ebc0df0bf849ad504f29a09c5675644d25e9f816b3d 21207d1cee1cd01e244b20105affad6213050be27fcf81e31ba94a125ec79562 56401256893bee35394f9110bcc7ff062f50942abcdf17453f89dac39ab1f507ba7bc89a3d6f9ee80fb00fab4fe1c14caab19f443e9ed77dfc7d907288fe96013092cfd4e578e7276bf16c0a80204b0c5fb9e14d76ad79ad080a9fb3b805c201f649f03a6ef6134ea47ae6401f10dae94392271c3e79e518d15fbb806312c207678933f350e01433b8fb188a2e28343e4d403a095b1df23eb70b12a782e4dd04093cdd138a4a2c44447cd5c6a89cf2ac853aa8118b1b1e4d98fb32c932bd8c058bc7829bda2eed6f97cc9b70adc44e4882591aab00772bcc1e8552c0db1e6a0455eb112dd91921bdcd303b71eafe57603820195748b207a56bf59524bfacaf0affc7f7bdc17cc7f4106df0220daac57622575dd4a3390430e2f061514379520bcbf4db7abb9f59bdd17277d27730f351f8ef1754e4f136c04ecf6ae676228c0dc1da81c983b71178a2886835ce4d220eab9451c7cca5ff68b33173b5460e6504e1a5bc23207b96c932d3ad6bb2e25e1c0093bead0aaf3dadc173c9fc47aa66068f35453ed15c0b32abf230f6fbb6dfe4a560640a414062f2aaf9ff18b18c1608df6bcbaa38cadd8b95892a32f3c514558f8f0587793eb8856f7a5eb6cafb990e true -check_ring_signature f6e7f2e662437d94b6387ba9c37c398ccb1bf773e2bba7625e556b0a56adc5f6 1a999002c2e1f8526e0fdabcc127d743bb82648d4a280708965d1662002e6660 4 37182e118d20975f7cc626b0fdc2a70afc6515c72d8ca4ace862ca690595745d d786e466fd36f6e871bb9342aa958ddb6f1e71e7201e54ee0633c4b47929e6c7 4d0865948ad033778279f09fd756874733633c566735707b8cd04bfd7c8bd23f 27e144c111d0522e1ace2a0a40a461edc10dbaa7bb09b043563167c9879470f3 13bcac2063cee06638de41e59ed072d9b8a9d0043c0b1dcb1b4b40ce1d150701f8e9f300adad192f3330fadeeb8fe90fba3d5bff760667e91cf16ffd4860ea024d313bb4bc244b4ae40fb2eff54cf6eb0ce074b6781de7ee1e7132676f7c9b0e34db34dec9e32edab1af3511513eae70c683a444b7448d3be2f121c689e5e307e45cedf295a7b2ce45a8c57929f71e5dc9254220aca1be30489cb16c69905859d1175a1921eb99db73ed265ec1ac45246e37aa8617cbe1be8567ffaba6fbc5072acb3a6ce6858ca2aec04e48151d9981aef9219635180c4ded8b5a3beeb8cd0a74e8ad2cf912716d57c6c14dd6bdbc88f38b90ac2dc2c438c81c2d8d3688a03e false -check_ring_signature 57a5251950ea5367c700b4299f0dd21e067bee4196e0882927abc670d18407b8 617508d9555035adddd161e15ed8ffb8f5d8010b8190cab6b00ec09a87836262 4 e225954de663bb14a866535faf2b421ba4137cdad41048ac792b432705ab7784 efc4eead3458499a7957f902c6caeab44901d080c10a0c9434ed3398b0180e8e 95d35af2b06ab32e6a5c19a300e1d9601f64f4da4fe0b5ce408d3864839e4692 c8c5865ea1a5b0537313c20d533d7dcb27ac5c164eed0e9fb8f16dc66a68584a 273acb2ce1c35178c39b8bb2dd1d38f1f7500e036f0859b36d62bd1582bb370701ab4c25b684c815e98271c18fb5fb266562d964584e44ee44b9a2d9faa985062bc80c14dad2e39874c786e48718283b8e9d3e5f5cb2463d30568fc2173cbe0695c68a7e3f6d74bbb94362ccf7da6fa8de23936f00c33b4861cf09f2399e9a08214739524cda73529f788d88fef348d7172e407c28280b3d175f84499668b900c8c1f674ba5d12e82ebad110aec8ae99e71ce6d8ce94fda57786508c253c8108f78f9f85e402d56f7d0e9d12618974a13734d2c6a6be78a84e79be41fbde420b472881fab1ba25f045e7e521977cf57a7aadfe97214920a74712209b36edac0a false -check_ring_signature a8fdf65f29745fb17821bb0a807c325482800e67afd36ceea9dd189ef417c8cc bd39c65674cc643731e9d4102b81eb9a595ed7eef264a3b6765e07f241de98f6 4 9cc22ff3171d74226626344351184bca102a5618f3eb3ebc774d08f92982a96c 2087238dbc426275ceebde7eba70500b5b5b4baa9d5cf3d977544afaf5d1d867 93b4bdbeb9beefcc75b8228880cb90a8b2b1bcafce200dd9932481584a8e9c12 b7bf3850920d10938f89e11a1144f67e65c6f71d28933914c74b81078aa87c8e 84ce13afdd5ded8a6c082293b8e10507c7f97913f865e2f49aa025c26740e30e3fd976821e1bd28c0f08043e75873cb534e88286031ff29faf283bc5b1c7da0a57be993651198f7f7ba1ba8fff98f420adb2374688496a765d111b1b79f4a00b43296e9aa9fbf2fd843cfd318f7b32760e3dc2cb254ae91d8b0c9d209ffe140e9f1046bd8092aa7b15c6e6ebb540a0d42fbdb9b6ea2140ea2c4ca8c6ec24b7074eba64c79d3b126fae80c6d1397d278df311ff60f6d3cd9b92b5ec5ac741510de32d3a9c1e5c120a5b59a633bf82eaa490cb193c642d1d92275a89b2c2c4be0a367f2481f5fa65444968ad8e1281b13e6168013cc9f802f99864743457d17a07 true -check_ring_signature 405be8e7758656e57332a9d05c48a4945782bc31fb303f4081b4795d5770c014 29d8e678e4f8c079e03dc5537f46e10558b8955b06e0bdc7d2ce420f21f3a6c2 5 01f50cab1ffc07ff00c9a0d9788b8b25de402fe54da9f29cdce646f334b83764 f4eae7a5f0c8e984b5cbb7dddcfcf6b8d6e75eb88e4246fe724c7b75b1982e95 7e857417ccc4957640296706d5b6e8e11c8e3d75d94605cc520cdefe13b37ee0 f68921c5815b29d9b0b813473a57e1b46c075420df01e2c4470a4bd1c9f13dab 34116bd13f0f6bacb736daf840ae6fb6cd9253f3de39e5cbd0c7fbd5b088f4e9 4dc3bceb886fa82bf5bdaf9e83cfabd2e8bae6606e06611d4fb6cb50f7b37a02c8965015f09dbd98f389c548540c80cee63d437f081fda77160ec45895c2b90a424aecd13c47a8a7fb955094af0ad673cb1a83b1adc9e24830659b8c00355e081671f36881f07a0a854f2bb88c0c33c3a56c529997927d566b52c4f96741ca043404951560c9bf3152be88d2e706662e2f02906b5c2da0e377147504cac28907cbe023d0a9b4058eb8a67c1ce862622cdae5b244ece48cc05cf6c6edd2dbe4033d0e294264502ba071fd52632f676517cf9b97a552e5ec8ac821ba4fe757f606fc2d9091f151bff2ea2691f7205f060da396746aa203015842f0a56cb04c8107d9fea155bfc1a77cf15df903de08d59f7f8f329d974f42da24d5b94719f0ee0a50f12038c1efda4f131327ab673a638a2263ba9f6f9510d7e731541fcf578608 true -check_ring_signature 38b95d1ecd7a2d2a906c9de66a62b1a55314c0540b4f76b3c01b7150843c81fc ce2afadf269244e2f226c65b7790144bc2c91379620a16b6bf4da32e7f659f4e 1 193d65fcb9acd2b65a7389f01c3b2e4c2a01130466e8c641545c7d9f14402cf1 112ec31a39bfbcc60cc20a3a3f9440c7a9bf122912a681bdd5cd27e9c42deb08d273d1e845fd1f21ad3f5158da3ecd5a6fc9a88c9ea4b6f205725b2f55595f08 false -check_ring_signature c564248394961f6a34fe9c881bd7f39bc9b8873b3331ca64f3a9190beece11e9 a1f01a176adea62fefe43ad68a4056b5c607a7e73dd81a8313470e3e75ed4ff0 1 b03f0b3774dc8faabb38f1ff7cb55ef5f2e1d44736f85f6ea758019d1be61d32 5853448ae84c32a756021a874eadbd82d4f41abd679b79bfa5ee0887e94f7c97cfdbd051e4334c70c07625aef8f02f43264eabf8ab1b0d26f8b9db5c36d8a053 false -check_ring_signature fecbb3254642b839ced69a1e607b5263856ebf57ae75d37684cda31f332230b5 e5ad5e9e9f5292ecc2832532c7250127a46f3e00ae7b2a975606e747fce21008 9 7263bb833f58fb8a22e5e52898853e320cb7a726798aab8a8533882afa23f074 8bdab2fc774da7551fdc639f9d2befbebb53925a81ed01caeadd8b1150302c17 fd8a4f609ece869843ad7f925b8b68af997260f002cce266ecf934296975d8f4 881b789f5ef4591d059f4963e5627f72de8e4537d0e184fda11b3ef8f1fe85da b98bb1118a38c32337087f51cd762cf981286263a10bcca16d9a72d390e32484 bec9a41936c61ce8b7b1279caddd64a0f3443094466e7f5054a24f184e5ae79e 76e502f677787fc73bc50797a01b075f91e28a4685468f16f10289ab63524fb2 84a6a943c31b766628c535760caf2050384a51b158e2eb5b364d977522b42354 bcf7ce9f6080cbbbef780e97d75725df3166b79a5e5ecbf059511fe29c5cc4c9 d5a81876b8e464b81a74345c0cf238c355baea9a7db4f7bc974ca987c3051c0e598501a58a1fce8b7a0b7e9ed82551691dafe614577e211948846b0468834b08213516a0c09f5c7482521624379fb0c3de0e12418e76bb844de50cc964896e02c31ff45df94eb8f0d7e5f3d51e2c6cb6c5cdfd48f7a5098e59b5204af8a9550cfd111677c3b1abb446b4c1000c400d75eecd6ff3b0ab7df6ed225f4f7c18290121b45c19c223ce5e9c7115595ab637f859cd327c7fe5965596102b99b349e8089c3e93630df948236b6f8cdcb9f3a58ab213ea8e5d28497fb6aff8887e018f07419f74847e06b7cd5ff8ee89f22eefc65b5826a912c843a392c4fd5fe587d60224acb898b6422eaaa97c32522d7927280dd370ef6a50aa318d66499d28d32d0d12006e973de2923ec142bd02e279b4dea95747874225bf3cf0e7a44a09c7a8008427bb2c45b4458ff7189875b4f3bb611710dc3dbca55651cac9aceb31dd320136c64b2e6c0e4248519552280093ded40e722c706a93be1870b97098d23b020d5c9cce04296af1b681fc3af189dc0884b6eb1f2682b0146acf364905437cc10188f8ad4241d4ad0cfad056a41ce0ade3124332ceb6497b116bc51f0cf545670a94d56aa4089b488a54e9105d77be1de9a28d482a7d938b1802fa9be271660c0fdb8eaee438ba1744cd0c83e6552148bea2606729048ddf6a602fd5d5eb3adf0f7ce0af8d671fa0a7d783e469aebef533a36dee21925b64bbeeece3745a91c90b3c21e8bbcfd4f2d4dec9fc63459cae432adc1c493c685daa42ec55bbbf6b9f0a true -check_ring_signature 2cc767153d536bb921f22a319064025e5080a850745d52bd0bef7e0235cac2aa 3e8dabf5fd0fc55aa7f5fe715381b894a0b0eb0909fab23e93445c305f8ac2e2 1 56415114133747af07210c32f89cbff4b73c68f6c45f4228e2789d4ec32ac802 82de5fc2f2b34e84dc444ed844ab335435825e115b6960932cd2aed743abd30f4dd8e29f66a2557eafa90e4bbd59c66ae1aa5b05e84b2b37fa246082848cf406 true -check_ring_signature e3497ce4b127e98596f618cc8cc346e19de1c02e0c0f606fbbca7a4e115b549a 1d09294c65c11416c667096701cdcaf2f8d12b2ed710c5ea73cb9979d6be072b 1 c5088bf473c60e63d2cfa5e09edbc0d7e21b7a91a1243d61ae5e044cc0792fb3 1d7dbbff3443a172135bb67a8925b8146f899320530af5b036e7f2553bc03886a7044afd92ac871510e3a7a888490ec84c9d818ac69409b3a22971b3572ca70a false -check_ring_signature e38577641da190702de58d7771c63f5cb2b1f2c6940137b0e6ce8279c4a97fde 44dea500f8728cd953a3f3e2cbd24d81f9959fe844251766a738f53f4a0533af 35 2b3246ad118445aaf0e65777830105320a782933796b060d63c597818ea9cb1e f64e7ad43c22216b962d9aaf239a777aef5a07c3f215b0c878fb71657bbc2a0e ae22ed337a7a28a184b74db18daecb7d532d5a94eba23d195130bff5ce3af0e6 be7cf7cca8fe7175d2c42736c01edb578078c9855675f06738bab2a98c457e5c cb6f2bf64f430f14d03e696768e5c19042ddb23525658ea4806ceaafb5cfe2b5 873c9d296059659ff20ec129924585f028f7a1b2c4e7fbadb998264c588ed233 e1d4a507dcea648fad6da1488a502ca7e515260fd338db170c5a67cce924e32d 09192d53f9aa93a7ad63813ec78d2c7c7414c8f4acf34b408c81eea460a1a245 6653839fb5ef10f2dc7bb0b045e3cb701115c3f1dfe38e030b4fcd5a4b780a84 8ec9d26326a10754cc7b91b1fa736d6205997249262767f31e0b6568e6462bf2 0de8ac4846be991d27c4a0bd350886eb3d020c1c7811da76b06412fda3026e8e 48eb9e072f05698d31788c3414f02e79e9f9abc7685a81c21c949c94f115e3d4 67de35b8ddf6bdaed150cd16e877641c911258f19d835a058af744f8a4523475 487274622c2116d1c7344e05ed4702fe01fc71648a9344a21f80827d00e4d9cd d6805c64f9df9e7747011a57b3ad5374ddb7ba4ef8bc046d2a29555b89bf84be d912a6d70268e0e288b6b268b9ee4d7e7b2f4ef080292cfa13f8b3759eff0cb5 44f632d39241aca1e39b9ce5c58ca31d7b04ab265c288de4bd4ad8875457dc2f a1d0631e13b92681464bd8bf38b212a1aac3c228224da133a224a14e28bc0151 322485860a366de05d24010a52303de1d1565307deac3d5d00e22c870eba644c c9017d51431dc5ed2ce387f4b4115374c3fac8b6fea83e9edc5b4e2afc5b3c1d 580bfce9d89bc4fc2338a429057c0eb90570121fc478d011278e5bf499f2060e 3612fb0298d52f51b912094429c84c1b00a162002c20a299a9e2bf859257ccc3 0c557c7af7702b7982edb6c90ed9a8e1de952db2ecf07fa4da40e67a3606d244 d9aea29db781de5a6618a5bc7290a90f3d50e8eb6bf157fb06ac97cc9a0c6e34 5ca6623cdc5b5128c5cde0c37babc8a7041514109f8ddb3f38253ebf77d729c4 07c4dc5a928c98e2fc0eb71d758b2b708d853fd6baa322874a522100e2281083 fb76ff4c01a5c3d930fc09c77ce1101420e117724ff12f62fbb3b6ccd2942bce e68a66d9d9a4dc8768c580ba6d79febd3b0b17c126f0c9f6f22c2fd45011fec4 7c3c9379adcf648d4c2668bcdc229094dc0f5030018329211084d9f1c22cd0c6 54e90e2666b4e28985ffa407c681b149964d2de3c5b73a3bcd05949e2917297e a167bb02b56188d3b94a360678f94c74774e094e572057a6f4d2aa4f352732e4 efd06d7bec7328cefba3ddeb170c4e969bee66403ff54e76b931be9cffdfe6b1 4dfdc5675f093ac048d6619fe6346bd07544adc106fca5b75a7a5840c53e50ac ad67d15fd937c283581f1518ba94946f7212d707ade00fc31d4b8829721eaa39 0bc436f464a12e5432292cb8cb70245749ef18a9bd3e65c434e759021afb6964 0b739f53f36f6838490d4bb08e7e405ca0d9eaba4a629f5c287083415e89310d7617ed56446e16a7a068fb5d7980723cf4f2bfada39bbb4d87775d536cc99e0f4743dc8827b159c56e0a2d6fcf192bd722d959fef51a1d55062aa84b04ff1704e5f8f2702123f744e97eedc066eff86d2c079e6e3947f6f44afd18e534e9850aac279a165c2a2e014c8c176bc00dedb958ff8f7e3225a28d12629dea16800f06870dd4a81c41cd031c0574b4661626eec2a292500323ce6f0a80a30095ebec039433efa9de56a895e10d845b52ebd3ac22c3d930b12b7a47c1f1384ffa9ef3037aff9e8ca435efbe8a236edfbf1b3131d3418692d12938f366fa8137fbf2bf048c8f9e625de804ea371a84c30cf898bac3e7c44adec7f8465e2233685bd1e6058c867c1f07bb17243d9367360e11211231477ef0206018472c31181d7d038f0f25806ff94f7829dc1082b6f690fb730482a56dd01b545d01ec4fbffa113da1008b404f43bd16be26e15ac2e4ac65b96d43410ccbe0c31ec8dea44cc6f64ee10e38f306f9b86952df257a48e4e3ffcaafa2c0905e6c4ec89c4a3270b9423e3f031dc1beb153e711d4ce1d07bc9a5f0258821e0d89f39a75d7e1c103fc74b22c05f47e0aae1a1425743803209cbced1b064767439574531a6cf8e832405f3085055a70185bcbc26e31cb097feea55fbd305b4980787c596f0c1e200010a8d3d50df82bcb306688c4abc5854732822e3b0ef8eab7822a9ae70210428c6a02dc460b030f63dd4f4579684e4c7b810a409b0e53fa8c06ec6ec8fcec5de233dbf79b09e28845e98bf0a6d748c59890642c5c25335f116744673ccabfa168c559170b0f2a00f15caa8e21b6a6e0c3cddf3b8a1aa24b70f9bb2f902892f95a5490bb7a0896b1c7f103c5c03b1d33b15f8b4583ab89dc8b4f15ff271a810e64467149700966b03d3756744671571ccc41ac6c67c8856a9417b74717d053e48964fa58600396fabb2db5836a2aec33e295fd832d70f8f4821b0e20f60410499c7dac50f10c28cb4c2633bf4c5ab0ab205029dbbb336508093142e4aa362a13383e8f58bd056a0924b6a419c3f61953d585c23948b4b3ad7f7a970c951ba98c98d8578b4900feea10413a82a52c26a8822bc7555c6424cb83fa9a1577998e4dedb3870d79045b881b23056dfe7ad5457bb54d796fd9998c23bc43e17d00f3a236ad61853900509f68701adc909275160004dcbac59e96ea51e972f7ae33384f094c8030660c50408f2fd7895a18af9adc8f59ae575f6ecb212e384dc92caef5f77857d63302ca23b23cc2f618074aad29e951ee8a6e4486093b98cef4dc74b52a98146d47092d8f1c59e8dbc3c21b3ec153795a16e6b737ec069f7c7a600eb31a3a5824880cf610983463e3355bb0c728be3f7b3aaf48647f330c1633aa8858089e30c57e0ab390898a48370e162e7a6a081b80516f8760b2f7bc5a8e71ff5a2258e9c1ad4d38c61e77d7329ee81c85919bd2e81d5a0936cb3d356a04c52347aa09a287c907a16a9db15a3691b7249dd929e8fc7bf8c3714e995834b7a8c15ff20c543e4208a259e5b9e4b6a94c408a81943797bae57d2ed2cdac44807fb9edc99c212f450daf972ef5c5d38312652952f54979c54b276cb44147c9d26c5f9abf7818ae680d81b8991288150e95329574bab10aee69994cbf0e8589c7c727d1ad543014c70a140cd6c9585b3aebbfc88d8b80271c4e4c7927dfcf91701469614a7aad8bcc0e5cf7df2d0efea8be59b02ce62fcc81c75e9fb5f2f291809f31014c891657c40fd3ae7127a8a50a939a82c9cedd27d6fc4b4fde96e5c7a6338e44ef9cedbda60702e8931aa4315d50f3a3d1bd3a6089eb4e6a12e3e74ecf7bc845f1fd688e2408fb2604182f8806aa841a889db734ac9c2d0fe2d9fffe317ab50a2766bd16880338c909e540eb61f358c9a9c3beccc38da05f0385a0db9bd25bf69dbbdf0bea0ef7a94d7a3d03464d97a6405957d02b012cc9d4d17743d387493b3f49240bf706547125437712bad6e67c4e23a19623e8aa8c97bdd84baf46b4b119fbfbbfe6016d5794eb279297ce6de8ffbdfe041a41e6314954a521a969f3d2a39105abad0f670773f6072addb93fe4deb582317c3cd44711fe17a9eb04b651eb77a7ebda018efe0bd2cf2a36469bb7a07dc32b31ddc0e5889b1f2743ef62561a0b399b8a0d9adce99c99bc355fb82695539f85b766993ed3e4ed98b2323c52a561fb18cd00e3dac850899bb2320ce4472b2d9decddcb3a4ada6f0a2b5a5ffd4a43f8243a09705bc471014690995215c179490d4c13f0e803eb5825a4b0a972e0b4924b2d088ec9cd76591d5e260bf2cb8db80fca01efc087d622da70e1053bb4b02ab5970403b2ef0535881b5076afd0f636868582d96c1e1f087b97f2de923493f8cde709fd7b4e36d0fdb9222622a6850e0c78685cdc9e1f18aa3b23c280f60bff5f1b07965642e95e60782cafa1b7981b654f3e707f13068e3b3aadc7e243ac6997370dde9a36892a8d279fb93caf2f671b1be6b3737df906c4a2256c5356edeef6180da3d4b5675efcea6ed6a8f09f3478342e9e1c87fcdc4dec6e92a5e19a34a8f10e848c9182d3abdeb50eb5f101625042d84ac96203f0b7363c47cf7a00d1750a04b2d7006413eacbeee67c7d340aeb0e6da1e011a22fd5b1e68215838d230ac90943e51f8f89804b1e9a5e19153287bec0dd5394b7ad9dbb0d878cd03b90211200d943dfb0270fab05ecf9a6fee9075cf7813f42e230e31b00ece66bc2834233020983525d98b61eeb5da8cccfcac5753acd2322327497502233ff7ba0189bf005f216522fcb141a596f78695e24c4f3324009f2d8cb364ea7566941221652c303f511a3d4be451787ed2508d028e1e369333c4eef85b7bbdb29ffcc98dee9730ca9e736767408e4e0a1442b9269e97ff9aab3b9d1ac33a6ef2cf355a21884d90fbe47e9935d0f79f7da4d4fb36cab51775803b68bbddba144c58a8581442bf606e5cc31c85aa94ff49cb2c3e83bdf3b6ee05be71abe3f54ecebdecde7fb75070a7267bec3b4ea6530be3dad10301328c9baf6bf7d49a30f543b2493267d937c08a1d18cdbb652dac7a2e008250cb7e4b027ff5d5c1842306b3d4d732c3a6d8807 false -check_ring_signature 3d16458bfa9cbc0c805e78d4174bdb5d96f128f2edc6c5b60a661d9dfbb6eabe c069a7bce95dfc6c8541cc595bb392e8da448c2ed9e50a073188aee85893ccf6 1 790790d3bf1d5e4eb239ba2c71539e512e222506117e7c157465cb570756535f 26a03de47a32903d75139ad6d4d5c89bed4dda48b8310c81a15cb96465fa98047d71ee0cb2fc45844a471fc4408721e28678ff1e3220d3a0a196fd0c09d3d409 false -check_ring_signature 35091353af008ba093777616f42df61df6dd5c5d16ba0a471cb4f605662d01d8 ca8b3a78044378ee5115b4bbbe63ed84338e1c1155f343761c13591712db769a 42 065f7f22698b25eb03c7e0489596b78b7c06ee0980a05b6487296f66689c7657 02c004aba56bd760f15bb75c1d5cdd6e0e7e9bc2312750656f8f37ca00af8a50 3af002b4e79fed6e71858a2aa26ffecbd93a6d78c58c13f9a342de2b473bdca1 ff9330d78b7dc754ccfd493946949494e037cf5282aea812c4efdf14ee15cb68 d0932c71b197065231a59462d5fc7338431003e1ed02ce4fb22f8073037f6ac8 99ec92f1f41ff68c3e0b83e1b0358b88db19a781f3db805edbdd674ed2b5ec0b 37875451dbeccf24b84e221fb3a1a3e92ef1280e16da92627c59617cafa464b2 ec215d4a3946b70485f7ccf6086f7fe96573a7440b16382c074e73b91b35a079 198f88e36909ac625de2a5f71786b3fe37640f1476d003e2d204e3c42c9899cd cdf11a005c04670fbeeb3a6626e16d34e7bb2a19974514b136ddfd0aa49bedfe bd3149aa170199fe0057fd6641f37584bf6f828d31cecaa51c031309c638f5f9 78689ff52fc8f35cd4ed6efa47d22308143e55d789b6a7066a978ca932288de6 b56d923e6d4708ce3834eee37aa960cc8e1b0274be42867f520a660d7b344b1e 58b2274d04851790d18185b2731f7ec623e84b55e6f13a67e08ff5afcf80931b 2c3a31070223126e6e1d75b0188670e4ab8b6b27ec7c1aadfc0c7dd050d4dcbe 7fe9463c018b219f02c0fb1908a215effa85dbb1cd60a3834141b4d4da8ed8ab a15090ad04816ff66086b431daf60f04c7d84e046ff1e505244abcfda1caad7a 69823cd6d7a17c3d7c3d066f71baf5c111de8adf4a20a682ec57bce4643f8094 27a9b8cce32414ef52f70dbec32d9349734668604f6f441b9d9a4bf0c73bcac8 dc51881356b9154b6f1532fefa1b0047b9b51d7042296f5b1a35f1374dd74a2d dfa7fa24d4b9737b66bfcb0069e2c364e9d033f9bae07635de7b5acd2f95384c 61b1eb05cf07e9c9675c88431775eeec05013fe660e688472950ae7606e72ace 40184ad3fdab88c16edaa943321a51a645cdd443e79ee4fe6b705eec2d67de3a 2af600ff7e6e0036bd2c68d97af9b7184aa17ecb9f4ff24ac289f3341a4ac136 cf47d847802b8e12a51318bc61dc439ba3f2a9e5ecb401b37e68b75d7c306a15 f9c1d012de07c608c529e312e8ed5b9258590ed42f3ea53ddc1abcb8597c246d 8442de89f43c02364a827b2c9ef97f6880bd91fa92c94f256a6d9281f5292069 b514d2d179a20e6ed334d0f9c7675ed0d8bc88bdad54f9a56c849dd4dc7b37a5 82d81511287a3445d47130adb5781fa8c7c2c27aa24e5e187699ea8008e25d25 b4b69a6a2d64e921cb1db91ce8bd8386ba464608ade33578013b9f3ba3d490d3 65aed89da0048f2b8925a54fee42e98f1a5832725e2568026699e102925a8cdc 5ebedf012e3b4678f8e162fd86b3cfa892a4da947d8d395286dca737fb4582f9 f7ef1a9bfb43a2c945e50545a703fc40aa30f961bd0d7c2307cfe737f9866c5c f55264bfd21101919f57969a5af8bee88c7d54a2dc1fd0e9b7a21429e8f862ea 5976c135416a08fe23dd0e204322987234d2ce0cefdea7f3204b5a60ec000bf1 484d33fa5096792750edece1b0e04963e32d7c7c7c85494eb8e68c7b6f61db01 4cc9131ef362a6de288183666c6f104796b07c931583b257c7966cf4c459a810 a287a4903c5ed5138fad9f3f528012f2c4c65e65d6260ed6a0e29d91e2e91a13 a9c750ce5f1b08c64813079b3a7990eef800d794088e98096d0da72c82de0626 383cc1f1bc5be550c796bd21ff4cf41ec73407fd42b63c7736a97ee989b1a596 3c35822b821941528d20464a1c36511c0332233e373292e7d4b6c38b6a8b900c 6c1106b5ba9f8903c5f237faec7fe3a05f4742ca2b8d2f16fb6c18d1ced7b4da a4a3c41d3613a71e24e381bcf83926ea8c8440b94e95074679de4aa78806af06d5964220e2c4816b72271d71ed2814a7c9a60154d722089ac3c2c6f1a0d373041f0fd0de83f277464380f3e6375920c2b9788416d2138bbebc5eccf7535d750b187339b781bc22306f369c07bb83381f03194e8e376338f78970e7ebd507cc0641693f2320a1b2cd892422a4e69668a08d2528be642f7ca1e5d8bd7e2f576604765b58f41aee4da25669110088118d78c73f60a6612519785a2f4843551e98025c3d4f872cd3043a545b3e7f6d6c304a6c899ba89d122585e265e41f3f456d0ebea48311b1d30fae4cc67795d0e597faf2175c9a128dcd84921c39b11f687404e78f80bd4be79dfb2d021df4c3337baaacc86d90e277bd1aee7a794a18ec3d0890ca7ed8130a83dfb4eeab54bcc68a41dad4b8fdd712e85f1ff567bad8838e0fec49466d9f6f3b2ed988029f9063afad74dfa3fd3e7a5387cdde2b80539560007da7ceaee7c2d00e46728bd70b28df04574a44214343d7201cb3c1f4e12d6904caa5e80c0ca006726ca9f52e4fdd2cd2f94abe5b9af714e065c8dfa25ce6e701610ab497a3b2aa7278b0c988906269df71038506d1e4548199019b87c02fa908863dc95ca9c5f103f75bd8ea77ddd238cf6552004f9ed5056727f2346a22970aae4b52b94cb4f979f6c759176635c48dbdb2fbacc6d98b4c9a7e24293e208e0ab9a5c6bb38c45be1fd895778f7b5b131b13fe73cd72ff9a5efcec02cfa814c0da1736cd79151fd04c93df3500789a49c49016f9fbd5f99fa1a37124e3003ab076a70f3fa77a18d7c2bacff78e4abe9284c0eeb7441af2f7e69fcce7563da130c7f692fdc3c420729da789686738664a636d05c252ae223f816c32fd8a1208e00acd7b80751cde707c66ca4c81fb27c483c071fb2cd904d00c0e4e9fafc404d113a86b2e7e3a9b581e5930b8fb835750c764190d4b91154c25d52d65cc5d1490acbbad0dab6b604587406413447ab8962f8fe30ddf983ee9e974a0bcdd35d0a0ea5ecc41b3d84d06f3584ba9f233e018c64d53bdb00c1f0467e065062830d5108a8384d79df1fbe8de88903a6eb7d34ccd7f7b0e0f8329dc3258bf3bd8d26370811ad3aef9697e55686d81f47a55a236db6a93a03d952fb7ed252303f4ef3c003a5041a1b7130cbdcb5ff971f0197f2d95971309200d4e7dc95141ebf58748b05154ffe2b54ba4400be22a43e158222af453cecc175691db4e71d509b9822ac0d0a2deb1342a764ad9bb774de41764c5029f7665db8aaea39ee8ff152f11f970618bb8e36f1e82faba7df10b355a6449aef16add57b8bb4be400f3e034a82b70487b11b1eccaf6403c44f5ee7665eb667cd828e4634bbfbe2dc601671e935ca071bbfde164b6a9966ae962166451dfa1245aba015606b119e52344b17c154300654df443bcea75ec65606938fe967806a21b786beaacc831518df76958cd05800940c6719367939c7e80644bd18ad8623daf662262a8835e04b6f1335df26de0149fc15065ca6de986483c6d3223508d2924c1b7c3df4291f10436cbc07a996035fcf2a50469db2ca4abc26ab76c0f30a5f78cd05c62affd067b399c00bf6900611a7036d8550a6cc69b437d53acddaad33cd0e202e055296efa6bfd9284f8e06916b2d2e02e36ccdc3a8650a07dc442981158cab83752c8c469058af18aef60e610f52175a1370db2104aa95e48b19907c654ac00903c93a87e5de8c3b290f0b2dd6573e26d635500ec775ff841395de20a4aa30e93ec8b22837219db0a87d051fa8d1325986ed3f6013cc056a26969cf8fca30b184dc98001a68e943a88390e7ad40521620f3dbdd27f54a958e019367214c45af25c70b958559325984957034acf9c1a03fe38d181e76af513fd32212b3c3c437ae5036327379127d46d510302cbe98689c215839e7c4d7fc101a48ad7ecf48f3910523452f6fa0df9957f0a5b0ed615544a9cdb2a1772e6b683f36aebdddfba7ab6f9112438eb9b3dc3ac02d5bcf2611f90bf5167aaf79c946103c231377e73de5ea411e91be31d967c9504728abce1987601fc18f7c763af97c433e4e2285ebca19249e931dd83b0b3480a79586be065ae28cdfa6dde10ff6ffa3da4b66fd7a1fc5d0250e687bb80af7303ca5380566db99e6dabca7fa7923d8d04a441b6c2563d3d02976ae466e1978408da4e6f0a572cfdee658a9f743c5585b5b2302d671457af1133bdf3e6c86a2e0b029790ff2ea5a995fc99471ffa819b925df4f5c915a88ad7f0d9f5679569ea0b24ff26285533f0e416b551da0b238ec360841b4b4269fc85d3ce7da4d2fd2c0dd08daf218733f0ca61edb14dfe9ffd6a89c922349c70df540dfe258567b2af0ddf4f71cf114f1e803ed6354ee308d66171c4cb8a89f5a601435e21d63906200257a39e3f7aea0c6280a6e8f3ad477f651ec253cbc9635faf7898989b603c9706b2f5eab7793497eb042514e619148f9d47e543f02ba56f8339568bd33c886306cf7abd6703c64e6549f5a9f99c96f790d1fdef28e8653b3d556e54908a53dd0f565aa5413452c102187c3152cc4f64e96ecf8714dd317f4959b575bc95040f0566f1ef9dc6710571c7dc07ff24e85394303c145805c02ed90281969b71994f028d50b0dc794275d9e221720a2c49f97c4bda6d34c3ce951f6441f4be90a3c1029821a0bc765422f2a395bb4468d3882db5ddb8341ac6f546917048ad7b812a09540e7d7b4cdededa0f75275e67d8287fba9749b04ec461b3810f911e47407e0e790598699ad3db1f75d26edcaeb7fa0faa5567ba8e98ab78abf08d3859abb307e77115167cfefa905430fa634a665edc939d4168c685af35afb9d1aab634a4081fe126f45cf7bdbf37f71e09b64e0ba97557d362c73fd4131b24f1513a3ff709c11536a8445b6d4b47de476ff9239864c018835d933fb792bf8c821c2757dc0cb8a547e74f04fbf02c336ca19e102abbf7e50d1cb3fc5718581203ba2a921c0fbfad7cde1d401514fd5ff1ffd460f75e006dbfd12e7e0ed250f2506247df6e058b916e9b02b33b35acfb8a8fdff1546ca277eb5e9b58bdf8d31bf08e18c8b20fe478e5d01e763f0aa40b9fff8c8cb9a6f3187c656a0a6056f6727f3be37bd30328b59e3f22303a19672fe3cbb9cae9bb436c842c8b671b6da07c959598afd90f0bdedfedb524c7734598dd666ae75277cc47a7622c40e3c639b0289f31d6730682556a5a5e7745b81926af9669847d317ea51d8f9dfb0c11108bd3330ba4fc0237dc2d2ab3882b806e253180abbfd318b2997af57bdf12ae4a1d2fe508baff02b0edb785c16c7e95bcef9cc7b43a7c6442009b5093b57d1d59849ef824af05029132be20c0c5845370f8ca88619355e7264e06166e9d46e8f9a8d9e051671c0ce49f6c3cce69a22fd8e657a8ef9aa6af5d4beb828a45afddd8f6545e6d50fb0bb8e9990384a2767e84d6018171bfbb67fdca80bebd1aacb2f5308c511a45d503eedfd961cd1c3b1ea635e1ccc08c69fa732ee29a41ac43c2b3392b5e7eef7a0edeb716c08a809b3fb6a820e39289baf76f5f1a7af8fce0a2a75144c10f54a2047cadb155cc25d6580ccf92a00fe2d735d80bde7b44afd850218b6b4bcf179100031b1d162813371db718a1f50b60dda75203ead6ef7df852ae62d1e32c17930eebd58b3e6efb9da69f6af498bd011ca14fd5841a8f226fb2975a400478f4950fd7ef04baecf73561a95eddf4a01e6508e76fdd658376aceb3ce4d99958808f09 false -check_ring_signature df8cb84d7da34446207c0f8756625b681f7d3cd198d93661e5f926f6c1c5deee 34614b82d672606bb8501f786a3fa73c22427a296f14f60c272752af79355d3d 1 7c9838a762a0689fe972c51642b39cf89ae9f25f195a8d76233d8cea0ed2f001 80ebf5a8183922f9aae9a5e523fa773626d6cb564733d46a03a0840a1fa192948bb76ef5529011b0fa5fb92727789a3a7f6a16aa9eb519474006a5522b3a830c false -check_ring_signature 98fe25f69d409a4dd8f9d43d68695915ba50ff77e54489f6099a3d83d368ac28 f63767b1b30f448c73ad4e957a62b31f80b779600899ab2c8f3f82b807f878ac 3 958d84ccd872d89af9cd584b1176c9f10e8befe4a953df6cf716f566ebcc2785 8d0c182bea7ca07ff2d19f5b822880a3d89ec1c590eb69c6bdc301420cb8c50b 6211101d2399abd17fd876a4efedecb9063168b311378a4ccfca4e4ca30cf76d 4af5fb678df6e15e6da17660f87559e13c85c5bc14358a0addfc0a0a592a0e03f5f83e0766e2cde65928abe34bc8fcb60c8b0cc23bed87a2eaad200c2f8cfb08c997ebb1fb6dd7b1fc455c75f6365dc8df6789b303e71a2c515cdffc533c5403205ed12295e8a89f6c08139b495b897af819ddc69fa63516c128c0d09fa5d3072975f8e04e5e9ee078ec955823fefae2f5e6d8f8f88ce168d443a79624a67903a546618f0ec4c93a512a123ac47d9b926241aa66af42f32ad88a1f8fd19fe207 false -check_ring_signature 007760c59bd77bcda55132b54bb87da8431a36d8a0f115375cb08e0fdf834047 336b015961db1778a272acb36ea8848338c1ef1194e64a51f49467774ea8e2d3 46 c1fab702d3c600b7ccf4961aac90380708f35f1d3be861bcb656c9876638aeba 5cfc11f8c65398123e595f363eb557f57723db842f1947a2777df79ae9c10dbf e5195a96bccbe765a50834493f43bdee3303f5528e4a2dd261c0398bc49eaf8c d9358bb580fd402c086d5efaf0eb635cf7b2fac3fc0ad4ec11efba99892848b2 b14c8cc1cb40224c911494d743a185bc4461f21c8c93ad5afa8d8d793766c1f9 6dbb8927a3492ec2a696bbc1e7bb0a0f094fca8018141c072b9fa1d531be75d3 b6205056ce3280b40b23d84259a8730220e85f4b75d1ff992d21323c9bf41f26 4c85fc3b1a6f6ed809aeae815f8dca648a1a6f06515a83443bb70926f85d421e ddcdd0f75521306fc74f532f65e5b21707ea75d1f91e8937b723ff4ec7ab4193 85ea74173826ea130c35d6c8e73373b78939dd9a38f0f04614cc5308bf28ab15 5607a3b632230455de38c027dafde68281c0f1a21c4ea55e7caab4b1b787cae5 d9aafa15d934e394a6b66fdd8a394e9177f07701bd4205aa36a7d2044005605d a50ee129025bd86595940c679ac152eef412d9af5af041d3378d4faeab00f7d0 8fa3e51c9ca0e8e774752a83a2676dc678e22c530cb4b8a3c6758010a74fb0a8 0980c76b242f9587102e2de530478be81a1962881e1244d4db24b76ff56865b8 ee513da0388f9d4fe74addad02fbed0e738c6301ed37590df84a1c480d7a9c92 4220ca87c7ab13e1d84bbb18e418819c0244855c47fa0c23c67937873e1c5853 a7cd1b4155492963f0d24bcc03b032ff4d74cf0e9629bea191fb8e2a2105cb37 516c2ec4e591180fa1c99e992f7896bcc40fae31ae7efe2474615026ed215ec3 1bdfae7508ef97c0f3df1419eeb8d180049ddb77657d9a837eeaf69332b805ad 22df34724741a57779cc65ee5c2d1c57d3fe56e7abf023e8131a6de2dc45ddf6 e26df8f97ca5ddebfbdb422e00bdd3a3e02e91036c503f7aa55c50eb22666abb e664261871333c93f8227b7f077285dcb6e58d1a4978e538aeb714b373a00fde d70d34541ace5001358d2388d872953043a83a1daa1f1823ad4ed2f20f59552c 7ff38a4de8336e3a6c93b24844bfe4a92fc17729647fe1bd6a02c7c3675066e6 77ee0c974dddbf3e7df5fefe13efecd4045d9b1fa3bc7f14c22f57fbb7fefbd6 85913eddd1574082f863208ef160c2e9ded86fb6f8d8af03f1b2fb9f703bc39e 4d97eeb7532d09739abf380373ccbaf29dccb30158d97242d9e7fce24244341c 6a9275fa3c3017ae21f6ea420d67a98c29557a299a30aa877c364a8a9ec4e325 94891b7731c6af959cbb2b006ce04d58e3dd7a855825d5811d83b0406aeafdc3 6e10002b444425fb66aa6ba14b639629bc766388270af14d817cb8f35dd61cac 04ba890385e0d3c5ebe21276c6cd55638684b1130326e2a4e1e4ca96dd2358ff 2912a9e1a53bcf4ab2295bc0a3ba488997dbb473172f89070efcb0a4b7d70b97 07895ce9825066e2d3a8862b378e7e5822df5fbb8c1c042cfddaecbc595dfa03 fe7e14d34a147888fce30bb610dc648bb30353512c4f537dbc1f50e622ebd290 06e5421e7644d67be424827088c99c1de28055b65ed916cd80cd406cba27369a f13770fd15fb449d65b2c4041129eb81b6a33840d7a0c32766d2008176c05220 4cf99f96d5916e2cbf047c07ad8b6dcadbbc14d306f27af5791640f98751c37f 26d1e0d771171f3860ab9e89137e1a1984109be545f61ef224c91721bae84ee7 7907a66f4a20e925bfdeb21dc838c3e2b650d289e5778b4e5aaed7e94ba10d6b e133dbd3298593d420a2641679b736bad54743c13eef61823dea2744f7e0481e cd82d27e1a0d6bf0003599cf717dbbbb2486954e99259ff2698469c897416b26 4350503e58173de59a449b1f4710a516aae72639ec80339f2978f1f7d1aa40a4 30fdf6af3eab36da6146b423672397c932529f585eefe884dae6a2583bfc66ba 243205456c8234aa7735efb6bc4b32ea8ba398a6effdb0deb4d6f249de3eb2a2 e624d7615a575334b3fa7cf5371b277705a5ce51ee5332b8f7cd217cd8ac1428 49967c4ed75bae1e3d26642399f2fc1a09e56563f1269a10d52345e089c8ca0f8c6c604518c3b121d72c9191225907829a9c338b49aa2b493fdd71e1677ec80d72512cd2887baf910f7374618cca61c926d9b8756448db50546c35db273d7101f7ab7f353674cb7b59b09e4acb04e3c4531686d701f17ab13b957553d6899f065c7644bf4df43926998a3efd9487197cd48a554df974320357470259522ec80320d13deec6aa65e30a29b3940eb18ebf4b6cbd63dcd3a6acdde4826ddcf6290712c0c769e8f66728d9ab3199a64b70f6d6f800efaae33b37a4a538b5d224fd033c49934398abdff90228a72c02907b80bd2366f4de8387f369b076f1b56792074c3663433f6b32b65a6259c986068ba5ba708123621e0aca9cba30b0181f460f3e00304e7cfe56a1660ee6d6a5c8a33bc4a3c1a78148a4d879521fa3f714e60031b3952db5b876f21b5dab4e13346e89991056cebca99e1bf976d786d0e0ec0e910be4fa7e0447f948364e87cd8b11b6c31bac60a0f6c374da7e984d209d1e0d8094210af82e1f112d05019a8b9df78e60a33ff0545ec71afaefd94c21ed490677617afa6345357ebf7699107c96d74ce197cb6254e3c029c24d097ce7808e08b9b669181964353254cb2fa5f2164ac2441a93e11604433cb5085df64721fb07825bfb34e005e09323680f36eb99491b164b1c72f6006b30783bc90cd23be10891d592e8e86b15ad422e1fd7f8b3723e1fe36432a65c0308f0deede853eafa00226f2bf3fd02f3cc98f30008bca0008ed315cd5aeceef1fbd4b266d12aa31c029f9edc980b9fd712f9e604567daab2015e5f0a1c3f9aae5e054f406db616fe02cf2897c50920950b22b18bb88a66ee0805463ccb2337e48484831c36165abf001f243ccff50852d9f2e60cdbf0c254a8ce4f5ecd2b53acfe3023a01cd5fbe10b679692219de64ce838086a71e0e50c20d07ef4853589e5221019b028d6dd2e078e26aa1108e6904768b31047f5dc0a85b1c48b1f0262ec709fb52b6636ebc9322bbaa4cde35d222f5df034c729831d4c1b164bca9c28a45c2f5e58220f7148009559708b3fc2834e14a33e5dd2ff149016a0799dd5620cd7a92bd2ad695d750bc23c2ef61484ce7029c04a7ae477ba33997fe0e3eee9a3f64e143d25228d080e113c86062e53e06339f3d0a8a80ae4bcd65775da08c8cc85e5178c4809555f0dfcc2f7235662c09a972d6b793213d3f888fda4ccb7b98c509ad8a16c7d5dd2064441b696775a72a18e3836b6aef7a60751e5c572d9ecea4d20f2fdf40de381080b425ab837647323b31444719c392e84e703ceec7040ea7fb65cbade70f0ba04b79a3274c00ed68e316cd8bc6042e686976d119ae1cd57079a1461ba6346410e8e30d38336710eb2841d6adfac0f38cb982ca9ced9441330819cb096e48f7200a92a364897cd83b788f36ede2940fc142ed5eff3ad74c718f6eaa4d3ef66c00bef0e322fbfe434174b740bd102dec37476d6dc2f7dea669fd455331ed832d107325454e36d596e663e9d230e7891aea695e98b81db58e62dfd36c8c2cdf3fd0e90ff980ea2705cab660af88561bfdeb581b1e1538799e19fe9262b5d928021068a0ff28b0e32e87461bdde75454d41adfe8f5c5d215e93d94705c5bc61098d069d15aa1c74d6a44fe6b52b750a95c1f5eaa487f5598392473a5ea5ac4e0206004178363a1eb42154c89e9e80736f080898d48ede32e1235539f82d900a51c30571d47ba1344ffe117962a898527698ffcfb48f484fe3ae192a1f7d7ad1d916054a6b7009aca928f4408f6c57ff6abdd5df8fc5cac4442e3dac0f8174b23b7001dc0635f1994b1ee0d7832b40b9d01321e472f655fb649ca82c1d3b4c7475cb092a383445c7ceabbbdf3dd121964c51094cca10d56c6e8a29ff0ede3c23d8280b5c7d7b53f5a64e09cd00029a4ea9bda56f98b3317fcb2a2a56cbd5cc6e573400c8e9f02f1579649895d0df01d53eefdea9675bf4f0526b8dfefc294983ddeb04de675e4767da81be130b1a20a73f8e91dee1eb66da509cb4bb9ddf1ac7c7a80a84203d2570bde06010b69082064b0242ef185b43db5ab87cc92d98953672040775195cac9a81f408bc7be2efd362ba106771b9787e54e9918b96aef89bbce407722bbcf4376497ec45050302cac8962d905023e6951bab836274fb8cf1cb8705f9778c03c96b9686e5dbc40de37415a573cb6e81f7aa68b7c4af356fba61af02db3e1494c24a7c4ce44b1e88d2687f1f580eae9d32efe7b50d9334643766260911e0cc6e15da782a4c3d11f99b0a96ec07e84a0bf5465d204f74fc066d2ad80ad79df3461b5af8ed52323aea0e15d1601a4b319ccc9d37a39d7fed82061d540eb3c1d0603ea75d53048a41a4706081a22e9e8f3db126699ce64477e84d1c49053e97c57a712a0e4adde9281411c7f99e293cc939280d36629bcf9ee512f33d03836db6f5fbb830a11cff8bfb461cba54726fa91f4a93a3e2d1136405a01cb205c0e5bb1e72d28f1f6435f048914c8955a15c67e2032e8c2b1256f43ced2a410515eac2c5a3c026a1c74a37d8b6083071c95042c7b806588ee264f13a025742006e2105d5a6dd3401da8398ce2e4d4ba41167ae41b7850e5119c9636619acb70a15f4bea2c2523fb83d1422c7728f01267b5f7a4aaa817ecae52e4eb0b167cb02db0d9aca321a3dd5e21fa877622a55dae9882982d4f95a984690f0a38a55fa0304a6172f6162c653c8f6bb29f93290b91da604fd2929fa669f2feb4f30978b0f0bd35898761361658d0a1061935616b61ceb8e546c868c2b73fab5096105e60e4b7b09cd3e79777f4a2102fd7806a237fcaec29c410c4bfe1f7da0e924c1e506fa44558651824221ba855c56490f5c813748479c613fbae05785e5e8e343a0005ca01725dc2c097d049028525a600066e0725bcafffbb082cf4d2fe83e92cc02c51bf1bc88c83e2593f1a78df64ff7524af93c4117dec41c3207accf3ebe0504ad1b489b0a3a27333ff708837637c6fc8cd827f0a2337f97f191cbfd5f79e60bc648dec6f715b95c91596d7232448209f1562c6713e8511bcbf1571244b8f80fd995a59e16587cb7e045fdc11e2aa1c8f91f7cf7c180e4ef7e675d565fed7c0bd9403831a9968597c588f16c84221e77664957f643e6d3ffcec01cbf65b61009f2a9a51dbd234140e98038da771bd2c52d1bda16b08a3cacdc981cb7f0e9d602d628634567bd9ca56eedf9f7eb8f708da7b3a8adc078941027405e350b1da90f3a2897c9196acef1706bbb28980e71146fe27089929d6fe5f5ab5c961dcfb90ad63a73a5983dad1bf09ad3ebc7e6e43358aef707400a328290f9993347d864034d9e0f43056bf51332a71fa0df60fb4ede380b6acb82a169888e9a9c1a55a6099b14a304524db95e1440a62164209cc7204fc19180289302fd55e765a90ac00ac223af136f9778925b59c3f914254aa03613e41a77e38312eee8468f8f98f50c4f887ec6fd39ccc54ae4d4e9720732efd2d1a08e9216dcbc181d3e009a2f1308a3a43ba5069384eaa72ca6859e361b38a5fe2a491173e2448026eb6c0a7b770f2d37e6ee3ffe3291dfe9677b534156343b271e413ee64cb3e5a0f5bff55e8d017e9e087eb2af688d5eeda994fb207a9021f597419f4e5075cec3469cd6c1490d8ae19cebf0de875988dcefb9a4423f3e65638dda7cc72b815d5f540fa27a4c006146f8d8f0a52332818954d36595d521f3fb3ad0f708a097d5c5864642096305249c6a55e45f756d10f6298a54819c1b3308cff6a936485f4b916241383f090e67f28d00056a94da34a278a7e13b918a8e52bf2d9d8297480db0d573bfb810021d342634a3ae8d30056d0fb15db010024c1bc37ffbf4b7f42d7a3ab640aacf04a013821769fc926a138daf59c18503cd0858396e6741e052d30797dedc30c7062cd34f0adb388a269511fe74d6727e411a7afb20f8a8e3ebd336e9b11bb68a0cab71332d987b9bbac22a5ae61a309041f3662f4e76dad5adb6aef6afcbdd64076fe1798365893fa9a0758d57166ce84db74130aa1a18757b0840ed9e1e940503f4c3d31eb5dbd4a25fd9c51f12c3c310499e4fd7ceeae6fa5e936e9663e8a406 false -check_ring_signature 354e1b2c1469105519eb3481d4ed7c9b7aea78748499bd433c520c0bae28b901 eaaabe196f961eb0a612831fc099b14686c71cb604fadac80fe8aa0d771e8318 4 245afe776affe277cba8760f8fa451105ebfc67123e161582c8c265ca175eee7 475f352271ee1cd1933adcc1b231fd9db14cc569fd266bd634377da07334376b 9401823d4379b5e3033368a7964b189e6d7971767754adf42f74ba00c222cb62 5007995c8921e550c978b83ebccf8b9ca99d41cca70249eb8b1d0e4a41516e2f 7041aac2a2df190fd27ee64e7e4bd05007265c17a92efd1c6cf258b0add4340fae0f19ec2cad2bb3435db10d98ebad09ea4ec51f7ede076bc041e086f93e0e0b184c7db1b77f2f6e543da8d69838a2dcc0eab7172e493b24b9d8552d8d16310614040ccadc51b8f5efb466e8bd205a2edc1393283e17075be27e4a3b3a1b9000a32bc88d89ae281cc49bf4de2f6d036e25525825a3b1e92ea1e11abe4b854a016cc49d74f695f56a41c9b88449a7149be313d02e00649311d226916f4c47a50b04b18e1e178453914d13f8df510ad14fe6bc4d19b1cb67ecb994e75e52a49d0081425eccfe55f85e6a69342938d28377ff6d614cbeea0ce456f052e0440e740d true -check_ring_signature 03587a5a814e95ab9af18e69da6b20baef894c1e24583a9c312a453ff8f17f83 382a6d0268553e2452920e25ad1b04781821d7a6590ca35fd2e9005b6b877822 8 8c74a82587ef52ca35cc043a31e74390cdc7dc59aca829a024fba43139779ba3 5a7f54743cfea4475cabebf2431e07c2aea60f94e3dcb2494bf23537fb3f86df b5595afdb15207ebc5b9812e0970a9ba85ae8f20a9cd4049a6f5ea2d51fad59a ba712b1ddfc2607bee925cbebf4b1e10f10d9108dca4193c443399746ea81c0f f92a4551530c0fad25dc0b36342f9d161523acf8a02adbc072e090bcc7722db1 37054d81d4893fcab1a70703e01e1a34ff07bf7ebb7969a22464fe31bff33908 8116baac5f4fe8151488b8f3489cbba308f6e41cc1e12828724d076feeea0635 752ea01d86b343367889bd36cfabf9c40b8bb9a166ccf84892a7689ff91ae9d1 02d8639af50ef08e03d7aa524d9527999857c2632dc7ec13d61b5325ab40a100c698ad327fb43639bfc3ebc03dbbeab3984938bf7778131f8ebb950fbf08600446f5ef425b28cb1a8a09754dbd610cc18b84dbef16988e46f64561b842f95e0e7426e41ef9b30f0f810065c40f22cec0bae7d97e2e84f345d00a4231de04100b9bc5995a9893af3e0ea477413739dbf0712a2ed3d3d69b2c35d97525569e110c7b68b4de7954385e9a2caddba5892d6b8fd756f66ef3c910a610c95bdd8fcb03a34e1e23e2d24a158185700a6453d7056d3eff6959142c60e604bea07a32c30bb410095e5c3109f051f0b0dab7f1829b9e134b203ba5a8ea1366839172220400c0c3d24c1fab0703a9aaa9b8660039751f8ee01c65160b9d21b5a737578c400af29cca0fe9e8e210fcba84b7104dde7b46d749e878abc56efd1e00efb8300e04787319575a0b912e8d3adbc1fb4af1efb99a0a55105c33c973ad685a0f09220c0866441ad54e07ac585c92a3c2cb925f43371a0e1d6834c5862586b7ae08f607323dfa68ac076dc1c66aa91c2fd1d95af904152666d40bef910a38b5ee1ae1050c8867c0a6cf370dde79aba708b238b4c9947f2910ed15b0214cae77e7cae50c61354c2238365a6a942606ba618d732674d39f56225589094f36fb13ea81440df08efad222dae068585a2c842c59bd19293c71aea510262460d303773f2d8907 false -check_ring_signature 9156dd21a52badaea45b88ef71e890c39d8231fa0e46ea8de1c1d58add5d3da6 54c96ddc8e8e7a8b6c606104480e870a6407991d7acf601c9262b289fe18174e 9 d59b01e12fe1d437dc6f1ea2dee1b909b2869122117e0755180a12ca3785bc5f 7f66e837bda31a84f1aeda702dea7701d0fa5f492a45a30c8abe050ddeb1b4aa f18a5c9240bca18c576a0bba787c818f24e8959885f508bfe5e791fd472cb71f fca71c19b8d9837fb1238814b6cb70c11e067a35389e727376e52a8ed5bc9068 8d5bbdb91d3ca9abda92e865959f1fd37176465e9544c7fdc2b90d104165f351 b4e6b18dc58a8e3f816e1cf002bd56ebd2a57e6cd5a07cfe7249e2bf6581181a 46fccdd35e89ce00ac2fa6d96a3dda49c6d12f79713ced1bb178e133e40f3371 41ac4b7750531358d319a2cd01386c51f8893477b73c6fd14b5b515f3dcd47aa b0ae06f1080e180e4978d0817f0a8fb2d89da4a50ec6783a2bea21253585f3f8 3b1822a27384222e63895a58d69f8897a71cf6d597e3b8b80c2b972396a5df022e1bb3c7dbd98b712b8b30cb762aa70c069cda95c2f3682e2cd59db5b192760657aea3b91d3eacf4d78701c06f941cabaeabd4eb2e24c91439b5e18e7fabca0c019134d5e4d6177258c6a36e6553326d29a74663652316ab09e0e9dc262c7a092f357e660bfda8b12e4cde76080fe3d85011612aaa81c3fda31ee1c0386cb90707d5fd1b9daad5afd8068cd500e359316b3997b462302778e3e7f4433dc22a0156dc4f28d42f514923601d827570f573bd96817883472efe492fa609ac7e0c03496f6301fd5a1b4fe3aa0f8f1c481caee57a260c155728dec7b6ea98065f8b0b904375ac6029657564c1be748e68e074a914deddfa6530b08fc6c2470516e100c98303cb7dd84bcbb77e5340fbd7921333ecb5f35b7469661ff54c5f4868f1027735508c3fc1f7a667d0a09a2ffee22fc0483b5dce38551514e4364975ef7101f5f32d4693ba7d9306d62069e24e50cbe935a4e0032560fd1ac1229976aade0a8eb7628fc311048035e26786780ae2419f1a7cd80bf6b383fdbf77db7410150bdc58cc90404777afbcf413660ba2965fd0df6f6c44bb24d359dfefe93d541104dd9fd4ac8148976a0634eab59be3a4d81bf8e7a3cda089edf7782b730b5b2504360abb1eb7b28a4fab558dcf36c84b89f9ea3b57fe046262da80c838c3ad2a05800215f367a68593d6cf770f8b9feef93058b7230cbd5b4ca19e911ef2f7ef01b9a0701f7c64fa6e116aee808386a6b92e2c390f153782d3e337d9f07c44820f true -check_ring_signature 585c0b4bdefdf0912d799b304112f4b5de480076a086bf6e89f7c156b61b0f4c 7e78677790a49fd49fe65cc337266c094605135be3d46e9a5a72cc220a847edc 4 9e7758093936d9bb2874114a734c5bc9bd5a94f1efad78654d5378e6a80748a6 861d4971540a79f09431aa79d8aa7f7cdeb1a34393813a60d0452f7ad9be7b0b 2187b8e2e58a90e064008822e2141abd534f54f23d0f35ffd1ec530e23828251 8177309ab9af283a77500678882e4592f21190884184ee2534e49787677618c8 ea9a30efe9c9da0c7614930e7d39608cdb9b9432cb77a30349fc05133097b2751eae4e09903da18ead3e88e1fd1789dc6096fcac6a59fef9604e7880b6e54c02efaeb5731ab68a5260bd3dc02033ff5bb9a9543726963f807db0bda42d93860975436ef97bf25a48f00d02c4f834d46463214cdd50eac331404994e35e64eb04d775eb7efdf14105c7d5c8bf464f5293e3306af957a16786c7988e979d1bba0ec5cee512b7575a404eaae8f5e5567191d5f76c63e89d1af7c1d8fe5ff678aa0b76700c66bba6f32640222b8e9e4772c68b431a54c08236ac3408caa69f575309488adb3100b7e35799eda8ce0ce8f7401ad57ee48a5186f0a5afe2e5762d9a0d false -check_ring_signature c947233fd91096b791af86485a53c7ca45fffae6b5ad70f0ab7f109f2728fccb 65925aba2b4a14b067f43c3deb161afa64164c625e3d66dccbfae479fd6b0bd0 2 f57484215841e38b3a326a154073cd910586683c2b18655d3ef6e366f3914ac4 3c362c3c5ecf59ec6aab5af2cfc705d59f1dd4e280b37a2276075b432d46bfe3 ae6f0de3bd43f26f4b9ee468c3a6e801e3aa24eadee29d2a52bc2bf5e2f9e3066645ef9e31a5a756b5fd66082872be78c9c3e861e52019fc2178a2fa2caa2703dd1e64f71ac3cfe18c68546a234fbec427dfeb72b95b46959be3bf57ff95dd0b4d41df8d6da682fbf774818d9980ea208404b693e4d02697f0cf453f3da61c0e false -check_ring_signature d0bddaa35808822f4fbafb7c567434e588f62dc6390ab7ace611da045fac4644 09c12b6ad739746e7ad4dd1bc217aa1fc88fe6163d324efd607c9aa6e6837e5a 86 8c1a5dfb44f10966b13ff84257bc4ee0f12033d77d72f93c857cc1d63a013fd2 c8afaa47c62dd0ba081bd6f2d00832b07d1b182b956460d6b7578bb310a50f38 e3455ef855b174b9c4e2e15df78ecaaf81c02d56a4b3f24aebd4c0db128b883e 59aea3d520f7ebad3d2f625731fa807edce9bac1840904e66dbff5132655577d 0756e405e30374739413a4d1353d0c67ccb0aeea4c2b6340c97811d50e38dea0 3c61de250a9ccb33b0e7372193caad21a3ef5392114fbd87f56d1248947e87c7 1ed134b253849e8a1253df367b18327147beacdb5b5942497451856df1885cf5 ccf7070f69667b1023fb51488c5ff52e27a4da4c27f36fac1c958d1f5f27f4cc 46b65223049ec9f93ee254dea9ce9ac15457eda2b27be9cfda2cdd9656619cbd 6419eaba3fd3c966f93a401b85b014894d931afa353d22d8e6eab228f5d340e2 a5de7f6fc59494bf96519b70a8d8d12cc05cb5fa42bdfe20a6a82b48b6226c43 600f7ff9e9d2098583fe3f4600bb1bc8e09c2e62db968e1f7f6c35875022339c fc612659cfe8f4213d40f5ad8cc38ae887615271b446497c50ee64d80de019a4 ced26f742a70d664adff95f0af7e06ba1cf2b2d3aeb359c85f50f636eb6b95a2 e0b23dbd64a24f41f6027e8eda023e2d8c2f5db381d9649365b7114a9c59ad27 018ee405ba71ffc46461bea400d5f3996b99fdc045b2308cfef6945dda7cf41e 5ae91ec4689b99cc7ceda0d412c9d0ccfd904541a03aabe803210cae72850ae6 01cbd63eb497ecc6bc5785385d304ac5dc16b859852997aa551938d5c8656994 d71d143a4a2f6b53bd393e33fff83cface13b64a2d42a771c0f3df890cf72337 6e9e8374344601843f73d6532d55789182855185fe6a0672ba8c9b8c18d924aa ae5945dce1ae9dfe9ac3c86941aeebd4d49bf5ad82ac09ab0ede68aad29d412c 77cb51d024da8fcdaca19a2621f177972378504f42e36a50d0df397864c060df 6c73584abaf2b1ad84e5af9a6a4c6550c9b4766d6e4289c3d08148236c103762 13fcba79ec1e4e77a0dc05e3c9abaf46fa77cb589f047b743006e14cd34c16b4 2e76373a6276ae4ec57075ba74aa111836054e3c1720f0fc53a5b5b84aae03b4 585ab842089b6d8384154b2c68628e6c9c36f9e84913486739961e014f56339e fa386dfc3fa1a2d07c5590da0a2e95a9a88ffc934508cde9fb11a5da05c7b8c9 fac39379aaf3ebc6d3a02f3721da29fcc3e7923ed202046ea31c8af63273ed3c 982d32845e3153dbfb28ce9f8a500117c440bbe08e723e99c6d32b051e45f6ff bab8dfe86f0eb901217d0f2683badd04e224157c19cd4124f6d3f88d03569645 d113613b9075ee0354226e20efb1b363f54af1363e03c37aeb41f2c8f013f04d f9045447c72a70cd4b19fffa2e2ad92a3c0540855a25eec8dd725ff87c7567b4 24fcb60f809596313c4fd9940af8c9ab02b8a24061ae7f8737a122afb86f1b26 ab389d0bd514d9caf734dbe0464262c54524664bd1f1f5c1734491fd48f2edbb 37f55cd4336ed3980091b62914c7977377b47591b9a9fe4ff1fdcdcc8fe1b1a2 87e804d8ac805a430199d045047b7f83d3ca9f445008edbdec433e45a28297d2 8530d37a2b5093c04f9c2469f9a37262be8fe51538eb5a05cdbcddde7c945f80 16791d341b3516d0469c1849246ee5669093bcccd365570ce89994bcbcc85f90 ac2a71a67e129d19207e0bcde33e5f4f8c3f682db5615f8770af2bae2d64d870 9d0ceb242d758d316ac01210bf31fbd17def8f5dbfe861eea17ccd385260faa3 8f62daf2fb45d745aa4fd2392eb29c939bef52d4132a13d0a9c66d7cdc2731cb 1e14b2f9702145205788736015f8a08a32dfb57cae2d2e27a55745fd06290c29 225161ee8aad027fc57fd4ea105292501895a6304da026a648947ab89860ca6b 83185ba652d46ff96c7e23240286dc120c1fda5ee1474de6ca819e17c60da14e 9d5e48995b3bd308c07b032a8a459f44be94cb8ec0ca97a09d8d38166f06f84b 54ba06d206896f910693feb5f401a8706e6b4186f9a6f07f616cb61945819e0c bcc047bfbcdc91a4ac31b81e7f97c76eb6c20e36cc65b1e7256a2ca3bde76764 13c7bbced42ed28366994029602403933d5dfee86bf9a58194707caca086e543 66eb6092e807b9f5153f389c2375a2140077954ea7c6911153421d87ac7214d9 476e046d5ab9292fa87ed301dd0246c7250aac5b9af68e7914ae39d952b73c5e dcb9476386ef5571efe4b29f5f2215e57c3e85265f5d5b264c5dc4fdd4b829ce 787014abb8e70440a23fd19475a483ecfca1def2b54f4ac35b0feb2b3c05bcce de6413ca0f77f2dd764298005e75878221ad0b5298881c9b0ba5f76fb28cab3a 959988ed8a498d83345d7a874fa9b31fffc0aab09cd05ce2535da3bbf1741d2a 46e08436743f7155bc0edaf6dc63f7b747bbd04f579e7ba152276407a8690890 1179067c0d32b1f636381838ebd9cd7f9109b785d33f9b5e3557fe008fc3dfed 0795882aaeb2e00f25517c654abaaf99550113c7455176a09d1f5baf5e50687c a76c0bc870cbabd958f3c784ac4f0d15e714c0586472313982999fda65faa73a 69bd041759b8a74fa299f1984c6c6719fac770c17292bb97333537dc7b2aa7d5 26d2075fe33541d77d8548d857d793b21c7c0d5d60991a0e1b58c0bba645ed8e 1a75013dc2bd6c38833195bc8f321c561eeec36f5d442aa136694d7c24b6c620 8048f087fc68dd02905b790843c499346350ffd2b53ef16882f0a038216a820e 3599bee562636866683d53b21c7c7d2d3a89f01677103a285f1b1fb1e1cb93da d27b5712cfdf8f66cd6f7736e6e729ee80470382987ebc5cc31c1832f05bdcce 07be3222fd858d416e268fdd7215cc5d7f60ecaf5c9ef86ed441803b074fe60a 4d4656ea0e079f02ef199a21c5e26209369f0c00cab7996c0b2573dfe94ca046 5cd6b14beef548902a485cd18ac1dacccc83fef696ab36a807d2fe9fb5db5d55 c9cfd738089e569abc1a0b19282e269224ff4b0e6736882e95a9b9ba5efcea9e bd03defcfabedf9326c8909febf6133bcbefff5f8097b0222e4007e97f53141b 90833b2cb4327975b4eb7369a8a894b76f29fc248097010393937fd869d3dc4b c023a09b4b75885e62924151ebe4d6df2f71be1f7d997bbd9b97e83722c579b3 d0ac9047feb39b2d7433b6ea8db5275c5385742aeaeefa31ad7ab04e2d2ace8c 665653bc78875380fb3b15787f51db737b8a374c3758c941316a007f595f2426 7536f35ef3528b445241ee59541543c7d1d7699db1c83861d317cb243a0ac3ae d39d2224e6ed373bd303d6a0d6560127fa1fc2edf1c8ac1c5bd8c5363ad7eb0d dd70dfd5d3ff785bdc562ad4742a22053cf52b436d9466f1b20d26532182f373 e114c393efedd9d6e7f9107192f9befcc28f5cc33315715e4344b4b1655ddda6 0b317f39d0b86d1de547a4de92e945e0b5ae60d5da4eef3fca8cb0a9faa9e931 d6c5f4862598d46f1e2c349ec010c2fea74b8e3d538e7b108baf3e276436bf1b 09cb71998e14b7880c6271f0ab3f135beb31a0e5a38e874d97a2220acada3ab7 28a369fcfd7395357efb66fdf3f3cd01aaa039bb5827e254f9cad9b790167a2a ccf8a6b91c3469a0ea8c350d980d241428dd872a990f933004638580f34f4d02 a9871b98e1990fb220db2d1b99df295f7efadb9940a85e4f16099cd0d342abde 7027e55fc398484937d87d44045dec5d29475cf270ad880da6290bfc89af38d6 b597f47f856b4438ed2b1acb80b302aae7f2eeb548d4fb1b628eb20d7f433e62 ab32b6cd9e92b2677513a719bb856393e17b87ec0ff4bcded6f1377eb1141c9a a619f77a5da205fcf8aa50cf18e6bfa0fc63698be8bdb56c2121cdd2a2685b027bdde29acdd229c86bfc4681d2ad177fe6c1cd00b8f3d58f9cf17d25d8e5680cee161231aba30511a74be68c28e71f3eb07f911ec7d5b05023209a54e129b60e2e8003e1dcb953a246d6abc8b7cab226c84fe56d414ce78997739f121b7da3020f05ca1f747dd320da1399d691d609e41fe3475e466d498f4565686ae1c8e80c79b7ca7673f239fc324d5f50fa65cad2538778a6716055d28acceb5dbf55bc066203b52b10e998b7fcaccaec7b74f42cfe01e9a13c36b3e5a5c5dcf301c8eb03b8688098822905fc6f63785c19c5a81e414713e17430c401f79959bd72f6a2053a8c533c15a1e5d6afb2eac90706d722739d56b6a5abe2020b7be95847e22609a730762843c5404d716852c14e1e093200628b7a60718eba33f7bb1532d187071f0937830aff5a6c62269fea050c2e4fbd9f4d36ad5fb63b27320664a8174e0d281fcff686ca2006ee2dbfab43b153d94dafa180627d0ae1a1a134d7f610ea0d55d0454b63815f233e018151261326342bb7d8f1f1cc772ae7a31ef0cb0daf0138b818109cbcbb7787694623f6b937caca6dee6a92ccb91cc03614fb3ef2af00364feb2b05bd6cb1b1ea6125fdf4fcddb96d9293abc7040693b609a5c28d2304c589a614742dff6fd4d31e348302fce67787f7bb69059b6504f7b19eb73d010a39b98a638960981beeda0a3f6487047e4acf55bcb2f51ee7f2627a944a46230410b9d98753ba691ade530aeb4a85c605837dc9eb616fb0e9e26ecde204c5e00d77586a2ea7c2f8b051e9d67183f72ea5a543bceda5ebaf5f01a6e1faecc9940d35daea1749b0091811b8d6ef68cd02d622784877260c68d22c831e35c0e3f0053d17eac0635d3b020189d30dec4a35de9da231f4465cbc87314d325e537db604b008f2c16a9c990c0dc3042985fb14487d163c6f9c5603938dc622df78f8e3086bbe7b91d581f018374d936692758f187f7821fe612e3ab08ee3476991ac4c0e6bde70f725b342f620912bda2833a5363f602d21568078c2d6dba86fa05fd90eeacecd49a7b6694b6cc6befaa4c4a340fd7e19f0a9c87fa492ab203e778bf5000eeb36058ed6af8c3a1a1dd3f86372c6c2a05d372d44ae83b1937f7d29515d01c3a18cbb6b11b9d1574c0aac2398369ea05951ae56a0efe877d22bdec9299e095e040d6f72d334cb64f7d1c2e9625f1dc15dbe31cdb56d666112ba2570cf0509e9350bb43fee140c785464fba4367ad8634776c78bc94c10469284eee1a1460a0b989350ac947d08b835e780d12b5be91d7a5228dc8680295fa4cc5d3f41b1050c26020aa551bf21eae2d3ec301a1fc819f2cd5b44775a1de35b6bfa74a97a0cb72783f3a76a42076f3fdc3e7df6ef5914e5f5e6a7a3dd5baa593f04f8f00102635062a619e1ebcb61501b6c01b520b70a965d0f511ca4dfdaa794923b297f06ae58da9c0784edaae296161bde13d174b1dd2a20d0d2f8f29f9dcf8b1692e8026639a13c06e0d19240ec1dd782673f87db83ce9f14f72997e11a2d377dae230b7fcb90755d8e8411ea2274cc1e766c45535e59ce7dbccad8d2d7c4a70490b70990be4744897972dbd59b72791666a091b773feeee4277fc967b63fde54b2950306a38b3eb5bcff1c677742208cdc7f272e6c9ffd498f66e626eb2ff3b7fb890e77bd46127e1f4f4880b5cd7da813d423f32f52fc97f9ed328b62eb273361ab0feaaf4ba771ee556c6fda3ea742573fbb11af176162f932ef2b92b29a7c86c70c2eb3bda8b2f0218811f9aacc87cdb9839c749b30b35d342926bab4a067b7d800ba202e5f167f6c4cbbd505a5d1e73024a22692a59ecc777ea2d61b43b976f80b7fe5b066c638f1270622062b64010246dd82e1467cb36ea73975158b6ecd010743b51497fb1bff70018d429088239a408409ac39aaef8c585deab099b93f500366efa5122130cbcd718858834c10d0ec706fc7a4da6b553a027014d7be118803d91d8fefea8b12ede8254c08cd5f3b1ccb3e732c949054abbe608e65d440ef0994811aeb4aedd0bab38bf02ce6e52c1c40580eeb3cda0a2c126a5d03ee849b083272b83f525898d56321ee55a816e2b8f0b91f2417c8b60e47cd63e8c5feaf099b53c43c63beeed2d37656cd944fe2f84428b7a97aab24930873e7f927bd3e0b9fee18771b2862ffafc962062211a7497505f205d4ef420e016912576bf2fc09067400814ca96235b8a2494699a3bbdf67721553fe1cfd58c064360b93ac7a0290e0919dc84b686d7a0c661f4f1b67a099f1ad47009ef8edf90e667d0ead960a93e7a4761880c076498ac02ffe02dad3209144fd604ae7d4ccb3b83a6865b60a04b2f305e8e29280df5cf864d0dc3c4bdd7f3b6b5a39c6b7df0fabc9e523300c0236bffc047df266693536cd65d277171c4507af1634d89fc52bf4788527da0ddb39928266ae248868af6daba6bed9120c5cb16f3f9d0ba2bf2563b9d1a9e00867c41c936ac2c5c287af35b8f36a5075a4b725be8fc7af5e045efb23bf28ed06f9140513fc0ac91114b122c6052baa8fcb886628dab5ff53040f31a73b92cf0903e4138f088feee82a2dba21990ca91c0978b7022c19804a6400696cb844550f3bfc77d50a984a967652e79458c0de7400760c2f612df977959060812682620ecf1228c637a076c2ba009526e366f5a3b99c1d71fafcad4861166bd794bbb8011f650da1d5a941b1536fda792c92454985970ae1a17328c7183b03f72ae93103c25d56d4feb8e091802d8cd83beb10b8bba40dab28d4ecd3d6e417e02b6b5200273492cc6f70ad6d478c4e1048007d0cd565c9a4f35bc5981118e5448de4cd048e5c3a16a01d6a351958f245a9189307d4ea3d9cd81103ad17a3288b453a5f0da014552a2eddc4161edd71f41fd268143545e839e13c52b8cd7a0a503f98fd0ad4aec0a5ba1aaa2c475ed5ca0f539d5e471b6c47740f4f6066902a1ed624d30fa4e551bf58fce902b560c1efdadea8e238ec121eb1a1446db27ba86e9298e1073b1c3f2cad06ed9e8874cc7ef894cea0c86cc9948191400a3a9d774346825001525e6fe281bf8b236b81ac9cc963411115b3cc51274098bd61ac5099d111130d9daa488018c7c3fb61c0b838887e717b39d53dbb88e2793de2e35a94c58a1d0cd61ce7f10a748d17f9e22e4516229cea0da73925fa0dc1c1e9e285cc51762e0d3fcaf4c3db741ccd346911d51b554c295ef5261f2e354af361fa751b50f184097243d9191e8b276b6caec2649b07a05ea631c167113525e67b33185827885d0bc2614efe7c2ddd20993a26927cd621236e23effb4e39811ae676a93201b34f0ba81f364daa3220a3fe05e43a7bd08f9d1609e1a706bd25e0c16cbad62f6771079f5895765fb7fe0af8d96c7cc6852bb4ed2ab8ffe8d94765082632d159bfef0328aa8b7626ace94cffc707ec2824a3fd10cd29bb4c94fdbb2d01af6d7404a90ea39c553df9ac8b6507b9b3931a89bc06d4c51e13f631ae0ef7a178149e3e0302021bd520de3ad1e3c06faf01313e79e27d9fe3565d9e1acd27943187654fd80346460ecd7bdb8d3465bfdeb3b4ef13db3c02ed53c11c24c0c7805640f807cc0b4233280232d7d97a91384d814f01c6880ba7ce8407333908ae901921da07b80e35e9927a70deaeac4dd1b1c85c9fd39d19b8ba6809285fec14a03af03de7f805303a8d3c4aeee88e36518fbc945aad566ca24c00dcae7cf7f6b378226530f90c388621d8677d3eb779a7626c6b3ef5c4f7c66faab682f8943721b4e2feac420f95fc1f14891834b36e29de73443bcc97062483599dda31e161c38f3ffe8e380fbe044fcc5c239b0a10250aa5cbea03e259310266dff9ec8766cc6359f442ed0f6ddf8af4543781482e67ea917bac65cba5f3a96e0ca7f219b9e54c04b65875058d0a0960849c8a72c468f3b2d26c3e22584ca81baa332bb6ac120d1c70a49b0bb23855c1c4412b5537244c14bdc5b3e1a12a1eba051e0b9dded565eded91cf08a4b23c57da4f6c534ce3d6575c0e14926677813dfbcfa022e8892bf959c2bb0379113df80ddfe7c45d093eb4adb742c6094de39379f3df3994786a42acfb1303181c4534e28fa9feb0950d0aa2007c05cc5bc254e7287ae1fa6e46b582a94603461b3593dc6f9bfb3db8cfd4fe454117457c4a13258c87688708ce390eeff804a4a8f62a1812537ede9e8cbf4a938b26d8bfe35701f652af4d2b933d98699e099be109ab51bfbeeecde8433285c4b7a402a5c497793be787a4a894bdc3e98901ef0b9186543efd9000f1b9d4b70e15b49badab25682c3b8375cded3d7c164f025c7ab8f99d10e5ee46200b1f723b29cf6b6be92944c9e1a36c6fa81b0a4275091e15651682609f9935b00a0e6087c63d674d5d687b1c1187544536e1bcbd7c0134485627f59484755091b14781821d7e21db12f2de65402256d0336cdc9b93047e6ea923094c86babfb09cb059e1bb04548fecf4fce57dcd664fa499a70a3b0d9417cf50631ab88546b537ba8f43d9b71cbe2118009660bad8fd0e1c43a74303dac0cb4f535f08f7682da61224131da3150f01993f571375f0c24f8b9e9f710c19c2a83d0e09831a22b28f4ce3150b20723524444054fb2c264bff7c8f63d90c49566dba1630b9bdd9b1001da872ecfae1f843b63263e9331b365bb5bc23280ec0f00b4012f3b0982b8252845953c2d6f3da382d3fab108d2cdc47387336d600a53c8faa462c5151154b80d19bb34e801e485316ee580118c0ba355cd20aae0133a295d8d67e63ccaadaedbc1f1a19387cd1f84aa55d502a9205e1f1adc6110f510b03370187b366fa1f1d5f32a6630bd4aaf7d69890693dd5d47accd80789033dfcd3f56cd533d055ed4a6f24c1da2c2012fb3727634e8d2363eaf50c8a27086e4e488a3a134b4e672c38e99f97460f23cc00eb1b77b7b92e33c24ab65d3c0e1d1c591842ec9c8635315223136d2dac91c3130cbc83886b6f600a548680480853ee641d74c212b9c47af19729673e21fba35200b005164cff512738822df40f95594b62df711ce36f36e6f96ddcc989a1d67d58e11e00ac7dd814fc86e6f506b22d7ef619bd3799cb388e393aa800a6e32ef48a7c645f692c18f89a79deff0f40a2e5677981d1a800d1614b84a0c19c55b8833be3bcd7e8b3731ce5180c250a8fc13ba0f9bd5346e48d63ac59a70eced1bc1866c43b0d7a4a1d7934e61438087cb72e41903b4bbb65317669f3895c2372874bc8c8db01659d4ce2054e24d6038f9bfcad8129e9cbf879255a7d25b608a84241baf5621172d73e0fb2ebd1480248ca6264be5642a3e5870836bfeb18a13c8c2faab20b946d56901214bc38bb060faf006341b01faadc051104b314c9f17d8304074afe436d5f24d78f5d4f64083be460901af1ffc3bf446e9211bea3ad74cafeceb1d203b276a4d9984fbe85075541bce66b68ce97829d7640c977725eedb809c44ef4bb97980b5b71ef92ed0eb452cdf224d8ee3b77c958ab8c9cd2b7c3cfff767293a761d38d15b37978a10e72e9d2cd195587df5cd3742051a418f1e57eb9b5553525ad09118b8e3037ae0cbeb83081923eaa961ad67dd4744b6667822bbd900262baeef130fcca90bac90dc81c336243263edc9ec04d7da3db6e0e092abca4a899891ae901c3d75d82df081e7ae84c33de342a7d7fbab985b680f50f06b654de0d3c2ae0381ab8160ba20848ee320ac973cdd5c2d9ec75f0901162388c6795b8e853964f90c4996eb0f6054503374880838b3812ae41a9c286ba2740f31c382974bca1ec549f6acde2d3083250682212bfa226a46f145469d6f595809ed461b9ab60fdf8fb86fa0f95e50059e358f350b94719698c0f57c4175d513f5c9d273d8366698190183203b16a0acd356735651d21419c78bccf3bd7f1036ffc838f0d7fbe99452dced51367840709f65433f6e5fbf57696196f20e056dcb6dbd18ae0cc5e96b69c3205a7cfa20f7fe30181dc9dddd3252c0bc0d826ced2b97695fb66336ff0a55863870f22e307c1a0c9a95e97867cd6f7d563edf0401e4707f19197fdacd7a86cd5b6d5099d02981b8111552aaf9476c54b341e70415fd180c5f5aa4569d98909db0e4ef8dd00b7c31c48a351897c3c5a47949d1dfc564341878fe72eed64fbfdd87f249d0f006cd25e5e766f1a1f631a08d803d788a2084b9a0e6bd6486af5cd86501c145d0ef4356c256db0bf4cac5c1946d8fdbd73791885b313602ddb06b387067121f70ff65e8c90fbe6d9930aa106af7512222001771707979ec3df00b3cb00435c080c3032c94ccbbb47ab4b4bfcf8443dea2e25234ec4faa3eb3fabb51e6f61e4c301c7f2af814d425a4a3fc4ae4853d32da05c61af2d4bd81de110bb3b0e45161f0a5a53726b020e68094e1109e325a55a8bf47cd6277382ce4841c42177c8cf1a09036b4b7d95e2baffb0a7de38d595de61398dec20247548886a7b8574d5f15202f2a7e843bed9ed9037bacf6b4f634ad807e3c13b89b83a7b575ee339f6f23c08c5cf41ea4cd67239297bab87d1e032e81f1881abebf6a70703c65e0f8cacd00f0e0e91a0f69c8775d670f51c7b6c3d0d726c4f4133546063d31cc44d4fa2eb01d5e874820e8040b3c0af667e8ba24eabf75289d28d946324cbe8f1f6e65f7e0a53cdcdfe04aee9bcf3786e6706eaeddbd8e1158eadc33d825848365824122c0c8c90eb7379f28a9d06df1a259387f211931cb67c00e993f0a88b9b7565aef204781fcc2a3053d0d6da953131729edd81e2584aa4e013e80bbf78429e54fefd032ad8e9b612a149a37bbeab1a9c6143c6caccffb9dbb6f2ced3d0ba719677b302d3a7415c713fd6db2769813eb8aead67360c80dd6c23517de49f5f298952be0005f447774fad5d796d2ad2866a58326b3243042440303f837ac224223ffbfb010eae4054e3b32157cb39290dcd730f7794a2fecce7e0d4aa10fc187e957a0e03036aa4b8a8e339abe1a1b93a9e7b4163a48e5642426c3dccd0d64c706528700b1a03baaf1919eb5f9f8c05e1fc35e3faf685f35d6a6483f7e769c8240f3fb30e4e8f0e8208a70d0d82ca3e84095e10fab1ecaa8061b9fb38d1eec9dabf0c5908a2015bcfb3b0923c83167d0c3ca2b56183a925ab26e7eaeb053f5443c8d33c0df4b4c50250981e060f1a0897d2644b16bf353b66c76eb8111874dbc05896800bd2a973d36fb9e5f852b460696af4a20ccae5efc44d8ebf8c356a55e1f842d80aa3ded01af8c63a5dba050ad47dca2012c80532d8b2f85589d8fd4e631b805404980f243e162df05ff4085dc855ffeb412245846d3c4e78a19f00423d8685bf0bdd8ddf07b16d324e8d4cec4b53599dde948479b98f40cfefadc40fdb7096d00e68cbc0280ec0f811efc8b42e656c7af2e0d129edffa5e28f8fc3aa415e1393014aefef61dae8f739cbad155ceca6d5f5d3976f8f9006e7c9a15c8f981b47e60952ef29f93e3ce6466b18406cd5e5f2d0e22cfc93c555cf2c6b2eeb0cca8204000536010cbc255d23cb762fd8e836066c2f611876644d75c095973391db8440023dc215e3c9679820e5779fdae06a8974ca0e95447e8eababc9975b92298d0f0662317fb6ec22ce1d5a2428f0e4c8a21c3d195ddce5954e3f07212e0ab4c7520e48a1e24dc439e780911bc5c8c88d7a492917d8561aa7eecefaf2c3ec9501000c true -check_ring_signature 450bd461774a2d2dafd59ce78c00bf797e6aadfb8e0a50e883720eeba4c7965d 8bcbfa4064ffa636d7e6ffde9e4033f0d2318b3964fa91f1a6e0b8b3ec713ce0 3 b6a1cc122e1df722a03a2cb917e66b96c1fdbf4d3893f14f9df63c4b41f01ce6 c43188859b76b4ab3ca277588ff27f9948bfb387ebe33d41f77dac63c6db9988 ae07139aaaad29b9220b036e781a4554a51c72f953da754fcd3d453b895fa0d3 4ec7e778ec9a3d49d77e2ea87b1d8b6122c04ca2b6a69371a5d02bc858cb8a02ab25ebf6f958027ea1e9d0ffc11e78f18178c7d5e7dd7b1d73f73ecd7219790195be16a44f442a73a7e320b96524d24d6037767e21bf8ca4ff710f02f65c070a4264f8abb50d681bbda76b7dae0e5dfffe3baa54527044ec3c201f2db3076000bd03ad768e99f650f09f901800f0f163a4edfaeafcabea009b69a3960972a207127c6724885066e3ecfb07e35a1298ed8d00a90d82fd026c30e782fbdd77f703 true -check_ring_signature a5587a1ce8c2d0a202c84bc789e52bccb52425f15c4bbf70c8332cd2443242ac 9c4865ae3ce57ae740f2478e93f4a198926b41b9d75feeb3ffda37b8c4c93c74 1 35ed3d5c2070d05da2627b97347ddb942ee6e5ebc0070587bf6ef733e0eb0172 9bd70456fe554d55767755aa837d8144d5869390ebf5ac0c1dd55f5f81892b0e609e147ab6b47ef0f7996feb33ffa7693f6682e6bcc2343432390e551d62890c false -check_ring_signature 67106af702b6d5be0ab9987233a1635996da23299e762cb305a8b713fb39ede4 e3218b463365474a20072ad6525709f4b44c2d4593d00c892d37218ec8a0151c 1 563e935103d8d94959d63783a33529e018378085924c615d32cce6119327ac0d 85e976cc678b7b46bba79b6595a7fa652fecdbda0460d800758c829ec3547606109774b64129b562f26904155853b6007f5809560fe6c765a7078fa5a2e84802 true -check_ring_signature 245329a6e8baf1a73c10c61281fd90fcfa1db3f60d502bd1bbdd2fce7ef548a1 85a34cb71eeb128ba08e9240d7fce32b4aa7a3ac13fd0590a831b3eecb9e6112 1 a8878644b686d2f75223656f1c9b349b620eaab93d1eefc57b2e849ac3e436c7 fc9918a8fa31b6e71fba49c4587bc6ff735638059ca4077085a94da3550e0704cf4fec70ebdc2ce47c87b67e314cf89c797d38aa27962cca9171b689cc320574 false -check_ring_signature e53abe6b382aa3e3ff8c2412234a2b9875de906535e8c316576d0afa01a75bfc fe73a9dc3389da28cdc073d27c22585dd1d45576d8142a0611132bb43ae420a6 60 eebe59ed6a653765e0d0f17af2d974135bde55677167c03f7cc702d7d3ac871a c5191b60fa7b74eb7b055dd2d2133bcc42b2956cd62e1fa0c0ec142009c90a36 8f69256e6bbf1098e4f52d2e772cf2dcab8ac0661f764ce4081dfa1b9d85a5cb 2f550a67cec2744922e4c77c98e7c123fe9baed85b9dbcf799d2b07390149f62 f9217d094c80ef4ced1eb321e6b41b367db154917315574d10784c26648a4ce2 cedb988518ebdebac1642fcdf323c94d2b7429382cabfcff2adc242ba15909f1 611da9074af86f2f6ec85e8723d740ce0d3ad15643be96ed728d7cc923dde8a9 4947a4fe1a6be574e9182e404d3f1d7400355e6e63cedfeb09b276d19f1d5974 5f8846a3b6ae502e370053d61179dc04ee089f687b81760d9c39bf5a92b42577 65872060012225682f3a2f8b1e416180e9815574e6fb69ad2fae42591d4f4324 1d7a6e2abb722ad47dfbd37473dd4b2e1c491817961588de0e4533f3e2fa9d8b f623e065bfccfd68ab6bdf01c6e8a0d207cce1edf1b6bb1ebe60f6ecc7574ae4 d8f2c6883ac18c5f06cf001dc8e17050542605d4c55efc01a11bf06420ad17ee 2e3fc3b172074b8c7a6be082ed5e2c317b63794a0295b79ddf074e61f6804516 ed314c38b20cd8e69eb2883020fc27d689cd634a4d13cad7785eed9bb44a9e18 35372d36511a644733b1bd045bfcf6c12aa00c45b68a726d76b42e90de918918 b6e057d02e114e1eb75add16464929051d10310284f19c61aaf8b549e0cdb423 15b52863af69542bd67776dc00f3f5e1fc2a8bba93340f313cc86da9a0f00852 c254203893be637c25c396961b3b7dfa9a7239066509dd0e2ada8bd2694377c4 222ba493bc771286e5f1e300ff590034cf84404a0fdac40466e0a70687030dce bec6675b315b4e322ea1c9e1f6633b3291535c9c4a013366968f687fee371fbe fa06b1219cf7b494e4fb43082684e45498db06fc7c29600ca3a9dbcb20d8b390 5adf7a5a78c3972fc991ccc2ca5476d755539324e65ed2b4612ddecc5255366f 3c6703c06f2cc9c03e01a393a8d6081f364e2faae7a6c761a79fa4ea1f973315 8bea1495ea4d7cffbfe429c19aeefa8f9a067ce64aa4411b6db128a475a39b4a 4825cb3fa85c2f73d9fa2e8610714d1af8218250e73adc06f0e2e1db49a6259e be2ff1115459d87f95efd27490cf02bad55bac68e70dded56e6b3dde0342094f 1083758a1d5f93fe2d485414267f4051bc924822b57e8bb85e618237b01062f9 0cae60a0dc113995fa8b23ad99f4b1b2785651fab8a6bf7268f35e0eb5612f76 3a7d7411ddee32b9f4b8e976b61c7e3b39c974c815ae3d4841a4ce8966a8b568 37bb89960af84f2acdd1cee584fecb0230727de5a159d6cda232a468e018c251 b61eb0bf7d119adcf089f2214639915347702de8cf07b478e20550d020744662 391fa9f1a75056735bb21f6fe4c614167c9e8f9493e2ca11cf29d3a69f90f760 905c8f2bf6a0e2ce880efe9d43e7a1d465384192a4c4dcc8bb3a6fe9d08b877c ebf91adfd0a8ab9ff81f8e002283ed5aa8aa19bbed5e2331bdb252d5ac614a1d 3b3063f7143c6e6c17abb0505fa4b55ced2d35eb19e15df820cc4f6b97ad32df 88a5fd68199023ff615b906c69f25317a02393935c53d573d4e5bd63947ad24a 31b09c53397e6efe82870771a5bedb9b7b9db54f1899d5e689eafad90b2a1760 9de31ea49e446fa2fe64e1bcbb0c205ed1b5e3e294e1b4ee4b46a32f91bc4e23 3b260079a62abf5e4f8d9354cf183820b802eac919363b8cf831b91f5f098c90 576f61cd3885dacfe80b8490b219918b5b79b745cb9b8edcd5e316db15a620e2 d983817a8fb9a6eb3e88975d821939cee7f233b75ffe9f73efc86069d0f51413 7f4adde7fca87960c86f69d500b041467ab6f88cf4c8afaa5566366cd52e13b1 bb5cb46ea92c08ce99eaea0c38517c52ebcec8f2d461a1ed9460fc05a19c4409 620ddbf7b9668d29ccf16d3eef716e39dccf71f6bef11b8caee977adcbc23c9f 0fd3f908fb51985e652d9562a6b6ec0a9c7d4702851449260e7c7f8cb99c25b6 d4daca60013313c28e3852746b914e8551437ceadaa10b112f38899c4f84db2f 636519093069b6dbe0ca8f111c793d16a5fae017bd86aa3f9ec6ded80265ec31 adeaa361b9b4e4c3ee55e830172586dda96bc112a6e55114f18d1982fa1e2227 df835eeee42db50bc049c3c12b6b96e2ee2edb0a62213564085c792cbebc791e 2836f18e550cacc35d6879718d55aaaa23ca801ad7228f09850b3c28289904e3 ec8ddc800f7cccff6c3d6acaf93b0ebebd58daa42e927192b658c7d1aa6b93dd 8d1388195d0d480a8138cd3585199c7821d40025275e6e5eb4dfb2ff1f72fc2a e3275e3b4dee933832416043d57c6ff40738198b0d41218056995a3e8e0dd377 d1d936985153b5f71463706809aaa4df0514b136cd6b4461718d4ea8a9e56e12 c3f656be397fc80f6feaa2eff4ee4246d1e6f71798266572630bf8205e3b5c89 9e142ec43b4ad5280885ae359b6cb1b48a6ff038babee173c21a172ca9967164 449c7fcf3e15edee6816222626a1619aed39286638f0569c0e47ebaa5bb9579e 2289e09b8a235245c6f350f899a15c8ae792591cdce284f71a8dd69c69a68911 a9ae878e1eed06495d1e326d182ed4bb21db495baced36f73fc9f47900374e7f a0e52810c0f16a65e71d7432e35503a11e445eb405bf98bd4e825a87db6f130156721e07a68838b0a0eab7beec0eeb71ba768af5559538052f52f434beae9801e432115a077ac246b2f93fa52939882b7d7f0a77e17b3d92bb7b6e83bf023d0675258cbddac16fc0c3e019c4a7cd76d6e6865cabccad81d9e42a5d0c4d06720fa1d5cd5b10b0c3eb0e2b63bb5eceef849853021e5f51721f55817d08a0d1fe0c3a2241ffbf54831356e9c5461d93b8a64ab4ad29679162195234c5cf481c690ea20fe2793e230f4e9c238529056038b44ae8fd7cb7e2d87fcb33676b1694950dad7d2a5711213a8a1db7b2e2a120371d4b7e844a767953811369b5fba82dff0f4986c2287880e93823c6158a096464fde9a08fb6b88a7c50cbfed2ad718d7209869be4272500216e3aa3e8f7fdb54724fb1fec5180b0263a79c12a7b50c8e40b441ad29b019d47273625fd381bb0af8906ef95f61ae8f3ac505f0b3e2dff820544f2b71a634bfdece9e19b8eb92b6308863c756f7d92fabdbfdee254d7105008330bf3344e91b9badcefec54e3168de687bb252e89f8425f096c26f67479f8001db57ff146f76d908419b96417aabe4e0ff99e46120f78895ed6987f96ddfa054ff97cbeb10ee20168a29dbcee493c7aeeb9742cf0ea7aa15c5a43aef6936a01ec81434eab906c5b0c2b8643f297f610681ff830420d6e76b1710a7f5bae4a01c1cce14d4f2fbd32e7cdbf38f7251b8458c4b5690c0c9650b222a19ce291dc0dbef52a5c28075f83d60550c7fb6d30ae6e2c0d2e782dbdf68365e3e59aad04057246a5e371e7e878861ddfaf0e4dc2774c1c6153c80c6632f4e64e5ffcfb450c31f9b519bbfe723ab39ce7b31e2a105058fcb22c667b987facd96775041afd0128f762f0747321423180dd3780469b342dd9e4cbcaef8e28ed7dda12e3284b0b9c4a804d496620f63ac0d291344422a1db8751152d30e9ba043740877247ba02d8cedbf7b96c2bef37dd22274996315b063c8bdffd08bfdc6ed8f276118e1c0a6b5c7ad3ecf294c15b28c073b59807e490c23f416bb6722e56a0e8c9d0edad07dba6d96ec1d11e94371a87c6694a79237a166b17a0e90f2c0f37bf4ef17b9c03538dd10945b1612a93e8bbdab8b7e8c152806dc710a7866fd9d047c31f9ed50d59ed5db67b90d6f5e70ef2304f13430a75c12f549657207edebae476bf7fec0c5fd2b74699a800b902365f5dc9de33fd445c6ee27890b0c085adadc02bb35d0ed998076631759129ea4f401f8386a9e90836654d09ae6bd04f0d40650c8516078ea788faaec28c85ed1d1b1eb00928f2383c14244db0522ceaa5727aa27d5b0d554b6f3bfa686fc23a63e0ed3507134220111d8a908d8b71143354f01106bc00d5669713fadbc7ebf9642dbbf95536cb11f3ddf3ab4f986f838f5a439835be033bd0a54cd63f0fb271451ee234125243b4d088873cea7bc3601e548bd8c8c607db904eb14359024b4b1f1c6f8b6c90f81914706264a0a8257449b7a0d7e9c30acd92ac06a58f4b3e454b2df30e2de4718fd4edeb2409f0b816ab4593ed2aee0d0eee7e12af2a706c78ffc7263430b037def47a248f6df0eef0e704e3d75979055d87e22bf956f5e319433b6b378b287474a5eec69bdaea2529869ef74d401a092278a0739b94f793da0686bd95df329c26df4ca59b1c59a61519ab30ef1dec077fd4d2421249661dc36a54954deac8acfa9eb1605e2e36608ee1259fe4805a08327d836b3f41ca8837c429027c03c47c81c64cad3f27cd2f6669a74ad4a03505461639cfc5de67d19b01f794b3673bb22c70ae9d93bc0a9d76061dc8cba37a08b2e7a4533bd0f983a7e3e79198f3c6f998b3fdafd942b7927bddce4fc5987d0618f5ab7a872fb22fc2d818b7dc3be77953b4535a842a474ce0c972d14db112017c716ea7f24e8d3b697fa1216573c15c883e3a280baa45b53076b1edf002ef069d30ae542cda5073f7db4097aa5eae6cc5938ede439aa19107f3625baae51d0e2ff1f13553b80d39c087f1364fd0d11a707a1cff1a58757fc19b24b992f1160480f196bc3c3b398634d004781beb885f4483de25a3fdd8532dba6f3c0f1c480d7867c646a1ff4b8817aef316b6104d5d4b3dac0cf0ea9a26104fa3b990f33e0fb519e18f0c22dac0f4cb0cd67c0144e99a2f950bf67f164d35a6e992f3861900c8adbcc49d659db0253149171a82da05d98cec61d4cd3e2e164fd36b8912770e714f6d337e605dff24488443ca642e494d3b384dab2ce5d847cc6eb00422a0031a31c9f54b0ce0057c81742120e5f1ddbd5ba914dcffdfa502c0f0789c25fc028fd94003558eec09aec22e32b5299a2f6803d3cf484f3df66a7dd0a74d5efa09d7a340d3dd39b07832ae145e98c2cd172cc8062880028752505dad484bb02d025c7c8eb756a967156df20192d9380b2575f54ed807926d24b4b2ea66e39fdd067880e15a7c3262525e9b757bfe962b50cc7bbe3180df5fc748d69ae722df670bed6e30cc5a328a2e692ad870c968207b81cf16f629920c6334ddc9b231de1d0d1610bdc05bef9881b7e896ea0892322dd44a6c01d0c1b0a436c018abe564950de60528ed4afc31371187ecc90a4b05728f795021bdec2038d1c6b84c55bd310a89c290208e2c30c8ea5472e2c4e6cd12dfb6ac1aa2dfc6b753fb04a335f04a022c82990aca9fc3568b9ac3523adffb4ad7234d3b2a8067204128b9e5c389390071019deda0d1f8e84cc02b4fe69b089228a942c8216aa6daa8212d0a8648db064c4cf1c20f4bb9097b37e6e8a32f86b48e88c4f123ce0aaea9c5f9dbab62040a83bc2cdd79151fcbd2016a8caad4102bd0b48002dafc66f12e54b4ab3ba6000f0764d9dd560ee5f0668d2a7596e8ed67faae577cb165c8bddfba4d6c340c260cf63f16e55e1b69a5578e7c3597742bf6604739953082bcef4ca204f99631a2022ddee919ca1ece2cad7c35476c057e186fd68551a913f389d69eac0587dba907fe2579c68fcd525795641940fa32c4c79b8aad32542ca9cd255950f4507d87046cc40d6564703c9b79062303a4ba4578783d26e781b943c62884fc7dd90f0f07bd27fdc4a57608f410a047db4fa9b05372c19b0463faae8572de150c5f87b503ecad136cc527ecad47815895f6d33d63a347bc2fe62e02dfec6fc8a9ab8a0609467d506d73a23e4fd8943930b2f61f718d5ad62ec1c642a749256a7c96af8809a4b93ad845d9837c1dfcd5ac63d7eaf03e7fb527b36a3d40623dc66f73466b0f1c9f40658b9e293d3b9fd48b72716a31d358a1bb2a5f78ef78c8b0a5ccf12c0b837005e40f55e683a0f92a357f19046323103056fd0f0b50290838454da10b028dc8b54c4ef985042d262603be190c252c7238dd5dc03951e082e369134ef90e8d997b551480d4ea6837d7d8d336da310c217456045b8943140e5bbe3d363a0d388b7a1483aedc1675025d9818c0be0d7fd057de1e082542d817e81f0e1c9309a4a1242b720594d07915d60a3296de5839367f10c63dbabb528de32ba3958a0a7d35682b4d24e3ace475735077fdfe6806da515b648e10028f9231de8d81d0048d6b21d496c72f4476139733debee9ad0888f262b370d77be5a316b37d7d370a713d24c40198bdf86d29e10b177c8b8e32e75d99e768fcbf2fa475af35389b0ddd1fea9df52a4e5229d1e7ffd73a7b63f1db2ff18df25c75bb1f2b693471de0a6391b216d6e001666ac4a451fb50fc0c2b4af6c36a806e4bef5cec27d656f103128617abacd1c3606a07a1054a62b4bd90ed7b0a30f15a7b32b84add5c7636084ea838c10f4e6707be04e7822d247d56421e0605202c772125d320d0e00b05061c1394c830eb18f0227ae4fd4371350b579f7c767e3dedd22b552dc16d0146078ab17f0dbfb7ddaedc7fc233ba75a92b79ff6026d305f879991d9472ce4a590b5cbf3f133e35c7de0e12ee76c55739f22463f29e7892a9c6971fa8de9fbbb801b289bbb0a2518b41d769de2641bf951e64ec1641afccd8653bd25d20c05df206c0a2227c4dfc2aee1ad0d716e52ff786ce884afcba87baa7148020b322cf6d0bffb4c3d125347e683ba827e7a2d7a668c0967c501528825fabad9c2c84e179074f892e0427a0779c2384238ba85129a3d87383c243645953be1b2aa8e381d602f01e2b97a550b5348a6656e0b215c2026f175caf4860b17bc19f69870ab3d00fb75a9d24e27fe9a11c47db940d4c5574566732c987223af00a5b752d80ec1c0472487d7a880bf21a6578ee78a81df5007c23c579a657f0f8530ed5bd9922ff09cc351c03f2a7638f45cbd3447342e3b9af1b20216346f6c03a92b711089e580e7ec8bc06c2239016dce54af6fed1f6a49d304c08f5f4391f5071210b341d4a0a8ab606f923859a959b1e2269aa1c0af1bd2bfcdf5cb6d80ef233871a34d5b80644b9a1ba398be68d854266ab195bf9b09b0911bdc77db1047575e02a6feb09025c02e2e5a2b0ab53e9406d549112672d61f462f734a62ba6da955782f4edd00dbf3a0535659160a1eacf8419dfec70de41a0b3eb351f775d21091ccb8162a70f9c3adeca0727d1a462e1495c11956c38253230a235ed30fd29495a50d052670480d215cb98b5f0f8fabb3ca566f2c7fb774bc490b8f1157b4f8604e7a1d4f907a17ad398e34f63de62db2f6df1760ea3b33cdc1bbf5cc7e620a8ccd0be62d20712bbed3ac69559afcb486a8e10d287da1a502481499cc9ee07742281ed91820875c39ca040b473f5c9a13a11b3214ad08dec8ed3e9471bb5ae3be3b3cda8120b5a2f1af4786a6e300aa75897ee35ab59ab0e7400b32c17712f91d3aa3d0583022d5d3b84726db4651636b0db24a28db809fff12e039b1c6c98093a3cb8626807263395a2fec76f62f2f2b52b8349bbe9002767308a4dbaf17c3db55d65e69f0068d4bff4b8254649ff0b47b49677f0164068bee563f2fe9b79b2f71030b3f00ff84861177f64266648fc337f3a92d1ac67238f5b414bd3436b9ecd21acfe820b4acf1fb0c15a3f4f6f3a0251659cc170262a81eede9fa76cb6088622baa6320d4ab60fd01ec02a932ea132169315a1bba72f1e96097b1a58b53bc1f85a245a00be3bb41ccef2a1161cd52cccd1a9c5941276351873e9f44f6364fb42bb401b0723b25da55925d561165cc9efde5ca32532867f79a53d9de27df3ae306c1a710333a4a78e22f56028a5c14e2fdf0c6993b1e8d979a48f1816319a0d89cf8f510f66614f90018b404332f499febcf80fe46cad0cc1a76ba4486164a4625b037c021b64eb2b9b728f819306481ecba20b0797b6695314743dbb232425d3885cce078c741077de8692a87c5def4ea98d27c0b98c5ba87fe5eb6295d30aaa51755501 false -check_ring_signature c0a1a9df0939609244c0384a6db5044ece394799458c4ffff0df754801c61d1a ddcfe950a3bf82a264b7b0891cad90d530247507ca9c334b558ae175f16283ad 1 b58aace2caac842e6d4829fc8139b633155b45938658436c3c23e00d1285ab81 794325ff77835eb04c8cfb255c00c1d600d30ea93fb033b713c769879ff9480ce96fce1a593700826a096f90f3a300e2d320f7d7ec62949654b0dbc9cb735e0b false -check_ring_signature 276026c337c55791cf0a9d6e65f31fa25a40a28b8ada504287e25d997e2e71e6 3f019e31d48ce9392eca0c8a110bee604dd67e341e6a5459334b9548f1fb7b0f 3 7a97f8e507c39213984a8dc464636bccd5a7f71493dea157fc1a3271a07a7f2c 08caa663024942fba96a402429febf3ceb0d1acbd56bdad74bb6d56ad0a9f550 1eac4a2bac76d882cd726bad995dea2f01840dd70c1fc97c3fa993cc5ab82c62 c430e3012a5d2c59c92726562a462122f9a7ee167d9c807651ed62e9f9910d0217cd381d9127a1ca9ec3c3ee501e8d405d3f8a820d1ba4d8388290a9ddab0305247342ffbe454f3de1f3d8cc25786ce1852015f1cdc21623ce22e954dbf8790f0360dc3ec507f7937382a81e38d1e20a57035018b762e781c4134b130b513f0dea072581f1a0f5306ea14da26e90da1e457867aec9ff2fc2e6a15e87461f9f01d1b50a33092f4f7d1c605cf986df5d42c92df79995da06d2961fe144a43ae102 false -check_ring_signature 70d1df3165f91778c7cf4120a1f2242c0f4d8b44a861641087cc5f7ba6954929 e916797a2e9b672a85c01902a9848cc82489be12dca4afad50c84a0f6d286005 15 f0ed65f3f57e16d26164e49d059c8698f4b7cfb17131ae2e4245546de5fcda9c 6d41299d86f49ca1a46f3e5a47e3d897f9a42861d4317e423992e5fdeabf3de9 7f605ab3e1f76b624deaa07045e7cf8332fb8804081168787df1bbdc954ff6a6 10b3095439600fd8d6c832599b0d3cd9eda606cea542f399de667f53120d1713 7fdcb7b76b718f17a4dbe7858a9ba473384010a508e4971f665436698b605def 55aa10a0162e92f9f213d1d3acb1114b12bc077114f17f76a57494e003f3b48a 753978010aed159f24355c2651f454ab0abcc83996859c86b8ab2c5953991e32 45d51fb98b97cb81b21317fec9539d873ef1a29d4606dc3ee4682049e8c7d4c1 9c2c0c55d059bfcfd0e0e253c05520ab0bdd5045ba1c3c9aaeecee1da34d703f 10c72765dc9737478d334c6974ba67a3f6fdaa3b810b6f6cca9907cd467d4379 ae7a73d1b7cb98d5899e58fd05df2470340bf1a0c1e52f71525f047ab8d19017 1b98a319b9ecbf188e93ba297ad277241b84a9c61425bff10424c163868a4a5a adb545ea935d7342faca9e57cd1fa3121b7644d72900290c2181613d31211c41 cfc1018e1ca41fb39af6bbe71c3b0a2ba63adb83137695177dd1acf5f7685830 4134eb2d9f3f7ba5970fb8e7aa526e03b006fd70ba622313493b5e949d89fb4f cad8a60b4fddb267121b3c3a4a118eb9c4b0b29e0dd1584518848ee0818ac90baf1edffb03a4a63391ff7a6c9befd50ffb6c0ad88541ace84d9c7634572cdf0d3d3f1b68677eb029fcce6e896a1172bfc7a4d87ea49f0c1986c763c52f0d7006e27fe2f7ebe5c739cc4bbc34205c2e3508977fbfa86ab7647b074567eb772f0a289d33c0995bfb9e5adf0c853bb567416477c8418a323433fecbc30e195ed40166915e185f293a32add45b0cdc7d224641e62931d5bcfac1518c203c0b728206d117dae1d2a693e63b814a57c1173737e91fb834ccdd246c1f3e00b5b29afe070714615bb7ea118a2fb1f8921e43208afd2116602ca48d4a72a735bb6483a1056a2e46c57e163baf18bf78a323d231646d90f2a4843ba27c9daa13375eeaf702be46d6da2fdcc3ca227557a906d4c18553f0c9ff314d29195805c8b7c151390c83e037a2948ea159c9e580b421b4595fc0eb4bd575f7fb3b99fa96710321fb0e82b3250fb5509f4e313e2cfc66d7692453e0aa3113b618c80483ff99549a5b098c7c4f78d6203de4774fa8a7f813289e2218a456463ae9c2938a93915e0ad808f9c25c2460999dd1d47874c426ebb1b71fb4c86e28905cd5faed586584ae21039da01e398914d8cfee26075187db341ef3200ea4ea98a6af3083d3d8e5b83e06db5585c395b85724deed31d96b0a8f26392f0c88d37fb27bb67a84cbb695260c8f68e3b78bc2cd2a1d08ab946613dfe5e0aa3350a7c39b39875cddf7ac4f240a9d51d613b0577e6a709cb7fd744d9c6a612e4f3a5a432db318f0209fe0f79107575deaaddd3e9403c44a8eb9a5435a44e546eb2b061fe5c2b0c5ad8a3cf17309aa219d968078505e91aa0269a821846e9912a8b3251677f72d4d740f85c30b0e91c351256caf1c0b0619c1a37245a999e5ec0c55377ede5039e6646e586fac094fc72b7accc9d209b3f81f53ba257935da5ec5565c1d257c6b17bfcd554a7d0226a3d24ecbf4508144ad09cd3b0fc3df89c3b4709f512eb0b6b51a3cdfed2008e3136f823856bf62ed042b48c8a5c75c0dc29e6a54e7ab3dde1e6139095e29034c2ab9b7daf86d055c9ca3b3ada931d1508921a8c3140891a8229a9c05ebdd0cb0e12de8f52f41ecb185abdb1fc55704d867b573788dddde5cd5acf2170fbb0cb2e114f3f222870c1c4585a272aa150ff519b6e5ef40e3329b30265e654d960f6406665b0a61573b68feb828f1c972eac050eb20b77818a73553ae9be9c72409c90246304e60346fdcf484418b9ffaaa4eb664a1c49157d1e151d244157f8f0cfbaccb4226be917961794ff4a8534f8648cf5b727dcb380d6d1c051106bf720b false -check_ring_signature 3dddcfc838447e025ebf93c012d70f8785b35134b63671c01fbc18c5ac26e556 ccf5324d308af68c896b3bdbef78e656072df53c384ee6cbc859668c7047d28a 8 d54f087b046e25e397c80e528d7d275ce1f8be3bb6b71394e66a98bae7ebc9f7 294b10fbdf0a24b9fed4feb1fb3e9f87d45006b856ee845a9afebb11f96e8bc3 bb3988675b60c41360ad1709dcd470b3ce4e5c59f39a8c0ecdaa7db4a97a31c2 42129bf0661de6715642fd30c27757fa60a9a80255ca2c012fb3a8215bbd0e56 f71fb5a6c8bf989e52a59203f84230f0379a2c91e7d6f6360f00d7a2f7c8a00b 4f0ebc4237e251ad14d4d520cb6a283cca4bd2e8439914b755bc0885ea99a7d6 3dfd9739c70b9300df064fd23b840f6d7df4f47398e339bc80dcd84da531c413 74936db1bd6a3b74ead39e43c624c80c39245d7cc85d325f91defd7e3c943f73 70ac9024d84c691797be6918add48f7a34742736748330c2b7e3cffc244d190f704cb77e4c56d1931e0e5946df047c2c1372c555f3efd4aea10098edae5c1005095032c2ae43d6a0f0566d2c5cfc18820f222d2d6ca94f6f090706cb9dac5303fcbfce841dfa3c64f5b78667b518a179a7c9c1e34e92f4bf1abbfcfd4cba1a043ffaa1cd17ad2b61b360e4b07a01f0f87e9df8df07fa12a0bfb7bfc3aec4c63d498da4ddf61f0b97e0b3c1dcc523fb21ff5fcdaf03c6875f87b72a529c4cdc03822bce6ea38cb110b1892fedd2e2bf6e88bf8b9d492f4bd0e200b743e613c305ce8c9b710b66af3d33078aa1889e8d4db325d1d8aef859360a31dd772b1c62093566edcb92c16ee8fab9859163faccbd9795858ddea4821bdda7461f43466e053d97d8e2da382e28099c11792e66b1e33731b4e86a385ab3977a89db544ce2075cd61ce212639b35a8f07cec96fe27d9d8a8d87214fd773715b829de4ff1b505e681eb31748e8e71e7b713461b683492357e2831f83205fafb9b6268d81f97f342984fb1d9c3cb1141a813e65a0231dcde68378addc1fe306e48c0d6e21db40c014104edb97ac1e1f119aa2bbedc7447cb087ad843527e6ecb6cb5d2bfd6eb05662795b5e7bd15ea933540d370bc0ef1fc62fc5745d5c4b1f1d181a63b55fa0e413bd3026c1ade9e224f3126cc43e5f52ca76c7b41ab518c0af07b453007ea0f false -check_ring_signature 943ab3ee15cab5ae4ef0cfb51e8b41c5a25d944e99d42890144d55923a47a6ab f2c092ef6e6290823d44b876b234df505266fde9c9908d32850540724963d1a0 7 e836eedfeaf2dc73bee3282eb3ed2ca29821bc2dcb2464e533a281e6865a14a2 ce5748f4a372e81a0e9fd17a6d60a6fdca4b8e38a9ef325b5ae2912e66a5440d a2c0f69dbb9257e66657e172ad9b507737a1228c60bdafee0c0051a63dbd58ae 636b9eadb746b4c78ef16b6a73b17b77af0711ebac122fc9d7819d00350eddc4 45cd07d3d172ef7303471575267906f87e83b07109be654f4f9ea1519f43eeb9 6e3864dd79659f013e794daa49402dff700ebefa170de26048906f035a1c520c 4b0bebfda2d82b9ddc1be8423754af5072e79401b41e52923aaa7ab9f4aeb3c9 fe7daeb9d83a135b2bc799d4e55da53d72433a3526f4d34ea79955817d6d630079440285019177ff6d15215e1b38321d13bb89e5d2dee2900dcb044a161c19021220cdaf266b5e95a0e5a9809f642981ee9f9d061e9368dabc25b425ade1760a719e07bc30c984f6ccb60907a129185b770e6c0004c47f92026c4af477457507ca806f8cd42490d07dc68ed7cd85b50e9f330eba753e0562fdfb6e13de1bed0776c50ae59bd87446cd1dbc013466a9a966656297c3671be4502823f43d67a60c48a8d330ad61112aecf745c3f80379b12af900adaf26f7719e68d7ee41452602cc0138d015adc6002a4eb83dfc28aa74ad27d8736c9c5f7e559bcdc247674309a6911a549620a580b0aef1b5db851f9c527da71862c2803a510ec6421e9d590ba7bb1741952f4d06526336607b6846f8a09428450775bfcc988a658bf527ab04756304da1cdba70edd47079f4183ef99a193c16374c058a5f50db87a99590a03475cfd853b513db2a10825f4ab8bf31dd34410c53ab094897d98a3501db64704ec5b3d68f45251c971ab501f47436b93d95db280f2c98a67bb4a0356fbb686021f3dddad789860bf5b5a41743b02d8d1cd43c7db60831a42f8ea2ec5d71c3d06 false -check_ring_signature 4d45c77100829b97bdfe83db61a74cc78ff81a0203eaed667111a3c9df1f6980 b88d4db27c9a75b70e0a9dfb8a3d47425870f225c65acd8f2f69289a6840e5b5 1 2b20c49730e6930d23c107fa58eda3c619e0b190b8e8edd00cee2e74767745ee 413a1e6d308e98de33c16665a4bcfd3d4b4fa6ac51ac938881ab0036fbef490c64cb2faf9de490f27a84ed4e08788b48dce3541f91dceded143afade0406c808 true -check_ring_signature 7c58ca693623e4288b66e0217c1eece2147927c0cb47915f12128f3b4493375f 7b1ba4b0284af18af14162ffe7c6fb27dbba446a828c5f01aeed9669ac42e948 1 369c2b3e7d8f6ca89de3a3e894f49515c4dd289e6873bdb73c692008567fa5ef a7fbd4fe630f4511f576d2fb57c5e83106d0054fe83498ab40b31fc50877fc0f045d415f115a704280ddb6d73d8a30d1fbc19516508610cddce4b7c1ad32140e false -check_ring_signature 83b3d658bf6a2e628ae5098e26d209df295fd28afb0c20f4369f6cffea0c9656 a39e1227187c6f5c1f9b55362f59895e27d568bd55c04ae0b300de57f0e6d48f 243 284df5f2ea15cc4507ef615ba09a96bbb8fc6c5c2e290c7581c6802799794eb9 efd27ded642b63bab26af97f61d5ff913a4d94887cd939992844056a3ed3273b 4aa547c14622eba202a43c338886a747c879e13c277dbbd28e208d80aeba576b e3119f86e46e1e0f2ad6f53b3ed569ea0890eff8292913973034e6405656e2b8 0932348cf19fe46b5868a05f6be78e81de0cb47d74e0a92f9a0fb2b090037117 67fea5c8a9946ba3980305a820d2e709900177f95ad35b2d7539cb3e02bcc024 9f75a1ece3bfcf3a1c05c6ac35c9caf1d9ba294c29d330a6cadb51f73b61a988 1d3f5f944aa58e425fbb5c0a3334bf83d49c6971eb09aa4574a2021b2a00571c c55b6fe51da6b17702426f3a3acd4021c088eca00b89a47a02a2ac4803a6beef 4a5a96cd8ad5987580124a2f2c2551367bf5aaf4a4279960a71f340c03eb7602 cc0edfe788dbb85c631bc02ac5f36b8bf3868f3685d14c7662647e6a9c908df6 f4683eac4ab31f4f9b0ec2fd9c997e6de82900635d2cd47dc7d2e801baa35676 8058d7f41fc16e429abbe43c3186e251ef13d97d9163da909d8619ce5cfa5d09 e1f6b41c4e680b1b6e65fb7fc9c5f4127eb6a4a91c4d226a46764d2ae008a59f 722c27ff7acc23f4bf459c0e56236a866c869ac634c7993ee283fe064035f395 b75d4378b1ba54710ffd6257c7c73a23544c416f2b0dad28a201162b638e0d5f efd6832dcc2b8f69bc7aafa3020f4dce32ac30ae85817c9f7a42a2f2079f8f28 52c79b3015b61ce313f583a89590c23779f15396a90a3dd05588cd5d607d3a23 41020511c8efb0623ef9fe28d4f5a696f2e2c8c9002f1e7581a2bdd3415b24b7 bbd0dadbad4773866429d1e3f7cd02ac3ae98f58230a391b440f866ae30cbf7a 03b3b187e7cbdaa3de72a28ced23e5d23d81e0bc9da73bdbe58d21d4f390f8d6 90e499b2ccca1892fe7041a3522a0e731a1bb9d9781137abdbe5324bf95d054e 869ccff8c5d6d1180110664757f6618f7c26122ba1452d009b191a3c8bd6c1db d190dd0d2e5ff39803e1085bdb877f9e62a8401b9777b3fef4929f05ee60e5fe 7925a9e8ddcdeddfc5bb95d2057c6711aab4380c0702693de6f52b718059e981 c2b44b961d4a4a29663765200f5a8ca323727b5f77774408d7e2189224117c12 74eb1f638e84af422944934eaf6c6c15a28c92583883b40c3e061227b514e50f 503d9616f989251c2aadffb81d89f0995e0413cc9c2a06367e42c0008a5732ad b198ca6843ae2044aaf491d0e82e271fe19e96aff1f361b84840cb7abe555490 56e9b6c9e2c5dbcc4a7cb8489ffec09dfc8151f335134df6bcabd694f3391c00 583e854c4460c1e95d62edced8fd15b5d05e9397fc1c88b90b6d32c545e36218 fb4652337e6d3729f1038a8b5e6c5c73b236c7611aa972b8639e9cc57681a6cd 86f4fc94b750bb94c84f4f39750e4e8a54ded77d289388e9635ce99a2411dc88 3a0b13030dfd21dd95acd394bc350733b7add5ad0f02992fd0887da5333d9870 2d496f88c133f15577f2dddbbd8ab55523c817e9dfe18b9743cbf78514b52597 e9c959be1c480eafdbb69f02481fa10fcd76134240cf1ac0eca7acca38b5aa10 bcbf64edf64c48fc6e2e63dd0d30bded5bbb0d06f2925cc0d80a11bffea61b49 9a5b9b5866fe22e101bbc89b56ac8f5da7058edfed8aa94b9c36228b7f857a9c a907a702e98faad5832d6ef161fda4beef35d9e901bfae57df836a83912dfafd 3499c08f314b4e3731f690652f7c05d2c1a6fe350cd7554f39bfb22d09514c33 3e99c24f014155a5b6eca08b942c032dfd58d8b93a31f9250a986ca6c5d4445e 25a4f5c6da143c40d045a2c76f2bac82b8bf72baeed87f14454eba8d0be242f9 415e6887e20880674e1593a9c0715f90ebae7eafe19ef8528f5f0aa50a4427d0 af75f7a882c2604e90dbae880912ba5e77fb0d6a82aec568e8da44a99c960f6e 919de2fc2763e47ac7532c492c5ca05e2cd28f060645c310090a57d292bdba4e daf2890030397573df3969dd7840b484b0aa4aee1b72dee154a911ab8aa4c5c2 b63d3465c3506a891272da1c2cfcc6dadeefaac28f104e3bc89d76d74b1a46eb 250d71097381778452e002b826cd4ae790b8d874ba3e3d71b908b3b4d88553df 03df5e25bb2db1e98f53b862fd3395cd586ecfb5caf761e66406601b98ab3ef7 58558ff526ebdf0278e5f27b2f0b581097be0e9140dff34ebb8cef4293224c67 1573966caceb4a5ef581700e6d89c55500bf4bd6fb42f16da6b5802a4a18034b 8217ed49c890f71cf2308591573aee5e0a248f572ea89120f824bef12e6eb703 1a01a567adf729e7f2c454ae4a46ac290a1c590e8618c11b2053a1baf8b9396d 0b49622c12c3539767a1bc3d9891749d4eec6f6b698f4640423d01e37a083176 2232db9e2875d704ad473d1d1e982b38e59aa6a6d5922e633f276147b44115db af9f4b91ceb1f585d0d6cc43e7a5ef760332722d1661ebf7a7d68be68e53caef 72d2141b140c99a09dcd98185684cdd62d88a2497f34a889aaa4d1c1aa5d3b7b be2b4051c0117464d19b6dd016aa1e265b0f30035eb627c604976c9ce205b940 f65c30be51cd64fe47bb9c4771f3ada6c8105cfd6f3256663db536ac08ba7bbe b5763bcfd3e0db16131aa30c3e5fa444e345fa8413b7962d322415f533f0e827 918d31cf78101dfbbebc06fa46bf66a78caeafb3f3ca5d6108ab9cb4ac5ce772 a559c1fb62b1051bfe6e6d3364be03073ea6a20991d61d38920a668c23010261 0bf659cc2d9f232efbc534a2cf470e85895371a8880162c4099a1be11712630f ccd1715d31e6207426c0d54589618c35d67a8dde1052cde32afa6a02d058e067 d83d96661b6fa3be2e6d0a98e62a30db9f8a8fd8a6b8f95a42163359547d8e92 842f13b1951726b879e3107461da9e57005c1fb3e323e40adec7befaa1326e53 d9e646587ae9dce86b827313f5cc38cb75fa08845b9c0ae24f397c692f9ecdb4 dc55c31fbd7744bc73016c024b125c50caeb496856ae3b1d042e57441c94457a 3d9b30bf67c0f8b2f5d9f0d3ca7a8e83497be9baf2548f878c39ebe7f337e678 7bcf79d1a0923c9772046fccfeb53c593528068f89b615cfada570b52d5e4a9c 0a31217879bcabaa80b0f06579b2c8ec6d2eb167632dca3ff21fd96fe5abb3b8 5716f4ca373204344790b8f8a8808e38b9d2be5f95d57b7342efd294ec3ebdc7 6f473bc108eb118ad1b269bbcd4ff30caf6b51c91df7b4ef72c318da64f87b9b 2d246421c91a65d47d205b7d09a3eebf897437d236fa7c46a5652e49416b7e0a d0081fbf3a5a455a5556388f8c269437d4d35a05ea1ebd96ab3863ada1f88dbb f93dd35478c457ce395383d7bfac52f38a3352a68d6b36279754b102f6bff0f0 89b36a35ef1af052f37632da8b41c5fb72e103e5e937449c6216eafd5a9986fd 3236f28299c49757320f52d8f00723c4b495e14a7a9edcafefa7e29b9e1110c8 5289a723ffeaa0c4c3208228f6715e655719c9eedaa3bbdeb1aa8daf46184c4b f0e185d735f9022a3c4190369d458bd00461494bb6835c015e25df111e75fbab c58da20c76bcc640c8b16d4bb88c2501cd83f967b7eee3d34e7fcaadf99116a2 6fecac2bf7741793b9b88fa07bc5c7fd870c0059437eb6a603985bf4c7d395a2 2f42f7d6e93cbfed0796ca322e94ac2a1b5e13af312a519153b24107af087933 d21c6f4ab66cbd14af6d0c0ef8113700da88ddec9fddb92a8beee6ff2506485b 85c153c53312b5de4162d5aeec67c3e5431be8634cebf72c613e5490b5318067 1a76e812c8d3f62cdecf91ceb8c6330fb04765fcd58af80e4dbca3ee89e2a1ac 40e6c9be924bd8bcc8a6b8c1acad66205f73470281d93fbb961f0ccd87da36a8 a6560a39f63ad20ca537d4dd8e76d9606453c6f635253bf5fe001fbc433022b4 0b4c5eb9f1ea7205e217c9d498609c6da53cb1737d894c6b472539387cbe85e0 f634f9348d3d5ea560c9a812debeeaa04af7da270de7989aea5ba3c8101b45a7 9ae81561aa7688819dfc77157bdfb8424ff0199e8cb10ba2a0a001a450c1de2a f4b83e5ddcf606b94a5edec1252eb46e65feb258037b54a547481fa4401edfbf 5b3bc8153b0ac0a8793f74a2e640571dfb679ad3d9cf02354fde58846a2f59b1 d9b0898631bfaff64120dc8d74453f8250e2b795aa0c564d9f9a203593f228b4 99ad799ad3b48d7079ad452c43e2c7806d2c25d03e9ecbc123d9634eacc6ae0e 255ead2b93b2f039f04e2e25a754ca1490e26a3c81d2815f400439f3775a74f4 4ae860b2ee1d10d8d2677fae96a99c002a01b87a27690fe121e1a27390337d5c be501dcbbf4904d981574f066e87c1d5b38874869dced20fc467ad51569ddd3e b05df33c00b7bc3d93340d2267b9b175f51cb730ce96292c1fc8520f65c2434a f80921039d581fa4ad8b52c8ed4dbd20304423a44477a2144c6472c446ea8040 1364d69edee377abda0bca76d60c1c3bcec4d5c79d9c10cce56cefe90a7ef586 10ef9033d6a155037b7592ba028c0a5e93a2634c053454ddd8dc173b5251aab9 c7b2446a57e3b3257dcaae4cdb44c43546a983f1c0b45f25abecf0551dfbcfb2 6c70892103bd8edb9226b73b4dacb0f09b75b84fcc8cdc630a855a5253a103a8 0a8befa863ec3ec48f150c16b81dbbf246284e9821fc0cf5920257299b06c9cf 3d2d8953fbdf4eb2d8e5f63652ccdfa76c1290495e4d5edd489d6ae898607503 96b61c9f0eab33d9fb1988173095402ab5292e580fe400d6ee32ae7620c5d23f e110bf6b5bbafb047073a5ce1cab5954cf2702947e00f2219171098b4048bccd 9b39c810842b401b54d11c016a2b023d184adbe51920028289a5ce74a03cc153 c19e6b512567bc00b788d17e05cba5936051db3d27fcb3568b7f42a610b86617 ea84f148103399e2eda9211583514e118e897a6fe08164030cf5e96f0f842077 6d125ac16cc2c32a677fdd8b127a4efc49e4a49c978c3a93cf05da0d89d0e3be 6c05296f5f5332eb921d2fbff7e9b752e9d91ad38d783b71aa0fb841e2364cfc 2db3cc20b1ad4957ff841aade419ca91fe4356f56db21ad02e3e671ce3278434 38ec5e10b104dc9864a8f4d2d4a5743cf87d5c0ca521b96f23498d233f971f31 b717e72a9e3c1abdee96204e9b98b8d01ab0ca6ac041b1d470055b5bea33ff28 219536c36cf24b67e6fc5ba9483ee8ea11779c2d9b79f0f23b1ae77bec33371c 867e9efaf6355c966a73c6098b1fc56bf525c1c2c280a08f4a5409c15d940f66 b17cbfcb2a5dfde9ade49b7b70342e28140c50abc9ec2b14e668f73596e79ac5 94569826e1d6d4e31a379a69023eb95f411e79eeeaa8d619714192171cbf852f 530286e570057b8a8aac8c337755877f3a4daecb12e01ff7dca911b916a66b5e 2dfe12f4c698aaaa63e7d394860757aa5ad9a53db94861ce1309ce3f56cd1ca3 5f281b80c4fb8cdc19baf1355852c2486e3475fc4e312a6e145fc62c0f45d756 bd977bb5aa97b555940c986ee14bd56f227849512c221a7eda5aca9ce94d8cf4 86c0bea8cff253dd61e48f5686fa18e50ecd034ef4880110ad7ed1d9cbbd3789 2939334e364fa2d9e83cac972dfd09ed025b8ccb32264e796a204477e3a39d08 dd2b28743317ad27f6759189fcd581feba28c93944e3a77a9fd79aedbaa88745 3dd4f7096e4fed9e5a4382bd6dde4cd2bb25c8e519bdcb10c6f249397fb05254 ccc2417699c11f53100d93a44c4ed4e23c9ac6e839468e28ff36b0bb44cba48d b6afd7ba8d0b563a07bb66f1c40eedcc559fd2c640f01b3342bb636f6ea765ce 0e6adc6cff62f3e5037f0c458258fbc55f33003eb64d7dd41af5eee8230e03d2 9677c1f2bbe2ac155a2dab74c8460443508ba9ed00fab86fc4dfaa6b5e04226d 175acf2cb4557c070fdff48caa25244a74fadb66e8c54059fcb2ac9c8158f31f 333f9ce66c52956127e91f8e29def81e02ee2522dba1b71a1059a2f02e621d68 0e14cf5a81c48b6122505d1f646b837485574b5840ea903e1a199dbd19943e42 308c7e440a1835939e0d34ec0b99d778ce67a0cbc53b057f7e037df5872fdaaa 6f10e4c8b16cc40d199fc92cd92bd39a0caf2531f9be3510a738d2b60f2aa28d 48c4f0c258d09357724f1228ff319666724673a0bc536614f8e6d101884dd5a0 6f80a11cc9f56a55daa20e6b8c07062f82c5db13f006e6dd57e2151b3f04dc8b 8dadea0f0ae85cda01dbe4a2850fa9fc7688bce7a097fb30856e12a4dd8627e5 c27472a571bcfbad6648fc0b0952c0d6799e23ae2460a62275d790b806858f6c 0a0d4af0fbccb5eb4aada3ac9508a98e43cd7788761b7b110d1ba1dfd82c7d4f 06dff22e0df78b49659a1f17123436a89cc14ba69b3a83d4bd560dcda179b906 11c49475546850352c593680c45eb4ca64f0797d4be237ca314f8c5e2ef7c2be cc3fe333f8dd869c5b08e892b6f51ccfb013907eb546bbcd5699b68468641028 25685968642e8cd119e25929cf48d7ed9b3bb19db236241c20ca8769a9938982 4361f1a122f6be4eab064ddb7e55833b58ac17815356997b7fe00ad9886b5a01 b1001afa86513fadf5a1eedff46151a248568fd1fefe949246425419202f5bc9 8dd0ae522ca0d2b073c79d167c27ed395714885a3a6b80b24aeb230e22e3d9bb 5012bf351153de192d1b4cc4c9cfd3fa7ee24d1e7f7120d65eb17d390a23db1c b17712d1da49b0c20163ec4f98810877264fbf0408d2a3a421d97371f7cc68ef 36212653483f5277a1660137d19f85c7d18f360e409d284131083a82c9c48423 4af560f0e7cb883e660c4f07da9756f0f59319e02be8f878212894c0263943f8 631678c031e5811cd9aae7353c836e14a8bad017b32aceaae9e809d3b43d15c7 634e4c02269006a370dc4393f2698653a69e566864a59c27a6948fe952e64bcd 1fa07f4bdaa25fd03d9844efe8a337102cf0dccf5ab2fd255ebac8632c128c14 537d1006e4030453fa42cb93a4148145bf4a38fd72c722fe593a4478222f8936 774582f1097dfe1a4e812b8ed2f873af66cba9cb9fe903de108bed8cfd81aed8 ceaafa0305cbab00d57d86c5ac7692471297fa3ce2f650a4e0495863d3f34307 fda82555cf58825fbdf34c8c4a48ebe00d50a4f73ea92b5a98e841931a58985f 7d41a5656634030e3efd72f6e0c4d3bf2bfa087abae635fdfee8ec0525c9635b db2690cf86734ca1d7525b17b502638d74cc28977209510541a1618fecc6ac49 ff5f62272a3c4e56feb0bb368bfd1f776026fbc8afaf6302c4e55ed363584e4b 0d618b8e89c8e6feb3f49a5a931b8c4edb6af03fdb6d65ce876c5a1869ac7aa3 dab34174b47222e116849c95cfe4f01b4e5dc8fde9e1760084164cca15795cae e38e8399699c4262d8930352294b9a4b34396fbae793a974256952d1b58403a6 a98982b7c974eb1b214d297f24bc111aaf532b623025cb4350c53159313ffe52 6d78c2f8fb4e06ebeaa41cbcba5e5408eea0132ec0fec244e8b145d351119b05 fac9325cce6c896b7d7ee68b92f5d72c5b6a4c1889b97bc0843a5ccfa5290921 856b7eb54a9ebcc03cc5a8720f30e8f7d475bb07ad2784ccad86a94402f2c601 ce0af4430aeb48a496a8acf494cfccc31990712e9a3d2a0ffc7afeec8ced5e2c c99bf124ea0225361ce505fdc91547314f4959f28b50babbd1dad8f693a4f0e6 8b843c44e9c771b90ff315c64a139e0b0e637bc76444c8e785fe82d398c36ff4 a415b04cef2e064762eb57a35687fe7e16d8faa4bd3e475eb20a67cfb1a06de6 c1bef2a56257ed311d6cdd5ff843ce80ed9e3a6fd1cc4e20c668b5603067bd75 e9c15051cb0cc3eef26400f402902c04e200b0acb949d2718615a8567bed71d6 0899aa4b02e933cc963f75e2d596ff996ad5e2aeb1cea3efecf36c03a47c499b 21757fa18a3cd3d597d07568b9ae5f45dd0139a72e3c9160e6a8413755d32c82 f31aeebf6ccdb8d506b3eb416594993dfecf3b487b7fb1972e928ae5e01466f4 09240021436a20605cd5bc30fda9c44754fd37eea8f2cb8eac3a11850cca3ff1 58469924563860d0f5702b315924a413fc03452c16d06437693d59f46e6c885d 1167130a36e3e8795e17ab6d0de75fef039e9959be15503afaa469ae6c7ea6f5 fb910958039ce4c515454e7e9323c9133ddee5cc337d0cef266719047b24f497 30243904265957633daa01682e0a07024bc5a4dbf5fb50a9de72959db1fdbd58 200335004fd4a2b809aeff0431cf7918231a0f49d2ae75b2fa9bd0fce313ee1c 19f108da181cb44a5aa3654b08e487afb2ba9391f331b7874b8da7c4cf640f28 74768f5ab8f4c91523a9936501424d45ee50fc2124108e3a5611f35021f05f71 5bb5b90ac48bb97bc0ed83ca460b03543bf8bd836e6710ac1dcf640c24e2dc9f 2469855ecbe68ba37e0b67c382f6e8b37149c059bd4768f8f45f53710d16e079 8e9f180219cacbfcfe6209b4c68422933a968130fff0e07ba005e76329556325 bb1d39710def73a0aaab3e29aa64c98e8e1cddfa5310b376d99fa39a256f4bc6 5e7f01fc6c6ae59ffb567be31c9f06f4849161a59e3c8d6a5e38d44f01414050 b6878bb608c3a69bd647a350c4102e8a93a21ee434051e6c2b8e3f42703ef237 9d5516edf509ab789a5ebe7be892c93b285014d7c443097b137188c6287507d3 608de6692d3818cbcb08ffed93860df8eb022f2c09f23299606bf35d09d1ac56 6a56a52dcb35ba322860c89dc53fa68bbe003cc8ffa2252f066ac0e77d4ae83f c9ca9826900c10082f30e963c0d848c964b060137865304e03a72ec1d41d85e8 697a9c53719bfc041d841446e01af0cf420bf877659874d046e221cf26c879d6 8b9be3ccf1a2529981811a7cb162535c0c661f5127b065ac38e4fb29084d7b44 196ff681eefda39b11ee0f02b3fa5fdd04e8d3b57929eadc3c2b4f9b31ff5082 8f1eb5847fff6ba8add1c7c5d2fc57421af3f8064221a9ac4457f60fa199be7d ba816a07d65ac7119f830799d98c35d0154697a8e8e59b2122c98134ae29b544 159a3c7ed97e181e9224a18034a05dd2faed08c2e2ffd0e1f383300d8332f544 f7d17eb6e67acbbaf6b756fbf91e8871f1d42b288113c6acd8de3e775b20bc90 fe64f231e06857d241b68f550810ef5ce120b2d4010b04b7b09f5c08293e318f 3fb56b6b30f329016ec7d2dfbe88d91ff71e26ab6d59021a8630cd6c0df0acab f0b2375e11c0fdbe6a1aec5844a0096dc964945c44b793522a4d68834c4e5f9b cf8501905b60752acce92c793ba26feec3fd7ab2bf90c705b929c8134aafa0b2 9a79d292e808dd278d66c3d14cf88748fb7b36459afedde58c85933657f374e7 28029c093db7d1d24d3c2eacc57ae559fdd93ab935dcca02d4c33089d04323ea 49a01054f4c7124391210e50d3d10a344d5c428d02aed59dd1ccd0663ea4e8b8 88d184b72cbbc70338238a684e894e33451b8249ec050cc90bc0171b57dd1af8 fdf71ac8a50c8de0e02839dc523048ad1c81764e0fc660630a0f55f0240b6eee 5d09e23b1780b29f6f14dbad9387f6d64c55127528b5864ec3ca285c14cc20e1 f72da001cfeec323693a7b77ef46f53f278d04916c139de99c54a8e9e5e9230b 8859c6ecb126eb47d6e8ea21847f350360ad0d81a6b6ca0a102f63d8b80e04af 9a876c9a8aa25d8ed32ec90b86197781aaa2274527924954c139997547b47f3f 87d6cbf9cd2b65e30f6f98ef73787872e35789b4fe63f3bdc9360a871ff60585 8882f91c0e5f5eb16244ebcc326cd74164397ec40c8e8d9fdce45fcd35847cc9 7d8e93e64ef49c3efa1228cce4acffac9ab17ca89a6ed70964b3a4e4a34e7d4e c0e73b558fd5912fb2c2f91f9d95375eb4eb11b087acdf57593a485153661ef6 4536417bf4e59926bea208dc1cf4040232534b0eee8ee83c84146a52a372f612 3f45fe2d60fdb1c8b0dee743010bdcb59b16fc5743fd53f05807c466d7057a95 d09fd5867758c21366c9f7e3efeaf117c4c60a8eafe720680e79db81da2c2490 248dd60d3bc43e59545f310f980815c74a0ee725f9b28adfb0cbc6a58511406b eec3206ccc5fe19e4647a12c75095d484f62499b016b1637362cc51eb89dba9b 822417d7ec0028be892aa8ac36414f8dbf3125898393b2f0929ae3ecf5165ccf e4e202ba4a63817b54b51d5b94f1bebc3096dc9439eaaf44a9a072b1fc11ead2 df7288e17f586bc6bb9fa84ab72c442f1aed72b4a2b9529164ddcd18d5a4bb0f d2fa22d1a3b8de25cf53c7ea97087c2980047885eed29d96627ab03ad7f798f9 26b8e9d03310a7b750233464c2c284f8406d3436729016329d8e301efdf9bcb2 95b1002f196e1c8f83bc546bd9b6929aeea1f7d153bc1c51250ebeff4bda052e 043403abe772c964071c4acc9a691237a3214b2e537ec98b3d581e3ac82285f6 9e4e5bda7b313ca6dd8820297f93ff4c0e6a096fc51c3c82059def3df1ecdaf1 36ca836d9a8a92e4df97443565c83e39062079d832303df5442242321fcd41b8 b85dcdf870806b8c13fcca1a0528794cd2014d54057f58bec3019142f664d9f1 8925be15c4485f499ee60fbd2836fb016f3925befaf1b7e9924dadb22fbe3eb3 54dbb4bff3dcccbb40b8307c1bde9820d9347f4c739df5a6c419cb8accc14434 90dc3fa205842eacfc8ebdf1ba236b08802ccbf309be7634d0ee908741aad9c5 c6f8319ac356f8c27ae8776440e327a674cd6ee6837a3bdcec7b3141cedbf642 d9c1e8af3453ec730dcd964c0264816e6bdc3d31eb83638b71d76bbfd10a1d66 b69a4b9254fa35947c78f7e5b4869ba043cd4d2f72c50326b666fe4a20a47908 b932676c61a06d0ddbc6122f6906637596c274cf2175c1b2799d68ee959771e2 a068963d4ce4f076abc39c1b9c5a465822df6314462b70edb8474c871aaff105423450ca2f183815ebde1bd3c91324d3a24a797bb5a2f8ecc8dfd53b5a22c00a2b0354e551318f7e9482e84ef6730c36fab59c98c2ae829b20bfafc2e50e7f069a96a9a3d7f22c7b599e1ce3c611636eaf3b818501c398fcd6c4e483b302b3099122bac33241406d94815bb52b6d631cb22455ebe73378bac453e9faed23800fedc75e68a404b5b124b621f9c62d7c1f42d9b96a878a7d99646d8720bb680f08cf6724a80568d05cb6876c79c32557f978ee10597a6fa534918f03d6e61b9d088a94b8ece95b1a98ae5155c8e7268fd483a8ddae5812e156254f2f86458f2b00a2179653fc9d92fee31021d66b9fd14f1be134993ddbac0fca4af5ba1e327e09a48a38bb5c8b7c0a15e26dc9bccdd8394b8836f6dbc54c8f3b16b4aecca9c507157d7794bcb284f7355d0d620d9432b7f08df9d84bf2c932b13ba76aa7eb190d6e807678d0d4920c34bb513076e211456473f54970ec1b2c7cf046910ab347006f5321a91473170f84f54c8445e003617e7226b140065ff5653974fe8a28b3030137653ef85639822c2d337b1db581f92b7a385941b31bc1c4941540ddca10066a75eb794907e38677138d3a162c4b7895d149db18f06738e57c720de180ba09a26495411c59db9a50775bafa8b21496c30b793ed990663b8ae62c2d712fcd098d72016b4a04c55ab8193ee0f0fcda90b3e753fcb153f3a4f27eb691bf5ae70f8acdeb011790a4d0bad58b72d6984ee6ab99675349d3026598c8bef6a94f8c0c7e2be20e510721602e5a77e515f8f9189c637d3f0b8cf3cfa37f9c200c513e01c6ce1bd06830c2abf7c896a665aaefd0c85744c01785aab11840a779c405c10424eded176f34f175395ea68f782320692d027cd5556c3169fa63c8e22211090a1120a382d44464723e87616eadd7c3c41941f0658dad36086a990ab1c856da09a4f79fcc8351cc968ac200c57e92914552f9c7398885d0471db323fdb686690e33c37cdb20fd47fe48246483bb0419ad499a5d0088d24965976fb224bbfe6602cdf0cf3e5ad1e665cfde0ddf4d190e830c53ecb8172a674a2d098997cc95c00bfbf1d2b21104311c202da58bba5bccca9778db6aef4da135f39d8ddb0954450442c67db7dc7872dc979c9ae5ac91add6ed4582290a2a8330bcdc85246ea0d403a25c0641d04a5511ce423e32adc2f622a5b419e9ea8f616bebba693525e9710608e2f6242a0062d8bd2bb28d05d8cf13540010c40b30129d980dab3375855f0b78d716b26e2d1fd8e32f1df6328bbff2caf1b44a8725c85d96ba87a7060557071585ee4b6e68efda3db9b512ae04c411f71956b2924951e99bd0d91d1e23080207d24ca754371cd04c9af5d862245e535e7f21d535c6555fa97176961da5950197d244041093ea0282e3435957855f87bde846452e80af6e37720d9e8f8da2090e870700d04e1feabb0d47eb569ad22d10cc281c0778ae3e72cfc9b8ee31eb01d358dcf991707cc0cc0093693ba320ab6f35573fb58b4857865d131d4c768107eecc219c6a9bb5a37390ae9a1fab1d99f72d941cebbd991127e2a3061d1faf0cecd462ed6a2d961cee5fb2be345960631ea97282acaad04c0e4529f7cbdfba00070cc6396515e169c54b7f57cc4462edc34a724d38e27f3f52f134640537bc055a6c2594c479e98e8ef3527bcbe681d14157efd6a6e26ca9305dc3466f170709944a29a1f31b038fc024d990fdc9f32cb1cf505182f07c91669cd21a387b160d3dca5617e0980531ef3faa4aeb231dc6ed5a5c661381ff54787cd8d3b45b90062bc7f5eadf81d4d658e9c4280a06695ffc3ff79943db54807a0c86fbbbee860961e603c838a7d90d30e785f4762f5d5a50c0b3867933ec017e537fb6843f7b058fd51810a8df1b4e319fc3d72a2aeff967e5d0a356242139f6971687deee540e69cebf0d4882389d77262900226d2dacdfb01547c55ca3f960cc8f2484405d0b37afc020fef9ba73b39f464f33558398fbca122d192804dfb8b9c2f5044bce033a67331961f1fed0ea439346165ab8c1ae30678c8ed188155bdfad61ed3f1c0c3f826e7bcee63d24f56c157bce6042138d462229c53838504328c319c6a2280815dc5157f5645ea86cf354e7d11d75e5670835498b5fe4a9ba6869698e306807f34ff8dd06e0890bf9b1f16c077052452eb1e26631d47b9dc7f393f9e301a901035d46514b74cfd2d05940f855668afe1e05c9649d394a8b253988ec1a392e066213b8f5cf05025393e58a76fe3fbf7523bd497c6d3191244bbab8c3c94fab08faa019e2944c6995a44aadf33403927edc277abc314d404b15439d58883497022b7dcb6ae4476e4b8d65afe4f59af43ab806e12fa4d42fefe989b9415ebe390b30c2e558771ad7273d14ba81b267abd0de2e244f3320687698312ac9a424e908dbcaf85837598ba4cd9ac80c1b05887bf0ef59f6b616fbe86650ada4f672680f4958fac59a2f8d41ffe214e333a219641c446ee0b9b70053907852a72d7b760f8af3ea968bd7af4a0d85c1cebcf526b60d30483dccd764162f5adb0730d9de0978756f301fe29b94fa57e257510efced7ea51051f1ea3970bb3dc0b74ac13b052f8a2ac61bf988e52a5537cff65e1cfb6fc141ca11f328a71a96ba9c981ec500571fcb673c1c2bad306af690e0b19e645a36cf77ec7f45a6bac5fad14d6cbb0f96416c266bf684cd502278f34407ad970404072f099e704948a9dd0698a7430e8181bdf485251b87e62d7095be900224434269abcb966d926abe5c7403a477082b3accc181de2db87dc18572b07cf483cb2fb70f67871fad8623df932b676905fbbf66e997b898dbea4730785e9a8b0ed518ccf03147b58bc8f75701c169a606151ce549083147a244fcc3472867887c63f6623695eb6849b2f4fd0ed74f7308a2bc5c394eadf2fcede023a8c4a06ccc86dafb68db575ad4d9ce5b56b866de0c91323f664455f25bf33e1bb5c55640b64214fbd2352e4a5b0b438791413adc0a771b18e8222315a2da865039244ced0065f160994920565af62e941b5e63b10642adbc7a652193c407830bffb6c8e0594ca9cec8f42d50bbd3f933c16e144b04a88a059258150bb9cdf3818de55c42aa54f8d6c7b7fb6b0728dc2f776707620c736b517bb867ddc40e310da42f31d15f0af12ba736e55f1f9b263e916b9c2206074605ddfd303b0f51946c9fdc569c0df47c1d798326647e0af265e704a25f0312b4077dfab0c30d90511b02fae7ba6a06cd17c6c7c200f512ed72140e5e570055c86cdcee69fed169a2a9e75494fc8cee96a1436af5230119a0621c24f95604e51e73e884f2bc5c4465c2921415696b09ba76a4dd9c6334f61f562909ed1d097e2ac73afba8370d48577188c34f0d7670658cda06648075f96f965b3e476806f02530968b9205fb4e00bd67e84d052da981308215f043fa6c0035ddb36d0800b3ea4162d7eb6a2e70e662ddc7952cef0c5a5f7ccf92bc0b9f5beb807643830a067776c478387e6e35942f8ce7e8570f4496baa52ad84f2408f31191aa2afc0dd6079c07926f16b769c00fa7dbe3a49e8eb9a76ad2f1588b4667e3d666a8180f762bfbdc037b9f160480f518c41de95c2c7623436197d4f70cf098e27ce77c00dc9ac9877a7c2a04b366882dd8c0d892f73097f86e248609cd6c850e4cc71602e12b02d75a2f5063af6c8c2bca7ad299f7ceab8ab30a01856acdc86ee32df006e8780e98da2fe518bd713b080d0a6f078e20595f8620db6b7a04a62540773a0cae44943557df2af6050a9f7be259c5c1554d91026de0f5a4b3f07ae2afbab201456dfe124bfa38507737412dda8dc08d085389a8dd8620f6d19fa3132d329d042d7644db9aff4b5ec3e1999238bc7e6c5041ce8a856ee26a61ee52101072df0125e62c164f77bb7927daef607f353589a5180313f4eed8ece8be9f8a5859960afec860cb958333391cc44cad2d77ba91bb99c00881fcd2cb98a34bc285abbf0de7717a56054e6642f3ced7e43b883b08b1a5c40b2af2d23cf390eeebf126f20e23ab11b34bbe7bba8e1e473dc69950691491410b662a0cd2e6b13b0fba26aa0a16218187855d97476e90d0ebba0f55839e03531dfcb1af00d5143bf1e13b450bfd45bcd811b9446556825eea5c2af3572feaaf3a350b6935119304307ccf2a0989c3aea864684ef4f5822b69924c288f7d0c9819f932f163e2ee75811e87760f5a20e9d66a6fa3a4e4b66c29813513256bddab9d6d0778c5496ccd2014186e0047f9dc5b6d7d39805fb763a98586daad012951401fa0dcbd8fd0bffe7b573f08ab99a2625e870f1346996fdf7f225d77a5e62bc56cd5b75d004b12072270ff06e55f3e57508940af9a81af6666282a005c45639cd9acb05a6c9439aaf334c00b4d322fef38fcfca14efb2282866cbe706bea09e3ec79167d380e10121e61da05fca6f535c30c7ebee463ca8254005e7eec66ce2f4e9494dec34287ea41857e05937c580ade56675439ec5264f4a3863c6dcf24677b9ba5ac88323e56f86dd10722cf613d4b562a60060044f7e2e191bbf2e23bd349b0a6ada050fabc72661b0c88d003a44ab8411797b29ca06c3ca8439f926d9c35701025419bd8c155e615077dab0a702926a9a7ca778ea606bcb4e0148f220ff45478a6bd1a548546f96b04bb0ba5ee2506d8040185ad9f1af2db5202d564d18d0b9b2907dc2637509a1202c620d9bc95b05709adbdf9b9aff35d61ee42747cbdeb657e692f2a915a9d5804685f473bfe9a1f941dfd84e45287f09b4a2c7d93eab8d666e59a1067e663e005efc664b48e5a96816d24d48af227c87dbbe3cbac64a74a0601ad47a8d298000641c7e610a03d0476b713656fef1e852291a7261e92009280728d8cb5556b5606046efb42dd6d7d793ac291ae748168421a9a4b93d57f5e848e22c8c96bf1b7041012ac29df151a38f24d141639d71d9a5ad03dd36459789c690782fad51b850293f21c546fa67fe2ea796404acf047d95cf2f44479ed75e268596992eb86510da3a056f2f0a3ddadb90995cd8c6c312f8b825adf7e097764c8d213351c4964070c7beff7b5dd2d51f69f0da7551a656f57e2cb7ded7130e454c14e24eb219b05aad16b8b9d7d13faec1cce7e93d37772205025b46edd3e5ca673608c5f7bde03969d3fa5a6f165b3c5dfbba785f9d05810383c23d8ca3088f4b5b470bfe0ab0d59ce900ec3b64c48f02f974dee07adf9ba76349ae928a4727708d4d84b4c6c03e13c2652d6b7ba31b2d113a58a0426f209b5bd1abd0653c43475e1593d858e064f83e4a0b2b8046e236c47bf6eff77b010dc4e72fa3164a44b56ed6d63ae6600bd3d740ea64d801977db96966593a968f2e324958cb67558ee4fedffb45b8e033b064ab4edb27bd1c37c1a6bb3293eed5da04387b372a514fc7cc1919909c702b884be8e6abbb8755f62eff24edc152578e30d5b97436201f3685753183ef306f2f9aee06b04c35121785965056ae19e1db9729fccae08d7bd798f31ec96be0979fe1e63823ea11d026dee2893c1490d253c8426864c0ad8e5b90721b8c9900e33f9dd029a3b661bd342c21fd43e1ef35f65f9ff387cf9b471cdfb2e7808bb09e098b2564af0e1a05ecec2953e003159a2c9eacb1771e0d1684b1035b2a7b40823cd13194a6663bd42766cadf17a175ac5f03e725fa9ec7c730150c404714e0497cce442c91c62d7dbf6e87646dd5a13ccd6056603de8a62ee162616a220cf092335857db3add1f8f41addb6e66e99747c6c47f781091ec9eb480778f26e370d1a641838eb8ab9e29fcaa76f1e898dbb055f9aca8c01e9feca4fbe296e90cc0ff69a60a7753d5773c2520799de8460f818656a5caeb9c562ac74399b020d1f08de57c642367f7ce7db64d58eeec2b546664a5c4da02c71754ffc9afbcba5d8045112784247efd2046f7072b8c3eff40341334a58e3b77f3397b6b8f7550b6006c71ba2839c5f74300e018cd1dbc7c1d136b45ea5a645fa44fdc52c53f52b1a0b7b0605b7fc029bcccbb0689ddf5f91ff940aae836aa2f6aac41793ad38926207dee25ee7c714aa160fea08803dff922df889289635147c8693dad01e80184b0d121f1d43f8ed2cd29aae985440c2fff4f5433dcee6adc9e0a939e03fef40ff0f80ecac69cf862150d8ee7ba8aa42341b80477f0d69d8176cc531ac7383c17103d530004faa69fb8cc8e9677350ae8de1cef52c1e59e7d82f4bc4f36334646f02d1e8b73dab48f59860704e37cd7cf7c2adbf5de9ab2a7b14eb07529fdb9a020f75eb260d4d1c975fc4cea1a6fc66dcd8b0f35e8f5e08184b3b9f53d0b8e0930a99ddf2aeb0e6392260350c8a30f3c999547c2cc5bf58952b953fcfdbc63a7503274fc4d6ba5e87ace4ce28beeddb6bbc8650322b7b5057a3380ac71453de2401c1a2fa5e4fa6b62bde7ec3952cad92f62e55cda00182f2a921aa2bbf29b34d00fed0d532acc0c2b1847d808e5317ff8706524206076b73b48682cf13954ee2080448ffca4742bc03058a94b7645439b1bb28a0b1f67a083eb919ac5a339c3005c7abed5514b5ff12427edec9fb7a4e0fd3d0fd6e692289dd97f0dff20180ec08b89995161ef20b840e616f31ccb035334c83a41c042a1d95482069cf3bc1c30f4a426dcd8cd4ad119df656c0a64427dcdd05f82ed9eb98b3c0e366dcac8c90080a5d9fac3e63f4904b898f9b1b4187a389cf36102b469929651a3c7a5a4fca0536e05f4afc8194f49fde00e49eccc541124d13dd42704a3a6146e813ba5d7d0b62da593cfabf2f3b98c3d69b18b95b4fb2508ad9c953395be676831eb283d20f2265bc48c64e6c7de1e44501d8b52fe893f15361ebc8ee2481f47eb300e0390fc860472fb13d2cae92634a4bcc888bbd0ccf1b77c9d93a1ed7953d698f0c430ffe2a76902c8483ce648b659d48c8f390197f5df10a108b3cb75ef170aa7637047b873338b33191299e76fadeb53ed9bf74c24d2d011ed3ba5b91e803952589097b4b4bd3d4bfac5f77d7807b6c48d331e94d4bf7208fc803589d3c035e55370b8a088a2fb1a2839a061116af3a7b80032b37b819a5a5dca6f812afa59cdd0808e840c30e0f9d35658b7a5681bafe971908c33e648dd45a4d82a988b61e597a0826fabf5a35b5b864a1419b805a65ba061ab09aaac7fd14495fec2bfcf59a9203716fa671a3653297c643ae245253448447c74c94a78553c738c5de34f776970c75e0736cbcb7045af4e92ef6ac9ae2f2d73c413b360f94dc573740b8b96c8f0718a68a22edbf113d6896708cb42276825347dca4e1d8dbbf3fcf0f9542565004be58d383a64dc05ea91db5e4178ad716beb8a2f76c0e8af2f5daf32b95a3650921ba2ff68a38cffc9fa29bd837417e101c66144514ecc93bf239851fb86be9a51e845c6c5b04dca9491af5e13979c1882e5ccef27c26d9382cbbb8e6c9d7da015b6af8a5157e7bd80543b5f92b149a2752e2581cf55cf16be67f0f0d6ffb270248fa750dab28919b87a748c2e62deee89132fd6a61b919b7e8639ec50c15b801c4daed0e5dfd85f1f70bb764d07451bbc50214733acb31d04fb1d9dd780cf709b740306dd0457274271d19477ff30e1a5a365be82c0a1c5a21928a178de2b006154a4f0a502c883c6486b44078f328f9da5f07d70882092e365f8056d660190238f31a78f547fd303770f3f02b97d589e29a12fce90eb3d446bb216121b7750e0c206f06fbdc7fcdc25dfbeac3a86c0caa87de041cfe862c158dbd405153950f890eb4150ac413afda4efb3341a7c45031b51910e21cca4ad33dfc27118b690fc7249ba20ab5352c97be02c162bf7a23f605c6b0154b4435af820c437bfaa601e5dcadae819be5ce0243bea44122f7c952d96ec8655bbcf96219023da00e860805ec0119d624bd59a5b34350dc89f38ae26411643ef5eb6f8846d7b1d7d92d084a652b5c2dd07fc15a9c0eac3be6aea30a7754f070da069ff641df63566e990832380eb8015bb4ba7a88529d87c4c18cbee97f3d5fcb3272be33e38388b6ee0f114953eea32ce28083ae623c4e1d2c4c73db370c39ea15717db5894497ecd3075dbc865b98f9d0f45bf50177d4b0bc47bc7fa8f3dad1d5d138b3c0bc65544d03f91679b80619e5fd9b695663e49d579f290c5a96b451be529193b7d220b6e20f8faf1c19b72b35b257019d383fdcb71960ec4d4e3a164dc91eb3f97a3acbba0f500885489a9d649787c957c6ca2fa9720871829ec134095a825b6bcec90e2c09ff307360f86ba2ca8cd9b35479762dba04d151492d92c57b53817362a41ad803893339a005f40fced903f650db985cc4ac577f01127bf40273f9bbe1e77d08011a12dc475361ad5c08cafa9b88b1317dc3d6f8f81bf4bb786269aaa575ff3a078a4d21446ebab26cb4df65cbf450796aa9d0c27402531f2997ed89abd66f3f00bcc0602014924957d7dc2a21a391f654a76aca4fa345aa55c8d0720710f0c709cabdad35d8feb4d6c4718e5cc75310857989e71520a8504f3503ee545ffb96086c0ab16f33ee3c62e2cc86b87195ae7e741fafdc15ba50eb9521c2d0b081400e579f3424671a4026a0cd2f8b5f066fed546ea178e421c065c1cbfbf3ee93d90ced8cd58c039700fa38a5f15dab06828ab21db8b29e86e095e3bda0fd4d194a03b2f323a2b32daee86e5580852beb0a7120517521b21e441008704b13ce1fb2082c4068e10aaf19778f5d4ab5898330eb3a746523b3ea0807eb0557ae5d152a0925cdc764f073b11ba3871ac830d442607bf0790fbb411b7d486e277971175d03667cfade2233389ae7dc8c3a16d6305cdb7c573b6e67b94a95e7bf509d384809bd04fde20689e861e205057f3b7b3bddab4dca891b46f7bd27af53ea97e70404d573227f08fea2c691fe77c39ebddd0ab9180151dd7715056add3210dd436e0c97ed5f6047275dd12f5a9c76c15298af54321ee821804926bde466c927c37b0c2ee0aae5bd08509d308f3749ae06711286eb3d005a485efd2dcb431722075e074d286d99f40b90ba31f08b5fd5eb7dfaca4f6b379c4654f2c7bce97b76632900782e69af226ae5a215f8089602459a429111e3ac1cdfc38dca670fecab2e450121962405f673228bf7076622ba1cf7bda262d1dbd6ae8e1dedffb804a9387c0caf29a24875dc06e941ed3b56c9976a41ceedf7606821edf0f34277b16e89dc0c0d643f85292d21f5ddcfef50aa80c6fb1887f4b99b5260ba3b80187840d0c8053a39300e6ff36c4a29859a85ca7a95e313c5a54e5814e2f964f506bc297477083df51c5f3ce552453082d79b9bb94d1eda84f2146bc4ddaf4089ca1877c32c0c435d22e6dabea6ed585b1505a9833476a5b67f51d5f56c5f17aa5aabe0f3d806e60ba4d2605b2258818fa54011e4632b9436ba9bbaf234a999d19dd8e8f84e0618e9ea19a2df447d44042a291fd7c490bdadf66ea5a0425fe049bcdb10aff703a952e92cb22722965697a46dcfa057389ef2641ceae8beaa776e88357bfb220cf31cfdcb76cd94e525d3e4cb4e7e590b4b74b7fa09fd2c9f8f866ffb4e2a9204e6e53525d7bf31bb0ddfec2782b9e95c7cb0379c2d57ee4a227e2519f602f604d8adb6463a289e64d6c4d76d7d74ebc34e54f7c67868497eb0140ac1c7465d0112360810fd59c6e2221a82ac44d02099dc5dbc6b6cadbfba4a252d6492cb100df3c29a84d236dabb98ace2f35bf87ccc7c9545bffdfcea0ae68910951108af0a2eaf2d9bebddfc0a3983ffade12daf780a81170a4a952e98875a15dd6074820dc217d94bbd4bcd0cd286bdc91e5490cc2c2a74179f7926d3c33d5b08acf145041c9743d6f28107344829582db8c0f09ff2e1fd5367b966a6f285c2a928721201b4d652999b14ae27d1b2c877a1300452609c8e88db964a04b5c4c2d68ca08900ff3cdd3f2735ca28e5e2008a1592fe7db835f4a485a5da414b255ff25f420c00ccee391579de56d0e384a87c2bf906b1e63b56359863478075026e842f417502d3677473aeb58308a9543585536e2b0f7b10955e882d59116bafef856f6cb10dc4e8cef9a5cb9f7f0cc7d3b79940b0c1877ab0f3471bd6355431d901a09a7908be2754fd5ff43ce0c1a0da6224d93cf96010eab236de857d29aac679ca50000b64d23ea6a7656f003d2f85d30b50a008f8a01a098f6404a68b4a8ec3b363470f06cb065a7577badde816c99f259444d2b2d784c26f9641b22e4208ec5874530a113c4734f1791149f4489e5b04beb3ef97847c651f049dce9ecf5710fe2fbf01e68bacb182441fa5f10381aca16c67baeb4e3219ee803844d0f0152d62b07f010c83011a7ef1635fe154cc0ad60f6fed80cde9599fe99c13dae09ce0f03e470d081d88b352aa9f22c3af2f4709dc4bb74928512421f4eec5afef4329888df005ecc51d48cbac3529ebaef6884fdb0bcd6654eeda075142667fc7c4444f529a01816dc1f7d38c8fd28b3795cab569ac87da2a6aa0daf4cb2bd04531ab706ada02a13eba23a6572cd9418ad99948f82e0519e6783217db330ae076cdfd58c2db0de6252cd8767b3548d6e33ef8cdbe1f9959dc3cdc13654a56c2c5e79c896e9f05edc241af0f226dc5f686f52c0408d41801965f3b99697be1e484e71bcd9be40b8974332087cfb71990c70d6c02df61884d68f27122d2c3f22b5938c85d97a608afb242f85486a6bf99634293683efb5cca46031765d37e6296d0942ec9616708b324f0ad2a6fdec41dd0f678bc1c026231983dac49d66fed588f7ae7597dc205a53618b16794d0cca5dd02e83749d7e86a4ffb85e7b3c587203abd4132bf600b0597c5c919d83b75e194f29a2c85fb72fdde343f2d5a2075b98650f2475baf0e84f9d7f0e27a578f9d2cfafc36cefe02026413d83956e14948db1e68dfb2bf08c7e5608ae116d8604b5f0fa61eb0fdc21124c72db4cf2d2e9daf924e651e580ed49b7bfc0af47c01ff5fbd8124a28a15c2055d215f0c601495b2d78240079908520eeeca1c867abeba9db8bfb776be639dc5f0de1c77230da04ee09fe928a302b60c895e3e18d566c5218bf8047f858b95dd00577b2c2c0b3437846898857506c86d7538f58869c4e5c1c74db1fb2b79b494e297c453a3d4412b3ae6bbbc360ca1e0fd93d11d2458689be48dc16646d9468f01f598baf14fae9331eacd547201ea1241e90d01daa8d6522f4d70a8daa1c52d76a399885fbec8d7c5e7f0407d076e53e5948c6c7163ae951054e13d60b4bd40b09514e58201839679457b7031043d9b9f4cd8f6f25101508a0b165a62e80644859a748580e0abb7f3e4fe48a50bfcc700814917eac4cfa5f665f180f2570664a7167a38a3fd4567a056e0d2a602f338a3a2c13462c0e87782e2b43b6d09786d75a7e0b0769dfd25c7390b444c03a968790f832e866b589e0cf39713a41e5d9973c4b0bbebcc4611b2c23f1ee500b0e1af0425e714042a610658a0153183cb34d6a8197e3a4d0bdd14aed9188f096f84f45a76f9e89b9b31f3584ab2cff507486e125f2b5cee4093261b8647d300b44f83682e4e9b2f03d5ba3a738c902dacc0c25c2e2a5a3fb4a5be69bd8ccb0688e34bc2a108bcf04e97a01509fa635114e99f2b70d5ce446dae2a5ac54b4f0ec3124e910b670c84e417d437640d6e528e53013a392c3584c92b4095982aef0de0e8aaea6b7e76ab41a7d944d5524f085243d145f7350b13b8e39cf25f11eb0a5ad1f7b26009f1c21215663de0df7205f00743e56503d1ef2458522ce57b530f2c8e6ad0180e8a3e5a1ece56da5f02b34bea71a6391061efe7f8df2aa41b1b0afbf823e8064bbacd39dee0d93fe4279425da5a61f555e694e0be595c2835e20e0fc717a418144a0a0a8ea4c2a6b9fb8a12a4531ed562bf3ec08bca59bd0ba501de0b94d62c9a74c90effdb0943a29b94635dce9f822c34d010c1e8664083830ee5ba49d2ef17a28a5bbf2652f0b51d4c7d4b44244e7753276644b6f23edf340d6684c96a56dbbe04aeb4ecbaf2072f7e1222eb89e75272cfbcbc971fa30245084ee18487ba62015eef95d68428eb00390b1c4e25cddef842ba22bd92e72fc80eedac3081d7421c50169accda0cd7a78908f42c7fa3eadf89e9d95c073493cd09ffde2389a4a93963a197c9b314bcd69c1fd0ecb318080e3306a5a8f7af1e9f0c249520c5b8a0541d857d658b7962f61a5e3c356eda62d4ff1b9a839dc5bc9d061666dd84da6046853f428fe4de78dee9ce0b5984e5e2d8dc5c224cf049354d01ef512a680ef3376e6c17a63d98ee9017de12bac7a6870fa1734fddd1a019740853f0507f742a4c61225207ddd61543ceac3934856b968e889d95aa0257462f00f9a4e37d8e1b869da96b7f9705b63639241c0ff37e268e63c191fc75fda39805b17611cd327a4215ad25b276511e6c3dd459e4a30d41a947205770b62956c302c8df4ee4a98cf57638481bf83715c857b16e4ef6d411651b1423625ab8093d08b3ff035650480ba1ff979902c79c647000100129139408fca5f581d62a916c00f8851e6d4d5610ff9a54f98a4f85e948f4e516efe2e1e5d1ce00ff0f72afc907a52b8c2fdd73229e193403e8f2c31ead0ac0b87f0f2407116cced8182773ff00d573ba4eec4c9d0c94d46ce1b52b355a3eee2f099e892b20d591e8bf4ed0ed0e5d27b43c00fbeaa7471fd3c606019c5756198e69e9bfe59c249fdf082d0b5400f915831009e9154cd816eb0ee368d3e43f2b1b9881298045c374ddc7e7815708b1ec3da35eca89f8d586a061631453d77b708d519c89a44dcd478335ee1a0809e25ae637013975eb9a6655548a3c411761d4dc271c00c9c31db152d22a2b2a02511bb77c39ed8fa4d0e35ecb7e4c0ba111ea0ad112591ce5b3ae5ba5aecee50bb475a204f6163bd5b88bf092b3a2b887c4a202029b9f4308797f1857f3f07707a3c4b883d483bffc0612f7377d80030de9705f647e3570e2e0eb484ea065d70460cc79953bef0d73ab3e79a17cef98b5e7ad9d26b5f78bf0f7663b233488960b47634df87c08c219f57015449281a4e62be1b2ecee7f17eafd3fc6a11e9f230950c9542ebb7909027f38e2dac1e9528551ad7c148f07dbc03760cd03570d8b0e250bd8402655400e371bc6a86f33240d6fe18daf2fe1eb8f00e7c58f49aa18025fd649be5a728d8a5fb4f1570c859a3c8603a583dc54e4b37a3bae9c80d1a204657df256eaffe9fd9e899aaea7935d4c70b4c2e543d4f728a20ff868cc61040f3c561a6056b8edd187a6cfec967e38c8b34a8380a8cb1ceeb66cb628534a8808b433eb27a0460548c64a0e7cff9a01e68ba34faa8cc49b9fe66a75765774ed0e44d693ede08ab25b919a013715327de3973bbb1b97ed2b1155a8d51903a46b0449548535fe0bb84433407f79f58909c667546d504608f8ae098d00b2d82deb0df6055e5b6496908788741013f2dd7816ae5414a26f4fe4900732d8ba0b555f09e36b539cef040a71b6ccf80e9b5f5e3afccc7d064b4838fca0ada0441a59a309e896f4e1e087e52099261a152d41e80c3c7ede22f47695a9ca23e26490ee6f0a902d468d43a13a136312849d2e07a5c5e0682ff71cc6292c321481adf0650309fac3ce46b211672977607e03768f8a48c7d03d46a113de451f2eae3f62b3ca027df1e34f2707ab381fca78fae81937fd1832b23137effc1242ebfc5b7ec4a30ac20c4f5d96115cfe0f6e0e44ea52d34505c31bcd93ae33df857337a82e85020380b3b0f2583d1bb2953c61a07e73574d8e8d35562a4428fe4c83ecc6b6c3b80a64cb122d7e066c7edbe5e675fe817c8c13c9b6a22882797c3f4a9e9389301702c0958bbda071a5723038741deef0ff18fa6e2391610dc630b0eb14e1a9640c06730986f5af6a4088d00238202daf8b479552cc89b8d843a74034e91fb9fdd506ade95da86e349bf041c87376c010c2cf59408ce9c3bca2a8025dd868849eb100ff57bdeb9824ace458714f0dec097c3cd73048db031f7b9a8cb4304d1350c70d236c31e81fe6afc72c64fcfab19affeaa0df145f15246fe725cd347ecb37e60348df4b3746a7bac8adf8f65d7499fd870b79da4cf4dcd1da8aa1e39982a39e0fe502932afb1bc76073c6a1350ded9e89817ecef9d7bcad8135e8ff849ea02d008fb2311000fe918ed9d3954f90672b6a053a3122ddd1592b21cd99dd926bd60b65b63d86aadf9a7ad23ae095abb1bd447915a94be3616f198384f29a687d330d3775550d147b1eca3c1dcc2c83d2192de7886837cf8f77878e680e115a73e20c0bd2a56e050fa1fb2101adc35277973316544d4409c5a2037ea8aa31d9120b0298fd263a3a5fa7c724fe7e44426a2fd0e61fb741d880f3e459d8f7abc0118305f7598249671f8fa20610f97661ca77b859b30d962f10833de1f061c3851298045c0cf4d059f159b73de466a8bed72a5ee7b7fced212148e6ee3f16815cf3e506eaed30e0fbfc45b67a2182547898c00702a325c3124ea74e5248b8ff481179022bfc3f85b947f4d5019a9363f5c43a1cee50316b856fa5f659fe35192c50ec06c1aa89f55ce85f650a8a2ce1b518d4a38e14b964ad70c1a625c8adde68364808ddaf86c18bd1d4f66be28b728a48c8bc1ce9a7ceebdee159e9ba20fc37fe4406a9817f2a187a3446b21b377d2545c1e8118a9867ecf726d3810db4926c2ce50068be29291baf0dc11c28937a828884e0d86d56093c6d5d5d7ec47cdceb5c3a0e9b60ee3aef4c646fe01b9e001b233c3e9d0698f67cd51c90f1fd9b655291720c03a4813a92e5f614ef2b7933d57020f318cd0b9c09666464ee5f3b26232d5b0d04299a58d4552e24b4f9bdc4da5b74f2ffe482f5ffdf033f76a2e82e617d2b0214b1910a358022e8cfdd9d11b11accead91c53fce8e8be40b322f5a576d3dd0b3cdc7c34f6c577ca019dff32ad62741f317ff18ff2f6fc5f77176fd1d7253101d8c609406b4acdd31e400a9abfe5058dd5f488a3ac0113b21a645590de4cc8002a9ddee24e997ef217fbcf56d1422dfea448503d21888b554b926da5117b750cd6332fddd1ca380e999fc0de421330f5b7ae6054b9cff2f9957cc5a0b8cdab0ff279da14309c554c64e86604b7a419cea65c2195d13f15237e8f7faa73c60a00b31d80e5cd783f07b5b14519fb2e67b18462a62811961dc9d32c95bf1b42020e96dd345c50b01e4d92b511a37b53b63c4d13a02026f4aaf346b48777afec12001e5ed25ffe08d22fba5aaa675cea5297b782b00029f8bde9ee9fa58793768f056657f5afcfb172ae75ca975e0fbe82f488ec09bc4f8e4c735c1262112a5e0106357cf89a32263ed7741de1f2fa25c757b1f6e352bbc9172af01d107e2ff3b40d005371596693a55a7451c955927f4a6b58463891bb398ede18391123b2a05f08d5af1fd17c782f9a92b977e58c3a8ad6436fdd6456fcd807ac00ede89c8d490f842a9e06cae630a3df613bbc71b49a09be73fe812749204096ad35d034ab0c0d790d78a76345a7addc8762568db6f56c32902670acb9028b061685cb8a98e605eaf136b5bc628a3605822f9cf5400c557e88aa52936ce880c21d695f39d60607590a05b28874e03d8000c6bbc28c199c3c3a2c4d646f4b45114b1b52c7d2140ee8c5383ee7843f37ca568452320bc55284bb0d1582d776ac0473319896edb50b0702ef610691da8e18620d4fe14f33494f281764e2aedc0522b02b6d9f25890c67bba8880a3419870be3b2a2efca30d4f696aa73d2ee6c00e3d1a520aa5efc01f73503af36028daf9c92ecdf5de9da9494fb164a5182dc3e3c2bcc837ea5cf01910f98c77f5ebfb20e5c80f96dbf7e97d356712e76572aabff5067448a704e0c35b170e189e585f0092b08e4f52dc4dd33614ed81d55918a6fda6ae049a14c0e381815201344723689b377761698cf8f854e22f8d092d94a548e4e0cc4235b0cc72aa78aa2bf293a3a60486aa6cc69ff33e650bb3bd94d720c3cd4412e8a31001ed8a3137f8545ed8052eac0e314bcdca3bdb08969fac8234d403fb9acdf61053d79864b0374d7a0aa5aec9a233bbec6dd273f56b836808460733e5c13fe150f91cc064a26eba5d49399288e1f330a53bc480e2f269eeaf9fc174ccd74e18d02c1c1efb74822341f0806ea6c67fed028202368025a7b7061f2729268046d42096ec724230e3ee9ffccc8add3e5a98bb9b0da9a56d830dbadf3f16c238dfeb30b567a534988560fbf96aa8da794a10dbd5ab9164d23fbcfbf9c1d1918f5f0ce021f566a4d3acbde8708ae7c970e02f91115cddd1245974aabd1a03674f0c74102a930bbf236d8df1ff7b2cca1137f71d5d6cf22fe60d388bcfdf2e6b1f73cfd08cb6fc4fbfffd0a300d585ddcdabb9686677ed98c8de55ce4e236bb627b5aee020ba70e164ad842a6a86e35633e39cecd497a8adb13dc77742c5557357f54b304d5ca6ba572709ee9ab07ac23c7c0b9242875e99e50d99f8bfdbcd3f5ad01da0f49f072175ae5bbbb734092f1e8224a5fec69b703105e775ec50070eed4a5fc01b20a45e7a5b596cf52e38b9810b7404fb63f7450a368324c17c27a75c86ebe0b66e3a5cd798a7ecbd9b1d9b97dc91dd038e3417d619a5b942f5facee714b090c6f89826c99749cb6505e2b4b5b184f3ed56f686aa037d6b172539d4729d2c30bf93fc66dcb11caeb951221a09c8bac7d278077e42424fdd0b282b653606af2089cb97188ba6fbb2add8ed852c21a9c7d23b0c9b9b697fcfffbdb0d67d18bc90fe94562196162cde7f434a3902e92087551c6ebba1f13fa51a10bdaea86f13301bf1893b2a82aa7bdb66c1b2386f9440102f8131eedec764eeba156a9751e7806f3f4aea6adfa082a6b6b0ac56f0bfba59cca2720f22d45c79ee1bc83337bdf07c33b572141f83a270257beba4d781ea817723df09893b8d5eef84c82746f0707a226e3f69d2c67d9d080f00cc9acbc4fc2fbdb2ed41064f71faf2cb1d316190660008b7e7e9b3d0077ca199f08662e19f4dd3cfe0da4cab6699955d7bd9a220c4e81819b096fd9d7d3d2bc4c61077849468922ece966d359efa50af271e3f40fcdd2c01d1dd752fb8e4d26f3d10accd6c5f6df2fc7640b41dcbfcf00755a270d831ff97b76610454aed9eb817ebd3b3ed6775975aafa417e646ddb8ac1cfc903f791a85442c61ae8a663de256c680334f779671bfe3c4ac6e83e4945c6ff9703178970317fbfc6a7e59596d548fb954b05c28632b12397f8a0cd35df771aa407990d472e42d7dfb5a5e0c5f63fe4b7846e84b36f523cb96c57ca5c43da60960830d2b7ae4837aca6046d2a072649f4bd168557003a485c4c7e2376e030ee5e0f33c032b55a25278d48b67623d73fc8722b9b827b55f592e21f4ea7234c405b02176aa0daec2749298a37454554055b73baa689fd6a3241f947e858b4e067820c5d311490591240ba836b2728fa6af9d4ed9598e3a1ba26e9ae8c3f53b0bf500ed13a151a20d33a9b05695b622be5a93d218784669ddbf50cf36efe8349305c02127df9a10e7818712d56ad836c5e9288893666cfde967dac467bfd260de82604c6e064ae2537d3fd41ae69a2d36f273f5d3173d36b10b1a8cd04c9aca5bea206c69bf21c4320246c5fd4eb4a8be270ea8dac83a7f72c69cda8e34a50134da40b724b663fddbd57f1dc54c0ca96d8d7fa3e299014bfa08bf170cc79b313c1950581f8620737d393223044469caea54b091f65bab0a44d184d2bed58a1e163b409f6f18481818876d051ab4d3091c8bc5177731ac3c7ff7373069a6462c9ccea0194a46a360eb90afe9807b38dcdcf094ae4018804cadfb8de079c1c4f01a7960baab2e9acad251dba1da8e4236cec0c89d700e513be4515168aae8bdd387b87083d75d9069c9c82666dbc6d7693e67c3b6fcb56cb18ee288900c37e88ad33900db1c9cb6517ac63e92ba0c5e3b5d6acb6dbfaaa4e1c4e5ad7597022eb31723803639a25594c4fdf2487badc96483b035f4900cff3706c33064e2710d90ee27e0108126859480987d996f93757d74c5cef5c017c4fd417c8a8cce4200058fe9d06c0fad391b3810e226f991fe06b43f7e349bcb26bd811d11dbe1fb3a48abccd04e3119794d266b441509dc288f0e107c98bd00b72eeed3bb08c94365d407a4e09972b59994b5f1928ee4a9475a59b169713786993290c032da06f35d1c4b5af0098cc8875359829d9e1d9b79d579e454b8ac9b61ef2336cdf49efaa0a9f074e088e75015e6f1cd246fc3dee279dd5cb315aa950c97c68ed2b4b2b45f600524409dee2a3e3c7d90e68852e58b75bcd62748f3978a1ffce84887ec21b29e226ff026b7cd6f1f3e3dfe9f8d9a4dbb306f380bc1db28f5ce58b634c3e7ea6d4a37b03f76a11cccb7e86fc3980071e4e3c7edfe3fb8555222f4e0c6218500f0f0c95051b0441af5ca7d6ece8560e76078406004809070e1921fe454e74afddd9f40a0633fbf35812dcdfc19c03528a53c8a2aeb506ba0cb120bd4e6005d9a60004c909e3a5c1524c5ed818c3e6612e546ef6c01a115a104b7501dadb46712b55bab101fbfc2f1a2ed5ba1f1f79365dff29d41931d9f6a7d9bb9770dccddead555af802e16a9f49760bd4149168cdcf3f0b8ee026d4d2c06e0aae33d7528095c5e1bf0109135a88e964d98ede3319a27610f55915730498e0309acf335f21b129df1f02ed54e302c2b8620b7a340de9773a3b2eb157fc90c47745710b9f1243cd42b105798c3180e146d3dd2dbb31d630c5332a8caf51a53072acff7615f25957d48302a6fbb63f9b35afcee8dd7ed8f8724402261b8f49f0f8171fdd0920aa802bbd0bd7e1f1d85a2c74e9be2bba32c3a96f85e791ca8eb6f577adc2c7ad272c6ccc0720094006155b9f56497209a90287e98d0e8c9faf4a9c97b3c92335934f8f700625a64348699df810d933166c9cc66cc13187f8f80b29634710e0ade65a1ba301bc537de63dae5980002b1a8ed7526c03efdd20d89e8bd769a56f10de5d95f4015b315a36b7b54bb9a202e2219735d59a890de476991e062205163a0f1925c7002e99c11d7188e3db5daab8de20c01ab187ef2bb28748bb0ffbb74d86712647031a0e57a5b68f0ead4422a17a5ee74768d76d21fbf1e41b959d2ba240def2960b2f2bab009e2ce40b371e491b113d7102c86e5ca76d094879c923a02c62ddae06c8abf4bae565ee8bb3eeb9f579dc8b63d0165e38086173227b8babe7a752c501aecb32e65b43280e8014cd80c1483cc670c2bbf270418b0a5f95dc34efc5e10fdeb9ea0dca1a764741acdad3081cba77621ca94245c635fe87beb04bb9218c087a300cefc1bf38e5b2daf00763dbbdbf6e4415894d657732693aa4a296627604ee017f70cc7d134e918b740818bf59ad4fbf0c70e07efe05a88b69089089dd09ab3cd0740c0e3f578193c5aab0590730898eebee81c4500e3914c71c5b71b00d337e87374fca83475e5788b4dd88a633555429798956f21e54b4aeed394b390cf511c6743373f3d9280ab29d7e17ff8fe9ba97deacf38bd5c30ce6a863914d06d7bcf4d61fe708570b4bdb06b8624774acabae8b0d5cb819e39068324d710f00377111b060f1eeb0be7af2833191bb09b8e32360699c36aebfe693d3faa43905513bb8bcbccfd0f4c1d09ce5d0e304304adb05e3592b3109ef8efb6814d6a107310bd4630ac7db3cbd1fd01688e673a26fa1088da214abba5032e8b527fcb2018336173cdcb9c2c072f2b8204121bcc920ac11bd39b19818beb2646439ff2006232b7056fbd515bb0b0135c858e15d02366810113fa68e8d5e3baa8ab9277c0b4809f9c1029764935f0bf9f2549bbca39f725ea5252b41aacbcf2c3a1a352505b22e7bc2f6501c977df7392b402ce2e3470fafae86b75baccb7e1fcd8a7240061db5c946c5dd2240072a12288b098856dd851cedf9c7afe7833800e01abd13055820b9f7581da5e037f320bdbebb4936492185985f186fdf4207cc2e5e5aac01729831adfd4f8acf26e69d3e87709df5f7524702b0862b2ab7932f316b222c07f7433044bc3973ae6e441db0738490cebf79889fadf8150bb9e79b2710fa2b02b9fd5ddd605908ef32d23979dad65da2fb5ecef2b74ed37630b8aac7625b6700095651cd86709476b12c588bef3a943db814071d9cf2e9fd85828dd2a0c6b704005557e3fb3e2bf722aaf0a52e49f6e138cb64ba394bdd274e0dd03c6fbb1a037bb1cafa9ef1eca04ebe7a56bf14a006380a318b7daf95d1593ea48e5320670abdc3723d4541905da504df074444d04916535c19fbedb229149c607c87f43205fabbd30312bdc37ffdaedcdf713955b7358752d9cf9b96c440e677b323ba860df8f06b74e8ca2aa612470f1c3ff31943bfee4f737282799f99e4195a943fea0a3c8a4b09f94b0c158991fee6d29339aae73d16f19ace30d89e7e8f4b165b480619d9d41f42ea6157b86961a51ac72307d88429046796e851bc876391a868df01fdff260d0f0cc9ec3d3b4c287009654a1a1d506ff8a8f6afc7a8f19f1b19250cada196c48ef7c157d3cc08b7fca8f3503b637dfee779948d41529558c0acb50f933e87b2f7cbdd3585f17916218729cdcc85623d41d514fa7b2b2c219ce5cd0e04ae291fc1d5fe5960f6f81d0d4b72fbfcaef147586d02e6fca0c3e96818b40548f5926e9b8b4ec00685366c439b3f0c40535a51b4a41ce25112809b96ed1a0ba0190e5a9cdb0f4db52b77a32335a5f91210d5097e0d6667a038e3c17fd31607239bee0459ad7ea9c33ea050a849d3c4a022577654461284845e84cd3e35790901f6161eb09d44373000a27b3260973eb9cc182d40229fc3b211f77af663cd0ae0f08604608f181099375462e176535ed322c095d9f6f03014ef0c9444764903c50b76cf5d07316cbf023c91529d06faa07208499ff33d783eda6dd7ae9afd02bd993225c2854e25b9bb20cdf0efae33a0b6e7bc8fe4419938fa38322873b9014a6abd80d6e0e13edab3302a591a54f82b8f81f7a260b9cff57f85eaff2524029e85e3e2c72559c37bbff1fa6510da69e62a6d0d7b981f187042fac3cc97f80db0c37567e41bd74dac610942c11a99acebf99b80d3529ae91e9e73c208117e05b35e266f9bfedab66b32336f68147c2e7e7ad7aafdc9cfc2bed207d487f4e6042a72bbf35a8b6d02eba717ca36101b30f379631b16bf6b3d734d0119fb35ab0a1b27f8684f4f29cec7733064e6f39c41fe533e9f1d61bb89dc981601eec12c026eb251ad3eb45629b001ff36fede346d59c02384a8d44176d8dd4436bc60980ccce0457f6e9a406f40b34d296f818a65843c388398c96bb5e18a9a42ee510502657a562cc577d830ca90c23f82ba73a48fbf9529569008b6cc2a8e6bf852370f5399935b7875a6453d83f8006a2d50444bc4b5dc751fa0423ddb9d35e19118024fba496aa850e9ce6016801bb47a28238a4154019d86fa94a05ea7f349f27e063a1d3d7996fd4fc7719a3c718437151818523b654b75cb70d463d054d7abeb08460d27d734c9dc933812dea42faf29b4f4e792250962b3a622292302f27e4406f0cf5e6cf55b4404b84addd5c4cc8df3619876bdb836eab2989812e9e1d17a07b9ed8edd27ee1ac436c4ea6912415e78603e2c511180f23152dc93a70f23fd0c1d9ac8322e96f7e1025817aa52202863e6155a9be5cd930099b3adfbce02cb0f2032f6c9fd269e3d5566d944e12402718651c2c7d65fb4406be5e76e16c74401d255ae99eec4c44e57b0c6e2913c78c15c0ba3697dc47fd67a92debb7e599406 false -check_ring_signature b52b4383b5f92f49ee46662be37941f51450288fab9d804b431dcfd6eb0e8f57 7312adda7a1289db4bcf89f2dd46b011a01326ecd40cb1a8a6825e14b2150f19 64 2abb356425ffbe96d2eea1755d097fc6d619f00cf5f3b25e083cc1670f37ae99 5d33ef7c04ec5448a52f8f71bf7118886a358998add47d91a93f5473c858c638 2182cf5f0c50f0688a045df60fa6f47ac8bab3d2ed72445d6640cdc7ea90628e d85657cdd454594da975bc5db44f9300b8be080aadb16372c0e0ed218825d6af b7a837ea26bacc8788de66f4a50744339ba760e25f58d90f9eb77f72603ac75a eb54a8b0790a458d504d7b318159db0a96f0798edbe57822e137f0e134f0466e 7288f5401a9ff1cc7ebbc04a56ee1d14ca64c7c954f722a5d072cebbbd687d63 404444cb8fdf70cc1520ea4c92bc8a4cac1ba274cf22a034bfed5c0edf1446ff f3c617bd98135a87055f6f1addfa0df48a20203905c7bb915b0d7518fb92608b c42a58f6f9d6df2e6fd4afd7baf66c52865a1d005eaaed600e956f7beabcafdf fb88014555bb154d605829930c84bac2a90817b20e1036b87e54252d2eb3c67f 61fd21877cce9094420741de65094b4608ff9a1fc2b962bf5b7d423e4ff87bd4 d2916e4869c5a8ce3bfa04f07ba345d619478f3dbd75c9439112a7eaa0ce983e 763c272fabe6f9bd53bc4532783f68081ee46e49c861c8b499e353720b549a63 dc939f103e133ab1cbea1c6c51091789111925168d67a7d9ec84365a38dfe4bc fd231009f2114e3165001756aa33f977695c7b289426a167a18db77dc60f1e68 05b321c7c9e7060f1a2ec73531810734edf8ae6e4d6c5a3ba3496b36af415de6 69f1c435dcb0a119986050505a98695e91218100af8e900de6b0150f58b16bb5 f08393c9f8822407a1dc6448be20e777a871002eb62945f167721663eec2f5ba 81a0c5521d618aff7d8bc02c89ad8e19708a6961b7fdde79a2af4fe95bfb1cb6 8d631d90f4517545a7e832e1121993a525045ebff348927864a5cecca829888f 9298ace9d423fc5bf3e8a1d45107d296b70d1d6f3e637b0f53822b6dabeb79fb 19b382f6c89382a2c987eea05733a226ebab8ee9efc262e22e8ce21aab59e947 149da3af19e9f49ed3d03c1c7b65bbc90e282a1b9213b8f3d96487b5b71bdd86 5c9f9da811f9c2896a3d8dbef69da7d0099306c07a41aa0a7a8eac16ea6dae0b e2f9e07dce388038fc6bcf94e6c94e99a9645bfb834053f32ec41515c9b0d3d2 c7d40d323497a599b0f8db0d69a4f8dd9a2f82be600a8570913d684d1644b841 bb7bf9d0225e8849244403dfce98223529ba8ee00945ec206672ccc2e9cc80f8 7b5527c12b90639f4fd6b6bdc33f72b9d6ab1fd087d14da4490407c05e397beb 4fafd17749bb53709e1a15ead80dbe7060faa98593b230fd439a5f1b49af7c7d 072d8e77dbe1dcf8a86aa93a53eea329eabb35f438ebe1bf5792e117a29a9b92 dddca68bb0a01c19b1a542e19cc7b40f8318b5363824c312840d6c2e17521f25 35578a5bccdb4b4a6b20b60e3b5140bab4916a365d97873d4e7909ae848357a9 773beee68ca2a23cbf7929bbe55fc8ed69dda924567d36479eeeff0273d16105 952ffd7362c9d5363e0337910a6a74b57fc5360d8b34225de6649c51a128c78c d4f5b8dc2d83a35f5c9fbb1a96b67bc6b1065c4423a9693f9cf4d734286e0d28 027412ca6982aa98b248b6380b7d25fca97672921f2e2a15dc5b95126f782f56 b84a2aceaf3ed74248ee4d5ae738dc439b3eb949d97a41adadf23c648fca3d1c 53869b5e7279c29857a63dbc895a2202b7251c6ef64d7be943928f3b16ff591d 023c2df66a80d83b6cad6e0efaf2d453bd423d1a6f8d384db2ada2ffae3ad154 a7bf8ae680e5fe1b9a2a07500204adfbe932f750fd43da8e92324c1a1584bd47 ab65d4f9ea9235ce45e5c61d323b94f2a86c69893fcc67650a4839a379f78682 9974543a2f74ae4b35423c8a6662ec6011a480dc9e503948aeeb848e5f9150db 66e402c3fa10a0221f86b82b8bf72ebef4be709bb3ef8c4f74d79c8fd814dc17 c148d1c3b82849b4fee9a741d84f4636b5c112712abba0093803ba92b941780f ecc814ce9e988e6e5bba19815cca8867f2feb58e41961d13bcc95a25aea5a194 59c7191f48ac6aecdac7c9ced51e5061f934d8d05924ad8576e33725e8bf6b69 1f85e9b866d30208ad63d5876c7395685b1cdacebf43439dcebf4824af651e33 f1ab8cc43caa91e16523364f2d593a7c950d2b258b214d8cb61b82aa0b71e5f3 4f88e7efe5deb028040a8980463ca0812131a7a5097051a5ad38f9068f8d8fc3 1db2188138ae5d26b00ebbbd6927b02ca1406703e6c2cf3929208a9a25ec5baa 2e349e799bf895fe90a656138e9ed46cda91a33e4731fe5cba6f2e64265de078 c245ecfd514b329345db37b93cc0bca66dc41c0acb64e3217d507c58c6eddb99 5c2ca0e8289b81486bcc8dfd4e24d2bdae9f9707123507d473a1be906e703329 bc96e4a8dbe542da1c4afcc0ec860829f83c9a173ccfc0fbb021b7654e5c4e94 832d16ca07d142b982ae9040754103ac0751bd8843b31cafdcad859403fa6a4c 8048633967e3028f9cedc8280cb3586a203641834fdca1dd3fad9cf67a5bd2d3 f6e7d2eb6fda27e00bdfeb6b6d06fe8d5bbd2f0583c2c35b697b682c11017c37 29f1314ddb81a9d9e6911dc57ab2d27a710a68c70eea4c092bbe68b44e63d0a7 aa18bde7aa8b3e35969c3a29af9bd918b5abea33c97f51a79a422f8893fc396c bfdb0db70f84cb8c90991edcdc948ca8fc092fe97e9e8fbc8e6b508b1f729a73 fcb11115332317596b4d8dc3fca94f8c73cb6335d1e2cc78c54efb1454f84929 c49b6978f50c0185724d10421ef59f9bea74ddd244b8d3a8b013dc3218fc2696 09bd5ebf59c7026295e2dbbd96d0f24f4b96ffdeda48b7a39158cb68c03eca14 504c403bc335f285a9f9d51e77b22ccade12aa12776b0ccc296e61452fd9ca07cdb8445b93e1516920ac87392eb9da8270448e6f91a4bc959f64c16e75b6800397892c220e5345a2df259aa1ac19eb1a6cca6b0c7af200f15cad06afe23cf804ba10cd44acce3b209119e158ba4c0bf1471b8727306a5c974f41244ebd1a080a089e876e0da011d1bee8f5e1cb54e35346504aa5737814d95079f6cd09e43a0c3a463efbc7532d4422ce7188ea3bee7b1d411f3de5c2f0750907dc92f4c2ad086e7e0f4d58f87a09273385b1a596be914444aa09bde2c68b1942699ebf162301ea11c87a19d44a5feb87c1112b271f69602408257a8096499954ad47c2386d02fa271f010d9e88884493234cac67244f0c34520ff35a451a47be2987080c750b8a88812e4a7b44cc38b50b7ba7d259792c5552521d1622eec081c8e6eabac508c5d3d3d71b67eefdd16835231fe7f3e46cca9e791afc6cd6bc10b5f466df5b00ec068260fd6fa2cd18fadf6b30d43e270529352c0859207d567e43c3ba733c0f3e18964a37a44d24d3648a0f49df5f44fb336447d329a5ffc9d3f613c5958308741c9291d512abb33f926d75dd4dcb109d9f88b24f891b2600557c0245ba550525da372f4db321aa63c756b9c2b4a5201d6ffae682dab295eab80545790b9f005fe021a8e3bebd485d19f4b52cf279aea055199c1081fe6f5ae246146fd88a06d676464af895f224e8e99a56cb1cc72bf792e020624300ed45b2f0c7342bc0025f9e7e6bba99a6952704efca6566b6703241faa1abd84bc5ae3dff34dd5c4d06483433a6b940c0c21b93521d996638529f5311b6a2cdaf666fc97dcd84ee770c3016fea8dca96cfb6c00d93a81e2ea818e941b1b03e0be22ed105d6548bf840f3e2a7df39618b1c307070f949bb4712c4c94636df17fc8f7a0540217c2eb9002e70f47e3a307fb1e7c6fee358b0ae8abcfd693adf6c5e16ef9fe04e6a9c52f065771bdf4ab3cf7ceb6395866ff7d2f6b5f731488e9e7e0c0d967568972ce3e07acff7dfa946af342f7ca709294197732a112480d6106377acfbd61a0c5ab76005947e6786bf597504d896d6a3a11272b8071240a537d2b6b6243d39e4650f70b1268e0aae2d928de514355f9fc3b5f82421d2dd5a4bc3669b7c301776190870c98744f18ef25a1c9072bdf0d2bab4e794899a9d554600bedb2bf6d122f1c3d08e3b6002d784601eb09482fb6388ed44e08baa9626c4a354c88691530bbffb70c3b8557d6844ccb2e71d195f5aac05c81b4ac9a87c5c7f78026f34a5fabf7590ee37e9d9c2a2279f2912c707bb0e3ab27f645493ef6435ef47c8bb4a29b0c77037e1757e8bb41471ff74833aab07a108ccf151de43f42aab6c730cc4c3b163e0488a752ffb782052f3ba06b4cdeaaf517a17d61745b219558cdb1d4cdc32a460f9aa49a6bc758746a26a081d54cc8a7d48fddc92c8a363de065cd19fe630adf0719a359c228a5ef2fc919379cfc013349d44cc41df2652aa3974eb3c0a6b97e04cda4d57e95fbaf7798e4ec19d6f8123b5124a6fab5364c3ad6a7963c4005ff0d44f626ff398fca1608165816fed824991168433f3a66acf74a63f1f3f4e0480b9c725cb310fd7c6257ec9457ab83f620fce298651599a686b65ac49c197d7a096d1406457bf1a37a73ccffeabc147b6094bf661f896c06ab0c02134b070ac70f8a7bf810ba0f4cd9ad2f639ce542a265c32fc0a3391be3a5fd232cf1f314e50c7b2260c9f0cd2c769095cc0414bfde29526bab658fc41fa2d0a798a57def6509b259290c561aa7436e8f61fe2c8261dcdbcae544d60b601cdad0825ff33f8c06a9acf05c2bfc7afb0375e88ed437880993de1ebc0b0c50205f422bcdee500805e42926add6338b4bfdc6b88e217f0e036e69198b1fd8e85c486f1221e3695c0ee5604bc6ed33bec301c7a018d545281a35b855737d5c5dac71fed52ed06a9f02088f082297ff2200c06ec94c462bd9be08c1c8b390983824ee1c8b76010a4b0a5657a039aeb3be4ac2f64b47f0bb8dbe98517e1026b63b266ea36f79b576710f2e710664ca36ff674bca3f6a695748803481856771a0f023118a18c713441204513b76dedc425040a9476ff3549127e0e9111f57e79f97ba353549ddf28334073cd490f630603dca82d1d0228dc3c9608c97428faa39885511ea14ce5e83fb0e7a2643162a3de5aea5beb12e2cd0b6932bd3662041be62787ed29f78d6319e08bbead39e109bdcf7f6cb9b830cfe12cbce920ba152563aaa04177f59b0e67a0770515368441b5f78e05c395ac6ab5d94cd198ccab88375a60b2db5bb7479be0c1f0b1e4c120156b45cf03d8d1133289912f106ae876e5c4d7c2420d8e1d98f0d29be1eb200b2b51a92b51468f99908b369c6ab3111edd64e3d0f4b0ac62464027553fb1886487dea3cc1c0766c047c995e24506ddaaef6d930cc3fc615b3420b9fc19393829d518141ed816065eb7d70ab3aed553ba6a6f5ec36cf63b7bb2d08a60f9a10e9e6c244a7a0a3ec09bfd09cd7b04617dcc8b76f8578d906731b60061955b276a6b7a7f9d00170c137d1e92d692ad8b129a6a92f0aaffd9232c0d10bdc52b961d83d275a9299a1d3e5a48a176d63d694e15ca8263b62af9580b1470ae30365bfef30fbc5d1a83b46db6236a9b15b8bbd13ef3b5f2d120baf6a9e3405fbfec3891e518a8a12eaaea1c3d357e479b0322dbfec4c6fa8b73fc826602203364e3490b9e2ce192403f7ca439944687f4c1448410752fbfef0864c80953c035e493393c3e4c1b06842096b56184a9ce942704b861240173ab21cefe639230ca02d17e93a72cfb54b1f051ff7bb35cf53ebdda34d643cafb103025693aabe06504bc208e3e8a28a07ab345d1847a4ad43e7fa0a6e608b3d1827663c81ffb7026f08471401753a5a5c865aa46477bb89ddb58444469b2daf727d5a084534d6087959b8bcc0840198b530c2c76c43b74a3f6c7ca77efc0de84602792c24644d05c4f49ac778ba4d7c4662be1e2d62bd531704b985d28282e39434d591aad9630275550cb42ed81d29a8ef41f386ae9d9790567f1b0db2d602cf1961fcb3930900b80cd6c61070442c6a53eccbbc031a0cb901ec99fb882c833c57c0ff23b4a9095180d5d0040dc82c6342802db69f1128567d069753265390c75569ed07e5d40847394946080562dd65b278de380f5de4ea0930cbf4f30d1deb135a37059ee10a2c9b3dfa8cd73b27290fadc49baeb0305bf669929ca63ff9534858e9e073a10bd49d7ed16e8dfdf8877858192858ddb004840474c26133b51a888a23bfc5de0d682070aca810990524202d1e0ea18d50bc9875339a8289b27b377d5221cb08070187ec86116850691b1df43da733c7665239f00427cb889be62c19e2a2861a0d6b73dc2c1f41ab7d56574a4ca7fd56b25ca4d5257c8345424971e69d13324b0cac65d4da4d9ec1391d5799c40625d4e030bbb574cea90ba44c3754922ba18805423030e55e0e5e8d421446dc5832e9a91a8e2168157242bb3351de6649dcaf045eed92e718f59c5d26497ef0d4009537d4017e6f05e52bb7fd077a2f0e746d0e03083012e74d2d4be82199be13608f706cb031e154da617d31212fc1459e8905c956fb659ca34406a2a309bdfacc5350a98c23e15a5eac155f5e646cd555cb0bc941875b68cace363982734b4bfaf57b1494aa0499a9c0e57dd1234ce7f5f3083b8a29edbd738ffcd991492c40d9621f8aaea3f11e79b0ad22c6dbd53e5fb20870f7a21183bcab8a04fd4c0c9520d9feb4a12635c1f742ed19e0cfd16100ac0d2ec6fe7bb7547c8432939ca5a9dcac19b84201602ab52ff84252932ea6b2010e011511ec6b3ab08c5104570dd743943c7a1a6def931f4d234bc7a8a8af88c50bd5ec52f0f5fb1b454ac46b497975088e3e27592f9819843f406e62f2cb320c0df6077a692e1d5ff672a0d42a768ed8cb5d552032ed28aa3bb0907a740fed40052ce7f0f7c72fd46b35a77d3abb144239f0168939db107bd638c65e8b308067003578bb5c0416722a4cff66cdcd4b3ed852b26f9ac3f56f678ae405a98a1d440adf89f24b242cd02a014bbabac1f2a2f08dfc2c725c7bf690dbc8538757440f0490afec2b0997983b8a10b7f70273772227245d4346829c90f8701931159b790b216e8f1e7701514c2f98fccd4475932b7a01878a0bad85383973b49121e10f053339bc869f08c7fb041f1785d8b130b38aca0732483f43a849c1e50aba153a04ffa57bf35f84cb5a649ae68a141eda9a44980c5a4311eead61d2a95032962a00b24260a375c3c0e265291bae600e3cbdc5792055ed3a7d3049375cae609ef30d9003a5b6324bb7baac1b54d49cfc851e16a8473ffc530bf7d9a3ee82fa3dfb0ac289cb71a8a38555ed504a74718b09af7543273735baf86a646b3f6739d296084defc927565593c363ec742f0bdcc2d7c760181b75f21ae9ecb8d856f9ac8901ae721ba83477ba504e827e72eadb2b9b209fa57adbf7b90f126767a3dbcb490be501805b1f2a8d899a123378ad5576176c087c551f3ad320a8f4c78620d36f077d4d0d9d1723fc0ae9f2c5e7e90a8e386660c907bfcb5ba62ab6d83557a23e0543914590b7b35d4b3b630b3fadbce79d5d924200382181551fb52c3dda06c1087ca35aa2bd323ebe900ecec0cc853ac38d57d0f4f1aabbdf00ff8e687a0b350154b2202bc9ee95e04e1e5e029b45b1e111bba5873ef5b59a9c70773af48c580ac062addd604be45579753591671b21dfc077d5af4c33b8c53b36fad7b98dc80cb765bf2041f91539df37cfc0f2733324ff060a4bf89cb41c4c08237764836b0811b7e9d987dcb57dcf2b348d27c32e7953e6b073150768352188129d81d3ca0f336b559f100a5a3227d122b9f9a088ca7cc636c9bd74c5517e261f9fc1d91d07a03c5f95242bf4bfa4cc79413d35abb0208990252d0108c71d49f201d13df80358748a9027af37b4eeb5a310cff9557ecd5b1c2d9c844e4d3128b382caa9eb0ce202a908bc75209d11cdb6c5d787e668a8af4c5f126add86e787f5f6fbd45e08569908e1d7551fe79f72585855dbf966456e87f623615631795cca3ebb166e044dc38c86861d6a0468d4caeb8f35de6454df31f57809c639f19ee1acd45e3f093fffd53924c7d4986d3246a750ba5fbed27415c71259396543a2701054a021030609c22166dddc12227c6209c77fb50db7ba5af88f576264041eb0f7c8ead808e990465295651c0882ce03ab8209ea167bb52c11290feefd5cd61163fb3fec067862f38e1112233d589ff4a9fb99e86c3978e923a29eb932c9666b3f2589f90735b13d5ebc92daa962f03bef87dca65026e26e32e7df5eface54f2da29c3020f680354dd88b53216a02c7cb25ba503b86e29fdec4e95dc740d2b1ff1e0fc3608804e9f8b96b7363b7ed82a0e0744fa8bdcf0a5171bd773e06a52a8d958b3e4060e28abbb365117dee8b9c51ace70806c7c6a19aa990e3045aab01c946efd8b07c1be1ead2a04fe19439201ff6c9ff94ee1a42aadf594076e477375ced88e2a0af6ec23f8eb40798b2b4048ada43de782920609c1ffeefa5c95471d4d6a7e780094b5460fef3fbe5b1d1f84a080d6980b6df4496a9b284ba750039f5cee98aa0b9db5a39f14efe7f846e380444981669667f60d6ed72a148e24a2997009dd930a6a3cf3851a2aa7bb1b4fd10b8ca83855449cb2c9ae421e40a3cc3a5d6c9e8d00 false -check_ring_signature 893b74017235d7c31422c79d2bdaf12185bd2836f45dd3c0a0f2780567b58e32 f53338cbf277f1af860fe825b230976777a56535d0ac6050746f08c0df2e1fe9 14 0221ac03734db6743f7d1d5e7a3e7c266200d9ce4e9e7b707d1750dc100bfec9 6574c6969f76d84b8083d5b049160a161cde8a635e46e12bc7b4c3221de6f4e2 954892d4ddfd8dafc10cb58404aa866826a020fffbb997dfc160edab8496fd22 61ff4118513199173b3a46861894a24ed72ab8510984ef659456dc55ef699b26 242b337651f137fc4ed957cb325f001c22b2a40a753ee67a2d42b90b6dfc58dc d2374e5c0d8a0a24ca673f0e7586f4e667086bd3338ba5aeee328ee4f5a13686 efb83ec4f71030ea886143daef3fca74b5cf2eb6565bc2a420762ffa557737c1 113c2db840cafb9c9d561dbe038afb4e7d5e893bcb858279a8763a309e0d9ce7 06c398c6821bd80cc74c469e27645213e42ad76e07d90dd5a9630fad852c0864 9abd121a70e2023e8d9ab920224bfd16daa13bb9577195da3cb40c70cba52658 624bac3facd929d34414db9e6169e3c9b911dc5ab87eb4041cd7149d5d210048 4c95d113e74f04fa208641fb79c4eb7a79f5b941e55318c267a522f90f274de5 d08e9c591cec23a9b9ead71469e5033b27dfca6499542ee10dfd46bf30e516bb b1717785b25c209b868136701d5934f17d739a63f54a2d0ac958e8bb7cc5a5de 6102b6ee1ce61c4ebffddd032886bc5dfb10ea432240144b86a1adfb022f5d0c16aaff9761704a5bc38e1b5b9268deeeaac2a8230951e6598751aec70599eb0de232d72965309361f5405272d021d5fcca5975d14e492c74d2715b20a679e8079463078fa879b3030d212d0ae002f36f4dc5ef10b7593e9668ee91f1f8abcc0d4c7f6308db0b2575dd8a9b608fe6ad5c08bbe96ca35456b1fe297e818288a80a825a4fd7bf7ead55add52269fc4f2f077403d7c59ffcad095fb00e30f6f3a109f30ee975c10f2710e9dbac42823a29627bc091f1c91216be0d5e10f52bf1d40c85cd870586ef7bca7f1badd7a7430dd1d749cf93d0b47d7949bebf6f22c68600c4703592705bbada2d9949ed2345926ec3328427e4004ee039c6b671251b5505a2715855eaf452a62e05ee693605f491cdd96dcd90e04248e9094aca6a112006148e770ac2a1b1ba7026a9f4165b61185fb37847d98b46dad32692b1c67f340a478804a9a7061a887ad472e971c3c661f2ed2e0b1ebda2ac26d34a61935a7b017ec994a3a6dc2c4efcdb32d447ef3c446ddf0f1bf3f7c55a979b1c5af7a29d0d2392981e177d4896f3eb0d79815267296f731ed2b638cd4a766a4c14770721004f42648a8bd20e85b9505ff6b47382fd80441f01cf51fe11e4ab9d64f7058a042a3e5c0ff5d4b655b1da472dee0abb76b9768b2be533dc2cd31160d53a7cca051b9b144083435a1a148499e9aba52cb8e550cde2054d9de5b5ad4c9599a22b0c7d6110afb4fead688f4f125486f8ab50eb9265431f5ba55e2d62cd62f85a580adb0ec6cc51b687bbb6297b208676f43d774deec9881036363283f7e7fedd04080d1bc224ba7de21edc744cf27a883503bcfdaab1b6ac7e05ceeff3560608ed0274dffeb411a9a3e6438d5d6a757e12608a2faa3fe6526a2d8b3e00a1f5e2d50689af01078c186375dbeafc7eab339db0b2af9707c2b8e8810ef298d880449900090a8a22f27be0bf1bedc4d1331d8abd81dc460b1ea2972255ea981984b7f3093a63b1ad47476668d6a7627bf0259f2174bf605fb9a638e439247e21888015088087b597ecf3de665e9805dc8595b0727d1cf1aa665d53c69df2f7f59890800c3c3d3fca4094e208a2ebc9710d1d034389a05ef9c89f7a09fc93c2e40b123d064cd8471f4ecd5e78a0499ff177b184cb950eb47066b7866da00af4a2ed7b410f4d2078293cc6d0e00e1f816e6078f5477905cec38064d11fc1723d343987c301 true -check_ring_signature 93b1d7b30c1409424e4282584a5d1fc6d6950557cecf2ecffd7c19afd7afe06e 76b86f6456a2f8526dae70d8ed372d6c3060d517acadc27240e8dafe8ecb7fb5 1 7bd47e8e7150f596490bab7b8e08ef8f5880add7e8f3fa91f29d45534112ac6a f3ea137c6fe209f57cb47736a56b3f24aba5ed2fb5ae3ac77d8413d40314c40ecb4c989233dd0d20a452e4c51dd9c66d4929875b64f35c1aa3a1892cdc5fe10c true -check_ring_signature 6c838ce66974cf6e9610f53c5e63034c3c998598542ae6ae493663e0a9522e35 4b36ab9a3e926fb822b7a04f45726172d144294d2ac3893b9ab426ea3275dfdf 2 c8569384b426cc7b9071abc23c24d133783cbdd7f481c5ac3e30aa7cba3a6b3e 3e4415c8f1ee444a25e4501d640c020dc4636f16401e07171b49f4108fa0d1d0 8798e3a9d2121f13c8f636cb3ac9f4b5b655f6557f7003a4ea2ffaceb09335089ff4d793e6b9fad93a1298f20bdcbe9848f6512d6a899875aae191e20bcd550371f6417f1e9165e6918bc65ffc8a63ce2b49fff099286bb70d03013abfc4130ddaa42d9243d0dba476bbb5a515fc9ad3ade552d933368a71c045f2b4489d1c01 true -check_ring_signature 2e18122b7ae793c92fc2c9d156266616989e656468f7adbf10cf991a56d7dd58 27a8667fae13a215945e94737cdda60e629374524a6f7ecf971870ab3136309c 1 0c1d4c80fea84996007a7d944966912b7faf562542dccef453a9d9afb2797875 f4f02f455e112c914e8a567d856119c29f4a59b7706ee1ccad0b8a30c8737c7a91f46f77478d2e1ad9be4835ca05c334f8d697e6fa4842368418aa8707e2fa0a false -check_ring_signature 53e2db5d49273bb204be4e069a0287844ed4c175dbc8e39dd4d82a6f394354bf 4ce610477728e3bdf02e8dc5b1c5b3e33f47287b1e8ba4e510b12ba0b090dcb5 2 6c8b3a00d4ef4f74dba7b1b79279dfb6514fabac02189907b218d574c312199f b61e0f412de72cbdf457cdd5e17aa914f996a575f3976f25b983379ed0e77d24 33fa285026fb0af9c2c9a85938b572b0738036b946e5ad1d16c9d47bc9a01d07f3ef8f4151f24c1d8b7b2f9303073032c3855203794a2f9ae035306a9b73e30e70cd812c7b7257dfd9727fcb24413755aa82d7db67279c1ce2ac7c29254ddb04c4a2bc163e5042e0f5ee850a04cfb89b7d03c0ed287a92ce80d7693ef89ad50d false -check_ring_signature a5bfda28bb84822d5a28a578399c41f066e9060977fcf35b7d5a3194b55cac45 38fa92aa896fe57e1a4e6d29e147c24c3244561c368e4141a4ce3190c156fa5c 2 0450c81ff08630e9f69026b3defed75f73b44e12632028a4c4c1792dbf35a4b2 fcab0150060439109570713b6bc67266cd2e9aac119e2ea3ac508b2704a48c5e 3a2f4626a9de16320bc121c906e4468ebb18a4ead6ea4b041cdcbaec9234c605630b8dd61c8e89c716c8003ba076c2da372a3e62acf7e0ec027513eab18b230bcf8f4b350d7910b5fc82493cf831e0d84e115d508a7487eeb1ab32837ad9b801a0dd462f77a6b239291521f2cb6c1af8ea58b5e768880a5e6723bbbe39f6d10b false -check_ring_signature 62af0916ff5dd4f7eea40f034abf7dab4ace9d0a55c13081550eb00bf35ddc85 eb064d0485c8cfc60b0a44b9b8f41091196731b3f0b209bff98e66ef4572177e 154 83329cca8ed2c3b18a3cdc8de3d7c79a272c5490d143c5ec3f363c26aeaa3c2f 29e8aa616b19a39e6e89e0124c2dde56ab808ed6d684531afe6bae22b553e5d3 3388cc16f66d3304f602b060fa2e1a626e2ddaa0b2d6c4e993eb17473c9e1192 cbd036f61571a95e218a40a29acfc0951d495d14c0898209ba06b022e37d5da8 9b1295449fc1edd73795cd50c542b2f45342b550d81a3fcd3810d153f6844726 53399c61fe902b4b9d8bce25bd58eb8401f236031d24834e2329d0250dbc5a25 9f49b1621fcbddbfed7b71e3f642962861ba864c523a0b5c64e29713a4411181 fd062e871794cdbf8b58014232110a8a7d0b2ef68101f34c2c519c5fd947bd96 f3e65fcde4dca9b431ebd3e30397170efbb272bce5722fd77533e0463e7319cd e2debd78bb29dd6671e564c772a0bf2764f3865653da01a3ca7b96facee98bc8 c736856a015af569c84651bf4a74cc7ba91a6a930afe85369276e1d1b5dd0b1a 43eb143de30c824963cd0c0f64605bd79cc7adf9eea5c7c4250ece4582733b63 27033c260b35d4d35034b482ebe734c128787a9dc4cc8c9c58ff0f069b9ca00d 0fa337443f6e29156cc4211b5078c44344ba0593c691d382d486d2c2c7a8e128 fd1873c19dab19f15b3835668c3790ad2bb91277fdaa140ce12c6ffd62bd94e8 66f209f0e0d420db36d530d9489e60df9b221cb8be773d309f1f85647d34e501 46c9fc3df5ff6b311c39d0c5f0f7cbaa541f2d897c54b42bbb9688362b493641 4190c5881055d5552fbf38ece9fba9c0f3f9e6ebb574aaa68c55fe62e0143e76 fbf65ba2a879cf189b46781b6d21e92f89a030b8f41f013270ea2c51b85fff9f 9a4c87e361940e89527429b9da8ad40e90f034667c42dfc011a3f5b02db1e97e 2a2bf2c3bf81110367b829d492a44e46197473d232d821189e6a9707e80381d2 354f6f3f94b1aea24469eeba6d9032598ffef279d4910df936eeeae316912503 9c5ad54ddda65bfd8bdce2e260bf49e3d94693b18b9bad406e598fe8fadaf7a9 ad99bc640dc9d85e3defc0101c8fc6bd464f198d4ecad52500a164081f67a9d5 dcf42cea7bc2ab0681a0979baeebd75f638cc8688593f3a995038fc4d9f50d89 9b9a0451f4f7d14d6eb2be0a45da9aa5150fc875eb67d15aeb3cd96069246a7f 0cc6ad614891b994e18d8a13439dba182c7953219f65598fec73a6c2754c52d1 6b0f2cdfb76c8bf7ab3b190c5bf05ccee3af4247ac5c932632937b657e4d4b94 26dfb5ebf126a9b71272704f61f6dc21d462e9d3feb5460ae43578f0085285b4 f318e25bac2d8b07f1cb84e0c48b218e3b6f5fcc4c637fff0eff54778c2e3568 035817ed690f7177936146e9a45ec78a9d40fbc7c1933b9c6e0ebe0b9e71bf4f 1eb5f321f4709ae8255605f7acc7ddc24879af4606083880d289b1828e885fde ba1dcf6aaf5f9eddc6445a6c0d23b4c1aedb8b4104fbafd5d120780cd4e7aebb 3d4453c71009ca67254be1042f998d6f8dd90e7926c71a54db3703992a1360c1 5d8ebaa22fdd3930012c2561a899f6a80c655f4763529e3fdc15e3caf4070c2c 2e128f0af569f2488ac40c1efae9db9eae3481a48002cb05261a68373ae8c405 de8887e27e02433c9bc47a1249945b202cb58b5d2cdc871ade945db145998460 c8291dfdfc973179162a6b27af776ed9e7b16445e6803106dfc46f228e3f1885 1ad3070724f0fa4126f8f4fde1d29da1605e46c8265096afbaad77e2552ca469 3f6756b308017d145a1a936d16586d2eba7ce7ba74427971ad54590a04ef70c5 bfb87399868004bd841311044d2f606e17a0fd6c4e247b160006ed2800a80e8e 3a5dc8a2a31706f651beca12ee1c87783dc5de45b49ee10da0038f33c3f57f0b 1c8f5b95cc85a3eb01f67da934d6ccf61ba67d89dcada0915e6c83956159ec45 7e6e33b9e98f3e3d8bd07ea43dfaa8e8d8cd66d165c4f1253f7f2581c8c69fc9 f1ba643acc93b4f496954ba877bb9339b20d2c344bca2fa2e64fb232edda7688 6f0b88405939f8254b759dcda89215f67e823e3c17f2cda272f2702a29f9790c a19bcfdc33dcd9e2d7bf3f1c13a25595ed960b38693070ad78dcd8fb58707d03 8f3cec45816301831b3351a7ee583bb99fb3a638c48410aff3b197440951040e 65e943612f3e018bc633287fa022c7b48ef253ea9a03d199763cd4ddbf65c563 17500021ff5efbe08a7ba5783c2a3db4aaa365e7d4524d58568acb33ee7505cb 589bb6ccf7207946d867ad42086ec7cc735bf09b767e30f414f32554eb7bdd05 7e271561ff36b6cadeb3d4e2a2751bad74dc04bd17cb41059aa94f84eb8ad12e 0d166e233d4316ff472330444e81b8699d5451a31b0dfd507481776ddd262bc6 a03a6fbabb406c10692a6fbed1caec8fafc432b69ad351c918b44158c06a28ac 9a4192241f8cf2232cef2e559f9e9094511ba1946cbc7f9691bbce0c1a02c954 bde7db3880d3c6bce2afd44d827a6068d8ab87e7b0a856dca423356d0f11fdf6 3d30271f2adc74a535efc0f61ddb259d703dd22d3d44614f99325b36e7d630f3 d73d41de01b8b9e7cdd6ba356bca0a37c5782cb25adcef79fa4126cdbddb2c46 66d804a345945b4dda1ed6ac8c6735b01405994311090b60780589fc5282bc70 8c458956e8e30535ea19dc7e7efa211149ef06fc1113c5fa77bf79feb9aa4255 35648fc9cf570a77f990f09e0fdf2e013fc162bdbf07c0495e9906ebc2abebe2 b0b4488060b0a92da0c3d5faa1b48baafb0d84347553c2bbcb25a8cdd267ee0c 84318740a2e3e3c8478c0cb4a8b4c36e57b8ca08c6da285844a79410f3382dd1 070a03042803061d8d9a16586ba9e07cebd2092d8f988f885bf4252aa0e29d9b adb3b2fea4f171ae5a1c8e37467dd4579e5adb86bc44f12e371dbab463f04590 aa4fba203c131c9ab9062e9b0b046410851f816a080b29be6276dfa44334ad8f c7b12ce7887c970e1d7d0069df8d732358c517ce336393e866100acf88ccefa5 f0adc7e8f5031944603fcf1f40b2f7b0fe571aa45ebeb3689738f17e64fa26eb 908b9088ac238e713d9cf2f187a0f60f05f7e1057679aa3cd7b88494c21abbf9 fa49f89c94273355d08ad634cbeb319c77085f8551ce83667b21874e54ff9a55 a4fc4e1fa6467061c54641f4d64393e264ede03ce03c5c13eaf35698bff4c266 99dddb11f6e51bbd123a25a241edb4fe829579f7d7e89c935ede6f60f92e4e90 6c566cf1d6e6057372f4576ac8f9d4285e8d160258a65f5efa4b0d64424c731a 1349b23919c62da12d9b130fac8a2127a236181f7823bfe46c4def730b686b98 67ba68943e48b6dc66918e5acb52c35db76e76d74846298d43a77d0cfec8246d 5bae800d3754822321acbe8cfd54e2d438588be5d48a0772d23a77215ffaa6ef 8f3ad343b3607ed4e596ba644b45fc97e733a0497ed793d3ffa3aab4acd45aae c4e861e3f465dbfb4bf60ef7e41cfcf6f664beca4fcb0917334e62a2f1b7642e 0777b1ade1a9d063d12f4b13bd9558328c1217d5811af3d58bce4f56f4130f44 25f48570d9a4816770eedf0cd0e6d021a4f88301a83c1c82a5ff5d99ca603982 039195837c08b1fe06afce2769352b28c46a4fc267f77899731b531ef48c7bbb 438d326c0562b4f07634ae0dfd662acada29c9b8b684309568c9fbf73f873e5e 8fac0006545ada84b637396e74f31ef2b38b9592fc1eea2f0730a6ea9d08eb88 38883b65eb7bfc9ba0e936aa8e85e7ceba674a6493690e7d7d450046bf851239 f1dcea70eb0c052a06167382bd3ed098b03811824d531dabe7fea4fb029a6d3f a45cba13cff11b3c6845d550818ff3476b3b4986816d4a9229ac870b53cb0c12 c483dc584392c6f924bcca6c06165218db5586d3f9f51e542afd04229a4eef1a ef44319a8db3f89a83fd04996d1c40cce80aeecd36bccb7c49cb0f10f03cc685 faa162778fe1f649277c60efdae12fceb793816e7a93d9edda0f512dffd3fa51 7af93599d98d50688b5fa1b0fc77bbb06c1221e4df96b7d957263fcc302b1cd6 990e6fff8f30ff6efd4e23ff4114b5a695c4cbceced7aa2f4632811ef4ff8e59 800e3611aea006e5a6927d6d729e39cdf104ffc0db791add0d2c9f1f60d3baee 9c21b43f1c6854bb29efcf1edb2ace2c36218c309b09579a3d8cc656b5b0f844 89e306109f992d17c2123f3a82b5763d32f0bf5b333f342715e1f268f3b2fde1 f9fc7f9ca5adb5a6acec5fbdf595bb3d67899bcbcf26244ca1bb961588ddc227 c23d2233a6dd52ccbfd90d05e079caf09c1d5eab377bbe1383ad4f2e6eb2d55a 4779bccb15c4688d36643ad62fed5271b27c1be86b752ef16b87179e15608977 278075105c6a351a313d97bd5123a9dfa982cfd66295bf33a3d058025d9a3eb9 a0dd758e389049d06c7feba77a3240b912449e7d9d8dfad27c7d0225330da807 4acca5062ba99b35122d4732138c61e82992aded4d45a074a66b5ff6982d642e 9a4c04b8d9db04e9c77216c99050679a04d72f5e6765c28cfb5c45ee1b438143 3f6cd3d7e0906ae8b24d198fd42e5f0ab6b16b334ff213d2063fbf6f0040b83b 31d109ac56099f9e972cfb5d8aab947150cd3e1c957548a946f3386fdaa01109 94f661a3a8d4b4594b4978c5d0b8cdab17bdb6b3d14888f77ecc07cf1bcd6d03 d3c2fea2cc711b985310a4ac2bcc9176d4dc2c37e300bf6344031b211a0e95b4 6cdc2ee6ab853b4e091a178235b1e34c9df07bb53fe2409c5d08fe1350360286 10725f0d251999abd348894f3c583f832db790bd1a449e3afd77c60331682044 d31bfb5533d076123086db63e1d297cba34f4c71e2da70d52278697f21500a73 53599b611e74eabb9369d60c854786f84cf1843464f9f7809a1f1573a4eb0169 8d97c1996ba9456e5fe5e86a2e8e08e8bdbdc6e15830ba52586cba7cbff0d2e6 c50a93571589b625d1a23caeaa6101970fe7bb05bf041b0bdf5eb9d0b86f4152 b35f37d7b95f15aa6dfaa27fdcb15cf14c5e8796c812789a49d47719c421b321 7ca5fac436230d668e5e91bd26293e4d97237b0f5022a70f61c844e49f938d6e 7885b638012b4cb5b6c041d64860ab4f1824050e5f852d617085b6af548056cd f973ab78ad4d38d8c35cde6f977db62d5074d9cbf5f0fae2722b88da17e1e260 64fbf1bffd18f4607f08eae533638cfa9051a3a20e9fc181dede24f181001021 e1ffea0ae3a4c394c19cb84a61a401c183d4e08544a5ea8fe324f477f0d712b2 a91998f76f3999cfaa3abe09a288c1fbb024f7eeb8d3a2c7ec81925d68bc4c34 9f12412424b4517ed6f5974d856a2f4ec3b908d09bb8bb33203ef2c45a34a120 b2a639f97e1f2c1d7988d5ffdcec52156b5cc7e107437c5295a43adb582263b6 910a5a25df1538a8bccddce6768c9369c0aee62c882b1a53697e6f87fb5ea0fc ccfd19adbb5009f5d66842042d9fb4d322d3dfaf455625ff465f7f39da96d3c5 867290032d9998675fe2a03bfbf4dc5f0218f60ad676932fa38e0e29fa9eb0d6 cd63915698b4fdd83f905320f025a48f037569217664d78ca5481b38f26e5b67 4837835fdd034d1164902ad97a0e179fe4995101ed105d54f47d6ae6c586fc10 a3f329331c1123131cbed064658127cf8649cad48455a89b67878ea496697992 0a202f93aaefbfb1de8f9c6b197d9149a77cb0f83c94fed583f6775ba28b468f 83af3e6fa07b82bacbed4604e9aa5f93d87e197582a4e1e9ea291d1e87c91158 4f704de963020ab2dd7fa778595bb5d7d04dac159432d5cb4a9769edc1468fff 4979e38d09c52e6486219cb4ba56279ec5a4c9ad5c1aefb8838f20cf8c49062e f43c98d1d9aa9ab776363796f5d624291dc8191ccf61b966ae96ba3d8e193cf0 6e0dcdfc9da577d3a65ee4d07ccf36cb318c17630e7ab75c8cc70c88846024fc 3c1498b30cdbe4fd5382c4c33af19513b4d94f1e4415734090818cdc28bc96b6 aa833bae5725f7d6b12c00c24ed17b8278ef8d3c0f6896e398dfbdcfd8cd881b fd2b8997dd337f7c95d07048cc734f99cebd5ddc2f07511f2bafdcf315dd0da5 3dfaac14fa684d75c29b79c59504a1625c0d366234e18c924268f888b6b6c287 6b80f9dd9a18d2d08aa9b1d3cf26693100de2196d7d39e4fb61ee62fad0288c2 16ca81fd35dc093db5fde18d078ab5cf02834314781de0b3d5d971b7689365b0 17ce1fedeee356599afe366f82585afa871dacf7c537bb9693302ae1e0e43d19 7900aaa2c7aafdbb3517acf4c2cee430a12db0b1018326a23c460cac7ee41fa2 f7e6394fa50909fd321c9ebc8c20ce332e2d4a0c7fbefb803aead525c7e68459 0e8d57825aa73e10d1c4fc97926cbc9057f89cd7ebdea5b91bbcf881d7e939b0 d1325b42c4c74e9d097b08fc9ca91333dfeb4962e149c412498325b07573fc27 db8fabaa3d17ae296b862f9e55ddd7dd26fbf4eef5d6a4615dbd79da01a98281 76c4d4c0845d3c4025b6af7937023cba46ccfbcc8e7994a0c199dc311aa4e728 5811d9bd04df184fbf130c8e885fb9bfcb9c83b1107d59b74952039233d6bb66 c02c25e3a016735e293cb059f3669f98960af05254b93c5cdb0338e15b13f90f b2ce47ef9291a75d0e5b15ba75264a74c23c88442381b257009660bfa9bfd7e4 266f53ea339d27d7ef0946ac53b391bf4782f633164019d9d5f5d57e7ca0b5a4 cda0fb76563a5f9ee1ce5725929c73945ebd5b7edb1e82706731b56b1393f1fc b7d87508f04250b79ff71001b0d6f3fff67cbab8deb7e412d356b2cc9bd28a02 4b247111d326316d70b7f56a19a26b3a68ab3d1975436b64d632ed42240703f5 a8e15d7461048f8738f6de064df6e22b0ba38289504e07b39b3e5dc53a32cbee 9d9175431c8a952e7b9ee50b1971828fbc6d4f9ee90c938e40040fe348d63342 ed0aaede3cb1a7a03b8ce804fe70310a0a9a7005452ac8805ab680b965da2d0ae36accaa43b5a05657c7068ea249ffffd04a017280b1f207b0df092eba737305d5b1092d01f6840f4c34dd8a393a3c51fd7b95b5003f9ad18f2c439da78fd00f871a6f0fbee19546d41c84c10c6945024dacecbac3b538e030a208c314f8db0a73eab4b29dded3553544c18029ef2f5bdaf77be9355bef9ecf73ff7edde2ea092c5287844064fb2ff589dfc4d6b170700adfbd4e6172f00d2f038242fecd690e668e4c1eddf183806a1ee25432b2dea101cb690a86e2f93ce0145e6af3e9fd0f0948e3685e59fb86269601acc407f4faa312cf8014509d213c52419caeda2f038405e358f7470009a820a28e9b4da33b648f865d57b2b1406a0b4dba4aa94d0294cb60434447929278603b496a8331c0600c62b9e741b37c220a5b1d96acaf0eada3d1d0173027e3be138a4505d7462592aa9c7e28ccf5da6b89ad4df770320f03156265bf42c65a9f83bda74204888409d75ed6b16583e40530e75ecc30190f331d45c0885cbfb581185a1bb598d37c45bd3ee78fb6f86446e62a7d8525b80a7b7bdf631944778e01d3b66a703c3999696737190b8fb58ebed2c62cf393ba0c9107e8de22d6ed4e125521cbf487edbfed625d049c91ac6fc355fcce015a1b0ce974c1a11a75eceaf82b0c186f558c24302160910ea6f1cd758e53c252ea4b016336b2a8361d35601843136520fce8bcab71a3ff6499a00c2b81053987fed8094c891efe636513d5834bb924725a79ea48e98e47ba12f4ff97467d2003c0350018931980a750d29bcafbc71cef201bab391318b5bf5630c696e8b9d2ec433e0aa15c1d92b521d885ee407b0a23ae34f25ab155baaa0c3492f86e0eb27c4a0905575913a1a00b2e86b02c8b8ac3249c1eab6bd639424d4ecb60262b43cc76ce0a38fa6b82f6b1483a598a7a91d245287c925cb15463e2b9e29c2d287500dbe005af1197d0c417eaf04cb6a6a9a54c154a33e05d7556e56b0527ddb631292fbe0ff5271c609c4bfbeb585b66b739b72fefe4c6c0fb8cfb6217f0d59cffbb945f0309be94ce54e247643d5ca5ff39469429b4d119e386e4bd885f64b0555fe9e50ef970e4b2bde1c5a6689dfe4291d86b56c3bfd76c3a3f47fbf02f35c0b0ebb9096e85abc069c15c7d40681f11300a77639e2803a81f0a83deab780a20b19110090b2746bfc059b850164526de3c13e403a1e52f2148ec822c018a9a733dc9a701c1917a4ee1a07c16676576cbb9fe078cfe54ad388879e66e84637c8cded6340d05b55e0afe34160ef09f1bd3fcae330fe9fa12379f8ac918706562f79fee2b04996331ec49a152a820446c4698582670c8637d44aef9177a1bc261dc6341cc08435f47d81f3e32158e3d700c45eee59dbaec777cc09d25c3f39b33252ef73c0287c9eaca5c27cb629361487f5518ee7c7a6b90a8d23300c0c23555a30d7b4c02df0ae383380f7f987af1c02aed6433e37243cddc2e11854c02ec0e1d276d5501355c1c700f6e40cb5e35bb2cb1686799ab2c93f87fd3594f1061269adf2a2c0f02a19454eeaff1b4e0238bf9446e8ce19c4d45a19760ca20068aea1141952806bb7a245b15182ccd1908480db9846229f510eef24f218caccac992a5e05ce20154420e4173044e40ce20b1d14f9734bf2ee000cc93fef15b72ca1b155870ba0985fe0e7f81d3107474cfbf6f1d419cd3b70e817874180e99bf9cece130b3a90f4e54e9cd132b8b023301debb50d76f8a65eca671e5bd6d7dba2ae6a66f654c0206bfb0e182eb804e4f1bef3bf73a3788ad48df6f11a78dbe2b68d0da67fa540d176c9fed10283b8c92e14cbad4803bb984b94d7ee3476e987cc5ed1d7a17ce0602d62eaa1d913cd04add155e8bd97d006fe20a1ed461921a983a93a92aff460899dcac807346d5e47d0a8613135c4e7b7984886bf42a2909be311d47c3e8c609f9d4060e5c616ab5ed688f53ccd93570bf5f1f232543b0871f15bd627af879066e5efa334772dffe1accdb6f91d8dfb14f3d62418c8e0d8b01951ce1892dae01d0750568d7ccbf5d6974ba56a93e5926130bb3c96404aa78401c41d5e803d306441ca0e9cdefa7b04e588570a85c0d9fab1c2e023351ad7da88135d980598504fcd6b27e3310917499cf27f6d4656aed7e35958052d99e854ff7324351b8200ff6ed68ed880e573ed6c65daf19150395875f3cd39d861034c0725aa250e169008083185770bcf16ec8555d5237a0571cab719eabaf3ba284054e0f3d2ec1ba0da7a4ca0f5226b419506aff194fef4402eeeb3c67ac55a6908f076a240039a3030a6c4c9e2107e7b6f0c28a6670e6c0be21b6fad6087fad5110e73b3f201de80bfacd7ec711b0e91c5b8808411338d37f1a1a7f05fc6013a7d4fdc3ff2478d7021fee68bf4d6c47264650c3f1582b65905fd243175bfbac5eff6aece8dda118053b80570628e7af9946f8fb3b657c76674003e2cae4465c3f7f351a3719615a015a2230ba6765989a7ce648db1efbc46e207b62580601efb6f8ba15e760db8d0afa2fdadf532556e63b3d9e7451e5443dc05e44f5dde2c6781f66e98c43e161019a7caa6ca13cfc294785308a2b62856a685bd7007d1ad9dec1349383a5b263055176fea5a4ebd72148398d513b9d39f4616c982dfbf52b670dba3a0e8ec2d4061adc775c2c9f49b15f07fc4e0d6197ae0b34c36616f88bbf9b52b4498590b208f4fe7edc1e3818cd40dc04622410b93f1bedb1a16809bb626d5586a7b096cb087103469e3d2911a593141097f6241a6b4aa5cc631eb7e54025a9daf7e042aa068f73f2cd67b6f4576ddc75d616fd89633221888672f93d7a0adebbce17183805a7104285977b1e03e2e1938cccced0727acfe5a3ebcd70f3799699979a38150e19efb4150c2a1a83cbb2c2ab883dda30bfa3d81fd22f6da9b191795140e23709d3829f2502e424cb8c2c6b5812de07c449a740812285310b28fb117387a0dc05d130377f74fea76d4031453b14da03c2c98dad6d412fb5d4c382c7ab89fea30f5f0e4b054558cecbc3ac3334ade334f9d9bc53df9268b15d03e29bc805ac5304b18fa88663c1ab57fc220f7ad9687abe14a91826381de6f649f27c5245061900f032336054a656d591b8bceb71d851bef9c2853b9c75407d3c5504917386ac01fe48337f6c34ca85a044e20588e8e389afaee92bf651e2decad99fb272b2890436a830b903cdc75c6ebf9082343e2d0f21ff21a9814ca2e54b4e25d9b44ae1056757bfe40b17b7363ba912fe30a9577bf495a0db072574e395664abfcbf8aa0a18d655c26ed8b12447709eb9834f7fed487b71f534821261067aa8354f86c20b4d35318586a65a658f998cbb8a6834e59aab1f0a1d687dfe11ebb43a0f131702a507c4e7a1333dc85651309d4b9dbfef35db2efaaa423a97a7efdc4928987d01890d346508370a10f27efa5c6099ee7fb1df3f7d121b0136d00ce37ec708fd06285cb0f6e0704c28be79a3f1205af00c38547f406cc504f2f8acabe012eecc0c07b9da06d61394b737932fe4bc0cb412de4fcad2cba7ef11cd418b7e47eec00faba1883de2e21d1df77100097ba1593cba3bc9852cb6daac3080d00fef6f7a095a05568b62c677ecbaa86a4fbdb2a959de1c620ce3c74b6715e2295c4e489f0f96d3afd044faa3c77fda7936574015d7f137d6073ae95be2a4c84a2514c3280e8490e0c950aa95944ec534749dcecdeaffd1121e92ad838c3e06ba2b7974110b205a1dae71b1190f238fedba6a1379f5ab555d5797fe78309946ccd7f46a5a018efe95b6911c232e77bf5c67a9e1ac9ff8751cd0b25e963686acbd0ed5381803b92c1156fb181645a85c4127741424d0f692d396084e0177e702965881e98b0a6caa3e5af1421c959a59c5f2a875c3174efc6cff9f24ad88a15bd666f06cf500e3889b9c94c89aaf56be50ccb06404ca76a956b2e8b55af420a5609c1bd03907ab20c50a9fb595cfc83c5fca99651cfd2c77c9e76a8a69ee01d52bf06045c607a22adc922a3ca33d5576c6c6722ffc19358800ee560921fc9f9e86c6190bf10c1d4ad4cf136e4eeaf3dbe3c734f66310b28644ac09cfacdb9984532e9f78fa0b599fac99099a53957dab9a2cdc88dbdbf71a53277aac1a049266ba1311f1110a13b6a2c598b8c9b49e1d06ec504d5e59643ee6f0de05d47b7a6178ea1f88c807936cc6bd75ab052a88154495fd38a28bf7670898026cd18bc66235359a297c0f8b2b08398950a7dc7869ab49cec75abab5d02a0ff10d2f48d83a79df30176300a03b8284125c88279f0cc52c36df35bb562f6713a947bb4f3aa0dd42ef60e1023e55753ef68cb4282ea920ca8e1139513f1e77e1f4c2fd4100f6930bf831550046853cca2fffe17f313009284630d74f37408f05b11f7a2b85f1fe31e559e30f4e9411fb72384fe7dedc39089f75c20c5d5caec0ec10f13226ac5c6cf42ab2006706e665673ef24a055edc509a38fb605c381b74693b5f82a6b684c6f936ec0c2e49f1eb609af7a9d282551251bd18218defd68048ccba2e36104732f8ca0a0e540eeb30f223e9a267dbf15271d0c0189346e3f66ae9d714c57583f9e6e4b80dd1787085539f32830c0ae65b77761be8f9acb75e0be23d7069721cb18d83600b167745080ca647eb99e38d82b5ef86fe4f55a8162ea411e5445055f40a5e7e0cf85974ada0333f55cc3c6c67330a0ea2931e0aec2827d6db70d1b3a8622df20326365a8990d5cae7269a911726c6d0a3f033e6763903e937dae09f16e254cb04d91c80d748a77e03831fa3ce78029685ab355a8510539d3d9798b4334a907e0798582c9557bea0c084e556c44d836c573eb7404c2c8b958d954c41b0d0353f08d6b66e90416a0d7629651f1f82b2a1ba672793ff059743a9147f86fe456ded0cabd9d2273e7536e0c31fb796cea7923610864d819ffdb342e803c2770279980af5309edc2e1b2747c50ee2a67305d0919a6739657e3174edd35c762563b430036b31410a6659d93f05fed79a228fc30d598056a210cbfda722b0dfccd1ae230cf2a14ad94dc4dad353dbae4fda7e4e916b6fec91a1fba9821c766f03fe36ce072ba3ae6c5d03814ba9a21756fd1e7f769fc4ab4a7a41ef5dd128b3ec40e8722e50e0a938ba7be64aa4bd40d1fbf95a079157a10f198182ebe048b0549159fe0096beb4465ca8b35c137bf127615507fc1980aeb0213cc6d31e4ca94d16731506133639d3e13f4082aa490c8b28980eef1dd23ecd55becc22079a2ae08f13830c9bd5e85464578704aff17892f38bacb1173e3fe69197274b2c0341fea0728f0b06096e448068ae249b06f475ce85f07edcfd675b943c14f14e49cdf470ba9b0646cc55dc21d10645c55656b7f1afab5139fa520054a5a7b578a37a67986e980015d6814605586a87bacbb45f1d7beb65234b870fc2aebb32a2069433ac3b390048cf025e29214bf0397638b416ace293ee8ed8afcf2d3de84710a488c1d0ab07c100d601a4830ccab8262b16d6f4f9435f268db97d4f5a4941113c6e2c76aa0aa2ce65e5cb2455ff6be29fb3b4250e53212f06a9f2650e1b8d4753e179d17f07c8e30ae8a6f8f024627735d6a9a82693d89367c89d75b48931476017c4fda60128bd629d1291af97fc6959f7b081bd2d7ba1257a6c31e8df18bfed7a90d9300fd38820a6d07507574bdda8a5ec2665c854a4798c3ef52681e560b85f0cf0ec0663b5447079dc6d3c7a3a4781c12c1ee238b9366294edfcf4e6d633ae50692a0da66ad2126eeaac72c98e814a64090be3a426df6a58a7acf93c6cf931c0a24b00661ae51cb3f37569e2914246e576e5153020a1087e32ce322555e896c3cde105b3e5284486965c05e31998419399f0ebcf742971ea2e41290acf8553d79ae603a4dc3e46ed42e6884a6c0bf0cb41266cb4cc9f211c6082532042534802c85307511b5f51bba1890c5380c1b9dd4d1bc0d87cd29a7400f0d5db5c5b49bfff57052e47e33178215fca780215e45a635a344cbc2b25b939e4774dae8b65e4a38405a6f322421f81bf3cd389fb925fad3ac331e2dab0140d4c24e40a59ce7d06d105a702ebcf2497a6e8493829133b18d6be40c1b1e87c92b49c52efcdc80555e70f9032e028b05014a9a7a8b6b3eb2c54e655df4f7ba3d2b5934e5b7f6672539c0a371367ff9b5696726e2440fece6eb75e540301615b5fbe3ba455bc89bf46880f26e62902b3cdaf4b5f1ead2d9b53fe37e70a420b3e375e63e00947c1a8e7fd0c5aa42236bfaa77d7bc66274d3889088d738da98cc19f49cce32c504fc926cb0ca9aec1794ce7f7960a473894bc9b98ec900a1183c725969cd75a25078be5430e5b0ec41b1020467b3a5d637ea0a4bccfa3a8803bf2ab193912b82f6744aa2c03da4e31c858711c50c34bb7034fd6fc4cc7aafd6edf9e0842c1d4fd663330e930d75130bbcb706c9d95e5da568aff5ea00408ee90aee3f09b93c12caa2f0470029d9d59e621433fda382c460d1a7581b64540bb7e670e52736abca3dd38620007e54558e0d445233d7f2bac8c2298c1eb82956ef452d8994ace078e28ad0f06044eb0ee9c5d144e3e815097f02ea113e8ed982bb895ecea58805ecb85eedfff001a3020566a1c5c3f866f26cd48aefcb43cd8973256ad0f022f4b2980a52277099e1f854f1f94c8805712ea0db4d39cd4713e17c2aab483d610b52ac174c00d0ffb8896807992b5f0141afd333059d13b251ed61d22cfce7e973764ffde00af0e7730c66bf9ade77c52a730d23f89776fbad594f5927ef90575e3c6a83f4a5f0602bfef284814840990e424faaa0767ecc12880fa77448ffc3341933830e088071cdf81f9d2e0920d885327b7108276441661b684ad6036022968f2627a49750e5f69b77413c679f0f5f881be32a4a5e29a3652ddcd5a093be2e6bc68864d910ace806ab797115e2dd00a124257045a7107f7a3d8cd6ca3a3cbbad45aa00f7d00249f3422351f2a6c63e4d1d8cda570bcee4efcb9dca96d947aabdd13bf37a807af4d23264c3dad163183bcf1c014c5b11e40b673c0b7c212b261f421510f230a2a9298c3549e6cad59086a85cc328a81e1f7646f74c21764b61a81d025792404a444db45119d367142fdff17bed3c15002478e7e4361a889f4161b885ff10c047377ec57f120c797debc238d01bf7388d469ddc9c76bdf8c7b9b28ebca083608e93ea85110efc524c397a40418a566e5249abafb8c4ae6bfded862bacbea3b0b0d5f63b8b1983ea2494be00f42719d4b7d8ef468d5ac88dbbfb86356e9a4680d0ae15a86a62ea632823b05c12235502f206bdb7accc7e05ee4e06776c0d269006916ccf49e5b2f01fc2cf04dee3f87deceda6c5887c1d56e69df0e7288d2d10cbf07d2aedb49011e43d02906f8c945c99e907121c38118559dd49e67fc30b602ecd05b8463a2f8f9dad361aa92b3847f8279253d4a6df73bc7dac35bd0dc7600716858d834ec07b81829cceffb19411acf8df0c4d309f698634910a6a84fff0d7f043a5d53182fb22b32709c4a8639046d61b9c0511893f8b6839c620b04c9029bb785cbdc98c2433f98c2fec73ef07c8ffcf14b0e978e35f79502f2800951099caf8f53cfd19098ca6a93bbf7a7ed752d36ad7b3bf18fb26146737990719f03d2491f299bc257039add44ffacba1ca99c63c80525c901bb338cc40529ac5c01eac6c473677a2f7c48e1a491645ce86ec3965095d05283105c87a3a2d5c2740db18f16e61c9859ff7a5403e93992d24d6e2e219a13853eac4adb6e86f1e56d01b0bd01ab66504f3797e4fa0215d8ad64590bc463f14114084dd9faa03caa7a0c3b7bec66ee286cd7c4da0d49b10202547b8326ea8f71dd40d9faa60255be66018ca1b59a026d828900502486cc762ab6a431c7038ab678bf4edd21e4dd70bb01a39308f9c02195e97852ac08d5eb9ae22533f359455cde5b41c458402c936308b48683887e4ff45308557aeb307cb66b6b1e7c126b7d428b2489fd221742b60219acdffbb0358502c2cac8b554e953cb19f0ce8ef601b020089c74d55ed0900c7c042f20aab7c150e104b24330421ee8e31aa3a65062ea62bba526649d8cf2064379e5b4e07db056d70fea37b2624d2545d001705311d6508f0a3811c8d2d50382bdecd8ac8dd68679e33ab4c3c2491bfd380109669597911f72ad5f17f1de00590fa7e0e0305299d6078740136a434f4191b19b4380a9479a8cd7b23685d208c33779ab1f8a9debbec3a6b5fed81ecef53a4c25c1b925d83a30ac9e4916aa0cc706a209d166e9ec1062da2b770a884d654a232ef8c8da187d5c236121e5ad07499ffd824e7e744d4603fcf171c17c6ad4613f8e7d047a37fcc7eb723d916d04252352bacd6dc27c12899deea167a28a8ca3fafd87f43380b65a37d400116f07f7e792ace74a5927cd3dad7330361f218718dd8f2b8b847afac4f262730d560aaf500d9b65ec1bfaa07af8bd73d38acaf915287bd511b64ab650c9908468160f882ae6e3840b84ccadd2d5961ed4f30abd4e34f0663ebdebf4a0582cb3cb820a82a5c4fd78c64ed0b41f28f54c0cde390d78a443da23e5e1c7e8d8f4cd4e6a0012336ecb712b9fe54a4f39335696426a91b9c7685391d4b4190539a5db6ec103aab5a901aafb1fab7a313bf30be0ef3e323add47c00b461e0b18c4e3f3eaea0f217fa4102c04e7a88dad0fa2e6f3bd91787fbe2b4f84ac1d7d60b929e2a30500055acce61c6017a4191f371bc7526da5a9887beb3d81b2f0f8ef3c75fee5020b18ffedc69236dfd5579782c730e99613247b3269fa9f30e43a591c04421070083fec69c1011bffe9b47c72aa1d2c9a3051ca0628d1fd7c46cad7a45f8f477908238fc48e41ec23755fe0c1e427a3c046888901de4f89081d35457b4185ecb20ccfa05795201ba64cdbd4bf0232535f914335104903335694fa0099163db8bc0e8520be06d8b8fc6183869d9b7c54cc0351857c130b9565767339cb7449a1270fe2b4afbabda1a60214cb2c05d84e54f324bb2eb5674604a2dfbdce06a5b0d40810a7f7e6bf269fec92478f76be7d54efbf550a09262c19e4261272404da0b804b1b9c0c704cabb6766dab338439af35431c4605eeba518359501e30decc62f0c504e0f4c3116d496d72cd8c404db8209cd049ce4985f9412ce0b413293077908e23152d654dbfad2789a425952816387b42f6cc68f6b0c2bab38e05a82a13f07d9b807b4a43a7d7db83e83daa91ebc21019135014602631bfe1691961ce0700828666a60a31c9c509bdba70a37fd24392e08b081da128e527a11740807ecfa0389f84640a17e40565a2fc382360687a5e950a0ed26d2396ef365d1221f6882033d3d3f9a2e359ee63a65b65f46c7316a212d76379fa6dc544e6b80a385560708d2bc6cf56b8af5d9c2a3d0a067a1b489872bf0fe414c0219237ace0da863560e95fec01e0ad23874e8d27b6ea48bfe0725ddbec3d7131d5b4733ee7aaa9fd30189202d36a410a94210e69970d9772b3b460ae8b53d5357acec9d961bebd57705e93b997619b8ee21268b0b34d08c925d3d697c0acf738e24b627c09113ff930891fc485d9579326e330527a7625e000faaf1baa0bea630ce813a6ac9dece4e08b078aea2116ded041530ef081f1e0f39e6623e2b60ded7685e47ac002d527e05e9edd734fb6c1cd10a77d59de587fbed9bff3c80e8906eb410b58eb0814ff908f72a0550c60d7d03bb7883047c3f9ade6d6a086b78b7474635a0d25098d5fb0d687fcb328511812bb30876ad68862ae61393fd08b45d4955101f6b0361933f05a77a7072a49db9573b2eeee482251abb6efa8e539a6c468a1677fd6072a31500f7a503f98327d7240cc3b372dc421156b1b0b984ae3305b3c69404aaf86cac01f00632bb567adb715182b90b7ae8f35af9b27d5e4f154966b7675ea2337c320ec6370f56454d6ee674e62899b781fd030fbbf557ce3a3265ac14a4bb88ffe40dfd02e0f3575dd6e333ede6242ca511b44634f771b7f143d24003aed45fe31e0b870685da9aac9ac0d904f4f96b40b8bdc4aa70bd3ccf1c5d8986893848383503507bd448cdec341618a35c2d5459449013a13d8d7c5f8371422bf6da186aed0fc3b9fba4e3785e9371968d275439f16f23d2296cf918899bb98b972dd05aef08f6fd78fcf72becb124978ebc03d2ba67d3f450e512e5b49b62dd537ba111be0012891d4c521bb3db9be7e7d8416012f50ffd7caefb5a7b0082bf6b45fd5ec8064acc6802045e2004bcab901044dc92b18ba950cc947f850bf72e4627d23bf60aa2f1ab680eee07a2ab284ca3feef7b9fbd939560e747d586e895e1629de35502698f3f942bbb7ccd4c4cc6f8572c3226ddfab108a53a1e974bec825651fb950006d805a072416417db5dde705a57b420f9ff69389c3f438b9195d1e90fe6c305470a8195f567f5c9cd72bce4a8ef8d44dd9f4c3466924db72d30eec08e33ef0e411ec6dc6769b8d23a8ff1512c33850d44d33ee0674853fe9d2266752e557a0aabc6952f1b18ab72c19e56ae94d5b3163d6bb8bdf8b7ee824acb7be995dfab0902698136e0022a8faa2a52a76e946418267c09a9c767567089e5fef68583a70b35b21e0b5d085548d182b75f3b2c2adca02ffcc90c26b320756fc7da91dbca0a4291a96ceba3c7973f0af8bbd00c5f8a84a4b3a895dcb2cee8b427c8d50fa001d93b0ccec944f779ea4f2bcc2dcb002741dc1d367b45eef5290fc13d12a1fe0f12364a82a292acf7b810e54a33e6a03a4f425abbf3ada5b84dd42bc79a926e0f4a1588f7deafcba7029651d500f2070ef1b59f342b0baaa53fcb8f2f60b58002cc6f5e6f9bd65301fc63730f3cd37228622d97566ff6b14b690b6a70f7d68309a97ab721d1c16bd63ce547ba942f3d7821fb73d0c4f1c79db599abe971584108131bb3ae1814951d601ad8d7b3f036c558b60594491f77eebd594e3847438e0ce243c3077dc432fa422ef846eeac0fd4cb886272f565f1fd13a7f3c2e1f46a02cab6a773f7d2d935fc12b0d80da0465f59f1d0779f4f905e365d27684534730080eca2eb76408778e850da57e5610c5426b90f275a0f168db30aabbf13007d09cffffcad5adfea4dd1b80ae82eb25e0a407d291a738dad38aa40994dd42a5e0d17e312b7adb3b594ff6b9f9ec62c80f00145d820d4605820c0a9146d2a0abe0ad31d0abc589c133f80c303dc8bc1b8faa9c37b274185b52563e219425b5795081beed6d33b8a36c10b453785df1b4946ac0df42d7d413cfbbaef611432a4b4029cc4792ddd0b7a5c03e7ea46ae8d302a73627fe79d268f35d7b61f6f709ae605c5ecd08efa54a357a19f801f6e8b8c34ad41595bb4ed473b47be4b7f8bdaa00f894affe2135b77e8f24f7e0f1b828cfa73868d60f6e45eb81518e1568b09bc058549aaf0a8118e3e838443bac6880463ce698c17dffff1a6675ce16162f293087d98bb210cb2d3c9bfda3a5ffd60e86e9098221b8911dd9a6e03ee4d25d8dc00475bb87d63811a205b58bf0bbc2c23df9878aff2378d2b620ffa818c83ee020428aee8103cde55df4c65244d99d36b70a9ff3881a7c34fd7e85ad6ac213c870b278abc9ebe992efe6e88c2577586319cce3ce7d23accd2176ed3934bb9781102f6b186e1650e87748ae0a3812e5fb7a12fa195e8de5930f1efdc9c3fbe848105c18428b4bdadbbbb2b7929bad616bbaaa43a586b9080c870d1ececce3dbcf10d51fa84aeb3d1ffc503b5834a14115d93c2c769e2e1e803ee54af2bfb47e60a029667bdafe2ad65477cb794996b45c2d03b442ba51619efdff72f75cd1d95a70b86dca981d0b6c13eb779002043cb03ce828d024882d31ffd58f398d9de173e0a1af3a35189298f79d4e7ae280fa14e2e5aba654134f4b11af7599c538b55eb01ebec84a1aafccfe0f6426f76102f24e0efab893e97510e7c47013472a8c3d004b4990dfc3e6e547f31e6a248fa5c9cf906505d9b8f76e9dc348bd053c927e302ce2f37d4e1a69c435be78f4f567ab37ca6dc4895f8f1487d5c02b93ad82b1900cb11ea46d5ec484b506219a48fec97fceba151d11858d57b5a09f3b856a7910713b44471980f9b33ff7ac170ed2929ed1025a1011e8dfc3efc815d924ead4b08e8463d6d555993cfd278787566fe6d7d1a2b04ee0d7f63c6e0e917903b3d970d896b5478a6078a8f2f122fd4038c4d246455e54edde735bc7f9c47b258ba220dd4f7579a71bffcfd6cd82e1ede137cd0936937da3da5b7cee3ef41b29f51d2025c04a188af207f0f4091605560c2f0fb425d6b322021493841328e1e36d102084bf84ab56841928c9ae1dfc1adbc9afefbee113d0e7763ba2de8d4f7a185bf06a6ce976b82e766383d5cbf8aa53b6c41fefb3699e149c56f160fdef91616b206286d9a584f6e53204ba3cb0391c0d790b961c89b6dac3d8f45d7dd7c991bbb0791277f126fe966ef9986f987bb82c25f43fb343545c2e30ecb46fb3f167f24034bd82da5c1f7fcb1de36c87bc8bb21a016ea2f2f61cde5c692c12cdb24011c08d1886d9fcfd1c7a97b04758207f0c53219a46037f30604554e23dd00ee9f5304747ee58e16a8c162db0d584ee0a55971a6648d33d2749d5274e3c069130d0f0f7daaf64a756a29984513c86af1b1193f15fa9b571eca5de4185d6f97b4c5290cfb89bb55fd9677c88a66873218639e1319c7fc4bce398a5f435a6ed4dc4430093f2c44d7c3a09640ea96126953b81f6fa96a3b174cdb1f5b06d7f8505467b504b52112bb66cff85f63b8a97d8a21a50d5b0e4b2855d04b27ebbbb46c2b31ae02f5430c528dc0758ed1cd23d1778440516c444781471ae43d8313e3a4bdc1680da89f45d007fade68677acb1fe32f3f5abbc204c6186216b1c7314d4217d71b043d9b2d6682727ba472020c34698179a3515769639b17536b8bdebab493386d0296ca83c73bf9d67a6cfdaad308a7c00180cd3aa3c902063e5ee74a391a8d070b49ea47d584ad3bc79e004783c7887d89c0c69d9656bb44f136b60f361188b40c598eb2ca0538e1efad02ea14d1f925eb0de6d1e058556e7d7e86cd59602d9a0b37632a037c588a0090beb0f7320c61dba5ddaedf1788977c657df4e8cd1aac05ac466ad907725255c8d7774a55cdbc26c6979b6e2d8d6c6103c76bef68b855018f22c6220ffac152dd9d06c57ce0b9c4e55e7b6c8b631e18fde70e392c4e7d0e43122f6b17451656ee3854773388757d2c54c1883cb6b0008300b3810be0030a6104c80ac7b471b261ecf558b47c4088ff79d0c91caac0ac15936ab95f0181067f1454113f1fec16840ca18d6bfedf44e18daf777e22d7e7698b787627250b099e9ed49cccf1acf95f87cc60e6d1c4c560efa9cb022335593c58053af7babb0ce3b1b4e2aea9e28b38aeedd9bcf2ea1b300ef0651fdcc3ea575d902a3da8600079700b7c3dca323f415dc9caf8a853343bcc086fd73aed9860d321ca510382068eeac20e16e747f517da5c56c31d79972181fd7c9d75ff8d8ce2af1c7fb4610b7cbb4e0f9b664f5e9bf4b20b8f6e360f43e02c20bb77ae411a42fa8b7bd2770e257ec4a1d3b8db61d7c649c263647da1201dbbb7612678001cdcef38aaaccf0628591fcf9d66439fb2612f029eef4d5dace650d516e0839087940e0701c63608c3d9f4573c8bae6fb0e4eeb9f0518ca7140d88f0c81ae473b0fc274472117e0f3eff46aca1880faa169e3960e483ea8487f7ee9f78942ed1090671e3f0662604ee81399c9021d3b8f47a821242b6f9bd6cc88744ab4ee3a8cbf8864133575807 false -check_ring_signature fcf8a0202fdcf0a7024db9e0a62e99f2d323d48575df4218c9dacdc6dc03a043 45bdc2b62682f22e90c115618fdb05fc708bf6e55c16c378d4cf6487d6d66ce5 23 ae7de510d614d6dd36f1a18193c24e0e0895814c2cf4276507c634402c95293e 8cb417877cae65ed0a79eb5f51f289223c6a2c6508d263c7bcd6a99af2dc2ef5 41cab6c84ed126913290fc9358e3d8a70af3642373740cae0ee89e3c2bb43b08 348a0f4a6ac5bee6e77342df78020ce2b5160f54be86b191daa9542f165a7929 e6e7c4e543f04a1351236458eed52da0823bab39425aacaa0f64f250ab2d4397 148ddec7258a0f631a8d6ac62ac7f97988e3e1c091c8ee5ecc00cd692435de59 53941c3d7571db7d937ee799c781565cee1130217e5ae09014a696bb41c7deb0 64d5f227e198f1f8249c71b3f9b2969809d45d2d69a4c01c94d9a635395c2e01 b9b8be1889bc692e345723f9ece603c925118d6eff2dbed5520a09e69c648056 9be17734209d852aa54f6e56040a353f3e735d28be6037c62e971b8178674847 e51567819bdc4f75c4d706b64fabf8c24978011d0c6eaca3905f3897ec5d5428 dfe555192fe74e99f220902ea81387c909bd6908e1fc64503756724ef244250f e9f15e665f5b402b9ff6e2bf5265285de026b9fe3a51e347380998213c661e56 3d9c7d52216543f28b1007afd06e0be9dd4fe4e43747fffe5dc862252da48ac5 d35b021155a7b6a6805de4fdb59b4c3f07818e804d85f81e3b49accc0bd9411a f4917df9979aff5c3013929fc2f8f8cf6cee048a02eff004054aabe4cf335de7 32095d4f0f34a864e1153981f39d9bb40762ab6d5dd918c43a0bb8fbbdc2d7f9 c541fa21fa3855fdab64941bb0663dc964b2547bf3b41c380a500b27641ba87c 10d62d562db28c39e9ec06af06ead01feaf7a3dfbab3ad0593a90385af022291 276f5712f11ba4c813c20640fac6cf1a69c7569e595abfe9b385298a3e54e090 5b3e08b0dc7ca4ff9f5b88938618e31d8708a550b4ceb4c0f82742f75f534384 c35c53a6c2dd3cfcd81b6e95ce4e24caa70f8be089c470f9f3f0bf8510de84f0 7fd6de27722ab64e62460a514a197cd5c4a5935c134615d3348a07c462b70110 e94b95d07b3693da26285fb2219b39ab63913e1af6eb7fe9d3454ce548812a0ae096ef42af5d03f7647944eff81f250c73522a5c0804a2df616bb10d3f8503004817830187c0341aaa7d55a6b3e316bf9fa5b5de983fdc379067073a3064440504ec4e3776738a8688a22cd1b62a7c79ce266bb1e3939e22f167874494b04401a9c78e4fab2fffccfce74ea981e1022bf7d28d5b2009bb75ba6b4f7fa40d5f0cfd4b51fc6852d046b65b332d9579af3525256ac1e59487f12f92d0d9458f6105f28c3f71c6f941899ae4b836a94c63d9d55759835ee94b0ac610e4928bbb4c0e635a1b2eb416294ed0e27624b7d40853a383dd6c7a653c87fffc31f8dc23580c11d75151c96cef3aa9d1357c1076adf188f33acc9a089c160885fe5585c3360da6ae941837b80b6fdd67188436836549998850eb4f1ec8eec6dd2a9df6dbbd0abcf522c1d3a52a1ea2a29afc5f023d42cf93dc746048276dbac10969ef9f050e18514730dbfe7eb5f1f11f82fac2a7ebdc889969da93fb85afe08d9b02b37a0c9eccf38b472dc02fce2c3114fd01311ec0c8bc75e2a29c56932deb869ee59401e339eb2432b8a358c27eb5d12a156147b1378f5bf6b4234581f617ec1d808404f16bac9f8c5ba76f8e2a6906c5b50b37f25f3670141c5c4c1aa1b352e5d47b04208e37393618b893095701c49e52bdf47a9849158862ecdf4a81bc98da21f303c1df78e79783394f6121f956f850890f2dae85efd000af8147d839ea2f83c204d4711cb26031ff5975bba9d9b60b0b3e64f4eb0e9f864a126e6445e9ec92480cb6d3bbb4716be4a3c54544411f87f7a37c53e65bd1f7c69435ede5e9a867930983d6275eb9ab06406051b7be90c8d68c65467cb2ad0e95c4d576f34acd8193001ea4c6261d37b457361ccdaaffc8e189d7510cb91f4e07e5a45942993770bc0047855b3007f8fae2a143ffca68f71fbb7e81107660fb3ba3484b4318f58e5b0ef348bdffc235faea88300c065a8816a2257e366d939ba71615daed8551f6890a62e4209f307a946191145c4812ff380ec71b183667328ca655bef5f1cf9768052c38a11a6aee9ed82e82110de043cab8ad0ebf92ccbc438b1654b129c0a1ed0aca9baf3106acbf2aacf1df670398f10ee1d5b14f8ba77e9ed0c5649a7138620e985fbf187f4340bff0f8ccf7c9221c89144853c4007255036741e9a30ede77043df33ad8542299c55a0f69cc22d3dc35467b4216984c7ec41d006c8c5438850f95e07b6b5c26eeb49c6cf827d62bf765ad69c7ee7fc3bd060f7658cae95e4304c0677dc6c7ebddc9eb0e4af729c57ba6949c6b23e567409d426013f643eb210763ef8b38a18c2196b1611b9ceec27ccc3033b9960c586ad23da51ee57b737e0372fc55afbe3f502728a316b12504a6b861ac25e9652dfe1c0ea538c4a87570020c826c968a605d8b24d829ab3e639760973f92126f7427975c8eb2faedf4c4089d9508c5eab075a58479234803d6b9ac847d2935ea17d6d127ad3c5815a2100d3d617f86b4836f709113f032eff71eaaa1d4552e1e8a7300cdddacb7e7fad805b9f3a626f23f570b36c2b90aeb2a13f447e76fcdd7731fbe791cbe305412a202f0c59691ef261098cde442df2cf474a389c52577ac09b17e7fd399214854150bdef1d232d4342a891abe1c84069b7a5f665af061ea70578b863e2919d7414103da3a1cf001140501910c5395e2a651e1a7e23fecd821eff5ccd8b4c5253cf80474a9afcbf2612ea0bae607a94595a3e3654de8e10aef92510eff8671efe94c076942f4d4bf267afa8fd1d9a8204ba078c5606ef8b75ce1806d029b5af4738a0710b108bf1f4595900a3f31cac81f6bd5d28e2410940faaf6d70bf0db80050c0546cbfca9334fbd205d98121576ed23f68770ca166f0eec1b60f1db4222971d0a3dfda453a1616b80a0e5e78d54f790b2e1c42abedd665e37040c22a512d7e102bc6a4f239553bc5793e8cc5c5cb0c60c33e72203cf5e0bcc360010d5c30da9016f42021533d5bb685335b48e448126dc3a26b65e474b0728de17e4060bc1e100 false -check_ring_signature c718221353646ce1870913b2a79162bc869c8cdc8b7aa018daf80533fed3327b 935b194d44ce4db6d450b7b7b4cee1bf7317cd99a6cf6904fae44dc2a0452244 28 8b5185ff67a8177aae023032e0b6256e7195b4d9f578a974630a21772101717e 8e5e33e4202f1aca6a1900be0fe27911c6a3c0c3cf8095f13ef398e96d7a9cd3 f2ba924c74f68cea3a85273ab1cbcdc2477f3442d27bc16da77beb48e4bb36d0 71bbef7c79b365da3631997aac2ba9b870c9067f7424358ae9f4bbcef5ccc700 b368874db40e6e6701a0f42f611486427ac4be4dbd728ae1ee48f1c446221c08 a654f3cb6922ef823099f1235be8c63b96450c7cfdeb7479dd23fd424241e058 7fbc86d898f13989ea1295f0e4db696df3990189ecd81188b59f10e061326b21 9936c83fc75b73b0aad9c53e15277386eb9288d61cb7f033144f4e1285af6c7f d0ca67710040cdf81e3632b70a2dd60d4d0b42f417982d7e600b99c237ec6915 f03cc3689a2a836e41e120297a86a5afbbff8415367165287510300a4bc67bc4 7647c5d15a428c81942f6da056761e4a940bf8ee241153cddb2dbf9cdb1e2c3c f421ae8e77cbb643817f1939fbe40f2d9842a115312d13091acdc51cf700ed10 1091072a84840b523a5db4b4b9c190ead2db33eaa2d76bb34678c0c4f312249f 1e728e13976d3299a2a2b4042a03452644abf8d9c03164a6772b9ecff03ba3a5 43cfaac6dde1a3ef7ebed3c1d161f53e3b609e788d84e37e9f51ab0a2653b801 98d50dc2b67ac54304b2b4432cf5ed4de34f4625d8b97c0b7900b6de01dfe072 8d162f04d8ce5e3f0b224e9354f690763c5e24c28f13392b414c4d69e3e59b70 0a7a56df43f251590c2c5c3012231814c2daf2588b182ed5df1a9dd1ab1607ac f8f40965945574b87392f59a9e90724bf86327d71a1cb722d5f160e1451f10b0 01b578ba561c87dea3e92357e599c976e20962b0d6775c1bcb04f5592d031256 de4061fe09441905722b18e024c38b49ba73edd1f417ada7b0aa8f77d8660434 80875fb23ab46eaed1da6aa13c2d9e929dcbdd35e3d478972f3afa27039def63 ef04ae1d61b3dea09b742e526fbc6310d288b8393568dbd45692ee045366eb6b 3dec069c3af060a43cf1c396ed72847553f30d5a7e5b87a8299afe02109e7874 1adaab380427568e55445585468d975ccc00b4ef0461881582895069d44ba20f 48ea52d4a308895c011d460f5a3943d7d4f29e59414900ec0d0fd27a55e3f488 1685d56bfe1cfaf3ac92db2b65d9463fc8fcfcf56202f22d451e74bd568b8440 ceadcdc785a5678c28f1416ebb65d1358a7031319a9461c7b44edec5d740fcd1 9890b9971e4e517e5883c5b365f3e9f30463352e8e31ba46d6c8d36d71875e0cdbec8571304cf304c75657db41830238dbb75d415f18573b7060a76a75d301071f390fda292b7e8322bc8db2bce0402802c7234074fb68cd22f46546fbf7c10224ea65dc2d03f9e1bd1701bb1ce682a9170db8443282057948e0f4db93077d0e5a60be2086b5585f606a4458971ae0b10ea9defd9ecc60a34ac74520fa6b0d0e1cf98c910df18fd5d5ed5afd39fcc7aeba5acc974364c013bee97c546cd6ff09b720eba09192c6f56085e5947951d352c617922b19e3508bc7dd5b11a91c75022b8d6b42de05a1550e4bbcc48ef1c122ac13eacf5a79b6d07f05568122190409a43447b1d6f9ee13fcc2a4bee1dd556bd24bca321c9352a2dfdad9bd889b4c010476a016bb1c1556345eb26f10759bb0bbaa89e4beb5eb4a0b05600f3f6d7b07b17957460f21936184cbc7b69d716eb7b8cadcbbc549ec7921d0526635f7a303a1f25521655f7ac0c23b3ec8fa3d92628a7e3bdcffaa948c482fec34a2b24005ee803b5a898ab107ea531f4107b3226bfc7a7622fa0dc5cbdbbd6253b63a3a0412c058d6fd8b1356e47d42ca0ad66697c629e9306b612f8a3255cbb090538a03bc40d4502de23178bed247e1e66cbaffece939cff90d8b773623cb01d54e20065b6a1b8fbe62c7e429e37bde33de3f720d23bd5474f9a58f0309c6fa5d2c8f0231499edf2153b61229f1bc80ccd5ae6c2c567df34cc731f251f5da300f457508d2d920c6c8823832f859e3586221b1879a1a94ee3720d3c2c118989de8a5b20cd1d8f52fcb2c8d48474839c2a0d2c8eb40e89d4512b3cec4e46e07ac7625fb02ab57b7560c31b16222ee64831b3a6fb82849d65eb1b6f6e91075e38d80c08204dfbdf417a9f8fcabed7c16dd19759997529deb96abeb152b992b7221caa4b70e82583cc4f351db5d3c172d206745dce12c50bd9346d330db0cc4c7e2593e9f09b684ca375a1bfd53b36e487812b437f2fcd2d09bfa3a80aab544ff16f2d7c10c86ca1c88c250a288f09b978c308db2a25e383cd7ee36577f6cc2fd20d1a10808016f726013508724b54f9b346096327974b2a95a5c71010edd57dabe9db2a79cfc82fca4ec4a6666c91e7d53da9197017b0fc52603cd6f04e3245e6e94af810ce0436625f9b35748a461b9e2736f4d674477f0e99c24ae9ed2bfef5256077402aee8977fa672ecc3db3e06dd0e2b80332a199501c1e13779c5a45a5314ac560643501f48bf8714280039ff552c1128f595bc485822be719d5c3f921507123e095f8f4365492ff80ae26242d358615c2e14a70378cba4fa8942d5d6ac73cb920f42d4fbbce4c64ec3e4b9523696256bcaad76e9080f0564ff402d8aab54d5db0734c8fe88eef028cb672209ac17772cd75dcb68cb2fb83a314641e7af637cf3019cd7821b09ed984a6b5d335accba41d765e08cd59aa526464558f0f29711580bff527f143e092cf9530f8fb1a8d5ad35b76efe009f3d1c5a70ff595fbb2bf6070f58fd44ce7ce83ea2db18e00acb62df143bfea5be45b0a58eaf4bdf99b257073f579c89921b94cea9b1a9e6147725f02d8a9bd8d76c88cc8a44499549e348070d30c1066764625f98e9337bb245867bc42c3e777d6964573570a02133847208227f4913c8f19000d4d30012e4837763195d2a35b1dc95e5a0866b50a25e0d055a7c58622bbceeea421a0802e2d854fa4a18f23538accde22b991c3e18861a0dbe0622243391014526f99936ed2934591b7b941f108e2a42af862ab65f57ea015ba49e9f3d6ea47fa2e237c8512782f86c8ed5054c24706fc8667e79d54506042f7fd04927936ad38a6a5cf90eaa2ab0618e699d21fb281d60bf24fc37015303307565ced6bfe3edb9450b99d225bfc811a13b37b0f5269678a312c541b7bb0644f9c07c08de0523d5bc2850da375167fab1029383f114ddb2c5366660fd9b09733480fae917a031c20c237c5cb1646c765a31fd11b90cb89780d0ffb8ed9506839e8be740668605acd8511989d3c024c729c8dc8544231408b458152d6f240799b4d61b43bb6552071e455537a967eb2697b9a6e3e50b659917124287cdb900edff8a1441138e188320348c86dfef454f297257e1359cb3fef4db131662330626eee34dca3e58b68ce113df7f6b2eb885c88f03341fde0162e1881657db2f0424310be93af856cd3f3281ad166fb24a250496a5a3d3ac88540fea94bc335200a8db60dcd38c513d9c1fa5742c59ef152b677367e6d419be9187e1bf74f65607448c2917356bbc4eec9556ed3e319124cb43af361ee877c5e0596abc497808060718b124a7128a5afe3bf18681186e23fa23cee4b1275d6bc229f9d6d036ee09b2b898c8f5ca0f45d2fd8c4e2d33e1ae2e363d0a6292a4ac4e3f08523f9d1904b1caa841acae9b7230fd5c1a6a162453ca2b73224e2d5e9890edf3823257590ec0db67972279a85d98035b1515029bedd78ff3321649b5ca2d5f0b66927aa809 false -check_ring_signature 0b0ae2077f7662bda2e5222b1d703ad6cbb6b1980b112930825f548172610802 b5d3c62099c06d179c22bd15fda141b102db0512d79dad09b1fd551a60b84b5b 238 c836b577b56043b5a7e335adb9b8efbb5745476c16a22d2a3c5101e917b465f0 88952892aad7f874d7af0a353fd5309448f4617f0f48ec013c2ed24e1389bca8 58578dec4dfe363b779055b6093ca970bb0b2d7a268f697807195a329f959fee 028acc952d4642a30b95a8bffb20ab37802eac818acf14463f61573e0729c906 c5521667e6226c2a2f9c382fc6e3c73d263c139dee4e064642bc0fd18854de63 1a552afddf6b5413823793f9dc4f7ecb2d16fbe3a61b34fddfd0913edfe8a7ab eb1017e2f06cea06cf2559fa007563644346c86d9b76a56f48131998d8ab557d f810533db562527ddef02346e07aa46b9bb7b80bc00837a3027945ee1ddff78e e8a155bf6f45bf6c1c033ee946b50d053fa76aea0efade6aea456daa83fcbe2e 9931ce1e691bf92bce42b9b8a80aec82cacc7f5c5e376a0c51e6497b2cdd2659 760b55a0eb71d669fe2ba26fdda7d7f2d42aef7b4e4cbab54d39c631caf419e3 c0e5fe1488dc96552f5fb5c39ce083ca564c9397a14bfbeff0dddc0ee83f5356 cb1d0680a32d8e7a969f0e37a5cd106e7f110b6cb2d12595031e2a5a2baac3b8 68b26185ce302421ca17a6c9a39c578398b2f69fe59523b15db9d6d018623a46 42d9a9eb4254ad3bef5fe6419dfde2bba0c5b67ffd793fca198157cc22f0b3ca 3485f168175f41198cd79b49d34f6304824811ae6a93ba93c2208aa44af2a6e5 ab65afb7244880dc195c8af7c0c2a80675427f28484041101735dfbb0fd817d1 d7f0b179cae8bd1af637e21cd1b0d39d077f0c89d6bb31e0671d9059b8ab4686 96fa36abe158b96dec2e4de94fec46649eccb387b246aea886d42c2f6bc92197 869b2f745c88f381ff102541917e42b02db6231a92a285006769348289bcc74e b23bb5b751f07df29d23a9f7212506253475ff4a16ee662a41a549994e2c0d96 916f6243167a4ee51d00362f87074f55d6e85edef025ec05c6af5d2923b49d7f e09ac0534f55bbfdba1b1e532f8f513060551593310ef2bcdf0a98eb3641e9a4 2c8ed1fdf321e8169c39bab8b0407e9818f93d778c2c711f28377d2f2b2e55b4 7f2591e7ad4c7d7cf10b670475a077292541936ca690d5b9f3b5799cb8d3efe6 387c6697f630a76f9e9bc0d491c62b2e0658d2a33186ebe69f9880bfd6232faa e34f10a52ca206a421dc476117207b4ef1cdc297b58d42a55d6c81ad46c67402 3dab6dd5ced59a51f19ddc090332351f04948fadba30d88344bcfce88932f761 a711f2de039bcefe427177f0896a63b0cde2be4ff3ea6a5bd0a4196e69b69a91 5f63dc61e2ebbfd42f5268b3eaaec092d0a504545f1e66397ca91756910d0a06 9b05a30a51452c24377cd641a61770e73db52d4af7041cccb302655b7e25e035 5445b217b731022d434de418c344bb038f2f828174c585b208384de498c585e6 48f383d7cac2a830ba451d6015ae855600291c0b7a3924b0ecd9a10ca402df7e 1ae6d5b980131b179c80e0ba0518872865ffe21a9fd1f3a3891d4ba4767f480a b4197edcd9f2f43766addba8267467f686b455461d652b6b4322283bf76b280e 1811a0d80b54cb61c13c907a68fdece8dc56d4d3b838489be2afb06e800555b8 8c355fad2ae36c51e188375464262e16d94490699445ccdba42bc03b22388d73 e10769dab394879e46b8d7f4c04769a258071475e4243b341d8a5705a5d104be ece9916d8433d8555da6d802f1041846b4edfe56844225161ee9a3cd7cf83e53 219617bd206ef106e11c5e9eec9f462536842b5e8cc61e9fb3f59af55de25ebd a8f476620b5a88867548a76a19b5ade63e2293f8ac576a682f1db14634d992c1 3ab60a227b4a80cb1b4ec20f5711244038c0dfa4a40d380eb0c5583fd47672c5 61b68fe1b8bf7126a1466ba57528e3f99ea3159f641cad7051056b43ce25b7f5 65cdbb8b6cc44c4972d0ca322c58105fe8052af3f212262e7e1dd1cac70cd05d c0bb6ce9000a00cdccd45d4292813a3b3d0f9a04ebeadee449b2f2771cdd4967 26e83f7aa0e3202080e640531176af676be10baaf7691a56a28ce97150839883 074f087a3e42647e6becc7b441a9040b2659f2576f320336b36e9613e8f623e9 1298800094c59a209222b93ec076a292a0fe7d8f1bb2bdf64906ec928be16a4d 340c94ba14cc24dae61a9cd64154e15ea6893e7c09ea0b68d57cc485f8c78519 8dddc0656703354a02841f15d05e34c63251f0acef6143edb29ee5d049d70912 8d2cb5e2919e875b300243c4cae56a7e4aca2c3fdda0c62798201e76bd957f92 5646078daee837db178cc9614d6df4810a05f885210656f7eb9d79cba3cbbc90 846ed31c753f79b83853112f0172c4281c2ceb34dfd20f1eb3a4af916aa3169e 8b3d79778824b3326c9e8cbc4279752327c1bc4ee2650fc60a8a1d4e9ce0f45b 28110f0c1d133a4b3dd957a7e897542bfac43eddc0052009937454371835f640 53d14ba04bf383283213d4cda72f01fb5154a39a6a8f836f0f162d71f3c43686 3aa35828fd146761c4ab9461f7ced2d5381af51ac206f845fdec4c3d7cdd177a fe551e4b205bc38ecbd79f37be15e461bd37376ccd653c4364a8280b7952a816 1214879bf5ff8cd9020fff56aa2e9fd922017223f4d63de30236477ef02228ef 4f3f529b0be447f43ca21ef4adb125ec5c2f0d6a9cf7778fbb9860faca17deeb b26e73eb349abc766d25139d28eda9e5834e0ee14fa0e2039db7038326ac25a9 1c00b341e093c7065a1142018ec13b53b4e71fb0eac5d4c82861d8b91dd5b12c 3f776a05692e669a57084635c8968167fe2e1af87fa65937871e99f89bf4b469 f16cfcdb2a8af83492ed5fef970f4bc4ed889a31b4394b5d7e54d6f7a9a90bb8 006d5646b5b8b76643a1a0b29dbac886e1a9103ba38c20a6841c3f13b8871d68 c38bdec1730ed3b2b1b26e47a6ddf67157cc81d0a00e47123b01110259015f96 961815836f3e36238d33cd9396dd1bff25872436e5cfac2d359f9058b47ee1bd d9d27405653016e199cb9174a05204daf35a3478ded4aef4c5223215c7f63362 89ae4faf87519c27cdba3eb4bdf1e69e96d6e5bec164c4c8a98923b86fd623b8 0f34c5323d47a51196fbd0f940c1829d225dea07f4affdbba0c91202ac721973 fc4832b9ddd05abe379e201f09e2b80c43e479fd588fe37378c011bbe9581aed 29378aee1f918d648cb2a45a8dbed55eb75b923e58ea349729952faec4351ce5 e0b8c9e4366ed0cf8dcfcb1b328997924024665796d08733f503e77267d222e5 e6cd0a5a466ce71da22edd2516fe9801452f341483cb9d5d8f197a5b4a07aaa3 f232ee2f1e574cafcbc2dca08fea5c53864a61e482a1e2f0f7ac0e2f475a4fd9 d9f6934f8f8d100472f2d1feca174e73ff7decec861cbdcc935ff9b7723ab351 a05b0524275f7de10afb0678b9a3f0ca7894d5af8d2fc3b78a144989b3966687 1018cb7dd8667b35fbba580862c7c33201e724cbe051a4970839c04a4586dc05 ac32a7488345cc28e960c72ce41df864a0442f74c75d2e43a902ac9bd4d278ba 18decef621f97761a1eae28ab26ee14da402240baacc5a48d8cf35f17968cf4f 9789eb5f8d1f5dcf333f6b35da0b8150832ef477e2d8fda9fd6ded1670b3d117 efb24596945ce8e0db2ceff01a04d735496ff515bcc238895a9260ad78524959 c640f0a11bfbde22d40843a5520d9f56abe6c3461c61e4d08e90a2c85887ba8e aaa5110472b6443de170afdc530002c5d708c36c55f3a4b91579fe78c2775550 7db81fd2b09f654c25889ec3afb065894fa829ff4b0b9faf0b6f7edc67df029c 776ac6a4eb41380cb4c304c7214b69f6120f0aa09cf147dc36a4bc4cf04c412e fa16d5f2d085c466c8bc2e8c4ccd84c1276c6341c15d7c823cb539a4c60b0aa6 500f8184ff093fe4ac88bf2e801bf0bd6722a8b87f32d66bf38c45889bcae123 74c4010855c5aebcd184c640c7e9b41cb27c33e8e31d89b9a16794d4d3789fcd 9e53a00c85d8c60deaed4d1833ac0b7d7a5fc5c39b45d7d8cdb35fa39ccc3814 0eba149b52163c6ff75009c6ba7d7289bab1b880535aa754a971f18fef7fb3da ab6e6e7542d0295307011ebfa23581b4dbd9f30380de996b32f38000ca436280 19efed540b6d05cca93aa054e9f84b78d341d75835234233539bb0cfb30acdfb 322265c158ab69fa5b8e2c5a76593d648f199c4f3dac541748fc0dc046c1c4be 360050b593931e8e3230d7a2e79d7f894d96f04f5c6a3e31f7d5c0ff2cfceb32 d18256f8ed12b84a12f62aa1f14226281b422636018a51a7a966982d1ba0e2f9 894a50119814d3db021462a9ca56b3d35948cf18c37b7deb3ea95a2fb523a091 278b8163914d2f1412d440d2b700678847a39ba5f1f50a7ad3ce2709ebd7a299 72af589f8224f57a3ab84d0a69f1733483179996c3be35d0c9168f44a3b3651f 2c63b2fdf7f55b62632917fd1d8c4d8f7b989b6c16aadab5889dd2d424a25799 97ae87473c94ac1c25a3dd90c24be7be4d8285046e060e7b86941e36ab221695 3af3b99a1461974a385cd770177ef9bd84f36c11bef5ffa3a3fbee255ab0493c bb0de73c689d15759a5482fbe887c4927ddbedd5ed44289fa4ed3377098cd65a fee718afc352674c79f3954f89256a8864d76cec8e94c497a8ba0975d0f5a3f1 81ea847d6ca34768b5e309a5a1b912d1f715fb7aedba3c5094a6913a0f7e02dc 0023c603aad954ad222cf54738330fcb1989ee402889f6bebe5dc3f47775d9a6 50f1c3c93e359e9c80989effc15b93e2524772e458e7670f27711989cf82ab69 4e004e27fe40823fc76a8db72813dd4babc196233007a5f44ff7506c3eb243ee 144dbeed30c52af443ca9468f6dc32319db61b28e40df869a18ec480c8209b76 7167c4cd2ed88c8e9fb9083a0e660cc578ddc0b3b78e0d4b4c70c6c02a82d120 19279fbed5ca5ce1379df075efdd34a09d145fe822edf4e919ab19530d04b53f 586024c8ce450a1ed577ad010aa594b9123c5204829cdd17783e01f2357c6875 94da6b39ac2ca6dc64687eb6f6bfc162749b8a694b6c8235a184a60b5f814c81 d53fb872165b04f759cb04c5d428fc3e723f55203519b569fdf8f1e0550e21c3 922e0d6da18df43810357e6ba38d505ab1762a3d57b3b55efc902bb266dc6e60 1cd62f2e0579db4529133d55cbd277dfb26ff5352dbc261273c80c27a5201f9d 70f822aa7a846de6ae7d5141c8d1ff8c3ac3410d1a6e560b84c558a848dad5d8 8db8e8d03dcef052408c84a81843940bcf687d38e3ad268274424004e294f76d 19f242c1b041b6ec179cb20162fe5bdbb9c2fbad036f280891fc1ac4ff49dabc fb145866a9a69a4202c9f4f8753cf738a3e75434ff076f2da6f651905e85b4f3 0c8351a09095cc678c1e65db78afb6c75985bd7ad11943f62c90b315a32a53d7 18f5ee12195716a4ba5f43cfc213dddeaa2ea296936ec1e71a698f8cdbb8cfa3 8aa8d4c4b710d82155eb53b81df69b0530d9b6a740f56a3c5937fbf35aa5312d 581c3440b834c514a6c996d62ee985b7f20b4e4d4a19ba202565833c47ac02ad 9035946c030deb8eb648b9a2ae1ee556ae701b3deb952cfabee186d04b653bf7 c3263b6cb558ee49fa7e28a03edfee099592827d3a7a30dbf17380457b0c073b e24c9631268ddb418c5899bcdc293cca1c87c68fd8d623ae13112c53886f1c41 880e643914375207c118a2263c9b39aa705a7a534acdc1e0e94b27104cfc9d2a 734feea041197c63bb5abb32d86e161f39e8d1489aeda8aca23a0cdf271e9b23 80919380f078b614823a204e4fa1ec8f3e4e517451d4d6a012111804a021df07 bb456f5b12a617bb3a9e6c173bb95556f8ad9648fc0fba460731dbc3d47a19c7 05a7098ace4dfd862e543d3e440e37e9c30096aa9b9257ecd14a4b86bbe48ae7 2d714a0d7228a76a73d90c92668621cc1ffce966650facfd7f73c3a398d95086 96989b91228471ff5636f1621936df49af3f8f53691d5977e6a6e9af4c92956e 671c605f8c35ff06c04ac7d350438e93b431441827f368f636b0d8eeb7dab560 4b4be02dd6177ac2802e5fbe6e5fcbc71c1b8360b57056c7b1ed0e5bd44b1519 0717f93923215388815d85d34125eca9c5fa829e9f87120e045f64088f39e57f 23b2f77e7b4ebf39666822f98a50dd40f3cbaf168e8a39ddf8422768ef3c97fe 2fd01339fb35362b72fa6eff23bb2139965d71eeee45f189f55addaac01dcd6b 5dd83f0ea83945d6c872ff09d1f74b133dd1c3de3b97d62a9b7a33f638a93c60 b9ac0c03303ae68f72f66200fe165f57381ffea91425cabd7e3d4d3e03142ccc da9623018c880dbf6183d4c5c41f38184d8c0525e28731f84911d00ee75dbeb5 8fd79ddd4c90dad1cc85ea8fc4f05ea95879b86921b6c6fb72de643a5725ad5a 0aad91ead5f9c18437fc8dbff098b21a1704b79f39b93bbd58dee9579093554a b5d43f697d021c75e04a6d4eaa02449bc5b1c3d8217c838fdbf034208cdd6562 4a6c43530af781a15e1081659e6381d96514bf1a96d35c886be3ce3670dc23f0 ae301b8e26b2ba53fdd66630c79684cf2d6d8863a7fd1de832892a499b8e6db5 1a70990cd0db5f75884989c29d22b61de5fca6411026063c6344011aaec57767 f5c020f53f9e1835f7d69b7b4867b8bc986d043f151231442baaba1cadf72462 02bbc509f4eb2e3f965836d4ca8c8d1b610208b67fbac2bd36386250ade4f42b b2db863733abef5d81d5f81edcfb6ad581cd96833a7b2f7e43364b6232672ec4 e3101e5dac895471ad43577ddd819b798794526ebfe701872a7eeb7b289ad42f e811073bf731f5c5221f4b58dbd3d7220ad88b621acc938320e3f2907e53c94f 16e57f56dffd691650c925e054bedaee3c41590770b18a5d9dd832c411cd391c 8a584d72d0e22ceb56b5a8800fa08d9bd0911a100ce146b1d3a9f1b05fe5d0da 5e5e59f39f51a8a8330dcad1b40da3933bcb7124a59aa43caf3962e7c52db264 633ef6875f4406da9f51c2b93d02c9c7d03078f85b544ad667e67baa2593db8e 0ec198f694e7939a2607c985f31ea48278a14cf129431b88a2ea1af2e16a24fe ba126e60147656aa24b58b25965ae4754c6526ed10f9cdcf48cca3855f45eb13 adb54ba1f991739a2c0f89cf446334933bb5aa53ba78342b4ee3fbd89679f651 16156de2694ea69abab0f696eedf3b4f2c9f61e5047f42aea8eacad649c41d8e 98706bef5ce682d62d03da7000f9ddfa8916afa980ab5a706a83f002832c571b abad677494b55399677b5edd7213c5caa426cd4939fe8a446324ba08f2a4af33 89bc3a2d7bc5ec92e5ab93740f5b370963b394fc6d347d4ba77c14bec9a89720 8110846a883110dcc3c9c5fd44ab0a534831dc1801874aedd56a4c824138e89d 5f546ab5f771e1a29c26730139a8ba89e1802097bbed9807c9c3c2c751df1c85 848ff8984328432f8563129d342d7084f6001de21ba264db1adf7b9517032c43 e2e973fd149ac6d49dc8e0a393b2b2c1d4261bd1065da20b37c0b2b214643831 65e39a736a5d260ee5fec3f8bb63ccb5e258530a13e64e5aabdf03d5f085e982 2d118d14ae5b5a8c65a763332e34d79b9536683c829f7fadce929b68663a02ed 8d50ff8e908397b719103f27a263fa84919d0b74e9f95c4fba4e0bc03b7a82ba 030b73c79f0c4baa71363b88c4bd43a7974d61e07e33b94362b7097e49145320 748e64d531b2a99a72ff18d284142746a0f003f0b734f94994e0fe29eaf9ed68 044389b618b10b81487ae27ef10706a03392cd3479c38562cd651a4b3ea6f328 6730b8d5a0155f0408e82ffc429f4492a133c54c23b74135bbd957219eadf413 fbc55977c66ab74ffde909980e0802e005dc1fd78de8df41a3fe77f9cf7eabcb 50d1447993ac6ebd9e514fbc9ae2a719d0a911e3a73107ea328d79d9d7c558a5 679e0a61cd332615e2efae2f3e7050da8ebcc76c63d071096002acd255d24c21 232d894bf524a6e2814417fddbe12cebe28d9754620fc04b9a5e4ab110f76154 ed30e4739ce5e9f6bf32b4c0c8483435320971092254712784e2e1804ee1300a 78a55342a5558e43f36cdcb12c789fdc59ced07a6deaa1493cc01b62fd79f13b bf41eea2706841f792c9a4536bf61cf914d009d7e21e63aac6c8062deccdc891 1e4d95a20e97eebfb875d1d3c2329921b4d44c0ada21b713d94904f6b8f3b652 72b50da9fb4201d63c8ab548f1f10c9475865a8267be7895c2eb751a3a873c4f e79169214edf4f7510a75ceb497a53bcff9c603efccc0b721e5389e2fe682f8d 348a7f687d680e78a92a4d49760e59fad6d8b2adf636ba4e04742195a628088b d6e3cd66e21c0bdb2f3e34d23fbac07a699d6a33c3031043c1dd23a7a92146a9 35824cb76b19319a7cd916d0b180f71f38c03949e6093d26203de9fe78d9c10d 8ebe929ac53fc83597bc070bb316db54c0620b9434346f952d358b0ef2c78949 4e64849e9b1e4f6740defd1135b29de5e6c2fc0dc69793e310bedb987787890e ff4091ab2f13e8b41a4f9bd8bd6f322af666c231d5df2f4bd9dbebacb6487fc6 254f05b3f679fdc4c9462eef9f4bb3776a112d251d8fefb9a213cfb5ebf8c029 02efaf2dccebdf4c29090ddd24dcc2f5f0dfeb6031c5c8ef4d48320b630398ec cf7a6d5d1919deb1239248556da0c4947136d6df401e2b4d5e718c6de23a8f6d 7e609af6daa2a85734a95e7f388152fca23e75edd0034800d946dbc97103b30c aca644892cf9289ff70f3c3a92323793507901e6f6c2fd92650d1e236a3fe1cf f93e492e1d57180bb389878715f3b94a71b1d3c70376bd20204b414b94dac1e2 8f4f8ebccaa1376d644bb2bc36a4e7c1ebb76aa3d5d84176a273f58ad71c9e2a 48f6f435b16c9bdaa4dce5d77d2200030f85a86c5d641d764fae282997a18e01 c29b8d7214a15a5e087bf384b8fb65b06bb696992d72e05d1a52af9f9117eb3b 68a5f84ad60d45f6eaf8657e3227cb7af943599122d1e76509a2122c3f687b82 8a8ba27248440a19d79af8b764ecee10b691100133e55c04994b46fbaa48bb4d 52896b2abd668802df0043c43ad3c0f77ae65e7774342fb2a9233cb546772326 6cf0aa85594c070240fcc03227e303474f093666c480d23d8133d48c3326ec89 28ff7341c7a4b316313c5f106968340fa8d3f8f33e348534313dbe1a70e87166 1f8b557394a7c7e71808bae676e218fc255366de5a16c4759b60dfc6fbbca894 17193450a73d9f64bcf72ff35a87f1780ba79578dc86c17c41c364f69db70453 fad0e78a0a92ae6a8a704a449780848702cdc2092a3e4e5b21c68e0d48e0490d 1f8ae17734ebf26c7ea5fb3e72eb79705d5fb3d9e73b080fa307fac23a353ea9 1fa5dde96ae0767d981588e8b70f45fbf9c5be0a65cdd8ce34748c54af38f7cf e47ff6b106b7c4504f4372cf8fae905ca7756fc649281cb9bcc1b58b9e37ef71 4519581473acde2aa0a72c9d6431c13dc09b5c6d414150544a7b9b497e1e0d0b 389f7936477fcf4a017c74813c4489f38f7c722dc66c3ffc8eed78f560a37fc7 a8964dc4e54746470629a211b5c091f73c6250d25f5960c9cad0f2dbe737de4c 4687db8a11ba9e3be2b8c1607adeb1709b8bae9b925f37f730b03013070f7a5e ad63004a24d640aaeb37610343733a2cdd3f0a4587758a2dd4e96f3b9cc7783c 221f909d356f8750d0873e5104e7d7d1d1133cfc34ec182b9db806d39f60df85 8248e3ea7054737ab997b2d29a79a0728209c69e627f821ad116ee6bf6ce994c 86f035b1b9ce498e9250e8a5cf0a08757e01995e9c5fdbcd7111f208df89e2be 12d009f934cdc3f2dae7554bbd0c0b391f38e29b976ec45cd1ace78e8931849a 6e51313195fbaa97ffbb5cace94623dad3425c9464bb4172f1a5973fc2e0dd2e f579e307bd7bc44a962cc10e286d1d2169b0da81a3126b3daee71637d767b471 80efdf9266f5b6ce5d2829511f2ca208acc8ed0d9652bf1c04f1ef81f8d95082 7cc3868b1586eff60cbb69e36c37c574c31a6e0880306129e99c67d8210d95fd 67d545088a3354829fe62bb2d5c6970d65b8dbc6a99766b6866251f9ff34e6ba 3add6c063247b816a5eadbe899ca952589063e9d21a422fd7ce29f8a8cca5325 5020c0a1d4fc450e6327436d0178cc092763ba954934e2581d912d5ea4d5bb27 21b6578f49c1cd0eabc5dcae0d33e2480832f445a27b61a6e170bad0411dc2e9 291b832a88b08a6fad8c2b353a692bc9f097070d401e46fcdc2b7f0fd4b88d71 d7065289b61985551accb16d9c1effa8f38a78c7f8b42e8d11effbaa290c7c5c e24f7647cbe230816e3e3042cde2abef5e411f38f8ee3c0884e2d263d9fae66c 146187cbd0b590dde5642bb2b9d6c2e0134d6eb5db188e8a1bbf6532420c6a29 f499cfb75f64ae162d8611827e14b4379dbf2a4ccda8d792efa48e889375cbe9 f780b4b482bc20fbd556ac9c8a856d6106d10e0c8cebcdfbe3604ed794963661 6a8468cda7755874d478360df80e38d968d257f77e2e83fa62835bba308fd464 b62adbd124dc1fd4a1d69e058f774e41643c6fb64420e15c73c2020d680c5794 e967a72b9f6a63ae7093b80130cd8851d03a4a06a36d896faabb3885005ac234 23e2da06fb8b46b5110020d74464c7165026f548e06aff5ee1f402aef20c6116 cc1ac6bf18e4d5c7f860b51d40705642f00908f05f73b25b6d2beb2ad413ac0ef16f77a6c0e3f80b5a136ba34900034c6fc5a0428e55231a49ad516224ee3000c5ecd92291a925518b6370027fbecb35f9adb4f9a459b86d20e65cbf3ecaa3029eafbb895571dd3d52752b7ddd3ab0ca4cefaa9c92b4ad4cb92350a2e84e4502e37a03ae61d4bbb8ac32b39ff7859f10b6f580496319b7d1b320be4bdae2c00ce0f82fcf49208a9329a6f3d4f5f34a4ff9a03044504a9d9840cbb2e2013bed0f72c5505ce9e6f2f8b37a4ef81d30d27df80c35a974a34d5f35b951cbe587350fc19edf70be3b4f3581d5301002de29add4b9eb9d072062c4814b2b94bd64e00677c2595786ed10c37e3ac1c519473070a51f2d111330c304d246e2a23a7bfc069aabc252cc05c13946980e2f2587e57edb5d75ba947a5e17fc872a85f68a17009ba84e680e205da21ff3c31398e9ec9321cb5801239546bc158c89b26237690103aa6f4883f818f86f551b291b4df95f8127eedcfc5d345852b51732f8100009d5975fc8048e8ea691b12f219bfcad090184961f584450bee2eb9ab05fe81e0341286406ff81ec6073ad72cc71dde28bde9b2022a976d4887039e203da431b034394a6dfbf6ed3474fbee844a9c837976c24cf4888de890279a1b9ccdecd7c0c8f9065700c9f5ba1f9153c5c319be595b00cb0c6d940de055084e2b2436eb6036701a4357eac89990ac9e3656a7c8c4e53377486065a1a6efa2c9de872286b05f41214c6c9f7dd5e9e78f09f0e71c1def0e680ef12f584125ee35bafa1a1ed0e028052bf10ad69292db77d50a91da42c256bcad4465b4ccaac71586e64389105eec6e83de6a85e54006ade786d59ac0b8efab5f5d6ff813d8acb628bd4c2b801972f8e52168d314bb53bc68a8ffb346f0cb9619be5488323df250797d0250802759af3391bfe688bd827afc173cf9ccabdc8aa3431d3e073871ed4dd5a2128022a008411f890bc1c439beb3c1280e42bdf99a40319dfa5f3cfe9a39bd4b61f0a8effd4cf86773f3432e431db74f4d3560ad3d435f6d28c489a6dc7b556b4ca0ec8bdf7ecd0b081e21dbcaf91d648f1e3b91c6adf7377450bf5899ebcb119640a774cf2b3bdf2121fd0caaa9c31c9e9cb533f155b23388c55370f85de93b1970638ad9305b0da2fcd9072655f0269b0380be503f30fd59c803b8388196d3e530827ed35319e60b03e68ac2cf1ed1986174aa1ce0765aa8ecad562e05bcaf3f10c8ff554383458aab00aef546edb1f24dee5c148cf83b4f9e6bfe7442d670b7f0bb264de6b710e8dae21b904bc5753dd04279e8e29b47893e9b8134088b2174d091d6aa8dd204579ffc3f2032a125d0ebf9ae5d4b502815cf19afce1bc32081b060863fbc4e49f8ab09568b228fcfa7631f252558a613077f44f723925d93cd00c6b8fdae03866974052e5a42d7fecf14fc674947c35cb09fb1479e56a35c27d0f8b3d1dbdff164881a067c27906d7a067eadff7dadb7eec5e0e62031d48ed210902a67313932f57ee7bf80ffb1afc469c6977da1be5ac2baa668ace25ba020402fcddbb8fe9ea7f0a343195f3eb30ccce25dd6e20a8a5670393e46dfce321dc0b7acd1411afd0691713a68946e92f4002c0af707495543aaca79c15da871044079e72dbf63d11f49b0a2b39ac78155fcf9f2f6d96b76cf1eeadaab08a8352100b81927bbb37c390b724aba9e18d9f09d0a7427d2be7ca485700c018725fc5ae0c9b98d189f14ca3f8cc1f6e726726db557658adeb45f5f2cc0beedcf1535b330f4bc959fecbce96c7f6da2e15eb21dab80ea4dd653ef762eab6044dba0337af0cca60c2ced93d5ffe17f8e06af593b35583a72390e1a0f85c41bc51297841cb0c39c147a80778d902a59cc7fd98b56f54393dc17ee07fbb24bdc803b51de5ce031ede60384536ce1d81e308b6b70e5513c59cf65a8a01435aa6d08b88d658b1064e01558023cc1accd601d558889a6e1778678d76b927e3b4a923b1d34ef1d8035477b7189cae5b7220f4c5c7b0c717e38f0ff634c59f3e0dbc849692fdd11000f7ae4d66a9d77e81a5a1e351a4193852f1377706e98486ee2662fa052450bd0c21db8d0b236d654bcefd103f780b38f7adc2a27426cad41ed9306429418e840103ea5e65c2a55796153b4401498fee3030d361811076a485582c25e8538ce30e9e255c4e51bea5c84a3fa3384a924c9c7f72753738fd1bd54ef092f9e7c28c050f86320f0357d2de2bc3c4542e83f5c840e43e281d3f6d9e1fbb14813b2f350c14b158f474b75967bc645b978ead3ea51853cd737aa994744ac0b145646a110db079f8cd794ca1e53634cd3de764c211a1987c6143580adf43621b59cab50603ee6b93f940bb35c0b70a0cf8fcba58e84bba016289d49b93c3e6e7260ddb3c0254ee2d78eb4ae3d360551e2fe7c7c63357c9db8986fba91b1d1e4a8d2dd3ce06630e8d4bb5910199190d764876edb0317671097d7a1a55ea0b0e0b0a5922d107c555997ca884990526fb8a7602f1758432d43ea5a3809239942a7ff664835c078571b7390c9830b4c74272641f07afc987d6799ad7afec913f1747c6ff42320088544abc841040b10693f9917f6b3d61cb75ef32e99b25391ade28b925c70f0b4c4bea29eff7a1a6ef6670f868e01621fa53adee0fa2c31a144a92610f54140f4901f69a202e41b7bc4f465d0b555d1be5219854b9f3949f0d8ea09fef2b720ff2033611511cf0f7cd4c45b613385d949def5e748a63af19c061b0a29255160faa2fef1a319e3e0734dd79e64d09cace8e9cc2d7a118ec0dcde2dec8d11d450e064ae3a0df947aeaa300390b0f2f23e7c1c4ab3609be22449df9f279a4612a0f2e79003ccef257c536817f8660bad4fd0ca7e095eb55bf3d5adfeac13b952c051b7597b726aaa32f7f9573056344cb997061ab482c0f765a2134eb58b382f2017021bda1935159a2bb66db85ae201346580cae936f6f3e77b9cdf811ac1da104e87a76262c0aaf6a3b2142ab65d8db8a0872c9888a79b3961b27ba3bdc614a0eb740430e9ed1f2695b60ad7067581f981fbd4fccf1a4989489d6418647d05c034b89b447bf7272b35a347f97e46077ffbfe3ac95502cac611f76cdbf5d1ed0062e3f67d40586d39a6dc8035d418ed8f1c3c0aac0c1a5506a0fad4265e860d1075fbb541a149b03efd22974f5cc73496910abee429456fb38551c51faf131cd0e39958589c02e39a5996d9b2e518a4c864a3c0416e50a3cd531d57f1aeb6f290948f8b609824a3d128e00d1e3bcb83ad4d9021778ecd9a1a5ac680aed5a57cd0d272762e0e8079bb2deb055f0e943f4eb8b4c93adc23cf14d14500a5fbd4a2505dc8ea7f50aa029c1bfc40ea04d45aa9eaeae53204d307943c9a0ea08bf927d081620c3e3b6e31142cf42f702512dae391ef925993405c06e33c82a385113a6020aeb3b7a76f2eff3ba8c17129abeabdc506109e1727df6804eabada97df6470af5f9c67c00d9ab3383f51d97fac84a1f84c564f7d8d8630332edf388f8558101cca1591f236de691c81fc1e9044bf233ca2015c00c4029a4e818b524d718c70c842fdda5bb896c6891f55eb0fb69eec149bfee90035ec1860f98aa4e6f7b4e02b518b2c8394357f96ecd9b19752d1c1e7227472a1dc7d8edf84661be1fc6a40eafa76ccb35c50fdbe4dbe14304cbcb088ba50ecc5a85efb7a7bb92880ac08b06f7aed341ec048747dddab9d0c7cc8fda569b54d9919f193617bc8bbef9a22f0323528d4dd7bc63f3f3c677c300d2ef8e408b188178b0016116461cf741352c0446de270097599a80caee853455e36736ec0840fd70c735828b5c2cdf175b860b8efb846bad3fac4fd7a3a59a377da833bab08e91e0cb58ebd88e78f15505b80a937ea3a733062939cca594f742fe5bf56f0699cdac6dfc72180a627ac35bbc082ded997328d38a916c31e593219415d43392b32c91a5c00986cf12f61cc9db0ed5f30dde3118a2916ec32af96b0517bbcf0f8b7f51c09a98e42027e40367c2032077308c435b5d8945b398745998ea1405d776b8e5dc80ecfa56bd9392d65b0d912e2c31105a7158f113fd2818d29f930c42276b3594adfa1ee1faa08d5c7502c5b8b56a519d1411668c11777f5fe435589a01f555909f56d9f178ff01c0020f71f592e052d2bf4e6bb35b8ab3f52c16579722c67e92daad6ef7d51d47adcb013ccf2ead2fad7bfe4958ce45dfef018e926d31d5610493a560c23ef54e8c050a02a538e07f1d39885b9d98755989858f4a8b6f32ad4849d52c694b8d6d7cd608697646db36ea65486b4edce3597ceff05c44497ec7a9e12bb57ee62db742ab0784cb2568745a65a79607ea143fdd6064678d02d8232ab4cb341d736e798d60090ab973b84e4724e476b27b3a2cce2852219b5ca86a9665f559d7aa202c25bb09f95758694988383cc40e580ce42f23c752f1d546bf9499f235f57fb20a89400ed1ee3598a1719fd9465311371577beed6c385c09bd81437cdcffd80ea5cc4d01a86c59bd36089589cfca37efb4fc6249b6acae9289e25af8cf1ef2c00e5d3d0a7b57cf5074939d42d73907df4211dc8f520143119867e27bde3dc241e759de059670a2ceb28913f423f52958634a540bc4ae3095de7569715b65c61d4f4cca0340e7727221835eff87340edaabd9380e9de72a5c7b64245e3df8853e56bc8b0a1b86fba37b90857f4ef01fd56ee5ea341c99dedf4a04592e361a81c48ba82a060888d391d73921b5d5569d0bc33987dc8e70e3436e0b30042ccef2ec68b2b20f8280c2a343fcc3af5f014963188417e96fd997595e3e9217dc227cf44f3b4e0cc16e6d540ab4342344bb0c3b1658188b184cf593534348b4efba2f36a619140b1bc030a96d3142a31552be31f297d3ceba5d4705ca9b3ead532d1e9cad52f504e6eded9a4113fcdd77a419e7952a8901acc61feff649818db5e2e5b1e8d517068ba73dac1f974a333db8d660478687bd04fe73540f358bcb859545584f7ec205098f7e0d443b227328eef394f3eb5d59c7c5f2b9971416043e5fbd2502e8a60b5e61599a5d88785c892049f0927a455146d7bf076d662bd9517f6f6daaefb902cfa1450ca98249367a1b6d71a24733e1d9fa1c6a321601ed786f5680558bf10bc1ffe938a97936ef13f0c7b94b066c00213c1f17b447cfd65c86c91d80d7b2002159002286618880dc7b333d25d1937d684ea1d8a0ff3502e3ca777e3656580ca1b36f4ae71196d4c6746c169d9a51db586294530847b25d4730951c2959970794d0bb78d5bb3b1e2df1a5e296c89af9d721c2e03cb4b5b6a78416fa1ccfe50a764a52f80c14738b7ac445c75f94679cc7994fee20d06468f44f3c787b3dd20bceb3033b5c4b6492358be4aba9ebf2ffe437072ee719a171ec6541e4ef020b052cad5a9a30f7e3b1fedb677718a839c1cccebbc11a86e108ea87cfe5688cee09ccd7db869647588f4e83ebe5afdc33b814c1a14033308f03442141d3116540052a0c7560c83f13ec502a8eeec7a54c12e4efc5b362d10f7e1fd3dda1129a220d09ecdab00d8934d0635821e6b9c2cedc9898e99420d693c91e0b451b61f0d60858ac6e00a503ab3bf2cec4d505fc56c150aa58a19273eece1459ad2e516f16086d4f599069e4eeefc73ef21a5307eb2c94c8893f07c71708efb73c6a3f9040018e53f02596b6fbf023cd204e139a591efb7de33f9772ec5988fe0bee1b9d100a9f7fde029150580234d8730c0ccb70cb1cd9843215094f3c49bf4ee04965d40a410b97625e6994f90288c4daaf5d2a4b2f53ea987043b9de755e0cdd3163760f0d8fc9eb5ee36009b1893aee28e37a4c15e4edae9d6ffcc996da7ba8df2a100ece2c6a031eace29de6d58dfb187b71f4561df172839a141281554a549b687207617afb03f08f637faba39fde4c22bd9bc72e66631995616dd8be5bb1670b87031d7459ea74e94f065173886e0db832cc675031f5a0678c5c8f1f3f0aea94000b9e0171a8499f099be7a19b0ef6e3ba288baa311a95a2717bdad1b674148a35037b816b277a4d16c79d08cbcb05faf95256c1240ea9e61c8737ba4d5b1542c90369ee8f00081e8250ddd510771a7256a727a19cae29ac9a04b00bb752d16b9a0e4c60b94c44a158cbbb06ec902af2ff4829f6383f2acaa5984c8c4971d9cb8904f1f7f10afce2a5858384e6e9008870f3656761a47e7824aefeec04206af69c0dc656d7b9932d8c0a28a8df1a422e3389d76c9d330cc6a5815f99e6f74a2aa70beefcf4d0513ec009428c048f1a7cab28f53a872adb7b2a8ccb5b91b904bf510845f37b0475ef7c12f3a6abf2d775e17640fda4550aef0c1de095ac4625e0890c1039a150fe50ffce2e843b9e85bae682a8e509f0cdd4fd13ff53a826c840470ace46cbf5656a3132e586f6df0ab0a541134eaeeffc9f5cb7c7c9f001f753f90a83839bad88a0e6a32ccde456cf6225b8905f9c238d07dabccc31c3eb31c48207a5585e9d26242eac594d43aeb09a8b44e172ae1a1f79273635b5ca1ac31a330bb7a65951cd9fdf8827e33e16f66596b7ff8ac876e0860c6d4902fd65de31140ad313d5f2c0311ea8095c56dc4ef319dca89b22159da4759d81605236a0026b0a537f0ab8d7a01c878f412636e8b1464ee78de99e71a443e94280719423455e0a93717b3f52433e7df4c256bac6e3b3b9d9dda994bbafb19460531e6615a0ce0466420067a6a5c5741f61ec7ec8d1aeb136e28d9fa93abc6c3f0af122bb623404b5f686cd762e97f8c6bfa97b8460e7c628ff57f3f506ffef5cf46347cb587b01187620b6706a344649fbd35bcbc88c5032b8c246575abfc7d881794c74e9960073efb13daeaca1838afdc7b358fefc8fb443d33960ef16566341bf94e170340f8c16285a8c0436936400b9cba38aaebe6bef4b83179a91f2ccd4b208a7992203ee65cd1a9ac81292b750bc1f23c014b509146640fd95e1369185cdf2fa99d6020930faf0f2f8166b382acfe4c2fb06237c38d27d10a1cbc3806e7af4a57e2d0db6752ce9655d9178d934b12c74d1d18567c835bbdd7a19ecce71a25636a4d40d3708c51bb988ddc0cd3af9566113009d303204090801317a71832f0c5f8f670a3dd47961334255d83047c1f52f473bc0a78c28b8e32b5c3230dd501c47f4ee0ce822b7621b226a02a258cf91fcc48d698b862a41619ebb38c5c8b101a4603b0492a2c4b44ee23bf793ff30d8f93321b3936839402316d0cba8c9de635da3470fbd6712bab02d9124add1e6ad9d09034fcd5bc7365edd7ca38a5cf61fb660070213ba5ca466307089749ac9fcef0b2971c2f3e9ebdb6378e6ba393c36b1205609e2fe598a8544b006b30596448176760b9fd3af6ad742dd3a6a679a10285a990b26959a42dcca2561890d11cee0ce427d4ff5bc5e50fde6e36ca5a81dd1810f0a80a0e2a7e70b07fc647d0cefcfb6427f29a3de719f11a62a9b791168bdc39c04b5530b76ac51380a9e7e4883eae18967586bb1e4242153c57c93ff2d71efe90636675473a3a68071932086a1856e3c7f796dbeb6792280974d6013836d2f6c03c92ae7418ef4808bc015f9bfea4441ff2f4b408d4685c250993356c964b7c109c671bc4b0a2d5c78dd2a0816731b015119aa2616db93cad3936fdc55fe959d0e04b9a399c480f732c2b27866102d71d5df59e5ec64130dcd8e795faa6ff2d4088dfdfdb0fecf2725d6326aa97c6b9a1f194a3b9499b9535d697265953b229004bd83416182f2700a19e4eef178215b753472ffbb65a5c9b3633e74fc58f0040999b73593eb07afdd5ef94ad3bd4f52912b2c48b90a098a8c9c47d0f5929e190cb42117e29f00e9b7b5779cf8e598f0bd3d95c44a29df4c6280114dbcbea0430871b177dcd0a696e146b46fb2eee8f4dfd25b21ab58110e5c316d2ba1c6b14609b40bc383aed908d802c50886d41db693a6855b711e6e48cdb843a799f1a35f07c086e201066bfa67ae77009546ef807f5d4c6f08e41143b437f325d48bc8560b250e47c02a4a3af196cdbac72abc57419ea4e773153bdcc19a0e1b69cb3dfb04b455352927b842266fd3897f51d4f5e0fde8f3ca9bfaf152e5f72628256eb80ced09437a39cbbb43e212f3d319af9b8f44a2662b4f199370c8b69cf5b6e3ec032f2ba5dcb01e58a99d7b3e9298f790eef60e089df69292134d54e262f7858608e121e09a76fbdd8d1623776432b2647a294b7b23c1a766625c591b1edb91bb0c751f830d03e4fb784e35f28e3f72d28cf241f01e816f623262f2456ff327de0739c5fbf2a15e0a12301a48ffbbd8930a7834e983db3d5e072a227eeff7e1770ad461f0f9d032ad8e33cc3807bf8b8b1694dab0d851bfcb4841770fc1394069082c8b0ba431731d795c898e48d956e80091e13dda6d0d39d61251271ff14db4021afae1b35d73d0f5ef9aef83ff98882bc9ee1b47448448289e150917686c24028ae72fd1eb90cc607efd589fd434b353d7d923e94a9a2fb40c04e526c7c5070fdcb0ded6762c6996456561d94e43b2f43732fb989d130b77c07c976f1838c10cffbd2a7ef5e7d1c307a9cf929243e96588ea7240b91d02c12450183108def90756a26b4bb56a1bff0a594015f3757115360e96aeab294411f256e7047a7c72008e9731c80b1bf3e93a41e4448b33c8bfad0ce17a02fb6413060f50ab61905c0a1608197d2dbe21a46651bd3da65fc0004a1e38d21be85fdd1b35a4d06a988c0411257da1a28500b7a36f45eb8dad8be0ae92e2d53069e36c288a164d7208080102ed9fb29075eb4ffb812159b29dbf902a7a779934e4a4ac7cd510b5e9741304a77f2f7c20b965ec5049aa13eb5f7de0d5525364079dbe6d0e45397fb36cdf0c3fa65a11a3e4ef57e46a54fc4aa113fc9383ed2119cc33334de74eacd4226109ac0072a88aa8ffc763dbc59af9a1ca162359c5459f24db3bb27fe41d0eabe1060cdca22acdd0daf7374eb268e5121ead1f846103d9fed6944ef56c753ca9fc00e16b57e0bf22eb158eb99ca2b195640c4abb2e7a1023a416844834ed601fe90341c41260a1df507bc5d0e6f36e1c340b3c3b6d3253eb2e7855fbefa066db6109cf8dea5cecb7354c8e5973927f8e418d9b2199fe60a5804333f46fcd8813e00a269d75cfcedaa49550a9db0021bf6c78f39b7d99bd4388a8b812a5d317180a07f24a6d737de8b0fa536ff9aff2f5ba55107e1449a59de5ea5184404ac550c901d1a6e3398f6705ab94d7cd3012f74ba9baba6c6a6d0d63a2a9cc0ebeb8ec580c80e84ea11343616097172c76d3b0130cedca29f4e5638a6e05b4fb457a2f6d06141082a5ebb257f87b94b3fde547c3444a63b9659eaca6a2ac77fa75b2643d0d4eadf94c6cafe7e05d9a4b6cef812ee1cab3458cbd45c31b3191e0611efb6c00e64c148de7fe1fbe846020e3735624c9efd5005a9fad4b3cede58abc54d99d0c8a450efb57e409e571396683ce9c6ffd1c6fbff9f690a7a84b14168728b9460828b2a3311fb72417cd89831d093028bb9eea18895a52c75ea92668082a739502b4546e728cf82a206bad0e9479f1d7e682bb6eef8fc635a793a3a94b1fbec4034b6d9e33fce92828d992c98b7216a93bfb2d5215a5c96d3353b17a01ce614a04682111ffa0d02e7e764fe87b6a0e772b91d269c31ff2f9fff155b2d39b23e503f78fc97b2e0234241a11088e6830d8039ab92f1c03dc890c0304f94e289665060257ef1f078c2af0d801aeac515bf827fad80e6f936e078190cd486936ff580417c59bbad191032c88cf5197de4fc81f00f24a14fa21c002fc549ac5b529ee09258cb680bf068eee11bff24d1a510e9afccff0f914ca00c6d52500eafd551e0c3a965038587d89892497bf731e3401106f37b0982edc03c87bf56d5a1d705a0cc2b65bbae0fa87aed07dcf3521cc69c641d39031f62f57a06e2064a100108f05aa4bf89038c8cfdb8ce6ef9034b3f6ac3c61039efd81a7b61af9591ccf43b50dacf1aa228655d87e0e05252cfbf62d77ac1f43bbcbed7b4c91fd37a2f1e3810a19669d1c682a2e1c4e5bf000039f606cba6d911dc6143e0fa31198441467e30a03a2d23bcd7d3008af1a265975308f1f17f323341ad8d53a8cc8b472e36ac200db400d2a5898d9567d4ba209824147369018b2b4f5db603dc1cc625da34f290ee4561947be5cf63c1f7cd8dacf4071dd65622afe033d3ebec85d433c2992aa0b77ca42011fe37a3398ccadfcdb241c12fbc43b955bd36f0d460c3064f601660c7acb76da7025c691a9e67a8406ed84dfb2007d7e30f46dc40e1bc3fb68418202a847a902a6c600fceabac086aa7447cc2d3b4908f3c35d3d89975bb6d28c3d0770803a79e02e79a8ed63afd56fa5e1f20e9ff5df38d9e65ded67a50879ab6b09717f465e85674f19a873d256f6c3430514096f79926803ec58826b69389ba70303cf63fef723c9634bcff54da286550c806cb563bdef7f977e3eda24d309df075a336c1e0c6d9983391fca7746b96f58154c643a5361fb674b60cf9f6ce6cf0112de5384f5d866f0c33bca737397bf7a34edf520aaae72bdcaaf1503d258ec088885886c5598f2a5fd639492fb9fda03c448167ff54f065205b5f9dac74ba40d1fb9e5540ff334db7e4044afaff6d91e1951eb292ab4fbcb8642e8a37604b900885eeebb478d9ba20f8807f63149125ad169a02eb3df516b5d4561b4086f5807f6bbb8acc7ad93c63b304b736827102a3d4e7b87a18680dec49f67e7efbf9e0d4382b9cc1f156d9653ea6dfa576c0ec3ba3c7b94aacee6126f5066a771b7a700033c840aa99568836f5f6df42200ca65f3aa17a2a9f7dabac1c57dfd42498a0b97f2d611a1289c54d1e0621ca21a48e0ce522e20efb555524541635df1336402e2795f2fa4b2f69d7ae562837ffabfc6f4e72fb3fbba18b77ffe345bbef6820883f9543efccfa0989ba5f134d5d958f63e5c0ebca1ec24cdef10fae86d637707b8c6fac973c60e457a316449d3c809d6e49395aa6179a5a273dd11b53221ab023d54fe5b2b8f7125d2ec76b3f4f633fee4e2725aa6252defbd7cd19bb79a470ced9f86e1abb99171bd406813276e32c035fae82a54429b7184735bf3819e2705b91a9cd1eff68890e650a2c4537f1d1a69376061f9d7ddc34ca2ff7ed576d2009088357f7e861186905a9549bc8150788960843f9163dde65b023367083d9700def182f386d2fc1fd58d444fdfaf3ef342c4d5c39ac7123a5a503b6cd65fcb0d35880e84d1597a92452199813bf67fb37d23ad391f73db63d07f92b9cef2830d3baf12e8657f1caae47e8dd53b43acc04d2eb060f64dd1758a5810ca75c3d40bd59f04d26f266a665fce5e02cc2de33555d4c838009600cad56726c1e17c9003c410e5346a5e01258b21cd91f6a9fea346d2609003c7d5497331cf27125ed40efad492987dfe565f0f152577a4677dec764c385412fe7c6fe6609120b03df10143f3084945105dd52c70f39eb5c446c7f4a60de96868f6427ec73069dfc8ad09522e781c015d4588f9e9a8b2f64d05fe7727ad1d4039dfbf7da70082ef0e4700ca6ebce74f39905b3212f3d599e3bc3649adfa00f7917e3932eec4d8df2e780c4d509a82186effa2495848557744a416aaf419ede913450c0abef53ee01bd708b552b5fc02076345121df163d4a91b63df07249ad72aeddbf61002dec346d70aae404baad80fa9f5f835f5f19fba8a42736eaac7d98111947cb35280e81a87580373a479e1078bdc56e14ec97b560c7480e3af3c34a7abab72093f1e3cfbd70ac0b22557a858a330300ad182cd08407e5aa8244fc2e2b7a7fb8725a6251f9a085a610b5181b12603ddbcb97333d36498a5558e4fd2bda34283cb318f2d2a5905f698c00afb3a19d3a07e14a5e31455a438db438feaad3900a0e1b98e260419027bc3f1e367be10236e629f20c12f36bb680dccfcd36311e77ee15b60b058fe074bde942d8d47136d3683bbd52a7c0f5a00993338cfdfb0c6fa56057909936e02d361404babf9a6e9925ed6e47b10272e747d76b766310cbb0492bc512845d300e9849e617859e1863461fde178b3be178cb7b6b0eea9b869ef34d64ee4758702be7dafcb1eb8bc00de394ae0fd678c618458b850c63953549b6c38dba82e0a024a769542cb7d25c4e0f4164364ab67242186f6162594137dac0b77e789009906d307e1e8deea79b53347251902e8d29070c2a0b2f1f5953d65ab7f094b230d032a13b3913d5ab5a739432582a19a4df085b67b541af96bf935242c73279653090bd232b72a2e31bacea9b65e9347a7f3534e56e6202666a958c46938317a8d06570d3ad072e65a314a93dbcc6c4e63632905ee9de5f972a288e235467442350a79d8285039a2801c579fb15d37bd2f7fe3964b0c14393c5e497472689a66190f43be97f68093eda8e099c7b1a9c53e205b50b9d5209fe553ca955dc34a7dea0cdb4c793a1112c9b88dd44b9834ff7b48fad856d464e6a81794bbcbf11f05460fc94fce3bd01c87b083dc37cf5dffa760600d62693611e4fd12af52fd3ff91407e72fbbb8559b459a0d4a9972cd4800af1e7de17d102a8186b0d5b7725cf8ca0886ea51d94cb6a72f6a4aeb7e450949ef10d418d54f3b2a317c64769971d5b1051a91420045a08e9dc2e8654eb9afa0257a63735df35b4678c3a6ad520b798b02fa2d96df814b642abff2136222eb9ae70f469d66f25c1a6720ed9703b1bae10b8103c1394fb63c776dec704283a56d81a197a5790c6fb7f8701709db87b1e806383aa989a7c3dd916c312c9358f3814d08a8cab28a196ae4a483ab713c18570eaed58d22b9f1882cc4cf2c35dd29266fb37f887f672714116b99f5cf306ca103f5dbcea075c224e64ae503adf62dba3e96f113a4ee7a602ebf4069051b55960f3223f972b12b04864a3c578a4e2f4e096e176c058fc7456de371a39569c30501d9d5483bd9767a9bb66653e3634f5ecf6156a1c3ea2aa3fb63bd8c5407de110596fbc46bd670767436ff524bd6cbc83b1a0c81d96bf8b25e76a9b53c20b0f90d02f4933760aa28e2e6550c9bdc2512a1a96d81172ffb9af3476e045ae5d97f0db97cc782dfb681993545ce2228dee62ea861c3b94c6c57a4c1eae351f80a7b06f8e3e9794d71fd206836ca52f36fd6db8a08e99ea766030587370b4f3e52bf00ce51503fc795905ab98daf9b89f2486102612e58342b2dcb894535720749030094369fb30873d4dbd9295714653da68c926e1760c1d121c8e9df8be671f5c70cf5638074c037a3d51670a0ee35e13ab377c506f3d2681dc742d7db7dc349b60c41c2de7fad3698812345a7d0ee0d733f490f895fe3cd2652fd951e77ea85120d1251b882e0f366debc47d46d77dc5ff3a469ae8fb3c9a2af12c3dcb8c2b02b0a9394ff15b6e702af71702ffda5af1327c2056b0554a0771890c0c36ff808d20a233c22537d743a02b36f3e44f472872a1246b3a517cbf1632f87b163e824bc040aaba0ce7959f98e4d47739fb9cd172bc8c273a94bb022417292b713bef41100c279fe6940fb5ba90d2efa0715f95285f20607e9ecc6c1cae75b62822e77010c97da9aec1bf8ffb2889cfe8bdcd261be565580c761d19205af219123da12e5018d9b19027314dd932c0956dec585ed3333be47c66e1d26318b197a5c30be4b01f3a6ba2ff81f525e1f62f244dcd497f3bb2a5e46382bf8ff0e0737a3233d8e07488446fd8aba34eed8fcf2cbc11db60d393797a9bf782d44f25f780398ce44033cf08a030104795d53a6d5f790d53a60766d9030c758550d7ad63b4a92231809864a21eda0207753e12e78af11de58a0c27f8a17990dfdecb1e0bd9c782e9a05c35becfaca81bd78175b5eecc831a8d6c56c5aae28ccc8f136cbf9878d66bd07cda2ecf49364b33cc8f8671e23753273334b1c594fc4c29eb4808ca0fd21430ccb949adb22d56995c79a5a98f6a9dfb9ded307be01e1c8fe2465a29f5e9c45076249978ba3d63500d00b2c7b769e4afd6806111fbf6e39b50c42a7e8b99b53068d633e0c345a66dfc6008e8717135426b112baea6a36a583e7e7705a9f37df094339924ade5ebf948df55c4f8a79f74cd23a68290f87d54983076c519589eb03e55a8560766905fb5390e0a5491879399f8dae9494e5f2d212303ff32e01f40e6be984b602bca96aa6ef2e083743516af37c82c1eb1d5ba8c5d4f66f7358ab07512fe5cce6e47f255e4e09b900084fb7ce05b7a742cc891a62f4d6a3cea032028e02ace99ae101915a31f5dc1d9ab9fb21c1398bbdc2f969fec5350ad252000d948360b6e3f040264859e76874599a0e7eedc6b261a0fe7aaefd70608a88a90fda7b1ce0a4d0cbf2d6ac038d26d844b94d9e74672520cffe83787344f94e6204023d0006c958fef1b73588bf51705afef6d06f2506665e663b38c602c4cfd2017e5bfbf53d493cce4187f2a4c4386da8c179491abf51d529cf27ba1f2a795209249282795bd04c96a4ab13e7878013f30600b59004a6d7e7bd0334ac6cc754010b3be2c72021d6c72fab51797b5e7cf1b856e36b60fc836fba79ea5b261ef309546dfbdd36337b188f654ce00e8ffa399c634623a6200452a82731974628f40af38b34998b810423f21f5d4ed3624c20d3bf32fab52f49df79bf9a6e41ff4506d296f82bcfd46148d90546315d3216ae6e6a9567daecfe2523a22ef6d17c4d0d4c03858b561cda2f14d154e37e4eafe60f93bea4581d3071518218e4c107520ab6033ed8ac53ecd3c54a47ceb611e113bff94d699c67517315703034ff343701461e11055cb9ddb6e3c2ebe0ca2a011c79f6754b5f72a19585e579fe4bde8b04ba93429742105bd7017c8de8357e7b2e917eb7eb7f39a032dd2df39f537d4405507fa2d4f4832ffce296689d05c88ec8426028bd2ad55e44e06548da91854805fda718ecbac9586f179b34eafaa54bb6931161809384b05792b73d53fc145e0d57d12800296aaf09271043810406cdbd5b7d4dfbd9998f413acde7249fc5ed02665c56c8744c936afe5f830a4ab14671d27366ae93a0a27298c77f8b77f77009b894ef193bf55b8f418a73b4b5b16490f5a55afbf32fc0f22f25813c1f0fca055a684f96084c866bdc0f8982e219041120d054717eeb2243284ae8fb0bd8380a6b47b0d288922174808d66795387e037ae9a43896bb0bdb6d5552e7f8c11000ae5d99b87b346547b5401791bc696f9fbb9fc11db323aa443666ef9a6f5ad52004dc15aa19190fb8f045bb49e49f88ccaf245bbc331ec9623469d55749d22030074047cd747e85e65947fa09cbaebd479743d568387242fe05ce4068553ab9900d26b1b86a495d15b6902c4671ef6d8cbe2b393e7945373ae1d4734daa9eab10c160eadcf9999da643f08994abc4f91ecc243c0d8b0b67a85538560309283c10cab9c4fbb46d52433d1039dd73406970930294165e487123c4c9f4a1951c77605c9b08552142031abceb5d934d5144b81dcd335ac9599db10ab79711f4d679d062a8aabe946878f9128b0543f12dab4bdc53be93a4b6535181523dea1c7f46204dbdfafe91799f0819b88be66c901d9b77ea5618f15922cdb0646a91764f8d5064c4e89b2926257d9e293c3d529548d5dbbe15b4f6349a0b0413e313a16d8ac07eab6c60ff8048bd93b343c18066bdbb2703a936cd20263432c0e0cfc4639df01bfb98fd649e8dcfbff5876b7c2432398f5bc4f2a395154bb1d724468f8ab200307330f4bb106d95871bf8b56ff218ec0303896c227210d74d6a30d97fe0b0008dc174be06d8af1a2adc8993adc08a29949e00f312ddc515358d04198170bc1012c147c2161bf14943fa9157cfa856999579576b82895182f0fa8e857d80ded01a9bbde74b93a82d49a7fb8db9e007b456654fd578dca1eed44d4b18ae6b3040e335d456467dea06b035d2348119a86da154aecbd206919728e97c9003e11b50df8abfaab2ee8cbe22fb93c0390191c1e6d875909d46419d81c7e956dcd56b803a402fcba6dba638c2eeb11e6e7d98c420c1d659d30ca08d68182395432924300c6ecdea94dcef663a92df736a2a607ccff82da2aebc8913ca58eb7bdc6f3860cd107434ca3f9f3859f6aac08604eb89f9dbf3ca187204d3d3dd2f0ad585501038f712a7dd001c8eb4dcec43c88a91593a12ef139dae1c46b8e2cffc1b1aec80a78313451386725c9922e3ddcf00a1e5faefec1e13a46b82db099dfea57b79f03f7d95dd3ac3789569a5194270b9adefb15d124632b4332c5389e866651ed66063e0288be5e6ba47c16f54dd529da4f093a54158de0847d24e7543fadbdb9c30088478c5197f1c87778f0e7496b8e330b7185699ed1e9ec95ebf8612cd6c1370a794db97a93e35d1da17e8f5b0226502e2217867ffbc4921635a1688a780c120a2eefd3e6598a293338e2a25fd54870c41d721e38c45b0fd3872a2fd0635fac07f6df6f5b3f2e3dbbad65b7f59264fb4c531ede1a695adcfafda9b951f5fd5002dd0b47bbb8e02202830e0d229df3f3ebdf9d54b34e95c7c18c11e7cdf4eb5e08c55e118c1d22028df8a8e88745c46dfa00d0f7bec2bc07b7b3fab1e03c22c00fcbf83f6d00d3e02e84f4e9bc78fedb32a9409b3918482a44b3435551e3308d0cc893c1cbca39a6a00ae40419afc7425089e090dae7b9cfab3d4f6e6e80d9170bdfb177a9bd747efc85f89cb9bd788a364be4511cb7842474a1a668b9eae68e01b313a2737953127ee8b2002e23377a44fbb82bb7a2474ce855309c535a0d4807e5bf590100246e0eda73fb83d29c2076df6b14d6ec81f2cd4e308e5a23f03a03122e9474205a724b5c02e65a297852035809fd13d7a592beebee846e68e743087265bc6ec470d82350349f2088dbe6dbb230203cef560f992f7c50ecf952840404ee3c4525418afe7c341b703f20a36a881f6a9b22f4329dfbe0d90cc5f6b305a0edd29fa8c4e6b95304390653f1d7e1cde14f6a0982d0159fd4905d63e749059f5a8cd5d1f6913dc20ede23bcb66e555a56db89387a10a81d866b32aa769c0e128479422fa3e52f857703296cb2ca85499e066f2d93fafd17911af2cdfffe0bb4df1b83b432acc8bfcfb5fb1e55d8bea15fb8fb8d1a0a1d2df92ae5ad086e0f74eec49ba7fff60c9cc1559d129a8bcbff2fe8a711d8318d0711f415cbbbdc05a013969fe06faf6a6e81020d7568c3db0f8878becbe0f7572dfca725bf2a450ef5b8f283956b4e0bac3cb1d0ab9c88ab0230d103bb8e2e49c7867d938bb715005b7f05edc96a2342ac40d16b9c3c722c9fa7a9280404d30d53026ff966cfab08b1df5d9b9afcc998725faef06a2c41b8a6f31b903278489a1791779036c89c046d355b9e083c1d6240dc5e2a52fb9bdfddf13e564e3e525fc3ff62b5b847c9009097b2a5c623a30cc4935ee821d82f22fab62dc574d352b3a2499276b1b2690ffbadac64bb303fbf325ac32c95ae944fb7567d040ee1b8bd7caf3886a1e40b0207229c13e79ad99091aa46b012166e2c3422d25cf7bce150b14588b584810b018534ce1a0bc359ae61a46b342c63946cd1a224c452ddb72133816b653fb59a000a95766e15a9a0569d4d2229324fa20a445d3d6134007ee15cda95f78a86f00e61d4077068cd933e95a3128ca532d39088c0312431e0974e160f41027dbdf109476b16455039faac154ceba41d6309e4f05f4f18f3d25f940581f100f0f6d608305726e2abc420f812b5dc0105b7e7cfe5afb8cce250fb4a0e6b2133ea030804953aaf7fe1ce0ad1778fe6451c616bf2fbe9751cc4b1414cbc998baf95ed740a5643eaf5546eb6955fd7f04ce1e177a5390c89b032a8512b3e18ed7edb68aa0ef8d4486ed100a0ad331585f1270c43387c4bab0204221dd1b0b8bf181696c80555ff6cb0ab32bfbea713453cc71df4d21721dc130601c13e0bebfcede40baf0736135078357769bc4ffa2aab97e9b7b57744c4e4570bac53aec8fd4e46527e01613b0b6d7299b02b19fe0db8931631970b75fa88f8f03cd023210fe63e58e807f4381e73f6ecfcfb84fee8037c8ce762cec8bf2d30490246df70a4ce139773091bd8a877f326d962d01c1d30112fdf7f37424a985c406582a6bdd17d28205104e2b4032379629f05c1d1800a9af0aa6fc57f808431df1cd45775962c5faef1016373ca98d57a4d47862c44111144d42998f19f123fb4e726ceaf4ddb046cb105039c23b6d647872cec8b826367204db983f8c22c8281c7177ddcedd7da661806e4f51e7bd4b0d6deef4728993c1d20729dd914243dc4a587b49d53cece2cf900661c877e187c9d84cefd7ab0ef01771581c612b7255629db618ad5a8ed6df101dacf0673b90a99c2d6b4aa97496b2e09fa9ad220884f11f4429c2ed1a88a3e0136df831e13923436a8f7bd1a9b3b54e0e39a0d9ac59bc9659984d748a407b0099615ed68037157e11739694d3a0039fbd97b76c0956c88cfd318e1a000e2f106a272b96083f4ee475475a6ed77bc69ebd6a70d1d77387d152208052b2a95e50d4c3cd37a2b3ea14033c43edc805f4857604fa90b8bc7dc2268a6218282dfde0215805136ea7cad2588f1609c902cc74345f3958f36ae45fb77647ec2d60b2c07676c2472f78f0c17330886f85b7bea0f194f4b3baf977c76597d6105fa126f0847c82f22a9122578458f272c3413bf830a74e5481ada9322d536abd33899b9011d0cf5ab4cf4e07626cd9ab98ea8321e8e57b3b5d9d47918e75390b955473b04fd1aecf9379b0d72a7b329f9fd89774c3fcf67d048d200fac29abd39a2da9e0624bc88adb862e29fbaf437d6b99cc7ee67e7def601b97319c272bd08f9a4920f35d9b3866d8e241680629e996b47cf51e9e13702ed93fb5c0a29c540c2e83e0cf8347b3cd41606374c2f0b773ee124d08859fa0b3997b0bfd1f1741b468a7d0c2ab84ebaa8cf8dc805f74149da2737774502f9724dbfb2bb319ffac33e23190463b4f3bde51d1464df4c0688222b63bc6996ca42ef17184610065e5c8e3a67096e0069fd427f49259c6b92dc8e9575f3c90f6d80f780f029b3c696eb6da7840c301094d46f94729a1ad25105692873e406d404a3ffbb7bbc633d7c99eeb8df09bf2895c963c912bed60d46096e2066b6723e26413c20c971caf0c4d9759ebe0f430f508f316e112dbbc22821c6c5c092a8797e148a1f287c43630ab16bf3a10389b7a88774c21d8d7a702d173d9170659b225309a7b2093eee140c9112b5940ee1d72ac38802f754e7d71c8930ee07c110d453d00cc6f123cdc286a48393660bc809a3a4e5e22d6b63eb1399b396bca90ab4a7fbeef03e3c300d8e00c115ff06383ceff58b122857b7d5d620f21a2ec48f415037f3946fbe7d320b06fe926201e67eaf621265db7bdcdc4cb88abd5f9d6e729c5ff8294e6a9e151821ae15260c257471a9bc6adcec9e8744a7e709d223536100eedc0ea48ceb3096e33e041402c662f3a761fd3c624a48338612b27df61b5106767d82da25f39d0f5634c50d0da4294dc8239049fff0ffde48d1b4ae5ea7dc9f17d29e24a7fcbd801f4b35c70ed03ba5ef7206fd3cbe7da147343f2b5cc9181f1819faabc810d8e9533637690ac75d2f091696509e1c6c282b2c99aa922f9c3eee74122a10b5183584805b9d058fa0c943674b5f71ac12667e517a3b7055e77b532eed2a3ded77c67bee0dec00fced2cecb8bb6b5c19ccc229eee2143dca054818428beb2ca408a101c82fd70102f5faecb86e3a74b327911ca56fc243ca3f92cbc4f2d8cd212ff7a5a3ae250dc3cd0129568f12719e0168d479cacc46ffb0987b7f8e80ebc596ebc2e2466f02c6aac7d6876b853e4c255743fd4e715dda4e079059657ff315339c5601b181011ff1bd4f370a365a1152f4abe895de149c3fd0d1ad81c298d01319c4637e040021ed4eafec6b653cfaaf308eaf98ecbd578c80879caafa8525c2969dc96f1c0fc86666ecf163d818c9da9193392a4cf45277c3d343d76cdb4425e670d026900644340c61aef2ab8710126c0e66640341d21d65e0c6a88cbd46d0dad6b01dfe09c597b57f85b83ac342dc02f572a2b8a9637b413416edb9bb798af564fb8c2a01d7dac6d6a5ece1617fa749b5f840c9b6099e0e7ede7957dfd43e08db6d042700134cd85dcdb9aea16e42ec19184c4988744065034791463c239c169b54ec160bd2913c2ce85dc532b0ba3cc0a6e46e797101be15c2f6045a572404db58eb9a0db2cd7b75ff81202cfdd3e90c865d2018cc0b538272de1073aa3823c7ce67860f395fc3683682f4ffc047c973455fc5dedfc6615fd47e4bd215ce40969e3d5307ac05155da8fa4222a3efabc8e50b734fcda238062ae6184083a439c95d0f8c05087953de1444cf10f535e7db81c5eccaa070355288c8e55d74bc15223c6bfc0c2da3b6aa829cd823905073db1484ed5d71fcb1a973987a9803e2fb396e624c05d07ac6fcafdf746af9ade60e968cff4042c7cb62ac3a7cb942b784ecc3d2d102bee5f42688d47ff760c2a2b9396def135f3fe1f7252e344a0d804276a3ff0a03ea6714ac1fb57ac258e3eecbc25cd1e1034d55415f60208065bcddc050af8709a5d05e4075985009e7410989489794c9106b23624d441fb4083c630a07a4810b5cc1afd9cdb36e2ef6655e59f17f56a07ad8dfb099062f995482c7037b253f03e188b659651b8d0ae7da34d1287dc99d22098f5d3e050823f0ada1dd5121580260c7a9c602173345718e2df4d761c33d3969a6259ce53321e0f2c6a3e342ce09c8e402293d607aeb1c13787fe5e13215cc17844db4950fe5e28f0a86abfc190ffbf9b42d71c3107d92800c06cc4f035077c5c870d2aa4ef9a42ce2d7c72783066383092bf3f6a6735cbe5ac003ccc3e0d9973285e92ed72cb5ad486211b497039080c813a101e2182fe717cccb4861af14e6e70ecf51aa3a2eca665831cba50fdd30d7a4be3f8790e93a1cfa54921f8ef8fe08b50a4292ee9eb2d29e5df619019c13ec016577779b33c28d35a9ad2ea74babafb153eed65b44e571ea8b9d8907a0e254755f817cad8d1a99ede3a8a4916fab761a2a8768ec3e4776ff168e0703b71a315f27195b12322c1493dc8b0913c94b1d72308903b06b5d482344e0d6049bc5bb1ac782e8425d330d9892d7f619b513182d957c392835012caf3d4c580d8f436994c5dde1fedfaf9c34876588f1f797d133cc6aa831c525f2cf6b338208b77858b670ddaa49c671e2fd32250bee1a9d6c03d4c081beecf8aa2aba19660daba16bb2d26c2363b5f1e62d398c4659c30d4495e2dfd1eb5b081512580dd509bef1066801f25342e4a2ad316c9e1f7bb691358853d1d60824b596f401a6aa09 false -check_ring_signature 1404e39f6ee8ea61fe70edb9d798c4336d3ef33f8d296c117e96b65d10aa6949 c2da94e891bd99438406b7fa4cc045cae5ff5583ee60447b7a175549310336af 3 bcbbeae5a9709fe6d7f962c090b46c4decf8db5fd3a0d2eb897982a7491253cf d5fa29f371b1168de0679da37548977802768da1ce5ea7cb0c5cc101b360fe62 5fea607c4fc7492ab06be8b478a122b8ee51c47681d7ee3d05803412426e213d eec19d933adad4d92979beb5f275d60c704181b809db8a82fe10fe56ec6bbc0c1db67d1381cd8710a27c9717416b5c8d0dfc6c06486381995f109db01a13100ff321a3b1c09ff648c0afe8c72ef9e54ed71d1bedf8f2969d73d06eecfd21da013b8099c72b8e4f93ff99337101a951fb8e93c7daa719c3c046977827bf6b560db4265664c0117254d168fa118bf5aa0453da41518e6966659ecfc5a9fd2e090535f78c62bb34c04641daaa3f6a85b0fc60ac502870b90edaa49a4c1f8b75cc07 true -check_ring_signature 2425c834c1662613c6a73cb2aaf781c020a62363bbd96038aa0410af93bfbebf 4254df0622d3e85efcf3ffee6e14eab8954692df1cfbb762d317aedd0b3ad403 115 f21e7df265693eaea9c4b8c44818a110fdc8869e0f449b3e9b1a0ce57e47eb18 c8aab90fd916a674cc196c000b86a7af1b130b070b261e179427a7cc61a7c482 14867a67b0a085acabca02fd0267de3fa68471d0c8a8753fe8b93eaabd6f44b0 a5ca40e3b2c8bca473cb2b3c0b8c7f3c6980d7d17ebd85604d8a06f46ec0f39b 4cafb90afbca275caa54af18e1ddf9d24b39aeafd455b9415c48ef4bb8b9c359 cdb64173c229b352118599a5656f2635322eb7e77a4659dc8cf8496dd143bafe 9f26bdea59241ae843b208dff3d817ff54dd66c3da2334f675e73a14701fc9bf 8f0b5cb9963ed97e871c300cb5fa086b193102e41e5d618b2257cfc3de47cc63 dcec4c165af81413d97842a086bdc8fcece9ba70ac2642f2699913f0705b4cd0 083a31fb7baeb304e4be0268eec5afd984a0d5df8941a391d24425706e9c6c38 45f758bfb64ae814f836458b7c3b7b7fd0ef93566dcc0f55170af5be49f24db5 cfd491a6eb91e9056479f841c481d1fc0232a27c313b130f443bd603740292ee 561eedf667d01df5c6296804e3a1e0a4fbae178217aaeebb5cb4d736d0812fc6 278d39a6ae654e76f29e91b639dbbacba5bde880d496c70814397cd1d01c51bb d8fdb62a08a52544b2e03f2f791d815aa6807740ac93ab9708ee0d0a084da36f cd86874944820185095a76f1700d9f6a9353c18a2380067fb450bf0c63c4a65a 8cbcc936ed550cc91b6f6a73f1343facf6f9326455d708fe8636cad07012f34f 8c0c4cae15753fd65f7e1147a6f488faf8cf3903b006a73d0db2034ff07599cf 56069f17845fe692d19004bf5f9e4c0cc04dbad96b1a72ba74cc753335f9f05f d6d82f0d92f0b00c66b654c6f4ed27acaaa2e5c9aebb35593dc9ca52efdf0e12 994522fec3890c5c2b8eaa0746145a99d4cc6c17ccf0e9463950bd80e0fc600e a02dba717700071187c6297947cbf1fe3f776450469fb879c609758038e9b85c b2a13f08aab720aebe3bfcf8299ca0a255224b59d063bd616f396b3e1d5e4fa6 d6c415c46e3b43965bea029c9e9c4982f617ed70b07aafd5b5f3ee3fe4caf861 f2a472444d3e84c0634509891b7fbedc4cacb000ce84a27cf40fe3443339d163 f4e8c30367ad38dd9d529805fba09388a9d2f8c553f32707541d8cc165700691 51efe0acf6dc9747b5d8c61c33fe7bb21528081a51ff2024f7cb445d564a4cb8 db5dd0fc40412c9e19b84242604cea309a253280295cf90dfebbcd9b187b66e7 85efea3caba6a39088b49514d95b28ff46315b0c3015d99702fe94493cff882c 6581683df7f1a9f3510cbb92373ee04710bd03d9b7b411d0615c0f99c0f30331 e115eb93dd687018ade65d9f6661eb9fcef2f7da018d6b24b589adf571303eab 5451337522191b4f9e59a5cba8855e88d534dfc2f1bffe30fde7f7610b9536b5 a187984a872d10622013d8b366cda785be1ff8e9966db31a80593726bf57ad84 e9974d9567b3666bdd3fa1d8f3ec5f4bd79f290893a5e2040f415cf55b488b11 5ebacda1f9f35b06955891ebcfa567562415e718e753a0a76c740a7185ae0f03 bfea353e2f50bf53106d857fad7994b4a8a9e8330b427cc33df3ca04280c9de6 dd85432ba7f24fb1054927ddd350c2c8d6b0508fbf7531c4fc8591ed60be8dd7 66ae3b00c4b5d4a4ce9c77ee870cc86ab589205018395737801bde602c47a7aa 8fe8cf1b35bd2f6a195515333407d3bc2c31d5c3fd8f04ebc63e68c7caeed7cc 8015d22bc9fb2f482ba80608b7f1bcae66f3aeabd6701cd8754fb2da413d62e7 5ad8edd21813d246c6e53aedcdb0e63e94b3a4a07430a1396e5892f2d85f0575 7dc3c5ea2bc0ce44f3385fa4b30df9e0492a15b788cd2b3763e8d5191dc17450 318e59f22b76902a7b8ce2d4e8a30159a6e43d715d77b0175fc74a3d78809b69 88974f26a2f4276ed72e24f7b14450e6251cba2c663cbd794f6eb460fbfa6563 f34a5288663d694ef25230edc5b0a755503aaedbc5455cac60fced14b4f1da23 d79dfbc80c2450f8759335f3470a83ca3930fe99a26f576b692dd24f55f5e4d4 f76424918d4a54600379a72a446fdcc636f615bcce773bec2b26ef41c334416d 286a03ae011406b948e818758f067ea47e48ac771d5e3e44506550b834e7634e 93cc75db4762a5ba958a470370f93fe337f6baaad4f54bc0d3e7074921ebda1f 57402b9af0d80c96c5fb814e95253d262f56792b6fe1872afe691a1238e51224 48d44345f70fe6f8b823da1e3b949b4ea42ff71ec553941058cc62a76f2d69c0 c35d5b75daa348b791b8b9ac8cd15b2a50c59f804ad39f9ad6fd7a6b0ec98e04 c652fe8e2cd24253aa46acdbd9d34cdfa3ae12d12e5834158fc1c13e90ed11bf fb4741192331c2878b5155a480166ef9f9be33031d6c2639796865bf1090594a 90d27d8afa623c20e3c1e737736a0acfb14dac780a4a9b885f4eb04cd354d071 4bf02e826b8fdeec68b317371b1820712fcadb46cf9b02c18c3a320b013b5049 df14cf5b362e1d07c3d4e128327abbaeee7cf96d54f71327b289f7637bba5a94 18caf22b3bd1f5117b43da223048bbe37f264f344f38f70cb855ec8b36676996 0d096ba50f9e30dacc4f9acb26f3e0e18eb7091aff8a4af86b0e05b59a1c3c0e da5998a6e1412d5d3c12bb43012b8424473334600bcb67299381e08d7233bda8 f09775b4cd9543667c0c56c0bbe05d462d741a04aa55552f669150c29b13a784 452fceae8437344b63ba764d5c439ecfc4d9b3557f131a81d452e64e2e8141fb 02c23c6ab912b316f58d4bc6adcb383df274a9d16bdcd488e86505b2e99e1c57 d51a0007261bf0c3546b45e6ad0b48e6c113c4d0d4d46bbeb2cb3995a3b4473e e7d125538e9272c41544977a3b6c8e68247d754845eecaca16f853a64403e91d 242daebdfd493e541f6450966839f8dd705da33110175eb39e75a3b35ed1b42f 63d59c98561b0ae391c4c0561be06a30d26567b6465b0d5c8f40d519971666bd e822db39a4cd0046f7e2e1585cd78721361206809a99671f3f47b576c0ab1e8f 5cb05f0e9f35803136b30965ce8776490e616db224efcf806a19858fcd89dae9 930a543e53bea48423ab82c72e4214d884c4d77ba4855074b2eb8cb31b2f3659 6fe868e30ccf0a7547b7600aee2179f43f5f9e4e001d348a21e234fd5c2edadc aec2d25d5e7ae9c26a69c76114660f61ccfe7ceaf89d4deb2730e586ca947d17 0463212c2dabb8a4634738f52392da3bb56dbaf27b5215e9a5c8a2f0cc4b0d52 60d1d5b65d46b21a9555999560ac83e86c8aec2ad3ee6722271f2c13942e3711 984eae1c3a3bd278aa58ba2f84748d870d633c52c04f031a66086d7ef967462c f9d5ff2c3b68b4b65d3164f24bb6fafb53670024bf20b721101c58ef4f24ba48 6ad1e742c475a0f0b0cf17568ae3297397599df42993996c93549788f635c102 cc2b0a8a60a6e580f5d6dba4b079321c634018417981b5802e186ac8dc3b452e 0276d4ee119b12df7b60a97dc657278b0c1466f99066b31946bdf88e38330046 5c548555e54ced5529b92ffa616b356ee5c54e6d662ca9a1792569ee7d71a494 9a8fdaf4623f049f2bd8f4eecdc15c846fdb8141a877bc62ebef27c283d0b47a 00739ecb1f56c5e03b56095c51fe06f1c085928025f4e7f035f8cb863f17a477 917cf2b0157a56c646589c56b84079246c08e04d45c740e1162b2f06c7b2be03 576916725d9843396eaa5e718a6e81104da076f059db60b3ad17fbc551ed8620 0908028de77f27023ee3eedfe42b3442812df0e478db36e270eef9beb3ef53a6 7828dd366eb9c9e70767efb858a8e5d7140791a38aaf068085771c72e27b400d c6365d427908e91e2852b553f56694f5e114c2463318206fe99874d70d202f2b b9c0f84ea54057605a9c6048dcebd3723177c365916c773b8125ade5d341ecd0 6449521b464927fe0cefeb0e4921ab7fc00a1d4e8d3a9856e8e91851b740bb16 c04edaa6f3397c92803677e95e38a1e6d4aba5842a34543185ad72b3a40375fb 4e58420ad1492de6dee707157880934e25d5e7a509af84948355047004af9d95 9417172f55e7dac8fa88353555d05541ab92b589d793710e95e7b607c59201fa 94fb6ee58bdbf88202e8d8d6ad72711a5cc7578a390ff714ee198dfc6c2b6570 fe1c3d2c0aa07ea320a6d868b98c87b9498890ce1129ea050995f6ca1a5e3715 75c7b67921ac3abf605d3c8e78562dbd22ce0b042d87da750fea38b4f567678f e7a51068a532e0874d02064a0b021f6b633f5e5a2b302c3ba0789265a41bdd78 09fb1bd81a64daaf908f534495f744800246293d1d0c51f42c23548a629410eb 452213fb51fab56e14e410c6ec6c9f0699ec4ccd529fe9fa60ca7d9e83f4945e 96e9bbc6a9b0f2adf133bc3a6b64fada30046c6c214145a041c6786e73696e52 d69234a9e58b05adce13e768a68d3024f0f4ef3a346faf90235e9e1bbceaa6be cee357f5918d97d9fbdccee15f346981f729df36713c614f403c5be6c7b0ebfc b14ac7a219309449bbcceb7cbe99197eedb39489bebe8010021d1ea237ac32b8 0772d9ebd5e5012f7799993dafcbfb44e31f61e58c99c29cb5c2a9b2f73122d2 ea69f937d1895545a9f40ee3533a2502d8a66aa8762f33149e41aa51abe8f917 71b6758af91dfc138a7c1b4d2fc51bbf400040adc7f2ff4782a8532aa58a4582 3c2467898e5cdd4df82eae87ccea2509cc8456a8ab0427f3185dedb0c81c6127 92fb3a1bcbe51656407033366eee4673f1304330d79b34bb0869f0c9fc096643 6b8196afee0adc37c5da9773c1ba09ecfd81deeb0936b5fd4ab3253b5c65cf48 491dba067e6c20aa0e250f20c4408b69525110c1aeaa71b20d2860ff17fb02f8 e1346c55da38e76729119e842664c50da636702dbd4c4dcda5ae1fcab96e5eb4 d4e8062dbf51569dbd75d582556d288c3194917b8fe2a9d265fd5fb08f973e51 33833f5a74eb72ce179793c909eec68c933002f4b9610520a32fb3a6bb07264a f843a4e0161a6f75ac348f98424f2b42b32eae64bfcb44d37e3ea63dfdeb63b1 c8c437e4948f852fce64d5693d22a96b145ee52315d2404101116a897a345a84 01babe817e14be9968d4227d8791d17d0787cb033454dc5dbebf9fda79de27a6 f9f3754fe3734e8c6a8c43ca8ffb75c3a8fc54938200028be8715dad27a2de0072b3c52a87344ce0f5d12a9c61f236d95dc7645d0f940ccdec69aa530df7e00acab6d72f41deaef25bbcb44adfe7b5fafa509695d97a7ba6b0601f74121d6306ed66f04b107600fff88474094f3881dc5663ea0c379de1f4c63dd401fd174b04c5fda0ba717c45cc205e6a467dfe717a2ac03b9ebb906144271822b6be7a36039950f34cbd8a8fd84f4c8d1ca5bb04bcf4285e27dab0bab6f1184af2439af300a2f2df3765c348b86232196c2fb4a0571c46d9fb51adaeac5d305ff4a84a0e02ac45e521dda640095b888fc733ebc469ef1e5d195ff66d108ac64676ea8d740283bc566125d177a156a14016353e889b4ef9cac27f2001317269080d9809980bebba65dce76a942f0b5a05331fd2d506d0a51c3777d0110449bbc29bc192c6031fcf529b3da1fcce374ed2734fb285e1d258dad82be6511ae5a5f9d68bd18406eef824bd309d963031f116be620cbf4217e98fca4698eba961974a612144dd0613a1c55ccd5b4bf8485ad8e0f40ab3c6ed0d956fb82e2984cf5cdb60fa0bea08a6d3a111c9e74853644089de1ab101915e4231378d69e6c34bfef6f222566a0bf0cc4e0b429445838fd3307bb7835fcc786154bc58dbd58b6034dcd1235b340c4b4d426db2a2be3c4313e43d59cafc717318bd865dcedb4336739013afb2a40901a9a721f4d8fce1183c37d840e970b3a4ac47e5d1f4500ad428f4cd89660f0661bdde0679c60567853151bb8f2ca0f7c711d43a69269e784bfdfb80dffc4c06cb23df423dd092206e8c5170b9ccaa369c5fd2e7ccc701bb1ef61e35a185f209dd21f140e38bbaec10e32822d74dacf2f09da3a664afdbc9f0b620c8baf31c0635020a2e4be097cbf8425bdd311aaa92e1d0e8817a546888a6786fe8253bb90ff4110c49157db888f510c58885f7bc7d9d0077de3c48d6c6d799bbe02b398e03233a8c7bd7504a0457b1fb92c17ca9219fabd9dc646357f6a34e9b62e4d6d30fdd352d0dfea7b2ab47f8e39c4db53a46d955ab5c53e02f2a2e4edb02634d730c8914628dee12bae3e27a068be895488e14ce32ba6423ff7d13ab9f12b08623032b5c678c065ce7cba9d5d406b7b52a70b705dd200cac977aad681bc44825a905a30ebe4650a07b9b9432cde7feb3aecea363005fbe9485d3c3463626e7329c00904964c4717049df0258f9e63b65a59e9715afacdb47acc8717680c98ac3cb01ac41fb30ef996551470a9f5f9d35e4dd3928831b4f4aa06ccecd253ee0c75609a9ac9ee0edca4cb9b9597d2d1f54e76a3df84fc7e51023b187000abff6729e082cf77e1daa6b44d6fcc34be68864034c736aa35947bf1e6a99371af03a708c0aa8814f9adc6886e7625b857d47aa807eb5d55e2411bd613c8fb6d6b778507101bc1dfc90db57c69efbeaca4df517a99e41dc984274202ddf8b52127e7993820f524fc9de2d74d1f38fa412034c61bff9f976a3720488c319fde88e629873e700db65f34cefcb85918942b4f77d67b78f81cd761f79d08db19ae1561f33ad880abb92cab549f399240b69dc2f27b4d9d523c93cce8a5db7f908765f867dd43909590c062ad93462dd1f12850f3f9eb6390f94f4e336fb21fb3c7e8ab9fd5ea60a1a668e02eb3e2674c7a06c3a4bd93753efde3ce9a219a129dfeb07b10567fe0d974ac261c2d7e1aa30f1fba39b54d05f1ebcebc42f46e45a96802535d7e66503889bc4125eb92d665ece0bd80df7116a3e6024cd2b9b79e3733806eaf723d7013f6933f6496db0010e57e43c249a7dd1130d9258ec8e7a0a70b59afcd95fcc0220c2bdc36e7b8e48c620efe3f4aca4df35c1e09d2076e0c2d0318eecf0f74e0459fa3fdd5365de35bf19a4c90799fdad83951235c3de1c84146352d7e3b416059d14e5e85241d1c56925afdcaf1357a0ca30550e5212bbd48e055ca16510b6009f02fc06f7c3445e177241a8827afdc8a872a7fc0cadb9a0e8000969da79180c9b827c83aff03701fb814c3a260ed95c0e6785ff1a65dbdecf4cbb8c32a5ea06865e4e5f701c1b28f59e6b63bb83ae9d67a911c63187e2c930f793caa7d1920036c79b92e41cc62ee714e9831fed2057cdbe80f7d256e5bb0a1e90341d80f20159a4847f14087ade267f221220fef75c8285cc09dfde4eadfe774d1bb8fe5a010e2b3d063977fb23614f58b810db7a29b04924454d9ea79a4ced7e0cbc3a7001817e029ac382b7de14c69a2b2cd02879e5d81c926f137368f00fa42be1d4f50da04cf350d623617fc3a033b0d2a209f7bd3058c0940d9734de0d2b00c1698602a05686844342eca84ea14d5f8828d57b8e780f3c2b8dacce87210dad07c5550c9801385dc612483dcfc1d23f556b433ccb4fe05acf3598273e715266b9e0bf0e06dc9923dc6ead6e3011fe787b93ffaf6f10e8e4eb4d08e6f0dfabbdb1e70a03d717bb9ea6174cceaf62b0689e427f76962ab152a2e3721a684d4e7c372128055252bbb76fa6fb4450db5fa53877808419e29d4da1ffc8650b591fe40d41700c5c093c64b9bb89176ae2cb70e0c1fff8197d2882190e7279ef011725bc310b0982d6fb7344ef4e3168727f7b53dbc697bdfbd07ed0428018995640b8368a8a0ea250541fc1adac02e7a16fedd0c2d2cbfd7773af34e1089536199e3f1bc826081c25d75fb65ffbdd42f8bf719d821ce114bff7f3b3edba0a661dc506038cd70785590bdffd5030a0a6e1279ad03a4ca04510d4cb015c56727d25e1a2e18b490e97d9e4f77e57c60e2c1839961b2d5361143100c98d70220174d7bf36bfcaca0b56a1742d2de74ccb0e0ccbac2a7d9d80b8f8992d682eb1a2ff02bf2ada000705923c6ba0bbe97889eb6d211341b4071c7792a07edabb0473b14a849aa4ea090988d0d158ea68ae7ec592578f05f49835be35955c379d0197f11de6a651aac008dee9e575ce7e033582793cbcbc07c7afc1be822904ce89baac94b21a25ed8c08b627c87c1d1aa8ec8c4d81404076a96e7d34d6b0beb83d797695ba4ab103940ca97d513017fc21808d625f123e168394550eb121591ce95b9f1e58c8f57be30d7fb39eedc6ebfb9c6b62c33847f3e85b0a2ec20ce4acca66ac3a9aff0c5f0e0a39c9315a01c2ca1d51d46d5ae4712b1e7feb782a07ed14969fd8a24a3aa1ea01cc09c12890ea8fa1f524289073d0befdc861c6cfab1e03524366ccf8d6bd890fc48067768e0082e14e1559118a966d79d4831050b39316ff7b80a4dce2b02d01d38e5d99fa0a8b24067c997de81b681266ba0a04dfc6d35d949eec87c3ce430ed2f6b86c4a477be323cc741c14929a08afb19d5597e5d0a71c84d12f08ac0f051c9aa2cdd5c08a1e22da53351947034f044cda83fd67ae62d429173f9266310317361317687f6a239314e0bd98233f4b90008a564a0e5c0d1b6834d172c2920fdf8b897a2884ef2e70b6d8e3c1a41035a1d6ac595de68afc98f71c7d17e9760f85160f73b3bdaa1be672dbcb6a7a32c3f090996a88121dd38d850865524c5603e8aa623ad00ff08bebeccd610139c74a8f3aa24613f406dd29e28b1ad0710104c0247c103cfa45f309d5773f0da4a0abf1581312457ca06255980921dd424002481f820eb1e5141c99819be24d3e789db6dc92a90a624ba22c14905fb4a0d807085833fb74205071c53a1c6a5642abc64cb118c7a3233a30085f2a78086891046dba2da4258fff37df843f98f7ac3d543e5d4780f71f6bc4326d55c0b7ad490076ef69ece4a091a5e167a4c3819741d8d6553ed78cc0484dae3e69096f1e090aa195ae96eddd70f401a671e130e809e29c34f54b382f5ad65b11d1790cfeb909ef87d4467510192c089355c7fec0b01406f1c9939b37d00cd5fe9cdd606f330ceafe1fb3e4ec17eed2b97cca1dcbb968692bc23f4b81f035a42bbe678592d4081ef213219b5cba13d4260a3c2c96ba3bee0b520f944ba558aa2e06c58a8b94046701f6cbf5446bd30bfd821cdf31ec061ae1417eb1da36f2a8d3e7ae1667220e9414bc420c763fbcefa41372a5791129641fa60f421eeb9e5273f33446bd4c072fb873bc5c6f99680c1029e1ff932e536627d2c5727de0bae28afee973684d025b4a39aa116035833ccbd22959126067d37a208070557268f53fbc17ed422706eaad25a99c0f5290024c6ba32842e5f2d298bce4bee4d8e4521070fcdbfdd703b28eee65a3ab36cb21a38f8557cae13f27ae127e3f1398254778b7ec12316f0a1d6a9a69140d08b2fdad1438ae287acbb51ccc295ec0e3e3b9eccb88492f4508e151a2bfe25fc8e673dc3f080f64aadad24e5eeafb38de368c8090cf5c2106089029d2ee5c7ac4c1ad1de15cea3573f3417bf0a986027ad27ee6fe9e5fe10a041292d5006de77a662ddaf055bdee58aaf5e8810f2189589091004a559fc8df09bd22a7c69a1b8773dee978eea03e5bd95fc3a56bcd226d75ac7f9e884fd50a08c575201e359ecfc3eec5e6fb4dea966767fff040f94aba7ace4944f4486c6403c09a1a5f47b8712e7e2f87fd677358803e319ba366e871e5ad938c3110227302b9653d6088b69446a3be52d3bb7b56e18bbc5a5616bf97e2b018973031650f0de4265c760d862bd5cc8941e7d4522ac891878d024b1727ac604104ac39c85f02e61de6040714b3737a4b0ec2a687a18a31e5505f94d3698d4f76f33aec76680b766dd21fc49e24bdb5c235994f45d06cd2ac64725916dedc4124efbb48a71c07f0e9b1450fedccf866505747d305ed6a2caa0cfcfa4c307fd130aa6e45bff807d650f2f38ffd2f4172aaeee0f0e5439bb18655415d01e37763174eb555763001df2271c78541ffb4ca44c225ebdf68491fce1146d2b8eafaf86f8c2e2343d201b286a9d2fe534265e596adb8b8d1f5d1bb92785f6d57a4d70233687af6ba7f03b6885f26851437f695bb5cfcc17424b5e964f16ce47c2241be98dd1fa1465203db94a0228b9af2e7bfc9e66721979a981471ebbcf0ebb6cc93339bb7b6eddc0e948a9eeddff8cfc507032493908fcdd58cd005a243f4957d9d76e4283c8fdd08b045a064dd1ac1a6227434fd3af72ea74c92b94c8387e5284e25c2477788c300d427defa933e9a15e392c83f4b9a3c990a662d26298911efb182a63e484a300e3f3ec33d6088b5354d8ef722c38c8dad720f0b732926d6d48141047aa6f3cd010bc1cac53ab39bfba076b5995ae6ef04f8ca37889ddca9125b6636705daa800c644f9792e80c647857caff198ebccb84b76e102b5d72f2612d3449d009385a0f66aca5b5efd8c65094f9f596ff5e0ae5add641fec4223f01a97139926859ba03c675dc85cbfa1be8846b0d69cb76252242338481d0f646f42c125d16818c6f0563cfeab84018456d01721df2f563727705527f890a55983d30739f0ccba88c026fedd05c6d3b16247ee11d2b0abff56b82a93e653b7f264cd196309b9ebb770374bf9d86c8ecabd67a862f3c671ef56414240c68d130f5a77ae9358b2c288c0f804cbff16749bd31d6825edce4400a0fce367fccb5a653ae3f4b5e24aebc4a09646269651f6c18dbb2ad605951e7732c1b952471b851f861659e02a0b0586c00933da8d63f2caa09a5f24636c4740be001fdcc1744f71b05cc7de40c414edb0c7f9ffa0e805f840eacd2c74e83bf67d708d14e70f7823793195c0237fc59aa067fbbaa35ec6addea417db4390f12fe468440df2a6436230c892015290da4ec0dd1f9e3a577f708cb1652a42efd86a714438b70339ae029751927e4d02af543034986a14f5f9abd65af485c602264fa85f73c8d0ee1f9c4ac0106b7bc51f4bd0cbf6643573e6eff97a1fa1da91d5a265b753b357c77bd991e63312cfe1fab7d019be31b3243b5e85c173bd02aa3141023867bb35efe905dbf5658c10b8d328809cc9a9e3ba7202fd1e233e530307ec7104a6f99b27e9fc974d7db21517988fd082baf3932d8b9d407dd4e2ab48cdc56268f333ce21867fbd246f373c3c1a5f30349efb99c8f1fb6793036fa5012638b0efb2a5e77359a2c10e96ab68fdccd0003aa69f80ba446777463783d83ae2a59e79e84a6b97c23b80329eb3ba8f1665407d6ead36e42ce3f4ce12f0472b576cdddefbf8e7f943e86e7a85b518dcd8fdf08952132a571a8d6383eb9cd3f4dacebcef888c103875d2e460a167166ad088c0f887867c820baee13946f5d1b5cc0a27cb8ade0a862560311c82bf34ff11d4805d28fe5161b1f21f2c26ddf79273a1ee2fdc654c7c46ad60fcb5eedcaf1d56e0db5c48a9da7e04b56dec9db8344fdb34b608fc73567f633803331116bf9b2b20a30c80c3935d360d9b75e692015635d5acfcd6f857382051144afec4511b54e01d384e78a3ec73c91b69141cf139bfb54f56cb96680eddabff20535bdc5ce74003dcd9099f9fe6e304cb5887b5f681fe9de6027373b6edab5709731b80556800f35461b6f19d6e491fda55432ac3d63ab5d47a9e68b61af2a97c6aa81dfe2f408c7340e79b85ce1a6bb8a04c5c8721611e8c554dd8604947eef2f75b83d6d2b04fe85d0fe95b1fd2cffc2aa39ce2f8325a3af0e0986c44b4f15c439b9b138f401f495101e63fe9b4e87d156a3d3f21c5c0aa78f48dd6e05b601a430b876c56308ccadd30371242ece6ebc7ed32ee840a9d7f571717d21e965eb5e8e5fad93ae0b284a3ecf756853e378fa28fc3e4bde1530310034f4c4b9f30995ab569730fc033410b99df0107d614487ebb5825c5bb0a807b39b482408a0893b4235e4201300449f91e303f5901a9ba836ffb92ffa34ee13979bb805c20894fe8e2b36cdc104fb2c5fa8d07dd2a4f1d281ea591c72ffa7b9a6baed4a0f43cc0da0b157048d0dbaf4aab3809720b9601f492ec92097f03bf0fff1773aa7492aee3401bf637c00017d5f930b094724947996d17d73b543ba5d381d34f866642cc15b88cbaa5307836b2cf4457b3f9b5bd0fe1f48ae243de1623eb915b347f912485bd26b99180bb584d1c348bb6ef12798838ed27016300574fcf62eb3a2c0d60bcac51cb7360ab688ee4f3725d7ac69cbb793e0af35a36736d863030290405e98c4708e800209f25a3fb50cac1effb6a1d45df051244deec8e5b26ef1e9c77ed519507757db015bd7f260e0633d5ef8e7a061a7ee6c1bfbff4dbba44cbf1543d5a5cf5c5d7802b650da362cccbbd44f0ab1f8234914f80e8f5af11e220f4064baaed8a2c0cb0ff98691380bc4bf61d850d6d8e676497d3b2a3d5d972b4dbf8574e151d808fd0fedc2f845c2183106654b8be8cc8216facb57403198c9189ad8c6ce6f19c5c30cbb44142ec80c0a93b51193ac77b59b793b253bc66741c52493d60fb5ca9b88043e4648e20efff2f866751c4f00c6ebb25fd295578a321151bc85ded8afa5be09265a51f1560ddd83ef42d92e1e6ceafec70781131dacad7cbb06c099ecc1ef03ba7fe849cb98fa6e17b271feab05a2073d581bd05744c73729e274ba04e8ee041a6daa4a88e2702d989ed089bba7aa5c071c3a75e468fe76ad0975a41e16fd015619959eeaa971a54053924db12e17e8ea0b2539f974f78ee46b4279621aed042ac9f8ea874a2aa3f88d91dbd3d5e50ec80452c67010c18860a6f243a56976051e4c619e27abb0ff837ba7826bc3f10ac77efafee2b9080a78bddd3c01c5a70fb4fe386e99e040029d9c668bbef1f58063ac2acfe774e0d08e2698edcd48d6028e7014ff0d69cf64c97e1e683dbeb7803b04b5bc0a2865c6a6c6343d7a353307e25ecf469c2305e0cf96433bc6efa81abec70cf4a216f78d41357eec169bd30ff5368c49bf8e8eeb4113daf736a0b8082b2437fe6690c4e5dd4416c387f127026a85ffd3148fdb32585b5d26d8b456da871037f28dc8ec2f7356c2dab5459b04b821abd1c0b7ea60c8814b13dceab47dc2967f5679378ee38249ff49a18bf60df65546bfccdb205c2ee094f1e774f16248daf8977d58b065d804773935ebae05de734d54eaccee43a2061cbc9761f670697eb987ce248a9201cc50ca6c4c670b916fbc815515ddb8b52a9e08aeb755141edbcc580b9010993d0e96f94c9ff00dfc23c04de0762000c02c4ccb5df96b044b010deb357e7d54b96144873d5a6603ce243ac2d68f13e852d20e2a7ffeddda177bc77e0c7d32c0742d2f965345c104eafee8517ba5965129a7996411a5355f3da08abca0b6d2e86eed7178c7f27f0b907e5bd505403171f5132af97a994bb3ebbf471e95976f10fbe5f6a366b795036b190b261ff3449c400d92923efe1b951ff7ca6316adee2127642ad61e4e76039684d20af0a9ba71b1516793c3a6e17ba0de5593b7b52972e636c7dfddf3740667e54dad236c004476c009aa256e011bb96c0eab8203224cd6d7cd19c7d59b009587f26c69a5008dd031de1ea174dcedc0b16a4b5708197bbd3e4b4e51ef9c077fa2c540351b74b1c42bcafe0d77566781faf1a335885ee433de5a585036bb07ededfae780436bbafc235d65093ab9bd114e3ea0dcbe0536286a3f1f4a3aed097fc21b2747659a889c4c2b98faeed493477953acdf24cdae7a9580f0e05aaa040c8f5e1adb7f98b915998eab611cb828ebf26720b3d74acf19f815dd684b9002c00c91ee2a9d5f0fe6cc6a0f8b2834e424be8cab278a23461c9f4d0c3d6c7503520480f921a15fd6b16d9a179e384f1442e5ae80a733c7280bec6ce9e3cb99095a5dab7e6e3d5ea857fa435e70c926bf38412de7ab958927a0175493b2e5310b6be32ff5b7bc9b148208ffee1fd5353466198f6ddb3d4b927aaaeff40fdc9506c5fc351e24b670fb221fa76b8030ff9ced4ad13ce9134471b231bb674776ae0623190949ee0c2980d385473e88afccc2605bad10cbdd32db6ff28dbe817bec0482db08cfafbe9ab6944dcd35fdaf3e13a693f9f6a6ea5c3edf76130ecd8efd088525f708c5239da4ba1c00419f70261d87efcdde697dcadbaed193f44a49690e054dbaf30ab88b8efa8f09c120ffa67a0317ab46f8a31d70e46088a85ec4e50f4c0810ae03f8c8e7fce8059f6f4084450c86f60a3c13a107d42df926a5f9a30120879746022050f83cf8c68be6882b85d338d81b3a191b899dd3e094e04beb09dd1055f4dfe819faa4bf69573c105395c57ccc266eb9b7a9569428a78f870d0a529980fb17c52f968237ec4c69f3a9b8b15fbe510318b687357b1d85154f8b0161e68617998929b4b6e2150740cfbcca93f243b4e293fa07b478d2eb6c639e09c621d3e31472d4bb490eb6a46568bbceb50f5809efb7f4c9a89680bb3517090d94094a207f244a0035151e81726d557b3ad68777ec59b4e9d5e121a980b53b0ccaa0a4dc8b7d496be256179b16afbd522a306a82456fcfe193b03a3ce233ac02b3feca38d5140f19a05b5f8c7d6d39c2a360fab5c60573d7e35090d40919090acd3e08e8afe6fcf630175b37ae2bf8d33efb638bfb6f2cac270983b859ec97078cdb08585f37dee11994bf7fb4da3c757d7507a52cc0ea5cf2f69924fd9b7205f64540f531e81ca89e5f3f06d6c24bba7f5bf330af4641552a202c5ba01c8705b270fb3ca736bf219ecd6a30c51423b26c5cf56d068ce491e32e4bd209b4b30b84927054d326c235bec189e476ba933e30672c63826125be901ad8bba2c6bc07d330537b1d0a8988801966b154286dd1bee9f954ad59757ac037f44d21a25603033ee2f5c51e2fc3bef73bf865dd249bbfa41429dd9d8b65d7d1222b300da80fd91abadc044e9e93bccb1c7a59ea80b25c851b7f4e5e0cc5ed865045d4161f0536cec4f2ee1133bce13959b15d182ed133f4f8e68be98b5c45b00f8cf100ab0dfa8ca3faf9af929a71b908c51098e22909a18a26ebd17aafda433e35508ee00a120d642588c8530b06bf5da8783d4625b93ec25d4461cb02734b1e951a557a0e7162cd55d795233bf45e8c596994121a909ead86f09a6ca0360ee401589a0102208bce5519d122818e52d11e0d261a179b38b13af609c9ef2828e4a5f767f20fcef360bf259ac4695470040cdcd61b1e1f1cc9a6220c52fc1b6648f8ad0d13020ad91b822f009269dc24d29858ec505f9eb1fdd0ccb695ea14082cbd3b0a110ce00356acd9e02758f6ee547583a43e0b6c08c0a29940cc3bc2d999453aa9fc0c431ea4b8fe355492a9fb49911055eac9c272aba201f03ff6c6f38a8d9cced40824881a1bdb37dbfed290a58b285640819d20523159f11a61dedeafb2f17651037df21f34301693283b1029cc4d2f16693f4cd7304a62bbe4cbab241709256804985bae789b8539cdee1eaee25e6efe170678eabdd1e015942405efd3c5464d0d false -check_ring_signature b2e52161b9b5ec3368fae241457af6f73803ab754e21a00f047d92047c26bdfc 9d4c3f1130eafffa6d2d1979163b3596388f9c0331b927a97a4e559d2f018f7b 1 d8dd5f602f3d8fe98abb8b7e35563361944239734e08c62c1d5459cc4ffe4408 b06926489eb45ee496fece954b9c4329666554130f7b29be29a993f617c3c302c5159353dd7f6ebd83fe91f3b266f388700396458de75524864c2d3110a2ef04 true -check_ring_signature 994143a872a41dcff222926568492dc435942143c3859d6a75bfd9ce1f8ba3b9 05f653aa263a123a34efa89b7a77879d7442577d3cd1b09334d0fc04acec7f27 3 699a506be3fa870d4ae9c1c2e8ed1963d06800ae0f4e4ed450162acc155d063d 25a9b045afb98f4fb7533ccc545ae9b9b257c914c326f354dd73b74bb94bee65 b9f561542e9aff0aab5fd3dcbdc592e6dd60d4d06728d3211401bd2dea1acd16 7757520bdb8d74f120142f7538b9b91dc1b40570a1bcc70b8278ccc04df7d80bd0f6734ecfd8f7e18c1ba5309d9886141be65c0425cf6a67bc65763ddaf9f30166551991343a78844acd3d7727ebdb0230724188d48063dab5e954bcb0ac7d04ea2dab03654cfef9d9c03d6859302f809aad40d03f1df5945e8ef96a9c7a5d0b3d8133208b74868efd1574be852028af43a12db9b5e8bdc96651b05fd865f50f97c6793a59e5d1e29645b837e289847630477ddb24f3ed27dd9e0ebb04117a00 false -check_ring_signature 95945ae812702dc0800b1b133d3798aa641e3217bc034832cfba7132bca21037 f737cd3588acfe15ed05589d40d00ab996e71d81409b0c61cb738e152d0d29d3 95 1253652b6bfa52105466eb067358d94ab62ff31e1c65740594ee54b5aa61c581 440e1fcd6535dfcb4b7590767a896743f4d6fb993391ce06eb059d370ae53584 bc2464a9dd0c66f4223e11158cd2a82259a4a2f827ce3f0d0fb3258cdc3b646d 9aefc52782824367e07df46aae28b45f99d50ca8b401121d4eb5f08d7211c98f 5a01bd9a07516653d6c621119590397f9e9ad327892ec688c681e265b6df50b8 d0908adde268971bf7ab4b2238a8154d80b6c459b4e32e4946059658471fafa6 c23df0890d388bda7f6eae8177d78202d534d13013371ff4a9aea453e8ee7321 2f8f06b253685e7b609a083e4681fcbbb52663879a300b1e4215788822d92ff0 324b37dc536d455eaaeaefeabe473ae667ff5d05599d3968e09c3aadcfb5006f 7b460401a8b91ad56f85b780fa9f50495faaec5b7360c99c1f6548369920526d fba88f9477e3771db452ccd27ba7ce96f5e5a76ba21338bc6ab115d736c19f87 1f0af292863abdbf2521c994fab8113ee19c4f321c21b7dfbc72eee0016b7a10 13de8d7da2e109ec1c6499aa7934370ba1753893cd4ca76acb275a3ab92d7956 a838f24cbf215bb864fb469d47442bd6ac298f98935a2c204c1785644aaa5e25 28ee5c13cae0c723289716270b1398d0350c02585165f97b88e1b7db19743d1f c189f1e13749c4e5b6c260a6dac361e7b961e0e9f9534c925eba0fea8468d556 f809e80e3544954982e3574eb97039f3aaa536f29753be5f9fd1eb5334c319f4 63f786e7b2306b7ab235f2bb041b97f153d7a7e0e768ad97aa8f68eeeb9a6f47 f4846ae6db35d3802f3819738cee200551d1e4d884e91807f7914a7c235c9352 d2bf280ee8d6b1db7f72720b0188dce439da67bf0c9ce5404a96615d239e7f8f 2082c619a2f14c20b454f37b6f0f1d97e2b315c17c4a0def22d6761a5df10b95 b8124c71e93d2396c4dbf3b67629c9f2858209034a3432bf02b74ef5719c3778 6bff3f878efe4a55d7c7a69ad7a8aca22b8d71a01f5103c5a10c5b5ea6c02550 c163c74efe965a03aefce8424aa14ab40f1f0e25381eb5959f10f09010383609 def0bda494805746e55b6629abe3eef91539bb587bec943900a2f9afbcab43de 1c6a0d7d43f11fff8875813cb8efb11e0d1e8040d35b4f4fea32856bbec8977e d725ab3516c92fca29c596ec9ed1627549469efff8d78274705137b5149d5beb 73b44416b75e21d9e160b28c245463521a133633b3b4421749b5e39e4daefdbe 3c38ba28c0dc64be11dddf300a903bb08b70926ecd73630b68eeb3356933cef1 da176dfa83103037ea20094f00ce2f9fc04180a927ba30a38a489e4e08aa5156 a38c740bce470f15c92cd1ae55242521b09234b4785c351516e1f0e1707c4161 53007e8fceca002d3caf200d9830977ccb11a3e467b9e7332a5262ed351bc8dc 66d15182310a3f0ef49bcf447ecdcaee5e704821b5c0ad82679dedb18cbe6d14 e4ed338fa6b18528deafba9dca4c5e5cffb726b1c0a192fbce3d27e134b9ae78 ea74e0c198c4b28d9d8fb1a736f26891963ac296cd2455320535dbf1146b0bf8 994c93dec2dbb2f8e011e5959b2bb86958664f95d6f8af000a7c8080f0deb559 bc43bc99eb850c5594f19ed3f8ad0b2ee8dec7909a163b95eafebf7fc7de5db3 39b9e98b515fed939146f4cb594a0ea6be11f095c40b72bfabda849a1858c665 55bd1f6039f871708a5edb1b71112e197f0ae131b47350b963e76716927e39c1 4b6052a074b85282a709a55d8c00792a2394ececc6a0edebc97d99b1d1a6c86d ac8fef56cff77536cf3e14a9d09f8632bfc9408d78ce4a53744d3a1a0f7da7f1 ed3487997589bdf91e89d734c1fef7cacf3c7ce2c8978fd9c27fea457ce86be9 19dd2f54da6d58079eb4a167c497efe951c0049a8bacba2a5258f98186157f29 9fc43f8cd77588470e31a231141d8a678a7c3b1f4a20cf498a58e306719c3a86 981a2d76e9b75b9665b26b5b9bb0a91787ce39acb83da2ea2c684700ae4571b8 564d5ca55cb8f4f2f0a7c7cd44875bc0575f1954812caa012dfe19e8a9fcb7b2 1287d4610b56cf4ceca1f164d78c3df136323ec464748ab4d54e516c713d3074 34bbb3a3916bbf9a4b2fcf9a91f093b1194ff0f1de4d92a17a2c6ea40cfd7a0b 50db09f30d7304ab87742c8645f87090f0ca2c42dc7c413d25e8848bec2d1e68 e61d65d69d953798c2a90cd28fe802f9772a73308c293344be59935a5a73861f 231930039eaa8bc3377188a1c4e0640613d4f946af70acdcc6bc8a88becb37d4 cf45dfa863dc05372c7726f380c8a14d817d292b886d390de3f1a5903a1e41fc 7ed4ca25550e802cca2d617b6a58d1ff849c222239bddd1d8e3b78b16450c4bf a92b9188b2a01dcb7d75a614cc21880caf09c9d1ef8252bd5ceffd850add920d 3977956751f73a6d6ee0b396169b6f63326516e8d2529db80511f5d2f58877e1 cb2217fe1c8223286f0b560218ba68545491bcb8cf3edcf96854118fe6dd6aa7 fd6a167c12dc7e84a6f3337274ca8160a7548d67f38d2be296b124ba5f3290c3 efc4accfc9cf7bf12b24b6c64bbb810e4d9f4a20936dc5474b6f26bf2893adfc 96b815bb0f600c4cc68d1fcdb7c0b4c70c45f91b8f94aa6152d0a96f79abe510 b8f8b1743bcd46de65fd6048f9d8f8edab3ce72a5fc9750ea1c007337dfffff5 fff7b9e9c9a33b733eed150a7128fb0a99a4520b7f72163ffb51955fc421a7dc d102b447d96a3f4c7b619cbcf9314234dc924b1789fee700b4e41fcecb3ba3ab 6eb44fda7d286088d51c38e3705c1c621daaeb3d7d92021a019aad5f53c744f1 d061fabc7442f130bb30a00e15e8c391bbea0fa00536a046114cb6fc106b6c3d c3dbd2cd63644b23f33b3b8266dee7206b98c4a7bc3f2efff7873a649d4320e5 7a162aaa40262735a9f2bbe4d0e7e5496d4cb2729b08cf961d11605f4be04ec4 12262db00d583ecd4779c148d7d36eff3d1b8f074e915f89d3e70f7e84a110e1 c8b6c6d096e68798b57f222345489a2678f532bc6174bab8346f1b698a74c42c 630eac3f9fe92344ef8fa73fceee58cf239049ca595e617603844d0a186306bd b1e4f487ec022706ce77d4e5a4b71af6a0b8a25cd0a0624ecc34a2de068611b4 298051d27e9052c8d03c40e5ae61856e41885c4190ba8d935ed4335a85f1c107 eb137df07c93e19c79ad03e933287eef3d3f7319b27aef8f914cb873b28d52b8 32ded800b65054ce16e62fc079165d4251fb6cd2e9d152edf228e8825ff199fa 6ae0e64986cf5870db14698853af117e094d4cd3b3f8aa04c2ffc0cc00c9c8c1 1fcc2251bd7a4903458ff5bc2e835de36ae99a1f09981bee21b89b321d33701e 3e4d904b0228c85578d4cb408a12a8522ef3acd66544e4838139ef47b2ddf288 c176884948da3aafd00cbb3dd710a8fe2ce880c36cbb2c87236992adc46aa2ec 805b1f9f3a99fb70beac6d9da58adc38a18f3a0b578a16e7cb2033d90e8864af c3c324287068d3197a7a5409c83738e7b0150d7230e7d416dc8eae19b0cd56fd b6421cb476d46477182dff6164617e06d0b680f1ba3e50c0cd942429b282dcb2 bd1107bce0c1dd75d1fa50538a47573760749fdcd129009a387f862ac682a4cb 3924103fa33491ff2479fac38eb71e8350c92fb443a664da44d359826f282dc6 f5c1a95337008091a616fb0544fa9f79af98d20045257b5d9408da476c5c5e28 ff1392a6092343f3545a63d46f27beb2cef9e9b75235f23630cb8bd0b40d6eab 5f07da9b25203e9181096abba91d726245297af19d943df8ca3a2e1c4ebf6472 1c4b25112dd7e19c959c2bc98daf2c96f169010b629e183dccbb99fe7dea4ea9 29c7292ab296c1e536325eeb03a79de1446ff891eb5db718cf952821f596d8a2 3ff25ce61e0f7b236475f495e8e563825c0f32ca65b590f702309b357e409b7a 45007241fa939dede382208520f6c9da48a796d6376c37e868c44158d3cf6961 f9d97dae850a55c786ac66c5669937b44c8219fbd5e5b6a87fc253093f7569c7 2225f2f93f8fd06e8eb4601a21fb847dcfad84abc6429fb4688d725083b88c87 be2e05b1a60c214c75ac2115054c477399b0014103a2cd4c26f6e2a9572846de fd58bd56e7667ff1ae833685aa678fd08b2f7aed2bb2d7eb4d7162e4cadab35e 3172f9b961eeeb1b098c84838752f2d9e2ed2e3f140df6af3ac0874444314ee6 76b9c01693129d79d7de3fcbffe158184ea9a5920658bc3dc4165ad8a97340d3 46a2ac335ed4e3b4f5370a736cdb36e251ed5c9ec1cd338af307f169cd8be60af3f1ffac8757421dffa972ad0956810f2605bded64d4e040c1b01f5302d76404596e43e75b3714df2c072c28b09b2d84666dff98d0935e94363e77c31325a30cd08b8f23d5490f677fa0c5b4e84f2d200ed6274aab739cced466a368f4b3930b12f1ac20a207921dff790fc43badfb6951e11526b65ef136e583fbc611ed740f91849cc6d4293b0df39a7e9378b0e1bffef90ab4cd8f280ba21c93207449f90a0ce7d554f77e8c06f482b9e6fae35c22f4290c323029dba7fce1275ef2f0a500bf227e2ebf1e87d017ff2942551e092cb9c2801b2c25b29a9a88df49795f390c7cafc84659a8b5974f92e31f993e877aa38d40a38ae0751882bdfc7da18a5c0ccf77330d2652a2baa3d7d21a9b72ed0d82b9962bfdfa08a7961c0a7b608498080c59c328357fbdce11854b89dc6f7af84aa9a1d18785fdd51c9f62304db9630f56a3e4bfcfd55a1c40b4a800ee18db4c863858b9eecc751896746fbae6e77909f6a79e7ae2e92282fcff07837ee3027e55041611cc9a1b02cf7a5db464123c04761bb4441a6f65b700893bf1d5cee106700d7fb864489d1f88217078a980010d8d65fc53ed518b44fb4dcee1feaef9142fae6bac82cec228985bec9e297c1103dab3d033e2ac37d0d9323b15bc9de50949f7d878fc14fffd3a47a7c2f9903c0493bf28f6a3f761293297006daea05c535a2acbef2b4e95d270324f7a5c39b9032cb645002aa5f24f62c82fbf2d48edff4c2604fedee296783a219d264bbdce06d851e301e4d1b91ca5a274decb5144e52d0e40364ec03327f45c0d525a9862053d2e05791d5795e5b8df0028379810b72914f71dce4162e74b5adebf477906080f13b1b763e8f42dec894d86c6de0c1149f24dcbd08307a00efaea885d059a08998dc04071884900bee1ff0795301fc805c2a9890c3604045e9ff7d21b4a2b0b0f04725f0a055efb2f58c5018e5840b1bc33f6539b807036a4c66b695dc257071f8077283a6fec8f0529393880ad303242fe4f109c639e72a16864ade204820cb33ea3487f9d4bec2ff42bee2eb051f90a859452de94bd1af04070f48320d409107bd7ed1d095d63b027446a08e4eb120d1567963154c3f0f98a45271111110f6efdccc94d256be6188561b4e22ca0cbbc6a6b8d89c1fdd294a3d7834cf41103b71d627e590ac712819906b5509b4acf6933f028b3103969bb53bb43a63b560bd98d61af41d0ec3c2fc627a642dcd7a97067886d82d0b889743739649d4f5d0800530a78387e97a6dafa11a3ff90d0fc8bc39c1ebdd9ffd0e189c31e667afc069829aaf11751916a29777b0b1160daaa9f5c55b54d11efaa88f74da309120d0d253e09ffad2a202c1f892be39040ef10de6a69c7399b082197ea767a02a0ce093a57ff1598efcc804228f564b5eddf817dd5dea09718119dc3a4fc9f70cfbd07400d9c2ff158e01c3f4593297d460bc8eb4f22940f6c1aeeab48f3fd947ec108fce98e84f20f1163596400d89ddc50763121c83e14be30c0b7486fd6e40b770c0db40397aa38f57f847a5f71ee8c34306806c0ffec608dde04045d19810d7301d626361d8369d8e4c5cd13a373ba0ec27b6525c670323096842bc9cd0241f8070ba0125701185dd625b0ecd754093debda7b00850031cbb344bcfee159681d03be79af3d51781f8f57145888641b8e933588d30de4ebdc91c012187edfb3d403817c23b624a04493d30f29b099abf43ca58c30fe957de601148562f95210cd053d4ab04b9e070ad49459d2b9e0fad956b3770fb8b3e61dd740bd59c1c20eb6038bdd05713bedc5bf1508f5d3d4037fe919f0c9853b91b6b78230936c4b8774065808fe9cdc0e4dccba3c87a283b6c45d511c8c97fa88fbd5385a2238d0fd990281fa5e1782430ea36d29e8c074d0fa7d2bc22e1a92555b93a22037131a8d82057323914a11901c546386f857454974b8c5a7a7c847d4303e7dd97752989db707b433b92c5b2969363f8dcadccf0fdefb51c7d0544c1d00b1177d375aa18f27039ff4cb189fac0c1b0e3210e15d3064a33a14b1ddf0fa8e991d1ba4ac91f7220c8b8fc95f08a310167c7ac065b7cbbdbda383a87f249bc7a1386d4671306d1b0c6809a56ec3d6dae631e29b0e1a6ab76f27e99f7b91f158ec894e1c47788a2006a1cb5391447add5c75abe198dbca298d6714a0c316085db8b63db39d4e39aa0ad08d0af7cc1f57392614cbaf0b92922c59caf7af5a714c84635f986e6d51650cf20f5f435c1c58c7cee4cfa49533ad316a40fa29e55fc4dcf970e29e4e06810b42a56e98dfcae78e3bbf7ac4f409a253be4af4a01a7639c34f363a9deaaf400890f03182e11c13066948155abf87b1d22f5c5ee0f2190c164e89b88bf896050701bb78179c1c31739c8fb7771683dc65785433e2f2f47eac9d03e557c89db406f68b18956bbb65481705c4b11449711619692f5bd6917913861e99ddbd48ba0b3e911cfda20071b6d5f3428dc0dc9934b80a083e4cb7a4a3e1e5cb4534391d0d31e7e3ca626b2c44eafbc53333a2312a51bea49a7e544a928177e7ab4ef0e205db1f39080eb5d59d40bf464aefdac28cba2fbe93d78b3d8968bf28ee1ab57e011003bcb4eca9dd46e6e605e47807ad5409163978c45b0053b7c270ec4f49240d28077a7c5ef11fe595769401ade076ac45d411aca3faae8c88b771fb10d0a1091bb2ce3e830fd43e133d59d93d7de2f925b40d76c122368144ccea0a1d052f05258420db69486253767df2648e5920db70319f7ba78dc36b378100dbe66dd1055266d3d9c0fa7168d8d67dfa5dd8309e7a1a09bdb7b748425c5389ee927f7401e61856e08a54dd4b91f6edcf395297d1e6efcd4bf1cd0c5f0fc9c71a1aecbb008024662bd7faedccf6c9ec54e5561cb3f7017822ba19b6d0df83741a6491630015f0418cfebd62e98db07fbd820692ab5fb2f765b89c0795963e552d18a262042ac9bd41df88d72f1d25067fde03e2b3d956353c0fbfa88151a23ae3b85d040ca0915996d79faaa2c37ab933ec9b02fa1cbd5054c730000543c1b4d97eb9a6028035d6d7c3e70e5151442a19f317db21ac8b682643386808bc8a4533c8dae508e2823a0a11ff6058e27d8b26ef37f20c90fa34b653bdc5a7874f1f03b17a540f9b6b3586d085aefb95808fef0e978bce3695381d37ad885a2b9c48610ba9e005ac228277c4df0eb6b22b89715dd82c252bb161e6a4ade777a62eee1a70083a05a428f700026c088aaf57db46babb8492dcf68800eec0a45c1a8751c51678120884ff2295396fb233ac25eaaa4fad8eaec73f146f5307e529eb8f5d59d7b62306f138df9d583626eab656672a1698f692fd7415bb23cb9f9831843272f9edcd004dd165059ff1b8347f92dbc4df55797fb7535fc421a6e7586a689c1fe08bde0fae499a514672b3ff2360a95a9405faee025d560dec0de3906ea9ae34f5d9c60e13b52f4f350b434f5790955bef7d9932a0995c1d31cf2816affd6c3ac188380fa2c8ae6b25d5299b8502cdddc2ca9b77b5ef5937360030a4cbf0d3780730ad03ed9afc461e8e48f092fc92ae9eb37ccf2bda4abcb93cd8ed094730cf84425b0723b9bf9a3527de4513d48c8ea0e9c9b921fe74212867afc3755a259463af8909a6af3bec44a1b452621b2573e2d3ce1a23579946da8ac3b17dd12d03286b970549dfd5d4e631b7be02d73afbc0435ded9906868a11f58d7cfc4a440d414d510ee00b6ad9a4d50381068fb48d6282c6d1f999df5ab29ee6dc76e4074866ae4c0d5c77be953334263052830f04d39cf44938a68a17e68a52af547a4f14ae5d7f0cf8b38cd1a98f7874df6fb32c45344515d81c6a3e90cf7b66059f150d44c47f04c5e490c7382659f314b2990a22aa0a05321b682d6353665422fcbbbf3c0dda0b7336feb30079915a52825d7891029044995213c047deaaa21a881c57ccd69b04f6615684ed227671e11bda78186af8bac50e024d5e452be3f14597b894b7fd0c7a6c6440e19e93aa0f31368d513f2ba7bd01ac892f15872cbd6d70d70d81170afe78c7b63dbbaf6de596e59c192b57de484a1303553efc4334e36206daa239091cc96310986bad70afeb6e19f77edb2cf39034abda2e888f4f80d7db35855901ad8f3704cbd632c547250f256f8047090a7366a787dd8c0c629a84d1b1b3f9020f82b587905d4d28627638bfae73f0a5a8f4df65d2dde256af303face4e925059030174fff296bf7370e6652c55e8ae7c658f6e57acdbcac6c90d3e1feb6e30eb9b6317b366a079af4f635e76e3a10bbaf5e92e30a94e74a5f89e181f0424005b9068ed6c4ced190ce545a57c188e92f222eca8256568563a012d0c21780d20d64ed83feb9d35451103e1e47cd706c6d954fd0a5f557c1c9e430d77ab89dac0e0874448e594fada22dc125e26dda42f0c16025c20059825fbcec3cfab89b5c0b6b3d7ed6787dd88cb0b64545ac631092f85a2e9439b78e3b0ce422866266610cf6aa467e84ad34d6b1fb3b2d544b2de5b71ba55bc12bf2fdc0faa8a5225338040e1e97a7ae54bd5c049063675dc863c020fa294d445f7bf3415887e976990307a69d17517b93e6dc47e25f54191faa34f3d936dce2fedd656f0daa827c5fa10b28e0e325a04c1099c382c1969bd9448e8be2258930530a386b7612184e213f0c515023c236e087b61a781e50d20288aa88a92e4a21059b18412a23d326598d02660bf410b171f6516d8078d192db79d9f023468ac5c3e3af0e10884b0d0c770ae59367958753f97805237cf58979ce268e5175c9911fdefb98425f7a0712b303f56a09908508ce6b1d919da432ffbe11d66a9c0698b5773e028a321051674308a876a4418740b3b8e8068e1cf9ed7fdc5b764142da9778d4a7c33f562fc3c504c1e013e8e1b8d3afb1f79182c8360013b542322e99bfba7631d4cf41c156c70e74beefea202b4aab5c993f37cddc2a0a28aec30734516e9662dc38c5c85d490ad6b2c729474711704388960ba3d256ecea9883e20c060cc28caf0a42f9ab4108e9b409737badc1b6050e406a060d902d884fe09f2bd433c74041b7f80afc1f02841a498ab2a40910b3afc84d72295e6b416833d86610a71b7d47fd10a668a50c6ab4ac8c7f3ddcdb91b82b7eb217e00f96fb10c9084940ba30129c965d1c3a08513db818fb5e91b91a3171e3ebdca3a6ef795e085c26f4d1f50bc358c5300d069c2995761620efe617dbf309a0d8d4c734ae259749d1659b22f8544cfd0d2607a14f45997e4a2e7322102304bc7df7498ac41e1912fa6e7d1c05cc25f103e7017532a7fdc891a6783c09f95983af77a2c3937ede36ed1c12846d42001f8fca0779624c849de3b0e1da2cd4005d469fee49899169391cdf5d4516fe9f8dbb080f218cbcfe636945bb51827dc737efdf5212da73ac349a84f8300df72fc0520503958cdd33811e47156e243ea1237969254def88c1c1fe66e42775a9095492c104c489b9418e46c4034c5869a87309c090a349bb39cb2e1c83379275a634d5720c31a21132e6c311125d3006eea8eb162f646743e0e4cfb90fe6731a163b9d1a0aad27b4a542859b56196a1c074675ef94694d0f6dd8f982fa5b872c0c9396d00710ca7d8c385e5504af3db8e2349ca8f8bc459b6897b8b28f2dedfa7da0edc50bbdf939dc8fb9d41987afdc46eef46ff1f8582495add6aa804bb1dd180c92c104345213d862bd0386dbc142eb299dfbce18ea49b5bcbe545f78f091b3013007069daf0f00b9b97f924112a8ce182ebb89adc88b878483684203d15e6f1a0edf075e61c66044e1f702d3a0a0186b23c275f1f39893582ba27eecc59312cdef8b0e2bd9d3d72b8d540bc1ef01218282712242da550704f35cda02cf90074f53e90a0ff3ccb718d49129a132c95665931b52696bc4ebcfc518d779d5b8426ac9130e1e1910fdd0fe33dec0a0d0ce3ed95f02a7a1def7d15c4b9212fca454975e4108da8efd08573e9472b47553783415507a3c6b63eb55224229f7e22d97c3374a0f59e4118238a52fb3b1060dd97188aaa2ee9b8cda6bd9136d9d9b179d982ddb0f3bc3532f8d1160b9d7dfb5215cfcd0ee4e49ce905681725a702c5ade7e091e0ac221ab36306bb129ee3da59566436fdc0573af9c27f0f1ab56d2fd8f5f00e3011d74539bf729f717a56a8159db9702fbff875648b3dd6c38fae1a01d1fe3c304a56e45c8f11ad9a8d83fefce9d5a13239f5163192739d1afd0ed41aee42d1b057afa3264c0feadf8a6d3c0cdf315306d74304298a6fba00ed737e54d44b24e05320b9fbf852a6f13809c6e35ed912b0bba30a51f6a6d656e6ee9a51a83d0fe0bf43f97f9abf2b28b392854b1c1927cff6f2ae69073d6cf75af252e5c9a5fa801fa637bfdc7f7832aa0fa7a9355b8bf5ab1cd8e62f6eb18d6d3b07e90dd16690fd04d085c3ac44c0e4b2f4b79b9efc310ddafa3d312f5f6cd7a854764ef4812065939997d09f7d6aa8466feec27922f1ff126a612d020ef479001515d3f7bab063d933b2d8e2d12333b581a0adbd1fde5e97079bd2f9aab909d7d1858c595c80d9692f8a628d1be76958642950cb36b92fff12334c268284c5b86ec9850e12c0a23e166fc9aef0a6f5f49153e65c36f19c5d08d4fda0bb97ec5c57201f5847301e3d3499f5144d26218ab486bf43b457bddd92f14d26bca3316f47eb166cfb4053f8a406045522b1228227c0dcc1a64250684c68d3d3992077e1d6238c04b1000c62c263d69a877d5b013b310d5383df6eb1d2481f32f1098754ea2759972d1087bd1c401ccd78b52fe611290461ef4987b8255f7e6d889ef3fcb925da9a20d08271c7a5761d8c4445041f18b469f1b358c430ef4fa4aa480a9612cebd77b160b7edf6d38cb6236cb1fdb15c582b52866d6e3dd4dc44a9b8f0e412553117ce10e612af3b7c0120b71fa957ba6eeaa98b9394df47e113be657631c1d2280062f01560fd0192879278c1eb78be3e9a27695b5e7dcb67ac2727e90b2119f55a46a0c151df3b9a1437743e5eb539b663b374bfb55d29dc3908639388654e147de8b04e88c12a55ec7864adda86c85730b9d644b44b842c64fe1a91793ff5b945cf00b5b38edc12ddd6c5bcdfa6f5c96aa67963e80fc9991a2021123c5cc5b829c77016ce92cdff611bbb90e077c6e50d1acc1e11322dd9e677ee5c3f5950a9c60fd07197c834e815a4be0aac764e23e817921ec5ec5ab36e64b1f9b7c4aa3702aa305fbe807de065bc36bac314b0b79c9492f6f5c4b9556b44c35055844bdab2ed20ff1584d6ee08a7ab6eb1fe3cdd690dd6cebe731738be3bd8ac890d5a517e34d0da4197bd406d4edf120d57f3130652db5af96a7427b4a90cff8f29f2307db090b9a0430141f13d725c9f56b083382b9f03bcda4c239333bfb1fdb61e098584b0e06c6c067cdaf070f942ccc0e62e30f722a5d31565b9d91f210fc29f81ec83b01e4ccbe196becf351bf9f31727d9080d2662afbd26a95f83a3727d0fc54cedb0cbbba8531116c4dbbe51f49ee7289a962eff5c6027540f5d66e68bed11770530ea4089c667098cbcad4784de23a47e51e1ba574b2150e1392653ba714688fd904075684bf18c36388abce23a3be4bc69436f55f01b7f0cf60b21b4906830c890bd794e599e4a1db953a70ffc2052545b5e5dcbd4805f125ac646bb34099085505e5c7b196e3c129722d3eeb419f06e73815bfc6b15f309ef8fc721b480e283b0dc93adb4b940ac896fa305a62f59c7dc2a27f5ec400132e5a8f30869077d3820954c4da5b95310116bdf3d1c1232017c2484b795d2f66e1534665320c8726790b543ca074c13f07e7ea7229380605fc9b00c18f2450d2076e8b784b39583d07047850b3755fa1605312f8e8ca8dbd2a245dceb07052cb9e064c657670bb533900c907b182c8837d095d6b144002cddfad7a0e42077577ccf99e0b82fa8145090a72e3c74eff516a0208a95b19a745176a9a79bf828e265c3079cbaae0fad11401c4853356010d20c1b2c6ba2ae4eb2ad88528a53307eea0bff1ffcdefac68da078a0a5a64bb4c912e26d4c9d72c9b46e859eabb5a38e8d9732cb87ff42d899e09cc48d47c2c4f148791500e9c271e228fae929ca8b531409ecb8808f585b29f0a8dd54a1869c5a115f7b3c4cde00ade8948d93cdadf9e7ceabbd8227f55164f0f0ed0f80245163565d2c9c62f604ba9f770c3b5b123ac5230273c1f0e8a605c0812979386096e9e5a2e9a330e5de45f7ec53fc222b95e6cc65789a44fe50f7a0cadaef43c78436d2497d7a1dbf59414f8ec01a39628fdd17f4fb64b1424b75d0b9732cb553f96806bb8e137156f516a241507b10d8d65d043144f8f2a9444d406234cb79bce6435e6e7ea78cafe19d2a961683937526614a9be3096bfbff8470956893f6533dbd4560babd82977b3f276f8d9e71b38e94191bfbb3cd83ddcb60b8ac79b8ac50dd46d35ff98ed15229f4b897c88b4aa7b377026985f69aa48dc07 true -check_ring_signature aab2431a31b07e53929e6cb25c44a3bae43942818e8a602b70181698b2aa196a 08769384a9ccf26c6c36afe908af02d999881471448607612ea51ffbaf939643 22 4b47b9b485e0a0463163fab7e05a6cedc3a620b407a6cfbaa2b3020885a66d33 8664d52dfc39285e7b116a8cf2c1c2998f15e82b7d25699ccd7106c2c507c0eb 5076cb0842764c8c073654d944a661b048622be4179cd33bbd0e67701d829e2e b686dfbe8ba7d6a3419d25dbf1e1c71424a4b3cab621bd03dd312123a5899a6c ab2c6c7dd0e8b6720808c83ba34723e8de771786119e32d8f3501af5db4af749 5e69627817ad3b57bb0729cff5dec24cbf26aa6deeaabeca7dc08578f92c495a f2f16abbd9b02f75b03d0ff968f7219673882f386bb6719a87ceb47d8e2e338f a578a370bf7bc82ed3625080a8d1650a74e80205eda0e88126d7a844b4cd8fdf ae35b8aecd654d75264a0ff566bd19f6fbf5be15009e92a884a1adba77d5596e 3a2be5ac07e8caf096c8afa4c9780097d0553e66a0f98ec4e3561e779cfe84fa 1e937926b6698cfc90df4c5259830935453b27d817cd5c1c3caf71d17ec154c5 b781b95703a3e97c57bcf3cd47c3dbd8fc2c3a133e9e031bccd4677b10dbe6d0 c47cd6240c6c9c2f9ee21467ef625b27bd47baf65a7408b1b25e33eb835809eb 4cd3b74f0610ffdec34b3938e93192dff945331771ac31e2e3db984e262b6c3a 35ba04b2d71ca83cf092d643bf88b1e1a4c8c75276ebbe24d7b150677d57dc8f c299fbd2fe907ca66c0312f7e99cef46fe5aee540eb2d486bc90565bc28d5292 782c9b32061e8e56c47210bc47471de6ed85273fc4535480fdaba1c2b1374f09 dc102afa6b9e0e05f7141105b3e4fff6725f99b5bec69b14a3dd5ebc0b54e99b af3492d86792603e8719344d2e9b9187f69a448ca7063d04786d07eba0e7bb1c 3abb90326269b8f2d710d9afe7dd2cfd7fa2d041bdbe62c4849a50ea10e6b3f1 75a1cb14efe17a2d3d25ed77e9e5183f1572daee16d8ee1444ec5209988d2093 bbc7d21ad6ec092096f1c11ada626d9f0d9aaf62ed69a952e58f5ef306e7bc22 a09d58a287da3563bc41939861fc2cb61321c9a5f78fa3fa8156c40a2234e100c9f1f352075b67bde44e0ba3fc1db39953c149484faa53180fd7b6b9c5bc640ebfa6d052472a056a0fb84aebb4820602c6520c1a81fabc357716312c9da1c205268e23b0b46bc66dae263bd5b2c72038dbe084a29ac410be34320028d87306046a92000d9f1a8e33ba1d27b40982d67f204b1d9b9b87b49cae8609b0b28f41055f03dd099b6ede0ae6a80e082dcf4ef6c47b8e8438ebf11a9489065a1a6545064b04d495a22adbc25633032756b23c9a67a449654aeb948c37f2139436e7cc031d81cf5351736aae2e31d624ebf69b1c752d99da5791109c212e92494346ea0f8e6fc6b40a7bc9c5fceb8ebdc141a24398f3b6cd7f5c89966efbf7d64cdc120ed641ec93bacd1803e5179111394598eede84181566c60398354a26e41474ed0963ccc397fad0e852376b4f348fc36da3b95894b6dbfd50650a73a22e9701d2010109f34b2c5a68f12ee4f34a44f779be42b6dd23ed82a807a28591402f7c350e8ab7d6bf98095fdc5510d067cecfc7e1ac32db8d516015bb646c0a59eaf95201aa70ee8dc4faefe7395ce32276c813b662a8339bea6c60ee974f4a74a510350a5eaa8ca369663787fc72d77ea2aa4310378fc45c848fbffff32b9e9d51a8e800c796c0eb19196594193a58cc5bdcca8323ecb55c7fa2c502da9dc5eba86b6606f2f79717a9f7c5136ff945e990133f29db8b4cc4301acd62e9fe7f1ef858d802cdd1c7ec15d8f20d99a7545ae50a361c726824de9c9c7d3ef77ad3a1781e1608254b80f8aed2ca3a852e2dd5a03807cde3ac628266d9185e106b7cee8ed4420a227c562f28c3592bd920d54f4c8b3ed9871d85f4d418de88511e5b738c4528079f627220acc237521673c5c31f420539b908f8c335b9cc90c7ae6308b53edf0e08f43d20e5262e6d27051867ae34532db0c1989191a9aef1fd285722cf20a80fa9330e314a557cc5464a03eec3704313ab99838756d653d94badc05f832bfa00584ae931a2391767bf0099133c512c687622a6cfb967df58a381220fe81bbb0108693d51e2c9c8d84e6b0d9af3660b7a95adb786cb2c302b0e6d71c17eaf800d5837927a99b5d10a16211d1160593094af7392d1c76ee331e60c786ade187103ba4d175dba0f75c6b9a580474052095bf1bb9898c4e15bc12aec050e6b20a4033f486f28a06dc6f327dface0e4bb975355ffb32ffc33a78278a55fcd2ef49306468be087f8a5c87846f9b0f437c6ec60d219c6f0b662d5085d078ffed8388f0f085c58a520a89b740f2485606fd0037b5d0adf98980b6c2461b1c98468d84005917a78a0140fb7d8468a3aabf18109ecf73b61abc32093745aac34ebdf10cc0f0828d3db7ec16dad2c63f086b5e44f414da73fa9f6e95372d3415e932a10a80a2f9b78baf4d814f2039b6e2467f296a680b5b42e8d584fb661c1d9166a7fee0713f1139669bdfac09d7b9c2f6be10bf7e962e73f4fcbaa040f6c797a9de32002b9eee4506973426447dc7d84db28603db4e4fcc565d0e804c7886d61a18dcc0341cdaae8656b48ce2dd59eec75a0228407a25ba0a1e2c2e63e3266f651fc92060e4a5c3f90873b1fa8ea45bb7f6e114c9f2cd91ade91eaef4fc9415ef285de0936397413655993ac5da29921f307d9307d3cbfa0f828d5ad04d412fa73fcfc062df7b681c2869095097d4bcbdfdbc35fe9156a5b50df9a83707ede6c0c5a600a4046513a29ec4ac9fab3a9a74d4a834e58cdfdf3f7cf5e6b7ad616694c944e0c11e489b75be4e24bf5f560f23a5e0443635ce0f3fa8d920f011e554a64cffe0006df58de25fdd1f57cfcf1a20d24291788ec7d4a80cee93802c55c46684ce2057e8761e71aa1e9a55e414f04e53e3e4ec78a9aac14fd1a06cd9117bab0c0c50dca386a00447e04f80b56496a16b674d56eda82cd02ac2bef90b4d5f991d17d04 true -check_ring_signature 3744f83417272cc1a3437a19e87f18040e47ed03d7206f49ed0c5011084cf453 49d63590dbf51d444f7ccd63b187785238ec89566526d9f3b6a62607d6d11503 14 7d76e58b98c69e3d7e1578b2ff0b7971d8d0493843e1dee6d96aec0ef5b5d34d 65eb296c10d41b187e38fdfdadbb598f7f9733ec5e7fc891dadc83dcbff70968 f704934c51c508aace64ec1698325b527e0be2fa30eef2d4cf24af7002011d80 b6707ab274081142d8cc173dacbc9b886ecaed78d9ad6c8be8bc6395aedaed7e 3824998bfdfaedc6d940ba8aa39800949c27ac42abcb2299f37872cb2b6d1505 e5e6b6194d9f3b59801bf0845c95d542106b45ba38d36e3ab2566831996eec9b 2daf990cf8edd24eeeb94803b8afbefc62bc982c6e011cac671daf8e067155c5 8b5f761bcafa477149d7d116f601560b518cd265e712cc571f4a233d01741b94 8c58ac854350b4745cbd07313e852a3176e388b6c90394b3e6400c3d94af366c e83ff2962747247e87add6029507dd97a886b17807158e0382ee4ede0a6fb26c c43cf34302f43b24eb5889a8efff02565519cc98c1863eb341d1318088a39412 569b1b5080b47caca9b0aa2ab11d89f3c31494a835c4f30748dc2a49d54af667 92bfc7b4bb886cb6b5028868dd973954b61a0bac8481fabce315a08fa15f1d2b 7b4423049181e507680f94829f222108ca679bf81d9a675fa44896dd9ff3276a ae4f40485999a10461bb638d5e8d1986de26fbb79348bec93ae4fbd4d40eca0b91a95cf5fc86195e2967241aef95188fb05d5871151a0367902e302ce9ec660ec6506e47424220a0652e97fe7ab1296a4e08fe4c4a622e3db4920a55bf0c990ff9cdf4c777db2958bedbfd225c8e2f547e724e47007453bd2464da43c314d5080d7afdc769743d76b87449462266bce2d067fa28855b8b4e5123f8a7d06e730a345552249eb06a7a9b5bbd9bc05ec2bccdcca2b532e63959f471c05a76f7a50a276f96372ca9aa8fe6d1676f4fac70ff4b30af8cd69f2bc18444d9244e7ec20f846b32566d096853561c0ea53d1034a52c395c98f12f8d56e6fc7cff93fc3e07b5a21ab0bb99b8b12b9db0a27e1dda8c00f290d795371c3ac7f96b037869520fbf2e7802b7cde84801eea9fea3cf22edb8a0279b47cb0ba07b3dde678258ba00052ff400b873f3c8cb0c7b12b0fdf81011baee5819642aa1b5283afd2363fc0edbd16fb70636a70bf4ded08c76e23abf2118be58400234cd39cbc4a352c6f304ed646bcc4d762419846acd9c24bb7761d94b32d638fbd093cb2d92133a90210f41344d2433d5ce08e11526c427c8c7b3bf2b2a1a06acad7ec3431a62af165409d0997ee6ad04a31d5c18aefb9227a6521a7cdd016684cc0a9c097ba6057ed50aa260a9e41f22c986c3bab48d4a0112be44860f08d50b21d12cf9e008afe2770cbcbafac43337c12b9332cf0bf127abd143af0307560d042a072a690ffc55df08f541e76ab09f1b53f6fcca4ec92f6352d2fbe85359f56816a953b196acc0b509574d2bb0221588742214dd4f96143b2db8af1314bdbfc5073b4b4a8b260aed00cb943bf0dbddbeb025be94f241a182a12b7a4b3c472ccdfac06c47ee114f860ecb5183a7a78bd14ffe678b9393ad13f48d94c7e46a36bbb9c81cd47530f6b3064b48a0d709469d787b845dd49cf78363922299682a846d2988bc331a1a9af306f6d0fbadb25cd3f6cf0829aee05092e0f66a4fcc208801b900196302941f8daa75f70133d55cdc05b3933d5c6c7a3a720cc3490eb5294f26171cb3e151160d01bd23be02e2494fec02675857210828e73e7e4b208037d5982404f3ba11226302c01a0d6c01ebeb70fc51b6f028b8411d0370ef8f13a07085d57f79abc8eb0a0ed3135481bb51c6838baccf46fed3c0023aa9deff11c71e5af84838a6835d2b0a35aa34ea3b1306d298265ee7ea1d11d65f0ac241f86554a9add2a29927d58a0f false -check_ring_signature 7cde12ac0c27073757451d17e30b84b0afa126fb07834ddbe7513244e6d7ebfa f83180d44a384f65daf2326b5708eeec091b179d9867b662bb1bf0e2612b455b 4 5d6389f0558fa1478a0f10c1dd259feb09194f8601716f22dd1d355799d62a4f f97af9e41f740e4e2a58c65b354b85feef44fc42a5af06f248742e00a28e5016 31c200da6d38cb9c84632005f3b0e14c34191b52581fa63cfa5894e42f3bed54 4bb5707955740b8dfd8da521686881a7838c5d7597afa974828a897217e63c15 3ea28558b4197cd94b41203efcabdf57635df7ff2978aa11deb47d9d80bdf8060db1b72be8b3c3935ce3d51be2a06e62f841d3cc6070595583a930a0d2f489083db1b7f6ffb111695644614260cbf2920fd1ae6e0efefc152d041b29f6ef95015515505d57a0a2cc7b3c467ee5c262f5788bbb9e1ebff3d4ef13bdd84762340b2941ea53bce73f1fcce920a2a1cb0e30aae1da869901971b464c102857925b0763a6c411fae7b08f78434922d74bf2269ae3c2727989498098a72362a3abfda0fa810340f492996401c9b3adfef06e720beece774529698726872f0fbbc7170faa508584d2e13c562fb8d8ad184ef9f4dc037a9cdca09971afaa678db9f13caa false -check_ring_signature 9103b9672fed8cb41812c986c69027aa4a30535ab28ed18f0d271ac5e032d6a6 cfacd7681ce783496aaed2b43605e074f34308144a3db30caaa402f49151b5d8 1 f93dee7dc09537b5e2241d7d444c10e4c2584dacccf4f7916fb9372ca95e458d 583e7472390c812bf28f64d56f398cb3d10c06bcb2586b1f9c8a663a24e90a04f11c37d552e4e69d8a87f2a256f9a07655f242333b70a4aa94e927349d6ed501 true -check_ring_signature 35f7b7e75c1e7de6cb0aaa1d37bb75c64cf81c5eaacd381ae990a5d46903dd91 edc57b8022029f3d68789192b3c4dbf88fdef2041d4a7ac772db2b84372b873d 22 bd94b63e73835a5432b6d2acba4c90e3db988e539b821ba2cf60b3667e3ac7cc ef417b3b4de887649ae2b4f07069cd3f59d3ccf6d0357685e862bc5f0e5e9fc4 178de2234eaacc6ddea98060a73265ac5697bbc1a06202fec0a519786c769a08 69182f8cca69cb52527ab82ef615e16d80f661b486ac672e4feb32319a6d79c0 f0bbc723ef43ae8965100b48ba5ca6d3aa9e1f34fb2d3953865989bb6d1c3255 56bed5102508c79282094a519e811ae1b888a9bbc17aa9e3e4e640d9c5fd72b0 393d29b230c168ecb61279cfb2326891f78a65c4ac7f2d8d9bbe04f3de99538f f6a8edad36bb734594ab2351076d69f3a9da4ee4d4ceb8ea05ca2fcffd994749 431855643898a82d3fc71725c8c522bbbe4e08409572a8098faffcc30de1a0e9 2ffabc4a9f969608f27e4fab2f934eff249e30816296784ffaefe130b6af26db ec12dae609349fee20bef45d5495fe1ddc65e23933046b1a3caf6f07acb347a6 4ee516b6e10afb840ff2195d6554f8e627d24888a8771769d02008fb27308bf5 ca2c9053b9d80546cbc386e18037e8fb2acd99e9b99f8f9aa3ec0f975d7759a3 8142bcd99b8a6e5b53f5d0737ce77eb052c9a66ac1551a0a9e306c074fbed9eb 473f20d490949bb50c7f6a13c9b8ca62cc8a498ad42bcce80e8ceeb8d2cdcadf 8a6c8e7944bd5d371af4548a2910f7a7c907663a67d203073c02308ff8ad2fb2 3be66515f9885e1169249dbceba868da247d083965ab23bda74106b7cb5d1883 3553bf18432cf50b24b1e43fbdb5feff17f088203167c58e0e50f9f05f28a74f 955730a1f691cb5e0a4697c6739c5de74602f9e4a509352c2217e9d64dd7cb46 27383bb63b49c87d3d61d4e38525a562f3bb3862a7f5e7dd6aa335366f22441f c0d79f730f7430fe2bebe9891a041d9d95cdb8b4ac3ae265c87586f1a8d3e053 b401e6f1802f164f0ad72120155c789b5b8f26f7ac5c683c98e76f2e60cf6230 e12e62f067afd2b0ddac767a109bb46fc2bdd2c2e4620941a19e06c4e3237d0ed2e6a479e4246cf0698403784b5f24417a0f297e154ce2978532557185e05602982262fab309130836f24de4d68488c371545b31f0235711458b2ea9769d08042f012e3b19746c136212a3ac970b2dd005a9e7a1ed5a07df23d450b68298240233224a88c4f599c8ab9dc385f1fc94764baa2299dd41dc67d0adfc5fd401e804b80d0851378fc5ff6445e3e5726ce5b616ecf238d2152cf36d29f88e2bfaad0af87984eea0d932479049ccb5a544f65c5e6b3efb4294631666e89b0228f58202f35e62091e9e3e9dcb61d78d341d56cd3aef7ac643a213bda5b23e38056dbb08aa86b54ef0d65549f62adda9fb385073a96c04545fd3baabc4b40b49b34671001e7faf842f9fdd302f24a2d2a1a9c9f6dbce777ad722833af1b44bc05b559d0cefda6c05d6bdf9fbf97f2494b7091ed01d198b3150c624ff0ccad99fe279f00b62aaa9c7a25d1d1906d71b8a7f402038d987d6012144208ff407e0c30ce8990d2d6b3f393802445ddcdb270a40c8a43e9d6456c2e488fb58942427044c4824ad9005a8efd5d9faa333d6f8bc8696d67d695d9b23ff627009f7bf45d4ba70c0013b5bdd17dad21b80a4ca1b11eed1178b62bd03addbc49eef24410d50c6dfb70ce4f944d56e5c5c8d685148a4be067f6d19414185e0bede4e043e9a1be085ae04f28b2f93bb4740c9e16b9ea1e848788220cffae3e7fa5ede3940164d14109a0ed5d849bbc7dfff1cd2c145e627aab455dc7e76eb075c49f5595ee7438cce7809bdcb10230820842930e9e15515445c855b77cc579944b6f278d0757b06192600141da200668536faa9e5c40ea06112aefc731b3e9542e0f0a4ff1dac3324120256dd7465a00bbbf3b5983bb6e5e4cca6d3c71e86a7a35be8cc2fb44238f0850e0bfbf11ded6c34e100e92c801626f4a0c1662156699d9b187345038405afec0e9e70e6119cb74a850c475c319282244a2f9988c001d739da6bbefbca5307b000f44f3d5a136afa2abf69b9d21296613090dcd45daa654511181a1592c6a4f40a719845f9870aff23b86943af58d1f436b80a7c43dfa2722852b07723e282bf082349a33d1bae06dbff5acc591d1e1e9fb0cf36b016a4879a24a80d89ce93b101bf019595a84f0485624fd31d7daa6907ac1463d31f97576d743cc2b1bae3bc095713fdd9d8c198a71f534a05e08c18ad2b1b1165a9593e5e5718633410af000736ba93794f542c5649d460fc81808376286eaab6fcb47f6314464a20a0e6ca0997a2cc3cbac80195b46c73e0ad6c10791b00772ebf1d9d90bc3705b9160d5b0619d05793f55f1f6f457e478279e1aa450b745f0322b8c8dd195389297e320f05936038a3c6b4b75a46bd95a84af60ce0e5c93025816138ead2e68b9ee3c6890f2737f962ae3e2e99b5317ae3bee1187637e6a0ad55fc6edaf061c0df1b49b60824906189b701e2dd9337cc62d0bb93823d2799670caee73479a35cba4036280b91879718a97628707b97d1a8591535a039e70a2be1fcf952edbb60f16bd2350ad61f01af91caa8ffe4dac4991303ffef24b8869c195f78c45d493e4ca4b22106d6a2205b9b88f4a63ea1d0f39f600967208fa8ef75490d718d0d45fb269873049c7056fe049ae4f1fa3b1eeedac3390584b1d84a2286b876cf0a1b3eb88be40d4deb7a30d223d49c48555f7804d2ab192af7101cde5aa38dae7de9d4b5ed760391f0686926e630442b5b9732e54de8f942b751525c217702c2f90d6544565d038b40d5f367473b7f04fd5d183171a693c8bb330e8144d25e69ce0edad55fb50ed2a26fc8b599c8fae7bc0f5228dbcaae70f0d8b4c3763a301c930d61cddb350808fa6e979ace5655914907aacc063330f0ff4006cd74930ee58b426f931e3904e9217966c21223be11dc7b4a98841e2aa809c6d9ebe85319f9543ef235bb8a09 false -check_ring_signature f7abe637668845f6ad1ccdad5fd7db39a4b7d8bf96166029e04bb386adfc0331 2577dbfb80b090cc299a32c02c49541aa082e22d555c60b66e7c36c21d101751 1 ac8e0fbe20fb55b45547f7af5f581c41617c8049ac11a89775c6bb716905ead1 e72c14c3e53c49f0787e44c0aa173fe124eba68ce69b31705cbdbd2021086d04a4171a09ca61bbc376c4c4c646bfe2f7c02956b22142034cf5070b5d69cd140b false -check_ring_signature 08949dfbd51f606cde77d883eb6f9d416ca19110b2701ad94f1c9e07468b41d4 f57135751dc5ddaba671a2df388f1236fdd04ead3c6b80cc393b8ca727a7413a 4 510ae31f0da5fd8913960edbe8012e191f08596964cf271ce9cfbd76e0df7f2c ea1e3b9f6cc0ae02b2b760bda6eab2d1515f7dec9e4dc84e33bc8ba5863ad5b3 efc5d6a84e1f39643813fe9f4a1fcb429f6793f70bb654aef7d6ffe609b78575 4a300e9573f92058d473a52f15317d949d63328c20426e32eb8b27fd66eddb52 76c2f70b0ca17d0d1be02d2fb4e0866735634fb024f173ecf0f40e2173126c0d00b311540600fac9427cb4ccc6365c07a355d0b35e4cc9533d5c2517f46fe30ff8c9e1b9f9a91a4d022c5e1c0031be470a9ae79704408793a75a4096e0fa49058013b00a7fe095e5082551aabfcc13798a5094f958719dc6bd57aa3116011e094a87413dfee8c8837837125c67b4eab659e4c19cf63f562cda93d8c095e37908e094a8bedb81652c0bdd50efa299662e477dfb1961a77c79ca96c608003d220f19532727706b4ba6c1424cec106fe663981625bfb2b7340bf050f65f655505081c184cbc625b2020ad6501165c05a272e852243d2b28e69d66d8ea446ea7e00e false -check_ring_signature d3a90bfbe2f0e5993dd93c1c10f01b60f8c8c41d6d4b9c4382e6228181e7c151 9aaeff5ac2968732b8305ba961250946eff69529e66237df6a67a0a63982a233 1 53f6b9470331427a9f0c9ba440e70dd24c6dce9399f0989062bc614255f65a94 efe23b2a6333797d239403692216267c6d3a868fc0b366857d379c75ad4a9e3024280a4f66e00635e554206c051559706d468300f985f182fe0b3f0400ed9c08 false -check_ring_signature d893b3e6f1348b23ef6c8c05c8c45f07f72c777008100e9dc8a1d980d37e191f 51eaf7eef9b68f8fc71836bc060369e4bef87e4a0a11179fc13b6df9ce4f9049 86 76c1253769256f955b0f6262f05a1070f778645432d9ec77f3868c1932329685 89c8c71d2cdd80643444d40a5e93232ea82365ed60aa768d6a27347e385ceb39 7caf32682bb45c8f46e9da6bda7e40c294887a76f88280a1d0fc47d9c3336af4 da95f42fd04aaed5d9e18019b5e24c7865c4b5f00c09ff6c047424ef3f068c97 fe6d6c13c92554f96319efdad58abd227825009b35ff66a8e0327768756aa4fd f3e3f43676ab028699a2694212befeea796faa3a04cab451ac43298dbd27158e c147ceb008df08030306c65fb0f52a49c2b3ad90ed753af73ab14756a63d0dfe 5fe60ec5dfe74b9dc801ffa29652c6cd38922cb5ab4b33ab6b0970c5b7746eda cd2baa1180b732a9841019b439a036260fdb76679116d43497601eafe27bc68b 7a0cb67549944c3b4501d404cfbc9921c4ef19d9c539e4d7881cd564d18c2844 4b0a1154ed9625aa061e6e3088a545cef4228c0fd66d8ccbd405731c9cb4b920 ba5ab5b4984c23e5267511dcec3f6d4045c138be2593294f990e5de83dab51b8 043fd61cf8500c82a60b94e700fa4d50d40d1ea7759f8d285dc96d48cae367dd 4dbe1d778b19333d09d4c4e2d4db4317f9a95c0c56a89b3a7f70c0fa3a962785 083b07f3e5bd7afd3df7d879b3ab14d4be1b6f9e6cd46c7c9fccb01ab2695e40 1c517417a24d7d04e8f4cedee854f72422120fc6f74863d381cae9de4e839505 739da8a17c07434d581ec8e0ee41e5c96475656e40389a692c134bbde04ecb8a 00f77685546ac919717f65fbce680d5cb809f8b2668d47f02074e6a2d7ed277f f39687de32e02da5743f3c6110aacb74d2c55a454080d53106a2b1e238c1c9ee 0b069d166a77f7d4b218a48165b8e1ee55c9be40ce917c0cb051c8a85c1b2b61 b5a51e06df2802ee2d96e1bf335c81c3cff79810dfdc84cfde0b1edc724250df 5f983cdb42354ea06628c21699411b69f9ec0d6e8a34064cbd80161e474989a9 e84e12c396677eb6a44f8301b48155cb2942ad64fea8f278bd1252ff7b300cb4 e899a4ed872ca35b0c75c2f724f0402323e180e604c890f00ef49560e35128c5 67fbd075a3901867d6425d243cc0aea1c66cb54446d3d3bb307a7d4f3435decb b8f23de47e0e8b8e5c5cbe735aaa64273be2ae60e0243be459534fa42c0c3860 7dd2859dc83edd781ca362e448db7df683c868b47d492d14a5b1a181265c2bd3 175094dc958395514813ee56dbb5a4408ae232756becdca6bf11cd8fa8755752 cebab02e691de22299e26c4dc89058b7e24668678b91817cf49cb61545caff3f 1262c63dcaa802f13c73c76e0713a54d8149e821280492a2d92800ef79cebb84 5711aacd3392e5cd1ede5e67add30624bcc815dce4484ec48ee487187bdf62d8 77958a6601ede5da6d217cc4d11afde56352ba7d17045ac968c5b78b6556eb5e 4595ab8309e6c9334f70fff223b82bfeedd8a14ecececa7164db32876279a217 16e788a1be11f91938dd1737b9cc2cf4ac4bee3d69f836948265085821994884 3c3f65e17a96b57303bb1076e7193515056a99c5a00cc246f1547b541175cbc0 72819d59372f2028c18179c35f887ffd43e0f198e36c7f5047c73e9e8811cd3f 38634348d65b39ebef1b36f9655613003b5b9c8d4ae9c669d1add385e811cda9 e88a3eb13a555e0473bbd0e3a871604834d8857cd1d1d7136e852f6df503fc9d 052a7ee648946fbc7a03362bdd432f2a7c0bfc218f16eba909ceca69e1483e90 384e98015c4f6d95eb0235a3fddf4bad48a8ef50a3ba2cc050718fecf489a1e3 f2e2f365a380a17ece854d3c01cba1f473fa386be9d94245d61205e842b14449 bc7e376ff5ab6e40e7abe9415a5382ed121cfaec5695fb8dc4e595a064585199 786e4f9de78e110065b99467b9072c43da12035d04420298834c35b0111f4111 edbc6ed5b5e0df0bcdac6fbfdcb6f089d70c653540fd4e55e35d6c6efd53a32e 56f3d9e5a8caa180194abe2617ad59f79092b279c66e47f6d29f4b9c97a57348 4aab20c433815785ab986dedb3caaab2ce6fb27dd4d171b9d29885c0270adb72 0d0de485de1e8ac335138fec34b15c2492371f00244115b21e85b0ce227581d7 602e37f97014fc8a6177b3aec32ff1905c20ca6fec865668af03fccf40b663e4 a62c7d23e96c2e349e528296a442ab8bf7860a5ceba8601f4403bc817bba68f8 312467e42cf0a503008c9e0d2dc6c84aa78ffca4090423447891aceeb4f4a747 33144c4cfd377331644a639c9d543815cc0436591fac2c1a882fdb62bbc958cc cd9bfb61c9c65209c5e2f2692d1e657bf2ffa75e8438f0ff7220a3a208b02e2b af2d48966c7ec9b7865bd356c1f2e5ce677241234bb26e2e5c7b15a772558ff9 219ba3dd5d7ffcdd351d9aaf27cf9e692606023937b80dada0b3dd33560f7893 dcd8368997b5062f1dbd7e2258c2e53b9eaf2bae9b5f5caebb5dcaf16892569b 0a8024ea3f7ce4a0433fb137ed0f07996b2c0933a6cea76bb9cea97538dd3248 5ae179e4df9ae5ce7e618c10cc584b6e97f6158d62f2f09c20395dcaa8f56ee6 57f06cb47abec6ce36b8f940fe92dcca6d486fe8c8fee42b03ad48794484bf0c cb787a6a50093292a193fdda67cb7cc6d5d2efe320e555b3c34f59a2576448c2 f1bc6748f1107c422746bf7bd2ed7963ecd1065a28fc23848b92a212c1fb1f4f 3185654066267c1fd76bd11708d84a556db126d5300787064c6885b26693176e aa4dd1aaca208b2b22374121de8bfd89c5e82d878b8227817976a6c2a8ea7736 f2cdb8d1a5c124ff9b1511941c2416cc35074d0f51dab39b048f54c1d47424a6 ebf7b2fbbc7db369e49b7b9181ab0ebdea3198885ccc8f9012805579cd45ce9e b46020c450eb4c9a8a15ef07260a251c5f895afe8b56851db68aeb4df19b1b36 9a4654b77c65d05fc78369f3a7b87631cb8254355aeabb29e32b861df55a4679 2ed64d813e2e78a23f2808289a24901baf9bbb54e1ad72ee02784659853f18e1 3421e1784fd6836f30ecea38a8f01bd0ef2bdef9f4ccb8ec466145a3858ae6e2 aa05a77c2fc49b3d0ed352c19e7df0f1837e258a3c2987690f2eb045af9ad4cc ff70b21906671d316387d636fc894a39e29039c11d34899fda175c5ac5df0ce4 5fe1daf8ea85f2048a37000c1b41528f394cc123253c677a0244e08b9a3c9d4e 06088f5f4199c74bcbe8e07b11f1ea7fdb98edf6b2693a724af5d63eaa840b1c 1e5bc5105bfca0b3edc46926d76d99848a08c8ed4993ad8187e63a8e38abbbf4 45953abb2e824885bdeec0a6b7b620a2f03b4957f72eb636bbd95d9f3211a424 46bdf5d54df60dc4859159f116e1e7a76b326935fab4d85feaee20689e43c6af 819943ce9e3d1b72ddfdd0b8ef6a40131c10f96df0adbd0e803f6f185d0347f9 c8f5f6ac46a0796eb2684122975c3db25b59b9bd4752b7fc2b81958fc80693cb d3e550408362f584a9d5d9a90af207e171ae972abbb0fa43914d5e515ec70e40 030bdfce4771bde801c4765ab899e3ab10e4e0fc0b4c7de4319224744fafa586 e79084661e9d21b6326ba61625ffcb5b6e16e1c9c98efa292b09e79cad6d264e 778709e5e2d538bddaa965fe273d7a85d5bc3fdefdc5ba0ddec57f39dcfcbc8d 47879029cd80fc2e601310b83fa070b941e1bd47fefd8700c9f37e6893c88a3d 60b6b6fd09d03c86419572f5b57c8539db1ac6c3aa098b82b7d2d6c43d29c330 27aa05d2318890a4cde5921127343fd0b9237c09eb55393a3c05c651d84e5083 fbe1172476de7c62150c921616cdccb0e36153cbb995d64d0a4fbca26f92b8d4 c65ad68c388fc26f8a3d57f1a091d556478f35ba6de707918ea6ada7978fd5b2 0b6ad19d9175ceed5f6e1ff73aa96931dcf308f44c6dbf17beffd79a20f410029a10701b340fc9e33b894e23fa70a72c8eac4e361719827ec9f7c90c0aba310b88562ad48fcbf38ac9c65c5a2b221bfb0dd503383459aa04f4f7c06149be05096e177a700509e0d8742b3d45fd4828515651f0c4f79577fbbae2ef00c73a1a00698d7cc7252aeb10cf0dcbbac56b056a92909157dc2f990960dc82d007a8070aa9ed6045d9f76579be4d0e7b809ce35b8dc507a5597b6ba73e85c99a7c4822036c7b5a16aec5a4b5e4e5e79eb0278ec9ceb6027f44d0e245a5b34dcd633b4b0584e3908a472ecfb5cb5ab566374c74fb5bfee43b799428024f93ab195d2a4008d27cca8ac6888b1cc65c5e8e28fda9eb4c8b27c92631a16b8a261b668a35f609b105fc0cc4ebd2fe0fb9127c10f225393e005205c2a0985f7220e7fe86b05a06e957dd88c35aa1ebd0cf0703230c1ceaa85170cfb92d0420ee8a4de4679a8d07e2110083148b89de456cb3a878232873860372ef59badf061620d674f32b8c089a970841b1fd53bfd178d7e6dd6258d472c6ec68b4c7d98763d42e2caa3bd80bfe9b7104a28ea1534671b84712db9f02ea0cfd3ae05d7f787a571be96bc5c20bac3f14efd0f95538d08a94a30cd9607545601e8cfdb4ce35b07a6018487aca079ffd86d10de248515ba245a6fe2dcad76971297460d4ed3c0fc4b3312ae50b0a43549f83492f4eb1d95bbac8359f952ace6fb849e35d4ac00613a4b71b5e7d06839bd9c2fe747af7b0f7d4e4d1e616c94a6000ff687d60b7f85dac492d5e1a0cdbe8f900793eb78b74dbe003dd7ea7226b1a14e36cc0c43fe85787836b24490b143fb8d10db044204207485990bdf4ee1f4cdc357df16f610f197637e9ccbe03baeefb340038beaf6b9d00a089afdb90fd1812e70b112785d9cde10d2423a80de1774e33f3634efe7aa6ad66105d86c5de899a9f05c3be7b5694a53e211fa40cf4e8403e20f36702e102cecb7837cc90d654573dbf11663bb625a3315bcc3f0d3e8a326df88b2d79a9b366755ffb70f7e53b8301e3d3a0ad9a2cc3c015aba50839970601dbb7d758551954e485eabef806f8ccd2c7b5580badf9b58b65ef730d148a287010515708d6486698ba86ba81fc3071f4e718aa1d5b7d9917cdfe8a0f82c5ef85c8345c416fd5416c6fc521e2166954f53caaa8c4e4f74f6c210bcf0b56164ef22bd3486835ab3ba6252adf87aad6437d01cc6ea89cc7350aad84270852097d7fb7aeac2f0331242040d5051339e82e07ffc149aa72d50209be6f810da3e167c95f492ffbd7e312850fe28f811cf64a5843b81e4933866632012aaf05e4ffab854be45dcd9f0019341c4d5b36d892486366df763a30b0fdd77e385001266ae7b47ff2cf8a183bc8b3b04b27aa4bc52fd3b00978a87d6a28b6cf3014010e290da87800dab5ff0eebeae371f910827e032c3488cd8eb5fdbee6133739047dd53d7fc35c32690f0bbd5c142d1e624636bd059208e4d27fae223b5906b2010f0a4c6680acb46ea895e3ecfec8f09e0f80c00181574bc8aef6d29a70a1d7099f32edf2ecb0d6a28aaa25c436d85699687f392fc35f8b76493ffca5913dde0153fa81f93a9ae205026ea9ed06dedfdea84eabc69810b28540ad9039bb5d4100e825a02bc39695572a90d7a82f63ca5714078cb9524a4319e608d0fcb7724e046e4e77af62135288396210a5b285f108b779127581b770031b2e9bed2dffd20fe4b12fe6157f79da699e1986c024fb5bdb84fad0e7eca59a34ed4e4c31547f0e1cfea6d80fa987793f005673c8748cd256269703f738f0a2aa76734e218ba4008fbfe49e6683c05ac46483d5051d6fa7a30e8e3f21eda53ac9758e699565460150ba2d366761558b18c66dd798c3a7e113d8d2b42897c2c944ef1b46f2116e07656d042a662a265a743945485865829770e4b5dfe368b22a5ce5a07b10b9760794c71495fa9e1c8b9c1692514dc065924a0096c7b6eb0ca613d712ad5c60720c574f23dc76246c1dba3c74a64d20dc11b747e94f14f143b5ed233795457507096fc340fb36b7d378448a794701e629290cde33f14126922bd2e2dd7eba83500349056739b7e730f80d24e028b318646109f9b9576d56c2dc11eb58dd5a09bf07a4625d6e02d3cb01929fd9f1356f245aab22ec06af54a6f1c87137f2c79efb0cd302cb924f09965147f3fd7ace4aaa92ab87a65d3ca5945d77b4c26650dc540a2197439b8887456d8e72f78c77e3f2de81b8e0035724b8694f4dae8a1ae0ea035597ca081ad226884d9218fa9be67f10018894755d45d1664630908882acf004d0f43cca8d06a165fb6e04d89727cbd88083abf994026051122b6bf06ac7cb01bebc37e966de08e6948bc73ab14ad5351dc258cd7f2676ffa31f0ed261e7990db8e7719bbe03575d3fccb92c00eefd0a4089c235e067bb0260ee00efa9445c0a1804f1cf8df918d3e9cd768c248eae23bca56b64141dc2b122d22635bfcd8f04cfda72e0db61524bb3ddfd99b4374c91e1c31c9132d4e826707247d46b2079070dacccf9bd7e4622e8ef86b0b361622cc32c754a2f5d5fa4f24990fb01d4cc0b683a32b823fa4082fa741bda2d2139c862e2de056e2e4b1d174e2126442ebc0143466b7a605ee76b35487446139088aa44f0032ade751537ee852c8ebdff6e0c411403f3a2c0230036b483243ed5f44132e049e8aaa820eb5dfa2dd458d0420551c551c28b4418dacd1632936acd56e38ab72296e7a9f5bdf15f1c8e7e117b093c38ee07dd90c5aa0e69431e2c842b86b9f67e3f5d7c144f4dd599a070326007af04e455b4969b4123ac3ebf4dc0de64dd49488893606ece717e78d9ea0aa802224737a2b964794f734c9ff1263fefefcbca9994a5e7890704bb3f108ef0de0dab494e8a5a47f73a26bc516a2be0af79a04843f3950250c14c60e65a87786f0ee5cd022a9ba26cc56eb74c645cee2f985f21e079cb1ac7f01b10e137c8932404ded225145bb62ba04f472249dd15a2017aeae7ac649a89951acbad7f37ef340755ce0613971cc38e1b78a65195b8dccb8e4a7c2c754033f2d5afc8db53ec6b0ed1683b1b38627a6ed28194e7d67b4775b7acd7228959ec2b1c20225570e1a3093112356a3f72613081b58008dffc838bc896b2beb4f78d63ee514856e1b96c0ea9984d6b23ce86eda786b1399d41a94ad2db29a05f32df9d8c95f8baf91d3f0a1b323300123049af7733874eb805ec41a9cad5d8d2ebf3346b56eba7074e780cd57a2ec9e50d773319d06773bf397bc4f684f7ece65d4b5f28c09d2c9b58cc0c800c4d29c7a5d20df6527d57a180ef4a86f31b384f5d300d28a1788052a44405257bb7dcd8b3c7d783a17ed0f94e9940913f5eec0b5cbf3c1dcbc7eaeb007d0519818ff12e6ad0aa67feb201a14a44ef96cc6493fe7942ae6c00b134d3cb9f04cab29474761ae9968f0f3b64c53ea7d8f03762c3519be2f0ad0008e4141735019927a5be7ab1d12449b83653637c50bfe548b17c751fa1463b9409afc7802705d152d2f3588c6bc24139da188775cdc7e7db2d1ec86872ff686a86ae7fc31208b6eae6c459ea8263d215771ef25a9029c9b8b0681c730d6c7ccea90b5beedb02f8af704df263b373048914812d214102eb02dba7b948d5245509eaa2844a250c9ea79a1a5d2538da8f0256e18d61cdd70dda6d1025cd5aa7f4613a1395e3da047c0d7c26a5089227e78c5cf8ebb3771c2ff925c06a78372de9fad41e08447407c1c586831aa48d8d214d91dbe3c4b2d04c953567ba8a216e55aca9495f285609f140fb8bd7ec586bba283f091da18c8129f41b53450033dd5d8764c8a57dff059bf972b54056c91d475616657cb1ff245208dc0fc4e8b2e315385d300444e303165c733b01f0868106da3398e4d32c69ca7a5722bd84cb2afb3ce8840bf531023bceb713bda6dd0fb2372eaf49b76e07a59e141e8dfbb712a298d7f01ed9dd03c7008fb20672499b1cbcb4c9f1ebfec03218a03b4442f9d3b56154535112360879fcd1c139e745e6aa157444da7c487801ca9f596eb4f5e1889ef29bbf679e0ffd16318e4b96ee09f4ddcc8e429870a385ce3fd88b5e1ada5715326d4a14e5041a1475be8e7e039b3db3915e2eab2abd9904beb405b8b005e975dc1527c6ec0ae2f1d62adbbfbfa3e66144abcc1c6dd09d690f5af687b56f66fa461ab7de2105bc33bb3523501b87d90a66b1f24945ffa68d7935ca04827485c5e75d41033903bfb7a906834f497ba4e4a510425a3ead2ca4e8a635b64c00c48103a4be86e4001ee55de6b47c822fc9df2960f1b2dc735238221ab23cc53aea97cce0a1237802e27832e3a3cdc9a4b3dbb08e5b3fb289ec9d202f3ac21a456d68e83646935b0d83c04b39038696333c17711ed9b3da4b189fe78530c03127ca9eaa42c1d2e00cd9cb3244419b60bf6271707322574e4579da1d3667ac137dfd3295cc91ab33032e0ed9b0e63d52c30e9f136907274aa788c99e2a2193a5da202838a7b7ecaf09b6eaea25e9b31edce641b31242ed280ce27f6ec38642bae8d733eca892aad9003811a0efd2567b2c831ac7750d626cd146de08d8b5342a5b2ffa928da2e46901b10b702be12b9b72c7bae273bf65737c1a382c78bff46c765e117c9dd8d05b0d7e87abe04170c9d4460fcea689f5efc9645e9ceaabdf28698dcd02073f489207972c7f423d323a3bfcbd4dc43e38b9d2b94b46fec06d6f62a0a57f1404149c0678d2fbbd3a6e3ddda693b566997e54facba8704e3d311e721483474e4e04350046767e4334530d2cc4682378612d201d3c106523be1827645a379742a8234308f2a8f06f84dd7747c64ed6b24600f54a6bdf3f25d6676f8bd5f5d32ebf45750f436846a25ede434979965d757826f6d818f03fb94eb95004bfe28924cb736202ca98449a3e63ad3afcac618671d33cbe39349fd4a1307d7c62ec75b299bd000577bf0854a8c9a252690922775b1b24b13eb75729cc42da47981bf599a60d0000725aff4431d4daaff0bcc6557665ce4bb0c7b5d9a3959b5c1f901d209714040cc94d9d7ae85ff16414944582fdeafbb4c286aa998bde87842bc3b50892ca690839966384383dec1b42f40e03cfa8687929e658fd3759dd12c4b9c0692a51500735c84588ed50473f2f7e42cfe3c230aa3cc0443c344a987c39d9e47a7cda150b106d1f65ab44045f1f9c11b27fe6a893697b799a583f871fde31354108607001f531f740a5cd9e9bd258907dce02d868c618fb43f232c02ace47111fc4bc2b0e2ba6a46c7fd7689198298871902a55d1aeab26e1c22db3b236632c9b3e50ca0cdc6c18ab4b9ff5af460d67924d7e7007b100e0a7bb54bbf8a6e2606076794001f6298b8dc64a7d6e233a81bc03fd2ef319cc4bdb1386519431e97a1ff94a09078d5e86382bfaad7362f33e9a41a2a4ee0cded221690d6a1b41cc7a3d8b1c2303225694c36cc30a1f621ba375e6bc4bf582e1f27d9b27bd94fef3d9ecc5268f0f1506f0fef5592444fdffbd89bb69141c271676ec78cf6021fdffa98feccf170435c669375927f7caf643a69a06689ad4f77331e23fc85a7fba3b6b7304c6590dbd3f65da5ff236d3a480c19b927366c88c2e8c3d736d1b55a007b2512f52aa0737d21411b7e7803f1eea8edd3d7200f1e4968514d49986433a4544ab447b990261a87c9bc39b20eb6a1cfb09f931ea9f89a91c5d347f7407e5517c655f85f107afdaa81014b2a86bed87098825c5d8d2553bfa4af2331cf6819191f28582bd0a9ca07d579e2db84a85741722170e7fa89a715a873ea20a7bab188206b642ca06dcc99e587c8b08c126777784d043c0accb500ecb61ad947d20c4f4613d3977098f4b54027a9490fa332f306c07674b5dd4916d716eb11c9350220f31fd05d70f0e5624c15e639eaaf36532baa3b338ac94086e6be36ac41a003762104de6c00cbfbd882d54203f682a2711494119166b4a2f69761e86548830f4f2fd3a6a7700508db6c761b458098b3db4c593be473b6e55a7e0db84957ce8447e9c34c65a0e2fb706ccc5e153c325eacce71b138a23bfe5d9658d69b1ce6b4c22f4106c5a00550700744097f21b081e26e45d1c864d3fb1896e906b237104241faa82bb1a06610a8c3196b53a4dab37c56feb67c41de7a07c442830752d1cb8959d465bc50625779c997901cbaf058f8d3e8678e1239f8ee3b88a3ff99516cf244912650908b2214d4361ca0ce86fe0d9774c45e80127ea37629a2e6fe7243d7d81eb51e109d208112304ea51f87feb559b3ce1379afb1fbd1cb1994fb4a48f2f1075a7510b8e759ea9d2570bdcbda3ae7e2b1d6305e8ba6d89c7ea417112e031aac21caa0952b63e679a444e7b3610e0304b3db0f82ed615de0e098660034ebc09144a780c84ababf127b7646c86560ecbeb9bf47e2adae99f424b30bc15dc943734ff010e5f417202426b0f66428a68cb7d54d87c50a3b85fca10a7cc5c91393e48ad6f0df34d551a13756f1f66d330402f15cf2a8c09dcdbf6b6a6b86835787b1375e60541d411ea6b6efeabbe6a6a05be002089a6ea8c9f8bbbd771ad0c784bbdeb77091331d44c3f1b740604830b6ca418b63b983dcf271e04a5d50fc3f98a3c0e0e029a1491e54f5ba67d6d38773c277a09bf3cc875292f1c43215973c370996d5f018267abf87a794bc5f7c17632886be6bdf010bac03aa1d1bc90ff5212355a220420630fc517d28d3420257e4fc1d4f1a81fdd5260bf838f4f7d2a01759bb0e00848bdbd6b32ddddb3c6b78fe49f3115ca334e37a0da644024ee24bee24aabb00ad89a4bf45c2022e759fccbd032ef689091c7bd9866bf9f7422caeb7845e9c20497c49a633faf13a4717752e8b70bb15be8ca39b24237225dbb1868516950ec0c03db64a98a28db46ea0b078c98b0d80cd6ae10ba551f41c6d138057797cd4c09b6551531af0b8d6e18929f938ab51193d8609dd878ee595a76fe6ee8a5422b0cf52edf6da0a43ec89c542a14b8755576e88b88dbee736aaf76ec3c6b67730e0145ff01f4a4bce1f05b61b4b977d6ea3d0349ebf0f281a0df84ab2984f50d1a040969235118184ab164100cc3f6fbb20699d9187fd69f740efe65f86b1ee39506e56032acfe985ef9001d692d8a0d52a3ec9df4a3eca293471e5086ffeca98f0e70a7fe0d1bcd1737016b778b7ff731e4406913dc39221ec4b86560c219bbba0f7428ea0222df42772cc76d0dc521088d255103fe2a0887e59b198943f34e0a03a2915ab13733383ee6b9b24d70396223021232c88576c6a2350cfd0d8030f00fad7e68516ec234440603bcd4241b08cc0b57bd6745d01bb14e7672128b3d5a062816d219a88fd6a7b5d01a3c2eb98f8d0680f05b9e5a977d4988dc751a4091054249afc5e1fb35682798df2193a48170da276898584aaebe64c065d9b05c2c0934060c057b078b247e1e2932ba6af8424dde393cf40437e17d04e5db07022005f1a23e427041e2a9416dd58617a1417d9b226d2d914a5258f4d86b03683cc8069a64b064f54258810fcb53751a9fed4c87333b08c0a31399e812b929eb05e30e70b5314a1815c5baf87ba387bc1969d0f0e01a1173c9598669c9a32171d10c04c01ddfdac1979051701630de68d068e1946a466322f003379569e5a64086a00d803208bfebff45d8469c4f7f6e00cc7ac44e45b671a4018e41b7d3b61e22920d true -check_ring_signature d2fdd0d83999f079802c700a9d50e907b362bed64ce01315bc6e2c74d5cddc5c 69141739b9e5e7533969a1d580bdcab655128159cb84db1f433b326a80e53b06 3 8efb0f43ef622122a1b3201ab69cc6ba2c47a426815e5400a9b0ee4924b51baf b435ff343b05fc91d3cc618f9f0ea82101543499689d23e5b1e7caf978ac90f9 0a2be7fc91ecbcd4e3d5dfc9d1b024a8066beb51f53385db126b0de3e1d17ef8 861fb3f5b7ca2833c3b47f5df2dd51d8ab34ecd7c454343496252c57f958e40fc3ed1b2516893891b4521630d037068630c27166ae8d70fd6ea439d596384b080aa502526358e7b36ee645af69ba99df5d83cef2be6b28ac923ac8febc0e770ff48480acc340ecc98eb7597ad5ade5c231888b8883194dd2c5be89d90094650ce0725bc923438636bb727367f41ded5c1deea325a1a2c0365ebee08407d7d501a135b3fdb4a366d09328f065a5848c57700b333431e0cce70dea1015623f250e true -check_ring_signature d31475028e8b462360bc6cb5304158abc1253d573b51ccd8d5705bfc2139be3b c37e822ab905014a9b907e5ada0e586304ba172805dde350b2d35838d4dc9bb7 1 5d7f238a75fc818a6249d70d17703fbaef95a993239b46f8ff7d0528a2dbe451 60df02eeb79181b730d2a2cf50b34e809074e620c443ee98860aa0133f12b1d01d324bb70a19544dab943ede42b576506073b721c4999d9039b50147f3fcc10a false -check_ring_signature 4620ebaf2e2a4eba507c08afdbf873c958771ade1d6a99d35863af641c688ae9 384eb4f915428bbe655ac906ed9eef4188ebb9b3007e2e045c0aac20f8ab319f 1 c9e56d726cfcabf3b259583258c60f3cfb504f4a70a03d3c5662e3c3bd58f757 31d65be891997a8ef691111d7f99b9d67a2aac127731b83c787668aaf0d1be056d6f84de587f84cf141f76b39baaa13fc23729ba2d87cbc009a52759d3eb2403 false -check_ring_signature e08b9bc79cc08e5905846d3f602e2e07839fcfbc8a9401b1d3e6549bfafb62fa 5e9091071ebf9bdb65d1450d1927c5a292e4979d4a9e3c4c0b08a26cdcb965a5 4 f7ee8dd1916823a4e5ea93d74d50e1af91baf6c45c1818c1e5a255e27880bbab 9a8b9c546586ea25bd1013efcaf391d223e11884e6c0b9c27be4c54b250c38fe 55173258e8a281755f1df061dcc2d4fa500bda886176da95be4c89a31946f475 215bcdde934b24e3ac123bf41298f5c75fa1c6ca096bea06e16616d5c04c31f4 a406417b642082cb2a24c7579847c108faeb34b4f68a97fb710bde0945fed70156a6e0483595afbb33c14625433a951a3b243b456076540d4585fa2764343b08864e9cee58282dce9b427ab099dd482771a532654dc406e3db361a91d9acf30a9cf408d34d360d7e40d6e15d9543abce721d15967b343ad2fe0c44d311dc4608f840480ee3f178bac876b0727c53c24aaefc69dd4ca69e65a5e5a0e5bcd0890779194c203360cc5d37c2fdc78dc19b539e6593138f6833f8bc855bc4d9f8a90d145de1b9974ee6a7b8c4a6aab78128ceb59e6070f8c24613da0698e4fce84a0084c13721d2705128f0d6058535446a817f0133dbfc698cdfb1a6dff4c4c2b602 true -check_ring_signature b736d6c791b847dc10a8f406f994f601c6e516d0c8e3426115c7976c68c9e259 6a00e577a27dd8b96d67b33babf209f5537333f679a6fc04f43199a32ee948f3 5 a1ed562d8c77c473d390bc548374999f7c474b8c239c9172f02e0a8b6b62b884 a31acd1c836059f2c431f9380bff4e0ace367ded5f39bed0efe6b3f2c09e663a 1df6cec895c0b9d05c36e081b813fbf41bd9cda385e5436ebcd0723316adecfd 94185128209cf888ca12fcaa6fde98c574a74de4d4938ac1dd1480a7282bc278 aa084a4b4873adaf6bda74a84a7e04e051695ca4384bfb3b66f2b4d4ae6b9e97 63868dfac3ccb8446f98dbcfd8977294c2969325353a5cbf418695c38124490e8f804968a57b578196c2b1787dc4f0b98d3eb0f9c54b82fecd048d3a60d2fa07c9425ab2ff23431134648b738ee47f29d48fe378e81233a5aae68bc43e308b0a538fa8444164b7ad513d4df97bd520b4719b69117d25285e9583288fb182c9095d681eca7b6307f30e44728207409fb0394c6b664a86fa84af0f0a45f335940ce5161d483d19bf1c97c24312af94d861ef005e2e10316c543b2abfa77366660783f0adc6aaf3d2493ffeb51ac741e1aea833e61526e758835af08f5e3b76a50ca57d7c91bceac39c9131dc57e71ab1c343967a8e45e703e70fb408ebeb7e2608d9df6172086bcac8754be5a5aeb3923ea48c444badef678d32ae43013f08e00d20cf8b73e4e839b03c55e3a477e6fc861b8437cc1a19d0a81a06a1d2b5590104 false -check_ring_signature 840f20f4c2d142f9e520e19041b3d59ec8ddfc89f6502f9f8c58d93058464625 aa467a623d219f75ce6797be1875dcb5158daa8a5fbd9fb9520f793bbd935f5a 63 ae05d916e30325335696d426f689c616b85ec5eb553f5c50b0cf87ca4001d35c e3a4c8cca56b617903e21cfbe43c5bc808cc81c4d166d1367f4291462cdad22e 015db4e32a5ffa2d8f20f00d93be3066d45417791b93e37e5d09fce797bd5310 42d1fab416dc3e8306cf81156cf166c2e97f1ad3cae0ce200547622dba1395fd a00091f373537f25124fb49e38bc2cceb22ff0a1f70ff29cb08385d9778d96f8 5716b959cfdafdd7442e032d155ccab08c27a7a179fb53c3587fef237945097a ee21a585f323e46d6aafd408d88edafbd5117bf17ae020b077a9f3762e71f3df d6005aa5f21ab5a7ffd7a545ac73b3ebbf7e66860b761e0e4a4aafd5d04f3d91 5467ddfe2546d649952dadaecd2ce51248cf8d36200a9a2dec341069d7b50c38 be61834ef31d3a87f09e21f9a3236a8f6019b972fedd0befa69a0c0b9e660f93 fcdd234324acf90b3d45b4a7cf54726f21df94da11eb464bd83dff566a221645 f97e3810e4b579731d4b485fe6d49e61ac6cb3893e87fd4ae8c96103149bf0b1 66724ca67f1b8f51acd15c033dea076ba983feac74f9e63fa4dd8843c982fa44 c2503aa037e675cafa14db28e2a9495425a3783c8e60ca19031d4a3c706ef686 b5345a05db9d9bba9ea4540587bb8e7435f903e1bf32d96295ecc1ae3894cbf2 29d929e5facf381b2f64f279da0e17cd659ff38c8ca20c6b649ad208012972e2 0c447a1aa23358c24bdda790209a0e46183238a97dd24cccc1c71d56f1f6dfe1 4cf4e5144a1494578d74a6956ef1a921375ffaaf056b323b4ee8ecc6349cad29 551e308847445a8003cfbceb0c6a68100bb748678ccab8a46db749767a618933 5ea25a5c15037631289d5fef2498999bb2d220788c2839228ccb4f430ed34aff 93b23b104f9f62a09ca5038665ba397e7e5f3fe8dfa11195ecac3a96ddd5fbdc 5b6e5635b896be6da3cac6fe25989d2b7221f9e430d74246de553e8a34b86f7e 309496620ef1c2fa2427a3ef9fd0d3a5fd88374308c1ac9c51c1b8b91e6b5ee6 71e1a4787e58d0d7967d321ea9917107ce1fefce0f84c24d93e89bf53540bd86 cb44fb8c120267cb4057605bcd08257d4ad22487aa232bc49046c8fdabe029d5 f0cf5d2e53c518a761b9a8408dab6461856a08f43bf28c42d90f1f20370f8459 99ec83f7fd5a979428564161020ad1b3d7fcff0bfc8ebc4ca7f6f9346163edb2 d3e8c3fd0cd844c159bb3d476120d78890e787ffee661c9b02b981df084eca26 adb6f0a43043c5e2a221514f97705e3e51e4ee78bb281dae1ac6cb9a6062f432 972a3fb61e2f3025d971d0c9af874516d6ad9f7a6114004e2e5d075f9b19948b e358494024129a7639ced01db22cbe69cef5dd41074f5bbfccaaac33ca63b98f f72e2e5dd370e5e36b912a6f986852794ed88ae6a90eb408c8b068093b5950bf 3903c257986b781c26caaf0e6b36d18d81889d8928fe36d74766595c93ed7eb5 1f2806f48c5e790d719c1ecf77d92188abc52d83fc1fec1f23e5bf788172c76a 22faac0d37bf0a9de64076d824f972e5587546b34f78cf4253f876b4f545dc89 da1ee529e9072c7fd02c2b9ad1254e88d74420594e24614bdc88da94e4a2d86c a5e0b8af22a673fa01360e2aba190efa9c23ced61b66065119ea612170a67e1d ca67d907f3c11bb8eee4c890c44a6360131852aa892a62e87ea8b07f4ed361ab 0864a77c99162e20602ad8e8b93c7e3de13c1303af2804ca52a46b0787e0f3b5 2682a2098d73ee4e33757398dbea15b61fce81ca5665a64425ae0d914d042aca 19459b1a5c34c9b9aa8f11d328714fd8953d8ed26904f85245916136a9310e60 24ae993223da2eebdfdf3897da29f1ce4a6a6627a7fa69b7fdeb93a680cde7cf 738e0ea6d8fe637000e281669a8ed200733ff92e9d15c3e97aec2c52167c147c 28654b7f874b963e58dc889c3757fedb6f86de11189a5e165670c6cef3ea23bd 58cdbf143f044bfa9c97ac5ad2ab4e7fbf309ff0ce0d64328725e8346a228403 63d07feb523cee3c4ec0fddd05138a1a226ff74648e3fc9e47c9dcb47c016b87 9572858a2e741a1db1665e6f2d022fa69d832eed8fe8d04c29a03cfd65078c9b f5a56f83d692b0627e567cf2cacef6c3980a68d17e9e46cc0b815174e5591306 a51b79410a965fe6cee61be9d152397f0460fec265f1ce9be0d332f24c8a0881 38fc3b5ea555a8b0d623cf57f691e015524ce0da9e1661b9550654edb6f7dfcb caac91d7e31e408a5380400e46962df61f66cd8e00ae423315d9564c5d64ce3e abf3c3654a51d4e1db82c650af15305822430480bbb2af22b8e3dca01b39aca4 d03bad22649a077540c7bd63cf0034324811e5fec157822a2974e9bb76b297fb 8f27154492961dfadd977aacd02bba0abf3be4eca3703a61c09cbf07c92c0ed6 d282b86868e0235329d661c0e074c8337e5e3cc72cb5f6668ffe466c557a1766 eaa1256d84d46f26dc06a719283f72f2f390a734f89535355aefd7c8963b11e1 00e6019a677b99c9f08d37b5517d47ae11e41b910218897a00d59de3454cf9ed 434f234d3b326bcad8f7f26afd92427b7042920857e960950c18db54ad221f32 499717d7a1b46e5fa499fc44bc0120046e30b66687c4b59b5d49927b7a3e7fe5 ea9ddde6bd0391f1472a5c36595ec58501bf9a3cebd1d3af67ea425f6c3076f3 01a252a421b915b63000dbf1acfa1b290368f22899c1c47458ea37afea1b0da5 32e6f124d070627a69043f4fabdc732ce2f8ac235cdcf90c6e03d50858e5c0ec ed2d8adb4f856f88de2e66aad573ccdad14eeeb9ef74739b36b24a3819aea3a4 1b76a60315b7740f36ef37444586e13ddb72ced4191c7a8cf2b2c262e08c910522b0671bf6c5c96e62e2c4156c7cbc97c414d0ac3bde6c077948567e92172507fd2de7ba281f4ebd559e9327b5d4d026576688f0f6959cc5362ad24f724d9b0124bc381d065161ceefdb6223e0f451ea2c24f0fcee236d631c106fc86f73330ca5716a296258aa01db851b8353ee9c39e129aab17067e5702eefe51fdc3c5c09181eca62cd08138203ec3340c48ead622af797ed859dbb77252cda6746849501c84730f0d507a971e008fc21a640b7ff4fa6aea7e559e53712ecf8dc119b5003a09bf50b6cd7f136bb10057f9384e7a908ddac8450bd0dae4dd95702b418ac075ee09b5531dd456c7a575fb4a6a87096ffd3bcd1284382f5718db29a98ecb306198925eea4c8046634234776202ab46c9767092ad3e9238cdb62e16cb0cdc703e67ec84da5b90c55d6eb39935f1309980621397568b08eb53d1403792aca1c02b5d6a8df2992c3837fa7edfecfef806795cbdb792e6e90ed55c841525abde2067739a8e82b6e8acd609ea307805b7da9d89303ebd5d40668b34de93069c8c20acd175514ecb86267436d2ee2f1cd1fb8b45890a2edfb375875739d73e436d90b227d29bcc737865c5169cfa06f9ea38a7f004a498d7456cc41144c79e58e010512fa95a3197ec6b27ba79517e68227b6fa8183a83d63b8c267fc71603768c1021eb37274d08f3763fade8386434f48a4c69e7861b0752f241377556e638989010bc704732fda61035404f47c4d98810cfebc88d174ffedded4e903f877e7e00a3f4ef82d9e19ccb9f3dcb3158fca5c136b78ce9ab14c0ee713737733a288ad0a712dfb4578f39941b044e6957c5f4b64c11e51fc549aabffd66e913d0a0e8009ac91d4ea48773e5c5fcc086bc3607a9120ea0f1055e9f93ea7f90609170c970d1064d83688976a84ed7b7e5df43da692132f1674ef9761467e6c3c0b9929fd04d51606f27f3c63b34956dc2144ba75088aa8cb0ab059917f2caac723645d430f579931ae44b62b5944b6e3b518ab57286eda3d8f991a32c6b05f744e66e7860619d70e7317e5d6b8fb3a0905f2a72778f32fcca3407f62536c439f2a9161a60a956c98a80216b4142476e4a45db5ab72c66fc06e00d54e42aae401c7f34ac40a079ef8231789eb12da856a233387ba88e051251e3336e52f1db06622e1fea60fd9b3834f403a52dbcd53c17353b333a8720ac9eb42d957ced88b5a3affc1260311e7b1b9d01296e561c312023890f13a655bd7d94f99cdf02e20325c7ce718085ecdf8b73bee2af248ea96bd045cbc5fe81275cc73f33a132553954360d7960a27046fb93b520f28e64704dd01303ec0d514e45828325c2861ea163aa4539402ae53c14dd8547d1c3f4f32ea1266fd2e12b9cd62e2c7b5548a6fe01a90c7b005cfa92b3690d0632fb0655d303c4f0f07b597a507739c60fb5afbb6e6f6d8b50383bb52a9da8f9e79476897c671d16fe7b62b4dd774430f5889f0430f6126cd0e5f34f6499151a835a2cb7de07798cfbfb7519b6c238bf4c06e1d4aa34d324a068196f1843ea8d8db1fc0b6d46f699a7c4511e245bca8e58975eb4e42da376d04e5308fccff1f2a1fcce8881715ada4263ee7553528487a922c51a03d5f0a250b3764cd0652a84577738c895ecb1c38a45cebf7684de349801e4e894c434a5c0d0a9e6ce376655d7db0691bfd78b11bf6d082d8b9aef785cd415d663300f61a0c29d5fecf048493257d721f466cbb58a8e170224992d52037a4c8b86120d5050a5369396c43cd52bf8b361bd0eeeb224523c86f6c4751be480a1739d6160dfa02822ef0c873f5397065836767a28345defc4b2f475bd3268d02cf6dcb8859df055ef6f8ab4d98307f4228d615a6c2c6c753f3cac81175026a4dc91720bb6009019e69ea6e026f53b6d933a1fbba01da57e0f90ebf22c4a93f75d7a4d754b2b30ef3308857f6f826340dac9993a3483afb079f45b6b03daad0b3dd71eed57bae0085a63766559c36982c5fa37ab7bd66f21b2743a01253e93ea4426cc0e260d90c1d97f24e4acca958bf06983befbff056af10f1ce7873d3bca4dc4691c8f0dc00eca1da1f6895f06b9df2d3992c9a6727d52ef64e9efc2884a6cf4950fc63170ab74f5f538376e283a38c2d9f92d2cce6c2e392ecb5c91ce67ac4f2c1382b5503d3b318505c7871ece16839bf61089f2a27236ca979e48e986082921a381c6e0395f7df64fd3f5a6fdc1980df680e7455b2a88ded83ca7b3a68262fe9ae4fde0421a15d6b7330d3d58f2da110e0c658fef1e7e00e3a59efdd27b90c0989767501b8d959ebb6a16105aee2024ddaa876f7017c4955e9057615e59a66507e65f405857f738991e7f3b0cade6e3a2cc1e031f5745ad3a1ef5a9055f48570199b9f069123e7b35b3f1c6c2cba6732bdf16847caaed63060a14f177ab04660989c2d0a32087b07c41a27ac261425910d04f084dfa8779410ad225b316d46819d9bde0355d25a18b5c41c2accb3d793b820c83190f52ce2058162f66c4a0bf831e6760d13e31d16b8e80a3cf9b73d9ab8feb47a64dc840550e6cd2084a1ba3bef71450e017532034767795236abdbb04023d4fb4a9c0eab5f9f3848c2355c2410daa0023b2d58f8d7ce682e0b60778f93ca18d0a9be3d3edc7be6f00439758c56d4a7020fb1c3622549cc5ddda4c08343832f58516f782103977f043710c0a4e5f0530efd2133ddf110bac1e2add6a6f9ca716cfd48c924b6084e1da287767492abec08f0f491b72ffec50ee7690a4b7922f359c271516ab96ac7ac95041f27ebbcb30ab57ca3fa0f945e2259f678523aad7b7c563f1d5736506eefb35c94035c9e7d00d8e50608d46708b27b77838fb11d9f58e7786b3da11383cc8d0edc199874da071795ac882dbd7f815b4b430b82fc6121f71379fb63983b31a661734d74b554019452e71f1c7dc932f297d94f944d2d3aaac4df286a0bfd29170facaa97e1f902f0271a786d7b10a62bf033075121527c4c383b9d84f22a0b57d0829fb6b7e0050ed6e6cf2cab97b29766062a2720959cad25ef888a7b38fa32cf970c0cb2740b4b2ee665f2ca065b551ab91ef6a30cc06421f2786dd4565fd52bc4aeb87c9b073694c1d9a24cd737c13e840096c0b1210cda2073a100880a7e32f4a5fe8d9f0eaf80d7242d9955ec86e12de535e102fd0d5426f81583dabdca1b2bd2dcc00b0e501e30112a5ba2ac9ab33d800e23cc9569b42ed6e6fe60c7d4c7b82e00e2990f17f1ed81aef5e75008c4ee4b7fded6d0455a5a38f1a92a52daaec569e3519d0ba7a490da83c56645f723b7b313d2ee05509408cbc6f671295d1c250a4b96cf09d61e88e26f824b4ae7ca34a3fb82da7def1226434457460945eb9427ea58b70658d23943a23437efb1651e3929d4878bfdcbef1391abadbc7a3540318536cd0d2a505735e5a7a978d9e85995d94d511fc51cf0efa029902b30577de6cf8d45025c3a2d948838b845060c9ec9d9400314080491bcf86df535ab3b7863f7dd890283ceb65bd335db40b7b4a5f9b622d0ea158d0edbf5e9e29133ff56c857fcc705e51c7ee7b961defe488b8df81b66674ad1bae886569f29549e05a0ab61ae0b0982f41b36d5af01f7674e19411e9eeb00c9b2da8267c9004e8721ae78c3206f0e6be2bc114f14985952ebe0758ffc9e9492d4573f97c0dd3106e30b10fb26ee06f30f0e9c981a54fe1b0b15487cbe660f9761372edd8fcd189b243f2de5ef6709a1d8c3296f64a88133c317fb08d8c7824b7225334dfb4a683d30c4dfe49b820b4e98542798f487b187e5c80bc0e448d21897a55a0c7972e55ab01c50ba0c410a1fb3eb63c6c2480f4433f6b8f054adf871579499a9c8344edb693f6a74681404ed448e90d1277268563c31638309d105ee7f5fc551373f822ec39a04e36a15056b2c48cc22db08f20339b5429052f410b2ec2619f382f4adb47995f23d9d830303be40233ac7cc654f2f424186650438ad3b040ef12a60e128cf243cd0286a053541f355e4ccbde19d1b4beaa6172a9c88cb5bdca01f65ee527ca34e043e620469e70e6b811bb55143b2f4459f5c1e588487ce318f8e442b844842eba961fc0efa4ddc3071488228f312d18929f6b7c7bb9d386de91809a7c55dea1f443f400e7c5bb91cda0d04cdd7a8f2b2d41466a22678e69c2eaad04481b8d19a1fcce607e20addcc69c52ad7ee815f44dbe1d1f70665fdda71b5ca02ce24e789e46348096ccb928fe9ba089aded80b4392904e413a921239860ff5c7906e0cfe8c134f0f52c0ef8faea6824fb5970dc61c29dc5a50642c08a0e7bc44c0615cf49ab6ca0efe877430cec3c5f6ca8c6954c2e2577203bcec53c0bda3cd3fa4b3669ca69748b71e1fe108ab876b3718091cd9a445268325aa8ed9ae2b5a1dcd9a88285c6b06b369bd0bbd19c39f027c03e3719041e7fd9a3f2977f132f6f99f806c67ab2c075f6393a41f62d65708fba411ba3b09979e22dab4602cd42cfc572e77617a8d02822cd63f7d41ddace851e5c5aa704518151e78620441f84063f8930c1d0fc90592f4e471060841ef5c805662d6c02ac8d4451deb17cf69ed2fda21d483d8850c3bbb2e16f350cf09d01d757e5dfcf78135f5bf470b7442c9bd21ae28064b350a173e3142f0a70b41f047a5e1ce4a7383861f8673f657bd36ca6dc08b8970500c29be5bbb4ca74afe75d99cbd6ad547d7a8ae1d674b727dcd70df96d77131c60ad6a9386815b2ab71312b5130a0b3230fdbd50da0eb651455f6324cbd8853280d82ec2ea9e63dbe32158ce50705e8a230f74d927fc6b069893315660dab82180afb0b9078b764ed1b1ce664f2dbc5382ecf1a7a2e83d95a7e3b64514808e4570841a1b356f79d5c9c20e351421fc787120d98339f222575ad676793764d448f0af183dc9fd09af67bbd584f47dee677810e1f6c03d0b141ac0586f9fe9e213407625126464019470f05e5ef4f785d9725ea37ae704a956742e13c495ec7fe580ee38aab41ff4d79f2bcec32e007fd88096e458d00c714f25ebd6ee45b45b1400d218911d37f2e6592a3259e7c652013aacfd00401bfe44a31d9eefb854f900e03b6174345d48b70a371ff6c80793b7292d48a2a32c3ad2436f0ce6733959a8108b3b3bb8bf4e5d4f746b97576a302d2ccf7890054cef36f8eecccf40f5dac8d020d1006818b73eaa7333be579eb6744316c53d219b42cc1812f39b0fdc7915d066b7e847c546827b1c22dc8c279393207a62480f2b048fc6a3669914eef0c14059372db207a292e721102b11bd0ae825fb9293c20cdc4f0f779ceee47f315190b4794d8a48a58cbe8159f232e4430d48ab14fb9b8361535d482766f24cce846049c40af5e97b0ad8fd72c272455db648a3bc4fb8eebfde95d72ee3452d0bbfb0e105158cd0f0f2efe1f59eac5ca5d613acf4f1377d9d545c56ca31b19d10de90569c5896d858d86726395d83abb4415d55e5f61217a0c5ed305431abd179ba900ef279df10e1c93aa3f4e20c6caaebfedddd24704d9a5234310b5c4f39b704309e96a0ed477123f4bbc42d4b5423efd38f991d1ebe474184b24ad007a4395a40b8d6a8bc03b21e7f2f29daebdd5142342689cf4723de23da9290307200d9ffb02 false -check_ring_signature 56f3c66bc7088f954b843e05dd3bca67310ae334f4b86e1b1003c025dda2ede1 f812382e80c2fcc2c850f8d887e8a19f04b6b269ede1b3bea9f13bed1a46f31e 1 266204ee2673344490ae9dab8b8696e4d526d8aeb6fcd235e4dd62509807f781 65b394ed8c30f4f84df5101b3844fa191afb1fa4880b5386cffa86f492795997626034a52fb6018e0994e77e287f4bfa206e162db863956b61cbdc93c7599800 false -check_ring_signature 0d9c18ba68078422acddd8b791ac3abaccf00a61db4e32f83b0550f3be8b6572 32c452bae0bcc069cc2825b517179324d95a56be31dbb733de595d490406bd9d 36 07f410517cbdfe052cf6102fff61482f9494ae7b29805d34ff438467d41aa317 d86a205a5bea750720560945562f01bf28368c55626f415024a36a744a82d4df b21e71404fb2bbb415536a860090efa487bfcf4816f3452c902189e21aecd5c4 f2543ef4d0c80e83538fc0508346cfdb9d30e9cfd15298be22d91bd6af876622 b440653942ff720c2a383e9e6599bbf121d81646a5e8f4e56b46b3c973144ede a6dd87e00503e2f32b682ceb6435841686327d86fe89fb5aef62301bdb9cfd6e e883fb8612424b75a314281721252134448447c99d101cb5ddb8fb0e675e1210 dc3fe4b5559687a7d9dbaf3bfae00412e4ae24d831e423b3ebe3098c47c4cad0 9aac33e96736a08594179efc2f9f62489a5c6479c4036e2cbe48a486c9312d12 e33ef1ddef296afcbf563d2db4b0686a02cae46630a83d557095cdda7873fc13 71421909407be81a06cef7e19ed9451015d69b9a0656ed40bab4b52713d3e0ee f5671d386bf4cf70ad60416162bae84ffc3ae7647376bc5393feb8554346e165 2673b517782e746daa1deb2aec56148941ca7ba0e573c1592269f579e89a79f4 854c7f04dfd9d82525c3bae08427c238bfa68f38dffbbfacde8e043732a7f78f 9de047c945bbf265d4e3f6c498147e1cecfe8e8b2ef292484ac71f58889dee2b f47d09ad61d3a15585a64dbe93b1f88fe853b76dbc1d6d206ee590d43c61bfbe 5ec03ec902a484708ab6df78f8010b03905b92dcb76abb5d1ef9efd687652fc5 fcde496d2c3827fc3f01e72bc83ce78f836820d7cf2de286706aec851d0ff081 2e746e60796e946eaa1b85fe8017b4227043afa35e35d34db225251933c1cd89 e94970ef934f620cbcf6bef18202e1e91aac88ff61e08b0e844c166a35c9d7c0 2a2e1ef5246403d257f7a98de8f3cbf9b9a009000ccf102cd23eb50c19926405 514f3b9aa77ff41c214d49aab248ba9cb2030fe58170332ff117e98866adf801 d709a5ca964b388e5d09a03cb8bccb035c48b7203424ab88f2c2d766150ff5ac 6459bc37e55a97e7763dbbcb98b768afa3375afeac80f96321f1505ac3a2fc8c bef5cab44f52b80f348315249f5d276d552e1c1bd00bd342abdeec872a64c20c 9b32668593a6bacb0ce6e3f51c9024841a657a786fdda0fb156db0fe863867d5 4fdb2ecc809d0ccda808b4c1f38f19020c545e61fffbb051671a27f8032905c1 ce28a0579e3aaa672be667e7537d6e205478f70c9171e0d36768893d96714a33 94600e7cc80633342390d593a1ca8f16393c39e52e7c4bde5f939a29165a13c0 93d58cfafb13ae2dba392071ddf71631f8ad60d1eb71b16d76236724e11302e3 058cffac4c071a835ddd018a4055deff2b41ad45e4fa4d92e2f548fa904ea48f 904494a113c1b4acecd8aa82de6783e1b5c9b5d232f2396af67d711dcc2ee73e 35f07f7d60f133096f9761d43e2882bf5882f54977aa860c014809561e11d705 6ed4ea935823deac004b84b6d3b77dd9731e614b8347c5a9e9ff0b8a5d0cac28 bd12b43b99a5b1a543219d95890be067ef22a818ba08f4ab8253443752f03dce 681e51c23b4d22237d9eac6fb979744297d645e824d928734b0044cf29255264 748e77df41b5e91ecee13a221660c7bb0ab93463a43b15a79a33376ba42bd40b00e8bbc66e655dc96d6b23036e7f14a3a0d79eec1933d63f25915dff8012800be888920cc1a28c9108cd4375a345408f96e0a1cfa0d5b371ffff957eae8a7a0f3a4789d3987ad4bd3571f49ba5e077753daf5762d07bd76bfe32c2f5121eda0282474a0bd6b4b6f9b7f2d37c82df41de9580971a1b8d37f53af02a80bb0f940d032fb551aeb92fe6765990bfeb6b994aaf7ec896ec8efceecc4119385d4b83072087a4d1158b3338cafcd496b59da58c2f3dfc18b3b31cf671a846e06a489b077fd87f2dd8851320d0dacaa957dd77a54d5c202968f38875da23530717d90d04fe919abe13e6a9c39f193851fbd50ced0630c5e8729976ed156541dd5f25d00958d618f22dcc20a955b417f472a611ecf27036b5752acde8ae187ff52913dd0ede456fea03650ba7a68a7e11f87931614b28d2a141fc0b1f3c4b8de1a1a2d60461ed98a7cdd849f12657ffbb847b61dee9b0dd1d162fe18606af4d64d25ca6042b190c7da55728373e79afa4e005f2baa1c25c4661ccefd3f069221b8819780bb5543d02f91d7e7733865979630aac74921ce29c387d46fab8c3713200964d0c05611682fcde5512758e9467e4a418de0766397c60a21655f23d4a641060b405924c790bbe5b09c11a6ae884a433613f84a23c6ba0358989489093bb90b8280e7423208af2a56db9ec620eada05db48a112127f698e07becc674dbb0e8a7de092aa7bbc0b9b0658b6be1f2e7f1d22ff810dd2579a87005d8c019eed15134ef0fd6d7e3b642c49b1ab8033829e21c5cefde4eb1c054fe90373dea1aa266e7280c78a3d47aa33642cbc380a5e6bae3772c4fa692d6631bee0812ea590feb7d480cea39c32a43a248ca866de063f1dd26ab31ef7698e8b17ae6b28d08b8cd79c402bee41473ac356f8d462696a3f8bae58cb2931175a4e38093309ad9663641f0003eab77354dd6ea7c3794998cae5252e2d4b87f58550cfbd5596d91fd1ed57508b2ffb8b408b8f2296a2a7d78b8d4edae4eec2899e9a548c1414ede59b682050bbbc66a484a3c1c0894f09dce913df6b379ea66d44f48968d37619dd364eb820beb7893a293d9093ee34f01e176cae9be2f60f3059be4df64b8f44f6ff4e2f00bf3bf44b42edcab76bf614b0b62eebd148f5b469a519332404c67653960b44b0142a39d1b13c19991af252885687a25b7c443bb8bd0bbfbd45c856dbd19eddc0eb9b1fe42797dcafacdb5bfd831b3410666ed727efdd0ffdad868793dcb7ac70959be1fef2cf417f6ffc732c3f974a860721c8bff63ba464f58c5baf4282b9e013e336cd0310bbd73cb2577084df1418d600c230cb373aaebbc2e219c872c920b84fc9d9584031060e3aedb9f3d10ccb3d635600dc7f4e517308530e4a9bbf90929817f730785b96b71f39c199999675b92323490d5c92eb74aec45f96161c0081c28e8ee16b8b43942c706e0cc61eddf31dfa3fdb1d1e592cdcdc1cf5a74e10615f4920cd34ec6629d71974a5216b08863f135ce477ac46af5b66fd91762c1003d6e76f4af384966fa5203598ccc193be1da3b366613cacf97cb5bfb2e75b1020030cdc3d1ef4d01224a544d284671705bef8d0bc345c952862b4b6c89688d0c158e29dcfa798899ba6256ce7431a34d1d212e45243d6317c8107bcf9fcdd306afd55179cde76bb3a97bb984dcfa4f02e857228385b1a62bcbbedc79d1c1bb074985532f46c94dd07d85a5c50649e2917e80d8d196a6ef7d28c34b7138a1dc01c6170ea6b07a31dd57ae9733a914663627a00d76d5d8eefaee099302acd1bb01863c572e46dffff0b10d654f42b7a6ebd6b68ceadb30a5fbe30594b6d25c920e27fc4540c1d2937a386e4646f18628744ef3b43d42a27eb835b7fb8e16d6420a266196ee8a76b9062f9f78914ee47a48db54e2b66787d274034c23d3699ddf055da1da951678af26cbc78311b71fe63af4818116a13419f3c9daed049b5c3c028e6e9ef0e7409959e4cbda04c7f489d73f2e3a50a9aec43fc14091e6e91c7a04add102ef81ce0c45a23d0b9b45a1203c6d2fbef0053197ae1292947f00fa310fc890e8f1e85f34392f9add64914c05661d63cf8284dddd951c5994c21e8fbb0a754b866866c8d888ec6b7aed1b376ed74cdf4ae0c9bf077242b87b1b4dfbfa09bbc88f3e78f3ceea866a0ea7d2adb5e82f6b5c4463b226efe57c3be3d1a03b0070fd999862faffde8b2ecc813534b43133d1b4d48d355c023305cc0ec563fb057d2f2f1b8fbca469968ff243bd5ee7cb0937dc47c8580e43a3bc22b466c3c3074d83b2841f4260e7cdba2ab557467f1e1779dc5ae11e85cf7d24aa9bc2e1860534963e57c4b33ebebbae077574533a63b617cce908f1d7cb231716b4090c8408559c1686b0c347fce5265c929ebf9144123be3b946dc5785b0227d9c6e4ec30a073a42cb24b32987389f8efdc878196fad0063267dfc07bcbaf11dc64d4a9607ec4b13c3463d94a63d4c8ceeca74ff4545f0461271abbdada54965549dfd400a004596a8715ce8ccd6700d409adff7f7d50daf892614fb8de5a50d089adb4500ebc8dc65fdf24d710090f1494a5fdc732306582938be29b1e5bb61391d5ff9056ea497d167c84efdd9a835dfd42bed2ffc125dc83f2a1685c96d31df1714a9023838b57ce9a80693987997ff958a3020cad447d3af6a2c7c3dd1b432a8a2a204031667261fc88ddbd7040d45089f15209da0e0a7997a701cc4f252f44c91f90511fa7927a42a08962a84db8f3dea6d2ccc56a21e08a9934cd49bab761ce86106e53551aed3e5da0209e71a8635bf3031de7c0736e772caddc7595115ae46200faeba93be720824c45c782e00beb8896647c52491d8159ac021307212f73d3f0b8ca23d2bcf212b7870b3ae140f54628863e9e97afc5e315f8b410600d749220352c7d82213b0f65826335d8697c4e852df71705c0f66f07423d38d0128d1c40277d2adc2afde021937af56cb7adc6762294f1b047e89bbdc878f05cd0c7dd6049bf891f3f6ac29d3f5d0b9c2af5c39a4e845910e044ebade4f850a4771ef4d012c8e1449de42183c5db6ee41ac55d3050e731cd60b4fb2091012f64ca0257c0f4a9caa31ef5e45ccdc483e6d0f0e8c28dc19a8964d7e6e7a854dbf551717eb0a9086144fddb5be5629ec10f66839d8194d6f9a142a13dd680c1466f1229dc10a true -check_ring_signature bda516656f81ffae2ffc93be2192b78492a35298ee4856b735b44d381d4dff5d 1ef197d228d8651830ddc7539b32464520ce0eb674b67c623922c787dd97ffba 31 c4056a3e42857f9be6546608f7ca92153c47302c2bfc5bd431e6f83d4c6e9b8f 4e30a53ef6424075ae0859d9c952681732fd3986d4983181c44055993e203ff9 d1ab6419c9e26340eb8cb2cd48c87546987b0d0dc2283ee2a261837e49ce491d 99be763b9e82f8ef4e2dd135e155af8a3d4aa4dd24d46d0a9db7921147f6ccc7 62b34a781edd73f1b727b502a6ef67c6f82088242b6fa28208fae1973a7fde08 32ac6832a8b070481297fe2b72c98635e09ea11a88d463bac9d0d98136c07500 b8b8d94c4537009913cd5768493443d52fe2bb035c0665a449d3d69ac3d9c099 5f861d9cabb80bcf63c0dee7c3aa76237ec49dcb3f68dd1beeede5140f14eee9 f9cd7321c1217401747bf91f3fe82639aa7ca67adbae7847415fc65173e8ec6a b8267f3ba26841018dbdaae833fed1b50ef8720caa0a42996efb7cf36470c423 af18a6e2206b1ae6b76f14d795be8776d44e4d19c3e5fbaa6cab1bdc78656b64 a6e9e1016a8f9820c3bf47057488eb2132e2b861f379becff5d9b2f24c0be1e3 39f9ebc5d8668c84737c2e49c3ccdac646497cf4695d71699a85283b9e18de7d 889b70eb2e7ee34e48bb526a7cd300713e1b638725ad163c3d20e1afba65232d 0ab5ff4e209d2d36f22a393c49078ff60efb97c73f4023e93c5c7a30e8108214 b9234fa4dcfc54eaac78f9e9e55083e814195f0da5bf462ff9f7b0b92f412129 6d551ac50913acdb43a47a994b9ce0378098cc9b11009317e5c5797336c1e699 6cf0a6b17dd60aebbbd1f141c6c86e97cb7d49c6f9b9b1f70b79452d6e5f5a58 b9d35e815fd0637f9119607dbc45a78ecc8ebcadc5c0db46b5405b8deb1633c6 87bd99f1d933e9d927912922f466539d2edf944faa0c4b87e72358885784e4dc cf11ae1ef5bf4f80829f3617a89d1a3bc283f2cc341d7a2e93ab77b73683e335 9cf5e02516b8affb8c754209095f404cacef0f1c4205c35d8cb3f78a2d014d76 4996f60cbfd982d8578b3f4285c1fd81b74bdbbcdca3fef094546aa10602dc7f ca79aad4e1c511bbaa141ee3f4b084341326dfa639cd3000d1e0844f4b30d2d4 89fd5143a30a12ecd6dd0f3dcb08d3b8319053816ad2e7b00ee3afff96740f88 7e5f49faa6babd9608c26fc115783e8e0553f838161a9a3e39691c38b5972796 a28b405299c297f36137dcb1e68af3f85299570ebe4fa572bdb68667651a6f85 4ce29997c77c4dc3bf885c5ef8548c84fafbe13c77b8c6b4e1bd9361515aaba3 12417ecd1d0248cd2cd6ee11667e0aec3547206298480d65b546b58babcfae40 20ea20fffca11263b4c29fc773df39924b4a12a2b40e40c2c94986180bb7f2f8 72dae1473bd34081f55c050aa30e6e2c05e20fea2a4e0f4c649175ff771c6943 7e50f49b3e18d16122ca68aad891958be7dbe1e149ba243c01be06e4a0848d00b5ffd1f84bfc8bae53b17e56789876724872defdd342bec38573d025e351080c602fbaec215bdad93e66659fa6d99407988c4e2b90a2224a095054fcfbc8eb03b7ea5d81f272c887897d1add88ec2e3eedf59652328e67d22de1a7786e51f1045c5b651a2fd73f806bce06af8ae026aade2f4274994c5ada30d3dc004b385b0dce0bec2a1a8d21280dabbbb2423f58c14ee2c44e94cbef33244ed78e2430a2012d6f7f1a1fdc0b32180796574fd4a49a71a64046da50a894ff982ae29c6ea009c926bf799687f72ee660b03e08457e4d254578fb5513c83e3cd251603945f5059061945af8adcf124c94a52ffdbc25a2f8d051aca2f57b5db965c877fd0f660e4ef7b8490ba0b48dee78b777b9a34d7904de280704485429c0a60477bca09a0ccd5124759e921bd0bdc5ff23bd277b7f7f76c9d84e5864222f3571e85d914b052a57aefb55d3f54772bbfa596bbd634c0bc5b50c98e544eabb8bcdd98d75260db6d8e76467d7615f3aa647aea998045473771ea475053273cf2b4a71e51d9e0b91d692eae634ae573cd6af0a49f06d7985f16814d747e3531cfeb8d6a3bd0406ac57ce44eedcab429a2185c55189d8fcd51892c4005a9d4abe74ab1c93e2bf06b5f1ed0c89bf754655c6b8f388150540157bb5772c53855fd0eb7390b61c600225306f9b198e6ec5bfde9c09e46d9d6e79fd0026df2792d0c696aeab21608408704ef5b1b43c7735fd64e0afa74ac8efb67fc75aeb475112d070cb4ba4f1f003cc68b0a9bacac32aa02f6ee6b13b5cf6f5f503ffa70fd29f7f1f90a0fd12c502d930854d2499a430a41c100b95018ddd745470428a8fcc2d51778a4a14318304c472f391569d6a8e3bb2218101cc7497ef9d2a6ec6b4c6654afadadce8aa710791dc6dd2b04c201b4607a46a487dc007c4694b64a9a4a0facdceae110c8ea505fc5ef4d43e32a2aff6dd25beb679770fd683439eaeea2641d9d244508aba6607366076608e4ea2cdc69549378b6c5b48da2a7b74013f2585e4417965a19edb0ed6fc9655ef51191af57eaa7af82e57788162e564dbfaaa9b055e3f0efef7460dc870066fa1199f668124e86b95803871012ff4b0e2ddcb7e01e4811d704001083404ba30fbea5b5bc09f8b070b289605123fec3668cc7c69ed4ea110c6f1f50d155d2c41c4ecf3b694aeb4372b089c49ab1dc82405f612938b9180bcf367d60d90368868755cbf36b330566f170e70421019a31a55f779d1bcc6ca81640d360baf49f88383ca51ba949e2bfb7d3acf70ba3f28701a435be9b2c036bafc280d00c1956efa8653af6e9857b983f418df848f3aa6886191cf242a22d41729559c029604d49aff3c4768d3411f6b08b543889d8298a967aef7c84b7b8f5bdb98430264ca9e5fc5b2d1279f0706143cb1bf4c576e93da7c6b00799790a7ad4c0770093e0bb316ced621637106a90e28b66adb699dbd0877d7dce05496307fbb3a4601e4b1112aee7af97afd39773d715ad2ab4cee2a971866974f4e37e2fcf2e868018036e8544c35ab44ab6798c5117d375ea452ce11d95a881d79c4a8ceed7121031043d1695efa0609b439be4f943fa6b648ffd6d698508dbfab688ed13a4c7e0847f821c95e9996b760cee16274cf87d06efac4ba3ed632a96be75c8d3a47680ff6fb21be39cf532daebf7b0b971142e3bd64d52ee9c78a23a5b89cb77ebcf506dbc9054c21b0af754dd4ee683e5368f726b0015ee8a619ccaa6abbfea4ca03063928ea58e2c4af8788d71ec759e28674e27b5fc9c11448e760a14065fad74e04f2bca51256405cdbfb37add95d585bd67d6ae20999c8e751a68fda73a9b5850ff06a26f802e61f4af0e9e8e8e6adbf9cf98c42ec60b8a1f29e1575d91fe6be07c0de41136d3e81174f77c539d2bb6b3301a043658e0dbe093828ff65cbb06f0e57ee36d73b1416bbb56b3a819dbf9b4ede387f981b774a562c7713889043e70cdcba6650ec9a6d21b52523f0ce36013f4911d2a3619ce6d8b5fdf834a41a780fb083630e484fc2c1df45ae3c3bd3d2d2cb5d47ae382a5193de070dc540e20003dc25b2e3c2a0485e401fbc7b09063498ebc478f0eae3988a1f5f1543a768a50a78df3ea1b603b32c8bb3710d91b960f72731297a919127b5556528196e2d6804f55249d3e2a054bdf522b3fe95fbad1526be7aa1fa6efdd878cbb6c767c801083520d253639596181cf51209da9ec8ef4d4cb6df07bad9fb48117b5ccefab808130f7f83f53b255d96c642441f33c8b2e1c37dcfcdacb36676143db647131304a5694456f1e47120dbd6384586ec39c934873c8a7a95d33c2a0ed6429c40b002163865e19aa25e24f2d33da3ab84c871d514376fd93f1ae2f437d9197a5cce012b8a0fc2913e0aae922dc0b565b63cd1f361d1d4b53772f5b635b1718972150bbe8d3860574d6540b7784f4f55dcf411f81d8b5b0d9c9968ea8eac1e95899c03bf053160166ca53cb356cff1ce52bc7b06ddb19e70ec576300b32e06114e350374ca97ac11e2a3f4e68cfac020aec9d92a05299ff29a9469e4989cd587327a0a6397a1e3aa54ff8a123727fded47eb7d3a36ebe172c15b0ff87c847c006d420b44f38d3fdaba09d39266ad816de20b7c863d33529ec1e1618c3ae8e47678a909775177344ae9998b46f91d2fcfccaff170af095b7edfa044fa37ddcf0b80cd086a8e22c582352635538a05caaef36ba17569983023c4970626c099c272c20708 false -check_ring_signature 626561727cb98e78d772c6e0cdf2fa6d33cd65e055b7f765b830fc7d448b6022 bc18cbe41c9e290f055d839750ca93e12b64833522a7a16ddda9db5bdd1ab616 13 7a9060046b43ea116753be7e1e43d4e3ea33a09cd950651ef0c973e6ecda35c7 316d818c4ad2839e366fdf0178d8d8ef2577dc761ab3cca474a71b4181ace667 411822075056f4394dbebf1bc49b84a806741709653aee72907b72a5103516e3 ad25a922ddfb20a570808774ed19a15faa6e509187eb12a3dff4d3ddee42d3cc 16cf1ebe751b6d5d56565f23cddbf4874a37597c3fd540ad360dfb654ef26796 a11729ee0437e2914630a685b18f332c59fbd4f9383dcf31a7c094273df5bec4 9211dae6dd8e4e85984b4f457be236a4b76a1cf0fbcfdda2802af7105ab94fba d4a7c313ff04eb40cc30e964e7e5469446859c6041e140e31fcf88a69e526299 c9222ea6f0a8708a52683345cab1f4e9d6e498b080d665924c43d69906fef194 4818cf09328b62652ad425fa2eb24c1148820a951013fc571c8286df65341f60 a24ce1773a62c99cce7819cb35a97d8b394538abff1c9d72ba5ab5588990a286 457400131326b6f854e1781c3d87a62a3ab89f6780cf47f7e32cd1957603de6f efe0b6c96ebe71d6eb56d6cd194eecd485bc5740cce68f148531f23e30627249 8efb198c4f65f180a3b38ec9ed1e8db6dc1f7bd03a0448b5a006ea6e6b99730eaedd72c68bf27801b256da72f19d6091ae1194313b17a4ef3f24a3f6ff90790ceda6454995aed21075f21791af78cf05fab0c39c7283082222f6023ad98aa10b157ee8c04018d0e83674fc00d0a3f88e1514203f2e2645e7e6d7312fa6c54709dc8bea1920f5bef5c1f57a06c3a5b74acf397de07f964463043b186c134106096ab43ca3593043b5dbcc453819e1c95bf23d6b592fbf9f7c5555305f0cac790330fa5b6a3d25ccfcc513f55ee8983e44ce3411083d232f105ace47d43683c400d2ae1438b040c3c8a4759f7553271e9aae75f7ae4458155dc068745d0dfc7c095b2df161204180026210eab665c35e5e307d80823be22c5440e8da31d3512c073f214f6160a3dddd8aacaca0c2c22f378ee16021a536348e9929afab65e0f605c430be7f70119dd69af21a11fdb91fce7f1fddbfc184dc24afab801cd4bac804d1c217925890ea9e39027e46df5127c91121e989ebf8f4fc2508ec3f54b8f00e0d3e0becda3e9d8e9f93c79158731fba312a6fec8e0d9fe460b73ba58e77ac05f1c107c330dba63c190833b761a7fb9da50c85801b91e6430e2016646cbe0f0346f9173d43bb9e8b0fd911f37509b4b26f0b1003b00e5b0d016b75f386c0290ab8618690ced906e7b81687bd8791edec0caeeba950b92f9db17fd9ba83f81305d6bb4b94561842e1297ac732879c716c6a423b27d40eec25621037f9e5da45049ae588a707273d56efc08b2210978f9ead2bbabeeb1d7793ca9803ce71ced70b0b1dc27117a8daa0f93153c0824d76099272d8c81dadba9bf6eb35b377fbb50af146595a78848b97c6a6b568b3533093279f4c581812bffa46801091976499055de29544527565e874c2ab49b4dee379172313ee86af51b48d44cb8ce05b550834e14a444a615aaaf6507cf31d3b3ce4804a2c959ecfada8b6670d8a2690dc04fdf2596b08e6e711f21d465a91edda0ba11a9de9af580c3936aefb06f5b4140c856bfc1e2adebffcb8ddca5659c2947cb891a42a5fcef68d6a875447fb925007ff288201ae0fc4494b88b341c97766604b19bb2715fc9e219bdef769b0c79f04a48da92e35c4c996100d5435cdbd74538d8174cefddb96d701026a7b58655805 true -check_ring_signature d33628558fe7212f6a83f3b4c26b6ae8ce48c8d5c27269988d1d561d4eebdd68 49c7143b7504e5937bd6a77298b55845e37a69ded944c472e6a1066fd1ab5968 16 228808ea8ae5a16229d5d8e4acd2bafc566693597235df025403eaa871a3481a 390ea6e282fa294f7942ca45449b80d9c724a8a3d5f4a9634a60fe2888470b92 0b0c6258d9676d4294f21400753550fd26cbf0bec23e15df95745a85418a76fb 7461064e53608fe67e317692c2bd45c6ba6418a5bb1cd95444101f0d8d5173b9 00472a2830504da3e2a5bb302e110718b5773cc9e191477003cf0e229bc40e88 9086df79cf86c9f79f706419dd08676d378c0681c486929a2cedc4a1a4c3a07d f57fc6332e01960f904373b2ccfa03cb1336bbd38020a51a5173bd7afad713bb 526b77c07e8950e6bd2105f2710341f3ebe3cbbebdac63741aa8fbc2cefb0a4f 1fc5b33eb17b23849082f2adc739e8cd89717c97815eb2bc41d929ba5eef35d2 66f09fe3c7f1032c1562ab78974b8eb06ab72bd497805f93c3206c1922dd66e0 ce6ffdec66cf0f1881d1a9b7586ba1708bbb2c687d5473fa640bf2349ee80fbf d17d54b8be65c9eda7d06c2833ef3c90fd8c0609925c0111aba1c04c29966d0e 0f6b22c6c9b79e77add532ce84e8d81db961f859e995f924d2e4f9c04b758422 426dbcf019f34fb5c190010782446e3d135c39e59cffbf1ad3761b878d426e3c 17cd05e3edc34f8afe9149d5ccf9e6231201e6d8715b3f2de4541ffdd6d1d6a1 fc00a575f594afdd193d497e125b7fdb059536c4fa535dece734e23fd6afc1e6 ba538f7cfb8db0489276357cf6804ce2367c6ff2fc2c846d7e6fafd19fca350a350b67598610739996e5c8a20b356cdde28d781e4f15b94885af0eb316ffb5029f4a4683a7195e0570cf5e1a5a2f23b3a64f2b2c3834b38d538fbe85595f6b0cf63a21bd1ca28eb7be650e2b6b1063cdb2f97a0f8a989e58c3194b9efbcc870dd3c37aef64d38d46f874199a3868b0799934218473201c4d78408d745aefc20aaebb16d31934c3ad3507ac6668f369ba90674542fa95148e07b1563fd08d59b386d7d54b4b8aa4bb1c990f7e23587ee8e1a801a1b4c5b49e72c854f144cff1029efbd5b988ec215f526750f6b2b3ce768a933505bd79ed49e69f1a11ce78a50ffac65c4306570b0c481dea5f96086e08cbac9917871214d570d857790cdf4ffe4c01270696fc2be8084a8c5b116f69a2c87352e37819c594f455d9d3b55eab0e6188548a4c7ffddd000fa334aefbad00fa8b21622398f847847127d876868106932d394088b5fe02e162cf87e4b6295c1689de79fe2705f3791ca1fc8286860e6b03160019a0234b476bf3e03452a17b72212d2f605fae80c127789667e8300b8620bc8f4adb44dc0200292dde03bd007adf3a0d8aee62ed920f70fb5364c20484a51fb4018105b3c314625aa9775c0a9b6f474b8f4d112e506059db815ba0059d4e2ac31ca4e70d411dbb2aebb61c68ff022ca8edf506ee92829fe43c82a40d5a1f1659e6b57325d7791c982fac0a2e9e9301f0310d39925d2df7ee3267de0607b826c21f6d318ea6f66984dfd4b73663069eca00b4d217d508bbb9154319021111bf83ac7eacc9499f4fe4d13b52b259b681eabb8d3c0e5e3fdc2b52f0c902faa8b12834264293a67b4f2e90a3f82a44244458e2debf954ae0e78293478d0645c91dd9b78e35d36d098a11d5e762867767628b74121f50c70e2457e954610cb39af0e5b04608b79ce9df3fd934913e5101c1f016878b57914801dbf51ed104acdf8cf42ecaa1b0161d3ed8f43467373f3c3f2feccc4668e8bff474b80a71002b6d79c1d74319c5948742cd68508aef8db3f28c0744c6cd1b4d3d097e178e01fadf911ade5d014a7d1d5e6cb3993c3ccd17075b0c067b03f38049bbe7c406004037eb9aa6007abe27d6ba5db90dc5164e3e4c1e07b7855f2bdc3ef8027f6e0ebd7960bbf2048c70f8a6d8412c0c5cddfb24fa3fdad78798d8de87fd71ce310e14558ec0fc9a77f90588aa74dd6c6448882bf57f50632b929f52a63a7853c9083acd604d5385515cc0a0c1e9459bccfb934c12910c725aa0f770315462ff8002b48479ebd31be989fb61f2b934b9ae912a3f56c198c58bd34c11797e3123d006211c47b1d5c19e2297f08bc9e269e0b17b1704169f3dd65a090281992586ba0a2e325cb76f22c52d03859f271cd71b2cc668152198bcf098c4071d1bcd0de10a false -check_ring_signature 7d25bad80d06009b2065258c3262457d829308d3684f061f70e2d1a7e7c57a3b 9d9de2203dd6626dd742eb584092ff60cf553374ac6e9605b5ea1015b2214a6f 91 a02fb8bfbb0c08da15204eb4fb0f9cb392e27c7b6e4559057adf22327097ad53 2ee95fd60685f9922b2a7b26c6fdd3ac0c2f586151ea40d0dac7ad71640ed954 5354a631cfb9ff60312ee42b71476362dc9469762f38c7d2ed2bb0c564d20d1a bdc0804f6050542ce235f74ad134a75c0f4fc1c07fa7a68f4a1cfa5ad8e4d197 7bc6e0841a3c22a2f7ba15ae42376969019623f61867621b76a2b044177fe2a0 fe4b250cce8027b1fe26916f637566a53f0e5de3e71363d05c793fd6fcc6b010 86feda665f3e5fa45f7826d02e5ae09c3dc332cb134c96076b1146b5789f5f81 60abe03058b8b3ed9dc25c0f0658ee6a5f4a80f660a6fe043e2e6934e91190f3 14954011cb0cbc1151ea6191d1112a0e348a56f2ebda9e448fbb6e57577c71cd 7ab7324a81e1e3045dbfb8a952dfd00f921dd9e8e388f07ee711623f605cba72 aefebd1f106210b15d1fbb13f745cfaf21f00145f4aac75758e9962e97eae67e e49fd11f73a067c5e055e695cea0bfab7bde21635a97e5f2d1523a0b9a677e39 14e0e833d9156c054a1b7100c1389d2360651184a18eac61e4e5e8e16fc1f051 a6a76af45376caffbb78023d8bb5adcc350af45a5dfdc2fee02bb24022d4e5e7 f3878c1caf2a913bf4dff7062d3052a5b757de1d1c78b7936c2b925e1ca49cf5 4ad3a632198bb46602c55e93ce8e2d77c91c23c3a20986b3e235dfc55104169a 8b50d7ddfbf1a4fd0aee9bfbac54f482b0ab8b728763e66472afd0c6054821ed ac34c3885d2c03b4967178f81aa1ef4f2e930d7cd3ae2508f8b2db9b19cbf597 e7d344adc26a8968990a6ac82d4e77c7b0cf5691d3d919f2da3516b819450106 9e85ec37bf0232b0a16f53aac291924a1f648bd159cfeda32bcc8258841af59a a57a662026f680ab786ccc9ab004875eea22baee16c6297e378fff9d85abc52e d50b8bbcecee9b2af1fcf1f52bc6fff64927a60202aeb56f5d49bb29a8dd77d9 90f27e809746747e6e3b2ec62933b225beb341ff2f1d4d71042e616f40f50c57 91fb188a87a0ce363b4919094edd2fd6e4b023d9067806af816c5f858411b7f6 366bd6df3b42c8342174ca07eade09216eb3029a40da51edbe0e0dcdee427e74 dd19708f18c440258a3ff8e4df195446cdd8fdc332001cf9611de73087bd88d4 1b8b63308cd5ae08495921411696d40ce68c176d9548d85b954a41cf7034a329 3eb1ad56f4f7b132e883245214398ea1249a253dea5ac161137c8bc682c6b786 5b1c1946ca4955a4b9fd979951499e3d7061116219ecff8287021e146ae616ca bbe312d2fa4a7701b7baadd0e1c7d52351e2028529d337f964210592d7eb631b d80bdec7a4606a18980380b57ee97f45c106c657932d2abed70c3e7940438c13 0d70fd120c713457fa5de9249e737ff8d04c33ecde9df51260374580371ea719 e68eb0bd01628a0f563f8895c7fe6b8a4d78fe627fb2b160b76797c66b8d1a6b 959253fec70793c6347b1a8370ce868ba89da55c5c1775bede9a3d3a60fdfa64 56340b96fd89c69174819a3c6fadfb20bf5b2fd6090e231f6f137165f1bf9d39 fa5962fc4dff1d96eb17ab1149478cffa4751b0f980db1cad59d5f31cd7ae79e 6e100546cec3ee34d6d5ddcdcbfbafcfa379f1ed32531135c0032571abf0ae28 985feb12e367b6ea9577a71eb115c98312717d08423a67753b8ddd9fe44ab0bd 669e81de2a3babe861d1d9763004a44e2fcf2a0c354413648d61d385feb03c07 00e7d7e3fc319cf1ab8d68cdc27f7cc9302e3bde5964fd4f376eb90feebdc296 3a45245defca585f40de78bca1c01625ba6f1976e139e546ec314ac9640f18af fe621749b8057e7b903a96952ac7c6ffca923865209f5f6adf42f22ae7cd6543 3f1d2d67d19e89ecfbd799edc5f97887ef628b3d00fa7c47910ebe672d94a884 c37394a9d2692bb742a3318ee2753552492d6482a79511f622410e684a2d9304 eccd1ed75b8f6b27cda72d1259eedb718a638767fa830fb7bd4dc2282e5abbf3 b391f628deacf4272f35980c922fcee4a288ecfb268a487c48f41ea04f7473b1 26bcc442a1f072a423d261a96065939b6eeb30c5af7be2ed6490e586c3ee0fdd 91f58e9aa07465ba5d1388b2556824c6a3c393df7239527d3572558c5cacbef5 964a6ba8a7f34c7b389be0ef1ebe8fe872a9ddd5dcb5a0f7df22dc837f55ed0d e8e30c458565d3969de6da1c96525f92f5c5950c6a4a3eaa5db57a6a296b67c8 eb709517981611b629aa71a88e5b3e220004842fc88e393c5f49361c42fcf7c4 4020541760741e2998d3f2eae3d07773ab8fe9b106b5354eff3901b963cb95d9 c027624cdd4654372b10dc4885f8e8c1722af0ac5944de57962cc924e9d86cc8 320341943b97e7bd73526ab47db08d2461b156cc847328e12fa147af032543fc 7fc3e1111caa7badce78f1368cab573d71673cec8e0cb0cdb7ba439746422890 1e1305286a190e18312e053504468e495156b8f4ac1096b16db417ca456ed535 02d9829e973f7146e5d322bad2de78c2789a71f88546a5149b9418494456b6f3 9bbae86b463f9ca466c67358f1e99d5930e68c492fae8eb3d9ca77ca8d046b6f b557c902c7d1266b013a6767482fa2d32df94a183ba28554be95ab382849e3c6 57d695a438a1384db0f28fac0755da054d57a7da656e4d71a7d25e18739a2e5f e6d936379361effec64af862ebbd5e735ff455a3551a475ed41926fdaea4f11e 1ffbc0d81249368b8ff7b1547bd42f01a1328d7f1fe5b1613fbca9f9f016f821 3781da804d8cfce0bebb263a6fd2abde713e1e42551d04c0cbe943eea53c4098 13cf02b2ce0ef7d2ef32e6c9160b21d70c30bcc52112ea851ea3bdd42e23923b 37929c09f2889569bc78b584b051be956070ccd3540a96cd49df454fc6b2996c dda51d541dda80e0e4c0d43087c5022b814e93797e673bb245b554e73856ccd4 f95a30584fa057dd74a81341d4424230dcb63ac4973f7e069f392d3167fb21a7 fc8f5aaa66d63c42cf183d09cea1506cd75525f3f363c899a32a17f610e1e454 625c9bbbd215ab1b6aa43876d37e469d09167f2701d319fc555612ace19971c3 574e406d419392221d73ed76479bbe2c73dcebe1ab7575acf56ee4a937edc886 ba68d6b2b8226f7d81222d62cffacb4d31c1eb4e114b42ebfb1b6f61ebd9a6bf 9c063d320dc4e553df5f88a44d83e1954d67cfa9ae1dfadbebb455e84707447b d2cf632016b19b58bcfd862edf64b21fc904c9181dca9c5c4a098cbefb240562 a839c22c34af8b538316e97d8e731fee3d99aff57cc0a12892e907eafa100e10 b645fe2499951c0060c84b7aa08d382ea8654f05ba60d391edafa3d5e71efd12 ad6fdcc6e3fdb0f015037039a9acb6ed2dd3e59202191cb58b61734ddde85a80 eac58a697fd4d857bba8b1814a8455410a5ed51e838fe06b8f086d8612de531e a98e3afe77916ca1e7605bd8b2dad9c438e5ab05dc8c3b509126680a9989e2a5 6b89cffac8d1f7ab52f6d52f95dc0ed39bad1e0c7ed26a0a866b7695d076252c 86083e76874adfef03f3c97140ab04a2c0be83f42d6b5f12a59596f0bf4de34d 0557a4097aa25dceac984d72c032c6773a7280d5538383af4194ef99ec15a2e4 f5623c3a41bb595ac7d9dca17b561af9f04f8cde77ba5e5ae29574a0596f936b 41e99d1dae5ca9dd8c418925fe35831d3c258d7474fb0e0da27791264aaa21e7 be9508e8c39acd38f44e894d60ba3410b7782b3e1e0457718bf924764cf5d6d6 0bf74216a6401bd41c6471511c677af0b26e14c8602d6fec0d5fbf09ca6d64e6 e20d1ff591d8a7ac515e4c11121319296cbe1299202e80b81e0c5a2fbd7dd5ab c243fd95c27df8eff688a6d99bf97998995e370ea9a13959cbb5cf324322c5ab c31cb7635f8e5f9f5fdca35d49a4e8cd39bf1897ba8e73a02eeb15c06d02190a 91389c0fa6cda22617b9f47584684a48d4bad89a47dee29213cbf9828129e6dc 683f86d6ddda5c611fb74abab40716ee6212c3b7b3c9075e8a05cd7511fab850 40e043299be5259c5ea08d94f6e078010a11b4630d21d0bcd82e5711e6034aae 9c6a627bd46c2b22207471f1c133176996847783276ecd8e1712bc29be67670463bbe7224c2466e14cd0c419c70a34a1be524c0ebd79393fe6ddf7f4114b960c41954ee30961a76f8c337717fcd2e832f98f13255e84730e61862da0f7299d01960fbf2eb280f21fa1023faf6e9cef5ab232abd7d43627f830e6e146bb43ec0e12e627248867dcac11505dcd3ece9130104474faeb3652a07977ba3290abef073a5c47930552bf0c0822df9194faf2b78b829e98c548808596f62deec824510aa753fa8fab549c11f9655ff09adc37db08164c699d77c2e7f8c94a137b0b1e0c94b6eb83d3b1b4007c0a3ad42a48424f773559301f93695ccbe6e962ed542e0604fcabc051e1783bf8012ceee629b4bb6cab40d99737199ad7cd7b5004411b025adcd1ba683fc6c586fabf77b6fcc65c42c02010b46a2d1f4025a94113895b0b6d3f738df655cb21b28e6e45446cc6cc5178f9604b7bc5d4bf7b24df14516902b087110b6f60bbaeb54dc3978914fe91a182f746e58cf3bfd83a98694da2020545c30d1eedbe2f54308f422c6279f85bce206415730eb45e15bb279f0333090e03529040fbb23afa39da52c46c89986493d29d4d429155134ccead23eb228b0b6e91237daaf1fa60c08cecd47dc37d41f404a451e496e61057479ade7e05b303f641cb269e325286fa7f11df1d961b87f90b15b93929cb537b68501383569403d5dd97fdc24049e0225b8e58a0974b25b77bf1cd243f6f77993f62cf4e629f093f598521ab7ebce2f8042070f57df854842ca249b7a7f8bb49e4f0c8121251005fb0afa4250827a45526275af0130b6ce7b6e5f02baf3b38c760407d48cf7a0793fb4af93a54128a550f1d23392f666119494bfe3384b7422bed006426f0d50874a2f3f6fe27e2c6de9ed7294fb06a468c423852a84eb9256afc0ee15da09308293b692bf6f5687f5e5900f599d88c5f40c214a72440b79b897bfb5ba9c6cd030d21a181e770ea082be91deb0235c4118e017fcd6288aca7575244bc922896e2e5302656f734c253179af23a128c14396dc62bc5d9fe0f823209913f99b4d90fd5c23631199cc23c57b99f2d2e0248b4eac993390ab38cd90ae7e8fa3a6393018823a30aa0c5e527feecb8ae8a7ffc1735e162291fe87c232147d11cc673580defe9da2b75a07d019b1ce5f66bfeafd12c8b8c2dc44d14268820ea3719329609909f2be384c82832dbd25da6cf295415290962d115b3aaefc916c99fb7e06a025a707bdc7cc580f7bc9ee74eed9949c679f5552f302502c14fc8290faebba106a4ce1a1be10660234ea715319bc7e835ea0108afaf59f89967dd1bc1ab7a5a0cbbf56bffde519f26c3fbb4ea0ab5011f6a8e7330730aa2a5f85432b719b5e307d19d54e974a0cbd794bc5049f29ca29063fcd6d31599085869de2861a32725011279aa216506769475cbf0e1bd039d47285b2af6bf51a0bf2930f31a769c8f006b3908e8de1dd2e86e1d90c8b35dd6c445cf1dd510fe40e9454674970696190c8a12503eec93088375170d760fa01ab0d0ecff0534ec4041c395101a79eff80b02f0f8f6cac7463ef54535c23d7a43d688f74f6d8c618f4aeb243d2a87434700f7b5e80136465eaafd31c372393a96511019130855732ac421c7f323cbfd2a0c321fccc6993f9a1d4dc2aba112b3eb8d43c05df5193d0c18e1a3f423019e7902a2211fd87f12953c5f72e1c70bfcf61092a7a5406f91aa1f6dad46cc84ea6103b15429bbb84031a7b4bf323ede80025684cb702cd520cf1c30132ede770d2f0774d0a5b2485f2822666ebf15be217b29bf8a5219e4fbad5fe46779b3de888a052ad920ee4f1e7f8b92486e7b8c5414236452315e261065a24f96444f2e050b0901d9c99ec776f8365da950775fd35e6366e67d5d1285db95963c430c435c1e0d11248c5df347564426c7d2bd5789dbd357c92715ec3be27c160edd729f5b51051b4d15cf9b81bea1103ae62e6d2a68575aa9a8f50af9bb511d916f4a2cdc750195b1e6d8c1e6ed17f41e22418ad5a88ba07d3fec25a23374fbae0683dededa02cc5ed32508633b1f7fe31998ff26f1278f9fa6d49e32c5e26e1c0257a301af01b3841dbeb4a4d62da84041836cd4a2a55c1af95376f78b444d0b0d7423f57e0e26570ca5d4672e016af06334f9ea73d9b83c380b8dc50e7badad39bb38f41a0fc57cad2d373b16bc9f81add1d1c6fb7081f8e4ef61eceed9e094ead3a2430903b435a46b7443a27b028ec9f71840a9895e765e47c01611f8bf327035ce90b30b0cd26619d04eb637a40d5bc9a2704baba67ae81189435636de2cd5a17289920778f64c36d64733ceaa695dd420cfabc23124011f365b05166d147b0311f808018bdc6357d10332090dd73969eb31e2c79c52f7e5d0b62bc275273dd6a2bb160fcc8c5a9868b077471c011491078d79a9a5065b3b88511733cd8fe706bbcb31057c0e2f8dc1dc12d58f7995aede9603f46b31fce34e961f9558c4fc56ed3cb40da3a6a4e8e9177a200b0edfffed454e6b4609b28f1c89f0733bb01dbde6f1770dbb4a038ccb78b3e07e305974117dbd95c6eba29fc271c26e2b9ac292b4eb7c0f417d83f9ebc63b6ea3cd550b389db2b4dcbdb35e7df54516949cd20b090da803f6ddd8a759e2f7ccee422fe4883bba5d505ee86e7c8b489e09e3b16ea85ecc05b1319682b7c6dea06f549a7cd73314fd613aba7507a779b895d168ae9b383b01064f120b76d9dd8d7ee232f4666d76255ad4f3bd0af41042db55cae42ed80e05903dac35d0913b127ad2d4d5f26a0a898f161ef3919db559895d4214f2bde6002fdd8f3f131c451e7883306a57e7f1e0b3445ce2d9799cf3abbe32bdd0ff9f028dccd68973eb2ac5eaf292620d10d76c30ca8f9bb8a9a4a79d582389ae78160155334204976050e8e7defaa5da3529f23babac5f2feaa4aa038905554242370480a895a21138880c852eaaeb02418d5380e085ac35e6704f50ed7035ba37dc00192ee363a4201363720d0afb8501f4a8a5c6681930029a994918a58cf21fca0b9bd856f163bb051f132a2c0df35d295df8a31e151f6dcd60493a2baf22b4ab03a9da26d2b67a9c814ba5ec869250cceaf2823b933d97bbaca1d52e5024f54104efc143ee82b9864346424a7ead3c626fdabd67578b95ce188697b77d7c2b4904543e0548de488edbbc7963c49eca6c3f969902e15e603bff0b0b62d170568d00a167c9d7aa8ada7fdb78f35aeb770096bc2fa93bd7db614577faaf787b0f82012dea486ed6951297382130129af0dcdcb195ca5dfd0d7b605b124985a57e690f6b15a5b889f9a099396102c821d4f25728157d39250d13a998aaf4908e32e509336e6a287ea15b33148a0f75d46125d267d0123c73697c4b25a541dcef016c015cc968c22c55a34e6465bdd8c59ac1e281af3aab86aec23aaab502b751ffb903b704460075109c0f74a90ba28b5fe06a6d260003e96c10ea8db86b3d87f2e40e2154dfd4d256c13b1db369932475762e980b9a5b6e895e4a59e6d042c5e8d10e733742fbcc34c7d85092181e1780cdcffe86af1ee85d6ed42ccc0facf447c9042e62f10892396f6613f54a735d5e7fb1452cd0c5441dd157a5643321f74798017df549cfca549fc4b52ff14f6b24ffdbad507f551611faa6b130aa9069b0950c4d1d1498670b56c2c4e06d463076350bf2c5fe3b46cfe63e771678090ef6cb0c3c1e5f2cc73ce16eb5be8a07640a50c922687d83d7f7e97c1422b16f9bce5b0cb9113c53f5c47da7720277fb53ba157b2049629f88ab4ce0af798498168df805e0529c523b22ed70f71bf0bb8198edf6cdef7520eabdb063f22e73142a8c5409dabc5dba9ae43f73fcd74639034162252b1252b877bc04331bc556a68ee0cc0b13390e2205af425ccf8c1730b13a046ac1b0be83bcd7df8dbd41447694b89203b4e3168161b7ba608804e40418a8831106b421e6744767155c5025c475bc560c8ca54346b791cfefa4888134fb449975b67f56012fc78ed21f786f5a4e8ec70eb350efc61f35576587abd339079e3630fa5c8671555ad325047c49c6c6a2500ad2e32424e73c9bb8360521116bba02cc59b5a8287edd4fb66d998467c822610694e3c492a9ca2f1ab0d95a84d79d3b0ec21f1c054f1a91ae8ea1d8e34415290e6285c8ecb9f54c4a38344ff3c0c57d0025d300e864d565a3b57ee18a905337073cc3b05881baaee679648de581fee293000cdb44abef4d1aea1ff55ae11814050a414a6bce0bb1664f6c011c6b3d47d794f075184e5d08849af8dea0f80626067a1a3bedc95cc767b451b0cffbe65e8cd6e7e8672974d6ab596b896091af5e0144570e584aaa2d7d562df0a1780fb461cb35eb45ab10d7be94f2eb11071aef0f1cac758a331c725830882b1911d4c3898e33ace5f8e039e3ecd46819001381027646cd268fc5e8e4b88e24d2e1d65fcb3257986f0890beaff71478c0f90cb5049b02b15894a148da1971ecccb2818a6eb4b1fa477aaf48490df59c0179b50e09b773a1f507828395f0eed59ab57b1bd3e5fc6b411c93c87a7209e125223e0704737ea7cb8ec78a79d7b53e373340bb3b7b33fc0fe2198ac50d2369e5b79eb80f8fed48f2b66d99718af9cb0c713da582bc42797f03a8212de5cf4ca60e32180f696f3cf0f66f2b39c29b2ccf923c64c2562d248b8eb0e8b7150630f34d4f9e04562dad28ca3d69375d7d114e00a13ea285c224cd813a7dea4ffa0d47c05a1301537daa0fa007bca3b2072e12acf3f2d46524c7e6ff61c136a71af38c6150c50617103b6e4d776fc52685cbae097b222dd0216dd64873a472127129787524dc038cacb1dcfc9be340325ce2871141eb51bfac314ecbf103e85e97bd964d78cd0d0f2afbcb1168c7d51eb3a44f2580343fb256d3da5d11d47de12fe27cd4530104c70db09f3491a2a7a3dd5c5607996cdf0570daadd99effb899ea15081986540c2e989a07d3dd844fc43ead0203d2cf867f87b95d2be530de119699162566930e3410b7efe68b00907e14dde5fcc8c043c1604e677c2c93aadcfbe04ad5fac400dd796f57185594ad0864c9f6af8d0ca86d52939095390bacdbb023b8b8ef70013882b42f8835baa93911ef2dc5ae2351e5aacef8707aac99d42eb2ace9399d0fcf617c7b39ef7bdcc5bd0737797d8736e6150bab80af5c5c6966c68c0981d60c011e0af47d7e6f6597c7b4c761f1b5d7b3dc3cd098e13d91a8d0fef8286d960bd0b2ca5ab06f22bea50f3eb14403466bd8762bbdbc1b171895ae3a65aeb2a10592d11fe1a2fcb0b12cdcf5de0fd1070b07dfdc8e411df4d5750974ee09332e04cf8c849c9b13d1df2fab0c7dcc16e8195609944241ac4edf3c1cf5bbb139690cb43f84695e37addb27e9be0b0a33d8314cd99e11e38d57303dcac38736aed10fb9245a014afa24682a3a1ccd062bd727aabc0235b60ae206fefac81378d930053731a8f21d9c113be2f70c6b017008cd2e5a58cd7343b9035eb7653f41291c0a75ae1306f9dafc7764dc6c7e373d5ec1aff3fee882acc13e31c2baa0d36edb091e0040c037b0288c3d528bd0fa80cd62dcb8b77ebbe35e46c0114ee5329c3f01f3056809abc6858feec6eac9070793c0eca8df60a2c9af9f335aadaf6ee2610fcd304a9c04f532c7fe572fd368773a88dfb9df6297f9e8d3f4d4ee5eed9bdf04152c4ad8daaaa5619c9d618ad48f78064b7ab8add2e3db75b382f57101b607014197aed8b8902e2d7bcce6523c0b27b003af9e78202356fe7e23a1ccbff86e0793696c5f4740fa019a622928912b292e945ff0a97323d0a1d53463a2ecd6630688b1d1653edd9d17ecbcb4679d4b96be2931e8a5023ebcd1232e492b5b1d9807b676e9b8ba96eaabfd7c22e45cc91a79a8c72df5da8c3adcca80054cf75d5f0a79d56fede520a2b512bb627e595806a6aebbc03a6d62a4880f98d792a984150c33b9fbc775941297a4c884a5e9b89f24c2a8d4b45b379766711e649a206f240421329ce2d6f2b31b56216748bd6345a56da2e90a218d3cd4e9024241f4f15a02b0ea3221f13908709bea98b1ce4cb80f284af2f7f349632641cc2ec1f991560145fb79f242733708cda4c72447851b91a5e549cd38294032255385437bb08402b1fb5b927bc78b91702ede32c3425cc8f16587496476f815c23e53486fa2540681f57c9caa5d5209fd5323a8435daee2b58106c96cd28ba53943169f95f6b605c39d7e0667b16c5e0c5237df1b4b4825d482911a7053a18a6dd85698d7158000cfa35bcb4227da14ddaf4731e4ebbdc01493c4c26db6aea05ddb7ed20b889e0af68f9d74ce9fecf56cb690d7ba02a1185b78e8d00c711a0e8c4149f067a7a402fe6631c258faab0ed34d2e496b6454fd40ef32254ded4cf18813146e3fa6d1098acf4c24cbdf4ae5c5905446f7432317a20ec004ca8142cf3b06b3a00578530f3f276123b931e5f6a77e62d99e234147801f293046de5cad8223ad4a426e2809a33c57be4a5890967602ae3a538923cef3b9bbd1c19e6ba6331d738cd913ed0d087382fdb1f5c811b10d130e598e8d41ab7ddd0c5f9d3dade8619a9b9276dd01baa73fff5b89eccdc4e8d4fd713e6e6edf1631efe6698fa1a62e9525eebf30081c723e061027c7c93c6e7cc9040fca4375008e0140efa800ccd9a9824450e602cefadffb6491eaa34388ddc435adc147eea3f7ed4a02a6c8d344754e2250c501aafb5c51d4776b09f232bd7d311abceb8e3e6a470040ec78d150b6ab873c050d480ce4442fb1a75eea2f03a1ed04387f4c614d8dbe206c3df7d56930a2cb1b03cad5138a1c1363ef0042d7332cd90a44d86674024966b13e7cbbc7265701b303ea089fae669bb0ebadb7eff7d38a4d6e1037b57fab8633d8cc5ba6efeb3cc90c7112337a43482c888ebbc4da6a0f428e44bdda2eac603aaacc3e261eb3f314024b36800c3bf94e20139ffc300a869751e2f141cbe0946c095332a25bc39d510aa1b138d7092d9188c5cafb60c55394ce2fc90bb2144f722abe4d345a9e737503692944cd41f8991726b021e32095379ff80447bf03262385a9f203d29564130aca07dba90e6dd5e96f0b413ea4c625c89f892a1a5a7aa58c691b961c33e6b901164ee88520cbfcec36689d3b8751b0aabd8706a43e8c7d3a1cd83b72ece32e023f8ed3c4861499c8c2d67327b81307eb5287dbe405d14fd1fe496aa904d8250b7bbe17ac20c40eaadf59054c5673f0bb3d8c95f9b97db09f6d22317cb1d6e0051cd923ef05b69ef858c05f5e36287c3fba8d77b167d3a370e2175c72359e4d0caad07e77729090b304a3595ec8732cde7f0f7a2ba58e9619460169191e725a0a74604b027c8ecb7c5f6d32e3369a9ebcfffc2393f2a65cdb3ec9919724d0d00392a295ff6bf61c69e2227c7bb2f0c52da020715dc7f39a37f9b6751bcb79eb03f9ff8b92d103d92daab885cfc872e355dd0b022105ac9c88efa18c35d7bde604ada943bab4e92d91517de9c180b87bce3c0f67089b9bc2e725b3ddeb9a34b003b663871774299ef5beffade0a2c5e1dac5eb880e7bc79080441785e111eb32078903b8dcbf35163186dc4b4bc97aee1d3e961115269890bc5356b754177a840ea1d11afe67b50bf4112ead2d20ebee42c488d4e995cb962d782da35d18276900c7db6c890141a1db4e2e211eaef0c29650e967887cf79ca413729343099d870add18b9ee8f5ef4d715da74386d0b54b340c04f4546a2b3364e52842d27411406b90af16ca948065ebbcef704b5be0b8d0d4def4fad91a947e44e55e6225169081f951f7415631d8aafce51628c3dfc0027eacc05da30a6c6c40b86218f44660617df1ad826d9546cc1570138a9f8def361d3a744a21797dde80123dc1eb3990312a096a6c5bb12cabbdeefaf8c81b78a1ce37893ef2266f5fa33a31bdbd28109dda0e22b8e80c08e503739571b6d952320282252f1823f3612a970517aea380cac0e2dd3ad15c610a27dfbec0316b3d1e3bd3f115e17df9f7cb296eca9d5e80dedea490777c8a1d7a7f4bc98e67c60018393a28eaeafc941f100712a9c5baa074165f29a4891ab8ee208ea7b302ce42bf0513ac70c619a7f46495c6de50bd80257bfaa3df90beaa9e83a260ca479bf2c34375fd5b24b17078236d31e5eb37a05 false -check_ring_signature 8b12ba376980d53aa0ade70a284c601f8211fc928a097f2e6e11773b1f4aa938 e20904bdc6499997e09e99c40d4ed220b22d8a03698e8dd533079dc6358a3b94 2 425d85bcdee799d849b9bed60bebbeab16f70df53fa8c4145b6a3579ad9908cc ff26cee7e9a20cec99b7dc2a90a6cd7486726391b5be42bf0f309e2f16dbb4bd b2d03609b4853696538b01863d89ac038d9cebb4e2fe536fd45414f0b467c904d0e97c432f2eecad112d77237fa7198be2a6f428614251f1ece8a28bafdee80afaa8353bcec5722d58b49624e27f250766429708e1741c11431657e4c0826f0156846ace3ff16076c802b047782fea7d8a04c7759fccd19f6b16e2955469a50e true -check_ring_signature 432fdc81eff2f6dacf18eb3a19afc88fa9759ee7f7ef237059e2bbfdd8de39ac 645908a558fac8da7eb1f58395a7b0ad1765c8561377a3b76b6967146cfa7ae1 1 2a5c52acfa8f6a8485b76278fa319ec83c0ddb8241629089c0c885342dff8cc5 1b71ac657e6d56933c55484121601f2b5448294829e842711358f3bda95a0d3643def5371e4e177f6ccbd1068e87d402fcbded074521f4926ffb3373073db672 false -check_ring_signature 78f8311a5b613a26a9a0d147b2119a438b7084b444098ea33758cb92f0d3d7d9 37fe511102a6523b32833e4860b97b35a3c79f9806311243374f63dc080f10a2 10 fb80c2d93fe824fbff9422e872326807ced5760211ab74315ed6537820fa4032 d383678030877970224979cc98ebae53181db29963d08d29bd455ff3ecf5fc77 aa0c44ff7b0fe9b2a5e2336d42de11de51ea8d94a5c0b23cbd9f3a4941d294f9 abcb04401b480f852dd735781c64b12dc24fc4b6ad95ce16dff5d20571eb9d11 0ae0d73594f696f057ffcc2080ebc2e382e20b72560b4207ce3af916eb5c5365 365fad4e0434fe44668a04d92b94bcfc98623d5e7e5d8b7f82216363e1fa6a1d 9589895e40d8111522df521422752d219f75a79da6965664c48ebfe57e916a4b ccdbd1ce8640711d398b515c31885481713cca90562434529306691efb6785fd b7a7758c741bea4cd53e374dbb642d25da90a8bd745c02eb0c3913ff87f12b96 8f43ba4fb2033ceb507cb1c0be5004437889c3e2df5cd39d5785079553094793 c51c3f6d1d08ddf9fef7a9f5ac74a66cfe44c0f369f77055d958b5d16bafc80e31f18e7b0ee7482fd7b69c6f6e337108b857d26a529e9978c0826f760752e1014469d8555f67da85885c103195e431549d2d7b7988c77e0cbd78669c546acf051d14d667da4723f5a8f6fb570487c61cecc0131db17889092d140eed93eccc0f8e20916b27ba93ce6c225827299770b2d2c7a6fd5cff322626b425dcde3a1603ee7fe84bc953e5ee993e6c119173ba2aae5525c753419ddfffa0b99d6513ce0356e5e032adadfa0af080a9e84086abf99cafb0242146ee0338231da16d6c5405772eed84a21b37437aa504af23c63084cde257cc00c278baf8774aee2fc2c400ccf1c4aa1b70babd842740bb0604989774a9a35e5b1b4c10b9bed7807c42d90109bb810e2e6ec9b061821ef42ae4ac9491f872bf5f2668a39681bd2cf36f7e05e0b945dd6da594790fa1f049c8bf5407f7c4411c04f691f9755b714c798c0f017f90cd090f69ff222ec5e6c8972c4ff4d66059a0eb1e5babbac74c3120873504b360c705028f2db106806fcf305b512ba5efe94c92e38770a63d9b5937ad4d053dc175197c4bbc9989da568d68459406bf3f663e9d694791d7f57e5a3dc3970407d88f253c1b9d91263c7343058c7c62388c6acacba399587527ba00baeb3b01799d8e4f2329ea65bf6276d3c9079252e8a709d4571781bb7cee5df7178d82094d33cc2166253083d668494f99145e93b12e8784659c7639b50e4072a9f442046ad6bbfb04711e9298c397ff412816122755bf2328e156b41f46c3e4304b940b26c85d6cae309d2821bc924f80ff3cddcfe740fcd115d451d3122270961da8054770333e0579aa2fe7345aca1538768810a14a2f6491ae1a3c79a10022020401 true -check_ring_signature 04237bad03d745e05778f16e14b10a48cd1e060ebd54903cc5bcca1f8014a3c1 872a0848e03be8efe227bc117b4b4075a8b9bd04506a7c0f8745d6ad8814945c 210 05906a7d86ad7e0e7ce78aa6fb31880ee11f7737842dd409a0c75f0c5a355495 27a882c3f2bb4f0baf6150f2604a8e0a2e064850352c400fdefb1a8e52ba0f28 be4226ff0e9287452fe7dfe0743006e3fbd4db4abf909cf53cb490f7cd475688 62aa3a8e3d81959d910faed327b844ca7e89e5f4e2cf91bc0635ba7653483114 21d28a23fac979629b31f50cc7aa1af07fa8fc884917125fc7908a75fe37a894 f5b9d5ea4266b86e158e717f0692d6dcea89846fa22c0e9de73bd88add07958b b4eb569f050e1d6058f8f2888e3dc0817887b2a4667b7fffc3b645f1f0267d5c 231053feddbcb655064bf403c252e89ca85000e3c726f73ab6fd6c9ccc9ca7d0 879fd149198033a3815776098c8b03137b2e1202c117b0557302cd9ebd754ed5 06ef427d1ef4d8763b4a194c97318b10bedecb922fd57a63b7dc55f6dc4a2103 e3194f40675ced0d33e9fba0871ee35504cd143cc0af80c25bc18d3c6df26477 0b4d9e2e7602593efa434b355be82a86fd0dcebc8b5a4197784c8b866ea3f004 93a17b1df618c143bf19a2c864c18452baef375c5cf4054712d600916a155dc9 ac0372260a5917d7e69d62eb83c73244cdd9f200e55a7ea22e1036425c363f59 1a32849f9f985940e0c1a24a3f6c7e331a343a4bd3462d06c9cd65be7c467100 bb33566f10e8a150a3077a47c56cdcadd514bb2b2673570019b8a30025a45c1e f7c8d277ed19f2c8cbb181647f8434674d3e598e0db319d189cb6f597ee06b9c 1d20c86c2a44c90348c4ec0e5e94c168815fa09766ef23d4444be8d6c2aa224f 6df7c806be112bd229d08cfe075faccb4865e518d6deeb7d6d682ae28e3e840d a88f2b456911dace4db31984f6c77f7f194ef2960ed52fbd823fd26d68a096a3 2c8899651454f542524615f2d723c0d360b41b531766d2b1cdaf2a8c847a0f32 57e81e54bd73a309921612152328849cd2fc2dadbfe8b1c34dcf5a455bb48919 b22af9c56145846f562ae8c87e0d09dad62860998c67099a53f1dd0994da0657 d9c107f9d0087d81092cc81ea410b1bb6f55c3d0f3e353c973868fdc87ed522d dc4b00434591e0ec1b3585251610a1330e84b61a1aea0ad083b74262a5109135 4ba9ca5589227390fca21a15252842c306f8c3b9ec494ebebc9a0b8ee82c0085 cf4e2936e2d58ae6594945f863e3af87030240f98a0619e53ba3652e1058c996 6d68d5072dfe8b074aeb3128b059ddda2f52b8387fa71075685ad8b961c3642a f60eb1f485b002c1ecbe73d45f3cbfe1efa51eb3e0ab554be3801d7dbdbeac6f 9e786f50ac062ac290be1f3c3d318e00dce0f28de1a49b5437f5726273608fd8 b6c3a089fba3cf7d5fc4075a34e2f27ee6eb03090dadd597dca6b2677b16b737 d888da9eaa043d70eb8afb5435033ad3734cff99ba728dbfdf35c3d5e4d0048c 05853f3b8b85e3887387ba159495f155e47ebbfb1036bfe484edfea3c65c66b2 9b3a1641b38785533a844eaa9ebad3694443acbc75fa69a09908b0597cda66d4 68514512f46624d561a9158eff5e4a72da751892129d98f85e4cbc804f617786 ec2a0e09d455c4f24e721c385be0b35a2102c14cf45c475ef2570cdfe73c9809 346f789f6a77e44e1d3fdf4fb91c80584c4eaeb0288f234874becb5d14a70477 354e54afe912da336a0db9eb230668de8b72bf432af683f8d87704bda65c8b19 731b5df100c0b48389b7177bba743431beb08d26ee09f9d0943ebd22cfa448f9 95d1e3b9b893b43149f73c54922dcc97f731f103ec6e5dec55278b88622e2d28 2e217c4b2771f9996e702ae5c64ed1352937d325bc353c7626ceb7b9337ea781 742563da3a9f009539270aef69d5105316db2c599eb16b4f5539403e0dd439ef 558a0ea7a058a513bde73055574bf66e77915e4b6afcf66734a78cffcbfa4c9e 33ebaf9812a098090f7a46b690027699414e5e0ad82a9c7f63665baff8e8a233 72b1490e36eeab0ae34514c0d2ede72a4361c9708b3276dd6155686bf34f6cb9 a52c7ed58a150345827456f062a5c7753cea0c50022f8c5a2cd4bb1c53131cf6 5ee59d958f69e6dde96a5d018185cbd80b5265a7e0f7ab5c7d80a058758ad4c7 a5715b1fdf16c9f07610bbd52e4f90b5069caa6e2aaf3c8139305b15609b789f a4e9a74a325aa3c23e238f0da171484edc06c78f924347183f504e4fd5614475 0ef7ec71bcad7b48e1a9925f42e6dd62f2a7e0aad4414d8ba9c9046eb6b396a9 e377e3745b79fcf93265dcc44572d19fad791da87f152a35304bba5e902e670a 049fd52a636f4001950cc08d6e725dbd00596106c1c819a1add19b23d626ac75 353dc849267cbc3a51042224f563d813e27b9e251d692253316ce955dc91cb17 6a54693a6ecc83d26047a3276aeb4b0c685626104ebaca48a950563588ae0408 a2fea0578086cd4599ca7829baa4dbb6da7a18494a3dfdcd6c8ea14ce452d08e aa056f920595e8ac377b62a51d2cf709d5a41d40e7dbe3dbae0aba2bd8a233a9 77934a0376436e73c79bd56f130adbd9cc6f5123e885294cf47bc1dd3c414fc7 bfbaa3ecc649004bbb15cc7debbd46816323bfe0105b3791e0e57386ab487745 0db947e59dd3a8d40e44288cec4e625eccb3cc971e0bac45f42800b1b936cec8 521a51baff9d302a581fdc8b6b2a082ce168506443cc80a62ca69f14a4a0b11c 76e10ba2190ee077a12fc79b6edaeb33c7bded50db3086da134bea03c40db8f8 f37b234f26035226f260ff57ad8f84f200909a5cd110869d6005e0035813a55d 51fb1a164f3a7c2821691e2eb24c2f0955c1c740559897b83d08ec32e800632a 2bc61522cfb276c335d8927d1c6b8286618b00ab24526fe802ded4d3bbde53c1 67fc357d58c8d43662c0874f33b21353c19a69567399ea52445b1849b913a909 48b52f16b8b3981b540488f5f6acfb581d294450d65be14e65e58dbbf60253ff 1320a2c1328db73d7be68bee141437bf677500185e6fc3e929dc3f9dab95419f b18460dcafff16da86f75762e218c462d9289e8d6b2deee0a09be83e6705a3db 90d39d4010268989663f3d1c9f299def738e997a0b7079050369a5b53526c4ec fec94f51224c6e831b887efdaf6d5870119013a2fc5be56f27d7ed38bb106b7c 00dec43d73b11b205b4f87b477c5a83c710f3c24acb593e867fb0c5daf4464ba 2ef9b0b6244bc2b13027ede7831fdd68c81770a4deb8514014bd67838a80d9fa 6894fa92a0f7664270334fc97ec1bf01cfe6b0095f335e616c01d02b32ca9d48 067a8e3008725671deb5d7ddc84019b94a9551dfb73bd641b2ee9d871fce2705 1b99e13740cfcdd7a6bb8209add555fec9a81a7afb06d748713dae825267f76d 863b8e2648a9608e277ef5707e06728b7c8b79de62f548d37bd77a39202b8a36 0db33436281202e971b9396c931144a74689c6433bf55bccf33c6041fda7ec95 4a9a8ab4a6f29c183fdbd5a80d811e8aca72d00ff2de29dd708c39c761f2fe36 13be0aecc992e4f22fe4d7c7992e1752d33cc734ad259037639d6d32a3047ee7 049f80fee9feeae6580317313af61e626a33d53d878eb567ac2d9bfbae102248 5ac30e124fcf168aadd04ff3b7fca5621acd92d5b6f06936c1e616937da8d331 1dd7d925c3797a072f83836330b32d1e7c6b52778cdc92f72c5630a44e91220d d66b2c6be6b5aa0f1a08a6792ad38142fd2bcc51f361715b952d13067dab1f9c 6d381e22a2726f8020e97cde4335eb2c56527a9d11e28655c2b4b82cddbca3d7 6bfcb61c388715ac72cdab95cb33a7af0ca1cd9fb5366cc6dd93f4fe468d132b 8b2454e566e21ba66d1ca980fd00645b4bdcfe0c2a84c0af2533e2dafbdacea5 8459126df9d9ceb0300f76c7595cc5a8bd6471d878edd7f8b384d0d1fd0fab5d 8cbb4f1879dff562fb19c5cb019ad8c23f8defb7e0227e406ad4053f1dc32755 f43a6fb903cb42adae1324ea7994d73a1b3d5a5042ed73edc212741077d24766 243817fe1658b55a49c8ed7476b495025efc7291c96d4d7ba183b484d4ba7e99 875e114d8b496e30dad537fb9f62a4c890a2e1a84b774babeed789fc25f73c9a 9dde7100e86ef6ab7f69c183400a70d43e373b75403397e39cfb110c03f9c6a4 333915296becf857cc1680394b7a3729b751bb6a8278003f44e4235e7f10e439 6f0f0c32c9de9dbb47b0218df572b90f9f96ff5f0d4137d85cbc6c9b176e4d15 364401fa3266e249799cc402b5d97f72564793739fb2680bfe2b640b6c96be9e b89a2d9b796551a88aa683db8be2088db68c48ab18bbee36805b7b88cd5e2c3d 272dec6c03cc42a9b815b95c943e6fde71cc503822c788ebae9af0967538eeee 6191c9ad0f74f8cd8b89d34802039d3508783dbe2f79ddf5a8228ee2f926c8b9 0133e73a1990987e64f84b6c1a48362107c3d4a2c3cf3891a754bdd97fe9c42e 9eeda660ac5a7aa58df2f8a0e6e41737f740a49500fcb719147e0c2c45879390 31e115ff5344b76d4b61beb667c92630089ad645964c7ef0417a3185225a6254 d57d591fc4721f3813ab14ef59ff20352fd5a377a09f79154d723070471d6216 6e71c41144008da8c2967c77960e3cd50fcd46dcb630f771c70d4c69f035e58c ba5639331417c469cdffdc2edd76d89ea8daa8b06de36096d20cd2426e58a52c 9a0e48b9d72bb4f8ba1158572ff086209dc2c8f1fc78bc73064fe37213b27eb6 7434614a6db85b35b9d58ca14348330ec4f5d4af8bd17e6ce127d8dece89dca5 6e727ee9cf224501a33f4f47e6a4451c5aa6bf929a37df985ed701952a81af30 77782a0fb3abc39148edb56d7d4b537a227d41915e6891d2f16b2972a0e70058 14f5cb8b60e2f8094e3047b9f9c308b10f0a24e5af7af16f64bdc4c3c77bf3d8 dd20d46df69e0c2f5016ed036e03b349a90f01c084989e726880f8361d5cae84 e851a82b24a7b0ca3878a12482fcb1f60ee6ce4f4cd47597bf9cd22f5fb1e568 2b7d5a1e872724783d86da998b1fd93d4193d0ce19177cc71ddae5d760635fee 71d44808af76e22dcdf3a16bc1877422a5fc70517fa198bf0f155bc8c5fced4e 2e6b8bafbd14fc9b19fb0919e3e305439e84f499b9a14ffb543475b5350e6a7d 821bc4600e1d2a5c7a14d67265bee4b030fd2ae58d87eb754ae8ed27e682ba9d a8fa817583d67f918e99ae69e8552435d347fe8a6b52cd806819f882526699ac fd4fe251ede77348949a17419369c58cbcb991382bc23f5aa36f5f19b2810aeb c8d6daf1c7ab0123cd8583eded55a897c7cd36a5b8bea937002c64ec2c1883b0 c36e1b5d1c21febf5dce40b32bfcbe6327de0a38918fff2d620e6caf51ebdcb9 9e88fb0d78ddf334079af4ddf23230870dc395823e4bce47209f79ce6b477940 7fdfb9f6ea3c8c58fb633d31016df64d5ff69f455f83e2f867bb13ea1354feb0 104decc7db38774a234d12051fc3caeb468a8e75cd71bcfd0d26cf0c67c35951 82176b3e7d5f0fadf612c423ccd0ed4d2ce649efaba968e4df54b3849d0d17f1 9e6827d1ca0e143cd389c00ee002b3feaca98b06664c1ae31911bc764ce02071 b9f0f644fdae5fdac3d826cf50855c20a7ebee5cc9695244423b95aa975cc751 0fbddb9441a90b7c07d050ccfbc925a23119fc6799eae55fedea3eb0b5aabf6a adde6f441d16751dcee18d4a91e6754d0826f8a01f8bc7484e64f041b3b02d48 dcfcf0985e62c6c466adc2600953e08255a7a00fe9becca70876669ffe13bf92 0f448e104a62db888bfa6e8ec59b05495e8941df5888c1b9b5d1e23e368e1bba 213f423365288408cc33c05b704faf223648c64a1be8fee5922d2e570c534959 a0299c33472c6b4a4d9567c31d73bbba880abfc45ad36c6b76c9d609fe339228 14e742056c0586cd94294182d584a344e1983b9667b217b2cd0062f60959e913 6c6a8e654a74956f16048a6ecc86b3039c0eb2134da37af62a96ce64dac9cbbe d0afe0171370f1213682335e1ed8ef8b86e29333ae17f5709d1f61fbac037204 c6fd55018cd447be601ba759811160dccb0ef30a3879369f53f849658e59e1b9 53a9e18c47c7902b3a5e37eb9cc01dbd047be6fd8d0aefd8b834ece1dcc73d91 10695326df92c00e94ee5bed89db47bf1d176ec77ff909a7ce51b80beb9cc73a c81f80913b460b131991736bc1189e47bfe750c5eba7aeb7c9706fd5504eae73 ae3376b66c539e57a3a85b0407747b5aa4a808f881cc8b28e05598753b1c7cd0 dcb4a75073ec0cfc16742ee3e11beababdafd2c2971838f9f61f405198b9cde2 4f43453234ff3cd83ff8610f0a11a3cd80ddbd4f2b01dcfbb7eaf9b0e34d6780 b1e20924a708035cce261f22fa64832bea100bbe25376710a1d63e28a141da73 4354238d27c5b245ee4857a61b672ca134ada33bdcaf96428f04a66a808af020 63bae93da99a2ee4a6ec49040677f4e6a40bad4c507bf8f53b80dfa9712529e5 9ae93baddbea01705e27d4dd13a399083112085a61f005c7e64004717dac37e7 35274aa3354dfe2b2c6dd3e9f518ccbddd08f6ee8ea0bc45713ad8791d579ebb 3f854a0ef357f46db968ebbf8a2e686c05b3addb16ed6c5db3a7c36e42cdacb9 2a266c4a649e19300e55b98f85848047c657680bcfe24ec80e84a8296345eab6 48e89f8a141a6dbcb3f85807a3af5d5a4cc8268a7f1af7fa0fa76659fd3279c7 0fcfe21a204db6d8e058d8865e52f2114d8edf6fea0353a7e9719d9ac8a047a2 d21119a697a400d04f91496461a93fda9c0c2ad605ef625a3a41b7c24e31e669 7d6518f8e138a301da092c5133039727253495b8e2cf5acbec15ed7f8415557d 7e86fe6445a668559eb92896d11b780f085b3c7544f57f81801b58116f4a4271 49a18999aae26bead623c293a6b7a71010979fffb93c058485b787a31fdfbeb9 8889d89d2fabadcf70c5a42bc7aac69e6374f9298cc1bbd1d5f917f1ee08dfd4 dcc9a9f6c6b429b754daa54566b6d93351228b1966c278329f898607a44b3bdd 641a46c0cebf5cb6cd9b1d7c91f5a3f2231afb3c3fe5db019808d316eac1362a c5ecd18a3e13f4dc954d62243270db3001e96a24813dd29c795690afb8a031a9 9c029f54454ded36a88edc7b15e341aae381ebee9c31e86348b16efb7f990bba 3958eef7ad6ae2ba276770117eb17de9b960a3466937bb1c20da5efdd3a60893 be87cb8fe5a362f53dd796f131cf79f13d444bcf8ad60d4f25f8fe7d6455fa37 18af8e9664f0b5deef56a7abb8b63d502388417cc0f644725fdff85cbb5b9e66 5c70edfab8b45b804cb2ba4cc3b1021d026d83ca3d1580c3f14650c097c0fcec 24575b1423c908953e36a6a24c54e6e621f06ae843e9d89f2f52e3218768a456 0260647016a82aff1e62bf983e30a17640cf24ce3c73a9d8f89f59d226f32b83 27535f0df1e54b627ac4a37cadff9e6cd5c96f43855559fd563268bce841b335 fe9b1154b1297bdbc4cfbf3daa035a70b79620cad8f6a8523ed2e8fc9c1ddd97 944ae90c5f38e6358e93af183f715cf4a3f3186a9eddcdd592cbdfccc3799a0f 3e9e657e7c8c148a8fa07a4b67c45a2f1e69a8aee84f3b3b40898db2d96a7e3f 6e1daf3bf663ec8316d25fbfe77ad09f19549cf04b9bdd3e0d48e51de1234ce9 3e62f566af10f50651bdfc2b2a6f932d26010a3065fe765a8ea0a4981fabff37 162b843296c3d0c5972c7c0f90e1c8e40f38c3515969fdbf54c45ab437ca88d5 8e8ad370cd30dc8cdffb68747a2a2560977d1b2c59196db372e781ce8688a4e0 abf3d8aa5907d51ed62cef935e4628ffce6deceeed64f746ebc169668f2e865d 9667dfb5fd135771b10d10ed39e012760b5fd062b063cdd0e7d381be79918b03 fdcd73976b5896cc5858e4c31122f954d8eba6868a960fcf6cf5165b4419184c fd955a19c747d6a59ae7fb3173feb7b4a504de010055458b2a9286f1740f7d6a 4cd76b98c047fc6a90f4ae937d3199d4d8fbe664799ec75998366a27bca4c271 e4b619e62c2ec934be420514c44c1da07c127e6e125c6e019ec0b7ced669e777 17f6e3cae742ffb474168bb4e087015689155744009e1bda70076d309893b82d 2a460b4dd1245bb5f2f66164ecf5820b29739249067cf6942511bb811c3daa18 ab0523a1ddaa8788076c7c9599c06a4f8bb19dc90cf49246aea691699ec09870 688b3eb5ec3f927524328b145326de1f269743cec6eb374692b9092a7ee5d14d eddcbdfba55372d653e19f16d08a8495161f1a88c2132ca559ab42b89c719378 3d1f8e63d4953c93f38459dc0a65b8084b45394c86147209d98cb775915ca9e1 3ea3833810e1f68211778d5877a92758eedf2fbe1e0aa7c62b58f5d089990db9 f186ec1c5270e1b0f2bbd2c051fe290c46fa439167e1d1bcbe3fb2402aeb2c39 d63c32f5ef2f0aec6c331482e6938df511bb49598f7cd6d826fb727678e8ed23 73252d0041c16b46364ca492edf0deac3f846b2324ebd7f0d290b9927946f442 dcb299b9c841a47c5045fb3f1aa2b3884fa4649cdf5e1d5b07e7c9c971d40ea2 bad0e363c718712dc48087b175fcb8144b1645b2511ffd4e225a285fdc5a3ba4 654cfbbfb2f1686bf62b3786e671e1a09d8f360b7d194611a50300076c3297d6 ae1ffb1a87718f167e2c6c50ed481e4a42f89d6cbbf693881736fbbefc91f23d 4894425b4a2f8a1eb5bad108304e174032e3d96366ecfe1685367093351ec64d 0bdc2a6d32ffe1bf7125d96936f318f354db32713467b5b0edd4b2fdec836807 b32a4e79b2e71df15279f8a316ea318f165c54228f38db6e8d7bc856e8daf4fa abd6244a727172ad8012e0a4fa9c56291aa631e4e44e4aef18ce61b5832abb9d 785663d027a175b64a09d175c4b5af36b7f478d50dc9bbe373143a7edfdca4ce a8d453e7cfef64a9ae74c508a8f6e9bbc055203fb76ff0190e3a64db114549fa a6ae712ef2060def16f74154df71aabca7452de2702768f4e7e0bac77ffe253c efa52b382578423ad35ab41eb1bdea3bddfddeaa05d92b92f18b669abea7b871 8466774991795ddfb8d4c5c08edc32775c7ddb2a4f813a2a37c99ca3d4cb5c19 9b425f646bc664e667dd4bc4c234852dd0a7dbe655407bfe1ac63d718042bda8 52c74c8f9ce6074cebcf06b9a198fee8bba1abb4e671d5b2d90f491f1aeb1d0e c598d4307122f91ec90835ad2a191959a9a9e7b2aa79d30037d39d6b93ca8795 369b648ab0c6913d71536c95a98a73163ea47df7a9120b2edf8ea969f92e3829 171e0bd2c68b457be2cc971acd2ee633b71c037b6b93877ca603cac69f393521 4d2f9ebe8e1d02ef2a259167da946e6cb6451e5233cad44a07f1a27bf390e497 3741f8f6a258af381071d689f5b5b885f1f180e5dc873806cd35c5dfd2b85913 4c921ca4eb0eee796cee46ad32c18ab739efb8ec738e148472a871ea18bab81f 9087ff4162f8e4ddb82098d47f04803f43af328b5a7838d289988ec74cd7470122555bbb93b8c831c89c1614e3deb16ca833dab316a265db9f5826c060f0600523ab57686c1246683c21eeefe1ae9d16222bd4968134ee90c6c98ec33d5b4808331c5feda62fc84a2bf1e7d6b8d889ecf7e9506054d30067b1327f3c4777d207eec54e80ee2cccbbc9139aebc1b100c9ef84cf3b91d12151750cfab2fb86fa08e54a389108a7e884ad2ba36a2d1e3c88ba84e9a81a8531e077f7f0f26076270a40101ca2a47794c2c50ae36655aa3844d9da1dbb29ee18ef555e126b15676204f3294606e805a9a4874ab2ca44f1620c73aa98ff001627e2a50731b0a3094b0bf37f3e183365bafeb7342fb487bda71ae84baed11a21b9f33f3d44709c90c00ef9d7c6c08a3c4a876c543a3cc2f15efe98884f009edebbf5356609f6239dba0e4811bef4337f3de869bee43a12bbe6e4f03ccb9468fd3c70cb0775fc3dbfda0de37a733cca3721532cc9dfaabfdacc300df612c7fe511e5ed9b3b0c64ad43f0324a1a77894bf05dd232267ed16409b79a1d56205aef93ec65765eeb6274e690b486a9f62bed9a9e72103d20aed6c306fa689fd0e79c288fcba443c0e3df80b050e9c1f559aa27267a602717557f94fc0a7c78c39cb36e254cd2b2c0eb1131e0d3f0734ef699a8008a04e3b399d7cc0061fd433993e6e33cd90c6be9508b490052cc90ca6cff050e9e2996d5b852ea604aaf8317cc50dbdd6f0d4c68268e4b306731e373ed0617bc3b926621c76bc6a2e4457e24a86e1fdc67d158f304c976709c3df7bbfb0a09e0c23bad35d30cdf902a8a5f3b8da749f86298291b2b9cbc601f1dde9e047e248adaab90c6db49aed5dda594bd994eb15b9faa02becfb778c04a60dc303c75c0c34fffdfb57190647eca2f177886c1cf5ea8a613fafb94f360d28968e9a205a00d64e2583669d3de98a7dcaade5d597b775159042f6ec7e19001d96099c106cea30ce45db4b698f9e86dd148da67d2acce9aa9739d425cffe0a31af35ca173554efd0a32cffa753181865261380dce4e2a1daa314700c4b31091eb7290bbfefb6090e5f425d5bbb1b95abcdb9fd454662e27783b65f4af81b0ea8f8be69b583ac6082565f8135f0c2ffddbd4564be5af7efce16c8c6138e0a0b5e9581a23525f5dde28d3667ac63c10cc3333d3a61e926fa9561204fded20500d83f152fc82b20f609945095b3d0f4c80c6ca68fb03bcc2dffd98b6b30379900f4b8580855e316afd67937ecd808f8a3762d3976272caa766999dc383d11f10fef986091fce1f625ce15e11cd3a966bf4ae06fa57f802c9d691f5f7e17458806fa2e913c622eae50822f1825c192fbc058bff95e90abb57c285588d442061f03cd7f6b208f9fe6c2aab5edc41e8c21aaf371eea0bbcac4d2a06f7dda528275004c22eaf7b4d641e21198323083a90eb1af514813b65b71bbe88e10a5667fc90f4bece9abdc4125dcdf13846042034caad468fa18e2428304fa3ba762aa5b640f6cbcc50fb3477d43396cc01f23c694b37ff964061b2ded7af760317a4dde9e04a608c2ffe9924c1ef855b1ee7441c2d52acee165b8a472aa2a63eb81d0bd670d1f3d48ce83f30fba50bbca58fc4fd324e0b77db3c08d0a8fc89d1b39becf1306ac3c240518a22de95994c926b427135680d73bfdedb3a58d99f5c499117d0201a8d097679ddeef7f3d48384fc78260895cea97603f7a7b5532aa8ca3ef8805081b9c03eed17c4e530ac7164f06549814320583f33c070e1eca2aac6ea4bb9f0d5b1cbb1b8d0db4d79f4666f969312e6a8ad97c188cd159de5b1663afcf3bc206b23f18937a46a706ed555ecc701ce6616e12258ca3352fd48f76ffbc802a4809fd9a15f6114fd9a30093a67831ca26f1b5d841630eaf62502d2467314c624e08274829883e328ce3ac3d6f825781783c14a1094bfe5a16d88baa38967c0fe40f0bb4156b6cba1628463432dfcd9b79631a61d684b77e99d35f9d30612e3f7e0f6687a5b3369e47c2b46278dec185f477863ed305685f704a0dfd67f831d7be0e7ae0f12d744db982714e52c3cab4e902f1be36779ecb18bc5118db913b0c13027d7afe76e58d0e50ed9f5084b15a22876a55d4b89727928e37953c1481cda305554de16e84705a073180a146aee38d7b9f354ccd612c3fc10f117d3a8193740d32dd561e05f4128efb5b53e515b4239316727c5c8ec703a778baccc4a8552a0ed4c41932fb8915afeaac32bfa033b824e1b19b6599265ecd39c6ef5cbf4d4b0da915ec5575fbcd0851be19fc05231d66e61ed672e70435c00b2d92c17da3bd0f47206f1124f8ae05d0106d9ba18202542f42c9dfadb871ddd6a093cbc6ca1d01f7bedb457857a2c7da64b19fcce74da523bcc628a3ded38c906e6d63b81309096b6f7ca1abe36e57b7fec49eebb29657ec0ab57acd719a01574cc965935dc506e42c4a23c0c7072595f45f96a2a1e11dcb521cf6cfd470ca7f89e078bd920a0ae43f673c629333a1dca92d275003d4b345bf9f668dd2d2bb796721c6a90bd30cd960174223bc0343b4b1157c8b61153d3281a263613b5781b64e874c7272cd04f8087680f9f4c993ef54e9bc351e34a3d95654e7c54f14c384eca7e5110c3d0e0c2acf17ad246c75530654d01f711189589b391737247f6e805301fe5500100f2e7a26b387ab1993ce2cf9aa2c58b43458cdbc4d25d3d6c77437750272ea7c028fcfcf75d5f97141f5fbc55342e0b7f041239fff79cb84a147dc959e2687f30c649f9695c355f9050eb029619f487054179356c5a76b11b07c85d4857280e30720be6bdbc4030e05419a4bb3cc86fa69fd85d58ab7e48318593f4a900f18060c9bc3e096f418928ad2a7050d32218724895d58817106ff19679268310c07fd0599849386def9f9ab6f7aab586b3764b3278fee5c1f8b9e231bc8dc1794096a00b55f8bc7c09cab064503916c8a7664a025acfae640b53c3fc514b00cbc94b60f767e3c12bb3d60cc98b57ecca40c8bf3902941470ceae4176728b51be1cdbd0647bc841570f8852d73690620c70315640bd8a77639322fd260f358da16d2ee048d787bdc64fb68972c250ef52243afbd7aef602f464d2758457ad2dd0dc470066f6cb9470a048d9e932c823c9748132702a311d6853db3d27ce654fc5adda90f32d964acdb655f46c6128fd96f06211fccb7951d75174cc87222627e9c254a0f1c81860d0ed072ee05522960668dccbac8cc8e7f3260be271b04537bccfcfc0e43e512ce569ddc7d4f788b8e128c75bb1136a707df2cd2d523a6baf7ec028b0b6e6c8b9d2388ced25496ae066a0dc664c101b60e8154a776aa93e3c00d8f61037744dbe1c0a58fb8c8a1a852942a9ed913e6075d288c10cd7fe8d615ecd2630e4acb6d1ff816e16c2dbb94320dd5fb73193d84caca39836df3e154f6b23bdc00277cfb7a25f93bff7b0997614b1f78ae79a676604e5adae3975d0048e575c8000023c49223a181369c8fc40b24b768cffe7e413ef784eead099fb5758cac510381328f6172e4b397bd74e247168c1c399a481538363898509da84d629893530f5f7c25c6840dc3e9a0d733158f39093b3be89116b8b33d3116f8ea6072bdd00f38d1a949cdc49744229547b9650f6ff3a24199ecab0cbcfb643a6a04c05c6a097f640cf550f48758060748d4f40b355efdb6dff4b2bfbeb80623b13f83b934073be1bdbe6ba37395cf9d1c555e2a739f58d0bd635f063a412459e0c12af04d018613baf34cdaa95ce4a9cc52dae55bdc189caa3619265a17e17affeb7a4d3d08268f7c4bc3406852d4bba48335353010ffa2c1b98f0b0e64986decd78f4e0c040523987d94624fb4e7b6da05b2423f3b31886a95e10545080d19c1ee18a3a503a9084f60265d5cf16b5cf0b3ae864dedb90cda0bf04a670b9853e5e6b3f0b00893432d126ec7a8f71e353f7fe8124be12eb103e6ff81eb0f1230e16f35024a01c634d97fbee1670d63ce51e098d07990356b8842f746554d3c994bf280d5a600329c8db43eac016c33eb7fcc11b34c2876b3bef3e01bf00edfca45673b1ba9074ad8a41c7bb2138797e2c9d8514c6c44fc714065a7b630931c758a8f7730e60910a0bb3834952a8994fecd4fdc9834fdb221d55d4f00c548c73902d3bf54580096244234ac6dcf206a917f9917f81fcd0f0431c12d158377219b52b603f6750c143635e99239a2ec355b080fbf6ca374d0efc99eab2de0f9954bbbb048b746008b016f309fd107108f6334eb7fcb699bcc8e4148b28de807f4918aa25db90802b2db3f1e461ca1b37f57c37a54208a2c6d6777ca4bf4cedae86262661c85d80702d68d62370f89b8e222804d8bed082379d37adeeac2efe4b7cb253e5cd8ca070bad8eb01d5c170244e4fcbca71ab4c6b225464c49d5d7305c04a904d32c22057e85ddf2aac67b1a8b1ccdfba3be898b8dc8aa5ce8d321260ae299d1388d4d02c2951d539037d99ce7f18414885e23186fc65239cd893d276847fecc4d217c027292e001cc626d6e712eae26f32d06f9dee10d6bffcef932e49c31fdf770c00a6f90b6e615c20326e837dbc81ba13dd664ad891a995424866083238ede6e520507df4c0fc97a21eb9404cfedfcc851b8439d17d68201ba2df2982a9af14fba02f0648407efb96142c25063e42620fa37b4a76d86ad2cd778c573eb20eb4e8b08c0f180bc5be9154f2ddbb11ed0f343a7dc576f65de9a791d1d59794998983a09392cbdff2911ae8475d9bdef2097c715ed4e7e6d3dcfa362d94e620af4b1bd0ecdb80482b4c34c3f998b3dc5b7210a5056db6c506e8ffcbb9ef891a28c45bd0c0174c628901c361729b5b3d45a22470eaa10862bd470d775cf7eb8e763e84505c70dd08b53b3f5b9001119bb97acd3ec5bea2ce5f4addd3af025c5ab745a390d7c034dc6f3c242aaaa69ad737559f88afd917e541e88fc5eac25dbf60eeb1f07c9b15e95344452be08107a2a72ffbb099493da35f426eb8c381f2bfda307950db7a4f54ec1995c0849f89a4680c1611bd65f69e9481d2ba388cf757a04f3120e00eda8254b45870877cb60273994b238b379f90a6b7b6ee15f5cb402ce0b640e1aad849a553e4e550bbdb56b81fb4e5f9c29e56066b9d7b50a710a7fa67ba50faa36554a5b87889b7c13f6359d8c81bc009b67ce44a3615d12e0244306137e0edca5fbc823ef70999b84eeac95d603f0fb3bf9195c325ecb2e33da4fc0fbd700c611efe07ed0583ed750eb69fad962efd6265bb5cce66d0ed350a8a296bade007636250445031006d66a77275d84ff581381459facc90c00e05cf20538326f0836efb4a21ba6c00932c938f0fd17ab172ed0c917cddcaceeae0341d09c76f90aed521b337e6ed7e3523f36b92f18f4d39024d321e714003d5d90abe62632e70dc8f9fd0fe8f3c05005bc2fad501ac48674d1186ff11038296b52bc1ee6e7250bb8535b86020a383e7a7a6d4a223252bd53afbed0157963074796eeb81fdb390dbdef193109bc41687975e882b76ecc784460f7932c6cd65e283a3476951e2d0beac84f6566beebaeaa600c6a70ddf3b62bb97fe0d88523ce0b5fc4d0fd9642071683530c7f35a89a2a4ef4145aa1acff87aff091c880c6fc9fa6447ae42829048741e345f83f7b513a513738186522469d02bdd3c4be7e3ed69f2a67a3377c0fe39a1525b1457fb7ce44dbb7e932bc0a05e2f7d0fbef6f6cb1bc0c82e752190ea56354004bf14e38b745a049bafa202b36b4b9d2f83ff3ec20b752c7c28f6f0fafef651b45882e71c68f93e35e92f848e55d376ae44f2dba4e392f205840af083fc7915b96d695f9509bcde22f1f0df09898458ba7a0c270ac1ff5d796c63902b80d0087737a28880a11058adf9af95b169548609ae88c15fd5f29b81d06f001366c6ba9e6c19e66cdccebc9362334be67d43c4bf323274890fd76a0b907c7096d8eb5bda3f6b86927a0f972d1dee8c80dd3776c7591015d0c14683335838c01c34d939c7134bab64d8127a9df75108710e77ea9956689724ba6b33d82cd7004674bb4d33474bd51ce53b7fbe4f8e33d7824e23ed0250f61ac09e1203540b807cb9df7adc5527d58dd3591e8b374efab7a5b350e017a004c729316345ac77c096c5c55bf20ef2fb2322e42cc14d6e1bd0d6b5ed69b3e28164523a5492c481e0cbb5026ab22c07043e2a882b3a6562d4bcced3d4cefb4956f2ad2dc9ea80cb00e436987ecc71c12e1514cafccb1f51436a55f716eff2fab991f30f3ab2a7de008bc782dd7374feb8f5b770526aecc04035bd0cb65e075239fcb29dd8004f39f0055217afcfe2b2ad66f354da492af27f4ea5996c9d9d9798811950e56c44a650d201c1257f836c5cfc6ccbee8a49bf5e25edce2b5ca57a667a7ac18f508cf550f4f2f8b57062483a1795521e5f5b8bc792bdbffc2680bbe6922f36701f1549f0aba231ae46f2fb3f1292e92d4e439c2c5b60993eb454c2cac6aeb3092d865ce00d10d9637a5c92d0290ff7c93e2e9aaa85bb95a6beccafcf94c916cdf3638ae08425d2536e160ea7156add4de734867d4db8b143f0ad50b69135f11a6460a5f015e15088c1847a4fbc0eaf1775e4565fe4b78be62fd99c965864386f39713580f3244354dc93b5ff2518a22155e61218d133c1dbbcbfc09535fd3d36be3572c06e7e0bed50498e761e3c793801f9550299c140fb83a8ff3714dfc16dacaaca600e6e0f365e79096691ab41ad4b227b95ffa4a09b67d23f04d4709e4e25a77e5072cb6be4eb0ab08eb96179b9d06ec5240be539b18a34aa3b14e81d0f950f52b09aadc6e4da1ed11d828a60ce24af6a8195f80ac19214876d722e1ac74814a6b0fdc8ba229ac90a0b9b9a634d3d225bbfa31cd6dab80d2fac0bd09bc5a318ec60c14ada5b912123ca9cb1b58af082ad376dffcf135aad650a02f2a4e644264c10ccbc67a3cee851e576a755790ab858102d1386a8719eabd1a799f2f9b60667c0ab18d26f489dfc48cb45297f6196781f79f960264fb66e5811f9df1f728d9f60b8de1b7c799856ee3faa47e177cc8c557762f3a3151fd37451b28dc6d0584c90e4ac8e8e4686bfbb999b195979e16322ba267dd4be18c38e44970bbca7edfa106d490143778bde6868fd3bfe836f30d917b6c15f8368ca0e3feb60436778be9080b12a8d4415ad3263863545bfa8b57b4b81ff4ee5f16e2f2d4492a2cca225a07e5fb03c64f201b46200697462ef631169335d7c29d766e1083fc585490cb610544e8ff89153e13467ebf966b469a04ba32b709bc104e30c36cee49af4b153d0e2bf6dcbce0c05a226653053c5fe0906fa75f3920df92b89e524607fd0dd2a605d0db5ea1edd7d1891fa6ec27d8c8fe3330cd41098ac3af6df01fdbb17a72a50aeba3284696e58f57bb9cccd25ff34b446c5fe33e5c9f936e5a1565a83457b0003250943a05367945c851c1ac093e5169cfc09b25537eec2713880cc197191302836789d0d1d5bc6c9768c1aa24d339ed175f11bf6148cd50120efac67bf5b200352f6e57118a2433bd454b1af59c33eb070ffeb48f53329b8a6a7f85f0b46d02f67af8ce2c9bcf4c609d40fa0b0b5b753f2a0e0342dfaa2c8596ab5c9ea7ba0123e707d8fd5f1486b673a005d5e2943281b010a3d3733b7f77fa0a7e8683370465baef5de7eb645d87758c5568180ea224b4747e34fe80b6d96ceeae8eefd607d8e37314d18e00008b8ce3cef3044dea5ccef43dcd6e29a1e340c20072cdf206377fa016c5e8437a6e38416ac3de4c00cc2385961da0c7536ef322dbc37a5d09f0aa068cf3e7be2ba0f10c5308cea8dcef298307017225723228f0f91c101d0ad830659719a7f20b4ca79dd2daf87774876fd9da9c1119b5635056c338a06700ed09d9afa5537d7c6f85b3603d6b0ec4b9d0cdd9ed8c6cfe232d67f2ac36150835fd477a4151046a4cde0d02439870d5619c8fa2ed5429cafe4135fd201df702337cfe315a3a293c0fd223391f6bc5518ed114c8329d12ff828f580d31dde6088798ae5b6ebd675a078787fcdcc9b4bc5cc4244180d7cb96728cb4b6d33bce081a9374a9839babc6bc286b66f47212a34a2c6630c687608e1f00281c064a6b0c21021210aafbca3c352e13b4af5eb64af8a2a8cafbef76d2bd4ba24cdb003900550848a62dabc49802e7ee045e44176e9fc0df257e34c59d6f63a2704258c605466e40a10b1bfda021456f15813dd8cbbedae8b58c1a532bbd82895f0f4a0702f924c183f711a2ee5ecf112a7ca6def1858854bbcb08eb1bd92854889a51a706b94a2a83226f140f97b6ae3da85214e4f9423291a2f28850244151c0db024b0a47d9e33d3dc061ea68dedcfea95c16b6fee10960eb7ac068eb5e402ecc14bc036999ce072c903d9369f53ae9eded3b6665ec3207a0997275eda23ce660678a0a0c9d74d13cff4c34793646727cc8cb8d8df6fb8b88cb83428b4d74721e6ddb059b36316e09528bea26653045ddde8aee6aa5cc8261f67fd00469c2dfd763250cff695acb95ddadb307f2cc0995f602c8bde1f5fd65cc252a8242c3d94dc76d0c0ef23fea25cbe107e68a3e077bcd0cefeb396d37b4678b24569a3a6ebd18cc03db65a19f47685f6aa13fedaf0486ee77cf0ed61d6ab514f4074ca16e99869c02a5852c422283562e7b2842eca3c4aba69cf46fac9e7333133eec51e5069d1c0c8d8b3d20945f5fee48f35bbc6aff2f72357a3b5e952f4c4eabba6c6eae34400e1713a452756f1367fa4a821f29c8f2e8b3a514df72d500a7cfac1ba9dad0fd01eecbfd39a78d363e05254540da1fe2458e043fb18356dd7764f6c22f4d1a680fca3a168c09e1232a6e5cb10ee02d31a94e97ee620fe57f2de41d4521947a0b07338fe14526c453c3dcf9160876fd3660053d522da331570cc5e1dac8f58c490d2fcca7ec19b06f8ee3be24c3fe7ec067af6017a255cee73100b24d2515a08b00a4c3ff01dbf88e4ea23995ec58ac134bc52d4ea723cd6c58c46acacd7038e30e7b3d5c27de19c293f89ba51008a416e2dceadf0b4da29d16d2410c93566898038029fb8dfa8ce2086107293d5ad617d00d6471374399c09b996027459ce359061e56b93b34908fb81dcf5d2d2dc0c84e333eb2730dcfcd4f29563c8800b3330070121f05898d5d0512c862a668be5de00163a0c35e961f1987f505ff17ed370b99e37812f736f9e8b785fa28523261ef3263350e768613b8ecde2659eb5ac70b72378d6b025d60e3b712ed9462a70ede5272ad4b969c57259905c8cbf36883034ba15094870750d25d4a277163a98817e206045eea209d10b77d07fe8d5bdd04691594865af4b17747f2ba9da455f11aa189153758ee23b2e9db2a8adcbaa70385ab213ae61531848abb718881de1d3db56ba162dded370682b99a6be6246309730ee20136c7e1f263d98df0de3be425a49c92bdd7381bc3a72624e399f9d30bd1ae9c6e15b5c87318f40eadf3ef31d68a920f145d64fc3c6a1bfa2cc8da8e06ff944dafd27937834e4d65a53891e5f609eae82d3f914a871d99d8ba7ac8d00b6eca47cacb98e12d0544394835a001b6e6db2bf04a767a011e4365fd9699d501e47685a2641344f11fdf227572fec52f32600b8b137ac0ca4ee100a5a843f103751678f6892ead5f605eb7bece36094987bb803e46d07112ba749f2307e50f0907d5a8414676ef5b401e1c972cb5597f2988e4e86c6af3082a27b25a0ee18403e566e937b7397d0d509831a48db23e50f1c3730ed893e3f690f67ee29255a0067e3846528fca35895b995a302c8098249d56afba059680d29be9c7c5e58e25048ffc33bb016e85936d307643e617d5cc7256196ea461b85b006c06e41428b7073838d70af8f88efaa3255c5aa896b06282d86b11086c930c3ee65bfa121d4909bfdfef5a209eb7335e9974ef348da1d4daa482aface0d4427bd0a131f639ba0a589079c8ec36eb0246073f37d5d046aeaec6f522e95984c32d96be8db0ab15088c91a9c8126f5f1225b0406a211a2f04d9efcf3dc02f9f15f9e7237cf096e50930681468f6f72aa958734498152cddffec91ec6e094c6517c25cfa764a768a0d4915ed0ad44d909659ee324670aae9320c454305daf52721e6a9a503db1a5f0837187b9e0c26d1b6d1456ff894a63b3c6246d26182875efd1671feffe3be530ec2b626d8f696bd06e6f7662a468894bb587931315425cc9374b25cad0591ed013e04fa3a4e29d3ca359b9e3e26cbfdf54659d6b61701c657a11f025927975a0cb221b0ab9b03d1f08612f43afa0b75de374d32a1e539fce56c9802e06397f409b93e3eaa5ce617697e8cf8a867e88fdaaf6b11a6df4129a1e6fd448f684cfd02c78be65f4c3572787345ee23566a16adc248364dd34004f2abb811831397c70e4e25a124a70f9965447b382511d9b53897148d3e8a6a8bbd4f1a0aef9b478d0c65c1b5d9b75749ed0789877f5fd4b8616d4f7d3eb6bfbb42621a43a01078e7053d080e5737cf86e01b13aa72df1532e3723b485236f91fa39ed9366800318b0a2dea26c36734d21cf41c8e0b4912a5182c3904f133907947f0809ea70497c1088a9d1ad7b12eb637d6864545658495e6803c66406d1ae3dc63f2607b05e8390011d34fd0385b2c2b6c1d8a18df9ce760d11d01fedb03b7830865f854e01b220531e5b2d1091476f2165a90998e5851d01a0b7f3a2bbb920c191483d87d9a1e00533af0ee419ea877281312282d6a89cc377d089231def89332bb6e5f784ccb0cb07af278ba8bd9bdf3d23a70dd184e51d05203b03b7438b0b7afd765302f8a0d36cc94da07f9a1fa06926f817549af6e7e7c58289164ace61819a714c2b22b0010ffd00b9edde4cd2aceb65e787f4a36725426b4487f75d4a8807a9ed82dad0a5e493840afa930352e531d8ba0f96ee5a599377ee7d119a9316cf78baf3e550798c6a1ab03cb6c7475e56dc23913d16bdb447e973b36a69bc9cee69f8f2e1b0a64a66e56e7429c35e9db8832b6c8825a9511b0aead96fdf6cc37e802021c0208b5e324e43a06cb7ccc10dc3c929b0ad8842ca815dfe7f0a6e6b719d75912590169f09616b7363116e3cc3a53bca1f2de4ff18b102ad0acee5ca40a4c7dde4c061870093da17366617e66933e23f741521c7ec8e3922d806fa47e2470626022079b5de0c4e7eda45080fa193f575b14b82568cea71889bad57cedc1609caaff03e45fcb34235133e5f4aab82719f7a026a8a2d954c948e69b9e9904119be18802ecd19ed88ea9990a3a96dd47d4f7faa77d05b89c0139fc8116774c2744105a050ec9997b0ace7437365ceade49f3bd04aae64728ffaa165c10f86fef47d3cb07d69a1ba1cebbeaf79bda813cdeaf30a139ee96cf172047ab0dc1860c021a3f0ddf6a0a432febd80bc6f41f3f6400af1a447e0925d394a48589be9ba17b5258036ace958ce93a3853076fcdd2b18ecbe4690ddc6b64a41e9346b8ebd79f91900000b9eb1e27c6cc54f8ed1fb709623333e10ceda56e636d1be4e1c6506bdabd08c27d1f0f9d5ba25b20c538342bfe376cf7809fc402544edb8ed96de1690c440c37e2c742ec016c6d872647233b78299a22f9060a34b6dc832b34cb6256fb3d096ccd771786390a405c910042505956d5705daad9a0abf00fcd9b8c400d0628069cec7be8d7fe54abdeacfbff62abfbd527d96a98bbcbfcb7e388829e72c48a009508d4871b837db969328d102ea49e5a1788bcfe8d5b0bbca59db67898a804027dfc1abfd1ced9d4d0dec131f5edfc8dec4a5a2830646699b9a5323a07687f0b13415e7227a53ea1c0c91b452aad2933bed54d85e464568b81739aeb813d94027c21bf9decd574a8b6cd8cb48eeeb4fda63f6f316e0605011367977522e4e90b79c8cc13a59b05fa630478b7133665a394d1d7d91b83b40bd4e8fd5d7e831f0ab9896d43a017a1c89bc40840d0f226175b13e9a2d36351de36e517e4e7551c046419a71ff244c0a2c4c07e6ee5485cf4f57a8da1472ebd5d1e91c44d6f6cd00bcd33a2e02c9d6550cd1258892c697e4a5b0568baa40ec690dcfd61f2ca7eb80581080f586ad7ffff3e73f6c0da6cb168c26e4eaf080fff0b42ad3b6f40483f01cfd5ead10a2c6f893ff36743ff8db5db49036bcfe97ca3147f8e73464ae73f0afd294ae8816c3a19e06b6b919d15f61b210e6013527fcf021e117d5fe240eb0f87363a0f56af812c032ee69fbf15df94c3bb8670c111d59cc8b523ae127e2402c025bd80d65ff50ec2fb7eadb4a78fb2210dd301e7dbae2bf1d724f8859c0e015e61825324313d67dcf892e5c9a16a4d2baf720ba62f2580398203516ba4220bc1ec1916936596ec7b30eef5d5f3051e0d9f8cb1623cbf5208f70d02debfe20233ac1026a3f21d367f253e0833934231438ccce62aaf9866f7a4b8fc8c5e240fa7ac47f329de82aad37137a94537e113d56a38f2580f67745ba8b719a0167a00f8421489551b4205eb72ee6f48e9be5c94fd26134506403a74c8400214a36103b34be11d359e2731fe0f4f3f25874c9c2868a14dfb1b56544fc6e0122a338a08fd8e9613dbe34490f721939a581ef2055d6e0a764b48830616b87564a78b02055e928ef37ba6a04f0aa743e6f3008bd6eeaf9361eb3ad273b2c0a098362bdd064a7332a692b5510d632b39bf6f73c8c2d33809b8e4653d830b9862167c11f70446f9eb0847adb30039caad221f37ccf95db94336a6a8cc78a3ded898b12b5b06a8e06ad588bee10e1fae510b9f37e1ee4a0882880cacb6160e25989260767603265a29478d1db8f87086f3070dd2cceae819299a401d243f82769376399f390bd60d6d22ef8bfd9902cb25a2760922db6cfd427b25324c8cf1a43c985b9f95055f19f6515f84b89fa33a589ef65bb81b73921046eee8a20d805f726fd7b65105ef8cbd3a034d9e0a5ad547ce5e5866672f811583f7d6e58d22f11f4b59bddb03c40323e7e5b647a7d5d77e6d99e3aa410c4bdbf2c43be825ba921172670b690ec296b6d95e255e0149567bfec2973403c944beea0b6d27a2b7fe93441bc48805736e9817c199eb3b344a0cdf929498b0d0f4be5ad88364bd0830d9814c18d906cb779ed7b3a023506c32929d00f5572420e06bfc650bd8c31945b161de7f0e027ff9090e2ad37874e83694b51e9e9bb6c1496276dcced9748c867a547309b20b930bb68d98f33a7da97d655f23b44dab2b82c8d29e8e215fc07a611cec3d5901ca540ef8ab3e96ef3c9118e775c2728f14de45e6e5d5ef2714e74dfcfdf40c0f809525874e0ec2162bb4169e076cf46d4b4163dd7c4dcc6a1e936cea74f23d0bab83e6ae1e832e3a02ab142b3f0700cac0b7143f4a1133465d5d69ff67489f0b57728896a45fedb567a1f2fde109f67895429d880338d5c6172e7df4f29ea407b6a96ca6b983f2a283fc31f68193c2ce74fc6f010256761659f4749e4c4b470aaac11342d2c02ac72b3a8e668379541280cce4b5d7263685ebcf33838204b60c81fab6e8333ac9c961210dd4e622c3490ba9c2d9a103f002fdb5b37b93b2ff080f2aae0f5aa0e1925f10fb02230699f86fec11c07484a55f6bab1c718a231b0018e57c0b92cbb49b49e2494448e5a81e6a8d19150ecee870faacfc9dd499700caee7388c1f3c55cc1ebe01697312beff7fc64c5c100933d3b91615b6b6c8a9064c11606d8fc6ee98c8b2b806eea1f00783a66c0dc6072ba882a5b2bc83257702313fbfec2f8e1f8251565083bb5976989dfca8ca3ffa0f8ea890cd393fe5ef0127aba419fb1409fd18876d4142a5cbcaa1303622fd995d4acc4b4feebfbe910cc453fcff1bed1c8674b91a710af1122acc49d2315032248d2ae35632dad07d0ff5ee1e12ae5e44ac617b13309dfa6e0211c5ac17fdc2d407ff2278cdfc3f6d0e52f98239dbea8e26b272fc26dfa00af3de72ff29b5d6eb24fe7cd548314e310ec280f498c28bfe7225dc334282a081318f5e0ab5cfcbf3ccdd3335cab0cb10070ed72f1d1e22bd5fb6740856e4ed334d0ac352cb75b997ff1664ff2f2c95d405db10f74e9b610ca1f6e4a6568cacaaf6cb843ca350281e16f5808f11a2d5610f9dea029f741af9253b33b2ad9a48a4cb19d5bc451e2c14da456f6c1d499eea045bc5474821094505fd9a0d492f5ef5fff48fe4f0809bd453bfb4c4918ef4f605fe89e9808131c2f4c78d1249b5fe552d01fbdfd3f1fef9cac1fdccaef5571b0191e76d3e97c035c609b1ee00c7969e604390b388d7837a49461cf8444359ae08e473f899be02f87746594c4a63c109e0a8660cff40d219abb14906f5d6d97f08f008f6c49fd161c8c6b4f911257d1c723fd5b387f897ba64404a3ad9e6df25038c7f18f4f13db7fb1ee49f8ea0fafaa63e7ea69f51db52a93ca514b57df0cf0bba54d0595d1d2c92008c1f2192c40accd5880b94e6c275a675329e4cf616520df192ba21c21791bc18943c04149fd9b565efb0ccfa36327689df9a1740f225081ff5bca82fa73f760c79474db201ccb2364aaecb9d3f041456daf3431dc4f808dad71076e951587368960eb6575918808fb60a5d0673b8309dc1da0c296a8e0d25d8f3ee1c9c2ef9885a6e3c1d7d103444e416611bdac8cba7235be3384d9c0f0bb50b9066cfd5e2c972c366e9314798dfd35a4a10eecd30ac163c589285020449b44176f1a3fb4d2f59a41cde4d65da3fbf85b9dd236ce8e3e824edb73a620fd85c084d4f4666844086238129ed514e6926fc9a7d69567c8192307c7c62ef0c3554ef90873c1ba59bfff2ff1ad7513bf820e426b9ac71cd033122c589d38907c7705a8057c62bd472385d9a382b486cf8596b2710493f0a78c9a35e4a4f8b0cf0660a2334afbabf855e68d0a3acf42106456604652634ea69cc75d344e5ae059f0bb0a0903e6e187590b27a574457c5458cf78b35018e9f47db0012c0475d0195fc6b656d2c75afd9a859038fd4eec41a27ce1629457e9cebd1235a866ab50bf3db9d100a65764c11886f1c8405190d837ce373e576cf9bb51c679a82afe202717ad2c4cdbb766e293f1640944f858a173edcbe212780d5265d6c68b76f3b0f2cf4b8756713a27b708e9e3b11a236db0ebe37f45ad3b739c7a93411a748830abfc95e856d6c93220d79f982630271791da63102196c5e2c30df0ed36d17c2028c8a70a2cab40457225a4a5d7cf2e423a1590347c57afeafd71494e240e6840ba22c7afa6f10728df4e827dd5d0c07b16c9a650f321ab090c01a98afc546d3089553a78466be2ce29753bad39959fffddc1f90d70beb5f1f82bfc000ea51bc050e70a7efaf936b7598c71b1af5b3ed7881de3c699f9a1351cd130c470b7dda0f87171deea276acdae1c31a3b7ba6bf8975db44fd564ca6e779926fd86777680c4347a68426627e67cf51ce0da3cf7439d2a096f920d0d48fbc80c7b35a34c30b84449ca7eb8625db3a80a9ef2b12a41376115b2fe78447c5951a3bd4bfddb509acf0b9855d699574e44825d2d23ba94841889987ba8baf41bd73a8c2c111a50420b47e257569e7f8a2f60a88793fbb16271177210092d4fa0c9cb186ff706b03c51b1460408967cc32469b1416e7c2e8f60d990ef4520186a8118181af207707753bd8e22e60a19ea2b74e71ec4e9765eba150c52aa3bf0c4cf07b97c896660fd89b1f7384b191a15906a15c701a6d93683f97a0b7199f58bc85e77314458f0482e7aca116551985b3de4afba7d72f77a9e3ff95adc626ed7d83416b670a4a05a93ede79a8572ea51533eb9d57ba35349254e774287bb569516258fc4ad59d015f21430b72b50fd4e75f869d1ac7bfaa9d2845c11caf1da298b6afa33b8a2d089bd2116f3f03d283a8afe0adf08681a89ad539ab60fe1de9a006e9bbf10f7a0bec7234a625508a0c8d93a73abcce5c1f0d7075d65976fa1cc22e840765723e02df60b6b7c1e847ae0518865571115122e95b00ca5926a715939f9771391d300a84c84ab57414f4739a02bbabdf5dc7f5eb521e415102ef88fa2f4f6dfc1d5302047c182584604eeac35b565b654d98950c4dbf785626cff8890c850270e87106deb1a50d12655707b45871dce54e81c0660c534b2fb5cafe4428758228ceaa0b6900b4d781cbffb123343306af6afe384f720b610993f46ca09ac9bb7a72d6020659e949b05f7ccfb3357e093d375f59ec8c20f7257473fe2febcc6b608a0600c44c0d14c385826b6e2c8afdccaa08d2caefc011050a07149d4454a75a590701dc7604af9f9227445cf09bf271ab8931d7936295827cfc90a0521b47336f510e80c66439fee2312b4ed1b22da12cded26c25645d06e2a342dd09b64fe990d705dbf9b41eb3121a1829eb9cce5ff8709bc98ed190d4175715d145a84d5aa1df0c2e7ceeee834b063170503a3551fbffcac005ca48f902d38fd9357cb1a6ecb40d9556ac765c60096341780a4f09a2c8265fba65bca1124c2e67249523ecf9ae084ac552558ea5c2b0a90309b0d158de5cbb77d6de57d8d59fd1324fbfa8faef0830fc407dbddb8fa26ada9ec6d834c8de91da8203c1d4ec273f1a0661f1a8e60311055a268226bb9670bda5cf8d61a6af759ef3d5cab1869be37e11a24cbd19044528ca263184af1f3974717275c3eb4ea838b65767f7f6192d49e51dffa0fb078784f0e80a1d8e2e4cb2afb24c5e392b7cf0da70adbd85b10639548d605e2206192840d9d4bc1017a0cc0b05a332bbd7fdb73437d7da9a63cf8a61b10d496b080a814733f414de610ec64244577c41c7e354cdee908d7ade7285fb2cde64860aa8793377b30306c8e5ee3ac2ecdf1ef03d088afb11ab1b23c4880173fc011f034eca3e910b87b4821eba44ce384e251c86800b9b2811be11aa536b9894cb5806f2443ffe3174b7eccc5a441a556b56b0fef6c0b91f9bb84ab6b7eec3333f920df0530efaf60ae04415c011164e813b194d36fe6d109b8260ce7631dfcc96320f6d26505fc75c40a0c90e8483cd9a301bda7df179a02160d43aef021ebe22e9011ebfa292827e26ea7bd3c6f3d72891855b0fdfa6b38d29e99837f1679b4659077f3c7bedb16e9a66e1d636d91a4bb4e8851fc54fdee2782dafab5aab5a61980999fe2e45b1de0ecba6c2729f41e27603aa58b1907569fdbfd4c5b9c7b08cc304d13b41878f10d5b72624bb88eb12005642c2f46989ed541b8379ab3b989a8d00e6b97adde3446858f6198eb5b3623d727c4d5be5ae38a112003595875531e90397c7cae1d44ec7761d6cd164367008812312bb45f66c24c9029b3c267e93a501376ffd7a024ca0a4c700f5abf90f6427964f4ae303f44e1a31fe8c65d4a0ee073924a48b2e66418124983312dd96bc1c6a21476b7d3c780e9bdfb222e4b63907197602435dd9dd2f61ffcbd9e7361b8dfd0c510c206b21a4c13fad955548920d65b92f573ba6463ffd0faf75ee9893dafd0c3dc781b995bfb773a3c66ac7d40775ac7d3e536d79e0f8b8a21d891699a155607ddc65adf690a30a7a9735382c0d3d87bf47c3cee2d99393db54a90a195308e567915e125f26dbc22f34fbf4e0072423d5a417b6fed251291ac94004c8db1b1b698882fbfe9a9628a1439b2599071961aa96c6952d832ebfd3b658de469ee49f5772be8a42afe4e57129d83f82017e6a57283e6889a486a4382bc0bca0eaf5de26db90cedf2d8368ed9ee69e5d070d357cdc9082ec80b635610ed41045c9f36a4612f3620b05a8cb643240574b0c9bc0e8d24b9d9ecdc9fe29ad30d7d6b317bd9822ddd0e2fac781c7562d32120f3471fb5a7e8e193b9ec5dbbbc1cff5dfb81619f8ea2714e21c814dffee1e040a0c0e765ddb8465ef079779b749bdb615e4d97ab0307e67732c0b73fa5436ec085a2ea5c4ca9bc073bc047fd005cf70efa3f11d678c0671aeaefb613b216d4409692cb5ce0b649556e80627a577968e1c9c5d2f45a8eba03044c5862c791e1700fd7804d5bb8da7deb43867f370ea0e6c2291e7fe908c6c05ae223d6e904f610849219f916de6c9e8ebb96af52fba85afbf72f31f842ffa005db9b5f942ea3e025725539f5b6eac821ec1eba9e0238c9e86888d56d10a9b47cd5c3da5b2f7b40733235e224f4a0f9b8d627d3e311de07662d0eb47e77e80c51e6d388d1094c90bed76a86671c4ad2e0ce891bf154f3a3a7c62c70be19cc7791191c20355504a0b961596c1f12b4bdb6c6b19aa39fa10ee8c45631e98524a01bb85c03e97a6c306bac8478436aa961343a295da79b86dd605d9fcf05bfbeb672660a3771aaa180b10ef6ed680d225510f4ece5439daff3aa6c34501f23d44fabae6cf81a7b08407ac8b86f687a6bcf7e3e4795761e3caf78d28bdc42fcb9b4be3763abfa8d95601901b493cae031ef94c1d020e9c9f10e9f79361766f2824d98374ca0d6a2e0b03e0e65637a8d7dd8fe967b8ff95e61212aacc48a651e3c749e3c883d761df87004ebf953dd8ffb81e151ef1f0ac91ed4dcabffc3717d3d785ebe2bee8be93e5057891636458d401394199159bdb641d4cbc03a208b3f6252fd17ed9295efdc00dbb7bcd5017c10108574b7b8b292f9920d8e6ab806242491d9da4a325e5d70b000f029ddf8719f69fb03d4b3f10c2fdbbef15f08819c19cd54e3930c8137d940547904223aa9e903193e6fead10e57b0188db784dbd29ff8c7b18401cc5e0b805f8bc52cc51b75c8622da79e765c06188a97229426abe554a965bc13a652e8805a1728b906ccb8eb6c0d89b0cfd5d46fbd8880bfa0eabb1d95d55629b369fe80353dfc6c5a2638ca82d5ba5e6db26ae7614015d922d6923e16ec8ba2acca5050da47869b41490c0b9d00618da154956b46f086632b171dfa478bf8cc73aaec307 true -check_ring_signature 0144a0b74a680a2b78d13017cce8ecde72b0782a3b2bfa8925608aa8619e9a4c 3a57ec9b5fe3a67a42583ba6a131d52204acf1f6abe55b030252293b26e9e551 6 c8d30c0adaf02612481785b440fd9c18491b596280baff7aa54d7e80e9d074bc 9f211eaaa956d5ea768beff07ea8cb3764a20e2016fa8e5fa47a10832faa120a 9dcbfa44fa3bdd4546fc33b201e5f0d604ec81654561bf2e82ca73b23cbe09c9 8bb59f9be016a7f42fb8f9c5d7ff5981fa403cce4afc68d602b56e9f7637403e 21c71cd0895d1672ccc995e64460d3afa0267b928472fb7aaf4c2cc2c340d67d e4dbe3385708a672ac93b210f5776ecb59f0b8fb729dec76cf5c3bcc22a00ccc 0000f9965b3822d910df4a98538d154c741a91b95653cb76eb7c15f13732c00798be0069df127ded3e87cc6157bd08705a24cc2d25d56ad4096546a813e7c808bcdc9ba1420bb0e14bf27d7e212704a66328c1958eac4af9b982a6c0524f460dd11d45e33d6a21cee4aa900434d56c791bf44fa68b03ac94be5d301630a557088f056c432a182911a11f6304fd974dbf97433bf14b674a6fad931d70b413d208fe0e4f5881188e2b42d466a97bb95951b3276adbb6eeda8a99a747eb456e810f440472429b9e769987bba7ba74f2ba5b814eac6bec11e7f04115b90a6b819609e9113e323295b8642c4e19880de5dad9d39a72f5342a8366466c9d88a84bdc0a2af144557990644eb471c0ad06974d8bb845695a0796fbb07a34483ca2a09100fb5ce76339cb1c3ad161769759c5c006641719db1b4aee1a82410f9daf7c7a04bd01a2e2b62397772a28de636d8ea0ca46f4da652ac29247d052e0845109f55228246c58c6cb5298fbc5ade1019208e4bf0f3a3e57d92a7e9cbb98b3d9b4f701 false -check_ring_signature 4f83aebdcef5d39f47794cfe45ef4622838f662e1fac5ea99b22d5a314da7ba2 969f46a281afdaad7910ca5db41b37fc432a9b61c65da910bad5636995da48f4 4 795e51da2723179e9b0e58438d61d150be5218035ebf310d19349d115a4474f0 4fdac355198c38f28d9413b56159870e5fd1052e53dfa487192cf54b031ea0d4 0798bc295f4545dd306ea8f5d0abf8208a6de21515a1d64d590fcafa0d76d185 f544d561dc2891a0947bfa2b267571785e0b050f61690bf1ba178277823a160c 4b046626c7a0bd129a83f55650c7324957534f3c769eae557f2239f75a608501550bfe3c7f84b319ab85dd186b584b73950cacbb6cf683c7b4df484a326f8e058e6a56cdf4009af8d983ba4bc3ca0fdebd8abccf8706c4423d904228e4d20409fdb342a52e371be8693fe456e9d11c6ee1aa5d450678947f1727137f83dae70e6004e7e111d54329ff33e5aa051baa7a1c6638e8a4deaa9d520a4c71892a4307b6890c8cd8151dc0d0102ad6d92952cbdcda22bc553bdd7e369516fa7c8bed036af01da2d50c663d1ccbdee3d1d7d014fbdfe8a476c2f86887e2cb869a3c7306461120a1c2e737690d9e1b0ee672332f551e477a8a7748050cc559fe1d668205 false -check_ring_signature da9ccff187ddcf3a12590e551251878b8fe4b54fbd1be8d083b7b74afe13dfe2 048077c69457cf9310f137f3fca30aba29096b74b8c536f6e1854eb298127118 1 c147349bf4e7451b058ea5798ed2c21cb85114c8cddfd6fd87f57196d926dc25 c4c4165226dda588dc5d7bde4c1b7a9f7210dfce53da7ea3feea5b55bcef730297b10f765472c5fafc3d06477a208da18130fb64e47c5c201a4a425c7e0e9907 false -check_ring_signature e76e4f250db29f5f15816fcd35aa761786a63b2d4429c18f9a413a5d5eead759 1f6b7e4b16ddc0732bbe15cc7ee329ac34133f08c82f48f774df1eca9ce51a7c 16 91a4085484d4c71296ce2fe7584e55e170315717abd9a81fea13be53d58dcb2e 9032a982f9537deac4a391725376424b1707ffa10b1fde705c47231b9b613224 12e811bb1b1b2450553626afe9f419e616c06e6fd102f8c186331f61cce0690c 4cc4ea4c0ab8a91cf79ef7a1bd79adff17b3741120d9e8c537da1f2d37af41f7 301bf78f0cf82646af699bfa95c0e9bfbcfa7c89d7d324cc4d490756c0396cb0 faac565e09d9aef1f2d20033fd58c17a4e83071a43eb0ec3990632714c2bf1a5 e60fc26322ea001370416454d4b4f11f3f747e0bbdd195974227e3caf867ab48 1e8dcdced9baf07a2426c172a931b8d6bfe109fedf603567da6becc3deef85d7 50a372bcfccf8f4e3e449d47d358a736cb952e0dcc5974c738e63b0b1ae0ba77 1b689e9bc35bd0d43d662d5ce32c2f9d562bbd4a5390ca1fc41e630f7e86f93f 0ef7edc3aa4f9e22a9880f9691fa39fe1c81cf739422ffcdce2ef0cd8ef481c7 18ae00b796c035be4a8c0d5f0faa5bbf9f4daf1f638a932d2cb43ede3fbba1ff 7ffdc45caba07695156d0e7e33ca9709fa6a1006db30850b463d88b20ed54214 050c03eae91c369abb054f80d67fbd46d9b2acb60a65d3109979733d80de410d 99c2fe47563bf345c341c6875367d66c8716f2576647a3158ceae69159995564 30ced922d2d9a91921b9e05e9f665511fcca6c30e0137b2d5f290d32947d660d 3cf2d7f7c23ae3372c56dcf966d10a9b509c96cc09cfdec5c3301da541579e0a579709f793270c8d14234c0bb25ed9bf9709af8302cbc295bd41aad5c1a5430d4bc259fbc066d9b2fd951b2b649bdb656ddece9721969653ca07f33f2c6b7c04cae0e443202aaaf8b9b04bea326aab95d30b79ca8df2ed872d46f584cc962cce9fd01d63c39aa6583eb3d91dbcf853597e7ab073dd0c77d0f9052f501d6e98096af9a46884f3c42feed55c95e4e7b3320caa4f0c50a71d33bc47e6ea6586eb0ef374ae381ceaa8da9137f3676e1eada22a4e4aa93d7de6b928c9fcc6cc8e2c06dfda24fda0a087b2cb27fcd796817cc8c3cf18f990033b5174ceb8e008737809c9b668c2dd89061fed5b1dd262b12ca08b8ba2dc65dcd03711d5f3b4812be20c72313e1fc6ce7002ef277e1b2227f5f0c3d3aa6236fdd7f7d7cde09259d5860f95b5a13299130697f598e829be5e38206b85e5364ae471ea13748a13412e53adf05d0f9c726d7db0d2e2ae5af9068e9fc85c5c1675190cda0f6096566088e50fb80bc7487ca903757201384d6b153c78385184ae580f15ac5e630f9b47c53f0ff036e5dd31d35f15710e054da21e8df97d2e247f8ebee71b84859282b895540080a3355356ff6ce6b1706d65c8761f77c9624fbd446ff0de47b5fa791d45d70dc354d605ac0b383b5e43932a69d2832fd2481ab050452f3b1a599f149e73c2029323aa23c31f1c36943389697a1c9600bd50f42b0196569581212a070ef41c02ece1190af52a53ba0518f7c53f178fe4c218344f4078dd6a2ff1a47861cb690b270c1e3df89e4312e47140fae5e42d165e7827a6fa9d7c8d4b32768990e59a0da8ac91a1f6131534125eab29bdf67caf9f59bcf345ebd6323b5ff0787cbf3b0709e76f7721d49653a0b1c2de25f22204e8a5779485dca941a0be01b9fa884304dc8117519e0ed3706b23bf19ca8bf9abf2756ccd02927951662b7824faa0f300c13357a8796db0fbf80a7164b19437a1d6dbab50596678e7f9b3c8f725befb03e438d69a606705bdde728a5bfe510021a48bb05d02d5cf583f23416f7506b3032ed7485041fb5bf8362dccc217ed66b04afb45b9de463650f3a8f2c3e52208080bf66f8c6fa0dbfe8d8ac800aaf40d1e1b6c27985f3fb23da18bc78ca9940206000ca0f511e2aa3bc5aecc5bdb00a50f56c0bd19a3505cb02ae3223429464e0950549514a161ccc7586635296f1030fdeb2c079116cafb7680c5154a7653f60ce44e14788aa1c20ce68745a16182efbc7b433f02a3fa7cf39adfcf9cc47b4802462068a5ee1a12f47f53a3001e58a3841eded63db640a20e3f998d1d4235e2048112702940c8707217261b36c5d69052df0abf9b82595e92b9151e5606e5af043e440041a33ede4bda674dae8605ae7f0be1dd54efdcb97a71414404ec7ea20e false -check_ring_signature 5eb36f8f5d00bffa2803883c08220399b56f7e19756d2087d85297a387ce45c2 a894739adfda4613f2dbc20f405fc7c046937c67230c9f9c5c89a558f6c2d47f 2 638e4e660b5132123c461bec7b7ab70ddd7c37c70dfc301f1cf375b0121e5b40 527d1b58fcb11e4021ee1049e3e95624cdabbe0fd21d5aaef834deab089e5d7d e68988d7953791ee9ddd486c2f068a61d9ff20e8bf28a95f4f8d2d933ec3bd0443e0d1dd5db11b6b1db8592f7316071256c77f7ce20ed59c192408231e364c0d44a9519e9bc8ff18903ce47e89eff3f72d24b8ebe745d23c7213b11fb0e0f402f650e232f2b825fbeed85b07473c4444e3476beb94ef8712ce22af65f69c3746 false -check_ring_signature ed45b2cbf72203a24e2fe680f8ac83068accfe60547178ef6b97ebb189ee12e8 bcef8b48e5b025a1759e051c15d05dafccb13b0d22559e750701fcc1b49016c5 1 b8dc1af7d36ddbba51ebd223f961faea932f6af791caec7b3d63af1c33912000 ac61afe40282eaa7c17e053197cf1fdc3666b5e1c01c8fc4c8b1ca7d59922f03980133686f5e0bb942c909a513a71d70562c20050a47c540ed3154ced935e906 false -check_ring_signature d4e524bb0b72bcba13fceb1506b7aa420f3c297b3a1e8e12843f046f2dde1272 fe7047684de03904416f7e9bdbe790c2e3aba6be619286c46fcba583bb07f125 3 3b865dac2e1346ca84b9135eac5cfd5666f78fd45d7db4e186867f655ab2fa1c 65accb09d010719f7f3893f14f7c26198e795ff36e6f4be5235eb9a3890a0da4 2c72ceb549ca4aba882e73d90ecddb4bd1e439c8486925862e88d9c8f388365a d5d9edf1f52533107a02bc5d82074843267355e1f2265f739030106b41a47608bee390f9acd34f16ccb8893a5770c6f9e7d4c889e12af2efae3bda85bdaedf0f4dc898e635f86690b0905be258b0701c95c117db937e317a6908307865fe1a02863f0f9a30b4f6cb71add11e2fa4db2a27cd067caac0ff54fd68b195dcd40806438301be4a95348fbb850fa11f42a6b6f47a129e26afc57f08f64ba66529430e4fa9fd2d165f1f92d12b8029c4d4367b95de014621f3ad3fff3140cacf655008 false -check_ring_signature a5f65aa883fa8ed0cd4fb38c9574a28c9866927b00a55f18c8ab090e9b1f0d91 032fb1891a05ad610173f19efc226e0b0a3fcd71a8132344bbc219f9b5936140 28 b6d6a6855431d73325f286e13c7d8d139c55755c4304f2b5d44b679e8616a54b b96a9fdbbe7ce8ec359d722c74efde6d00f206154a35343b67e22db57a6a1bcb 128b70b632b079037314d5291e65e7f55343818f271ddb7aa3707585ae6bc152 8ecb7c93e5eaa97c955d187a13046f8d6da02db4e9562587ec9d0d90ac786937 4e5bf46be662cc0bd93075d4fc55999442eab852e67a98713de500e2e197a4d1 6cbe306f57a8b0854f6a8037f0954176debf0a75427b6baafc548ea7ea547552 f948ab61f2160fdd0e2216feac929e5b4c980baa8cf81b9a731d3135e5152a4b aba09aaa6acb7a2ee0dd9043fe09b2f2e2f0953f22dd7838521242b34a170fc1 654ce03bad6648102ed56d19b734bab8978ecdc26f13f7b8e32aa20f8fa2ff81 e6665168971a93c44bf37b7a7e12c3755b03612bcfd1e7c865e474e7cd65342d 195138dd7e7909af2802db22a854a1196c876664d60376a60bc10e6f485a49ef 15d6f5a14c05f95ba4e87c6c1a8056fb85f182c116fa5b9b0960cd856a599eb8 953a629c1c66f17f3bb81e0c501b7cef11fc462ac9c0e95545d1e54d22a21b3c 1530c82655487efe81a450788d6b7d3cb5d90b02a46a774e9f06d06cef98abc4 b00e88b3f4b08b3e13b6899cf0e20467e985b904f286cc01cfe45d996e6503ba aff0e4cbf2ec83375628c66e4feaef4e78356d4c439cce282b0eb401ab9e1464 266b5c381d58264331396be04adcf0983cd6ac28887a0008ea1ef9c7506ca695 09e5e42d394a84ae6bc54520cb1996fa09071dc638a3c24ce1be43bf4b198335 8b297bc7aeca5294ddb429dd328d6bc6ee15b48b3155d651cb0cf8f205e593d0 b9b07b10749955b1cc49c28780726bbfb37ef6216ac28a174ffff1fcd4a7157b d260a295da11714fe2a6dfbc528a57e8028c1004d9673fd0dd80d8dcdb485af7 cf3d78d49b59eb583ac29e9f6a730e314bf1417dbb2d71808957ac2be84c54a0 bce4140c97327b6f601753aacb9ec38ba6a6b362ff4cb601be21390fbcc3e490 b4bea55b6f5c7d8185c5528b98a5d8187d0bc6cde4089f825bd51b6b48039184 75d78361032d68cefb973624c56f8a2343238f3792c4ea78c9d711fb47b82d6e f758cf2f4a7fd05b3eb71963f69bcab2e4d9bc43832d4ab7d80fc67d30f6293a 474a808433e7e2b98d369e472c5f35d467f743e4441888d6c2b0200ef798914c be8384b04c1d119b89d3b8d458af102fddf8945ceed0e7464cdf49088a01b30e a287d47bcc263720a88204fb00503a1e175e0033d586e8b817b730d22211c1024e39b984d225ae6e03919b4183af5f4b52efe39fb3fae63ac7082c9f0d18cf0de0eaafa3141cb4674a724726615618f385b42eefa50716d4decf43216ca625067edae189c1db896c628cb722f5c2ba5d617181541d4d4c6043d2988bcb59e40d74e8429979dde153ca276cc0ccebfdce32372fab6d765786f8dcf70acf2aec0f8e898c0ab6eb0309057c3f529679f0c4377ebb00f7f2e375e624154cdac9cb06f03035eeac1f538f65fc4ea1ce9dad2ecca825b7cc6932581554953983e343095bdfd32fadd09f798dec0e049c33b687e96d22318c425a89c6f7733044172f0cf9ce3ef9355fa616a6f749397e75ffc3c4fa2bb09f389933ce117845782e230a36f8c9872e576a9bcc77f41c8efee691bb9a79923c0e55690f67e73013624f08eac52d71ece28a751c3deedc0eb57de3f0e6d3526f843b242bee98f97f4ded0a51d3caba336504f15407fe54c31b3d0cf40c3e4db33261a48a78c3ea19a159009767ae24c34c123604774e3d97c33f770588d06174d0c4583f2192b7f9b3660d18b1934cc7a18b76e67e5144c8d999a537e6e5303f983be04dc6afa60eb0ad0171c2f05a693aa2ab65d2ae0c8e96ccafec0c495fd0797ad0a6518b12b6b488007774e1e2758fda32873f8f49862dae9212865aaf8d15af040349ef9152a339038398fcf9e7d50e00c8f759a902ca307a61bb9595487942b9cebce66cb4190708f79cd9d9577f200c8070765231dfc500667b06921d1896be682e2713efd6ab06bb519f28e41872fde9a539636f2cc60c0eca47213264c641526c51492dd04a0beec9e03348751a84efb16de5c5110a252bdc543b68288b60d76ec08fe2611109357010d9406d9438b8af0db160a7d55cfa78dd8e345a3bd19456117ce9f8fb0f58a3de83f27a2411a0af608bbbc02ba8995a316c95423a77157b790eae13b70ca47cbde6cb9e33561edb4ed136514a65d7b86ad5bfc33d1d09e92ac3a1db3f04115a72c71e3cc4cecf4ebf4d4910dbbc9e08853f22908f64ef2a2c3184f51006f35f217bc5772b9039462f17cc10ef0e7682c31dce57758e9752de3229ff2705a435390326853479ee835b3aa3ec1b3bfc0a74d241a0cd73bb2a90ba07db9105af1c2d263c6948f07b95fb8fcc255beae1b23a198fd514b65c597f2642a6120a7107ab3145aa186f5adaa514f9ee5ed3e329f1936bc2a9fbb0dbbbdb71ee420673e8b45cf9cd0982e01f3edc234994cb0df06753a2975700490f21bc5b912b0704f2b104bf7dde75ea81dfc9cde54b3a5136eddad0193edcca61a5855de8120790bab3c72ffa864a2822e68b0b42a059d22a852d13ab7325e01a269819769d0b27781f063608bcd3e1726d0de35293849639d94b96ff6cde84def9e97328340f8943b21522ac37b6f37670f9b822d5bb783a5e52e1e59e1438046edaa44af20ea0a18ac1139984d6b62319646311faf833ceb51023e01811407622b9e33e7c06cd6e5204af1187329710e85f49f8a2947f34bdc627402bae9d78d377361a6c09d4b599bbfcca3366a1b43204d1ac2fbc698c12fffb40189d187b36cf3cee7c0b448224320fb913b83adc18d836d18e6489b7d6cd5d048d1d0f7110ad1ba8ff0d4fa35c50ddeb5d2d1839017b071c8207de72070e17e75c5b36a9232f59f70505af76cad30f027c52520999a9c4911eb44ee9d11d4d234fe259fbece517fca700ca36089dda7f7c3123709165ef010d2e2f3d909b12e8e10a962d9f681e177c0bd203b6921e89df3aa59be9a1a14db5c9233386ba01cf6411fd19c6c6dcbfe00dd5ad80c754e067a9b84c95a11b4872defbe4b013fc99be00d02bf4bed85b8805a417687c766d28d3a67c1a93f053bc53973e36b04a37c2f2887f04dca8b24f02bd4bd5a12a09c1ad87fa4daaaa691dd8b72e0a621945bfb685fc30d1ccb30b0167f07e176becc1a034200533aab7452790d9ab86b0f8cf6f9ac9290da72cac0de473278af2d0a4d48d491c0acaf75db3d451289ff3daa4cfcded40b36cda9404e0362bf865d1c7df6d7cce198b6c40c3ec41e8bf3c3a936a5a439380897f440242334264c0ba2781feedcf6da5dfafe0b5fb1a4f7e928410e2c7e9f7d7fc7104426df2d2de2f4de98b7eaa15c38c9d75d640bae92ff64b6f85a41752ba657a05115686bdb155909437a97606b3cff98ad7085ec59aaa99c1a6dbef57983ba957d91b7804f9e515b8bf08f787a55df4171111283b13ae4be64ed5d6aad6d9d802fd785bb4233c8bf349e5053998b59f67ab8bcd7c2d82524a8745fa66dd311904d9bf114696b44fe33547f7b64cae13d9508e2042a0d602faf4d56a00a17a593c287d686090719d7c5de290b6e1842089495d14e3524f43e074c7a3b7ea38270e4b23cb82bdb39e82cc3be08056c02deb86b68bc5d4fb5715a7375a33b20522060684e7f94a4475722c8ebd4a1bd349b404a70f6add0ce8e1d592765f724b8002 false -check_ring_signature 1caabfc4cb272a1c99233cca2040a72289f7b2d1cb61392d1715db9c19639313 d13aa0d6bb7d3ae4cc10dc6af4813166b906a0662df996bbe004a14aa6ed3033 15 c5403c494e60894b1e6d5d1b72135195c1cf536c11313c0913e906a8cb9eaba9 43883f2b5770527b10decd6353dfb316b2152f1141c93571ea08a70cd3cff9ec 93addfd2eba26263ca0e6ea83ff4f90ad5bec1888462d2d2beef74ba395cffff bdc039624b610ed87eef4336460ab5f5d20731e861af9d27a4dbf71695a626fe f272a3d40055223ac5accd20cd17366a1563f01b2263a3b82b858a7dbc021aef 06ae6f67ec5b767d0c0c6df6041a455bdbeee017de33c014260e93c81cf3b478 30767f6dbd5197c05f6c3d0c92236291d249152551f2207b4d8b54dcfdd77b5a 2ebfd4d85dd3dca9b2ab7fabda04611a4483faf5a41f9f810b00676aaee7135b b2395c87bd8f3c1f22fdd49779e469f95cad7a6ff17805b9de5e093b30bd23aa 56ce9e1d056d6111a703ea415eec509b8c9055fac9253511789dd035edef6d0f 6f3cd2cd0a22aa36b8386ae55143d4fd8ff36fb288c4b4cbfb1526e37abd18a1 519a6c32cb517b08e59e8b5344f288a4be5cb7bc72d10dbb701d4d902a1c597c 5dfba5853be68c2601e436f494344638655d0adc4f7c2a6359ed4ac23c0b3a89 f57a910cce83ca989e65233901627cd8c096be137313616e5a5033a8379ca2e3 db25128fa054d0b4ac4c595f3ba98b476d2844c3205b6c75710536823ef4096d 134bd8356bdb64dbcc5a4db0aaa352966283471599d1ea0fc93af4ad6714a40923e317630f455fea731b43d88033874d1ce7fcb712b53d556505466f51a6ef0d6936bd39ec98b4701c467b7415b670f957fbc0a478f336f10ff7bd138315310e043af3105c56eeea0b554063198c5bacd8b486aa33d439e7ba43877dd8d5d90420c6a1ab78aeb4043a178ba8888ba1c49434879e41f72fabec8ca5282c43c50aa6727247898156e863c06fd317b28cf1ce3806f078778bf20635738b67ffb2075a330165415480ffcf175f7d7ad379991a2c89b4b687360eb62f2a683121d5092c7b41b0ca36e034cc710019b3df97e203466104ebc8ce744a9570c9b23f3e021bec755822a22cfbe9091d6b10975ab7f53b0575ab19100fd553e6adc31a7b0e21a136b5158d611fd8b8c973b57aeb195830607fbcea97959a9ffd8964a99005002e76de25b2ca445c19cb43b90f8de9ea37c6a25e9c67042836be44f06386081f6d9461c3b0879d9c658afad1de6d717ac2e688fc11b653cea8c1a4fb1a080694565c609035dfbd7dab4c7ecd4092d1fcc29d4164cd49990d87694e667bf902abe7055b5f5b9dbfe88d51a50e320ffe38ba821e595ab5b483872d795bf53909d2c45e246dfaa86cbc3713781fab64273a2743629432db4b7bb5f6dc4bf26e0b262f41e3c87cdb4a95f956a3acb4aee5730f9f07e350b11b49a6482274ce1f06b213566722045e81e0414b0960e10b0370b4b5a4ecffa4e72d9b5ddcf0d5fa02f893a8e656ff3b31fb29fa2c7d68eb3897baa1431d45a987e2043a462e16ba03a556f72c2554be0c98077a31235958fc5b2b80d21dd91ca425570eeda8fb3b0554647564df5ff1c8ef88d20d26a7b299c08c5e702724c835db54c31f290b750be79b7495a3fd8854a4b5dffe86688282cfddf4a77fd31ad1105976d0c7714308503619436527314911e37b9d12c5fe8466b0a89f10b1c3b7644bdae40b1640052bc4a4ccefa8c22987c277d3cc0dab7e73bfd7c1db7e1f0fcace077be70d16015d1c7f211981d84d2dfc0381aca1f204b27b02f174d8649162d652fb7176fc08e45b9dc79c9109af724f8c122d8b5c6a91aee2f0029aa5ab8661793bc3a41f012a8f1ae8afd8732e6f0787b58ef09965e7835bbe9df3dfc5a37dc675d284a30b53c96e914b5527289813980b520884a86d4008b3436b70186dc60aa051530e0552ff36cf34caae2c71fe89277354d98f45a17926879262805435cd02e3094803047a63b1d96d28fd287504544a95d4e1b8076f501f816b5582535982a1cdd904ae618667dcadbd98cdcfb4a2b8a807e32e7b1f0b09b8f67b17a38f3f16aa280d true -check_ring_signature 14cec7a88b1db7e34d8255a80ff4008c90c89d2936c2c01a3d45d129c4aaab7d 78b8d4a9e2d0ca6741073628ffcfd8a96e2f7f58fc492f4fa3270ab5ec001e8b 52 ee062814248864ee2e23b1d175466508cf1d872586d5b7d9538ababd5bae738e afc4d7806ca36222535864ad3573bf463b305560b252cb021ae864b4147a2c21 b8a809e7626fb3edf221e5bb666f278a5abadfb108ffcf130f5e041a7359fd57 1a2b8cdc12df83711aa55dac0cacf32114097dcc9e2e3a001cb593539ef0feca e4aa8a4d1a657ce2afd8571ce52461dfb263ac731abd185aed7e290fa89676c1 2028b043c8b597be5cf08dd087db0bc2fe1a85f5ecc429cf784217529ad522e8 8af88a7e0dc9595b37a9baa1fe04b3a2a9be9d7206c5f2425bb19f79a363a69b f9b79e1eecc9f6c7aaacadd19341ee869c4d9f68f099ad16d724107522d32f0a 6acc924fd126e6202d710052406c2aa926971b9dc1664540fc90994c2afa4bb7 8d677ce2bf108fb1ef3b2fb7500ad61f1529aff9fa5e6ec67b6051a1bbe183d5 6fb28a67377c073db6f8bd16ad17756ef24b213bd117cf7ce8fbd9ab10bc6d3f c9c2fdac1ffb0c578a2782c7d6d28e06ce51fa211dddf88ec665ffb588847503 6b58a03a8ee0f91e2f8b4e099799f4ff8aa38ce95b54c04a794a1183440e7377 36ded8c5b2c38be68e01ab8ff3f5487c5088d59f1944e40c58ed23a7b0946adc 3f9387af625aa07bf633112abe1f56219a1bd330c8eca6d9a79dcc2c59dcc283 7f6d825db409c382bacfa1eda36f298ed07b5aa6834ea2347a2b30e8d39f0b2d d0caf9510e24a1d7dafe61a6788e918211e567aae8f29824183433c6f0f90994 34e6e16ddcc513ce8ee88463a075b7194307d319f4c6012a3cf8533993460d76 92c1fe3d0161d6a112bc7804fd2562effe4bd83a41fa3ea5779f5994276b0e9d ee4661bbee4fb7c47a6ef264433f4f5194d880c8f5657c3e7da226b1c95a3879 0b5efb4d7e9d9638317ae773e28760bc9c95fcd205f398ebf84573863cf6e2d1 fbef628bcbc50bb7bed296637275a7e2bd5539d6847d0d220feb647db96d7b21 21125d1db675d37d2ce8f72fc4c14cf57e4e40b2b7e57d9828d7660f961f5751 dfca9b014ebedcd1566f9bbf737b043fe1f50411140639c381b6fd930a4f636f 458d9e5b968420cb22025283b749cce26c1433ef0343f570cd9d052ce8ffd8e3 7d16800adace032fe6a8334ef5638791415f3c1a1482c34ceb766ef2b407cb6c efe2da90c97cbfa4d96c75bd37aca98395e9498435e719a98823e837e7d3d776 30528ed1cf5878203c4a7f52c5aa24463421f8fc1e5ed65470bd42ecd19b9f20 9644aa60657df3352542d90c4208ff8da8107def4a40b1666532886aca7536e4 af9ab06966636864f3c3291f40ca337b3188dfe6bace350dffe8093e1b2466f9 930a0493f875a4bcdfdb9e8d21b8ed861c464c3aa15214e0126a58da54f2c435 273c6f065f604b7a78ff91658253203102a3d69c643bf3be7bdd7e3387ef6cfe 408afd05b348b67b9d1b6566d299ca0c9f55ab9971f59771076019da9b95a001 3006d42a922eefecbf8a8c70a51c95a5e81d24b760511a703936040bc9747d06 8ebc392a2763b1891e0b1e997e14e0129f08de1fda72d01597b44b96af08ac32 6d651593e283cfa6ecbf572c8039234d7c3e37d8aaaa0e3566a324989b1976c2 f9c5971fa816f5377a5d69303d4a65c472e46f85422a70745545e9e371bcc57c 942132b66e729c5ed96f136a5421b52a313f31cfae70c9c6d7c668e6da5f154f 2ab986fc91e88fa111e2e917b69d1240f1697c1dabf14a14d2dfac083ce0e200 93f6da75ae84168f28aa08d7e1083c72943c50a4d81d6f2c8152ddf604c21c1f 3da01f20ea125674dbfea9c5bd7ed685c0bec63dea3a3a32a33a5ebc870f7c1e 7e6c6ad8e349089b5b2f76b264ef65d7e47d649150a7cdf38f4592da8dba2baa ec474d5ddcc892e8bd49b2fafd0bf8ecace8897fdb19b2c9d89a7a30dad1d1c2 0acc8f468ba7474c92242204055a23b2b2b7bce384d3eda910f102ccbf0f1379 21e2c117bd7ddbc4c6e55732dfafa9cfddc52cab5f70803e60f52eab332ba08e 97235a26bba0fc5e1757e185461a321b08402369bd46df0bdd59aac92f4cb301 8710afae0a79df4322d26b333cb90c5d58bcb5bb230ab64737bfed9b88722534 0214dae84c9c6547f192cd2093158bc4ef6eeef508ef10d7d14fd5043cc63e62 773a40017e4faeeb110098acd0fc4debbb53fa8af77de58fe0b3179ba78a0320 1f76ba24695e7975b439446d5a5f9e7fe8976b9b831ad6847ad44aad92a1fcac 050c12a767b179b0c75d6d2491b54834256a4f9eb2302953dd41dc358a4fd8ad f8d8ee2137d7e6864b8a955505591f142a4a28223d1b73dec189267316123ac3 01daea3ef58a7d2445e72cf48d384f7d42fe755d17d672fcd79da2383f2f0f0872b97500a439f8fd8693de709c96baac7a0836391a9dcefc323f73dec1e10c02a2c375a27a4a330d6afb2e4c4fe5ccab7bd252bb58e596589484091e84993606de22eafb621c75188a9812e6fb616fd89e0e7d96709c8c189e8881271761940e36944b121e97b0a7a6388045624e4760c0b8e65bb3a4abe0e3495f82c15a3406a9ed514ea55b7d03a6a93cd1bdb06ed12cfb4105491e5144f62e9f65d9f3a00f3bf73c31ae5aa097e8538c32827687c899e9e5ce7770e9228ed92ab1bf7f21ed9ae2334ca73796a05d45cce0503296c6c5f180ca997335d9023f04fdcbd2d3062098fa36667d46212a676900ac8628a81f2eeeefac1b36c248ab006b0c1bf30659e358e16dd23e13c024bc9c0b5403b221c5937eb0e5a20fabbd5c2c4b6df8033986a54de7f282907ac57673f2ba9f1b4faed1b23836eb3d62274cba4c56750e0fb2528080a000659fa2a524b29f01b2ad4e31bb405d2dd9a3deda74dd54aa0f41da8218f79d70493e145ae1b40d60e4c5adc80d3587dd75c14ed65dc52bd80ee98d28d17b6770de0e53b0294de012da23446a189856d465940d871998e7970931fd93ff85cdf36e74aee2a57a3d55c5addbc6a28e16c62039852cb55d49730756abe724f2c20d7d8ba9cf4fe13cdd30a504ed9828cc8d11e48915c6106f6e003b891fa00ede9b5d3d9a018fae6577b72e5c30aa987e2c855fc710c2c6040a02b5aeb477b3f4ff02a59efb26af91d809dafaddecb415cabef64402b95b41ae01c39a8bd4d63010fdd5d252d71371fedf5c7f67b545164ccdff82187cdfe1f00932b7dd65b6f1a6261d595641aaef6ce851be4598c9c8af603ba967adb56adf02971914e7aac5a1b6a26f26fcd3c1b7581e76af3f82180a0b45564fd64a102a0f3487060c44cd228c266bccaf3111066c351cd9fb9f27f42ca61c1c035302f101b0d89099b5b5c3bdec064c0c5dca8aedda39eb01a3348b77ac86cea97aa88b0a8afa3ecde64671e98c302576e943503d2fa535fb24e7a86f1438f8f569cdf10b2e0931afa2a8e83be394fa2f7fa47616f81ef4834279d1b64c93c0b5c7041d01b329fdf115ca57db0ecb90bcf51307b96c36e7f720592b122a1472f35f698c09ac1d3f1aa12f4de9ea093f05c5857e24a2b7cbe154123ccbadebb16139f9b80b99d99941c5f272a83e1d98f48dbd01870279c31cb435f78b36d72bfb5e619b0221e40c791133cbd5fa2f54bddb970520b2ce19b4b43e40ec3a961c8e83d2700457be5f370638b83dbc9480e91b8b0598b4d5cccbdf400dfdda20c0586d004e06d9ff76bae85bbd1110bd9f1ade329e443a15e420b91e38590dc4bd2935dcdf025cf07d540af67ceb2ca750a1ede4b98854dc17f1411ce9c5aa5a20593d8a060c64c638c7a8773abbeb53e63bd0e7b33fc397a3707cd4691b759cf2c79ea20405c9d76fa8cb5fa08f7254b4e34ea06f69cdb73a576fadd1ab3dad1c76fa690501d1ccf11274497a2188e8fd054785109451c8ccf8e026db38717c6804ad7414048a7aee4886511070399d4ee5ce9c252b7a60db7ef1a3109221a4ce80a5f66c0ad57d66cd9c6321c77d0de31adfa6cbce73dda15355ebb20af7bd02fe74366b0e196b53536f7031ececc54bbe1b78af036d2a29ad60acc5cbfcad5d1510ba3f0fca064526163d346dc12b101d06cc9d1380ff2f989db0f67bb7a62929e6a1a00ed716bcdaef99e885b44391d36fed0859e1f3bc4a7eb0ed0c92f4c4bcf8d52302dcca6204254f214298c0a13c8bf166ca8365cf2e3d912539efcd486de75691072407c4d273734484e23b3a47a01138e86d95040a0dcdce16d45a35a4f49ef302e1cc891992635c6f8c1ff095f6458e785c7ae6ffcc81202390028be34adcb704e88e07b15bec6129d5167eba1259e69371e825972540a27790cb5a6e2b8c26018df3e99b81a61ade1321a8803f64e66712f3e52676b7eb884cff56d74ecc220377495486a9b71472ef0fbc5d711e13bbc3db9d4f1a5c4d3872631c89bebed303a607ef72da94f0dc2c8666979748bbcbe21c36880d9198b8b8dfd2181c8afe00126bcb2eeace74adece3b3821a4a8ec6d5a2a06c98cbbed70d99689e4b2dc2078b052878d2ac235874946ed831ac309f446902e362895ad375be5197343ccc054f209c34d0d1d2e11299f9a19c1a7b944566f2477af5e50cbf2f8e9e577c75083d3619df431086196f8aee547265e9ed757bbb43180188d7a92c1093472400032b2172d84bd07b1cd3fe6db65a6ca5982df5aa7f6182cc0bda713b4a361def092860cec189b13699e9e7a4e25a4dc5a64aa16063dc5d912000e44ff4b2c4b7043bb1209d6123bc649354e414a7088a86bc383c41bcb4a83e8e39d68e79e72b08ce02ee14497236e0b3d0fab4dcaec95c881c6480885e744d82c215a76d78d10ae8a299599454bc5c9e7ce24a15ce052c062dad2bad8f250e3a6d36a283955708dd1ab4f49f98b6d1d195d6e4f0947bd3c941175f76978b2c49b92eb32aa30f07fd413430b4e3f8a92b66ffd192c8719c49671690c1fb0386a831c2c746e9de036900cd592005529693265fc294559ce2df26bc78bfe5fdce257a20caa7fa450141ef640f5b0dbfaeaf409a15a3878f2cff2b60686add050bf44816e2f739180a59a3303c4eb2bd89a9ee49b461f530d04e2d450a4dc85d0f159195d05b73a10635ce6b45bbad1a595519d49581adc11da35f1ef478c04be2d37c1d6735afc907c4e3d3a4f35329e705a9360ffa46ae95e3e388d7e297f7fdc695a4d79fcc8d0cb6a9aad2dc11eb185d16313b2ec4aef9ec324005564eab0650ed5f77a79f2f03820adbfbfb0c621fd6504779dbefc2a680e22ddda899164694669955e93257055c670525d9052d53f8939166c2f7acda544ae88befba6018fd53fd6273ccab0feceffee829857e3bfb309b00ebefc1f45f6949734912c6d5247c2d76f32cbd0699b78e79e9a9ed5b24c6d6fc4ea304f3dcf7f9acacb9e9eef5b7908657cf3e01527fcded0cbc8563a2f2cab50074e2f6f133c2ff545716631cd486312ee0bc043ef608e0b8ace59290c628bf3dc18c608875e16da9744349d2957f649550c00ae8ca37952113f28773dbfcf4ca3b4a19d7f6517df3f937a84b51a056db2f070f36cc97ccc6cbe8587ff2c5f95a4066020d55ccad0ab5b3544e023aba5f57530727588af49b601521c6796f2ce2b8a781be9dee799f2011d12e0fee8fe46b6b0943b0efbdd6cf91959aea331bc5ba5dedeed628d8030b7f5726c5e588f1897e0401b808dc64f8690bd3e8f35285648c799c32dbc08a8c982190d5492d7559a60663c8064f2339061077eed6cbd482afd0cdb584d4e81f4999a78364eabe2c600c7c823f5a0747aa83a9c7da668fa0ff69db5ecacdb52d38b6f3d16cac5a97960f725083360ec7eaf3d01ed49437d5598e3fc12a7e89934522429c001df4244b0bb122c58171857869cd57fa06c43aaa16965e5ff3e2b710ce256133930e39e407fbbf1e4c03515d35eb478f6983649b05539584e4a8b78e4730f5271668f4930a5fd184a56c90542c37c5e94e79b3bc9ede657e417e524ce1ec4ff691ddd72703e2a04756fa9ad548270b08749700f0376645d0022a677c6953f9bc1c9bc43e0f9ad000891caaa8f2dde720943b57bd88dc9a785ee525f74443f67aa70d7d16081653b782533708bb8cb10d666bac27fea0e7c5651cc520ed90122f95b17b83077008d6bb9f57ac1392e65c5c7b7fef4e93d2d3c1b6afbd765bc6b19493843d00d0b873644cf9e4bd1333d9d75b1296d6e1e46fe1d12f6f4b223e7b3c6296820bc5999ebce5c69c5aa7c9741a0a47542ffe9b645d5843eda40c65bb72cf76df05d1dc39c61485c346855912adda4cff862e6cbd9df8d114474ba960449fb3680814fc5f52ff3b665cc7e5e71af59f1bcef41ffa7700b8b7d02ed4886b7b8b8205589fad09b643db51fc8896dbcdcb553908824e8376f08ca0e8016fe21507e8087b23336ec8bff0618c26dab61a455335baf12707033b5ec753cd86a3000844072e91374f8ee78591d7564d55b6f09b5adc827b22a9d4472e3d4732cfeaf09101481f167457489ad0d0d4fda7bcdfdee1d34ef0f16fd65d5a475592f1b8ee680aa74ede53568ffe9fff503531bcc4f6156fd509c6aca1e23f752dda94b2549f089264c8b66d98a611f1fc3f1d3706a4b72a13ee34b2ba61dfd58a838e8ed4d70e35c9a61f4e05b7dbf3b5068bef2ca3c74c932afdf5895270a54d1ae4af284f09a4c3302c6fc630ff9bec5bef6bc4bf1ba7673df15ae073dfbeba6c1496b68e06346f80265a0cb5bc49deda591883c403465a976908a6e1c726852cd14b3a600d7fa49b534a0f80c6c3b81f99a0b4149a9fb04c9bb988c4078c02e76fe82b7b03e2133ddf48c187a369bd14cb688ae8f2aca0bbb2860e3a9a553c6f15e3b0500234aecdfb54aba49518cc20ed16a6e9812c2ffd0fe75b555fa80a0f3b1c87350aa4a121fb6934a400103b2d3933f83bbdd9717bfde5590335b5fa470fe554c00442c97990cca44acc8e7ef04a7856cb0c3a3e33600f347497079b0d072e18410559b9ef8ef12504eefbeadd965f22a57d16e4ed13efe9c1b23745df9f6bc69e0d false -check_ring_signature 762bd3a06a4dd56358fac7e77977443ead6b3f5f378e4baad2b67ede666d196f 900b491998497e9d9bfef8f456b4fc88d3bb46aea910e49a90b8c9198804a315 1 cb220a8f68667b8d0f62481fac7d6c58db4beaea7897442bfb617635f7ef589f 6b10ee00f897fdee42a8ff346db7e8738fe198720018d9d3097eb8ba28fd7506251676f6808cc6d8ba082810579c375c18d347fd8829cf5b8474804774f9bfc7 false -check_ring_signature 4033f0d0bd47a2dc672c26f413aac0cafffb682916f2bcc82bc1c3a1dbbcccba 63c5b570dd3ebfeda2e636033cf594057bef069203bf10efd32c0bc6a9490fd1 6 0db4487116f0ddb4057b19412ca18f987f1585a70a4c747b0526d93a87e896c5 c72ad6aeb7b24f270faaf060883f1fedc7e78059edb4374e79a785701e9170c6 e60be903e12cc0a4d8abec391e509d2afa1b56fba22e4ba55ccb473cadac2182 8eed5ef33c5322d87151cd5e40b4a45355509f5336a1197794c4fd3086324106 ddfdbdd980ea208422f88ea3e25f99920795cb79b5cccb73c4c6e478fa769f2e 61db8c42ab1de80873ee4d84936e6f823d14a1615e5fe6a2548a345bf8ae121b 6260f4356da15ee502c396fc75a5e674ed970d0ac1ad5cbc7a4de7ed73e9fb03d331bea8472f32c932ac9c297ffdc270a0125ca4fc86c9b2a46621cbfa6cc205aceecd7b8fa0a133bb00013f7e9769431d29f550c57feaa435254eb50b17df00ea4259dc0d8b1445b4e283d428bf1ff3916faed80496d2d185d7b52f04a93700b51251c2d70fb3a13018213e072a9cfbb1c93e313bbe9e335c5a2a042370bcf53927433894a30de6953c5100ce0819671845645b89bae7b19f156958640f116d23cc844a671e09724e04a26c0e29bd1a1565e7397d7aa005e17dd34d2ed94402f034f5ee2965b38fb40761d85900bbeb2fc4bd735ab8db5b087209c4ca391e0d9c3b5a212f2ed6d3eeb25c8b238b071d1393835b417ae22e96bb7e620d8bc907ef11f2511071428942fa4949c44bbe42aa43720d5f72d8aeec3c10b178e9720dbff8ee6457e2c5d2c25e0d9daf9b44820effa14283cd6bd2db9a9974e6ccd90649dcf3944c24e81f0bead30abc44588af41beec32b6cf77d81a67c0ed3b49309 false -check_ring_signature 67133b9068d59b16793b9558054b943a87c349841763304605b4e15b8da7e23d f6cae888fb80bfe5d6ad155bef546efddeced8da0b88e870a948cb9d403ad277 1 22bbbf6c9a6fd8b93c945da2bfa6850210534c8836f1e35c7b27687be4f2edad f0cc521234e66b2ba29cc0f78dd0c4cf728ccc7b2e44bc0178423e113871f1031415b89739c67173b3e6905af77e9c3aa65640b0ab05186e4e234d87cdcee90d true -check_ring_signature f3301a5b608b671d6d97d065547ca3c035e35d7d98d3c6d8e4edd73cb5ba4ef4 e36a7d86f3d3e1c47afa38b7d2eb18c3ce031f910673bd9ed984fe224ca694c4 64 5c26929354b0ef79e6ba72cb4cff2e56f4a29eba310ab3ea5b77c88e81e65e51 d66f89b4982e68f2c9da046af1aa09120f3c7dca4173b4565845740de9bc2af6 45b10842da80737846881e9ef963c4deba36ce91ff3e1c0b80c66a58319e1cb1 92c8a40a91b4e0ad1d264a502aa4ac4ee5fa606cc25a0cf7b404dfe505c0cf94 93e326ebeebfc415ae276dd489eaa4ce83fea6593bdfb43beb6ad80ae87bca5a b0f2431893d6e8830b64aa25ee39923d954c641b1aef8a6b7667c2abe8b114bf 9908ac981fd8d3fa5cb7a2f2d87e8ed595ab581dc539b57fcc0fe8eaa0d0e319 c543eb90d34037f7a033954d6b14a893a7bc3f6b67083159ef243917b2ffaf65 732eec589f0442baa2d2248af9960c4fdb5341800bb7509d5dfb96169625d92b 5b46e9e1d8f109f48302d6b8c773181800e4056523daa1175e25ecf01fcc42b4 6906ffbc04364948409f4acf7320daf1ad909169e84eef2b14bbc71e6b54c498 3d47f89084270a72ff5cdb7505f2578ad9d4a91578587cf01b4cf0a6e95ee7fb 4c3dd143dd020a82c67b7353015c7d9cdc11f3d91f7fc04c5ad7cbee85823434 620b9f6d8153dc939b70fefd3c73c5b52a79254eb15bc71de6eea2d9fccbdba7 71ed9b8addbd9ec814669a9f005b7f8ef71d72fc6463b394368a0800aa46d34c f44e49e56a436babde084d1c55097c32ab00cd377004a2c43cad72ee468350d2 831d7fae2cd48600a8c672c6b5533a7c211dbe2eb3600fe8f1ab675199915d16 fcb10addb174bdfeb578289a5f339a5314f73f8af3947957ad65daec30747356 a25fbb21fe3f35e6fa2412f2915e3d94f38fbb603533d90e39707052900d1ad1 1c0e3cc4e36206c17e5c8a9290b47db505234d661e3e217076d9b4385fbca821 68fceb6c948c3511a86edd64c434e90349108da8359c4e55c7ddb73d98761f30 e3b10fcae480bd0531152b3276fe5625475d68e5e0b0b46f29fbedf42d778c4a 078643ea663421e5fc3512d146ac2f75c39fae239feec0b9265e59cbfcbe2926 1a0c031e82265e093e907a1f80a9ce136babd580e4087755ab9b38499c057031 3c84ddc96d2a6c0d13d70c4ad374362fb972b8351468cd484e2c39f6b7d23348 4d0f47645fd1c0ef7a072b4ecea9f3682a915789ccdff8c82d181671e7a1da8d 66b9c859f9ba8f2c49610a632913900a6e56806e5a84a13222aa25e0d8284b35 f8e738db57d7213a00b0336c781e122b6c53ff1f903aceee98a5dcfee319f944 d3b7940f07edff5aae2ef4561bcadac6687908de878e0fcdfde9cb3c6baa8979 423398891cdb4522d91ce8bf4cf951693588bbe18df92629f9c6e9443301d030 20f67526a6333a84b0c41ee8dec1b18418cac8409acc9ad48334d7644f02fdd7 d6eb508e956268fd09787a4baf20c55051d55c522ef1c74454cf177521c66d17 25213f8e3b9646b4a3b8c9e63a9094e4e5ec0c1a208626bbd17829b6f35ace5b cc6586ed17d99241de48c36c6fb1dfb266c6213b179b2b35fa054046b9050ca5 6242cbc596d77ae18afaa3f4191ef7f414fe8db044952121658bcc4d368de91b f67f79a601e2d9edab13d2e2c10d26b00776ee4edc645d0fadac7ef7b59cfa81 03d528b06d8267538cfc78cb3378d5e13734bb75922e8695c8dcfafbce9240ba ba1de7071625af4581e43ff8948b113f8f157e487cab43698336407d29d28ec8 c69a498c5362c659983a22dbbb65fd3ba7ddcfd84e60971c13cc0e1282ad301a 74d98e806448ab465a4f30cf63b3e8aacab518b74ccc08c2c24b031ced981ed0 cb028a169754903c88d540b9f7673a5abd382699aef890e7110df8bac73b5e9e d06564d3edb2225c80a7c62badda94c3134e13e2e0b5127f8e58df4ebf121f53 484128b25cc95d138c1c2469eec8182b50a8f1c23c1ac3afb73ad85c9298eade 942cc13ad46dcf06d6c1330e9009ff5b8b96f325015f41df98a879bebf467b35 6d47d4b10ec46f5b6cf3c597a46d4609ebcacbafbd0bc484a6bcb3455b51872b fa45a7286732de2b0a695f14a1dc29c5b97e498433dbc101fc12dd448f8bdd53 b6e7f9ac636ea9aff7d36c9a4d770c92d0c88bea9a091f006b460ea1f6a3333e c2ad4e1ac92a356cdf85ef34e8313b99697ed61360593731e182d451c35f87b7 39618865abe7ab4215037991d47de9fc403b30787b8228e266c975c56494ac47 9d6b7acc26b9ea7157a395f543664f0debeda6d5c9e306d7f64448f5a5573db2 82e259cad3e01024829822d7749f198653010de3f155c45b99054c78bebc5491 957d15bd22c0d3f247225d2ee4fd7068a480c5346c9c7048534e0b8d12c878da 5ecf09565a1f490808e59c65bbce23f648ae7b4f9568740466a7c93f2c9dda70 e8b75e745bbaa70cc2ed75d8e9c7e5f52c4945e253574a753c7222fb76747ed5 39e1caf4fe8bab8e58cb8c34b17101360e76895d26e8581a5d484a47db468bd8 414e059d87b806104055ef5301f9933451cdd82a77e61a4983dfd419a00191ac a09dae4824e381bf3e8af04779339865be5a11bee249deb6a19364c49aef0806 b28a9c81de271bcfc61fefeea147f7f075d628f03fb125656905b9902bc3c70c 55850ca61317027b15243f6b8ac91a4465d16acda3917e609a3334eb65ee44d1 0ee5384d47b00872328b49fa9c84337a0ba159eb027354d7ec44f72dc3de6e9c 4ecf5061de70931db95a1929f66954558f500788f38a4d2e83333e870baea3d3 b66dc73c085ff1dd555f58a69e656f637c46d6a24cdf1c9afc255ca928dba6b3 bb6cd272b4f21426a48515f011f6561adc689e862dc009711a649416781fcdb9 55b7758ba50e6f088f2b0cf75306441a27bdc87b658271e3a212130dca7f61fa 1af9f8499c027c4934c7337ba0d6d3433022c127e179744ca85cc9c2518067016b7678bae24689dc06f33d83e4fe5962742fc2811c7668e9b334efc43d587c02bb6936ee88110808727d4bb1110fdeb595ae6722898a9544b5c83fb0b6bde30dd8c343aad8654f44fadecdb7cf91aab9ebf61766f11b0f81362d9fdc477d610bd212ffd1a57c384bacf687320d34b74f66cb88d4c9f95823136f2c7c2aef9c0dae1ebe74c2919dac9f7833f77e720504dd254c2ca3f96797af746c5c18e6c70c2f0bab7e0e8f1494546dde1e9cfeb8c28aeea40b3fc4b842119d644ccf7b8b0bf7df8aad507f961ac01a8f5a4d497f828c8c518c4351cb2c93befdb852e90306a9d0134a3468de5d33d779ad276ebb484f687bb0a79570a286a3f651dc35ef062e72b00f6b74f24f3a3fd06ec2d1e1653db6d2334170745dc14a9fc6aa37f40d8123664060ac451c272bdc04b404fca45eec5ef4e2ec513d7864271dbf055b002e17514501790c309b44f6e1759996c435142904c4e0074d65c0536f4f6c6e029ae51fa21815b1dadb9c113cb45855e7746c77d0b6176f90368eed27d7b739019561e3fb3dc6534dda175282d34f145be281a86675d9b7ce8ec183bcdc389f0bdc22a0f282ee8dece4eeaa7a3efee94bbbac1decc355f65d6c9a7402d9c1a40be3884e608627b82488993289df120f79a2d321f9f9195bf63497a246178fc190a86dcc50ba94831a696adff7a989f180cc3f01a453e677ed7a4af37fb0ea6e045758e323c69c96b29bf66b8da93ea61514b7e460d3c90a18ee1a120c3c4654026fb1eeb840fb70c6eb700f443e644ec0ec74185a221cd3db59c45c38356ab506e102fb32204bf4ed5d54199fb07a270b2004b0c7e4e9494709e4570eea2f3c0afcf8bbb631d5a31a049b62048aa62487368dbee7ae912f888e8485ad41bf93099c7e6d932b88f6a5f55f004fc4c49240783d9348c58224e8e384f4e8a3b6a80d4c56564b8f788bec67ea40f5537ffea7c374fec49391be014713cb9934164405e0f5a94f47062271cb6757e29b1b1542eaa275041171903f840d91524ae3a00a189c64c39ada809b444fd2d607bca0efebc6bacfadd4391f33cf2e61cc6a9609e0e329888bdfc7414e7175f1ef609dc06811a3099e0281ba7a02518e62c491085934522cb5e1f1718727ec706f52df19f823644d7ee0cbb23c9f04266a496907cf0c78169bfc5cc70be7a68f1a32432e548891841a61d2d81d38e6e4d0bc3908e2bc9a8ec2fb2f498bec68002f37d4c5d48facef05507b3994793914b24d21041acc97c22e8f844a3ce0b560f563774bd1aec0ff077977650c8da6a7c8c19b08e54582d6cbcae6f1d7fd361a37e2c6ff68c76f989ccca6ebdbd8e7acf9773c0edb4a9951dd645f5339ba1b82427955c8accab7518950e4a424cc515aed25b70fceb3ec1d9674f981d75a1ff08d639fa0cf5e0b1fb8d9b8e60d002579df7387094c65b64fda5521c55e9b1c7c0a6577c21a3847c67e634d2f03d4afe11227030baa8ff8ab0ae5d3126ce2ab18ef95461114c21419e8cf3f47326a970c4151ea07dde5f6f5c5008cf6058ce3dc22a2c687b5cae49e02879f234052896532e7ce0b85a69676ab0964fdd23912c063ae97427acec5b884f31e81438bdc21cee05b087fed9794d891091eeea58bf2407a292b7f39e21c21ab8e29c99d882bae7f0d06203bd1fa04a32e3ff18b728bb4056142f9f944243c2f5097712a228eb84cf0079f94852f53e46d677e6a2a509414fb331b875adde47973b3ef0d2e036ae8e7032740988ef0df8248920f57216c4fe4174a7ccd6bf255d0368c5a0281fc43c60cd8173a7628d66c9707fbfdf4bcc5771c1e5b36d3a1d342b5af536f2e912c7f483e5e7a9c3e112db4aa94c6471fa6f75ad5bb1af301e69796555ed37709103b04b4f29b44f7a7c3351d17441d336158d8da62c164779b83a26fd6a1c858130703e77df7742871583beec57f07431dee5aa7b54837f4f377eb127898ec904d63b073ae9fdec1fee061867c0565ecbd4e2b752a77097bf3d3604458300b78c16303b72cab5c428d6b1e3ddff8abe54bedc3981916fcf47690b1936cfd5c7809e7099c270b8faf7aeb79ef671d6cef34487dd166fe4a42298f1a0f83519cc4e516041a9d1bb92154f1f4660573d7e3227b8cd6d3b729600122b0c864b5353fe9f80c6dc1305817aeade46789a413156d604c165ee74b8e7afa2f51d8f4b1e73c450dc0f8a479fcb96df582b63a6de835a82761817a9ebc1c7a4191b78f404176f80ff99a7c366104fb65f3adcf38720e8a8ee121c2dd2f881aac35f8a087812dad05be02c292aff4de5d749bd9959063b46da46f85895ea66301e527169238924c01a680cdfe4f41d7223ee168045e0a3469f45bb10f2144331801487d4a0615b302ff9b07d7130042c629b883783f83ba9748b61ac53c643ec386282d0e1fa6270d9c6304dea793d586d6ffeb3ed2406918bde78c7983d3923e5ffa8a9244c527031847a2534ecd3ce7d137a84f5496494fee1ec4060d92cc87412c72f3d5682501eab78f8539274cee1da62206f6f2bd9c0da9707228c7351a3ae1f4027f7af205ae473103925ec4211a31c85faff5171afac776e5bbe195f1762d1155f8b4d603483370778534d9a30fef447f639321db79645026db104386fee9d650cb78c40853303d7dd5d681473317aa99f2cdfb590cf9fe7a9387a15e379f1be6670799021f03dba0f61f7c86dd6703d36e27f55c2101824fb1aee87288faf005676d6c00639c4b396202c3ad0e148f0eda71b3328434e57b0aa38a9eb6d074dd3cc4e90cf6172d822c72e24bc540bfbff4ac2dec6f66e99a4cb6068acdcb204965b6810516c286b56d476aa21c640301f1a93aa8a4d72b54d9085dd651d161f65ad4d20a8cf4816ebf9b562ae8541759483eeaa013285ceb108fd84007eaebad4c9fb90edf04ab7078c5fae61167ca06ea03dc8cb3c22cafe4859e6a1d8ddac4119f110de75dc6fa939fbda8669444bbc2ea68de3b5ce44bcda0d388db77da283c2efc080ee7d9128bbc1fc1be16d879bcfc5ac0be4e54c544b91c1a25208dba6179b2020349583bd1878247acafe49d22bf6365496c3a6a334c9adba90876ea3e986f0e8a0b34fb007087e698866ec003bb4d05d6411b502963321b5f12c28c47b5420d197657c3050e5c541db1ee4876027ae1c16dfb1368269149f224c059fc678f0bf3a2aa26ab70f36b9ea657706ee081c7df0997de6e9e818476d4a7da88d8c0061c852129362f130f6b0449b360381db6f676a4a20d923b5b01ee4fc1e3ab5d044263854dca9f29882f28df90ef89f9483560699110a40d1ba0462fce3e730308847c1d05305fc8614b800424bb86bbd47c2e04fe34b27b90b09fd3c1d6a63c0686f6aad24ab80055590b063fecdc74e8bb630e1039d5b32566a654e65c281509e3577b03802b3a24b7b799f379d8f0b514a5559654bbf49d56cf62d6befc4b02104eea6249ec2b4bc8db32cc2399642d9b476ea40eaaa04ab0d7ed706273b502ddd33909cdb037ff7dedfbf57e59b2fa0f22339b2a40feb0bb1c7bb5a3b21f0afa4f0a22869565bc9a055c9b9c977e6c58e80d0e674654ad8ec72d52cf143b06517f016bdf2485730dfc8c93eb565a05f5ecc7ff2ee8f646a49a902bc764310d00c6d46c2ee9d59efe83a8d4d481d8dfadb797c3b57ef2e1040f78931d1d670a285e24991ce8bd7ccf9208fdf74162abcaa224d2fc3186b0096757dcf50894015be223d176471972b859cd27fd1c3ec17705937c0199952b8ddcb9f8cdf6ef05aa4b6ed90aa13715fe4f07591cf9ed7cc109148faa59ddca8a476d845c1efa0fc3c245ca6da24b4106c2965c2879bf93bcb6b36bba7112bc5eec515a9a850d03be23f189cda0e1610bd93baef321d1f239978b9455599082678895e768d4b80c1b0ed8fad1bb2c47ab64dfd6365658fd24155cae755d382e7b39a10cf5099c0409e82a6ceb5fa737bdedb5610ecba399dccfed5c13e6223d3892e88cba3eb306ca798e4badc7669aba868ab0257905acfd0991292086348c3c9313796513be072839d042ea6537dfcade52df3837cd6ddf8ea0a9c2f2bed052decc0db5c5ee0e8f3eaec80c529033a870e5575bf89d4f808b3cd80bcde553cc25b8fe71c244008df65c9e5eea15a29bccd2e8a2f3ac3588f1a6e27fd2fd5165ecf0cab9d5f60dbce7092fbe6ccd682a855ad9282de5b6156014fdd44e487ef2335bbde7a05e0067a19c837a44b83290fc8d88574fc42b8c1ff5a11edffbfb277b0cb50239670ac7f7314e5b01dac8adc549e0b509790dcd0e3a84478deef0927893e20b11c804a16b3e74a3dd9532e8267bf85206f8be5bf4e4d60e4b2f32720826396c6cf60b9c1bd5c0a6c8b30482589a4df3cf34215c559918182118e2689125ed19c3f5057a0f075ed7a0bd48bd89a83c30406114075dcd0434b3d62bd2785438d9bf960822f909acc3336dff91bb1024baa61d39f5059df7b4037f5d1050c63f495bfd03dbae56bc3dba60c427142917aa80eb4be1d2d3bccb64ac759bf9a9090259190181c5d1bc793d52bbe90581178c09113d357b38175ab8d7bce5561be233a96d01ad42e8b3251ffaf91e61adf2b5a61f20f8e98013e6141f686af91fae41b8070ecd5772e90630ce3435e56d5d81875a2a54c0fb049d39d97f9a648b2425a40406dd1b95ac0423b609b22b666b4e41d51a6329e5f8432a7552adf87be9730ace0845ba895d6d550778d95de8f9aac4d1ebb7f56a14602050f8dc4e5f2b7950630ee9e0b41b93021fb7f83b44a9536d2595aa8d88b81dd05c684235c40357c09706ebad5a0620fe518e3fd8bae25e2117743657f6e2578835c9b0d72e53b3c8030d7fc14bc0c22c582ad6a12c12485fe87d131a77db950d7200751dcaf25607a1060faa9806bbe0525be254d65062e13c7f4a1af4f5bec3abb8e7af6436061c580c805965d7002c2128a7847267b9bc2d79dc316d8ee8daa13feb7d2df3e457df03268b653740b18b8e1b46192643825a70ca1ae0d77d6e94d821201ea7140dd60ba28ad1d6ca56703192455c89e3859586b0f3515ca8b07134a172edd22b4b3f0c3b4cfaecf8e2a7c95f0c51fc4cf40a6f249190055226bf75b20e35f210de1a05e48ccb4cdba821f62fc0f98fb793eea1946bffeaba6f7923fb691a13415531013a7e432373c023afd041ed25451ee7d7907f3c49e4dcac0d99436d4af973710d4ef6d7dbda3c34174c5abfa4a2fdc8a172cffef1788bd9b2f63852016dbad107bfff1d1b51b35024cf009a09b981bf06ff76125beeab4c92cb6f3d8a030848043a179a9629abbf61c4f8a9d6a55213e2e5f0b596d9b36e557a21ec098b6d2104620aad3b48359e0007e2158df8ac6b561cd6134c567ccb6069309888ab118206c9190a558d40c1b65f0191f40b9ff2c6c51b2b9d9d8982ffef938534ad8c16011f442cbc9b06f5d3254d45d8fc1b0fb5a418cebf0a934cebf4cbb471c0ee3e029da419a0a53f94214d991ccbb72d6b1ff0705df05d043269fdc3a690ddec7a0d4ed399ed5a68ff5808b330aefbc659f0549216e2e10e08270fa7f24974bc990717d5a829de9fbc30fecf86811725b46e0375f8d6f00c601fac58d586f4569d0f26f0ff6111b42d1d853162ea06f184bf57ca412555d003799e0b0ed9e2510409879197837c93fbb9c2d8470077c7678fee895b31687ad576edb905b66e35d00e false -check_ring_signature a93cc480fa556bfbe776ccb489d9fb8f4e8fa35a6b5f28b0c42cb49f5889f54e a680340c321f7496ecc9584da89dae875682fa8c5a550a4dc6c3c0ae51c48894 7 a2d9498fe8b8131d8a8cc6f4be2c00dee6401edcda57d549fe15e60fdf90d5f7 13f17547cd8ba8d5f908d8a239ef7ef4bf082d2428ce9eadae4e00d172f2b935 e26e7d73d84964dd455b54f639c92aff81e8ae8f0a42b62941a80f82716000bc 983a893529f1d12ea1a4b0c3591641fd2c9159e13dc886f9aee108db2cc55ba4 5fbffc1a2375100be376d130aae4f5661880457b4e0241601b39def039c38dfd 2be9b887e1916c6cf34568927b696a75ae34aa76508fba11915c71c3b0ce1c74 e03f70f31c9a9544a082d441459d5f2a284bb89755d2631e6d22ac7d04743982 8989ef10880fdb4c2e36fc3dffc866ed644529bfa2909d5dd007f399ed206b04671c19ac51a5504c3b7ea73a8fbcb43de23c5e5766595e25a3a8c0883f64a1051e96b12ffc0e8a54c7d210bf97683f915923ce024d160a1505133b94f8caa1076770a495f2440149e863db3ead1709ae18d9c580f94c62d48e9177aa19dfac0418512cdcd9b7becd7388f19e6703c6340effaef6aea8b9b88e8c999d3259ba0c3ae6fdf1a9d9640d7a431b3ef76d8c2de1c186e9c44bbf548d2347dd69b8ea089cef644a56b9b863876ada0a2a048b1b24ad7da18c7b1fa9829365a41faf9700261c778c643c9d526a87778613d1cbaf8b9329720d7bfe36062f2cb0753e8207709639aa5fc8d29d4e6b09c9ddaa744e8c786407498524ac442173ccd7a61b0375ed80a6cfcc463717e301bffd2e5281c8c5a5380502d5e6239664c443048804c777c4958f371c9bffd6bc4b38c99b7f2627837a382ce2233ddf15a2fcb61905d3282af9c3a285e2a2f73e0f593d04be5f2f6ce7562f641951ef96478c0d2a01fce6b0169537dff7b269d64711bd9e5c9c8c9393c1f2006e9afe7d7eaa546005421ae39ae4c45504a96bf116a1fceaf4bd20fc87a990714461d5171f59a5bb0e true -check_ring_signature 8bbd08f5737e4e7945d62b15c2cde771308bcde9b740b58f37bcadbb3b8553b0 2f66875d82dd597ddd7a2c9156247973cf8ac76efde34acec0d4104d98f0df8d 1 a3c1f0e50c7b7f30d4257422ee4c57b4b335222bbc570dca063ddd78724ccbb8 ade1b33503c34f3a63a6dd377407994a8a90c269683f212e5d3b75b0bca18e4ebd073fdaa4eeb1193b6d84e0f8d9fb7568285bcb1e8c4de218f78314e2c49a92 false -check_ring_signature 4a410a926f9806c0f93b4996c4d36e8824ea1665cffe33b36bd0db229aa20a13 5837fe7512b41faf906b265f2f5152b84e5edf4c6a4283ebe0a031e3d1ebda86 42 59a053ec893222a4be88fc10486bc47e9ccd7cfd2ca07b2defb96601c3576eab ac52ffa7b2ed2bdc2afda54a4be6fc8fff7e5dd3979a3800bae3a13906d76028 ca4be13ea19ec1dca8df2cb0191d7ef6b4b53c5ed9df1bbaa4711586f59b329c d1ea2a05b49d0501642acd024aa57bdd199877845a616083e6c72c8b96b7259a f7f71fd70f96990264292655893c49858dbae39fe6c140737de1b72fc2de3bd9 e0af5658f0df21a1c1f0f141019c18fbd7b0c1d0dab7ba3c5b5c3b3f84061700 efa0fa83e499c895fa5b9796d38bdb558606c409e0d2a00b6d2d74f642aa467e e6478d625c2254c83dc57ae68ddcd17c5210bc497bddae771f6b9a22c7eb3549 5d0773852b3b1b11fd27cc1b0f537943aa979c09e8e98ebd512979d8398a4659 cb8a8379e751f112cf186a05c5ba956ebccf57e29d0eaf1119bc99f442150032 e373bc2f6194fb15f1754538eb27d4fed31d2fad80ad5bd064f5525de5ef1b75 826cf7db49e6c58c054f7556cedb918988c36285cd37e5ab6e4e6e84a93ae391 9650c9641cededa4632f36d0230a4cf64dbc6114dae33547c602b5f1ba443d26 b000af398909c692d585fe520d1936dc3d3531bc60396e8aa0699ce2772c2102 a424f06b439e6057f5c1410201e6bcdd86b916e4262de61c8e78e81456bf8dd6 364f3719b5538cd9d3df5141b81b4325a785b12ef5b51ce2ac7cbf0b3bad3e63 2e3ac7b949d16cb8a21cd7a58e0fcdb52cadd4f65950f64cad8c37ef3c68d78d e73dbe4abd40b877078a70e3affaa483ccf642428364f91bbe14505c40eee765 d41b05c654c98b4dc31f728e313881be1f4e7f699d356c0077c0ff1c24f984c8 08d9fa04e2903ef70e31610a59fa8904f0d6942e171fd40f8469eb571d048c4f 205caffb80877d87b53a476edd75c89ead20eb734f2492bc8e2d31af3a2ade6a b2ca81f869938b2fe560ace350578f2f92f725afe5ef30b177e6d9e6e9b71ee7 ae6548e808cdbd4dd7aa3f78978b2759dd806383fc482375add5755d04c60d20 3df6ea1991a907c140c5bc18b50ec226a0596d060db0f3095019b7e35d837abb 561d6fb64502f9e6d638a738dc1b649f113edb7e689070cd40e31de53b23fd91 e0059f33342ff672a69f53919031dd43763a8b5280fbf8e6eb9e7be571ca271d f9dd2ea7532c65d9a72fd05c420e8090382713f344142fec3e0fc78fdbf555d4 a8e6b8cdc0b615585c21d66425d8a9ab9a57490fd7f598c9a730fc5d9a6497b3 784c42345a27ea091508b79d9055d3c5d53c50accd36b35c43c4ca862a281dde 1679367a61ba24a65ea427cdd2bf3c9d32c19eec0b18a6b30666fe5c95959cfe 4d6fc95b821bd5d758b847821468bd6300816106319a8589ce0c9f5e03a768f8 8cbcb54524f58d5e393c794c99d48b781d11a46e265044214d473623596103ee 3bb149bc6f3a37019618d10d044c22f152e769942544a1b35123c3c231fc709f 20229641a6140efee69ba3e4829ed0b5a7c7f62224cbe5d906936badcc176257 98411b723e0c255ebca8d78196e9c1b38cd7c71f0d6a96bc7bf92fbb4ac6883a 3d0b630379dab35bc5bb4abc377781d2596b24eefeba22af76bee8202a9d49ee 0160ebd4998e01c8c85c318a2b6de38c994804d02042ede554473b6f2cd8beb7 35f99b9357d4a7b27317bea6c00fbeb55d8a8b5b3f1080dd4c9945b410cd101b ed59fd30512071765305525d047bd09085dfc1945dde17f413b51449a8471bce 7962750132ea9928e9b206fbf2cde38ac4926425de16d8c56b99185a591b862a 0ebb323d3992dd11dbd6f38d7dba9c27df0d605154981552fa7201fe93982f8b 678f0841e2f77c34f8288a014d8dd639ab44dfcf618fa7b565575287c5e51177 45a9cf5673db3c2521554a7db1ae170be1e2ea953f62595582e7e7a54eea4b00e41fd48d185d088d851a5eb3353ba2593340f5c0f25ccbe12ca971684440d3044d188ba3528eb81555b75b2a0732ba78a5348b372c5e609a7b98ab4fc8df720f45a74603158c3e587049db6af971f26d931d3e8032707828e7973093d51e0b01e978206274aa54ca8e618aed08780172a71a80120a1a4769e6a72f7e2773520bfd11904dcbac45acea1c71c53f67ab4a94a3dab72162002f6d7a5eb298393e067af54cab2995e2648fde72705e334db148f04af031f25e7dac864f43979e2d029cf807047d779df2ddcdefc9d23cbbb6c8a6d2e8c6bb6223a40ad0ea03fb370a7a45309b5e464d3d747b49fe6fe96c660a4eee09149635600ceca26d15bf190799aaf409afcc6b94e2ad50109bbb0fb9a94d53bca2358c079392645cec3c9c06d36a673f7991f4606ab49e27876cb6d1f86e390841532bd01bc5c4c0e5b3fd012039f5c4a343dee8332372541474098c628015c3b64ef11112de1c2587cd4f03dd96f62231f56eeded856f9137577eca69263837426b720e79c2e9d23b9db60e0eab7c74edf76deb42f2d36df5d5f778ca4bc6ef122861fd065166449171d10a81239bc3151fca03f04f060d5775707e056ca3c49ca6e88ce5a3760940debc0a160d1d025d9b9da5510e383c50228e236eef7aacea6096bdabace84da573340a5aa53555d9f618ac9ec8d73c09e3ce0187658517bc13d4061015549034a66e00e7ed7ce83dec66f807ea56d133e96aa112f741a68de72a1f2b106a532f9bcb0f93dc777d5237274babb9b44f14f6a85721fb4d421642dbd80f69831bbab5c903e1ffc82c700c02739407714a137cece464d8f33b376f67e0ab2da6fe28b42b05623a9cd6881f83d8c2233fdd84b8420ce4fc231e2fcc393fce031c96320b56023a72ccd95ab31c892ac19b66419e237e1a50e15f691c960c1a1ab4be4d3bc60aa29437194c4332b9ae311951f8e793e2ef3d13c2d223c62dee166bc9c199a60758d88916932aed420758d7f9a224acf1a514251cff04c6e79a830286a5f731001f037e4b6121df28071e10ea5edd1c26f88272a368504df1b37a24cdac4136092cea5dc27e8d35c82a49ba37a0096371dc40a1df415274320b7b74ac94c6a80735dd0ef30d07d8aa7f104a17f16f64961b7fa07f6e7825bba2c8609a71337d0852c16a950ad3c56ab599625f3757c0cbe4687c37b87b600c408ee924e8b03d0d21f4be8a6b2159d6bd8d89ca69ed2dc1fbc4ec47f3737acefbad7277e3fe220b2bff38402935a494cfecc7a3af3542dd826c671f5d760a2c85ab56e46eb6e807a8dce1c9fc27c9de6cc4fc489f8ffa9ad6a7de198720d029b70679c38db3e50b132181561bf8c40b22fcdb8cddf5168e2f403ae63245d199b7ebeabe3827ff00c4000966cc7da9925a5b93d5ef50763304097b8d313c1f156ffa10c90d81c50f9876bffabef2297957c5f8059484aac83e1e6cd5b159006d3b2d6be5d94fff0f023d14650269dac8319826874f6a1816250ac8107fcc30a7d1c60163715cd400e303f8390d23dbe3a9415f1f1d9aade33273e219e51d09ee30a1bf41c7adb606d2d330e88d283068c01e6c068cf9bb722d8efa0613764d61030e195df045060eaca09c27bb87e7517c45f7a83b70f5b3c30110c92b90f022c771ef7f4d74580611b7ad8c3128c097ef1c6179badb9f0a66361f91d1f008ed5b5dad0d529bcf0a13cddc6b957e88fa1d3466fede09ceeb7f8388bddd40a99dde43d1f297e93409922c08cd7bb4be50468528def4f4a0ba16f5d608d2213c1ddb5adf49c9e93c0fa0d9568e3a8e31a9c68f47f6032c513984ef09d7e8f90f01dab9b177e1a1e90cc2d1bb3d22a65b8a13268a37763c657c27f2d98fbd605cdb7258aee4fad39600a514133c9b0a40d8792f3fa06cbe760cffe49fcef5cf802ec75e65a8b2744802297e149b5548aa9ef94e7c5ab4e1d3ee8b45f8546397dfeb0b800271fed02709e321e2d380ae62f88a771567b3ea3c799e2615c29ae27871db61a98363e81b084d3ca104f050fc636a4723df48c809341c9ef165a1db636372284327230f1809c271c6d726549f2f1d00f976ab3e51a93abc621fb0c3cd56e33abf5c1ec7910935b5a502f4ef80d7a50d53b503b28c4ea8e546e56f3fd2078b4393562dec360b943a5ce87f44d69c0327a306f1772ac0a213f252a4120044918d29c0f5a75704eea9314efccb0376d75dc86a1e228772d28cef1330af03c787a6f5ed3457e30572f677f72ca446e68d54641bc321a1c78623a1afb2bed1eb0799cfd341f5fd01c7ebd0b2886169b7a0fecd411c9f29f842e4c77b9da011b9ba38ad4b316612055071e7a2d5429849005c0559554a46d3130f96ccb79be0b4201dfb2c9c7e9a0c1924384fe52a9c68d34ceac0ca5aef5de0bb0eee4cfa3e1c6d9908621ac51a02d9131981b834b1bdb34e1111ccf242df912fd23b79d4be128fc3106dc754db0bb9c66fa4292331c71ed4b224da5f8182eafa1b3c46b5b61f8f66e4bf7129610083031a3a470d7709d4115d33fb4f68753fbc10e1fe5650bd1a8d95eab65df50cba123cc6e5eef3ad5e6194f7597020f3d11e5f325cc069dbd6617862da11a3098981ca3462b445cf78edc106bdea58be97e849b3cd3b9d9a4499f76735dfbf03f95bff91644e0ebfc250a1b00bd663e023b1fb738bb307b291d5b20f1a776b0f043257ef7ba15dfee5eea7f7884d173865b2cc6ccc62ff4f41259640f921fe00768e8d22f38fcc9528ce903cb4d18998b1bf9d448c09af7c1abce3170dcc56080fa4f1c1584bfea37135a0bd84b46f94abc4034ceb5f617824b0da2fba6c960ec31665bcf19b90767f3ad208b7025c6145e91c3e6df52a4ae1f00a1f61578206fb04ad946a78fd3e5dfe07962ffe5cf67431489ebb24b817ae02797c17089c04e92b7c85d223fc1b5890c44959a9601f7b0d61420960224d18e3d6bde527ba093839e54d2259a47f82a108873f49f182402d182646a76022e32025caeee60a0d927d495f1fd8bd9aacf2d49ac332a84c965889057fa5094a7c6ceb593cc51e033a82e46e6485c066be05ac49d5062067854b2c10b8cd64710524cf0442dcfd0ece50ffa12997e12c234226c1aaa103fcf37e7209900a7ccfe56d6cb72bba000b57532c672d4fc73d1e10169ef4a259e7fa75787b24cbad1ab164bbc2a85e9a0dd224453edfedb6db5d6e4eb2119ed451e85591e723aa0c9199f5394f4e2e4700a4de08df9a00aae7d4202de221f30b53a969e490d534b7ce3b10d59ab1462d08ff20ac2e3a3aadeab00c8d3dec2a7284f018f3f55e2486bb6d97032d611e8b0a8b29a5f32bc72898f6ad412644362d1d12b4e94e7287778687e0519fb904700c8f065cbbf571f1e4c9dd58a8eda77b283f51270b1607363b50a2b238f3a153025b70456a2a2324a800de76fa02fbd9b9d9e9b26c0f3c040d139738815cddba04f065979ca3b5050e9e62d3b66913ae44a0119a012f28d22fc11f1edb08e731022e44785f7f6b8757bcbf12876439290f828ff8c1bb7c51b0de8c80a80d35dd0da412fc53ef4eac9abb08f4cbfb9d79950f464a19d0d941aa6d52f780d1036503ca342572cd05ec02be2ae321b86344a9e91b83d5b27a4ac504d1bddc345b240c6d08e05effb5e25937d74ab4451a88043f4d1de2f321fd2cc63d3fd527a9ce0a74b26292476efabb5b70d2b98c9071754f47144fc6ef9f9d7106e6d9c2c1860c true -check_ring_signature eefe1a8cad003560bfef693f5acfe6022301ddbff05ff388a1b1de2121af09a3 f4a959c3ad6822dabbf0bfcb5d92e600ccf9172e28aad3fab6d2ad79355ae47c 5 0792b14ac2c5c6bae74ddc655351e5ba414992c742218ee5686bad1391d0949f adf8182b0034f99da19f70f017ac741acae9b74f60b8e96c43aeaf5b58e3b037 bde2d9b548dc7b793ee0277d046e3bdf196ff74c30baee1c428f7f5c764df2e4 4ee45d13b1e39a057276afe3ed3ed5e6d5a679447cc32c814092224f05a591ef 13ddee10bfde6caa22efbf04ddbe9aef0d86bfbbb0bb6c874b37dfe1413faf26 9672e0f5e131567e24b3c261c8f1abe0277b862387395c659c5f2a2fb7909503922a52b76867cd5e3f8ba19d689a21bfd9ed4a128e4ed7591438ed69861dc70258581a443e3e6ccf679ce8bc7f17c2e112dc52a92f34c734bf4c3b033faf5f0a4180af9a7a4acd0d15c8d9ad1e69bb0da0a8d81fb3dfb038b253aa658d570d06a558245d66084ef8668e223cc18c75f4c81f04477288c752aa80eed1c29b410ce5d53e52ca74fbcb1b4d5b664fae289254a35a822c423238d2be0d6a2fd62e0bee6819ffc985b3a20a2e4ab04d631acf3d3783e25d8fcdcad39b931b3ac1680643ed5624ac4ba3bb4aa3dcdd80f34bd2f3d6fc20cc2ec89403bbbed10a412c09dcfc8ba5b84424a12533fd3188f48fefe872ed43cd09bf48c3a894ea8e487b0471def6c507153dd7d539ace5c513777bb199afa023e67d3bb5af793451afbd05 false -check_ring_signature deb249abba8c88566cbfc989199f810245af9e4b3df0dbe0349987957b5ac5f4 ed1b76035a4f1a856e3afffd12cccb3e488aa1549e1ca2d8a52423d507d73cbb 1 a01da5a128f055e1f188c33636d695d796870e4429c1b7d40df7cfc593235cbd 4ce237d9ebc78ce64baf0b1c5c6ae7766393c418746ef4c8fb100df72cbcf40767b23ca65c5ff09c8953f2d7dee0d5a9c499f7dd07b3502ae4e281564af89309 true -check_ring_signature 43a4fbd3ca51a65bbc02bd0ed4a8e07d47d5a066517df0e172bffe55db7f9623 a31555460e7189533b21e4d06e9515effeaa0ce9198089b2cf1204d31248debb 4 b3ea1cd8fc4dd627369217783a79e6cc8e834a99422127b4e305295d9b912517 3f7fa562153d5dcf8a0ed6d0392aea3cb87816f771933003a3ce6a0166912512 668c0b24a4554c4b9de0c335f073ae5b98933b2099852ac80a7cd0be6ca71bf0 6b134e2ffd9836fafa8b1875adcf48a09dc05bca342593329c5a9e139478b238 21ca8d80660863659c7346a187ca180b8fae00da8e28c0d4611d5c2e4119490b0e801244a94860441d82097f7b6463d82bda35b5aabc6d9f9a237e69c6ee980d4cbf02ebabf777404075d6fe929a6605fe0d171cf1cd105b21304c23032e20003cc3a5df67ac0a91892c1e39a5707bdead2e0b8f0f01a582a77701c80a3c5c00de1cd92695e25fec7e4ab4e1766919cf0d94f9e9c7654ab747b19ba5c3d94808721b46bc55b12f4161fc75d4cbb6ed69333052f4e22e18eb2a510c94e3d11f01515a06103e8ffdebce1a35dfd8bdbe0dffe092b2c8323ffc2ec1675f189ed00a1e30bd0a806e4ef543b9ac72f77396fa55489619c98b6de8231e534da0117507 false -check_ring_signature 3e59602ba303a74e8bb3b5ee0762f147abee1bcefc82ac61378f4bedada65f6e 40197ee776b2aedfcf6041bafdd15d234e51367eba3f256cb7868564a9004f52 84 f7668255998f96333b62df775bcfd6f91e73bdd8445edc45e9146a91f37bdf5e af752bbce78f138357f7ec567a2ce9f94ebe3b358fa0769752470610965cc5c2 85e24acab0059cd8a5d37cf1b502abeac504b4b7a09358706351dedc198cbcc1 c8657d55f903c78957c035aeb6bd4e29786115ed83444cb9c5089eb758a6c2ce 5eb5586e6902d9f9c8bfc621da589f627e23d8a8d9f89c55a398ae76a3aa928e 315a52fb5faa2df7e3d29f7e0c7b414fe2305e9da555944dca314d81a295a29c bc319da939d3d932ff2b17b770baa8c73b6471eb04f9eba73124e1d5e1e7c5b9 de60a95eab2439398a1ba5bd166f06fee9ccb145a1956bf40dea759f29bad314 d825da9c57a453b028bd13b4464a021423148f086187ef9b5611da2785a132a8 f9e1ed6394c1b2de95cb5bf7fc79bb158126b5a9a60c5366402dd257c1868158 7e8130edc164af69f0e565d447dd276505a6b848badfc3ce435d50998f847d0b f0ac51c33c0b35d20f728fd4fbb506096cce6dd1dae5e8990444ce3276403ff4 fc28d89000ef74647822efaeddbf5d0310bb006cacf82b0e9e0a01d05cef5166 f361fc1fc5f0ad86ba241eb93770caa2c6b2fb7b12bd111d3d3da13fe6abee63 08ae4ef7600f57624aa894d2e0d1a07eb5603c3280a4545f56be5804b0cb3658 bc0563f6d02e99496f9327a762ebf41d9ab3b3f6b41d3a5477a834e949f9fc64 3a9cf812dc9c4071ec7a6bcf151ba7c29d8daa8170d9f6c13dfa280e7e91e943 25eb7bddfab72308ca702d6f30299afe6803deabd17e9bfd7c920de893fc047e 19d013f8cc2b62794531b303f2bef2ddc0db6607e7b0ab3698548492dca54a84 9b29874a12a8ddc5785bea156a340c878a81d4d889691243f10b85a2be5a8eb6 c4d0e1c0526773a447ef8dc9a2bb17fa176d233795479f0c9029c15f75d106f0 868033fdd8289f012f9c2b9b8217f47142f46d6fdda5bb9fc45e710a84c39995 fb3bb72d8a2dc85c5ad7fd6f14ac9964f3744d5bc2673b2052f745883b9a8d65 41c759e023c7cc264ca0b6fd3765e1cf839c5b271b827a2704f07ddf8b421e1e 17148fb764016f5f93dbc05387bd952c2e2716f23a4dc92a8d424ba600a61fff 083cbd1bd09f4ba51828fd766980a83d05524277f9fc91862aef43c7469edb88 bb37cc9330d151387144338bbf2f6ba38a43072ad997dec26da9325fe2b3a73b 4b51a9b4c58d4909b60a7759b3d2edc6b0645a01c699534841d6420d1a56c939 eab513f2c16e1b65acb1fae5e5de7559ad93707ea0e3efbcbd7865feff95c9e1 5b1ee74ad757339cf33a00642041644839f307eb16f48493c8d943e9f45fa64c 0d88bb0e2ecf84a849190aa345c11495057a67c388bd23e6d99ca4e273fc839f e8da2c7f76506f6baa3895ee441162ca0fb705a2125f3acd17536eb3dd410538 07cc84985f503617cfcea700848090fdd75d64b44a1346f515bda1e3a857cab8 14414a4a974def02d94674da1376ebb8449c09e97dc56f804809b08dc0fdbcb3 b213fbb325d79410bf93a71d48d6ac80c3d4bf13aaf89cd50f174125ec60a756 cebd3192e9db2090e14ccf20acdda5163e0872c36b765fcbd5a3bcb36b6ff70a 99c8f9578511d3c283c7f82df11d1d6a246766506de09c3641713ee7f0da08f8 2b4ce483ca998fc8092694671d9dd0be1951857ae400f624908c3a941b89b666 023f7ceeb05c30d4e95c6b6286e703b4a5001ddf9b83720930b50929be9d542b d873aa6b1bdaf7d284d94a8258cf5e04c1215badfb1aa4338bec9c9b6d974dbe b0f4be57ed6fffabe93a110216ab68eb0315c8ff983e56c1c4f760d039af5010 6bb2fb7a0fc9a03080d516d66db5256cc348a81a4424f358e94c85c4c1024f98 0cf0832ab1a69ef202115bba76676298caf227ea80b20374ecf7f7d331242eaf 301344900b41c6a5828e248354019cea4562dd013e9fddc08435f8b53118d274 db43d9c2db8032150a31c5de4979bc5182479c81db2eeff15b1f4bdbe4d6e906 e95407608161f1dcb44502f7f98427b654b537be834a6edb0910f649df78e970 ebe8386f5cfdef86a88cc432703aa3144bf39fef429378583b93e11749527a5e bc5da2cf8bea79d415477c9d68fb4d51596041a587c7c63acafc5331c2dcb0a3 89917e9102fe2fc78962b7c42e90cd81aebd31bc42d5abf25eb2087dbe54e194 d37ea90f47141687a2c407ba26762d4972acbf702c7738ed68a80c24749f7a2c eaa63931fd18a1ab212451facb976ce8e6c951dcdb5c3136e2c9e5aae1630b99 900cf5fb823aafaf17fdca5df7ecdc0b40e65ac30ed0bea1c51b374747ccafa4 c458496b0aed75a87d7182174813b40f81bf2b47781db588c629c8b3ecae6881 888531bcc9e7070f67be967d2aec28a3c39a9b340e7581ea4f17c618283412e3 ee14e1cd67eb0064ad9bc5fa43c7afd0c3ac59a99f92791ce11bfbcfda4fea5b fad0d109cbebb624ffdf4170ceab007237326ba43591967cf35cd20583b1b2a0 f0341ea3d4685f5c8d2ee2eae19efe8226250923ac4f215845ac0453fc686bb5 f533575574247ac8f7e3d33edfd928b144357e1332633e141421d06f6f659d43 bc3aa353ca8e325b5a7d35f462a11f003c6c8fc0cf769effc897f7b3cfddc1a9 4b80b88bd7e5dc8070b2a33087d795e6490887f65d41c79d11162707a1291ce6 4b289ff4ddefbe00612b2785613672cdbd659600a8aaf59ba10e9e76d2834244 f8ade76c5db411d2c1f4c08bbf2e15398e9509b064bc4524fae666866127c475 800caa959f6df3adfcf2ada21a65535302e2d2cb23a6df44a25d350ee797dc79 b00c7ac3fa538677e74c5495b4c970c71967b98b76d50943b84b2a0e1b001818 360f081b01ef100c004c02f410f27807fff6687febfdd6ac99f3f48521838229 d471c27e4dc1af2cb2102705e58687dfae61dc1117c5f6c407db78c8bd7d3c25 b9b55663aa8ce6fafaec0e9829c0c08524d427aac4ea03b34b24d415c15e167e 4a66a8f8011aad36dbecfe808deebd0647c68d2e3e330788945d045e92b81324 876b8f28986e5d3e24dec8c2b75a96577014a8921b8b77f4d11b306539b29df1 9f54f27ed60b4e9a691b2061e2df1effd124da6c2b56fb321d1daa44587b2e91 1eb8510265a0b68f34d82521252a71889721e89e577dadd52c299a0ca789752f 685cc1d33cfc4e69081fb16d44ed5452e375eeac0820160130f51c5fc9e48de8 11df4642bd4396c924b9131c2767be28a0ac83b69bf4c16cce0e99aafa57b5cf c0cdcd86bc461087a2ce15ff43469899abbe8fcce508a395c4fe4031955c1ea7 12e805be2bb0bad0596e17f6e678e7b8452efd18f4030503e23e551e6949a6d1 9a0b9a925636bfe9b5d7abd170a5b0dcbb6da93d7652348db4cc48ad00d2dc04 665689fdb290c93da2481aac3b36ef568436e3831499e7657ee3e370156b8cb9 7ec00e7d6d17722be8021f757b2f4fc1e7c1f36c9788de390dcad3cfb7ceb6c3 be6494a15aaa326549728c685cfa18739a6f2981c6a8b7db63394cf81c3bad44 a8eb539d25b0f83a585502e8b5f68d9d96e2fca859ce70b76affd26dba91113d f2d0afe4a4786649816e9b49edfcd447d2f02f2fcb4fc0ff7c8e0eb77b590658 71a26be9cb35e99dd3b6d78bee6291b05bcdcf000c5b7c0293e5d9431b81b2cd 88388316f6d328dee2cb7897cae4556ef8d7479d5730532c8f52aaa7003c1d28 1c872b6d3ca44269d8ba84b9f042a1a2994b79dc3f8020f61c4ed7481556a6c5 784ebe7e56a0474dd77461bac598b70d952cd64ff0f7ff9ed7bff89c6165f60b00b6dc0c5fdb71bba3b687d8fc01572c7ca94cefd9856476efb328c2771c550b24e728639b254e7f7ec26196fa445dd226e7dbfb3007482820173577abfc170b8821369440ebb399bdcb0b54798d868eaab8149a78a3a073a06c242548084f0e4fba541edea546f5cb1b1da8e4e0d4da0fb4a0fae397dfb8576108a0221f8503166d6886ac2e5e754541906e781c065d17b26a6a9d4b0fe70bf27501e37825075e31a7cd8f378db9e7eed9f21dd133ace7fcde268df9ae88ee3b91c3baba1f0014d568b03be48d25614779c7c4dda79de914443f5596d1adb5592d943022d20ac4bfaf00ef2fc8c51577ec6729f23680ecb3351d58b306f0ab2bbadb98c7f90667a4e334c19e0b0b8411368f9f2a84d64a4303c4b187ab1385509607ffb994049151c8f0e52def139d830b1c5f5b97af7aaeda4865e7fd515ad01b4fa9b31f03490192b4fa26f096b8da8dffe074c289aaf62c6863cd9df65f4d5121dfdf54036d4e869021a1a129f5891c71a3cc83f46cede057290409df199aa30c878ace0c96a3c901e279ef8e64ebb8d12fc13c23cd35c7f880c89bc79b2e56aea300cb021699ea1b758c2bcc2cebb01c8688c83bf6194b997d48bdb29fb5dcd6200c380305a1804f34a118af48d235b087a1bb1d8fbee35415e3444f590f074103ddab03f19c7a271daafe8a480b50b8dadedf8af2c21e2d091f95f8475d862955e3c0014205ca6ff3675a71290fdc3178112019c9bf9f33aaf1fd9cce636876f4f62808725c3ebf17e924524162c42fa29437cb6eb2dc27146a8cf4ff6968245b74780c43366dcd9587c43392fcf7b3db10c9b4cc7554f74a584b1040ca746db88c8d01b3b052c4657fb6118a5a9feae902ed0e1086b85a00a121d5e22d4da722cce709829909a35f188aef7009c2c63b1b212dc3bf635ab7729b034071a157a9aeba05cfce358d53c1151c56d43309001c69d3130562ae445637850de3f65148b9ed0569f6729d7c0f20b4013d8f11a9b52909935912a3734296faf02e7cb3a200360f2fb2bd5353f5c9a9aeb92dab6f1b3fbee9d4f58d6474d40301294de8ad4ab704d3bb25c06f4ffea3ec212e3e32fae1a95bc292297f57fc9aa357125d2095540bf7e35275f140f73b8798771751fc176a3004b9bdac01216fbfe03dde28e8bd020b193d1eebb980c34e6abcf5ed196a62b47703ef0e5193771e058009b3d69f02221981b790e085716630abed9cc072295ea2db27448283d60bd4d58f84b4390f19e5d878e4e3302edcd1a6cfdd31356d1a4108098b2d77d8ed7bb082db887e0e53a76857d16be5a0cc31b69edd5652b1fa724ec8a61a06d6b6c032035a0c5c0ce3f36661fc80ec17c5ff2633fa376cffe94c672fc696c20c7655ae973b96bd061695f47f5c05af4819c869b62239f91c16b0935b159cbe290599740aa1dd6900da95ec310a40ad6e511ec3837b19a0b206a6617da8605097ffc0cceb563b9f0ad0ce8ad6a1adbd2bee801a55a0e0b8bf53226aeff901683ce8de19e872e3c10da2d1a5db1d4d2134d2ffb959193ee117be75da24d4a91de20f994af02b89260738f25018dd281549a37e634af94a6cb6c114485bc80f99fd6958c07f445a00052f4bed9661c1620d0db49c780be025c9402167c2e07a8ff2cfe5af57b22795020cbe853f65666f97a831a7429ba64973d8c0815f2e77c93c586c747343dade00aead0e241670106b562eafd4e910a2fd4ca422e727dadaa0920610c8b6c8670d74bcc335539730963ca049319b70a30398e62383a6ccfe3df35596aa92d3f40b95a7fd125d811c27f2e3838d1db587713fd11118de6e66c84beaa35968c035088fc72f631183bdb14873c5218485fce1309c31453a75ad617b1b5e4af8e04c01b183d81b0e86a8298251b4975fcb1c0698c440fa9696dc89890c6ad606f73e076c768103c4141a65e94b90fea0e16878064532d7c368d4d170de21cff053da097b697360d6dc9c9b8d545d1ccafef8a92fdb09191275459890d7745e5920a2008459858853c7e845f678940f0e45a0b0d7fbfba7b476d4e42fb8a98647adef004b68df489185bbb10308f9b2ff000feaeae3bea8ca2fec3ee136db9e0ec00000064e3cfa871cd7a67b351abaec3bb310912bc05d56905c1357ef965bbfb2ed0f0c76f2952357ba8f00bb82ae6c61659420517e1a2d7f9875b9c43b788fdb9602b01836782567b8e423f1864de5566192efd01c6825ceaa8121aea7fe788e54055b2f9429338d35f3d7ccfebe8b890d1c39729b4f7331cbc16cca7223d7192507170afa40ea84ce737939a9345bd3cfe7f41795474b5feed02a92d402d9852104041da92b071aa2f71ababe76496432727c37c1e9bad7e962f52c2cd99960880a24b4c5b87b0c2a67a587a4f08299972320decc920adb6f41dfaa3633a20a440a30a159686d1783475f3cb4070fa59d838585b349624f7c060372ff2715bc23079a17cc7eb1878a71e5a53cbb0bf81fb9b4992495d692d1c0613387d25187200db90316be28131cfcd0d46039a417795bc2de8176b9ae7c2d2619278d040a360954392969098c90ac1dca8176d38cd8fe080ab6a23cd8434f9e1b900bf7929a045647004c2ee471b9ddec0a3651d985eb40972b36050a85ebc15c9e438fac6e073c76b1731dbddaac3686de0ddc7e2c31a5b7173a5fa18bfae386ea7c744e4b017d083a6bb3425160b38f5cf6580d3da4d7dd9e3d6e3f4a30ac858530d978e309f3da3fc5908bc0c27e882834c7aa041f77b901bdc1192576659a528f33339200241106628ead8b8743d4509c8cfa76bb97c1b91daabf01e92a384c5af5c1a8079ed571fa6261fc932eea927f40ec182ce7a75a5730ed5f9d32973f67d78fb9062a4d9a4a39ea9939bff67b3d90f3e736e79a11848a6e9aa52a57eb28bd9e350a81d1c8c8e3805507dd0369510459b00f2db61130473e4ba69041fd0f61580908e897251561ceb1f759482f432450602a1fac580b07a560f2b60a8dc9f8b7b0055526c98e3be8772bdd84c43009deafad37f1015faa53091e416d47695cad6b09881ce272b7815031ad3346bc14875719b4819f3111ddd302e752025d04dd7f05fb86374a6a0fbeadbbb7bc7bc988d92816e700a7b0baf916ff353ca120559d07211291f0a36550685233e4249bc9d102563af8d1c2879cb62101bbd256b8db0d93765a83de3392cb9c5841f1efe9e5851e5b146fdd4067a481f6e3d0690551016c158d6ed1d3dc9bbafb8351a20e9d287a351ab0026a44aadf64092076cd140d33920169b030e66aadb79f6cf97bd0eb1b47756d5aea149dca17ed6508cb81079b4b1aa0b46a1d0b4998e7454d2c75538428ea2b9d0008ab4ce42ee69b61e2091976429cac4710cee8e9cd42f255c0aa56e82360dee8e6dd5f2ecb8a1503cd003fc6d7103ccfd596f12204d2f62dee79541ac481c86b7911094449475e9448098c39ced89b9581b83c8e5e959e57fd2c4c589f3d0cd222fc4d9c506139efc90aff48016c5aaaf6ff97ad0b9cf1702d18ba4a4f84314d786b1be8a8ca4fafae001162054835dd0976c43f28c0a972186080f338cd0eeddcf30e8bbc57de2b4c03ade489325ed3eddbb55737305ab65424b995ea324ce22db80318f7b53a29140923af577b9f531b3e4917a3bf3fb3d97052085db71ac1991075ecfec14e93e30ccb57c2bd7d33d21f70150b70f63ff56cd0ff1b83bab3bffe7eb393b0b72ef60aeb9d728d471e5678cad261dab3aa293ac6002a8ca601257a892443636376e2096c0c4757371718b0dc331213fc6e35400396b2df9418d9a9ba45697a3a41ba06f1d71c24bd138379fc67b07c6a5eac2c4e28070c3036c921aef7a7ef36089601c1e4c8295a954863fbdd18916512fbf674cc7e496d87e186eef418dc7424b40cc1eaf1af00089fdf6de206fdb5f76d1e7a6cc4a802b105d64ed1bb2619fa7d0b70ba018175e1a38b65f131f44a1ea91570092cbea7979227e511397ab664b2092d1a7149e578cfbbf63fad24177d688d2bea642b2b7cca2bb734f1761954500c673d37c0fbdf8507244e63c096ddd71b03ef84fb03b02c72caed6abaea7c5d00a7335aefdd5708bd5752da2fb18e310a280b57ae7946341d05e50faa87b8650329e26f659288548105b849d4259ec3712c0531e848a74f0fb7607c631cd7bd0795d983876796c15158e747ae6867030a34eb8385127c2d0126109cebbefd260025bd553a7782f69fd28637efc9e8bdb50ef50b609ba0c9567dfeb5eb013fd80a0b80d4ccf0173d1dedc62deb42914ec39eddeb7806d6f00cb1c5bf7151a42d08535e89861c9faf5248254ae5c9e36de9403cf40958e7920875cdb7520b75470de9f16b391e28d86171050198b26fbf8ce4c17ab66e5b3c7435effa470d5f9b01c6bff07d7a73cc5d99bd846fe8186889f4b95d9190b4efe772984a27f61f500e74457375c6b815a7706e487106b90529dda29cefe478d7aef91a3a1a5c9e3801728bf7ca0d1b79e8f694ee073f03e190ba696a974f01868418ed21658f35a4097ab0f862c56b717a2f41bc848c935550e6ed157c5a96e8a29a3eaef46304a70d8e7bb5f73f82cc8adf1311879af04bae3c188f7da0bf3d63157080cd9c089708bd8667d112655f90b4a9899563e8febd59142dc40aeb91d030b4d02b90c6580e31ea0ee25e1d95151cbabb5eb9fed4f3e34cb8018c6acdc2c0c3b24044cbda011dc3624ef19f2e23d683239f4bb316b41c75c52229984bb65b5110afd27f2d0d78481f27d483e889c4d5e6a117489739caceed760422943d6e78d45a6fa79f080c0b1b5fddcc05f932835b7b553afdc04df8b377e92b82ccc027f82cc9e0c10c9544764ca5fd4f361f52d6c5586b05d855a083faca2132d1f10cd08507e64005c46888b4a07a8f7dad501cd96d5c1c5027664391d076b640195f654ff3ff4408b95b10aafe0d7954576b268395e2ffcce7636b61d88c8eebbce1b11055ac300387269a9c40b3c0c7c1987fb1f62e840b9665f9b590b5e6301fcc64020c3f0d00fefeb23b746a3e437da5df85addb4ee90b93ef7b1a87f3aa6682cc84faf6c9082d358a23e08c5263c41c092a3d4058d95c2ef32bb59e7bda20239bdebb013d00abc264f1076fa50e5ecf8d72bbf717998cb388b4f625fe63f425a61a1f955b09be8f72d247e7abefe600a985342058b3931fa5786c693d744465894a2860fb0e27fb10ee287f4cdacc87345d1bafdae522f3031a64222ef9ad1ff0415638e20c7138572576c0c02a6d78bf47662f94ea7f939151c435cf19153b6badad9d41019691f258adddbee4d4901a65e53c0ee9c52dc1f4c9f81119ef9e925ea35ac80b76e893fa2b3b31e417362f4a79ab8308347a05cd7a2ec8597c5466105e5bfa0ec31aaa36eca7194426e4212c27d5697570b938c8ba4885e59a807ceff55c870437ea00be7d4136d431daf704c106ec0d90d216e00d0c5f64b13e98448dbf1f09ea56946a1d61a418a97752fe66eb435b424fa21463c6828d833a2ffbb720b8099001681b5bd2dfa3cc3656d68ed2e37707f55c6a119253b6ea4da01c28223200a801e84a3173758a0f312d09548c239b81e84734a75ef0a03eec3f21c0881308d82d4ef8f4268b7b4e78b853b9a52bffe812c6acaf765322b2fdf5d56f1eea0d7f16454acd27e0b5ca10d90ab119ad13836a6a053bf85e7e0779cf2fc8809a014764cff0e6fa32f041f58015a666199eabe86c7ee6653cecc35488e3ae80da00dd1e18d23f89621973fc709e4e95bed1e538d125b808ef9a02016d2e34810d07c7468075e0ffc4a6bff39a0f5f1b4f6f3bb1771f381872a31b02d35391913f0508931ce22522f4621086b5cbd3472537cb8dec547ff6b92c722c502c6fadc50c602ed1e7801530987c5e56da7b858a46dc25609a2db47cfefb541cc3970d1703a785eafe1cd4439971703232d352127f0733c89a711d95efc459629e0402950e7727fd85935eed3df211c3889c4bb3339a953a89d297680706957745cab0ac03d46ac7e53c768e2791bfbed1fb0df71828e5318f3774db4156107226d2ca6c04b6bf9e40a04bcec857f474ab97c64f086277bb0c930b317a46f462050ddab000e7d52aeca62f25cc95de318b1914fc86cb7ca08f1ae302b949c2d24357e84708a6d7c6c45c14b56fd63cc965e58e97cd29716863ed5fad36670c93e7fbd3820729d34eaf9fba6b593569ef3ee0d46108b55a1e3ca5cabe351e2fdab7d0e6640903837b2984a9311929bf261fcd396dff5e68c96bec780cbc2321d19c2cc7d10199463caccd86002f38ac0ba1f0cd859254406a80ba0d1b905b5433c36c325104787c9f394cd29e2ddc2d1a11fd424daedb5d23874b6bb386fabb8ee92e7b6405fbc1178aa7ce227824e1c40b557f7f9da198aa3bfbe71fc6cc5daa549bc6150cddc451bddde1154822acfd742f8fcd9cdb73e253c3945162a74f61dbecdbca038d152144195a0d797e93c6e19333ecf0dbf04f72993245dc2468ac5b5327c60e98aa9acc8aa4025f126fe973015964736b4717c2e41e9e028c144c6aa394ff01ffc444874145a53e50177e9d00d492ebd2be46ea4a16e2e5bad206e62ab9b60e6407155f5cf2fd199348f1b484a6487dc7dc99aba0a6b8ab836b7c47871d8709f08ace652d0f200f1d8cfba21d49f3353b3ca1b7f775fb0436eb211ddac63109e99a7a1bfd75a2627c24712a73878e1435b328f05ba5e2122492845b2865280e818927f0faa374cb2b41b15fa60fdb83c105fa4387b38001dfebc8f8f8f74504dfa1703200b62eb0bdefebf91dc4fcbe78ea4f40c2765df4647f85b21ec19b08acf4103f722dd267fa0402d4902cb5d8dc0633226bfdb6cf2efa710096c9390292cac9984566b27e1e7bafcd38af400f38f826ed6c8554561cd5040e40624f0a6e9f3a15fd2c1beca8a4253106d33ba2bfa6ec72c433be731de3ef8ce4eef0066cc53fa60a4bbb0d27c0abad562887ff44a369f03db35e76a3f6be19abb9d7002a1f43b8e6548be2dbc7832b7324e9bff5c24b7a08fe6a46031162c2d7d506069c9ebcf7565e1ae7366e36d2edfd1782ef2728eebca863ecdcda74e5098ca20fa09d26345fe5148da6b0fd49ada57ec5ca177573140decd9dfa4e25efd7a279deb903530530fb0976ae0491b620f6febf12d761734bdcb542cb11742094e680816adbea50267072d7d319170016ddbfd5c83621e5668421727b6e3660a0653060b6b4cb7108d4936d911d27ee03d470c0de0ea46a9df0d157150e43e66c6e5006c7cb01b53e40035cb139f02071bf18943c5715682f9e5b6671b0df635cb5703b0684affbbe1c34cfdfe1b17d291f81cc0c902e9f118f5461eabf48d43f3d705244e19e377709211a4cebe0a58daadf3add35d59dac22b83a6570d3848d0b303ab4ea13d4b15a7fd02fb28a7d8cc5cb538a76adb3f272aeb8df3e0c9335b7d062ccf3c74ccf4c3e48ee4fae7c0c1546aa7d959292a7aa60e7315c5baf0a19804 false -check_ring_signature 7303adc0c3d2d4c0840eb27fde6b2349e8551e5bf2db3198a31d300bc4cf0863 59ed58cf78c826028137929bcdcaddfd1b50f626fc39022133fe1b635cf7719f 2 a03478fd70f347f1636c39c90db7187dd8e053e6de1a45b0fd462595bc66b27f 57dad4f00dbaaaf8cfb461aaf13812319f11e0dbac334656fc76c959bea754ab 1322b9ddb96fbeca97c07f275da1938184a80c59fb6d2696d35985b3e5da2b073bbb2f62facf924bdfd0fe7bef34a800f985fdf1bf61ef9f99ebb2f1bf20018e2128afaec498404c973297d574d19dc3307a61044b4410998190c30bd7899a06cce6ec5de643e08da5d0ae2995984083b8974d2dbabccb37c209955d6737fc09 false -check_ring_signature 18835b11c801fb636495a3e3dc50ad033fd357cf0b2ee40496b33a59aa645a70 bea31ffff7f1aa23dc3339a1797a8598b9e51f3a691f5f1831456fdab940d6e3 3 634ccc39dca614954a8cb4be1ca442f8aa92fcc865fdd8db7e78199fd4d2efe6 63f45402d7e587f0a5d4a8f597f916a3d4856f6762514e86ed4dcd2a8c0ec203 4449ca24622fecb7db12fcbe982d3d6ce13372b62890d98870e6b9601ae8d9c8 ab65afb167bcc993ebf93407abe2747629eb6e2741641af9f60d8b0973871d0765f77bcd2a5604ce24a9c764c572fb55c392e82af7e5fd48794b53bd064f75062114911f0b27f06b96c077d350ef9c5f28ab1d55325d7da161169b2b563a020b47667582ee4c765bb38eb4c81040d753f807135c4ac41dd33e174b2598d7700ca5e9e47f27421c4bf574d4a8fc2775f60ee2c30c1ddb94165c1633b464583903bdf6478548e18c0f901da95e02c8f989dd16c0575fe8ba9110c4d9c35bddee0a true -check_ring_signature 57ada156f409efd678917b6a0839cc132d98169df72b00d935c3b87695fd2cd2 17513574d294da299863b188734762afc792b95450beead2d5584af8244242c1 31 c11ae9bf0c0808e52a55cf6dccf6f211054531b7257c330aa75fa947ea4d9cfb 63e7aafdb40fcc5a2936b69192af1b54f4ca09795dac466cf4def6c3f5bd4ea2 c3970bea792458be79a92f41c7bb6f77816139bb19e619a88ad1e8277dfbf63f 236e852e65a00bb40f155dddffa9d9ea70266812b05f88f89cef7c4d067b58f4 65e432d3b07d23006cb381a28e802f7807372cc7ddd3dc1e3ac251d3b7813662 9c3b75e4bb0036542328aca51fc26493565b4431c57a5e63534361e22153d811 aeee311239808384c1913617fbfcfa9de82e2b14fe752a3ddf6948a2030b09e9 be023801474399b56503e248bb088341879e1d639444551c3c447bb90a60eace 29af76a00e3808f12e01c25cab39129dd87c9c15117f23ed25c12409ede51e1e fc978462488a2ac80b813cda482c26d2cc62c2163bdb0b40339764c43752be3f 942a6947d69400864cc4bbb29920465eea07e82fb9917c66e939deb2edc24e00 fd148808b060647c0c8361775613576478b5a7291cbd18760daf090df8cb4b2b c077ecbf1a0b06f7cf7867a4eac7b6eb33dfb8381919a97ad40deeb8a2f5eeed d2e20868aeaec5056e46a89acad384642143ccde5c53464c4bf6acde8ec8dde7 725c6f9c4cf555cdebf7970319fdfd764c6c49ad4a33c7f22af388bdeb8dfa49 3fde4d9b4eefdb507608882f0fae5131f1c817cac2d30c6ee049a09f99cfd51a b188a6ef4aa01d9397fa1690191ab49b22c2850331466cf1bc81331a5cd10b11 0e9ed64b47cec06eb751d19df9eafcb92f6b5f9437da34f564c2d8fd1113e153 e9990c7daefb62c5246b2a2c814879aa5f804069676c53107bbdbd90f1acee9c fecc4fa2f9ff18b4f77269f15aeb3865c60e9a629e2b8ba0b7b3739d03cae81c 320e30bbca581b5df562ed953d0e712fa1e89de702a4ff103a6f4cab7f73a92c f55f3f00b52dfbc54a967c55a3da4d0dff1c1740dbffe3f0cadcd175dfc1966c 3d0608a65ed70c21d7d99a3eeb7edc39fdb7145ffea5081468fd413a07c4fde1 2b86ff84a00533d0a221441e4a158865f5f10a9b5cac24b778e2ba26d2e868f2 0baa3df46cd910278da2a47e3cabfde268cd1f74e03885d26db32a6efe9bc1c0 67296d3a49f9d068662d5ebddd35d989abc642ee24f94c87faa05b733a96609e 50ee75edeabf180b49ee1f1aee9259b9dcb504725eca52b7c8105caa1c797f38 61646638dfe570f8b9a1471106829a9b3e9fd2535d730edb863a7f9816e4d2df 5742322308efeba721d5c4d14cd87dae27c83d978d7e9bf1df421b992edf1e9d 94647f286806042b9a523cb3e5f67696d31112f076d830c21d2622e1f2c85cde 896816ca3b5d135d20d84194ce3b411c063cc99a6e0f42dcb9cda94737293a59 8cadaf3707655dc4b68a38e69c1da0ad282ddf717ed4849be68e9be943bcdb0262674b480c4e19de563ae3504cabab684f21ec84bbc3eda1ee5d7c73bd359c0670563d4533512c552d6e565edcc8eb076919f5b702acc7dac4ddde9219fe310392cc468e550b19d73881a16f4049878e6ec117f266edff34c0d3e6fa96a22e09c33e15793841333fe65f6c2c10769edf5ec731cde6ab129409e2fe468d87ef0a59413accc5d97ce791690cbb8ff78309df47d8d01d2dd457f5aa30b1a138370a42f850f8ced830b53b8e527f357555ecfdac832df993a273ac8e5f7abc149104ed389288251793b1d55f36c362ebbaf03e7fc96362bfda0433f989aae270e70eb43b6e3cb91c9fa097a15bfe2de9310e8ce4345b1142fbcb2df986ddf3dd1a05a882575c7113aa89aa4f1765bffa48963f999bc51ab73470daa107951c1ba20db65885335ae6c71c2ae533660729f51377ba21bc6483f0320c3b26e82d9de7069084c6564618abb86ef264a4b6f4a7818bbe9d8c83b48d6049edd1eeef54de0af97858798ae189a687a08e6164f1d99324d142b40710a799993b5133efdf2d0745a89b0d5018d2b05b9bfcc8e767cc5edf5279e8871a3e036c2932bd4272400beb10fc9f8cf4ba2f6e3fe07c0607b2498b2e5052c7a65de29e3d139f168c12062271a7f45e24a269d31def5bffd093b7c1f31cb62a7cdfa99f154b11441b590fdbd5574adc6112fd638e73255267ad9ea88f503fb995bf245d9c63eb0b3c990c6d49bd9e4c5c1e7ce43adade7ca529edaeccf3b3a96609167d6d8a9651e5d4091922a8d1553e32d0ae3185c7fcdd9d55763185e25980aaa2d12e116173a8de01b31fd3b6825f5d86e165b76fc56f4d0e0c9ec99e582a9ce78bbe1e1511851203f0427cfd8902c30f2bbb7f59e4ff2e55f68316672afd83e07a186793f2b13d09971a0d5124bdda705c92c757c33d1826da627ec25596a991c084bfb30562f40e0426f7ff667017ba22b3cabc95f593d6b574c4a5d8f19512108bda5943f29f06419b4cd000188694f6bd5ad960acd3c82d23d8e6c4062370fe30d7e6046d3e05d17b9d0f7e707ddaf9e1e544c1b79bf8ff1bdbe3bc5ca4fd122b1ab4083407061bd3e1c2b4e8b7329d37f73644e36ddc303cca49c514386a4edd4b78f0f3420950006d3fdc5183813ae237af72abbb6b99e5a2ddbb6e2b197a40669b45166606deb1352b904f0555f8cc16ee813c1afb4bb74faba3f1c747cb1683f013d63b0e3edd6f62bc30dbff73a6bdb33e596f3564fa4dd20949236804d5be8868988807a13bc91938fb1b50458a094597d116db667ccf43b247d6ea871d84a5a282d20e24d63e6eab461cf029a0f389741d5ec3e10681d979317d875cf885cb3ffb620d6cc6aa497ccf21b8cdce8a1f3321936a366ff155dadad9de359e819931dd220601f0ee21afdcdf24fcf485d4710c67c645f04c2dc1de22c8ec065531c2a81205c86d899048a90d5f05986c560af2de675981c724c623ece8e3227de920bb250eafda3fb21ecbe90b990cf205418c60371a556ff30a6f4a73971bbefeeeecf6063d11a5e1427db1deda3ed75cecab61313866b9d972d69773e2a83eb905a64f0106ff76174c61d6070aa6307b750eb77dd25ba9b69fce42c1c479eb8f46c1e40f64ef76f9d342620f12eff69cc6e7f51f420b0c1b9917ba00a0248450d78b800a37e5e349c7ac23d045054392660f8c14cd6a3e98daba6aef504a1736ee0c3d0515d9fbff83aec170cdd8424532f6fa3175499445d0307ed8f6992352e5460a03b84363be506b0aa238343f94ed48d4b374a0e5b23e11c94385d14d04a2e544024bfa5b76bc7c5844e11fa9d785a1a0c51735a77fa77f1dd25a4a4e0a2c8ffd00b50470f2b3eb932f7d201dcadf13fee45218a233c2444975b64d9429f7f3a80f534ac151b0cd3e90eaf4c4eba8abc5a3a54d5b57a7617071d2f4cbdd0ee5ca03f5421ccae0a324b8c221eab9dcdf992effc036f9ba70fbed74981f7aa0ce1601a5f9a802d72cf344f1b9efbbff08bcf85fddc3077915514009176a0fcf83b10a54b1ec56e3adedebf8882296a3e7f3f8dfb307bfdb55c67eb729e1d5686cb60ae7bacbafd0d41cf5ceeb42cb6b5dd3e3638504a961d2f6787b3b1068b1c14805937aad9256d19508e63abf0fe68ddeee6b71bbb02a975a3c50e3ee0aac10140de15d72e7dab1d780f32e2e4c69190432e9e29066125f35e5b04b5ff949c29a0f7f64039e7768e71ddc2ab46291853b162cb0106712996952c5f664aa8c11e60488211dccd4027e2a83c1a452da6eaf7fc8825366e587fcd0397c11ef22a9d00d8db75b9f1f0b21f5421ccfd0e0e20c65ef5d0502401577024604ad954ba13c007669da1499edf29a16501c040a9db4b2020b22043146a54200ea87f32ccf0a0217688c7d0a51eacf6e32b4a24cdfc10a4007bbaa566578d28ad787973e9aa0026f742ebbd854fc8031d7e49c8812f4032740ae6cd76e2869ad6c7bac29ae1c07c16b129109a9720084d61ee576edb9390410c8a13b4f8763f16a22c5e7d4fe08b059367f7b8f4a4f794ef0f9d27209dd86e420e3afcd642db2210bc3ffa86000b3a4e5647061c8acee077fbe4451ad24efb28a390aa061221c12267c09a260096dd59fe8f8c014e765927503a6935c64ccb6f5e68d6ecdbb73aac10c606e7f0572dc7f96138baae7dd2771b555538ae636281b6df7439c85bcc4969d62f2830b36d3139e68e432da1d1e81ffcac52c164dd6cf4cb8917b7872bba1496e711c04 true -check_ring_signature 4f155cbb0859fb3dcbfe6cb65f240a7a0f71ef07366af1dd6d68d5eefac6504e 6d3e6de6e178254999fccb9317b46669350d80d51ab841b69bf4849ffd8b2ceb 57 41b1310b4c814052714f47f8b731069ce06904f6e700abe3342857ccf52fd841 306dd290595b7ce56cdc654cd35f5a37087c17079ac446df6a8216ef1a3c1392 06378086aa7bd0ce448ba28d6dafba5fb239bfbbfd9e61ee88e94b2472dabb01 bf2ede7a185a93d4fcb4230518a645ca8e71c475e4b3b192b2e0fa4477174741 1bb7dc9bde1fa1873aa5acde07ff15a324c976a49803861c599ded1940b530bc 034ffce396967351bf5c909213b590d23bf236fdff99ce4b5d06aba0bd759180 646b7183f9a9fbfa7a431afd4a696dc8a3ec8bad4e6e299b596938962a1e44ec 2c128ab5ae6f90f240fae129ff6bb724123cc10086451ac0183228f706a64199 d9c21d2b17e355ba25d27162fda31f496386947bd838468a0aa02898a230359a 9ee4d4b83e1599cef4e8bbb314385444fe20c60ed33b683cb6dcc9d580fd85d3 797077b03efd4cdd0507ce95ff84fce54be15a9576b4db69dff5c4aa005b5474 774e2b5d5c75ad3f9b042a2dc21d17b0ca39f711fc416bd2bd09d1a8c8cbef44 ecc1323ad0d5c1b0411f3dd326bec1d095d27d5fcefebca64b623b04d2dc1d51 bd2973a3c3c84bc25ff1ad3bf3e57921fd99bf992a9310b44658bc3927ad149b 563358a8c69cd971350b17224c21991ec449543c205aaa1fdaca2cc459a086a4 83cf3dc6f710115b58db1c9cae600efa924ff4336b1aa0c81f2f48d5ef9a3998 37cf4631a7c372b5fbd53cf919493caab5e22ab310e8666b3690cefe517e8d36 1a4b918d106c7fe7bcd7b952aeffb5faf9d1e1f73e5b462dbc08a32bafcea4bc ed33f19477bfb3cb845769635406f2174d50988448e5f4420250deb1701568e4 dd486592fd65824ba69217a4a38c763edcb350d291aabce42cbd83894b5304ce b8e374557533dfaddd91c5ddcffe575d4777557a8d44450183bf5b1493c7879a 121272a2d4c2ae81f9b3eb6d47c81c0ea335caf4be871b36d5a605fb67b6ec3f eb8cbc038156d58752a598f234b2cd7e28ccf65b9f00a1612a56ab2cf9c3c9dc 5e5a8619ecf6be12979f4a8c09f526f05fda0b8709593dd5fe061782a6664d7b bf9841792089e06832b8fe8d57fc52d0929ac7c8ec7a1a7ed5372f1c50650956 8f859ad997c58550585a2a5872f68fc0d4ac7b324c2f305747f01b03e4c597af 940c9263cd37dfede21544078b0d3862b37e6c2e4cbbfae6e8b63440e384e155 5b5912c90d12abe57cbf2512d67ebb4a01717f1b587efa08ae17e4b07d737273 017d761d2d5d2f4628e0020f44e25787a83b577f43d318f63b1be8e280d0329f 8cc6c43c303e0e83a4088ed45c55b3e5218f2caa26a737710f57a9e2c560d718 d9398c06a650bb17ab2921b0d5db7d9e7b0b1f471d54ed1167f0934056b0c44a 15f85c8cdd87bdba76f2b2281e8a7c5b99baa6940d07e06871528b31dd4ec2c4 df1c0da96060f5e4069d6558dc3901d435936fbe2482b3a451004fdeb7ec12b4 aaeb5b52acc5130e98ac539f92f76148c5ab2fe94ebda1fd5a80e1fad243c5a7 4b4bc7f30a11371ba63520a010cdec839645729fbe21c665ec25add1b63bbef6 eb9275b2c208a97ca3ba9d8d4cc403c37ff0abba7916e3905149126c9923f0e3 e001f0c67a1ca99f1f8d5b20801f34b50cd0c3e29c4a623ef707ca8f66ea9ff4 06419de5002e55af964b0870899748e041bd1eb879ad434dbd85348feb835f14 05cfba8d6f3507fcb9b282a5dbfac28bb8c348b6fe22e0135070c7824b3cba44 c9d690adfc10876063a91c86b5e0272de42441168a9b7250b2225e714439ee8a 364fdc1fe382649d7b940bb9930a4c4697010ad6a88f61d778a207a90a429eae 220ea27720ba24cbd1fd33b02b192ff197d22cb37e301bfda898d97822ed34ff e5dde188295eb632cf0aad9d9bae113e7cdc56fabef45236f13e77a97d38428e 9f2759f2139661f8cc5e0d02b3377db3197705883b1358a869549ec7a6a00515 5e1a58eebb20e044cfaada2c5d8c0de9709df0b5b0046e773a94512c80644763 ac540474a27e0369bea53be80e6b375ff8421b39e786c451c27438710795f99f 3daa6329a5f2e3c23bcb1995818738343003e727419f88122d78a762865d6039 a0fa3ad0712c6f74f58ba60697e97b7861df98341af808a9a3d720cadc02bd35 1d9bd724571da66039a8064f62654e528e65add6246b01a06d053f7cec578fb7 e6c82313e29734f071610c754bd3a475c3ecbd79f1e0767319874cff6a8cafb9 3dd2f24fc30f6c0354b546c596b59b425afc534a7e2d5ce785aebd05c888942d 802c1e273a8c174efcf621a752366696b45eafe774406fb79bb5b5dad59dc015 e9a875d1aee164acb1417a6856f521ee990fa53fc3169116c31b02ef180f5cc9 c8150da28188f66a08c6b8060b6d5dc9a571a57e18e0d48bb30c741e000a93af c721b72277c0eecc0cd3ea5291c24a5c81bdc0006e4e0b5c6e21bda4f61d9f79 68a07eeb196a748d3d989a5f64126d51ec4d56815d2a4e67ddf565464a7e74ac 8b5718d58c21a3ffd53d76d963cceb5d79eb2f5fe11de73a050dabc9ef56bde3 e3e7c4b82dc02b483760ba9621644c14de65c002e71259e37014e4b144241c0357fff1a132b129a0f596a53744da0221ef4684334f390c9a29fe939d9a845804a2903d577d1b5358b8a720cd5abe3f44e7740ebd6244238560d3c28b05aeb005a68e06b073b5378d81257d650ec0f433bb90728cffe071525cea7a90e191a00b995ea642601d7e3dda1223158b0741225a96922d121ffcb791e0ff2c98f4c602b1b99fed7436d515acc981743f4a5ae6260b66a1cebfd883d7d5955fe10fb805a9d858a34fe6e426d4fb33179ab695b3dbd046b07755fbdfcaa19747a776470bab60a8406b1a55a3d4ed6373cdb63702398e4baf57776defac88a438b84e0503efd8f4f4528db68a75ca028b3929032ac479fc79d6191c20dfdea182686b000eb7d581b3aa5ae4c23b56eba7923cc68ed1f6da9aafff050232a0d6ad08fe38027944b4b4029eaa4ac07feedf1ca708de82a586860274d36616a1b01822559a05c5772ce32b8a4b46fc5709303d18149bbde3189dacaa5c3fce881f9156a7c70159d90fe4da6c6fa8127d3903cfbe54467cdd348ccced2b6ff001b65ced1845010e70b4214af6930eea17b22c67bc20f0e5364c6ea2a7535b1d5267d41e42a709f980a8dcb49203f566ae8f0ceca2b5596d5b2e4cbebe2275b9cbd44261950d09007b2528245503cc27faedeaaa8a3f11005f472d8a3174523a9a53832d4db40ce560d9b3a5153613bc830ce34ecd6ce6dd5e623ebfe6f34c3c0227dcc12d1308fd88cf049c8dbb4386597feef76142b5c709a74532c513dc8f7a89e0da92e501f2aaf9ac9944953416cfdedf2c1eef2f2daa5436f925d8d48275f28a81de7604b9f4ca50ea3a40513c35395fcf05df79ec1f935f26b854749ca1c32d6b22660491c67e8278303b37ede1234d06d377e4858163fb2a1c1c3eb41a0a34a53d5008183c88b77a7bc0686b66119a52a3399a286d794d76ec26cd9b1bab81be578f06dc70aff5efee91829014823faa816134711583ff2829d861f50064815e8d360d0f39225c5dac6ae878ea1c7dc48ef18befc6375604de342cb7a0b5ec56023c0ca08c281ebd41b5311578d9172c39c76341d81e615a50335fd5460bf3cdfe1606d95f69af422fcea9bf44a2ec17fe8988095dcd03c984a0fa0faab6b5d7ba3c0ece05b818402c486685c618e6b2d3fdfbb55fc233dfdd2c8ce51a1b85e80be401b28b516d8d17b6c2d444e9f38e4ada742bb023a5ae610910803b072406d8db06a5a8a68fae650facd51b58c709392a4d44e0a28c15dcffe4121779ca208afd093a3211dd347447091aa20dd81075e0fa1848ebe8f32f537e94c64146feef1a0a943b1d71434a9e8e065ecfa2f028de733950518a9f286eced77203904dd2e503d03a577f5bfe74431ab1f717ba7400b0cc1f59cf9252d2ff3ee3322a1adefb0f1dd0dbf07459db2ea270e2c3f946f323ee9405ac990ba166482c0387f3546604ce82bc63f26f27291126db0c892ee598147d647d8f6e7f87741213db6126d90e64640fb0a66cf8ca79e5522563cab31f6295fd4c05ad0d0c17238b0ea8667f037044c41f611425ad59b08cca28bd0d1cf69f3b8da1d3eb3b7bf70e01029bee0cd70d80b7082a43c6dcc2491c534bcfe8bbbfffe7b4341d41436a8aced7db0400efd280c9d45af483f1ae5ab4ce5e42917a447b5bbff2483be95f961924ac1608269c8dafe0523e3d3c49f7299d84bc0580f3e216412c2f9c944d7ab3d86b5b02b9f8d172888910aef9d319f534ac5f3135dce56d186a11a538137e30d92a6d08b842941fb099d3437f24005e47160e3641e6a521c7e9bb779490a64eea443f02cf4ceda11ccc448d16b200b0ac3027a4a0bec99f229edad019c771447177ac04188ca046ffef8d0ed552cc96b177e8d637475b84447de9813395a27c77b683095e0fedb09cfb922a29150328bacacea7dd7c72e80b4f2686bf27eec378e33d04247e54130207d97656426ee332f5d9a46c1382439daa89bab80790f3882f7700b9eb4b0154fc28130000ff34d8f1b0efe7879180227a7cd240329443e943700f463bac648fcafb92e559030c9bf8a6f777abacb745e35cc426719a75354d360c2a24fdf44a805082147024f05f84513b81b9139bb48047f1befd7d5c4c7ffe07997a8d400a93f5ec3f7cbdeaa48520a5af008e31fdcc073e6b5dfaf9042dce0ef7c00152c6cc8e217605e6da4fd635fe8a5830aee1c50bbad4e284c23417db09788e502a724547320cd03e5d940db179a8dbe8bab3a861cc7169968dd2ab9d0167bcb483e40f63f641d2e59a6e0fccb50c36c1cc0880a775784cb2a9b6e1880cf245ff3f77b5c2ab80a93dafabba5ffe5d7697753831677cac595717a984b504fdd8427b17cc40784e77adb12873f0a857fcda86025959e2917200cca03fb106c579f63a74c510e61907191414baede5cb5a4127a4a696fa1c361ca62f5225b4824c21a24e2829e6eddb7e7caf97d8c8d34afa150241d11b6cb21aeb67497a0b41afe66ec2d4bde28fa54e3504ef1b2da1966512f19e9c1dbd0d6a6d288a090b349d0533b987da6de13f7c0af421ab601278e4c3478809bae914ddb94875f20e1cd38009ec66c24f837f75a6926a1e7debe7a0e81a2c24b6a7009cb82de32c008d0916af60fbb769db21ece47c5fb393e169da0c29ebc14b142af6005274b70cbda912be535078595970b6e698f15a5169cbf1dab5ec5ef4b3bf36e63b3a620020322dc386188b3fbdacaceca69f19458b9c0dc02468fc29f4bc25fb14aafb0fdbfdd955d2d314089f12f76ecf2a20766d777e9d977f6ba243abd71fff47c40539b8501e5091f3d8d3ca89756414b04ad60d118b867c2d7d1329286433317b01fe3b241568cdc8620b4ad6ce001c83bd691c7e9c6ad50ed6c99c0ae980d6b704cd53787deb56759d8d7299667130b1842abfd98dd4361fde3477b19987127e0c0e2caad2bd56938522658e07db927bcd0cc43610c778b0d303daf60fae65cc03737b4893cd572f53218a4ad3c94cc0a55331caeb32eef34a535c203a6faf3f0c973ffb85b0593c1378b601122f1e2588a72135ba3ad9d0fd816be41e6e59e900e5ed4ece4ccf71f2d9120c430c82a0a44eec5233a8f089a1be9c40e524dc5d0577cbfddb3176d381a58d79cd8d0738b72c8b7243c42cc881afbdb6ee40ab2e038c93d0b003f5efbf9c1e8b8eb9707b7ab7596c08695912f85db9ed25dcc757028e5a07557037180512f70e4659456bf789021c66160a33ccd42e7ea6ac7e2c01969f89c155e7deb73754ada91cb738aed2c970a97e6f9da51d174d2ca6840e0d15d066d3a1dbfefebd14dad129591d6a1183c5bfa74e2c31c6f620d0fb2bc50d1848f7b8d66fdc0057f00caa969439bf6b2d13020ee869f1d9223aa579f7c40a10f03b6c5cee7987d4e970d6d5cb642c67a9f47f7dcb17b5992a86569f5909031d72333441f627a8c1a03543144082cbd9c57cb6480ef9a38bcbd69671c4bf0c87f71344e18a92edae0f5e61bef0b6d24f56811dcc8a12b49cc3d40a206eb901056bd3b09005bb5142b50514aef37f4d871a11bfbd38ee6dd9d7eba0e42b940e746d52e2ab01c33e0a24fb5130061706cfd9a07254487024f28228d7c53ac3003ab88ab56983b1eaafea1e78b416e6f187742bafd8e357047902930228438b07fbe647042f0fe28942b9e1a615da68f67a4a1dc0b5afec5ed99561925d00e5076fb43c42cf140b305edb34f45761f6abef240e7ef5a96eb897deef366707050eebeb63db30f4295fcc677b1d011ab9a7ed93852c0262f3a977a8b91f69e7cd0198a6532aa881ac29a767862c3bc31c52ac522d8d3b7b10f1c826f61a1dac0e0e364fc3d7256ff7cef9c0db85d35aa47d8e113cbf039e66789fd0994eb5b1cb059839f6a8dffc482f24a7a17b0cc80cd96df64848a2e204449dfe063b42bca6095aa38e598671429cdf3953840c5550a57dc6c0cceaef55d043a10e9a4933b90f423c4f8d12a79102105dad91a4fe6b05148c9571fedbc7cbd85ab168dc44880bf889cf385e0652306954cc46cd0cf4f03e73df4f001647c881595ff5580e540443679292266491c1af73c2df332dc053a3b0c699d6c49e775c5e98a7fef69808e08e61686897be7d149021909d945e711c8a572d443f046931957da5be3d780b2163a7b611928186d5c552a9dad147e7dbe04d282e9449ef9ff5aa6c96c70303e6b3dcbfc25004b99a0cd94035a9a1ffe7f436f7b26d336a52b6550137a96f0b5f6392ae0804a4d7e9b3ef46d5642f6493494da9b8872fd3583b20b8e5fdcc6f1878dfe9955a0b0cb0bebcea0d72b885c455d9e51581affe3658847a88e3fa0d9be7e219b071760f8b6c982bda581b33812d1e0d5830a9f587b447b54cc7b60e9c2ec6697dca66aa8755680dc3487fb06a583fcdcd8dc13d089950dd57d2f303ca2f7f3ab5eb6f2c3ab5f2b69db9f0510cfac792a74ca99d1c89bf7a54241f040827d0dbcf65481b9f9b9d78fbdf897172151221a63fd5e145d6e3f98b53f10ffa49ecdb19e78428be96ca8e366baa77a239a7c69101faad70bb650f93274c0977fa37e5302fed0b5446ab8a6a296523de1d7123cbb7a19d34f0dd1fde4e6f003a26cf529d5cf743dc79145c4b7052e26d9d97a845389e125727fe70e69a460bb106ca6c708fffd863b4572d3ebd94a66d6aa734d9da0e031ce00d202905c80942a0045128171b0c1ebe60a8fe8a8ea26bdc9a6e846615c65184bbf2c9cc0b08df7f1c5c24ecc1fb38820585068b1af0fbb853ebeb4959524e62b381374d2601b344542bc7e1319c9d8381cab3d28075efb6285f6959b8cdb7f5a82c82a3290f9b06e909dcad82bb47777a00eeb716b7afcbce5dc533024b00c143bcf97bdd09ad157218937e6c692033bee8d5a202e432f7ab4faf3b0ac861201ef2584eed037b6e21872dc7bc2fcd6562d332b86b1c6ef13bd0e54ce647027edaa38c7cd309f16c67190159628110270217ce37c0f5d81f96efe8eaed13df3105587e41870d196b50241bc84580c432e11edc2927fb88fe5cffb59f4219f8cd86fcc8f409065b11a5076b1ce1afedd87a0eeeba8005368de7e0bd39c0f6a1f08b8ee9919e0d false -check_ring_signature e094be8daa195cbae877ffb0bd67ecc2ef3b1375e7dac11e753277ba45f96db5 d040bb07d8287ddb36035ce316e969a6e0dbd183203c0a1ce386e3b6602cada5 30 b3a49ef8cd0cc92024278ee6859b3827afcf313acafa8c241f246e9a9dd1d447 3bcd8ba4edb74179d94fa8d576eb7a1fdbfb258acba90c38549affe4d55163a8 997365d3670479dcf83ff90667a27a2b077bdd190a543f2ab32abb9bef9e9714 bcedf2189134cc70c35c0cfd65c7da28a8767d000bdbee2244d402e01bdac26a e958ea2db379a8e2e334a1d0277dd5a92c8b38497d5f6bcdce6a35d6b734ebd4 7126f309da3c07b04825153185dc97c9d6c6caced24afc46d387f22601a02a7a 89c32034e13678344a76f16980d605ad77f6ed3c1b25f7bba6fc9150e6d7701c 4d07ee5de9d858849b3f0bcec46aadb05a69aef74ac1d0cdb42debfc109baf6f fbddcc26f062e904b56f1ddca12fa839d19e410560954695159ef5e6c9d4dc21 b79fe31ea977a97854243a9bf072010c1e466adc644a624eec81fca7560e9b99 c94610fadc6be5587fe2df6399f7c6c1d4fe03961a3e6051a944b34505b03a55 c14d7efdcfa1cd942a7c6c46ec77b9489bfeaff618702e4b6d8083e45331b0a7 3cfc09947cb6a7728840af7f4c6c5a8d0a663777b32c8abda4e594d2baa6d1b1 a801b9541f41df1a8b1fe201ea64c9046d516118cdb121c175690d727788faa3 aa0f6106fe81165c60f0dba47f49a26c044507bbb08089669192b57f52a950cb 7299a4f76fc7d3b81fdbebfbab0e0ffe3ebdae01ae39ff88d88003d1755cd153 37bb7d2c2dc5fe2eb1ed99a050c4e6b6ad094481ec9936831f8d9dc183656eab c83ae53f14b712cecf53c42ffb5b1be31907d76badcd085bf6d52f9de41caa1c ef3e048984884d6a1bad135daf24d03d5ff3d26e1c7514cf02ec2eebbad61806 4c02bbe501b0c2da8dd4fc0ae679c293e4cda89a7000e9fc32d8c94e80f52ac8 dc253429d52c864195ec57c75e78d95d0c4b31a54ecd8198ae7760d3ac17aa95 c0334e3511dbacad7deb52346f0dc6ad1d50207d065838bdee5cd2a1d078c2d2 87195bc91cb132d5b3f5f3a45c70fbc7046880925ea1018980ee8611ff545b01 7919a88d51b8bf21adf7fda63a4f0da23a1d10e023bde197eea9d63d79b98027 34cbc2ed4d6084ff6e4aa53bf1dcd1ff3f66dcd3a1343e3ab3ef34444028173d 7c0a119d9906e05a468d3cb1bb03c0f0f8b0082d22a607634009923e9a969dfe 0a8201ce36d159f0ce936c7c70637eb572e062e02eb24756e526180925e8eee8 bfe17ead6a538ed20b5784d8ca046ab6616c500bd2ed4556abf090ed54daf899 17966eaee9a5d75d1fb14946573b9a05eccb629ae45e5e68744f55c2bada57ad 518f08dc43e7fb1f50f5a7ef8b478af37ca61f02ae613ee7583ccd1ebdfa1099 4aa137d5279577bdc49e412cd41db9f05682862e15c913c8886c0821d80599082ad6000720c7b2535015252eb6b99d7319249ec43301939af1aa7965d00a9a07c7a72c8de1357d3fcf53bd3e45a7a242c52eee7f117d74088f9a40217b2e5c019b7e1887ad026ca8d021be6cf51ea11aae37145751b9762a24e6679308b9e709e07d358eddffd65a6157c5a5de472a21047f25dfb14fa3bda906bd00514c3f0b19bc872775ae1d94d9bd243243176b2ad0c38e6be64e8cecede669dd6ddd880872406298466eb880f9e5c796cf9fc689cee5e151ae4d5b8a7407103033624a0766650cee43102159707fcf540990d35dc4047bfadd75fbc2701d7651ad3e700088ad822ce1389e037cb003f95b55fa76c00f4444d6902f9977567f6e6bf3d7017de353604a7e40658509797fbe1c3aa1068144d580dc4c141b622753e09c71028f26b0549f548236cd00ae7e693a08ddb43e5cb0ec8577685155ddca50fd2e01075385aafd121b63f4205d81c3c7ff2e3917d31e93e7f36a33f732f31f8a5c00b49cf6238ab7cda9a2e9071397994434ea013d8c18bebc34d26e724893c9c40bbb9cff3671967eab4a7a84edf426863a2f35b15c090c513a5fd66cf19f629004ca537d939e12201ec940c001b4269f77824a54415d98b74a02faa3a9cce2a6001f72886565085d8489ff993fc487d4bc7a10e2a34b5054b0b2741b51210f290efe93308fc252a8304cc99b4e2ee755c50099bca66b356c6184a780d4f5d45a022866fd8a29fe1be50bdc7aea0b3f8a667e71976bb4ef93f45d52dc24388a960d8a39e43874fca256119d644d0acdc07ebcb0e96bc1d12380635b0ae3c404770566378d47417657573cf73251579663e7d8c58cdde12dcf5d822f38cd4abfa10e0187b34b8bc51b1be805e38c7a9a1e8a726bc0395dbff25f60224e26ab929f063c6d4b49e0b7fe2d636b8c1a494d96de835c0081324f64c17dbdadda2ef9a10bc04dc03a9167677cc0156c7f265883d99d00dc115ea5f9101a620d6bbde4a60c211c55e8a1c66f7fe85d05404d889a9ad9d2ef71f6fa45dee616f46bd97e520e60ed5dcd90fb2c18efa515a132de47aa16efe95906ba33858209166bb3fca005334f8efde809b53a1258f84b477bae803aa0e39d9e7d832b110c9dc2ea2e7d008226d3cdb431b5d5a1eff3d2c2c609ca0b7efa06c14dd8849c424678a3f29d0ccc28a3dc439eeed4a6471891e2825f0562a405ef4b29c83ab0fbef06feeeb200bdfaebe00401c3b73cbffebca4419195581ee7322cd237ec576b427eb4751102c0f1f115ba196b6b4b713469889c2330a53bbdcb001569d30c2468a117b50a0037519e63ed38338db83c20f6b92a1b2d8c0fd7f9b76244a9af64da7511853f0652eed3fbd1eadf54a01aacd610690e2a1467d80364acc7ba40a6957cb0a2040348e3674f0b6c03316e1d4c321ef89043a27cdc6d86399a9002879a10ce63000787a0295d019f25e5e4b2108be000b95e6766be161da50967358895206501c001868eb236cf4ed28a2851b62287bceb8fa27d4c3141a3511c85fb25b982f1f00cd555ebe6af6896d950a289796693d52f4f9968622542340fb53327315cb7b707abf075ff3218ea6aabae54b36eb1a3981846651c2015e712c3648ed270e2db0b779fa4ade3e46d922026f234ebc6e2bfc2614dc371eb2aeb3c69e73328bf1806701e584e5075e77f6863e3917e17cb17fec92ee2121f897e46e8dad3c7c0c1049e88035de6d8779c6ed5f9b37312f72b6eb07c7d737e691ab08fa2846509280a472129f5a00bd97b37b0045ff60a22c853d063d80bfdf724cbd7594bc4810a024d19ddd44f0910e0500a70294f3bdbbfd3f3bb96a912a1c572c3bbab47895d00473b3497a820a79b520e142e9a95b9b056fc9cbf5a5279078dcc4b39a0584504589b7d268287e47d990114aa150fd925bc833ed1d2add2a6422577f9d1a7280186d3a89f39fa5a53ea19c681aef2be041ca17b0d0e8679a3c34b03bc2ccc04006982964462f59a1007d1c2d9a4434afac161ca17a0c090dc73e8e6fdc165830c99b32bb975dede1368413c6d4974a2025087b9b812674765acd8f7f000196602cddc0df59fd77c70fca6daca66d3019eb02dfd112b5df16a813a5582a210830a6286b96524b2b53ec81315324d66e2989ffd2f7d94ee2a47d48878add8c98101eb1cabc37a456f1a2937c12ba3296016576494db6e07b29c802198459ef9bd00cacb43ecff751a593e62e2119cbd09f526555a09ce3fa76d874ddfb6548aca0e7ce0610b02f4fc8410b6253ef68b3feabe78200cde4cd7ad86b44b1ff217790f034ed501074c13b568faea12646dd79255abb28c6281dd02611dad7c3fcca201c651972860495c521e0973c8d1c12caa92f71d1d78863092e5bb1d52c232e6088fdf8d8a993eab9ab7e33616169117e798280c140e35433ad54286d744ba3d066f389f524679038df7daca85b41a397c611e53eed7ba52d9b9551c34025a9305bf22f2b0625c0cebbbf8ef422c1891c77c2f0837c9cfc756f2bf7ded601398023e2f1e030236be23e4541795fce756fc8580b68599bb5d5f6edbcdf3d1bb0c09ea009a403b09c24b49a893bf53604490e5483857af510d0316f94145b3105006366839a667b153cf86c50bc0856187dfc1de8ff09c631a7d96e3e72207f19707 true -check_ring_signature 7c98cbbbf7e1e2d844bc77325f38a18094051ce11609e3e4c70ec58ce0ea349c a0601447fb3ccc7dd4be17808f3ab33de0c3a38ba8caee424da351daae60dbbb 45 e3d5115ef151f7325688c86ffc4b2cafb975d24e2c161a4675e396971bd82bd9 20eb8c30960bee249d4b3b8522b27e7c85c68f2cd856987b6311f5f06a130737 bad54fb59de83bf8b899d52ada0ec3dc2d9edc50ea42c4a04ffcd72a9c810cab c3ec7e0c67919dab570912a41a93e7fc60b4e1e0c844269c01ce09a72fbfd1cc e707cea657dbfffe291164a28dfe6822aeac48fcc03c6043a622bf9963572cd9 3f1da424b484b6fe9db528dc5fc99356362ed7a97276c4e0a035f60c75bcd5d1 b2fd456d24b50ca6cc30c5b361f6c1ff4b8e92cb0ea533639b32beb9fb8bf7d5 94ffc9034904dbde45a0f2220ff3b97aa7fc571ab3f94dcea6bd82da06f381df bd7afd22e55bee939f1c17bce72a7d42a1a36e2db25aed49c01f46ba2613374a 091bdf9b9e4a61c9a7e4622c52ae35a7adb54fe3515ce20ca6e17202e16adeda 4d40755715910a3732d50466c05d07a33987cebe6f127c4f0dedf0f279ee87ae ccb315e3526a2158e6ebf6c9d9843ab1668a2e52acff6edb362ed74b4df66eba 96887b7d80119dcdc025612765cd7865364f9c87f4e4903fe071a0e460671f11 4aa08da4e03a22e852911c89d4bccc9c71a573acff9df758e5ac4e607f27ecdd 3ad7b171e30acd1883d5b2cbe3a2a218973b8c59760ab803e1fee138addeacd3 a3f356504af8566e80c3f3d06a0f20a99a8e4b6bdc26d65b1deea525728980b9 e3cf52ea76a21bbc0bd30d05635b8369454ac2fc2c6de44085a57a770ea8c736 ad7578c222b5b2f4e02b771db75fc8b840328393b38da16ae05db1d0143c71a7 cbe08c03529c433e6add82ac7190c6803a3280239c80b38783d0512631e47d3c 0d4c8162bc32dbf878fc08533f05488f2ce3cfc3aa76ab06328bea8c4daadadf a15ac703d006d4f1cc54df393b9bac05dee2e59f7e1bbd241b8260f93dc64f79 0784970e3c8d5710b109360ed2b756f099fc00b5142f18c40be351214c4e163b e2238ca2a0a70ca8e9d690d636a20ab2ba5491878ff387ff331c9063df3c7a62 49a0c5e76e82cb9477287059f1861cabf47e285cef5bf48773bcf95f1a5d4216 877e77d28277aec92c081f7b67ba694b23afe24c4148a3eac7ab71351658e992 bcfdddb74812a15e92a39f5773a1438d797d66ea98204b6047453f2513582500 25820b7a100df1ebd57c3cbf58f2ec1e7c788331f22f4e472f0b741761f29a02 dd93b0831b90046529d42c09a4186a786c288c9d8dba8d26a0eab5c8e52f0438 838a32a8a4970653dbdfa578bec3d6127b40cb1b87e6e3fa81d57340591d758d 362e5ab126c7275c97bcd14e88983964ed51bae2323fbacb317634348e4ae7b4 5cc00d801c16e8bc073648795934b603d6c373f2f3ca542ed32b6f1259bfd7ea 553326e96a626d154b0d55b8155b338aab59fd20b7034758205014ffffaa5ada ba02fb0391384c43dc5660614dce296c84e2d64bd95a29ae6acb50ee4db65722 418b5036b67dae3fb1de46899f901035d19ecfe602738aa48bb1da0d1c631ed2 573b538afd9dd7ce8df52fa8bf47f8ea5c90b0818976b0743108f07c184b13fc 21c57eae485830d20f0a3f504ff619ed601939466668b1979178bc8922cea009 2dc5707594d893758ea7063b7f944d6e8e50f9cac6b6207da9b3018f8bdae1e4 878ff490023486801b395076a7e35c05028b7ba31ef2a93dc2be4b6e3f947f6f 78d4fdc18a674580b048e07e8c5aaf07433c21bb061347f1f6320ca25f651c5a 9b2f36cd25888aa9eb91f8df5f826c22db714b1834bddb0c80e28cea7ffa472b c330a6cc3a66163c981aa1353ff696b821f4593426c1a1da68ffe80d052bc549 e4005382610e2e84e9d237c5796eb7872b5c0f5379114546942a35053d9c8032 856ba4da22d1f73d2ff2849249b345c9df41bd623aa62a8cacb28bdebfe7180d 82d4671825e91894e62151382beaa160f21bb9e4497cb5b79771c81c954bfa7e eae0821e7cd784d7e5b99a7ae470e85dbaf488121873a732462616083c3188d3 9fca9c196bdc80f007ff3ca94270c4089e6bf3abae1ffd4bb7c6073920bcc007904aba5397461e2dc893a47665c37f984bde0f95ca9bda6e859ac8d6e7a7d10ed9a287bec414eddebaea08be49107be32b8e3a606462423888d5e64c07569c053be07388ae40175c4a8a97e4fcbe3b684b1337a0edd0e3a59cd828c3216154092ad84647bc856bc53cfcd6bca899d7ae965609929e9c091dd7ec88ab863ac204d301ef12f2e24d9e3bc24d796de0779c77ef4c4561f6256ff7b7d8496b7bc2073ea810083fb900d252c52930f11a7842586ba7f91379180bcda79240b8e34e06c936475b2a282c054ab831b15883fbcc00c828e05d0b268e309db8f238c997011d920596d98709e6a714d180143af4d241e446242bf277b047039e640d97490e820117bea21b06d9b2c35422a2e53f36269d3936451bc76e92899de888f0cc05d2517d444d1ccfe2a5524c4c82579eb384a686688696c549823008ffe9ebb70b1a714c023689c4e737f49b89d8c0c8402ea7994a667d0ebbeac67022abfea0015bad9f9daac6ef10787a5089db318fff01aecf1315959a70e4c3936e2cf0580b64941d6a655bbe6ca72c1fc99c2119d3343955b3f31413ee843cd51e48ac95046240db2956a697ecb2a08c4a52a4b31047d6786ac42c559241a1fecc984d7905699fabb86588133494008acb747f63a18746261b9e1f084d9a395c06001bd80f6e6dbaa99deb5348dc5a27ecc0d2a44a706dc03b00be98163b232dc408444f0d5bd84e7a0232afa2501096c98b2702cf4d4931b41558c00ef910d26925fba8048a9d7127137897a476fcf4b0d48aff1dafc4fc88a0549d8ddb987f48d89dbf089a74040dba0ed0abd666c689110ea0f47e68785e9e2f8539851e1bbbd525680e26ccabbd6639bf98cfaa5ff8aa1849b755ec46f1260b5ee78b268b5e0ff40904c5716cf1800cc9272f2af3f48ccd44b0518542bfa668bd45a750777323646d0f6a52b192c9c9e61b604bcd67868a24473633b30acd2d3adf9baffa89c69ab002a23cbed3fd3d61a8bd210d5c19f11ad5be07785e07a3b2cf47356f537cc586098b3c3313055bf67ffc97b19e7345614223bd8cbbaffc2349528282f8d10ede01977331aea2ab3421e013fb0e3e39f0d5f8c6267fb67b4e668889b5d25ab0de0b4fb7b6c4e5cb78cb6a9461cdd4fb7e43b89f15278129a48cdb000814773e9e0293ffe380b2525e6cfae49f192302d4fafd729f9622537aa187c1ea184a5d0106105ac96566e751892467d65ca56abbb4687a7d0f7415ee8cb6f42d4e598a03022377da9efa6c0e43c338f85416cc2f2c0f9a7716fb9cbc35f2cd053a36a0750b4655847a258647a89ded5acc6ab0feaeb48ba508eca14e751f5b6ef021263607dc15dec0bc29f98de649e03932bb5ae2dc6c53d9bac86d053587f80ff577ef03dec538f4dbd6339c939f53c7d8e920576a979db4f40bfbe592f44948507e3905e750bbc87feb0cca1f0da7dd18535faf796a41443dacb65fce39332325cd0c00a8d81a7a7c49d4ecafcc8e9ee3fe9dd6b377f1349e722e7b5d7de937a8a60a055dae750fe79d8e10244aa848aab0d9c5a4e889c15cd4a43d9cab8f97855148065a8500487abc45ee64f46e00bcfe27ef220945acd54662fcb6c8111f9a92ca02caebea023bb9e42768e3dfd7cc35a332c1375f4bb96e03206fdfbc36a6c76a05aefc855ffe7b8e9b1adea5c20a690b071ad7c8902ae6763e722acf3d69515a0491894c7f64b43eb4cd13c35ee8eef5a1f370e425cffd346788f747f40f483409d5721456ae1ee290e50eeb4939ce305a793ad6d0916ca44d67a2d77a3c25d00fb88573639283cdc21fafb478196b6286d40b54c5099381b20a2d7e50c6536c03054091b794a00840978b052a2637ebc696fcc299ea6863f6132a4acb8e393600a4f90937de2c3c40f14ff6bc77188d7712bf86393483a8850879290e977cc60bd46710994565871f064793f7c64a2d825888c3476332f4bef694ca3a64ccdc08616669de1f02b92fa442b1f63b2af2062c8f5a774c752255d64dcc2480d16d03cf9a1cdcae3fcb919f7210d29b33066d0cc82dbbaaf1d339a7962ff43b3334063a4fd424aea67d67eeca909b8e2b8bad986790ba78ed8f8fc99b86582301db05e44e805af21d0b64b58b386b81fd088c495df6ac76dde0cddd1ae524b89cb80d05c32382c0bffaa01fe4bb030973f629f7cb3f1aa56f6a0fea3b84f2fc51a50f4a3fd6189c6adfd646f2ef864a09f7a6bbf674e2f8b717acf749c20f8f04220ca573350ca437072326a5b1bb60df66e14d36c58a77438cd3d3c4c0454bdb6c0c745b98f1a1b8939ee3c4379e71fead0d28e0edf3c1dc234d0cf8952b06735805ca17c7766343a6b01d2694f1fbf8b8555504627e1bf78b8198df5d079330a70b9cd691262b07f8bcda0c7d586c10a7cb310e1c30e456769f52e33df2109f3c056265514b25503ca9cd72d22536f203365a3737dd903885c030d2e9ea9496fa087c9ba7d112478e986353d936237a5856d731bddf852bdc21a728f61fd4a6c40cc77837fd14d3c469cce0d70638349af03226bae243072fc98979ece373768404053abaa9f6a7479e82694d580fbde174556a45d76155f8d8ecf07de84b32dc0a6daa4693e6d466530c7a7691bfa2f0f44731606faf19fec890e5a6a052cbbd001c49dc35c6235de69a9f348757a8ba7fd2cbb1385f4263bcf7ea943099031d0f336923343ed766eb1945a83271d33a2f1b6b9f0aebd3f7defa55f1f40d62a80dc4999cdbd4ff8c63f069f1b3f2e4241e3d12455b8c8c3f7e75b33a02288144042e56ceb1d3f2e071a380b9cdc7c75b558a54ee9c67205b21482500a3f8b6f60ee68114e510f2c7a9f1e9b58793f470e7133441c482d92aab3d4501e276711200a3ae86518aa8a95403c39f01cec4cd960d4f2a99827f20d21df6b21f8d01f90ad4e56b953a1377bdf5f1a543e420e1b32f146b1144124ab61a0355a30f15a80fa0da61a30b7b7ec911d5a0f23ab447b263c1ccd44450651b5448266c493e85004bf7f323f3f2ce1d166c8f84d15f12b1880dd0831efbba2574178087d515810beff6557533978405660cdbc7b28ab94ed102142ab334131de26fde70f8a60d0147513ed308a7d480152095370b9689514b139774eb81ee8a24c5cb263fc0960d5273776ea955b0bb6c096c0a383344c2fec6c58701ca9abbcfbaa8c64f1e89071f9f76a2de4f20727b2b265578fee095aafd6485e1e683c592e13850fdff410ecec52f2e5142edba1e2a98a8b47c9438f49315efe645627d3518b3a459c6920166565342dd0fc57d2bc16611b9fac07a119d10bd6c3083844dd4e7f06e6fc60c30ec2d0d3117d87a133f72aaaa44935d6b22e39fd660e6843035b9e6bf1471074bdc571b20af25405f6858847ca6e6e0e6dbe72d65672263aa7fe91eb9ca88023f6568c8851a6a5a78c22f6161c567731b3509240d54f9a87df10b2726288909e24a4d79b83c21246c7adc90fe436a9689fca0dde627475c21cbf3d98a0dbc0e9f305e400138626f5df1dce53b4927ef10e34fe85ee055a42c67067d42cc690f4dfb74bd793c5ae2bc5ca0df32d5099ed8b07405b01fe38f222278e08c887f09654480cc43ee3c3a69be6976eb9ffbcc61f4c99fbedad261f216edbaf72a54003f30fb5e68c381956037343d71ee83c32ea569dbf2445694fe062ad99c72600d7ddbef0bbb8d79120fbd97ad54c22c613251a645a82688217370e86203eadb098763a875e9bf27b903b0ab2e56a071740c46175a7d8c77415d856f2f58c5b908f442b2b6f2a83b19030fd02a439c6cbff69fe8fc55b7ac9bdc73627f085d33030fcddf6e2eda9c41e34b75482369fc7edf4c9019b892f472f05f0b787f935d08fc7f1f3d19c46e00afd5fb349aed07803bd4cdf34b54c490dcd1231921de40049f73684aa6a869656a92660a591b25b1fb2491a329512fbe56edc8cf65ce4307556fb3672c3b7a881ca76e178c2dc77e8f6a6059585b278b685e4e599bf9540d false -check_ring_signature d59e9f1184390e5d19af03cf95f6a72c28ca50834b2dae6801f3b6250ca31ef5 20ff34e49749b5a9b79bd085f1d7b68712dbd5f10c6f45fdcc75afb9a0bfb1d2 46 3de2b889d526f2de0de543863125b81956265d4501b664af98e28f4446fc9e11 1a4122fdd0e28acc5e5afac2aa7163427e09e0239b1d7f7370d020e0b9b89dc5 1b4d8e68bd01e73baedf4403ddfdb7c1170350b39866746ca7b57305ceefcfd3 1b64ed671e83edd56acd18a3861bdc974476f087b922bc309f62081ed72d209b aa0cb857f28ebd0b2d391ec6a6560851eacc703f3c3c83ee4c39dba5e505fbc1 d3c7e71b3fb76482eaf775989a124646f99c214e86a87eb358d0f0f3dfb8cb77 48b1af82afd4e0caef29b39669fa4c39c854a1cb21192dc1b24786163cf4003b e6ec35094771cd499e7f46f432cdc39ea76c9f5b244d2c29aac52a18c797e9e7 2b0b98b1c15111d64a017920422943911a3306b565c8474eb63466b93b92c7d2 ef2f039cd2d08f558772e550ffc78d57bb009de67762a02cfafe46ad00431329 bb636d5e604ec9dacea583d096a6a9d2c29c9b3688b58ad6d014d6cca8699678 39c4a463b74ceb9203bfd093ba0bf9747a4f34d9e7bd1472e0cee309def3e7ca 57aa569b70f02581a58efa9c80be8482126f8eca16a17365470abc1c7b530059 ccae7f5e75a4393b7ec8f3d0b5f375f9b61dd558a7f71bea3a440604397a50b5 69450dd9c2b8c7fcb0d33dbe36170f668074592c4af88cfeda0e1240e3dbd1af ed3e5d1967802acc3929d94ba06826732799892c5d448fe39e7954d936815c31 95793d46383614cfe389d5d47b92576416c285a992a785bf85376cf53dfe3ce3 c82e97ab1e2104d41673edc2a12e4b143ceb6fdd2c4969b80aacc4b8b8f555ab d4e2ce593d29c0be2b9075337070d945992d6d6445f67077bb35c10e1232c9c9 ffb018be2bfb63f42b3bf7f8e836d6a5332d8efc83dc846d50c56a657b831e58 1ce31127761999b5fe7b46998c367f98684c604565bdc6e135710fb80c3af53d 842edba6bcfb7fedd9c62417485a850aaf69a42a4ca5c8f6b1f44bc2b87ce86a e636b3f9e0198b425e038d2b96444fecd430be28d9f54455856e90b054832e63 ac78fac44893730cd6eecc712c6a1cf5b30d41280d8b6b1da92c4d1b9b1d083e d6da0dd40fe41952211e711884dec2d8237a2f5c4f8d46e58fa7b6efabc4818c 9307b9be6166a4054d9115e00057c126dd2473e8c1832390d7e87a176f5ca0ea c1c1c59792191116775b7fe868f97d4b88f50ab425d9e553ae583f610caf4926 bdf1fe65c30c1c23d131aaa306a2125c38f902e69b1357faf5e1aef03a7a5322 89d989972fa846d905a1b8f8a21fe7243071a5e96dfea95f2396525b3af15c55 3bde16d00a75b534004ab00d421f142d87f19f31252b17f3b116845abb070741 dd602b1eda910143e2aad6a80c519a56e569bc11da4c80e2176a6a934d2c905a 192cd7c1ec0debc28834ce6867c38384180440bfd1454c84289d2d0f0c71f04b aaec6b1b2585f8b6f6c979fc11bd0dc3cd695d8a1aae939e1f3834c239c11a09 b2960feedc1ff2fb95ceffa4ce6980c5bdd2c78b3f2d10fcab1ef522d762c51d e884b887c027f92230492b0c033e9776070a2d0b6c74f0aed19f5582bbaba753 0ea2047c0eefd0b24bb3e92b8280e27e03e600e4cf1ea27eb761d712fb51e4fd f4e65354ecf5c8e578411b966dd4cb125f0395dc920a458ab0ed411d2140bd19 1591dd0c6dff3690c712fcd11ee070ce285e3c50d87ec596e37c206c0cee96cf 42257324ccd1f15bb68dbbf9a6e9f909a3081e2bdd8fee8e1acd0a5702217c35 6981d74a781f41417ed0a8630b43acf2add3a8b6bca902d94e4d00def93794a5 cb39ccf6abe2fbced449c5caa8a54e445d0c082d964bd45635e6a390e80b2c5f 3d86a9e3306c9d706915810cc79341920ff88b9aeadfb6e2fcfd9266102eba9c 45140cef1be97db6ce88e44c6c6b12724338e77a3c96a1fcca335c6f43f81782 fa34e39a78d2c4638264567734341115e51a4fe2f1957c10d5a267d207e7f1f6 9a83fca60cebe9cb9f07e7dbbfe2c1d5e3ca5b2d0ce1dd6a5d5cb2c0291b5855 c6089a6d4d86904392cbc3e89ba8a153ae2f978cde6920c5f0a2b968b6d957e6 9c37fa18c5ebb19936b2298ec7b0a357e93a3eca37c2049f1cbd539faa82a50af87fefcac55cbc53c7ad8a460454150202588c7f14caef17b32f8ff983d8ce0e94c6f718dd2195e26f9f8f9f87465c5cf4b04a19f607790079fbf4494ad0de0b254395e932a74b603c7ce8617009314ed4c5ef420dc0a4a128bd48e7ebb32a05659c8769ee1d81358fae3ea21cacc261dd59cd07b33dd56482fbfc7f929e7a0606bbcea3b3d07f21d9cfabf6670a24d5a6fa6ea107459bf0d2633fae623dfc0895096b5e1b3b18938db7cbf9a75c4bd7b8310793341530dbb8584bb196a0080882371be6ad151868c265fc2156b231330ee525104abe66318ad153852eb5910519c327870798531b442463355d9993e14fcb308dc3b88e41f3520a69f90330030468c4d5ef52edd41baeb947b06ac4e54fa1ee5543c122cba512ee865b9adf0f6787690d0c107d607ba4ee5a4d2da8c384c17c829f4d12e419b4a2d63cef9802060892d2031dead9486cb8dda6ec4653747772e3e246a5fa8eec03e68826b0018eb0f55cb5743723c0177e586c9cb1e4af66ec6402d94a7d3cee5277dfca510e2ad3ba1c3fcf287e95a6bf76d8a3c96e27f56235334cfca02641309e2d4a690bcef5ff84afdefe5c5db80aab9b56a5b442c6221b316ec5b93702e7d28e62a90f820d4cb734711a6b413c5a0b7aaeedc9a79b82481267dc75bb49b01b04dbb7019b15dba85681e1f8d33c998cc057ade704a0f85d31bddaec3522f16062f7250c3c21a5f63f5c952d31c2addaaeb64921ca230543373fb19203432c215c726e08f8c14c09fba27f1b2ed7b25466a77a50b3f4d77d895dcdfcbce5d3f3d83f420539ed1e2962524d0603684980a2fe77e6d92d1881252b1da932f9ca208499d7045aecdac386b719687a669390701dcc495ec1f2fb3dd84a20898aa35c2b25e301c4cdb50213930bb1b3ebc2131e40cab3252ebfa5b851ad6abc6a539902b091007b572fa87f59eb207a29639dc854c50b2581773d4785f2b48928d6b1bf31b402b6cfe5aa84027e8cf79f925e87e2edb77be4456a568ab6996c8e22f3a7480702b53b4cd5be4dcea0e6013f7626b66838abfbf41415e19a2cbd98bf8887f4220b0e17ae105718e84d2b53725a1291250684087d54e8acff7b0717422079064c063adcd4bb0d9d2c921a15425b657f41572f3fc40b5a3e04d54a162ea41c435206fb00563a070ec5956260e666d682eaaa51bc249785c5140d58d2a0bab9deea0c814bde4f516687561cb3121edd438763245af35dba25651f6a860bef12a68305189defd2cc8946a2ba1329bc2b1f75239f0fbef7137d04fe2b1e9bd72d265b05ea84764533316b64dee6e1c6818c1d6b180937415fb9d047944df67002b8080dd68b0aa6b0b7539c593b12eaee5206a8e48c073faf8014fd0e7373663373a3054f1ac92355d3d8dfe070cfc8a1e4bfd11e0268b8cb9ec3e431341f4cdbe811026407653f851205e13c6e5192506007d29d941c86d400970797ef26fb411f140a0c9604ea07c3a5c3c8ec00f9dedf9ca75c5f360f3122e300b075490ff863140e2fe5b1ce051278b4c81ea57836fb13952d276d935c154c18d09913af89552d0c810a5d5f5d7df5c499b19f2d194177a2e97982c0d9353432215482aa16c60e0b323af8bd36301bf3beac59ba4611aa3346c65d870d0c48cceaf409a79e92b908376c4d089a887e70605ded6e342845b48ba36ddd7e4190a6abd7af0e0d5afb00a946d78f2822e1c248fd626bc2b1b1863af863c6ea6385e140556ee2f5dfb80e18b0c3eedac519c309bcfebcc1a795e97cfda41a075cca3f034199c96e20c009cb792712a78507554ce0aa3d05485cc8de30797c5bba29efc99270351db260074d8130b97e55933da6ead645e600e8547664723b356b65ce369fe29134813906195b32b6dc9ca8800a9a1b9b305c94360f4ce356d0bf43c7b2d3afa3d828560af4445159a2f9ea0a8e7d2cfd5492e34b59a12a8feb28fa42780005d547fa0e070c06cefaf26738df91f53931bec4944eb4c08fbefed9b044cb0f8d8c59f36003bd7f445b58da3cfa6bef0ed1c850aa3177b0926f24a5c448fc3d52aa64775c00ff9c47c5a10492363be0c1254f38e2c7353f6f9cac5373091a3d97af6c83fa08867400d7976edecc5899f1548fb34cd15b2a6c845c44a5e016667773f2fded011874d10c9175a0e72136e11ec8c3e7a118fabcfa3af7728af21c9c9fbb332d048487a082faa19e761b4c5641a1330b30e62a3fbf81b3dd4ec819940f6809340823b8a1679a71de0b6db920a2e8ba3c9f139857befccd1e71b3ac1ca48420c8083561b9d69356a43e413f047954da2d8647619a47d9cebf04af499001db9fc50d5087f1b37b508fdff6487c1aed96760d08537c46cf3ac2480159b766c902c00f7b5ed0e2617da49ab5b0ec3cd7a3443d883ac20776b7515929096c0ad500840b551c29b5190e3387dbbec71c3bb9bd2c50b7894118dcccb43e3b2cfe9d5f320e111d3c9074dc6f4d9afd579fbd3b9c182fe1b51a8d1c7f811ee988ebc1372600b6d72ddde0dd747774971bbce90e1a50d83669fb728451db9fde911c9deb8a0c4067198becf7ec1532e5b88eea58bbad789f2fa3457b06a3c415ba6f3c3a67091395c9215cb3bdd28ce4be1b2be5e3112f78161762ae954a47026cd6fd86c20add7d6e2c5a35ea43f914c7e9772d87dc3e3fae50fbcb77fb0ce0ab55280aa603eec04884583bcc7e3572e0e51422374013214cf0755a6a29a924bdd8f7fd630d7bed8c9e106717116c6bb483ce195ae30162c64871ec4b675d257c99fcbb6c07f8cc7bd5f7a2ec547f778865f17cae10d3527ae6c7000d09e11eee294fe05a05c170d83783efbca79777f88bfb03d3be62bf5c7f6424353de4c727b85d72c3040bcfde99fbf2c24bd25b99eebcbcd1d3d6743e2fd9d827d80a8d07c60a40e8019e3d5bd9e7c203fb52b82180ad302c6097b7242939be7b51421854f6beed390474addfcb25e8fb5309a7fa34a81251d585c2249466255a60d9dac1e767d98600d0819327d0a8628099a1432fc0511369fd131568cff42292dbf6e8f9822ffe08becdb14b4b5051a9d2f55da4569cd32051462cbbdc1e93db81ce92fd1690bd04de368c37984b25edcc0ac4abb3913d25fb0ee39f02006b8834f0f2ca5aebf605dd061bce11a498f87ce754b85c5709cef6a314a214eab5da28881da2e1104f0aa8783dc5a080679d176d24816f77531468cf7a39a344533ec88e0f152fc5930b84b7339048a0002d15c9f9fb924f54ce7560ee016ada92869c5907903da27a0a5c1fff251e1728b0d785c3a116a6b99b489768ab73d22f92c5718e8886c2d501d89e37692ecc4a77b05f4ee4a475d66399e6c77eee912542621fb69d1e4e4d01da7b091aa72908777ad0136a5cafe45477241ea7a7300f00ec9af61f17090603d6ae7c824cb7e207893c73e1bb7df48b9e0b4cb77934deeb8c02cd1308288009ddaf92950b7c69e2692b2df034311668dbbbcf5e2632ad61e021ddc70245720d5b53afd65d532c61e82f3b264a484a8bc21d2f20c3c50ba181bd3fc16166dd08565dfd768a617ab660c21c5dae157bffc38627a37df236a98d0b4fd1fefd0a04213c1608a99c950aa15c7a189d8a416a56fa50c68dcb58b89b4b7f15255496003a2ccf4f85cabb7fdc98fd65724f86b6c476f448aa75db6e6e2cfbb7b33a5c04473de15896c08d98a9f197ae7ea2ed5c3ace61cc8317406c400be69647ab2b05261a21afeecaba6c5eacdae5c702bc4c8d446a754a39c71e35524dc0ee38cc0319dc27f2a1b931d4f034537d62351bbfd04950c555092dfeed93b08c5754680caa5ac87417505728e4a1a71c302d59baf7bc7b1bba2ffaed2061c97131c5f60dfe5ee7efa349e93576ef5d23f99efa806288e3f56e4228eed44cccb995812c059db1957657ac28029996e09cb850d8c290978aa961e9ae414b11c48651660800f93c02e539bf844a423084b3c4885aaedd36092be870b204593165a05a1bd401da1711622637b3fece7dc09eb2587ec93508c573ddd9af5156f214042aa6920f3e39c1b7831cc54236e5bbd0204eaff50de31b67ab89532bb82b1387b9e46703 true -check_ring_signature 737c93fbf9ea800a95a12d65fbfe12c662876d1a4e9965e73ff5bb54ad565c56 be02985bf02867d7a07d1b300c71f93977dadd52d6b3eae567178a4fac67ab77 15 f85603f140ed01280ff1a92e92b989e264753b4d63aab0d3af4915a051767e9a 9451913c7b47b04680f0e9f706b09b5a4d66cd0aea1f90f6c8cda0b8485f1f7a a56a3678a5e312187b0275028af890daa556b9bf8b133a86c93da80b1e529488 d8f88ad259e11de3309649b6d2ee229a927484c971b6a41469318d9b27167e69 835cb00982c0939d13e1748007b5b87a7caa6498c9a1f2b3d13d5349bdd2b85d c50e3a78babb2e143338efa35571773fb5afd345f2c061622fa4bb6974a7a0b9 186d449279d7028e453d311e53f71fa35016d5e86cdcabf83e839453e138bf8a 0b1588b2a62a8e825ff8ea82948ecb999cb8e7743a6bbb4347e80df8cce3919a cdd07d6244009f9c9d404f8dfd70c339d9e6c8b89b46ae3121a7c0b0cf1d9645 6faf7071806188f5c623e330d76dbdbd6124c76f00162368665f53bbb1bd33e2 d71b9a8830ea0b2c892dab340e86c1722492b61cbbb8d118a4a90ecc6632eaf4 dceeefee4bef18b18c416b1a75750223a6f0218bd9ec7bb15ecceeba1fbe767a 098629b5ec476ef56930548b6b4737d3c2d77940743c5b881527456ac733d00d 8678e5cc9a3567e5fa202c4240a2a2e65c13aa0f5579b178af1eb940134c1b10 895fd94303051899b3994e4075e8c9f3d674bec07c2c231115085e5f203c2452 160f4a996c145f6a4a5883ea3af5ed75870cbf350fa96a0794909d47c3d7ac0c394611f4f726dcf761392d52e635437d6bd682b1b945e78a34b41a0ba15b3301e35523c36396b6450674134f65a8d3f7555c2c73a4fca4fe4afe75face185d0b8fe5eef6ea9ddd65ed30ecca403eee1d1df0b525c338f92fe4bd62ba531b130d937c771b35bb0aeaf30ada495540b9bdd938faf0452289ff9b392e2a700785059e31bee601e0ed5f4beb928f0873780cfde5f45c03be5f0628695df2459bc206567403b439efe3abe4f9e2f2518f20c025413faa443c625c21ec2f7eace4790d573f2074eeb42a99885f89ab8b22eb1d7257b9fedb15a43a2be82ea48a8b5f099111d88b60ac45c308dae3caa9921f3ddd887524a6a2fdc10ca3e26791bd050bdd5e52a4f1f30c4de1668d73a267dbe51020e370447f55401111b64c7f2e7602b2bb50383b16b06f45f9497c7cfe5b13af05eaaf5a9fd24e136427f988b5f3060aeb1425d2e1187f47970e65cf35fa4c7dd092650e58a71c9228fb1093441f091b6679e13f180e78260fe135bb1c26c57f8e0647bf1848940f727607ef8fa9076f70ed88440872d57efe342aeb52186120d8fe70ef031bb6c1a168a2fbc43c0b18ebf28107da3659b0cc4d8550016e766496333312118cc9fbae9828a551100bbbc689ac226c8eacab3fbbf88751dea8c29cde3e4e67f4c291381e8e8f73720ad7c594fdb0acbbc6230bc5c6736a29c41a0414f4858d5608b7b7e80adf6cf806af9bb27b2607a0b372e7e304dfdd39101d24a4d3c922e329b9b37ed9dc722e0e4d0115769c24dab9a3fc312005fec8b9c08b628611e84dd46ea717d47d484406ee209ef2325eeaa9339ddf1e0d98bc7574124fd26ead7be62a4933d26c3805022d3a68dfcb4e2757e3b4ea938abad5df2a9a880cbbe1e75ead1b3848e415380eb03196e061ba5da68d8e3bb25b143f6b9b5f5d98045a550f9a592e5dd042de04accd60574a62033c716be937b63b54b00bffbec508cc49a605612f9dcdec59098cf6a35fd8c2813b77cdff4ba7fdd1617116fe7c8fc789ddcd8525b28c6da90244724ec97299d158d2f3908a5afb7eec23c1c4c7b750af250ede2e2cfdcebd0c8e0be7508b6f41659b5e389bcb7352e100172e1626609ead550f47b4a686c40c0cf6960d40837d37ae0b8ffee692f9fff11cf20a5a85f3024fbcee13153ff40602def67a6c65c86d1e0ff2fdf2bd3d0790b3ceaf24a1843ade2ac2555ca8800dddd16c7a957aab6abe3954a9cc36bc7539091092c5eb9fc8b16138b207e3ad0380092bbf5462462a8f9d7f52fe31376044c354182aa2ec4e84d2753a320df800 false -check_ring_signature f942fd9e3c7f09dfc1ab78e0c18926d8ca9073295fe4ac9f2a73319e2184ab92 65ddebd5a0b39abd52a46a596d45eba3c323d4c3ca4e102349cb7dcb71c9ca92 224 081599ca5f1f030a88c07a439e2be17702fe56110d4bcd12e9892558ecf6b17c 64285b36e219317d271160a9696fb1b0dda96315bdd1e18b6972f064d29ddb2d 97ecece68043d0f6c18fb56cba9850f46652a73759d9f9931de36a576733d77a 85e4a86d0e1a43b6a168228218815f7ce6fc7fcf046163704376418f57d9ce58 2ad0a327a2eb3d012f17de7568f8f2aef8537636d33e870c75866f3fe70424df adb7b3a3a1d3ad84e5c56387b98c8973c00b9ca25fe65f891d397c8d5b0d70ef 24515020529e7a2d7c0fb40c196291425c36c68a69ee16da1d19a381d19cadbc c51165b2d1f2b25f5637ba6a7b90cce3e0abdd3c3533e7e7ad1091b8adf78218 71c4cbc11d042850567e0fad2197cbcc57d8f68a942b2b008c3c5c2556de83d5 dcdfe54daea685966a0ff6bd0e4d9190253f447777614f88a1bd64811e3a6672 14d0b2cbe53d816f330aab3263ab2ac173d519851f93025f171a4bfd4abecc49 1c4d6cd0374411c1cec659acc3ac12ddbcca76278706b008a0b5276805b9fc97 fba1cefb7c30a8f502434359be5779ee85bfc4571a254fdfaa9b4c8ffdb7357b eaa17a168e85b8c7f4f8ab7938a7494e8aaec61295bbeef586948e7f8eea5cfe a29512dad02fa408b07c33617ae7f44770ea8bd633ddb442a6eabba3cc2e64c8 3e7f594ac85f56b6ca7aaffdf2c95348c95f3f1fda8d24fd1087d63cf00db7e3 95442f15f87b4c67e73fcc4d45b72e2267aadbaf6b2a0c6a8ec0a5b6938e748c 6294faa4e381d21f71efffeed29bf256158a2d52732aa4499562bae7aca8f42f e6a01935f1d21304d4029b4b80ecbdd7aca063bc8b3bb62b0202f8b2cbdd3111 5434adb1cb5ca490ccfb10f8af664fd07877cd05f81c50c2b108362617434fae 39fa884da10dcbb8c107d7e04824af86a9db1bef0a73bc3e29976021d586eaa2 d1fca96727336d7f78434cc36f67daeef4566a27204599dd08b25387afb62444 ddeb205697c3278b958c4364eeb241f4b91469a887bc4dbdf462659010b70089 7a8ddf0803990e744acde0968dd17f91047b96d0618be43ecfbe88f21b21c848 826218cc6f5bdde5611df7aeff2571c84b6b5827df5739f7a8ad3dc56e160ac5 b8734c6eedaac63f5a80e7f53b9e77fe90228fa656dfae9f442f8bcc42a6356e 6c3eb1cd41a40f58b78a3a068bae4bb148aff4bfc792d00c2b56b89b4a811fdf 17038a08d380af3c7a112519f83b2896b63dac686bbc98099f978a4dace9869e 5a49012f65463580c6a25b9c8349cb2fa0c9eba4ae8f4cabdb46b4fa23d70b8b 9b5bc48b6696ddb8e9efa6e0595b93670dcff0631b5b1ccf1fb17fe00fe444c8 5014e8d398d3ee3235580dd9e6bf376dbdf5db10fa7027406e63b5fbd17c5376 b5aa14a42097f37f5eb225fc8766ada700a922887cd77ccb3264960b27ffa7c3 691156e9fbc2fd4163bda5782da584134c225ca9558a4108af172e3253ce109c 4fb9df911cc0c53954b82d76cf3c734a83a8c7064a8cb707e2316688a0c3a74c 6a8861382fcd89b7eca2a1c625d8eaea5502f3a019c171b8a6b6d242411f6e8b b858913d40e6f0bc3465085ffaaa2d773fd80f386bdecc0a1d95f518c99d1b01 e84b78a6add6172062bea901b1ef3081d87453d51a55d2213bd6f7c488e1ad97 5a989a9a9d32e9b64a55c8fd86f40ecf738e9cb3b0b3a348331f4c113e0ac44c 06934618f17791cf76de672e4a68920b77986cbeaf1af74fc8bff9e808f2840a 9903e88ae26dd8eb768b517c1336cdbfe4b6641b0b2b1c6dd65d86ab4c6b3cec 477f599a50f3bf5808fc4e7d25fccd8b44b0636e5f2db298459c345fa7154d5b c8d2deb05f7f98e083d9cb0b0f9082053c001062fde1b7d13bd759d8343d2ff7 e4388fe0c7d33864321efea049ee5da0be820d83fc5e6d43070e9c7595ab6278 0943c0842e338924bf60332f56839291863117a21c9393cc978c89b28d709d7e 6490472aa5157703efce1861eb70b91c85ed1f87ce7b28c6a36f823308f5513d 2564a32e6c7d4238796338683db1a1d92609421b3b4286842daec15c8322399b 0b83ed9b865157ca3b81773260e91976c8ccf1c1a61befdaca6a8d78965fe90e aca21846bff5861344561caf5dc67fa8e48abac0f501862eabc91b83ddd5ef5f e3b06ae255b6af003099d11e85a4eab13039006906af4d1d0210ca76e73a2b5d 8de0a2425a653ae64ba46414aa221b089b35cfad89381a18c0d926fa27f60fed 7a4ce9480982240486dfa565df1d6caacafacc4a55d7fe7173003c58cb43515f dbd0e681f739c82b8a338ea1dc6db89c1989aed69c28c94e7a364646fb977e3d 9702f14ede2d091e3f8c77f09f293058dfdefc8e42b3f322afc3540959843885 73866b144498d3ea1a0487d93215c0d4638fe332ae1083f82f502ea2c65ccc95 61c73c8824c5ac655b73d82f04dba2a2358153fecb6aa33b90a5a4b6df2d2072 84501f067254b1eee96d0fff1660d23f710d9210e90996d53b268dd2debb2981 40c85664124f0651d11134f7c8b8442529007233989fff918b5ed73d807c7438 87ab6a8819b4e946fc8fca0996968025f1315f68e667bde2610643803aae22f7 8713e3d9209c79e6a36ec2c07567ed41ce23c86efd66b54d18a154e6d65f3e91 6874bbf3d72c8245f8ab6546cfbadd736778813c899e3a409146383684fbcf96 eae3d24daeb5d60afbb732a2ffe0e7cc287f2f0a96f838c906c2652cc27ab514 dc37830c30501296cf4addb2ae7b237c44a9f9d2c66575a4ac1d3f30f2ce8e77 bf3e2522306e192f163e81dcc9dcbbd93d19096b3678f7132ff44b9f03859bc6 f0d4d1db1d196637b9c615640fdc08bff2b23681d6805a555a01f453cf8a9343 fc1d63fc23ae092cdad0e93a02beaecade321eba7f3484a44c8e047d48529812 25bfa01e64f4444a1d1057b307ec9047a2c69e097a9318f2fa0425d86f8f009d 30f72f50954b496fb1582706fb2173de773b84ae8bc9f924346b299568345fd4 0885a581065dc2f7e2c42c43fbc6bfb4dbeec50cd67548b666cff2bf6d1ae227 3205539f73f342f7a4b1b59640c32df027a8f82990c6c52774d9d36c94a0506c 6cfe8497320204bdde8865c86852671c56ee37b40333cb23f811cbe5307d4912 99dd147399ddd42d6d17530b29baff2815dfd461b22fb5c0538005d15207418a dafae4893ba9100e322ce5d1d3a0aaccc0c59bf24ba6eff722b4da069250fac4 33893c461e682b09c0f09355b9cf899e039e999384eecc255e17d887a7090ffb b72a46a396366bcb9ba1a1b629bef2be2c918625045037158e5de6771e60ad02 1fc454dc6cf00495d312cc12fefc0b54d3d69ac52b33ecf05a6ebc70b31d1eaa a49de1bd26b6c2bc7e96233fd3dc30b3b7de7d3f0d4258bb07a41cdd0b723cb0 25620034ec9130c58e0db703c437e989397cb09dceb6fb645818bcf00cd31c29 e10887fccec63043ae88c94a5e90e3a911dd8bf432e41de183446807cb7e8cee 8476bc8b19e269bfa5f9e072f8c3ff44bce8837c9651baca235962f102416bd0 eaf9c6c9444fa6ee5452978202d36381983448f112cf1cbbf433851113bfa5a7 2dee625a6fafa9c3c586b7000b78460cdc26baee9603adce98b77295f5fd6cd0 65b83cd112ce4863fd802e2dac0c054a9e1b45c88ed7504870c1dcec1c0d7149 64c7118f5c65cb2d471e3d6c8506d9a66de910fe8769d86b1576a25c922da2db 513289a074666a5172aff29b8a940cabf350d6494a0459480a901696ab3784f2 d6a3b14117bf506ba18c6b708a2585b72fedbf2fe2c86da64dec62b50dba437a c4a3fe131fdae5d2e41c19fd5fa88c2ea8649213c96842eb11b7905cb7347722 140c3ad3fd481ad8f7f06b4c6ce3a52aff3dffc93c6f02ed646236b94d8ce0a3 dbe5848ba2ade1ec6bde2864f712a8dd038e12b82aee38ad9716dbf0b8c30144 f528d25ad0da19a09cd3c1c82539185b92296c4aea5b6b2cea8584f9a7839bd2 5ebf6a095deb902f24953a7e2d00f7ad6c0355463950282cb68929f57acd60b6 0bce1442ba6c349a5682398bc6eb38c71252ea1f93bfa1591d72ac1839d5b06c f8f70d6090f6a19c03f1dbb6e9b74aa072df654fb06d9df0030c6204b378e2af dae1820241ce74e7ac393ee7acae48c6f66e0c19ebcb2f9be9d4dbdb2a0abbb9 75e5145918b40ff3f9a70d4e350aaab67eec432ef11a4b68c639b263030e59b6 7709ac517175c8dba2bed36fd3bfa4abbad312cb427272ea5c405fcf616a59b7 df7c2ecad089ecb43ac6e7541a7aba28795e52a4b1b362c2a9120786e0493e22 cc94c1f39a29f589d870b4c6a871436920b94bd4576cb8496d63e799d7385253 8269a92973552f819d57a40783bdb8fc4621b17f332dd352fcb36924b5f07a4a 9baf9e8e7f284ba86f41c01e6f156a9fbbd0132b2dd64b69485ad52228b117bb 1bbcb63d136fc886d7272a21c8e83705dedd9c4d04face6a7bf3a1b37eb94029 5a1359d15878e71a15628c736ea9288cc243e6f402568861245a6aa58923de6e 665d3b6e13efce47c06c6c58997a9b481fbace065f1d5d14b9d1e1137571f39b 280663e811962b84b3c1c5d33417c6818f05640fcedf084544c97cdec2892948 2570f12843614248f8e1d7ede40b7a2f9120da1aeb4e2a46c6531e972b121c23 a932c28bb22fc0608ae0f70fa926feab7ff7721eeb788391724b64a60b37283a 95487b8f1bd06351020544a9d2cd934e8cd5a172b9acb4dfec5050e45e0f880c 55ee7c96c6368fb68074be1deb2cbaf9d5336749c496f7c709d4fc356e6a2c8d 8608e3557dce392b40519750395d1d2d8b57b3f74c825345dcb44ef6c2082d36 c05e14c89c180e566d6ae05d5732342a45c87d3066f2fa8d513d4dcb59d34c22 a703aa5dab96fdfeafa961fa17f68e0c6015b6ea342c145e106d41e46c4af0b3 cf7c1b3194ca2b9cffa0333d922f145f6cb3021d157159f498af8476649ecdca 21344d9f65a0bc612c27409c5a963f83c1270972aeaa3323634be483f50bac50 48f2c00228ffa430276e7bf180b330e3bb8d08f8f50f98bf5b2d4df7dee55690 063b7ddd668f308d119955fe7beb6ae8ab424572cb3220fedb35faa9db367eb5 1e288180a62159f4b3117df49ac01f34dbbb93cfed9405ee1b87215ed9ab99b7 bd25fc1e454e09fcf37313c51ba619116ed0ea5963ddbaa619b5d7db3ceb68f1 3e14713b9af49720d5d6cb038e11171e4e4660af05c11a5c19eb75d14e6c108e e11921cdec2d6dd59288fed616c81d17211ec6f1ebb6923f9da7df4f5c137dde 28d0d9bed6a1f1c41c5b6c7ef08999a830e7984ac7504d9c8d8f24465d9c7c89 26b98c0e21995894989f04a821efc6bc716ceefa8d3f90d0578b05fe0005c629 6ea85705b06b8cbc278e09ed6d7f4959ac46c436e59b1b961a59dfde52aa7ab1 92ccfb983899bf47dcc59bf855215bc2a7ec5fdaf5301f2c25994c75ff2e05b3 23266bc685410b7681c5872ba76f546aaf17867c33f8582702ad427d6db17889 ded532abc5d97c8398bcf41ac69cd24be8114463377c455dc1d21b877f1356e9 0e0f385137326a6a87a97b408a713d785e2ba616df66b03eb07318ef232eef9b 056d533ba6e7fd63505f4005b46ab5047bae3003071625cf8f422b3607f1c56a 1f3eca80eab08a0eaff87f42e70f03747c87ca176407ea7da67e970388f181d5 509e2fca2f660a7611465f040669343b6f022051c46ef81b0aef2b991c780c82 8a09cb2c7308e3f5fa25a8f01a3c0044e7867072b1a0b5dc75b3af8039b4a4fb 32e13ac891091be59d6e0b08cfd9415a845cbec1b67447b88db41d562225be1c 9b57bba919870f1c6f47be2ccc9eec840616d558548883080b24723c6bb4689d be0f43ca6e5ed1ef4b633be91f67e50150b6096c2a959c9e735552bf631d5f36 7720db0bece2923dfeb0bfe9eacbeb7eb48a2691bd78f4a7b1e6383223b066b8 8e2e16b7895f96cf00b17acfef8fc22b708ae571ab6260f6203518ba33a3bfc5 c2a86015f5b5399718ada1ba299669d7b1f3df3cf32022f6a1dcc1f01dfc571d a9bc7ee976d3111e1b2f98f412d75379754ee43d568733d0c0a590823054bf53 1d0af5467611f252a1505e82bd92eb7f7b19879472c576325202d2a213db29cf f8cc81e96827de371930d334e41032e08a1843b72d883c6415785f554c5ecc29 e3c0734a52ec6e565efdb016b6bc22332ec6169860941a5af885b325116498d2 706db57dc543609e7ce3963b4dd2c04a4587255beed15177e34ae8b97c65c670 8f2e3a0a1da376ed690c8063da48fc5a6292da0fbe4806e0b9f624d2206857e9 6f1d6f45723668239a557e6ea2993a2af8e0dd25c1ab086629b3229be394669a 08d18eee83b61ad0475b200c02e9cd876ac2bb62351aee6ac06b8d16cd3b6f99 362e2c87e65cadf0ff68b785ecb1eedd856e84383c58b9a82d81d22703aa12c8 78b8a96bbbd6ff996794d98dab11c643dfd0713e8ecfb3d3ce6166090fc9b495 d0483df1643c8f03c2629be68e337d49289a307646b46132ea286b2afdd8ebe9 c2aae167059e1575795928463d7b51dd06fd7b2bbe66f4a4364925cccd55e1f0 0667a0e56208d45ba62c471ed106096f77c496906463fa5ea1f3ee76bac5a4ab 49e4cdc6fd2404149bc99016df63aef37e89de3c308006392a8cf68c4ffe58f5 1406dd3067600a8119dc6f7ba61e07572590588170e60627af00a6114165edf3 498aec93692b272fa04437b7fe889ece57093f64efe5c3960b8db0d152c1da01 66bccb24ef7af12cb4d86c033187bf52a6e2c505d5dba5e15f8d099b04e01c80 17003ffd4ad7601c5bf24dc2d82a28f45d8336a7ef730e52e170bb57c2df4626 582d0dcd9401edfcdf95d119929859bb3eab4112130e2acf5893df666acfec09 6fbdbd44c01e9bf5ba03d2383e3698e516f91edfa27d55bc3219b86c1a34d50a 78a0f400465167902698b53403d128590a7bed575e3a8f1d8afaa74f2b30b3d9 cff14214577586eae5bc9850ab3d1f98c9da02b32181cc4c19f2a17b864e9c84 0ee8f47c89cf54bb65119154c8cca2f4a1059d47050b63cd0ba77f509a664911 d09922af6095d392bab779793ae4b5c5bb19b71e02b3bbe3c5b4238d1c7a8dd1 dc567422d4817a27c904d083541da2382b4f894092a676928dbac978c6622903 8afdbd78c86854a7db86ad7b5aebf47fbca686e7622a450f7e69bc8294789ebd fb971cb6e1f85cc85f53b14c300df4d78bf8c8533731d7302bec8e42e43115b2 9f5658676c02c7ea6ef06e2121b6ec07a709fbbd3aef4019434aed171d4aa2fa fa5785423f86600239f9ddcf3632d2288c44604221c94bc39e4799568ac1c3d5 ef3a9dbee19eadf0e6d7aca3f81595f58ab70c8b54f8b60536e91049539c5c47 cb1448194c6c466d31fa885de96bb77af735f06e034c240854a33d2f29e8edf7 69c0d73f656c611516d4301a2dceabaa49de9ca62fc9383362a3bd3c83b1527c f51905f617015c3e97be3021dd07b1174b43fb8ad475a42030a77ea2fabfa797 e619e0d4219cc7f3d7d57dc32b121731f4efc66b72637c4bfe85ccfcd72418c9 407739e939d18f2d5eff8aa87e122d54c4eb78f53050c039b6ab75e54f6d898e 8fbd61a96e0565a61ba13b14ecc5c1646d9ef012e4fb3b3684c55c37edebba65 f9b0f5678a91c08aafb656e2df15ac47b5ac58874ab8ed1802ffdbb4eab4991e 407f5760dbd6f0594ca28382c68efaf1acace6947f02298190a76bb47e97d692 a6f6d830fe3458b886bcc8743c3c321eb41cfc80dd90401af73be6acf4272fa7 d0d0f6b65c60eb02a94dd7c43c8a55fa2b001666f3ec046cbb0c8d39b89205ec 40d5e28f0092b929f1bf1b808cc22ee7819a1140ec20322994be809663d4181e 395ac11fedad8d79d13ed0bd8c8d806bc42afe6f81cc2bf6bb15b285c2a84cfe 5c34a63ee9f67d4f7b47a78b88c1feb74a02117c3cfe34e7b786417bae045112 71a95d3fb166fa39a3397cf48a0c44ad023549fefc774893640c925fb9bc9374 4a6d7e5206286964f521f8b1ee6da187dfe5b7d54e944a7a9d3ff46b100599d2 ddb6d16c8b7dc00333158505549bd75f1054dc8338683ac9ff49117de2a5298b 7e78547476c22eecd979b02c903a0d2194aa3a1fe3ee5f3fff7187efc08cb282 f43e84ca4579a76ccb412c7f50449c541e3379f27f53ada2275b4109dce50b61 87eab533080f558ee908413be4be6db21ebb9059cf2cb13cfad8e550be95ac28 920cad260c58216724dba701b470e8b66bc55a2c255bd85d1793fdeba17cd707 30dbd6d88c5a9d468f13dd80907a93ebbc1a149e2022ca6393ed64e561d65753 82ab3ec1a2be5102bf6e7db63e4177aec60283cdb36584bc6f09e94942ad5268 819516c0880a31c4e7431701c9a2447907c84803ba7822598f0a716fd9a65b06 37f938e5c90ef0f2099d9305ad525cd67b704636d74fca19aeabd8bed9e84d48 1e69db24f4ec6f224ead7271f895837a26c787ef7a04830d8cbdda854c126bd5 47246c64559ca286893789e24e4447be4bb41a46a810791625c478c001426450 6d10d1ce8c281d94ba2eba4eb2c736d4365ac3161df9bd861ebd6013582f40f4 bdd123690f2fbe90a771d6b3112cc7bed97ea153c46c5ca7a407bc058490ec95 abc934ac7a74246695d84964c83675c65bfbee72cabb3b317d8aaaddf19b6cde 862ebfc59de1a5c7aefe529c183f02a9a7b519c4f995337f43927872a002edee 3ae22f6b80114fbcc1eeaca48e441307b7995f9c1d986fd2fc003f329d64912a 96bd9771fa72c9172feb25031556a013a127c6f0f23aeb1c0d52f53bafd72098 9948d740dca9a1714a3825972e43cfbf307ec7e61729eeff8bbf6392ffc866a5 b2d9a2ecdc8eba22b9d5d649a9aaa60d8a964cd2f6d8918483ddb6d791852099 b008407eebff36d4e47ec9f3084cb228b4bbfb209927d37e159656bd58e68130 57767a5bedce8d5b025dc8b174b7f5d7303966b372eb5bd9c8f594bf82bdb62e 2556d6a675c70a8f5e3cce63e68eda0db7d00001b182abb0748029ce4e43f511 e27cc2150d130d80993c7fcf39274f794bfc4a0727b76897dad4f4776ff20837 7256b1957caa36f450d4dcbc31d8e63305477a60e8bb270fba670fefb748e58f eb76f2653f720b081218a52cb68d9129519a7c3c888b2f84d11ca1a436ca8ce2 ad8d5405b9bd82b9ee37487c561b47ac1e4fe52debb24f9c1a1276afed596c92 7bbea5c45136612e44b5b93b53e8a643fdc905a92ae1e8acdb5d7f66b098e235 e0bf461b82fd26e3a54fbebab652fb3ea80950d14b8687b574bf31627d81c494 0b2337263f988a1a3f5c18782762313560ecd03e19ddb7a6b8014fc4659914eb ecd04e6f361f40f9434b6015a9b8950f2929cd82130accce501a031a1bcddad2 77f36e1bff7fd550bb50de05222e7c5889c88a1c07bfd0716f087773cbe216ed 82499961c4594e3f584fac5c499853be566b3666d34e5b68e4cd092228465c95 b356f99a0d0e9b280f7efedb5373e72956296cafde56fa7821af1bb904fc258e 653956533771863db4590c7c96863cded27aaf8b73fe56dae7cc28aec30318d4 193bdee418a24bd4e2895ecb04a70bdadbd0eb05f4ecf777c04cccb245ef51d5 9c848b9726add2ad7c9be8ad884617d02c17e69a8a9123165bd039048f263bd9 78fff76b9e3c69ecbd7df5b10b38cd87630f74cd66ec46a4edfbaad5c405e617 4b1c36b47b22ccab43dfe2294841cd6ec0243e27b383c6bd0e4ddcaedb417ede d41af1b94d871d716d25d1c2cc05770bdbf4f61948bbf6cf463f582482451509 bf792f3a21df2b41d485df5403d2b918da34749e6464f9a759aa2fe4802f6c32 0ba08d46532ff9858c773a0b7cf5fdf56038eb6e34c44e745eb4e00e3c90c6e1 79a2b3e079f8867eb5345f7c90ec5f9550adb8c1e2e1aade6fce9b717937ea6f a748c7c52ecf2f3da0fec2a26986f669264d85d59015885f634473a19674f5d8 a7b44839417f481155bd248d52942b7bb55efd17e003feccba48fdb21a7abcd8 3d767bd9c22e1f18ea26a3565e74960691ee3bbcf7bb569c4cda9a888cf1a60f6528465bf693de347dbd4496d93d78e1c6d4ce763e9929cd10670fdfe5926302cfba7a291a02c2a759e6cc2e12e31bb971ff334e0d0942da54777e471b13c608049b7f271c6088bba59a075d0cde8aa05b457ee64017b7702de9c051c1b37e064677da3dbe87e72a5516e2cf1a1199bb699e257c728f0a56cbff71d0f604c90215bb7f7e5ca0ffde262d1a9d95c76593dfd478015057f413ddf5e2b17352ac0ba4bc0278f294b6ac62ffcd0a8ced91e48ad2ac46bff3697fcce1eb4d4666a40e979a740897fdb2342e26ae95e0e1440af39e854895c2f1a7702511bf2a4040032c34a95e095cab4dbac76a33867cf532fd4c1f0c3666ee717e37497c184a38017211a2f7936ac2b4b1fff30f2893e8b2d8f87f4b59503ea442cadc7628af5e0c64764287d639a816186c82a177118d0cf69309333e680b14c08db7001cd4a20668a7958777bb22a4053eb7dabba2a33df292d9e5d5f977b5a400887175f7d40157856a666187b1b4ec9868e3da9adeac09676f5b544f6901ee6e85f921ece00bed2259637cd9ac501319a6a37d13a87b13d55f0e101fd931fd71e5bd57ddb8032ab1b364f6dfac4edbe2eb64c782457338a175fba7f3234c2b71c5dfa0fa730066cfe005857ae182a0bd9d666e33a24439f5ebe58f6005b4d3678536f819ac067f320c6478a7d310495f89d6bd71b667f1c1ee5c6b2eda7bebf174408f371605a58dc40c980657e20cc0b9526a24ba49b55a472084651c76869c7f5fa5de96080dc9379681354b4764503f77325c6adac6209789e244cdd23ac907a0cb99f30be49ba2076ca88cd4ac7a56d0954a3688378ef5f62dd5502a5a666583606c18037a25b69fe4954e1f16924fe4a3fb30201482b74bb75ef6137dc62b1eef2aa507b8db8014c39ff1e0ac49beef3ca049ca4df8d766ba9bc469af8ed2387b0c170a505bbf2cb0df6538da5ca44d682a386147fe4a9326261e657af5db17f99b6d02cb652fe284ae14e23bebce6fea31258700d562d261f6201fbd40a505dd442b08054136032ce8827dd632831dc3135f0d1e91f3f1017b22b8a02a4c7b08ccf900dc2e9366736c48d9b146b152144b999298158d09654969b9393883c58796730e79b6fb707309340908f5993f7f81efb5569555a9fbd00faa26b01e930b02ee0bc0e1a5262e7053138363c48ebd1bd5872ee4a8717cb280f5dd81cd0d79a2d20c537381d4b23627ac65e2aa72151e4cec2b7ac023aa00bee63c0e3e09329a3c02811bb5245435db77d0a9f94fe4309cfcc4cbc2b136b0697b3c5ee2cfb08529036ef9a5d381f5b343990bde6e454675d197a4241ec8aff34e14d9783d74a306065f8980c86b678306be8cac7953becfb6fa39fd2ccf5ca7ad94ab5e19180e420f738d407d040cd8f6e7abe76f804ecee5cc097607dcdd7e4295b246533052e708a52a1a038a07d71345aac5283f640d8c00e46b199e4e95a3bfa9b067f1e4310d2f74b205ed475aace0c195cbb20a0047d51c326ca46eadf7dde9c55dc9707e0021f6f24ee0b577fe12b249c509922f9157e4fa79ea1e7df4a87adad43adffb030d719fc81f87040050fea5ee739b37d377cd90ae64c901782b2f9c57e2a8bf03a4bdd95624d419815ce058036a0e9803cea94e9821731cf37e30004d3ef3f30d162ca8b5ec4aafe462989ad8b552ca31906e35f96e4d74826b9bf6e1b2dbd20b620686127fe4df4b117f2269377d8f55625e6c68725d785b63a39892848d810def50acc3f2141adb7f9872acb84de74dcd43b5dcee89867dd38b5238728b130f82c488ef676a4cfa6ff1b7a947e45d0e5384cbdb8865ee53d3ae7c4214b052094b5fda39f6b51657e798b5bd9e558688b1cfb7ca1bd25a5be0fb06a88c87d8056acb7d719745875303c22f8b3fa8ebf71ec8e6b25fc727d58bc9f01d93f31305dcde1a43531240a1e7afdb1f5d9438b205dbc4c8ec286672a18c718db7fdba049c5dc406fbc092203e0d45aa1d558b7830a7311e9c3051f8eca112dcb12adf04aef33402ae4628961665b56771877ef2a64827a467e2996cae4c4006b48feb0d5176893e2d505244f28fb9784d2c11652484c93dfca7d08685801a7a2c354d0192458ff34ca6e8a91a20a66ea0eb97ce52cf773d2d3a5444ec92a1b2712faa0e3bb4ea1b2dcfe56cb16c16b6c33961a7d98c4cf6aeb45c52f6c88348196fc602a257ccb39604db5d1ee9443b02aaddcb545e747090100ee4e6d090ad4000a80f022f1a2c3629ff28c62c7db8f7cacae29bd773c864296ec2d9365cf04a27ff0184008f490c16c4737d2cce829a49662dce1cbaead1466b1c9f91538bafc7220293fe8e1a1e5413bd5d2792c101955d91c2864d8117d4aaeba8f912bf84baba049d47d0aa57047c20039fdbc98efe82b9cf6d742b4a0a13c391e1cdafd1ec6903b91f929257e01137d4b4ec25075df4b7b80555cf85507fe3cfe8acdc8f6a79005eb780e80cf458c587d8aa5a2edf2716aa5239336c7808c595c71a784a1cf306ea2d61e08b2b5bf1608f358ee3a63bbc8db8ac455224f77d4ec12a44ef4c250994e08e1c8651da3bbf9f81fb9d5a7d8692ddf6cec487d246551f0d188aa9990dda494630889aac9823ea800174fa10affb128ffaac97e5bec90b49b42e971b0850afaf57138f693e01b77dc26d520a041a0ad7a76be755fe3b35fe16e82d6307dd2908b3b53b9fb57885a112581d5e5785d2a8ac98c842d7d7871ec89ee7140f44612a93028ce8daa91bdaa1964ef6d3b732fa38d32eb74d0a9e0596facbee06403e190ddc80873bd60966e9c54ec665789e3e1eb343c2069fbb6bb636a3c3075107cf31a3751b412fe22d727db96f33905ec6d581fe9cb2df43af05a8074a079728c3eb65a71e6aea925701d4c7955ad4e16d8173fd1b41fa037158764ae40e192bc2b4c3d548a141bd5e48b64eddd1ada8f02f234c228e9ca8b616ac24c40c0198ea2ea7ddd3d76afa2804e4a759026cf334603ddb53b1c798a7eff4104103db63bb0d60a6176e827d03b74dfba84bec106fea446bb09de74c32e780347b01e7d4d2d3ae5e2f2ebc84f4a7b09e6392ee6ac498b1bbce87651a03245a98fd09bd7e5a12da8bc91c423a9051c45d92129463aa26d779215e8cef29f2d5fcb90b251058a26ac0cf18af70181f72b1b019ddbeb465fe8c005a045879d761b9a90009e35209bd26c4f9957f0d8aa69c6b65fa53fae5cab5f0048a74f31a10a4910d32982a162dbe7c2fc3c471f8f43954e6fbb227645fb86960bdc4ca8518c2a10cfb2561026ccf63ef06498464a9e3e783bf8f96707cbd58a8e32cc1608374ce01cf4038452707e23763090e08e7ac5d8656cd035fca347daa8d284cda9632440b247f16740328c4f119d622dd87b84457c28f9fc312308ff80587aafbe236560a5d9d010f857654cb56865d78f3d79934654ed5c125fd45b6b329465b5d2fc003e07c73f332bdeb4e4113c8c1e3f088a9eea05cedab285a2943db78f4e1537905600c28c3f16b66a3d108e555b962b8e1d59c3dda97457b0985e1ef301ffd260ffe649ed8b6eeace3b9d7cd9010441ad4d7ea42a9714e8cfdf82b3c05b18a8704900979d14b0f0f0ded07297bb4578e260b806b3f89827e649a5e5a8ca9eba7074799a907012d68fc9db4c2bcb6b36273d7acb939775b7e2c4f1e8a3464bee807c297bec6df678ea8ec619991ce159ab2936e6204e4ac3f588ae4deae2d09f902a3984022b02fa36dbe711b5f5b53201fef1e1c356cc55240330e2db662a4630c15423482b9bfabb7e9192a3c702d56e044fe8943c792c05e4513c222a686a209e7830391bf273b5a105089c77a2509eff9bfafcd008e89743277281bc749d5081624898a49e76a72466e948be6e149583536110920869df83ad8a2f031d1f80c12ba8d6ce1de53b56deefce3fb01561ef9ab30d8ebfbf88025da84efc502c30f73f9d29c9bc9f658097ea5712e37a7ec037dfe77688bff05b791bcb552b42f085442f3cbe7f4a3fe4376a5f0141d360abdb215ede9170cbf84086ee218f70e0815d15fad3f737060f39fec095712a1c0befbcdad0645a7b3dff9661668427a00f85fa2a1a17fa35783f49da3e9fe78738b2430c106649abd474c23ad8e12130b328dfd81949ab010ba270a648608079b8e7dd2eeb0c356d162d275f8be52b7064767d5b5e4125f74eccac5a3741eb080cc6dff4d508a4a983a1ae7943359b50ab559fec7a09b612e4003f6ad5c2352244b1819475e224b678fee6782201f4708cae4f9a77086b06cd4f0331b4b766262c4f2ae89f77bcc32d4ae65a51c63a9099261ffef15c410517f689fa5cfb6735c92f6c057bb5599a0455860d0e0260304db2d33243f0d5086af9f1f99dafda744eac2ba8b1b6ee56c31dd87c7f7883700376997e1dc9be7863f68cb1a9ea99edfc56a081fa692e00d0e39a4a328dab30c58fbfe23e93b559ed3402f2f56f6ae3c59d7a5baca77bea1ad500801e72482039162e648af0453504f7bdf9043a14f22313ee075858c9ee3a828b82c66483a05b925b5a407d5f43ad7c57040d38011a6a34950b63abe94a2c54b2fa58bbf4e06011a405f7c50257aa9322c702724eedac1772a3b13dabf0c6434bb547f32bc041ba6a9091aa1add2b162f76461069ab2c47c562738dd751592b8a1e00075240852e5aed8f8fb5f38bc32554c564f31bc80801df712a0db4b0d16f52407dd2502bcaf2d2a283177d8cdffb1f9be4034605a815ca2e770adc80bf306eab82d21058f2ef26c36c2295c8225ad7b05ff2885d136c5f93018594ce2e00adcace05908cf82ea79231e079e55d3e9b3a6b73844690b3ef206451a832d073d834d8b540b604d5198b6ea1537c0b0bbf7ffac7593c6e1834220f69914db98d55b1498d109dd37e8f4b99ac57d3d75916009e000c3030d0106bf9b1cdd6e89087e53f956099e03a1754f5d47b18fbab82882aeb4aeadabfcbee0642cfde19d0d92fc95ac0fa548f64da7635de46db512be4011e1281fa5ed8c56516549f73d63d9e57e850d57749d5046fe9093cc9a75ba0c01d08afb9daf9e199d9bbc30164941b3db160e25b009e4e47bd3b8218376eb05b8a9f849fb9d069c27787321ddbd508c2e9e0055e0c9852dfbf262259b5dbec7ff8a71c6bf3cf3d0a5f5ba017e108359812909189fb59725cd07335cedd86fca77647be80856438c166b0f7c8421051679f20dd43f5439e2ba5ad2c2c057b209c51ad89e6852cdea8ed8affa0e827162c42c0437516f7397667cef39db2fdf1b477a8022ccd1bb16edb1adb06e4ff84a61a909c746553258e68e9e61d94207cd5ffc5c7f3c2fb6858e210dda88cb2a564a800c92adb8eef2ae43107e43cc266cb51e0e6d964a4fa13616d7b765ea49e993270551496bfdc4549f4aa64e58a323dc30dce9a3a0feb43ef9ccffcdbe6738b65b0e7aeecdc462b8cf74c77f2491c5a936c2ae851f65be97bd8c113bb68d94effc08deecdcd7ced7f1adbcc6906d962cc97b448184bab76a22be09b5fa16d80bfe0413aae3416eade3d939c32cc16a77a0d619b743fa05bb0aef805d6e1900146b0bd19954b44bfd8223e001c6ebe920a94ed4d5e034caacda81a1b78e53a42bef0e2f098b6e69be5d8a9def575dbf32fcd2fa8c8557ae7316630d1a05e8aa91eb02452ff031bc42551a1f07915f5f7e53119673c99c4615edc07cd346b156f2bd021a10c5d1ce3dd4e9c863ed1892bd2a1d0b41bc6d6fff4b77e7f9e285cef22a04920a230a422228e904dcac47a617aea19bb09ce3be704de48da46a364d93260083fa8c9806f4471cda9b158db99d5ac558adb674803fab68e83b93e6c977f70a9c68e7ce68db0408d8d89007e73e056bd3cc7d7ad1fd8cdb016c17ee8244f10e3999c7934cbdb29053ced75000bbfbb8078646bc7b93e9db045f5da58fabe000cccdef7d2c9bf440a29aa0d95790ede602877aa943c71a64075d2bf6b14c9a03092b28eb84078df8431e43d1dd16d87b1c0dc271395ca612bd7d5255b671800e45bf46fde5e57129b181ff3d4c8b7100066f40013dc34a9be9f307c5ab4e4a05b43be90be4c3b50826ab7cf739c1c4f3c1fddccca4242d55fcd0976f679bd704f329b1b3eb1793f80988d623e709e777ef3e1ef1329f154aee94c9f4a2257a0dcd7cdb75218c4b5074e4d237b196ac03d2de9475b345a961bab0d3518cdece0183ae7a9b3a5287b2d8a0e7aa89e1b4194c5a1ab1cdb16ac0ad7dfb693695c605a2602084041bead9034d530778d6314cd33cca91d4596dd160d89ef642411c025a71e773ae32a2e90b658072a54b9109d2b49c83a5dd0e8bdc46e37a27185702ddc3887c8893fb0b34f9b2de5c1d18f0a83d1dd6d60809ec6fc88ae8cfda73067bfd99d9f15184eef13f0f8d9fa45597c85ddc1133df177ebf95f4ba54f0510bff0a754c054c40dde7929a10bcde47f5050c731626eaaaeb9d5914a9e1852f02fb31605b2a3c2225c578741fa44d9fef601aee1e4da9f741f5f732efbf8a800840c96e644d8028f2bcf6114f073a3d9ae90791b102a5d4280f18318d9f184908325a17deacdb73fc896dbe599106cc7fb33b32b1600b4a76c6cd456e08d42e0115048d180922dbdedd01e1cb87bfb1ffa4220be56b927977b2752b264a2ced0f4508c29919dfecb0e29dda5a0aa49b32109840104bd706b9be258b1a5e77db0595397ec2d852f233fd9c09b98747a5a0078752b4c0dea76f16276ec11d049f068e04ca2d3d80a45080d192037574a18366fb51e6c06ecaf700ee2e8f812fad07c6f40a766c72b15c52a14a162d726c88bbc07660ff9f9f4ba31a8b736e3abe00339daa313e2b307d84e1ef7b419c3225774cd6b4b7b36d0dcb209cf35abf8509e01a194bb2b9a12ba857fd17df13b086ebea3cb5753ee822315adbeeddc2290c91830788cdf9fd25cc5f345c934ee31401de9fc1b4ade52f68ef3b909cd0a70e7ac9aec2ad7eb491f4f9f4beb0a3a565f609c74a51660729909c7951a5df6207e160b3fc6f80e2d2fbf771de742e5f1cd19610a6aaf114392cafe640fcf1e0049056c7065f6aa1587f90aa8581293dace0141cd69576a8adae1408e339832304d70ad5f76ef29c003803ed01378004f4d567b21a144244342c8bfeea5dc3b507efdc7c20d3f7163469813ea65c16bdda89a4b7b2243fb8faa084e12efa8a680cb346a9a29af2b5b74a7e96b370fca372738e29531b5ba19b8f72cb85ad36ef0421e7fd3c4e22f928b95e422d045187f0937e7b419ddcb464788f6bd021d070068646386c270c26001a17720c99f912f383da0771733f96413c64dee3fdbf8504d2bbb8b522e1af3453beb5a699627395bfdf79390406c5f48ba41c2f1076bb0c4d42b6783bbea142f45e75f66be8c069aad8b56941ceb10b50054070ec3c5508d8b0f3ed5b2dbbf565242f600398f5883fc8bef1d07e5dcb4361ef4e83e0540cbbd5447be59a0a95c0bf4be5d8fdecd4169b44109b82a175714f32a1136ab404175591b4cb93531b164fcff9540c286b747e111d54584e13a1d325f1b538b800c3e4c249b189042efa933d8742fa9e9d32bf7cdf21504b239633780a5a80820c53d66013addb0df6957db78cf5a32640d42491c0f693de25b591259dc892f20b1271040f1592266725da88f4973f0e5590c46b176f2fe627a8f614251e4bd90b09a5d6944e772bb6018b206033d156e6981705924dbeacba2f36c3ad4200ca05492db690b392d081de4a0ad332bd09ec837c1bc49ac2adb3744ff62556b6c60d2dd4a10910c2b5d34b46e372a680436fb9ba65d880cbf13dd216af92cd8ed708c63cd60565730c525fabb95f1742814fc575281d647f8c54ce4dfd03f6fe56097279e7597a5dd1ceba816536926664f63528a6273406d7511fcdc775e21e990c1dcb8ea0c007ce56de4805fbff7167b95c0bf3abca7363d123af839eccac97096b554d0e88fc1de27f9ef8ad3b172058e30f5fa9d955eaf510f19a42e0ecc1034741a826c9d719aa941fe99f494eee0f0c423b493b881f482b2168b66923480a7133a59e9175f9f9b79029654c50328f562012c7b916a7876f0de2943bb35f0070fb66d4b19d6141dbb64d1cfcf775f75d3045f078b4d2adf43f9f009fb23f0ee11e498fa40c40a6aa97dd45dda366a027545984673dac18044288107ba3400ec6acbf1caab8bce79fa88d11af3ba229c20279919e1c56c968b1e22769e0140f3ec61a32c88330bdd7e2dfcec98ac65df2135e4409ea7e9fe8f3ad8707a2b6003311bca4413314b580c576f087f3d5917b8c4a882809c67e40558d451914ad09a8f5e6fa1637e0976bc9ed86bb2107ed7dd2d478b765e2c94058b9420aa97f0d1e2c3b33190912c9bd129324239a1e325bea50f50fc7f1c7160485046767a70b231a3e7d4f0aa2cfd22af3fa5ab1058396e3e53573a22d9fbaca6549431b9b00a9b82159f79135e1d958fb7d4c550fcdb6bee6d72254ceb8e0c5a7f53af56d039908b3d7d4dcae26692a756414b3f9077c3430de477fd5a19ac65f99538eba078832c4b0ba030013b01a8a9c7e38f67c3e2a8e8a1653822e09c9e35ca2b6ba0ef4053ceb3280eb8d8ade295f83f4e9b0713b5ba627239630c13b0c65fa32b507c77d491b3bae76406cad745c714cbeeb71978193caf6e8291529aaf9766aca09afec95da20e747c9a70d315c1c0d7265a98103db1fbce97f2c73b48bed98640f367da5f15be0bc8d99cd7f6cde3fb054d0978d9729c11bd421c45581badf0b07a0124c0b6f97dab7054308ab0732d818fcd9499f9404edcd24d2cb74703ef501962dabc65d13968774bc773d3df1bc8a857d7ba1745b31cc183a7dc4383c5c0543c1003d14f75ed846492b36467e5bb99c542f28b36ff45199f5e8dfedf26e0c5e3f0eeaa5cd6e91e66fc41aa13554fdf1e2a55ebbb9d4628db1a2ccffbfee07117d5540b7616cf8db5640de0d5377d082d92afcf233a79d606e9bce1b3e7f021b52aa1e22c6c7111f2326306a4270f9af53f502142f83a086bf859857c94e0bbe02076b00a179d6ac1be8be45674919f27d77e8ef523ce2718ed716feb6120fe2c8807fda4a6d74a40ca2dbdb37e6e0a561812337b7409d9b713cafb72d1508c20573233a4b9a264afa766659ec7aafb4305e50cead9f1c22ed296b9f82e304dfd5651af449e3b28c733c296506c2231cc607bc71bb1801dd81cab605f35f0a3708fdd50fb7b504e1f107df38dc32c9b4a5d63e96cb9905b5b3cb66d4cad20cc4c99dcc82e745d2316b4316a6608df7c84bab7ab993378ee6cae487f6c2aa09fb4975f9ab686344ebdef7cb547daa8a8e1357e37ddc1ea6683f86c3b8af5c0ea8b2a64b70006197c0acf9002c67eb7cfe6aa055983aed12e82181c283d6070b5a83fcabf63d62e53183d708a367f7c85b0fe514f9e21b8ad4ca2d13eff5ed02c3a7496a28fef78edf7a9ec5f624d57452b11e2fe951fc06ccfaf0cb7d02c604c1cf0537c7b8105dfae0a200460e4db17af0f76d04ef69c3f20f29e7d39c090d0b8d0f8ab845d45b7d2ff55b4949c116b44f7fab4b7bebfa3df88f6db567ad0e60bbf54f7fd4efe5524cd7905678650131349dc3cb343ebbe4d7efa02d518c01e84a21383a76c692d00a2be63f0ed10f79c840326b94baed6e8d14989223860f70931aeaf66df503fed5b8c364537cd8997d1e11be4efb1364590a977a2d020dd6ad5468a68362b4562df1eabc31a4f0a2f043c14db729c20f5e03df34893d0c0f6e04d8196a81ef0e7a95c8939958a72377c30567bba494b2afd0222f3c9c0de3cfbaba99fe297e1a43443714695a5a890d81e07bc5fa88763b08a6f8658c021cc5deaf7e44f099caa9e5e9646025f6a6c4c664e70d7ff90f5c8733bfe04c0bbdfd7005d5a062e1b67e7f709903e8720a2209614bc3456ee481bdc2a4ba180c286708c1a4b3f8a30bec5f99f03ddf025d15486c1beffab2f32971fbc85a9603cbacd943853f37263b0f40650340bc520214e08c5a66286244e93d03e7ce02078ed29336c1d8868ae65576bc24650d715dd215e0efdb6e6941e0ddee6d0d3d0b9341fc035552fc234221ba78315a83db6c5746e36ce54b8f69b79c726c93460505e024a6dc110c88fedf2e4cc2722968dfc8f3ce4f8882a22274d8c79c5fee0bc2928e2322f624f966d5b71a76a8ededb1afc9a7c213820ca870f98382ff9c0f9f044772bbfefc5adf62e5410e7669ef08aa35f65b1120e11796652fabd7b40bd7bd27189b8a8799b0dd2298c2b91cc0a5432dea764773b7888181de1a00b3055ac0ab01be8c397001fccca199e6809d039d5942d1a8cf2c75d071fc614aa6074ff08e31775b46f414e46824fa1c37f4858a3ecccea07815dd21227ea1329e0d88c8165da656405dc23db0ed59db9d88231638d3837fc35dac065641ce43a60c8535ed03215f6c08fe4f6204e1deb828155bef0703dffe94d1afc63cedaa430045ec4aea54bd26b3ecf1fe7fc59cf2056d04394b116def93a123caa98b824a0086fb5abcb760b192f61823920bff6b0df33760157c1b30e13a14d5a7e17b670e5fdbf70e43443ad20b69b71645764497f4273d469355a42691003a80f230ce0157730f7c5f46f2c8935222f647246b003b050adf5414ee37e88eb480a13add07deb19a2bf4164a27b758b01fb9c3896a0bde95a140fd61badbcd4441304c2702d5fa7f6e090c8002d87782b58c300f8009fe931b36ecd5dcdb8f80ae77b1190125a1a3475983ab855d76883729d1de29607d9b8417d4e24cc8810086b0db270c35b81f7d9772adf155085213c5980c5e312d3a61bb2e1c700128e321fc5d6c0e66b9933950139bfd159699bbf0483bee490793fabecf13c4557ab5b60bb4360bf00018ffb609cd9657b1b18fda5e11a8b6da1b6b5899d046bf43230877b5b30a4cf13996a58652e308ebe80fd0595b67b72ba610f29c1d9bfceea43c210dd40547fb81eadd146ddbb91a28b4d1b5221a761a938584fa8de98cf07c055322e00cc31ce1259e270ba3bd05de2e593d030ee6f7e8d3bca1268eb7fdc176aff05d03684b0b773d069737aa328d8281489b2720756b7fa0aa1ea9cb9657af881ae705374b0a39f80df954e31e8450d291feb4dc9c51c0389572f8ad52b29de148320b2d4cf508c4e329d086cdaf378de5c195bb0d903bad21e66b48b2166b975330093a14a40fc2cc366cbaed8ac4d046a45dd3eb8d5a39323eb7257eae7fe20c440a6b1f579a6d7d235c2c1b49dd3d217efa7122164f13b14eaa6b5ae6fb8a7a6a04870cd8f377d50cc306692603336a6971453d7eaad92bca80c81d2c0f05713f02e02c8fcc418fc921bfe2c9d9959a4df419796f153a6514506e2d6f2b1032c20ad118c17fffbf71baa7fd68d500500915fe9c46c8d4ed122ea03f276fd7d72e0c6e78670e4fc56a15007d6b225cc96cfe5ed220533324393fb7a805b6329b6801aa614f47e9cb233c8c36bc1a0643f7cbacaee796901a0144e8af4d44e53eed0cccb4d3acd6a6d98916826260f97ef9a7acbc8f485857b8477e098ab3e993070a34092cf19f65e89894eb41bba7aa0147588a72748f670ebc27b5a61f37f39d0be3cd6046687cf70a5f0bb6780116fc96e1165b4844fe758da9de84db009a2c04684f36debe6c332fe1d1dee6b9391f29887cf06b93003b77b9c60db69c10020de7775b7576820a2383944880bf5395c16fd426c0e5cb9967ca3878f69240ae0fa03a1038a0351a29c601dcf2c40c25d3c456a5ff3673dc7b524bb3ad814b610135a8ffe3f56d532729b211af6053297d5a639bb75e5da7b9ba22736f07f5170c5a2ff3b6feec8c069068e80e3cd9d1fa60b51c22fdaac0d9c6906feb374af50da45a794496920cf72c3290709e82542d9137109600342f34b039236fe8137d07b3652690bee61c2d1f9b745e7217274c373cfaac54e930c4a4dc79f5eb959c022fd38e63836ce8bd8e5d35425db9ece0c3e35798a1a76415f50c1260b43bfa050ac20d75e010f68c637e73716f760d054068d7c3236ed0898349931ad018f008c57497752d84609de6990900123daafba7db303d06fc0a60a4ad13f44965980241f188e63756dfd74442898e2e24661cdef6ffe9980c7b9f813fd0b3fbcbb80577a471d0903d23c7ba11acc645c6147b9c4b48c5f0b1e55c5295c09b9f9c3f0c491dd93f5402772dcb1a38068a307e6e9a1af564e12b0ece59d7e3ea9f6e360ba3fb746d45e161eeb534cd2ff0f92c82d66c2ece30b198e45071ab65f6af7c0c7924bd270013d03ff7c7d4434038d2a2c82fbf20b60a64724e225a9d4b7c4b05c5093e57c04f8587734a207a15d761794f838f8c8104bd37b1e31260fba4170a94d0f02f3489b7607c4c01fd8b7810461c0e8b67ca67e2e45e31fd29957ff605b82cc5cb719a881d0a6007678c9ecea7c3d6cbf116ff74ffdcdc7d77a850e6005699c377fd3af7eee2a5a7813016571fbc69baeb416141d543de0eed41c69c0b8d69a9c21898b9ce5fef122c98118a85773eb46dd13ac019af3945d2a644280a589ec642f3585899d90d7441bfc9ebffbc4063dc48c877379b42f181f21792011ede9e6fbc3d81c8ed1649f1d20641aaa344da4ec417a44a82fccf37340bdf098b145ab86cda2cfb82fab8016eca031acacc91c869d52110635c9560b929c706cdcee79993f21d38e67842c632f28c1035391e945ab0630039a3536dd4847306dc50b3721e7a1a0231acdc6dab5c1023fd04018c892fbc114c699f85b3f69207941d70f176988fa20b1da5fc13a40a8c8e88907be6f3596599dbdaa4a71ca90d44bfcd09c272dd3b68f7478be0725c8c6de629a31f12ded6345cd7fc6b4f1d00e9be800fa222f03ad50272b34b579d137e5344aad85b3d4553da7cda0f477709549b124c88706954a927e25ff916858f3bf377d5d8e4e1ea650704e3ed615102ebcb4dbd8987334eb369285b312eba1eb5ca8f0331ec88ebc48ebabb09fc4b0cc4051e1bf2d220a75d95fff741f5c5f1edcac5d8930882b67dedd10ba171ca0ea42320a38b43b6f387dc84ab039852c10bf73255b000d11c9b6cf05bab9c21029614d86ea65c2ce730ac03f72927f32e1331be665751439829407ced31f7f00b4deeae380a6a05ae8260fe3bd00bbc0d77dfe4fd7c17bfa9112ede9c150e4103255f7e6a7258ad20b84e382f9da4cb7ca2cbc421759af4e34088a993a34acb0cdc73c71d3cb940f5262f989dd9f0900e7e10470c9cd784911b7bad4e0eef300a9145431020d57a711c5cb0a964d22a8bb32086c7eb61e5e036f6f92f5a93050a29b7a57d5f36058d123434b54cfa11103639c3e9e8521ed96303121b6d687303f856258eb3d2e396ae2e4911a13d7e31f9d1890294bd1c2c9cd6033d58206c0076e6a29914c6fad4a0b0a16053890ca76f499598ba10b1ca3da33eb78e595b09d67806f56e013d8136d55896dbaac6860baa167d0aae4f78bd831e78d597690601e76ebbc37cd32fa5eb0c2a84f9e8b01762485f92e07b1ae18c625af0a329005e0d04d6350f466f8a57bff44c8e2f6f540fad737c26b4efccd3629466befb0608c2fd590acca4fd0bedcf5670fd9c0ed7cd44155778d9a2175c8d65f2839206b350e0f224321a78019d020f44d4b38fda4893c0e5403733f578ecbf4142870e4872490dfbb4e97d5ced9855256dde88032dd4cce0abfd8c3e9b85040e4e5707fa40d8eaaca7369ff85149bb1972395cd22c1316028d2431927820ca93fa120aad883331a8420750a7b3b9892d1d17ad84323bd379a3fca273bfc6c301e7c108c326ff487d1789dbd4f9a0dad84e2b52b3583092cfb0f5d14a29e40fdd777002cbc363dc28426bdd780b6f9592e7f27be362875beb8254f6b5d90d929c783d0cbcfa156f3b53e6d35a2e6d4fb0a2b5daae1348bb288726e05c06583ed8a3d1018c104a0f2a82a3ca8c82a84d894d26d61c1ad51aceee6b9ead05768f3f679803fac79b78f62e929144c9d8038b5bed3a650e0f20745f9fb4c1183ac33341b7053c2b38d71f55f5e52f97d18436cbac56ac44becfb13bc732076c480aab6c6c05c4b44f44771a31fccbdf4fdbcabdb481390c0b7ec1d26418b466e679c255ae06c96fc827df0386c14394e4c7c384a56f14668be106ccf0f67b381ea86c955c01638c7f4cb4352e0f1aa25ea77bd719aa78595cd4a277fa947fcfaa74fd48d6074c14ce26ee3725e0f5f566bd9db2ec566b84805ec2a6a1dd226c1ef47b60d601d17b47594eb011160f234c7447854ed54f81c1532082e7b4e96268b1b5714003201c880aca46bf96dd33d696729b3a66c8376efd5ee362481638ab1cd3213207254d9be80c6afd9923e17d1220ccafa9d0250270ca2563653038627a772a5a0e45438c7a66d3308c00b842ff08881560f237af529abba21280db67111423860f8aa5e2cf4f880c968320dd636fa648fe4da232c98f8992a345fc1e4e1fe7400b507ae7ed31b419b49f0f09c21199c71b1af720204aa75810f2df1974ddeab50d027729288191036935f08f087a9bbf1a56d0f175cb0246fc16fee0cd0b869e0d5fc58cbf511aa7427eeb666e3ecd8406971576107b9d135a9a3478d99b2dcc018376293a039a305f8956fb706728504c1594a5d6d6f250e7ded2a21bdbf07a0d88f23c5b7e595960e027ef798fb38d178aba85d7a5761c6ee3b6071e5b02bd062b5bf35f3d121589bce6d1223daaf60ad6fe8eaa05abff0b671aeeeff3965401647e7247e7258c2e0002eb7b4009d0b0f6e5fd4ff258c2ee1cad2170a5fdb605600b790ff589b780d38892af92cc6d13b09b25a239a9e2557a499ad15e8fc50ff29ea27ca8292a5440cc906a17cc609a9871936bdefb232ae0400953738b0b0da804afe4a271f9ad621117846bc05f65234941d1ce44c6824a672ad884526f0d5180c2a20db2290cfce3303a7a5ca19b6459e899a08fea8a841426c66264a50c58d2f5eada0ba6a31aca96be3fdaf9b77cee1650528ed934fb285551f6d83000c5281a091918367020eb4bdbeb1e4d7cba378a0bf713138f1e1de04edfc5fb0ad08c13d26b475d2386eb137aace2f91cd0a9b4615ccdffd753cfb4561c50390451e538bd273e9e504069c7f981befa1b84f99734c34cff5541e1d16ae0bcb202484cb52ebc34c650a3b887c163a779cce414614c70ee51b6e2f1002c32bccd0fcc6399d81b90e809d37af90414fa179fd857e1c08c5759976e95d4688779820a41f70f20a455d90c38398ffe138a4790bbc877463d25e192fd76f7f17942940f142c03a0d0aab593f46308c55a03107be7a720d75b2f4b3a9e26ffad58ca0d0e356ce209d119a4baf9e63e90d5a08b692303c3bf976eb6d188ffab68f8c76d0a6fb0460a82f3de51a86d21e2d9460f0ac3a61c3eecaf421b66f222d02e38860d61dc24202f45f152d820eb94514999445b24e291ee7ba6303259aba6371bb00f6821f8ea2ade7e9f93a4859098bff2572492a12d94815549748f5ef11d515e0412d04d66e1b622befd0541276f2d83ca898a3b8ac0f12e10b298a24108661502f1ab24922d893ab3bf7632c433b53b423106d80bd17494aa0fad903c0679fe0ef039bd9b0b8a34430ef4ec4deeb2c5be5bf7eba4494969f55e922a0794955d0b25575f9e7e28f1280dc85ca6a459c96b6ba061530ca60bf1845067b3e9efbe09b2d7f59b978e741677c2358600f48e6ab96ae4126a3b40c19d78f6c1c8ea16006517819a0e582586d9e6a0f978a30737bffeedf297dea6fe17fafbcb6cbe3b097d259146287042a9519cce859810342d9cc883712a8854fd3e7bf1a21f0ca00cabfad6de34b2cb524620f9922a2782c5ea5d34fbbac85f4d30ebb2749d77f205529ba588a85d7763a33a0f49270e78f4c03e65cfa4e8d36be9dbb5539d92000323ba91258d5803cb8e727dd430375025312006f83e4e6777bc5ac748a194eb04ffa3511126e43241961229b798f2758205413c31803f473c2b30cb75d169780502748ef1b75ff3251bcd9b1be31a74626e960caa69c31a2d68fa48abe26f9b0b637ff407647ddace263aecd6d9b5d1ad930328ce50df0562d11a2fd7b9413f05d3f666c3efdbd6eb1ee8c65c097f206af671d8ba0c226111ecd397dcc78b410a0cf5347c38779df452f54ba3d4d60612a4c7dae50887f05857090c0efddee401c67c5446b515c0d068e39db83f7bb5d372e6d3bab78716e97f3a65ca8cab9105ef843123d3375a1343eaffac43e3312e3f0fcdede6ba8bd52b842a02c38bb909ff59c417a9090bae79dc5037131025681e0d5de766da9ac2ddf1c91d84e69e00b090978301d020b5b09558cfd9652c64fc595f361fac9e86686f0444e70b3d0720be35e6e4f318d54ade43ea10ab1fd5bf3d64c8231d3453b770d3f80b7bf405d31b271a5540b7535fae5b8d79760ac34e35912625d6290ed05e2254948b840b8ac672aa6ee254e7ab17b77a91386006dd46f6dd9d031dfb34903f0aa3077e05c192439c4c4cd68477a0d97979d6f364e47588f7547b0309fc23fc1436c69c0d2e3842bcb45eab885ab30bf6314c632afc50a22d774f7b8d0c7da7438b7eb70f094f907708a5ff68fbb26b6a4bc80f5a7841539396fd5d136eb68448ca975403d059cf93c5360ab816c690990a39d50e32af4aa38b1992b731b925b46ea53b0630ca3c25998312f3f7b4853ab4e4093f8e40e03e16a6337cfff2580345183e0da3f2a013afc03f226ad6f77ac6e20e5a23f117b5de480002573478ea58860902a94af4a77d75fefdab2aedf655cb905acab2792d7272beed1538f6d364e7140700697fb7e8575f55a8b44f4f68f2aa173ddff02122046114a3e8ad950d69cc0bcc59da4afc7a6ccee6c907f871dc2f8b81a42d611e87fe0bbddcf9a429a13c0d3a39f3cef516d2416e8773ed8287e1abc6d7a2855975df1fc37ffddd737e2109c68f1e067073d4121b0f2f8f6cb2ebcdf7513ea4720fb09511b4eaaf9f59d40f36f810a0cb3dba565cb6d04c174c3d3b1d38159bba4d28d77acbae8f7a95340f13e480762904d57943815ed303296fa53266177289a4241a84033abf6d76850391d391bbe7c57ca17def64b70962ee7993dc10fffc550604c3483a8a15a1110f9a6ba80a07dc56fd29b3953d7757e33aa2b9bddaf8cb3e57a28d99b31d2c110857c9ef40d09dab0eb8f9f7c15fb5a060a37f54263db26e3330490760632d6e0f6c6ddb462faeca4e4d7dafa4e594e964d65dd5f8990df1050b0253aeee033f01d892451873843aa81c6dc87505f3a97d89c5fd4c2de7fc960a9c87ee7736040582fd74981e697fbe8f24ff9520485ad63218f609f3e9b6414066a126125c140eeae79923a14cd03f4149f4bf80c08e87cd2f0b2b532c1d01fce49d86acbaaf032290cf12d242781e454e74467a2a7af4dbc8942d6079f59332bd0c433dfd670a650711b5a3fac419b8fd4fe247978462d6c556eff7de5016bc6d89ededc1930f2afb0f6d6078b20c84ffecd7fa7aa5a8e0aa3be459de91c999cda0af46bb25009804856257ee7d265ea5cd43d4ba478c3280269c13ec06f555b1bda63d041d027bb1b8edb42ab1e27615acc8f130838e74b7fe67f95627f4ce76c4159116930e2f88c22fa8ad1e2a4bec214656f1ce579d637ab7caae94ecc60ca22462c4f00b10572229b3feb1c736e75b605d58514ee9d7efa02eaca3c34776b689c70b130244e7d95f99a5a809abe8c46286db4f681e2083cc1ed0419d5df76271cd142c02524164f9186382a7d4dce8404ed7ffe14a5a7963d8963676462513e674d56907f2e93f0049bc279d86688af33d5fb486b053dcddee2690a372e61c0531ef4a0f935269157031adfe05fa81825ac13da4c90a30ba210da9270451a115c212280c965fede16f1ebefb1c092e7db2667a0bdf380bcf0c2e11cf3d7410c79bde8d031b1be4347dab8c4eef4883dce0b6253af614e790c66426f2088b333b770d0a0dc739abbcad22adfcafa2e3ca9649ed4c0e5fd2c0e8210ccce6373fbdf339dd0396a460a1ed33420213d49ed6e340b94912fccd0c08563e946bd095c5d4f6ae09f5bbda8c9896ca1489eec343eb6f95469ebb9f34dce7c5764f05670ef16d19048fdf5d67144ce65d48e1024496952fd735bd051f5be8bd13c592f5097d98940e6a051ea43d17fa214469b7436ba48edd72461bbd56c952463b039c386ca86c0607a7c2149bfd634f62f7f776ae7b8fd13e4262df4c55506dd69f92acee7fb60d93d2f686ecf6765d7edfce85bd1ca6c8b48ea2ab4334f200ea8e1fc68e288502d18df151ebdd1e9e18aeca463e9474fcb97f63125d55a7ad6d61d859ad1c1302fad32947a3022bfa0493ac783c8a67570a5d4cec5369ec4c06f363feae9b290cb3fbe91b2969ffeb1c54a8841bf06bf42b6f08baac59c1e0dd95c81bc5efaa02db431362bcb1f05f8d77df1e18512d90cd760cb8c0efadeaa7e07a2bcd32100695483cc4d740e7091d51dcf41d55fb5af35bacc8eeac4ebb86fed8a2dae6db0a731e2251f047fdef8896cd7f342dd75a8c38e69c12819f0db7204f578db7a608be63e1f2dd8430f9d94715f6369c90995737be08b70b6329ad1433118067050ef7a2f16d1c4cca986a7fedc2ee547383714550294cc82b8ba6b4b2e94af76a0e880305ba49df8b453c2388d6675857a7dce79780bf6f168d5efc8f8adae79a042453825b208b4dc7c4abc0e93dd615a237f36027159e426ea137fd8b6e178204281d06c9004d1bb12b0822210a4547ae16f5678bd0a2ef4fc0f0d55fa25f4d0b7c3b4866770bf13b36962bdb89b3c39e023310206f1c0e06aafcfe1feea0240ce95dce6ab1e931e43d1b39565ab344abf16f41ef4fbca1e14e126ece7fa41504e4551e7c1002fa029c94d114e5776c9ac0ea92350c6162fb7d817ebd5947430b8dfea3f97a9a85cca973466891041515697435f024aaa40fa003aaf654d3990af16c4375d8b4c5df21ac2d2aeedfdc1f84a7f866288b5b2ea5e9281423da0b0204730af975ae3791256c395b9abdffd748160051484b8b05d60aec0bd2d5610e579ab6772b7be598c7d6f172deb957567ed9fa1953129e0b18457d1004693009f33d6190985970343113fa60a97736c94fab0dca4928a851b29fbf3bc7ee9d0c8c7e30d3d3ff0e6ae24251a7f94ebd1851d5cb18531c5bfe1b8fc522eb757c00e5d04d63db46e23f54c09c5a6d6eac5da6252c05e0fb26094efc21af7aa840075994bde809a681f4fb14a53d0b045b4346cbaad94a86853e9215f88ec11fd305d45eba073bea69ce150117a5969f9f0c2c7b633aaceca2611d2aa89edc7cf604393617de972ef61a5f1df88b23ba88caf73ddb6f3e089bb8fe076b1ca8fb7f0e990935b9251a11d5ad70e79e879d4c56f9a799ca6deb047d927a1bbac4f333092bed2b980a3604e0b753367fb8b42983af673fe4e52472f2c3078a4c946b870465902dd0cb8b4c735ff05235bb97756da0ba7ad480d09488892d2fe90db7d407c089cdd09779b014395dfdc4e82a2310663cca2d0b6791fc8bd35218b45e9c0f9fce97d385d6e62c461d0c0e55e4b43f945cb9083f79b92f2c7bb6b041c5ff0301b0444dafb9b1a4b53ab6e3fed87d7333501de274dd2f4fca5bb602baeff80296cb29afa8ace632b78512880861b815a71061bfd22a2e333c9d2a45ca8dfa070f34d9ae49b582333c21364746a86aa1ce4138142fbdf66cf7cf36e83fcea10d55eebd92e226f067a037e5e3d87ba884d0312cc0d855d1af6861a0a750d7fe0b54694c1b27a8ef1ad54e879fa4f694faa9d1ba013a0276aa5a741abcead7dd06bcbcf7789232c38f0fd8df74c47533d881472a41d49dd0a806ea55d0303a510e8001587e5a1fc6744d16c1441fefb905ddeba70d666530b34c7b0fc3eb64d60dfccb47ea78635cc0eab1e7b1fadadfea772bcb2d8a8d665ae27b4c8dc12c4c0335443a950acb08d5a039c9bb6158c0bbf24939e0414a6235a0af002e4edf8201b7d82a894742a5f672bdffcf16f5e8613f3de5d8671eace33f4ac02cc833e10be93bed8166fd4d993ce9aea5f9cc8e35c8532392509440aae810ead99820ca07 true -check_ring_signature 2073a3a4e983774d43c90c4ce33a887b7c564c3af41340702585bb7f951f6149 9f3cc9b09b0bd47caed175b3f2b7be55b53f1f9835888e9b635e15354ccb9314 3 4970ba0ffd00d0e4218cde329bfd68755d04ba27249ea2e1d0c05f5ca6b1aa52 6a56db2f64650dbdd54ca831201157d4cd3cfba31dc222384b31582a36019742 f2d5a9376471146cd10fe46b821e6e0d5a32c4390997cb14c21514d1cd0bcc34 7120241bfcaa026cb29a31cae6eeba06f357089fba9a35b5373c2542c37f600eb81a0e8049a5fb28f074b0ee8092fc74e06b5a7c82340422a5627e4e2df35e04026070a028bd847a0f146b854601a41db5cd8586f5c9e5a51804fbbf5eaaf0063b04ce0922b98547ebb7364cc177a3491e000742529fb9e4d9da0528352a79037eb774ef5c8b6396358942b12d32893a689c4d42cdbd6780e87e5d851f8ce701d87f77a35d6e8ff287c302026654f6c24eeea2866ea02182158789a1b59edc05 false -check_ring_signature 569decc62841e7a1bdac1c683c92399947ba732102dbf0ababe69180a0b5d9f3 c21bd25cd8df8432e2b9763f49edc7c8b4c77ff242bd6a4e71d3c57f250ca6b4 5 9cc0494c241c3d74fee1478bd3ff227a65fb8d396b20bf23bcf26313451ba8f1 d3df1bb51d6786e1276eb06ec5b4b12fd5d5c7517597819657736ee5062075de 57b5c993ea9164f5d49f7becaeda6e7f4b7bf50fbebd0de7c7f09d3da1b960e9 dead93c36e265cdf573b3768ee6cbe2b6d1f89ce4ba8d06fba74eb72d5d3a0db 644cbbae35e927b1deb370e52c85285d5dc0ee39a2ccd2e23e27b2bffc3f58b2 2ffef52311c1baf1837d13f7ff38f1fdfc7333e244a42bcb5372290009595f0421e669b18fe8e7cafa1d90c41bd27ca0fcb0fce6467006c385eabc609fdd300343726046b2df6eadbf204de64a2e9fe4a9af26385807c1dd0d8191bb2a97fe0612221e1adacd2b21a2ace928fc979a0a786c74dc6c6e14ec3929f86c958a2f5768e60de3272681552445f814ef7fb0a27976c5b2d1ffef7e2a10dd3c9a88140ba1b02d6ae6646c0e3a66a91ac7e8bd43feb84b46d755750125d6383a40fb1d05e108104094f92bb5d68b6ac5c63f69f722937ff2649464736f505f64910d3d060d34a458fb38261fc831e375594e92cf436632cc50dd97096c48f235801c9d09899500ade25c5060c01f9fa533d1b4bdc302dfff5028d340cd14ca596265e4070d7a54ce1acd2826da8a8fbe0e6234ef234e79aa83ae45ee6a3ac925087b9c0f false -check_ring_signature 136d0d57ef64463c2adeb95a1b0e82e5340f681df179b5cb2c26115fcc8e9652 31c8f17b26ded4ab7573cd707f76f8d27b286e9d3c5d97e8a589cda513ec62c4 3 02a35e6f86e7036c6c0600f4a3501b1ea21357cbfe90c16a2fafab9f4f20bd34 175c2aa1e2388e5efd98320a9e213df6cda41957c1d017ca36870edab6db95bd e6664552097a271c32aef7a3c5667ac5cab05bb1800f9d0532dbdec69998e278 107a6f7722892b62442c7a25aa222f01af25762c7a0498abd1176aca9843840e63b4b6ef922f5a0e2426d8ec2eb0ce1620ffba28540b2a4171861baafbbbc1073352bc5bf6dea2cd610c2c31df7632af3844ea7b15165de2c6c6c65547b8ca0603139c213af4fd09d05f0c630f5e914dd4a892ccffb9341f3b71c06303c3f206df9b4cc715130d545d21f17d0bde2faa3fa8a9fd8abf35be219638c957f69c0a08a3c0fd034d064b4c721be04c9c0ea584b1f80fe6cc25b44f3e880fc602a60b false -check_ring_signature 2d030e50f9fe4c8b564b215247e15be9ff4ca40a3ed9a36ce3beb081f760e4f0 399ad6c08f5bcc3a02e5840c7b49d51d8b56e892be2a5c4fd21391a9e8560012 8 8a6d835bc2200798cef18c93a949e9730eaa0ce960ed9491d5b57d9bb15890a1 613655b02b5b1cbffc492ae4d68e97121ba28fb3dbce5d4ac3aa8ce64a0f38db 5d4e5d94a91f0d409c3919ca1121be1eae44d885fe185fe572c93c87e9e5c8da 55cb7ead91289b7739f328f82ab0de4f65cf47b29b6055a760d7603a39731f37 4795aff098106a1ee047ae3f1cbc5f81e8654216a330045728c73702e5e96e04 ffd7d17c62da4509b5d9befc917f1859979cdb7fd7a939d5ac83a48068ac6c87 56cca0085a16068d887bb6c941fb99e700ff1e3cd6cbd6ba3566e6a9a88c3c52 a0a4f4f689292fb74832a622226ca48b7541322e887d1d4130ccbeb2f4355125 315fb7568cc56fb7cf50b3d1d8574d67c17446c71346fd0b939d1effb1c0320c8d8dbf06a6025c68814c85dfa3d640593ea5829c8ce9380249f11524f436ad028a2da9eff383db96d6c4726c12a450b0d0170bf2858c37e26ab7ecfbe5a6120f00ca55adb924fdec01ad2cb4e39686bd61b55f6eeb8477331bc84f13a7fdcd03cdf0b9a88f11b7aaa3ce4e8f893a1dd0936a4438263d47d2e6c212f2f6b4b809610c307ea9dc77184ea151b96f4b0cd2b9be7813d0df7cb8444ff78d2bd79300f1b58034759d2d220fba2849dd4066c01634ae968b23a974555e9a0b09856a0c03da518f82bde9bef3cccf6fe035e6230ab875fb00a20ad715bc799a0d7c440913ecb12b041f09b35cb97e8d40cf30ab269ca3ad73403060e93fac1af8f98a03c51d0c5e45c09a6c5c7877dd6bb2ea7983664e5d479421a5b17c2d127690f103b657d071ca542d783a503876d701582a4500321b718d44252c2db39c1dfcff00756310d53dc5403be04bea416d566d882f5e5fc10b0b4307835d9f8f434aa90019a69e62f0cf23a243eda6d0b24332e33a24369e2e39202db883685596c6530b7453a23d0595a2d27c30c835d75b070369fc3605ced927147a3d7da58bfae40eb11ee0b77d8e503878e787398939cd66c1f8fc0d5d598a180281999e48d1310d830e5b4c12f4dc378db9fa2c13c284ffb6ddee741f9fbc3db622853845cf5c09 true -check_ring_signature cf50e2e3e2e7d09b80697ee34a204c452964d428cf210146c051e012252f2565 76a199f8eda53881d38f8af1bc002a97e71bf844a1f579be49661448ddd31221 4 f132fe6177b90e99f222d6bd6977b965e484d5749fd189232a064031794bd34a edf5e1bac2d769f5edca297ba8e5b0a6119a3afa931054db6871cb8ef34ebb7b 81d41cb371105b162660fd8e4ac6b9ab8786f1415047cd47a884dfccf494d568 f78cc0533f78f867d0b8e7d66788eb249c28de90edc8834c77a4337f940febb7 9905023718a529e4a2c624cfe1521bea058c1b444e9048d7934dc9f90c79cc0afb7b3a939b2fb5704b1e563bb11ba1403aeb88de76b3121749c21870e2dc4d0807ed13954e3aa164ad59926f4fba16759fd85b2cbae0eee67d21fc4306e67003c52904054bc393cd2f730dfaa67a4b4c27a4f9a109bf90a80c6ce2b459caa10794c0b5bb294100b05bc84f84f2c7482038697affe88386e4e9de6f25a0cfaf03408edd93f364ba6f0c44f157e139bc420b71426ad61540d05286d5e279707800d03ca41b9e4725ae16fb2c7008d1e8f640c56ec5279b7271b99550d9e34afa0d6b6285f37dc7c2af39ffcc6deefe1b770de7ca373e12760eefc6ae04cf4f760a true -check_ring_signature bc33166d3b3d024f20fb44ea67d775500b97f41789da7968c6d287d69919774d f04b703be02a91395123710736ce574af2e5925f3ec2cf5fd3738a79d2d59757 11 c925f7fba23cfe2afe5db1f492ace163e2c777b72e0a2df861175ee99d15c31b 7182be9dc3517023411ec180b847d398ba69a820bb4ac6d7016c9557a09ecc37 4a774e2f6d05810fb99becc17ef04902edfe13286249fcf9122c0e0235bf5195 cc97ecd383db62ed0d27018943e29fc95fb92cacd1abbddb2397efecb6da533d 656aac414ab63d0125c9f4fa1d50f29db63a845c654024fd1e4afa27c465a3e2 70b35990355a06ba67dc3a54d885cfd868b0c19b6f5ea44bd65b4ccdbd0b309e 47e8386f9be747b1f6d48daab621bcff453e8036233379a4f9e2e07395da96d6 e4eca32e739071eaf5bebe569c08e6d0988a3e3da4e336404fd851f33dd2718c 671b22bdfe150850a91b8b5def5958208c69e58a06a26364fce9a995689e10a9 da7bfe494c5c5126e0593ddb8855ba35875d0adaf226ae4cc8668307e7914b3b 535f8f661bf5f91ae82450358ecdd39fa996b9de888b72f6047dd690fc33fd9e c2c994cee76d86720d565d68f373bc77041c7682304484263e21fcd4a3fb8307f6f6eec37fedaee93b0251368001e531dd440729f79c813e9d9f3e7fbac0320ec52b5423ee64f3f064dfc688857e183c253468b913cc53e6bbec476f4703a40bef910fbbfe62de3303a26054adbcd006d40634c6ac98964e64b832655aaa3406cb0719729052b721e97454cd1ee8caa2ab10be13d136721a43607696996d7d0dec694b474f7752beabffe6196a9890db498222264c8558c9e3f99fabf1ede204cb3dfa65fd28c18fb3da73bbbc21b401d8529cf1c8e56d2d30748296206b3d05f790f25ba3d4c539e72664c2272b87aa91a34350431a2aae4347dc1c1aebea0ebd2672b8497f17a6a5bbb7fe197e04a42ed83f90f958abe0cb5a67ab4fddca0f3f327860cd0c3eff339ffade3e5beed613c0be21fcdd339d993fb797d8a32e02966859f78e8509c3c8fbf15019a9f02f1cc25c97eadd07784658b907a56bc100104b72ab013f401de6e7a7b227e7756ca874fa1c92408e7397aa1effca29e806c2c2d199f3361d112c0ce84a18f6e8a90d4e686372a99bb13d60460d935b5c0018c2ce5de07b222cfe6e58bc1c80c37463fc5025b067da336f44bc259ce8eb0ebc7ad98c6d52948d36205b2dea6fc64211431c910d4820df18b1b985be69dc0610b3144c4ba9dbdb040d5907a1098fabf6882dd485f8429e8e48f5329f532309806525f73e09322906a8e52ae2993e6326f28280ea356c6a9b894962b3e5150b51b6f90da9ab8f04b863c574db9d8bb6b660e888fe17a8a781d3ac5324c09f0298a2edb0ec72e9dfc7616511534c50c1457416ede09bc53bd2967c90d23b2c08b4c7f100ad6f60ed3d79f40a6fa57068d0d18df8bb0f729c7836163338861101b1d6cf68ded06846c9b43a5db079209350e562ca41e6834b9d44fc988e8a4a0389f4a1a421e194770815effb3d6ecb99c07cab2ccea145006ae9b7bb044e8c03 false -check_ring_signature 4390d4d8e02f3b2bac2c2b4782d7f076e501f2bd29620c71e34555903266899e 4cb32d313e358cbe88116fe2a67d16ade245bdb501a9304a64f2118cfbf8fffd 111 1605f70f376c1356d64d94b97ee5a93a2da27b71ae4c545bc4b87419d44d15dd 4b97e16eea15ecd5e920a48a8600d637f429af469e9969493519ae88a6a3257f 37bc2b4363c6c569e32b826144ed83f85aa257830068c2b90671c054774ccb6d 43ef9c2e23a2aa1fe8c6d08056a01f1b3c726af40a299f3b693da0cfca72e82e 12d1367deb6af672efffa97b7bbaa9cc4895ce1b4dad9f146aff79cc0c1c4e85 9e3c12a06316d1938d1bfa2cf80123c146aee4c72cffabd4d846c1c833899205 2dd3c627dcc3c8d9ee2cc50f29ac5039371e05f56127fd9da602ad083e62bb5a 594dc217bb24e1bb6b50463f43815d5473047d952dd79dd5885fa97be548ee35 f00c1e9b2ba1433e1987063317375ba4e51318c6c845eae60ef28900163ac1f8 a556d078fd8eaa7e36bc53102c605120bcb63a7ada485de40e7f0bbcf52fd965 caf8c7f1bafe687c91039608e2b0dfbd3248519766fb3c161bb3eb5383520543 b1e80d7487d22986baf9fdfa9fc31f44abb13910e3d2fa062ad55218c53e5b73 f1a3049e218a973533c0ab6aaf315f37e7596bd353d315e227ca6bbd03f76803 a6d5d2af2259bebf8d9470b383b9dd5ff7c41ffd03adf716d1c75842edd613bf ce456e878ddefd8a0d4cd545c25fc3098eb005abe5bfacc1790a161539ae1851 aab57e2ff3415f332576d3234e6061467a69315d24589613050cd6e1d7c167cb 35fc69b1a3a06ce4615180a3a7f9013a6874f0be063e2d6de632ac8af49c6801 f366d4763900ebe94f4345b195e5f6ec5bf5f992d8b08309d982ff26dc865c67 1c9a096954c02badf4a0aa57f3f60cba75d988ca53d3f8333c90f0343379620e 631594e5536b0a49d07e0d66ad8cda81f84294ddd1efee085d0eb1210234b2d9 1aac2f99896ccf3530d4c21dd9f257499e63c6c74798253b47a8b171f626da33 9582af123be3cdebd109b6730c9c707e84324d96d60d5ae62b030821f3edf17b 0db00479589d1331ca5e1fadd493e5ec2357af7837c454dffc0425199c31ecde 9015f4c3f04d223c15c3387da134684e33bb2f879d1d02a7206a7c5533b5c414 2e3b3f29cdad88fea5b9ecce050e6ac482731e1d138f110ca177320d73e7ac31 5a75e98c2c0b04487259fbaf827e3bd49baf635390994898b9cf79355229909f e92d4c153a1a209813ea51661ecef9534cc8dea965ab1bd88c7ea7e7b4b48460 6940fd7200e49daf9741ae6dfb1a93979f8a19ce749d1a84380369941ba2bf8a ba732ed198bf407fa2aa0d939111a79b970281c0a8551de2cda9d2534c719dcf a9fb652f06696a83f473a3fd282ba8c8305c461853f0b56c37d6d7355d528774 83a5b3fcf8308fdf79c7f79e213e848c0f8f13669d0dbfd97fe7a707b88fe7f8 ffb7ae2d53ee221fa34587e9a363e7405462ac023b42564460f042e6ed1d3e7e fec96aaa3d7cac92383e0a4a8f3699e5dfe8aaa38fbebd156b37f0031120a415 7d9e74bd4ae2f2d812289c7692c07902a3f8a164d5c69670781890baabffcca2 6f8f58263aa97d1dea1a41bc7afab9f7d618b5332bde6fe7b80e8fa175146277 d2b5b62dab27d9c79cd15863bfba7a0fc2d184e32c4e45b996de5cb470ca4653 00c31c7ecd79158dcdca73fc896332555b8dd979fccda6a66ec1a81a32f8f675 474dd30efa2286288ab6c4e22383834f56991a67c7727fce3438619e6d7b4a04 42f61642595edb571fb4ad214beb751878dbc891d6712e7c63bf72efc050de4d 7494caf1d92d658ad46a47a158a3b8aa698076e346daa1e3e731689dfc8cfdf9 d9cc9ab1b4bc89d4f00eda143d71848a7e4ef99e334a24208772c44576176685 898bc6ad0b96a90e03cde4ac9c9c10bb3860d4d68cc9117b7decc994033c1ed5 75f10d95f0aa0996e583307277bda91a03d639128c49be6cb1449d4230af8eb1 e7d1521621b9bb27c633dff83d18b42e5c02a0cca79736d0917e47dcfb2c7b63 2ece4af253f9f85e0cdbb0a7c692b6ba46ba1711f2b710721e4cff7062faf101 77bbdebc81861560098d331956b6fc12b7ec47c7a9da6b0365bc7111fb92961f 9a9c957324ea254b6ceab4f18c28d29159994eb877538c593fb512d856f48cf4 6561efc16bbab400fb3a76f885adcdc5aae89f853f30220e71c0dd850781d74a d4a8d0dda83b75ef5aede87ec187357c9410ce0d13bb98207023efd84d2e4af0 d0dcd91bd361d3fc12e3b59a3bd7e69445ce57418e0b26808bfa34ee9411fbcb ca0c948b2513d4053a12f02c1d7e81684952bc3e2e373b1081b0796fb0e37443 8d532cf002bb48c3957949dec43dc1b61741878f8246b05f31fe32ee933f8d7f c2453894930737ae4940a285132daa497ba121e2f9d0b1889120b87782ae6701 790800732a7af393be48300ac566ccca90416827b79e0bd0947b0fb519967c2c 53bc768ef8d21e48241492236969bde33654a402ecb0e1cb7470f44e528c2690 251d656153d5c2d39cf4354724b67c0be084cb19e6e1014f5e1c595c89b8f06a cfd2950b1f2e71cda52103c9332f5094a1efac51cc5ecf8de388904bcd92ddb7 899b1066e37143b3b917cbde427dacc36d1892edc3616ee7500533d48337c26b c55efc30fdeeb4d645b315eeae3e7cf244f255020189dd7cb1396081fa0cd8b1 7d463573a0b4f9810178ad0e0e1bb57761e439a9171e90b6d0b0748fa68dd9fc 0731c1b53c2fa5f5d8887445dd09c73575397a6b38f56e2841eb06e9a1dcddc9 11247daf8e403921b421ef33486d32dd8686f71f547bd39615a281e0ac304869 c67c0ad947ea612622c92dd388a9deb0d72c7067fe3576166bc2730af557887e 18635e4ef7c6cd410e3a855fc9e10c74c0886bc51cd9be48ddc1d38bfa0987b2 03d2f20b3c0451ce656a61f9735a8dc0a1b640b4aaf59f70d57400891af30571 04fb4d71c268ffbb16f279ea6cefc8fd88ac7ade38142b4df4f5894d1062ef45 b686c8d81090c90c4ce2b60489eb6f353c9fdfd65fbcf292a14bffd97a138232 30c82dbddb9bae741af30800a6c93ff7c987297a486ec6c6ce0b2981fd6a1fc2 d4046b33b39cd12c318fdca173e0aa6edd0f03c351758cd974403159de3712f8 cee0ce0ce4370c6ba72b6b016163dbbf31cb230338356dc79223fe51897d4679 f3011565a8e08a4d65f5b6e39f754867ef76a1b839285733470827284b107a8a 299f1735627cefd879753484c4005c8d7571aa95ff223193d5f7fb77364b4bfe c6a14e96765ec2ad5b5288ca4badf4a4071d41eedfe18f47a7680d50b05a88f9 f067652737ec9a2940d5be41b87caeeaaab6f1abf09693fb2184614493aedc49 a1e8f03a80a6aa6b67544f42f4ee8c2eb8b51322e643add1251bab4e81730714 497867ffe224eee0d90be4d916de981c6297e2a893911b4300254f1f951ab224 1cd888ad46325da99ab38efb41955cc3b7e22a030cf5e5c0b7bdda5479a20fac 7753f347c2b86a8c933d8069d5a50e4a5770aa8109b2d74122872abc18bf6154 d3b4700c59cbb8a1ca5d940c1cf7cac0b1246e313665956152d2cbe5246cc6b8 245407071465a28b192a67665dd7078bfcb28b7cfb8d0619de51b77e0ffe7f9c ef83c5cdc4f50bd626d0f2d267f34e830a50417d43618d41accb645cd59a3a26 b091376173e9311ca8da1c959d13233ea00aa4c2eb0df9c076ef6fc9f8f531b3 aabc67c2343f9819b8f6354f0fcf0066c79e7bd4e34dbe4bd3e687560d51d01d d6206de6f6bc890d4ad6324d720acf20ef50c964597c43678fd134d27b1c45c9 17d143dcbc1fa1664c922addb51682b2b3c3393ad74c991338b3066c54813264 76ea1e8bb41cc561eb0caa2a2e41c244d1929e38d9f1c9e8e38f3f64f9142a9d 66544756fe693abd94d2fe966b2030e196f17c2357d416020274c9a7061548aa d2e9df6a878fa0c39216c1754b0d5ef8e12e5efc042195aca93deaec9005734b fdde01dc2faf4fb8a99b963a9de1ae9da620729545044b4ea54c312a977849f5 2d1f7d98208c44af0f49cdb9039466031b46e7fb149eb45d1e7f8849aae41c03 b0e8cf5b9f84b6a56d95eca40ad2ac9c1a75ebcfab3b8938a3c44745df6bfa93 0e3f58768be66e0e0fa3f158df167bf361c4e64db0af5e58094f165bb942892b 9d417a8389f2724c5f78b1db8fdc99759f820e72fdbc53e7e2ce0efc4c1538e6 18d59469bf630b38e04ba5ea6f6b4195f0dea57ecca8998100951e096583071b cab5302bf6f8d2e14333c524d7db45dda1e54e655d620dca68db2e5a306728b9 4724f151793b62cab2cfcc5500a15b498d7a5eeef4e918e515a418e50e040531 47190da0235215ebd4f3f5c3e8b428237bfd3d0710ef71a6a4a25e1bbae5dba2 8898fa04ab0ca4e704ad5dfe1278b8f392663186c4cdaad0ef5eac77c6041e14 e997856f44443fee8aabc2b8b213474754f81816038f0c2e4e8cd5adcac35e2e 705359442f20089fe6b4db5f4cc40c5ea2506734758477be00b61e7088660bea 26533fd93cfc0b294e29557f59eb6c869a0f911c8c2362541eb49503db226bc9 bc057b2f794bf78535a7517d2654d841963e525e72c7f007a2d1dfa0c8f6d23f 46ed7dc78a56f2c4d5b961a42f476a6a55ec5909360a6eea69f73aa627c7112b e902bbabc3a518873f6fc9a230bddca6e90560a904ba726ddf93ad42b20f34bf 3031b30bd6e78a462f387a58204da8af876add7a09454fafaac485cb952a2e87 9811d9bbcf2ba1e3bf1d18773ea7238dadec32a79f5c302317aa716831ca3ac6 5a283ed2e747fa3cf15eb65f4ac7d5ec5a197de3d4f51c05f9411b67f6db2d6a 7879a7eafe25c05f3d6b13a7e3ca59ffa00a3535263aaa2b29e9405e888f62ae 3b80bf3496bb2833b2d5f624d2849dc14877ed11f769f9d86ab6a1c1fc016fd9 2e0db33849e26147ae2cbdf6408acab59beb431b8d5694087bd2c7f48f84007d d16c690fef1c2ca013191f7527a724051fda04f1d65778c9d19e2fce8680ca17 61a38a6dd51a5bb31165c2584db375b2bf8ac60f35d08ffff799411bd84f500bff12e03f34434da924d86a648ee3b0bb502eebcfaafa4d1b34fcb3a5a2eb260e8132833d61642dcc3cde9a6044234d52e2bb4b503ba9467dbba4e4ca1d276709678900af1eeccd2d5ff428db0c94b9e92cac272b917fb085996c4d2a25157203df1fb1981ce79eb8c747df54c7e05cec6d01a468e4b683dbcdc9b192b7c74105bfec92ed2236c920503346d0c664bc57304a22c8133956d9c37f0e0e7ea6160658edf886fb43e0ac23c49328d45027e184935ffd3085428fe7cbf73827e34c0abe66b46e917af640f9a38480198dc48cc8e634d14036d83f28e34c83c931980089caf128a06e5c34be585fabd2dbe0fac4e2c0d15a50312b1ac66fc0bbd48e05305e58f638a85e8902fde9b7d027381643c5b4d20d070fab4428fb5e04004808d3e79ff186c92a684690e24915336419c44fdc9c3a4c9dc8cd76514290df1805a16cbb98c7226c4579d989bf4c51c79f6fc1b39b31e70c7b3742330f4f19e80c400839e54e6368ea00505a3c40b1dca39a9652a94b281f6c47e9a4c69b531e096f2d76c03d455c29edce9c1993009ff182e3bfd29a27f7127a78862523a84b02c90a2f263ce8896b544e79b6975b03096e18e146b56d1ec56299be86b86be90bb585054ddec98047dbef7ec98fd1da05ba8b0aefda75e9c692121445d3e0340acab89917c5e9ab376ab020d8536c779d3de9848fec334a82b515b2a61cd2f70a2419ed55c9ffa2a0943df7a8bf7fca2bdbe6a8473c8b2ffbd5860a1a692bda0fd9808e71bc2404cf82812607fcd1541cc05a7ee9b3d0fe89866b0c789a9c1304ccd552f29d20eea83bee715fe5c927b3417f5154668941a865e543b046581d09bf000cbcd001ac336eb486220de7dfb1f6133b6550672c140ced8afc8b5b280721e0085238c798b6bf3d11222284d1a8a6575dab3249f19ea6ba54b18468f902897a212c37c784dcbe3ae6e88ecc86b467bff24e964c0fb1847d6473a3bf320f8f3556f84e603de95aee0c970b6c1fce2c808fa644de848e8a65e969431f8d01728757b5da2e6ec518c057224164d89aba6f81f8559592df81e08938cf00150f148aac2ff4e3d5a82ca740d9a7448cacbff60a80e14e4d4346a14c3d368d8e0f572286380e5a03c4b23ea6c30ea4235550a1b7bc7e34ccb2dab612c6e8e1e00ce08576e32ab35f39f1c8afe3dccda6e8878200e2cb85cbe849f7532fad5cf80c91dd5783a987c75a82243dec3cc8b7794b4ed4ed283ef8795d5738d7344d8407d32a35d3053a70415f75605d4eb2a58cc0dca547b028ac896d1910dcf43f70072a7258966d124da949dfc7b23373326fd7db7f67ae9a4d26d38b42dd6360910d5b9f4528697ba91d646d8b95be96c41c42269b447d8c9d648fea317a7a0451058471d5ad35b21c1a67020a29708904e0d6588c4842af3b05d91b3edcfda2c509c4dc0e3b07dcbc2ca7c70a0821cf354dd19e28cdd23627e295f0b36f33168904d2f8fc8902e39802d2fd1127a03fcfee5d32483a40e229f8cc2d1a063cc56703c0f72318c738e6a926af2f232050fedd8e5d7d38113248007a87a6493eefd202a61fd80e684e936520887e069aa85f1f82c1ccf211892a296323cf2a7f2c3501333a264690448f980bc9e2646b1390090356eea8a63cb7be0def27757a4e2801bdbf9651d4e9e322cf51b7789273f768b73f7d79732e75ac1c57bf9520ba4b08a70c38b8d5b39a9bf20b3c99c2eaad809aaa873621262827ce548db817bda10aca03d568a91a96c39eb10d161c3ff5400d7f17eed7d9c4a0419eb1da566ae40c6598f8cf31701af66520a57387f90e2796f9e8948a4c275979ddfa2a8b4f6f0bb9243dab4f7cb99dd2eff33e797359158dfb61ed6ed465bd45c0a01c11f82f0749f0ec80b12c07bd64e6fe85141e7644027864cc378bc5478b5f93eb3167c2062a2dd0ea97b2856bb46ab4d217886fd24dd786c68f69a312a9159219c8530303b657af278a17f9183edff64aef72afe55ecb94b2a10918ea30aba1fb80c6e20164dad58412b6f16692971781025f28a223411dfa51b1193687dbe61c49f52b06a40250add41c553684f101e3e52c9678ea545c0dbec25c1930736f619ef8100b5b2937cff4b1c592c615a256b0593611b7b6f0c34ade189471b2242560ed730e456417f3cfe4c4a2d5d9a202b3de6cd144ab600e7d7533a0c91bcb932d99c1010e8bb2d45df6652424611c6028781d89d8ada119445c90e1080d7a5a491cdc0cf2c003b63a45b0c75e5f43522ad444c16b394421db8111d93fb51de4a1214c013bad642696efb1c577ea00776b5d7e7a738a1c2f14233b521e52f6558e85ce091d61cde499b57438347e577b0aa26a1a896d3393fa221f92c8ed01e0176ac505fa867cd2f7d4db16614aca6228840db2b3eb228594efa65e6bd7c0247fe279024aedddb64d2f55a17f34642e5b538bcfc774b932eee7cce242a07cfc26e61d0c986fcb216db7f98c5bff0270f090559d39d478b374884d3aad27bd403368850e01fa7186f6828300c7a6b1ed6bce5d0bcaf0655b80fd71d94b5b6cdacb256109e0a361fbde3e98bdb34ae1015847256a9bd268a759b1b40b320dc1b9f88f2e0ea40f7a30e1f595865722a15c87cbfd6f3af34ab71f1f9417ab5296bca6f5490c30edbf2c10524dfd00d81353335d6c043835c04050cfc60cbde6ae3479a8b301e248b21a6fb9e325190af889cd9912c190b154fd1d4fc18123609c8ee0d6bc0c6d111a9365840e98a37d2b854fa44bab2867aef24ae8a56893ae66567cf1570a5cb8894550770ba414b9c7ca2eef91eccc7ddd1f65086bd12af95f6a26b2700ad3ab64fdf564b36fde19aeafb53506657011a16a11146cbafb7e9019f9958b0599b4da9f43d2e3abbe1a2482f7a3ffd7c35ed9b8aaaf936d45a069fd1f39a60ff6e65b6501866aae35bfef8b7f6f96e73419d306e802cd90dc8a005fbe8fc40afdf83729f9551ec10c8016f601122a645166f31cf4a93ba9e8ac8aca4f9e1f0230e0b68bcf4e763ef369e333ad4f2ad1e3ac16f8663b5bf5fbf8b57b1c52ce0fe5c39cd508aa8cb3689bb3978e560a714880d2cf87551313c776e4d70a5e6b0ba5515aa7d298c5090963971dff6d5212d1f321a64b72ee45685a93768ab49f0218d0a42b1d09c68226eb01c346c8159a60e1af2bbef8e17a0a02c6cb6343bf0c12c6f08688745a87ae1318462799bf8bca9bc50e4d96f47ff6bbdcc783ffbe042686d07a79e8b9f07d5bda583b013a35bee276f66fdb720fbf006d0719d7050c232cd184fc92d4520adc81fc1f95b791d43460a34daed1058b4384c3afc5f606f004c88723274e99d56b78b58079492147eb65bc8d9fa3f78de545a252bdec06e050dfa1a15da72393b85f2f4772674bc8db49217ad85f7cfa52b376b261860431539e57f94038e4c1e503d9ae8d898763f54ef6497c18101c49cff55309b60a674f11563e2baea75e64b29ace682f1b8441e885e01d04df45ab027a25f0710db4a5d2832ee17122993f02d0d949b612d6844a11d1c43f03b7f95e8cc614be0d7b02aedba99cee6b7ad6879fa269f5d35603c144fa1efeaf1dcb42a507e47303c9cde0bf33e8fc47ff7460a64c6a3d6b24580d00eb1456af35229af8c270e707f6d4e297439576c484e7b93e505b39e659b58ee9e798cc9f545e7cff336ebc004f487b440cd2418bcd74200e9efed4b2bc3c6d7ea450b6affaac0a7821ec5c0a9c1b9f0584ccaab599d7eacbb845a722a325e4d5c6d2305f9c679af3b8db0e05c81d247fbd792eef53f987ccab9d16c2b5b171ec8b38adcaf8b080ed79ad060251ab9709c410d417ba17af8a784113d6c79000f3e48e28d6b50af7a70a99db05dc640a319036be9cf8f2591abbf05acad0c63b613c78f0925136bad8872f0d0db91fed01ffd559282ae0ba0497f5b6d5a5cb81bd01f48cd7a341a17a9ab4390f061914376bb9d3a33a83fa8f7d0947aa2829193805d23091edcaf511cdaa8f021351d039168722f35f77908a5a55982b36dfdc666a5eed1c9e274d624b77d107ea6d4d0c9b19cf522823354271cf958f3424d5bbb0a2b983687632a51110c70c4a43049e602025bcfa703d254673dba46ce5d119b69a90f7a4040242b451a30e1100ab01d2f4e9e089dff1831e43070c7e8c782f3eb887256de06f33c1b9a30e56edc291efc475fe6ef774240f6f305b727d76988117c60daf0a2194b9538b0376bee5c41523c7cecfa2f556c21e5597415e9e85eff8a9d4a78ad1a03940a30585c4634edb7a28417594ca7bfbba50e4f1afcff04792a7ce4874976a3187d506285ed21fe847a51b281b2ff17c456dc86b63a5fc246fa939a8b6631c8c20a80f7ff24f6a16b171264423ac7c98f99ffc39f34f30de7df822d6ec4147e03a50025ffe44b3b914c8e1cd417dab74f58f41af27b9d25227122abd86eb5fa57ef70622b34fcc8b624360e71ba75a06fd91d84c099053ccefdbe4f300c9bf5ee20e0648e658e508472449457b48f66abe5dca9f309af97856931a2ef4c4a0f090790ca3cbb8916e39b8d1b4aeb0cdd5162afd51d42fb685bdf32eb3c0d2e4ee45450969e7f489cbc1bf5268c5410df6956296b4e28003011c0d64798f1f4aa81bfd0c7dd7a18ccab5c9a00f3a6b5ddf5c67b3e4563d2610b3db8e7726c56914cd97054dddbde4b43f85ab2972ed6b10f70c933fda6cfb390d3f62c3f1be77a3f5ed08a44acc1a6eb4aef795ebca1607bc06884a631f8b9c58a1084aea73d87e669d04f7ae5552f812cfec53311bebf6cb3374d18b76b457b732d9b56b0998d2da2f0cc48b222ae1725f2f38baf91daa8c5d017d2370c6881a746921d3e9ae69b81f0ad84bc20192c98445bf14b7db1f1a2a274d07abbef51b197a640e9c8ba8412d0fcf9ae2c84d1f27193db7b5b7cf535a96031f77a8521cca2f0e1b241c28caf102a1d3a36e840b9bcfe88306d75684353100312d0419191bb63710ec3e0a2e13055ac4de9faf6a680fb803f476dfdb5ec86a1008a478bdeb3fe8a1cc4bb8d67c0b83e36411b5a5026765c003071c730f384dba291ed7ac244defc1e5eb26ea4305bf0a6fe14ffdfe1c75556a35fc9782589dc336d6169136c7b32aba02fc46c90d3603c63f85bb92d9fb336c174ac5c97693b5f792daad2ff95dc634b088e6710c89f0b14c9e578a5a723dfc39923e8b4502eda852c59dd943482b7d98d952950b46e0bb201a6c12eb167be411f3c6f5cdb9580ec985881c5f9780e827a4e52008605a0017658d68a88a6723e8e5b47dc4ee9a112c450ee54ceaea62e134dd190fc239b17a39e611a5bb99368efe1fa97bc4d5b41b88e9ae69eeca43e9593ea40b7601b479ec9563e4a4a475f18a730cffffcd3548a2777433d0dda14ad54709076b6ebb686a925daae646ef71694d682715ba159495454f9fcbb603a0fabd52061ae289ac7a46ecf889c3904f6be0e71c5b5702abe6b607737ada649cc75d2e0b8cad0924861308b3ce555de1c5895382d9c011737c3ad248e30ef3f6ef59c403abffeff41b988173ae8fc28093d5d39005e3499c02817bcf6c3cf7ad00025a0bdaa5d3df7cf411964ea0d1d4243fd5c1e8db01b7bad864898274c1ed40208c00d433f51b1a61e6354cdfa1db47d41d4c1e3112608e1fdfdb3247b00d88992c05a916e676e5238158e3926970a7ce4943b9d75a53549df7fe7067cdb34727dc0c2c001d56983b21dd9f9b81419e6ac1abea64de178a89d108045c7a5671a81f0880ebb72b0de2c0965edf92782c0ea816152827e03b1823e97bb68d010b29660999e545b3d649cac8f60f41497ae676aca8376462caadcd778e9d642d7cde5503b27c4aab65ed769a67288c77323c98f3f2d1950ae501bf0e27439ad839cb2a07b219dcb3c1885ae4540c8132bd01fe786ca642f4b380da36597bd0d4524d4006da9840b46ce5c75510d72eb1b77a45ade86d3aeeaf8dfb95c6525c1759ddcb09564217c8eb8693e71799664c0ae95f05ba01cad81e87f39fb0dbe27958b052080c1a39e535aecd50f327304c674fa2a68ef926542ab8280cc4c0ba8c90eaed043f997864d1cb0d66fb85ced02a10a23eea4b65775782636ffccff20cdd131702a93feb3667038a595e3fe5aa9a88194b25e153c74b56124262338591e0dd9905f17eb24218f985c1d0c9c0c681439eb861d0010cb2fc4854ae1e1128160d8e0d6defa0c629a0bb34087fe84d35bc56b5f4bb490d8d7d1da9c1bdf47849f9bb0e5bd1dd1861f28e9be495b6204c3a361c65b2761a1053dae635b7755319e80302e9045c9834b9d6d88221c73576f1eea1652c85071896e4eb5bf984cceafa5206fc1927d362342c2806d885ae40d02b0440a3897b7c6948429408bd6ed321120ef9daa4d7eaf2e440e002df81ae8f8767546ead8e6bdca085af1a8eebf0af91075d0d28fac115b4866a00229fa2bcdae1bf9d9132f68a1a433c38d789d7363507408658806e7d3a39da75553190b88f789bd0a4c3611626f2e07e99063259760b4e6bfaf0f9214d863b7c43c8a03090135965f5fb4db74aabbef2001e0725e10a0e130d05581723d0b8a336ffbcd000a93d4b11bc2db7b9ed64716e0aa4419203f07e6c4f7a3d2d2dd0d3af81a8265b73cb887fc92bd49f76a42dc992cab0950cedede9b7384803ddcc92c59fbc9ec958210e2ae434855423d2d4cd38a33ff00b7adba3536c4019d8943600813330b405cfa7fd9fe863c5a9b5097396f70b410c532320cf08d3798f88122b5f1f3baa4c32d6d72cfd1397670e5c62d82348f70ce9ead649cad5cd2a6cc6f589e868c3ac8683723377e8ed7930e202a35b5d52031022525e3051b844cf9a58d58ba54d936b65c0ed5cfd7482c771c2abf5fb5808284cc5206ca5754bf290edcfe40eebc92f1ef6df11d65760705ef1b5883a8206ec993655a5e1423ba7a36e9544f317d089b059b7bcd6822c99c8338bb6cb090350457ee9d0e05b57379a73e2e9983ada7cb00930ee90d2e8f0381c70d6ce390918afa6db7ac572d0d0c8b8c2903cc43b56ce33240e0d48db5d610bd3954b9406c592cbc1858b7dbc96856605004dbb7acf5707ec7c34dbdee028a456b36c390ffe6d4f43f406d55b119d8f01eb56ba3b1fea194cf245919f431ac703f6ba16025ce8f822117f9b9e984e20061a4e41ca72a9273106c22ec197ac4a7dc4711e09687dfffe7740b0e01a10bb96f9321e36b89fa1f35920a5c927b9d1344e73740feb8e79f44bd52591b72e07b9fec70ef3cc844b6dc267648591221964fc19b40ce8314709b74213a6af5d1340808364e3b60af034dcaa148982101bee2f428c0e8ad1e3ec035bea3a4a43caa7de9dd175a19aad97b017977a7dca43a31c60c50359820833f911e912f12b67fe2ae6f7dd2451c652dcf41cb3b6b5db96a0b65a0808a159fb21a3e877379e6fdfff945aa4d30c4551c44c18887cc586ab66c402030829b961e60dc59b74cd6d788ba6a59bbf83ed228554bcc873efbb8cb59dc307fd664f62ecbf5838d7dbc246a41e90fb4d8ba271bf2a2911e6f09dcfee5ee808519de922d50444e04dcc2079f6ac52d9a86efe267ed71c26e1c85d7147037b04b625ea06ea8ee9613539296a46cc02ad97e8623e6693ae8dfc8f7110b1197004decbf8b48d50ca0272c655bb34ccc18cee976bbd0a35266c4819276b097070008deca6bbe8e0ea6b1ec1178e48e7beed2b37a028baf5e040927af3f681a0f58074885688e46175c0e5709ee9048f5a767063d994ea8918b2ecfc637142b76e00c63a7e4e01d494dfa73f830e5c247c1f1c7f591e81eafff1bba572c79e883b0c2047ca05c903808d92f1fa2e19ed1b8a1ef0e8582d8af9b55cd5f636d9d1820f0104a571317a1683109bcb4ddc28e39f64ccd54ff95cc2c0f34e1f71aab142068f4fd65c4f6df25b257e1dfaf445789d38a2ac83bd961d5392e75eb6605dc500f04b65d8e0aff7b0164ff0705aba0030c17a6424f83db71a5dde5b8ffe7c580f39e2dab17eec81bf0f2a23c8576ab0700efc2ee711867459a00cb3459768d0037c18de8b55717b955d138ef8f7be82eed9543dc38873f505074ad0eb4b86ed065246a75831d40ea73d8316e6f435f4c115e9aae470b7d0703142f3db34ae4a0eeccb29472500d9fa7a0b3deacd26fd9bd0256ca7d08ac4b841b3238baa278905382032e52e58f9d38b485d94f25ab0c9c03ba5644eddae2c1781caf28790df0962f883d278b00a76124d0e7ad068a44e16af4b7765272c67d7a7461c3021ec067957ce294f98b3b2e003ed6f4a6430ec293936fa5ad0f46a74e84b46533b540325748d5ab4885d01723b18288b291267571cfab053b6df70923b508f0dee780e6970d99120108765888bc88e1fab3859d6f2f5e8097705e69b205c0f9d941e0d933c92ab8f45cb4b1f9f6838a77c37d3c89aa4b383770c13af2fccb437cc1f00529766fe661e91c00cb53e986e7f2687eb4f5b1cd5d90a265af1acdadd08fd06a895dbf7c7e51bb6b9c35d1f903d9baa13fdb859ca4f484a49ad50dd6df28b053f50bbad9cea4f309463fb013a6d30fa287d1849980e444605d722ba18db670d080b64b1adf3cd8c655244177920ad3b1154b1ead7dd1ce14ffdbbf4ed782805ffc78efc9abd979f0bcc17bc1f84694703fdacf0ddd904cf69cdb35f115b330f002d22f87ac711c6c50f68367220f253fcd71c553c3872c4999979ea23068306ea1999ad5dcfc9080986896649a49833de60c2c6718ede4c7d6f645f2e6081067e3bce8f395ad5bc44e9f479ee95c5a48c8101ea5305a6e2674018d113a07104233d33a01889af24da08babcbde554c1f6f6603c63fff163bff7a8cb0cd57d016a3e24c42321b59f7c0113cdeda199a4cb815fb6d8af154065cff67976a67f0a53eac37510161f36310c4a8245033f39e00ff8541ac768cf2c516166e7ce5d02c6f31a5c9a8d44be3feeac2c74dbbcfcfd85b50cb74c00114ea0c50ee662320b6ebeb70c8029bae8772dd311bb5461efbc56b47057cc8f1ab7b20f70ab15a7016440e80f385ae79ad50447e1b11040834bc6712fe4ab3dd3aceaf117f02be80418705142b99073bc747f61622653f1dc15024e05ee70bd08a5dbf9f5838dc80f6f9ab2bbd7e8f70c4729de313f315ae2b67bda03ab54cb8eee3e0b44b348750a0cc69ac95b17cc96a128796e5246a1c66b0b0d749793cc37c17987ffcdf6ea0d66f3403f3779707ce9ed19f436a27f97c403f81b6d596b81e3facc3f3ad2a60a927a05ece1d1b06c9071be012db017aeb9de8d2d72f01d6919bbc7ca41e5ca08e259d36122ea279984774c5d4c7c836ba261c0b17933d92b23b84ce9c612170126895d5ced98daec83bb946f1dd284bb849f80eecbd81b0c05831a90fb364a059aa461ee4ad3b2b056dbab48cd8b0e122165230abb33c04e164e117c7a10bd0ac10ace3b4419b5825eb638e233ac6b730a957de4782bb5a7fa4aca83d9ca7409d26036f2dbfd4049dcc59579e8d0e335533bd8ca5ba4d442ec156bf25abd94030d7b16d9046faf0018fd042424f222797a9861e74053db1280cc074eec218e0756fbde321b1b6a6d4e3cfe8abf983b0de1c08cc62d6f72a8196a65025c227008dc1af9e458e665433f3fa5bb5ca8d9181f883437ffb3ba0b122e6b93bedc49074728590ba626035fca0deee2beb1336e12a8bde03330700d0e7de485886a070900b6337b34262db6505d563f19d1c71200595045c0547b0c11c16a2374f265027e4d06a4e55f45be75e2c9c8235b2f6ef22171c8661caecf6cef55a467050202a3a023885ad8218fb4d27f7b2e30418a489ea62d90ff8f4ee3150b3060c067077ca08c50ea01b7555e948325513a3b2482472c483ad02a41043b27e1809c07043d4b39800bcf180636bcb6e4a25d03efb251286cd6bfc93ef0bc0454de8e180d false -check_ring_signature 8d6d2be44897e1521be30015615f9e8be8aa1d029d11fc1b3ac5f162f5cfebdb 71a7c6320d819d428e34b676356456e78f99c7453955d957a60c225e0dcc096c 56 c73a03632eb08bbc229d993ab8fa5df4aac6fd4a1e9d76b1169a61db54e2c745 9169b50f6deb472f55e03e2e452f757f55f439a5207411b90e03aa119950d5fa a3801dcd7ae4ce268053e193f5a299787759f08f866b9bae51ee5a427045b1d7 412209a1cf868f63a903b4bd7cbdf8bd2106263bde74ef0cb0369498f2cce383 5ab0f50bf7ed69b5f232c9c96601cddb087f3df9b1202958c01a4d6ac8e57846 8bbb91806bed8c48ba4688bd559428181b578409eb242f9a761622937a6a366c 6314665af26151a83e1064c0549464de8b5f4d7e4e15af2d30ccc2a4cd10b654 0a81d727be5acddf91a2956e833ae9b205ad409be3fab9926a342f6f71c2c64b 8d9b984a3ca5efecc8c733e687a7ee6374532560ee221881d7d3b221cec79df6 3233f9b8c7b21dcbfaa24a6f7051b6fdd1228fcbc40ecf29b16b0e31c13db337 9583c014b5651e6ea68157c231e621958335511437b37870a6f129c3a8665a19 05deec48669766da17139f4a4a286aa7fc51b87abb0d0ec4af955d434bc3c25c 2e7d7ee6e556eb14feedee8a502ae49aad0b41dd39cf4fecde05965fe3e3c69c 2e1ec966bdd6af4f16a84ca3ee129e91d237c050f8da7c08ad737125818dc7f3 842f9026c9c4c64410734cca5abf509a76a6322f670918bb17ba3f888dbec2fa 710235bdd1937c7818002771c374d06e0d29fefa678cbbee953152dfb4704f53 b5e0fcd6944753e50d38f81ad595df265d2a8d260b7de939a6b5240d377b1e1d e9d4999f3c694cdb5f0c77054c44d69fbf0203a9ce0ccb41ee3e7467263c1ea9 eefb6c8e248e59dba0539d3355a25c998c0fd7c23af9c8221be2cb7a18068d99 8d3a8794b8dc11df53844607ec1f2ffdc949bbd0da3a2733229e7523e74fbcc5 1176744259263a868a37c45b601ecc32ad26f672dc19e9c1a08cba18abafe3ea eebbd914f30b3abef93d8dae9f86dabb904d7bb5702c79b8fb26b9851bde88d2 e068326a79cc57d7deb3efe67dcaf3d0278c53157322da79108c824d2924ce3d 89836a4ec863be08cdfe646116250c9ff0e8b11d64aeb0e58281f7667f609fd3 51fb80f161e2460c37a5ec64d88de90e22105848e2ead1de9590351c99a7f66f a66ab5bea4ed771f21397286e6293edecbc0cf9e0f72c7ac32388adbe23e38d3 7744c9c52e1c9221430a7c582d6390b68e3cecb341f5700fe8d0684dcae98eed 7b831ec359f560f4508a5297f05b3d662b5970ca4237a6c2309a198fa2181956 37afa7cf08881907cf7277176d268a7efe031247e694ec8a5fae57637863eadc 231b267bfded4f3ffc985cc32c577898ff20f2857d5f0404e616bb109d060816 b96e403d84062c54e20d6b4071e5e4dac59d45aea3121e62c3b897b838d4557d 6f1f1267307a205b89b3ccdd2e24cc0b4ebb4cad4be8c4cbe6f0db0f88d5b3c4 30fc53d76b63e056bc2ab096d02f4869ad2d0736ce31d4761eb99ad45c031c8c 03dba861d6166af4a0438d87550a30ca62ebcd77fd94decad76e91ba100e9ce0 4b139093ae44ab9f8913a64b118927c7461fdef467ff3d50fd7ee3f913ca023e f8e2bb6501a984347a58e1fc66b79d19173fa190847e58d6c1267cee4f71b4cd 3073502fa7a09fb536c5ad9667663a811d3981a129261d540dc6cfea7d36ec5e c834ddd6091357184d56f3b0b9ad29a387f16abbbae18374e5ad68516b00ed18 e166ead15e3c8b0bd5fefc648a7a3f3a173948c00f796cb22feec91cef772527 016148f6e8ea0156adc9bde8b88064cd0b2cb5032382d1b661f7d94ce221d1be cb1918c4a756ebe8b2929ff0210992120d591409dc3dda7cecb2b7945c47184e 8e324bd163df227e7fa68e00f8d1de4e375fe1983962de441b3ee32322f2ed05 1403a5ddda556250514b3fa539be9e9b4e06f69b0be972921e8cc8e92752dc35 0575d514a1a663cd180300b08a0b65411e3952cabbe253e031c9476d6a5aa701 691045b9528b6bb122428554e96e0075518dcebfa22a8b8a715b1b1f8c42e680 fa781545b42529ff982b3db0f7a9942db59625cce28f3a4d62f89a2453feb4fe adc6da5be19cb601570eec9bd0abfc96c4ebbf78817d1cb0db62eed149c8accd 58c5bbb8916a1c4afd554c9aa3f9e57e4f9f6d4549f63a272e13368e9f7b2012 39241ac76b27f42c82b883c2946a21e3007a1f3ac3a86dca947c97390eb54df0 7f5d865d18feec6339d65f2d5e7895fbf98a72b5862acb9b335a5f980b798035 5b573e428e769cdd502599834f4c9678d0ba778f2491ac93694c910d7380b7e4 0587ec65b97e32df5778abef65fe82724a65833c2199607f86b8b950a68e1d5c bd9ada8225857babb7f13ef19f94374a424e0c1df1aaa8aa2be5a66bb9a20c0d 0d4ceac3757929380bb136a699c81b9a973b8106a999ac18b4223f8ae64574ef 74fec52351432cc70039a1e806a059f3bba8fd39d27d3ee8f5e934645b9f522e d9d4cf99213cbce323259af81e292af7e61b2a39040b07b1ab6285ba5b75af86 001aab4d6672c83f999221352030479fea2842b222b17991987478242ad3d20c271a74352cbc1e9b5775011a67617d12c9faf2b59dcba3174ff7b6351f70420319412ca257335169fd8eaf237c48895b55f8c5f6016810835f8e8e788144230eb5f0b99a9c482879e28effa104d5e8ad371291fcede8cb6f04a1bc11f4f9a90d05e11376f0327b86b3bd1a4036c3ed4426d28f8d841629c5ecd434d6dc295107d099b40819789883e11fa8dd98223b59615dda6d9a50b2b54a93017da3af8907cf1bdeccf2a7f0463e33d3279f1809390fb88c52f721cc72d52af75f7e167f0a1c2f852eae3d00bba40d2734b02c34371ad37e5f758e2ac240f7dbcbb58bb007f392d62f8bc5b3e20fdcc82d0b88746e866b14235872ce8f28af2c7ee22d0b070b0ae62c3ce1bb902aa9c585ba261dd32547cc55b7cf633e49d3cefa722db60d53df68a181bf8ad0507e2231b514a3b00791fbe53e866332449816114a442d01ca8a6c8d6e5f1a5822073469f9ab6902ad563f0b32ebc11812e556f708b2ae080b13303426a580dd25da986cb1894a1a3311d16ec4a117065522fe7645c678073768cffa7b242cecb4255bf3a9c51024a3bf812a5576292c579f78b1d7de4709fd2654dfe6f9faab2265f15d0e51d71c1592bf9c9b97a1c0a30cec3bcf9865033ea6e3de6e7ad03f66aa98a3d7f1cba95b4a5642aa67a5431a721c1b133acd088c8789fef248d16c2442c5cbc7a80570d31c7ba10f4440463deae933073e1c0c4b6814cfdfc51fde92b6991ea047ceb918dfebecb5335743b738ea680cbae1007b1fa2c852022e48f348415c880d57f1d3f2bf96a6093eccb6270076e50d300061c7483886d8e9208959a2006a2ffeed274323fafd44a7f1de211d222ac7210bf94c7a5a71be619972f1ad5bea39148d647661f2a6824706e998f7e2f63fae0f2138394ecbf5ee9369758a84115f3750e85d3021e834ade1ef5a8e481be4b20ee9a4932de941bb3b941aae368e1a62d024c5f71c8f37f339575772ade4932309079ace5019cb518d7911681547327ae0f731fd1c1ee02390e5676789be72f707bd35a3bff352042a11a4e09efa1de5d10425d046bf807ba285de37c0e28b8d0a011ab1943d332d48d74ca00e902529d4424a795d57d6535ce2e6e0a3fe17bd04bc8c613b30c7409134e12a22cdd5a0ff742bf12a16666bb0adc25bb2bf479d0be27ce24a4ba66c26e1debfd8fc24e798408f97f09a6a52a2da31bbc4b090bf04c330a3b9201171703074cf9678578a33aedcf524e5276ec40e887c8414f63203a86f66081616c592ac55359b8eb487b1fd0eb3c925f74670e7c4b19b41642a0f6538a3ba09aa8916cb10e25bf9604d511c219e63f0c3ce6955a55320482c08006d4a9a726b195b27ddc96bfdfc8667d08485377eb3b1204e62426e8656b805032c911687afb221154b3c8f7d9801e009db2c2cbab38e3d1314147b2bdaab2b0cffcec57966ffa265853ff0eb4d8eb086fc6bf4a6c321b41f81afa45bbadb210ec210d31bf18d7bfb14cd13134510f25ef6c3c5c3d771c09a6ff1d716551c410bd6954e0d5b6c4f451b5e7981fac763ce3d2fbf7159001f734ad104c6dd0c4503e2b2db6f649b8e6cbab4af8c3154eca4ccd84de1be2aceace7f169a3e1752404a00fcc794b55a484cd483fe86fbc7c06b0ad454631ee26f9eca8cc6f362c300897ce6479408b668279542710236a01b1941a085636c2c7aace4006f09eb7320d8772080208f1ad90aef057c26890ca85fd25f509da3e0ec936729b38cbc065034bd53af94f38140344aeb63e8cd4690ff968439d04bd63112fc770594e5f0c066c657624ea69b677d5791ba9feeb405b9548849c587826f0ebe60880a78ea009ce4d0f39d441394d8b5c92e8f39bd0d216087fa830161516c9156ef2e031ba00225538832c6b245776057676e95898e6e2d05c930412b623a6e649bde5916d0c0deeed6aa87006f0c95b51b34a06888b13a7ad9495aa13e2079b63ff7c1d2801707b26fee616ebb6e4d08341e63cbf0dc323a9b5219152a26ad34c7a505def01e34fe34a3c87aa011da8a421c865268daca3cf129ffb67b44b71e3c3d68d6d0234966de6106ad7cf60b75b64443ac97d88e6b1bff2890c72a9a6cdcd14393501f77724c0ba49ab598ffdc3658099c2b0a0e866effb11bc01622307e4e6a9520aa57e99b5efc15060c1b1dd12802565ddf7a33ada8e4412da97e4e18c7362b00040b6a9d112c7061e4914a49d47a8c894960b091bfe1d3e664809627aad209605fb6fb05ca60e83f59ecfef9aadc9ea4b920a1b37b63e31b6b33109a100afe1019bddd250f29705f4d3e6d9639bc6476efb82facbca4ca4cec5c52524c35b580934dfa80cad3476b4cfdd7af54648f817d33e56e19d0ab10ba41f88caf2d3550319e746aa2c132bf4910ad3bcb104251fb2b235963cdae1b941ebc37bdeba340dc751ca222c49d642bcf17525515160345254d906f096b7c2ced826f46fff740a782e7fa9b629474be72129de52e837ee227e68296f4033398163afc012651f07cbccf5dc9a2e410e68e7bae4bdef26c3fc88762b157e66be1e34eaea81e98001427fbba849354d5ee2ef636d635a2475e0e3c2209d6b993a592a88377b916601814de9a177d3122093ad0cd82240f5166cb33df46fb76c00a5ff8ec95501260a4089408da6fdc5014d2a75c19ea1d8ace373a409d131f5a1d3779ea856d295faf1c4e4d228aba4117f8baca4c319c99ab548bef1bdf2d1258092a044975fd90496a54c23b9c42e4525485cc1de26c8c8b6e1b8d25ec420ff0e94174d8bdd4b0dea528bd696e531b0220d6de39a910d3c10d68575177285f8942c595d2a96f207e1d21214ec43fddfbcffec738b9c553249b8b9ac6b8dba13d79b7f7e1609a705162313fc48698a818d807584a6716897f9fb329e2b8cea3fbd6c1d7688beb3016fb824beb7f559121305b0b5b3f33aa2735eaa50a167f4422ba70ae4e7776804c21ce7a4503166f2ddf6d0132cbfc08a2206cf7ad0110d2488bb18499d60700623f85dc2b65bf5d920bf1416df15f81a12082a24060113105065c01abd30fb035da0443061c5865dfb4703d373234327a5fab48c238201ff8235f3cbdaee1109373d51b31345d81de16d75bf1dc44cbc980a4fa7688d65e5019177737d556804bbeb8acaed4096d23ff0778ef87441b3c91b676c2d50ee163c4aea6bf3a7af0658245f5947cbe6b1d8649c2dc2623da8a20f035c0a8524eaf717170d6eb1a400a953bd97a3e4d780b532ff07759d51e636381467be8ca971365ebfffe6d301020d3b130404fe1874e20cdb6a728f91bc415fc5313c53f296771430cd91a6b10b7bdd7bdc5a50a1bfc19c2f3c265b47939629c97132341a38a3edab2382729907f83150638a78a947da7a9a9ea5380f61a5282588eb73848ec18c2a528fa3080917eb678d8c674e768257471146c52e6937f56c19fbb3bf5015651235845d350ef7497b3610a29b3eedec94e590ade2f6d3398e559a0845c2b3c891c3fb57410a20af47aaa28823d56d6b7bba7b432e10a7ce46a4f0edced3654bd9a8a6a7860d88776f7cf8a7abecc0d07192a82bd4bd2bca879ef7b8f9d7675822f61f229d0eb732159592d716391cde948b2af8ea2bb3c3f64c572c641deaee9146b7cff403acf2d288a942df8ec2ed109ad0d8a72afa5c9be35b9f434483f2b4b9b551cb096bc1b8a70fa086c64ac0f08339edfb9ecd4ce04a7c74bd9d2e50659c9447410f00b7679b5c4828d42a1c7c2481e0aeab0e1f8c1af89e5750f2397245db29510ae258afcd03d1da43ceda4c35762d44c90dd6e41776afb035e1f69639595446064483b9a608e9d4867934f025ae6b1d98c1aff5af45883b27f6a24a33196d377108378199ecad44862235e5160f8caa960ff3a6cdbfb00789985c565a1271880f57319de7a7aa2dd95293fb1fee7eace99e59d92c2fd54fcc9771625ecd1f8f08d19506fe158653dbc9e973dcfc7c1b3e2d289b0a0ebe24e4836e45966878bb08bb8989e6cc1777c316a85f0d3f21d72ce3b03c603f446f2fb89666b4a8c501068d04aec2cdeeb2e1319160cb96fb5e8d9fd62901b1cc44e125e66764cabf2a01b94331640f0ae3e3d8b15c1144c3f8407111d02b4573437bd47ed6ecc0febb083f2b3990f07afef84bb16315d9e3a2bf331860d6b62151e2584cbf84058312058e1338ed6b45a8a86917f239e0685554fae5b94bf3f4befcf5f61a7c2fa6f7039c5d4d001e1e0d19e46a0c9e107be781809aa6a9b81e941728747461ea895f0f3cfe86473b9a4ec983bde2a4a9b20558a259f5103e11164185193e727cfc25008417dcb8f57cc271f68f2847a7bcec6ca2c2cc80729ad3e7edc87e3a42bd0d008306df36968c9d438fb6e25b37949fdccc8541d31b1ff6c90a59f052b1b5fb0c6bfccaa49ff4ef2aa705088b66531aaaeefeb8d7c37c4cf03ae810a145d72c01370a9949f23ae1db538128ee4437c10411232d2b2d0015b3136625dd0450cf08bff47ea523ae5e776976c41cb0ab995cfe2a891df4138263af9da94490b9c002920bb77b1c8ce539c968c2e808eb8229c04b957ddffdd7ebc24e68298fe2e30af144d0e65bef8e00915d3c4ed6e0a349e54f018c3c35004e3914f4eb0efa1f0dac27a357080a3c43b27c6ccb3c8c442f470445ea6980139918282dd9d232cb0bbeffd27b4086d981c10f72f97c13d8688b8fd61baef191f6ab1ddbfd40e41d0600532616d2537f0b0ee36a3d80257d330a6080ded5106d99228969a83ce28a0c2ea8c79ef57a7bb0b3d4aaf161cd3b23625e6136044acfbebbc5ebfe1ddba70409eea18e65d94fcbd066bbf336998c1c528fdab197f0d9918dfd4a9b4520780ed0518b787a0fb550dd4be0fb1e78ae04b0dd2ecbf1e75ce6085d5057ec71680c492d1bd16ca07718c60d8b9eb88e4a1e4c8340751251f98e614775da86af1c0f3a12e79674d4226b216661054666a4cc151b9bdd78e46515140cb7c357b1e006 false -check_ring_signature 5a74e5137be64a71609a18283430ec626f918e8dcc6817e4cf9ae59089f56113 879499a6ae5750eb6e56005330d2d0a769e67b66de06a274bb84b75d5dc52e1b 4 5485e31e4b9f4d17efcfb36ed93102c2ab2f7aec79dcb383543f4bd5bbe2bf55 392d383236a3e10ec072eb5d658bccd718c73f86fd0e2535b0abf5f74c2ba5a6 955404f655439ad9f8601300749ee0385ad5a08315472624ef8e94bb3ba70d8b c9dbe9925bd51a45dec1db26275a653149104474de360bf596c2b10d667fb10b 0f9f8d6e9e8c2e4c8bc4c9364e3c136ac19a2b1a9b5450c3c2439cd86540bebf7c614f689d07014ae73a4cef3ab73c2a847e6091264e8ad97c3b88f133df23b737b00d764dec41c259cd96a746a16d06e33adc72f32e69d5905394179d37a60d03aa45f0938c0b84254536f1c8ab48d2852f09af5a44259c03101ff6497359f18c0a96aa2f864eef993092124e7e72589ca49b0d3ede994d46f9eaca71f1810f3681d91e24895573aabf3c26159db25db0609b4836793a699690e6d0d04a5f0396ff9a309e6ffb399ba6124358a3c553af62dfe40e5140c30c04c2d02a603600959cb702d6884b9e4ee1a7e6a55a405755c7cf66b55902094e21633d0ad8f402 false -check_ring_signature 416447b615d74041587c83c7bcd22170a5b75982bf8131c67e4dafd9cd4ffeb0 17e271485baaa53d44a201447f85e50d6c0fcc9135ca8676ce7afbba7d4aa5b3 4 307ebb2882dd25e7169f4d5b2f36e97dfc6dcd6ba613b275594140edaa912745 4bc97c8fa2632140ab3f24fcb8ed2b6421d67a746642fbe45d8a76d8c1b34d00 26d538a9cb110a3146655cc9a5cdc8f299f04872f1f0882c4d2b5436b0a6b722 b0f622cdcef8a16f8110d5a47799be6a1193a7f254689e157f75b47a1147c7c1 1e1d5d6e5ac64c6effdedadbf7fa80c7c969a640101ceff41ca550949564460ee4caf0b4ec13eede4642580ac00dc45241e8423ad684993f7efb231cc4431a0cb497aaa29a5c67fceed6ecbea35dbb80c177d587b5837315224df3014864210d15981cac99a37ca7af65021c4021952e328ebde9b13de2baff2a135ef35df706a2b3f67caea4248e45ba8bfdf6e8b04394db686b7b273938e989c3616cee09095fb733467b128d0ef10be40ef1f5aeaacacd373d833d735fa2d790e48cbb1e3452f0717e19dd95f8aaa741342f245de74abf94833eb0c26851f62eee9df1b1047e10c15801fb644c024fa37d8b5fc13811e4c5e6bf4dc88b5d23dce89144280d false -check_ring_signature f079f6209b34e51942ed166402bd142865450cc2f71e458791dcb7b13f5a8c8b 68a49e33e5aa0068febdc4d71943dc66fdacfcbfcd48591ba99588f25b88112b 2 9cba43da3312bf96b001798a6ddb377cdd0bd08bb0431348ae2153e3551a5c5b c7b82608ae3bf5d2977d0643b063b388e031030f7891eecd857cc335cc240410 4f43383f2ac01bdf60380da784cd7a5087807fd75914476cbced0d49af03f40284e332ee7ad9410cddfeffa78272c961f48d465ed0f225956e025640c3738b03a6d879c748264b71d95d5dccd581b09f287bdc715ed838b4ced3d4b59f95c20adbed4f2c55cc5af6b441d249e457919fe8fd63a60d47c3417f1b699495c8b40d false -check_ring_signature fcd5619225e091849d87754fe43abac5817bc05636f46bcbe16985de52f5c86b 5795066b848ea5dbe2f19cda70ea33fb71e1f542d3772f2605a2a24eea1b3342 2 6564fd1366169df2afedf4b93329d698042afb2d9aba22ad50c84406ecc9c791 a6a9ddf03310544409d8801f932b0827a93852d62fe5c5db926aa1e2d8b83dc1 1026b671a6ea82615ae17c6e3826838f6a0e0eeb061721931365eae44e95a40f3d193c88e86323407bf056bdcb4c42c1116674e1864266ad62a432bfd22f760eb156e6989a83fd7d1c34ea58c71c14f86c0fa84b66a45a45956f503316c5f50c8123517147499c8850a8ce387d9db0c38d0dce8e4f697dcae39c86d53f3dd708 true -check_ring_signature b64c8c0b526fd3d00b1107148ffdadbf1db04c9858d4a8dce37d941dfff88e53 dbc2f47a2a84f00a84b44400d4bd7a4371f9410dbe85173e39129d5ae7c93ab4 47 7246efc7036251bf59bb78ea4b5ad2332094e4828d13d24b762d5f79ff2f43a5 16e42e91ad9353ee6813a081d2e558d250866009fdb427dd072aeae745f982e6 73254cfc55e04dab34f02e02dbbcf92b480d4cefa1f6e2ed88a10bc2189a39d6 5dc92fb547acb67b963c2edcca3cd3681758268a359796bf63bec5601158645e 42d761626c20c299d4fa4178d88a64df665bfabcc991e5ccd62cd3b3c13cfb41 194c944989211a06a5e85300675a9d254209a0cc38e4301d66d9e559d4a9b687 37671c338af0e07a9a234af3f0c0a692d93363d7f6d5ce65716b744258e962ac d87bf44222625b092df38a8afe3f91dea09730a18357a238766c3db0bba04ca9 8187c37b0054ab9ccbbbc032f3c70466b5818b1862fe8e41d3b0d65b3419a298 31df4d13f7c6d99ecf314064205ef56327793436da9a0574300e413e4f3b5b88 1d54cba4ab9018effd2cc1686744e72e2e74680a5c0365cd8397c00e39fd3af2 d3a3625f5528a423ff5810124806f5ef7f0d9b82f69422fd070a884f69058d2e 5afac695a6258e1815928189838b712772bdf8dccae63b1dbd8f714e4cf5e761 86b091303818a88aef1d06a5749cabc7cfadc871dac827f8e4f77cf43520995f 7a630733245568575071253b1a36c15e12615fb66cf4ae4719e1508f01b34772 693e927bc99482c2ff26ac56a29398c9da3d3ab53f060c688349b9f9e724b648 d6ed246c7a92290318475bd82a6ff2ec0ab3889b75133b90f89b8820926fa50c 9ac3e2b8269a92c436234c1ab564b3bb1c2317df06646fd550015f34436d89d5 c6336cac954542a1bded68a691770b136030dde116288f1826164fed14606653 94662197b1d245771edd2528ad303c784e4fbf1f5e3a963e11af8372df52b105 ee60e2d18ac31580906839f3d1a47a99b7213d97ba443a7e3ddd69386ef2ccf8 f2290c3f37828437f114e79006189be6eae57d3901f4ff80eee8c095e08e2808 ced01f0b05275199b4d4edbecf8979e135577080532f3c54b0d8e2d306c68532 f8a2609dfc62431e17fb6b6469f743b7b949987dba461cefcf64ea3be34ca213 102410a9fee1e66600a8c8f0098bcb351d4df0fb0cc56ef5bba2bac0a09bfa5d 7036744b1970e3a4485995a739d032dc7835009641c035f3afea6ec652e627c6 4adc80e081c2b4283206682b5c4906c57e904b45ab275a3ff59da88c4ca246a5 092e478abfa1dc949edde7986b9c739e221bcf52f56ed1f9d1dd58cfddf7f68a 56ea63a1faa9819461633b08e26ca22c489c48ba47d29dae99c7986bc84f1087 a5f0e8b17afe456fdca89cc2f49f041494a57ad3175d1c67983a7ff1adc28991 c519d88352a0e94915288df367d91db802ecf85aa7f68bd0b09e09f2e5540be5 c3a474db2ec4435867bbd2e5ab817aff5cf7bac74a63333c4992d71a3315e4e3 e1a080b2f0877dbd80ec2ef9593047e8007c9ab8ecc34e633313b0c3a4f95319 da80de2766b31b4cd2268b82fa5bd6decff58e4c041e729f105a74c1ec30d956 671d6ea27904bf48178f78f0638744a06139afb67b8513ebe75be5597e278910 74374abc3f371937efccf41bce7b03f36cb1f69800a978a244b1962510429332 df9ef2563561eabf19520590fe10214f3266b627b29916018d76f1ea1c19e664 3fcf1804e24a5f244d8486720c394ab96a7e0b49a77a4b804c74d21df1b53335 8bdf390c3b333ffe9f40d76fe58b62066966436018cecdbd0e6b8bab8e7035e6 f33f7363fca8ac00dbf3a4e57136e4ef7bea767a1962e7068aaf49e3fb033df1 0264316e8abaca6c94496b77365a795b896e402cf490215eb3697819af78b30e 3e2444b799ef361d871986f320e2abd3e713e0239f7e2c0e85b7399768bf7338 3f0ac50eed0da7d6b32213c7d2251f4e65f225656e202bf1dd059bfd9f0058fe c00b656255ce74e5183757cc36b3cf7fdb2b51e443ad2452d32a4ede74586820 5c62c3a00b362c0ab7310605550ece9c0a1fe32799e3649cc533f15ed2c431fb 2539202af4e5dd64fcab4630b3ddd16a1359e3d19da47334e258b9acb8c24eb3 d6183f0adbcd02d9b8824ee0ce5e58f5dca8876a2bb4d3e5bf53754ac7c3e1a2 606400fd2b4bf0e3eefb261f2de610e38a4abaa5dd606ffca211a09a57981d07bcd5cf99939587966bd91c57121a40c526dbf85b69981e387c532cd4618e1b0e736cd6e99dff82b633cb03b3813dfb715336a62f1635088c47c967efa63de9099cf6d7cbf9bc6b668aa8ee38fe45f000cd482e6f5e2a8f6b7e93644bf278350ac2aec70f2cfc7d1386329660b90e5f9a94ea68b5f9975a8bebeef5a6553a6d0b6d1307e80f818babc0e4b4e336e3b2c81d5c5aa76eea1a9a34835fd337a8b500be901bc28504a21de3f5afc9d3ba57bfc0b61745cc9375dfc66927383ca5c204dfaae8f8871ce199cfeb0e32cb95c0915e1ce178ea33edd1f5bdc409c6a77408308561446aa25ed840ab7b1636d2ce60d392429a0ea54588e142dfc4a3f2420225f83d77e2700f2a5abed004a6f128076c4c7e93eb1a9cef3efd5f4e13f1c30e93565e49a318c848f2c312a63b27d8a6ad565daf89748cbbee75b3081445f3069d281ba80c160824cedd7bdaf3a091733c87880d677fcaba42614b86cc30a10eca0b613b2a62638893a1ecd2bf0e635cf34d5225926687c1526ca4510ee00006ebdb525d6cf5bcf3c7686dc6d9d679ea2dd1bd93a33d7dfefc44afe007fd0a0107de87bc96d248f7336a18e0839c52f88c6eef38a14c5a4f7ab5deccf5eb0609f94951ba4ccdb5e4970dc487bc94215b62cd7465d969834d14b744450af07b039824671a6a530c98c14075589bbdc15e72dc09d62f6454d50375105842b35f0506fd982219d73cb2e22fd9c950f4fedc215d86dd58e2ba213962d1d000b2a40350664b89ad5ee2f1909729c7f370b903c80e8beb059680c21487664ba820c70a85835c1f5f5d3df2098b84c7ee3ea734474145feca9c5069283582d3ea2de00f5e1eb778e3b8f83b361fd4edf8b6d81e3b04d92aa1b0d11bf53d0d23b7adf60f1fb9a28cfc04988aaa8c5f4e602115a0074788f10012f87c1e7e0eb88da8140c072b66fcd607310211639fc6f26580a894f44209422a72d6d5acda794deb2802404d2e60da54eb6caf42417942c31d671a696cdd0adba4657113872d0088d8001b87511e8e2192198678b580567f9929689ec7241e2532ff74c487ac27270503bc040a52394b0b821cd313b3b28c9d31af5c985aa6f810c5de449e4d447cad0f84616c979ecbcc619d3b4d92a3523668d0ab86d9d3807219baa76ca168f7980e1cdf0c474216acf467a334a2b166041b6033ac3b99d03b980b73d5bf7b32420b04f5619649699173ef09392721686af460d618ec30862a4b1740b2b2a7a1650e4525c1c9e1b861a7e92659f2c7c8d8d64446a1f16a534013c4a7c2a4d195fc0ad703896f5602a031753e19546e572f7e8714be0ec37b4f0ce802d0a52a27b40f9093183fa6ff19e8d4a715aec08bb0fed6ab615a78aefb854ef70e6177d3080f602a113e6f78a06be33f0334257346dcc59ca8c85281997d983382333ecb4806b5b20fd69801b85e8a66ad9d875f2b33958f2b8aff63330dcecffcb3a531040c37a7c695b49718321587ceea7aa65e5463b8eae179d223ea37865d4970ef1609e375486b39199f0a5714431d4d826e7bc5d2a899cec40cc1efbef6b29d76b80fda0487643054977b6544ca96dce721d9d8cf922f9c1b95acc7de9a9d8b388e05e3c553229b2cf57dd18f24f65883b37cacccf5b3b89696c0b627983f110bc4074082eb07ebf4cfe79b76e10292ec12417e0be2a7f7b447ffa592532b88abfa0a44ef5f94931b6795eeae70db001ac78c8f934e84acf6b967f19c86936ff0ac00449c5533131291ac6d052f8eff90dcae43979a0572c0c5cdb218ed963393bc0e29c3e776015e457ef683b12c27775126db99b27b384130741f96d6b2c119660c833c593a3858b7a43bb7646a176b20b9bd90d2c48db1736cff9cab97bae58e02d8101e55423816f9529ed9061bc11b45b851f17818eaff21b6bd7c5724219d0d6be611daaad3dcb9feb0054d30faa5c20264b19bd7b2883e6c0dc9bbee2ae80ee8e59e1c5e53f285a13a6d912cc8352ad62d2581c843674ed5a600b18ec10e0135b83e7acce793cf9ce0717be0c1186fc35b976be0d9b8cceff05c55ff5840052e98982f8deaecc5800316a2cb97d78e9ccbf9ada91f62bde8e58b431f7f0d065fc229ed3f5e1635e9b8a706a2aa670d1c79ab8d3c6a4da95ed2d1af64126f01b31bccc325c0dfc52eee61848ece310161f305b857dad3ccc8aaa214f6acf70e33659366b139ea590592421452ca85b73787ca208e861ee0cfeca232279bcc025a6c23eaa56bdc6d81b49f85586842d4d1eb6c500c03b890772320f6834e6100f5be9dd186ae2cac2469982d7188c6cc3a0c84ca5dd4ac181a2e76eaa348f3040856e23d9191a2777e7569676e3685180664441712e92f14517b15389af74705b7a802598933334605395b17fc2ac8d9c6dbf90aa5b536442e44fe765e4cbee853de14f32ecc8c9394cb0aab3f66fae87415bb018635757a6b1155233e34dc0a95773a722f094120b1083fe08ef6c5c9883f9da56949b9f3b3fa689b6a209c00f8848c8f40d23858b7938999a354868392f407f1d09676f472b2989134373506a2f6b05985eb6f139d2641420151b767043f2223f6fee8bd1e2ffc4b1e319204c2fb5d91a864c21d56446e58adc22763cd7b8b56fb6889c512600d32c72c9372dba40d3c5fbccb0a94c21e5287fe6f604f8610a9f6044a8b6a03a14ddc8b910b73e6c279439e5b5b90e9c904b435906bf58a46590a230363242ffefd08d4a0f5bb35a7d0bd947fe629e95a05f7df0cee38fa5686b47f501b209e8de5b2771c0ddb474e9e6071b373b4a5cdd6074bd4f19e34f5023a3e769bdd8509fc323c020c3039ac3b50a328c8ff2566c2071e7286eb57793ecbee5586b21f58a9c1f51e09d4a9d6a66fb4b326e9470afe3b108f8c477a3d3e2c681bb8de5a7ddf5ee0ab0f4ec6808628b96c93b4fd14bfb77aa3c02c8f4d6ee4c3cf953b923524d30fad08a41de56cb1bfd1aa19a2c0a84c256bb2ca0c124901ba09d454b5ab8a5da01303d124a09987d19ce77b125b8bbfb08f70ec06bf8fda67b2065131346e41401903c6c6bd2f4a2ea95e96e05d981f876f0dbc6137fd703e698f9231231f7054be037406aa202d7306072d4863c417c791a8c61a52f0a8bd6d6ac0e7dcf6a6ddaf03436c138a5061159f0bfd78f86dc7042182e60f7de93b4510457e8eac27e22201c33f08d2dc28900c58887036f015d9c3e9ab9298116d0c5425a645988e1ee20edd7a9c10a956e13c32db39fe5ae83ea1a9761f82e8d18f3d44e65e4b269ee709f6e8197940aca8e4354678b495d89d700efa36fb340590a5d223604ea39c8b050146554f9caeea8bc5d0398def8a72997247d2cd5fe27fc766e4456a06d29404c5db325625c0a4b631b9776cfb3eabd06a9036493ca32b452987b63d1fd54d0069cadf73efcf44acb19ae15063a305d08ed3582a14e7796e8276fd1e0af97b0c828fda9314c0482af48c1808d81d84bea715ba5627ef9e8eb33a33d4f281d20e2071c7b2f516e87eaa0f20cdef6b3348cb5c5c824d92738cb4cb54943389060679102602287fa4965cbb4a73fff46a71c6639f38963f791914fb27754d90570bc8ea3cdc6474c1650ce99f5a4a1da9cb028a20ec38a6353fbea6914c2811ae05d292be94c2ce95fd2a1011d52030a4579ecd8e49d0302e98b40bb6523112940f19e3be419aecd93e5c29e4fa81d4ca98bb3c1d177d864efe85a6872f29459f0824031557052762d83b0be2a780b1a7ba2badfef91f9e97671e6870e006d5880a3e5af25a0e8185384a5b50ce17146df18651d51d7b9e4b8326ebce536d2ba60684cfe0800d40cfb4f0cff342e8ff01dbc3717981c655b6f3cd50255db8a57704b46b89b6d058855257eaa0eac41cfab9d7c5df7319ec014050bdcffabad92a03847383859e63034d4ef86d2cca3288e0cfde92c00a2bbc5e5f78503ce45118057a5e133b5e46773c6eff728aff545241015e3d47d0a6cc5bd21be20cf594a1074b0fb747018fe5cd96348d112a8a64287665760cee6a37a2a98da844c1445c01f42fd60bb97fad2a2f3ee94427f426e74b73c8af1c62a21dab1bb0dc1addcc01f1e4a2510bc67a4fb0b6986d32a5709f49a2a3d8fea25eb4a2a75b1198974a0c3e51d46486cc70df2c61b2e84867bc218b7caddff7df712215bf5ff81039b30e false -check_ring_signature ef2ef2789c3411f400106dce54ac25b8d381d8e70ea165234fc9216d664190b2 0b6f960f3a615debb9814e8030675d4fce622f7bcc2a6f6386b96effeff89dd9 37 94a5d42c4fb62b1b55ec7ab4128887b5b5c568fffbfb2b06c275a1a1b4465d5a f35c26053bbe35631a2920a68424991aae4157e6a8441b331da42fa4407216ad d0cd249b0ddf98112f15fe8123fc565d41e1c7a2f538933d2c33f64074f2be84 8142adfea19c6cbddd6bcc24024fdea70313b4c556b94c962b8845597e4605a2 93b7e37a0cc6e6e9052914ffb80249d6322b377e5463636084274325a9f15e43 e92bdac610c9b1dcf43be982264d3dac4bc10dff109cb2975afc87fc3bb137cd 6a5d5113f1ec3979898918423d09e80d3ee98f48f821b963a5abd63de1ce2ded f44f2a54bc21f67655c80066e6f7ef1aec55eea195044a0b694619f9deb4b709 e55b07b147db8d27c9c46f595a2ab7b91306db8138f8a84937eb2a14df6a0ea8 d1c166f0e28277630a19f1dcde59fd4e3ef9ee0df1124a189cf0cc9fb2616791 18d78e373a2efa6e9c7c6f8191e6fb864271ea19b7331a1f112c87f465a96af1 9773dfa4cb9dd2bf3c75a9dea8a7ae46c127654ed3cf9f7e77b33366af29bc18 6327b452c0e9cb6681ef7007866d758d3fc741bcbebbb6c293e5c6e2f8bfb2af 21f2850dd4b722f9389af8a05caf3f37bfd14e2966673abdda2812bfb17f7bf4 a0fb5b11ec48e3535d1d52490d1fdf8715ac0a3021e4e19a73ee070814f12afd dc3bdc45153b06e34289b018c65e5a9d6f87aff9b449f3492c8ad0558c09ca9b 4abb82592795b8445efcbdd185dab0608cb40b9a1d44af9e89b5cc2a6c25fa8e bc1cd0f6cb6c3da98178db1fadf932651d47350c44bdf9fe7d9f99df1bb225bf 57ce61be3a1cc8727d2064a8e2d2df4ac2414684f570b25d1d618d50ee59bc71 70ebeca57163cae88d7a837652c829ff695387b4c7499b4127fa20a9fff6a989 a9c8e1ca0cd77a8ec1dfe0c1e93f6d1c1c529670da42e23a158cb6cde41ed23e 53656612b801ae542d916a48f3e4a2aced9a5dd81aff2843bdb912922b602bfd 1dee1983c43fe2dfba069176a08337e858474009cd0d9fb1957d050d8c7a91ac 38d3d22051fdc5046b4de6febdcdb422106f27b6e0118e388bc6d189927f6541 e268cdb5db5d2cd030768ded09b9510064298a60fea651edbf33f79205268a27 e4ec9d168583c917dfd365542ae6fca08b31a1b920f363c813315243708dca53 de182ecaec171b4d5777d0832d8d83d1ff2ce7616b4e9a9e758e0aca29763253 d06e493089c971bd235b8339c8ad72de6abe1ebf64d296c615a713d5ca2cccef 875f842e43e5d3bf7ac932ce2b0007df9beb272a24ec073efa9d41fe964c7276 b430315a5677af4b962c710ad2a6b2d2e685ec4b3a24e51121dcee9755e0bdc4 992d20d4592298e58ea5212f89ace7dbf78f6ccc72c112a8edfdd2979d69411e 1224d434c9c85f3d84718c53a4d7d3b884bc8c53925a5ae50667c87e5b791e7f 04a9573b57e73d3f2747f430ba54c210586e1f5d39773e6a29c0d05fb26aeb90 1e1e686dd7ef998ed1c50b964287adf5cbe59b89476357b5f0169b1339c44bd2 6d46da10d45898e8d0dd2a4e8de1093793404b9f2e875f3f1c066cb5fb73140b 82de964edc7ab1f0f0a0270b8b7624672bc30d671225551db84bb10bb4624f36 9238e1fa4b18d7e3810a6603e86204391553812513daaf2a0217b06f66239877 26f5834581befe23bb0f7b48ff0a463fe4090245030d466c4d80b5161566bd0e36fad36b11135d785dec682f10b6f8212d2a44ae404fa8a02d9d873a02f57d03212a07b2520581c85762dfee63f4ac47f337db1773bf51ee1193f80567e4040f3bcdb90b6e43744cd87fbc8f10434be876da4e7603c712adeedeb23a4afaff0c1fde3378dd8caa412cabf40c5b6b7914bf83451f3d45a369d0a168d4dc75bc07701c7905d96c4a69bdf8d74d481941aea9d34dea3f13be692465d5a606db8109c8be02ade90a02c9eb9afd048bd1721f6b58d16270e7e0d61afa0e44e8ad0d0160d21ce7613c7ad7481587bfd2a833ca47942314314a5713fdfa23531a911f0cb5d6cf5e718d6f5325f7b5229a1402ba99a02362f63b1994d97be10b94e4a509b20da49d43e9d9af866e259bb216bd107a8dc6b538082adb0c3cdb8e8c162e0efb338654bfecfd0840d7f0836e1386e8781cbafb8085e8c70f0dd7828f08c000b0f331ed182d5ca90904729de348abf5f77cd94ef796133a8fadddd2ca406803882460a2833ef44b459e6ca94cc73032e920c352b96310751630b1e2346782009f9bb08fc11d4ebf792970937da477c1cf5cc0dd086d4fd46e5ab16bea022d0d28e903eb9f76b1f86046113febcb4bc6dd2d637e1b13eaae2073c4190a929605880ce7121049ba0bea74e30d07813fa8cdbcf45bdff9292f9039578f959a6a0afd8600bff9d40e33489161160c32e225b0003ba6867c219808da2b0037b25b0dd365af4ba6841d90e3ca9a05886c624fa43e4f20386f0e6ba11ff737a6915a01c72a5298347021cdf9e5cd6ec785a4c8d597213b9228cea4bfb0944c4a3fb906751023b33ce8eb2695ac3308ddf37ade2a9130e7ccee114f9444c74f1b6856049e9b3de6c751d4f2dbf14c2b48f754ff5b17869dbfdcf542769e6801bc24db0c7c2bee052e1f359517158fb47cd47d6f62e7cf95c090fb7a5b17fd1cc3be000032b1df1cdc4fd66b63d145c6a9d0ae520b42fdf55b5d93d6097f4e2c62d7b203d42f5d75ed0cff5cf5459930e1b1c653e88ce2178917f6c06b62b80778ccba0f7d2b56411d28e8eefd5456def7f055825c0c9edb0e4678f3f4ac8b1893d9c20db1fe1eee7323043e576cb20b8d2de224f00a7ff3487c625c81e9ef54b48287037db0d7dab7267b3c6ed8c41d085ed52492681f51f2b84a040eb14704db6c7507bf45e4c06d760968d96273a944ee43a8171f17305d6b1f3a6e7badc2aa7fe70ad6704b690c9628c62ed4ca2ec24fd77ef1fadf12cdf587483b8ab45a734d4d0a2d285aedd359069c4fe8847415306a9d762809d8934c1651c7c2ceeac49ce505c59b2f75e56cee924104c5e4026e6602dad8cbd5a06e8440d78d3357a3348607a315dc97fbbe61e7ef806345b86215f115278d5499ad2967c84287fe4a427002eb0e8afa04ae7cae12ab696a6544d5218db02f5a200e3a99fed5226551ac580b01ec5bea9244ab869af40d67fce81da8aadef278a0d9c86ac285d42335935f050123a163aeab8765d94941778a733d1d37f5df177c175737ddcd64065c0ca70b7cc833b29fd9c4d811c06ab4cae8a7654f6c841adb8064dc5b5e26864903de03a78a9a36ba4dea70f27b3edcfef8aa98724de6939bf5b594ab40c77e75774d0e4fb0c5a7d8c27329048a18588103ec951e3b80170d6f425de2c244842821460ea14e36de8d9c78004befb36568ad17f6018f8f619e72fdc578d9466d49e8430b4c754801d66bca7ab513494ba38d16d924f7359032ec8ca326997e84c183e00571ead444622e95cf18df3303c3b2e840d837bc4cf31c36e4a8bca69000485501f2027c73122e1ca52ff7593ae113dd214e0c574699c02e2cab7a89d59211fe0af040633c71726974396408f9dedfb21509c4d01ae2cf3ffc7f26d5e2e5947c0b0dcfd84545a36d635887bbeebfead3aa9cd1bbc2fee41b08ead44fc6ef7b3504d6ed6e024926316207544614339c74444d3e10963881b6893d68d87c87116c0982e74e3c40a1be50624163b7924a8fa0dbcae02459d1e94fe7b4cd0443da760ed9a96f8c3d509208d80f51d0f3c05d18cde98afa7cfdeabf907ba81eeeddc3062b13cf80af80a6e5dee516ed27db69a65d437363db1727e93af1d7ad54ff560caf1b8cd0483aacbec4268f1e2df4fa79fb1c5b17c29b7e1ea3458cca97161b05d7f59be372a29787fa221267126fb04612af7020794ec4114ceb8903c10f75042c71e5c1e69ec27941c0cb2ce4b64a22b3ebb12e44bf2765a63d9b520c4ccb0f070f35563ca99174ccd3a58be33d21d9d33c66829de06f87256b21a277194c0f28bd3a75aacb7c0b1bf0ba43bd7dd826c4aabf57b66062abe5e2a77d4bd81302d01559e2fdf32c4a4a9a137385433595e82ea71118e508f122bbdb4ec037c409310d63ad1994559db7ab0d4d3bdcb31c6afe732c6938ea0816d365c7ec32b90816c4f9317479c743c5654467e8873fff2aeaca6e2fdbbd5128ce0243fa0138065a9866df3b0e18ab18ef003167bce70f280ee5ec12bf9d5797f5f0555c0382024aef0dc0b2deff18278ac06e07380a27b2f6cd201ebfa9636dbe58e4d4bf360fcce8cae03a41831bdd067f0e8117bf915460f5d787ed694e39804dd8eb0fa40d8650e4a8f0172eba150e7dab0dd2ae2e60d35e0455768f5abec97c1f4c9f4f03a2d4d6a04d32e80d00e6f21aed0312d4d40514b72a48d53c728a011cdd39290dded6205cf9fbeb426cd35033bf4d9e0bba2a130b89640f163e4927b6dcfba805a757f9cd4c5f074c96270fa8804938e09e595edde4a0702b5a864cc16bd427031a49913c91b665edb6144bf2a303806e9f6cc185c2aa6c021a54648b189da80e695395795685e3ac8ffc20f6de2fa07917abdf5a1205b770eaca088a0e03ae0149482d7e67556f880e51e7f3178da0b27edd984df0830aca08969555a53e7400edcefa630ec3d3bc30777ebdba597854d3bedf1e9bf4b03e23dbae495bb34d01d5cab10cab55554d7578a6d39986746db3bc3eedf46487f2c3660fb954aa77058b73fa5989dd175aae361393871a04a3dd2ede30f67801fe4f2c41d2ac599604ac65c3bcb2206e08770b55614d9c9b93ad4ea8d9be415f31d82b8e20c886420309fe0b5cef23e0f153ca083f40519e813e14cf5f266387d17abc652ccd5af804e989a0cc754f4003586af3e756e9f7c4f25fcfc1bdae6ff441c2456e02f81d0371d3309b309fee8bc290e49bc9552e1e9478b032170398b57cdb3d2e47550506ac521e8df128139cab2341abec75e2b9dceb8a9b24a1176b80ee23b69bea7e04 true -check_ring_signature 07e0b77a81c9d0ef566a3cd7ded0fc19a334e6abea7fcc9c54a3fe0f95bc9877 39a9f896f176bc9c2bd473a72ddaa25d4238c0606ec4009b5222458333c892af 98 e620a165f18577520be77e4bd2cebbb174b13019bf307b5abeb2169e743623f4 bb9e30f7125887e71d97dfa98a97597b7786ec633029419a0e35cc8327aecefc 1c1d065649e7bbe2101bebafc8c3b731814dd6bc3743df89d6b9720cc34e9fe4 b795355040fced71226e407e9a10163219cb08f1fadb094a0fc0b5447551d7a6 8f5801e4e3951ff20de26c90c8dd0102a36aa1e51ed7900ea67c54bafab97110 c82407885765cae1c67a3bb8eaf26aae74490a591fa22a3260f5df6df88a53d3 534f00eaeacc0bca1ebdec65eeeb768ab1a93e50ba63541e839ab46c9845debb 8afcd54cbfc9abbeaa4e5e0b641642f8cb69bd49cf6642e88dca5cd25f77974c 0af2967464e048c64469cce88e57808fae42d72573fd5df4762a1b6b7de9be55 a771d15b2cc2614fcfd882b04d1964dc71826422620b21efbd655a478f2fcd9a be489a0f34728cf50bb4fd5b908737a4a4650eea4f64b198e74c6bbe1c4095dc 1801a14b84d4fbbb3d7efe44f867715c2b190abc6bce06a3fd2489e658756c67 ee7342043f8dd285241e25f49f2d48b11a193c8f6eef21e97c729006833383ec fc12abdd485b162e7308a328b4add880da72eb40fee745d9f14190d3586f0a84 2298501e4b82103b967f4f3949ea519eee34664a49e44c780b1d6767a01cf61f 6e5ccec6d88fc91f1b861ed693a70cad7646e539322e44806d8cbf4b530e0c24 181b58dde098bb7bdd70fdc43156b6d83a80b329d435b3b415aeff2263af127e f0e61de765317b14106500c7d34cb667c666b263f497137558519014ba231c8c ceb55912b41794bdfd0671d417d695c881700f4b023391296b67913ac72259a3 55270bbd2fdfcce81d45f551daa1182609d443f0256c768fdebaecb661ddf87c bd6a7cd055e3a448e47a939199599638434cb489771152a6f15ce4b812d539de 9476f699beab0d4cb6e324f0686b86e595be6fef895cfbea78cd65b68d39a2bd 817855b61bc00f7aca519912c5419740f72bc3e1a3d1e69d37f195e2672fb0b9 62e3adb144493344401a8b269b8cda2d609846691805d052f481ba705a334ee8 79830f99bb42486ddebeca917afe0214493788b650cd16c84c17ad78cd8ec646 967817e018084e34a320a0d7da9709231e8a7b2c48eb76247e75d895e4087121 d4c21932e6702d75678f0a8417b732598131ef727678afd8deb63805d532bcbd d41060e9d6e9567e3430a89c0ed5b1f748a7f736e356b418660f111ce41b5c00 47fbf33c9b06c3d65bf1991dfea4772c3bba94a7f384bcead40ed1c86268db27 a91b1dda91bc98a69533e5905308f338ff064da22d3244d88c1d0b1d006f068b 94d31b02cba0661b2611819d9ce0596dfc677edae4c8d8f9a47778e986ffb9e1 8b7ca82141c6bdfdb48f747864bbd6290adbeb3ae7f99fbb94d4ea249def8e48 a904b9c09f77a580c6e8e57223dc18c0463f9bb5be85dc65b03ff5334db809ad 526ddad2dea02f7b37bf266e59e6a495c4e2739900a0e95ed98e2c364248c3c3 fb657e3ea9b78fcd17395105e96ba5ec13f6a7369276edbbafe98057111a8d90 f1f5cdef6c852312b200b1b1f63530b4c629315efa055e7981395e18c45bb942 c077725bb00bdf3fdd4b49aa1b223a9ec3d4df64dd02a71d3c5fb07922c0e219 df07c7887e848d2ce9fc560f85167747a3021f063b57994bcbba600334596b9c 192007efc3d5a1a22bc8f3960b7870c364750157216432b96837c2f97327739a 35f76d80246b0f08a3306157f53e867051a8f87878d40d6a79e7ba410c46ab0f 29364ddfd4d1baac1d424d1b779022d6b7171ae7c4d2f8b6659383bd12fad045 5847e457799a42f06c9331e6881e2f94c31e30f53bffaa52edd4736a81c8f936 bd077921914f8f55e1a0b391287da07d5cc4a775438d2c1f2ddb28c94103d525 ac028ed3e60fad0729028e7c34fe06b7297de39c4c419be0cac2bbd3d7292e16 d1f2f8ebbae6d7f32df2e208c97d230f559d9f10e46071b33641ac1386466913 7f12380b702ae88bdf9fb3b60ac4d4909cc7a33f3e14d2f4254856f3e7017d1d c9de67ff7cff9c9a3e1b2b8251b4b458fadf6d872f6aa7f00b083ee8eb530104 3d30b8f635320a891cb510cccd028a4796709a343782dd5ccc2db51b294a9536 4375c8d368f9963a0e6822959fe87814945218aaf7804fd36c59618c3eb899f9 2f281631d7aa246f87a7fb6e5e060676fe69c40e4656acc02c282ea977bc0e24 af75a6daac726e4c30e507a5b957c84d6599c6dc936c4225e517c17aa63d553d 24dc7c1c3e92c4d669b7dbc7e57acaaa4bb28c061ecb6252b16d82129764fa7c 0f1611a587a3dc6b917d6a57a1cb5067595b12cdde7808178011e403a9bd21dc 9090c62bf7ecaf5ffeabe29de51b6189ab85a26ad2ef016c8f7722deaacddeeb 21b391d960724b6a204b17f56c0e19892ba6f280d803fcff15ae7d03ab34a5ec eaff9fc70f7a32e9fa19a1fa0f9e358a857588b2f61f234f97c84b5bf13c1fe6 1c6c009bff169f28fb2139748b0e85cc199bd45bf22cc1bdd2a4c9a7dfed6f38 3f5f5c51a3eb148e32552de1d2f4b16682edc9422c4a1785959a2d4d9a6a5caf 0a4d902123d5b9034d40b998a02d574f4608be1afa58f55ef076a5d07e9d3684 96f8f6983755f18f9287b21fcf6319313f525fa71b468144e568af262f5d4bc7 c3e3d920e0362b53d649e8cb6f04990406ef7d1e8d0d87ea04f265f79e372382 acec2e56b95bc8c6973daf789a758fd69e441bbd4f5ade7cd347f6133937c076 850304eb01de560c738476546b688aec580d86a877d9e95ff78eeb5cf1a6c638 b6d386baf61f059477d148f295993022c8bc7a1ddcf557b2513f8d487daf0ba8 c78c0b80f6158048b85655b5b42699198a9ed180f4a01de24e38a1d0d0fce4c9 8a9e03749e77b458568ffd77d9ef1e24d199d197b5b1d8f2321de90ab8b98f82 d9b9a2544c7730117b55654e3263be6019fd69fe538eb3f5d088f6856640812e c80f26a320a949ee36c2cae6354c5aace12b45190788b9e3e05b8899389c61a7 1f39fbe1e380bfc4a41aaa6c5093c2b4155040828f91bf48bbf8f54baefd53ea 3c12a84a8f8c52b379941846911554f8cbcf7f95a07f2483399cff84161f5299 85b4f1367f68f1822ffb66cdfbf43cab820d7750a956d17a30d0fa5e9e46c3be 954c107b92d3b2151b5ace042ec2638f3a28beff03e5f0ac56a44d74c6f49fa3 5dd69058fe58ae2f77ba23cdd00b871b50c87713c3d60679696836e6e037f3fb 93217a9b8c159388be1f07a001ae5b0059446383484343f314587dd757ead388 93992b452db2846d58aa72ea320dc1939e0c200cb3c325b402bde954035805de 682efbbeaf892713f87fe2db2c48fc71725038db6dcabfa2c2852ad0680d9f4b bf890f8a31d940e882319224d4acf8f99c4035fd022f7d9139d5acbe8990c0bd ecf09ea80a7962f2d192819ba641b6f6cbb655d7331ba58037e9133355625ac0 2ceb8d2907d3bb153e2c024ff1b18b0cc476c65b17291deb621d33949b3d73b4 cc8dccaa97982e052c226be60594dd93b92891275fc03590c604090ca11171e5 85f7a56f6aed6801b45ce17d32f8d5a7a39dc5f759aeb4eaa13fd67525f75c48 b705f2e22736a9d5c946e3544a9315af9b638fc036f9449a37e60b6718ac3ab5 9c184d1ddb8f660e9c96b8413f21e7a3511f59ad8449d393089e0dca6592d88d 512d2c06658b08f0869d616c2f531b3c9ffbd60d6b0992d22298ce14fd71fff2 63e7323a19b73d6ed32020387a3a30ff4d678a182723c2d4a0fc269f0aebc2cc bb4ae8021dcfab3842359830152e5c35a77154e779f2f51c73230cedaf046615 b66a8071e963c6c40aeb7a459cef29d5e1ccfe36ba1d2990d81448308a908618 70f1082ec8e9e344d27f4138dd702e9b4f2bc0eafdf0e76f47fbf794aa71ffd1 5bb87e3d548e528a807cfa38059503c6c0956764d9d3d95e4e3c26c98798133a efb2549635c389447e21349618d013c0bb352e8583cb18cc2de56bf0b88ca2d3 85425ff7db2c54c8983d260c155fec920b61b4ffc31aa3438907349c3b37d6fe c283b83a012da127e84874ce4554e51643f3c984553f0bda57fc7890d9c84dc3 36db331ae4eb55ac3c8b32b209e884cc35a05b0394cb34fa08495d8513076658 4c54a81c0d15b75475d6c276d30777f7c586c316edd7725281a2580018ab2f94 2ebf8e86343d1a5394e6d322c8c4e92ecc922ec0b5d73dec84888c69805c3356 a0547f37a5bc6e8eec700c671fcb117068297ce66f45a6a73fded49a9445b2ba d69d2ad975333617262c589993bcbb6592ac0258965c9fb921166b5816aa4126 0404c1220c529d06120943f599ddc15d35d218195eb3aa2edf930e3d960ce8f6 564d45b3e1f335759918b9ed78cafc619c5405b77cb9ee0ee961ad60c089350e9fffc69b64f3eaa52176f6f382181b8283a7aff6352367766f4e19419993f50d76e2f7c42693e393f4d2b2f9c7f7d61e65dfbf3fa4bfa674e6d1514b821ee602635df0985365f6aa70f8dca7d9fdcd9404755b15222c5cc6cbe3394aa0059d0873b959343c514442f1c2dd464e65dd7a35f87a68127d60372b103124fa47390309ed053f381c34a4ac027e233d6a824905ed95fd651166d46e8b3beaf8d3090714a496cb748b61ddc96d6f380e26621c29dd84d6ecd7b68ba61ba1ce49f9000a440c7bf529572940074f9926463c4294da45f6ecebaae65c892f7ec6a76d1400ca37ff84c4a7f610d10071a41ef9ae42c2747d14d89bb5773aa2a090a27cb00b56a14738dbf71aeb2c655f2aecad76ece17aa216cc68902e0544aa2fc42893015f321c8035d351c4f8ffffc235e4627a03c2e740229775321d58d67ad256ce058a370061dddd7bd5e9a554d92edc4e31f89f8f7d9ccbab8a268d0d33abf5600c02387e6fe82508771922ceb782d08cfb95e25f1bddc7e1ef6a096171f5fd3901a6a55fccb85e6b5f2bdf39f33f05bc3ad48b2eb3f2d6131f9b43843648e9370caa5d0ab3c6c071d1dbd036692332e9f76d790258cc7047ac48096b49848da605f6df47097a4c95e219ab6a738b29ecc0e838b468d66e11e167341bed84a4df0447915fe62290446718495e7b25989fa5e8b33f982aabd5ac6491813fbe6b980b04e4866ce7b2b48c597e5931e02cfbff8c355d940471fcdef2df9d655398ee0f618131d80df723f2ab73233bf06f32631025641fbba0e5cd12d350fd976a6f04036aafa5d6d30427a0ae3352dcf43476fff0a13e1e562e84745f853c64302b05364f21b89001ebfc423167ba164f0156088425a2b425bbd2bff421efd80acb0c3ac0913d06af6e6cb377369d2c9a5a024aee2b0458087600026717199a6f8904225c26b0cb7f1f4aa7cc0786c79dbe4c4ab92260fdfe9eceecefbbb68bd1760a327de83a708ad248d79c43d557e52fa39ce7ac0cd7e8349ee7daf6ec5910e4035f2ea5c22b234cad80a14780f40595c3ea05e6638fd97ab6815199056fc64706188baf8ade68dd0550cd480edc0b5cb7f521a78c22a31e6e0f7db41bb78f0a0d687dd9a976e9eff6d33506a6c2ac6319cc1e3907c17f250465c1ae101a2f3a0eeab54f3b65e462fa4b9ab38e30f34b51f7799d8f558a98641e11e7931e69750670e74b1c70c7c6490bce293f51804e4c97578151654fdbc95ff380857d58560aedad0a186fe54df5b98df662b197a1950b08bcf44900b98339369fbb77dc2407d89a4ba71bafaf28ebc098cec6aadebed2444ec373b8aa6bdf13558f95c11d0dfeec0f2cd86e013bb863b0648da6083464b7199f2797a96a79dd803c6a8a960ce5e7157903ecb33c76e71938466548d88273433993d02c7c7c8d5d54d57b340265a1f1c4b7bcec702c4823f6b2cb50618cba452f01f0e83f552ff02da1cdd700736c4e89a50c9b0d55cb9fd4f0733a339b036b7f8a909c0be58f6dab51c7c700b75c4ef862ae4e9e948ec4d93dbad389893bc3a3f15737ff069eee29a8ac9a03e4488ebcb85af7f1ba2fb042d1a35dbdf17bb590dd98fba97f7d704d4c4775016225f2870ec70aed79c3798ebf6e6f161416846ad448cd0f9f01e2823847dc0bc1686f5e4b83daf3b4b8501ad45cf1cd13a184f97c28c4433edc05f6148c910d86e71944e728429c035c9169f4bf5d4571e50ce146c1c0219a7eb1e6de7d620df3e68fe4db715d0f02ea8053a8a0dac329d8bf63281d901075ee5c70bb2ce3042e09048e54b14f7ffb260a7091cb2a549aebe1306a16748031ed600dcd87e209d4835373f9c42ee4325160625679065e9a0f1ece0f7231b7f741797c04cc8b028097dfabc7ba008a195b99dc5bf5facfbdffcfa48c09d6235d8b6f50b4d1e7082f9b134fbcec968a941666a9f55735563b192d396969a13e111cf239fe7afd005728675dc1d6a9e4c658cc342f58441b38bd85cc379bf6ab12cdf212710da704abb4470080c7d69cc391fc5d9507a1833f92f9a34c53a430c0de52d476c0af0f183624f88eeb7accc68519370efeddc38443ff72e6ae9faaac45dbd79c217f07c386e3d75b2f664c331968423d4b93e3ab5c88792241ed60bfcf9cab71a10e0a4dc0fb1922028cf8569f569e9341858c92c01c58d76a804ea7300d1dbf732b04054db7a0a0ded32cb5cdd8417f5ea54d654dc3bbbe438d89463bd76368138b00a5fb87e9522cd348c05cfd4b9af4f3c9e8f1338ac55654387627315062c7580665af677b30490e4266b300fe10e2ecca6ecbd589242803d6d39cf3ca2a421d00cbd6b629df05644d62d6e104fac8acb5d8c2132440b720abebcc52fb993b1c0c27816ea19f54774efd83d5c0fcfe32aee74649e2dd5efdba11cfd8b85ef7ad0d91b5c16e0bee111bfc012d4de411a72aa9b6e2aa2d8fe9d1b409eb7cd5b6c609a3043cba2c8679523d78fd02651e6093f168287fa2b7b5e5835a27c5cdac060477b2f67fa35bc81847252761e3a4d05c2a39503479051583777f6ba5d1ff270d3987ccb9aaa29df455a1d346f8546a89f9a3b8095a0fcfe80acd8100b15c590a7b5435b3c566de4418c313d4132dbc9bae8d7498501a8b657144e60340732b0625a2ab9b5b20ec78a9817210282672c08e48a39042fa081b58310576dca5cb06df5a5ffd24c08bc0037fd610bf179faa2ec06a2587a28f60a18ef219035e1c0ffdc68043394c2cc34bf0086a22fd3de59e0de2e05a725324e9077241b32e2a0af9074bb564297f9497c9841f65c372328b767a12d73b001699f415325e89460e97778269ef78f0adbff8a0ec1d1ee756531c1f5c98618fb521858a0159164f021384b0c688c877f817b20df05926ca0dbc2e9d3a8c4509cdefb9378129e02f04854738a592a16bd0a5696254d4307a19d02ac736c1cab918c1d367b2abb3af0e17ca3241e11e1c8a028bc7631572d06829ba86cb8455823ce7c267394206d70fcf109a1341b630b3d5ba3d9ecc8067111634bda47c4f1c57b62bc9442e4f5901b3d7a1c9c51795fb4f3b71a772ff2a2efa60961a6406ed2b63aa21acfa7ee80accf6cce1265ad96d221d735f3e1ae0d4f5917c71f59388c5f8d6158dc71f940736b9bd892be5fd94e13fc381e279f52b8a22e3e9431b1756d07e78b257463b0ea74ad961863fd9b523131be1eaa4dec0ba239e1d954d4402b120c284d0a6c3044dde5c52e4753a270d54015910adaeff7d0ad21321d56a6ec32464a573cf400eacfa0e34fae060266f3e73b47a5f430e8502a881ad258b210cb84633a4eb6f0b96ec9cf763a889de576cd1045d2c2f372c67a8bd88ada0cda36189b5401979009f237666a2238b2237fc314a5b1bcc33be08d2d48b9ad2811e09b304235c8a0ab64ebeb719fe1235bfc81cefd52dffb8a97bfa23ed7e1a919ceb0eeb9d021b0249b5650a0a4e01704638c4673ffd743917d5ce14d55c57000f3961526eea0803541b5c876ea36be573cb0f06405584202cd5579c515a0a0e74067901b39a3b038961911267339fd9955f9c5934ac2b68be6368640ac9c1d456a6ad9ad841d80c044d7040c68f85c6ec7ebc68f892a0cd762d1049c86329253b55eda012115a0936eac1051656cbe775f332839daeb08ae828d847d658fd3530f2cbee61ff6507544a42a52fb5f996aa14f6266f80723e338bfbd816524f305eb36aa1ece98b08cf3c8740db207923559d81a206a7015a7e7f3ef813169577de5bf14adbe725021fb90859ae9b186a167821bde131809f876a4dac601a5b4332a4e6caac61ff028e278612222723e1c27e983eb5e3e1c8f07f02400d910830d2a3ad1af2063b0f15fd0a6b8415b592e299806ccec324b6b49f33aa11460856392a54cd4da0f10ba1c58d27b36142d1e006cb2ae07c5e218604f5ae766fdcb861b8fab47359c700ba37d66ce98fc9727f164e8ec081f5b74c4f6ee2fed548df0fc8fa4690bf7905c6422fcb6d15f958aa110f4a24859ad45d586502b88335fb3416a88e626a72059a321b88d4d05c838af6ac16d58518dae9684c3489a4cfd058490511eeb3ac0195b20e848520257482d0753b88e19733979fb4c9ad1b9de9029ecb7e944bbb0f9bc831bc98459af75786c9707ca61444cc2d65c60c9617d539b148242f9d660b7c4a0e69a2944fc664b5c080787d333e4a516da233989abe401e6c05d3f2510df7da434edd41740f5a483702678771343ef3ea2629097775656335e280f3bb0e17f0b6c3f70ebf38e6cea7a0d159b38631c92338920c2919e1628dd11c74080a058a8938f206fa0469584aefcbae3eec538e1aa0a717913e8c75ac59f338df0f70bf44ad5cbfb709ef70c69e1888366153accf04059be7395e34c1e3a884c509d654ba39b8f50381d44115374f19375d5993b72f4bb44ab8c364020b43b8930f6df5c04168d10ad24f9c924222073c5c4a3ba7e96a6ccb31f684dde190cb210dd1f2663d71da05367dbf9f7e5f711d9c087e7c683cf964b18633c67f1273630094d36fd2676c4f969253bfffad24b60e2b58292108fd6b2242268e32c7a5b40426fb4c2b7262c71bd794ec0f0768f8e8a8e31771fa3d58a83ac1c44402476104973e2ddb2e979c3ecbaa1d193c1c434807d193e7f40043e1d1ad66f744cc220fda7e04c633187ea0fed0e9de573ae93544718d96211f16cf0dce760e58e51c07c8511241d090c4ed60dcab8c29ff59c08a11d9cbee0c1e47977d541b8a2b020a67498d48b135c2346637ba988f7238411ad7621ba79fd1731a0271cfa1775c074e2430d058f17364b25cb54ff4d4e5a839c93ea5f16a686cc33071bdb088610994219b3e8feb2f2f4ed6a0355f486899fb7f903753a34a4c38ef72ab54f5850968c5ed071f4fe336722c10ac5144541ebd1d4cb01ac678ec036bfb35ffc2b10dff45bddb6099c7e28a4884636edc3750e1ae92dea41fcd7d3827a7dc2916bc08ff1c1f8d855faa9f0f60e5a9ea5f999bc66cb2ce5f5f95f0184778d63e37780888b8c66a848cc563d179bd57de6af8280b549652ee0262900d1abbdebe90e40711a89bfd3c0135895949de834c690428d7d1d8b65a4c9d53e32e2968b8df53091adf439e7e77d1cf7ee81c39b2feb2bacdb6f023e4f2041da8783c040c02690f560d5f7f7df5d3cae2dda1ae74e13ae18de2940086ab2fbd77fc1d8ad9298901520718cfb7cf0fa55861104a16d4d34e52c588fc29a7ff21ef50c45279182f06d0f4290bc3edfe026fd91f0af28b7ea5916182899d7293eb723ff230b13ae70c5b9593330b509a28d7f28159f49c84dcdc1f73d512aaf19328cbf81b0355920efb76535d14aa672a90fabe06552ccaf25ffc218721478b4b5f5e49a34d70f3045c92657be112265598418eef5578dd16c56c6819e40c0ecb1dcf31b56c058e01bc7e326b9c442430c680398def089aa2f2aa8b0feeb34647b6e19adf01d3490a5ab383e0daeff434865edce07805310ebb250d12bccbed8abbadfc67ff79f20dae0d3b55bdab53409ad1c3b2a3c2e7b9b88a266570ddb69488249e89376299095c23e825eee7d3b4bc32627cde5203d68de15f52db120eb026390e15d961040d6077c0b22d93142689466ff356e21d3006e2a3d2e0bbe85c4d7ee1176c7bd90c2161d24a81490efb6c96c545fdc50693102ab442872669f64e54a79c290d6a0ea26fe6c39fed8bae97db3a0ddbcbf6b79363ee64b6b3a6392655ef6771cd2a0f0f9c0407c32d94c2e741477771c8254b16b6b7f8a61eb4fec436c762cc2fa704235e74e2308cebf464c55ca87da78bc4d3441c7a39d7615afb615baa940943029402c616c0c8dfe562cbaaa123cec35fe991db312166575479cf3bf65011c30592596a84488c250bc166609a9e3dcef26f80ec77f208a6e87a2a82c05d2282066f23804a1b0ea3cc826c9286bdea30a45f6a32eb13139863f3c42e3d22086809285ddd9923b71e20bd8af1ad1ef513ab2c4eb2485ceb0b7c55344b44252cd106be7a78b24f2384ee5d65e904ada3e7126d4868c0edce66ba3c95b39fc5b2660eeceb156743acb737471040d2ce8929c978eb376fad551f287b826c8d7ebe4205f5266d32365b3c48336ea4eea9d4432b992fa396baac87f033a17594cff0780cd691e7ed78e7200d7ca53e18f1313c8b6586aeb097b7040c90e14ec2b1ddad05a04de8864efe3cf445d28a665a264624f4362cf40a14bffdda622f47e4d367015ef7d59b206e99d72da8e242a7430a9f8fc0d4da69885cbe2fe9f20987d97803740f0a5c9a0371b2179b12b3f09bdbd11c7bf428f1c9d8cc6536b13296dfcb07676db014b5bbcd7ac2b38da587e844430762f03aa01ee1d53dd1bf90be25bf0343759aa0400108cfbc2709bd944ac8ee4a0fec2919d4f2749e468a07d0fc330ce8f2a27056ce10e80f9d7c6955cb8decfd25ad35b97939c06bae53d870ff830bead8e991c6713ca02a0fc10884d9303788598113dad5a9b2954a5fe8c9164c08bf6d3aa91aff2a72b0eb32443df4a942df507050a2bd4bc61dba174214b2d7094022d7d92a3ec28696740c74d89eae066d7c1aa9e023e281b921511099d74b0c8a594536c7f18e904fc2f73da5546a693d89ae5da216a4d6d1079f3214c83305011060446a911c6bd00eab9e818bf09e1ed58dfb63392268fd69c593ed855e00f43a62e095c8a4b3f0030417de6c10c3261cf961cd32d8211c8d1c5d0897a30e056916be963a34a6dbee7edbb073a9d95872f6b107f7fef887a45d1b021c09074ad222877632a932d61630e527d903ff8b7a98b14b625077e2ac5cdacf7e7b05447e99d539e3b1cf21af0ca25beec9629a823778f05b4e80753524c546460d08750f57bb12ad0f7ad564128ccf959b4ffdac4ea5a56f2187494affbfe94eee0cf4d0499e83e41c2250869b8874894fff40f5ac8faed42ea988ae9ecabf72570c0686d16c919d4c6b8c736dfb412b8eb71991510ec74b16788732cb4916d5db00dd2aaa75003d5d5cbf5146d8abd73c26173ce3554beaa0976ae38c9a5aebb5002215a19331c6915ed636976bed60e121a2f1d5a351bbc8fd3377613d5bd9db092faa99a012fff971a7bfcc7d32fa8e3d3b2793aac7751538c69fb5b24d51c7054cc9b7c4fad9b52ab60dfa85c98b1264092a736348329a2efa2a7d281a148703f78ff54120f421105ee38f988bb0019a871f50eb994e034507d3837d423642016117f54727c3bfc2a665e46b0cfcb1381de45d85dd4ef4b0963e1f4ec6255b0b02819fdc3b8bab935112ba6d8e21e008f084c0e53be8b81e83ccb39282e5f00f376fbea5bb7f266f316027e28d545b6b59c6122b3f54a4e9e7660208ce6fcf09b1db7bc61f406760414e588a462f781d5d4bbc9e253de69e64035fb4c60c31013d5072674dbb007727d346715c710af45b93d85b6a707dfeb4cfaaa8c52a1006e02165a800671917565d7d96d355bd231004ab6ffd69a77d3d4ade1cd27d990f090e65fa1cd4605516cdafdc03766540f0659606af52c94ea1ac8122524afb0d7929ddd810ff9db9e071af0aaa0ec7aacd9d819158da08fc13059d6b6ced8c00a1b337494d545960df134834de7b3263f03c68fab331ea08b25b9ab19274340cf519dd30e85769fe9142af590b4c251363a34097c5df3ece8f7c188646843c0f186eb87bf8bfd6fe936d8507666236a9ad10e880e40b4edc046e41ce93d6e6047322ed353d2de3bf62cc2ab650a4892d1d1be73bd383eb20aeab1a6fb055ab0859495bbe5e38b9ca0657280277330072f4431739bcfc53ba1fd27cd361695e0116660c9231c240747f00638b14a7e8ed4e64e6fb1aab61918e9f34e2ab40d30cbf2b97c91a105d9bdcf659eb7a3fdb09c6e51a3ad5e1396762d6f840bf1ffe0364e1359ff787d3092637c32b1e0dc9fd561b18be1a17abb6067e45976b86bd0b91b014e60dd07168766f188fe3fcdf854dd2c3053cd362f8bea354c38a47f902ec7449a2295f1ff6b4e509cf581712830fe7ecded4e245910c057b541130f7046b9264e52853c33abc64f111a9d5cf8c8f1220bf28db736fd565bc0e57ba340f49fcd4df9980ec07251453965f2b8c484c3d84e4d9af8655e2bcbc193785f50e952205f171171c84884eb78e96bc1a53c8c1e51a07ef1a99466f7e0e749f8d0d9745c14bae6c12710c1eabbe85756c2272039ed942ad929b819c1d4a7d286d0bc4a5353810da8225cda10b323736204493150901cfb0a51e93766d4d5369010411e048d1591a7a7c3409cb0fd04852b0a4257a3f5dca0f80f2d5ffcf6811f101c7a4f345fa700272e1fdbeab0c9362b90d3ae2e9545d7c38b40f828f0928a20331bb88a0208137f5f4de98bbfdc6ad3736ec50073206490f7f5d37bb1c5ca606080fe4e71b2c9761cb6770983967da2762eb67d992f42a0d65a3a9231a78290d1140ea0aa1fdde4498cfe84d82ac569c91aa8425e7cf7ca2b2e60582c3fe8706e2f55f81084fdf07bde61270967b244431fc7d3d7cd4738bf85260c7dc190603ff592bf61e1e0bb4d606881f6ad041faa21dc3a0dfd968eba161f2044769180d88e91ca069058fedd75eaaf8e3fbed1693354f37136661ddcd90677541bd640a4ce9b189d3773bc789c9643cb7242df54b521ed05b61197d19fccdc60b17910e7d2b496d706ebdf275b2cc0103ea2c652c53bc08f5cd2248f4b710bb70270d0d7126173b0168dbf9a8c55167c641186a812013a5f0f22f029636cd5994dbfb0e false -check_ring_signature 1366364d335b3103c58af5df162cf00f91e9a6af1593553892a65d09fd528736 d0ee526df7747c2bdbd1007fa9bcf6a9246f0a95c8ee6ee1efa63ea89095d14c 1 908004e7d18ffcd92acad9dba6bd29e3074773aeaaadbcf2e1fc08ee6fed4578 626ec5e0a9812ef2bd8f383c11001af86f7fa85d6cf4357c7bf35f2e41f566a739d88cb96a9073243cf7125efdbd33daa12f50fb1820b1019cb4f76115f9001d false -check_ring_signature 42dcd3232ea28b8f2fce849f08d0d9c7ce1e5a9e9cbfaf1e9bf94a6462cc8b0d c4bf33c1b461d64225ee349d7638dd1079c798b9e19e6800011123e7db3ad6d9 1 121cf6a951aa96367ccf40bb604c935014f6d44f7ba677e8dca51357e7ddcf85 1d4a2f01d526da442f08c5ac5e78c600dc07bb57651e2c34659a3cc45cccfa4cc76750dbb04e724909a9d9a2ad6af10eda3a90a7a31f3e168464b0f1f0b50b05 false -check_ring_signature 11994119fef5448d4d3283e54b59124dd53af6f72344165405286bf398c87e97 4c0c56de0f8a747310968f4e76c37ae33f864d6fe4dd1b3d367ede69102adcf0 5 ad3a3817396e0fe445e5fd492dc2575a0c661432760d269b2cdacc9830a562f7 280b7b7fcd19a714e00dd65cbcdca2a232c030ad6438ef1210c23cfc5505c338 16d5e25a345980c53e6357f8202eb5fda37a6dc34d802040570d0b621031dd9d 096dfe2147489db5ac7fc28af7bcba682a859d56c8048d21065ae69be4c871e9 d2dc2e5a8434ae88c8fc45e29b9e9441af55fc8034296a198c093c837efca93d 0b9e78fab2d3c419f3c8250776e8b543a006fd874c67b6df76160ab5be3e36050b90798a759e63e11b4dd535ce7a5d056e5232f936c90e0d7bb23ef472a49503bb3a4a6ae0600be90fc0abe02fe3ab797c6e0bad8c25e550bd71a2ec51e4b905abcba3b65d499eedfce08561fa354898bf81608815b3c9ba70918b233d96cd0098d9b036e4b3b396d4e641fc0fc7099715733efd8bf0fee40451b3a3a8586500a1f47c83abc54ce3fa10f88646984709930b1b6b0bcccec9b57aa92803c59102b34569edd7cbcbe2f51a5f9f0d4e871a0026fad6e6a24adf04854e1a3896d4087332fcd5bdc8811b840463726324da8170d4e497b16028cbc3540520a0bb1f0f5114bbc54aa0b768f87a32e9baffbbb33a1fbc2e138b906839a4abb9164951029d5e7571d905642ffdef541aec22ba39b6441cfb710456f23b580c901085a40d true -check_ring_signature 3379f09973d8371a8b6b055d2429e3b9389ec65baee8b51e210af44ab70c3b89 f79025bc8a27144276f5daea1f2157bc40748d3e32b11beb7094613c837f726c 1 f10a082be84ef9fca3eee03995e8be0abfa6c59cbc2ac940bf86801f0916d02e e956ac65acac1435ddf61c6cf41a659d120876daf3a8685eae053471a8449203077426b9d4d2500edd41d97d0f33cafda522e4550673ba27f3d6111ffb2f2abe false -check_ring_signature 6aa85286c07346aa94b29504b4bbd42f5ac2a7bd6eab21aa79db7a766abe99de f4f279a31a8f38ae54a721764a208f268a14e2139df5a78e3990f95384303f6d 1 f065039b219d6dde631082652b6550767e1d34480d71efba906d66c79f99c221 14c728388a388a00dff5dc30f948c2d6b8264e893a7fb845437581b391719e0d12204acf595b84f23e6a207a0da59cefe9cbdb7c72627e875dc3749b68713200 false -check_ring_signature b6d685cba67319419187e7a28227a416c3fc293e94b0e4c2e94d04e277ec9c64 ad57b61f9a16be5ed216a124e0f91ef0368a94f0c8d91dc7b73a0dfbed82c87c 19 6a188f014b63787ef11aaad56685a20481fb33325f2cde0a8fb39585934e2ced d0e19149d5aa2111d6015a162d74333944ce64362b53572c7da4f338bd463807 4200f4b789e714439c4bc092f0d0d0d0ebe1a188e09250d6238ad58e5909469e 77b2adefb5c3d42732caac56fbe719f096851388236733a50077b893a02376e7 9d746a6c03cb824aac6abaf65dbc5f3ced3f7957cb4f4eebe01a48dc828ee559 7071ff2bc986df8f089ca36dffc001a0c7a80bc6ba6a54d7655139806f5bea95 9b4240af37de14a7c2ac7e3289d4a421d1f4eb9e471fb0fc2054b7c97ef3b3ca c8d1242b860c8714a71a310d1457a3bd50697b4ae2063c5718ffcee30c8edec3 b4c20007b3cd6fbb8c60c673f1b4080a8c9e3bec34929d804a65742ab65c98ed 1660da8dbd944c907a0d9defe5bd774bffb1e5bdebc9c3f15536fa7c5bb1e6dd 465e71bdeea5bf52244b199db9d2f40d117beeb17d0a7bf9d8e2c7eb87fa492c 789c8531387057d298d7d1ab44da33a54c3d2f6053b5251af45b4f6a6456653d 809022370f9efa16a5a62e954e1311897a463d67fd569021a22cb4e4d22b65d3 2bf7d695d7971ea3a152c5b4dbed40a553558ac8869b28892da2e7a37a73d329 d07cce73be06a26b2c7e05a6d3f96697a18eea1491dc09fde2843da14985c5c2 c6fd2ac63449e81b061be12718ed2118e7b3030d9de533b97990220b97a141cc 9fe1d65b83897eaa398c2ef7d97a7ed52bd471b9c0658653ebe9b3f4522b03c5 c8d5e4dfa046d2027f3dadc779fc7c960081ad19151c9d901cd71653a1d28ecd 0d999dbc1bdc7ee5c13088791993fb1f440ed795ee08a209b9eeacdd36a1a618 9a6ed8fc37f6029432267c0087d4685b411425ccda1bc81419bf950cb0e102050aba904d020e42bcfd4940050659dc210136f42269c1136e8168c19f03b1d1068919304f45b3efed98c799fd1964ed06ddb8f9c150d862ad1e5993411a212f03c125f3e97021fb049f4370fd3ac9f8093b8565fd1c90999db06177ab4e733c04e68a11293e4202bd0d57b9d61fe835e311fc73e59313b9b2adcb5e526288ab01fba1c0be055c224a286604ceb4f9e381aa8e7fc2ab48af9246fc3c72780d6d02719948c3e3f2790c66b9c1a6a30bae92582b995b0ace9d5bc1b88a96b8558a0bd2fd73cf0d47aad286bca9d3cd048d7876a5ae41882c7ae3b8520b6585f00303edba5cd2e0bac23db83cca915e20f8d4a53a5c41a87db35f11ddc6e6b8058103dc3f2e54cadabb3866391b7b8e2937f29ee6c924d0818690e55a63f284d21a032d5cd420d5840f555db2a45a2fade8ee5745c8001132a069beac5e5f0426820f3e2e19987eb7f6965ef54d59a85db0ccf3cb54b8c37efe9fdec696eed44f1c04013554a3c6257d33d5d1133c9b7bf592514bb729fc5aa731d42ff8f07353610f1cf05419edecdbf47535bc6b299b62ac3da2cca5920863734980b4dcaa02e10e00ff8e717dfa6b55e1ed2195f8de42b1992c583c3b967fd61be0233bbeb03f05759a9fff4bc9b1475c0eb9d0020bf8a98ec6709deef5cc31412f505c4f5b650038dde5b2f61b29888ff076e08422838759b0ba8091f17092bdf74ba76817bd0a7845a88cfbe4246a2a868d8a5a2c0f51dacf56bba930a89ea55828d99323010dc706ead56b08730b3a632ef57314cc136d9e3a58c0b3cdf4626426311c8430090400ae1e3f81c3a36066732fb54ea0438a46e62dda9d355871f493b33d52d3089c69503c13815a91b09b69514b2b6ebf0bde177ca76705bdf74bd509ead2b40c812f9c84df359b622441739be6545fc700e0275f794e7884b84107c18785f704606b2dfe28c1685c0390dc8ce3e06742eecd78ed1ccfa1f8360de21868e8bc0561b72f39066eac99d4113eaa72b919bab224bb57279b3050c1eba29b8ca83108b0d3146f2afb6f33e0750a7f147d523ff011b15bea6324d436aafc7bbf81450c30d24323204eba5f3598ba28757965ea971c116aa8539f600a7ffa53259d7901305491742a6a85a74abe9a4b4a97d24fd95d439d6dfb098f41ff2f3a4f9e5500252c3bcac183072fcbeecd0ac5843e7ab19ee46c8021d452b02cda2df818a20e2f1f568c6e53455e42a5202d8b32c8bef98be361f9684bab8033c803740e4d0d4381190d39de5583b5edcddd92bb05bd82b9130843c35af12193a414d351e40ce017d089436f828f2e7f00eea2e7b96bf4d462d73e619650d4eac22e2aaffc05db6caa3ca83fcf60601e95b6d2b8ca723aa159bf7bd793caa5556815a75595098106465477d7dce35774433abbafaee2b9d0e997b3a0c4d918d5a114f9e31900b3a0108c5371be2bd198de539614e8281c4fb3bfd939bcf6ba3beddb25b782085972d547db40d17dc12515ab36c5f8c9e503443d015d190cb947f81223fe020a821811fb10af885d73f3e6f58a1f413977db5551fb64458e201806cdaf951c04313a06fadf1993e6e29d34fc40995fe85035a2d5b13e5e9b21b81c4785cdfb0a86a691f680d771b5f52017d46238ed5c65fdc4c0c84a58aaafcbe88d6012f101 false -check_ring_signature 08eb1f2f573540508839644604d7c7bd61e76086b3907b2cb7b2d9cc351baccd f3fd87641f336e75a4595d936c8610cfa3c2fcb8c6f54054f6a18d6eafd09b11 168 193fa8763b920daf9a60dd3d2e1e71a289e38a933ebcfea587ffd592c6fc8926 74d5b46db71ce9d37c9b639c928b9fc0dddaa564491d0ccd4bf0c123e128d84c 7373fdc49b6a7b214f33a8a09fd399232f9bd09bd37c5ffc17786b018b262ebb 2de44e4d0f1e902af04e591ded67baeb3dd8c9d799c5efeb58831f63951653e5 8c3b6b616a46580d81c7d1256ec12e35d4a0f19df33a29fae5528587d388f7b4 fd4897a9f9d1d9d5c41e872407d8ace59dfce68c19d27779c736b471c96e1f01 1323669ea1102a0df5c3066445cddc91d73587de479ad342e929ee085cffb1f2 23c04bbc77e9fa5b6bd1a8a2c67dd0df7ba7a6acb1143e938b74d0d6f88788cc 015428e4853e6728edb474c4dca122367151511f56c5166320492e59b0e76e33 4437c4670471dd2236fc9b38ee67ac9c9807bc420ef1064224730c01ddbeae83 5058c19baabc11ca508beaa8eee8f56830db5976979ff79aee11fb8864980529 f29a4028a86b1d1d43160b5653d3f5dfc41b3ef0673b688b1efe3095a32cbf6e 7c3de1e109eabe7421b13d8710fd22d3421d3000f1b81a219ce5b7d341320e21 15cbf8d2c969006c84c52f4ce747bf89d8cbe59c04315be6f1ae80743941ff3f 52cf41b3d200237b96384ccc0538dc9d5b85ace90f1d1fa34ff3580f55a2d770 988cfc90202a70f79b38b5efcf8fa4b8145dcdd4d7331d6d5f7ebbbed15fbd30 2fae7c2f1a99a9efb8713277611e7637cc8a488a2086f63456fd0407d64c4805 35497e688a8254a68dfbef394d43b7db1cccef0124147351c526dabbf99b01ab 65a35dff5fda0acc1d58e024241d50521035de936c814223f4cac32936664398 9bf63e0262e3c0ac0ceb2b7039597bc2db5dd896653d21763b27ee190e102a0d 95a9101b4e32df2034c714608f378440983ac5a36fbbe1ecfa008bc3dff39bf6 f173abdc9a1fcc4d004d343e05341f4a8caef163f16daab18a56e59edc860e2e 738cd94aac031d05ab8b7cab895da4f2d4828da82b99124e62b3c8df54f7e203 eb7fa7a64ee2b5fa80dbcff9bee70f8ec5a75b7370022fe9a707034ae016ba27 964579956733c1f2d4cb54da0ca1b8653709a4dfda568317f42069a9a54e54c0 96fac0d532312c4b8a88905b30698218c85078bf76aa12b332a783b739e56d55 4be0c502eafd978ab1051bcd1eb1f514721101b9909e06553ec0a8c285deb02e 9bc48e9d2946551519d196c8c315016912bfe375e54c9e3d8eeb5d17e1812ef0 306e8d2bcf23f84dc4fa39a0625beaaa25fadc7ef435201373d5f6d6b1546594 889bf64b0121b7cb19b2aafc6280c150bcd727e189fad01f1464d07eb716c9f1 0bea989866253276a332cee5872e1d977253813f75108977673541eecef74d65 9a4a7557ba58edb10a1ad7e818993c1dbcc1e826591ca2f55c1558256bf7d6b4 1048c9ca06cb67a2c8128b44bf996fbff0fbf648222157da96dba44f765f41c3 303cff0c2718ac64efa7ec949be311f836436abdd46193a0826517203d755b0c b0d91bb83f77e9a08a2a90f2d6644e033394fe03afd5a4aecbcea8c7150e4a5f f09268e280155678a9621b7961f696e7cdf87844efd3a96f6ce5c9ee4f22cbb6 fab7fe124666466ba8326c08c042a92e98c0e58195443d362aa1b25efeac4eea 1df0e420df7690e25401ce1bfa0bcdd4b9323ce98c42b64f1747262832c41006 79095f6504c2b8ecb99e0e5e3644d76e3e710d3c75903ebcc7ad42c303abef8c cabbc4ccb52980f8fb3843396d313209f4b2d43172fc8bc81deff28423b441c0 76f43a8217429b8a37deb77d22aba664ea64ef6155323a1ff891d72bf7f507a6 4a7f0e68f78a0fff8f19ac6bdbe23e1fbebc5b7147e0d12f9247d092493f1a48 8be5909a758fb59ebc6af239d84a98edbb19504a4f0193c8e0771ab5c593aec5 7abc97c95d2c4665ede21587110e628e2026e45472e81ba583feb2d6aa4be1be e69a6b94fe232c3c35536089e3aa56086cdcc0af719bdeef66e059f8adc1b637 4d5976d761859cf12e903b6d4272f4121a7cefc03d4bce8044ff67e5278e2c5e b4ce53c0d1383bbfbf506f7ff94613e23aab63aa89e42f927fccf654f668461d 9d11ceda0ca9cc99308b626fb02022713d9f646d7357e35b0a254486ae26d6c7 05f6c1cf759645e4bb74a9dab8bff1e94f405759e1cc2e7399b14b5151627dbf 69063b95cffe28d40eef277b45a0856ce5f5de5ca02067af22476c34216bb138 c5d1dd133cb8587aa54104bab6ea2535595a0797fff1376c85634ebb8514ee77 399d79fe43a36e3d625f8d6ed7643d49f649e97d405451a2b094a432519cf994 db85c9cca26063e3cfaedd898f0c9f4a651411f3211f42198d88446749ab8a23 c702e50908dde59f23e994651da6d5365424545c00af7e622ee986cbdb78ee3c db2a2db089e733d0e135892c63f1edb88eebe7135d790aee58cecd02c2ef99e7 7ef1baebb0902affe3e0b96e83eb1de37eb69d283bdce79a0d475913d3fe71b6 0c820af8075c68c7818e3c9d0595c9c6c6d1bebb167f1defb64dad6ceb778c93 7e3bb98aaca52626ec9cd22a9bcec8358950a5a7b0f483228c022c99caf4c688 cef94d48a5e3798696b3ac625f1e8d1d6a6a0c40c8aa71b381ce46088df22362 4a0c2075437952ab9087f5d297a4db8dfd376708d2551f33b8f1d3ef672a20ab 23cfc9aea7690664eafd0db7f53303f68da178577c772a1c8912ff321d3a0f8b f02279e5b04f266551ff6027212657b37724c1f48497ef154f83adc6fbca66ea 6c908a79436a1744bc7c3a43008861764e46739c765346cfb24ae11b622ccac8 92738ddb13fc1e09adbfb0421051c1e98c7f6b0b3ec135379590ad75445802e6 c39d81eeaff9ee602916bbbcd57dcfeec538021bea056df9c36f349c55ca12c9 b3a08c76c6219c0db25b5bc12f4b1627eccb7aa99315a0693640702e0d4bef51 541945f8b05bc405a59adefa600fb39528b4d0c0a192cba742002507091a4f57 f00a0c6eb27ddc350a140bb42e7c80ef5afcf1cb82760b5aea1a51e7f863cc17 344549b7041442e494b04eac2bd2fa9a928dae2ba86a6ebfcbe4a663b03d7fa4 433ebe46b60db7f81ab7e13dadb91762fe6362a97b471e1f291b24ee1dce1447 0df64a99f67a05047c27042175dec07210777b9e0a79c501c2688363ec067073 31eb68c78f04b9fb7141d5efa1b076d13c102febff96a287fb73120dc3a50e1d 484d17a5571baf6faf62d328aecc15904f0df5f6107895fe6e70c13a4f7c8b85 004b4a97001ed33f083093a4bde8ea0f748b5cbb24d93f1ab74a2f5fda175efc 8ad10b0f14dd0da6168ec73eb2bb18a05a70292e3ab609f15c1f455d01fe919e 5f31905d62cbcd3a3fc7f86b241cd9df430df4f174e3b66b812abec5483019e6 b56a7bf9e9fc7bd9cf8fba4b830b82c7d264bcc39e285bc951cff1d76478bb59 5428562d6cde2524a28f378afd44e55d2c9ac43270e3ec731ce0077a06d44358 3a73e809c831d4f4fa15e9735fff626f82e5552828057029e34a36be43397dce 225bdf4e958f48be5a7abde0f6c2544c544de53f877c9790d8c65ee891c02a0c e137163d28bf91ab45473bcf987fe946a93428a5d53358d9989b699ff3071e7c 25ca2bf47bbcb52a9760a7232fb1ac5d8058171870401d97d85bdea5ca386961 509082e9a4995b37a787f2d31e1a17ab8f8705cc968d3b0a8660f9b5e95c0efc 20e3d218cdf542021741707c934e7c5c135cd561729a37fe25414065654d2c52 50f55c1ff236b79c6f52a2ac977fc272164c10e555b467d011c28722cdddb785 87c5e2516ebddd9348c9b397482bfbc83b7be4d4ac670b2f9d2b3feeb47e328e 6b2fcca2a0361375e0d38f4db8846cbff7f3734e0472e2c6d38adf224b7fb9fd 0db7d10c36309bc5365720c093e0387f5281373c1d1d717eebdda592e4592928 977149fae9628187bc83dd3184a450d1b9e2d9e7e4c169f547557450aa5bcb0c 177752b932dd050ab2dbe76d7b2b7fd9e73855742faf7b8aed5e55df5108556f bd6b0f27a3c34c0d2cec08c0a4d5e828634c22cd7b46a119e8a90deb9686902a a6eef816519595d1b1ff63fa0fe5c5387818a696e3fbd996b39bc5648483c5c5 f2d7160c99ec35b3df5050da7d9629656f39313b55c5ac53d698d6db8e377562 12d135fa38ab24510f2e06f456bc7205127f61e589d69ce79bc479a518d6f1a5 17258f3bb08b7fb8ffc5f256315eb68c43be5503c763cc56b4b618028ec291e0 86bb3833addbed68d37b98646c087d6795e5b2c1ba0dccf98b430fd3213edace 9fefb906bd3be0063870d24a2fef3c8c039266d533b54641e2635ddde4705873 686287044dbba0c1ac7fe2aa16b3d2ab20054eab37a5efa3b629dab518dce3f2 21e9925551e71e56506c359e2738f7445b93fa645d18535cc3209695967602f0 c22f8f2b3cf08dd55dda98ba6d15615eea49bce1471e957ae5b558a32643483f d92e2f137d8ea178963c7c77936d7eca9d977eb97e92aa47f61fe9f200382ad4 2d15fafb53f20a54193899f70eb550030c920045a48c141552420b0fe7033677 294a64acc624a8a51a576eb3bfb649caa2392afd8c3d2266f6d6634917268d88 3d4a96316cd9c402ad871ad3a212102526e9c1eb908ccb1054f51a58b14889fd 5cb27ad83e7011d332364a75f8f37fcbcaa20a6ee9d7929cd5d614f49233de1a ca6fe6e5bd9dcd3c72bd8cae9fa3aaf6426ea8b3e99123cb3badc943c37fd487 d7bca1ee3e8a65c9ffdd65c423bf59b1198f968e0c6d4920717fbc87a2474d5e ba13a8be41776ffc7c63129bfc03e00b83f5aac6eb77421d3d8b35f0837249b3 4fc94da568b56d0768d901f3625b3db74e43d41690b8ecc09a30bbb7fa29f1a5 8761fb1a127678ce232c1c9372477c478b522cd19f28bf1a27d8cb118e461c81 ea183e70614e20883e6da162e03995fa26c906856a3b7e3451ac6c69e30f31a2 0c503911984bfe8c154d0c84d74f4beaa0439bb22beab91cdbfdcc9f5c65f2be d4a6e1de0c9ada16c5375fc986e7baee19505f944f4b4455cd0c7d7fce89ccdf 1280d94a967d1b391eb7949a95a95009d143ce05c6f89d760b1bac998732c7b3 e8d8e0d1a1f210984edd13e2ccf0ad8f0bff8ab7edb9b612a8a1e050becdafc2 d59f625ba4cb46a2a22ad9d15bf0c03db75ee28528a3c488a208d4e599cbbbbf 0440ce804ad36e5eae792b55fbcd4c00ae2908b0912e01f15f025e90230288ad 00e0377677411b118f3203c9a52d1ac0fa12ebb597ca2a4d6b985a26249bd8fa b4cdd0f1b570e17d5c2da98077b2546ede6cde2cc4035993a8c0c33445a30733 5cabcc7ee61a69cbbc2f0ca7883188e47b088574c200afdac8f6b29df6da4eb2 5ee85b4bd7a31801abaa5679fd9db0c25e794bcdc0dbb585df120951624b6bb6 0cb2c8fe4d819dc66e7cfc3b9be5082084cea45881ab03903ece77dacd1390d5 3c7147ca28cd7b7770ab26b717e0f339000bedd0b71213b5253de5fabe2afdb8 b41eea26b0f9ab306b6770398801553d529d1f17d5081a4965783e25713e76b7 cac339be9b14a892833d7a6a597d9e62f8ad32b9d76e7245aa743299c051fd99 b1f6b109307b7c30357760f57d9e54bd4f4c8323cb867df5e94d48f96a41b8f3 fd295c17c9b55e34af029e914c0fa564c43692cbbee57c76c3f367d50ad72928 bdbf90559d8a527ef00c3f7e5303e8198f4ceab9f2087b271cb38f1631d7ba7a f845e80dc1594d06d29de473acdf215742cf63a0945928a9d0baf6908ef4cff3 54cb89d33376656efb62376b54b1b07e8ae0c88a68c54c874e281a105e6f0341 9aacabf442e41e94a08b5afef4471c01ca8de154f5f5da1955b5ff33af0e39ba 46d8e6c79b3e9b92cb3dfdf03e138b29e1eb9911da980ec7caa5fa1c498af800 acdbd9dac8ec0a2cab6eaa7a2691cd0430282ec730bae3dbe5a0130d7c33320c fa8e7bf5d43485ebecbc7379750851c56c03cf5cbb4dcd09766c99ad4467215c bbd65f4b2d14e85ece7d9fbe98bf32be139969325be1be0096b958586ac72a72 595b2cb5745e198127cbdfd7a924d8df77f7af61807e6ae821a4399715baaa16 a225da8a20f3fed872a75f6f0d4da3120126c23cec61cf38c53488db567ea977 379e979216796aa0a7be8b69239af546afa363c2b6597dab88de14a9e48e9d00 fb32d3343372ab09fa1b7c15f54352023aa4d37d13443e263df9fe0327de0193 3570c3eb75d7b797e96d2477f5c8118681203477424135c27171ba71451737e5 0bc7727046d3cf287239b9089b7260a7c40f1f8bc8fe9f98a2f462b63794cb3c 24ea53cda498fd607cb9df3167d37a1fbcd0e19dcd0cf72caf6b32dec97d608f 4d5d7caf077d14fe55c81adf642e153564607938d059306875182efdc13511b5 71cdcd3b8f1ad5eafbbe554b6760f0853ae582aa88c2c1f67bed443f749d9c63 cabc3809bf09ade439027902b287eb67af9dd8657879bfe4495b48b6065f265c 7f663b7ad6c09053fb02617cc29d05b39935ba761669b188e651bb282dddab4f 65b1eb6588dae4da078d7a12c9f9f766e4063fa561fbc5a0b63d8b700010f3f4 f105aa39b344f9a906afd635e3bf7ac4d5ece281519951291189e394ab1bb783 3e598f28e522aaadb582411666c451bcd5b494696345932286376c77bc747c95 0e7ab7cfc903c1b51d047920603e903a152db0e60796e99ff296571f4315f29a 2eaf905878ec5c5124c3c6920a72b245c15a994d7377edd00fd8cfadee24e4e1 1d95add124a1219a0d86674eebb8eddcbd1802b4e94d42b305ddaea70648d6f2 52252a1bbfb1daddc6ae67184d9a09bfdec81eb19f20de674e2155d221d63205 43a58eef37997d7c66e43c6cebe981d56ab10672e388965289eab7059852150e 56d9e7235ed6563c096cc519e480529fbf9ed041b0422829571887d5b8d4d174 ce14c0c896a19d5de22708caceadd8af707ff66be570ef25514d433a4a3b61da f5dbd1f711bb28b1f86ec97d482600308c9a08e3de3c28a961f8fdd35dc9df48 7b449face4810a2c596ca4f1f67c60538bc4b6d9805d4e803df3b77063f8cdc5 d2dd51f2f2ceeca17b4874541e49a9fda10b4d7435fd6f20a74870af4d7c176a ccac3315fa6fc631e26e786086d456ede4e0d791d70d1ed688adc2e8769f8235 f8144d43eccea25d1df11490f2870cb16429a5bdc47eff8ffcb32d76ee4d94bd 7e447d65f34cf3fe7fc726e240bd6b9ab1a5a1dd9908a9bc01a5d03f1921002c d97d77126eef04bc98ce8625c2778d3aa30d131c1681f427b221671c212aac76 27bd90f411c60b207fbd6bdca0dde276d66f2c76919c71889d4b69b74ded6d55 d7f79b8ddb34efd1343542f0486144c039b4a44560c7054c9f416acafd1e9af2 2612614fc3f997b49d9ba5ec03cf15298da0fbb497c1f856006b455adc9c68b6 5bc41b0775a6e3606d25c499735d458cf328311144ca4571ee61fc19dbdf1c6c 740c8e80fade598e1228624d458b197a4e65f0ec136232be9ee890109d8afbfb ef89eb5b69200c2aad71c945fcfda3a96e71c6d463defb2842e57259b7dd090aef1b90f5e6a2283ce1fee0a09521a3c766182c20aee27a3bef873d61b181e8054486c2eaaa51725b6c1a13c721edf56bd18ec4ba4b0ca3660ac0b6877fc01b0678b836e5a624caf2f04fbee696192944e5478d39d57ba9c94d304e647c2bcf0248d00681940feda7b43a0ebc855978bc78ef1765ab7b223683219ccf774e9f019646bdc5692d3b3b969e4b42c73481c3f04d0e28861066363f904a835e68ef06219c126d503bce7a3da63d256b9ca137c23c00d125c09a73e9fa308636d5ac008bab1fe2cfa0de9e5b6c3dc5bbd6fb415646632ad88f9de0fdf72207def659053e57f3e6bba9951f6cef82dcacb6c9bfa3dfa712e23cf62d0907af40a14bf70f017dd3d538bfed8eb78087d090ad689a26005b0a63cc3c18b3d706b208b0c700c73d91c38cc54f360d55d2686782641f39b887b638d067366ffb7f08e073e40d5db35bc68b63e0921fb942fcfa180ec8e7219a40d849ba1146277bf5d208a606bf9c9bc5e96df03ac40b4cad38f39ec3bb3e5a58190b9f416ebdcf8aacf35b021175d98de5b396210a84f858f5186f7013cf3ae9af1d73ae708e69f59de07302657070aede5df3c44f1cdf63bfd45f7b51def34c92a2f9fc49b883e8aea7930b03def250457dfa5e72bc8ae1e82628e1df0284962ffe7df088ffd8c5dafeb907f0f003a501f8b4c3b9639f5123354b646e30c3bcd3c472b8ddeb8e921f6b720879411cd16e1efb477aeb8723fb09bd958e91b8563350c7267dc24a0cb2014106b39d4892d2ece4aef5830ab74c0bf153208af35cdf17bbd9e72f1cf851caab083b92d2370f8acdb24bc2840441308acd8c3aa55ed1d05aedc8c943087d2cfc09c4119f144131d50294343bb8ecf364a9bfdf7a13e8114cb022a0b6434ad9ef0f5136728b847de927d5c3daec27109884eb13b62542c739458932529c9d4bdb0afdd9c78ad04b83d8b6028686f213f8b596f8bed9e6b108e3041b7dd60a331602822fd8d08c8698bfc698940064fa92c6cc8366d11e9904e2003bfb711662af070db1696ea28f017a07e69ecb838b6c4f1a59c7966e64fbf2cdfa5d87de1e8d0ec9551185938353ea3fbf59ed30dabe2ec4f804937d4fcb88d65313b1cfebb702b81935ec45a25c64e8d55303c3992738aaa61ec3c1d170f429fc53f03338050c83a827e7f0a68ce228b715b48237c47ddfcd611cc7c224356b9d90d608684a0892e4ebb53061b1f1eb663e70c56b5b5215522817e590cd9c6e34d5099a043b0710632034d15ff58485bb2ca9478c05eb0f6ecb5d5edd35811870dcf369eb4e02caddac3c007801360f45705144bdef1bf6e379a8e37e4f05de0d7642775a630abbf1492d9511237cbe139d3081c589e8e6f4d204b1a18f8892f1a3e3622dd50ee4e801a35f31bdfee03a2abb3854d87068807d00d9fcacb2abd02bd05a83df0eb7913bd3d5c27797b08e155686a9dd923749686a72814d2e9da90463c2b0270b1dbc664c31d88a7d7d803e3ef1eb0cac21e9df1de376f1f7ccdaebebd8ea4208ce4919f0eba5022cef97337f357906e2e47115b08a7f8075d5fddce8fe7f270237136140bed1878334b4a4fef012e37832ff3b9fc504a28e2517f503a507250ed3ae6ff496c5901a5c17d703e9e70519227f2e3841a74e989f6299a3fea74d0592aa4e7aff3b594a78d5a251bbc3a2e597cacf2e746892fdfc85ab1793158302a23129766567a86f6ab7a295eefa11bff26d69fd65393d4cc68291e325f7ee06de86bbc4700547502ca0e88691988dceb986dd944f162637f4d3a89a3edeab02361bfb02aa122c0205800207f67a9815a444177d4f9289b3b30b73c816c727098c965dd7d8a0b860d0551a0c73490a2f599fd2ce5d28b3c5244235559388fa091fc64ee76dbec016d4b73ab303202dc2582881ab25aa5576c11f67020179a90ed01d884217cd64a8bc47c37211ef9334dd3bed7c621e597f06d3e0cd213fb1017df7efa0acb1a7923b2f452aeb3f3c8243b7a5868fe2ad6d00cadb816934d209a259d56a9f1a077578c91c9497f35e482852431fead5ac008433e803d6f02d08ca408e6962eb29d73e3c8ec0a1bcee7218f51a9bf7365a403c3cdbded7828304bc817214f8d8f44486ad05342cfde47b88ccd1dd76f9e3c24796cab73ead930d9d7d49abe4911a46e04fe5d886dc8a75ee8f9b54ede50e672bfe9873ba447b092c2f47e658f96ce00f23a700d0838a581f1a3ec0a38668dd4ec8505f80701b00f2a7047f2d585759d97bb2adb21f9a4570e62ba245fc338dc2940561d930fb0fa7049fa2397e48d238c71a092a3e7f4faaa13718bcae69d10057d8a47f656908276e4e32aedfa5f8b9be625ddd89a647aa3c15646ecb04ce6262a569424a0501976262c4a5672e0ad617e9d57037a3de8332ead2f5ab3de67691a400219e310f7eddf823705dc61ab2a1ced6cbef63542d4d3db5c35e7acd7fbc13289a1bb8087f955f76340e799849ec1a439d054e4f6f7f7b543c120660fe18e71e1a8517049b0fd6bdc86e00ed2e55bf18280cd1c6ad52ef2d039f0dc93f923f7ea675e9027960ec95068d44c53b4f088436282f954e4513554e44fa1f0d655770c7512801aaf6f74125a58d8f9dc88edcd519e038b30b98c4d879ff3d54c892672de7800bf50a9befdf7a9a4af5f7f8917106af70e4732fc976c84410647671458d22d1055443e2824e3bce44f799908233d25ebcce940b8e903af246bc2a72b983941a010f491e4007b59ddeaacb5642e1bf4affef1f9caf3ba0d0f75010cd847828e8020a68cbd4d74c2724af5a2ecbde5607e39a2b300b0a32a6e5417724bc580be30c0d34f815e0161001e8e713c61a739644300b79f4eee0a018a77c3669d7f1bf00dd82b0902d92e82758e26ac761c409bd84266d36c909e7175ee00b362d9fd802fb2a57b77d8e20833bf9ab544999c4068ca430bb8f93f1f55206e427ae73ab0e9dfdfdf85c64473c25d9d903bc1d6f54c51b8149c3bf17948d0fc447784b6f089804a0b89b75014e19fe5ff40285d4b2e7ac417e2cd1c7a82b69806931710907474f93aec012766263670974652a07a8e3c23511bb40846768a6582044268a0b4cda0bdd85f914db63cddcd58a353ce874457937887d121caab9747aa9307301c92f472588bc4d1eed04d7f6243c0c9e295078a24373b02185c93aab7434560c9e6e819f226fcb6accb303afd353e20b7bdaec03dd7b0c3aafcdb1c7748d6d034de3bacdde635d42de59d62fa941947a4d304fd843d9920cb54521fb343e940007bc439784b93b6edbcc99ca68dc8cb067617db267e9f83294c113caf6dc880eecaa4f37252e6321114a17b4d6a1cd261d654e3a0132b519baa3ce5202bb490d62ab168dcdada2faa9cb2e50b0cb07a3b4317c163bdf702d8d813256622ee30d0198b121c0c11424ef039d49b55140e3ae547eca2b91ac12a32bd0b4bc6f5a0a56e7656a1174a0969875a5fbf8865a2f20e9126992d419208ce1d63d09c9ca080525e0936e3f5abb9d513b2623542e2561bde653a543531ee10703dc02bcf2092230b1fba1c048a62baf4160666515b42b9f939afc8683e38de00b99cc36ff089783c1bbca57e43e28126d929eb0bb3e74194c6ee573df1298e3112ab7a98f0a76b429571905c0ab5aa5623e5e6f9d7e6d081a9595b6ff7343163198b53e3506efb04f1d51996aea1e01e9c2e0bd69127bc887a3c54443ec213480190de6cc02b150ca4f0cf91132a8b0824a7b048c044aeba807715cb9b9d627c6e6fcce5603930f51e2a55533375fa4ff522971a41d373093098e1b8c5eebc2d97741ebc80adfcb6570f2ba3c0bf6367fb3d262486aac57708647586e71924d573ac03df903e8c8795d08f095248325cc33a4f0747efa2d275886ee61253eb18835c2f5d30cf3fa6d95dceb4839cf4939aa6d6fd6d387fb2572c9924d773a0a9d98bc5762017e7592fd2cf5c4f155d8a93c385066e34cecf65b910395ed0749fad2126af4046829200170f218a3f32c10abb5c38b19bbd090289d6db2675117c772ce36b00531dacb380757c607114cc3c02b134a920412dbb9c6b120827357a89a71422d0d590bbb568533a9fafe1c81a02330d0901cafb0cd5f2fc8fa430e630d62c26c074f31bd56d1e4c22b00fd4c8a653a3bb4c2b44e981c576b68bf575d381aad1409652436eb821b9baf676400209ef97781d9266deba605a24f3cedbee6770e840ab10e9c6a4801efcd27424d33677020cdbcedebe58d7098ad3b769cb8bd195b01f4236e0eba52e825ec94dc40d936f678a45e5ad52adeea7939aa0f82584ecb0642f4619389998632d67d98c8f1b4ca26f19db04efd99c896096354deb89cde02d9c6b9bb58f7924b33c400adcdb5053b06261501e298c5e83284ad7c4c3b8105f2e6b16f0e096ec53bc3f7277ec8dc0b3abada65142f430845a1088400d4fb04e99eb033ceb494aa35c5fba0266bcbf92a6560555c18c9c70c8f6af01ca7e80e557574760687b3dbb78349dccfc0c7c58f378f9fc0cac36321e648dfad41083f5bae4368a4531b540352730297c3debdaf28ca063bf34827d151897edaf34d008f0afa5e1fb588e6665a240ef0511f698583ab117db88d880d52803cb69630033233818c7f24ca37737f6260ee0376c983dd7d34d2a03c4a1a65dc53329caf05a994ea5f30a6c204f679401f6fe4aa839e630365ec085e6708ceeee5747bc20c97a026bb9a9928a16bd26c0c61fc3128bb9c272a546b0f4c0f7da44f7c9aa5072e15b26a7837727b2d640ed48cf2870e83d34ddc768f7ec70239c492dab376023fb64d8b343c865acd22f47cb49e46adc1bf66ef45ee78b87f6b34d91197e30caf0250682f7f9247652c73a7d7c1ec883abc79632b530b8d68a8b6a09570ea007f49ae9b24c64f031083fdcca5c7ef6c807c88f3b773be8ec1d82cbfd1365e06e0d4720bf30602e30b19a6e61eb92cfc3c73d1c069839555dfe37e3e7d640e02987b2b4941a28afcbc0c4a8247c1d02d6c64b0734f0848076e8366658fcd890a47b59fcafc34422091fa20982234216e1d30707f0d3b79a107cd19d261daec062b4b71ffd2d645efeca44ecbb9f404236e9e996027b84e9f870f01684de69306039f5b3d724537d7304dc12042cec250087340b37be67187d058a91ef24c3b0b52dbdf2f63f4e021b6bf8d1f9880b6435915a1ff2ad291011eeb55635e99d109e6d25ddf221134ea2ea17d3c46d53ae7c373d2a2ab02c7a7845a33c4befa730ee550b4c3dc5d3ec80f255704388e82408ff6c5fd81878bcc54f7289faa2d690db017244c77ddc2296245357918c92d46d705118d60966936a312fa231a76c909e35b18e404699663d19fd5dafdd8227568dca1b74369e74c5dded705e472fd01090a218ddb761227d60a90e1360c0b7fdfa4e60aea56fa70cef3f8c1a7e1dd0ff1d41e44445889aca1cbc4a2317ddd077d535a6f40aa556be3fb879ac304f10c1c7a33a85edd3ad7618fd2cbd3ee25c51fc2337610bb048feb196a13c80c610f4e8072e937f1e70b7bd60b7446dd9a5b0d07057a6baaa617f7edfa47120526019ce29bcf9e86abb87c2daa269e4358f6e77401ba424595c93a20f632378fa20dff2a36a4ab2eea1a10992857ae5c936cd24c95283ad612d4a45535fc9773150b6eb7a72d593e0b5f6404c780826226359fe689cb3e9bc93d62e115091a8e0f06f784dbd4d5f2b4136ced0236b1055369a33d11092788ccc32863e77154a44a0bfc7c79ca5f67acfac394da2455ff4dc0b106b698a91bfdb0e4b1fef459062b06f54f2eb34cf64811c4a4233d82eb240ed41fc3d82acb5b5b3b4df291779630049c01b3abd772be37c2a4acb539f7ddaf8b87199c56f4e31baf111980a8d49907ea0a45980040de8b1eb854f174067c35e2e387e45cb4b8a0885929ec7a50bd0c60ea2bd538d122da4e7461ec10e8a609eeed34e65a7aabb80500887bbef67602e0853be9791757d4cdf8f51c569e1586ad998b339813490d7fbedb4e12e56106d646bde57fddd5f7095738f25d64318239dbf04d84dc9a71534301776258760880edee14d2c50bd31fd4f8dafa2c85a9c2de51d262b30f02d8e0e356fa808805369b83a7194fe5e1428afa966cdbfcef68ae70f19882052d96066b8ac05bac05ce22a824d9626d5ff3406e3ee3a8f8cc71b930d2dd0ef8afe94b4e68f71ee50a1f857983b4c67f1b8c1b110860f0cd2758822315add58db00c395c5aa3be7a0e22a1c21fae89eb0a14a97aee6c79a7913dbc82e488a51e6065d1cfbc91836f0fa7ce2581097362a1ff3be21011db735bb13f6985189283253f32ff05d7cf61006066c9fcff719bc419f940abb52b71296b5ce057ee41d5bf7d5d8a81cc140508467a9afd9b15a52dc3b723b9da7a74e2efbef055d947e9044eaaa893800d3e0e50efeecf7700e63f4f594f024c4a3d8a9cf450305888a2d007dd1baf7e41880ab6555394bc02eeb531cfa7301c884edcf94657bc3f7c117a9aff8d93d1841306226379b92347ef7ad4abe055bd966c9271f2de6170116e17c31b40f754817909d5873e598421771cd4163452c578e873afec516f346e5a7032473129a8cd660385f37be0db352be92e7989a22d1c26b08e52b3803f7173c6fe359192c3f2090ddf9d895816d43e661924bc56242f3e8a66bd9a8766dc22cb9404735d339376061f77d8294cbc26130fa5cb235ba926156a87702074755fe184a0d7f920b2140cc8e04046fa79049cadbcd4f6dad1a8ad4ca092b7ded8745009dd6dc2dd1a960b49d1d93804522031b840b799452490c28fbdd97849f5153639717ed93311e70853cbe916d737e2bb1fe197a6615c36c5a25be925d1fc4850483751de83dad30a490070e57c7609cf560a353f2a2464f75cebcae2c6e860bdb30b624ec1d0070838158908f291ec7487fb02e718156baf79ffa382383e5b83efc5cb7db6906c0ddb4c639ab774a839744b13854d6793f23bf2d9412d4694eb17b08c517eaa36083e7584eb5280f451bad4d6c83ff30009587a824d044c4729a1e2f73b7a062e0453bc7369de0058a245a01c31bc8c6caf2e0a699a0e7029c53d7ae208a15ddf080b89bed66b19dd8626734503027a8cf13909dc29215c72855cb6a15c76a059092a61551934996b0bd512edb37b4604f3b7d859640f4276a323513805c30a99050772d572612161d634f9cdfb5f2e4aa4476a15258ee34f6fd921b3fd4e2c2e0417ff9e843e66957a34741f21e44b90d382384eba03d7737b9f8e6fa7097b2204924bd4fff616d196dc60337be6d30dd203a2a041e97fa9e55e31f5935b31d706cc3b092aac84a0beb4bfc6e1d761859839e66eb2f4224648e0877cd41c85880f71f72c16cba69136f74d30b5b54027f2871f4bd43a8a0aba6fb56ab8a28f6a0234783a1567e04129382290ef0791cc42b22318a233c1673608bb22234a5fef01655efaa9aa1c9007542a91724676bc0025f53682ae6b38fb98207c06da3f090bdf4865912f8d132ac7fe0dd265a54cda5b20b85e79c68f9b28fe61c87b2ddb0626f8e68d2c20cecca288b6e5a5b4b74ae2651c8d4359d74ca2c779b86d2e23087e54a51b9df8d7c95df25b27beae555165df545e8105fc8dc357c3f5e193b509ff642c412756285b393d79ec18db66963bd3dc65804d68f0b51b2240f77e6006bcf9a248db76ca6fb3fc7bb6cdc12a734bf8f05e4dc08ff0bb3a743f2704f80cc3eb66973dad89471adfc7eaf64fad6c73becc8c05fa998c4ba4b34710079007175d836b117802f1dc6dc3fad1c288f4f7ed7e36c063bf8824951e3dcb03030e508c88caeb7452ee659a949abf07aa5658e2891a99bbab057b86e54353c5020289ca240aa7f7b3f0575626cca70b60c2f1b89bc18db1deeaf5bf71eceb712f042de06952d2d858d9cb93f589a1ae059bba5224ad8000334cfff665d7e200830e9b6eb1983c7dfaa8152f6fa1966b95ea7f4a26b819cf82c776a12ba741a4770fab8c293a09b6fcdb6b8c4662f4591485050cd357354a49f5ca8dbdcb41e1320e1418c9ffd0ebc95c38a15892eb3739879c04e06d7ec4260c6239cb88da12d40b2be973b610bb2c72f79a95bebcc4eb518b0cb9261ba703710201f93062e046082a11c0960b0391074a6c223ec3ad0a0cc1f6a1d9110e834aec21ffb0e38d970907c38a78e9833dbe7cf14d75f7a360dccd7340439a4dbc60bb448100118ea2018816f606589d3067d069e5d463a29a1f70090801219e71e4a20e7e4ae1ed060558c6de439fa23f22b2cbc083f780d758975f7f461e753c8c39072419216404056158a81a53a79f30e93c2ca3958949dc7408a6ee9e6eab89cc74e0179fa57b0e4d39e90bdfa44eea15578dbe9a62c2d4bf2a63cf2cc9dfc04c281be536437e07d20e2b49cd3ea2290b1f4f5bf72449104983d707129fb3a5d8e04d7a304a6507e9de5dc136eb5cb087f7c8dcd3c832b4f6fda73175567d9b3a84c7542d3e470f70ea5e997bdc34b969df5edf19c19ff5bb7ce52335f135b6710b142852b3720f2f242bc8cb08aa8fc1e916f33586c982a7746fe33f242c08219a4f141a66510881edfdfdc55db9c1629f1b6085e1571e9c50ec3f0ce7dc02cc10e2556991ed0bff02d7dd9f7c0cb812aee6dcc209e37d9f2f87777b0abd8b4105b78b406ebb04ddf9ce450a033a29990f5ab2368b89ebe6260d4bc08e9c1f0ac9c41928c9810bd4351bfb96ff8f53be4f56cf068ff8a427a35f3b62f0617ef2548bb7b4292a090a2fa3027da5415eca3dc982eaf2a8d6155d27df9319c3d7f28cce910ecaf903bb51e38ba70a61254d7fdc40304e2628eb8e7848c25bc6baddd0282e8169e4080aa169cb39e57620fa717208f0f38cbc2f5af1bd13958ae922a9c9348e4d530c688e7b03ffac501e4f84a2633cd40bdf38169b11b0d88ec8a7fffba68c2bfe0b552af6acc6c91c4b51f9d91d1f5697900d237c0249a7f28c75eef6277578ea0b5edfa9298293bdbbfd61b0a1a82d31b86fce6a8dcf3e1d22ae2c2fce1c2aca0d59ea05e508f678c8f5db125bb3b4b7d6bec3e2bd889d700f7f96b0a4f7daac0b7372dd9e2777589488437f2f003e831d9cd43dda88df493cfb60ddb07f53960560eebcb7f2e29edc79de33a4347ab289f3cb2a3b1cc9c176b6884616407772067d2ee47ae20fc6dde97d1f162e190b3984e75119b08510a9d77b178171cc2d00095f06fd06b25566ee66c58a57748aedff3e8bd5b3cd42cb3a9655d37e104703b277735206dc4e8be5e1d54a9eaf14173a9ee8bb513b6dcc60d16f4f3f8e0509c074b5676b59de2b57694c6cff39e2844f8ada5fc65b2cc59e87d84a66dd1f0f6b92c90a4957563e22b95120cbc118a52125361e3cf02878fa5f7e775fbc350e41ac0d766f606bb0be7d3e83d7179aee828f98d0ae4d23a2b52848e3f7a99d03bf83a7d177a2cefb8af56eb9eb3a772e1901357fc5b45174c0017d96dae3d605f97c0c99f335d9e328ebe1360f4ea840480f2a81661383babf0637feb87afa05aa43619bf983abc7c0ba43aacbb7206de00a9708fc823981caabeb8f7ccb93099a38b42fd019cb72269c6dffc89ef5d2eef3062df7745fa624a4747b178ab6011fffc8d450a9377fa4ebc50c101cec52e882711b186a0b17ea623119d64663075d0ecfc5004fe57da429e78f19c0fb362c15c7cd3c12df22f4241350f3815401518dfba7e1f6de0564d10b049f1aae96305af80ad79a0f8224ae266ff6ff0a05d8f1b2f6ae9ce035d0c60d086275d0c1724d3680188437ea74dc8926961e1e0f8608eadf0515ffbb6e2b5a98a925f4a15c5bfa7687a3e34360b327f380b29b006332921ecfe4a058ccd0336c69b40ed35b293c31207db0065c2502d94ed40909290a84e75726471870b607f1fe0607cd96f7138bb78a4e7b626280a88c85de01ed0e4a9126dad259442c9ee45767893c6ebd916d74bf07baad23b8a2682ec902a4024f5314c47ce315c243a3c2ceb73dd69d264c33b2f1f19f4a5e08da8c5609df848d6f91632c09979e4a3295e268c8c09c10bddd549b300f1e7b93060d890cc813e630d6b8dd60ee9cf872120d3efde835ddbd45183c57d98ddb4a73af9005c01486e813b718eddcba3def379aff3a421fdae34e2a793ec776cec59240950aa260870178ac87472d9f7985b6af68cb3d4ff1568ec341a94fb95bb80b010a09c08ae1f5bb52a4727208923735de6f4485614910ee42327b9b86a2a286c2210e22feb9a6bec1cfda9822102717cde53c5c5c71b053577371b83dc30bdbe3520c6ebd64c628fa40203ad86afca99f7d3ce66f81fdd4fb91c9bc56c88ec52ff60d84517120e8c2fd6d38b110dec29d0066e4d0fff9ce00c64261b9148c7730f30898ebb22269f785f8dfeb79c55ac6f2ce4dd43ab02c000979e56bf933cc84020742fc8b7c050431c0c0628001676328310d6162baec42154ecf2d76a554117506939132385e3d40aa5bc00fc1b2b3265258ebd0d0a6038c7618ef9852cc4ff2038a221f4a4e95fe33af5f259e3f786807226ff4105cffb8949c6117199a101f039db459e330617b12286ae26c54ed99a73f48c77adfa1eb379eae2b272cce8209f89f0214f31595bb9bc8f39a805d82ec20a67dda2f3e12458a4657b9e6f46702527f684c7f7c4fe36f3122ebec01e11500e18671e4c01ffdb2cfef3425c49603fcea41e627ca0345dde33224377883ebb6ce6fa96f487740e8b26325eea5520e7439bd3a2ebf48f9d89ef48caf4b586262071920ab69550f210dd1ab1324c90d2b2a9d18210a523b8b35cc6c98044b598b5d42293c67b237b1e81e119f17a0020a678f0e6bd9e724fc3ddb57415788e59c42771eb5366791d530a537201da30aa8d68440277d5b406eb460002101e80c020568f73f6578b89fee7fb7e344a902a6f7c2b73c6d0bcebd2c173e9c313694683bf9b47f4543a88c6e7416192ab90354a208f46b405ed7a71be8f4fd9c0836714fb90a32cedaefdc1428f1ea182b06565937dd4e5007cb4b221a13926e586a7792917a8b54c0bb985b6083bf27ec0e8daf7ca3046de6692bd9eedbc2de82234355004172f4b841dd7fb0829a9115069e5e85469f239c4c1ec53bc3690920e876f1ad2a1180f9a1d28e153adc4c390eb05035a105d0505fce7dcd3ab6da0966f151e143c71af1a5fc1012e9c6f526004b85393880c5fba7a6dbead1d6d312cc23881f28026bc789f8c8738df5631f02a4e7ca26453144dbdecfcbf6608f0e36a60f716869d6c712cff206946c45e1073119ecd9be2296963631405b9836ab45de21f5bfdbe6d582489572a00f04ec08eadf9162b5058e4240b7f13ac7943c514b0786186099bd38d30b176d04f6950daf78793964d3fa4b2a6505be9249dca4a6d3f7f85e099de7704517f362d76f071e1e00f5083392b6eab4ae88068e84985866988a364d53eb0dc6d6edcffaaa0b1b4f2cc174e0c23f5c7c7466d8030118eecfc54673279a372ead1e4f64cce20b21362803a98acb550ce22041f94046b487b0146ea47d193976a634643c84500c6f7e773742b5cfd949b709fca089b8be5e156c3bb7bb54c24b5349ce972ac30fe2ed1063b2c64c30d33f4a1a3c2102080448bde60b284fc07c4e6127ca58470b7edfa31bd279d899a0f131764d960a724df038aef6917cb41421677a042b13001e6657a896020612a8283fa2612c3253694796d228e82a50bc3677ede1e4ea0c5129eb0a0a0487274c4327d77fa0b17bf914846651d70b204b3aed51ba3b560591dc74fe801c39e225fd5ccdb46445accaefa6655435a0db51e0d09e6fb54d0b3417eaa20d18450077b25016d9bb9dc8f8432394c062c43766a8837dc75dae03d0a83f72b76cb66d88d2a0695dd00ef3b9d33e60274c1cbea58043646301d50b4f4ad4cad1b7b4b44f6f56d58b6f971f1305c5f28939c44b9f4ff854645b52071d5c4ea7e75990a360be9ba7f4a9ce034668bf08a444ff12b1f916528c4582050de82fe0b2bed2e1bc6f08a4b674ee45bbe5797178030a3d7e98a2c3667dbe0eb20381123329e2e6418b0764a367817c1405981b0447867faf4ec31752c3c5031e93117f293dce1d5427869353ba792514b78a13c0bc5afdf19b9ac1fc58b20c3193ad3dda841e84853541945a2b04f99b1fc3bedaf62242febe01cf1f44ce0686f292ea9dbddc24e6ba5b00c8b4c07f394ab468b516f9fe929464a6f729a70e8b9e154db689dc3ac3d0ad161778346ae66d763a5c22dff60884f5a539f89b0e685cf0891f2b525260b69bd756b8f8706b1a75b9753134c3d6d7148136323a083849eee8e365c81cbc3d91a1714e2fd449b0a1804e58a01b0ba7d1db80953b05aa9f491bd93ee2be0484e7e427b1b3f0cc85f27c95228aebfb980d2f4fb8330143afc06476b755ee0c1615249831149578e2a45fe109f3920fef3a709dee3c05bf446548fada12c33785648f71092118eadb2fd6b4881e9038bc33a66070cd039054049e4918d0c045a49ebe3a4928cc985bf984bebfdd08769aadde90db3f0fc7c0536ee7f7941c9bd09bec81ac147ae98a7cf0bb130be5730592c5ec6db60be21b0a2e4e0c4d4023d9478dd8bf61678fd5472377a30d9f217f6e40deb2e206b5bcfaabebcd895577a2a6d9d0eaf3b758a5152263d4829e00820ea4726c910cbb677353a21255f8f386c0b0d6aef510ab29511a38d6e0a9284a43b688e4340e0316441e19287a16d8fc198d8337eeffa8de29f15e3570a32e84136cef6d620b72f40ef847aa6f8c8dc9561ab7b4edc619d4df83a2d729283d2aa2f08e82210e8666eaade87f3b4be9dcd4d9ffaad57e31a435a344801525c6c3c5027f838102a3a6d3cd93fc27e8327c1b75834cfdc7e25bb5f8363f92e921f5a72d4ae4640b78f4c282f86e9596c687520682d1cc456c621edc61e2cc3686d61de065efbc062774dac9d398854bfe20889873830053e0eab260dc47176320326a1a8b56b00110bccaa8f7421898926fe25ecfa2023b5c0f48c30bf7d56ec60061dd01f52f01a59e3be41c992fe5c3333602e877d572521e1995e996850e05fbe1abba659807cecc58f0b71a64e459a55d3280677bf2e2a71c6b26214b3b9338df50f305f80c1864370b16a60cad1faf2877f37767cdcb8fb1883aebf79c6560a98ad874b10325a75f119834e46724c124c0dd0b645b6f30385e9bcfe0eac5998bbd5996f0004271e2fba0303634809766e4547f4019a66f7718bd3884dadd1cb98f24a6740bff402b21c7c3585bc51f805ad9bbab444b4c3e1fba4afd837b14fe9f178de4063db99dc2a3b6ef4a01fccd7a361e191b5b25f5ae391e670fc54524d5cfa3fe0fe3a8c6b276ec6ee4a717b38b19a61b0f335facbcde630c16521198d290d0a902db28fbf099e19294d0c22a80410d73a424b8a4272213ca7339bee7945ac8d00a62751abcdb8d1f5d7553f0e115609a250d8682e2fd98f9f2ae708f8247ef2b0f53810b12e3edc01d47f2198080fb807d6e9d5e04b6b8fbe3c7e948d6125576085ce19fbf4b2ae2b62a3accd974519b7038df588bf15c36e41f9ee549660c570739ac7c2ddffe5721f06f3353f76bfbf6809727d984c741f88ae7e012e4c0370c756db4e3cebbdc7f45f17fa633f6d5382ef4763980b60b0094360be45c7a7e0e8ca782d039eae2215df36cc5996f42ec9c0c77f0d566bd4c1af69ce16c4ef80a18dc38846760d80efe7728db360aa6f023c80ab2879d7dc492481043da446108f9b6f7c88d53b0884cfc211239cfa2ba86fccbb8999b98308e50c4579682a10889782088815f930f839e2ac1f1aa3e5c503f8ad3704c7f11c38362fb6f2e9703c1a1c6c99c0735170a921ecccffe4a01d2d57e73cbfcad4953225cff4ff6590f4ade83964f29afa9e8bd8bbf9a98ca6c72745a47238d17de2bf94e9096145e02fe715634ad86a4080343739879e425199750a86c2a4ca2a0160367026b573e0d9e159bb8e636013370c6d62c7d4b240df22274a064ad26febde68c1877989a0c78b4d4a6d58fa800229c038fb744fc0577f6f4c45835c61def963038be85670a7c0b0fd1f69b96b64cfd37fb09e5f7e807814abca827497860016ce91ca0400b6e0086e5479b34cf2cdc7034b5e1e3b06c622cbcb5dd03aad1cb464588bdf50b4bac67bdb2b925ad91d8bd8a4501950db5f289bb743bceef3030ec1f88cf150c1b43953bb4e37b788ef5c6d079beb59b8cd17437d07abdf2653146425732fe018918c5cf1db9734279712b3e06d39222ed5018a7e3919cf0122423da670a7009059d2b18ff39690efed9b3fa53ec5629c5861659dad9c35d9d5eb210d3e4d409a64f9d39aaec39cdb10780c4a64a7fb5caabe35442c2efa4c103051929a1570684d54e1c9338708f1f9d615a78900e0850bf2f7cc7be70847c785b48dfb70500a799350fa45c5dc5493f4181933191c9859cf09bb39f971bc55027ca4f96ea004d3c6dcdd3df73b06e1978b5374dd92d3d31dec17eda88d7926cb1c33647e305b104fd11f0a8462aaa6470277622c4599a9447c98bc2fbeb11f9ffab0148c5093006e00836c858ccb276c6ae3bc70266adb2740159e18480b505188feaba8904f5704eae8de73c4efa22c937bbd5496413b453f7502a715dab70b02dd91d8501e581dcbe8bb3e522b93d748c6c16e7035abfa39736b5d72fdca1cda28f9b2b0aa75d8e236e44ef06d76754c2c1b2c5b7647b8dfc7c9f3bf5617c19cb2eff6a026cd11ff97a49340573abead4aaa3115cec3dfec86a8425a363c169512a7a260de629582be46fec4193168e8ddf97e4446ed2387672d660cdf50292150043e20f0cd06b966080c27880e0a32c0e992d2dcd1a7263d3d7fbebbd638244cbdd2601a81b0e0449ac16a75a9b39f937b5d57c69c37411ea61fea1984a32d1b868b2074bf83f753c95b7daccd6e6a93fb4586b49d959e03e7a8b0e70bd08779c473609c1867364c63a9f8f34d30345d1177a4cdba348faff3d98bc77e6411fdd320103411a025233ffa7fb813922761d73452b67510400d3ab453846d10500347ceb04 false -check_ring_signature 23a522570737a8a9d7275e22bd68e24ad5cf13d804f7b32d080a390fb9c3b95e ae16ce8bfb6dc0394980947a20e225a078fa18a81be252146b58b6d176f03517 27 d0125e08c7ccd586cd1b36e40baf58339825db8f9448a328245aa511f4d91af4 8550d71f746002e8e86e6e189810898c205cc5b7a817af4fd7ffb9e6585a54ff 84fddf48a6fafa1b5ffb519c1ad4666bed503feca19e0c803f1c2ba3cb990ee6 52dcb5a0ca3d361e5fc842bf143f5a852f2ebf45cfb112795ddc1f93b54ba2c3 97b751b04eb7959e7a41cfc52091497a75793184c269d27befdcc6cd1765a9c5 147af8836777f2e65e0044d82d1e01d693e974fe674b51f796cbc5036846decc eefab71e6d1b464fb840e24548af17016d2aeeaec8e8783a36691f6f2e8701d1 f22465958702cb43db5cff1f843becf46831dbafc379a2be30fa1a40d5508ebc f89918df8e5df18423d4c2e6fae8ebe149324422fdbc98fd89686e5f9b791f02 e47dc3f6eb5d8d9861b338411b9168cc033e670de0e438053ff5e65b49c8a776 95f1012f7638be63b46767e085d0b70b9c30e1ec7d8eee6ca804085cd331729e ab8983c6213fc8459b02079095f24547b8ab729406f2aaef3eb08cb043bf8c21 2b9c0ce7056f933d7165cb62554d33bb4b15c82ed566c080dcc07a3cc4c96e8c 2d0a8d4e0cef449c2a751293d153c8ca3d58c1a893534b139032226470cf32d3 8ea623488064f6873ff38fb366e5f5ad1eaabd3eb76d21d8f8a794478cb9978c 4092af7126aaee499cf2d4b463c86769d4f6c707cdfea28bf097fe5dae77bc82 027c021dd31fafe2bbf4907cd34d84b6ae93b84b1ecc5242ec66220a4475db50 dbe971295754243fc4ead5e324c6f2f8de5c9ff9e3757996f0dd8831c273e848 fad7f329e9f3a20501f9b4206c65136f41a5bd30d93f07bcb6fe62e21ddb18ea 343f473f2bc0b772f0538a03457a1746a3d2d4ffea7b795604b48cdc150e6258 5c5fa0ee9b139d1636384d84a9db39fc472e796f2981889feefc34921f45568d e7af8dcbf6463ab86a1de5798f3a2f75a5cf7ba71b14dc71d09e60980837f21b a7872924347ebd21eaf40201af3f0a6c94d7d0509bd3175e30a79f271fd5345a eaddd9a67ac7ed0aef9fabcf66b054142200abcb8b74ec5807d8a50604219e54 f7b05f69a278e3fd877deaeddb570c75b015519d2ef8f17da8c2ed0c6d9dce90 53e634810e4a6e8af3e8a0271fea951c9a93cdfe775e2d7062b712479c08c82c 076b1843c4e8bb72a36a872b1e05fd91d4344e4039a291fb406d69901137abc7 79d92d4a7ae8b805c616a5a870d1c7d58c5caf74ea8aeb0f4ab84dd1adac910f105ba74f4b666e99f7f5598ef464db0debb2b8ce455962dd6dc2a32f55b427098ebf3181d2d302b3476f13849f6f425d1ca651199d93f5adf183657329cf8401e9d6ae9b702591e90ad8c62c777ce7dac69121ef5af164dc06863178c9ab2c0253eb286cf393a00adaf2cf29cf928335bf062aacbcba84b3f4b101be4621a8050c6a47698beb42aaf1bdbf60baaa71f2c8749b350c0d8f64d7559637a0cbc30cad267baf0559b05bdbc1a790f2ef15bacb42d1bced2b7a4275ebadb6001eaa01e55e6bad25e356f686b364169e5698b6ff49239785361aee31a33e2f05767407000ad44b8373a6b1aabcb3b2e172c8b0c8ffff3c1aefa8f787aeb228d16c710658023eadb034ab0d6bd8ab99a213a732615216e3902eb2496419b0d090de4602385c6233a05d15c381ef3543831dcf071f63a12bb40adfa8f02db8f20904320b4755cc9a6c79c55ba29f4e95e51bab2a0a1a0f5da07ec4c57fea6196bcb78e055cffa80dedfba887b34a3e647ba0de03872aeeb0b8949418184915b9f64f210fc8fd19ae945b90323d29166d844c83d6bdff0f57e307e9788ebb757b7d50de04dedba05caf4730df80ac8e4d116807429ba584e41cc6c4ca1e92c5c62e749c0e4db41280c53869abf79b380fb457cd4813efc4e92512e651a7add9b556862e0fb700ba4f341ac34f03ee4a413d4ab1fd26e3bfe68db5d8068b79ab3aa58b18018277d4cdf2449412deb8030bb82453d5aa6a1d79c517daf4a028a9fa2a51f70dcc5b3cbda122638910c088ad3ececde93f6320d00d617f1007929e4abf0f170116d3c292750cdf4ceb5602894557f2c138560a8e2fe08ab3ead219cc328d200a8538a4fa688cd8d03c5dc5cb06aa038d2e1490c1355421c93b582fa4df121a0e69ff433c29f4ac98065450dc84aeb316ac51668a65306be646f8c6ef6403200d07ad9358f6ecc54e11e98414f323d049daeadb114a4b7ada46ae47a1bc8d4d0acf761a356741ffff37f0bde88fde64c63eb3b01490aa71c7ddb5f88d85aea70194ace466b23cbd6476f9be6d183415df56dfa7665a6f4beaa09a16d8ca8e360682c6de006f2faf95f7358f6036d1b865b686c4dfb559d37ae367591adbb1370ec7c1f8faadb150aff0e1450e92201125719c7c41e22698a292848a4b5de1e80f616c5a7a158fa5c79263eac3524aca24ad5900656cc99d2e3b1f918d2d134f0e7bffc1be97f41a8ee267dea083ca57ff384adda434138ae7c6ce8f81173ff20cbb76b70a5eb8b1cd142e50bbd94a8a7671dfec0e77fe49d7f5c18b6a199f3909212a6a571fb6912faaa48e9e5aa2fa83cf36d310fede4631b2ddd417811fd60bd9aa843df0506698453e7e129a19af5d7af8e71d135f141368c35ceff8048f08838ed7266876f54a2bf6fccc4b8271a3dcf38c6fe395037163aaf2e7dc88aa0d82d91f4adbaa9ebdc79b372cb22603c6b710fa8cccb1e02c6f9d8c0a161b0e06b9beb9cc74033627e85bb1a196f696a0b9c2fc73cdc4aa4f391738d02290790136b2ed5aca86dfc7c67662b20dc501ea29a352853850602561196b55dbbabc02aa5986466101934258252cfd9b8c70a85019f4a7e72186eef34eee4e0fd7180be002b4996d0f6895ade60d79865397cd5d466c4c59c20d7ac30fd8bbf6250d02ed014c42f07b606e225f59b669300dac8e57a164643059cc90f71fbcc424d70c8aca017e7d434faa3b32ff27512f15622134ef39145a6f2be3a1ab24247d9b06b9f0f91a619e77407fe05ce48ba98ee15e5253bffe37f9abff9729b438d93807e3c058dc3df0bd3b86cb8df238b42474faa10874e1449eaca9192acae4d2db0056b15f1c4565b3a88baf257198975278b0b77899a22b105f81bcfdc773240508a8296c54f885a846affb2651905970478b31d08dce00b9ca131c8354192b150853960073a30213d47fbca738f444299322d5afe8ac1d76fa5e05d2c163e79e06ce062bf06ba029697858a5066b49a170cb18c651a1879d456e7c715b0bdf7a035c0a7ec4e59c718c74c7c00e4cd393fc4fd2d8b2c1436b134e0dca95b47ad203f8ceb3c2daa1f52bcc6d5166043bf236441210219e723318493ce0d7c10c9b03c8d13d7b239aa27fda8009945f1b59e04d206e46637da8066fff4dcc7de3e907c2090df6eccbec96b0f839a4293b7e9f5e418ec89d41dea776129fc3f333cc074d63fe9dbdec08d497ca393a9b1d874a0358ca46252c6125c27cd6445c429d02992b1e34b660a59f327c5deb38b62ea34fea3ebd64c1474882f52cab69bb9607431b32680afccd3d59e14311a80154eb648bdbde622c2763eb753ead3e6ba60f2bb56896a15de6a6e24b05352f6e1b33ee253d768dfca8252db4ad5086e65e01 false -check_ring_signature dac8e851f03d366c6ab7175578a9bd39fdfee7739e1149cb441dd1d4683b3052 e36c8c42f292a1024bf2662be9fbdb3a0924d890c5634c504ed7281da9bc60ae 81 6b886619358a7f141e17a4bd3edbd040c797682e6771dc6e77b270617b3ab128 dd5b5b52f25d7882f0cde402b2f2b8d891aabf602e88ed7b553ba2f048e11791 74da9d8b99cb8c43fc7a2da78c2606f781056303166cefa30dc0b3215b0d4d06 d0007e19cf11cf3a36d4a025117831c7ea4a6e8c2c0b6162717b60b5821a2dbf cce59856eb71d7df4d527fef5c900c29413228a9f8ebba2f47cbfcbc7a77082f 190febb53c9706cc8138840a5b54fa2f0f0a8c40fd9a77dd8bc64d1d1c3ef3ae cd59ce263f96927ac7cc5454f5ef68ea50b236d4a33e1df8b40249c0cf3c61ef 4e75846995a6a6dee40a9d92bd22357034f052573f7a91c08fbe2715bbe0cf6c 4c93f8a020275629783cbf0945579620ed17e08881c3f83cad00c8397ee7f655 980394e1e7975eb0d42a97864a2a298807856f9af132d84539152eb3cb1b0f2c b8b2216d6abeab7fb11b3e58a9f278972a6882cf83d5ee86f035b7bcfe50d0cd 5a6b762ea5f34194b3aaafdffa94668cc931c83d9bd69bda7eb3707d77f726dd b4d83e9a063f7f5638870b16ebb777bf78ce015379fb0208c1ace82d86f6d2be 801a76436e4053b33979b87231cb7965fff16ea4a0206502e22ba4f56f09f999 88b62acc70d88a0ce0cb89ef08360e55c4de32dc0685ba73737e8cefc8cb238b 291775de4f254b8a6ceedc2144d24059d751a376aee818138f6d0ac607f11c41 48dac887cbfb349894f47e6a16aef2586e81d9321ed87ac864814d4fd3f635b4 581a4efe7bc69657287d12ec41c65fe9a673617923ad0efc7b2fb805b3a77756 0f690e56e68b2d59f2f8768a639dece865fa9b8154b6d63a5216e8f21e07e6a1 48bb8092e81b4f036520c77179d935940bd1fec9af08679886935c3f4f7fe9b6 5d0cce8c1a85bc861a11ce4516aab81ea2841ec9cf9a0df9fb95d0fa52b49232 bbbaf8dd613bc95fea734321566f14cac992e00a8b4143bda09451d3f4ed9e61 d892e9a7fa21c2d2677a96eb40ea61f30d99ac0f26a04889c37677218d5b0ee4 8ff87ac8920d0f9cf6f2b6ab90e8a53fcc3df1ce8a583515332ff719494e90ee ec000a43db49ae57f43827d8adb9251d7079e554d4bf911d11a919efc2756c23 cf6d8fbf46c913f08cdb48c692060328c3f98a95f81edc46a230247607de19c6 fb7d57f5555d09b9ec09bf606fd35d26fa1684dd5c5f2de7a9222b51ec8a8498 02f40bd8d10ebfb7bbad0aed4818076a81d82081b16b30b0d0c9e6f57f6fe1d9 7a0b6737738c0f478f9cef1b297c73b9d518276d4637b27a426babf60c61155b 511427b5aa785da6098322ad39a235603f30b861c164f22dccc9ea126de906ba 7aad5a146f5df617eaf15583cf24e9b6f3a176ed51e5b0cd03a6d3ced3a54491 5824316b08304eab88718f8393ea7f6bfdc0fe298982f9272867f3d814a385dd fdaa8fe2c7a32f1ab3e9aaab84e5feabb96b0c4f43d7a8908ae2ab253790886a 3e9548514ed02e99f5115cb788611e3321a5b62721247a91c48aac16ca0be7ac 085ae06d9f843d9c668440b7fde9cf7e25723f94eddab31c02df1aede298fdfd 4bca4450043b64e1db8fadeded0053537a752a3d050e30603b1d7a74f245b6ea a63a99d19e914a98fa59eb3f966ad076e0dd80331e095c4ff75575ceadbbb6e4 2e1795320ee28ed38325f9f602ada264f61434dd12919712995eacc58d096c97 77013ac5f19d45aa226d0cd334b45ed9934b0242a9024a0472671b5600570e30 f104c3f2e95599f94b24d29a11846fb58fb1fc57720301411773a60526ea8375 a4ee7baffa2dd47cd691535be9028677562647a74615fe835f799f701e57df9e 2a274bea5a3bdc0132fd38b04236a273b4bd9f463b6f24d1937d63dbc530f7a0 5e76e39c89120d374393754b074711182ddb993b2cb0453d8ab735b33b76d19d 7653b9b706337c162f7d6638a1dc4a97fcad82a906f84c33544ef65733f0b255 eec8d6b02c7abc09080b5df210f4eb0cb317383a2392a9be4fd80f68e44e4ef6 87a512b93002e4910ac8a67e961813f916dd7519cf86009088d6cf7ba9b097c0 f8b2e7fb4989e4113b1a9b67551ca05d73bf00a404dc64ee07523d3504a1c192 d3f27fa98c1b0df5779469f9bc3c3aa7499fb037f7f0ad7a6f4f2f425fa2abc7 33c86146419088d015dd2aee829604833bc456957d5250747a94b3b3d4185832 4f4985024e667adf3ab259acbcb0a13492a718328b2240ab2471b613dc05aa75 3517344734c8b6725e97ea37a26e45c94ed2f776a46684f7f652184c3ff7d18c e9270e774a09747f3cc3b4b826425c325d7a2b8aecf99b61595a2f5e588cba48 b6bedd36cbbcf139faad054dafb0f5e167f9b4f1f424fd51970b53498599121a d0ff4df1e3d865ad6313ef4d599513ad24356b28760f9b02d443c809689a5f9f f7883151ffa66da3bd7cf7b5823261bcb990dc8883182f3cfc956d9e4ff6f4a0 184a25170d7a09d11c9112e90a94af22646360b1f4733db6863161c2dae157e2 b213d72170e4c6a95128af13a5eefbecffd8eb6d299076fd62128d2a8c8b1a93 c1672e9aa7a965519728f23c68b90834b2a7dd069e6878005c8b2b884ed41aec c3bfa855e7b94d1c7a2a82fd28aae213824b111dbefd48f8b451a10a3bb9fb7b 9ae2a2be33b25fe2b4b5618f506665b96f9d64e22690a443ad010ea3aa4c9941 dd92aa391d0c41a117b02bdb597545102e880ec1129deb7520145adf93c025dd dc6d8ace123dfa6cc18cd4ea8b62e443e4c8a03319ef87e90499a3f96b22acdc b92a7a9224e3fda0a3a1099eb22812caa5f1f1ed364f07212ab5f9eac52ffc62 1798990205d3e15917987bdc4c2b9d5bed0a7f69f70538abf7b68f95fe07dd7c cf3c1aaa641eefa072340bfacd3c6bcefaa114404eddc34285027cbfd54566e4 78fcb481cc4995c8f48b91dd72661033898cc8dc4b1458b59393ed00ac34b4ca e77e6b5fb92eb43e01e1d1f38c48390b71db8a9423f1c39cc4e7f0a0d7c5dfa0 1c21d96673159c2b7be4bfa8616fdb47cf191c149c2fdab62ad85ba186e4697e 0d0093d296de57d0a0e6e8cc5a7e234b9542388452f2c5bbb203f02a014642ed 308fa19978e30671e16cc986309348a531915a60949ecc7b9bc97c29dba945bd 965d51dc66e458a767a7616d066e36eec9774cb0e011d73f3746f819efd1366f 7d81f43b772d6ed37208d96e0ab9db76fe60b6596ff32c7fe743e87ad16b1feb 23c83cb4b812a893534eafb48685a81334fc74e1e8b3e085ce81ed7b23c51ab9 9580470ca5da620d46ac874e7ec00977d59d4a6879465c4a79f5a325d41b1a8b 0e0441a454b02bb845b25ee27ef073bcfd125f5559981e3aee9943af5d5d29eb 3de74004f540c4423d8a4c51d2f3118c544f3f980b706a5501d398c4ea9be56b db0cedb80b88d6327431eb30d118f86759712f5bf0d0388af7276bb27d16daf7 2d3e5d2a19ca26e8bdeb1610a8a1c46d889b55ed20c6f322586b31fb0fc25e43 166d8f46007e730b51bc4870351829f199ff4369144bb64974b9916a80448a3e 800e18da9b2aefd399dde973ac738b58adab8bcce79fc4e1f52c6a9725b00384 a844819194412dc492604907ca27264db48aa083a497e6b2bdb33359f3ebbcf6 c03902f7464422cbbf32b5df8c7295e4d654c93770f68e85f0f1ad030affc40bd206ab94b185a66271014c28de1247ddc054eb4d1012d97e134bba5f0c96bf0e476c360d7cb5ca9ff58c68d4e68f7990b8b6b539e56bbc66d6c51f4ba065bd0c4b0f5cebeb3eda9a69052f3bec3647628f29ac2c19f31df14d76bf93c37c6d0ab6101d77650af195411254b5fe7622b2f540864d4e2b477ff44bbf80b7d8a80acd24ba8faf2c629624fe96288d5366258dd174f9b612ea18d97effb22be1fe053e6b367231a8200e4b317ea17823d63877d66fd3ec4402008a558b47adb2cb0e78133ad6a570eb51f81d7c5de245c43f2d9fafcc57e13ab445387badba2fe00d9083f5ca128d602138d6f5e378d665a7787e4f38fdf8247812b76fa323d64e046e65cde28ce3bebb688afcdf385e77292c2404003210bce0a074c972a9beb7008b0b5d7b9f23fa461fb9be32f57c594949e58ea4f05ca3cbad65f853f7a98803c0f2cc9d0365c5b04d22e858ea722a55d91d0ae057e108bccf4a5e13cff8d1095cb85328d4fd7d7fac91ac43350b61dce9a3c46a2b9c97c9cb1344e7a584b60039ee2eb8821e53d34696247ae3cbf1393248f182eb53aacb642c77e03ed8ed060bcf2d45d3858a8891e0aedd6b1a513f86cbb13909d24608749e05fc8ec6b20c8d5f7baac4751e36fc4ce2921cc83eb188ff63c7c8bcd3d82bf4ff8a9517cc079f3cff11c1630b5005d6e8ace85bbf1f46c63ea2a549f5925b575351f193cc03fea9a8debdffe24a1e0bb0a217b4cc8af00abc499733e4af119975cb72500b0634cb7a6e697c9ad65e85549b14db02d0099a5a3e7a28fa4304a88f2fc8b38607ff92f19b0e37838346851e3d1d05d3c2f8919225b206655a2dca0bc387607f0e7e894fd0bcf1f0f777bac8acd1214b032e41b8a0d6ac8d2ed7925c66cd13900cb57ab55bacc3e5e729c2a6ebf9750482332bd03867eaef992a12864da2331904b7f4cccc8ab845731785f54488ba7c6b8bcb3f4fb3bb14e0597aef2489a9730f7de56a48bf623627e49ad10c118486276268dadde4783278998773e9e50ba80595605a8912d0f19a40ccbd7424ecb1e530f918d35a05babd6064741eb5bfae08c0ef387ad9f9ca1a15bf94c4a09f8e9b3b49f9fe1bf4517d147b81c87744840cea35b5eea87dfae3c5ed713063e7c0c8fd4a9a35e6d7b65268a01e4b7ddfd105e315882af471cf13d72740dd69b8c5abea3ece7e24bb1e5727e30677521051035abe519cb7011af3dbab4a4317e99417bf1edc03f2602ee30a6387a2f012330ae381a3bc7c6c2e0ee8b6dfe6bdb871f0a2a382db168bd59f0e87dcdc049975015823bdf796474afd93a18ef8d101509de4f42b4bd60ad8bcd2b4f01ac32baf08469edbf7dd3c99da8d35ad7ec068afc04f10174f30db145c0150242a74eb1c02e898737cbcd0e6fe8b08cdf10b36f0502886270e5d11df9ef641c6aa4558ed02e7c99f8f82c85f757e25ca0cdd9f0fb26d934a2a7356898f40f426c884d79000fa94f199eb9421421b6d770b5e04e9b18dfd9da511f929d9d96345e7dcdb800fc187cdf24784673484edfe725971c88683202f4a3910097ca7525da816bbda08aa40283f057f5e03d058deb4abb08891972e875402a004114ccbbdfe034e8e02c377d69ca55ecc3eea4d29a60fdd51dfb095be718b88ee221ed7d405175dba0aa7af6b98d2b6147f90b651edd47bcbe53702ff307d106f54467116ce82e18503d6ea461425a14abeb60ed91a0ddf80061678188e384a1386da65284b92f76003983fcb16c9f955cc9cb636cb03ec049666b5687155a7d7e0d3f53cb4e785c4002313aefe990794a1e375d05dc5c7ac54351455a28b3f70c58296f2ab78755c0d0d2e6c19c8d4563553cc095911cb71ca26929edacc0672f53ab9c6ad9cfb1b003f5ea418a7458c265e6d095c093cda95e8da891fa499b29eaa7fa9c055db6f0be41651345e0bac17229b280f5e65178b472fb37a6985d5d0273332b4e8d7c705767cd5c69ca09d87d332e607fa72aad090612cf4a95ef33e10d057623cccf206429fd0b1be0b211cf8e369241e0eaf3842bf76dc2d0fee64e9e43322dff7a40cb505b68f40ee56f76fb59e94e1eb12e6fc65f54f2008ac7f87c09d82af89dc0d672e56d72ab3065891c14ffbdce10cd14263b839c540412b90e433c13e08bd0d37f7d39db80c0c6ece3759ecc57c73983972be3af74b9023abb9fd9ec73c61074358ef15fd85db4c7043907260fe1a973cd5d339e0073d174cd75f1e9530ca02023a3a975770b335b6183fb6e112770584161c0bb97ce2104b67151b5df2f205ca5b976666572bb7abb6aad08d98fa525529e89b7fad5039d14316d167ba620aae50b394a86ed658f8d58d949bb95f8a81be6538e47422cc6ba45ce666f3fb02a08f8695fb79e0607055fd6b1f65b2647760b53604aae2f53eb4c2a61435a50dc8b8a78a72fc9413f75710a73c39469c23b71a740db84fbad26c9de5bc7feb042a5af1f64b8df88fedd88b05a71f122a7201e3ce2aadced37745ea4ab9fdd10a0376189fd74062790324319e605bdf8b5c2846bf2791f7dde6381489fa646b0b931d90cb3dfd51ef895bb2ac844b25a1f14aaca994a901a2723f08ede289e2038fcf8710f4bf20705f58894800fe3ca28c2e12652dfa19fd08d67e6cc861a20210b5761870eba9ffd8bccbc95f8aad7f769b15d91d86909665d1ab17ca15720132c62ea02267a635fbd86e85c7f104bb9538eec57774a77627a0ff6bfba71b08b03fe6074f3243ad6d91fc5c2c9153b00182a2aca61a5bb9ddf4f2ca3a34ce01409452a2375e5404d1fb6d541f4d8996f53e598019581caa9021b059edda9a02065ecdecb5ebf57c58841bdf178c7a9930cd7eeb0300a24a9143f20f9db87604a5b82ca6d68d45d3f5ffe0e4a9436f9bb6f8c0d6a464c085d0095d3f0e7eba09c928cc8ac1d679ffebfbce2375902d9d563f49bf252e05c687846bf4222a6d0da4a4e1d3e2f0a673ce8392efead8fb6e5a191042b3b3b46a778c84dae8302609d91bb3d522521dda095ad78693f36bc3e48759b13c9950dfc48ceb528a03c10a12570e13ac51a689e0056ce1d23682ea048582cf9f04f5a90400708c9ade190eb296d110f2a011189244d3cca8beb54608faaa521d75b21e0228035e0960dc073b06148789ac2146fcb561d11d9779dc3cfe49e7f2033993e53997cf5973c90716dd54ef9af15597d1c224e9e4d3d183bea1f74997eef2a9950780347aff7d0111930e4922c022c95cc09765458a1e351220ecc4930b26903ad5fda659d23b06e18c17ca794df871a3a123c06352ce057fb1f9a4e5b40d0733bed0088a716407b2f431a1d1516e1953f6f4cb921eb7df8712282daf8a6c38f0b9427dfeb16605280042593535c48d2576e7b80e0546b66a0139b0ff9274ef652a0ee3df84e30c49d4a47077a7958f792c11fb61aec7a85eb84b6d934996353d6ab4b3c65a1f0226fa54adf1090f0df112d813d6a76ae066167552b1341844510c4bf7145d8b038eb048600e7a9c2412159ea6daa759cbbdb9041c7316cee81e287307a77ce10af40c5ce438d9aead9b25fb13fb1d7c2530a6c7473ea386ffeafaa39a3016120de46453b304df0d2e32529f13da4337857b68cb97ea902eb6b3af5557cbb3fb0323f1c9d19d729329021d2aa835d23f6298acc3f467b9963a8332cccd6fea89029782352b55ffd760310e0a201cf6ed9f90ec92f2427f1943bc2e1482ee7ae3021b88beb5319a8037521a9afe014bbba76b593fb461a156f03e13bc5e2480f204fcda6b9ef533c5854b7abb2db0b138e732135adcad9c6e5da0e12211117b4c000165c1565bf2a37526934a45257fad46ba9f8c8a839128544ad2ecaba50f3b08307b9c10091fd083323ebcc7ef0e4dd2c5ab582d37689905a86b9b7f170c8c0bbc385280203660520bc25f93dcc00b6d652843b3cf6942342bc24b1f750b51079616fede7eaae11d84528e8301ed43d38c307a5e6c196cfe32d2ee3ff1dd6509ad5806fd052efefb82ab68e5e35760c42a047087958ab77ae848b008c2066a0ecbb3c23bb5df7e56c45f7c14aaa68d5c2f0d4bdfaa75ecd002f32286890337050681a88de1b74b65814801f9456fdc6235a140b764ea5e9ac56acecfb038400809cf8d5ee389977985548f5ce754f6ffb0f6c4b5608d160831d7b379be20a50fae5e33d53d98d1bb8cec4d654e73d1bbc4ff9cdee23f226c3c2420087eaf120f4e145023fb5d8a0f9fe3c73eabc9f56b0d6fbbd7a7eefbc694bb38d29230ed03fc950f751aea4496fc7e558be6457728f9f5b7e9a51900f6eab73e5fe262ce0ff4eda0d00e57854776a0f4021f4ed7bf264d1199be2826b42b052221dcdb5e079a075210ea9993ccb2de0ceca091b00ac790e5955d7ddf52ab7e435694cd9d06e65981e12e093a51802032e6bd291d4a50f01517fca8160d282f7e3988c6da02e3abf1e2cda1d02855445e4b4b49afbd7719de2bb4303084c437112c7928860d75db7da40b6f2e20acc9d4862159dd8051cddecbf995d726d4cabf1512dd8b0181162f0c68beab2811de7e0672e2245754472bcdb2e7060163f3d0b6cb8f8906c64e4b106feb9735937dad6ec5514ce1cba687969f7304efb5f925867087110b3fb1b38be935daa70ab0b6fa6b0d3e51afaba2894fb4a0e5346ce744b032600a7b53c4ef2525d29f66416c44096ad7c55c99d018033cb8cbdf2a90638d839e0f319897b39cf1efcf6f6dfec2d603a219be87d378f7eaf22e84f64ae9b2e1ff0ea08405363f8fcbb1dc136a568fa99b32c767499897b38a61404a0aaf8472e60421826a8cba7c302307c16a38f59699c8d914f2a2baf29d3e267f0d7854c3c90806b0311a9a6b7afb9703ab2fff7f2efb94d8d5d3dd4870ce672fe5d196dca00ac66cd4fca86659b3d20571ffdc75839e54e592a2e2344938671ad4701c522c0f17e1bc1b7c9f4c381ccc86bc796b713942a1c0e7912273e9e3f417be2ff3410a5ee984d018463abc67d680b712f2c0a7f203a13e7f9d8f59e2bb9f9b6c56900d644114196d49d3b09439e2a169650828c1b19fff573bf77a8c546e288db64a017846b067a344016d65fc5716087c24d930e195f04e4a9e672b23756dd45c6c0e97e26a198bf1253946cdcb3a0b6ca3b39c9780b7e8dee27a30a0627cfd13c906024151a85e6482e1da905c3dd482bc79652457f5cb46e816231a301f72bdfa0cfd5b2413d939f55eed4b2182a7b5870493105fe6444d747ff948b1d842fea30eddeb72f1edb9d3aea5b9631080c0553006d8de8ad58d25e9654eef9e1636830244c53846e799e7605484767a9bcbb059db75d2d1d2d232a1717413c2beba6e0631aec5bea7a181c89263a278c3f498174c5130b82e9010bd4e08c7b15b50f0060f3918b590370b543bcab739fad7ad1d5eae32459f1c9fcdbbb4390f922ca20adf819f90be7d225615fd410eecaecef29688cfb312cbaab2ff585f6131c4a304d1952e3f669848bc0c4f26fc810593f7ab7bcd3569525bd891cd3772f62e0108d50199d2e7a1c01ea09a05701be0f9c43deff0b192daf4ac3084f389a00f9609f9eaf5e0ff76020612ef163cf1ddbcf27ce624e32d7331aac7af5bbb2705950c41054644dea2310f8e1c7b4886ba1e768ba0cbf6e7d064254c1a35142604de09ab349deacce679a9fc272edfff093dfca1777ef3b4ef622438d34bbeef642b0a66d6456a1db19083baaa29d7bc6ee4ab90c0e4a7ba173c7c3dbd3533cc3d3d09abff9fc6ab1c2aab5d42f54ce8ef989fcaac419f5cc4c7af00acb526bc19ee09bc28ab7e41eb20e110406fb9330d0898b6e27003e9f568c5a974a04c383c63005f786dce0853ef4a712b85d22776cb8129ada8c184f84fdd817c1dd662322d0abb28a894a829e4c61c3ac52977b5938746579fb3df65f168e8f92e38616be7020f937d880a0ca98fd53f3b0fccc8e6447203b85ab7f3380e1efb9c9ad417660905a500254e5425b59caed8503596ac187b5dcf010d758dacfcf14996f4378700583d9ebe23f2e02c782661c12312c251bdd0b164ded79916dc70493ab313e004d1e4d2cfeba8b2038790167f7c682196b9050468198496879be36d7a8ec94908a870b0d2c31de4b31b44af055e23117f5954d34240e2667e1009211bad8fac0e5bc67939e4e60b775f1e6d52d8324614e753097337a340322c079ffdc0da54058486aaa5dd61801d71bb527e815dfb4e0657523cd127c795e78dab563a63570f4b9c2271620484a2b4633fefef1812e70282ebc4502ebe5b22879e1771d12006e1851903f1362159f7720bdc4e733827dce97999785a388c5376094cca712506f8692b913369bd8427c8bb4ecef77047f613f0e1911dff1aef8cc514ee01060c3ecf17596ad53c777cad4644702eae2c388d465d807f792f033585807e960607e9c32f0b24f177c5aab2330a71c00ae9dbe45cb3a3a4d403f1cba0de24373a0e5906924ad4724b298597fea251aeb5346e0bca7a98db5fb439f4838f5d15210834c0598e0054bc95625082f3decb63e87741e49e9e36f8af8ce6e96636c2430e6ce489fd3a0ad0a0ba14219f90a2d0fdb05b9e36ce417de182ec7a7075f1dd000ee7b40f807bb8cac3330e97524bdfc99b3d754a3a5747034d62bc99b6e4e60d77aab76652a6e05d303199513b3ee691dc5544f1c45e777892bd920c134f4101c2268f49d84a5d5e7202372af5d45c5a86685255c3d2accb8f41a0119dd97a0cfe1e1149fb1093f9fc5b3de112fec927cf2fad4dbf1f6e915eaec782d78cc900de4d7c71cae298e77a80735efa227d328da5d1a5b3da17282124328cd36bad0a77cb0b675a1f8ca608925b3022455f3eb2cd604ab6f0d5b818e638dbd8f45a099750bf4413990b40183542da58539d8c984ab3d219fb8bb68a6de2eb60bf5b032aa28e494e335033507cce5f63b095c776d7f0096d68df83a7687b6002bf0306e6ac11f2435fdfd61143ef01bafec37750c00ae1818fa226b6f9e266ee8ca005fc0334b90c77d3c0ac3fa1b0283f0dc85414d17eb00cc1d098989945b7bc830664863c27b52c02f8660b0444592c1b4c3bd4827f751d8fead32b46364ff8570624604703533bab54b6f9b2d5aa7d97235aebe07c57f73b93f75b49530c39040f82f90692607720a11bda6f7d79974828c04dee708c1bf850d347b64c14aa7709b391567283e921e9a75b81d0b3ec1a4dc74eb74fe563b5d6b329b4d16b42e909 false -check_ring_signature ddacacbf70e20059689ef56490703de883a60d106cd9463609f3367aeb8d9dd6 21093235ebc1baa439f87123c4bb33ff80c96852542d4a7b417bb6011fa12d02 7 262d41d285382761e321fcef305845fea6cbd4f51062c562fe326e28fff682d0 7ce1cd53dbd8eaf4b4c5280308d69be10a403133c524b1b07523692b36bbca12 2f107b50d6a8fadd7cf12989c88c76ef56f28473d23af57ebb93cd14bd7dbb3c 53e69445240fba7ab3566401a735319c943e591c36b075584078ac7cd111e948 8793978ccb9462778094f20278627f7a8277293c8d4a4356b94f6232a6dab536 f26e3273a97e5c3522ebff165bd8e86ef415e6ecc0b7aca91089198c3ac51f24 84ac887ab30fcf354c86d2b44722ca02e5ad09319e8f97717a3a9357a7e4af4d 39096e9732b7f90ae79fcb20c90b8b783cbf27a7fa1ce28bd651fc17298dd606ee005c95d938e11b7b6f03dfb66bc99e860a2c690c948ae892ce45c34af0d502771ac57d81c80bc8cae326b77225442817a3b4d766b0802340f1ac3d1ac4b909a243803acdf10e35702ed2d0d04603ebe2750e54d00f4c037ac02c425cb74806de54d3e97844749a88f40b14ddfe95ad7ec2d528858b4787d39a3c59f017cb04baf19d939bbbf3beb253a375961164a4f0f4a724de19edf3f04830a6cff86f0b494faf793089fe1f3c1bba6d978b3995af2d286f4567dcbb52957fbb6b69440f326e22b1df0f59b3340be128457589c47057ab62f2d55a508801b5480798e00a59da8c4fece877c052c3ff70d1ff1e57a98334a2190ee04109891eaef8822b0d864c99aab0c192352483e746a7264a0081b7c9f04f537cbf660a90c88e23a10917cef3aa9d417c30eead825821ac2eedefa72bb74cd73200b007d1ae9e9abb04f7e58bb672d7d413d90aa044a20e64ddc50c2be37c73e11a99fff669587c0118c87e40d35532938a81e7ebfe354cb3b0618ef7616b2cdbb59a8333686d3fee0d757cd4901138fc967ff01789aa1f267191e1368b170e6ae0788046bafcf08502 false -check_ring_signature 020c1abf89bba29008dfa37fc57365e0c315e54cc132a6af3a8045d042dbb1df 29c8fdc88b45786447907af87ad9b37fe4c3c91d7722705c4afab0218809f2e7 38 f7caff3e9192b311a948de5bc168db3bad905f15596367932f6b1541c5f48099 519bdec2f59d1632cb5b60bb21bfb6c9a131707e079a247f8928892bba8471bf 8e6563aa06b0f45d6d5b59b68c1ffaa80bfd5a3685d6c18796442c1ea171ebb2 0b99a392813a735dc380afa1d5cd61c8b5ca9169af47ea96f1f9e11aac6725a4 0f5069f23392041d5c3cce8a4916dcf31a07b909f1f967fa4b681ced918ab6cd 567941bd9fea292b7f4d2a867a0487858307977675cf023aa8c9300a560c3284 0b096e259630cd439003dcd0671d7f68ae8ad46b7abc754984d2315efea02a2b 2051968d33e8eff6d6077570f98dfea2c22fad7a4b78e5a7d4d361864eb0eade 8fc456bd917e9867c5e7f7bdc8e43e86c22d86f867a0067bccf426002c1e506e c5fb833c2905b91d2187fb4676ba8008b901f3e8f1c04a8b882b5a2c598f16c0 9fa14230794596b838e4c9cb9b8ecbc3aa11785dfc45a715eafc94fac2174d91 fc2757bc10885059901c066a55d90b71df889bb2c0635ed0e0cb087e1a32ee4a 967127cd2b59081c8f0e598cb05aaa1ab1e26e6e73c872dc8ac7fca643b9010b 9a50c679478ee436352556d70a27cc2483376baaa3e9ccf682594fcb23566c66 21815484d4a445ebfceaf5cfcd3b31e03db6082d54c7a2db513bca8795ef8967 4ee3e391c4b996feb592ac0634a51035cd25ccd87d29ce5612325b94c0ab82e5 85f978b13e909ff5e5c2f7da656716ba82c21a450fa7c26396e5835ba4dfd86f e76b2d341b7d055f3b2c2a93d38df2d1618e8df9c9ca9b5a33365cf341211ba8 c8132d71a34892dec4a1197740ed92c2fe0cbfa042ad9db6c8193caed41004ee 0f7ee4b02bce65ba033dc550d5d98711f06f1c955cfb7e7a1bb02b64e48e301c d138ffe7345d0845a052e3a78be1fc2e28a953d55c349b20298b134f2a22df16 00518b20e48850797ad9abb3187d0d5beca8bb523662b968e3a910a95c77a71b 57c6ec95b448709739b964209b650db7b70b07e3b9f6d4cdc61041ac36eb8ee7 8c4cb69e3f23267209ecda480b7b2b5cdb8f02bdf11eea72d10dd5219317640e 350db558c13021800c59451d316060fbf861837d3e2dc8ddf7fe8cf13b080328 df3884fd9104e3951b854e48f5e88ea5fe6f4b5c513cacdfcb22e698cf318320 cfce6a72d1b557877ad41342485080b8d83a4d63b6876baee52039522a6acad8 8757afc63ededa801f499bde3789ea9ddcb519f10188015e3b17d0a6a8c57b88 644fdfecbfebd07e51cec67b7b8e5ffa020449616918d09391a36890731c3cf7 9c143ebc9de667b5ec6d1bb9492e17ef6daa9e693076e25fd9a73951ed22c0d8 8650b7f1260be0b86f98dc630c8ec51173303c2db4ecef11d2c335a5eb117dbb 1fad73e9cb2a74d8575c3552f8d4dfbfa76723a6946a580ec3fe7f484d0abef0 a910b5b82fcd7b791e49e3de3fe9a1c8f157a32ba9759b623b58a538880926ac bf7ab116de8f7d51ab0827ba10579ee3f8b7f803a699f28efa7bdd835acf9cde 023cb4d2e1fa2ebcc6914a4965a5e088fe30dae81626cca1f7c1a1a33748a9cc 6ddad4181d655923e27c2303e41b560a6d506bcb56940a4235ba2e343d413b87 a3ef37b9cb68cf6fb83dcef8e357e946b093f5592a4965f9281c29a6b9f1e7cc ad467b4a25a6a93c2623b78ea258b7506e19f41363307f4767a551049e4ed2cc 39bd729407779036b81872d8768c7087d093b22d4105e89bfe4b25230157aa040128ccbcc412f8e26b48f2f94653451625513dd1e449710189ea0d5346ea8e0eb944b4922d8894ecee39c9b501cfba06256aca220bbb4da668727b48c637f40d93cd7491919cee24b5c2dcc4db436c16463e2d487cfaa7196685c203c435240f51f69c45f56ffacf2c3c7adc6586da105a5205f24b7b31a3354be145a7de8404f0608b63f9cda84c5e949cb96bb3eb4d61dd9038666b5349017d73c1ef7a510eeb498301bdcf43002cd1bd01b78bab550b8286db6859b0bb7dd31e5a5d0220082c4c9a7c84b9ad831e02ed73e2c85bd8b7846a3b4a403668852f8728a0425f09d4f49c98c69cdc5d78e48aef207675da06340f8d0233801d631f102337a5ca0197f6ae4d39c3383272bbada784ab1441ae7bb3eb0108ca642e0308474a04cb06d06f34013befd1cfa1fff596c508f7255a3e3051af0e6ff368a78bfd5fc03d0ca9c128b9d04ccb1d849dc7b168036d0845b17461917cfb1b61876703816dbc044140c5de7ac9147f0076aab3c474af934ee2a55ba38d5a7bbfa7214d7677a200dee5d96a59b04b5dddb0d5c3fc1a7bdf16c8bc397efe4c9b429e6473e8889c0b63a8cd503fe8c05d1746f5b339b34907eb3c7599978ef93a1f772d890a59d8095fec0b435234ec06ef261efbbf18954a56e706b328599efc4135c752fbc05200588e928255382afb8070bbf649c18bcc46a3bb6acc28f332e9e9bd3b71e587036d3bb101535410f933e2b2f30da61c1d246af19d52d3a5c175eacb5e16afc008eb3c2ae4f2a621e40e9e9391c8ca60089f01ff08978f650f457992a61d46da0d8f01255d33699328c5f0c55e72292579b849a0214b45037c9d07d5d1a379350e459f059c5058b23b2651f2bc96fb84f8804ad3acf3dff71039c694ddb63ba509cbda469664d9c124ad95e0289e615a5e522e1eaeb0d37b49454e69eeab5ab108af9273bac5b892786cfec9b03a17f6073c8fd804d3973cebfd0ee22790377b04ae8602931df48f80738e1cb3852ae884290a9735f5c1ea4a39599ae6fe87f00c5806934064a2de41d518a81b05b168c2db54125491d794d2072305e33758ed0550bce3cf099a1f4c271eda9f59770eb3ca03922429d87eabb43c18c88b37610fcc54b5fea55501da6ddd173a42776806db4b2d43e530003773498f498c85860d9cdcc1f51b18efbadce2f6c7a9041a710544ef2d85914976a8a7c0057b32330e23b68681d640e3cef93912097d99ec781ef7bb5bfb518bfca2228dec1ac42f0263335f888828f7f6593ce68fc5c48451d6a86defbd4823ac14b6ba679c5171038140d16d59033a8d2542f701f98de95193a67b44144a7e0ee13157c43d58910e096377e838c6d8988a360ca78b836b4d6bfe48e5eda32fa9a3ebcd38b761360bd40d97f38381940af786d64bbc3d782c89cdc5706a48eb145a5466bb367fa90343842103721326f4f3d71fc7bcaf1bfe9db08e83fe9bbf863a1ae32c9072bb09a16fd6732e5a5a413507ccab2203ff3a5f3622734e2a842254829cb8a4140b0c9827a5dfd7d7d13d558e305e7252d393ed1dacc69d8005b9ae054625b73d040aaab0d8aa279e25d77fd13ea718619b890517247a0e4436dce5015c2b3f8f19088c20c7d9bd19a24c84ad10250d9115f45ad5285013832c03a8127b63f5b19601c1641c6af0bb8110616a45f5c939766c7c4a6eaf933fe754f02d25fe6ef0d70435488159e5c72f4e2025517f499d7f7ba817e343607c12a44b3418d54cad6e00c941ab762ee29f4d7a5b2e65ecc4b1e9e0f7b244d5c2f25ef106fce5383c3b00f2128ff3c8a94e859e3452f99baae2436ad2c3513413de9516a384e6fd40040cc3d3657be804e50380726db47779573ad57ca00a73444410c7c6ab0d26ef5b090bf9b855d2b3d53e7b70e9437142f5d2de0203aee7dcf3dfea773fc5f6c71a0a4598ba27b46f1f494903febfe014d451015bab108a97f3c2f13d4ad663ce720b49c4f61305f8e6c9df6febdd46e16fc150d736e0091cdc6064c04c87ba3b1a0c15db40402d41aaedbdc504e4c438db085824d704c6392521f5d6432c646cdb004b84385fd07e35b0cbb1d27ce97c9461a74420867681f51d2cc14680b9f84407f1ab583b85b3ea5f70add9e13f1b9c01db642774c0241cf9f56113c632f8df08233e20519bca6bd6fdc110667682aa3bee6ade3f4ce9e99aa7bdec9716864d0e47c407a01020c00bbbd5f009ecfdccb073be0b2c06775939ab646a53703eb80b55abc59496da9bc058fc8974aa96bebdae593c73c6015391f58450ff7de7f20ab14b60d0c72afd601ffb314036bdb0cecaf42c9f77468c5036710d37dfc264014b48cd80746ac447259be6eccd3f78277b9029eaafb4a7547a9fbd11f4aa9702a94e0ea3d81de0aa348e31ffe8562dd9c938fe606cee06c87131e0bb168cd00158fc9d4770bdc686699ed1465982d01028e992b96bbc8e2e8c38aedaea8dd60dde3c07e8d93efc417ceaa08ee66330d71e1e268b515050bb2cbd1be44bdeeb08a2f4f47f404df1be19b832f6bc92f7b582420f76ebc6b926b5e3d3f698db3e0e77d8e4cf61fd6dd855b4cea18a92f9df5ecf8e38250b4007e0d1e9f977137b08bccabfd349ad6811ee812e8d7c26c10522061e54c1ac39184b71cbeb09496006172e6b48902afeaf19df5fe8169e8196fdf6f7377105872529a3cb296cce5e018e4affa540a352182d665453926cd1e691fdd3a05c24365b995353e89a13c8046be93b239447ea7296a20ff1c3f4ceab97e2cc113a02adf9758451b60df1e40f1fa6323be8f04aefb9e9c9f3451bb35ce973f94b9768842c9c078590ad7e5106d0620658459383b21d8965445e13f7b548e7ee886e8af24a7d025f1c1806e30b1c65cd858ade19b8fcccab4818863d6d9dc75e5bb198f77657502ddf4e1cc805dd64710c07e6171fe0f9a879fef8aebfe53f75ad1a984a14642104b9eaa43f0c9db16f0c3cc2a65d2beac5b7a88b547046ae6f422cd9dd09c97a1a390e13bd00d62d6f75b67166aea8aacf885025fa4c7ddd02bfad049758fd933b30c276d3087642110f82a8d4501a73b091331b4ad3dada3f20a2184cf321f6d285f8055d0d92e28c487d43518122e672bdb83910bac87c0e2fd517116c9e8c5312b551650822f6e7323b384928bae7538e0ac589fbe8874eaa3fcaea906cb38e307137090b061979c12311b3e392198881a4e7aa010677325ff5cc2a062eceab242de624005da56434b9c4b2e0739ea1ecd7ee69f7fba2f5248b8c7f1a60f80918e5f76704a9d7ce753990efc57fec66920a8541458ad78834d414f9197861e4ffabc5860fbd7f80e183c1607ab9684eea565ff21a12c38efa064f6b68bb6593426e81cc0f true -check_ring_signature 4e1945ed23b346d3cbe140d3f9cb68a4e1dcf515998caf7c30b2921863ae01df ffc9f86d8b6d12e5c754087338f6c2fb59b02c685b9ec9b156f33728435b7d29 1 a70d9d5e0ee74ed85d5c0426eff9529ddb4e4f74acb096470f0474e511dfc6b1 e02a3413c3d9b8dafbed10dc9e168877272015f316aab00580e18a81e7861f07aab71eb1ff0d32ddab3d8913b5b8e8e4487412955bfef608c2bc11eee18d820a false -check_ring_signature 0395b5e5284698e3ce401f8bc0096fc621f0577643dee8494a91e7614e3eb77c 66d45f27a319ccf3f54d7effcafd3d1ccbd74928404975e8b2d56de2ecb3bbe9 5 ba51365cf7f6088922341e45ebaf29271ba02adb5e7ba4c69f788b1dd428c307 c42ac89bb690c829eb2f4b640a18ce18f515f204e2348d0ef3180882fea0df64 a048e1f93168be5d8e63332aa6ca03156cd7b6de44709fefcd19748639224c86 477330df28fb508027e951c403b87607f9b6ad2567ae8000be7ae067a598585c 972e78efee9ed5d9a5a971a3a686d0dae780e5b8e764e00284a6f347feee992e d022f6a1825f8e9b500c10f1a150b7d6ef9338195eb29d598afe1ced6c997d00e8f46b2b36bab7dfb4eb710c0e78a2af1dd3acd087880412c9d8a0b233d7b902e047c786ce330025a29b3f81e0d4c178d70a38ce0d482b1dbffa690aefc9a009e27eeed958eb5bb263785ae412bdcc90e6329ce15e53880a82be8b9607b12202603ad266b9638690cd5dad9cc2cd40fca12234b4c4b230a011732bc7ed5e560fab092652695f6bd942b3537d4f50501ff2c7c858964246dc9f88f573cc017d01f8307c892b7c1793907352077c6bc9eb3eaedb8cc4a444780c47810d13945103bf6ad658543ac308b5e9307c58f7eceb6a303117b07f391ecab54a138007d60d591399d9d3328b59b1827e12ee816160e59a667a598682f7e33d7a450f95c90510db699ae3db443014293c77395ce6af4f0b53394f6b29d46bab1837e02a5520 false -check_ring_signature feb42639abbc9a20c2199e2deb92239654feca3e219fd5933738e39867f7d4cb 4c1f045afdff7350ec2899e62d0813dcba940b6728562f50fd53dddba2231792 11 5474b4946bfe2c9de08e18e8e7c578789cb5e816f17a2e9efa83ee656d3ce3bb d1a3a74b386578795a5904bcec56c961c3037b1f1c03604d9eda1952893e7d89 d6a724a0ce83a58bb1bb4ea9cf4cf4e725e696490a2eae8a5a0e92e8beddec43 98128aa2a43c214efb21d198a3734020f593f86708611823676ea0f21f4a1f42 2291c2b7ed7b4b1ad46b1df607bdcd66613cc2bf1750591d64b3d2cd39da9a33 6a3d341aabab7d777f80fc15551e4fdbbd06758db5013aab260a9c6b89474813 60e60004984481999560a75626f5ac60bab3cda1bbbcc684dcaf37c6f023c767 2bf377abe9e9d8b1448a09954b6a17be2e6da63b4e29c0958f039c4f7d7f3d27 270203492f0733ee59d9dfe6ee5127c67e388b52e28d7e2b7602aed2dedc06d8 bdb3bb276cd9cf87f68b1f396f35275dfb2909ce0dc1a6079316ec88d837aa67 4508fef27449d8d7575d01e21d626ea97ee454f3cb340c2496d10c51b0bfb374 5a57810362664b60329c6dc24117219556c8976ad21e35625c9132196bd58c07ae0d6dabee8d0fb11860fac70cc98e027f32b262220547cd78d4e2fc1823360180bffb0f9a444f53b4226f7f1d08a0720148a2088f2986cc1ac21a6cd2dc990baed4ccb7301cca955591a8764006af453298f54586d3beff1eba491ea07073023a3338be1ada896d724a72161f800637676da0b29b9ef4fdcbe15e5c542e000cce90ddcf08e7a482bccd04d065e99cf32458f45f564dcb70bac1d8da5e99780ab07f01c5e2384af215cbd048e2dd2e7ac10f99a557de2bc1601a2cf2ed46e70536ad88a685835add872a85149fca9dd35c18c26c339b6a1edd47ac2758c1830685551eff802859e3d56ecbbf9188b4300e0aef74bc579a556061b44b76d7600e45cc74cef0da363cf888d97c381ccd7c649d40b29e3b4e799c75fa2b4089d202477b9ca08a4b4be7fbee4416cc1833ee94efe3946527ba0b7e68b92988cb160db66307eb66e06d5f96d0e0d9f673a3c02aa57a60db35f7d9fd93fb80e6811501056ce4ee4e0474c330d0e6c4b321550a1e520b35658bff8b1ab4bd10c0498a0c010ff59b281058e9468a805d4f0b9b0d01fb98aa2fec08c803aacefbecda9103848aa12b3146d8f5287e4baaf48eec5b3f651326ff9d417389611fa72d313d09b46ed19549513567b26a5676f986971d86c7ff5ac544f9cfd270f7e0cacf240848c3b727c979fca98ecbc28c6aa3f5f91808dae1e217194209bd191759c5bc0e98543fd917276405f4129012f27b45e207d6cdb74e6ed36ffa22e28e91186c09b8ecc02a7d9602b1fbb2fb8bc9fe4e94125e13096dbd8245bed8b240cebe9d0e2bf729c44ecb7c3ac64754610e1b52bc0ecc2860c1a09f96db2fac4f719f56013354b0787576332c2bdb94f8b42e6df955ff076203dc8c1e1e42876407d2b6082de3f3a13f5f05447975e54f8c1af026e16caeecc575e114a5543467d8a0d203 false -check_ring_signature f5af9fb353138099a2e0fdbdcf836cdf3909f3766fc309488f171ae674c76b59 0a34f2f510b1b8175e7a7cea7da76cb2e48a60aa0edb70eace55e7086f182c96 5 cfbe8a4712a68a14e8533bfb78c501a7e15da3a14bec1ecc5189e7974feb8727 8a889179e2bb05df952861fdfd4d7c4eca8b9de1c60ddb109537fd6d7ba44ac5 ce6662fa54bfd28f7cf04cbe789d186ace0316ee9f040c32654a70249800126c 23294c51294cc85cfe22c59cb01480923e4be256b68e6a069b18d83dd55073d1 8b86398f0146dadf887e1932f97a0a35df18b2cefe17773b25d7b85d8addb4bd 1344f7f9c41a5aa6d68ec10d355900bc6541da828d9d21be9502befb59e73361acb2d510ddf1f5fb5f39397d3c9605da159f10b84bb46e1a0c88d25b31cca30e2142dd3911f014c4f1227c05d5628d4c68463978201325fc9691f5910a97a324299954ec3c81ceb6c604c85386c5a7277b295713ca907d933b4a591b4a2a540c29ccbf2cb2a7141da3d6fab5cdfcac37b72d67da38a3a2449e44fdb116d770b22dd8d6b341db5d5be3259264e55222452d09b7f3193de54bb53fc5a513f0650b030d25c8a0f30ff748206831f29784558f1331a11bac29228db6ca9cb066d20606480658904f83aebea1cb80aa8bb803365d0a7f5d19293046dd874cec3bfe0ceaaf9e8df03b240d71c3835e4bb7dc0eb03ebcfb10447ffc97cc2d6faca12302617f6bfff2e4e3f7b22ca44243c4bd5b77ce59c77ee65ccf1a440c9fd6229103 false -check_ring_signature 67cb13193b03026ec1237194f2ac58ae268f1654fd94d095215e2a3757ca7172 3b3eb7c08dde92f18456b3462aaaa5b009700ced4b41c5228ba99f9373ad59b9 1 bf54cfa8a2b9fdd0a5de95eaf4fb32a3a946180770ef66dce3dcce2746af3ef2 df363459058502c564068a9078fe97d44227bcedc4c8e8019a3e4e8988a80e030d503e9b75c5c5d6b06517c86be710c77e7238c41a69fd45c537fd8835da60b2 false -check_ring_signature 632d65e6ee68f522f6f238cd1d6e01f9cfbbffcbfc66e8bf3e4c4f0178409612 da3965f325a705ec9d876383e8f723e35ac7136590008b758720c2a856b15536 112 9b06691eb81af086c25d33effbe4d451ac6f2957e42752b8d3733f418ea5173a fc66f27ac80449fd42b3969ffc1d982ca2cd5606db42188e9237a99e28dc9b96 4f005a1c50825558118cebd82e0506b985771acb881bf7758181af8008f73e7d e9c193a0c412dd665a6eedadb520707819d2f1396718dc493026dc7d8dbecb25 420db0a4334042887dac09328690eeca53efb8e9756e3fe62965ff216b4ef6d4 12c0f08d85d91741f4a75a9fc1aa4f7ef3e4d204c464c00c5f9882232b28c466 5c607ef62d83642d2cff3893c68f21e3bb8d6c161fff73a25f40bf72ed08075e ae3fb2442a7f83e647b991ee26a14e868291721d6b8452f469afdfd471c1b7d3 b6bbb8627f1fe0acc7b254c94a4be5941b7374e4c1285cf806a24fdbc00f83c2 693e10f50ef40e05fa7f19173080d1c15170e20b726ec234d9c21f18dfe96fd2 39f6fef9f90ae4c5fc49c364fee226162042984ed83e5a4f32dace9a63505fa8 376aee15cc1d845be68afba71a87cdefdce6166004e400f847c92d7adbecd1e0 9ceefafebfcbe65456fbcf30bd083b09bfff8c26c0654cc934c7c88bccf0f2a1 965c183baaa47a952c971296ce8a9d921d707eb435c87c510882c33b690b8431 1311a8d5fe4474437ade829df305bcf782f7f23543660c498b2cd5d038778cf6 8d2bc19215a56fd0eee8d9a22861db35ce8a79064893e9aa474757bca806ea5b a155f275e4fd97f07eaee1bd550aad6ff3112d870be9f9fd5a45ee6d509eee7c 75998dc51aadd9736511983b3fba1b71df1161a5fba1d075429f7dc615ee1721 3c764f2a00bcb6ee2ae876b2c1a067b61ee4ae800d6456f883c7c9c417f80899 7b32adfa05af2a40b78acd2ee7ddea0e0c08da284f1a5d1b5acd64982492a22a acfde3afe6c14409bf540517a69b4591a7b4b3934712bd1b30e3aca048ec57df 7ecf94d2207de8d27367441bea3d95ada7d3c724ba70cfa05d1d3d307a1351f4 ea0a1523454da7466b7f2d041ed6d8e0fa6647e4bf7bbdd4f556536f79024ec6 1092c284adcefa690a87f463eb2f1179d0a1df94bc328fcadbe4487e54206083 949d257dfc1256cb08cd0e3dfda4c0a2e7ab4d58b6bb30f1c53995a20fdd54e6 a8820c353432d4a5870f0eecbeb40dcf9fb090feab404f0986ae344c2324d7b7 11b79c7e60042dae862d55d294a6cca207d8c9dd2c55cdd4e7fbd74dbee81c9a 9375fdb122707fe650cf95b2c5bc0ea86e614a941bb303d8ad758099929c5db9 89e80d7dd1e0eb0f811dd58c6af823f0683ca47d72e46f52b01bcfe42c5caf2f 02fc56909ff81f442270995464c53b7ef5041ec16b6344aa30199667f9e20cb3 a601554dfd1b173f62e360412f2c7e1510e9393eb257eeddb8a89bd47eef40f8 c4a7e5885565ae702e78e5d5d9f1f039b297a8cb3907e6ec6a395bedfd881f3c 7f7f9669b83fddc30310db9f980185b961980ae47ac52e3121e3627ad6db863e d1c92b001fac8eb4b735e49e7f983046e1ab1a1ddaecec2d88758870d2c1fd96 568ef4bd91566fb31d79693620a5997545755c8b014f1c3f3b7304a99027508b 09dae4e581944b1e9e82e5b3b60a9d5670486d5cb217caa2e8da7e3177092934 aae9ad96364fd479f99388c8f1a8e83143cd63208d41c6d533e8c1970889fb12 5f3ca575653d7ba8c172b67fef4531455d7195a90350a587860e981224b95b1b 736bb828f5bed553ab78726ba1f668f320a447061794f5e902c265b30a50e598 510bb20cdfce58e15a447c6e1fbf6008cf2cf6ecb154426dec822c39cf9055eb 901da16f314a4881ed91502823016ab274a4defccb30941a15ac44da885efed7 a7181aa105659b62f45a0bd798bdeece23145e4018d3f8c017229aa651219620 f9b82f5107157d2bd73466b3346d0a0cee14ed8c2a39a752217a641840f25e04 dacd749fceae680928d54d7e8ee4a6ba9a643a0116ef4840bf3cefc4b2b63fbf 182c5759eb53a8a774b2735170a7260e0095d6cf50252e0d8436644e61508f77 c7eddcb6ebfc35db4182de753416807955bee687dd0fb5d75125bcaf80f3eb9c 17e19b042b719e3252261689e88b93a81a7f2a23cc46521bb987f31bf08824e5 abd09fd0777c64b9191bdda75721b00a1c4ceb604d89c71894cfb78aa939f846 9f23e1d26101e4761f3cfcc6fce8023a07d1fcc00daa4cdcf4b0c2e250fcd43e 9452358fc5886ec4c0f2fac219fc65778505f40e46d4b24f75bb548684822b69 6a8c3d0cd864b28a70aa8f42cb0592d43c964c9454617aaeab9d6a1567083721 a99c71b1852ea91cde969074ab9738ac9ec1a1f6b4e2660a67139335d38f81b6 e80b74f712193d20c89d950feee6770e06e22a3563209dafba2b37ecf8b309b9 bfe7a00d83a1e6062b5d56888764858fe486dc6c06f78a4679cd768598bfdf32 246a91443852deaeeea21449f4cc3b1eba4e80545fd205f5c777c58c39133a1e 06ffbf75155e0833fa3f163ce282fb589d970679c9d12e325fde4da86512a7c1 26a0b25b053b306238f03da2a73280220b0722b6b45bdd0464dc53e109a41f11 507ce0261a02bafe0954fa822c42438ee71c3f8325ee17a80e25b128de7ecf0a 63802ed683c78e0ae93c4418975967407475e07d70d599abf4dc13c3f20640fa c239b0eaaab82cf43e643dec29a41e500474ab243e2b4096197998b24a1a7fbd e798153c4810d31fd484cd880138aae786687bed1f7bc90de2a3991a2331557d 8e12e4c229ce16dd981667a7e0fdd3fe6a1129535c9d0dff53d83a34252b9c03 c0391a64d46cec4e3e507ecb38a621d2a7b1cad1720d2bd140e940dc869ee406 8d618364f48234d87e8e97a9eb2bf95d97eb391b9ad924c793f209ab3141a1a2 6a2900ebf2e7fd4a734c9e90307b0a701c6fc985452591ca46cc6c7034aca603 0d1e73d1f268318b25297e4a7c25f079d38d495bc9253cc4ae92c16402c18577 4ee4166a19fb3efaf1997d5ea1957421865b9dd1ee7f5bcfcf86a4a54e1352b5 38053e51fdad9944c0244141e0b05046948e93d5a0f40065c817762cb094b995 c548a25f18a0e836c2449fc92c215ff8ea0ee0b997c28fe69cb86c99d180f87e e07e49f9a9991da86bb4fbb71413f4a03ae471cc2dfc76996c6bf718f3644818 517066972b60672f903e53d3adab4e2c690f0f31b5ed144994a691e644314ef0 c285fad9d38e2d49439c83ef188f44fd9fa3b0cef3f2f77f31e8e42b28b2bac7 486ab6e08e29f39bfa1e4ae2b763d2657b3840ff84b108560dccd2f73efe2345 7485f240ff47e14ebd6193d3362f1ed28ce9631d046d60a284b4512f2ff98645 230d1ae27b3331776e5b8433c34b5be5f66f59b4250765adc81cde19f01e0994 fa3a772b5a6e2ce9f7d0cc481b9fec15c8aa01007b75ea8c5105bae26a43bf79 aa27231826b28b087cf8bb0d4bb0278a8f7bb96938f5625770203e3800689c7f dd40738744c91e93b10d2d0947cc847a06a92904bd3b8ee1f7c5c2d5e6f7cf49 b812d8d915addf87227e68dacdb0e0e8ebbd4ba22a37dd56387fc09bedcfc62b 55b1f90c1d3eae93f25186f4e91abc2bde316d7aa7ca7998cbf365de487c8354 35074e6cc45a257ccb5d37a90b145d6f90084a25485733960165c3f8a8ad7f99 b8739625f5c935a936a05ee98e348c1fca45bfbc641db2154c67fbf349a9ec3d 72954bacff027610bcbf824fa9800656dbd93cb54cc921c3b8bb444229543611 885c74daabf5b3daad2f78826d9447f946d9213a5014ed652b072853b92b202a 5fba2a62dd7250afb8386672baeeac8cd25c9f20008e9a629440bc8c1fc0ba5e e842a4e6066749b6007aa3c5ce5e34d72474a91ebb29554cc15bd3322fb2eef9 6a996a1d6812438f43fe69eeebe0c35144bf0b912ac08ad325adffaed2a01c06 d9791de011c2d599f7413ef2299c4a06186f5a18d1ebbf52a16609a277e6334d dc73e2f0cf1473e4fc82cfbd9d6b3d057d3dc43dfbccdcfbcc9000a0c3b5a831 5aa5f10e6bdcc19f0be5a23e437818b76a566b8725562c55b5f04a87369d05c8 91eaa0deba2be41964dc8271bb2ff1b52bdf6f0bb78d9b93a4963fa27083d6d7 a491a62db5a534a3621a9fa0ba9483b19a7a26b0ddbaee45c6aaffc8444b8295 ebf03a1e5bfdf7b51347d9285a48965be630e668ae068bc046ae652aae3e463e fc73321fc75f456fce2dd35ed9dc3c1545e5e5855183d1b050d5cb269539e36f ad7d19e9f17a912b8620b9ada70959697e50d557b539da6c6407a729e21a84f0 a509774848d04b59953e69d1d196bdfb9cf72204674fdbff0fa341ea081dddc2 9e4826cb7df8442b86ae772f21331c4edbe468e727aaf63bd5da47d55c43dcd0 9772981e352d8ad2ef1a4178b725369166f5f6ef8df18c14f031089ee0ef6d15 b3fd3a3d49fb4f8860537ec6512d1d656ddb9b6bf4e050870b854bc73b8122a8 65b81d6df6280a5bf96d1a14e51cb523db34a4b5334b826e970f55847a20b01c 479c87cb785965189f3541856ea0b20196a888990869e15b1eeb2755f170b0c7 5f4f6145be506e8aa34061d522ffa259b8d9c9d9ca7c1b8ffd6917c31251cf45 955ce374606c426e587f17a019411d438daf97651124c7dc0a28acfed5e38b0b 8505ef7b77d64f4dd2883d3518b5fa2fba285bb082a5fa8ed0aebcf35a4128fe 7e168833e41b06cf8f6b4eb9187e7173997194b6ed0596351c28352239965d8b fb31bad3e0f0dafa0fa2f572d37663a08363d749012ff29426fdd33cf5591c09 82c8a557b009e980a238bbbf2fb69d10ca8ca1d3cd0c23e7965b9800adfea4ca 079ca8abf96a112041b6321e1e655b1b74da9c0273123127a75fee1a2df8de5a f92d112f3aa791c1c3139125a285c60b2bc97587ce1449bce2d531449037567b d46e3066a00df0f955c57461e82caf2b1a73b3b986c45e35298a435495bd8480 b0c68c4f5c20484f4d169f1195a66507d1ee749c0647ac28459ed4830da2d5f0 820ae0b4c10b53d48631ab0c193fce9aed65bcb5120fd0478260196e07bec383 b33c9dc29cbcf426a2aa4e5d0deb096a9ecf3e5576e69fed7018f10855f62a04d931b6c838692b6c77816b55e55a6d15657dfade1aa86b1db6ca54b29377f7043e9bce4d6663776267c1ed071159432b87d7c9f8be4af3c3b291286477df4907cc97db58063595fa7d648732c2db8d6db2aee6fd89d427a49f824121c28ddb0b994fdfcd0080811b4094110361f5633fcde9725736b0f841623214c3047fb80eb2751be3a30c0424522e9b7abbf56b976fc99e16f8474f5e1f141b287883730901dabc091f70610fb48bc9954257364f06fe7be205afab0b53697c3777939a0524e9bfe6ba56e1ccf26d3c1b051991dc39fff6127bb2a6214c1cd737f0dfc60225df8aa9515cf3fdfd353cea90a9c08de6ff99cbfe574a71e273933ec6b39d098a557e836d7810d5f5a1963822234c541c174bad7cf179346b5e5a2d54f42b0c46550cb5955ac26002e28b465516ae375e78f2294fa15a6fae1d14804363510d9e03d52402e3c3eee04e893cbae82e84b8f6f9e2c77b3c432a2974b90d535203c068b01fae355835fc0116d6948874031a9360d45052b1b348eceaf286ac840bc06c454be2f464318fd314cff1edcb7ad4228356e88ec70460ab47a19d8de00c72310d09c655dff178c79905b4fa52ba01d2573e7fffb1f36a6baa9a7fba0b0870f7c16c2c09209c9b0011156c9fc5e46d701a0de2c06131380ac21b4cbbd607508ac277ce843f4ed811677f5123ca73e2e32ab3ac9c3eee53f3c3561f5bfb07fcd646307c3771578c69c6099f23a805f6750411c7857debb9eba92c39aa740c8951421a11c92d25a2190a89f3eda90147dde6dd9f5f13ecb1cb981e5529e505e4787b90496399d9208c8c3e68099451142e8799f9320d95bb74e5da9ac874091321a68492b656f54065f6d02b381b01eca2de702cc89866fa6c05f69e73f60a6faf974d25594dda74efb5e263c608cd155f831ab65866ef920c26ae5be1f30bbd6c1b1798ff91247d063b9a57143e677cbd268ecae8c245c193a3843447a806201898d3362c0d1f89a143e7db98307d3a37fa43b1a0bc54729a6537ab1ca40d19d4c33ae4e3a2b4acb1267a1d0c1a3c283dd3b64e346dd18e56913adeb25008829372680d0e5fadaa9a6f6fc260a296f17aa8f8c7528e61d71f7c4b7ed8650b34a4ecfcd3e7bb84814d653e89327fcddf7ed2b459b69ed685f5956e55696b0d57a134d9e2d5cd64f6207263a9c87808ebbcae317f636688d53f8cb80f74c80228ed27886e2aa6dd50ad710cebc365896dac131ddbc4ee6bce140331298b890086473484d215f2854b961dda84ae6d4cf6dd87ff0a9d8d9def72749013c2260dbd37243e4281a33fc233253528c16c42d5ee9f64162ac4de8627f66aaefd0b0ead3aa564857bde759c50c725f789fa16a3c77dabae5a59731c64552a6a5522036b57434f2895494a4aefc012e68d3e9df68692f699e171bc8d5090529e2138030a2da2c5d159bacee7c80421389b7e4f0968ae7d6c81ff88f80d6fab87401f0219cce6a3c35b3e37846cdc2d5d790308d63b63b14d849f3dc35611e07a6825044f3b25144a9fc0f178aac5dbdd40c3d499c7c808f1b6935ed77afc2ed03e0504625c74f5620062d4f9f29c538197c571e81acc584f8b0b0ae071f81e1ecab101fb2256ea458c3d22ac8cebdb30638394b32b45650a9b2e4406221141ff7c3003f3f8224717b6f8617a36ab92431a9466a89d5d51f839021422ce86b23aedc20f47c216a3e42ab31668d6b03095ce39b03b62610317f5c2e07f42826d66b13d0990dece69046376207cb394be5aa15a3a9ec855e857e614684a2534160c8f5a0a9c02a4f824703399f85f650bc6f90996d4ca8eecf5b9a5712d1bb38ea65ba50b946d77635d36080d59f21abcc235de47189381145b7e4eb400b021175a129a0b1c1cc6d1867014f710bf7730f4bc7fabee2d408d58998e1538adb617a18fb402bc8a03ba3a47576b872d67990fa2d335dbd00321ff590b3335dfde25b819d60bc88f4b94149b27e1433296abc6f07897d6951777f7298f0c85f48c272e3c870e0e69a009b2e03b14f754e8021586b2728f60d405224aa95cf02582daaaf2ee0f71bcada619cf7d14e5910be6d2e1de47405e15032c058cbfb898c3012739cd0a3617a448540c13135a75e6f4d0a239e7f96c56cfb005424862998ef32c0875043a3c804c801d46c5de3030bd05c4dba71ebb490c8d92e690b59495034b7cea0c39f74a4a44f41d3f347b30bea642fc743aa6d9f12a55687d9d9b2310e24a6e030e3dfc20bf61b5e947a145696091d524187d2219cd3f20d201cb3b3ba81fa0001a39e6bacdf9957ee6e532922c38861b8c9bc9562749cbd4749fcf10ea08ba06b27b8f679f86d9033288ffab131816b6f8c431fc330489aba713ceb87601350e841de7732959a6245bd40a8921c2aeeb13c0be7aac073c4a40209787e760bc031df3414cae21a536426dfb1e0c314cfccca4944233ffde392e7cc978836f5700516285ff1d90fb1853df622cf223fcc3bc9ed5b31439437586904232c6fab9075a0a06148fc101bf802fc8264502d8226852e139d99019a4dbd22d883b177607a560863589c1f75020cbed824c0c691cceb62f00163ede0a134370691b0ba60eff2b817ca187610e9a4e48fefbef38da1e16e147f07b7f1309099a03a8a25c0802050371235026f795078b984dfb5b6417174b34fdf18b96e19cbb307542fd063b38569d0d5c835615fc0034151dfcd93e90b675ba384745a38dfc3bc7fa410d4d6da787b20c120f6d28a0d45446ae8570d5017398114a6a85fa3a90d701da06a6d1c1ee31950e8e9e439d722ce88b27c0beb7b31772fcf0b0cbcedc09f9b1065c6d3364544ae48cf90da7392ab881a3e9a4115b69f76d92ea8e52542a27ba01048cbc2de0d51db7cbf6d97d29a5c5443490a833cadca6892e10e143d7e71007eea7cf879e5a46e78279ae2472798470731f2213d2c140d2d2c09e4ad0a7f009c44639d4224ea12a8183e801dc188ce5495dd61d58405956d69d2f09abb8350f6b9876c34dfb169df28eb0059a1eb657736eadb30813262ac7af8772063602071047fd6603c325a4009f2caafe2e32b33485821ad7f9af6536aec99125dd1b029c79004fbe585e20c83ca25ce6c0f7a09c1455af45e6459f30e22af32e83fd030829e25ed2d388cf8724a9ab692245be1ad2758a10cd083c6a83ebd22e5a7f099bd8d042f0c14bedd0a956ba16199d0cbda7344100d068ed64e64201c3523d053c657143ac4f9b6e09197437dd8956120fc7d53774fb79eeb3fe5bda19491f0d13a66e1a9bcee26bdba4c7b16c3eb465bac472c8c5d2b136ad2079d94b606f0a76ed33c59d5f2d2c1ef72592d4f0b5330b2353b32c94104796a0abd1689e520d2c938b6194f305b9514527ea1309a15104b6b6fa4337311878c0503394899a046b7476a8e767378cb1ee0bd6a743c7e5c0db988ed66da3f73e0e78024d3ab806a1066bebd766ea1a95d5c81d2d62726c2e07d1d3254e12538f623421431ce500fb912a777b47c45e6c51a969b531e6d85ef4be516cbb1871d675ef56fca38d00c224c3e84870f23b1e6e7f194c7e574b6f351f41a742b79370fa87d395ae520c8e99e3ae7cd9d14177a555eec5d6cea1543579b5904774c954aa1ec73c702c00b5b879755a82cdecfa8da9416182ac2e0e6e43b93df5679c49c81a1e150a2406229e1a35b016eb90c963e0281fb7cbc4a67d980b45f874438fc5b02966d2ae0653a829a1565b2466dd7e5e76c82fee8b78a60ea48e304cba20f3bf3d65d80b08578b029d5a34db0ac6e650bc31c675345b5003dc4a659000d84173eaade6580d32d1358629238591d5cb776eabf02f1b7a08dba2a5192436369bf76171737f01af6fe7db2269a1194e9dd56c2a0ff661b8bd45da0c3eabe848d5abe9486eac061654540dac0a608e42be94485c1cad321a00d1dbe9705fef84e1fc10e2cb0f06ba8be1ec2d840ecfdd4ff48f82fd311265a2a8cd2d02584270c7986f16cb6605eef7cdc427e19aaf7cded31884b947df52c4784e365b80d439907c99879e5707bd91ca5e9f3f776bc9fdb2746c0d2b49297f4b554a0c60d36ea6675ecaf5fa7dd4868d93ba5248d82893d298c7cc75dda4aaab67860af01ca905a71efb21130d3b53acf61672ddf3e9cc68c0b47b52e70650f9c4cc0ad04c9a3137d709d26f0e5b7ec31193617097090e6080b84fa1fb074d1b988515a7a85595dc621cc85e08299f64f5716c0fc6e4185b5aa87a7db621072bcc8d20a70fa5e331632bb8e90f2d04a04b035cea3ac816fa2811012c1692e7dbb668f3ca7349372d69ea06d80547982d2f77c926c9015549cf5131409bd7bb216fd4267c3069de834ca29a6208e6d5d98b44445729c07d752212a32961d5d3f141edd71cea0894de14592bc7046d8fd99ca761d475830a076fd43a663f5646a17acb57697512de893c1c5c51087b8430d213c2dee2f1027f7127931d84ec5194831940d4aceac02b7cce1ffd0b5c57f0b3edbe111e07f5534fc413f3de5cf332b76e7eb447d99259bbb43b7f0deb60787c5d6b1d7825c0b289f8befbc01be31120908df7da5be52d2b12e5a90aa8c717e1c2a2d7015947d9cbff9d5225bebd35dee51e7c7331efdadf95e8500bf89a5645269678680d0dd940f3fc4c86c5102ce339ce875777f6ba44ae1cb609c0380e98423d93513d7e254fcbd4134ccbe21f252a1ee1347188c71ec2856e06caa5e79587898bc96a4f0b000c0f03bc057852248a335f1d84ad2ad274b4100e66a94bac1af95d19475dc1e7856b95e9c6f0160f4b5698215d77b6353223880f23073650629815ce2901e87dd62059a072d5b69ec59d3cbe521932dd85e93c059dad643cf34a6ec003c6a2a1a0d5ecedcf095cf8a20cd7a84e1c23e0fa290a09432f690f8ed8a26fb63ccb4cb010ea24cef0e9187e015946fa6ba9b54df2b705cfd72467f5e5ffaf291588fc0caa2360c0aaef69839a67604723a2be7e501d06e7466349bcfb633962ba2dd75200bdd3c2f3038c3bf37c56ca95f5bca703f2091826d4aff0c34a87c70281f51c81ed0a7944876e93c8ffd50df33ebd9f92470978240cf602c1f96ad60722bfb97aaa1b3e8b4d852e271e13d0afad82777f5b0d0cba5a26b5101a6427180727a91f2844daafc210b6b6e111a2b116fca259e104f6088af66060900c5d5e1bbba377a6b45a62726b2462bfc371b776f0ee06fd054679404177bc51afa72ebd6589d53d4ae04b44ac988ab8e6988ae9f50f02ef07122aca4d0c8c3dbb89a0ebb9902d73bcd3bbe65249f26c71067c32adcd6bfb0de5585be41d9e5af232d219ac0151810c8dd1f75668efaa65562eaccd876a5a074f453859799840c762b717213490ef663a98db67c0bb4e1c8bc2b24208b1230124d901552e41796b33cadfae99d01b8bfeef119d144a4e2b4e0544ae73c1590fcb52396a5fa12398e218d39bf5f0a6aff25ad22052470c9d78a88c0f01b8f60a66d4129097e413e1ac169146e9873b6c38ce208709733c9a4e8634510dde06047b573de7d4562faf8f28bc9e74dd2e3e15381557356ec220a18c561cdaa7d903012ccb646dec8d8bce91e0d67bd8dc89d80acfacbbeaf0d690d3180fb6996302484aa94fa68e91c6fd2625318e79e7d1ac224fc939cd0809b9807dc8dab72b05666eed8d27e2db7655e568a0d58939e5afd0f527a1a31d205cfa8b74b8393f0d383903e3eaadde3623048afdd2e0233428a169a24404456de08d3548c5c53800a936e8bb365afc398400b41662440d4bb8156fd9c40e01fb17f18e1cc124cd059b11d1e84ed6f0a92525ad7babfc3baddc520f033a1c2e739c6985ee8d62c001cc8fe301062710de2c178b469cfdd75e71d5c655a0bb6b6fc1b15ab23ba5d002c2a8975fc288fb9d1c44dd259a1a36ed12cda3980a979d3cfc620555551750006c895b21b42c19a0b9c8d03ab52fa571b3b1a28d34ea0a104743557ea3874b0eddedb6f2eb37ea630191282091ba5f99e0cd666968582a2aa0072ba065f46a068852667d9bbc3de5200ca660023987b7bce45858b8f1ba5a77ef1583e992160a79960ca357ce0a4baa5eb193535e0c9412c6c5c800160a80db9bc4390487d90e5c16a273e31485dc11c86fbea75491086367a236921cf0d85aed29a69c6fbc0495b358509c34ddfa625b34e70c86868776ad1355882de8cb7cd74f0a5b482104e4780a3bea812e8c7e73fc983bbe7cc0aa4172d35c1c66e3f9da4995c5a9970804e43c94cec88081053590310dd5c21fbd7d2f4dbbdd166163b73d7aba666c0039b486121912d27e5e6469f00d013d3da8e5a656a70bfcfc6bd28f0232c42a0d8f3405c7c594975f2320a5354db681adcd14c0952048d1c321c1624a1ea92308d6bc4d63a8487b19d5fe900b6cd784f7bb5573c654376fafc593729522c67c02f073f03f4b55001f2fde2d0d37720836a0625b14c63c642d5dad685f8d044b058003bbd4cd39cd0e1a804219c01b67bd4aec4b818b56727ae503f4b9a446460af9dce7bf573a6dee2333beef1b9cc84b555ef582948c8995d7d6b2ce13335b0a9f29372250110925eb0c10224e083d7fda4d8cc186104127a1c4c81274b9c102470d8977422e77b07af74f756dc0ddf9c93b2627e1eafa850aa016f371057008b76d1273199ed28615492d0be3a2e9b1d0e2ede09cbed8ab8503110cb0826f0525a02bad25e385878df7c7cf7a839a60bfc5f1c9f70d94a1ec7db699b843c0039ba786f97a949e6ffad6c2562e5ecce885fad7c4ff7be43fffd5aca2bc16a9016feb3b29a3a1ae76818a196f05050c2c3f5f0aba1a6155f1ea39f14bfb58f705731a3752b676ed7fb4ebf598845ef83aec46512056c5bb3c99f5bfe0bad75d0ec9c6018de93ae32726bbd76239afacbf53cf00c7bfc20b1d19434cb92024d10447334942a9f57cc63d503c2e4d2e030b8fc95843a52a02ab1d183ffa5f6bc00fb4cd6815f627a6e8412dd6df8737a68fa0ba74cdc370e629049460c79cc9a008f195c47692ed92e27d1e6408f9feb8703043fda3c10252debca837b3239d3c04c3868da5fa56d7adfe92c53bf048e8147d922186cd4aa17902eb06803ffa0e0e0b9c0d5c1af86cc48d32a68d553c722c719f18b3d0ccb9a0ee69e365f6aeb60432b19cda75ec5031171de457a3716cd12059cf35c4a20b396f701e51abd52d0c90423754ace00afef5c0c3f562f6356b0139c2ce3a0ce373e47e73727441bb0a3d55818c8924b1894e987096dc57ab58ef78e023621475c9461fd5310497ce0a6a98baa20a025f68e6e911e5575b0c276c8ff4f9563fc7aebe2699e67a986506d864abc8c6a9df5dc87d2f35c684cfd8a9d3f65fc52f4244db72e4d68cd5f10398d836a33352a65c35cb07a86b2927699ccba76a11915b161cfdbf130153c707d5cb45cb2dd24b176da863434b206a2712d8eaf6a83fab94e344b96eb4e0d705af36367279b48546d686de07ba4bfb1cec4668d49e6276c6888874b810cdf20f8ad79e86ca222c29bbba64dbb12924b5a13497542a52dba78c22c81aad67650ba4df8bde5fcc8a8c15ff832bd68203b046ccc8a2607b607270229fc0a1f1d30d153f9750f1c847642f0aa984a36953b3310a4459220466cf5837f9f76548490ad994016921ede706af0665ee296bab6aa8e4ea6973e85ba87d7d03c5bfc45d06e620d3455014f7a4fb8a8951c4abe80b133095dae5ac852eb5ae96f6cbbbf30e3d0c49e813b7855b7cb21f4ff6d25cc2348450941a21c31b9ef839c9d4d611056daac1f5945ea55230afe505ad7669e5cf26374011d14059e2d92a319a291a021c84c930fdd3082386e2945b6c62f9baf95ea4c060bb50afc686fc7e880c1009c744180026e31aa8144bc0350b66fd5789bafe4fa59ebb93e1006a72bbb41c0702f2d7e58334170a9a65d3df1771a5d45b94b76140fcdc23a758521923ea6a069d798704bff97a1b0b7be0870767159c6c08b3ec278c0843b7c7c246c6ebeb0afefe0c0c632ea5b74bb5d3b5f04d7702353481e87dd34ac47404ab9fb908bb05614dcda7439b957b6a7fe9b4bc6f5d591713e78f56c09805b74fce1b574c700306145b23e5a7b54fde942959432beebd48bef6d02038b7d672e6a3a7258b25038c21b437c01e7a4021cf5d2c77b946310d1861526426162a6f7c06188e2f7002e00118931fa2b5ca7ec409432c70a2cca868719bbfaf5b837298b8daf3c22506e5628f6c51b41f9a1fe90bc1c3a6f6c72fefe57bf2426f5748bc6c65b85abb0267ecaf7ee1317bfd4b870999e9915dd9def3b9b78012fcb84a25e8da6768140c594c5efb60bc426bf2e5c867878691bccc40d4da2002909e8e54090c09078f0e013a3cdcb35074df47c36d4bcf21d8b8e0169ac6e31c0f33f27f3aeda268050a93e77d0667137d2d2758945f8f265ffa39e995dd84c6dfd4a0d38cecb0e01507f67d266ef2d2d100a05f246ff2a9206935631f2d4ca13d87ff0719ac333bd50b1f3899ff6a4d19d67ff80c624b74f016285d62fcc2525f34752465f2c994f10c43e1131053288a8f2b90301f50a78141a1e798ab2c1d68cdfcb716dece6909ae4b1d6c1625fdc6b5e49ca4316f78923febacc0a799c7363f188ac82e01bc1309caa037db23597fa5fcbeda246f22faf660b269b43183535b872a171deb3266067fdb1288a5a62cb3494d1080be78d0d6ad2c709922b7f0d6117306aeff4eb40cb1b2017655c4d8466d10929fcdd1e4237df71e64c9c3d799c6f07d2b64967708bc87bcd6d86deb661e5d07483071bcb12514c7502064c48e8fdbe0cc67c8b008db7da8090dda4bb0c5d4b5444266d88e471029eca351d1e701f6b9f5def5120adb652e4b392b92e204b056e3f9225209028bae23187dbcaeeb5896b46eda2002446a4558bc5d18c7247b281fdf33a5b90c59c53548f5a6ee53bb042c05766a0fc74df59a3806fee0613d6aaa75f674ed57889da98a756c80a070763f14e61209bfd0b852c39e40a14f59f21b5ba0a758812681c338ca89c13a3cb9febe171607b071f4fa18d77d6aebda25bb682546ca8b893af2635ca70ae31599389f1bbe040363b22e644f1037f58f71e55ba51c37452f9784b3161f71ec7ef24c6bfacc05658c062c0c9100e96eb1925b038bed8cb092a5774f9249071a98a21c606a3002e94fb4caeed5642601710eb42a8e6d75a079ca7856a1ab501294fdd38e28fc0a1bc44c462580da467051a03dcdaeee0c2712ed97eaf25853ea65f75f0cfd130db61bed97e6458bf022f5a8aefee44f1539b2e4d2fe9cfa06290e633207d6d20e4c57795f88044ae370e44671cdbdb550cde9f35ab103fc341af3180aeaa9be0b1b83c5e28810c75e9f82fbf51498e8d1955dd27fc82383841d9f55ac8fefec094bf2c6f8fef61d7eedafbb08fe39a129a7a877a1b944a0cf913648e5ea7edf06c098a9efc09d2b05ecedf5307932c67916bd9cafa4aebfc759a3fa698233ea044c38031585ddb919d6f64dd1c2501550dc5e0af1b46e176a50803e1dee8d5907f15347a214541e6553f484576d7f2f4dd6a03f12c167d7bc3f1516c6e6b9be0148e1b930b522a73f1f8efde72f835bc27c1bd424ebaf3a87a77efaed1dc58c0d040a899d45b63618bcc60987f9a9ed08ed6bc4c9e8658ef2185b3d5738a7ce0185dbafc684b8a6a93b62fa456566a30abf72fa48b4eb98ffcd9f17af119515042b343c577db4991fde8d77d003f84bde2c1844f928035fcdc374a2bb5a2d4503d42c6eb80fd6000461132a8425b7ea2175aeef6d444b8404dd91683bc38ed1065e79d4f6c42844a9cd8500e8a78c2461336875d9e1170ebcbd3e22723112f90debbee02a609a7345c9804491b046658ed6465c685dda77a8fc6b735686ad730e100c877b2895446d343649867ce8faf9a34cb2e5201ecd8bbab52fe50be6770f821e1f0c3249ff099ce77d8c7e1a31ec405bd0a357f08f5f7ea932cb9d5c0d0e9ecf06e67ab3487b9f61e69b085991ea78579894d13906041bb1ad9604041b01 false -check_ring_signature e3f3afc1774fcbbc9d0d9853fd5e5ba344ad06b283031716b1512b81161bfd53 a556fcc07052e0283d45391058fe5f0c0290cc40874f4cf6dbaca098d5aa2465 6 22d74d1c6ec3f9570e6f583c20d3c91059801f82d1e544b5eee0b9057335fc4e be5096bbb327cda901c552a2578d2df7623ede26cc7fa67f9d12f2102b99cdad bf27d430aa1ed70de5df7f5e5e859c57582996425aca140cb6fa19acbcd48af5 02c1ac26aef8b1b80f6a3b1c8e4d36a78f9e9be3efc61fc810b7ef485a08ea37 8294fdaeedc4e952e1a4a7445656e5e3769d29eb1a3c189903431fb8514e4bcd 25acc5da336822183f9479becd091cd063083317211147dd99531046e85f9d39 e631d13f24920b3248016338ac1b3e494c7988d3302992d202d377446157de9a8999bbd50e2c68628c9f1cd1a811583fb82d42c52856af2fc2690a53782cb202a644df4a73fd01bdd973e7e614f4b59019f911e5f251c5ce87a89fb1f0506b080e8ec103e132da55d8da39f3e6ba55c9ded204c55e2ac9ae96316359b001e30e0e228421c39c4096394c49ae979832873c1a896f6780e9ba61df9ac2e37cf4018a3561d8c3c555659b5fe7dd0137ee274fc2baf698abf57d2522498fd046000c7b793f25ab0c56a2929a00f4dd6ac9b9c18d37cab2ce2a1fb50e6bda5fd86c023736318504f6ec4a0a46ff71a9fe563e262ca43015d4d1de6cc538f031ef8e0da3cd3505ec5f1b914cea35597e25835868966dcd0f975c82ad6db931c6b94f0c78be44f17e0c8b43915e688f6055b113657f7f697f84558ee67b284f9fe76907a026d975d2afdb7f12c9911b2d5681551a72eba11964cca3090583b6ef2b22a24a4f5fa7eb0f881d258b5a03f58faffbef53ed41ced322d58390779eec4f460a false -check_ring_signature aa91c87aaa8bdfccbf37d1f5fcf7eff86e15711fefd9c0cb0e22dbd0a7ef04fd fcbafe21a602335c8e3f0dcc7c39776c225e867c1640902ed8837e63bc96608c 4 cfed0b793fb5da05628020fc2d4a36464ca8982c43f0566c4fbe19186fe02dac d8b98e75e82f5bb7f445d7c147255d3c571637df8f8cb24345876dcb989dc428 b720d961af8a8d9c740534413ac593a0008f90af105bc2da7a46b2e61ae1d301 a79c53fc825521efc217711315a1271d836b4e8be31c50a14b3c5e151a0c9041 4d907734ff7ef3c6132cb60e810041afacbc744770e0f118e94cf634c5a7480977cded45c84052a7ad4318d4d2b2b85c39e7b77b45f629f8df5c1c4d762820043ef87aecaf9b02ef99b3d3b8b5d5e177e777ed5527f67be7ad09ec9f2695a8085a034b7b1e2b73599b10fce1d857e5add4d9f518f71fa1461e2c5f918b4c4a02efaa881c6a88e2f37c49214f18c5f795e4d1437b2b10da50ad3a09fe60499c02d2bf46bd4e4d61d669d01288d8fa9c481c4ee528b84721666195768c83cb8d0130ecf3e264cbfed6c8bba5e0d982aaf6a113fc95aad46596305b52640d0d3a034ec473dc095c0569166815f8f973eae950eb8e7212b81decc1952c045c47c70d true -check_ring_signature 11e8ab7622df67854c006e9ae2e836fd3668fff50a44199749acb73b866f9033 703ccd341e1d8b68ebe7df0ecd0c0a0abaa10432108afcef6de6e78b13dbdd89 13 5b224a970184cdf350e4f07f4d4119acb77ceb50324399258ed2ecb5e3e78183 98b69a56cfd416086d72d518794a84f8f236d78aae68a3190c6ed63027153d52 8c6823a4fd95f87fe0b92e68c13bf6de17cd2f74b45090fd0f3ae47b82effeab bfb50aa1ef0f10da1efc6e56e68fecede9ac157e728b476ed1a8434072ef21e6 a0e06f1445661f6295c341f594473a8cad1c9df23fcd3d93a6cd4958d35b4759 7bd188c46ae6e3038aa1d11cf89d3c3353a93ff1273c16e99dc2cf7e6b955847 5344ce0f4b36c4bdfa3b47908e2493b869dc1935c559a5c90c14412fbbcb059a f820f3ebef59b31d927d336ae8766f35b828851550ae21b6616b376b4c5d7e66 386257d96e9b10988df64356fbdd36a396b3b6be1384c2511581609424e99c62 959a39c032e7def66ca639b757c394a6737e2223b398062407d44b2302555b6b d8c0961cb4ff67423926aaf4a9d698ed1cf77c91e7b20cc552603f85d316e32b 8ed1f5b0a6c930e918a524972041ed3547bd11dc75cda7f5af09ac91f4987530 e2e7fec5ec123271b45e8dc1ff21c05de39a85bd02a730b56a53409aa336b0bb f104c172df6ecfb5ddc0cc6666cafeffd0ee3dafca223cc66219a78e4186fa0fda0bbf6bbd1772e68835bf60702ae07bfe2ebdb9e960d8ea31118c23a39c4f0c4b6a304fc859f00840608da2c67f019fd684fa56372f3259c1a01517b36603087c6a93cef3f1493caff7ad723c20334fa9c85209b18d76c3206aefc2e0a8fe0bc02224210c6e1edcd03eec419a3c917a2d8522d5fda6e3dd4f786ce142d62c0dee96da353f0d819ff9059f6eb84eceda525258f48b7c086d31f999c93bca0003a6285a5a09a9f9a78576a741c54587bbfb4deb35be9f9b531390280572011c01d16d82db54fe1c52ddd32bbdf22632335d3e1318cafa4e475f6d05bf9ad9f90f0c1446d3251676e748755baf5eab5c6077a2d707eb7150e78ae06aed2d62870797027d70e464c5729d5c643a503b540ebfc079443a0d439e85b8b927fd29d60670e6c3b3c03273acddc0725815ed4257ca4d6e9a61b4bfbe05a8c93008b3ca0e701539770d98b6d7e81d0285d1ea4c7e0c4cc77137bbc25d65113c575a17ab074d4be4669dbada7f215315d25c36fef7dbc92cba3a07193549b3bbf1d4f68904115194f9163b644e1b85a7d192d4ab8cebeedfd77a9f2e291361c1d9d7431d0e245e365572f356681a3cc25542c1276028070b4da1272e178fc444b00285eb0b7e522d4eea1fb98b33057da5fb016a369df82000156d4ee0c52fd91b87a40609a5d9ff64d2ed33252da57df9da4bf06e3cd65407c38e540b1de5885e1401d54656dec87640435ea1f33d6130de1171918fc8202412fe063c7d7f422729ef150a46354d80b468edb5c293ce819b0886e20cd9dad1ca21e07b2283a9b4cfc25a0cb44399d1965bffab12f79fd3c9359650c0ee0c036f314084f264c3c2a608bf0e0d7ad82d8c7fdf0270403406a8f1cac35bee81c878f4cdbfeaa0a149a8b08c0ff1b93a6f18f1b0fc0ae104ed1510486cc3902e6ad6d10b76e192962ab32ae90ac9a9e35a11b342d9e6521066f0af2ca1708251243887036dd4b2ba5e8e960107d3ef35b8dd0407d0c9529cd613421e3ebca84577391fb17ff2849a33543a2108b84a2a9b72ab0d7191709849c2901006e88527fb518f0ab553f46f7acca90001551774b63e8fed8115d34d5a97c842cca3f4245b8a3bb5f11d4715940b1bf101 false -check_ring_signature a4eddc0c60ee41fea8b752fd842e3f22136bc4d35d42e3dfc3983cd8212b9349 6371128f78ee9dcd818af0d9a9607f76c38162d6dc17db88ca74ed9e8ecfb19a 7 a93206202f6387035642c80385fe922849b468e65e373a673e2c35628440e835 9d4a85d4c61f79c9cd24d6ecd07f1321831d763d0c575870ad040badf282612c a331499f65c9de76625d94c5c6a67840af2a99fde53d9ea7a43cea0840e0192a 3e380f3879615ff6c0a92cb9977d6b31f1d77baa1c65323ce56e21282376a6d5 f34b181e9a176f25693b4b11398e72a4a75f1a51304ae5780bdc311121dd61a7 18514ace5719f46fcde43f7f66ba94e55cbbd72936741aa109879cb3372757c4 e30dce3cc0bde202b3ac84331b7782ef2b3157cbc0dba7cf175efcf132839142 34b6fd08a17befb149be2e54a8586bac319a362b37ebd697a46eb648f325e90af76e422fdce12d2a0312351adf782d1cfa0c901af33200acb0b6b987826d200fe48c282d2e829a3597f7a7046ff044fa39f3f6662083209cca29d260c87aeb0f84b8e41bef6d7006fa14c49c228b7ce220237fc330051b8c295de469f98a3f034cb5bde8db37c9206414ee1d81816f9b832e4b29b50e11e0d5eed7fe2a786009776417c76a4b0459536bca7fd8cb57eaa1f234fbfa077ede80d69aff6e002c0c9ecbd22405816fc9b8a01b46396a1965f5dfb2bd2616d7cf5e32e5e627bd2a00c27ed68bd85612a48d9bf646d005ce8d2072a5c2c74d3a958b33db1634291e0245b2e42596962f6da3207c6ab9cc61272c94e2ecd990f9600dd39027a24484072d2b7a5e7bd02f73c9afe6b57c4caeaad7427aec5627e31311a65d639efbce0ca76107e916e2322de7337452ecf97f3e373ffc283495d3ee7328fa160be299077ab75005df32f9d0b222efa8981615a6c074ae59cdd52e914edb8033ba34a700a9f8c31ba5b8bd0cd3447d1ebea3259d23702c5842a231a3a48756cc6cd2340fece8f8b27ba23e510a330106cf5ca897720c16790246eafa7a60deb17a80b004 false -check_ring_signature 8532e12992910a65520eb7bda5fd9ce8f5201a3d13a292a948a547cda585697a 5bb53d6512d6da1947a0eb42e64e77449b2bd4fb63cf24965c86b7beafd55f7c 234 9f2299532648d1dc8e4f50bab31a4c85e40fb9dffcd66782b9243a953a67288f 382bac925a3a15279c0aeb979d5256a5276f6bdde14a379ff380e803790d44ba 24bf22957537575e1779748f80364f33e597028d2532593ef2637cead2bcd5eb 053046ea4cd05debd531bb8dd381975b7a945430d6d0036be53f7e17aa2120b6 ea5f21a5f5bdd98a753d16c50c4678148678b021ad42786dfdf10a7efc5f6065 1bfaf4c574f98eb6bedeac61a4bb683d3c02032e5a881fb7a51254871c5cd7b2 365f4a02d5f46149966d787728c1b6b66a54502e173b616140f70a86b1d13ed2 9a271db1e0b1bdb2847c74d95f472ab8d51ab2ef7457d0d0f3114711e587d946 b6ff146a0885108fe930f0caf2472d24a79cc38ed67f9f4dc4100bbd30303124 b7b6c3b9dfcce2a57513723344338fa7d9d29edd59d8e5a8c20cc58f7194052d ed81eac2b625277f06789a011181bf54ede487a44a6310c5e4c53d36df302542 6794f20a8178ee2c1b65f6bbb05f142e2ac530cce432f24507eb835503ee17c0 5502ee9dde670fb4ad7040b5a61a7994f9cbfa68dc265cd42d5199e01e42da65 c9c7d03ce42b36ff9a373ac7b055bd009b8162eaa4bfd99e5fe960bdd9b7cef7 b2922fc21a940632598fdee4c533b3effbd08f1c31466d9f7c4b092bad4967c4 057011f02fcd0d48cd40993845e194703792a36ad74c498c11639c3481ffeab7 b7f447ba90d084f797d672a5ad490a89c4417773bec85e0b06dfb6783a8faee9 9183c936ca23d5a3ffede868f01342730f0941e370d1c84b364df6ffbb5c26fd 37f8b336161b3c3cdd2bf937832ffaee35d17146215e25a2e55d06b58a7856f8 b39337e7831ba6b4ac0cc898d8f0afe81362a981dbe4853464ac976b0c497c88 ada51ea55898574131477dfdcb4a14172093c496ec5fad7f3bee87dc80209f0e 49735c48ed953f1b909258058efb770965f670ee88c9a231d79882e3feb324b9 8ae3accd8d45b55dd4ea562411d107a3e5388efa714299f56282889ae01a36bd 29d4728215194b6c92c472d34825965bf4b9cccde6f9747d71431471cea24aa9 e8e465061cc9b541ba5d661b0c6941bbb7d253676a2ecc656789b71934409a36 14b0481f87c02a2efd5abb4cf564a5852504cd02b047372e19eeb750c81b181b f74bb856694cb50780fe5f07361cce617b1fbc2c43a7ae36e618b5a87b09defb 5bff570db6db4d6fe4a37bbcbe8a1f16951ffde699080f2e3675246d26009a09 dfd6c2bf32df8c8e1ddf6034340b5771f22b61ab9b6642e043b236a54b428020 daddbc7fdb17a0671daae49013b7fb11610f5d9284abeb02de9f2f727f52b8b2 b4335d443b5a053d34e71823b2355d509d3584f08cb7c13e480ea5fc331b63e7 aff9e3719a4f6dbb4865ff767637a90aa209f806e5a42ca26fbfb38ca6148043 6189b82822d9c3dc548a6d1434f43fe19c5c67e70b8361d75ef5e7aa9bea9c65 5a01fd8aad1cd6fe78c1ea937b3901f0b6fbe2bcb4abe9ab78f8db9436428d33 2d791354977a97f1eebd843774bd992ab763e4c7c05c1640994767018a6f7aa0 e55e540ced2f1964970292e1b9b2a1819635224d4648747f1c4fe44b54e23f2a 2a66a0fa6e4d7bbbc000c75d00921ccfd8a0917c8cbbfdb1d2ca50003292a148 70058e5b64384d1baf2ea64e09cfd061aa843bba24abf75197d70ec36fddc837 dfb9f3e9a1a7092ce58249cf1d039418aef42e1690c2de632e00409348259cb4 61a9f0d1ca0e37b3fa8a18198e2389bd8f425295cd8976c26ff1300c1ea1a2d9 d03607280f59f0cf79c1f053ee2b7a96a606b12a054416fa051c4051c9389150 986184dbbec098aa0bb933ef8566369fd06efdd4a9d93619d6f6a8a744364704 fdd821d63f69e52800779d595ce849f0b0e98d4401c17c1348798377be579c9c 7dff222d905067654ad92ff87880523cc3fae88b0570ff462e0c8de649b938c1 34c865bd705921bea71fe36abe4e85b5b5e7ec520fa540a9c4c2e3075492f846 ed046f3dc0ebbaea0da82308f289ccff4b0f72b4badb64d77667fad14253909c 909a6e4692d803ab0d3316cc3269d613a0b76a51f54d2a51665300d15e1a72ee 5ffcca063a929d08d5c1925c1229fb6d6315f06a327785c5f084ceea9169bdb6 de31aa6dbff9cb5e1219cdd932d2c3623d4dbb9eba26f49f04fd40a493d14064 0ff71108884e30b00a014a114367cf07166e6edf1917c7084c018bd7222246cc f65be0dc4acb6ca3fd9e85dae2f3e3c426b4fab35dde26fd12654acdc169f05b 1c49379c57608e7ea58844373e4d91cdf6cd18ceb6bc14590ddbfa61a472f10c 81cb407a9303ed2accbf669607b0e816c94ad2d6fd279504f5cea6cfb6a9fef5 8045769cf18d6171a426f68fa378c54f50b9a594227669675ca1a364df4ba39b 4f61c7ecf13953ac726bfaef87d2eac6e5b86f9c0f49889571a84ace6df3405e f89331f658ab4529e8a572713b0401b7cb253681c3284c167b93da20e213fd25 f2614c9008214329ff235f65105840ff11728cdfa93d0feb2bec2033d145ed3c c595c03db904680e0e261bbce9ddf73cfba1673283e807a938858a56d55baae8 d9bb120a6c390cdda16b1176a6a62712450765a63df5174c7086a3932ecaa75f a20e9dcce6a1c36e95ff4421c49035a8ba3b24e3430c98641d79a2b26e7d470d da2eb079fd51d4d7aa67ad1b3b727c51b4cbf98e0256b0ed11be7f3da71d7be3 84bce11e6bddf11e22dd4324c076ad9c302f4392afc92e1b2833ffd6d16d12b3 55a6ec8236c1314310921391c989ed94f8defa75c1037c4e68bbcece12ee30de 4cf08f06eec954dca0706e52a579d44e8e6ef4a6b54a2c24abeb5a450c1607b8 6c38a52df4d8623cccc451282b316a9e08f840962e388142663702c148d55eee 8e7756ac1aa0dbebdc0439a74becc62a37de6531db2091cb5194e9e2f19ee2ef 7cebf65f4fed2b1ef0c8b296d091cffb7fe6521e50dd2123ed6574f790ce19ee c101e3e8a08af60ffbed628e4237046bc3398e5518a0ad2b9ea1ef672f66a71c 3f80aafb528c3468c8b0e6336425302acea1f531ed1f11dd20b54555a1eab38c 337013a57c8a6da5e9f7502a390d5d99933ba379788e67f7b76fe43582c58a2c c11901f257b296f3241fa3c4d5fdfb48d43e7f48d487283afd7b0e07410ace65 b52c32e7163dbaa1871c8829e646f206edbb8d489d4c40e330a5a7e4bfc88040 4dbaa549c1d39fd6e910df30342f9e306fc84a84afd4a48f569436794a669e27 487b47244812b2824f431d56e8f3d5a6a4798b02be61521c21edc94575a57109 bf5bb733278932346b1031fa3ffd67e52a4997b63fc96218b8a56d316b9f5a3f be3b573869d334959a54563c2ede68b1831c12c9100dc95d58af97e77920affb 322b489186bde338007028a0fc2a7e9dd3182181bfa71dbc909d4dcecced2151 d9b5a3b98d4bf5b0e43c2587a9a74160ca4c4c1a40c0e88541c76b62967fb1ab cab0b44c3c68c95f760822d69123ca4e7e62ff574067e32fc60212d3dfaa4741 62db7fb5c1db8b240cd7e5642a2c689b9bfc5f20535abc8493bb273f375e1dde 941629af2919273abb2a777068009737507bd549c510c81d457016a62900ef81 f89c3474141868a59aea98840c4ccce7050230981ed172eaa8ebeb003ace7eaa b89ee24e023c4964442b43647403899f4fdcddb41cf1a8db81ee3fe6da37ea7e a3f5d092b9c802da41be509dceb20e7452a509ba1cc7c0e1adf7a5e0b19a6a5d f41d5c2d8cec6bdc85d498b8f6ce8dd87607c738dbb5337c3648db6fed24c18e 737ed9e9a613c7f35025d40ec9368c2d3842b380336dd5ab0f62ea4229dd21a5 97dde1ad8cb17afa3283d1b98e85093825f424e6b701f9428e4a30c9d26ff682 749a75593ea31d30762ef42f34c63fb69b0c2bef9beb2f689432d908fccf071c e000cb381cb442e71ad804e1aa07dbf678438dd074ac2320389120af853f8ccb 5e73375f1a38f46e4050a9b2ab417b4d7ee85ca4c6af6618d69cf930b1dfc22e 913f73699ea4c9915b669d73276e941d33c4525df8a4863d7f3498faba340c4c 53d0292484299557d06c5c9fb4c0b0af947d1348265d0fbf5563baf328d9cb84 4008194b293cd129e2c17624c909c67a1d4ca2586df9bb4e1aff45aed7b285d1 71d3bae149a0ddc2258773d3c40556c792890634a64c676ee51f631f3eb72d38 f07f66ddd74d56cfd3e06a3a4ed5bb606c55900cb7b3a239d7bef9edf3cebfb8 bf5d1510ffd93ee1df20b7c5d9b6fe94246f396c09624008d08374a5419f543a 165bd87b266f9cc8b661aeae621acb955eabddd4996598898dbede31ccb68149 d660ea06dd12541de507789253b88416e64279d39fa6fa82b765bad77d9008b2 f1ef77dda6c49ba9630ab1f4207fed3ef196fd24278278c7d2bc46520b872726 964387e394679772d03fc2d6ad436167d73fdfb65608bf67e3cb7cd375ae019f 9ddcba8643eb6103cb69b6bde32870477fa6065187199c8138b6cf4c1162bad0 1b068d2e1ec2b9e70b5276ee6e9d92f0ec325872304d4b6daeff572f5a0b09b9 b2c729991947ebd2176674a0d1cceb7555e26a1f03130d747333fa968f9531c1 10e2e2ee9db704bc324f96989b01f0a0e48671d50101e84ad72eeea87d706393 3fafb3d29ddd41c5a92a0b6768a06a167eda64b034aedcf1a590aa4897d4501d 4dfaef80e030523d47aed63681ae761ea791199a2fe737cae233c96a964a67b1 279eaa108b31315547deb2d0c55d22a55235a40ec6e90b769ffad0fc7569d047 506c936d87c884ce1da07273e2062bde7b26fe111659c55ae95626a612d1272a 7757311c55484dcc6d39cf2ecde3d814c13a294e6ff7a46c3b4c061018daed76 3c77472d98a26243865f8ce9db2ff33b1b4737ebcab5ce83b37e327d0e7df167 56720a62b1dbf310c4f82e02d51c81f46ec857d5e78a99fb0816d174d4242cbb 88f55c3eaed767a8e05af8a0b47e2da2a2c6ee72f67078018fbf0a9bb6f4c5e5 fab2e7a5df14a8a97e91eb77f7daab618e7059d17872fb17ba70503b27ba20f2 32a98b2a740456f94db9642c5eea68d5a88102af88c902c1eab515ed2f3c3853 cf2d7de33f4e4b08c3db0d7461bb685d4e5b5dd95e60d9364a39ae2949d22721 9aaa25e4ff9d4e2bac799af1344fbe242c74843a1917aa6e35bae09795442d7c 22a7b8030b5be0aa95e3b474f582d844e1e662b9a8458307c10ccea3b4295c16 4504c4fbcace9e746d90abd792357efe5433e7f4772658eb531122bfcff916d4 4333a1226dd55620610e45a05ef5ec722b4412148b5bc9d295f8d7811d8b5819 0e222777cb75e24afc0952c5f1dd7ca5d5ae3b7ad2e7a54b1afb0618fa7e414a 9d047eeb94990ee801fd37e7883bbf57cce026dfd9228d35490176d638f5bb37 556e369598c1c29d4c14d3daa8faf54ed809222f41333df486bb29f0b97ee0a8 c18bac82eea60a22a27dd5e22ff997e4193a74ac6cdc4f929ca661c3c8444837 954eff279a58f2c723c4a0f57472beadda8a068307fcc2edebbaeda6061626b4 6627d30c84cb91de986a6fd8cac2bb784dedb388e066296b092dd3dc0460722d 61608c852a263f2844e916512e24a2489c3666925f5369ac423023d84c4ea3af 12150f9d2b3ef8d7149da6a072786616b2fd613029024243144326508ebd7dd6 0c82e6c64b9967249fc4edcd502a9aa2279c06bea46030501465b040712deca1 68a5644275152964b7fc2a19993ce54129d77eab4b1da460810bcefe6c087fd9 130a349f6bbbf3daf6879d0f55fd2ad5a48d9af74a3ac6c53ea2d3ffab7def21 139bf5597e8504dab1702ba0769f282dac506895beb9ef6b0cadc6c92f40cd85 45ab2399e77be937106740ec8ca19bc88b37c9d86dbe085d3c775dccb7ab2fe2 ec33471a49c94ec8ea17b71d5f4c1ffb8901e6aed6e1fb06b53a3e02b5097a03 f4b64c3a25de1a87f270b9a4106b33806a684fc8b85f73a45e2cff593c857a43 1062bdf6c0bf3fa3327ae80a75a5f286efe1db32c66dcde985b6470c60dc900d accc2b5988363a4291dcaa99bfb1e9d13d8e83e0b7617287e3a5b8816db28f0f 0ea2d55b0f9f9a7b4b2fd053c7139dd4c68c849fdc0e4be0fbdd87ea14a508e4 edaee675d4ac66b76c31718c9020469b326ead456871bcb58fc25b5c73aee003 12e2d1d2d7df027a22844c1ddee2af5b0200a74b268003841ded3c2fb6e701e6 a80bf97aeadea8bdb143029aadc802aac6095e4140e372ff277e412426c1a3d7 3ded4bebe2992564d883bde735dc1357963660df85469c602b8ff7ef40b2a5a6 5b2c8154ad4478b37d5eed36993b4b8a4c90399e4b4d8a8d221c16dbb2829607 8637acbe0e9cb5ed769fafac41f7ac4d3e791279e5aa281a24a1ff2df840f539 2fa3a7d4d6d868a65e8a0d23d32e4a60300eb7bc53de41932d395fc6db9829b7 a5e6a13d47f09533c7579de866f961cf03a57460f5f0498e5b2f2a1074bdd4fd fee489bf604679e8ce50e16074f2dfca2fafa8b94f19639df5704e806a406850 4ae5b9cb6b7a6bf4560984ff58074299facaf4a43861709ffa3ebe08f8924394 a0912fefcaceed9086d8aab951d66526d0fbfa4cd1a62d55d8f506cb53758ec1 7e499c199ea33c3b45be521ae9608edf1fb987bce62849b7f1d5dc408225ce87 12e7607cb54621cfc196b6163c26aae3dcf4ed38e46742f8cd31dde691423035 b33a9a19f0d416d24ef784520570d53e9575d29a812f6e66995656e5ffcaf5ae 985753b12f333018f65fbecca80c5e53ff06df8ba1eb27c66cd84b4e906bef0a d290d78bae2b7303b6fce8016afbabc116ccf040f6c8be317a252e78758673fe d74f0f21521998df1813068c78c1a13451aae164627724134f8fec1088edd7f6 62ba4914597da39284a7647102e823fa95e594d67c6f6faca79f38f84a78b278 6b29da31397334815e510c4eeef61953e322454b2ec0c3189e5b35ebc2b422fe 1758e01e4bcba19cb2f06f41ca67d140dd4e7fe6e611f7334e33279681625350 1f6f602bcf697bb4edda2716b4d23eb338b923d62740fe3f26b7b2a85544a858 6f4829cb18130e115c8ba595216846ad9ac6e6c489f015a858419652054651c3 5cfc518b81568a047f8540c5addc3dae2e372608a2d1000140c7590e978258a0 1fc22a223df8c5ad4e44bb23d4fa9075bac37950108d8a37cd021d2a2f382b3f ed637d8a92f5a740f8effe03dc960961a510c2ad6ac10ec8a48020f012f8cec8 625a61c698e2ec73e229558525a4af49e636bac584993597211dc08a59f50b90 ba19c7457558fd116a8e0cdd1f3fbe294d207eb00063c1312dc0c7eb02faa622 24afd5d046c6141681e9ae62bd22d0b5e1fd58aed5e36019e71d12b2c00337fb 79e5940244ccca26a856457d5280b9ab68976290f5fa2985cda93dab0b8b95da 22b52f0e4d727e4447b023acb1873073a19dc27ee16ad5053b249977f4f557f5 0e7dfcef46a9ee3c304d651f1b33b1b3d071503be06aa58466244e73ffeae8ab 818d7b0e31d0e0d4070fa9edd3bdeb49a114998792916ebc1afaa62e8a851810 f0dade57621836fc70a1f5016109c1ee768405d05134f2979d6bf3c018b0f71c a4dadfbc71e5caa0e67ca3e8e3d84e32e782ca968e060b2f84d104c5b988be23 e348649edad034a2e540a94c8cc6e8863ac013d8c14460509acce9023de3ab37 9a93de0704d98aa7a5681aa4af0a123a698c13f8980773ad47b8a84b9da7ec5c c9457911e715a67e99af4a00280c7264b235ef57bcdb15944f255915849781cb 7507909953c136748a3fe8e71737e25e358113c218515b3e6cdc2a909a9ba934 b96c07e2d026aff61f3b7c90da7ef7eed4519b2ca3eb37a47b2ab0b1c4fef160 cf2f9fc6fcecd01cac8c549e30fe1ee85c01e8965f072cb4e3b431bf0547ef57 50022a377a180a9f02040483cd5d907d9078e87d190fdbf44b81fe16b989621a 8cd59716ce123e53a52d798f03d652eabb6179c2d6082f40f07115a05737212f 11eee1f93b2d71b85ec8c5d39ed529a8a21d34fd3bc627778d729044f15f2074 a23969ed91f75df3cf53ece866a273aa4e8a78e171bb23708b23380e3883b350 9eb30b986cadc6a2c2d06282fb736bd2911d9df1edc30249cd21b8627877ffa1 da3a45c0ae5357c45979b7371c2de5b2c18c8e2925594858600d35e752271d70 76981494009e56d5ebf5a5aa9a429cc906ae4cd1f0020757e7074cf50dc62312 ec242eddac1c24aeb429785bdc358f6f5845d521196ac3da438ece8447906c09 8a129da943a1d79a9d88cecec05bfc20867e135bc95b18e10021315aa6d612f6 34075dc35cf3d00fc5338b2b2d45a6d854ed09eb4512c17c54e66c8c6abefb4d f67fe95153b17f7a0d5d0661a1868c53fc4a8d42c8fd1f9b4e5c75749905ba46 1919d6ee5bdc234cc19558fe9b06a94fa08de3828f05d8a1d86de984f0263fe9 033c240550fa133103c0cb6bdce1096e5ec6f213130d5b7451ee6878217dc5f3 2c3719821aee8f30eac0cdc78d5fd678c010b13ca1be243298b964a97d16e433 6c8ebe4c96cbe908fe545dfda791010d185389b433663de5c35b31e7c2699c0a 8da04fdaf5aca78ace5312687dd010b7ef982aaed184477118c5d9e040776463 62552c3c2d409fc2ae69a8abc7256fab2622c63ac699809229baf3e93d83e149 00b9cd7d7630888e8380634af1c97d2e5f13c3c766df92f89c8f7b092b30209e 311ff25fd505f9ff435107ff443138b5685d703c27262a97c5b17f3b20845037 c9d624bd4c8796a5298fdb214c26467afc290341139c3a0c3d69c563789099f5 4965612448eda64a21b2ade68b0b63f0fb721901b20f521854569aab2938cccb 227974457a350694f220a8903560a8c41e5cb3692993d3a990b2504db9450013 a47fcb5c855f81214efd87676d801eca490799155eac91a7c2493e263dcb6213 1092d3ec4e0a1d0345e92a5fc226c1e1a36a5725dafe89b5351c83792e3eaed1 806d80ed0d026ec8d8c7ec80dfa7f20e533f47cff2c5e0eca0f37454bfb2f53a e27e8a76941f058b3830876d6b634c7eb90de18c112c39d7e8f191dfd1f26958 58df0ff5a530a6b05025708e3110453cb98bf405c0a8996abfc398ad28ff48ad 77241265f113585fdab0dffd0b967af079a49dda28baabfb170a6be648b5b8ae 2bb90ec95217f0764308f48bf27485f6ee19a59bea9ed75431ea0d458ad92a84 d35fb0353ac1cd285570d0c767ad826eeac9391c09ffe6b2614769643d184f9a 7d6a8ca0bf6f643a7740ad514378bf63a8ef3bd0cc5db2a71e2c1c94b463ba03 76793d14baa2f7e8c338d06bc4bf1f4f16de11f19db163fd1e7b21eceb0e5bac fae6d3094430b187a37e1057b22c566206205680d448fa73abe17574fd36c3d1 0a4d2a14c4215aa83fe829c1d893a5f41d7b777f7594841e80ee51f5f1494f8a bce92ed776b33cba21bd6c2395c7dfa7d82bcca7c2076f4e29bb7a2a6d01f697 434785260548d3f252d7b93678815a74c74edf964ee66ffba0e12260f410f9f1 6dab132acb28e62cb07693a5d4a099a3f9bc20d8dcf6ce514c10701b9da28bc4 2ff418dfc2152963e022bd1a2b632d8c6e3e45c5c0ab0252405968a237989892 2d8b472375385b19a49f779ab62f7395ae0a9de8373e8ddd28055848b11d90f1 ce3e47374bdd9d33f925d3813e0e74afd2b2c93f4bc70733006731bfa100517a b009a765db7b6075f2282774506883f7c82f49bb38debbee20f19b83f1d7e331 d7a778e0811bea8885cf8c8c0f1f4a9321f548b61e326f9766370b76e3701b08 db89fbf4cfcfbd7f4d0dcb379246300389d79f01baece8102e8a9cdb7bba1bdc 8312e240ea5b10032d3ee2eeb640d2ecb6b55459b28bf8130eff8928801e75f7 65116a530a671874863b4c657790020ba94cf297ba59c77dd3d801bca4edaf42 42947049d665210abe403b199688c99f657e719a19fd2d8fad323a6fc6b79ae9 de2fe3e2bf4fa548d18c29e66ccbdf90bae954b4ac3e730644cc4d32e78e6e23 9f86dd222547264819bcf3b1d42aad4606e50cebe610af71e982a8d19ef0af0f d9362b84c4c631411c1d7f43a9c2a6fd1920660f845539be3d5eee0fb31302dc 0a059233a140ce0caff30416642ba7001d8e4d6f39dbe8db3e4ff42f733c2031 f3355949e9bf3b2b22ad299802be89226adbb27ffb81babe758c17edf4a90f52 4250222b4eecf3badbc1cd95b7853ea756018b1b3e1a80af31e0022053aa23ae 68adddf6c78a6d2e3a75f419c957b8eae2844468ad47b9b5d323c819c8b750c4 36c342ea101e5e4a987b67003a2908246a42b7140ab564a4adfdff61e40bdeac a12e3c2f80c5ec418eb62c5b43dd9a726c9defb6c7b879eb523dfa9a7a47b092 41628d4959216006e469041e1cb13818f49e9b0dd66907a0e080065038c66c19 7d7684661f222f777e642d94c307c7878adb0e9a98ae4ca43123be8b2243dd36 e456ae0e88f03350cdff1c1b5442a6fccf0d9425fe80e735d9541fa435653a010ae6a275abaac2286dc08c9c345a0b844b338f7a899a79cfce4c71fdc416c7057ee622a41638ece3a9b8183392e65d623ba01b04a7ba1cdfe18b46ba3ad19200bdaced0844e5b0d156e9a28133aaab708b98df321c6cec787bb0f291c878ef034152cc9e04f9309eac5c0aa90d23be21ba73ee5c96d1f64d0d46c9ba1331410e9702abbb36ad205ffd3166b11afb206f7126d24c98110fc4b5d821ed0afd2204474483e75c3081d74cb9674e0e75c30722665fe0a0568d4254031b843fff2a00a8bec7c174d670b92c18d4f84f8dfe991f211f980a339dd20499c71ffb7054046866c68e6c5c0d1590f94f8b6e587d7bb0bec47a3854514ec72ef1fec2a6f607d38cf5d2d72b4d1775e7b46197293515e42ac9c5eeec54ab8804a6713243de0c213ea3e93dbca4507f584d556ad4fc2e5c24eab031be444c905e713a085dfa06882b2cfb5baba18ebce1bd689ecf63b8b8d452a10ab9775076a38c0542cfa6049761115fcec9458e5e6b98d415b269f6de8361aa400eba0dd7a14787d4592f04025920745d8b1d16a8527810c8e6bbfb9aafe743e0e7e2998b57394182998100f04b3db49ec0ced2ab75f5ebaf296aef434312a67b5c437131a92facec2bb704a2ec70aeeb2659882bc4109fab6b922b881980509d3a156973c6e77d6af76901188ca6101a091cece404b51fa8ba4a024e50a3065a58f0a17c241faa4202e30fc3de007ad1fddd00a2c3570b694d2a2e97bc90571d64fd032887306e206fab0e67747a241f6078733af38063abada859ee68965e180ba58bf8b4b7591daa530ce9bae428550c8171ba822217fc73cc74387719f1497b63eec2b6564c23001e08b3e4688e4360708210b92b5ecb3a9f1314e4a365812335f6b40dc676facd6a0190aaebd7ee8b2d9a57d4433a1e7d847e77ba9ea63664a5bc658fe3b28ebd860e0696ef9e12559f167eb1e5732544ddb49b414bfab1bc9565a6cfcf3e28b88e0d300a968e26e8676a637d602a559c48df846bbcb76349355fff407017330d920d1bc9a5678b0cad1e92f19075eee7c42241d397e3ac2b03a5f167f10a4eb1c3026e0982612a8d2fe4d0bc8426f1eb2569f573c41a49e2fd4a32fa5c76ab4b1e086f96a7980cc3616c156135fb9491493672183cf707c511ec14bfd1b6e44fb50e41cbb954dde16cff659fc2fc268ec932cbcd6eea8665af9c1963232683bbc903347986736daa658b9a00f507ea4c5cf875f09242b91d75c5b46c18a054405b01c1c688e18ef9bd629bd0722baab1e76595edbd4ee187b669f92fca62fdcd900f5801c931cb2c84d8a9b0e95d6623ba870eea32901bcc120417dcee7273cc810e67962bb55dc8d93bd91a8e1a04b7f28c9e2f42421352fea2292f0a2e81e91e07b0a8506b6150b80e209557012535cf92cf4882c082b8e20baf047dadfdd5d706cf58372695c26ea867e86efb5fb89a7a2cbe261efde7171f77ea403c3ac7560862112447b7c7007cf9c3e898d7ff9184f1221ef726484030fd053b51b953a5019bb7ed8de469e54784ae5309e6ca9fa76115a27f6e206462f7348677ed0bf20cac14911751f1840ee5c0cb36b5612cdea8ab45310aa54b2fb042f131aeb55a065713d36f635593d43ea733c278bea9bdbe03421517c703c1db954e64dc58590ea1048d4b25c4222e10b09da18c51ace3c7a50051357310c9abb6e61920b6420e1e4c7cc8006853486537c54e2394e20a4508e6e911788da9e1ecd60fb82cce0949a4f857a19dd52a0009e4a5c4284349ffd67ebe579c6e96609ac2d9cf1e250ad7f3c24ae4e026b7118e409d7c4f293dfcab9331a0fff88e616f012a5e851b00a17175405f2827eed2581405d51c32eb0bf81ad546522562fff846ee9ea72d0b83a299d5a497de4e160d4aadc8b7bc21a7fa09a6a9c90491ac802e4aadad88023cd48efb7d5370a2120493d64b43eb5348ff85387c1e904721dd73e5669fcb0f5449347d2208146408b75a742c310b0ba340c657d54bf61eb3438f723f898e0bb154f6d75ab31fa9b8706d31bf1a8586c76c2346de5233c92c6d37317472bf0a8b559d4032bf84091acf72781af5477ccf83e1e4a5a793215eb1172536a6a30dc28450155b833fc3a565f8837da5528a1c4673db8681bb255cade405fb2355098f7edbbeaf27a8506550e31bfef231090bd8685a3d782188ef453661a5e2a5014f5eb9e7076b6668dbe4381fd05ff97010a47fa5d9a1d5c671d73a1cce27b80f96df59fe851271dc52c7a29d2208806498e174734bcb896d829c551300d965063934d0fcc296cc2b3cbb07fa9ddf728b48da8666b6c10340fa98eb8b91e5ba035b19330782da79ac3c4876ffa95032d5c6351047c775ac329eb9a1ba416b64070160b09d89dc8834ef331c27b78128fad460460848109ff462cc0ce2770f350e6769738203842ab162aa2acc87653b19f7f15b3844d47716f668cfefd8a7660c30af65503e99ed03bea319a4db1af8fa9a6a373d8a24b36bc577104274576305ab1823930eaee36eadb426de5ee8875aee48a8a2e003af5f79e4ad07983bc706f94ebff57de3e4c0a8aa9d04208f67d4fa35ac4a536230e8e6010cfcecdbb80c18ccd412ef2b802bfcfc5f10bde3e2f07a0c6bcd3e94fa1b260dda195ab7cc08f26d5467e5a26eb477223b591283dd0b4ec262f5517a9b68632082b7df2c6800e2adfe4133ccfc1df451ba2d00988f0e223f00c17c4e9e182ce4dbc4eabd3104a7de43002efc128f0ff88b48b0d095cf8fd20132e9277be417d2313df033b60b21d242c214c305f0192f50a588883f4fecc358f9c1446cd1f3b1a8f52dd11a0aa32ad4cac5fffb487798039796bb25e094d2c27167a91ddee5398942b6f72a029545b32500a67125dbf21429b663944de86870ac846a3f50d1f27bb379facc05b84574dd1b0e0f73385adcb66ccee41264d6c24715f18f3f10fb49bc650bbf022a7ba0d8220ac677fbed5d3183cfba240e78cdec161cd671694da2c872c1820a72005771db8a6b7477e243139f0e770534d6885b03e7e5ac739c3ce84150c70ae7244d6c876122a2e43eb2b8dc93f7ac89f4edc32c4a45cae5400c2a3ce4d308a886f06d1e384dce3e4cb7b5ec124da06acc97322e3dfb4916f0a688ccc263080ddab53cadb64149311c569401b27b5efce306a1746b64cc0a829194b142060475827e8551e282ecee6e52ae615b24b5ef449030eaaed32752278c10414c8f074aabb516ccd5353c8f16f15e4f10234ebe5fb5c8e799c2f8191dd2426d4c3304b21562038bf7ac10156573362a200dd5b66d8fc24f4a8cc814f3ce467527890a469081592b50e8fb0e8068f78ceeacb736bd96bd17153eb9bc79cf7b386b6e0045cc8a56bafb9ec75b2af0971fca6981c04959e588f2ed1ef1d3f71991455606c99976933e785ffcc5d4062660d95ec1ca00584e7a2297a4d03a9e3adb521c0156eb4b7205a045d32dd897aa49a1649295f2f7471f1f2e0193c7571514a81809f06a7aab92123c2d97e781ffb1593a317ae80100c33abbe5d728630dc2453d065ce62fe054603cab7cf267690b1de1f58286b160931b18723c14980c7937570421e5267a1431e3cfae1ac6e9c57505c2fc766742deb52cc56a69a907731bd909d57812471e8f9cd88db3a617a545696f6d1ed3f9565e90e83ef6baf35931660dd6c80c30722a0cc848628ed9ff020a4c21dd59b732f610641046ff5c34458c0d54cd96315a044e156a50be5967b71ef27523f4482b6d104b8427610af6a1a5085cb6e64ea8c2e0ab4418329128ffc9f3826a786c7f0ef50e4ffb62cd21a4900a072006a7d218b6bacccb66674a09c38ced3c3f0df57dbaa42b183639efc866064e0ea93898117bf03d4f03d8049500974bf412e97c0c3f23598916472f03de0223c29a36e328a6be4dd3d0ba53a66c02c53ae4e4a86f600f030294698f0f4808a4b3ffe91ea562933ff2d4730f02876fe5260376367dfac62907f7434f48b10abe18a1a65b6ea518d0c55da307af4607b665d859bbde1e72103fddf1fd7c4e0990ea57850d63dc74e94ba5491db5a03f3c2d8c15d0857471d1f17a6e57835a081d88d1352c737949801054cbf55376e513d00c2579e58fb0758dbcc409672e077d693ff6289b0ca40df850dbbfea0ee87e20d635bce0b8803850a1e5533d250cdeeedb45c07c876481a850a8abe899c0d9ce7a85402dd98e2273cf6ee1a9cc0ef3bc365cb20ea738861cce91a7fa16207373160b0e0e06d757ad79e989f5180de8c1d2ca19209d7bb779fc0ab5b2b82ac72365fe3824ed5431d47c4030d16f062eabecf298f986bf6e39c6840c77d688b4355d6a5195d2ace9fc2ef6e2d3e1063e0b539305a0d88b4f7b3bd52acda30576ab1bc6bfe9b7ce9fe63a7aa7f038077a75477c5e11f38bc7abf9f4ed510f2aea1070664610049a7ad6b904ab8ee209287fb0b25df195b83c145abba2a90d12e5d6471d31bdd414ad0e906d261851002d59c718153cbcca291836df8f403a9bab5b445e069eb504cb8fc11255388806ba08378d72f51df1b1b785fd711f2ea9a00344b07b5aeb03928b3ea2a7417701bf8d72b5fa015c7d8996a5b69fcc08f595780645eca5648552c1fbca0888f5074d5f74a455ba4111969ce99c1137a67d8b9d1f74eafbf0195c2f403de7555307f17e3aa10b8f101bac745099b55ee1049fcba59971b2fb1105bd332816b29b066b988f7849d141cd431753ce643f74d298a863d257bacc74aa5c4f0633f1240199f905207b6963e80d43acba98d7264a8f4902fe82830627a3438dc7a64639083227cbc2203d84fa317c5d97d3fe769a5993adb0e4fad140e512ac2e83ae0a059f60cbaaa8d7f1cfd809e95af3739875143d2a2161024344a8a3c657b0dbda06c2468525e7adbd7efd3390bbe1705e957f2c7a65647d0f158e02e8d143069407cab0d984a192820f6c1acfcf2de594eb69a96d9b35b67ffc41da6af18ce8040ee5fc1e35e6e9538bc362194564f22bba21c512cd3db11232d7e46d74957d700e1ca206b71fd6a680ac9513104a66212bcc5b2fb6324d154f6542517a384eb70a86a6df20b3d7ec0125fe1f3b56149460f8cc2a2ed3b76c14009a57c049169a07c45f4c211d9346ad38a929dd51b2fb06d6e2149379cf517597c6abcdbf53680d9fbdfd7c917e80236281898eaddd036bf3e63cc82b9ab29dd146ee2eca051b08fe3826e787c49e742109fef399d23fcc9133fb5ebb14f970bbb48e65750553034dfc2f61f64dbcc32aeaa99f37c46fcb6763da9d91d9570cf02105574f8259065e50b3f8faf02f92188a37dce5707ae3c89c68a8cbdb5af694f6472b7ac033050c4c313f020049b88573cc61d363f0740dc2f8310a8fe71025cc4ccf45e9e30a7549201763dce99657643c2d8a4a3d39b0d5c266a5eb0c977f9276e06f9b9e09ef1c77bd9b7e6ca9cd9896bfb34e840f0aa15b3888fc64b5f4474c0ef646650795c0e34c99b8ec91ef361dc13c82680709f5130d1840de7498e4a913cfe6c60941e42aa62622936b60831cb5bfa98562dcf38cfbbca71b3171005ea7f822c2062242fac5e83ab2afe013a55c0a72ecd39f9d66d595ced543ba8009b202d6210b95609118369d3b01f4db43886393591b19473eac33ff82151953296b16ca3205e9dc056432fb52269034a6ee7df116ef5e033bcf53491026a25c03505ba5500ed0c76a750dc1dd844a13ebf8c68fbd5c8f786c80d2640f18d6a8d765c4c99b02e9f5417f679c5330f14cb304f27f1327bec8dc5a6d7c365f170171dbd12d0c01e375203382ad7d6d281b4da42ca5e206c3a36dcccd5d55e216f7ab73fcd9600e178adfa5f3f52d68d5e1a5d106f21d11e4ab9ac5e987d687dcca206bfee6750b9fc117d2bdbcc0e69644e45a2f466cba3c9cf1e3691c7322a6dd9db3cb896d03353fc49bc72dd611cabe9c608af3997ba372e67b1b1e8fe37bc1cfe56646ab01b78b7045352d9ec79233188045536a0a2fb4266e013dda684525a9e793cb7e09c82e249f5bb5ea5743086387564ab4ab62ba5f39b42e640aab31fac4b6396100bf72727ff21934523b65a18ae2445a9afbe6f24078097721400a4887007a710fe268b8659941ca199888c3c3238c4d4a5addd9d6d9e7a5544673cf980d12f50aa0fa5346ae50b81b34131b136ee4b5a08e1d606e73ca2a0d8fa4af367bea490721bb4a007fdbafe9e5a6d83383ca41153da0740d2b85a792e0624139b867310e928c2f9ca0eeca16723fa3c85ca2152b45dec6450004b14ed0ba0f8615401f00737c58b8a592f5d78c5111cfd0339037c54cbdf44e11c6e3dde878e7d65d1304e32a55a9acf5dce04f0e73d75bc51fb4ac9054909b313e4a8289f36387af140a2dd4d44a760e24cec690703761cd735653cbd40aed63079d8e748d9901455c06526a657f7f473f9be6726bd6456183aedf22bcf387c1d0ddf4e55c53ad64a00437f67c59f461d678d1d1ebb8d7cc13d07423eadbe197d796c960fde23299510659231f0be86d2255ca27e5cc640eba83a2e91adc9cf2facebb33915bbfec6f08540bd9b2aaa63e6aca7ae36f2df5682018e4779293e1ee435ac34d441ea03f0d2de8995ed1136a40072052a5af0cb6abb289b63cc2271dfcce6d51dd52cb660f78a0707e7b265dcc16038a414621ec58186646aadf9c79eaacd144b923371503d5ab152562337fbc22febb504fbd15208b5f177d1883368e285301da44c9af0b1f2451ddc084f6911579039a9c34196d5df736e5587368a589243e9e0a94c80e7837555cd25e337068115a9d9bf374c7b2b207f3435a3f031d19bc7faf7e4502c7303105e0611b98636473b49a8cd01dc0d9b39ae247bd76917ecda6b5f2d6015624f6cd787905fe81a1f57ef1923496807ee8318681adb275ec7cbe20c186086851a484b319a9c667d39f1f1a30f3cc1f2cbbfc814b40a0523581cc65beb20b246f46060f35c9ffce476e2353fc60e675d279fd04869ea0378cb648aa964b0f33a176add4a13011db7a183e8b485882efd37458a9c24739a3bfd6a26349cc0ea6e85c3c9a1ada289d6cec605956f7935a6dbdefaeb24e20092b8dbafed987013117b03f734539444fce0db28073e078708d5c157e31a1ed96b67da89eabd9047e125ee4b3926359c29905c713243942e6080cc6bcbc5302dab5bffd422b460bdfcf5b6fb93669800b4dd697485dfa3de59d4f000a32645a14f81131f3e5460591e35c572c4c767c33ae89a2377c530131e12f1d89b3117d6c621f68a33a2407451de8392de6b6fd8b0c119fd7d0c9e16c8d6961dc8897da6ecb4d1cfc9b0e0841461d23c8a2ab12cb4e57a10a5d8c7dbbe2a89ec9102b4c6fa3f91fdbdc590fd2019475631e33c00a30c4f0cb42085d9b97c0eb069cca0421e6719b98a5ab033bbbd1f2425ed7081d0181235ba5676c6fa79052207d4ef9655c3ffe7f36940e57aac15bbe8abf73346bca251dbdf66e440b416634069aaaf8419a5a63e270054e333ea8fe20f1f6223f13ab3c653284fc2f6ca859d20b54f69b6f3bfd36840a76141a02392fbeae140a5d69af11a376a77ba191ce4fa2a722cebe10852ef404a99472dbbd711456bc1b4e4b5edc75b5ee93aa91aaa193c1e946c1ce09397d0390d806cc3cdafc3ae4ad9bfd489c500f11c0fa607214971c62dcb8fcd943d60cc5bca53b927492321d1a18f6fd4cb48f665ff1c63fe028ac258c87a42b1636064cc6145307534d8801e003071b7deefb78f3064703a250bf4eab36a1d6109b030421124ad7a21503e8d4c2d03ad6322eabdd1ac71c8320cb54f48cd94797560d580a7df5f121a864ad95b46e78056071a1ff118aa5046843be18a64030140f04d108a4d3dc59e68a43eecbc6c1ce71e5ece8588af1ee431c6997ae91d6788f0278650ad35f26869bcf3105eb71ca564e77eef9d8b8cf8517387962f9dd7a31018ff3b6cbb4d6ad1d9bbf429e4271510241e19b100897c1bb450a8e1ffad0b4069857474c4ed6b568009af189863768e2013b7c765bdc58830d3eb50d7502ad0edcac4cc55e3d7e455b5485f456eb52501174ea2a36f3367e4654209a77cbaa000678330519948e105c02d766e4e1366e83d012d8324d0ea264f21a2768205601a6880dec066c4c0c2fa85b92a676a5f36872b943f99a660eec2628729aa00102b557204b8afa3b610f966d929ffba2af25e0bc9f8da33f56d82f10de5e4e4b09e93282a7efb9af6a3615aea15899413f6108d7e064dfb035919308edaad45e03cfcbb887dcf34cf94c3dbed509c9c9969e7a317896c65817e0c09cec6a1339020852e4323585b6425a6255553cf716948fd833f19438ab42214c9e2895f7f80304d0e56f178a9c441077bd60204f62b973389906481b85d7afc3ef69cef59001a12aa2294aa40fdcd4b111e4ffb45038367573a385b1aeae98ac305e2180c7070cc030eea669ca4d099aeece300aa66110dcaff38004948d22b5ec03e3ad050728db247834b4a870358efaff30069af799f7c1035d08494f5fad93ed3bff1e0cf60224ff361a55fe8e14724a05cec95782ab131780cba6005413d2ac7646750d09bb69236bfae74acc425e534883623bac5b8d7688846110d0b30d639428ed0bb722a69ede3fb8a907fab20dc584d26a9116a3f414cd2d09545257e7648ce80594a736cc8faca75b458ae1cf8b3b34823479e345203dabc6b44505972b308104cc92c471fb5eaf1e5f752fe2e48e8a4a5310810270ce925fe9d9f3c6587d1b06d2003864c55e430eb780221866d9bc2b42e7f835eae1aeb0ccf4666bbae9f700854400aba48d4b0d0db3ef8c4d614ab7f4776fb41d541e8012a96d98546c750c0e79c017a21dc336d64b20bb3cf5354ec75d5f06f293f1b5d603f833797af10f18c1dd8db7e54b076eb6f1c41f0a0087de2a6ca08b5fd53cf6300c2ce6caef06172cf9763b158f31eddeb6777df2ea37e2d127d157d1524f9a985c5fefaecd0bb44ec59d9a0de6d0a168b85c77b39599e31c7d97fabbbcca90b01b015b44840634fa93df194e200376458cda8eb93e69310754a7dd719f7e5eb32303ea6d9b0f3370abd92ebc154236e6203555231a58b137c3ae467ce3abc42459d9a59cf80478211901d4c98867ecf0d7155ab48fbd8226d36c6882a9766380d291d56b0204db28972aa357155b7d9181e9ce96c6b52dd946ec235c97320cb70281e58fb9093a7254b05dbfe09fa46771cf470037ee2efac6a95449cf6ecbcfa2a14b9cdf0d75ddddeeb6e3117db0933af5e3474bd1bcb57086118ba4afbcd79fc2f08755074ab5dae4503e00a423e3f435cddab74724585863471253417827daf27c544609c27a58a381d230e31d9a1baa51a1e132db98b47181f6d5d6a9487539e5fbdb07fb19c8eeb20bcccb8f930f2ffbfc2b1b51cd8af50a03621b9cb651ef8e70e60122111ca8f4500a3bc82f2a049d117b56a64644777dc4bceb08c15bcdcf454508a76b9d6c8a43a0536f1655e82fa05514ecb09a13ff60ee4d81423b4602111b0ba06e257ba6e0b6cd96606ae2351cbe298fc6820a2db2e6cab55f063f78d64301fe793106a8e8b8521132e50642898906af25316c7466f949a70ae23e8b6f830b3d46b7ab2fcc5cf8a452c803db0e020eb66d46ee7570f6abd72163229de1ff00bd494549e1740c18948fe8002ee600f90d5c67093f72ebfa2e987c647eccee0afbd55ff58a513f62893a459488e2056ec638812c3445cfa88245b44c46ad8d0b0513d20afa9988818fdd57fb6388651eef432a4c9e77245a7de450a26bf40f0be9aac997f6d9ba0aadaa1625f1842edec89dcca6053ed339d334a119276839061836f3688f66f8e4a427f2b3b358426aaafcda99a828e409a3b4c35bf0d42d09f922bab27d23ccf8ad0e3e237c4375e1bc0c9f82f49f96d8b4666d3a866cc80c5f5d6043639e6c084f6abc9e7ec6027c8fcbe0322b4bc46644ad005f086f2d0a7fa156419462eb5594545ffe8b73da30e63e513145f5a694ec940a1e36f47a04bd22c73ad2acd68d74bc3f90998186ce7c46507540d277dc9cdc483b6af3f101e1a2e77f17c1d4f81a00d7e7e3de642e5765933eaa6fba73403624bdd59f740bec34613c2df7e3fca623a3177fa17ca3b2cb31175266ccc567506946881edc0cbd5a6cdc659db8baa7eb186b0facc71006a1e85d0dd341d31053a4b848ea850b145fad8adbf492cd891c824a1fd4afdd2134d6a659bc98eb8cf12bb80d4793003b995c0aaa6dd41800105410f7aed66dd17f4e2aea1de6f85c4c2072597a130e4d434aeb7c6f70aeda98ca0bd3733726e7b67f3eb193297b5773cdaec0bf090ad7ddfbdebaedc4550a3105642706f5df99ec12e63869e3a953b7bda0e7004b0c4c5678e1794973599598ff88f93fc5c34a26b4d07279c58f772c818758a0ac0f9da1866de23affed86442af1a99e9f257b333c728a4426efbd0693d5fa05af030dbfe1b85a64fb12100085d2389df49c9b4130cabbc2913419ab821a15cba10f37422347bffddfbb6d054547f398f5511839855cbdaada451dbf4236c8104900088a0f7ccfa3235994959dde5cd732625a9e6ca3ce4674bf0771c62667e40808f0409de8f5f1486f6d8e264f726c7e00ed285c0094f6f2c54562e39ae2b2ee014ee8093e438497e7380dcdf3473a89eb833c63b449b79dba07fe5c21cc254a02e79b755e0be5241a67b0191946791c0af88c13ef5c98de9e22970d69516c820b8277bfe254beb5027731e55bead36886c13c93647a312b3ca4c3a32f86a77b02a278f8178118de235d173d3763fa1ee251a65a6717fa735487e35f230e92230cbac08e934e5451cfae0ac412cae36a6847230d995abf10ced8d35cb9146e2f05af9d83046dfbe39f003427f2b3e2c8e3fe2f71ebea133358b818d8879d44e00fc5a9ca73f16b7ded993a33ecf7b0f3125835f46119b64f9f47264c1cc05d2c0f7c970519e0a12b57c6a8700cf155915f2770869d8b262f786539fa0c9ee94609b9cb3d1cd133cbc0ca2718991ef031b3575899c90e7008f5996f89594e0880035c7edd2b7e09ad7ef69d15fe5c43ce814f7b82461da620a3405cc57a9c62ae05dca67324576ee3d61d709c9725843644a5d25c4929b3eedd277d4db40719e705043c79dbb29734ef0d95118ddad259c71b582d68efb2c77ecedebf8a08f6960b8d4d2a81eb8da527aa93bea300ad3243e04eb85f31c0ec883de9fcd0c968df03c7c385eff147703a0ccde70e53af50e510d78fb9c712a281ef32fc9a950435073b1e1bd0b2fe546bbfab5865a87a5a6fcc63b2fc1a2653c8fd0a2f56b593cb01295d74b33838b99c14609e033e9d403814917efd0ec4bd632f64b4a363d9d002cd1ee9812dd29058a3850dc310de562f2c6e22d89096324abc310abf244e1f05d428f076b9575df37bc54840f8fd99c8b7ee4ebd8bfa62e3080e8204ad21230decd3d04bc165b590a43c981ec093347884b5cc3ece8af6ece6a9b179d724730a97658fc2cae815af9ffc10b5271e409f465943b2034a8c24198ba35981e42e0e6b9639c0b9e253d4afca68ed427becf7813d355a2e8bdac97478c390fb81c3027fc2ced195b36b35270ecf79e5a30420c69fabc8c3df48ababf689883a7d8c01fcc7b64e08088168b55d1561a88d643551bcc0bdd7dff3dd19b3268a81e241061eea757f49f6561f4bb303533e64c7519b5be75e6372f47b92b4e40298083009b842acb12b10de41158e2b59ecf314c1a3ff14c59d3dd6f65cfdd56b26562b0f3c900ae7c30d7413e7be6a2bee579bf49a0ded4043a0d19bd955d768a5d7720e0772ac53c1f547f61c370e49610b43110607ba79bb3013d147a6f5b366f60b00633f9cf44fbd84f2010eead3fd66db05a06c2e4fcc19986dfa6fd0d94eca470c6fc9853990495bf7d4ca69d09e7a0356ffca8e626256e6fcc39383e071a7390b2543889ab1ebf7a2ef029b21df56c5d436326beaa0dea63c0bf6a6ac8a13ee06a21d77bb16b25fb00fcfe1b7323dda6d922eb5b4b3167697e58a890f4f486e03786fc59a1d3b741c104d500de302647203bb67b852fa1599f244ca667570db0d4574584500973fbc0b0f27d1d6ae1f36d3a6f7ea8ae85fe4d8b6ddc822cbe00350b680d06c6a5f2fa75e7f8081c66a3e8a2e7d7f1a91eef4a56d82ef9bc49409bcc95771deb47df502bc7f4d0ef341db61e5308a369682f0f28fd77c52e2b20a916938fa1055696089bf5d8f89ad346a117b993b9138571c58fd79b5a4f36d0acdcdb6be6e79d0f1f612dbc80a285d5fccc29a8917996d21a69cc218ff060e0459f717f0779cbba2bb717b93c3ca49ef624de48a0ae571d8796d62ffbaeb01076fe7d51cb05c0b5cae51f309b3117a0945d0ab5174e95fc4d5060077dc7f50007e5f62a72bc872ed173bd9b7a96e86eaa6e7e2731d9a0ea95ad30d3876845709a438371b4369f856e90943624ad67fc8ab6635a02b8643f12837df2f58567c0398d7264fd94e1d25e029f04ced6e7339ae3b575fb42e9ff5d2a456405dd9cb0d92548c957a23def44274e1af8e063a5cb32ff3b4eb2a7b4b6352126f9cf09809640e4328eaea92f849e35cd314b23b232c0fcd94cc1fecbaf23fef59564de102485a2639fbcd11e706b5036b5b269306888ed7e2acb463014fdbe49c95c1880e493d6b7a7827fcf79707bd2d86b7c5f2d578eb2b2968c30d5efcb08046c7f400d0e30dadfce860374000a32d8721cc8de66972850cb58a272b08a8a550cfd50d9742a6838b8ae1d4b0c0e964c54bc324fbd1c6abae1d1fa42b34545447dba60aa9e73ce36db189c096473034c4df56b3c03b184f2974d0cf963fa30d80221a095ab531d62bc5325167ed37d2d8efa07e003aec1b1f8d35e2de152ce5920d8e0609f050f72fb234f5bb30521a4a8ee01a2d934a597f1e478fe657d73c2376f80bc317ea1080d8a51bc8f078b146521fcd2ec999515361842498318f29c8c32201d39a41559cad8c6b7180bded5f425632f2387dc026eb92965e908fd5b6df760aef0d55df686e782fd34f6a71a5a87e9f54620570824b17fe4361ea68c7ac8f09333275aa0466a2ec5fb165611b0c1fa91fd622ae6005a6082cc24f717926e907496b089f1a5d75dc9be9b93d45554f3f0bda0be5b450204d218c1a72eb08530c80717935e6fba922cb46ebb488add55626f6ca52fa0d59897be4ffaae6f5a2048c5b85b7fa8bc59d9ca3794d475d9006415f4362ad32caacbd677a21f17a450e615cd64ab8f1138a0fcb0520cbf45cb0a9c93b480d5b19ef5fbac4a3471ed103ba344cf9d0346473fdb8c7517cd1069eebdf5d47794237c73871765a612ec30add7d0f6002f84e9b18400c7aa344ab5d57805eea82127d8cfa25a650a995d4025690d86a3587bcc2fcc9dbb268c84efe5bad830744bfafa3d116f7210e79fd0362f2637a899650cbb0cc3c9f0f9fba03912252016e83ce8eb93c524c1438950a2fa89de7e4ff06b16d7a11bd15ec63c75fb9bab260f37251f6e6ee1310e89a0178d12a2b777fedbdef1c2aaf50f51ec92ab1195d1247d5611d62a102d2c9e5085cb9a11d1433b43bd002fc5eeebc39960c18d87d87cb2d87a8c729d749ed4c022692848835161a0a4802186ded4f971d08cf7473a48a4f1ded1b273ddd68170755e2e04e65830138e5e7992745c7ae0f975b1e2ff82bc3ff4fe92eb131232e026043092f9756205fe80ad53500197333a458527b1b2ef1dcc398be9ea304c90ea96cce429d84093983968e5214cf19c67e1500fc856f5a52ab97d4ee27a355074e2c8af9884454df307877f340155e2350121ef9d65ffcfda811287e7de10c0b78eb43c61b0a5554f413991bde033c2ea09b37bed56141937a3b9ed3e8d4a80d3930ecc06c685bc46d1166a3b9d276b72e09c7c545a580eb898ca3efcce4210e0d2c6d19a7270099fce7be062440a26366ce0564ede0765e25518afa1233d00b69080ed7a1887867aa414d25dbc7321b39ead337ba76b470132a2dd2ce4adf0cf2f3e2f13c640adb40acbfe34bde690dacdc70aacca02c11b600bb91fe93d20a207248653e0cbd8caa986ada39e601f624abcdf786546110d5dd5946fb3068053d1d91bdc76dcfc6a2d67b3d952bbc443df7990a46d02bcc83746d2aa93df90463f16edd5169a2adf7cca6febfbc82ed6797e2bd2de28c071598482832a5820d401f6058ac6b3c9ca2a83a6e23d6304d31943a4f7d684cb9475041a85957330974487ac91e6b7861ffa27191e47d477c852c27d4fadeacebc084fc9ea8a7c5044e99556c7a7ffc9e9729455327695576773a78177129c888be42bad86471bf076780f4f66ee4539491d84a01e60e0493f35101a519a21d6af7df7d870ffab00da13da5390b75847874da14f7c3b991f4ffd551d21dccc1201eb3bcb256b2890f90cd425176bdf6374814449f7410f8c2f52a296f18224b8ae1db5004e44ef60e0bbe7dbf881eb64be19cf8e8f8c767c521530da7d83f9e7d323bfe95aecaa100d24406f8e29360f38eda0738ee4b03008cc990c3b121a67c0b962884e20a7e084be96f940a6f77528c7ac96f38d607b6cb5a7ea8c78960fd1756fec25e13c10efd4cb5a9d5ada2db6081700df6ac61ce889d45a8947a5e9b267ddb9b772be20e18d620ebfddd5f90741772b66eff40a9f5e22bb8fd594f80ef61975181f7a10cf410808c3e9e2f92dd79b2a3539b6e7997064ae0c243c5deb0fb7e4615f1710525f6295ffc948a703a9fd4658493afaa0d08d6ef7829fc16ac816ad32ba6ad0c9fd988d891f9825601435c850af295ee4bef8c05a2f90ef45245332b956a1c0342026fb3458af979bf32c584e3de7786eac001aaf77225a55e417997a9cf890b09de584d373d0b8c5f31354127f53ffe039ef0e66c8941cf9d5e95921feac70e0d9f94a5418e3369f6353005d9183ba953901eff885c4352f39154a552a88b05346fdab50d64a1ce3793a5bcec9b9baa94fe40aadb1b04b8b48bbd8c2bf6120c662cac8a8040257d7fab2db4174d7c624cbdea1edad5e16b16b0a4e558ca360325d784925b2f2a9a0a1682c05f5ec18f439e8da40d1abe2681c176a582bc8c0b16f770c0f15100fcd26aa86c5e1e41fee55c7cfcc9c85d6f5959615b3e891508639832c52213c8f230ed93dba153d3d7d7b106f7a1e6941579bad12721cb5f01524db6f468f32ef84e3fab4935c0c4b41ff88301e2d97b689f76211bf0c485062f8a066daae3c4dcdcaa4e4858609f4ef80697f6ab0dd7fae7d2fe2bf91d7009a117b713455ab44d482029114fa728019976e392da40b4dc80fd01a2663ab50c1899e2ff8b215153a8898d351d9612e90b639113b0b7c719c66c24e49f1663022e0314dccfec774b88a887d75f2f6fff98e2b27ea68a980bd24ad46665cf900f4668023755c21e9139dde2d94927c2182006d411dbde83389a34f4dedcacc70976d7d0ae125448329fcd1baca7f0e64387d3d28913d6680d0d80db1e7e9af6072afcbb906ed11a0904cfd46d174ae854a35a84fe3459a954ebdba8766297ca0398a4618ea4eb8c878cbbb5a2aec7a6c1ba58b01867208b7e1833bc46aafa2a0ba263e76239d61f1a43926094b895a73b8cdb26f567dfdf76ed177622d4caab02f72c7ef4fa8373c481610640779c2b2c31c3390eb8df0cd892dc1d0ff28a210bdbc1b54b413635d0840e2cb57c0ed19397c403599677d5484e491d51d27dfa03e9dc4c37ce7548c03492b36c072b17d40b131344d86fc9ebcd904a51b0c3d405a99b307a664088e5a1feedf6188425355cb8c7fa7e30e9415c564fa76088ef0339271c2d46922ffe48e3a3d06a5e739dabe06fae618828e9a1cbb4c1c1059e0d42d5a902c5787672c6ab37b5a367ad47ad159af4df85662c7bcf1971c53e630f56a9fb0fb75e8145e6df4af35b5b4746d7718e2caba28f4266872836ba453b0dab3561d29fc2bc7b2d72b236b08da169e240ec241b16487c3f2903582b78060e35884e7749690918b03fb1f1504249306f43c4b2923e3ad22b34657305b4c40a1b0d5ef2614780a4894eb6b3818f836ea1aeb6b36781ad0eb5ce7640661d0c04646fa1eea212aa960398b2f8c98fc08f33c7c2dbc56677bee05b6c622bfe4d000e1e6965f7c9e847e1cb62c7f78f0b50453f634c04ffcd4dd4bad740221f550f4aa1580d13788ed2cbd98c3c628c3692b2b8819063cd8d819ef36ce130695d0c41d6111576644d04a57f76d8aefb99d55d2b6dee83b2026f2883f47dd98cc20cba1dc9a85fa365f1d42af16fff7bebf535638b50764b07f9d4dbfd7b9f1a510f149f5f10de5348d43f383e7dd607058ae6eda778d94d9cc00bd798dd9e06570d84348877ed2062cc9045a53f6cf134778bf4a2fdb2fb6acbdaf33cecf133110f224f6f972daf026c718015e28bb9f85c25fe784c67f2b8b0fe6629e7e4c8c409501ced37cfd1696b851459ed3135d8fef027f5315e8b4189b1f088205db1110e950a005635bd79dfea65b1f846ebbbf3b2650e9a17bd9169b6e0eb7d39caca02c773a14ca4af6909cdcf6631533116052573f089ad53024aa67513d51a791e0d70c96b8226be7186d5383c1a941681795048a57b51a2554fee26a26d2bbcf10c7dfecd87dce8b6f80b0c914b11b70dfc8d317d3d115a52c7e3b6b2e77e82150ec230cfaf18b42f4c5885dd835b7739e9c1d9aaebd501f1dbf09a39e4988ed10b1b69f77d4f14a742eb77a9e7457a0fc0bef0861fc6a893e359e8934a2224ea0c2243983435bbfd2d97225ceb37d233da056bc543229968a308014d778939830c5a107f6e31e53ae5bc83b33e25526f1179b4ce3e04d5cd95c63c2b139e2acc0c93e12f415d4aca861a5012345882edb91e907f23110977e233d04f199ca2880ceb48266fc436f7a444948bf6c6e25a64a2c5d3165437e42e60ef1be5628b7505f5e55fb51ed40fc921516e8f366fabb59901840986a10300f5e77bf09acce203203a0af96ccbf432ca7c2e99b2e8de2716eb4f30f5ceea9c71a1214cfc3e2f0b91842ec00e15e66f332d260d854f66aac3f93a9476d402bd4392ee83a52c8c0b10d1091e714513e559014b20e0952f226e36465b3a2fc43f077ade6494d66e0faffc1cacbb9763c6da1e3d6fb2231fca97b63429876007804bcfcf49128f910d503a5a6bdcc6d4b858489048c603787dcbefdd4c479e30988a9d19d607fd310e950ec1c7b77d360d026eda0869429a0437b00088c72cf39c7af64117054368084227e1a0c8dfca08b7ad220b635d4722faef409ed44659ce06027a34945a6c066f45cc91742fe2e5f29c9ffaae7b5f7bc642f035030cc9729cfffb4222ccf3029511655d0bf87ba4a34e9c75ccb0a68a9ef39d40458e13f001b70a1a3c5c270b168830e8fbbc92009bc8590905089c89c73ee876847a251340dd80fda37bca0c4bf5441c81b237379402474e0b3e1cffac43c9b269e3fbcc4f92826a7fd2ab036ad87e2352238d92eed4d9dfd789803d9dd0780ba0183ff54d9ba85ab8adbe0c1e131de80a8c11c2d8b41fbbce68b0acb1a59241499b1b656bc4023466763d0084b96b54753b1049887427fd13dd7a0fcead48ac7552b1b471cf10b82b88570a8508c379cd19eaaa0fbd0a4732a881959db549b5090e611f5951f330e7ace101fd200fe3e6257ac17071af6af4707a3280b652166c42701cf7f8bfdc42a374086f28af76c60530c68e9bc3c62ba47260609dffa22540a5465bfa55a8687a3a073ab3915912cd2721eaa49ad28f1a5098df44e2ce02b087a4c1555603b739b60cd3addde83ea4448b035bb7790586dfbb79029d333d2920433145c827c26cc502eb1dacadedeeedb7d62439b30a591a25037a76ec968ca9dee479430ac2d90a0939f40718eb37548866a96f0e264e5e0c32346904ab4220fd2314b4deac0ff50625f1af0e79e7639c600d5e86f2f0e77b153a1f7222ed6fa1605dab26653f5d0cea2914e8c602d192ac1e148f3f33ded81c63e45f32c34c00d561506252a42902bbebf7afda8b78aa9c84810fb4eb2234cf284c1fb795f20f6336fd37a979d1060b6172ae76937bb057ebd5021d1bec4c63bf6189421039a13b56bb343565e708e7a1c07a0fa1eada23d4f889359fa86fdb876e53a8c92389d95e120340c29606af1a1813cc3a8e1176f90b18f31de4a1029aeba1ab320783449a6e80ccef290d9d975e28acf1dfbe0ba52f30a45c95559fc3b44089f2c605dfa620d460ac28088ed2042413ebd3d9e8c9f98ada0a22b18f53ce86483c778ea8d1170c972e9c0bb636e1c35dcd448d4775c474f8e80341151cbb45d89e163caf3d6b082e8d230669d3cf2d20143c442c36ba970e3dcff8f2b0affc78a3c9969ac03d7970638004ca91064cb0dad83c98e9494f3dcb77e500b707d6da74f198479458e3e6ff5506193fcb84cd2d2f9215d509e97678249d5da8752d9de4a564b818eb4711af4006fc14fbd8abec19ab0c16e14dc4a9d726809988b5548b48e80d63075824a54f0a1f20be3afdc4f9ae34bc45f53939dd8c593ae90c791110deb8218f11f1443401d52fd7dc51184fc99efe065654e072f2d1f35e3a31e0213516999cd086788c05e1e3e515af1e2583b01ff812777eb549b517446a780a0216bf7c7cdd54ac00003070b84ec69cfedf59d3abd779cf84abe84c7cc3895969e9330e8889fb3ae408bb678374948218ee913ca6e3b264c4e7e79ed53c96b8d650f5b60cb18bb5530afbd8680c1d462f730f88223c3822ba8ef5dba01ccb258821fcbe33cd3af01d086edc3dbec0b327ba35a4663f474f1d644d92d8c88747305300f2f5cbd743c60e21ef4fb55047f4fdd23fe85c753735aaae4565250827a122b1053add7854540297176c7eeb02b3b705235d7ea7ff4e5b749ea3c3db8a250dfcbfadff93e5b002d74c6de8f5607eec2cddba4d4a834a6601099b9fbe51f2fcc3c329dcb9eb180545f329a91485a3125f159f066f065b29cfe4489b7faabb894ce7e837cd734c029f1e0ee3392ea7cf415d9a4d6ea316f9bc935df338793ceadbee67ae47e5a202cf50d6baafbc83db1b4c26880109ba52218530bd7017b43f85d2b4302a3b190b53118bb98eb88771a904a2030688c416832dafdf080792ec07fc0e5e78825a00176098d7ccb7216465d71c2be0d665b9bab7b507a90fb6e8ca736e09c480ab0297ffed65af843973c516ef75701bb1e04bfadc9c3d1059b219626071efbdac081a9343a301f30d2f3a9d0391e1a044de384de2d8f0ba71f7aab9f8371d2f1908bf4cc4e2c2728d4474a40b398e2291605ee02b0ab0208850af80bdc47f87d50a36c06e56bcab03eed45a59e911763181c74fc7eb8935f320a2ee60f09b645704127bf678a4e7ef21716c391760c22569bc53e27530b11e492d3774c596378c03c29f5477a35ddc284f0cdb44fa0280358b9fe9f60be8cb6a64e88311ce18980150f5e9cef3598048237e76305032ffe12ba4732de6135a52fc659bf2ed7e060fdef3394eb9b6960996c43fa2f1e8180bb8bc6f8a183e948be8b9dcf306802907b32e1a86da99c79d751fcdb9752c787a3fee49abab581c1df1762c0b6274c806f8be7278d5db14652b13dfe207402d329a516fa604994c31f125a4efa91af70abdb68d04b1e2ecdc278bb7a633d13cce6c6ce1934d2f7a7775e5e7aa8d8c880648424690f24840bfa34866c360c74621da04891da91d9f4ef199b69fa3fc560939bcbfcb88aab99d2a58d8e94baa520e79a4b3e3590eb21a5a1dee2e93225e006108e8411700ef672fb1767bc35aedb094405cd6c57d097cc191833cb19541071b20ce2eec3ce9cfe013a9eea057a516b7ecf20a30f09c21d158d07bb313110c096270746e33c13914da965c706d9a9f3e896efd571eff42189efafc52df380c6f970f37aa5ccc7d1d5252dff1646e669ef2c2f346def22af7ceaa4d9a0ea0085dff3e61f883241a86036e9b0c6dc45d06b98c3be266a0333f86007a88f1580341b15809f5a5296234f253c32c33a9eca99fde3a19edc7ab4298fb245f17000f5642218404cf6d1176a144d4d9f9978955908606ad0c414a034bd8ac4e0f570f6d8bcebead1e336ee5f844b1803c789ed9d4311d1bc14bd70aa2219a87c5040698eb67ee1daf3616f823db06ca16fd2a0d2221b74031b470a349a6bd824f220fe1ed4f20e395352710db6b058878495e3dae5cebe44bb52630c746273b33a2035cab69d1e4403ad57930b5fc34549cc3ff1e6a7624166b1aaed10c7b54c1c2077513e0de35073b6f3fe84be1b04db3a22c1469bb7c0bf2410de8a7cae1c9ff095672abb5bdf8a5327b67967aba04e201b7c04634f65f7ca262bf87adca631e039966455e89760fda58f73093dcb4db5e2ccda7be91c67723f8f7f6ad7b088d0e6b97533b57e3df2cba35791635b3a3e28b3fef7da855ef94ddb4513808430408daa9581b241b38714c97e419f888f8ab76dbb9dcb3a2175e6f47a4b48bf3bd01819817052ea3d2dcd19053cbc857c616c34601c175a1aabbdc95e8bcfb50dd0a3c95b31b0ec91513d094ec705839e70e865a0b2114e8ef8a0181e8e79690a108f0a753c7ea28a94fea22117ef6d71aa26eccba87919d6b01e123b2778095c801ce397304a754f025afa8462951042aec76f47562b010b8c4f1eb7dd67b1f480f0a575343f54dd305f0dce6b37a51e579e9dc5cd2ed6f242a4fe3fea76e4b6a0491c70f8e0d981ae1d01a2785b829b3e747fea00fd4c4fd47872ef11d3d26040027b53d4e0262c46214504161a568182b548c0f0e8c80d56943793f8f204e6c06e74817dccc8d060ca332e169ba1755e782bbc81a010240b7ad0b0d8fada12403da4ce3093005757d150861247963156fdf719528fceb36a1afc7915c2ffa7700043ca8bc23aabeb29c1d63b49c5c9c43c609cca3864094edb82ddc28e3ada30e25ea92c13cf4c633c256299347bfceb8733993a557c3a45cd4eb96110584c50c true -check_ring_signature d61526745ac6c88721ddccdd54dfe5924d6baff892f1b85c3299b41deb239148 1f935108ae0f3c986cd8c69ccf5f6036bb5a28d7d6d7af6002ce32dcde8f2ae5 1 05f047c0b83f38b6d08f065d44c44581fe8ba625f5b3dbd6c0e947ed2fb47ac2 64f48ff19665ae7e293fec7167d05b4deb025cfe4b2ae82b5b4b27d87fd05c0d5b82ab5f278383114418366a0e0ca4c44961fdfeff0798fe9440b0eeb3ddec02 true -check_ring_signature bab347b7f7523889b1b47a52304568ca383c2781ce38b30a5f4365be6aa937af 9a655ce01269c1b9524cf5f01ed579925108224c7161dfd775cf660674dc8d87 3 9b6704f1d07fb8887f158e05dce53d654255fc5f5241d2e9e0c1a68f0ae15926 eb13afd6917def3c000e6f240b40c1fa0f6bdf72189a758dc870329de75ac83e ce49b09f780ab6b2f4f16187bdb61b421225996a77c349e3ebce4f582326f4a8 3dcacfb61dbe601ea57a4ec4784c52d3890acbc6918711203d03d72f3e08910f1618e5f5c3b14496c3e88673aa19f25246455184b6cadd857ab032cb8273690b1e1d295a4654ac0962e6b5ec043aee1e0bbf6e9ed81db32a6a930c8828d3a8017fb3b790ec2312b3018406b962255040b87142ceee30e867b1dfc3c32d8aa40b5f618b2172aad0915c05f1d0472da29cc186bbe4b43786be72a05da32222168256b2a655713bdcd075456e80e59a997b65eaa6fc6122b43b5c55c923bdf26e08 false -check_ring_signature e463c17c525b65e87450956096e6eb122ae516d186ee1dc42af680952def9bc2 22567b526e05cc4a4ca7dc7b960ef0b515a287fc62210dd95fb233cdda4e962f 30 eb49786080a4e784e4af223de134d97e5de03a807500669abdd93ba1f2f1f92f c9452db13c54534d93c430c61ab815eda220882ceb3784b9447e1a4e71d65b57 ae00560ac04587343867f8e26f45aeca95d97126d07fa6844c8948e8414e9ef8 b113f337eff7144ce29a77351308ffc5883fa3b2844967dab78ae9ed79238603 943f7fe1f28aa02552648f5e1bf685b2bb59557fce5827a64e6deba749d0e1e5 f200a21c7dedcfb28f7ea21b2a7ef223a74c2a38fcb78dca5371ce142259404c 70dfc6b2c2920fa7dd43a0d447071db904a00797221e388974a76bc0e0b4cbec 9c8842577954694eed35806784e17c6ca25dcb25f39d6c1600314940658477e2 2fb776af046cc602dac4dfaf0aab46010e0f793695ac1f8a42277580931e493b 30c64ec13de75c414c75f7b49b40c967bfedb8c3cd63e5f875618bbf25e21484 f0f6bbd1574a59d48a067b4617a114f3e01e6fd0fd914c84a96e13573e02e319 8e17b2be818deec5fa1bf64165abc469d7740923ebfdc49c19d9a78b4baceda3 41673edabec6cfd8e25f76ffc554bf61a740dede73e8b022d2cca90a2dfc0fc3 a47dd5b9ec778d44bbb0a220e6a557ac5c0d2ac895cb61dfe086a7feedd7c3ab 902ee29c4aac2584fdba709e0fe1d3c904d5693d65dd99090f8850916cfe2ca8 1d920d9f5935724adde7f30e6097d141d37e7bbac7b1ec7143a8999b46df5013 67f1dd974c80b35384e77c1e17b13468620d71a1d7877f1fadb2a2fafaa69dc8 def20e34cb3f07a5dc80f1762ebeabeda3af9636c5a588fafb870620829c132c f6a7342407f40c9a2d5bf4d347ebdb04e47f17b0485ef6020dc82de69af69abc b61c6ae4601ee1a1b706061017287ce62b1e9a331d4f0f0a914bba1d799388cb 88bada33021541febb11c0f8b2cc9a80a1125094f9dd75ba105ae4cd97f76b6c bcbd09972a6a6c748f6db02e9b8c8dcae23414517e9bdaa117d0a6e73071de8b 8c2288d4a1430a4f7c1cd77b2213d344accee4a8238add1a0a28388efe93a1f7 4ae63f90bce2434c4b18156092a2cc9a47497a13eb2d0a4d487dc5f4c80f3d1b 6eba6f2d7b7811b759dc8acc3251d21fedbc6d0a1f34253401674d028d4c3ea8 9ea6beec69c42f692cf1a4f9fbd4d82c03028d91f6160b28f767bc356cc115fe 317512922f76ae9d9222f95d1e164eac7e1147635e56c966fa511568f11497eb e586aaab30d173622cee6ef5156c02d778d784590c40faa796d9ac3532ef524c fb891a66efe9ba48b3c2ec6388f4518b051f2517057ac240b2980dfc93dbdc16 30fbbeb076bdee931d33348881a7162cce7bcbb1ffa967145c981c050d4d9e07 184a925920b24dc26db6a9b7fcea0709c855a15cd294426f840d512c7c856a0deb0742a87a5ff5f82044cd52c9183f116ad9fdf1e80814d008f59020257ca80d15ed5438be0a4c8f202701e3f00e8898eefce9fc65d90ec8d1e59b64e1b1730b67136189c7dfa59c0d27cebd87b4ec7a2268814ed0d7a2978f392098791c3d046dd0e700ab6cf846f33342941f7b43937b57204fb37710d3d2d2835bde178703c1945ae0b561bb632fcaa4749ea9354bef8ccacf39196c3377afd41e2bbcaa05b4d3b0f3ce31bf2cbba8a96d1bb2067d2d1cc1e9904f2cf7e9f8f8ada0d0890631ff292e702f779c5eef19a90afcc81c1c41e4fba6458766484abc3422f29303f9394021156f935cfd5fa8b6be8b61755b0ea7e16c1fed2c4cba068971ce5a0d949a86c71a3dc389c1615024ebb4701c75f472aece83c803cd40a95ca79a4405b95ecdc491e5eaae4a588505a701d4f0dbf7f3d5131f99d5a6a94ab3b92c6d03e257931852bd09a37a0df5d6c4d7957f7e7ceea0487050aba527889ae1e3c208da9918bbb7c6628d46aa5b63eefc3389c1f3a0f992d0f93dac9abe7617f9de0b2e48fabab1f9fa8e9ff543abecf40a26ff6e8816e96db3cc561c85b48296540e279290f0a459998806be5126feff97cecae0d4bea75df549c3f2f6445caf410cf9ef0db10f69e5e81c6ce18d3b87a007b982d786f82f208102e8729d492a47031174134f50e188740a15053277e922c878f2676b3046664282e7a19249b47a0ca7152aaabfcee356e3c276b46ae65872b6cd18419e0b1098b5223b0912f1010cc5a0308d56d9133a97a540843c9cf84aac97055257d6e33687e4ebc69149e4004b3d5f7ee6c5f2a5aea9f2afa980be0cd09a9e5db4848271707609494d734f068d2624d9aca6e4101466be4116e6632465ef1497c8db87c34304924c9c0d3a09f19aa23d91074c0d45d890cf04cb12fb9dd7e8bd26cc3b6046b7d86e49e9c50ca35e3a7660164ae8478375fec958c12219bdb04a484c05d029887655bc4771003d9a2ceaa58fd78d3607f3797af89229345bc25997a21a613b1985c5ed302e04e53dfd2a255f2f5c6b5dd5e03e0050ddee7dfb202dc4cded8644b668ce3f5003abad98bfbec4bd8642b3875626d27718d3cb516c228d4eda3aa351c492a14607a743ecfbe9e2f06eac8d6fca04437e58d3dfa9d477a4b34d3e0fe1c2d979c40ee8a7c4b5218eccf901a8363aef5b7bf4fa8584508705e48173de4b4c5a06710d0892b1c08eb89394a45d79468552aa07d1d732d972210651a5a13bf2bb51fa02e2ec27aed994a74fd324bcd5f3a08c7643f53c1dc66491fe9e73f2c31fd53e0731301283679ea56048dcd195202c32269029742c63990765fbdb063a9849270bcd8464414b706defa9d7480c570e8e257e73a5feda635c213c0cb593d03755073ff3e22ed4ae4ab4a71d9d5e2464c0c17d934dd5e73704a8c83082d831aa670c6e9b189f675784d1ff1e35773f9da479161edfe8d73f2a952a857d04a5533a02f7d3c6ee77e374961752e308b21fba252e5d7d6e5e6793d66ccc8f8dfb160706b75c276cff64cf4ba89b77045567f12db4fc38e6f002183aed668b2ebe75520889e934f2c26416f93b4890056d33d3f011c2d31095fd710b93ae89ff56d67701718b00908f3fdcb2023563c942e8f9e70e4ee4e3a635c117ca70acb4c38c090766386f5d2252e22ce207c01ec6defa6f9034921de6445f1da424fcd73c44fc00a1024a7df3a683a6baf2b109cb8d098cf3a0814f5fa8fc12b1db9a8d9242050027288fdbfbcd477e05ec5392be7cc7e8456855e4fcca912fe125fea9f445b5003a84fb1e26ae941f7129edb48d7bdef8c1ee4560845ba557e1dd04d47a7d5f086b5430e7f95d82a8b8c6730e1324b6544ad1dad968debc3b00eb47234ebc9007b16aa3acae9b2abfc00f534552204e1da7636ec04eea1f75e364277651bb35a7a6c3428906f7faec49b18fcf3f3bc61c21462673aa33d2d707a676cb9e8d410ed3b51701037082db9c03917bb5454f097dde546ee8fb8ae5ae6e5713253f01059dbc3673cb698e195d2cfe3ab64f644ccc9a3886cff02d4fe924b2b14058680a52a35d1acea718fe1d7c18608961bbff345826a544aface73ad94d657dcc04015c71462fa228320def129abfb3171d4b4de32b36470b269eb5413000eaebe10dc7acc9bf4a4db30e193fe4f2c8dc51b9204f4aa772d04bdef979d7877a6e1902499766f2739cdbbcd9be0a33ee78414bde804e02cf98fda1920de4ffa87f40072fd08b2535542b653479c34223254068b0b2947283fceb500e42b505006ba8069a80ae51d7f63e79f5e0f25f90d211069a6c84b34419da893fbaf104ab646601ac0daee9cdf9e24ff4b311d7ba85310ed409bee61c2650f1352095eaf37944093e73bd6198de3420383a73ff509ba09953f806f486fa167e087b215fc79b8e04fd4636cf97f721e302fb0d7f950d180664a9d53f9f38bcbd67f9207ded28d80d5b1f3ace4ff13fbbe1ec5803be89cd9a816e9f5297ce251e1d9e70c02f3966044d196a48bc267f4acbc2b2a8736340cef4dbbd4318057009d24b6d9ffe3fe40c437fd99ea0f80f15fb1c8d496ea0fb10c1d5e08f0d3cb4ed231a93483dd5f601e8438daf4b09c3f5e48f825e1ac2f7bd7abd113dc4238b30520529e35380ba0a false -check_ring_signature 8416ec3b38b8a1f630db0dafe0f156d99d901b1e936e38d392c30b07876e86d8 60e30422e222379b1cc432bbb40dc745acee0b2adee505638ce648736d7ca74d 1 48ab047042db7079abc117da677bf29825bd955e405f6d2031737c7d9cb41d24 ebff080ccd668f8722de8043933cab9c30bfc67d014f21d519005f0c4ee30e0025ce4ceb0c09eabe9ff9f019af70d7cf84e28913d21435372c0a825fbaaf3809 false -check_ring_signature 94309beebebb9a9f45fd19ff1210533fa81e92bb219b3dbc4e8d5545446386aa d368d4500ce8c34a957712cd48836ac43fbc92cae579c6b7098046abd9eb1680 4 9b593c7daba4f0cdb29d974ba33f10e8953b8f82e2b8abc586a2dfb033254fc6 7e79996dce1272ee4c372825b3c9e0ade9822478f8fda3ba7b27e899c4b050b3 0cf719f2d963efd8df581b3984d03bb7d4858edf4f073dbc72fbfe239d3a4e00 f48e06c888933b8fcdb826c91b8465673c3645aa9bd4a3574610cdf221bf6136 1434b29155e21fb577523fd19a0c23195cc39c1021edeb75bf371118277750475b96940411eda184f201870a06b45ab0473cd49def2678ab33bdb120756984059ea7fc2f0b63cf48a75686939fd2a967b29393894d3dab25e63e9b9817a8cd0c7c14f9e0a6445e652dd244fd7335e1bb8c06df36565051bd95e538c1b055ec0889fb3b126b5c8813b8e6d1002ee6a15853fabd8008f96224282218999d325a0afb43d7efe66eccb4acabe3884d4bb4f86138567e58e60b17f031ac84ba156c0027259110bbe25bd7951855387578201bf703af3927f73cfff04c303348fce305e8d0d2403f0384b10ed181d19dc715c15afa8014d53a06b713ad372b43a48b02 false -check_ring_signature 6881b7e8db8f25070d5002211d5d762b6181e7925f5bbc655dd0609c6f5ab1cc 2fa3ab8a5e130b9c7dbd34f2e305825fb7e9525a50929504a45985c1745e7510 1 cf2a62616de966e3c5f653e21d7c86acb2f758ffe75d9f4bd204c8a09aaab1b4 ec6a2840ffbe84e9ede5b9580601d6f9e574c2dbc16398960e8102bc54497f89300796e8f800a575e2d8c37190a6f568544d35d3a11d9d8b6da464eb56035931 false -check_ring_signature a68bef7ac029089d0bae40e771c409bb66c81241f40d22cd3eb76724e747ac7f 6221981de048ed5c635f24abbdfb7c146c998263102b97657a1cdeb6b005d408 24 891f3f1a189f3f84165382e9c9e69373fa754aa8521d45cb9e9037f49c19a2b9 c4bea4ef2185ed4979665fcdd2165b126abbc547cc1ed09986d979358ef5170c f052086f8cf502c34f2718a11d93577d0e435c6e237aa005a046ce7b5867a843 9fe20e8e8ffe4f7426fef01227f6b189ab94a30e2ea71c8d06a1a2d65ef7d7a0 00dd8c0975bfecdb1e988bbfc3fd26bee645523742015a29df4cb7327675c7b0 5c5da731cc74da1332b2b9f990439e9501de0832b35b8968f8ec163e0c53a7b3 6a08a28e0870ddda28b5b8a47d676334300be37b50db544dd52a7efbfc4b8997 0d2e70756c333cd8591930e83a965377b954c1c63e5c2bcda69d453a5eade684 5df73cb6f457e60f401807cd3909d37ed28c72de81222817db130c838b56c09c e8a88d179ec5b11dae1e0b587f20726c889fe57ded465caad9a5af6c0f082133 2798700cbe66247bc6ba87e906458d9a47e0e1a8b7dc3e5a630ed76efbab13af 5f85ea58dc2fe081d37765d35a08092b60c5217f388dbec5a7691a90036fc4a7 f24f39d7dddd43b283eba668d530cc5f0f7bd1880ad333f839cc14963b5f245d 77a75bb692f6590b35edd3f0f6e4ab978e1f5ef513307ad13e3310eb93781169 681aec7ed29e1c4f0bccab5c870be6b291582c870c447270502e8366ea2e2917 657cf47bd12c949af4b0ca42d8000accf30eb4b5c1648bfa2452eeff936c093f c12d5e06f0e70ea827987615cbc6ff992e693caea46ca60dc6b9c64d6e6a7fb4 0a290742acd02a215179bd5f16031574f0130728c8990cf2e0fdae84e4f58917 f4106ec595ac73989746e818f556a57c741d541bd533267cd7da4ed8fc1adda1 b88d0232799f981dd00672050136e8ed8be5ba9868bc854478d78f92a32d4262 18fe1daef89bc54d278b00ffb7e0343f192e474c7bbc41e3a77fc01d53f8fe44 f83f01e918ef56e94219c588e98b8c4905b75f31cd818b909ce0449c62818f88 93fcc9b04c9ab2fca45ab8aab8e2dd052de3e627dd5fe7a996bb5007e8295925 4997f23bfd88377305114db2298f70c49719b25670f12fe442b315a1e85afde7 3fb982166bd4ec3245cb450db62a0221b9968f32d121a63a375592002ddc420f71bbb95df880d93860b8058aa9f489718cc4860215932bf40203b5bf60912f0f7b1a5728f36ae9bbc5bac78538a46a1d88148e12f1e7192af522a0a0ceaf7d0065d5d3f291e6c6dee921fff24fe40a2b2a96c3794c1bd178671b3b9b0410c2080e6dc57d4e453274f7345b3cca7e7edcb10ce06321b6f8a60839cd59c35b0000e5eb1a1232817b73821224c4377ffa8dbef499198099b5f4b474c93a8637330fd21bca2f7f0058399a0d291ae5b943da641bfd6e3e2b41743bdf3000c52df70816e8a6b18e8b14f3c00ed2c9e1408b33323645fe50a4ab1866d0060ea2ad3205edbec20bb4b86f27d2a5eb8a6ced75c375a220b3ab1fd4d1e162728346bc67085690e0bfd0164f25b3a4108a62f82f0547cffce231e27fa7f84079eab09a230ec320dd3a12a8478ce73f10af3e0a6683c1ecef5c0b540e207b24fcae5400fa0f89530a22d262160eaf40e9d97b9d8ec2a82863b6c74bfaddcf792c40a48c70034b68e83ab18b55cf03c849d751f71be584239088a1ed85ff5cf254177b198d0e8f046f05c37637cc92fdc07f4592224b6d8acb8457064d6f6e4a4cf57b3fde0646fcecd725793606757a36b22b4c1a6d6de0b693b003286f8a9a9088d88e0c04e017afcab605cc52e470e38575daea5f0da8167669a64848e5c26542414b940b449231931316761df8ce280b2b743cfd3b0cb9e2b043ece09819a1e9a27c940d46378d5069288796b88bf57abac09632d5eb8b49b0a7ebac0b89f15ff9764e0866603e21096baf52df0eee537982b9a333b0800fa87aa9baa7ca8b5863351f038c108b9c87408b661a228eb8b20d32e0b53c80db8e42ed7b19fdde2422dc5601ae40ea6ee400d99efdecb7ac75b9fb92457caa9ce653a30f22eda25ad006440eb4b98eceda4d29c70ca15feebba678434091381e99ae7ff07664a9ad3e048c0a86ac3b92a5915f96f814cc9f86d8fd89c45df8a06400b51f14c152fb66a6600a3496f6bf73fd129594ced8dd9a2e069c6991bc8a783c747b42b730cb8357a004c0451286bbc6eb9ae19e91c29a135ab29482375fed7dde26908a5dbcb10d750ef4f62003bb7c48b24995a3bc8e1a36e7b5cc73fc8ea4625112648b6d01d32c011e6ae3c0ff512a1303e7108ee463f6e993b0cdf81adbaa0cd1ce2bbd95c6bd0d29ad2d5df20492b5684952856fd7250a33d36086ef62003d886e872e35d69b0fbb837b3f55c3c46c70aacbc0c290b9f46258ee1ef6eb14e783b8392cd44fa40566c3ce440b2d0558e830f946dc7881b0a2d6f3cfd5cf2f6ea5f481c233a11e0ac4e289188b73793ca5b85da85853a762d894b7d91880b9860abe70ef5f28a20af224d9efd607420cd4169dfeb8ec788d87d9223bd91af7248f8802c651ddc50c9c5fb6c595d13fb0dc7cb446c2b8218231c413cf32b03412e3639b4a7eea4c06b84046614996a7a041ffd6c58b7dbc252cb8858d6b70333ecb42d800363db60d8d8f85b1c18c59475b81d4c19443e9627e5e51066c8261a457e8395fc4deea0ecd5c46405c24ca623fc8a7156e84e7658486582be491178a0ba1e83a4ff79309bca1d11fe57d610a634c2dabf4fa2dece6a2ad0379bd25d676c530fb4d9317020117d86e4aa0cc84611f19078704d7792e33be479aaf603a29993ed62152dc05fca56e8e255af63da38e1c25781cd0626d17bcdf1b55715cd4b10048f0d21b072989eb64427ece04bccff524b20b4e897ac406c8f731e5c0b8a4553be3e30a0396ed9bf0d17ffe5af704ecb924424149a371310ee860c2afc35e629b45362f0c641a3c627b8f276ced77be16eefc6b53af8e621357a5377c08ea5fdc5ab6e108d24f0fcd868f98f4372f76fd18887c22fe8a53aa5e4983210c3bd49f0c285e08b382e43fb93428d7d2da187aa9952436835bf0ac9427d94aa4a8775856b8eb01cb48968f1cb7d37d52d6df0f1d83bac5b0435f9e02ba3a1bfd2aa6732b8e450e8054cb7f2ef6898aee735ea75e93ed71441b0b75a0cc78b74995af6e0752db06fb6905d1d97c26cd6e3af00da1f0b230c6566657638c556b31d8d938dfc9b909c13d0d0b2653fb5bf42bb356ff7cf83bbf5bc713631fa10e754fd7e395666708 false -check_ring_signature dda8c96060f5d18346aa4e34a308ab5644529082059a2f3e50eab5d7a646bee8 4ba9d69808da60f2d202424a9d9ba8f31e0398e7df0b9973c907f726e54825d0 1 ae5a64b5f8bbd8dafd8016db8084d3eca467b00876b4e84b21c9b8ac7dfa19f4 739741e635fb806810433b38e9df00a933d8f5cb085fa3e450f0068e3fa76c087ef53d76ae8bbdc160bdfb47daab6b4c11d2cff6ba8384ce9308c018b05c8d01 true -check_ring_signature 276e3c67106378a7581e3e070b9085ade1944a04398be785322faf8cad9fb4be 5532cae293b02ad3df3d82ee23ac44c426bb65540b1ae5ceadb8291c3ab83101 156 65b5fe57540dd6c228c4b9044da46f93efc98435099b71874c0841842d45af72 34bb878f7c7625d7e198ffd6d532c95d19ac627c89be52298671c16a14106688 84e2b63e4de822f4c43281f04b56488aab89f72307bff17d1dcf5bd737950741 7103868a316522d9e074798ab50cfbb0f92311e87a07644a779e5c1689b86df4 17405de9833c0c0f20aa11d44b099a328d5164737365d6ad5ce50006603a7f78 59c689b6b857467dcc20587e7a8a948dd03977635c3021dcda82c9af2f3ece5d 94290ff444fe73c48218d6dad6a9ca5c60206c89f45d867371ee1175cc60ab62 6227678c5add793ae22bbbbfa8b115451826cd6c3b94f071cd45242d6a5c3f30 d11558de4c1130bf455acbad891b9135c90d375e415404d5b6178ea98cce14ca 3efd62a67e69a368e546eb7395a54c5cc83faf0edc8363d8ab9c2b800b95e32c 660d5e46a8eb614b394646efdf603c54ffcf233e678f0cb8677de732a473fe87 90ac7e891685c378117f7e1b7ce66983d0768c804c0663a18a844db4eff65aae a57df5489626a8586275c571731ea7140d88964af588c5e0329dbc7ec68c6bec b2f514f34813ad9845de16ac20474e4efaaadf74a931d50880aa541f31da8308 93bb9ffd47996d622c16bdbe8110e7c5f570b5f19ef947f680fab403e2beefd4 b0614cd1804467119702869cd204bbe5acf857f1220b92729fa14fa199f9898a daab33e8e711198512d1a7035907b9b95ca1eb72f49730e32dddc31b9e0bfbe8 28026c64a677f492ee4458941bb1764584f8c9901b54bd81281196eb267f7c3d b8a2db38fef01c5715a7b516c2e9f9fc1c7174e4b592821517a8d21089898313 5971241aa177d0f5afb229e9e6863b58e1a2fdebabeebce22c826a56b02eb42d 0fbf4170bd0568ec0050d82411eff9151b494ab458f44c70dda3d465c5ca20f2 a0ea26e4b2fc861229f8a66685b782f1c5a1abf88de77bc1b6b912ce96263f9b 4dc3cb5c76dbd2fda95cd488fe9ab49ec6169ae728af63a776ad465dfd0c28cc 3af87d6515ca06b3958dec2a7fb7b4a9d5af14ec08a5ea3a02d93caa40ccb1e2 3633040e904a29a649049254b7906d51991f7af3ba395e29ef1dafbbc794ee79 506dc8ead0fb8619829c5d2665ef4d0f56040cbd68222c04d7917531762091b3 06160beab321dfdf9821821a8cecbf2eaae98e11147083c5e46cb744692d497f 161b66285f36aa40dd765695603f51f60379c14a10099079d29cfc6e2d8e5a20 0f44e3f2b15a7afbcf8094199fea4c1abbdb9f80e003859818aa2b3aa83886c4 05a5546381cce39b8ea0e327fcfc95081208f95e71b2d06fefd076b4454cf65d 4870c2a0773994b7fcb1c93b357de1df88d221a4fb020e5bc992523a08de091c b96b98b5cf6b1052c716ce1d21f32e75be3ca95eb92a9de729bc9a7197b62a49 9e48222c847f90ee463c28d4fc4d6e39e7e08ae250bd543132b8147f3d71a2ef cb3351e213d21ffc7d60d0717c757772e4cc11fd66ee8b3944ff23f700537c3f afade2b6dd45abd42f2bf6d4b0115671cf55ade63aeec12af8c5e5de5c2a21dc 70edd0fcbd79c37cd6295d3cfe29ca33bc8fc6854708eedcb66220a667c23f78 64a45e4962b5cfc7ff232e7cfe0a677d4925468d8ee92ef6065368de17c7b951 c42bfb3952aa740b0e804f84c29e4e745697d7dbb85856a27cf53afe3ea7f577 2988663989ee6ae2d816028d0e96b10fee5e562a898d13fe7db65453670a6fde 00cc5e0818e1aa3a27af8da47a04f7440354ef422cf91e5e4e4ddb3ac64cfd44 b6aa98c46e5df5a2b21963d147c1b9cae498725aed971d744f3762a6d505c29e 5f3e1fa45a2f7fdc3e3a15cee6d690014344fe253a0540c9a642613bc857994c e824138d7897d2a9392cca20fbac3af9869fe1980c04174c3fe94ebad3062eb8 c6fb9a9c7482b658b39bed2b027e4fb17fa793ee852f230053cd464f92ca81d1 965afb02d52768b09a926c1fc5cfbf64fba25b58b1ae0217928dc10de9fad106 cc0fdd1d70e38f4a38d33e697505e9dfc98c42875911f4ebe91dd5cbc9993719 8331e95c6aa5aec83307afbcb33197ec4fc32b2050f7763789faaa4e0184c17f d6ae4ac6180f3ab605eaa05be916c53f567502487753ea296dcf50c4fd8f298c 405a47189045de01367b134ae9f34cc2c19606ef2588ad7f8b77d7f2553636ab 68e406ab5f8efd29768d9e183672d25db58a67d751bcb711d3437241022a0df4 3c53fa3659c58379f4206529a4aa4db5de7223467412a9458ddaf448ff2775ae 1d2f029f2ac40bcbb42c63adae28819170b9d82ddc859e7c289efaa9087381d7 a7a0ad1ee439af4347fd0ae14b0baa5cbfd5f03fe5cdcb51a7658801af9b7b59 e1e4631e1d36f5f5866cd75adf407f1dadcaf7d989011406b63310216cce57e3 f59eff9cac1ab32a9d21995b1cbb6df5551d292c39c1e1d34ebac94afacd268f 428b4c84feff28029ff8f6819f9e76c99bcbc1240fbd3136bf19556175b0cfb5 df66ea94a2481547fff0fdfd2ece82bf4ccc4b46e372e7c1e0f5aafac5cdcac8 8ff88acaad48f8957ed6e9858f8b1ba7b887a22c499598b7726615962d176440 42417d7dfe3d053398ed3e78e021f20e4052bb7f6d0aed9b70c21b69c9cc6a95 6295956ca016a0044ac574aed2414689fc4e200dd8ba1b1b5a95c7b4d3b82240 908020acd9baacf6d0d044a73a562f65eab779f071eb7832549386d682a34a18 c5325d8cd4c0d954646976ec05bfd0d532b6adfc2c2061c5f880d968f7a4a1bc 50d24bfb6a9f8755603da2481273c1bd441a7e13869d37d6e6e6df2a07efe1a0 5d56e324d4e46b6214f308582558cb93a4e6f26d5ba8727ed3336701bd0ca69e 8aa4ee01540e93603594e0dfe7356306f5c7429529fec61825c0f921d01f7ff2 e833209e5d75596196c81e915d4584b0e40738dde91bf15ab7c7973d2a09c36d 9a8ad22dd56d94b326c3430979bc0f325ad981428184c77c39d187731bf96912 b17dfacd6881c9f8e4521a5cb50407c2c3ba64fd145f12c53b261698fd37b154 c6cf71d99496db7f056bdd86d2aaaa0bfdbb208494cc1495987bda3f4c66aefa 302601ad40586f0372b0d6477af5f0d6f7497bdbd8f57518b0444a68dc9f64aa 110e872f0296fe8a393c6b067e206a52d9e8199d4098c3c20cbb3edb33962f78 90df0656de186e23f3518388fa7896d01feef16473932f3744587e649271398d 28ddd78e7e8daae4d17fabadbe92d7c29a55fe3043ac48778e49da559ce64fc4 afa68418ebc77b43812b27238d6fd5258422b869b4fdb5a0087eff020c8dffb0 2ea60bb46b96a1dc3911c7ddb29995143f22f62d853017c26f18e89329754b5d 64e3747c94dd6086368a939cd2934d820ddd3dd156561de15f19d5bcdd633a6b c91632c8b7c073110828ec7a034c54867abccb3392fea3eab465455e1d994bfb 770d8e90c4435deee65084fcdc9208998954532a28f91f410d80c9f112fe9136 3cab314c676afc51baf83dce6f2068eb7108c93baaf3dd0df40c0ef92dd382ac 566c4db491db46f9520a3a868773bf38e95c92e6e775d1a59083a75e8b6839de 64502779775aef0ef51bc602989d2b1faec90df0144e3998fa314d98cca6ee3a 207d3246d2700174b6443600c0a08ed947c6432fc99a579071c62a05859676fd e517136c139178f1bb2c428b9a20fe13bc4d41ef0c91e7022a665b5787f0ca61 ef913a3cdfe1b6d7fbf52ab5a3ee42103ed6006fb598702807b5e96645f038b8 6b57a514015099a23194ed7cdbd6fd313f7cdbec7d22695709677bdc0fd2fa47 ea37b83248b69cbcc28f5905770cdfb572fc71fafc08e5fb5697eab9c38ce17d d1bf3333a894dd3954928b7217a044d5ae03989309b791e53dc28bf7136951be 4ae6000ec1f4f0ceae8ae8020ba23d946281fff5e20cdb4abaf0a609ccacf23f bf5e5fbed86e3846ccb194ebc3b0ecaf88fe6c49bbb521bb326334e971a28e61 61d01d789f9ccb5cb7351051466a9bb07b956a163e531135caed83c33aa362fa e9b9291873ef01223e51f81b6e947924fa28d52cb0162293f067101b1b0f084a 801069de40a07be1a0b61fefe13ddcebefaf2e52f39281f1986f3d58b4535bb7 7ccf1e47d53c4c89d11063694a67fef1b16d9f15f065decd91536855a094467f d994ba5a55549ba19fa200110708899325a53f006066ee7fd426e63ac6ee5558 47cfc2088e80dcb06802e99d2d592e1ad3da90832c848cbabd00f5bd478fe299 ac61f0b9f8f0f6195419aa47fe8f443ef994cbf894d355fb00206aba03b29a70 c27e14d731741fc9dc05e2cbf11d60f12e415bce4340248ef1e8702498b41a0c 65fbb037831ab8dc8723075bcd2fcf63b07f207b5441b7b59aa94f1f8beaf635 9ecfea44e3d2ac28d75d56c736d31bc41bf21a6b4b3d3e232a171dd4b99d8d0c 5ee8af170b5f4a82cf1d4e2a929944dd446fc43cdb50c0e2fab95d6380100769 a0db33441e3b433adc4cc6eb44c470a0c51e7980cafac57cbdd2f3e3b7f6e8ef 586e6d3461de33bbd79553ec6a3a3adb0748179b52699b8691d92660898dba64 6ed7ca00e1f6ac1bf9937b7c929197cc2d1dbdd1d23705e50c9ed23df7391c2c d59466884f989958fe2f293946e6391de9adf59f9cf6cc50098b1b5f1b417874 38e8d0d68c5dfa615b363d831e923d7e6320a787506d5e6c3722d95f41914ef2 ca60f75165556d3781ca280f7b7f715d0aa2bb6a40467e7f125e080c60b422a1 c06815714926a38ec5fff89d67b7d91ac9deca75c9a0db4a85d7d744a4c50158 a69f0156c511a805333c40d0196c4a4868a01e3628fe4a55fd06bca7c5fa32d2 4566084577c12c2203d3860dec911d553a97f8c5f335d4f34af66b427ec7d993 52d4ff90448c8523d20ba3d856c0d0a94d10efab5d343a17c7655123d5a21734 08a39e10c8c5221f309380f78c97feafd09799712b1645f1483f31d39deb9329 cb6eb89fbffebe86fc743e0939d365babf76b22a6fef00b22b363a1ea2ac51be 86c03d7c4eb8b260fdadef7f3506e7339717d606202b86749f6eb4ab9fbc58cf bd7337fe2b0c932a5ec4a9337ecbd2eb6c4a7a0170c57502d948adb3966df02b 0d1c86407e38cc957ae45f35663c8149fa570f58376c37cff922d35ffe9cafb4 57bb54e63529c99f9cce1e0afdc1fe7730760c9641f4ddd4b517bfc63f1f1932 f5558f7f1d922a1d6c0052ecac7f4c605ed1725eb123520621b32047bdb5972b 56471fa15b7ae7dbbc96952c93df685041115a754d033f8a9fe48cd6222e7686 4747c7c53a57e1ce4c8033a7ae04fea4577fe65be4bbe60ab473d0c0edcc6a7a 17315b7d9aff88a53def34a46690b027331d2c2f49469ea4ddfc0ee51dcd2a4b 8a8276d6c8ccdd0b2f845b9c44d597481b8821fc3e0c865d7f0eb7bcc9f9719e 98d8eba4fb16c17871aeced9dc54671ef858b2f656fea8f62f7356b95072cb20 0ab0d4246b25f98e4708f3a0b4378bc3e006654c7d7321c8bb4629ebcdba5244 85aefd42e14c103ceda917f4aea1719b228d06ebd8b23bec8395af6f8527f3da 4c7a5b7e6f8f97b781cfebddaf5fa9952e3fee0bf1714fa0b5288bbaf533c161 2a85c0a9646db046b91c411c29efd3390b21ce94f07c2022bdea4c977fcbf674 ddd089e731f10b24e2cc9b6ab66b03d102c490d51afcb6a6eac9130774668ff0 c7c40d0f04b39d10abf5f3fa6c6f883ab5a71de8e80f875fd58a95a5f4d284e7 ed7f01c9966127bb9eadf0a9dce1ec091c2158d7272ad2c93198ef80a849bd5c d1e87a816196ab50e228a43667934b4bafe2207281cbb03ce1210286f9f92818 4b67afdd757026310a3debda8b834c951438f72c893998c520e70c4bfedf3e28 cd55395eae90926f735bd762b307d4d1e864493a5c3257f2a8a2261b04473370 91ccc664bbae4f70bcfc20845d2a44502b32c966e4fa0fb08045853f5ef2dbb2 5ec8dd9f0763e22a4075ddc33ee2f544e81c172e652ec1ebc69f4eb10caf57e0 a2496069972ae6e559cdb5764699ac9885c2f3f0c5ae388b617f0ffc0b8c530e 01ff4eb03cf7446d87342f64dfaec6a59cebcd2167d0956ee8878bb964936bdd 1467536dad0722dac294a54aa255adb7c83a7d8dcba1265c0062cfef1d3cf5ec 0c75f0e1a483152d747b41821ae7aaff8f20414d186a875b9aa01306f76ee57b 1cd71efc31a2d40ef3337d2965f74ca00b82e91461df6341eeca2203aed6a267 7e9338b9bd0bd4aaa700bd7b92a890f16ed9790274b1a77f0554872b30461e96 f13ae7032d699f9a65c1533466ddfe26a2f5d8309aa2c8a1c34f1d5a31ae440b 3190e0d8b9d8bb6002e3d034f84909cdcb3b167f77d466216114cd70cf0a76de 244b0d406b78d8c0e6a0de1974b81aa15023b46cccb6209be5f987ea4e09e19c e1d63bdaa68df26f044dfe7d8ea3f4eaaf1fc10c87a86a5f7564a981d7f26396 2f04520cb7c968413eead55c6831a0b9ee0399539ba3a4bb163b8ef544254c08 aca1f710c191469d2beeca0a3ccfe8929f498c72ace027fcf6e123ff9addb7ca 4fabdc420bd2cdbec3aa516b1db42652e9dddf22eae1e150da9bfa3f215cd585 05e9f7771cbb24dca0ca02c6497797fb612a770873579819a88e72befeb0ec23 9f1112a4d25d7b3deb898a76b3cb302fa273c08ffa0415f711a59cd5b326ccc6 c32be754cac3558638f46ef85fba5ae5983506bd193a6a9854636d5630cf297d 5fdce8c0fa66b69e606e9c49393c18f254e4bc8d664bc3749c5cb2ba0e657c3b e3630ce33dac38b66bde125ded16adbd1613315ca5f39ba5b7d9c31b15ef29eb 6737ac8ad56f346b1545c1114955a0a0d89b23042650b337b121b135e96634c6 070f4d9e8b6fb56a41816480880ee1d0d155a7373bad862e699806005cbf5931 4ee3ef97c025425ea81fe1d95de53f14d0d5875a18dcf589aba934e05ed4b6d9 73933e483d15d7c315aaebf5aaf5a0dbc4f927615dba02ebd4675a665505177b 33f4806f459c49195f8785ace7ba1aabdcbcce405e6b16118782fc9813797b08d657e8e944e12f37d79a6821ec818ca6923ac41d88b00e18b7903beef9d1290c4f839f8cc4ec242f327efd9338e30b96943a14031bcda76c01bc32ee69e3c708e68ab02b6086da20c86043da85632dcfe0eaba250948cbcda0573fa2acddc6028d5488fd9939504d9b5403d69c7306f743195c8d7561f0f1d5d4838ce9412e0e63ebe070e2874c8396ae30ec3df2bc3e044566876411b7242ad36864810f1b05820b5eb5cb10e9bd5e186631f6290f8efddae0f4a351e0a40702f82fae88dd067873f04f9ff600012a891101c197519e05ad87cbb697650806ae7338da4322019dc2355b29311b364a6fe692f1c2df71c19a142a8dc0c42d8998100bfe8c6c0197d717d2ecabb5fe0d730d4c03743ea7a76c0f9dc76497f8f8bc9f511aa11409e4739936e21ddb1718184812dde5c1f2a617aaa2e06d89c871ed4f34d613430acecf5af3bd1f7fdf3d372002f4a279656bf3e0e9f594775b654ceb19bbbe7f0d0b3cdf2c0d85012d2640aa6f731be63c69c9c3d57d5503df19977a4f8e174b059e0f50c8215b5c1952d8c1d474ec068de8effc7bd0aebb4df87e409b82186d0d5c7ccd7f3a298b63b80027ccdf514bf1032ce06529af22e454bc63d65dd1380f55d91aa3713b983b0a4a435ed27cf6bf093c1209c2efc30a2552764d7fedae038c014a835943ad10107c4f7ae21d6e3f60a94bf63c5b7eb0166b9a374475a50de7a9633282554a2b9e41d9a3d71bf5e4a1019074e593929db8c1fbf52ada380227e3085f5f421859709fa58aae0f5618dc1136d8cd073d4d5ae01b5cbd75e50a7b086416756b36486a9852e8adc7061ee79073dce3ffa70885aaf4d975b4c80e1e6e0b83b9f322514c4d25da4e44876bff5d1ce8cd9c57545e74a40ef232f80125e166b280a18b4fa5e9b1004c23d6c0f6d7803d974d6c0f47e2caeff0054b02ac9cd6c04aac56fbe245ddacf77b812c20255beaa9c0d611abc9659b2d78f2098edc85e7428c2a98f79e46250cb0d264a30b98612eee7dbf674a9c5e97dfe90c2f38d521478247d25ef8781ebce52c40a484b6280df69b6bc1e27161a56c440386e6bafa8de199e151ebdb23f51a77c630be64077d0504fc77f01b5611238201741d289666b09dacc6c7794d393af333b483230417eeff7528f1b97b80f05a0d15e049476c2c8d24fc15fef0b6fef91d60f3f3b6ab89b86b6c23a7fafd3dc9060e08ac6771aceef94cad58f99e16d687aae7a945b5972ecb0a5791cb80ed75087b04458f32d0ea7689c5964d6b3cd8c1bc6ab1304d0024d15aba257ee861c703e393bb128bf8b6c200a7e885066ba31fa0a2395b7bc368b954e085b9cf934607d229b9a7b857b2b4a3c6c3463a54cc14283e82aaca473fe21ad96e12e7a9ba0def8f64cb11c9f989f2e289691046aeb8064dce68c7095a3687ef01272fd6a907cd7a7d8171601f8f123cfaa04e0e90995d24a662fa8f8cd8eeb22ae7ccb1c706ec0f51263f458ccb85ab6e4edd603658dc196848839e2579bfcebcc0aec0500dea0ffa0bac672c6bfa3bba82db8c514fad45282d6ac6bdc34dd878ce8804de089a8860c6eff20eb80e8b7171ef75eb49f4a48fee5c5b7acc27057d35a9486106dca9866e4aedd61105961f772542b8bad6579900d4bd982ca27e8ed42434a40c6bf9a75b9905e3449afa3812f986ead454da7ad12d1dc9eca3f65db08d75fc0122a8c5b8bf413d73fc255f058eb78454df957720754c5b0c88fdc513c315cc01b6fd9b31fa76df3aab649457f6fc69b10277e7212bbf50097d5e36973c7305018ff4bbae2e7d93ada35f8865c82bf94072b1e66cc52b28f375c89f8aa3f32b08ff21523677a8a82522e436e3df543d4e45d16f76b3d8b8e873d02e516b294208335ec7d89e7647d952a00a86c70db592476113e2d92d2a37ab9c7325cc3d000e8e2c1af7d0987f320e3f8859245b4139665b0a8cec18217c225ff5e034269b028f66e2fab39ced28dda6939063741c0b67b33d18c34e27a3766ea9a85f3a7b06a6f194f58eaaffbd4c50e397638a26c9468ff11592c823dd2aee77506b0d340021f01c83a767763aca6ca4beabbe9e1561d8fa86072d6d76461491931f6d74048709644fc2350bb33c7d0da82497e72c57b5b684365c26dbd91051dd1a2e9e084659ca1cce7f7e5073866fff70966b856a79472c194f3d35fab1a6f83c176e0b004ceb41ab3ce3c648d1a37e9eccf6e2154bb3dc1d0986f61da1e4fadeaf7c0f54ab0c90614c522c40f6732b1b8ef4c689f180c65a981a2346aea779a7a0bc003e5781b509ad6891a3aa3e8d0d2fc6a5f3310ccc632835006541e69f993a3402b19e43df61401f980b5fdf2555d1304ce1370df31bb088f1fe219fd21ec1300c051b849a8335ee939376b44ad613178c8682d61d2a8bf58c71e92c3abb8b0405bd1347f85443fbf05a9610be8796c476b4cf78d1f0f5082a1d8e6b05f324e00d2c7a63ede95e7f2daef47ef49660aace9816ceb5bd16890a4ce31fd2de649001a591438b231abaf96abc1ffb115e033a9b61fcfca90e27133c19719f154aee054920ebafa4d5a148894f12cbbdbdab29d62daf214f1000b74007bf443f70f60af1409e428dd52a8a2925cb9acf73d10e61d954255a47c74d7c62c02f116dd00dbfaf507e3ed68a89d03c38c0792c6f6a95e88348f1ed83d12e3f07e5f30f8806387bc685ca3cc45e162b6ba291f7f61537f6264396be144a06d9df8d50ff6c0fa6bc03d27873bd2f9f4ab3cd05e0993170e132a5ea4728e3ad1b3908baf4bb0e68ee1356dc4cf0c48a8618510f0661c4da056d56c641cef801e60ba9b4c28b06c455d2dfbc206c845725a940957100c8257a33f3a92ff638d38345015bacbb0d3c5518c79635987a6048f210de0bec9c31b8e60228b26097ab6acc79e8af4405776551150e1103e9b167a5e4355a7a2445e721f411b1c325562c8255f7144709ad3e145f15544f8a84fa0ed363a6775ecb986c668baebfe1020ceaac83bb210da9c711fe3eb73eba7ab0e02a0f9ee1defa635359a152110c1b5dbb41710e8c0b6f6a841d57590687927a3d359dc13902c37ff98bb1b1b330d6abb9b1c267420a6e98caf8d32b8233ee80e43d4b4281ddc54551d88f9bf2efdf6550c3a8e88c020518029d83ae8f190f0297d0c24a0abb560df308d1f6d77487a515eaefa39700e1aa9f10d397f34e78fbca2aec630057d023e36a44c318a67d42bfbad96b880630d10860d38775c070c3689b1a420b9d38eb18a40589fb9324b05adc648bdd03f7e3eed849bc69ebbc9046fed6eea6c60c345f9505130c6f91bd543bb7a1f70012f109e372109f195d07dc91a087f59eb00ee9f3346c8f19052543baa0a760096f8407e175732586bbb722552c007e42d3126def852e59a2673981d59e1b0a02a0ae1d62688b920efe0ae91ecc5ff51c3d6cef1d5ff6bc953ca656aa12230a05470f096ee5f8c3f3c92e0e07a67bd98b464f90aa0a66e77a5484f879b9d6da09b87ebf094fae214841533c17b2c35901b7bacd01fa205ccaef5c6bc3ee513f0c2b0366157fba138516d4e8d411ea7c9410f98aa48338bcb725dd99a927179308765a3ec6cbeab58a174780c534194cd814d5863f20185bd94bc5e26a991b7505d6a44d0f9dfe0e2fb3f7b11902e6a29bf8d6864e1c8f93c2616e4ff6431ce90fa477b1ecce3afce75802ac7e3e1a82979c1dbe63351e75c15d45a0e6782e5f0293119f1a62dcd1a4ad629dc9e679bbd3ecf8e2158a1d39a5680f7d35be189b090c3306960306e95231b0e0dbf0f4270d7bfa60ec4865efc384e099a54e95250d0865ec07ffaaeac827b19ad3dde1bfb804242117bfbdf1b00c9de8976e18ba0cbe15e10fd1fb3ecc601ad342972014fa329bf9f70721e68bdad6d22c8f93e1026a850cb199b9d05617f9a65a670afc4e546ad468d15827a353dfa85a7580e30f660f89c29fbaf3f19ef8f8b54f0b584b81c354145bf5226aaf775f9c4858f30a841469640112fb6c456cfee6d5ed8ea2b7bac7f366e41666a98a57bae77e970ddb7607994ca2170a95adbaec95e0da1210d66489b0c437c6f36fbbd50502150867335783dc36cc94094bf256ac57f4b6f8b0d5dfd9e8c0f8fea382c122823a0be372c27017bb8e115ef55e01f61216fcaba683b0eca2445f8dbf5edff5faac07a5073a9b14d92bb832b48943d8e0c226439e381387761463bdf0e3ef89d50801573ce748604181e2d7e7fcec01f840fb91be538ca5b393395e73ce3edec8c50ed7d4e62e1571318fd97ea60f7274ecedd09ce9bebf4622d08e817c70fe650703f984e76938692f42c53e58dd65cc8d6829f6fc9198c5aa872c8667971ef57400acf62209ce86d2478169a3d340b17053ea7f77007a65e34eb5aa8a76bb04ef05785d827be926d53d5c7286d15ace0b7c4df1b74f64d4edb4226f146df52be20207f8514d8b2c666d24bf8edc5c68f68089acd8971512c5a51ce2a66ce962270fcdc7a38586f46d4f194b6d12d39cb60d671bd4a3423b85628ba06e4314d63408b0683a937ebf0edc9e66dfe8e75b5ca0dfdb4fe08a05f582aea1cfefcb86510405c4d4767ac77aac995435321e0e651642effd9ee472cc156fefd33494bf8d00f570cf4b094fa588120f9505ac941f41c29065e1c5791cbdb5d97abf1d5ce700a84e8be64e8b88ee3bf017edcadb40ffb6d445e489a3295319c342d1aa06fa0c65671f88076eaa557d110726b543357252078fecca4530f755883d66b0d054087259cb3f621fc468713e28db1924f892c6e0ae239e129e61f10550a5010f1a01d341662a1e1da9762b9f90f0a0eea413bd39a1798a38343052b988bdbbd7650f69147ad495025424d35ff139693d490acc9b88392c7b5df9c97a64e32c2d0607dfe3532694c305da10fb37eab2eb3158ace3273e99c59c11dbf40fc75611c00eb4db5eb9927d5942d08727581180c8f58679ad12d2a38e2f1f6602195f48e70c16bbd9ac83aa680e6b4ec408f8c9e892cd8b2215e943e9c5dbcc97d54629e10e7b253a58a6089aa9d1a5f0be9ac2a58ffea01c05e7f7f4116c276369012e610992542fff432a89fd3d2b7e6839c50e9cebc90c86f84817f947a34b2d70a59f009b408bc80c12845605b730629b615e128b49b61654cc2758c049d33a9f1b4a028d6a4752c8af0174fea2d88fca7a0bfb332380dadc5593a9a5fb6273547853006298b03bd8cb3ca3cd4d2181dd6bd814085210b19177ee14677546050999510010491366ddae703299afee4965352606459cb2dd19dbf8d2658ed57b3ef9b701f665154d78f8915d6c2554ac2d1ea1e2aa81537e53e98e396c5eccb5b17f680ee364ea71890eea6ef1fb98bfa9a2c40f43a3a18cb1c2718c230e1abe460fce005a7ce14176b30a23a9bd8dc751311809d67b05fd39c4ee82f5c15708be59660d3e29a2f1b2c614c82e93d30294cc4706832f9733f7d73f67e57d7a340d2fa500aba90844129bacdfc7bd088e7bc26c0228e02e6850ff33561840e57396773b0faa209d143de711b8a1be50292d5be6fa6fefe87ab1170405879234dd7f65000093e7313ec83dba2dc78b2b512f9cfb41f32ac3cae779dd9e2273c9905c33e70dce3861f04b4533879ede7b2ec6396e7276c2306aaf81e282763cd67f7b5828002c124d51bd32abf81968034101eaac806acfa52cb4432e2a189cd04ec7c0f90585905ea3c03fda83c68542e7ea96975d2f54b9bb2eb3ad9e6df7804f9f151b08e4a621109f81ba7a175e018a8a8adaeeb4d062bff706122721576de83d0aa5056f6d736b28c30265cc19cbf7d19092ed628d051f2646783fc1d0a22943c1430f1dc32366397c947a85a0701aa3cbd639ab6dea3a98a2e13aa4ef64cf23349102340489c7630974cb9de101885b769bd6bb5b3abe1490aa8975dc99341fd69901cdd5ca575f68a04a1f9751025f8416cc073c9c5a74bdf8dd5a5f6876817dde0554b315ceacbdd3538733cd6dca3a3cea14cf6f93fbee21e6abaab3faaa8d5c0b05a335038e978e5e9ec9a3144ee0412b8e15fb8534c9de08b3cbdd10497d0604f57af3881146bfec7bdcb1528c297bcb86bc922f77800ce9fa62c5079400100ccf663f2b7e3d57c5a1b93f79cbfcc1b73856e817f90f76cf418a453e2ada540cd623be8956d1c7faeb49806c070bfb64dc9207274a1b884338d13d8677073609a86efbce7a04f20a450c2b79fc53a6c983a35adf3f173f81a6b8828713499e074a448ae59ccc184a309a0312b5f884045c14ac3398062a63b0412d5f1cc2a76d26a14fab5a9e69a6b51df9bf389007cdd120424618a58a1e6dee23d8fec77005e3dd1f6cbb96a4cbc11fa080bffbee9291a0ce6b0bb19498f803cf3a37fc510fe1d8fe31098171e4f681ae9adb51a41b7cf0ebc857ac93a295096192421296005f940f9cacae67c45ac77f3667c9531ecfdbf6a4544c817b574fd9397cd4540f74eb00999da0fcd0030f6403d865c5e79dc5cf62b8383a4ba24bd1c238ec04044870cd432242468573d715c9d26a80c2685a301d7bd7d65478da6e92a18c1d045da627399a70a295c6136dcd1821a2795dae2b88c367dac077070ee51078220958a8bfc4cc530d7cc8898d98ab80bbd80b0b43a1ad646602fcbbba1361a07f01222f03201ad49ed51b8edeac86d0fbf42ea42a56d71eb1f751a29a36f866a503787bf0bd585f07ec2d5dd572d3314620a71b3c8e63cd81dc6de28c2614fcfd0c8219d9cf3d4d37d0c098dfc0ac4a5a5ff393360da0080c0c3f202b43bdb0e609086e29446759ce13cbdf11bf5c26678d1218a56f984568eb97d00e32ec3404087e76dda5aa7fe34c1081613fe29bb2981c0b89ac827a153ff9772d0956ef7e06fa1ecc74cc0a5ab8e1079ed8e22b70f3c400ae96ee16015f971d6b8e91aef200903a25593164a5c7f48347914736f6babc17ae5c8bf4c337f1c5f492b748860bf4ae092b339ac5f4fcc06bf38ee638bccee9f718344810fface7c5341001d10e74cb8f8f232e21e08b349bf23b7650d1e24488bca438c6958339ee8200892f03e4b8a0c2f3a57fa75c18b638eb009a8aad306f30b03f8b8c1885bdd91baf660a6287432ca79dfbdb8b0de2bc95ad30154cca26376f61129c93f22f3625c17d02c2a61bb445a04bcad0f81596e04a60961c7ce0e9c8266c7fb50bbf4896c9ef00113bc7488a26369707f47111f9979d027120ed0b3ff41517df78046a7ec49c02b3f20443cfb332504e8e36da014fc4b08c4501c5d855e6dd882fbb1b41c037098f7a52a868cc011eb416571fd6aee5af5c5a3bc34657de2db7a0ca2c049d920271f021d3ca5089098c64a23a34b32c81c410a75db6ea1a9059bcb776bff85a09b5766d606b3e8f217dc58920e7e891cb1673d6e519312c833c5127a2415e8b060f24a9e892fde9627f26e4bfa74e2ca77a19ad513df0b2b45a35d850b091c3090363ca33f70f3ac38153f459e5413ab3b58d12019d0eabbb4581314f2bcb280a553bf90be0f10b2e5cd12a03c4772cc8d4e44d02cf1107b2781ced200a84860d62e318bc9b7147899e340f7b06d3110452e3732b234cf2f71ba544ac854cc70e58c80c24ef04fb6f84430c4741187459048906306058f3d6f59157a22ea3e80ace2544d1171b0ae9635f153e8a370c2055eb6e8a5684e0f5f1014144a07ad902f64c0cc8e363a3776439bdef6d3155b13165414123287fe9ea36067d854ebb06a7ecceeaebb48d883dea9598c0ade36465bc265db484568fa1eb2425d28a3b0286bd704e0d29f843b4fb30997491deb00e9e3bc45da79603f53a4ae089dccc0bc31392d9757ced9fce99899e052bfeeb52fd9756e2774dc763bdf2231478cb0793097fac3aeb6e13ef2da310c920debada7bb15eb4a0e89e3f50cc0690d4e30fc3463a739852a36f88685759c82e4525a566e366dccd3a99383a48447d2109048d31606ff4025731be4d688868ad185718d08886ba29483ceb576789f733f008abaa6b88d742a706509df8f09a0f4c8547769c3cb3dcb98673ee990f67a22c075dc8f0ad318f8fa9973423d1da067f8b636ec4359d2a27fb844178da8c83cf09e34b48d21df80dd149ae47dfadfe685d64686a5b5af67f3509d31b9d7cd4570c6c073e10c3a0542d1e09c4717b56be1bf4919ab377c6da4824b4cd5fd18953012375d4437172bd89c47ae6ab4bf6db17b3d5d05d2adedd6c747c578c2cefd8093131ae1515ea4d5ac28f997bb284e595627c7327cf40f6a4d3e672bbcb5ee8064ba708c553e26c61527700c22989b959173259248efc92b533980c4dfcf2ce0eeb51d160979d5aad37353135f64df8c6dcc2c0dad0d2d510f1426eaa6047590a4167355aa5a3f9d52dec7cbcac49137d20b48d2168b9d0cecf703874fe9d9d0b2e83db76d79b056bab970d671a8205f4279ebea1eb1b600d26c47bca3798ef08251deb48435e23cb315c265358b738da9965e028d3ab1d2ba368cbc14217fc0c33f2399345dabaa2781574eee9554c00e135cbeb6728f652983ddcce1a43340dc9a75d77876dde993a138b974de26c4c7f9a9ae588bda077f5f5a8f5b1c3860cb27616a15975997447af8d974fb5a1d51486d37fe7d079fd632e7a25939e2204bd45d1cc792603c29b84fc5676e2dc61cb0148fa3d9678971c5c68b0c0413b01cbcf02a104b83581a926cb96cfe45194b50f53b236f0f4aab418fb202fee6801c1eb50827be27553a0355bf954a6e8300d45b2f261836844062350976b90800bc18b663e0d3a08cb88b328fa169977e61b461a413d09a12ef0a24fc28536910f8f50d09760ba009b7df6f6ac67e1c9298271552d3616feee007441f39ff4090967da1de58e405a6f5e8a3328582e7592008d25517d4703f62dd00de7392cc20f19ed96a436f41a7318c734d2ce2cbc83c96a44642be7366cfe558798ed792d031599a3ef87245287752cf19816bc568b724a4fe57dd8b6cd7d3add4fbfc103098ec6af96a0ce0920cc5bfc486eb01f1b67565481edfb0a7da7d2b156ad0d9a0a55f7e4e20a70243942c53db8b2cf406a43445b6ba564594b3a022c33566465011d51fb094a773d882ff5468310e472617dbce6cdecac2440156aec8db9030e08fc14c219a2dc996a3c0dd1a9ab1058eb78e05671834b7064acd64cc55bdc070b1b9689e4282326b5bc0e0779d35b126449f92802d73b84ac3f166bbbaf4c47044f9766e3ef4eb9b8613cb3b065e81930568843e9d6d68468d0e05ee050b207028c97a26e836562e56a165c9a9122366a93fe87d341f069b448256b90e2bdcb05c90f7dae7645b399bc923c20f9bbb9440123909d9aff90b3019118e511cae50436ee4c98010ae88ff4b86f70a2274ac92bd6f7a0833af2f8d438ff4a97ef16064578c46b274e7581d2a3940f9e286478119dceadeaaaec49808841c3fefc210c42c44e62f879cda5bb678df6d5b4623cc632b93e3b921465ac9e344ca14d1909522f17efe908aff3020dd3f2dbcf3f8b2eec6c89c33286b5146243c98a2b6a02fdd19687ea5a3d0f2bf95d086568d66b5c856443ce084f87e99ecbbfce369b00b4d5f77f3987b1a34c93d59441993a3428a0e3f69c4c64ab8fcc339b492aad0b55555d1aa5287b142d4cf7a3ce05372e04d9cf721d672e31f75571bae701e00ceb2cccd45276e26024f37bb9406cb751ecca1ab9a51d11ae705dc0919e83fc0c40e47fc9d7a76308016cc4ec829929c28f419a74f34f63b8bd617982d5a16b01351c0106cc2bb55448627df62eae06ce60e4feb0bd65e3c13f04f14d968f320d078e0a68e810cf6c39bdd17f3e595f6c28b08fa28c925207af95a0d800b54c0aec25e04cdf3e3823c74ada56889c9f194da3d848d02d389dd48301073132630d49ba31929c64c799787970e842c2805f5b1fd269999d207a36049e244977650fbd5ff6ca59e76b796e266aa74d7aa67e061edbc0943b3375bb750acfbb87e8042b110744f86b06881af2b67f983a6f2feb0a2545436a3a15b8ab995addf4720a765bfa8c9acd61c386494316f1d2bf315b7d8267618fc4f7f883d20496c0050e14920edfe7a17e57d5d92086aaf5522972fe8463f9fb66fa3cacdb3a82e82b0e1d367f05bee3128d8a7512b8e7b27a15ce33992f54de04faa6dfc0fa7aa9bb0ac421faf5c47f75bca49122f2bb60c96f51fb57e0c4f82aa75c2b92b5e8e1cf07c58301e0f91daa642ba0fee98e313ca4c9bd618446162b86ae1ef03718046609c83c7ba576739fe1b50c2d8f32613526f0d81edbf123bc0eea1a1762ccdbf80ea15d93071eb961bfe24945963952cde887ca7d0d71cdc82aa98e58d9e5679d05a508b7914ee8bdb4155cceb6c9c567c58905d5782cfa09ade3a25f3007898c0fb5811e51421d341da17a6a08045da7380fa3f8e2406ccdea9c2ca59704636d01c22c775d2b386b390edaf1564863552e4f3f3493670d326745922276de806c0e56d0187cba60bac5c77967fa259b8aa1a91bd7096ca9bf406850e37866a88c07a7b5b4a070964c026d24fb191c96060cb6cb5a444112a6cc42183842950f75071adca2458c9c43954f49cb027adde26567e03d867b118c5fc440ed09131b250a6a7866a3db9f4f92c2729f1c285c02804f94933431bd6c090d54cf10c959d203f2a020b4b987d16f83883fb8a0d8833027813478bb217c97597d86c1cbf90d0c2855ce68979429f62e0f516eb93fe33c09845c60f5fd39dbccbc706cd0953c00a35c5185e845ad0ef5d7ddb7df6e0a73d8986bb0e691c6d03f8b4171e0c1ac0d86533e0718d3592e90d9609597d02c6af7263dd8ea0f6e39e170ce9b4462f60987649cf91bf9033eb3033fa46956900d5b2675712d16c2dcd7159dec2d3b92015a493fd188d411229952b065f761710ebf7075da632624f764aa0731456aea04365e760dc076811051a310380fbf7505b7a800288de2311a1eb8f82df479ba0aacda3788436d0e191886ec429150fd02213f4c8bc0feb0462092857867441b0ae6ee490006676be7d2896031dc80ffaeec09d8a61d61ecbd395397123915430600d91580191a7c2805a554284cc78656f2ba32d7448102f01aa9189e35a299076df088dea99a3688e26f42a229b6fe9e396f6189774cbaed566f26ab2f827200c34634e8d390685b741b4966c8598a45ced40262e14ccfb49c5ae6173394a605dc491c53cc5159cdd84c4097c991ac8fcd9a83b5c92a4ffa2b042b8b2d40da08dfc8e72e5f9dd737e56152ac2894ef3aec712524b86979c250e0e867d614090a9a0c7e4c732732f03394a748c4395638ffdb5d0d979820f902e4a3c9482e890f889a077b48934113073eb45a0bc8141a058d030d50ba55ec4a0e381af84bd307aa2b797db61d8250041ef2f7de1926c224d062d5c9b2b22e0695c93482c10007a60af405645be3a335f7f69481ca2337b82641a6982b99711876fbdafa44e50bee3228c2cd84b577c14dff6345f9f8dadbbd0b3c5c5acaee0524d2e59bec5706d151a9654e6cde2d7fec5f63d0dad08c80c74f1fc0fe743a24c4cc1774c8ad09b47e8591f5793ae79771e97bbdea5dc2d161463fcc4b872dc7c795055662a60efc8e6f017df41bc500154b42dcda1a2dec23321e4d80b02178a15597edf9c503f8ac24c0230dde952d84e5653fdcbcc336ee6dbae0150c09abd312205074900e27439a941b46cbd1ecd8fb84aa605fbe503bf4fc41f76beae83fa08ac1adef0a1bdcebfc4bea81500e9c5fe34ce2e16a30762f1f0f0d724ee83ae97167c2410bf3170fce40744a34cc531f0307af24907deb6bd9b0d33b8638c1d5aa6ffe320321dc5689eaff2b72dcb9f2d4f9902cc64beae860219befd9739a6b822ddde80e8f583a995d4a800dbfcf5fd58f081205ee0a8a1c0effbc7c05b19877d4a6ad0f63a0459f087e7fdcc702cf42caa670559108131f0f4bfd64bd1b0d5e34e9800e00ecc0fb481b177756e170bb25ed46f8d85a943e5b5aa968cb36a7180712c50cf245d82e88995374d811c7a6f88d4d6e063e589bec7725e0c9e25d3ee6b5470367e7b2dbf9467e745e138cc484b460b2d25565137ee8b2cb77e28229e105dd083261043ec2a7cf3ea723f96c46e323d5ec95659d6a591526fc28d022447320043436a37e303dd9364654186056929761c2cd6e5e92a16bfd446363ff5c153802341efd46b5a065f01662477009163878e7a4a3c333594d34a9c1978f7389630ab469cb717d09d56a144487fb3753b7985ade1a6129dc73f309fa8f211cf3010781cd920b4c921203795166b6e2c4345e762b9b92e07bad0785c3bdc0c23a460bc4d4bd8648aa7d423b907a8b840ef4967a87580c1da5bb1b8b512be8dffba0038d473539a0e6e38e38aceb673b3752a115e3aba4253cb62f8d20e636bd3d1f074ba48fa9e052ad96d529057d02247e945cdc2fa25b2a958d67c1dd0c09d94d0cb1ed6ea3e51eefecf4b5a0e4b6ee4eb8a020308e9b545b7794c5980906be94001814ac78391d01b77712b37cf48c56d1c814f793febd5e72fd1656549a75c500e29e35d1087cfd7c478293cdb9234cac86ebaef1af271a4217e28f025f79b5078cddb0166b6b213a3a6863177c9502883dcaa1aac6bd2e07082062d4581437021cc72590fbe5e11c5d3821d06a6dd44b18745c8b4a958938f5897c0a4460e80d58050c344f67808dada255a0ac73d7afaec1e300a1ff0db6a725690601c4310542f72ab8d1de614d4ff0ec01e9a739070ff1d658d33e0e51fdc0335c6c57420c7fc90c7160183238d04469c63f3bc52b59aaeaadd1837d8590d013bd7dfbb9045084633c2b05a977c91e0424bed6c6f3a8e4ae3a8cf3d78c62396bf8af1f390a7b51f5b2fc7d1d35ad87f9ff9a43e1662eb37415796ccade768c0199a65a26082a5f58d7d87a98c3f8b86135764545937038f809f72d9f84b71a00a375d72f02bf9b154364514d17684b891664575524492d1db690cfd3c8557de46211546e02afc3efa7f7e5a3ddd4088ba7d692538837fe49dda1171289a36d600ff9a3ff0e46b31d43bc065255e408a409b8e4c626f2af389bef6334fae7393a98579ae20dd432a7356672a504e521bd213d00450b9a49d0cf8deddcdea7ecfdbcbfbf980f697d065fe6c3339745795dc5524ab25fea3ab2c6c9fdeee62a8bdfdffdd91305a1142507c0f41495a38365224313e714844929850720f12d0bc181fc8eae360d19f87402f4826c7aaecdcfcf26608b75b24d4b94a7ecaa8d558d9cf75c996b0fec2e2973d911f4d4d85dfdb1b2248f3ba3d34f753980da9f76318f551660bf0e1bb5301fc1276fe3ebfff7f6cec75162af91c01f049a3550b31ad6cde172eb044c1cd956883434ac15571469689420faac3f6c7a0974b4162624e9405fca4e0e141a1b5bea6c5aec29191de7afc6769622d62924c88d0c878844e2bf6dab820f50545ff17c2d5a1f3df35974d439bd3cd3c168800deca41e3a0e30d2f588fe096a7eb76caf2880a86b77fcc7ae316f31b63db607b90eba915f08b7113d51c206678392410e61efcb3d64758a3ab9e493ca826ebf2b02cc828b0a1846fc66fc0b564e1c26daae619fa7e7315404c4cb3ad20543e67e61ae6d6e06bc06002f120d559aac7f7f0476ca1c3224f799abad35aa5c9a99e3ddda048337eb3266952002808ca86b0a54b2acadfc612a0a73500d0e2bdde7369186cefab67b4b201bc102885f6bb6f2fdd900dd0dd2196508f036fcca3468cb577539f808a476aab6160fdaaa4bb9cb43c46e947329342c08316ccedfe74d54e398a4e3fa6c5bddb4530fff7e8ef193ab0038401d9196e54318b4678a0414d67169d524fb354de02d2b0ca102504b7737088ae3187ddd4bc6eca46a7ab17bae526dc647f302e02e72370964a20bb55f16107d127a68ef8d7fa598aa083143ee1abc7ce67fb09698e2600270cce86aef8fb801a44a80df82d5a3c33161834ff4df68f17f6341a73d8d1706 false -check_ring_signature 1d494c9f62eee69f9b132533d838857abd0de9f85b7a81ba4153eaebd59559c5 3293117f7fcab5791ded1e914d2fc65cb935dcbb483f9e27525b923404029623 35 6d8c742273aa35ab7af47723045e7e8ff65b37d86671b46d01c8c39caa09087a ec3f16693c190a805173dfcc7719940bec26d21ead9bad60fb937321ebcb1bda b4ffff3688c76f46b2d5bb1ce04f13b7481c10ddbbac9fb80e30337200649089 0b839a33c1105ab84277755732e923ba04ba75162346cad7989969a1eb5cdb86 7a43919efbdea58748b8fd150873703d1791733afd3d94bd6407aeb22ef421db a1eaeb06b20a546eabb60aada962b61cddfc346186004a8dd0822d96f1241e21 22b64ead1785efeb90bfddb30eb0c3ab6e24245281e5c0407191f5803998fcc1 59c1c6d2dc2c37beaefd646232b87ab76a7ba6fc9a76398e372ab283bdb92e17 9f8edea78cc633e9a177a3037e8da358405248a602461e649973aac6332f1cfa 1e12e18f11a898275021011e4648fc950d2d4e433045df5167404d919fff04fb e0af4ae07ea1ed5ab2c419bb2685a7a03cc2e981ec3b05957c6d158279460bb3 68990f98726feec159afa438de22bddb1e401d6e876184f9695e2f60ee7737f5 49d32dfd7513f77915da1b25d8b7d671920c9aae1f152f7d3881cd94c2a4727b 26d369af2b98c50ca4a699a06c20dc888ad4f16e94a0ee444a6aa452dfae4944 a88e1251d0ce963af2e92cf1c2e38fbb111d16734aaad06e96f1a84de1093efb a7f62cb3d7975b0479d237b0e64d636abea2c084f38fbd61d2c51d30db897807 de1ea408029a51ad28543eb8a152892ba0b954053ed722cdca846ad008720b2d 72a207039bc0b9044126f9907fdb6d2d66eb15993935af46458e19f58db3bb7a 05a424aaf52f6acca3846f4e80fe6c36ac9d9c4c5738af258ee5efab57a23eee b025e1b1788d5d00ef7d1783c16ad4f8c0c71c501e69fa1270de8d48dacb7434 721d1891e8e8eb9f7f9abfb15d067b96303bcc85c1d7adbd4f804f4ed37873f9 f44ea6863980795aeb97458e9edf05e178263261599d663dbdcc353e79c162c0 ddf85949e8a9ba4673432f2043a67c7165a43a945a953b092bb54d5ee687a2c4 12d7291ceefddc3f380f2d2334c0db3fccd529227b3d3ea0a949b448992ddde3 2215307ea076f9ce3703824460c3c831f317ff93896f048e20e6fe70f890d287 1d575b20a1908f0c1b99f1514ae345833d1ae321ec264fbcd04f1959f5e67dfa 488e12d84cf5d015282f902ae46d0c19f0a32803f9f5c4fdc66ec01971270ddc 2e8e8c28cab5247236fe35607e2ec457b647a363833ab8f42c987f5350c8b29e ee439e5b6775a429adaac42d60392bbb9057621aebd0d9dc94d8196190a119c8 e2be4311e815afc98e41155eb6f7b52ee24eb1f8cec410d443cb94376eeab844 7922106121e72a4fa42d1770293db90213e122ab7a3f7b9795bae4d41a831a0f e856c063d6b227301ff44fb4e7fa585cf30a0fd3c9f3a68a5c0d5f7f65454caa efae169dec228d42d8a7eefe50b520683c049df710f36e9bd8e5dbc5345aeae0 22d489213a615ad0ad7a35309d340b9eeafe24016b9441171119481bec91b080 5f2a219e1b1bd6f3bd8227f77a8ea1fc54d70a7e6327728d9c83706e0ca28432 ef7430d743bc877ea4ada39958bbb050203a5bb275f148debc557a8783ebab07f64801727b7cf24ca5cfe7ad586afa5023e7bcfe440133ababf0ebcc657da90083967c4adca2e881bbbc84c6f17d3ddb5af48f8c58ff10f38fdf437fda197f02033c4aa684dd77af9b75b7d2561179ca3894148413b309b939e3c3c33aee6401ad6f9a43859ec8d24d0e7a056bd80213e39f3ea5dedc7614751a6af092ccf70ffa79e71867d3e2ce0c1380261e0ce9c02bcd6efb3a3fdab987d1f8864f95a10f6e7da988148cb7a9b46628fbefd5639e3d98506d8b316246bfea9379fc4bef0c7ade26240dd95f568e5d5f91a5e79fb88735120922c00fdb780b32300ea6210b9d6b800184281174dd238d89325e49baa5671a86cd4b77f89d58cc88f61b5a07fcb2952955cc30a3a849f0affbcdad81c11238fb4ebbb4af977519257086e20a45e4cd4ad7de1872004a35855073fcb56548e1f20394e36b050a2cd88f56f60e592c9129f07f34d7f0eaf85113b8c379c79e67a550772cf218c2d6d797e896051c9a9a6b76eaaa310f5ec3b38d8999031f2ff33d2ca7493e07e650552997150276a81c70a78184e8068ff0ca2d8805593d2cd535bfc0880ee9bf25a0fd9b35005d1f48b5a320c17f2784dd26e7ad6d649e6da728a58cab68599536fb110dbe006d33de0c7dceee038118172fa6bc4e833dd7663eefb4539676db59071db5f600fd8a12cb846895fc805f24108e3bd30a740007a16f83e73dee6db62069892c0a02d490e95f374141839f13286a0d09999bceb16f9852cbec10b8616dfce676099fd100bac260e31a4698efca7e755c6fa644642e77ca6ad291efcc1a0f96f6094e3835fd5d45a08189f3d6c7609ef4548ac98c468d4728c9bc67c2a5c87d3d09fe307fe1de173cf41a154c4da500807818367656801af007868af04ff012d10ec1a89478ccd2a96fc57b852d7ea58b02699aae2fc06cd97e920855ce99b01105f35c72f0f3ba3a663bb035fc474df22b6e94a3a833f48fed899dc4ad7cbeb369c5dc64272cda172be9a559d710950a8330ae0f8b51f18727a42603dab2d7fc03bf14e5bfd2b7652f28ea8efca284bfe94983d23d61aa444ffe90e1ba823df005f73277e7f6e32f3b1b213ee13e040ae49e4cda731cc493c09a92c581090b9105803847e3ea4c8c79fb3ba84c7135e1a8fbba7708f05cf54c4ddddc56fffeae0474a34678a9c8ccd573dbde3699a64bb228d16178f4e644060b654ff6c2d62d0b157067bc13f79283e716c6dde29b2a1269fb057cf55c6abc60bc4e600f384302a8be1ad0980629c99349b6731ce1475294b5e3d95cef2b9cae2c05f68f76ab041c4c139d696dbfc51a9fcee82ff01b8cd7bc9725fdd3030869d99ee5317e0f01384b5be723a554a0162fc83e6552e1531c710eec78586f7485a583b545a72c09cdb77a7ff920382c65ecea4c9b722f4b53294cf006d35b5871058bf05b04bc04764dbee32bd91b87b1289082d123e1e97be5621fefdfd60f23c8c8f9ef98f808787f70c16349cb5ceb8aec02e1afeaa49eb0d3dcbad62ae54f67a5aac495500d7caf9dfbc1499d972e6b4c1fb09e69918a38761a24f6557b4f6b51fced70a804cdaf0cb63aca514fdcbecaf90d4fa04ca2b6553bee7bebf25a46f85068b0db02cedafbce0f5f425cb81433377f0a7e5c96e2ea56d2799531eb41db39a26418081e718d23957b34389e67a46d449503e039557f6123ca31bf2dc1448746b7e201280c4221896968f0d41355375769345b53ee5feb1a3561b3cbe89221b31dc907c694a3c9901fd5f7a3147d7cebfb7280fd4bf796533b63af0195c90a2a227f0c7f15480c6d884dce3e34f6d90deb7ef2fa3ea9624aedf58cbf36295023e18a02dd0d35713c8d1a1ff8c58be5731397f955cf217981905cc1afce182d02b6030def4e237019d12917389e4e4eb4849f4a7cf10f7c6bad5cdcef3392f1b44f680846b322c798c08881974e94dff6a289f7e775ed94ff253ad4d91e95552b2d2d00ecb82a87dd4deee01c92c056582f4454faf52f3d23242a16ca39ac6e8a4bf000c6060b38ff1863f02876705123c916ee778bee142bb168921986bb050fee6d070932076787592cffd407f2a282c7526e84018b6e7077bba08e73664fe3c880012040d42d115c68d2f4cb3a3bbcd12272dce394914a72fa562ed6d413cff27406bad70c65dbc0f1e3cfe7580e3c4d98206fe92e0f06d7b73412dc9a870bae5f0942e1aa33277ea3e9ded4facb9183660f695072d086fadc33e47cbd23183f8001263a3178f7e97ba35c223fde6d1dfdb91a0ed1beed3934d15a3bb5f921218309c54de6d7885d54a519ec2949b4cbf5925622ce8d4de53e90d598e93ecf010005a6c2ac973ff6a1ca6cd6cf521061c4ca4c080db6acebff42b1867b290c7d2b0a5fb7453b6731a5543803083330ca9564ffac1268d6e7880d33602351f89cf101bff87755b1effeb72b3e7282d3c123ea2244d0f380e840d05cd8c11996110e0258e6d5756913513ded576307f59949eec5c2afbceb32967ae2774a7a2b9c4a09bd0512c24d579f3d65899b996bcf9459d58eb3cf41c6f488edabda4980c4d60cf594ea8cc6263605159ba8ec098f314847e260268993088cf60127bd996b80067f204f6176970bc54f344b0e5ff403bfa2983d5bb1788f378813d528cd0fb4075a99a593d18368e91ad1a40b865249529b6d2fd9ed64d96ad9092516b80a1205df05f0e8f68920e6f8c98268e86c4ca6489031220c8f8b0a32446266595cae091ee2ae9c50ad07d7e031d22d248776435fbbc1eb44cc99bf404da91173ba36093625393fc496893a6f260c03fcf8d18a3cee4d22db26b050eb069626bb9f4c0a040fd38e7405ab2912af0c68fd7ae97d3ab25250995032dfc6b9b208532dcf04fa9cf23d3d434c53d99caa7c1c64066cf310342f435b4c9a18aa4f7df868d4080f585615bad1d7b757bca6d3048d00f1d4a63291559dd091b95b97f7b5a6f40e14f353b3cb836055b33c9fc0c7595c919434515419af6ba21d52c24e8cffb90132b2f52c26e801cb83d7ae48cd537f781d112167ca641b237535a06070a1120df245be789c098cb26b4cd6623f50e0807c7de07c4e8dd72d95d04278678c410f false -check_ring_signature d1359ef7bc39bd95718d249043e04e9facd17c27c8398d0b5c5a5e05e4fb05fe 40346a808a10775f12dbb8ea2fb2cb51ae015641a84adf35fc020f3778627132 1 5eb3f3f79b9e5dbf8d50baa985fcc8b76a3e9a39bcdefd32262bfa347ba6d0d4 3debd752ac08e7bf0b3ac57fa3b7a5cd7b1a9a64ee8d4e0152ec40d041adb609f3e67fd5c813257d56675ba50fa276647931c96aece152dbeb265888539b860c false -check_ring_signature e69063e6cf60512ff6bf4ca77f6bda997a49f8ce788f3dc316003b9fccb9018c 4951f224bbabf07b7bdfece2bb5feed790a52ebbe8ffb9da1c7cb42afa6bcead 2 08439788470b3394b7c0972dac3ef1c061fc0ee6909c03719b2aaf42b26e801b f0a68c724817d37d66a144cd70d61400116b1aeee71e603e3d4d31eb8a14f2c4 e890672d0be0fedb685aa1a04c8c44a923d91b0a80f07e006484d234500db402289c8900f66c56fb003032878524b42a98859f1e492871bbf0d06b0e9ee5f30ddfb807780f43cb9e3978287768709b9d8d5ce4c4bac4131d13b6d937dc6e5a080b6ca672088d216cdf10af58dc53907f555f34312ca368ca3de2ec4df3f00f0a false -check_ring_signature 9f2022522588d20752a215d43decbd87dc38d86e3434d5ff9c4eb5b36ecfa6ea c1c6449b808f96386e734b8514caaa986d9bcb005cc74634e40cee72d331ba53 1 737716c7faf1ebd022d89e3238d039222d439a58e90aaa77c0ba01f5192b2551 fa1d00c2b0871603d65f0603128315f35c3cd47ac6805d30e01b4b7742789f003b9ec08cda600059996d83353f130fbdfa4e044fd88c03bb22119b3c00602805 true -check_ring_signature 43f044cad1da1650bf0adf0fb27eebad8b5efa1183812e2463f5a3d808a3d1c7 1a99eda108e780917cd4d7cab3301a5a595758abf4290bcc748ff13e4696ca66 2 dde0e4e85650ece7a93fc09861bf2b70fb2d92ac9354a869537250a339efef5e 92cfae54c77c09bfdbe2d05431e8efffaaec40e15c105978cf3e87b467527c4e 4c042b574a6b6f0c77dbc7661fa1c33055bdcf5feca2f533d5d8bf21f68160075ca8b68ff11b477f486e0cd85df2ccbdd5c2827ea5e946b1689289b09c0ce3037899d071c52a03ed934f36b45b325bd12b86d25d9332b89c9edf4cca03b8f20f7279e0d7d1d0113c638f3ec59ccfb1526d9c7f1197d1f66894acfb31fa075b85 false -check_ring_signature ea8e9558dcb64815781ab100cfd4cb27766d95b6fb1099ce8e6ca20457c9eae0 548f79f58e4803794e1846454032e6b1369549a7d891d86f49f2ef1d121df56f 84 0bd270d7a664883621800b4d2dbb14958cd02c40c87d18807b9b6d5542519cf1 33cabfea0bc327b96c3adb5d158d8d30a8f05747b66e2512cba956a9dc33ebd8 de25955f0b8f2c6fb27dbe8d21c354d1bbec66aa3978333f2ce73896be9c1a60 72f26cf326974e77af01f4beea23b1410b7b747c2dc0bb7ae871c22c3762381d 627c03c212ad9675009241a360e1a850050fa9ea519d44447915887c91e54325 c493207da25d1a640ff02be6a7c1652713ceb2ed62c5805a9dc9c4d4bbf393c9 5119cecc79ce79961b934d2712f5005c44696c5aa0da20577fc8f0d80f331900 37ce26a9fe3f1606f068fe11ef319c6a88d1dbd3cac7830b862635ca020286b8 caadc57314013fec256dacad09b207fba5fcc67a493cd3b3f5a46b09ffa79e63 722c7d25499c2003f41921df4954c937d91682a037fdfedb879e3a27ad663158 fb1d8cac2bffd8461ec6c2b8782398364b2fd2101858eb7bf58d0aaed735cb4c 5003c3512cd8d2e596d0dd07b18856892fd35d9953cdda7c49b0035fab99bac0 b4230e169b3c167fbd1ec43c27a1ed04eb358ef081373c88c5e4e68e7349a2da db4eb228b5fc0a012d519d2e3ccdd4244089e09ffd2f2f1084392a6300671f65 4040a4abce754bf0049bc27e492f2a3144cbb8a36f012ffc75d02efd67fac2e2 77d6813d92c7bec23fd0feb53a74d886eaa61c1b9781455dbb839f1b33a17a2c 2d0d1bef6db1badda49ed7f8c718e1c8e3474fc880afee13ab598ee751fd29fd 47a742fa0e5328b8e0a030157cf664e5280e6463b41010f8bc095f5fba6d9fe8 8662ff5e20c0cfd2e479dc429bad184e44f666194673e21b4b20df5295adfa7e 3aeda7e370dd3d6aecb411baf384b47f372856873ab2abbc383d63ec0a55a6b9 75e6b0f0e193a77f1e9cd9ef196f014f6a6c128a89758ebffcad51b20a276a70 10f6ee5b03a77ce8c5971a96077f8a51df9dbc7e1709e86c48423d9b4fe93699 53a05cd5993ef81a2d4e78929bb65eb022410f3e8a89fd6fea73699215eed243 6933ae4a424660fa29863e5a83c0184ff6f056a81d450c4280609fde7f0289b8 50f8cf2f537274835af92c5af2ec40dcaf66c7d1d61d15af80f137e4d8e1bdce 34292b469dc032838cce8ddd584eeb6eb640123f1fe41bab712902ffc1896bf3 768ab47435bc410e3dbd7fe847992394ff62a57a42194d05a753ba9720e04b38 86aefc0cd509d409f14871beae3ffd6d41a8bf30d3201068d85a3671b84c1157 b2cdf9f1e419ecbe573a1b2df8548ccbb8b21ee2c49090ee172fc62808f21d16 ccb1c7e37ef63f2536ff0b337bf1f9b05a39a1b419b3b7fc88d628ed6edb13fe bb9a21b84342c586402132c5565a657ff60f512b0323ce82f92d7b928d03ee22 978b797139886630ad4d677200a7d5c950e296cafd2a6de2abf49c87c9ca9bba fa6a9e78517dea355cbf84045e1b1b2a70861307426d764cee1b8d9b3775820b 61e8c20904872b72b3aee51e8adaf787c8ce202714d217fd5f14ae16d2181a22 91af28c1ae188dbb631a05af63019f43fe2731177ec1ffabc6493fb14065d98f b3c2e607049e866644769e61e93756f648c81e96c861f459a4ff7e89b2fadb3e 13d5cc13ad593a2c086d705f23cdffc7fd692d4d92471465a5dc0ee0e9c44491 d9440dc23b7977ad079622389656096fc3393f81cbe4f4a20676c4ac7f18040c 74bb828919f95dc9f6b1ac2d70f31af84f9bb632e22da014fdaad8d3b1f550d8 2997026042af76afde91b2a280eff32bacebbbcf9898148507f2dca58e9bc9e1 f34dad0e6c049d35dd247d21e9ff98e34931ea84730057a80f4306a6153d88b6 327df92bf2ef2374e60cf64418f1ac47aac66b50f521142eff76716646079dfa e461b32ddcd0c7f4d924476db752bc993e271f54cf890976bd8af04e85a6cbeb f55213e7a38dd21cfd89db86a62d6c52c3737b9bdcdb9cdf424919867f729fb3 439491c7d2d44dbec016f219d39e53b9d4221f662518663d8f9bb3c4423f5d27 789be546055390b03f95bd00d43320aa849f25a7e798221948270827c651775f 50e572a1bd87e5168fbe2d02887f9b292b19589fc4a89276b1f80d7611de207e 503d219307764f4417a846ec11b9e14b7949db9021d86c3d09ff9727bb790597 04c81f7a0996e6195547cb783ea06740b00147399ee9e128dc1713c1e5b2bac2 84bb080d21e260783f12f86d988a057ad444132b790703c4fee00d3c815c784d a3f5734f0c9e2d071f003a213d359a59e9eccd84a99ed983e03ac6945880768e 91e521dcad89d481f658399ab6dc0a03dfacbbda9bf8dbb382076e44fe1337fb d7adcab91e24b2a3844179824f0c4e4a28e25cbbcf5b9c94b6bdd3706cbfdc0f 5e77f521ffaf8d2c66924de380a4bb954c91b40daa6011debbe6afa79557c2cd 91f8001482052f4474375216197046f6e97a80e3fcb4e4b69922dabb28b84295 577661df59427fbf287d421ec2a0c799fc49b1cbd48a187439f697c7e2d3380d 9f43c214b154513bf538c4885d52289909788cec3d55897d6e3d92d305d07d2f ae98e0a221818ec0f3a2b6a7634cd0558877d5324d1d87676b08485512becc66 f1974288ac7639cc554193d623602be00e93ab4ef16323575a6a35eb1f716a8d ebe8fd7212ad09d962e0d69d0938e04c6b8afd66379c9465ae9c65c6956b8689 6389b42d473c24c8903b5e452aa22eb28e5e83109e76c09f2417ccf248883fb5 774695b8ba28c0ea8864b6305fb0ad20afdbd36259e8a995f3884b6d7ca34200 0d4ccf2b60ad80796da158bac3f192e18cc804e5bfc988c7c2f4c4a6d0bbc142 f1ba734fb62c6501e147316fd8f362cf23b96ce24190229d2fa7fad1cff08cb3 e2ea17a73637a7000b998926b322b18109dca63457c2f9891a5f6473bc8559f3 db74d8d639a5123afc6b24e525bfa55e41e21a716e5a47f727120b14df5ef279 85ae0288f68754ce363b3317ffcd8d8fc5ac4806547ecf1526b72d863662aeff f0ab874f30a8bfd016e2207425ae274ca10002ff34cdec1267e97db524771890 93ff4a75b658765e9be73fd61811badf2e012f0c2cb9d001f2d2047610ae3f68 d6214325b2ab7b38230f01bf5bb963ba166ee34737c4ac5d9f1fc46629b7562d 0d85c9ec409e74204fe65be3f2de6f508bd45ae5db833f1314e562b31995490a 2d2ede83779a01a145824db65e431162accfdcbc09abff06de9715bb74a6d3ed 6fbff187dde4851aa0b9cbe722c0f815fc09c675e0a5f9694f3825763eea02e8 47313503021b95b5a2a3e12bc87fe255774a6553396eb662bb78a7add231843a f28b35dca7f4229a129a895aa026f54dbbbf0b1c1c081276c2ec789bfbd6a523 318885bc179740c2a51fc4bf50b24288e025c72e3aef21b1603de6488e758992 d16d1d096ccd93a9f6a86697e5069e4921da9c3065382bb56a89ade4ccd40e91 197135853280c2177909fca87216de4af66087d47659c1173beca0acab5e9ae9 6ad54aff2dad7a3f29ad48293af11878fd94d84b4a1f11c5b274bccca48dc6c0 2f2f2ff89b00301e60b5c78a17afc515f476b045710a8a05bb533f8bb8b590aa 268fd836ca6cc9f1fcd2bf81eeccbb124d36905785b250eb99bf63324c3a696f c856c032a53d32ac41c7800620109afb89db058d9fdc1f69705566f5c8ec9ca9 0826b16077b97e944b14e50cdeaea528afeb2157c67368a4311b0ddd93008566 91a356891d29993efbb3df02c1f367f65a2bf078ac6213ee5e34e63ea6483567 1ccd43f91b3e4835d080e0ab718e8b9e47b72958deffcddb29fdefc94d0b3109ef11eacb11851541655f9f2dc92a64ee730f686d402c87b341fea0f4503cf50ddf5d5283595104b99e3e234dbcf236520f18c959cde19f02ab5552ed79b61e0558558c622c1d1b037b8e183a6cfc9f96daf6aac4a2b77a8898e02fc87339c90012e4d0bd68d15ec1c1ffe949572169ef3f4a8d74bcf5811ab5573deee16b320c0843d46a2480e253748c982ec45101b5d2280189c21a500c0affb98fa442f9025b9f0641e037c12441935daecc4ffed8f29945956c43426b2e2328fa0a2904031dc4ef56d071b12abba12a8fdcc0444224aebe8f289db8eb3a03bec642cb5a03ceb2b56e16058c9575818e22b36c89da2a94387bb6c08c0b23db8cb92935020442e7d26a0c6d94575d9429e6af0165fa153c6df2428c0459baf12ec6afc04f059fa15ea004039756c450409c9c8ea88186440ca1e85675e09ee0528484f40707b721c45e1c3f68f3408c0b757af7253012e457ac2627d58c9b0589cc3a2e1f0a7677fd384b083cc0e24b72edbaf71cc36cb5710ea4b0562f12427b3772ab0f009734985b8041f03c3cb5f6d1d668a9ce460b74dbe214fad8bbb589b298346c078dbfe23f62a876870adefbad424be8aa650e151c28c9ed6c0822fa56213b3a0756ffd918f2b127f6b142a7579c373bbbd6f93a719af26f3980b7ca64ce06d309a9527293b68dbb05e692e9021799e50d98ba3a9c38ed0b2f71bf9bd17a71890e3fad5da0dc3aef17cfc97c82473f8fc80a1524e40d024270e23b50b214b15803aa222e920f46522b4338a79a59f8800ca7ea81daff4f3af2e026292bc1c58c01fef0b2e62630b24f1e51bad3fe51ab60f840bd5480b131caf445ccdb1a7d7d0d888780659edc95a86ec3051f747257e240f3e5add5ca5aa859444013a3668306abc362a8c234b9f5d2d3b5f18eb77f9f3897208b754890d4b5cbeed6ea992f086514b1821190f5106aef354ed05191c7048f4f1ccbcb738fd89aabeaa70e780cb724925051703203e7a6ab474fbe60e5fac8784648196773b32410df2e38e8019479e643f82d84795ed75151f6b39b28bca0cb32326c0a99f167e260ad61f70265e05b1613b3ed75683a7d6eb99e3258b4af68c78d7bc26df9aa5b6bd1795d02ac05624ed9b29a2ae67d67c3f377013b48e5fcf2f2db7597f4d059cdb5cdc30556dcc51b3a58ef63290a0740b78d3b37171d1a3a72ca1227be985d665eef600eed9323f3e4ed8c9d5f13e60e86cbefbf11db92c529c0adac091cbb79b52b920010a9b191e1fcd730f9fed096a50c100a739e6f2b79fcd61bf37f5feb854f2a095d0fa75a41e32e511de272a53f95f6672bfdcd1ccf548bc375e782e2efc7ef0acd141be3412704d93491355732239e0ce751c42f2fee35e2aa24083fa8a238024a1593b8b143811bdfd2dadf33832ce4edc4cb43ad06f2c74990db511d03bf0accefb6b40514e6b35debc8975cfb3e229b4841804fe06838851399a1d0597a0caafa6723032e1464e90fbf8e303e0f7d63b4e4d321e64c8bc735f45cb8680a0808fd838c70e41fab4adf471f55f5e56c98ba55eec46fcb03a8c14e38bed54201ed26ad39b33d497e62daa9d953c1b1a1c1c366ee342bd514c859d19f8d2da30fd2773f4b0240f3e894802dd43a0110fccce663af4fd5c5b80e75d51d4784080f75ae1d9873e003750e1e04aa5e3f8e92d0c232919896983a49f75ed9f568de000fddff5f353cb75602d1261db20be23ee2544dc3027f03ad63ab12a1f3293f05ff9d100a5c8a5e59ec309205dcfa37db2facb5ce47cade2f2fe95cbb10b93909a77d9135711de24cb998fa44da0a6f7bbe01ca4db5aa4dda72c29cd72ff2c70a6653741d2c2e98b8f0fa187f55224e34203533c998a4e43a0afd146159e686032cac11427f421b3dcd25d51f85243e6ecc4c133011b3ce1294515ae16941c90ff57d4edad55a73b4f624f8ec7de56696d52268c7bf6e23f33d2e1d1196a9e80e0585ed9e1d3937fe78c9ff441b662cdebcfe38156a72b21db1e98b8ba5af7a0e8c3a24cf07cc1166030ceb35f98a54c22d33fb705bc2ae040cb2653674fb9806874ab7931f049d13d7edbca21c029955016fad7bcd70b82c14d99d2f6e55f10491bb63b7a1174adcf44c5930f363d73822ad889ca27aee46d4499c8f6162300abf3234b6e582930dc740eba037a38303311a6bee1de628f68856c62660810a012357f454f0aeeb0e360c138802ea9ea2cc70a820c5597f8fb0811ecb13cad20b980ad2fcdda4aeec2a9d5a57a19f04dfc70472b6a89129af95fe7ef75b23e000cec868d54bb51cfdc4b19b41ac09e2ef5944e60ad86c8b4a35949259bf45ef0c4749ce59108e82cf5404dfb458a6ae26e2e5449b4c2cd4e5912ba9023ff36e099021dc2ab1118fab4d3338354bb9675aa9926024fd892cda4a69886d8d93a9031928a8fb673fb520ec25405892be057bfcc8047a637ce3cc4f96aa865e9726016335d35c5347d2a59d777df9bac8d0c19aa7309c859766482f273c63c3643f00c609ae1ac97b85d7a97521d9d5f38c3d7b3a11e7d2c4af01af7591c61158cd078730ac9d128e9766ffa0a2e8545bca316eb1f25bc1c6d20eb5bf1b700fd4d50313f589f01cd06defac44d4bb9277ad286e9948a93dfbd7d3cc49178b083d4d03e4077f31eefb78e0bb84b11ea730299ed584cef3dcd5d78486bccbb57119e6040c0bb326c595da73ad8c367c8c5d7931128b197c7f1a1580ebab3548494c1c09648c63e94cba4cd752855def6224885e183ca9431d6f126bb03b129f8124ae07f3475c2e73a4b9827129afb39dba3b13df2a3717c2ef0b725b802940eb4f9f06915fe1dda6f4ca9392f9fc94bfdf5d4513d16ee5a7019575b0bed0734cf76f055a06d671a98a28585f35bd31c8d6dbd9effb99952f27bee7de27b6372bbe3904675cf151fa78d9283a6765a65f47d598343e5adad1aa5f616fc908a4f30f500380e182659810425951d70838b17d1fe2db5a5124eebdbdba655b5164805032080c2256e0cb02a3f8ada1b8dcebbca9b05fc74b63a07bf399b0f6b97879bf43017d3b5ae2b53089c3696637aff8e36a479eefdff15a4689a0327b43a0bd43a6095286429eba9f63508788e44c44f2a42fd83a3d665f651b54591ec7056edba907337be40d2b032c3da1fc62be6f5e9edb1239364ddcbfc21cb1fc27853e401b05a668b9d9f63bca339cefd7989b72743da2b997f10aaac070524d00cbacbd4e09051aaa44d5920ee837be3053787a89b697736066ae918c1a7620195194f4ac0b805d4549530d163d6adb7b95184222d231f41ed4e6de333e3b996bf61ef5b307de2d55e9232dc519886d19b5e67a54fcdf3400d533e45d7ba6eec42431568c06d84d8d4d8797810bc7ef4e55df6b888ce60d8a607e5d7d607876264b6d1ea00c64cd2f3aeb63123f67c4834ef6b1dbe30e4276860cbcdab17f23935f29df240bdd91691b3946dae3167b869e80b348468689f6f518ef5c14e6a774b2e296d008b8f313df4ae2a73b21bee792206147329be8ae94127606de582bc99508f8a7028375abba150bb833504feea7de686ff8ab02a3fb539c5f997109990143b37e0dfe368f9beb6328be19a7caa6881d09676c3325b490c5fb7b607e142c0239250ac77fb8f6cdd2885405893e71c6c1338c94a75474cd16348f9946d8d40e4a7103ea6c07d53a6b2398a5a7fde1161214e265cc650d6261d0d8d23be2c32d97fd062c6af1837342adbf91d6c5fd8e33acae509e948fff1dc1f569c216ba2ed8cb0ecb042724977a26a14b7ce88a084306d6717e513ef7e175a12c0bf835469fb707efacabb086ce425fabebd524566814f3fdb4e0c0c519b03fe82f9ec62f86b504925fa3c0353e52ebea32a3496ebcb23987ce799d11bd393f735cf638a96bec05c0e4d41ec8a961465ffbb702b99132d3fc69c7ac0751d5dc5b99f34e1497590cdc4f431a1c8299c57b7469c86917f8628e3bbe3728be222129846d8c666be7001bba197cefbc969de16a72189a8eb31b6555962d08e2ab2f6f13b5ded55bc00dc3faa0fc1af0cc8f67cc716f069d968d4888e11a5f22cc9ea9fc39bbdc4a56063414816590929ecbab8181fe2093bb181265b63cba8cd4083150d9fdbee0e80099d4a1a1a8f184f066f6c133cdb69f5f430bf384f67343f2a5abde432f34430da2df29cddc86d83f632aba30862d4b2a3742909a0be20b76b7b3e649336b7408a64501561b0530192426ad284d5e7ee48b811c8c43c2b493976fba988196df0524d96f25450113b6533aace01f005269798c53e440bd059c76a1f1c7bafc300e5da7cef0847a87fd989c0fc5158d561b7ccff21ed4e68815bc339cbdc015b10a4d713fbb0ece9b4393bdda2c0f7885a11804c687465b6761cc7e76cd5231be04e23dc121fa856f611ab057875367d4633f6d5b146b2ee145c982f2b5b85fdd072fe5e23f960a8df3fefde7964f691e2bb9cdb6efa902b519b659c54a5ac05d0b80d73a53eef71fa76ace47ca7f590b06c4e3532587c655bae2cf5552ff43b40e88992590b4a292deb0c2fcb35c003202c1af3f11cfc003bdf099091b0b8bab0df78445dc1f6b4cc9115111b1cd82feed6d794ef74c621f777da9afae2e5ee10a1ec27ebf60c67b7056f46e8f8e2910f62b46b5f827cd0f269d5c19b8fc0e2d04ce0a070e4cfe0dd32da77e8ffe4374440609b08b5d86729e7c20943100f94904ec68c807de660ae8aeaeb3c3ce8b614ed967780d82850760e25599a818a1f801613a289f2a3e2a3632d3e70baf97a58c7c10cb5f9202368716555133e094db0543243ade179568613740732fcb06e396b8ae37dc7d24c5d01305f8c50ca3840ff5bf1dcbbd17e763a402d3199aa8a454cae1ad293c0fd740063c0717441b3d0f72f06f55e6ff63d4c17d2be5fd5ade34c66f2825e0ce47c342eec199595fb80016a528172576d97a2600f2d36106e6a6b7a87839c5eca92344aa598b5527360bec01bee73ac6a827ca58dd23b5c2172386796bbda040b29628db29d06716060a6d79a8a09b0a28aca56f91046a6feca79c0a10cad6aacfb2a231353f2a7b7e0487491dbc4de82cdd492deadc559f359801cffa172b6bc0da26c47bee9c767d025e2b9a0b53c56bb8cb80d3189673aa95cf720a0db9e0bed54876362b51001d03976a842ff3310b2b8f538242d0e31dd8aec0a145e74ffe24bb5c93bcb4819709faf6fd9ef0de5930698af54d69f8b0291362d0b4bb94174ac442bdfe7fb1850dfba9c0bd14d08ed42ee0e57dd243d5d50c92eb02dcd41001c9131daffd3be50af4131c529a8abf411e30a14bc423f6569af1d641a8a08ae09a7494ff1234c30762d078a361dcd259ac22c8112bcb2a0bc18256673a5ccad33f6b23314925290698d1acd05d21e4d59c1ec6b75557f5020f0a57e12e7e5fb13c1c2a940785eb0d92996278528d3979418fc1a1f877ec0f9e8f7da18b560a8a401a3f37e4ceb00901db331df9c6c0c3ac420d1d0b2bb45e3d21aaec4f7feb527c7e1df34d5f1a0b6efb80123af74f64094cb0dd365b91b5e0281aaf2c687f1bef90911b5b1e8d0ee4e431941d5ca05a6ba9dc828a1c7ea6a60aee99e7893c0eb1927de8ed3ed60674e3530c3809aa088cf4a72a15c92e3c3cd75e5bb326ff79cdad64061e8176042c62470b9b9101824421ae6cf417c824d833234c41629c3d88939f0b88fdf7021fdd907a501453514ec526edce5d0b2ef75f81e477efb7e5a4edbf65432ba802af5005c2da83b7996f03c6e8fefaa87f6836110ae47ed30d1ff8f60a9b1dad01499131348942c83f0926428f16277c1ef74e983cbecdcd609d401b33c0a0ed05176fb671cfdc1a7299d13245e02de4b8baaf91412b140046572aa3bc7ba40f01ea09ffaaa47c3b192cdb392200ed9fd0d9de2421c26757501f2f2f7882c8d50579ab901d591cb93c17bf7423baf8ca9488454c8661c29ff03a0328c864df8c0dcf89d844f8aa012293395530e56ad66f8415c740bfa0b3a09c1b39f8cdfd1a0db8f38d709fd8778f84734686ee06e0c12dcd2b42319c577475bf3116595a84010ada1ff03b8139a6c0fc870529a39790c38eaa855577c3d57bc7ff3853b7cc0914f94ee561cd3dc18e131967b5aff796d220e1208d4e1860ddac04ce7f80f003a5ec49c5c043fee24bab61594fd43ad62a273bb68e947221420e4cae75c36e03d2f31b737f9e5800e877d1a133c8654ce3afb83b96662db4df370e8b844335005278b699dbae97067defe0211417f338462e0d49106c5a183be58aafc6ba160f3d619d50ad9f15cb0e576d92e5eba0d2935ffb3aa4007c1ca562e25b64922d0001aa3a593850133064fffa7a97f038aa5213ab91e9b2af96e393e8fcb02c9f0cfa1ac0ae1262808179624ff82ac20ea0868b26016f2b852120216e2563d0fc08788b9115e435f98e501f02dca11d59ee845d0bb717d9b2803171cc1cfeb1ba0b2fe9c94cbfb71e343e98f61f5d020e60baeec663a05878c9e1b265f544949b0acacd2ba6cebc4a48e690f68c17e65f0490ef6dd8926c1757ce313f003e008600882fec17de5d4a9d0d86af70a6cd316da65ad6b44f5339b33ad276fc6a8db0039e60bc43ba7b5183832f2ceddd6308cd5163bf0aacb3a8209911a0ac02feab05b77e66d7be0db55fd5d94a3cfe0e053b017c12a375b83256288f49c876b97002c51b0052569452f5c263d9d04402622b6a39fc290179cbac81f9fe438556fc015a111d8f740fd2c25550cfad1c1ab89b980f3df1ab6b9b9cb54e92e0fd0ed00521aeb530fb644ea5a47fe5efd812d9a61c11ae946b48981f0e0a42964c91b6061dba8f0fd714547862731f2e37b03c610d499aee04df404ba19ebb97ba089c001829708581bcec6df0fcc997e0986bcb8ab486df2d3d62aec6f0d022637e4e0080a09b0b8f341fc75d97f57ea9c5808bf041faee3635e57e8a77e1fa019d3c0f3ba0b0b363f8a1722681431669190feca39981beb497fe3050119c2d7952150832058748d088c0963e263ae1d2f9492d43ae903593fd94e97248cd2a6f0c2c01c0bd108f96f97b9744fd4a380149c4329bfb4480d12bedad560a9bda59f78509ea8a4a6cd5d1b84009345d95b0708aab6ca0c2803d6b0a803a03c6d285b2d4097eda7ae71af9a419034fc5eff6ede8001c974a51bc0ae9538a5b95edd3c8e6047711746c71e2a74390e3f8f6228ad7874990d7b2a8ad042611b8b02e1c4dbd0b888e727f1c1d75702d21147577bad046d8bd003d2658431c73bb3bc175818609ca9c1470c8932cb70ffa7f43eb37c8c80470d77c91e6c2ecad1d03052cf832027224bcbe2a9d6d7b323fdb6e66e45f3d7901fa93c2b78cd2ab661c8b8538f00533305ed4cf7aa350e083237c1047f2180db346e4eaba9b9afbaa1db445e5f306aaf51edeb2f60a71b3db4b0df8d3030f411d9135fce1f006f76f3f39a6c7db008fd103d415525ac02ee294a763bd77797cdeed9b4e80c29753520f046c3de10c false -check_ring_signature bbd3f06c617084a283b4f2fe818e006925d7be6b47cdae6ac6b62af96bc6b4cd ea705b1d87ca561db5946792324638f596d807e3c21482715501802f8a4394a6 147 7dd495d46b35cd6183ecc302cd70d381d66db3bd3df20567450511185c95f578 fa5ae3554f38915c3f23b991680d91e23c059473027567507b0f8a4fab4f8ed4 69a8a26c9d10f19f4356114c3f9dde4480835e685896ad9700c640dfce28c628 a243bed0074d4c8e5b659cd3e27c94a2662aaf044884ca7da2d24537bf3c3eba 7ed030d418d88c0b2677f7791ff3a0d5708088666b0d6be9e3271fa8775db5c5 cd5cfe39508801776be729a8b4dbd4f64e7b1a6c5ac3537689dac6bec5121f78 13f826f824a0e49c824dbc50603cff78a3488d55a027f75151cf948cbb990801 087e0f3cd78b6b4a344a01c3cbc1ee098e04cc229738239bdeb7ee52b807992c a95549f5933389d9fdc463863e5c3e5a28dcd678b253d0628738fa2c88fe33cb a974c566c1bf6f28afd381793132743b5a4d589fcc34c4cc26cf686c0fea6a2f 2690a2ad23203edd90ca23dfd9ea764737fd2e487dd4f7e2746ccd9d76b7c402 c3e7e67dbe912c5c98396fd07ed8d019a019ec394d6d7583debd8353501cc2e9 1d935004f40520587c1115e6bc623df28a4c0e437778642f7cdecdfcf901f45d 6eb533735e084fe85b50f8b2b7ecc51e4025f33fe873b3b83a05f9b2e896fa6a 15a4786c2268056f448c0cb5f948ee1996f7b8617af8eea01cf6e0938867c92c e1596b6ea92672998c80d5ffb16bd7c7ccc04b359515ee8dda39b7f77b8c14fd f4ce43686f3f73a31474fad8c402c09e26ec198a860fce5af00a8a526bba4be7 d2cf9c4cb43fa1555a91051e85e91faf5558d3864124967f51bc0247c7afb64d d5dad7c93f0fc2644a867861b0d3b07586ed757c61b4a538458620b54698ee78 5f075a6923a754f54d5dbef7efd07b45da181020360fae26fb6c27414a91fc47 c63292e6408fccbcb2473ef1e7cd17a9e6841e07b51134e8970ad4f5b5ea9938 898532fd843ddba47eda62667d3e1ed4195dfb3d10928d2afb08049bfe368881 216e27f21a7e9238d16e61112cb52ad6c556449a94f2d5c7633fedbc37e7685e da74ae40bc4923fe7bfa8622c49d4f5bd35069951c32b8ae6654789b5ecff6ab 577526b2b0087a6bde07c004db6f3a72070df799dd92d7df4614378bd2443e9a fdd357cc2349349d05dc771ff0571455b3f85f98fbe7cd4d99b03397ee50707a e5ae8713875d3dabbdd3a23a9ee724727766f2917cff0ee94de02d83f0a92d87 e601e60405f47a38604d671aa28f328510d37af902937ab69b5424650c2c19b3 806d1d802dd1ee2ad9939250c7f7ef99f0c5ff494beabf05bfed4afccf1dab66 bca64685fa9b1254f10cc50c7fef196868dab2b8036ba15674a5519302976408 7b73d143424f0e53a0ffce85a9a6ac22773deacc249f8d3057aee8ff68622d09 358643ccbe0150c4aba637162ad0b319fa8b029338eeb88677407b0fe03cf92d 653565b3798c5406f9ace323dde5f4441730ba1141a4cbdec2305665b7b601a0 5c2102fe308cfd61d3c5af8101fed03e608e7822309a0a77a7d751ca058eb2fe 1c8acb2a18c7d1a359f4ba665a18c0b588d7069a35ac0ec8984756c3764ad4b8 167036a356c17893170e9b102ba6a668230e5582d3c2e40de8ff2617bbbad012 a71fd408afced7a591e549d84e5d0f567218d88019923f720e66580105f05414 354254b4e7064847fd24e61619c47b87e4e4a98c93c4cee7f5bf2a93670d9eaa b9ad48e34913cb70d0c7170cbc0a1f09dbe4f400840602f78d37fb5ba55cd486 f43d9fae9f6ec3cc7b8542002f5efee9dd03e0c515470b9dabe3311364f76bab 2903f24a8132541ccf6da4b1351a7e5d68d7edb3664e10033addfd7d51e267fa b46a9b152fc73dd38018359de56969a099c2d1850b84b1b681183fc32dd03d78 1eea10e9bf865179ba86ac811c590e63557e052326c7e73abd75fa9d23def5dc 035b63a5d38ecaa51515cad0db0ef09c24b482d073c5d3a8bf6356b211797cd4 fd2d5b5aa226e377debe0db917c4d84476a01cc470d377135767edab9c3ea3dc f8e751f24a151685fd56cea32e46e0922c693c3f4b40518cb7874bc019a712a6 ff8d8f61a5726ebe6081633b10178835a654e8de851a7be95ca785ff53497ffc 3646fcbf1fb4fbdc5743c2655df83d0ecb2f0f92322538b7855b6625f3110152 5687eb2d149fe524377530ec07e6b3c0cb4faaef316f6fae7a99dc049fd8d72d 309868e8f88c46ac67edf831ca29c125ca54034440cd458936f33ed2fcd7a2d5 a851918058b3208ad81a161431ad52411e9d2648c71e680f043c929efafb2d26 cce410a28f342cda88856e0231b82de6320c775237170fee1edcf4a402dbc306 373134950331771f3fccd3a1f2842a944311c86626b910c9d8a5bf32699a74ed 694e2871867e2c6b6da47fb9e1970816e3fc1aed764b66179cb0e6028250ca65 317c9ab026461173387120f3ae4cfab12b6de41fe9d5f6f506e32dcd555ef4e0 e205a114404bfb07aa8460406b1d561fcc327f57914c4e38c333cf16a1e57937 be53d0d3d56fc3973fe3ea71530fc8155f9894289c9e42d519b605e632aaf562 feb543d2c65d4fafe804ce9af41e10cc7f161a15ed83d30cae2224071bed3c67 46095bf4e829163969836419b57cb23700faaefb5d2d73c646ba2d78c9d37407 cbe7509b424e55a735933cbb665cdc99532b158b22199614d380997b8d1c121e 9212e685fdd59a48c6e94d7475f8376277c0d587716175c8870e81f9fb2e437c 793800db17beeda6dcbaa6fa3c9ff798c654e8f188adc9b26ccef21f05c86dc9 17fcfc205faeca2f5c091fb215f2899b6fbb752b7839c6994eb4fcbf02d401c4 390d5231c2e04b41dd59b6ef8b92b24aced02bb2bbd4ec34c92f55ffaf593ef4 09c2b097f6274b8e231e33e8cededb2ee7f638d5478d1d1f934ce125fb622d69 b1129f2ce21d471ba5b4cb86e012e3984ad83c023b26052cc9eeea8329f221b6 03bbb0b417d3671cad3a04dc41af2decfcae747973d5250ff34322ff71e5b692 23f2b95240643512fc1a758ba15f4bba6a214bce563a8e792f397770185872d0 870a715c9e7235e9410f987bd385c9823a25887dabb42d05f3ecb8ed2c743347 a7055a6bda61dd3c57e7c8b945c972d44aa79eb6bc1d7b46972fc7fc516a238c f3733c573fb024fe039500f048a0b42a9d48062547c7c02dd8303c7a500cc623 74f82826ed78112bd6b7fd2bd63cc9d42fbb890b1146029ce758da793f1bfced 05cc3ac8da1f4de2854f18e5cb59cc30e67c88b0d0c01e09f56751fdf9bbe49d b59cf75e425393faa299cb46ce59c1b706a5c3460a60378d652db2c7645baa0d 0f7f02c037e61b53a073902a72d2c55482611b04c614497733a8ae25aab2e54b e0a28f0e82bd944255ad1d56845e5934f515af2dd32a2ba43c8de17786bd5834 62a7f8967fcc8f659289f1c555e7089f6a74fa744233b163ed4f58a4e8494cda c058d278e277c9c384310eddd365edf255f8b7370c8edb33304cfbe9ee502fb3 382dc9759a4815568c13f7f064e31ec6295d074e675da7f878a726d8b4bd0ecf 8beec119b94705c38a18f929955e607234ebc32050bfa27db945b996c587f7ed 611e4aa0e0402ab34f6fa49f6aa0d0688f0489ec864ebfae547614cdd10bdcd1 0caf1793b85152b6c0fa11f600ab53ff7715b9ac12883a738b7289fa46dc3fe6 b3270f4ea22907c6377bd6566e039bd968d8aacbc804bcc7f07da794fa0949c7 8f3e2bd24b1749f46fe2f70519855e24a4196e815bc5de2708dcd8d1b3df49a9 0bfdf8b73d1725137701d0187a6e62e25530d65474c507e67504184e396505a0 b3b82292d500a98ff9a3a432c2f8e785e6346781b3520f25e2479f1327433d13 d825cbba7fc1d96f942ebeb0d8fb8865511201dce6288e78f953cb6b2df2e881 609c42490b41bb1f1634915c40a6194bdc630d0ff80064720954901fa4284654 3e360491446fe835acbefb68e8c8bc5005a371c3db20673806a2fb7608798498 075fcbf84e3cd5db60547127f8a0011761da9e48a7182b1f2989f40764820b5b 6063d6615d1c221c45b6ea2c76225e5573d5560561af9f85aad526cacd6869bd c5b4a1de370b0c9a61b2d37cd7dbb20dc6844a7a65cc89705f08fba82eaaf4fb 594b4546f0f76f9fb943a01fe67479a12f54a15f307335cd415c5634bfd865ed d29d260cfaa9af9c9ed34c251191fca75d4c782e5af2a1e160aa41e7d166c7c5 35a281f33e17be4ebf27ef450943749bfcd3adf153c295378c96546023bf1fe0 a685fe8f44ae1506fa88519cc1b38f1b71896bcad67f6bf18ed041c97e1112a7 ecfd5c67cd74b7eb366d850388122b45f8c4b0553836cc03d0352750e3afdf3c 9d0b7e3b7935ff82b056974a829aad1aa11cece575ab79bf61cddec96ee26b69 fb78d011a44f7ec54ea20c529f8cdd16caf8ca1c2dee1e48dce07af38e2d2797 85dbcf44fc95b4d62fb43b0a238c61a1ed0392283e8dd27f63e47a086ade3791 d721e2367b5bf4b235b5e08e56965438ba5ee3c960f46129dc40bee7dd14e2e8 3acac53ed33de4f600731cda102032a53df9e7ee4e9bb92b2947d047c285c4a6 4a85dfe6651d71b87012cb19e7aa64bffc84a0358a092c03d017bd8ada6db4a8 7059847850803b1b3df3992057ccdf9d745306f19651e0279d365bb7603eaf21 73695072156cf7ec310b53cd5b826f248c1874cb3c6273e6c26e27413d615e7f 1fd3a70e8f45d486fc23ca06b35bbc5ce5c153978636d952b6fc7ad8623f40e6 c1dbf9970e63fb5864543d5f7ddf559bab5f35c61b94617edb85015e7e39754d 29dcb261ddcaa2f103352d8962e2973cd93eb2acab536003a87e55369884467d c2b7885aa8e7c9e1efa3b1911d588abb6f9608c7d6d30acbbf00001ea4032509 ddabfb30a01248084ee71711686b11f3e72b1699ec666e3544af623756619a2e 65dec59f2f5d1f42fb57a4b171dbe6e00d4f9085dc7a4db97224d5472321d43b 18dcd2d81610026083c4af0db2b23aec462f99552ef151c93aa79a2d6920ae22 4c38524baa7b495a582dafe5091b08a312b9c812ccc29549be83f041fdcdc524 8195ea43faff2b72556964d86af9c28204d990a3f9c989fa986104ed57ec9d13 d5589330fd4d06c4d2c8bac5eb28a9997ac2ff9a907085d1710965831ba80047 faeab703071c02dff17cc320811e69d8099547442a896dcd87abfdd2241a9384 a303dd2b8f8a59d0fd165e5abb3c7e9b32d59cc2c00cc02dcc97d2295d6d48aa 182eed9056cc6ee492949d5fa740655aede837470c15be9463c5538c762e75a6 3200bd9045087940b91830a647e640473b9ff258b0f076026721ac76998f0b86 b35ea8b7c4086c4bf121746d63192449a1f44396ec7d4ba19785987d2dadf834 f70d76a9431528de284b430c26c0c097ab7d9f5fb39f8a6bad389f9248029beb 7bc6bf6c6b778a37966c6de1f5bfe5f912ea5d0aca1032a9c873becc0921e9f9 dfd0c41236e4f6c10456f7f7e6f14d06d04489c28346948036d2f5352c8e202b d0b40b0f5ba91f53f773c48336d1a545f5dc4d838c570b9dd97e63f535da98f9 a09906519c308416d01c3eab78e8801462523cad6b765afde05aaa6df14e70b3 d1a7793d3c32e4d73c54310da3d8180335d69bcf47db9f97f4ac47bae3e6ac37 eaf769edad139064f4691f9df7d340e2065ee8daf35cd275c3a3242bba133b4f 2d2fca8f7d611f49a077920632291a035c1dc4a7dc63eff6afc00aeb0ae9c0cc 71088e755493933f1c245ffefb0ac457e7bf8747e317eeabe8b0c421e1517fc0 0eb68461a58104ddcf52bbd4a0ca2f1e61295e239e2c2bf0d18aab32ce0c43b7 bf255054d5aa711903169245caa949dc2c2bec8a3313278c88e2446565075a51 2ff9d6af1adeb93181dc8f7357c25680bee52ff7c6f50654f7a4b53cb88861d2 adb707a645ad628b29cca8d67dcbff397cf004d7e11b05eef4b9ca0102f390bb 39a53fe77c36831322d55f3c2a424bf94891bf3817abce8d149b7988b7d2508d d73edaf28e9bf69ffd3f92d8d389e9e5dcebd246f5f824958ec86543e3765314 44c63da3df860bcc03836c05603ca3513fcfc50597cbe3c6e48abe205b940dbe 9a59228e31446d1c9cdca0842092fdaa837e9847338c36cb9649eecc0286b9b9 69987d672928caa5ed2d9fa7fdfeb531a3d3fdd991a8e08e839f65ade84412f2 84f1d01bd50ca593da2f823ad136df022dfe0af507658c4cc16c34742001618d ad5e1390a1a0c5e513bd919177c757c18d01522fb890c75c81be9f2312d08df1 2d6ed1d63639787c77718df2755579aed937a848bb3e2bab58b086f442c5bc53 3a3056251b2522fe62b6d62b316cb9b6e885f2bbf680bb97906ebfee8d293a68 e7b730bc51df15c95787d597acc6b93b22515cd479bb068da3c9cd5adb748ccf 313eed698d50f4b5c9dfae924e2c198552b7076405f8aecdfd3137a7d0b1c71f 007314119d1e7d8fc2be369a4c70aee6e5a68e99a297f139b6f7130671694ac9 835d91932e7d16439629e284958315db8f1758537bf4943236095c110882d493 191c81ce4a864a4c0d3b83d4ed84638fbd9737b234951ae7a00bf2e855bd4927 f7e3cf1f6514ea9412c113dd3f7307de8aef5cc0ecf16cc18b22dd685bd9cd08e1a946f3bf6b85c442db8693bbcb31ef61ce9aec1db641c44b5094c28c081a0a7686d2b96c1100de4b0eedf0600aa866ca31edb3fb0fc099516a12e45869210faeabe32fb3cbb42e1abe0b98cf3fd7a625571606734c48862918923dc0876b03fe64b66352aa43fb57b8e0829e7b7cf29b66787dcbed0a4183d55883fbceeb0b8081f8b5f414c214909e1e3d9e73f1bf7b75699de75ddd5493e3c5480547db08ea8216c45c0cd11b3ee2a86b82257625e2e52c17084f7a7e0a63330b521fb205a891b5a63fb6134466321991604090fcbb24c4ee1bc12fd78a9489a8798f030b4994f569cc6efa34ce4f7f0045872eb1ea352da8a37c860f2c649a059510f90e4aaad39cd1c298eb3bec650f51bad9c79f8221c53b7223e53a22b6e898e2870243283613e0df1d1152c38580669fa62858e99bb4dd43b318a8609a8752ffd005af33fcd38c22403d7d3558baaf1dd293ec851942302d15b666112f75c7cd0c03e8566aa37a4752bd074d7c8955aebc51ac70ef0656a9e3f9cc0d2d61b2da38089d5a0da06e8e952bc3f56ecb203dce5cc04f8abf187d60d53d99f5f278daac0ed07b073d34fe795c307579372013a96845aaac5c66ca45411411e9a89ac37b0f50f99caccf4bfa0d4a36e422ce602696c4ec8abd4e9ba7c0da01c123d7dbfd092b4422a26365ab62f89c2e74e2d6d896f2f9ba01fd75cb3d78b51b0149e0b9069779500224454bdfe2c459ab445dd77bf90d66a87ea3d252dbb82a30a885990ff36942bc39e99fe41ecb04d65ae6fcfa035e243d74ddc41fbe5c6567b9026403a8d946bdf929f33c16aa4c96c0a99569be453a2db5e13f0a5de4af1f453f9c07afde586a87ec86c916f100577ca562068c3efbd0e7befde6d0cb02cd4091d909c6b6784a8a0329dc534a30af45af1e8fa5e07744af334e4ef3fc9435bf00c60bb34a386e600aca1cfa6cdba6e7d3d3479a3bb81deeee8abce04e528933557c00bd7eb1305ad1fc211526b8d0a5a39d64dfcdc47bb268b133e00f376b0736590cdcc3bc8b50e9065177837913fce374f0bb179eb119d8f70db884df15f319b804f94c869b61e80386d3a0c231e1c2105db6de1e8964e3f25442c243ab6a3e0209981b10b22ad209d35b7e4e89b9a6da176ed17f2091052fa980b284de85396b0a953dff61a37686161b94f20fd7764e9dc817925aa0e5090a7854ca710069de016d94993d7ae0d7d5e6b917f855389860650ae8eb354726f70a09813250cfd603e10daf0c8f4943c94b65fe01271a9a314eabdb152d1cc48acf04f56a3251c9098f720240287393b951d748aed1b84ba1b2129abff694dbb57c9f04e1f92b8305dfba346875af2cfc09e19cd7e473d8e96f976d1a31bc8ab1e8427ccb53501e05530177566efd86536876f88abe362f66d5bf493e20b1af1514771d1bfa87c10dc4cadafb1dd34cf867b151579de1d8108ea8432e1e5379ecef1b58d27746010155e024777caa0f634b62622a329d81bdb813c441d315a2e98f1a951405307403bf16cdecf55e03a4fe61e077c403a6d47ce1f695245191fdedb65b3d13d3d7020c5d14f81f6c3b6356203fcb81676abaa91e43f532479d36ab6fb25f3b25260dcf8f66c23ee09776a5995615b6e6a74759df7948df6af51566d83a44e6f20f03ae9a736abd4b763fac8c4538207fc19ceca08af1506f4f9efa0239eb75837000651c39062b3d6e8f0142834abc88723fce1effdd1f2065c32c9e62f373f00404dc424cbf7b26f373aabbf4089a784398e64f20c80e112bc3145ea4e667e6e804f905f563d7cdac067ab4a038ed04674c50d8be2d5efa7453ccdd9f8793edbd0fa132b51ee955bfa4e88d92f9b40f6698fa3bb0def1c0c91d986a3ffbf921c50a9319785abf7dd6efe571246783de2fb7b02c55a18d5905525ec9b145cca7760c222401324a6c71fec4c46ff9d1c263437b324f4d5d311d6e8dcab56d72f9af00b485cc1ef96d797aebe4400cf57cc942d4caec06292e3c8aa5d92718d5657603c2c353eb654daffa4ce6d6f51883aef567a38cd08377805353b15ed07833f3049f7ecb76662825f5e7e6f94d655a498f479da11ec25011e5eaae40912f63cd078ba6402a191090877b4f01baacf5cfb2971adb3503259a9c5f2f0de6b15f4a0c30282e97f35951c9672ed00e526b7069c0ac42b630525db86ca15e8a7c0f6c0eb19307a3d5a1234100e5573372005939e574d5fd236abdba23d4199102f0f1022bc0dc61d3fb9676c2fd1b8fa0a4d34964da88941e2a3019a66030a99c5ee9090dc49b56b949405918aa537be6bb53cc3673b50cae43714ae1e9daa864ce0406c490b2b6c763417923c52021552574a27c93e7bf935427ab55d8807f683e580355cc70855f018510e992af237a3919291d63cf0c736931e04023177344f9190189c6561f4b391ebb5ddd91dcef92a9b73f8f06ccf76865afdaf878239e3d3f0b5dece693b7b263f93028da0a95f57de490743b943fb7690b639c3eb29d858401a6d076c9a63887966511867f18fbba9ceda45a449cc1d6663cb91e0227fe6f078d347c9f1f5c14e969ca5a65721d0cb0d59578247e5ad621a16e24de8492d108d127976ead4d8cfbc8bc6472e09cadd3ee094dcbccf0ef316e395696b8a6f40f23114e472dc656a8005a43cb58a432852c30bfc4c1b8de3b49c2ee9a1a2a8d0a4dda239dea5ffce753d5cf6972e4d0253a415a2b49b6f09d5b3ffa8cd0260c02fee0cf5bc5ede02015ecf6d1fd37b5f6502137dc45165f5c05c5ed6e0b026606c8f3c502d0aeb97b5ba0e81fe7681891a7276666f2aea5f1da2213afcb24750b502a28ea58217d27ce0eecda42340efac00b2960a8ba50e792e73063f49f860740d2758148e2072acd22799a006e9b69491d9a541af12cdb1264f63b60c6000c41c06664960eb77ab2a35c358484d5549f4646e06767d27e67389b00b77d7000843b6509c22e6c4500bda73d52853f19c3db88af2fc8593df80a7b401dce540f7980eb9a1a3ddc9666374ec18325601bd3c5900091d329d65a264b162cda500fb58a0a6b2f3655f00b5ee0c0a96d85e918797c511e7e5a9daa56d59a7523170182e85898bfd84024e3fc9924133d8df540cf1f76bee62477dd9ea7894cc2030d582f8cb1684723d5f4c48bb91d5ad8ea017aadc717029d780b2a224e42c4950938927d13838dab88c6c630d483775f7c2d22b174dbdf1aa63e91d9f7e330ad0e502eb43343917d78b885eb82b3422a34f95769e79910c49aa0238dab2afb5704a4ce6a52e9c6bf4805369af7d8fd911bfb7a08848b27f2e28ab213e241fe6f030e85ebcdc0579588b67979e045c52f6e434ab8e334419869afbcb3f55eec6300847f948934d6112815b73f95103a871f82a2e08a099a897239457045632f5c08f5dd63ab3a71bdeb0c3c8e218b7677ed6ac6c2c46c29fd83a8eb595979333c0a4236e24b8736a31d5f0a3213b98a4f5b2361b6733d333ef273884cb6d3d88d00d444fac85bcf35c53881e04770fa4b0077de39e51d373b3007282b3ee18645039217925694c179c4583ab42bbf31ca8660dca6aa9864a1ee338f11995cde920b749bda3cd1d12ba77beb55008276d023b981e42b412729f4c2802e69b4fc250578c0cedeb8a2cc3482ef76ff23ac97a5c97d33bab98ae7bd2f23f11924f23106480e6423cbb2145e14b17c91d47527eaeefd3c0599cdb54060ae73dcdb0f780da407008d6206318d0c06bc59032e149029e1be2fa794ea795e374980aa4aef009fe42350b527be41c53ffe713bbca0e507fb6583430bee7116e4ecc5a3008d0db6118c79e0e2564e33ac9e4310a921783051a0586a6e997115dec71c96cd94061172ac40faee995c938d2dc5060535c938b4091a813578184c26d7201889690dbb60e80ff17800830dbae469fbbf5f915ca8c40b06a722cb0e22a971a7138a051d5ab0e6c532000503fd346b791e289819eea19f9e3565a9b8fa2ec4bf895708aae4e710b37aec5cdb556a31b3e7784f545eb0f96cb27fb8a4ed5540a2dea200222590e2e2be054d2b310343d1ce0e5cbd1a275ffa76838258188e33f4212803d3d8164d249075811f79f7e035a85b071ca414e366f5f07fa5c4b902991f8d0c81e3b64ed92bdd566f59bbeae65fb98229fa6059c7769279252805b92ac04307d80bee7ac0964ca57101b6bf197e2ea59e92b6e4075ea551a485b92bd030df0f41e7669692a5b9a4fbea98739e18a71775acde17596f53433c14577863cdee0ab43b595a22bfff452a5ee46b9d3b094ecce3c5dfec4f94318ae22b7f6188fc0e95807beb8bda40496db225bd718ae59615955a87557dc01e3490471807ae4d038f5d0cc7bbc3da93af208feb99a63bb8ff457154d93a70ef8b609b613c3f83068860e6a1091fa3c88e0e2e0efe4a61e7ac69d5618d555443a05e12c628271f0bb799f27802326a47f75172bfaba8eeab9f429a227f937d6ba07629d94685680a6f2f3f8c71b709a9405882aa67216fc072f1902d322c56c265390918c0d743031f697a780d50fd5b21f42aa4ec366eb061182e25a6ea0caf982be270c5212c0656c375d46bf285c15a55069d829bf25171db70436e1a1afa89766adec5f18700d846182fd44ffa9465acfa9ffdfdd4829237146e74cafe07fdec2f2ff71dc501b4b9882d689a109d4cbf9737fbed7c3874e176a12db13af51fea40a9b0e8a707dad45557d87d97848f5f34e53871fa56acf912122d344a4218514b3d00cd98099eace64141e6a79af354ef7b317c132cc754bff793aca76d96cd12768e5ee90b99143ef1fd7e2b4e759fa2bad6bc713d0e0c75247830f6ab932251905966c00a2184331c3e3751735131756a34a86148c19cbd59ccecddd7e2cc6d8fdf6b540f1372c7a198390a2bf98f455064bfa5911c67a8856702c0c19387d9fc3bbe6d03cb0993c76e4cd3af09361137bf4cf39f011eff34fb75754cde6e3f9220bd2a04a5b36646d0021058fe30100873c4336a7c5d98627080cb23efa372355bbe4909876d74b90717ad8ad75a5dfebc5f8bb86b002f7e95215944b549db828b0fb60db3140f5e78fff4d6dfed154098130260d7a2c6b99b57a2e8f0fcb88734c67602965a030032c6dbb2b8d8f80ad4beda558ff293ed8db69e45cca6902965ea7b0d4bf01afd0f3e59424c4f609a8144d6e1e7e88a75f28a14106aebf2fe7836ef0d82de7b94c6cddb2cd370ff88c2ce1eb8dc725798c75eec6dd370bf804d27070cd8aadc0b10475fd03fa7eac9f9a57990ad79827856d793d86ebff3682337fc0ab79289d8fe6010938f3282d9bbcb9fc062d0b2583d78e2166e110b0bcd9f7803dbb788a5cba3958bf3e6e680464fba62301355ceaef80c6b35b7e6335be96400a5400c2b08916c9c2333dc48f5925bd720f61ffd105c538f8b7fbb8683ebe60f20b00c6b5c626399873748bf600f689687910e6f2756f7043b23f01f6e70a405fd25fb822afa0eb47d0058224909e3545e387d16d7d43a410d7903892e058303e388fc14fbd43524b272467c2cfe838148eb773cf24ef07ac618d2c77df0b709fe466e257f5112097ff7f12bd9695dbf895a9039b009f2144ed030927747520b37f0e4ef932b9cb8e2fe33f475dca23d022f716c3076964360a4cb2d79bd3e0ed6baff7905cbd2ce2d1eaa852b73d8cea02fb41d54c2b9cbe8f5e7c8ec0d3102af1710e234d5b56013fe5947572099e54412189f60a0c18aa9be0d22fdbc320446d4734f70fd260db22dca798c22af434b65f8abfcaba056047ff49ba946f90483ece72d2be1aa2bc8308b772271ca78bdd0a040d95f3a0f5cf4d351bee5fb0d8531200d92d47c90ae8712928ef419ec7d2e5764abe14bfa10a4cd3d154e71091a3fe590a21b927e3d82d3159f05d2fb37bb21e318f731bde44d4d0baad705074d253502431bb970b897c0f2fe18718957e376cba619fe39f1355280e18caa02efe99f82131e596fcc60940bbbf8074a4106b03564ee20fd04ff69fcf8aeae0ce1f56c2dc6839c5e91e9c501224bc427d70f703392c03b4d83b7fb5fd5bfce08fc85a1ddd2adaeaed92d1a4ce852e2297e3ac02b2c48c97c61c307ea4033bc093881fd008c5712ef93559498c8939b0ca05b9e7576b03cd1f4b0f141383c9a0738422ceeedbb79365fe8f00df1bcc6fed377f68537978a8488977c4f51ae4104840b7eb053b711b3274a9b3662772f0be2e11407721a61c044d7461cf22f110dae7254b2f070d114b7b780a287d7f9a16505051ccb4201a7acc4ba4db7b142039d3577d761f79c6c70061e4f56b43941bcf1b9e1ac28b3ce6463e20ff2dce60f31974db14cc502ce32cb3164fe94fd827808ae079ba14ee4cbde5e23d608f5029055c1d2c4cf9c4f4ae06528acbc6df892584f67406f311ca87319bf7d7f210f46d5e296f6928498a33cc9e1cd7879428ddb5637e27be1bf6ab4b6fe32f08207f2e7729aede94aff4b42cd8e45a7a1d28f102b479e2923dd795c14f4fdfce20b7cbeabba01caf76d7fb8def0fc1651d26d52dcfe91e6b287fa6622e6fa4a5a08c3e99ef0a743868c15def79a1b94954f4269e84f8eb1102bb10693f4b2078906fbaff40bf9499d8f8c7e03fa8bc1d973b6c393fef64e578517b245a25de7780a684fd7307d465288249c4016e27872184d1c40d9627618e1579cd988b5c9da0234abc8275cdc81890a5761cab96bed29bd3896577c047e8bd2cd91225b33d506a8b30692bda2e21d5d5bfd4d8178ef66f974d3420653fa3199fea4ef1a143707d5356c207fb28fcf0fa8e4977af47cea0e15992c9f7dc7d7b1d9a3c98e87c60ee6f01d9ecd05ab60940f004cd01732901751d2a65c69639de50ef4f2a9b29e0eb29e503f582e111ec79a2cf0f7cd6a66b30c087b1a18e9d4f19cc5117254210e59603e371af3d2678a9ce8d039dbcc2c3120e2043efd7ed2b326a9a892d1a40b9b1886c455fba5dca282214195b50e9275ae40522b35c54e584192d52bee93001ef8f8046bb54823194cef31887b1f986ef6c5005015c17f12c92f7dc553ce04a8e746d8f0a4e5eb96e646e35a64c3243f21df3d3915a890f21e527253e0030ae9198a3c4e699dfce690cf6d68d291d5a2a7abb2888595dc09425cbf5e45670268a42da638519003e064fe47529b26d8ff5f5a333b98fc13971ca41ea49b4a0c7291a930c1400ac79a73c8d5daf488b4026d8ff243660c7f3c5636bf916c0c00bead2bd3e573daa0370655f79dd7dffc8744e80ae984937cb99466e43793bc0f62b593c4a1830fb07ef26f1fa39fb9f4e4ddd5d53b69090b23adff13e5576b020a662259a36105ba71eff29730283b986afd0a6cdd34ceb3d95f05ec715d1401027f6563c7e8f3f5ddd1df52d830160c670304ba8385afbe1d989554ecb06c099418c44e6e353cde226e83d7704dd6ae56c0647eb1db290df92c89fbc6c5d80556a89d0de748fb85cce6987a037fba315b8bf21918c5edfa4f5014d84789110deecdc36d3d140d251700910d0efe0eba1f6995a4e07f9d15af2cd277872f700c13f77553a8f49c26c5bbbcba7d5aeb8cc0c179ff641b012ef271bc87b4f250008cfa1d4952960a832f4c4f690a9c9f107c50827052c037be983136425794a10d51caa2c1f12317f6bd5a111501f4c11735e7e91ff187913910bf6d37da248709b4456ce075dadbbe05fdb281d71bde7d4d2d07da329d55196af75e549207cf0b7b7fbfd1e5b993d0004669033d257674927172aa7a4f10cdf17b4f77c8f4b70877c78f245bf8a1d827e7d5c4df33cb593173ba4d14a9bff7908d0ae1b283400eb5f6cf4bc89ad0bc366376c85a93a6650240941e34549838905f49b90c671907863c5e3c8a300db8c052c74bb1df6e357b6d0382a7f5c4c33a556513316d490142d2d496d4de2981d8de05eb54a6b2d5579b0b6c9ac4dcd063d0598a8e3336087920c0f6b1476b885be323e02499730db939b6bcd4a46244201c4318631e3d0e0ba23190a0d1bd6e72dad5dfdb94313fdc003320dd71b010efbb3f64c7e12e0473421a15dd7f8c92f98c2bc329e39034ae64561241b5ede5402e9e2fcea1730bb3abb509575c2690d3c415d190b233c01352dca5f0226b29df8e729586c7970847839a5987cf51c981419032945a4828dfbea35069c4c8e59e8ec53ac72d610293ed001850d0395a2f064f36bc4d0bc1574d5389c14c394f5775f9409fe7430730ae8a3945af71fdf34759500410fe9d6582b1d66829b4aa4157dabb482b2706024e4ebeb91fa1955d726b709ad52d260408540fa60ad0c9187a2654115e46063ca9554bf53eed267f82a375cebb6b15290b6ffe40393973448ba65b60b91c0c0e60966f3ec3c8a7800ebc344918d985c46c9cfe7283af78d45abc8c24ecab042b2f6c40b19acc7da31dd9abdbf79741dd8adb6a0931e409f037d95f9f856409d8b217a21ae8f3f53781c8ad909f18424624308563c2ad542fa2b07865c3d50dd65aed4cce3d5c6ad092e88e7f0e9947b35bd47b5f097a9e2ebb23eeaa0d92098cfc174f25e3e3b355310b3bf7bc3f5c02ee6d93162784872787b0d56ab9540f11c83737166d32cb328afe9b2552dfd7b0940599568b71bf0216cd4914d8c309b70cb0edf3a5522a92e85527b170760bec19ad6b85aaff61f91b98698788e80fd329a7c4c979b62f0d3be3eca0e97aa47217b904998a59bc59d9e6da49fa730a9f37ac0b43861128735b92f476ff43dcce29479cb1e793293f8ebd71cc04240754d08aeb6f36644b3f3bb04d6bee3dbb9593fcdb3a8f5eecda426b9d6c224f05c8e742947a6cab3c95fe9800174d2bdb3e9f547de37fd62dd7837679d94802094b965c6dc6520e4c1b5213e757037f47115e35bf3f14272e402f7fbb3c40f5081e8aec8f0b1af96abb9a31a67d6b06027a8cd22d4c135b62c18fa49c86dc8e0b0400b8df9937d216ab3c2abe634fe88df916d9b5fca2833c46a5b062a4d9970f6ff07085789807b5b987f5b4aa1f42f9538d4a8b7944318c46c5b099e4e53a069ed76209c9c0f27402332bcc0c8240925992b7a89c08d6a7b2a69da7a0908e05653af5e3e4acedbbe5b307ad75732c2d4ac4c51e75d838baa697cfcb21120d016c215625813ff3edcfc5d9a2d7f97f7797ee90c530becf9b49c45952d5cf660f0f8283414799edbe8a6e07b3f15efa06d724de8d341214aa18c67654ce1dd704e10069799362df76dad246b41926bb8c1d1f83f4ed99590bff006fd3e2f51f01a140eb2ddd8577802864b6b53f741865a940a1bf2acd77af040a6add404f260208db48652ad7840ce2d26dea5cbaa52fc1b2e5cf8a517dce3fd382369bd8500942f1aa05bda67c460cb5f2b6f0c37f289d39f35f881bb7ce4bbd3dfd23ddcc0137a78aca135db4bf8bc2e1b01b4da273ab8cb6d38e953ed5f970e20b49b2110eac71bf7ca763d2d90ab757793315e82094364691ad58ae4d88009b77954d0c01e2db7ac5a858a52d6028ae354df27fe26ee8af5f4a7231bb67286dfe8508a40673f85a15b13084be6292d76678b06b22ce7451e846ac74434a92f22bd054240cb5f0d5c2cbb0b06189b99d265aad37e8132eae3a549734bc8ac479bdb2e67d0b6c61de484c83099daec3ca62703c10d9ea4cda27eb039bca19ed89dc11145607afd33204a7baea7302d97793bf14a3e4b44cd427a4b582e6df1167240026d3001d0352fee4ab2f4c869043c6115bab954d61591b9c5cb5195258ab0fec2f3d0bc069352df5f5c008dc10b175ba431177ee597ff60b0c8d90a3e307c0d2e0cf0521bdb1188def202443b099543bb4f8678e5199bf04374ab88b64482ceb5e640efcb6facfa1b8be95bbff9b7e9740017491bc6c270dcd3ad747dbbe728d2502003488bee021fa660432775a60f511398095c726fe762f4f478ba036b91f09a60872087723d781bc277309fac62ad69cef4c0dee25bc3d1f2a78ff2207d601d203d4c95dcaabb231a5087095dfcf670942f72ee6e77b60acdab4021f5155c4cc06334e421c507fc2d4e6b031fc87cbf931f887169b9489841b8e6ec8c883ade100c5ac49682be1d5138f24e5c711b4b60cf7efdbef4ae44b025242aefd6c30c0098ede8dd857a74267dd01cddd84b591082e8ab86b115198babfeff4b0fe0f3b07751a1e81d51c838a787869a1699f771fa97a10b43700c070c6ddeb337e96140edd4a30aec70863ae62e0e17004d3232ead54a23a019d9e21621e97ac1669b10f0695813d28b164a4112e14d5ec6242408c9d3bafc0406129358eddd1ceed460cd33f8d412e4a8348a43b50fd15974b981eac964f879982aa011f3b84d795d90f27c8238b65552ec701d254dec4b875d36ac02c31bf8f02288a22785b7fa75a060a5659ac2a349359584898147b58b5ccc48a8f564d2d069c0020622c7186390c827b543d4d02717da4a34fbfe911e9d0f72a6b01c5dbfb906080c74ee0c75b0c6dbba53dbd549d8a90ea51ee5f197e8832ae7ce9f95a25b2ca7c9335f030860da797bee9b30474fb3739d13ee261f41de39248cc319b69bc05a7c7d99c77800d138893be49167b6de6575707ca9bb3ec74c8cee19ac8dac6e607d8b265178e0840b99f9c443b832bf10de1360407707df72f534c52331d19377012126b75a709dc936a74cea95152032efb9ff85b619d3e0a8496e5a1ffd291c5b8c3bf9ec309d9ea30f1dfa80467100de5c541b34d2db7a736b65a644ae60e49b4da3e66f80a3adfe2f1665100ae88ca6e6cc72695c1d71ca95a682af86a4aa50a1c19a35a0c0c67349af8512b46abff9ae81da45616fc3305088a566c588cef598cbdbaa00f041f51b8ac0f9b328ff9ca7e1d503a79b37a403f0134689d09a828a30896940f192bb3067a6a5b180d18177ecda10285a856e6eaa2fc9025f781a15a965a0f0541c60e27da346fd4871d702324214e7f932932ebd4c1b379c5aa6832bc5ac60ee29a9d09fa5d1cc5bd819b9eec4c54cd4b38ee4212397150022ea897188c6a0f2af6337fa41432b2f1a808ba4f1d25dbb8c656d26f00bb9cc9f1aac8aa71f70b6f676a11005f062bfac1d41e04d99196eeb6417365b7eaae2b72eef1d6622c077c3d88e18ddd5b81f5996e8adb50958cd07262c7989220977c17b733a7368c0561e1098f323fd527e47c9692e07abd2814ddd84101aad75a22cb99733b2e790b32fcea7b4d655b57b3f759f9b988b8dccacddb031c2ae2550ffd622a698da80727b4794cae3f90b71d78329202e9986e58d05e6a5df7444d7f0fd9a83a4fcf0880e5d92ddf3d6b84b621cce630e51d9260e641e072b95f6764aa35a44d9f120599f084e8d48f8cd55505ab15b1def22946d180eea6685a52d3b54c70685d830a97e8095a8dccd6e3d99d93e294cedeab32ff83fcdefc444b7a7f99f7504007071b04b56cacfae21689971f3d41af62c5e2c5da4ffbb4b8405ab64ceb3e39780153d6210c5f0f9ee8cafcc8794b503a7e44c130e894dcaf59ab7e1e57b7af1a05287a404cd6bf98780e93cf64095b6e5c478f6033523d7808eaa1677685ef4e00d189728977f764fa1bf75a2b099bb82a5790d98ea2e389e2071ce1b103efa203e1311f3cdec4f65741e58b2dc0af80a9410b7beaa3f9cff65bacb2f2cb284a0ca03f94b8bc408d4f890511597c7da65049b466179237259173c5d85d8ca63b007ce8cb9423eaf7859748106446726f3d4c3c0ba666ac95a229596989091dca05e18aef234df915f3e20e19fa9be8bd9268e61ebe0796de33bd8b741a08e88907a3c2de7320fa960c94d98e279b7a93af016b9cd58fa6e3d1d5a45eefa296a8019efd1736a02342bfb938ef484ee45ff6b788e3d5fe1e50884a51c316b015f305bd48c9d87f24ce550b7080319c24d40afe7587e9d4db7b51ed274f7f548ce00af009cdc5b8d050589be9c484437baaa9c2bc6a7dd24daf6beda9f539c923690457db024a0909162ef3b399441c104cb3607c68a44ba84cec82a64fae59e67800e366345b592979ff8290e5626a2a901455d4fca58421c179a6167ca0ec79cb0b235ea76868aabb6884ece7c1c075b68af392b01cd9369508f42b2ffa5da10200e561bc372ec6cef24d5275d9090c709a83915519f671a6b25866a8280fc792033b9ffab06ff31f95367d15d17beaf300c86fa53a388036efa499e03b4894960539d766f85452c678a99384c7bdfdf12cbd35f40f73d9615adb4cee2a7f5fa8048a1830f6947291bea9719604862d10fd09e5e04b099b9aaf581765d2e89cdd0beb7ec312b3826257fc40c227e096929bfdb15809d72ef9abf8acafa05c2b9d00a811fd7fa4022e24f06dcda2bd62be42f2d408a1ccd329bc4fe6158b3a25280ea1589add396ec27d423e91d075be27a964662c067c6413e9604ebdda3ac1620447ec22b30ad153e4615b47cfd0eea27300721c3812d4f4dd819191ec50cb1a0fedf5370aad82c28d4f4e63d51220ca91332d0f247b8e8256c422a2bf1ef64109fb4d34e347c549968c561b7255b5123f9e760f37699192ea61bfc688c07999015176d2257350d7bdb1429e340e8f0d596567c59a5f68575783e2aa955979bc0c1888112303d9217c4229b8f1c8760cf6db2c2bbec5bfa37447fc83a8e89caf08384c6d6068ae147684f4c24ba053739298380976acc6e038d017e2bf780e67065be245e64619eebe165e94ee0957b90be2b629dfdd057c4dd950838f604dcf04f59bab1a259c1c798d91f21b37a2b8aa79c099b5e31c3d141c89bc3f8b80220eb656f6c52e0669277bdd75c2ac8b06f4829cf7b10ecd0cd3e5869f53b3227102c7e4580e110851c9b90bdb1efb773f50a176fac028026d055454745ff156a80e8a17924b982d117e9baa2ee7913a9095c4f955ef55bd65992d9b421f7aa50b0b4b402b9b899cbe48ace284eac2555d2860f774e0faf7dd90b014ca6aacec5d0af0b7ca1ff0e8c8f4de0f81c224166d3228b029005c9c8ec57dfaafc545dfc20ebbdc2a8113b2a0e6d62c1cf63269a0c7d99cb5949265d3a7ab4150485f0458041b4ddc94166e3793c7ba100619041c886e4d76c9c9840594d77eaa081b02f504df484a3d5ae9b7a2e9daee5d8d7322f00617a3087dd43004d68df53755115b0a847b77968ec0e8241f38af5c4cb2e842a3e316eda6ac26fa3573835211d95a06 true -check_ring_signature 0beffa2eaebdaa1f1c21636e10b5f416f3acbc83eb161d8a25f7490787b8be02 abc6daa173f9629941f6fd539dd119d44a147e6d383e00341ebb04321a62e117 10 cb23c8c65e9ff50712d4b08683ec599b96e16cbe096955bd1f82fcaf0855a83a 713712d60b3c37336a2a680f4bb2a7c5be7701128b5601b56eaaeef39f51f117 03b298e854317ba6ed0bb0c72b95629a0d0cabc3cb7de2e5404724eb3f50120f 61e30dfa1782afcca997f13bb9ac755afbd31c8fadc4f915c910a41bb95f0d4e af2010f38d9244133e0e191acba1fc3d457be12609d0c2117ecdb705d4d96703 692fd0d45042340f8b5851f3fc8aa8e26af8007420282144b96355e7680f0c56 beb4a0b78310ae3c63c924b3efedf6b1dace5a70bb8d1979ff3856801618346c 4c40b9dac64bf7208472bbbb65c29233ba64e0a75da59bad1044f75017bca57f c011312cdc76c1ba3a17cac155dbb1f37fab023df047dc53d9de127218f2de45 426beb94453cab67ea22d0c22c7c0e7fdd7665b2534215ac2010407cc3dff802 618334c8dfaec67d7f2c113d252b079e0f6913e2ecabc442b9d6acc32aecd70f7a39598e6166fe22d4e6ef7a3cf2f6896f988fb5cb4ecc9a65bbb8d3e93216078733a8f24b7a341a643712ee161213d22d70d1c85322bec34cab8738ff59c10b46e97b19a7968216ecbccbc35b5e261fed778ecbf33bfc8a2a94517df676ec04eb8124b2a1cfd962fc87d9e75691fffb179ca1c3c9e7c3154402b4a5a04480027933137ad6bcceaddaa45a755979aee0b71878402145988f31d2e9822a5074001934f1410f670d1515a231e5407330c5c63943ef3164724c75bfeec720dabf01394cea4ebc395e2a833b21386fcb5d31ebbac0d2dfa765910eb7f679fdd9710c2935a0768a0d47c9bc9a03d375a7308ff1c1ac1f8f68abd7075b61e66e5ef107bc8f47c62b3a62d2320e4cb5bb7c4b132e4236eef866877e99064ecbabdbbb0ef2e7fbadf6693b983c50f3d769505f16122202bc6cb189f21e0e1a38c6ab4a07e8a15de4e3e38b5db5bf475b7fe84b3e5ed545e39fcfb30a459cb9912e8ff80eded1b672d5c8ef81d9b888366985913b23b7b50bf634e0a2244086419a836d021ec9daa203587838edaaa6c156557278e8908990231273b4395e9b545866970f26c6bf08ddd3d1ed70f9f4cbd22488f08a9bce60194aadf0a561f1dffabeff042d912c2ba87e9f1284cd30747d5235bc7e23596a91175c522913ca272b6fe70c2558d661cbd7d4b0de562feffb19cc00adef6ccad7931e74c56f1ecd8f9a18035dd39e56c046acfbd843bc1064383003c5a243e66f431c69f88a7b676c51a40a4b5b994a67c334c680a868b6ba7015e59684db51205806026f239cd8fc3dd006f407ce8e2148338bc0ee513750991c6d7e6801132da90a2a51b2f454107c3601 false -check_ring_signature 3ef0e6e8cc86473d0eceb746e5361e4366ff8bb3f1f484dce78c7683f306b89b a53c32579932cea998ee6108757cca08b6bd14db4ca307c8380ad04ca1f4cf52 1 648fc363769b719411d7e895ea9454bfda91b7bdf8d2827316084686e9f8b495 381c6b57c7a88a2770a3cf4efaac0839aaf6bb4e73a33e6c712bfcb85f6c61fa23cb2b450061f74f54787f497466cfccc1a97f8fcea6c3f6b413f1f0acfb0e92 false -check_ring_signature f97ad686e23979204a5bcc589711d4e355570aed221eb7082d8f537e3c487e7b 6c4dc7ee773ca6f485e288234c818d3b4bae0738cddbde16aaf85e2a572d99fc 76 638247c5786f438dcde66ef07c85bc86cfd251b6bf66529943e14b844ddcc581 800da8bb667c6eb9c16744b8aec472b5e8abaf2f86639ec97136b777d4b15a14 2c6786b8fe343c6a48b8e327d12b72a88e77833235d0ada9c725f22a15929f64 cffac5de95063dff441d4cd461214adfc1f5e171bd4ec9c4bd0f9bd7e0cd13bc 579cfd1e93e6e78ddf99c6a6e110f4513f001cce30b59e7dfb6a2cd79128a22c a6ef9bcb75bf7ba038b38ad03bfc44e7e4c176846a5697328a37416e07e3b263 10dd01a599961a77225bd95af869ecb436db92866a216696fd57587027512668 2dd345611f617d5b5a318460b7d3503ae1db592bd4d6f00dd16b825e7523e9c9 8a125fee430b2955ce0ea0c94704d35d797fcdd2be51f4aac56216df43fbfe11 2c504925d028acc5f12261097d9694fde5c4926f605603649493c96f82ec243c 2acba3b387a0ba261713dcecfd9fecf5545c4aacd5e6648577de5e0273525001 3e306e064be4142ef27f8ee57a2cacb448a700e5be5b0542fea636a7b29bcfc7 12b7f37ef1e3ead46249ddd0c84d2c4c7f2988fed173dbb4b1ab1b8074356a6d 511a0baeed351c5349eeb4da3e322118bcdd29b3d571d4a8cc2d4e44a282feda f5c5c14e6057c67bbf0b89b428bd18f0b561ca623dd408f62e32fb83cf0b7dcf 5e8ddf0bce5cd9661e4a5901f6270f86180b716c5e4733845ddbc0da02ddc1c5 a98b8edae4355b0d1a3a5a791ca255ea371fc6cce4d46d6fe113b2334b153934 b9273bd6f33bd69a4c55183e50d52a39013e10262e4e31ea8382761b58ebc7a2 cb83bb5bfcfbb2ba35d95a513a5bb90b627de77d7c7c9e6a6a55462cac30be12 fdd9d3dabf14112ea75b8acba3c96fcf66ad2850f01bb601a37b0f5d8bc92303 1772d154fe5c4dc4c55baa72d24016e31be4a19cfd47e724ad1ee0e4b20267a6 df2b6305266c9ca65108255919cda8b16d22ed18f653f3825a8b16bbc21b0605 948bf417273c8c563fb4391a2a7c472a3bdafbab5632c52e3ecaa1997c7a4f00 6c8019704761be1b6198ae337b1800ac2d9244e1dca9d8e0d9e0750540ef30e9 1a75799f67109b35e2536cedb1bf111eb1e1be4bffb1b187727e209f60176640 dd0b1f90a474eef2bc06b258c823393402328045826527700237ba75cf43dc29 863b711b5a88e2080beea867befc4537600c9fa32790cdc3501e2dee96098fc2 f51b5cb9bf2cbdb914b6d0b2295317694615eefc0e615a644d973d990112027e bc85998ce578187955264652e78e697193dbcf8edd264a8faba423b25c4923c7 6b83e2c3c80b5b5e5ef14e4781604760c8653d68db614ab87ee72f45a3e93703 7683ff33c0184387ba7fdc178f15a5c3a4dae2e9eb7ab32b27c6b060a0be77eb f6bb9c919ae7690a78e82e7721ca047070a70426cf7cb353739ae876a76ed358 cb652b83157487ddaaf39a54e22c637c78c3a31e1053d40dbe4062c6aa774952 5a29055b4332df9a86a538e82a70ca13629831150e60799ef44edaccb27a6016 f9e62a63cc4c3240160b33c417e48af4ce14168513e224d618a0d0aed4b12bc6 3d259fc14950d01c5aba21ff645d8a7498e2b4040c42bece09de62d9d7f009c8 38cc26b2ae1ed56c3610f0092dbefebd1387fff060add95a26be2c6e50c5bb72 272208b512331a1feaa6b112a1ea82dc42f9a7abbdf632052b9406e1afd68007 78d6cdd427c1a6b4bf26e6fb6945ebd76aaffb577c068a42b3a2004c199b9863 71964b44986eb5fb4a9d04cc83222a05b2abe0b85e7abf444a15033981906e47 1a10982ee7475e06c8f3b4a43cc6a8d6e120a3935f5288f25ab7eb4f9e265cbe dc8ae0cf42970caeafa45f6c60e70da375a5538bba996fe1f2866ce568d62b9a 55003923511518e2d5745232a4dd8cd6a590ebadb766e9b4e9f5258c8a0eb155 0188ce2323f4a5fec481e38a3ba606d3628da27005820a8724291a47d1a6acfa 39227f17623f0ee388f147c8df97ca2fb004a560f128e6b54959f3a361b80e14 bf2121c96280784f878d48a0543d3433a25a4411c8b9d073246b1ae648e7f629 4765ed11ce9996f2f4ad4e3952aec9e7022c95e5521088e073e86eaf47084219 535884de3bb497ba0bd59b849a89ac8ec4fddb071f9405ad5ff439df0141dd63 dd8b5e4d556af0425aa6cd966786013485d63dadbd182d75a564e0d6ca015457 5a255d3e12264e77fe0cb320e44d76d3f6b5d0823c81cb6c38cf775996a285a8 9a28f824800c866ebcdf586984b5a1b024db9b6f306b21dea9c073a5cce905e7 64e90b46de69c1601358c7322b8de13ce662e7578ce0917f08549aca2c1ceea0 dd45bd23feb4c14b2de273c8a45cf5e6f984b05e8c1b38e651292bbfd1b22f1c 5117a532130d59d732b70e5fa173eaa4b4d726d828d9d85ad3be9528e34587b4 f6a379d01f5c8c3ce11b8c05a3aec5c5c4a4135aaaa93f214bcc58199a660c59 655fa119d02480482b6b153a1121bc0912aad94646757a29a1cafda9fe4e5b11 90c8bc15bf176e9549b6a9f85ba3d6f0da87ba8f9187f85f94b6ac267a5163e9 dd93b42aa1f6cf20ecbac679f8187bf58fa669ee8d4b00f63a4a7b25a0e51e3e d60c27fc161e4b047a90207fb8ea774fc53ab224e270be538ea1b3607ad80bb2 4c03bae6fa018677afc1fa25e73efbd61cdb9e03a91f0f2cba9be6ed2daec75b 31fddd2c121403dc3053f65b851c72f23abf1f78ff255ed31bd851e46303323d 5af2801a02f5bcb8c7845f2131b057b11cdfef83a779969c82dc99768b2608bf 248361245396fc31d41ea89e184558631ff06ef8266a7174d11ea053481edf7e 4cced1f82860aa6783b7adbeae62d479c95fa2b8aa14ce03a229d13e918dc748 64d3cde94203cd13a2d3fe78a5208dde676e76f6747c9922d49fea94af9180d9 a45cf81100882338cb610657c4a0dd2aad0c9cf95d1e8521149ae99cf924f9c7 40a370a8bd08e3dc3956d6ad363b5e45bcf8c6847ec065c8a9f063828a71664c a7dd7b602e2ba5e197945c7628b1c2ee9f9c701ce1c5745a8633048a40be7219 1f29929ae1a0f79fa9d1910243b738da93b22454bd12e27d2d97db11603f392f 03b56245924ec5bce85757f44ea3bab0b0e841583b1c642f1fd0342c838e5d7f daeedd56916e909eb1f2db5c86147a1e2cf8c73c3b008e25f14e139bf8877c16 d071dec0215060a0f46f3f1d9021b7f4f0c13ad83109141bbe6bf802d9132aa2 b6038e89656172c3303f39bec0f685eff066838a93ac17cb98cb314ca62c9acd c4beeb43818d7f1983259efcac6dd0f4ab70fd52183fada50948ed766a87a8ff ad57317408c49c99b79c41edb978188f9c0dd1e5d81527b1f28f16000b0b8107 eeeaa945e1a57caa1631d3b839fbd9096f37bf7dd91357d5bc4845ba580fffac 3f18c7de1c1ad35e4130efed7a0a499f9a2851741d50a58a447d842492f18a0eb71602dccef37e38d9074b86ab9185999e0176514a221d6b7e82f27ad6721f06ff6cc1d8d419842be00e58229720b55a8e531459f0564304ea35aa03dd65a505dfa9da798598d211d8de3b3be4d49e935c87cc85ad3ed06019af34fb12b5d00d4e9689b5679e38f18b7078b712f3281387e5c69b15c657c786c87ebef437c2024915c73a0326a45fb46571127ae8cc188af2f2bfca531e13d8fd323b8b6d7d014848dc345deda10c1e8041f39f9443b8c7df6ed322af0c9366705c231978e604a96ea3f2e6e9605a0765c25cb1d300ddcf6336816d826bdfcc5496d025408c0d6179ea5538dec4791eedadb219b9bfce7306c06cce65f6a6f1d86e097aed07014942d3c734948c90ce12b05646603377e39d7983eaa7380182e2727b53c0cb0aae19a18541fcc39fa08de9e39bd5bf82122dade786830a5dfd251fe46a7e2501bc52073b717e4fe3d449bb7cf946da282d075933518734f973481af8c40bc10d496fa7d1b9b2a19fff9c545b48cccd6dbf8fb5471319646bf070f8a003136300babdf71b5600825acf41c5c57d0da415af55d704c7d1f03bcde3a4feba89960dafffb5d07fd5122089d99ad79966e5f27302aef742ed1c39939bcada2873360b3d853fd67b48c9f03cb3c8133cd4e34bcef546b8d6146ea3e6665eeb214ff4009b57d08e300ab553735489f97acf4ea64feaf4164563d41e2308576dbda56a0088988e6a528abccd37b12aef0a067a290db3b8adb1c1dfd308c78d18c7dff107a4ff1167d8d6f3e7de3c7167f620296df573854bde850d667a8c262c8091770613aa87b33e651749ec0a1fcf274bb6a5750dcfee86e3edecc20bc0227680140dbb13e041e87a4c4e80cf6d115f2ddc8be656f407af4d690b035bff2b4fcb8300b2fe659c830d24bb733a21cd39fa6bfb60dc4cede20dacc0700aa7013fd19b021cf25c8fcb0163d1633e9ca1b6902a7948dacb5097710eae7fdc3993d3335c065cae05a7dfe658060557150f2c824e6dd48fc2f775817f33a20697322101f804082505c02b47a434f3ac0f6f93349942cc2d2c47f593cc30d0458e6329854a006e460f39763945721056f33b1b2b9a0d008c7105a15634c0994788631430ed00cb966a468f22cb5586250d47e2ab6303bfe0a715a7d41ef0e8e45fbb55ea570a84eacedcc25600bc2b6b3ed1ae94f779141afc389dde1806acbfc26804e44003f7af8c6aa770ce793dfa2ac72e68050d0f7c9e33c7d2a6986b3ea44cf6652f07af5d18190452dbedb1968312f51f06d8e1d943975c1da8e3c695d5fe8f62ce0c6baff2f0b08d7cbc9888017348d07829eeaab844f477eea324887f329f9d470846ef6639c81321841d011553437062cc75606220a3f97afea87ddfc03f914306a240f2ae8917abb37891ddd93a312d11ade0828e2bfe41d9fb184298ef5b7004103390ac2b8c14402e8318db397da341f4ef356114a8593423f94a574a29f7030120520be4a9e1ffe84794a858e5ad476e8520ffc35f4fe4b8164a7215ffce0fff1b82a4456b6d069f6109204d8db7a73bc4fc079d3f38248ae039e43ee3a006842a4fcc4ee6f4aeb2b4f81f9df709d1ceabb535a81e3ea134f4cee507597c0a69eb9d5b916a57ff39dd335888e810d7c0cba71646d10398d7b5f73c4433de0f5fb9cabe0d2f87c1e1f1bbf32764b340c5fd495e73bf548a49566806bd5a5505881eebddf0dce835a1359803c9bc35168e2288f4c9e9ff56e235f0e85381d709422eb1a5c74f9a58b958239d28f01923c09886a38733f209c3b32c3afa37b309a557d0e3c316dc65baae1671885976e4f1b0c7a123dae9ee96a45d89708f970a25236e4beb325501a19e851c8e2628adf5214d48bb915e5c482b681d73938e0081c2a607aa7d7dad21cd2e3e9b811d80775ad361d58b4650ec2b8373acb9e000573471be3d94b4f8f2d9713a4a3161f6836d52e5d4e6c7a726a2b98fcdf09a09a06fcd5ae6ff183e08ce6e71be89c83f2d7744f1e611e1a58f090b6d9293450be81fd11c88a3e8d74f733ba38f68f7c2c17200502271ab96c0e186639f94fc00f5ebfb6734626c181c81eb3d4153a59134d26c5381d2455abf3af31f82eb990c732b1806589302ddc5bcd1585bbe2238eb1382afcd34412a6ab2bccd811ef50481fe253baf6e1fb884913e6a84dac1e3f1a3fc5c8a3d55e99a452f8d3402e7057ae65be721890613a012202024411af1f1d5c666ebe8cfe64a746f96ba103f0c4755f5c49561c3b5d1b050f9e67a1d3582efec10535062b6e40202b5839dd805fbf02f9f664943f5b4563cf5f2b82eb874d59e68dc837e5e4cf8fa7735893d064f758155e4f744cab55b84c7b4635108eaa35db401646fab20d605269ed00a0b2d1029c9aae22cc7f4b1361b87bbd04170459719c44397e46039ab8fbc69a503af4087f681388cef2d353749931022677cef45f84a5b78eaa563fe112459d5004d7f8e48bd5b5d866be2674c3ff4666f0bdd245977529812eb8562218fbb730f4814afbbc2b65aeb98ff605b0b8042efd7ac71bbe27ef2119905ac2ce94bff094f4515df5e92f3f3c6a8e829832b7ca14f30aca8481c65a2f58e1152fcf97902e65dd679f54e1a5896ee2a61cf2128113610d18305221c2857cb73796a5e610e07d3069df14a608fe63eaadd107c2ce9301766199c09e603d2513cbf00c4c10df4a3a9aed9883215ca5dc8d0c01ddb6b1a6bf7096173679564b77577b48e7c01b5c122d8db35a6ddc3f8e5ad8679e71a79302de009aeccb00908543a40dadd04baececf94d08b3e9af5a4956342cf815cedc3d16e6e7fe6a69478fb8de968709cb04c525de54da2e448213927c84a9f1e4cc0967a3cf395fa9e11bf1541ea1018df1bdc786a8312fccd86647aa21a228ee38c22384b871585a403426027b3708e78a6dbdd535715b7fd57c23bc5abcfd2c6a1bc89572f9fe9cdd6dd16d65fb06c6f80b82f41948f37bb6ad013b91ca8d23c85e3761198f0437e78c6fc75b6f073860c3cf33a9fc5a7cdbc92e7cb7de2df7bf019bb5d1add0dd7521197d5a410c12dbedb0c206a02296d8ce82d8e59ec42963f56890b9b99858e5cec2d5878b01aaf8ed216781e32be342114605813877b3e3cc6989520879c1eebb07ffefdf081fe30c67477e02462782843148825dbe4b640c693ed66e2376fa85e416020b0339d53159d3254709329304659e6f5556adf970250141453fd5e4c42dce88480e762c255af77e7a1b5e6b92e3ecfea23258b527ae8fe6e65deb0f0c576482c40be3f5bd59b50056b2d85778caf26b713f3e6465e42f7e7c608e3cea78c479050de7566dc533ef25315ed49e1cd96c82b80fa0acceed9bacaefadcb43bf80c8c0cd2d060d845b4d08a2e2e9b5d1acfbd4e52c9a0c95cf8a2ffe3ef0a1fe0b08f03702651cf450989fdf3d4bf0ba130f391216aba58eece3d82ad5b3ad27240d30798638fb43ce36754c594a87e4b874ea2a671e246e7ac8f8a405306ca7b6b310d9b61ea37bf3787ad8c4f6b9cf095bc43efc05078f75f9da1d06e8eb38c441a0a24970444ec55c77d5c9bf3cf008e4fdea79cae60b990e2ce49b540b32ce8d001e7fd3fde64b52fbfb618236fdb67aab99a07df28483b24702b36982c232596055fc9ffdf46c18ab9cb2d3fbf878283f70a00f0a16f5c3ebe7fbe93326d17c903bafb0679f9a46b34461d53f9fc144330cb8f5bc7b8a75439e5b7164af6cbb8022c098c5ac752859c74289cfd5584060c09ad2b44c1d74669f150469ba361bf0f58d88935a7dfd9e0ea3e84d5d30d11a9f2f3d3eedd7da06d936d38399a0ae7087642039c0fb0a00b455f0b2ed69f2fac3eafb5b5453e4024aca84356631c2706069cd11510a66e416293b27d52c647f3de9b0439a063f5f300dcc6f51bbf3904ce90bbb41585b3e58912c27f5b3d41eff81d82d0f066c11105f07cfc6e8f76064bb7be613b57bb4d75450bd2b5d42c3bdeaee5134b134c84deb22a2c2a5b4b0170030e49ec46f95d4648915fc68a537f14637a836ed5899313c5f53a317ad00c990e5eaedb39bfec9a65d539369dd504a9847bcf5f94ac508be8e8f357fe480b6768cf29d381f41c24f99fc4f1fde4b3b653cc232baf4bffe1d1653ebd02ad0bb780be93841375766e492f3c8f837c9f7acf5add0b48532142be641713077a060f27a94990e9ad3be7d1b4d980fe484a71da5660eb3b6ecb100843c71ad7ee009fcdb9b6c3011a95c94f02df5991a4c12c5a1a19549b7966f7847d80353d760a01155cc087a3e64f20d2f5953bc3517d091670c120457fb23ed7647721b1d90b24e6ecc92e83ce3a4aac8b678747ba6cd56655c09b3d742e6f311e0fc1c4ad0424b01b988887275c5cd1dde7e818f0d61d379e52eb20d59137a463f288af480481a9f3c5a45c6bd3816cd92908f4a18b7de5b5ab5669c08d8ede22c23960c606fc9f225ba22246e320c82576ff4be34710c3df2c4b15d2ac2700afab4b54d0074f2d06da039becd27f4da814bd8c68a3ee97e0af6310aac04b05781c1859e4003658fd63bd138c41fec4cac07b29979a479386f055499eb8323a617533819a0d5e67acf66a4511c51936feea66026ba7cf4712c4b0c6511c349d5d25c16f950dcafd0e7d0f705c0b92da666fcd22600ae7faa0745dd52a5c91b884e756fbb008e8daa2cfd4db3d42790f5feb41abf9de11b86cf18f6929df989aa13a7eeb0e043978cce2accb5fbaef5eb6ebdc5f3c28616662bfbf06747263ef7b7e095fbf087d3fef361cb3cb7913c7fcdac576b7e523f045c645a9d6bde8fb589c28df330527f386523cb472f2bbd43f3df2aa071f1341940c1f9ddd9c5b65cbe635b6650154378d613ea74ada23cdacc02388a47741913fdd5dd6f7c8fe66ecda23e26c0425ef8df94effb97e0ed02dd1c59efa076aa7381dfad71e35957254837ef8ce0510b0532be6d64b773d9ffc6ac91ad4b2fc811730c79273250d3546748ab8a307a806e51de4434dc23fd8b1786afb317d0bfcd2ad0f31468f9649bb3e7386a80e1be61f6d21f9b13dc4eb45da9d85b5633f411afe3f125922776f787be7047a06586d345aaef5f983d4d0b76ea390af47796d2e3a48e5c4c543af938e34073f0c0c23c8fc3a1733232d6b105b14d56bf69dfd1fcfebcf11f54bf08b262e279e090ed87ab14cd8988c535b5e3b9a900f38b6e5606063d5f45b849b90eda330320dd477c9291affbc9ff3a853955c1c2fe916ead797fefac32bfb302290cd5b6d0ac4be6ffe588112bd386954b1966e4f8d99f8771b07e84aea037af1ca14b91e06d888ab19d17ca28274d1e43341d1ce496fd7eff7ea16b9feace82a5eba7b6f071826bf26f6f1eb7e1ddae3236f934dac47ba269a00423fdd7bb83c5997f3690933ae011ed6cfbc795ead0fd35f1e091c88a1458ae2e9da4ceaa87a589c0800062076b72f61d926effd3c03ed0f3f7131015134e3592b92ddd20671b2042f4a0527f94c62f286ae48388c59a1f05871983601cf205bc43f09c3eccaf5f97d570f4ab7a594f3f4debebedaf190c3cc6b5aa148b7983b4a680c722ab57d0ce3770fdd5add7671581d75d3e3a3b0247aec3d0daedad0a3028b22b5710c1b8713380c5b984fec6d67054f8d8e315a20a34061122e244650e2799ee792a842f778b705080288470678474f77a7490f88ee2e1b6b1bc8ef5a121c21ec88469168ccc009133a0f0e685b926c498bc954fc7172be88c3e4f1fe52a2662f5e79e1e36d090dc07339874b509f198e3b2e789d607824df843b43a04ee5f1323a9e2ddfe15b092544f15fb7ec1510b8c2949ee25a503668ba4c91d34dbcfa675681d7e1bef003d8515a7f860a5e71f584c56eca4e565c2d21f77d56c60a9dea4476329abf20010029506e70517c05986c7d7f00699f06e65893fdf98ecba984e1c2a4eab2c80fd3b72bf72725e22d6e4abea7ef235885bc1c4978dabf0951239f087f37a512019c3ba47e85d09b85c7532401cf98bb4955fd190aa47a1a5238ebcf698ac7a407359bab6da92aaeec369dc1f14d6195384d573862592d8f7e73b3c594d2894a0316c18b091be7aeb1f3f0fa0c24401a7e3c7f38e55e80d454f3ae8a39e76e9c0ad9b2dfd6bdd1fa5f601a2167fc76078ef41a6eabc5a1d4ae72cdbcbf4ce6920190b92ad4b56d5c5b428bc9351bc8fa116ac794e0ff284d4977d0793f12919103945c69bd430efb7c1d8004be6f0919ed0f551026bf513b66ffc4c27d94bd97035f22b2b6c6eed8b7ab156496db19c695585b72a461598d503a261cd911edcd0a820b9e052f402900608979503fe4bdcd04452ef6e7a3284ba90c7c5b3d0fd509704d99601bb63a28d358a1d4dc0582895ac884f01368a377a155cb94247d6a024543621e57d1f383e8a98896fd6fb2fad5b50eab4777f029b61698b1e73f2006d7eb362393163e98bdbf693d27482cfd31a65dfb994d2bf5c7295eda49f1a409c94c070ba3ddb84b6234b6cd752ad1200377d36c4c81c4d049009e7a97c83d0a6bf42c2408aa0e27fa571c4692cd865ca84e75e6767c20b7cc274c41c3eee1086dafbceaccbb6d138f30588fc9460c41fafdb9ce90b58f71e2e9839ce923db00c3a1ca3ff587bbdad51ad4df580e80a35582d68f68f7995d8411b3c597b1650fed6823f63c5cd4ec8deb82816b11c17e7e53bbc5bf7970f0a8002502c0df7d03a928e652bf1c16123853f274a7829250747647522c8c044bb80996251b13100f8af384293f984258275d59df70def040a1a0d119d191bba762d45fc661357209 false -check_ring_signature 92d78b9de3b6c118f1eb802b7152242588db219010860898b427a1864f7c7f16 8e7fde2fe4e3014c18f8dc1bb990cbda429904c98465cc087966d6c8a6351881 1 950a9c2167971bee8dd3786a37a773add8319501ac43fc6aaf05155350b99746 89f9ee0acb49508c68afa2bb9fbc35bf2c1a1e6bd0fab4ef7712c36bdb266e06c1cd446573b2464ff8849782eba6c91e7a7c608953e363e93ac5f1805d5bf107 true -check_ring_signature e95345fc761befad1235ae06ccb771c85e21e1d615ce95ff2adf1df9a580fc79 5e967925f08a4b40b209818b35913003d189289dbeb27e9aec6a0c98aec33985 10 39e6c05c1175c9cd40dd2fa9e5c61eb79d0218dbfef54d672f97a469833584fc 92c46d2422c848c0dc1516a95a72f93430b74a9873335b1851a021eeb92f06cd e29ecb3b1549f9b076d76dd726d12db7319317979b2fa509f4f06436a4b66577 7ec86020ecc1b25674797f01148244d21c76a06e991f15a70aaf9ec988fde33a 33b174dc6462b2a8d9d1b5c4770740936442cec871cec6c2f1b7a0b9dd4eb8e1 1ec8b4445e34be366e94e11f1cfd4193ac0fbf4aa8a16a5998b6e517e30d15de 10a5a5fbec77f81aaf0ab738a02d32e08b4e0d4941d8faa5636cefea83c64ac5 64ff2ed13ad3d074c2cdaa073215d16d905ce5027011281a85f0931808fbbb14 9b734e2a79bc87de83badbd1f59488601f92b1a4b596ad0c6f93593273b115aa 6aef38f79968106f327adb934bd2206ac9e8086f0002589ca7dbcd6c7e831142 50c647a690330942e373d9d375eb1350d45a4db38ccf282570992f1f9f97a00a92468d8af35ddd1df8242f9163673e9cded762dd50eeaac7e4b66853873f36dfeb65ebc4e54ae7a7c0c8405a3f5c7dd98ad3f05bbd0f4f7713bb6ea8a934eb0fe0f6e2181021ea1fbc9e5d3f88441bd55cb143b464aa7edd630e7fd02b00c502a89070bb7347d35d45e0676158fe4cf975ef0e461ccd0cfbb1ddd79217cffd051e44cd156119af81429f55eb1c09e047c18f319f2667ce3ab43f81bb59bf3e00eb798d8383283cd233ae3b8a7bf39ef41355e08faae35eef782b7a117449e80aa54ac162cf68b96aca9ff6ad679dbaee0ca46ec319694d370e18c9b9da181206bf7ce1e08caf1892d08d26055f25b1dd2339cc0c63ae3c86b4b97afc98c15d0561ef0b6a8f190b9efec717e536c2bc139c20042c5ee5875616af888ba907870576ccb78bbd5d0079ed98d282e40785e83fdb3509229ff322cb4fd6af1a2b7301deca33357c5298cec912bdeca5f1975dfe77be03fc9885dd56b6600847992708924c5d69f6264b081b56ba5f04ad6487bf8033bfbc9d3568e105f63e212dfa06ae93924203b199ff7307007033f7d45a8859e3d18a89c1c68118796ca3a0980cf35c128559edeb718428ddd4d8b5c193adc7a6e2d47609b33f28afe57785ce0a35475aaa00b7650a5cbbaff2111ee1089c4fc4622fd4ddc63a7123e1483518066c7259ee18b5f9e9100a07e5ca6ff628de5e0d1acb2c142d1b06848d2c9d5700d7781e2f7dab7f1bdeb5eac00ff29445815772fe28adf8623af60832b0fb530d3de1aa77f0411554f521144a7e5d94f9c3249a54f1e71b29b50859de6657750ec1824fb3ead989f311590d3620e8eebf3cb97f6d3f5757a567a1c9c9fac60e01 false -check_ring_signature f312ec645f2da47e3841afaf640a081cca4f083b1d703a65a47ff62c75157901 4fa2a7de08501b601c12b683c0d4427383394347a4dc5c94d5445130caa784eb 64 9015d78ad5b67a1fea2bc8cbfc8675c17aa33a793fa66dffc338d2fba07273bb c3d94bb0367e3f97d162e3fab392f5edb54522e5d73119700f74529bce9ef35d a2bf4ae68bf6ca2ed3b97220cbf8bb83d9e821bedf2dd87cd49c076e462cf236 7a6699a997b934173d1b11b6044fd110aac7eb4df98fa146361b04101e52e8b0 32f23e67985bf7f6e638e298477bf62d50d8955da4c278fd574771ac2032a63a 00edadf8c2fb50ad1d91147b53c1666f4e38adfc7e4e286cb9a167dfd7d618a9 16c5198b1c50046d0f40b0636a10c7b1abd49ffecadda660bfc5c86380e94920 042e75296b32058cfafb6db1638fecd9f1df49461c861e1a2e11ab5439ae25cd 4314093fcc13397110b938e2c1468c2273a39539dd55b8c33bf6336dc9d7abf5 457ff44a70b018f954cc9b75b60c89536ac612aa6c2e9a7a53fc81e41ca07667 99f3e3860ef04ad6f60c33c523a896385d18a1aeb9c6fa7133f50131ea6ffa63 ab5cf70050dbc597fb2e9992147fb33fad71227b5d426898ad95d9f45b107431 91a124e02a350988b14f4b2cb17c4a1f8e987d8297cf173dd188b845e7aeddf6 1093f0d3b2b9e0ca17f5da995501d9e59d3565239aaa72254e17cf56a2027504 2599078e646b9146f2cdc76450bf7260ea605d4a649b977a56b174dcbdae39ca ad07bc27e901ca82bca8bef989af7b425e8407e4167a79974104987795866135 9c05a8495681c3d892a985616f8a74a33c1791a25d672cf8af0548e6fc040334 33fffe66c55f3a7472a28d709cdf56dc2030844ae22d9d1af92e97f7031d5c60 09760bad28c543cb2b8ae89a1c743a7c62b71feb338fa766d91534dbc6d2b6d2 7aa1c7b4142e7fa51d5641854e2618c19e7db0695c5a627eaf5cb4f23fd7342e 3bff169c444d6b48d799defa44eb2b7ebaaa744a3430f792f36d40d9f4aa5b37 cd3891244f6a5dcb6bdf0d047c4f043dabc34f56a901b61ab0af021fab2b6018 dd37694d354d188288b79ff535e0e3d193e31c9e47b8f62d96f1bf79fb61be63 5b50b23b5f3b1e9dab38a284c6ae524c31dfc44d3470979cb73f96d62bebee79 b290b89cc2434709608b7ce84d66e14fcb8a8c2601f62ced30f0f175f661ea47 e668e37925d0c7066e46ad54b873318880523a98c5f781f7961515417b095297 1d173c8b2c7287ac037bd3febe73f1f37561f5daaddee200ea8d55448546de93 395d96aea501b004e4e1819f95c3ade0cbc6a843979fcd3e28ea969590c29d08 0c7673e5d6c1b12b666a5b703918fb73c7341392f30be4f09dea421b16fda245 1d8a7a063aa44deab876af21d95b477d6ecb325eb631007400b330cc5b7a761d ee89a81fb200a1c02a874a52f057ee846e8aae44214ad9aab7d23bb9a7a1734d 5c73c6590753ca7c013059d0230b2755aa20d8c153e661257e68eb7965ac212a 92972bacc834c78ef4445f51662910dd1ffba8a08bab689b42051b8a170d4fe3 2799eaa88f451dc80ba98189200cb3743805bb111b7d13dcc6c75ae582617794 1124da01d63bc97acca5956714012b65e0e5d3c7af0e01ef4cad8e47e49ea2da 646bbd8e30b3b47a8916a0b37cd6c8ba2d7b0aa2cca37a49d6fbc3f1bce3bbf3 8b83b11f60f841f60973ecb7f498dd2989a1792d5079eaab70ec22c0f620c297 ac2de97669b5f780552050b8fc844f981742f9116f404daf8e06f4f9218fb94d 5acd257ee9758c459a9c8035f354c14f2c3d569ba06a286de9d32d3da5a9c025 6218de21ab4dea7bbfe7e8ee1a66075c842d89ae50aaecd664be8a3438517a49 574b26d59d4430df2f457ab0c0adecbe63e8eb972315542ab9845bf973fa75e3 ebdc14c6a3154255d9b46b71fe870dcdc64c3e6f33171a1dbd29f97685a1fcd9 d7cbeb6194270d84f5db2b306c9a76c29415dbe71cb6edaf31a5e1a6495512cd f4ca6d8c9d9a83ba39647a224038324cebdff4693855c032205254bb60b6428d 985569069283473c8b2c0b796deb83975a417cc8d7e52af41ec28aaf1de1503e 310c9c52f0f6d9195677ac08e1299a08f6f33f5f6c633d35958601782291a19f 06a931888387aa648e089857a6aba611c022aa186a23daeb226fb512abcd3300 900f81d28f3cd38cf1402ddc22d105b1673f044c66a9db802d53d5e260bedd87 1e40836ec0b3fb2e103016fa4611a75bd3169af89d751600eeaa0041abbfba8d d2add5f134b3842d7bbac61f43aabd15300ee937ab73a535e91eea335ac0354e be6a98cc97a91deec7c7b0e5eead0db38a7af083dbb9e35087df8ece7632964d b43e772e6098ae880b0ae07688c644b6edd21dc84d34ba0775de8eb41ef6592a b76f08ed078d21dd1f44765c4f916e58d110c4f866ab7922f70a52e55e813a90 6db2707a8003d399680eb6bb92841d75d3d149248b1c25c206bb5147e18da46b ac1b92db118c1783bb55d9b444eb82f8766113d952328283f7efbd30f74e741a c128eb42f0ec09e2bbf574e9fc29a7f8546889ed8717b9dbdd5b2311ec47381d 9bb755e5f4ae04008ad6f90e246c5ee0ad280dcb694e4f6af10bae5d45d1726c 5c23c4fd2b9dfb2fb088283f64f002050490e74955ca29b44fa60cd2af403c0a 92358066c90befa6f8607307b95312d751dda1983242d13f1d263622eef3fd48 114589fb77f51f83b15dcbd686a23c7c2fcdfbc5af01856498a70a80026dd210 e1a398c69505f9af76b74df68aa8701bb5e65ed4a738385b21839525b05cdce9 5d983391bf91371662309cda695cd05a8c62b85da74d69a0bb8394b072635dfd 2860acda6602ed924f8d4984a50e38d5722e7c096f0ac40ad24a17d378530981 6538441988cf3f75b01f1273e6506fc6ecdeaece17e2959186446c3288252783 31838a69d3a4b076288e9d0e8464a7120691b6bca8cbf1686553773db3c3340b1b1839f079393f39bce52f9883b6d358d30ce6787e4c8872e2c62a1f60b3bf04298065c703ee21147bc057638c361e226e639bf7c32be2af10979a67f1009404c3e10358a440bf048c864ace709e790182f05231045b213d3098f9cfb7506b07cf6616dfafa48c328e9ccdf832ac1c9a7f4ff5da010b05e3e7d6a2ed7026f7067535eed6fa6799965216dca6b90dd151a5db9cd1bba9130d16a9abaf3a751c0c165256a05f24a919387d3f3eb12f7472816df3b0b4c7bd440e5228a0a3dc260778ab30ff288b7ebfff8639ab2e18cf418cab1b1ef77215631f5910408a47380cc3ccca9ac0fc9c18687de8fc791f2fedd4f895de4726406fa907bdbe3a778f070c0e4d05365d7b1412309f2fa432de43a03605caaa507a18f2aa70ca5f87e6016a3ca3b5e491dcd60e4783eaf6b9022cd4f392baed9582ba53a69c3f1ed07705aa709e2945f6e4d2663e88c6797067fa141604f2788ed670887dd4bee3f0cd0647e5fe46edb59e1ae1501e4b904259665934f5d1428526ce4cfb5f609d063a07c759a5266fa4beba6feb41ceebce24b90bfc8b0448587a9975a5f2f99659f90e8a7e4f3a6f9493ab34e422c123ef4d1f9e81aaf68fd17372844e46a4d98f390fb40178d43e9aafcbd4e8ceedc1d73d9571b76f972eb2199726f4c513191b14046e2e5e349820fc6a92e149f7b0220b6a42680cf993a709a748641f50e5d5fd008302b1ef4534c39224e8ab8aa8382dca81d4f836b6183defc020ef3a95cc0e0e081ba2d77294619a4e7aaaedec557bec088c78431b6da662ca5d055e02f61b05111b40258b48b9235c4f842c14ede4449d4d1c95f9b21b38e193ee04cdb6b3057c2292e4ed8b87234a9ce4825eb32b42be67b3ce87958a8e726d2b125c233d0ca202aff5b8cce7a615cd0f29aecc27d843c2b4903d06fadbc9e0cded2a4fb80a2f7a7b964d43d51fdca5ca81e1769a351e46b95a4c8b817f33b57d4ae5606c0786db224cda6918877768694fb99a081299b1ae2bb48b918917be2bdd07e22b0553885f9c60f8765fead3d7e056d48431a72c7dd6c33990219e0643d334128b00df65c9faec32f8494d5e4d14670f6ef21ec1d4b8db74225eafd1a89a26256f0efe9d14dcd6ce8904936d3ae3c775a65f8951e76a50107be2cfc7bad78062790e96ef20ac5d225e83bef9d555d6bc252ab0dc08efe86927f79c07c022654d3c03f6b41d1c6f88ba42a4bdcbd5a337742a51395072121b01c362af28853975240172603066de7c621a6150ee3c52461d8e21ae8e56a3752eef9ceb0c897f60742e44acf93513b89703bdb420f3cf738f421d61435f911361c100de73200f498800f34ec0a119954912df0242439c8d00ab10aa595686de048ec46e802c4c6907013c9852792c44ce9ee93b339b01ae9c46058f97e572c0a22f515169553f2ab00fef90c7e6cdbf4b7193a1862864cb4c1d615f039c7a98cf643ce6f0fbf70dd005625f385d686799be5483caa4cafaa2df2691a9e46af85d505df5e2310ce573075536983963cbbf8ceda596fccc194b01d4cacefc0c4a8e7a6592b367489f01036c21a102396b10579f9564e53fcb59cd5c95c0988d30cc833d887838236ede0126bcb9e5b8145d88239d7735732d42294587e0877fb22ac4b608c150d374910f61a064fe4c6998dc42ed445fc6bdd0e6e1b1022b84770c2f02108e5581b11c0375dc1ce2d0ed60fa96de4e305e00bde5b7372fa967a290eec20872909fffb5090df16ac0776af33f1f40384f44b2111749e1508cf8af4798545ef4b40404140ffe134e43b3ce0a7496ff4f8c39a086c9f6955ebfdf3929556f337550aae5ac0f718a4e17e0716a51bc99316d396c2f425165303634691735fda24bfa4c753102a7ac535f2c2ba98de56d3b15e050d967ec8bafa61385d65a7bb3e91b4111cf03e3bfd69245dfa39f648a7d758a8a0e7bfb87714807be389f39d88f0c852a6f0d9699afc36068e63056c64960579314ce2a9cc540086f4e4234ede8b385eb4b0b4d3adb9b85f3e07b6c6a2e6eb625863a4dc27394e31fb9e64de0045fec41c90ba93f7e0a7560e25ed4a535827d700d02654fb41d015b3198d925f489d6b4020527f0a86825036763272ffbdc219ef2c7ec6745ff643992275b35f56bda722e0a1cd74ab457260629afc4e7e41ef21d5a90733794d2df42016eef74180f0e4106d79d0576a8fc2f4374d20c460124a0b9ab550b5e56783828542dff085e0642090f60766225af9092281f26710eaac0679b252b2a80cbe39c6d817bd2e3ac7c0c96d2fca58e74c8c1ea4e85843c0d71597fd670fd22f482d3d9d91aae15d7a30f589bfcf0ba6d6bef0d42cbbfd91f99066d655e273b2ad0d975ca1d14f80d0c083b160f2fda6448f32f27e3c7f9e47c7710821677cd29817157e6bcb1a291a80e88d904b0e080ed5a0f4d598e573a92ab2a84348ecd7a4d51af545ecfb852cc004b941afe055f67dea236f55baa3e4dbb77834c0cacb040190b846da9d78f77021f3a300ea9fef51683ef53fba36505d18ad481e32fb41fa0cec59eef4a96940c00b45f31883dbfbe12ef07e4788763f74a6512acf9856b50ff898fc84e26d80aeb9f04b1cbb7f813be56b8b4cada1bdc788b45d6bcf0196c600bce4edbd21d034740de141cfff32ad27846d334b81f1ed88d7f0b9952b5ad32e0dd0ad2e6ee0150476d689d1a8b1f4aa6cf19763c6722e36c5ec8566e26d7a1b53265aadbc508c1da1c896e5c561222d57cc39bedd2e393c6c8486c7866a41703a936f9a1c50220a28ec6aa09c83ee25bde5f8d00d16b4a34b18d5192ae33990c7f4229d5af0e9ea189c313a4af9a0a723d9b76f290ca7b6e1b4390ea9ba56631300379ac1ea512afb3b27e026b2d64ce80a1633fca0566d62caff2a41f59a1804ed9784a91084e8c4d2c4625850038005191066d4d04a4dc758dd2e0cddefa2095a7894934358d21b54055be07ed14a2ecb263a19c284aad6865b3524850061dd206e6aad80b96714cccb48e4cce88032c4ba60bcda2471c6f62c121d2c7ec92939ff9c57a034482661abf1fff9256e6cb624f2e4c9836bd37d630ac76b38a4a302bda07e400e451a8c7f4d4a82de1eed4497c8945a6e27d2e183f9d59e4b27c0fcaaa45260efecaa9fb1603656fc7e1e17622e0568528dd2d7302f5536a068ea459346da20101f075440213b93843b42c15621567a9392c3fed690ada38eb92442e1f7d4a09f28b6fc7aaa61481bfe5bfef549031015304707d7e55a989b5458007d566670c886aa5c5f44325006601560e039dc9146b76547d0ebacf4087c947eb5200320e70ed693d50f71a09807cdd7b571ca632f86c2165472bec5f4630ef332456310fa523393548d9b84b77727c4d16f9fc4f80c5b4868eb54940b2d1e04e1abadc08dc426fd146a851b92ddad49ac9c26aad074d8364968fb6a944cbbcc64dd0ab0e8e61e3089034ebe6fbbf26b218e41d2039ca4df5466cfe7a1334d1f0da89d0038f6491b7eb63e79e91287d4f6928cc23749ce04e6d0379033803826ca133468512ce3a3d51f5f0247bf5d5bde16578120eb38405b413ed5c3fc623f922716e0b2113c5cee121e93211184d604bd995d9eb51d6b34d002be16f1d361c68728509200eb2017611f8519f344f99f57df01b96b3c1cf286eacc1a0700b5d0c632a055b598b6eae26adf03b2410cac2734ebcac37d798e921c60a6656fcfd6053f30a9d5b5d506ad353f42d42848cffbb12754c59d2590ed341199cd50b62f6591d0ec8aa58a4b3ca2df3aa720c3bf0ed1d26e30cc38df5d9043fd2c1e9a765374304c2b14d5e2d2871bc540774f07f76a7e7edbe6f10f8075bf0db7c1a4f62896f0e2c884d967ec28bcd266f0319659a65e8c0cbfc8c217c5343cddf844f253b3a0195f89e5f78dbe3c0f83b678b9b53ea591eb1a635f9174c1c0bddc9e488860704c31ffa53b757999b78d12b5af6a8bee39022138a31e9273fddc5f845463b290a75bdc0081e7cfd1616a928adf5dfcaca4077eafe28f9969839cf3e96acd83e085e410efb06dee73024dab3ceba1465875d5198013138a31f1492c7415627ad07c20f93d2924a3fd08e5ca497bb4fb8bdb01dc02370834e77fef10ef55028260ef35dbdd11e41c2df259b0bf103d329e0aa7e3052f373345bc2a5b0cb0fd2c90d3b9d820d967cc1ba6a8bae7f0684a3da30378dcf19950d79df7b95789917bd039d1d780231e12cc61f15fe87aebf92e1f0015e308091f99219350bd354367605fd0b4da28ec68789a146e013cdc2866b21d794466df5e15f64aaec29ab1c08093ace4a939c41068556380e0dbc756a4b17b6e6abd7d4666da7f2b01f18e1df089b8d2560b3a7da18f29376374196065e82d9b1eedd1d98efcc2ec9756173b60db9ac373e1648089767eec58fa2c1ffb7977e80bc6597254f50fdb6bb9f8dca08a5a8a98f9bf11141217361a99c43c74253040707eea737adb99eccd20371300f1aae0b80d9fbcc294bf3c356aabd013cac6ae8982156bfb5bcdbe92b6a9964002115d891040810c2564c1ce29a6651c01f38455c7097ecd1f3aeba02e1554702653eba85191ea8ca3ce3ac0e7882bc96ae81292d1e2e00572c69f94dd035130c99c90d408d47e98e9552133d0700ecaab8c5af2846ab796632af3d06ad87c80b7a6052af1b05c5ac6c7be6968b8e78cc8f805e6f20263ce623bef433476b7002fa9cc3dea7212ffbb8b32c2c121800d4aa5ff18c94291f7c2c4e2ec5b0a5e4057a08f92ae7978934aba4d9cc4bf8c28437404b0569e868f5b8488f152b555305d7c9eb4db42a486317fe8c819bc045f20f3afef5982d21925fc47c9451812506e008ca927ba02f912e639b786f98d4b4e7b6833f94a9e8aae6a7f312f55c4e0d198762175eafbec32b97554a6e9cef571580092275357efcb9f9a5a6b1fc9b0c9da71c2efca9b401d66fde90fdbe3ba6e728ceb247f097401baef1bbcd914802181f348dccac437b2ea2d772c139071afa6c1d04d0c6b3d021782a6752db8b071b0736dfab577fc18ae411c348a9fcd57638c984b4dffc47a7c22f9c2db4ee05f048d0fc538756eb49f62c921ec288dde45e33d75f4d89a71216455f35e5df0793fa577d5c410de7ebac8a3477893b40e031898e020d8463f05a2ea61607b600b5f4be0971be4fe7fec8690fcb2324a3b485b5989359668a5bb3906a2114ff0c3aa45ad48ab0ad7fe4c43fa2ea5e67cda1dfd99e8f6b9be8a7ff914f9f50dd0a04a1cb46c3282ace975615bc9248161a7ae8945ad56a97bc2551621b52d87402c662caca3841ff989f6a62b57e5bae2f33e0b9db82a48209b0481ed8d9e773016c6f5b5e097f9e3c20a7b3384f47ec78f7dc987aefb200df249861c8988b09090cd841d964edb3198e19d13fcf8e01e232c0c945ce940d44597af36888ef660290c41addfe21d49ce9fca6deb1fd161c023ee627b6c9722c1740e1cd6e758b0f0c23b55a794501860a3112aac23d76a148751a3222a525518092a1bd3a782207ca8c97864b70cc4420898c08eaf7ab7d28ed0137a00c4c77cd8ab85c64659b0c54200de4f321b6df0f89146ef8b659d8b0b08453a01933ff930370ad62dc5f0ce648f4c3a57de3c413d62238381472654a0adfa4a661cac34b27b1970836e60235a2338eef52e79ba385302d3d230d91bd184aae1c54c49fcd148156c381a20c false -check_ring_signature 1d71504acb517677855d479b4d8ef15af3bdaca5d8ab85a19f38afd8f1c0793e 4de2ef09c9b2951200b9afbdbd4db2f30e50b130200f5aac479b440c882a67f8 31 170f8834134109051b11cdadbdb17fbe7ae3addc6e9ef1469af186c78b818cc4 da6b4ebd9a3b6b43a2d0fe3980f2c77f795fdfff9df25d60c1516d2460797704 4a7882375d82fccf160136c4dfcab7b728cfc36321690e7a458e61090a108988 a302565ce16261d1505b4a3f11fe2cd3c7d2e081d6a702d2b6bb16f1ca037a19 92881d91e5e377cfaf9928f92cbb9c750fbf6493899c0098dc396cd2e7f844e4 f4688418e84d873a0fab03c216bf341b7b0a9dcdeba4856fcb224c2915fb9175 4e967a03eacf61b70ac96800285a2ee675cee2001b153e02397eee54201e4b4c 952e5474b9ab461c8d30376e7245b18b0b1075359f8acaef12784e9a988bb9fa f0bdf7598010b66a4b43b1e5548509ce1310c8e0f00cc425b20ed183d3f648c8 8bcbefd1740245b608b243f5e9a11b9919f2b6ed119c0cb3b41270c89f4e80c9 bc215f17a53c43cf54eff7ccfa858d3964713075a75955b4a0d8b89716a84403 558bbd80075cf9627d3ab12c7d215126ef96893780056ac9610d22eb9c63d41b dcf4e7e9bddfadff0ae4740a1dc31e9127c59761c377a849ba23ac9e611dcda7 80b8a7745fe9649be794a6aa17ef8b78a6060dae6254d6cbbeed51a0e47906b6 06616092c4069d9a6b2ebf3cef46f1eecc53b3874e918b956f00a515d4cd4d51 a363323c2a089a404c696a556184098f53f8af0ff8e520bb0e26326959319892 f9a13a463732640e8fcb595ad96077329589dfbb21472a93900b21fd4dd395da 56b7acf8bfc5eba8d76a2e396373e2f139592309938c428e847a6b4edd5c5e73 6399119e2b22d5584a525cdc5c2d16b9777020629d8ba21b7c56c55632bda2fa 4e2633ff89a2091cf01216c457716276871010f6789c48c9cbb49275a59d0a25 7ce931734ad21fe3aa2d8cb527296e05a42d4d73f26fc7a25116539f0fdabd85 fede3b5918cd8d58e2c2cd88aa4b919180a2d90688e88232637ff72f988a8e7d c25acbd7e16fdbfdbc5b1637c7c34eb803389952fe9c5d389e2143b976fd4681 d8f14fb40337e75b46f13c87ccee596a22ebf79be85f9a026d505c01b1e7fa8f 1440b07070f6673eb772a85a33e782902eaf399cb1c121992db5279923274375 b592e1013d1632490f73df5b286409b8996f0ea1390f976c2b02173326aa9221 7bccc21954a654d6a9e2bb21d42c353565c97da2902b31b697c4694765a81f64 a11eb65f5ccd5586bc8a24edd15fd7a4533db4415c9a677b8479ef26f345f910 7b718b1519f1b0452fce146ba8b48c031ca3d3a41976690904fd2ce64dd2e232 0c0aac719e4ecb0b2de5e96816c07110a6f6a7dba66bd4e97a90a6ad89a462fb 6ea954d5507a3b4733fdcf5e6f59e17b744272ff7897d159a7a26105365fea18 975c17c043ac6ce73b68b1c039703b9474a0fb9093ee164a92f71078864b440cc69f2beb32ca147df54d17b4e9d656b3d92ac0db01e54722fc554de4716bf70054aebd9a1b83ccc91de21234d76c4971791ad0f739c81412bc5f200da4f04206e4ba7cbe2f7e5b05e3fe092be84178e636c533d1041dcd34bf00c0f9179c350949a8ac3d56c964c7e02937aafb913862eb3c0c04d0ae6a30b81e4b5e0c83c00ca46e2347ac15f92c95e7449367fb83a7ca8e83f70fe5e27ff473961606924408208c012d41f9535c7635af474b0eeb6dbe84bfcdffea06dcc935dcd54d10f60544294b9ffdc984da800ab4f9563a323f4e214e9a4a6fc324b89931a52b9bb4059318a64822524d547fdbc9c806283eadc2a9bce6d108f0cc72f4f05da12f8a00563efad17c4ffec638aef5bade68a58f81a61994659bcc6eeb89fff23c2e510bf4aab98d03c8bcf7e51789f6abdb50ae8f47d25d85a38dea01eb9be25ccf3509e5db949912095ae21f63f3d33d1ff26c6422d45a3f0a29928c1477e8c2e77509939857f8dfd579f2fa7dbe8f4b2a0738faacab847d20c9bcdeaaca4e06e6f90bbfff0b2ca9241bd037e07dca4a46eed1ff019eabf6ea7d67da0d7ca360ddc301c0af681ef2e87ded3e4b8e0145f6d0a76d6899e1ac02751b74ef4100479e50086b9951653cbf4fb9fecbdb6aa66b0bbe36d13a9f3aaff781933af51cdb3e8a074a3b67630783ab7f512e4ec44487a3b431a510bdefd8347162cb48b0382ad15c2e1d85e96f11e0dd993e2a3290fc68effa899b9c4da33a25fcba3ba23c982f054b2fbc6f2ff01c19f9ed4516ccb84b8c11d2acb55c5a05a6a11870bfc151430edbeba26840939090fd52adc54a57e655c467bf1dcf3347fff3e0d91ce0ce52007cb575530679297ce5b2f90a75b13ed6ff7ffb69292d58ae79f0e8c6d9de390bbd3f05d1eb727b4353251476967536513539fb5016a1be2cc92db645da09440c526c77a2f37fc1992e3c4056d1340f8ae5aa4da65ec006bc863f43e70ab9e20e69a3a792413f1517823e756fe7d5479cc81889e8c0118e982ab5f34413944f039884642be9bd7cecfdcd9b272dc253bf4a631d8db2e328ac5161b630eadd930eb63d04ba764fb61d055d759c78b69a3ce0fe534ebd1670e09653b00a6939140a0c5658c2282586918715445a07b3a5bfc1b5b69a93e33b0d7d9bd200fe0be506f793d987229bfa5a02c053456093f0d434117b7666dfa1dfe4d664eb59a5c800599f1ac7eb2f0bc5cd613c9b0a5cad3e60ec56ebad37f53fbbbc918f09ae83016769524dd2a696df55a7fa37bc0705e94340312c6cbc4f804b6dd4960909e10a7a0db652d6679975d4501e11fff179f37ec0b51ec8001dd6a42188a041bd7a0ee42cf61d57f3053b1ed0cb3a0b9b378a100c42484a88338a3285a972c40a5400c0365142209406b77a767bc4e5eed362c0f484b492460a5ea41daf044e6a530811f61419779501ab02c6c42c71f87f3d5b0a6bf821a0d5c689177d94dbb9c5090b51cb2dda8fb33ac193c986c1592ab25b09ed7f095479855db1d24083c4290ccb7091e515c6477b8a3779e2483d79f999994fa8c245a5f37d5fda402bac1107a7f9c711f30f6310da3ef237846dd8b5076c28dc2af4a8110d8d2d40489fd0002b966e04c9c013a4903df045f58fa36eac33b96ae10290fc9c3f8b1176bb5004245a110505a52cb00e6a8d95a7194084a56f1514fac922a29aab310c7011e5d5140a80cc14588c5878f7144167392cd4fe72d5152e9d8025b77c77b446140c0813e1401d33d668aca2088a3847c5d51a60315973f0e82533f04049f5e46fbc0dd116514818b2731370976929aa541b7c8d4548fd9cdcced709bf9088203fd00bfe18d8e3e1defac59e2e3c29ecb14400b13a169b4f6dd88446ded323419c6d04568e37a59219074d0e066e000028276ef2de8dc7e2858d40966764db895db102e519b5a7781cf0852e3357dce7d3531edff938d1280fcda285c3bff8a277340b72f05cd811479e406e6ee4e0312e8809b607d3e7ca6ef6dd1f75c8727d42470ef9f1a2f969bf2ede60d323484b6860594855b7e1618f17ff9858333cf9b5800f0c329699a439a93660b14ce55de93b98c9c295bef20dc821cae7d58be046f70ca07c216b3331057c17421668736210fee876659c987cb8176b7aa67f2105f90b090c58d116362b3cbe6fe4d06f0c7e341642eab140b558c2116e7258d589030ff3f1dfbfaf6aa9100c72c436a5e5108073ca944654f1a75813495a6e4a54c4071a2acdd3b36881f4e6422578044ca3cefb3ac20c076ec6b1c5c32c9e1e2db70e08ed7c5e064c8d2f833e6d36c21cb2c0223f0a8dc63a579a65fc1958ab4bb40c55299f77e2b65fa6dabf973c5cd91a146766356f59aab1c731fecb8a4610d00f8c68688962984c91a393413a5bd7421e4c135360927c0c321700ee13d7241509e0f449105143320fb7aa3d76a3dfd177ddfbf852a3c97826c79f148e422235053ab338ece9b9a82881e114580b7f6e2b2a4504ae4ea0593c049c78175b95640c09f2202090d69af16d158d0cf52e96a1ee2843fc284ebb85448620414d75a2006226db2cc8e5e93f3404d624c760b477bc2c27e2cb9205310a692d2888da4a06776108d062f29271c0e204d786ebd5c02a2df5f7a9cb8bd159d0ec75b7382d009762aeae0ddf6eb8a5737cf95374e7bf1698d62b5d4ad6438d2c078c11c82906db4f6eada941e8e761e0b2f029a218792f9702b2b93f9956cd585fbdb061c603 false -check_ring_signature 43d3d0925c083611956d920986177f6952efab33ff1440002b9be5efcea8b1f4 823a47fcf711a997496cdcef7695ba8806d84b7b4b86866eb6d89cecad4814c3 1 7d02e4d3c711987a2f96894b992b5407f4e830e8f2703da21a442ff38a4c2fd1 b2fcf6fb1a9b47d0b208ebd22062d7a1c488b7ce78f82c2ea9bb4bfa995b7103fc013448fcefba5260cdf895f63d36921b0df6b099464195ef9313a9e5038b0c true -check_ring_signature 919eade66c95045f782876cd1f9569c1de331c44576693778b79b3289b695110 f88bd625c4a9b3bcaec8d722b351bb25ad4d4fa83c0624ae055495001df7a1c6 1 087759f40c915ad0a1699fd72435f39e00234d50aa43436003298f78095c5236 ffb05f4b7f5491f4098f0e56b0f82842b54af37bba0ee9a94b88aa1527308af703e428a13f7bca095b6544844651bb71699a4033cda45bb6ad70b427c7508ff2 false -check_ring_signature 2deb15d34aaebb7288d20f145786bc390e43715766510eab72f8cf41e60abf56 94c8984db985b49ce9170f73dacf9c54166fb767a6af610ed8217aa93dfc00e4 1 f3afe621273ffa7d863612188e3cf9250e12881ecd3b7cf5bf6d3935d205d29f 4c0f17f8a02a1afce5c1bda7febb2b7c1b020e8e7e451f9e5abf44ed90604f08f576cbd0808783685546b0753cbcd280024305ded3caa5d259b6dd617964a901 false -check_ring_signature bfe4f73aeb5127eda3859d92f35c1bdc9a45c00fedd53c574e67937030972f57 2651d1d7d05d03040e6d5bbe3c14b980995d1f1ffcfd485768a6fadaeba97f9c 7 1f9d83074a850413e4de0ecb6f0581865854f845203e320c0e7d054cd5425fc5 5856da93971cecb6bb758c123acd4382534659a239adf8a691a3059963844eaf 01098859827ee64fc26a0a406b598d0a85b4fcca7768ba8c2064a7712ae4321b 4e9f87315add6ead52777cb077195aa4b983bb7f9e205f26fe8adc0577776d31 739215ff791dcdb74aff1107044d9c8a1ffcf733106373b3f2b300199c81e3e3 bc694f04747cbda2a0d0c93902f9efeefac74155b9f9ca23c87462c49ac97cf9 468906ee6180fcefe53d1a3f90f44b10afc8ea6703fbbd09fb78d817befffb71 2870edc75470912d60a825c49954efcd2ad132069263008c86d505dfd21b3e0cb8a187c4de1557d63ff34f523d5eed6b7caa9868c4f679e91324de111285a404b29441da0b05b77b69df528d320f0fea25cf1c0e136f867c293cc0f18037d6054a7a4b0e4684d2f1a3982c733cb5e16c576e6354ee54ec8e6ef882ea5669bf0ca462b869640f15f8595bcaa411e74b4513dfcd5b562c48671118d740f58553079ab1959871f20862a3c200a07ab62d541df7fc1d4ea8efc1fbf6e419ffc2400a92502004476cf583143ecc0a2ad762255d6cc12367703f7319414fa2142e480bc00f9e76f6e92ab9ea942431058f0e684d1439612348a0b7cb119225b54f640970c9582d2eddd8c699a7f43c891d119f4601b12fe80df4c8a3136a7301f56c0e5751a1ed6af45e2b346da0eccefb0000b078edcee8ac5276c92834aa4627cf04095ce5cfd166e4bb6145afcf45a3496a218dc471ccb52c8a70e1ecdbc188ec08ce44427fb7fe6b0fb4975bbec5d71fe67b5a48539ef9921618151c84c35a3a0bb239f89772f9e09626689d56edef502b0ae8d7666a387dbc13fcf3c2a5ddbd025f3d5f0b6e8aeb8d4de0e2ba919a24002634393cb63c5f3e914f96b289d38901 true -check_ring_signature 69f9a24f007fa94e34f22fc71736c68ca5dcf3a358d2cf3ef351a87b7dd398b1 09bfa9493d86f9dd6c03a8d45c7d73674752133863bf6afca54f2a1dd5dff442 17 de9495a1bfbd935e35e9b1a836de68ce3d9c7de88416c58899976233a01e1b7a 6fda225fc95dde47c1422fe34778e3b4347bcf0ac4b5d2a84476d3c535c32be2 c66d6783662fa58920fd2c3c8f69dc50c0ed2f25734d8cf425825746ee0ea2cf 20ca89626a35abbf46d0626c486a8bf0ce818ca885f825f34599d3a6ef19b82b 1fe4fc234ca2f17736eb87b43a3414288d118883be74375a1f50fa3d6291bf2c a5bc59ae3a31aac23c0b47a87478cd841a4760ce9e6f4d98c238d7cbc43e97a7 c80bc47c468fcff65197672dfc1aa4064e0b1d610d1e68586994d1ca2ee23adf b5445de3bc381fd68ddda94879921aeada7b411ffc5ba4faef857a10fdaa2cf5 26cec6a28a6e1dde1f1e744fe7940e63df9df72283bc5cd820ed59ba35d23aa1 9355e0bf639f4bfd1ee49ac4df61bae2b2953815fbb1c7792eb219d318857f88 4bc3834c923a1c3ec5301974367c8ff6379666167a79b971056f3b202ec2adfd 06ff9a88d81cceb1de2b774776545da0f69732e13bc66f492ee1498197e71cba 26ffd5f74e1872f0d536d29a8ee9aa052c9e44edd365171aeda6419c53f4185a 6138958bbdb3fc1aeac8b5fcedac6d1aeb73fe0052976ffa57d621dc4c9c5b66 d6414f8c945052dfad9de522b7527aa2b25177f40c6910ff617ed8becd6ddacf 88c4d87c388c18c248b78adc25b224eb73bf526d69eb80fbead6cf6569b15bc8 c60a06f80d82cd4d6f858eb39b2a929023ed8d51c6e525eed3c9ab7caec81191 2b3ecb0df027a3039b1e42b4fba27c4c3934450d215d03dd489aa1992e39f20f3a7ead6c94e789bbfd00f8f3c3553e37e91057f34ba534110154311a90d5ef0b0725cdf3ea7eb17070b19d79cf877d7224a0ac3860a670058cbb6c6937f7d0038616c5a22723d7f71d6a3d478e7de89db12d3ac80d613abaac0f1a255018080802d7199f9019a906f85db97ba8195ba9b4036543e34d89a58db2a83b053e3d0a9cc7657e03527756aab3d8f022766d750eea37b89ecf99b8ed5a97cc79d64e0727b30ad2502794bcc0cb23e19b34585a2517cec46707c89134f5b9a510ccef0e6b4750a79f93a29f4150d1765a1f3f8786f769eb3e423a65dc6b068e906c2604ae34112a0a566da9f62deefb746a86f408eca1deeda23d8849b12c282af57708eed8fe06712b4d595bed9beb8631d5d625c5b59f0dfdc39e1ad30b62eaf53c05cf20906273a011703f3145ff96b6b6cab50f285f1e341b5390866b3b6c3b7c012241526478d57ac5b4213e4e43d12e54db5c654058ee011f49c216ca32c3b805255cd0bc2b6f4f46a0f849f38a408b8f8a41aa617e7a0fb58ce701f6b9770903dbff99682247760b3e784c4cfc94db313ea238bbb8e2308dfb53a86d21cc13026e23c01d525f070526a548c40cb33ee45d7e153ae685cbd9b6520c80a06bc10c06f59c1bedbed0a2b5468c9d62e048b5635da8f3b3402b8fb39eb1e706af02095e96767ce4af95ffe4618529175a7e0a261f9fb8ea227ebb8bfdc9516cb8c90d146782f007e2e96e76cb0b84d94d140c434dcbb4da3aad3723246298f9c21502611723f57aca60ff54c14c502bbb7d42f327738e1cae4e3ea0a24daeb6d42604bd7058dee616903746abd95bcff2b059308cd4f6b4852474efc0f3bae96d43089ba7c8bd9ae50c4459edcfde423dd85de249696481994dc25f79ed2c77b24a0946cd9ccea63e8789efaa87da65b6f87131dae39b0166cd2464b618b6a021d30ab4dd826de80f60dcb3900f7e08a06e61ebdef723cfe8e1cf27d1d92948df500500c437768b5a59c4ee481d173093db1b2e3188556e95021aa169a511afe88e0c810f9a4658cab7a039472218ba4fe64a3116cc79bfd95e0b61806dd4e8a305074344c4fce2035b805da8872ce264335da56b11a7aa8c967bdfc8e53df015b00320074726a94ca0b838865fb0e4a8ab8b5311b67e9e824f1abc712fa217b15304f97e5cd42e238f4629b613bccd7f58c4b98ed42e7df3c849da900d3a9718ce0574e1c149abe9c6270e3c19672df0f99f70d3d9dbe3c6b52869b661924040b40869c97c75de23eb36f601d1d9282a0c88f0ba56a386f12f183ba794f04fe7d001832885ae28d87704214cd59e93b0ac9eebf6655acfe45becf97b73efa72abc0aa51ffb89dabc05dcb919cc600d9e1bfc1116ef2551f91a3a0cf4fac6aaad4200d2f304a5466c1d62c23ccd6d4de66342c03cb26dd96c22076f7adc3b013918045b4e2bf48dd3f8b89b4473115067e8e954d50ee5926b654b29344526d49f140a false -check_ring_signature 1542f56231765529ce0aca8f179c7f56e795caa2263a5fe1266ea459e2e51e59 68d106a4ad7df34cc677a679b644e79d8ca7ba3a8852ce6e68989f453294cc5c 1 21a006e2ce1a7af108c9ce448ce2cc8d2ca4b421f53917cb1f948ab7eb85a6ee 6416e28720671f82d6c96afa8e09ca6dc65f8dd2f969cc40e8e9a5e5b08f6603ff1fb8a68417528cdfc5a62c4609413b9c0b366522e88449aca684945a992704 true -check_ring_signature 919accee40a9f14947bb465546ed9c827462b6ebc7780b8071b37ba7604809dd c4e88dce81546128c28039efda4f879a3dcc4127cbcddff7f9fd53e6af8d55f0 1 95f4988f8e23f12b2b31aad728b74a334681a19eeeb71a46f6046c173cb71bdc cfbdac5122a44a1076d331bc35f07a3b7c6f721ca67201a0fd958f307c4efbf6c30915e0ac3132630a4dc34bc9b69479b9c7c97a05fee297fbbfd74ecfac3608 false -check_ring_signature 54dd1f031f1edc8d95bd7eda75030ab57703c52226af37aca2066a1b5d6a075b d47061ed214d80e5f886e4c5a0f42e95a64bd8c34388e99bf4da114471845415 2 77f432a285a7a9883fbc2c05b807748dff572c81b16c84ba87b95ee28a951e42 8ef6664ddb4403a8e93e655dccf51b08d7cb9ba02fa16164ba0a8bce3b85d226 3f479a64086ed59961fcf77a55c6d3ec44bf642d5f0c2e10f925a200751a660e194a71e90620aef8ddb87274135d7b5259d56b60bbc299215eb6cafe23ad2908f77f9e38aeaf9e6c92454311492c52a10c2c409eb99a943d1bcf007517497e030d10d94c9c0af94a4d4819ffb79adeb07b05ec797597deaa969ad0c062dbea09 false -check_ring_signature 39b6a0ccd41ace9ace5eb82543ab211ab76a8921bf8f184248eb3c82776aaf9c e3418acdef44d51ea6ea7a9f1fa10cbb284ed1e71cefbcb140e26bf85f73290e 3 2ea6cead4bbd022f5a5e7584d9171adeb7eda72366e5ff4e7daa82632e7b0d66 e02c3bf733f14864d3c08f4802b74236af9a69d0e74760602b5345add4d985ce 8c481b7e272bc1365e8beb9e5fc0bb9bb24a472a1f7d1df4d61a8e93cbf6ed5d 71864f6d3d19781afdbd0bb1a9877735ea51926389c0fe6ff7663bd53feccc0b67567ed776d61893fb56394b9b03ceb12d2c4414457d8c6c067d535af6a89b000922cdc4ba5d46ffa1b55d839a958d0e41b9c43ea09a070e02bd1c6a10b08f096b5090f6e7e5df206e0054a408a4c3755b203aae546b2fbd2fd150e57bf6b906a758645c150118aad857c0a3133ccfc7b51841be815b46b680f5c100fead1b07ae3c46b8d3da2a17341783605b72d7bc55bc8a3926c4002e7d296d926760ba0e true -check_ring_signature 10e09e2f2643dc874673410fdacd9f18235deb69c160aa69be736f2318978cd3 b951a3f8699826767a25193eedb3a0f08777f223fff3683572f309c0a6b650cf 126 3eb0be40f3ad8cb954d027312f9b4a5072e0aecd0d7114a921dcad2241b2a0f6 c2f8544228bb9e3823f87f2d948a084bf68b5396194535dc84d1f73b9101e0dd d31d9845ea0404a42a5915e085a5d0dace0b8dfa9950396a09a696b87a211583 7059459080b82cf4c2e7329255598d7383a700c20dfa2ef3a827db904a737567 656bb0c1f47b0e401cc74480e61b92898410c70c74798147e9b776eeeaae85c9 b23633cbf85088fbbfc5e1539e6858b4f8fbe941d2e207030fea6461255b668c d92a6bf84392392a328327326f0724faaa7ce5ef9e38f402016f0ad11a39b441 547c34da9161f86fe8950ceb961bbff323c6bc4a29b6591501c8fe31921d1ea8 99d06774ecc2fc89de907596a0e953d72e48fc1fe1f40cbdc3ca9573ab9d8c93 273d4b9249d79e9ff6c17614845a5bc6cc03d4b625292265b06e31c1aabc3606 e5de06ccdf15d9465d4fdf36e38e2f45c5b3d07a5af8a3e54b28d125a9d92f14 ab2ca338c54f6b9131b840f309784364454d2d169985f7f92f6290de7e34535a dd9d709e95e271ded59f16558024f411908f10ba6f5ef604306a551620de57b5 87d8194c67e5c3c8e5340084d43e76aa201056b797600d43fc9adf4b97011014 2a13921dbd7d26ddde1c609fd528a195e65ba41c627700e273c751e6f25a8a07 16bcfee6ae898a903e7dc182ad950af6b18c1ce709ab7d7042f1184480fdc7dc a212c0b130a5c7a1fd8103eba7638887e8e0565a51d1669fac93a4e7e29d7eb7 a03cd2a3b6582caaed66cb773cf7b3bc1e14b52c80440c0a610e3b9cc03270cd 49dd576ffa372890b1a88ddfaff491496b41a2e662990aa3c85be2419554c21f 8f237fe85ed33d0314b9e65093d9ff0a687f16724f7f10f76e8d76eb1e92e8ed 7e859616a01728f19e5612167be6f7d6feeb587021c336100eee22aa8b58352f 4a1b8485e1fed1a7b906ee3ea15876145231d2516239f123af3f281df36c4745 d5c150c8c2a64d14a188ae07d140ba251d3295baf06f4c7ebba5a7a768f92594 87871b3b826bf56444d8820ff66ead5cae297ea2273253d062d20d01944c8da3 299c462d49267de22a63dbc7f64f6aad0a98a800f2bb5c53fd0024233e5dda62 a6dd79bb4c0ebbf89c544c245bdb9f1e2e2139fe1ee0542cb15258497ed74e0b 91895c4faa8103dd648a3ee25f8f93e2e31a74809c961b06f507c741d18b6c5c f3a54e9af91c314388f1255e28288e53bb1bb222dcb8cb6ebc64583dd50c273b 4a39d3e963bdca4974549f49c9f80fdc6d16a673a230efd629c68c9162a9a707 66beb78ee385f9b5681ba7bb4a880549f246aa59254972e756101d21b25a91de 58efe78503e72c93751376e167c797adb682d8ed1befe1991c9c13ffe5c97685 1244bb05114d3d79180f44584ced14fd9f8d3df890ebc8599479e9c887c4454e 7ca55346aa9c86e71a8dd43d58adc573ead91b1b9fe652614350be02debc510a f2afc6829f6dc946482c96d44daa5ded3fd6a4151329cfb074e27307fb9063c2 7e4ee01621521d5220daef30eb79519d4ec728955e66f6d89e6371126cbf6b55 94692ce3fc55adaef894cf3981d9c89b054facf875c93f3dfb34a6155e4cd318 5884fa2de477ecf760747dfb038502f03ddcac6c43fdab5d4f4001dddabdaa26 d5b0f9d69664e85ddeb65c2e459742f29b6c45030466c0c0d5b9bbf869280e95 02a4a195de258c094bd2ec8a0f54b7f1dcf30c7409715a5075bb48c35c215202 85f2418cade92b9aa2376d149fbea01457fa5133248dbb66f5ca4361486ee700 69944994257b6febe5d4792c554a125081448f901018cf00ce0063e82af86ffb 2b63ae42de5b4809913a8b143e8edc693d0457a60141dc9526f549d2db4dc770 17f30bb1feaaba0d0d905df8e68998612c43b52c44b8ef7baa28d2bd3cef9fa6 960e5875c1e2ddea82d791c5e779153e8b2404f13ba935c32bfa38a44fa3236b 899a493d8fe9ec25adc69567d80dcb932caaa8ddb387624c57009ea7f530ea03 fc8117a6f724de8f4b4493b7692673c2bbe21addf0f4f3fd9f37ad0fc281cce4 b00e8dfa35dfea11721c8bc33fcacf7476fc4f45bc977f693b9e5eba4c58ed4d 70978b2a6563b89df18c33db5f9334fa8170fb8f0b8ab913d52e8aef54befba3 01e0e4eeb54754e865c0654b78f760e28afb619139f09473cdceef9bc44b6dba 3c9317d7d0f492321bf7870222c815aa4e8e5633b773d4c34cc17b24810fc68c f05cd0a68816bc7a19c49b31aa30cb086037ab9fa0f39a624fc5c0ac316287bf b75fb8532f6af940002a3c68d3a6f28868b3fe12a4c7f6e10b8554d8029b5713 55979641db11ad0940b878bc6bd2862ea2c3334b1c62701482707df38cb75ab8 b9d18a84883dae3bb9a18a73f9ee13b85f2904d5f104e8f89034b4acb633fcaa c24f16b363bf2ed8d8dc84932fc554eebff25a0d6e4775f3234a84bf62ad8712 959486b2aca40247339483568dfcd1ccd082eb38f7a3fb5be04643359c38badc 453dfab301f500f4379c1cae17669a8970a52520602d7da0b3a3ce4b021adbdc 583ed84a53071c41ff7f93fc6e84c026671e6b4a043fe2fa155e16b5d35526b0 b2b536bce67b58f038bbeed8341cc7040b620bf7da87056c23d18bd70fefdc72 1964aa53cd2c53143f5e5e75d0fa5d77a6ce13e10078bf195a6959a2b71f4108 fbc0fd057f74ae19eda9d047a7e9f7f252e734c203e2441f41e5247f1e52bff2 c3ac77fb2e06aa846a8ad6b3c73de27fdf6c991d956d6cf0dac774077879889d ef6b0c64755ee8768177444b63eb1a89a5e7c3215ae070d7f025a6d813c4e989 6f79f03e65484fd94b405f2e5d70fe1b996753c304a93152230e00c51427cdcd 236bd3fdee07139c0314d1d19b307968dbecec9455e4fe81046fea47903f8e00 a5323c5277fbf10017d8cf7c8f8635ca2294efde2dbef872c087fc721f7ef35a 011560996e47feda7a2e5c9f9f3ec3f47b24035f585b02c87a0f9b95bd6d58e5 c86408329da5f2b428c2a1c2d9828523f7770ed545be4eb451e03f33f16b8262 a7c43e1ad7c4a9ff9aa71789eb1cad51574eea6e5eb16f89f003e8b791ba23c8 d373f10470cddfb7f87e9398bdb28306f3d846f81b292ef31be8e679b72854d6 30dc5b666e14abafa2534a4c02676ee69bf79fa1e04ea05592f62ced6e558f94 dda6ee84b2966d64e969292382014a40a7590af3fa7ea7ec3e6c87201ccc4fa1 f190ef69606dd3f02a107c67efa455d38f16d17fe3dbb71625737e14708a8973 b3f5ea0301259ee667e442cf310bf8e0b42deba5320332327b9af19a05fa3f66 863903cb527220921c9ac6b8801dbe692903738b2ddd19f12b2ffa4f93a01fdc ad0bfac8eb05e23858b195bdc9bdf0953d70e33bfe1db4671853e2523d7178e2 04552f28767ec6cb44a2ccd7b335e85e0165773d4a9f6f69c5367b252e937eaf d83161d392faf06e4d24db28efe709a29585ca96e1fcca31eec997bc6b366ed4 fa33cbfa15dcc437a1c963d0173354fd4d18e2db0f8c2756aa95a02a3bbdd244 9d3e261089049d58f922585fb6b17fee1a541a4d677249ac1693e50c6a1ae6e4 5069498bb2935a8384018aa104c2a9045a5b81928aec4802a1a949d417a77cab 9f535b917cc20914259a5b2c26fd30e8f6d867f1cf2633b8d2c851cb4b82b16f cb8a77dc29d82395fa0d5cefd3094935940ca5d3ea7eab019755439d31c9db90 ea2bead70323e183ee330968ddf531a88cc84a2365808cc5560753f2a653710e 07374e7991e6b7f4ef781c230693ef3ec0b349de5bb3939200a35071048de265 82346f8473c9a0dd1f978a072b35060c2d91f4faa5c97ed4347c92baa02e5aca 2af4495d7af6d50d6343ae22c402aa63e54075f3d4a5e1360abe7a4828717833 409786af62b04c59460f33c63e1c481f2a98ffda45426bf3effe48353c24dcbc 12b0ba7809f79da1b3bab2ebc21b98fa0ba597931f088d2d3d2230add5db6091 e86e07543b10a9e637dc7942a6449ead4fbc45a302872749c15d18c59982d2e4 31d2b2542475ee8cb1054ad68a6248b3c96542afa7d9aea12f1ef6edd9d9dc8f 70062410bfd6605b675b115583d4ea87c2cc9b7d5acc9f0e2f0c764426477df6 43332b943ae8949c3555b1fa13122226f22c2895f23d76b06419c7ae2a0c2c46 75c49ed2e7f11984843263db45ef1494f1d7a6b835ce7ffd18d2df371dcfa52f e30dda1edad23aacd3ca369baddd0ed737a74bf8572f60d0c5c5aa95d119d150 ebed68df53568458e4fbca0c5acdaf1d6111974f4cd5f3ea1e8ccac608b4b2a1 6976d0a8f3cf88f81858405e4358fcdcdb1193fcd02c6a68a0bd314c6e8ca18c cb396525ca1259afbb7b8cd1b5d76fa6402dd73277eb0a6ca9b87ea53098ebcc c70343b2c2cec70428254e3a05d1732524443c0ca9ec2fc3d11b92873162b89c 8d1529d1bbdbc11d6650e36b5d3e81ee40bf1b0c876835b1b3da0fc756b18223 7b4ece453a140d722a848ba8675c7ec70867585e7b9b983d2c2ede092093af73 5dc2ba8911fd4bded378d8a2aa049b956bc34eab46717fadfd4ceb3e07a617ff 2b2528265bab9c8c7b4993d8b817edd2963112b391752c1a47af7810588e3dec 5c5b848c96f41ae0886aad9237ff9839d5ad723d6856e521cfe77117a076bb98 5b39845ad8f7aff139fb11f43cae4f818c59d197e5ddfe3aa178c1219452c077 da59d3cd160dc4505c6d069821c27d0207acaf8ec1d4acb799900fd506ffe50b 89104850838b618ca0046bbf0461066e5328a13b8ebea71dbbf90b9cfb4dd8f3 84018cfd702781b4d90f6776370176e8952ddf62c613de587c22185d4c3fb183 e0dc281715869ca2bb2abec85bc410935a2ef01d69cb4a72a8de220fe9450d82 d0eaadcf78aa1af1473813a7be767ecdf0de718aab7ddd3f1916cd32aa24c698 37400c2da0a552923cb5aaa41df14909a40e7e7584a20f4c5a25d6f84ef36505 9f22589fd24186fc27e0bf4e63e7cdbdaaa54217fcf3e75109b2920aa9600ad5 6979cc394aa06967a00f2ce0fe2597048e1b0b436e61a07c29e0c7a95a54023a d2adf4a1ba8b0021f82eb55e11d2b3b4d6a6921ddf5e7e121a508b9cf13c2119 1c11612b7d3cbc471e31886361a0fd92dc7ef69011336d9c842c8896568acaee 2cf009c2f9935fda652acc29f172b30444f1e16fc47ad53183d8c736ed55666a 4d9924eb766ee454b48740afeed733591b871503032005426b18a20b3d6cd8e2 bef2c50b4077606a8f13544af49bee01503ef33c0250381b103150a7db786b6b eb70991f58587ac03567508b5efa4c900acb162598999a06ef7cb9d384b99834 db0aa3cc82cbdcb42d99c2d70b8391eff877db50de98c15bbb24a4ec73116d1b 49124cf15580e56b7b72e9b8072d5ed675e4dbf088a4f7b43a33a42c98e15e10 cb3ce0aa40b4e0cc4c7b417c035b366b4b3b1c5fb40d6f3f301fc9d89826e71d 60d9e42df599f981f51a9f34382f69c6cd26a7bebe8eae35d45fcd71237ca6be 2b7c162793529b64f5eae00034e6a9d4271ae548ea6ad0c67363258975e99e67 6a4109c88744a9204588476f862c5920f258df7338d0b66c1078a0b8fb0f3fbe 682352296b5c5da29787aaf2cb6c3c866db3df2f4126bd3be488ecbeabc207db 7cb3a48e5894bcdc69b88db6b6642f7d68a5e3624c1550cf2f5f3c64bda2b1049f5569e79ac92374717e8a9a3e9af3e9a1fd3a2e14215fc90fcfc6109aa02d05d4103530147c894255c62dce9c3e21871f1c3552d9f031fd1070cf9beedfc709a9dd8e4fc8f1c38cb95ad8b3d2e664678db78dbf68a6c50b98bbc14c5fde1201448d923c21bf13e9c3a3b7adc7839c4392ac870f10d4bb7685e8ac059cf3f8036a5bd6094cae4cf0a7a7a409ad937c1da77f197ceb90b7bf901eaca372241e07aa39a698bd66c050cfae2eee4d9a14b0aaa5ac785ce52af49a1c91fcdf469b047501da970c3c71757e4d0a8916b0f892bce8e563b4f790cf0cbdf876b2246e0a9f9bfcf2e9c5bd5f339e2d4ca0a140b8db2dd11d05ddba12e0ce91b2fe98230ae507c17be733ee0afad9b4f72eeedaf1a4dc9f83c6d3363a17b46d744b839d03f966cd03ee2a69d4328c88a96b531a6d72949e6ff5dfc782ecc24b08c094c607665128a6345e676fb8a6b3037ea9ce3780947e9616eddacc6cb82d05da01ef0def1b81d7e42b348ebbe87bc377fcc265dfdf1d2e72484a2580a6fe43de9adf028693891b8e721be33cbcbc25b8dd005a4aaf326c1f3809c3819f8981dba1a40e969e14e2519ceee0e5e92da1f8c97f514e723ccfd36acf9998aa71351fe142090de8efd7beeab00d9f7981f0fe09c5cd63ecec30527c86c9fc1dc0c614ea7c0dd5b3f4406cfbf46376b8a864ca04ffc919ef8a0a19d9526571e14047698c7d0bd634d23a7fae578f85d686e869fb618526968de77f0dcf1cf2721f7e29534b09caa6ba78c7e28f9f137161b51541c13881d2d34659a524fa05c9026497c27c0e485ca250d2846620eb858b35d8f21c26f0c0b0bf77f74b6adbbfbc74298d5501cc856b77884306ac08c291836eaa6fc14d38278779cc617ce95689831ba2800f0cb77241046a162a5cd2cee35a5bef8f84fdb58d47e740fa149837d41819b8088bb009b5f9819cbaebe4054f26e9aae4f7ef51b131b32a9eb1bb404bef214b0541c7d25180306a169c6b31d80f2f1e1412bda2561bb839aa35ccf0fd638edc006b690a116939c46bec8028d3f847cc410e38ec4450150c2600be3b1cb8f3e80bb7c46bc2fefb233f693bfc6424373db6f6c4bacf2673fd39c2091f3437fc0106e62011044e522d9cc34fd1d9ff106b4a54efb5fc9a578900cca4c69a44e18b060f0e4bda71a1d035b4a3a1b64e0bdc347b3af8ac897d06a9a5984796843ce806f3da930459c235a2c66e0abf2a14e3f6e1bdb0b5a8486c53c22cdd8d4387c2089c0a59fc913f733d9c8bf8236ddb92c996d60ca87ad1f5652ccc7ffeebfefa0d038077976d5bdba5dead7b59820e3f2f0a94ed010bd6234731377f8656cb4503907d3f752462263ec2707e39807fdabbdc9549ecd71451670085d201700bdd00f89341a864ab71ef2705cbfe50aa0f10946e776bb63291e4238a06b1dd6ead02ac7ab325e83eed0cf78b4cd5d879df178ebe743f4696ccdb7ef4617ae686bb036dbba9564241d026d88a6e7be366232448c8e2cbe3921620422e39cbab4e580134ca8f427f635928708d0743d2a1f82e68c47669f2ed27648125140e342d760ad7ca9771e882131c1bf4490b67348a329c0ba8cc9ed81fc97082ddaedd3fef080f634cea884813b5bf22e19acd3b436c3f7757807c045bb9180b6e6102a89c0758565a94ad098b788afef4e9d5f8a9e3c547d4d031ae0fc748eb1f7e22a299010c375f92bba710b82b888e159c01fb82e56cecbaa75bf2fa723f1c1affb63701da19b5dfc6381434689856fef3df8fdc9a3620a2e2d1b7d62878581fc0d24d021473cd9165af393dda757c3e1336d29c011a9678f87dbb22606ba8f599c8400005dc2a868bfeaee7a5ae98464e7bd2f8ec579ee8ccd2d3e17cf1396488c5600ed3b4020ece6745a847b799f6fa3a6f820645b36b1ec787bfb9cf0ceaec5d9c0bc59b1de3134a9f0e9b5eb34fe8c6b8748745f8c77d638774b30355bf75a2d308829213002d499ca0f51aa8d3d3eb3794651b2c3030e50c03ee13a20fb2e60302b3e9328793003d755bfb8dad65f75952bd1c60076e715351d1d6723e2e0ffb07826a0d0fe7b64fe54e4a5b6a094d55f4a22aa051923e344fda934c47742f8908bb9cd45992eef1de60946fcc819f4f0aa7f0bbda44f7c9c3a9f667b5eddcdd088367ec7f758c9e8cef41e0f8cacfdb41d53c63ce79e0821198d1b7335f171f001d485afc6888641a7f98dbe332564731b19afefbe839c4d1ba559a08e7e3ad0ec71ead31c51828a3cc00411a9897bccb52449d654b0be6e1ed9a8df146d5c90cfb113301c4f3cf712780b494e4d06b47fae3ccb6ce19652358d0f7a3ed576a09f46b6ba7e0538070dd5b99acf6562ad44ed15520e2bdaa0f9405a1576f393e068792cfd5062e4f6238ae9c95d09b04670390fd810156c9da32f8ec28c169ac0e0447e247e6144bda00b4c0096012d772d11d241845d7e08aa05e5b7b83a1f403667e36172c440a87abe798ed5402ad5bd6f65cbe3b490cf79f3e04315a72d1010517899b3231efebc3fee8ff9815e19ad10169f7ee740df5c0b0add6b78f3005af18ff497bf4533bed499258453b4dba45ce0bd1f14ff7b08901af6058b1e60ec04e0b2f5cd0481ebe149991908fbff0165158a914c31269df81565ff4e3dc059d1992fa1c7740b3bb2d2b8238b89462ec9dfa803aaa0a3d2d01e5a58ba28f00862730f459fb42f7d62249bc3ea246afa668279fe3e39755a0e83f4869d7360b24db704f5e23156787d0cb16a9de1881ea04b5ce8570347263628e7f38ce2009b6bdf5c2271f4ac9a014b18500d01a7ccb1ed1c00c7d364db59e2a37974e5409169af53285b0a8078938b15cf360131368327e47a314c9db6a0f9770813c2b0ecc847139d8e78bb07a15d7a5e4355479019ab9604cb26ab751cf4a6c61c5700bfd3fab073b70a0976145d15cff7af94b0efae14b2b5214af26fc4feb1953d40daf9a8667d400240507f7c780e8fb73f34ba6f30afe635c2102ee7369e343b300dfaaa0fe7ab0c736e1c5001be6f34fca2d9dba727b91a9748b43bd869817610b8fb5f581c8649641c39f47d4d3348d30fd9428efa118151138f9aaa5b62f6b0700d617e5eb01a60bbcb88044f3a239224883c28c14a50c0860d32f33554c8c06cab47412d13e46f3e3feb2c5d86eeebb709d6584d07ad8a0554b355b2ada630d8fa9840604c75ec8208ab2a312ed9a9c578512dfe9f1f8848ad94e01143e6e0bc4d53db7e2d9a9e3d6f0dfe0c6cd9f897611fa8a9b491bd01a2da6bec8a6a70a619261c14017e8502fe1c8b867a2030cd9fcb651f91a2022d589a54a562b43058233d41709328e0f42caea53b4c7a2f1d6c39ad5e02f2556c6c83a169f534a0ea52b2d8a14436d8cdadf4b1174f022876ba1d4b3c95197b29eca296c66999f0e9e658060f9879e2da1d733c1367b12930576ecd182624dcb5121da420eee130d2dc985e7a08531471bc44e563ec030ba9335ca9bebdd16a08e2a8157f3611405b7cd0fe4cfeeccda4df94be77bf45c7ac78943ffb91ee71b8d3c5707a419270b178bdf1be9b86e43bf38c28d0e6acecff69dae9c51dcda3d8c63f6e461cf8108878e5542a78ea4ac7825d2b28734355ce0a05301b2837d62a5e3040f8a635e05709267fee908178f93fe8dc09d9a5489889e453fd1ccea1a5d1beeb4ee815207e479998b1c5b46a42c140a6118cb46ff063b75bca1ee5207d2939519fff02902b40b048aa1dab01d4de7215bec9078135fbdccd30485d38086e84ae89bc4a10e309ab09c0be8dc8d3fc9f44ca26aa8b9779313f2aec35bd49b185e496445380a6e700a44ff9939b5f54d34f4cc6351facb27b9c8265674dc4bc8961d7b053901268fcf238675a61e928f5d75a3c21d416a8a69aef9c2fe115bab874e68e11407b0ab79996e57f983e6d25717fc90e1330b01c84a8600058ba9cbe2741babc90eb10b7cc02808b356f856f7d7247ac04ebfb06d5963872f1be3f18114fb2bb30e44605e3a79ea056deb66f3e96b80ce8bb6192fe5c977d1092497915979535a05a2551a1cb5db49c8b0a21a0098b20ea94c0420faed07afa06f702d93207eb3098038f10e88d483fceb0d7e2ba9050c81759c8a87d7e34a3fdec343bf48434802e7272d24308e1beea239f0bcaac6e632ed3588e9c1e00806e8e93b614c2b2309ad6a69ed1f805588ca9229a9b05bbefdcee2ad9ff9a47cba238c3951d0f30100563a54379257ce81625f27b5afd6649e705418fabda9776add77044105f75509b205ffdd20192624fba63224026f272f9278a18c63512ffd75fc48cc6e0bc30a4baffde2a405eb0ff1bb536a9b7ff8c120a358cb72a46831152e5a1f29f17304f920af92349f5a357ea3267abe43c21b75473c10f106817d490eb9569269890d469efdcd44d01a65761c4719238efa819818e2891220feebd05f3fb42bf28a012927a85cc8f8f2b671ad19234cba95416ffbe79c43ada75747bf67d3d9db0509a6204e3d3248eac89debd2ab4ec2411de1d93e0f34578a10a8991b65060f810f482aee74aec9fe5f715157ea641571ccfe5a5caefe58c1cd7d453ccf1c09a30fe7dc019e10d6aab3c5b70d3aa7223da3bab5d925372ef9c8d62c8d6c8a5d3d020209fc4004d3503551a04f72b437a52379afcc602c00e5ac8761cad0fb833a03fd5263a7aec93a84dc5aa83ca9afea2a75565bf4118bd3c90f2f455e59f1b30a3a148496b65e23e8a2503d58cbbad1f80200114cbb490e6d4d5bf5f519574c072340f700ae179def17b157954a3e6f1901b48781b20b9dc09ad6e460c37833001c4691cfd05463f912bb07274569d9b65673753cfd8158128d637c29c6aa2f01f2769564330ed611650d559584b3f514efe072a7a850de5b44f27089c9d06a048efba143b0584a0eb321a55c1a63903c3a92c0212f7e92f350e489c6b472c00ce6b3c424700c3c129c4e3cc9d4dde47786337b4f99db8f03fef07adc94f3d906bbccfd2aae929639652e1059d4f1fda4213e694f5c708ce970b226f6c49e190bda65ebed12199aa69efa293779d71b87b2bec3fedf6b6ebcbb9e87bb1ee0c305aee8c0a184dd4ab81cee8662a3324010fb2410fe570ee422fe31ca39ef10f90e89e1c5b9f47599ea3e4bb16e0bcb7536b36d3f04daa44fda02e00c663ba17a05e207c3befe585c4463a6f6b93cd0211b34a8d8af2b25af5f64c1ef0636e95e06ba65d36050fc69ff02a8789396f64929e1be30f53c7a5f1266c82ad72a08400363a611fddd6d29bb101068c8bad64b7f53d0806bed2aac70a8f59b6ca132e50ee246ea6c97669cb5f12830ed85d8150d8bec80cbc3215dd0e49938dee4c00d04912e429059b0a957c97460938be9359149ae611fc1f68927cfdb75e3735b3304831844ec533aa6026cb80860e7c25aee60829332bdf596520201480d1775970d5f142f5f883709b59300a5f1f2bd0467a3d0c5acef594944068125c1f119ba057ebed43e9b7337dd91e50bc2e1dfb5f9f996a14e9940007edfe3ee143c91490e30b1f9f3031a766a02cd2c39f0b4ad11c2e3ec67dd111422dceb79777b3ec40f364a70ef227b4a31c6ea2132561f44fde7a998efaea0a84b945a0ae89d3beb0c65e108236a8eca989b6d53b43dfbd4c77f1bc78779ea8686b045b3456e7208089704b605086b2a2171a7a9124c4e1517e7c53c21355415f2a5f585701446f406f8c45128a9a94d362d419f33428d6df7102af773d0bac522c821001999777d0263c4d5138e27cb2917195882dc23563cd8cf1e3015d6a5a891f26ab089ee470fffba6255f229e1d39d1018fd8c5bbabcc13306c9f801b1698ef693d1ce8f3002de52fc61349d1eed05f8cf57f8e7fc925eecbc26c7006c7a1dadcffbf1989b0c11ae543577560f5d42647dc5f8a26774471d668fb302cce432e2e48d4255f807b19c85b2fe06f177f4182f5743eb785b9b46be16c39100f1b667bcb7f701c10c17352a8a8ec43633dba041f0a29c0423badc737b8ad678078a546245572924058f0c88d09b3f5da9ae333999a6536bd9cb04a3957a4e93b2062dae549a58d1075127d9f3847d1f8c3ea803d57d6b99070b07637a7c931f5916d6e82e6dd6db05534de25bab093b56baf6b9b3600bc6ceb1b82a81269d3bc99585b729bb311f03fd5989f3d1bad5e4c2bd5c8e67e2dc1ca0f37146288055a3ebb8df7dfd7b1805292273430c3d8a11f7a56202ccd064448ed0df04852aac41b6d6abfdf6d15609b2cbc4c159ff479cbd5c7c3dabada001cc2980d836b10df083334ed2b88f920a0156a40ec5d1e445d981015e19e3cacaae20c8b7f0644dcad24e6e64de6283058f6bf633691d0e86735bef657a76687470da7b5173cc6f78ca3a2884e514b60016ca5d687025b7e080fad8b5625f5e1f1eff00a5530f85cd4c38ef69651829006ffbb5019ccf2b7d7f23f1576bc0ae6ef8a70212c8089eefe432c2cfd7d0ac0968242c5726171d1d037a9587e4f5c074772526a3994d3db8945198a4b12ec80904e9281f46fa7a058e908b03e05fd2086a8142a9bbbe7ac5f2e5a53b5e0df70a3dbad506a9cc517fba8130bebe030e3a759899313aa149ce758a3be2c50db90adccac98c00e8dfe07fc5f8ea0e0c777fdb7e4d6c61853bf2df4c9d8490a93c0a5288cfee8bf17e7f1a1cdccc33fd968c944e4fb02b5e623efc1a2c5c7533d700f4611cec1229591d1e53e491c66491ade93c43c8badac73631a5681d7d18c702bb62f15eec7472b28b66d4122fbe288c798eb67ed56245ce65ee0a3e39ee4904398d8b2dbd72e07eac9cfacc6c983e4dec80e50a5c82a6d95c0799a30a954505c3dc5c665ed85eac95bd916bc0d15037f9281f23bea1d7d2ce52e0c4e16a830d8922a39f88c35f259b7deb42b8fc77399392fe7580cfacc499b115bfb9e384024ed89f0f5ee7657d184556756fd8a69d5f39adf716f83edea8f7c915787b43037bf8c1be07d5a7bbe4abc070f6e419e9b46a33ecbce14a8012595cd7d2e331080281a684dc3db82a7d0073050fc56956bd383c8dd6d6eb11204af0b8578875034f03ae6811ded31b11a9fc89caf3fa206707d8fb7f553c3373dab49cd1608f05be4dec96d4df9ced760950757500c6e2caf3960321ca1785e4853d7f49a4200b79605125b571289ee5d16119a1dc06d52045ab6de125cad2af1ef45e28859507c69b4f4e2ab1f318a582b8c188d927171cb6d0b82d055d18ec6ddbe7454141047f7decb58b0f6d8e0f5c53b30657c52cffd78b765df40933f0c48b801d88ee018250e8bbbe860cdfba47d518bc829960508f2cd74947cf4542c4b843e2685f058b5b0b61a0a56382c17221825ea15ee6540fbee8a1c06e75035acc0a80edc8028eafbfb0d56b559e944848f57ae2f31a429e94c23e6ef082513986dcbd321d05b813d05e701daed16f1daaf308b62a346f08c4c99042cb424553d1aecfde9908bbaf36c95d8e3dc61144631db982f664fd26d78d68b1d25eaa3178fe3305e50bce836125886389e0bbb0163ad7c7a2cd422f417da5ef39290be62af5dd766703c2e0a23ed2b670d414ad9c3bc4b52b1415b2a9fb902e4f58a4293d279cdaed046ed614830d0b57c17985c3235b92e57df260dc0352a6d9c546c328b1d13c4b0feb05cb90a336889f5b9bf8ccbc1078a26355fb5b9df1944eb9274c377113120e714a2b5f3a158f42678ee27734631da7e6615cfa63315341c394c6f5a1582500235d3ccc0e3d79387c6ce5bc44ea8deb6b71cd5e08ab20c265273abc2abe5d0a0072881a13049daaa90b9acaaefbeb7387a5e42278108d6c2189c6196a8a450f426cc50bf4b7d1493adb1e2dc811091c009086c14cc3c8d336290fa64acaca0fd9acc07c5cdbe54a2aec75e433c132a9fd5ad5b4517fc220b4d6ea5708f421015ef252aebff5634b94c1c06152483e01d2615db8c197da0c9f2765218eb62202e3fc4ff3a43a64794f1c1cbab8de7dddbd837543224a2b0048dab13265d9f70e3710e0cb8798da04e420b388c45e311dc5f7da67e7f1391452cf4565d8efe606ab532355bf2c074d8fb34cd0841d4138b7b26e3612ec82a4f4249a902d000305d2a84d1f1db8e504dafb21e58513c71e74ca0c44e4ca8c4c1a0a62207f4b320049c2fb9538e255584dc0f2c3b5d70eabd956ae23fc7766df973da32faddd1503a74ff68e150552d29eefae6b54479ef9778551308fbaa7917db410ed8986020ea19e5587e66293d41b05df24a68bc8aea1cff2d1a2d8e6c747c6330f2f340c03fc8f598687c6f477247cf790264b0e0bb5dcc6eee15a3d686d90270f46500f0b3653d36295a54a02bf05430b55c4fb605395198f71362ef7f86200b744b56f0b92889c3329eca3ec144c5fcf47febe9b403daa682ed47cd7b7008edce012e80953f22023ed50271981852933415cc5cf99d322f49b721bdf84662d0a19c367077c1f40a96995474b834e5f00c0c64ce743bf5a2c99a08c084d84f7856b7b2802edc4761e223cd3a89650bfa430e955178385ce6a66e7227151db86e75c8dc30ed2bcf5bf89e3843c4cdd072a12217228417e74bf1b3235a301137a211113560cea7e6603dd5ecb2b2bd0d883f7622a6957c9adea0dfcbdba57ecce80c70f4805897df22a6589e73a8be98b4acba655aacfcd0f7fa53663c7fd84eb7685e9a402e8737418f3c3c3a2dc5c4e99897f8c9f53cd513d2c4195cd1e4b6cb440e6570e94919e93cce545c9456473adb71a44c0c5905161ca3df07424f4bdecedcc850f824bdcf165fdb2b620a1e81a22630f4e853c5ad369a62683c40ce4aa1c7ace0d4e6771e654477449f4dfd05fb53cfdb9f7d485f2cd84965e79676bebfa8d2a0180f2b68a91be704a07ff9adb5149a4756728f6fa405eafaaf0183852aa2eb80a30009149dccd3ae307efbd26780877e2db5821222365c83721d76a3d16b43f095f591e259a111b1bbab47fe7d2e731d0b6bdeadff20c032d04013f8dcbb5c40ee1c14b940999d43ab343c91a6b6590ce08b03f740b60f605855efdcb930d0b0113385f118606424781790100f5d1358a2c7475153fd7d2677897237c650be404ad864888fbc7ad77964b727176e29ba0f8f73ebe2449cba21cca7fc2144fc903bb4e7162e101f772c4101ba738db754bf15bf5594156a8e1575f496be84d8c0fafb5cc363d579cd254d005c4c5d4cf33b471c8efb4754ef2e9561423db18140d24d2fdc08650180090a459ddcd7485f7a011faa7773ea59a95304617bea545016208e89e3afa460a84ef612ccacb1bfe2e0e9cb21edef735f0e2ff15a7032c05953afe8cc3607920e08ac46ec23fdd53ddb928f92f6871d7ea446465f933090b04d5f0796fdb82cea7f005c2f0fec1e09fd0005b5f172ff88abc165df3d07a0f98a370d2b7ab90b056a2efa217ec042d4c596124ca175f8ba4b728f9c4d56a0d283bf598c0de4fabdecbbf1a6e3a667accc708b8220d4d1f0f6dc2078fb66e0eb9924a4cdcfb6d8bd4030b788bd14b9b25b1b80e8fb7da10e7b86280a6fa8d09a684ce98fda5e259ba0bd04ce9607a648404ea8da7c7d502a205756fcffc210245cab1522e428846b6d38cc3a4af88437435de7cdedd7181f2d9e79774e0560281329c652375197c7691267ac92b8bbcbef0f563a9c99bb210d3bc05ac1c8801aa93401695807d7475f4a9681b5f6744dfb39ec30a562a1428ad846b6f91560e88f9a51758a34c167ee7048bec8bdd375bfc0c62b79dfafb5d301225faf1d20c638b63c15872d3945f696fd275bd4da6feb50a84424d5a1709d1a9981af20c0a351eaa549b6fd65a20f767e543bcf66384ea5b6e94ec2e3e17d15acf1a95b10c7e1a1ef2e777d7f4686bdedee152fbe1ee72462f31da1169ec09539b946eec0ed468554b8dbb9fe85d2765cde104cf7814aa8860e6de4e420f9bdb4742bf800ea2beeaea4cf1612d6b79fa5d1b0155f4f755eef5f3f5b8de2e8352ffc7c58403cf84cd126d32168fbb7c15209f1cfe0532a7c0457af37ccf14f9b6949f97620bedb0eb781883f38606367baaf48d6cddd23d04654b8c193bbbf1eaad05260e0915acb5e34e587785af6b169cf2256c0136d263fff24f9946845cf0e98419c304e639255b026fae8e12b7d62507b1dad5473b7db7e965eaa9195c46c8437a35068b0fa650132c41eae1ae5116138404ba20919faf583c6a03b886104432f0320bc43749104bd3bd2dd75e395b368e40deec56201f8d7a2a5ff92292699ff8800da737cac8e1a20b994c7ed51129d5e53604bf90a88cf1731a5382d9760d6895018e3f601a5921bcf70ed4954dcb08bb8e5c1753243836b218d5f335df2a27bd02849a46baf55b5d0dc9882641b136f664b4985de0df016f1376334a7f0146af0aaf266373e79ca5d6e1383419a53330f60131511286fd814dac32ae9cd7eb250628df4bdff0d9f12c00f00e90f0d8ce57af4ff991c8c5f56c9a3d6a427b7eff036402c11bc34bd9fb5388ac59d715bb0b4ba9901f45038887a3a37e86d3abaa00fff6bf3c4b8388c82335fc04520041a40f2f9762ebe178dfc6b05f7e3679b006b69fe7561ddadc9c0b436ad78dc436d80153952efaf93bc6946966e4ed51280e87a2e9ccedf596f52362258aa95e7b1a4bbbf7a7f1843dba3e74fe6c42e77b0fc5802378cb54efe4f9c924674b991cc92c7e426fe34d17ab24a5185272b01709f326a53b61e7fc7a8c7f38fede19a27817cebea74a8e6cdd95e321a7c33a8405787bfa3ca25a6d8c40aa88d2fe3051c5374db7cf0046e5ef14c1434946284d06182673367c13569acfe2ce374644214cf7cf1a092db6b1dc2e0d2f8dff908c0db1157d9be063e8b5e1f669fe1108f719105b820340bad814fc18e8279782d701d4288824fc7f4cfc0bb66dcf131ee6490b77feebf93dfce830c8394ac2c4db0e61a91eba616258dd84916e2279b0e4c89f0819ba51e27418f56a433520e4ba028e998894b7644996eb5323631b9aa7263653f3c407473065ed528e3248a9bb03ea555c25d7b90900ae4dae703caea912208d366f452fc967e5e8fb26ba281f0777acc206fe4a7303f6661115994b2e197dde47930373aa1bfc3c79f3eb653409b19ad5fe769ba6af30de5f4299c0d8ab908da9492a11daf1032d0a60dc5b240fd2598a6f42785584546652777a9e445bfac5697f84bb7169874877ad6c1ea605f25c6239b973cfa38b2a95c8f9d32aa160d89d5d5fe61945a276d60982c74d0e32f3e07212ba4a181ae5c08b83fe9c4be1186384fff8eb47211958e715c7ed05 true -check_ring_signature b630e3a4f361a967ae9f83644aab92626d8815ab3b84ab771a0c1cbc976e19ac 084557593cb1f2e57d507f2c9af1a16b7aaf00c2edb34ca756354ed990b00386 107 2f5581d63fd0de55e226060960a8d5531ea85e30635d30288d4fd7dd32bb83e2 bf034b1b9f663b49de4435c0db4785e88327b3313206c510653b2f653a8953db a5b852fa01951c91eb192f226ce81014396c24c5d0596fe019457e2f3edac131 3fde3c00760c09563406bb6377daf2c89d7b03bc85350a7c8e623e9707529e7f 002e65c65178e21b65f30219b89877a9b11b0342ff51ad132ebff2968d198bf9 de67332e2c4e2579af29cd2bd39dd612be1142421edd40ba0851b3e1799fec08 aee5f8a7dd5be2571d50b1fe88bbe4a6351731754b86f39309bb3c3ca363cd6e d24084fe11455576102e89d3b8e74c7ee7acc9b1c2b9059221dda56e1132ff72 53ac0c13c237e298725d1bb84f0922c64d43eb143102f490c8803f06c58b9c1b bcc979b10b10b767fec905741ea281cd6821ec895a43fdd1a49d7f8d9f32bda4 90f7e5a3f2a4ed3c8acbe0be971dd3914d6cf7f11343dafd1eb0a79593e34070 872d548a63c195f8cb35c29ece75b311c2d979cf58dcfc401ae269e371da9f12 fef61544c4bf9bbd0015dc313dad3cdc68eb3865b52db7acb02ab80ee3a57821 c181b3631620247637780380aaccabb57504090a7d3f7e98fa169b924b1d554a c7fa227f77318cf115b4df1e5beb0a511c1def85daddef6912d03cf6e3383fcf 09461a4c6d063bf2aec32a45a41be6e5a458080dfed23c59e360598530db847d 3ba3e240ed1a388962a2ccafc623ede418702de35a047f1cac175afb47a7bafa a2f824bef88b6bdf1b743c53f82573cafed6dee3ee6f66ea1c6b7f077b9c05cf 6054876140e599be1241aef5af43516dca63088c1cc3a695c91c5f6c178a798d 3e4d704c658cb3ebdaba88d6fd5e4bf8a98e35958b11817d04e59a6c152832fc 931504a0989ab3181657100e5a25410555462fd73b01d88c0e89b8dfa2d961fc e242ca16d6c695b53ae0d8d66a0bccd71be921f1dc116059ad49253cb8c6eb67 dfa861fcbecef5bedea0edefd12868f2103af2665e18a5286164f5b5f9c667e1 b130833c3f169a67a55b20d121886d2071c58e3bc45c57a28444040013560cde 65b5f022a457e03612b2c6a777b45d9d7cb2f36fa2b1c14e9d85afe77a59e59a 45733aaf3d62e8f4989b9607c85a243ec9a65f40fbdd8743a7f5f24684fca766 e9bba6a6444ead0b23393bd5437bb41164d2248a32311a78691751e5bb7f9e0b a861f917192b1ebad6939d32661ba79d0f7af5c4dbb69cd4c3569d9aa0128f2d 3243c2a98dea15b1e65a136081278aaf6c93975147e3d7a3bcd8b3b5305d6689 4a57792e2d27f621adbc1da26716148d6c53cde370dd0e0b16bf18fe0700f9bb 4511a13e9484bc49b152a4cd15ecd3e5e31bf39db70a5ff24abee42146cacdec a3786bd0cfa2b24f5a6b0ae044c1972c8a828ad17f423a8d9cae0d0d608c2740 a542c77e863155b57e91428d02cb6f8a1779a40c19273d49ad801a4a4fc917f4 25fe6fadd2b0dcda89f198b2d42a8b848cc3e6b12ee3a57d05a2053b06cda398 3b946b86a57645121109432cc9771f84751673ba989a364c62403e049da1cea7 da907420813147d73cb2b2df2228222d905aa100252e09ff8f0f11adea356c89 8fea6ca48c991c1f6542cd92efdc2ca279e77a345be845c33d77cb555dd4baec 69b5fdd7d21a46d16d74f8cf97a840bd7fbdab165d91900960b65f71cd4676f5 394a5a6cd48e22dc37f80f441ad20cccb0049b0caeab65abcb2836281889878c 1ffa29d0fa2dc6252fd9ca09fbe2ac5a7ee4824d5d6d5869f18c3546c6fcde03 c8c2e8ef112f87a6ab1cd587f1ea4ab800600a14d8af9f64d880f6b94777c75e 324591945fcf18eb2fde577fa687b33a3b776e567c25f2597778e016aed8826e 8e66f2ce7dae8e850331bcc7a9d88e666a7b00585bef992ac95298df8c796560 27543fddbdad74671abca9d56264841bee9c53925784256498d9c5838743fa34 45c684d6c9201e958a45d36b2bbede4beb278d08386ea1914b2d8e09b8220df4 7340dbb2a7f7de507432b35c725190806b51fa4c4a65fd4be5efa951136f645c 787b133fe3d11784e6e28b8b328345743626f5a0a938efba32802fcf096b053d d145f68c5c516834a08080c16382c9a0ab3c171e146545d9d2e1d916148859c6 6e0307430cfee5f9a6a5b2f7554bbde94c263e87d0390d98a1f55ff306fb221b b5879f6d9822b21364a9a05e6ac47c035dc9880a70c6606e2d8d8c585acc3a1a 061722e467f4058ea24631765eb560abd6ab16d3a0d8befe18df8f7a000beac9 7ae48a537f9cd968ff6794adbfa73c866389dc42fc0ccf62d480f4b224388af6 dca7b014af559bebc0513f6f747bbff815cea1b5a8ed91ca2ba5a7f6371db3bb 4fcfdf42abc0d9224a5fae14db474880b53da1109c0d4d1cbfec2fd8c8b608e5 929445955634bea862bc5ef6ab04ecdaf799088dc6e6a57ff1c8a9fe9aa43fde 73824a7e85260c36c9e6b048e7a457b19213b4fda6b55914cf9962ab44963d36 179ff451e4429c3f3c16884f0b088a4fef8cadf9107e1a028b25102a2672be57 5846ae1ad16ed2e074eb01c3772accbceba8ec0d075ec3ad07231347a417dcff 16c3470bd5e415beb77463181cf328f6b05d18e46064143e0ae64559488a305f 2f09a100f759c1cadd877788e260267c0faee9215d78f0f0b5faf0db8a665509 0fb67c8e2aeedf7b534764c8d2171175384842a42f3cf39067b99fcc37804225 9ec9324666eca071012bff20835a0f5b799bab941cb8f8f47bdd161b54373e47 1d0a9bc4e697707a7714ceeec154e94450fdfe79cddd016e26af1845cfb10d66 e75b8e7ea47ef7a112502ad999ad6e0e76bd38bf8206c1a627c901d1183c7471 f14861cf591b39a4d5201a756d39619f26d052c915377db52f3bf5a2599b50cc 5f7c181252333225627136e1d5966c9f976af595ea38fdb8bfa213d835f7d96d dc0aff6272c6d5fb6e5b71e829505d1cbcd06f29688870b8e460aa2faaeb69a5 e02c0152edbe88ae3d39b598828197be8d784f6f7eef2b543a46eb951d54df2e 8a08fba25cba2d00470c8b1f332303c8c7fcc6594e67e705bea68f62905f4b24 d6a838d040a426b8bf090d42bbc928583b01a7ce6a3c896e2aaa4d28853363bc f6c4e9ee7640c7e7a63ff9eff31ccd6cfd303ff1ff173a2e169002e02960515c d6146363b9fbc91a75380e47bbbae70042c06d3882182389f365a9c4f12c7e41 eabd855f55b98a38ea1f0ca26cc0ab80cfa017d502d9d0bc6d768269816fab81 7705fc7562d051b8229aa983a1a7255965557716b6e611b33ae3a541441edb07 b89753b40d53a9e779ad2a7c3ca7097fa0b813468d631dc7770a800c80180011 a2859d840559d9d7af18ded5786137b6f72362041a41a11e67f1b187145b39da da4fbacff11f8be9f62a9b245cd365e29e2d08972390400d15f873857aa75c1f cdb1288df7a2a0d2f8b02ea0bf1be311ab22d2d1fee6bb75bec20aacb404b7ea 72787741daf49ded89d6cafa92579681db1b94b8de1083868e28a3624f19975f 373e2f9caf611c1498fd0a572360ec01dad07b29123bc2ff54ca9eb31a67c76b 6a2648d5a3d9a1942cdb73c24112580d85591c2d95064c20e26b62fc7a516db0 6535a2c9d0c4714b8b1eeaa5d262ccfed5d8d3e180fce4b17076ff9e7914f932 1d8e269b7552c060b1328e3e0bd488713948d560fdc4f8e26eeaa6a0633118f9 b94d844cb00540becedb036daeb2b87267aabc3638e3dc502c8259ab5051017a 7f4f0ae0d3b77ae63f07193c8964a1612fa35eff9e8b84364fc4d39eb563d777 e9dbe5863644232a032181564515cced0b7ffb23bab9e5cb346a231f4385e735 aa83df3b5a08c34292f4ede20f0f607fa0435b8fad6ceb4625dd0f96f939c3c8 463c337ea573557a802394e2e2f4dac24b43f7b1776196028a832f5b2c482233 f5fed4fc45667e503c69142d64af1b8fa6ab7b1a978d2728f1cbed8bfcdd677d 8a338f83dde6f4fbbb5a4232acff5ae753ad20004836c71f2a20e173ac7b6bd5 2f84e8e7f46831028fbd0d378da89e5a3a0fdacfcd1a572a62da465a14c20884 fc03858b75f785ded3c112962367e5af0c250202595beeefae24fb15c21a5350 179a1c026758a042ecb624d1e13c5b172c23871bc4941fd45d40bff56704de4c ee6bd6b8bb40e9997f4a2e98fa5adcc342f4471aa9bcd90291c546baa06c4924 fcec0dcf313ae6336450c9d20250c1633c1c67d1af201468bc4219128232158c ef947777c0dc868ac558be0929eacd2019f4ad87dc299807b1a160ddda1b821d 134d499c2f166256019daf830ec013cc0ae6c4bf05fade746e2f478b05bcb1a7 a8853a78a658c1f7f3904787e60cc2e992383483b22a535ae137db0eef8ad536 b64b330909b08540d059509c6ff6e9e625aed15699d66545a51ee4263d968d8d 797df0e265c03b0a52423b259e3e6c0dd15213c265151b3102d431303cfc4d68 7779737f2a1fe5fd2060f6a22c52de3c2574e3017bff88b631d1d621dcad2453 1cf4330a87aa3f2c450789b6f9d546c683faf06cd6d2aa28458fb80fc79c297b d570b46fb094d78d0f7f76189100b6bbfe575befe9699717b81f966618706cb2 f55926bb6cc1d7bc603543dc3d23f3d207d2ee3d560528d7d2839daf41cd56b7 221246720ad02d92f772dc0c2312c7ece9082452722a1cac66d4494aa0266f6c b25826d68a4a166c392263b52a241f4a093b3e4d838528d6d3a1543c2bb2971f 08a6546d5c501cb9e9688f5ab37f566f523c05e12d0d5050731c3fb0197a31c6 8ea9be866c667fdc569d2a200c8010637a54206597949cba11103ab5f3ddc709b54535e5fa706b04ab91c72c6ed60b635538899e7a7c1e3fb4bb92d14605f90eb7d7616f1c6a399364ec78558567c3d1bd008a964011ec3f7ac0dd781ec9850faff5b253bc3ce41ba5ea530d563c6627fa56f385713a3584760d5e2f80a5440f38534f6a54baba2c7f6d29f1dca12d972084de96eed84ebffd9d4859877ceb0e84cc66a02549e80b3f4a3f9fa2ceceb7dd052e1cf55360f0b5fb6fbf2a72890fc62708b602a054d9ee67ae27ac5bc6f2734a5f644592e02047810a6fb8f49f006effeeaf1b264bd0416b957dfdaa98b6750885531da8ca6ada3055f47830710a8742f79bc4054030091c7141a82418c731b2d0effb4dc48694755b632a24c0016430020b94b77d57ab3a8f7c075c2c18feb073107fe6b33253abbfaa935100048f6234aee58879bf7fd61747f9e2527f82da3fade4c9378e966825481542e20a432fa34ec8bdb251d41d010b02eb6097d624afa01ea151b64b29a7b734f2b5085d687526eda774c98b9a8bd0fc94f5528e91591761b96107a306cb87ca16650ded24a8eff70e5633e80ab973ecf5df069528488a064a2ab57b8b11936303720649dbddd2a8188a6a552e65fa57512d9ebb6fecc4d6c3d395faded5e828b10a07e3746e0ddc4823745ea87c3f61547cd2453f18ca06b89115e4216696c762d70718fbceefc298fa736e1e9d8f22b667767611f3060ddcc48710aae78576e6710e46ecbdb01cfb3c6a3f2c77f498792c021eb1f60150ac0525b52be52ca2ab4b00372955ec5026009cfb6490393eae7159b775ae75cd3a32c67ba36dbe2e85150da505db7a2bb1d3ea18446f4d127e5e54984717cd374784ef20c3519c5372b70914131b2a3bd590f5a63ca0db3adf11618968ab814ca91fdf248e98140af0370619a95affefd88d2ac6f4861a5252f4732e7e9b6d9d73c01f8527612d08d6fc09904690cf862aceee1189c34f9482f596a2d20128b4da891ec04f79248cc24109457744b63d7a744a8ec31aa9f498937efeb5c5bbfe9e6ababccfb96749722a0dd08a0c5ac978b9153f8738519e97d50a008abb9b16ab9513fa03118d80498307739de643979a42591d9c958008ecda2144f2a77fc85a64b35eefe180d157dd0dbf84a284389f2cd3fde687579f7af0e7656499d42bc5561ff44b53dfccbac50c64756c3013679c702a5c34f7f2c907d1a7121b51003a6a2d8535788c4329fb094969751fb63ab3fcc07382c090dd3d5ee3b0cc531ee8a1e950205f258169e204d50bd49a4008aea09abe479fbc5426158dfbbb89666cbf0abae00f46de01da02611c7b69fcdd3a6603d0005892a045ceea5d4c5504c1ef70f686c55e959190034ac0c64aebbacd3ce31e02c57eedb2d006deb7cbfcd707a03f813e631e154008bb5720818d678f4032b355c4002b97abc4ca00f8132ac03a0b989cd76041d001212afd1e922019f021a775a38c1e649c4c27a9c536cff0d0c0c3a0d1bb065c0f510bc687f0db175c6f5063162705552dd00a1bbdf0f58397881bf822af4593086d814ebad2e37a108fe0871b81d00e739cd52a6ed36f01e0ecc0a17a59805b00cf13484569c46bdbcf41aa80ed894f024d4dddfae45bdecb65da36b2662e0403d14fccffe21f3940a8a1c7c8f45680dcd8e4f18dfe1c87944de1770a7495380fc9301af88d54524a3027a2f105e137a1f9c1db0ee96a64f8259980510fb5fa0716d43254bbd2869329f01fa16810a635c6cb95d1fad71a242b748ad7b4aa2f0efb282d6eef62bd172626be116277354f73cbe8a2e69852bfdfe302c14bc70102169fae34f93da802f3d591f77e4acd4b91b0e3b8825fc82dee89cfe4c2591b0acd8683287c9feaab33a74e9e21543cd1cd1c7c0eb72a46e9da69e119dd23a80392a5cc261ad0f6d78d85b9b64c9b92c81d0c90c857c080789096d67775c1430df9c336b7259cc0df7dd6cf1e662b23ec1305332b966d5a19eb8eb295d153bb022cd7ddf5b624b6b70f2265c79874a5156a369034b9760065663cc11b5992e60af7f15a2b6a647f00825441b6fb11ddfd913bc0a37ba7d3a4fa1c194c0cae5701932c612aee9f0c124984bb5208f00a40de8a76d1a868001860daea8af5751104fb0de16c9e38b18f1114c6932bbfdeb1ea640f0dd9df33d196105b0134381f0902a08ce81eb1ec72eb67c8f6c03c349694346c4e3912bf89111c3a06832b2108e52230e3c531f2aecaba7947091d2ae65a6830a462fe626a2c8a1b120b893201db8807f7acb84c41ff26791407f63d88205c7415b8cb30462d1396d5c784df01387c723532bbc065d8542cfa9dac007a47e6f373b0e106c01c328d71957cae00e9435a99a202938fd9ef772fe2d0d5ef8839e9ee9ee253f0171efef14fdb080d8b14f14ab019409b578ca03ddbb057ff5df37cdd531e979fcb6756ce2e22f10e6d829d9b1c55953b12376e7dc24a9f93cb6372bcf080226f52a1ba9b0a9e100b51a21fe751549f14875e47ec0a77e954562b4bbb8982c2a8f6b365013576a2064d7ab2ee2d24354e23e29d2bed584a613dc0a267bbf5dbf5ef5e3c2e4443c00b3f5fe8564a1ded7278baef177833eba8c8bdd821b4180578f77ddf828702bf074c8466575c8f381b64544f39014f9670fbadc2562ff45522ac2a7674cb9dea0c7769004a65a1d6ab8b4b924e165674d26dd2c41ba6cab13c3d9531d230bb2307717e3ba404ed567cab9e81bdd6a121e88e924502a9157acf503fcd28315f490dfe248d0bb082b0bc65377bfc61efe84776b4f08af55956f0c33775b2457a3504536c5aec16370dcaecae57d626893f446e3c9afd449fd3e745ff040e213ccc003d62b5c67671e7d0ba1c8c972ac6093628dafe0f9059fede86927335ec0dc000218f5700f8e61cadd43484dc1ecf27e417195902e7a07413f952231aa8f2aa0da2fec0a9620a577ab22905c3c5626ba1dfeb1ff0ab49281dbafa1363957a2c048e968e22e69b1cd828c09edeef210e8df4f7591a8376f912e944a18d0338cb0eed1a1172f0d4cadfcd8dece08727140691600aff136868dd1b3771ae4f2e9c07fab897174f61c154293d6b2120a534ba7b493923ca30dd117bde517d8e704903d8e9647b22de9ef6162825281167ad054146dd9d777845113876989c1cc239035f9de45fa4e275ee9d03e6ffa67a353cdb8a5bce2783527702ec04dd796cd3032d2cc2348bdd27ea78f4807d07349c2010fc212d6b791cf6cf6694db2af6b8060c7c5f2397d0453ebec5f0cf8cd220d1da8232106a29d7e135d10643cc848d022f884fc1e0ad7192a84abdf0ac726e7acaac441e39574a3f6cc2a70e51d3bf0d851275ad23263da1a32428107c51442a83cd6304f5a945e64e0ed0d04cd3f3083055b4949d739cc8f5d0acf51637fb2f1fc072e2db5919c886c2955711e74b08b290aef1bbb54146f0cb115a2e7b534a2e6aa122be802d170c5013866e83a80eea6fb152df826473440e54f06bacaa4d2bbe9197114d653314b1ed1708337804796ec9f1ab0a08fababf3bd198ecb038f1c0a87f6cb2612cf5527bd9e50c880ffe15e7818df9cb451c74bb8db29003b39e1b8075b22b5dbfbff07a24cca9510209198971ddd52bc1987c3b2d83d824f7e3cfdd33cd7aa9e6ec7953685032400c1dbe0fc74f5039880597529d3a46b476306b9547cde4208e9dd79e3409fdbe006d2b44d2d0b9edec03ee181ad34ecd5c21cf899fb795ed29bd6d355b050089042969cf5d5692581c7dba7238dc26169f4eebe20ac968323b417bee1db41f960dabfb01d549d186337b426b39b46e1722da5790af7aa5d839fe059bb5f4b73d07bc401257664f59b2d7fe63c223b265aa7447ff6220fdcf3e15b38c1208c61207146db81b27866e99263c717bcf571f2c4f8a80ef75f2c62bda1eb00e09161803a7f08ba2894df19151b192b6cd1f8e7107dc3b6e2fe46eac3adb1bc257a3c704d5267738f2c6cdd70d8ecdb29fa51feb13809a8761a52edbd0b24ab38d69d20489c86a358eb65241b2c78fd5395ad3d4b443f51ae6129f77fff69e22416968023bab20cae78e0908ba91dfa80ef23d617ec2188005e7e2b195af86b4f6bc0c0c4416cfbcc30f394bd8f38ac88049fa5def1186ab81ed77de43de2a9ae99c9d057c58edb698ca417eab715836ae439aa82d8cc3945dca4b499c41d028436d9d0f512aa90e629d5ed5c90e84cbe635f0297100a43ef8266b2a4191da0ca88f6305dc7e6baf38537478fd881d215cc90130398ab41010644bf6e2f623eb4118130d278349dbc905d6130ae4d65fabee9def7846aaf7f5563608704d8074bd0257030bfeffaa1fcc23e625634147e575ea85b2cb78742d52b7f539937f903df4a40c01218272ca384dae6cefc6a4516a16f8e3202bb60fcac79908b362adb0ab350f978797ca1267cf8ed7e0968f38d1c96d5835b6741dcd884664811512a6ad2107917fe4fe138bb6ee21b6c9ac2d68aa8cacddffd3ff64b1c6e34e95823458cd64fc938b03bb37df23be5181f0055407f5c1704f890e069dc548b12a09445cf707033afb7bfd1d269a628a602670a6689a211ffd78a7ddcd2e310ab1978091b709fcf7dddf4d3d8e51f0aa11baf03fd73d34220858ca95b10b753174275467d40b9fa0c59202045cca50c3589fc3f04b95bc95019b8bd6ed0d3195e9c86fcdda0459b23ba814f41ea4854ce2ae865616e9e0a2274efe85ece3ecabec3e97a961093d63fe189e413e445b8f59948d5363253efdea7d9fb66fdece6aaa2abeb9e00d2f5823b772fb65b61d430e011d5e01f324830cc63fbf0b7b071f5a4b75ca6401407d32b166f01024dbd7dbadff3b539596a60634840bd6a30da57a5d70b87c06b7e3c232bbf75585a61eb790822578189aff30777f61e90e984924dfda37b80fe950a38e38b3b0a5f9e1b3ca0ed6e1c46d58b1ac7f992eb0b45e9dff273bd0076feac05aaf36002074f3232aa2df216d4c5c73008393db1b60ccc7fc370c030991b63630ddf326df217be34827b0dfbda553cd178a25ef2830c10d524a445a0737efd07d0877c3e7e6d1be9abab541dde50b85ba90128c6ef2a9704a74bb2a013a171ecf4cafff7174166587ed504df3b35879dec030d47362375396398de308edea2afea14cdd22dc92d3f654488b2a965304bf496d41f6b25cd4210089560d2aa14f5de03afbb884dc9b4d12d457cae483dd9d3fede48126c67503399bb10c5ee4a78980f2f74f459f1f05a612591ab2b6333d638db1548412c5206e3a8f02efa2ec408b97b2a9984eedada999e3bd3b8bdd735fa308101e84ec58606b4003961833c889bee04fd6aa2307e0be200fe00ee2df3c527bf3d97f7b5ee42b4800f7ac98184e2746923af6c92227027eabeacab2597c16d4472f40e7a60c4c630aef889a3963f26544df458286445f63143d99aacf4e2668ff00ad7f8e91e5dd052366cac9de08a67091777d93bda061763293ca52604984706efbd7c3a8852e0c6815a7239542642eb75e4c523a87621493f2ddc89a95b114906a79a799c88408c6cae007c0eb16f0df7a9c0d33b5f8193304bcecb214e339aa07518dbaebae06acdbb78fa20a7be03d88541a9697f3c72d078d74b7544633d2ab1ab35b4c8b0d0eedd889f9d117a0ebc0e313d06c34f9e53babfbe48ac494a69833be8230a301986731ca1d4987d0c0cf24ab661c74cd7de4b9cb242208bc995fba3a43bd510ec5da6df5063160ce38544c4816826a84a4a2bdb3b21152133a9c394a3410260fb36a96d79909080aa21580487af88d0fdf89952a11eb116c690e7f38fec0d108359636770f3992ab5bbbebc7e29bebb3d16a373919598d0b36e67e92914e240cc78a8d42be9a4039395973f4da5c21d0f2e71534c6075a900cacb12d0bbbed065eaf4f73fcae20924792cef9166a06f57298f580b482c10eaa16cf07595d670c271f5f88c74c992c43fd2a32df061cafeb82c52660fe64e6e569a7447475af00a785b7aac9dbf6f2ff0791ca3bf4146c1525570cbd08e471411dc9d47bef0108e023fff0f7f5e70c0a569fb1e2242e83e448dbda7c0c4276744498ba6646730b9e71ecec656e7eedb4b4352c7043cdf0749d16140e03476b3fc7d0da2f0a64053c9ecf75ce743a474f7bd0224ef2600dfab6be22405e03b8b08d42749135c20f10838f071ac91170000ebbc64084ee84fce6a3b24ef37d47abcedcb57f54b20ff4490138c331cb13f93f0974422651fbc681756dd416e6dd8989303204bc490d930730446958ae082b212d36956e978da24c661dba23c8d364abc9d77f6bbf08d45299dc953455a6b9333d0e20ef2db4fb7b428f12912025b5ad2c23b1220f0b9638ebc0797a3c334e663ccec56e7100d8fc16fc73796d3285be468d8988e9037d233fe3724fa249678c9bce319aad97979299859b266694bce15b448139b70be74c332698c85223f459185f4a43c5e7320558859bcf3a58de54e449c5324b075299d28c8635150ed942512fe771c64876c595f30b9ef2e5d400364c8a562207c2fc912fd00577efeba72185ff5f6e29a897fb39aa441d5ff4ffe2e461861405384b42d9577d84513e44f5ba3ab05ecd3fc7ea0d39e5e99d5123559962ccda0618683a91a320f345b9cac117e73a613fdd059efd1620d1a614cf93f52740a1068e4073f71b5d9a56ca84302940a777e5e789ae28dd2a8b67d9440963479e8001e667fd61a3dc086a79101b7d9343b0c5e45ed51d25661728ccf687514a6462043c025876bded33382af33af2f53bc4d7358894c9b1ecaa8a44e5da51eb7f120a0a642a0692ac955344fd3e6c40566c987e9eedb519d995b94340165ca0a2d70dedd6ea762c3d3c969ee2c995204e8c48c9b36fb3e53b32b95f0cc97b96f86b0a812e147894add9773bc8de1229590f1e665cd07e7b3c16479e187992c69e93066da0f33498cf99bc7cdbd067eaffa70d5453d097b634ae694b1627daa34bc90944db72ab8d14690367969c54e8d35ae8418030bc4225f4347cac9423606f9b09e727dcc7f2faef70568c692bf9fe1fb513ab9bf9a1000a1c32442ffc524be504056061206ce2c907dc6e1fe5d6156f1c55eeb11b5a734d321cccebab4fc3c80e934e54f971a632c5dacca72e47f89e745af00787a36e78ea8e2d1af536034a06de0593edc5bb131d1c93b1374a2cfb2f982b34b8c3b6fedaefb1b2c21bdf9103f031d443b0ddb67add5135d1e96d22b1572d7efa223fff9b73c58807bdb986067f98bbb8edef86069839169e25cf61312ba9772c16fc11c85cdd8492fcc55c0a651f90fe30c6f8574df95f0bbbb2c2cd7c0b3f3a4b4a6a63357281000931f7015c423011b1417f7d909d12116199a84a7c5eff7afd21e369cdad4190e3209e0ccb34b5ae2fd2b05cafcdeeb7b35efdb84d9e250587b0fd2c36afeb92f4f3490c500dfb7b14fcedd46dceff487785d3a97b2c507ebed57fc2d4132d11c939cb084bf0d75a8861b7d469a26f85e4480d08a78a3e94b43123d86df63823aa555f0935725b5350cf3b466c2bda93dcca2f381e356c371077d565596b39498dfa46024c1b060c18f4220f9ddf260ea708d7b07f36781a9b8bcd1b9f2e0f1e1ea2790cbc21554bd5115b89486d4520d2a8ca15bfa4d234c4d319719c0f560409014d00236d76f2645c972fcfb3a045fe5845c924d18d8100e1ab4e92b2b48e851a140e2cf0fc0838e843a97a072e4e11163ec8e757669cecca74bf5b7adb246d673e0564b4c25552f77dee80a855de7f459d8692fb8e142adcfd661b8270ae3e2fe006a2d45bb3df00cfc8a7412d8d4f608582fd1821fe22e651cecfeccf2d0991cb0197ff8693076152f783fad0fce9e8a9db6a5df8e721f196b66ad01d297130080f2df375e52d4b3cc662cfebb86574287645b9096236104b14016395e3ead55a0b4d9fc1b22475b60d8ce75934ea4ba127b4587a833bbe254b3c4cb969f6570c0d7678e553c8da791d9ea756b1ba82cdca1415318da31d430cb775d3788cbd370eafb36e28ca0f3e7b728a87223e4197c68b693d43eb1bbb1b1d0c11d00575270fcf14108a237d371aef652e0c308ba0c975f8d39cb28200ce84be6bf2dbcae908f0fecb7628c2306e304598cf65865f4b34e5509e125c34671133cf0022cde709c397bd4e9fa40f201106084fc8e9e8bcc80bd74e9b6f42d822074ed532a32f02669cf680badff4e46c57679ede145abbeb92aca31421d20e8633193a4b0b8803dd7a58f3ea994b33320c228e35fa4d18aaf5ccdc334dfcc9c20443df00e2990ad248f5c3f8bfdf434fd8fdcc21a21aa6da0ab0a0ad83478439bec6c65e9c5f0fbdb733184112506190a473b11afc3387c864e88f205465263d507184ac2fee080cbd16b2218d0a281f52952824b09fab3c728080d3951792899db072d6f6d706600dbbec3a5d279d3817f0e734bfb8d98332b76578e6ceb902b7525baa7968049364d7f57410254d3808ac8a9def2c7e3a1013dcac5ce8cbaa33878463d5050ef969752e27e545412a4648b87ceddce1e79b1a110f1d217040a577ab38722d03dc90509ffda29607a7a5dfb3fe2804bb31826b370164ec3598d94fd7e39b080b505a5893f0965fc8234d7b6bdfed481eb8f9b63a87432e457eb325c3c179720378c532141f44004674b9d212d597dad8789088ac21d8e85e91caff5780e4be0d8a69d1ea1adc3ac3ea0b9c36bbaaab76d3889f5e6cbe35e98e76ff26d5b0a1068267c0566bcbc8619f21d0940aa38328bdbde54dcbd34d2a776ded3e9a3e6e050be5ac0e1e707b7f8a284ebb588eed8daef8f43a7d52ad21b42a2ebaf3b66f0b1faf24ca860f721cfc8f6894c7afaaefc36d00003912b950d5187d2cb09b930593cebe294acbb3aefd7b98882712d7b797a0f99c6762a6bf76aa3e0e7ab6030384cdd2004df8c903e59f8a296e8cfd08a4bdc701a5ec5492ea51f4d4e1594d0901e32ed9cb7ce7f2003f84741b6551d60ed8fec606e02cf28de72769bc0c480bc626b83309d68710f0b6d540b7eaa7218de614fe2442dd93caef8f75dfdde901237430f2ef7887189ca45f6f59baa673548454fb237bec92ac3b980c72d0490d87dfe9cf54da153fae8f514f732b74078b5f69fbb22ec36bd1b2016ee45ae90fe6c0f0197d027ed97339a14a34db030f02752b31172b74c8fdd17928b1339a0137b8745928c3d2475180caf76e942f603f3a340ffbec558951b5917e5b3947057372db7dd61d5f43709684e4d5e552c6c5b65994ff90c92ca23266419c9b3b006953beaa481e6e35cdae258fb758de43b8767ee534498c0230a7dbd0892f020402ebe8ed51ae9f9efcd5013a5bd7754d94e20dbbcdef39e39f8a8e49a82e120c68a70a4868ad939759bed9d4ddffa247d5760103181673851cbbe4ae25dca60e7bee6e98fc06f115efc3fa973a2fc252958e721bdd8fcc8f2b332ff158074f056aa4d5e65f463a7bb393b1927582d3971ce3840ad9cc150eaf0ebb88f97f5c0a4c78a2b25ec51721fcce4063ab04824086af2a2270198ecfbb2bd565d5867403aba2eefd9adef3a40e04f44461b58c4459862c56a4c994a6ae9558169fc3dc0b false -check_ring_signature fe9952686c53f4fdb1c4e0601d52452eeca4ca4b19b8ee9b49f4e2fe66146625 73be376f2ccdd3619500ed0e8011552d42203ba9c549a790bc04774274711b51 26 c3ef3b32b5c7429974f2230b9ffef54205a7a9a9cd4c9e442f0cf203fcacdb34 96835f36ca1cc7bf5ab4db5a59e1992aa73eb649540d2bd4eb254e3732467510 d3a352ea1d2160284e1b6b62d679527d9a475800fd65dc62f062dd574bd6d8c2 a7a809c56e1a9f114947977d042f61d6ac9d445cdf027226f9ebe3611c836f50 4e89615747b111974a7fef7d5d3808fefe967029cf7268a61584a033e1d77473 a025dfbb72c15b3e9c9263d6e6df02c60876c3b6647e7ae081ee3041ed6d4261 c452be4c69ba5eafab04f94d578ccfd26327a466996015ed619a3a0ce0f1585a ea6e66b0e3f0aafabe4817f0f2c746e9818434eca059df6bc51c8f72e4c7b6d0 fc9bf5d74ee74d60f5c0c11809f9e186dc0a9dc725d067b193914f04d65b662e 5653f7f8643f574e0c344e5123fb78f4439ceae805548d16e451c681fa769f81 5f96c13a98ff318c26323ba62b8e8626b7e163f4f9c60992da5acd27182f14df fa4dfc658b61541c160c1ad81a29c614849770474b37707f6fcff5f713346663 e08545e4f9e87154b4b131026adee7f1846c022c6274c60adad740211b0ee159 ae438399c90813fe86214530ed082e91bd79fb1231ea27079fcf95ba4e84384d 73ca611f5e296f74ce19c9357a2fa8f81075c10c72de9b3a57686fdcff3a7a93 9720495084e60514ad0d5996b67860b9b64141cdb78ebb87dc61269cbb30d9e2 96b4984a471c6055d54372d4dcbe644ec8102d7ab99b0207d146b6e49f5d6451 1b5fd2bb5188a5ec9709b38df7a8643e5117cccadda382860f3bfa28458df7c6 d9040430ff5a36199845a5601a2e6b11c5063db360204558c2d204a53c89fd1b 5acac6a52f70ee18cb02ab6b41c9f06b6d9dd332c174f9a429ac79fbef42c17d 18485a199d4758a3e218f000d65552f41166751e33f58613ae53e9d6d1e6c777 e6e865884a331e765e71984d4da5d03c1cc9825fdadc666e774f49f0ae7b145f 1e2d8c6ab1a8370aec6351341543b2db37f6f62cf18789fb356422773f3ce080 f406d562e4d83cfffa8f40e8d8f520ba7daba55c3ab10b2b18674399619fedd2 e518c91c0a14fea3b1e602b35ac4644beba9b0ef516573b1cb5cc16d9f8c8d5a d21fef6d6c7e3b961762c95d41bb19815cb087a9a8f6a130729515771b40ba95 25f37a2dd7b9f16dcd2ed14c934d48faaf7aa679abd4f69c0cfdb818bee62e052e8a4e42a5519f2e90939a36244d2fc7b9d1b0daab63ebe1165a483501775d0b8c61519ea84e9352e39210a9a4afbba7d326e17b2d4fa8c4c30b610a361f99081f2e80e89a6e924611c2657537eafa6a89d55c8431d830518cadf0bee88dc602a2b558b4bd07468ad9a33005809a4c7ee1722c0cb86ee52e5b778a45f5019b061d9e4c4a3571ff79a2fd837600499621ededb5542880e4e095cbd15879ebb4097eeb5f979e711554a134e357d7f304291389e8fce6cbd826a7379e4043d06701dcc331609ae789ac81933a4d800043f0237ff0cefb8f7efc0b9e62ba9a9ac305e9a1c47f8ac487fc0153de27aad8764d395120aaa16e7d1fb0fc0478f7ca9308f8f57adef2c9c2bf1d3602cac40080a384c8d827fb9a67ce0d20f8640dcac20d9cdea961f86dca991373954a7583872551ee0147f9b904503dfbf908bd44af0baec3b8cfd8f496fcc8db22d2afc2381e03dabd99cab8f16838b52109fc4180011a4590eae81fe5cecd7907096304d776a1953ff30d6ff530fa54e3fb7f672b0b93d65fc22306bab18dc274607c166022443c88973d103ce3be930a6e26e55f077b60b6138a4b1e67696cbd87b5e82a25aa7149d5587de20a0d80ff9d22810e0c90a5ba44986b40cebb23015a95b55efaecd553fe86534b6aaa215f40978e500bd236542cad1f9b1f3c73b4bb500dc4d7385fd40244c261ee936c985cf10e400a0a00a411bef6b0eb3ed85169fe7e772e0d57165c16a8998dc15e97263362b00a575bf49a684f70af79c2fd223982179051096aa53c11fbee69c923c19edfb801a100e01d333971279697fcf0ca17b5b41ed696afa8cd85ac76b1e7ce22f7050d6c968c40fbfa30cc4f0be44213ed9a7bec24e8b302386e75c2ba89f058b4b9087078991efd38f81b05ae2763554710129f5b979ebce13a83cdcbca44b7056e099dfee24d54ed0b8daf5eba0d830f10daada427c33ba8f15952eef4bd2b5707009aa4b2da4bd7dce282cd36e600371c13f299be69d06e5b66dd738b19e8e49b08d1987682fe593ed5466a8e81c61a1472af4f709738441455c5ec0308bfe3410a4ee1d2c69d5941d12c0a89aa9fbd450c5a288fef8dae69cb7359bd32e0e8a1090d3912f0f2a4abd0b73c79624c2be6f8f6b96807dd935a0884cabddbe6a2ba0c784057c2b9e75dff8bae5eb8afdf8042ef94917d383ea6095e60450758772b0484cf19784ddddefd60df50feac4cf9f8fd103b46466043f339e95939bb7f4d0282cf900bcb3fc6e13d109b8f752a3dca11409a8607bfdd9f2d6e06e96f14a4089c98ffc8d5ea2681c55b2d56f76b75ae136dd72ed2f6a8d671174ee0eabb1d06c575841cfae1f21013c252d021cfaf6b98fed73e5c75b9d9e916abc12ab79007eea8e7d2100af086a10b233726828ef2463874e2760aec917e533f0855cbfc022562151aaa3b344706d77be5cc1f9ef01435c2b2f6e65cd1f0451b6781fc410a81de2418c71152fa9fb25cd01d0026e577754e2ad0ad8aa55c4ea25f75b71e0253242fcbfbf7ba18feeeef85a8b7df7a316151305f6ebf7d36f53512c74d4d0cbc83c622f3b33c1aaa17e53791729ec74db2e3048e7a79583345d6cde8686c06e10b312a0ce0c932e6eae4950721ac37deaea3a034ea0eea95aa0147356ab80ccc7ad4df7a67eb28ddb19ef48c5cac0fba3fe252343a000fd7b2259f97ee320d83494b7206d49a571284acae75ade2130e3c030972e728609db37ca92cc5d70edf1625dcdc23bc8f7185a2aa8d339c62dde8040f8140780b5278bbbedc06300812996c6650021e38ee559e0edefd7e8e5a2e7f63806e73893f6faa6254101c02cddf03914ca0eae32e42ebff0bfe70773ab1006938bbe201e122b25f235db70b2b9e86195597d4b2684517833b87fff1369844714e9639a2753602bc9c70da0f3f239db5014c2c2f63b53a595c8ae5be119c348086840fbe54fe2365c02d7c0f294371a97ac08d40273230f3daf01da30480c4f4aee226023454cafd21d69304b4a6b791aae0981cb7cb850319a7704f0f42902c468fda61724fb837258112065f8484d6824d014f65172a3ba04d958e85f0595c2cb6cec3b9ef940d80cf0201f3655796ac5c6ada28428c31984b34f21990aa67e51dbc4c4ea897808a5cc1099a45d2619476733b8019a7979766a20351962c99bda7121d4f2f7ec5728707000dc7c8fa2d55ef16eb848774b590a7e8d1d502ac5fcbb8648a029cf21ddd250c0cd4c4efa8a30b269bbcbbba0ce3eecf9196a4e447d958a7c351410038902604 false -check_ring_signature cfb349e6721775ea1753a0d1ad3e5caf894aa80887bbd1cef6cbfea16bdc6b64 766f7f2cbcf587f424eacf415efd62785dbcc2608b3b7c7733e4146a4964cff8 9 548e635b529d75a8de19861e53fd73f705ccc31df6f69e629c996354b648a2ce 8cb4fc1c864d26b170581a848543a50e6f9fd1d2ab9203d13fb4404ee9adcb17 d99099bbd5c63069859506adfff51b0f592da292c452b45278619dd811bc463c 94015f72f07bb2f2cfde22e3d01fd94acd657b31fcd5c9f29619c89ff9c59e6a 850315e919e5b6d0e306f33fa1eb0370208d8ab25effe41551fd7e0e50b5d7dc 1b4dcf76f7ba65dd9d87b1120cbc0bc3664aa313118722415cb3b84e8f622c74 34d7f969e3dbcc775666c177d11423248d37efb72e472fbf447d3ef3b4443552 67d5e136b86e6397e56604d6d085c8078d2fafd02429a4e359248a9b2e44fd0f 5673a218a6e23925b4e429cdf48f7dd72483790806bcb910714dd56e28749882 de00f7b6a3ad8832c6c4c9413a83cad95a91586b6c29153f646a9e459a1013005961a30c775f71310be926b19f0e19d488b06b7797409643add59d1c07b7e4004473e0a38efcec7f62e684b5f4875549eaef1bf6b7d72c24dbf684e5d7a2080ff614e34606d968d214fa53c322f75666dc4eb67c8be470b10195a239aa68d20ac346958c031f13f40d061222cf6c746031937a7a8af9de13bc0fd3bfd6ede40d63d6584ca3db5ac19b07dd5021dc763fad46e3eb4016c0dbd26038480368fd09ef47ce78f4270aceed0342c373db43f30c210eda315356f1e148b3cf17c20e0bbb6c6d873858119b06fe98feb2dbe7302eafe0367fadd9ed6feb5cd3eeb3f9046b1bd86818e0373484a7ee7668ba2b496849b0725c27619f894cfcc884322e57425682f68e4dd8bef96d032c96dcb2ffa582c59df1cdb8a044ffac3f2ec04602f085496620a09b207d1884b0383a91a10eb90832b0e5bad2725c0e7700f31506be5d78aad4dc40d954e52a0f4d34f57b277107484763bd239616a4c81f7d240abc46b5734d5e86f94313f92984d930f401c58382e929a121d57f5f753ba4a405fe8e1f0bf1c141583580459d35f95fcdf03577bde98168d3c8fc7a91b8337200cf1c194f642c6368b968487de3fbd3ae66699588b0da42a1047f7a85eedae70bc6de5c7cf59edd7195bd7fa262e02633de80223c72e4dcce27995f1a8aa9980881857bd703bc7a4752bb2ecbfd15dca2792507b86eb0005da799da4236152e049e2cec84ac4b2b773e14b4f8cbd3c70265a6580bd372933cc2eec216c21d8501 false -check_ring_signature 0579925c87974728483dda5e35fb760b93b84077173c41fd7360a6f8a6963da4 73f34654549be2c0bcfd4d9ce0df1865ee016a8ed460cfdcad8a2d63f0c9d63a 4 bd44d8f281b384870a52ee2b03c036419a8b3f9dc49f85157cf2d062ed27bace f51292682ef93efd2d1742f4927b9c98744113a041eae84c5b40bfafc2688709 faf4c7c8787e13f6963923df3d43ea441b63b95b72f4f6164919ffb719ed3fb8 95dde5bef69e177e4a1dd80c6df71a17a4c1db34b15c6947c0fad20da57d149e 6ee00104a298fe21bd3601d68d548e805f782a67aecf770528e79ab510e30104c6c1675366f69a7fd8652c5d4a1318f6552838c1d1cba8dd3998cfa7e69c540519eabce786282671bff0ce80743646c2a6dfc0f619ea7f8233db7a310501dc021cc0cf12dd6fd1cc95742c5cef9e05f5a5bef17746ca96ccbd44276ee03821073ee1ab60c0fd07f9e7fa471e4de72900d1588a227c66e18c8a24ba3bcf08fa07911e3ce9307cddc03797b5e8dabd6848f24b96e18cab32a970e2256f3edb460f43d474cecdeb87e7514c0f6e048e8db2b666728b954a31417641aaf2fb178803c89f7445d32d5862266ddc0a54ea01f89f0d6c959f86e3402c396db056784c07 true -check_ring_signature d628269f5c6027b5096e6781f83ed613d521a988891b4eb35c0816442c9ad6ec 30f49dec73dc0b98ed8e2c784cc058b7b18e77b3294e77a3d995b4237a7cd6b5 23 a51224b278fcdac16fd9fb0c211f38d5ab7eb29240f0730cc7649a6775976c1e 69faee5473c35b20888a4b3b9259f9589b4a2724d55af8ff3511e75a3d7d648a 249fcaf728124a6516f3ae97228ac91b5dccc34d0f09c80ef0f5ddbaf04e43fc 1427879e12511dc8a1c270e493c47bacc2103775665fb326aaf24f77099fb900 b42a7ed8db2faa150305eacf0dc7f8d6a55cdcf9ead30cfed2683e6e1d79ccd4 31a354fb8a5550c751260afe94b3de28bbc3aab05e35220e8e90617ba00237d5 0e07f74a9435c5f6ae7cc4e89a453f1efe853a5b54daaf4c5c364543388e5d61 58b0213fa68f317c2beb860e92faecf0e76eb3cd16cd52c1fc076edb17f9c1f6 b1b2393ae80d3f2398f1db06e9776dab6b8a21789d6452626059e4f683725b14 37634494868fc41331c4faa424a5574bfa01de44b756c8e544d7b8fcfe20e019 47d356cec9a89635ed388c457f706478e576b0863a963176fd9986f8e4e3f61e 9aa4ea77fa8b42ea7319fd1bc5e0a9ef8aca92dc56eb8b4467305b5eda05a1c3 890975c5a0794235af2d011c3c3edf38950a3dbd97b3d09f80a6fb5d6b97f2db 781b0ac91b062d6eed2d34a201aa91f709b415ee1be712158355b98364b27516 ee4d2a3189248ec753a05dbf246fbf0ef50ad48d614021babaabe00f75db2f4f 3bdc7aaa8bf4b60fb672fa14d0537e9577c625e1f5b8c1a274f6515bcee526f4 08b54efcb94b590f5f4431c549eefcfefc7d247d1b206a6ac3d7737e1873a103 0184968b53eff5da944a199d1b8b9c37144e0e5b84ccb8ed9d89532662e64502 4de239f448e8a0ff25ebf3f36ea164edf7fbb6af6c23d84c6e906d89aeeb1cd6 df030952067e03eeded0913be2ab83efafcad63a66020630b12c710c6691e9c5 e3acdebb7b6b6d1aac6db670e34d96e04be5a103d52d97ed57b98d6ad67ac626 fae01be1e1449baef52156363009376c55c07420e044ac7594f2eb4e171695ea af33e2bf30bed7495a28d5adbfabc3a5d2f8ab936f3f35d8ba22dad2465c98ff f359396a54baa9cb73e1cb14b3b2de32e9d960b99fe77deab9988e88d7dc170716bcba5314bc35c088aa477205aaa33fb45a47266ae88e5d4254a121c8735b09d4b35e54c68939f32f8eefe02aa20c48326fc66764265bb97d0ef2ca4e1e6909a387dee0af20750ccf404062fbab1cd2d5b90ef8d5cf0eeedfc9a8b07bfe03055b2d29e17224e32f967357323632d9f64cd3c19afda6c34159fde13e11a11a0a629ba48ac12839a5ed2a75e25f973e1b353674d6f65a47d5477b661d50ac180506bd18d520cce17726c83a02f5eebd3dbe14ba2a96b7152efc15a70775b61a041b77c5a8ffd016bf6bc200c7e0a8e270ea6ac923f1e6947ae92e907a7c36f00101f62c3ad2b40bea6dd0f7897d68ffb5fdabf4c92f898f43043baf508af4a5098ebeb6a00affa2dcda26e896e18c9b309ba9b78d04f36140419104cfedba6606f5eabf46e4fd08542215ce2d1d7ae01b0a955f2a8dddffb81391cc99285b4791e34def5f0a5c3b1d06eb4b8d6ae9155ebbeb2856f66e45ca380cb1cc5bf5dd02432fbf926e0ec8b629201fa5200d04b11b651457ed10b179c5f2c9712316bb06bc03657c37f3be2bbec3e2964ef1310fc37262a8fe341ef9cc2375192d855e0dd2a7048d1bad50c7ebbfa6f670e8c3ab9981f1a4feea8b334d373c4f8c179e0e86328e6e73ee6d469507103b3d90c317df827426875e4c59037bbaefff926c053a1965e7a00b093e9242bea57eb72a9109e67e5317ea1c5a7cbc261d8401880b83722bdbfc49450a2796278cfbca46d49128f8df31321cd94e2f18b108cbce3d13a91cb91af3239644b6b894735aba6095230e4422c9023735634b5b24738904d71322af5703d32f6878e1c2d68e85479fc7414496c71240132badde9bc7f70d9e8623acab34bc2e10457f0d767d2124c0470ac274cfe390e0ab46118c2de20466df2ca800ae69854275b3aee6d5e4d6458cdc98414529e932295c26bb7f3b0f7e77fd722bc0fa5f963a56b8f313c8c81d16ffdcc061637dfa9706d620f13f05fda5634676bf11bc562fba0f9fb16fcf97935a4657c717dcca4c40546b4ef70057cd0e7b7367bd45306a25cd9d10e9a19e91c345dfb224129ef5b8e0fbf82806075de81c601ce272367b8e343b99d3be5cc00ebdcfcce0c851431cd0811bc80817c60b41369b00a053dfee13a930588bbee8c13a3ac418ef2c44f086979e8b0f9f809556ce982cbdb7a345f4b2299f3ae7cb17bdf8fcca756412fc690022830e8cc77958c246a4c5c1c6704fd2d2becc8069b5c4f3fb6a5ad1ea54dcca42c5c82e412036a9002bf70a51ada21f011f651148b0ee0e3a872dcc66b238e2ab1b049a32c0e602adffc778b102c430716b1b44c1f6b716cee4b2b177bc4af25a1603d904bac00a9f6cf23cf4c3eae04a36d115c38d51e64c5533a3e17c5272bb2c0e51905158555b754f2edbb6f658853d66b620db93555005f030fa705b4fec010d20be89ae4ced22716fbe57fbea0deb432778ac1dfd75814630735597a1fb090181ef4f21d5cc0db4c674e22c8a5100c3ae3b5f3dd0dcb06e9f188710297b7408d9470f45801d49c16d01ee82fee8b458de78e90a007269ec5f32407cef75dd01a0ea35c2c00f57550849f7ec6b98fb5080aae8a2a4c28b3433b10c6f65b7c20e5a85354b8f5ad26ac38f04a9878dbc706f2a67f7ed766447d22f431d85baed0f4b3a64b7ccb6d79dc1adf5ffa383c971952c8b24396230a0837dc70058ecb10c31b36587f276dac53bc5419866861d628dd1b83e1da58550ac2a044b9ff75222ecbe98f2342839e31c54cf7ca39ca3e9c79f3720645bed90fdaa9cd6c45f0b05cca2a6ba656547c961557f0676ad7df654deb87254749f4d05c049f6953c9d0efee323f4218356a591fbae9a2dd7ed123d4dda5f4732eed6fe3f51d16886d50315c9c5737ef1508fdf8d00a3002babc015b9ec5da0c17f66a611bcd1451a500b5cdae2e5ac0db29602de8a9950c4c92ddd5b6da848898cab893fc9f64b9ff90a470ed0451f52f087bb587a09d6bbaf4886b619ccdeb0d03c072e7941e7aefb00 false -check_ring_signature d734f2ab429a78cc1818c6946542a196053f0bb9a81741398ec3a9b4d6f428f4 4fb145af4081c3305414f9dfdd81479451cb625c7c5695807b2af0facad5b528 1 10c0a6d3f058b0a56d497dcefac54185b03954f19d0132941ee7ac8214eaf9a3 8b9997b88e01915e06d03929e61ecbec2500693d909a6146c0a1b4c3b86187f1fce487f85f2dc63343bc1dbcf9f3d1850b6c8e2f138bd42fcf21816da343050c false -check_ring_signature 45a02631bf0accdc6652bebf60cf98d040a85fad9794e59480890eb285ae5af5 7a02e637531c17d361a0d0646de4571627bf1f590900db7f46c4db9106b71f1f 52 ecc3b1dc582eedba3ecebcaa004e79d569e7b7920b3c492fc6899918754074b1 99ece3d6e1d9e1a81aae39229668dc68f803332e45c2848b711dcfaae378c1d7 0ab7bdcad2d1c4101ba86d719880dcf8b3b9408353a70c993123d01c9d7d04cc a8ec5021b5c4789fb89aa2cca8199d081a311e1118f13f17a0a20a385a21b9dc 386dc043ba06c2f952b772a1c6027f61a42e5b1c60212e7b60900d6598ec57da 075b8bf5b28da9a8d7b0f31c7926a0b39538872575ab10bf5dd315aaf84f7605 a90af15d9dc38ee97e2626b7d106e84ceac22326f96f649c76b53174af1356f1 df801a2236a440caf6d3d0a435a9a2ea9102a7d8e28987ae3eece0f5cecc6ebc f34ff09d501f61187829ebf11f60989c8fb292811e00d6fb95378b630cd9a661 91e4792f31294835ebe49f22f2a56b22da92d150fdbe03b75983fbb8b4ecbd87 a2756572e38a72a5904bd0523a7003414a292121973cc7ec82b090cbb57d7841 d00207e83957969a1c0b99f1b84eee3e0c22342bbecb87766906a1b1cd5a488f 2fec82026129e0221aa17d47187da29d62d76138837f9a3abb1b269e2b09d63e 6318b4fc81c1597ac2107515e4f67590e701ec716d459fb9da7d1a643a258923 ac62add03c3bb919f72fc1990d8ac520aed13afc4a4ca0edf8df9caf83dbcfe7 1a2174226e49d13e16c1528f433efc3947bc8c7ae1eafe85c10ecc047e221c9b 13cea838137c17d960e5b052ce88d7f724df4e1d4b1bb7484f6e44a07acbf370 473c030a119caa631c4532aea1193121ed8b2161efeb63a87716d8be972fa481 481e44e5820d54684388e0bbfcc61d98ae629058367ec290ed40caa5a289eb3a ae970ec5cbe3a800aa68e10a5f7a4e04e70a7074214235d5ec53c7e7b384074e 6ec305d859a4b0ac3b8789cadead9dcb38b987509259e5fddafa098de1d43719 0b73835b82a497a25cbecf615f7eedeaea9936fba5cc78932a3f85666bfc8e18 99c7e97a3299f9bbf42e3985e324b35ebbc8f0f151def4df6a84dca5825d0cbe 3ed0caa76d802c10be701f357ab14ce0bb35be26112f2df11409b9500de63bc3 97039dd31b470308d7ea3deb0b992241bd1ec1c74bd24727f0c15252e7bcb5ec 7c5d88e03fa44478de8a72d1534b7ffe57b446b55cf8d152df41fd241b461d19 eb99602d97f4947a376bdf84132ab75b5ca21e4d0a88c248c792adecbcc2b24d 0b5bb36021f6c81d7afb4265ce4e7ee6d2da20c29e80bd1a16d5a01b54a75c35 c83ebcf021da04949ce3ec23f49c5df0be06a7b6d45d97e377a7acac3dfa2787 5cddad888878471d57f1ea36ff657c987ee86f6e1e7ba5276d996f9d565be8f9 b70067d0cc5bf0ad1eac2bcf0cc081be3e3ab5526ac44be1d2d8494df1d3c812 801d834a10dc9c60eafc1b5f0eb8167fe3449bffbaace76bc9cfba77fd2f37ee 7b8f96447c3cbc4ed9c5a647332a1624c0e4c9e685e6cf75260b1bc8d0a93a6b 9d2a5e7a59203561edecba72fc70f5eccbdfe250b173d28c77337f009954b0ff 8965ef24d6ffa810073f54168058f23d9df0015d6ba5bde1a83e2775ae544951 2bda2e1283776c60045ff6e5e0e2e9b31c6dd425d7504d70cbef3c066d25a63b 8c6c2ffa72e0e3438c1fcbbe3f1426910dcc934b53e999abfd65b3636b3ef29b ec05cfc23054f7e496e382abcf8b1fc9bfb3056740040454df3f6e59919244f4 a73b8bb49e5ca8d4d942ee9e610e6d9ae0280e9eaee2637fece87af4318cfa7a 05c3c3f0670fcf7e7d561a55226269b79e66b7099e6ef151f498546f242870b8 1a448f986803995831607ec209f2f20aab18b64a10430fc9af77b09e04ce5123 a28f1127ef8cedd6bbdfbe3c3f506067800e0a0331145b3a4357c180f7927051 c9290a58b0d3c96fcf80318ba9cc9525f356aa167b0b7d8303d95f121a8ca275 c6dc0bc950509ac1358f11f2df7ca8c7aa415586ff88ca49ac948970e7bde93b 742a287c9cc86464991cfa1eb78dae6ddbd82d5217fcc1670a9206d955d8cb2e 7f97f463135d720b59ee0d28898486aa421ec598264511560530e22e743f4ca9 d6fd3ad49e4d76bf53847e611c51f0e63a2911815a640a6902952023149dd33b b76cbb19cbf824c13ba567d299a638bf957b163187c5065019195e9c64440528 5c45ede7004cf920d421c1d3238deb91d2e3b4e1bc4f8d50f1199c571135b515 921f4b8d21144b9401be367b4319c4e1aa0b18ddc86befa5a28d5ba02f273bd6 25e9dd77e70e48648f315f726875a9fe091006da5c5ceb36388e4c621cca414a 704491b2ca7f9cc686ae1d6b07f416d0f7bdd16e0e020974593a7f6903390dd5 42fafbadc285fc398d1b6b57e2a01cf06919b565885e3f2840a7e59eb551670b6df6e10eafa7a4f69f6d0d65226e9de490463c23c6eaddb225b98978b8c33800e6b0e0533c327b076f8f5c94bbb687e3b1e553317318b6ec36faf3d053c87e05ef4ec9c7325c5241b17a408e1781db205f9188919e8ba9c22a7b607839d4310785eac183ba1720e2ab7b1642f2330a61ec43173bf0fafe25af7f699c4eaf2b071a51b469704e03a57e22cee5ca8d4bd70bd241e7181387ca68ad5270a154600a230bf44778ea7691bb0664f9f32c3a27ecaed6f4156d03a3f474eac85814c40aa19ac23751659b8ed4fb3cacf487e4c801d2aba9f4d8af755e5cb88134f7ca0e439e30871091bfefdd51dc24e76a8841fc5f71666ff759a25d0707747025e0067315e66fcc26738b9f102005656cdab807bd33c2fc353c3b74bcd32abfa2f5040593aa88bdd433f862eeb27ac5c6ed12b8d862d32b07f0829702c24c91c7b602f6d6ab394c0b9fd7db43517d1a3dc68a4b2119060f8b821bf9b1431eaf9a9f0cd352a4e9454487155139a1f31420359e80b67ca7f9d4be4fa41d79587266e20164fbf941cd2f4ffbc471165963e8705ea9cff015218a84aa40b0f10239a83f0ce56bf47c2bc9ac6ee31b6f36bba110e656165c83d552efe9ab1dfe0c745f51010676ed6da2f778036381426e1d030130daea0a1881929b9845aa9d8d9940130d1f086f09ead47632dbc95996a5495c98c05c8d6226e5ca6ffd06d40e9a1bf80caf43376be3dcb36104b67efbc24b8fb321af5379492d94b09c9dccccaebed90df3fdc7af8430a3dfb616bca9b5b414328cd0a659c7e080f099d9cab9e6430906318d9c3a98475890b5612f31e3e6737a5fb331d4370a3258f822be8ef4f6ce0ac12a781cbe00834d7dc96e0979235b908e352f2093a332fa599eaecb3323090c45900e1302490882b1525df11902b6cf63878b763909ce5dc07ce6039043f60056a03f9266a6b8087ea65d7806e5086546575ce11f97924f70278c962152ec0e324dcc26c5cd03c217b8f2d60c55e45b60c6ab3dea03d2da0b37e247836e50058fcf47c53fd226ef71655ea22a518a30db0b8b2b75b4c7636b67a9a05ca16300b79d46aeb26e34b210c3783a026f956aad295d54a785e0c1424e55097202f00824a6b33f0f3537cf1edf2284ff48c6c8bad81dbdb542805986522c9bf703d40343ba91a9c012f7d1f84cae3f4e29054e95cc5f8b03f3e08e741d039b601e9309bd1dc5773cd3f3b156083c0ef15af88680ed36e2867ecc046d3f1a9a074b580aa755bd19f01219008021d0890c842a2158a8228e1a3e0efd7f1fc8265868cb051b4942bcf72763628aa19a078167a878b8e35f6c020a19dee1ebc54fa408ea0dfcb7d951acfd005c6b6ea16f784382fbc46929e5b41d3d836b02e3e4f0ac750aa86e9eba842e1c4c26913154f700f2f1948c8a7d1b6bd3c907b52c9e3c01ee0d0203e37688159b6282f4332e60af2a0f964b1f23b018e4b9811311123651e808c8a66171e1e755429eb5b7a7e35ae8539ec4ae55b4c69d5527cd345406e42a03477e0687efb4f5265453db1d060862c23db3cc5c746ffe6101a0a6c0367dfb058cf85c030e5fa792c2267e5138fb8018784fdcf4e821f7d3406df4d2dc6233018f846a6afd156649c0efec03184d7ae4290c98c60a687223088927faebcf350c1bd44dbd95913b9a2654a52b58940a1461395747375f924e9e488f3de1b6f4011e312708113dfe6a9226eda66e537782b5d137e8bece99889771f622696d2a09a606293c51e7022fbc2590c78eb8331791f5c27f55bdbe256029d02397fbe209732a9bfab6abaf55782781ca8bc3d675b3dfdbe481d0b5b8deeba2bef671230b876c56c111ee62993404829aa7f540d59d4272784fe71b53d94264fb96d6020c9036fa0b60c83591230ffb5daf8bdb1219c6e886bf7ede4f13c3822129957a066de79cb242924a25e9f3208ba9c7001d1687f03f2ca298138464343db89da700d7a88c37b13858be5448fbb0392a31bb860238998029b54c5ed71f7e0529150a280839eff805da4baf4f0be612f472283a43a58319ce9a266a4174ee2176e202a97f3691ac3243df92ad398690fe5bf39d45b0702997f56aa703a58c22dc3604847c5aa2e7fe295aaa663ce19576a0f1d17a972693bcf4508aac301c3950650e52015a738bf4c5cf4bc56e5e4354cd3f2ad4bbc9f716fa24606be006fcb7e70e746a0476fbca587e665edf87707b27d2bc5deac53186a42a67cc7bce0b85dc0e8ee2c0152814afa11c8e361747ea10667a8a03eade3d9271726ccf2d956c9f088c1081e5a6d99c150d8952b9f69dce23783f0f48dc67d7908f0e85fdb7b86a04d015735187e4e7c62062d84e002ca3ea4c35c9340a452d753b81345cf05e800ce207b3478817f3c49fc22165c892272b216b266bc7e4a8aedcfa0fa91b1bb907d7fbc314db754012ccd7b884116d4586d352f358bbdd6d750470d27770564b05d090218c02fdb9b1cf296ac119f3b4a788d7495b3542d847d4788d3bc4dd7901a27b46ccf55b6b086c0c9904ff5d6a2f9955005460703944697786f0f2856703bb4db46120146eac737fc992cb3eb3f4a5070036e2e0dce51b4209087b4ef605f0fd3f116d61aa6bcb38938cb72b1f439e97ac160e40643b9dea3cb809ab0303b6f26887b6d4e09cbc2f5f9c2b26269d838920975e997f5ac38ec8c4ab16170c27b102d621a843b3c90cebd8860a44a383a9a39b5cf993a7b5393bebf8c7120f1f4c0de8f99cf6cc983edf4bbd7b130599848568695d3b8d3e12a48b03878b0692cbca1e61f8c49731771227b2be2e6e3cb919d5f7754fc5bc893410be811a0cdf4ead315cf20c43e1e857105a19182a34c544f1cfc8459df02046ebf5896e0f87cf45b2c8077ffa42aa94e6b9089cf1d2ad0fa70c96e9712d679569b6701e0df420d3b9d4b924bc43db578b85dc218cc3625e7971e7082f8c56bf0df815d8041c984b2edac5a66d30313b1dc310f7c53dfbf56710530fe3b56884317f1e39014c4a3522498e954010bbc5f4b5592261dc90f3c8da84dac72e12d4363ca4c307c5c2a7c74d4de31aa70661b9f2a0e618bce4a930174ebb3dc35f705a0fba030d0adea3a2ffb48017d1474b07995a69dde946282c1bcbd4ca297f0242be21ac0d8529e6039115a136a7506fbeca2f2dffd44b54e8d02e8dfe1660cad46a1c35071922acd5ebf0e7b1772744357601c3a8ad7d13cd551271a3af1a9ccf103ed70e20447ede3f0fd4e3755a2b75507791220e57e3c533def542dd9b22da99758e0fbeae30036ade78f520f68ef90afe4613a413ed3039a352dfa3484525fb14b4004442c8c8065960fe1263d13a1c6984540e38ac61a5fcbd2c0a44d82f4fc4cd09245f37a7538f6e5fa67b1fb671627eda614a3d9f37057e5ab6b2e22fb7d3030113b1f4198a9357b4f812ec177817cf6bc8472eea1e46d45893f082e99a65090eb1a6c3fda37aa84fc3e2febf989558b6be0d1beec0c4a435cb2672cc513da70a8e7d06b39cc62938d1c66b3c9191bbb1d13f9d96553a2795898f26d889f97c08e65475302a83bc0d8facb1aacbce85374aeb32d2092f3abb7dc93d3119acce00899a33b5c02962270a989028c3123d1c48aea24a2544ea70ef17ea9f3063c407c6c426576eba823c30b99ff1b59d37d4603b1b7c12afc1bf4e8d7f98f98f460aefe420e86d414c2833ac14553b67fc04a47f6b7ffeeb402bca5762c2a39c350f6533b375e1440ea5a96d3632f5553bc8d8a2cb2c1a05e1731fd594435af2790d2bc8f6f10d3db38f0813f20212bc33e83a8a0e49e10f3eaa7958cc13dbaba202e4a132311aeca61e3a46d4556e1f1bd1262818ca614ba7162885ba7d4eed4308155661e7db4d09e9a279808c31ab02f08405b715f6eee2797e0a1827513623074b4eafc689465f675216b2dda70e41135fc760180af74ad443ec9f0ad7b3be04ed19e93148aae98a9836b5667aff839f05c8e9f60270775b9142aa89b8fbe708997bad3b2cd765021684941753f09947f6c66199df9a207f2b6e67d028c23d051cb0a180739bf476c431cac627ca72c8147c8ce1caaf84fcb75a1087ffcc910d949622d2f5e90803d3ef6ca4046b2698b869bafaeeed5b9644f65ba342e7700c20d4c2ae890c17f5370a40419af8fb0f44a45d0ef9896960ac5d765740dae900b816283e7fa0eaebb0cf4456c7a745ec584d07a1567ea495765892a73c088d090436a0183618776fbe6c5fbb5ac72b3123a215488adf49b2053f4f824882520a38204932e2fcb3cf710e925e68fb667d40a293806bc345bf5f1d5216f8af9f03644355a3b0e6f2fccb42fae72b33242e85c46af49e153cb963c1be0f5395a20a7e307e1d2b85c3b70a76db1ab41a68e5e9483185ba1aed4e9af71f83ed805f0070f6e4db671ae2064f7ac379cda4a14dd5f3e7af5b041304958663fe81e2800b489f4f24c91ac3ec04cc9c0567c2ab599aa2c02eb921aa0a13de4eecf7fd350156d3208da4f99af697c4ada7d0eee099a9e23e582cc702d14d223030b76bf204eda2af1953ead38acf60719196003984b63d09ec165e8a1c6de9dcb31920a90074441aa6c1d995e8df413ede3de171d94538f551b218e4a32eda35f329db4505 true -check_ring_signature 50395014c7a6c05346932ca25916d518c350993a72df0c9997a0c69d94dfe114 e522bddab934c03713931783523e6968cf4b2ae2cb9a31356408c7ceffdcb353 25 99094d19cc3fd029f63822b1aae891fe08295a4e48d64a2b386825d7231c0cbd 8cea9787186d94a0585ee93cf64b1747523c6cb51c756134fd6daaa888462027 31227674e3db2f03910ad32a36ccd54aeaf48ba8dd2832366c31bec8d29cba95 03d300ecc486b5cdfea7639c27cb643bc003a8cb4eed879b8530093a1d703f28 c8a476b4189fb6a6767e0c69602c9cff6ee6de79fccde3d64c8b899b1fcd2d73 af71a22384e68bc455537142b5bb4e178fd8eae6ff88c9125592b8fdb05081c7 bb430026fba6d1a3f8cec9cbedce048a5fd78669341fb1bd55b35550e8af8dd4 d4484e6a5a786dfac8479f2784df49df88b9025a34327625df639231ebaf01d8 b7f946a684e7712b167b332568d3a6d8bfb77a60f3a5c54f2bdbfb361f3c9aee f4a227e6aef78ba1aff440f006be13f830de18004fbb6f967f0d3cb252ac89b4 057f9e16d7c4271758e484e7f9cab4647d1b87c4c317207c599cf2e61c77566d 7997ac64933168ba00f1b62a2f52743922dcb54d02594557983496bd40999c6f 5c4563c954598c4e5427d8d496b07fa3ddeb8f2150d5d6e747d4e93bf6101c69 bb41f371a20760bbb766dbcd6495955fd7e5c130b86dda00e07873b108ce4edf f70f03f546980c4b93e6f29d5cc1650d1bde94c3bb5abb96f695101e3deb87fc 9a216ea8ca697124e7f2ceab308a14c8da7b2bfdc1bfd7e8d471983f29f9e93e c5edb05f297ea75b58a8490b783ffdd385e9befb4dd031e2dd7546c9892135a6 a70ce26e1c3515aff80fa8f66d2d502ab07f6fe2e76caa27954bdf78e84a5aa4 0b4f7e8eeccc274e4847b9d41ef9e63b5565c65a1846996bbb00fddedc4c9316 cd0ed8142c10b1625023b0052cf939d3cc1c4bacf6a276c975d2417537fa8d30 524251e525b9717f9ef464b39f696d0b9c94e89b167c68351fc3ec5095c7eab4 499bd397dcafe052aef39abc01c943a9143f34cb260eb4ee89f699b894931b97 68854000c6de6a2f1b4fb183ee2e117cf9f1c230e177669c8af750d893f39dea 93ae79643a6ef5f3d9aa8f28f01b00f0fb54074eae53aa9c1ca3009495927aef cbf193827668cbeb5125f64e729ecdeeceb77ef6a9cb2c4902132ee3e8d40230 3d6a6bc81fed0798db33ce6886eae0f2a314ac571ff79ff3dd27056433fc910d33bdbfed77c38c68fa35c378975866f1a2842a85e13ff2cf4e0b3747fc47930deea656bc75d488956c783f92e0e28fe66d383b0cde909348c71429c822fc380256e7f93a9bf57beaf2b199b7a212d68ee38c1a407e0c157dce43e8b108f5c40d838da63c8443f82152644433c13040e4accd3a37b390fdff01ac1f833e20db016d6f954d8578bb0ba5215aeb1cc00e817de938b5e5c771cf2e8719c131f10e0265d89746ba5cf8098c612184615f57cbeb5912e8ad72db7457c6c889f099a40db99a85fcaef44b48dab7f08d662ffc167061409c03399c478ce406703126d40953d7e5aa7f9517b4248ff34922e09abd03b8a451e601c2a099a8fed59112c7007ac413ea8d68b41a062cd401dc829161997305665b6f2a02073fe256c5c21b080256f6088a563244b1f8b2bae3c6a0fc7ac5634359a2979b2295b6e02052a30432acf0e7ef4099ac605303d9d177667478efc4bc2d7cd68440dbfea3e67a2205bff0c3346df72c2e312022c593d8acb6027f1ca08706a7aac6adfbd97ca1240804a1e5587a6b51cb6812f198f7d3d073787c455cfdc7dc594ba6b6b7a385580ea46a3b81191d7ff539518b413730603a6cd01fa34abd426564634604344a4f0e7f36a56ddb6b331ff68b810b98a192a91a71820c3d9afab78704de4226e6920065ff65b8bc0ca69c97990f2d99b8008e41c510249fc2f5e89455fb600374660e1ded17b5046f292da21a2ae31fe6302d92348a6f395d9405c520e6403b91e70909c4e8747cdfd6d58a5a73de039e958321aad93544a25512c1e7fa038957410df6d64ded75a9ca59ab83b6d905d0987e174b9ce450ca459f1887a167a0981a0d5e087a559588439d25e5cc2aa0a53c245b766a36e83c83129d01de484bd4a405dbf559b110f3c2feffc1736fece9503cfd3495ed010f0308ab03048eda2a820583b0a469df9bcf3cdf8b63411414ed5459b378631963ae72a1c3882026c2630115cd9f21a4d1fc21d2986cce4568c3437720e236dce22a6a3d163b5755bd9f0b3db53881158196cdfac0d847cff0d58bc90d029cf9be524eb741e48383d35c0c6fc191b8548c891b3d64d257a6f417b1fd2d7f2aa7ce6bd674fb9f3c846aa2029ad03cfa88b2d03c4fdb59de3ab1b565f35d4bbf4c29145d3546a65a2e3274031c66ce5a21505017d4b7c08d09e7af8f4f0e8eeecf2c1bd4e31ff119dd17360f708e44c8334fa9533d18fe88f1a5cf9c234b4bde42ba67f7a0d761acb4a97b0ea9c7b8e9b775977bbc36e62a5a141983ef57f6a45e0a3988d1a7a24bc5acc40af75d48b0daaa139588e7b10d83c05931380115763973b4c1d9cd24e4fe687908311a8acfe35dcf1ddfd25b6d5cc5a39a5814b7cc88093f462494f8084311da03e810e7ceb3aba57f735721c941bb2f197ac62cc29a21d390295867b909a8f801bc50f5607fe6aff2427acc94f7f0cb42384a5c455eed0751314ae93b6c63f200bc76e2d04048d1adac5cb93ed18b584eca3198ef3335bcf7a77f94d6323fa900dcc894ce6d88e4e9550102e836c0949edc7ef05af33c06915f0b2d765b0b700cbcac3c0cd8ec479214f0764eae814ce3dd60c4d82d8e2a4a11c97a939392710b8d69d400c1720df3003b9d8f2449ed443ad8a13cea4bb67a4feb979f1eea2606212a24d59c40345d4e629eb622bf1ed4762b523d0fb2c7f4eb00a4f7a375ff0d3ace13fc7971854786d2018782caf26c4f42ec502a6d640d8bcab60cabcc9b071f2bc95477e415f591b9e24488f5b4490bb249231e0cd214ee92c9842424d9022bf6e715e9e56b816e26fe0c31e7d1de0dc8150a501965597e4986695e49140d54bc509e4df5be3f4ac10307376872fdfb3715727d76da5125bda3a8edae340f4dcf86775571d537dcee3fc4914f2c2b0128b28985302976b97a512f4985f60b606ccad796e9be6a614a7556a41808a1af839d54753cac68921e3c59910403010a23977f72428bea65f072dcd9d9e012ee2cd44926dc94f0027c306bd007250e2fb7283a0936b2aa4382f659e10e691e352e49c1f5c62d9e2155fa5cbc477b072c3024b344746c3afbbaa0dde56cb6abb0f042e9c03755ef1c4b42ba1ec3dd0d9215dff39de4a54ff2c83bbbdd20e19c817bae61fc68eab7abe65d8a5537e00e6f6a5f7f0806399cfd4d06d49aa8a8f64bb2c5927a94a95113c0fb791f491003 false -check_ring_signature 57b42d5f87ec871f9d539e2f1c0d3954435d0f35a8aab8cd7f539fe6f1b8235f 6aaf13f5a6ef0ac163745afa35ac566f8164a32e778d5b2e74a7537bb364cdf1 1 dd28b81df56556f11417b5e31629dba0194c38f0856866d48c3dac80877c9498 9f21cadfc9d0ede2071362fe22fb9e451a89b3339f0227547f249ef8ce9368f5672efdb9a26aafa62fe911069f41f62ffb3c0bca841922adc6a1ce1e4419cb04 false -check_ring_signature cc7bc05ff7d0da18a71693f4e282639e416d6d42700d40f6f89e11fbe21af13b b74be110e8c29af65b13e7b502409dc55c73ae09734fbfaab6f5db7b52fac21b 28 0effeb9ff22dd928dad472f6a7f8df95e6c39524f8d747f9c9bfe76c61075d47 33728137257602bd17a29fca6fe66cc23685da86bf92c305c4114bf8792f7530 07d570a991c7e7e3b72170f7ef92e755510244d0bcedabb6633c8b63eba261ef 17e8fe4799db6a4bc7dd53e1eab1dbbe1a2ae555f09ec9152e00a0ad382c89b4 ab2b6e7da64773e34c349144655b0aeed7c88bb37598cd75b583101ef82e0643 bf52ccf08e526ffccbd05d9766d8c506469e3229a1cf56283ca058f4de32ba41 851182b0051964e6f884347be670b45c27a6b503faba19db0760815269c35410 d239e8ab0c8101a0a939fecbbec658b3d9a5edc2fbaa10bcc6b26bccdc3e5c8a d834386f939a3446eeb893fc795ca437b26af07e34d3955bb65d82933f2a2edf 3b2aae85a0c8c37ffdf4175afcd60619bead7e6abed0ba250804d5dedf97d9f1 d55ebac09291df7ceb23a8246c15d6da68659e42ef92c861578d0c6673f49c2d 7b721e4847e0f64b92a3c590213e5a404ae35794882daf5dbb7b91f8468c2114 393809bf09aed7ed5e68b3f042fc25fed600f328a6016afcda188c698c259d74 542294c107769ca4c0aa6c6c2e329cc09c250cfb683d7c4d4bbd94eb48417c3a 2e70112eaffc4a87427907423f62525932f3fb14be0e1aded5dd3d03cd862f26 8e900c4220a401cc58488a076e2cf1962d2b61de2ac0509e09dbfabf7258170f 8e2478e32ec2aa3e2463e0bc9a3c10c877fb9df305dfcd8f1304f64074845108 a09485b65c8b0b76d6c8135dce6f9e27bf8dc4d98eb404c485e3592d7c2a6ba6 20b0bac6ebd2e6dd4545d469a687c864c02eb3acb4534086fc4caa833cf0dcf9 de353fe9bf02ebe75fd4fe318362fd5041a690f599e1ec1a2497b1a458f12143 99c84b8ab8a0ad36aecf578db55b65f30df43f33b66f293073f5851d46cb0990 4bdf1fe88b9341a6a718cb3de9826e710b956fa69a0fc86628fdc2bdd4c4fe2d c263da92d4df28b4c7f69e28254357d2e03c23702208e8b5ba929970243aa151 8642885b3eda033472d2967fd07185ad00838f892b50927a50d0a88990b5ac3c fd359163ddc64985438cb5622b14de9acd7bf0c34624ba1dbc73abdbb3cfc2c0 fab1e17e4c6b0f2579a39953c224c97197d8204e78cb384c7f5a78ea72ad2f19 df974cb376f995c2ab1a83eebb90db45bd4f05c6a17df73dfd5dc9e34b8132ad f9f0f8cfe4332b62d4ed3ab8e8e0f793690c0de0b4af23b52cd5c136b3cc9c2a 1091067752bd78822c7f272c8c0ad6258cc5241214965e7804d1feb63fd8310293ac47970183d7b6d44dd535488020fb5f6e7b78a7ed86b516baeb26541f08063ffd0f1d803a3e97386a0276211599886e57f7b18780b2c0f1e8552664cb410d3987060706041dc342c9ca6bdc513ebaf9c350511a4b7673da23516d9616ff0cf1793584013d09b13887ff85327320b943ff89dd056099140d47fc94568c2f037028d81c54300830eb008377e4e4ffed17f9de004439901c22ddd51a30063c0d63505a0e9d1767e6fba7d7f0d67b3467892a2b9b80334383e024594afd45da00474113cb731aeeb1be4e1c7652818c9b01cf72a8cea45daa96efa383c09e450379d47579a117e2a3e2fc15817348f6f490cbbd53d2369880e0e4e410e4340f0312a2a88cb11d17d167e4939bb8cca0c7d38c3a5bc585e73d6032348ce154a901d982ba6d8b9558ec8386a0f2f7544d1897f9364a0c1f648e56989459f9af6f090b65970a686c26088d17a68e246f104e5578548e4cd7dc2c19f3e6c014aa4908fd89bb8279fac5434eb5f004b6aef2f59f1bf8a01ea04b7a908ddb8c2a35890fa35a28042c0d0a355635918b182d976912c319efa5dac472151bdfc74ba0d4043cd499cd8d8ef6c56263562ebfa5c1096a14065d7f450ed651db9ee9e3c5d801a632afcd2176d99df21d9900ecd2e20eb960f539c90c104335d1d4d57467250fc38d120562d62268f95629e06af40e33f659c1489eb264136160de0a56740b0beadab2ccfa249bb432778888de7debb12698efec44b2ba8bb47526b782d1cd04b0e9a526999503d00b7de9adf562720f49749e13ea4221b1828ce11e6a8582066389ab323c70fce14e14e75353dfb8bd345ee6b6de20741c3e4d97cc8536c70043c44ce2d2a18c2beed8e0e37fbb54c89027f7552d1103849e17e12a7d3bf300485499042f66439182f73635e87b432d49cd13dd6fdbc1227f448e516c36cc0febe6b9ac63b7626e6ae47562deaae990d43aa5b9b17578e213c57c37f2240f07a1b2e3150cb45e0957a271524eea40c11a9a22821c217669164694bb9d1920067c4b08d68353625ae46ec8ac7742465392cb42783428b9ea06034a1f66a4d70b6e3a1f13e6b412a393140afe0d3c724703873707e665eca346fc4f5219b1560db11df3d2774f32c59e37d69aa185864caf825081ed1466119ef7a8c6d09b0c0b21ff74e8e4c36543c53aeadc765876aa95c47a14c330fc6fb5e37fb9c3476402a838fe2a1ecbcb86c61456bcc656649250bc4232287f56c424b0804208748c0b15c4345c0aa26925ac3a566f93a0c11f6b05594d78596b0e76d9e7e8a673b7037d174aef4c4abb09fcdafb77e39c9586d6f239a8c2095d863e53d5b6ff39f10681a508c337f46fbd2c89de0e385a5d2476a4cebf9d0a244557151142421d2300976d788aa4fed15ddeb03a2814ba91433876bf2c515ca6c0d013cbd3a8dd4d0e51b2020560f5be5323a22c3287acf6e3a7b4c23afb8e0b683656959d95d0f5009bc01219a83a5e25b170799f0062a81f27a8293e2120ce2c8c0dfd74d2d9420d83eb69c184d0c818a10c21aaf7083a0e643fc902af1b1b45a0c00a7fc1546e00e4a5650aa54d7d064464035a0ef3a6477aa58729b5796c0b7a6aafb16a71230a890859cabb253df473de4afc863f0594642ce2eba200f7b769ec89662990b30bedc4bc1f9c850f8f8a96e3ce2d93913188820a7d03893ce2769e166cf78d1105ab9f3694034de31ec68905f2006894bc443f537dac1c1acd3dd949421b33390218b77cf268314edd3078843b53e3cea63bb1022e9c1565ed010a2b1d83ca9b0f0e0e31d3d4beacc4c80bcc6f15c1ad34971dbb488a45bfbba98d5c90e9e88a09d614bc712e80efe3eefac0762a12e2cf30c15e1101d560237c3b0134e2156400c0878e6242654dabf3fb5f2fef42035cd724023be37b57005c84b4a4ea749e06ac2dd89e3605387039de25e172aaac2d7d8a3d5fca267d2f142a771450cbaa0a5e634c59bdfd691380cd7d3f33841509dcd76487fd54a9b1680a07184454190334fcdc7f77c8a4f0d1f1818d2a475b52964049f348cde4fb7bf9a1fb9a0a990c87ffee335164a4e3a94894e00d97adf3428354e9b26758751c5f380cf77e630a6e303a4e309eb2e3c741b872701c4784d4cc897793fb5157a5aa09e9771b8d05aa4145bc116951a5a32f1400777db826d26d09e109bd0103f33fdb8c3f6265064625109a4c0f5f124d547ed359df237c9c6cb7fd09723b8e648c15234070a7043e20e22188464199d151a97746972afea275cfb05659bf6de59e11c98362a705d6004919ca9a2e7a6f83e5d11cc0576a07ae6aef82194b1e5bcd95e3200cfa052e8f2f80bca541cca67fed3bda16b5e58de95fb56be06817f7e5be7dec2a990a0bf7b8e1ec544399ecf632cf4704f49206dc7347e5d3ed05ad397a84c59d54015401b4ff7261251b7f66ecc36c86774460f1fec7fdb3c61b5b1ed29826963205 true -check_ring_signature 2c65d67c67dc2427225d2b590d0d55fe958b11bec8fdd58ce4a947bad4a0fcaa ea567f314d02af402aff547226d2973cfb5203ac8888e56548b17a997ce40802 162 1a21400c2e1ade9fcdd97d8adc989c5907592bcaf432deaeee9a695c887a5384 cbe8c8506226b797448d98ea7afeead5ee5dd0900dfefd4fb3b84f83c8134683 0976ace3df7cae00f6ebd9f0ae44b67da5cd439ff40eced7d8d1405954aa02e7 65d0fd47a176602e1864d922cb8012da088efbaa4ea6aeef3b502758b6f8effa b6dfaeeb5aadd902a273fd4df1569b18841ae4da435a8cc45a2263d84e563c1a cc312991705f35a724468b5a3079e4498e062a1eeb550fdf584bd27d61bded18 b6cee558fe20a2ba6d3ba11091547f47f0ed358476581203db2e4f423a28b241 a8e832473098798b2395cebb5251338efef9a598b72e671eb62026286d83b700 8739f7927696c938ad40bf0fd3d24f8eaf06ef1b48f3b4acad1ab19f821dca0c ea668bc5bea84c7a5960419df2d28199bd99eb34d146c2ea50c97a2f184b7dff ab62d3abf9358f81970e9850fe7ad0d9af87d5faebfac7cf0db55e9d3f8080fc f3b6ac60a92d7f3e93c8456775594868ab610862ed14dbc54442db2a667e6f28 da867b9cba92365dd8482c2a22426cb640d097b9e17a369e67f31d1f2d215ac5 d157a3b5ff0659771a748b6cbcf57d3a0c7446feead92a9c04ecdbfa8e97d3da ea57a1baae69dd047e5cb36de19fedb6435faab1b74c52482c69b52737e0ea76 cc68d2471e14d975ad5c54459bb2621d7a03af93ac975b62942dc93ee79b61be 3ef505b37ea8fd63d0eb3a2a89066593e766cb3565a9de9928953e3889a0078f e69617fddfef05c80337b9ac55510ab48662983abb95d1e58fdfbb855af4feb4 6a2301296143de57768e742f3510621ebbb3a7b8b461144be8ba282d29f6d6d5 1f2c28f7ba194712a73b0243dec6d67c0908ccb478f652c65c0ac53c3229ed81 7477f5af121087333d10f458fc9e9428602a4b13d566c662d3800868eb6b956a b9d7c242e9e7b6cdfe1a5c6e3c8bcf80f2ec2652310fc04104e152a4771fe387 218af5294f46dfebb06204e6c406ac0afd3e4b09da5ad900a03c0a188f8cf012 9e6ba79f690093d5a6704515e67d08b8f06a4275fe9e6c7277cd311b094d720c cf8ad5cfc3e70853ec2a4cefb9dcc8e92608d54650439adf7d166be354a274c0 ba9457d0e179a10be858e3ada950c92826d1d56231dd0beca6843edb4df51d15 056a0157214b04d31da220a421d101b4991e7b317e38daa63e8e74566ce272e6 cbdcef55d520e3353f1fe48abcaf0c61167c5838f528d05de5f2bdba176001da 0ea413eff5c814c86b61e97c302f7e822bf6d828e2d5f378ee3a653b4a0742b7 6fc58064a57e40de83fa76b48a46ed9643ebcaffd85f1b788e46761751c8f72c b2a9691e844e81f2ba07e5fb8256bad0a98154479461b20c369d8d86aacc28c4 0d7020a9f766a0dbbe4a2a31e3b995ad1d47910024dcd3a171ffcb4c30af47a3 b678c1fbe088a75e3b3ee6e42095542feb2d9e0e99489459bdc253597ae525da b75fc87c4135d07070154b6228b6de80564c29f12e291ed8b7b4fe49306af59a cb01713e14766dfcf02332946c616e9946ce15a88155cb26faa3dd4bb23f34cd 75efd6e7afb4e3ef23b53bc4a365fd5312b8bdf0e04f58510c5f66d37c7b4ddf 46643148af8be8901cc4a7f99e5888308ac505ecdb6cea5f6ed104bd635f14da 82c5269a1a4c0133d532aa845bb1b28d2ad010bcd153dd948154dfa164a2c73f 3e7eb8fb9fb339efc1de931154404e9e4975ceea1dc54914ab8a19e6721fbaea 24d81e6e68f65f4c0f4294c87a4fba5544646b92d3dccefa3dbaca1fd6b62158 01cae3bbc44641bf1572d75d819d3a00355175ec38fc62d9aee2a94ee6a66daf ff08eb43fd826031cc9e74b576cfa7f860e0ee99261bfd551e6c3926e40483f6 329c3afefa0843e2585de861e04f99b90eafaa30f626b32eff766b4d04d907f1 1c74b58e38942d61ca20f994cac869114126460d217aee0adc11a591b1fc64c0 2373330c28c40b9ed922765d0bf48ffffc291d1ac272eb160b3e5401ec9775b2 ec8df70c67c9595e7bd51602a5e2348cd28f90326df3d6320c3ebfeef800ccc9 002373b5d30cab1b22faa107d240a76733d8f9b222d570720acc6cad7592472b d2e85f7a876558efc69ce611d0724301616c9cc9d04a78e9a1ef1e4d6549f98e a24e70970167d6bf445d2669a38151b4e791d271a0526e96579658d731dc9ef8 d49d355c8fd5975a1c973772e105a234fc50301fe00b3ee8472187ecbf8084f1 25715b56f4f0b4275cefa0658f17ba1f72c2db4bc4a5cba08a1968d908f06712 a9fe6ae7a1bd7b7c923cba2b788cfec6e0c76ebf3a78d61745e80ebd8d6e2e94 ca923e1051bfc66aafe12881ef80cecaa00ff1705d7e255f62455e1f6a645a20 3825e1efcfc52ce3f41a1645a2d7ddd1b01bf1b632c685e250a2cfda0d5fb17d 865ab5c50e7d161ff9e3761328af7fb83a38a176593fd35891c3fc5981a64755 df8748ca541c5185884a076ca9001684f015ea8d2cb6eead4ad160a266ad6896 ac8dba2956bb66b1d19616ad1ae1f97b8945bd09dabaa58c4a55dfe8a51dd26e 5b78aae2b243ee42c859593f4903e6dc2ded56fed501da9881143f110f6560b3 f4110220605721edce3a902d46b32fc6e5116a432ebbc6a10c34ed4c2d1a1faa d3d6a6795bd5bdae12cad29a3c947c0f5e98e28d7495f7fe349b573d06b50074 cc4eb28116c4493f799640e9c7af4e979af9e37223a4d973b66be499cad68e08 0868c43cf2e572c5a7463db87589102951a278df3413b4eb0f4fd83d3659802e 223f893bab0996abc4fa81c7101f2283dae3c624e7603423a5897e57dce63ffc 74dc5a93c3fd1a42217083da3d372248ed8ed98d956e7f86cfd7591958c43e2b 4398561c19e8b728408e51e15464c95c52c88605d4731f1fa8486cdf2e6e8584 91f54d243acff5c6ea50cd49b855e2d8e3d59d4f258dd42d1b452058dcdb2a61 d9247c66ed55aa1b6e5f192842f93d10233d5d64167ef3f8a18d0587237227fe 3c05b22665ef87508199d408030dddef021aac810ad6ba088fe4b92d5c702b7c 8350750e34a95bb40fb68875d3016deb27fbd38730bc57fe31d94563303fb6f9 30a12d8ba24e6025e14a3f5b3a715feb35f58c50b67bf27367e4da8f0bb902bd 7ea5f8735f3a5e4bc6bbb6ce14030890c497ac86b58c35436365e04d3e47ebe4 8917972122f1be3b5b710f4afee9fe642ac8305e5fa251cb65b6973d1f21bc75 cd27b259d39a6163622a83cf57a55f786d0827983f8bd47298105ad24d07d9c7 e67b3c212b33a87e719a82ca791033eb0beac081d2a57329acdf51f977da0e26 ac52c8ac43ae6497a0b45693dc3a7daef46a6c0a4c371cdf3936667434154c4c 81d665ce3589ed5c65646232f7577d50c5abc1a398d17b43368dc61343b4b3ba 27576e75a7e045904454d9e954ea3ec53d064e9f4a5f6640aa925812a9aa4c61 2d818e59d1cd2a3bb9872ad3cae28461b5ab3555d2d7368a32edfa65da2cf29e 71439a78518c554552078af887a6bffbe871e305f9b6b98392d46b82ffd6a8cb c5ee9a56badcb53878c8561c003dcc8961e8cf9a80a619f4dfb91a1a42fe28fe 6dc36386149db2ffb19e418505c932d83d2ed2855241505de659f3e7c83178f0 be3272e0235acc3e6c6edd900379391682ca6fd046a99cdb2843153b787d7a51 2305cc9062fab9ad450a66a4e39a9da0e07b83237732992cb5a688b20cb7b5cf 1a587c348831e29e35fca13909325ca395051dfec97a7bbfb0705f450f22b622 257c7d64bf44e5167f2a47c956610abaf1de243fe56a82f683e275fd9b0b11de 00a7048a5a092c5d8dc6dac858f4bd142bf6611e8afb76e5c3b8839cfa0834cd 842dfd32f51260318125bc7f4ff62b6883174c6b01248fd98326b2f8caeab165 02f29b6938f352a67ce6d288f8555b119c553f361b08573c99fc7ca09dcfe478 4c2f4619b5e6252ffc5bd11a0474870097509fb6bf637a68ba9acc5724f6b63c 8e084a80d17b257ae3bbc9c048d2d9b6112b3862f69d2c9fba5a873b69412fdc dff2637c30123ed37a3b569f7f58e6f180d56936a1befe8b1bfae4d5e875735b e768b920aa15577558a793269dd61450451653a905050fc71f35696abd5e58c3 2ce8336e57f76fa88102cafc9b6ba629d3d24a3e2776c9b7a67af82ce937a594 a1a3afbdf80a37454a9186b8b4344504442654f38f303a469def440d04b858fb 42ae034c9c04238c39cff28cd1ae26459d733da7fc66c23b5de5bdddde5fd196 38e9cb9bae86f1d7f03c4cb83bbf15883bd7d127c6b02661a0d061063737e1e4 dd4d47bf14e6e2c7fba1e255ff73c621da0cca33550a5c9cc7da9eedd4c14543 a9065528c51af4402f1e9faa1cb4c5b258ac9cfaba78ca6ad51b1f50cba29330 8fe7b900f328ddd25bcb5fda8fc7924a6198d54324f02fa8974a732a9a9456c6 472bffc1e0bba1d3baeec1b3800c249029cf0604ff2ac46a87170080a4c3de0a c8a151737b4236a52c3844aa92206425f04193b9ba15ac88e27c62ba9c60a60c 767b9889af076020c6df7748884443400b8d075533cc385a463a0462017be10c e3e7e3276ba22eb4748a86081fe1e365826a0dc60a55c68411c64edff69bad9b b160e61c99da3c7970496e788fe7e2f0982f39f3cd37aa042bf044438214d8e9 415c577a2f0207106ca75a86470a27d46c704e405ccd828b2492d0c037e3c947 dae96edc17c8973490334be926a4e9a35def18c7b5e48178dfe12ea8d29b5242 9f9831832078763a2ba8a89088d8d4e44b3dc45b36de19aac9c1d81a530ede1a 5df5d4a1ed07291f88dec6db5d929ef0561e0ff6f356af2c8186fd1b36e4540d f046048e53d979a24c3e76dec54db9ae6aa01cd5f02c1467dcea465118ef5812 cee85d01d7e14057ce476269d5b17d67aae9a7a6b6acf5615fb7bcd7ab95b77f a6cb2133deeccc4ef4ae0e7ed90d1b89a46b4df5c858f6da4e43eeeb88c7bd25 38cb5414c71ef963583b8896aaa3490d3499a8938565ea5ecb9bf6938f8d6d76 190f40112acace7b5e3f5719ce54f4eb6fce0105315a5f7b5e2a3aad5dcf3e56 316e2dd1ed6e2da120a8c617912941326259ad939d74acaa76cf59ed1537db69 65618672f397d282d4a18eec7aaca6439a261da6d668e4d69bd314f168f3612c 836d145fb11329b9025c166504b35ff448c391d8bf0cf732f3a28f53ac4aae9d 145d281415909566c7d058ff560ee8bcfa3c7ebec533948e7c319a3c347d20a5 e88a611cca1795382d02242fdd488798d9c69d46b39c21f2d7dcd6eaea5e29d1 a0dfa76fa345d093483efc7ad0a27f5891d78882a5015b935ba8c519c9cc5a2e ecc9a8dd41feb69c4bb31f0668aaba352ce077c7d8a86023b24e8d5454ab17e0 fe9d42ec67ec3927d73967c91362a44a7611bfe82000c334b277b978c63fdd0d 9c11da30c0d48e54e73af0c293e6d208d7de1761dcc598a15366a4dd2c529710 6be6e0130bd6e4898187379c7d98c0470d9865672575cbae6f2899ba4e854a7d 8207484f083ce528fdda38c7de243e53d4a86346858eba00e0412d784f5c92b9 86bfebd52bf90da55e3113708a4ae85d4c05ee9b16623c330f5fa640865bc880 92e57611babe4e7f6c8b34e86f0745b8fe115b6b91b49617a86f99a625884ca4 cc5c6ac01262085cebc1094e24be4d606ac8dd4b6d3cd6d231d8aba29ec9f6e8 9fc60cae6ab19e328040905c92b7a0996df487303e99f0903c7255681d4f28ee 8701e892fdb0df89095d4819e7587a837b2382b6f0951aadcd690adf71bfa72c 955bb1994662de428627e000a3826a9a55932f99a257affb9d70fed749091abc 0aba49ab1a8c43422941032940810e184bba72b6ebfa126d836b8a2584d0fd93 e018926564473db672f5ad160079e5e0c047a1d2551ecd8565174abefece2d91 6786d375ac0fc3c29b9eae62120ed05cbac58c63537b85597406f97e37490de3 7c9d4b70416f14600539f2b69bf216554c937f68dd94256591aac159ac6114b6 f77cc035f60af3c8dea405d1ea8a952c226e57160dd0477844608cfdc9fd2406 7516ef0cb4c032a1d5f6f2b0166a0cd3326f38243b6feab683d9f74267d22c17 cfc49923ac345335892cf9bc21ff3d0337ed1650dc19e679bca13b18565f88ce 9cb140e2d23a71b877edbcf6f9d65c7a6e93d3bc0fd90ce79d2be73a71815da0 4342e81ba2a5c289e110761fa39918bcddeece1e305aca11f17a83a4d191e551 36859c0b9b3b20e2a5ad47d8b7f6d2106acee8330174bc204d8d8b640b3cd2bc a5e399f2f8a84447183c808a267c2597d8cd7217e0624d211464564f00710c3f e86d2cc218dee2038fda744ac75c261e5a636e97c7064c17454657556fdb69ca 63221397d70dbf4ab38ff853202629e3f86912df765d0b865cbc2ff2946c2494 a3e19da65aea4de2686a19cc0bcd7c6d7abbdaf4fd33fdf1e9e69e71e001f5ad 4cc6ed8a1d19da137563e2418d7623b83ae4f047aab22a9b010938c33a44b065 f4ff7317c12e1efa60c92d0a8616a1a1f7c302d5ce30ce8599f19cfbe96438ec d0fd8642788f75345d176f877b4d85b15c497b6b8d203254451fb4353fed9dc0 921235695227ad030cf34be066e0e36ebfb71d33611db1a44208c83d642f9174 1ebcb8bfa3805ade05877e961804b7f228a74271f7f91b0115292249aed5c449 c4309d55db22737ea311c000f31edc88a24e846421fbae36dbdfbca6b303f134 9ae6a15c563e719640cf06ffe845cd70c2674b672a71bbb71d0c1e99083e3420 273b24661cc87cf215c50f2c307707bd313c0d0836e2c08587b2d66a401048e1 d5bfbe22cbbbf87123bb617709570435d5a2fb2ba3e32e2c8ad614883eaaadfb 620e210676f3a86a37add06b19a7da112349cac1779c25273439d047f7962e72 aa6089b7618e706ba5ffaef7143faa457cda1ad82bda9fc96cbff2dfb1d53f22 9fe4120ef2ecacb37433176dcf5934fe230890f4d8cdcc9f7fe086798db95eb4 4ad2f52ab380e2b391781cb5ae1d145c87d7be1099411b43d80f37833ae3e6ad 098b95f451162d2a7531e38fa648a0d56a232dec8b3b616025cefafe641f7de9 735f2b996ca731000fc807248a74907c2c7cdc1f9ba35f2b2d9b84ef1c678ade 7eb9a2404cd69abca95cbb875533fc84f5a0c49a2d6d4a0175043082f819816e 853089636479f225009b1db70e30e8006612874299edd3449c6df89d839568a9 fa8f5cba0ac4fba9a948222cc647900e337e363ba6e58933367abef9d84469d3 6c9b3faae9b136ba64a69145df0b32d00b21451fb0fe65e4e81b582d7e951108a1cfb1949265e8a9c59d248d9c646bcc44043c12db1a30cda4664cf09d616c0a4dc7e79a4724ba5571ca8db3b1893596163b971bcc1da0cb3f1ee4e6b1a48b0d352b10567942b1f422391e0d93bdd439957e2fdc2ab9caf5129ddb3bee1be209407b31f5fe909106293f8c287ebc24af82469e24652db35541ed6b6db2eb2f01b573a72f7ea378b111b26d5b67d37b7e4e438da4850830b83ec69b76a73677034245a81b2586cd6e273edd02f9ca7ceec54235e0861691f30aafe9e184dbcb0ab36d33ab038efa4dee659b6b1a0c767b399f47ff72dde8779ac8dd87edc1e00e9bbbfb8d38210e5e9b89e0b28dc8c83a1933ccc3dca169f8580e9e06b1df4d080878c18265359f73ee516c60937bcd7bc62a71ed5b415a4f7e1c40b14b67c30a94f22a1f45908ce4797ac38cb7ba7c51127c670b6feeaae639044ca44571a601d0a4a309b03d018e377445e8ddba7ee9d38d1c6f2fa49080ee0cff681bfb1a0be37da741b0166ee316a3c7b356e1b4923db36a3da656b0c1e6ee0e8b25b30c004687ec3a325bd920c8b297435b4c78308092c34ff84a5b09cd4451343588290afd7dc3b63666b4c24b3178b6c69baee490b939b90d380b89c4a9fdd4171c1505570129003adab9e393c10f3668bfebb3846fee41bead7b9dceb75c3cc892400411cbc17c50bbf7b333ef355de16a5cf9e6a878c23221f66672b4c7a31494850cad738f757aa161350cec3e80be7ee483680bce62e584afc4711bb966ce9b600ac73562ed98cef26c891b444fee94027354a3b6720d80ef8a36f434acd74067081da93c9a7b30cacb6830c3c8d8d0b4fab24e28c01f2fcaeeb4354b2f6ac854095cb70f0849e92fa7cda20795e064ef80e496754e188b51c364703b4b49381102813c6c62c43b7b269a3f21fac1342973b667dfa7454ba7bf504f0f8fb0a6e60be8de70c2f3268ebc126ebe5ba970ea1d102c6fe2743697461f7185b7059b280bf37fc5000f5dd989e9a81c293deac0b66df70603366653747287477494e3c8052eba1627f1374c498d99f5c89e66c28b86bfdbebbab3638875915c10b97aa907200b2936ad33521ce99d1bba5cc394e04272a400db87685b5654112a4391080b836215328d30f6d423041bb3add7a3d5e6ae07dc11d0cd839ee8a9d96c659c02b86dfd34d725b3ec3c5ca2fa50be1b725040658ead145b8d0647e9b089561903c8759bc33007dcfc5d58db7b12ffa731bd5c772b5b583dd57e6d336d118fa2012b5773ff7d22c73abf32b4f4f2a783ec330e82092f4ad639636511523f20050e1ea804adfcdf8225e2adb4f71ebea40ddd5859c88689c6beef00e3b4b7b2840055983656c80787c399ceb6ffaf47e9e37d8fcda8ddbb22859cca0c6888984b04e84b8c23826a8bad38725d2d4613a5ec9e1b0198e0fff8d622cb3597cf99f80b59eb00e327fa9e4990c111113a293e7fd6d3f53a32953883c696087964acef018e8e4c03e3a504d4e6d5f2a84aaf05f6c4ab874eefd3dead50e472b042b55a08f46c80e2c0404e39e8c771f2403d65d31838700a6d15ba15134bb11db29e0b090b8ab76143121a7aace19e4ea47401ccf6620fc3decfa81deccf6645a1b11805205a337407998230dff86ee5b6a1fdb21b8965c80edc639a55e720c794479e0ac105b09d12c141f30f54372063c2bbe13581526b97dbfc9d140b613a49e88203261a2a7921a4ca4912560e287a263c3a0346bf571d9af577fa0ff878f05bc80fb9b6c84ac4113625200c79a5395da127185bdcb90d535d2c0edf97f29ab6d20e2357761597f88b55d2251ab3c96af1f95aab31eb0c16d3e1d87fc4872f63c5062bd5cc46c85e4b621ef6a9a4bf059d4b36215cb97a3aeb20f13b0134189abb0e4317035740108514d0228c5687cb50b2b1cc504b5c03b1a3945198d815105c06c828c98113c42a317f46996c6ba1b65d5d685986427371ad272f01f46d29de09b28d24979f25e018caecc46a2abe110f520abfdde8086cbb3b15171311d435078a9a4dbf163172eda6335454687a50cc05853c8bd53954ad57a4fe3a92f1340f12f40a063d3e219f8c177c360dae92c46568e8010d2e15c9ff52a0a100ca1e09c01ec182605bf36f9f735e7d78dd47505fe6158b941b2d5ce1dbb292764d2102dfc7bad0b03660e09d0b15118ca177288a880d92f14e25a1143a248003ffa4067063bbbdd1a3f93aba62ec207940691b143fd5015c5bc79aa01885d4f57ecc0ccc7c80fad2e5906bf3cd284a5d462c429006ea4b6fb0bceffc4d1b9bafe3630aab95afd1379ded562cff53a74d7508888a0af899dc3cb656f5ecb9138b608c01373458e98cdea05be8d4aba56f65e25b0737229cda4d01976eabfdaa57ecee0b5ffa84a89928306adb40715fabae47480f29c95525d9cf5052fc2acb60c8fb0ec212fdc2831d21d8bd6704fe446f58b3269cb8a591cbdede0d554a341ee0750b85d489b42eb1f9a83adc19f2d19ffe0cb4f47160a03910e4eb8845688cf84c0b21236bf094b209e6ebdc6637331e28e0e3c6236ce6456edd3d082e58ed3208080fc26e60b8baf559f8a6adf730e1a34052aad4bbfca61ccb98a498c837d6dc0152695f3a2f103b642d3bd805abda26969d129867748c6c818c7f0713e84bb00ead8db1678912a17ac51250430fb5947d6261198175d8751b26e00be1066b8b031b6819bd14a3b8068fb4d20e8f16607e91d16b708e578a2f0404568b26683d0ac5cf5b5ea50a7aa3292290f09fa00f3b3abe2ba7e84ad850434769633cf95b0717c5099ee9f2b01632a708cd83e2b5e6a574c052fd0a56bfa787a9c01f61200d6ac7e2a1e9cb91b3fb59e6ba0f8ef5add707c13f26fc21f4912006168afeb80f7382e351be10cf40d365a19ae1e1fd76214d241a9425a1af8c21b6a78d2ef4096a00e069d35eb8a74d85024be37882e551de33139b056af8d7b765b7e466d9022526e6bd781b3b924d1d8582c62e3e7e49b125841e0bc3e00a09d3772dbd670a13af5ee783f2a391dd8fc847cd372fe04b1efc670699dae58beabc1d7f9e970063ac3d50b79107d097dd5c57f4db0285824a63984cee9c858eeb96ca2ea81f025df64c325849be6d99d77801cb2604675536020b7ea3b1039dc7a917361d740be00b85857b58c6fcbd1182a59680e6608b55c6e121d98f261ec84dcb03af9b0bceefc19e1950415045da925a97ed3d7124612969b727212a91c6b2414dd71206a414fa94632730c42cf10c6bb9123551547c950d5de9f56fff57eb6fb2eaec0a583568e1d838631579bab6d35014cebc7c8ea809fa0800b8d7635a075b66810e57e143d5043337b290156992d5110a869326f6f764f95801dcb252867ca670012204e605a18ea443787f64143d937b897ce8bbf1635926f863ffa8d06e8bb70187bab943fb36feab7382e9683fbefcfd009e0f708c591027d2c7d39162cf8407f4026cf43265d09bf923525c5f9400a710fd25716b47a530d83937ebb2bedd098a6be219e402e58f4051cabf7a7f1913a6576c4628d0a5275a3cc149c647600101e43cc5b6efbb7bcecdcd071e4c8860147e123fcdba49be4fe257ab7001700adcdb40dfcad83feb1e766339db08b7158d489f995caa6a543ac70f9cef6da10bced90929e3a67572ac07598d518377620459ecebf1e512c51ee102686fbf080108aaa24d964c14b18b9439243ba5f08e3beaa21d91ef2e0688faeba6cf0d8004318caf0f017cd98a70daeca5cdbae7c5e206a5ba3257eafcf7e23ac8d06c280edb4a83eb7810b06c080c6e56750f125267008a81fb7db7522095f5d123c4d60a44409085432a54b18f3587c70df6400464d81dff609e65915b1785b8e9e4720809e0025f8e34616b958d2eccbfe66f0b5c4979a5ea1fc49cb7def35a9016e504a808dd62f4c9cfcc20f4b2ef74361106f780493ed88678d3627041c8f78bee0b62267300c20104041e5d70dec2f0395a97d0c6e3e85adbf2d9c0939bb3c6cb008cd102a74a7bb1cf705326a94a0d54eca11eb18a95cfb93de213c53441084c0cd5a1bde3b13178ed0dbfd38f9e98c4527254a54c46b97a9cda6d2bac50fe130c626aad4b7692b3683499ed816d52aa99f631923a04c2dd53d1775934a282620f2b5fb30c64c5c85bd4338bcf9e5096d3348f33b385ae6f01bf07b29479a73d0322d4584f73300113a3639a6cc2f00e196ff4463c905712a0720d6247db86550b9349a7e58c95be0614d536ddf238dba97479f0e148dd186f47c994985a834108531ad02a2537139c8db17b0df893b3589698103c09ec699b2820f97e30fe2d0bcdbee7fde2c66951581956e5ca122214e956b4670c27a2b5f5f13ec24d6cd408952ee42f4508c0c68796f4a5ad43bbd52d344243006044371f15d44bb3169402a8d84f25175a589ec1ec2f55451356febb6a312867867d4d94ded53b8f25d80b100e61a7e97d096999d613d1f513f33e7abc78926fa865f32ed78605bf2d4807594a7eff1bc95f7230c27bb15ccb2f2f2229b9bd82b05b3efbe9594e3913f30fe61c4b9935eb07b31da09bad421c0261c78fed27da3094944f8664b726b4f903703561448f77eddcaaad18cd5463d74a46c330b377c41bfdfa766e4f75b56d08583e876e75c0bf75ba86395e5d0d9d4d5579b59d832b5cf009f811b23ca7de0c1d7504c0c0701dc25e668274dfb732bb65cfa7a2fecf30b6808fb04945b0880fedd6a7b75432983d680dde62238f45f719d88ba86b2cca7d8d5f45e2fad69b0c98a078411f2f7b0727b5b2abf9b082eb5217993664309a18a7af457d0084d00d62578d434e42512cb88a80d3d50076da19f850a360bbf302b17d5266cd2bc20db81296c33d8b79e2dd4e64ee6135126f36dbfe0f31bedb0fd5e704833ca0b80d7c4bbda7fc1118aee3419f4bfeb88c178138fee6f7a91e2957364bbb1cf9990b2a694cc1b15ab94b2fd7a0492338af1ba2146a80971315f4bad5af1c086b950d23feff524de3f8e66e33d88bfadda901545c70a0ba67d0a66ff5767b8df82808ca86317856d9b70ac2cfac44c7bc5df29234e2e131e87d0e2893fee0590d890deaefa944a5ea4495e42b276d750cb4b075c9b3244abd48836f7942b762ee0b0fdbc5b3adf0a771b652db992ff238424df80f9bbadd71db8c73760bd829e35b0369c8cd459ad5c19a7956a8f5edccbfe65612466dd4938475f6b8966596672e042678eada2add71c61b084be7955f42206ac5a014f7117da30c860a4cca71300742cfeea807f54b75d2a80460c5c56f5774a42b897e8988523daabd3bea63460a170f473bd7b3d88b7be93cc7063bde74a3a9b6a615951c645e46adcf981b830e456180b9821b1f9df14894810b1af009d527e20200fc8c162ebd6660853d4a0fbd18a9e245faa92b3a01c008a57c4f56b38d5baf0610b04be4e505b355fe7a0bf2ed4b683857a4d17c9f3c22b0d94f8423566228fa441a66790c00ba85b1b706c4e1695fd947c82fecc7446b23958c2463b8cd64ee7bcd5ff94defeca38ec001d2a71fdb962203bbf6daf30334a969d3485e3e00e4e28afa2e4d265ca9d0330dbd452bccf97b0f4bba2b9c48c242872c05076bfee97c07dabb7073cc3ae0eb07ca00e4d745d651c411aad8ad2e701e47de4c568ce0086fb9d518fe0af4545b0b3f3c9ebf02e0fbb679bc2c4226ff2a325bc94dd86391cb766d2614c2ce7eda0fe8a8c8949431f88478df979d53f6003df5dc9252a587309b0f3a10ee6d60d401ab4ebee15523d799f10a649a7101f00536876950c386d6dfd0ce3ed0dedc400431aa8a2ec276dbcb7359df40773ee25a743ab6aad0260aa478a8da13a74efe05d3c6d5c646f54d9fed69ef221ba68e85d087b7aed1888997f61cd44a033c4701f2ac88740f24b0729ca0b9ada74c2fac43dc9762c28d7804e9e4529a92213e05c662d7ff87ab8e524a55e0259866a5b0f8b6cfb267f9a6e9bbb50a58bd8428074a55ec82ea692489683008df47407d8a9e721b8107cfbbde3d31d1ba11184309a5f47270063ff973656a03697c9607b0508e5156a15da70a9317dad7db1a580b2dd6fb982e30870e4a239f553d0b4f088d20b5884a09a3bfa31bb1ac9afe350713630d608ba872936274969db6caae2b6954aacf0d2afd09921cc4a64c0ceb009ca47f487261fa7060a1dae3c36864e78c21b4f0609eb6bba44d7c4e0fdae30c6b43125b2c711b09546e6b4c863678f57e957338edb6aed58734cef5d8f75c0f0f1d43574d419fccdf431425921448bf3e2bd622bbff8028b0a051eeb2ad4c001696f5d9886ac4ecfa1fe1e51935a46203b2a00b6fb30703ae7106be3164290e96499b2a7d59dfefaedaba67b1f1df14a191b32bc7bfbdd96ade8491a6c1850c2f2531f069668303f2f95c98685dada2b19ee6c3eb7bab0e9074f8b83df2c5075e8df7fa2743c07461f56110c4afbe0a8de8c5fabb7b491a10fbadd8df02da0845dc79ab3a9f1f9e3175e738c34d74b7a47ec43618865ad1042eb28070583700c1c249d7a4acf4182dead1be7d44d2c75cf47ea95ed0d5b6e02e0efc5650d5090b1d9448521ce6127f0e2a8b52bcf4129d04d1b36ca0a00590178561bce7b703934cfb2dd1f9485ce7211bc427252a1faf6609ce87a0f9d888a560ad2292f20b6cc2b388930d38aa205f24636c41f07a371defaef31ca6f6bf9e85be9f823c01ba677d8e8a0ceefcf2466778ce1e472f61f84eb11b2466aebfc122ce183889088c2621a2d975606a62f2cab063aff48fb1f33c61f07e98cb21388554cf7ad1088c3062302bae11d3362e3933e3f6db5920356b212700a636e9ce1eafd461330a2b3c55907dc763af949b10ab855de616ac147642f8c505dec75f83e87437420d5f8f2b081c0baa262aec42494d1155a843586ff76f9f58ca714c67b56502f500dab98ec610e93abe41117ec0fd47fa3a6d507aff5369597958ad2a397ec8e50f866a63fcde3df7d07086360c8fa598b6c3483a92bf4d56900405ec790b327308d59d4620287494d2186eaa342d2a3ba0b80a5162a5f1e4fb7c0db0dc594f8906b5cc27747b271a9681e8ea4556d97acb52e4a14433bbcfde5520b10aae51af0a6a5390c948ef791a3ded2b3b48f38504b6ec810e8e309ec6490c07d063e3610ca68c8d1cbfe5663b89bb8ff730f125c1d9d341bd0ff78a3375b7772954bb9b0a0517dc2fe01002382fa9bd156e586fcc147fbb2f659ba3bf1468a33c5aced703c1466567545437c78d64fb213df8d143d61c9fe7066b1a4aabf4e873a7778707e3e5bdbe16ff3c30c84b3bb74cbde18c93badad06ecc63b7810ad363e000ff0ac051c61b478a69f7790b9128a30f15cf184138672ba7b3d4c76fedc3a2b42b06e6543d7c7cf1b1b5c6567e97c94aa85d58fd624fd266a0daa1c640f3b9b1ae09b603e61fafc8c2b82b2e1ed84a6f4246d3840d28e3721f09626311a3e297cf0ead512d1bfc6fca149094724eca9db317ae44928779efb9ea49553234d2c42606ee793bf77cdde5fa13fc0f4b40689a94ed2b0a418d784dfa96f46ba83e16f60aa7c04115a1845c89d71774afd7fa9615db486895f76964a332e1ce14ec930201df5891d3614502cedc0e81f0fdb8d0e84ef120f708d6bae2f0f86f26fd710c0eaae41f145e1dca1d88f3ca2db9244bed3b4b79ffc096cddb224845c61b792409753958fa1bda75f2d2ae0a8a45472d3e0f8c4d51091b6a9cabccb0d083f09b08a042d43b8f6f397dc2665a72d62cda30a3eaf710a55261f1844a3bf219a1920cc51b9cb1425b807d0704a756676f633c4f61c2c46b0ab1313d6bdf189b56240657c53222b8e3349956c835b984feff45b7d11d8d7d595fc5c262858773c3140cea17f2badd665df42993f0eacc5a8497feb33590105dc1a8288f7b14b1d6930ae98f84308711249209009cde04708c4708739a40aaa9c0e6794682b6df40f10cf896cf9436dac4880700db50325761372fab1b75e2f9ceaa6651a899261d160950e4ff79d2712d1d6854ac3e876a1687570ec477e52ad5cf9d3d3304a56e3002ff2e82f5c2b44e26a2b9778fb0482cff528e6e23e645d915808491de6c8cbb083bd17a39c2db59469714dd2146c6e4b1591adc566f770a7dd482c58f7a45db0ca07e9cef1ee6034ecd6148de88b3da70d8793526183b63b370d96ae15916b2025da643b0ebf9127669f1bce21d285e9b2cdb8f26840bd506961f1802280ff2078c0998d347ebb9ac924e7ad261c49fc3a3f5caa584b7215ae09bdff8c08b360164db74cd84cdfe400120d2c1befb3125eea8706b48c9c68b5ef90ad1f712f90ff1515fdf02e00de96c4873de9c03c00287eb8809b6b77f882576cafc2dcfd70f96200a48e73171360e2551efd48bc52ed0240889a90f1b4be6ae32db8ca5650a6cb0ecafecc8d01b876b1d464359f2bef76792fb414576a8fc78a03f98ce690c457b9e63b171d5ec00ed4b6919ba1eaf2099641d0d4b76f28f6f1ef81a1fcf0302152193b6b8fec712f8f8424ca0fc9766155fdafac4496f52872d06b12668018c3a756f1b58cf334e8586a3f198521ef20943ffa0041b6dc45b52a83f61ff0fbd4642433c31ca7f556cc6b9503616fb8626b6357b7503a1b062be5a6b51490d674bddfb8501bc1beb56e08110f71fed304fbb72424415a4150ea05cbfa9c7044bed4ac79f1dc08700499358675db73381b3827b04e05a169f8f1af4e491f3061020849d58d4af684dafbe3e9c8d9ce6f7525ffa8d00dffca3e8a327af1a030e4615e350138fb43dd26c7d322b6d9c8a48eb51344ef12419545d7bb95f20d8056c8723accb18beea152cb15dc1dbd5219edcda232cf58f91bc4bf2be0008f50c48cfe8cef203aa4b31ea19d65706c2112cc776018d11219678053758fc7f71046f2be069e2df267ecc5fbae750f153201d83e112f5dab34fda7c911935abec08bc5cee1f989ba6a4935e19b0fb676d18552fa2f7a20aa829e7cd1b67aa28f4070b4ec11c84a126e32ea76879d40e954d376943476f97b1df170f227166f31501964d3d32b5474257a50248162443ce838896f9ed9663c0988c4fbb8381b225048fe8535ddd4fa49ad4a7210b70f71e0c22890e2c42060c8f5f525b1f7d449d098ec35746502e692185bfa1810e565f87b55ad7f073dc51041e977e115334410cd63e91a7e2ff2558d71290c985072851938d2989564170d630e78bb61707480a7cdd453bf01231b7b6a11651f616b86ec1c0765c454293d2a9d68e9db1f32405f1657640686aafb027c6d2e98b7d580d47edbdc2db6eda58153ca49a01685707728b35272cd3e191f083e2ea98398ca68e48b864359cdadc0dd7d09ccefc2a00688ee4e7aaba42a4cad5a046ef76a680b458152ac6e36589be0d2a9a108f5a0b882b46aede61589789e00613910075a067cb9c618d06c1ea4370b71ce65cdd06b441cd119df51aa263d42e87d1dcd569d120ab4a33b37d9c2d753190c717cd0c1169c4b8fd6ed983c0b1f425eca446562931ba0ae88ef5df00e4e543a66ac805ba328243502dd92d2e3008bdc3ba529d10599a17d3b5a190a411e22e86a9f00b82dd2d01df6fd17d9583b69af9235b1c10e251feb5b7b9fa05c864fe2b3ae30d12eaec78808ce290688b7da19e3ab191a9c6eaa3f28545a1a082502b4b17f30b7ccb78f35e2ecb4f62913734dd9fecc7cf1d890c6ce42f4a9a716e8b7bc1cf05fbf74ddc604a64ddac3dfc8cb1d9e6f7aff7cdb98dab8139deeedcd2cc90bc05e264f9c75c558aca2caf8d287d06959048343f316738d8881f0a3863131a3b0804f490fdb366bc06f92a35c469749ff43237aa136ab59f29c1330437ef1ee3057a7ec5cf3f852e48225acab78feaf53967bac41240398d6f6094a9a1f86346068cea9452a0d44dddc65a522c51162297067e2f41a1646407e62b0b54db60610f42db7d37298895b19b53b9ab78b55439b20877a2656aafdb8b93a45e18b2eb06d286367166aa13fc2e378d20c7e798e51f7b1cc2e930e94b8f703a6d2774940afecc3c2bbf6cc86a99e91eee1943aa0773727c5faa7495d58b5bf0dc9463bb06760510023e5ec7ed8a77781bbf8c0a5ba68197a618342d8fed6fc74e8a1f550aad310a20823d67d6d6cf5456ecb2e05de91f60904066ada4b90691b2c8c9df06d53cf604f49da833feb80f9d80f210f9fd0768b9edefe7fa19f886a035f5130ff71220e9fae5b49abbb1c16e920ce81b965f87d7012e2aa22dbf171851a2d307ce71231da3255d09089b0dd2305d5cd261d6946f8e285d071e98b29201f924039e38824c61f056bae57035facc274a7516f85e0f994be549347c7b1845842f06bc679ea43e133d81792367a384bd698084c123897a126094494d8f9f306b8b03c1e8f74f23d91325e0c9dbcbfca0066bb33c311b401a3e91f0aa4547c9d01c013f18611f23b1921ab2d814010d9b8fe4b60df53c7784a86ffbc9bc0f3b7cc402cef1fa8fcc085218e2f841767917b7dc65a7e4a9a8137267a528d694816ae2086b37c6b160db355135a763b5a385621554dbc87e46023f29157f7abc4bb12c033049174b560649a2fb719d9f701be2b3fbe05be31faf39c84697c6f144b8c10957da382b5dd9958199484697aac10b3d7e6de24de1c229deeb8c7e3a48c61109f08e9ec855002d3f50561c5a5bff12b13981224627beb2de7743741d94046109068a16c0d25f73883e713bd70a53b2cdc3ebf374bf65605114edfba44110640598a457b999dab187a3151636b59e8a50d0c3d57feb470ccb240525b2d30ebe00fe303afef23f0b05fdd9d29da9dd239c67d0f2fafcf04f5b4e53774ead9acb0b2d87bf6328704acd8603312512db177675387439e60dcfed222b805fdb5330058eaa1eba3635c5fd1e3236902709f91261167e358b41e8bb677c6c9791a243015e28c7801c6a3a1917f886b0066abef22bcebe424d1b8d8aa8760e69371305048a51a3373f9931f073cc660d985f11747e2a0c1efac9dd32915758cd4d692b05619812deeb0fd3b7a977c5731a07642cb17d63938bc7ad5f3a394b7cc6017104645f73846151327fbdcafad53a4e0ab5d7df61c13a75057ccb19e1f8fa38230662d7e409def10686ed422dbd2695f090526a2a2fec86bd34c778cd500c46370d716fb71602015286f2fb7c9253484713e4313e56c96336b31e13d71d55001b059e84a4cec9a00866fb9258fc1bc4b544485312174c321b3b7c377af569a1f500b24ffb99ce6a405304e9063c0b6ff4e943f590d6c030ace989b0e1b01e23c70f574eaa5ecb77bb201ec51eab46c99e74257e605577f88f7955463a9ddaa200046ecdd359fbcd3942385069dff8d200091a31955c88292cc3afa8b355afeff60c244e969aa1a7ab71de1d6de90523348077bd111ce9d6ffaa14a09d7cd9b9a6088c2ca8c6f9af1a5859154c7b37e9c9fa490b84b3e8d69de1ab26e1bf518f310b9f2ede8336dfddc4fd93a1aaeb81301fa84001c845eaf46f56d4c8f9a7eca400920adb732f57fa56f42b5186b29c5f81050b8bb87566dd5608e505161829640c873bdd89a24118894ae5cc1af23097413032c790303eaec50f794e6d10fab404a1527deea4b2490a16526a0daf5836d4e583e9229ce5277ce8f2cd1625841006afdaa2cde32ff07788924f6ccebc2c0007911ff7cb87adcb0624b5b3e122fe05f829302b567792636e06aeb6cf01894c98668093bb7b2fe806fa7657af77c90b404906de6a35654b492bbaf7ddce2bb96c06f4ced97914fe32ab98def4866f0760c0cedbe05a6d5dcbbcdee0ad109fbe9e4c46bfd53ca63797de89b513965f02ed9d1fd1676d611d73f2bbf4fa5e802088f18bc005503dc4205e046bed7bdc0faa93eaea76dcb25b585e6c9d6033d04986b4a6c8f310e9d5c8e7c94d1d9b230882cddceed4ef9a2aff15b0e57924cbf7379cc71b617e9f1be448a389806b9502c6311da5cd13ae1746a9db49c9a9c8466eda5ea7649da0d50e11e1e73cafda0ee0166714772c61da9942a7bb37671f6299b6715efaa934af0b0bc1441047bd04c18cb849e1d87fc67c2048946749889be992bc4c352365766534585c9601ff0d88975fff04edca41f5e4e67b651b5905f5c27035de11fed1a2c2187f3437860c5c9b238a90e43a69ceff5bc97dcf8babb42ba0f576ff70b6936be211c8194d0b951926d3efa3f319a8f486aba32170ee77c336eb79424887d59391d0c35111051279752a4175614abc357f79de84e2234e7586a3d04337816f4a6aed3bd7e5035e08f22c3eb4c64cf5cd210301db008e179f5a58eee36ca944098deb150f67072698f44395f835ad1bd990dc3cbc0ec8a3dbdaf160fcda09f72ce9653e87310c3961ec9f5729cd45556cec3c374acbe065c3c0f45251185e8f8545366296970ab9636b1e3d09b767fb055da139edd5aeebe25f9ab924262221752499794a9c090346e550e3973f5acb43543c730c499be5c821c1419d2e1dc9c081877411e60f0a10bb3f787088f0adf1dc594640408e31cbf421cd76c25c36347ab1d66bd00b89f3388608bbd32c9117690efa55703728032228df345df5d56c042d1b3cda0cfd05d6b08e981971d1df9dcb48a68343071caf92cc4c812cacd5b99c49e13b0287c5eb6e1af0eda1b19fe78b2b04628db28ff2e01f26fe99bfac5cf99d299903c59ff3419845a0ce4258701c066a9aa90b4b9914d77e4288eeac3731caff6102dd5be3e058b13cf8b75cecf69c8f431e061e88769647cf0b862fa9b03be85401a2d67e85c480a5af014c43e810a3529b3bee02a5a0d74bc86797523e0817830cd7456945e6e35983d6b794b2ba1b60869497e49b1a5fb31b88e3f6c2d953f80aa09897fe12aa1df88be955f06f740e69de6bd517d8a357d4d8ad051e7294f40009f5d8effce8e852e4e8d0172f28cbb4449a071938f8def05d77566d62bb29077276f8082ccbd6087f01cb1b2662565c48c1316d81dc174131fb82abd4616c0d9d7f90fe97c63b4cea141871a7b3ea561d960badbe1da13575c2b6a833070607590854693430543bdd2c1c20f87d087cd8cc8ba3b4ec707a9973e7604797680bd665b6f731f5a9e29e931ef1789f1691ae43a05fa60de2fdd4990c642ee7330a8ac55e3ad84301403b1ef03ff4e5b0618d56469f5837843c5c954053df8b340585c9e295117bbe6bb65986d36150bbc0aac6cebb7b1d992dcc5fdf697085c90fad78c0d0ffdd7374de6e57d092c3f65e0879aafde3bf433418464cb82a4c570814e8ac4bcc741017817dbd698526786000bc75a843e8b1a664116701066cb207dd6f8b9964fc2ba4cae2893fc6cb98aef40ef7f458933ca6e2ee778562f5000a3797f5d51c8f03e04591cc265b2ef5669f968ea2395e95c348aadf610e13ea02d4a3f5cfd0f74a69f3028a966cb492433fcaea81a1af778d752b860d3b1b0a04eff1b3d1a3d1be11cbc2680c1a70f181f43dcb40a981cdbc7aa5d09787a9dd0d7f74b0e8b0c18aed363f209d5de078cb66e8c7194103cefbfff422144d5150052b13702d42885d5d4e70b2a6f38455a68b743ebf5da8815780ccc5b2cf88dc0840afc0ab45faaf0781783dc3e3f5c6c6d94855d6c9b858e64a3fbcb0a065da0ea2cbde95fc501b90c05c9c1514a8610f983c59463addaced89c0dc40b255840746eefc03c1fdc3ec1663328ea561c222cc3e1453976e9f74731c511d16e4ba0172895dadf750a0a3f8637ace75ac01dc7dd90a80d6dc366edcd8001b49df430a5d7f670ce56fb74e9c2c14cca2fe9c44ffbc2c8d868192521c91fe3630389206d192de65e6a0817d831b24da61429e5fdf6300e708e4ecb046356a0fa2ea290a126895f8a7fe17354324319c30b92e6502852e3df208982ac3994a4e89d81f04511ce143dfaf46405cc2985e5303f179d5eb6984ddb03aa02992d438f836ac094a4453d3faf30fba5b9d4643467f67e4b4bb50d9f6c99d3ab3e58825963774045b44fdbd364889c27074d1ae223c7118ccd0aeb10f37ee79807fcba475b1fa081f9b6cb0bbb51bab981aa7a9a5c65b4b40770829ba72e77f0a88788f4691780a6857dda901d77c1481cb6dd67c0728ca1e033e1024b2e066fc3b6b4cddafd20b931ce48525cf0a321b72930ea4556b7be34ba746f09ddfa0645298dcc4ba770cb5896c4f5ac4ffc2cda40c36f60ad9ab14d0f93120c4a4c281c761be9e54c70e5ee68c5cac4895cd389e78f295c5d9f1db1995fb958a8be8b4e6a97820033804cf84e61a8a7774c6ab2ef0ca884a1c730e5dcbb731d02065e4bda449a5cbd40b42357b1250389305dca85ee477c875e7b513b4ef024f1dc88f3f6a99383b0f01a5b516ed60d0a80dadad1dfbbcb6d0e5632a4f81bb7dbac84bfbd7cc0492630eac078790ae3281dd2605aec33a5993cb4077e01055cd2bd205a6e779b437780d68902bacc6e3ef66ff184e517b2fe8aa369b44ac7d601d0cbba4f77d3e2d8f0519befadf0d246a45f125787c5f7b798732238b0f48dfc6ec93bbba39668c9a00 false -check_ring_signature 69a78567fc93048a14a2fc379f6942cd8f71f592722c0b322f64fa6f2fa41f5f 362d499c108426791ceba065fdf4e34fd617e2008645fa61dd0834b6d8841869 44 4a62c686c5837089ef7efb83a52774270783731ae64c522d391e3c3a197e6b5d 2023f50a063632d264fcb180fe8a0bc5c8a0ba720be3c28a60d8bf93fc7f6f39 7c468a8cf2db0c23641e1b487fd3fcb60d66caf04af148e45a88e801cf949b92 6d168a47da1e519fbe350c563b0ec476c84c08d383a2e5cac90ed9610cd752a8 3c2e6f0ed8d6d20f024cea470b258aa691ca9ecc795f9f1f05eafde53a753212 00c66ffea39d420ca9c946273b6457a37855e3e27c20cd6e971f381f7256cd9c 1e57aab320cdc79fed2ec7223826aa6474e864308396740b5fda28cb093af2e0 ccb5d7b2a1dc4dbc3428b0bf73855168ab7254bfdfc4919fdec3276db1685389 92214e0ecce07601d09fb6b3ae8b09e811fadadc588fedd563e65c71001d7b3f f7beb3c9cede250a4f59cf586d22ef2703991f93ff9eefb3cdc42b75f879a5ab cb767ca297b3df457dfc1c9372abfd5929194abdc3e6269d6ecf76ea725ce23f ca398ddb5c2738ff7581e4ba4eec6a7fa68a267797fd9cfeefaa36f0683a8519 f484fb50e073c84cb94d4f06f50a39b3e71354a48494b5cc08d63be84c7eb5e9 2b3cf4c6826ea7d40959235626074684faaef8f4630f9c983ace6433705aa147 3dfcbe8e86f8d422793de338d69c5a03704b86f135b4f48fbe1d1cbd9bfdcc12 92578f1b0f3deda54fe7b9a52c41be4b41946b63794d52f5b2f8708d8e1757a9 e3820fd2dd450663a43529129d4f3e7cb32584facc8449fc09cdef26d083c53c 17f556bbe8f8b1eaf3760ff834aa850ec8b04f04205deca20418f62e9884c1a9 95e17fa1574f3a4ba7f130851fbd5b299aa9554cd12090d4b0fe39264f36ae00 5cd3f61c4eeeece6ec12d33ff08ec9ede34b74f2717b82a2a93aebe31ebca221 829c598b3e2f0060605d055a759a623f26045d681aa162b8d151a373a6ba8154 adf31be635684c05e8ebd83f080dd241df26cf935229557f657fd21b10fce8ca b37ce920f3326b3cc12e8586edaa44b1d4b23c318f50784099c6679764ddd602 d98bdc944ee08e847ff9a6e61cef8e042d1ac192fec36eee4a8e2b92a41aefcc 9e98cd4a2a43b856ee0350b161655208b86ce2855e5469de17e4246b1e4d66f3 0429c0ceef5fa6491c6fb72b076f5c7e7bee9a8c6214f487aa0718f65295a86d d8ff74793fdca92b1452cb1df3cb61074945c4b9b0a543d0a46917be74791374 1fdd371ca90ca5a925a93bdbd06e9f57776e1926064a05a7e89fb93eb53a6645 fa8cefe9f14c57309d266e64c1ee0073b706e0ff5b66671f391d13fa0ad82474 7d753786ae656354c697ccfd7aff55d061ae11d21d090fd64d5d5c4563527dcf bd8c5dabfa300f6a5bb2088628899df5d09fc651bbfe9e37b94bf72ca82522fd 209a5e3194b387f935e57fc64bdba5df30f75447facd0107056213f7c18aeeb5 ae84bbf9f1064f3d5e7c80bcf66901a45e4358023fe2169ca0f28090cb3bcad1 c68209dbd09bea0ff11cdfdc4afe5daf3c0753db16deea839f45bbeee488906f fa7007ce87eb1caac991cc3dc34a870e7404f00eae24e986777c5b130f165c58 710895afd7c668637a1697245c6bf6c9543aaddc75450d80e860cc4815de50d1 01b060de3e91a66e99524c9c5972f8747dc3964c6f2324d746f3e01eed847143 c1f6ceee13811891fd063c084fc40a65afe6cb4512bf22172f9386ee97819baf 2afc2eb1bfdfc8b3c56392aa666b7413c8fbabd8272201fa55ebf0cb94f08cc3 4011e419eeee55751a5d00304da85a160cd8430fbe820860f20a2ad5d3171903 b85e625564283f0ebadaa3018aabf0a33c2c6c537bf20172299b3a86470b58cf 05e2b28bde19db1843d722502cbc87e59e0db73a30de020003019005ae29fc3f 7d9f7d76032e925a374b34da9c6eef62f85b99284c8b1df2ad9f0a2bb204ddc0 73c4ad3907deb268106b95504e7187c647f5ed21627ad881b36ea91026a5e742 2ef81c3287d96a57eff2c0caf9224c3eb0545fd12936842b3a09fa4bdc983c0bff0e7315059c0e8d2c2988d223216b234a9288f997ad171319ff61db76270200dc143113b565738ab2473ef0e9984cbe3c570ef877d7422b55dae60c05caac0db75ca8cec984e21bac8e1728fb52adb1ca3b1ee7eec99ec56ebbd5eb68a20e07702b5d8b903a97dc45e8a80da898864a621aa67105d9c12167d7ce2c0226d202d8e59170a42fc66bad8e4d3871b2f87ae8c8a2b4902227f2dca2b74a5af28407ec69bd40cdc5be08bc4dbd73921b7e07d22b16f31c93ba69c6d877cb16f02c0df8156d7f7baf600ea92a04927fe2c3e7c4e8bc6452512aa804682ab6bbee420f0af582b2ed86670cffc62765bc5e27528c521c055dcd33f32f6b7b11963be309d85b5a3cb4fbde203282ff790045f248b640e78ebb723e8873021a77a0161007fde879586f3c0f044b88acaf8e1cb98bc45cb1b033d0d7f64865f110e4f5010c77bd91366b73534a41756e4e90b2428c52ff830df289132cab029ed12d263505d5c2e26087e75bb2b97c04475f27df6d6b4d4ab165bd7021b76393595a9f8701e911c03620ec52fa3b2b5f3dbca277cca96fbcff07e7ede2f44084b418c52304a6b29609d7a7894a6b98690ebdd0960303ef0bc7a6be9470f83e627fde2d8509cdd9bf9d0f02db12340704883e6be52ed248d46b5d23c8adcbfbd5e66b4db00bd3bfb5bd19c693463aaeed0e43ef53fa382645b126241e789c47eba4a789e50451ae793fcba97b7eff25203acbfaa9d3b3ae6f8526fd4a4e866b5a3b9e0b660a9c0ac3e7644fc3cd9288efe0d05bdfac4731f8d6386f2f43f3e1b15ebc94a002ae69de0a17f25599d64d8440cdb11a216bd936988c5cd9e6ae8ae6cacf933b083ec8e714e4cdf5a60bc4868eabc62d5add0ab7ded18c68407140ef385d8c140b1b3e0c50af54d0c4d70c9e59090c806a9200ee93bd1fd3c2b912a2181543df0000b47050c0b76194cf480cdf6bf30335d10e714172082ae73f4e91778fc86408612adf231592b75584c04ff2952db6044b9b6eefd846c25ba1c4cdd2cc287d0168775aa0b68adc2b207820af6110d3365a5d4a3e79d85f7ea6fbbb0abbe7a407baac7c19b134b7ccd1e4866e7b43415585eef429e8e9aa9a65bd45f327934b020680657a3fbc2ecc25e2ace0562c3f74fb2ab670d8e944e65c63b4a1ef483d0246034b821d79530af9306ac8058fdcc6bd2a9b540b0d3c85cfe6136de3509902c49a7b1071aa9fa8341d6e0779ebf81659a13e02fce863a1658b42e5acebd70535dc304eabab1224e44974036d7fc6a64be7ada04188344a2278eea9ed24280fc745b002a4e3fd319567ba5770eafa7543bca601cfd08fb7b22615ce980c3404a6b5e42486341c56758da84cb579cbf866c22e3cd933fedab5b32193146b5f0cd9dfc23304b5ec8806e086032b2db827a1cb2f9a961049dcadc8560db3a51e0d48f36928cce03677ff978c238b893b0e2e3fcb8439f5d1a35d5ea0fe4129c6054e368d55b9695cf0178b7cfcc7488f6ac690ae64614f58a0b0cec6dff978310f5924435c9b2a2e9a28e9ea057b55b2f9ffdeab4584f6a5736fbdfa56312cbae932512c145e5d6d8b6d0d20661110228675663d4778df3d07487c2b316c575c022679b72a7a2367dfff1d3c80ff398d3732d416c16ad7d245e691a5159bbfec0b7e180cc9f00fb8772b9ee16bcb05dcfe4b54289392cdd944759f0e51abc5e2096d502acf469e45ce802ec6220db436d4b4e8f8752f473dc03de79004f31183093f3b4cb2acc979ba499dedda0eb0af7e28f8cad14aff0bf91e845852a9370f000bcf48da62974c5891addf593608459078f8ee392958e0d4c3453986f894b00ee18ef65e2c051136ead3cf3d35c19982358c2ef7a388fea9c6651e33d6c86e06159668ebe847b549e21df8a3225ce4126320fda7dc188ec6cd101158f3dbe103682be3da7d2f133aa8e04c50791a2294879269a0d90a29644ec13cd67bc44d0a69f87f83f6590fc8323702387e0dd8fb5da09e2ec8d5a2f8a2b093cda7ec5a0e6f17889f56d4192d7b66bbc898128155e2983a634e361c7339e574134e64080b09c7197f7fb4ae30dc172dd86291a11fe2f0b01735e16cb5ea428ab37571380734d7c51c3e6d38945dc9376d5bd0b6d4887919553cc94d12698a52525ba3cb024e4df06064096236f4287f9a1eef00dbfff135592f2e89856745d180d1e6d804a79040390d9ed9233d03ffced7bbf58d83a371b74d512e2494b475ad25c25c0fe25c244c0f49a004e595249210e32c028fbf730cc4fc0497febd274e4228c80d4c69d9bfb4a2be89245dc41e3a005e0a29b841ab9fd7f0dab98ef20e81be40014cffa1105c0944624d0c616008f81852ab6fd900bad9c0494d028da5c4939a003c9a1d47285a25ca1708b688962c50f764117d27d7af55b4664c138d605f0303856e77b4639e61b2a19af3f1a5d65c480eaf4fae80d0f61e36c5e330e04f0c0238e63c8f9eb03e55586f4c049450ccf08f76964859447c99068d5cafaaaeea0cb490ce2077bc7f68bf0511fbe8dc22bfe4e2e3964aebd78728d73d24bcde8b0ec0fecff7e28d557366ee97aedb7725f00bc8fce8484252d3a289e91edefce50a6ef4dc845e6f69ed64759398ae011341b5fc394a6541b8c459867d9bd7ff840fe32a5bc1425fdca4a6855e8a44e3829d732091954bb7e97ecc3875d3f4c1c00b068fb592475f20e62c0347808ca4ccd97dd2221f1759f9a6842612d4e8c73c0ec5701c85b6f2f57610e6c4d686102d7fdd61fb7e9407013fed44c8489d20b70058636924519a12e0c3e4592ab1792a8a9d000471414c9ead425ff4729f80e2042c905ed2f7c557093bbd80eef64ce53b8615ebf6b037ed51d4b45bf9fddb410d04ff6b6d2469913386452161698d97b753691728499042ccf6c68d8b8afa870f37b641cd2f516f0c2b31c6e85422d2f09179d98d2ad2068e27080a17eebc100884f0187b5b8b2d072f93954c977078e2514bfb655f49caed942e1a99cc6dbd045db9fb34ae02e8f0096f64461784f8d77fd44e83e5ec9f3912f932f10f6907080a6ff9034c1b02c0a9b8a6028a7ba44e81bd91bb1f929774ef809153d492e801640bb8c68eb43f74e64c9bdd15002534a687cadf3b78b5f2993f2c80139aae0536937d628c5e2ace9782dedefcdd3230d16cac2dbdbd1c7490c7f9b2232ecd02e361690392d98019d3777816da73d85414d24f29e82545914227c57d2c12400fb581436520f6bfee8be8cd1ee681139e0206c1ad4250aa0dd837f3f9becb52098060b22671a50da8f677b75e10155264d9c0f3ed872c56257f7cdbd34727be03637926d2083c62085c511cfc0df903a6d780b89a4930fae77bf94ae5816118062a3be59417cb8bf0e6c1d4712e76dc9d215a2ff4273a50b14c1abcf9cc800d09e7cb7f58ed3353cf3a90ec755d85a2bfd8221e6c3e8ebd8c0f465ae0f5fa3203b2b37dffbe085b3f5f6d848b9fc12509e53c5c0a1bc2030c7bfe1032fcca520b2e1f1d1075500795a0027910c749bf453e99e0e89e00cc0e9f0b71768d400b0dd5c8b849d821e80de4197d9e8be10597cdfcd093fbaaeb76dd53c645793eb708cb3c08137d51465e8f39a6db88c6732e4188582ab2572f26541a8b6e5bd63305002b0a6e6057f299fd6e57841953ffd1f5d515f74b49d84a589632f674f5d401ea07b301a9b676b9b783e41ddda3adea16b6c5e951bb02390a65df06e9f3a8059a4f845cce37ff4c851f0e98c9351f1afb34ce029c0ee0f615e5686e34896a0332a3bda9c4f3e1dab543b694874253b44f250c2805edc510ecc291228d4dd10409a67741c0faef59adecf5a05501cc148049f0a14d72507fc039d7604509ab00b63c233a2cff6fe28e835a80cb1d443121c48ab7636d1ef92ab63cc11bfed003 false -check_ring_signature 67e269be1ea8e4f1d64d219607269c9308257067c30d48f2c78b05a22414ee96 0587b23579e61e1ac42fae4f53b8b9e11ce54a4c26d86bc19c5f18af11488511 1 7b016bf6cfacb9b7edc75c1138f3cb527bfd42c97cecbeed58f1feb6c01e331f eed1d4e566a306a0a96c6f3a0291a1e2f03fe3e6b7b16d9ef7dfe9d716e75d05a16f00c377cb663db0933762e61aa577e3b1b00e2eeb3dc8fa1906cb5426da04 false -check_ring_signature 1253d3d1e901a71b4dae20f07d7cfc71ba78ebb5880b8de66539f028d9964423 38c5ad08412fa9c7ec61519d16cd35eadb07d69d2cf8749c134d416cbb641ae4 30 b9276c08e099ed15a4f3afaf50bb4bf47f77a445aa101b4ef6a7f8aa7d1eb36e 9294f236655fc7f67201f7b8ede1fbceedb5886d949b88625a08b4e58db9be43 720297f8580c9ec15f7440cfa814ec439d547b406573f9c448fcdd261623b86e b428f468851f1245bcaee380a4fdb2c70096dfdb19fccabe04f48832052c11fb 12ce40eacc0705e0b2eda0b3684b3b870169a3dd54ac8286c59d292a751a9711 e76468737ef0270b7c0c430a0d5a6eb408d1dd605fce146a840d9c2f61a18cc4 5fba11ece95f131852438705daee1f9a0ed37e52968e167db18465e914c0ae04 1bb038139ffd48a5ad46d06691a01f0ed031a320f8c44d7fe137eefd6d5e9aa2 98325d91f2b785600c0bd3fdb40211642a29c943b9241f62c989d5c32e167790 08ad5ebe0c6ffb475c638bb70d43c1f783d8af4b9b635b228225ebcd642e0979 58b2f5c48dfc440d9b50f32259893529314900b41386b8d16ee48c23ee0411d1 0422104b4edb38f0cb69397f84011011fad2b1e0f65bc8c761bcc9c7979ea4df ac1315730c5b7bb409333c5479963f3102f75b2699d4475e367ebfe4c8db63cc c313420e1fb1853741d68830f6073102204d71c987992a446766d057d1d0f24d af08beee6950ee79c55002fb80e69f5c2fbacfc4986161a8fa663c8b8c5dfa6d bfab67529c0a8e73196aed09446b761d03a175527d47489253cd3ef37991891d f145f65636c41eb8cc5a62d6d1d803471bd094bb7e08fa4e01e6abd6541013f3 7fc8449d67b012ed862898510ee6cbe4905dcf663b82d0a12f36bf3571eee734 ebda3a5c4e1edca4570cba480c2a14325f90890e5324e908f1e4d825e125371a 7e02900a5e7105d72f9372eaa98fd425d503f531a928b344f66bff0441985169 1d8cc80f5ecd54a08832178b0302f8f6966f2c31043eb67f3beb62c9fb4994bd 389d4fe06642c2dbb5d967a60b8e554226f98e1ce29761ee7e9b14a3d4387412 a99616d2d2329b462c72280703b57ce173c1cf8b6eb104741d55a1b640b49d50 8e765afec7879aa1c2af4214bfb2e7894622a549faef0de156c7b153713b124b 40718b35d82dc7fe6a9cca50965211f6f0623850768f4182aa0f9426d95ceb83 eab79147d258c5964e9b9916037e68c8c7ef78093bdb1d521413e16c925a403d 8c5e707907db4231f885f90f17d974cc81fe794d6af8e37d3d6fb68b0ada7be1 953069f78b090c486cae7a35ebb4413ef9cb60e54f7218c7ff00af6e614711c8 f5acac36d6d3f7c9102623e573dff3be09c57ff2af0eb517ede3385b7794c299 7d973c175b36fbf33e4beb3ca2018b3daf21c64b4cc96cf7d169a3ceb4f2f20c 403bf076ce5dc378ad3fac8e5fbc3bb6d7b542e688a013705f295932258c9d0d84357bf09a0778bf760bf92555d7647e9cb40b73acd47013be86c782afe7c502149e6fbd36563e3079ef6db654a759a51794f7895ed6f82649e8b85fa13b680556b8b462f09b191c09c7d67d1fd706e28f2c8e7469c0b5f4b7933b4371eeb50b39b99b2f290ccd06ccb9f528f3b0055db4ff8a4e76cc537536770de462b3f10a368fc392dfdf1d61cca50cc4de78336b1e8aa009fe58fd37f3fa7709bd02fe0fb6cd554e6dc51a5e85186ab154da986baed48f1ded1e708816df48acf038f90736e9b743dc18f37051d96ce9bf0f162072bca51e447add241878ca94ad42cf01270bb4dc7062c4634bcd1d6fd9d7beccd8bdb246b7b4ffd925fd8e1b5f5d7200ca6c3dbe205a89e82c6625b030833117823d974a1e8ea98708f67ec3d8301e03feb4118acd6348dfc371b6f30eb5293033e157bfa7817470fd93592aeba7bb0004021448123abe67fb8bb1715e444999ebd84ae29a9b376ff0504762aa05d30f37d5673d32a99279020d6e9596e25b734a4579983e88d610f373fe8b6089ea00d1763c33c3b9aa52980dbad4ea959bb1aff69893026140abdf179647ad440f0391900daaa9e368ca60eb075fb3cc7db3c9b93d3db5d8732ef0942d7f012c670d7cd1eca9846f0b65220f63cfbe32bad286857a74cffb763c0cee244636acff0bae0576f2362c56464a1c27248ff7a866c593fbf520d8fb1f0c5e213aa360b60a73693c354387225309d90b0e6f5d27d16ce644c55542ab2d8dfed3473536b10539b5c829b9c92871c067debb74cb71637fa34ef217e2b91e96b525d900ef0d0820e4376cfab87c86e3ff7946402686006a23137df10fdf2a76686e87fb409d0eadedc88e67bf796ace1c7f160fa6cf9ee09e736fecbb34996690f86c428d9600ba6b99db57a9fc045b2ef8b04fb7eac128a070ef12eeaa6ee5e6412709140901ff5d9bf1c9ec8679ce4b90c4db3db99571a180d01f5a9108b7f13ba3f967d604cb250160ebd98ee044a296293cfb263f93b604e73366191b2df7b4510d9e84033e3bfe470068fd4a1e347fb9cfbd37ee7e14be7e065b6c7a6880ce32c011a50cead9fab45b552f62fda6aeeb613f9cf995383079d3aa96f008b7f1ebe9f91f0e7b797b0fdb337b59ebba9203fd8c00d8fbdc655ab7ce627ce42db154a61adb08e99c6ddeda556c1eae1b4dd1b468a81e21a91584f7ac6290c6973a8d80cd6e00112a382d4bfe28033eaace15142ae31bda86659e3058d0fd64c7a77e4d449603d895d56efc049ab9353ed688dee785cc39251bd8050623d06ad96a8bb925860a20727bd5753c143c3a62c8220c5d04f77ac656a9646883f54dfe4da6caafc30a40a6b2f404005416554f54d45ad9ced4644dde14e8f8b33f3f60d0486decfc0def7096e882af4e1ab576a13a5747b526b6da429a0c04709f28d66693213d1305586aa2b5457e0907362b878e4e7c04ec96571821239ffea077089437d083840d9fc94b1c386d22c55b7fba3ea59f9a83f12be8e4e852c83c94dbb6f90495f20adc3e8fef83eca5fcfd53dcbcdc148e567a27bd6dfffc254528a42b97a3445803e3db9e2e4bdf01a81248fc16b49d30e7a3f53f4b628c22ade4b98ca27f55d10acbd654d16c361040b1c7903bcb6158727c40d3535ae3da8374f246f489dc0b034cd4a811db6999c8351fd24e4740f1d412fd9cda604a7a21e5cd06b2f52f5f0023a127d375701d8faba66e018ac6e864f6e24235938eb0055ca8d3eccb39e20cc9a8878d321b05afc63736083eca93185c4922e7b8406f20d3eea0e4e1952e02ef723d5025d910b43cdc1370c7a701a48e232ebf742d45cba1c7e04b99b3150823b04ce0b635b48034baf0058add3f025be9074e8757e933cbf1cff04662c00c132d4671d0f697fe482034277f7641483caa8f3cab8d458d9238ef0de48d4b00938e648b7bf4aadd162a04106f702d967097bf3e913751e790db56cb930e0b0f725645ec8ff2d5f74fd40f0b84b16655bcdac75d9c3477aa7f6a6fd14a4e2f0e43bab75943210f83100f74cad6875535430736c6f0e87c7e7d0f6b4c2348ed05ee69d93dc241e76c5d1fe5ed8b603cbcede5fe646976e92bbc7e26a78d83f40b3052bdfb9b81c7b4dd67fe2fe46be48db6ff23f4ac8939cd861a5585dc63ce04fb5b73003052b433c5bb1c14902f695e4e857f4a87cd7ec60be230d148004c039d233f0710865348b3ca2b52938aba66186f5496dd5237d3211a00d47dabcd0388faeb95ac52bd1b1cddcee85a3f46f0f87ec778e39a33451761af0efc41660a16e6a8f94380e85b8a491ccd570259dfc169f691eb0cae6c66f748bff9f67a0b596caaba9b25ce4ceaa0c9781bb84ffbb5ae19c4578da1e4bba282ca2bf9150e06a520511243469f1993c2172a1428c0cd771f53840e3fc97b0682833bdf5f02bdab25e96021075f13edc610a349f71747677136549f09f59008c8d342ff6402cdf7adb8de63ac37f4bbcfa3fb68d09d033b311ce389145d57aab5be30d9430508e18cec23226e1d07babf9a29d27f912c6f0f765d56d6fe9d4725914191000745b395d1cf4dbc29c573c4d3ccd1e7e76ed295a9d8e40e397de9d657d090360a3c282af6454058d4eb96721882bb39fa6d9425ead9bc978681cb4b5b8c913000 true -check_ring_signature 3be3bf8bc6ab751d42d8accee5d80ee75e39f62e25b236e4f30c442f7a1a2c76 b8f3cb5f4238e13b2f3f0b291f0ecd0f4bfd48432fe69f580bf039b092188e97 100 7a99edde587cff201d403ac91b6699046acdde2a60489562ef5dfd344261f694 54b4398f76bf79d1c47c34cd2e036c4ecba0175c713d6c2a7ce35e4188d0aaaa 082fc4978b1a827f4f9f97ece436edb576f00b8b188a6e0be6777c393e864d36 b9cd30aacbd1f03ed3b267391223770e7902f34ee88e161ce2216742c5c743a9 a6cbf7cc4cad2c2e59966b6c37e75584fe245b811c58ab1d32586f98a9fec1b9 ce544ee77870f6ba34215509ee41acd033731434b8c63ae1fba4d3add6fd3385 73cbfd361eec552b280dc3c436778dbb55b3f03eeb8d5f5f243b2c04d8903811 97233e87320d0db12f139ebf5dab8b5b9a0292765f9bb99149917dc6c009c06b 1122a8722135560a63ba9135e60e91eb892dd849627dac000054d643a5204bef b2d7b67635146d7546873c5550ad89c73ebad6bbc78f09ad473b7434e914f544 ef035f2a2c325eb2e37953d73922f436c720dccca2d9f400e4b17726ff667402 6ab0407b7e239267bca490d4874aeb8077e9cd6aee743f7ce8eb37615c61914a 2105028a82bc42701f48886ef90d4c0112badf041990678cb598cb3b26b371f4 9617c954265fd60cd1ecd30557f355446e3e035c56e374d286445c2439d3f149 db196cef6c6c299f92eda52478fbcebb66513604b7a38b746ade19ffcac92895 01d496f73cbd39535826ce7b4fd86f443861f1c42b3f871c889ca924327668bc e22e85123207c9080389c5f2620bdd5afe3e0a8e9b83902a0dd8956b351fed64 6497d2b8f1062d3251d49d0c6af0406a955aa2c788e0ce1bbf527fbf6660de12 286beaef8818ceeb160ca9593521e5a27564ae40e252d3fd5a88561affd36448 f4b8f480e294927b66b1e794c64d8a80caaccd3578d36e7567fbb630b5274f86 b7e14124f6aca561f7ea2208f20110c37a053bd8c7e5132915f35accf87b02b2 7db07721d1ab75b509ce4a1db042e13bdc6b4bb00046c6ea647b8d30b0b9627e 0d0e922382ebe70c595e8b8d88944834acb4990a6959bdaaa946c4e213008456 0c208e40fa1724249610631dc528d61d8648dd20109c79350f8ca932af7e8e67 352dbf114f659c494f0498fafb51cf554e5b9a4278a4eae8633d224a219c8a41 0e013baff01e12d87a1b3e4d93da019ebe68d6098c2f63fc5bd4a8bed632228c b7029489bd41fdd981db0af17800c070d6a752520ca708de937b6a7ef85ace43 9fbaeb3d30947dfc5482a5ff511fe83e37d96c099ae03b683e8a70904cbbcc32 4fdbeb597a9c400dc3adeaae17dc764f9d7c24962fddba69f92dffe023b0c3c6 6a4a70017196497bad647e2abd096e5b9b13bb0a1e3ae62f3dd9098099ed8382 1e8b80803e633778e95f2dee49a77ad36005b27ad0ea56df93c596b43fed77cf a853c8bb66889ef68827f6a401df487c5fd1723417ea4bcdb3f983398b004a59 b560712be697e81da16e1cbf23f88fc6138ef432a5b938f3d698b0aeab2e6e7e 5a338cf131241dd35692d6260cda7db8517081f479ff1870ca9cec65c002d166 4309e67d1180cb97dc9197dcdcdf3ceb8572796fffbfd116a5af64178e2922f7 cc22fbf80b776588a4b37a4cae2abd5049562ab8548f0c699fa7fdf7a25d5e8d ad6c6bbfe3387690d58a787cfc561ba584ff3503d990694a88facf020e2a54d5 db92c04a96c06c7c0d867899479d67b6fdcd54090311bed97f57b70326cf070d 5155931ee5ef7f4981e93db2fd126020f3999415cd41f412b2ccc6fdc3890128 0c585899fd98d13e323343d481602bfea2801fb5d341437d14930fe1c2b4fbd2 6763cf6da35fe1f5a18f5051cf1c3edca71c17ce0fcf2d9ce5464fea70c518b8 606ec6e1ebcde84ddfd7c634221c325f307e442332638ae7b877442df7be6914 cd4d13f03e6152a503c939c41ed4781a29a15382c165596046d8b5ffdee3ef1b 6f9f400eeff244c7a58739f3d41505f740a9db8d2a8b15ac964345202e0bac20 5901a9c9394fc6d45e7e6932ecb870010530c64463f8080b7eee21c59a29894a 9fdfde99b217f941b8abb10d91faa5466b2216df94572c06b13f0012126781b9 95e4b4fbf599c0d492a7298285bea71a1cf3c4ce529b40352aca472c97e1029d 1a4d204779d6b6a5879c9fc2ae8e218f82a9faaff6dc06c1c31413f2de94a374 366e1a2369644df1e7262afd69c3cdcb8020fa3c0125ef96ca2706b4b0439592 631bba7415c1a39270ae81e7ca674476f6c78227e527bc1addb9d8be2d339777 802f97272b1880dd7f76366be333ea4fe6d9b2ffe5431141bbde64625188f3ef e876416f78090e73480e01f6d5f256619ec02ed7e9ad29e5f20ae2d31ed01d69 41c287f45156aab26cb7e08f71cfbcd35f7ad9b991c604c4b777d546f7124653 98e90f74f35fbed7d6ab1a82c052193c857e88dd41cede6ac1145602e0167b21 beee6cd9a0d9f6236efb8add00a29c35400880ddd198fe748be6d95c4e204c35 d89c37afe1b186def2a6c4e3ed27616ac066536d6cedb745000bb7804820b39b d089502ff09ae24ce27b7c244eaf94710023534b3fa5c25b8ed61c89556c979f b051220fe860d60122bb3397e90e22ba9d1cb8fbd964283b9d8f30d84efb2839 476035779508e7aeb7965354be15d7ebeb94a8a1914274385f36d7d60e6338e5 ca1fc3ef06bf8e60b7f4b8b09b286529d7291e5ead87936c645faf2b3c553527 247ab92e7860e9757844d83ec746d5a7dfacb28013677a339f24c244c08ee12d 899e1d3ecae2474857dbfbc361009420307dab73728026818dd5dc88aa9b6717 efcd0600f4eb81794a1b0dfcd1f639e4db64532afc99dfaddb126fa1cbace6e4 5df2183961e8ea106f9046f8224198baed8bb5c9f25318c6d6ee6912e88c7348 8805666401602911d73901c9ba76e6d76726b28d5fb483c4df2f7dfed0115080 b6d7ea798c5c600f84355f88872815c1f19a04d3398f3f29893d8f5cfbd588a3 882614b9199606ae3370554c8460b8f99d7d60bf465094e14aebe9444e85a51c 84b47c832e00075151575dca52e4236906118de56a7181fc90476cf68bf84676 0b58196525f267661b4f029458e85f5f0d0caba3b094c38703e0989397cc884b 3fcdeee3e129f571f8655e14d8a238752b823914f62ba5baea3670fec44ff806 928f863cfec5fb9ac886de89f3411fd93b22ed03d6da694e310a678c7aaff79f f89d1644d01c68a1557810fd29f6b016825daa61a46ec2c3ad7dd7008760be59 081926a06ac4f2dc622ae557381730f6854295cb744147476477128ddad6fa2c 7bfd2a9e228e24e13451e711d98d610dd6181913fff679b8351eb6c2c99d80fa 628f546f91d91c2ab47859979bd266a2b348779f76507c60defc94a9c592fc8c d79ec01440afd7eaaddc370b61e631eefac7fdde7c41d969674288b30b042774 227aaf3cdebe5ecda8c3d1c0d5e30340a4b313bca6a8d0a4aecab757f25a21e4 bc51fc445787019211e76d14a4cf555088cda6fa9250059c62868a5ee1d49348 9c165dc0edd9d1141bc7849edb66ada204bf7a63eccc33fd065fa1d9f763a310 a361f22148cdff9805f6a9e1e114765b9fb73b5b413a585beea51d61de32211e fc4e5ab56923a44b9e2935520c5ebbc1141f52734272c7c1490bcec6d7e6f72c 5775f84231b7213f7174e6c040658b97961107704e6dd69bb742797935df8f92 89f7c4ad3f059c4a6c956079bcd5ad8f107ee25f0a39c12f43fbb9911b2e9bc4 52026ce4b8b00d978644c932ce6f0a8e25d0586ff8b5c5f37d38d263ccd24234 1bd8fa4f2e184cc30adf1b33ce61aed244deb3e06e42124d2bca5cf67d80374f 0ddee4b110bea0d209107ad840f520e48e6a7684d77388db6f4c4d147707dcda 1e87ca301045da18a69391203ab6bc1da382971054c6bfc78b4b60e2049ba78d dea41c4a5093fdb53e2b3b6ca0f4a407fd0c3bd37a07745fcab47a19d940474c 57e11e68f87f21d204fb288a42470ffebf6f9579963b29ef0f87cf6cbda47749 c00cd632b506e8ecfc9424ac5a643b35ce03bb149457843ceca820c1abca0499 5531306d147c0d3e0f98d9796bcc42fd6f10d90e1e731f6da78a10b6cd52d802 50b35bc432da8b3a5b3c20be34b79486ffaeed2cc874d09a21b17eb08c2c1da8 9b7cd4e202479dc8a472490a999ccca15f54fb3ef6a09ab86a8aa0756ebc9a5e 1d8d3ada5365fbc1ff362002b50aba9a8b9239cf228c2a1732aca5df9d3234e2 24cfbecd02ffdbd33ad9ac378ff45042555e5a9079e0108213fa57e59ccc64e9 46cdc2034a74d5c1bb28a0b93b2743dee39f0601e3a8cb8511da30b3a045d771 438a66b9fbcfeabe41193a4c78240dff693d8c11bdabeea8d92ce12872467daf 439ea05906417708b712d6d979dc5a291e82b840c4e2a9807d8197aa7bbfe65c dcbe8060e2f62e1abbb518880c168acd034204dc4c889a5f86e038a8ed1d6363 ac98ef56e2bba074971846e11ef2ecebe142c9133e69cbbe6124efda3041c859 9233000679610cd0efc29ae09e7c8d384056aa7557b0cb740b8f2a20e3225e00cdf7d3afa335d6279123f145f3d68f4c20b06de2e9dcbd9cfc0020b0dd139e0d430cbc87aa56e2b24ce1ce124312df5be38c9676fb94e18610111d57a15bdd0c8431101a2489fde745501f1cb30e49fa6be0eb8633d21a72b402a4902837590728c5f01971db2e17490001ae4821cc486a185592ab173df03a2a72b79597700e1119f4a282c958dc50abc2846e52e8698fb8cdcd42f95b98a8a518782124280ffb3e9db2ddd3a19bb438ba84451991de80da9026388d764e6eb85ebbc365650309433e6f53d33ec25725898faafcfc8c50c95db5573c7492662e0e98cd247609a0182e05e8e0bcdcc706eba0052b77e7e00b2f001622500eecda20e7920acc075450e19f89437b39672a8a245ce5f1b893c4251b140217cdfdabbaab52d7860fe88d48722935833d5f70ddbef83b4cb86dc5aecff06d3c0e6a4d5648479a430b8e564bcb57a5c139fb8d97f5403ad4fbca234a42d4fc322b55197499ceb3e7022ad683185564531950a8b43f7283980513427133fe437d7675e35671011cab06388e59d5eef01a8655b2f5b1f184756a9d1fe9a558dd9d31121d13a80efa550dd3bab35f454e5de19dd00fc2c69d91ef8e3c75516f27e99328f83ddb5aeefa0ef952d60501e057a5b8a45cd98f04f616876aed67900ad9d86cce61b009201c0cf3d7f798bc9cec64d22a4cce45b88113e3a63fa42576d3d24afcb6a14a877d0c6e7ae1050d0a49ac368d4b8620c7f5ef3735377c530e7371c1fa705040afc4031c7c7372ddd693d805196e57ea56b10e54525aa157f67b6dde0be73a032c880071c4093f493e97edcb7d1bf4cd32e0d043f6016dd4b7b2dbf4cd58b9aa3ee003b6d9e200be63c662391cb85b01e90aed84a41b2851c4d9efa2548ccbd299ea07c69cb047982632163ee9fa1ab646361bb4daad81919b7554ebac418efe7dc9072ea9fb5fa56f0f919db112c4f04899bedcc63c9b34d5af3938bb2ffb6384cc085177a69028837a58fd2cc4433f9777f4bdc81d5d1e95e0a94a04be19cb6a810ea75a8b6a12c39de51d5feb66328445b8472793295b94873bda680a952e37fc06e8e823da91e2c6ff0b53430b316ec3bd866441dc73a52de5b9b965f04b999107d899d275c0277f2acaf493e12b2a383dfa4fbe172b53d4352e2f45a95c932a09a2a0a03a3a119588f504b3c892ccefbfbf32e7af3e02bfdaa0cbce962c51e101b877bea978ccc9bf5e93401c9da6d285dbfb8575c9a6364238e0c01c3278a20afe4395fe416305bc311d336dd1b67774b9c300d4d56ae92f52893a3ac3422f04561d045b922a07a3ae0758583f238f16c6f85bf59d28c97f4494cac886a6940c1c034120081f90980853ef4eb8fafeea1b494117fb183ddcc7f8f5d9379d5d05c14f1c7b23ae77e9897e93a8d256684d5f6a30f767e54bf7d594b23178901d0f3913ff0b54573ed252bff5a172ca3b0290012a86beb54c1f18a7119401125f036438810216cc959e12e93487ed659d72ca833656b424c37c2f5492fd7e59ab076bb189dd28a6b2ff0bbd77b8acfa056e37d9fd29312f74b42736fb6b091972030b743714d3210d8657bb31ffb84ab9567560528d931de6553b898e5b8e17380ac85583a2e125bf48fd7ee77018f7f7d4d85261d0d51f4f2ed164c312ed1cf703b3d670172ede893d028589a828f3bb54d5ca8b5238867ad875bb16e5c887df0ed2d09d6a462798d249969af863c3670b7852424106d9ff4ae80e9844c18a2a011295985f828a485c17eb982c1845153d03185eee872b6b934798848d4b87b20006a238b9052070033e20581cc567d70987243d1a509d97ec9c82261e1d31100264cf81b1026b5db3efa08698cc209621a30bddc88fe81ab9500cdb0ad6c2e4039bbe26b0fe455430d31aa25c8d19f107e89146e53a59495c9a75965ce637da0a3746fd7ea2bd68ad9182f109feca2a0a3e2bfc96d1c97446b977ee83ce06ce0acd69ba64ed0e175a613ada80b46d00fac8dce767e1ce8ad28fda92940d6c320722145849fc74cc83106ac7f9e9e6a309f57bca1418ef2e6580d83b10596b6309a0fd88315e1b2dfb1fb70372da167a812c379bafda1b40701cfe4e6fbccd920ff5c4b0e4870e9c45ae7614cd144f9aae2defb2b5f9d4a9919823f3c9d712fa0a0275312310efa5ca8f5b56bf519361848d0e072c6e9ec7333896b1855a968a054316c620415cdea33c57ee44d26607d10c5b9092739efa463d6c87f6d6db820c8215497b82537a317449bb4e847f06946199c285f1a02f6c2320ed98934aed02744eb06c9175038eeed8f744dd5e286cf12db2e4e724fef27cbe48396f9382055e57da623803c09975368c424d9b8f4c87e5ba9e7cc05ed23dc670fda449f00f5184311856b793f50271821acab9d6750f712c4ed403fc95169f7c4c1b081e02baa615786abcd8c8c7067a23084868a3421e2cd8d0aece9620689e9fa41aa50b3cd2620d223bf08be20fc20162746ca2ff226631df43e6539f53e4904211e00a2898910d959b297ca338131989c04f8a764c20e422042cdeb0c9a89b0d8aab04f94419529a8f9f0fcf88e9afed42d3f58ffb594598115f11bf6f2b419f11f20fcb4ab83f678e648f3f5b8d65fd9f965f11b1e8202bad218a65c74dfeb6e05606eb0b3de009b6845aaad5528f1371098c4c067eb3b089a788f858119f042bdd08df557c248ab3d2bfaa6a435fd0c1340c903db5bd25acd1a4cd05c88d71ad9e02c11abad4faccf1f82221cdd62825d1c470f312f0741febadb87dc39c52a9ed0edd66b54909f1aceb7e6998e25d09159a1b11f2454771145731110c0c887bed07d0913b66210bcfa45f1a6529ce2694d053aa2878304cbee7a557187a915932066764673efcac94adbc061e42e9a53a1157d2040021719048bbf0ea83a864fb06c01fd5818274350b39ab7d837fae177688c7cce30b3f50b11e28017ae40e0807879cbcde2e91ef990926efea097fdb4152a7e83de792aa59ec7caac02bd7ee0f6de6dc29bce743fb29ffbe7ca7d0592cbef5d27c3fdb27306cdf090991164005838241aa4dad417167fd9ed2b39a2115bb05a505ab8f40c8221e4fb8cd2f6c016adb538a570522d4b97c17d178c5ade933151f7a225aca6032cab8bb749031067f1552d3b3cbdf3981f3c9cf487d073c818ffff3be19af4be1b4f7e0cb0192074ac81ff193abf69d61eb67a1b65d1ffbdbf3773fa6923dc14efde6badd8ba802229b3e5827b057622ba979e6cb11694a309e22064b6a76a4d7b87079dc49c4059f9ef0dabfca5eaad0f36b08af0f6e9b1c3bb9299453fbd650341dd4d402370f9b7cefe1ce44dfee998d704bc41389094d2724d4d35b1847d5d3c9981f23f907d1aea5c756173556dd7e26ba5b7b80f69f56ef608c7aa9d89734282148e1a90d5376e31a5afe6b38584b6a192bb900a336daabeedfb4aeb86d27f8336969cc045ebf7b0481dbf3a954f60196014308519257b816a840e9becfbcba1756a1c104754a6085e20e22f67d932e4080f71f8d4a29ef9f226f8fc7701cdccde7998704ae6af96c381e570ef02fa15a5434cff18310604894b38223702301fb812c90068934777212f9cfe2951ef9e52a14bcb79b370456457155284f9de1963d2153052bfcafeb2f7e4b99e4aeafef25a6602b93b3677d8bd3b0a41fe46577d2636709f624c6175d17e1c21699f5e5929fc7adc65f99c895340f980a845aec9a1aa707577c4e43c7d8b9fd5e78a35725128b0e666ebc283e38d0199544bb90555bab05299096988f52bab3696ae5fbc0fe259148f5342806ecffe6bcbbaf8814914b0b56a6b3238b34bd08f9f4240266f67656dc280c633d49538b966ec35b857bfc04066c6a138a212522ef4ca50ce9713460027e149e850ecd03e319b5bd5e2e2e07136d1a47c0bdc5b2fb2766bea6cc48f65c1c406fe05bd1ddac918e817442360b1de1d2d8c016ba107a07da429322d21df57ef64ee36ea7f815fd6484623f030b5cde3752d21d50230f718c3cefb54416bd5a05a02c44a49115313ec70b3bc6092125c0ab8045111bde878ec922f4fbb6ce8fb7af585ba8867682d0545e24d70767efae8dcebaf740b9d25b0c2f2c8b4eaf99f68196188d1480065c442ff6f20eb1dc76da270225b916a085e3ac48f0c37bdcea7fd5a8c13a018129e751a5e80e089795fb4b2ff6ba512bf48b5fb34470189f840e7c8b86c8217b1a7a9567d9005316e67a12edf158714d6046b48d469ae83f2deb31d901ff58e58e6ed9bd900966104ecccfb2871ec1f36ffbceb92dbe5c9623a9bf2250ffa11da630ddefe704179d387046973587fb81d12fd05469d5760fae02f773b0ad1aea48a28e670e0933dd000decd3f6b688575faaa6e881b9cce29b92da5b9304d223a87a2740d402b6df583a60a357b5201af6122f70bc2f5442d930ef105af2205266220584090462aaf9ecb972fd13b8346e3cc889c19c98e6013d35d57fd44957aa6d05863609a2b3940df8e4312ecea9aabc6ff578250bdfb5a2718a67a9256a16390e9f2604b089e67d38980010238fbeb21a2fb3d2646325c65ee972d85bf3747eeaa63e027a7d4a70a855cb2902ed838ff8a75607082ffeeb31e2bd05eae1bea7fd252907649ec05f35e4b7d19d1815303bcb9b456aea3567763f730e1eab5e0e9bd33100b4586bdd5591ae8ef360ade16e4654b5508740ba83ae1344b2feff1ab2c69003d091659ffa7f5ae2e5df1f5767d5ddb8682b55fce488ea33e5f5622100c0f69e8624375c659a8f528c8e75cc0f85605cfc09efcaf09191923dfc8e38997726032b363fa25467dc632320dd2776e69bf4d06205eb41fce05d39a338ced58f1106b443cf666d5a28eba12b58b95831bdc3bf5e5fc0e343d00c883aa742e0e57a091139b42c2122fdf7d4e53d711e86c2d87244037dd121e0702ff862988d8e3504f6e25f386f4fe84acc1f5c2f2201b2da3fb2b81f69b80fb1080af7d68cd2d50dd72ae2f6a67977dfdd251c2b271f25855ab83ec516add98a2017700ce784ce0807e993ec165b3b71ef65dc7aaa39b930fde288d600e4a0ac77cf346c10ae7a078990aa6137c0e6d6549741f2c43f217c90b656a69281c53e5ed875d8cbc4490451ad62e6675d84fa359d034cab1760186b1a73a199656c4feff01c5c06a2d10f7b08ab8fa458da7473e24caf4f756d73f6fbd4bf28f050692a17fb8be68f62085a946badc8199f2904a392b7619acdefd66f162fc26f5361d3c6a402a65897028da96d5c7238d4c27ea421dff6f67bddc5016fe92f29528c3b816efcd1da8a04bd24478e5159319c1a07423f3d2a396d49b22cfa031f14487f808bd7fafed400b7d9a1de6a3aa9f67968c06fb7aa904705ee8f4ca13a493e39679f8bc75b100ed4c6b930e8d9ec2a27755ca8a1e21f9db6a5af3b724607ac343e147d2bc90f0fa9acd368b6833cbe05376c20419b788f6cbae069aeae7d6ee7ef2aeb754b9e0fb378b6eceeac6c02fae07bb7699ac78daa2737c916acb9c7474e9169bf058b05256f7330f2afd7f5c0b4eab011c5e7ba65d299b52f75623351e4d11b67e1bb0018fee0bfb9e5ba153317e9b90c113e55cb75545154dd98c643d8a3d7c30ec5020667dcaa3fd3356c45f40e9f19ecf8c569465433f25aa73881e23daf3733730a12eac783d532ca9dd915692abb5a4de2fc979c81da6e1b432b4f1da8bbf85f0bfbd1e0be0436ee7ba2a4e1b9c85b8d199a3a9defcbc08dffc2df86181d5cb60fc3f55319a8b4c28038f720922b07d64ba14acc2b1bd344aff8c2bf53fa100d0b0e1410fddf2d7f13d51b9aec7528d1ad9eb00d084c8856ea8d07e3524148010538cab7becfdb877c7a880bf698dadb31ec6cdf90d8fb7dba8363f32542f9fa093923eaaa7246394b3b4dbaf73bcfcf1597d5a99bd13acc249cea4bc46ee11805eb6ffc53342ac9c375e30218b867461bdf2ecd771ac03226f84fba62933c260a9819397ced703d6789f3c182e1762a0a18a2c2cf7c6096cf271942fd206a7109cbbb7ae754b127cf39552d252122229adacfe127d0b1f040ed3ba6855530c201a760b00c775e10bad816e53d7f314fad69dba7670c3f514940337f609f17270c3033dcf62bc8a75b4294abf731873780cea7c82a8a36bcbc01a0d9a0d29d4205142281c3731bac1e15f4ed6b2985f5574c9eb7d6df670e8466a92fa056408f0dbb93dc0518b93ef45ab9fdbb30a60f70664d7ee3ff6b6b8f7083cb7c28f7960d960173bf564a8239309f4d5b79f68639f3eb22959e72e5656997084df3c21d0e2872c33c08f3e012581a1e050ae17d3210064e2316646eca48f57d13b8eede07d4810b9aaa498383cc9f7df1d2ef3905e50c56de2d7758a594303c0b92cb1aa66ad6ddf205ec97caca28900cc92b9deb52fdd4cbe12b39a97467e376518ea808554eaf29e7d1e87b7eec6e7a894d5e208448078069499687bf0f9d7ae040850cfee5e0d728b156a086757ed645a7642b04830a144e0c15b0a3660771f9c65d001330012b5ae6780a72d2d10a7f62d22f76a56037fdc8c03e7f943432773bb10e43aa080d18371c35ed68343e543799e079bad8b4d4ab335581e89140d0b8980a5488ada754d412b23a31299d5ed46d462fc02e963b1f58f248113ff47d0464039b333e608324d93928d78650ce659647b7fcbbf7b6e59974aa62b83546d97604f0de3dfe9e1eed867e1caedcb32f7f672b8d559b46ee77d107119bb9063be00f332c8f2e5bb2ae1fdec8168dd41aea8676912ef674bf0b575bb3f6abb2e52e00ddc14966c46dda859057b6541f5c350f8803621ab734204b0aeb71792e3099038eec4e463ff58f560ded616a906a4893531a920ebcc30dc021caba0b943e160f9e3b0478ff48bb0e4bf2243eafd8a69f035fd1bda3801ca1fc845062a8c6ee09beb2498c646ca8153af365b26e14256c14675bea570f66be09a273466928f00f1a7e16d6845bd446026da91d1e0d23829f27dadcae5d5bcb7053910016c6760871085b528874cbb5995a9bbe14244e3bbee72d5875b7945416be259fd36798048f130c685f1e2c0cb8d6e0ffdca09d4187a26c2f575da0e76c7ef33904744103e3595bc4e39fa26ac67c5e78098e8bddaab7da2df3297ca495fffc1354ec410a788ac6c9ea8812e83d256ed88dbaaf5901fecaf293dce50b505ec90b6e4dfb08e7cd07dcb0dbd7d34a2a3b43cf5f53a858942a5ef186bf0a60a489da17011c0e1cbf8bd2bdea423058fe16c39ef56e0adba16fbc813dd7637b6563f6b2f3370285e8fec8722519ee3c91053d7f27bd0c9767ff8662dedc09994522994b4c340613acacb1010cf6e75e3af519430c96249274faea3f3fd0c8aaa482b5dd98e80f949dcb69e8527320bf985a08c77a7ee7796ff1420c6080135253307ced92c407d2cf5b801bc9bd0f421147e40c5db2b5caef28d7f59448fb42b8cfcdd9fb3e93e922b5e564aa87da524ce3e7a537fbd1dd72a78ef6567e332bf9f6f8ea45ff009c58365d162d0839d6e6159ad113c76db3984b1156d32fa869cd5cf6cdbcf70793500e68104ccf6e5a614627e6e937ea61249b98e588c83ef0e028ac3b08180219a7d84fa7ea30338dc69962daac593d8e887489a0fe8d2f600f25b982a0b0034972edadc4e0acb03dcb21bac3af2fadf1c4beb8d4a4d9427e9789770c64360447e2d8025884b166969aa3e4a07c786c22343b296c9ff2574c058af954687b0704781f3bb566ac2e6ca14770c8545498e8f05d9d7d5dfc3eb9ece22fe9dcd8097152df257ecea32f70a0735b2af310ffb973b5b83c154eff1069ad8befa3b703145c385aeaf031f52c1fdfea13d24047aae9ff4a801b5474cad1acbaba8d020f74016e706d325632563010194755ccf849f2ac7855736b3df73472a6f877f7045a3cc25bc35e9889b63e7bbe96d5d0de4e2f574a5d8c2cf26ba30462cc585a0696625be59be89e588c0b0da879f10547f1303a2c1a45351ff11b6747b37e9508f49cedb47dd4a482b8303a9afaf2b3393b15fc42a641ab125a5b498a51bf770e8bc49934e759643071278dc473d47cba07932dce12c600eecaf2dad8514ff80db3ef50442a85ff4e0976919978b2150a896c12a0bfb30e236c835cea3ed5060b8971361c001cf26c5115ea2450cbe5fa6bf6a2fdb87d864d0182461dbccc0a0fd404fd100a4c1f1371a2ff2b4551856a4d7ddcf229aae7c5829b1329aab59a0e083b42f2b42ab5a58aa486d19bbbcd6da7ae2d57ed0323012bf078f6a1df460c2a2a04d8bea9231221441d1be3a63b1c60fc79fb624d78d3042a399821f4b00e64be375938b5a1a60a5e14d0a409e8535b92b6d44a5eb132d120b4ab366f6a06c4901cdd9c0c4cd9a00c97f7395e671e1529ebae5179e49a8e70f2242224c4054590cb6fb812510d81be59870cc7888afa7d874b9db23c98585116f03e1a2b03824f2d1e77211e35f8c18d43fc30dbea2cabdd1b4f07f2805916289cfde39704a36286fc963248ae9598d9d81e9c8e2d138fe2efa3d156671d6117097868a40c4cb95bc39b1d8eefbaa441c9359b96c4feccb403225767a0cdf222a83608fa03b79afa639ffa2fb904d94c32181d6d4f0525caf92c39b420ac13127479199e0bd56e13e4d5b0c035df3ccb1748999e4fd8612edf073506d8f5721d909401e1044e52eff8ed26bea6e15489252fdde837f1be19de4f43308ef54d356b125fa5057e4c25c624ac5c89a9ab33516944784fbbc674e5e1d2f7df86830bbc8abfab0e2e3a82d78c1bbc64d18b260712fcb6bb122f035369163eece0ead671109b4a0e3505d8978cd01a12ee2691dd5895498241107e32e7a1721689f97e5cc448ca0661ea8e9e77410048e98e2713c8e484b8160899eab1a66f37f79ac76ea151010b674f4324f91c918d4fc38395aa91ab543ac2326e1a790f3b51c7799decc7b501 false -check_ring_signature 6e7b7ea886f0271884e33dc8fc1ac3e387f7f4bf91d4dca3f10e7fc1cffc2560 610d75edc2838b6fd146ad2c26843f461440a8f13922c5855a351ca9d0f637fc 1 7f5a20daab072ae9611ebc6bd4e6f909e94f2cc0d729129ae8fb662045e4fd50 63f31654a9733e4007800b18522ae2ff22583129c676eee1d51589b0e0e2ac0928df4549de3e1f7f44a3e94940b6af3c225007129145016fa15eff4074b2683c false -check_ring_signature 8a712a80581c2c6e5f7a72ad8b99849301422221790ee1ca082c9b811694becb 19eff5693c38befa84fcc2dd66cd30c6b2790f750ff6d74868292507222a21e3 217 4ea00ce3c25e45c845462e95602b0b0caed04fbc87857230fd2ed33b9306d4d6 e7675b09714d2cc7100ba012ae121dd851f8c84550dfd876566116d18d8563ca 30e83cde368210600d723ca1c65e5ff4dd7baba4a7c037c574f3c641bf91fc60 6fa5f18a92161fdb64d4c1da99df084716694c004b2b4cfc208fc715b8178466 546a83811cd6c06f0d0e096bb731d671b8a46789b22da6c7f807a23528013733 2e906caa5895eade356bf252a96905554e4780383cdaec877d9b64fd3e1c28d6 c89412a383d585f1ef3f99fe50b994c4211785985b782af1394748e794603861 b71674d40523c1bdfb72396189a42209755bb4039f23ac7b8f7e18075734501c 1119cb75f42327e7593bf976a07d0288854031cd9c2bab9db275c91adfd138e8 14144cd556ec93a7022b5b4cfc50d8462dbbd702b024522de917d9e790186832 907b664af008ed866282a70a586be5f1ac7dee275b2f68e027fc783bd4f1b758 9d68cafece0fac7ebf911176f3d81f52d55ee96336bc0ef3d47f5c869ceb4def 0a2997ad4d1b22d1a6054d2d598deb3259151a0f6e1eca5ee3a14932beae5211 3ed781d314a933b09ec20bbdbf568b5cc8a3de58cbef5e2137ebaf0cf00c2fd0 4025d382d91871eb8d6c7eb6ec064960e5a615bacaaf056de8d4a04d67b3a026 e52e4c7181b41aefae39fa418584b6bcb6e2a7ec8d85353065bc1515a8a4be6c 4ea5ebb2577a6f4b3d2ededb15b62ce47e0e67bf1757fdf056746e6d84ac2a0b 3c781cb63fb7da9fdaa32a6881fcd3f6c884d3fa8abd757eef03e77090ab6ab2 4a5154684f5df52b9ae927ff7092af4049ab0e7ac8a8144ac16160df8d6826cb 18d342650783c9f0987264574e19b1a615a9efa27d0d32b68f97baed51e0fc35 85a1a126f964fc727ec50652a94e02ce5993d0b06977fec4b83870b2c64bbde9 76259109ad7683743efa7248daae602c989d59ef616d156b29d701938e7d3bdb ef98a87042705fed5f4ca46507ccc830c3e57043576b68355bdb097ff56c2e59 f09117ab6c937f34eee2ae82c38b5d401cc1e957ed81115470b76e3f1a6ff1ed 743ba95a983b5262687a46da24d3481ba6991444d5a0e3503d4fc4238efda916 93d4ce941c4955b2f0d12c0fe537726cf907d42455232243a77bf64e3447888d 8fdbbf291bb92124ac03fb6f70dfa55c7f469f0f3d1a7a1cbb25fbd1b4c416f7 2f64bfdcdcad6716b2e58dfad4e0e765d9f98f689cdf7bcd012b73874512d409 2d41b837205713b54e89fc1cbb4b904b8c0c57e2e0d9c6c799b6a745921476a3 5f33d1777ee6b4a8ad5a4261c1d9cc39fd28db620ea38beb25289c4b5f4a1412 dd00cefc5ef16d2bc23d1d2a630622ee85bf9b8d11b8dc02a49166f4f2adbe42 375cdaa4e07174430c4150a4c58d7b8e2fa539eb9aed772ffac04001299300cb 1f5180fb1d6a1baa138ca6bad9796248c161db49d8ba30f50362f291b3e0187e 3a92868e74c01a32111e86a3b657b6331f3bf9f6e4c865a5be30154fa67718f0 ea86c14046f10a449a82c4562a7be39673ace96313d2e20bb4efceed618babae c6b4ea311d7845b282ff45d530e27c43d3899f6bd961d21381384564b9630e05 1ff10677a44f6dc04d05b602c48e26ca0f2e8f911685b619c03489bfdf351827 1f9a7ac43ca21bea08151fbe39e25407e4215c0ba689093e43f3e32e0fd830fe 89b3f9a953a7136c5ad75e8e9d5d35a8e23fd8fccd084e500405f71b53084aa1 9f29bf0bc81d6b3ad2f697bf26dd87e8e376c85ff7ce4d4448fdafeaa3a1c937 0c5eed015cf6d481caf3e565a8894cd4166742dcf3a5bfd834508d090f066319 e5d353182ccd8f9247f22219bbc7f54ebf3589475a1748980ee2199b8737e169 6c4898e89bdaefbf42ad6de8130ae14100a1e6216d21f2aeac8fb2782d1a6e78 bdaff671eb42cb41bf07da72bb61a00a85b576b50a1d56a3ea7a7708c34fe70f 20efbcb6c8c86a9f0ee27739c01421c9d855e580e81da0fd022cf56d558efa55 504801db0572bccfb8e86a0736e31c3797bd7565d29389fc5bae369993f7865c 84f0ed907c449faa22bba30fc2c773922948d4f6f5bb3071745788a66fdd78bd b01254f8a1d2dbd979e8219ed5861897f4ba58244ddd3230dc89b33290c444ee 4a80d55d9fbf51d99c7f3ee09b93bd3866aa53f15b5b2ae1307f8aa84f22ed03 42dfe824646ebdc3d52ffc6f8d23cadd1e9015bebba65d91cdda603e0e7ba205 9bea4ad7f2f9eeb783e91ac51b4a8f307a8d8bc21293cac8a3cee5e05a55bfe3 cc870f3ce56a0eecd49f0c179cec8611cbe9da73f134fbba5ce2d51dd35eac86 d19532e5501c2933b07b165d07199e0d9e1a2ba7813ab214cd1a5ca9ecdaef33 2d132a914618ddf19f0053012460170affca0ae5d17a4aabbb0ca0e10f6de0a3 4aa48a7d1e2aca5feb74e4c283ee91b19f1d1de9347736eeb499e23f6c223d51 45877b1be39731400e377e822d794cac12af56cb148d8b1c40970684141fff9f d5f164504a696f944c99e5964f4899b5ea295591c6a805b1075864fe891d1dd7 1a58645fde42937e58e408ba9c486826fa5cc1a3b4c6d5bc2707d0500a214f71 b3046604e6e09f29a165c69901def705878a186c8e924285f8182c3dcb8df3de ba9a07bae47e61aa94540af665b216b346b96006aae3cd82c2259c7265c52dd7 879e9e536b8854c7dfe96c9b6f0f6b061b44b18e5cb1b326dd61f4d689a33836 8e6bab8aab45ad9776e3528a62623ee781b8cc9019f655124f0e109c9f68bede 22fbeed255e2c4ca6e4f4baf723bb3b5b6b2ff6fc1ddc4b7593e2650a6884733 7b581e39b83b97bd9437564d26d72cc0efefa8629c6af6bbf6f76f064f8b38f7 e81dcec143dbeeb90ff8d96d3308e57e5a18de269419ce10a74f91b42fe2eb4d f12fabfaf879801b9e4c8376b999affe81890e79af219e042278f3b39d2666b2 4f4ef108b94ded532f059d68a4209872cda5dacc2f325ca0380d9891ca661b65 3b4c6ae481355f0062f41f0f7453e29edf655e1a71ead2f4de0498297f0d7036 3e6e3a9a1ce1c3576db571857e3c5d91c63cfab3d2290e59fc7319e38f06c2ea 2bd15b746dc0251ff4d0b5446ed2d3f84a3b2cc79387f63597039e5ac33041cf d92fa0d25c8c66ad1c24bd492d3b191286d20bdff302711e141c6447cc08f3b3 9abbea980a3657f75f4ecc1f67b00e5d0aa15769b03e9ce19d8633eac7c5cdf0 333b41694f7600f6ee624ef087589745de098af744c965d2893e21deb382b768 d646fca02253c58c97c7bf4ef3504657efef8d5c33d480eac4eb518c07cf38f3 31375eb47f2003cad6bc0d3e68b0ecd258099ab5f1246c5ef87dd27c61e8fdac d694ce7217173bc2ea3f7e4d0432aa4f8239ce67949f7d37731519e6e1503fcb 568b53cd0498c34a4d05eeba04c114787fd0eb64aff3ca11957c49d45e6b4025 6191d5f5fcb56872c5fe2136c697b5251268fc7ac0b0b295e63c50c6d611ad3f f7d617d27d206467ec7698225dbec6b88837f0a4adc62e01243d005e5cf9df3d 9586ddde975e4e7a1f5d192074d66b52b20b7f9c0f880da6d3371528e7d79877 a07f897cc86ce717909795b3557a7efa3b3908c914da5880b60a2bcc16f14019 bfbb81e7bfa914a0c02f3c34a2eb8a69eafd29c649eccef7ed9e7488c79512ec db1ca2d183e8cb2257b0d6a070d98c857e5c25426c9d27ec20dfd627b61d31b8 d9a1b92863e717c6964de3f2c3a9c89ea1b9d2c2dbc7b257764fb23009253dfa aed15c2bde7f3deb0914565d8d331eda7399899aedac8b6849daa8d9083eb6d4 f8e0e46717740cdbe3faf66ff9f9ef86884da6ef338c4f8be2df1e0ddac54124 ebaf591c68949ee834af9b9b35baa8603c04cda4f945e021024ac4bdc54311fa 2186f7743efa5a1652932cb53fdb8a9243a626641d8fcd7e16372acfa9a8c19d c00f9b8e655f7cb3db58831b88ace5520311273a9d5b4c7c830473e82863ce59 34abd38423f4a5ba99bb6e835248320573a4468eb09fa23e24bf0e576cf454b1 8eaa54ee3f37b903e1e6b2c78d44dbc8d0e09371c6d81cf65e8ba489ee28f48d 5746064fbd7521fba25917d8d80c06814440adb5d29530c813abf7fda4ee5e19 356babab8e048494f44550c3ecdb9c209eb5df0e9bd7b2b9cfda61a8f5af4606 dcc044f9718b3877083c64fe885b2a404ed2917a5a5fbf1eaa1415b073aa4dcb da8d65a3b886c5fa7bca282154ebee52f55c22c31370f11d5356c5955db8916b 8a1b1023d7fd8802380315357b92c29589b3cf03d64e1e921304d02c2f9cf670 72594fcdffb0ab1b30de2b6a104057b5f010e1c60a344ca8e898e68270fec760 1992e7e941586618cf408127a0677b5b55c1a77b45433398c87a7864193e548c ac705a0f0ef1a0308e5467bf3970b42ec62306a19d767770e32b9d18dc59e0b0 31970eb2c177c84aa9f2a75b9065573d5dcc437318e7b39acf99324f22a6cba3 6b8df112d8b3841c0c390e0494c81521fb1bdbe251a7615fd15bfdced4313bdf c1e684d1a5a313a4ad030f259c114a7b17c1c6c7fed39343e979db4aee1a726e 88a37c55e648ead961d30a9744840fbb8708bdfebf19d897f1b99cfdc427b095 88132d63485740e9f8b0efac8514ef6bc9d33f3d23f443de7554f2bc135bf322 d5ccbfea27456205ba172756f088e5df187a9206d433e438339ac25c989339e8 63f83f5e99d0b69f62ef749fbd5a6ce871bf138ccef57737da102409b3b22f03 176b9667b59895a8af8a0e8c02766e46b94655f59944e36d0b5839571576847d ad1b1c8c91d23d9c3bd523e48034313f020b6af1e3febf70bdca9e56efb13bc0 06f6bb90c3871d1f7f60db3a924dc71f8af7d1a3fb32df13ca2fc85e7c4cdcd0 772d39850ec6e7c96f54b132bca5642db737d1cfcd1675675ae5aace34f533c1 07a4b2e99a33205623de60d67975b96d98bb5e7292a556b849ae1c90301f3ffb d47d61b6372a153f6c139b01ef59ee8e378df2f3063e4dd702003ef7e1c0bce8 8d5a85345350753b382a72e997c6a3ff4092d119f627041d05bc9a67c2ad0c4a b22c995a99d231aa9bf432c1a82ecd00c82bfbf4ebeede281c7e2f7c6901bc8e 94a4fd3400bd348c6cb2eb489709709fadb3488430fd203654d0935a204e3125 189c1b07ee52013f06964213f8e1561b38c298c6f61e4ebef493238a661b4f58 c8cc4b5bddeaa4600c7f9778f30f56d4864295b7cbc3b20b9baac0344d6aa6cd 595672ecd3abbae160077295b04fbe6b7101d17fc83fe03c9201f55e5284cbc3 d6a29d2c5f11974ed61403756b27cce0d34774f1af812c08890110cf94f3aa9e ebd900c7754eaba20474881e3d04e55c9970a2c3dac4970639585a6109368b00 35f1b57d507b52254abf99f0f806cd7c996f4ef617bbb5a8ff203d2f3fdd872b 81f95f3cb4072d7f3b0f5e5afdf7120c594565bca4d3e9add7cc76a196fba0d6 a34cd82715bf94b80c11653c1ffdd5dfa0cf6cc2347e0ad554807da767fe44a6 2510e8899ecf750a58de2410f3f165f08f5abc30c65420b691c351729fd3d288 9e5b9eba4fd6f748a1c56f6d25ac8a52d27032873f08329ab8bcf0171e74f489 d178c8e32f969746f795ca37ab48b7f67ab194680cebf814fb6e02d9484aabc2 f9094aa45c1e78302ffa0038e9b26bdeb0fe0d57b28dbe83a13ee06ece81b3a8 4b70695db9b450051b36ebd4405f19286e05e5bbfe71df6471b15099a73b104a 7efbd879d58e7433f9d226b995ada2fd6a774f84b46d3237b2f177d5d811f0e4 f3f311a555809b4ffe188c66914fab7ed6ae02ec3741b256f0610bdc36242e2a c83fa0d40354f7834264a0188f42d7df2b5109cad1e24ffc60ea95b679a5c4f6 40a5ba65226c6aea04474b4904a4e71e1fa7c414a06a505c6ea1f5ffd85aa507 481dc8fadf0ffdef29cd3d9944857299cff21e408bb7f295a3a6c4b0033cec37 78e32a6811e53d150d2186cad396da137982ae2e89641d684f172bae35bc9483 efc712ee0007cadc16e6771c72d8938f2db4b9e59987eee85a691563ad877832 54fca588a479c631943e89d80162cf78da07bd834dca1c43874cc6d3a45ea48b 5c993198bd208cc062938fd38382bf6873f34d3b731b367717c3825f0977875b 5a60d4e1f63be651be933bf0affd2ec9f662c726a10d107e2340874fc0e2d9f7 d87415e5edbc86a4f693e130adad74c1cb7e09ad22a1d9f5c143234ec5530574 552bb5ee4ddff6955b6ada69d9ab4473cb8c50dd06ea3b918668eb9b3635bd2d f52f6222a3c6f64675625b0512effadbe48a0a392d98de15dceea5b20f62b0f8 cfd4e7f1cdcf469989331c40e4713f874d05c5ae13ec0f04baba75cdb1c5ca52 04d9a11b79c7d20bb4663eba94ab1d35149f7e9df1fe2c8c9fc380820df4264c cb4e0a3ca6fc3d0c373a829c464c7b6e75d53d4b825b1d46c34c119acdeec3ea ee6bbaa6811789af6c6662bd2dbcbc8a44c0b750a5d56b02a4fd1d539d10e7f9 1717b7578141cbafa446190da938055c89ef71cfbc8e1d4d9e7f9ffc73c642ee eeddaec694768503ca4f0d1c94c559f756999ca74dee6eb3d53ee47f22e51064 904c37bb6d942e3eb8aa41ba0534d1ae7c9d71e1956f55466d70c29d6216259d df064572723c0175a795101fc9ee0d3127d0f3edca38ad4aa904136eac7fed1f 65e6ba66d3b8243b3ae55b613e769637f0b25343c1b781263869c02cd947805d 05c0cb891cbbe9aa0f537feb7086b5bbd4120735715ec454ca0820f7a2bf9a12 5e60170ab0f49fe21b20112314adf2ad600e2b383faa56bade852cb06dca4d21 c672a7e4cbfccd0967a5ad8cd71d55ddfa891d7c5cd7c12c0fd0f55508fda5e5 6f1fbdb6ffa7cdb87c6503ff432d210a19e36a5f435e4fbfd132de94a3e55502 c27fb033e4649cdf9748f29c3a6f1149e1cb5396cc6e7fe95d7b622b5fbe71f8 7a2e2a5fe61935b339fcb97a97e5eb1958a920100853c0e7a17b5f8de39f0e01 44ec9e0c2fe1452a382d717292fe321ae79da5f7a14faec7989ed0e200d76042 f540fa0995710ffbda6c3b0179c9636480646285ce600bd7e4769dc431c37b93 edcd552d1c2621e3f1e63bd8f276a8d9dd28983b57fa8316ea76ddc53564ce95 19297e39b267afdbd925d87c290aff154d892a4ada043a7e815881ae4470675b 0756e90412bd820da5d5507103aa1eeccfc8ede5e212bf91c95ba9f10fafb52f 38d02b1f2fef1a1ea57a43044519ad1ee241a5dfee3710cfaefe40a3f3947aff e54cd6c96db095d5fdc0cdf4adf3747dc4811acbbe7f6ee384aecf5f7ac7c32a 2de5e4f5b1e01ab42283b181014bd65b15657a2df70c3dacafe632d6ba6e3418 81e90122e30712b20c28f8e4cbd5f5a36be495361238536f0483fba3fc6d9763 ccc98da48be44b42c04cd5b033224bba73f50ee3117dc20ff3adafba884c2e0b 2977c7a0513ee643567434c434e8426ef7ba5e7d3787b2b83648834877231b06 73449d3f4019fe8cb47482854f1ae93a568a3f63bb11c697aed5395393c6abd3 9b12ee4e8966700751b773014834ba9b2dd314dc5fd76f647003c91aa09c427a 222ee8cba310fe4ca1db78b8c14327f343f123efbfedad7b4b57b206449aef8a 20ef9fb665392d96b6871263db599b870e424ad2f6dedca03a876c70c0f3651e 8f1f090bb444ff7296096f9bd090996a9d02463c46d695301991bcdecf6cff67 a743abeb935f0bb2509b4669688f470504d035c1a8d836241a62b2b5e93330da 3c251fddbf4ff2830f936cf47e1da0b7f0f734298c11458047a87170bbd09c86 19de48de2bfa1ad6cf0371f8c6e3e7ec8e84194ad55370c19e66eaaf0db1c52f 40335c05d009eb33394acc930e46e7d356c33bde1057722dee9f9e90cbb0c199 0ef12d0270d6378571d2fa0dad5ce0db0eff6c521ad6e685eca2805b716770e2 3d91fc69afb2a2d7eeabdf126de48646db4932695c63221ac6daa8a51a417bd2 407598e3beb1ea3183474bab723fce159e97b649e188c257032399161ef3fe96 0eac8691b0e4f9a55934c7df535c46d77ccf0bccd31dee0ce1de02b4de2014b8 2cca28d40f93a0ae902c5c08de0d0cf0ef41113ffe41dea3a98a8fc77d0c05dc 236021d333ae9bcd8b9850e8292bb8b3f51030749267ba4f00469d575f086737 59f81adf40235fc4f02de6bd76ab3beba405857e1736832c961a38369132417f f2d017920a9394dff9df4bf3c65841a34a8881286b609811efb051d85368955d 6c9af8488df4d8f395dfcab0a3022af9d21ce979dc6844e8892c12f926a55fcd c5258889c4d503c7144c39cfaedbb22a00c03a879a5e3658cbbc1173368ab4c5 a00e6fe0fb9b7bae8f23588de082221afbb93ab15ff5b23363060cff2efb35da 8f202b8700ee4e354fd04179b619256ac175a176527854d76f5d9cf68402683e 4351dd866a0e36d0baeee7964a3505b693086d896889f94cc81292a7fcea5e88 81b3c5b0da17045771146d9511ca6aa742082f7aa4939f9fd62f5e5f21b57555 54f2238bfd9f909f208a94460a4bd26744c3f9f76a6c13d44630327546a7420b 25404dca648c59751e9e789ae6224dac8654cf36e179757d0378e51f52618832 5367c0c2649324280ae296e6ee5eac8798f2601998d82988001af2346cfb7272 0c7b5fb1106db006a39245a1cf8b24611917b6d8baf672984ddc5af10ddf030c 44606802fe0c1ffa54a3047702dd0f985efe23d1dcc8a98b8d4c9a4bbd6d358b 9f702feda51f328a1bd2e5b3c752ea668b4734fb7c53fbef518e263016fd0165 25f7bdcd7180b1757df76a21b6de94a3018db8ed726e85d838529b4b0b769aa5 ec956d1acbadc976eb6dd7b003267f7ef2b590aa0ab7e786b3a6750df1220280 62ef6001a1d18d2a31971ab0665cf8c3ac1d76b8ba0cc45d471a7c55c433506f a85f7793a522f18cef9c44e0d61172f0eb71e445ca3e810f72fdab1f07168d0c 175391427f781e0e9286124c8bd8b3693b550e803038a479e82775ea728b9060 082c10c22b8e4454b58fc1a3207bed980e987b291488656a1a582170049a56a5 da7cdd3419e051afbc33367a99c7bb1975f79c51c560251832735ea12ee15d8b 702526fd15607604129aac368079974105a4a183267e04e8e7dddacac26dcf51 2e7e20f0b19b697c5ae8dca0829e502a62cc2c98d9a573b8f5ceb8bf2f5b9b1a 08c73b7be62b5ed8b4628f149a3636b7f73bbb6cb4a6459effc1a3f7122b12a1 1a260d2f556a42d6655878c37b97bef17ccad98de05631f7b0f47b1cb1458f5e 54fcc028db9168a1b05f84dc38db2cb9a277db4efd4ac2353f689929bd9d90f8 ce3f69271d9bb6d9ac3a77f5ce74f8aa108448543e9d088186a5f7da68945962 509bc2eba10408b67443dcf0c9c72f2194fb230ad20be96cee8b034670c47335 53ed81d17abde6987c799706460e22736d994ddb28fece8a11d620cf220bd40c 4bfd321b0cf1bf2d3a4e0293d75a996613d033827d9d7b2169e4147afe8b1799 dca9cd897c1e499a6e23da9504ebe124456e746a0bffa94d36d1eefc61d21265 45ddd34291c6970bc4dc7af1ebd02568b9e45f600d2432d8971e2d4b4cabf9f6 545c7b79e57c22b66c6d82208c8844bfea2e411a8d9a490e0c2799f538798090 f8c40e67968bb40fb5c318f724002b1be85918da47e111a834a6b79e3ca0f5db 1cea5a15f610493d2ddefd6889165d8b023292e6f4b2b5a835c63071850b6a0b 71a4a97f40a23be3ec64560ff6423ab3ec8502a6fdf7f6b474545943e5730c0720bde6c311889ad2f71e4378961882342ca1a91bcbf4392168bac892810a9001a493804e9c8151200e4ac923ece00ea80c46d0bd39eb520e27fefaffa4b4460788ee80e7fc820bc4c63ff400a1e8b2ade927e2e9d2478edc6b92c10b5317be03e074557b5eaeb509262d8ea090e2a4dfd7fdc59d49cc77c87c735071d16c5908d2b63136c53ac27159efccd4be33a93d7164b31bdd2785b9baf22c33825e1105313af8f4c1c6669154a9a9b1bf64190ca35022760dfc186efa58d29c87d1360d2b8c87581655ecf3dd283e69baf65e7ab297cb96ab32ba4cb501807dbab267023d72db21185ed8fc1c80058546cd27e143285666128aaad400c27f811538d4059fecf847da02a470456b48533558c55a5327dfa6253742316582edd3a844c90263c1848c287779a8a9690a3555a88afac87732c15f50b36b02c119b33be0660eb1e7e8f19195ddaae4a3c26e62cb68d3b4032801bb36db3598b8eea403ac0a07e665bbdd1aff4a5260f4d623a247b0712635ca7e994991b4a9af9cdb974c2e07bf41dbdfaf381e83f0ea11ddb4a9de7e5bda91de6de81c95b06a863cfa60ae0aee1bb56ac67d5129107d8ebc8c68282c9493383bc3364fdc1eb411775b2d6d0577f394659c0dab634ff1b9027d9c9f60e201f7fad4c6569352b4fe6b37b02e0eb98c4fa033fcdccc09337aa6f764ea2dd993bcc771298db7e7c42c9579380b0c1565d3850f969193b0629786a99bf7a36c9b6bcbcab08ccd85dc9a013575570002f55e48af1f78c2bfec923344ddd60c5f985620c31d05a6b3331fc9271eb10efec863657c3511b6cc6f60eb55e4bd343a7b75b3891a193d4f5a300fc6e1580160c86f7484302e7d56574769da76442026a171e83c6f8253903f0d3ae8be490d28c27d2cf488a3cc466a7a1c957a09caf447a6c799f263d6a34291bb253abd08ffb399b0c50d442bc5a6e59086ffaee8a785bbe0bacdee1be5962953038c400ed85114e2560c778efd0b6f6fe549705bda73a43f89c19c32f574c9cdf562e500a2fe26e72d6ac12c11e6249c6ef8636a4ce24049875e3515d794c0c3d5f64b0814b79fd7562eebb95ec7b6c678bbb10b507bde9672c72a74fbaffb99e6d2b70b96854b57e29c1afde2a8682158bbbdaea185c98349ccd1c255736b8f03711d038525ed8d3f7b627909b261c5ea0b78db7a141e5664bd21247a39e025f7155b0a091f9debc5041d2640f9dc4fc4596d4d10c0399f92a71c4156f79aed9efd6f0ac3d74e2e6d4b6fc62e9f01d315246dc4c5c0bb70effbfb4443f450ec085f84068a1c7f12ef8e0a39dbd1a31f09c8f225befe8d7b9add30bde1079df67011380f734d7bcec7491fe375b591c720c4e3c241537a08aabc55353c209a76480bf5011aed6f0d452b4bdc743ef6f3562a7b4ad2ed0905ddd8465d958e8bc92ddf9008738b92f3032f3f987fe7d111c4dd6dfce18f3a7703854053a1376869f6fd1a0d666d2bb5eb592baab26d70daa69d76b4ab9c2d0055aad04474e2d63c1c35980ff20e4bf733bbe394b115c0033668eaa455b67cc63a2cdbab82a209d1677ece088bfc48ba0e7aef24379c56be6cb62ecce60356135613805db31678c64037ae088dd776ac0843ac465c52668ff4870db242e8697dc8b15a18887300606c8f1a0a2415b37eb909551e8ea87aa25cef6b06971e1f6022ba76b563150f5530640201537f95ce2d92f33dd09425493d8a654a83bb56a106adf93ad401d9bfd6db300573083f795156d0cc5ab9efb31ca6528cee466b92a341d30deeeb871c96df970196ec28ee5e6d60d68f7b65817f47234213f14401631e477e796464f25e574f021a3d49d1395f8c6d941c5fa814062d88caea00a7fbc50552de14d4acf32cf202e5b79b44b204fdd7adf1fcff7c95d17d27a63611dd1e4b13de502e691a5ff704317c63fb1ce9a2f74c3b8d02b46a2a29b3d991c59a104e6fecf01f7e04d1840f8a591fc5e38021935bec50292d8c6ceb7494d18352ebbb96fda54de5b2476100b408466731576d4f915d7da451342ef13a06b2198172a73e4bcb21f13d0f0706df23fdaf3c5901b071e605219363db657a6a490517fb699d8217165faf703900a6a4f234b4e6efcb906d05aca17b013b1ce82354c001944ed57d12bbbcc43c0ef9a761a0f9ea9ee05729aa01357d82f62b3aa027b053cb9efd697c0905481c0b24e51b1634bc7d5058442cb1a5db2a3fdc5217ddf129f4bc31552308c7aa1408c635c0b267dafbee1ccad0d25fc9fa051f3eed40bee5935c1aea603793ecc70814a4c13f60208bfae0d35c0ebc23fbb09ab4fb3fbee34928b2dd79f73ce5b706f1157a716820d125d032ab14ed29c4a2ab77d7900671bf68551939a0efc76f003714caa41b6928c5c93910ed51bf6da338d920efbfe457957413ba39db9b9f04dbfe34b647600837624f02559b2851248f433e22a409f1e94c7fbe5333b4b009d5dbf8cf97cace103e1afe5183d026255f7a766769b2e192796aaa46d4f93d083af1345ccfa8e9da6efa183d5390baa8612c11b4882361f5a65503007dfce50b019acbf238053d0630e2334a30fb7c444afc65e5983cb86ad31c09301aba0f0e36ecbf637b181e571d4c0a286bf110b06eecbb66778bcc5e9663ef8749387904e3298126bb451e92487a08918183ac871f43ee96864680f15155bbad4a4bb30b2791b9581e875a000c30f967ce7883be7d22858286cf73ed02aa4325f7d3510310fb4287d8f08022572eef6989b0450cc1d5039ebd1ed4d4be461c695bd79604a9284525b7efea17acfd1565100e16c3f8c8145f39dfd8b440b43b75c177ed0c7869f93195ced27e513eb04a8033f3a9eaf063bd3ff222074d59668929fff404eee62fdf2f086b1bfacf790093378023f47a96f0341e10671b2b5a4ceb9a060158db8ca3f971451a6a83d7de6764e45513749608e74a4c58828eaafa3c3ec40a29c24f464915c33d7254eafd9ad789e40f06c53d4faa161a236a268280ccbc09bd1a3794b5accbd4eed2772d14e0fde45e93422681afbefcc076b4239f44620bcb5478100efdb95f0591cbc5a05e0d56575922d9143a1a1d9d78247cccac9f0806299498a8fb5c9b36b9d52722e1d4368e2273bb9ab73555b944594aeac8700eba189cf820aac6ec101cdad130ec5fcbfe849296d05d274c5f68dcc9f7f0bb02b52ae7cba085e4d9b1035e654fb58f17a87f9f60c84cc229cc0da7413180f700bb2739cf7563ec86fe90e82d9981720525737ec3cdd12a7c00f88c3676293b0c69db690e435b0a5f48c92e4e69da5ab1a814366104d08db4b9d8d587b1ca9c003a6ffcbe65986c1c2c1e83e96782542aed334bdac005fad85efab2284d99d70e6aa2735f9265298626092db29367c6d22bf860a57dafd5fb84a652645d1a05089f101fae651ec56559108c06bc7885e1e13aaa6b2ac2e504d24f65c67294c80f6e6765b705228f285302e4f21f21c161bec52959a38479e059a9f02b629f7b0cab69c25ebb0c4fb038d01c5ffa163e7a3cbb70e8e2b121253e2175cf0e9b54083cc918c89731825055468dc2c8b6345a37ae037931c4f0b2671490d5c05ede078505a1eb7b8bddb0a816e57355d49187750774bb5168b0d95e37ceee483be30549c7c307face50c1c95d7f8c65ebf47784f03d1c9926ada7bdb181d17a7e48007e48422d54d27a4499f36a63e5e8a162028e3d552c09a57b1224182e502e400729210431107f7b0fe8667e81392d49a3d24ff52067f60ca63c99911852cf0f0989b08fde4cbf0cc5b3a2910e817d561d24e3dd8d6830bc32178803d3a120e504b944e008fd24fdfba32b88bbbf1a4b1c11bbe88a2ee4458c9814d93c252ad702282149a104dc36ecf97fcdff5bdae1b6cc6cba89b7883ee2736e5bcd3a51d90d28d962fff2ffa7660a4c63067a876deaa8a39b48189115b76b0466626f1ebd08840ca6f2f6817d69ad2345b1505a3bc746d44eb7a22f0ca897b8d3a1b4429a07f7a3a086194f3b3b9015ffa890f60c275dbe212e89716b89e27c387f47674b0c870a441a913ba078af30257baf50a058ef7e5fd844284482de2d0933e91b1a0b118f47a4184c10ad9b331f06ee48b7bade9e54b4e9d7ff546f468a59cbe356033c180e8bbe335294ba5e8326c116e13d31bff07465298a9d575f92c62b8056016ff881f3b02c786b9c9505c3717290b80d61663208d7d7ed48ede5c7643ac4055c2d85fdd0debc6d7168e34652c1429ebc8daccec84e91a61c8e8133fb8f7604cbe1a866c92c74f9773e1630693dce6942d87cbf92ba9b16a2826044caa2690e96e04a52f31e1dcc6156584fda62d8463aeb4e8cab44bb8fd135cc728cb1b908361f13d17cef760247773fa210e66871bf956aff8c9bd14d876651dca9f23a092e87b6d19f31deb274f810d163053ae4f0c3d1a845ae20d3b1dedb052f86e10b059149488091f165fde93f8131f49482db3a1c5a5d236b4c3264215bdebd4e07df40f6aa66a4a4a324d9e79d8d1d5cece06b58cb35a8a11708e0debfb0474b06842988a82bd6cbe76b181058ae6ef845ed32606cb7caca114214525801a63407800063bd5731417de720c1c86307226b7690dc7dc200b591ac7d836889559306198d62b2c1137694bf4106f40dbe03a22e0dff103f6f4b7759ae5e01f7d4fe00132c06c849d8cc9aaac34c833c5c5747688f0666564c8bffc2f2af77c23a3e0e75c558ab468c33cab6479c22632fd3d3c38c87893ea5ba1dbb3c388cac675301eaf31e54c9a12eee89a237da36ad6499a1193775d0edeb827da07198ea99180acaad160c0866b6b6ebb0e8788a9ed0842730a53a3a9cd30670e7133f6052d20b324974ffe78b0556a3c4387968bd14aa30c043a25940bcc0218da44980e4d50859e398cf7e7076c741222d7fe34c640f6b762cbc0d69258d074331a57b1c780e0ae5d451e50fec824a4ea33f665a5d8398382cf790cffff7419fb96ae6d2b10728c6c85556cbeeab0c86acc10e7ec8fa60e3d776a64615d737f0c5fa053b970faec153948c50ca36ca4041239aafe62ec51e5ea25b1a6d3ce2065af1e6b7280a2842e4f3a42711992346f06f131db5bf365bcb45be363938ac09b8f09a2afe036ecfe875bb08c49103e832d797d84f831e79cebc49fb2f866050b614df19ee0e169c15f5e5b7d8c97bfd609b2762a441ac1efb1880d148149fa0527199cd5e01a20792d9f7328a32dab9899946ce8a4b657255f86e5db3bd91cebfd90708d70554b2300043e59fa19ffcb99b8d28e3e5d0cf2ac66ba151498e57aa96030ec80a98ba6f7bc5e7f1b7d348c0fedef344c1660ee91fb945957db4cfdeab1ed95900462f876ea84f405304eb6245a88c83da01d95b2905a5bd03e8e18ed081fd29047318771ec5c2d46d8a7df446d7bc5151926d74e7d2049314ff037bd0ad4b3204b9b4c7921572228208807598fcbe56bb9743f0ea2cfcf94e88afd8b0add710007b9b3f5624090d335a65b3364772349fbdfe547be69cc628f6ca23f161548b0d9d579cfd4ec58b73840a6e8d3c4ecda99df2b7f40396ceb6b89fa950dfc44802711e085abf416ad04d1b6d227f18e2e283b16ce1efaf5f83e44b48f5bd5b030bd91eb4c41fcb8b24c1eccbd6026394437e4c57dbb018b6926056acea53647d02163eac0cd21eda3a580d08814a1870440d77b9b1cc1a4de8b42e32e71e5979052ab6907f7a78a1e54151d9cd056494aeeba28b23fd95e27c06587c8c3dc6bf06aa066289120faf4ba13dd3795b3204569e1aae7a9e355284574b6250ccf2f201c9fd7503916080514e9e734c98f116329efaec5a4ad7bca624742e5a3ad67105c0fa0aa78ad865e343d627cc7dfc6c91d0ade9ae44ba36ea9706f20d9cffd90c30df7f4ad596b3768ca9c55fdce8dd1dfd27836c80352ee41903a2963d5e310494e232b3b43755c7db9c5b179a735ebf3abad3c85622ee09d2cee3c7721d9a0ea438e28d79c1409a6d78742184a2fa15f43a1af78124b1c9dfb0444c0698410bab30d4238fbe62cf490f4ea9448cc48c87ef56cf3c4d2bb815ffd294ae5e330814a582a5925e4042f537dba7463d20833eb2138bd78b9bdf95f5ba5941ea2606152806761cc7f2634727537b9bd71dd9d84d920b7de2d821c72e448e532f2f03f617893d6bca686e6791b28bf9286d1cf15ebe54766b4824b5c7c38cc88f1200f821825e95522ddfd6a8bd67d98c65fe3446534c7ce1d34d2c35c7a9bf2fbd04b6a5fb75010c0f187278fe42966f659c745f332015f3e33ea9af0600416aef0f26fd509f214a69f18d211f563ea6c4fa8184aedcd9d3071566948ca2fb36af00ab3c0d89359a43e3200a5990e735eecfa3f167ff95f3e641d9139afe4f01d503d47bbf5217947d3eade9e5205dcaa491d0b9796287dab0b5ffab2c3f87b15709d7619e7a4827378cd6573d63eef3c5bc37c98793fae011534dd7e2b7060bd8041db04f344684d62738003e49fb5f4aad6cad4d715529620a9a78d0a41b371904bf815c257388d7f28703ffbbf272f25e110446cade10b637f0b6a9076395d606216f6ccde3a5a6a433926298ae6546491eb827f908ae62a2eba960f27cf3d40bd38581e66df6a77317330d75db9d464107f95d202a96de685e11232bcca6310f0b287dee8197e65045457b4d7b21b3a0beae16f05b38e7a8928b796f5e63ef02371fc83081cfc15d1f8fe4d34d2ee4b8d4b0ac05c934b9f23606f185ddea6407b79a6f87c6932f19386986a558a8425d760cac379fbbc8af5e67e6ca238f580ade052d0cb7e3e132a1eb2d660e32dd20101da64057c687d9ddeebfe3c7467c07bcf9c19bae486e372ec0432d6cf77b14b525506c39dc19b18015a526ebc6ec07523e51c8bec898dbb11c4f9ee02cf8663ccdcf92e5e397a19d0a22321e77b90669a84a8256d75580df940df01c26cc2e12d95b2bf462d21a5270786d921d1205f269017238c6b332113f631d8c0eec0ce47863e877096d4be46233fd7b327800be1d6328db9166b145417bb8c37a7e915669925a74988d2325ae86267ef9e4098411dfd59d4db8ca30163ec56c3af319a2070617f2c8f4b55bf90c5c2b8037054db04bbd898448b2b929dfe323b75cdf77dd1fd18b9f4c27aa00adf15ebf380bb9bdd2edb643c12ff83262a7cb05fa4d57d9cbaad044adb550ac4f693e35e304902eee216b4be2611b46c890dd82cc10080ade88e3f495edbe4f3249dae4730d6f05ee961711227511dadd82bbb96824b3dc1eca8152c7f521dae7febd3d910e3a38be7ab4d6c4136e95e94751910381eaf4b7a9de5208af0cdb9b0f20a010027fff20fe66901ff6e9b4555edf840971b97f288a20783ab284bc4311cab16d04f80d6fecb01b1f9a52576b01773f8b361a43a246ea49ef88358741f55f35f903a553efc21ed7f3d1c11ebe0150e1bdac7bd969b4b876cce7cbb6349dc3d98b0adfd8f2111309df72bd2740631b662161b4143905f7306eaf311203e9d73bd907730df3caa1d27dd188d258803883c5bc3052a3049cbe77dc07ab14146a9dc50b16f608a9aa9f7af154e1b115bb130523c31312c3fc34d961a6f082be14942c067f050b38472c8cda7d88573ff2cc31a6937e3131e83f710cf8c82cf62e5fbf068ea816757f8622a5ccffd6c1f065485cca1316bf16ee20f220a73510e793980b274d1013db19ed48148b5e058d793b171227cb9a0727744f59797ddc78d16d0fc531f438e7c047c4c6432fbbcf0557329e02aeab35b3505da06eabb4e6175303aecbc0140a7e8262754d30e60ab3a615f7ec31b389228edd8a3f4da0c4553802b70a2c979890bdbc983fe1e3b85724043428ee2aeaada30f535b2f0fcecc5109184121ba12746b583d7ef39c012fdab9891a75c280bc29e8f2fc2d753d445a0cef21aa625535c1c17e3e5cce85e550695ec94b888f42924366478d072d7715004ba8848fa3be6ea17535df1ea15f5c76288eea8039603d15a6ccd1d9f7f8e70ddd8a0969c72198ba488ef84c968e5125a5f4a57588263a7773c37562c0ba730ab1c8704bf958774624578fd63e1dea3aed009c42adf90a313abd48667bb60c031e84788444cf2eafabb6474b793cb6c0c26b67ab3663747bf8efb3871626df0568d02804652e09986f8a86d05251077dc36a9af0b9b1a7cf9584ba7bdb456e0f10e8e33e3965ec4a5c0cc628bfc8bdf48b8c349ab24b92f1c8cb5082168f0c0f012a9b10f9d77b783b9a14c9947469e9507f90ca225b8dda54d48a31ee565c0550e2aa1edca273d2192d996ba410d497ca41e5e2c7b0d3eb98d5cd0eb506510f6e648206228163f27b10238d6b2477d7409d94375b10adbc007e0e3a1a648a0cdd680df2ca71f9d796dbbfaa254002f9f91d2e4debcba7a966bacf5fc1f6ce0c1e1ba9350208a349ad817601d9d8a1da0fc4315b4815b7430505c667c85e6e0680f28c931ef0b4509033e6e167932fc78d96c80ed68d41d52165509d69a3ea0c7413671adfdb49f792f24613e078a8c4d6e335125d2d172be6b0d37053b85005b6304f8485a6b2f7304c1d530a4c3078cdec734a9b9a652a4e485deca7451a07f42d19af7e66621f1ee5406cb9361e222884ed7969f66ee5d3202b116ea06002b40b875a18472b4ee7d3c5549113dbc820ae4f1186d86a79eeca525f6496c10638942696b48924b44758dd492117c47386162cdbd3018058758e7d7a66e00503df075df334752706a5ed3b834cd16dc029a2661323f308567acad8c20c7416010fa7feb09f83c6912a10eddd07aef650860716a3b32c30546fcb302e16ba7a0e780b8dd00e6d21a0244e3b9fa742de3a9336b25e68f30ce13304f73691d33f0d0e6ac99b7a0ff2b1ca8a4680e4fd1db8fa2df6e9d30cd0fd381e289745aa46054439072b8a1d253996df42f1ffed5fc99fd1556791596612255052f680f9f20eee51aa68139ca5e1649935efb61845b13dccfcbf0cec10a449ae5f6436296507515672a5f6e826120b7f70b61a99c556188e9c74a3c54af5c4ea9684122fc70cd850b8a1b0d0af4ba0013be3e53319c03999b046bdd8d1d9589731a1938e1f095f03eba7a9dfd3ee38384ae0b6600b890e6f0d4e40cd65a6ae2900186e72ee0e5979381e73f4cf63ac707f70fc5772c911a8161cac5ed69ad91e3cbdcc3af807d3c3f2aef3b8578a6ceec68e5f825f2cfae9795784acebf385a9f7d49912e1064637e0ced0160ecb908395349ec7f78b748b46077326b7fd880105b0de33880baabc5e3d1851ea3f493d96bf02b333b8e8d13d93dfe6867420543daf1e40db0b69719da317632787ebaef674f14a9ba649e4c8b63940cfac172698b514340f08f0aeccaeedd44a0bf83a349c0bccc99e9fbedd82a8d707a135e19ab5dfcff10dbd0388948ed2c0990a457ef609f84f2dbc939dbce6c68c3e7503c6e4c5bdfb01d8d562be2e590ad1d5a6d30226b2270efcf019ec28271a012226a827640c4f026e724475947e870c5c5d3c542973099c131283ed8ebc86c5c5b0a8cf16aa820523a6da453d6020d8681093f4962f15376146ee553afd0d78cd011f1667dd390f9f4f1f8503715d965f9317007d2d7f55e15e7565eb88afcca6b9d8eab94d3c0e03fcb4d2eff06e7a6e4dafe26ebd73f01361858f24a0b212f7365899f14c290a2de8194b71bedbd210c25c401751bcb0c51c0aec402634144b5208b7c7af460e232374d4f69e670e7b61429e51bed2ec6c2259eea5be9914b777abcb6299ca0daf357e7f99674fecec4dd5b8501d62ae1464a952cbb244f9328986344206190b0dd5ecbe9354690c4f3694a3c9656e65ebecde0da64aa6797f4d1c11700eb500d2487cc5a826a39376fdd6841a20849f602fdf074db447c15674ba9257691409728996914268bb0c1bdcf38d772fd4374f3ef7405363308cad35b05e895b850589721e8a48df2a5c85c5bbfa7c005e6155060701e9261e40a72b1704e4d3cc042bb48ff233d97252fa7f823f0aa744c04e53e133269afbf4a76e1cab8f8f070f53ed8292ea6eda2087a9b50e3185c2af2d50f9173ebf4a78070c0b1f591b180121aad2222bb57b0b619ad84f04c64db2e0037e9d05ac49ea55ee99c1213de407207aba3d4c2ae482f3a5388d924fdb206a812e2d03e45167e6f618a4759d750dd1f547ae15c9d4afde4dba03de57fc17055975ae43595479c678ce4fe8e0f40eb6a01b48c7adeb4a628f6a42c9d81ec8928c6c1924902e603c3d8249ad39a2020fec1ee7cb426a0c3bd0c172fac34e31566558baa99a8880841bcb9c14b9830b7a96a38ccea0d68735feeabb4f8274e8a110869f64a7c81011fe0648a1e1dd0cfd5504765c154d21cb6dc0aec14bfe1d75a40c0356320ce16e85c25fe4d9bd001a099b5dae1f8b379960fe0f37f73d5e3826c0f5b07d440ca4d26fb98afb0c08dffd4ccc00a5b86b22e3947d9e81d0bad1a0e80a9b565d4806964509c34e60001c6dce01c2e894e5ce369d69e25314d1ea3bef9782daa19b72843e0d0ee02f0fc2505106813ac2bc90b5ebec1ae160ecf79003b548288c447f977fddd427f60a27f7d5ddcbf17864441ede47528522d088d2492fdeb7efa56baa05391086ba0e86902035043648e4388da2253cd6e55beea98732c4b74e99d6c7fffb7d252b0791a33e93b134df1cb82b870c32cdfc13bfd2e8b082dc4e3e6394aabc9417cd06be1a4e58771a5591560f6c7424df45d9fe21f377cc6768cfc47bf42073d65b006ca37b3f8d2f4f68f26f777b39d42353fb5bbfb4eecc1488d279406c55e1ff07e5601894f17b4ac6ec2ffbad0c0efa49146c72e2ede020a9cf2af239fc95d20938c40ab186a718b54435268b97bf4656edae32a952f224e27fc7817e4ebf380d293dab562a80a1c2aa97cd6011bdca11255b3b89937204722aa52118778ec60e4b14aba24ea900c8a184afea405f4cbcf6e9bc52f9776542008edfd882e18e01973345136f0eef08844b5654d5ce9226533cdc7020ac953e80c97e305a954805e72413fcfe4390c936813b6d48fc06b9e1df763662ee495534fb66087cf32d00da7d27d22861cc0de685aef8630a0805dd911d6c53b980430d2fb62f7a87bf065e77ac6e0a7c92611d8a3ee35126c3823e837d931d6d619fb53016c6d28d0808bfa6221a196df973c946a68bb5c90511167c0f05356714a2286960786e21f108548f82c9da1daa1fc74fd2ebde2edbe5b31366ba9169dffa7d5292274afde10d71425d76b419283e81540464fc48a1fa8dec5f540504f3a40fc061713a9bcf0e6450c11c126536f43d90e28c02723901abe055141124af077f7d138c16376a003e33b8643460cd0f3c2ea6127591d101c3470778d31ed8f02dbb16ff1a77730d0ae785e721be6a8d17b7753b929ebb8254d3f7294eb1fd9228039c8fa4b5bc00b9edc0fe48cb1a821096be1b41c1b6b1cfe7cb931926f24595e4b0ae142c63069ebc09ad7a52a693c53bc2a3aba6b234bef771e4838be7c142107926be30830ca5b27f92444716ced45e53b49c5fe20cfd16c38e3c1a00a294ddcec9f9d4a409db8946e3c59cea03b793bcff8617583bd3675b987d5af0a84b29afc2eb81c408cfd1643d174b68da8c143adcc0fb233ef5557ebdb023c76b00f43cfeb05ba804a5b49f5fc42582fcb4bea92964346d0603f52e90edc12f3bf62ea6cdac7c4304d99c9d7796e8dfaab261d4a906986241574b8a170edd918f43421728043cd40f52e55d84d37269c0c5051ba0dc2f597552c19e903c8970079fb5ab599d91c10415a33e2e1cd7b972e643e407532c0423ddcfde285467a162dd491d32646e700a4f70d63b9df4503f73b86dc0bc843cc4ad3465bee7a70e4da134de70391f2704fbf731f22d079e79e1a77012f0bb5ca5d27b570915dc29f2552a05f2731b160e8750e28c2ac9a9e563cc4e26239ea6fd81ffc0d535178553db3b6d139b94dd0e55b1b9775d98d0b2df9b3e4ebedcfbb1efe6374c16295d90d1bc58b3b7af35013a8a829200acb58a7f7a4567130b2de136e5b63858b92d8431fb790fe6b0890e5f65e54a13d441dc97d0a3debed6691fa279ccab7efafad54778236d3d76e3082e9c8f3e1694df25c1308d1054490e964eb3f9ab13210c12ebbb7bad9af72109d90ed41a4b539374ecd6642d8670d984db59ab1b52a70b8e01c1bf67dcf62e052f5169aabac211e4594aa4155a0d4b1e2b60f270c24b833204fb3129e85876049d661b21aa78482b01939bae7f3a7ca375ad2ff5b96df394c6163a245b49d30f2c1fed9d00a23993040393cb234e0bfb84b1b092d2474b7491243d8918606903e38ec052128daf70f779cefb783fd354fdea2df24b544fa57418b13df209110aa9249abb9c9f0e7b7b87de4e982e52a581e8fa8090ae462fa44794dad17b2904e82fa616403ed507b85cc664626bc0963fefa8872e4e2f0e8cedc23c8c977d0eec6fbdde3cd4cdd28edaa2e0e7ddce0d2f8dfd769be8072a0fa595541a30960a4d3486a3395109e12e78a195128fe92043efe7a77c148780f8689adf5eafab01610d7293fc14a24962b215aa4cbf787d12d08927a725057668f3daf4de3cbd0f56c3703ad080fa9d2e858b2f53f23e436c72cdf62848de3fc9a44004fe4ea007e2550003518e9f1c5e7a8628892d1c376ae79d38935d50d6003654e79c6b660b745c84ed1cc5e8e989a69661df0cbf828f6b9a92fad7807eaae6ea779f73d306f6faee3ec568ba6befc504d1535c9456790ecb87e7fe51900c78479c5e509708612da1a8723f410e407536f7bc94e4d759bf79458894c9a1e9f8dab58724970d964bcd97661724f0ad0ffa015f89a4ac1c02eee07c332b431f0a1bc99a275c0f9196f7e0524cd426c4f145d3c8512827aa6d8e892cb6e2b9e0f28374235b030c296613c2ee4397e1dc5cce11e51dbddaa0f5fff7b19aed3e7d7f875f193ed90d6bc2d4b9d7f94b99087481d70ec8fa43893983ac8b3221ae5887661c1c4345047cf85dc8ba704cf7b3785164a302fa5295cf3c7035d51d3727bcc1ad7c7c720109c7b7d3711048192bc444f38aca4dce47031173d5b0e30cf614ce9f25c9ba02fa4559c5497c6ef688de67c7d6a2e747226cb7e271e6a8bb054f8abf08e8aa0618990c824213cf6d06f8380daa99c84afb0f14bf4111484ded1509cf4fefe30bcf157c880d1ddb473a910fe1a72f0de25bca3a49529cbef511abc5a36e44f6091f3a981c34da59491504914be16ed751423aa8006936b4b1f1c696e1e569b80784fa342ec53d4a40bb64035d85585bc833f5516838d03ad0b833c9f942774f03cf1b048e9557fb94a3031e6391d7e63f837c0e416d2be31084fce2f18f14c50ab99d73c3d2c9cdca1eefb1cb25baba83af835a14745f6285f0f4f62b7666720569d237321f22b97d0723996568688ed3e715ea96ef678e3809ba199ef41e780dd270259d1c2bcdcca13d9a6d9cba9cdb5ca152af077ddfe5b635a6b4af64060bb0dd32279566a3261a41f2561b481a27816a29fa0b80ffe76e70b67106134a09c3f5df7a5ca56edf6c7172058b78390a8ca3696e42f804c7f43917ccd4fee400abcf69e1d91000c1318874fc19ef0f5dbaade4ce70d33f45ed966df76a0432095a3db4b3ee16129b2e717cc610fa4fcc4d20124d3ae6d0fb82c071d25860c3095ca3154221eaf94644ba8c35d6cb713ddcf5596647ee60e25929c13b0f7688007b45374a628ef7642c1d29c66b95ed5e2d3b20ab1e0a1974068537624171ee0b1339bf081e8eeeb97334ec82aa204106911829d7f301462251dd7ff5def8a80a266c5d95aa97f44eae376b45a366b23638af71c158e3fd87d5b63f823ecb330405721c56f1e70c7eea564cbe17466a11a035ab363e060fa3e12009c9c5af340c54ee8b05616112f2bdb0bc4dbc6ff6a41304826d832893f3c599b4931beaeb00d8aed139c7075fe7d9d148a7daae971640dbcf1c203103f79e4f052991b1410f18163f539ed04a412ea8a7b140b48ce57768dff550aa4172164216cc1e56a304cef5c8d1b1f743b3a905077a869acdccfe65ae92aba40b8de92d7320a9e75b059fa51781767d33e544a8dc6ab991d19405862794506d8817c84df30a25cd6305f70fc286556e43089948683b0d325c140f835fac41049adcb5165fe51af50b040f688cf6fb53861b4a2bbcf62a8f91aebee35e68564de8df9731ca5ebf1f040a41b98d6e931ab9c5f2a009b21eb26f6f6854906b3a0ddfb6ff77ab734715860bcda7af1cf98ff7d05a9c5b4e6e7bca70c271f188b4c4aae258cc36c94b5d3506b5e34c2479e609e0c88eee607afe9a4af78bb28a775b9395d26e42c3b1479104a07431530d8807e9753e3311f2f19ee0548f23e590e4d5c18ef2e7ee1386b70fb085ff748fef7af1e989cb4df662c84972a3e253684c8d2ce28272f34ee9770a2e85141f417ba1577329dfa6cf4c957b451b3f0a05e7d92e875809852968e60296de46dc58d27a6c8afc796a911574736b1e86373123aa20505ddc05dd853304424c48b6c0625a4318420990c10c0b1667e2e737170cffe371f9119c37c3930309a916c846715823f96b94931ffa6090893db644d7379029545c40405174390e949503e418816a44bceabf0fb547a4754f7032fcb92dc12ce08bf019026467018e0300eb6d1f7f2f9f679af85fc69f80379914a4848e0d64e49980c16bb31e080b3414e7bd7673e61ccc49b66899390058ca7208f88b2e733b8c16b5872b5909d06f80bcbd147e281c466be7ec09ad35a4d4fe2767d7fdaa46fcd946497393060494ba4aa2a0d258fdd533c740106c145ad550ae1825c4d1599ee8ced52d130fb4c0338a76f8723889486f8d5d9820833687bb8f951d5f8a741ec66155663c0a1b35635403b75d1ff1781367738db561382f58c94a86d7ca642570b6df18db034c5df49df83691e4d85bee9e5cb0fd2ce44f592a8626ebb86c5dab1af83c30066b39b00ddd962f8c6fd0a9e8bb88463b9798966c521466b0ab522671a98f06093845d86f60bddf7e536368b358317ea3b93a09c3691978afbd10614dcb75d80271ed20a6aece99f513bb09f5d18101931e5724a57cbacc4e00408adba369050241600d5c7242ab8312a7b92106576269d899c2965b028731aca46306902c7d02b6322da2bc17a688edcc8cdf52190fd91f496baa3f657662eade43708276d3066c39d2575892a4dff19d3766e951b30088dcf95372e4c5d020d874a393799a0695209cddf4a1d8e735bd27a73bbd9847f9eaa7783c308e853f1f9af64e1cc40897645ba34846e741003683862c155025cccc240ac7f44435ae303a3adfee0407676e28b2d38c3973a1f7c813df3468c86357263d01b6b624ced2bfee9ff1c9062eac790d096e6010c38998130ec027a5f82b16ed029f7d3c6846fffcfd567c0c0ab7df168ec29138945156b6542b7dfea02223463ce2e263c52b6f3159e9ba0d94ec143d4005e2ccbf61c6a9a37969389a8f399fb32df6296243e2d25bda4303ce2183f2fd1b81ff9fead8dbc5f0b2300c23c620c61de7104b887f48c47c390b94a6bca14b89ddedb9453543f316d1106e62b0c6e0311163dea88bfda730ff0c9d4042f24745380bb53138cd1251b6fd56be85459623eaf91e1dc30c82b40e00c64ac9160868e85cecba238c3c0678a5838bd2973bfd6fbf58c58de4bb44ed0326496bc20f885f14b30afb17d1a5c9f4b7af1ec8d2519e81519cd47757b80e0769d76096946ec51d861de1a8001d265680fd21daf0df6c5645918d512e66cc09669eeaf68572c03dd7a0e6c56330b80064b2a5e541010278e1f892fee2e7a107d6441c6e204e7ae61f6d4418bbb3cb5e521162472f690912a83a1197c66f8903682c4f0c729aec5c882d502423eb12a09d80926c88ac4f8e94f6132f66829e0fcef0201c74a99bf43f69c2a70a512962debde30ed1290bbe3dc247e96882360cc337fdb5968daeee4f20ab7d5ecaaac36dcc96072eb15da5ea4ee905acfb0a02c0b346178d0ef552fc1b2cdbc0300ce54e288e92a74325083c48f83d51b4600bf8bfe3fbe0dd1fd7c3eb34b4cdbcd3b4f1e481411520acf842d85119e55f3107c4adf8f494a6a970725323f1ff86f11f5e1763f72b5e7c37414cdf1347a4d60100d2cdda558f3c4f15763f546dce366dc27d0807339c487a540914295969830ec6884e4814ee8acb6902d9d760f4e2419e0994445b7045ccce211b6d7385870aa9e21216c67adbd12b2d049b36df5df95533038b4c5c79ede851635e437d210d38c0fa6a8de44693fe5bb13935da9ec774ded781aa7d5d1ff77f110e34daf00ae9f118874128dc6fbcb4e8f0620f38e1357ad320f82ac1673d0a91ae50e91a0255a6e380f0c6f8acff18d1559065900780fbc80f0460105c53009bc363369903c24f446b85e12daca377ecc4a4da1d40eea3fd7e7d2a0e2c24b49ba17b431501d36ea4ea91ba3dffd4c48e0abc9aaf21c074316d80d00d3e20a1b64463fe5c0d9cb92cb4ee705602b26550e1b4aa8559b6d910a6e0af0b19b429b084339b4808d479833c47811eb96d0b41d497d834516777f851924a37e599c8acdb70587601d7608100ed4c8f998bb9ee06c28d898c81ef27b157ed24775b931d492dd7860a527be70c643261e4deb2979abb49aab505373c40a53773d9077279a0c9c486012f2c1af0851dc091a7ca2a720bf2c9304dd2817a7398322d00e21cfea7a35a0bba5641dc459b2d4c2f910c678ea3cf58b82d9dc6bc98f11eeb25605f927af00b3093e7a76cff52b6cda0c888c98daa02615d75176be645157dbd017c0d2f33001b51c82b027c80e3646483348b958a7f00ffd9d3b328339ef66551ba5b05a102d86403941a6afc4e28a772c2b72faf51a53cc00cfdbb1c8e2436dab5fb186109601828495dab79df63dfea298b5bfc13ba233772d1e8f6e48ba31f0bca4b360617a9fd1e9e9f172a7bfe75f4e11e626474436b2b4989853c92a58b726e5d370f2cc8ef0bbc34258a4e9a6155882efb4ea0144c9dbd3be6a9179f0b819097e806c5146adc73f588038c4914a996c56b952a4202f0fe978d171c929d38bc232d08f5e3d077402a247b01aa329677db6d9d124f8553d1aee4de01b7e935cb58340bc123d096996601700c722761a4e5f1309fe53386222f01f290aa2e18a446a8079725ec8668ea3bbcdfc012a9e8375ebf6d404a0d3d018ec350541d9039cf820b1fbe03a6c8854e47f9a966abfd6396c34f47f8f0f7b0f7c362ffb913c7b8a60954be5caabec8a9213ce789f3f3c670bbf5a86c08cdf96783eb17b29f8a215f0b307dc909b78a9eb9f896bbc58b762cb2f2c0c96e555162307fd30847c1416400d6898c1d6b8f39ef9399ac0ece90bbba0dcf5793458b482162108fdb2bd81007ad2cf58dda4baa996eead6697a63ad615b86031501d4e252ac9f2c283c2ca509900d3d42ce6cf0b15679b990b7bc9d39418506b698dbc472eee7ee589d0a8f075dd7283af85d3a037815b9941eda4ef1f4d972dfe1574d99b1ffb58ac016250bf8b256d78e2ce0bd44db9f2e03868a70de9e4a460ce4de767b58fb59c1659f03879e52b51b690fe9c61907cf4276debcacc647c250171041e6fd118378a45c05d6500f77120eaeb88beca2330d1549042c223421e1a70aa095b24d6e824458018b0a1e679858ec679d8fefe0f9a5f189ac9287647a489d9c5db6bfec2bc63909862379f31ce5488cdda984a005d6feabe43c2ce2c94db311e940feca6956fc0c08d5fe2b347cb9a4c6c215c7cba71aa21ff80dfa84a7df5efc26cd0303c8c6021c7d99676f9332a9b9dd13aa0f85e672359e2a4a2e90e4b8541be4f73e38b40dcc86b0815370db55c4b62dad9281da95ce73e8a64334ed3564c89eafd30bbf05ca2e949925af930490bc544fcdb8e7cc3579a3d086ee028b88b68af21ce0b8078324f8a0a61d02e254fe9ddd2cd015b76fc136f87acecaf83e68915695e1bf005bb94b9272348872a06070299c9c6b3f96fe936b9699d1a2e19f6334914c2a06f78df4f37cd095ce504928e1378bb28f6e1fb2d22185c3c417e8f65c0ec2500d5d96d4fcfa40a370d437dcdd989f4255f6b299278394c1afa9ea0092cb70670f68216fab3db1c89f2540f5b6bdade9faba50b9f20096ea364c3e46fe6c7ee10ed9986b40fb41225a0f4477f8d565b358d526c9216f46b86cea597a8f76bbc10c5b6c083ec1b6673a5375f03964efa24c7dbfbc8116433a8f41158e1b4f013d0716cfc3d755d1d7376ebe9243a707ba9b289397e1db1d4fd21271f0a36baf4e03f2147de689f0b790da65e78b7054481342759f9bec955f3efb219fb9a3d8cc007f610e773202aa2c294fb6a688393a7632ef50c928b246e11c072ac4e8c28f01606bd1f6abfec16a600feb70723afbec74b3d5048c8d07e1ff40e94df4047f09c9625f43956dc4e15b0c8a8cccc2d8d0fe11c74e69b79e42724a66a8b606b80cd0bc2a59659922a3460d30e9678b69a8636a212cb1f32954a3cdee5a44b473036ff044f8f231b6447da066d73715d385a3b641dda13bc29fba9236b142573c03e5b4a10a3790f9ff7b09cf9473adb3e9630ae74293efec6e05797229f19e4607407a181e003a1e8030485ea391accf01c068d7b0c220de3954c58b8d7e77bf090ec60a9de9bfffb773f5337fa07340b6d92d2bec8c33301a3ac56ae99015ae0dba1a587d1466a257314e810877b43c33864215327bff24d87a3bfc02e1b3dc02d04d86e525c7f6cf1283e873671aba2a10d1657fbb2f3f8770c70d3746e3f2037e528574b6c9d3ba72726a30f95a3d8a5f02a0184c3e43d66f6549bee9057c030850364b7cc088760eb1dde60e9e9100ad5d298af53a40eb7de73235eeaac703bba05944c72e22baedf27be50b415463d80dd20afdb9dd03afa8112bbf87140a56134dc8ef97ffdd0a31cd89310aeded32040d3d6f1631e2851388c2ac641103327d4d8d8c882115c7113ed95322259b7b48ead875d1c2703e08b5f4ec11b00a1d8f16eb7cc5e22635ba03cf42e7b2fd57fb48b6bb69e40a46d5e55bf8bd6d051da1f0fc73be9de7bbcfac92454f7ee1dcfe77acae117fabf92316379f6e2707f609e23c56332befe5c92aab6e08453976bb98bcae329b7353b13140fc15c2091786b05af464c7fd704086509cdf624750ef2686e62257c359dc3300d533fe0d8e6e39475b9161c20d4f0dae796a7b1640dad421052ec44d770ccdf7db8b950784db09355011b2ddc989f443ab7717865f80f26f55468d8d9067084fc1ce8104c69ffa5e28c390959b54269ff5a4e48f11a63f74d226fe9da98ef03ae4b58c0df8e9522beb08e004d4a31bed130c9ba7566991364b9777e856ca1309baae9404488ca533cc7ba6204695578f3edc25692bad4d163b2c5bdce8733a7eb71e6d01c939a8b7634cc2e3a3a3d903d73a0f15804d9e23939767d57da912b83f3e2003 true -check_ring_signature 044907f63d2b493b608e2c1d87b80c95f25c47ace1ac4a1a95ca9339d930051f 4bd5496222261e73c345f4e8a3f734703b8930ee82ef837ca60ed101c1175aa8 8 7eee0265f020e20ce2a6df8b3e7ca5292eaed46319d9136ce619aaecceba3606 2425f48a50f2221c5b2fa8e6a0c94a90fa2135e382b0ca0c689da12719217089 5a4716010d44396e754b0f48468871abe8a3b0411a962b901010803af728ac8d b5569efa5900b9472c298e6c2e28da94134635df7aca1cf4e9449856bf836e75 fc67fb7fa15cc88ce34e74e2ec6f81597053ed5faeecac6a8da89af3eadd1547 d932ce703020c5c28068d12768862caeed7a8786ab6641f8997b5f910ad77d69 f05c1ba4427cd321090aa79375be08c0d76d79563556952716d5d094ace0d533 a1536426b60d135a2db353ae27431d09404f990c5d02f5d19e46afad00b98c95 58c81e73c8a573e890bf4c34bcb801c68ac33b5053cad3e9033a42151f274453f3ce83c813c3900a721d3c0a27c531f4f4b79550067c5034714743eadac726058e41c29f15be50163234b95226db5740eaea3924f4ee0558656a85b6d75aca089971e47bf07bca1fa7f04b2f80f781ca2b139ff258521405809758fa32abbb01b69e0cc1b1f72480e2efca8fb26f410ea801777d3edf937d70e964ff31e85d0f7aa632274966553ca34a83001d9374aac42ba3ff2851d5b21a641e82b72f630f06a9d00d73b9ea7751f6bfc04d044f10c94219108dc40cf45088bbbe5b44bd077a77ec0b3b844b796c4ceda309f51e2d08bb213f005a0503eb1541e85b4bf80712c01315cd2b92d08ba54598f4d3c3245503009e7fadfede964d3032ef3a43140c10e1e95c40f03e2312cb06eb5ed478debe98c8cb3fbd3f8455a1ae7455a90bd3720eb84349ae7e0cf1a12d5ab8d3c52fb0bf7d9390bf8d3808df92f3d87c01007b6f0dc23f720d0d6f4ec7299656cb6178f2f9f3a1d2055a920ed99b86d904170114e7713359df26885f489264b1d9869d9a8c6b35345a8975818b32fc020154f1a34eafa62ea971cbe140a4f8c17f52f6936b54d62efb5448fbf309e07e006d26da9e21c68bafb2e7cc6614feeb747963c18134472718fc44c97ab9390d07587d95891c369253011bf4af15951507447b812636c6bbfe9c4f7c804ac79304 false -check_ring_signature 2905367904f07955ae818844435b271b35cc4882468878470bc28baf02c2d808 5471f3f43cf1ad5fd8a47bb7fe186865f66d9ade44d0345bc4548b0aed09a26c 125 24bff13a8721e479e0c4bf79adaee5672b05a4f153f1d4ec5264b80c334ad736 55a6e07963d6af73273dbd782944d07d0c4bba169dadf7a31baeee381aeeb26e 75b29b1eef748052159ea90d3ab34f26d2326217320bdd452ed5b5b50f339082 bc86507e37abdc088f24a8ff8c5e3673798973829a6e98565e895c5d5883a0ea 12661d44ef3f4cad053f3f329b81a9f21b8adc4abb01165977ad1b3cfd6bab26 bc7d4c3fd7138881b61fd298e1e9a160e3a0cc582e0874647c45fdf9bac8c458 0241ffc34ec7a643e20c0073a2e502992b06d228a5911e210232a95c7965db36 1f14c062310b4be1657fec6891c1585c79d69f9e4cc79e8378d776ad67f56092 77aed6dd3e0e5b9bc4849715e4266f132294df75ff195f7ca997f58845ff77b9 c5299ccd91cbff1bed15d98f8ac64d674ab4a386a35c6512a964d1b1f9a0ed60 c996dcde2bd4be1426f46d683932a471ab701970b3714e5d8e7976d494bf9f92 4801717d85df4683dfa1bb957d7344ac1f40802ccd1c0bb80256178f1285852e 9f53619be0773cad992f9f7e56d9c59ca9d6a03ee6b85fae10cf1718b2b771aa 406ce327cfa6d038dd98603ae2d96b938699110a37e51118b92aa59cc180540b 689140e9d062ca64783eaffd73b3008226746c69ef59be2e36297f1afa9b12b5 a841668ab922fa2d87900b94a45e932a89e76e282e21ba69536458bfdc05b4c4 79c3dfc6962e56fc6ae32baad4366d3acb1d3a28e4f622489e8cd123e0399a5f 388297d9ad36b169398737f894d65449001288cdc4d69736eca8b1c3ca45e55c fb966b8bd6f7c6c1a6265aefaeb215b0d9c97c529f32d0c3ffa6966ee79cc5be 073547488bd044c5e3b7639bb75ff89ebe2e3331bae1b8e6769d6d32188b257d 2b68f1cdce7b126eedf4a0d07b368365adbe35c663a7a5c646447819d5e5cf13 c32d0786a6d79c950350bd20753f1efc4651c78f508294d254c0636ab2e56806 f09fe77a551bcca1dafb3ab2f870163fc2ce5bbeaf75a3fbc39de9eb14b11f83 f9d0df85f66890e1d433d1d81155173a3cdab4b91baac13d9877230e8bbedee7 1f750cbe611878ee4f38883c6389377b93ca0572c3993d77541e0156df7b2980 6f798c4a4b7a47c3ff250ea3f3f64f7c254029c4d7f625f36802109766764e9f acc4af369fda3efc8c5978589ab59da3c4cedea1f073d54b57d87e8438853e24 c0b2fa23f38e2bba92f33fd216037fe5789ffa231450ad45bcd49e405ad9c443 77303205d2863a148df7b2fb64d00242a913aa9c98d1fce51421c7e78ffb0fba 85702ce5d695b03c25031a192c9ec9b4088be3be2d29bf57d710d732519ae118 7a103a765c72d1d606cdc7de9c182b93c429e9cd0687bfe40fb147b616610e24 5371e7f647d307ea1383315fe79387baa48156577977ef9947aaa63c7adaf094 a3cec0b51f79a4bbdb2d470c204bfaf294d9b340314cb640d0f828acf2a1208f b471b5aaa529a235f2cbf34efffc692adc1069a2d5d1264fe59ef83c3f4b67de 01d073753f5514e19948427dd53e42318062f8a8e0b5b2cea5bd877550ee0c76 a0b04015112e558c98270e6ce9986a96182f991d798988da08a680a482fd47d6 e9f5b2d2d270115c03d5b43317f7b12383b53eb8f24990080a862ad849404fcc f7313690ae6d1125d2a4fea52782b8f85d8c64f95f8466a96c02b5f8d5681372 ae032415ffd86571296c0bb28faba1192d046498877cc8959036c7fea985630b 68c81ebfadf0fb13d49fd736ada9bb90fa38e57b2649af9fbbdf7e7b044a28e0 edcfffb0e82875205de3cf038764e846645a5cdd89d1f6d312208cc24f277311 83b6adc5e92aa5768c4e855193a333be259be457201dcc2cb98f761b5aed5093 0524a07895342e91f3c8481c0ac037e2feef4d141a2721d7bb1d72aa920bcd86 d919391ad399f66718673f875ce052dc2ef4596e323404706c023aa237d7fcc3 a9f9f70e327006e10ee5e0991e0b1e39d3dca3e7207fb9445b0867fd057e2093 5ab181a35cb4d6286bb402366913a1c885e9fb6cb178e8837f935152c1641c22 0a38ef48df0de0f892b312b5e273a7649c1b43d1ab056c7e3cd18bef632c266d 1ae23681b9c600f6a06564fe63cfcfda034c4b1d8786c81241ab50d243693c18 e4f1f9742271bf73621cf6a3585adb435bf5acbdd40d33224028319d2fb72d27 bed937714b17d9e5fd14de9fcedad98533c6144c833fb341e733c1e983bf40aa 55ed557d8744740a41166f141f0adc28580ad06d2b5d28ef24fdd48ac00c9415 483bec922fb31cf297540112deb1462b7d6cbecdb7b4015aac630a67c8f29328 78577804bd6d7d8dbd10a835be03c7435fc1403351e7ed2a13ce1a52f50a2650 aff4dd460a3bd67e464ad9af2b2b04a9d6c0795e5434deec47c72f550ec4f14d 70a0025545601a21df9ad1409deec2e4fc73dc657185076e986a33546440754d f60f20ae0122d171f88b958519c5c4edd3f5fe61dcc16d9a1cd386d7632c630c 93211431b8e849b0e874dfc367b24673b389bd3018fc3e6d87f95185147044ad 9449dfca120ce9e6d1afc956e71a1313ceb3738a3177e2785d13e6f9b6f5e324 765442f06e6895fc8cca0ecec4fd659a255de151bbc60e2aa47d4893fc4bb2a7 780cd06305c07d9c43f11b82f33bf13aff63e4b999ea74f23ab2a84dd8be7d7f ecb7f3929e6d3f85207c8c42f3f40d587ac0d88b61636bc570403da967ac13c9 30cb165e4007a7a919a732bf3b2b1df924961f921d7ad4de94ea25a77f9174e6 ed097b0ae4646f1c95749008870486ae82efdd34236f21af51cc8c078dfc2aef d96232bb0078969f29e3f62978937821c8052f992de7492be36d7bee4234d651 bf1ef2f34fef9028244335a51acd3316ff8064fcc3afaffedfe3d3bd421e6cde 512c977217c244af97ac88702ad3e67a136cc933cd7406ed88d911e223ad0231 cb088a4f84cf5e80fe19f0222db83a8ad4c96ce586f39e4ba0901c577facce15 034934f44ce6b0b6e249a333941d2bafa0838110ea9a67611f9112d813bedd8d 9351650ea079c181263ce37f5e3a0b45beccc70668eaa02aab166c57ed8aa476 09ce3aacc2976e2137813b607904c59ea86e9e048a6f1ffa6231b0cf1d5168ec d1c4c9accd3d82c52963118f84652a8b05d18731e26ed226c9b756a4a05dcaf1 f6a31329446a18b1c7371aaf1f3092ffafe93614392133c4635477985324e622 c85f493e0f38bfdf2e83a4dd70b2f027afef291effbb42e9f0e4a0a48c628e60 7dff4c04c45fb746fdeee2b6a4833ba28c4ee29e8f0f393d2e0a5408e869b7ff 460a271653be0d335ae2610c113e7e5d41ded303795b2ce962b5e87ac28b6307 6c5abee93cd0d034b7da83009d6ba5d7cc4606b21cc7f4fffd61460b8656d633 ac2832bf4d75acdf885c47270c2c7a279500fce8a4c94d38c2daa4dbb19508b0 654b03c1685eb29f8bbffdc3c7485820c409fe2a7533c9c2b6fc071484deb025 873983ea91bc46c0431ea8324fbdccc05fa53dc5aa7914335324dbfda1df8162 a001db70a42002cb96db9aa4a9cfa1e2dc78c7e4cb55115a34d13fafd489a66e 8a7f2baaef4b1612cdbb2f163c9d87e81dc865c0da4666f762dea6a58371e7a9 77752b91709f33f8bbc727799c3aa59ce7d26662ff65daa2c56bd2cec5889b56 a58a27493de129110d62ca707b17d6a9c35dbf1abb7f033dc2288c443ff1c8df f6fa73b4cbd06eddf5fb9b54609f4cd83247f231b64674fa98c8f78ce5edfed5 dbc2e960ea1504cb6270c1a329c831bebce0bd3a21c0c0c5d96324553eda1334 d616d3e0dc5cde60f0acd053ed42ea309c9e7512ebf33fc6babff2f87d05b5ec 07e51d281ebe37ee71d9e3c10ddb6b3d1983947fbf3819e8aa067f951960506a 6033ad06cfe4f5400c0cc5b734ba8aa26b26032ded0e95f75e5470dc0e9807d9 293bc2581ab23eca1b4354f0623d66dbe74548dbdcef608d3ad459da5c13e6ab 238d4f52437cc8c98ed13b2f8e9261a13d31c80c26457843df2b0db09d2b23fe 437746184ee78c24733c0ad66f5d13c6a28104e27d6c7d8f51fbcb76e5bdd0f3 5aa8eb11ef4426f767e4f20756b1f76a6ba36bc9d421de97230fe36305601080 ad890d34e53b56914bae085412afdc4cc1bec0db562be5f5f3aab4c7912a3301 9b823cd77795e88e9688a27288a4e048e1d44981cb8297d0c08a3655595f6f3b 9ed938f5872056245186354f656125efb21946dcf31756cbb5bc7c0c94153c9f 21d4411933ef8c914645e8f8fb30f6cd86ec0045b264cd862bd6fc912aa05eca 953f689ccd889992176ad0dcf1e56c4045d8797aebc11e0840329037c060ea3a d044c3df0ef59c426481e799222d02720380756ebd1e2c9d47d7ea32207bb97b 70b08d1fb213e7120d74a977faf19895eef6b9bcbe6fc37e7efeae1d3d918c5d 53802055e058b6887a182dc66a73ec421b42c07a21cc041340a47c89ad5d3412 fb5fe089c2689e8eac6139f26ae0db9c19be9adee166ebc65a8885cfe0ea3821 2c8b9e03dfca92ac8408263624c68baf80f42e6d5785d604cd596d7c5f70b338 8ab262f46548be149da8e35da157f2a20f7bfc9f1c36da46921d5328d8b22427 a1f9421b8485b0e9805f0fe56e144dd7c4181fc3e80f9ae36f80cc7ac54b85e7 f30fe136998aae2e385af866afb36248ea6b5b464b59742bbe43e7f334290ca0 1599677ed07f3db69959b3d14c3792e97a82933f158ba4d2e585943074fb7a91 7d6cb184c2b8892119265b3aa882c56dcb835334cf915647c68bdd8520f71b5e df553c31d718533342fb0bfa975b3c91d89db0e80d317efe306ef5341c410dca 8bc5d24dfdac485167671b6762aaf74aa21ea437f4af4a1a12539dd868ca76d1 e0fd2d50d88df121a2039eb5c01f13d277ac83c2855633f55d888f517125b5b2 c0b976dbab7874c35a9064d8d23457b1e6ff3c1ed4ed3fddae24fa2ea5cc9b7d 42c891214910b8628a90d83bcc1563ca3f73d7547d99dfe79a474c75204295a2 d7ff843ce324f302b17425737445ec70197ca44aea6753ebdaf1102fe56bf590 bfec250a04cc336d716b0aa605d41ec0a520584a48d7f087c958176ebcb6f917 5ea5d874f63e43af9530701ad98292d269deaaadc1ddf0026acb4611c3c966f9 0237ea68baebacc88b807e92b5b24b89ba7416bb4c291080b7741e681c33d9e7 40dccaa35e9cdcbc9f36081963f0569e0afc393984bedfede9b9cbf6158b619f 2f892e10b56c16b3c6f2141062c4d4157c184411998260d65f6d32698d48abc8 233400019a89a8092176ea03925f2c418d7d461a33524062783e8b238f0313dd 15aee9d64d73e2891460fa9263be3efb3d359314ab33556666671c8d83d135d1 9a45e92b04b11e80e22505e7b3ad0e95b04ee4cf8ce19736761d37f2b6373d21 3e7d283c255beb7f4d930ebc64a4812144f25f211b2984a1f166be0f877d30c7 53cd8e98edd3beadad4e89eb2fe11e50ea0b0c4aa866749c702f73d667920a43 ec04d3a98fcb55034f04ef0721a05c232b953f9040821bad6a8eb77e36b31647 60c9fce71c95f127e3700042428a5aa3d2cb5e3cbbc776ab9e31ba9a41f9d14c 32386a5c3577170af3a42a0c3997c4fcdf1de18e5ca0ce2cca1465fd8b27440f77910b9ee95a7de14ae9f8e0fe14d07de1caa3e08c492c721e070c5ff09ffc0bc5cf91e27a06f159511ff18481955ea90c4673195bf7e7aaec3db21ef9f467057073de7316a077629d2583df4bf1535d43980dcf2c4967a7f02c7f750827c309a55629ce6595e2f4130e30f324ecbd7028d467b84eec599c8a865202acb9ce0049c77dbd71d33d640cc6cb10adb722000680a34bfacc4807789e4d5cbbd0e4029a7372c6904a65aeca6ce41b69265708afbb3981b07e2a988c3d886dce78dd0c15f0e567a0e79c626ecc19070cf961905bd0a3e6f080f8aa77cbd89cea68be03ecea15f856210ebf93d090b4cee98841d40522829e3d965e9c3e0d0af3e1e10229839586ea67609b0ebe9ecff0e839acbf415134f8b3ec2055742a8a73428f047b2fba92d2534d697327e47dbe810aaec4a89570d094a178340c71c5fa55a202ab63258befe1822b335ca4e8d11e6126f70feb7bb09b37026873008397b01c0c51863a38a87fb92a677a249cb1afb3984cb3b2e195da678cad1fae0a028e4406bc60548e2debd47d31e1feceac15b6c319f255fa44514c3b23f19922c679630e04082f6ffabfd5d3c13a9358d401719fe9ccdf9f16d1e04beda9becd73c0d101907816d67a88f52743daf90297e9ddb1514d9c0221d26ab31f6c78e773fc100dbd97cee9f51693d1415d08c43a74b3bff56d5e05b24de981776ce1abd01f8404f5e9a93eb23d58c01ab0d71a6b006503633799a1b07b8da29f9249e47dfd2c0498695d705dc74092ab626b7c012c5e2f429ed02b43640749587c1ce3bd891201985b613568f81421c5ec8d3ffa4f15437f01757cb3e79054944210dd36efe90d832ebad25a07fbd9a8b47492ef159d13ad97dc8913d7ff7cebe0938ef8d874069fd7a171798f3a1379c1adda5fd34f603ac8141ff656b34cf5188c5751d6e10ee75a6ed65baa7366f0c7f2d0b7e536b92008215b5aa410ddad420c478e7ed607a700c4b2485d443a235b5bca4fc5e1b841fb0e641d890e89fe737421a5553e0ff371a332ac424913a3c299a118fe2db0daebbde2fd76de46453404cba1bbe7083ddcb7efa3b43d86f2a44a347cea3ef741d6606f8c0cb3573fe2a06144246a04f57b867985455d934046b401dd57199c313616cfeb4ea7f7305cc916061c4e0da5b54b92e98034a8306cd173acc40157716157d4b52fdeb453dcf77b5b33360d008ea331201048c1d9088a7ed30e09a810c599b5fcbe893004bad24b837ddd010e7a70c0fdd48159fb3de8a2d1be80e58406389e30bf3f5aafc4b98a6f7afc004e9b748a47d4489f5c4f65f01555d8b5d56710bbeba588cdda57ba56f44f7107ce4f813509a13555b90b511c6f23f8864fd681d5e7bdbc1ba37a1efc1b945300491783a1c4993356ed574cb0dd195207425f52d9f7fca11e7a329e68f1be94091502b00a42190df3081e477234a0b879353620849776e5ad83dcb63ce369ca064c8fea2246bb824fa58945cb9e14c24eaa97a6f765fc02a1f444c00fb6c0a60c0a2b809b515173c3eecb40e0c7d58bca380f1df177534549e1466b6a87ce220cf064ea85df59b76575318beed55bafb75053ffba7158b87191f4925ad44f150a4bbd8b4276951ac4ef37051c8081173028d7e10d1ccb46f68fb470851c95d100ea7c942760379740278f105c3aba287e24c575e3f9dff929a95ed15cca91790718f41b337e62e1d52f6132a13a7ecfc882804e43368ee5a3535eb00eeb3e5f0c4df932990f68f97f56e136a2dca2758a68e82c7f73ae07748d7a571bb918f00ae0aca95fd2a1f2415a03b80eb1c232cd2f2ebfa84bc596c1c3f24a228769ca0cc7c8e61956d009b6d995360373db591e400e72379e5d7d855e18b67c5dd755075ffe09dc5f20601940850b1dac7df70e10ae31766535cfa2b02ca2e165ce1b0e6ec93e058b1c96d251a8de65467536c114cbe6f5cd85269146acc6816f14fe08d2f252cc6a8423ab95a87e6b672a9c95faeea502d4aa8da1eeba625cd1778404e83e0f3c69d908f59651be34fa16ff7b28fd44ff44cbe866d6a41192536e1b098fdbed226a729314ea1b7efbcf89de4cb6d68b8e3e12269a76734e40f460990f462410dd91ef62fb3f51eb23628b2503daedf6e3d9d160a1ca6e941f3d0f01050677c82104d1d7c87d697f26bb1302d986663d323c1fc994179c72389db52008246604fb47b720e12634f9e720124c4832c628b198c8adbebce324c8879af000a4749696c43942065b97b75378c315f87fe006f87c65abb9db4178870aaa8009485eeecf5c64d5a9f320e1b0d91683b3b7979e412ee041b61d13561b0a62af0b7d33b5374d61f9f099a46e68c7553d122aaffee8f30852adec6e54eb6815fb0e24bf00f4422bdd675f2340bac85bba2c4db8c8e05a1bbbd3af4aaec171a43a0a12298d477f5a6a056a3ef81200276bc7f19c871c54b04770b884b6e1e1b35f0eaf063a1e68c5dcb4efa1f5dee8a528bf94c44d11fcc66631f6d81b53f261ae07d7f2b8615610225d6bd1a12ae6449d2adb271a8d997d856deab0f0e593918b0d9dbed84826df1135623775cae22a8098efb2241dc58292ff7358df724d06ec04a90fcb26b128acbc573b63eb153e3ae3a00e786cb2846dd41f29db416fe73f07f340e69f827bed35873b940f47e6763f08cd8ac61dba22964dd49e9a1e5459084377fbb6a7c150135e81a7243b1a6632c9094da76628160de2b587c6a424df0dcdb9f32bd65a5a64b2d18648eafd8cd44e3eb56c397e5a9ebbb5983c77ee8a0a664ecd11d3738525c7b91b06c972a1a87ac0226fa04b9db47d2518069edd5d0e0f1c2dfee447ff2cfd8dbd63741a666e2b35a69626cea767d51db120b4da5805ed3472d918d49f571843a2f1907ac4c8838b75414b3cb9df152a4113cfa6c10e2c8881afe96fff025e392747f110916aa4b328198a9aff40eebc9ca01fe3cc09cbf9e3adc7f54103b9a8dc7b41ef7ef54370c84a47ce00f422b3c4fb183c190ee05f1bcc13def59009259750ebcb37f1bca5d1fd930e8c0669c545b817107b0cb819cf52ffb7f06d1f5c858107902ac50cff670e3e83685aaff393561f639102456a4afba84485eacb69f118b1e24834db114f974be108e4dd2137920d9e14053f2e5fb9078b34408385af84b285ec115eed43cebef6ff7d291a43fb4bd39906f4013320fafaba6c1c1a030bf3de26b2df704686d36074ea642465d130a691043d936f9eb562aa289b9223ef5d5bca7dbf3a9cdba7264be0c96279f8e5d3a2051a9a50dad96c9d0bcb53fcb27a6583db8771260e7992cec4d8214862dffd9a036d3c19e0bb1205206b4ddcc21afe1c125f797f7629140458810577bb0fd1d00eddec0e3c4b03c790263e90755c0f7de8bd1863244d7bc188d5ad9ed43e04ed091b81e6d5b2b23f4d7486f8bcdb64538601337d62f983982acd0c0b7bfa57c1050ef65f2a430548795c7c8e3420ff875c82829a39067a886700c210bafdc6860819a41f23755c4c2363af7adedebcd800e0ac655a3873b346cca0c1a755177602608aaaf91ccee86c7c0a6ce56666a5dcde0a9399ff042ef34a6ed49eb064ca09b4abab81423a1804c372c732ea0bc30333ab0fc7b7df96db426eeb890cd1950fc25b7f94737947137e0d8a513ab39b31cdf6770cb59ba9dffe988b20e4688a0b318cc09c1cd1d4774a489fc9fb671e2696bfc8b6e2525909167e85d1cc2f410e5cd0207e49cf33ab50942c1dcb603c3346f0ecbc9443439627cdf212808952030bf9fe8721d681547859343f6d3e014fde6ad1079182dacfda4f25c3fee5d8041c9d540c448d58df412bfadb7b2ca84fe2b3dbd82c77dd713b45cee8db7a1a019e2d249ac4887ab75415c72cf525ebf4f35eb5efd1efb8d1f9ff93a2eca50408d34dbebd0dd2f55771bbf903782664362c22e4de434e5244ad978e24fbdc080596da3baff7ef233d51421bbab9e413cd8d6220585498745bcac00405821dc700205badf9aee098f327df9ca817b3084b9cd3bf22109c18cd79465faa22aa9207b833120614756d5fbad0adf99de756b6694e7acab27367e772a2d231d8c0f405d0e8661c7385fd06ea1675b24f51c1c606eb6d54cd34ca8d3907c0bc8c0e9f06c18a59a65a9b02ead06fb8de8da06bce8c1746449d97eefd807461db190e870a7fa385cb8e4a56500fe35a5ed06ad79af527ba3142a0b082d9269f5b7f65520a810409c71434502857f951a7c0ecaf9d475fb12bf41d0d3d8f4092171f048b0f411528a3cc1f71988893567f01604e3ed33d96fa37f8d290ae250d111944d20fa07396006a5c1e7be2008d424f4cd2ebff523140d4db07aae7df9875d4a1be0f5f1aa325610f94645d04f7761e8866db4f8fd676248a4d4d90dbe68b0bf48a09a4d133ed556597fb99bb32f30e9d89bf636569e5014301b23850d5bc34eca40d67e15d818b4ccf37c15885e5c1bd6f11d042d86c3dbcf73a3ba564de11a80d0985c209f011620d6494cbbc5fd74e69cdad8418128005a598172ca544d62985049b6f40c178fff398a0f15f048acb40c257decd2c02268adefa34b3d175f6110cf21371a627b8dbf1520fcaf6fcc1d397b68d94d3e537e3286fd512156d930c004a1e313aa872f2850acea2e5861f9c6dc7293ebf81ce92875dd13e83dff5530f053538c13f0fad781ab74769c1003fd855be3d014e773a495915a4a3022f050f18ff0a3711b7e0767fc0d6a472574f810c0b2b99ad56d568c41cea4ea9840e02a533e825cbcfdef871525f9cf14e5323952ca9134752a5280dd0caeec155120e86fcbe4781802700903b205c3e9f6d47b41569feb3017d016dc0f456abea97067e4996f663c84029d3f7795601027a379e8474b72b26947175dda7fa9fed6c0b8eec8da8fa30cf696ff12137be2f03049fc4e5de5427b1157883b10a901ea10c203b01924803bae5206f10b110cb03ab401054983e9ec66d5c31f525fd1924046f0c3840d402bf3eb9fa86a53ee6092945c2d6227a6d3b0f98ac11d2c790dd0215763865c9208af8dd4375629372b80508cea4f6493788042a345b15cd87320981a97216759895933e7914c077a9d2dfebe6096cfe65a53ab8cf98838805b1023976a4db9014eac9ab4e4b275b468bc2129e49f60b09c63c26b90debc373e20e5e0076197786cba1d502e69c6c702c32bf6304f2808b5e1521b74fa1f21cbb0c772f3e27843d7127d0b4f996db6440f5b2f424b6e42ade972c4c6fd1382f480c6e62aaa2423993f1ca9291b512665e4979d6091721cd13e6a81a6e5d20ecce053146f1577a27acb745779c6139a5f67cd429b9febef201954bfe5a40319c2902b0d7db25cd0f0e9588343e6b2ae37fded6767a7d802de50224d63363d579a201ab2632d33543e116144ec8e2e5037ccd64dcfe085deba9ef495e762da42440093f157e79d086b76e87a48d077ccb38bfc6e07839af56f22696a60fab63fe160004e05add4854b6ae91c2450a4f2ed7ab6abd1e3ed9cf87b63ed74aa346579000173ccb54c9cec9f5104dbe6cdeeb48a0c3611726ddb0feaff8655d9604566205e235268a8df3804f203cdb56203e0811b6a2ccbe04f8e49194047de592c288074d23bf89c6da332653c1f67894f814a0f8d3b1f20a2eea8327ed3d946596230ea509193882ac582c4981091ac32be6ce6d28fceb0d859d4ce0058011fd6d160083b59679c4e3b6a257d8958dd30f3512d179a798c5543465879e260adcc2880c54a48fd9e6cf9a05d20cabaa653acdacdb2929f4f0dcb3a38a1d7496317a960288b6d7e0a48f17f5162e4cafca760cdba0454a5598a115923bff29b2f74bdd071d820b2f7c5d3d367034c37c02abad27bd7d1fed3e7d309843b7519203e69402432315b86a94bb409e2c2732634f2b2fb59f77c6d66a553839570130cc224b0978ca874c1681caf0c616e51e594d9d328aba6c52f9f692b323fd6c4beb64b706c1f4c3aa326b64e580d30225b8f6c9cfcff1d5df0f5a86d74780b443feb1d408564fb5952ad7c99a4bd1824452fd4fb270be772715083b9083ebf25ff705d004de614fc746a1a1c0ea1eef012686de136a4cfc716664809fbebb565cd1cf530d957320954a95fcf6726491674cd29f2cb611a135737d8ec36b018ff074477600da9184eab17604a8ed6d1743d65f35063d31a1c7130733c2ca727a23264d740a1e7062a68362f205c3e772dbdb4b7f5fccebfde2a33252f48556902b57061a08a0f14ed104594c6c4bedd89b686356bc823e52fe8f5dba3f8f588479507d490ba60a036a24d7f0a6cb5c1dc96d8827f11857eba3b4aabe6bc42ca88e203cd40b542592966dd45c9457a90ca0011858bbb45e58660e7d27aceae8ac159530790ee7e6f9d87277b3611f3c2d84e0a511b364d184abd3b1b74cddf5b23eebbbad07189c2eea2733e6c2b5b32aa4a6484bb5ab7902aad47171651337bc7825ddde085adb5e78a669d358fbfc4144a2fa4ec3e6cf581cd7e3f62852c174af8d5b8a0442b88d1323fc4aa7a8cfee2595ca7a079399114165579b3dfb09f79dd2c32a0e885d41d46f8ec31cedf0cfdfdf306b0084afed2318ac2b03b412541359e374099ad20f7cd513a115c4f7b70212600e069a50a8228f356a4feeaeeb02cafc2f02f614d121f472712f3627c0ba4b17ccd4df0c869ea3a809d51c8feaca15ccdb0180799d4349c10d7697641b891be3ae47cc9abfc4fe5e0ab0d56fb4536dc5f10ec13ca67f499aaa7bc9c3ee4ce61fa77e32ef8f700b5ea521740aabae51f124045b4e768d54a2ebaa3b14cff8d93d9e1e032a0d9f8615cffa5e17581163a714092e773eb3fd92dd1f1da5aada67407ce7befdbff1f67b4af4cd0621a6fd276409596feef0cb680a89ccd7b5ce4f13e6f3875a5a39bfdc47cfb340a38b4aad300b5218ffa682888fa3a0469934198f2a542a74921b0867185a1d503618d774890dd4711a6abd154ee5be85dcece730f7b987c78bbfddf38b642e4e1c707a36350f2e6e740ed5cd7cc3c4602a46cd44fa108b89b605594ddc1ebe888608c88a4701f29def10086946bb5f377aba05186eb4b386c2ba4746aebf702100a5fabf7c0ea2e25270b922f9a7af0425690be02cdebd5d8636536deaa419d47765c797660c4316a76b7af62ec73d11ba0457f6013cd6d6c56a8fc88e0ebaa8e48028b2d6088aa62d67beb195a3c66f380e11b80ae97d72065b8b0c78a88a156216cb527c0981d63bc576361f738d978090f6a949be7b1cc2864ce4f47e0c9932fbc6bd5000a4d324a9693923c9cc1812e3b7194fdd4ee10520efcaa66223490bfc6312c702b88a5adb504bc39aa5d68f04850cfc14b621269110abb919401ed2d0e760240bb0722dfceed1d5f070e9c909533af8e2ff815c164290dc26ce098d5b1a082607aa1c8f84eae7be361d5fc3392fdf9f35d6f2af533a3f3d291f56c8467504bd03852589e292d95a4c7ba93e06888913c479a0e13e78ccc5fa4dd23c3a41ba050c7820fe0d202ffe9d1ee2f4a0931873ed6c5e52bc826f1b1470d08a4e05d89e0230307a6baf0ab0f56d94758ff4bbb2f7c65b7b38b8818f4fe7f3463ba4e77000cd68343eaba0f3a6fcb1596c1464a6d9f0f046160589243393d88682517cf70444298640ea0fdb0a81bf5c93baed6e301cfc76af3cbcbb4fa6b51efff6380c0311e55fe33b89a81c640f60ebb209868e45c12fbd9bd5792635074f887b73cc0eadc9a385dd8a132bc70bc139b54f31f8707f09330fd588539961005290253601e93fcda345e7b514643c8b0d49e738e01f2dfabd63c0ed7986843a2f2250c60eaafa165783aac06c65012936707b261b91d7631c26b455e9a477850cb400df0d7d8e631646f25ef6dd2855d90ae4a91f27d9603c0f9610b705903b134cca070dc17b8370d4e151b6799923050deb7f5a0b8294402b52f483147e7dfa50621e0cfd2151e38f34e52521c1a2a3b0c19b983ff39276c0211177b9c76c90101c5c038071148c5b3f79885d3f8a093debee346766190d300fb3827906d6bba48fd80928a89d5be3d60037e21a86d8186aeac8ad0ee51e7857093eca513032dfc3a80deeb82342f9a4d567a811d38a8c0686b0b4abed2de7dfd86bf2caa51ea480e40dbc26daf149517b64505494bdd588347a4b887264c4d98acff3b98a71cdea4e08ef91c02806a5d758dcb8f4d3f743b84d8130a905b45dc061e531125177056201ff646db8d44f7967a59d452f971565031253b0e28991875a5c20deed7c3b540d105fc596e248e103cf1de9e45a2bae116a5bfb914cb6ec977c4f78dd813e10059fe889f6243eddb0d10746931a648a1bfbd23396d651de4a4da0b7612739d10af16a139a4387300e7d8faa0d0f25aa89745e32f3f90c8c60858dfb4651e4830c6ca04be6ad72bdfb8c2b799b3106c0aae0f418c2d2f57e9cf1aac10e861b2204dc22aeda87ba16731664cc5be0ff783bf152e81257bb6a154f575ff0f4e7c80fe2521224cd04aee43ca49fc031ee69c8847a796dbf91ac91efa3e2b9a8ce410e2a0b03be83994ba7ec064826dad90d39888ccbd3f42db894e9d581826c9090007e80b41519c550e7294a75e7e6690c85a554df92f44c4581a44340ca011b0905365e008dca44b6afe7dffe41491490b3176ac6145faa5fd62befeac9248d2d045d12f03e5f3ab96a3c1ce01e5c13a10acacd076fee8eaf96b3c9d1874965b00cf2ff0069b58d1ec9c543e47cd13a590f54d8d08589df5939608d48fb182fcc0bf26a85ea4815ca25cc48e1b662d88c84fa00d414edd13427477012cd76a8e20b14374592a45ae0e1e3ba5bcdd9708ebdeb8d3bbb06dacdac9fb3fdebe734890f677d42d6b1a7e4da504393275dc4a77fecd85427cd1763968088142922cef001391f6656e4dba00efbbf06bb62f2c945cade95d2b57589fcfc0511d3fd59830bdbf555e591f5fd0b9b7245f8e10a91a167b85f2e312a56ceeada83c2324fa00e342bc9a164b5947b2b6b5cb487c2e48621dc4722e6949f356be74892762c6b03b899bdb922bf58314ac0e18878f308585014b8044e05f2a6051fbc22cfea6f030033b01cd0b7a6737419c882879c612c228811bfbe820676712cdef29cf3f40e19e42357b8286b7b44aeaff11692e1223dfaf874a9dc368ef7d6b3fd7820e40531e547655e93766967f89a36c7340139daac3e38ceffb68559024b2276e4a10bea24e6dbe2f185127c230d6302fb084d0f81ef1686fc7445ba40800fb6bc350d36632ff5ce617ae03d22329b336cc7a6a6dc1cb18a51c55210ed2aab418ff907ae61990dd52f94f2c0d01df01dbe4ee8bf512ea3ed6a30e93ea0532ca3430e08ffb7c187f907b8f2c6a0df081b81e9472d3eff55b6d341c84afe1f38f68bf8051fae47f8346617ab29fb96e2e3723abf874fc96a343d58545792f7313d01f100df9b839061452fde7c47b7c40e5eb9c8cb8a507d9ce944eba7e185c9f177410cb2f4c191f7bd6f298273082dfaf8827d92cf6eef4985bff27b3a5d4863a11202f0392ac45315848a5734bf6b1702fd5307302a6a562df3446afc59287b4c8601e4b35436f29ae031f5179fe6b2ef063f0fe844db5d6b7e5b837aef3fdc7c5c0826dfae3842b743c72d2718471448402eb621b7bca8ba0115c2ffed607d20650c1205c47b96858246a55795c0bab5a63670d046950cdd1dd051ab7a94630dbc0d93f954039d52a21d4c1dad0afba8996755450bb39feebce7331363034046340d1c09e9ffd1a2e741be23eeef45eb4ca00c848ee50564bed5af41329332463f07ac1ca1539fdfe742f32974cbc0add332d6d3073b99c32884258ba66b2389bb0a457d37933e5a3f3a5f2d8afe19f79481236e3ba8f0276e95944ccb6f4bc9d801bef979a1d1be0b908e4e42aeea4c9a82da47b32e7a112613747bd65fbaeb9b0d659333ef8856f29eb89d9336461b5d4ac8a76c0a5c2a20219380a35c2ed4dc009494a5681d21e136e9748568bc86dfede1b944183588c0f490fcde8376065e00b9d970d38e59b38e6ed85e4aabb864ca717801c78b444feed75881491d391a0db2f5481743e802e08aa3f3e7b92dd13969e6bf15b036addff64f0eca3d072e0d896783276ecff40c05bf4b9adab76616d8c5681f3cd5f2cf1f9320af168bd30cab8cfa5cb0f0b2e3246ec146d10929e1340b2af04dfe31e7e3138c51ff02ba0bbfb5f09e3bf2fcf8b45a525f9a9581edeecfa92ab0d8c4f04934c7185fe95f053da2dc7e09343b3f73d1cc4959472c3c3061bf5faed74d2849aeed8627199f065ab45ef4aba4e83285a220493d36da9899c8fb54b3d92a229ea4b311206fad0793395a46f21c8bd3a57f69a232f2f068c4092e9ae00b426f9cb67fa0ad106e0b95bb471c6a4987af5df86f558dc7fc7096d91cc0dd1b3acf69aeb810e5b64300dab8b36e4da887262f083d001fd672b3ed7cc452c3fa0970feeecddc9d011b01da39b523d278ae5b918a2e4195a249d9675b98ddd7b9f9ba0ac5ac8b89369f0505168683f2d3c9a8bbc1cf83eec9f0ece15a17e2908e941306ad9fded5281e0551857bec8b2afd7820738998cd3281dfb3f4663928c421acff492d60e1be2a0d0dee86fbb5dd646ce7c8000dfcd3cdb7824cb7730238c8fd709cc8639429e805285902f5a12e791637a5c97f7f4e91c8f6c60907b7897bc2c4dd508cbe187407d252c67e0498c570f6d9111dca88884ce8a2946c6b1efb825d56641c01df4d069dab5c58af9150edb685eec94bbf570074d163dd1e37fcd5c6fdd103d6162d073c3a326583efe9d4267f44d86c21b1e4913dfd2a5183de4b2868376345ddd80095ecf812c1b440bb97a59be09fed50abb63d9af4c46c0814cbc31c354db4390748d5b9592b872df3397f25f4e6115c78bc1a9160b1ce1a3c25341dbbef4b480a154ce5ef9826bd2ef965da2b34ebe2be685dfe49c90ed86c0f3773fa2c42fa0bb24c0f636041bff12c43c917424ad0b9f85461a7f64920f0111132c2a5cb4000c0c05e75d7af9b4a84f7062ca434a115426f4cfb2d57939f782a561f5e02c80aad03ee09983990955184ae6e9ff48493fa7d6bfa6c1e7c8bb421697534463b0ef1fcd21920679be8262e29f677e4b11b05f1262f4683c3217b730b64f4adcb0f96e4d3d93d0e916b5c222f50f32cb5d00663d7572a646d3ab8687c903567720e true -check_ring_signature 05456b761b894503eefbf94f666ba089c73ce567acf3e96793d648deff6cc7cb 9bd537aadc7cf3584b89cae70e0c664c1859c01becac9f649cb1034998658cc8 1 22c90464db2e089334a2ee6393bbf2ddf556e5217fdaaccaaf24aca4a952153a 8364bc2b4fe6bd3f37c59f4b740c089cf4958bf437ef5fcc97687257c70a6a08cebdd402dafe49f07f0b9ef4aa2eaad78e66c58370eb00a01af32e39e2f3dd07 true -check_ring_signature b12dd0992e9da6e78e786f7af05b64271d29b78f8ec1f037aad6ae8388d7311b e4bf2d087da68d651d9608c31be6402e4c32979bcdf2c939583fd5259522ffdf 37 fcfc5278dbb5abfaf3143a0754feb75eaab47cf204a9d1c47577551e14b12c0a bc7dc2c818a3ce30b8499490acc13522c3cf418716f5df2206e83e652fd60b90 770b7ef51c25222ea7d0ad23b0b2c0fc91b45d547ecebb66f4fa5040f0169ec8 bae007369337ae40294331135d2c3e3b65609bfd28c6be657fb75b60df99638d 124e522479bc6edb8a17fcb242b4009594a1dbcd1e6edb41e1f8b58c3dbca9e7 080136cb3cf3064f9709d3624349104d507c2b480638e54d1487b055504d094a 6bf45e1cd43f29a9fae608a081d17294dd2e0b9277a47433cd0f95cf0385a42a ac0bfb0e8b6f8de8c8ebb0fb70eb0bd4577e1e1cc06b215e5fd6319567658f3d d6a7100a010c1a029c677d0fe8be24877b6051b97f15dd19c2f6b710bab30390 7c9ef9a5907f5ed29e18db16c6326ba701f5be5abae539293eb811623a17f9fd 4565ebd18eeb125fea4a5904af9f2941244bc7b8299479ad4bca0b46a40b3318 9d6b810a8861b779fac59de7d2450912d08870b50784b4a0d2432491be2022bb 0869ca0c5c5c2f8d5a643d58f991459804cb0242ff95e0b6890e2b81b3bdd53f 266caadf399b77231b141577346daf8aad3b9ff28b46840ef9abf82ba26c1812 abeab35326179fbb39bae5ff4f88bfad5aeaf45657e6592e5834a9474cb64ea4 34c5ca2bcc54f251a3517300f56cb321c626ca3c2002da50f0afad0d5018c035 35d7443ae4edf60200d7b02b41617aa273d187424dbf54201f163facde87cf0a fc18e8306913806c6d6b0761d8eb2416c2f4934bcc1d3785ec2ce44bb52aa0c4 162d7f3fa05a81a90dbad10d8818c7f6a4d790f526537ae6ac16fca7c22cfd98 1a7ffb004a12f8f5667253ab2a9cd17d7028df6a28d4a50468e749cfde98a461 960cb36c3b1e1bbc30eeaf5bbda19add733243a9c1fadfeec7e853b8129f124d 5c6bd2a8fe3501257291b202f5e496ebebaf02c736ffd71b761f7e717054969e 5f2c09401b5a1c5cb3a38e82f92111b6f1714efb92dab78060dc392e61a8b06c fbf8c89de25f773b534c120ae6b278463795f0df173c152a3de711caaf36a617 7a638e44cdc2826d4df0a3bb1d617bf983c428f39af7ce5abc0c5adcc171cfd4 fa6e0bb008e9af0c10d4a6f9096a919948bac7f6db8198a47c1c7e91374ec6a2 0093a2bbe7a198aff87576a5b94cb81743b342ce5a858c98b02cd892cfab7ee0 43d6f0afbf42cf8219953f38c2c6e6aef79c5e32c35d9e2b371c400a31d7598a 90403e30f3014c8767057fa340e65925e5fad4eef1ee61ecc3965c42a02dd70e c7ae8a5ebbefff599a984fb0564a801aa0b5a5f179b4d48a91a4fc9486bc168d 9eeceab9aa77e92d16ea547a4eb0aee09cca915212b05b37cd97a501e729fe53 ea080f10d390e0dc793809526c508af13a320773047ba363f57ce6f448cf726b 958870c90288471c5b3902579a25acff761574a2f058e7f146d37a0e61c84308 a730fcc236dbf955de1c3485124d880aee0ad7a9ab109c380aa1db4f0d9f151b f071e920343be458eab9500ad4e2736031ea08e0050731becf0cfe7ee2771a3c 17f2a9d3e837a84121fee34055f651793c37de5c1e650b708557fd8801639dda d48ded5505b1a84d74e5caa0331c0a57ce33bb7aaca436631d165f946226d7cd 945316837bb3b00f44fad6c7052158e8d2b87cd0d16fba5ae2d91e302e2487052b18d6d6f5e74edc754d09c40e9d7fb126976a8335ff94495d928388bb0f2f04dd30b6449be7f25b3df37b949048616374738cead669211aac180fe127b15c08cd446bcb1c7c0b0cb22d7d9c7a2fc05cef36d650ece5330fbe1caa778835d40b66bbfb65a57e11db0959ebf479e9428982710b1d22d3246280fcec650618fa032b43dc7c37c35957aafb843ad8ff7b32e6500d059f149135b639a18086e48e075f25bdb4ae13b67e389f41472eb037048afabf098319421612eefa03097f9b02445a98fa67f9e20069f6c0c84627c6e42e9aa4a5ce2f4a478c6df5e6cd519d0a650c075b759b5b3d688ac3b9cd7e72c6b31b4d149b110db44eeab4996917310be8eef76be2e8d18e55ce800a4be6b9e77e10758f1197149d4aebb80a99c56f0cdba21930abf66385f23b26fdb293dbc2eb9fc0b60c54745be263ed993367e704725959e4557ea2c270424414ddaa7ff64e36c3a739e319ba858e12506fc2b607a5536e8b2d007e93649bf638e4c534bb4d5bb6bc199ecf09495b6724ee70b10764d85ceee9a1ca8a84e4c6ea69f85363ed29eeabb356a09cdbf1495215472e02cee27d1698da198df1b7a18dea261751a3f5e1b291edaddedcc1ddb10f5be00b52e524ec5f01a8d468ed403c10c85a2a05d04bc620ad9a36a3030f6489378d00786028ecdf06571069b41381ff97b01e0cdaa7ab0f17b9308f9dba645af272059796a336b020307c8ed957f33c7dca30341dccc83c653cecd4eef146918e8f07a8cba7684ca04281b6fe8d0983ae377e5221c6267049beb1ec2b32a8870285050f1f4b3285585bd157d4928dc2219cd08b569da5ae8e628b8eab01ec1784a600618fe6c0829f7e812ec928197179eae153e3cf4a4d04b09b6ba4ba31afaf260df1aec009263b993e6061be7a0b3b7fc9c26fe7d414d0d42c78ce6fd0ffce3103832c3fe641a27f988e22cda728e9c9b0ffe3d619e3b625e4bfcf215819820c012b20c18f9da589bfa103e46658072f3c323dc866b2b773b6f7fb27fb6c440f07db012446ad0cb07a9787f02982461b5f07bc6378cd85ec0cb57bf0cee7a5c20096a6ff428256958cdb9abfdf40b34dc13135bd204abcec0e92236484b598220c66c863fe8c337b6dec6b8e42748c23f069319a574ed157c306c0952ab67fc20311dea007b4f0574ab549653c157f902b5bbc81cc4437d471297c832dcf44550ec588e94f0615e3716df6a69e9e036b82a8536c364de744018ebd679d5e25a0005dc63bc0e667303c7b3a530bc8dddaef158d3c0b0793f82d44092c9622986e042724cb229177bbea58ff431ca4f8e24568e0d9f3c6d772161ad65b2c5551500d6de244ab4fa435ffe9a40cf36a33402164b1e5db6d44d3e82dfbbcf7dc47c108839dfadf720532d2c87f3d78157c9194912b212be806e712a511ed890d16220f688295e6935da09681f4f1272ef0d6f442bcaed795c15509672059586fc5e604b56b22d02a4244adfbb8683e583aac3ba4256bca1b6a0652a5c704243d7aca03d0a07a56cd8ee463b6969d682c0ef3ec35a8d419d56d756efa0871a3015c8901d400c851c6795a0df1645ce374f59015da83d1aebac16baa1e2f663f7d805409315e11209d91c2853db9ffdd6df18af7c4b67193529839f15cac90846249f102cc2e3892769642967635b308e6b7a549dcf038ac3a524795fe61b1450fda710835f6ccf6285be08dd464b364990f07b7edab18bc0f1b2201dc2abf78323b4906d344e847829a3239e7400d8f766a708239c00a0c52b6b893e58030fad9ed0c01bb6f32799ab858466349b2762d15978a9b5fe359422406c0686629946c6bdb075259618e7ed6ba3bbf693b41df52689714858f28d2575ceb280b18753e926e0a5b78237733c76db14ad77f9f638a77a48d0762a143646f26a65090e84c1f6f0af75b4a78ddd4ce22a1ecf5f1d336232afc0e2df5206b73936973213eec827d0005535cc5dde980ad585b4cf54bbe5e4a4663436c1155e091e24dd47e5f0afd03a17ea716667c78a51288dc26cdbc3538705fdd210cbaaedb8535c56a3db41d05f72bdb4893df67e56be7f53087433e0b90216d63067511dc3be341106fa2470126ff6fe3115533ac2ac844f82f5e4d0144cc64db23c7ece1bd3372307a4e220fb90335c7be87140150d56828fa9a27630ffd99f1b641e886e9b232c27792060978e708567856e3d417927a5a9e6cc561564d60a17ea0ed117785c6e4d7204909d35a27e7f8f5c7152ba93553928be8814535fcfe4c7196931c0a8556c7b96c03154dd915690f2ad3f408e9dd973095a783ee25a4a582178e355fe8b9a8122c0332c22bbff638fe51859721d58574068f842bf1295230561aeb3d60467d25330aa5be1fdbb62027f758f646f38cfd27bdbdd15840f0a21b498f40dbd575b34b022c62cd7a317f257b431ba1871117d342326149f236552bdcbef8774a61f35c07a6f8847af2b718e679585d0c6e6f8773874322df81d731ce3b13be4965ee8c063d714f087b59781e235521bfee1bc018ad3c8dbf42730b70f8c77529f25f190a7db5b38fe1e809b1d02ed07847fb0043c36fe9060fae602e708e69bc6800e30a3c8fadea0271b6515f41e195a2a1a54a9a75f6bde55936d8d02419173d34aa01e515a17f7363b1b3c1b3f5f54808351e665db27725d5205677998463c2b7a40e032cf5d762f98c7f96ad87712a75f4d98fceb6bfa3cb5551e7e47c6398f7d005ebc53b50c17a82493580e3c8e18bcc9e688a9989fcf3a5086437c070d136260d6918b62b32e20ca26a637b6a9656d1d6b33db48affff2f8dfdde559c230d9106fcebdf06e0a3627aea04ef90541398ddbd41049cd22194ebfb4f00b40c24ea0fa2d5c215c8c10ac2eb5ec9e4e464b0ff87beb8b85468cb90deddd6f3d9f5730f56406c1096fad7c38e563c42111fb2fcf302df46d32a9b4c65dddba6425783023891f8d5df2c873509477807d4a9a415f3acb4c528b5828f76c329e7429ed502f7d1a8eb5ec222006ac39fb120062af750d46854f91a09ba54563fb0e063710ad50619b1cb2a08cfae156247cfb98b79f45ad928aa13475d0255d0d2b9d7eb061b7045e15cb03ca6546b55c42e0fb9ea1a31ec475c44c43f000c4686c95e4c0bda2cca61a27331bacb6462bba5b6b05921f39a297d7b2e74c274b03f09bec506e3d313c12a2163fc75724bfb19fa5b07168d538a94d62ee0e13a3d1b7be4990c55cb4ba70f8ea58eb77c108b361e4b4690141f46502b0b301c41c47fa943ca0d false -check_ring_signature e747365cfbb09c0a049dce3f53dc0d2123c8e3b25ae3d596485be3e381a367a9 ad7f700883c41f32d6ac72b826c125ff409592b9efa34dcb3707106149359347 6 b4e6bb568a2f04b195b8ffd23c470e5d6f27bc1d343687505e45094466522ffd 9e6e57f99c4ed840e5cd2d25f22dd1b3082de6c0a4ae4891e4b94f522e4b5256 954f59a21cefbc11bc597eb9b2086c4c33e72436b3936c0cbe1be60fe06ff2e0 35e229f17e9df7984bab6f22a12efe4a735ca54430ad55f3cc33a2ecafb06ef9 68434822df292d8a53437ac01e0a0a2c5307b6afe181cfce48930c9c030de888 d71c0590c4513565758b28dc2e20e8536867f94f4948c16323381228da7b4b38 16db11a0abe1a79d832b365a09d7e306e4a71ddee37ba8699ed7e253e64167071d19c1b81f872beb338f463e503e7ce501317c4f01afc3be6860a73aa5e2ac077d45a5aadafb21d9b85176ba7eec57ed95d211f430828a45e09df5235115550304ae7495b9aba58ae2b1c03354945d2592212c371b454afb69698ad6521c8c02cc7d74b4b49a6eb96374c473c26fe0df49cf345d8806bf1f1c2d6b9beaadb4916da1d4867ef267d1fceca0803dc1cf93a9fec8242b031e29ad5b00eb4e548f0d0c14bd1f70f692bee8e4a3064941dc37c22e492ee0dfb325cd368b3c610b370adcdd0b085f9be1011b329173ea1272b28a19a5f18716aff84adb88ef3e4707001dba2e96fff855b982af62a7a14ffceabd9902a9659959775c529db2d4c0a80285781f657ec462802ba85c569f0eb5d39e91e84dd49575bc76b3c3238970f7030e3c0306e2ce4ba3f24b6050490c16c0cabc59979ee56634daad61ed17bb0c0399e05659ed90afaa12a49b98febb51a1725678a48cf395135e810438c425cf01 false -check_ring_signature 178ed73fc5c05fe9e2e7b391d7ee1c53f10943e1c0955016cebbe1acb4393c8e 73ddf699be36ffa9870ded27aea49abf40abfd901ff8d7eb2595d623f554c955 1 47ffadc39d03b0a0420c82230f2e0a4ee6675829630179df4104650754b2c875 ee17f26844113d55ce8207bbf66b1b7b2bf8d851140222a9188447e504b81deb8e4232bed230dcd13b4a6f760f6db46eb3c517f0b704cf13ffcd122ac295acd6 false -check_ring_signature 5646f272838fa2b00e184498b02aab553efa76a164b6757f6a01c50338c56d52 e50f40a3dc6393003d3e7a391b53290537cc57f255f31005e30c6bada833832e 1 15b011b76d774010ee77279a736894ba112db9737e4545b861fd80fd13cbae2e 1c6f2bbbb1a50e010ec1cfc94756e438b19071bb0ad221117dce3d759cb3ea0955e386d5f36ea6b0b2d4976e9c32ba118de40b99df67578a85a4985026e0d300 false -check_ring_signature 34cfcb5eb516610a5ead4eb67ffa06519ec9a350d99ffecef79f4b44fc62889e 59424c22c2c1cdc59c0edd74a8e259947d9f54cb8c6d4fcc901cb010951c9eb3 29 32d19e67104fcd2137b2de3d15afb289f2ac8464ca8a480b0b89a2cb4dfd4418 0d963bf2558b6ffbdec3a12dc5e3f14283646c263c971456f5f59dd9734505f4 65b3464546a50551b68a6237cb49e585a93f6a791965701f26864dc1f1e02d87 fe1b06682580f3e91f0f499147953bd6bca83b3168fd1f67a156d8ac557268ad 430dfe8043f96cc3275a672fe7c9ac6dd8a8d160e5f87356fe138cef3c3ff2c9 29eefef24e0967d6fcf9ee3635658a1a2e6187ced7ce2b09b069303e792e95c7 6f29dc7b53be82a0ff89588e4613e4da22c37bb06a13dc010f261d35de2bf2d0 6d1e03a492fe7389ed4d18b8324181250bc43f10ebde30a08920c621c5aa35d9 897c7c206482f6156771b0d51b0b140b7794fc0d35b9eafded361d39a9aa2a07 ff26e684f6d1c57af101571858b158406a54f25dbcfadc4393a48bdf240ae67d fec27ecb5fb4e560199652e719e21a104774100aefaa20487cf798460a747ee0 cda3dfda857942ca6bd113372e3530d72b2ee822ada89d19f4c3692479c8b4cd 7eaedf0e12d3612823092b0510f813d029a74f64b294491f4cbb4e39d416ef3a dfe432de969cd86f18d488fca28fa0cad12e5f4c093af46369c450a49a844b90 9e230af147ea444b54e64a0663035d3a47c2b20916c4d307c54b5ae98e17d48b 5900137d52228da024fdffcca9428e563c106e056c44ec2045476b34a7f2d437 8698a8f38af40bf71a554eb04be524557770e8e93c52a0cb277d7a176d819679 a9790f055873dccbb4df6b6ab64b84399735523b52786fb62264c5970ceaf23c d44e96a2e97540e02926c470a91562d36e6dc58cfe4fa7bd9fd498cf4c9a1482 8a04f299dbb55c4a667940145d00f8d9d8c16fcea699e958d1f713a5994d287c f272d76f82ff418fd3d355bae9673cb3e9cce813981afda2c5283e162522b848 ddeb6842ee7e38667dbe51e401a5c413d3e15c69e5da30288a2550c078ef7e6e 2431b46a19de4fbff8cddf600647b5eff55ca3520358a51b8fee1ee06b7221e5 1e58a0b418acc7c1772a17eadd28eb404dd9d8c8a7de919084eda831b8dcc500 4656fbd7b7050540a3dbefd1677dfdd81f37bc39acc577e1affcb88e221e932d 111c6520e8533285c32bce53dda556752fd37abaeef70c793a727a2b67516ab5 ed10e2b6d651e6a9820864873cce7f95872f747b63aa33772e2ea748b0ceb4cb 67c78cfe25becd469f4c868fff223f73ac6fb8b7e9e7f7e3eec888c58c880efb ab1afb33729690d316740f62cc64c97519e2f016f5ad4f13e2a08457de87179a a591d0bc8b002ce713018699c2e35d2c477ba66a8bc0e43af3ccc4b735bb200d1440596af3d32c8a660205e53742f7754e1cbee1bcc4c606f8af5a32ea7b700d43f71f152e9d01146e442a60644535ac957a6e8e9292fe2c72ac1222555fdd09620bc8b38edb2079bca1da58fca459dab08420a4aef1b55650b462fdb7480c040d6131816c258a253aedfd4cc7bd0731db95e01bde84f238b371c6048ec7560c9408168294e16c03feb750b94c070e0ee1a2998a25030648cd82fd05069b040a07a13773c7735463fbd169776233efc4a8fb00eaaabad493c51ec22d1fe75d04fb988c79ac92afb4209d3c5ae9f7af5c4a990b736d69ac5f5cbf41e08c39b202cb7a39e4fcc12cd06bc965dc7f49cf988346b56cab96708af565ca7856882e040ab8fb8aa9a883767c19c0c87b14f5d3669c736455f727473eebbc85985c4a02cfab831d62740d17c509291f3d7e0b619e76f420e106e3049d05cc202389c62023fc8f28646ff7a6f1d3f5b11cc433d9164d21de28d292838ad500f1c0dfd20a4883abcd3162028b610004371ea46f31b780dc507afcc23ec2ccbb2e6ea6690fd4674067b2d7c3529d41f341c774897d1ab04a025a9a36957bc99b1fe7670204b90012d17a66ecf0b001f9d24a904f4ff6bfae8264bc1039b5d9a9ed4c66200a7808b217f179e9d057bb4788437e673dd20ffacd9b56f3d5d912b65e46a64e0349f364309a5c5ba9a195f3cd820f312ae056e2f337697f5a9e4c024499b2d5028c8e239fa0d87e94d7cf1c97bb0eb01db133ebda6a68db37872de3f91bc7330cbab427ff47c0bbc972bd98793b105d18dd7c77ab99478d8a65d0e027095ccb0895499b42c2f61befd9de439f5636dd652ac98481a7b7d76e7d8200130da0b0493541c2ad819c1f079e3ff7cb515800fb5363ec02cd1ab8712f9597ce05485a0bd9e22c3d8f275d4b6df56f84953a9de4981d6255d627c116f3e36897cd81e50f6fadd9b7090780aa65d2ec4b289dc72ee3bdb261e20396cb1f633dedaea2e001bc7406f8581ee7c1d7a5a5635d98bbfa5a3530804aedc9d7b0584a1c8cc8f60bf2157afa881a9155e01c2312c32fb978e6d5a8092952db2c778f853fc983fd0d1c04286eff396fdcda77ef6be7bf1bbeeee05bac7acc990d12bc3f73c139dd0b36ff7ff22286b6df14fd71ef2b727e528c1ca4d70a03c2daafce1999add69c03c6f2d704423234342d05dfbac07f59fb1f489a96153b51b9a86e7aba0e8dd00fd561ba507f8e30906e00f7ef784be535367b9d5f39d77c390b1b81474713bb0ab53e5ab0cebcd180b6e61c5418b0c23941c993ee719a515c6d95eec4b43aa70f32732a3450e7553c2046d1b12f4e793ce24ff6fe4dd21d78ba812ab20e7a17088b885f5176a6f0a6effa1b1b0f52e53582bd73386bf049abc84573a2e74d0402d72915b44da203e4285329b0558aec42747a7b8d68a252292ea40bccd13a280a727dc7f91d7a9b20b843676b079f5cb26ab7dbe8ab2e173d517e7fd4e9ee100372cbe0298b743c9e2c56db7f407d7715318d324e9b908c177e623a4a5003540c89f8f346afdfede620ed6d71d750c047e39acb6bec3baab09425e08560e9d80648110834f90b1fa1cd72bb4e30af0a588bfc520bc91377a5874a3fc8243bf20eb285ab07f2427cac86a03fbc12b734d65dbd755bd0dd6aa870f4baf545e4fc0b1e7246622860c833627130564a3367ce0ce286f7697d0162044f3e7037f6b7047fb9b25bce147f6f0ab03dac39be55f5bc3ad3c9e93f1b4e6fb6ded6b62aef0d69f94e122258bf351acf515d633f45a6ed25f035e447808007a6a3bb471198071533de3bea8cab7374b9a963255bf66a85ba8dc8c8d732485fe2d8533d9bcd01f5c8c5c119dedf491ec709af7d7a2f407d8558decc591509c5842ae9ec0ebe0d8fcc526bf78b341d938aa29425eb1eb86cf204ab413f2ca037c2a463ab806b0b1433ea9ab20b711b7653f308c8ce0ac22b89d8ff78b6810dfe5e4ac59de5ae0dd458bce3214c5b25fe1ef8bd243085c1fafb7638ff7fbb3616ae5761f13a39022ac024e55f7144f85652336407d8ede0a31a38da557cb4c36f2bc9a50993060893c5eb51570800b78e05b73ad093f24c5ef0fc0d4dd378c2cf83510f35c74d0187b2d7af9707cac5b3ceb05d3e270d60cd560dffff295eed14d123067bd3520688ff31a1050241e13080bf9b9cdcbef68b890c0ebf02b04414a1c3b7c8519904cf65cc96f09fd1e6f414be9fbc9966e5072a97cd42de722a0bc9fed643f247010e573b1917dc1ed999aaedae33f3c4d0310c69fa5ec686f2dd905b8fa0565e03ab443d38770cd06fd9310a1ccd413041245369acf0b4258b3c9aabb3dfcf0000adbe6454cd78a5421ef330ecb6378d312a72d2ff8bd788bd6a0b3937b93b4c02be50f0018ce3d6719bdbff89d9ee7eadb94e2157ebbf0b8cfbb40656aaca160e889a0af13fa4e7b806e4448192b3e5303a003e3fd0d988ca9af2f0e80791a704362d5e9a3e69bcbfded6a628aa3ac050234e831af5932a0373ecca00d3d65f0d0020f6061900558cf3101722d98d8e9e1b09bb7e1fd696c6ce21ab7d6b659904 false -check_ring_signature a34d4ecdc8e1babe4686bf0ea6479df368d97cc093af8d3347091d4c049f9bdd 2043090ceb976ce503489e6043d73b0dabf89d5aa90d80eccb6d5f8747839ac8 2 dbd8675f8fac5cfd49bfe50eb237f8bb8a4c2411e9afc2013728bb7cfafcc4ab 6d1cf784269f656cf803e452d82b93c567022df36c5517b1f52cdcd93833344f d1ec8495236835587a9baae0a4a16c48a5141e0e843577654e563eeae077900c2ae35337bb8835dd8dc4393b7d0661cba06d5f540faa0104981026a54092bc0eb80e68715a00eb0285317d874c00c871e526319a9c89529565120b9f774cec0e71f97cfed3df6c22ddb621dfab50b3de153b1c2c9dade791e42b1d8e1735d301 true -check_ring_signature 22698321ca4ed8b8b64f711ed0653be98fad4d3a311c12f55e3db8a5fb5da0a6 863b21fa514a05cee56c87fa15aac86b6890c72c0f8638558d3509f12f4f11c6 2 594d0d852a015f04320178e9ecac6f1103da6b686cc34ce221af6c11cf88fd29 631213a7ebb9357b0342bdb05d7b4612096e9925bedf87da92e228a68398d296 266038a56a5b08607da0946ec5d306a76985c10761d1be70f61ce23232b7d9051d588371cc6deae7a0f24be635a2413e8c8bdded6f48df1e78de36394be6ef3bd7f9acb86749ac27536830576039472647fed813180b442adf1237c3183ea70e2d8139beea5af694e7b9d1ecadb50d2c26c4912d19a0be99fb845c796d056b09 false -check_ring_signature d2ed3232385a77c538df5fcb9f173dcc6199985ef773c89ecb32529212193056 e6278c1e99f6d3a697da69f762a0cffea387aeb037bda85695f25858d6cc12bd 10 bd7335b6d36fc678712ee5c7a0cbf2c97240704349540b734be2e739b9d0cb5e 7111a1b778b46bf3a8a9237f1fdcc60c7c73792c15b6ae1b4c316e5e57ae3485 d61426813f9c92118c7eae32bd68c8d1a9484d3e5329425c258e9554013dbd04 9251b83b620863fa7d14f3a0eb792217e66ea7a09db9d0e55a120ea464a49982 c2afbfd41ce915ee0c061ba782423d6e5b6f2f4a8db4ec3dae6154c111402573 8ecb189940eb05ab960c629d111980fa1b389c13ff6f580cc98745f0a77ca3bd 0dce10904d217bb06bbc8805422ea6a15e2be1aa473f870d4a5ff9eb475bd07e 6cf15f79edcba96900fb19e997e7d4d831b87d1f24b246d7c35d0781b68a5db1 0e031963c581a0f8e890ab793b9bc0a249bc64659b491c3603815d580d7d6932 ce481571e0c42968f50f5163581729a3109c5032d401ef2a64cae0ed85c4a769 829b21459f55533e0d55c4d1ddaa2d7b2e4b0b3c38172a807677f38fef488c05f821e8465543dcfcc273249d5b9dc04124338817b5fa01576f0c7a26b0049606299bb9814acb4dded1d78b23f21d03d3dcf00da507885f2cecf85dbe39eb5801ea87ed208c2eb159e83a3c6a11883c65bcc5f1c5a1a015760ef75b65bdf4810fc96dfad6134249ec0799b4b7f175654eaa208bebbdaff5e7846929af49189e01c4872737223fd0f7c83b8bc6d0cb405873d352b95013d7915845277665483b0f004e709e39442be9ea2e44f14ba2af757b68f1c0e8d1842ead3747d6b17dd3096d214f0292904a510c89c20c8720d2489c58fcde18f5a68f4f18339a9d988a0ace3a49818529562d3e838810384caa2d62a8eca3ec22cc9cbf8df21a8e5f2405f98e02a15fc52ce36f335700a2da6322f99ff9db9a64a046348210f3d22ce103af92d11d58971c1366f4cea065a3fb8ffa9e1b3e0ab6da5ff83c516e0b025608170ff9f780337bbfdca6ac3796aa7a4793f047407bc6e3c203f795a39fc0a701e5f8cf5fd6f1bc2d7bf5a98bc343d44963b11919b3ffe9dfaebaa9fcb7887e0baaa82e80bfc7cea06baa1ba90d545f715f9cb88f0119fc1fd1ef77aba09b840e441479b16ccb72df7b6c8b713d03ce34a52cf054699249bd4a76ea0e400ef30bdaacfeba9ce026a4e7edeea9580cd42350d80cf01105020ec93f63af60aef70d40a88435e53c3c1c65238bf9cb8a982b3bd2ee29f2e5d8fc0dc23dba24d94408418cb4ce400b738c7d1c8e9a488e22d54360a7788f614ba02b8b07331ee1c1053b613d219e519b8135549d9f9d913f6822f1c2793998f807f62652edf0d614048651868ac31caefef9c15fb121f8e8d5368e9c313d8dda1445e8f7cda9a9cf04 false -check_ring_signature fb25495cd7bc746e722363a94bc7755234cfe5bcc53e54308053762dc6dffc12 29fa0d1d400c0e88cb0b25f045296de9b2957e98be9f7345f053183cd8aa3c7c 28 449bd90c98576f745d432525a9d69b50b321d07b236183d5f375d9b60df1f2d4 27e671824fa4acf215c6e6d040129d49ee87051d4de922b552f145703e660277 2384ff31a1d74a5bab76bc1dc7c81be48cffb419cb2f7649b66a1f83863ec871 905d36501cfca32191121ea67b38e35ea1549d11981631567736c2ca22429090 6118d974054b72e4cbfcf44cca33296f02d533c982b62603eed0db097f920f9e 352bb13ac320d1c81b339f9d71924a859c8ed190fda41cd214c0e638acd88537 1aee7d5cdaa1399b30e25aadbd40e76b490d59899e40b6b5e9c79729a63e50f9 b233800de7977575f5fe50c4adfb0dae8673678960d8b1774dab45487ab5675c 6d604daa73a60943daee6f5ed2ecbe7583fe1dc7b34ca5ab207fe9ac0f132830 6307c7e869f138fe4661ab27e2b41eeb8d8496be0c06298c0596620e17a41593 e08f4cf1c6ece6736b69f7f175ebd1d9c8d07d7c17e6f003d742b47ea2f26a9b c7209d4601f717cba273b8dfde0123134499e72be0b46c03838e0ac9b926b391 ed245d0c073726e4b6dc39de59883d06acd471e3aac75396fadd778af8305086 b3e4dbda62a1f563afe7800b1014f6e8c00a2c02c096695653be91b2c59aaf1c 5d06006889fd560f3b2535fa87962c67c00f956414b61a72e98f56274769806f 873dbf0e0297914c0b77fa299846abaf2fc413de72d259c38d35b57bdcf4efd6 e7064addb3b9b0884667d732a18aaa3b5adc231f255b06ba7d582893a5be8940 2b4f52b0834e483dddc096791b4916c602947c69ae4c644656a0306ed3620b24 7d5c604c716b997e453c93f722105386b411e0883d761857cb9b7c315f0a7295 4edef14116241009ec89bfe99e69a039851391204400dafbb94923f3eda67a55 d9dcbf42a1171dde6204fa51ba48a48e95949ef5d3e1f37f12be641f61f5948a 3eb64a4660ead5c2b8ab0b9961df945c3df986ffc6a590ac574c758b9218a1d0 17a4a8cc5ed9080f9adc5c0f0bc15ebc2d9818a447c5b2aae233cde1c28cebee a1198518ecaab28929fce771e54977153014fe6c8efde1c79630a5be779c096f 394ac37fa5cc430d85d8ae7255682a4264f8412fe04e7368c30edc04b8b3be7b 34875013ea6ba98860fd071b1be944ece3c255914b434298ab30253065e48a18 e13daa1f86ea4cf8ddb3e563de4dc9ffbcecfa956d2a8c0b97ff33405c46b5a7 62c7fa0235034bcbb1d3880806096b87144b8cd3ba7de1423a689659ddb9b90e 43dac9feb7fd5f6cc5bab86c2ec143eca4706d5ff9601ec99f3226afde3f680bd82da235d5d53d92c3dddcb5a3f183615bda20e593bad346ade8e74a50461a02172c3f9fe8b9ef06b84bd322e67640b33bf233d4249ba204fce499e8d27f85001c2f709a3c9b539441316b1e88028da84c4cf2d1dbf76c2e149acf374adebb01a7b9444d3b6882a40fd9a5f9619ff20f7accd71010ada71d6a46e49452b2640265957cd29fe04d67f444a40bb3881fb344cb18fccc6336aff8dff8dbfb1abf0424072658106a97535112ebae7ee1f15b12765a76ce9656562b837946c5acda074aba96ee747fdaa1b927e0416c65883d550ee1013374748cbfcb0316ef240b0838d91192c700826be586799c6657a96538b66faa211b5c0758fa85b9d7fcac002ee856f132def97970d49bff823b0a1a3f60d06ba2334da67dd7b08f670a97044180e545a7b2057339921f4fa269eff176de07861e333a2a1dbfc2276a7a4b08ebcc9cac8695f8cb2e27cb8eb954dba4549a72cb0e42a24680b5661a30ff6c0760be2a2d4ec1990c0ef543cdb639464e5ea4b2a4512161a7980d088d68212600eeee06358bfb9a39981a74a1413cf67a3eda8da416114e74e4576d19a113dd091ec2c8ed1ae884fc87c86467ff1601bf701615e1d537ae0eb496daf4f4ea4705585e5b97e3b45a14f5b773af2b9282b5edd26b26f2e7fb0c42b8b6b3390f3302b454d3fdce5396df23a655079e768c7d75e7b9837457c1d093089f3e29b2600eb55b2fc648d2c62b347ae3179102ece2769b9c11cd4bbe9dea246aa1e7b8ad0cc17490cf61d8fae308252ed2932d3dac925fec340c87daf6b0a23d26b7705c040c53102cfcd8cece6e9a121ef197bbcbdb657a8af401e413c35e804df0840b09dd8d920568e62e79773af1493c7d66d4d943953c111e597006c9ff61cefe320fd12ef359b464d8c1d53f42fa06a8e6dcf3b1bc8d3c4c497b0f768f0406237403e21176c636d213c62eb4f25eef171a0a6bf2706490e5363146c5e77a202cf101e16831d0e1b567a2eee46497fdf06d9c0b481b638a2eaf1ca79cbcea67d8980a312528ef2b9ddc5d3ba965907ff479e6697ef0430ba33585ed242031ce29c20e19de399e82e1393fed8695002202fac7fb2905c7e1d58914874c74d76f015a06997d01f7a82bd1700881247e2538570ede2e5fa3b704a0a91d0664c3323ccb0b791203bccd3f643532af80b0252a01b2b58f94dd7eb608c5ef69a61b89a14b092f59a0451dcef5fda6d13cb2037961a2665a764e3fc17884e1f285917655470cb1f820de29e8cf979bd405eada36960d361de7bad83e759095b2b2406e870c0ddb4fc24f8391c23383dbf163f8d9024b0b8e8c058f53af696171957de562c00857bae0e6f4077a602e41f354cfe98d0c819356326de9b51294efaff59f82fd01f7dfe7628cef458087150246735fbf0566796656e73b5612d6b5e8d233fa590d45958d216dcc72b53bd0d73f6199619bf5f2b5fa633f5917418a9ba9a9e3d40de10e9df7ee8023459df1451708f9fc7d5f57f7a11331ed57f4617fd96de1cb0304053b8dd607cdb471454d36ccd79c3653175f7bc09023208f5d86924364ef0c6c2b74e727b2e6b273174d5b330d41621a52eaa8461a365d70058990d9bb5505e72e71d50be33afb8d90cb9234833388bd43990a441494f2a75a9944e94f0f0614195006733644252a82172c864c67cd62d853107afbd3ca3d9f575f293d4f09b4c0399ec70368cc48192cf2d07135051bb78d9b6f77305097f68a8a1097e502c9dfa01c0760e0f6143b9f0a0adaec89fbedfb41b86556b9a35e62e000effe0c59a4d621e6d629c71fa9eb2673184ff15e3470ff102e152df5c3cf1067d3b504295a4ee58cf8fd6396f0bf2ced41c3efec712694a57c01cecf3ad094d0e11c09e02f610cf73558a7b3140ae04434e9b97d96be802e2152bdd9baa9536b4c9f0962a07b37c5a3a7f790e8eb4e3801eb2448c1053a71bbadc6ddc3dab0629249063fa3cfd6ed8bb07c78d59048a35f46ebe09407e952401b485b9fe81ee2362c0e2bfe385c2f4c2ff856837c627b5fa72f1a2c4af5c5d5ee5429c02ad72894f20426d068484867a0089c5ec6413f9d73d1ea68834494db4b3ba2c0c73024b57e02ea3915d6a623df443c83e2de63a90704c78eb96f8f88694cf95455944fa79208453a674135c41414624b5e499ea23750270b690c452b83a3915b9d436f4d3404b65346a7c2a13deef766020c9e2af129ad12772521a4148d65c17a1970969701d2e38255068db6e319285461e831737fff5a32f106522514094402a27a474804dcb9de5a2a582cb31a5926bf5670c0fc84f8181ae2de7ddcc1aa0354770047053f1c0e2750b1da2b135bee07ed5a1196c705a0870ee2a538db6ca9c669cac0026a164d2f0a120d7e906a259b01c65b8e9b2a646eba35f99c902e4ad84fcfb100bf633eba55dba98b654c86abe85a10a801258791959ee66024f13973aa93bd0d true -check_ring_signature 213a4a09ccf4e26a277dca0441e05474c457991e82bb1643d2607bf01e39d57b 3f1423b1973955adc5a7de731f2f62690d72414e0fa937b50391cf48d95a131f 6 2453345d568af45ac2f6251ba0fd8eab60591f4f6d28a2a74c50d575ae275721 b111440c1c2a6eee772b3484878976d8bd1e0d41505e152a0e18e452cfea7ee7 0702d7b0a8b04567ee507de9e2c6787f45a0ca481fd813bdb1a5a1293eafd484 e5976d2a0385ff0e91ac45e1518afef944bd96e787f4a3e92e56d67b08b9968b ad5e98c23d41629dfdcebdc02a86a7e27dd368386f819269f7bc874cc80a5934 f2f513c6a4e1409faa698c0ab55cba8b50c39ec0f7c2642a76a487b6b2827b09 7d7391fa901d42e9308dcf11d2e930a65f8857e5dfc5596cd28ae0b374caf80116ab587732b8fe3998b4d95390c6f927fcc2ceeaa91a133d28f94bc3b9f4c30896c0945008fe7a718c3f644e5a71adccef9a013a59f4b658cbee214926917b0965b486d04716e9f8c4920d829c2328b0524dca7af9e1edbd55cca9ae8abe4506b2c21e0280dfafa63e799ca8081db89a51f4930afc392e11eb28730bf1b9d40c8d8d220f468d099809a537a4cb154ae3069499a3fedf7566373c7cb5296d20095b8220d78b59a190f91a26917c587b61735df387932fa76c5b03faeaef67e00a61f87f9a656f9fe42b4398e33494773c5dd4012b79584b189619d73f642c5409bd85b5487c6ee8dc82ca2d8677afb8a48e8a9bc5f5702add27d3c8ef1b0600065452b2c6b643be0935ca244dc79f8af85cb4fe0769e309284f935e85abe68c07f5b0c5b7fab8d5b53bc753b9ac5986551af413b27ed8783567e77a42a5daa50a6b5f1593e573b57bd676d0dbeaf2fcdbbbc107d02c093f4670724bf49b55a90c false -check_ring_signature 8f3577a9b1840e5016b01f2f7cbd012e6ec5d5d15b5875439f251223291706fe e206306245df38ed595934f44bc9653003329f9b393e6f073fcb6bf5de2c39a4 50 736ee6b905af8802ec4bd027002bc7d900354561fa9da8ef11b3d1b7346fe572 64e1d40d4d57345cb1263aa2350b381cc45d1380de7786b679a015ec1e3db052 0286755fbc759e00e2c2da5a52ed53202ca3040724affbb0a3069fc437bf5fd5 b477f6a73a6aa2bd8fa61660302aa0fdfd110db793c10c7c253b877d7ea90b40 9e5b376dd3e072ae25f78b5084c3dc6f580304bb97174431ac6ffc131a1860ff b631ebf02c7a7d2e21fbf7cb2c50156a8d97b91b55e0a9d11958636dccdf24a4 f8b37829f6bd0cb717f7f3db741c93ab217ae9949bf63082603a4e72db468530 32f8b1fcf5c904d0d98dba3dae1908eb88f609cb83da66ec4a583d8ab751ab5e af057b4a3afe6670f5d266a96f23afd815ea3936068ada1a9db9a93fdbf8714e 9b7cf6cf3c04078f6f489c44f5081113296c404c270e6730e7936c9c33ba6ffa b7f27f5a632d5e9708625ca2ab95b2f81f757f2fe783ad76cbfeacef1c5fb5b4 a817701c62641a169ff96da42c4c9780aebd6a36734e5869aa5aafd3abbf894c 401d26ddb1db7675cb804d45d36e86781549c443c4e5818d8fef3d3394b39488 4d07c43cefc1c7ece277bf24d805dacf2ac695a653d48e2fe23a83be506d810f b41a6961374a96822987b35de9abcc6b7501a3a0eb2c922fba96e118feffde96 70365b0874ecac4e874d03c587e08758cd1ea2769de0479c280f94d3745ba327 c94040939196e6061c5e98ef59493101a5cc9f83cd5af2d2125818332c662ac1 f30798b241fab898303a3c14cc55077ef2c715c6025969a1d48874a5cd59c793 25cd92efca389e49832c5a6f54fa19441eb1ee569af9567719e673be3d072e00 36f7c45d91dc3a0aa932935d30d4960820a6a90b4ecd5e358aa9e6404a5e1d0c dd953ac9f4188e0904a40363d3160a8f6575b2ea137df6061c76d7d933865d93 e72c5b438f54ed635e72bf9b7199af411ccc4f8c9dc6487fc7ec2c9264b1ad9b b04ddd229a4f51ce7aa21a393ec9c29edf98b044aa511f603b7f7283b165653a 42a08ca5f15e592a9605221fa1965932e4ec7b3530cc553a35c8f51c1f96f737 04f3c5867b2037f8672267d09c5a6d91cc836fcc3d237c60f100fe737a5d78d9 7396b91648f6c0c9b76049e50a754a1a74563623fe2305ae948f4c12a9a60806 a4b1f9b4e9b240c0121139a62713daf849859f8fc44aabb960038818dab8f2bd 16ecca64e33079a76850f8d8d5887ff3e7e81fd9f968fb76b420348a171b8f47 503b88c56c63e129f343213f95b83f529c853b5491e34f0752460ff401559e3b 6e6e353f0fcd4871984a59ad03ea3be18e8c7f7cd6c90fc1ed607770221ab57d 6e59cbef80993f9421ddf30fe1ff370026899aacf940a1785517470367f97a6a 313851f77493f9ffbefae91d5f62ed503c0472e6631df0cf0fb0c88ee2cc9678 8248ce77f1553dee47cf8745e7190652cc84fdeb8f7a3652aac4d98d114a445b 46848bfbd9cc4d8521591434e8942e55be720f927c468b26a3329b801cee42ab 83916ee6b530be39c6e9ceebdbb3f3ebfa35e33ee7c8c798217ee9b412d06fd5 ce96aa34cd0a7303372a82e3f3b54b3c87913f8f1aa5a82f546d13c5bfa9c7c5 71564cb932a57ea48ec44c851498a80805105eef87bd96c2067ae295e7090d72 20675fd78c3aa597130fa589b1640d62a3f3dbf133dcec1f3456ade9a1a6b317 539e4f412fdf3b23c8030527ddd30850a60224b4ba9e0099151d3eddfb90874f a12ab8155f57d3e087c5464b601a9d1afe9c981fd1cf34714f957931303c8f71 5a9800dbc0e086c7a9a813e5476403ba83e4d5314ee2778949b600ef34f183a3 3e6d0b887e98cec6b1b433ef7a368d1824bdff08dfb7dd304a333025500080fd c6c2806e82d8d1e7dd03b78ca99171e41d6439cac37e207108706aa1f2460a6e 3dffe53fd420d14f5e8e37f73d02f1d67d94fc8620f5233a39e142243d56d12e b7586bcbc12753e8fe73f68a8d21e061d5378ddf619aadf5da37fdd6e21b2a1e 8bef88dd9cd784506bae8c9efb95ded7f08cc454a8ea3cc34d90a6b12eb73273 6b171bae511e5b4c1514794205b44d7e6eae9491a4d5ac2af4452bda6ac13fa7 9423a7930ff0827923a092b26740b51a9f68dd8ca9085e5bb9595ff2b1df837b fc5fb14cf6902775bf222c77b9709ffcedb0e130eeb2b868c0c71c08de178d00 1a18088e1667acce8c43c6853be66a9317d743f2403652a1076a152fac70892a 23e159d8f2abc693930c726dee81043f31740ae087a91e3a8d81aae54b01ff05766b834c801ab21853fd52aae0616331e8f9841d00746e05da35944948cdaa0b274476199b0a0960391ac0c9c4c407b4011b3d06d4294b2af3e7b090c9fb9b062952c899d42ef5cd3df250e79c13c9352bcbf261df9ff15ebd6bb98a8a8de40670c2ac733bf5431c1fb95436f64813ce9833fa5447d08c9786d64ca2c12ecd06ce869f6bd13a5214be6b0aea75c6d8c04589b5193fb560c20c0d20358499280876c55ce327ff0bcec148de2f4b7efe3c7984458e6a2caa91e1f452141752e90d1bdac4210b74995f0a196548ba244334347d15df94fbd839300ef327d3958a0a72de72f911ae86c4f945412ccd5d4a05cc72279694fb90fcbac76e9963a31a0e0777dcab1d3e87c6e150c54dc4bda72c674a1899b2025d18fc916b7e4352950f33f7132905ede0df8ae399c1912f94bf89b89668f4074500f4741973137d8b090fe20e5342168b1295f43dfac532ba7ea009c7acb387b77c85a9ee10412fc90bc05fe4293da7f774c423d75ec2c92ffd6c19b4eb3ec1c42f79bc6ba38ff54402e3f9da3b8577d9099d7bcfca4332abda9c1a0e094e8fb4669a61e5b2d8e7bd0a737eff85849db814a3081b39c8a0cccbe1fa24376032332f14cce3da14a75a0fe936b46b20cffeaa5287c55d0c63fc64d353555b577a6f06b4d34584ddbbb60d7ca772f28b5db79cc358d4648b10f95e63f190f7f59fc83e46f5c5b0717f3500f46c05de6c4500bf3882855e343c8d9f35350689d803654cb2871368aefa8001c6d27d94207d53604653ace6404a9b84994f6392bb157dfd96f9b9cdb5abaf04f6df710f3d0d58951bdfdaa3264d4e035979c04e1d8e45775919155138d5180e097b4b1ae14f73960d8b0d7f7f65a16e466b1af0aea0c306a54861c6de1f6f0482d1e56439cce604b8936bcde45fdbff9d4e280beed42719f82084dea7525c0ab8a19392158c302fe24923513c108c5ea26de399c93517e23c134e33c44b990218b908b001fcafd7010bd661a66da805d015ed35df69223aa4c62441dab3510242df0921146547443ebef6aedc5701f6582559de852096460f75f16e9ac17f0f7c322acdab832c087a58a4ab0b6841812926d9acbf0db7eeb84ab8c6922ba4021aab5874645d8f52c7e794ef56a619596038ca734ac8795f538949b695979a0ffd196f1c678509194ddde038ad8f1b9049008f0eef7c4c97dbb0e47f9b07eb09e686814c677750958dd505998e90be038d88eaba2c878079e10b559364cb120c6a22f2e365a0b717f7f7c7575dfc4c2007307aa951d1a13c13b95e86aa4b3e0870a327003657fe7f0b80fd5e60db7299d035b8337aa9f5d515e97341e0973b0ab984c0b5e01a7124bcb04c8da84972f81240c67a5b93a09028af2e985b64270c6a31cc7e5c398f1a9c94392824fd9c650c6a4feb475e7f1a0d80ef572ed5b70c8f8c7fb37eae5070d2b662cc437284a548dccafe732a9fa63dd7f248a59fa006d6452c0ec5afbc11198de94330672582131289f38de6b2896b25a78cac47fb031d03ffed931827ed09c946d7b85aac4ae3c3619b51091f39c0b83a1046820e02b643d4b31cbc42a7bb84cc63c6aad9732b5e3db88403cb8fa0c2130b90b9dd094be3b671dd7c05daf5cc7f176b80755eb44e7a6a0e2d7700e8b976232c7947091cdc3ba60a7d4093d30a8011f77cc5c793670386b8a2bc036c4da5142a3640089a644ab2dca0c40b1d882d1c5adfc03ea247b1e13f939f2c448786dc06b43c077e0699242ff9d75ad6b4491cd7b657a36104d6c1cfbb9654e1c00d5a11ddfe0aa8d5d8b84c5b66c9437c81c0f9ce098785f62d5d6f2ce85687c02ae5470e720f511f3f8cbe7575925e27d634afd8aab46a33836376c8e3ba0c618ecd07a92a0e7385314441bc0574ca86e5d68b60ec4671c527ba150b477498f0f6725dee6006e1fd1ee148430b1026f8a054882a112bee3b89aa86c88ab96838b962c703710fde47b10fb90445b091541ab62581988736c37be8d6eec31ec5c538e28122eb05927642043fde74239e3298604efccfb5684a063370a1c11cb6881812820f210a6e834f06bab5a18158390f5df24011ade6f345cee584736409cfd865a805e1084326a64b54cb24db9bcacd12537b15afb2376f69f2bc269f582fba4df38c220d929a1cf0109dcbb03d623f88894f643cd71563aa5aaabe4b615c55ca5083270acc8fe58187a096f2ead327af70796898a7cf856dd9ec4981ee78d9219984ed06b89adf1d53caa414cd9cbe326da67f616263e57c8b5ed8d37680824e710969071278c187955bb75baaa176d79b278eeab6648451640c13dcc6afcd013dd8480a8667c89be489d85fc5d89e7e202997394cfa2c455440f3fe3a0ed41df2514902b47ca6f8c1ad2b78447a22b4e6e8b583bfa236697612d8c3bbbbafb71e7ebd0198883e3dd058d1565d23879e6fb1cc77fdef5ebc19ede5e0ff208ea0e9b3080606eae79c10aea0b7c9de51a3e6ad45dcf01e2e58d993d4139b0ae8906e787c0e44e820ec610a3b797616cfc1ef6a25ab834594183108b30ba660acf1687c4a0c98f54f579d0ec6afbf858f49ab3fa2ba3288937f592cd83bbeb217cb54c1e6022b0df7419564b9ef606ecdf6ae69b22e4c46263411ec903fd13b02d781c49f00084061f3cf8243ec97f2fcf965d1cf371352f51591eac8c064efd815b555500e734867854820fd0ad57f8d54db9cefe348084df1345264dcafd6bf40c2677c04e7ec4499e07706934b5abde28396af7f61ef428a90c93ee96ccda2ac5a208c0d5f45be57d6f18d87c5c28eedd26c0613c4af977559856c45c28ef3b0eb25f40aac20fd8c89ae9ba847604d9233ed8eaaa549c7724d7eca1d69f448e27a90070da39c89d94c4c115b5a695ed1025f8e0859f1d0db46c4aacfa4029a87a6030609623be04a19798c03b5ed09a3bad57c71998e6dbfd20ede93482dd0bd27a088030d2437f8b5f5d2b26d7e98beeba3b2295b8560c974ada1e403280926c2e8a20d28d6a14a57c46a63ac3a5627bc365b1c3c6a1c7f28de63dc38c9047d58de4102d40cb25ebc0e5d3973a77766ba1f0fd6458fc832e62479cda182a45993f9db0ec5e743ab1fe3fd67a9c2c1ecb94396fe079de1d47b6721676de7ee442cf34f07b6f0e15a3994aee3357c04f216fb721ac4e22018a70941aab7f6fbe9d8d37f0a5af184cbdf6d9438d6c14d15afac74f846187f722a1ae9eb81667563c499ec05b6d24ccd1875156595b150864796cc87d350b68fe8e946e4071a7d9f5d0feb0a6eb16b3c12471d8c5c5e821c19da601c24691e055a5e04c53a51c6b4f4a61d097340619d8e9600367ce4e78b65ed9b42478d3674bfa3660f9561b8fd4e460b0590fc25b649b1a90d11b4daa651a8cbf33773785030e72294a73534f052fe0f09aa8e8c83051a4196c0e4e1f737aaa8ec274e77d03c81882451e0dc8780548c00dcfc7c6c9fd3692de76f1ffd481489f2fa8dc90a5c263dfb59d0e565b2aacc0b07c574397f3994e31d805a8f0c883dd210e6132e4327e59575014dc9ca03e20f64a6f3847b2ee40430cf020222e72d98a473bf2d8ec4d5cd3ee48082adc422084857fe444c94c049754f7ccca88b149bf951c0e5bb0508438e3209c7036c3f026550b02956f66b4ffb2c1e3c5d1c8036bec70ab82ded6748d74b5acc5be0280283c242b5b702827c4aef08bcf7fc1ad438cbc08ab0d06f901ef451642a62030cb1a3ff67a50447b0e82febb9bfd1105ab780e8fc78029ddaa5302eefb462d80e429a18e2a8019fa7f63bcf3dbefc7316e923015251580543d7196f733195dd042b3485ce315d4f0cbab9443eb79bc6108eee7aa48222ee6ef93aed471fa2120c25f3f41bb20b2638545ec69c771c5df1704a6dd1339914ad4d5d0faaaa4de803f7fc9824e557f59ccaf6305dc04776d302b55303476b68eab582a4673b05da0c2e0be76e68f4ffaf5a940c9840fd855472b13106635e6b8748c33c4f8e791d08051bc06098208828033c4866f3ff071ae8c20a07067efcb3051b153b00af7f0f7a6cc1c4cd5112bde220bf133b0dc58c569835186fd50afa8dcbd983ad6be205fbab2fc9c9253e392f012ff4bfde03620711cf3ac1db23c745c651af35573b08d41185d5402dd44471f133d030e1bdb0b8abdd3eb3024f720c797e1446f5f30437648df7fd0dd1bc095e04a608b1050ddf881516aafdae886509b558ebbdfb0dd295127b3b8e954c50b67aac53700bf6178970a91c4804d5bc63416aff14890dfd25da871e2672112427e660ea541313be56875c80fa1da323f10c862310ba03fa9945abc93ab21590fd5f9d5fd053f244912755d0e73cf45b9dcfe0b7688d03bcf1c884a2e10db4dc7dad3d483e8f1e8aedd0c1f7d33888e4f8324fc2adae09150deaa14f556dcdda06f2c2fcb2423971e33d17b5233e1892edd53da3821b02 false -check_ring_signature 788c75491398bf194e827f7ca4b84ac7a6bbfcf80f238818874d3184bca35732 3ca321fbe6607981314b41b36e335c27d09baf485b1632e57b9b05877306738d 24 409a19cff6af31416fea0c442af64a14c4092cca91328ad72b031b9490ef0477 c8a5e8f9d18950f56b6a3231659e9ce72f319910475b83b4bef3725b4c586b68 3cf31884b6acc44069c91d99c41cc880d253a1854eb658361122efddb6306cf8 8f5813d81c2d56d311e0a7d7fadd1044d8638e12cb9515f4ade71cbd44c35fee 77b0eafc1e107202abcd9ca06bc3cf024e034c06715410fa9593160f013242e8 b58f05612c74cf562853967e07db67f1b4e7b7d00edbb08bffe8a2498dc2c540 6f184896c52c796479c16aa14515c50c773d2186f9a85899a88d94023670c90b 7812572f475a851b0c5c21fd7ef5bcaea9e1bbb7eb38e3eeecac2b7babb735ee 59fb5e1ed9e5aa238b925bc643e3c11b2ec4a0e579d31c3c2760989fd3a8c32f 7447dd26b1dde04959bdca31502dc508734d3b6bbc371f69ddc900d54e32e47c 87928ffabef021bce1db9dc6618d12579ec9e6dac6f0fef63f6d6a4907ad3cfd df95a4b26720cca791b2e42b9d57748004bd311f874e9f115afc99a4eaee4a62 f786e28e546e207f6fb4ec538c75d819d831ceb778882f42d30d8e277c3441f5 891861dc5ceb2ecd926d14e9b6e9b86d8786d8a7112bad6f1be6d6303de82d4c f3d670b1138fc4624ac0e6d24a9fcaf393c2b40a36a4e2880a57b80653c85262 93e029ee7dbf126bcb5d1aeff7a0522ac191853669d4ce285b790480532a5f23 97e46e5ac688a47be4c86e78087dcdc5a061a33e6962cedb57c3699e3d53470c c537bdd75f228447115a7ac27caa8fcd50250da4e34a84725a202ea8de2a6549 c9f0b6aadbe4ec9b23e17623b4ea25e99f6825c883a7af338db0df7a9615e3ee 7d5d54034dae1a2cfeef3ca7ae9b97662df491b4fea93d14d5adf07baca7a663 513cc1b1b84bf36b35ef30ada5ad1dc209825a06a2459aa8e81bf4c2c5c39af8 aac5f9e167191770619c78443bf83b529f8b38c81e4f14fdb4e6102e5de4de24 c43cee65ae02e871e7e9d53e5726905fc140163734b9e20aa6f982f3de71bb14 c9b04db0f5056f2886c52bdf39e027d48c0de8f4a70b6efdbc70d7028679439f 8ff258aa72790a9babe7e9c91ed1dddef098e79f2871373e322ded146c6e59019a772a8233b76da8a65e5284b51466651cfe31cad6d1a99caf57c15f8742fb0920d82f648ec14e915e9eed644b6962f49a0ae207048cb2b6bc01ed6c10822a0b02af8d45ffad2c2ead97384f5f56e92ff60a52940055dd1f0d32725f69caaa001d76b9f8b3e08dbd4fd8838a3c14f0fd1d1a2d550f1b5f6db2357e2480cd370722231774ff9183ace0572b29374f1f3a820100fcebeda4e6f7b7a470117f630bf492785bb431d976a1b2cb5067256c3764929141cb4b43b018ecb2b501b70209c5ddacd57cc73e98e7b942a3ccd17d3d1e64ea8e24e670aa2af197af9bc3f602cb125a4c0acf6670e675b88324bd4de515c32280d6824f27ec260524ddc56b051f10a028228a99b0e29da3ddf408f4d2924fb404dbdc7541600cd47388ead605b1590daef56c2594ae7855e6473f481b4ebbd6d7765d83043fbc42ca5a6ab30242d37803354b47ba26282cf2be77b6e09b90d82415d8528899896589959666049ecfce0d5ad46819833121089dc19978f3dbc9ac9d6fe90936d1316bea7f210f01fca5b1ade5ab27d142e6fa302e1b0401587eef871947bc56a646398e194f0e2a289f1c4351324ebd36cf5bc6f7f6786d548d7822e9b998b0951e30dfa6740bdd7f4927241be9d4b79d05cc98103e12b81a1774324c60c99abe8e1526d3b106dfaec4c562cf5ca5bcbe47899689d4858fa991a7c51c798f41d7bfa6941446055c7128634cd649e959f264bf864fd510384e6e65b61125f4f0e8882e3c0b020676b74967709ad88fdfe10d89eef77cf25fe9bbcebd1dfde91b0d5ee5c127290cc27db821d0fd5ea341e882a06ca62c974ba57bd4149c61163710e03b86aa820cd6af374c35d86442ac40f923a455fe346de6f731d75204730b05d15ec7a7130a9aaf9607c742c741604a80cf99e7d3cd12e434a2f2d642f7a0daeb894fefdd0631c92c89ae51a9c5be9287d085c6a696ee03fbed59c027e57e6bf81f819a0e07a1085a858a562b9f4899a2747874a4eada11644618d84694488e3a3cb45a1101b819fbbe987c3ab091bda70b8707eb4d998181af55cc4cfd4da0fc6d9fc7af078e7a7d06c5eaeec23ac3c755e7140b412a0c7959ddefc34e00a3b3b030c015069d8c8a955953f2270d0f561cf7f7155ca71093dc37293de01af05cab3925460e942719627dbd6cff648e044e6c3bc39a99d34a0f7e34b6ce6c163f9ac89f520869f833b8beebccff55b52ac69d1e27daa04f7019f2ff09d5454e7503acd89706e551d56ca1c2a5b649acd7aa62fe34b594984aff9e54a13259c0af7374a5840cd25738d32bdb1701cdbe4787b21f75213df4d617f7baff35794ed9c0b55656020eabf3c5a73a74dcbac5d4ba3c911cc5318523cd5548abf8a59ff4138e2a7c06727c0cd95a563d2df278cb286fc6268da655f99211b41254ec4c86f9600b4d018ef62671373a8e9e0d65f0d228ada03d38c5869d1fab014cb0acbad4c72f94086e4da915f19a35900fcd536fe8cfec440b544f1bcfc14b1bb98c28d91d5d580aa0b9b1885d739dcffeaffd7980e65a27aa4339b805d0ec1a9d04815834df180675ac8bdbbef284a674381ee8494040520637bccab23850090b96d521b83abd06cbf901e90ee41e7fe8a1aab075a8f803334a635fe27fc6a2f4f8a7f977848609a707f8a7951a0cd9175af3caeda705e46735f9de7a7e4a099946d11289a0a40162798da2490e916c798b9861246321ecc1ec91b44ef422a78e7880787fac8f0f8e7ba1dbacad4901fd69984f933188fe689b9e4b6df85087a433ff9706d34204d22319792958741e469e75a41faa5e781acd9c94ac4aaa7f68ee59fc06598003b4e0dfcdb20e7199d397b304a20a02bc1471437e4bf389ae43ad3ac5cacb9903ee7e312c87d325c727b80d693b70c9605c1a9b0d6be66d6aeb8f18db664138023edbea7d73a349b1c4d070c257c1057d1773ebac5e1fa1e98305f690b8641a011ab684eb761e5136c64524a063c06e2105d06d08121227a3b4bd1d89bd01c100cd71dcad773aded66a6e3ac0cb538ca723d7c2d60ea41ed1b47f63f5558979091f5c4c3cf66058ec255094bf53d599b96f7268da53b3d2302c6ccf2bdfc5bf03 true -check_ring_signature 1ac1a92d2f0956d0ab6f217e48c9ff9a098d558d79d4a15a5d64094565befaed 7ffe9aeaeeacf52eac08652309f910108482c59ae9809a34e703e3f7e3aad09f 1 01c1e0b154f1cd6a3c92fff352b6f618c698f53d20d3532347ed811222625887 3465d62ef860a7fe265e61998821f12699a66439921a1ce42bd341a7aa30e4005c81c24e21e7ba5f8c690706b3f982c9fd15181df9f322f8773dde1ef197280d false -check_ring_signature 3f080fcdbf635589ddcb71f987078c7f047849de2dec03055b69b18c0b4e35a0 2458cc2455ad40812b4fb58a619d0166622e2ba765a034f6b0f147b1e61ff22a 14 2537b293fa0ce60f941f145bc3ab90efdf8c1efee8900c66dcfdb8015b9fba39 1041f28cbe774349321377e72801c81c421a70de66f4db70f9c5938b083ab242 34dbe338f7fb2ce1d2ee1bc7e0b48f3ace71bd73eb80b8cf1d3bbff5aab74f5f 1da776557504e338d273a80e4b84c1850fd52155194a57b5bf37f9961266e9fd 2a5c47260346dc42b30848f00603bb28158918aa015998ec2e1aa57cc436c614 312bd06d455082cf05ccdffdb559bf9313801e94c69a607be7a71498578b94ce f0df4926511b011c39e054712c19046d7a073dd8eecc91c0b5c178f4ddf58e31 ef185abee10ca355331458667a9b45f4733b363289264e44c6c0c3f86edd1c5d f99c1d559c0a7baf6f815ad31251e53686ecfea44a2cc15872693164775279a0 72abc84e5b3b1e1d630a457e39b8da6058fe54cd2dc8648c4989d6b18cabeda9 278ab9b6e3792325547f3c754d6e500f5294a977333603d6b9865640c8c563c9 198d00e36cdf41aae83a457a41032701f07ef903e722b9f7934a0dc6c7ed6a78 02d86c85c0cd7f4686a219b770976b655d7bf44832bf4903679580fbf8cf9c0a e54e69b6b90d7454c7850a6339cd48be58ed94fee9afdbd599a03028ef5bf717 28ba329c74067c018004c6e4ed8c99f964ffbe757c04b29b06b3a62b8c23b3030bb195f2f2a9169cc54ee35a956bead93fbfc61adbfc25e859d1395e9e07690eb1d3bfd86a933eb425d433ed0f739bd52d298fe3f94af287b8287f7a802a300c03b4791a7f708acc5e019a9d87f0082d52c96745cef156351fd362c578f71309efd525c33a30a6de944328bb2c82030991231ac7020bb522a30455ae21d66200cfc807d7ddccb818c996ea2f62cdd7d65cd6177734f8d9a51f26eecc5098c30bb9842c70075ba13bffa2150609ce101c758a62b1a72e5cb0e9ea8b9a82231a0d228b11bbb8a99af898eb5888baf378f473f7e65f6ee36ef99c3ed1d51500070bf80d2b8ce98180edfc2deafb1ed85730a11010352f7e257cccb40c6438cfb40fd4fd78417bc9f8547613a798ff3d0f5c2c18df53b070432d1ebba114046db80625dcb2a497411a2a4f1c98afff4b292bdc3f96b273f00c82742c4083be709609ef4636c356b83108dfceac7357022a8c87a1c7a007ae972e449881658a914c06fd543e8f89042f6fc06649d4c214d67d699b083eb134682f6d109c2b6c59ff0f0ae19a9e840db5fe19d15774d4e6e4aeb9a1fd8e12ff6bd602fed781568f86045afa34001a4dcd7a445d49764af791a6c31bbd5964b6c63a5efdfd78d8c69602255d1b4582de42928424ee183c3272f1d051163afb6ad08c54a88b55e1f70803030ea438549e5a340b4c326c45d56b1a9c7c47150503791002ce47e3dc34af026fff70dba3cbc108582ed475eaf8010777f8a4c97ec7d82c5f8605e0a8921307bd6f42e0404b69a4d11be851d176311951fa2ba3709d815e7d8b71bd12b5d1042a497312849e5cf8e212078d5c51110105503df2708a675cb1e1780fe7583f083a150d475601a0de52d3fb11c968c2479f9408d898d41f9b686415ad300b6a03e25337aa42ca9b08db70afe99658de85dda2be513e3aff5392b1c7a874696a0bfa83b29c2b6ffd5e980c8e7a38d73cbb424aa61276f2e3f0769d277fe20a9d0584831cb594eca344f4b9e7d9780dc7474be3d365d6b84fc800642afc04f9520aaed6d2b8958dac12a05fb8bbbaf3b013f419440d4411a31aaa8fced5ba119d0384eea0ddbba12684a49ebf69c2ffc99aa3c13e1736d40b4f67c5abb84504590968786056a86e15abe715d664b3de0d1e2c22366edfaaca9fe0d6d15b6736e40d13e77eb9e9d3aeef86a2d79894167a85c9f92035cdb0c6b2a1d6d9eb5ac48903 true -check_ring_signature 94406af04c5918b574ed9247ddcc312270ba0d12d058fce0b3af806adb2f7e82 7abf1bf35ef10bc4f00211de2a2f51c84ba892b912067282796a93fa07f619bd 51 d8b96598d459cb65910c6cf64f65c6a330606b78b4da0eab123500dea0a9f0b3 406bf2e600842eda541c35945f5dc23bc53f4363a9d2e994b770424a63da9dae 9e42c1beb7e2ae9c96b2ff3dfb46db32e1a7f1cd9d43dab728da18e58c9f5f75 2a987a2671af962c852fd03e4012281836791e6504b8e689cb8b34880175bfa5 05383aa44acec5b6d4386c3e36d7db715981e63f9da3b2b705b2d94fa26aeb06 470c9658f7a21ad13911fc070bc3b4a2780d771180b49db3d2f7951ba0f9be93 30a04acb093cb2c67395c020bca0b5b6c2fd73aaa028e4c953422d830d21b7bf 0ee214438a4a26985efdabbad14f11e02365cf6a0db364906c37408a7b04f641 a450a55ede06e447e88e9b27e743d6b1ab2be0dec33de105e0e51649e4a937c6 abca5a14f539529919555cf8990ffdc454fe259ee7417e4ce7588e1abe964997 386a02dbc0dea9feb5fde40024eae803b83eddf74fa7a30d98420017018e103d 408b17077864e7d8886e51068aa50592746cf458dd1f1de1d8f7686a83ca4fc9 a05a1e3655eacac2d9b6a62e3f2fbd6525318a157ad690f46ee243f2bd330a8c b20a4d7eb5154d46dc4b8e39cbb7b0eae581b29031de66f86cc3fc1027dfe2f3 0e297c0ac37709ea3be3e3c7331a8e62011cc29e63deb5f56de101bcef71add3 39c0393dc42ea7416b53ef6a8cf2f995497a1b4a0ab1be166d8e7d5f193afebe f4249221108dd012976f3f3210adb82e2d8b2584362c3c1d9ad75f5414e16bef dfa7cb30932ed3dbbf354fcd66c97fe2287cfc04845586b008ecf7c202267766 561807bd4201fabdd77e5d2a97a532d7e20f1a5ac6fdf4eb33115c10806224b5 670bfbc323aaee1a17e0ef290d239cc5cd3ea07c796359d0ddee6589ad3ae67b c9a8c9deb9055759f206e41a2ebb10f1b4044e0fd1c9da4b3cba8f4cc81ae774 6f91dc1870f045b983de05fef1ffba0cdd9ebc4a61f44649aedaf59eb07f30ce 6c7923b42f428361cd0adff98c3b4df711c3c8a2c221d89f0d2f82c585a5623a a80ca0d2f38f700305f65f6320a87e562e921f40bcc49735e30779ee99625727 f38136dda6df66fa4fc09764ae3270c8f2fefd3ec2624197a17857fbc69471f0 b4bf594edfaf9b3f084b4acfffd0fca4c8f6b4e1a9d3c3a324e78adad66ca024 30fdf3f11b2c203615b214060d9c08032bf1455f2d86b57610950f96474a80cf 0e9771511db62af2462a0237b5b1240c7f43b8b1a2999f221267c48a30cc1e9e 7ea10af8d179f30c7e3c476339432535b64463f1b2c90398815a3b85b5ab1d2e 6b6d2204c06074250024f17d8eeec132d6aaa9c34642fdda255ee5408f590065 cda1a626c05b120c3957d0b520c9c052123ba14e4e1fc58ffd09d26f27aba0bb 3909938624d3e5af7bf0378900d7a96feb80bae0cb0ab9abf700663311cc90ef a27a607a2651b931f4755129c8713e0026b10f7a4ceca9818f2395aadc853509 6d01697b38fbc53ac8dad80a7f9021ee82af99f9eac26ca07bfaca3fa338d98a a8b39de1b392312c284c6a0317fd2d7869cdfcfc8dcb660208d1c013396a3fc2 5afac7e2bf6614b0c3379c7ac465f4644c0cd856fe15f9881558bad3dc38a48a 3f8e304fb97fb7d8462c322da4826d621bf15ad20b1419160948ec9e5d34db51 a6ae5f4a87a346056c8142341436cfc7c458f16a71e8e619e8f94c7f43076d3d 36d0b1c2134db1a4a0e9062c937321933f7af85a95b87a3dfa118bfd4f6aa532 ae4347344bcdbd76ff72c98e92f058a35356a22553c1d56fbbc9a7749fc17dc3 7dafb06226b33345a1966e25f90d25da80c47d7c299ac3058c459082ea3c274d 96506bb5f17fba82b1995de951d468542cffc4b301c0e37bd53daceaeab15666 0db2446e350afbfefaff5fc133493b281bdcc0ea48a60f842b55d27aab244657 47065bcdb04860d0e164fd8a82bbebf1e5b831d5b352eb453d4b5c4605ff43f6 6db020bcdc210127334f62880d282a63552189e7b005247c94c18356f008bc65 0a71f9440b719ec85c3bf7b75221bcc030814a4e1920b1a3cd95d11bcf0ade1f 043882af25c5d4392dff6c8c16eb6b2308b15a992c62c9a05e334ef996dd2f6b fa018b8a5556cbf76921af317359958c5a88036deb126497e9b143dddaf2e219 62d9a8c0e397f904a1fb0ef522735a1892e77147f34b8b0f0dfead2f7c6d219c 31837ea5c31dbf1c33b4e427e0b1d67a76320bf63217ee19c963817f6b5056ff af2eb49572aae9e7510094d6aeb42f77f473c2499640f4321a3b416f6d682288 30874959ff42290ddb0115cf3c8a8287ddad7651bf328d112eb1905842bb4f09c6688daf9f75cbebeb93fa4e0c74b79862d5ab72bb84eb41c3ab99a2b5f02c022a896e49ee824ce31d0c524032821901522cad3ce0600f43396c0e6d218ad401d2ae8bc68e3715415d997096c78eb4ed99d824eb213ca2d84d93b3573f5c3003606b6e6710aa23d38856e3d7e51916eed95e3d24df168e611e6ede6b6637b70ac4ecb6e578d746f36404f6494dbcf0bcb818fa5941d7e2e041d965ccf4548c046869c52479b7cbd6f7738c4ab65d480859a8015e72d6149b90a3ce4edc6b5f0cb650f5a05c5196bd11dcc1e76c8dc922cffe3b4d2c00598214c66c7d9ccedb0e0799c0fc9e356e0c575ebb8044b02cc1be2df8fe2439f565afe4246280f97b051f4626c42aa8246b7079654bf9265d3ed721ef423df8a4ba366bebf889f8460288979a0c95294de34fbb46c6624794a5cde161d2212dddfb2655a7470484070dcf9c1044249f201e0506942f51e4ccc609ac6335ab39398b40f9e2405c274b0f0f8f9f52aaa0f08e22494d7954cedb845a0d047d115fa605aa531297688808027ee6a728d557940ead61897c85cc2b295a93fd3f2deab5d58d8392542e74cf013407205b754c652a3cf6b6380c25731d4975b42ce591e42150a5c089f1dd760f4560a48835e77d06b8e617a03e654c6be066888afa38c7f801fb1137b179e00ee5143af5d51ff618613bf86c0e2c16ed4eb370d6e9fe8166ad993057e156780671850602d9f56c2e3cd844cabd08b543a3fa54e83bdd6a3f2004e44a68a6420a358abf9676dcf18eccb5035dc0fa388db363e83f5e79a3be56f2df32030c480ef4bd49226ea425c291d2ea5a235cc8cb3b7ccbce361608a2ffe21e01f1eef70eeff8d82c9d2258ba8646a5e479382693623890c571882a582a71de23bae9ea06aac625ed7bbb5988d443f6f1ff201e5981a218a6febd5f1d6bfc10292a960c043acedab799b6d2cb505a5174ffce582930667cfb6a801c47937bfc233996940c4aeb5d663563e5b186ab9b58da20f573f42d23f1ee890baae0dd25f72ce61101da043b65223bfdd9f6e7c1ebaf9ac150c02b995eb8d0d1bf486f7ea7ec88bd0854a22f121bfda2fb56b9e7b758d0bec253bc0a4be6bc9669ec388c5724a3490387f2ce73ec91eabb1a2e2fb1db19e4b6b6d838ea9447982bd143b33f655d5301b12fbeee72316c2cc5bf5bac35732916716f8d3d248ff882b3cde978f6d2e40d872c89a7dfc291267ad5e2e1e690fa78a0a8eae51524a2160b5528f42238d20bfb2f917f940853bc96dedf4b40d8191202f4ed9ad62d1d0201e7b1573659170c9669b4d342a744347241a8689227c25480280a4a4dd3e11db957da8a2a00ee031b3f9ab8d509f6963dc5a29b1974d0d8226a28632368848b95f85a17070fa706963f9e41648f5411f5fdd98bcaa222ab76d16758aa7dd16b9b76fd7523f2b609bf3edd5140bbfaf7d5a7843c19290f355167c1a04f1741996f955d60e4f9df013c2b898e07982b78bf79343888643d050b20eb892940b472cb03600a0b694b03895d40ce81aa8c25a0266c382cdd304a0b44740c4fad2541a640e9e8a60a850fa3fcb9236b62deacba0db74849118514a9e092d77ddc5d2dea64b09236f3940212e16260fdb2309169cecd28e820b4212cacc094ca5d95731de12fae9fc0b50999ae281b8cd332c913817d5f6afb169b66ea8721402924eb305fcd6c383e52018049ec817c00609d073b23725eafc5af2e3735784fedbf0b12bd3c455e5040017895f092821be093e3346ccf5a448cc4c3c124a5ae89ff59c49c801d37bb92096a0063a319e525396aa594366663e09938a30a114e6222eee5255f08fedcff0d38c5b24809a6d0bfef788eb71c6b5e3d57c306fb90dc50ef13673303eae6080d5b2773ac5939fb85bc4f08305e12e56e390e0c399179009c7eb99641f1adf80085376ce386d391dbd10b43daad37fb2a586ac5b62ad56c521a683fdee0f6cb07e90c603e29a6f3afa39f40ff7112f829825a8971054e9901d99af7d97214290be6ca0d179a2dec890847c941a29bf4d5b7e364596bd284ac28ac0497451ba005c0448f965e1f4acb22fedec9c772aa3b523fb0d5b557f98c52d72f482706df0ecabc3843d316cd6527e0cf039313b2e5bfd6d0ce88b1179a7eafdbb63012040ea8e0184f53569b956853b4dcf3562a8352bb3026899ca313ab1557e172de5d0257e6157a2aebb4ba0d737ca409a6e8078b7c37c8a9daa8804dbc6ee43cad410782961c3ca1073031d608ac9c807257d1cda2d9081be6055f0a2ca27cf225e70a69cc0423d063263911f743a703a0915c5b71c4a04d53377f515f828e4e92ff0c762bbe9bae055786f5ad9d155c80a9a7620d206354bd09fea50af4f69140ba00986cb37eee49de41eb7cc6304cc255fc85da4a6e686c645e080fdf9ed91c400be7e56c6b90f516606ed60b34aa32f4450394edc0870d2df3791827c40738990cb7d7ace1f3596193038a12ef0fbfed166edf6fcf88f34e55189f9f260ba17a0dd2e86c4ecde68090a4534acecb98d934827cb90ff9e01624347bfddef6cce309c92d98a716d793a49ce304e771e790a51a71f74badd7e1ed6214a740796fe206d1604250a73a6ade0ce5295706f0cd635a2eabdae731ca4a9aff5eeefab40a08d7e4510dff5fc669c3426dca8cf852ee794bb917431175c60893b2b38220ba097eb86eeca0ce3802743e4286cf59b2ca7114c16b1b75f79e8d851805edb0290d78fb59c29f1a5fde44b5aaf02d2378fcfc2bcefcb86db539c7f6af7d9ccbc805b1237c60c52a17d4c0ceaedb0fb58cdf136302275b1669cd7ffb14f8a8516108533bb50e5571b329ad6e098380270a672c4ddde9004b04571d7a91f92112020f07e3d3649ef0034c4508ddc84e5e813258b6ad700c0038a54b737719df5ba006b1383a7b2f2c399baf3289bdc07c142bdc8617d8d5d2d12b26db390a0ed3b101951f09ed93701dfdb4777e515a914534e65d88a92db03363728e457aeda03a07d3b8ee90ba05ea1f0058f1124f80d13644ee169ce5ec9598f02a92c33bd1d10e0107c1d8b74fe7bad2f80e1f7d5b9df180ca69c2c7a36955de9f9f425cffcc0235b1abf8397442655bc35a46388799051038133d7dca12a6728e7b5e5244ac089846fddf5620ba20c532daa5f89c3a73854596a74d34e86ace387d21b30f6c09dc9471c2a0c48eee366087bca6c10bda806611fbdbe42319cfaa7e8af155470be98c95e9f2a6f6b5cf580aa765914f35a044e6f7423252c7dbf5144467facb0169c0b42165bfc06332844f8ac9c9782773bf5263b643f12f0fddc754cb75c1079eb77da65bd131fa497a465b179e8efd7f0ac13b5409a0c3efca69d3a7319a08f915d59a9b151b08628791f4d35f524d92b3c8198bd55a3e86fa5043b6c6a30ae8b10bd4073387d241e95c6e699d1297a86b3b4789dcba086c6cb326edbd3d02538e77e58286788f45b9d73bb364cc7e734bb16e001f68618d9b105ddaa0d302e502c4f48df762a8e60cb7c44e37546ad80768b244d5c2478a8891c88a32c2028c51612264990c13872b41d882f64a57bbab6dd6d4c7588376ce333f0be2a201df4eda11bb67485aa33b5420f62d6068f88cb768733b52bf79ee6bed79ce4309be225523baff744c53c79379b0039b29194b5c89df0095a4973a6dfce22b9c0198d9d4a44a98163d84171ecbc8f34ffb90e68ea71f312bf34b9d67fc9377a908cdcf576011957e8b279695dc830e043cce0145a99e10025a167e6bad392641000f26f8f53ace5530b351ea254fc7c0ee2c71adf66e30c2e8ce6df3f46170aa0deafa158af126f51ae9c1a167ee8809de13e847453c2be5b74b28899e4ef8ae03373259873a21c6b56f712644e5bf4b5221234b07ba5421af028fe7019675580d5525d395f2ff829cf0d6426f6cef137efe76cf326579e33a8cda01cb5302db0143ef0455ee5d0b4c0b690f9d0062abba202fd7eb1cd92268a5588d0709e64e0dcf364e9f0693d628a850c6d68868529b025fa8f5a43c52164e01f8ac391747073360546eb70fb18f230d9e52eb3f92ff2681e7e23bfa904d859adcb17968c109787d5ca72d56da4881b5dedbc29859d031450e7353f56b7a032e9b8ca7418a0624ebcb51f5c3cf7cf138739abf7b510c22d090b23063cd70b22f5d50b5e8150ee18c3f9872ccecbb2a261c09123a39fb8b2853f239aef5fabf3806c4df008e0807233429b1839ee7567db8b0c971990e42cfe32138afecfe8c138da073a40507f5ec1d41c853a7d3fa02cbcf37dd9b73ef1b018a63ef7394c85cf6c5619a1a06eacafd5cfc538397cfcbec80cc448cb40e8f5b810bd7e7e22fdef69f67b6a60cc4d34677d14b473a3308eeb50b3825588ed95b394d418d2bbf0abee07051240a97ad3f938535fe59678fda1f19e910f97dd14ec29f04f070b31002cf72fe09008d83d692db708fa1e3bcfdcfe847d3c3997433ef9ec308454295e95ad81dc506abd10f53dd683cccde2865348f9c313457d7ca7f175502bbd8e7f28c473aea02 false -check_ring_signature ec367ccbe3c0f1aaebdd2d5d8a8fa3236f528ca4b8bfd3df9b7e8d253ecc216e fc6a8ac3c76437892b8ace5a94cde695aac776ec9608dc42ce4dfba516e9b8e8 33 15023cefe65b03fec4b773c05c539cea4d859d5696cb95d69412f9f445a47d02 b4d7bfdbc84310ea87fec2efc04b1bf7a1a91df6793a12ec2706f167c310aae5 9ef3283ebeaccffbcd5514b5287256a158441e20faeb4d03470253a14ae0c209 b335a2982df9d2eceda63f2f550a3fff55ddd73b53b0be45a0b93576e55eb8e8 4ed429b0c948b0a4679e85b0c83340f73e300ffa0d84b4bfe08ab33a84b41d4a c016ee432cbbfcd3b2bcb48e4d17121144d62b181a9771165c05352400fe81df 82eb4bb7f5e139266583188fc8adc09bf9295ee2165e3e61a2ac2e65a4fe1f9b 1ea9bc3b882bed5197e3ca4227016e2c2e009228261c354b51788f5c00e656ae 6550417c90e7d4b24f78cc2502e65be427c9ad3f8414395854049a83765aebd3 73d195f56b77ce88b70ffc0b236e3bed86bcc1f69f1857e1ec2b96c4c5fde453 8357b03bf58895122bc5ca55af26968fd37ca19c3dbb044c5c719ec8c0ecdc1c 2b29dff9b9580f65f56dfbb66658c82c2d2de68ab5a3fe47e513885124f26e80 7b3f3792b15bd98b65782687112600a8e4c775ba60a9212fe0be94590cf13761 deeaa2b7c5f0b51d66643485d9b954a77ffa048bfb6204f3556e42d2bde2816d 7e0c04a2980c231f60d690df5744b61d44f77303f22211f0c1215ab4bba82365 e3ebb297c97efaedf349f1d2d05282788e1463d24664c09bf029f0cb9e571109 728a38ec9ddae432c7d57e51afb12874caf6d17f0cc42ed2cafe7b75532afb1b d59fb51460cefc75c41a82d82af3c1f7ca9396150998f82ebee957c82d3b3460 920889f5a37a736a187b65ddfa8673770a57da271d4b7d7a604841dee2a44b67 19bb797353cd5a0c59c47f41376f26482e167adad26561ee47e45943f18fbe3f b82cb06bd52613e58220a2b4fc66c815c4d68de34713cec54c916fe983145d1a 83c3e6e1cd49e257fbcfc55e1d0585569a609bb9e36d27ad8f8e8b8dfec9b7bc 6d5faf312c4b0d70cdad954e5551136894f3e16fe020c3636c0e2a496ecc8473 992fb3851b76a787981f1200300854408ab9b4028ffe8073d0db77034a0013b4 1bcf1673bd4befacf26ece85590dde17fb4b04246a5b56ff9b35b4f607c9f3c3 22c963f39fae4125d593e98397e1442f1b9e133da8bdbdb72eae58bda3f6eaa5 60b5eec6f5a4ba44225e48cc085ee5e984fe28151dac581c4186146ea08877ff 54f31c4de8b1d3d7d4425c6846ff3cd1df82096fd6c82423b7579f6695c303de c95a55b431d5c605ddfe463303cfbae86878bb70aa5058a15bd294954fd8907d 4383bcd96ad2c82d5ac757cb5b33304a8209e5a63e79eab4cbdaaaf15aebdb06 3dc25d5e9f637a6b23d8132f52d865490301c771cfa63a8d7a28460aa1531bb6 eefda0b0bc034b0ed8f1e0dcf4b2c9fd1a0891a9cde5a48224d4a27a8b102f74 d197d1ee83336a685b029780fb9894b18ca3dd42bb2ecf07ab1ce27aa69b5388 f7a579c05abd57b5ba2856a287c273862ea835c967da3942b334736221844b085fd575c81136f336c97bd46771eb32da6e7b890cd5aa3567ad02c6bdb8346700856ac55fc11ba983b86253e8b32562e781a14d3c81c7efdc0f0960ae80d6f402e5aaec3c54b930684452ef3284bad69f1c1fb01a765b89658d7e8ada71f5ec05035fe938d8d227dcf847dcc8723aa1ac182e442dbecd9f4e4792b77b58318c028d604ba7a67796927f989102055aa6b71c940b0044949d9fb99ad56e4345b70a325148d0189ed97b2127c50e0d55227049a6cfb1df29aae34d9dc86682677906535b52daedd976fa786e2073a673cd28f7818203176d91c5f97ecac25220010bbff36303001efa5a733c6002b6af4ca826a5458e3179d9373d882de06049c6091575af874a61ec72e265fc2fa9e736134fa5aaab0898346cf660545d1821fb095f1943dc2cd706709edc29c1352e8f1cd879cc8e2eb4cdf4f5029581f0cc0e0be05000f820aa9055b02f408bf8b33159935e17e157a9a6107bd1ea314e6ecb0b46e177af463750b99d34452eb9bb79941e5bd9b4cf053cd23008507629e66f0ede4419c39147950e69b4db37b0c5e5f467c658e8ce3ec3fe69d7abd413f8be085ab53f7a78a0ace4ae5ebe0b08a73f9893760c3692e42a942d5c01fe4730a3004b21badba8de13558a1a6d054691be11433989db9c772e2ce116e87ed67a6e05147f4c9f10ecdaa0af62f7fc6ae3d3e0075fb78204d48b099f7093fa74e1de0a3c9c18055f8828de01bf29b68d8cf171ab8fbbadd5490314d545108c28ff1e04b1e521b55fb2932591fe6475092c934d072d6ac6520d482a7aa63123bd6baa021cdebe978dfa729fff816a7cbfb127ffb1c134f88f12de0248d9e713b4ad0f0416ed326e8bd1c8c789b10d4008fc704294fad1804a07fe0ec7732736a68a7a089f93b301f5d7382e91df7b98c7ea1d8472465c7c464817e0a394fcdb2dace70492895dd447c089872afc3c78779137393501bd8f75f061154880358951da7c093b96aecdc43735adea1b9f6da973c1db98e19130960dfad4d3c331671cd41e0327f603d3fbec0af8c1a542c187213a2be01d1fe6a48446ca40c3a247870d7904a1eafa97b804eb2e79d0fb10089e5b98e8d0ff3019180ae1c4b5f2a8ae1f42010149c68a262d58f3504de2b2a8ac0de1ed91eb776315671d9462334e66de93ec8987b49431780ba4f5c63ced08795722a0c8648809f1ce2481d7cdcbb8460c0cc9ebb6a412c7d8b879d12d957f42d5471211cb561e92b82c677f0825a877e907495eedfe6d6becf048a1bedcc1009a701468edd50c5c5ebe861781566a2a8d0c3d57cd0a98b79184e7c5bc1d04aad97bfb49031911b4315a52ff2bc8431fc90c09aca3c294bd881b3deae1752d6890b07862d50451820a1e5dafbedb3f153f0b4314cab03cf016eeb9b21fff1e2623187362e751a79037b4a5ff73e5f83c6e0e99ed3d83a9d39e9834825f683f96ee9ea43bc1629056dd8c0034995d26bf2d0d9d784915abb55b16356d243f60f5cd05e8ba3741c7e34e0c8d1a53b61cc16b07861c8d573b7a51e14c7c2feb7f7d631970e7580152c0466f3b0607c51f33b2095e1b632395b92f03b3da4a561720ef68ed5b1abf4ca87fc4086483c5b6b5d90c045824dbf5e7885105903667d8a91e656c38b543a132e6704749ad3086e2180bdb7b223c2cdeac55ddb95fdeb1c71f2dc0179021c086498a7aa9419b3abdc504914d9af5dee990544c2443fc33966872a800d7c8171f769c67e6553dae0c230715890837e0de33e9acdccf679dd36268d70b91fa37ad577bdbfd4ac0d513d107e7cc370ff22173a4c06454fb49853a3eb1075514ef5f395c94f7bbcc1badfc0cb295357c4f0a7a7b70641841099a12d8af38b3da7a821b675c4577a0f9f49d08c488607354ec20915579bd29e3925571b23c76ffa68c8eb47b5d9aff5159dea0e0f2d5b1aa5c58b9101e67d58403d20c30188d4fa8c7d787b7e33e82f6c58a02d25d086a758b211b0b24d39c1d758487c0b2fac1d11f0ffb97d463d99660420fc3f03de38b7b598a6f231aca88bebd070b6d0f02495d97aea136fe0dc3b2fe07a064e000de294e9ba29ab3c9a665aac1f4d1359acb93005cc8714ef4218a460a9ea1015a5ac857a9fc167443cb957a325e9aee84185f1978407977108058ec0af3d5d405f8910a72735e26143b339119e69782bcd64ef02e92b2a0fd6cceed0e18e65788327e74ee25304a2b87b5638037869ad8213d06a0f674d09d096244093d98dc9c7fbaa8036de147b41ac75ea49a2117e69553ec73f8692b88e5201e052eca4eaedf13fdeb745d92f38ac41063d7e7783fa2f383a65bcdc7eb7739a20801d28cbbe9efa7746df2774fc02f2edf2eca88848523d6f77bb9c674cd7f43082ff04a44e6f711896945b7c09286308299a99a7709e595fba3b0cb7b7a0a1f0e72f2ce054d41c822933a58134b5f3e46881ac8886a72a3de76ce42e44991770525b10d0f858b28b4bf80aa78da33be3ba0127cc539cadd2b2244a92bd6573b0cd117fa3c51024720125def5fee605b46fd5e8fe52042a2c1d45316b2001fd60fc1cd9b13db276e5a81f02a385a96ba754d952ec64ee14c61b4701ebdad605b0ad4999888db07556e0b3b7bce117c39af1ffa1b6e8c3f4ce739640599eb365f092f6decd12d43544b5fff0287e029b17857fcb05ee6fe7945ca0a329d3c217d0f3b469d6f63893ea6f45aaa6fc26546ce99da21efab05c06ad6bf522d9021fd0943c6d3ffd67da633f86bc9ce4205663526238fa5c844e4ad30d3ad744e282b0c50d89effecfdc744fbecb38453e30eee0b8268d6b1319da2b14f61100c32580d517e760bb8de7bad6f6b54a4e8224f8aa78d2ee098ccbea85a750e3619f539098eb278332707dabf94d683e4698cab730487514907321fba2248e5357edce003 false -check_ring_signature aac6af0e0e6dc9ef4c04bb56bdee448fd1c55a3a01b344d005d9e97edfba8c43 843dfd290b9a909a20cf680e15e5b2adc416a98f610d2a51246f9105d5caeaba 183 5bb8ea5025549796a2678f7a731a696fccc134f8d76885b9fa919a4c021d9817 d3c334899ea630c1d197931dd29f7d0d06f7ce2f2f5d565c20f97063548aa021 ae338b10f957ad840199dc9e9e3721fb9af81f00112183239e161818002fc100 9670bd843b582496831fa07dcb6589f989c3f934c2517fb36b763da292cc9052 e05c475341b66cc99d0f702ba848c0fffd42a24df0ca45921edc61eb4c050471 08981ce019a1b972e7744b603b80483bb6c19d693fdd86b82014d6cedde93572 ea34b491c05ad898cc0ecfae4b0453c6dcb02a5538bc0ec723446d679a562887 8fc0b2d5b8f846d1c5a426791748ec6ff7b3c78a656cfe1c76265a8e90249617 3c3251fccd9f142e472989684407540f3fa9fc88d0b89ec1ac7ce958c9f3993a 6962997c36d63f075bcc8c1d22f951bc9f02d3fa9cfc06c16feb5bd870030769 befcac18ab4239c0ab886b5682a062ff555681a1509c8ad38d1152d5f137d330 e7b9489974f57052862b51efd996cb8feeabefceae75896059a85a5c26d0122a 533c703760204a8be799c3bdcb5c6c0dd2fdff95dda6f275c73482e2d4015262 361727f3f5f09333ec46b144a96bc5a190f49c914bff2fcec6fe4978e1b11c92 29d60760ef0a4b8a2de407de233d1b96ec12520a90e488fb490b9440c8955ab5 38e9bab4b383d0660175c71bfea4aa958eff64effd5f51703df6f434f8d33b1a 67d97fc635b35e12aa60c147375df7dde247762dfd817abe05c578aa17969eb7 f5a8ee63ceddb6954c47558fd8e9dec55bfbde2b07407de1ff67828941bb3ba5 39b66f7f8a886bb3decf58c6e83ebcac527c095feb46f1bc7c7be6c54835c3ae f2329d82536b92de4ee43f105c7576cc44620ba8dfab8096643e30d2f99cabb3 a530fbfac63099d37e34cc3943fedf03ef0805d0a5b601c0d63c613aea4be34b 4102dbdd720b5f0d4093f2e13b09836faf23a5d67b3b41523b13b29585608c2e 59925df864616136cdb4bf784ec6359b17e35e2840574f02affcaa4c99df903f 43f026ae53778284000316229677ea637869fe1d1f76f18dc9e0bcf38da94dc0 881b2f977821a29aa3e66c422ba027900bfbd6ce0ca8761f9b170b0fe9828039 2d1a03826b6329704e561224ab07df1360da1ade133dbbb55a2898a7c78e5e54 03b3a565e5e4a8e3f46a68040ffac996a3d7c05ccfbe821f69e9bf7b3f38a9fc f1ee9217aaeea6981dec78c393389dcfc3b53de9d412d30706c5f43a3e1e6e63 a2c623595887cb2196cbea16031d8d380c768603a17f9cb726af903115ef6432 906d85486501cac7b779cf1bcd3a2a331ed29e09e0d3e4bd65f8e7e8302e76eb b88bf752268e4a09a8a729a8c6afd36038dfa65b2f41511f68058ee7e11de90a dd09ee4b644a0d228dd4206442697cf6ec18c87cd18d4613e5976c4cecc378ed 311d87c054f0cd0c506cc1cd45eb5e3fa1d9a76bc2898ac4e42e5a627f03343c f4f1f949065a072539c9899746facc62e126aefb1d35939c1f9c0da825dcf5a0 85b996d38a4e9d64be7b1e2c75e179cb152f5c9ac9a7a4f351f50d58f43111ef bdeb0e5c6141e779f578125c60984bc498db773a3382f803ac7402aec114ad4d d3ff8393f339aff91a48a13abf2b696ee5d8381181b923cf4a8444562d2e745b a215bae63431deab9af548afd00434ae582cf64872b895798d157ed9b6b1b6a1 b6dbab1d8d7c122c2d43de322fb883d35c4856d2aca84386061e7c414717bcfb 6ef80171fcb78b4e4326eddcce687073f19a053d618bed132509641040193336 4b1ba7ce79feaf3c6e79642c2728ac423e324b8046f14d7ba8f1a8ed7d15fe6c ee2fc68fa0e66c18b3db9bec27184d78f58a864ad79ced1a6469b934cbb4c85c 3b9dd06285691bc14aba24009066a3f9a8682e1a6ef709ffd8e4f0f5b9e95b3b b2e247357570a49c8381002f46dc1119aefbbc77304a49b1e688a02dadf3b648 cd4fcd716ab4604b516d6f5d8697e18f2d27f8e02e3e91044cfa0b2945a19dc9 3aec6da4af54d483b2005df82dd27e4066a23d7ce47402e20e08bf450f58f0a9 6fea586490201fdd1f9bbe19ae46e6ebb1e3d96b8a3651c6acbfb9640e6e7885 f6971350e7be235e2d5ee4dab010fca1d57250e05d80ed53e8b59b6447af596a 1a57e2313b44c592b6113ac7f564ee33490044067607ae97b558695b1348d8ca 5c348e25ffac3b500f0ccec71cd5798e362dfbf42afed4cca627269104aa5d85 e1acf415fef74b5322b34fa07af0e96ab66397f07b1869f9e23745e6267095cf 3fa38feb317182581ce297c3e0e28a673ccdbdb333bbea3988a14698f4d23847 94aa7634dd6d282644cae4cabb14dfc944879e64710de26546c2dcc355ef3cb5 e89d760c45a769deaa6ad805b941d103d02a494e80e0a6251c3d146018a07e6a 311d3464dcbcac897b131c242e041c2bea144ea15e963c483b491d0f6c309eed 510d2b2d3828a839b519c18ac37140601ce83c1743938051756d95b15092df75 a700d78b6482036aaf51a777793f983d97be6d712eee25ed357eed354c5f6c9e 324568e1a39f7ecac287d41399ac452dbfa40291e5c27803eb179987b5d9c016 dcd22c0d81d031da7714620425350b079fe2988543e53c45512a3ff083cca1d3 49d0c761412fbc4676869da6101a332d4dac2e56b83ef26450e64761fd9dfc19 4150e2d87a0cd1adeb77aebd26d540d73e00393c44f5650bc3b5b5b3b1b8976f 1566586774822a9223246b28330514c3ae4731c3a6c1aff1cc0904916ae40e0a db6a9a7a78facade5086e7b1e7de1f82f4e389adcb3bdf18d1aa3a868f91e2f3 e9c4a135e48a8f801b99dd00391394ef5c6cff01c230b62e6ac26c49c9c76f86 355a5c7485363420c28f544ce454e8d7ee08e48de47e0433c983f0bb097f76a9 c2f65de63ebb6edc36386d4d20f9153c10780b7555eeb4cdca5a9707b4824b7d 77d67307659a8f4a15b7d554edab5acaa2dc11067482f7308b0b13ff5a867499 00c156d2dcc1d13aaadc1f4c0b12a1b5d5bf4aac987df5b5e8184c66ae69ef4a f20b6826f0c1d35aac6e80b76d3d5b033c3f4359689c1ece50e978e0bdee15ec c2bbab2de58ef8af010150bbfcb059603446d0dde7cfc117690f739ae06f230e 74d7d0e1f893e04de9722523e1d560c78027bf7f368f15f5e56fb930db64ec4e 37e87fdecdafb5ad988c148f115cfe31eb27fe23bc01168b492a775f63c1a0d4 88fb1b3bae2e6693b0e1d352e621b0f4e42a225eacf6e7e3a4bf32d29873fed6 720da624dd6b2889258554eeca8a5b28fa4f399e98fc649ee056a0f8baf2d69a f95ce6dbdf420f352174f58c0c54c0b549b231eb6b4b594d052249e347187dc0 868960c7d8e2102da30c21f30b962c23e781007fe147095563d2fd39651bb8b9 27f312c62dcf86f74eb119b2a5e831887e479f6545a08fcc3943a6f69cee1d47 998284aca87c65e934c922b7fbfdd7924675e838259e5c9b840898afd10172d3 2c173b105d1e0cee10abd8bce68f089730d82bb9cf05ac597cc6f4b1929ad205 e411eeb0e916fbb9da1d1e30e99d8ca4646a0efe2f01fb445a83e1a2f2e10259 1eb1b65efdf201f8704a7dbaa23ce1bf1c2b0f69e2440af0fece4ca6789f0056 9e5a8b063e011cd65b9d1f4c07415869b5efa6c54bdfad44988bf11db64219a9 ddcc21fb4cbb8cbc036662eda81eba080397bd181984bf344e858d5c5088dea6 4d4330029ea43ebaa8efde95cff2e58e3258cd92986ca576375cd88cc609f9f3 3a4db479c9ce28100020294dce99781579e36159132a932a780acf5a04146c1b 7ebfae3bdb624c987a83b1da3a59c94bbd78cd5af287eede2d1014528589aa60 dc851b1960c0bfb250758a4e3df6d69845a015b8a667509eea150e8dae9855f1 5e06f82be9c84dfca821c5970eb8deef2c5a4bf7071f2fdbcb91fd0b373639d1 f9b4600ca2621b4eb57d59e2badda3b3b2df07f5b800cf6c9bea7af33cc4b301 f953cf8b9a244a7fc9c0c788806e7644f5966e6cd8bff9176cf56556e06b84d2 2608a74ae2c72233a8ab17a87c403adcc9f86d33958986972343b9525943d61f 57c71541b0e0fa0c4e1d545c92159b6c3b8d04afe1ea366ec3cfbef2d34f4aeb 9790ab107742819573704d4e6665a872e4e6b1ed9b04324601d4e61045bba199 81d5e56fb9a269039e814cf43077f5223f15addcdb953e775bfbe2a3be581d6f 73af4c13fcfbbf136905338d52e6ab1441a663955e8e814692cdb8e16f0e4735 b2185efb468d143a47b463aa5dd1b32e41e9d04cac6000e484a9505b49632d5c 6022016272a2d92ecde35f78f522c5e01c2f7f5f0365205f21bacf15d176cd0d 332f947a1f6baee8d64175c418fbbe38bafc36e4ffa7b2f781c86afc597cbbf1 c2c10caf2ef735adbc64127c38a19fd50370bd558d74ba1e5cf2daff17dd901b b32ab3b7cd782948564e8d7ce53808383fa4e33d1cf632f549770c5152a9a2f1 6ba88defe05d77202a474529b697878b9a0b6e711d7050835a9e0a374ae64fdc f8c634bc70bb1afb9783cac5bb635d7df5d0d3a9387b5282750f6ffde72e1c56 15581fe32272902b1cc8ee5d242bff986e8dd0f7241a9a27b66f9771ff7a3a17 d4e0ccde929a7f047187044bb95ab7b2752b40d50f32ee68c8d4deade8b991d3 8430800dcb46bddd9df0c949aaeaa71a3e1b7cb56605e0d0a6d1177ee05dcebd bcc6ea06806a3a6f672160162c74e76093355c34d65a3155bc360c994700e207 d1c2832b58739bf2d6c5525f984ab4e8a1d3fa8cfbe4a51bc5a2ce4fd3fef6df 6a6bbe99e654fdd13236f6278f9f5064f580acbff8a1e906099024e21308a1a6 c65d046ab3e0cec20667b4f1a7c38c21a693b72fff7f12f80d767af8e3db3578 5bce1052b61a75bd1e80ea446b3dc1df6ce7531b92c34b1334d10c7fde7c5fc3 debf06281b55d4a26c5a68cb2abbc4496801c032bb7030936e9d897421168ba1 beb8386d87ae229e22aebb4b755cfb2f88356a1cc5331de7320e676c5d8cd3d5 5ad634afe6cc36d6521be097ae607629a0a5a569e95f8ee8f6bcb79751d68cd7 669d16a0cba40f540a204691f13354c195e2a24c5b01fcdca77eaeac3d8a4088 2c3c5b7c793af03283a9364c53c64687b32bccd6c246372dee22b074fdd09e0e 8fecf737b78691edc834625ea68d6f5454ddbc2e447068fd8505731dab11726c 1de3adc58d62c0a0d097293ccc99a2f0272f954893e9db59da7d1c8011c48751 12a1ed8525054a87c0783ab949eb4a996570149e2a93af15a1baf7f096ad01aa 5bd2d6915bac859ab5d68867193c890b49c01f00993644fe6982bf8b8a8c6d35 e6fb6d8e80e1cfd220d52985ec6c7b9ee7d2d4161b8a2e1d8cec96df624c4d73 634e814b07f4e3058948b7e64616bc09b6248e2bb037a343d7efa553c980afe5 d2279969fa62022d458182199b3316e249294b3734e3e30e663f43821ee30d70 c50c710a7a1ded10be9e5785d304c11d4c02f363b1f862ed9ef771885be52fb2 ffd6047bf37d16d3e02cc816cb8603b5e4d98613eafd4710662ddab66a54f8e7 ef3fb8270486557c7a8f3b040ab7a800dc916a0661556d2bd6432f169c538f4b 07af476fb6d48ea6cf197afb5f4f0fb62e92345c02a2a74b3864a5edb64d64a9 546f71a30c9b6173d92cd7a02cd60f18229dec62353df7547a28265b53e36131 21571a51e2659168ce933fa8d34743fcc94eb92c89cea98243f72233f115d7e2 7d8c2be5080920a54ff0dafe8f152d9f1b1964cb269ee8f20a2714f6773eb14e 27a1db2e7ed45e0a5e51a7e1c88abbe0abb84036236c3331fed2697e08988521 2b73d74acbd5e25e7e560917c26d48e3ddf9673ab3a98b74c67b001260ef5dc5 6355be92d496d26150acc4dc1bb2bb7e6c30bd58d16587ca7eccc79f55e5f40c a6144818533f7e5311ec012854326e36fea94d5950bad1386e0abbd4721793ba aa2bd99db155eaac09d54ecb4da80393848844ddd9ee0f25a921e5b7a993b3fc 9ccca4de1ac85e37c46afbba406faae25be58a76be28cfed8bf9d3112733ad21 0505c0ec120d85eb26221bac0d72bf1906cc172fb20f9be01c445854dd1974bc 48de5717030c767ab2f630443b90782d0055f6fc957963fdbcfe2c939439d639 a971e71ec812c59528d3f24a430ebb0a7ba38974b1b0a3b647ae5f858b4ccced 6b5cd75185c47dda1e354556ac1a0e555d9db105076e52ee4de7a72ab16f39e5 3c3b9b13cb8a5cbf058c6b850df95c1054445f76fa814acf1205418f6546e133 b55bdffea66faceecfc5e8b0f82c3d5b96b6e5b5dc52fcc9a392d028fc01f58a 3db9fbca2b634ce4564d57c13294cc4bbdddea266a25365e8950a6edec1685b9 3aa40a2f9d1bb63e8f8076e0149cc6ba5770c1614acb5e921607a74a492f5767 d82eb68732336ed44f75c2b338fdeb966a1c70b1ecb566565b16137c539cf2f5 6fe35445c21f6e134a3326002c0c190bd189c5f27c6957adc21c9eb8dade8ed3 a2ddc9fc4a6ac13c9d25616d6751415cae39b96fb029a6f9edcabaae451eecb2 0113f3e8e48bdfef9e0a8ec40c9ffc33fe1a04e35b968e3409bd79c336b763d4 261a9644a7f6e10af0b903f01b2420060b004252a00ca05c6dc22ad4800f9d59 3752c9944873507565b8954ff06046daf4f949f9b8d79932260c1b3997957076 4bb7088a1cbc7a6918325e5a73193275282c4ce4300cd9686d667c7f9a617517 f61ae69220c65e13447ece497d6d908e5f830e1fe3dc44ff98fff0e4a4345d41 8418ea4c135098ebc8427a75c41ae64193ccefe54ffbe6cb513516b5745b8ee0 02f825a1648b9b1c8c4ea94ae043cb4b73c8b01a77e2bf193d678736c75f84eb d5d65bf1db73390bd2ef41b31e169ce81ff2159a720a6cc328dc3821248dd88a fcab6d45d14b38aa9421707da0ea5b93190178b174d06ba4109dbbb80387f9a3 df2c62f1f0ab82c3765d537f269028fc821f0c23a970a4ba8f40c30ea303110c df1cf534424cfe43829e4f92884cc21f069cade533a1094dfc22813b06a8a2ea 0ad3f09935939185f70e5bd0568d76c357dc9a6a0940e06fba078a0dacdefd46 4caf904ef9712ed788a99036d395574fac27e6df478e880f775ca098d920d074 cba1da34d12d7dd3dfdbd6f39b45ddf5a0947bd160c0f667d1d46a85ad78a71d 5d07585bcfd4a90b446598fee78213c1b0b1178c7998649d9427e3fb326f3505 d2c005d652aa3414929dc315f228393a97046639039992c4e0c5232df88b2a0d d70f9eb737cd3ae4ad9b6a3661c10eef8bf2e79d3da8530b02e4bf5f425bdb30 11547844c338e92a94a72fc18c1fea869730639e7b1da3b8874dc922d32b1429 3395b69a0903f7646860748730d983533d5cc4ccd1b0cf4b55df5e66ea5488fb 2b709b56ec5b5c006c71151ef387e6e65f8a80b8bb4e7d111b345239966462c9 755446312992f7a27158c327516f427c8162fa4962d3285ba213ccdcde803c5c f3216e0b90187380ac9ce71d2b02c4bdc757db48d44f587f6c7f4a7f86192980 ee4e5a46f33ece0f2acf26cddbece3bd0cc09dcb7807d95b16d1cc751ff57240 eb84d02069290b614f70b46c71a10f59e13203da30bb9cf4f72f70130b803fd2 afa4e080ec67f9d15cbdb79504854b5d03dd22ca685e5ceb4133e0b14abf7d51 1029fd9e6d3458854ed99f4223c1d2161c540bea84a363817fc5b8e7ee8fc4a9 ccbd26cba610b4a9c3397349267b670588b75d335d9e9edb5b151d6c424d4c4c 61911b8e32f36c8816f4a4013bd67efc05937b9f897123f61e4eac6a88b8d135 7aaa07a56e7d6c2e8125592c0c0bbd8520124acebf6043cbb0c89c90cb1ff64d 85a35a7a16a10c1b3f48e37ef620862a11564aa96dc80dcf748d29031f641ead ada40d4aba58b8d7f68e64fe172c1bf527fd4244bc229dd54dd433776e2c8a27 8016327f09401bd6f5558d3a885d316aeab8165af24cde5db55a4e3897f116cd 2a09bfb08636d33501ef7b01a2b907537f9ec643552f89551e207b19f3191307 9b9cdde443552c29488449b6e7abe4a0f2aae000cc43ac12a18a4ca8c82ac74e 6501b5cb1a997ffb237c345add27f0b9b1bfe5c57d88277869c4bd335bd27726 7d9dd3f34f4e111453808a41e9fa1a1df0d056573351702aee774e42169ba958 e997018a8c2a155b58b1a32c7e51787005378e10b8394deab895bc329d5ba8ba 41a42e44c4e999025696fce442f78d56740163d1474077b9698b730d33260f0c560d984d73163f2b866046be195e5022b86856b0731d04aab7c581e0bf20a104f5e0200d4d7e0bdebea562180b610b768246b37d74c60ea1d5b2741413249f08c471ed8415ae890b5a91d80b33b9b86f7bd8f5577b6ae0f60a47aa060d76940fea199c1b13d3731accffa1fc4ecb2fa5c2c8ce90c155708abafd41c6fc99d80782f82153373a4cfb4ff9f15155f515203d0175d1fa24c5d62288195286fda70d7f9f62ac64a51c0a3438ab0464092ddc86546e32a5abda5ac10425ddf163e803adaa915bd7cf1e9fc8cd56805361aabf3ec4fdddb13bfc8e64e431398cb60b04cc8fd45e38baf960ac8769791684daf0e1b4ac073909c2c303aedd95a7b7410991c8334314c683103bebeef70d8f00b529b17fb96e564125bb5160d0b0bb010e6de660a3b5992e941a808f7db4a587c0b0bb4e6dc427238ba7a6bf3bdb664c067d56f4df4e6c4492ebc31cb47c156ff42a378c57d29ccea1c8caa4fd9bde2b018948096066834f9eb759e099dae3f0652c4da04597f2c1d09b0958a238587d051fc891dd1bfc276c07b499fe3dacd1c41d252ad789248a2e1a21ee113fc39c077c6c1076d7428db7b03ef4e70e10b5020b7767af2db16e22129b168238dc1508e9c23f133f132bc3fad897d3bddc14dd838a91aeeee407d85e2dc539c4e3350e6c378ea2b732b56cf1819b6ce15459b5c9a05e2cb4916bc244008d9699f8ec0ad2f30f12f85d6cac178ca2203f02f013e96b2a06f10d89ea0fecb8b0599b020af30adc0228c8a2eabaf2e9b6ae8ead1c4f703876e97d858bccd0629023b55a0c8be415d1b9994020ca69425f17a0fb7a42fb0383aad42c4f69326060889c4b0d5c6f879cebff46c6fc26a4595a955c9226e638168760b7a3e233dbaf287c3203dad4eed48edf6431f1f05d8873987506e53b254540ed23f2089a1b9d81d5a702166a1f27e3e02ef91808bb3cfcb356dabf946ea28066e260c43b8c6278c038091841cc65aa73bce5d62e1e30061872b27f24912b3e298cf1fabc6cdf04ba5a0e28d0953e68feb863847b46f2a5aa8d9569f9e070e4e1d04593607473d7fa3e01c961c26f60ea69f73c7d41500d6aa21f87c5cf2e9cf85bf429307ff34f97f705fe9c953998e2e8f751699ccb0fa4a57c16cc9fcc1ada273614377cc572845a06fc19c8536ec36d107222aae10194edf42f1e291c371491b1f0e9658521cadd08590139edf9a1aff851d57f9a286d32ba9d94147e419774fa2be22c4e6a57800b7ff3dacb7b5a5019cc116930637d224736a7c3a2807e5e9d32140da859cffc07371b9a246fb7a6e521705f95d8a1a2be31e01a6a8271c0345eb1a6d93f68f10d220ec16c402fa70a98c8914f9a63b1dd82d06d5d4754a03f842eeea25686c80f5957b097078c5bebc535db9bb19ed953c427b4d488090a800877a43771b5360f863e15d69b891c960892e69e4ab9af3c3407b5e5e74230b61bb2b49bdb9f1305177c4f2918228b164d2ede2b87a11015f6c647398f40d69db4ab07a021c90807953484a30fb32e06301f8b1a9fb535f2c0bb9352786824887d88617f8585440793d33baab15f90cae0570e0974e92e6eb4b6c89a79e7f884fc911f7e9cb7bd0da7dde6d7186475497da37695d4cd45cb458319380604cc17847e3e4cf0037107cacf63658be60a090922d6865f6adc272871453d6a3f94c00af952cb93a97f0edd7c681f203e77209f5eb36ef5747cbac733bdb6a3bd2531987ecb4af35cb90b7bfac64da1629b248cac5ab5a1ef440d29a6ebc0e9583f185c09e5fc489f1b04300d6a5c4d151596feae1a4e70057731611ea3096795b228dce2f2c33dfaed016862364c75ebe32960622f53e85207877e6da208abc782ffafa86eaaece3820e4155c7811195c7ef542e3c40c91b245269ea4ff121b0f24013ab041647729604da96e2aa2343c65e0ea50643f57c0678781a22be0031975de18b1ec913070801c18a5cf08eadceea96fe29ec6128ac68ec028fecb49e4744eea4d81dfa175700b6d7578f056f8b277471ed951e9c73fae0440d46c99cc37bd1cb6fabe5a89b035d28ca71e240e10ed8ef4bb008f638418f3940863bb2a95871bc1f12076c470f33f9fc5fa73a5cad154f090e219f837679ff91227c2ba093d0b8573c5150e802bfc96b90c2bd4ca438c1c27b7ee788c7a10cff2a927d4deffa9b51842696f902aa441bd2b59ce471bab51ba612b89b6281ec32cd5dc0296ab9c881c324675b0ee334326ae15714909bf12c38d191c710cb3c21342d47a90e7aae6bfbb09e020ed7f3553d8a805299937e24b9aec84c89e172ff52a8b1d4980bdd1e8be6b82f001a2ff9b635811f76d306f4e4d7116f9e353ad266b9120154a6656fa9a97ffa00c5a7b57d8cdaaae45616a826dc40534b7e4dd4f38d73065b41bfc114c6493b033e967ec8b8a638a4b2380732120a8c04455bf6556bcd3f341a302d9407670408fe83d108c910adf16df8da3bcd9e221f08cbaa737a759a0ff3c2fd29ae597e0a971513cc72ddd3c0f9698449bf940ef904a3778421b6d2f7be33dc7b2fce02000d19493ecccb537413df173fece9225c9affb9e4cced5f2e990844208c033e0c3cfb686d4cdd456ff5cfbda92a4b872a1a67a358229baa9b4f2d0fd5c9606e09dfa66661396d4b8e7a830d4f6e6d46e466dbdcfcf1f70ea8afbe70460f5d1e07bb06973c085535fb3ede6f82ff52125789319ba7c66a2b40d5d25eab804d4800887aa3826fea13564bb954b0f4d63f534d765ab9aeaf2620f5fce4298a9a340f2da018a302f4dca5e8277a654910fa2a2b506169f673835006ccc46f4295d007cab07f6e61cd82d85642b30989c813b6932199a77816a60b47a45042a85d390a382189238e36efbbabf6f46e654ec31fad8f6d958763c7d1d109a4823fbc160fdf39c09a1c729af807d5eef4da4ca47c9288f66f6e1f4c47be94050fd22424026e9826dfe74e08d38077884a246ced70c0f8067ad03f4b236e5b4371a6be040d3fe15857cec088be478d69a7fdb4e4c161754696a1e9b87a3b43c0cb24ae57080b751437b8cb91137a199af9d757aa84a5f67abc3a6b7fa81e73cdb1ded2400c10797c4375766d0324b6085629e931dc1b6200733113843b057b80a1643b3106785988b66af159f93bdd96eeb7bcb269ba84b233015971ce56369b58823e2a0d7517a80a1d2dd6f179976bb9f270e66eb5ac39ae8fa61c2c8eccbcb7d95e0b0cead056893e6bb463e2a8df58a22b858a6b5f24f547bb9102fc9fb359f1ee2908440cf08998c01ae8442686950b78234269ffb5bd2848cacfa79b9da6e028270d6db050f6735eef7381f116e3b005454ac53b328510ea09809fb5142d40e51f0114ddbdb3949b9d40246780e08d505a7294b771d65295a23c3450086544ae1b027b2981ec17a990183d6f966163639b82098e4b660e2c53a864b591e210a4e70ce3cacd55cab7e1aac7bc7cf4bc9b3bb72e220e26c36d8cab07ae8b74d7fec507c82a1b9462ddafdb6eafa4ee9a3041fe3c8e6cccf80bdd3525a0034ff2aa69091dc9b3d823eaf378c15cff221f367a2bb6cbdcb386ce307ceaafaa5ba6ca92080e9c3119b0e00b7fba16677b146da8f9f3a8b74f19871787339f9b40fb07460f7836b20ee2054fe09acf0b4b2c469a6b4a19f56c0515199f09abdbf7b8e7f70c5b6113d505bf99e1f92f7d5c83a6e443dfb9f894a064c0ed5dfbf8ce2ab2d90f766aea3459fa764b293e0cda9b737470a64b752335c882f0af7a385709d0fb0c379e537ff51ffbd01d056e81bbb6323f1d1ea52a0dfac05e5426970a08e4ef03f0532753291a24203a07388c391f7cedeb009e09a5fb0232100901b9b4c67100fb10cdf196fb6557fe45132e854eb4b2958c178c167ef5da36191d1f7e3ede03b4329818950b9ccf11ff1eb2c872316069227e2929832ceacf4625278bc5f30a4eda6c670c2861488e22fc8d5d7ef1dd8a5832751bf435c65e101d075bb2900393506366570d05db66d2b796169bd7ccb24b844fe33b228f23994e9cf892f00c6fa4990126b0a1125744a1b0d6fe08b4d5f0c9aa05e6d1e7a8cc0cc02eeca9058edeab18d5a5982230dfcc7425afa0dfe621aedd51a46f539ec630068a2e4c01e2c57f9558282070c872f6a27ba939ac56f9a83c84cf1c2fa6fe1db45c51f80741a4ec8bee8430ecb5e755500abd73309472f78feb505752e5fd4f870e007404ffc8ba1938984ba92f5596a9ae72b033d6f7c08f0b92a6c180451a1f787f1802996afcfa2b7d5d48cb5897fdd60631d64dd058c14af535aabf83a35fd70ed702ba0b0386cb9379b4887af0a23f3a8342b56f2618876525dd65e273aa1e9ed50e64f71b4c1300fad2ed9004f338989e2181647930a195b42272fd0bd9a247830681db12310d4dd184cc0aad4d5321249c23af0f6d44d38cd88ae0fbbc058537013abddce65d49d4910c274bf0aa0cf96f09ac49d8e07d01fe569f37b7a4439a0dc93e1360280056b63d392396ad4add67774e2df04f04edf9f2192a0bac06b30570629e83b8288b29ede3fd35f70dc613258c994c378a36cb25fa946e37612d02686ee07e9e9fe25dd5aa69772ab3e86560a96e8d95ae489fdb12929e38657809bd98b7a0cc252230bf8df6633bc474f137fcd9cb22de8ed6ddda2e23b8f3d90711e1551160eeb4b58ea69e90d6000de61fba48a1186d680d55fd0d6a34de9906dcf5190bbcef9abc3f3f0920f3286d450df3ad2a2c56ce8055f9c6cf89206a0392041d0f115345fc8bd0a8d2bc38e250806e28c5d8664eee444e9462f8d09000a2cbf13d17055bf4f23f646cc8b89bb2816dd02266fe427b5e1955d414606504162302f652f82a7ac0c89cdb56c10fa678bbcaeafe8db6ecc56fd3104164400de562ddd2b0625683412f04687de76d2eaf011a0d39df01eed2e9908a5c073f0a7550549e5c1e5ae05a8da78022e897aad294580f23d127e8ec9eaafdd8c92c0a8679d013208da9df23719cdd65ceeb3ff05d8becfeb972de6bc2a2bb95b54a0b27c816ec9edf8228ccd3c38029151d66756fe554203ada8dccd4db8c2197ee058297ee028a3d786c9b206dfdee680f8f04082729d7e3fdb158ca5da9f50b520c258342837715ecc4f9138c47ffa868f93d8a2264d27edc50f9ad4e559b2c9a09c30770e2d7b95507eb2dfa5e23ac0530d17834190f85ec7c53a24dc73f783901185848212a157e6fd9064bb8f23b20fecc9f94c1fcbcd20c8ce4c4a84d54c5027c71562fa6d45ffd9e71c449560a35141c1f703bc1feb1c1ab1071a5453adf0acbbfc3b2f9b207a1b9a0fea6ed752f5dd623e70ccc0ee0f9c23f2618572b5a0cf59af1747e5738072c4bf43abb23e2d80d716e9cf2e187a21b2168cfca4f3c0ed87e2bb0f6afe9c3d94a2038fcd9b27033f0d9c58982450148e075022da43309a3b5c47946c665f4906fa51e663dd405f3d4870d0af16b40f1c50f9b0329c40403d14ca0b202c5d10a6cd54b39178366e9bb93585ff5827caeae71e2278fcc03f1e2010c1a8440e4e1f2aabf6df05ec351fe39b99c488539396c3734923ad606fd201dba13384e2987004e24c994afdfbaf13d6c5db58b3c8d3ce4e8bdc52800d178ec81f87b9c3059afb4b646af8d44e50a0c04f08c34124876300da321e40c2c4867226c940152f03c15b63c4b56bb811da934e1fd95949ef6e870cba648039f70d512c3b17620862b46f3488cdc5553cb85597f883f705b706d5831fb9a0a65c2e0f3765571e17b72b3e58b9576bbb571891a92aed80251f5ef78b2cdf90a73e4a77643722456bdef53d23d4aa1dc604717fadb79950907ea8bb0b7138d08c81bb8fa4d0ed9346517d8930702801ce40893ccdc7f3505b9478af05d74230ebb9f95f2228d1ffc2da60ea7bde2a66234478a484c8301bd664fbede3604e4027b8a1b8357b9cb65b6471620837d11353363aae31a41266a6a38fff2949f980e6f62281201ce327be53b687edbe9759167956c646c168ed138332fb7eebf54067811a0c872ca815474a7f4e25aca6f9339f1c0b8e56f924fbb8fb952f7ec520f01c91d7e6e63091989f5d59ef9aa57f743f0dab1bf22de049b8fb2d741a0ac0a0308564e28a06785f4032727c8523b4241f505bf209d45ae6037da601e32da043324761362247fa5e8bdc20e599d4757c84f51b7a389da266324b9f13170930c51c17a4bad4cee7807a95830d3fed21845b970888f1e0100ababa986ac956d050b9a1a12c0db3b814e02b250b37ec7f722517bf35b7c4797bbca015e08ce950c25be234595ba2093c2d0097907925a4adf5860dc4b2480b4f4cb7cf2f900d109fb5f8d40db20ac824e22ee12b7cd39286bbb9cfbb1f4e956d9d7e76033f36c064b9c65211420c13093f09df8abeb0157f14c8c79510b5213cf6bc77a7eeb50081160f68df84e05212eac824ebe6b85bd1ac7a87bfbca7a01339ad07869719f0d8697d5ba4f3e7b8171243e35dd24289aeb8533d6cd618f5ca25de55ce468290c75dfe873c34c0b680a4be1dd137f53172ee6913e13b5d8f2af20309a8b217d06922436913a0ae7b066a2173bae68318ac4fd00d16984f860f91fb3e161d64f0939d810b6cefd61147e654394e4e9363f01efe55bdb26c2018aaf8dfaa971410afc84d5bc671c34667ce3b1c0dd59087aad708a68d827d62eb51f45f17f98a5026dc3c43be697baa4680f37255752d3763b64118fdfac4fec321d8eb461d8930bd501c6982891ba0cff28ec85798d2ac93e9aa05114ef0255b46ee599963b13097ac3b7cf54a52507d9665fc9c466fbed80d584073d8b1862edf041bcae2cb10371fdbbfe81d4fd23a19d1c198919fb8af7b2f04a3b3fe29d0e2aac9e5cb55d0cc94cfd8b18ba7c2e5a88a2251b313709fe48927d1bfcfaaabfdd14201686a3030cfc0234a70bb48c30801ee5d599af3225d98673f91b8877cf0939ded2744d00db86c93750facb1c188b49f9d28f647b1c82a332c33daf0708b762dbb6274408d92bffe7c50159f658646f6a1f27a988eabe57380fbf1f46603941677abde90758ac370d55c8e151cd5265903d0db1add53c3df62d742bb84e8f1b5ed975f60b1320082eb49c5c305887a0068d02061c028fb240d4696ac306154f4338338f0e2be1276e17c5039351bd0b72502ffb1ff69736b82fa31c19e55b6fe7a194240140a66ebb9ed7797f7c5db2ef02be250053a8533a1fbe54ada02e3f5eba4ca909894437c891a989d16f3d647333a1740658a03d352ee7e7b47af620974a68c50259bff10bbcd2b8709b0a989f3c73ce098ccade2886f47fd67cbbffa94c70a10c28b865dc2712cd6d99d80c824c1abe687d7a3837e8feed7f9a32a46cb1c9000ff2b71bf3bc5887a93aedbfc8750c4e2b85232495c47ed601b7792286fd350c08d7b5d2f699e1d45406e6ebcb12923d6b6fd1fd0b6f587b3a692ace148f367301b8d74f9b5536bcbcc89dd852e3d0f69dfd5b0aabe13b88c58b569fe9574ae604b454d34a8042aa0798e9f75e9c7ae11969ae8addde545a7e2341c81a2bb54e0a5f4f6393d5e76e3573e24834ad69ad03e8ed38e6a278615066376e9d7bff5d0e72d5e33dafc24a337e01701497923b09d3659fa8b3e33cc7dd9c81995e637c05c235550da5a9faff96a2787f782798bc037db03be3785520638d06d16dc8b809995943e8e076f2a9e40f4e9fdc9d3b60ebd4c7364881e4d06f1b3fee6ec9b20a04cfb72e3a1aa584f706c234cb957a73b4651bb2e2cd6d545545c5fb414d020fcf0302f15e5acabe827617aabd325445410ee0831c1ab96195924350d86738058e84ac820231aae306162ec7bb5d23cbc398940fbab768b4396bcd6adbca950c83ac42dbbcdb532f68192b174a0d184c4adddfdc3d4ea700940e658320f1bb0e5df7248b244b868142ae7e304d413651686431936bbcf3a21c78490e33c9f80bb5d2101545fcd6617fffc55e81dff88413c1c39d5fddf9d48fb80ee93ca9aa040c56fe2dada15fb19cf659ad22c6b31178d79daffb5f2cb61c0b83305b3f8400a27b04b9b4b09cd0af8b343a94499e80c0748b2a0e00eb2a9349f1619d7de60c67ab2c02d7260c2a3d8d65bac55d0982964b0257348eccf6660888c57a19ce0288c8ffa47b728b08c5c619593d7e30ccab9de83103ab6b0f1b170ef310df710893226d8f146a7b019240c45ef63040fdeeb3c915feb2e1d4c74e44fa075d900855268d761d1a4979b535a4dca35e57c44fd5312d45327d770916065451dabe090fe7ba96e6762a8ffdb8575d09344c3bad151505c2a1418c5c039722c277940cbea6ae3e6e36c102e62ced14c591d1436abf5e7a87d09e2d6fc343b2645613080882f8f98f9969eb8c2e82ae9b49dd14ed08821617dd6d73a80123f49e435c01d50cb2bc1b1be273ac89040e4068209e45009df170b633cb1592e052150dca0ed6991fd81e5d9fd8f85e0bddbdce533b6c1651209d1de20c55845709122cea0cbb0b0e3fef7803ba1b00d360b27ee7a77109ff6ec6287d56375cbfb4b7e288029685672e77ad45a56db3b3b13097706dadc22c2b373f4ee5468a0d6161e8b00c5f6903aa549f6df50f12a147409ae1968cc72ffedcdbf39e7f9eb63c41d9330593fd64822f1d6c0a39979999f39428d535c26b99bff03d8f6a5a6f75b0c8c3052b93db222b6f6558ae7d88e684b364b1a9e3f737de16df371d743c37fbaa8c0e576a24db3f9db5b762333792acf553d7e078bc49fcdcb04ad014f2cfec297f0da172054af9ffd4ccd7b04e3595ed75908d6a63b860095396bc4161ea3ff73f041ff7407dc5b690fe5a76de5eb83c1ee2e2ccb8e591bea015e63e04d05c8ee70414fdccfe99750477ae44673d9ff9615b55ce563a9734ebf1ce7aab0d5b426d07c87d5204b042706718a12311afcf3ab831945faf52a3c7698b74fb2a7ca85d017eee843a0407499a8ab61c1e7f7e433132dcb4440e2790cae597ef244b371103bdd878200fbeab4755f577bce5ebf2dc4d6c8563150e4de5c3d9f85d25cbe20aef5da14ddd8cb2fbbb09e932573b63fbd9660f041d72db79f515cc8bc43b420a2b24c4f33cd7bde8521ff03ef04d1f5f6ba92d410e4acb2ca736fec2825f7b02be688ed7c8798c497b548c41e7addfbdaff2a8567ea2198521b0b7c87e14be0610645d04de31026aeb63fb1915b52e38323e2fd43f717a68c06cf52fa54bc1015371aea914444fba7fa93492cf762a38f98878753903372fc47ef05a111673072bdd66767cb4cb0bb0f001d2b284a30d40f56d8ef64f96ee3d8b58b5419112087c6dab6c16d0003f1141334656a7eea08987913f7b07697eb140508d4f19440684a72d5ae2bcbc4bcf7d1443447c2bdb4f81f72311dd3ecd82924f2da58cf2071a758833996f90a3b74188608fe2269eb94087efbe1d6786a56a963e95df6b010347c553e208e5449632c01070a58cece1c20d39677121db26ae2c70f703dd03721bd826f373bb534b748aa46000fd881800238d2d60a46cece9df173447210144663af30f6aa0582dc20182e812144cf716ea5fa98c3926377aa4f574616702727f863621223f483936ad659c934c2e14e8acae7048deb4e3eee70283c8b1000a208eb25a93b7961989f3a5b2758fadf13842f27ad3468d9794b0ec891bec03a2c146d1ed37cc2e5e7a7d4c5c9e5435faf78da519ac0c987fe1e1419e7ba006e833c6478cf14d65f85fa677af80c37616d7e91ce4085e593614453bd729bf06957cb32fa0fa5fd23214381ab48aac515289ce6c9ce061fd42d678a90b826c0a838d27e9c8df3846096bf61ae96dc78c4fede44686beb06863f9aee1b4575309f4ec0f07dded23213fde2421899358889e72345e8f33a146c87d115c3e258101705a5ffaef763b2e6960a5ac26aa298cebbcf062b0b4a02ff9e41066af53ab05dd0d61de71d60769e2711402293d7cdd292a877d8b1c1c5eea0d700785f55709a91e7d6b7436c2065b9005460820b12c3dae90727c7fd8637ec45cb4ff871402d5c6ce94f008e51f7392ea837459868cc195af50941688440bc1c4b281ff6c02f4f36b0ba346b0adf51428741c62664e4191b4e9d20359b5053b91707d612602d19fb73b922f16dc920876edb3346ada8a2b8a3b0273dabce41915884467860f69903ac1fdf7010eff9b238e78c77c12cf3e6247932b351e063a6198c956fe0695c8bb98631a08ef262c6a4890aa1416b390404569b05993a8c56f04548e440975fe8b4ef934e0d1e7a711b550cac41ac2aac7703ddf08532515892224b68d0f79c7a2f5ed26d23fe32a69472cef4d82f46c038a996f7194834da8b92c2a710da5af597b427757e537893ee6ca589fac45cc3fb7e4c4a6847e368a95b004d303cc25a233e49d84a62124da90c3d8968de694b9770b2d32bf3561abf2e72ede0c5ab8b1eeb6b1ebc1d8411a106ba0c97b3a74646b2efab444bda61df370c4fb0e0d3199890ea29846563ac922195b9dd11f733cb0a5d5ddb74c18ed6ab970fd003642a05aabe88d05c149ca8e060a1c8324dd8147dfd85b7d7bcb66af4ad8730f35e74a832df2021289e7ba1dc5d01063e1c739ebffb7aca80dd1f9fb17ff38056449b40fc3aa1b721ed21638966335a165457348b44be4dc653743928647b2014f246f751949fbd122ade6205138dd5596f3566b9a7040e29c07298c70e03e0b76f8cfc923eeb8c0c054387573fdaa33db3ca7c2acd2b1c7928069b99bed3e020028ba85bf1d078ccdcf1588529d97d2cef4e188a7c29fe884d199ecce99c6077d4465be9299bce7c4159e8deb6a032fb7e5bdb6584fe5d06329279d2d7af600d5e1d6c427301c208d53316b8503f5a9cea5009769382865f12724d53cb9e60929b9151416f60ca995dd5ffa8aeeb4b4436608c21238b3310146db35997f2204c51b3830a7c230b30fe1af0d1b295e1863d3447b2fd2f465546ea791dd06460f73a1d966e884758da043e77ad9c18cec9113087e017c321b0e44a3ebca6fdb0ab15320d606caaca1725536ef41526c7922be14b71d613dbffed73546a7332a02fde12d74544f1860047ee2f5a086385f8599f45e9b0702586b554d48c1f53009d88372f9c28d410d9f8509f351e615e4c0f6b1f8afc38fcc757811ea073b7e06004770db2ecc5aa9dba6a0ef0a0b0d5ce626ed687cb8c578a0a6b287ee274f0a6c0c179622a585e9a1dd046f719af6fff2412e0f5edaa9c817cfc5de61730301175efd0b4e6cdf05523fa55f385342fde4f2d9b620f0a8cb173b1827dcdb610659114e3a86329abe97e8c1bba0c28a55296321e4444a9480c596fb2eb13a250596a8a4d66d33ae3f00ea276c17508d32132a6a3819ed97a5ca65603653c72d05d490eda245cba0e976fbbb0d7b6368bebc87de24b3bf98754bbfc77fd788460114220bc3d00821e5c2b69f3b7a19754fc1a6edc619b54ab3e8d772b0eaafa10ef02b0fe362c7b9dc7de65967face62c60281731567624e367015264a028af20f83b1d8771334e0c2c45ecda7329256ec8f520952232f7460f75bb8df3e272509fff59b920debcd27997b3163b96bac40d7e952704401051edef2a322e06c670f2dd89d901accae6086d31d0df712d14290fac3102351b00593364d133f91b40ee0eeeeaac394e86d0a8b86542c759c0bcd71f26adac4f08d759b1282432ef80b65091da720dd8304a473ed1bc304b278ec3d6e874e3f022d63d582891ef05f0eb207dc8535bd95ba0e64ed8e3a6fee2149ed5d70f19e5e4428a31be5921f7d04959e2bc06f5545f7437857c315034cf9eeec059720ead08e1f898e49210afe0d748547a64b677a454cca8b3440c4efdd62f601941a94fa3b041c61ff254e7f02d09ed710366b1a78be746c470c2b7920477edd6d470604847a4e23f18ecb690300985279226b1755adf515d8ed40d9da353a79c118b1c59966ae25931a628b058b4d6c812da6297525676ac1c8594f8e622b23690cd78781c156afc7403df40796ef6b1166149e029d4bcde50d7f61b5fa64f5b62d139ef421d237f8a651bf05c2882c7bdac87b7858dc796240869a5e40e1d4ac2327d96cf6b2c294c0690f0b8b08f01e5ecdde9a9b5f02bdf0cfa27d34ac4c3249aa4b573948148ae644ac00dfa8aa4da4e44e0b59fde59dc93ed530eb1aa7c6c744f60849361eac6e3c1f047453d8765edf7e1f726222925034849f4beff5939665f0b24528a4e9eb0ab00747632ca12fe8322458f826897afdb0b39958ca3cc6f50f401edcc61c79350a0ce15d2394b8ca6cb722c23a22859b7b4a1c7554f21cbf2b359564f2a7f33500020f113572b05982cda36362f72ee694fbc3a49fb48c38d8e848bffb3a3049e108d893468e452da0633e1d23f6eb2dc05647dfae8099506249f8b91ff9d0b32c0abac3a2953a5b61fa1ac52ba10f93179e20dc2958885a882785c46256a3c683075fa1655df5c975231d570721d626870940bf26c0ff698f6fa3fa374ff8a94d046ef88d51b750cfbce4c4ac9d5c2ecb8a00d91390039e1972a334cb8ff3df0a0f05e3f267b35ae893fef320b65f635f5cf78e91948a58b7b921414ebe6dc3ec0d632ccb682323b7090ba818eba1dff7b919dbc8bfacf7354dd5310672f6f8c504645470e7bac665520407e8ac54f3b20b6076b4a159717a24cb716d6847107c006e645affd48e863fa49994575f7aaa3f70f553fce1e264b60885de9631396e0890209c160ff1d503b18930c9c065b28a1c44df72d1ec966473b3e8bfc383a50dd68f630667f7d800a2a3fbb4fd484aa7937065b61bf82ff1b166d7e0ebc2ab02bd8a1fe7a5592913939dd40bf3f0af79dd15593b4a671093231fa772a95b080796b7d86926a4c95cc61bed609dda22e0248f3d238c3b8eab0850a5dabf6d1a0f7b74d7a9fae2868d0b0c4d4bcce242aea9041a7750e4045a19bfb35b7e28e20a3539c12c99c7e9db4da1cfede78708805a032b68cce6fd4cc7d3234d6f6ef0000a89fa84c4049035a930ca23445cfe32a76872dc4b411e4a48da9ddac3a4510a3a4834f7b3c0abbafe01122979725c21ad8c1a8a2db93af848bba39492b21106d2fabe495bd65af34fda5057ed41d0328194173bebb2a7774a615489537ed001fda9a43ef991c5b82123f7fe8e213b80d62c63a40bc3a93348a3b9bb0011d10fbcd9b8997175ed4cc75a485cca8b9ecbc05eed90744e844974f699baf58e550abd83032608c91835c931f033fac9a1adb3327510f3de155f2c67dc831fd14d0dc84c97f4b3058b8c38d269703627bfc4bc78dcc1df75e550aef7cde945fba70ed2f4638aeaaa637a0c63374d6df6b6a02c825a7fa5e4df5def2123309700a40f78a8855d0ac4afe9d95176a272e6e2c968a6766cf94b3485ff5f268079ef7108059cd3176180316dae297eccc21bf2f71244b00616f0a808dbbccc115716e00c5b3e32d3622d0911957990367dad490e4d150a78ab546355080738bbb25fca0fabd9e167c15bc1564f9ee8f5887ef1b5dcc6e19d27869431b4a1206f61ffa0051557109344403b5ba7869084434b8ad5775981a099aaed847c423340c022390e19aa049773c27db485fa4fd24e3b4c6b4eec470cadf10c14a52fe9ed52895804a6896dcd2218c789ed192b70ca326cf836ca031e25556bc839a8875ebbc6a207c1c7f706d74756de58bcefcc09bff3519a222ab3d550e2ca16885237d574c70f77f31c02d7f935b03550202147623c2d965e93ab8193a180c82a55b30fa58a0d8402b502c5beb7bdcd10246101482c226423c696bbca276006fe1daffd08e60d9d59099845c70e76a9615291d79245bf15b21e473af7e0904cb015798918b90b4f20bc6022fefc022b646ae77e69e47135bff7f8383ffe0906c04ba51c301106fafe76754d6f9b2f9ee6801cb901d9dc0550143398a6ce2139a546e7affc720d9e80d46712bb88a4c6846f657395ecfa8498fffb2969c49dd56980ea9bbb8d017f9a5d3df7850f443ed56c9aecd3189557b72c1765c31f4c5b5b40503c1981001dfb98f677308ff5064ebe9cb095eaca64c7e795a084a32bcde071652856070567eb25249c598e9e0e60f0aa498cd84c6277c56abb5878b9a460df5525a26704bf800783c94c63d5d051412ab3e11d40ea419d2c3810d01542e74b4f0a69270bc9e5392df458baef38989cf868d4979addce11e7a21177b5ab2c49b94bbf50009ac2c9c07417d91825d28f5006f7b8de17a10237d28046326a3ae6bdee4d7a0e00afb00fe783f20ad9f1fd32ea4e0818475dca1e2e0ef143f9a8efb1f0527b017fb0683ed4a1dda8c466aa26eb5080a6ec9b22caaa33c2bd86ed2c1ae8046a04d4b8a1c4d35ada72b5c8f195216bf0ef0397694c6c3971d30f47a34c6d94d300d64d41569e0fab658be6e8f34df7e26fda42af82c5a933c0c36b6bbe0f5bff0925236a7476abec626b08bb8eb6415ed17855b6175017bfcdeb931cd3cc831c09394da3fb1ea78ef0742b87c20667bcfee7c34309b0a113643d5624eb95d1020521495ad622d46a1e8b25dd0fca16fb639a95d7e897beabf6bf125c1567507a03183f8470ee54d7348bbf14d7946b73661c22d51d417185f4859db7330df14501a5ba1ee4e812509dc9da225513cef19104f38f0d7952800bc3aab83a0f510c0228a44eb272582cd881f55b845a27dc192d7552f6ad9f1a814ec5e52be0d7e20388bb1c7c10dcf01ae40ab38b840ee630f52eaa8226d62232dc00e887c353690280081c9dfdef0504202c68f341ce0038e1ce586b3068e9b118c73b099c51af00b9ef6c0b7dbe59aae294d591a9fb311bbc878536856e082331fb0044f4bdd907dcad9393f52f44eb6ee5ade48e76063d06253d50db03051e00de69587ef11203e2fddd0d2d0f439f887ffce0e1d2a5b3f979d46423d4e27b6ff77858f3c874041b2ce5aa0d664816e32ffcc6a747cb96500c64202fff86c58ac6c44f74716e0d8ff9cf97d2180adecd3d23b754ba03ee7fa0f837914be567d419c688fccae80bb23cb1750fcb6aa06d86637749114f779c249936e4372a9586d6c6e6b05f89098f342a111c4077ec2a65b7c8316016e1a80b4d6f15a4d94a046d9c6dc78a650b118a50122c1e02503feb68bea470bfaeb4c76a2db4c76e677dd09683f94bf20b85eaf7a88376f562af428f539a4b267847b2c024db44e9ff75596e93b6dbd20b2fccf4dd4a3aa3a8e3cc41250df2233c81f743f3a54a118534e88d1968f14307e2101df233c4417eef11328f15d09eb33a56c50551587fcbaac89f6c9bb44c060993129c9b048da587fae8cd000a217707897fccc273bc48315d3dddcb984202ab1a011536b88ac9af44be647b347ba8512d4a908f36c5d95575aeca3727fa0b148b332d00fa95d93928fceb4ff7ae2dce3893358fc301c1af2308660872570a8c1b3548a8768957cfc98cc78945493472a6924d00b1e25b6265b3d0c2a5a40db95dce15e910dabb9efa0bfe4591b0ef2526c6eea235629c4f445f0e6fb20e01740823269c252cdd1f430d701e5dc6b2b77a8d5ee76e17a00c9b8f0d4ffa3f07c3b62f3f2898f52932251a685ce94a334333279d49cd797c8f550e5c99234b099cd76ec53996fb1bac80c8833401d7fdb27e0ae5c9930f2687133c48e01992056eedf4d83e9cb4fe5730f51a2826188529811944ddc41117a638d8ee68f74200183295581eb13e2c65a134c3afe9c06f947797fb71a494d4be55e6b68448e3002d9a03b9ecab7b080aeae2d708e1c68758ae0135a9e9433bbacc48c51f0c2b0c4b13033cd74af6d023633cb05a8431b8de9cb1f96d5a44a75689c107e5da2b07fc7f6d1702bca0476951969d08a0f079618b80f49393ff483d42884cc824740972165ffa63698290f7407209a3703127b3eda093e7968cc1ad300d14b6cb0d019dd99e822c1d21884dff11dff6692f2eaf544d77051e599ca9e24157facb6c05b1a82fa598cfd2f8def27c73cfd0acebeda7511c5e9e3e9cd08d654b9a211f09a8758f09b35fb46ab3c20763a8e8a6390ead274091047e8a84d0ff69dc36f90a0d670a9c547a097675f42456a4e90c271257ff3fa1271ffa704a48965f82c80a0f3f9430609bac68b75e610eedffbcea023de3f1f9fdc32d7bdb35759758bb099620eab3827ed5798f55e40f9d2370f81ff0bb43ee7f959d5945951a3476380aac9e53dfadb4f366d38681a74b9180222266136416e6bab6f3ff388242d52502b5e9a7a32747272c7a194e88945d9631ed6c062d6fd7f05e0307f24e51d07b050217a20e0ae470507458273cc3290fd07e7b3876a0d076c5fab56d612ff8310c182f22b8674489c208bcb914cec3e5a922e8f234fa5a5e9940c7a4cbaf0f0e0af11cdfbcf58fca97dba9622f8468060d7c43edf12b9b1f156f07f53d793be40e false -check_ring_signature 023fd47514cf450a08b827bef3e6c3bfa5ab451d07d32450cc3282cfa673e902 ef8564c1dcba06245dc1cfcdc75031f10e1a0dc2020940bad64679ebc2cbf5e2 32 4b8149e2b39f7e8b535b72876b034059591daff044d451df83fb465165bb2c15 0206bfa00d43756012fe104d42b2e4795aaf8b4b7381761436cc72f77be10b08 2296bd981c49b1bab0fd88d2d20635125eb514663e6df602818bbf4f9dfb53c0 0e2ce1498d11412b51be78af3ebbfb24e8f88960e24556f5ec46d9043d9f9134 8d1b4fb5d35b55c5d19b5c2e17d5f97bb8d05b9f16e37699a8dd9b319ede81d8 f2cd95f099f673ce36e9d0300bfd64ef13f42a72f4ba644b9825478a25abbbb7 98f14885da338bade2f679b293eb87a0b2a4142f4725a3776e5d5fbf9376046a 5427916a989c72949e5268ddb3617eac255cf4bac9792df7215e85cd9d53b3ba f79eaa259b026e5c506e6844230b01ea3096aeb0c3ca78b61fff6ba4bb5cff26 cc120fb601bd341c637ae69707a2a040c1bab044a6935f357ee227165306982a 38cbf123c5001ced8956ead653290888909d3c9ebcc8678be7817352ebec018f 5fa35d6f1003d37240321070b758f6af55a5f448a7753d80f885809cf7406f15 9541d41580594c1b20b4e33ea75fa9066e6dedf5b91d0c3ab3ea7dd350d0a679 85f05ae9df65027fd50eba86f3ba5486e97769629a41f4b9cdc70bc3411861af 774cca36e9747382ce2fd5740fe734f2d41962906c94adcc7c47586ac98e1664 245b4dd45e800656b7a132c189764bb992e038bff2f0f8ee6f8ebf28d97cc250 d24e9780bf30f1c92214a906694a29b1fc52f7f38a5cd1f4c824996b049c7f2b e0e5f303af9823f241ec05859f37906e022470a8c97a8ca8acafc8f1070eda19 2b419b5d4557ecc0aaa3d5f00cf1294435d4916daa3b08e0a160ee4664109a25 9a24d6bcb53e610ca3bb27155c238a7d24dcd233344ac36bd43e9dd308161500 94b0072fd69c40f4cb23e9df36fb2b67c3907fc11ebb0a9846d0f7bd83d1c84c 24c2ba9b9a17d7d61d1c45995c69bb7ad91dd425499556c76f302bedc11252ec 55f49c783922e433a6e465283670583ea27c309560317e53481431606d845d37 83aa732f5f22382f9d704d80ab2077a166dfa5690ceed8bc8aad3a52e36e46d7 7348f4435bcd58ae45fed1c0bb26ebd273e941dac33ab7649e0a91da75fadde4 3a597392f9eb4dda42032c7d91d24d3cfb3c9f6d50ea85c5adc8174f78c5fc04 103ba21af097c6ea6265d56a956af038fe430d7fed70aeb22b06f1f31f92508c 03feaffc47c0a65dcac3e700b492b5c60ea7e25054c41e2e90930135a55393b6 de8297c80c497bd7539f1d68e44c2b8c5e5a626d2b830b868ff7c57f731891a8 f6c6ab844d56c2f2fad4e754c66c54a626a1202003dba3da247373a2fa70d765 a927715957b740f871063314d3274cd7e4a716a914234107bb33f6886631f06e 6c16e0a1870cb68d379d7c320572851364058b1e27bb155ff8231ad4f63385cd ce0ab672574da2612e6dae2b578fdeb571d246750aed1d7c61c5c600139b3a0aa810bbfdfd1d9d369825a5ec8773fe44a0d0b1eaa1d15ef12d7cc38f03eb700a1b4f29e24d51dc6ba7d4ea6bb65cad2f66951051ce3bfb40447e68337079450abeb7ca213f7959d7b432613e227dae2b186b7be00e8f0286c513d855a964f2058daec4218c25e1943651d0dd137ea9cc92eb17110c34eb42a1f63c3362243a015f365d7312ac4aab2c431278b2e8fd65d6f7748eaa22363261bed01578f0f80bf8cc5e7a602b95b4e148f1501633770614a7fedaa48f5c3f49b84fb077a10a05763c3bc7ce740871a1f1f3dd983f31b941aeb71e5a1f998152dbb11251e64f025a1546b0a39294a709e12f04fd275b3f246f5ad861df4daa08909fbb4a1d6f02c5d4719737d82b529c1d49eff5000636a22dad37ed7eaac6c484745d2ba1000d7382e34786c1f70b515f8956d790edbc1e6f4d43cdcfa8c7d95d46737492d3062ba388d1dc6d517176ca76f7b9d617e61b8606792f31db773223dc48ac51b70ec106f373cb980f025bd4649a988d4ed764013204f772f0a928301081488fef0f08ef27c9c6362773d0ce878f62933787740abf0037206e4be44c5071bad0fd05241334670698917971a333d6d992359c5619fa0ee80fb35a7fc2ad7004dbab079f0d0aa798ff44fa9a430f31d58d2a86ee5a420cf78e39ede6eb65c84885400781819653d5ec2397c92ef1f3fcc5597b45f768c8eff8a9db2e924ece13f10c06b2155a9922086a87fc0c52ad89312732812e0d74fbf385d66f426f445320e70e91162c688843d2cf2aa97c64142006ed826f3b80cfd16a1b2ee049dbdc26e20ae9983ba48a984902e4f26ea4006ecb562247c884beb9cd26c4ade7538af5770934fe5b8fe68a91a776fafe401c625c101271fe17b1c9783eafb906c3796521043bcfaaafed5fc807e9fc1c827ecd32149969a5641d7f2710b335d1fd040d740dc09ec33ec22dadf02aeee0ce8650899d8d2bf03d9d565f0f48b1000aa943850032fb39bc2eb582339314def9e8aa6f1ef00e962cfb3eabfae560f34f97c014062a8c6bf9c99f84959d2a9d2cd9105a039b1f1f6dea0190f9d87e50a09818c1023147a000b8207721027fd894356391400f6b08d96375347bf26d924193d2d800161b26987c07afa0e2fee7f7fd0e8168f9dc0fc34a5e33e57962473da3450508c83b0111796d5f8b196a739d569c8024b1141d1cd0f819ec802366d20f0c1b07c4442253a90b39b14335bac278740f96df9a4ac09a897b636c094e2000fc540cf541979a0dcee0ee45ff3938f46808b5de1dcfa116167b11505c5881a4774b0c31eeac1457e4be23c9510eb808d04d8f89d4f181973111e14b4253655dcf450628da05ad8d2705f00ed1b59f79a6e0aeb153d86a7d776fdd08efce3d02a1470d707903ece01f6a347dec6cb2a9db1aa42890764eae0f73da422a29fa3bfedc0f373845d32321f22b6481a47449400442a278e3028c192f52ec1d7a3facee8b076be5aa99d083d5f86fb5242326b595fe3bbb22eca4e43d33801318008ec05c02bd10286e02e2ec11fb36509901ec34f3c4219dfaac1ff83b8d04a8fbf661c208fefc92ab46993616f705a35eacba59f9ef53c4338fbea4995f1d53e7ab0ed90944a9569c2bac2e0efd1c15f49c4b69733c19a39fcbf64e3a90e4e7ccc32881012f94537513643f34c0109b5ce3b3a11f6dffa0742cf6b32683c0c5198cd7a30b5783dba67942e2a7a0e76f529f1edd644abd97e6e009bb56c67ed08c415909004e234bc7f5fef02f0bbf27885d949a6b7e8c9821a6f7374f2b0d65e64551ef013247cae9c6e0d93567032e9493e7bc88f7ae52d152fc20bf7751b1a68512b40a03ab0b4ac8aec0e9f9202769c6caa4fdda2071fd55380e5c485baf9988cb5200713f45dcbc967f574a6b7ad0e90e5f77866c915ec4ebc5e82ac4f1388f05e40efc637337165d6c24d334fb62133a71bf04600be4978d81ab8fd31113f7958c0a8238e8fe79da700d3c1bc5d32f8bb7841c2561925486b018b43afa35e4a6710dbc1373b070de95a7ceb5333da826fec06f71da435dd69fd666cd3be5aa838307a461a61ce840eb38897b62a5ce8f2b824256876ee5a6eb3008a1dc31785f010aed70befe6c2abdce77531ec838c17d3af7532e302e43c30b7fe723031660e50fd2be7f883e1ecbc57c02bee53a8d3603e41cb090dec8cfd43c623c08637134078704a75584b49cfa9f2e20eff3af213e7e30e611e21f119d31cbcf535051030ef51dc42dbe30681950154f96808030e35f6cb0e91dff6ede300ca30a2ec5680d023997314e0bc69158598e83ea717f82c7bb58f54a55255cd2bf689592a4710f1ae2562f6bc52a9ba1b122d4eaffeb34a81242c07084b0393fc8897346b7e90e20408343b21ee1b85f720d862fbe09f09db12bdd13d03bbfa00351c9d6de460bf93f64bd6eb607f53a6cfa6c8ed82b14923030458a3ab59b44f017ad5790160590b432f988c078a9a49a2806ad6c7852f4d8cd18d0149daef911e3e52f765a0ee3375852b2f6ea7f64f2d836437a9db310f0412eabcc12f8c2613baf7c4a84067a04307a8b1cfbce4848c85bb93ca646ad2f9c7b442dd498192421877a6b5e0b8061a1e9e14cd2e9a7ef8f39e9970c7d57bc71150eaaf75c978a599b7c245e0045c77ddccf219520ecc90c69fbb841fd80beedd3c4212371ec1bc9ff89833500da791b96185abd3d11b6dd4e5961b536dfe92d45aaf35c50f09d8cb01fc06c05d2f07fc6361169c85aa4bb8dedc178ba5fee6768917c34603f66d762320c6782b3f6aa9660ad76e7a4606f1a883060a9d256d34ba5622fb595e5bae40b7ba10e false -check_ring_signature 15a4b76f4ab52957a26ba78d5d3b8902ce67e9bee2f5d416dfddb7055c5de203 effa6a08e4ce44fcb74829bdf5c21736caabf3ece1c983a8112d41df6dfc0c7d 30 16452f460b4bc2335f6c5adcf79b3e0572e5afb1b2cf3376bbdb317bcd75c544 d54b4344963b83cbd9fed57172e6ce638511f1c72e40fc29078fd74ca08ab9bd 2f0f204e196c2c7104ba913d3083757ececa8e207a4be73d38d375428a4c7117 d1c99e24c4ffd066bf155203b1a6ad80474abfe73190e88c93571cd09a163b8a 2f193eea6f6bdc69d829a033831eb8c34ca1c15a82a2ee5a42a43963a12551cb 482e64ce1c852564a80eca2513b4173ab8a565f945d32fddc7dac67bb7ee558d bc1938b128402f0104bbbda91e6bfc352de670b2ef67fed1aa12a14ee646dab3 26d2677c955329f7eed1b8c3f50c88fd12326c860c86ed372c0d646c056eb7a7 016476eaf6e760f65986032974b9361432fdc1267f17634f463e2199941e3536 dc36aed5b97edeffd84a45c4704059745c3739478f709aa104dbb7bf4f1598e4 3a477b87bedaee56bcf7b3fbca2d0651d4060269cb50414e905fec81701990a2 57c195b0edab5d1856c0935781dd3e10bdb90083438624905dc14b2fc6c37440 df73cedc38a9b083642e949269470c0382a9d32a742a55b54fef241e94955dcc 86cc77249a358216841e4b4f603c2ef0d5b2ca3f7064c3a1fbe33ce27427f51c 37fd12b4a9ebfe65cbb74adc78c994eddbbc795f58a09b853279edc01cca0433 8b3cfbe2bb920937c5ad4a168801327efb494fb195b3ba914f673f8c581d50e6 941334074ad539d8df23315958f96d2ebc37cec900f9e77ccff0f37aa089e21c 9ea3b26e85d02e70afeb26bf3d63d092e9c28ecf1450d1c2edf0c8def9c05aaf 7a546784d87301ca6eafa8307cf01196456b8e60d6593f50affac1396d95eedd 3caf7cafe1fbb88569ba004c85fd219c832c0f9e8d5cc2b5f0f218bd8d009a08 a121e6ab1e4b0cb1eb2fb73ad0ea39d809fa2dad68a9721dd5f20c9a42d4f9ce d3e7e21fb6b2ddcbfbdfaf405f74bf019f064174771ea68a0851954205000f86 d0663b8d5f4fcf3185df7190b4019b5c34b579dbc4137950896e965c3846c585 6889acf03a7174ee407c8a8db54d8f2a1f43b66af006b820717a7b83698bd6f6 6288857ca4a92479989dfe8b87d31bd0d3e3b1459b7d67709653ed0900989301 de6ca839c0cc5dc2aa2ed02d8d61b0dce6479068e5b5306c16916c5559822c77 1cc6365bd0cda9b8e13c3c5e13aee72244fd02d60f7f81190d9c3fc93fdfea54 c707c1bec81a3ebeecb8bbcb57e9ea28e314547c3a52ef42c1b6763cf425af30 d44603baee7940e786cdc1201b35bb9ec83562638b9ebb110c89043881d7cb2f 8dd007c2cdf01d43fee408bf57972f81e7730990e39f42e0b987d805c1c001c6 6bfa696c79ffd6aab0ff7d22bb202827abed0c7e0471a177c503f65f3641620643b73ec9103f174d9f2f8b107ecc67aff07a9deb9c415fedbbea4ef574c506019659a5ce8c6991a55c1cee29a185f674931d9625476f77a0d4d9a9654219ff08313fbe468cefcbc73f8f168c79b86896b67c62dc32490d3084ab5338bad2440a87265df8861b8cf204d2ae261edd71506307efa594464ac34f5f8ef54c9e4a0bb8cf3e5b2b811e694eafc69c83824f3956fcd05e637f046196b4616d68555b033a9eaed64bd5ea5efcbe8d737c5c8afecc88366774cd55c51dffcd32f54f8b0e8cffa5d04ce3ed136195ec051bf3ad20651878afe276e7e7db860f2a6085de0eff94cbf89b5774c985d27ebf2a4c7f12a52ac239336659d68332522672d7be04e80c50a72757d9ecf401e9db332e58d13a44040d64225451a0f98c3af40c2600f37365151e51dc540da686f9f7ac0a99cb1a5aebbf4f548378569ff7bea64f0dd0462e9d00fea5fd1237b4d14839edfd8d96761a0d1cef2ef6759a6ac7a1d504681eebb64e6b2a9a4285911226eec1d43aea304256a4d7b4060c46fa7d641e03db18821500df5434543dba51af2f9009ebcf6f591d6e0801dc549f3c63930907a24de1a65b951136e7e4d450ea1d3b78f067bb7da5d28bc9650a10ea40e6020bb6c0458bc2c4a79a2fbff8878b0a3633f180465d3e966bb710ffe34d51bef00181123bf54d5f2e8b26fccffbc88e04b6ab48153cafd53ab7467b2581a7a709075e9184dd1c2ed583e39fbc13b53e6c858d00a3a49ae984d09c43b6a0d6bbc8082ca04edd6744badcd75f76cae73d5cda15bb0e68996213f852a51fbe5bf1e2076ed6e921d0dc7747ec9979dbd4bfd56d6fec9c0b81dd4b5401c447ed74ab5b0d9c0afe914ca561d9b008c12d489de798d335c4002534401a3d18fa7b0709710d77b3887f8a5eb0515d12c26dcba52b4e9b0918e5f45d1ae6f68161baeb4d140542ac436652f4330d971ce09a26885d645c6032429ca39d5c1fb937d6ea563c0e9567b478baa2acb1fb149c3404fad262d81b8f2db997334c101668e8aa2429070003b9298b42b219ea729006527ca2e2f6c13c276150667bc1ed8ba9e90c59078f21f12c3277f27c80cdb5041f978319a6a9216d6239916a60b72df9ea1d5700258ac62f78373ce77c65ebc1c3b34cbade3ac15e8facf981f7343ba1e0e9af056be3735d1a48cc23771f4fe0b8de4905f51c46ad385dc42bfa52e5d4a3590c0ec36e0cfc2b0d2d0da81be7b9106616f2adbf32b4c6aacaacf33234e87545c708b486f1af3448ee92d2ae42953f2f6bda06da1c82a66bfb4975501ce824b98f08183f5f76abcedfcb44881a782d1a849611ab0277a1a0d24fbe406ab645caec0f48f352291bb6d54daf2068ac74afa06aab906486fc72e4eb55b043a4b3e69d0c5bbd87a599c9168e583a891629fb53922bf0ce0fac088e6541edbea5c49ea702b6f8cbb6ddf5610ab8b7e5db08ea743b988ef37f4476c803ea32dae3b094980978cb0a63a221b7d2d82badae8dc2fb61b88002ac304f89ecf3e9fb697e89af0d809f2d398f1790224d83f05ec458360fe1e557f58783dfaed6fada8f92de71062ee9e621dfddd59b80e762161f96cf7ed8a6791525a6a1ee41a0b418f701930a335ec95655838748813063033fc31d20cce0474c659bf9316b5d0d11247750052225f36fb6b9cadba9232d489779106c77dfe939c3674043f5be69be2d4eff0a377153565f87d5229314e6169eeabfd6adac68e8160930eee36d3229ff83710a295cf6344295f730ef7a4aafecb9746b5fa50663135019f84fb952583b05fe08e61c0ad9f56dd50cfa955304d39e43712c7daceb497c759aa8edf77395f7c607abf7cfbe03fa5ccfaf307b28be305add27ccf50ef1c999c2de950f2e569ea608f72cf056eccd79c833212e4f77c3336a3561e66cb80a1a53376d8528f05e9e069853d0c11009c36eeb1409a29c2c4fc52e92dc3bbc63f5e3f4c3a48cad23a80421a90d88560d019bfef3f7606e27b8d87dc43d1908a2986aefb47dd318130d085b9cf9d882bfa84f1bdde257a4cba098755239b5accd8cd15f2c766ccbadc604d08031157a4aabdaace655a181cf3988182caad95b442031a5c8997ae9ac100f3cbdbc11178cee6e1ed55f0fa61d770c2edf9e2c2287aec38400f15b5eff1a0960de4994cbe41f7759fa60325971d9447a23ad72c66cccfb6b6b8f5955fc240534ef13f045aa3ba678d2c4326183d2bbba54691be653b9d5b9a52e94e2bfa60e0930b579e60650d1157ff85754b103a2b2466378b52ab8a24dce65e23ebbb50f970edbb4d8b1a602c82b200d9068873cd12d04ba8e12b417909d09de3cdc7d0a292d3200c3d9a0d59b91d64576478cd96e4cf41103a05368bdd62e8d501c69049195d8a0e55be288c3542896897750ea10f9c674c877905b88bcdfa55408e60555d0ab9af10e2beb6b1332bc1b49e45ec51f5597a123cdbda64498dde88af8074549c96ed51b8994a1df1dc2efe5f6b6e0cc0ce4622acc5635a22c43254a370debe94119306e5a611ebd59689863d7742d93c62061fa584d2295ea96d8d57008c8c25a052406acef30de7820228201849222a1e3c37e92bb2ed41d7775bca13e1e217f0dbfb7bfcd9485f41b523f9721ac06132a58f066907d3b467f9b1c7b08 false -check_ring_signature c6d79acf9467f7ff14038259704ff1b9d793f5bc60470922d54499f1da5165ec 6f6199353c0dcf0390bcd4b1983b80ad61742167d55df7e7c4a2905ab092ff73 2 7b95d40a5c3dfd8b7ae249a2133c979235f3936110d0d08910b42189f6979021 41f95192c8a9ebc548db03458d97cba0fb667084c21e4de791950874b1de098a d535ee83f6905240801bae17810d996bcbf4a8e5c02b70ae84d7b16da72c230d8e2a2b66a12cb1dea86596c5fe1a9b08f730d93bf6a67c49bf5afe39f243310c7d6fe37453fd73a37c337ebcc4f4cecc43a7fd7b485ef9f70ae7949aa8f8b104028a5f4a422dfe9fbc06c8b6ad63f1f9b45a06fb390793135db29e1fe947da08 true -check_ring_signature acfac8b58cadee24cd9b1aaae0abfb72255d6c5c10b8b2d7a02a4ef74f00edd8 55279af1c5316be1a40fa3d55a6fe66105a79033052f206df033840a213b3913 2 8eb913d9da06649bbb1aac008364dfff7e65de56438945edd7592a2dda7a8452 66b2b1fdeffb23442248c2babb83d9e7ffb7522713fbd3713e011222d37bf212 f17964a75667282f2a5978c666668266cfab44984926c980fd6e1c7bf3e2590bd86ff442a51ac72be3bd42a06b481ee15a93bd6adf1c887021607c6b40cace0191429574735fda842d07bff148586173675ac7f16c50cdea21058059d41cd20e75964c82ac3a960bac1ad7c83931629be8cb7bd782bef045aa55ba301d5a140c false -check_ring_signature 9d7f3e97a5a53867b57e0e3d38700a4459e45038809ed6fe74b86f984281a6c9 63940c2800803ce780ea9dc51fbef2164cc7323073323d6fe2053200804e806b 4 7af6fcb47eb2c589285d25dfcf4277c5bf264c55882d586f355c3e5872c28e39 c75adc06a9d5cd4d6ceaeddd6ca778593cf34dac3a5c65bd25ef503394304d15 44b3f7637a8d30a9acc64763fbb182d03ef2d018bafa57741859658dc560b38c c7d9a7fe91baffda8e381b0792437a5208c0dc5191097b19da7b2080c635d39f e3e544b05ac8d384c45563571d67216497b8ac8c747878c922bf6fa5ddb8e406c480059fa4fc4a218c151dfa9a6f3740c9b3be982ff2100bc67fcde5d28c5c0704ff38748c7266a7a66127d2f6a044d6c45755b19bf590bfc48bc37d604c610f1a4edf66a6f8d9f118735e7199e477723745d71be6d789c447bcb5ba8470660eb6b34e08503d9b62a0e9a01904cdef867b7f709a5c618dea51b5a9329b4c990ab70f15ca5de9483bdc2e25112b7b24c44f770cd5d3e7a8806aa877eda76f1f0466f69c19c670c22e59f7f4dffa372dbfb8854c8e91f73aa0f71ef20775aaa50aface39e1a5cb9a31f3cda6be3c8e7a5bc7887357ee7047e42a21ead519a13309 true -check_ring_signature f860e5670345968f162300916916a23bc59346147cbaeb1dd50693abcdd1f2f6 bce6655630db94851a0ed44116490f5cd48e5ed831bf33ab86a97373c03c25e5 8 168ac2980f67cd39a4b01a7fb62f3f4f92e827455acd0cd0b3d1ee3274314214 058a45308b82ed8aad93a3eed60279d88a6b904770419883fcef94f1dabee341 62fa86e203717bd804d8f2a03808a221958a45d0ea9aae395bc2680e6d550ef7 1def9c4f2f3dde3bab8b51e515e6b2439bafee0ad454f37842d51d1ddad4e542 52a2493a82bd89e93e3ff75b3ffa7e2ae6a603f80f0400de1a5925cea9a92c35 3abd34a0e90ab696f7ab5a352f7e2608c551774dad1356e449eb326041a44de1 65f41dd5293a64b3a35dcb4d9e1103c3a23b247559231fafd1659ce37e88712e 7f3c6576ef9a1a2df40b57ceac637e0b73256c708abe75859ba762c31e24f23e e83817789b2ccda20cba7b705b9aa96866951cbdeb49373bd4e298db42750306b480568ceb63c17bd350d60ef6df2244d65e1a8c701190b704aa6996984c5ad73494ce9e6130f4d62ad9aec5978711cd6a0d7566c74c90c04a407c268596630a9fbe0b0da09796ee5820cec5acaa9e624e1a4c723133607917f94067cc42b8088181ac9e80558465bb8bf8b7bb69f72579aa9c98895982d0e8eeafd4153f9f0a2d336147b1f67ffba715b8e8192a064d48b2822401c099eb03e2e02677b44901d687c7ba62e952d68f586896bae99a45dadb57d277aa4226538d85957d0da806d185846a19a62856695c19862757adc36e14ec7fac127faf34e58d20b377000125df234174d37870bb4142474fdfc25384867244d3cfe49d02f8d9286ae2ed012347ab356712fd18c7907a3eeef362263cbf7be3ed9aa2dde1358652f999e00e9e06d16cd8340af166bffb3f48607b784bc85522b4fa393507be3e8776eea30b27bcd8dfb0b05838005a76618684b876ef490bf5b714e0e431ebcff2205be204377e0117cced590730641e9d1354b646ba1a2170368ca75128ffd120d75c070ef6906ebe8d2bf4caa56d6eb376787bc3924214ed7ea30fa8b192fa7bea9333018607c9a848c3298ae020f628e2848d4732684e9ec57b604171afb662f6c03e0ee31d6bbf63315bc76800035d05b7c925402d8fd62bed485f486fa3f5c5a85b06 false -check_ring_signature dc4b3d3db511b924ca224406ec2d9da0632ea25f8155923c40a661a5a0e0e2eb 8ea601b7eecc5b7a618e79c3eda1f473641cb6c51b88c2705d8316d461a9bb70 1 2a6a742e2c5e99026efc0d5cdc0d26e4a04f31eb247621e29e81bb237d34eda2 4c5bd6c8a8afaffe103bbbc8e777e39b75ba6b05b554da8da359d81168fae40fdd05c24236c91e9b9a06e4b8577ebc0304bc450aae6ca64c28efc7ab317e0a02 true -check_ring_signature e522d294273e71d8c12034ece67072c09f92121ceb8af6bee0bf1c8e99abb67a 79551ae0fe9e26ba1437a1ba4a563ed3b4776cfbcbe50fe683dbdb32adc48fbd 55 1ef00e1aaeddad1a010d7a7d7f0e771396ee676e9fedb2198f7f27756dd98cb4 714ee82b36c6433fd2228d5dceeeb8eda76ce9c2633f564e107feb623d700e0a d6ffb212927c8d977ad4687a38e0a69b74f31b92c147ced170fe947887f0f8eb 83fbdddd9691b80845fb3eac68e56c2ccee57c6bc7f9f1595fb4f6cf79972a3a 5bd5d325861abde6674aeaf9c6290da80f1be719d9327f930ae081c2b853ae02 59a220cf70300794ebf926cb359ea8e3da7a20791786d5276ae2a6255468ff31 2472a32584f185b6621c07a44033ffde522a627ee5dba772e83e750228db1503 5275719c56a6ff76672ab765a50f4901eba71bb21468cddba0072249b54548e8 089ff9217c6ba2152c37d8ef0b74cabfc34a80ca60923dc737c3e2f82227ea00 459f31e3c381ace2868a2b47c9d4c52f46ecece6ea03087d5a2212b77ba274bd 693135cd8a64293566707e99d6427b78b5a8ab784035751ac57524308d5afa23 d3185753aea91d4d75981825dfdcc0fe26daa1c0bd3c111775859a9e48b6f544 6639a7e3f4851826f918ba63171df8789c9f2bf1c4c52b4bbaaca64574c0be57 9c2e62b453c1e0dda2dd4f0d729e32aa45e82a7d01417b3b65bbc6c73f88deab ec1b205de6a646c1701fb7394125b38cc06c39074d485489f023157a1a02a1b4 787c1431c4e2c66f4d20daab9ef0a1ee4bbfe6e3dada832d5e4261a0423d47d6 d75e03dc37a61e72f2e77e84e6b527906246de6eac812f58e928b7293bcba102 91aa7bf486a0e08a0d8b52b4ce0888cab721dacb5cfeab89806da85c0a061ec0 63b5fa2c580acaecc7ed3a9f3b3da3b8ae756a55208bd9b1a53f663f0a2c1b54 e6d6f2b2d770b92ed2779b47567adf1a107089561ddacdd0b150797296e372b7 419ff95c4a6a9da94fdfb05f1178af3e87bf865240ae43a3020283aa5f56519f 44abb530b42641a1221ec8e396d619942e2fe2d0fe8094118a2fbb9939d31ae3 8266681ac691d462d68dfef0eebf7b4ec2903ecc09cbc79dcbd682c0f9938b0a 9b5267de2118c83102dfe1ff190e0284dac6d4da207fe9a4cacb92cc76a6507f 752164fa433c281f9c410346650fed6701b950747e0bac695a1d8b8b7b9959f1 f43eadac86614856e9461dc945b1f3bf8a73d18faf8acb3bdff80a67bc3abd83 9b44cc3ec1cc938e293085184323e32c3d448403312312a448e0bb2532aecfd7 e8b2442bbb64c20c35f86ea48cead3d63c1096e65bb4037adf9cc9b872f4fc13 0951cff207647aa7354bd5585fb158bba636e93b501309bcb04be34458e007d6 161cd44ab6fb6924ae19de04c2df14692d8ca38ee171d6d7d817fc55576477d1 61b95b6f77811e88e135405806d9491e14a895f0b661d5ac687907ad2796455d 5bc5fe90de49dcb23b30d2e95327e4a652eb563980b2e7d9cbf11d180d17378e 17e25b4e4671b3996adf4da195782a353938b407ee28b53cf80f696708ded3a5 a55213b49ca9a29084f22b120ce2203f9a6355453f63c08da37d5c8ae82c3e5e 7d982177c3a0a54f008a69be4cc709247e2c1875ca563130048477cd12cc9a42 a9e5dd93a499516b8d8238ed9775a1a01a4cb23f8427aa3e42f0171bf9b6247f 8afcc73bc0e16962ac1885b99f15e5c78bfd4420abae3b79bd586c50c45f8584 da2e7b2d03d48797ec189f8b5f4394a8eb7dbf90db83adf730a704b210700ce2 5555e572a204fe0be3a9d6fccbc92b8423907d6b321bdce2e9b0eeef68247833 cef0a660259393acd8f6a747f036682c6c99e1a130b343cb398dd54f50ec1ab9 44416138e60656ed628d1f33f558ced8e75dd306a1b3975670ae71683430cd86 3ab1e9697fef8e306a7f3da8feaafdab446b2bb225afa2b86f05a0c3bbad2b42 a3fdbf32c51f5be173e587919f6aea3bd3b73437b1d9727bbe05395285c783fb c3b6538d2ed8182a57dd2c92425867e38525669f59dca1a855e4eed31e5e693a 40437dffb546baf0875f5a0f665e1b411988c687c28b8fa9a8cccd55e04ac5c7 790f420d25698dced5a23bfbe46dbeb276d305b11fbf1d6b350daa4e07d34a18 d26bcbae86eab90f557c680d60dd5cb7778f3486b9701b4ef66c8b09e0dffd37 f36dbeecb59ddc2db4a8fb157f34cbeed0b1d1f5bbe432a7ec50b8be2fd4fb4b e292ececf6a9e091e23e9b82272f6d8fcf5f1fb48b20151e4755ad5893a35a50 0927f5a66fe620749a25e1374fb17c4d3227f9d580aa655f7b41bf37d50c5ac1 924d53b590b2de8c3c5fc3f850a7c193faea73fb2c4f25e5769e160fb851051a 80ac1e8bd5b859d2fbc37b89b93e9309c9df89235b0ad9dd05267b98552a1b2f ba276cee16c1310625d1ac22688b90d515112359c9630517c15bf437dbd24d3b 7eb378dd727655d182310f120362c230983a7ef31cc59c70043f8923d0ee3a5d ac96660ff929a9b108170b3f1ca7644628fe1f3b0c5b558b2667f4f5d946b29a af01fbe5b6a15435fb86d8d560d6959fabde8303d227c1d98dba7d12528a9801eff8d41c1e36c3c37325094e71775717170f47146d8694061d7fdfa1384be20af385fbc456bb98fafa3afa5dd4bd86911eec0289e3311863396cbc0d5589fb076bcbeccacc43876cb272e60126abed4928aa68bd84198a863439b6928df8c40c3d5cbd21fe36fea865a8e136ae64572bdc7aa49be98dbfda48418735a232a703974248e71c672032bbba5727d81f2afc380251802bc66c8ecc98336b063c7306a1172e2fde98ebe8e7400348287bfb0e2593cb267b78b91838dbe1ceaf6ab70cffdfeb125b94d40c2eff6fb84418ae0f8f23db17ff8b5dec8fb80ed60d0d21066838d218d449edc1c3549c3f2e21a79ec358585673bcd8be82616bd3e2380a0cc3037966f26306c399788bae3ce2e04de7f5cf2b150aaacae724e2f268a5810407c8effedbad55e1d31c72669911215c864795302429ad002a2cac5e6633560f6210fec50ef7469d188cdfe9868fa6e37a5596318c62dcb83734fd18d1a87c07f9777728b645870e018417c348cb682a8a08d490e08af7631cd0a05e4f22750cc37c9151be8de150787a00be5cdd9cfa841cd8cea4938d1bf79a564a9b7409045145675ff7e7780d2b92d351a1d4f7ef87dd1b4992d964a036e80145b11d8404b24ba794646e9ef475ce7e7c4fe4008a7d3eb8810616ab11b2f310e6bcd13d04e5333fbaf341f217079d8da302738b5cef922a8777771ae60fd95b71693da807d9680e235492c1486892280c8cfd8923fa8d33ec053bc248f34c635623301d0eb656cdeb895c52824fc6452e917f5822a2df96d688e3de987a9cff9e751c64028848c0ea1952179e0e85a8ddfea9cfb69c390d7d2ed8cfc598383e1c7b94130eac8dfc622ea52134417b7dc5d4185a06d7c272633d056c35c745eac86a16670d27c10ad6bcb300e848141415f2cbd95281b0d5ef970d960a9966ef21d161380b147c083b86c2f6daf3557ebf926d047142326adda105de406e0ca09e576db00bd6f0fcb15281f215ab0f15dcac83eecceacbe83d415357a99c73531c27792c07e5d85d62baa17e34f4c82b578b3477cd8b812216bac8988e8a44908954c6770a0a24cca364c6304dfd5b0b1f3c756b2ec7d2831134d0522ad2f731fcb9cb83040b22107169bcbc928d0910031701ba2eee448c4ae09758961f0793950ce82e0294f5c135c255bfe9989de31d097e86cd2f91a1feff5af8eefb8258e0f1dc670ed538c17f826b806b76e149ae5f2a4bbb85d7d989058d6224f090266d4595d806a6d9a2c100aff54cb74b27422fbd24edf31ceeb0f19cec7f8b767b04962e3c01784ad5cb4508391f993a8a5f591554f7ec21a70bf3bfc5e333004f28b1412f0967ae6c2c708cb4bc007fadcc10c62e4f485f68e543664369c7257f4bd1425b0de51ce102b35bf0b6d1102f1327a6024f1b3532d3fbdfd1667f9dc20fed21ed0ed5c89b6df5f62b7186ee18aa418b58ce20ef822aed2127695c96ba044647940b86246a19bde7a69635c187a7ae74174f0c827102f0146002fd654846320c29081cfaf241f208a1e141d2657bb44160522c76e13a64abd57be2300a255e9f410d4c7a079e50630820c4e8777f0ade0aaa46699eb967e18479a0f2a5b9c51cc9000819e7d393e6d79ae5d71abec4a3cab18fad0c8971ede91b5b7979dafd0d1e0de3f2e7a3b49b9a87e4b5ed839c8caea1dca9b97c49e163901427d8ea97d90c0343687c27a61ce6aabc3e47f00a9c061eeab66517fd2b570fdcb77f7ba5ec9301a378cc9c926021bec2946b93013e4f0a69671d2b2705b694c0fc58ef1876d8023c76a7904a9932087bfe1b4328b838c428613ba3c3e0644ef4d818fd8119630fd8c8a1fe4d9f7a1df9e21869298aa7a9f5ad7701f47fe20fe1f15900eb1925040818c13a5cab33c3a3ce39031413da9ba61f3ac9dda270de264cecd2658f7e03aa528ed54cc1f881789901a7a9acda271cd8f7ec51f380bdfe9f6bdbd1d0be099f2d9b704ffa07c7a092fd6015f12d161c44bc2eaea15690124e2798d43ac70c17cd2ba798fe695c6c6524aa08a095b214703baacecb33613369434b2b1f530f036aeb8f0aa19feaf66c260749f86fd3d21178bb9ffb9492c52e5b43ffe30c0fd4d4f2f69876924dffc258c5cdfb48e34c9e8f7e68da917a69bc1840b2c8740e9da1ce164cab9b74e8fdc043f170dce435a225cac798ea3b7d792d720e44db0f0122b192ccfe9bf930fe34afdcf11c53dbd3f9c0a9d380bf7226264268113d0e1bdf78075cb97510069db2fa3a2109877bb1bc19d0112cf979b2f55e7d3518054281876f59ba7d6b0af9b9acb887b142369b1ef0fe4c3fcce2b9d2a7ab51720fe6362b064a66bb9becc6e36e90a61c519a866fe6e11e3788fe5390d6aa47c60b36b862a8bbd991a624625f90a38f02b761750b48927381fa77c636841e52d90947d3e1c46ce44ec3a5a245540a185054372c568bfc9fd2e1f1229e7f22b12508eedb0deff1feb3138ceedd84e2e5c27766879a9ae7ffbfe52abfc0da8b4e1d0992f6ed4822a726d34e2770c030f7aa5b1cc5dc2326d848736799cc879d045b0db156397331e0d505b30d8c2a97ac120cf0c4cf22a1df057aa2a504cff2a3a50dd41d1c6f814858dd4c78ce130335aab7d179c6c945e1f0d31f60c40eff78f70f67770a2b975a49831702947ab5e3334f47583183376167a03bab24e488e60408ea30fcfab073969adf996d76ec480818d7e6f98d03ee559e25eb9e9a3593790343abc5fd7495a55b4457557bde2815b7974fa25f71f73a2311234cad510845028d389051523fc567c25f5756a589d738524e4dd77b5e989f2d6be44a9893e10d127254947557e0074695f6325c804b238b95c9e6f4845e29fe16f559a846650a19afff34a0edd18ab07102e7de5928d0c8bde3f5ef2266f8883be89c163e20029bb501734530ce6562d714574d62d2366ae1bdbd1314b34ea34eab61f274a60eb67dc569027299db27e23f9362c6e52e8a32d55ed4750e00b7947ff3ec155f0b7ce2db5c2210fb360f3c8cde1b24c4c28358c1f8f05862b749f075aee6d36c08920debbe62612dcb85f60705aecb61894d0c91064d95819edff816bbbeb885066b8951b6537699a89a1c9b4bdd566c0316dd88ea6a736a35b5191c618f8f3d02ced8c83fa69d1ff61902d55331c62872368432c8d8d5bc8ec2e31d2e4c950d00a58d7e5142ce9d2c2f7423a884666fb21400c6a73378d0b4014ed892549c6d042659f1b6367f2258577ca412d02d451a2d249b72f923edbd4eefc7cbe39529023c9534805be06acd24b0b12efdc74e4df9f1150664ee77349967671798186b0e73180483cfb07381eaba12a5341b19bc506cf4ace24cdfeee1c5bd490b4654053f298474f82d2b5368929abbba31c53cdbd5763f0aa4f93a14440ad3a63ca7064145f430c56ac7fb9134511942108c3008190dac464e066c65f9decf173af40e511a2957fac50915d43e50ca598342f5fcfd6a6b63f45b89905932c904bd2f06b86d9b499554a9ebc4c292ccb23243dc58443f509b4e78dc4bd817296a25cf015ce4d842c39174119bb55f22a4d2630a994826f73707b4934134271fd4582e02322c600884aa95686aacd9982e4c8ea7a2076001221559b81b674142004e65090ef26e15fa828e5c9305e3f080b935e4040d2ccbe114c25051a643c29cfd80064f703467455002a7f8881dc2cf8a727bedf9512ae8d3c6e9fd77172be949b60d2c9a0956e2223882016e64066b9442e927a2b0efb6d7c999d0944fc63e38ab0d5146eebf2a364dd5b9800f257cec19d3c754601f60b24e33fdfa3ad6524ba8088a9264e1c148eea0c053e2243ac52f5cc87dafd9f8cea9d68048b5641e318a036c9954a604befb55d836965af283019f4abaa22d5c34b2ab12404d67e0c4210d8a7a0dfc2feb792e0d0949bdbb8e3d90e62d2d56aaa28cd4f7ad0b27aa3d480cc8f9c6a5a88c9cd7902fd91ea7968bf331e0af9f2ea39f6566678b371af77904e823c7997243a667d214a5b40b946fffa6bf3cedc552b1975347a20a986094082b2a6ff4d840e13c487d90605ee84f6e44401c5aa3164cd9f6d4b4675c943303081f3c73364a8b3068ba11b42656f1a0b3473780b5f8f51f60324c364041b50a6f68bcbbb13a31a9163fa8cf61e0635b5750b355b8a8f46e761dc14a7af4258e675acbbb71abdac81efa457eca76747aa368e0d111aa0e30f68cb7705aec590266ba7b1111eaf59d76b26cf3d1562d8961d97159ebf861e8f9841cf17094f70e5c34a79ce10cd726d8032f41eea88ebd6dff8a89da76e2cc7a6855d170e45d05decd4038d7bd736a34660841007b65dafa8a7369046e5bc030ed56d969ce6600a266c6b0b45960e075199bbae26555d16a544087a4abdcc9c6cc3e4af8860506459c3f518a55e4dde086626642746168795f3c338c06e37b56422738269f5108879e5cdca403ccf8c52cd149426b22ebb88a8c140468a7ecbd5fb98a50e8b00c56231fa4362e24ff607a92bf6f3101453404759faadccfce67f3af2eb43c650ca1bb2845b2dac7ab6c035d9ddcfc54fac87dfe1c9d682c64c5182ff14209c50b2b44371c57367370fd640a5aa29bb52a0a3c326a1e50fb7782691239453f170f1cd1b43eb4d799641e247ffe73878f56f65d4c751734ad7435b0075504e19f06e15cb5d6667dd82e15dab77677a04825af208955e87d220997653abdc33e1d0e4eecc361b6f335ac5732590b81d4fdc4ad50926eaca88bf17094b08810316b07c2a57ee5733ef55d9aca68feb43445b0ec2639f75b70ca988e46e8203d1c5b0b2841201e1692bee1f23040925fc9f163b512cb39e313643925516a7dd9f4ee00ba822899bb30d403f0129a2414fb89abf7095c9d6f3e3b79df3d4516083e1e06 false -check_ring_signature 51d1f1e69069f042645d694c038ca24151f3dd2afe92e95fac8a3c16c60631ea 29b572ef9814aecdb56a3a4467e29e9eec1a0df691d9ff53f67b1e39f5f1462b 31 1f594c4ad175e2ef4ca86fb01d64a4f6c920632eef59cacc89fb7e27a4ad0342 4da1a51c491f1834a4f2805ede68dde7f3b57913d65d4f3e9f9e6745297dcbb1 416ca60db576657e8a3f0ee5fb037f88b561f05d60a7c39596110996989ce465 5d1e0b9459d04ccb576a9a92039e8acfde5332d5cd8d601d8ce740be091ab39a 08ef95ea424c366f9e30febaec7a576f22674802399217de695e06b7c3955647 159a9d4304af7729401c1e14d2ce8bcf1028d3f75b82d9ac837b66d62b9b8c2f 46b6c5ff22a63c4dbb57d2608b3ac568bc3633d4129fd59255e9c649d146d1b2 c62eb732c30d8ef825699c3c66812349049af354eab364dab88f4c59448c62d1 e2da3ad6bc12ec90afd759d0cfc6b6e30f2b224180e0816897ecc775a1a9e11a 0ee9800e8321f033bc4b13b507dc59401b9c9ea582a34dcda6b91180e212860c d25bdc873b229622b63671b0eda5f1cf08f4a511bd34abe0b47a7b7694c07c70 768fbd825468e73d58ef335529514e1295ae5500213843b4819843cb0192d68b 2baadea6b4c349cfada0ce13b83f4b394d5c98029852dca73adfd3146b4713ba 15d31fcec0527d5bc3a415611a08822eb7f4a27a3028930fd80a1a7a73c2eb85 7b8d3ee384cd95559b02b26371eefb4ae9aa4a0ae5ebc25ef2c8dd8627fd4e10 0c37eb117a26278f4f67ab81bd6ab912a50c0e49059388101d91cb02ea84e401 8c25421c4c45ac19b0c4c2f9d192e8799b06c7222b1d89a0da75654126ecd9f8 7c51d1c9e48c1c9a753e2fc98ada690bac70880bb54b6db242d8c2c2a94c2647 79c39e29e61b579501ed7d5b806870ec86ab19bfa6a6929ff74629fe64a54ec4 cc5d0523e14a8cf21ee4b25a52ef394b1d5677160a95f2bdb3209159fe9227d8 5ed33746518b8ae2e9f91e66b2e45c953539f2d8a4d4996e44e9d58f6e127799 81a0fdba95868f13ecf0a7a3d0315881f1415decbce2ad2a6418e079987ac5de f4ef8c3bcbbd21247af52257e42e97c332a0b44555e64974a44905e4e7e7cb2a 07ec90a6a5a4d2f8e8efeb216e32446af53b8b51760c799a38c7abcdea52840b 178e11db7595bfd5b70f801d484d82780155fdf27021990a4848f454ebee51d1 527c8f0dab1d54e196b11c746c0c06d1ba81153a8c90df37e8fc6cc4e07d7380 1c23d90d6cfe101d87356065183c53d7ca980a007bd1e22f16759e763cc53bc9 5cdbd79f935fc81eb00e33952deba6554437032c9507c9b1eccf907adda8b0a9 b8c8e863904126ba4ff37091f5b0250b8088d6f64f7497ad8185726fbc48b64b 89b369e179ce56cc3f96a77b90fb2259ab9e4baf1eb93c9e38851ee4452508c8 f57a2ffe9e6771b22d8d09775d87f73b924a87686c960a27f96e9ba4a76857fb 212c80e94593b0833c11517e48652e71c8cb48d93aaada07af8ada176c3f9c0a1ee716cc01a55a6cbfdd8c9ef47dc3ac78859eee9afc1e08be0297dab1616301a1689b290750643cec0f683a3d830ca53c7491535d271ef7e4b8240bda12af07c7c81fbdad72bae575479ac1363a7da581f172348c6a0911f2d3cfa0cf8afc0d7207b71230e7cb4a1de3e8cc35471109674a7f9f46fc8b04d37348d5771ed005c97743a742e2dd693f9d6f3cf330e1147b3d43f9e76c29b4995aacdc9a498704c873e470c6636cd82533f3a3b77aade0a91319f7d21cf714300b38b6200a0c0a5111380bbcb0efa495e2090567602cbdc91063c524639ea681eabbf157efd80c61d11f179d11148098e0dfc9da441e3afcfc3b409ec19da97659c9209ffce4029084d232e7cd4b7ca9d2a4fd26fa723bc10137714a7c9a78059f3fc0f334f408ea3656f307b0054d78b834857c8cf28d7c55a829197072a36923003e3b9c8401ee82d60af2a6a30e0a69a75fc898f4f2d5d0795e141e6aec328abdd2d54dc10a94750afb4f97073092274821d82a7e1e0760f30065ace7b9da1e75b2b4bdf107136060800ea4fda0b0844229bd4a9195d4183f51ee70a76e15a5352e4ba71d0020e8cf65db2dce8fbbd3381a79726cb085ea1d796b2b298753bae1e0a7a41e0b1ee988b67dca066171697592f60dd6c5f5989797dc27cf2aa7a61ca794ee370fda214ccebbd82a3bbe5c9d8001844d61230bace1c5adeaca5aa7ddea848cd30d21ddb867137e903d57e274592eaf240fd593549cec0c81d1496bbdbb1270eb093d8895a137a5d8ea98be7f7442068af838a84db462f98593b18f955cefbde60acd4dccace358013f645bc877ac087d88046b479c096efeb037f2e945a223d00a3db2de072a1bdf6af4267f00358224103353ec9f4633dd99df358a40b748730b74cc7bfd9b8666f9a61b0fa6e954137fe264db47763cc2ea56a22ec99b909b0fe7ca2cf8ae599eb635956d5fb8570f20e90d1cce78ebc6087e17e43f3613350102d6cf5284ff33fcd1a505744035e1880fdf976e42904ccd64aaf8a3f87bca06cbfa045a15f21038838318bbdf813e2dc245bcc4d7f4dc0a085d91790f248b0537520b4ba2d3ed930e55ffe3b1d2d1174166c3acc3356ea8aa61032506d60b05559e8f535978f03bdef3bf1857224779623b04fc2f07dd099b0de85af158690caa7b873c71121482209db58494d7eb204c52b99392cdefe7a47e91a4364da7071f0f7cea6125e1d5fb1dd66451b70dcf2912897f601bfb19c50da80e923bbf068971603ca998944154dd35ac02d309969b48093d758ca412751995584eb3f90ee38cd025c121aca0fb590af8eb741d1736f4aa2fac8e95ba04a38fc388f67c0040cd592e30ddb6b312f52fdaa5b3e4f3c34fe36fbd07d65d6630c74abb69560aae09d45cc47421a45a50f18b5356269165fe9c21af973a465407413dd9236108c9a04c3de8ece84d33c38d72bc48317e5b91cec6e1bd18b2737a4d92288bad04809cba00a56094ef413e224289e6c6311b45e5f02bd3e6d850b095ce4fbdb00adcc23657070bed5dee6f3f0a2a338a451ee4167f77da8cc6c432a9c544d44b0aca32eec6ba9b579809aa82e57cfb86f43c3b5049538ec143c88a00241c361e0e3977f4df341937dadca467ae4917b28e70f2776bf7db5c8abbce254ef423610adac1c3d1f077252c3990eec5cbb550f5c7d7edd3e49f92c48476cdae416bcf0d50a80d1ac921c8e6a53bb235f83c84b4ee53ec945b7a2d914a7fcc21e2acea082c98f91c9d27f6cddeb10a07e4e7062d91d651d76b8398831adddaea87a9be037b88ec4d78766597dc4c4fb03a58bed248f203110718021b35b30fec27cfa50e73c680a3c8729e9997a096aa06e3135112c307787ca44a345d3fd3913f5ef7008554a22d8290671f9da65bffc43c5b8c88056be4bb6e235ea54dad24a353d608d1e0a25f03a0565bb0bc60d16f68b75dc8e5e88e6b82da177d35d263e1ae1e03ae7677df1f68bfc35bba2caf4ca22efd1d615257c9f7edb1a015e974e830880d07f29c63a0c5ab01308e5efe6d1320b90fa35243eaaa9242cf7a5319c4bf4f0ca4eebb6f4c4007ff30a75931b2a69c83992052ccbce19a207dfb40fa15abfd083087ff925e69329b798cf827bd54e85dfb2ece0f662dca974a7c748bd72b7907121cfe09229a70ff7d459da282053ec20a0b975f55c061163ba9c488aa1bff04e901f53342c9f012c1878e261a6fda2d1d9607f9b23ccd97fff0b2f5b4b5bcfc43fa7aeb2bf1930233a7f9a3a9c539557f77f604a129923fb9451f4e5d27c405d80380b08e79b8a4d2546410b03b6bae977bebd2ceef1c56add35edae3aaa701486856991c4334370be590d5da6bb65a88eb9afd7c805adb30db91eddadb180579a83f54f1abcfce571388284c3edd4cdc8d983c2e1e66ec6f31c4ba84e0fa02da55421e6dd22edde7b540d2c3b0fca0af04266b1494ca9143f7315e61ddf6003806705eb6f1d5ae3e47ae8d247c90ceb596c795ed9365beca3f5f0b576ad5074cde5f1767eb4bc6f614e626708a35a3e66a663b79769fbddc705efa0d9c7d0cd4f48340f22ba597a854a138ddc23cd8b328888f6daa145eb9854d1b1f2cbf0c635864f9aaa27e9ef39e1618565706d39007206d20c166a91e1705c044f9f60f1c731f30152c5b04e3d27e6040b1efec0d97923fb3b5622ec3de5551ff73f70373ec0dcedbadb14697199a5f368d74e353c89832ab269a224316d4370776cf0f false -check_ring_signature 746971bac90d436ef9d2a96fc0ad439eccab203b06020c609f24f97ad2775c7e 15a17d1f22312cdde515d3034ec4aa71eaba3dbec9b15e844d5222c794287ec7 17 dc03b8f9ef4c33e4b82844e229eb7d07c49b8b9cce6fa392cf3876181bb041cc be6829fbd6d5e4089b5705ec83121b902b83291d70456b15a142dc0aa1e25e2d aa82a6973f65b27c36ae43cb73a94aca0a3f728e94f806321ad4b3c89d872d32 f2f0b07d949b2a3663657d8f266def064a942131b2a3da8d7ca1e2002be055e6 fa1c7c32aebf3f3870148052c00bc71c380c22afeb3e5dbffcc82a05ec9e01b1 5e6ac0b70d332e22c411562939cf54a5a234213b2522159f20df58ac07dbcbd9 6772f3e4405a71e292b163f6ab4f5638343e5cf27afb816f8e3f2be62b0a4ec6 3bd36cde8765f2d6da670de410dd3c69bf32a5f778024f186470b56466f126a2 651731ddb1ab397d4b05589c76a543b0231ce419eeb131891a624ec76feab8f8 de00e228415eea071694fbcd3650e541e1518882b0f729c6b3237fe66a3771c2 003600b85c9c5eb80ccd6080b8a4a0162fc9ada6019c639fef39632c7791145b 2885d2ce7c23bd5c005ca3f261eb081711cb989ba38f0c3c5adb58d9a0921796 8f3a359b7083c86be1ea995433f86b10d849a9d81b743d7fb1a72c691bd63f2b 7590b9e4a458e7d1b3a649131ca04d4b8265e650f38babfc941bf6b37905ff4c 288e6e74685075071bc62a588f5a2f03ac4c826a256f033780101af60387805d 21e3523bc1b6d300f1594586d4b4b54b0ec52e854e04ee169c88b031dcbfc6e4 5936b6626ed80bad581b0ddb8e45dd359592ca1b4ea5a6786d3c384aa947a9ff 4ae6188e5e62a5ea281467baf3f10b8765bd5e539d57d683a819881c49434c0d616fbc84025ab0a99ee03ebf16f9d3f7e44ccb0e2c890c25998ce12eb718e50b09d92276d140271005408228cbf09d7bac28947929be7f4f0de50be9c3ab2e00ef8ebe09309666fb5d766ab5acc450615d5c2066e28c8c948b1a9e24db14b70732e92e04907e9788cb892297d5f0fb4261591b89eda7807678fa7452a50d7d09117f339a5272eb668d84d7cd801ce06e4ed1b747647423a4158946108ddb4f0f8d3edb7cafa6d48e6219734f232769f4b320e972f6b476d9b5e5ffdfab3df30b864bddcfdf5b252c28e4dfaf54b40c6b00c02668530f2a09abee583cd2f383079aa6ca88106d0dbf4baa40b3277395fe1849ec8b6d6a7c708d85a05bc81da60ad35224cce28d328fb978f31c9e6f8327b0da398b17d45384d69d656b25a6ab02e085c3f92bb90dcceaa44424c1292517ebab4e988d713feb98e4dfa36369f607a67516f9878318667108e69d26058610dc7d6ec018b9a03b7ab6fcdd764fe907017755dac295569a3a97e1ae1e9e576b30f51643d2a96777bda2ec2ae02e070070574bd89f923a98603c4497b8348511a7a2f9683e69a4b98446e6880cec0408ac04692a576fcd0e26df1215fdfe30a3d7fd36cb3b19497fa9e13cf604c08306fefff5daf1b548930043cd05c2f4875bcc94af654c4a58126e70a80ddead680817c489fad5e71e240a6d0cac13d503948eec5cd605cc5dc7e66d2e309ee4380c7ce6781bcb4fcceb4df0a9218d8383cd904f043205650f6cc00e7ddbef0ad6052f42b674fcaa507c2f36fb78836e59b757caaff293efccf2abab2c202041180d3d1dad60247f52778bd8188d5d653196673a4003bd8ce59087425972389ece0792e9f2ec8d874eab27d94b7f78acd8a575ee16a8880db25eadfbd2920a41870ba3ff99fe39a5d75438d0d07b2c0579a8f9328d9f36fae328b59b3126ab8e7b0bf6f5e61cd1ccc17e46a95fc938bd6ef09a090fa5950cbed289fc890fa77adc07213058f62954ad6e9e5f0ed416745082d1b86c8932239a31c7fd07577a81820f55b24d0d49ec370c659881381bd73119c0d89a1a1bf61434116aa74bcd25890e9d2a57dda9d798ceb0c1bd713990aa57f97a6e0f21360b0d1101e94d221522040886c03fdd936e9eeb76406dd08e5a1274761e79a366d72a1a12e3470a051307015ecf7ff9160076e4ca0185ae45ffbb713e90921f15225702ae705a6485f0001ea5ec64f7e1e4e2b5321c139a1fe0f1ba65b31bfdb659e3260a965aa619770d41e06d89a3350a34be532fe80118546f44800397b7eaf6ac396546a63075100f1cd93bb496006fb4491deda966d9a29a696c6d6f224bcec4f639c456ac5d3e0d534b26d3aca7b9ed205d368ba6727b846a8b65670a2714ea99b66be89e92d90c8e0f0cb2f7ea2da6cd19a35b9224e98d1c12c6e2bedb36ffa4f308bffde8770412c33e2c1066bbdc9d03b79456090f0d16d80296d7e6b3bf14449163ef6f3509 true -check_ring_signature 43c67dab5dc24e2a3b9c731ed270488669f6f4aee6a2242945c2290317990bbe 7619e4db581f3290e66a36275eb8ab2f137f44c8b536cc03ce3d1cfac90ef488 23 54e61a07e87edd0597a90d7d2fef0dcd8af2c1f3813de5bd2e1d01581bb34e98 9e2cee4e9b35d158a390cc9c7d7219ac5897c89d5bed4296b0182cf46e23c7bb 6cc0e7b88f1f57ba2eee222d69a19b947e66dbca06b485b67b4993f42c61e087 7e95357756010029c1878cdd31c6f84e620f0db18cb8d2da9a9764fbc803f37f 9dc4a87c327111483bb74eb55e4e917efc3cbffc2a7859d6f0204df564838e9b 8e97f025f4cbcc8dad2ac01f99b4ed50877efa9766c01a294e306ffc6da1a3ac 217c3107d3f0395192da0d4b939181e4c46a6a019a62ce96087370c5a03d9925 2a53b32dfbdb53b183116f97fb3eba373be638dabc183e51d8c79a3ab187b1b5 05a99bf4514b032b0319abc95a3b10c2ed58cf4104d081ca599329bbde919817 6665eb447bb94abcd8d9e86e1fdc71b9b0b6d63517819b6f9b1fcc14892c2e2d 9b4a5f132a9bb6f9adcdb6f3bbea977e82798e40ba910d11313e53cae2374761 a76dc7687cbe13e8aa78b77c7ed6291b5eb23ec965f1f77fd7f5071bce03a3de 2f7c2545bfafd201c79c60a88b4d757132dc6aa4965de31b670d7d9a8f2d5ce7 ddb8e6d334a576467d6f0abca65338973604feb9e00efe37fe0c869c8eaf478d c593c25708d84afcea7f38b93da5f2de216eb850df4de3756c1b5d7efe9bbad4 8eef731df2210f887c1d1a9c856bd747fda6da77063a84dbba715ab6c67fe9b0 117211ed56f1f302dbc9a0458dec5e2d5665720e53c8add1a387e0bc7c0926a6 999297f069c48b9d5dfa83dd578eb2c9f2c6fd634b84ef5ed820fee2066a3a9b 0b46bdd8168114dd17833fd377404255a8e9eaa80d3619fedc77bbca99d9cbdc 56384c27bb3866874a4d09331df42b0832a71821503048ec730de5e951e496bf a2c99750a5fb38e30f61bf437d3ac59ce22b1058d58ceffff041c6b78056ac44 f5da3a19789750ccd391dbca16051d1d7db9de1dc71d72c43623c5f5afe78af9 99f7fe2b293e174ff627bbd0aed383c496657ca94b86639aa0491c0b69102b3f 90798f774c34f87f479e3a57642bb943bf8d0ebfe0d3d2159034f9bd835c72015aac231d4ed71c28b54a323ac49070e85881194b83c5a0653067bdeebc13f607d65380fac0c72b51f7cc075c7e81fb608c43eb7e207ca022f1475c0782571201df09b39a87626fe9d49202d2c06ef81b16518153480e9f213d544cd8acf9d10bc723be5ba40789525a44ff3119f1bb80dbd52e38a00b02651f81213a8d98f432f4f0316525793c0d77028b83d16fd5194137278f56806ff845696d19939a090b4de7a037669d21d9564472d184c3d3c0c4c0c989c7b62338f311f8645d1a4dc895056d2000ee4e3a9851115279712ccd9871963f7c3f44af02c1cf294e8abe0df2c273beabe3990f5d1f6de5afeca4a118f425be002b86137c6722057b1e3d003f133b1bed736576545143471a1e28d2a8287ca5724179a595322525d245c00ef0778114a1416cc02c9d5ea9902500360495f71afdbae269dad2cbc3de5d910b5e2f8ea3b962500960bd7ec4b7b34d0e041a43f9bd81a7d6830ac6a213c3fa0293c7b1a38b99d4169cea71f9faa7dabf352c93f6ba9469247525e54a3573640ed263e9d5303fb78dbc71a503a27cdb21d4186013468a9ae7c663bba7ab249701cbd02b2be89f4adb6e35d7d005953853831cf646bd388d53c393d1c479e6a907ac216dc99855f312ce511e6bba8bdd22dcae1d25ada1d60c6539910cdac8380ef2f591e3b4f18ed0a81cd6298fc5a20b0df6806926b7a98b9ab10cbfbba47e026fb37abb5250a75dacb54f71ccf855e694483aef8eff270e272080ee04502a08b6f42adb67dc81c32e4ea00b640f734f951f3d316e144251f3b6b0dcd6ad710cf4c32ed6d8f570c6d22bcba4c877d49a57530fedabc2fb9158e70a53692e060635626dffbb0d192df5fee9e250d007896b10fdb42457dd7c2a598e7162c6480d86ccaa751b51356ca5a3961f4d96ed58de91c479d424626f2c7b0f93d1afd40652adf6d54259d76a46cc094de7e010aa54097b6f7a41b065d83d7afb83c2000d75c85f0d56403aa66e5ebce292304b7c7f7a6101482dee1c540b8f6f16002107cf7a3cbd9d260e61a5b004cccb5be5e93736b0bbff8d20bc42cd8d24752a0102b1e74018395b80c379bbe6bf8f35ba0c136a4daf6d4351cf29e4da407a0e2608f82e625a9bf41bd9d272ca46ab33fcc9034a2dd35b013bf88f80ff5e1d4c470f410fc5f7d754d62274366c9e594dc48ae0309a5995263febf69088ab65e28d073059d86b0836921c49454774eb72c238ee740028c855974839e158b882ca52089c216e58a8a04ed97735f98a49975ef8807c5e8fb9538f280676cc766b39180138596d70fd4f476d0718ef00f18c92250f78d76711911c63068012ca41f8f802b56488ea2eda130e50b1f70f0314807a82da858127c22912bc6df8585fabd900db265c07803de9cfa51686ab5d2078c6222937ac18357af5f8938293b54354229a2de40e811cd2d965626c8f68c75ad65e6f0923b09415df3f5751dd7c62a00cf196a9f287a7bf5756d46a9d64ad95b2025cb86a813fd497f0a792aace6c43095272ed005e1ab1d860dfc4778f78e9bc8367da425a24856f6ba6abdb03892b0758f6e072ee9e309ae5f2fe3ee2fe47659527bb8f3e1d184e720f406dcf00af082592a2e477e4fbee55a2c9494771f95a023bab4104d042cf032ab3817eb7bd746a48f676099686ee8e301f0bbda901d5d82ae5eb140621d21c0559b7ccca190db04ae383eb4466caef6d0713bfe031006c55661ffb870ec55ed22be1dac9ad0ea126e6516b8fd1a528fbae076d3c6505d51ee125a4e2027d659cf3c148815b08500a7cff028d3f3cb23acdd15b94f33b2a46834ff01121bb6d24fe650341bca013e256757e908b83e7361e0eeb6cdc1c8e662d84ae191af03850767165c6020bfa1419d51dbcc415439f2d601e109ca146b844e34611989c659fce6a5e93ca0374a5b7b294223e9cb3595ac808b0f60f96be1c25587d6f235f3c6c091d6ac70fa8dddb7b9240eabb82e524ac2e0b8907129caa0bb246666148e1795ee6190d03 false -check_ring_signature bb232496d8d50a29e9075e7f70310c0fe90e95122ab03fc9c0e4c22d67680d6c ac560f8bb576db54de6c3684f25370b0c791dccfddf3a0f9fe41675b3da1eb8a 8 c932168b3dbd9043c4d32699b8e0f450e313ee46fd062c3af5de3d8a4edab63a 5e1cfa899ab05673f6a18515db603535caf7a3d2cd1a0641b0d367ff142b13c9 3d83f44857cc95d9ac263f3144889f2c29e8a12cdd81b36f45fc422f629ed571 a2ff8139fc5e399637b428498004f00eafb6928573e15c1e2e64227d629c9fc0 8aa51c3e7769f946025e6d5474081064752b8cd177aa1af7e64096327d58c97d 444283a83dbe5c7234a21ae58672fea063d5daf29330f86b7c68e82078ab973c 97be5681bf9ec98e937f2e6dea126797160ebdd7484ed84d9911b6b6a8294eaa 4abbd9abf19ea848614e0e910e43f1909085bc1a5af063a3230a84b0637de192 8b3341ab3262436849aec8ee8e8d69afb0af2363ac406fee5e2e08596a9c7c05022b83837d5cf2536106365a87d9437464acdd0980a32f832a65639dee5d9f0acb9ba25aab8d2037843ec18d902277f42bf63bf1d20bdd05b7d41b4dc6d2600d861dc83cef1df27841249b9852abc20c38be1fa4e9a703b44bddfe9850121d0b3d3800acd48fcc7bb73777efb0f7c367e85b343a41426078a4e8ba20062e74076c5a8b4d82bb9027f514486eaa40f080c40898ea7ab40d7dd311aafba93d120194cc17aeb48cba5186a128d9ae11b096e559c3cd7db356fa8098e757c5225a0f44786f45f0b275d1cca9e934a78c25142902f878c82058b25888375a801c9008d92e028b27a93cc3f42858824563d011a99c036a9474ae5554d35f5cffa25905a4d1011011ea10204a1ba3e1bdb5746eedea375ba29d44895e0108b7339c690d2098a14d001e40707e44e9104d945ea25f59cac41974f82b55ec12f5ac1b14024eddbc2caa1bb0719d703e99aae92dc2c9effb1148c6938c5664d63a84433709eac9a8d09b85c63b4b05e47415ec033cca4a14e6da4bab51b8a7193fd845150ceef239760ac4c5da2996881fd9164cd9c714bb46309e48982cb6b8d096c46a069b8f889157f4bb178aad3a4b7de1d9792430e101099e903911cf2360be0dcc06846efdc425e4d62df0a0d6f033f7fc36ffdbe0b7e95eac247ff8e8a738f0cf04 true -check_ring_signature 3df25b8fd7371d1c1a4aa53abe841593c1df1f0700ff2105b80e09944f2ee033 bc72ad4da1d602930c1e7366fda554fa7696a5fd69c506522c831ce8905538bf 1 6bc0a806cd8517385bcac3d4f4118d7fadc89ee6c739830a8b03d97fef9b7159 26dba86be656ab5ecc1a16f14257ac318fbf09ec63d207dad2d96955d7551b0faf9729527eaa50673db42ec440d177093bbdd40b15051aff344cb61788a5b7f0 false -check_ring_signature e2ef9d0c903b49dfc64407a7067076eb8e4443b9006062ff7bc773726db419aa e0bb98395060e32a80c2cf151bdaf68ef088536d7c983d6523fa663799e711ee 36 06d8a21662534e26c0e2fc3c67796e141d1f0df295fb3f88391bb5a068c706cb 3e67b25d99d22c40493d15d0e0bf09f480e676d2a51354e08972e12c4ff92172 f01d1385cea278bdca8623f8935e5bffab20e314761dc8d982b10bc454849a22 75a724d3ef788868fd59e38a5b55b192193f2c428c5464a0d16efce5f1f90852 03b1baef94dc630289086907845921223475b83fadd48c397191b3da1fa74c7f c1448b12f02a273cd30209ea25dbeec9981aaafc581762ecd4db681a6e211060 4a8c42be13c4da1031f5f9d3d0191e2892ef02aa056df5e60ef10be7c229a933 bca0aabe4611e6c70ce23efe270c1341b08602781de11148c2f96820a76cac9e fababb1026e9e22447004836dfd8dd86d80c91421e187072d8d3cc25fdeece81 04de60c236e72fea6a1061c743f4ee2f5342185deb61610543a80f5e3b125e69 6c7fe57ef43ef63560c116c05719daa672664d5f4de2e48df7a61a228e987a76 ca38676ec9e675291af0ba62a368c1c4606ae9f16c3e50b7cb46a2a4f71e7ad3 b95c0d1cb9753a701614ac64b04136af33f7077a45f482b6953b08dcc0112e8b 33746a4f991a42738e5c96719e302d65d49b0b0e95f420d438caef542cfc712a 2f841ecf1d2d1ccfdc79b1d0718726c2cde63470a184bd40ae1337d9cd982900 4ea27ce3f86b0c555b3b7f9a26f804c97ddf66d101cf0b341eeac51aaace821e 20075fbb836bac47eb28f6fe9835e369d3f4e2b15da96602a42f2a233cd3a16f 0d6b1ef0e651e1ede7cea0d7e886f5238ff27284de368abe85598386df1e2a2c 4f3a08cdd205d87c561ed3eb2eb23cf29cc5b04c0a6f1eb2e3f5aaabf15881cb 7c2345f373c55bbfc7909b92a8abfc4cba9ba214fc863e443c7982ad8b072134 82676f7828eedfc4d4d03b4fcc7dd572141b28801a5f769d7efdd3ee7078586f 8be15698c5e7b634c77788673d2d2e942434d22db3c154751a4ad999104844f5 1bb00b48d364452542a4e16cd28ad898c78535ff3296f2c28dd765577770c740 8d2bbde978bd02e070f215f2b819ff75426b6cd0ec93d16f91ea4fa2a53c9e2b 9cf7b4ea824aebf2a85ee0c6f093a2e7323c5091df75258231e9b806bbcc0719 81825c1642bf7b6614d1452feeef1a1ac855912cf7f74d9e5ff05ea170daf4de d888ef9947aabcbbb613ef4ee080cfe3b31f8abf8e2e1a786ec0f3949a0c9b76 55b24af9455e7b1ec5de7ea22ab9a00a7a4abb3dffd3e0af5bb5509318013e85 45b69c52ed49a50068d1fce47e40fdbbe6a7ecee25186804dadb24e78e743b1c 76072e98111b3e973cd41324574c2031b989fd8ec719e36fb2c5bf44a371c902 0e5abf71ad5d52b751de0e669e08b1d0bf14fe996f199b73a12d165dba1a19eb a006c621e0cbe0a91c63d8e2cdda9462f3d81f3fb41c7f4fb476dcaa684d1318 c6d58d5fd7cf2ae80b49c914ad5ddeab715bbcb1af880666d61f82e7bec085c0 6bfc8b726ccc03857e922cc0f703ab00ae98bd534183aa25ad5baa460e6d01f2 da7e5b977258df955cc82fbe5ba1a4220bf776b9b1ab94bd0701f53c7fa9dec1 cd29d5e03b28f07a5c7350863c5e008a4e3b2f5b9b77a4971e5754e142a107e8 a5c6a5aaeffd692846f697ea95372fa4147f26effddd8e1d63df7dcbb8688a01b36f39e9ec6383915272d105f90ed49a33614839f21864977844be969fb95e075521e0a13980fa00e2b8cebfb9a02e8833c7e25b4692e13b4526ee95aeeb4f0b8a292cca0e44288f945ddbe207e7b3242c68a117acbb3e811a2061cb80d0d0044a983ea50e5836b9f4696ee3ff1ffb9c24f1b68ae011edeb8d3ef2082aab8f0074700bde83d24f97e679ed8c89f1eb07a0f0d076cb58818ee0321c690ea427033843c3d99f31383b5a568128ddf43bfbc4d623bd0a91b9dfc2414cb835fd2e07a62f585c397cbfa91b926d40159534a2bfbafd5ba69d268aa7b4617bebf5ce0e782d458e3dfc691a76783a968729040b134e0c2fd98907d8b822c4f3de573e018e4ea4a77a71ffd0a0c3a70ebadd533263fd91dd657592e21c6d287040f63b05dd3c3ed17fccf4bd8210d780382de0a43bd27828ef4db6cec6ad74a1ca21c40853e38b86836532be345a2129661b1865d820d4d45645eecd01093aad323ac507fc3e2f6f904141fd6b945ead8542927a8377715ca1fca66cbb7a167a1fe3a50ab3882ba15a4059c833bc2c70fb57385970ca2d311c95cdf0e3c312e5f334cd006dfea89ec6b309c0d8af9ea3ab990a7817001eb05503168fa9ae41c48db02509f96fd13b73ff9c262d6f06ccb7fb12af3deddac1c19a49bf56a2c94055d4ad0d5f150cfe7e141b356ada9ab07185c8824bde9901ea61e4ca8ce8bcbf520a220f4d9af95e6dde31d80d6e7ac897e4aabfae81befdcc2353581f1cd14349198f014618b14e862b88d345d70b47de7350c00f8b70475173207838e58672ee4aa7080ea1a9bc3bfad0e36b344f10dc6b0177a6250d73d21ca15395fcf0074bb6db0c674c10b9aacf354bd79c45da90fc98b4d2ddc4b4a5a5d2c7eebc7bb7a11f750e21644ef6d70ff992773045f8199b663e052970a28e7b71b170427dd1e6229107738d1153be19bfa7e1d4253d0ad7712274b5d9408e85c3799417eb149fba7202328eb592c45b63c4a2ee149fe93e341df71acea505ec4ece8f638880e70cb106ebb8d0a988a1c718dd959c844a3af383cfcbe0b4422a09c73962ed2c32d3e306d48a931bb2c92c6d27d33267546b6cd7c55dab86f804d38c5d4e7fd8c4bc2704853218701d2965bce2a7eae0079b043c5dee4314312c9c30eb024245ed120504bf3e07a83b97a019fbccb5b03dfb32a87254adf0430482987b84439a3ca4f408b13ffa633af3def9de1f41cc483fa6dbe09e5b84411a7169dc3d28f9f86bcd0b113605db4cb39a32a1a4225eb95788afc6d5df3e33217d1c9c6ad17fec500e03c2ebb9cddda9b990019ab61e431085aa607c5a3ddc0649042d1d92ff8474d40825ea84a1cd158b27638df8f8b5fe6d8e963167617d5e075d87f11ef75779370f165a26f4a6ee824ed4ace6f1ee0d9cfe13a7d1387f55f825e6657d670124df01640cfd99932a46f65f3b86773d36793151f2b900930c53f07015021ce7ef6808de60a5b610a34a85d3f62872ecf2a1ef12ddba025db5828596e67fd01870e40ec2f9de16e4a471c47ed692e2e76d0bf8126ea218c2f4042fc19f635657e3d506a4e72a734d74169cc5d005cf1de7af01ac3b7ea0edc67d6ddcc2467e6282350e89192b5931e4601afbf9c4651d5d57681f693ad5dfe74c7d9415a903e1d2070350e8d432fc28d566cf8cdb5bdd4d691c20f36eb66ea0d1b270609fe924ef840d94a33f8eb27a72320e77182c94e01bf2e6f11a7a8484bfb0a5209f2ec88cca0b9ecacdd62f3a3d18503f14fdab9b54dc2514512fb83ca333c1ada674db000508387e91625ce65c0decc5503a75a8d414fbc8aac9d297185c1555af1bfdc2e208c79b91949ca1fa05622a0a0a9e1184c983783f397cdbcd6fb81869c7d2346a0278629454f077e5d2d244dbbf182129baf3f7d9eedc87c4da57966e6ed0b1a003e0ce0036e2265d4df74267d11ad74148766f8087c2efb1c58cb8e2aecf3abc09e53f582b1d20cc0da1c7d3ad1a18c79668e2b3fa61988e047e92b5d72249a40a724b5b09523037f9382c637441ef4f95ca2240ac3ddc3130ff58e4793e967f065181eaa05152998f10fd5062944b27ed27fc4c2f9e3a188af55390f27a75eb0575e964ce09f7d8f5c0e7fa2176775001cc6e2695caaddc5fce3d343775230f027ca9f55a2f9a7ce14f3adb4a83e6440a5d228f019328e7a50e840cdac8f5bb098b33755876fce33c4a7b97f4df2e01d87248588da8375f16a230c35bee5ae40dfef82109652d8a8026439a68288c4887608108a70cdaf9fdf51026a80843300f6ccd102daff7629922719f4893639d0ae058d936651db4dc9f3d55b7ddd7ff0e233604c8eeba5ce58e077e3660a9a833478045e8e08a4aeb5e7d82774206990c2a384a30537a44c5a9ce2c9cf9762e3aa8d9bcde370f5b0048b4a2526f3c5603ac44da3b05c55fe459b26f32f53eb3e1995c1302d35024637b0a8877032def05c66105f6f6eb71969172f826e11f872638f05ba868cd7cac51df450d5afa0b0e197fc2c47618971c3496dadbab0d059ebebdbe050d4c425010a304160c35b90cd72154e4080e3ef1b47806c03eaac5434868c9bbe09848c7157d6e30d5ceb60a5a9f63a37011700ee68bc4abb6b22c580be411198b4a83d488c52fb663a048023f304c465c0189571d7ef9baba762df3669f226d5671cd4031df2711fe19640a0c2e6390c6d55cb69310159672036295f2bf8b776756983fe3c9cece9e1352066551fda9d6fe944857309fdd7f435a6ae53e655ed1778efefd93d4ca8dfbef0eab595af6dcdd9162af4ead9b9b2a415305ad38347279561ef4e3290878d95b0a247937f4db520af73cfa56593fedb0c3f4a21e0923101a738bf826180b26a90ee21860ee42012e54b407bbaaf6c436591c636430330832d230baa7d06c3b650a376379841f67ad2b52a08c2381ca665e719d98881dcff09a34b66826de5bce0c34920126eac1395a15fb31afccdd8d60452030859a52ceb8e36ce77dd2d0fc06967b03c217017c1a859a9d323e44b9a890ba82a0e67acb40e965d08c7d79600b0ca0986d37e0a502dfd63fb7ca8860a7134dc03bc7f5407fd1b44d7dc11647043d2e97e0967cb6ebff1249fa79d5081dc23a0c09c34bd1882d9edb34c50aff0aca8f21b92a437adba6e593790be991feaf15bf2b5d2ecd78a99056f4a3b67401 true -check_ring_signature cde59628ace31fcc34b2ca24e15f6ed9e1fd8882d3737281659d695cf3045dad 59035801a46d3ef6e7ad8c66f659a088e299929694ca220eac8194d7af8055de 1 b8cf6f2577486e977d4c6d68fe4fc2eced3c9da0464bb04802f489b335dfce58 4e70ada41139ab8c37f58298e78b75ee34a83558e7fc7c84fd0112bb0a464c0326890e12439786b9f0846d1094a23d42574d1d64e57ad6f97694d75e4a37cf0e false -check_ring_signature f8bbe0ae5f8efc5422267cc8a2f641aa17b4166a7f41793fc7ac175381ce0a0a 097d36a9b58f912c996f15f919e621fa37b4348028ff32aeacfc24550048d464 1 9fa4fbff08e1d5d1b4984f3e7c39f63bf6dff2edee83e3053b762c47153d5f1d 30797ad8bb83fe8aace92eeb0593b4234554c0341ba0ced68dfb740f8cdff1857a7bc71b0d0ac852b631414b801a36ba16e79fe0425cf98d8bdd9ab4e503a280 false -check_ring_signature 78fba8e0b27250c12bea7a42355b75a6c65d448b4627944669bb259f9c60fae5 5ff6044122a36f53de0b558fd28b0e2d99099040d0d21a89071a52201947cf82 149 472d28298ad3cd29431dcba63b0151bc679708179a012814250182fa5f9c8128 667cde30413fb5ac90bda398a8b1d7631062580e6d35dc19ead795e580e847ab 4a077273acad0f284ced78df465c5c41b7f17df4fad0dad49e81011e68e43102 6cd10bcda5343e76b5473cbc3e0ee67df772920037aa8bcf74caed2887379616 fca30e4d6f10827bdd427657b453669c09ec4e010ce3fa0e02bf4384df0e1f0f b4b63f02cbb7fbb7062af2b8928882059f504c2efa33c292328a3a718193ae2b 5ad6efce7e89e20238eb4e9f0e40477f10951447dc951d6cc34bf3cf89316635 f1d8aeebd5f67acf3b93b8d165e8041c078cb7f038b8a96f6ce9b2c486d8fafb 6c9eecf3aedc8e67d108f84e5f28155d0cd9ef93c090c99cf0db4ce317813b8c 272ffe746005819aeb8e11d87526f13fb6998660daea833cc95e5d402becc8d6 c20a90d71f5cd4ff8636de020ff89dd3b7746607141d127387a4d850f7700e4d b828e4d18ee7d414cd1928890d7ebf43cf65ae770df714f7ec57a95a392ed9ea 6289cbed62df8c0f500e223ff87e38214152c73adcaa0d0576a82fd7034746fe f0c6b88c39fcd17b9e3d1fc7245dc5fb6ab69554f368f29329570b584da3604e 4f1380200f36c89410dce48b10d4739fd2c8387af343607b9c69198e97bc93f2 1bb2f58c36e2493325d4703bc0534a0f94813da32e36fa8729102edfb41fb98a 0d466612b94e4f704ae279dbb3cc21cb4e502954a7751fe2df1491919e72d2ff 89512d6daf71d92be9bb5145c9b2d2381c7afcb72eec525eb8474992f1c898b1 4ead3ff4d2b2332be13259971d2d1a76083badca427e3c805bf47415ac02e3a9 40d20acf149e2523ff2fc98b7155f887d5957f5b10878d7a647c1cd4aab10467 d6bb5c7ad9d18a449574682a607b32854466ca80fdc267b73147d5851d6775d1 cecf34fb124446ff6f9d0bd217bc6fcf111c402754980bd58c04c74bd7050ff5 24432fbe2f1bb024dbe7ec9c7acd47f21b570af0ac84e4a00af79d56714e5c89 6ab6ae76f7fffac8f557c0dbd757c2abd85ece986b34558161fd446d48030e48 95df286af7202c52976b88c4a8e32007228d70a233dadb0d04afc37b73a9ede3 432e34cb4400b0b794629d03605b515dba23aa982e717305f377b59ed04100a1 1ab9360c6da753f7701321cfa460382566d3131c3b095897c429170d33f8e0c3 3418d001cd4af3f8aacc55320807d9d2ca2caa75897d61ec5ad5729afabd96eb f5fc3036bb1c6f743b885471ac8829da9f176a3967ffc2ffce9a7296a71ee7a8 fe961b17591e614408f8c0f911a598cbb987cc139f965de7ea48aa609eb483dd ed76fb1c4c60515c3ea625153d0e8332ef36c0f114e76358b391357b61eb818c 4c45fab0a0d12cf28b0d10540fc4a25f2a2d6ce1e078f648a48f5555108ef000 76f592324d230c051a45a6a2bfe90007dd62f3c3672d2c52669d5e8486968135 4c7bb972ff269367e28bbd1260946e087318f51b6b28e0a021be9b000165cd12 d5eb784b53953fe6dcff19bda2005f0ac17a1f45fa3706f95b21abf7c215b3bd 4b63ec3d59f8025efda3dbc4b67a4095a1851540aabacc6988ef77e3686d3c37 b1db5f47d7f8644d3c73b0eafe7f34859286cbb754ccdecaecd7e490b47f38ed ded87584d3663be2ef9b26ad4f7966abc0818797cf82255a9f7e749325d269bd 077c7838630d39561e7c1366ef8c8b4e0a218c6d4bc01cea3307c28dfd540a10 8d6d5b473dceff0a96b9bf6f95ca740f715ac33d95b247e88db5542b2331b472 17cdec341b5a262dce6f7d3acc7dbe7e593cab06d4f83a90abcbec07ae5d04f8 2814cc66d63093b746e51f430461e1ff3f4fd67a8bba88d69741936334cc76c3 d8328469bcbd966384d3ed6562747ee55600ad0134fc6db56e5350e844863ecf 69aa70c18609cc74af8f8739cba7d10cb6449cad27676ae8a2c2821380a4dcb8 553b35ff78c7a0487c53624cde1890e3c087d7296fae0c9a1aca19465002e1aa ce32ab3a09e0d55d43b3275bede8fcfe121c6d84f1c1cd58a0bb16d375734020 3c987ca7cb6815aaeda291e4313e7cbae901dfea0f76504b1d3a5e781da69f2f 372f112a771ba08069cd52d7e0e140f36289f741d6b942d2275f2dd3b8438780 4733c50c16aab38c1b2983e2f0747691e511e5625fbf4ac6a68da2cb4cb4b097 ee6186aadb493088e7455b1cb5261205262878a5f6a79b55c0fc8db1050c0ef1 4e80c8dccf4541ad17b4339a159195c7dee6d611eaeb42ee7405364b0f9d037c fda966d523482d71a9528231c982472868a94df2c96338ca982e0ab9b26e3949 67025f0d977bd103ae0202bef32de970c32e0eed29a52dccf53bc63b739ebfcc 94216533f981be6b90bd11bc4a3383b84ac93384b17911e2a9297a136ba95e71 c4e8ab63f668f4a5968d53d125ece46d51744b9ff9724439cc40c5793c681d65 1cb5bc0a55d17400fe50c50b2d94c7d2e9cd19cf26ef4ad5deb9a4b0e1afbf74 3fad989f57813b9c09ae252f7b5e1405928a3c2b80274b991f88456ecb6b1ffe 4b2119a4c39efe72420048b5bc904edef397fe128de0b945e85d6f8208433025 86ca96b15f6c459aa3120d17993042c9011721cc7e3b9a84b96ac339d76227bf 85306b5c4e7b1b41588dea1ef006315205ceb013d1cc7d100614be4469e7dea4 d99c9cf518cdcc183ffdd6254058f26a45050de951acf642aa738bffa0406d69 7e51ba4bab63d5186bbd3e0eb8a11cc0f6c5bdf74744cbb488d378b7db014a3f 47c81cd461065ee922da60ce1ec1212be72afeee8f7329f742074a219b03c8dd 9e37cdecbb0bbcb3c10d20d663812223995bc747396347373b46e61d449d9511 3e1aad4f71a5ccdf5fd2abf22cc0aeff56ea7ec55d06a2dc7ccd42a443f0ff63 a4b8694841c85e9795f0cba43525f04a820615109a56b26afb8b67284e30f9cb 09a0b357fbdce5725dc2f8792e88f26271843a1f551ab508827cbf2c4c9a5cdd f831e1a69aef230258c26f140598d21ea89c6356ce609ec3902ed83ed00b94f2 3e4b26a22240d3e6fb5e1baa93557a6b6854df12708c215abb1d5507d9423df3 a70491c302e9bba3771ca3f7b090069127292829592b13c8aca73c1084350bd8 794717ce00860d61a0a56190aa651da38c1de32918f90d93cf3b66e385ea82fc 988d8615356376ae3cd437c26efd235c4df5eb766208903c81baed3a05957e1b b5599809fe706ddbedc29268fc8a79df1e55bc2a9dcc9ec84d14660855ec135e 201c45514c119fc8bd5e15e2e20159f481c85973f0b31b435a308765f94d481b 1e4deb7e05dcb5bc3f7770937cd75ceaad56a99e23c47f6846ed04b7cef84886 a5361150242d062c31d004bd61c9e033529598112df0568ecd9022a57e047603 d60cc74f52c8c3db93e78083717d0091b11e2d67dd908336d395b53f25417168 426d72c159b2ef432925716c52d5dc7da9041567e57744218ea59683e345343c 845f05b3ae8859e82ce391aba7add1a6491ab65a3d44c98cfe38e8fd6d5be0f3 f6baf86a63c2a472118ec06492a90bf17d64d2dd99456358daf01951abd91668 6efa59a600c614f7d27b09ae785c262b99f2515a05e57fe8b9cccb4b53abf072 77440035802dcb20504a5abd1fa7d2a263cb8e6a58bf4892ab965022d72e43cd 03ae2335fa0169c910214ba68dd90fd7b8188d4228c02f2d2ca30c65e4acad6e 5a0544c60c4cb472173586d6ec9f0e3f9058d0daff41c4571f498f115269db2b 8dd6bb436c3b9a466df0167e19b415e313c39eddc19f2b41559ad9ff5cedeeda 73f083ed4fce395f014d94938eb36c23d24a0465517fbd18484b79b30461987e e3322ac73d76e4e1205ef85b2094b3490b9fd608a7693fc3942df3936b3a8672 155f8a68d75572b27f3a0b4ffa048a9d0cd8e121463674d39f973374e91eed04 f3217219d27d2d3e95c6da1c1221449a6a2b1e8613299feabb835bc980d0d157 72c6352281ccbc07bf9bee454f5b7fb06e0f8854631975f0e00107816d5f08dc 9c1e969c5cfc9563015ed954d16ae6ff625b2cd65b9222d008d7f130508c152f 78c37eea1939fb15ec94e6ddc6cb210c6e2e7676eea7653e59f71943d56d972e 7e2fb5831fe1644e885d3b7a60a31dc109bd4a934fcf34d0391f4319f2ab3389 dfa7229d8838aeff32b8f2f8217cb5b78be31c6de500371527ca0989c5643e46 868eb0cda8a0185c01f5a2ac1744200b548529920f8b16457b8f22c3794ef7f2 ddce630c367bbbd3eb6524b0a62a352785c5171c468786045414d98a6ef90363 ba5526440bab8ea6ad13b263e2143e1889640fb465f3b9b7defb404190d792ae 5ee6049b7d7f2d1ee4d084fa6a4ad028d8c4f7f0b2c3c76d458b0c6d842ff973 b34c55f15b0e88ce1337ca1df0278130d0581ee97522b8a518b8d2e2e25ea23c e3077a54925939c639264dc96c6c6c6b36f9d3dc93aaae9093fc02b21de73bc0 5cad325f575f47004e07167f7f9f04b280ecfd2b76a5d5367116fdbe99a63d5c 37e8b2cdcb4d207b52229050a271d444ef5035952b75c07e6d0fed86bac00d59 a723018e6c1ed4dfd63c49c6a13b832c6d9855e069171bb590267ef7cdfeac12 9862aef3bdf06383a18fde6e459bdc970e2b5ad2542dbd8633a0d1c9e5ba95cf bf6399bb892dbf1e2423d8e99172a46525d195c8fbadc6d86134cfeaa4038adc b1d469dbada2dafb49765a22ea1e8c2ae21c78bd3cdadb661eebe5280dab45aa a08225f67558415d568520314fbf1add4bfa1a6c10e1a7fdf1af55a4cc965890 0fb99a16bafab6134baca54a90ef725f29614446b779f697c6bf0e86af207617 321ec44857d5d26d16919f536f47753cc0ae72e8b85adbc499e6de838fa9ea9b 39ec5ee79a3a699b108f002ab39b34daf2633149b6ffd2c7116770d11e82f768 564aadf7f07386f5b71a4ee76da4b857e3d05d9e0b14444362023683d018590d e9d1cf20171968a438aebdf17b479914847e0cb4d9ebc4e5af9791a29c733ad3 3d51b7c3c848ce59171b22842dd9e11d1f5ec671622bd53d1639ffb65fd3f567 8ec3262d0e6bff92118c4223669858cb5cbfb8ff92fe761f4ec641432af1d5ab 1e7294b99ec80f734c285838d0faf21adfc3e39271f4cbf79da41c9572ce859b 99ce07e1187f6cc58e3033d0c72225c89d8eacc8947c61abac21c73ce3e364fa 285fbd7a4511e45bf1555e219552de27f47427ff8949bc53a948d941590aebb3 f4830bd3a89bfb9f64cb1005d739635eeac3cb0ce0875d0647a67ff5a58c7cee b75dc0c7a93e8da431f8c74c5dec821b4664fdb6a7dc100361b48d43533f9d6a 389b31647f120e96b334cb9d646755e6a0101e72ad2efe085e5518cea76dec4b 26a704b08e73bdeac5635a4897087b2050deb3183ab56e3d8c6e4071e37f775c 4ab105a90947074d0f28833e1766c9345626bfc66bafc815bbd6ecb9881ea594 03701ef188d594bd6a204b5d8f3004ede9f26c739e1dd5cbb413120baba9d209 2dd6b2fe28be5217ca7fd47f07849d73527d7c99b48adc7053813b43f62aeb06 a3840a59d47256b8b360a62fbf66bf7994f54f858962bd533450066b94fb69e7 1925c2b49a73640fad0e52dd7023e428be675144169e3065303aab65bc984542 201e7b844cb075a4e2ca7835e6b675070ec09f8b761dfff42e5f05e3ec9f2044 ff2553612198ee2ffdf641e969200875c3f853d3d76ef4b1b09efbd19bf865db 1648f1fed4ef2a4a022d0103840b5897e13dd65e0bc6bf1630129056c7b8a8eb 8a32055901f11500465016254580ca5f8b9332b05ee91b8b643db104f691bf82 893f7b1163b36e73d1967a5ab91e7a02508be9b77baa2d254bd3c1f923a928e3 9e201d7ffd20df4e1ca8ca3fa720d422d4383555f11e180cd234e9004aafd4c5 ea19041327ba3c0ed07899d03bf9ca7eedf08b95d869e73e817de82828049e8a 3814673d2f640ed53cc8df8bcede7e2acbd0194e5a5ebeef398d6801b9f61aa3 3ae68d989f03ac69f65b62098e504738f162fdf57c83bb75f1a9bbf3e128af60 8fea39a42ed75bfd2d2c5ba637caec01e2408bc7a66abf4b753e2d813900b4ba e66e7a03f4ddb60acc7f13668bc4212e3ecd54dc68a9ece0c54d706953730a64 e819d43d5e89710484d0c58a71743be67a182281d6d1021b8ac15a1f5360b4a9 3c0350334b532bbd6ec381438569ac44d6cd44e6534faf1d1e79a6b744dbfede 8e9aa15e649ecc9c9cdfa4529387823bdcada651fc86399edd2250726901914f 4bb55031829ceb6b4ff1b46d7e9bcd048960e747caa551c17a5a639357433f7e d646d8578e61c7149f7c1c5176317790335ea5f635fb6193c0dc570d547793fd 8918d2afb916ee5218e69377bf468835d954c03f8534e8cb1f898395a1c80f65 2c9c63b10015d07cecd97d943c3d651683bc483fccb4c5ff06090f68edd3d8eb 5425c0739c7c07e9ac6c2f5461c3b121ff6fe08a01c56085bce00d0e6d8ff9f1 3d5e483be30c7ac21bd624c818ded72cdef2020f6b6ec694c3d34fcd1db69494 fe0b313477f82914c5486674e1869f9f9f51709f81fb5df26acc8f143c40e8d1 5d2f5186dece078f4304ce0d81eec336ccba3e4f3e27d678cf85a0644bdfffea d28c114a4950063a251e54edc5f15d0e6e9738bf9a04e383ba4ddf9619339953 23f21528bb0ebdad7f4509e7180ffd5849e67f9bbc1a54c20b9d35c20566690bf848e8f6e9a4b9ddb2e68d0b101f8b9c3fe78bfc9d70a16156a6242f77226e0e332147a2fd04f93afc636c2838a02fc0e820ac8a8ed8db433585e101f76d21037fcb7584231c56d1ade907ac6f38284d822e8afae3cb181b63888c502a3a3a0a9730fe389d064623d45b9954a26aaaa7420ffbcd5c1bf30272fefe270da3ed0aab4a1255305c11dc5f05e8d7587f7bc7fae14c45ccec1acfd87f36bd01a8c20ba41c8b934d64755d33e9efd6900f9983ee93f8fd4f411a1b7c0350d66acb9d0fd1b0aa0f590ce01d327cf98b0ddc0165efc721694056c583949cc22dd5a42d0773db1b806900ee500da657ec523a7d7ac442d091c739737e6e03c11fdadacf098da6331cb052a62bac7fa8aa877e09b46d5b644976bc2eb514b6b89eb8b88d00fa1495a3c287dfb5d67c3a74d4a62097be089602276d93f93a242dde46e9fa07cf3b9b10eb473eed0a2895285cf6363c100e70d1bfc52164b8aaa4767e150206d5859f0792f9c414c973865db58142a5c48504f535f6629d7d78902dbf1a230f2b2134fccd899d329a250936ab7a6bb0e2b28ca42710335050ebd19c2a1f6504393a10be15598aea0736d2fce741227cea0fdedc315374f09306992422a74c0ad518a3db0128dbb33b4398e47e5867dae8b1452b6042172d344db4ab0a489205d1082f148ce4cff20c08b150c8f9e5c14ffaef41930a7fdfabc278aa93bcef0aa15c6037e9c27fab4d0b4896f809ef126e9506deb3b9a827ad279c30e548de0bceda51415d1d1f0528004ba73a11a5ee41f3f1babe5db100043f1de0f87c090badda4ec43c542dd01323b5fe0689302f2afc27d8a83cd8d3154d25f60b4ab604a4a365f2ab4338f90c38604541bf2b915bfe99f801b352e36418b435550dc800c4c1ab5a47a27b320f82c4deb92cee4279c84bbb9c5591c1a19d20944c1cd00b8e6903e2c9a9dff15c5c5fa29eab41efa5088301e0befa439e413a883960c50140eb5b1145068334abcbc28491922f5830d2a39ea97da9040f62f6bf00fb24038271073c93770712987cb404b7950e683cfe1bf89e0cf9760d6aedf8025a7a02d0113395d74181f273c2986e7990123f2fb47b725c48a033dde40e46b282c3078c1e6729bf0ecfcd1ed8fc7fbb6ac7567ba1c636f24cd05e9881c943eadc570c2ab70214b7239cfbf510faea6b687d314ba66971d59a087cb983f81c2452500971f61e8bcd0b704d80c8eec7e6d312cd5f068b06e4b6da95858f31a9f106560b73e064b3b055a7359acc456358faff5418881399e057ea70c891040f0c4d440a3e8e5ebf86c7e23bc8bf60e17ce041d7a06a0e6cb8e69cf3bcd2ba1782ebec000d9409c56561e3ff167e224317c96e192ff1da9baec73fa158f794cc3569e6099a9813daf41a91ece7a18861d2bb4caa9034aa7a746a087cb8726af02cf5f90115eea07ca78c3719e884da7939ae0378d971c64c1c36e0f1e899f293b8d2c30387d000773507176fec1a11bb2e7f8955a30753701fc3302c7ea247b8054523045e7a4719668d3d79221c75fe39cb100bdb406867dacea18f011173c10be5a90d92cae359f273b54033634fceda7424898b45639a453a42efbdef68a65a247a0439124538e7d5fa63c62e5a6a7e5cb9c3b131c11e2fb76d9f42c21aec30d01303541713596dc1f4f84fe578ee6fb8083621a63d1aacaf0410fb8c52e6446ea90d279b6b1f9416e497e07677dcf125a14d1e7956bdd3cc49d26dd49b6dd417900a5640feda79eb2152425d3e72cd6befbab34844133e9b0c73d9083007620c340be39225cfa36474bd1eb6526a8f73f9e48a8012e7269d3028988252e4e515f9070acd21a78d93beccc73a5d59b28a6c2c7b563bd02625195bc57779793f3a83024aa2f16e37f874e2191a24baadf642e675293b17f8e357d9bfa264a63fbe470320e05b9bc63616fb0abef28d432c1ec5aa22fdfa6284cc2a405ebc3735a1ac01f2e42d2461b83af29ce8b698fcd249305c9f64af16b4c481d19eb8d86348c60f4fcea7033d2e82ee4251ed54ff29c06b764c0bbaf8b5643fea5976fc007f5403152459aaeff3d946742488a8973b026013622d99108b67443c4126a63fa7a106c11dcdbd8ad8238024e72c027e3db242fa4e796477b2b633cec191a122612b064dc144a479e64a4ecb63c038f910d202c9b660861c53571c7cac7a5a7ebdff04f3e70ed2eb56ab00be123aeca321e7e08bfb847048768c15f59c50a4b3075006e4995b26bfd79328a7098ce391486cdf5dca9dc55daa5b76046bc4f5c57f34029f740575ff0b67f21f20c3aa7d2599e3b0b84ab3e2446e9c66a5d3f5ae5ce208152945f4ab9e17704cc1282a01524e9af9d2401c4df60e27edda806aa4a9620d310a4255eade2001f5b4eeb33f85baaecc7c54de5d1728ca79fc09460467d7051447c4f2bd9b5041365ff58bea821f95a6d52c17d5a29dcb1b32e91c83ae6b0efc5cfdb026df0f5a83e20b25e35de9fda42ae5e45b799f465febc20ecf9c2a0d488f622cc00215941b1504a6de7d87045b3deb41994d1e23449fba2140a83e0e37f023433f642f33c236f1b88a0390415f7526877ce5c953d1f3187c7744e809954cdf4c19ad9680c3c3eea6896929e6a3907c3dcd0bb9911961fe3dc196930344ac31708a9652f5a6557378868289f1234a510e902d281c6483fc2ace4cf90f6e56a48d69611fd79a1c4fef3c96dec279cf8e8a7fd9c4fe2eb14e55ced23d0410ee48f252ae4f453a895c8ea1873278a898cd2c54f9f72ec0d01433d371510533fef8384c6fd25431d284afc5a025f38ca065dacf8335342d369f7ee723fd010e5ed3c4c01aee78e631136cb65ec6e5cea1d93cae1f6685d8c984eb09c32b00ee096f6b3f514ca5ef8391f7f942ac59434679eb5085f7ada9b71cef872fc10541996261ab65f13481a6acc30dca87976dac721e720d808274f0ac497a445901b59add6d37ca322900d50e9e0ebd5c06b8af9a603dc21d8ca4dba5cea6c58606c406b636e4b0bdd6be8ce70f3c64e7f19cea80a9e3ad0fa036616e6d6a498a0dbe6f4cccd07d240a604e95457c3613053a4b232ab4bb9eefb0dc2e23ff77f8044570756f3b78078a4833bcf5acc33e0843adcdc8f37455434a164cee46db0205f41f3a4da58d1d96a5967f5c6d234bc690431041fb0449d3bf903c6909a34c02936a529c00287ec2ebc5639c4929cd00db50d42f39f2585cafdc9a8782cf560fd230ebeb5946613ee7822230fe45814acb2dab46f5affb14b6d3029efaf7e9085bf466fc803225910f4f340a6b91d835f473ddf70dd6d97b0618d1715aef1d0b94da2cdd60f8947050de6d92ba7e0194c94fabd3362a3fbf7651608b47c7c20e3d83ecf66729cba3c52d8afbf05a70e3de7917d86507c319a32f4361cdb5de03eecc7ee63c02da394410d021adb9c8a9338902b87eccca3ee9e12e266a41f80cd2d43ee2baa25156ff434688250d30e1436d3d6f49cc6244c8ecb8ff0caaf70b9eba7fd567435bd5f87d63b88fc9ceae9c8dcc3a17867c1294eb8a62f198e9080966388393bbb2068b9eb97ee0032cc34671a418e38603d677e33a323499dd0e071d482f95227ce1f5786b3ce9e7f7a7bfea2ba17ef6b2eb0bc4b5c92042900b828fc1b5f8d02f71344d32f746360dac122fb488ef3c4b318807752724925e0b57814fc3f041be4a6a8de52b71ab9dc6d0f55581e3a019a281b444c63b7bb0028eb6224d15cdcaf58950c469398906c61eae61832ee200311d7d0c905a789c030bbeb37f9ff480ae2996421e9a0028c643ca796fab94c43ea50e8bf4692e3a0f010f2eeb24161f15e91c6a9e7a06f4ba8870880184b307b556659ef4f63bcc0a89a978b11eb964df829235a70ee3a39f01424ce9c911189001e792880532db0e0227db168cb733121f49e933509b8237ad32e7425bc9076894a1b50a687c2f0c070d4928949a7d3a9d87e6d57c5f8710aab5e5425d7947a8d9ab853a091f710897b1a5f44a39766b462dceb75120e65624c500d28a99a921af9f25b660bb060ea2403d25f9e8afc27195e5057f4f9c5ebe9eea13065dee1d9f1879736928da0c71a9d8641307953d32f6bffd9fa9418ab605540171ce3446bdfda0415468510dfdbf8e8f66904ef8a3acd6b95f520b60f39fd72e8a1d0630974629abad6c550614a0730ae468ad06bf3a635453564f6315dc4034bfe9fbe55027709dacfeb009dcbaa9a0d850ddac6a97c0dc338c165f13d2006376f8d5103dca90d1efce510ee447335092dfd0c91c0529a06eac120b23a66ad0226f57d4db2854cc08db52026d0e9ad5fc3a93c66b9af6840b47e0d1c4a4a3a8dfc9f98fa650c7a68740250f5a98560785751b4824c49149f371645c9f9c597f3b21e6d48d1306332bba4f0ee271d1c771886ade11db85b4ed593808b9b7110495a220460129b4690ab10e0a17c02572e92dc04382ed54edcf88cd60a7730009881d60ec2e08d1b86ad5720a8c2a5c1e6fabc3a7f54e048544e7620275335c9e247bf1f79278f38ee4b87100f7c6c6350e2c062901a350fe13bdaa1edab646cecae2c74b558c5d87d9f774089c5b65a885ae03ede09d1e91a50f214ada7319e3a89f3b58b9ceb3e6e00f880e4e5236768ba26e32e7c1d469aa94c53711e2143420cae368ea8c92a651bc2309528968ffff8e25b5e7cfc1843349c3e074407d0679992af2415f6e6e916a4001f534b88115dedc8ed89f5d7df95215c6f2bc9f5d3ffcc0fdc94530c58db1e50ff229857410429470e9ea6171195eae6d8ce3f1bcfa67797bce30ea2bf0b57e0cb6e1e87b05f89b863e4ae0722016765aa73e57690b7c999dc9fc62b362597b016ab6b344e31c1a166e035098a8183cdd034ef11e785184cf33f83e6d136b6b0339ddb81f243f66967080f84a7c59873a6721870a37d13e68a4c845997df6130904f0db2bf850860407fcc198167ffad892f807ff4121f8c84e531d9db0c35c04de35ea672a2c18fe7c3d98f73476b47ebd09e132c0e76c6af665988e733e040d7500e766ab947579db2b1b6cb6d9974294b067d7ce9318fd30c6dea703eaf20b9323b6ccf6896f9d9bb87f2ce85b0ad8cde3a4a84b0883d9be3e7bb0f714040184cbda5187720e807c190c6ee9ef29053016596584f0fe9f6cc8d9c7c351890b7ee4afe1c2a73e99291e62c174427f48cc1d98c038fa692a705b836b313db80ade9b10c94f4a995f742e19a1bf4246854c445ed01ad075e884b2a74961726003356d25e6a00b94a5f50dee17a1111424a1c2a3218bba639977716362d3189f08e2e43daa53847b2308de0e59eae304bd9b016cce317b4540fbc52f97c3fcdb08ea65cf40fb890ec3eaace1865f094acd8c0a348ef6f3bf09083bd2264d1c180d1912dc7770b5b2376e9bd2f62f7d08dc4b809dd4a93d42c2e93da3e328027702fc062e8c6dcc2ddd70f8b667ecf2b05094c4cbd918e0179dcc9a1a937d969a030a182567dceac8bbeae4edac0cd6a1a6d6da6dadfd2665333c0ed6eca00a8300515703e528a364850fa952eaa12243dfda2f1d0ea61de4cce816558a737ecf06bd62dc93db313f4ff4c8f971506b62ca886b32d89e62a74bdd93041b3f76c60afa54fa62bd61be70a95a0639bb7346a9675f7d7a8f99807b5c0613cc70cee70b22a446dc57ef11a348bd6ce77aa5fbc52890c4dd94d382d2eeeaba4ada60060290e8b51cff12cd8fbcfbf2a365125907b4a8ea36d371eab9d0fd1c3ff924e60b675a0ffb2c6a766c93c48c82a04750316a30e1c0e4a00318e5dbd36e7f35e6048bf8bc4f86436d1f0bb8cf64e55112a15d00fc1d34d37cbb3ef252ab9536a7047bb468257f8ac10aab6d63f1197463dff5403e6c91ceaa372c97bb92dc5471084380f5731860284f408605138d2712942c37ac54a8981b2e296333a164713a083a0c8c1cc8edd6377753c7ee8d580473d4e1f16fec72055b23b53f02af4be001c0a20af9a9d7f79aa1e51e2214ba41df6e4673b762bb04bb2bc53e7dbc8b3e02206a986b92240dcf0be9ca73e6b250affa3b155e76fb4acdff680a71ec9fcb0289c09192161b10b5be9d38f2e8416d951131ec1af2033c6fc61edbd295d69c0f8b52bf745d14ea4ea2ae4657914189ba0e9b7e47a3a1c6fd80414f008121ba01f9df2b8cbcac5a3838e830b7a30f44e5e0f414205119992779672e4a2327ca0c3df86cbac2113cb264572c0b9950c2bd735d2ed927e06b892c9d72bf82653900298eda321d76d35d9660b524ddcdbe5383eaa7ee3c71ac3486d3df75031dd9067a61e8b0b858eb7df82ec654599d307d83b0b9c6bac19a987784cd5b62cb340c78c0b07a22d3d27ca2bebbb4a8c1a44e91b73d835a48700ef332c878c8f13f0d30d855da4591cd7f49de4776eb02f09265dd44aacc620a1ebaf936a9dd5e2a026891abdba3454ad1ab1747041dcb94c0c9c09570a47305e9bdc3a6b07ed26703d2110f937c06cb4a7c1cdccdcab907b2fec4aae6111b654f23e5c505d08c2f03ebc1b740e76a861fe5d7f3b445a11221a22454e6742154e30faf565e313b7c04a7aeb63bbfff6272850e4e0609947150091b851b8c5e83f1034e30cad40d6007bf52e7bdf5e4aa14814da01686e8c786d2ed26b59f40524c6444ed3e9fa29808575d07b6f5ea7e351591123e2714fa257ea172e905fcb9ee5280ae07c92b530c6fe2bc0010c9bd272564b7f3fb54d65807401d42688ea5e61641093afbd1430539d2800318624360bf399a55215f32ea63972059f145124ffae662117af0c804b94f015efdaccec030fc5d55e123f690d9a171e8176c12a024a2acf79cda110c873b8414968d8e5cebda272f6e80e3a4c8a9993100af41557be086b498e8bc094c72a216dab5b026075b2efbf1945194f0ceb3c6b997755ea31f7137b6b8500e9d50936f498916c371b49efe7fee57875b7e9aa1f714d82a544e0951477dc3044d8a0b8d20df14da8bd6b47f45366455529dab6249599430065001b5dc3ee604ac455643b118a59c8a710ad7808c02c486c18f6b7d12dbbc4975ae57a044600f242881789f269a2ea420ef73ad4fed51c13fd32f490a667d1c48024a2869cb059bf0c96bbb19bd9dae7712faf8ded52da16ac8a6ef42abcdd17eb7198c4d68091cac9d0a0419d2798d9718bf5c0a4ca3cd89ac937690a52699d9a44df8b5660bff76b07bda465aa46bf1864e07afcf3283b846d585d70f3fbac78a043609f50bd20d8832742ab1cdb28313224ea563428e9794837e90c82183d535f7abfd3e03f4834859decc1a05ab14c0cb47e8c2c06ffc77de0dca5bd2997e87e2decf48099ce07e694326e6b49106c9ba20cacd8b0d02bcff7004bed2129104de64da7f0da6b1b2c7c7b8fa90d3745f9c57bce6f6ec74ad33b8d809461b750bf55461b207f6ef23bc4e6caebff270b85add5944de140b1143909e1703492ae85f73945300a72afdf118f434c31ae9ffeccb8e87e9c3e332304d4fb4e4c03f6678ccd4f10a8eed6509380850c1bde9f0b2e131536de40d10abf74a8ae7718c120587d3e4099e5600639ff6d9be4f50ce9d6de97d48b267a2c9910741c0abd534a5fd7fb90307639d921a0da21cb3fbe297a6beaef354ed1ff9f3c376712aef2ac5c05f6a078d5742996d5ae33d44f3850497d23f83266b6c7cde7a4f869e93daf3a7b15109cf16abf41cd6bf3d605f3772a1d87b6cb0ea47e1a70e9556ebb1d9360b30820d0abd4de11ad4defc15f8d8c741d850d64a5d0a87954a6a97d9734316b0b6fb0e78bf67948133f36544885af43ee791c71ba2ae1f361f5bd31584393ad61ad902bd1dba84bdbc32dd1bdeb493b4480bbe08551a8ab022fd21f80ed4a5a64a7d03da293d6df17ff0c716fde6acad7f8faa280783344b0a9d23511a935db29d5d0d0e3a968f3c39e400ca9997874fe959de5376ce4e26b3299b227b45206b86910c37473c7e3d3360abac9c9226a57ff7d6ea8c3c55e85ff436b3a237be073aa905e942cac62c3ff9cbea7b33500b20ee4bff8be599fbcb8b4fd153cdc722155c0b8a7e1b759f4e09481260aad3c7e9101ece443a663c548a7a631b0eb0e26b4503594a832e131f751e8d3ac4a66d0daf1c60f0693796d399d023c22648ea8b7d054bcec813be779796e23597cdb922b0b57f079749ef6263967878aceb12df4d066bbc937d86123d1f479a50d0b738dc070ad8fecae3693bec983b7a0c6c35df03d6c05654d3bfed1f771b8881c59158baa6c6f4fc49060e63b7965998ae61900dd3dfb95d3fa926a430fccfb688eb02ab945937e251fd45b2943d3084c2fe3207a71fbd6f9225895ee95ad7cd2c36ebfe5e81f30e03840b768902f0514301bc099e9b0a9f831c79cba10eeab492bfd4a7ce5c080ecdb0373d17d760a4f6d85e054c477bd826fd6803a2acb4af2fb6ac1029d31edfa32d684e2fa7400d873d620b26bd3ff75d04b9e599559cfa176506f6e7bfb7eee6a701c4856e181e7e825e0decd7351cf3be9407a91053f1253e05d7bf43add175e345ef0e0edb1eb8ee0e09bdd9b09a3e781391644f290d2c34cb5de8bb40fb7e0ff37e20b69a8627eb970b2357e70d03a6012a1156e7ecf17c6e986182577bcea826429297ef865d4de00a30c89ec3354e442f01996d2ae95f0c3fb63f84d323acef77d517d9b66d98ee05f224b9eec43b9de428d846b37c980c58d44119176b07f44bfdc410e865285602c4ff044e985354a14904ffa7cacb205a1f1463fceaced1b322b97e66a59f4c07e759ad68b509a46e01074ba853bebf5532d83809999032f453ebbb0a391d4a0f1f6affe06e2719ab456f8d732684df1910db4479c25ab96abaded81470c3820bf6d8304afc2240778b685fb3f4c577ffeb0cdc87f85a29fe98ac83d038780b0e46db544a8d9a8eaad2e19780d90efda669dd964ad6b4cb9a67981152c4751103e8403dc428a4ad812a388467fd6bf1a17e797a0619ef576e3963a66c311ba5004e86ccb2e34d8c745344178ec5ec7d4862d695c1c563da928b78bd1eac746c014a0e94f077f5f8b8a19eb2e12a2f2a5259deb8f63643252f92c270025ed6440cc31df958c2b9d6fe62f67f9419e19c158a781faca611c583e3bdd8546aa08b0e6f1ca5b7764356efa92f739142c1798d1a0e33c797aac14b4f2dfc4988d7300c189daed259c995207a1b3e6a251b2d6ce19d67ba9e5f855099df1932ab4ec005586a2592123479b2dc215e04449de45ccb2f28da0a6529788f6fb21ea571410e413683ad9c97ab03a6fee51fb5ec5ac97cc2a15e11fdd32e8b4cbb01cdbec603788ba303ab1576f2d208b0a08b2aa75db1097ac09e6953f3c6eb43e0c0813108d4a48afa7627eaba768942516f0f3b3e06ca8693eda084e33c4cef5b62d390001e3d4550b330cb11e37804a8b697b944b80e6d0fb75feabd6083a34ae3e5180df10eef52772e8798d7330a1105cf93fe7a45bd76634bdadb5bd4c8b94f518a06bf68b985c8af9259a78466c181cd92de37b97f237714c194758b258a44c4dd09bb85d3f134313aedb35f2a03b42f83a7b93fb13c3a4e34faa225961afe8ec804c4e86de65d321a7774b696877a7df1416db99a41c8ee9ceae1876c67b45565085a07198b636af0b76e101f2d128c453929d8cde266f5da962167b0c84511c4033b7a538f9fc23a286ec3eb4912ea8f9838252b4ca4cc53acbf78a3bf34be9f0f5ba771c2920253fb324da9372c57b7fac719f06a10b9535bdd3f08e76b9ba109232672ea0fcf124c0d37cad4c5b482b5f5d4237663988df536bb758c2082de04de2f0721497f6fb85aa159668c612c48d44ff21cc8371401805700cfa807590445b7e65c9bb48f999eae1c7ec74ff76649a7ca4505a06c639bc36efc7328c204f6926867891a5f4ecaac7ff67c6899ddb77881e920abb0d3a9bf906350068a08103e1b6716949195bc8ed1cd67c6bf8398d56b872f96594b03cf1fe2550a490999a8a9faabcbc6dc5ce564b50ad08b261527b809eea73e90e4e2e4e11d056f099397b2a0083b64316bfcb4f48a888489a0774b6c908bcd6a21def87bee2364025c3c07b14f077c25658f695b45a9c78488d301ee9b34ccbea8dded2a879f25088c3d21c19e65725e43d881e6e7d090dfc022cbf300b80e87c9f1a0776d42af0e63b20a970a226abe21436b6b9c4d1dda157f288ae1c210c91ef247d3d266380a3e9de2523f55f3d0a48dd1e714f33a4e1555d32f18fd7fdf19bdda76a35a0a05bd3b62bda3d594a8c51c6587c72c2535513042e0eaf8f728482a0d3b07e0c905e37e354e7a0ad38ff58e7060c33558625144049cd6553d696d45728802d9c30a954f7c452a12e26239e1af76d527c2a2249827fabed90b8dede93f967929a706066a21654774fe265c2a3a94b2f497f74936a022666e6a979b34509744fff5088fe7e667e242338e06f17348805ebe0ba70ce8a71b3276ccf3a626096d4e170a6afd93c248eab1bad0c7bf4302fa05855d3b1d22b4d8eca3690b6f0d297e7e043bd2aef0d828f9445474eea0f31be01b37bf2343e3dd459f884076d9eca30604b30d88b7428492f152841a0828fdee21c092cc6de28cae1a92662d1238522a0c317284db388d5deede0cd5be307d9e448b4b2ae93f3929acc1596ad584dea901f2d6e32f11d9c2df98cdea781531063816963d8de0d716994ca2290d9a66ad073250fb792ae6512fe66d107ab020c581d0bdf84495bb666644dbfbdb5d883a05479ae814149e2a56713adc9cceaeb0f05c92914dc2c5887bf14dcc8c812482073fde552f439c1d8138057ea2af4d60559dfe927466932474225185026eb9350308469c875a24f96245455a4be13ab2fa88e905bea80ab95dc43b4cbd1f80bc000b50b19428d38a77c204614e29cdbbb61b89e3d3805acb5357a699fad8b24a052fd82e6bef4aa48bee50258a7a26dde5bd1f614482b9630d56d57a0fd25f43010dc0ae6105a52fd837beffeb20edc40cd9cc7c0c03bb0137f855ccb81b59650f80c876211711cd9e6a8b91d1dc9384ff39f6762cea2c83ffddbb031af4b2d90a24cecda15aff5610a4727773085dc68a99d11310a9cd0371e32432166cca610d4fe8e100c8918f2fa6679467f2c11f5c767fa4feaa36750f434d5bd395f89d09c8c20b17605676c93435f8a4d0eeef403a34845d1c7c4be47557b313fa3a7e087a7131a5bcc7f8711375f4a4ad50211e0418fb33c874df711c1926b39f2c6401a187b9488bcd97ca7789cec35c3ce8550d6767eb37b9f768ba2c4666d5bad70789480dab024ceeb79e92f2201908dab0559ef9fab0b132f88d646dd2c659f20a75f275fd6b7a4713565d7035d0d4b8d6139d60bd8065c1de19d91e8774c67006198e1f6b671c89de795039fb4e684c79922a9a27fff06672b86bc4d8245a0d0c92facc55714dacf2cbf8330d7e816244c69e596f8fea9741e00412709bbdfd0a3fe86557170f67ff52ed65200fbfda3d1eabf7ed5653ea65321449fc21a63a001608a74f5465529736396a6c95d85cff43114e28086854971924997d5e5e700a788a4e538d1db4b761221bd8328a32bdfcf6828b2445d8332e2cf67633373307ebb23578418c0984babcbd0ff0f99a40c56bf94cf10e9b0f7ac034df59d654000f03d8ae1b13fdfb37f6e3596c5c07fb560d690960d85b4c3beb87cdedbaa301fd7c7ad04928cf69b34f722266125cbbf250d6d83aa9ae0e0266f7ea7b2a7d0684f5a5e14c6a8a9f62169dd3573c689a9d1e6e63bbe6a2a0126767800bad3607f70de02147e0fb855a744f3a919235ff7bdd9f3cc4f9946ec78fe7a86768500aa548fa07d867ab8aae515aab602bb59a2dac70c4deb2095bf868511d30d1bd0aed144730efad6994f25c8d951ced705c58d04f2956fb0be9f3476b2e9f036807593cc170f1442f9ed24cafdd337d72e29d64fbd386fb25a472a6605ae44e87089e0f1db1f2d2ba59c35a06a3e6f548390b2b95d149bfc78ce5a588d09a231407f1a7a3aa6d8d8184baffad29c53ad2d07cc35f6ad77c6cdcb9e5d1524972770074960fbb9698f73aa7d77bd31eb36d170bfd3d3e187b1268b2f23a1d3a4d7207d369434217c64106508f4e076ad9c624179b1c63815e64eb0f93099e0a272c01806bb564ff2b6fb2b7fd565a7337875871ba311ebc4072f48d3a9308628fbf073b36b0145535fb8e1d9457f85dcb70cdd6b9cbbd8df7a9374371bf01fc06b30e515994e6bf7fb1933f1090a2f426d6cce9b9ec81535e44d095d40a410af26204ddf1e972847943ce633f94c0745b8f04d8ab42e43ba80f1693b619bdd4f9e900b2fd5a3a5c093e877e846eabe2af1ecd0ad10bbdf175c47c2ec02387acc54a0988eae527535ad6ac4361ab56f9f27ecb63be9eb109a24561b77159592231a10ee58b43e8969e91960141e5cce011010d272caad4cdcd9a589654f00a28e50608adb28142e942c0bbecf11f86962b5810afb04340484fedd90f2147dd87fd03076b154b143f559dab2a321151dc5a2f4288f58f4edbd2d83f33b2309dbf68d307da873e67a4a16d162a4c7d2c92b92ef90fc023a2f13550689d53c0ea8cd81c0e0c52c4a5e0a879dda679e035dfcdb610230fb6d2a28c4043e0f95be24983fd023f401e1d586da36cd8cbb8efbf89cd3e1d0a9ad46c83c4f80c1bf743539a0c05ae6bc0fe04f6adeab8ea4b6e47157c19a3d2662480c98933882cd2b5a1c19300232b41194c639f40b19e71e909af95730b9854e454437d139b4a33551c22ab096ddefa22dfcc808f87f053c1fe0b0ba8e6152e75fcdef4e9f73c40528c01bc086b0296f16e1c6fad90554626d160a057f3b42caa89ccf7d9978886e953460b0e11d375f7635b909ffb015fe90263ceb744e8e56d4bc9740d8e830098fc8979012bc3388f0f57246a1f1fb4620687ba05af9b5619c800c9699c7c8498d2358a072a2a24f23d4d3dc12bfdd0758be28d6bee87516b8638a38639cb4b4725bd3a0d083c99458cfa06beca569e75a3c9160adcc7a0b155713d294665f1ff8c372a01c064a1f388bfeed7e2b1bd1f93394b694b4e6a3b2e06d183881e8cee4c51890b148ffd297f0c77d035db382cd56d67d6a3cfaf8529b20de726792460dec42c0390d714e45c7ab449b63b7b7caea2f2bc4cac2199cbcb1112025cb41becadee070e4dab9b4b9e05456d44a3ebdc67ac54b526bacc4b35e117ccfdf85b335bf2094693bcc7447406df84ff5e70342d9b36190e2f5fdac86acd1835d450c8d970008bf2897ea813004aaf47ae5988932f38bfc466b2999c0474199be1ed7ce0c3063c0fe413936d1d50a4c890cf42a3a22151a7a5c8504262877d099e275bb05b0f false -check_ring_signature e6de04ebda5b4af88394863df2fa8aec219230343f9d6dfa8fed6f43a5a7a1a4 e2b9bc701fbc1db34aef69dad5f220229610ef27c1471962bbdccad2eb17bcc2 2 4b0682dfdacf7b7c028b83a50561413a4486ec5c77aa95de3b0444045fcffe57 10d9273fd729308783c71352b5520ffa4fa684b72eba961d0a5ad08e326578cc f1b480a1870c4b0cebf062cd60fcba1df0505bc9b6eca757abdcc210e2d5dd0fd28bbac8455bf77887089a88f10df27626d54b56035ed9e7244409309a573a051a42e24be1b1b632ffd7ee2558126104ee2740978b46428061c852d583b7060da24aa045511334464483015430bbe3ed1dda9e34ea2bdc9fe79f220a87d98b07 false -check_ring_signature f2a12d6c948f96da86aceb38326b80215efe8fc46c27609d0885e82d997957fb 8d4124809821d1f7fe1dc711c28cc49dbcbb66018af435b27a09008edba3a08a 29 60809009b4b412ce45657f53dbab5e305fb0a0b8145f7abed008aa5437279619 20c027f36db2b80bbb482e6673f1bd4266fa4ed5ed182c8400374f06492a6651 78103cfca95e007717b22e261d27c69791f13fdab18d5eddeebc336e40079a36 00967d15b8655385cc9c96621193e02e24cbad6dded8d8f5592aa5524e487245 53be56baa474d6a2926484ba226659789114cdfdcb9ab26ba6f2b087fc3b6b8f df1ca737f4009eae8549f0d8be6a64c1ff1c6cb590af8e2b9b06b5a606c410b1 3c58fbd8a2a8077144352d51c9b8af61c1f7516193d5e55a1b73f7d4a06e5958 f72720ed4ffef4770c759f785c76735bc6d127c32a1f8707dc2b9e3af5f2f58b 2d2dea2212d7f5c1a49aa3372114922f4662d6da0a83aa147e0ffea378491ae6 64ae6620704feeb6ea1f6b524a29ab54cd6e0729fcca6af460cc2ef2cd11016d eb22bf7e972588d59ddc4f44833b113f220c0aa4e511861e6d9a87a1f6626c09 1adb5c2bb430e31ff03a47dc527e179254937aa07a46fd3ae39037c9e47a28ab 6be090e6e4132243d592cad0271b25b31b719dcfa6d1a9d70cc2f513becbc58e 2cb1a3ed15fce67611eec255407976f3dd2573d961aa460bacc1647243b1bcfb 11ee64840a39a2b87b58559c7f131d3c7479831042b05125ced9f7e166ba3993 35b73ba8827299a6aed92adbe366d36f2d8d5a5ff6dd66a16f1668e679a15d30 929df06aff20c4b3453873f880bd15bcf016e02ad7865377594949ab1d07126c 4a1a2a92b39f1be8ffe39d2a4396a166a5990909914628c98c1b93ec9500572f e0a3ef823443aa6efb58c06fa62b30ddf5f676ec963479df9a952cf1c9ea27f4 a4c560e761554510afb724196ac774f338fb7aaf9a321ecec5f3a34a22919d00 c2c0a4e8ad5197377ff7c9b9762ca9951b8d6dbed536761d6f0742d8b907f55a 4088a36ded75b65f0e4efafb341570a8388bd5a8821c3e289b165961bb2aa032 9eb5553ea97eb1c07161a161eee2bb59809085ac5505e5754f1dcf4566d845b1 cbd155d6c925358f5980a0aeb7a527715996f6cf11089d43c490958a631de3f0 5490944181fea8c237d67727bbbf6cb7847562552df8e9a65781bfa0c108732e 8417ab20a7bda9516f6c78f81d06118d6bd552df5e00302f1cf9f94c24aeba8f 3be63efedc00f2e646e72356c493b98e4ba4dc4df39cd4ee5ae84ace4d6a3231 4046e85b41dd574f93673f5b30b448b7ac9173ed0617142351cedb9e07c5c571 3733bc213194a4db94473bf4827af935230b17ff1d468889d5c9b81ece97305a 10ff79906c13d826cbd42cc761a397bca9daf4443beaeb51fac657e9bd6c0a014030b64e56f2a6a076e758834b27daca18d5edea2ea3f018d06ff7a7f2e0530978c7299213ed2385ade60372b491e5f4b76c5215784b69a6f8734efcca15e20a7223fa5a2b3a99c616e3d6101e0cf9d03b1002748e29bfed99a5ee2b081244a4db76dc8ad0e5aaa328680990442745564f5403cc57ed915720c02f2d4ab55003f7196d1cf2aca48964426a1e705c1a58245b68a4a65f8378c870b464b6bbee09b52f7122519b3ffe6066be72bf3fb5b01e5059734276a095d7709db4d3e7db087551d96550d6e4ec039394f32bc39124bbd15e58dca0b1f57ff7340f4d5182057974b4ff7f5936c59acc12ba6cde5b24c562a679dbf2dcc2d0305e33fab64c06dabf4f7e2f60aef0eaa8ea335f647f663985b2ae811d32567e1331bbf3be2c0a09393d6ffd57cacd9192d8ba43ac7374e68adb7766ab921277e3cc477938b001acd1ac731797c650acb5da57b0894534fc7a52632d07281f802e126659b774014052397bcf50a7556d64d46460507ba5a2f31322eb31a6820daf0ba8c2fa830970e6652a5cae938b1e89922ec451df106f9be6885e2f28e0ef197b30efcdf40fb0f6f3797b7581887b2599f4f63a68100dfda40bd915789ab9ce0246871db40d6fd15a1386c6b4555c197da8991bcff282d3d88f28c5d5aef2012c23e147bf0a53d2eeb904d7cc59bb027d33097c2bb0d697af27e3bca49e241fd45e02844c0ff91506ab80b5425939acce63a0651a022f4dca941107fbae2b8d86eea65a08b1212853c551aae334e02c08cfa1ffac9b835ec173d5c32a16eb89995a871fdd0c6f44a1580f8501ce9be8ee74698aa0d11b1349c80640854cbe7eb5c435287f07f70da81aa2515628427d1b0004499b0e930f98cc716d94433f13ca21fc56bf090f0eb792231d0a15abd1e3f4b1e0d524769bdcc0132793dff18db60b74274606b606306474e5a9fc4e5935f3d9799333cfd4be1b19b12753021eb2a780b76f0c143596f7720b294f272bad1f67f787d96e3a6709e44eb9056a8a7e31a30b520a3c129abe12a08bd489280b7977eee8a1e81437ba9b1f7eaaca2d982f66e3ef05bd27661f544f3160e73bd5a1fd6c5c148bde46b83b9d75bcb6563b6a33272c0afcac3fd8a223e66cbddcfb497d8e0a6514a4f43e58e3f3bc38b8eab66ac8db0954d31302d5c6fac119fbd2befc212de9fe86c189002b1c33234544130c3b4e0fc0a346768ae5b1302ad6859d4f3b9bac138662d9f54f3ab4f668d1f22163c50625e862ec6e7be3b3a8e0afe4f2e7b5d30c94654c90d54333e954810a08c1c80dd4f1d62ffbae938e1b133d261b53a27297729f3c4b49855b0ae664c10d8e3d0a1676d9df601d4c96e17585ed04847bc61488a1c1518782ca77606f26861f9600cb0b8d177b94bd8578cd095f8cda5ac40dcece3d28f34979d87d343b8268a30df603081c7a27d2b1df9e4618c305ed4b5b9502e0529d1c9890201cad1af00f0f3a10e6f09aca7f52d64f0ed9fe1344ddca679fb4008a3706bfee0599a7125e0ff711914571ac26fcfb745ed8019055b7a78eebdd9bb1232c6bb3bd22b93ab7085d0e6fc2d0d86f6bdef2ff5122605c637536067dc17622f29ff137f38605030888b1ba92bfaaf9522c8815d8062b4520e05a3a10edfd092e7589a8b2b7508805e64cb5b88077e2c04227208bc227c0f948a069e22b59d591b21466b681d31f061809a311475f718b33a8b6b628e17a29ebe73913b82fc22b4d4ebd8c7e0fa4005f0799875ba8e53641c10562513aceafd71d332c126543b28d274c73c3a6f6086ea7d33dd8d351e3df72b1f1a788b17d86e25cfab4f1ca7e657553051bc5870794e67111929f214f56413f0cf86e7820da6315dbd7e9f31deebbc42e5b8d08038c4c0d1bb077ff095563008b677bd1d935c893239a88dd406d346e53f9d0b403eae8a2ec16cb0d2a7cfbdaa7171cfdb504018aad06db6a759114606a75bc000e2e9e05765acff51afde6645540e80454ff385b7c8f474c6cea6de275db4d3b0fcc219d460ca6077937fbde35cc9d19983970bebb5a529685127fda4058bc020c25fa2db14554b43761ec4a37c9db9ebb15f2aab21627b434f12571be61c52200f6674d95b808779759e45adc895287f0b4db43445e11869067dae42af5cf750d7f07d3438813ef56bc5b516aa04f3dff42a8419bba6ee9ad9b720d0c97501b07a41755ea9bcb0b55ed679b10c1766fe3ff0ad61e17aa3a2e4e7649df12193f0e9db8a82bdf6953690d7fa84eae951ad2cbdf4be3a6516b325faf7ebc6804af09e5865a6a3d0b205bc062375f95cbabfd4a5fd6fa43d92cbf55fc76b81ff1f5049ce3f7ba0ac79a193f4f2572844485d2078b18716de629a7b7acea0a6b2ef409ede54270034c591aa3af4efea175bb3fe7966f419571a5fdda4e3baf97d62204a74464ab4992e9b1a0d7c8653e4e385eea37b6ca275c8109ece19e5d37d32508ef85696a700b761958496e778376650bc534ebcffad30007fbc2cf90e9e6a90f709cf280bafd5021db9b6f17b2985ae7cf2458ba673a494eea7f75a97778260d false -check_ring_signature b11e6413fefc72c61b1555aa7974e7c53cb526f1efbb8b1b9a4e0a02429c3628 7b2a4c818038e6b99a3c62e6fa7f93b5be66bc753224c2849a01931072f11c45 1 570c285a448dda4ba28e44f75ab3e810e9b4f154692dfd3ed7130b58c46ab774 aaca328ac3f9990e948e48d57e14ea9313db2f9a4685c787e34897ad1db09a0470d5ca3730410a41ae49c2eeb59f783b131b38330c581ad764dacad2dd6f980a false -check_ring_signature 14fdd68d4294f0c572aca2152241b6599568cf6913e193eeb6f9db43e1c4b30a be0f4ec6118499b3bb1ba12ee18044e0296b5eeb6e5a093bc4ca53035251e05e 14 a286da6e2eb20531d9b218ce7f1dc37485fce188237856c88e62b7ae64a7926c 93a2524bdcd2f44e2f43b868a3397187f9052ec9b294a7354c7b57394b0a0327 21258eda5023feae2e589970898389d63ff5fb7aafe05a7403dcc0dd82a2f5c6 19ba5655bda800d1dae11367cd22bb8c5be92a4e1b0e12c317487fb9b89037a2 0a485e668859de3430c2780847a9efe4698c08d51ff3ed40d256fec0ba856b67 d1b2f937e02fe6b6ffdfe6f16f9c805da33fa18ac33caaf3c6de7ef361f2e235 5805afa585d3c4f397b49049a6b8f2af53a623033ba08e1641e6c109291112dd 25164125211afec94f177f43fcff7da4af96df1144ebb5fb5acd9ca2baa0b269 b2164a63f83a7995ecc2a0e662af283c7b2ac5a01121662876c8e1a1497d9c93 a09636a0c701692518dd647eef097f4bf26606cbd73cda2b80fc43744c4b47f0 00c5e425880b5aae322ee5c1cf604c9d681e3ce23bee4d8ca40ef5d05b48ffce eaba12697ea0d694064b79c685e7d6d9c73e3c9659c01fb2c0b5191335c0df64 567ea299fa20c64bdded1923baa8756a738c01b0bbefb7ca2942c7043eaf5380 a99275f7e633e331fbf8b646eb40e0427b7f6380c4e8955b7574c95f4d785339 f6be724abec18e543fb44cb032ee2e0360a9db5ed169291e0520dd48ac47340cfd5710e5683ea184478ca1892e6231299e76e692db14e2269440d9a40f4c3c0f9bbbded13086cec84dba1d214539f4ee0688e87f26a14590a90371170902be0c5de433d01cc9dce598d8a4066f349a5590589af3ebdd464e4060d578cd91f00f909cc0c051cbbddf614707347a6441fbd10ff41024dde8cd9d29aec79a557f0b70328af70eff37dd94ee335702d0e937692a3e7cdcbb4cc443d50005715a550cd1da1009ad382638a00b1377b6456258a1f4009f5822ae4e444c19e16b39200282a2f2849775b683d6047d8058017bcf01c965734b0923d133d854add4eda608a5680b26313a9121ce9c4a0f63f2ed4fa24d88b62f852c8c60c32cb558fc760d3651aba81228172ce26ef1bc675f7488b8bf9614913d990e8b75333f4fc2f90127334e2ee7627d38c71bf1eb2f8d85cc3969617251a4ac1068841d488071e00546ce29f57acacef5083b8f6b7776d84c1203a594a7ec306325c225d136ebc30a21edcc50a5e979e2cba07e47ebbab535d8b6400b7cad0fcd8b8864c0cc09de003caf830d56460c58364df481f4f615cfa8f2c3f6fc4692f54eae9f0f4804cb08fd0fc81eb9770fcdbdd63a012267c81d3c0e233b00253c15a9de26017ed7b405ea39dfefe855c31e85206995e3c8d1d2906b0823ee534765252308ac59587f0846cf99fbc70e6afd9e44ebfaf4cb7d9b49d6066712dec822007bde53558ffe05b5f5801d34fe8179464775beab9548a3a90ca75a26e122417ac7fdc4825e3a0cebc27d110751fa6bf508a14d6b9bab26e0c490c1f7f5d519b9c0e0c79bb8e7001098785db7707534d61f1ba6d7c3f65f7ef63b6503a698f29c2fb5169c78220516a8a9c22d78d73e5cf129bd6844959cf7fe29f9e438a6533048690e6de33c07b944040886962ce6620ee80a24148c3cb40b43fe8bd7e40a844e2dfdf8b79f0f16d04ca0b347b5831d31f54d11744bafebd6fc16f326911ae18bd56cc90f290066ba9a3199919d03e0a681665e90e12f74c489af21bd640859aa5583753e6207e02f0915a27009f221f4586309534c3d55e37ca0e419c97c2ac466bd86d26407aab81d37148b207cc47bd575a92bc13b56219c80fc8cfa6ffb445a5720397008084f181425340da1930b090097fbc959c940c9a25e59da026aa56977abf7220d4c3175e8ee86b378f36a0e06cab6a136fccb8ed4491caf612ec6584de363d303 false -check_ring_signature 90ba498dd19ff603db5ad4b4f85a798784dfd99379a3d09b7adf473768393ccd ce391acab93fbd372ad66f79d494770621133589653800ee45604b6c49c9a408 4 41d38c8c1545e9d1a8a190b81ca63a6b77da0571fe5d994f6aca9b66252a0929 582f794522191e900ed0d9a159b42796856df0036e1562fe59eb1a7bbfbbab2f 825a3e96809bca750028f6004b4eb34a0666824b3f85d60e6432e145c6421025 4234ed77b9f91e3ef2878985d8890e7634716b6edaa5a86b8caca334f84c2a52 a4ad00c2952ae2aec4653b7addcf4af0a07ca9cfbfb37dc4b4313aa7709bea010f4a6003316a7b1b13bd16ea22f0e5a9f7186c5375c3ddf5ac9dfdebe2a34c0f3434d73503df35bd340e02e35bc12121107f6f6a32844d04d5e6ec04eb8d9705b03aa5a63070f7ece5917620c77849b546cc39e3ae32725139610830af0518031b8b7f8641faed0bdfc7009d2596391dc76e8a2e7c0672b3fe025a976842c50a3e8bed2968e669a8b5bdc90429ecee3a4dd5f937ce23ce9b48ba350d1e89810d444e9100e1ce571695b14eb47617f39f4a86db2532d2861a805efc62871d520272d7a245aa4115bf3168bbd82675eecd03b826ddfd0f614c54141d0db2ee9e00 true -check_ring_signature 7e826fe3b79449c35370763b7153f5f08207872227b8d088cd429771a50a3231 0989f9404f16febf3b7b199cd7e3b22fbaa080d210faee7227a48d968e21027a 2 9e1237bbfbcf9d1af1e37955296cadb23475105da5f3d58dcff32e6ae4d9b309 5129053d4c5d527cd1ccb8de536075357130b51d85caf77f2546a320c7320a47 9bf9a5cc1ea6189c8ef6e4a8131523252f3e6d7539efcdbd035e40dc1b810900ee024b9f83ff058c4d75aeb7080c87bb29550c13635812148ee89303436f5ff5b9031ff7a27cba351a88de73fbfa2676fd061115d3ac634184a19dfef9b73f0c9d4375bb10c1c9b69e69b22b69ba636fd2e4b8406a85237f4ba131c5e92de101 false -check_ring_signature 3356877db56c5b84c0c5f4552ea619dad8dfab91a364e58292ccbc7d06e6d2b0 65a3ce75b05da585a9ecb37fd44992c87d71399d8a434345e2641596a74cc7ab 1 bdce9f89a2b60736004b357239e9ffa88e50bae92cb8fc09f2ed00b7e1917f98 80e2520476d27b821a887ffde08b3b56b900d39c6781f9e8a03961f2f881df00e558e8aa9d76cade85d461f7b47ac1857bc6747c40e78fe5b9ca8e4d5e5c1d00 true -check_ring_signature 2df026c81a0f0777acd7521fb9669707aaa869d292b92ff309acbd9b4a6132a2 f6476b5cfad25dfdcd1f82863ceee082770f2d33e7f89ee8c70d60c924774474 4 6a36c548c6173b4d8206a07490b1f86ca4b9882adfb77cb36dbb4c729e4c223b 6e58b02f4ceb8eaddd968c49d2d14e8575c05cf1467b0c42b92780450d60880b 65dc39c71c581e1bed1bc6f15f66972a986a4d597a5da6cfe8027a9ab5868470 4c4ff494529ccb5872064b08576ddcf42d00434e164ce3b9bc4e9a2530c6f219 acdf4f6b1bf08b66568fe092826b4623e66cfce60569576002c21054c2cb20042d562777db653131096007358d4aae774207e16e352e93efec216c1b8c238506a7a27ee9813f0433dd344e75a18306bad2de9ccf36eb3247342638c219c18f0a326907a435e97c70ec3590988b87e87ca239c5f2e4b6234180f2332dc254740666769b24d7ff73af78ad43e0f619780c1da6d1f398434abc7c0c69b6616bf40c3b2782a17d040f8a79d5a903705968b23829077b9130aa244548a5ccbcc63d0c4f76942c888ed1f6528e1ad45e7ed25f05aceb444686e1b58363c0fcac3b8c023254a242fe849bbeaeb87788e45cb06909d2ae44f57229f4784c0f13c5444605 false -check_ring_signature 07df28cc034994a96e9966aa9f56d1aca465e4681eef6d18207d3be1ebdd19e5 bc5f1f1164bed5474b3b90f08f158ff7d893883ca8a4bdd3ac151168aa8ab731 201 7e3938d6cff042b85a738aa496b1de03990f334d01b337fe4dad4bff9df36eae d282c3bc88c3afc1ade2df267e4c9114c85ffb62c2443074f014255075e3d374 fc41362d178e88f40b57840ba665030bed2165ed3c5e529954fc38306160e6cc 63580aa1b883985b722e684b75e8d05e43160da738bfbab86c1243ad1ac938e3 23ac141fbdc6c04c9f5ec062f2fd788be70c185c7b768809e37aab1797f3322b db6b122e99f0917372d75b37c6fa8a107a480a13062db9e0dd53bead5dc89fad 42a5d693c9c44816ced5c201d2f98193bfafe28a2b8cb82e94c7806efb973337 85a0ca7f47e76035ad8b16d462d2604e6ced16a266bd128107d9249be5117da0 190b1781b728829952104e0fe5568e09b82ed832b084e5aa8798b810ba098944 95f37a86cba7061d31b97e86d7a094584837eff2d4a35975abacf9d3c99312b6 0d14f9161b0e8cfa173c71565102de23701107fe6fde4e8c7fb241472b4320b7 fd9604dd813e73993956e946c2bf0b94eee6b96440449e981e72d8935fcfdc0b 6322e01b407d3d7d43db42eb0ee95d88efd9c32a4016621f31af729fa3844b49 71cc9743f17c52411f42bf59e0e9a31176d29481063f588687c12b3512e8723e ca37013afc605e6e54b4fc22b21ac5f49025c2849babdc07c6e231db95935fd4 d0020e5764811b53cbc325984449f3f9536ce004e91768327e69816537e1320e 20b6bcf4a06b4358e97695b77dfcd976eff409dcbdc2210fbd7bfa4438322746 b28692d7bd5d477ca6310fcdce1f375b41fe7650272b5bae162a0b2956b42d7b 5b9fa82fd5a508f58d7f3b17d4ed1edcebbcbd1b1060410ccadd891dfe5122ca ac6a4de5b8306650a4ce444d49cc22338e1876d89ac2f8543aaac8ce2f9892fd ce091899962ea1b39c339f709954e47811e0b93d9b1ec76e4ae0774740e6960f b34ea3cda01974cad60e2875f9d822255f4cd08366e162ddcd3f233401ef7df2 9c9326c4c65726114ddefd0b5d284e41be71aa80fbfe5aa640a09e31fc4f37b9 099299f692cb6328b44c2aa370dbe90167626ee6028217e352671c276a9a87d7 a5ad76f52a5986e68630fd0dd2a9c0925dea8103978362b5769de5604b26d480 c9823921f5cb27450268a9a3da957c340637a542f67eef14a0c9aa3e99f84729 8eb4f9cda91dd8438f25fc4004f32011f87b8d0d863d89ca1f5b0aa491633074 577b7e7c1d14da8ee694bcc1e4e1c6f3f8967b96ef10302e756233d1b55341e6 c9ef8a813585f8aead054d50bd9e41e1f6724b45af35f1de8aa0418a980b00ad 43d260e9a8d8c940aebe5b3c06ee391cf19e8606d429a4a37664e6189c19dff7 c3608e78703375c00cbe321a6f3ec4cc7796c8d9b45e2292c9dc647ea64a7e01 ba2065c1559d3cea9ef535a10361231af52023fd2cb425a2767d5f4710798b89 5383b761778502818d70ec80aeb42f0e299cc125042c97a7a724cd98311f8705 01063c33fe455e823ac79d9125c72af4ba1ae6cdc3ca672b6dd986140698bd5a b48a30fb7beed3b2871ef1a4d53c2eff8c0b36aee0b9bfa5468ec8823343c9cb fec08f2351265884a87de667c7fa0c80ff2b5a43eb32b102e4f7a4cd81bf4a6f 094524e299626efd1ecd74a13346a462d3fe6c0012becd76795f26e5a0ea4a43 a8c5aec6bc43600e9898bfb5effd1f6f5716332a2d63396c1f9f8e9da3f480dc 8dd16b80d8958654d86f1304df23cf362eef89519aa9e66f723427c4d115fd52 90a360c8e65f1b2274b08b2076a8ae4bbd129bca43e0b783faab6950ff19ac46 886c59a4de5e0c0275aaa4d0e4bc7aa31a27faeb61ad13781bf91d6830a95c07 a6b0991436b1382e8516c61fbfad4f421283de677da8656bf56a107953835d44 b2fa67f1d2f241bf29351042b471b6eb580e9f94bd1c9f451595d5fb6be110de 20d1a2acb1d80ecb93d36e97ff415183bee8088a64bc4a142ed15b7cdd108c85 d0626db96666567dd5ee22c8b64f0c10237f0e9a94c8954a5c7f4f4b1b81ca19 1a3aa4c876253eb456f737cf6c61954151f75c8f864b2b9ce1ad584682000863 6bfbb80d387a365a414a9c4315eb413818785b8ea351e5e1ad5345460790752d 2888e92a949cc94ad79d6a2395f9e688e8c8ea9613baae5ec51c6ce16c3896cf bba36f647cde15febce73617fc134c387a015fa837c4bcd837a75ceb82ae2d88 0db57a44fa562ec435b3821f9b5f18135e4fb88a32610b1991821133d9e0e435 eb14b914152d2b87b11603a78d0b6e7dd305a2d0c985a69c37b289859723107b 6680eeaf77901b99ece5e6e1e48de54b2cfb9b868216d5365db88539f5585943 a96259763d0cdbc8807dcc40081c2f7c6b2993abb912340e24dfc169e58d0b78 fc78795927c348fa001fee2b5a08adae3e86abee2aef4e94680aec9271b7d0be 54f19d521a339ef988c1a8c44e13334fec55a8abc832d37611c3954771e3d4c8 8e15ee0a00f1967364a9fcfb9e69e15d90c69c6d8e6e192c25d578d51c1e7082 03e8191f6e51304d5552a7915bbb60cfd8f5bf57c98309e6804185ecbecfbaaa 416ef2aa552bb2e22a4f5bb4586b497efa5d2c1cf613726acf1731783471941d 155f2bf399a655b5963485fea5c3f7f44fded5db3fb233c06ed87f84c33e8374 d46c7e2ecc624e86e56668ac43ab9fa4cbc0be413a39e4bf5d328bd421a285bd 0a74ae0ee30f33a42c1cc7befc86bd0dc4b133e8dfded6a60bf2eeb2f4a81ccd 2ec63ebf97a75a285873f78edc85344bef402ac042c3a21a14dc8753ae13247d 5c33fecc576b3372362fabcf3030af474272a1a4490bc0dfd932d13dbddf555d e4f7dc0e487ec715f87e85ebaa05b2936810b72b4ad3dc8a387a3df1415f5859 50ad069bb26c85e1d9b3a7f1f0adcdcae11cffa1894e61cd926f811e45d98b0e 8edb4f16c4adf0b20833a7a4cde95ef72684607b3fd53777b4b430573b34c885 d209dd54c3c961d0adb0bbabc0cd051aa54cae460a1754b91f4234f4042d2d35 b3da90f80665df8e0db8cf02b1d609739622688f62f991b60e162b0f57626e6e e6d2556eed69bbe4481ede57cbdaa769069bd5d0b0b8c5418805dd086988801e 309b112b60ea20f9c2e1831050e6a5ec400f44c62942177998e9d18480aa8179 a5476bc4055a48e296c72f6094931efc0d82c88e01b7368d4331b9574acf197a f0d471b6180e7fa971cff49b2b25316dfc829d856701d53f9bb7e28e1f166f3f b7279ffa83819e211a93b740bd7e69679af9c145e004d092462981b5fc3b1525 ed26c158ed04938c0a9ba0fbabe3e707fc5cc6e2f8fb859345a351f527cc649b b30c880bcb0119687febc5be7f6d4394f7ed5b2518415c1e0ff88af18eb5d09f fcab1aa36436ee38549134cba25da7236b2caeadc0f0f227f09ba6e06ea5b3dd ae6df4b18bd75d4e09dab959649c03e7e1475ed1e10c4d70f652d479b3c06d88 dfce48a680e3d1d839bb5ba1b04da8477a7db9b627082b62d09e49b2969de12f 470cce5013b51dcb6466a462a7dda77f68fad48676e3949935a8ab57651d344c 58c8940085a949ee25145579f92e30feb74bf509bb7fad55a0b5c8ecc9fb96e9 2f345bd442f4c377e6ad0b928157b765ddb0492c1b9eff2a152f611d1a52eeab aa0fb3d62274c2e92394fcd31d278f4b562f1f0683805e0d7c0b26bc90b72723 863a17efc1d9a9f8966fd760ec18f6c7a525626cc8adde18527627ceddf351e9 63341ff088e9d19fb4c968c25485dd93f5add5ca784bce9d6687566facafbe87 6a6b828998609949616135213e0d14d49b2647458f7ea2e49d9cc718a41bb5d9 0b451caa8b16458fa8b41b586e3b59f5fe9ceaec415de265b9eea2cb478e6f5f 37a2a5cf66a2d9108a713428cb636c4cda5a0d676aada6676be12191df39f7f3 d822dd93a2d5e18f3ea57fe188ee3eb60da731cb8f723b84617617bc70b1e522 3292ecca6cb8fbe4f3f1d8fe5aa2c26b517f35ec171bea4b60b9f5a948be1802 cfc2a18acc47d5854c8dc3c7991f1fb69aa66e64f8b36c8f08d6612eeef6372c 5004ef81cde36ddfccf78029bac8ddf2a89d500e1fcbb301a6bd6542c5362a91 cb3f8006dc2dc06856698953ae1179811e1884cc355a3c5eea8e66275d8d51ee 6dfeef38905363a9fadb5d6d91d9639efbfaa97a5a281a9769c88e2767b3d861 0924ae6cac131109d3abfe1fba02f777937cd38f93c748a560cb7f8580b8cc7a cd353244da2feaf0065bcd3c72350535249676ab57d7336b4ad846e628b14e04 ebfcec89c5ebadc078e5417f8d70809fdc6b4f703aa85165d0e8d038aed1c711 1ba94c943f12459f200dca5cedc486a4b3e4d9e3ddae2b0f60f200c3f109045e 99816f327b245c272788e1cfbd16dc89200d4da530549ba1a34a4e0ac939aa46 4b73701566cd21f1883f0eb39dc998fcbb42960103acbd87a0b2e9fea000d624 c90f319df2603031bf8cd8538104935c963b4932c9fcd603775657a9d596a702 e9d7fca3028dc7afb60ddb4b5cf8eefa579881c00f4465cf8e948841b5682776 47600b9febb24c0ae8cbbb96c5b8a82ffcf6e3dd0af14c5269c1e6cf87f6c319 f862861f10949a87f660aaf02b2e445dbbf93538b4f38c914dbaaf35e3027292 672186ae2551bdfda7a636d449393e5b81a03763a7dcd907981c37d09b9bb630 4c6f9fbb4eb8fce2c1577dd925ac197215a9a05aba0c6f99abac5e582ef1ac6d 54a98aeebd0d5e71ed8a9745401f7764c76eaa19274f7f5b60a342b64f2822d2 f0e2e3e775a45241a8403ed120d7d8d47feb52f3b454da10990875629889e618 9f3a90276327b83e92a8c78822c36254c9db63f0c1c79c4955c6ec95e069ded7 473d7d402e8c5ba48e31fa115360bee41d9a5415b2235343fa08ecad3d30cbe2 4e7c6b74606ff51cb0f0c249df2a4a92fc1c8834caca9448fbada83518361bd7 4857a74c2d6899b1c76d800542d40407fa93e0e9953c953fcfeaa97090acdead aa64e6fe930271626096b24f53178fb1d3f12608ca2d58b802d6308e09bd7cca a73d2b688e096a76dbdde9f53868c09a5f73d42b1f965f6242e73e4e96bb1dee 4566961070041173886245842f9baa9b816a6ded629b86a9b3f031f4f8200d93 36a36abc2aa7a3931cc9e8792c66abf2acb2fd21efdaeeeb7a9ddb75dfccf3b0 2ed21a329fbe2f66d4760bc655049f30f7533dae000755382a16818aaf0e3d38 cda808820dc7d58d0d72eb49439c78c31769a3b2660fd18293bd6a58d9d0edcb 5a274887ebf8868a36e00a22e9630923096cd67205d73723872e463bd0cc6d21 786ec88502fae0ba99e4df7889c31daf2e368d574a8a50e64a38174f46eabf12 a7d64e85c46c230d802c29e43808268e529b2b531d1eebca2f25e2e306533cbb a50ef6bd325852069bc43bb3f7d13b4ce8493926ee9a0004597240dde4a7f625 cca361364c0b9bbf4bda4398f87dd0f72b60d8f747e5b5419c75f56e79bbba86 43edc301e91855cea8ba0d1a2347b6ce30fd8b36d1f961ce25a6bf1d2504cb0a 73a1268dc219a6b778e84601678b79569dee6367c536b16c0997372c4ef4bb91 3911bfaa9fac6a66438302fe1e5017261692ca76113eac02c5ebf726ec38dbfe 8b8c47982b5f313262ab063bbea6f2f399f2015225109cf9fb9ca8835412d7b5 ace8f2db0d900bacebaab28c469d9f107ad84905687f5d9902f7f0ae1f78e04a d963e3e909d1a57192538a93314b3f696432e50c504359f77b85877abe711ab1 6cf25a70312c450c280b9c9943649bc7b010f3f5a4a6b6345144659b33e9c1d7 12aaf21015d48c098ad66fe23896f6aefa91d51467eb48633d4cf2319091bb4c bc0f2f372a8222b1be5aa8a02c043b18b05fe19389e4a3018aa650a5cbcc774b f93bbb56bc7c233a704bc8f5c061fa0ebe0924e3f222251399285d0cf0f9c864 c87e635d97793d3df9d5ab44f150b5f1f1da0955426978dafab71b774ed82449 f66c77c31ce742f543203fdeb924291488d72490b2a36a29299450d2650b9222 3fe0751bdee404f1d8da1a9fa4c7e403548b7f9cec54c00df5d88cd7b419d32e 9be781c68570072d6bb731806245d7f24f437f1887413b551400b0ca9bc45a40 b6f80074636088b03077d7152dec88a5991ed59329531f8fa279cb4b1ca10fec dfa1cafaf6dad3e0e837bec93a21243e662f48bb35d0355fc95a5b5ae1669899 3b405bd3f73d00ccd6209368eb1d202c111dfcd49eddc2076408d763ce0fedc6 57f227e5bcff1c9c0e070f5ec368bb227debc8b401a27aa880fa11193763bf62 bd721d54286a863fad56af3a3c264da3dfa5ef4c2b222800486df9a0324bbccc 0014760eec2f8f4dbe00859596fd584fb3cdc7b1bbc4ce0af9411a3824431256 9d9389890b439ea2a3faa65c6bb00f9c585e90a3fd3d50459fbe1936badc3b1b 06bf76202eaeb055c733c94298ca9f0025518c6cd9d43ecd6b942724afdbd521 fa5ef3f7ca8f09e8301c63a258c3748e100a3e94cdefec174b4357138e170117 448d4e9ff0f31cfbe621225ec82cc44e7536714fe8449513dda2ceaa1705cd4e b9ae52cc3783690f41767e070d4c2eee1f724a6ef2e9887634b25e90aaee3279 f5fcd45a5f5c6c1b076dea9a1a6b2976f5c592990cb467dad463110d786043d0 8d92700986a88afbfc6091d076e6ee610a79eef4ee4d941cf2cce64be7aee41f 7e044ef22f188de855565c7eff654e45c16123df4e85d3cf4ab9f7b19998c2cc 322d718d8f04618989c220d587a2257edc7c2a3b1fc0873118c6037fe35a4c52 a784bef5193ee6106752069b5ffed38e88efd56c3ba1022246ac912d06e6e170 861bb8499dd57af6a549b6599f38894979206b7b8c62800d16acc5b59d716790 9a9e3e521ba65cf8740aa1f482b6ed248a12029e2a1d9b5b23a2ef9d682de193 0967a2cb0937d133966483c49f721c37ffd97837c8b17eb48996a7376bd9794d 00eab064a5a7c7d9a01425a4338e97a42e9263dcec3ac3195488fe081680070b 01eca5022462ab571a3206123681a3a8fe44b953be2d7c88491d19498042d01a e2fa28933408066e3d8479a3c27a13b8766aa260b8fec66e377e002fdae9944f 5e60815bf3c5f8137b04a10066babadb70e7bc100c39af8a313fcf56b079b802 1a2a8c215e6a13e39d7ec16581baa31b40befb17b275014c0f704ba9c1e1624e 9b4ba98312bb1e47794731a60850afd81960b33f373e940efd6bc634ed1f9fac 43ff3ccf9f3de82d29397c969b46d6162cc709e93c525a0ce8d04cd7318b182a 66c0b0b796e6544daa1590c1158e3b3f0de560be8dbd2c1449bec73999c9a94d 358a52e4760e68f3485623de6bf23faf37de26ee49b2f6a2224721528d945a40 208d0812aa37bcb075b0775a7ebf68ae00a6cba454d003b7af0c4e2c81cdf775 c792dc97e57169038448fc8fa7697f7124219a9a25fd42115217f982aeb41088 88fd6edc3c519e844b9d53d9a9c585b48cc81aa37a9ad2716d50877bd2b4ca16 9c738fd97ccf2b6d0b3873ec65dd0fb52f0219114c3b1d7d9f76d55bc7432d8a 9899fad2e7fec95db52c8c124fff85268ed529b962d5c7dbcea4d9ec86f3547a d079486bde8a260fd46d2e7338c2653d4c0123c71301ce98626b8ba0665855e0 17d07eab73c03010c26a9e78377c6cf80683ac072cd5544516dcf12d50b7031a 17a4dc6dd6f5a9798b42a89e9ff65f1ed06b1722f4d4a77dd1f231d791e671c5 023cd08b0485386cf7b6111227b0e302a3e32e88e446f64a59970a2e93576217 f5e77c8ea6fcb46abc2bd22d08a15e57fa799fa94346b969e1f72f475c66ae8f c9cf90441cffbdc9963e1a141fc87cdd2eb65fc53eebec6d2ee44fce3eb3d059 d5198da041974b4df2347b5e92e09551017839a3c7ac02134d7bed6f05a13c73 0d53c20afb223cbe0029180fc7182ffe26aadb70a4dbcfd77b575d10f70ceee2 20ffe3a07f0680f8f4a9497aea22930fad8261bc85018475c03f070e5701ca17 cc7a022b637fc284781a503b2948b34e4e4de553dfcb16193ec6d3e910c10448 356b96f9ab11dea193ac334f7107ce5f7da0edcc9dcd7c6c0598243721414c1d ab1a39431987b1fb80e1b142d3d27772f9533c02145f9134909557abb8c015b6 7e445dad530a509d2d2eef5d95be0da04b1cc024efe7629f94b399bc0ea93446 96197d76386acc58d14194798d79c3f30c1ab466937c2b8e8ae9968db2a714db 2e3b23efc24158bf43dd8d7e631c1360881f6447e07b40a0aef8ea8d23ef5475 d4b2ee72a713a6f10088f7f1b798121ad0572794121e685ae1ee0b95283da2fe 4b5d454324b9f11ce309d81da71ea2751e08ba3acf6f03dff2a76761e36c7255 b43e22583015f0b99725c9d5f030269b75057bbbc648de553d9998c28d98cdc5 31ab872d720ca18161a5eee94544d96ea5e907ef47f1bea19dd33456cb7117a2 51f485bdc04da03b84bf0c983e7947638c50b199a0c48d2a2393a06f80fb0b3f ce092628487a41116f804aaf617aecb3217310c990272e5e06183967f274d09b 0070d6fcb351a23f8c37d50b6c7ea5ce493fd7a616b968cdb157a2302a6db1d2 0d06a51a350e56bbe853ed77e6d93389429c70df30579e3ea83d28e930e1c0f2 5bf29f7b81ac40c8bd90f0d917e931fc8b6916a924f0c7c3f645094d2b7bcabb 10d66b7a39310d27043cd4e27b2d5e9ac92165f54313bad20735134c4d2c3ca0 8141e83d0658714827dfa4f4b13baf44630a4d54755b2901a15f8f07c2883fa7 909b06b97c4c77e4c49a838c7200484ace26887f3c5eb8052643ff38ce003e8c 9da3c87c1f9171d0b4e712a6023b6154cd4d403ea82fbfdabda457edd536e93d 4fad2da0b68fe0aa298e3194117a7422d0353658a9efefbe5753bc9609bc5843 ce06ea0bc3e8aff9f9e13b45ef40bbadba664629720aafe1a2d70e6501d77011 380ab37a9a6b8599557be2c2f5d86c1afcea791ec1c1d4542f34ad63d0bc36d8 26f4131bab52cfee9c4acf0b187ad8983dd55dbd0ff23ff2df617374a8368a0a 99eb53b9b5da3ea5f076a4d6c422af818d1425c4acf38a3294e31299bbdc3608b3d2acc5b4559629d7613b52f422d8fc588e898f8afd38e093c6d64850ef53072ccd1be3da2b380da20143c137843b643dbef4e85bd2b3ffcd16d7bb6923b60f020c92c1b0eb3668d3e22e06e3b0be2b4ed16472ba4a219ef4625f482d3e080757531eb2c319af8ee6a7388040dfa5bbabe361203dceafb20c36e0d22aabd10ed15970b54d12b6ccfeb2851286a1ff9b9e1b377af91054b3ad102d4853d54a0358640a199773ca2f62f052702af9ec78dc79bd4b5189a2dbc0cdd3ca33b51307ea0c92ded694926ad8957c7b15644335b35c71f6a6b496d763324398b8325e0ad65bc18cbaba45bfba2fdb08b4a0008e7d78ad843bfaca7243f4eb70065dba039f3db9fd334702b17f539cbf6c90a90eedb5b7dbbdc7e64b273ce16d432c4307abe0598098515ed42cc2584dddeb806262e95a577582bc668014ad10d031e803a25032a098900bb5da6af8f3d71965d11d08f8abf62e86ddc68aafd29ad1430d7934bdb46e87677ad612814250d219f4985bba383cfcab521ad275b33312390ff47a54a65731dff5d66051f65b4574b221984a546b97456b771ff396553e620d4d7c0db7da030b36e33cfc020f8981e1d3d1f26ea78a280bc191c660619b3408efe1cab84661afb6a03d535e47087c884ca5d569dd97adaa4ef7d8a14247fc003d8572167d4c8c7c59d106f0959f785827e5958452f2926a4a2012fee295150308a4d588afd4d58ec9fc6ca2f497c96b99ed23e50245130d39aff6cdce6ab6095d4c8ec6876e58c32e4f233bc5e044ba96d2d8875be4f45db218ebafb10c4006a90aba762c032521706704707dc19855e9dea968b8d3acac3ff09795239ecc0aafc6e4d3214b2acda5e2ad0a0b15cf8dc1f8f1d3187a479185934d34c1c6220f89eedbcef6ace128606bef9a2657c1b15c1cce40f5a28a598827b13709b4d9011dee5bb98deb4f98872de2089c7a02fb9ae7aba67a3c837c8a6385fcf8d02b0b69f5d115d4339690974a7071077ccb9d5e9641cc3ce49ac5f1acd4d1d88aec09960e5b980472801f43456705f644fbbf036882ee24044ce2544a7666c9bf330927d3eb315d49a5ff332954a39aad521c09406e9746b274428c13cff64dce6707ace0f52c9a1f5d441a3950070f4505189d360b6b8da88bbf18919154cae2190217a7b2d44459915427d242390af776e58d04a5214bcab72e50ef28bf591fea0aca77e3cddcf293201b1ab0ab0bcbb13719ac9d180a5c9191c10712010116fa01407de559a651dbc5e3ec4bde62b6edeab2695aeff0c50769e7cee41d44a97d084480ef84b0f97e1b7ab79ae29cd67a9f0f143ed02765cb9ca5ad1369cbf4e609bd0f1baa440e79686234fd74985c0de57a49fee02f8f96de74caeb596e3ff309b77bf39641dd42e1b07b0351f299943b945cfb4dfce858f4befa45c61204900c0792bd36b00f84b5daa7d6a899006acc6c3a466e6c3ea309a9ccec37753bc002e5f457acab395f002a7780f0e56a53d457c3c099f7d9d150451162a015a7260d666c821b36a79d6dd21e896259b863633e09c08da209eeca26a9217206209d0c6bf2242172ae20ac73ffd191f691a93a43e4c77e98d087b603b41cde4f523b0a4e1239fbc0ae9f887c1f0ef680643ef1337612ba418cc0c58eb12de8efdec801471adcce02f7697c8757fe13875d8e5e18730ec8a91071db99920149da259105bf91a5dbc6bf215bef6d5839c5316ff0666a12804fc31ab79774b8f26b3be203ef345b784ac35c2e35bb71f77b087bf2e13b9c098427988f6d9990fa44c88e014291810fbc4269548bb3dd88f43b1a354540fd9ab392db86d8e339d2a0b302066583e27fbd4d704662af7ad24f281084902dd1208c9ebaa0d32e5225a63558029622a76604b7c546ee28eafce0214184568ac0a6f88b39fd3ab415e626419f023f06f1872f2f151bc4244cfa3a049340adc6ddf14f541e45f98c7f1a75d9520fca5bd7b53bf62ccc294479557cbc9953cf56ad9f9a877987d76d17d976da1b0d368d5995863e8a19598fda83167021b1182d35647d4cd8ee720c59b05890fb04fee0513b12c965559727129c009a6a1db5b47a3ca09cb44e071293bd595cfa0180da478cf37eb38a2427d19a00bc3a2b78d7aa0a03c31fda9c1a7acbcdc2bb02a8e56de661a1c5b3d6c5f3d05eb5dad7d3a5539d45cdb3192887fbdbf8c7af048018398a9d4f0a70b9d861803888d12fddae7ed79a95233e077e7a51d02cdc07f216710e376d5c6fd27d29da760e291b2b7c1353e027e0f80ec38429319e560a97b089ea7af0529a9f7716836780291b0ed23fa75dc196c2a492027cb0f3970d44b3de04881f79abcb699f0a08a288257c00d680b8438a0e93aaec0cbcbac30e6a8eb639fc127ae9c2767b0c58a488a223d30037bd3b54b8a7f788b54d5346075217c19624baf2db86afbf396cd14547115efeb22c61b42d4c1694899e07e50144cacbf3866993077e20bb972a155da90939330649a9f400c3fdb024cff0b007bb44a0608de0d4044867604f1e7413a7b7ba428a025a453391647c9ed9dd750f672627b48821d26c86016f60674c0b04be508b7fe6c927b96ca0ec2cd6136c0663aa3b1d3cfcf025fae282f558d52977af5499194233a12ca902fcd69ee0320f08838477d7c0b7d8b8df30ef0e173f9285d9af666c62416e04d4d936ebfded08b03fcca3443e779cfa490eec61e99dc318ba91c6c54a8da402e86b810afacd01ee042d6481724b4dfa28116ec2a9a782051b40f6a7d17e91067c7e27f3a00c063f26b8831ef33e619039f9137250a171a028de86c6efaa90aed39a582c1de707b3e28834cff7386f38ae1cb32165f68933244ed0cc6f369e21e577d3a203c202d950fcd10363f6fc1818963c4db765347efd103ffa7fa345b581e1566890be075868dbbde0a89468981158246da8391d599a4789cd59f229fdd0d923f4ed680f026bc1e6575394cdfd9ad621b07af9bca4563a5a1e2b0c87f7bf550d112c2d02d343a33e317e48c3fcd964716c3bce53ac275d5096e8b7c60d71071cce29c005dc248a9016ec2684775df282ec89be740b5669cdc3e89a28ee7f3ce82012b10eec205e7af4d783bd43956924438c918a0025e7d3555d6156c246e40e23b80d083d146a92a2a542669a697af085dd6e90d4323d07ffdff8b5bdca35ba1b6fee0987a70d94e023264cc3ce6f2afa93ff6a494d7a265eb27a9c1f05d5e811538c058660135e007e4a3d76eb7518ed3fdb0592eb4145cc193485514d6511b9ee1c0b0e4a1e6ddf64bd6297dffd95f8ca86d8f4f2151fb685fca1a3aa782cdab8750e84acec3b29a1e7dea9ddb400d9fc137b61ebfd8433da589f03ad0a0a3fc174029bad104c1ba857d8d28834a29a6d9a4a79c888409263260828deeb9b4d188702c4caa13ca406a96c884f30d94cf458a576b1c2bbd6e79b65fc6b30b77443900afe5102e7b7c5482c4a407b6f4a325c1839ebedbc18a2c8054093d3eec1574809b2fb6f98cb4bda9c697373fcdad81e0fdf1b4d945d04bd1bb07cd3fbe446fa0d691a412d6073a2e32d341f85b4f5b4f3482278a53e1faf3d031d8c6e42b50a04effdf89e970d37d487ddf12b4ceeef75ee726eb11048124dede954965e8e6901ed0a39deb8d0f044665bc4b2fab7ded226825647886a943b70e5a90bbe83ec03521996e5e73e7da30a3f3cee544f54950b423500b698a8ba4d3ec6f40f2c91067b8ebddb60dc1fe0f5cf1addf9ed1eba5258263abc588e315a81c05c6b1ae008a07319b02d5f24c4223b116a717d744f75574dd9afc5b9355d553438d4d79909cf5c0b56a89f402a63cb7ac5de2ee8caed2290d3ebe361bf12598b243627570b779b95fff43d7281ccaaeb6358e7b0790346a7161292489368b83725c45cf608ed2bd3bf45101ea6fd21ac10fd163fb1b2c3c2248e103a017f0c02b25d365f048be0d4777a700b167c721205c4cff0316e95a334ca2265fe8850d16b23ac2c0642ea92a02618b7e253afb7f04750c366232a037a2ff4eb8b74ea97ecc4ead806afe768c38f05020455d49455802b3ab29db28dc657b6decdcf74b5d38004d20a932c65f33d13dd8cf00a2e1579fb0ad815244559318b47d5fe56ca34ae1244053835ca4985e5a00977d2b5434b0d17d9067ebde44a91dd27f3a7281fa018460c5d08475b7936d80892e23ba106512243da6787c8e9e97019c188c47e65153b005f8b161f6f4c4a2eb0d66e3bf12bb7711f0aa5ae3504c7132b3a4b0a85b08401cdeac4fb502bd2cadbadfbe3601f7fa295bb6e3a0b5e2aa89155fb939b399701df65a240e5faae3d587fa1e3c4c21bf0e1ee03b87f0eceaba6930cecf71f5c0e8466e653aa766f97387cae2a552d9481475934b7e15102e35e8b59d359b85900664f69691081e5b1b8061fd2a894f8326a1c6dde1af355ab360f45c25936c600f54a4ee660aa7123ef4e7f8f974ece5da9b06a9b625fb130a2e9906ec84fe905a7fd308b38b7f7091d53dd24077630ecf4508c3fe47d4ec034e1b467e09b82092493c6f44127313fcfd04ef88c6cad88b53b9a7a07c237d3b8f426360b95a40c96f4a7fdeb04d18af8226e77ce162a3e3f7dc8d2e4401316ddfcb51ccf647b0ceb6c6093e4dff2ba0384c23f74362f6b39d96524785c20f90c4ee067dd8dd005cabe22150415c60da1d2c2c031097d42cf99ee27b180decda12747090dad0a06f0206008f3c149ec84b308b6da7c099b10abf887371a284df7b8e075216b5505d244510b21d569fbcad84108518458d4ee62442cefdd4af25b96a90626f05a0e5892f172a0b83eb4eb1bf6e526d61cf6ee6889def096dda9435149f1b8c4ea01091f25d497f2c745d4238bd923a7c54ab535d116339bfea757485483f43ac5092d12b2205726a09708b6aa24f192dbf509060353bf8768ce2ee3540aefee220895fa6501034a0d77c8f841e5ecb43f5d5ab8579af7a8259e50522b680a9ec3087ad034516e05e8e78091d619f84b1400917f6cd8889b32de43ea626344aeb90fa92a53c9567c99a663b4ce82bb5ff6a8b76a8ff69b197e8d81372364db5e7c0bb2db9a3d18a41a547978c20537eec986f37101a2a1f37b3ca025164765851d0188cc539661d26f1f26053a3593f6d80ad8b3acb4223e3eac0a3074238b314c023cebe8634ae961c50f8288d36150e41f00881bda97fa01da3b877edb780952057e483e623382533c0586566f1c7f12cc77c6b9607a28275e1fdddaaac82137070f02f7b4bfa1dcc1163c0f472087f489e009424b27d42323ff2d7df77641c40a9d9897216b25bc68c5632ca16556e8e2cbe77ae42811c294eefa5b7d83434502bc9b316a525a16b3ede79806cfbed865f1dc4ad6fc327c063d7e8f29921836055c09306441c733b4b6ae88354972bb69a781e659ff218654e500e75e31aab9079bf458d5c8ddef0e599c1043c4dc131cc4037288bcb4661b1b34468353513c04371ac3bc9f9c8e06d80268b438c01be6344e64baf76dd8bfce8929ef40dbdc0f10cd1d79a8d43c175978d72e7590af8bfcd0d7dedd25482f892c326af3b97403e00c61c60660804b966ed2dd12a632063a23ff77bfcbbf05adaa6acb7664f8069dc82abf69a8337ca7636168547593eada30559aa33d1532c4bd88cdd2f24d0f98999ae3ea141a95601d21f0a4b15db205c159b85142cd819f9683d3eebe7406aaae7051f368a050e1fa2d923c66cd6e9ce0c7665f0e8fe6e7bdc7232c7a34032b61ce6d2cda428e07367947c2a034e48b950e8d42717ea2b700eab3c8b2aa0b19d167cbc7163c73e56deb1811597681fea158a78027729c6ca57054c221090c707cf5a01aef32144309de240c53b042d56f159d22910693aa792205a9879e01a57600b9e983e5f0a4f238ecf60092d9a9de5867b93e9aa756966cb70464a00f487adc8ea916362ce95b538c61090aae17258c8829676cd80e5c4b2c6b6a4604a969b0c08d08f7dca67d23d70cc1913647ccc1a4efd617c73f61e8d7f3ece004221d77b9d42583e0910f9015c1475157b11fb735bccc53018ae858115f6dcc082f9928b5d7608a52d1b81020cd3af2a77773ea9e428308c98739ec74cdb322039f50d809d3371bf3229965e075e051e5864291f3648e55304340ce0725506307f64095fc476c5a80537b294a7158890e49b4838128d50a2618618e18affd040565a1067e970034ae954faa3ab5a36c3f870cac59ea0929ffd6f347b9b5e5d100469255b67afc22d0104745aee23e3a6e83fecb1dfc036888aff3d77f8bb5280d2592f866397481d4e12591d8102a80826167de28a48c6e0a33c31c376e507f0f622af5c23d2409ced49edb72c87690f83d6673aa0369764307b0966975287c057ee7429718f80587a25ec7aa8e0e5fc1801ab9dadeaffd5f7c799f712e572d0ee9db6c7cae44aaaec39717691b5802209eacc2e12524e37689a2cabac4c2840acb2e7c8f59e48fc2a52975883fad50f1e7d27ef2d8b7d729fe670607bf073c08e51bedca4f56805b7d754ef2729a55d8d5a0e24bcb4a1e2cbef28ec7d840060d67777ab896909fa7bb59c5329701e3c106e988ce20317f2c8a690228e5b58a0688d726b80ab52df8c07b027ed9fb7012c7a01becf83e6419d25e1203399f39017f4a51f3cfb8a2c7bca3abf8eb5db37bd5e03b18a4d219105e65f1213c97110e7e14315b595b57caa2a1e8b076f02db89198cb6a703e15ac126b95de4c793e088ae2399745124e35b9a0ce60079e8a30e8f028f84d7aef5e165747bef72fca09390e5523767ff54c465742289c2f57b796141f41f355310e5b38fd1ae729f301a8a54339dcd426cd428279ae1e2546ae775c1a0c68b80db6fff744389161ec04a163e86247cafd563a8e3c162962af3e54dddd5a1fa04d8f668ea5d24c5cd80f3fddb0aaa7680837e08e8ad4a3153e546a86778fc625cc57fc37a0c38aa72f037027755b84a721d5298fd173571cd62d6fd342c48c965bed12bbfe6eb2d4f4034302f215e5b00a144b5d388d124d92e4951e1d7a64c33ca331ada4a6e271c70eb3e8a7cdfda5fd0ee6fb31f7150d68516fea746f8694fdfcebbddd19a3077d03d119b74e5c092c87e880e559314099d09b4e713bb27e7d4f1b39d357dd2813096cee660a63ee3725e5a771cd1bbebdd51e8a6062b308c8cded62323f17c36f043e50390a0695744ea4ba64196c085372e2ceac859bb0747a49d3bafe40943309d26b2985f4d71f3317f6a23bfb37026c20b9e26cdbb3be00db208135c2152a0b328f90a6341defde3c3e84f89e3e6ef61ae22febd41a287cd37daace5ac2700a1d4e224a2da957041b869f1f5ccdb913d6f567c5305d316b8659393ed1ea80065ab34e2f4cbf30d8f75888f9affa3201a075ad86b1092e646acfdfeb4b93da09e86a88712bf39d9dcc44956cce17d2cfd5ec147b8483cf102f1da97cc061420008e338c3b43f5a1afda9411fb47c420bb16ec7b85ef848e5b8afab80a783cd04fc2a329beef20b2e18ea3bf02c6cf8ab4cb053105c3235a4710321c0fdf790083054943fcb55c870602dafca8e1e47bcc29a5c19a4a36b7adaaa7b0a987e740e5f40df6bef900a49f1165f2b04f844a66328487a5337a8c63c78993bc9a47208d16ffacec13b2d71856fcd2bfad0c0e9b4df2bf933d4bf60f895f4491773bf037eade6e83a489850da744ea45f9631cdf038cf190f8f11d777eb24f8fb031b0c13d1da01e116fd82ea7af7154b81c3dd5c895547f4f7b5eec30d97c7c7e65e0041f8c1da7528313a075c09ae83db9c07c26e65905ef17c0ea3d9372285c76401df268152813245e64f5088049d5316f7227d6c388d197c93e4b86c3c303eef0245689a7fc950bd860612d4778ad7a49974c9bea8d4058e0cbc5cdc963a9e780494c6b62c51260c9665eab3e43b39b3b85ce924fc45c45f9266d8214ca26b8c0a04fbe82e90158a6f7464bed27ee8f175aeeb5504353b1fc13bd08cc365773d057b029542825e4fed1dc13e122f6f682e37ff9ced4fc5e3a5497db34a981f7c029101212ecef32cd7c06ceca205bd2c4771dcddba5dbb77bc0b9dfd226c034e08628deb4d35a8d0a83c8981e973b451d3ca827eb639d36605285d9d0791d48202c00cb1e793ca329397c9f22db59f7765d0526c2385b534f60dac7e68cbf99a03c0eb123747d4222be6d5e776d40393c0e6b8d7106676ab95395b9d0cb7cfe50f73501c54b59a938265ebbdfe5fdf0f27c122cb2c033dc6ac16cc9c2bbce4ae0e248c6e4c8b459a93f99c3eb224736edabd9577cab42647d83224fe5b741ba2088f46970a4d449f96fe82f762aed41773742da1ec9488f53816f90f2dfdade107fbcf37263f4c3ab3721bf6bb4d86f8c61041c519440f494c75df83ee3e8bb905d696ab399937d9d7fb89d626003b890d797d14826db0e7870c0b398fadde530ec2051dc8cdae2a5a8ea3a9143641ea0bef97a633ba69153e0379604b71d6c70ec1293da8e72c9ddfbd1397153b5694d451c5951a052ad81e36ba2d1c3c603d0ac64a7d39245f97d688756207713e5b9f69c9c09e91b861713fcfe79724aaa80eb07e45a12095cb57d6701d4e94e1a1f40b323fb8f60b284a6163f63f0adc2506567750670cdc87aaf356d347e5f0cca6eee7328021fe712d40e8296ec24c2d01ec1521bf0a732d63c31959dce1ccf3da5d7b2891b55978f0d5cd5c84bc1b1f05ea3affad9e72b6d26bcb172705ce6b657658b9c7a88bf5dfbff548a0f0e266061a7e03dc58218ec2fc3f119c7bcf20cfcc15093087f70267c79fcf976b696e0704cf29f73ed8a44a8b70ea3d636743f43277ec28e472bdc62bae55ab58de760fe8b9a76572bba72f9cc6052d3df97cf916c47755b84acc1c2e847140d1651b0d88b37dee6a1830f6d7fe8dbeed5046aa3b8fab0a7d41ae7d5c501ef7ad60410494680a7ebbaaf482ff27a58f7923a26dae2220e44c964edf07426ea19ba9d10a5a820743325e9772e75dd95d8d39d5a3d17d97c4fdcec6caac78b30c6112c2059efd623709ac00d8b50fadd7413fc953b4eaf8e231ca42e30853e09d4c5adf0fc2d9d969a645f709d8076e2af6969bd5eae1b8e1f12dcb3badd0e8dd1be7dd0581a86d6773c702cddcc8b3f6841c71c7e15775ec460b486f9a47e80ff01c4a019e83f8ead27341be03b7d5322a09e126e4307bffb918685aea5ed2e7603e2f04789305fbddda534c4a56bf544a0c2c81a7fa77e8b2f369773581f08abf51bd059b80a26bd5805d6ca5e4484bb9b78ce09114b6bba9d994ee4498ce33dc15c40ef8b36cc7212dd11e27eaf9a2f1e97b12176db497cd456e7934973216bac7f00975d61d790d5f1c6acf8896ed54daaa13bf05dabf087310c100311f7daea40a05274e32d3a5348a554480c97a278513d54431aaad0bf25f3bd6a625f13a75fb09fbfa839f356754b94786456cf8870973ba4f3ea167269a29e9946a534ed3bd0bf206a82c7971a1f7dd675cd2756be042716e3e0b52ae97e68cc868ade98ac4011d942643027d1776f22e1cecd625e95277f9a7855f4bf894952736374df6f606e89a2a71bd50c907e2e5474f5be6aef9804432f440749724e151afb9b1b5910e1a8cf1e50e78879e005abd6eb5c20b8c67929f1c606fb0afcf9b9e5710019d0fb7cc4132fca9f8d3dc7ffde235bdd4dff5372bdb805bca6b94e0779fc129ba0be14e258bd68ffffedbd469d9989ebb09812330797824da35fed0376bf39e8a0442058aaf2ad5ba500f0c4b3785178ad3ed254dacb01f53f5b57e4eab2de7110fd63ea96b663cc8ce49306aa1f4eef72fb2fe37628284f08510e2b7b77aa3f2041ac8ac472d807ebcaeeaede415288cea2fb03213f62cf5d3b107aa45bba52f04e9dfc76b7a786edda31d31901f41c744af662217d65fc0b2bb418ff923f2f5019eaf84490bd07c99d9d492867d48bc3cfad10d398f873ec1b89de742db76a70646d3a3f8a84d2e2769c27b9f50b6064323ffcdcb370ba8100d4555d8cc8e3502c5fd231660e12c0c3a5ccf727b7d25d269f778d205da6b009a60792118ee090036f70ce505c7e4242aeadb35a2a8d7cc1afa124dbee230c3341f6d7ece8b5a0df810785de4f5a2397d6d744ee30e1aed9e75beacb4211e657026f532a0d0a801584fbbff5f67842dfb5cd56d118087503e12cc6aa75f448a8570d7d9f67ffb044deb94c18385d1238ce0692d4bdf76759ef3f3274af1602ee02a648b52b2d80b79f284a4b4b2360ae0381a5c7808099d45d4a03f7d607fdc86e827504a97780288cfc2e0e1cf0798092fda6809c191151cbba59f075fd4536eb8fe138fc7bc096f18604fe0e424d40481802340e12c164db131cca0f6465051696848a26a240f8ec2e32758aec00a321c25a04b5fe1ab0a29d7f004ca294f530133f8f92d0d0c10bc209b6e0de9c1902bf2062dd59234e07c65897ab8e938ed19a9d83fe6540cd43ce3832c82797da35308902e1c8e32d4130b0d99c95f2e09ee8c86069fc10014673ea3ca982970906b27d57eceae9adefbe5e7996c02cbcd4f213e1392a00abd7beb5fa098d4cea632af99c28a84d73e6160976fdc9a8320838930e1e0680c77b8e44ef663ccf352c68a615f2a2b12f895e5685b11d0e2ae2a9faae98a1c0f83aabb2b202cb54041084944f8799af51e85f2460d866695226cdf68f628ae0be942213922b8f84b259a5f5e61b47f553e0af377f9a470a4fc42d0c5166cb80b238641f43032939d6cfc12fc2ad59737429584dd9574b9ce316516c3094b060d354751602d496603a35c7c6a2ea6e13c4b7fb341c25e5975179ef9eeecd1820757cca993bbf9258d9786f4cee78fdd723a898f2f95e1c5974fe803ee995d73069fa77eee1f52741fcc62ad860fcfdbeadf089d25d6558df2986728360373f00ad3dea08584c3fad09e3e52dd9d2b3963a2a896c3c662f7af3fe4b61557598c0a052c9cabfa99a56c11ee7216324030a44990e0bfac67d40cac9d1a9469bafd0edbdbd511881e8af1f5d5e3a6d4afd7fe010656ca16de83c78507d637c10a1a066489c3d89dd4408a1d94688f73d962ecf56097d6f65e5e85d643f9edb15046031d6e64821b0f825a88e0a9844b2cd11cb060cf10d0ac7f580e282ec530a4750986a2fb93013f7100bd58eb218ea497113efef95f6b36466d6e7802eb93fcf90d1c11517ad225c380692d6498a1d662ecde35da199488d11b3e17d27be984cf0c986947e2008523cc3cebbcfdaa5100d679bf7b7f65163a5abfce12c708e7df0cd8ee7a683178ae11e42d8db838f58e0bfc12007cb1ab3da2fce3788ee0e50f0a1d5c203286c6bfb8af9093374a69b827eea3e8b3701368f0b1fd9692453a160406f4bc0bc129746e1bdc409c2127129630c374c51183a5afc3060df886003408f38d8b19e5a4832f888c3b1c782f16831370a2526de74506f47b4be10b697505f2778af5e2380d234543d20d28621599891d8df23f2b5ecf9603dab64ffb4f0a1c0681166238687dc8c416160da4280a63276f37ca726e06ad6a74b137853900b68b5a4d2469b31567a9bcacf916d681736657ab2711019acffd221b5811c409dbeb6ad465a31df915ea403a28be7d4d64cc68cf2be4d7b0b8e731ad5e65bf0d9036e58cb778990e07df74b429c069918f63ae4300bbf084bb10116eb06bcc09e7ecdfc6129f3cd420b862884f6024667f1e93b9fe0de49742778b612cfd3a06c07d0d45659799586dd57463785df7bcdf96afe38f9e804f55ff0673b9d8ed04566bae814056cbf6856f5a0628e551b7983de204186f1adca052e71713e95403adb52168f1a6b708a5718c76f8b51ab30fd2f33fcd6387859b48c3a66a2b670ea16948e76a7a2c19e2e3d53d2346753282f515e42ed7759487fbadd058e8ca06d5d3ff5dc56f8e5d3bd3bbe87359dd07bcafaf9b9828889967d57df49d3f5b0505137800a4d7e0d374dd607420700f6c8f02e02976a2e10005aaab4710d0da0bdfdd2e93af5ba4d2896e33c1ba162f5688c256f0338b3182d4af2a019cd7f00543756a4c0ddd9508f24cc191b059fa3deb31d7325a4822e4a606d35d5df97d05f924dcb7f145895e2627736aa89328b371703f182e03fb493070ce42e7be3a073ea14469c0aff989c3cb3bbc241dfb31bc68dff66aeb4fc76092bdd30b954e08d9a7d463a76b7e52971a2a1f0fca8f91669891589fcba16b9dbd323afc7ddd04edc90b7420032d929187761ebdf1690761ec10258f03065d525b15d34644ff0c67bd51634c82f4009945849321e90bd8bb635a5ceac59f905648ba0b435a64018e4d214b6f8dc31444cb53aa71c97dccbc291c8438fdd9f5bacea47c1f50d603ec9b22792950c8312121357d9424ac337948b896cf0c5f4cee1b99acb9814d0a512ca2f2eb970bdbe8fc0bd3bbcd53f9602e8403e72a9f00add7d8a29e33de0eef3218a7a7ca4f63e746c742e0067c5c09fb14d6d0d30b34cbb95a287d0acd04a61cea6e2afbb7ced718aca74600b6a34d71de4b5c85bbc84854fe0eee086208148dd8529a14ded5a8ca4c47bc99c1595c6253819a52f862bdad4fb9d1fdd80e6646e1d0be07d680a637bf63548b7bb967c525dbe8381008e5815b55c67a8c00525e0af21881f949184507a83522de95fa1e42d5daf068cdb1986b24c08e740bd2267fbe88900eb90df1f394fb8c22ae20bc0b88392851f0a2c114b3cbe50803de31375b002a411f66513bbe5734c13548ec3d368b825a48457151175227b10c2a401babfaa06ca67f42f70e7aeaf0e6efef9513987f197e99aff8bee684450b2493d5b5d8f2bbd88ad26b22083a583b2e8c0baab79aab81d1fa6538d20455013f3c58ebee0465a1f87ca816f272bea9aa798de8f090cecfa02f8379c41d50089bfaa7e38adade6ef4872728fe8889f5dd022c8ad65d53c7d655288269b7110c431904460d3bb4b44a2b88d1836ddbc011aefc203c33a31af3e3be49c917dc0b89510e0a47a0cc56c7beb8e705636c8ccce1a2e332fde24d064b5ccf72d3d20bd649e912f6ead680de86ef08dd223e4877f5e683376ffacd2cc8679f908fa10f3536720e9b9b16769129580775c46b0d33f0252651bd432ac48fad46a648cc0f930299c79775034a9962be684ea117897da20b7004cef6607a892b1096985e09711df9f5fac66c6a486df847788d8c00f28c91aec21e1b699c7e49b9879163028b91f7a7f109513fb0aab9b93a4f9ed11417b52708c44f99d31fef2179f8990dc405fb7c3409a45266363e72649e8b54ca9f6cee7cbf4cfa0591486b453ff905ff47e422be2635442a1932998bdb7fa7e3b218c5e73e51527f4ba87d10cf05084dee1a571acbb9132e9ebd450de7e5a134781c967dd5c771fe0e1241bda73008fbbe9a5b7939818bd92c386b700fdbb18a02b253e90365a927e2380b78a63c0d899e50f243f293b37ade33bbd3280b39f6bde190f5456546bfada02b4bb9030f2ebb73667b3a259d0857a3b78d92cb995e73d4fb751381860256f77da70c2e02eb0ac38ada3c79bb64a237f58c3e34ffd6c7cbc2269f12365a6b6e677e0dec074f5ab68212a51a8c254936e942e545bccda6f3c035ecff1ebd1e3d0caabb250e8dd69899e11d5d193308801609c0a20af64ba680d668880da6c0ecae8dcba6029874e0ebfc19645a9c1315476902eba2b6b579266a1892497b516e41ea15660690135c82f2be27ce58c54b6979591d6f8cd59df80b3b45038487b9f550be720448f5d21d9e1859c845ea904239244fb2d3c1e5f3fae62c9354acd3eb6305010152540d0d7e28ed60340e9440bc40b0a6c646d16150c5464bdf48b18fe2285d046f2727e5e33fd3d7804e54f4d09de675ab7c6dd5f45fe577effb6cd4181f2c02a41afb488d76464836dbfec7732e85d19f6e691b3f8d98f1e53e65179c2d17063a1cbfe26ba4823c7939cabcfdaac3b5665abf669c0236c984ee8b22b0ec7b081b4662875e3ae4f8e03ae05127fc23635146b40d2b42f888ce9c84a036a897081a2ee738c15994fb6423e96ffde2f1449351beca3038097e91e76adb469fa7004531655e715e14ce0dfcea3a5d92f5a03a2dd50d193a75cfc0ac31a589853b0bf287e3df1c9e7138f5ef301813fbe45b60f97776b3aa0d3f6c6e0868e2d3580a78edd49f1cbc7df0935422077c7a3552933a09d06200fb0fa0af20b577644b0520430a233c379bc6ab54f7fe0f3b864009792b7148abc9fdbe149954ba12dd0eebd6dece9e89c69ab1c22a5fac90bfe588f70b8341ce8483fade6f1d15f228078fb8194ac5b2dfa25d1828909d4c34b1c5ee79be5a48b41bdb8ba358da5d2e08a5f1c5e615656caf1f2180fdff34164202620898f2a0b1e257d2e00e03891e0885974be3b5091579de598863ae71e7be9eff2c671ba5ab0ff5ea9a0c08b1ee07bf2b737f0a7e1daeec550465481eee3093eb91874d4c00a6979a2bb2067d5d07814c4cc89174d107159f2dffdb7315a819a4fed4417ad977297166d930b24b0cd8edb70ad472504d5b3f9e6229fc901cec249b13552f6eda18f833a785545508fecfb82ae17c982574ae0f8a5db7e94b929a2e925be80f8566c9194d3856c6068361a036cccba9b776e1cba4dafdbfcb82145d4faba092f2816661227bf5ef0eccf58fd242e00d63c66acef68b6666bcbca63ebc444742847d88c6e23575e50baa31e9acda675878eb04bffd8d990273d1cd79ac208a7940dd647166f47cc6099c4c44b7cd37791c49f64529a560525583e40cbd7593056670396e3723b3a70b388a26facdd3d7250f102e5f8380b98f62b37801c0ead4d2a013566f08b00f0d6e23a364a8f193f2048ea691140198e5d25369a0a13abc1ff8fff10153152b05e1ebc4143c9476f2cba54fea2113be579bd4053f97602b09da6daeaf76ade40971ceb09525bb90bdfa084e51d82871c5081e73f51f1358bb4c6023be1b3ed50bc25b23f249fe5646f8e219a1de2f95f29e7b957e336fc1273d5afb63a75dd208063bd0ec5f5b210c6db9e99a49007a9f275e7331bc50f936fcaf9ba6b86e010087da37698680a6a502ac3b9ad4d57467398dd76fd964706fd9bd0a42b9eb8c01622f89189053b72a8b838f7f7285f4faea803c997ad9ab7a070bfad745b492033f0b22d766bdc15b76993aabd399ee042dc3b5c30495c44970fa52b27ff22a0916c563fa1e9a835aa592a735f68c9b1f7c7a1d34fd8792d21ee7bb2914732b0a54beb76213393847797cddf00e38e214301ce4498f6c685c0187a420c4ad8a0bf4459dba338aa2ba5e546462f40c60c46a8727438a72900841e805027314a30c88b623ba7d8637f4321a34d286ca1440fb1df5d19d66ebee8e6b8faa3463ae047082e36bad0e181cf76e00e9a17e470e68896934ad843e709c7bf4a21be967002ebe880b232fb6a451541cc3655df0ac9cc516175a2332869e4b9fed0b5c550e13a7f27adf1fd7f886d7216788e128db7d6f4ea3c3beeedd5278096664f47a0a679db60d63c10761f31b9b82ac403dc63096bacd72308711225980ee998617058df7f9d2d671c4c84399d13ce024e2294dafd97c1ce7ce2da13397d34b12ee01f3f6069f74b084052ff01b965f808bc3a71b7438b54fad012250ae5bfc816f0e14d3bb68e283fcd081e2a674ebc1c2e9e0aaa686247e1a1f353817898b31d102a96ad85a667d0212311b16d380bd6dc21001588371ef1c49ed7e1bf139066300257878a92162aecdb2d715f92e9baf33f7063d2034e4f84f5c54e96c04683b0dc4b787c0c12fadce477806250eb9440dd56d65686f581a9a94ac6d9058803909fbd1bab2269010fb0028bb65b722cb58339cb2f628cc6c26add940d1d8baa80ddb20d4a21b545e9ee8c808ca244d7fbcc856926e5b9e20d0bde9abd3e592f700c9d0577636aee0d2c340d461338e5705389d4487f32073ea16a7de80dedc6a0fd0f80ed535bc739951832c35f3d6ce55c1c57a4f96f25eaf9a043b91a43dc7032a819a5024bc05385deb689ffe9618a4111a9376da3bfdaea1328a521520d00323f2114e5fd12a172d8cfba75fe12714100008db563b35e2b07710a446a5400badc69e1aa5a14281f42d28727261e62096fab29bb96ad945a7c9768cf7b57009bb5829b8b4d9f0fe855372f313b5c07a6572958b1fbed3cc59e6d4e8aef7c503406db0775f0a0324d5af4a1d4bba94e8d1505da189ecde85265aff7d3c45f3038e558c36bc78e990fdff328a69f45ad1df80785bcccfba515e4c715e24bd6d0efbd266eae36a594da036580a995e19069a7b16071f5c8bdd06b349b3fce2a9047d46b707b9b37db24542191b4ccc65f20e178f8e8763e982baefe75776032a025baf11ae3f3c5ba11a5a688cc962913f33a03bd449d1e0869573ab1f2437c50009657fed63f8dd17896bc2b765b8bcf84a2c2353d8b4d81aec962978279efd0af1bbf65e21e9a699dee3c781ef95911d357d01c3733a2e9839090d5e10d60e071b7becb875bc53cbe4b684efa1f9f65ed8bcd2c8ec1ef9e58fdecf06c09153009eebe837ee350c5ef95c38f9e28616b60df0f26cc662dbccb2b9212cc681810dc6e6d5de47541c2b8429bf420222774cd02f65b099639adb253359a5b11e4706d556b609df7a6c0e9ddefcb9921768a15830dda588cb83995d1b4f8e13fda706b5e656fd1abe8ac08b1abc31087e726ab799b54cbe05c2446950a3e918e7e901691c5a809bd60ce8fbaa6b326c75761b42794b8f664f0bc36ee024e75b76ef0bb91394adf8bb0e11cc038f20de9e7e207745448a45887e8c6ff0a49c036722032dd3c6083bc227fe4e62785365ac14180f1d66fcfa3f3d22449f46fe7f0f3806a43483b37f2673601738aef221e6055872da3e14da618dfd606c966bd4e28407254582323e99437dd8ee269cb68db518bf9494c9f046b7ea6a23f967f8649b0c47092f56a26fc03b55244240fc675e8ad36992ed5f34a4b3b56b4c96665d3a07bf5daf5e5bf4249943ede470169c87de9f0cf9ae9bcd8110681fef4785ad7804dfa2beccf36461189ee6cbbec3796d32b23c0cbf2ca8094dad261ede316eca0b3b45b11f70989dd8d0fcb1ba330a0dc631ccc962475c89cd46015b0e1d96bf06dd2a5d609cf75da3c5abefd75a8f4bc4e2c57e08f5f14a0b48222c8ebfe2200dbce329d7b72ff94c2a69afc5a98500728426617b76c6700c965900ba1029f6080c39697b87904cc466f438c55c977fd81554afdbf8d5541c5e142df6572d380763a7d36f03093a1624623910d2c5805bc640b944aef3d952df397f7fdf8b2704d2fe3db650da090b732a0b3d5bc45920ef8eb74de1f8646abcb1ebe4670f6e090492423396864335e37b4673686cba58b18d49cf3c841e82e1dd01b0d1c0b50197eddbb5fbad86ab4063b9c01c3ba9c99fca51b712f7083aa652df2a0df8ea0468d0f4bf66c67003f6d9e499c7fdf6541381d09029196fdebc889d851a9c650a41125922b7ab6f9745ebc081ec18a353f75b1ec74560c9c044f7a336f39ff40c4c5571c223e5abee33cbe141d50fbeeb1e1cd6ae2e72b061130607fd0e7e8a0837a034d6387e49758b95d4fe1069d6abcfe259c48ad4ba5160df38963563170da82c44d243b2e34950184f7a0333f18acf0d96181b8bfc4d5aa7d52c8ec7e808d487242a369b87d677141ad1989f4960e2d2c64097e9a67b2aedf1daac50950b4c56007518e499c9823383f5e86d8b43801c030a561e7bc41fee8f4cadca3d0f8afa0c91122f99522436e46e62971a21a3eb0745edb149fe8c26cfbf15753904767d790b1772bf34c7377a9066fd1741a310c6e7263a9997904ed62450213d0fe6b998fd98827b3fc607a53a841afc49e245a46a4c7fdab9b6a09c0de86697006a5c422577feea009d31273bd7f797e9243545ce05f62bcedde033cd7eee91067ace2b28dd12cf89d174feb4ee9c35583844101189574a20f68f8daf316c2202770ae482f49ddd2eef904a50886b3345b37fc562f383b01ad153c4feddfeca04 true -check_ring_signature 1d1e5a15cd3bd1cb561bd15536f89d3327fda0f3258091ea9aba0d89de2883a1 1e7c877f172513c450ec4cc5951d1382da06c42fa5c5a716caeb53f8a71177c8 26 490b3a96e12de5e7be4fd711cf5093b6fe2ea6014738b62e29b8dee99eb1bfe9 bd389717ee5ba026c93b323a8ea03994885b42ddd213d07c8a89858a61427817 98941e82f4f2867ecadd8534c0c4631e403b4c57b7dd78b65aba6964b9727915 b053d6d30e9ac1c7e964e2dd607a6d9e5a02c54aa6aaac9a90f6abf8b8c72213 2e2e0b08cb91da695fc549c207293e4aa23763965dd36cdec2bc1424a18a3cd3 69bb756af013da2838da043299867db18606c19e8999b55936414d9efdbc4b30 0cb401f0c11384dbbfc8ab40e6463473b43980270a9bb1968866f5b6180c69d6 84babd8d6bc84cd14905dc718e58fb0a45ee79b2c9a82328e42df28f2b017123 75ee08dde268bef336f93c4ca558c9b76562a8cfaa1041905b99988ab6df173d 86c45465be1fa008ad33291940fd23566c74e9f4529b6c11ecb09068a1629657 5fae954b00da576684e3d8e4b5effbf95e30d717c2a3ef8af326a08fbf0930cd f6b36503eaa18863e4d96052592daba3a7f14767b8f87a57d9833c3bca457d01 c3cfd145d1d0015718f893ac56054d34a152102c41619fa93222add516ce53e3 782f7a73a1bd40bd5ce9815975a9263c016ff3dccfad107eb861773b48bcbb7e 09db760830b5c718262e928af3464b4484a938d1f4b75a614756b330b96bad95 5feb2c4c1e64089f1f18ac62f099e04bf03bb27d919cfaa94fefade8503b8d0c 776b7bf921c01d85ad071c84f20bed00ea7273da6af2eabbb1cd0c27febca997 5d0d5424849d5b6e8835a7a1b17bbb7ca86ba48590a9cc9f137a6326979820e3 97730127bfb40e457a243d017a8b4bb7a159ba06edd47885dc939f9d5ecfb5e4 d9c175733964e23cea0f8669d109851bdf9a605e9434edd6052bf4128e9843e9 ad77caa1bc19ebf05ca39efe1539743abcb9c640c49009a1b4524d68c75b0d8b 1f08437d3c9feab5b90ce46ae852ef155d3663b93ec50cf141c169c6ad1dac07 5b88e12cb38188b5fa649c6fbe73d3a3a2dfb8a6351e188acbc7c5129e370d03 0359768dd47f6bcf4fd6be9b8732b3dd5db87a6d2ae912597bc54c7d44eea5e8 acdb6923e3b6e7c465c8a7dcfacfc40dd0e3b181ed0cb0e686627e01cb4672eb dc3a00c3d7ab523253f7d03fbc47b6723cc1090cab4a8979ab90e0da336488cf 03d2b38d3bab341d27bab0e3d51de24857beb5b4899f2c634585ef199b5bfe0366a2fb0af7911cd0e880cd1563c69beef618fd8b3258712d74f7e98e881ee4043acd21b88cddbc28481a95e01722a9313c2e5d0e878809d1df7dbc0b49db3a0ce7344c75c82e0650bcaa1fdb4b3a55549174e0d2ea643e053db1a1551560770be971ffb4b78e3e839e767833ebf39a920eabd88605f2294535bb033301667708ceb378765d46ef2b015149bae38540c90a96ec6441200640ab56228ae6fb410670ba93685a0c87553278635a998b7718a3fe2125d64927fa4f1ca1ca992a4b0df08539025f385df9b0d495a248e4d3398aceb0b600e412b186c720a91084c40937c06bcb7466cc9ce6ff5d692b3ccf6257545690c3320ef67369cc824f4e260a23aa1de4154aa4c06834433256cb5dba01541b809a805d55a1ce19a4d0111e0f9366a1ff6e0085c5b54db791866fe26c1b48c7597b610aa13dc64b70f727d467ee91d1f85eecf237cf7dd4827e9d5d02f7d2c783899addbbcb4b03c6725e5a0ca308a125205adda75bf56d344839c14485912b4642985a9993bbf247dac3140f798b85bb6af135e7f8445d6dc8127936cde514aa667510f15f73cacd85c2cc0eb06dcbaeaf37be3bbc48d49068059305696abdba1a3cec5968e61356787c2403833a0b83a087b520e40b409534c1dafc55ad18e46460950214502ca74927e204cdfe810b3e93f243936338b07f0f543ffa22da921b15ef957908ca4c62a3c70284465abdfa82c02a5e45d67609dd418b369e0d1873147214bc9af1429772890b6226df2bfb0e05254c0283d3b74234918ce5e78dba521a010cf21b9aa4ae040aac0fec9b3f22f54d103490a827c042105426a547a635434ada7b35fcb3c18a019747d168acf0eac3621df398524758560186832275bc38b88ce605bfb6c9440d0fb9024c7e958ca4d528ff6131d0d51be444f23ff8dae455a6c202492ba0880990153a071efdda4d5f25f0318c4052b9dc606d9633889b13dc58168ab059edfc438a640caf1753308bf08a2f4903dda93c204f1eb121d02357eef0711d4c9505837a99d6d686900e255449620578d6cb934f514294263f266410c60eb6e1b20d97484c4da51d8bcce6e37cddf2cc973ae89a63a6d68600675cb5f99df3e5c4033c5f867453dd93fc2bffec004b45d7bcb88c95c15d0a8433632f6523d2e57804dede15405d328c4e26bee91cbb6dfc8520e1a4f18725fbad08ef0a3d53d78004064803feb02bcad75c5f25d4ea7ccb594fa9f641406441c177eec447594c030f1ba03dcf4029f2adbf347d3f8f967109172cf2987c21f8b6473391a8ab656407de6e7a66de16e3c9d741b679fd61c70b70fcca994abedd7389b06b69336fe606858465e26ef09b950c49e0948b93155fcee59e84ba491e7eea09d401a3c78403ef47e7fb36f2704cdb00fe0897c39c5f1c10a20ccb77ccb6b18039d7f9b62a08de782dff96572355dd2d0a8be8ce1daecde1afa4d4d9c75a38056f3b4862bb0802ea9fbb6b1d44ef0cb3ff253a6d2ae386f9e45fc1d15a7bde0d5b0d8deb000103e1c6c49fc03d0f0434507c3a8a8c1f7499427c0929fa301fd2a4758035e50e39083d93c65dfe38e9c169ffa851d632c51fe0a2b8215169493edf89178f1603d492582a551aed967543804ad7d9266628fc03605e8ab455235b632e45ccaf0faa78f4592c8557f9d17601dfeea83addd9fa5b0e5c42541730ed9e21e2d19209c39384890cd149b9c5d452d4232f8d273e3ac6db991da398f243f0e2b5b2370af73722551e422c78caf2aefcc8cd7d9d6dae10814ce0436f455449b8914ec7007731fe0452f87c61827f90daa3387ea1450423282464ca1f5298960cd205c207601b5cc333ba1da7eab6635c646c2ebd4118d25217c510a24d7e220835b22d0dade21ccc65f76db81c18dd261b7b83e0b3a646bb1b947f8da6da8cd9aae68501f520edfe626ee41d32f1ca25b3ccd1c1d99a43dfd54a8061d3dcea13dd48040f14bc140e3f4a35b80c0c38c0cb3a62896981e5df00d75c1e44c1b7784d10660e69c65a3f16d9448273220fe0296b29b9ec6b2e472b399df92d69c1dcab4a330a3ef66ce60aa15987c7d20071a05d1a54f3c0cac5b5910445966d89239012c00f188734781bad6e136549484f16f581049f010f34178a5f7bbb3bd998d8594f0db6ab3914ce566cab87681c9c52571399a9a747db25a539794bc6650978102e0b4b486b78730c006f8abf261db2cc43be3d3ee2a3c76bec6a559925390b72210b731a7e9a2f51ad3933c5d07997bf80f347c5c2bed7224f1d881d8217c3fec7ef false -check_ring_signature 2c41ffa6a70a4d249e665aff7c9e226c12b21359a32008dbebf543f2a9b0923e e078d651b69c9c6f58bd573028c47b78050ea2dad2867a8a854a7424d4ff32da 1 e0904a73a099745dd5856d1508257b799fe9adfa16f1b04e424f147c29a561a5 6b93275c286a05e75a1b2da1da39136912f37e6d52ec9f172c9c0a536e58fc05df6739e40d72117c6bcc42d6545910813e5bbf54db321234ad40bc5d04af0a06 true -check_ring_signature b966101622fb4711794362dc3c95bc32dc81bbe54417576d84cbaeed02642532 dd99466e2e69f11820990fd01b6a7c117c2b411ba58f55a972000e52c763e2dd 6 63ea79997278b9ee8b628be838bff0ce74c2c4e8118a5339da18ab5dc0e8999f 9138033ee7e1f8ded331cf108e7cecf5cef7450bb50e9a2d34121d41905f2b2c 6fe2c31a206c975d11f86b551b183f6465d5d69b1efb3274e8eb881fcc6f9a25 6a716d31a6c99b16f334e8e668639a612fd3440ce297f6c33a4e10e9d6683545 b4a2fa41ee869fff886a78e71d9a7889487a617a6ffa15465b1ce84bc243b6e2 492bd31c01fafd0292d2070658b0b6537852837f297ed0db7031fc82a289089f 77452f51fca423546ea9322c30a0def440cf139f4be6fc917f673402c9579902b29c46c9f6accd7f74d597a0fd9463abc5b36f478e32aeb35c000e9b5fb9ea0534f0381045396bbc9cffcc32c5b7315a1858bee73787c258c13bec90635e630dbfd2c66728d282954305d4725a1786c82b33972979da1bee6954bd3aaf819906c733da11a82173150e9eb740d65a152f74c32e61b4e840a253196248963f020144f720e982610763d326b410668b70ad592856d1e3ecd0928f6bd071b471ca098a89258fce35ce8793c5244c7511692089dbe2a8c647f095c14d82e46e49d1049f953c820a85e4273640e6291d80509853bee5377d544489131a9c2ad063a104554895d6fb5db56e5d5a8dbf96eea63224f431cdb15e5c4c9437b4dffa44a303a554aac2e568a53d4a8cc033d0702c754108c8c10bf5d865a82e0d1f191f6b05693535e883f6e31365998aa746858894065d39adc76cd952130b369753b4fe04e739de4c42e2ff1e63d09f4f5c2a895668eeb260beb87656bcd5eba5063b6f03 true -check_ring_signature 5318c46b99a3eaf48b5b449dc4547939e6f2be178ad6bed0a07a98be72a69b83 a6c05e262054ad28577d628c4ca75f3895f5928d8b967de70728f3adc71198ac 8 aca0c3cb323fbaa3c1767ddb7166566cd10d88691c748aadf7787c766be44145 fd7ed765565a729c34d6574435fd7c400188da70ca9b6b17f5a0b4d45026e50e be5eaca3f2709ebe564428346f358c75b67805d7f5ab76acfcc06f017ffe260c 1af3ede416e5881bd692e61acb98dc9f70469e9d95a2ce0a24afbf6f93a1f648 6fd0d0219cd2e5dc4cf6b0afece52a31f24f55846e9664987581ce5174b0de04 d1b5bc859bc83176ae2bfcc419cf8f969dd9bebb5d0ed03a255eee86749fc6a0 bd335732b0426d2c2f7c689245c34be40e65a0e35825fc3d52086675ba95063d 4dda32a69a26703e2acd24aac8ad65dc0d1c3a4f8fb667b6738e9006c50b92ff 718558eae98fdb71aed479ffbb052e145f9e0aebe649fa40cb878142a3ab520fa588ce4f952869edebfd542214ba15f0f6379a026475bacd1f366080e520c2096b93224cef8fa46f08b02c019d68dbb9c0f9ae13a346ba97ce14efbf9b3b67013c6c8f4b5d56433fac7fe9698dbc69ee426ebee988a0ca14241d7ef75f00430aed9b771277da111fd4728a9f6c6cd303130c4534b4d5845f5457f0fd7a647f058934e13b21272c546ed0e4d1477b135ac788d29ea6d305a3bef3a5b2526ead08c29335748b0ffc202f4fcfdc01b75e74a9a04787f5ce15699a216173a2bacd03ee29208f7e65d4a8e0a0af9d5d3455b5b9166649b0c6523ec12b6f549002c300aab5cc1377ef809bc3cb3ce10085f8c8ae0b0d2d351c2827c3e27c5c429e5307e161158f47ed98f157d8235353affed63f50e1a82b99e3a01adc105d878fb2047191147d99221f730d75ee9f996c707293ef106537eb7ee948b65384c9f6d40be26e625df7385860cca53bf0ffb9b76831d447759ed4e620255b19c7939ade022efade63e507f267c76c2030272b3cca75e66879c81115d43d92946fd89c860b43ee00b1c2493e583f7043b09a609d8003b42d7b3cfa6f690ed8b5993265430bbe03229463423cbe3eb6799c8d75bf7aa4fa07d1ed0b9339a9154a9b8a8cdd0ffce3b665bf8ecb44fd13a1d6d87d5f3489bb5808d9b3593f694f450800526902 false -check_ring_signature 0b4876e105402ff551f2ebff9e31bdf01760d809c3ecffa40c00fe5e9cba50d6 9ea334db8771651f1368a306f41d9780af56a542d5aa5eaa495557205008d0fd 118 dd80c0b1d9f37a534ec35d8daa854a8ca3dd3737bfee72e9af85d831f1d2d7a8 2153bd00f81c2128b4599bad6801d898308dd17da3d3b1abced8bd4a6629858b 2745d9b6004693adf23811e5196cbf006d001fc007897380e7a187b078788c1b 2098dfd63d94caaabf4aa996d344e4ebb51e88603407d33dd2be097393a8550c 13895b1262196650c991c9647eac66305f35d5d4a4df75645b2ad4f8f9979968 d0fcd2322e43cbff818fb9fb356d2a5cc2e7262d996432610a1fea7bd4877cdc f14b5b70e9849bb2a621252738aec5d96dfd20c9f39802b895761c790980abb1 53e53bcd988049663fed8a2e23e5db1d29e18c93c468df0c6259b95f442c61da 174a46dd923d6f124f49be0f88120a9309b36132d6f58f8dba67a94a145fa7b7 7923e3ef5d4025d2cf9518423bb5949dc4e8bde214018ccf3edba7490d71eee7 7b0655e09b15bae6cc2732149bc62f9e1c186b8bdf1b605405e7da8c9cb8e610 f5c833eab98502d229fbaf002ae4242a4a61422cc360724c3b04c5d415888290 c2463c64e1daa3eba952fa318885172518964e8d77f9395cf0b12b82cfb23572 f8ac2d846ce7a3c87eb33ee3db77ce59e31cb9587036ef50a3d46f5df305ce27 e07886fa982752a2156cbc974e2c9dd575e881b615748f76502947ac2bb2ae37 2f132c65376f91dae32d04e043e4b8b24b63435f0f026f6101b491e0205c776a 4e57194ac8f6d3e095fc3209783db783901e9fa22759e3717e661762e1a6d864 1cda8d70b69c15b48d3dae48080fa03a45c2dbb6a5b6fd0e77cacd39b544d79c 0ceab2489d831958cca64cfd16c76435fc81031b0164bc88e11478cdcb7d1a8f ed4713f75e33a2aea2432ac11cfa1b88041a487ce1692c2e5cf79be5ee728e9a 97f607d99cee394d4827b4046ff116f5c263cff47c5f935e527181f86818ef14 4dba8945487b00e4d046688c117d31475719d323080accb7cc45ee776f31668f d863a0dc641289cfeb4a7963df7050461696d20ed8cb0738d20133e43a74f342 68071564ebe5b151ea05a69871dbec049313f5c657454fc8127669779ada7b11 6614cd9e15b6668dee7404bc38cb511b26570549b51466e23c04a813933142a4 5b1888333fe5d5d8c712c11a8fde209a97b9ff326033d8acd6f2827f6607cad7 445f7fc863739f23e9a3d4f9d04311d9fda794c51ea6ee69e93cd8f4272a7e8b 9ebbe2bd8fa4d50ee058b2341676fcf831f7bfbff42a58ca4548e2c892ac24bd 893804f4c50ae51c645261432d3a63631609c53a3858e75798009a8c944a5124 9a5c39313d705fe9ff96dcadd84f3b7c4073c33aebfd7463729deb17ebfb6224 2b1980b4cde7f604db1419102328850c2babcc7fdc81d67db7f1e5d7179f2aed bf33883078a971b91e729cfd0774a34a422d856639e0d99a0a187b5ac827cf30 569c6b9200f843f23175080fc3f02d81fb038c7eca8a1cf1718f4da7654693e4 2de675647c44625bab832e16b28566dcbd7183aa26eeeb41cdf36956c7914425 ebb4b5cc097cb63cc66673f06b7c2b6e8cef9041f97c1a9f37aaff9ff0419c2d 24c9ca2a524f12b0ffa10e2f5f78149062c0e591287233458460ebd6cc374093 d301e277ae6b4e9cab228335f30be00b1fdcf06a709ada75693a668eeefbc6cf f767805bf68d56cd6d5ee7d4c0d90156a950e3f946e5309bbc28ab4695c7cd18 808616e98059d16e511b56a70e3aeab960e0a790cc1b4a2e9955305c4d930298 ed6837bcdff01ffa68c8f0988528c4ce6a6b2aa2c0fb53cf3287eb1190b9cdf3 fcd65dcc6db9f5f5eb7ada6e31b6be38d2fcfd3e9b31850aade772317d14e352 559ea0873abc4dc7cc4d7b9353d16129cab6e98fff0af99b5a500501e5018367 a68339d5f621192b5ef8197bd703b0b676c23173402fa4f3eab9fda5698b6a8d b163be56b5c60fb91da9ecfa4474a23aae81677193089aaaf9f244297d96f863 11dbb1fc4a6ea42f3776a0b7c0dbbedf67271960e24a5dd43032e42bad81389a f33bb3fe3f61c6359acdef06afdba28f915c0f09901299a75e7641bb79694237 86d77f9002b727c0588822ffdf6077a0dacd926a8f5845955cb33cb54701e6ed 002d80dd93833dc1bcbd90b5fe10f67c97e92caa22fe2cee34d07e44bf606555 c3388bb9eb6fc5f6764df169b0d7a8fb30d7c176bb7f08557dfcc7d2e24bf720 5bb0ff4de4492d0eb819ea0050b9ef15193733fd6c35b1e5c3308f9b47b3a4b6 8e8f5e416a1cea31ae625c780ca5e176138093425bd95bddb3d68ffa3ef497cd 64458a67b1c4e476a2ffeb5545426b6843505fb8d08b9f835a5be597c49a7076 5c59925ee660cf4e96133efc4685761f83635aa7a77dc157f1aabf18e22339dc 7f810e7bea2b258dababaaa54faaba392f5aad45d6488185b8e720bf6227645b 8cbe9ea05db46a61bfd8d03fdc26cc160cacc9add977d394cb5d56e0e2912877 1fe6c334cb69147c43a9fd94cdd391eaa77e5d8ea20689028d827375c46c9e30 2d746c82aa66624d0bca9c6b6b12323c047543379ce3dfd3bd2bfa6113670caf 7e4fda8cec545cf417500e191585e08b695675eafb9df0a3bdc2fddd07e792f7 9b4ea1c2a9b96f79712acdfbb7defb2cbd0faae96c961f1660445986950c71be 0511a268abd8ccb82726e96456c17aaa0ed29deb41aacdcc27770779cd0a9f8a 4f7b284f242c112a34ef3cfc90bde004bbd2159c103897b23d8917019554d507 75f270db41ae6414104bbe298b2b1b9632c0e8635bf07d966a3227db7952a758 e27169988b87879d82408be83933f9a775eb09541fa1e4ef5ac17f14bc86bcd5 55d0202ecdf160b7e07496324937fcd601e37d938d9087d74eefbbf11dab7fe6 41ef6ecbf709917b72c1084f66920c02818dc83117d859d2c41f91228777732a 387451105e702c73e578ead45ebaf3b4a2514b7307245e2fab8c33d4ab28f6ea c2ac61ea4817294bc9830ed54bf4016f1485d0c68956419e670c29e368511f28 bd10da1d5ad923ad7555dee83a93020eadd2f519aa8e1f32c2f08bb63fb78632 ba8ba7e7f931bd885f76c9cd794e061547f1a615a5665710e8dae01b715548e3 bb0cfa51577066ab016eeeaeffc56f41231deafc0fb0b21d22ab52490117edef d0b17869dc3f0bb04f7541c6da0f7d9607509421312bdc870c28df2d37bb074d 3219e667077d5133a57fc31c4d8be3e0d3f697fffeaca42af2f8997d41acdaf4 276eeca7769d462ef3ae465caac30ff4852d069984aecf333eda4aea86e399d1 b58ef4540137055df09e05b9919b63258c9212a72b529fb7792c8d4613ccd348 ae3fe2b56ebcc78ebe4ebd0e1e1ea5eb6b3dfcf06be872dd7aa01fbcfdfa3f2f 96f7b28e990c719957a1451f423564496d024250002439552f273c07fa3032b0 f2b35618dc2f9ce6e0e3717963129ca9cdc5a5c66099115545918462a9ab146d 0bc372ed7ce6856d934e1f661a9a4ed25be042d40d0441c203ce3e8248c646a4 88771091355d9e406c45c382a86e8bea1c947ef5035c7a9d03b1664139604257 49bfab36d1177f885fe00f753e90242e0787707b8d77f3fd471f4f5095f97a2c dc65dd46f0b6e3becdb4ad1819e3d34080d472bfdb7fd67a345b3292b72a1678 4cc700b5fd17839e82fe18bbd4b131ff5e8acab4a2be94fd4b4b976157dc04a1 99d32f5d2ea18fcb5ab6708b8a119630cbc8953ec39dd322bb70eb8f4fe9fa69 7ecd1dc7d0d000ac01a4f6ea8f4ee6c29058a31fc7676523df31fb4d514b02c1 02b9b2184a59100b81e3070a4051ff3b2c70ad9dfb6d75179a678ee8566ec9e7 814db4badf5dc06938aa771a03b501207b83f445b7c8f347cfaf9b7271a792ea b9bc514502c569452f649da1da3073af0af913f7c5b5de302ed07910506bb1c6 6492d18466e8882373f6c94f68d8d2d506b0ec452c4d33a52460182811e0cd41 d71d56ef267d02026eadc0576806006d159b840f8ad09dd66c35031b16f48752 cb185bf0634a87b561b9b43711c67e9bc4041ed13ff5813b27f8b21f445b061e 39749388e49e2292ff8d4b4107697c4dff32af574d51b2e4524ed6df9fc63cb9 d65dece10c828c4c510f3cf7d899b2ef6eee2a0b90ebe4102c15ac6ad8413985 ac26f02375e09b7c0cc8064d94891d33107497fd10d8713ef8fceb58e3eb3c37 b356541c4d4386935eb3c4e1cdaff3dbc8a8a79d26e3d39455d5568b6a421caa 5f0e0c2b58c88095ba3c9654e81f6f7702a755ebba847a7d2f6537d961b4cc4b 36fe237dce6622b1b6746375dbeb92c3cd70679e77028e2c5c557a731aed3a07 100bf307102e14a1d3c38d51e91cd5fe480a1376ab93776acb56ef2444307c17 130806f22b0e9108c84c4d4d50a7152f5448732472c3a850f3f49a3be8b6eb6b 6dbb51f7096ff7540dc7c97ab17698722bc89ce5844336d0157cc76ba935ceab 3b827d661d46aac58e049a3ba1c88cc9c67922ee2a592e345dc773df3f5efba9 2df3a818fbf3570fdd50d43f0a8f7953f97bf74b5254072b605f137e1a3e06ad e30bfc431698235ee99cab9648e1178180fef0cd7f24d3d5ad1ca80c6dd91145 978e6416ed62d462646ca1e799a0fc0686874031b5b627614bd56f229e52ee82 2e762a6cac6017de0dba6639fd678c7f14b477b0480d742e283c6d5f4d06627c 1f9da9d98c7dcd48aee8b906d03805998a3154e4c3cdf19b270b8a44a124025f ee073e08638d693a57967adfd7141f41887ef98fcc73193310a814d8e1fcdf35 5496c5c81461f73492a9972cb3808de831542fd41c36bcab8e96535d7f96e3cf c9cc483aaad63dee8edad625200b4e1955c2693a9f3451cb91118f84536e9140 65af27df9d26b9ab1c7d4b61e5735d8eeba07de2a29b180f99105065bafb3b31 a22044dae970eb70456f15b7066a0c2070876b6bb68a05c949c3514f198f3b66 1e49ee8f6417ad5807dedda420eb53be9e959d77f9317dcecbc90defc8cf76cc 4ba339bf2fed981e3a18b3b9a66f22825dc8bb13c1d2b9f715a2a0766b1e1e62 b3f77a20aa40a841fc43cab4dcb6a9663db1517d717b2e6b5d22a697552868df 4f65dcb0affc8815950de88a0a2255845428b0db65d4a11d5a596a176bddf4b2 dd3b3d84e07e723729a91a5e4a52aa4b8f74ace7f373ce29306c1e22ccdc776c cb7fb5bfcf3a5f5271148708a25126d27e32857e61c8135756014dc6066f97b0 68edf471be230ebcb2cbf4a8f38415d357ebb3949b622f5159b3402ea24e281f d09553a6c6c3bf11c062840f8651cf868a5943931570fe13d204bc6f0e03030f b281da4697978bfcf78597715dc6e20588e78992fa3693068b6ce3f92fff8206a7c50cad89353de97a735c355dbb8ac8febbff80528842aa3b505ab57ec4db08fac8420d998780418093aad0153da662d2807231dc5da888966da9fe24ab1505eead0a0000ff02f9ec172649d25ba04785ecec225b94cd54031f6ce1184a600bbdaf57721d07f091c7d46ee8239ebd356691a0a9a7ecff76a4716047c48e710c1f250e9e31ced49cac6cfc38c564c9c817f4990d7067c243083e88580ea7e30486c99ec8eb8d8ddd1a2898c3b0e53cbd817eca01d51ea90bce5358042fc0a809e81134faa34cc74255de3a9d2c5bd98e36953b77e642c880152b54683835a706e28c090a18a863dd8ce832da739ad9befd2d8139f12aa49cfaf644fe046a620cd05d93ef496be6a4214d37d5ebd2abbc174534b586dd7f9a7475ed3590a9ff09d6d4ff05c62d3595acd111d7dabdbd75a0712aaa3e1168f523651aa961353b0ece4f019fe704639c52b8996ba0b26a0d40e722048ab0942953c1b331c779b303fd3fafdf2c2d73b909a2a3d4ac123222dc7687eb9277f337246be2a89e021d08745b3517c18fd099b0b1bab5733735c6cb94da56c13d13534d21f4e613464306429ed8ac4a815b17bb71374decf4922b679f008f79b63268a80e07ac0f589a0076d09a1d543a321a46a685e41804be023cecd1aaa4bae609e956b90b9e569c0b4d947858d9f47c3ec79fc2ff50e0591e6fad091cf1df3ce402c74f323966fe0e740229c9a5c005d403fb7bcf2be216b7277fa4071219be575e5509088e4c000a86d98e5322ce14ad96836c2b795a2937a6c576b82eeb30ff629745740134e401241351c1a67ed4e893285580bf044c18a5ca4f51a71a1398f0fef400c9e0bb04f4a6aab5468522c1ea2052c541ce7b3941752d9e6cee5bef7e7986b5611e5d0ceedfdc879c06ec0624fd8357072311026543bed4cd11f906e866f2fdf3f6b8081fe2a1ab11740fa43d6eba10b0307555d4a277cfd6bd90c2c789a9a693e0cc0f9c044cbe5617486adb57a6cde9323f10cdf0d09fa330a55438287d29877228018e64cb2fdec5c49ec842166af46f5598eeffec181021bf3d67477d3e1f818f06679cdd8f7d90c899418316dcba204f2b7e1602a359d5347f0c47e00f7a3e8600e925a3225509fbfeac59ecd09a0afac0ddbf0eba4c4bd60b06ed117c0ff420064e1e30585785401f877408d8ef60c3488f912645083f119b8a9afc1f95c8d904c965b1c87b13b6490ec05f7c839dca552e7eb140e59ded3e309917bfca548a08c06e2fd8b04108dc2b19462236c6b3845b0410ec824a7c5db783a55a629cfb00b40ebfa9813f4b5319223d8cea8457a3a5b1c6c28800f45d3f7a99f78d8d56003aec4cf530f0e7910d9e44be960cfa80a9c8ad934ff9f0c5fadac7c352f01a0ae5d82a6598378b1ba06551a5b7e8dec9b1fad79fd46001e36e08acfbbaf1530c7edfe0776b288a09336e70c255940d0ab372328bbeba3bc9bc64f34d46925b037d3ea80244fd74bd61f8bc439c01bf732846fca8888ec87ddee0f57e47028f0f6762d8d6ba60cae35bc6affc5d2adc00e62746a3f0fa28de452ce2450859fb03637975ec8a441dc4f664e2de752bf773cbeeb2c9f4513dfbef55ff63b359b2075635dd2bbf219251b7110c1ae122b2f875bbf2a24b21d3c1dd8488bb2c15bb0e86d2b69dea05e95d9c6130c55bafd63cd9d4822d1e1883ea52a2970f83416b00d8f8337335e43a7a0376fb0097592bf3985cd83a38859f03cb4219fc74a4e400622d5497c573dc0b0287a2da51361fc68696af6ede67b742f7bce4614841f90ab3316f75dbe439e84a5e591c6a28a0d247944736a31d426231d877673ba58608b00eb7b29d3a37ee58f946585aa6980b49adae3de164e336ced24045ce71380e3afa6cb5795f78ebabec5728f19ed033a5aa0e367b1cbd367034d34a68859a071f94df51ce1a98575a7e9985cd7aabb7d18d9f4564a4dda9c7bbcbc6e4814705503c3e76e1f9b0c47731157d3db5cb11d8b7c603e640b7341e5ba340c8eb280db83bd65f0b5609898293fbc140ab3c6b8ff853c045b8228f09138bf0d6b37201292f4d7b02ddf12019e84cdc2104b36a4cdc9250928d1e682f90685c01d3180f9e465b15185616864120cf85e3c67c4c92cfef69d1746fde92622bb057f71c005088b6d7391dd6ca6870da0f2f1c13134c619863408b31a0f701731c7c7aa70898d363efeda2491abcba71c3378a5208c0cbbc21ac1c6c850f73ddefdea7ad0cadbc648d7b486f22c8f26a83f20ed11ec538e25fb0c5dea2c8f6dc01b157670984dc5af35e0ea2feb0f553d9bc369daf65d1856bba5b717f853b400d6b705907774da37044f139003e6de928e8afd4504d0cf23b395981d6dc34040f4d3a2805e90f9bda4e259102016e655dbb2d4cd8b2c747b5db0b6af16122b953024eef07a38bbf019e295639eec179a8fafba8a978a3021b643e16478afc5ef0e20416072f28aa4b0d87352572714f35441fd9d76962c879c740cd6c43400aa2c019ac026989f3c20d754c5445fc6c3ad74f1026b06ee5a3810f01aa229b4feb31340b01d691c8d804e0d925cae3b108545a118dd416e4bce3273ddbde391620f197730850d37036a4e5618d6fad015c1930bb48eab7f22c212385af8eb0763a05c39f032cfcf0e405f59d2cf787ca38cf164b02d698499bfc328c50ba1136d8627b73040c19476b430eb71ed8c422809c0a3655fb44bcff38ae86b7418886c716201d0e12c176e23088ad2073ef624967da8417cf149e861656ce4044d8884f81e50e0dc70f8a7b0d6ea30c82fa3c67c0d8eb915bc01708c73276c6bb1ec63691f28904fdae51e7051854e1e6e66020a7fae6123249357654e372f333611e41be610401df4daad6ede4eb224f765e5df3adadab39af5568b73e1a61bea6ab687c8f170ad0350905587ef75aea390e6d6542f6f3ee333721bfa69841d62caadd2417900025fe28cc832e171c843678ea0af98420f26284e7091e3e63fc37592f2e4eee0ab92ab2690416cfcb86d3b1bde993838693e7d484e2f2c51aa9f6455d4f58db04135a3ee917ea706dc77cf9f25cf7ef2f606922e86960c8ae4f756b52e90661070cfa55948230ca1e46f95e093b2b7b1772e187cf6cec9e2fb900d21b4956360194cbb700aa741db4fc9ab7a3fcc9c63d7a7e5875670098f6ed5b2b5e6892350202f6a1996d1aeaa58076827d86314fb3e6f69b77b68ab92d82e3deceaf7e4b0bdf2589c45cf13d36115c4deb114577ace2ebe8bace4024de92ade73cc9c267022aa41e9bd6b2d7a08dcd75a882750ec64b7013964865b7ff7beffca446bd9303bfca6494fdf3653e2ac8c83c3b40000d202314586640e87c1a795bbb084fe504bfc360dc747e3a5eae226a322df3de2ebc3fb932f8186064ff4da378ec643e0be2ff41fc93ef6eb89f7a5e21b863c283681641e0290f04f1a424772abf437106942f630374769d967035785dbf290b6665ceeb4958bb950b0888ce5caa0a4e0d5bc8826148d6b54528c5c66383ce2ca6ac10a4302ad0dc397eebb7384cb46f0862aa683090eb26e01c3e98703be8245b27d5b4c0f854a1e259398f9e08b89e0c8cc8c45bad45df08a1c8612fb8f5092f8b02e17f1c109f949fe984fd8f36d20d642458cfc70631e49a942a82ef8896a16fbf17ae0c275763010a6f176a928c0cdafdd965404a1dd79c2b284fdaae8701f9e297b5fd40788c6167e7a0e043be05b4dd5b1496257067c9bf2967f7456362d7b825326599b66dc5c69962c10f720cb0b3102ff28619c8303128bc50cb451910fea8079d0ebc81e6b4f4412e24dd0710f77507e510bbc9d10b559d0c4d4a4cbfb10f600d2f75bf38bbb702364f3502c1aba9804eab2401f6341371fceed685cb43f1d265a0004074801e5d62939b0d9f26ddf419ddafdc633be38bdf326befbde6f179d4d09242ac7a1b04a465d90d2fee8284d88d57f61e3820d678120a313fe85075ab22b1b9076b7d8bec703c013096f1f10333f269496354983ad48d77be5b841d7b42b898f162114d95ea2f0750bc1446c548b354a23166a769ac53102627695cbf568980194ac36cc42a9e0de355b75e2be3218cfff3bd4d62f00876f33ad7e64fc4ad618dd185ca0df74603c217df3fe7220e1c1fca8c6d6bc10e5b77c751239548f123e9fdd092ef8d2c01c41f96c9da7d2bbc6d4ea6f931bc1b817095bac0dfe9b5a0c9a83a04e7f8a60a425939ff1ff8ea5e385d1653dec784a3c4b04db2a003c8ccef943a5f9ab6e40a560ba3725cb9f8232a0dce3e9e006be59018bba2848b7675eafe6b41b0f56e0d05e0233b560089604cb1bd215de9f9020104b69ae072612d02250fc1c592b505f0daacab92f74e890208b6f7b4e1786b7f771d3283841859cca25d0ad863b70c0ea9989a57b00c11387a02d7fbeebfb5e0468d36fa7614a3e34d81e1487923080e8997aeea11879691e1dbcd7ded6bf75995fb4ee86f0a7a3e5a718093916c0064e509599c14a70d79c6690508282aee839e2e932c98f1339a8a4f54949a64045291712a7199a4a8212a30fd358164f9be4ef184bbb4d2caebbcc58d9d410209f21adae04719957126c427d64f52b7742acce586562d89da9ec9a278524b510e00d1cbba51166a0edcfc5bcf80698ee81008c7eaf92d70144afbaaa4427fe70240e2eba4238ae4d70076899fe3157de4af897aaf247abc28c3fedc616380b60e5e6482b011c6ee336a971614adbb9b938911c6c97b86aab813f54546a66a7d017fa8a34f93694871bd8ba7ae2d96bece0926016acb59cc3ba42564564dfa7b06f9fd6f0169aa6dc45eead44eed2fdb302bb78acfb4fbeaeda0ce6dac579c1802e61045c014ecdada2b2e98ed7337c63390ec5f95cbbdda73213a35542b1bfc06c93dab026e0f602c374739667079548a66f52cd76d3fc94a499158c684342d0a4197b0dc15f56fa89fd4c9937b5feab9b5d79d945bb5f81564c817c7f8178d0c2099d39d7bbc531bae940aecff296624bfa83aa0fbf9b4018ef039891c79db00890666e373e25a4e2a58210ec96951182705bcf719ccb409f3df6f804a070b077f03d87881922d012956fa8e956d37dd796092cb6f23748b7d95f2eba3f3120004d52389323faafd82bdc33839356d7a0dccb04b8943be29553fbf61a994250d42f026819e1ac45d5d145ca0aeb5df9d49ad5c86a21d21fdadec355a3a48d105cd2f47db9382e07ef8b57438000a8951f6949a5fe132eb3004810a7ad15303008c42fd7353e55411433ce967d8fd9b58614f568fa2401c0954c733d68c1de301cb0a573d04a28af064be49d7123ca91dc2b47c102532f3b9dd57a63d0ced2c09b2ce1fdcab0fe745d91e8a276d87581d2f4a4e3b421f300f0adfb88157843801da79d7a7a87fe37552512593f199fcc00444643e8c89aedcc1a6e2d8b6a1d20d61c184d79ff311441abe9d08ea26f45c40fa6d910f5b19c878ce75d1e0b0f6037313bd2dc2a91302aaa81c54f2f12cc4b8219b639d6f8fe83309265740c42f0429ca5939ae3445e834b6c9c7d412b940c6e48a547359470a7f75234d58de630515161d68264cf7bd0c65057f78e27acf9aa23a34d8ee121a3701f0599ddc5202b66fa23cbaa2818c0ebb6ec181051f3b1bb82ae0af6eea353cd83c31ea24b80e4491e2320f9d138e741c93da9f449b112bc0eb23062427e5c98850bb2ac3b102fe5498ce682e599f80ef7d95c60af927d699d1e637c0cbbf291b2e17b7a1e1078738cff72056e00562c5ab4f5740178a8cfca6f303ef713db7f74e2fcb038e0142a89d6c5b6098c0caf25486b7e93583e1e3d9bc6092eab89452242c9e85150e461e5447304af7b0cbcd83a8a4121013d7c06e07a05a252658487508e2a2120ff4c4b1c6260a45db6e831eaed8b4204f63b31f58c422d007b3b38afd7cde2b02726384c8db8bbef3ad74c52db0486135619113c0a2656f55b81437dbc5ffc20557e40e72ffcc53bbfd3f5ba2bbff9a0265c67bbad427dd12873dc7733b15fb097fda64098895d12373f835cb8e80f996a9406bbeaa89b1cb12c92c7abb31f40ba730ab20248bc387c74483f591e39bc835a089f7ccbe06030fb9df302622760ea525bbf4e71fe21750c919cd96bea64fce2858c2b5002b8d88a34234644dc2035eaa0c461a937154f163e3cd80a0b49f35f2880ecda84ad91d3ed4946f1b3209dd5e93cc0c36d76dd42da3f1365774f2fcb52a68a7868a3d9ccb01f8f3775208688ce5d98106be0e91fdd569d8e6776136e6d624d3938e62381104f4c7fe8d0a203cb5221249ca72e2d283ebd476595271092a70bd7dcba50d5982571edd1d08f42fe272679a0cb4db0b4956933276dd8cceaf3157972c7b49546f0cc6b16d0444710e352b4e26745a33299e22cfcd5d60851502f81089d47a5e4c6ba678c20b65f91bfc62992d5078b730722164a485296c2e840f8d2edf3f043edb2498a0082616fba3d7f57f265b4afe6143aa4851309dafc327fba0b82f70fe65df2b9a0a991cde8807d7adb200281ddede695e0a05c26387d57c65a4d597f0412229660137898fb3e8f0d95c1dc1dde9d6c61fc8b9fff141144169fbeec593f4aa8af807c717cbf61710dc8d750780fb955dd38ef3dc131983353af913f0c5d3cd087604ba830a96ff7807c88edf0a505ac66b2bba3cafc258021ffedbd408564a195e0506fb736243d2130eadbf6f0d0d9503b58143b0e86d7ea616abfffeec36873c03191823c2793288faff1338a4bf9aa8029c859a47a57f0a328a6aa75171721908749619945840b3dfc0076d7e2a69b7e513cf362b8360bb3d4f8b8e86a9c21609e877458afd2afdd47225d76aac34613e65507a20d6bf399250a33e2a786aed00747db36ebe6d8f509f428d2651920b74f02a29a0df4040e0a88e1890c8482c0596510b00de1c3e02309e9805c5abf6a9c6cc6a3cdd3120da4bf86996d8d07801e5ca02b4edf0bf24566bc417bf090264551529d6e33cd3b5bfbed016fa3f10014a3f704a72c7b9df94e3fc009761845dece59d6687b1aaf627589d5f822c870e6b0ea8aa833236b0d1ab167aa868c0d19ac20ae0f6e71ee48c054c8b1353b707613b0cb1f19f07c5f5b940c3cb5319448ed10a9c5e2ddaa19089840959654d0edf38aa4ad92eec7c54da40b35ba86fe472cd6829e5691e8e6f949b31a7f23a0b2509dba619cb843edb29668187dfef5821aea42d1b75de2fa57016ad588bae0855cf2ec7ea3b4f439a7293da5cc574d95f70f47aedc3f5bb52844bd918bb500cb27731b871f46430a22b46f2a253ab2b10b52c33b44005d89867468997aca00f83d424a6c93110cff9debd20c925c70823867060240728830827478dfd0693016f972855716a69e89db2e7415e8b910092999ec8b6803f899c8d47cb2a014f01fe78c1034ee4f0497f5074df8a5fb9b177bc34e15a1a2bdb4355f8b16053f407a2851d79ade3c9dae03d63d121a8667620651425dbfde11820a1d4ad639a8d0ee275fc9725472ec9b7a13232d545063a2382bd71f22a6a4d0b388cb2c34a9501791e9f32ec43094cc0bbcbbef719f3dc487d0a24f65ba1842877c327052569066ad1478061fefc970e154339692e9683ef1820be9ed6e59c003f7152d17b5a0908f7a814413e04e374f6b59da0c55ae5596b1c972d4a7b79ef8dc9a8c2cb580184d30c5259258927cdacafce694e5cbfb0a65146d55aeaab6a17e6ac590b1309a99ca52e658dc58ca97f2007bb9f6892aa990781a8a837d0ff8602be33861b0ccb697ccc4c7f812c7aa8598cbe5976ba75d6dc176467899730a86ef5be06c608750177b0011edf11fddb17737a22b6f17ffac72649300df1884b7ff4249d960d913354b182b9ac04df59fc6888025fe207f7551032403a521c138c1dc996be0495f9cab0e0afe29cdf9683fada4e7f21925026ec372b4461ac772629ecac53090e26510a6e86530934d484737a945119b7f6d40c6ce2ac752431e2f91eb15c044649577a6a972ba658289a627dab0b89d26daaa873b58bf1c86ecc32f3a6100c4d765a95161ceba326cd44a9bd04a690e11b548d938cb6cbb3d0907e037b020398ba731e2377f950b79a46d921efd675ed7cd016e3fd2367554ed65f633ea70888139bd4f17ad6638287fe07fa198c9033c9fe89ed59dcebe702ab1ef068430f326b583d0abdfeb82e8fabd6106cd43015812dab9eb5c19e68c83cb459294009da8f1795588197e89357ff567a7ed5a9b045d59ad44e8de2c7a31fb88c2582095b4ad7962eac532e889f1da0f66301292066ea6e873d38e31ac3bd9dfa67590691827d7d6d408789a868e3e479fa145b85a2ec044f4d0449e765aa2ca2f71203c51f87628ba8c2274966ee7d56e3d04109589c70d08f5b2c84793ce58f5a1c03259ab80ae8aca357d643738a040c1edeeaf462ab0c213cea4ef71662c42e770e252c7b9056578ea5ccdcc226526be094dafe1ece3faf0cd07f6a3a356b92910385de29eb8f5c0239246af1e3967401c1eeb1c586aee3d731d0c5afba79b99c0ab85165fd33c4b7b7804314aa5509ff9868a59acd7c4665ba3998370c9f1f1401e4ee7f2f9e71621b49188a7d48801339d9ab6bd6bec70c34154df8f5a3dd80073955e37d4e6968453dfe619895eb3a0b76f6d94f8bf503e917c3905b9a16510aa75b600d97d686891a37caa8c51ce1ba2e32f4241c6ddaec75c84ec9563fd00b2d3155d2e30b8abad7a486789453bbfcbf93d707b540b0e3ca0dfc431d8aef08b58418ade24c5027afc33c4bc98a781c3b4739363b23fe8dfea2e0fcaeb2950260ec51c65c459e8d3fecd18b9ace5343119dee9059e25e9502d09bc533ed020b92591f0f2c3dd83b7e5d96f8fbc00b03a68d2c04f7eec7db7403bb5668dcad074c3b702bd7aa6018eb21a3a5d23bfea4bf0c4fcb929a55f42b9cb58464b1d10550cea981292fb5f15cefda322606fcdc78597f0f7e55f42d9fe354c93456c800ad1045e27f10cf2f445cc8e76aa8f3f4b89235b023167943fb36cdbb9d43da04672d767439348929524c4d61d23b7f8dabc7ec12717fa8f00ecc7814f032ec0e212f1a8e27fa93693888a28e8576bb7bd166d25d0457e8591a2492f43f7ffb07930ebd1f6c230765ee60d56647a418912da8f01a64e140ec61b802f244d14b0c5d39c5dc12b1794f1d77522c9aecfaa0869d5e9abc73609d1c5c3fa6b2b30b0f4bf847758ea863e0fa60b2c9060e50569d1f7286cf6db075e909e78f2822be03362a80582d4724b807cfab79961169c49949ed10dca63b5dc7908124bd05530b793bfde27b1df66bdc52ebb7e2026ace625bc75388204fb84ab4daacb4c7200f69619d7bb5603a6efc876034b45587eef78ba31fa24ed9d1746ad9b0f2cf200c417c255d56bd033c40559e94ad65d9e18acc018422645a5a7015f906d237f30d04fd9ba17529436a5f154b70401cf6c38adccae817bffd871a9ef8b7ba625b0fe27c667ff8ad44c089c651beaf2f370b9f73ae8f2dde54e13dcf7a21a79a460b8a5429f7ea56e7d732e67f9a09899abdaa59c0198d13cf9d42a148ddf6790c049f5e3795931e3fb1bf3af3a37a42f8c5d549e9d42d40a339a8529824a354b20a91748d89869bc17b771399308f4a79ac8f2c0a298de4322f66ab9e3df9508605a2ee1e9ffbfae2e8af7699d1a53735b4db69b46f02921ef3da685bb3f49e1b0632895133593f38eeaded5fcfc5031dd94dfbf73a1197c9f6f90f0943cab81b0535084faa614d897e9312c3f16ae14632fbd34d43405c102881c54a9b82606609ab8426b10427e90472d470c54fcfab63b0c43d64752d24b152564eb60686180723b58f23ca2c741e1406c8474d96735d06c9a6a1e254959808d622cdfa0a670ea1b27414e9ea6643db2ea44cd88799b12528005cd5515c341c42372fc76f690f12e7d112f51ad46f42e47e9d5f99fd5be9e55bc9b6d7441b6339b606ed24ac005d595a88c160e1f84298a954afd459170d9bcd891d5b8fc2a01da2677ed72e0bffd98cdf94ba0e835c16e3ad9ecae4e60667a31e69aa924bd3f0f74a6cc03a06e31df32ff828c74017b4558621fd9099bd6c5c5c88bafce2b3ce81d391220c0ef082bbb3ab6eab685ecf203aae7c896f17cc72c7b2bd6c0fc99acb0408b1330f073e7a32f19f8bce47ea048094033b046fe8b0c5080b3d7b8734b624b3f09204b3201d25a400c4d968ee4ee19793a25dfbad959ab6509410ab0d11bfad043c046db074cd3aa60b98183e494aabecc5010add02a90c066a1528e605508ad1310cf2a917e83b917c8faa273e057b3efadb4d46ad63c81d32312c0808efced5920d43e13feffd3992336f6b32dce74ab0c0faf6782000216aea4f97a4a4307f8e026a76e2625fd021fe4c3c20e0e1bac854c6bb745de108de67439679edc5c4dd0b8347228ba1a0020966fe0973116eef6ba4eb27581bffeb861eddc59231f36f0e749dbf2c05a4703edc391c3b9356d6154736d860fb9db5b30ca5527f9800700d449abb1b9bc61ce5ff54bbbe8636080cbe7550efb60ac303a26407805c331c02 false -check_ring_signature f8b142753a3d5b49aed5c8993b17c302abf0c12db4beabc5c5ffa0a02af302d4 9e70195c0647dad2eb67dfa5154d3e71c636d8519ab525b6461d759b78242ce7 28 50ba24c12fa157bf3ff4ebf548335527acd1490ebf5fbb18664c8c5111865512 bfb31e42d8cc4d51945ecb68c78219c21fe3e717f779ada009dbffa7c190fdd5 6963290091a523e9ad75fce4f15f14580eebe8c46e433742e0461fe3540d6c98 b45d2615a0c5c6d21a4c667bb12cd01ff165c5d16fd1151f7476da5c306b7dac 423fc777cf6c6e2abafa8676d2981321c395c8d63591b51abdfad62e2fe6121e cc589256ad104b460c64e392010546e12b766014fb39ba29ba32003a10aceae2 d0f1f634eba470384991ede1add67e14abfa0db7430e117eb74b99b625425095 613acadb0381ab7501a997f63942bda855e8a50fef8782b35e7d9801c65e28c1 0a23ff82008f842c96a8ab5b21e6c5d95a07387150af33d8da98757ad4fc3865 17be4d1258c6229afecc50403c623e27a369607e84f1f8cfb4f95fda6bdd01ff 013ca1122ec4180dc5417226f5beab736abb6344bd6660f1fbcf1063871ab2bd 4acae18b0c2c245907a11bb5d43598ee81d9fc232a537d578c49f5ea36a63d31 9777c10cd138fab35bf0ef48d44ec253af18da667085f225e266120d87a94dfa eff38b06ae5c7862e172af0b2bbd76f7b96758438cc1e3e71c8a78e3224f0599 b9e1345ac1339b274873c919d723e27f8322a0ca4afd2329a69f2a5b10091734 7b78e2e5444747615abfceb81aab252ba1f76a57b19e71f0f6b8dc03249aaef3 2496bf91c576f559444992f47db6c5778a782730ec81318fdbcffc3a79ac399e a6278a6b1eda10f2e11e7d7da7c193439067725edd243de5fc1142a8f9d38f86 3c0b6ecfd2c31a6ca45b9b2590b614fa3a47447f449495b6ca7f267f8f76439a 47b4bb4f337bf9692519d24aabec70a1640ab9abdd18f38badb1c2b0696ab834 3a6dcdf6453be7b1a77b20b2d304a1aeeaa1456ef58df973066c963cf6aaaeab 02b1ebf128ef9f10282409b780f6e31d2112921e22f2f2a9d5525c44902b766c 8ef08f82eea54369e24d5c1ecf070c39286ca765ac50526407d71f546d2f14d5 5e5651d2f7808ce083cb1fca6f034180cdf466b74bcbb65ae357c17a97def622 f996709559e2b5302e30672e868324698d3034718d350d03f3cd44803a8fede1 be83ffaebd8541341031894a49a4e00978de87d9e3ad6c1513363034ba364209 68c725a84e672cbbe9c1e8b50ee1f3c5b082d49935bca9d5179b3aea75b71e36 4ac6a5248e9a10a37938a3e64b298f8cb88174ee31c03cfb6231df6e2ad1b423 adec8ed23ca1d36df9a6423df42becfcaefcdea5b94af1a9c1ee6afcbc31f4019009b735ea18d9c24c58ad7e90878fe50ccab1c63e2b156679abc5de41608c00bb851a13cbea605909e3260bdaf48f8b421b7f6e8f19303ac2403cb1d864810fa749fd81e4fcf405b521d4bb95a959af5574a1f14a42aaedef69d2969360320cea9b16bcfb5a55093e635c85f0d5f61f1ed36edb497879077cd3a295b542da0fd41406a9cc930024c47715d851878e5024461dbd77a776d71023ce54e16b2205b9d6de71a8b93387c4aa02e8657211cbfd3b5a532c11d277cf18e1c5275fdf090ee9a89808fafa7d82eef894179685115bc872bc437c6e1bcf28288111024c0255f6d5a9578e981f838b2d3e91c24933c2cc02fd9924e6e9a7aa33a129b00904a0373453ee8fbe7ccfe07d2ba24ebac9d36b2765a51ba489b6f78ce6330e900ef08d825e35c9805e6dc069b0a9720531e3a2dcec41e1e4db874a47507d6e330860ec10b70e260f077715c10d08f08d5d7d5f197688c1675e744356f683a17802027fc99840ff6e094bddf6ddaffd9e65f0cb02cd32896723853ee74d7c2d2d08df99938766d0622ec05b51fe9b9e3121dba043febce577812d03386f8569220261bfb675de1016a159b9c635d3e2008f065a8122c56014916d919a2016df2208841c3601b18fe4c930be7a35c37b302878c5bceabb47e5c8bd69211d67918607112ca0ac329deee10a7f729e1fd83f8c16c2190b21d037010d0b623ffd04de0963a156d219f8a1afd76d226ba79e6bd8c3073adb16e44d02ff7d05aa66f9200785a61933ad5da0ad84cf58a93e8c1dd1863e3b42d56abde74c61a74ad40e360646408715561cb2fe3752d52dbb84ca6e628d4a9e93cb02e2db181952cd672903eef4d1ebcbbdf61ae407cc56204d2e47c99a35836efbdd1ff16d89904f406700326a59979af7b709cdf928ced23a05471689f9032964ea1a94bdb97c396d860e79143b47782b8d9476c6d087dc326d3ea5b60407a9870da512752df38b37d10f13c67839aca52246b7fcf598f84fbde10d6528a3142fea32d739bbee01dc3c0f2bc1dfd7bb3e11db1ba25a6c660b3247cbe93d3ba73f5b664dd78ebaf2728707b8d046bc7fd7c28c435019bd6d61753b610b7fecdea1db4c26f07587c12e9406da450a1191b959f6c55d0beb95a2585c616d1e4ba8c6b5e584b9b1dcbf3359014c6cd8abd86b077076c29d6f72b5e58d1867adb29b0a0f358f9c7f1c5889890267f5d3518960247b82a3f9c9ef8c44ddad6c1579df32f1b7129232859bc40f0efd799caa571da8159fff2956feee6b57b1fb8f47dba2ba0dab939500023e6f08635c85f995d8495534d987c347b278806a40aaa4527f086c27074916497ca206bcf110652fa3900e8642b1dc4ceaa29c7f8b9886406bbd4883d5edfb53013c034eeed1ba5e208e4356250d27a6759794aaf7c0bd0172b042b6300e0b802477039d25d8493b7f63d43cb49af7d5a174400b4ea7c6655deef7c016269c0ae50a031f7c93052432ae2dcc26d79b6983e515f977b0caf58b1df3bf322ff4c7d82e0b6f35127bd043a9efb208d5bb95a929428025ea6a46f2d2a119b56d18cf4c470918c2603d7a9061e8c43530ce4ab3356fe5061cb69682289479c7dc00bede120f47db7959e87d024ca535c7570c4e3cf366a4df77657279534dfaa32265562a0349669c64b169dad3d4e01cb8650d9cbced72556d0286e70452364df9525a830b36f6a69e7dfc1c21ab588816b50d1f2e41517d6fa88ce23cef194784cabc920efc76f5a4d85c15c816f680aa05a0282f01e153457995d8322b5d8aad5c109e0c2991038a1db467bd93438eb7746e387f78428365d2dd07398379707531bcb40cb010d5d8e143a522c8622dc94a3d63d12084048affaea8dd45e48bddd0987b0a50bd9de1a1d36c39c34be993c3ec843abfbc3c2b18a90695d8508a2e10880106d02ac4c7ee844305ab79aafb89b7dcfda6a30a873bdc1b1309a89ea5ce7d730243e252445b56b2d582dea03f6c2b03a8cef599b84fba2c96aedb19c52c79ab05ffbbf6323c4c9df8c9084283d0067979742d38c3c7e1749579e4cc12e9a05708a18745bc7149ccee41e90bed8ac61bcd8a18177d695a2dc5038aa35e68898803ef2b92bf0ea1b896484b05204b7ba81dfb4f1bad33b04bd9c39ed710e5d2b803ec64d02d9e049889b05f32d8c05ff8d38b461d7dcf7e895fa71c032e6f09bb003177fad56fbabaee0f477f6b0b2fda681b53104505005157ab538c00fcf4a506f0ac95b1c56ff6e3705328a5a0a8ae0525947a280e8a3cbb76d502816b37340bbd73315187399fb9a750f2043fb161adcb97191571c97821e74498ebbeb82f068151d5a11c30241d8f2b3c1a24179d550bb4a93c384e4a646c0a513005c9470141799fa1e40fe8183be7b05c99bb2f240699724f23efe66a312de0e06735c70c4bbe192bd82aa381218ba0ad3f08ebd0c862181a911eb4afa2965b237bd1460b true -check_ring_signature 8b722c3a58c48d947dc39388cca3c68cad4fe3025f461e8fab672e3b8e2fd3c9 8c64b2ede8b325fce26999fff0a5a76cc6ffb7ad86c9ff21b9f0d5d4fbbe207c 2 d77670e961abb39449c7cf085f2b4034f0ac4f8790693415fce8546f452b702d 6fbe0dbff38a19328949a64c6ca14db395fb715d60cdc24381431be5fb123fa9 65317c57c250d52c7709efb50b59ba8d89e209f91c24b8e0c72c88d81daad305bd06a9f7b3941c7ecd7b653e03247f9e27fa9046b1a3ce63f4f75fbe12465a0990bea408bd246b32da427bd33cca15327fdf0fb503d3bfd93c99ae03a8254b0e9473c5948685efc155bb7a281cd22d5fd15cdc87ade916faec6400ce567a2f0c false -check_ring_signature be76c0587dff567bb2702410f9c08d2fe1993616e5972ff2cba9d648b9ac751d b27ea994690e3cbca794acfc79c0915cfac1e5a7d2e841292b1313cb754797b3 1 6e804d99dbe0976de2d353c912005563ea9f3a8f527bb46c8c44693d47b8d8d7 1b2b2f910477d76ebd28c79478b2a5844bd080a8a9bf8bff912a76982929750051290729749779b30811c5a05f19d69915f2831d636676bb13e5e715a0d14b06 false -check_ring_signature eb27bc8f46d2af820c8ea604301c3d383af6f512d8713743807f4ade81a99665 148c330d4047f84b948f7e0f1e837513e55d4ebd86ec1cdb1fb60160ee4bac2c 32 015220c5a3b6f7430178d8a4dcbd62e39120a301e400da5126b6a7d057051c19 710f81dfc8f62d85e8646a65bb8fb4cd1f36b665d91a6ecd8fa58937c2726738 4e8a492c8a0f9f5126205e01bb46bfa5db49fb0c58115203540fdabaf7e6df35 7cd33a8b2b600d1031b8e534a554f5ca7727960fa8c8e5a363d1d6f20cb8d5ea de22cd8bee081e72cd5a39ca44e8bfafbd5aaa3e1e14240a28d91eb3c5da8088 44eb3833a4d74454afcd52f905c173085581d11b2b4a03eebc7e02bd14f2ec3d f30a31bd71adb956e5932c026a78a28a9e976efd916b05c8e7b1c83d0256119b 47456afd89180c28417102b8d07995fa8e5e38b995f3346956be3df855ff8797 f42bbd33b9385bbc6bfe584bdf91dafe4c0d11e0d451c05b2a901010d7ea1edb 0da8ff568a75e3a528751c0d601d1ba11f62250a20fad8992f12a053023c36a8 70c8da32c23b91642e77fcefe274cd8597dbccaabb80056778c407c0a9dc6719 2ad1434c035f9a68203f697c3244aaa3e9494cb6d919fc57ee0706039fa28065 3e7f66858b00e776483d5c155702fee00c2c28afa36134c34cf7acb672cc5507 ac8bfcf85673304d04db00ee8600c001b2604953e1abca94c4e6cb3472610de0 6078e256d9f1f279f82fd272d0653a0b38a9c3d3aff326f501c17ae04c7a50ba efbec693e8922cfbe63a4f6feda118134e9c6718bc216531664b0a496c9783ce b96d4859cf802237ee57cc2e80471a9f43c488c2f64b5ee7e5ee846459a9ebe8 89f8b1b111634005c77c39bd2785880cff07ad2420a74482f073a3aacedb8903 e689040bf2b7c60ecbc6551d84d0fa0bea2f7d3949adc51548d8a31b428c9c6a 64748b6d3421df3ed5e07b50f8d9f8de405d74a5593fb8f67ff68a9f3c33fccb fc8749683b6e76bf4772ed55576b903e46d62ebbc7de2edfd3513cf279167417 d93bc3cba05c1e1c2065094439d8b2e1df01652e4c69ab224bbdc01bc607d8f6 585287ebf11abcfc668088f638ca773215d9402ecce36a85c2ca5c9de28ff363 3aabec5eb5396c0d23ff6fd91016aa0daec249054518538523b9519a3bce4ef8 84b590cc2f501f105bdf1d028a24ad9700e1ca76492c697f69935101c520aa61 50078e220f24b74840bacdc9bbf9e0e6affe098050cc6babeba78e27c8417cd5 ffa103f27d66e84e4ce9d5c225ba1e3fe09ed1d968bbb4e18744e734e7ac3426 194edd6982e49941aaa5bc8c58f2379ad246f8dc073444c7c3972746549a3657 2f115fefc22c0c79c350f96ffd49c8ef154dfb3cec8a2863717a36129022a8fa e87c35277ad0f04e5a94f53db3c1454f1c45460c96db7f86a2aabca5bb16797d e6a58792c33c87dcaea576778d47d9bd671d9be4dfc7b5a88bb28863f0f7607a 0218b58976f755e7848daf1d3fe9875312427b57c1eb90264ba9955c6032de6e 02fbbad636c9253bf888a69695e4ab65bd71d3d6f5c1f4db6896420b3d7ed9091d8eca481ba8b5443f2dc61d02477a240cfc62af700b5ceb9b5ef02c6c110b0db66753930a5b452d25ade4418b863fc992c53b68b599af7a835363aa4233a20ab7b032c8726cc10a71297022e37edc0f0a6736d573f455eb548683712d82130e4e95258e41c8289d3c8c00879cc09933c9080364c847b754bee6f3fb21b8f00a8a56992240147d86fdadcb62ffb5a172b6084ec71712762493e84a0fbc8cbb04fe9329842b61592dbcbffaad0d92a77313649a7caa4fedae1537a34adebac7000239e3f00001da042c84fd93865e83d5c65f51d529d2aa303e890e9e5fbcd10334dd90da6bdd64ba48f14cfcc7eda4ea6ed93edb0f77394c8330864507b68d082e40fbd842d8320513c224e9c26ca295436b249209741f0aef8be322ec15e70e298da4e3f66e3cae88f3785944c3371b5b82291a5eb2d562254fd7c6d88ede0099ba2364f190ee2e680d2afb14d3a2f6b2de5f98994c6a445654fd8cd549140f92d5a0f65ec02bcf61b5610c155ac14360acd4ee307dbb6e0c190aedc8eb3d0f37667a2b0d568989ba584ba8bb05fa978fcfa4f1aec966faab1bdc2d044d5b0f712d3bfacf41eee604273f76b548304ee847abdeb56d3777da1cacbf59c48704880da918cd73a34de9f34b5f951be645ae84ec508201483de32a867042a45708dae8c730944354294d1b3fd297b712a44a9bb4f325485362676509c22d54c803de2bdc0ab3cf0f9d8f7b58a7f73a06ff1f9b6267c9a5030103ca044bfc96da0c0d960d2dd85841959d24976cd5c90f2379343b9a905a43208fac9194d9a85305fb74591df9c9406c98dbdeb32c98976c196ac58e5b043d32ecf8ca666c8db30fb390cba8e620a20a251b11440bfe23baf05aa697c0c5fbf9a3e1e2a3f5623304d3e1dc52a7bc3e3028f822491cc3d0324fe5086d2e44d611d8cb22d9b84fa800f5054c5e082521e7378d2877b004e905bf34f5ec32a4eb37008580d8245eeb0d3df5d68f711de8f91810dafd4a064016dbf2ad721200b2e2076bed0d1370bf00c6a0936537d93c6bfa0f944934945f7a6bfcab7830e7674f07ce437b62463800f374561f2e8c78b91ead1c8ba740bc749d51f343b256bff2743c97d756be7c03c360c48aaf2f20f04179345bd1957fb7d315502a9b7c3e71eaabc045cd24c306455f09a3c9644ff083031ac1ac526d5cf48df2e0260ba9a89e0a86f6ec5cc40a07a77a934a68ea8679200f93b7380bf28e8004eaab0c327746fac25421ebcb078d18db0206a8fe8bdd8b71b45f7d13e105ce4754a7537c2dfde5b259f25ce702f921415c7e54cf0d87e17fa4ff9f2afa799523e1ff81c886e166ef7e11038b0d400994e35e624d1e1ed07b2a5cb849493098b64b78db52390f27247a78d8fe0963a3b0767e815ea95353e508472df58e6f4c9bcaa16e045cb480a5ef899cbb04549d2dfb42dc99b159efc9da87bcb146282727081e0a862b87d00091180323068d21bd75ee8ad8583f8db4351fcfe8500b32d2c9996040126275cb632b5484025c9df8bb0e467a609045c70514b9d61a790077eb2d8c0de95a5828089c797f044338935b4435c3ec015aac332745f1ce20612f52071fbca5fb0a14b98c62a9076cf6eadc1107e0e96eb0558bbe600a96dc7bd3cd8825696edfb3e454c428460decaae738847bcf23d24b9f8488fe754c2fd396140596135ad003e9020e24e30c2561b79cff185fb20adc50d9fa85f8faf5b7f473e29793666f57f61ddcf67e091a1203f00b41af36745d4d99efa1265e4109554d3a21b6688ce7ad9b1f41ca04023c7c55a38f717fa966413d540a0762c7c26af18b814126cf670a9c278ad6033f36de92ef28ce21a14573a3314f357e17ad096a3cc6050b97bc2431a48bb409828ac3acebbc562dbb4da89d529b2a2e43647cac95ade2945d7ff2cbc45daf0de1d07f5e9f634a40f7269136e971e8f382dbb019d53747fa65bf59a38b75290d24fffa64dde24ff5425d39d4c2bb6280572b0e2bc74dfa4d999b842edfdd910c312bb31792239906c50e8dd7de0b59e828578f8e14dadde7395d2722ab83350ece30afa3bb25260889c14c02e445c536b0127410e6100126418eebfbfbfe300af3cf7cac1539ab4e77b95759ddc53d781cfd5046b7ba0fe12c57b4bb12e0bf08c1f69b3306a4dc15ad0e52b5746ed3229b5286adc942e2a56783159feceb090bfb54b12d648b625c16c57cd46c81290478b8021929870a802db3495f423d350f5c383ea6e82904f6f3d34a65cea11397af30f53fa2fdf719da9a7ef539fb7808201cae2ed65fe66b5b6f090a8671245a0cdbb599d9bfa034651dfe8d37e405037ef3bb6e8bd8da048656e7ee7a36b4642342ba2c40afce0e1711e036f10e04022d6922b3d9773908773abb12f56013d42a7fff018500112f1f2951223934ad0b20a3f7f74bbab76b45ad8b4542c8f057a643e20fd8368edb34c8ff0d02dc3a09c50114bc3f347c7febe1514a2168b810f9865e88084c6d5c23b89726172c7808f172848a21127e4e3fff156ee73fbad5077bc5809c18401c0c7974723b9b6f07dd26eb45e80164017155c032adb913868167a0d96dc7d824e30bda2122489705b062458dc16f65e3aa1df3537277c2ddec09a3175b812e0784e37657d9b4fa05d88a16c9f8ca7c201ac929aeee859dbd704f979dab17812f8a65264ab4faf701ec47b0d1fea1619f4e35f397c68b49bb424d86f01328f98905c66d0e19027c0ab3fedfe545ba916f8d49562b67930e7098d2211b73eec579772dd48b891807049319d83173f4261b3b6584842b2eb193fcb95ccfd92d569adfceb263fc50240e true -check_ring_signature 48a9080acb7ed93ff6ba36a40ba7edec49888373ac50388cfa72a349c16c1f14 64edd70264d5b57464bb39d47ac8a2d695e239e536c40ba17a09d313f6e4a462 256 ae6c7b47ce6a743e686cc7636c94cbb9cf2b880b98bef795ac57bff1d2e2f732 f3704a21c37c3e006810c3b28e5d75ae1da4e4b6508145e6bcbb9fe1b211832f ca3449bd6618066b11c42338442d5493c28368e759274e44da421c93153dcd41 c99eed7a469e1306789b5e15379df011a3efc413428083613c46915379f80a04 506fbefa2d80d37844f76f649cbcc0c521d7eae13fb2ef2085f52f8e0e819679 23391e4079549a2eea6ff3ed2893cfec272b87316084169062e37306629cf68b 3c35c8808787d0fbe4017326345a57a80bc95a3da90bb476d377cc6834cd8517 86dd1b2d943dfb6358e8e14b9f9a3aca56e67fdb1e2e3ca3d1887d3409be5bc0 4c8a2925c9e0c1fdae13d9f9861417e2b75ae9b1bcf9cb78e6f02b91c858ac43 50ae86faebbeb15c36f343b6d36e24dde94b2dd6c6c58f0a5924a0ebc7ad482d 4a99767aa4a638ff1c201bc98abe1a2fa13b4d1ce7fc5c232834ca2136bc408b a60e3c9410830fa4e7b11511daf69c6f9453589b3df9930bf6be5c2edfa63af5 d4a0c809e57b5e7dcf2326e28e9d3143b98aac9112f73dc40dc38e702f30b9f3 78812a0ec8b1bfde53ecc3b0e371557f2457a45648c16dcd7c8f5b0b2eefbd20 fb9c6602f377fabd18dce6394e48ad90c2d69aa27fe087a1499fbbe713a432ff 12c8cca4b9b119a54182796abfd8715814c8137ae912b8a13e3efb2a39f426e7 75b787cea70161c9f8714078357c2a45ff47496fb404115a3fcd0ff43b5425f8 cef99a001ac0641ac5a81e5f67aa97842e0a2d2bd8bd8636008c9374ca61a53c 4f02867da61e0dfcac81e449c6d227ccbf052244552e98f276cec1a63cb6a672 60f7d0db5f6f83b459d467e355a8d9f3219b1e2f2639749d5258866b0c485a32 de8695354e9b3609de0b6a6ae17d37027f86169f803010fdd9737a232e714a51 b60f6c4d090e08447ce14123dee4c42510019057b68d30f4e9b2fc81127153c7 93c9eba7a0ef9a7b2d5feffe18cdb1d4537b63206daf08965e4e0795c002ddb1 aa070d325425dc3617fdfb08f2a8024e3e4baa48260ea9167bf6b6e6a0dd9a98 77d8873697744c2400fc8fa166396abcb1510d5f4d2891b3ae0fe598fd7845ca f91fe422be89bd2e36c8009d71ff1665274b11825321d7f58b361c7bc53c767c 7f45b21936c30e663e9bfe27b1de5ceaf226f4677b2aa72cada5b0a3e3900e74 b1bd986dbfa9e30e33b28be6e2310ba2dbc00be9ec04c284f94fcfae3dfa6f1c f723aebdf9cc4eaca4d44fcf7ec65818c6036bf8f26cb4d420c5fef39b018cfa d88a0cb13b26fe18c17067715410605016cd1c6f1890603bbad269e3f8474537 50dd083f8d52f7f50ee75d08ea7446054fa8268899b122e2aae04fdffe352111 339081351a1dbfee92a63cf245b59cecbaeb6e22b358c4f302d3e8b52c89ee1b 699cdf9efff580fd9bbe60ae9b43f85291e5c1e5021fb7fda64985fade6be278 8ae5b9711f18ca80073dc981a044f9e84d288f06304c709eac795b8d942d1865 26b28b90faab018beb632dd84fe679787a9b21db67f9e32f2d3562b6d5e98dcd 86de913b4b334c990726aabe51ee29f4ce2c0e01b74e51dc837b43af115fbf48 165e7e153bd5a37e253c477163b9b968df09ea97a2fa3f8c7cf282f8b79fe5f4 ad66f26cf0169482bb43c68ae9a946a81198f64fb6a3c3b5a58bfb285fc3af18 4d38ed732bb34ae01b67a5399831accc7eeb78c0dbe0a987956b3e6b72a360b1 7f4789af279f533c6435a1c8b753c299a4e30458444ba857d5e1c1ca8e6b909e 7ae0836545c136e227ba9c28fcaa2f63f416ad1bf251a612d277134d9d5fd9fb c365ff2c608f4e2b654888b9fbcddf9e31e764de168a72fcaf0e520b0172bc18 d8319e48c1656790e3dd61e37a322d969b445d73ebbc726875d99383d5127daa afd8081e21337a990d126c493b13677d481668832b45a528dbafbea9e79c1670 86d1deb1c89d02d2585603ecdbd705ddeccfb469519f6830a4fa83a5a7d8de0d e3b05286ae595a40fab9a8022e06220d54133276c299b346eaf4a0a81799af16 d1f3e445f288f93b8dd5824c8661e4d0acb0786ff87cf02b01df2ccc3d192efb e3f0998b17380171ee7c4b0bf41da87aed6c09ff94f3c060c2fc82ad657bab2b 9bfd98dc3680604bf5693166635487b06eb8d793ed8a9bd3f46bf1bba3ccb32a 261ba5a61858c0b1f5e682ce92fc7c27f41a0174b5220c459dff24524bf41a70 3bc43963291eb29ec523f80661aa539f0449097ac088d251d6f19a92e444fe06 fc0707370c2a793b6cd16f3e6845af695be91523f12013035f832e4dfa22225c 825f6ae66d553fd0c6e2a2d9b8edf2a0d0d114cc39d19a285f004abe9895312a eb2fefbbde2b6e305b711fe90a05593c706d69ab00a2fd7a631be6c65837d34a 6c7cc6ff3421639349338fb83840e119d86170a1a8d402ed0749ce317da92369 1e9a97cabd98bda4b227066899cd3882994703c8d57eb57396b70f7adbec979d b1a32d911648b8714a39effef66615f76ae9cf26a9f9cb03021f27afac616b40 b038ba1fac0ba17250b07442b0a410399820c9e2b8e315d5b08bfc8a80328848 c0a1e953663a0f6ec77948bd8648663d0c73195adab5ed827310415bb77aeb1a 6a104c3a0049971b486c203816407beb95639ba571e7b9e0e302b91295be8493 04bf4b7c48abd37ff095254fcad585e3ee4d0c91b17e69e32066bbec621f32b6 48750742a8149a695ae74b747f507a638edfb658e0e2119d62942b769ea6f13b 3c444d032b3f16c05a16000750a32056bffbc5fe539215d0215bc278c164285c cf577c19f362cbab57ea9a8c10d26c219ad819f7a314123f90b819d2d33ea980 f9e5c6d23b1eb936e1b95c3713fcf111ceaa52e167ef9bd50cb094b554cc282a 33666ddfaa878ac211156557fd2a03fe849f890f20bf03e44da38286d31b381b 131ccaa51765b9dd0cd7ee1b2b52a6d5619acec8208ea24e667e2f22eae5db4a e0a664832b4739fb49232561489dfafa9468f28795f3a6e3a9c399e4871acdf8 55b8b7361c5dd9b1ba187577066b92f786be0a1307a82e0f587080fd08a4c7da e02c764d8ec14cb371ad6db38773f59e0e749d64e4bf263b3ce82ccf2cc04359 fa3bb7a3a7066e7d12beba3ef26ede758bb80edec4d329e496acea884f470f4b 9f051c8a1bcdb7d40a55a467e35d55d1871213e93809d53146cfc80b6b84d2b4 fabc7b45032947361fa64eb11e1f0540092c93ebb2612bbae1207ae705cef3cf 1d720392856133c6ac0f6afe7614797d0db7bf2198c3ec0b26ccf1504ec72a3e 38fee0eb8d01d5d5bf76ea4f11b86cce4d571edbe47f07985c7f0e4d2b289d39 e22fa963fb752e1357cd1df4703bcb7a51f8479a4cabc84142a8c9bc7fcdd627 fd2414f8db4b5c72955272a35301057a61afa0b2d4b3c16a817b1fe1c554715e 0aa218370dded31b6b1a1284a43ba966e6ea2f11f142e79cde6bb919ad7b52d9 92c95be07105791b4e13e8943824d26466e89358e6b0e58f130121aab2c56d66 c003814354d402795bd9d8d091913c3a1ac6210c8d242b6c694bcb602d387bfc fe38c6ee6b89e5b261415a662633139695f9dec780ec17ebd7ef4a8b0d202677 0da116a7b64c942e26da2abdb7d400f63bc05728da972aa4d1f3c4681a4d39a5 3a46b74c7d5f91a7eeb5712de881af51530161425ce74a9ad849932efea113df 13bb3d42d32bc61da7e0645768da0a0491d7729e22801acbe81b6143b35e73ed 75ef5475eed7826e5fdc631ff2a18358c514ce22c0702ab7c429c32f7e5c868e 99eea8291e5de41377a7e694ebfc62ffc9e681e98dd024f085b0400be723eb67 6a46680f537166a3f103689bcab3f1b7dfb272ffdd17faeab498681c12ca963d e65ca6c6eaff19895bff6fd35d2218430920ab2144178e850bb5978579c6a882 8d007085c9d1158ea766a0970e9754248a9955848fb8b9f45c8d01d8dca5cab9 f8f7f641a5dee7ae50d6d3c596b2047e3d4b5629dbc331febfff18eceb8a506a 66910c5b6cafa901dc146367d6d5a5d72c7795c58a78829b5678d0d88bbb7004 4c9e78a674890d869e6f3b453006a6a06f39b26dc3d17e494511ce8de4096727 66884f41c8728935c84a345f9b20c1227b147b68f17ae98a551dab74881a6a36 5317c53e0b743373eaefc98b911ec9ab712def730ae88da0de49e0c8cb07b762 436f777f23bcf7d060b40b84377519c7528542a7ec37c4d75da9c348a13feec5 cf81275ed332bb601a58ba2c333e2a06ac09481c7a09f441a8f8dc73ce0d5648 012d1df598c7050578c440039d2bfc95f2b5e38739595318eaaf60c352b0caee 186edfa14e8468337bcd89a4970e66f3213b3b4f3f2355182fb3d42abc1e7513 1a25b4b0c90aeecf3c161273e2d168827c27723444f22c997cc292c97e84ccc3 5ef5e41683bd0c617f2d07abfae7f1ed74eda4731c7da061f4a6dabd2ab69cc1 d14d09046d8b7fecad47cdca8411fa9697499cfc476fc2ace068f87767a1c064 20477cbfd2532d7382d1b80ad9c4cd740b02d4cd49024c9d8d39c41a46e9b8c8 ff620609e25425bb4d6ff359eb37f3d0269274b261e6f3e1b0cce6b8ad85aa13 1b28fee79b842829d80170752739a355424c0ae2935ceb9f123ffec73280e970 1044ce9c622c990924325bae9cea4b92b2d3d3b705900c4f1e574d82f5505e15 7fcd90fe1ab7d2a5ca4aaa7dda34117f833b4943ce5a6d4fef553cae627b54f8 2acd674202126c69f78f4878b752b748fa14300105246acb4a76ba1f235b54f3 b4170ee82970e90f5f1559f85cace4e0d51a0873c336a7924080788a28c7b97e a6928d84fc62a9b0c5654317730c31c2aa1a3b0400fd8a3bacca69f810700f53 e40aa51cba6ee754beece43eef1e0f526e8a383230ae737c10895581d50314d5 3ee21334338a1637d3be3820f8dd742b8685d21e885a34a257dcce4f6b883e96 04cab10923cfbb9b9878ed44eb3a96c256a7e385af5fec34180aa8df80fb4a82 2a10c30cfa986ac59d6de4f1a81605dd0e8df02a2c8bb26a8111c738d69e1e15 0aca6e0074813d50ef8c5289d272e39d74a2d2619062fdff4f5ec5b628ecc3ae 32deaba752f9dc9c51885b21047db057befd9765567083eb485b4ad1b4592fe2 55d46400347d4be93cc5207857178a3d2ff28398b5c8df5d7af8115f90cd6f7e 2137776e14ed3a4f7513883b859923db35d2fc01ff83b426d37aadfc2a809a1e c40a1685b9b9a1b67b4ccb4a64cd83347e8caa1bd0ecd4e6e3ea45bd5d645051 de3f207eb7d5d883626fef31bb99d808fd8ee20a7189998586b9ea9c287c2b7f d5c2a964379c8bcb6f06ef0b9623f535d0d958727ae375c5714fb691bb69af9e 1749b1f4ce10d2d654a34f588422062549f4c353519f82e61175d193ae0881a3 c6895e6ec82c39a00e12be748c36cfecdb03313baa7f746554b8db2850a7a4d8 c1a8de4d1e127e2fca66ebb58275a5262b05a759fffc8648dea83858959aa799 9bd0888e430bc2f09b15594f947e02fb38b94b7dc18d728cfc5bd0a7d4f387a1 1256961f771efefdc504fe09d58bf7720c9d2dea45bef969a3258a3f5989d2a6 3e88c13ecd7216f8fef0724986fd17f2e6a77bc17c6b6d67c447b9e0ca157b5f d24cad5458e452e42506c6d8f4d12bd01cb718d1401a5783e263854dbc59108d 1ecc43c3bc2b4c0959388a1c9c0ee9cda74f11bef13bf3ebb4b22ac104026a25 91253e69b36b2293c484d8259bbc2c9ecd67b60e11a581576881c37c351d9803 8cdae2823e2dcd4248b4809196e90abc1dd1934052536a5a490047a92efeb87c 9870e344da88351566213066158f7a8556f4e55473f258ba32dd09ca77164eab e3cfdef9034f62e5794ccbe5ee51d7611ea54fe9b508b0e4504e4038e69bfc8c ba05cb96a730267cf6100383fe1602d689a72efaf0cfaf1db6196a7cb4d4245e 795dac3c9050fd439f17a5fc9254257fa4e42ccb0253f8aba2089b947dfc9c4f 4ad8642cf165c0746ea5c3b16c03e7d67f7c58ef25a533aec1c30e0cb481b18a 072fe5b8ceb127887291f42f8a6dceabf53bb319b387c42b7d8418f8940334f8 d6dad6028ccfbe4c0c82c34ae1acba46db0a31af7a926dfc75244ef2a777072a 2982f0f88e3a5f1b96fd303c2bc16b23c0ae6e17de929b1641999497e0e72c1c 1691b4e98ec9898b8bd66f72d3870d71125287ed6759f2a7df134f3f43ba4ddb 7f7c3ba3d0b270548aedf97c09fa30bda09f531be7181b7626ab76b1d5018952 1e13823ea64fff21d3ff8d058dbf3325ab3d68d8e736c8ca625999e5e042ffb8 8a44e03c41f7df101642fc3cb9e91237c2d0b2c3f7b72cc986b59a1eb366dbde 133464b5fcad4a2f72945d1a7e1e9977b15ec77569fc08c275e777d36a09bc1e dfc060f68450fc3a9d6f812b3a8b68901c474a90c4a3bed89e660950e4fe63b0 c0bac4e4dc52940c514807b15d56c82e44ccc13c81c36c4ee20df73dae8bde67 fd63921708789cc140d6daa4b220bb52135ed39d9f9b064014adb098a620fb84 81c46ccdf231bba42e60b6a4b55cf8b3ecbc28149a86e8f6cc858e06b7832d23 d65d87db73deccd9670bea13b3ae30cac36a3e6186e8529cc11f61a503318898 8bbaf8f0f68d2e56b0f028830d01434f99db8edac98fe9915250e0000876fe24 a8e630a1405c2b18403f11fc3625108d8011072a326f2e057b8e36a9df9b5cd0 01f4af440ac9846ed9e4713e9db3d9b5a570801ae078664d9cb6cbe68960770b 5f487a273ef2334eadbeca483a4b30b78f59f245bbff7d197537f7ea2d714b22 db8474cb9df69bcb26297adca2a642e5279a607d225260c4d6c430240b9421f1 a5ebb6d7b8f7caf0dd14776589c6b1dd16606f4812a709b599c9b10fda93de73 5668cede82f5de291e9d8f1c45c252786cd72d2a6896e8a9a1f0b7afb0a446b4 601f109b5a11bca660a67c06f35833656094c5d4b2a636bb7240e9d503f1f6a4 a469396a1461301a4f8e66468f10add685b676d222ec256883d19226dbce93b1 7e17ed8d1169201afaed3b7583b5e4cff90a41b87db882b3346085e4d9a89646 b78c8e3db2a41cff99d9cac69c7d64c876ed8fa9b79b6f2cbac18fa9f112a28e 8d466b451f7033376bb7152f2a1f491be19ce3b4110b99176af4012bb54bf2f6 f0c76d82ad1350f93d15c1836bfda15d3dd7ce2df76bfd8978e1706215f9c5da 02a6b47deab74224dcfc763e293293b0d6cad511ca47174956caeb9d7fcfb2d5 9407122192980b7241a8e5ad4ca926500a058a586061bc78d4981a4ac3ce5dd9 c1c027db507f964ba9c2b39c0c1adc96369753c315260f9017e0357e665530da bc0b435053a6688be653d1b424b9bbeff39cbce5816bf861c2e90f466c4d3431 07f6af1b1b4485b06cf75219729aa9d8961beb5623d1e1927e57201744b653b1 24ed05fd6ad90dd6b9b1bc6bd32abf7e5db7879fa9301fb2184ec76190bf91b8 e01b9a9483b406a1c7685848543197f60f2da6a792c8760b9258d5729890fd32 da0a4149f55015ec667bd61f764cdfa6e22e3ba916a59727520fd9d44f10cfd8 a0c79e8537be627accb017155070709d44e707d9fca964801491c5edffdbac7e c6bfd82f59b140e0cb1b9b5175b31dc3b46ffdc4cee3161bd7e5920d9f56a45e c92419f341b6ea2e4fd9a33fc16705ad9bea517188c8081ab4d3bdf2fbf93c1c 4753e1ee170edd4d40c606fe444f57dc4bf6914109a64316ee7cfff6a5c4e679 8e9ff471e96ae735d0e1972fb077205a581bbf08975c79c74fa7068d6f88d864 c039759872edd01881866a26ba64239838f5d03ae5a3332e8e29e585904546f8 651b779b4fe258f735a7e445c38be578a67f66f9a98539771fe9264cb25d0e43 a1b78263485b7917a253ad2833dc09ce8249306a1b7728823c84b9e8c11aeedc 70519087bb6005b18ff7982faf2ff336d283e1ea0f2ea8b82e87402ee5a9ccfd 1e3eebb6011a1460e1bc22027710115a59658790ea820f735ee4e1467e23792b 812459760f353d212027cf6aa7f848f5fdce9631e0a946109aa012402bc94407 909d8db9c38652416c7306208e2f0b713680e1d5106dd913fa38c066a70af87f d60543f6dc530d88e26fd012ac6cc35a38d4a02079eaa7e18aff5edeeec07502 89bc2d5ae9e3d87212dcb994ef97920fa99e94ac699a724251db7cd7f785d498 a1fc6214fe7df8e9e5f93f8b358a7b53faf0a19b5c89ee73e762cf07ad11252c e6cf3454ded99dbbbadbf85afc558671a03d7907af4766c2a647fe4210a0618a e6c2d40c95ef90680a2d50563cf7ea0425916846b401d8728c1bb45c0a7220c8 872a28ec8362ecd342ff310a6a6cd3b1e02714471ba8103b424084150feb4112 b2388e5b143d1ccb23db04d989494bc17906d155f3f79143b499522b19d9fb99 e5f85acafd5deb5327fce7629186751e513419aacc08fadcf28e5a3f97631f5f 02baeed1b07a7aff9796f9c7adc212e4af8c8c94c12f3b4929ecc4c2de0b7035 cd09587a6288e150a3a1d768360c7b31d3216a17cbbdf84702f9ff987795ec75 3a4b3ae89230a7b2fb35f33856c06d9fa0ac06919bc63aed50dabc93e3158463 83244659a81b65636b41d088dff412b7afd300cd38e6e5831cad510ba09584d2 b39f4cbbad3e38844476a7538c9315b658ee0db512a079c09798a26143c50fd9 1020af9ce4b3cc03efc2943a6be4664bb8deb038f090da6f7d3835229ac22e6f 497f01e5d76d88dfd5b6cb165125917b346a488fafdeab701eb12a09d84500cf b802c7a8d4ac0248e66a83cb7cf116c1d58c7194694166e192a035e61b44081f d398e16e0ca38825d62cabc875452877cceea121cedf8a4941bdf9134d9e9b1e fc36d56202ce70529dd1a8d11321d2b50610b6d3f7d839ae2a420f5cae859d1e 7e009a13b5aaa4cf5e83f91a86ded20a1642e6d97ad141e9606af9a60f661d05 160be2a6e22caf11dfa0531b0aa6b5bed79ef3d0c869aa069afe6485d9d130b8 98effe4f099dec5c166686a45231e3fc7752b8f580930f74e789912b2df1a5f7 521943f7e9369a041d97b47bfb1ca01f817f65be3d8fea6d631f5d3a16f00313 f18c83811b55dc5a1b964e8d6bb47cda6fc2fbde38972fe3916fdc5d50a4d4d1 13bc6df749c02ceba2ac5118c343cc49ee926c37396d0510cacb309014f46a99 ff45b3fd1cd2c67fbebc799ca79435488935b378aa2ed09479897ab98553c1d9 f3134d8055e5e980b3f30485faf1faf541c9b5de6e646cdfb39bd3066174ebe9 d21a35dde7298dcaa34dea986dac935a7cbb2b75886cddfc52ceb8885feaa421 34b75d8960df56eabb17cb7ef8683cd28425689592f2b4608a86c5ec052cb48d 111e3b9555c17a60d65151a6bde7d5b310c68420e1d6473d12dfe027cd9b4005 0f6c796371a39b0391471ca65edbc83347e50ea5b9ce7a68573bf122da725667 71ac210223d2b44e2a998ace53443aaed0e6be7b4e6f10d62f80968f5bb4d1f8 587c8ed2b155d598e2193fbc41af9755dc78ac5aed6bd5e1dc9585a9cdb3c7cd e1539882ed5e2b8f58c28c8342401c7e999aa4260fba0d254670f4337f995344 644872f95d4bb3f214a7e171d4d08dda506600799ff2ddc445f003bf2703e894 9cfae71fd8b188a4e138950c078a45b8798b62116db40c8b3bc210b60e006104 82312abbfcb29e29445fe9f33f0b68a92d67bca96248d747b1a605c5510db418 bbe3f57e6a95b93c7c0f65e3aff786a98db7b1328c8f78d1ad04304383e6000f 13e20b74726027b754b56b6325311c595370d85912b9a567a1f7ab7b9ed6e3dc 5d6bb12093f296fbbe238ccb1c4484d4628c532e8b688f50366fcbe4cc41eaed 1bbcf12d8ac20050c25c370d7105203c0f534e1737f156f2fcf3edd9e7d54830 adf89fc67c222e6246ec220df5251442514685a233635aa137d10b5c97352900 18eafb100f37536f0b67bff7a751bd25c221eebece828da50368cb5a6fe274c9 bf867b10e14ee6ef3b0441844c15b3d69e51a71298b7dc13c410d1368a1da384 774f4e585eddd03ba546f0a1094c0fc4f4344abe42e7ee1e548c4bf1151c97fb a63460d98e48ae654fd65636e729009860e3b33e7ac2a1bee476f8c6106b6f90 22c8033548b78aaead87f097a0d975b52049cd3ee66177ef1e92dc23ce788520 eb9cbb904faf7d8a52235dc05f16ace00c4c57663aa7129afcbb263321dc4a92 5be55b670d63e213a0db4d0b7db284175c4d9fef5089eff2b687b110537c51df 377aeb41515075e95b611e74fe4458b89eafe098c44aff2b720c021c2adf9f45 eb4d4dcbaec1a03d08add7390088a1b6f78ab7c656f7b6eb80b8554b2b81975f 0857c3125407dd52f83a0b0a2b28e84d9649dbc53a3d7deb129891a856362ec4 c1ca95afbe51a06f02f02d086cb23b8fadaebd61a54aeddef38e3c2402cf87f8 a485c2b74a62074114eeb52be6db88b0e77eb7d93f2969a6d0558b37216f868f 211bb3c86eb6905498dd282cabf7f5e0158b4cee713bad6b485b9fbe6ed921ae 4e3e0e193251a5eddc02dffdfd44d1554a24505758906d55badf57bf362357e0 037e4ad427edc9e1b886310c6e91ea188e4834e03523b4f73b0a020d9004fca3 8218be639e934724475a88042b92b4744d8bc554c86cc3a6c913d6c749680329 6a38ed66ac3c60824bac4b3e7d5fe885b492b480cfd4e7d4fe59e8259d556dfd 37c937fe74d6b2570c3407ea97ce85e208b4642fed0e371842819336102c9e9d a2e5d2aafb845eaff397027a8766ab549fa2972c7d30adb751e2b0d33bc1dbf1 76d7deb2ca4d8d04cd588e5b94f19c45204a4be00b278b47c473d181b9a61d2b 84afaa89c024dc8fa2059b8544e0539a1529b709e4989952de5df69063aef5b3 26b64b27e528dfb646b475020dad92aef16dccb497093c8562bc53f7e71e6e69 e3f839f6a39470ffb32fc898dcefd5bb5318f51c61bf6a19de1b5928e24477af 4feafb54350899bd10da84ea262c0fca17df1b5d5fe0dd9752c45dea7749b8b3 6a4a92f5a1bf6fe26d743852a39eb158f99e0c3e3a21d342ee2c330b0a0b8f6f 82656a40b7e1e4af389606ce3167325a25155f117ffd1b0ff7364578c76fe7b7 4253ef0949809d50e9de9f14459f0aef6253f434d37ac518905bab3e1eea1906 8ba669a1152d93b537834ed02871c9f063858521b0c66c090b5bb2d024240517 6f0340344939d2a1e7038b9ccdcdd68e259e51a8334b913855d1f5b2c11559f5 345624ec3fbd716ebe26d307f382c141fe30c281422616e0eb6cd515b2692bb6 e21be4924ae2d87af502f12d16598d2e86aade0e7b51f1cf14e372c912fadb3e ec35dbccf97cf18bf9d8b531749b7a63ce1c3aea12e9e97a6c9de80334051841 a3c6de97b996787ae19b86beaf897d2b09bccb9c51878da44bed8eb50ea413c2 a44e2debc66bed4012397f51078c5fc68119bb2a18640e7f693ee03e5e024b34 c3f14f260356e8d734557cd3ea87ff72aa077d244ed8bdc36de036a8365d2b0819905e758d875fca89e4048d5c95b0d6aae6fedcc26fb1fd3f401bcdeb97890a025a4d5420b738d88171e56bd442c272b817010509c5584090fbf845d7451902b051144b8d075aaf9366bfdeeedcc329dac528bde1b5b95186a3f1b57d016e08dedef0ee17de5d925c17e56dd90df2a515ee6dabbe908129ac9c4b0d3596f809cf36131fde9d444fb97436b890d856efe77d94b6f9f69b075dab87363b46e30f729a46e37343aa57b0063f95af38439ce869b6abe34548581905bd58fef22f0ac72fab0df6c05b483ef253c167438498798ce3eb09d9a6c0155f012e34adfa0f465db4b636f04248d179220940cf095a290900a02cb6d012d0652ccb6dda3f0527459f0eece689770b858824a8d5452903c4ed5907e1a288fb010b67a034380578f71423b23627389bed8b2dd60b0672d92f126e64c5e01242f03a7ef87e2d00bd6600ff1b25c2795f027ce87275f09746e1fa72287a14dbbf65454aa8e2890fd0b9e5eb204127ade2661efa361399499518e6c978da1115ab5b0e15a109020589e6056994eed785cf8647cadd9120ab7c47dadf1eff25b1e0a337f8f7f5ed053fe2d3cdee3dff539b0e1257ab2cbe027d3735faf9d0ef38fdc4bd2e71511d0c7e484c64871314bdbdf7bdad6fbd79566f6225e800ceb9e5b7929c5c0c1c8d0e1479683056d5f01ee0770f912e0b00907356732c8d47d2dbcdeb6c76a19a460ff7fde6bd113868cde50ac39cf630c93472883398261871855282df308fe4420132549c6774251a49d08238a801dfda74daa79cd06acf5cd401d47f02bf3c420ce90889163d51fba33c62dd2162892dcade7eca34af7b47e88a286aba64e9640def7ed4ab2a16c6433abc146fbdee56c4168d752a1010a7b6c52fd44e0b6b470fe86176f2ab27169853cd6d252cd55a257b3f8b9b5e059745aecd04f841ae3e04f282fd3f6a017101c7f851c0ed2e9a0d9039624b9d78c31713295a7f6060f30e3b5213ca7b34d25855a7f50e0f3c31d7992cacaa31bd882607fd7e945bb7d3037bc7d6481d48cc51d1d560ae60975ee0123809bec9f96d6dca26da56cc608609162f09583b80e35b79a98c4a511a17a7827709a17824448ddc4f8ab913541702a77eaa55e869dacff5c6a5162c7f3cbb6568d0518c3d9f9ab4f21fda49a8c70d7d09fd63b621e4fd360afbd9d043c236c520cf83b839730a2c9068300d989e03e49b3c4ea7ae650ef65bf9c7cc4de4b4b5ad77deec7a2d2888da0ae56a53e701064969f19f4018576d3928c67e75ff53ed85a0be84993bdf16796c91ecde9c069a7c5bf5f5a441e12decb3a002bf666caec40b8cc7357f8c4540275afb63c801642bfdad46336556aebad8868dabfe310870bd14f3bd4ce6a89096381c4aaa0d9f3f5d57478b2a4509a9580f182021cca989d1db94f31f8fc73a162a09a409093ad6606b1e1477c0d84867ec61dbb29fa341fd7d71b38ccac76d96e85f2b1a0e5b3dd338f28b8fa27043211a76656fff3afc2f4d9db79d060973607ae3acb7034a683ce0700ab595b46b864c6dff8d909e0606481f641a2baaaa0f06106a20019b6671c652dc56643a6515c5d5299876603fce80516d2ad08a2e8269fcd6d008e3ad0da6ee2c00cc4a2ebbf9f4cde6033df4226b14afa7cd8f5ca7d41ee8c90a12529fdd1be1f2621a5339895b44e99b8cd6a7ababecbba05906de66c99892049739092105cf5440e82b40c6e53694d401bc5b319b7793ef39e65c87e1de8c0c4c508cd342f9e6130eca502373a341b71793e589b8074b19515328ff63f4a003dd7e79aa57e6d726bbff3b428f21fc7a924001a292423346f166b32454be3f01ad893eeefd7647ab1a5a5839da95dba3336827f3ab41ab79eb19472c41655a0f654fb4667975701cac4cf3c13bab671f1db7a4ef21aac07df6cc6154294f6107edc4951eb9c717fb1191bc18a38f37cdafa11eb0ff883aa5f8cb85106b6e6805c556a066680cdc97b7b46ffffe2b00563744a3f3521bd195176472bb22246e0441d13d68a0ced286d4fbf71cc426da0f860ad08a7a97f3a7ffe3cd19c6be3204870a7a2cac255a40da607939d021d760f993ee585ec07e3d21bf58c7c87f310027f7600762e3b1cec2e71df3a3fa6b18f4f651057cb5f6af4a594ffc54f84c0e8b0380ad42dd6a4f7ab4dee4ba2198cfe14f3d1f0d468a580079f416ee0fa606f7cd1a716e54942651dc13c58c953c054e59a62cd76ec7ae646451b6bcdf89084723e7fbaeb4278d488b7bfaa5490233a02bbff8fad8305a9fc165453ba563088ddc8cfa9335c64d328d259f6aeed1496f23edfce26b55d9495731eb22b8bb02dc9b5eeff0cdd06698466a2906daf0890967609dd72cb3da09e6a3f01a9dc60eb6257933fcf246953e9d23a1435008ce5ef38ac6fcfeaee21680a1151efc4c0292a50b088ed293146a9b9f4c6235da5cb61dfacc4229ea958ed1cb36b9d02107618390638843d83c0a14ca9e7dd28e699962ef734cc3a0f2aa1efb1e63f1a90313140118055369126adb97ebf08a1a0419e76c6d3c65e77454641501e3a648049c691a9cba10a06d6cbc2206da1ab1157eeedc4537642794f10ee02159a22f030718d912b7d9e8edc61c1db7f92ba4ad12dba39adf8ee8ccad99faf8a9c1590eba145c81a49cdff595e987c9e0e25c891cb2195d44994a7c5698c2dd199a8208afb7132fe071466eae5b1d01320cd48e344a528bf47cbb9ff1273932e830e4005dd9414c0e6b8b49fcbe4fd750ac1b44f8a964fb724ca4f08f51edd7261d640db092c5b0dc24c9658ae506f6a1db1af3f054aab662824edf0ae164a1a72ebd04e91299f6f5230e59b2e42c4bda9863fe97399f7de56c72e68d674f2b4b073401a2693043927df73e7eed890326713985392ac3a493b6180f1978b8a6aa3b690d100707ffd7e8170fde795cdc39190e29729b73a82c0e3038b83373bc3d157b0b6cf4741e9f1c78dba5d510db2ff0b5f546787802e4a04002a8283ce456685d07853d08940f9199de2f609ce5ed14f499f637940a1a2b662a917b6bbad0a72e0e626a068982962a7d71e405b0d0369680ef0747545d60193d633ed11b4fa35a0207299c75bd98284cbb6d3baf8b9643a896b38762a89012840850b2a01930990da371795489045e1d30f24f5ffd554b0bac74e78790a64f0b7b3b8aec4f42970eba393ead34643a2a356eac359037548cdacd3d620f9d17f66d6e828a34ac3d09a1ff2edb6891b2cb1ee123b3a99a6d81ef51d057056b12a1072134ac305dcc08bfc9f58c1f3aab07a60269ce97b92676fe80537ccbf57315dfad36278cfbea0caa686d7a065ba7d4b6d7bb9b8d3b07bf5957716caa0c8ce45cee12841d12b301c05949c690be2350027649fb8bd454e3c7a81094deded906b58fde2781db240ceaf611107c5d4a596a0d4bdb5f8c86ae8aa372ccf48a431a7747b2317472ae0ca90975c7932791e93e9aaf75ffb94b5bb5761334ee8c9c9a6b71b907c3331c0523969ec72124c45fce442ae298dd9d08b67baa9db4cde9da45151b9acbf6b60cb49d0a400f244f1a991e060a39e7c02106a76dde1a4d254b2fdc24a12df2ec09d0c1536322e6f934c040a955784cfc84252d6b570869aafe22ce7898fb4e7e0c21453e61a38beb5255995d8dfb95bea1bbac29637d0606703cd44edf981f440192a3e494aa6dfd895589c7ce0e5452839f4d04d8dbca389d761070d83931510d6c1bf7158e3be69ebb0961bf9c67034b05ec49f59a1137dea3756b1bf1a9430a1c56bbb8405d71e04080d37e1220ad0e20ae1d314debd9f9ddc50f023a2706029875cfefeead18426d7daec1a9d3f3673789f0d7ddc63ed837dffb25db8d310b99a3c3fbb8a892a0265c19ce80d206782f6e6b4abf0eb538e337aad9c21c690137dd0d20ed75b1914ab98d76bb605feb78b4a1ca03646f787a98ab14f85a160d7e14c0b73ce3a5bcaea365709b9faacd0d266e9d38efcef45b88e06ea41c0307928dd9c048dfc9323b708c9321c7b823f51e9f5df837f8e8d25af36a7155cf0f1bc5f87a492c639f743eb955a28eb1bc5f777ba340801d6d789bd3a178780c040a27f6787c6e0dfb4d57a0e02ae901d1d029e5af03078cde07a56dcd0f1d680e0d66229f4e7ec6a05a5e1ae22419b3a09f90b5ef3af99dc1d8c0b72fb7d51b09d2f6379a5a291630dc664b35e96ee35a1527312fd6a81733b9d89c1037abd2089bf445455c54ccc4dc8572cb8ea4a99f3f4c4925cdb03ff39955300009959d02dccbca43d5920867b99ef1206822d42e13886be9e12eeaee8fa99d7f07b48c0cc938eb3f8fcebdd6b7eb202087ea7daa492f532224c4fb107fc55d358c0f9803536b13614d2573c398b12f97de562de0902133f8325aebe82bdf140e54ef35065f95ff8bfa5869be0e06970828cb3a9bda0f3fcfb321bb78045b33d571d67405a4bcf181738ee12fdfc88cfb1a50ab241f1e0edf27cd5b8126a5c74be538430328557224524bea0e4465090ee656eb790bdd2b09eb3e3d0864b7f879d2f6bc0f12daaaee5e5583630417b1b15bdc0a5eb2354a52b5ae4901f1887fc8b13bb80f94afef8bd39e3049129513b949d0dec96fc7d009bcb0a3f563cbfcd3e030a70f5036069effeaae61422d248f5ce60f50776f7a9b43fe141788fa75cbee5198083f9f6c99621c9a5fb7eed5db61d2220ecd89c720a1e390ffb33c63ae040fb70b472e5e6efbc26195bcce850725a3a1d32352ef5b508651277aaefadcaf585f0c2b50e088002125e26efc8c13cc375849bee3e10b03745bf69c39d0cbf9699003a23126ff2a59b1659981a5b84999c54c5385f38f6415f2f6bb3dbf8e13edd502da8cb005209a3d3a2fe5d399bef7bcc8c8d8962e2f0ae441005e70707a98c609043ca672ee4289eecb047c58417d6d91e8ad305f4965fd8d0bae3b50c9aff90ab2a235da289081638499f4c5da076264a266ed9813ffba638a1f5ca95d3c810205327ae65e7ab72a5b9c5ff81ac09b95e47614d482a2ec3ba9ab5a754137bd0dd7f947a226035f93595cb77e4674cf69c35548737c7771c60619dd5e5cebda08e1afa2370f28385258dfa66fbdece120ff9540a08a55b780342cd4c3bc91620e0640611dc350e8abad0ec38f411aa535ff452ddcc0f911792e66c30eaca1440bf47586cd981c28bf826ecb74879a427f41d82febaed7a5e2ea907fee81307408c233e5173c10ea37a24349f4a4158d3fdcfd0dd4bb1deb8b7dd3abd5a560ac0ad5a1a7181b44fc6d2404fb94e1ccf5f7a9d32a3d60f535439b9f4790748e8b0656cb798c315cfb2db1ca31321187d8dae8360bd85428ae07e38d23ba3578db01c7762fb588ab0f358f00e6259730bc397424d6c1cb862f7ac855f3c75328b204bb8159fb178a3f4010b3de0f9e546071a234a0b7cf8bac51384e07bd0ac24e0f697f653298b1a33159ceed6c162f28a97bd9dbad81f57ac40638305b02bf8b0a8b5d8e5f3bb35d6ca577791c862ee73c74d3b35416786400c7e8405762a0c703a79b93f8a9db501f2456070550909eb1a6cd303802bc2f6fa89e6b94766b6e063f0c4c3ec8d3d053c9204b27c6d8f896facde373d1551f504b975c750dddb10acc0d74643cbb8564b8e86ad5bb19f0599ba5506ea7448df0e44d87a82c96a308a049f1391f7767ec2d90a6df860657ddefc875cf9f2643bea0ed1423e6536301920d34de17bb80b272080b3b4de79161e8f641f19aa4afb92ef04a517d7c2f0f9b15f13ca812709b3e91d83dd1120a6857cea211c25cf16abb2bd02047dbf506b608491e6a9ee1046b4e78c1112f5dc52cde1da1cd6259fe2ef4b5a05db8f806c36e4e65cf73ea2e34f94df19f2ce925a6de457982c203cbefc95312cbb755082b54305388a5ff5f923c628e6366a19c054435c1900b717a080e3b96e9d74601a44e17fe1afb51886112f4c634e6c6388d903769cd871c0a5a8e93798468da0263df04af3e0682693c94a8c272649d1cb566132c510a19b4c875c3657f59e306ddbc3836e2739124fdc92326baccb2ec97b957531b9e4437446830e0f6b2aa01add7057a692bc43eb3b3e4503822fff552f657f305516284e6653261ecce6907d91a1dfae75fecdc0488af41d96da7bb65423b66965f4262393fafc7402a1306ad70a0b2659ebc9e9d14a2c7526493dd9be7731de730545d8d5bf03b8dc2c20e494bf07031b382aa49ac56928a7ea718f4798999c25572b11db11a5803a927004a0af652e486793398436916102d3c107c7177db3c6c66f24cdfc681a34dbb041e332073b81e219a26933c0a06b2f087a972e87d2abf741e307211ba64a9e2091617e0127455673de5457ab63b23abee0289855c85cf8c2f99cda31b5ae37b05157513ef0d640409cdf60cda3aef331c2bee62d3f237223b5c494c3dba797707c3d782e629d2298ba13434414a10d545a61c8244e5cd7ee92b4daa8b78d49c0477fc613473d2be15fb906cf43cca3d2e606e6dc308dccb02b1e97e5c6a44060c1a422f6ac2c31f82d61b447d5859136e393795eeb5031e4b39a56fda0ff310061237583e2386d072ed67f34b07fc568762bf4e16a1c77f61b0f8523243aa540484ab19d7aef916bafd7f8a0e89c51ce0041f2ae3571fa38a215b05b107724a04e8711a4718b55f7e25464bda765d274eed749e5897a8ea81d114780c8f2df80bfbdd239310c7af2c20a8ef97dd4d72347d9b847d3c56756c3de4f8c7c9a7080faf2a68e302ec5a1e42459f654bd099e9a2e41ab674ba04a291f7b6d141279d02de5ee20103faad37d464bf730ee12c3a04b908e94bc51bf55706326b52077a094b05694a7eced90f7ea0bd0dc26e9908843d1ef8b00dc0248bdc38e7cb9d79023286284c75f8c74b185960510a6ce9451651d0c930f885348fc17f7e3b41320c7d3ed45194cd59441384304dec18973594f08c2fcffa4b6de883b9dd4a93620bdf845bfb631aacdd3c5ee1867f9c475df03ffd22e5df87c0be3bd31ed86c4c0998f698d210acd25fe35a932a5a4e9ad0dfd8c3701bac74a37a004eb45f83380bdfa6c3a5f7beb6170c6c86a7f24c9e6d3340549c197085523e65b64eb25fc20b9bfcda1a75ebb0a3331f34f4edc76843b9e10c8b490d49891c065a6c99ed2f0005ff27c2bb190284ccf270ea6360a498d88201f99df9f76a8a1d005b244b610f154139ee0dfc1d94140357ee737db317282314236037e7bfc88e06d85cf6f809090b7503e6358619c5093503461f9450d7e99b3c1eb79326d6a3cd4f9fcda005c94db805b0b2ae874b8f3542fb92e0a0e164ec1d8c806e94d12bb6fce214d7031834b72c293acea4526e0953e180374b1021ee4fa5f89497c93ce05788a17802b2ac002191fab62ef33c8a6c84e69f11d6c09ea378e552d85b98d01c34389408d472422841e1e8ea34c4cb2f536b5caed1c08b785f45c6a13cab34ddaa1ffc05bb1d77210f857b59c4591ce3ec0ad83eb71d204b5ba88dd0f9bf7c55325a160252345e53c20a1b4c5d6ac06e03468b666d4c1678c2ee7652a09bc2d839c4b40da9ba066d77c4eb8db965c0f815a0ee1e1870fbf54341cc1c564e2aa798bf2202781bdd89e283ce685247fa8589310bef9aa8f35af761a7fe77a8920c2d0d330e22ebbfc96ccc8e39a05d450dd6f284af41c671cd6001b5214c21c17f52efb101406243a56c0ee1f9dd2fd97e38f13c874ef84cddff2451b108c24c24494b080aceaffe083b7faf7006cfd488860c2a4b72a8bfe75ca8bf404a029dece8496e00f2625884d2fb39a7c9db2096473ca640df2be928ce303e331e5cc1960b749f0d4f897f8dfa451056f620e7b5a591e7130dcee2972ab17589f46173b878bc34072cbb3a4191b09575d9ad21081d288c9919f49377d5bd55295a30e81e9c047a042f636a352aae4164b45ac2085efd49cf2e0c8c30e1cbb834ba9e197461e2130ebd8fd78d4fc0368e259bbda59e74b301e911c2c82d75f8bb2429dfcda42e4c05cda91365bfc3410aea1623c72f3443d0c7f177f8f3c399b2b3a9451bc48ae302d67b85ce436d069b56e108c951d3af527f5391e8aed3875e32ad67f03c65ae0517a52ba9a89a4823cbe299d4ec329667f86ce67519397c5374a08a269e30530da8802fded4863792e771ccf1a98f889d852871d2293c6eaae3eb85b8e6ace60f9ea467732b52d5162eb9febbd88e0bbbcb265a1e91dd6d03ae2a9ecf74ef450b297c2bf138e86679a0cd4243fd2743bb8a3f6103af8452ec8cb2b626cf2c790275949c8bc6f37e91b61bc25065fb7bfb796b4110eafb9fc24598e2e41afddd016601b122f5c21f4d3ac5d81a3c1513bd6af72ac26549a558eec287ec5b0cb40d5f1766766007ce286eb8418eeb04cbe11dd3c9ee95095dd77c733d8d0c262d06f02b33f0fe4d029d5aa5552502b64aae8ac8f102fe0bd4002929039a8c2c8202458e64e98f6d92296ded84d9ac3e80105bf2230423080cd975756b4d08d9fd0fa88c96bc751f357bc618c81b7b1cf6da6b6b4f5665c60e6a3ad1c85e2707cc0cb5c68b3a3acc9aa1ee86cd1e78f043ddf60061ea8b6e4d7c4e2419913a675d0488638be97e95cd77a9153a95cfb8009a0bd1e84726acd8ae0adb9beb4d368408d562df02abed9973a06accfbd11fcaa2698d26a714d02e595aef2565ea52e80e662bf06d8a602b34461ea19e87a6f06353de021a0990214ed45f005f03312b075e93add08a45e430aa7c7dd9d696d1b9f8a716716f54ccd45e93b15501bf090c218570f804d0f7c0095b548962d8bc174acc349f73bc17dc23678a76e0cc8002f3540f9b5832fe129df88c2fa5411ca35468601acd16389640410ff2f096df0dd6c5550a3ca9f2490bd256ab0b012cf6a8365e3425bf8c1231acf3a40728f102c0c5236a086183dbf8a7f49c8846f9a1d2f0d5b9da393a86b1368f39606b9707810ac555e5874a6f4c45ea75df9b34ff6ad99144f291dab2ea0d02a743dbe00fbc1f97cded2b736cb42a451c9a51c71695a13da6b2ad0ddac7371c5aa1699e021b6bf5eaf0b4cc542be6b8a8395ab9e093ad1860c065ffa15721363d45ee540c464dd3e00bf8f15db2e51eb8ca6ea8b5993e1d221ec15b321c954f4bafc53c0df253172f9461d0f21af7ca5ddfe74d3dcb07346b311cac0eb3431c4a8c726b0d26866bbf36e53e12d17cf7c48d42075bf90eee0e17977934aefb4c27234fe3081e2e1febcdbcbb7b7f763d442ea9243b25c846853082d89468aced771828ab0b6775f93cc687d0f420ca3e256ce0b5d70ac2a0db8df6e741da7229a8405c9b06acbc59a34519f3124f6801f013569a1d421e400e6a220f293150437a9c11c5042ea7099d61654d6dd203e2dd35baf942bb4acbf0de55b8b3e775497f3b00410a647d72b24b482f59677afc94f022a0b0d53fedd09bb67749caf6967704a26804cfc188df966d752a03d7ee4d8e4286147ec4a0c86cdb9bdd461fde104367ca0f064e215417e7b36472dcfb3cea7244dc8d2df380185baeea6ecb6c0a52d94a095e09a8ddfcfbe86a8351dd15f874dc75e9d5ad49eac01cd9a377ce9a034fea0f95fdbb6272c2875c367d66dc9d61de7e1b6cffe3a20f3fc031e11ee976d9ff042e4a107eab652fdeb584b1a7568561bff343daa86dd998c9c97c05cc246fe80a7e17d1f576512cf96ab011d7468c08ccc63f42f24fb7bcdf879f39b4a8731908aeb6162f5e02da594687c695cef02096c29423849295a1fbc5bae36e2e31c708674d51bba336b5beb4164c674d8fb7d3f3bb744ede0a89bb6599ba410b049d0d1ed12033dc3aa73bdee91bf829e92439ce1b4ea10a5f1c367e3c8759ff9f0308f3c9767bbabf90ae2217df1678dcb710d574d84729f5ef71fd8c76830f09690c798536b30461cd52e9187dd2d466c0d88d37791bf1f85a0f22f0037cfd0ae808307c26a9a1c9d0b72028b59784e11119ad4feb38af51fc30f4d7d894e307b7026ffa9a0cfbc09cbb6cde81bdea125cd51dfb92d1d6d750c7ad3d448d81ec74026b6cdf1b2509ce5c2c421adcfae6003432a744407de1126b8bd48e74501ad903be35b823aa52560f7662807ea50b61e79282ff531b2b364cb7f51e0716a27b0579ce7e530a6226f006c37c823fbc6bd59865b1c493016105e167629421370908ee895eb257aed4a99f11571daf62c00ff7d68cdfa1bd8139a2fc856aef4e9501b2f7f8dcce0e719592ea0e3c6f5a47cac0bf1e008986106fce29bf1b630fd80444459e8a314aeaf16bd671d708e70c53fe45a687f0a6135c0525920ba4902003bc3c9820c6096c59d706df5f58e599fb608adf2d5cdb6c0e7f7bfb16c735fe09362f29a8fbe2fed6590e2ff808b070e6843c98dc0b1e96240db549c391836c0f8b33888c131d8dda3c5babf4e8c7812054bf15d99997137d4fdb67bdd84d3a06b73496d1b7f1843db0420087d33e3a96dba73398d866056449e5757f0342000fafd0f23ec02b393f57640fbb9d1269c638fde6ca9866f614444f8691585b170f4cca4e2e80bee474181f293cbb5aef6097560705f4326e3c63cb2d676b66c90e880f692b394880a9e8c3993be92ff3ac663d00e74616a5cb62ef82fd131ea303b98870244a02b4e9839e96a00b7fc8a90fc33935287a1148c8e07c20c8de4d084cff6a8abd42c685db71dc95fc6ae042d82b5db4079c3e60975ec8c1ff855003d34b96ffe2c6a3a2f628dce30708583d97330ef2ecbefe6efa109eb5c3c18006c32c9cbecf0872b26a8d7dbd7ae0033459400318405349e6ac48880a3aeaa80978e54a34d06dc2b79f9d216b0afbff32407698206bfe754da1bb32faa868ee02820dd4ac0a0d9d03ec7bf3b02c2da36bf019773ce3944176034f5b650a4de206153f00e5cebfbc68ee48bf36f0b9213ee96759687f98acecd680b217b6c49d0d3aa5165d91b12184f1882c802626edd353a047aab1f23ceebb9e297c6d609904f0552f0ea6a4ce0f0137040144e1b03b66c2de8f039caec2880a6d344dea100b1da81fc5a673768b0b22c701d6d8c6216f93add8d1822b974d5f49db00cdde0115d675fe9335debb554121a2931384e364228751e916aa69cbc6028c8b5b36052ef0b96a96da569ae40398645c63e666eb920c3def85293b4816fdb32aa1eb0cc80ff69a037bab40b9162a157bc8d7851fea1ab4d5b409a63e2e9565ed0eb504fc3c856e146d9c1bf6ca4f2ecf193a8d15ae4ccc4758248d55479554c4dfdc09a54f501a73453643a9d99680b0c4b5f14b0f27df3eed842280664c6bdafa480efda382c4329b843691d065e3aaf75695f05c397a1cd776705e4be29ad82acc0de5bea0e103f1402a65ca3b9e5a1a12f06aabd0b04ff7bcaf29f4d32a5daba90902db139e7053240273afba76e2f7dde5b7f4d44c5b6038f1e7f6445b1b78a00280acb7db278d3160d80c805c12194f8cfd89b0d18aa80f945c62d45bba19130976f9ae20b82d8c5cfa3ead222ccd710e4262bb8961d0afd32beecbc4bfda08069209b8d4ae18106a9a6c2f21262ab5071df3a692b0a612cb96616e37cbd9860282a71e3be9465048e629e9d26df055a4a22acd5ba50ec7aeddc4d6f5dc90ea0a877c6179acbf3874335a059b4d7c48cd3710f637efed301b255e27fec530690f6658c44d10dac5c808b67ebddfa0940aa5dd06d526194c8b3e4de169a3dcb30a778541d11b5b4eb0fe71b9e8aeaec71bf7d54cee39e1afa4b2c240614423980671c92ffeb4e9641d8b4988f84852e7ad9db292f503d20f1e980ed84759095f0b62830649c15e990209193401dca79e9ec3e9c4e5dd8cfc9ee8194d260cfe870ed6c4e0e611b604a75cb304cb1a283225ab2ca899b8a96ecb5dab7c9100cee80f42e33c692b25e400dc0045a6186a5f8f63bb94015778b56c95c059bad5dc4e0e7be605f41f5161b5c38d064e3155c3b022381618d8d2b8d285a202ea810107045828fc4beef93f9575b04942aa90e530eeaa46b2b6686f357f19d8b515036108ff439b17904f886d9282e1afbeeb0ee1a2bba5754e95770f9db2307b45c0eb0d410e296cc980e15100664e9dc68c9cf1b5911767316dff90533bd2bf9c725801da1504363bf76a4188590cdae30885e41e7790e623535897db229410436ed205a100b3d7f540c27ab9d5ba51de1a66d63ce561b2da65563a06882e157c575307a02959634b3c9502bdb796227afbe4a64b53b5122934dc7171bb69bb610c510b724e65a1581830c64017ae2ae7dc93a719bcc49183553f0a70ef37f667187e0d261dd719f7caee55c8ed7b28c5ee7ae92c3fef8cd891f385f16271b1b50a2708c9aaa21c0ac08da7fd7d55d9c90a0143001740c2828eff9ac6eade868b5c610f309e8691435009e909df6c48bc8c0f812488ce456e8a58f3d4ca1bd61c4cd7039a91bb51d1a30219d73c662d6c83cb1b2e24b84a4b86c9e1b7266ad0c1d37403dabc5e013087de400a26d6f96c34fe854fd91238e2a4b44e12973f0188cd390640887af0628b6a62e4db25ba3c79a1f2d018730e61d355443c0582a8d390ed097d1a234022fa8b4e1cdead223471473ee11ec31932520241133e31179432700db04e72c08d9fcbe83e83a2e44a6c3a76ce3f1dceeba4bfe343e8eee8f79ba80c4d11669a59a1da4978fa09df8c18fb06371b7f6220ad970a4b47ef88551e99027efedc6990038537a4555054262199c840f0a583d73375ecebb91df8541910084b4eff84b333c0c39db3660c60e6e407a713ea2ff51545412008d4a03091a0032695fdba52a73e21c4b81646e99f6427f32a9a3bf158cf266a2d288a02e1df0e8a781a76276e64e5bf68e78651b781e06858d119d3a81f89c6ffaa06039b2007901ee81330a526388ea61fb6fa12058708edbece4a0e21fe46f2b801a913fc053be612ffa3519b0d8acbf5dce2d39498878026b4bea847281b1c80b2178e1d08af41ce16c6bd87dec9110059741b965405ec692863b9d5b52eea414d1a29b008de4fec808ecb613d6deb69876a48e3b8c73b9b6138f4b2c844875c46404113034997da4f9755d30d955f1f5dec5738bb026aab6f0385ba3ab3b89c84518e460e5bbc2ccecab20e8676175ddc420ada9113d0e80ef1a4b3e8a715e94877dda80d157a49e81023df1ceb08c15faf11a7a12785ff116efa6890fce17029d330080f87fe467a488f235294c11ba8cbd65ca00927df75467bd6046f291b236d82590b093b4f07229325e6c5ed23eccf82d329cd297e398c5df1a6d1dea66333cbfa007e8b8f5509cea49dcec279a1f74fc61f1400eb9907b5f93a656b544a118e7e0de2f036e2f2eb1c86d1a0e30093715b6f30111ca666c0e2ad3635b76fddd0b50da00d1c30e789a0d126b257bdbd9523187c43f2a5d1abc540ba9e1430d5c0ef0479e0d6997a4fdd21ada832266bccf54b383bd8c82b250a0e5eaa350d93ca790af0b81ca7e1b31b469b0187ded161419c69b533378a687bb66c3509233e9476090507bab98caa3adcb8e3f1e1fd696605ee68180a78e0425429c1b6195ff8a5024bbd6adf704308036ae22d6191597c72bba8234956c31f499c24446225c02e0d4211f4cc616d9376ba271c2684b86371d94e204c79d4513dfe95a2e3c5927a0914e954704e93976c044ac67237911eea2f4c8d292af99d55d90832dad32b4109eda9e143d6cf526b98650d9c1e4204bcaf2d0c3f39df32d92f5d99fedcfd2203c15d5980a8dc69eac9e51cd4b9f089cd848d5439d7935d2412361f51e9c9030d71270cfc4e30d21277e63cd1690a84e1a81038c0fbc65c3eeb12da2a09267602fa88f7bb847fb42a7ab1a149155171be7f7e0c60ebe9dfb029e7aca4e463050055ae3056ea1e89e252aedb8f0871cf3a326e9f6e39b4314bb007fa385e0fbb0dec3c6df5f506db7318386a1929b637b0d625ad93ce6fe273861bb9df5209a000fb7959b56c21b8b52c735720062660df1ca5335e58c3f98a177bc28f0c07f00d3f0055bff0f4232fbfae5d03da85ce6611786990a3a6450df562d9eb5f810805d12e3e3d1744a38d8c2e255b6d4123fd1e8a4695f0b0b47341115d0190135809d11ae3c5bcc140310b0aaefd7a7f0432f364a685d417ced8c31228b4a1560c0c63da30f25b5074342a667b9a92c526899f57cd5753b6363bd5633187a62e810eaa1316dd6e05ff9778b79cbc741372f235723fdc07258800bff64c2fceb6c40eb27c4d53976ce70c0047a8832b7bed98123c7e5ccefe39e666bc12fd68f19b0ded8d545412588662af819af08cf4bab38b79b0262c16287f859d87172a77cf05741500a5da38d938b747ca0f46238bfd39b39431dd31de39824c1c618c229109b7d1b1237d0aab24fe637658dcbbbcd67961824f232d1c7e2b52650d6566170f516f5a3f8c84a096219caf6c06f2d8fbb5584527b80a5ddd1ea7ae917acb5008acbac683e8b666bf4adbd8ecf08884ec633843a00d53da8184f33a18d2ec51023643458f9c73bd2722d01c9ff4b49a391821f9cd8afd9891ea63f0c74374ef0fa7915e7f025ebb59fa5129d74b24a0d0ddf871ad0320c962c2ce2d176f36d60f1c7b8f2efd1855c5db95962f7e3d8d56f0816c0f67dbb1b5a9c7bc6d6a50a90409f831a4c09d46220b7ebf3b52e76e6e433a26d38050578bf905552551f0ec077e7a24d617bc94380fdf383613cfaff182c9c8464691bacb206e2f857b3474011f72f97a1e740100ac1562c8719889a66c11b1166cb810facf38270373448e015ae549b7c3b2b56fd573fd5b8155667ab39820ae2b2df6bdaf9cba4ceca8760b089ba969559f96b158c921bb2e8bba8c3fa8c05384c42f85c42c7d6839879203a64b0145439c546edbc91664203c92b96744944e9d933a343d7abbc7cf7b440e5538e68cecad9fb11818d2f2683973a2218b0a98a1ee83518fb46eed599e910207763e77860ea10a24ffad65193843f674de31cc9653a965f9c0e0c382b4a40391fc3d1eeac251c358c603ed24252330d78c2521480d0186ce90bfa8a5fba00e775aa271b26d2ec014064ea1ad516ee9a1d728d71dde25badeb22544c0ee45019f1c28239ac6014aca3cbe9d64bb6207f90e49587101d85a89c45477334f620c7b367d2e022d9e0e45f8f0bb0b76a6143767f7623cf45b3ac1e0e0b8f89a680242a7ab5c828c06158f0becf32a63057b4defd2d7f3b83256e15e3d57302dab04ce952dfa7a3c8e1532c15c8efa28b43dfc447f600008af0ef876a71c5aa87003e6c780e7578e27db3acd917ed96f47228764fba5fe0ad7bee2182c964f3e0e0b3cc4c694758901d047a642f49a4f1a063e31dcd9834afe8073e8a80ba123df01205c747fa8b315f51077068445b32474842045100b5abcc89e1ee46e99737b0d0777399b3cf91435e914c937c04ee93840e198336989ef4c8a340ace2763060188e817ca5ffc66ce268bf404aa519eed1daf7485446a0915afeed71e2af019004ca312763d1e0b5c737698a6f03d4782b360035b0ea72926ce716fef75438f01654d9e7c48c7e94be73b21fdf0b224b5c34046c972b5c657f60da28db512f40e2e236a058ca441e9aade75be688b57528ae19539d41bec9bc6541fe819193e05a4f0b1fee32ebe3662bd937aa17dc1d6eb366883e2b2d15032b3b5976e93280a8c5b39c6dba25e1782b9091cbd9bf3f6aef953b7c1df9fc60591f0831e3e7d00e798b228931d75d7ec915736c8a180c391727be329a6b05e9b3d08c752a9590544afab3cc3f6cb96203a95247fef71424a28e3dc5aa90d3d0d5ffc0650545f04239d06f1104a1c907686da70eaa9ffe06b95fd2fe26527843d2f7498c89b8c05786805a7859d036dccc92edf6f6097520afd92cfa166c12425c6273328efea00a45984df0ade3f4ce4f8dc21a713c9953237399d9fe094bf6fcc05c3f0e4370e4c53846e17ca23dd61141ae2fd8226b9a165bad4a3efe65a35c107e72bff0e0f05772e591dc8cdc40fd30af900361a94747a103aa6227994a1f5d32e252aa20a9122d126735db32348969039213c0ed4ccb7e18d59b81e4a7d9aa0a43117e50be1f6a38a9a9a5ad05aed103743e16d54b43de831dbd0f456f9c42462fea7c404748160951f2e2235a1eff2838b01962bd334bd9f30943348ead2be9aa5530a091d2e0309c23d39bd9f95194559db4ad353f32b77a67c97815505e8350626c405412de77f0456a463f5c94303d1ec229dbc1bfe0b530d338fb0feac195f34250f52c397434ab541fc544b18f2bdd599b387e9be56c2bac2dcfb2457a661e8830f697e265cfc05eba0a8ce1f6d5d50f29735b6b5f02ed32eb4969c290293134001910fb798802dd86c4b7d793d10f03bf0621b6522a11b60da6a23172a5047950d68e7d0b41fa0bd5744433576fae3a4657468b5674af804da4a67ab8569a2ef0a2abe34e96a99019200ba290c7d584500a35d88dbf369b8003039b910ca37460722a8511c1277d6d2adf0da427f9732eca64800f58ef013ad270413ebadb08f0aaf5df77a506b0f17afa7d7a79c39f14fd4d158826190f95e71babff8f510540113254f2297ba189fd1e59b77ba03cf56c3b7acbaa9817f9dc3af0fc9b735d00c65c9439aa80fc276e8ab8f9c5748de31b70d70a7240dbf60d333547dc20f090dfb9f3722f2743746aaf290d01cea80876b95b08dc27a2154343e523dce17be05b99b4b6163f2ce83ec77af74512190317fb486e8166059220fb1c4215c19e60818311c13beaa1a8d233b1a1900427cf0ea5d5c0b5e3911eb81e2cd0678a88f017077e6f32b8f7f1085aae9462846029b627cc9e4ad64ef6ee0a7d57827d1660e25275b5d8cb7b735778b9506b1eea18d62a63d6213fd6f1511d6f9ffa2824a0ad2d740a19d184fff15fee7a8f785c011d87018ff33450dd9024eca2827183d01afb1bab3963b46994aeffbc01d0233cbad3f913f20021876397128891f66e9025f84da1d8273b21fb8377a388b00c7e6c3b9b46f9d03dcebe1b428c97086400f9756901a17ac050801bd677b669e9d6d4738584c30a036bde0ff2098d797f701a31c0c4622c24a28f780876373d610c5248d204036497d23030d8c411482fb02bc416d9f6ca80f880df8fbf6b2c52913b0df7ee7aa9c1a891e5f3bcb7e9a69027c636ce316435887603b2fbbe2de4e740a66776db43f25e03ccfe0b217516d0eb0c0f814d5137dcd1f812d76bff934648b8c383d65b1918016af460f8b9d8009426325cf0ed2db19f8e8aab5283ee8f44c53f5e3dff98eb904862a31de4805063a9b8274e0cfab8327b8b3b2b6174aafad6f96a946f940bda0ac1385c67a450aa16d98e6eac1361f10921cef223125c0280ce0260db5c78492eb559a385987085cc22d5b53e16ea0735f4c0c233535869232c09039bc55814c2cb31942e4700af422575491448e07e9e1cdb99e95d05309d394b2897605911097af973db70e080215a55345fc529b1b4f6cba322bc2cc8a4babb394adb2ec970bac6d059dfe0f06c936df20037a4345929ff9fddd0a4cdfc36aff2ffc44e752979ff903a992054d19be204a2338628a12f7f2b233a5f04374aa86741394865c0d987dcaff6a098e882cdfff6cdc462a6b8583d2793ec5b843adf6f1118174c83f8143eed7e10d35ee7a4494bb372f8ee63421a19c0095d06214ccad437b4335e7d60ad7323705f86f58ded05890ece9f7171162a96bfa6a2261568a024db71594ac2e32087605639399859756342e5f3364e300b61f6a5c6e8dbab82ce42258f38054cd5d150a9239229380e378dedb0f985d346f3f18bf1d265ed765a59669ac7e5eb6b68a02d48792b592e03ece1c1d02c0c4f89edad30f10df16214b1467d30fc0b893e6020c77c4d6f7c7734715e5de10c2cccc251159ff9ab353a44fd7248d8cfe624a07e9f1084e8209d8c1421e9dc70a57b37e26e2fdf5911f4cfa70845ecc10307d0e0343040872d09d0db3161574e0237660c3caa38ba40cceec52b9a82f5dc850055a8dae627331d639c064f35828266d00e0cdd6382e5713ffa8bbfbe9c871f80679f35962b5fd859da24ddccc762b7b603f22ac82881a1e1f4b819b53e584fc072cff2bfa79a23f242232d575f9b0cc19f6e808dafae1a6fe07b1f43abf0a7008dcab2982ba40b5d666ad729ed06acf1ec9ad1b380862ad5ea3d7bd5cee28930b7ed0643ce89e5def4b89af3b9bd32718efa3ed5a37b96b8f8446e8536dbcfd091f66c8477d0be15071f4ebcf70c6dfaf38af6fcb1c10f5e234d7ff4729e3b90d3a22204799df91ee3edf5a9a9544786fce691968590fa59dd948be6fa94b380e657273a1f2dfd8357a948574fdb0257edbcb82c71321b182c1d0e2057480e50c06e5af592bd71bad2b5bab5233b7f35c35ac535c485736973db59a948dc0f60fdfc81eb34b5cc4456fd4cd6734fe111d6ff27640c673160828cfd8cff3ddfe05986e6972e553bdcb9295a7f641f91459f3f6cc740fb594ce70608adbdc8abe0ff591defb6285312f48f8d64854242c45d101703cf5f51ceeda381c2742a2c0043a8358dc92d2a09ad2dcd75b9dd0f3984f1e22b787fa586b792020317852a207f65d3c091bbacab73495e8c8f35772912d79898e628eebed27464da25a28a9009108ea9c46187b949e7f557d149b7ee212481ae2e85bcb286057c7cb5e4ea3063bfb73b30ea9d299d5af22bcd15579c731ac5b6aaf3c3a2d85a82411a73fd7015d124ad243de730252cb2ab3fb11b17b814b4d7e60f199e65809d46c31b7ae02235facff1f5bd1c02122852ad9acd0df7f6003d3866e6b0d4719ad108516970c3d33ca6ae9ccc2c4bec8513c69a34a117914a0411213cdb2f6c49a480777d3093a21d1d8851d8e827e13bb07e1eb9533d1864e36d5d9015fb7e99d3c518285056eea1c74d81f54d65776ab1da3bebdf37f39e32fd0bd36c81e02eeb6b93e28049e6605d162e5240d80fc0d1a24fbc7792cfa65317dbff867136164650d37780e780b921cafd54ebc678f0770656e6de5382a8c8af399d0631f43c5ef2a49c003c26928b8dca25c14a55e890e337826bf973185c38deca45bb7f5d4678fc4cd0cca0f5d8396fd7f55e89b8e963a775bd070400dc7d5d0fdc886f8ea1e6e4bb00f8fdff1dfb90719f87452a59abd10f70b11f2625812d3d36dd44683061861f200a3e3be5920ecbadd5e9f10dc857af4372e321a281bd91290fd285a63e1d2a20c2d6c9b7657af47771567187600ff76792218850d112c9abe3b9c64c427da9d02672a37720933572c8bdf2a29af60c240e6cc44a04678a9199cdba6cc9db1f608167ba0089358421d51ad578ce6aaf0ea2d3fdc40fa0a432ad9cbd2fd5169270222b60283113baef79e8bd5d050f071525f62c88792aeb9a16bd54182423e0f0d93312245f8d10404680753d84dc1f8e6eb8afb67aa43413af66b98e558dbfd0f41efa85ad82b252464330966e668178ccc7d7a6f2460d4c474e6551f19a4b70cdc2fddbe26abdedd65ba16efb9c64635c2f08dd33be2993d31de44a2dce490026a32963a34cd564a243edaac1ffca1877846b0d398c6887d793c1f45203bf90e888cf9a27f8b6739890ed9b980d9a0f6582d0650339231e5b1c6a8037f732601dc9dac1898952dfb9349e2cc1f61ca1f182614419e5af088b41de079398cda03a2b3dda03c14ab9706f6559c9e3387dea0800c20876eeb059595e61f8aea7b00ca2b270112a9f39853bd7b5c3f12b21609c35e8b50d5ae772516eda94ce8820ddc01c1c0c21706617e244e11383068398e627bdf77ad19d88d6974932f3a540fc6443b3a4f89f3a46ee1e29a6cd3d6c24c660f9304df711b3cddfcda5652aa0a97fd0d51e4a42143c82b26598641064959377aea29b0df0b88d5b0caace02c018f7f1f5d0ba73c0d4e5969b42e779876727ad955f969ef9e5e3787fe4266430ebbc77cf748e92b4325cfdb338aa637fe918c59c30d99a8c4a7c69413991807090f4dcc31b5b2dd357dd3e4a6d3764890c526adf860c34942ceb1565cdc085206b52ae7caccc830f79c22836549e1588a1b164b8e21b37f3a5eebac4f373e0c02db5605f464cc44f3259ae7390617bbaf9c34389639f501d7147b1599769e4f0015a408944d9033eab3858b452fb4cd8bcd151789cb99d39554102a03023d3b0d2a48e77ac6c9dd2eaaf36e0152ebf34a971cf84daa55525ce70958f4ba75e70ec36188d9ea90cd02a75d6d98e28fd036ad5d20a672b23d1a12cbcd8d59c00e02e39bc8e8f0def03132d8164a1f38694999e47a2b4af188c953a52159321dcf0b3c196fb2be2b2e7d152b2d117869d912e75fe29b53f15d729da598728d71180e5164b09688db8016e3d70f6147f495a157de721ca97acae304d813bbce2da406161d5e6988b834b20028aec812ab2d924fcd2903c67139f3bb169f9e879ec10067644d4b5d0a1fc7da6a783347b430dd300afa4db1bad6597ff4ac6012fdaa08b0d2627d6f3266e6e4f41a9fa81299fd459fb8e69135511fe99c06f4f3ef3a013eb664e262f3deacf728492ee1684e66adf9b14ffaab46d4eab318ed38a4f8013fc34a7773b6e85da3e28f4c56a818e56ee6eb8910e7412f6a567890fbfcfd0ce8b7490df68f5dbab3743cacaf133fd437c246f84c85429977a2169dd2573208a7f7478b643d3384d4cf4993432e370a24ba4c45f54e0fa930d628c78c0afa000197d3872d11c4380e1589560a688a545108ff4ece582ad236b4a1247f21ab04f2883a7b071f8fe6b76e6a1c7bdd5e0b72089d02abc7a63f4214238da75e180a36045613d126e5a95df787112b5e648812edafba03ba9900e166deb494f0830d4a3a948089359c729616d3aae40fa86d8584ec104ca48f1ab18b379dd28ee0000bdf395a0103aea2a383f0723dd9b93f848d0b858a86284b48426d09c954eb098104d89f3461beef9c71599b8d9e41b4f5287327d866b334297123f2bb08d30e8243c0e28cb788dea87cecfd120deb6e9f980b42a6ebaaab2a2faf45fe84fe0bc1b99314227a7c5d81d0b57d8566cb4c6b4b77d5a612999b58f345385251c50591b3b1e8b718a7ed554151248048ee7a99b15626d38baa3ef50034ce037af603cdd7be3912e7c3c334048941f19d742a2df5bff4c144bbb483732f6376cef800291e8aa242a730d0140352c29afc331637aad3559d18c86dfeeac4e19fd74506e2b045cf97356abce5a171870c1806884a7b5f628f45fdfb9b0f6735239030014c69ca95e64b3e2e7569b5a3b39852ed7b24bb0e804edac1c7c58e829bd23300c0e5945df027e3a69a88b80e215a562974cbde6c54577a5d2532a43b5e51d206374f812172ff7b3384a2418b29b4023ad4faf48a850d365ffbf80de48a98cc0a17e0c5bbb3d42ead4e1405ebc914d095171049fb98fd88e9e55b83847ff1990ff085c59fb77726805328ba223ba116bc67d88824b5229965681bf1d1b7659f04092ee317705ae95d3bbd5d62086d7ec8976fe1adf0bdaba251d03b371323f0045ac97107f27d342435000d1f333c0a0b3eb5e9b9b8a3a7684d48653f0341e80c836146637798f9ec7432779e0ab25a9174cac4d83c06de40d4fcbcc883d4ec0fd40fb556dd5a262eb7b8a8d2adcf62a94d7b4d803d9dcd6551b317178d7b7006d339b47b5f359d328862553a994590b4581a00d471aec42c8860d0097dccbe062b3675044a837d14455e4f0d54f210446d3f7efe63eb1d5cfb94c1fb54ec9e065fa2c540929cac0df8fafb10aff94ae38000ccc828f351fd4cac66589923750bab5ceb0b01f91b6d8b6bc0362a12fecbaa52fd6f3684636fe3ae89ccfc0cd60f859e46f51d62912390edbef0db896e89a929e384ee94b69b60b121a32c074b0e01989f8afe32d0f0b5964832a775f6e6e38f223863c7fa49ff8f4e9d47b8e40bff6b79b0ed9807747fd63ce87573588ae453fc8ed39e7a48bcf65cf8e68efe0b9a8d5dbad719164ce9ac45a10c1d9ba9e88db8414adc5fc9c0c6805a2255b80dd7560882e8e5298dc8fa8efdb8ba35550ee3d1db0d2e35fba023fd6c3cf8b60fa28b1c4f91b8685b255be08f382fc628bd0f19c9bfcc8e13e7b3e46231555e0a77fa29af45740532eb9d82fff2c0a24f80371c0495ea7735f7b8f831efad530df6bbbc7f8926d3ea567680397630825fda6e686e9ba7620635ac5e033b6dbc086c0d0705748bc115f9c178e3df487ae6790677adaafb02d9cb3d0da117431307a1b9d3e81e967f8c66caf07fb119bfe22348563d0e51d85d8567a92648be2609cda0b15b216fff4b12bf249c7f94b7f25bb56c051f4da3dd8d65acc669efa40d57d544b1cc88d80aec73ce991f769211eec11274d6cf45f13f68652eaeae13028a017328fb9a9c3fb16bebef7f8052adf6ffd5eaeb7fa73531a9dc5ecf3257079de20202be8a5b8e9b5521f82ab3f082b7e88a88cb75c62fd0e3ba053569810319c8b2d1d74f3471167763ad02e454a9c31c75c83bf70bdf08f0b798cfa0be038df7f400f19c2de83da5e5e644bc8b654e9f69c740d5ad9b60b87a2f6c01f20584013e26291f0a05fd13aab4a45573612e1f1382b82baa9d51834e0e0061f9012032f8682fb2b71b7a6d77d7c9239f95d6d2ac6dbe8bb7de39c2c93d86b73305a71645594c43e025f97e3c649cf79e18b4a7ece33adaa9b858e71d38bd171a0c5560ac83724adc558a55d70a3de19550618ec09197cc67675a8b0c9aad79b00eb0a59300bd8a1f6d2dc30019ee62ebce878e532a06c5d08a89d7b4d4d5c42308a7c31f32dab649227bd826bcd163b375d164e5cd6869b1c027b5861a07df37075fc1991d232d6e723dd623bdefe7d0630f398af3db8454bfdcbd29d78e66ec0b6156b3e00b10166f3281065cf63baa663ba8d0a4774d7135657e31c64955a00843ab0a5c3a2095614597b3192122c6f589eeca25f7700f881ce2e9be25d5af0e79c2550d820b547a42fcfbdd9a6eded0a12b98ae324feaa7d1cec89123c48b01 false -check_ring_signature eef27e4a2da883a98e758181e540d268f88620ef3d44014b693fc5f9c7cce13e 65c7c2b92cad0d1841d182336af010aa7b2c3a1257a7308e6c7ab4e350f9d339 225 c808efad8a3f8156560c1ea14906a8fcc729bb93ff8005ebb53d71b7fefebdd3 78925f49234ee0fae2cbfb218266bb6846f38285381f3c90f6d55b87fc79de3f 9402bb20bafd94f157cecdc5729d9421239ef54e929eae462b085ea4bba5392c a208abd81cffe242a250cb99a64e524c967df158ae55c53f974fc08269368d41 4639d53ff559975a3613fb13afb7acf6e813028b1d9a59f0c0bf6bf8523ad8db 3301155ceab385386a1c120f9b285e8aa7711159b5c2c43fa9271b138215f4b5 ef02137d17f514495773e43f4c8b105fa73eac178576eafccbccfdcfc5aaae2a 7d64f349a9c8da5035eb3dea05d548df4756e17c34e73b8c3f76e89ad760fdaf 08acdde11f2a167deceb0cbf49bb0c47edc36dbaebe0061c7055805aa8ea5e53 00283ea7ca3d7840e51a5024264ca0be61db961b8ddbb961ad2fda5d907aa5e9 5179d57b85fd5ed63a3e64c4b6a1cb2b825ad2c7b4fc45a650e03a930e11b2bb 2abd1bccdf7262821812631615561cd1a131aa6e409206c936a13ae3860a4d2b 72a70a0c8fc3fbf2bc98a48d34be710b31329998425c73649f06dcc200ad33f3 c5065c3fd6284099fa4bb307c5ce17cc9b2ea54c2f8d53aa135a7e2ea3b3df79 1e8b4f6d919d18bca5538bd6b789a52f87f54102897a09d9bdd53cb71d15c1f7 eb4ea00936f7f69460f4f5d11e05c898a7ca5991c7494e0f8f46b55bb7b2a7b6 3e1fe34c1fc02fab100fedc895062d11ee8a64386d5f7fc2b526286c9f708375 b737dc96477a290d5ebfe038f9357f33365fb6bc49a739c4a8f95daaa9ede139 da597a73051a20a7133228dad67218adb899cb6180e5c248521253572eee7abd 2fedaea0fe0177acde8cf8abe76422272b20df85b5be6a4c3eae9767db7a554d 5b60911e42938fffdad3a34ed2fc906cc2ceda592c8c10d172d8f7202026b043 da1b5fbea4e1b8c19dbf6e9f901a1b65b8fe2eab266feef81751e8a477b708cb 863e7d829acffcb4cb761fdc7e98d2fb8faad17356eb08c101eeaa3ccdeffa03 a2dfed009afed7aa9cdbad6628b47001973520e422f48045fb65ec51654aebf5 131e36b0912162445d82d322789a59f539fbc214dd53331c4b50304abae2d4aa 3faea3b8ab161e019d0da8c3d3aa7b618aa74b7a5b03e14e1270d21d07baa3a6 b310741b6065541ed0fd0f005a3babf114d471901e2902f844cbc094374e97fe 59ef95d9671a37d5cd208247590ed6d1a4ac09a696183a26e985716dc7a8c3db 2840d76d97ac799cf369f94a785edda82d4fb0c4520409dd03606c22f62c07a7 ebbac3fdf8437b20a0ed8ad98113e2711ac191822a671789d72afe6abecbca3a aa3da68fb1ee5a911bb2360a3903957ec24d507e06de23528f44af06ef4bd20c e967367de87e59b9c2a8e072ca246d80ee762b5c4db756a56f60fc24acb01fae 94064b02c4f424c3744cee0c9d65cba74370c59893f996d73db2ce0500d2ec2b 4c31fca263033ac18d4780dcb8ddf7fbb1cdd5e31f841e6e7d4d1ea8302d5f92 8a0749a749d67acde9f9798667154cf87fb82c6eec279206c02feaa93a6b0562 d130e742c07a8df2bb188f05137418f2237320711121cf2714f0aa561b4f71f4 bac4b10ae6fdf78941b61a790449fedf61533c9a02d02f1f92bd558ef197d070 335a8ed685460ed02dd313e1cdd54b8bc178d65cd70d281f8d8493bcd7e4ec8a 1b355c703ca75d5837ce3635cd265651aebcb79adb127bb064a89762d14d1cd0 d1b4b32d218a539f536089d8666eceb555138ac0fdddac1a6f8908dd061ae2a2 63c4d28012b17c33ac5303e093a812d239427a8efae3f1830c52325108c957d1 176ca6345d002d3ac55b9d60c10270d4a5b2ae81cabe91f5519c8e69d47a1e2e b2e1428456fb5d003bd0ab408ccadb07cb4b0c503dd43ab456341e8b6746f0fa 070477450a9dab56d91ac08963f05c4546286a62223b1d2482a049706b382937 3c430e9a119e73e2395854973d1c13c4296f6c282217d337edba77f711a5a991 d226c8d1990fd6fa5e029f98133acf90200b732d6586c589927cc7e3bd3aa9af cab130d0734711f4516592ba5c4fe7ec47b065e2b4d3853ea5dd5c05ed0dc3dc af5c66b80aef9fad3c4851ea9414f5036bc0c9a7a3545e507e6c35bda9f63c79 fabd6c02e8983b3729c49c91c4a3f21ecae05fd964b62a16362bd781521e635a 5242694b0ef55a474529766aaf3a31df7ead7d1c5793fb8a0be924f073796224 9a173b2e5e83e7ca46af4748f98e216a6b2e323baf9e200ec9069d2e0c1ba3be 88af89f592caa2dab93aa8765a7bb05d95cf2e0aab677ba3e9656fa613d96ff3 81d44359521129e9ce2d7addc9a7e6eb80639a82a0e81f4bb3bb9122a0b1fb56 981ccf82f234e26ea9de71dacacd8c0e98200646281ea39ffa9f364d9004f5a6 54adb7cab99b80e48fd306bf40e6789271447b3b1129f2dd136c6db50f7317c7 88023392c759f2163b5c5cbce8de93c7cd232bc65b9ea0403b0387d9e38a6a02 b49a0f7a34b8486b67f2e7df26a823e45a035ff9d3eccc4f33ebe28320034b4e 82a448d4941971d317c24166be3e38ba1bb903f972604fee39286ebeedab0b39 736a3a1c132fa5cca59cbc3d78b2a3edf7600f910867d68710724e332df52e11 b299b4f6fc51ea90865007c0d84427ce0e6bb0f4ce7122adf47638d3bc14f84b 4aa3a6225e9231aa269e069ba99935dfd5dc79a993b6c5f1b9b141eb45c39ec0 a973147f2b9faab0b55d79e017f8266638a14343485d4d34a007658c8294a448 40a18828a1f3898557122edbf71bd9475aeb6fabb29b19aac24d3abad503bf3b 66f014ce25a58b460d68beeda9321ee68d8cf2cba789a8d6b8c61610774af6f2 3c7c45b9b5d0c340dca84be33d58477922d5aa6720d600af1a5c4c9ea4002b08 4069710b05cea080ac4c6838ecb15de28ba8a87d00c9a565c8972805846d98bf 8395ebebc3a76e81cea823edaa8794ef92ef79fba806f25bcc6b2e11d3b29a9f 5861dcd23f2ad7eb4faada4d8c5ffb47a8887b71700e45c7143dfa521e3ba3f5 1ae9fd3b25fcc8bcf0572b483c2e47ce9e61d687a33d9041a93fbf6db0804fdf f8a5cee217efd65f2421addf0a34d0ea186ca829835db4758bafbff30c2bdc34 2da6610724285113a04a5acc26f74191d1e8920e52741a2bb37198c23b98a88c 043abc9f8699725c93ecf0795af466367105ad0b191fc6a1caa924b6ef1c4ea6 4a5ed419457d7f3844e7bea0620c916befb5cc9dd639e27891f85a1209f92926 7cceb2af3fced88a3e1063481b496f6b7b8b3d320cb07c9e4b2250f80b19d64c 497f014fc15be2fb2c8829eb860ed640d10377a1ca754a10661b4bab47cc2e7a 8e2c1ac26973ee72f60b7be1c864eef8036c7f585efee666d1f9fec1a33d20b0 0b002ee7b984291fd22364609c5c0fea84679a8d4678730fe541fd879a1b471d db4053f1bab9241d1a0c3afbc42464e5f17acb1c4275df834a4cd7924fb642f3 ca4f2ecf895dc232dfe175c0669b6c4b20ec20e4120055d9aa797e84144b95ba 6660a8782664433ac45be32b83f7afd26f3d8a32cc8d345a01299ccfe5403aff cef9ac827107993a7ae92dddc0ceb5358bacff78a7bc60090cee383922859740 cd68991e1474d382c3909834669c37c49cb3c24f9540f464df7f4a2b7efd0340 fbe86a2f7349c64ef22909d840a19539b44c5f57bca344df65141f215a5dc8ad 3491df0eb0cd232c99b0170c7cbbdc04204302b0c37617ff990b0e2760b58849 5778817ea20a8e73d49c8fcb0543dc129695962dce7ab5ff68c7aea416e2110c a1ddf2d62936b5c77f6501eeba9eaec1eb98628b037f6b7bea27824fb85ca83a e0dbecda21dd25c34d09036fe753e657ec247f415246522685e76bf5816e0670 1f5d908d69e0e57876175f0850b31e18b8dedf8a9d35be327817ef751f275ed4 19ad8381be1d9c43fcef0d6315e3c5037c1b5484f9ad80fc40e48c0c387f01d6 ffa371ab1cefde7e819659a36b4643af195c3657e12d9e62f546871e01976ca6 0d2cd8156c263349798651a306fe3bf192c7f3e4b34cf621bc702d405a4a4b50 2ecf8b75bc5dbc1c0f61bbdd57175a3ba4f002fbdb7745c0209925ad870573f7 87494f245d8218f567b27e0019ba9c5f0f76a0630efa0f2a5b7cae66b32c03e1 1526379fb1ad78105370e4341bef9e7ad460963171dfea3ad7c44af1d730dfe1 360b8796403fb2c8755d3a4cbad999175d84804f063ed348804dcdf927ed7725 2340692a33a9243da85724afc67ceca3ce23d8fbc664cae2937564d97e58f5e2 38b6295656edebc536d6611ca84df64fd2b0d55c50f271b1695a05b237e08519 6e217ca3cdf2343cd27994e06d0f95b29662881482edc8f6f386f5681a775b29 1910581f8989db9a55ebccb377ede6512ede84306e2ca3e5df70926fd2c39d1d e72a0e78a1b3f3ce7779835b3bb81325aca41ab3be49c6872b610105f8749800 ad0d9fe6d4b649a94728410e67a1dcb1a58ef5ae98d7f388e88611319012d32a 6b7a00c47fca62c250208978a540e0e94cc0a071e504bcc474877c9f863adfcf dc8125e89c19b8fac0b916c7d3225c2e5929c14195bc74b93ad4361e3d745805 cb1e3326ed208eeb1f8ba0b69ee7e84184f39415ee37f90461b6188f6602fa0b 2fb8f4506046bc46326fd8679318899cb6b0d52ea2c78adccf7c71c2ad503494 75383b4af313f45e35a9250fd102ec3c2c6853b9fb62928a232714050ab3f6bc a251dac125e90ad2af82b6bd27722af2a9e6123626a18139b8175a648eda5a1f ea8382234d591796d8c780ff5b391c9bc6815953f34b17db14781f4aa55a884e ee01e8e9956aba42a7d651cf2203de2616f930310eeca629878b85053bd52d38 f88d93414c4bad0e916fcdaf8a412474b646b06445e4d37e8fd3eae8ee0ec0af 444f3549571c602ef925cbdfe6af4548bf8d5f9c0ad4346d6a35ca8c80cba3cc 1576c1a2d93c70ec10adad48ae9e987b0cc86d9a1ec9241a8e20e1beaa1e85ea 5860bc797b10b7431b46946bc32484bd61ffd2a5527db765f9973d0e9cb7a2df 049a3eeb0af87d6db5f494bc8401d1f3d8bfb15bc8282388d23db65879a1e516 b502b98777ee70512f82d3a3ffe4452f10818f46325ccafa4a9645569d02b246 5cd29e0b24a27fee15b62578c7c03c4b7b9e8029a8c2ac11d3808effbf1f1617 2c49a35144aaa6461610bef114c5f254348ac02f00620330a2634de4d2a44238 6fc26339e6a5f0d01908e53698c157c9065d2a91e03532426a4024bc7d6f727b 12802f6fe2ac6bb7a15a60f6372e874cd6da17f57cbf2296a21d7e76621b6cdc f2ab475d9942896710204d9d9cf78d0d29435b6c39d23f22f27a48f166f5143e 81a80bec0d937417b3d2a9a933f0d59ce8060af8c969b0573d72ee7afe8788da 812bfaa2ad183a9842d325d6c586c6449392c4587a7f368cff776992cc73ffee 9c00160ab70008acdeb46b5e76a90d7344a9a0bed18b7621ae03bceb3ecfe061 4be527746f18524968a76951c1f7f4977169bf30aea23af0d29b0b4e67ffe3a7 5911a0b42fa950c31678b820767e834a168171f845af011d6f487832595d913d eb6af4fcb3945da3fb19f9868a6200d0c0985e234feff0a3a13813b40cdfe2ba 5d5eb7863d0986180b23698cedcc5995a580a4b46bf67eb73f5fc0acd82f29a1 63c24f3a78eb5602d15c5cdd4bfa32c3636418e059ff4dfa38746c79602be2bf a2482f211289f086baedec59b53e55be757137bf10a1b1d69030b73c57515b7c 809efc969c03de97a96e13ab4c74e424d2dff137aa015636208b1bb02edbac5d b2b97a88cb15b0a9997fad0aef437aa980dc03b0ba5764edbc9cabd176e423bf a416e5d8438416f2a7a18a4f3c6c7f9721e90b6bd929376d42629410f3d3fc48 d70cb54099feb38eeaf785b5c3b575060a9f82b68e76553acdecdb338074d387 139ac44fbaf68e5fc6e359bb3561f79b157076937e02ed817fdd96457e8ff469 e6cd131e5eb186b73bf17db2b968c7dcadc97c7f26ad8304eb002cfab7c2906f 631aeb6102d4a324943c8566abcb3bf649fbeba454fb0e56c1cd3a3b73c7a5d6 87581c71a5b582b421c7080098cdeb179311eb3dd5169161f57a9b24feb8193d d86eb4827bd6584f72637997bc35898865f846ca6ea518ada0461f545b92f277 e34ed00f896fc5e1f042511b09e7c63db63706f69d3eb33d2a6f31c5d59b8cf7 eba6e195c0995369c153384afa6894e1dfcb142fa23583df1227b1f0da1cc8f1 6e8a7c9e5e386833ccb41c6ddfde64a2b36bb50312400b0aff6a3bc87dc7ec15 aa86d5aa97415eef1e4d343b197d56df0153b429e5a3f3b5bde8285d79749145 160aabb72390be928d4d9deee4c87f52c8d3cf4f83c6184703250e81e6f4eb8d 8188fecc89427131c20eddd7963ae081a3f46291f81f26faffdecf921a2de353 dd4e7e49aa0140b7b7c616080fb96c6fb5b63b4c1554a1372d8a55f31137a2ad 8e66b926f8cbd177e993a6a479ad55b31e139db6177f2b19ad646f2eeee3151d be4cefd54aebf81d314712428239569b0ffec56972cf304a7c16e23fb13755ab a737a09ab9bc7a86b48f6a4ee96213cc80aad17483369db22afd85b07c57448e 51dff80a3d6a077bb4b46870fda128e088b22e25fe49b91b6dae913e7d16b747 a887f8f808e5a0dfda0c71fa1ff0bd1bd772f6290f5c57aaa8c27c2d06292929 c4b1fc513c155bc327333607a515c5650a7930062b147d5b2c49d6c4145952d4 6275915e4bfcce38b85071576ed7a7b539ba587c45ddfe39085da81a7b13c672 7c093af65a57d4af0c2b437228f76f05795169b4ccf8d4478eef4fd69c967c9b c44f2ab3f7c3bd15a756af6b5033aa1c7c500d2f141fceca2bcddcd4b2baf2fa 315465d1e3acabc70958e67e400f23a85278a6b669dda98a302df6b952d3897a ab8ec100f7e459dadcce90372571da0029ba3b860c6823f0dd7bd99b42468c17 207753ca6a5f0f1fa5890115183910a32d2d270493522a788722d591a9d9456f 13507fccf39c7e9d2a1e7f2e15c1a6b22ad97185cae59a607fe8b102f99cbf51 b2b09836f414ce167188a9d01fdbc69f3db111c67206f583fcdc6d2f00638309 2947464dfe726b06d99ecffc9dc64b9b28d10c458222b1f67c2b8b35dc2197a5 e0f77544693152d702df5dd3e4d795f82b23066831f53c9bb23a8ae2303e7d34 8b3ff66b63378621713c3aa737f36f8c5635a1ffe9d9a170d69e6f0cec5c5225 e93772a11556ad9e8cf4a084f742b3ba5ce2e575f152d2f55cfda2ee9f34d14c cbfe6ab2404ef0212356e5ddffa37be67903c6f109c30a3b03501e010350d556 8974e764c3f4ea6b0fce4dd857920863b4c613aa26f1cc305347d5b21168efef 2d00c72b2c0074129454de2cd835d4ef4cf8da6d2e16adbc42ba4ffb4b05ee1a 04afaa99e00374806315cfc8dc0c59620c03c5bc94666988a8a93e81e2c7a7ba 3bbad8e2ee49bae7f8768f49aba594232d9b0ecb1e1dec96362d4b85c2f2b603 b902dfddc51fc3df12c48cb1943c2ac15812b2f5f1c3e25d92a5697f92d5bb5d c6fd255ea9e9fa9e9f775ccc631399f3e91a6f3fc92189001860c764621cc26a 232da0e013d1c1f08a0f60c2cfcff8a90c080ece618a422694358e219215bcc9 f1eb06ee68895839a845b26ef73c4ef6f4edabbbcd551f29bbf3e29394238f89 0779da70ef0b7700215230bfb5c43865b4c82c43edc7bb214ced95bc37fc9a71 2310deaeade4fefe8321ca4e8f7ccd9a16e9b9afcc72454e8e059e440da278fa 098aa08ad28306c9e3f747fa24cc67b2cfbcab0e9adf34aaf7d3664b60e70252 6cd81bf938624f9e59d55bddddf7b2fbffd5f2f3c0236b3a45e4201f6756424e e012b640fcd94ac36e74ea7acdb6a08593ae861ef4353fa53a2ea62e1674c956 593d878e654c85250479dd9e8fb1aac0fd9e4c58cf5308f3ba125693e8ed9d82 8f7803e2199548e0673c65d32beeb67aa6404f0d8184789ce2e2fc257140e90f e7c430e10c4c45197f147811ae8c49439c70433ab2dd95232d0b185ca350bd54 db3c9604ec1d55f78aa7628cd3d22d665acbf51e539fe2d3b03b86715d091690 5062fad7d3b58c246009a4e4ea8ce2305116c70b41f41f619dc3df881d78711b 8e1dcf4fcfbbb90a4a9201260cf844012e1aa0e68c5a0a595271d795c7834b35 e3ccb4b363f6edd241e35e2bce72dedfea669d1843fe50a4251b180e8aa0f5c7 c38020f9338a1f4c3ba87d763506f0e694532776dc5329af0fc957e98e79b959 35a0f0a70fbec8f72237b58009e1a17ba9389a876642767654efd7b137875cd9 c1a2753032f7d59d089535dc4ac296e4a5ccbf09cd506b08c9aab59184305879 7408231dbe88ca7c9181205ae590c63029c41c2172c2a63790752c76fe976825 850e070a4719d2f5938c4485700dc5b1e038f6bcc109ee661b352aadd90fbc81 1731206b2ecf5e6d0778872b269c43d65be0497df21ecfeec4c10ded2336c782 7535d9a950950f486344d1110eddad1d9e7e612a74d84ab1faac4ba9f9abd6ed 25088e24208ee6e9fc78406921e5a4cae7069cbbed113dfa1c09a9b67e472449 f4aa5850012a2361184633ea4246149df2eba64cc3292c88b7e55609376de868 6e551b666ed357e398cfc65397b9f193c55c67a4076412c4d7a51ad7a650b403 10620e14636c6aee485c87e2c0fddca4ae31f2edecbcb1db55810016726f6e8d 130bee1dd5bed6ed1691ad501b5b779c2991f191e8bd2ee89120e773376be2b9 1d06e5eaa45ff5ba33302c449148cf4dfbd48c9a592e89ab7b81a6ba2905ef73 1c0293e3445062a72ed30b0d05646a7dc95c12fd16197b36057d1fc030eec890 1bf0dfdbdeb5a477e39fcdddef9af1bb0cfd909bcd8ba49d44a6c80feb85e4c1 25ac6efee7640ea7b8b4d26c6a901fd6c64df6b2686cfc093207049ffdc33b90 51d45fbe238c7cb3ad4f60bb471ab0646e0a6fecd1963850add0a7fd8aff25ad 6898a7065d8aeedc19389ef4d1e640e14815ac9145471b28f0c3438dba6d3c03 297e7b3f6f2945271b5a5017938af548c9f4d49e5907bcca3c93522eeb099ef8 7aa7533b6c617991c259911da4da5419e087958f6d1087cf6f3ba5b90185358d c6504594333664bd12f38a3cc8abd9eb15afeee4e4f768f6972fb206a5385a9c c29d4d53dc08ab9b63513254b03e04e7ef020c19722246db06019a28647f3842 776140b4f3825b52ab73546e3958c2d708bcda6a7280ef015dddfe609d53e4c8 c96d1ed4448ce66231357d340ad5180d99bdaccc9da37468cfe3ff30f1cb98cc c23c9ff6a354ec461fc139be24d0149f5ef5b10aca34f599a65c886cdadc506f 7aca10004df08f10d7a4f9ba0a1bbb5c427f1b2faafcbb470d1131f939e79ca7 90cbd85d63b11ea06301679074906a59bb6d68ea23fdea446e5293afb654c604 e3b181389b2f94b4905f3dd6665fbf07b1cdcc412849fa80f046cfe021ee173d ef91d0d007f82ce9abc84107cbf4774838934518298d515b7c7fbc4f3e9b1f50 1f8cda664d8d62e9b18cc70ebf8243ea57841f3c39c74db90c2668e2e7f878fa c782855f83024e323efb105d38b4e9afdc30c01af907c3a6f8a2ac05f5cbd131 f9a71e87677c416b11dc2605bdd65f658c4bcabfda834de889add478550caa60 d92548ba7582e6664a740f47f7f467e34a2bd9fc62a020a327ced1f2511be7fd 3024671045ea98c2393a6562375943d1df45307c099b65c839208bdde325d4e4 d004038d68592c6b674a65f2d3d527324429dc60d50c8dc371c5af1372a90c2d 8abd4c66534c59825ebe86ebef81a0ad5e1253231c1c27bd5b911d4cbe73d417 3a06bb37051fa123f4f78ce0d86089fbbfc3581392b7130f2a38782248074996 a860e702081e8edb99771feda1360222c1c2f7f6129796de0e303e947ae77493 050d92f3d33cb399717c4ce2fe2e9356e9f96d3c238a12307b6f3d1885f251e8 c0303c83ac347863a0d6e0c023db4541bbc4eadd75e8eb420cb5269b375203ed 82dc810b70babef5b698cc3711a10488bf367cc4a099c6912be9cb33610479cd 3e657a41a25d8aef5f3a5c010317cd36f9558c872bc76e5849cbdf447a47d104b70a5531125708061a2256d8e9cf74d4b3dd5bf6203073ae0f2252a63f559c0592cb9c1169ffcda7107add1bc1a364725f8eb5551756b766a59e8650cdc940084d65838c1ca62f9374e3100586b68c048459b3be81bb28c79dccb8b7da87150599b0481eee5496e96f3130e0284277b9891a6a1b4f309ea09bbc4868f7509d054118186a28465cbecd9aae7466db5a76b8e1a8527705dcc9747a3352c39b4d0ce85d0e1aee2862d45c3337809935a123ae7be47187403d2d8cdb820bd38a7a07d8b907b9f19aeabb996999370f0f33f553b8925bc7447ccbd545ac407481de07fa89d3fbdb45d9aa5e6ef89bb3a99ad147bbe7c9663d94c0ef926f9607708d066cdd65eb6d59b402ceb05d444ab912255ec4cbe6b6e3b7399b6edaab270cfd038a01bca663dd4f921dab9d83a491a3e553abaa7671a885923ec2d225d45e7f0e2434a4fcae1d419c0b0d502b7a8cad2fbfb9b76e6560e54e8f5f833c3f3444066ea13e785e82280358e9f7f9332cd1b03871caf46857344b961eb667c492b90a481c649cd5825e77f983cb54e99daff9c9af224877a4d36635905bcae5be8d08bda2920662fcaeff9048c320f345f3bf6a893adb567120e0475210c10751c609a60d1b138baa29b4e5350b6ef4be1754830b6070c438b819a81edda015dee404ec6dbadf12416bb5712c50f0c2fefe2b7b1d6ec9bf4b5614f669227ba505d501b168197bfc76c50b04c8c3d8d18e527f5917e7ed0b4aaa2c2c57ed0e038d130e88a28b878deda8c38460e5b7bae5ee5df5b5e44671da01b79750493c37806a085b14a6f51b17f0eac5064cde1cfc29afc9117c97ba9dafbb24f831efaa649f0d73e2f261619714dcf72ebfd5a7403bd270fd8a2804ae9a2b6d2f1f749c18fb088ce12f54032229fb2db55427dba90a01a5a53c7c30ae59153d63d9cb876587002a4547379746f7b6e1821d6a7d1aac0ce6540b0fc1271949067445da246efa07bf754e94251f4a0e910c3b924b52b09feb26152416f97105cab5cb41e1e4e40db8df178a2fc895f561a48f4941aabae28df0247557687d9b06be18d3726a900df4f613611cf146a68612493915aefc58786b949d1bd6fbd1eaef0fea4a23210da65b0ad9d6f04a0aca03d3cdb81b113a952af2148f3654c39d0de0ef8ff550007fa56fafceef5212f261d75c4d3cbc113eaf38cbe74d6ffcf6f11a7d9b9bea0d99908817e1fa6cc7a132c0aa1b2a2acf4ba55f0e261c5894f2f9247f72ae440e59b7221cab79ee25889a3a25aa77972b59f295a87473155dba625e43ca8c93066b5e4d36769b96ac2c7ddcb621e23ca2c028d62ba24e083061f733db5a57bc0be437ca2d32f55cf2d0c65f1ec21a71919c28b6e636f8ebcc0e98abe2e1da4e077f6d6a52dc9dc1366de12eccd05dbf5c451a4ed7d923d11584ca09fd837c6104b5a5bdfc6aca1bbbfa7f15865e10cd2d6a3ab064274c78aa1c5c66ce25f42c0193b60b8023a4cfba5a5cd9ade0074aeb233eb3e489925044ab2194bd5701500f7346f2562e390fc3b5352ce26429e038825191d23a97ebae446648a23b2d13095dcf8fc18bd97c2763d0ced610f74729e6eae9bc15ef2282d296e5198e622303a9a79eea6cb7010d79fc50e35107e5b88fc4977ec67099d95b43b6fae8fad0055d354fa6ec3ef96135748b28f8900dcb43ba2ca3e835c2ec8f46ad44c25d08023cf53785c5fc49d5cf06004b79fc106750fa508dbd6483f52d795cba547214028275d7ee28f3ae945d518395f21662b38b6e1ae718bde23db7a85207b4ce600ab72953205a9fbe7637cb79896335c52e0540402eb8a767faa5238c07dcd4ca0a3d1ffd50afb96a99fb6f2b2ed5ddf70f54f4f3934279b06b0b986f394dc0ea06e097beaec9e59a7a65e4d37f2fddeb15a29bf2ebf9877f5277ca0e760620d60f4e3bb057b4c8697996dd652f4f911d9b8572106a70cb57573f96d866865fa60b227efeb4a5668cac5b7dc63e19c89102bc5b58b455171ee21d4cb27bee490707484ddd74647a2a839307a298f0b4bb1bfcec4953837902a3d6da16f6bf85620edaebb502b2bec6def632581e1a9ee69404f10b85837c0467d6b9fd3318a2b40d0dd199d93d34348b5a20a21e63eff5ccd4a4ffa8b6dc35c3ed76b0e9e2877c06fb9b34c0334a50a15a4d21023c3c9634e8fb4cffb27455dcc58dea0685556503e969f66f787093b9806911b2d6d13c02e85865b75bd26e7122595d91e32ba203a43f1584a013f50f0797b226ab2a12b6ae70e67fe12026414d52cfed1f18960a8feebc6354e704ea7cc5051024c923e66435e821619cc4265454e9502bc0060eb84914992204740a877e922cf354c40aff6d43af10594b28ae5630e1987eba055cdb9bc03c4058dbddcc5b366bc61ac92011c7ff3ffb38f4e534c9ef7e20070d61b2124d2f42bb9fb1078059b19461010cb44e8dadf2f1583b88749e89f497014de9c01e3b832a2beb1ea0b4278c02e919904e803f94d7adb753465ad29dd507144b62cde1a98699d0b9e7650bd57a24cc9746fff7f6ae19d01d85e360175409109ba3a7b94aa13c8cb408b6d6e85ddf8d9488fc17a78a514a461317940f3b03e06e82c2b7c0f2497fe79323c8fe1ae69930a2ac32999f041d9925d9e7cb29047d84fbf5d449fb74919a29b66002bc3d653a185616588462261c3dbe289fd801ed4900b3772e8c87d838b9706025ae1dc590cfe4cfc7665eb84731cf7142d7045a24c55534fa12d7f84e98917c3b9f9524781d274474cedda6ed122b014cc30ce576712e96c853de5899d1d223c684e6b4e6dd1e5d93085bfa7e5de516109d02f3f07374844f3cb389fe99c22eafb446de95dc37b902ba598891d047cda90703a79e95d19298ad9ef91196acee2e85170e2a3461a983a46588e2ffe399cfd900762a17c542f0b48a68f8bbcedfbeba3932d46e6fafedf82ac2f670330d2aea0a3ae23c137c37eb3f14220523dbe50fb278c45faaa74790b823ccc4d50de3a20f077d256880bb166fe5ac131f2bb7096cde9ef1cb9a3b51bda83c8bce778152007dc0e519d356c83a2ac29abd500407935353e2ea14cb9b44fe168103bcdce304c31f9c4dedc2ab4b6c929804315886fb61c2b0433b0fab5450e9533772c92200d6e51ea6b811e6b9891ca51193698f13d38b1ff719df7b831814aaeb7cf5a2029413bae3373c922a65e8b283095cc3b64c35fe0ab79fc859a5585fa5193b1f0e40fbe1356c00be8a4efc566ef032010b64531df903eb5d110eeea48dd8ba8a0381e931d23873b7a60a16ac3f3dd26c721d4c6c091d2e90ae62b16fe377c2f305cb29af2c3ea9fbb8bb1b9a66d6d2082742e1f1a4ef6981d855fbc4c4cd34e80fdf0dbd91d2f44b0f4400d014af4b63ac9eaad00b9acd05c93407083313c46a09cac1ee1a2caaed0e59970911c0a3662b3a1c505a0fd44725d4daeb85421734015ad66391f972967ecbe7511bb520d0e9bf67d380d7fd0756687f4bc141282708305d45cc64a85dd50bd17e520c804b4237de100ab5fdf1983bb0342610527b0a98e5cde1ef17ccc5e65b4f728e2c28674a9f7dce8c1b442c2f188bcecd77ba0dffd9aee81d1b4cca999005618cb9c4baf63b2fa75a5837ba422dc626ae5ed50923b3eb003edcc041ad62797c6d238868cba39e509b863804d45fbc7560db790648106c801f0dea07172da81f288397e2e830c37abc9a31f337f719ca9d9df6032ddc726a4afbe9d21255c95f4e0023473710a76d9d3d5a7e9f767a64f3f7560ca4eb560e20252295d248cb36ca899eeedfeb4bc228074544b50b39e9a6c42509ef9bd4fc12aeac88aea8da17a273dd116408d8f251cb184b03768b07a4896a028f95a2f497eb34d339e03bcf7727dc07c8c85981c279a982dd4bf71cca4f29026b7a0ff3d9a2c06016bb20cf8d5992b8e34e2bf7a3ba55525f216d74334bc30359743f1968ce41f3c938a89087f17071abe929b7c5d1d99d49a91ad638cf6901b24811853cf57587f0a51c16fc36c8e52020d33500d573061c1fcc697cd2830eda6cd7e2b880e14134b2fbad0e8f1c11f82c3834cdf6a9a1b47605055b093800f7c4070bac83c53a1c8cada5d1aa2e45b7b8ad8764cd27a2e64fe1b2ac9ec8031e4e448aa13f7eee046eac190f822d4d6110c28c78326c0f389c410d5bc74700740c643add9d6912b8aca7465bf922400eebbe8e1611074200d27895e2cb0909b3e8fd4c0806b2d63a669950c2478534383355ae1cce773f68b63adb8e94b7095ae91400e4bce46dc00d0b8eaec4b9926deb552cd29445fb8905c498c6542b0c39a07afbda6b2eed4acf3bf4cbeca5fa6460e7f972ce07574fe960089f72270d6346f1802e9bbad7ea87f43211906bf27565b91933e286923e70df82473b060dfa25557a46d4b74d1f5164fc283cda5e2d9342ffad798f13a10577878f8f8c0b8cb81e01fb0f6ba0348977d2a5b5204926e924296b7947e644c05b9373c67a01475bd654c3da8742822e7f62527c0e0c27b4db2ea1ea1e9cc6a1864f9a99e5056c1d06fa7e2345106a6c795c1f2b55be419404e803674f37cd2842c388a75e013bed49d50778a6058b994a2b85cbc4e0e297c3a7fe43e2621ea7c8d5a9678504cd7ce0e8730344ee76cc2979188b06c2d7c1c588036a934582fbf4d25d84680df944f4c4cf169e94adfab7d807e3543f0e26a7af33809d1926ce1005d208c00e8817011657d2f987662655c6e2a8814b29ed8efa890429a9b3a65629efc06c0204c52aec2d9fd307e23cec1d46f1fe87371876b1373a436d3aa24f60a4750009fbc6563fa9641ad205e733d35b83ac866245fb9bacd57033d350ca5294a5f9029a91f76b9086ec8e09e3d1e009b144f6d53713bd26c3a60b47f0c4f0f9ff120890cb2709a102443bd9a12cab2aefd4c8289b160715b24bfb29571fb8442aa60ea5947756b1364a54665351400f2994b7f487345a3d244f5acf567e90b0e86a0b8fcad158bee2c54a18db92b7dc067c48c22bb812aa8f692b0fe4734b85a2dc0a5b133d3e39f3f780d66842f2f08f9dbf811e836896289222f52f6fc9f6a964038a9833593e80d22db2b543968f01c5ec59dbc1a7336932d1b59d7a9255a3d504d5d59c1ecdd579de5d27f91b96b65dd1031fc3f2933dba5f55704953028edd02f3cfb0ad3684007fa8ae96f2a67758952028e0b25517290402cb1b9a7ea0110decb3a1460c54f5dec4cc08134db07a6b698670bb9c23aa49d84b7283b792d6005a80002d90807a6a34a1f1e965bccb5be9fbee87d7f9246b0000dfc6135dcf0c7b4d28554449b82df36b8200115c7e5e3ee23020248b0de4deb1e6ede43bd807e228d0e0f2f8229bfb696e96953f45601a9ec063e4f051c897978433fd78c404d0fe0e427584b3490a123babd31543288dbbe2739037637d5721f4c9dfe4900c2fdf55c519f35a0d600dfd3e7c28e1f565dbed7aaf04d12312bc2d60bb33fc0d6179329b9b633a7de6404dce3f5cae4f71fa6e674baaee4749f8ac0a39080e0ab4b1bcf163daca81f2fd1c46998a82eea7d3c514f546c0ee688db5ae0be9c307531b9c40dcae1ac51a363cb2edc0d0a7c0ea7784f00fc70bb570978a1ca48705bcb1badf60bd16fd0b4343bdd68539c129e183f0f38457368394a584eba9a702a9c50a77212c9ee148c9f5af24b5ef75a4c36d265c80057e195504eb5fc3a803dd3d5d10cdf20e57f2b39c104ccb6fe07e794e811f151ae3d1f5bd9a8f057a099baa688742bf624256de550d98ba9d3512c8c27b3d28965365b3285848f2b80d16028ca2c4b61ae384f4260582644c5766a7f67811428c5a7f8911b62d65f309ed765d78b02fd6b742320d9280062c57c781562048b6139f3a7a0cf34c4a63075fe4da8f696cd3ef3b28783eab12be9c09575c2fefbd5b0982c2c22f0f009209cadd305fe17aeca064c7e8a7c8a664c84e872d3a070853d2a65f1b10c9bd51059a42d39bce2116eda20b2298b9eee8038b0bb08f9f5ef4c154a31b59afd4690071fca3966d018b7b6fab2df9c3cd263561c39135ac338be4a4641faab7d2ba076adefa2fd94e35256e662379e421180d95c700b3ec23beb683470779c142370cf9e60e97bb9a0da4274bf20134af77d47a4533cce3aa69f196fd660963fa3a054e9c4971a9ff56f950e7f615982f102326e65853f768ba3120f0902990abbb0420c410c5d6803bdefcedb85a0fdb7bdc228fc2a15a659cc24d6b681e6e5aff0f3ee791c214fc18964e9c131512fa5d9fe1774411861b4d28c41cd5e6ca7a89079130d27219d3d4d1d6dcb582ea85d95ed0ad9ca0a95d6bda2a4c0804eef6470f0dafac5ff62c0314d115ad54fa31de81544f0a916668f62c76ccdd251fc93a002058540077458a920c81be24076e69c38df0ffdb0a04669292e43e93b1be8104f6c85bd71eadc550617d55b8ff0780196a65fd68be5b52595976a21f5c8fa50991e0cd3102c62f28d1e46109b20feddc33ef579b35cb135aa89ef6c046d7e4065526d7f5f0759469157f985b532a0fc178f75ac805034948414a59a339d27507e1580c2fa150f2a8c3d4cf03baf38b71591cd8f1a3c4ae53329e40b0c4c3ca030665bcf9af85ad3112844785636d31f2c464d8c03f3cd4145296cf49ea769f0d387c72a0c04288721dc72af5fdb5ffb05eb28c26cffbe9ca4371717712b2400a475bd8cc0b89913b93ee450ce60e31d55f280622d44821a4949c73a046a49706e81235f980bcd41b33087a05f5c36853d7523a16af3a9ce98b7995ea42356c017324f25cf2d202294d0d3fc82a101a5186a31e70832fb295a3a4878f9171b907a0bf135b12c1e33efd8b0692f5c7500d169d9ad68f1e60be3433204cb9f3c60d4f30f5d554847927504ef465a7a66b502b4d8432586c7f310a05e44a0deae40b996b6c84f7f0eb7f6f61a54fbaf5d1185ceebb897ffa1a8f1d7e21efc931c50e05354ec854d147407eb0c39bd169a501323eb20cd3b2277583e1dbb98444e00f4e61b430e12e39e5c4baa7d03c6047d3fd5eb990a40a8a2e3d7e394f1e3e470bea2188bc39f581d35c65acce1d87cb001b179fe82080f1b823ca40ba880bb70852737e15c6cc924802f0e95480b3bc4faadea8220c9bb26ff2f9fe6a2443d703118738c53e0c3055a23342bc5fc21b1a6dc4783a8d258e9367284112ed1ac307a1da8d73bb72767ddaeaa6c89fc3f80abb65991f2a68c455eb2af7993e85f50a0d36dd7be5ab2c1e873b165faa4647b44345ccb6d7bb82189f65332d844ba7041e004526ed6b6acbc3b6b96c6cbac2b2d600440471c674ed146734212960ff0df5067dfc2549eb80842d48ad3521dc30003e2fa1245d8869b81276251f229b074841eb98e260c6da798386432bb8ed738f40513fb2ed797d2a3cf37d53b0a4062d490222c57c57e890dcd0b6cc2645f8f375cd0ae2f590f31f0e256bc6a23c0e5379ebde78cb14a27f28341233c3db46753d5627914a9535a1228437ce13cd000623cc905650e1e40ca22c270a3425ee7d94b25a700efeca55c1da1fff4e0d0b295937392c4e7e3dd5c4951011613c3624deb04e442e4db7d8a038c20b8aec0fa949df8edf0aae689c197ef54a339bb2ce983dfce27a73c0a8d7ecdaf6af2b0b21dbfb0bc0d5ff9e8bb0d733207114daf24f03726b3fcb40c3bb8c31f82cd707f9dd39a0f9382e494fb77485713e707f4076595bb415db62f625c4b0c9b952009a54bcc8f4c33f37ccf3c6ab686b9924943d7c69ab9f818f60fa6e769b09bb09d3a2c0cea64e15ac9e4e1d609bf13d3c292b157e2c72ba622bdae395a032e30710deb22d17da5a8adfb09f0efa0545e3a5735eba10429a12039436dfced880096e490703a7d0a2e5999604e256236d5518d3c175a864266ac45d8ba0c98b590df8f0cf4ed87b83a7c895085038efb22353ebb9f5ec07bf5b5eff63afeb0fcc011b94a787a0f47e5dad7af9551e249577306c437cd408e92bf697798dca85d309daebf109954802330e5a292414fec56c9f5eac89d6fb445812424514846f5f075ac437f6af2cd6e594677899eedfa1d2807b47fc3641c13f282ac08b465305006b47f529c99e2b3a494b2be350c5373fa6645a1887ce16adc1144498145fec096ed6c8ea51fcfe38cf3781fa8a63c595c44b07351b9a22a4165bb0a5774eeb0cc2fc4b362878675825ca55353d493551493c70101a667af6eb86cc478d33ca0af03ca66bef06ffe634b81aec7fb39db7850bae3a3e8ea90f15dd8011d2c79108815ca5cfcade2908efe3159dcd82548dbd8b20c593141023954dbc2dae17b9040f77ccc919945cd17a6c77cef31543533178446371145594b8810ceaf7109b08870b41164ee21ecc16bc96802a8de0f31abf6726701eb48140f0b268cd77340838956563177d95cdddc4838cc15858ba9aef0a3bfab0ba7f1911a70e87def90a1f859f568e5a8cf9a7032a8d01b5ffa0ae9c271c3ec1eda30c25390016c62f0939cd5916df2070a9f4b06f338b4c336ec2c13a8a8b37723f296905d007cd4d009676cce1263113b3919d426c7afe5d66106ff4b3bbe9bbb8068a80f38415720b9756669a8b9998c3188442e5957d25fca8d7dd5b25a431fe636698233489fc032af38c41010269d6a6ae06a3ad2641154dae18da561ba17b0c68ddb04651c6079bee1b7312031ee0b592653b400233d03d7f5cd0e4508afa5eb223307d1c2101e95cfa361e10cb174892fa6560465bc99ff6c6b03c9afd7b09de53e5ec3e8d022b97973ff79a1f0506b2380f89fa6131d64e1bb569f53a2f3559cc785eb3a70b1d47673b5e06f9b2d3b4538c5fc13177c115a7b40ab35d27f0492da263ebb206dcdc85fcf5f9ca577290d2670634e34dddb4b3c389e9588b3b605107dd1cd6083ced4f53853c7b6119e3c7b066c7aa652d35a43e49ce8d686a3c11af507e890da07d20c5fcc06c31a9fcb7811a00b5d7ce4217b83746d220e75fd15c5d030f040fbfd23bd7706c0eba1a5549cf5a1254d4ac9bb98f110d6ac570a81d5bf17109e1d928f460c386ac6c734e37a8b1f29e122d5cab222c1648ae330bf4ad3d860a0018d8a197e88fae4a0135a01cf4424bc8f30b819e30bce0c8dfd8019b66c30f446768f1d6913e4276f87fa9acadcd5dd717e8feebdf564c59e8412789a9930675c66cf7602f9019ddd7329d2e3511ae3be79800ef6c59a8dcee47026ea12b094b0bc7899c90b53068ab07e4fff9158d3f8fa4809dbbc16c34dbe0dad0d97b0ecd633aeede3363fa3801e358006f844e0891129309e07a4e0af46690d8b2900786c797f10160bbba9989d6dd85a90160744e7b07a6736261762d6d1ca99c9c07ea8375466446f5d4399945d131571d7287bb8488e3dc0c3d0a4e9211808ffc061b1d8c50f156512dd0ffd70d3fa08b6e38bc3b039390e8f12b832ffa28ce4a0561e083541723607cd03400013f108acc2495ce6a9f0a7bae923ef97f6c46510632b47fdf2873166cc1b3d3a6a40ce61f7f8a16e8a46c7beb7dab23bc2d0bf500b7a0573c78f89c955159261b17d07081f667568d374e2a10933fe66ad864d90af47560093baf89d468717bac7cddd95e2b20a362cbf3456d458c89f228821606de941216b844a5eed8a43b13be0cf8503f9e4db5588ed48f73cdb8a337a1a00e12ed033a772d03c718b77fe0bd3db7167e0e1dc4bf4b3535c8718e443cbdc8017445e12ff21c731273a53cfd7117e5223f6d7a7757592d240b46dc036c955c000ebb2750ab2f72ad1a0f364118796f960d8e88abf2a783da9dba6a6676cdd2051c3d4fc83e2f97b382813b013ef757bcc75526b0273bfd4986e8051311db18086d468236c59aa7b85b91efc1b28ffe42cb89c9c643324202c2188186d4d3b30a25f04cdccbf026df3aa1aa98ef439719eeba3c00ecd5c3598d66086f04b854027889fd2cc4f6d8518bf191453081703961610fe36ac945199e68376369fea80d15e9b7a71d82985d816bb94754544af5b53b4d2e22c2653d9cdf17b27dc2a509ed8e1e67137bafa78465b8a531654709df232134333718c15bf011a80994de0f9b92ce83f63a31183f93370eba573dfd1d86cfd7e7b8a2422e40bc00e712c70719fa4682ca1c6c44ff804d0d59dfbe66b50df111a7ae9343bbb495c6349fb60d1bcbaa30a4ab72473d52fc2380caca0a5aa6621d53876210be2e710119c0cb048c089f070a78d15dbd3fbd69bd57ad15db6c4c970ce915c67a51e3e91ff3e7093c5297c7b16c16793130020122cc87e4ec75d8fab88d463a63c6175ca7755c0c960b64127ee00697304e555df03f70603c52a8eeac25be46c8e699651b40d600ed8ac6869a14f2ef6583d20980c73b181a6eb916a3f83936b712866bc9b7fa0a6a7273db3d74fd3027d169bb34157724f53e993334c04048e4236332309ffc030351a9e33d000da2b40d2b5f725d2a29809239baef37b8eb14836ea27d833b05bc93ac3e2d2125cff1d6dedb389ba9a47ed90688a87055a8660c624b29a2730b0e9a4d38a21992bc435f3bfe4e8cc1f151547d28531a0239601f9b48328cfe07cef9567253cf317ac6201bd610a6f6b62faae06cfcd0f32722545a66a4b3ae06a8c21c1396cd113b136644d9dca4ee4f8f5ddf53e803f05edb71f25e9424330bca7122d73d1a7556f3488a8b6d6b6dde5df42f423345b2a8469189176856490ca04d678282518a2cc81bda14c3b8c1c64f9c899a2849fb73afe3ad5bb173f407311671c10589231b0f88aeae6e73403e94bef0c4cda41c0c40990845a57c0c06edeb8b89f139c87909647886c3fcb9a939e954c78bd3ad57c519beac9239ad05ded7671875ea9a3672ab12fc45b75af835ae0ee5634c47d2b0dba8fb82d36c0f7a39d63012cdf188fb2feb976600d11eca2ef953efb1f78dec61a40a55c7cf02435c502cf11b5f0ab3359510bb1f1b18fbf6939e29f1208690fe04d505b3630f5a249fbc8496a20a629f9faa96b597abbc4ac90566a2649e20d6a5fb75108b000f257c6b4bfc52467a76f368e95e0e7c9083e5b9e776faf92f571fba27463c038a576c2f78609a84d4c2d8b8ec22a34edb80a72ff1b64361bd0ecac356e1860824a4acb6c5adde57435391d446daa3977f0c06f0976694354ec9969bc7859406b867f2d9cf65797c28444c3dcb6030da9c6c66b65c5dda4c73345480cad0970ea112441a3a23fe389c041d0ef84f508d977f385c4ea8b3dbc53b521c859b83030b95bc84876dd769f77d47c54cd69b1f55019d859f9bc6bb838d35f355af6708439e2200f7798490aefd11b92289b7d2c8e16e703700213381afbd879c680404acc8e2fcf43d1616db7db261c8bd44dcef781aad30599fdc00d21f78a8aa02048eb0c1ad0dc804b17d3964c91050508ab64f02194950e91094bb20ee8b40420c4b2725c8db59b2e993a15caa1dcea49511b61664a9de4636f9eecf9921301500352ffcc8ca27ec01259cd9626580f681c7c51eca0db4261924338351482a010aea787b470d945d6226d9a850c12dbdfce39945ecc45f792e27eb87b554bec30811ac8a8366a4fe3ccbf6b5462a21aaa760739c984a26044eb201020e79b4a8014efeb26cf5e212f0118914552974c1a66dd947bb1fe7b40ed7cf3c7bda50aa044192e00bc94a87e4cc36ad329fe5ff854da31e30b404fa969a822dd2b67e0704391a7fe69c45e2c6d2be163a91ec8407d713a49d7496de38ed3ffc8010eedc0b1d0c3f96203babd3f3a2cd1ee088115a8d26b6c00407e62cba25187b6b7dfb0c2831d18daf42d004782042d909935c4a90d7c1a51ec4ef1b7f9e423f264ec204cff8ae22942640e90b1ff4bea82051a5f5fd3eafab0bcc1c2f045d67097b5a0f47007d81650dd8ed1f566808925814f529b55caacdeb427a5fbd903cb32e2c02d6ca65076bb79a530a97d47a71f1694fd647069a88f51536ac3a849f18d83c0f381dd5c9904e7dbff734e7e9630fdef94defd6c489e843d641446d31555c800de88a41a960593dffebc2f135baef1ab55894f9e6a219ac1e960810ac784cc40d766b27aef0073b4416f9790600e4aff3714529f81645dbcbe6a0632579ea9c0ae50bb2835d6b7dc2f0cd484f23210b77841e2f8ec12e311ed5ed610d809b8304771400400a65d044a095bac13eeba9b34507ef1a536ee8546687f9f0d935fa01781f4bac349f71c51c098477fec918bb07e6cad07f60ac45f01d2ea31311620ac124ef7c2a29ded8c7613b53e998b321d697991235d435aff01e1d00c6f7c608362a07873d69757038da22fd18c0c403644f61109f2bd90211f56faee341b10c70134442974c2278405e43dbdaae6140e3f11849bd8b6d95fcdfcf05adf9e50bfe7c3a5b50cf78d33ea419174487c84ed4aae6682cb406f7c094f902b02bb0081fa3636bbd5267bb6a982aaab36da50c03c2f93721cfc7c8a9138705fabb0800da5fad69f79534fba8cbe7debed4849476320f7587b79ad35e392fc911a2890d9f6ed0e6616ac578f8de11099013627c0c29d99db3ac815e87e9ae6fc9de4e05c243201ee5636e60fc96844e6ed6744f3d8d3af77274d79d1ad5b7a1cb7cd70b11a1c88c71d5b5d7e84613e1910c40784e85ebf75f2b0c68feb2314eae399106dd39ab7a3a11a90573338cb941cc43a75c52d06da0861e67cdce0900eb31cf002eea5ee15d265f41af57fa4ce2d7a64deb19c6e3f092d4768355b7343ae3130076cdd7a02d5f5e643012e159c2a5e8962b1a52758f6dac06962f4348a740b90fe49ace11be00a12d8abba36ff90ca7ea451327d94c1aa289f7fd0634e30841085b84b8aef1dca45c56290724a65c65d44ebeddf2b0a5dceb8c88be43e03bca05c778bfa0dc74fe41ede1f923243992184ccc79968be043f6153580a4dcea71088f5a912c85ece054c0a70acce67ea5ad2c25c62e3df307bdae5dce8dcc6bd50b750d0dec95325afbf32a81e739b77be48be0bae1bce94f687d2c3acd3f7b6e02adaaad99280afa03d721db292b4ae881ad35e20a38e337dbb39b650d3994160fc8f886070548497b3447ff5ea7bdbe772d1afda1be5676e7e3ffc5e4753c680dd755e4bdeff268a41a7cf57d1f6a8001de86179d828cf0e523f78f3d4428c10491e934a3ee3e0259a753a43b0e4bfd472c57fb0a52136670f85a3f5e6fb3fc00fb052d7df73c775af1cd95ce56c04ff7352fe8674ed2b50fb9bb995f52da1b024d6eb67fe660fb4194a7b50db6b65f9b701c3a300798de9d753e1073f2f4e00e1d0e74dd7bbba17ee5a951915961c1894aced80f6eca05113a16172db99ab30cf0ee48de6629da1f19cf96b2ad4b9558dad8d730370f07f6536233221dcb5b087c522a8fc2d9272e146e3746436b31a7f42753f1810ba2c4996f3b24559966024d100615b42251d5dc2f478dbc408e3497eb1211092edc4776b1c3a61276ac083ed4d3a0d7a2a95256fd7f5455c23d4c6bb2a5aec5dd492cb9a7ee71c0ef7b02f073dd3a0dc46e7b0c47f4290962f0b049c5784340ac6503b16b124155b5b70cfd4743ad779db21484cf9edfa0e2fae3f9b0141234441712b85bc922b6121c081ec620e77d1e8d3ae65c65b08476c546cf5bff9f0aa9e166d33836a28c3c2302a44378075f71b78211b0f93617cb4a2d751acf34df9f1ff32e6d8140f1ce350cc11ba622837480769ea7e1c3c640ecec50cb2ef0c1b2332da6505c3848341f0b4d088ea96e5252faf636587eade6f279eb65379c8e139c461071b724650bbd063fca249bf3f74abf14f75e5c75839d0fb08ef81b19c6e0529264ca94246e9b09f2e38c49732b90941b09e2c8b81c919839296b042552d9a5710793d32b0e1e0c9f7c443c50129703fd5208a61dcc08a89e0aa7fc02363ebdfefa08f92a9bd60bfde25f5aca63a389d51e08eb05cb52c966590efab06a69ac10321e2941f3fa04a758e1a626e8abbf5e4535e974e930088a90d0f225926a0f0f3901b8e1e3b80dc5097b15f0062f231a3acc39e8462b9435f2f55c4de17303cad789211850be0b11d1a6132f9d82e1945072914e7ce3ebedc9e756636207a7b43a4e6d84cd260a98e5894b0a6e1d7235d0a96ba2c3314d4a7233dcbf2e5bc45e094b49e05f5b07f212bbb5e6d8719dfbd7df4a82ec3a2278bfef9c1d806e8419f8b0d41886220f45c5297aa8b2679615195408f8894c48baa0f5a1f96c9bdbe1ba48c8cad6580a04d95d0e01adeca7077a51d177426bef3b17000fd6e3ab320fee808b6f3f7b0b48429a09656fa8c799c5313306f04fbb17cee5279506696c2328d0e942df9f02c95f497f56bd36f1f7a4be0f5494cf8161997946c9f60a4329965907366e5107407583e61d1c6f6f6467caa28ae8ce7de3380301eb74e24a21f5416d8a9b62074407fa6cb631206ef51672fdafec0f45f35586f199e7135ca37edc69e388680c341eebe8eabbcc9f68b5197196e0774a5f989f4e4c59c31dab6a4b62cffb4d09a2c11da0f407a9760e44e14f0feed3a6268e8528b9dc4ec1bd5d5ff7d0210c04259703f617abb2b1d178843343bc213b0bad474c340b068cdf16936c292945090571642e4880856dfccaa2204782a9aa98229b5b7d182a5e58524e95ba342609a5a32fbac378261996fdc6506729782cb109a9e7da9f518e287606e457081f0e49fef19b5b1f9402d2fd4a3d6a4aa805ba1ab89f4b8f2fb214b707f724e5a80717db33617a0615145206ecd023528c00854f93fd85c7c1b08bbdc0b44fde7402d789053c4e9e58717dd93ad841c630fc1a9f19f7de9653c8727754768d58e70aa25dca48f9a28c8022c825efaccedc376cbcf9ca2a7c561b14ddcc21913ada066e7b55865d739fc503e63131f08eb961bd81d72ab6cae32f4b3eb2887ae376026867241470bba0fe7e3dfff292f1bd2434be57b7f53c2bb7d9ca7779d6bfa50f7c1f783e21237b9f3e52ebc7ab5b573ac9f16affe3a8cbab121fa4d2691c05091150329e792ec1efc1455f39d42766fbf382e072b552ae7dfd8bb703d30a99024ae73c9b426558914cef6b42f54dfe7f4c32228fcdf4bd3c47cfb5195eb0d4075710d18d79ada40fbe2e6f46f27d7edba2c549889195066b4895deba56cafb0e032946e8a099efc74ccdbfed71c8facf238befedee9edee213a4ddd37849c809862067e4d4071bbed0e496a27caed3f44efb517f33e7f49759b0d67076e0740ff0915c13d2dd1860b6ab2dd0687f237b4ad411378941c545325588d3d5e43204305c806fda12abf4b06966e75552b86e9e0a8eeeead165a02a1275f07589ee0a0c16752106fdaa84513cd8b7bbe126d3edb0f02165e7dbbd2b1b1060664dcf0defe0bcb7967f0d8b0f785d73b6100f7623aab38a497b24452af1351150060c063b06dec60fe32c4e5a6a67cb421b47849d8f62bf0db3559a7ceb2f1798991c01e75fb4b4b4350699153ca1f332401b53e354652d99d7a89ef254c6a5eb258c023d7fef2d4704bb556d80b9b6c32594404c428ffc964bd9462e8960c95d0f4e061841c36ccb84fcfe591926ec3b9500d8bc0c6ef62dbf8b2eaf55db87fe9c090aa84d171b70c37d2707825b3ad06b0f0128d1c4b2a3de7ebd3dfdbd7e32a5af09e48bb2895f54e3b08c10e6a3cbf962ade8ad96a34d8c89ae5f28cb627b6011098ea863a23a6abbdb64d1a6d805a8db316eb55f02472bbc3c05adb386e7d13f037ed06562d8989ced20dc169fc8ff85274607b7183dd32b036516eadba45d170f219844ac79707befd288561da599d23599f8bd5e3e48629151cc88bf1826cf024ed2b1d7aca6b8986655b3a01d019a903d9ca388fe5455869651fb0b60195d0ea807c53480d3eaabddedf5490f7cefcf4e87f428a7e15f2156c15181cfb0d80aad8ec83fd2b7ef056d6176fcc48025be0be57fe68d1138dcb42a09906ff25c09ef71dbfd42441178a7be6140b39b4016553f2e29cbe7f931e9eb949acd0293032e8edd6eb154c6e530ffe3c3edf2970ec893cca017e3f2615b7176cd9449250d99f44dbbc7a6f9f547bd7e52eceb9260bcf680c81b97337f4682c2165cf56e0343459f7003007f93ce9774cd2ff79fd35316e449825b0d98cd23a2f93297540ded56167ced2aa3b44755e7a7700d0a4b29478e7fcd34027909e4d03694e39109e5784b736c852188e64f11fe8673725e57d3284e2d070b70fe5e0de69a3a9309e877e4dc7367c84c87090d84041c8b27d5161efd5c0367d4d67b8a421d95da082b6da18f7f527baf606d875a47eb661b795c67a36e7309fd59984703f0591f03955b245a8b096c3ec28ae871b39771d0c7248b30e450ab1066f0cb07f82d240716363d8a1534a360915a3d546e5e4f2307ad2e4efef30e75edaf26c027217601629b95230be191757c17d4a240b0edc08783699a9eb38677d8ab11ebc40fc505533fa82143f88b28f5ca4b4d534e308bdf1046635897b1a0d0fbd49d8e757a0c8f9d925aab1217f6dc7f261de2139a7b8c9a8e7d7c848f53b0f2d6c11b3f3805d4dcd38a863bddc4bd6d3ae8b44efd9d9e4a2ecd4dc4473adbf3c37a58ee5f055fdf25cbdee59e60c81445e4483504f26f0dcf4ed6de27b2a77f962dfcd2da0d2df5979bea29fd96b8369614c83b43ec50033d7edab91c6f5bb44873d3b2110158fffd569edc0dea7531460ef4a1f8d5b2e9de05d8a6e5eecf8ee62d657cc207f8df8d5837f3822b3946cf4207b575f90ffa356dd0caaa895663f974d4b2f10b007e4695fca122c98015e8c5101fbd016fb5d488f2eaf27a057e9cb252be5609a60a2f1b28e0e8b676a5963e7021fd0fef04d0c4727c63a7d38b804752d7c30fe4283c2e3aef6b85373edd531ffdb142e018fc8bd1803ebafef8ac8d19b93e0db3dcde068e450cd8ff833a14e61167d1854db0f786db1875a43cc8889e847704bafbc4193cab2b514a248a6f633d694e9442797a201adcc48c47d33869cbad067b61f8645002c669b4244eaa7fbebc8e7f58471c83d897978e791165aced7f00f67c11ecd16b451d13705ab4b177c4bd41672549a24b951ee3456ec8e6930400552990f35567e280c4be151e3a67dceee905a154359b66b765d689f8ed5e8506433583b08635ab484142c77cfb5f3ec25f85e7625a9b02f02240c7c9d15e9b0acb247e43181bb1cc9782ff844f05f0068c665ca5c33171fb75847f861b39d40f5f969bdef24a50453ee174c0f95fd10097fb70b07ad409932a8a4b39a0c46a034481387345c830dc8260740ebc679093752603bfc935b883c132cf01f6221c077b0efe1f48b60c3bec0428d86ec437eb06081774c9c3413165b8c92357e56400a79e544fb163cb51e6870c0ca01f5a20c25cd8d27906880aa579297382f5860ab0ae1846dfe7dabcd064cbf056c20991b1a195c83beb85589e613d9677e1f4017b4043a8fae22c013be45ef3a9e262c5da949a1c38530f0f85b7c68eb39fbe066bd99c8bb4abd5e807fa556fc83bcbc9865753cac5d94fd3d24e84435b961f0f45d11aed30f349ba9f21ca25667bd1d79aaca715146d9e78a0a5dc8f3e230b07f4f438ea644524f47cb9aef12c4400933113ad1b15ce1bd30d7f84ebb32ef10d13251a7428df0f37d78629f9095fefea48d12793bdeb55c0f5bd66b0905a750a28a075b49ae2e379b2b6f75efdf1611efe25c876a07bb25a4c25e43d53c8e402ddc19ceb0bb96830ba4843090997c3f29011d553e371cbf0d541781f44106900308ec56b9dd15c5d8b5c49be370ad26f689d339c6009d1767179a8477fab25001fc9b7a32b02d6e844b87746a328f8b9b6699b6ebe4b34c9ddc184253fb9d10e4e2db7cdbe1cba3bf8958bcc39352988589922780a0472b40be30d19cb88d70418e6c7c5c459443866216ca12cd7239e2e50d1156d489191632162d0bc1ccc08f03a6a334caae578d3a14bd1b1bfbf995dafb0f7263a588a714e257e0bf4e2066c8ff77f34e6ceef8416d9f1672bc501726249312c2048282d1cf22c02d81001ffb83c993896beb4256de092e271ae4dd820c279bbaa27c2d5aa4786deb52909653555a9b9759331d85e344f969733ed509f17ebe646ee59061107f6a6793907a7998ab87674616e41eb2b33ec18632ff78f45482f28ff875636a690fa90a50062bd64efe26150881ad388e66d8c122123e9e742a89f54116d96d633b51e3e0cc25b6bd0028a2e39500932ce174cd940ffbbd3ca11a6e79d037bba371ebd350f648a6fdb3d7e7fa817fe10d8461282ef765552b20303c8fb81d22b923ae5d80339ece5731b02013b5a0895f272cc022f869ac449005a66999888afc9b472d40bf62f040c7079d70894b2d40c621250b4e59ae77be059cda33081468bf289e8026ade8718b106279c8a484f4aee424cf22f745b865dc4790c0071cdf651adbf055082f33a3303655eb3291d6b63f2d6d7ecf72ed2b87b0c48cc0744d28a1c8d0699bad4efd249e50f5114c6a6bb4a022c8b1ac573f9fe4fcbb4636203e21aea06d07772844a2bc8a4b4eff31eebe76f3410a466fcc59411215865ba825a57020650184352c5a718cce79aa152fe3f0107737fb3b4c7478c5afa91ae0db5cff6097a4be4023ee6cbd11b2b26681fd300ceda0450b1769a2ebe96d68859c34ce90e413fb93439dd5cfc9abe3008d1499e69c962a358a3bec9183e9bdacce2ad3906877b4e7936548fecf5c7f8dea2fc27e2ce2b12e7e22d806d62503d2e27e7690da9e364cfd2bbf08b28b3d17164a984024db4768707713167d2f57b02eb3fed089cd52ffd1d577833d2e15ca35ef1c951090e24774d10844158532b82bc95f4063ff6bf0da6e2668dd9675294045e18d89f111ca4649ee1c6c1961d1d35377e001ce51bb655452e4d8601851af57768600d239afa447733c1b83e7d17b4fb080e9ea74922e948aac975984ec9c0cc2322689f6fc06b7870e778fdef3a4a77220cf7364a70231b85a275b44ac3b90dcfa55fd5f7f00070aef9ba23dad5ea2230009babeaddeea0bdadc118d82e5c1243caf2e38817a1a4e85c8d1431897cddba08f4472f176d7aeeeb6c64e47cb04425030b758ee6b77f2bc202f49e6e4983760d532c6b947cd97e9604db9119cfef8fd920e4717d18b450238ac112c9921f1f04f0ae4b400def9c00ccbe69440d47ee4aacc477f492f9456a55fbfc70e40c2c00549cf3ec8d112818cabdd9e80f3ff31d989264ff97fd1a1f51aa7ad173e74709b403e5262740b74a8985b83164c6348313a53bfcb5cf105094f51fab4b659c0ef46c8aa35231e9434071c25ac4ff84065d3719856091fd9a587a82025eb73d08e5c00198e67ff94402079bb37122fd08cc6a5e07e5c28bf181b669fded5ffd0298cc1fff7e80b75c8b797ce1d0999184aa12d5b0710dad8fb90182c015a1a10517adb7cac10ec530c005770693f65327d61a1b383edc31ab24ba9ca9796ee8031a774bcedd9479e448194fea079ca38fbcecd0aa8ac05338a79392362f8fde0f84855991f8421d6485a0df1ad007140cf704611454a722c0aea764124d7fcd0b9ccd7709e0e4879a8f15b1b88a94127de97c9f629db3bc008e76e704fa87950dbfb828ac35e6dda0f7bba026fde0110b4baa99ecc7e92d3f8262c839f89a530a335211852097820f5b2cf9fb5279d24cd674bd615c6dc515e8eca7add9ac7d0005c8d504ddcf3fa69d1ce7d280d8d55211937f7f065f583749d504c93dfa3609a3d4c81d9d93ab8fec6652107aa3cd2b0a2b6c0bab148725d7c4be2a1e1ead066eec6fe2b882cc9f4bed437a7a5faa36d15752b2d06d61a7c2d2d914fe848b022b5975f6c50ac0bef4ce6f5183d0a3cb30250650fa8eefa65e4d49ef72bc050dfc4f9bcb684a611c2531141f06e526d34f9889a82c0fb078a1682ea04d3d18070ac082cf329ca8bd3c82d1d651302016fd1915cb1f63136d872e31eec038b40aa0fe03949b7e24eec6daa690d3a14ad43fb6502659d7b24f4fc6b1244b3b820e034cff1fe85cafd8072b07cf9837da0b6d4de9e438d33ea07741cfef9437f605776da53be3cfbfb68c3a9eda0f1cf6ef14e738786010bc98f65c75b3154f61095514b68feb1cb137955de57a6c7b9cdbc78160a78153d00c95db4cab11c59e0e2a4153ad9f47bc2c713be7dc877924601efc5312ea28705ed51cd051ed79c80c22100a3fc9313bcad9925d5b2cd46582633909c776c0ddd1f66354440a6aa107 true -check_ring_signature 388c18af22f9e5f024a4ed02ddb28680b7bc3f3d8df8fd5aed161e394582f27f 3e4488dba58edef21ffec5c8a43da4ef7f0388f934bd4054b4d6d4b3f8d41b01 98 727fe3fc4766dfb57ddd825f573a0e22c7ecafa6dbc19356db90a408190c3770 af9f66df452e66f05cd8890ff0aed1bbde7d81b43e26b07e0025eeccd07b78f2 84afc506629083b43f60e0d309bbcd0cd739fb86a91f4a18538d127b4f4fa208 7ee47aa1fc6f7b65e358cec85ec93c0ed2225896359d6895104cb80bddeb68ab 214b7edda6aece2eab07aae110772118c99901b5c818891cd4dbad9eb94cdfaa c05dc52e09ca417b376a40b74bd651a7bad18c807f915e0aec6ab981bb73012c 39f743200ff38619555fd170f5eb25ef1ce9651b9083716c01ab6fdc7c021c45 0932a810353041469044e61de7fc67b02abf494906e5fb2c602d32480678248c 486750fec533bfb266abc03cb7a158d05327f3d8ae6b13ef681cb745613d9f89 f8344144c5c5b777316ab37c242b016e40306bfaf2f21eed50d374c2bbdf83a8 2cc0be0e24116c79883cf85fbf48f509397e8fecee8cc14a7ce2da7bc53af4cc afed9ebec4c7d5277852ad1302933baf2d8c2ff43a4d0d3ccbe9df9cd6ff7db3 24d634a9f4eb66cede8164b11d40d53fee18f8aace654c37c3ea70cfda71cb66 d8f8c511a016fcd544ac9e0ce4332ec35451085393ebe8fb1cd00159f597098f e7f05e54f8cf70d760fc8371e0a029c0b6a014929d875930e6f50c45e1fd0bdc 39b7eb39eda41c0d73d80cd2fd8ce419b55c0a37467a349b9f7d3db5db619ec2 66de334e9480e6d21a1461a4011c07f9091273f386aa284deb5bfa818c55ced8 aa635e769d5e69a0a8190554fe518ad1eed322fe147907401d5ca965d1b9829b 5c72bf2fef0c254f1ed3f4efb2e0c8cf93942d00859e3b5dc645c7537c1c600a c6ee83a6aed840da332c211f3f8ba6cbb98126eed1972a67ed905a62cd4471bc c15cccd6163d4ee6a496411b160c0f4d548e02125de05e3bef6fd8d79a87c70c ed535cdbb282e1bf7274e4411d8e2a153b84935baf9f61b7cc28179177204b8a d8264fee7213dbeadb10b2f28decd15da1ca5da1d821c310f46478987f5b392b 023a0c0f5dc2e35b4700d6b86d51f814fa728941382f0b590a7e669e3ccb3f74 3146bb24161e4d8fc3edb164c1c7eb3865d09810bdae9308091db16786e287d5 3b992804e1b8c2884c3205356aac9399a8de157e3d04eb95b2fc608d91af0230 8f81bd0b9962a8ade607b0a5be96798cef4f6ea8909f826566bc1194717ef546 aac5710bbc51d0c94f8b47277e325fc08ae39a1c68ed884bfcf9d72c47b492ae 5a3454dd2bed4e95663192bcacefb57d297839e54c55eb65734cba53b74d1f3a 47f990401f81156243baba847ac4a488a6e476e9f3c7b662b6989c893a361419 f23be093d2cf5f5a00e8a6bfac327aa37132d6973f208c7829ea994b8e6a0278 1c00f3f21ddfccb5a4c7ec25f7899ba47ec318a0f38848eb03cf0e3c3d8f0630 88c9decab70006f6af09e79511ca759ccaba96a650ca39cab75af0d7c9601316 39b0d3c9905201ea77c296945bb92b7ccea45a17ee9f906574067de8a53c766f 68aa939d3ad5581cef446164c7671aca75c98c50dcd829ce2f4ee8515da38d55 0cc0e96a30417ff0140e192444bc7394cec77efd2bb05c10eabeb265efa57c02 0703dbedad02b15c3f571fa9105ae39f036fb450213e0757fb1d96fa4a48f9a4 a14ad7921ee09aa4c4ebd7695fbd5c371c7774ba82bdcbe31bac0fe061f375f1 e5d3f3c03d3b47fa605fd6961ead9f78ce89f1c73903fbfc3aee8e8d09bf8288 56c162d4091be46cc93963790826450e75a55ccbf63fd88460825769118652db c85e4742a6a6710d7cb17f2a5e7d05e94fab056e7ecea0fecaa1be107c904fa6 6aaff2c41b3bff7e5399391ce5462a0938ac45fa789f6dda9645e834491e0e0d a1e592ebb23503e3b1ea6d8fecf5b6592b42e78075347c5c4ce3ca609f744294 dc6fcc61fdae35e8d1d567b729eb5ba87580c07752000a75ec911cffd3ea310b 6d379302a4b329ce018c13eeb8ebc4d7bbf58f6b7f288bf6888053d44005318e 5fec21e1cb1e014fcb1cbd73f6009227e18e3e0ecb839aed530754964999f901 1a17bd13f40febc26fd6b77991249fcbf8c6902b37c9f60574621facff00f3a3 b2d25c26684f3f49c54f7335eeca3830a06e72013871763f095120a8865fcb35 2d0685870c8d2431b71cf41c79bd32ca7611de0347be42e4d0a3ac0b3d2c2e1b d01a9b2347de4dd3d037717fa5f310aecf49314bc7d61bd2dea86668d59ca4da 0c353e70c9add528df474a5a95723344b047f36149fe69d4730620b043589a46 2efc0a0272259b049a3de34612f875658e68d1083051cad1daea3f3ca8f12110 e4876a0d39af965726f820e0cdb6100988be240aafb8c20b1361d1234410d226 e740c66ec24ab94a7c401725f652ab9a2a482daf6346938aca1061dd487e9223 c30a565abbe20c4ae4f5d6f7318945a61ea964fa43ea775f184678f4ccd017e1 2a0e3e543837b6b52e202dee9be7c353530e899e0743a39451980716626d6f80 3c80de7ffcb8e616ad87dd278a8cfd69e828708dfdcc16812819a66520709093 f5a0d245e55b4d8d4da5bb00e9f9bf8c506791a20183d867f7d6996fe7d8fc54 6c479089897fce8991ace4513283510886a402e56929a4029c56ad094fdb789b e1e8305dfc3f0b6b8ab054b929f3567cef4801af9ca13a94685de4b8159e86f1 398f5a9fe0f7344992ab09cebbfc3c1da63052e809b913741a4e813b46497a6e c442400888ae72a840a9bec27922132a7d44723a482aeaa16b0b89da1be64fd0 cddaf7b918dc3f2b51e7a807664b27d919fe1d9ec0900bca79dadbf21e1feac8 b98f83b6b060bbe8aed444f04d0dfcd7ab2ce956f91ed38d3720303434850888 d8345ac8574ae4d6fe4cca379101f7a7b904bb75271eb5e065ba0ee72322cff0 b82682345200f21495c1605c15a6686f477c37d764958fb1ac5102b798b1be67 06fdb8f6eaa7e78b56a7c8fa28043c14ce75125fd161cefaa6ad5496e7a813d1 4c4f0d78cfd1f4e5afc27f9dab02c7b3aaed9ded68160230f20baa7f4dfe6933 746c87748edd098a51ebb4de691ca2cfc1db9968d85b9757c06973b331826e55 0458624b3278f8fbcace8f559890fef1052d418716535127e8534bb74871cdfc 0ff81aa1a9877b0f845560ec3aaef76c522ae3e4f0aaf4c023e3f55fc4b1bcce 50341ba843f7f0cec908973617f3d03edcf2cfc4fc89c03811f101d04cd5e769 3ce6a8e92559ef56dacd981845054ecf20432f19576f27ef373d135d391c400a 99b1d5cb9e2b47b2a26db4ae5bd83bd6fd923fe586551d2428e5c9a91d21c6b3 f1072b7552233d4ffdd4fe4c35f2f7e0cae3e17c303f00ae912ce9378a25e961 e3c5d3a0da040c45d27036eb957adf73e71c78b1972db63f995f5ef1506c09e6 a825f92c1d7c1b290b6d67061d13a5d5a111ae7167ff0cc55e5dad13c7f5aa9e 3a06cb325cf77c673c3c1457d832a07fb03f1ca1c9e8bbf67c22756245679925 617d37bcb1c0f86e01a4fbd267c72f47e2de0f459c47c3e4a2f6a4cd8389b45e 7791cfc6d99b01e01cab5bad787898cee1f63e1b95fb4ce33a96e324767cedd8 e242202b950f2fc8caa5269dcf98a786bdc76f36e9773bf279e5fd08e92db518 09968e6572b7a51bbafb42b87935ab9bffbd8411923f5270da6bd3b3e2e508dc fc21504a8168dd5890d3d6c5585d1341f2572033badccae7ae3e6ce6ea74d13a 177f7f9af7529e31df5583a8a936861d64cd7c66e80905bd20a8b9dc6c9f0fa7 8cc9496162aa541b90aae51aede18f9112363f9af12d906c2d4915f180ecc8cb 7bdaf317e21a3e1f23ee2a16623eb7a9bd48bbd7c44b2cc1d6c8d809f8480bb9 cbaf68938d3a717aece4632e25f7c0f698a87d4630464956cd7ff4f685cb34da 6da15255c73ee6422cd8e9f5b27c0bfb28e91924897c8c27c45693f941ed8efa ab096f92e68e8f4f91ba3225d120a81a7e602f29c49d8f6fd12bbb87072e76a1 acfd7760a8ea527788cd642b510c5adf3f230d047645a57a088ea82141ca8c3c 015bc8a1515a17afd9b357317c5ce4249989d2d1fbd6e02eaa224e64b5654d0f 28603bf7247758ed38fb2773e0ee8856302167c9f0175e01f85c8d7a934c79c3 bad259e1a784d95448c008bbe1ad843628ae612a7c57ee66506f68a8d038b3be 275014a82e2007c4b1c0446646a133558936832b03db0120b0aae9793c6b9a70 9648aadfdd5a7f3ed55f58511de140fc69d0c776ccacd7f61f45640c3b39153d 410e6cd6ad76579a9cf292fed4a477dcc011492ca284ce4960ced97d07b9b30a f403eb0989646fb2b4f3fb9dc3fe4c6bf00100800d4d6f46f6cb6ce39ab70d24 d3493260221ac7fd95447b5e727b4be269bf6d115331ac906e0dc62437852704 9b4a4a768c2996fb18bb4f93eee0b6923eeb750e28a84bc3be17bf4caf6156064ec60d144d9576d8af242eaaf05321a2f8ddfba54a3a00275a4f37438ad1250420a3b7702d78f7bd81c6f1499f6712be891c675453ef3360973e2db54a3bc40726cda2da4dfc138fd72fa9944814ef6558de3f4f7ae4cf9596fc88dd78732e0324c67d1b5c0b47f78e96b6603e443c906fec6bd244302e8d8f54cd8d85852b083fd8708bccfc06deb4db4e96676dbc11976da9961a03ac1a35a0ef8740ff7e06f55ab55997d07f4eca490d2149ad2a54f52623a35536f84948356689fa95eb02331b8d0e3c615e4c2546f4276141fbfacb227b77ab7f2ce0f4c536611d055e0cb186d5f6f74aa21e32df6b2041bd0ec013877518815111bea2849e672ee213032491ba230b012933a80a2db8fa0e40ebbef3c5c535ecafaf42f8415b2470d20a87c80ec0c3a27cf7871f8e0884ed66376ce8158d7954ef969ece634fceeba50b321ae921caf29c2b77d99d41ef2f7905a88f886977831fe576317287218ff808ed0ffec487d2044fefac246cfd5757262c5fdcc55ade8099424e77d50215650d49cd7df5be6899324331f19ef774709f9d4333df52cc0f077218dd09ab6f5b07e0f2b7ab190669df1edd6e81e12818adc94709a19db637c7164ccb93f6ea100952b9b6ed8154fa958b6c70e686e2f820b38e1b20e950fe5d6c6a064e369cfe0d1029635b8e34701e0b3420da2efabdcf621289b61423c27a771d4cb7fe37610b6ea5ca3bafcb7cc3ece24cfed0c84fd56e02718d94a84d51406e659c96d79202ecc12095eb10fcf301c9dd53c45a53a4f958c7a4fc239b160da2444fb3ddf205ff56656b9d441609f61c237486fed7166ea4bd4a8f07b66e6e4e4833d3f69c03b7a1b5e6af0fd69197dc52f8b7b29bdea89de84af00361e9b6087fdcdcf90e037b3df7a04831809165a4545e4045dd549f5c3c6a0958479ae2ccfa612e55e101eae7e0a753a1b91b371e48c4a8771d5c3e1e03d3479701b70f361e59ccf7eb084413b7217bcba2cd3016905906e1f10144a75a7a38e57fb4a1cbcd1c219bf303e18cfff0683f88e7d19b2308ad16fc5e190a54c517dadedb4fd120b0ad446f058d3bbd259ad2ce6f5c81e52cdc35003a9652828118981cf9df1a7d63f53cd104fd978d058c189e5c57591feff1c8dca34963f6c80843352c2f96f7490baed204481396751311f615ed9abedbaca9afb1245f65fbef6b7e3c6e5cee536e00af0de48f8a7b40f4c85c45ab45c613cee0b6f22d33159269b8b1ac4c197b9645fb012011673cbb68c43a00ba024079e38d0b038ac5b7d2c937733be53c164c342e01274d42ddd027660907718db716c1310943f293073f6c51e57d2208711733f60d36a935ec53547486a557532404df49cef5a30342783fbf2e4fbc65c0ee74cb0ba076a39bbdc5749089ef57d9473e90f496d8c42cea520230ffe0a4af3369f902feb6f8b350fb2c4ee4ac02728ec4bc7bc67bef24a21f42c617061a7591c3ee0c3632d2af2a7064692702458edb1b7b11234ea41c94a3ffcc0bfd3246eacdb40cafbd61269aa658f1679be13fc0686d9149e12014e255f3630081a1e1bce2f50a062dfa398c3641b2c3e40be090b371c60397eaa7cf1a18b0229f51af98ce7c0c4cfd47ad9cbd021e3ba987b8cb45d042312433e7c409ff55967ec0648cf03305f1af063c652be652edb720f50fd94909c39b87a00dd36f51b318d0a82ade9e00354a5ca8d7abdd80d1fa48bc3a4b7c62fadbfb74426be090564fe29662dc8409f906d707f676a8040654e374d241138a0f451b39161bbb5970c9e1820d2db3045fbfcf1bf4ed4aab662bcba0fa282eaa6a7ba5f453fecec2438bf41555b7540e0972298064d318d2e626df3ea932ab4384cd3531e84e5f2c6d6228dd3b783b0c982c02c1d8d98c19e276a6291ffbeeeca4480d77cd0d41537316d91f7c68600e92017aeb5ca73b5037e05eaaffe1510eba0ba37f33f496b148e281573bee320e9a555953fdb840082864768c8497befdec2a2fd982fc2cae190dc0a6d54ba80a9e048e88a2d2f164e382f6a7fd951a4a7dd470b051dabd804ecd6f53f6657d0e5cf9061b6154ab77b6b061842248ebe5973861dc353f239db50cd23b1467bb0c55e9353e355eef9d7616f948396e513c3169442128225df50b19c9e57a79e509104962f80a2600fd5d5db50f975c37542e2ec5fa6e58e5fae5829221f5729906862f419efd2da41f0ea91f0b0cb1e8feaf695e3e548b9800b317b8f739d72a0b8b364032cead73ddecac5e9bf5edee4fe685c007af44483c050d00b986961601bebed141fb9e8bfe54cdb585e54c2d357a18e0fcaea39a8fe28d35e5c5107d003cbcb56e06cef5bef5a222b92c82ec0faac069bf201e616a3d420a7447215d06ce99f8d9bb9931ecc2fc9aa7472f3c021d28e075af757592311652557e90a60a6c75a7d7866583883150ab50424d9f087aab4f6d359d031b73226fdfab00080160611e14c5d88341f3060311400b74a6c98b649d0b4a2ea3c33058cdc7d94406ae1313e8906fdf0c8bcbade04c5b6c57acc8ff13e3500e5e50cad719fe91dd0c97cc83e237cb9473fc54fea4bdf73850b8bbf9e77e4a45b7297b955e59e84005d44e6a1fb0a7cf06ad84764744bbd7739696fc82a45187c82f01a5fd34bd0007a9f73c53ff732e2fb4c41dd812437e570b2c83ee69ff3dc309af6781b586070ba0581d24935ab1549dcfeb0709c4fa656024ecee2f41bb032b52120b52b9e70c8b54fe2e5761317c3103516818ec48b2282e1a639a554b396df5b22be205b70b326442c64299f7d2454b237c13abdbc5994741f00da1cb82190502c63a7f380ce163a39d70f3aacaedcc8f6f12be9aad0e6be0ebd086591c5e6bf5128a0f4c0661030867a883586093b441513dd80c889529293385b663062392c03ce4490b0efd320e9d78356808a395731d67c01eabdf8ab80a390ad0e783984583f3dc1a006d84e17c4a2c5191d681e703139a4d7efc8ff6ba2118577e1d01ba89fc7dae09122648a4e1b456dbdfee4d6fb39f685a7c261b3fe1f69b502b4b2f4b82cbfd01a7d2e882328da7e1783520d0aaddbe5e43e427e1ba2a6ffe9a72d32529483a04f5c8e82b8854e9f71cbc4b635d0e434af531d5a67205a21bb247fada1fd8d8029f1ff0281873e4208df1570f6a60d954219218b0ea5cee38d4f6265ac55cd1067b814942dda426c47eeca5abd5e9bacae9d9a178c431749c9a0290905ecf7005bcc689c8072e47e452741c870c972f4d09f5ba5cd53cfc41818430b541aede055262657f844bd6d58683af554f2a955e966a053b7d5da96af1f0a299acf85c044acc806728d9b84bae76ca05d5b4f109249853cec81877c0da6ce4b438724e0a84040ed0bf016ecb584a3f637af1fcdff2ee7ac55202a4992a020c3f0a067600504bd8ca7d061d9e6327f30da318c4cf93e7da3c412272d03efdd7a1737e0e0eead02d0f9825a96ebbd5b726dbcc6f8d72b1377f49582b6f888028cd9dceec09de7902871abad64b0d6f430bd9a1d4ade3553118e6eba07c0f9bdc4e842d170a64becb3137c4afc648dd5c9515772350283549a559453684c96411ef7c351707cb1bad2e1d6f9623ae19682b853af1220c6ce81104b33fdbfc367507e2c3520752879f9283279280534cb3fe8688fd72b5edbad960dd902807a1ad8760a98207b74224b33ecd2f875765f7f833d4500395bf7ea301e863a12a0203acedfd4000894d8a774835d0f51946a5c29389c949fce593faf3f359b075b3a16da9d02000fec68018f07548ccfabd6858342d5e9702300b2545fd84dd1381c8a069668d0967c08bf5b6f07d9623ce0521f82e1ed302825b8b40b63a973d8e48ba5d641107827dec6555cf3ff8b2c86e27724f5ef24abc0f4c421369f87d1a89823e195600bb9b2419771c3f41808eb954b0340d61ff96a53d9208bb509155317ea5c656059779d081158ba8696c5b3c3572f682aa90562b2a03a8c42963973d908f4fc30901748cf96eab917404d157af901ed138861c6d2e95293c654b9ba79d16877b0f3ec035bd11bb435c404f4de9e076feca0ef5160cbd3a308e974658b0a1145b08176b8df47eea99dd4ca210084efdf799cb2c7d8eee1acfefa441e846b79a6606560d5fd8958175528a729145cb8daaa03a625f4cf3a5255401ad58918a4ed50ed2871ceaae666b9a628199ba917ca2c60e6c742c9e93629a46660de440628a0a0193eb5a719fc96d463bbdeca7b590d686fd24f079bd9b18618a941cb7d7360692d00b8db80bd122e5465047aea3c6f2c60518fe1814df0e3c06d2ef6171dd0275391003e8472e6c0cdf0dbf1c2557aad267a2dcb61bba8a2dc39e2ad43b930ebb39b33fd24378101579246e86d07f0ae71d661009d1c50f7fedfbfac7d68306fad0647c8df90f0f5e0d41728b9171956e42f87079df606b43724b75d0c65d05247767443f967a5bc8fb6d30d6c0758800c8b21c55613e671a57f516f813160b6b57222a50bc12754830920b383eae3fb480af5ac89170a8de8603d0c5c5b404005fa9b45a50e2cdcb97151be25b135d940ebc1d1312c6433b10e989a5ae0a0c46b3c6f0e02b79b672d519db9c8f0adc5b9fc0d13383da5de8836a86b430ea0c088490b2389e8178636b13e6f9ae677e65979311a8516397e2f8630820725a0875f71ea49732448e4ba1b913d07875621da21ce0056871645b40d65eafd7900366c9acba8c48cd0e882b6e8188def18fc07bf54ea6338f6fb35371d098e4ce0e35ce5472f6b7e823e791d087acea083c380d71b2a93dd125bc21862d13a84c0f6aba47e14b526bd4e5434b2f03c801958b283218f0c3bb0b341c50839ba0880743fac647517af8eb6cd86979df2c0f2a25b103f2d9a7104c8e830129ee3b75091f170272e00ca05d521f739bbce52c22742bc4a48d330af6a0581cef97c32a0515d852c4483250bdf7e968f7185b9b0c03d55482fce2fe0971a52986ce0df50855e8b3a0adaa81386f0f0a30025e800fce1ac33ff728918d6e4ce96c59a5160af69d64166f75a400f5b2513b499b395a3bfcdec852b002161eab09ff2a48630dcc908c7abc150baa2d472461f7b9f6aaba1fb555e7f569918779bf766f52ce02849e70cf138b04b34dbeade21250774ab5951eac459614829b990a5c16e1c401f3a836ad31389a81abb82ea939ebbcd9716980fe9e2cd50d00c87de2b0d879030aed9073268b4ddab13e86ea44b2df57e2fda55a698233292874533c03ea830f1d532ea2fe770e9e41966136552f5fb20d65f31ef5d8ee5637b27f7198337a069c3563fe24e3db9f680a32c14f483698905dbe3c120fca35cdce84004ed55901ac6515035891309c1cfd74ed6ae57bb5fe38bacf3011bc863e7c3ce3120da80b7b8d1078e2d74b42f499e4a45635499061c757b7b60a71f778902f5b3b4bbb06cb47c7d8579e970b10e9b06133e90234db7f7ee0fd027d9f22fbede1f89d580527319d6d469d2d91567ceb3524e200af7b954b96fbfe7bd7ad29a73f4cef850e70fd6441c24151a70c8dd1b5a36d56dabb01f8e836941f012565d44866c7c20977dba3b69bec35abebf4ebfa8a796240b52f2c74adc72469a7e520efa35e7908ff5017ddb6dc4cfa8cb3b1d32d627b2a94251827ce814b58453f454091d9f70c6e87685da09dbdfd93d3ff2dac53ee92abfc1aee830601b51bae2fd746f3310be4aaf49f714075821ca9d25335825a6b9032052a46fff6f0de4c2b7c2a27ad01350842fe82c2c85b9cfc1a9df7853adc2f553df5524e7c92768369e17993f100326f51f0a3f7d629174546be99bcd69bf09d88a120267a7d9e24a33003c35a024c3c62c46571cadbf3efce1480a34c74d9a16346d3d7c268459e920591f83d00b1290bdef7496952497061880c4bae2d585e3c402547b704fb0d666fc3bf6204775ba5dbd45a8a9457ac750e17bf0527409e03f05904384336db4c8969892100c8f2f2f661887323b5d2344da251da891c3d93e192dc04c655e2cb6f3a28cf0eb385a8954acfb54641b67654efb0989b5f9dead0093cf288f29c19abe5a2040f7d5b78b561694a1d6793176e835ff68088f32ae35e92e64cccefb9ea69472500a1ef6315071d15291973f1b23f027bca1f6ed1fc35e1f76310c5160a22fc0401178cd7c910874c8e697f9523a8ac4eed38c92d11074c8e1177c3acb3573ada06d67dcf279d0b65f19df1b5186bf8fa807225f9038a78e54dd08977f55b01b30b17b2ac474d12d373ec2e32817bf60ac0c180ceb5a2804f33312f1b62614d580923d5cefe2ae48f109074dbe494d31987726b3c01c45fc0383a18c8df27e0fe09e4acf74885c9c64bccbc71622359bc26965155e7607a1dc1074fce1a5930700b039af0ac11831dd406be02ece05edd4ad2322c1f9a764382149d7d7a967f960feb612b5eaa31c12961f8605335cf2c33399873e74447cf99d923b124c4aea70a7c45e9287ef2d424c1c81f3927f1dc58dcc7375d63df7d82d964d885e7e76b0286ce8abc672bc50e3c2cba6799bff874a196b404abf4ff56bec57ee593fd60022f1358e8ce6e6358642276587bec683a398de664524a15caeeef9d1af1de540e077d036722a5f7b84a8422a36e26107fd500b8250b107b115240f367e624600cbb2a146f9a3c4f603c0fff04b099f192c11ff112c9fb1df41cb8d8773ac65a097d2d70c923589fdef67711a78c9b0b0a4bb43e3849538be334a76bf712566d0865ec07edf2b8db13195b00f65e7de85fd30bcb64bbbfe604ef93b091f349c30f007a164855c49af6988ab785f46d867d1fb2acd07726dc9973edef4084fda00ad08ac7e116427a8ba2992a6558f845b8de15723e097573ca0b13f3842196c2032aac4eab69d7ef675c764c681a0623a4dddf46cf78e6502063ff3c326d491806f21b0e5f1acbc4d8d93fd6546a3592440d70063cf1d1b35ce4d5a0d686092e05142055fbe77da418eb0a615d3baebeb0a1cb1e3e6b9fa6dbe9b68e6447985e0d95784c1841f699d70bcf0a2d0fc8e079ef78a8eb08918d05177db49f05467e086aa7539577f73c942340039e08bb0f5f3c9f641db9d9ddc5630b9624a31c5c09b0191cca79ac55377beed018e423a495cf127d3954436e6876ecf5d9636d0a016e50f3ca4f00084f258a2ccaf1613b18401f3ee793b685cfc56d0eee7c8f6209b4c35f3c177b8bbc66d4f9f32797b2f8c805fa1537e17b651e8dbc490015790cfa255b957cdac0ba44c04beb3fd66f006c2da29c21feb4c49868f0425788b808e4cfc7a75e0cd58be3c8e7a7122e82e830826125041fd90d5713bea4a6586008f8c382785a843b4ce2e4dd8b57c9772a14fa3150c4909fe39b6db4d53aaa500297cbd99941e2b47c1edd3604aaacf442c5929b57d124377d586315cbaf03410177215f942196a2c60d097ae568cf52820bd8ea593efc4be456cf2fbb12a5b20d995f384b5b3355e2b129a71fa848337f0c24d2dfac916e762745aedc0a28c50f65bc75d93517ecdf41d034c69f70cd4ebffc817384ad87d363cf2d0cc342f5023d189c8818c0e6111e874905d09dec7308f5148f8ee515e3824dc5e4b7ac5602429b0eb035a421a364c923584809ebb44e2e9abbbdeed78631d0bc100ee8ac0bdf78bfe00e22846ba32ffd8203f1ab413c0465977a6bbc0a253acddc94ff7b0fa66baed161bd505da05b8f0e1cbefa7f33649496156a593ba52f732e2f5fcc01f28775773a4df510ed5d47aadd0d0af6980ce4b4e4fb133531dff56fbf925001ce55476966aeaa38cc86507bd2129d1cc6bfce5fb2d94a54967447024a52330cf9a9aca148da70dc12c93c5a3a8cd33f1ec1600d6210e17ae33298c756f0900630ee24bc9cd48a051abe47c96899a918801dbc431417daa27d6e584ebbc5e303ea56106756da0ffde3364c7fafc0d29d18e157a309edc985e2d2df3d2df5fc07673168d2e8a226b67941ebcde302fa651936a079bb2f093e02465d730f73050d15ab226d5fb3c2c13e946d252e3deb53af71e3641676a1fe1390afa7af74db0f044f9d3241d6358afbac5424c5857b72686e9ffccd622a18b79a8393aaff8a02035e218c8d4dd0dbaf8ef9eda4e731acd0ddfb8e842039f157bd6d380cf1560fc6eb451d86956c6da0454caa660be04a617f9cd7e60611579ef911c75f0f6a085c4018341cc5c92d339de38e0160e72d50aaa6d117de6849eb1a51da8d09280774132a8acae1f0d20c711186bdab33241200a130bff8df684470877c7e44ca0021ea5e2a358514a0d5e8f81aed11d5402bde762e5cdfad8b6a0ea0dba634ac09fb456080f2b4a4bdd01a499aba44ee59b214880d6ca39deb8396dcf1a25c0001e92b4876acc94f753a1cd8f47ff6851a0b51425ff29c0128518c1c09a584c30779cb00afdd69becdfb7ece68f864202d5997c89128df9c71c6f3c2910c041a0000f038ce051131158c0471134509e47d49eb6856c0628cfd418e689f95bfdb0c3052ef98327006975ae26011507d96044011ac61723d391ee4f3032c869fa6084f0089ea0698296cf89e7105b37c823f521ee066429f6ec909beb1d39b8bc00c63741a30d7dd43b90c84a54f903519ee5c49e9dcc25a866af0f784c70f58520b75c294020e2cd9bb9a8d7baaf59caa82b09bf88abdcb547a171982adfd2c460c53e53af0acd1e2a49502422d9d30de6c7f5d0acfa27aa15bc09ecda18b3b0e07efdc88be70c16531370a65251f6b97925d95997dbad6a1ac5a5adf8924b7380d true -check_ring_signature 819c219002ca245696f7b889b2adba390fae33fa1eae9f250a9587ac9d74186e c204bec36bc59dbade5462b8ad2d4b0bcc6f44587e7a7712a58d41b61c9ddeca 8 85ba4c527acdfc06c8fb030a78c4b01bd489d672d5cf0a18c8a8e1aaaf7cd5cd f70c41a7a9abe4435a705f8ac312f3f38fbcc1d9a2558a030cf86634d929d31d 3ffb0a13791a3f35f59c8e253198e3d0c8d48d7386474642ae22569f6c5247cd ebf2b5b0fb9caf8fde5b33a507aa694020f323c44c0cd4f8767632c7cd732a26 e59cfe5b93eaf36115e0f48031335bc5b50dd25c0975494b37d73194fca08628 1d99334fcaccc6534dbee025771e6b6389709c9e9f2294a60c03f86a14b14739 4655723f038ea698a04c3091ffc1a0ea0f118f60a120f6a97839240ae3a8a8e6 2d023c4f1bb5d93204587e4db33713721399b5e52c1194954860073321a58ca1 671fc80ad176af1523795d383b82786b7542dd17b6d91f7e4c9deacb5b3fcb051e4d98d795b5ed998a44884bc8c82c35a1f4d95fb163cab5a85e58128d200e08ed660c177d2a9d0833a4138c63f2f85cc2f87f84ad6c25e0d141522e2603de0879545dfcdc24fb01e098ab65c2c65a86698d1731258e1d413f245f64ad9a860aaf88e52249f30e43024f6abb7245f93ad4b0d95d7b6b2e2f8f666ead71270f06f293d768caaa18f4f1e74e4201ceea885d3dc7ba6d9efba9baf8161dc74df2076ba4bee2e126c4a9da97881c7d2029e4e3ccaaef11150ff74c00c645184ab60313f9a213223f729eb28e0e412afbe2a4c49d6a16dcf4cbeb58f67aadb37d3608e47ba4f7ee8f35f8e84d3808717dca134c49b1267431a82e9bac6d207e58cc052c400bb5bbb0940d2e337f467a0fb4794f9ac0614efa30e23337c2e1a687ee081b3165f20151d2f1120b4b106c5cd278cac992670810554db6459164aa75ef07e863025abcfa1b56a56e57a28518d2d68564a4e66abd8604a8ee144902e6be0fe9abcc17946388d75bd5552d73d067832e19e9a29a642b97632d6eb31c0340059f9c844578c374012fb48b10a155be47a404c34f14f8b5319faa04055456e103a01ed601ae43c47cbdca97a7d468687585e5ebbc66c48a66af300d1dbeef7f06bfd1f93a66ab39fca37b8f79b423c596ba18192d2bcf727e9f0462789eafcb0b true -check_ring_signature 4e229c30b286ffb54469291a7b20f52e9fb21590ec8cbb7387001dd51cbba73f 5380cae1c961815bb53b400198bc293629c918c450054b96aecaf533f8fcaa4b 2 162004b1c38dad93af408fbd5ef129f779e7474df75c730d97c7fe661bb9e543 8dde899f536d209e95ffa5480f6579145a69f9eb45668c5118e04e0d2094c666 31eba92f5124a08724665adf922a4ff34e1b0929ce96b9be8454b5a09508b00acf49097ff87025e8740c75697fe1bd0e3c6bf5f32a38a9a69ffa132e1dc72a0b64ea9d894932528ab17f6d398eb90e5604f3c50c580ef0898b4714c92cb44702270567f68597384f877c59cbf054f97266a1f1dcd12c06efe7c36a4d41705a09 true -check_ring_signature cc947bc3e76e6f2c5dcb3ab258a20e2aa942b063a6c121d619e7c57029d3f284 9bfb5ce8f159e7157f31de8081908cd76a71de65e9be18e82968aca822b13983 8 f96279cec66f18d5a77f550bb2a2569846628ceee9ae3051a5cb0e1afff49ded 0bcd3d1abcd6e541a9561de548f84a21a0d772cce7172984291957162a8e46cf 4364c84058e3be62429dee9e0ceababf6f988ab367180a746ced6909fe6f8cca 4bb5270217bd073c4d203e0131310215ee7e1d3c58e0a00c2e713ca14d247dca c7f98f7919dbc1883cfdfa659b58cd27d9296bdbea395ca6dc622a2ddefed1d5 1951db8cd608ab7b21dadd4c1c821b9c7b1cdc29b53bfc6d9c60cf897ecaa4af 7ff700db5bef4d3db0220315c5feda36c5b8197ac8f4a600f85fd571b2ff2ea9 de55782c7a988820fcfeeb8c33301d333005930af155a2c8ef195cd7dc206396 f71e9c7eeee72f6b94f4ca958ac4f23da3383e66a3a1a517b5019a8f4b83e3076696f7ea576255ce1c618f6f3a2c7966a8c7a5a0e930e1c8ee9ae654a44acc08bcfef5d9c768e4072d6ff1401f16a2aada52a93ed45d90eff8623670f56151069c275b0e71eb840b92a201d5dd5c2071f3e1adaa70ff5732b32babbb45b09e01dc5e9cfe9873d275912b43c0ee1a0a056bfddf9b55b62b51d4a612e223d3a20ddd13ef7af080cf16b836589dc2e07a5b76b8347f05a2379face2b80ca1d5430e9ac49ca014e66284d96a1945c59c8494b5f66d51ad60448e6d4f029a7830910d34579074fc11971ed14807a6bb80a38067a0efe7265ec51ccc61e544781cbc0c577bb481e74b1e10f9fae17a2da57ac6583eca90ffba923639af5c96c34b7904cf4b89408b904531c6735f5fc1141c3ac765c596abe505c9ce59a3202e19da0f9b7baf119df257149c6030f07f165484ac3e986c4abdb238479da2525f453100a015af3e5e2a8233fb0cc652728e376eb5b5ae8cf598bd95573bc733917676001904624cce09fb0b87c466f0745cc0285315d27e59f5c75bbf8c050e65866f08691d941b7806138869acf615120bf3a4c21a8140417f14763b0b5f7298285c01cbba750d30ce6282ef46c657f2d3da0536ad94c3c3946c6f9487cc273394000fb822fd28d00e0ffebd1fc0c4e62554049caf345bfc69be3a49d66b6430fb5b05 true -check_ring_signature 5a3ff6e48a6908c67fb41441c19b1298be6ff37468addbafdd6508f7cc1244c7 3928e49977a5386da4d7121d82e9a688e407c76ef62817e13fd6f05a8310b04f 2 7ec7179a48d5272c7a4e4dd68490496fa2c52811191aaa14b8f9d50f19deba4f 7b087c5cc41be40521021d3b0b90880c88ecc247e3365ce78bc1655bb9d6d3ec 4814fd2285de739e8485ff2d944e5e7df217d21354ac5def430d254a5dac110095ec0adb52ea48ea2b62b92fea7251d940208919ec119b99a42bd6e3a086660d444d0ce97e8e250db1ff1a50a1f805d4c13969a1088a3768b83abec136168802484275d5dc6670cd16af15e5bc51b175da9fe39d833bd87100f2e51e13df2b08 true -check_ring_signature 715ced8a87685e1b2fdc7c50a40af34a3fb1035cc9467e5737b83242d4502ea9 c5ea2c9c8a79f116345582ffec5325f4dfda6ffdecf410ce4f32306dc7dabcb1 2 c47740912b02cee29e7aa341c845ac8fa275106947f299f4faad6ff9c7be0848 ad56e081cca82d7662c98edea0c6b4bd7b8e12df154d4c886acfa0cdb31febcf 1a0ab8b33c7af5673c17a72cc7f416f0a864f61d312d82a4e6f3c6ea26fe12ea8c37ef89037daf66b4161d0d6c32a8607672ab53d769603443f713061527cb0261cd58611841f1fcbc0bbe3fec87674aff7e4f4d9fd0781381ddca4f4605a8018fca9090f2c88dc24622c623eac03c061a59e112c4993291e967728fd3fdf129 false -check_ring_signature 3cecf1f08dc0d8b8591ad192342e529bd27cf8c21f0eb2efe5d80de1a5b504b2 5552dd8d7cabe37ad457cd9a84eb297dbea9242c9abe056f04fe03af7ffb687d 16 04bec568b2381a619465a4dde48c6ed28c7a2827dccd7828e83c3b3b8f43cd0d 0d14f4c15b9f8167cb26e4425dbaf53ace58e3d35c5a5a25fadd33d61bb38f6d f1095a2ecde36d7b76df2fcbf5775529ac1e7a82c7f97bc74b15f16fdbe76dc0 14f6c3acc8c942eecb46da652154f69c21dc63d3d20460752a1c09fc27759439 934779b23d986bceace180b9721bc377a620ca6dfb2dc5a1bda1033daa5611ad edebe0c79b246799fc7591728d133aa0a09e0e1a4bb8528c7e6983d58887147d e031003ebc24c2b4ea45afddbf7fd644d261682430b43160c8897b607d914d82 7f769b9c86626a9643e175371d0819b8c1af7d72c4bf1bc71225ee71b2105421 b4a8808eaee7e6b90ebeeb4cf73e1825d50eb2200d3abc5a83baf2237f29eb93 9db379eac92e598474523a0175fbb599881422c8ab5b2beb3f0ec69b3b08d7ef 9963dc5a72ed14812de9a766ad277113175e81510fb607c56720d643935e20a9 75cd2263ee415471891213886d2fb32118bc09ef446ffd3c3b530cb6b4f0f27a 5b73177962e8f4686eb45e04811f7e7fdb81227a9214bea029792adba3ad0673 c68188e40733a1b2609efdda2a3159816641939ceaca5ce3670105d3d1132dc1 2fa7dbb8d54abbe0a4eb0ca44ab297b85294ffc5ebf55cfa23d9e02c7c76fc22 ce9769142da15857cad50f8c61e6c62b14fb5c18999632a3e77e725066dbeade 34c165322fe9a9f8d6ec4339f4f9f38e531c254c6079728de28d3d08a8b4ea0191b1d77e90641a65491d24e0d7307d007036af8454b2a19217e3d053f78ec40713dcbaa3510856a3a91d5aae679537d8113af74ef1c6d55a8a35a30d4b61e500be3aac10688bf3afea1892f8476d420c6b149d68ef082e0d2be39bfc6265ed086d6d62d2dc205bd69a0fa1e914c3aefb1dbd21767efe8af5affb1539b9459f021ca9c8b010ce764a001b8a466632c30cc16b95510bd323e17e4d41474303a90a0f648e3c65d1a3c5aefc9c33c7a28f827ec86e0f9f271e41f07751a098f6290268f534aff884237bcd8f710ce5f78c1d62fe253cba5988c1d6b5066b5293dc04d1964bc49fdd9d27f571954aaa9f0d20d68aeb2590f530c4a835ad635882f5096d4c689c2365867e66843bd9d8799d929afa399d4204bcfe7f3d32b1bb8cb20112cb887de986116de866f060192574f0e5b000410cf3c04b5ebe56aaff775b0b3c3462f69c915f876f150d7729e41392ff1bc88d15e14bd0d5be7b424b112303194c62b4d6ca987d67a66999e5b559ec1d8f0075d0eee77efb5ed83747582602f19b29083ddcac9e94f8d7546a98c1b9015bd670991d0a4a83adbf56d1f8a50baaf55aa386d19317d2a470baa0eae78546447c541682eaaa385b7d814e6ea40387c5c2c33d3daede273007b63dd708494a73e0450c97636d13aa2ed4e3925e0b69e4284f1056ca0b37f391f5aabe0c772122fe13ebe793c5c9dbbd96779bb10bc02559929dcf9477d09f3a0131f7a8c181b5f8c9ad3e1c841c77bae038fac30435756a1d03fb11f379c45e3ff6c8617148ef88b542886c55b87d6baa924bcd01b972d9a239218d0f04970899276aa7d121450417be53bf109677dec602a567011e55e0621f8361511f80685749e58415c54ccc203c40ff015a8c6361e9f33601c0fabf0cb106e86cb6e9b830b2581348cc32124bb8cd68282b97f672a6702c08e0bf0399424664a020d319a84751b25598dd522ce8eb65cc7e8b8e69fd4b190d80059ce1da14a94dab2dec0d3b06428d6a44b2f05d6fd318f5d0ec84550adc0779671cd7b906c9d33f839b554c7d405dc9e60cb2cbecc86acc98ec34342893087a90c289526b5d2507e634aee967ec4f55f46cf8c7739da7af3a334c43b11406561ade2274d4df8cbd9ba858f0592bde865b5039720698e7e688a9df4c4c9c0453d8b750d4224274b36bb22cbcf872fa8c44bcee2ab3b6eae54ff7e209a9e10eaebf298459f48e35abcac94f362e0de1261b658650b6b65e94f7e51291b3ce02203549cdf9cb3f0baa43792aaebf88792413aedf91b54d7109a88a8a94ce1100a1baf289541cd6dcd295bbbd16a1d7beb9ccada7088c89f2b13e1d4a1e4a930ff1b6a23b3e2c4427af67e0708da13543728422b272b41ae113f195a957236908 false -check_ring_signature 8965d8ca191b163880bc22851538f073a8aa2cfb354c3e55040259b4c2aa78f9 d33069e9e90000f37fc96e59173ffc5008d3e8cf95e762ad942d4588143d7d22 5 4d8117403af68b086556140f6e0ad00ac5d2d02e68ffa08df951a72dc88e8bd6 ede525663181a232fcc7a8294a639f3c3183d15ecac6237365a0b976f6bdecf7 ff33251ceb6b905d315b6bdd4df4f42a37224d6793f4eff62bb95a9fe8abdeaa 6d4b37eedf1bbc8bfafab1ed3c337559c526b6bfad8ae48c71f7db800a5a4e38 0551ecbab23851b5fb2eda5b42e7a972e420fd0682b8eae90005a9344621e00e 73f19e76fc5c1595de47361bb299668b45bdc1c383925d9ec6149df91967d90bc478b4f38fdaf2cc343e3f25afc923bec2c71995c12f6a9dcdfa5c5884725c0004b3ed3939853e6abc1fb77820f28a70d9b6aa69c6f162470d888a6c9a1eba092f24c7e80d9f7dec1a4f7c3518dcd5835850378cd9fbf80bad367c277d05720b67e3a5112a6a168165d382b55c255f960bb4e38478eedb3139ee6469774e3601e79643bc342afc098bfd64b843ed5586c07cabf58afcd7dd7e23076ef605f903fa67ac78490ba13348eb7cf5141c130fb69d3935267c81af446546923746a802a1f36d5c65d5732b6bf20f386a188a5109cab1639a739f207c1a23976a3af30ee60c62e3c8846cc7cd7e838d67be99d20ec7a4d5444f3258482671f6fc350f01010f63d4bc5c2f2e3674d94c49101da4c8a57fd3796bd683ade5037bc28acf05 false -check_ring_signature 356af9cde998730294b3434b1a425e2f176f375330f47b5cb7966b648ed7aa84 1970753a2b762415441ea7d53e050c25e8e15331799956599231fd33869a12d4 1 fac3e7ef5230c529cdb34192302527b81b04f9cdde142e93b2bfaeffd31c28d9 14b28854f37b477b4b5689b46ba9c3a6be16779c942e8d5d6a930309008f1808be664d0015729618ba62d496ccd4a8bfde132206b30933441c96b9eaf3974202 true -check_ring_signature 5fd67f44c92c83366136145263c354d399d244ccb9855fef648076c0646c35c8 4b3b265abeda7463c9586e0da49ea5b9af515b6fbfc4b43c0f560699b4b59a55 2 bd0062c96db78cc866fcde06478b150161106ac8784650dea9f0fb6ad2d5da2e 8633894138cf036d9cf04b0e9cb60d22364b0f90889b2d67527887b521987ed2 77df47d437ad470e672361dde3bc10c8ab90b547c489c1f7661ca36b9b17cb0fb2af0f56d38d0a7ea09230a4da2903804420c7dc9a2f722ba9de5a6a9170f50d7217ac70ede13b2bb13ae17b70673c5b05c7d993e440832e8d8914fb53cf20088fe21c137e88296eb4877fbf35a3cc8db5770a89a06f548edfc70b1fe6564b06 false -check_ring_signature b86ed76adc27cbd0fbf43e96b0107e3e5357984dac7841698ea0c0f6eede6a71 db87c185f53be5a0e828f17184cbb61e3d226578e7bbc366263f8424ac34b8dd 3 2670c2e17feda9f65a00d35e31db714e711bd66f4aa2f6939167e3b993bd861f c1f6fd9030ae2bb4885db90cdcc6839965adecf01910d8cf633add691ddc86fe 9fa96bf39e45b80d8da156622fbe566e58d8fa81f4712ef413e1a0b482770997 60a38a6515f48d54a8b14607a09de882f6809b679cde977871153a7214f659020ec78ff9ddaed0e117453ad74606a441772dfcb06a836f0b65d7488bd80965071936b749d781727325cadcb54bdada31da2f018096fe2fb39c2841fbd740e500623e4d82b52a5c0cc7bc5886591412f6d7d501c4020d7f3e490e9d031849be0ae46b18feef43390ea66e3b861624fbd674cebc0bf41b49e08008b76f195e5c01566b70dc716b6c171a6085fd4041f5ffd720668842e80884ed2b8ef008df0101 true -check_ring_signature 881d36cf9357fc97f75014218a1dea74a40e6b716d5e1e1272c4371f329ec3a0 b1341ea2ff95b5a1225829ea285d4572bae50d819b27c9dbec7ee27c6fe34194 1 78eaf465fbdd8d23145fca7064cba55808e5a1e31825bdd9fbded0d97ceadec7 2848714af427270bb3d5882c1e84ad30134fdc1faedf450bb9c5acf9b7512b0efb68e12d32532594b99be920ee339169f403fb16c8caed0a3965bfcda27db805 false -check_ring_signature 24687d651c339a40ee6cbf682ad50c7e750aa6b334e409dc6093ec3f9e436b75 beed22f3dba1d35c17b237e61b6723162c54f7f2348b88b0e619018ae15659d8 12 41869c396ec90f7daf701aab6845050329f99f3b72db61ee81d64cb5d5fa4e01 b76ee966f70805c655734269b143c694a7e86177a7be54c2f9a9fcecf2c9877d 7427b6918c143518201d294c218d0b51fce875953fea58341f1cf9e906fef66f eb7c88dde507d9ab0c37a36a62bf64e3af6acb04748168872a07766b05efecc3 2d4b26cb62e87a576f5f45bed4850995f2b2aa1499f55c9bf943c97696642387 22731bafe312a4b0c159bbef59b5897c4462ae542d0b57da6c75e9296c9591f7 ba62243dd81b0bd7978fcc3feb7bce225c4882339a49cfe3f38cdbab21a51d32 31a7f2d672722ce755db9f133756ef7f80fa2bdaac9da5b56d78bdd81a830c28 ea33df483f91aa5dff075996ad7af5fb84998dd88709f2fb9f72b4dc669ec198 25aa7ad136723dd32110f80717890998350a7288d41a95210ee2abb47a9f75c0 b0f343eff77d295d6c4e4797c80b7f9f3733d7bed8cb7507601b3fda8d54f726 b4dea3ec49e4df3634aa882138da9543154ee68cc48054673525e5aae5c3dbfb 81ff7cf57a8484f0c67562786c6c4c3a228b6daaa95aeb0b9ad0e74241bbda05f4c2acf13955b0bfbb1f448e82dd1bfc02c1e03229ec2d60404b84dddb949302c238a214ca399647e4bc3ba3fc3ae00b984d58b99588a2754df27d8da693e90d7ddb9be791490cdc508f04352a91493633355cb05c9e461eaa35aa696c80c00a3285dfdc92ec89f1f6919eec932cb1997093b97b0dbb18d4f75c9440937b0a07ee026dc5e8af8e4a2f28dd3c02c8b162b274e3d13960af0aa009906d31fabc2e6839ba6e3e12c4f0e912f0147a886e46ca25f16f3e1e4361e27d9aeeea2b7302d872d6d83ef9d97e82af0d18cde1912663bc66306bf9cd42b7680b9534030d02566572036e7c64b901415c481a73ba781005ae80548c231541ad610673ee760716e98e12067f5d4cd92872d0360a3821e34e912f0db1d3b5e6f172840cb74101d4705cc8df34e032e2d317a7f02959a3763377a3553f1ce2b7077596ca2aa30def5a758f5ac6daec11f54177f1fa0c06f30f974e66e63db4171c71e79b190b0ce05f73146874b2f47f0b86537075b371e03fd1c6dadeb152f22d4bf869594809c1b1345459323c1b9b7c43a366fb35f3f68a120165f1e1fd212fab4fa125360343d941d528ee45f60c2dc176c4c621cd1729ee271b5138d0abb79e2ba7cfa00c1b98c1569148f5c540f6c466c7a0988c021fd37693d8b5bd85d15334318a6005d8bcdc82c75f658cac555d7885bb57b0f4d33374a4e073de252c2dfbf7a55902c99666324fdc02994b947e4f2cdbd384739b4a1be87539db098bca678b98f50fe128afa4cce168618c50b3a2059f4219cb906e0b8787e16048b27507982fbe030796eaf417964944a9b520419d7fcf5f3de403ad8ab94fba85e9a2c4773473052ca41125645cf45463849539344fb159441ecfbde01f87cc07a1854dd68e190f8ad65c47b8c35669b9202e411414c2407e531eb0e12019f31ae99c98f4f3db06ed7ecaad75b9eb735888598d85c8b34a221114961c2b32f5fe7339499018ae0af263e584af622352f341d4f6acedfbe84e61d7e563d9d35ba4d13fa0f2f93303 false -check_ring_signature 6ef8c2a48e0323c06c92fda2d699b9c39af66e7f2f52e086fada80b37af05dd5 a0134319696992020dd40c7ccda602ce8a183409ab8213ed2f454a857cdc1cc9 19 efc2183f160beb4f2f0dd24c562c172ff8275b792dc9e4dcfed65a6e8c4f8282 616582e4f0805eb0f42874452113d77a2abcc779a49a1d48cdb82e4b3aaf8bef 68fdd57c98fc1692758cd50f91b3355879d37249f02ad56fc510ec009e3554a1 75f8c3ff7e76d546590f7f6b46326ea64c4a482a213d59e750fb8b1bf8ed9bc7 60ceb0136501b3296c392773072f1495f997a36db92785d61ca258ad7a86658e d8fb9dd0262390caf961e4f5843ce97077021ee857faea12f9f747d7d0cd6a31 42c00ddac829281b16d6b3ba2d1b9f9714d5c45f11d938e661141300a354ae17 65bcb8cd20d7eda00dfc8e3e37e95975570b9da2edd71a28f2dbac237034695d 538ba06e38a41c02fd77194497329fcd557b6777bef5a88c502d5b50551c63a7 fdd14b49b70a3b6fdab8d279f11cf83931eb4b9cae0bf07a0d4167579408d004 6fb15fcaaaf94011e25a22aa60e411f47739b5b26bb72278743959afd847dddb 8f30f43fc2c70ade02d23d24d11f1bf2f1aeed6fad14b339b6bcba75d86c2e06 ec524b1d47cf21ae8b5deeb085f3c4a60495ce9ae7ea3a1f0600b041d0bc79fd 64ec33b3d530f9c4d3182fe8d29248d3efde6e02590a56761378d67f87318e58 2f7cdd9e0719a5ba7226fc90249e9107e60f582489bbecb3427bcbfd7fad1134 277c5bfd55aaf35f28531e9d3d61d93b19e37497674fbcb421a567da54dd0ed8 873b8126c77aa3ba56eb50a6f13b7d482a9eac14814719527acc5efb7b46c97e fbdeec0f3dc4648e17cb94072044c550455b7125b3c2937e00e58f865b60e534 0f8476a97bf2eac279772fcff554f480647f6538781951235ab935db6bf8be69 42f120179f592cdca3b4dfc0405139994c758658aae19337af049c6e050bf207c016f6da3f28bc787fcbf4d4df16518cd48666d75c8b19391268a3d33f84d90f8ca692351ef5c04330fed1501aba972534aab15a7eed0efd66e925d7a7de5b099ad8dd17b17505f0aa33169f343a0e0fbe5cd2b435a210e52b8678933d92c00751764edc92e6f8ee35a2c8579c940cf976e716783affa39f4195d38112955d034d661f103990dc5ee64c7c993d7734fc49f4478b503aaeefa169c0d29e795d024e751b585ac9fcba2d3d7bdd612d2fa58a0906d61a17f7d0d1ed94e2268a2704cf9b92cae0e9b7ed46c98b3203bab97fae41a611661f863d725c8cd66bfb630477c66d5871d2a7190d270fb33dfe9115fffb1d0dbce4a724f4818cb3216ff5025bc6bc942299fc24f4812969d9cead855b71ec91f812e628d54faa5c6eafe5058088f135cf54cdcca79dc950a3fa5d72c595978fe001ae3b70f9e54b1599ea0acd6f858105558717bb4cea1569cbefdd48aeef91d8a49cdc61547d96ea4abb09bbafdb3969b9e011dc15adcc5d13117553ce3367e3d5c36eaa6441d7f706780cbe47f43c6cf0ffe37afa29e90fff3e0fc808aec8d475d659a4893a5633ee620651de835daa558706b0b0959fe205111c35f6f660313c5fd0d09b6c8739a7fa088ee52b7a97f5147a55a27e38f977486d4693e433db34014da83467a5029a150358399bcadaf551709bfdeb38f7d6bd707d7fd4db12520e3c6de40d6236446b057415487de309336c798369a374d85c5670be14ad5cff534ebd3e9248c878f008461319cddc23898d350a7d2963e89aa233c7f70043706781efa9f0ebb5cc3a26af4939df261d88fd360b8090efbc72a1f0fccd80e07134d00ca836c80e5e1e0c8300cb1a40acc4a2e6bfdbe1bf7c4f5cb2b1c7f7832eae3f071f8c5f1944270d9b2930ce4aa25c89cd54d2f0cc6115f93a12e3095dedecd37ce44eb634ae70047b73e5c8c4cfdbab89138cbdcec21fc6ac32616ae47c7b8331fc6fc47fecf205b7cd56dae364f7fcbe12fa48680bd828d78c161b30784a0dca2f09c15543a10d4cefb9d48c42530d3dea45d8dd586b08a9fa8b273a2049b2926483f42a6bb305193c330ef56b24c7bd28dd744f553034e0d98aca511481fffcae99c93f4e0d0cd17dc06e10d58502ad51fb80edecf505751d937559056a64d13d151ceabbc5095313c59767fb542d6fd27943af94b82cbd3be834a913d80cbe2c9cfbfc082103405963e511008f41dadd2a2bc2ccb91a78987e1a2e089d3c666d4c91fb34000de2ec6db26f7d17f3e7fa0b5765dc0f972766a0d62fcc7952551cc5d44389050080ae1874e02f684f39c4a4523b1ba715c0567c1b824f04fe316d091c42f9d8023f1439f24953ffbe816a3784c47ad2b4b8022c80dbe5ba06f3df85274a6c9f0f994e80619c4a96641c889d18433bb4a0b81f96ffceaad9db96db4e54b3538a0cfc7fc15cee6b512075dc1ea5d4e39775355cadd696e75d07809b96d9b932e408638bc8edc068ddd1ee755b48edeef0738156c275993bb4e7703695cb2561b70a62f8ec029dc25d3c0c2c2414f7bc03df5770892f2c14725f15f912f463d01b0eb19fcb0b603a2c78a83e98c9e7211dcf1e7a2f6b36fce11dbfed22d5cf8c5808cd456018ce42c156c9e256801425374e617f275511dca72ffd1bdda998401906 false -check_ring_signature 81bee911b16443ba029ca76e66dfe9160a0ead5581495fb52b6c1ef93569e6bb 2a9fe1e5cd6bdc8f46c10233467086d4f0bd56b30169702394f32ad394f0288b 122 1e0f3b58b5691c71bb57b151bb944d31c4a813f8c88d25ff7eea2a6aedf2e76a 8776ba6278f0cbb9fba4d252795219bf15317872f364766e8520499f4262a839 fc0e7d8276c9919d9abe530094f3d60d5935593b4e288208bbd390f9547fa30f 8b07d59020c4ff14d16546ffa1594ee492b52a8ec112303202ef27670b976d67 825bad4eee03450341da505d594fbd100576a62b07523552a0bd7541823e6726 cf3b1eb1d4afd2f538741df0f380a59842ec47c93f68e2243108f555d02ca520 01f7bac4e6c3ff73b1f3decefc6484ef21c763ee7e8c058275aea63ce2219892 f21009b5c57c739a73ed64b90649a1636ce07676ce91a69cd4534e59bb016296 38cec0dab3021da554205150de34605ca6d959d07280a94bfc8dd8fdbb537f03 f7b94124fcd95c4bbb5fcd4abc0517980b361b3e063cc7c5d9a93d50206c5f84 05867b28adab77dc7845568f5c47298c779c53b5528227d6c4e7db4680f38f96 32c6346f18c01bbbb5d8d6ad4761a8e02668f9ae3bc331e031087858c0732796 338d2b8223ea3d8c2aace14ec4839b0275911448bd7963b792d0e7acfbb135d2 dfd4f2251ca95d021750d1efeea64b5cfb13c7a7f0043c576af84e0d8353541d ee38bdf558248597a693aad9a1d8ebaecebf289fba782e84e47bcec99b0f0cf3 081248f67b1d5f6a26078c9be014c29019d2f80647dc36c009e27382d960ab9a c74f6d578eaa66ede4212641e3086b264773c00f916d908e58ef385262cd60fd e22e20f529810e0dff5f047ecde769d0212a9f82de177a35397dcb0794bd0eee cc0408922d137a61be55f413e92a731654b6cf766d8aa756991f9cd5c5da26d0 c0446f676580df7c2b4567ce8d54c432bec9ab53f6800bdff79d382a395673a4 34ae5b78208c203668e27d00f0011964aa40bc540411c324cba492dcda02cccd 96b81faeb6b994397259cbfd96b5736e55001f5c4014393304a057a13d7a618a 5998b7c022a0d2ba828c5b5ff7e9e223aca23521b1581fcb1e7dafbf70721ea0 eeb49e0c4bde98afdd65bb10c7c8789de21d953f485850dd8e48369f3e27f4df 25e477f581a603049dc19af2e81a5c9225abc131b4cc9c94830f6af6655d8c20 7ed3b5e96256a472d50448ebaa40671fcde34f63c73da8d969520129c5c81c9b da474bb7c0fde1e7ae0bcd05607ebe03f8a5a325745e3ae98ba2135b640f751d a12d58f2906c47a5757ff3b24a92a325b42bcbd27ecc7f4800fc58b556f6f857 1496d6f202b82341e7b6959d96bdb390ecc85c04c9ce9a5560b09bf6112b3b48 79f3474bc2fe7994ab4b4cc428d3ee1e0cfae9f85e068e0516abfd52e31bd7e0 5fa1a28d5bcf3d172d58600de20a9d799b907e47c30bbe35797583d836448232 6c7ea8340cb35d9d4c614f3801d518b0cadc4567c6415bb0b32f46fc567b3de3 d7482b8f57969e565771be0795806831c96fd1a0e6e0992c01e8ba2f7a97ebc2 bede671309469a6aea3aab599d063ad6b8632a69f0a5844b87b4be2aa905d0cf b7e88b4089354a337ecd3a32d47668b5a3bbc334eec139e81e69e39d792fbf5b bf5cd681c5bdc0c0d5aa37dadb58d4f3ba75d6acdac732716042e66cbbb46c52 cfb41bba823737a1c6b2f6d22d99ffbde00f3d4d79bf972ccf919e3068694f31 efe331fcdfd9250f7b90b7e60b0d78adfbfb08a81ed202308c1837d97601ca38 e2c8e6704b2cead482215f13fb4dce9661efb3ce7759da0ed59122cd8c5effab fd6adc288f4582db92cd3647aed431a0f057c30fcecc5e7a91129fc28585f237 2a67beb3b35acc0670b6832a7609401d4577b313596878e514754885ed875117 48b9d291a398e7759f7e7ee9ab59d3ee01f43215f8ea29178c96a6ea12261ee1 cde14d7bfbac05b4454a85f57b577952cc060495a6e394507cc1aa122cd52be0 4d8f30a8b2f9d17174511890a333ec2f03f09e4ed6077e4cb854a9913490748d efb176907b2bf5557e023afa85f5c584912daf55dff3fbea35e4bdbef75f2c19 134866e7ff62b347e1a5f3a20680d0573de91db71707d947b0457f9e2fbed5a0 db4beb574acad330f43ce43073c81e9580fa04ff20d512adbf4162bafa54916e 673d74dc1ec22e69867aef3075081a6bdf07a606e0a3ce13e233b16dc9d430e9 ade1a84e5ce21227d59b63a5d252f6b0ccdc9add00bc83b6c05721f9db2c8826 5759f78e9bf1ac2b6a8a66d154c7bb732f438b57848bc387c19f1810d505e17e 411c898ccab42675d5cc5d2dda6c83fc0121ae8a2a672be4519b079468087911 bf8ae11d8c4bcef00c0ff8f75fd1232fef2cd6daa68736eb4ea32bc71f64a1de 91b6a46122190ac2adf28982cd235012c2ac66d4827ef303f9c94fe814e3fef0 bb7f32d7f0559f7004c959555dbb7ce89e3defb3ebeb328ff77ad89969146261 09f898451987bfdb6f1cf6ba70c5e634443e7a1fd9f972b874f97c943eff3de6 98ad7344a734085e41772a6ac1ae665048c45453e53524522ce22cf81df78010 7820282649e7f2248dc97a83a629e788db1d620f6ba614a23b8dc2f02eea5cae ad3b571caf65704cb31bec6fdbd3106789c9c3e40e0a30c293fd05f7571bc8a3 3d41dc1f69029c99fc47abb23228495bcb1fcd60ae63d59379cc02298beb3d13 c72e6bf9e36064cfc5bb16cc707effc77b6b228691bcf1875e69b5df0bf61319 c0b49a45ac0772972cd5872625dad0ea2cdffea52dae86078843b90f4568979a 72541e77d109d12631cb06848146d3b812ac0ae7333a8691303b052eab4f699c 5eac036fa37910e0f3995d34f150559d96336a988bf67b3b03fe67b3edee74f3 1f4e43ccd4fd9f7434d4dc24bb18bd5414ee7698656aee324057e6336da0ef36 42b611dacf95990f4dde2b40dec64209ce9dc2f9990f1a5918c9e943fe58c238 c4b32807009c668062bf41eefe966ab2f7167d885fd85b4b1860302ab80b0541 ccd7c11ed3b3ca13a0bcc1d41de7289bfabedd31de7cbcc41f310289e6aeb4fe 8718770881cd44fff95d3070ae4aa38d88afc7a09c9db529120533a5fe4a2c96 baff538775723b816ce5ee332530b6c38ac70743c091952a9c601c5c93e78760 6d7ca0236ea3c5b6aca07c0669b99a611efcc4c3ca2bac5655cfed43b3013977 5e61ce8a6a6210f5fa113b20053aeba16667778f9a3c2e8d95c72db517f840d7 5fea51f0a29a2f635a9423ea1b414de5a3a6c28329c9b2191b15ad504a4fa72c 006b53ec7628d26ed34eb2a08a2161468120d05d5c2045ffb6e29a014c023618 f711374c94fde02b25b3fc448f93585204a234e78a5d65353f2a96a837ee6ec2 651dc885c6143d4aa9388ee09987519ca6d7214913de21f745ff2cd01738026b 8b226ac90dcfa5eaf94966d2d0717ea5d56491d62616a8bc302f984e7f9f3ea7 2dd50126bccd46372c5f8af58ef9c8b78692ae82f29dca7deb873fe708e6459a 9ae9ce216c55906ee877a379ceed5c1d6f743823323585bf7126696914036c0f 5141228b3bd3eeb6c0795f67de6116eb42923c541cce614d3ba2b8c4f7542164 e30793c025d20d15590c42946e9153353cd2ae70db294d625c3bc86611801041 35a6472e423d24648c7f68a9e10e90cb09e197394ac959d1c27d2bc5fa70d783 73def0f5567da24c7c9b72d8463856805df410046070f0d5114e14053b9aa087 6333062c1a22891bdede7a8311cb4df8e90120ae1878037e313fe51a65acea67 d4fab3e841151c85cc5ab6a875d6b8ceaa0776db9eff013757bdf82563c31c05 ba8596a42582875a0c1041519d7fe6195ce2c7109faaf4432a819f6690f1d85b cafc72e280bc15c236a4159545ee291e550a37a5858c0c692deb1e3c42895ee8 437facc8128d3958a9dc4de8090dd15fc9e4fc552c072b1c5f6e8aaf813fafb5 eb6d09442be0ab71c785dcefe9ee91fc2540c1c394012712e342ad3dfccfd458 60aa7ec5c9e9387a10a3298570a8e9440891501c5058b4c12085f47c068423ed b42158e20c90409818ff5dfbc09917a1438f96eaa9e2229f8ce03d41dbbbf164 8c8f3ed6ca64bdbb31b40717b731c730c61484b274f008f831dbc1e4b4fe2082 11cf32d0597f43ac5f2f0ef5d271e8f29a306a352e3a79f3134f74f2849b0a70 291a4be167e820cbb6b84f71d462fdb3421d8757e7ee8fef329e6461a20a5b86 c27b46d9e4489bd6e3c1a077bca4723a81af3939fbdd7f4f38612a423482a8b8 42f60fad14abfde9e8fb30d210ad76665df2da6ec56921cba422ed65929e30ac 5ad1c475808b79b60663f12565d16440f18e51232e960990e74cf92582e740ef b88d767b78c0a7f7006aa5ee252526f3af162bbe180b10785f65e144ec83f547 6fd64a74fe676831d72e4aed32227a81da938a01f96ba7468ef900de836ce1f7 26d4411b6129725c68cba986b5c2d274344dd9c757332aee8357726fd692f954 a1657140d3429b1c9444d4bd3976d0a82ce1410a25be88f4430eac75f5e1a176 206f7f2a963896ab9371b7fdc79f417280e5eb1d995968a76de3f347a486e14a c9bd2c17b63dddd2b006489f966c1ede0129b4ae55b150c8c92fddde4d96b9ac 8125ef9615d8bce1c76bdd5a188c2bf2423694e0b39bdb550cc5d788a42702a6 9617a8c8ab725186ec64d7371b55cef1233884fa8143be6eca37037fb6284547 d926e6562d45fdf93c5adcfb5136c2bd005dd42a271bf3ff4239ccb025671ef6 e8c353e8672ad537d91025b58d6ca2fb5dc90fc1f5853c42f9eaea42fc1e470c aab7b6b22911797b73c47d2d9de46d484893e22758b173a4d297a53f2026ec7d 48ea4de2bb4aba580715b355bc1f2f1e36861442a1d0db0c84f1aea7c316e44d e2fce36b7cfdd9740ad68ffbb12958087d1f4e6a8ccc7bdecd094b5a9b818130 e58a8e5efa14e971718e3b44c16f492a7e1044c504a00012cf8b49d711039a9d cf08e62fa69729d511d7b7e5a593e0125931120f03ce26a739db4f5922e77f55 9aed90314a1fb7f321b60b9bf7bd5a201f55022e76407694f6472949062afb96 b9ad468d98e1fdbe182d462369b5b259c67157590d1891f802c172c4e47dadb6 3388d62c0a0840b5caa7abd97dabc0eec457e6b4829a326974edea59242553b9 e910c0aef9070b7356a802f0f9ac3d135e9efa23831d8a7995aaade126462ae2 6fed3f83d3d4b92bd9a72af6ebf812ff15cbdb1cf7bec252b1361763828a7f4d cf76940c68a0a4a4b67b663ec7249ce2d4b2ec892a6b06f006b9075ede677f04 85114ed03a6ed577757fdfa793888bfcf586270319ad81618943fe8c0ae9d87c 71cefab25600aed553092337a549dce4fc3102483046c0d5980780844ab3a8cf 8179a38193f4c00e3453a0d864124c7ac03e2f9a156c715d7a653c5e2e9ebcd0 f466ca00dbd13872b5dbb59dccce27ad1e0bd212b3ca5ca9e93fa3c38aedfe1c 1cc5cf323e5d853be14cddb1f7c6eb5afe23c55a4e82451005c649080efb2bb0 ecd76e7851c3f1daa478019492e9a6128243f3f714eb36f4d6967e55db1a32056373bf6d4e3cc4ec079e3d0fbf6605311f2984bb150320cf947131309ebe0c054e5f17db48db4edf37575fd2246e268b66989fdd1e1b6b7fa7224f30a291900ee2f7a6fd512c295f600cf3f19196b7066af08a155f8816321b4ab7d13fc02f030eb67a3c4261af71a9b8bad50506a93445c5835587d31a86aeccc6c26c7e060644c7fffa1320b95d0a6f1cf42b9bae8f39efd98b984df7f005e36d68b03489083fdb8cddd5486af490efbb00c56f4a8f8f3cf9cec1096987d8b246a4312e910b6571793999685d3c652bc45b78a3d0eca8a80979e3355793396fbeb8c2255e0e04b24f4f53397219784c85d13132ceac7eba708714ad65490644dca4a081960611306311ff437c713aab5913f02cc5f23257dbc5d1a1e21c7a7e1c8d580ea508b6ce49816f1e3e663864733d518056ef80e1059039a8de4884d1e6483caa540f9c2445b03797bfb04cea2c3a1115bf106b71c6a1f8ad7d2b1c31baeeb906c50a19982d3bc5e2c17b5729496290dc30257c530c228ca3f9257b25307ee3b8910d7255d2545e8068ab159d70754a1fd5c79c4ca83cad1a0963035e090fe764070c0051e5cb9b91c3f597e9f5c73bb3e1d5a55254f3b2297cf02bdf95fb41f4f00b74d842094c6e1c4a687a5cd3cbfcfba91ab2f464eb4300eca30d9426ca86050fc34ee8d8e9552e61332ec561b759958b1221b9d160ac6dfede052aba67885b09dc55b9a04e263191fe44e6d76af0f7923b4d37e3d2edba722dab1652ae3e100510c66cb1668367a890472c5469ac3257430f4f3694aa82009768d71aa87a1b0868c16426385554b154a3009da19ebbc1fc02466463c0c6bca9f8cc76330b12026753f1333664ada63863960dcf447dc8db070156702e06db6c0f881f8759370a0020850473ba12621f3f92404c6652c1190e6216431e12929de1ab14faf2ae03b31e6f9ebdab6c9a3a8ba8064e227b4fddc115aa7d9600472056c2bc514acf0c4990c0b096d9e4bff6be28bd7a5f3d0cb83f8ad6534190ce061e7a527beac209373e709620c999e09bf2f32ec371633d55ddc4f4e7aa02b124ef72cc84e5990fe28e4e0b9d495ff73d942c217d9090e880531e6fb50578a09f50cb0417dff70b2ee25c599be220c66b0f110eacf9a71fa23fcbc836114dd3a535547ab0a1ed043de78e4e90be64eb94b7f4133af88be203e8695f3eca63e0e37b56f28ab8d3088dd06113d7af767b57e81bbcf70ab091b8cab63ceb9c5073846d01b812d1df00295156b21df15e756567ae331d65373eb52bf33c5122e5c46b714f0598e2c308a9fd602175c2b3e4b94389b42089d5afa4d680bcf6af392a6bd7ebbb3143400e571779454f88f760f8e7ecf34ee0dc7964a2d22064b26619b744cd6f2d98f70dd8a27a9034cd429d3c2f38708308608965f1369f8fdd8f749e85bcbb875d37019776ce2f229ebd4477854e334ec27b94a33a7993cc232e541ed8199107706d01d037c23dad12e0ec80de09e5dd7cf40469def8a6a09b2288adf5b0c39629af063d65120591cd2d705c09614e97da9bdd4513ecab8f5ae950376aefdf4be588097738fdd569e39de068eed2d3db93df4b42ef004959ced87e083eb34bca18580e2460ca31eb187084281d5b37580087b00a3fe820e14bcb6952b8cc8fb3d01c066e96990845ae0fdc2df1fc7fd751e7bbb1ceec689cfe26775da3ce13cbee120757f0f6c58f7ce61f4c58c9673f99c90cd9c9960c35d35e3b0acced3950b0cd03389f55c64ea24db9185431f5aa5f75110b5e0adfeb2196b3cf1c77d9c5567d0bc97501dad93d6a20018845229abc06951f5e6f7f89a2479a4cc3fefebffe470c861f95f565cea64a3dfb0b824410cd8717860f8bfbc96323b18563cbc31df702f13c0db10ddb160b035fb36c80f9e69a09fbea96f9307a91664d957c93420e0db6856626fdf998b947c9d561ff9361aa87233cf72c11dd9e7ef1d7b7c0c520035d0453a726f11ecb728ebb88870fb32053b004e1ff24b4a0cbf21876ab595e0d95ce20086b54986e892d2f0cf825da12912bf16097a5dd4760273cc64c6fc0064b96bdcb6ea8778bc298de6d969c614beaed5ed2e03359244bc9696002f136086faabc4a8ac73472efc7d4d90c0ad0417bfb1e9a0545b7a874dbcfe81f4c240fd86f42737ca6997aa45a98e33f3c979857efc5868f56aa82004658e7d98b9e063d194a084699350b75f516487426d2b204ed0e9a243af8ac58486a4c2a2dca094fa9397934855de65da64b26cbd28307b6cb2f23676957b6e35f8b484a1c4c02feae0eb05a2db70b411ec5e1949a5bd4a86087311c3b9b8819529a3a1ebb2a04f1afaf583ea32b6b2ebe0944b7ead0a52a8c2392f0273d17492315ee522a6c022b6f5923ebec26f4cb779b6265ae00af1cc922400166f8dc5242c6300f414b06def49ac903ad7933deb97454f07312d53842295905c39133cd9950bd8b5ec00b48f249593681da5509cdd8a532f28c926b8c04243851b2d63151a4a9cc46210bc92c062e2b69994be9f9480df3e9bdff6ce2108e15abfb376258bac386a32d079efaaad871cb397d402d27a1d3031a56a04319e591bd2917d1545f4063604106ebf0bc62d4c5e0a0131e70e5c5c2fa40da3cf1b24025ab5d7957888159b3de04bddce71c62ad5caa11137a307e197ff8a1b36a6e2fa53a73f32e08b3bf7ba109cbe316ca1e1a7b9f33d4b516bc76cb522b538bf4be11c453ccbad602aa938a00c8af6ab78746b1778b5bc77452ef6ba8c6779c8e7755e6afd935c15e078cfa0ec15a7cf849cef2e8e2df9b0cabd7d4bb265e014ea2e612385dd3e947d454610bd1bcdc0f6d886578bef03b4cacd6ec9ae877e8b744634c3480ec721cc5d88109e9175745243f119b954b6383dde3204f8fd40bb3bc97771570764f497ca813044f474b622affb82041c74f440944cb6dcb1be4c753c8e0d603b32ec047deca00a4e5214123efefde53cd1a87211d2b2df6eb5e03af8f84bdec825cd326f9ed038a9d52fa9bce65f67da24b24ebb01e7aa02e5582c8af043d7d7bdcbbab32170ecc8df189e558ccde277f91ed0e221a23999612e69794d274cd5e6ad1ea694a09869125a1be5b0cb0b7f311ecf4dbef43ad9aefe4bebe7fe5b53c663e69148101e620cd871f0507e45650d70c3b41cb1b188a55099711f696fa5feebbc2a9a60c15e58cc9a329f6a5633e295a69122b5f389772a3891371bd73a82a6bead2e300522002fca87e15941e8760662d72bb3b85e0b49bea9968404b0ba896da8ad501cda9cbac2628ae79f280d2c1345963a380cb3357265a290b66c1c9598511c702f8bd9f01d58fc6005c7d76cee248731c24f99368c0d02d0775974c39537bf107e36f3db9e691b8b9f568819b204f5fbfc213d3aa12f18a4066f893fcebfdd807b78c224c62c036d6d0accece08af1c0320c56f9027b5e797913adc66a1f0b901b4b91048daa646afdbd745322ebd9961cff4f7a7853ec5c743aeb67127449c04550f2d6b341ae0fa44f0fa4188b51a12d55ce718114b07fa2b3ec6b0b57d2707fb18bebeefc0b20566eaea96627a48d0ea1bd2fbfd8bbc1a8fa46ca3e5a1b606797487f3b3b4ce214e19d3ed85fc2f5b73bb3ff995b717773f53efd990415d06e308b3bd9dbc2c6b7cfb02404fa7855b3d7aec2262207922104b2cf7362394057bd4f96afcd5c62ccf2f3ca70438ff7ef2abe801cd02e0d564f095461ea2f401232e7136a43858c922d7b6bd18fbc8c5ecf3699a4482d08c29ff2de1ecba0f0e359d20c0ad1efa99a1c9f06e540dd72bb9adfdbfb572710b4c72524b9a1513062bb5c6e4936d3463d8d39db6824176418c212f11c3c7f43751bcbc0ce933c8093ec0deffb64b22df97fc25abb98b3994aaa5e55a2f722e63a84dace255afc80f50a4a498a66aaa066b65f5b4cddf2b06423ca85b9b85e6d42ee38ed641ad3a0e7a38fa3f5d0f4cf3f48896ebeb5c42f220bc1b937f4249ae2a6c914547305a0bfcd0af5ba7876f0192afe3dea6762ef26f492519573842b3a7a4bd0b021b5c00a80c71cba797cff787bdb3f04ae240a7b9134281c71da7e103560c71b0fc8f050ce40d1d94bb793007f749d67c312c99f37be805cf16303716ce234467efe20dc3632a73e813ba84335185042bfc15296826f44b7f4910968b6c3fb3d1e7f40967b0f8fef706d229866355985839f5a7f55554b01ebdfce4b02db69b98ae1e0aac9787d9c686f32b140f524d3e5d7d0f0a5ffdf34151ea2bfdd4eae9ba06ae0bcc21a9852a72005929b404f2952b38c1177e6f1a54951696e7a2abab7b468a0677598993d680c1169270c9fdf9f8c08bc064d370bb0d70797a7acc1d7408ce0ef11a606e10c1162d071b4665b333c21d35b9e266d9744a983b0050f911992608735775f716bab89ca5d0ab242419b6833eae73e97cad1cb6ebb3a8f6c9d20902048263e53176b6c3cef3ec5afe4e3c2a06f66f362291c4b87402254f15feee040052e7be09aeda7b8de2ce810519db54de88cb4ac5af8bdce7a344d6cae5b10b8e35d4d192d70d1059d9bdfdf6e27d45c3f8b7cee07d72a78764d20044c29c099488699d41d41a8821392b2ac69936f53df592ed8f3711b7812a82181f780e0e1d6c67c49aae1b3770c62739a05170df3cfd4cb2abd5628793b23c8d8cac6b0a770016b0a93ba27e39d63d38766cb59e32ca58cb7053d1ee483de102657dca0194f561c49da0fd61271e2f702ab6e2bcfa39a8da75a40945e61f942c14b4a70b02d8a3accd1ee541a6d2c64a56e4dbffec73f730ce77b8266d563ec68f9f340188f1c9fdd7de360b5365b72446309480d3e767f2224fbbd0a329a02a54c7000ebddb05add175dbcbd23215fbab37acb4245fdfdcb50d0d546cf60a69a71d0b0381037a2d6641036fd0d5a9d4a4ff190703d8dd4eb252d46a4cfeff03e9df6a00019cc81d466fd1605a8e124bedb4bfa4e2730f5daa3144bc68657b23fe22fb0ec1d49da9001a372a9bcac9c4391db936d81e4eec17ecb81a486dd67120a8d30a6cb4fff441482b3859eba714a3791489668d17e8a624d525ae9be19a40d0dc0dccb2163a2b691a3054b11d662ba3551627a806d867f24b55bbf9f19381ed3a089802728ff0c80d2d0c5f830732e3ac7d5232726eecfb1b6a707d1e3deb33970ee561ae0d0f4a53590a9f6d3b35f01c616e8301fd1b66b0b36e75e2dce04a500daef0206c47456e238a4a87a1f1d5b93e1e16524aab65ece0a0e84b6553c523070123a8c08af786b442b5b2254d4bf1fae1976e0f62a4db2a8dba021c8ca35202bf0b10134e45b8a595e5fe70a627324619f6d362888c6f74f824e6c094dc980dd2679e9ff91c541240258efd19d4498ac9193301c3674c29ea91f0eee794930df91d2a725cf70e047958212da63e870cf719e9744dab20c5fb0dcc64d79cbe018805e89a2214c570d6bc2da794f19a9a2f4b3b4c696cbd4f417c63e3956a7c08ef3c62416ef45d96eab13d3f56399ef8983be8fece15b5382e5cb78b54be9a098ac12a8a4bb61468598fe2259807096318e60d2e02a893f13c127ca1fcac0b0ee4fd678024257a4ebc25b9694e11917e0dd693a2be67e944b6ae538feffd5705a305c47bdd5acc42e92e2eba519e81587519f7e9fe30b1a4df1ae5d4a76aa80b9e74b99657a04dd45b4e9b8dac408b33ee25ccb9a0328d1ce4024d997c1def079d28795d2b5b3dc27669dca88417d0fbe768017b8828a3099114107dc82f4d0b4632fbfb746e892a8b00fcc4c24b34eacff393abecfc6293e344d4b7367d710274833af5bd9a3e484eff1d3e98f03a8186b27b4029819bc8b9cb0a74ca313202c04915eba556a95dc38143e7667533b654dfe2a2d4c078ef16d9aa72531b5c0698bb6bd92d1ac39eecc6cb3b19ee1d066fcc1bbd55493c7c264484db3edfc1012eb6c72e40f53be46e2b1a9f0f6d33610ed20015b1844021247797bcba7e1406570ccc0bff58027497868536dfa77c553dfd2b94f6e0dfb13f18fea3e75b5c0cb10961809cd048df47ebab025f22f18c7cb174c8591dcd7d952ddd648a71a50957787cf02346bbde9f653c8c5075d5792ca01d22609b86c6b117c25bc1e43602105c9e6072f867636ffe83782dc8a7f2c8a464e3e16ff2f641dcecd0aae6330969a3015344fcd54c7153d1c92126825f6af85fcb579b901af079cb696eb57e0b5d58bc5be940653796fc58676682756d67821f638c955222fec2f86d4baf840a5f9961411689f0e80a0d6367241c533edf3d8331b0b7866488c5cef0f605190f97433e13ea907b32a622e08011e007e385b869e1d6258452923a61a77b57ac09713b49ff7b11502b42f895558da27c8e6e86c3478d88c5dae9b8283e49c6980f9aa097ad3db95ce182abc5352ca63496d3092e32d40680306f623347ed4f600cee034dafbe8beed1a0720106a3661f8dde85c450cadc2aea82f5bd50bd749e0d11e68af0cee6fd7c0bae54b61ee35a86238798ff305023662031ad8e81652703b0d2c6fdfcc9415e2cc9bedc06d819d3a31d4a524f471ef83cdbfb1da1cadd017d70b1bdf8d7e5d3ea3c235c4f0f429da743c62776041e4ffb1598e655cba60d71aa5df954bf0b34509f82fd20fdc91c38a386ea12c156f091c40ecf2a4b0009af5b7a3711d4dd80d733a7d2f1fc1f47d1cabfb72684865197a2e58f25b8370b37272b07e9ecf1d77c1485506f4fda09dabd7d60d4506c0936fb4067406c500ccdca200a872f475448aa8e60af3257889a3d2c04b9c53c8b3aca053ef3deb504306063f89157a7f01cc521a1ddea366de5477f20810b01e295ef70c652fe0b071e3cd555a3dab4cd17e64d191cf1c8887eccc604cc0a106d839b1f906394cc030e2d65ed6587f2e4b0345cf9d01268fb1b2168da464caf103dce52001111df005c57164395cb3b67b744a367f5c3cb1041f2f346665e3a772f69b7b4c83d51017c8b95b957c4d2280c14e1bdac0886890198018eae45086f228edd8bc266c70563d17a4c37a71e1bd9b0bbf83580df90ddaf9bbe06a830e06e8b5854a7c94d0191e5fbe3acb2fd20083ec377168952efc36399a00d6e39d2b2bfd1b8d7c4860fb530d544cfd9d8f84ce847d0c03fa5e788481d951731e186448d57fdbc369006e19875b231ea8522fbd69bd88889b8b79fde5f186f2022e64ccf1d378491170bfcb2a7a28a51a0115ed22a3bfe547cb5a57257f01f06b7d512635f6899a1320801566dc6cbece282ae1c498c0a8cab5d976235327eafa4e5bdf1aebb629b230c72d7b01e1198da7c44cd177cec4560d55b5012e535e6a3303473f11ca953860b156f4e5d276619f1b806deb9fa7885eb2230dee1d3afce1635e0ccaedb608d03d40d8d7c7be6e87c393faf333377f94f589ad04b047792f6501a04d9da746504d5b51ca12c06e655d6263629818858ceb1b5901aed1c428fc980097b69dd2a0dda88e254f5aad2bb5fb233e019751ff0eaa3b6f4c4c547919b7fd6eb350ed20deec3f69d975c64f023eebfbbd4ddb7f186b90ca6b5117e10cf4e98c4fc3a52037179af49bc9ab64f1a2dffa517290d178f81ada2bda15de3b289effe84924004b90db3055c6f2494e466bcbc9a2ab6a364b72eae206a4a20f6a4ac17b51c9403338d10078ed44438989662bcdeaf04424c61fbd36b0b7d7818f665a5f4f78d0cc11bffe02459e8b1e0898f9eee86bf47e5e486b91d9fedf55985703248fc890f21e80075032ef8bca68dcf75d137c4a16e396a26ce60e5667ac39543c23ad70f147f3027b8134c693fb541bf34180f08bd4234bb89a883498108e52ce8e24006b05e419560b595e10b0acc7c141103ce5c98d9da6c7daecab9239a83d67ca10876a1c0d93fea4c3fa71590acda79e64d262b08a4d05ac36b783bad9ffe968401c0aba9c2110a127a81bc06e189c8a464613f03e041017a822a20208365fd1f0bf1b96e533165aad72cdd0b0268c3393a3be523588e0cc19896da6f3c3b910007b4aea24fedb51e40210423a66f42b57c6803a1850f7769877210ff28a4a4ae007d056701bb122b84e9d10c56b9a81ca4490dd88ca0b937aa4a3ea3c93dee920d3dc4dcf298ca0ed014145cfe63e3c599596cfb6e18afe9a7e24ff986a04d0a045068881904f329ae10ec9216b7b44a4390d57eafeb2c41f9b7671ad1fd7e36046804ef2d3bad817f78c183d7e95e9ccd83c3351f13f3e98abb860f49ebe8a006b832ee8ebc19c3cc82257789832c4147cec21c6db4c3b4e9202195eb0acc110270dba931d133d1e34c1dedb8f69843d203fc02412b4472f1a552a594f43aa80a48be3dfa9175aaa5d4ad34bf537b30a596ad86eaff8c494cbd7c543ccc23c101fd57bb4e334527ed67c61d0ed526e058c22e3771512880c69e3b75024208a501ad97ab80fa30a203fc932cfcf2a1df252995bc0351dd281446a14ef1d9fcbe083c4709ccf58a3d40d1b98d05328fec9c3292636e65befc5f9526b97640f6890e6cb3deef0274f560994aacb05b7bba794d12d1598d78a55a81e444435232a10f86207be6d0c61f08f808c1b7b186c5e7e76684f4739c6fcfd233b9e0a8503404646853fdaef822e6df77d0f907a363132f6f77e120b806420df9ae26c1b22a093b8933fbb3278132e8d69203bce8b465a15468e90f83c6a02b95b629e2f87b04d9c6bc89e889d088b90a98a18813f1db3aafa15a2c62c24a50c73cf205bb710ffe0f2f01ade7cad744280f766a95fc44f07ebfcaae93bb55d27be551679c6d01629226b26b85d1be746744577cc57cc0d3957922cdbae7f43f19a87362298e0f889ca5d85d7cfb6e7828298a163897ed91c6fb4bc1fda7cf80584ad48aba5c0f127c432975d46b92fc37d709ca26ef01f404a64a8da0337d657f6a732b24460bab38a613071fa8fafadf39169384f7043965ba933a04cf3af399fa416a66b9061f3eabb08ab52a73c9e83bb27bb6c99acafb255b458c5ea23f5c6d2c4274e90ec9128bcf25400e860887e9c1a6e0532d3b547960f264cd4aff03cc2f470ddc00d648e3fb1a2f78e55bc782a8f34498b4c464861a3a4225f79db9780382f7760841114ebe8ca2d830ed593049778d159bf62e67154a8684e749a2db7830c1e9022cb2973ca0e0c785b5eace33531e6eabba649413db2e8acedf6c284c53339d095a9e5f00807dba950f42423da65aa3a7b626def6a978d678cbbb9e6899f0f103edac3794b56ecca14c89a59e29230300ba88773aff0c4228c6084482c4778e04501195eed1f77761cb771bd651c0b830a92f054bf84f9a2f7fd0b50b5c49de093064014b6833d7df727cb64ac4573667e76922f5fea130a59a5bcb2f1b862e036e7c3a423e0a266515f93f513667fecf036cab9c58dde2da527d8ce8168fe802141f821dddab995aec4dc2000adb382d8716255ace0762514758bc619ba431040e14102b543263b6615658d78df33bb49103a20dbe77a7915dc7011128c864056d978d7b01ed4a6d0de9b68ed3787ad93a07f8552c3b7cf7ce04c8c86214670b067ad1cb38c06f8e7b0a57a760a32f4834eab2ba2e327aee2207a0681992830fb181910992a76cde97504efd5d40b7a89adf24354cd17286a7d36a5c3967c309712406d9238f849ec3e45cce835a244017cb0282cff1d8832c1da44698e3ad0e34f19db9b24aec7214a4cd0bcf8c139cb8a67244b7a99a20e1d7462791436a09822840ed6fc30e1ab4d1dd8a6f59a6db5759889fe8681cbe3f3efc48e69b030ef315e210c133612d6be2d0d4ef995a76da5a2441800cd66b1928d4d8d248d90d77e6be0d8b88572f0756a3efe8875ed71ac6506250618053a5d8b2fbee8b540e423e8f0069044ab26d358ce21a76bb4185839c563b8d2378f9981783095cc9027cbb1d489bf9987a923f8117beb337f66017a2e80bc7367ec3833e446c66240d2cfb8a6ddc92d8b4132d22d316b74c4c6f959bcce03595de6556ba74787d320b0d4a305c740e7439893ad002f0be70e9f4cf134a17c3b0e22d661b6e5ebfa100abda05b4386d2abb7aa44d2354a3a4783f79de87de6df0d0a41e300a20b4300f3083d2ba7457ca820df3f27f57c8b16fd5a8542c60af24be074bdc726f91cc07e24a887042ba8f63dfb003818010d763c8e1013e56055542e06a5fa17a816906f4b9e7f256444d012202dbf9f752622b3fef6d1136dedf49ab62765b587dec0874c86ee5300ce93eb041af5e45a596c53f52f96edeadec66b08d8c4d5643840808fa5d9c06c877353cc10905d2f46fd7614547dbc36b3d1f550cd4c35b6510009df34b538c6196f1ef8e93123b2534152c065d543b0df811e67308f6fa88fb0bc292c74cf761ba8ba17ad3f0d194419f33e2ff5ede1ee86482042f9e458eef03e450e8eae58edc0e7de1afc104e51111a3d4a40f1d77ea4b7272ecb322bd4a05fdecaf54fcd88ee55a2e9f2eaeb098cc9375a7a3e2965099c895af9b99ea92002a97f3bbb8a51a5db561c6e86e1bfcab00261b7a360acafcf7bc242d359cb5079bd4c643a1762bd16d174900d61fd20f4cf980c3db5373ed0a1eb980dc3dbc0b5890b7512179540d2ccce19e58a9d78fd10874acfd6b075ebee6975dbf1dfd00d3ed8712fbbd586666a7c856aaf57a710159831b4466e8b6d8d77b761b49180247e913ce0abfeabb2b52a79e062694544452225b7b27aba27c02f332c7b55a0a6621e949187e689b337660880c8247083a2215b9c9f591478db21727fc36b00a489e6ed302e215dfe9f524175c2bbab45556a8f4b06858f39c1aac23cddc6f0209f4d624717cabbea6b3fc32ff4a765f39940be3eb2a7dc7d9e6ba1ea05aba0db0146c49505a24ce2989b5cd496fc0a2dbfd98d3882c2682eed96bd7ab85c30ec2be68a9688b17eb4c19d3f0091cf312095ceb96b4814ad4a51d3fc25bfbe10f false -check_ring_signature c76f58868151463aa5a449f96156b9dcd716ba0508b3aa72bb4cc41980b5ca1d 678716decb7c2b7b73407b2f2e5aa8b4c1f0f3b0fe0a63b3f07cc329bbfb7cc5 14 fdf415a5bc0ebb0e414dc477ed260e01974470333fbe9c55d2fda690c7df025f 5f6af3f46030f62b267a096a50d9cfb6346d366b7a2366c4ec7135b557cf9f74 d8412ce3cdaaf4921fbd65443285e1d358b734484a153e63205b623e76a17023 67808a9ceefa45004a4d6a11c2bafc000c385fe72b79b5cd4004b572bac60426 707c95fbffd669769634c2ea43fc7845ed588aa81054d5f17a261cb3d55c70ea f6651d6dd3f7986606d205001b3e518893c4ca1048a363c86aac29ed03d53fbd f9f8ce21a99806109ecf5c75928652614bc1cbd22a6cadff66d7b47083cd891c 4d688ab29577262bb289dccb2a46241abb32676a86e0bdca21270685e6e8cab9 997ff1e49566641fa9a00b0bd3c58c9ec8e2c8ecdde590d1cec1454a1dbd90a2 5852fb498465728bec1f9a7fcc6e59bd217349ef0cb817b481fefe192154f388 e67549ad5e86cfc3d22bdda49059d75dba7989ab0f6ae418ee2a770c7e8bcf78 af91cf60ca045ab092383da07a1d7e432b8694141cee4a72127d6d7f592c02c7 89cae3da0866620f98ec6ab77c3d78d6c7708a1235de6dec6a0e178acacda30b 625effd1ed3041bddba20cdd0324bbc812f184ccd30aac76cb105b351849ff15 5cabd9d8aea48efbc18e85c1b8d6bdc340c221ccb0d5a7f1f5d76e60d015790b31ffa58b9343bfc9168ff0e47a7dc5e996620f6ea1efe825165bb420a3a5ef03f83e1b234849c2497115773d1cdc2980c09b55cc4a6623f58f6c4bb1fba13104a15b9de4ac07c0d3160612d16273689e527c19b527a146244b4f484c9ed0670a620a39b8dd146e114a71980d51f94e07fc7c8cb7acaa0160e59614cf91bcd10371f7033a8aa5b10fa0f7d9eba61b15797b1bb74fc173eaedcb2669221921d600ee61926e827e83e5ca468e12368dfb245feab343c32831773047e5d468d11a7ddcdd672460559dee13cce6c5b7c2718eb0f401e8e56437006025751e30c13005abb67d944cee09243f721deff38aa8c36ddf23c0bb604053d56b0d30cbfce966cbce50821e3f18905a3624fe98b085a1c9bccd41d27aba4d17ccbe94e85e2006abd3475c4a305ab54372140f9ed293d18aaf8c0b360885e4b678997f9a95df0892aadff3918fcc2354eea11eb9a029ddb5d3eb9f2349ed6d76b18db8cb8e6600f50951ff6ee5a6b2448310b9839abc7d0849b9972788dea11fc6dfb07fd03901063d9e73120d0a4a30798375925e022df1f4a3274b9e0ae2e208f1333760d702cdc693dd60fc58339d50740865a240f47c707aef88cc96f0077dc67d3ce17f09417141c0f23892a9ebdbc85b7705519ab1caf3c24fab32e4416258acae68aa05543e33877a128ca051df853b89684e8647dff8975e031ecb8dfd67bd0e8afe0df0ec33734113c6ccb93ebe4a9dcdefd9f5846fdbcabf300d677e05720ab66e0881914e8c7f3dc759c79163b9edaf5bcc10e410e9d765b439a13d476c2c1ed30da11674165f7f430f099fdadfdd14a1dbcfac71f4c8ebfc17b17301d3aac0c9c946b347a6054c90deee0eeadb3b0c2ad73223f42939079361e8a04738583a1a0a1caeb3a16881a97f9b8fa6bdb0b7a05466c5ef46c5571694fff283bc013ad30533d646b9a6461f9b10ba8dcbdd876cd0b19c60d720ccc98d968b87cc5be23906e9b0732914182c72a5642da580e44510f3e8701926df1d2880fd33eb1cbb4200ea28eac4e1d12bde001f427847b66584b4e26f36293c975852041a3f72148f09e984b444a864534e7a3b1e64aed23812ab9e99fe713ca0bdc991f1239ca43c009a1d374a09a85be1c8d450d543baa386cae1ae8dc1cc27066f91b60257a9fd0658dd5946e4bdf0378c1d635c04e0df6201427f37a6826f9a537914d649d76f05 false -check_ring_signature a7f6d4327d5af8ff384ddcba8ef5377d560da56b4867332e162fad603ab3139c f10e8cd87bcb36b11b0375cece2d98cba99954937344b15a7aa7fac8368f6343 1 3ad68a7cb8b9d54c3f0ea39ae3885b8c1d37d796e3752e1162dba75ee620d2bd d8f156a6426d8d8c76f2f77213985e9a5ffdaefcf05943476a87e4f700365d7491fe403e2d037e0c8f3d8577cf61632856c4b701265e2b487c93ed2a1d7549ec false -check_ring_signature e6edbc35fa418613e388d15abc060f329d4a7a3e335d209c9888183768de080c ea26e5a5a3fce9949ef8a74cd359393fb2e629e2bc2bfb68dc39039fb9659065 2 31419a7488087ed6f0b41ef1e83c8ba10179ec6a97a364073e3e918d90d5fe0f bdf3510b1d325f8933de97b826936d48cfb4956f379a27a5429e8eeec47b9380 cf692ed7d62c36201a1fa35895c2d3c0d9424ce48d0ae08cd76f00431b418b02cfc72b29fa52eb7b334a96526768336c994757b16c0393df1217a856b6cd8a06c7491e9033aae9bd8955e56fe555d4d8e2075995f6b1ac344aff54ad77b5ff0aedbd6cdf5b36ba280d5564a3225bdef4b1678bf5a8324c39bf6288357cd7640e true -check_ring_signature 0f495a463d6f469c474fdefe2b95115e27c3b790a5ce028c0a39c66e2a94aff6 cdcad1e59a20d06b8cf15ed945cbebadc4787e828306b8f277524fa6a9658285 89 81a666870382ed98c507379c8ff944245945b9912d329be825271de30a942122 d9da03b797df6bbe60f1c445bdad970de9ec8c182859003221fc3026dc4626b3 6ea748dc60f72917bc4078b1e0c8e2f59daeb59d04629f7e8b8bb8109d27142a d53a4e842b0c6a87e311ebdf236a0cc3a412375588f653fede3190e8d447ff07 3273cfe37ee595b53393ba540d5bba545f282b5fbf9a5b17cc38d11114762bf0 a7e8ec868914949e1655b55596ec38ca747b5782d2597d8d25fb779fbc2e9240 d70c4350f0519efd276d86e3171f785ad233cc0cbf942fd40a55b9e228ee23b9 d2ee69f1eaad865ffbababa3631e46f95182419cd3721d65cdd5535ab2aeb7ee ea633c86559d4ea95441a23c61e5f4ff9e600b974d64d2680349cc338b11f7f5 3521abc8ca7a46ae03c50e6abc2cf5e315f919f0927803687594a14bdffc7472 d911d298168e47c228b116b32f23e1c31580303c058b68d1ae40536864e35672 4b0af04b7ef4c79811f90545c47a1f2b56ffc29a8ba07cffd5a8def85c0d69bb 8b8c184d088295d654bd630fe7d70a8216ba75aea8499b5e8224b0439022a53f 1b26768e3688dbaf7d0c1935e6728674fec9d8f8f3d513a8ffe74a0f8c82913f 8ab9618ae0d5fbfd8f182ccbd987875a1eb2087737cd62099002c5075f8616ce c42825fadf2f4f68a6b30d710afcf1bbccb97ee0080975f97296d30c455e40b6 c90459240eed2212d0b95d48a2388ef7edf18226540cef2765dcb57179ec97c5 30d93c25b2be9def7501873b4c7b0a82897f2fa30cea2bc8a83b0984565719c2 487609b871f03c3d3371424f46a244df445aaa5f5c4611eb4b3c58cfe13a1329 5e5406152f1fc9c51217d9ce22c5eaf6d79136bda253761f94a8854a97fa0510 5c05c2de8a04190d26b8d6af5004e9d742140dfce4b1669575f735563c7b3f25 bdbd5c462b5bcd917bfb32437a3aa9a3e2db3023cf6a0443146406b946f30e8f 725fc06104d8b186c7cbf0e8ef3d6deacabe4f904d7d01270b410b46572baad5 a513772bdfde75020b2d9df4beb32e35d7b3c9b16537cdab41c972c4e08adb6d beccdf4666425541ffabafe4f70104d91c14f48458c5c5659881a609e182ad4d d7b1304a39761c0677a079c4c9a8ffa0b06e8723af8ae12f7b13542eeb1c22b5 894a59bd7216688037a5abc15a37982464700e28e2ee1e461872db8d0709b276 86045367e41915299cebcde611a139f03fe06f472130fbc7cc6287f5b9846031 4cf2696f7d4a34146bc900fdb67773fbbd5499d83c80b106d2956d37f581a964 621a0a7ee8001cbd4936f011391538c5e690e32b7f15405d6a587a3f42efea99 e33c094544cb547e955dbcdd6e58dfec84add9f0c738651e908d0c4a86ddcb3e 456a2515e6da2db74bfb3e98a5c2fbebc5cd50f54fee0a9e64c2844cdee0bd83 e592a5d4236becd9abcae749115cad8358ef4e2e6b8a38937e8788a2cbcb38f5 57472fc6d0a11676670aa21b7365067184edc327d184c5e157f335c0b8e5b364 a628824941d8b2d9f8167133392108158d8ace410808921a6883ad437f59c863 70b4ed00cb6b951ae7f194df2f890eb45df7e4846f4a7d91b7a9f217f168f618 f54c5bf590eda90cb9da17f93893201f91f0c1bc9a7cc35a38ee5f6673e592d9 985079b0be1f385aacfc7d1555f428a46220baafeff45898ff03184cf82a14a9 e164cc9fbbb12c582d08f7938e2cce9125d0399aac64719b24b1e247d6bfd366 9c45f45d44b4e4aa1304fc0981be596a7d3361e96f2bc1be8bbcfc9c29e15608 e2640750064abc9ccd26bc99ee97f31a503d92c4b5ef6f179c711824cb57fe6e e8b4570f297832a270a039db79e47ecac8f41d94838e6e0d4192a36fb905574c c6d218ab5f5dd3a6189f0c56522addf9ecd068a001894632a2a09be5db5f5c2f 90b23fd6ee008af76f19e628a57bc83ef81991704395c6e87f392f9e8f658e2b 7f9236bc4f1c8cd7b08be9c44dbb8191eef5c3d39f032fef67d709dd6f97755b 33c925f4815eb03ef59f864bdb490cd05d4adbff15b1e549a44b1da2c1b5247e fdec36611cd56decb9f57c61d5a3c10e3f1d139fbc1677a1a165f75318a6205e 60bc16f6cce66ebbf7e2d3ef149729b5da82e8e884855ececf17f93927f648f9 2ca898a7c0f3150a29da7afb9a61644ec8f0afb37aa60f5fe3ad8593d7a5fd39 da6b4e9ea0f21ffc0863f41cabffa5a19b9da44c79c6fe80b25a6dd5c1c2e759 6ce288cc395d596af646f4f3587e87785b67d2b183d14fc811b439250d962c5e 637a4a86a78d532ec54e5b9ba743502dcde0fb6305d7b0eed102489823e827b6 cb0ed34c8e5be020c9686b452845ade599ecf9f3dca5d05c9edc60af9ff3c4b3 4c28b857c3c154cf0fd3001ab5b6a13a5758eb86cc185155e4f5dfdf70f3ac54 5a5dc8b5665b99aca8b14b8ba9885cd02c8b713b646c070aa0fd31deb1075e6f ac8ba4a4b86245d96fdc56e63a218e3b9e2db98f1a232bb0c3fe3c0f1d3da0ca dc4c26bf2bd0f1a53cb49f4b7c33691266de18360097644fa67f63bd6332cec3 77ccb0d939372469f243db664d57aa10f9757fe37456fd43228d3557f4bb6152 0e7a7718ce73a26f9f1c700d944f2dc9427208586ab2e0416ae7b610ea8e002f 63ddee085512706c955d887b5497ba7cd92481a4be313c150a7d4a25d75af4fd 7d604db0f76d56ca6dc0ab6d6e60003d5fca0c45b9d6c76ad5cc7842ee65e421 e1db8c262c535de6b9ee9f60b2f7f4160bcc1240f1d3293d3aaf35ffc57c6ec8 52f06e108f90a7bfeb155986a23009dd0fbeaa63b62bdf30658d5d55f6b416bb c8d054f9f82ddc4e22f19d03bcdf64543fcb7b96f39962ce7eb54062d868b63e 6a1ee1d0def6a21c748bca89911e8423114e36c799b5d2522c0affe120edf50e 779b9a2244e78ecf8f045c9552c4e304d47c666b3f8513461d3ffc44367ef697 4b7e20cf571938f74a406cfd32b82df9115ead40770806e2ace5d3d19d8a4563 45de1e277e895f6945395b5fbfbb9b2a84d95425345f09576e0f0d6e3dac0995 94e26c6c5ff2945f5713282327f35a8b9d14cb033e3a1ba6ab8f56c26e840541 a8bbef556fc1571ec0c12d1b4aec618227c662a2b3f84cd0c22276fd1e4cc477 9c250a206b8648ab2021e366ee73672f5df9e1e95192a7990d2c5ed157173996 5b60d3be981a0775035a45a8d6019a5b5d01a32dd4745a78e89d753e59510c3a 86c23beffabc990eef524c1b6af4769e1fb4423939acdeb8964b8dea63418726 dc9e68e53fc32fe90d1e0f3f5f5f2bdd5d45b206dc686ae93fff91d3b8e414e6 bb9b0f31834d15cf54345b31ebf6d3d35a695ae7cede1d4a75aca85854773165 c340e97c02e25727ec8b4374639578d3865fd7204d4f5e0f7d76039d249ee04f 0ab4f00d2b3b95d8259e2f30639974617a438639df3c66b2387fa00ec4955fc0 f4b03292da8982f6e6b27b405c6f72dd90142f9e9822845cf0b78451e37caccd 680357aed47751b3093ea248bc0ab7d1b3f3deefcbf8e1b1b5ce0b9881438141 7f9644876fde5a7c23202cc376192838d8871ea76cbcb806b53bca550a356cef 7df003df63c175e4cf243f1942c9d5c824e1e439875576d829277da95091af75 31a90aa8f513cbd8b499f47a5ee3951ea36e9afcef15e4a81a37a7e3e8d6916b da8b920daad8a0053725ba88cb8667af526151bef9f53d8f912ad111054c0420 6557db33184694d434da2a0d3260935d62301cf173e1198a6d738159978f6975 d4cedb61ab16cc73f7a39b9fe8696519d319f2512cbc7c5cb7f924a971e4fac9 5107275f2a744cf4e41ca49461a4d68f4d2035ffe76422afc1bec616f3a1e8f4 53d4b7cffde82e6ed474060ed062e7448dd506cb2146197444ffa4f7934396af 25fc5bee93bd111303e55fc8bf299a07b047598ddb9950296aff74653335e439 9d9221e92569a8545fb1d066ae7fb7ee9c4793e9bcff24e297097e7363698012 0a9cd9e797b6bef0701d6479a41b258b3100a4f7113a24932d867bc630ee560fa67ed3279f41223f12b6866063828832cc4550e6a030206877104010fabebb0181c58d2c811c5d58475f79cf50931cc7cf9d1cb18cd8a401b2af13b089c6410a1cc7d9509ea777b02ebc35571cc16b1847bee2a99d7d43a8b06620beacd5dc03845cb2ad6de9eb39f6bff5c86515c3ac46f0a25187062736c64a892f94719a02f0a43edb54e0aeee6b4687029d3eefb799e22b67809ed538cd2950d3cbfa450ba40efcac4f69515b56cd1605a2acde037b8e575c8a2b4ea926a384aa4e1a7b030ddf394a4e09f65016f013e12c08bbc9f273fa999f86dc2864e7519270fa870e715ddc290b43ae15b38136561218449058a1012d571c4cabeadf215fa204640532475078b4ed583f69f9dd9c9c6199627cd7ec576bd95e98d106f8aa8b8bc8081afd62fcd0624bceb2e06d4c82f9b704aaf27cb31a13782010b991477302ac0a8cfc64dd98f67e3fc6027399dcddc4f5fee60e13eae05aa0f577fda042d29a049f846cbd9af79eb82249803aae47063ab0056f98a3d589875cbb2cc98e4ab302ac0f7fb816ad7159cd3ca3b61c8c60dfbefffcba6e628732b6a6ea919e34cb07a580e5dfa65dc68c24028b51c7a9204cecfdfcb7f74b2c2dc1790ac072948808966865d38bf79996f217bd1955f0b4b3ae385bc942630266be30b669868c34000ec0d6ddd43dd767e08f96550e9348d576ab4ca8387e263c8eea892fb2640105484681d45e9cf55932b2371e25c46ae75c5f1f84e8a16eea4239a12cb79c390c5f3472b0336425fcd547af2a1758f348e457f009c77b7acd3619bbe5d3ed88034e0bbe150fb8eaab55867829d7481735eb1ee2910ade2fe4fd8c148b9e270e026013d26f742889c5c76b1b8547c503d66753fd2db7d8a5da3a0182cd076ed70415335440b70258219acbf16283bfed2faf76d8f93abfc33db2f8dbca9116ec0dfa4fda327ad875f70d6dcd06d7d4343f112018557031789767030c444c26df065e7d15c7d94850abc05ed4e8447e7251c2cd291bc657ff18995e7e14dbf5b70df9b53dc5c2a3c6d56549090c80f8901f5b55771aa0a95946bbd4aa79979c2207dda37d43f7be241102f84a101560aecc3d87149b49c8cbfbf4340742f6478503697d80bda0d8cec95d01df9ee43e4203955a4d0e26adb2c0ee1007be9f3c0a09f45606e5e4fd7b81401de20e92e22898c670a17b6a52884b6a3116a6a4271c0d5b1a8fffb6fbe7a84bb7f30c408117edde0c2ce9b73f7aa72e263ec940db120afa103c23dda4193e48068684e1dc3ba246d0b8aed2e07dee4a831ecd06464800032c5dbc553b7b094e48338a6af82239d173a6738438793aca1d04010b7b7a0e9b439621eac48c1f490529af1cefd55441261a0038738c7ceda83e9baee2d2018f77fe1a480eb1dc96624b1f2fddc184133d4550a927ea242c5347f1f0e01b02b386a950395d0194422293615542c7508bd9bf95e19953e675113f5a006cda0dcab033a4dcc13fa89b1582c70e0e1172499f276ddd438d009dcebf7c14bd150fa6778842e448dc9576e25d48bc37808621dbf713def8badd06d66fc699b86b041de200b8d5ce758193ed053108773a960ed9c733cb5b8c1da33f68a503c39b0b285f38a0464f3a8add0fba64167d4ead77580de131e4714d0c3f90be2a3e0d01372f223fcfb1edb8f091d6c23b2c4d5b9f8f71be35b8af28df03d0539d7fe709180f0926409e576ffe7471cdd02abb2c6814700fd11476fa131c7afb5bcac20b4edc941e2d7c2903155791bab9165aa7e62a0f9d8b724f69356a93e10968940441889f3a97e26e4d53f6153c53b79b10276eb4f6c866a94da515b867bf28130e4026bb2ebc33507aad8eff48106f4df4194dd5b8ecadb8244e62124ecfb5510b8fcb5073db06da5056dece9e3251dc9577697f6e8be34e2bfc2f0c1ddcdd620911cf821d7ad8d4e926d497aba817dea1effb33f2cec2ffdf270103e534ae900a2fe74e0a0b83650c5e5e9835076c384981dc5cdf87e713824c46380e94ac2c01f9711c111a045f0b2dd449fd15fe1dc002553eb9f6dc386635692b04273c190735e5e15fa9a9c7fda37f4896b27d6ee997088bff1dcfda081340b53fa09833080603509da691fe2579fac0927b8e5cd9c53b27a65f4d9878bfb3de4565fcd306a10d61667505a11fc1bf61f0884bdc4f98c314c4593c30f1aca9f9a20828220a2cefc81ed45d86994526de505f1efc9b63cecae24cbf2d9b9135eab63cedaa0e657d6caf7795e28c666983ee79a9fb134aa716a46082f00ccaf287edf5a6650c5f2c7193611740afe9b94593c031932f1a8f305da6684e883e788e0de19dc900897b45f8850c23105cec56ca51350d0cca6d25c7605ad62f623c97a88abffb0cbb8d485098dbd37c0617dc09f71cd6e31f89c50d56d9e8ba206316da4ce441074b44a9df52ee49bb89a69a66f5cc0c070d5e3824ac373105c3ee54f08246f001efe6f0fcbba2934a964e4ad55974df0293ec1059bb8ed40ae23315c63c43f70228e653150ada694ea97f5f6eef10cf6b200d9646990ad4dc5b63d0c76007a60dfb440509cb95f9ec265595c04fd4a60eac1d65c3078471647b31d15323421305230d5cff9ee2f7daed958270483701fbb10c41f3445a36e998f8ea0803239b05359888ddec7f43b1b4cf1ab77d364b6450395b801b5e86dddf691e7780db7f0335da8632a22e0564cf35173b7aaaf3ba92d9d50d62ff7926378f7bf6fdd5680c45a4fdffab3c1e7aceb13dfcef419fcdb4b686b14fce1d096e6cb298862e2f0fe9a9bfb612fcfff1383f6901438c03eb557511834d87e8841f6e78cb099c900152cf39c6631e65b051ec909997dbb0777ba488ab0de0b88bd30ceba18e85e30c7ed1603f028af90930ec2022fb5bcbb5a234129df8c2c4098f5525a89a9b7601eee608b77f2f3c22408d710e9600c40daf8082c57595c3ac19521fe2edf0ac04e186086b088127736a1f3f7abea067956afbbdd662b0de280a3eeea41243680837f8452c3db6d520b4f80e6881f44f0f5398900ca8593336bb8eaf900b23e60e9ae21680ba4743c4de6f88b785c428a5be946cbd71ce7cecbda203e16ef583040df2a00fa7b9d24540e7844235257b9889897d965caffeb7e8384646949e51032b2f282bd0448a2fe4cb67472d003e8e288bbd1d38e0105b69e4226737f39a05f8e5dc284103b2b613a79b735120aae45568e579e421c2a67776e25339f2ad060e52bf83bdb2fbefc366a5c9198915016edda04d423f9cf4be104442d84f620dc52f496cb7052e0c8815237a4bac76bbd9328afccde8d69974e10c0777b2870d4697445c211cfe7720466ee1e3ba3691165b2f3c5edb6eb0fc870131659ed904df9964de97a8474ebfdc2fee5e25423ad386ac362141250f2cde931fb5e0e40f8c953a2b24e573fb9047f139231c1c6c05812c4fd1b9c9f1bda7737f84afcc04cd5c581ba53e88d1eed08f3939167e81cfc348adc838de20409e7f2acb1acd0b07457439a91e301bb4207b416496786f8b73d12a7a12b45c08fe7554d901070e523121b5ca898f91953debb14799acd4a806a9f49d6bd0ed94eb468c96c7f30dfe69278f97f2e0fa81c202bc18544c46fce4a82c4cf2cb101b6023883e9fbe0e7a00be6f57f63a9efde6cdc9f53143b9f3bd37ab36141f2df187680cad75980070be19a7283c7fbc36516d734fec17345293cc71afda90ca4bc9153a8f97aa09b7fb976f65aac6fcd02bca8dd1994d69fef1129af1cff4ef30e826ca34de6b071cb1a5fd6940c02a539d5c1675949f281fc0526491bd78a7b4e371a5bf13d50baddcbf11af917a439d3f6af98a9a8221c8fe5e24ce48099de9fd6d8f60d60a05c11db69f7a289743e9b65b133301901580fe046d2459286476de857f7a2c1906b898db8fd40e8505f31d98a82ed4d08cfb43925a3bf58865f595de9d39037c0eb538ad8467eef085519f86f41919c0ccf882ef0a5794f773c9775db636c76a050e2f4d43ba6619e35656aff11d7ac107339e2a5f96062deff2c4513768bc5003bd7b2870374c8e8fa99b41dd4de9a77149c32398bdeb7fbdc78e377767884c017628c9696599b46e4d8642b4a2f6db3a9c4dce4bfd790c286f0ce28fc68c020a2d4fd5781760f6d2b511a1b92d60a25ddba81261e2d2095259845b7cabee8904bd0943906871d53891708793329ad97a176f5db65f95f647a5ec7a1685103e0d0627a0fae6060e71cd4b86c8f6632ee7f47712c746621802231c54eec1fe1b0b40730575bcbfb3435c3d6184186f2e277b3deb7db14b6fa24391d048ce3bd907687e0d9478f01fb48560f4396a57893fd12104b4f1e66656ef5a68516911940ea13f4da4b156ad295ddd6326392561b5ca90e707bac64f1aa89cd7cd0c0f110cce8d3cc1f9f20cd0f4543c4321a49a50cbd9424cbee243ab709388e47a10400c9aea2359e64d0d11312342257c00bd562ad8c26af7179f3a90ba9940f60a3e0390787d962f6bb9090dbe6f0a532553b2ed676c1947fa71acd3177c0c716f1408e314287e7bfd0de2afd3709f795656708d1109c53ae202ceb4df0083de1c5203c7401598ed96970b1100f460b1ddecbfb58aa150a38b55f4a973bb065c633705d1e25a322ccc5917c95cde4eda5d22166e3244d8a200ff19d5fd51535d12110548ec31f590208a5795e68839b9f2cc06e5bf19ce2d67b2695b2a77c681204805b8d7a0b2c6b70ba8d38f7721ab9f153a7601c3bdbb3b0b0df869bd4ac9facc043a13cf502751e258cde88b95d5d95a2025f911969253f8327f8ad709c795bb0fe7b195a7ea4ce496aeb22f4f2fa4a44934ae493c203e3b56da77dbc2fda5dd0610b77fbe86a61555de03be83d12acae7adf9c194f6920207016e972872baf3055e34bcec0c9e0be2e937f4058bf8e2397ae8eaa761c3b1d9dbef85efe012ce0b0606ad6126839cb88dc4b566084448fcb88ebfd31f81bb576c882f229f31260e0cf8cfb7817adb2261082e1f56f8dea3d00e8251815949a042e4c542fe65c401299ab65878f1488f8930fa1dbb84976ef1544139b28a58989526a9e51143f4031a90b6b5fbb619f21e49fa457da67532ee9f22aa402cfb8717a63011a8e5f603efa5e2a482a54779d44e181ee43e82f613e065c4d5b2428f63b1f6e674e6c50b1bc584c54531cbc1caced9baa31d0e7e750a1fb3b2a79c5e8d0356da82605e0b05f06a72cf38a7bdcb55b5a85d4a75435267b2f3d2e6f1e4f934928360b5470ddb7a979c009d929dbb9c59ae160a95d52db0369bc93a9ca786a1a56d475c3a0a030d026b37d948e128f543ead9323cc2a54a0c057c70300c1b25ef6e9f665005cb3be7133a43736fee9cebd4c1e3c748ebe7740cd78a2a3d33d4aebd0c226b030e5cbd3f096591fdde0ef2d6627efa9acd23e71ff5756d456182039c0716e10619bc0aadde102334c82d1754364fb2c937dddc4e3b34c9206d29c6096873e90d88ee5dbae939f37b541d7a68b16f44e326d30cd966ef42ddd60c3294423de3006acd71fcce0599c973ef868ec73e602f49bed6973431b5e4e4b0689f1ee40b0bfd41aa0a1546c965fccbd303570ea8ae6d220af9790a974048a38d235c6702067591dd371fc28eb7cec0ae5e5679175a94ea26f566d5b29e889868b2696bb009eb9408ce84d1a78129e4025eb3efe69727572bd5dd70d0e379f011dfa23a320c897c8b475fbc7dc3c0bcd95bbe3e5b7cc33ac9c8c19f60da4f0ea973d68e6f036f36f53851a81250b967d6566b20975a4b9ac923ffd9194579aa87e299afa90f37a440bf0b46dae8a850098a8289834a12fad69f58c780bfb970f1c10882450332bee5fd976837c17d610ae1729f04c2dea927d1740f95e140e2202dfc92c006ebf9010f7a59e0e905d5c9a5ecb2d9fb42245324b9c927fad269543bb0aee00087b75f671adbfc0993f8cf559a38df8cbd3133bdb9b95e0e9eeaf6c9f9cac4096939bb942fa7b5c2866605faa476d60340d88925ca49f3edfe733177de747a0911be0b3aac8151190fde105cfe1985b256ac1b353bf972ef08056cce45361b0f52185592c64cba9e508314619f6321f81eb14a51f3d47baaa7bb96d7746e0706acf0be1bf6c4d2029a8a11b9be3e7f361555c792be61b5b1ff7e7a5c1b02910d49bbad188bf5702c53340aec999c394c739e7471da9f8176373e22bdef172705c085786269ef1954bd5cfeeea3eeae819163af91c0f2197dddd4220278d15209620c482c915e55ab2a71ab6254c83285c27bcd0e9aaa66861f90c8d017154b09698a83ef035361c7fdb241cc1d1b45f1d35754252d880af658cf718add927c047e43775cc20bb4415f14c363c5a3c2ec30fc7c06c73750a8c7be5996105fa90be6a6e669566fc2af069eea992031f7efcc06cfb7c48b07a7927fc375e0a74c08ea4df1ec51933a1a8ba608484b4d90d636bb4b66fa94d6db708bf75e7b7c240c0f0ead91f9882f5addf00be1f6b30d8a3a296b1c45dffb60231df2e3911a3f08e0761ad147e9b95def2d05ec75e5297df4250a69c09929c4165ae394ec22ad0aa05f9501b528e827ec5f44773c666ea3e20942a93d7275eaf8fd44437a8f5404f72e7a202724d7bc08fd4532c82bb1a535e14242d7bf586b71c3d61cca242703cd5086777879e3ac57c47cc01bca93bff8956c6a167c20ddc6821417fd6bc30d0faaf873a492c79fc49cc8da1202e3277e7240337239d2a4c593f6f3c740aa00ac383de4d89080cbdb664f181cf76d6edeac976f5a84893f8a5049b31d42160875dc6b7b006587a3b7bad4433d52561d53c9ceb8fc4e368d1da8356f9d89590974fdc259b74b3b2adbb8cf2a1dfaae3be05c24ecc34d638f747cb2d4b8ef34002912efe24e811aed3bd1b98672c50a88ff5fe9c1a0900d9fee860d0695030a0a266938c6212c7e87256766b46e18319fe7c736adca93f1613f79026992db46070f59ef45dc6da05a16ce018c37a11bb831fe5eb1a38e1a40d6fe51751f7cdb02eb713041a0a5d1f39ab53dcb23dec249e6d7fc88e78a0b53472c6fcb2f25170093cf9e3d27c9506e4dcea872a75af82a8f4046edbb1dde4bcf99bbf41d63b50be25fa0e3f0ce5db994cd02b08f708fe3dc347a34207a4b7dbf5671014a5716004447ae91d858ab7b35583ce3ab356ffc37cd5bf1001f7a9aad68b7dad4f2b804c47f4fb2277b03ef6946ff3ecc0ef99ed5e2bc52d7e09dea7371ad265394190a589d53f06f01a81f7b6146e0d6edb94414770ccd21e1ae9e4a014b149c055809b39c9db9c7aecd81333339fb71d6cdd6450d373a9d77ad843f9261f31459e509814d6045a9bbce2f7e34d5b12f9e147d5e7c713f3b8d92db7b0643fe7525ff066b555886c5bd6afce3650a8070f2fd826efa13c29e2666d090786438f389760bd2bb0ff5e61c3ee2f9c917cbdc73732405863334207d86fb30accf4e550a560a313c102b3db7c9def91db7b3db61ee6df913df069f0f24e471fa58f83d02840a0107a390cdf62644f1ed5c4e3f0d02adc62bce7be2f8087d2af76c216369420bd7df8d31c0851bd87187624aea3348f204bbdb0d8f521ac43467bcb2e5d9850834456d4b65de2ba16eb870065d326aa38416e525cfc9e0dad4f04547d2d3660ce67d52b95ec4fd7c47f4099bd288bd751beb0de953aa1ae3d1c73a1e9ec95002aee7f85f65e936937dac5a45591164dffbbd6486e91458e65e046da10c9a3205248e143d002f15981871da16c6694aa2cc877499461f662b303576e14663550f4c693d3c5a2704b0d65d5ec6e170a2488c905c2435d40d192c14397ea6e60e00acac82b0b34a7f707c3890a948f4bb4f4ad8d72e67a062995248069383287c028d873da80f68a352d2d102b0a801ec0803841fbafb6f9c442a6280e46ac2ac87c8e6e157ccb64c6ede960094fcf9c58a256cea78a1b94b1d1405f484ca928d00 false -check_ring_signature 2f14480e7bdb48032ebeac60712fc2c26e9d3e3bd77f2d476d8c9407b6ebfb7b 553249a110fa4984df208e0fa2e0649e69e5a648c664dfa673b1095bcf980f29 3 eee26076011bfa3acabdd91b58e506a9abb40afe1123777d2cf12f958ccc99a9 74539bc072a556352fc62dfef0d2e3ea296b9821c664e825d8759d62ac410875 4c870c713498afbd362bf421f0351d8eae34f5b5bd4cdeaa2a3b37174145abe5 4aeb8c86e9a7c21f59a2a57dc6fa2ba9f43c0060834d365dd0d22d701d05fb0ffff76f02bc6027d716a48df432556c2e6c8536a9dea9e0c66766768f4de6b10b1574369f4253bdfdb66e641b50b8c8a33ad4ff011413447ee472847f91f3c90bab25272e62d566d962eddb4e9b8b084ae056528a7d043079a21fdf4b751ee000a3b492ee403f0a2492b801f6e7df72a61861a4dbf1814140231eb501d9a4fd16d7477deca74496a0a969d586ebe001194893960248bcdd315ab1059ed12fe672 false -check_ring_signature 93d33dd2482fdabab6d5315d5953e07d0c01841d1812f13243b8ed918635a0e3 8216808cc3a244d08a01376e94ce7add49f3ef600a35278aca7e02b83be4b0de 14 49acb4e2337a6f1e300ea6519be7e7c179b1bfd4938842cd98e173405c914abc 8548dab451706ae746cca54728416cc003e68648df988beaac486f98d400f802 5202bd557c4b4a17bca46fbf579dc3f2c8297b77df580e0f20209ec6bd512537 08c2822621ece09d9a6907dfc8d45afacc3c22d756514812600cce3acb7b1dc6 74d846217bbe36a299d6f4de035eaa519275b06b1b7ad2d733664888e337545f 3fc2212e352eda7bf681d3ebd24b75f3910c011d5b4ff90a136abfc67646ed5a 7e7ee2f8f5e61e71316c294bd070b26378c192fa3664475c5d6914ed03a00046 0ca2b2752201ef5b896a0c3c70b2043653f520417758089bdda2291df1f9d8f0 01f85e74d6b28df58f5072cd702adf5b075f3e2d3cdcd4452550331f0dce1749 be6e10e477ec150a46851cece50637397a5b303d1de94c369843ee52759f28dc f1bd7c36545b7f4ec4ed2f7b75244ebd67f48160b8d74d0fc56302900999c76c d4ab0bba1a7af1439886fe53a0185640947de3e579ea569dd2aa316d5dd859fc b1c4a1aa9b7241d01c38f167949cfccf9514a6ec33162c083cdf1fa8bcccb3ae 8f8fd81a2b2f1937bffc7a8f294f11d34878ecc1fcf69d5fbefcf4be6aaa8ad9 e7461d84c1e8483152f65e88d636134199f2efcb16549c1204333d333ecdfa0288d94ae95acd4429d50f3d8a10f9a711667527363b8c5644614d8ae3e7928104fe6f48f40bbd417aeed42883f3ad6cb847fb4e67878b92308678e2eda99d8a0407bbb871e0b64d2700db394b6966b7b4586e80e3e83bac50d647dc16746eab0ef5d4bdf433e56bf11fc459721119bcdd73ca36b3a8b8be25e7df49942de2e507816e44569f190fc30ad3023840644c04ef766736aab6a4312af136d5231bfb077c5a8c6e6d1246457e6b24758ef9aeb60d499288efe5be075fb3f99f3083180e881e236d3c4a78b6d3364b687606410242c01add3d62457834f7da971b2dcf044b5b041a562805d68cd2cbfa91b95ea49e4f479f736e5b94db2d4aabcd77210b58d25b29ea8143d225d6415dbf824036d147806a4128e11e219207b093f8ef07e87c2761d57b5d3236b6588db475859f8b9b37f70efe6e33382b70fc5eb83b019dba0faa8aa5d8878b28ed7567897ac3f3edca0159b75537aad1f8e02bea6f02db6bc047999684dd51a67dafd3a61f6d2bf84458cdff295a45b22e79ce737e0bb1615d277c30b549b4810918fc8cd94716ebd8238944bbb3d348d2168def2b04f282c6d7bc0134b9fe1d3c71ad9346077aa536b7c7b71709f681dbbc144c5a04f2ef20f60a590f3884eaf7f9e6d7f599932fa81312aabfa8602c8d1e86de0d0139eeaf5e8ac8e58c0950534d5cf7be0ada68c601e829d7de4622e69981624d053e021ea900bbedce6c72d80b7a09cf00d5e804268c0b072e10ec4c70d8194c04f2cc244596656b0801a474588aa046c857820dd7263934527eb85e8a68c8f60ff10824ce6196a7292b18049d84817d0c4396afddff376489d5c14a76351f840ea1ee31a121e078cd2f15c1c897d3b2683e341af98d14b2bf9cf6e004a26111098d5b32307654095690155e878120bfddc976dd5e0164915c23ffe18affe16c058fd1bf4b6b91468a3f27a030bf10c5f6285df609cac1f3f76eaedda553f40a0891997e238762a0c2d990937951e9b3dd9619d9149f684ec7e7c06ba9df163c04018c00d67d98e4e5d1ec234d703e14ffdbd44ba563edcd226f67bb4c538a66084805109ad4c01562ae25a07c3f81beb8a535fbb1467a0b249fbc8ff2a8d19307957dfb6f21001d33931ea1835900c66ee9d365448a0dc2c711d78f90563e7702bdb4268829b95465fe9b4b0ca069015852960679bf6334e82c30c8cbef548e04 true -check_ring_signature 6817240e839f4d39bde7cc9d63d4d28d19654211a85728f04a19419815531d42 8bcb59696808d8670c83c52edbd17c049b0c3f11ced2971ac0ae372205c89f80 1 f77b2ecd9e926544775945e8c50ce5f29f516bc9659462108913e05bd698e9bd c4c978d7b60b3cc5fd8c7ae3eb12e5c10ab5a7f71af13f2a52abcfe192b63e04b49b31a3d34298f7fd058f1aee593ab3a95ab8727073a6986f4eb4d5fb97539a false -check_ring_signature c94d29324fc26f1be8dbb63e3019fad9a5afe7bed7f70e237144e5c53229a541 622fbec65401f7d36e27787b8019c3023e2e976466edb3a6a2da823d5c5b65f0 44 30aa8ab589e6a3be510deee4b3da67eadb2e707451084081a1b272cd571b7eda 35b7e397fb0a00669d0d821fa55aaba2c9c2de9e839051d37cf0871e8a78c3ca f1484ea0f956cc41da36ac80d91060b102a9539343e888d6244a8a41a519e253 6784b11d1949b5acabeafe30575e00c8f19d24cea14887e5612ad7c46afaf0fa b4bf359bbc4156394b104496df8fcba1c342910798a7a180a785d29793b4aa4b 9631411a9936d10b6c6f3eb4a7714f6a249da2dd0e39b4da977336c5a008451d f0f4ac269d993695a57da0954523c92447abb9d961273151eb18d1a4c8d6f2ec 4e444b247c10f5337ac7dac3d8982f305c6dfd3d1f252e3cdfe6f55df0ac21b6 da4e24d575f938593c4bf26bb58a2471ebc93bb3f32e458a8d53c7c85f8e2d64 226d2dc52c1b70c947028decfa3e59fdc77b94fe22d5ebc6ab426946d19eb7ec abc60081c073bc273d26196f1ee67f1033c2f87b56f19c9d07920011fa16c0e3 3599e8ae92ceb6ddc04e85fe61b89c650325d93a5cc18165a8daa6ab3cf88556 e2587e69b19971a6536c9866ffa6ba2435c95ea2af165e4e8be6a96413455f88 903c9d0c8f1586bc8f882ad6a0dab91e4ab7f75a4344cdc495b5ce26fb9295ed 149a8a4cb9d04f2c70a783f03533adf360db41dda45cdf9e9c46eed83c1adbee 18d1c5aa10116faca1178db5ab1de0ef110b7b4d5295684e786776a1754fa931 be8f03d4edb24876bc64be5ca98f6aabcae1f5156ffa8b4b76f99583f93d829b 3407bf1eb0d1da0d24230441b433a729a326b5a85c0aed45cd4b8f6d4448c1ef 209afb0e76e0af8b37780b0e5f4ddc5c7edae6686056d25c13ef561150ed7e71 ccd43078b93c907c1e488314033cd7e45459d35528ec68b9de99157c81170954 1bf2e67d0bf84a95f565095edea88d546c567f76489dda0b2281dcbf62a8fcaf 78b04a4e794dbf8adae14c32e711534d06bc21168c89c7902fc607a438cdc16b 8313db373f4f639c82a86a8b09b5460d3d79d8fb1d55cb3c1b5f58f38a359eff c1eb1df2af9c1f03787d5fb01e0a326e8322b9fa6341d18c5c37b7400a352884 1461b3fa9bf92513732d3f26d6e9a7ac67807b180d238baee91eee2d92ed2395 7a02c0db9cacc62648d6278048c345421edcdb82cbd4b51fc2e2d93ff243c303 f260b5d5b826bd3de4c36d39d1072b592e19189e8fad9928741a09b6a8e11a73 8d89939b932bd154c347176b9d5598907e5f39ef453ae8329b8932fb2fbe5b69 27987bbd56dd7ac5ae5476756a8b7d248edc0963a608a11b0970ed99ea108337 3f917e056fc59f214a52648e43ad5ff9cbbea76f782192ccebe6f1f4697ed4cc c9075dca8f40fa6ff826838b09c2e805fa4b7b157b84c0b4744bb9e66e880d63 1cab4375f28294432a0065a4c484ecf887e07759a28975dba1eb016b70d314bb 4997441f84ec5c2d364db459a35555b7f4c1f3491d0fc83707a933aac467fecc 16e9bb5cd65c6719efefe8cdd7273c090779447790d3c2e910bbcc791d3651e7 4eb05e93c4a9443c3349cd4917f8050d99edddc7aadf906727dece46ed64feb0 f1c0946f9cd55ebfa76337ded34eaf28b1054f1c9747c15ec7a532e99782b8cc 6b1de5970671d1409355d49ee1d06c661c79528c0e06f6b89428c6afc63b4b28 c13ec399c391bf854e14adf2c4290bad159e07ceaef87ac08589f48648b3e5bc cb57ac08a5fc1d308d2bbe71e4cc1126290c87df0c4b916a356e14bca3301775 0c88300cde0eeb25560919654bff5772441bf64d5b66e7d31be3258524a13904 44fd7ed0a0215d02fc511de11ce9429fb23849564bb3a2228e3e71a5581bacb6 1679f3f149edfd15ff78484153f556a9ce229fd9f76381cb7e0ae7bc42aaf6b0 8032389093b984dda842a2f00cf89683ef1950784a3932222a512d14e00a139b 7f5e1ca35ef8594f3e24421e1a7fbe61e0448a848012ec6ae35192d290a3aa50 d6b9afc4fde5cfeda60999d681d2e45a65b17b37b5450179bcda82c6cbeea80071a63d18bc9359913228a857717b8c89cca0c9515242f08d3a7d6b6e57830306e24426c270d700d1c19796fb985cf5bece4c471d6c89ca5f6dc13444313b4b0e2cfe958d13826386433de8789aba862d803a6e132d3fd3073990195e4a03bf0f2c2049c38ccbe3d28f22a3798cf119bb8db8bd8fc8c52c0461af1123020a450581a5b1f427e1b4ff303ec0da717e0797191b73be832be958d3648c85cae39806342ca49815fc365334febfdf8bbe3a6f3e1a12d97e6d2525da9410e0c506ba077567b4fb32deffa8f0fc006bdd961f0f5e241d66c4bee804ccb9a8696669ef00195d9c709ef1a0f0df156b9a60d7be14041024314515727e68aca10ecdb1200c8cdd25ba4cddd6ccb3eb01b5df19ce84e69269e3e359edcac309d77ee11b350550013647fcb8a8e58341abece82f23ca69288acc72dad9ec7a3e02d940bb920a5df54c26eec7990adce3f6cba62c92b65b67a22f90afcdda160d5782d599e304c5cc78dcd37d22d868600e0d4065cbf208e05f395f282c44915332003b75d301798217defa1c4c02fefe8d30c8a47b8cf457b4f9543cf01df48ed16f59dc3c0ee9eed97fd276fe72b816408d7292662d456259b727921a75cd50afb136e9b30c5bdc5a601f0528d1efd99127bfe8c7addc391a1f2e35b83ec9af6da007f0c9045b83657d112983cf6239f040129ba7b6f9c9f57a6ad645898d0a1b4bdfdfb906ce331232b34b82bbcd266a5c06a3532debef9d05ea217ed5b2ad4d12182dbd0181bd09c0d2a1225269e16a06cc72a3e308fd55481df36aff5c32492ffd298006d403d46858c3f03840702556ad2c940456a962ce8a709cc9c871f58df1f69705e92d63229740bb2b7a39cb8d2b3352130c3c35ffbfc504af90be76e9d33461003aa2fce3ff7de096dd566e5f3943aea560a910c64f615344d94a09b4c45c120d9a2e6a42972dc0b38159eee6b9e89dfbb3b42f713ebef549d7ef6623ea53a2004f3a8351109614e9e479854bca489895649d6db8b59f07f0dc00d066d5ca4b043a0d8fb41d672a6cb7416371980ce26242758e3e3d8fb83e2a7bb3ac3c3ce30a01d25a59fc9350a969c32be3911a1e7795e352cac0897e517a26f7a228149702ee7c1baf1e72554a7cf9453e4b2ae31cbe0bdd798271355e411b4129ed53dd03b9de494eda3a08bb0ad576ca4b3def10c54ca04e396200352a31b6932048c00fc529519f0ccccaba6e5663c99e6b6460abe6381decaf6dfe65e74ef121c74c0370ea6f3ff6a602a423d3b7cbf6866b0d204e9fb4891ad431fc94f9a5a7e7860ac658d480f7d34942775a6cefb6524e78e24d3a3c4262296d277d703f63f38e09e45044796bcb0ce32ecc991a8fd596988f2d8006d8443b3420b1a6185cf5fc0cb38d9858f35acffc44c78c0078952010cc81392b7e0dda55e89126cdc940b003b95dca770051e9c907bc4b3cf356ca5d517fbbcc868b19208b6168f4612d340bf911d530f4912db54f198c261e943a750f11f159b0f8a659135af26a27ce0407beac46b5f4d6aac1da22e12c23f461ac2a3f590accf2ae4d5aaf165bc8ac0105d57da8753ff8220b206f699efd86dbe25a06a0b113d8456b555bfd1c40a463097dc0bfe6dd762e2a93ffe350ef7896995dee48dfddfccd8db17e4709a41e200515d358b17b35af4c123d0361dfc76aff3cb04440215a914c4470b276ccc1430be004a7c8fc756e0ffd7625d168d2aadcd9ac0bc4d76db66c0534a48469a7d40fa003ab4c36083b6b5bddc87cdb6fad6b627de46ca9c02714d8918d807f74000f065feb785f323f5a8584b5e4303ffcaaa811828b01f4f1734a1dfd321edc1e04c57de549d3bafe69cc3f0dbe50a10bc6ed775cbf1defe4702bae36c6b6f8ad0890ce62f622735504f08c909d719128dc1452d742a928d8cae0ac6527f7b92d01facdf6326717d0b02da38b2657de00e1510e2da0b4062ac56396673a6ab091016e2b99f4bec5609e074c221d66966a46e071f5902ce2e16def66873b2104910813702353189d71b1b6d4efc04a262b88fc9b9f1f74d0e0c4568e6885cf6c7a0a5b9bd2626f1a6dbebac1f9f64d17026fd13c9e00801279ed1da71a0b7cc5df02f8b40846b671552c7a9640c776c779c5ac01ede4baf0b626afacd5a7a388890b35e5ac7d29e8290ad9375205525597f987c9f2a0112fe827bacb0d7eccc15a03156c699a4430771bb72cb61f8bcf997b32f28428d9e9829232354a8fd5834406df48aab559edc8244a39da14ede8c4910c13d8b68159af6debb2cc1653f7870a9b99075ff57d92d2053c86f8e1e98a6f99a50e415ce4eadb97dc7b37b5be2e0a973ac0b37f16a34a1b366a81c37c609d5f6ab9a7a8689f0f1f7e5eac2981fb01d4e5b88af874e0d58010a77db330237a1917957473659f675c7ae38b48100505cdb32d3cf2931f9c3d1e50e2f600fbc593963f3a47eb9c0d766adaaf47455f00e123d1ba161c39a9c7aaba5c4d4930a4d44b8de7da1635adce4d3b9163e063067bb7cc9e74357466955a242c3c4de106c1ddfbe18822733639f0de6323b7430a9b671c6feecd1da172aeecc152d7fd946ee817d2a683f8f931c4566aa82cdf08f2235df6ab2413fa34cccb6e58fa73888c6213fbc14ca3ca644b362b7a81ab0f74766617ef8277cdf82f5a536fb59887b7b768a4aa0dad36ea0a34223c3d3f0427532c25c223145ff550c1fb1c400cc0018a8894023b57ddf2c3b4c7c07aa60dd37840342ca63aa54224702de5d67a1241ebb70ea147773ee28cf9ef5bd744003382801a01a861caaa468b36afcb40e38dd49681a0cfded2e25a8ad39892f007707fca7a7baf88a7b619cb157899880b5ad040ca18d9ee9faf0e2c1ae359750922fdffa00c4748ae903e0398e0ed5a4a27d78e687cfa26dc2e1b1e75e4cc220a593937a8094555e4d8ba1b564369105bbff60d8868d6bda0949885940ed39e0d0df21e7ee2dd0dab9023cd380bbac848172217d843c797a2da588a10e897a1058d9fd3d5058a693dc42f1a7e5615dc40d44da9d34407f16f74b0c01f114a070204efa7ba7b14cd270c7ce9a55d566e0a92c38ea7a6a84814aa26458156aa1205a7bb52ecde5373a5b343177e4485762eeb5f99601c7d480b2fa549c2495c080506b66a3fac9e1d0215abb2eb4d36716a852b643a1762b7b13652811a817b0305e5001a114caed8d07a23e946c68cb4d1837b306822e97e0d239b275e2862be090b4c75dd542f4616884e6a0c98258491a83f943b43d46851e8597cbb620183059c825a0c76f95d133bf144f2b97aade269c92324dd66a0bd64b3ace196b0b20be44f171007b73961ae64ef8c0bfb73b6d43f0f922d14e4feb066f799d531740ed3ad31e7b6eb83cb16ce48b301d00be67a9f02a243c87ee85f63184ca14e2f00bb100e99af0f6865108bb12379e411b558329a612b822033f8ffb818bb77800017f143a86f91663b3dde6e0f1011552f6c83a18f806f7517b7c095e58a8d06058aa1c070081a5e47d8a11e7e39c3cbcb3024229b1b3c3d1fc1c3e37340f386043fb5d485d360ae34b2c1b48bcaa2932273f4eef96a6f6d09ca9ec7ada67c400dd8566be0b64dc84a01a2a0d45dcd85f9b9d0cd069a5fdbe5644edf2f5bad1f0b859d4fbed4c3a65daffcc37ad6e211ceabf006466de0c13e08434a37137e2f0f513a6f286e082156bc722e2d8b333d521968d5d6d256e885070096938caf0e086b2a32a859efd5cf72909ed5f33abcdfb29ed3b5abf80742b3aaa1425fdb060bdc8cf33db114aefd19a02ada47f8e6a083d486039bbbf8381b8710c8c3ee4f0bc0ffaa89252c3b07b4dc6cb3d387ae118ef51ed18d9f13bbde0460d097d05b060e893dca4fdca6c173dee03757db8e36d8f38a1ffddb03122878ea5d42b47003 true -check_ring_signature 03b089a8d55169ee7b8e45e706d0f6bde752faa3b567842ed607d517a8846079 b80a91079dda33f6b9d2aa2629fbd4c203d977f4c7bc29d8618195175537a208 2 31704d326f1af42153812a22a3791cb3d90d6892593e60fa4e877de408599b69 63fb451290c8b00e332d33ced6990ba93ef1802902564f1699846c17007ca9b4 e79512cf2c98dedd49ec142ff9580c0eaa128d144756fae1ba4f64204da9230d9c96cecfab0f668a0dc499f02eec97b5cafa8d9c5ee235fac95c7a04e041a302d628ed6b34604b377a9bd7c11ba66231008667f730642a87c11b0342ec906f0699ae27f7ed6cc04619bace9ac062928d9c582c609a26ae0b66b1a16bf08a6a0c true -check_ring_signature a161e75bfef277eff94a10355a4679d0ed6652267b6e525ce85344bff50745b8 2a36425e3db875db14459b20a1036df7876524a1ad80723d16bd3203d78594fa 5 55697380604e786fb938611e4d4c7b6151162def3a60ae835baa20017a8822e2 c584f512887041a6f455edee05a081bbee42ad4aff3af030c5a6ddbe68026c07 1db786537c32fa92a18e864cfd6fbc1f1734d12b81aac169e77f3580461d63ba 4b502e0ba9f9be89ecc6b6f60111715f848931655ce7458cb6cf67031bec80b7 dfdf6b6498cf42e4e0addc16285169b91b1c676545a751699fd1c692c57ea1f0 5b5405e9a3c08415311c85f1025f9c1e903df994be5dd143dde71c42a5dad60a8f4abfce53f082d63f5b0a006f8835e8824345219bcf0d6a201711314c929200a7a360f6dbe6493ac88cd6410f2d41b1642f4382336afa087fa9c20c8fd38804fe99a1c54b534bc1496858e9571d05ca651a1bf33ad8710d88cf5fb11c3fa70c049794124db8dc1ed2513b3dafdce9f4386221f0c1e65bdf4532a40d00632709dbf29f27e490008a4665e7d6ade544ea414aefbe22c7bf75864569b091804d055d9c652fb674904300c98c4ffbdd406f9c9286044fe4129ffade8987e9320900b1f744777a47fa1e8110ef0ab9a0d942a6fd1e19986a0b3ef91f6dd8162e500fcb3bdfb5215aba269e38cb8025c7759fa552188a0ab9cb982921ef30fbb630072a36ffacf03f96cc16426889b7ce6d4fb38f6ffd8c437606461ef8f4be16e703 true -check_ring_signature f1784969fe53fb610e19794cb5ff25f947846ae146804d46b9233c6bda794ce4 1f5dece2aa8ddca641e9cd948e3a052500372d6bf1f7b41af3ef026a4034ac5b 28 216e39351692d0cf0c6bf4f09f5b6e8071bff658ec074b1cf8e8ffb592b83678 675d9c3e73299e9e3c0d0a0b262b28a28fbbd56ca011a70079e79033842fb8aa 265c8ec455db576467367e8b836d9bbc2ec8a3d91abf9a8ddb0d449a9371dd9e 648f63630e364d8324d9f6d1f3697a720dd091470b27a91ba59a25eb0b5c2dc5 b2fda266d1fa3d170478bad3a26695cab9e1805404a3a0eba11522ce9bdf8467 1f288d8dde1ad9e3fee9c235a9ca25138871537e61a7ab61e2f7f61d41d5bb51 1f008c6999346584f37174be8e60557ed1db6e5aff34458741db753c59528496 fe52178bfd37fa5987b13d479d2f455fd7839d7455733ac686a60ffcdfe43a0b c8432e85d9ccab1828f5acd70f1a3858329bca5d1a4579ee48f7511a3ab04ca9 5112c6ecce69e15fcca83bf507f2ad9520d914f5b314423f38db17474d0f3779 0f1ab3eafd95cdb5a30206df157239b0a2ea53b17286607b7c2043b06d8a9bca 86417c640f5628a05384cf0185e68c48937a5e16a68dcbf243a63cf0623f6810 1c946531637e04acb100bca679295e81c39a85f1922b5f74ba538f133b8df252 2a68f8c4b1322ac595ded91aa1788523f1663cf3355fae8cb6c0eefaa7e9ec09 9134d1358662689182a115c5fa07394800bd6e9a775558fa0c5d9d4aaa593e2d 471b38df1489aeb4e09a81e49518b19384c1de062bfff5f18527e6beda4650df e9c1ae5b97eae8aff32b13606cb21eed126597ef6deb8520abfed8b9d8ad62a0 63bc0eb8ce68da474c08550a4e739ab79ba20da426629b7304e02bc3bf0d79db 8067a3221989276108c056e9c435bab592b597646ad7316361027f88ac1001ce 57fb27cf5b39b8d1b59fa833c308ee82be0d9df049dd4f03c623e66e7ff048bc 3848e0792a1343eb2eb30251ffa98398fee2eeeb21be64035340c8c51337fa80 d3912cfe86c429121f4a31fbed4d564d82aaea1ae1bae5a3e3b940c753afc931 ce204ca0c637d3332d887f43672cb88f9a69854ab6d960ed1fa61f20318387db 4c109631033a2201a86fb572babb072188c2953eb157422360fe4fc1c56794b4 f081ce39fc0040b82a4765d89078e43815ec8d3d3eaee0d2a2975d77b7d1fd76 2876b7edc2873f88d535a627626e52b2051626940c090e1e95ec2f354ab6ffed 51a38d857f3c766e23403591977bbe510eb2349e56b3e6d4115a0b34430b5956 a3d046b321842fdeac426c2f6b28b61dda830369d9ca12d1dd0086980c4021c9 18179538c2db0cfb891a99fe3fbeb973ed190cbd02947de8125c5b17d204880ad559a2734b9d4f31305c578737b57df8033de94c318251a90e2e8a60379bcb0be11fcfde4330642d7f8762483fca858977dd2d78ce99e750a254598e3d1e91cdfedac8b97af1f8413c9970c1ea3c4b145cf00fc8649d743125e2f252704a0e060443aef3a07ba663ddcda274cef715ae2d03258ac88accb2b34c26e5d3478a0a0aa10d207b7dcbe16b45dbde0eedd2e0c1fb9f6f5dd7e281c15e53e0bd549f0243bb851349f98de3698f39b4c5d5eaa10decbccf045b18b2c9468457eb919908f2eded84fd11615a5483a09bddaa55a7390f5ecc79db0186b313831bd1f4150c3376527f852ef699eea65287daba61707dcbce369927fc7713c6d9e136e1b40ba815feadfd6e8ac82dc737aed5a3d99b6fcaa4be9621749044fafce3e1642a04a3bc10437222d71375a2430cc65d98b77e0d81b7b7d566d75cf627e2d3ae8104e15216ae6be7eeee65bcc421c9bac48d5eda9a6a1a6f607c94b290c80fe27300e9b714221f167d6d2180c4646bfd8ee6d7790aa21472af2042515cc1c926d3054388c848b27e217f8b9a995e28348849529e2933572e386ed48c7980415e2800f83b4e8b38ad55dc527096308cdce6b3470b8c91bf33d8aa53b850ebe448ed0b94ee2474b5daa095b930f7d6243caa7fd784a6cb3394b82622f5ecb0c7b49006a55a72a6e3ecf9f1555d357bbc2b37f8a3e78676cac7d0e32065c5887442d000cd25ac6acb3b67ef87a79969ad8267d2e3ff9944a5733d368d5f09b3a7c4eb0ebe7562d6a772f4554ce0c4414f11622eca8ad83f4bf2b3d65fb3acbb7bf3860832ca70fcf915958d8d67810643e1db8e1af0aaa0975b55d0754355d9b62f600795fb7f1f3a656d60c4f944644a7ed6e1c7ca72125f458f4a0966477719d2a505e9d96f9f776a8dd65620353ca0d6219f03dffbd62c38e4c6673edfda4b4d4a0e7e21cf512d00c5c4421b928e944642ea1402a6c8add59673205956c312c9e60186ddfdf51a3afcf6deae76701675e75ef2f2cb36ae1d1366b3e3541c17195d04762115ed07e0f1743895d98f83e1f7b3d382cf16a27c17e370d003bab6b2a80e2ce9ee04a2f8193cd2b4dad1a0a45c909e53e15c83028896113703cac4be360da157b741bd006eccf897fa087d54b8284e7212ff895cc9f79fd69a9ae8a09602976188180ac17700aadbface0c366be48ced9930fa9e4f843f54c9c57381910c1755fe301252f5e9b34e1927efe4a154104530b9b8f1734f5b015218cda3b801e62a9373675df803cc56999fe73ce1c25a078576d51b44c55621cf06495aff01274516fca292bf97662566b9ef63c86a2d5379db48fd56a56bfb1d52a35d2a01eacb7fad741ef82eaa28e278dabc11e7607d69289312a6afd7ac17a6daa99a082ea66ab2270d622da0a1fa2b038384e57edae5c37814c94928d54678d73b8c002e9289370c8a6e9097c958ffa4e81faa8016acdbec3eefd7f2ee800eaef6eaca6e7b7cbccb05b0b16c3e964f4a1773b511ec17a2add6ac3b38f3bfa8527b0d061355860440c51cce9755a0631a41f2efe3effa39d94c028fd199bff9ae7f00025d98f9e6666e7d9edaf6918be4e4bdc258e58f325eab39725ee6c58852b5380ecf7b29ba1bef853fdcaa17ea848fe4f54a816f9bd87ff6b64e4cd42aafa8290bd6f41d66ced71f8fb8ecdde66813c296811f88966bf987c01fd388e2d91abd02994b488b59129b0e9da2d3a6c84fdcc927a19630d4da3db70cbe83b582fa920a46303d1834e84fc84a0edb88e0edb176507e0f073d98de08ea9a686a98dd0800a7c25ec64d5e385183a111b840b98d708c0894cfd5edc5435ccedfb087a0b80d470ac8ac14c59946323e891ec53454442f714c7cd96db0efb4928f3f0e7fc80415aa1160abb14d4cd34bf9fadf3788a239274673ec0b8d51ab376e893efc610c3d7839c4af52c995bcdeb971d7c743f4611519e33bb41abc29db2d7a28eb5d0eb0648c35b0bbd29646622addae5b9dcef5151ebffedf3a63e4349bf6837ff102532bd5dcbe2328a1519e05bdf71aa95e68e45c124416f32d2707f2269c45b10d8a1fdf7abb735ccfabe719b6f617ba829eef46accfefc741111733f82a8c260326abf1d5557f2bbea74678265ef7f60028ebff8c2050770fb83da9fc21e7a80b55cd62f3b2be7c85d015716057b7bcfd9188f1ed161cc6fd9055771101b481070d68ab1ba40078cbeb45d24a66df4ba61961b929e559b4179db52d4ace075e0ee345f83e8556ffcfef2015dba076a419ce8ecb92c6a8c56c087cbb2d12874b01af4f08ee9b768cb8a7afceae07769adf1ea84b7395038ff0f4c835732ccd0e083e6f2163c74300b7ef05b4cfbd60790f290e0d3046b1a444f3b7e01364896f0fe035807aa4b100a1d35141ba3694927459fad5deb7b12f99b6adc8af7a1a610fb82378b2be87e3e02008cb92583347c1f3fabdb216e08054260d4e25018ae20c false -check_ring_signature 3b9a37ba19d2a810d2ed703f59111e01e999c37f7866138e2eb71d65e910afe1 0c5cc08e05b0cfe231d0c7a0ac85d04c1a51a7000f2a8c8bebabd46c83b5d720 1 a5db19884efdcc0925ddc2b6579afe8ad35d9bc5b0ba4d4ec50769bc802e0073 1756624edda52e34266b04ac00e8ac6ca1d8c5fac99a53b3abac376f9a03997916359ad8fbee2834bafb8c27f20c92302919f1998d9e333707b95c49b954d005 false -check_ring_signature a9090ea937ed546695d6e6b886c90e9f1efc40158d1bc5dc859742044c6757ba f5a3f60a4f2eef7b342d2bc7efb8f1df88b99e8cc61a9de05e8ea78714ce7ca1 1 ef0a423ec097457e946b07834bc88ef5c0f14483f7c3b46f2dcca5c29b2c3e90 3c5c2cd474ec25e32f2369c40b71bbf1c8f7be23d0d51ee69b740b1b11e7e70c2c28cadfb421d204cf44d545bac139d547b372ea255d221628f678840c548cf5 false -check_ring_signature 3b6cd04cc71825e08ab29aa81d4eaf5cb6f62378330139a192c5e6235ac2cdfd f019b4f3f4b079e3360764b14752d90f8a3363c5ac93b198aa3370d671ee2b95 53 1a9a6ff8cb5bb560d5b8f0c369ae416f0ec53732b1a52ee1bcfda576b06d82e2 1da2d770419d126f7103d791a84e62368aa5bd2be6fde3a1f96746a026284e7b 93595c7f3bb8a6c1a928fca7844c1815354615d575acd2129c806e23b3f06fb6 fb2e74c5ff31c4d4dbe5a192bbef1a0f83be49d9d104fb6bccde8667eee40b60 6c78daf91f4ead86306d11f095a96066d4cffb92f3096dde299d2eeb782a3613 5d38441e964ec8d1b060ac634f1a62790e67cbb558705f679dab842a633af362 21d713eae531c74a1d6678d5b5678690507f09c58d1f2a9cf1a6b9dbc52171a9 ee0519876ea73a99760ff4e8258f9bc46f773e029d2982f56318203f58759557 6cdfa918144c8252f058dbbf61fea027ecc76797bb770b192b41e57bb34422d8 28fbf8c395534bfb32d55b7349d0433b76f21f2002eb623e3b3da3b9a58e64df 118736eca16a2b5b668eec15cfa8d4791458048bec72fe2d19a0b73fcbd64f9f 735765a38b6f5e1f651a3ba2d8a2803422c164e4d062f9897d9ae8af71a48260 f0690d1189567780add430b42d076e7f702ca0afc7dabae8142309c9e1c8924e 1992b5d8cd840d1329be8b23ec7c17b6f8614e6954ae20b4c41f8f268a6ec448 d51efb183f532474452924e61711d5d325af7809900b4be0a47a31c1961cf68f 85e3acbfd082d155d57b987c0d47954bf79d34823230d2f5b5d4462be76b66d4 6854d478d98a8e1b217b34e22802e1330b02e1d599d7ab825aaeb5b4244e4a90 12d7600f0fbcd4629e481e189b7f2323d4e6ed91dcebe4397f9e7b4789f55b12 a6191795a5450a2704b9d419b350dabe76124a54937034487018621d2fcdb23e f8a6194e71327018d5e011433608379497e1a958ec05a921832c661224eb1754 1ff1f25810fb0a423c7da5354e2d183af3982b20e3252cbf4656ce827e389d88 ee69a7fc7bf59cbf740582043bab6f67092caf17102c1f791c88009ae95baa71 e0bdd1568e783b9fc4fc73b0f780c18e9129472a5f5a392ca6d3960b626b0a9a c34b64815e44fb22b1cde0397af30c7b1bd85ce463156ed311054a4ffe41dec8 51bae528ca67a6000f2297d7c5c08ad68491212dfcc56d987d4e50f77928860e 26dd96ca18d45df5d71f9ba64eb59c054d8931c31418920c2a67d5fadfd15aa4 9ba1020db56f2ff5acc2e87420cb5f8706cdf966f2758978989df6e773ab8d2b a3a7d5613950cb96b64eda0f372631aaf0396bfb39387e65ff571a768e3998a8 fab22e07673cc243cd0819bed68ebc52bd3a0c2a96822ef768ea63fb8fb7716f f5184016b37d436ac784c836272c8e9c7f58351617833af3107e12b56794487d af5a8ae6c3af6a53d804ce408588e4315fa5a1fddb807c75eb758901918ff3c3 bfb1962761c50d416a011d709e6c326dd9eebfad48723710a328a8cc0e31c014 642c3237da595a27443464af521250fd3f6c102a0e2576025a6c255d2b2e1690 0f7da704e7cebbf45e0147702a6720453464277c0b44d3f0d5e7206d8c932249 89b2514604f03a29d9dd486f6a0274b9d1e2a5f95ff5fda8797be5b5686892fb 271129971c87882c5182c7c119ccf711dc0d0eb4c8369e1044eaab7d53269e63 9e0ba31d532f85161c3756161cabbd8d8534e97e3a7fbc07607cdd6c1863d9fe 037862daa1c06b320d282d2548ace99575474e93a2520988e381c8f6f0d04dae 36a0191b518ee6925198bb94a791b98dc4d02b86d7295635889e03662b080442 b3a028300aa997244a6d1dc47eea34b6e4561faa66c82e57378ffad0e7da05ff b729b11f2bd299160cfa406083ec8aae7eb8fe951d12e669ac28d7ad2c157f05 979ec1ce4a54d33a0349f7bcf0287d524d90dca26e971667f6ed0f85fac3dad6 a0e42901310db9b9d72569274602ae4d8dc78567f8f334a39bdbe2316f7c9a32 bbbc1a8b9861596c54ae1f34a94876c6aa9ffc68ea5c3881317449eb83ace26f 7478560335457c340d6103d7e168fb0099a7e8e7f12b20533bc3dbed5cf454ae 466327660d9472a88faa80945bb28e0139bd794f42475ada6580000877c9399e e5d81509c716d228159cad09aa9a978cb590a25f30369105e9bf71bbdd3abc3f 306a797b36b89dd234d745a23ae936567cad2389f73dd48deef59a48cff1e501 9f7414cbc7a73a957fdbba292365503d78a12dd06e702e4cafd6b7775bbdfdff 2de66505a221cfe572acfd77ffa18148638ce908222b8678563483cd110e4923 7a2b1f729060f129b10212a2eaec11b4547904048b81fc37191c354cd3417eaf e3fb9d72546b34eb118f5e457595ec37dfefad7a0e7defb1097d0a62492ded53 647444aaa48c44a63d5f9b014d5825e132824eaebc10e51fa014ad698572c978 beb103f67619e1968bbf71c99a5e83a2e5d15b0c600a8a2af0b630e9667d840ba02035cb9e25d6c0edfea68d684ab28e7568d7a286f7dfd6d6a28de7fc052103902f0b7fb6ff9e40988d7e4db972f249f1cd2f955aecdd897d39c788a3c0320c0c69883997c22e8cf7599391fdcea3ed2198cf79b7d60b29396c9f2b041a2a06c6b5f3dbf09ed681f128c15e3d7a97bf386df6abf85daa5fed0f47edae70b40392ecdba87305f65582208a0deab986b4122078a5ca24f06060bac45224b9b6026b0050da073296bf4e87dd4a63e13a4043cce0672bae3542c243444bcb742e00dfd8f0908dfa15a3fab0989e00102c707ca113904e68ade2f9915a8c361fc101ac15249b42bc0a3f3d025cd896654f7dfa3d0c77f2258d64600d41fbacbb810ce8b7fdb16012be8e2ed1ebc91eebde4eb0ff184dd4200f499281d8b509674e0c8f88183d31048575f418bd1756862ad0e87c44207a337aafc919825fb2e906097825e5d6cdb2353e28d1ff997fb4ab099d5125563393b83514ebdcaf910ac60a761e851eb1830799e428e0730c0b0e14d3920baeb05e1541a34a313976d49c0f9a559e6c85d3f53376d82dcb3a8fcf3f0245375b41c8c33b115f6eeab33fb70e83a198afcfef8dc6e87630861604830bcb77b0966dff9a10b4512646f124ca07bbf13350802ce9f9b389d5f71a1bfb62b185a3fcd5ceafef189ca01114746a056393209b4d30a095a2c90ca05fed48d31371075ae9d509fe8178f5d454eaf100b5c694243eb8a14a712e7859137ac3c44039fa5d59c974d87fa36b26def39a078b3278b1475478ad2bd88d907740ba3b818e72833002c350084476964c6603032a1039b5956f8d2eb8facecbdac1ee46f64db0acc95702686768c6b05648270ac627bdc77715a63ebfcf0968ec5f2549e2ad0344f015658fce52e5f34b431007e6eab4f2b5bf46e830f9a98ad7ad2cb6ef835503321c253569ddf9a6059f340f859c6fc1f71c253679307cb1c623aeaffdcb135bd4f73c9c60d2c25bbc9e910144285959da8a193171d36ec80e32c1989dbecd2237af3dc2fb1b28c208cd53013b1a0b046b6fd8399ec292c3982aa0007660e768e52da44590196d0f159a59016058951de1afe3c7b723e9d9a6b87cee20afa5bfeac286f9cb89c0e83eee5d02f8acca1d952ca8aeb2583b0b2602926630f42b7c93005afc108ad7877b436d0312097a88a446e2b18cb6c36de6d7dee2bc3e8ebb0d72f3054acde0a6a3ce1e0351b57607489740edd4abde0c69aa94c8c8562beee0d33e7966d39199da198c0b244b58ba3d9f434eb4217bb78f16b32874defa2b23bf7ab890e911fdaeef18028d2ff931376206c499321b5290eb2825937a911abd0204b93e6323b6fcb6d50fdb7821521b809ee7590907f5b9190edf31ae78a76c8f7a43d926b48fdfd31be7229960c1aef29524909da9627db6725276359d36b0963412d95e8b4c7ef67b02e53af540a581b60755224cee594ea3734f73e8ce06795ed446488d0e83c85c05280c0916b46e22c2d8c8b818150aea2ec45482a9b5794ee40e4f2f7f3fdb8e03a65f1d9fd28d0a384fdc240a85f80f0d9f7225fa1ca4585a878e48da3fab670bea86dd4da24c1dd50aa3f3b37d34b4d92e6003e9de4a3aad216988db2f09c00e6a9dbb3cb6014df8c96cbaad517c37339fa484ea19ea55321a1dfcf51142340a7d55297d42ad46611a739baa2e9fe0aac55bbb834bd45a0c397edd9f5245e30aadeac9334838da81979ca193c2bfdc84effa207c8fa7be47c20c3b628265b701a802e2c4f4636ef1540845de512328dab1035c75b9f83721bc3aa2a767d766019eaf1862128d0c73cbb7010888244b768374ca5a22e2aeedd21b7ffd7dac500b3b2f0a152f2cb7d6507bda1679a08f73e0d6a5bd13364da78e03b42d4164b605c26d7166c1464ea430b79c0b6511b84867a1651d28e9ee360cec9466d0ddb6074ae5099a555146f6c54982d5df06c792bf782bc85f26f2ea4d9e64b2d3d322016b4c7c0de811875ae46aeec657db9a234b721fe1bf0d168a8ebae8179e00c408210879277b97c09c80e919177ea968c26f9ef36aa1a7b987303bf14f4307ae0b77d51976cb14be02602582b005b76ae3263e10e5d2ad36e8cc64bf8f9731bb0f7a83e99ed45114b1d1519a682042ef6c60c87da0b4d2c66292f863f0a3fb1b06284acd3cba2328ff40724cdc126c643e43299ffcddb26fa11d01d84e93471e0a748415af078a5fcf24d658779fd2d0069afa976a9ed4d42f8c5635d4c7c5d3016398fb9fdd554bae8c1a2b7a32aacff8a62c125f73dd676bc7b4e0a529072003b35ef896a3beedf5c114697c1ef8a8df55ca3097028b063efd87e922a4078402e3d898f015c26b3e1846c435a5b32412a643b991946e404f4237205c2700c80f10792c8de6d9be6a602afdf4a599a78acdd96e6aa3c0cff518cf870cf62b9505a65377e0f08547bb7ccbf70c4e28fc9f6ae3ef48bde53f9ac53599cfebed640fa24a335dde8fb207bae1e38e57459cf7244ee427363ccbac0dda6d4f7d1e8e0d1a8d646b779c1ff0e2170048898def0f64dcdc82b31cefeb6f2d6059d6c14c05e4f83cabdd66ac3ecc98f48353466ed447e73206894d6f1a1de073eedf1b290495e8a89e988c00403578e1cf71894dd057bacc3253e3b465ce1ada7940b09e013e2368d4632bd0a2f0a9f5bac4dbfbde6fd339472efa14d8f249bdb3ccc2d2043d08108b7700220078956d9a3039663476dbc31c24051968803bbcb158f95301e44a9bee91d26729a3a0fd69000b2a1a1797b9c4f27196a4269724e162e26606f4b4c98f1ed8cb8235e82b39f5b95924643a8b84afd97d4d74d29b4678032f08e84c8ba8240d8f07843efe9f29a48517c4a85434219bb03343858f82dbd6eb07e50c9311b4ade705d5afce7d98ee49dc40028ba4e08cfb092873515d6206970c1905e112a0f60dc23f3ac4923389a1c527781420ccf44fb376b3e7b2f762d4004ae8419d4e8b0cca158c16edf442ac34c1690fc24d34a7030dc930ca10db220a5d1874be60de98cf259c35a64678004f51abc21d9d153a658bba01a69b07e103adba4dff552c8d53f0948bd3958741ba19a0aefae1397d3ae6ac7df64f4fb50dd898b85e158e0bb38f1cbef69d8d2fff42e3d2c53b9452533488cc9e778d9a02a2bebc4ba73c9e31993a303052ebbcb80d69a62b273be81e9cf35d0d5cd72a0814cb6bd3c4519d739ae8a78c608055877f5ebc1c3d7f602a77682ba0d642670666510ae4ad5ce655ff463c785c794ea513bfa1d53342790cfef58c7931df560b2c06c0bf01ad6463281dc2a8186ac7f5ac2bc4d95b81f1f52669e8206714500253151d97d982b9f67ad9097b3ba600a441342a00cd0d69cbc1311fc63f7f1000be542b7b0bfd7a7b1406ef1c35260dd77e06be63ee8d0cadc6b399c25cc7d4038e1b7602b922d2a4f82144123e31c05970a3327bdaea4271fbfaef1463bae90956071fe12891365f78669cac9f9dd79d69b1ac0575d5412a73eea89e337edf0082013cf37c096ead5e4e649f7cc5328f57a2a3c99bfa7e2bf413fcef5c1e32062bd3a19595fcdaa54129815e45afcec989003464818e172629a4c3fbb928a30c35379c5dad442ff271e46947bbff3ef2d2d6730b883148398d3ee7800b86ca04b546489385274a51eac9749ef42a8a7c6b2f52752cc43ab3d873193d31e041019b617f86af68817c996ad3c3a4e0d0f6b0e667ed57206c1b86c8bafa9d07460923956dc852fd685296f0a4cda16c133b1566bb215902c9d37642cabf1854a20dd383dcb3207a6088f969853e08f7318d8b3602e8688f50d4d7ee780ffe67810162015ba6d785cfa664295420278e085b6c24a95fedaa026a9bc8846f3e19340ab8d9326471dd1758618167294f22f344613bacef3cbeccc25f0e4bd7751cbd0139a4df7565dafa77e470bd1b52f544a520fd86ace6ee8b206a24f80a576c5208447b371f0adce175c7065e319812be979ce5b4c9bacb9a77f0ba293ed0f58e05dcc04f180c42877b4584070dff6249f78418c2f1fffc16bf74cc5b774ffc2e0d784fe4dd02e372d97e34a6b63df1f69e61a08fa1da8fca26484b5a364fd1ff000e837b81524793f855cdaddb515c4edf52898edc3ceccf528d885ab43feb6b025f18bf4eec95d779b71581bff6edf77249d4fd2cabb3ab23e4bb6587e963f90abd8c0d154c337ea875cc205047d1d54b60b53d9b26803fa55b96d551d3e44e0eb9fb9cd383a4a047b0d91724a8cb387933d0760df4c89d63c2d49a2ff15bc4060417a700ee2bbefae0ce4e258e0d050b2a092b3f628f0df68ea1ad3d00d7d30a87c8de3a505ed7e0966e678c6bc7eb266863dd6a266e764a877f817a344d870a5f2a467306a18c5fe430ac18c7e193c0de1fc73a352c6f64472c1138fb848107a1c2259ce03280d7059446043ce9fb39a4802e82d19d18966fb75b01f4829d0e7056a67855cc4b3d4d5ddf6d8e4d7526a40b8086935c32ee8614445174283e081ecab69d78eb7bb57218239153e99422760b249a0075fdabb42c85ab431aca0e0b897ed0c67601664e05079c45239f690479c6721aa4e7759200bc8e85e02903722074b370078976a7e6e37f4dcef5283ce4126903d8f48480d677b5ced43109f080f6095c7637264a1ea8ef83e3772dec412820cb5f656adb21548f2f53840a71685a8ef1a7e7473f52c9a542b6d20d4b7d95273465e4b1bc6b3174ddea2106 false -check_ring_signature 2facd344c520d5f6d383c7cdda59fedaf50abd90c1f2944d19b62fb406ee3ee9 77b66f64b82a085e8a504b21eb5cb83c043e94abac483d60a36e117b42f41732 2 8cb253964b3b5fcb5f598d5f849479e64e8e27716a695201e7e59e16e084c8c8 316591b78a23876bb733436c4cc4c6fb0cbf7d70ffa04f0d6e527eb7b0707ba1 c51b3694e1d061a64d7f98cb71204ec92fa8b9b8ea490ba66bb1a865029c0f0a6adfc1627ebf9e9fe5ac1e1b61b3a86442411a2af3af1106dd9e8cce1bc5c70d77eb30e26bb227c4cca9999f38594d1d0aa06d0a6419a169551c3861ade23001725506b8e2060bfa89c08d39136da5fc4f0980458ed41c9907aed66a50241607 true -check_ring_signature 23f3aaf6bfec264faabed6883e06361351b1c0b2709356cf20a84fffa2a9936b a6116b54c9c346713a7a9eab1589943445207ffbe2a5463260371f59e9e4c05e 1 659eea0f0466a72e3bd5f731059362e52920062e47452140cd0fd529fd59f713 5a85e06e136ae86600430daff6a95803c2d442e64e317df4632ffe42f05d8803394e99ade766aaf8518bda2fd389965cd679f87a55605bf74939f7bc071b0000 true -check_ring_signature c87bc87572adf259b82c1e891e59ab32f3497df9b56e2b6910762e022a2aac7e 32c23791b5e784e3629385fec72a788a63cc3568d8ac6a642b741ef411fd54e9 9 2930e9be1040eeb3f02ca6c91707eb500f2db91bb772e7f1a82028d844037dd9 b634f402b7f3ad79b4089c7a13d4cf34cac48e471f995589adbd133e37114eda aae72355c265d680e8e4385ab5f1fb958442a83ed8a5d0f89bf25fe32f2a930d b3568c36e56cde079c0feb4a3aa7c0f6e4aead793b5d7f51b48b144af6113d00 6619d4df0e50c4df6ff4e1cf4cb5d4552ab6d0054973c23647fd71e9614a7c35 0073602dc1cfdf852d5a412bdf0b2326b75ecabe44d6e126210af4d387bceca2 4f2398d5c54542a0538148585a648fc6cd3f17a84c45baaed7a4f948fc0a3c0d f47613831d2ff3422c084df4b5a202c09a20e95d1bf0f0cf83d528ec4490a928 2b0350dd6368b706238f557b1188eb8d457345f72e0a72f2e833546211ad6eb1 8641d6d1f324c2d90eb6477d2f935fa493a6aa112fe076e2dde4b0a403cb7a050fbc9257209b7883c3d1149980eb1acef9d6a17d6c8c06f55d631f32cf106d097de8c8f0381be7551c2bda31378cbcbaed8b8d4b1d9bfae78fd29b06b7e8720dc3c3f105a5c666a7326f37ad8c57b962e8f1eb77aace46c2d59dc7bf1e5f88075fe73a60dd1d486df2fb9d97fff0f275a66646a85c77c5588c97a94ff9389f07d0a173fc159eb7250f2c8c2e6c95c9cb9e8ffc437fc2f40db9dc504f27724e0dceb1103a1e64d00bde776a4cfcc079e3c4f55cdcfa7ad1c98dedc3883dea2b082730b9d83cde60f487600986bb357f65dd769bbd6dc992ed7b13b1d4cfceee05bb2bdbe464844d00a9371678bf421df41e5a2159c34c628d663d6428ee466009d87fb2cb8f532d4a6b3109aa69883598b5d1880ff80fd5ebda3bcef596800407d428cd3a27f477b2f529ace2d463db3db9756f9ce65228c124ec695b25099b0882f878596539aa0fedd67292d474868ccb35037b0b537d192293744cea575007f072fce415c38327db882c7fa3f0acc13b21969caa2d6df666ffd50be379d70eb8fd8088da0133a87d8b992b69569832913a54851e30d2ef47081889f702ac0622f140e77b6bde3fe09470ac485858822385f886016a87546ec508b6e07506057d6d3811659f56203b10874b62f60c9d76970c9186099579ee3043c071f34b033241325dfe1a5f9639ae1cd85097a5b19d73b4ef6053ffdab047e677c4aea103359387d4f9efafa6a6a1ff009efe51d4e88981f65026a09f519e921cf73f8904 true -check_ring_signature 7192df83c989e73a8dcba011717e9fb382ee5f50d72281768e0d159dd49cb0a3 75cb6ae2ede7beb5547af135d746fc7bdc94c21681bd1947476d01f9db3b99e1 3 a1f9a9776d3f9fa6d33a319063a35296364fd6fe4bda1a6db6f8e5d9d4f927d7 cde8d4e82bf18e4f1b473105918ece49230d81c2e7e76241b733b1d9ae81dd36 a3b47b4af94e6f5e0da27b7449b9d707626f1348932813856e16b1ff56c80265 447de28822e7ddad7d4c9ffc4ea7beaecdd6aba31e7800fce792d5cdd0dff4e95c3acfa0124eff07c84943513e241584780bd8c0e4b368181057e1fb2619bb0682e4b8aebf67489a0274239bb95fed7e296e9e0da6177b09dc2c892ff9106a082de9bc0612591edbd4aa4c6974db24296645cb04152cbe2e59550afbe0e74c084c2930914722f84e1ac7dfb3458c9c54bbd246ce4a3df2012edb1c9a5ed6e001ba0a48bb7f7ab2e6f5ad295e8ddb8e13efbdd794f58bcf75e089565b4c0e9e0b false -check_ring_signature d64185ca32419da32affd6a9f87c3d1a1f3267b977bccf29de8def7a38efb70e 81271b6e6e155cf51641dd9e097cadf39c25a6ec614c92c0665e8fce3db26bc3 12 cfd62f42e74ae63df3ed5381f24c81b1da603b907892409d9d83f4abdee521b8 b6f086ce94954c4bf672d497e014fc0a9d5f87b6f0fa2cbbe91bc0a646a2fb50 e5ec438b184e6a894fc50dea852bf7740c46dd71dbf97899be1276762339a23f c0ba0c17030c69757fd2f55364b98774ef192f5558a3312bd0dc99d3ba7eb6e4 b56f26abed3d72a9aeeaef0f24948a5ced36d5d254b5aca508f637fcb1c6530e 4bab7330164bb811daa9eab48d87597a294823f048597e0cb9c852994512b824 e90d4986926176dc21a441e05075fc8556b47e1f7d4492ff5460d2db23819958 6a75fef692f11f567d24e865183b9e0a75eae75f241b2166bb9f36ff48e26752 58f2a64b34dfd89c7bb1569aaaafff1cee54a90e956a317968b46112a26b8a2c a32b77819ab8dca57862c1a66fa399913ca912abe684780ba1e9243eb03d5904 dd432aeaa735f688a9dac79e321795f4c4e3e68f7c3bdbebc5ed7eee0d162a79 540aa2e8a2bf5316f31f95c756453f52fc63247831b1a055f001320c6658dc09 37950da0b091e20759654ac886e96e4d6177239be0719213c20911f38f231b07dc125400255fd5b7532ff683a25012c28db47c11e8e21e07f8e44ec986f4e44605533dff9009dc57cab2a5718b4c657447aac9f9bf9bed0b695cfe0d4845a452443e17539849ee068ceae5fd9980900ada1dc57d15c0889f04337059b912ab0fd831c8cfc4893306f9e4a85c5fdc8111181b313157be2e63f4c7c56192422102496ae17530230f1f413f0bbd2dda11e4b528cd15460dff9cf0ab4d694f5b4e0a8ed5d62d324648064255f23a63a57d0a261394a8251e1e37efc5920111379d08504b769fd0fcb0ecf37758951bf6240ececba32d64bde8dcba94fc6aa3c6cd0b77396baac2ff5fe920f0656c0d4ffe31329d89063e62bd684c00729684f22d033a6039dde87603e569ea6c6b0a176666ef867927033482c3d45eb6a439437b014ab2ec1e8d69f039b674f12ba9af35395c7d12ea2f358ea926e61816155ce20cecb13d23af5dfe16862b07173e198e5025f9ce13aff5058c6a8fb0275e09bc09e01e6b356a6dd964a114ae9a1a04c10a0926b7231364cbef2106353434f0700e234777ca9b26dda92c8149de3311dec81a76cebc87fb598254a2e2d5238bda0cdb3c30056c547de07dcb3fa39e734029a19872259e7123adc1788e1b8cc9890c5fb3533fa146bb24b4f1cdfe09d67c9e45677d9e72e6a49ff8e71d28db632500dd6d0e279c928003463f3b0126ba821eba398dbb150e925a786af6411e3b2201905cc8e229d4c33298b45ab133b7b8a265392a4248ac69df2c01eb75ee547e06529adc4977e6a7b9e383dc34c612af2fb87b42607b191f29e8aeb7ca5894910c6daf6775cb9c7211fef7f443c4712fedb297a18ad5480e41a4c6005f20f0a6000f597ad61d3457f3c8ec3b84d6e351e27104f9a8c66492cf083ab28f22c2d20fed2f31c4936b064521ca4631bba3005cbe3f2529785ee5b55c835d8ec968870903ce2ee8ab035d19c60dc3b1ea4e439a68a8c940bb7cfaffea0c8837728121090f1cefb857f4303dc4c55f8513d83f0d247c66d70feae3219238e6bb3d360008 false -check_ring_signature 8e6f907c7950b9603dcfad1559d940d33320518910f3169cb5230ed4b91302e2 c80733d8dfe2d985479f83474cf2d63e68b6a42a960803bd7cc7e41bc9241213 1 a9dfea982fe1282c4a4becd23845d179562da1826e57ef04657d0e4ac6c8ee2c e3ce19d2fc82caa77878961aa10033086f201ae5a22ef66969cf9f467a6f770f444c72f9e37ae6c9484f159feedb4804a625f784753899b9737bc613e4e0930f false -check_ring_signature d8f531ededb142ae52b7348356c31614c03d6747980c0363b02e65d61ed3c0a9 aaaee09f36bf1d5527734dd22116b0f138348c1128a6b809be331fddd4d44477 16 0d5719e3234411eea8e2023c726d2c9b664f6d37cd4fb3c0b2e2d5ecf5a69dc7 6064bfd761e3036c8300244df64e137fc55c2c7aa74d1740a10bf1e27fbb1118 6bcc645f38bc1864728fe2d1145bc50377a91ce80d85ba4d26b99c3937eb2dea d25191179f721e28154389e45d7c1c0771246be04f2caf8661730ec403b415d2 2fa51551002b5449ded43624e0ab4832366f25159db670d14ba73657256f996e 860ea567d1a48d8bd541ed2c0e3b4259547aab3620e9dd2f5fde2a26f3b7d1c9 47b2788b57ced7b62b471e56585fce099c51dad29d0b1dc195065d384591bcac ace8cf931b5b57c8217fd384d6429d4de01ccab57373fd60edaedfa0d165b581 54f064aec6c89591ebc1422d724b48a3553524822623e60b5771a55d7c04fac5 735fd16ca5d064795104bb79fe41c7bdedb792bc4e1a675f3c47e32ec1d06533 017bec8a22479fb45c2f5edea197d3039e15c6c5eb49dd77c07f53c5e277c48f 365b569060002dcdb31e933de2dd77d7834baee6b4de55bd614f2bdbf3ceab0d 11687d1e646595df413c760e19d3c7aaaeeb39d42a1276b6f616fca3b1452913 2d4b2c709bc9b24e45613a2ebe8187fc295f2cba48f746fff762313c97fdd4cf dce51d739913e5584e0a82a87d86a40fc496807d324f1d00f6850c946a754ba2 378a21b1a2fa6c0bb4dd02f2e2bc3d7618813feceefcd972bfe03b9d319f95fd 34c4fd0d279e7463f7c49ac011790ca1d15cf2923985a3c69e04640bfc1f660cfa2bf9fd9c796e69f60b2b47d6ea5b42c12d6789ddd9fb78fe9f4b3b2a289d0c311c4732b56fff156c34603b3b60022130359b2bea9920ef285a9cc1e8d94f00a8389141194cc4b36977301d9f226ed7c739e2639d87d542a67563207aa89c067dc5843ac367273fdc60cdce509562f3aa03cc08f1a636f70ef434faa2186106a3c7c60c69a15a7e7836e54884d42735e83f07be229512117e621a2f3e0a3d0f06289eeb83301506c0d9a8da998c3a21ab57379b31e125484dee0ea744e8f804c05997d842b7fa50cb0d838c8ad26a238fbb79c5f79407f67b21cee11cf89e0f941b6a330d4c160351acc1e622d1a55344110d8f4e6b9c173d280d6cd253cc0fc63b32624faef16a77cc754d457e3e36a85ba74149ff2210dbb83033c61bf805500c097a1958166fd501edc8c7c82cbaf3bf90af2270f10afea28a5082f89506b69d155e60381385a1878bfe25fc99fbea68f52e9917a487173110b9d62a1807f20d0816dca126ad21a3958862901b11b886bfc716686ee961aae3f16ac8220479e7651873d5d38207cee559172ab9fc7c41a1a1bfbc0230cc69e24e6af62f08b59bd5cb378fbeb46fec19b0dbe5108e3e43a965f7ee55c96ef416635bc1b701563e4c3db9167065d778cd53fa779090a6b54757c22e008a50361c82ee9ad30bbd992af55b714900c1433b1a284aa586b6b5f9a67a0291de9ed5711f16de760c91371aae81520d37909fa1d5f9a80e9a5e6ce80aa00965dc0990840d95c4b10ac1c110263085b8ec9fcb8758467d27cf88a17c841cca65c44bb127f0382219000edbcc4735f1bfa5d1bd3b8fcb0b86bb6b6a93dae6f676a9ff4917b41da6e10bf20606eaf5ab2363b7284bd08c822783c5ee55a6909a8a0c8d8697cbd8c5ca0ac49b931c01ee03132d387bb1b5d38d08ca976644b8838d8e1cc7e0a26d63bf0f413207195cc8c0c859454e750b2d32b1260acd9a520e6936e2cb19e8e459500be97fb17b4688f22a9258a3ad301d625c411a092ef3247dccc3fdc17f5cdb87061d8a6bfdeb4c7a2ddd4ddf562e6e971cbb5afe4da4ea26d40fe815bb20b11e0189ab1eab2985461dcba40bc8e8f30130fd53446036bb005a1c7dd14d8d2dfe0de3752f45a4ec2a36556b987b25ba363986c6161df2e6d50998edcd3684686c03229ba8e6bb5c532e16bec90d5667d505a9e218d8b83a30d9964a25005e89780d1e3b888366223b3014fd283bc894ed7e4c8378a64e196342ded7dacbe8a9e7047c95427579238482e51fd6fa38ab62988c5e6940b11e6515466fe38489eef809aa5509c7db56084a2a59f2c0d98ac3466809b5f3738ab20ac3eec94fbd76750bef117cb175e9a3ecd08b93bda534a1a5ca0cab9f49ebc1fee8455005a8008206 true -check_ring_signature 1580930e86b4af9d56bcb43a15264ae2f9f3c79a007668b29e8c5007f09f129c 29a7ff20bd25fc48a422f1b2c22e41943a452e3348adfbaef5fbf50eb5fdc2ca 165 9362ec265d17c90ead6f42b916d58fd3157ce27f3f8585592afef214898d3121 7f29473b1488762fc8a61cb679a40fc4ef63f67f3b651ed141f88bafef2c3179 7d46120ae3e7f606257ac2d39364e59946fb9d9b2b03a61a24ba684e30c39ec4 28bbde42a2f44178017e261ef30a9af75be9eaf3146da656051de6db4cb0324a ccfd6bb77ed13d05cf498b7f051067a78d7fd9bcbf54e7d9ed9eda3da0be0dd9 f50d0712c9bb074dfe6a6ad2adadbe5ceb58af456735b6331b6e226c9ffef72b ebc54d06ecde3035ac61ae3570d8e4d3d6c2528e8a706239914ea9d54c372d7b e26d92f5d5af01cf482d5642a27d12169c47bebaf79048642535b66084265996 93dbc0c92341f7bc2341338d88666516a6cfcb54e6f1057709d67d10d3039b59 83e1405725ad72963a61a4f7f7bfd8fa1c1d4150ac652eb01169bf195e0355f9 e9f3ea91ba5479f5202db0e045c3597eb466a0d2f6cfefbfed407d3bd49eba38 2b4abe18545428db457b36eed9d408ef6d7a974d7d3c89d5ece728746e96f9ce 7b419bb4b34e376a66622de78d92eadbaf3f7e5292dd1e36b1d56c81b167c9b1 f21009e4e912fc8f6480562dc4c025f7df22ea2efe6c293f23cd706efe08f8d7 2d973cfa0adeb4d396f86165b83b060cba15f311e58330e16893cddfeded1e17 289c6949e8dae9a11fd603eb0c367bae1afaa13105f0a79bc2a633b3f5af0e83 4f14a299e557273ce7ae4dece36889af1aaefd85fd960ec13f1a5704f0e9360e dce6eef1317a6f09b8d9a0a8bcca60ef338f9ba836c08917b6894a05a023a1bc 94108e41d4b581e5d9215c48661d65940d1281523ba781f2736c1d2320e61234 0ae072a3adba3fa989304e217b2ac543209b417d69e887a7c15ad65c19a69ddb c35d3afb521eefa5e285157af29294f658f0e8ce603c0983e1ffae795e18093c 91057ba63130eda7082ebfbe0a74473a3e0ccbc6644042ce0528eea7bec35e73 7102205612e068b2bf8de16f86df57c0536fd074fe561d64f1786157d59b4974 f5b5ea0ef461b4b1147c297d82b4834e4cd6f98c3218828a9cacec790ec37522 abb7bc63b0430b210b1517c9cecd02edc861aa248b873eeedbcd6b88a2ada79f 164b8a5576989ccb6c8d629050052bfb1f30b0a019d680945dbd18eb225dd769 6247ca950d909121a5a216ee98f5502ec3d2f7abc0322320e5fc6631d95365a8 d91fed20702b7e6365208e04aa2d5161ed36f2d57c09e8b1a366ee6863308f99 a4cffedef91a0dd9760112ec17919c7241fa5d0233950edd84ecd1ef43d88d80 4db1feb759ec337e18dbb4a7c8b0e6da4bfe33153aa057dab1db837ee08a5484 26ca658af56ff26630f8c0ac532ea9a8a7b91cd3fac15253ce37d324bb06357d b3df08efe10b335c76d00ba6cba74c64bd7160e2a1fc6e2271d60bf6224daa1a 8ead8c1459731ebd4b76277fbc7e0c253b9fc3c4b92642c4c619eb18effc6af8 2b57358b980610d55b6efaf504035590d7058b3732ea78151504b7e983e74ebe 48cbebd833e3d46147236704a29b74301ad60345e685d2e3a291076e19e97514 183dcaf7d235ee0f5684d6d9edd95dbb34b301f8a00440c9492a4975b3666f1c b809355f0a09750ce624d551ede583caf608d09c81a69409d5a1b5a83aef609d 425f9e52d55f6a5885c5b155e2c1abcb56d5e6767ff36cd98809bc3dc4f2000c d5d4877584766eca131e9dc1fcf9ab4dfc9f0181e1d3604607d3609387b5659e 6bf810e6353f88bff7acf8c7d84397742c32d274c1ccf8315fef520ee4d1bdb7 b1cb3500f544f3dbfebbab73663b42c0470f6776d8dd748383642a315c0055f9 f8ab948ada4a7a8f67229a33596014563e9664100ddfada3c45f4afacb2c33c1 9ab8af4e0c51f7cd4a1e9eea15ef8fbec830c10272108261dedc49988ecb9058 e5d8dda43129b513fe461671834ca1af54e524ccb73ee5a077bf282d74be32a6 5cdc4108877435300ee853f83eea79b3940cf9a8b619d3d00256d6db93d5da19 471f6904f869beba025b6abce73f77aa2dbca83c6ffb8613a6d770e61c2ed214 418df4b6e93a17f189e3df815f7dae91cd706a80e2b18a409632584cce37c646 7a7e8441578b191f6f1f6a5c0315a03dab47980a17df65d68bb70b30157344e9 b31c17ca6a0d5ea41b0773a68419b33e5579ef947e4c14f71d8836e3c66c566f 6a674faf829318e11af5d08664e9edc4655639d07eabb190cfb9cb1a2b367432 ac2c6233df4bb3e072ffcd406d8d3c739da4a060b83ce83e16cc66a0c7926ef1 7b60f1c2089aa7f8812b453532688eeb3acb503e30470fd08275f79289212f12 6683eccd89da488cb107fefe1a46c3b3d2e3ff9facdbb3e4c4f64b40e25b9483 4c4fae88ab7c5b6736e405157b232933b27f7c723b60cb62e2a803665b973c4a 1f6331e3f7dec9e5c34e4bfcd9da8c162a5f3a11b7146b16e0c22542158ee035 4f40e6bdc0281bf2e53394eb8ea94eea7fb301056ca1f7dc1bcb21831ee62188 d1a2e7091fdd0dc29bc5e529deabd2b239ccd0d53426caeec271fb739fe54848 6e8f3c84b0a14e5bcbe775f271b45ce9400720e6605dca7c7cb43094549683d2 f202707043695c90ce01da27b94c86f7b16573d75dd175b4f33f8b9e8fa9733d 0cd11d42a741c5dd79660e498fec0e594255fa25e697afc134924e5d231e100c a47cfdbe480c17652c5c7bc9f846e39cd39a9eca3de7b26eada69b152d94a948 b951f22ea0891aa9304b92131f6508c06be0be32266881ca924a19eaeb4e5217 d2761b30cab0eedefcfedece828618e702be59846bf1cee1e1d253f18c9ef767 524594e678227ec95fda67f1685f06d00e2b7a18824bff63aa198eb365dd02ab 0ccc9ab1fb176b64840233bffa339a21ee0ad391f337d2ec486eaaca0733b78a 2120de302532acccb31b24169b893233e21fb1448f63dc56b45ceba0af055f63 7f35938644a1b60d578141700ea894526c7e042c27aacf8469be4e7ce75eb1b5 a1090e221fa644bdbb3e0172cf2c24f99fcc728201e5d5f210b1ada45648ced8 2d407dd51bd4b82215fbd0e26ca6f4233fa2347c97e5ee7b1e6de9ba59db81ba 9b0c52a5c64dd6f3c8f29fe8c0818981e3f814fbffd1b91c841a20a035909cb8 feb0e7eed392e6906c39681844e446a0d754ab99b8a0d51af6c13f0d9772a84f 6d60cb718d83a3b17caa0e19ed4410f96bc9e0274d40b16a635a1a0202f65e65 9167f459e81e6120d25f13777f814764a73971988ddcd8ecb31fd37fc0c6ef26 5c658d91aa075ccde4e66e6a78f1fa2e901b75d840a80babd0e5bd913208e8a7 353e798ecfe9dcb4a11da16430c4f586ed205cbe5fb23287af154599801e900f fa2d9fa08ff316f3a8f3a0227340f040c67e2123b005a5735ea0da3ef8853ae7 3d38b90a9b1c44710f32d04a5e359ee42cdc1b14fac22268777c08a46f16601d 46a7e4f303de84fc2723f2be15550441379a2a555c58551aaf084ab4b7a008e8 835c0370206016d668635f3ee155a5f5a5c3ecdb3136dee0cbdf82b540cfe40b 56ad78610c8402f88025a7bd99384ecc687265b108d5b3c77cc2cf137e5c0b44 2d219bc9154f61852f183b54963f1bde905dde0bd51e1e3f3d5cb4b5a85e650b 4a3de6ba9ebe1223c78cf4add9b407685b44680e5f979ebfdb0a326d99eec8d3 fd4e0d0fbc937c13b27cc6439111fa9d7e41b5fc609fd53608e2172e552dced3 0bba01fcc12034c5febed8aeb1c1484e8df91ace96637d1ea33bf9f1086b977b 7a358607c3cf938cc96e0bee4dd398d2a69c1209533a68d5237d0527a2cb0105 88f179d7ffb0e6c15091999faa3c5608c17cee10e255d1b2f89bbdcfc1922943 50379f1f13ec7448360be7c361132814e16dc974a499051b19b2afd2a5cb61e1 8d27dfd0b73f3ee91cf94d0536dde83e74c40c6a4da3eaaf0bb0882b2fdda666 aaf07b604456020781a49ed47e20747c3dc0fc2915bcfc3b67ae47fc5b01002d 727c04c630425e19073c6c4efb8066636c9a52672b699e739e03692205f81e54 ab4ae98fa1b195376f2cbdff6c7beb10afb602ec85fc19629630d40d21385d1e 9f7d1646a0a7dd287da238dbcc304eaac383c98b5f0fa78023a5fe57b877e59c 5d620cdb642eecd05404696013e46bf89aa3f7161288d44d85c02931f06734f4 5e92f97711a8afd4cc831f3c045fcdfa3ccfd83cc3751a750d9d163e9baa91f2 80f8c55edd4521a24b22c0feefc6bcca720917e1225b4c53641f0f164f92f102 4d824a38a191a8332db60e93ad4768c5b8a528a30b5fc29d7ba2a37d8c621965 19f1d6ea9ce8d365e081f57254759ec8656b237b131198afb29e4bbbe1552886 2ea54a62f2c2b5cfbcc3c7048a35f31e25d5fdd5c9535ade5896ff101e3bff05 3092485f01dc6241680ee12d37eea7d138436461d31914ba0f56eac8605936fa be98bb6918f4a7d7239e7c974bf240c80533fa44dad8e120cad1bf1e447d803c 8d7f2e0600c1f11a873dda7682a9a9f7231f01717e695bf238823b30438c7b75 8e878fb5fa9aa807fea4b7a7bd07c5b5db7b51e77bea0b81d67cad0c3a22d757 f71019db90737d33fc20c29caf843eaf4f380fde8de5d924be49e4cad7fd5223 3f3205ed7d03947e51cac1fc1f634cf8c928fa31054b7de93643c2c888f5cb3d 264ac5ec5c00110cc215707bf08354cb67b91bf666a88e9dfa23b11a72fa8144 10f4dd7a04f8b2e7ed7e6e324387e8218ea72cc6df5cd47c238a027ae8bd4590 be8bcc628d3ba750ddefefbab27d18d2fd40fddbf6bf9b9ebbac8aae32f362e3 d5d3182334db5c1b026499745ca728fa38f3ff91b3181b16a361ab511d853f2f 1103d8b1c9b3ca9d659208322da7232a9f96085b135054c2ba685cb17c5b111a f91298bd7a3bf2c54eb71e50325b73e41057ddd6db3576cdc0c408a90255e99f 323a26d048f49715cbb60d26731a0234dafbad12ce26746bb992f65ef437ad5d b3bc8d86b875ebedf6ec5bdcd3389b7575cbbfcc92947eda15009753b019c493 f24639650b6853855eb34fe7a5b346e90df6cb35f9734664a02472d4a8b33da7 7a33a540ac45d33caeab0ecc15f966279f03959bb762689c729d655fd752b2a3 cf737cb74eb675927c5da3a65b9b2ce750158c30b114d7b8070526eb9f7a3d5c fcdd18cdbb401aa490477f913e4307d0cfeb374cd45da3beda0a3849350714f5 f11225dcecb0411b0e9187a129bf4b46bdcd738d4040760e798d1ab852e7633d 4157926bc36fd21e5617f604ade132e99c746b8f7744e9532bcd005bc33f7465 3af6f4aa0bc62b066754f323ab5aae0599113d4583e438f7215fad45f96d6f13 011b25166d141d5e72acf9ceb4e19c87692ac855e0eb75bf2f37c0a094c6ef91 9ce83c0e6b64d3edbf2938e18a8e08b074c65582ba446834247e544e27ce89a7 5a8f8de32d213f96d4dc75e3cd3b6ca9761801d1ec72291de42efe02cf10854c 166a11c4b364a22d31e979c154868bd64ef349ef77d79d8f392676237ff241b5 d0593d0b197fec971c94fcb13165285fed45d54a9bde0f56ed498ed17ce2ca1f a3638b111a6d860229e8f562717b94884a360686a87e9710f699c45f95ef866b 05eb603463645060437cd713211f7cb0a3c497b2f2c9f39eb686aead510ca2ef 1b8c618b598d9dd68e262da65a7bfa964af4ee523d8a0a6529dbce5720321728 e0f400203d1f31258c381ed140e3694cde01b43e3e17164ce7d5b2ffd411ae40 ff53e4463b413e18403ab5e640a7144f3bf5978a6756f87daaa3d5c4c2acb335 273f1470502c42b6f29baa6a4fe27dcdef2c61d3818b901ff34386cdba4e8c7a 3f8ffe09ad1621e99c5a98158227c64987c4cbfe93b7a6f9cddf6a7669248058 3de50e27d76f1e4f3b1a04e68887c436feab10455f0a9d9909566a62eb8791b2 4ed087943cdcd31e6f25a94647b2879115c30376a682c3c2e2b6060f7eb441ec 33d76b208a5c4688d787035b6a0c4be0981383e72cbcfd06791737b403859001 dae1e2725f41a4a6b1a7a882512e8c148bcdd148a47fdbf4fb257d0d751507c7 b2a612402aa3a074fb792e1f61f2ae5eaaee5c91b29cff2bda9edcb0852d51f0 2463123ddb9d139a020c92512e5091c8295719485f65b18eec1a3e3d5439d2f5 36a1a706f4d28e46e3196efd94403a5e391d60d0b61d4d04bb50bcc9681a3cb7 f8927ffb8475f207cfb68018d90059febcc9884a94af8fc6f69d2bd90d390b2b 9fa16641df5983ed3665a4eabb0a4be4d5dff60b3fd481a10a0f54c87a8804c9 3ef0cac5ac0c84b23c9e98f4eb252e502a93a79b2458bdaa2aaf0788d988c9ee 4f26e1010be0a212b103332a7a11ea3c277e3a2652504edb34a517c7ab735b7f 9be0c91b0ea59691c340a65cba314b5a9be4e243215cd3a3561319a0ffe07131 18377dd42a4ee9cad659f830d7049ac30f0bf71ee08953f6e6cb35f1402c69f3 e7cc5756359e9ee1fa7f68b37db6e980110069437e0e88f93044b01f00e1c5f4 9d9cd33abcb2773d4aed0034afc323d1e24fee4ddc15119ccc82f503c6bd43cf 9278a293054b0607240001225ab6673a1271fefb964e35d82c20ae06776d4dae bdeb8fdb412d9b4efd2e1847f507cb3b17f64ecfbbc6b7eee9c1f79eb0ce789f e9e8fe2f156a223c8acd03cecfcbc0d9024f7e92f9ba5f811226db0fa3f9cfaa c94de3606c881f4459963e42ad3aa69a7fe026454cd6aad77d505f17a2eaddcf ff4ff5fee3115942e9dd4fba7e3f38f2cef13c1b40fcdd86779a7bc59fa3f71d bbea16cf18982af3b211940c0c17265755f87d1024f678ccac1730f71a6c1edd 33b2e838e6d6827a0c7dac081d9c39334ccc1058f4073af74bbeb011bc37ab08 723ed0d8f44bcc758a91343372518778651afd13a24683aae89be5be29d78584 3591277a1c509fbad205552d4d065a7748986f51d5bdf0f66e7225e30fdf0681 f3ecc19eb2fdeea97b12028ed287c10d0c3067068badaa2a169d9a6fb518a84e 5e5161f69b2cfa895ab0b3b2359b78844030f4768d595e3e81fd51013d053021 85770328df3136901846e99d161fcbad3bed31a1ecf8425539b880588203b8e4 52714213de4ce6cc1fab706c0d37e957dc44b3cdbca7f480e169a51148189be5 337007e0ce5ab469d2790b28b07be6a7578f4d2cadf8d4e34e9a2e9f932b903b e3069e2c8207524e39c53f43fcd48f9fac8a175ceca8abe9845f484f2b70a224 a6fdc27f450f8154221e9c15ab89f9863d5db77b5663042eec318e5aa975e9a7 f48528ba81e7407d322d1a35e58f15cf3af6b85d2335dba156fdc561bf2ecf98 1974a6dc67287fe8502b7cbcd87b193d8e6914e594d3bdc41db55c6ed7a7216a 7c51efcb78cb810003a0fda83f63054fcaf4ad4cd094a0c08e4549fbb46871b2 398ab64e2ade51b7b9cecb26568ce64e7f9db5ef336e0a48ed8dc975d6a5900188835c140806dcf0a2b5838deecf05fef5eb3be24557a3153ac5a666db962e056764fe278e22feb7638f5c1a382809b4483edf1f38ea4d17ba054b510f990b017241e1d36837fa90fe0d03309be80b725b9f59c158ebf9c9cad923aa4b345303815badfc722aa8a594e4ed5a94036c5039d549154d1b4a81ea99bdfaf629870a3950cfed9cf21734ea8f86a4e00b162209694914d1048a15335189b4636e7201e654253fbc6fc0200f9def692a1eea8d560f324b716207873a21e91c80f3e804ddcc5e8fd2791dccf2cef0f1545d3c4f9830d161dbb393cbe4fddd06f3cc6a09ca72282388c6f34005b4a8df5dbb4e79c31766536087d24fb24f6d06b60b26067d5a2605680e47e2a14f7fd02d73bfc0a9febb543d4034026b6bdc147e91dc0c5c61d5666b0c3193ccb04fd8c52b0a272559742ad6833a8fca2c8fcc8e11200b6ac59154af05c1b83322d802727b8c22fc25eeace27a37baef6fda1b6f9d670ebed0e3186fc38fbacd53710b5e69f64b14ecfe1a1fc9fcc5115f50771a2bc900a8e9f6cd707a915d0e958b884783b70455215cd84200b33875dc96c35741b80edc6998edb6eef70661980c97c41d7f12edc357489a38669eb794893200be190b3538f279cfe6d5d0c26158c0dc6da29be3af62da553b20c4d05d22b80e06300c61bcaa72c47247ad2b0c8059c80f579488d07450261d098f4668048f034c3002a0b05d5439771ca58b1760c887b6ea0796c4bd02584daa08533211a0beb99e05c013302331ec168b8c6e879155ffc9dc3a7c49c8fa30d096efc3beec38cb7209c63593ae830d8702c78f061db3f5e0e6f57f8130ff859b92b47dae5f159eab0e049dd03b0558e67c23162ae72b07d684aeeb82207113354a5b06af0ede65c609337e8f514acc042195b05da498ef17ad649117d38d1e08b09645d834b2177d0452e3dc70c89b94189765a278c1206c4375abb58b40b13feefc72d755beea0a05fa19bee9fa15aa890fb822051c37f4a2cbafdf79e60b3d6303d57e2fcbe3f00d6cf8a9652c2ee760a217395ded38fdfe66c34478447645a8f4105f3b4151b70aa16d15b98faf7ad5141d8db3c4dd84521bff05082d0fcf094488f4a3f1db4007693fd58008e2d02559510647b36384617ec97e5ff05618ca6e2add132060d50473ee3e0e39e91ab8501c4b4f459f0f25dcf2ad5f4eab6bd89d450cc43419fa097bebdeae5cf276765c1118c3ed190bbd5390b480048fbf3db2231b34df6e890a2baf81aa2d1ad557e6746d3a92057c9c2a96561db9f0ac60cd1d3c837998f6079e6c2eaf617578bb31c58983237417fbf8e93c8ba808f20c76aef23634c6210d692a0e356ce214ebcf0cea2d9ff1b008494d08d42a58e0c854a28c895f7f910457e05e5a5a8539f7e92ffa24a34a85ae31b613d99455a59b8d2c678d18cae8077bf7c1cb7d918f555de6e887992686db766fb94c78a3beb66cc0793cb832310adb7aa74862233b22faee0af26329afb7db33974ac00048a713bed310a409d708b57462c60d75a5652d12722277370f0dd81b9648b23d5f05904e12b9abc57a01c1897fdb10b831015c556bd9e5c62c3b183b84b3eeb0653ea8ea933d84512b051f45fc37792b806a1ffc0cb216ab90315e62dd8b1ff6d31925cb17c2162c64032bc0ed309b7cc2d80c7807b2ee4897416fb5fd62c26ca7d1dd82127bcf453d012d734e50c615d5352661b736bae6a7956dd4ce4523a619346320a4a6b65ea30464ff9c8c6da1666351da3a7232aaaf110959c6331c82fae9e345affc7028960d7e7b942acfb108d96ecaf3927256943be74fe9688c80ca5bb49c60427a48b705457e411d095034145b06ed3bfb7c86c6f27a677b506f087db8768a922bac64082cd6ecf0f8b512ae370729fc25ec19b127bf0c6fde1d2fee8439b5be28f9e20e7e9441efe493e26c1913000a7b3a81a0470211fb8740fb91293295f8d62040070ecd33e777bf294bb9d224eb4088ea2cd3d3c0c21790ba2dcdb64e4ac330b8031c9b01a34d3cdb28fb97e725fce756ba09548327c370435aa8efd60082a3ad0847744469112f62f4d31492cd10d77730c2afb1f9f68feba6cae4690fd9b97a06c8426bd128b515bdfe58dbf96c6f75e1c850958bb85d7d9e6d19890c47d47a0464c6d8e3e92364c8c6453780d54cd279b3b9443e932af2f182fc93c94d42150cd87f8d9a385db2a1008a09bf76acbb6c00c6d716d5d1bfa364c7f967a9f1f7057290f7d10b857067ca4bd32dbba4002ce08e738d5907759375bcfc31f512c40a490cfab5ab3bda7d01e2d0771e48970cde6ecdfcbfb5196e68fbd82771a65a0ccdc9269b74a286d6c62e76a86b12eb2adbd3792f25f2aad52d7116b7132841051bf9ccb69bbb877cdea2f0a25072d69d79f651868068093d292bb8e21e50630c5780556c4f41d8defd884caa4fe1eb9da1da0d7f4b09a29da9908987b55441099314be42dcdea28b22ec79b9d5fd8904ecec561a58c97dadc58771067f19570d0cfae9445e1389c870c53a51fdb5c9866754a2158fcfcfc41280d69fb7b1390197ffcbed46e564d80aedd8c59b97fad498abb9b231cb1d8891e9a694dc069e096f007e07898120db78ff049e43ed5a466130766835344492bdf8888e29336903f32e664dad00aff1f67d47ec0f48d6415ef7003e82f41a2ac0f1646afc6be90518483771613fa28a49890322a7a5e2102bda38cd62b7753f7a82c40547939c099421beb59389a00584d275d10353efc5a8041a4b9caa1cf30e5b304005964c064bfabd9ce3081305d02a828db9a00991208e73c570ceb0bd44985c2b34603b0228aee86db0bfc2fa871c8cc80bebd6caefa072e2f3a2ab84e76b4b111a23e90b10835fb9e9407ce24e9e08a63e69319e6e5a3dc490aa870fd6c180b33737540f52b5952b3bbc815af49a451f4678554f3ef0d08a02392d13e9c123f6000a7c08bbc48c80b1a7a8160ea4a024cc96db06fad636f6bee439aab6ff43d135ef6b038ccfbae8bddac109a1d11787a4586c2e66149bc8c1db443528d2c0928252c4096bbd594a08cfcdbce8ad5ab8607fa2654d4d69ff15e1374c347440059f8ab40ad4ec563dad84553db80cac6990a11cb42ba473b0ffcaa0771f8192d6f038830f5175e968e4ae4472bfd38bb2bb5fdc6f5771b97e12ef3d18e7973b6c37ad450cf3223267366813b731ae4675734ed3073e554095fbfe29936e404087b2ed2903a42aa4fc476bbcd9fad084d6a49150b1db383ba35aaba1060b22457c74421b0cc7a3f28055dab6f2375ba977bc4275a1adc9385043439c2089dc9dcdd303c8069b7307387734f5edfa88c1f1797b97a542979a9807e34610f4efa0ef78e8e50dd672f3051fd743d19e3fc3bba851b8c0585e791b16ad411aa6f301ab08760a0df1077af6523e9204f6d3674a47f0b07b343615c0468707f2d3596a45c022ab04c411b93e62947385d3a01fbb62c8ee5ba18784a5781c4f531bb5928bb5b6070ddd0466fb7bf77f33d3db5096a46dbdec1e9642181cb931a21e2ae602b27f9b0ebb1e840fc0046bd8bd5f96d56a0197376698c8a4aaac04fccf1cea70bb24940400a9fe5b713b354ea6d0c4ebfb1ca466257cd5d2d7326d919e140382856dd50671d8d6141b3c43fe638225ee526b51e6b19715a7dacb4377db99b34499d7580b62cb70e0ed41e4c9e7cee875d416be3f9b799621d7bf35fef5afebb380ea5903e8ea7b9c6be52cddc4f866098adaeb11b07dfc903f5feabd330616650afd4d03f0b9e3ac65074f6c48137c5c18a94f3bb2ef0a6f55e68ddd87ad8874b515fe0626029b276557c1c7cb27b7eab9957301d014a04fb9a8232a871d43f77af7e40c01814b95c83bead60b4ee6259491e999e15cecc1dd9a8b8d94a382e911c9ad0d3b1a40ef3d8965bb9f394b265bd8d5e18557d45950f1135638f44c363a84920add4afa0a2d177fdd29f5db749e4f5284de5a6e02b24e2004577937cf5bb04309a039ca1bbde3b5eb55927be3faf523610d1ffc2ebc10d44b8fadb6088628ee00b7e245f9bff15662587201ef8f59714c167e2bc0fb078eadecb927ece9bdce01a8cd906813613780018f16b1279e5b6e656679e542a73264406d0c9008b66f0a3faa9ce5e50f897d33f0aee3c2be859d4a9960cd926a3cf853a643b343f35e023b256ce70e820a5393df7a89d386584087b1353709d69d915caa521bd4075507f327fbe216c25044772165f44397791bad08e63373e1d4dc23277efbdad94703d99e8a41ef4ae7104b40d0aee67a48a69b9c1e4b2b08c8f302439d94d2507d0459f0d1edfb7fa39bb0e039e8b188cc5b654620c8c593d55931c47a5b5f559f09be3149ad993964ab2d9344de2885f18a5b8177beb41049278d4960c8edf72b06a8bb9da3333c23477e80cfa8ebd77c90b66ec55b9821af71334ab0794c89f306a2595636cd5cd03219deed6eda5104f3e1862b5691e18d1cc08f0ce2708c670a2bd12fadce245d57c339bfce8eff85e153850c7f0543b7b188a0d3df89127a02641b84096c3aafec0af069355c01c2e51489705b9210a895ce9cf092b40c710896a99dc1341633cd274164dd2d5fa4c6d078ad24d508634871d7605df94c100f3bb013da7cc3d7972dbbe0bead6b7d916dc612384f5f788b38e85844a6516e0859efb01017db1289e524d7e2244cc6ee2579fb6bef2cf675c73bcd4738290207508cbe1b50f4121eca745a8731622290c44e180c5728f4c6fd6522b99285740a4575b30b9e5d15fe60f7b1262c8b153c7ef78752243c4c3747b8885c8aa4060b00bd94448fafee2080fb2750f291fdcd779900f2f24787a1ad7684e2d5a42c08f1d0c6a41e90cf971566eb5cb074e3df9f510686dfe9aff478e64ed6e66adc0a74d6faab4da5bd3e436e9988b28aa93497cc8a2410b7d857d79460cb7ab7460760309d8f800bea8cde6b5fe3968b191c22ea3e913aa89d13d59cf197f750fa0569085c1ce8fdd9c797a94e69354a9ca8dba354c1e0404e6e083428545f3ccd0fba00e143895b8937ef9ad22364832d8ba78714c818cb75b932759b70cbf2b10a93f994ce6ffe51ecc35f92eeb8d96e4606ca44331939583750c5a56859709a057f6d3d3c76366f1e21c03bf782b5b9e1d1fb31a877816e5d4160f206f7d5f00b6e1088559326a3fbaaa9084eb073fdde9149382266d09d15cb1cd467af0147002cc852c110a4718a0f0d24db17f01f1cbb95393c72ef7846e869f0027b64a70d9ae34f6062eeeb8175628561baf2f72e25b9c6249c98230821525a7b756acf0cc396fea91c28eada811f2ad4d62e415055299bfc6e578357c33d3518d6080000e8499bc6b12e63f19a785f43fcf25ddbc4793d37f8c47dc5191090accd4c6d06c121cd06527429a36f8f6b9ab1c6a0deacd362bdb2c2361e7875b28566257a0b0671233ea32e423db7be2eaae1cf47a518154257895fe7c87c3f08761fe03a024bd27e702f17e987828dc7dab5744b8fe29a4314c519d3be2e00998d50893808b69cfd26c9c8add26997ae5a42c1c2566617775e33ffb7cb7364d3a321c46c0cb1c0fcdd98740c635f6ea32a4206b0be2b994fb9125065207e17a8f216375c0ab8a7be34ad9e4dd7b5c16faed11548e8bbe7768c59631d2b3f8075cf01775a071a02b70ff5ccc88a820eb657a0537b6b301259c854b2468ee33e557e2123a20bb00fdb4b1e48e2d854c7c185b552163f0657a3424f8fe57f47631470cb56550cf936b71dc23069dfeab2ab63d36a7928136f91c6d64b95b7ed0bd9b2546394014d665d48845b41e1b48c4bfda4accfa5ee4461d78f25c15793d58d6903272c05863f45454f230cdd0c9fdc022cfbd9a8fbd916c313663d5b68cdd3d43a89790c88129b2ca4bbc900119a18b8ab4daba56501dd1dc1c983453a0c01c0ada0e303eff644acc7db8b2d713276fd0cffc2d7dcddd4cb33f41eafd6e1074be7ca650c8a72a5e34da8f516e6bdba1371344a65827286c985b57235ce0860e248a7ad03d55d080ff0ea9d7cee06810672808d60bf1b9824407005d0acc8bd24bafb110720902048f00e5652c29974ccfdfee1e6cf21ca33117e00eb48b673c1cb8f2005f30dc3479afa49874da753e9023e876ed1ea55a1019d80f960b4e7cd0f89f40df4712a811098889421529b8c327b9ba93d1141c707c1915fc867d63fa35d9002586089dcbe8a69a49ee7b7e0245d68cf3d5bf277b8d639f1ca447cdc4536580a04f109400aea2ab8d32bd9879bb34abeb3871b80e9db246c4e13d5a7f87daf077f9ba355cfc8047e9919b86545de76932f93ff02a40d395d904e3bbb40ec300cc5ceece38a62b8bd861ce1950b777ff04ba2e67c68d50e307340d81c77b7040aa69b691097b8d544e728e73f51f3be6266956c53255473ae2056a6b054b47c06f3e7af9f67215976e66576910d113ea9f377fc9529901957510618d5867406030f6a407df0738a714741dda553933c5bcfc2e2a4cf44437503251781e7469e06136e0e3181b40caef084003a396d68206d08bd7a51190886ad4dfdd0b531f5085cdaa5830613d7657f4d80aa9d0904a2d4300153878a894584795db49f688c0a9cb495b71a9a122a67b3af17f1b8803fcc2c74ea3fc9618fe81cc515e0871609dcae9efa4379c2f945c027656b71980df61f964ffdfca653d7e293d89f35e30fb49770cdc3457d734506f597f2da09d0087f7a248266851579cf722f8d2f2a0694078e0f8be0391d7733d6f428a6cc2e647979386f41d4ddabdf53e35b96ab0eb3dfdcc352a4e48b1932a226a7be6ebbdad184bc7db91c150cceea889b689c01e21256fc51498337e18f8372360c740f4a588fe351be14c5ba0c3a62f9850102c12ec10ed0a4845497a1ba90306b8fd87fc668e5d530496adb59ca4261282f04b2938c8789c56b5ea6b12a70b703c0171b0263d65dcaa873ce566de1fb4a1605b319e6718f017a2c88b6effe590292985d2ba536ac57aa87c5cc52660202d20ca24f67bcc36e2c096e6ddd6f644017d4dd5d6de41dfd50e5e8d47241b3fb620ee344e2186f54b120c0d7366901b3f87e6db009cebb6c0a47cedde032d7ec890f231a803dad3ba2a1274c8eaf382c58002e2c752308f667aa5ebb7e60b74b47092989738ab221302d4280b678bac473224c56fdf17b7964d3c1132347b87d710ec297847b8fc0e5752da24e317e8f60278b785b5c0e76b5b726c6060a6e6ba80169cd1a85bc5618ce8729559b32839db6948d41f79f34e9bc50b5ed2f1ed5920eedd72413faf7e9113204a9dcb592783451498a40c2aa7d61e3de2a921e800b068165ab88298c8945cc11cf5e9c5c28dc4b6c7f93dec35a9d06595a746a838d03890146e8095f3a5d0f3a1eba282e4111d5c4ee77ccbcac9845e9fc52ed6f080249f1208bcdde8e979e0eac8c0b9d5c8c72b67e9c3ca56d72bc4eb706e3bd4e0c5e89aadec82b48a67bf84217504b92489fc50f463e9d9405b550e4e9b5e5d404e3ffee197fd141c00540b00bdb58380d595467c32d171ba8cf6166d8a3860b07efcf25f38d7a99ce92c7e07ad0583784bd25baefdd3ce605d4230df09afd390b6f8788089cb985dd74f9bc32b00d441e2c04c839b1951fdfad79f857c9813f054640c0f96a73128096d029e0c27d6c39afa60afe695ebd0a5b2d04c3cf30900aab33c9ebc9256c912f9701079a6363cefad91000b49ade1c89148175a8a1830e6a1776ed5a4facef8f9e3d11cf76f9c8bce51136a90dc24048d7bce2a2ce2c02191ce803549055a0358f187279777238f120d972c85841067be96aeec9474c03cb8117306d1ef34d87c30d9653e88c23636fd08227a0f8c076f01192ba17520f46d9ca8ec14e3ede8807d820a4e41e8301d07d85fee0c3bdc181ea95d3c5c1020c147a0d17faed610277af5acb948d71f81b22e33b0fdfc3215e682c645e150c0d91f3528c45c395ea8398751aa03000523d9f61ae04461995cbe841795c2c0dd1cc037db55100295a108aad6d2edc010b1856ad0b65668747296aea0b5adc0e86afa973e588ac07431b0591e7ebc17dff88efd56017f4bed7d9f3ae6bb45b035f255be99bece9a8a4b6cc82730ceb162d8ae08f791275988f112b800eee250443c15540825bae502a6ef8019147f040f792217209f326e273b3efa6572506026c1774de01040af48e0c0828679a46d09d017c2798d3c20906bf0b97045f550278b433daf0da8a0be44d4c9ae4d9dbced8daa9f01a7c9b7239b9623bd016c00d3a65fdebfd44a22d313f373940a661eb8d1e903f88410fea121ace28f782550cf1c4ed7ddb03bc77a9a7f9e285e55da1dd3a57b33bb57fbaa61ffe3279fc380152b3c446f9c18abbda74504cae747a665448b9fe2cea074953a0524e3cd88d09c1a860803776ec3a584b1de5c79b0f0c742d9de3966b10ccff16fe398acc7c080523537de347295d28fdb69c579554d801f765aa8d4fde9d3b94d7a68157540227305a9c7f048d0fc277cf83f765a86d70df3c8452cec51c19784bc6c0ac600efeb2edff12b488aca8618c8434acf90fa1574b146ab0e942c51c61487981c606add454b75ae3db4be676195e98a5315a14733cc87bd9ef687863ad95f80e4203e2b3f5bb70531957fc633d35d11733eea10f87596024ef5fa406e07f0a8c8708167e56cc9ee607581a43cf0fd35909cac7657987e31d01c0014b9f20dbaa90053d3b5a425930742bed24b576dd31c79b52dfda032ddcb8877d4928dd4674870b7e3edc5e2deb56aa21d5ad502dcb0c48aaada51cac0bbe8622134fc738239004789ad5563ab14873475fb5d8a014aa590bec721e286ecd4d3b1b7d56386ba70f9b57ccccf13d8a51785b3c3aab7e81ce3cae12a151372268eb985e36c46c98020faed88daecf1181e8a49fa24695d0beb355f6fa3393da1610cd50afbf85c00e8d05548179457188ac1ee0eff26a6e6c927af7cbddf562d016d1e75cb678ce00773d7ee872faa05510bf20a352c090405f8023287a3283563312569061fccd0587bcc2ffdf7abcd0703f4e83961c180dfd7b66332fa1fc2022ac558302e7af04acf57fe3bf099826f90505acc8ecb4e37692d4c7c8016d8162fd4d5f0d4bdf0b4dc20cb59fcc3ff3c2049d16f1276a4b040b1fa34c541e4181bee2c926cfb40ce3613153a1da8ea4b93cb94115276a5be8f9b50d8d8aea6bad149d0d7157c00faa4c2cfc8f1ee4b59efd9facf0cf27dbae43956a2fb6689b6dab64da296d690b3a3f10ba1acde1d5205475cbc5a375f99e1d1a138b506478bc687dee45114103363dfbc8fc4301ef30aee852599a0e9d057e13a694bf0df93af51ecbb7109e0ead5c5d33fafd0b466f21d15b046af0e0807cf08cd99a3d7e654939c461c4490ec8ac65f75a6a317ec03e9b669ed97da03c635302b0bd9cf18d0f273d1db6f70f879ee41a5ac196c4c5d17042e38d2bebf633e96115e30524a30b523944cf6d0ce7d90f3c73346e99e7aa03c35d6c54557a565dbfb7f440763be5333fb1c8f500ff18f6c11a957e2e422dc6116eec3fc0189fd4d399585846b90644abcfd88004c296eecc489bef4c06f8915e46f5a56cd5a97e4f07ea0b5d437481ce8dcb7e0d6e7a99d69e68a54fc385329610f38d5e5bb061d7f2c3cb8ecd02de43e90d9907f106a43aec3bb229a33be9fd5f025e8daea138bd42c5eb55d7e6f08237b35c0cd825aefea822f156c76a11ee3630cc634421d28324377db15cac7c94c5f18a0057dd0a7bacbe424b6d588420e475011fc09d8ab2ee23d05a74b9f171fa22e80e2cb24142c172837cd56fa5adb6add3dbeadaec971c6139e443d663f16ed3c50ed591dd994eaefd818c5339154712974acc5daa3e63f14bbef75fbcf41094b609e5de441f8a2f19a2167e3ac358ff0519027532c5ce6fb6f877c27088b41d9600b2c83e95569d47c69e5a061f67658436fe64fa4046bf468aef8033898d8c6202ca3dfd39074d574b724b47f64a679cd70b38fd70044a0b89dff501379b2bfb0a25f00b09f8679a9d22f44ae4dca149866790c392037ab00bca4133416c256a01a3d5e982658dbb8cfdafa797520c50f3bd9153632b701b7e6b88917a3f48c502d3b03a8ce43b00894d886fad65ca3fc2abc741ea51067c160c215ad1904a780417f766fb2e8b46f6411bf446a54c31fd5e6c6678363d77b45102ea8fa0d9ed0339f926417e0cb804e22780ea7bacbef5b7cb7a2f6975ec3b249cea7b84964d0347509e130a7dbaeb4cb84d004bd47e4e3882f0dd5e40dc84296b01cb73632706695595e3dd1fa3a5af8ee39c703cfb4c9639f82cd5ad85c4c78e4030ae307601aab376ea98bb4022bcf173afc125e536f6c3c05f6f8160f08301624bd2f2f301829e9aba0d5382bba0e5d3d07c972a1e540b85919272a3de878781341a52be0e30b3ad99d9164df5b79482fb73e07fc51f0aa13554788f71138df41e590a050a0ecf5d65d8d5a77fda9885c7f22564e2867e50c18e345c66da3f2fc49f09de0e7886cdbc69be5c22a34c735e66e0a67acf8c2929246c1f7d7dfee4a844683c0b7f28078970085feeb74e00cc55d5a0f5d7d98c68664dc48a5eef46e7cbd58a0617bd3d3ec389d8f17787f805e6bd31c351befc8269eb696106c2d6fbec2ea30193da8b5abc9954c2d24924bdd578747f781911ff515b2e4bf92634b6bc896604dc261d5f1aec19ed17115beb513f776d64df387a1acf9ac5de01004e3bdd340af4c58098b4e87163c8d3c24250b8bbe36e4470f53d2ba04766b3139fa7472507dce5c839510262bd4fc00c5c7a93a4aec169b2c50a55b17c8290ca4ddcdb70095cfe4f3a28f3aa9934b06f1f5a1e5e9cd5f7b1ce2077220f5f976c15d1aeee06cf767db9e17b4ed99987350e0eadfed14174897b93367ef2e0fcbe9af464a10be52c8886e438ff0ee078e32de02f6b395431dd3db6ab6ba6d140760e915166041b92ce4b53a3b7e59a3457f259fcd3475886dac22c80180dbff92f3bc309bf09bf0548f18fb7daa2a7f45afede222cc4f27117331bc31eabad8f20d016add2070699dacf8a0fada854a1ee5058299763de2d9f295a1542ca23ac2db20012100c13e930aec18ad1411d50c368a91f72a5dd5325ef4d7f89b5c751a83a8a30ea00f64c043fa5dc217c66f6cf0fd37607d70abdd7f47e5f4491d490725b03266800f7678583c41336a605d5bb172d3ce8c334449d78dc4b5ffb78f00644a68a42073a6f76bcee8959bd6109a134e710df0d99bf7766fc3b204f5ca7c93454f49b0e54b5e28efdc371b7c9f57b5b934eb7af0ab1483ace4abe2111975b18b4e78b0efb7400e6449a994db2baa9c30951961e1caa025c89b7b71f805726e205ffb70fe13aea9bb4bb224c0ed0a528e377c189fd8c9f8d63465a876acb100c13ac0605168a24c364e295615afb39fe2ee9ab9207e3dfafc42d319f920f05bc68aa170eba9dcd9072c0b875a06fbd721f1b6e434fae81b8e27c844091e8cdeea46fa905bd48b4af827a746ec26fa29b57eb1d3b3fc1607727a57cbb1ce27a6691287c0b19660448f342c201d1f2b7a3f0db79dd2c50b92856271d5b66b18ddf4768e40f27e8301e653e30316060f64822c5dc0850584bd9f73d87ed3a2dbdd88204310a810525b4ea9faa08aabef8fabae97be1239964d52e30dfe549269d0d6828f80de9856cc1b3788fed892506d497874baa54e30dd0a753613afb00707713b3a50ca0359a5a4467c953bb3a267722be7e98a89e4d9eac455deb9e444580f7fbfe0997ff3b070a3bfdaa81922238b0137753d402b8bcfa753aadaa4b56eb9647a60bae237fcbe00b556d5bbc4a1b9f0fca115d631448f978f843320c91e4421b500938c7b11675c41f973702d711db37db60e9ef512b415f3ef3846abc11eabc1602ca2d44b81c9b2150ed05798d9e7d5dbc9eacf37021422938df56effcd1ab540f1c5c2c6ca1e4863f941e754ff8741fb85267b6bb0041b93d95eda9116b03d3028a17fd5835f326ea98d40c2706464b3db2bf10570f0eb9be97beff5316159a04bb539a7e87bc07b6a968b962323fadde46e22f0540651f882b9cdd8bee7d5b017db8b2ded51b96a773b1199577d89f8b23a8267427393a63ce552cd3332d130d702d8f80a3e733909c78449ac5e37380c641a6d18ae9ecc8fceec6b97354c903d818cfe49e53f3572d306bd0b1f7ec2744764d0adff7c6b4e8f3489802e5ad0a05c84e2b522b51a339993fda4f1aade2e5ce7f98c755d3846916eb295f7f880311191b30a13c905b70ea0fec79bcc3423b5250920d2f27d75eeae9b90941e10a39e57978026751e3bdfe9c963cac98723117c02759ba4b10e14bc164d5e3e60729ff97439ea6aa9680cfb44137195f24ed68c8279037da2eddc4f027762a4b07457a8724039bd3292514f245607042166cde4187c324ece4877bf5a170629a0466e528d241ec0a8f647b4d129194078dae46929dea7434f96dd92072c54aba0d27462be2f393c12733cd504c2ad214fa3279fcb9cfca274fce0265a9e823280b981210eefd3ed58824af938d9b1deebedf17777c35a3538e7188816a00c01f093887649a38ba051025124981db3ad215ce3ac10fb22fc72a3d3854769634350025023a2ff38732a44a489970e7f9b950b456a8d4cfeecea23cb806ec91406709156c2ec2fff1fb6abf4c63991fd02fce949fbb1f2ed209e15671d8d36a52680d64e03caabbbea84e2df873d90c7d39188d098f552bf684499899a3059514970ae685666addee6a4176741b3d746e14e916d4ae993046fde72c2c18fc73a0ca07b489521dd23597b3d6292cddf0f95414420a974dde5514e6d24751b0c03b640aeb8e7fe3332d7477d2ba7f45810f000bd59c474accf8452a09eea4beb694fa05de9b3a02019351294eb13a06458781f53fa13bf6dcdad051e40800b57be4c70d3287c47537dff68a5d3c85cdc929ec6801131609dd834c13ba8d34641d55330045e54ba991d4c713a7a33dbff7336697298dd5341b42ff43c9630fc2e881980658525b6d4257419981d98419cd2a574b21ed3b7f2addb8790642c2ffe5a848063daf6f8695bc20c4a058a355ef7026335cbca10a0d4c17a7a9a5cddb70ac8a0aa74ea3df963e5e41cdd33efdd0032a7c3993af2346af9fbc02542ef4cac2a40a6a1fd5c4ca5c09a8cb88003f372d3f91979eb90de04f79c951c6455366394704cf04e9453bdd594ca339c4f8cac9eeba8bea45a24c4619992c571afc8101a20b10d9bfc331b1cdb0ea6dd46c2a4c819b1232752684e919897876b07a029c7c069da760396200537546a494dacdbb7edbf08c7d5161276cfc70884351273950069869823ef5230dca2b33863fa9630ff59f1ee08eac2aec5f7931848d8d4dbc09a4f169aa7ac7d38a8da263de3c41ec2895498b5acb33c97e4554a169027cad0c4f56928c74cc969bf6abcc82dd43973854fd2f7453b8c39cd1788fe97f8ea2060ec3d5cf4f4fe6fddd7b1bbd6c05d5638deebaf2e6c819b80cac88930f1c3c0e767bef1ef5282ab579bac375b955d878a43c77d47a5ad35b7fbb11ad3190750b8d765c3d8605d6ed9f0266b4e348b4e2e1409f954b3a093f94e9afc84e37a9028f5ea4adcfa4f4f7effb4b7ba27c84e509f53f654db655c7d2eb47f5fa54b90d7a525b7d53817d5d12ac4cabc353e5d7b81c5221479b33cae3143eed3e0b6a0bee34221af767202a78d66269b247ea25f2586774093aecc5cae6326c9703d90601af0bc7dd533e945fe68f7635e6dcab7b08d5a73520b291a7624a6c3251a60bb4a8674321d28d8fdcd2c1295964bfcb999068b15c306a2e2a1ff0c18c9a6501bbb5394405c1c9a7060024621234e57de29ffb98de21f48b838e1199eaeeca0a83c304c4c487e05ab122f0a159ca56a2bb43bb1306914466ac50211aa7ada404dc3a5b6f631e8546baa710ce89844f170c9781fdd9de6636e14e47061e61690dc389aaaf5f7886207022a3002669731f79939438e1b082b0c9d252a51ea1140cf3e96818e5231be2d4aadfd6fec187a125b1b415a96dd1d435b217540408c90ebb154882122ae5da9f7a87e9c5203f56e63b4dfaa8032c64c9a80686aef18001f5040d12f435d976d27410b35ed954d28145ac90cb45ffad74b29f7fcd8b6f018c6448f695f13e728728c0e9a300b8091e6a0b237cb04f6c64a43c34d0426e08aabf130402c25ea0205a414159a7cf9c11b3dcc54da60afc80da96210a107f0f5be6d42279bd045375dbe698f07f602b31ba94805f25794d8e0ba75b63cf81037fa082820c40d09437c320f6924af692bbcea8a1a8eff030a8adadea3c9c7e015b3379546a1951acbb26d47c7a9aa94372505483b4c1cb1a857ceab4114cec08bd08e56701c03f95f83bf3013561251af129789baf5e2b8691b434132c496800a7df006bc2b066ae664341456cd675f25f24ebe8e3ba0cbebc99ba1f8c75bd045dbb9b494079a8f2c06be46ce351d47796f9a05395035c1f3aec0a2826206b06630b2689be5501e0df81ab343f73a5eaac2fc5d69c07848e555b21184e20350b2cfedef015ebe7abb9afc593a0df569bdfefeb1a66dcaa766de313b5f9b1c907d1ff64cc20dd74d725772dc150df7031c2dcc5a197444e7e32e2ac26eb83700ab26b0f439c8104dbe6bcff60a29ef9dbdba5c9724b5b7db01257481efa57820f01d453084ada39aff17b0e7ed0e1fe9622e89d01ce3a5421ec52d47af56ab009a837faaf169be81f8b760d724d8dc4150a3f5c83ee2df47a71aff981e5c8060f false -check_ring_signature 88c494df6f7bc81ffced311edfa9326e112994848b78a98cd6540da2678a6933 11051bf848761d8599805b54d9a65b78e000efcfe7ec1f84fa43ea62b5d81ae8 3 26b9b18b7584980471bba772f47397f434e47e3392b2b695b375a2e5176c586d e89a2dc551292fcd3c5286f2625af7e20eb554a96551bff53dae696556bf7c11 96c22d1c5a1366f568640a62a9c4e46d60345d0c9e9a9e9662aee607c40f4510 45346edafa23ed1c0afdff0343857f654e50fc2d31f9649adb7e66bfd7da1700db39d9cf4f21e7e5b0a01f8e409eb9b630433d68ecb4e2583e95b1152ae547088ca1c9e68fc11e458c51434afba6d5ce28f4719690c03d6aa2f8109c1f45530b88060da82957366beace9c00f7e17d4bb4dac2cc1900a994b19ed2e1b8e85c07aa72cbd9115f925f279bb98cec8815a8ebf4d4a9c2a2f2e5fc8feba4cc97e20c97f0160174f6e78576034011edbb7a227ba020369a283154ccaaeb63ff17b507 false -check_ring_signature 63573d61cdcc9271d38168ba045f92f62ae3155f4c2917282f31cdf2428dd80a 0b9de5865c42d0a6db946f97e31a0698421a7ba94fa93cef496532e56f142609 1 1ce09b5a210be3d696fd40bf3f5bf4e9ff77895f2196092cecfc0fe655be41f9 8d7a34ec410c49e57a08027b9e73cde4248f6c91e172737b39c3095ee7806d0dbc0e3e2471d709c08192554170717ca17cb6e07d6637d34e2be34214bd267207 false -check_ring_signature 4a3c9f4b649ed993b411c217573b6010ca5e9dd3cf9b2800cdbdfa3a417fb53b ee7aee3233d7c44bb47520e054ba65cc66b7a21ee7f97e2677eb1b753264ce2c 14 0b09663202ab9f7a7f27ca56a5303d0418955ec803f5a6b64016707de4a57145 a4d6fc5efd725c0fe9ceeac903462485acacd9a73ee64d7c604072efeaff48a3 cabe46558b9dfd5ac68d31a749f26245ecb4ebe28d2ca1007626267a9241c857 44e7de02fbc3b5f0fb311f8b2373fef93b594f3799ceadfb86620c0710237691 e8a1038cda453c9aa22c6016f4764c1feac70fed1cd1531bc793b2209d9f6af8 6ac1bc1900023ec0f8a80f2e488d934c0730a69a170a2b15c35fd6423f72f854 ece85341a6ad687c03324d826cad43c041cb7bee2ff42bb4af137853e3fde3ed d5d8290306170d117b51e0a550ae3e623121d22b16f0cc12ef63d7e8b04d9a01 6df555c69cdb77d036cc8acb504b5df1db30a342197340db4ed3527a2673b3ff 784cc93ddc25a5bae830dce8d7c2e1b6d0e12839740030530e7bed0114ef0cd9 e9a9c72e146761efa5945702a2dcd96871fd65ca41bb2b839c637269d51a2c1a 1d5d708ce55fb77f01df2cff6e666999e453a9277e8d64c7360dd00a8b23ffa4 878f4911df9daca7901bc86c74711ac524f6c469b4298e2aab441c441e49871c a968c7968d447ac29b284179aa6a4ac54daa15a0b1f0b4b583ed70253065fc93 e4a3eecca805b03a8a2ed6e16dcb7714db6008b70202fe2375815a3b1882c0005f301e6d60f4b6b56315336c939c86ade88485577840954f4660504c423fcf0969299db68ba479bef80d9c1e15db1523edb8883ad9bf299b9a72c136543d2102f1292627401f4b5e1239fe3df2fb75530af6f820af6b7a481b345067b5a8340bc80806867ec7deab34a4225bfc132c737d255875725b816db60861e5e33ea70a4fadfe666e55bd7e84c7d464754503855f59c9a9f2d9270ebbf07f808471ee014bbdebea9a4363f69c1ef46676e3e95fadf56b08f8e6c63c1cdf9a14e844be0bc9d1c6b66e776667624db2d2400b213af9bcf2e17117a62d7fd435b70a701e0056c8b6e1f6a393f1c04c385559921d426fc8b1232f340042cfcdcc099ab495086f52ca6d1418f4821fe3b1fcc0d18505240010948e7a4b89d7fe24b81f3a55045c0f681a8edcb1e88a6c5e66df640a0e1b9c9e9a4d73e139a8168b8239d7b8039c95b07135d5cb7b65d06c1a60750cad01881911e35f50851441c413b37dab0ab6df2e3a3dc4edb8ba9aa179c9c14344d81a1c91cec082ee8dae0608d3417b073bae98fd04dede02dc277922b62ce3997bcb5c5bbd573e222c7125ae93070e0244aef83200a425c8643961724b56c039e1233e9f592634b1a1051ba6b129710218e0eb12641a4127435bf509fb78bb389ea049fe36960ead44d69212b437ac00d44ad310c0d52833f7a47aa8ba376fb2713a8a06dc6b3d0cb04b317d0a64e20c6af88c027519d5c2003ddceabcde27b5915fc964f59ada8095234a2715686c08aa093c325a84871bf7f18a86bcba6b5c3969b82229d673c182b09830045dd50c6292bfce9921a6d3fc738bdcd1a0c7f7848db4ab3aa2dcbf6a65d9167931010e14dc6240016595093954d762c6caa36eb49660fc58b6aa139e11a18a745be407bff075e9d21cbba1762a353aa93de6839b01808f76066aa0aefddbe03b0bcd0d1d205df9fa31dc85320e546fb919eae13165a6786bdf20cee1400a503143cf051b98f9ff59dfb4a3f48828a715fc21226600901dc93f7c9c4c50ca4dcdd7020727b05bf2bd6692007a2064ca2a12db4c3dcda955a71a99078d42ec74736c5101b5b9f53981a09ad19eeb8f035a6d6899b4b73b02e3f05340bef2de927efc2001453a2dfaeb993ac615b78c9855c73857ab984287e5ef0a14a5bc44c5ddedf306ad5d648eb4280037c74cad6fac5e15edf56d6201450faa790504a03dff052704 true -check_ring_signature b81ed841e50c42f03bacd3d96d4803240c3d8712e84655f129c73bc60fed153f 100787d555c2b802480d9b767427ce50dbeb6f6965bf647bcd21d9ada11562e2 121 a99905885d9a4f66e984cd59ffebd4f07c040333cc7cea7693857ca3c4c2296f 7b81ab61abbc984306894396cbb88dd37a5f38ad3dcca6af40d2ed7eba6bbded 97ecf7d8adede1eb9d16a958d2b77e81991cad49ec28c092205d31c9a212282a 7e7c6cc8022851caf9b02ff6b769d965e6d9129a6a1aa99f9c753b0a72f5ec0d 782c7353ffed69ff61e8f9615495ac66270f7ffd26e1c6f6ac64e1dbc7c6e511 373166b1400fbfb14a2653c348b4a4b693b827f49f407f4b59ab70d5c0799aeb d838815ec5edde27c33f354dd00ac0f180578f8bad7da15f843fe5df65575bda 47e5038f786c5c1696877126c54a1a7f5b7bfea9327ec7c936e2444e1fd97473 b0b1c89bb187b3ad3e9ff37d91dc9bec9acb2b3325ee013ca8c4b0e2eafd991a b6824454fd6f12e83a89d7b40532708bfd0999cb898e88730dccebc554edb9ad 74af945a464227f03f7289a78d5de836601aa2eba558ade619460578d6891d7b 774bf4e51675b368d7af2d175ba8f11b061b2e37ff3f3add08e87674b4cf4804 7e4d5137941bd0b1878be77e17cd1855e37f8564b3017852eb9d307aa16cca18 aa4618d64c94b8bee7bf68720a4f1609e4142018969727310fc30bb0eefe3ae3 5210f3ffaf98da5cfdc297670e8699cdd08bde1b75c580432bffbfb00ae7c6f3 afc2fb008fde6aa9cbc3d386f5b554b1f0fd1b7c6c8598d09039cb1068600b99 5c8dcde1b71aea687321f5f981fc06a9bd45ee91b5eee9d1df8f9b3709d06d35 eabf4fb764d29eaeac5594bd6cd8effe926aa83f15266a588fd77b22bb6a0c08 a66499c2da3998334869c40249eb89a6f44d5834fe5f294774d86151fda6172a d1a62c1f9217de30468bb277d5503782646812f6f0c7b8153646027cd31c8350 e8fe952c389532ded2bbb311c2ddd19d6092c22fb58ab13b4ce20462d8216a97 756a313245044e999731233922211f223b5df41071ac838ecb7db5eee05e10fc c8f979022cc9507ae0880911a88d83c1efdf660819078ee70dc9d6a03aeded6a 9c7f113ced63ffb9a152ab5a74850cba89128e528f5ad3db32736001cc269fd0 354c1ad7f3620c7129c6a9a1c8b828544d95eb8a71b3cf5117f8738d8d8251c6 0ebb1a5990f466130d64ec75009d4ce67b2104261cd96f7c56f1b6a42c66fbf7 230f68c81e24a2eebffa127d98c734b490c07c172bb66ad53b5faefc033af6b1 255ee3270370205ce675b28307c8217fd75a3f8a97d53144ce33810e09b3972b 3cbe26603abe7c9977bccb5e06fe8f397048f897a58179c0f046ae1939ee2cab 396c88f026ee7700651bad55f9177a87758de26da72c266e005bc937637020f2 85141315e3527e654d45aed38271b951da0dac935d6741526c2f991465b42cd6 e3bc9c1da54302daafb7e7a6f8d9d500cf0e9390e81ef4414a8fad0d590273ad 87a0351a5e84a0f27595b4e1ebed3d42ca4417ed4981e96c021a8faf82abf180 b5ccbe895915db91c9bc58108609d9d10b2b4d0a901c2002f71ff2f198ed7276 aa68d514a4cbcdf44dfa94e735e7076b0ca80ea56f4cf1012c09a67a2d6c4a39 12ef973fd0631493920a74207be7b2f542749ec6fb6421f84453065df901eb25 e18a44f458520198bbd51b0969acdc5bbdd13badb0c9b9a95dc3a47fd8daa413 86d57c42d6dcd4158b4a39a5c1462ec524d2d0539e605b9bcb857a5ba2a905b0 d747f7b12dd26bf56f630397deef5bc8accd175f54062bd51512ad06252c4c20 e4a484d1b16315ef24f1f81cc78f00f5682c1afa5be2305ce2d5aa4d97cfeaf2 901ab5ebe78e545f891e39d80eeb53cb2ac314c17d48b4b33ad9df9b6c98a39e b23f40ce28f0641167930188b54ba99dae03dbb96f4d047e7a1cfece8504b625 d77d9498a7e8b0410f825ec7747fada54297995a74f22b7a29c4b5eac7fbec54 505c10a987d10afe0897529c1d2f55cc5609edd9fe55e372cf30b9f6158a6553 eba67a5a3569fa32048ba352cfd7ff665b7a10b1092424023681f67c1b6f9eec f8f7b212a87075f65bc67e75331c923454ace73725539d16c5d9db8b2f04594e 737a79f00751ed6047ca3ae6b084a1a1a76cff1fd01f84b6980116a872639483 9f0c8c42117e0bb67ab333176e6758505af658511d776be6fa865cda0c4fde45 c2ad6a052008506c1ce8adf33fc3ba3d845f671a8b3aa57d05aa27cd6db6d2f8 5fe7a40f309e51b4962dd969ecf59369c58feb4ae970f8bb1de2e67cf9cd3e57 20b180e49a3757cc41a79860f51284b0d5ee739ec67f12c5db05c46d8ca10889 6b0f8965b38a60d07732d8bb79f42cc70c2edc9b9f7fec14b85d4954bdb987d5 85df86fcfde93878e93688ecf3efb7ffaacb71e9fb0e413513b5a54921130576 02678d48808f170d5334155fcf623e856ee4d495be3ebb1fa92361823198e396 bbe479178bb5d31c12f9648d3e877a96a45480de4c184f6f5d3c6f6029f73436 8472f6d9b5bfe8938a499f2fe06f4bfed008a0038fd618321d1f948a3c66d668 cb3cdbb8b63dec69dd041149978e34494e0b988f1cacdff457996bf4132f765a 8e2aded1591be6f51027e6ac59f2e62516e310971f98e1ae3ff1f644cd7fb70a fecc7072dddb352e2f7424b54bcdfe11438c21bd8340f27b874378eec11d0cd0 bc251034c2422557a65db565a59bfae22705c704d043464be64fbb785e04e5fc 7f5ee701fbbd3f45cd8114c9ec46f65ec35fd518ee75336d72d38e28236ad481 acd6f05e10d29af5ac804fdfba17900ecc160926bdc476a4f7e3ed8bcc989140 a7aca68ce2e762a9c70891ecc327036f3f3b73329184ce766dbaa8bfaa9e52ae 0862699f959c679738f0dc9b32fa0d140d33682b3811b1933c680228c3744cf2 440f12183de2f5a52b49d7c282e86e4f70ec317bfa18d8b51b378b89f83dcd97 184ac074b3557e0b05b837cb9b7cce458d4aef4c69f5d463477e9fc7263fad41 5f57e21151652e5bb398b03b86885a84f5f7d412fe78d3ec70fc84f59478b7ac 0a9647811948dafe88869538d8576c20c4aa2b52a4b37c45d222ab839a80d520 3d938ab98e2cc620bce2d316194039b28bb98ad256f9d1c013d67dec1911cf81 d1be725f31d204dbb27245fa9b7d4cb27bba8a9411754a1f0775382e0a273dd0 341568fddc9d7b2db2aad21751b429ee9a672cf70480bd686cc853388b12e3f0 200bc5a44c2c461e3d6ebfc7118c10089bf0c8b26efbaa3127645971df6555c8 c59252559490d4c7204fe51355079201c6f70661853e449c6685ae32e69842bf ef98f163e8ed3d500a9e88ea8940b9f51c4a7397c8299ac365e994c0d2b53afb e519c2b688f63b6b0eb0c3e24aa9b7859b47e79e8d22ff7c3094837b1142ffde c73f8a0f5c818becfcce6e23ae19447fb75c349f84438ec13c15a707043797d5 4d15f91ef41b816390b456e629d8759406ee4d6204bbb7e0602e12028753e88c 3a9c344848ba8d80c66c257e9bdcdfe058320d00936a2aa42e21ac7d21c6c699 d6b49521d15c3105bc6448998157019025dbb60fa079c90be4f22dda83ef6e17 44c90f86041560d499dd503ad18a1795e34eedb429382aefa27d60010c4d69a6 de791e23faeb745318a17863dbe3a831f32b8ce817ac311a6f2d7838bfc63216 fc06661cd479b3eed5de2d7e077828c96667670704299e8bd4af3ccf7eba851e bd63139c1df55ba714c77ae889612476a6dea157350b8aa1c43f62fda31cac4c cc37a05df4824c6f92d863daed22116ef374d279e4ef779e3cfb76f43c79b031 12630efc8f3d3949af257aaeef679557187f8318fd335f95a70cf368ecdc495e b808f0dc2708c0f0f3dfc290905aad9bbe45e2fafaa702abdd113692e17d2e1d 8884104c71b7a97b276e3cb559befaff259b680b89cb39e57e65640286454cbc acf7ce9e05d86ab4b2c7e859cdc68aebc1ccc165593a2ae079bec57b0811adcd dbc8d830cf2090a26a27be8104312abdeb6461505000f3ff75876850deba1b0e ed9da0032c3261e7dae501c55f5176494d95afa7587d05ce9cc208ae51ef98e7 ede16cf42fb4718ed24e3e0ae1eb892b429809536554b1a238a99cdbd1695222 4a5767f996b0754f4c8f9ca58ee3ca11f88fe03638716a8ee5bb1d0c4c9a1969 d3dcc31c99be3e19a90a36b0c747f20e6531f671d6ac603766c9beb01c5b4c14 75946bbea9122b4a11455a7a92bd2cc0cd0a2007f497d7b9fbbbee2ccef2eacb dba79bc92e6daf0ca524ac2d88bd0cf6276f34a9ef7c8be508608e89bc04f507 3c199c5450f1bd7c1a05ed1d82ac6f446af209d6f7520b10f96108038da320a8 3d8f8584a29db91ccca727362813d4239f3efb3afd989d1789baf965ff804e7b 359f19e241bc8757b415f79c1436ad63b46cc2a89ee9c008cedef126ab0db707 3612f361124d6433ff73bb1b0e9587186b20ec23d4a3b61f3d0aa7a49fe21f0f 21b123341ed425140d64e4e0d4008be823a685b8a657f32b7303da53eb3b10b6 ea3a8a5ad106ed47b7a960022e1db6d0a486102ff03414bff7d1f38276dfc874 bd3669d1eef37cb40d38a8574639d8afb24c14868674140823c6b16d11bf965f 2056926e15eb88cadbcad8d83950fb76bee42e6d40eca30bd98f3816dd443603 2de75702f24c6ef785a3d9834b51a975dc91cc0bfd8c2166d242c42f2330b167 4451388c1718d234e5c2b77b11125a49ac754b0c1183ce5a94c8217ebad91134 020cf35ad95223c253c3483c2246d35077518fae3e59298ae00a71290ca24647 862fc51377f81f519cc0708608ef45a63975665f43bb9486fa381fcc5c48471d 300c27589700131693bb352515f3de3d56234356d38768d456aff6037546e8fa 18b87d21d54b5a72bc061e01b03865f62aef5fb588e89cf19731b429591facf7 57fa7a5fe26494af14d1a1aee7c5206a788028d7a75ec3fc1c16f3770a71dcdb 506b60b991c90c37d782bf6241d15efdc71fe8b723827c567c3b6642b74aed57 7bf5d876e365cfd83dda9694dd695e79125b0ea178317eb2cec2ba6d04ba5e8b b871e119aa49424c56ca1b2841b943eaf5433660f39d77ffcf10c612a3e262f1 58126f5f0c503169881cf455b7ba0a6e560483e583fec1d629bb71852371c1d4 646985d144952918fe55497d8f39dc71709359c4c49254838b7408d175b1467c e57d8deb9fe93d5c51ba1c54cc954fabd12bad943438296317cabba1fbd064f9 dfebbe226306623055bcc0c4d157b92e39e5d1cc019f45e76230910ccca5e0ea 5d12ae55852ca6037f075da6cf8069e3e75cf925b53196431d90e4572d5d3b98 aada941bc3ce723ce16dc252f837c292c15e816bfbc357ea1ab3e4ef203862fd d5c0dc39752a48e1416ae009af877f4612fc218b5ff13023996af6958fc68387 8c4aaf8900dd816947e8de9417447f7ba72578839a26ee6dd825b8004a178079 fde8704d3c42ac6e58efc4fde574c6c3c674ba8b4de3e06e24289b4bacd3db0642e31d0ccb45a7d3510df3f9ef270f92d0ce471917cb70338903a82617f75300e643b9c350b5d054b9a5b684f9c5748cda9dcbff94164b4a4130b5777ed03a0e7e2e469fb7986e310851acaf154d955b6df55cccf942ca4b0776df0ceb564408f59a134b93e63ac065e1042b4d3f2c5fb41831ed38db62f12213008144fc4800870e6b86423ac7815515fe54449b4977ece291cc7766b030bf18a92a245dd506fbf3825ec87d2f46035fffcb4191d0dc0c968fc3cb1d3b2241a3a946d5a41507a0991f43754d51e4e756c2ed959e46a71652c132a427d124d7fb605d32af04031609e3b37cd9b064ac81e28ae515adc35162b2d38b0c4ecc938d86c8cde9470a8596454ba01dc09e14e598d3ce2b41a85c495529ee6ba55fc4a32eb2d6eb460a946f80650521a2379dce002cb6cc8c1901e3b1640595c1a6fdba2cee2d4b4d079602733cdf612e51086eefc2a91279b7d3c2aa8881fd872d4cf49a96eca63801e1fbef4004b3fcf0806b3ab3d004eb0321bafcfcd19d0c1506ac6de8ecb1e5002ec9cda0a6b0c4e6f5e5c60faac9a271d4ddd1521bd213e5e90bc11922380a081db174bde748335f4b7e4651123f6d3d2064385a3d9722b47369cf26c1059a0fd1b9f2a6c668ee8fb9f046a3a84af54768e77aeb4fdf83e174de42161a64680ee7fafc9bc32d4d3d2e095e6f1b463dee46f23c278e6c10c8bd34b05893802b0ea3a326e5b0a42c86d44bf43af1e3550192a1e8bb25ee2bde1eb8cac05a0c21025295c0da8f17c581face081a1341877aa48647c4e0a8c4164a6eaf306fb52900a7679114a427d7dfe8b377a1206543033b19f97c0e25379b3e0dce2a020f0f05108d7e06f11b62e52c93d0b096db651ac35a99bf273f115600ca71d8a610740622b3bd701d22e1f532552ca7a2f794a2264c6f6843ede9b45b2f82268fdaa70d7bc4e6d185bb0d6069e483fecc2477cbb518c98bcf69ac43ba1c28d5287676082c6bd16194b3b178d57f3c435d5e154dd7db32e944d492241fde1c86cee59b0f1fdbe2143c7aeac5d0515ea0d9b57aec6838dbb366b67f609ea70f5931d12406044af46c5e1c3aab568a86538050fe2ef5518a009e486878580bd3cd9e7ced02db1319737914a3e0d24f70f5f319533d68e3c8338fcacda4bf0baa924e156f0b006891964bddc82f9968f46197635cf5e696ea16cd4839842a379d4262f72b07470ad80e676f4ed89dbf76b9fb538b9a62f005d822eb842e263adc9026ac320926e8bcebe06d3c2ac264143d862a027eb097d0525f8e9ed5f956c1468902df070a7aab504e84d129684f0058831c19540ac5641ff3c73afd6d1411b654abca01fee08b8e8fc7cfc4ce079623089edf3a7204c55aac2d3a022eb83b6d8cda630c0674433569f83bb0ea7ede3fd40f889cd0f63d717a367a3550f41d7ff679360ce3baa02b435d979228b73be7413a1a669e28a44f08ffe14a23d10f192a2f6a0511be961dfb423828a8949394df9fee0693269f34a02cf5b03195df2abb8a8e07f29080286af4cd4fda02b4b398fa2b15332827ab5f9d85d1897c0f4463aff90accacc328de246bafbdbb6ff61764fda23dd5dcc6fc1cadf168b31a199869540389739091d8bc65c26920b0c704d8b36cab1741af4892e1bbd1bb489400095b08886deeb679daeb16a4cb5c1aa83413440a661e7f35d660e9e556180c89a8a40a1cab98830784bef95847f2ad76c17b820ca1cf135c85290f800d44daeff7010fefe7d168c13b47428c639d639d150ec98da0efdb78ce2d7ef7bab9c13910cc0cf163242cd8e7940a9a0a2146956db8c368ab617dca0b38468e0e9371b6ee8307deadc36c10251b30e34b199cca7327c9e9f22846913c55f57ac03a68a3ecbe083e71b0679d2f464b21d34ba96e5edb971921a0a8e36d462ef4e210857f83200304d885418abb3f5f99718ed585c2777ef0ada60e66242459e237bca815014d0bf21e61529bd8dd7430db17e97b7730d2af435e680393a81efd0c337313331a0a5d239e148326d610208f0f2671b70605e989475dc165cb5f0f4c9db830f360051358b0ff2ed33b712ad43070bf78ec8c040ca266c2ef18e3e07801b82ec0170fe1c3270c7be8f71dbea92cc7822473283fe88ffb96fa16b9548c4fb48f41950bdcb41588c47ec6f2ab252b0d7e2bafd22c98032fe86a089fea516cb1f4584a0ecd0c069aa743be9f79971351132d534f8cd05c1e73744e3b2aa2b1a476b1f00d139f5c317ffe92cc391defa2692f5b65ca5c7787d4300e290aa048bd34caa1091fcb86a84493d89ccfef5f551d4bd024726ea57e595d510a275633be23cffa0cd02235948139ef2e017ff56f2e6db5f58ec4c945db2518464b7cc80172684206086ad9e14595288e085e40b829e227da01d92da6da6e39f68ea6a87745c7f7072c4248016c81faa673b61444f65d20631d60538a4561eff255035775947cec00e1cbfde990d808d29c8dd7ae87fee37b6920686eb482a28c89d8e579dc5de0006bdf25767b77a0109ba3f2e8f412729a6fdf1354c944d31a3b6b7fe04645aa0790723fec3b0e3565355ce7dfe330f60d9d6b34d0ed03a5353703caa7e3ba7c0bc95e4e6b38c565a8e061b427db8ad8820439f7c3c9534ae38c49c21e7078d801e2a86659df5d134f842b6d3d9ded8f4e808f65d3692616de9dee3bbc942c990418ad76e9aafa060860a11391d7d9c7e6e70fb9d487b2b48c8706de6b83fe3c001b4a80d60350d969c8abf15795283e83f82d618d763a95bdbf2c126d2c8eac096141077b34312151765bc703f33927e51fd09234227980dfd9c07cd2b53ed008e5186df4ac4d5e0d440fc9e16122d9b2d9c94e4a90322417125afcf9dd0c0a03b11aba7ff5e8934ed6cb19ffb9af430505b924053845219e52b39df3a2c1ad0e6afee7d3ac825fc71f1a362c867a275294e60e0204020d6f7f65f51278ff9b03cab35e2ee51643008fda0d83b0eb7b1c1ec16b26244363ce91487ee37dbd37038b1eb3e27a370f4307d5b4bcb29db0e550348cb7a00e43005228e246e3d686017a04a5a01fc511e7fff97fb71d6b015a44cc545a0cc88dfe5073dd32955a0d030b6fe753be3748467933e8f99e1beb1a07e9ffa67f8a7212435bfcefd6394b0e4fd7a5b9f0d4212bd062b0b657718ad99a40feade384469a279b7855a4e5120edfe8138e9ac03bce9f6c8df7c45bca2771e354060dff813fb0d8273843cc570e4170b6b365f533aa72c58c675e6610523f445ae43d446d17a834e872bdb764053c1e16cb953b105bb78e1f9854495fae5ceb95aa5e88cd219c98806015ce7e095ffd6df8f2fbc94e0a949762175fe9f3927e6eea71711ac5c4b56dec54142605c9bd985fe4df0eaac4b52fb45837f71590480272397210a8b4a272ddd90d8106dec65ad260c3f37902d4bdb8c7f67ff2109928baadd3522affbeac6ed6cd8306df73353fae0434b2a1fd225fa5603b37d3ce651e4ab8b8a52153c48fdd01ed0fc53bc15153aff3d67a00b55c158bac0cd0966355629636a75863c5df21327c0dbec5fbc8c6353135b6bd03a6fca36862f5b9d73a2925e3c64f261a71b69be70f3bd5651f292fe0547484c6d6132254b20b46e28d962ffc24c15797289cf7260bf154ce622f4ef9515da1d45f2ebcb1a8d0d67df5f7d33372e66fb09f9422190d09bc9a6a23741be03780d1504262933c7b3b66712b8b49850994d57458aa34094fa2d86c5b13c3cce75ccf4bd37515de6a0982646e47ea46024d638b2e95f808c1c4acf2668041740838fcc2f332508ec82cb29f1fb990200cac60baaa63f003af376d78442edafd5c83e88f3abbc0c02aa19bdf292a07e73ce8ecb32f464f0143498975ccf67708a4a4126e686c557db9dbb5b139d7986763de9eb3679e0301f4ae28e040e762438ef67d8a95a1ffef423c2ecde12ea610623fb882d63045030d56a2f3ffcc4662df1d664e64c3db0a5da9ca1eb183d39453144be1ab95dc055a1f71d8e9abe2b8e0ad1605801a3fde018c03bee1f7c8de7e57cd6b8371d7045b69a670054b3aef032487270d82b9616c0ceb7a17c82833329d27cbc8d47f0b22d3293441bc00245b71dc724198131a2aeefc4c947305c15197c742c7169701a3082e255da361824f9622597f0fbbf0ffa42f8af8d025505c9830d70198ce04d8a50fca24f2d8d7baf804c189d650fa09b51d99d04bc9cd9597a82908f6df06a80188a90e472c480152ff686290e99821a8feec80f9ffc715287a2f0e367104c3ebe154351abdee3a5ecce94870c7e5c6c198c406c7b308a747c70b524e8b0d13c349691366cc8357d735209ac928ffce41e96c2c9a25fc9e6e75ce2925d4020e2ffe561a2676fde77ba5878e520980a174e7697b44c61f0097f72952566f0e333246a4b296b0debc1b8510695572eb34efbf1f39182c7995e7329e2bcaf8046e9573cf2857e490c3f1cf29c98209f97274bdb6a7c44d90b90c4f9776a1b800f10255dc425d9189a9843c989e4b48e345a5934a1bfc8da5d09b55a053859e0874383b7ce5c0843437dd5dde052e47bd968a5acdd0c6dbf630003e02566e0008cdb8900182e4a26b1e7369f370f367e6b80201bdabc225fc9c45aee53cd8c90819f26ce619401004ccc691f14d0b4fbaed8e12366e54f43a1dab6447f88717075a13368911e7762a19a93cd31d7fd12847a845203c36ab864b1ef51562833b07ef1dec9f481d6f359cf55d406625319b8e5e98a18bcda402d4145f336046e70773ee2d3b46ab2ef2ae915ed84ebebb1b09378fd6d83ce36136575203e4cc0805ed89bc1e2219760446ccb2db88bf5851cb7ee0962cad2fb8e2fe835dcc7387018323d484554019ba0b3c857264ddee89902214f7a02998b73b946ee7a074dc06484d6be0a6a64946e9448e12d1347f4024941b8fc78baa94bee378b70a1fd00b738f55a9b31c714c2a2489e4fd9635c466bfc7a107629c517c6d4cc10e822d031f793681ac9b7341079f31fa5c3a1806436f0c867775be478fb8f4a9a8a93e07184b9faad459b077f0d8c951e9d518b811ea796edc94cb55207a67a23cc9b10f299b8a6b81a9e867c7643196b35244220bd915fa57a1577de3d7a4f3871af104e50c2452626d2591a73f1b84275e32a34968068ce989b040c100cbd71ecea9037bd35b3f15ed63938aa8548a3a0f40c4bc7b3cbfbc5c778a640ba643487f7e0c82363df44fcc436ecf1e692dec97169f5b46d2d1d26e9efab7c464281fbc9003af3eb0ad1fcff6add699aaed08ca4f94f2b34c0e07c296ac53a3a61d20970504b87f37f92d810a51046b32f5d0f0eff279edae52275e50da1b00afeee1e19e03c064bbe5ffae950f27c92f6b629cad3d149f8bdbe56352ccf9827e5479ec9b05466ed7577bda5afdb0530812e6bdac6eb5d188801bd4bc58a762deb2565d8a0a0788e8d9bf1d146bc9247efc369607f899d53b188d2ec72d0d48a90a5fbb1e0f29be1e2d365de7faad11f02b21eb0c851568166771d71734bf85083b557343069bf3079f692641a1fc3fb9d066b6bf67eb7d4a8130a594d631b1b8e84990570edbebce6a4f6240d1f0fc8e538cd27be06cc674916dfac668a64b3ceadfdb2f0aa689fd2ae050d3e144fa5f70b114c9fae647229cf8b30ac79bc6e51eae3b0d0b238fb4b41cdc7e6d03ef9a3ec8ba236f50d9717769309608086354a7bd4b040cdd0a8a129526a2b21bc4e189a818c5dcda11e9531ec29a1fb4abd55f454ad2074ea050ff6e2a86fa46b899c10f24cc2adb9ebd491fc21165d0ae18fb7deb530a35e3eea284473c8f23d283709bc2ed04bb5e75ea12937e55a47e534ac941080295a3658acfec0a364666a1b3c5482e645ed906fd776f4829a4224aca45203007c9ae80753fbe33c535e740916f39e1d73e7c751296758b77b91b3f35cc020604a32603c01f9168971c3ad1fa05d33e22b4950620dbbc547d036946b1e246e00b0d625d7eaf80a10517b4db77afe3f86e63d38511fadfc56dcef3a459b916a002dd3d0ffa38f5a10dae4f6ef4b14075b24230f16cad25a77c0973dadc6c013c05a7f53bec87db33f37581b7cc1286651511d0137b4dbdbb76169924ae84566e05c0a8650a5cd7f1111bb5cd24c68aabd4a04c7b9f5b6713a24102330b1ed8d30bf55f2f78934e059d4fc743d5eea3a26c6da3c1aefbe96dc20e74f53ed12e570ecf89a0fe10dff1752e1d08044bd80ea6689deb3a83969eb9a90d91b1763bed0db8bdf804713f8f7ca3f6cc40279a7d526344c1a4c9f25c11366758de9417b8097aeb8293f3861104ebc42085da971011f5d9fc4fccc8eeacf506deecc697870597039c7512d3ef5d82c01ed8eca8d2b9ce8266f3af667653b816d1d380408c0f214b89b9272b63c7f99971601f9e91d9e0118c539fee0317c76570f92aab5803356949382e085312542e7d28ed4e3e02dede34ac97eb4bc73f5e9e77ca26bb0c16180b435bbf464f88903ebd88b7f24a913fd01e57e51b68188751d0a2eb040dfceb0c346913bfa9e66587426f97e9907605a40f0df8c1792f912c15a1cd1104f4bdc1cd6be6949d253aec56c272607fcd0e5adfd972afa674ca9bab70444c08db6fba613412682ec5c8040e09f727363673439d1baab17a0347b643173da307d710f748c7764f54975169ca69a3da890673e22e35ae3b551307e740cf961f05747a497509fde33437287ed66567d6e3d2079c627bc880f36bd92cd7bc41d405c560f9a63ad501a69ed1674f19da6d3cc1d948429c474b26c199e8f804ffc3072f14731f0951055e9b97fae9737ce8d0f37d973098d22984c4798fec43bfb60d5529c6c4fe98cbc31eaa27d696a65ca58b3cfdb8d6c954d3cec6fb4a1f8c8d03d7225bc8bc879959d4bb7ec0be5d647ecd7052a1ea139a89f5f84309430efb0e488eb35401f4fb277d9c784c80c6042a31c45bd796136d055c908411e19e6006cc56fcb5f64247827a80bf223cd60c46111b9770173054c1b613c27d14ca0106d1c2e896004e7cbb12a761092322ca542577ac4db284fb22daf14f594296a40bf27e5d682cf8fc32178faf05aff14bd3fdab236fb0c4992e4c7a64d5a88e5d00596640a535ed30125652935d1f31f8760944e6043dedd998a33eecd51c669c0c24e2fc9fe8b3d05621c07781103112be122e0d9d55c19fd137ab6a2a0dcf1d08bc449c97b4b4a845aec898a169dd3d35705f44eba15c28a2983d1d66085a4d017a3d38366338e4e3db3c5751e92541350fc4140074504431399db5a57470e70156298c23a119c683bd84875350728c5bbe2503e4008d8d58f9feedd89a64ba01a081673366869298587e527d5b0708056465869d527128b14fbfe903e060fd023a1cb7b800695ce7fb55879f729888f2ecd3170b9c8040a44b55cfa95ff7d30291f01ed0bd07a60c27d75fb24c27b930aa013724c131747937a8f5317e2c450a97279b71224a82955910df65f71ea5d8a3b8b18dd695530f642885bbb9e49d0e00a965a91c8dce0a497cc4db5d6ac19315a695338911820ca07018dbe67350047f29fdbc5848be3e977c4f5597d63eacad213e2d25280a27d079b42257189a0ea55dd6d361026c0bb5c443cdfc50e81474692df633aa544c8292799f13082c07c56f596387ea7a843d558d76738788bbf2d9e09c870b111fda99da6c01bc220b2f3c18ea11fdd55f5360bd884a89fe8feb43f62139be084ab39aece397e6b205972a83cc00175f414ce5e65045e4059e19929ba769d639bed85cc2dfd5601e0ce53e2d368a39f5aa53cc2b423917c7725895db930ef63b07acf61086ed0d5a06cc9c4d33ddc0b2d75a79063f1c4ec8773d6a29c61648304d539a87ef2d6f5801275ade0310a2aa618679e46198bbe6e1a82d4827a80b3e3afc9eb990ca96380e431a4d99f77cd5eccd93d5428348f2c447246b30d626b9ce47fc38a29b78350a038a2b63a831cd97330df734d172d91fa63747ff824446b30347fb0894545c032cd4090677ad280512079ec9ed35674da08e38fdd7831146a38235f9807810056f2e1148128fe322d9bb06d690b932059054a0035f9b3f65395e7205d8d5250701a435eac29d32eb265120a25df98d4a43164de04f942d7a7d113766595fd8031cb63d7a65044e448ed7227be822c70f7ac422065d1f7a58842885621d6a6203d4718b74f653dac14d1c6a166c759818fc248019105c954a4e078d8c1e88ce04d969edabc3ca3df19520b7687e0900d4dfe63344b8cce48a9b508774a15fb606e93488ecfd93d35d98a6fe77436dcdfce0104185e39268d40c172d5b2d08960bd9d776b075f2b520e7eb865165086781058d434b32075d72716a80c5d386ae0c6e085b223e2f85eef875f16f34f64b166f0cf400eaede10dbbc72e563297e209b30397f612683af1fa10df62de30805cafc9d4557fc4b5689e40b493e9cfc70f3928838f65eafb87cccbe85909e4a78800698a255288c4ee80d2cdd92b7ef40c1db52bb21755bffc1509117f5cccb0b07db19d9ad50b26b88b83b8e623bc3f0fd176224f51d1ac760cf2aec31aa9e976b89148678715a0aa69e2646f75dda10b3955665e2c4239b8c2dbbe810532c56f43962925790a5778c8a75fa859f65b0f2bab19942d8e3bb9c7088e76c1069baac42a2389c5fc117c9466ed8fc45e0f042efe0f13f44d0bc9912c946cb8e49900a72e4d04ee397fa316a92ab35cf4450b438190344ee1a25c0930670624d430aa870ebd097a35d9b334ee02700e537007ecec195f281a373381dc42cb5aba8289797a862c00e4994ec56302dbb9536406b46d7ab988a160ea892959890e8bb5501b9ef17394eed656b5ef5e6e87a5850348c408fe2f9d145f8947ebcfe85bbf9c018a40a6d5db062309354d358c58ff0bf76574aeb63b67f330fc2733ec8d494fcecd47f952a9d0417f0fcf2ed4859302ecd5fb2e479c1311618831ef1e63fc81bba81d5b620ad8dac74605eaeb744d02d37ec6e47a549017ed3ac72c06db615ac36a9367fb9eb521feb1b1a60e0b090526b69be6689ab7c4fb1f9e7064cd5cd859c16b0071b80a6cca51d69291c4910930212dbcfe2544a590660b860648cbdaaa14750c052d9f40a1c982789d57fb034a2d5f3be3f1f4663aacc9f0b4acb8ba84a87ff7fe10e129d78f2c3c8c7b170e431820313df893a7336c2fd3cc002770f335560341e22a8852ecb49a7f068408c64d09b772d3a57a6ca0fcc508828a093f9ca90c5f28b03ae38ae4611afb4302cd7f1cad1e067100adfce2ed5a9a898f937bb716d46246aba01983d1c00105009ff74e83b6c7f972dfa66ff6481753d1ca8b3aba481c5cd76f80c64ce12f1403f7090fd28502225dd57092e81d057f044f1b06b7e802e5ee8119621540d356060000aab78717eed1ec99ec3078e2ccf53f73f298acd8df3f5d7ed19cd2904103532a6dc6643d3fb35665ff84e45b9d13def58d4af11e7fbe310f6f168b7eee0f3cdd5e54be8747a6f9988f93febbe93403e27d2abc3ce1944a1f7a57611f2209c953f9a62969aa9e3c998580d301c2ece5d80e14d8dce8a5aa1439d17cb60502dfe4caac38a4e26250cd24f93207b479468c719e7c3ffb3d78125bfa90164c03fa51e9eb7d4ff83d834151dd5fbadeb66fc299752efec6562482908a4458be09205cab3183216d1321b4f943372c6e904f66b7ccfb6b17cfe09924dbf77b2b01a973a7f5a9ba21c6b6a7404910028923cc5897ab45c5cb962dce28c9df7fed06fed8e5e563396e94ef1436153dce893035e6b9f7885aaff02401fd768d2ad704b0b076e01fea5d52f2ceccf72e4ae65cb27352cb9f611b6f81ef9d0493ec3a00d0cb3ff64c1ab734850dae47050d7e2b05cace5bd42921c71276cd9526bad9093c93442b8b4670d0d127be1a98a43e2467dbef3362cc04ff413ed95fb8896e0ef6b240d57bb385ca1008848c3a048fe148d266ba375b1fc352ef8e3fe3c76f0a9538ec64520b8f2db21608db451d3423efa26fd3f3baf9047cfbdaab31a756015c7fafc96f596fa2c6fd27bec5052c12fd4a3f833f7135f2ad4ff9362ad77c0613b3eb4ce0fbdeab2ac013d0e1576b77dd21923b8e583c70ebc46aa0a50d2b0b7f8836926dc17a9f59ddff4e6efab91ce66ef3159fbbd2099d5783f24254b3031e6c6f409dddb426212c8b5c674742d89b65ba63240025a6f4eb46ed2b6208092df862d93ee4f314436657746a781b567253d86c0f6a90995a5d4e181413f801e40296d1a6a3ef602f18ac08700d9448a7f51935b520bcc05ed8973b6eb5ee0c7a00ff1125686286dfea6ea7038079f426841d67468817b941a951e163fe6f0b9a9e4677d64b032383a49291626d905152aa62b0370f02b12f3198474519da09a4a90c438574d26ac5b3c00a8150bdbbb08dce6513034ba811fc4b26324aa401810179739fe4ba72a82b604d8431dd4d2302d3e553fefe7ac667df1e036efc0a3c5ca136a103a629bd38a2d75418cdd0f7af017a9ffc4ac3c7fe4a76742eea073bb55ae66ff7d9db3a00f8ef34d291dcfb3b76a7a5f78d9543424d4854cf910b39c8819d99e767088693ed37cf8e6cf968fc5c9267fa823c69aef7857e060807a507b98af973c4014332fe81f141c41a6edd92aad41fcec760d59e9c79267502fec5766f1750d43bb709d3606a39ae944ded7f536e65e89a5d53307fbe37760e3a1fb91d7e1c3beff12289df6470954b88e9938f29ec2a9f02dd2225b7138c042cd8d6d53c2d06bb9ceb955f73656b31af272737dca00e0e5217812671819301515e5ba05b47dead3a8c727e837bd42b825195438a94c7970c154c5c7f5c1201 false -check_ring_signature d0a2cdde7eda39f195cd08d30f5ae7fffae5b6224f164f3d8f0cd313cd0e5f3d cbeb01235ce4c9f93cc3988c7f7c1fb4ed5587a663dc2f8bb115297068f1cad4 3 eec05a6cdeef77c9dc354d9da1154172174903479843f15ebf3bd7c47de9adec 5ea782f8df7ffab84adba8d0dfd72dd549f48a4f1e91827c2121fdb671ba94a6 ba14fc7df8bcc60e4488cf21153bdce83a68d628b925f0c4dd5f6a155b7aefde 2098a2b85396ba3a27371deeee6e4c8dea19124c56e3f3f0efff894a37efd90c1d8ecbc45831a6d5ebf622a98e92a577f6672d2f4c2e3682ec1fc7a79eb9650b4384a1ba84bebcc7548ab08ab9925f0e314704775b64d12da9e132cb429987057745d473686e79e8330ade45e0770e0522f0d0f2f53ed9fd886ef3765f532960b2f7a8fc8ba6dc7143c0aabbd74dbaff03c06fba5e6d2a487a3eb69b7fd6c904a65f4ac7af93dcfd2c5e644ed160c68fac346e5dab70e3fac697ecf1a075b106 false -check_ring_signature bac3763aa5d8ca6cdb3fda581efc0028d7b3115157f2436867fb0688eda9e728 12b4cca5e2d9e97fa2de3df57e7eaf23a4760a2a56bc9c6b0a3dae700b0d89a2 6 6c094d89453111393a46b5034d8af9c6c839c1149ad7850ade96cd2c0ad515a4 33b36b8f80e05b2ccf4b0610f3c5f19196cf91e20516690d2f8d7834a92a4073 a74f6b41ddd81fbf4bdb9836f292a04b675cfa785443ac1c8955294fadae3e9a b52d13a10eeda475e1f015037f092c51094c04eb277e310776cf8c948c85d3ce 1f5b77da48496bae4afa6075505e31141edc12d94b795bbbd193d41d09e0431f 7cf325e5bdb04197b5384e6e945d911b4fa2c981512e889902845fe21c44bd99 da014c88ceb75882dae24e96b926ecd94f225d5cf2ef360ef7e5d1594a2eb00529f3b8f1e8a81bb9c337b6965bdfc6484b20868fa683be58fc9b5841daafb50ce47dbc50b91eeaceadc9ff76ba0922e35d11c24836cbf5b1861462160bf1ee0eaa78bb4d5b6e0e755a4d0a093667dbe6ff69b06179dba3ec030d9b8c81a1b00e9af6182fb2f8a8e6980eb56c37920930a8a09661bd7549be622e093dfc7c6102c5f828642054a1cb11a9aed656a56571ffd6684da41c337b6f5e2537340f1a0bcebfd7a5683cb9756af4bfa2c60597528e9986ddb224d38cc5324d8293681b0cd7fdf1d54825f6876aac7eb0c9689038191f93640ba4d81f6f7dd5c89bd3f109b81e36bd72883ca90ad8b76504e1c67e7df605c62d8197412da2f12fd61cba01ab11987a3c2b20e6b0a9719926144936f0ed79ee9ffb035a87d7507316adca0ee64e5d07b40c54fba286496c9c2dd755d9d00b00cca6cf5fe433ddd4341a2002b336f5107a64697ee29ad5532826159e8ac110032fd857e9a5e6e54cd8d91500 true -check_ring_signature 93ee054c86f3977d758b21d5d5e92fbe2efed228e2ceb0ddf6dfd247ea61ce8e 36f32fd23aaafb344b59462c00dd1f46c0d59aa5fcfcc5f441ee21110ff8f6ae 12 f4b779b2df9fb7b39c144b3a1a259d14ef602a70a14731a54f90a9e73707292d 8fe4b5c2a1c8f6a84193d964a71340835d3318f7fd4c0acaa2eb254a7ebe72ef e2f6a7d3285ad40dc5fa1f55c57590498641057c5e8578476135f386cd7e887c 4d8ca80b672be349a7aaf535a57481517bb880e4f5693d84563c206469ef6919 07fc9522e0941d166e08f8ee485b5f42637034c3eb0e7ec3b35ac12e86647f6e 298ee32c2a8cb6225f474cd58fb2d273d51e760f6637fd3f2002a1c8541f43cb 48f3fbcfd035405556acb883bdbdb2a8ad12319e46f99df5e4d50942681e55c2 5574daa008f24c04bdd5261c629196dbba9d18e84feaabef80164803e5293863 50ff6b25d208de4edfb757cdcde147256fbe56250f1333e90043f140f6d83f01 80644543a68607f2dca056982d1c9f95a546c592194a25b31d49fabd1c6aed44 842173502c3b0b6394ac3416ac731e42799d29edf302344f5a726c39c3ec02e9 ddec8df0196baba4225b225a18bde8fbe1521ff1cd372070aab150da0f4775f9 121d755535d353b3746002984fdb3065fcb3c592c4824e0e133210c2bf59a3074f837fc5b1d4d1f36acf409e9cea6b623ac752f81a3287e382b848e6d9bcf500260b26c342b538357ee78bbf3c18a004f719fda4fd334ddeae099531c4481c0ebf9eb01ca5b64b285776a4350000b82ff015b66a1b8a1989dfa0b3161e0df70d084d5faf053444a93c47628795476083269d33633d7d9e9ecb4219d24006380ff585b411c327b0184b5abfef6812ac73ee10296c3533d930b5299081755d590d0e65e11e125935ec45e1ac9706fe1ae908e11824b6f0fd01a347f8cd9082890ff9e8ad16928310b37c81ec66da071c6545339753a4c8639a3935a1fc3c40a40fddc265bc9609a763ff53646bec7e9f97d445cb6e9fa3e174b838971feba3ce091bb582a365b2d9a7758d1dd37b5f4b84a123c958040c0409d606c7b2c71e4f0f960dc93e321d69f2c18ce1709b8284b2b46d72da2d37ee3b625d91a8fec3f3097417ac6a49a0a4c1d65698d014491994cf3bc78c01f51c3b03062dac820f570372bcc53f4ee76b144b5ed7d06bf5ebd1f1e2e8285fce1504da652487fd4bff079d8ac60c8c8dc092977652f9979961cb56250ae0d8d305a0521fcb2f20cc6c099d3576f1b37ad284e309369e84d9059d1e2651ba6c4af3dbe8acc0c71b8dc604154cffb8ef1c745e9b3d74e55512b33a05ca0ee227710c220c177ac01bdbbd0ac21268a71ac684de5445256f9822a842de6dfcd913b9570071e93c3d553f0d0c40cbc8a8009cbe6771743869834c172f1a9c3ac20b9cdbd70162814117ff5c055a5fc90ce7b853260f8604385563bef48d902c2f475facfa06cb6b0151605d0749f213d2e82a926814a862c9d9017d072174e61511513f676393dfa4f07ace0b66d6b5170b47e688d8b2eba07fafd1aaf045197d5d121c9f3584ff181c48ea03c1a62f9222f267f07eb011e32704b597dee6a7da7a0c82ccfdb86af0bec59c098bcf74eb567b740652d3e7947adea9df48f6db04b3abc10914928d0bbbb65a06743d1ff07ccbf77274b289331924627f0362a2f59b93917dfdd01839ea859204 true -check_ring_signature 20e479abd0e08dcec6ffb6fa10ea8440bad7220b9f9a5ffb54cc084e3add05d2 162ad880ba72d17235ec11aa9fdc7bfbd5ed7768259a47b6beebc1a172c05ad8 1 18e709f27929de6a4b00b36874b4fdc027a681d1f073058a5be77af9725d65bf 150af71409ebfcfa9929ac74ed504d056ffb5bbc886362f17aff0f3db8e8140269cfecc67854b588031a24ddd16b6deb6f8e3bd4a072ae59a27b538e23206c0d false -check_ring_signature 099c11991ce8ecea2eae6d7731a264717ece178a4ddbbf5750c03f556a5ea7cd ab877a952c5a45043d5ba268fb997ee0c488ccc69252b31a90bf6336145368fa 2 7cc484c0feb2c01b89f37384fa0370ae2d05e3de06d6a3a88bf5e9d4038f5c65 521fb81dd04097f84701f23c370279b0ca47698e8e13a4986ba0f1dd945e9c70 494114c573c6e8bf76e309702bb92bc56822fcc539f78f736212ad41a74965019fd8dc43939a0d3a615e6d8166314792eb44011a570a104b67d2fac45be2280ecb48a5996642cbd26a15a1506ef581bceba6fda6abcbffa80f0fe81160511b08a52793659744b27be50def208404cd8ce0a9f4560dca67bed0f0f923da5f2507 true -check_ring_signature 35bba00f445a76479ddef3fdb5f130a27503a56a74c25011119ae92c120fb809 233d68b552455438a2d01b4b16430d78bec863ca85b824eb29dbe73256e209ce 11 3f7bd5821a93cc7899f198e4a0723a5dc2ba3238b6aef4efc51e2a29673709b3 3abeae03ecee64465c14c370db5cb1ce7f8d53bcc9b23c4b19f748abf3c70254 c3b3c12e84a239702aeb9806c64acb3b72d60f30ed83059f9cb3adde28347d90 2aafe58aa3d258dd67c7fb05328ecc2d6022d53dec91a3e83b311c41d3117e48 230f03c04a4a810daea4cce24a87b9e4dbfae6d30439c46f4286297951a42de5 983666cb284f727537146047638f9f6ab7e44da0b3196e5151ec89c7332399b1 29a2ce43a9142a37aa29caea09a59f2671a743e3b315c8b410a24d2686028e07 59c7708aa31539c921542c516583deb51442688bfa65528a708c78e9bbf1e258 572b58ebe0fe33dd602996dd28ec66264445722bff6bade37277c869b766d68c c0117ee51a3275a918ac4ed50ca183333cee56b1afa96c839dc4d446a4f83edd 0e1e931c680049ff10f26acd373ca34db889bea55691d923ca1e8c2bacab2448 814f1961ec9c9bc07d83f42cfe858173c8796b522ad502d98c42729b6278de0355591c2298b68f62c881ae71e77bbcc2f35c1147a178e07ea73bc06134666906ed1c11a80ea0afe5cd6bd4e8880e973b3e798ab76528b0afc6c25e8ff2f2170a9ddf49fa247451057d6acf84d5684c674a00296715c807d33ab0322b57a7480386cbde3aeffa8b38de58770270120b3a9943a36f3ac98bc37d372ec5bd7c990836f72d7776095ec2a55b68cecb47e7ba8a938fc02e11885de9ecd94fa302e108058e5025c1b5cdafbc25ad758d754936cdf50dccc758f3f95cbe8ce3fe94d80140c1b5a600bbf70903ca92c10d996afa97504a536da30a5fdb6c2009f454db0e0356888d2a4dfd6a5dd1575f8676a9b17d073f9124dec2b2760e6765986ac60cf4c7ccd87db10c7944351698a0b9667a0c1d0b8031f9989e157442274eb06705fadc1d420cffb9c006d41f1334d4c12a99fc86ce65817fe3922e05df41cafd01d0ac4aae0012d3ac8b4ba2665af9427e8566b2f21a133037b3b897b29d34ef09acf4f920570101c9e8240d27ebfbcc2bd7edae7215b0e4a408ddab803d8e1f02b4974738a35a2bfcbfc1121857047e4e9992af5f78911b90ce4ad2e930de9c08ec75f9f4e0121e584678c3b91877535e06c4054caa5a21ea9d95733ec8cd9b007b25010c43c975ae98a3df5eeb8e8da754d50e19545078193a0bd7ad6cb44003a92d2d03186e1d777f72f31fb4c07e93e9381881da6220f615905881424bf204d97a2e4e936054e0e13c08a43143f973b6b7dafb80ad0518fd9ae23cf7cdd5040998c55682e8033b18501fea75aae95987a3a411c80e2d01074ca877274b850d515a2d4a82c7b2ef36f4a8bd640204d3a7ba80e7c53626781d3f228809752005f07650fee0c788fbd1abbcaba97d8783d51c7b9d7e31278288546b3bb6e7890cfdd44c9230dd3dcd1e92a159de7cbb4a90c0db11217cc98ff028d1397eafaa06 false -check_ring_signature 073adf53b98b7dcffe2a7ad02b3de59a37960219c384a5718d52f2bc7d97e5aa 2e309f3c2805d4b9b6d307487613c438d487fed595e8c7e1ba9ffd5fba7ab1e7 1 efbe1f31fea3c583bc7e713c2259b2dc6a2a1708712acfa5f28d55eb0d61762b a1d087233c8d733b01003e23483d89da3d04875667d21df952e12a4c956b490514a73d513871fe301dd7da5a7c404b0a5092ee32612d182959bdfa8424035cd5 false -check_ring_signature 4f3fda146c22b590c79956d55bf9d9863bec8115855a8418fd6fe11d124cab3a 71c3ee133a1601d19018286997dec7a8ef58760fb8ba0e222613850aa6fe9122 16 c2d06f5e918f925f5bfaf487fa61fd5f2ace42fc24d7a91eb01fdf5b25a6b451 815e9742396c225213e7bbf7ab86ad68c86378f4ba1e3a68dccda2638e3ab7de cbe9f9b445764042debfa1719f75d8c4b8b6b19fc4d0f6dc3f108362e502daf6 1e5846b0789e6fbf657e5555b6054a8dd08c82f67b0d72d806b85fb36d20b3eb 1c0cda03096d93e77ef4b5aaf3a47698b5d1e6ef80c17db0fd770f868be2ad76 4677071cca2e2dea56537122831833758e2565590d53aaa6e27c28ce3dc6836c 3e4d17e4b9474a6fe73a27176637761496f17b014b4ee04bbc0acfa24a44749a 3243b449826baa8df9cee8c862e12dfb958baf204b5c9ca1d63a117ffa9ba0fa f2b6fb62a3e7e554a322c500a3818c2de7f61f9330806e14bc79192642707ae0 bfdaa0790d3d58d2ede8360c10922e1ce82620f94988aaf21153e093bb4819b3 8fc41f1814de07df77d9da97d8550c4b8f0d6ab93f08c4779cae6feb09624ecb 13bc16343a7c83e248f978fbfa98b838388bbb69465dd586e111337d8c4b0924 a6eae3d675ca65fe3ba45f6e057ab8ebf6aac7303930edc4843f6b63600815ea 1c57c888a0da32956c5352fc27cb14639c70dbc02e5115ea593a1239dbcab19c 853e493534d02e8a50c261b7585f98ddf341a87af0c4d05bb58d349402075baa 4ec5ed816fadbcaa2e3bea0f639b4548a229e391ce2d7484b4f3dd6aa26248af 9acfedadab25622411a71da65f3672f18e7d8d117cd8419170180ba7efc2830a8e4d08d62fae35a8d50a24f46198805850018c12624d45c6588b649b7d8c89065551c014d230c0d1378a8421266d939bf18597e95b597d024528f92921fd1e0fd1f39c0e2b77aaab2ef5de56a4e504f84c9b655fa7f85122e2ed569641cb2207782ff08250f30a66b98b53799585a8f1aaec4d45388e2348be3c2cfaf018910e5f6fa915d9f7a4c74360d337c0335ed19fa619369402e5fef768f1883dd90a01fe1b96890873c0056b1267a2ad101e98bb0b97810e5f7deaf41acfcd6223cd0405c1a6b0c1086471ace4e8a18abaa1ca25ff15ceb93d921c3e81904e22c4120aef0a310f8cf5abf774f09506ac9e02668d40cdc84d2e3da8d8cea3da64a76d04da725e0f917bd2b9f70101fa5fd37a9a1a725da5fa39217ff88aa1f504765a01c7bcfab7291e90bd0cd85a9e7a8a3e036cb0d4ebdcc6e597c6daaf28dff4cd0d43deca4e370d0aa66d690775b7621fb94ff2c6458982c82e4efa531073671a0b1823c420793d59cc5ce036fa1235202156e1646b15a94a48039f842b2857280ce8bcc58f01549a17bb54af065959cd45011c22d8f7c719c300f39a42ddafff07f25a2588ecb48a3b40979f474a79a06baaa485cec80a8193a8d878a7089e190856608fbfdf6c80e58e035ce66e1e1a546abf8022fb5cc8b4ad7d01c9f974a50ad883e43643a4646879938c51203f1e41772c05c4ec5bea9df31e24008e71d5066c15c67a72659f198fa1737a434284d9bdc1435422912d50a8695e7e1c80bb0efaf78513e72d5cab22b3645d4bdee01d954c1e75df8f343c36be6c5ed1a6760bce4e0e749dfab41269e564064b5d2dd975b3d68cc84895f04de95f81249aacc7ffbc2d660a0fdb80eda01a9c616d0371e392f796677eb8435e89600f8587a007d77fc0e15c55f7106ce2223d4f10ad02d9a7be9fd7bb3c12e647a8df09927906022868e67d6cd5f1fe7145ec7c8362b7b9acbb0af3e7c6e7ed414a17523f2d04396d96416a67f477fefba68e4cd56b9055afa71741d19d5431f2dd8eb972f205f5fc57a353064a33651b628fd48e902a63118173771551d88ac9c395ddc0ab019677653709ace6fd8aa17043d26e7ebc3a47605d72502e328f5c7bc088a78a029322b32c96dde86ba111ab4a7b16ec745bc706a3da5b25440f716c7b0d88630345453877bba7404db3ef77fe5595032d263ef8af1beb0196c6b1a224b6e59b02fb0a597233376c438dcab1a5e6043f696422897a7c14300065f29587ef629d0e9ce413bdf40573218724042200662603a268b339f4b9ef384a4467c381df230e9b1ddfc1007aa321fa066997a8d5c93c916338be1d6ade8e307e49dac4420b03cba7de761891493a1b91bece089693e3d8aa5f83f17ba05329e1b9010ee01609 false -check_ring_signature 680cd3102735e93b656f9fe26f9f8d115b56c5a42ca60e177da547483251a777 f1f2204313fab5410127fd112eda66790846565c18f7528cf7d9962d549a3236 4 66b09cf390898583608662c2d46a257fcc1b83d279bf8fec9ad609c2090d54c2 b286c2989e36745422f0a9b0d12a40fdd012f362c33b08ce2dcbfb80af887e44 e6b2b58737149c9c80be0a5a5da3dae9bb22dbf9b894a45667d74f5fc49fccfd 9b61083f20e94c98ba004973ac38060cc4b6beb6760581adbe4565c6a96c48e3 d91c7ae73c1c7cbeda5671e2c902826639067b841e270196e2e031dd05cfdd0dac56cd4f99cde5d2e577b07e0bba5bf2c1e75c7c97f6ca4a5c1801e13c295d0f9363dc93d31b594ba036effcd744e932974cf425d0767801aeace95d0f463205d8e9c14d943238d164af31f499ba86a3627b03607023453d8e520c85cc976207c76421b98c8929668b776090dc7b6457b54d4e18b90d3aa4bb2adb7e768de00c8ce530c942093a0a89791546b2b3d4db02c978042f9a68b06727f7f7e5c7070d67368f2ab42ac084a7085851dc689cb519ba88b6a69a37ce970919fad2747a02d703ca4743a31be41b41a4cc482c90a2980ede690af377483675e37a2b52be03 false -check_ring_signature 34a04d1cefb0509a2e00e9e16f8ab004fa588530758cce02d09de913c6b02705 c352aec829fc2ffd821c71b9369cb7fb2e217d0af82414389d5ece0150dc4e3b 2 d4adf47fbf5b817d9e474f58ef06756b02e542db9822bd3797ee4ba12ef0b0e7 e9d6c054eec177f8f90b134c60b5aa0f2424e6204c046a1a0d53a56e0d20f309 90c4216e7f57579363c7e951ab959042f12bfd0409f5e681cf9e51c534d26e0f0f92e85b7612375ff7a75d0c34020e6fcf452ba44eec25991322920c5781300f279ea050b4b7adb09f83bde7663a8977811f2b7e01ce6bb56cf17f999f60fc0c2095a10571460b81c5b9a289d03fe4b656cb293ca7950ebdde8e3a312465df00 false -check_ring_signature 28cbcff656898d902b62b36f1ba67f64c1809b2bc5cf63c9ca47714f845a705c e3ef7191c8004c2087cfa026d9c64512ec36026640d137c17a92de6c42c1ec7a 6 c07b21b4424710c522b8e5bca568b619f77637025e91d01833efd1cd8f9376f8 58940cc506db3453ffaf9136fffd86d7e61c6c110a54962a5d72d48dc50bd8fe fe6bb04fd0ccacccd7ee3be054c450bffe7c4f1f92bfffff5e2fa68558d3c146 33f4f5580fc3bb7b792628f9c871a41f6dce805c2ae9a59812b97ff7cc17870e fe63832f19af9161d5ef1e7dbff7cb187faff3cb66f081323739f522756cf137 9ea17e91c06cd80bb72dc146edb95bb23579327eb72b0119c7a6caf992b305a6 ce799e297f283afcf93cc6721ff2dc994737a71626ad25150ca4adbe181ad90b4e844c956275cc50c3937835b53b36c8f239e7fd9887147745c4553203ddd6078151455c019051a73a7755cd0657dadbf367b118bba94054981227289bf9274e75370090e24f7628d162162b1370c31eedb7d5c6c22c4d86375d73c24376330f5b5c4ae6881fa9564898114225c6f2f92f6fc09804c4635563dc6bf59e71c104f584b0794485561a35b3f90ebca65167fabea5688bea9683621e5bbff97e1b0048718aa0df6d6d2cf96c54a69aaf2040ff3cb41ab9686fe8460d19340873b3091b897e3b34a3f1eeeca0ca065c2cb77e703ee6f2e628ebe8c5f29a15ab175c0a093696378ef36f7ed75ffd802337f7816fed75f1e36e7770289af15a77cfca0c2c0789e79d659f304d92b922a78ecfe85da5a16b50edeb433351fdc3bff7b900e0089294d8eea252dca5c6f2c9115f53fd251eb7ec3f4d1e9d35818cf4eb4e0194d437431337ce64766c8edaa82139ef937d66340df671a03e2ecdee701b540c false -check_ring_signature 3c3c359c6146c4258b1d0691c2e0e95c6dcf920ef84704ac81563b2bef8d318d aae0574fdfb7008d65ca33c9cb9398e985a2362770d66610bbdbb32afb9f9457 194 ba3683878149012f1df0f9b0ac53712bcfa18c01b38655edde95c806ecc287d1 f7441cbcfe944e34bd1a531db14f2d960d2a7736a9c5b87c69066dfdb015987b f6ccb21c90676ca843371e6b3c57032debe5b4839c84e3d8ff4718878dad8a14 cb3a75af1072359a0ac3e8e16d07997ab3d23a3e6faf274805ab0e085b0b45dc 601a99f07b378f1c50011ab7b15040e74603a5d34d0f525c7558126b8c0e2d32 dd9e40fbc43c0cb1f4b7ce7fb56d518b15c10377985e1ca7cd70510f48af63c7 af27be689d3737fac749ec3b1eedc415c47755a48846eb02ff757e6a836d1982 23b44827876305a49928b0b31d26c332c06ad1e502cad8aa4eb5fb8bb43db018 e44cd0663d31816a50f022ca9b854e6bd5ef67752db88fe07723abcb1b8e99a4 0ff209ebf37613f92a1e10d6cc5a0c4b16b95ce0842b0b8656b1f440d4ffc69a dc63d6185bfbf416c24c077f5f859f9688af226bd0e688db15d562542689aae1 e1b49f21aa45d15583c8b10f2f5c25dc9f3cf49b1359bf9c5716236d58388835 48d9f0aaa512fe25f13fdfcae4f8c411a9131dce06abc0901e00adc08ba2dcf6 be57dcb7ae2b8924e04ddbbe256b57a5ac9b47d75c1433c34e6662cb2195ebc7 e4dad6626fce8618a3d8bac10730ebc51c6995a11e58f780226ec96afdec20ed 98bb0e2e65c49d516ab20c7c5222a25b3e394379a19d88d609e6e5203c9c2343 481bdc221b133d71571ee88f34c2c85a2619844cdf43f6c128e1cf3a7fe3562c 1ab1b78c1ac099c14c3e7c3cda67d03dd30658e1eff43db68ac70161dccf3328 834f4614b9354de9c794ef3e31e01d8693abac45e37b8bf16ff25c522c73d7ea 469039f9152382821246a90559175cb3ab207e8371a8bdf971f66bf4f4c671d6 26be43ed8d569fccccd0a2055485d3a7596f4f9238b70511c92cf22cc4558d41 a7cc0187226635a9a1cd5c33cb4476af082336b589309f0fe66b87e486fef224 2a2d75179e56a0a914087f331d7ac216badef3059651f4f2a9cfec78926596b8 09ac8acd9f7cb7dc54d8dcf048da1741913194d8595ac3f11c90e2d3c239171e 783333cc3d6601f6b36ba24bf9823a25fd721c7280ebe7a5c48d5375c68a79e1 64063763c32020bfd18f43e743d96a1a1408c9a0cf19a0b9b9f1d37f2181d914 70090c28d3c6c680c39871eb89c99c39d8e2ad20e92cff32bd1cf5b01e9a01d8 c505f48220a6bfae492381617ff8939ed8df02a05f5cc066db72cf2c1bad0b19 a1cb2cdc800d80858ffa8bbc6689a7ff092baef9fbbd8a895473f20bc19796e7 4628d547eaf7014fdf586f6dd57d6531a5906ddb2f350451e9e148a87fe98593 1b01a742f8932d7e58608501f4304723f2207d6f348cd85db0d81e98b32df486 0d631b24d530c30a08e8e08b2b01969cf9211ffa033027fb348124673fa4ceef 91ca998f166ff09ab8603b9cc47a2b6d6ccb76f1a4bd07c0f46542996c553f9f 411ff0d6cf362df01f2e59c5d52c13b0f26da0f8e53e6d60f363a61b1f5debc8 f65b57b8584198dab1032bf37bd477eda1714385223b135e001de737648ebfa7 89934917c961e7532a765051cf989444eca94fdae9e19f54f447b0b651828efd eceae7fcc3dd6540ecf30d7ccd8d80454a747a814813a6e7362ab89da1048481 cf3ec1d67e35fe7fdb090f57d0c1986ac3c13b61d3e6f238045cef9ab7fb1908 079e0760e2543cb662563bbee50b13674a5d5695d3df47ebadb411a30adb1604 c2d2acdf15bed5a526906ab3eb23b92172c9c594295b97d632694498125ea9eb bd4eebc488f185646a44b13af82bb29497eea4ac30e583d0f2147025daed5348 ce952aecd974865b7ff922d688faf11b76658235099ab4b7ca9bb4b658227254 97d9499281e26fed656e9095dc18529b468d8143f2b5fe9bd580104b1d65e49b f39e4a22c6af321d973e5112ad5a2e2181b8c5009e9be272389c16c1c86e18b7 062e8d291434ae5ea121e4bd03e3fb340880e5baad9db0b9e55d7cbdf9c4850d 76b45e296b5c43a114a8d431941e3aac81179d0bdcd2158b9d250559ad837b13 1251583656a6542d819a74845efce7e098e8531d2c188e7742d83435ca580e18 65168d8a8843eca5655ebd50e07239f30b196fa86749074ecd834ff50d3d8922 ceba4a2512580aa5bd69b5eabec26b70aa8035f88bdff9cbdee79dcb8b22a67f 786b7c70405d4ab9fe8bccbe336de56e96540bef64d2202893dc0b02e002d60f 033fda4f260ed3c866f22dbf3501dc4ff49bb2930f15bbc653d8ef0667f799db b7cbe04f7aaa29e58608e88809c2ef95dca09a9a0fdf6e605c40e38cec51046a b8c56e3a26ef65d48f69ab0cd22c71f227614055b405faaf1460b54fabf63635 655a5578034bce715a2a3fdecfda45a31cf7a6f07115885741f8a4fddb126956 7680027587ab0845ad8b53e4111151dc75e2fe999116fd4994f543bad0e15495 4f8df475ddbc3646deea06836bbe63fdfab727c67770446f3f2448ef3f2e8807 d49e3a978e39c65619fc7a7a8828c510173cdf89e2b229532dce697496bc4220 a451a61ab75e9929a100577c83e97cee5962692020e1fab7c669bc349838bb6e a271f640a3bfd335f8c035a69096f8cfb9339c0c91295ba2cffba8bbb1ddffc0 5338f980900787dfdde365bf3101bf7e82e88be188e14a146a4b8037abb1cf6e 19448d3aa32ae852ae49b8b3d77f7f7fe972f2aa77255194d510fa41f5d5e2de aa08240621a2e6029d3d0af3cd208e1502e6d5de0b1a743f562baf16b4f17ca0 5b441848c538d3fe6444ff078e95f516f57d44774b359250a5b08a73f9a889e0 5b3d86286128c46743285fcdd1c2e272c1ac3277b2298503e2206ab491f72257 5933577ff549cac90042d4dcf94c0502416036dba256c1e50bf0ed08cc6c380a c6117be1edddf3eb6fbf3264587dd498956dbd248e53bfc0fa385de3f3a099a7 e63de9d54082acaa1a487768b2c7ea7d3c0b82beaff8a63838f84290a4c6a1d1 8db1b9bf7d65f655bef1281bc30287333b4b271c913f9f6f829d54ab5f8b28fd 35e947f231c2c43c9cac0bf1c6f299ce35076ee7bcb0c13097f931b881b80e79 2f1ef3501db46224d4f06c3776a69c24cfe94fe1b1c11086aee53d76e7b9ed1c f8e75e54900dae399414170fa68218eb53cecea287bf70348f472a1e6ac8c7e0 38c1bd2a5e331c7626dc0bfca7817cf9b8221006cb5ef6706b5dd29b60f686b9 d253de65140a594da678539cafb4a7edc96d186618505c992ea62ff42a5f3d07 0a65d330894826b258054d270d689d334e5c9f1c2aa2c74995d7200021154580 a328fa7fb214cf2a3d59982297deb3e170eb824adc66843cabe5c376bdff8dd3 ce8b565dffb6dd6f6d13e631a0851d71b4c710789579307bb32eb83655e67dcc d6f543b91a34d2c6f51013ac9bcf5fd4255e4e8982e0e9b6becddce89e3525f1 323690c5fc0166a2302337e21c600757045e7bb0dc741220fe38206b17c23934 7b467c78aafaaf31bd84f0f04cac7901bc34934f36393586196039a4ec77764c 29cf35b7773f0474f6e6ca385823a495a46d5f440288beeca78f66e0dbcfc60b a24a7c9de69c0b6d0979714cc9ba1a4fd6dd6d304135d2f2ccb2d6293665dc31 4e04f87585f9e5f76fa3ecde972f05fa238515fb9ad18db46b9a408ecbdc8603 ba60304c9bc330bc8df853008c2c26621c375c61d06ac30d5155e73519b6ba03 d0701d7b1234385e9a0370c184cf1cb348699c3709405190fbcaa999d366551c 56c7478bbf3c52c7361bc934d78e68c2fa3ec55d00e35bc21dbe72297fb5c780 5a1c06074b1ae2fe8669e89e4b548ec9c7ec87ca283b2a5f0fb336610d0b0897 443542063ed67d8f10df5576f34d664dbb2f78609ec617e0f1bc9c8ebfc41ceb 2ad53d8deb2fb787766c922105af9e784d51894630fb9e53774de00e20952593 8a6a53036eaec56687faab31ede2876d79a41940bb208c5d23a3428a7e3b7dc1 371595bedd1b3f3cab07b71171062ceb131765a000f5b45d2f1d185162f9c593 3b5b21c7064d3a233ba4a6e3f3315f39a9270470a6051627e7cf42492b4c4f67 a4ef8545e34608502e52544ac1ff69a0382634725dcf422c643893b3e4ab3157 d6d15adfe0971efb9ae575ee6dfe246c53755550b8af064ae1fcd6654ff76677 a1aa527bd964d461df66fe80bf48aaf8ab0bbe72d5c5ded9ca3085e49c65369c 59ac00a7af56f60077ba576ff5f4e7cd69accb1583564b49fa1726d8d4b6a7a8 c8a01a7129538488f2c2b4130660ed0b12794a9e60f57c61cc880705dfd3a662 65e194f0a485236c203be2223b602a3ff0b5ed24e413d67670972312122e0eb5 9e7ab63f43ddac9667b92afaabfe3b0ac19c669f263fe23c4603fe380198b110 68cb1b57144bbe65a99f1385b6c337309e8d0289d729ce954c03991e3e769abc 29f349d5d90714f244545c31f836a2d6b45e384b19c3c456b392ce61f890b3ae 5f075d99b85fb31cd5b8cbe7f94a3d67eb41344425c4b580fbc12e8b59131249 c3e1c271069e1d160877cbaaa79586afa77cb75278672cdc80b1de748a7aee9a 53e1a80c92d0d8e90bae9e06b4f4c2b50cbcfc6546578f937d2200444e69a94e 1f0dccccdd456af5e2b7d3b448e5a8505f919332276c38d71c1a30bf98d36fed 2acaf8e392d328e7220dca0ec0b89cb9d6bf76fb4b121439830cbac9d41d1466 63b002e8e2d9e7c2d6ad572e2c82382f435d562223ba90c908db390b23595142 6368a0e1b0ef95785291f0232f60e9a575cb8c94269c3d7532b1efeb92d1cf46 b2561b47c05cf194acb8cf10a87e0b13a8a93f8cd5ed7627d0d1c655d37b08ef d3ee4825bf238aea36100c7fb41b15253db3846b3f640d35d47ef862cc8762dc 907c60b2bccaba1bdd20af1799d3a9c5c5d24554b5caf7fd4f27cefbf7a5711c ee51109e458c09853f9a71782e5768ee66cc6a54a00f404685c602dd3d8d2295 1278bf718c437697b905a4d7fc96cb7c4255f59c16945d8f3c49ef30948216b1 5a489e26bdd085483e85a49539b8a87f9049e0ce0da919ccb9ecd012a2b42553 2db92f831422eaf94d9867dd06d1d2fd4e25657d2a8fe0e44be2500696faf538 65b4470f2935312ebed508238d1505ee5624be19a840e9c0c0a9db21478fbf2c 5164d0cc50e2241bb58b23dd89645c457cb406a07f13c3eec4a72063d4b110c0 1718cbd094d1768b6f0c7573924350dcd53010b23f77d669634907c680d6e30e 0009e8fd8ba1f8230e29ef71c71e2fdbc95cde980cd10ed3977bf0d16c3c6487 17d471b555a73d7b9771fb9158e3b55cb59dd7bfbae0df010eed42d197b29370 0ed7a381e703bbea3f352175d5c426db74712e705e65d7a6cdf3e663cb173f0d 4a10f47ca743cf3b0a5e10871666d9412fbc92cc698486440cecea48eee04097 a3b3cec69a257d3a2c108c26782e64845c074aab9858def3106589bb1bd89ed9 c7045b8795f821f32bd61ac0e96280a4f2c0a42aa5194e556a2aba8a9a3cd60a df1867f9b2e511a949cd7535af1e43767dc6f609f315e0dad78e93bffdce3353 36b0c9bbe99e1887dc05141d4d24a40abc4f46ebf6492c41e5fe19d88abe1160 360d6bbb6fbe3582f70a7033b0230abc5a1f3a30938ab5e79db06c8c319134a9 6d4212b3350f9becb09f53d4a2a00ad17897726ccaaa6be16168f087daa9a031 31a666cd7e545fa15fea1a45aec7f46cf35d032a3f857e63466a9e95ae24d4d1 12f77b62a8ab01f581e13d238bbe0761c23c74e20ba20d12287f271a76efeaa9 958f571d16ca233366a1e2b4ee03c631688acc669445b52f386d4cb1f0a738cb 04b8001bb95edbd7b1be04424f7034a058e579ca7c828cac191201b00727206d fc4814f69d57d807e252a4285c50ab878eb134a8c701cd1a34acdb3cbbf56931 68284a7dde176bd03fe1894078bbd31b4de1117f277e20b52c36a9694a89cb86 dc146637b87dddfa5590ccfe4d4442ba814c0d321292eada3a6188f9f54e88f5 9e44ccef07079f19374a9cc85b1d9fb253bc6f05f37eedc329a42707512735c3 8713a27200f305306870485438123cef976b87b26027cfda479fffbe0f42e6a2 080bcca3baf819129c9246cd0ce6e05cf386bcf609b5dbf371a96e2b3979d8e8 a0b2737c4dfe1ed705bde14a2cdeb4681df0555a9512f4f2ca967577c22d4083 4f688c3366a9b3c6f69ff3b3b3d796db12ad6e4a6811a15c7b5525921512e675 e6e13358b15f970c767b527e7e7a673dba8a5dc25cd2ee84e581bce4f51c3db7 d2cc5d204e9983217ab7eca57c81f2126f09182d83b3e234acd9dd9e38fa437c b8616aed56cfbb5243506392435ad70e572acd8e96615454b337d74cdb2b9081 cb837b42e55945ceb67a0cf82ab86b7fab9d0fbc0411498c51f7424fa0b68661 4528f3eb081416ec0af4cd2ad5c38f9320b7091bbc9fced9444e375187e9e0b9 88f8cf5212f76ec1e679e7b208602adfee276fb42473d5200069738c160b98ff 111cd3d46f142e5e7189eaafc9ccff902027366c088b4b5b10d1881246be16a5 55394cc8d9a56a7c8102046d9b8a5657144d99c3e40083a022787ffaab4b4389 be5f5efcadcaddc5266ba526073115f2e6d4dbab5af41f19666d5e5fd7e5730f feae4e87f35d1a946fde2be6db8ab9a18051b840c74c03423da0b9e0041ee920 a5ffac3e5d0ac0ca33107cedb40e030c48259434270caf7981e946bb5b8f72d8 d1c5bee389eef583884c65e54500f5d8f39a02a8a6daa5bf21ef2f2abb7ac056 dcb72de20f6ac01d03b62534674af4fac5d594dfaa0d9b87fb3c66ac3694b29a bf2ccfaa319745034297db9915dc294c45d3c01b0ae2d6c072e53829db83053e 1dd306bc238f92cbabf0a819485d73faf4765a51c707332a26ae6122250dc132 82ba79192694bb790715ba735b049de48f937cb777a0d142a38a34e34a30b775 d17ef1b946cd1bc85e8a136a13f2621593a6b760bdcfa30161298a5d0d972085 ff1be763c9592c8eb15dee8140c398c11cb5bea3a810c08876b96c651fe03cf0 a3342568f1580990ce79c3c79d2ba9a87ada139077e6af9e904fb99fce095272 b0b02c9152b5f50703a7bc0e28d49b49ed17fa555ffed3d95565bfbb324346b2 e7d0db7a473ff473d7686de860a122226659aab9700a29096aae93dfb3aa1843 42a9909604d56b6ef0bd8962459de878f7c8b066721e2a1d0c4629d077de90ae f72c4b169cfee18705be0d48ee0c3afff0e511865dceca9c1f9430d0f14b709e f1568c18b07c633de3e330bdcb9597a124786f896e97f8ed72fa4b32e39aff9a 7110381109418868cd913beb753c9713180da491ef6d779b63b4bf199fc7f553 16710e4e28e4afc3799d5fdf33820a192179515b0456edd8e47000edd744943f 63353ca254624768fc94e95c669038852d77c9e7f011d36163b359b7386c17f3 bdccbd8f0b28a767f9d6e9805a1ea48256fa9f5664467e1dc9923cbf361cc027 c2a0e973993837c7ec8ca3b649b6aabe21401665e4393e302f0e386865af3cb0 1bc60fc416cc931eae571b958244189d2c05763c6107b2e59521b448587ded13 b51c99dec74d12c1c69428e0e41f6ce07380dd9ab3aef7fb76abe45aa45b47e8 6b00ab63cebf5ef419b1b3fc29c05ae2858208ce5d83b659abe03018f70f5f47 7763dbcdb8838d66fcb9aabdc76a089730fb7c679b6860145fcf297da4917c8f e85ba830f1468c936301af1a02f6ec26c2f9426c3088450d969294e823de1c0e 70a212e693ef8ce1e2b8e51e5665e908c6687891f3faef721b92bec573111d54 681d64daeb3e20c5aac241a15ab41a077b0d741fcf78688793722f508fe39a90 7d47b102196b9a12d7120f7ed3dbba1945c017a9d987adb33ac7fd7460f10d32 c65d05ddb6c89e0b45ba24ad82f97788cc3b0627094d27abc5db9b6a406b1a1e c7f7ead1b0b6943eca2f8c1f76f08bf5c765d07b7e4fd7e47d7c8e8ae26a06da 174508d08a45d7b569feeb42f9e1e9757b232a26847990bbff79c803c14fcfb1 c030cc288326763937b79b2579738a98285893d51522844baf8cb7cb2104242c f507e371717b6a5d91a394aae137fa994e0ee004cc7b41d766d6a55784180c2c b086838aa57c0d7b8b96e011a9c239f3a17552ed68751eaf9d61339a58114a61 36f8d946e3957d8fc54eb7b529cd196193f2627141af3a252caaf2260771e169 4e8db9b219b0acf79b1203d791106502833ef3410d77edc46b9f01ebcfef9078 5a4a03e182c4f5bab772b5ed01b28d634017c86f0b80d39bc860e84d4befe3fa d0fcda2fdd20ed8a6f1e4b98a227b04986920e0593ce1362fcc550723c09c100 dce4392e6372c3e87c7763cf752bafaedbbf0632105e2b37a4933f43a9b1facd 439436935018cac8879283346c9c71d38b11eaadb4248945d07abfbfcb252109 76fd901852fe345fe03a47d9e671a6445190afbde9a2ec54ce9b9001efe7478d c5617b59750e971c8e05c6e7499627bd2db7f1afabd0d2b70f18bfe6dc8a63a9 9d074065d7d557944df6b0d1792673135044c5ec1501bc312509dde93f125887 82324afc5e7aa5e38ca5278812f58956db995776ef32ad76eb4ffc171668e332 4a74dc51eecff48af4f8cd78506a82418bea4da04b1d6cfe6e26f2f7bd82ee89 aa684dd6fc34114e50d14eea83240d91f77e419a1d7f38a423b02d5fc1db772a 5502be8ccd4f0653dd0baf74fc5ed50cbc2617699114e2574d802f4186b60101ff37eb3d9d7ec35061d03bfab7f56de86d1be06da94a692e861a148cef56bb079f25b2e3f4ddd0cb70b00e8d4c65813eaaf96ee10c90f6e7c36c7ceb41d5940fb9aa0870b93f3ea63685a47f3be6b7f6b7a7ce67a3057f07ea039e7b08ca63017a616fcc7dabeb0cd656bb758d26e5bcc3315a8e0d6b90f8cde90a16351db006527de53ba92941be331432ab8a76058fb397b10a755c7b701ec2b150650ba10f3798bcc6a77780d1a24576424297daaf9cfc4397899c0378d88afe6cfc4fdf06033f2fe5b257a63c29cc7b1f8b6e6d98800df5ada54c18daa32ec59da152f6031508a374a7e84babf4d6afb1101a7d694ed9b7b26f08268cfc0942de6b98260b0c37a5e468eefc582a9d6e13e994567657d2681184c1cee328899b876fb1ae02a96dc898e0aa4115fe6de019167950d4df47dcaaaf7f3dd7309b441b8f53af09f8f2d1645f3e5990f985ec741c11a1f280bfd76141e161465dfcb3090854f10bb6abaaa1059393e38d93ee3c050842c725e68de190b50ae186faf65d086d2d0e698416056ff7682783ac5e7ad2ed7a9ec8029d6ff90a5907ed8095a02c44270bfa4a34c32c1cd901d0022b4a3df474415dcf793df3dccf44ed9d40a7cc61cd0a5b71c983db28a382bbf335cd888b028040b00b06d56d546cd2a4c0f25c02d40fc13b5e00f76a756e6775a0f9ce467c852b25b2bebf82818885a5d8ba0d564f0ff70c62d54bc3da4a0b6a5d8f8cae2d6f04a381b15895bc47098cc0626e94db0dea9a7a757158382af27b30c36d10180d721bd2ff713d1828f2fb08e73d9dbe088a6fbf35728ab504e21c8e1f03b751f0f0bfa1ce6a44656ad8daf955c1407200be7a2a68527588a766418119aa7abf5e1b7d4c45f886afd9696e67f2135b7708239319c8ff7240b8ebcc7330025ad611d0f95f97f6944d0e5b4e8658caf7f00a2f2e342781f66c0c1238fb023c0142f9d809088bef40ed1fa47b86a33eac27016adb774c15dd51c5359d9bf70eac10deab98c52de7499d15c02f41ad83e6d30dae41a566d639e57d6260a6b953a864b0caed8297e7c3cf3e932ff4835bcf5200ef36f795dc3eff9604bf3dd20767f75afe6ff935582d3d1d2ed83e27262cf70bcb4f9b44e43db3ec9a2ae608f18f04dc74018d02d0f7fb2d6d4e907efb36630720cb5e08d78a3903c57a90f98b8e7be8f57adf7eee469a4ba35367d9948d7c0c8af9732212c4422dfa58dcbb4a50829e53fd942eb3238f98fed3bd2733ac8508ac0196c07ff6e855f32355504594afcaaed952a77894cc019927789ec7915b0f3e89baa2ce5dc28b4e79f4eedfa733cb7781d18cdfc25b63ed4e2c16430164033a8267628ca008b98ea06696b9bc682c73dea7f4bfd81e931d17a19149b1810d63c954e733ec19fccf3f262fa7398c8824ab6bd30900205707e757dc3d7af90dff1be6f8aad591df6ebef349f6a59f5961955aec04c5286f84c77f920c0aa30ae3374c31288b3570365cbb1e8d5c2ddedf88fa1fa8bc9f28024433fde480e609eaebf51c5c19b279268090d6d015e460a07b361141dde9114b6794edcfe75208afeb2a6432919edaa716898015d0468b0eb78230c1dceb7ac351c107dbd5a60b314be436df901318f0558d78d854ed8da990c731087e0ff2a18ff35b2f1fb301f544633ccb3b3a3c01b8737692477815e114e278a0ab6a02d529584ac941b5087ab8300282852dd9f60353821e95fc22840d9b9387b239b46393df21282a1f0814bb8c0927a6743052f3bd741cc7702ef056088b56e26fe2a642a1751a404b03f04d879ff0aab8794a9f635cd3515020a7c7db2ea2828f69a518cecc9c44f2069178b68950931f34a9b0a9293a481ff0422b5fcdce4e809523067ca89972ed0e73da3b845b95e3422bba3ac6c5b4b4cffda864050729a07f97118d917321ca0d9c0b3a83c4fec26e0b13f58e5be244ab09f127c8f09c8ee032fb592701945507c16c65fc79f6adcb4690874ed6477f5e51716cfd81df71ca7212168fb856b50668c62f95f857c79a8555ca30887069739186fad3986171d2d302a250f00a9d0fd506baf3f505f5dcef8fef9fe8c911651c43dd5090d0b5858848eb36ae38420fec5df7b6d50170dc11e1032164b666f34d33c5453980fdaf5b3b668988f1040f7942c2086990e8de70ed247781e71512e402a01b0176d931888d42a351a97a09db8cecbcc48e20af2c56af97bece403ce7354b8b21da6965d2a7369a49f31c03b3c72f4aa4e9a71cd2cc8584bae22e02aaf00206d1555d24a04178ef60ffb508127e9fc0854e2fcabf5a1e804ef50b0e3693d3fdf64f1d1eea30b1d86b25470caadbd4e0852c1506ebb484d77ac399b3e8ee82850c50f5374313ae295b9491040c15f7d1a65636cf78dca29220d9908c83e7bb581da65f95d7bd5a187949f106c9acf85639c772d485d9efa5d8b60edf6937a90f9c56975bdba21f058f19a90ec6670b761f75713d2c9a7e36468120ee4857049fa18270f384f136831ceb1207e4dcfa3360f256fac846b86e0ed5c957bc3d77b42a8963a52d5bb53978e1c6069e07d033b302e5a30824d81a6b9d2bffd1aa02fba3fac6572086c6e405986f0e54cdef44826bcc0c4c8979450f8ca62420226e362df2a2519d28b7e880a9e9052d911a7870ed0cfb8e035c9874afa26c9f62bf9e23e42c457170cf29b6901b0ef6ecb3b1b9039504471d77615791dba0baa996fb8451d1bfd676be313b0aab04487b2a8dcc934529c17e51de008a0fd46b7544f77d5904a5280af60e74a21d030d4c726644aa31eec92e207486976c9690190acf29713643829973bcacffd40cc49cc35fa458373c7f33d5ef57d2323287c72d6b77ec31e966f4acc909da790d8022655dc7b917eefbb98b38f7a731b0addc3b73d62fa8c9b749f671937b210ede833e92848f9711d882ab0cb2e47a131ced528f9e57e9fc0ed4c5b6ab044c0edd691ed43584b9abd789c70bce69ca3b3444fb2a06b87f9db7fc530c3aa9760f4955b3777583107abd4581fc9dfb25eba2aa753d463166f7f71349b10a59bc0432e351ec073f89059e2c95a23d6278ccda833aa5d02db76b5b6e8e2544f78f05d8e144e8c999b2671d18d0c6e0d74a79bf9e57582e295f665c3c2b43ebdd3e0083fa1e20d34587d0ff750beba6751e41f7444b2ae691ee917ee73a36c19030027de74e95cc445d571e93180e07c3e56b858a980172f537d0b8f6df3b23ef8c0c6475925f6c260b9d137f314627973db2e4e29d816e27b4cca79139751ef88c0712f6065dd82ac2e442663a0779b38a04cf8f549b37b08711da2b10527fd1aa08a4753b92fdad7cb8d6c834315377708740564402fe7d1d745cbc70fa607fe1049c9761a5d3aacce669486443c6e690eea4418c2f895c286348d37e6fdefcfd0a6307d5884d759622ac55ebd5718558e28cbb68e8af8ca0a4bc228f60147ee00dbb832fae19e5c62b94903ffc541cbc4892c558d208756c1d0e057717755779037085a766a862e7eafb8dec8f29692349435a1c9f3bfbaa8a690bed7493701d0dc9963ad4a8b48a18e1d5ddafc7438c7fbf37b79a70e6971255eb3a404db993066d8203e89f8c906257a7c07907c8c7e17c78710f250b15782ecad12e8e40be0a47f10ebea2597b0b2840d9cb626d67f8ef1022ebf2d6f1f9f32e1631f44c790d27543abef9fbbd035ef72184656fe7639900a696e0ef32b8e4a0b05284038f0bffae9e0d6e209ef31d0e105f77acdf546d1b5735e54f6fa66e79ec1b3247b40b33108f98789b0c242b9a24a0b1ac6a75a5812b7a937babc2a430a27a0febe00a8a80acf3b59e9179464c15c44eee068f4ab385de9fd87db9a7c776c96c293f04f4c9dfa0de2aed8d6f46521c5c0b53cdce06cadb0065668c59cdba267607b00e9472a623588ff02deab9b55954cd3d0b1c4639ad9ae791dd94f0f7763ebe85062779ab6f6d0d3bbd8414a4a570b1e21935d30dc600fd2b7dd405f8b0df0d88080b1b0f3f0b13992731e7809615bb28b4f91c9a05ebd3ae59002f0f8ae560b609c56b35bdfb5bfb6e27dccfb5cd330976095a40758144db042a6184b6bea6c70084bfdd52d9fcbc06488fdb197537b7de66486b8577337b6357a03abf8c6314034bee6bcf3ecb45d80e10071329d75b4c2a1410aeeeb1de701196b9a656c2e90c0c482959dc77e4f7ef5a46b53549cb0898dc683231fddea062a25dc7700b190ca6f39810029d30ef7a2057718b1f2997a26b918814b86703b7e833c805c41f0080ca6f67de3178cf906d16ef1b15a7681c2834cb2f4878102c59dbe4203b9b0a9a6ac1d48dd93245f3e7ca688abb74d5c16a9d78cb3a6184ccad65a3f8271a09206b53343976071c4fdbf635c148bbdb0d383dc0bd925b67a374659086d1c60e777457c69a9eedd6e2a5e61fedf78f9d6c612b76dca331bb07ac1e6be1566e0e2f635ff5bc18d2745d562eeff0657d59839fd914bece4ee718606fa2cc9b1106fe86c8fe2045b10f607df2e99f46252858deaae1cc600e7881a2543bf872b60bd9cba59164831ae41c9e906266a8769a4a2c6fb229d25046baf0fb28e3fdac03996084befa9d6d518eff49e46f2f69e7b415bfa9a725acd6bdfa8a348e9abb0feaa3268f43886240b88b8f04062a5aef153f3a4c326c578c597d4d2d18cb8d06c7a007f63f126f0cb68e9c6af2d3ad5ec93545a94ea7946a0e85fd5052c0340165848d270d9eb5ba742365836250aac169060647d430bc5b0e25fccaea5b0c0ff8cd38cb385cad37fbaa0bcdc5a54d0a98cfd22be5591909fbdc1b11097af60ae3fd060bc03b538e09a219068ae703c395977c8676d16dce027cb17dc3ea2b0a125d12f7616b1714496ecf6ad1daf50f432de8a0c2e12c61f06c20197f9f610a2be9ee3d38cb8f3263905543943d8a64e78f1ecf222b329ca9138943ee68550c4e912b6333905c4903988427a30882fe1958c9a611d8c91119ff5986858ec70a0142f45b838097c1d134223b0d09b82a3094fa7314e000e1eee1331c9d99f90ca37e8d0d2d163a9019ebd00eaafe2aed5b92075be8a36a5aee6652688af9380502b5e55c6db2df3027e65906c1f9948a87d1ab0fe36dbc487df4ee86c760040d5d97f120955675ac4d340a51f7ba31680005b45ce2d2a5572e6b35e55a0d320ee34343836a938b012527069e3dd7812ccedd171566f4050405ade2e9c122910d72960b0021004f2e29f1bccfa721c6b6c7c41ddd1141c73bdf42b5d7b114d90844480df31149f5f38818e68c94f602285ecf1a39f6b38375b0be3884a88d8b0c51ba4e3a5762c97625f2646b80884ef3a0dbd261e5f881391676e30db3eceb00714ed201b8df30a6ed7e3a73ce901c21c75e065084e363532de97d305cdc40044f0ecaf43a8d7d4ec65a306a3e7f898bbab46e21f161c7400662b76857cd1c0adb16b0f413e0f667c0250c86bbcb32774c1f79c9cfaf1a59807c5eca0a54070e9253a681a3e7702405dc551fafd0139726ba47098b500912951544fd5138a5027bb1344b55bec740ab4ddc2caf64bcd348af1610f40c9c32df0713e70f69e40f666735eb8a699ac2f85a69dbede67f3e2a63df1c0b34f755ec4f0ab5f5c52b03b132135678d27808941a3410557fc575f262c400d993ef7392e456f79811200cda93db98440a3003ff40699c802b5ba2272eb961093c25297acd81bd6845d00b749075b89f5ce564380847c5ce3bf70d61ed9a6b51708a0541d38581c4e9e0044eadaffae3abfc60a6be799b747728bd15753f645d58366c87f237c1c70491011c2ae22cc35cbecc8c5e556e31f46773a8c970a07454ec7dc77eb4fb32dd2f0f7648afc46a4cdec627d726c14cd911b2bed1a79e3cd27e40c95602de5eac4200107dd18517a18e980b1799266a96f08430a207a427d2b70ab109dbaea18e83051e664e7a06395431d9f4da519d9ed662f2bb4bd433464cbd2a824cdd05c4b200d77c4059fa27ef676c4b69d9d9de96fa227a794c10c3c01f3e4008f0984ac20dae6d49ba82658f158a1bbfd367c45ebbfe79180dcfdbe7b59ba91465bc66ef0c4aad763785bbaef4975ef16171ce3e328b777fa97d7512035ccd25aecfe6380472750ffeb31eb0816cde3573dbe13fea48e75f74de28833597bd9a3916b664076a9144ae5c5d1235ac64f76f5538077bd230cd62fcfcb539c5dc3bf8d24f880a3cb9c62d6616ff53ebda70bd1ddceea386b8986015d572456066985a8f6b7c0e6679767a79c8cf074dba5c7161bebc62bd88e5364406c7a957508ce0a9134607970ed7a14955e47e285bb7bfdad94083d3d69d75c737919f2eca055ed158fb0a1bcb6093e7842450eeaaef71402b71d9237a9f8478ba74a35695798dccbc870631616afa317f68a04e009fb2f68c810ac608def1674a2869a286b29e7d9cee025f6fc7fc18ee6b029e169a60854b19900b398198630c2d634b7bf1eb4a9c7b084664b5f35051d519b719ffec21c19e41f5ab6d21bf7ddf7b66f0d8ed07287006a5f12b49ea836dab415c3fc859ab91566d71c3e6cbb0b143282a9929ea2c440d8131f70438e09d4aa26d94e67bda9d6f48053d539283f6c1ccc6e11193415e02019a8a11a37a01f4e346fb20c699cec31faeaa5253f2d86e52862e04b567090ef11274b3d0439fb33ecd0d3b2c1d93675e0d6e79687a786bcc79343f34e2750d49b7f05c537a40fec91b6f74bb3f3e927cfb5174a0ed383ffe0c7a87bc0ccb02b6b3a4523caaf9af0f72bd750756be8f1a82b657d43513e02ec2e59efd058704a747b643a25c9932c37b0188f58d2d880faa9c88ee4af58cdf30ae515a349f0f3fa324bad78f12af0c71f00a175069c1b15382abdd095bd2c14916f350265e07fdc5a16d3e60d82aac33b09bf59f14c6f027f4b7ae6095bda55345f60cd7ba0d0ca98dc033d3a652fbeb4db0294d28941fc4f4480f69de1e61d4a7ac960f0b0c9c496b629860ba559b4981193ec27fccaae51a51bad6eb0d03031f56881888022fd81a97acff73401be628b2fe031c4d593d93027df46dbfee56893004f2270a0460c86b2f633a5aa7d057d362b4be24b80182a6af1bdac93c6ab73d628c110610a00ff349eb941598d28572f5261855f9fac368f7a2db843be4f4fc5869a00772ad60242e960647a6f351664bbe300bbab6f0caddabb55c04badce6096b4f035f6c12370cdce5c26d7473dd42a8ebea3acd9d2fade10d17a74bbf0e0790560c472f348ddddab0a8081f6d7a7e1d439cd4c921b14a843ca69bcd2d6bbb124e0191b6bf7c2d858e46134ba9483641d3cac7e6951abea4f6776a9c0a25bb2a9204e436280712cfbb8f39f7a002e6eb005b591bbaa76f4029f5ebe494545374c10999e58dbcd3db8b577957b35ea9d41f54c676e3ec2c3c4cfd81c9b95dcdc6da047c8e91374e4c340075460eedd202f56d9fb862a79c1322862fb200042f036c080d3142c2a3974af3dd35accd7ca6ce4d9f3aa28d990309d54072dc12773cc00299466bc4c409e5c48004a499bcaa80a6f06b15dcec78fdfb999d76461638b808bf408dd56653760680b45db60178f8c65d11da9dd76588d8bbeef4453b58d60f3fe07fa9ec9f5fda7236c93177cd25bd1bca146d784b3177abb72aca726bf90dfa5b4a5678eeb7f25fe6dffac484c18207a59c37de44ccfb5158f100608692077e408e49a2b6fb58e435c03d7206ad06adbaa274912db8dd623a6cc4bde951020ef9a6e06a9d22a67d1925d3e1df7d59c4269246fa4b33ae6e44cb7f754336092b4525a512e29b9a3a3dfb57dbbaff16560504669a36159ceaf91f0a458afa0e40a600b7420a97b4b3b9af9dfb44f382cc8c29b2a32762fafa4be7e2be280b0faf4ddb3194f6eb1e0eb26ad1200ee9b2c45a920d66323aff5dec7e2394a4fa039d7194c8035fff83060cd3e7aca272ec2cb56f08347d2beeed0c53addca4610e12d4abba4e5750294579ebab204facc56de3e3a0efa8fb157303c1cce072850c7cd3205440d55fc96391233768aa4a40779ee72905f6da28bf6b9047134f8a05b97a869cb375082a036dfb52890c6bfd17fa63a9f249dde0434cead611bbb106d56a2da133d5370fd76a5ea2c35450cda10608eb304f5fda4852eacfc58252029eaa16e5dd46e0425509a587ca2f6a67c76b50b37438c6c16b51b3cee6f033012551f9576631994738307ce5aa96e948d5d660c909c645dded003915d1a90d0be97a24572dc0d0dfa1044a211332b14fe86427f7c9c09959610322fe02c41e0124179638b37a4df8634a1d65403ffa6d8e8319e7c2d9fa140b6bd9e151624705a68c2dbb3ef0f8456b21b827e34938d0cd86d3c9cc2102798a1d79d38b1df705b2493366caa0ee31ed0c509b7337e3dab095311ff36e7a6c190a422c8c89f90d348396ce41f4046a55a2e1e6a8bf1b337616c309a58b410f879c6ceef47e7e05fc839f509149ac4fdb46127a4664193626f19fa4b1e7bf70d40c4b316f0c2401db12c42e5900a0ae4da8cd91a85d419b90e1601124458073c74ce51f04ac2a0166b57853ec4a21154bd7d0987fba6efd63799b8d0708a632171f8b3f63af3b017518737a80538f3e7b41a5c17867e9816f1bb6fdcc911bae2a64dc9661d7d903f3cdb153641305523986dbe60f916512110b8bdf919f257ac64d355e3abc0d06a8a40b267bcfb3896aa451b1e69ed6299cb8d2153903fa1451bdfff95453a20198adebe7895c727dcbf14e2c43bf1553616bc22d2c12e5a28eb6c5d28363b7013cea94c3670012654540acd12677cfa49f18806e57f8efd4ab9e3228aed3e60559658da19cdd1e1c5e58563ec118b3af23327b16593c9b36f8f25bd0cdbb590d21b3a229f22ed39f98f3195a47ae1aca651159be0a2cac9fe56c46158a864b0566199bf9295e1583d6fc40afe900d9e8b456b0c7456feea46843820387d34904726d25765063f36ccf592332070e1d688343bc9a1dc8c5f1dac4a3a75b1ae90ac3dd842defcd89fabf3420e935ebd87d87355a3bee254d2fac5fb1568c44300ffb64da686ead4957772544982f215e4c006b2d351533b351c3c8b8a4eeb89f0c8ad26b9b42efe0fc1f9f3377109fdea209209d309cf5ea21921c2fcfa5024e008aa595f7b87f3c78cf0126162c2c588b95c3ba3e1337448dbb85384ef9903209112593d2d03fb1ba170b775f60f310d0b047cf7d9c719465f69cbaaadf25780cf24ac4b450719d1514cec2ba08f217f9473e47ab1f8d95d0ccb13ecb7aff59036bdf583f9e6e8881a3d7c7f4a90d0600ade079869d2ccdeab227ce16fa07a20cad6381fdd3cb2b12542a42b27a53ec01866644c96ba5dc03844f415a4a42bc0bed185fb0b51bb323358f3760e6f50af0522ca53b31e26e0f7a2ed3e33392be05f0654ccdc3552c029b12dd6cfcdbf09f20fd8ec024c64492cd386db563dadc00a262325b2b4863e767233ba8f05b2d090e6d70d801b20f887fd3597f2f42b1099554f24e2713c0c18971e42f8bf131952b5349de3da8ec2aa902c7eaba603b09db251602e9c370cbf0354857de54690d053467e86a84b0033a1c3023f632c2078633ebef8dc876a7e6cdf7276c19fe463a5624bc39269dff6ccb3e9084d66c0233dd364bbf261178cba71d116b9b38b4a9d1a428b149c8da70596aaf2f66bb0b944665a8de3cdfe688da2b52b86014af5ec3389693cb1fb780e473d5d622f30876916b04c1a0cf1249f5b91fe1c4e771b8cbf6b907ef07ed21286399e5743b078e980dcddb25bb4b7a9578bd12e06e8da63c073186532fb8680fd42c25542d003533a63ea68b493de0bcc66f4df3212d863fcb1e3a49faf65f145859a31ae00a2588c7d479d538ede1d2912634f9dbb33ba559eeba9b06958188e83f1a397507814c2ec8b4f0d85279b5af59f28c59e397fc19535d98d7742a21355c8fe76f020501dde63cdba3605b4aa43bdd24f4c54b0d481cd2c09775f7ade11e33b63b0e5d6d7d5a61d6ddcc06a1f2cacf72c754ea9b69d7665ebbca4b6bc7311a29210ee962538783bd50697d83479f5eb59717c4bb29fb91a9180683f9332ac655a60771a6015e05813d074207cfb161254c4932dccd0b50639b9400a7bf37dcbda40c6a931047ce2130ad329e6d01e2621d1c20e270e7b5f2529c4e41f65cf991f60faffdcfad29ec27e47b93881babe1dda01011b5f7d11200cf5fd1738efcd11c0305308f100aacecdb9bf7d432cadc3769793663a007d57e34803f594712c9ef02589ff36a2584f668f87be93650a2422c41e76417502bb5106b19461834355908aad308270722024b4820f9e56e39e46d2753428c2c983426f23a7ca263515602cf4663fa2623d6ba2d3a119a409a2019be14c35a7976b6a158be32cc7dcb4109c264f9b595405bea580812c23372b96d3a8751455b5261434455c4fb9d1abe033ca77dbd03a498325490b23089088442e6b3f8645d21bbd1debaf63cc915700ef3eaa93dc09df876afe6c0c7323707e1f54ce9a2f5753d1f22a1afbe9d8a0204ecc9aac6fea13b09c81064de3cb47dba2e31361425a97c6221958eb9f87f17002d33596f065af06f32d1df9767b7dff0bf7a8061f107db767a92551e33037707bb7b8bdb14b40f2b46eb45359760c7a0bcd507ade23091e6a55bb010ab3849041c0138361f5022d92229e159295473db9a1e3748038e824ad8e824ac9c8a930a10f3eb4e11b5ead6c03fa1b3814a336d439345ece0b1890c9013095b7db790031f3451349ddcc6d7a75b26f866df1d479a4e7fede5d36e8a5aea0610a90548072fb030559a6a02de7f379e76b8bc528ec1b1245a26be9dca0733976e6ee3240813ffcef7de5719269367d5b32603840a28e9f113169502806a67e5ae2d10c001d42fb21cd909951b7940a28f3ce1802e9d8e463b64c1dfe8b101f7b94f45580c2f03c494ac5a2310464f3a3ca3255cf60068030c89e80206b64a470ea62b720cf55796afc0eff1dd225dc8cda668a1cfcce78508758f705a310d623828bc760e24d4f70a79cec1414fe388e736a077c49c84c19dd7e1b9cd47f61002e0d4fa022dbd59bc43bc328760fda7cf6bebf577579fcc975affada3d26e9511568cd70d58ff0aa650eb7feee8133d15a83f6845e2f25bb0ddbb566ed96cc52b2f777f0daa1239febd9594030db744400607ed754e7632175abbeb101525c55fcf03c00857cfc5486bcfc74f5c5d50ed294df476a95e2a33af737a47f8ce185b80ed6a0b58d5313fc8e9657b944d3e2c0e384e80179d06d40730744349ae403176e054061c424b338dd1294ae7a4c7ede9ad45823fcd5fe154cfe329bd1649a2ee3c1400418d93fbd851f334c88fb631e4ef0c10983cafbf66ecaa0e7deecc71b825d205fa0d01e9b310a637d4ff61171e2520596c7f6dbb23eb6acc50d12a2fa3e0540b94ce0e22befe59ca83ab4bdef97e7fbf23be5c3e5484729fabded58cb187690c13aa287c21ef3de7e9d753d7394db4e4d100b9c315d4090874bc836b21a04a09f7a6bb27b43c768dd25e207b640f45f86719ab9a4ae650d8f67dd07dfeecbe0b1d2101f7d9f0796bf018ffce9fa67efa3b2c5f23ec880677089b694b2ace860e1e489bd37cbb297f851a479e192afd71a5400502a08b910f5a19520d03463e006562bd93dd062ddd69b7fdfb546402e421e6317024c52142f9253847488c5901484ba2070de567b7409e1e62f1b3c1b4624f8f1bfa248544139f2be40c09cc031a0857028fa041ff894c9ab547e72eebbe2a9df4709df04af71bf037e3e044038d7f6c905d7cfaba0fa1fe1c3e1a7d1bd4baf5c41b82fa82fe838f45b7d3c50bddccf9c5e476b613904c3b83b8457dfc9b7a1367fa3db8b5f52d506d19f6200efc53f5b6e06aef648893c2f10bdd79bfa064be3ca6db1dff3b595c9e52f07308705e77dcba7346ea44ecc43cd5df3c0a63c06ed861977cc23e144d5f5bcabf02af6658ebe825765c29470aab0335a5e97776df43170bbf7d168fb3bc91c5c10337f00523b81a5df8ead54054cf2b82becf72a08f1e79158bda80497256f5d6065ccfa94acdc4a14103921dd38865cffc06fadd54dd43018c49bf6ce6626d2d0f6db0300c0ba6bdac61227c5a2c7d0bed330338813377164951c837e0ba65a20541e9a5e67045cc0218eb213da1c5fdca92054944848787701818511fed652505ac73bf7b98efd58cb9b3c3574325c52c144612d7257b1b43e0168847dc276502f5b5baad1a76b96f5612305bf0fc979c1c198393cda8a1e92189bb36f1ab06028edbc79a46fb56028b8f8c5f27f5b35949310cc8a1575b738cfd62aa8c40b601f4a43f2c4ea846ed2d2fce3beb6cf918d2fce00e0e45554219f58a251857770f91a7397a2fcceb3bd25f7fbc0e48c80f690f3764a816f6b3aa58c70f09052a048b1472f83a89fc73ad0dd27eee128cd9e0bf7903275759068ca6a4456894420f58d41cf9c4f85f6d51f9b013f2787e0c66815aee0a216c8142c346ce55ed510df2a46ab6f550d6eab23de7b795a6e250c89441ced89859577682a80a32ba510d21ab36c6ee499e9cdea2a77d74e59b3bee32188efe8b1fe75af4a8af3ae5b20731bfe8548feb1d1a2b440679b4321e7417e4662734524a4ce00b15b29989520ae49be9647b8a3eef456b36d504d2a1e8fa2091c0dcd197ff5d6dfed72c9c9805e6c18113092684759699676b5a04ebb7e7455cc3528ef1c902ea4cc6f3e4aa088db0967c8b9368a527fe9008066b29257061ee9e1c69a2269a9630175791080d30a4cc69dc68cd35c84e7dc83c7290b915623b2f46d2b70e628eec0686300b021024acedc64c1eb03a86ce9f688007ba20ad3cdf1256fbafc41dae360dba9e0582ccb0b331cf8846064bebff652a0eab2820e2d78257d9c9025261dfb6972c005f4e67666a5d6daf1984207c0ef48509e6b104305ae378f3fc9dbb021b96d00a5982c5ca79fe16a00f7d91c5bdcd1533b8f93960e4143437bfc01acb3b769c0c993faa87542a0c125ad6ec7f1b49bacdd2e8b90ab46c74cadc71f413a56c9b02bc00b516a2c5b000ffb12abd0734b07bd72f03569ab80b603d07c87bc99e00099b6a46821f004ee946c3b35fb0592fd93e6e6921939153c6453583c9b6974e0784716e5d49b5528c9c47a2546cc82934ef1ea7346c9a7c619e019749381ac008173696369ff6b36b31fa03657a0294b152a9992752b1ba48c4fab7a4877fae066daa1a2938db5eb38f80925ab244ba4a098f0bdf00202ee34e2768db48b7cd071da3ea95e90791c0ea5d9d4183acac93917abf007e3cd64a444f723aa893e00ef945e793196a8160e3315a5484ed039890ec8658a07e7f353417cf4069fd4201f88eba5b87e264e2cd8cf56a875dc6f60cff5cc068ecf19faa37ccc5835da607a135f2da59a6bf9fb45d8f8545a310601c1f744f8051ddea4e465d8a2715670690cfabc1c6b8f574246b97db38811b01ad96c237ff483cb60e32d1b758dd3205ec03d8d4b115661055c0cd82d45d87cb8f5df6dfe04e8e7bd48f65f247f5c7042cd32feabb7af2c012035db2e7ac6161e43bf12d161ba35f403117f8ff83580fde7e80ed37f505fdcd8c74ceff0f75b7ff65239f8f68f0013dbcbab7adda620cbf62a7845dab9eb46294682158d227b6f8cb7cca113a52b45ec34a55b973b506b07248494a347b846f8c53b1b1e39ae644a6dc0a1fccf545e86956109aac9a06610caca7a0ecca1713a349e7cc102c689385c42d7eb8bd3135e7a2ed4a18c70bce75cf9847882795f55f949653bfa0ac9d8e81442899bca0f32b1309c16d5e0e29b1e97bd1d86ffc2b104bd0a7c80dc03af77df0496c7da678a2ec91e9be9d09ff2188d6f906c24fac5b6afab7b4632a10edf4c5e850ff4eaf854d89a1037d07049586e2d51864971695257c7c37ba0700d6c9575a9c5fc82ec5111b2b111204cdc404c327af27ca7e7c41bf86dd43084c554c41d17bfb64d818c71914397b05a02c4360a1fcaf331c96a521d537589531be66d3a54ef0c0bbc1060f327ec0023a61d42b590597a7343f13f91b08b3723d12883bdd51208cd9489de2a88685042c21f21ba5f0f66941d42f984eeadceb1cd4bd3c7a0ec591808b5c9839bc9309a3632b9ce3858c8c67676615f6e51a3d74d3bda4f102e39832ab305bf90ab50d7c156af51910c9e76923b1d13dda8d0299ebb041236f652cfd6e0313a9faf50c38cdac01e2565741d8edd4033d5fa40f300f862e64ed46d79982ba512524060021d7965b1eb658ae6f03e5365d38cb4089181ec142f4a79aa4f3fd4df206440cd14e59e26c87768169e4fb7f4bd586d7a167f203c018c9fa10d96352b2924f0f097a207446bb8dfb57a1f5a5364987a7a8a09b47aaa1f3a043e4e58acb742d08fb9942b3c18b4f476fa9bb07b3bd5c97c745467e1f104ba252003e17056d960931ec11c813ff11698b50f405a19062e9382db73942c4743d61e67a0fbd6c3100db5131c2519e3a0fedef4197fc9c0f7a6da37c9636fa4ebb97036024a8d39a04e3aa76f3074beab94986d1bb55e22f31280b56dc8147bed69bcc99a4c8f12303224165309cda4a49e147f1e5c174c01eac6c971352cf4c39873f8ae0aa3094036876b63d94c5be4a032ea10fdd7b1bfb2f12bb183fc691e63ce0db21e5915d058c047ac5f767ef3ef28afa5ed1e2d80c902ac479a6708c9f47e1dfdc1d264900ab122f4410eaf2a60ded1bb556690c3b10f8b6c5a950bbe9e06c117a21338301223c9d5bf35736d9323a7b13459afd292d9cbaf907969067b848388ae2ef020862222e1e809cad7f77baa7ddb1f46069c059457c544476fd073c3bc2166a7801081f145c69693f298d9abe1498817bdffb559153ad63d5fb060fd54fcb6bb401b9b1440f11e0468cd9e1f1eb4a6621a89bd706aac13a3491f39060949da9d302eb38c7ef3cc2b52ea4dce4b0e8106d6a51ecd5d624d1bf7fc82dedd6462c6209a50a854da8f827c81ced5ac4e0a6861250de87aa796189089ac8aba3784db50243b1b4a9ebcdc0e4413484b7be27c65ebfafe307639438066e07c055f453730b2ea6e0ffe28c5000a89a87edabb19a985b109f503af76a3a56f4451313d1b20205be5e6583489c577bb2f0c5fad07ab8729854e445fe1fe8eb575d1fb036fa08c2a82c3e1ff69b56df3b25b9d77bd14e1b2904c449302dcaf914e08706a5b50d80dc4714f0af1edd3d8ba939303726c04be0a13bc37a62c7636e45a1a4eae80d6e9758219caa53b87dbd253a970418ff91e61876c30e5be90d3bccb25148c00fd097b68e27d49f10ce7a7dce17d7a8ea5d48cbc86781d89323220d0f5172380462a45d4cc8d545922acad1a3f5fa0e0075d42079251b60cf59bb0a683e61b7047b3f421cec97c2cb23e84c936b906dbf7685e9195725813fa42f46164d13e40e886ccac5e670408684644b51da915e6afc7edcba06cca4e34e641995bb9edd02155abc8a119abee3879ff8e2364b4cfabde524a30fbac9c2fbe8e5b1879f5204a365f8306892ed73d54b407e657fc8221d9891f24b86a5135180eb1e6c08a10e5907619129a653ca34d0980100fcc0e98f086617df075fd3e8c7f00440253500c2f6b89cd2010134b1a82da979d5e29289ab30e1a9cca65432c226caf4ab6c05dbc3d71c0a6a8e25e32ba193ddc4ddfd00a3daea03cf0e0a793a11ff7b03f50240b36dad14f369efa90b23f6d8da19649e878e4626da272510efc8a8d1dae1005cda076793d521a408748a04b423bbabf665419d8f89ee21276f67ce7473fb09ebfcb6015ed7f2372ceb20cfc5b5da947d4286f6d5d588e8e351cc8ff617fa0d164d297d08709536125833efa3247cc380dbbebe0723928f25ab1e5e269a2a065ffbe884818710f0860871d8e2f1584f8bdd61b75dec9c98eac5f155957d8b01f9ea0d799fcfa76ccea2036f17d0c5aad52408c6857349ea090323c7faa7b30f2949a4dfff5d1c8be5e4af7f0ad214b29b3776806c9d7228753f726694e1e806fbb003974e63e64538fc1eac7d08b83c502678dbdaa5859f42ea2cb06c99890bbd014f5f3c60af0ddb650225d2488c827884bbcd75e7bfaaf59204db2c2a920c0bd43325e2def5dfe47ce4263ce3b079b44359549d0ec7396f9cb8bbbabe8709c896446d7404eba8ad19aa09341be3561e86984305a7424f715add005cfc6204af7d801fae7b19b6c5c714f17932f27d92ec07eb3bc9a04735fdacffa2d8da0fd8a05422a83fa3667644d5e6b78f2c8688e341bdb4af677dbc570398c61eb50bbf1fd3aca716bdd49a06fa093c27cb632c6c538d8c06a59374c0e8f329a19d0a98b06ec6b62ac1cbe0d618b90391f8e57bc48c261aee0e037a64c93d56c6b104e4b36e1cf5f91f2fa86e442bdea506ae95d448ba50f77acc1c9d3b5f515d6108c2f77a58bdd21a1716c83c1533ac2815dec5ccb9102d647dbba703497922f80ee811dedc03c5e8bc171e5d9253a7f39bc1ba7e20fd8b9c0cb07ae8b9a730f70da677ce121c3a3ebc04f05d10df936c73282ec84b7cf9fba2b79bee9f3ba5ed0eb6a2731e2851e17992b448c708a151ecd189a80011b80fcf081e5665408d090d583865c1a03a23c3df8a785103a62f8649cc548edd74ad81887d86435a72b50118717b99686fe69b56c4761df99cd1301f7187df4890238bce4c795a3496160c98297b1a285f35b4f13ad2fd591a57dbd329d509a9434e190d8cf7d862930b01e4b4155ccddec30b1d176bff59c50da60d91bfa37c7c9a51b1816c5941f0ec0d75087b3dd9ae2aff8cf0f67115311ff77c97f1322acd1d1d4d597ab564419b088461fa9340908093c37522f0d768b7de55623b0ffcec5b65ded02c6fc635df0b06e297eea63c6b93ff0d1d5661a117273b373b6e9bd2087db645030c50fad70a050afc9b6ddab2f60fdc044c3d3a7c080a4f83176d1e66ea87191919f9905a0f2e37ce182869fd1183798b155bfc5a170e417a5bba4180487a3926d6a969e60c91b1df0549255d0a67b5c67dcfe34b31b1d741f6002cc31ed33b67b899e0c00eecbca41e842e1a2ccbcea8d05d08320d8462d2a70802546e1914cd86cc7d5303893f05e62bdcadcc7b0f0eca2012ec376bda89e1e4813c62a595fb1c18b89b0677e611f429a99567d9f9408b07fee5fd2df77fd672c8484b5a2d74f34466e603ee725acb4fe96a61fdb3fb00ee305552e41f6babae1b2259b511a703c168bb0989a229acf002a14479c511c28c9a90fb9695eb01009be56e8f652d247199a105c22280b14060e90dfcc035e6951fbb8b940bcad0db89db2dc60d2541ebdf4d0ebece80b4bff150cd3c31e360e87571d6fbad75aca2b8f374c313b99bdfedd604ac3882f0857d9798ff0d6e3afea915af2f0700b1cbc8a85bda3a5e9214f54608 false -check_ring_signature 7fc0a8cc6e3f38f9a82606fb99b9e67d4ce106663c4e7fc556c4abd0db995d6b 134279bfeaca7ec8610c3eb1b16803951f0839193c8291828216096ae59955b7 28 1ea62275a7cbcd04bc268eb4869655bd56a530b4dd2ca3d84b3c891495ff425b 29db40f4b6833922e9423d1a9812ee534d1812af54b01af4d5fcfa24a60615a1 62b8fb539b2a40ec288c7f99cf2237bf60e37c915603c87631c42465872f7f57 f73d96eb8956519ddb758076aa0a84cb03d98589bafbe098c50443807326f594 bff711915ac4801bedd270246ee3044d5f6dafaf9d1b59a27d92689b6668baab 8094b85254420dae266d4ff7cdff5bcda798a0886905c6e9ec2ab65343213d61 63969fd178633bc4e0310dde7c74ed91d56945a1d4b1d3c5779fb2f92c943d51 ebd0e9120b17f73f96670501e87629ee8eb1263794c4b7885345e78854adbc2e 49377f01cf5670eb1b827410519818b4e564fc96d7fee26063c467f7489849c9 9300dc14093bd45ca35b734aea49defaceb476c804d6280167bed02f3c2bc0b0 9ecd692714033d9e3b7e6450ca854d4768d9691a127024b4b1a7917c5e8e149e de862bcd7078ca60210d8c9f6b3ef9049bc0cfb08b1c9633cd277136911c8e1d a462a9bd1a079be91c2bc48c93b21215ef73c152e6bf1f3210a33c26fd0a075b 7da373b8e6ff54c3e7e5f226cdfaa3d380446c9a04f9fed63fa5e313bb8f99d6 d156d26ddec24fe09df974a7e14224c680b4eae6cee5bd791627bb0f1cf9a358 e535b61f741701d53f79552fc9857274daefe487dedceb69af569b3ec24190ee 999aa80c7c2fa7d8125ba742bdedd23edf8ef8f4cc25ec8f50c1a535c7fedfb8 14520c05704c6847ca0cc8255a3ba2ff706c0f79f4f27b31e7208c654b91bf4c 941c799796d0ef70bd2d8befa05d6e8702f5e0c6fdcf6499baa3af7ca7acbb20 2f37c3609f6254a385671cc8779e637fe33068a9825c467c8462b92674b1dea5 72f0642f7cec7e66bdcd82fe17b7485283bfcde75e0df2abb6df68f9db50adb1 549dff3a1f4e7017b529101d9fcceba12ab5ba7d055c1b90304e7ae896b947b7 c5dabd11a7be5e353b16ae7e4567b68b7c586955253d397015ba18a72bde5a58 bd4471b92ed015e29bec20d4988da92f8325e811beecbdbf182a813afb9c115e 6aa5de9c09a1c54f2912821de01cb85d3c80584d970dfa631e74886e072233f4 18332465c34023f5670de9abd348dad4c2ffef1c41b96162f9a5044d7e0a6d25 8033cc95f0dee27e6d94370c1b42c0498a93a6eb2c28ed2c97c1abb3cf6e9412 612ca19d21211e8ec90749b26a669c423729333b5880a289f62ddab5aa26e380 c006ea0619681cb9424bc276a670c350298761eed9e6336df7afd632e3202701b5457a8aa858d8302b1903388b1e498267b811f5af6cadb901650376e12b8502d4b2f17267f3ec9c5b8f004948c9bd206e2121501afa963e5da0f433f02ee50867b3588d4d5a763867b6c62a636ae9df63a78062c3268df4af267e8a8b2c1a0638ac088cf335afd08d7d714b0d1f613b46b47d6f785429acf17761d03a3f04054632f649a503b1639fd4103d4f51f5e096ae927d615ca0a6c0dda50d870d77043f8a954a12913eb3921657593e981189188b2e21d18367417517183420843f02d1f5fddb5d7a9775b6af5283e4095dc6cca2409e4d328702ee6d499354cb5c06ca1ea1c3a6bfcd6d4922e50a839d7753fc869263c136072cfb027d4e1ffe9e01bcd2ba83cd5d266bf51d19286e95a6b0b0a35b56d9303fc6d58ac303f993120c5ac82f52a54b5b7539630b8842952016e7966e413072d577786809299e10e40d9a78694357327aa5c6f6e49ddc5569de8085655ed254771250a359660768b2000826ac10b3d5099f1872080610c84b145848175c1b246db332e791b1d6b9b10ddf6b2015f005b84221c4c062bce3417d7a0d3fdadf8c24cf8ef59416668baf00ddc94ee3528563f132900d8e9ce92276250452e2226d376587df6055479b8e0a29e77e6ab47507e67ddee1bcd392ba714b50061d23d6fd48903b3e8a978bad0b6d9c5af20ac244876f77520629862eff062e60352c6923394287090367b07a0504baa28296cb0e7c7af430ce8eccf14ab1cd965587a9641186e7fd801187180ae7287983ca731a546da431b4681b5cf5e9c6c377083d3c49b1694cc8a509b1074b8e8b2544866364923a80230309a17026e3c12000732d41d17680e0c7aee5036051a0d265c38e131915fc553560975472879aebccd85790b3e36b8eda443b0900545096aab6d8f8856cfe4a0da73d5915faee9ba81613f3c32754edf84df9061b734d145b5b9809d31b2419efe924e03889bc31481c6796a4fe0fce8790ef08975da0385ae4a9d34b7c4340d8d729f9ef573a243097d3e8eb27a7444bdc2003554efa70b136ab097dd8fdb3818a28899d45bd9ef24cb10eb249c0dbc21e6b053e13924932a8546dccf8a8474ae3e5b72f31ea07cdf0b13ed92ae1aa9a326b0a1db4faaf3b9ad5bca05828541ef9c1babc6cc8dfec023280c0412d868eab000bf046a1c6f33f5c2cbb33a28e244d8baf6a63319718a8566e980813105aba490e7f0914128a093d4654142d31f46e94aa228547034afaf9a66a0659a6b080dc045d85ca84d4e108aa76f679db31639940b591a0cbbed503750b7fbbc2f50a9c0aa3702ed53e5f49ca371184d812fab1e54d6e4e5d2a97b7137c3ba746aa1a530af7bab74cacb41ad60ecc6e9860d1f0c122653705350c45f259f747cc5dff2504ac4238fc0836baf784468d0fbd22d00d5258a947033ee28a351430ee4d90730e2e30be7b501b0fd250d5954fe71a583d58bf728dbbca0b27598258284597150510ab3de9b6764f7da5dad5a7e84ba9630bf1b76579de404677708f33c58ffa01305eae4f080c2fe412f36255d5e11d95b1e17e99eaaad9f6ebf1514ef3c4d5077087bd9863baf156a6c168bc93032391a79cb49c1d85d3df8a9638574d229b0dffd07abd5004a4a387b0bcd703a1c1b4aaa87ae9dd9a7f9d9c8d6379b95fa105abe591c326a5df1f518b4e2b46d9a0cbf1cdefb5220683dec06a25b3d5397801eca760160bdddbd555c58fb40570bfd0180934e123243bc5bb227e4dfe16f80cec28cdab9321153448689492935e07a62d418d33ab1c903e69c66021305571063e17e6f26ff8a6af003ffb9610691d8ea9605c59082112cc42ad8ca0ca4495030182130fb2187fbc57d4f9d096da000a1f091530f16c62f7f5ed0d9e58c0c00e8506d0b6bd82fefb2a4c0007f1102f8ffaa5a5d171662b75070c0105b195e707b1694a21a9970efa60620dea6432e216db694b0067fb24906d3eeeaf7485030764a7e8da3880d8f14d1836b378a21e552a223a51ca7a7bb16a5a9a3b81d4f90970c28ca1c5fe08e532db9ccd01a6675d7f701a54213adf1261f2d6a512efb90fef0ae8cd0400599fbac80ecaf13114f576b35ceb83ab036b4636eaa3576e93077457e480969d23c7954571b949721953aa5b7ee9b7e4d1badf64444173534d070d12514cdd374bf5c9c7eaea0c06c8e143f5bc8850aed3408fe33022cc565007c1cf65285f3ba62a085e0ffaf7e7efeae359f85369aedd91bfee0691e4c4ab0f27354949cfb74af219bd3354fd39057448e54d1efaacf311f8628350e8106e0813c19d390290aa6359396f95c85214b7dacf7c95c3a1aef962ee0065f8fa450b352ffce0639a0c54bd04b79f8b468fd38929cff147761237b976cc7e44aae3050b7fa8ce18b95d58155702aa922f654fb5f5e9f7cd46f312afaec5155857a20a6c9021f4a3b3cf9b808a91d0623da1163f9c6f3f15194f95c09f38c65bb0f70b true -check_ring_signature 0f4d6c55cd59e660dedf8a5410c30580f1d742adab9f92db78b467593db0c59d 5c74ea380cc3ed122404b1e82d7ec9f1ce11be5849cd5ddb0e2a5d41273bd4f6 5 51c52eabf3ad6b07b3b6c1df35e3037485b7cff84ced22cae07dc1fb7535c4ae 71121d3a735233afa1e0ea972efee469daf0ff3a51d2c25e8076bd1f7d577dd9 a6d3e2fab0e1cb395cddcb051c06d59e4d1b12d3695d9b5ab1348267369da31b 465c424123cc94bd90e24183dd3de2e909b832bd3b24762bf7494d051e31e8e2 dd23e153e7270dce79671ca4ec18e22ca6f96582ff3bf88aaee9a6b81064f0e6 2affdab894f9f4be0d91bcb8af3e7d0e354b5ea34c68574462bc4bc494591307a097838b1219c7a15239e8301efa835ef6fdb32bcb3c39a9ae27e77bdecff90317f2de4354bae71c46604cfea2148bb29f8b5f017cbbea794f1be15813e8a901b39c206067d4f249664b6875ecafef314a754dfa6537ec73257807f7991d710b6f67b85f73191a61b237dca3c88dafebe18d60613b9919b1d5f6db417607ac0ddb8bd43015e3c4fdb70d7c39862cab612a2cde0bc7a95ce73f57723f60052c036d498963b136135cbd98eb0f981b43b8a7e6346d48a01428438b2f5ae32ba908916f0782a3ce7b2f863c6e36d4ac5bb92dc946ce177bf30eb570f53a13f2e90ee620bba18fa7db42d78feee6624f1974fc10bbeba0cdc1e216dd8649154b1500abf6af911cc31b64a8e65ebaa090b1e0925d884a95b97691ecf38705e3a9d40e false -check_ring_signature aee6b69423fa9e093ff673f9697d55e74bf4688a432c2838a7cd4d3c09d23f60 e7ad822a8a6d5d4ebddba4e0487022eab1eb5b494175aa93e8f355500cc88bbe 1 f773609ca7b836966fe9b8046c18fd39559537b482ca8d5c461f52714bb9a277 0c7c41fed350e971d2fda43980e322d6ac76c4a2b3fe3d252fb10d9d5672850a9562f226a979ae2930afc9cfa7453ce4764ff76548ba45964a41ea691c2d5d85 false -check_ring_signature f3ead7434713e50108ee74aaf963cca0e7fd029ef566d18a6a8a3809d7fa957a b39dcf66b56682fa8a67fc51e6fb73dae4d86d2093ad43007405bf4f60520b99 38 24dc4b8032da0dcb32d97ffc5cda2d5b76b87c3d9b6e941be2a17ee19c91135f 477fa77b3bce9ee9d1c861adb55d13740de790aa8be7b8b282526f3320adb7d2 9a2f6a090d7e4f2417c389a3829278731fa559d0b71afbd66635c5c9481f8036 1f9eb240e938f4afc35153d8f04bef6eac51cbb8a3315674d56f940ea8801d13 88cd543c6a796e03da48cb50f49aee29445f0aa863504db287946c90f4794821 0d26e1f85e403bc710617aaa7589ebca08932a4ee2200512d0e70c8f64f6d935 c8225943a36d14dea0102f4f6e1aa715af9a4f85ad0177920f7f66157c267440 42495d1f1a44852c266cc6d353259f05cb11a116e43a97a0a0e3319ef3cadb19 2ea34057d3dcae136bc49dc503d4a49a1f8b0b87d68d915a0c9d1ada9487bc5d eb4de9dd260b3eee34e8d3459abb7d5fc62186ec0c9e0f6fcc8e3777fbd1481a 6c0f15262718312696abc86c012dd76af7b9dfa573fe440321989d489c488e41 55f8bf5664a96db6ed28ef2f900195204f8da4920c51775f1a2d03bbd075b8fa b3e278d2fee48a9ba3ff3a55c586f228e1a18d2ddfad94d53def2a8e384e850f a5c57ae417ebaa0070237cfe57fb3e1da3e6a72b4648db4ba71ef9d2591ec148 8e3848a567a97b3de739d74bb0e8ebf54b7d2e07d9bffb8ea7aea273c0f9c41c d2fd0a6c5b6c571b1d88e4ed3c3b4bbd845d2b098be53b2dec3f708626e27eeb dad4623f06437318171c533bb66c83b326c2961e9965de628711c0582a2fb8f5 a3c67a87ca674fa70343deb8313d95a1176f698d01f5608087d3abe7de31c256 07a524431549b67c52e2e115a8534c0a19289a7484d9f81c6141dfb11006e34a a6cf2a924fc8cd306dd349d38bc862e0a20de6d281645297f5fe5baa18bee237 3462f48789c7fa7c28cd0aed901c259338d2d9e9b33265b57abdaad1b70b2da3 3236409f551e9cabb2ee1fc195e4758900165123de03f331ff4e6945a9bc5f65 e13fe5fe7b2e9afc41006fcce954b8ecf0556fb1f1573cd40dbe03f60030a693 b42584b35f94acd085498fa355da433ff27529a4124f0665856f6af7b48003b8 97bbe0dea1bc7fc7bb86073bd564777c5b0c13f533a2e935bc6c2303e3a09ac8 1ec69f4c64f49b16f5b68ae9d9685670c8e4b52354446843f46d9cf7aeac9bcc b0c7587313beef9992c19264dc6840041c90bd6698d546b6ff260adafe843ab4 8658522eb8b0e2483dd4e50a70e48bfaac7a7bb67cc97ab6b6b24dd5fba112bb fda4247fde7997b43105f33628cec19e276bbb7dda30d94627c1f9d034e6bdd9 c344ec67cf2274a2b9b2538bff4e4d3132b23b4902ed7a8df21853afd69a44e0 14d6864e0b4c4319f211b96ebe7dcb0d22e46504d55d612e3be3ab6908451f2e 63b499c9a031929286234c55bd504c144dc76df22088078177ee5262bebb9912 fb7f856d24d454d0da7fdc6fa0b4ed84e5ba3701ad676989ef8b0977f3bbf8fc 2bb82aa5ebe027b0ae19ba69e49ffeff7d7b8ffd71df52a3952669a7f81d8115 0875cc3ae8b833998392a5257ac597f7a47db99f3fcc40c7c30d384e8df9cbff fdea730bfb2fef20c16147315338e6597d0edde3559f5a2e8d181317b3c17d93 a3e69d62a3b5422ac62dbdd3d3377c9a0845842b1532ca9c5fa8d00fcab8b87f bc186f91b01b6a7ff20f417cc3e0df2e5b484b7c160e9da4d58a06d2bba03ddf acec26e356bee60e0f8b658215a633587e1a5208ebb196b468e18a4a022e220e673a213f3e3b69beca7664e439cae6417f0811c282cb404694b958007d0c720e87a58ce7ac4f41b06b1512c33720f5880ac50402bbebf86a6d6ffc706ef233075503bc256bc1972fc9e00e8010c77dc0e1dca07820891f08be11ffc6d4b5d90adb9d138f39a6c3d6a94cb4957ee33566b0c5ffc52bad1c6bf820484d74789f05c38c25bd56af18a6aba55c4f1df9e5fbf643d41cee9dc0b22d5889f6724542035361eb8a55a2b76a828707668cc7cf460efe7c8950788e67cfa8b4c05b1be1058c5767c8d744a225b7f46f88b15e91a6a272e3cfaef22a7072acf433bad39607f604803bf7fbb1e0dc8e85170c038740814ee086cd8c187566a465bda5e72e025f78e280fd45a2de21a9db2960bc758c4d33141b30483172875c1c0438446806240225ff5163fbcd563eeb2ed7d92f4b78a761fbccdfa691579202740508610666e5574a922b9594591f4908354cd4263d5c6af3fe719a4bfdbd9340a11fca0fa48a2351d8bf5f402a2190fa3c9320b4d52421a30cd8c88c6624c4847b23fa051b6bcbfee4f4b58ddb4a7868c20c0ba23f2f08fcacc92237cc106f15cd91a8049bdefe8bf033f1ab1db2e272e4324fd082380d2b2ba37d7d2cbf5bf4c2a1a006f21efcb572da04ede965beed1cfbad25ef0cafc74cf84b5d7c956e0f18cc00061c842192440a5e6ce2bc46bc67920724b95b7ddc3e0e6fbb88db133573bc3c03bc0c432935eb8ecd091e0afd618eb6836931d5a4ceb58a97f523d206d981fc0a5c7282022fee66b0b7b39e2d0eb64914b76b350f2cb38a19954d204e0edfa1048b1bf65366f35875ac9a046678b11b88835b23b385637cadb9ef99139709ad045d70d53b644615aefdb3b95b720013ee4547bb9b43cd3fd5639d5447fde6b60b6699211eab1b58f0a117b18c7d2e31ab69af0f0b5ed02ce12735b1df9cf63606cf0fadede75d67f15b65937c7f9914416c0932ae209a8dbcab031a8331012f04808a64b90193e6ae4f573a742c805c32ec72ecbef56aadf67131b135acb11e037cea8e83adf39b8068795959b46e812abd3ea23dd48d5e0d8e43856425df04096704a365ccf6e2137f627a7c970b25812a1ed555e1a8af0a3df0687df6074b0bcbac4c3bc9ef1058801e4bd311be3f85bd619ae582ce117bf6c038d8b1fa89084bb32f4da195754205ac145bdf86e44ef170bcf8cc7cc5f3ee0bfd642761a80a6809cd592c9986e7eda1e2f685dc79a0d131c636fc4ddd1b3fa3156e5a483c01474ec7d783bdc10f84eec972c13a72be3e70011891cf28d57e91de63aab4a00dc0167f8e86bcf721a899a1bf353f7e8698915bf0e4337f327bd9ffcadd83970cc335782da1289de9dba2206499809a35db07bb153832f88d01aa6314513699013b8c3cad2dfd8d484a519c0e2ce9d6060b2478133348bb4c68f6fd3d3664890f9e17464f6c65aff677501c9099c9fe6a2ecf3bdea49240648ecb9eae0af05d09027faf9f302e6afa41aa0c9ef09d1a5cdce1b774ffc47de4659e3df199abad0c169fba13fe3754f4addb9d2ec8c2fba369a44defda44964e0ff6a7e196e05d085ade00485c308ab8d3cfd447199e0b69d592b8ad392c78bdc6879483cb20e40f12c5c65b2a6e39472d460c160144c56dde8cbf39c1d62a686621f6fe0b2ef809cdcb855d9ef38d89d428fcd154d3e62df0cb04298e7bc9e8f9856b12b671390c5701378f6bdb9ed771d760a88c22452ee945b62cd5a2e04ef38a746b5fd988000fc0d43d837da229490a7389e9487603a4a70ebebc8a06cc9ecb381c44f74400e4dd53fc95b2dee9604a40e615886bdaf7c79cb997bf29fb117c921422f23108b4ad5b54e6db8e499dc6a6ec76072bcdfff4749b9227f87c69463938cb23570243bdc8efc79002c3470f0cea1bc8a3f7adef6987de2e89f114955d208e6637066fbf1a3d6099124c7774f15635a2c802195c3c9012d0f29e90eb853fe5ca700dc5aa4ea40350cd5780d0295b3766431ced586d648dce4bdf9eb0a01811a265073469add6921dd175cb9bccb0ca2bfc1a2c6fbac3943b645ae30486a283bbe10034de73d2c915e1151c5e8bf789e0c1e019b74fb2cebdfaa59fe2858d54d2b206b0586a8ef8ee1eeb10076fbc90ed2d333a25d1eca34ecba95c12a8b2a9ff670f2563c87ce2fe780ad88a470d0ea1a92e58a020df63a335e7491f624cb4f1a70012c5ff36dd13b6bee26353f124780c060a71459adb9e206ab873e99230cb7d069e0ca05566d3aaff5af5a03a08b65ab933a2c852f5fd8191c692ee92baf95a02547337a54633cb3d35b64ff1642efa51c2575b9393ddb1e8ee2365463d28650bf8baa806a435c2b19eca6e1b7d60aef54095935924ad219083390d7ed951760f8f42ffcf340f42d0f8cad1a66fc0d7357820d60771a4c48f614f72bc7fa9ed0776db82bc535c308aff4d8c78f98f649ca573968aa92e88585cba5827660c400edc4e795e988664bfde2679ed01c9753e1a8b60ee71dac41c0b8c292be6239c0190bd1f7e031f5310ddc4bdab787be737125244f528be25c959e681f906e2e2011114944e270ef19912a4d8e2676f1da9b5796bb4ac37d933c2cbf599267c6404b9673870eecb78697ff85c75a2e95fe1bfb30812fdc62ec08d06562cee5d4604a9cd8417481de11b880d21afeea07c3fa23cc969a5c0359ca1d4ebd3f0df40055f964e82f9fc780e1776b571aa421b51518d98abd7fee137d956f763034bab0b2cadbca8bbe6b8a2ea44bd3b390a77fabb6d432664b8443ccaa5010da7988f02dec4861c46009493142af1b5f9f8861282ff3ee39d24024040581c04e95489054a47b6b8181b07c726314a2764408808307ee6a31dafc4c6e09f525f5988a00a223583ce5d2e4358047e40c9040f67427e9dee4cc47e85684f600cb2d8f7310dbad87a33010887755e377a31493628179c7c923e0f7e6d6328d09c5df8f3a205b6000255e23febb47aadd8af71beb4240774630ab68b9ee7e95468e699a87a0de020ce808127acc0e22a53fd234cfae97c22beac29946c151c1d1ef3f66e000620a51ac832ef531341faed2731e4e4f695efb1d7c6dbeb3294cbfc81c5e10800d8c2006b2d841cba7ecbe4f4718b4947f3cf9bf5eb7c2d54df592e12a3c54401dc9b903149400a77e7c7287e22615de231f726db4f336952f374a0ddc1ab09068ae3f9469612c97a9f87302614d1f16257315dbf37b558ce04530a922d32ef091e19b4e8d504b8d71f48be7c70ce62af17e1c26dbc12e008fee4e30492dca40a7abbc347ec031e6de84b90be25e29f86194b65aeef3b238c96dc389bf585b3048f021ae7022f7123b6cd78884c5c536f78dee34e8323644b6e49335bccfe5c0f false -check_ring_signature 28818908147c49b04ee1aef61928e05318330b620246355bdb2aacd7d4e4e3c0 730691a7fb1bd34f64c38e55f6b0ab1823d84260a3bbef27af110037807d86bc 1 c341038519adba0855e7441ad0aef9644943256f9691668e143ddc6b0ae56f63 7f024b9c8800f50da43404ba3c7f4102020620b6f2a2bcccb476fade13077b0f1db69d518d829695303a7ff565d66760a05bc0f237e12dbe43fe2558dabb9b04 true -check_ring_signature 37cdafc01354c1cd282b147e52d7e876f43600c2646aa7b937ce1b88c610e880 983bc5dba2d1fdacb6bdc93d5f2eac7aeefcaa1cf4186b76f61171f7133d0ee4 16 605393dc439acbeea21747da57b72616d892aaf67bcaad6372a28ae4eda865ec fe8bf4716a830ca25813483ccfbdcdc108122dc14bbec78a74337f6037f43534 e5d959cc89aba16b035d39d76bc979c84796ae1f62ecab6362a2bd9fb95dc972 95654e23dca4233957f50f30e8255db3948255145f006a52eec7c9238e42570f a5f607f7196a7cd83b57e3bdd63e396f12b6035d40b8e0e4d20f754997030a2c 8f281010da04af373bb139aae0922822dc1db058ef54c7304434e9e0f25ed02e 4d849e038e33a47a3e5f634ceb5eee29ebd5224275af1ade25c363484ce6995c ec876d73df44cbdf9f455cc08769a7b6e86cf7dc1bf5c2e8730a88fd2529c4d1 4d542f6f593ba7e89a14ea7630c37985a5a4736aff950285039129207f6257e2 ad8930baa7641aac997dd4893015e922b3a9c97168ecb745659242e4ac139490 9cd17a04e3dcff2f84364b60a0bb7f950657aa4baa45ae949e0fdf7539e7cd48 26442d3f31a87dc572f6c7ccde7272b16c13ecc548819dd5ad0fa0bb7b01804c a959d093451f6a3cf514461f9c428b5678dc204d7fdc6a26dbb296e160fd89a5 913d0a0b8a6e64dff45df924b1b6fe87682adb605d36f3c446c163d22615bc82 317eef983ac9edc563a2f82257d305be7ac890a6dedde2323aec12d054aa45b7 91068873b151b4cfd1b57e8befb015e6a7e459f4d4d245febb9592515aaf215a 0b3434439180bc726b2a3caf10ae78ebd0de82ca9e957deb13ce80d0136d910814d26d00216b0907cfa6c71109b3e69bba5e7c6cc33fe9c48412206cdd57370722d759b4946663ec78b2b70d2054b8a880ab048df4fea2e0e6fee2f5dbf3590feb0b1fe29894dd32cdf05542bf59ccff7f6c5c8ca0b25c790b4d201d7dc6f908267c443f3537234e0812ea50a8aa5e9629770ca90a70fb1c744f81d93473bd08e71d6ad82de19f2b33917711451b633e70ac8da034bf5c2ee445ffb17c2d4f0d49e30e831543074e2f7d94d3c001f418d9a367113487b37bbbc8f360c6425a06423df23a990fc3204629cd877cc7d782efa4a5372f50906e4b5a0539b1676203c6551f9c0ea4e7fb8ce27b0f4bae786f809335cd7a2b8715e678f5165a963900f84147177094c392fd9a39e6f9850ae92eee1a428445e187705b730f28b09c0411420cd4881a17c0b0a2df0e187292489446bf41be2fec58f5c72f7f17a94608019ea7d2828984ce93ee65d1aa7b62ddb9ef6f9348bb6d72e997674d6373140eea64a08265835f9c5d15a744958ce9aebcceccb92f24f973774e0c77ac24870b821e0021cc0be90fc2e563ce51e7244e4bb3115cec7b0e9ea806807f6efdfa0668b66f5d237e3e48bb1c2f534665ea0d7359637cddac188025d8590c27aa7e0fac648ccd95979dec1692967a86485c8c1063cb4332c308e668cf762f2b0a9e06170cbcd0ba3472839309cea9f85683f888913a9ce9787328682953ec1b43f10c959d0e17ed0dbcae6140600001ed1622866bae2b65c22642980a07390536970c73b889bf428751583e2da87f8306718d47d387bf3d35ac2e6238ac4dae22f505d572ec60ddf4dc36cf3caa7fff2951dbae6cc1fa924483ddfb2f8dfd9766670947dd8293666c14a5e929e83c4f38df0007263d6f00f52da339e32a733d947e0b183b0fa56ce26479eb77546b449392e333c7d8238c6cfb67dab4e6ec4a5b8f08b7f8f7fdc2f2851e33b409b93e023860164738fd0537c7efa9ed0c54a6e7ac07f08fdf46cb7ce42adb719f97b52257f3590262f4b45c1ca8f6e39d30e50fa30f7cb3c29edbdfa1d8dab2bb77dbd993ceb3582a4a16ad8eb6db801ea302ef99049b469673c1981b8764f721fb9d6ad746c43ee3d66bea29bb51aacfad03d1160abb9c1bd110c98c6bd4eed75b3898f7f7c3857a06a36e1c0335874cff0aadbb09af31d8bae30b843aae3945c4664c068709650b70e82fd269587c2884cbe0410de32d8d2090732e671577fbc1604a6e84e64538f13d2a108196d97853778cae0f3a930c6d376db53c04a45defb77a18e64f86d77e1022f5fe16463c994448d20fad1699712687d7044309e14c11ac847368672b099cf5f7b4144a918fef6c19d0dbe73f781fdc21ceb3dbfa74723fa460df8015b3f97332deeec73be039f3d309 false -check_ring_signature 87a17150fb4f1ae2ceeaaa532f98cb38b8eeac4d9ff36fb622492114ff6f880e f474524cdb811d88bcc40e4af87ea9d187558154758a9788b6cb6329f9fe69bb 10 803067bc3c95b2fee68799fa6cd8638b68e35d766dd2688c95b7a9411c866bac 2b366b4dbf5752e69539103bb6b34bc20067b4499d13431905c3b66f43a1defb b2efe5f062a71af89403c8b4613114bf8555feec379b3767dadb2c9b80408ff8 04677d0d999ede897e8fb0c6bd68e3f1e34c5ab08ad1facb1bb316f34c84175a 10c58f59feb808bd3cc1941c8fd49be026a1ca7ccbab7298d3c86df967c7eab8 66b84e5808416baec0f5a42861fd62b3a9bc667fd275fbe74d8b994cae732734 5e495f3a513f498c306266cd59da87871b189356d91a23816b6b42a934cf3027 4fac6e06fab9a84eef795fe0288666b224d28f15525c9b0ec9a5389081f5d339 9f23fffc6b654b8637c84ce70cb04b7b71c118ad76e7751a0923bc0e75641105 9c3f0a144314ab7cf4cd3a14b5671dd73568f60b6cc9ee4103a2237af50aa4c9 9282e9b0cd05e5e51a959e818f7da6c22787e544f2c3e75c06e2fefa276d370ed6ebcf30c9d74b591fdeee9f94aa94000ddde77403a5384c684a5bdf3e790e0d993a81e2962532d92d8a1ccb832b5db73d863b635370a3a30253ccb10455b80262bab6e2862171d99fa45319ebace25777e1bd5397b90e56859e4e14f3a980093acc721099d591a3d94f8156c7bfde7a2c5a8f49d03c90d9635b4bb57685e90c754f864a33c359e9b346701f83457ba52aad4c7e0b3cf64530da46d10f6565019ce1ac3ee1585aa903a73d4c6298da6feffc34a4541ded6736a5e0420410800d23bb22244e5749373b41b8da22c945b4fb5aa7a5e87483aac2ac226c05c90a09f809bdc996922937b6050a1a66b9cd34aa4263a1fdcb94c3f70865e3bc8a2c029a2c98d1ea3a3c7c7d8a77e6da9fccc1d7f6392398e718714d3917a4b8c7ed0ebdbc50811c1df2387a537cfd7f2666723bb1b4f14769b7c94bf9b751dc8f0c0e658a438e3f6896b1228a164b2d6040836a716a25f1203176ed73d43636758e0d858f4fb7de62b1be9d7d59323174571490e323711a05c0b7c2d6d8e6b0787908fa068cbe4964b833df0a490fa4b343b804962bb26590f5336080bfdb0eb4780ef62c5ed0f8c7f151bbdf3e554aeb72ec37262277d95f13734d380f4c984b8c0f1fb69069834f768bb615a4102d84abee64d679f3d70366e42b04ca97605fa0009e180a549414926f40dcdb589cbbcee8b6e6f86bc8caa5edf5a0699f16825707f7b32a5c7d237dedb38d7113e07bd6ff52e88abce107240a7f54ed51ddde4b0fada07b04414737636548f2a5ffdbf8be98c9e81c756edeaebfbfbb7391f4ea467b7feea1a8759ae82e1f8a3637bc3ca1753f04fc4002a3b914855ad15e0cfd00 false -check_ring_signature 7a8327977e1791d00457b0aeac0e7ba004d00424ee13ec5f0874908dbd651f98 440c2a19c8c49184710d9cfafb0fb4c5a24a08ef38fcdff564a9a7e10d4cda9a 3 7d8b74ac2b49be1e15351a27591777eb0a6926ed0ae20e767ea7221fd1a4c92d d55c0a41903de7027ab07dee3ce7fa99895e5782d08aa51fc65f456db67001dc 4def80d73bde1eedaa910d2effac73ac007d4189f32fabeb33867edfd07e24a8 c3c2192489bb75c268bf48984b2be9069198a30577e1b82295f1f0e76619b1047be75c92734db004b5930137d2d9462f8b25c0a4ae505385333a9f8d81cdc80a2ac101b2f4d52231daa1f3ef55941a61a02fa3b4686273554289243c2de39f0bbacfb1fe1658159cad7bc7c535da6188443aeba45f991134f4132f2e8765380539d7c9214eb21732a46c39896e8d03ea901a29e1c2ee25af6592027644ccb50e8e912ead08e0d89778282434c1123664079c32316275309d83b40a6e6efe0401 true -check_ring_signature 60e364a4b61299bc3ba89d54a66945924cc7000311f204326002894875a9ce1c 8d87427884bd1957f73aa57213794acfd99fabb099fa30e2adab1ff0ed321978 127 93d126e04304e1c4182ec277c435089cfefb0878cffb56c81a4196c6a66984a8 3cffef97901c308b6b9295601536e851ac6ff7d5d339e87153343750e64e39ae 1f20a9955f67e7ce8961c33d0374cb40411e7bd11bc96cf9a7f061b475baf2d7 f7a9d618c6b289f2d61d385c632f8a2de1dbf689ecab70ac64d71fd3a32467c5 65663c8aaecc206d9a7633617221ffbab50962ac789150d0dff09b5e753db75e f0485621d1f2a341549478c0257ec38098dfc774fa995fcacb792d7529ccbc4e 775cf5bd8b9177715ae63fc86369330e87f46720c75110942421f3bd25cd38df 0abab2880a58a9ca2764b4313648e4bffd8c14dbaa8b20ba521a3cffacbc2e71 a2f9d0e12dec12952012e6b098a4e7bfb7482283b5bb5e29daf14af43d1ae5c2 69a70ecd3c108a7be7976e9d88d855e7d4722d8d9507afd8ba058e2d286f0ee8 ea9cff1d3f80808beec0410b4a53433a07834fdf4ee926b4ab224748b0ca1927 11830533053fa13373edea0d0fe4ca8a1958a74d89fd4ace32ff2227f7ace4c5 eec27944ed15cee37d9574d9cb5b769195e287d1304f0e60e352245414121abb a9e65605984285d6cd467de5a07afbae38ed57269d8aaeae4e5fcb0bf744fa7b 547a0c16942c10f7a2398300eb33d267838c01b66358870f330e61d5ef8982f2 a90d1d718cf49ba2e8b7ad219d4343a6fded27db57e7f4ab06b3b6484d81a0d9 a0d7d02647da73d9d490b31e73d4e126eed9a3a918356ac6ab1c26539e1f21a8 dbf658fc41b968cba0f8faa0c6232c4df49c90ea58d3811c574757c14d172cf4 afce1b7f65451196cbade7b251a5c6ba4c2bd5a6a4bf283e60314d2ff98439ad f771484faec41291444d856d693f67b355dca74321ced761518a21f2b9b89de1 b980cc422ae84eed195a95597ee3e1d9b511565eba3efba8aa5dd838e1b26528 da2008762734dbc29a024fee09b399df8baf0b3638ea52cb9fbc7092afad7931 d432af1e21cdf41fce0d2eb8b700166205f361f50717b7e97e2887b7a8903a14 530b268fd23b6c97a006585afbfa0910ce2d3dd907da004aee7196f4522328d5 5e69e4044035815f9d74a4e5afc75df2b2150eaf5920ce4e633a92c20bec62f7 62abf4212fb1121e44e08fd8c7a68724b1690f6c0ab212274c8c54334fbfa722 9136edb42a54882dc9342a141bad88ddca0003494aa4e58c5c0ec3d24b916e35 ef492a78caa7ee47b742e90e26c702363ba133cd0c2a745bdbd260909cc41ae7 d3d4ab7d56768a26794a6b96810ea286d872ba29ffd56f6255bbd6d53c6426ab f7e2c733cbf3d9d624bb63d05418950944fdec890046ff8250800a8c4d979643 df8b0736a4dcb09454a880ad254466978d65a988960fac89e236af39f76c27cd 95237d42fbff64e036c7eca36ce2bcd68f44a2a86c25806dc2fd1b4422115188 d34efbc988dadafcba59c2d3c42b93addfb3f060a30a82a70316edf161091276 05c8f0faaf186170ae7614641b79fdacc7e778e22efafa932f6b696cce934912 50aeee7f9c6ca880435c2fae1169b9d178aadfc5e7d91abe8b55dc9aa5a358d1 5f4c2cd0fa756dac825a9ab22824e3838bd3a71a0130224165d3296413d89305 b721a1d49b193d22d493eb5f7891af12c74cc738a4f2c5868353dd5e7c624886 b4d623f982bbffb326730e45602e64e4be617f0693d5ae99b1a4842b2f30059b 226a3e1286014ab194aaacf6c0a0b9e83081193db209f4401a7037bc6774fcd1 0f3c4fb7530a2235b6d273b0d453c49b57c3488007e227210bda02af46d31879 a4c6a9d384701fc948a3dd266b862cbd08e370a3a11cef563861059851fd2f67 430a47e8ea3fbec1a487a46fd27e2b105c0412bfb60968f5948b29c0fd36dcf7 1c3fa3ef4a50606185b35220418cef33cd03c239f27d01a9e29f1c497ac7a5f2 5d86291891ce73f302322fa18e919c2518958351feb02e7fcd8c19d0848dce9e 8a6f5fa8559177e1f42ed3bc1a3faf4d41b6e4af438dd1195ceba7f459c323ea 029391fba7a4e0e43a4f244288d273a2be3b80a930015cc33b92b7b4585881ba b105e77cc43d2c7c6a16bf78e64efee88a279e1bb52b839903d9d4a31e33ad7d e50737190992609932ae058a30957ab828f25aecf27051d8602f6821ea81d124 ce55fafcd7f7cd0e03f893fe2a1909fb971c4c411225befae17e408a7e754cf4 16880ce1fc9de5c16ddd7746ef2fedd35708cbd36d73a58df7844d6e5736a5c3 ae266e5f6c13bce54ded17f8e8fa13c65a7408584968c9f206d60996ea44a9cb cafe06ac80019c4b0a284ebd658dd23e4acc31ed22f0ee407c7f4cfd2e18d6a9 7f1d433dbf39a599f1019e42c374b8a1b1c52e74a00aa887e25c431de625317e 4dd0cc1c234fe16969d8fbd71ef2a5ab9af5dbf35114953c83052a392d802b9f a2a35ba451c92f719eebf31d66ae73e1943449717edd5dc173654e0aeabac216 a540ec61005e0f32b48bc2f6129c35e967bf07a45d8c4c5ce1c5a1f62b094299 e39721d24d2a127f11a441b506a80fec803f6aeafbd9678a1e2fc77f7e572a8c db5cf41ea3a0a45077e4f34bfb223b56995a5b68597f96c883ad6377f9d2f1e4 9f54b612f1e39deb49c29fa85180952f9fafc06e3022c548ca8502780e32b52d 406d0f9d0bbde264a4f10f3a82ba605026e0dd37c982830c5ab243ccd4084684 a757061fed3b1e9f4267b09a34011cfc8a3ec46fc871dd96526fdf5e1c7a573c c1f68159c649ae31680698d333aa0353396696b0c849ee72c90e9f86d604b63c 94d58d1185aeae03c7f132feda19cae60d7c2ceb8e5bb780759ed760d727ffc1 8734a9288aef7f861ea5c9254ae7712cdaf2e01d6ef441e2646c895486eaa980 095abedd037893223b727dd7aa2f93292bc1ac50bedf7eda41a5a169ad829c08 2715f062d99501ae18d037fc548d01477eb80ece6181a51b4dcda270cd5afc90 20ab953ddafcfee34fd59032db0d781de0af984485488dd54932539b9d99c2c6 43959bfa1a220c82af686cf4c9f3dcc5ef8d24fdfc869b0b943ef661d5e1f9d4 25bae2883e0e3b02603344167c00245bd3c570ebf062edb8407e30407a2147ed f2a1f986650f0edb8d86073e0eae29723e4052a04333c02bfee4d7d83befcc6d 7c5e4289771cab50f4c54ca0d27704eb374c27b959586a26370a12de3a7aae27 e1ff3be1c5fd85fe0b5d4b17f62b58e85533e3a5051b98457559c5226125bd52 a2845d39794c2e9b0789d11154d3636c913f190138ca15059cfde5b7a59757ca a13238a832288aee6fa1faef492a1aab3c67b84af4e70bc66df69340f882b07c 592ab78b5df868d57db1b72a461f9f2314d44d5d03840ff3b481cf5be898b84c dc1c1e435083513e9c8a3905abdcb60aaeb7625490bb8a812882cf9c51b194ce 7937784a96db5494ecbdb04075f910ed364852b6312368b1657bbc9797c5cc02 4758815e5864c586ae080b656d79e053747037a165df898f8615510652eba118 639a055408f7d1392456944262e5e8d9e4907d643be9af777195baa308ef0470 a9574f1bb2735a5df2621b6caf564214e0cfdf0a5436c8002a7a372428d24fc6 42f70c96b65a2e3c55d9f61f6c4024c39cb37313307fe879242e699f3bbae716 92102d646cd6ab8b2165d4fc80d392dff3e6af4ef6988c8d5c0a2c68d7209ba9 ccbdbb8e3c602acfb8e8be6cb64b9141de5efc925d1a70dab73aaf23912cd0be f570ee83edc803c265026e78c19d95d9cabccedcca1f9464167131fddac1f14d ddbfbe5be9756c25c0eb2638ddd5d1d9ec5f8a76e669c148adc2a9aa6f719af4 c9ed0376fc8672ebc1d3a90b28eae5dcd218c72e54dd0f84bc870d81ec74784c ba6c85c1dbb7132db014724d6e87dcb71e563cd229cd5b0d54a3d08b8b14e663 e62772ec2aef33dca0684c75afc307b572d586814063eb7ae0ade293f4adb7fe a2a42b9e1b913fba0cc49f37d30257bff3eabb9b46c1ce5d37042ec293032d1c eb895863bef82900d134f1e065bed11591fbd9b184bc9f0aa4a57b9c87d6b326 92543d4ed9d367e57607d8afdb3d9b3d13251c58ca1a74d001f2aac3a296fcd7 38c03816a806f0c185c6d0e888a6641d5820ba2e67bff1f2437084917eaef26a 6b644ddd5cded2f11465fef7f5a45facce93d59ae005d3cc286c8600175403a8 768eb1815a47381ac170ba77180102bf172b194887608a4cce4480899971db90 31cf405bf813b2b4203246dce697dbadbd9a85d7706e910ed007c203fea5667b b3d2daa0a40a525d79f7d1623113312fcadb26e1c3bb82d1e29b45f06b1e28f2 93195b2d61e8a8b510607ebe3338a02dc4bf68110a801807395ab1d7f72a6726 9f2667df98d0b6d9a2d57a798ef2b90c0576d4532cfb3d02edc0384929c3ea8c 718ec0144a7a04bf02b4cb6e67431f4ca094de3dcbcf599e48776bdaee1de5c6 17855bbbca4c4817e2580aeba35cb9db459b71d32e9a1fbb39ba4913eae99921 d0e2122d8eaa8a1f5b8d6893b0e41a3c7acf3aa5acf41ab754c1fe739f2479b8 c4c8817514176fc16286d9dd69bda38e9e6a3b9a915916d08bf1a78d579cab61 11f93071480341cf71959a06bfd88eaa0e9cc0b1055ce3b4d37c3aa4d74054af 46155c1e54d63c2a493ae1fad722fd0e3aea1cacaafa8eb887eb2f827854e8ea eb0962d3ba453135aa608b36893eaa6a103c7ce0af404e2ebafe6e7b721fc0e4 582a57fe55fa11c0da48e37fa163d5ae551a08ae91dce61a887cdb13ab42efea 70ffee2253e909663fa5aea737d78c7375dc71ead648565f031d792bae84bc07 4c820136d7f5fc28cfa0a294e00efe9fedb2d55a8ab8cb41478556c7a6b4940e f2060372a37f2b69b19eb463b756ec215ceecbed44e389018c170a0c9a65bda6 ae2aa435655c139775eedeb754920def6eb273b03255dfc72cb40c1ed9da4930 710bdcdadf14755939c94983e0755d6975d17d6faedb10a6d89d27f994b8bd7a 5b70873a944eb43c298e01817ec01c7df1ce071200d28a886c3666b541326e7b 3d1d59f882a565afc95ab2ae5b1809d5206a3b39892ef5c28e19029c7a17304d 4d43550a8f9f42b230a203b0f6f4deccaa5c6dd8734fbeba134a8aa45cafb0fd 1b4190d0a9a99e4ee849b266983de30e61109386dab334b742689f9bd280d613 3481777658e5a9b0bfad23fbb5de9c269fc855f4852075c52e39e13f0043fe3f 95c1dd812f9e43aefacb15c669dbc16a8dce4ff372378a6649b2b4d0ce0f64e2 d5a30dc2b94f4674d4995b8e8700d4d5611af3e390d19037c6c1d0d6fb723a50 977129464e44e9cdbf749cc2dd7496d75bc0eea4b17ee0869a4f909108ae3b30 3500f39406d96fe52a7b687df21df996e911bd42b72109d038150d0848070f7a 24cb393d6aada86e738387bf9ea172b79addf71f6b0b35421a48e9c903d07536 4ce7a9edef9d03702de13cdc032a6260817f361cd5bec5e0922c598ee00195d8 5f7a153908f865e88c99972e3befa2d967031dea080fb2a3d1d7b3728fc64647 0bacaa1f22e6b563f5f27ddeff4a84b7d6b9b989cc6c3a92a9e52a1bacac2d52 d91a95118e8012f40079365c107149842ba42cdf8e2b0691e453afba7f24ab95 7cacc0e2389f8f76b11ef3ba000b1373ba5a4e5d2dc6059317d738933c7bc97a 72cc5e9b02c5646a265a0ef468719bab77406c8fd723a94072ab5c6be6da6bd7 decda8ced55d7e984a7fc7a6d5d39206c1b9127a69e2aa6ec57aa5a396744304d9a801dcfead4241d2a2a705ffaa9f25263a9f9e9fe55e89a789b6715d994e0e8cb9dae643f626fdd6f9c3a6d4dc14315262b395265e410a2edd04e4ce91b20ff2000a930f27ac132ce8414907463dd4e73159a741e0e7547b1b4ce6f1dcc60310d2de459986d76379f79104c65611b0f36e9a48a68409ccceb997c176d2e602a673f36a5e0c66d2f5297bf432401a1ba60aa3ce865b82270601c22244630803f758958a66ac9b8b533953f985b394264847503752f2f748fd4755c001d6b307a7019ecd69b4247e0dd2808a8aa75dd21d269c05e031e3529bc08f129261d80d990adc0460bba4e254105b478537e01b1f6f2555042ff0700d41a2d6c9faad0a1f12a229d975ef527d25e21cb1f37f2e5d71d27447b129d9051c049252d43b0a40ce93e559534cc97e70c47fe5352cb61082929e5456afec884656e9c91f22005e59733ce231b2fa47679c419bb8ecb3c2d2d57376b1b81e93db378448e48f041ccb929f2fb9340f22648cfbf832238fd149bf59bc73428e597de547f85de106cffb8bd24b1c6f2dd9884239945ad8bd5a3e3355e8358f468b977031cfada30c21b7f2ef03ad56a82823c3e1c91dfa68adbb05ba46a2164f0ceaf188a06a5a0dea222f268f0a53e675fdb1390ddb94b206b620cdcc481611d95f84c3fc26f10c9a68c2fbe7a65ee8f0a77b31880b05ab838fc290f1cb30674159e917a87480045613d31523a6f9a5e2ce4b581bc465c2ace8ecaf7f9384678edce0639852d8051a133cbbd52916c51f5082cd001eec631591046d5d687e980b8d783774a9ac0bad37c1ce45b890c9e8c3d5a252c521185dfd29ad548b1c7df25b088a23c638000730a9dbebea12a22d22098c587edface860459538df24ef383f1492813d280fbe0f3fa5edf7e5394c880f03b660a59b45f323157f3319d8ceeea5f89a287903fbdaa9cbac4a1e533c653dac7cb310b7a9e70d8f0b299c7042dafff7a15a030c8155d413ad68870479061470502cb725b71c81ffb589beefd3cb6ac3b3ba960ef6bc5d3dcc729027579839ea1fc85f80dd98475c7b2639c4408b823e28ff5a01b67479683d819ce226a0415063cf1352f61c4de2444d266c79f19bc3b09fc8027ca6ed5891a5651db31dad9276cc9ad4703421a4d35f4503b488cf99c891170cb9dd2398745e9dd8ec08663be70f188434a0452834f0107c1a0f87e4d126540ec6b59a932b0c59bfcd84198ec1446a61967858ef3935380baab331260e88f00cc88df301fe73efc2f272253a782cc798fd3aed1b95239d841a05aef887ef4f09d4b2d7ee9128e3c96b10a7e44dd758bede1a6e4bf6dbed0ea7398544a71b500c41333087123b0acd62728da766ca3466a7e57974c0a3a6830f182aca90298400955942cc301ce2a8928f7cb0cb934aa403f88fcaa00c0f87dce077cc856e290d7c9ba0f3a68e73b3ed7ed9ac61b9a538e946a928e3d232f6ce3fc2a73728010edbf559cd92c5e9c2023230fed4110597f04bc6205b7ea71118bcb2e17ebe3503888a56b7fa07ec31ebf3835fa6677a4dc3b3e0bd04beed8ed6c8d44bde0859002c151cb8cdbaa8d492123913a2a32822998af7789781c597a0c25ccb6feecd0b922a2a95d279599bb069dd009d31439b85880b849e6764fb4057bf45ebe0520a23504a55d309bcf8047acdb7cea87cb9b0b8df7a0ec2b42a612b19a9a8bb1705143492161dca76f7bea764227e90333098a34e814467c8af94338aceb401680863cb9bec297d16cbb498f75985883e461f4cfec10f7c83e441f4fcbbf4b43103bbfe204531b7bc5b71c52a050332a26e8671fe34f99c4880880f20ed1ae18b031c20edc96573bf9c6381eec5251f6993d3b38c80b042ed89a3fac5fa311315067c024255b3d2c9a8495eee0b98bdd134e618fa813b4bab35bc5402f1b984780c8cf75fca25cb22ad2f8e1622f65fa28b31debcba6d99844672730b9cc1e5ad0ad5bc11001f7a9a553ac9d21f1692d7c99325d297f60a03ae94d8a72ae0f9f10e067346b81a967f54dedcb55237498529fc4c6620249eaf050151316cb32f3801db8ea69a379b13d806356f98e12d525a8b33f0011bf453ba00127529b0d42c0c66fadf308cfc4ee2a6a5dc80305d2b826476e790b5494059c9602b7721c8fe022137323329613116569827d489ba0a2251289642d926393664b45eb58c798f0a837b004f644bc4eb8af3dba3c812af12d03bc8f35f2bb6b42a6e3baeb66d81080b07b9bae9b864cb643fa2bcb561227f203c446a693fdc374857b90324d40e09ec58610d8c26ffa2f9fc5a0e383a5218206e88d36080f434a181e093f9c910044198cd93004dc3b40f53776a0617357fa380e0c098a89cc7cbf1e5cd3f9f710e3fa16150ad0246c7e811206ec333d9fb9361a300186e38c9091d9422817ccf01c5ba1239365049975c2a4ac5317766034e0bca30aeb1efd8bf11736652612608b9afb1d6de2745427a1ce4167385719e864bf6388d01cbc79dfe50436e8ccd05cb84983f885bc713ee50c4396d42579f87c66737171ddf70f2c1879ccc300107a932217d603d3eccf68c7b813ea30cf297570a700c121948b4de2c7cbd33340bfa834b46abdaa0bd8034a609e6f18977ffbb89d2e8b80e7860bcc7f3e298230347bab7975869b074689dedf8bc9660047c44bf51f099d47194c6200a8b903d0df32e43a7d909950f4b309db988a8c63a8ec316d800e77a0c0b1aff792d2a3001824b04b44b0ba33d7a2aa9b64e46da2ab4eeb3cd6263bd7a10cb6976b4cc820659cabaf967d5c8545e2d4970fd32f46cd876e1373adb2a852701fa4cd2ba32028f059694f643ad51d7443aea8f4a2b42b601ccdd9cf7f8c608cb2578c654710263f70bea3e1a2ac56b9bf614da99a9d2f7ba21dd7028eb98cc883da3bb273608d97a3aad2a8a0ea3a1cf6b0f523c87bd70b4c16bb6d41343997127c189526e01aa40c1ccad330febf7b2ee609cedfa62a470e56906587946cdd117b9672d5500b07622eec240e6c156901626beee7a4d03248c4f06b2045ff4ccb126710b360ff4a35c8b27c585331dfcb4cc35b3d3cf7a777c8882575447a2cdc6d0e9d3ab07c684f4145f148ab0929d3d68d308a2aa99a036073561d742bf02c3c320eb09062cb86e9c7f227ed5e5b7389c76f5bd833804665cd6222473af4efcb9677cdf0377d7f41497ecd8af3c94ca6aa578ead902c54c32bfa78edefa68cb78c27aed00904c30e5aebb5053cff4ff10191df3f96e3d62d98ccf2e95135c34d3e6d57c08b9602a1c1dc774de9bc5b1230138447e8e17fdeb3e9b54f5cf97ec93249f0f09a1584be84ea3abaa12a7033dce32a5e36b2233cea54c6e2e0dbc649b314eb80e53d92936d11cbf216f7d03e71bd8fdfc0a0ad9b947b07a6c98da4cad18cb3a04817aff0dfa04a3e636b35e659443478478ff36cc78c4193727860fc73c4afe05181594c1094316eb77a16cbc28734e985a217654b9ea96782e7482dd3c956e037600855662874180f3f19567369575b12b2d38d0e448260a07876637c6e14306174cfda397289e8bedbb9f673e95958a0dfbdb1c687f9ab875555701f9207305302b912db99c135e8addeba55ad4b23b06516ca30c9e249a97c0978b5e28230fb4ddf0c10d1ac29a1e5973fe2f8a185868eb757d019b66377278313aaff45b0f1b67d0640f7ca9525f3f5cb48019c24355c98522cb4f9823c8c3840b7fe86d0b18c00b7cd37f5740f6bc80e4cfbfe5ff246d35c0422c43d7b8c1163c279dc706bdf2a9dbaa3a87083bd5b51f98502ee1028e26373247cf828cc58bd2f89d3e0b224484e6598269c4b3b088c4e1f85fb270e9533029e1dfe583ef836c4e2cc80cd179800ea9d5346fa31aa75e543997980d07fc5bf52c7d83fbc1793717a9a409a0a51ccc232eb018cb7c6ffc4d641ca2b3b07bf8403bc1e4ce2f969cb466780da89bebdc221e3971e18ed5ca8d6a485df17a29fe14d5b020454a763285e2e0042eda423c57175f902315ee110268e9a80a93b8dc151e31e9b52830f3ea4e6702801d8e64129ce1dda408296eefd506efe5a54ad294973d44aa6187f60d7a660c9efaf9c7c2c705c294b01c7eb0cc77e4a0287f3fcabc4490f585b67b81545204d960c1cde195cc772f6724eeb19d53b5e7e8d584a6f5fa645a1c0197de029b0ff4b5ee0b565286d590ff6294a41eba4c361bc7487b847cbd75868fbf1397ed01e3454ff133b40ab69e62853e697b25989e2891a83d2350f7fea2799f72e7e50613359e95bb1588be93813b9b4a937cd50ddca23d0337ba5fb614c02f16ec960e18241410d5de3f0eef5529292a2260d8157e1e86adafec2d6970a443d0455909ca69ef698463191423a38dacb04067f09bcf201194394f4e69428b2306222809a678cf80869386f5dd55cf411e4051240b5de748cf8c7c05c1218e5f698d5f02765eba9789ea0830afbe1ff840d9e078f1b5c8176c63e6eb3631f8d4287f640ff10e1e15326a8032fbd03e115e6567543ad066b575b5704eab8161a106dfeb025753cd27ab822583782f9fd7af8f5556b939c000c08c39e8be5efd381578ca0a30e45f1e6a023ffc480ec3ec56a571913036687111d4fd4b56d0f0680c4055056c2068a655a2053effb1e6922f8842fd7561a170f139c5d6409db53d0385230f80d1ce8e4e17f0d955caacb03f213891c8e3eea28ea3139e34f12cbde27ba10b92458e8cc3e7d6c27a1b27458f5767ce49e05622630ceee2a77319d650847200fa5359ecd2e711b5350351d694763a466102c5dd678b0abe24c3fca7ffbfd40785cecd247cf8e4192f1658f30dbe3d2aa053d47d2cc8fd975160c96a97d4570596064edd2335fba607cd9f0a08e8de231177b8fa5049c596f8fa0394390cfb03ac766a956ebd934e92837d006a7ce6eed2698204f86dddd1ad22de21597005011145cf05329da6d4d90a38c0978407fbf510480c38f71f8d284abeac8d7bac0742ffe5faaf659b9fa66db0b9ae6ae04ac953f322aa842eddd3df4eb400fe650bbedc049db65ba6e4cc3507581222d55e1567dba86d5b3a0a6f02ae38dc30a207bf4aba3d57c4ea21e5c558f028f122f600640eb4201813db93292842d4dd4d0259cace6b296ba7ccc955575c846149e40bcb9b5e0cd119005440986f28abac03a66193e144837a392059b1c75efa31c11fd67ed08c73721083180a8569732907217404c1f24c7507a73575a992ee8d1aedfa53a4fe25153980478986a6dd7601cbe40cc4f5da4be094a3568651e7207fb3238024821cef0f1a318848bff88f06e5bec5a7c55dd04986edb3c9f87babc05c7372fb1abfd7fcdd2578dca21c330d61d0e14d79d86e1d4d8ae7bfd13348013f445fdcc296e1d5967e3b706fd2800048430b9677ad3b2941658fa122072a5cc6001ab0679d944ad56cfe0dd400e004d279ed38a8cb12be40c283a3d39015b2baf9434495d69b2f5248633dc451ad00de16077c7ca28e640fc29e76b9fcd0bfb21d92e315ac94a821a202c54a8bc405794412367fd2358310c23fcf58f25f3cb3b4412eb6eb237132c63e41931e6a007272d0477c69acc2a53b6680c2e19d96b754294b2c0571ad27f63ef31e3f6c02ad0041a4e496b13a731093116f4f70bc290e9e054c1fad7b60b2c44b05339f058494830e4f1ad18d36aaba939c72e945a1b6a944c9e70af09ca38a1d3a27a001e2fc714f37b0093a61dd4a5ecb26693f08b18fe5374655185fbf3222f2cefa0a2dce57c9e150552b51e93711c03dc9f08cd19d8fadcfb93a499b09f8f512e60416651bcc6884b28abe0364ced3db361e7162a43bb1c4c7f495214aae505f9f0f49aebff9721a7c1f904d2b0312ddff67280471f47658a9aacf1422214c910f00ae2940310116abb8be25625cf60fb12849af4a1f4524d4c6d72e34420ceff304f7002a5087f2d989eb926cb0df5342ab374971ddb4f44611e39b29982b27ae05faa1d9516284ade981fef4551eca1299e0a537056e80d0f175f55362947e7c086a9e2ca78e2cbfd130695ca6ffc5b837223d20391ca63f2bc6f4e16b96f872047655ae33aff5fc1b009c727b92f3e8e85ea44c2b36e10c5a697bfd855634b707825553e42a61a248bc11f3cf5122f39645c3f28d82d9735c5a199999eaa4ed085feed29ac8a49e1cea72d7e06856ac8f838fba7f884cb1889526ce2299a6db09a2bfecbb9b3c1097aa402e63e3314211875a767a3c8fd8443ab7afcbea02860d2f3e5237b699336006f4f5c87de2b79b2f95a8f0a145c92fb3dd19a771acf00808fde99306f68dc928ec516d010edf28659f241464f58bd907f0c3ec3eb2c00046977c1c99abd5bc62746c97abc516e8e8960e5c746d93396e7e65157c80fb0462296c4e7069ea5b67c43096f01d3487c22356266df8e40bf499f368f4fa68014843a162d12108825f5041e02a9650826f9ac65a09777b740e176ea89387c103c136c76bdcdb3d4b8c0a0f27a0074184cf522d6f8928110c29b61cb6698cf20ff042f65763c98d6d42e75b604ae5132540959239140660babf243d065387290c685a4be6afa5d5975186a4e74bf6293f84a98451c367b30d81161426c48d3108c08561a449b83cf0ecc6655005e50a33ef9050bd2bf51b988c4d7079f3418c021d8ea9e0aae413cfb2432830f7422a5ac52a6b441299e268ed1f81f3e5e67708e52947d9fbcbf41b234b634c33b93f322c90acc4b8bc3cd770a225351e4c7e040ed08a218227e47d298a7b8ac87cda261ef02d3c620ebdfca2b3a49962e5ea08704465fd6e0e09741b86c7d52ead52cb5d7fff67e0e9b8e4ab68034845fd62004cfe4b7a23574f13667a5bc437ca3834bb5eaa0f9560f25c6d2aaba24c3a2a0ee7f680e1bf40a99dd4a2b9fe34d9b49002c1101a972b02c3a9979c88fbba9e0bc40315fdb69f0a4099cb1212a6ab21bd498971ac6cb83035fa9f79a91af80003198da5a4f008f096b2916cc9018a722897c5449bbcdc21bc3b9d2cbc191dec095b920c3427c8873d0d80585b821b7355b596bf599f43f43574b943cdba9e3c0e4a283bf2e09980aa1d6595d4dc82ce259573985cab36bc6e88d0d15781e9570782b8fd324dde47c770035305cfec214594cbe27f2f489f171a3af683530a8c0371a9981cad956ff148e5b5613b1b09858e0b6694bd91eded261a31b7d5fc4a09d18ab60244667c550a373987604eee385277e91c876802fcd29723276b18a702cb196d921d9bedf50882880af01077b3ae21680a2a6117005c3ba2e28a4d760dfc13c0dc1539546597f9c73971e78e974957cf47cff2246749fdd432e87a2a0fd73febb840d36c0d1acb1c621628ad95fabf760d4025c42e81cf191b24e27505bd83810b70adafced3ee014f1be314510c178f4f08f8604c72757535e2781b02da44dd20fb370f55c549246147d956790ac7e434bdc0fa54bfe79011f25786041896e1fae1d7510f04acc5d52159b77d863ad12a0964007edcbc8026a8dc07095e697830b91b93f61a899e243d4b8e1fedb71349bc98b43e050f0ebc1b44800ca57d9ffe46012f8cf7987ccb64e7d587efc04efc2ebb65bbc616e9b56ac56c0b1a64765538a8abfbf76d1f427cd9d414c2a15495f27732957aad53f7ce6b5001722872f57a207e287aad85899f56afcbd9b50354c390fe0a65b507a1b4d7630b4ed4a7aad4f43ea4ede294a1d704c4f94fdc76397a95b79f8fec4e2335cc0502111eaab1daa403ffd2102f08463128343d575752e091c8ed822cda6a9081b906043c7f310acf29008bd9fd70221f5bf36ed3612214d6b72818074b31b024b107acf7fec035feee6b0b6163e34b94d9a5006b3571156895c0357777fe376d520c3cc822b8aab080d518d60800757f31219c4eedc4be01c4811374b8672640040eab57827adf0db739b287caaf4ead1985e0501ddbf9b188ec33390ba3f6f6e907de59d7ed498f455102794e657b0a4eff3e915491229a093cb92e1b11232cae023d86740421f325a5e6823f7ad9fb8f33bc80689c2309d11f5c0ad0f54e3f6502ac0fb1e375edac7ccda24d2f233a43faa62923dd86e0f419f38c934d46e0bb0222b2919443a9c86397c9c68a8628d8da8eb471353e4c4831531392fe4db7890fda7316244a7e7d1b330f7ff53afe38b8caf2f4dd0aedb5d85affe4ac0f023c0af513fd30aafff9391fb611a05fbc7662b17eb6155b95776ca97cd355e771d50b680a8473e8c3b431c278af14bb7fe06d774c5f694d4aa60ec09b957f305f930a91df849c7f928a76b5ad82a243e6eba1d9e419b95971c284a0e8dd372b9ebc0fea9a715c413ca9c80b1f933bd456a5ea4b00edd39f21e94fed027e73e8b6880f9f663d7fbc967201941a2541e07f24793cf30fe68ba60d2170b1230a5bb3040c53eb3cebca277d235924b8a820f02839d562e4a3bc61f1ce4e6092cfc3b0f20f716ef989a1983022ab93f65378e566a84e275ea9eb7f61d8925719088aaf840bd5d706f0bbd2a9dfc560c93762477883f42f569fd5d3e26680df46e237758a0fddfb1dbd18be7ad7a00c469c80fd67a9aad3c18041a11584bfc9a9da64c6fc0f0fbb67b75b2ece3d4a35fb62bfef767ca380d9aed484f55c610c2291dca99f08902a5506517ff066a09ae5eb8b874483cf1e49898d3c179d77abaa6b7633750b1b6992df05cdb42d2a06afd91ce81ffb3ae94db64eab20e8b874ad74379e910999a563a734d4d526672c681bfd6daec2144db6f7dc5b60b48898c7c446279c0f1ef2ed658a2b57b73e05d4054ab1fad8747a198a2cd6c8f770e13581839e980594789bb8b495ec9e858f00c324d91cf3a1ff0e325dfe3ac4918c696793d91503723337b509f0fbdbb19680a57d97798d17acaa7541ccbbacbd4e88df0c6ef8047c0bc025e9b7a315e275f849c44077290b5d1bdb37c9e2e4229fc81dfaafee031a800e417172ecd939b3c07fc809e5d45cac2bc889c0f94b8067a6219ade950f6f02f90dc4801d9829ddcfd91d2f25e129bf2f693354639232d9252ff3d1dd0fcf3dd08383407d8dfd80a342ffa626d281418b39ed4fbb2c3f2b7f079b763a032fe290004d9a2c6ecdc67ecdbe6856959819f9714ea66f8d2648d70b6d4b1009cbce66ad0d604e1a08b8efeb6f3ae94038ee0e3ca2982bce39f63716c31e2603e87381e79a42c5fa2f09d7dddc42112abb7bf01c38f30ae96e84ec483fc5ef0692332d2e202fe138e37e19c86bc1f0f7e7c6316dd802b450968f99951450cb065ae7a36575bfe4be93745b15f2b60c4189d2a5939a899d1925b1bcc562875d0c032f37e9e233d9cdfd7fbcc4480f2f2f4a5488681f5b7548092ec5e216ccb40bdd8a66e35a5cf07aafc56ded57ceaffe7535a3444660e0dd580d671e4030c504c542afe46b8c3682efcd543f14db2e696023b21c8fda5be1c7a48353c666410a31395db259547f3b1508824e64dfe79b644335317d9c3bec40636d586824e70ee0e8844085cbd16933a077dda855dc03243c2dffada00829a7e3d298a0600a0832473720af3e7b4a59f8b6c3ae065a8864971760ab29868ac20f36b253f3bd030664912f3bdfbb135fd789518dba21dad9a421a9c7bc9045d2a6f328a2f45304db87c54c54511d23416276277a841d1fc3702d94d5aafce352b7d20389d1620e8117ff7fd899d3cac952621899057d29b0cce2ffd55a22a1e2aa8aff39dc7002acf8cee076b33ddf702a0dd8ab3f0dd27ce69d00c18ff1aa971d64716cc21003e804d97abd6f0237eb0780a3992f784c26e473eb175fd34cd5c087880e24b60e1637fd69172121b83512f8f2d9b7819850e7487e18cbbd089856f1f846e2d7078f56b3d04d21610a98c937378ef84834e8a53ba143e1719cd0055f4bf9d82302afefaf5386165192163b8ad123c965045282ff5196df2019b6d6d77ede0352018fe829d92830bc46f13453d4bde77bc3f7dce5213413b8e95a5e28c80069820ae100e1877d835002cb1ef27056f0d943067bb48e5ff152e89f316ce30ae1ce04403716f3bd8088fb099cf040d89655cd68c30a020a37775fb7073ea6bbed3c0b6cbb97330b81d06dadcfa00984a15975d35d80117b628492301e117eff02570fe4b93e83eeba2f464644a9ddace1c94de37e96350ae8b34e9f70feaff9dc72042552311eeb9babe57a0a698842fc744bb2b580082a3d8a0185100d071779700d10d2d66e606a9a84c88994c97281ad8818f33697e565a58613e80f012957960c2d6edb2be55752ec72a550cd059831c9d8aac300d6b0a6e5b712856a3d42830ae615de9394829525723f4bbf23835dadbcb77a810668b530034c4a68e3bf51071b39045242d45515c769382c17f0be2e7cf4d42291383e9ade683f4ede553200536e19f3913169c30392feeb9e8ed89565540a4685950f0999ccf053ac424e0682fee7f19ef2c641748e9ab0d9b2f1f0f353d9f97ddae5243eba624ff3771a003f815d737f01fc4423ace1619c0d9836aab4fd8a567e8297e6ab7a4c7e0ffc07b9e4a4132797718f091fc95f2c24efbbdd65e4cd178ed5c8a86d0491d1a0a409f2fae70d8727d5f97d811a77aa6850baf902cc0124dd2018c93b46ad62136a00a6e3fb310c67cc4f9fe76576bde9aad2b5c4d9a3dd9e6438409a1beb6010ad039000741463986a3437c56a030eac0e06ff27bed63a10976bd986915fb5200e078bd3b2912e7880ba0892fcfaa1979543982d77490bfd1f38f5b1f0699204bc0457eea95bb70bee9f00e8f528db929c3def5ca2abfd09138b801104269e1dcb0f9523465f1603c16fcbf94edeb1cbdc252a2d32bb0c27a147a77a098b49f3c40ca39dfe5f48fbd3123d0b0284d06d191dfd5c34dc35a9f62026a17eb99a8f6d0ea2e283258b817c0753f7236d283dcae334f688b74aefe14fbd1fe7753bb864000a902172d7addd91fed0bd9d832b1db0451acbe81f36c93018da4d81f9fcb60724c37257bafc92f991f26abe8ccfb6b04d191660f2395336e1a62fd2c938c607dee98fc1b1be52f388bee57a05bcd5623e9df9f3b22bde1c49ad3ced67d916030a1b923b7528715e2687581f463e05254c0e49f24daff05f7544240ff9174005195030c0547c6f62663131d6b6513f0b261ea96e55063b3e82df7253903a3e0bcfdb239acf3dc20d5fb12914cad9c6a31ee8ca05c7433670ccbf1971ee4bfd0ec5e1691e1f5ac1581ca41011d916bd17ad7d47e95f18b1ec77d99ff2f4a4a50fcd80ec9ecb40ada53358ab55456ea5e81fd2d8eb2a58fe95ad530e6096f5080ad052df81fa1bcf3b8876465e0d41fb50270ca93313cd303353d29ee90c73840a5aa3fc29c9fd357d6f8c9d5730c0eb5acacab055438f1ae5caddc13dc35d810f false -check_ring_signature f00ba4ced939d2adda75cec22df76daf913bb54562cf5609c805628c73ccf224 4d2b017cdd0829b0538f687df94280a88527b41854983e3cde8e1dd43de49def 3 644e786c1bd1ccec24592c74e6eeaa1919c8f7b6694b84cb5899598a19ceb44e b8039cfb297bbc8346918efb0507c5a1f55901cc86ca619fdd2e451c4b7174e9 7128c99947e440a59bbb8ec3b9d32920a34d2522be9667ba3f9f50837dc6479c 80dc5c081eb91a711eed42910a97b464df50acee36a37a9d37422de6239cd007b503f42ee36fd77925fb88c27183ed0ab59d24cadfcd4e946beac71ca22f81062dc2b9fd018c278bf1943c8b67fe677799e35e27a6b61a9d64ccb80ef2129f05ed08d598a92209db95a1aa983897ee9718fafabff5b8195d7840fc5e344125021cfa8a7f5adee78bb4595bb20b45fb5706c4012776ef77b8018354e5eefe0a0d30dfd0e6706f3b1359cb22abb53af67a1a16a7c2ca7f3e21b6b523b04e77b20a false -check_ring_signature a1f99de03d552a1c2d361e222f1a73d008d9e5cf7b40ad1deb58f0bb645cdf35 90b0a5a943a3ce117fd2844deb4764436f4d0ffe67d8c9dcb22bbef7413a65d5 4 f66c56021478c270e6b65b5df3969b3fa1362d307938f8f87787a7ba6393e123 08f0a3a51a4b581b67716c20eff41f69bd27e2af31ee88b871a493ededab5bf9 c82976cd10733e65151e0ff97ebf0e69d4e978dfe54b3bbfe1de3af18361c831 c2945b5ab160b56c729286c7b1c71eec8fd63e7290ee5ec459319946de43c5a9 c000d57e92f078c960413dd3e3a6a7c0e58617603ad5eef75826dc58b222270aa4f09ebbe1d6d6d2a254c1bd26192c273ee0c78e00386eacbeec1d6589ec6303a7f26d9c4541b7e2f85190add351e87db7cba3c24d7bf9fa393409ae0dd15b2b829906e86cbad842b167708f5e06e516b1bbc00a2a0700b745b1869379732821eccf17469c838d9f26535185e85bf32ea88ade18fa44f37dd0f3037ec8c08c0345832aac3ac845c1e3d8acf48a8d3ad7e0242087699067051bfe6ea5176b66079d22cf425f00879ce8a0ba69e5c3bc933ec94b82b6bf942d6bf05c36a9c9fd01247ff78f1202654b9638e32b1b640e14caa9732335c69deb12f48a55b946910f false -check_ring_signature 0b60fcf9ce81d250d5bcdddf8d90e271580e4b4d9813ab5b41696bfe676051eb 5df0462c742cd7ad19c740fc4f1ad93458bdcffb28b9e98ca7f3588953b988cd 2 3710c0113f916a64bd3dbdf135ffac2ca6a69b692c05ba1130adf7cfd754d073 c441b10968733cf1ea69cae75f4cca8d2997fd6292407077320f218300e8436a 4201b10fd7a9e81e47d670304cdd3155cea587d5d4c81f3786bd59d7a5df070c61ad1be9a33501d60c84344bb048e63d7e689396af8576557a2fcb32eb6bc6014d031acc40e04188129692983a10dc05b11a772aeaf44a761546c0b46eec8e053cebb34f025ba477745606be37879a5f987b5ff743dc20c45d4fe5f210f6640d false -check_ring_signature 96adf554d7876ce4d458382b80d3df464f98c6c14360272a1127d047683ff958 73b10c5f3af7d080faadc1847230b62a8281ad007329489302dc1129117b37d4 6 62904f746306be32c609e5109982cae3b8192afe73ed16f5bcf663bd82bf084d 9d59254b1a5e225ad4e93c6a3f696e3f9fdbccc86b472fd3dd9bffb9b3a7ea7e 4cfd412108ce42fbd31f6d07dafb9da1e23ec8023bfeed42bc79d17a9dc6c696 40998afe0f8c7c25e639f1b6d7176e365353692a769e5720138ddc5428676d02 45352c3dd3a3ec8dcc992502734d2f2df27a2f1a3a911a416c04b09bd82395fd be25f8650a8bffacc8bd992423ac98fa2680c2feff1eddb51a20f017cfa80ff2 6bbb82953d1bae7951e2e3dbb820fc01873ce416f35fd6e349d06fcc98f58b0b82c4c5ca0dfea16c217f68f1058561dd653af0010452b45b0ba0ca31afb4670534bcb19b03122fc9fb7eb69e54c00479c7451ea3da6431e0a986f89bfdef4e0946e38cf7fee9762901987821b4e761e3d9c0ceaa91586943072312c469f97d0a9968540d564ca5a251d7667768d93a7810ebf26a2ab6baeda7daf92d52d91402fc54feabb840171a5f4114e1a573287adbfdc9a6b01610ced15e8e4f8b8f540594ae9fb86548fa768e562e0d0bc85aa934987470638ec1064d9ff3b4cb61d309c5b40623e803933228e6109f2491afda66b504f021013ce7b31096d26705b90225a63e5d6d2fba1c5dcdeb45934aeec0f834bcc9050021f7ea869cbcb701a0bf9e8ea5213e6924359fc287cf2d48c781c0f5687adb888a1502c3b22961b89109ec7da6e76cbdecaa05ab1e490c8cc86df7deca0b51b36e30d2ae4ee5a6d2fa04eac04f9eb01f0f9f7383e4873a084a76a517149daabff147bbe3d17752ab4b0f false -check_ring_signature b2589d538feef4e07fbe1bbfd6c002f3dd7d32948776bb52f7e3e823a7e5097e c643dd70efdd8480fb26822e756f575c0e90edf86a94b08916a6a99bbd2ca072 1 41134ba65aaeddc907abd04e42024ca874985f8c27839792d0805c77b2352ef1 bd0f076d86b6f3078892b97a6362e9e0d487efe9c842a207086cb70cb7046373cea31aba1abc6aefd3ff806d70784814aa86dbceb741a5d2f5e49934e96e5def false -check_ring_signature 306d61565b308ad1807387cf424851b619dfc32eeb5e85b4de6cb840bf4823c9 69322cdbcb4e99ad8dd0c2cd405c4bdcf45d7f7f5c43b45e9a4e8ad5f424097d 4 cb9665883851d83c67f411158a1d2dd0b899b0551197b959d34041eb647ebf5d cb862bd4a99d0a8f0af7a689b072fad9cc56bcc39959a95d6a66b58312448eeb 4100359a897faa48719c250304efcdd9d003fc99af7f397064de9e538ce72d10 bea54c6b1a6b2593257fc6c3774c0e7170a40319662a26ba5a31bae0ef74e56b f5f10c56430467996b87a153bd6f1c6a8c35afa5620775fdaa2784442ac607023bd331e2fff146eb1a6bb73dff01586e72f4b0861e94cd362edce43770b8ba09ec583ebe5c4e383ea0669fd3205c4d8f944e6ce368fe640e19c5cb9cd5090a0b39277ea5b558a6487e2eb04814819c8e16ada5187247222a5fc68f84cf4cc205080aa8592260637ce340468c4776dbe509f555a1458fdd314dae51517ac0ed03e665bfc75bf4c098c2b4a9d8eca2b9cf36311d035ed2a519ec82ec167afb0c01342137e82ea66fb1a47c3ca68bd068bb14de11cb7770c433f3c4f9a63615e800bfdb3469d91d476334b8762fe1773376bd9eb464d648af50cc6c899dc4815c0d false -check_ring_signature dbed4e8544cbcf443ca6239bf2e648f3d42684fb6ffcba51351f699b159e17f9 c3511578e521fe002d9f2d52387dccae531f08ab5caa13ae456fc15ef2d6b685 21 969e9e5a5c33814eeba97aae83ea8bcc5156606783ab07a5ab02cb05600240b2 d8801d1a783f6d3fc744de4fba76adfc523b100cdad344a7edbb68c24c3a7b52 fa2c52944abc360e909f24492f063d982c062a13a1d9efaef89854fe2032f669 244732b29c162e1c1df6cbcced1c7214c7a55366381127e5771ded445862ca92 ac9c636cd84486288e90fdf8c64ca093c0bd2d32b2e12bbe3e4568428c6e3604 d6efbe84049c1d2c7fd42287297a073dc411df7a8777ccc3f815a689f1cdacb1 df0b2564cf5ac657493d44c93588beedfa3369f51c4a1f96d289df14621f44fb 7c868b8a008cb4fa4b851049d5fbdd9bb7b44307af4c9da4c959ed5744769b17 2332a21af5912bd7b12a2eeb3796a8309c9513b7d14fa9950a1681a914d40978 1f4c29dd3cae813689c4a6ed57439100336c4c28647eac2cba047baf2ccf55e9 d7831cbf871788bc26f7369cebaaf9ac627df63c0bde0e5297a8643568dcb129 5ff8c6ba34f914ba9ec1dadf46ca2ca59cf308ee708e1abcb3ccdb0dcf3c1883 e228e451416507e8c3a851d11b4f0d287729186ec07a25f746a7f8e310d77043 28cb903a5ccfecb4699003bdfb87d7761495f3534d953cfdfd3a6357a1c5eaca a57d3763ac7fe0af6ca8bc81dade38c8a120d4695b5b8dea647b51a250c05e0e ed2512808b1e469900bb8b76e82d7f69739d7bfec79c39247d502ebd5912a37f 124349558fc7dc5d81c48f7717400838901b65115d05d86914d162f992e57987 59385560d91609d9e740690a1f35e13b88fac4b0cfe89f84128e7dc839cdedbd f49d6a216654a8a80fbc77706501fbf4bc05c48b64144f0b11985c02256ec34a 3cb5a812d53547ff4c8510b503d9cc1c03e4eb82c96bd9e91b5e68dd76be9129 cbfe5485ca576e19d9b3f5df45441117c2e92ad0f030b2b22a754c18cd52619b de87bbabc98ae17046d5a46d38f753167f7e6832cf3ac7aa38d9ac1bb53c1c082e2cec2df2c70197a0ddeafe5d3d3929009c74272f08cfebc84c6cf0c1239905878c4be2017e9f130c6e51327e5be0bbdb0229904fe66b45c54da603f52a4b02b83933b44f4f7d3f7231a7db12c9e902366b0cab686a521fd75165b660e8c60fc4098139074e41a2a15c8f0119f7828ae2d090052e854d09cfbd71ab693c380c08af8c56108a002754c8cf3cc03bc34834b0b1d5596a7acb20fe27a2936e1505b53226434d582d65eb94fde28f262e560e70d1690247e1e8ac14b0f2c1c60f0152bcf97b9e2cdce4479bdec0c477f0d594d2d58def6052e0358a295a9c577a068869301bab04ad67656da966bb1761e91d9a640a2f2f79cb143ea07fe6ca1c0668b6ee5f018bd2d250f474ae051c58d5146604c07aa7df33639333c5baa12b03663c93000357839d0313a17d0e2369adbfe10fa5b5804a2b8873fe22a2cda0011d19f818555fef8c9bd49e507bffe78a43d527ca8a9596ee6483d8a36f7f9b01b85b665156a6f2e31660d473dad2bd8a30e73b3ab3700a65e95cf60a8c600205ba27106e9f1ddbbb9d8baffc754b4a8b045cf89117113c1571cbb1413a549a005f30a2c78fb797c7716de8312d343dc537304b0837bfa72b8e4a9c13b171e10e688519a613fc9fded94343f71ba1a1926eb74f69c91f2519632dbcf6a97d5a0333de79442ece1d0a7a03676c113af8b180944317f7babc58c56516ee925ab7031aa7ed261662d095be0c1da0538001d61671a2e6164051c2ed6773ea9a669f04ee8aa9f47bb9330da10db8984ce15ae831731f43880d2ab48efc2d436f865d067a90aa1b5fa4bc99eca87d9d16bbad65c1dc0b3e30a2c4b0c7937703870ad00ae10f0cfdb64f1d5ca031bd4dbf7bf4e6249ef50ceafec8b0e37894eb0eca6f014262ffe4a4ad2a78eb7bef8777d4922edaffaf71a8e7988e62e1472d6da711051cdfcf57e5232e5ed8e3fd8e69020ec1d05c0868bc735d19f024623eec0a6d0ec2d73ff519badda6d7833cba8d7e1fa95de8805df7772a7cb967a41f43cd000dc51e2b7082512500121fd055a6b3e4bab4fdbac74df8e78e651b92b2de41b8019c8bc8302f1ccc7f181bb736a754e1443d493e27bf46eab66989523e99c6210c66f94070ee98629f0b1bc7cd3aa47a8d47638ee63bf184782b3074ac4efd43042b3105e997f2f0803479b189fc7d59ca355877c672f475f692c87d5e1c57540a04e16327e2d721b106c4a3717c1c753244de9c3fa2f6cec018eea529796c4704a3352e39bdbe3fe695ca38c5ca14076727cfcbe4bc2cd85a2e09790af3d7df071e1e637a3eaf58ac1ca8cc236025eee96d53023cea323a6bde142701c2f1ed0e0cd4657f9b2f86d4de95fcabc6a0890b168839eb6f02eda53d221ebe63285f090801c5e87be8cc2533d4dcdc392c5e8dce59981788f8d04405dce382949667012a12dae80d87519c27ae020655d67c3d6266fc50e018c10c0e1ca5d8baf67b05600c3662e8b9ee9274cce6ecd57c53cef66f62300d3d1fb4215649bef60f35038632cd5bb461b4ed86375d3b6d2d708a3d7a44873a45779b44c9ae219807770f1173c562312e6956545cbcf70a459edaa63de80cbd6fec7e7248c08ba1d2630c1d165dc2d3340e0b9587f9a313efd5260f05a94286122535b2846d7dca1df806ed9afecb81c560d61f7f9ed9408c8d5b0e52744e2407b4f0d82218b5737ac40ce10fb1144baca4bc4ea1609704c2766e08d1cf2ccc7149e41b22d57a7bb3d50ac734e6b1a93366f2a9e1550db5a4545769ca3bc1cc1e45ec510c0f5ef40c8b02a57bad0857fe0b61565a62916545240050901c9d0577b493c9270bfff2d21b0e true -check_ring_signature b12af8644fa4679ec3ffda70f3a1ef45bda79cadf599e7030599e31569ad67cd 5d3b86f0a1c74a94bad67c26dc1efe4124a23dd0f288e892ac94280e9c0a2298 13 14c152999db195ddb85a473f2c81111f6ff367553870858ac7a3e04c2ffbf2db db0c29f942be12be89eacd1b81536cfe9cd7a24c35b6a6931b338957d486f30a cbcd801b2f7d65c74cffe5532eb29275f04fc608238bb0b69d13b147a1e89ce6 6c58d89bfe916c6643e1b8e2cdbad88776a7ecd4011224b77595583f32479544 72d8320049bac65b47de2bd7729a2932c14624a45658d1562e6d4b9c819df85a 7a81be49e05475405bfb21da084f55a0944f561ecf05b9e500cf5b1a37ea0d46 5ccb9a6ede32255e65770fb6defc620ce4134eae6b516005605e78d586329a19 167357b9d305f550d52a881c6809cd393671289d27acf97496ec07e3eb345d55 006ce5beb645d46f2da5016edb7048b0a67780a54375c6678548298c6e498696 8703c3cdb32995dc8fec8cd381cb25a7690392d72f201e0f6ea88a1a2dc26e6e fa6d432c074ce19c76a743541115b2ad5fe79d4937b209a823a24603dd401a07 8c0e64c8cb2ad110df7b3103be57a2ab37e947d2d5779ad9cb3a78648be0bc2e 168f285bb902507adf758d8227f836d814e73ea9d658c0cfe6147d712953c878 270642d4912758fd317b9bad43c99d9154a87e3f6afed31794224464677c3c007eeb28f84838bfb4e8956344c7b9427d8d0d1820146cf219eb27dafb9a562a07c7c7cebd8e498281c567927031cec2ecd42ffc8ae54f76267e495c33b51028050a0c35615f864b083433d438ea37d8ab288dd09ac785684995b27dfeb550950aca64585e6fdd97ea60e154dd9e2922dbbf7e77a62339285f847c1e24917a7d01cf0bfd649a9c98fe1d896d950a07a4530993dbd6eb784427bd8a14d5e1d36f047e8a80f7a1b2f05082e829d2bfa8a1bd04d336002719cf5c6a31c2e93f06c70390a9c7c3468ddae2abe1567e8866eeab5b8ce930b960f4ab518fcf7a80db4301f0b6f91ba7720b868633e1ebed492dbe44d17ceae7f2da9dbe8ea5cf92ec460cdb3a3d872a1f587119c41290ba2b6d042fbcb729c7b9775d700746b37e159c026b4aec47c2461dba77c09b0609a2c29ff414eaeee2ce62b95f3827f56018db07237ed3730571a5d096208260fdc934929b070300d7cf61e374c0b858a465790125edaf5214505ddc23d396e886346dafcc2c9d433f8610037a21e7a518246a0cde049068d30000d90762f16c37b7c833041157127abdbd531fdda8a06231900fb8e24898e434bd4ddd15315e4b76391392b4c5b59bd9cad989a55693ca928306c4ed4f9c9d16f2ba1485f974358ef4e452d895639aa2df83473d6011121ae707d8de201ae2f419e4b85cebacecf3e93fb8cbaec92f423ada07a3af2cdab05905bc24f15fe52117d3d71263c557ab47a8799c9ece97fac4c647fef164c53c1f05f831b5c0f1326e6181e95b40e3b9a9bf85f6258ea226e90b77bd80a881a06c02062d6e44203929b76d6db39fc9d35b94a89ed3d75e2c82e3b950b2869500110409d2c3208f195b8383f4c9837765add16b1de76c3f141a025d1007547ae1630fe82ef8d32965df6fea94fbaffd3942178ca8240a5c597fb94df6086fb4a5fe05fc05f8f34b3f6cfd42732aa7167c6ea4120736581025ae87d58521990447f90934f0db95f2c61bdde2108df19d4775e1b064fb62088dcd4cd7ba5f0a3950cc05d667e3b4f293ac8b62b7d0adcc13eb08eee218d5266bbb6614d295b359eb3f036a71610705e2922f28ca02c6940d72df09cbe784854db9a5be3b6792754dd509 true -check_ring_signature 114f12bd487652d2c2009b2fb6cabf82eb120f3b6e67931c9f26d575a6c98f0a 55453f54cc82ccc50c5ab905296640954fa911bc3428ba19117b46f9ff324f33 1 f20b734c21b8fef3c4234904601d5d8c3fa2bdc65eda49888f5b5e8df5d004ef 85116e52243408a95fad77f1aa91aeb32d78a460961a564437de2be3318db40ae0eebda05727edde3979e9f2ff4bd4c59e9ec56e2b420acaba31fe88e04f1704 true -check_ring_signature a931f47922a8e014f6527de7dc560ed1ea3b419c1cc4b346ea67efa031cdcda4 9f941bc2fa01c4295dac1e89de92603daf2b3028ff7076c189a926e6da785f42 14 530a7ab18ddd6058a96e38677ec15e4b2e6cde428901e93996f8faf06735e0b3 4b660db4cb8a7d540e4e07e2def3f751d568c48399f4e61d8deb82f4470828ba 507627fcebba8cb293aabd4c47073ee368a9b4665b6f31824cfdf761c7733f38 61c4e6ef661a7afd0ac67cc563e9e7630bdb28f16fe41899e671d92554ae9465 5e7b71abd97f0681cc87f2939c9b58dd5eb89db3983e48ae3ec8ebf295dbb196 b126880292dba85b934d422ce849a36d42a55b8e43b3acbb54edc58cb738c2f4 26ea0df805ba9372c111e6c64bdc64fa44bc48f33dff7e8a6b950f40f65f75e5 ca146fda6f8458ad889c53db126a41601d16750d536a3d984f8dd2a5096abc7a ad3ff646496a64feb668ee7e75fd8678a5cb48c9b4d4cf89cc47266d00d555af 59d2faf15db2b0f2e28bdd844f9cd0e51ad733010f24180141648c8be290fa55 72997837ccdb375b227c1d84159f9c54f3d0950c3f2acac395470df6da9ee4e7 4553d8c30d59173f8c6b614f989ff0163dea32f7648a51d776812a5351088156 a5fad48732945085029cc01b12e607e195f471f56780f0be5910ca0d26b2eadf f286064970fd2d7923f6506f8eb9eb4ca0fdca013d044282ad671b5b88cefaf6 2cc938f0271603beb10eaa2fca5683e05376b369c0d25285d3b09061d9dcb80b5c6cca9bd3cd9f2c32ee23159cf8a042c9ee54c6506f4ff15b4bd73fed9b380f17bf34c5e30347a80039a5f8e2cac4a9a4ba390b32dca13eea38c80089a6fa0750dd63daf36e8214c02f00deaaa7bacc2e4f49de5a529a629f44146c2935b90aba67069c58556794b83380db993ecb95a5a0ce6b165b86b781d123d1be718b079cdaab8eed6212639395401e38b1d9d9c41418d2d87b6f7ac538124b49b2ab090d4589d14d13de46fa5a4943af498dd22bfecfea0f709757fe07c7bd9318010da8d5b50df2c6a19380f8f78bf20f484c810501401fbdcfb2a523d3a845818509209f11498878358fb660f9a8d93e81765d8b2de74d81d7c3e61770a38bf85704bfb16d5ea8840fde7691a8908aa4044b35dd9a85519003a359b9882cc19d770a9287a125ca2bff00eff9521979aba76f163d9d0ca510035c9dbea68211669207200f5ff04c023360228e1833fea17b778cd7f3b41755ed8d42b0efaf906b6d03474ade48c3f11df0aec2a4b05ab62f7bd3948d91972f4206421c9ead65edd1066593a546949c26d96e13b55802ececfa40baa155a459c67717439aa3f330000bea2c337febf57826595f1db20341172989ebe2573ba6d1ca73fae045b597e90212faa94ea0d01351ddaf08ae5ebd2dd6bd004f65700edb91a19d18e401cb70eb7c51fee3e67eb17a3b77b5d7a3999b486be7d15b79a16b7d852eb98b97e0e8075b674e23fcdba4cf31d501b8c7ba7e153d0ec85654f4334e5f2c650c1d76030512396e4715f60c2965aa0c497a87f7b42a4769c16f0685e0fc1383e4bdff790cfb3598fde62d2eeba30c91d3805059adccdc82a9597a7d8aff5a45c3a797ad07f99fb6c902b5da8843b633ff57600712e08c08c31f68ac99b0ac335b73290a0f40012a797bbd577631775921f4713ab9d82f2036d376bd40882f7b8cd72ddb070f93e71de08a4784124a8acb230ad8cfb438606bc0373df3ef65bc167a1c520eccb31a0d3884a24e0f13cbe4c08458f27d0a15b94a37a1fc55477e4943591d03ce5fda15f02f75290c782e14ac89b3fd7e45f06012bfbd9740b08fd38629770dac8918d4a5967eaef9e2d4ce2d756d56b376410530712764e9e2b5bec6277f06fcc350ed373f416b676767c5e3c5481e24a229b3ee5e07c70052442c1787110723ef178b27b896f0e5754eb256bbcf51e8f04e98aaa3ac67dde10c625b699c0f false -check_ring_signature ecc95441e55e3bab0d03b55de0d6420221b5779ebe03e8513e2b9c4c8c88603e 90fddd7965a2006a56ad0fb7e4500bc876832b35180d8afff380b651e2498388 2 97947adc73a3a4fa465977e536a150d7e90970d9ae1edde308a0ba28da326593 fc73f04336b2196bf1711a5c9e8a9cd6971a5ed9d3459714ca4604d6464f3cc4 b7656772318cb8fc1820b10007e81bb8a1320349d04b3071a0580602998e858a983e739d4a9306c797e0341659c088bdfcde21a42f7c47f384be48198b59450eb4cbdb895c61120cc471517fffe11bb8d9a8bf663e59a7b62c256a1abe646406c9b3d6a6fb18b54f2391839d1025c9ccc1287f611e6b1fb0587802cd21e687f3 false -check_ring_signature 4e1925166f5c20115b03a053d6c8a81eb367967fbb0c0a7f60381a0dd3b4555c 0b73f223711bf0feaf33ddfe7da34f83a9da7045a0345fa1ecbba19999dabf22 1 94928674d499977dbf98cf462035904ffad93978db30ea24eae8733d1b53c7a5 e22ef265f22ad06bc3d2bb54f1aab60f43032a485baf8cf5b3dab7a8f228440d86d55f14c6dfcb83cf7e6561dcd3fa4bc936d5119fbf1d35f4d1d9c3d8eb5407 false -check_ring_signature 905eb08782db92ac615b2df0ee160eca1fa3caa30fdb677f1562db86202e4499 18232ecf0cc875aa962aca9732c098c12d117855eccf8fcb7b6a7840337e3252 10 9958c50a843392f678b45b7b2c5b5de4f296f5778e3159400223d73fb45f5e34 3076224a0880c15c04dc0b65a163e003fb22711eab1e51896176ccb6164d767d a4b518b33413c98c055b42018dbc2e52dfddbef9224d54b7235f27ee477463ea 5154ca95e0f4a73ececf6f2e1a0b7ca065667f096e9214a074ab3a5c1f53334f 24a11fdb2fd0b7a1c3b4199d286f8e7adada86aed876c06283394ad5b8036c21 4032819387cf5bd5deaf7ea7e4dd55ec4f9dc8245fbb940e0c4f97ab9d1cfcbb 6d247954e95cfb7cd4b8527f3bde3a912210d60a23cc115ef3e01755734f6154 731c7adcdcc4da089af6ae53c796f47c85f926801831db3d78ae5f8fda45af69 acf95db2545d17eb8f5809bcce4df0c3db9eb2fa69418a0c6b3b009950b24ff2 cdf64d88453732f9f47645bd038d8ba2b95a7cb2bafe53ea08f2ba280594c344 9440671eac20b1b5b6f8684d0dc2446017188980a37027664489c5d704e8050648086863c66625da1f3591c643bfb7819bae01bba84729affbbc224bd9b9030c4dd0489d58cd4c6489256d31b2db521d6020e6189621f1bb5a98330f1b7e6e0adb2246965033e372a147e3d1a1afbfb7301aebbf308cb3d8796a84c944d2dd053c2b808249239a8585bb3b4c00ddc759941a23851a52c71a8e9f3d64dfc52305eae5899d003e7b9ff387c255fbc3a6e04cc355c342ccba2a89ccecffba95810ce804016ed0341aad7f62db376aa407831d32cca2e1976396829de3800a7e760f22fa4d92e293c8a1ad48908439a721eb2f8ee278af13026be65668014d2f6c05a98226c603845c25a6724e1982c350740de91ff08032a6709655036aed71bb0aec67b7e332d8abb38af263a691f25c70715a4cef9e67f0bf3f23751aa4beea0b2a14da93aa055717217d8cedff6561f218ba717367fd2d2b2e7f77e88b59c20a515fb99389cc5756014c06348daf317198aebb15c7474a2e53a729fcbf1f00035c9f5ab9e250e9294539bd562eb552e6fb1626b435a29eb9c57ccdc89620b3006f4163d441a36de9f05b1eb42bd7e565ccc8f0bae94a7509101c6acaf8b7320be7fc70a6a61e8d5e51274e8c43d1c7b4ad37a01bd132eb55ffc834b4e8497306ac9257ee8273c5fd8b0d827d35cbe76f21bdba669905b3d10e5fc16189f3860d2fc1f74136489fa49bc966a5a230605dd684594c733e85b48e9117682fe88806499e07bb6e38099a66eb5369d0c34437e93afa73df4b77bc2f79525b6dea7708bb437ea515bc8ec4ce56fd0562e74adfafc30876bc87e56f7b8305b9693a680b08d457002a4f240f9cc4ae1663ebd8d4eb2d39ae9f4cc435a779375717f5b40d true -check_ring_signature eaa2733c355df25475c45c21af3294c644312eba9b85b128e0f0cbceb549470d 28581b9f9bd668e85570b55cd995135b14a6ade5e122c751eebd3a6ca5847660 201 a919c860960f521e17b7761e67fe7c7514930e826af8d1392d5e84d0e72178dc 2557dc9a225488a138841084b93a5e5a1e656204b0a90e51655a062142237df9 cf3e40fd4aef3d0c1c6fa9bf39c03d0b7a553147b9b6ae85fbf8dcf44ce68e3d 15bc8b52a1a9c38fb8b50be63a6a1777f7cfce3c4e69cd209af9b5788a7a0538 57ff54363896c545522c5de9578000825c0a9be9b07f165a0f982e56cfda125d e54c7edf43c1c0d5c695f941e8baa46c25a51605714ffe36d5e25e4f16db17ff f0479c510197ec3fd9a5c889f13d057a5d9d18f8514dfdfbcbdce3d39fc935bd ae105cdae4e1c2239c8fe7973eee8894902fb29df54007851cbf84dc8ee4ff86 0d5e20e321e5235ddf5be11b604778da9d68b40edf7458a16732cbec2e60e040 c20773a5003b2516b20b35a4199686287d3b6c1a8c37ccac969074dcbe76ff2f 7817bc078fe6a43a3aa1b79383a492005fb3a8e36a767067bc0e3dd0331b3a97 0fdd921755fa87ec1c4833b230f4aa53583885940b3af47fc88d4a7d78e33723 f883e83c437bba02e3b903b8183601f0c2fa0cce79dd26cfa432a0f4932cb059 c76c15d1a14a6d9aed285c743da8efbbeb1734943934dc7fc04f090acd47831a de3c48120ac968c3e7db43658cf07c47eacae33f105065b6eaf2c8e65437ffd9 5b566f7ea1c19a61847412b5f1e6099d9518cddfd2f4cc4a10c45033379dba98 e008475150c70226ea7b75c87e5bfd3fda12efb2966acc614527b2e6bbbc7912 8ff6e2552d02a24138f2b51d039d82e097ea768522aeb4d3e174b6dfbdb5130f 2833b5d10133fbdf3b5dd5ff18a5773c9f9a2beb254d0ccf8d102474be3bd48f 39b23081023feb2e5bdf9f6ea4b1497d6d64c7ff7196310f844d54f21cfc24bb 4e9bf5bea6700a562a8fe1435be255da83f8a6120a7a471c05ab22f3cfd89e75 bfe4a19c3d6c4bfa3d011101205d6d907d83d9f13db4ce8c6dcb4ba65ddd221e 2790913dbcd09147a86e19c0de4307fc03c222e5c607bbb3d71ba8f3068f5855 0c528f48d5e195a68ceaf66b2b2186f0217bc98b74b41f3c45302f1fd3833750 171d40eedc3f2227f1c0da814fc4793ea7b13611dbf15085d9dae6a52fc22bf4 6f85fc26aa83c3cb679f72f8eff067862e590aed37fc1b91b655b400f6f7aa75 f3a68e3e48f3b7ee3df6ebb50d0f59c0dfaf4d1606bb98eb305fae45a34b585a 19784de660a2a05e9282763e10abca25cfe9cc1f21ef247ebf4ccf9e6e3d6728 c0738042876adecb376b9b6545b9c3208fdc271b0375ba4972d3e00c937f1112 6bb10d8bb240e3a676b88c495c67fac22a0dd1f2b333b6a0f588e53f7b7f0402 522a1cb6ebb7b0caf42b7dced4a1a393310b26edcf5795024a86e2a372f0e564 55561b67b19cd1919959772b78bf3fee126884e5a820a46750a7353a497cd528 f2b76025a280d953da66e82da48a0cc7c58bc80a233fca134c77c179f9d6cc20 80eb3531651ccd87103619a64c894e3e422e7f983e7e8a5a28fd20ee4ac53545 999802cbe4e4d20aaf2521df51b2cf1094bd1dd45c0436c24caed67820aa9d0b c95bfd8d408d8c5c09141da415ef396abe898484a8ecbad4d759e197595203ff d9fd6533253f2d7730d77e3c212a8a326c6a0f6b4864e37c97cedd2aabe77b09 0186360cbd8cefe5e6953f81def44c9705e40cbeab7aeb222b92d5619fc490c0 fc165b7b4649fca41a0b569321b171781ba701f59fec113052e0a271236b5a46 4a944d24468ac76769ba86392195b7eae0911a5f946609e3c44bd41e04c91f57 47993737aaf2ee87058c67f9ef812a5e9a4208505974af37bee19314245f0b7b 010f2f7a78560b585d09a5d077fcff878bdcbbfcf57b7df418923c9a27482242 b419f31d3b9ea2321d0d492377d56ddd9d770888ecfaf6c430bfb9036f881373 723bddea5c6cf96bc628afe4985bbf8db14c938610011e2531947f7c994731d5 235dd6e7e48e4345bc5c20b21a07c5e63ccab8f93083f9b3cbe3637539ff6615 ed88f1a70f793b8b4f642a0e8ee32a801fe3b5aa02ca7f2dd26e435040ef8c0a d7941d4da0261698e7bc911f21a95b92626a7d49ebda17770a2fa9b3c5a681fc f9577e9cbddf7e13cc73d34cd83d721cdbca6b9a9457e522534afb27d1691ca0 dbf32c2b52616cf41956ccbe497e84a1b85a294bc897c9929e71233184eb1810 75316d2e0b940b50310a2fff51414790dc5f26c00e3afb53b65fc61caaf7ea0c e8b2130ada4929a1c54cd28ffa283f582823c5b5162b1ad522dcb360fa240bd4 0c8ab74625818ad74075a0c2cdca70af7409cd487d43f1290c67c5cc7b772b3e d6fe394e3c18294178b5751efa9f623626dea4d515a7e3fed02f68438a8f118c bba39c307b7988318debce93a66d0fead9264a8913afaa513fa104c6fb80faa2 250aa777c0b9b2a9e240c44d41a0cb890a385067b8b91e52afa584bee66fb279 e8115fefb49013088026cec4dc01b0072c1c7c0a21ac6d836e2a2f29c19c55e4 a97f021b07960dd546acc58466f7fa7aff829bfbf5b22fa731d52266116a9b5f 4e4e7a582ecf47cfed2c73b67658c7cc94c966fe47da709ce9105470df36bccf b28e0d2a81657b9330acc7135f33dea59522365912b562542349471c4e137471 6f161c16f7e1a29e37bc6fe154674a3a7fc953ce6a806321aa39a41a456ac165 665d1a085f25e2c2c2beadafc83cc61bf6045ad07ff63a9b341166a2dfe7f6dc ec4569f663485327f9ba044d3bbad7481cad0663aeba3b1c5a3a9a2812eee9ee 387242370a12946c72c8aa16caed4a1d89a3ff12256eda4048df64bf72477690 b9a0d9858e2d087db321c0edff1662704d1bf83b8ddf7e01470cc3dca0933f56 72ca0c1cf48667d5e4925c17014d738e5dea7c2a7656a785fa3ad99dcef8a623 15ce4dce66a2685f87cce24017bab7e57b50d07be17f8575db72dab55ffd02a9 3d92e2985da85205d7013e95c9be9a430269da22690039997a8549a551975d32 3277560807e0b2cc29eb415f752e5b8fe466e96e77f524aa4064574d818227d9 554200f209a4133aed6355bcbc5b02b91e3e495ec726cb04b4493f99570b7b98 cdafba2a05464ddb624dcd19aed8a42c755ae139f86db58ccf73814b320907a2 5bbdbb0e89f08172906182e8171302a10280136ccd614cb722b3929fe627ceee 62519ec6c0313b08297599af23a751be3a8c0ba51148b6e8291b7355a9a519ad b3400da20d757b0409d6707398da7e29d5d4c0fb4569b1c6579937deccfbab80 dd685d67b95d3422d329d95864b5cd13dc49fa350c9a469f923ecdc59d5a3c01 9de83b502e746185ab1695cc20b36bc014287cfa59a4807d3cbf9baf5c7eb82d 1028962d032284692c14f190d0f9395b52d95fe3be7883b807b50912947b2588 068946f91ebd489ca20fe6db9d94bf5a452edda351ac85b413342d7c2f2222f0 0c73212b82f5c345193602b5f80c9ed89be701c729dca888cbcd59fc0e732df2 64c56c0b182c94e213a9d05a8a700f869398dbaae43eb8fa61ecd73697a17019 ead9d9afa436682bf9b8a9464af4914441339517b6ae8e1a24014f220b6b0e53 d80684336c33f760c1c0ceb98792165fd52653281fe69476fda27862aa56085b e870bff39d6977344902a88fb1f81ccfdd9cc7e9b6d3d620a90622301a922847 0727a88e60ccfdfdd1bf10e7c90e878806f7b88093d6bd05563da394d5a5cd6a 8755a6b8b50320bd3e6e90caa75cd0e976e40193a9546db418b3cd73177daaee 0901b3651127716228e6646a0c2bceb6a28f6d96517abd5aabe1825788f9a2a6 754cfb551abd9e704017efcb3e6fb6699060db94078952edc5e3eadc852de90f 45c2879e233333ef9635125a71f8228a1fea5c29fdb5177f240910df6a5ca4e4 afda2e1b42b7f27a1aca644cb7148018eddc9aae67ff7a927fa2fa6acd8c55e0 9b53b1bd1eb537345bccfb271e294fb1ea15ddd70ef7e07f8e002a6eb344a8f2 9d33b52535e1f380a540cd053a1c2c78a258cd1c1a4dfcb4cd52fdf6692a11ca ff2e215f839ffdea3d511bf5b39fb67bfdc9e85219837126ecbf86a768f740ae ef2a81935ac841766248a2c8a3855372c92eafc1a45aaad8411efadb04343fd1 8fb57c626c2d8ec1878649800f211367bd05781f99c899091d6068c9497756a4 988b73be190a99d1c50d3324441fff152ba7587274ac3e2130555ed414cb86b4 cbc6cdada7cc1cf28cf1d3ffed116cabd01c3d4b1e162c34244d91ec66dd3865 761d3c4c91dd9262f0a9bd3c4d3f42812789cce227e0c513fe0759294d7fd3c3 9a1860839996b8aeadcb2e9d17fea680c1336867b48b91d1d2603a437d566bf9 999082b231976aa0fad97b9dc789b52c503c8b5d95df21986666aee35e0885ad 993e5924ff6697fca024209c385b14e4f4a770473a792c870cf9091b7ba4efc0 e7ae51131a2f17a6f70bc8b9d0a08c755081a0c11911d2b8cfeda8f567891878 0544edfa8673e038c30d43ffb66682f671ed0e72d5cfbb0e344ec2866bc99b2d c64c71a478d2df2b06f792cb33140d453272ec3e578851855c1acfed4cc08cea a3fa237f4acffb8807cd3a94dc4648edb0259765a8605bf2ae140797b68314fe 5eee020c3fa83821698aedb065eea2870aae5c2408e180cf53b62d969732872a 8973b0a4502b1bd1f12e472a310982a94807ba200cd6b2a82d618c9246c0e6b5 10ff845a1dc430a227bb44d1ab9a339a363b00dbaf6ec730015bae50b4e115d1 aad82eea3b08cbeddc637126a028368beaa44624ef50059199df048d66f50d67 642b8a1b5559631d73eb2beb753f46b76fd72021bd77300bc3a57d7464db88a7 88daeaf1b8fc72a455f3737e0621a443e5d09e2bc7565e23db72b4b4e3d7cc3c 1a155787be5f37e8535ec8dedc12bf011258f415216f074715bc1ded072bb204 a4808888ee1f10e2dc8737ffaf19b408e6f7c5647901f1aca8044a12afe84dd4 aaff4cae5ce632ea4b4072fe952a55e248e2ec39fb282e0e4357c9dfa51363fd af6d8452ebb4fdd961673dbf87e4f6fda5d20f7d71344a894eef964594f72744 42a6730f48ecdf4905ef41c2c458753f07728b1d199f6532991251b7b31ec9f7 925bae457bf354d39bbdf38bb09a1731b30ca5cb681c495fd1a49bde95053660 6b9a7f432f3327464068f27b1950e36b0f030a7b6eaec97acf2d05c55e652e64 e1450be37dab873c0c211f477f4c64ccdddd9b3e4aa22adf72e9f5ebfd173451 8bf20a8dd4b2e61b3bea72b0d893386d515a863586500bf0aeef0aa64526fbb1 491e1eaf9807c3a6993bfb2e831b2b4225cb216c3a5bf416e0521afa195f2ec1 f123d12cdcf74d988fc0f900824b00ba5359b5acc52e055d6ef820fe3abd2695 1c741bf84bf9ff24fe7e0bf8c27edd3f48ac50174077951ac7598d18f99f2b28 3a4e1fcb59534071e786a334b034a1cd3907baf2007aacd13a6fbb6825067de7 9a92846595b5d3f0232d8f7786cdfa06b68f52644b28730370279785b8b354d4 ebe1a669731ff372766d93b8f2751460db512136eb62c6f3a3e3a943169b12a1 2f8c26ed08b42cc0b921bbccae79dfaa5fd4ac77bc2525225b5031d70e45c7e0 915785300bb489c045e56833210b70cbcd9268c1625878035292a26a1f33c7ce 3568a0fdfa7537fbae766a821f736a51e87cfe8ebc2db24f4da3da73107ea6e2 c1fb1a6becf6cb88dbb1c2d8040a05402885eda711bd957f0ac44674d7bab859 3214623ae95612052a24e3c6175811bb8fd5aca258470caa9db5f1fe35ef75bc 138e2794c827e3787401d850e1b4753dddb83504da46c617e284fa50678e117b 2525e2dec5dbe97c96d913c33357d4164b5b432395de854bb268dc072ba3b2c5 d8e55493787ba7701375abae86f19ab445c090017efa83b0925a4a8e3e00fc4d c828a723bcc97be501df5d691d95d15597d085ef3cc20cd2a0bb443e06fa34e7 b2135654cd850a398f160ecb5f1b8e8750f718008647db89bd26d2355842a5e5 4d9c6f6f3fe6b6624f46c3cfc99856850dbac659b7839d4f5dfda979e0b9d60d 0fbff11db40253ef96899fc7dbea180170a9eac5e03ca6257e544afa1a09d0cb d44172dddf6c56a7109532d71c623fc9802d002e20335eed9680ea154344f1ec c3503a0bb0d79526c846beeeae7770c64316e86a669d5a63a17963d08ad9bab6 bbc093470fd3eaa200238b8a18f14dee791ab0f02027c125a99e095dcccdf374 2f1dfce373cd5bd4c7e88485ff7c66a5c066e9ec80c7cb7eb333aa873b67f82d 5638fe9c5edd0e23592d887fcb359f95b7a21753cd865624764c6915f3ccff64 b60f8210ec01fc861d4ffcc63ae5a2e52a583619c4730fa86462536bc378a2fc f82074d89d7f0d0474956dbc770531a71151b2a1f8784792e4019ff75c4e7459 c44a58e9a318c8ccfb8b2905091fd7c5528728265bef960e64781aa9ac7fe026 d5e87db6e7c211ae6c5bfd1ab2ace2dc30bea23cd31da0101f2ebad16feac4db c1e5aa9aca928162071486a6809ada565dd5f56b2bfd7cf51dc6c69dbc4d04bb 100e842f29271ba50d207c3759cb7fc10b330ea9536c55399f5d39f2a1cf94fa 5b2f4a4b61d9b73b93e33c31ca84d9619e5a58f78ef3fcc01d6620c80c2cdd8a 2dee250bd0972e7618e122ebd7fafd4c950d85b0202bec429a1cdea8c7c6e2e8 cf225a6489515dd6295cfe594051dcf0b9cbf88de55117b80d3bb485ddaa2399 1e491ee61a2209851f5b3ff27bed8a88e8ba379886690325f8f8a6ad3c6fd7c0 cf3292a13f298cd54df27183d7fc10ca9771d33c66ea88b6120c91aa4d48ec93 2af1f775c6a73b08a05245520ead224896e2c4064a754830b46f03cb7d8d3250 976f112eef4ae31371874865842092519a191850bea8298f80b275a058e0fe83 cdead1c68c7d7645dd2b1d83f21a4da489fa2a7674726b2f0da413467ceba206 de428b930f30494ba3ebd886b83a4cd235b6779db0462836a0652eabfbed54b4 341ccaecff12b0f8350325d9e2e40fe0ad3f5b6061786076f49a40046e86a420 fc0516e14098389fce5da013842fc543cbca58fccd5b044c255116bf34ca236b 40bb66bbcdd81427c253c6785b1e60614b9e903cf524ae5c953d5f7a568f5d1c f32a796b41959bf1f143d75c6716fb7d295881b3bce7097f7a982f21e2b8dcb1 42031e855af1afaee8cde09da80bd81785530ffa3697192ebce357c933413cdc 6dce80641b51034e327dd5dbba9af4386311eeb701a46621a0ab04c1db50049d 972a9d76064a6ba4292ccde8cd547d720b7be8fe7b541901e94cade4da94f365 be87a85da65de3c45330c365a76a9ba0dfad3fefe043f89e9d5a33a08fd278f2 f94a93c59c4558facaa059aaa9301f37f0ed999f00138b74c071b9b861928b27 21a5d6f1d4e296101bf4caa89b7ab6fbfcb02094c442b96d61a050f379a0da1a 051f7474b26c6c28dfc1f6ff4cd7ab9769a7b2337fd80456b4ece73a44171e63 ca894d58c216f50ca17941ebbd6a747e15410e52f68ab9e3a494d7a445864e06 4ee894542101605991fe01ee00471fad3d0822692400e800a7ac3f0b2b5ed52f 59fd018fdc2b384b2c116acc0a705a5287bd6e4b4c0af206389d1f4bd8f2590c 5e25e516fe4d521e863d8efad6fd249262ec2194442ea29da0d1eef93349ca8c bc9884e1a416e65eadfff149c9d0ba5ec08470661ee62fd3722bee1daa680e93 fc38f4240d62860f38c5774ce9c5ed3b634a2828bfe014f336579fdf74359a25 c610a313b244833ddd6ededa27f578b9cabbb4819670e2d971bf385b3c84eb3c d7999e948308dab554ea85d85531a59496c9cfe8accefedb077e70bd63f1394d 904fc4041d9897099616eb1919195bd24845b75bfd10e224aad68901099b6829 cb11201978542730f6cc9866837fd9954301ddd04be719c2644f56f0d2bb9613 173f47717ca93a760e3b69484deabeff21d95032ee7042ea09a4d0599d10b271 7a3c2501ff4af655d4c59510e685fe726c5e4d233239b43efdd5e631256c2db2 5371874fcaf6318fd20596c548d7de354c94f95b137626b28ef50ca8dada44a3 fa2fdf331fb45ab37a62d0f67a1ed91ab28e3a61d96413cb5b819d46a3c4c5ac 787ab1d504ca4833d5bfa3ec3123e96eb1186d6ad5ffa3cba3703c1d464e4b75 063b67dbbb13f59beef009bfe3ec448412074931287a451d5e0ae659aaaa0da2 86a081d595d73496ad15e1cdc5a42cd724b3893938725c4713a771093a2029ce f516ff668a9a18551333153bfd494a9dd452adcf50add757fee9da75af8c24c2 e8ab5b596b7148cfed347c13292e836e7673e768071c45c20acf70513d4da39a c932e891721a344f94b5e17f01365b46a3ff31dce79066fa00bebf4734d1baa8 e874625207c8d7f774b1562a8a0712b9e189ff871ec5bd592d61ca0eecd8fe30 b950c7af647b27aad4566232f21cb5ca28d9bedaa9f9b0eea3e58a6da91827e8 025c2e33bd55fcc4d84f92a315fca2963bb04ab6464b47276f6fffdee432435c d4b7741d247fae8eab7e53121701298299b3575c25e8709bc89102022c3b9256 4a7229305c6998f1994212c543d0a11912f083997295653db291e650faf0ab79 afc48e0167842cde6270836b1119e6780e25611b7a13caec588498a4b579e3a3 0098acfdfea18b655a9f95d8ed3cd824bb880531c44a4373b213a59241d02811 37e9763ce203de0c3db4299a06051781265092d2c8e7b29af8901706873aec7d 10445da3a9f181ff5c7c656820000be22be13e5ee5d12fdc29f440a2219f7135 2d02ef9540d3cfe71a462d4066355b593843aad8d6c210b799945e9d3a52d2eb 75eb3106cb2d30a67029d2c3ef72f8e755f1db1b588d2972fbfbda6b69952ffd 3e308303afcd0dfb24c6e1e0e6b6625c1fbbad171bee85a143c4373ca1941b4c ede3e8865a5048e6959213e60f2df107d183a50605e3ce900ead6dde8e91f141 e709106e6fc2aa00a8f5d3afd957f94b3b043e4ef43ccb0d63cdc3916bace95d 632d75bba4c9316bdd637b9f5803cd2ea75277a112d4dba811c472dc8b21390cfc8a2185e46761a7f869ee5d54049c0c940fabc65f4bdb049b744cffda4279097e63eb124303c723669ec9da7ab23dcf6721b91af504c0fcd351b1b717846201d7e75edf69558fe0ef52b2685cda9617e0f46cdcd0e0793a3227e0e0cf739300fa455f1ec0e7bf2509026928d1e9375a3bc359a00239fcb0524255d92829000a89f0dea76de0ae733a936c046703c1c26d156afcae3d9c45cfc4a123eb71fc06511fe56d6cf339e819996cfbe9a6f2a6eb8a344c5236cc1e421a4e5e979cf30a162db563db7d46e47ec219166df336e0ea619ff8a1ae874194b8f06b8bcac803a79f5fc29c97a0ba1ea2f5b923b1a83c2d6c18db40e7eac255aa49e091dcca0083424aa7c7225d36584d9d3f80d5a0c7a68ca4a6c141c59215fc40b533676b04194e229fb9e4a67a124f7cc0b07f54916aa59e62f685340d85bc0535e041870bffe8b6735ab3e34341c0a46363e469bd846cb1f295d9e22c86437b1aec4f3508772a92d1cd116988a6d9e218872646ff1eb20bac01b725f8afb22490bf27fa028c4ab7e646e967b6ca499ad39f8745faec64eb91f2263cba13abfcbd7c82340a714b0b5124bee4eff6e0495b454e0e4e583b2f1850a397dc06ea5d5d00f3d605e05e2cbaaa1d7d80badc37b52972043f4bd4d4ac532965ceae0d530d13df280334c9ba242eb89b68c45430862d15650e4615ae6227bf4ee19ec3c82a88bc99099fe2c28747a4212795903eabba09e190d2e2ec83e2bb695fb1bdf5a9b72a65078b49a3fe525aae2399d307c9fb7572488a6950e2626f46b0dbbf4f04c2193306f2931a60d70ffc3c20ac05d0891b128acc4c01858b2688f12363f3f8d9953a08abcbb20a9b76e97acae6791ef687671b967ebbc6ed43411d7457dcd165693a05daaf0c1bd3e99bf4abd89218c5c97f6c439d6f019db8543ca6fed9655444c8063415218f773eab7abd96fbfe9ebe4fca7ae35599c20f215453470e494429ed0c450f199f8c3c0a2e75206f5bfd67e64c4a441201ccbb966300c59a136c81220350c64647d398359b9589b93b855bfee0ef38c6c8b54ccefe0e7e67a50680ae04be35a28c7e3e71adec77ceb288b6c9a12713d4bb83c20cafee3946bb3f72aa0df0fc06bcddd39f102e037442fe249241acecd10bb040457bd577e021d962c4075f602307b1bd11280e5a620ab4819948d283a7d0e130fdeb4cebd5465b0fa20d7d8bc04b5b44d08de6b0dca98b83384defa2ad2a14f94df40fda48c210db650b002c1717cf52865c8ffc62480cb4e0f343da05db22a08a283080a152f06fd5031e30490113005e864022ca4d615dc42eee30c50612a4aea2a36111e6920cbf0e1ac1f5c6c44ef4eda8633d612cd295cd988c0c3f6c20f540b543413a84ccb50036be5c7c00499624c9a871d66cc7377fefd4ca216334c76045811165986307077326d165f64187efe307a0e5843155f50e17c038e00a9d2eb626a3f220007e08576d3bceeaad5e608133c84232e3818be462a6adcd4d73e785289ec0f9b055047bf34aa9d7b774105b251422f594330fb487f0db49a3e9ec24c9702cc063910e93cf19c359b4d12c280b15fcbabd0c6b7b9c45dfa90bf7340762b75f682b2b07acc8cc18a9139d3f53d7c08e5e37e905b46eefb8bb990602140a2517d9da7d008122ec55a5fc795f83d8d61961fa1cf4e4651d9850a1e41004252463f0c8fb03de30f50e574288eb7cfbbe2d26dc99d5f5e19bd31a7d7ce6d66fe16defe11d0a0d3868f9659fd12516e482a4d764457ed489469b52497c04745d9a1e0acf580a9afba59e3f0a09b8979d2a6e30ecaa93f0a9cb21b06ba32686722cafa76e9c0a6a13306f06523d425e7295666d782c81a857e9b93b9dc6b6eb70ccba41300b0db46baa42e64ba9d6d48701429d10e01c5b22e1b2c2fd8699b71a64bdae6c5a08151275781ffb9fb7fdabd7b442767f3455f69c8de679886c3c49dc0702e3e90a27db73cd488eb06e9c2f9a0d6155d1c4aafa511d5f869d1bf79d62944431920a71969e74278d631ca0ce2a94dfb6a97036996ad0a3942219f22236f464ce6807c593d11e402cbcaeb6f83dc8a110ac3afe3c7cc1d153ed33ffa074293a64320ddf2ced2f4a8524451cb032e2bb188b57b9126bccd42ba3c7c27337770fbddb021fa0053a693e06c7222dbeccee6e6def386f999ccdfaf9835ec524cad7198f09280e29dd22be2517b698c59b69d771428b97785819861e1bc83ea65365de88058fb2a2743faa568a90b6b0bc46d476c077c2bbf562eea2100a4de34f206d9d03cfa059ee62a0a3dc1af0404ebd3cd5308aa5e0c5eca2d378c8bd5a30fdb63b0bc197a95fbd9b65d2da4bed0cbccbb73be5c790bd5aa1e569a75a9d634f3a4e01eaaaf1214718f0736b9ac1297491497326b9192bc3709ca563efdc0d9cb9960b8d4af9f71885539fe3d8467bcc0db6eabefda92b3541a2bdedad50d0ab3c920ac7c7b48f4335f4cfadbf51b3ab3c6b56b5c84554f7350699b93d7a3a57ffa90f6612bc1b42f5bc8d9a3ecca51ff3cab6f13f396fa359ff8d649959b8b1c73e00f64305c968b338448db89bd5a5774ddaff48235aeac6fb350e26be8268084c0df2688036be5984c15310d24471136b812e7ec225091fd1a602fdd4daa3cb6601f1ef93b44585404923df229b713a311af821c4840184f87a98ba82bca6620f0fc63d057845e06ad385668b5d30866534951fe547e66d56164894f9fb51b0f603d06983dda9d64fa1039200abae7f4e11a3df815e93fbf247b1b3d05b1b7ab30532e7458fe03061b78e10c887087948f8f6cbdb8dd284659431e104f6e40e890aa67973e9edfc6c343c3ab2f6b1b6dab16ec5f647730ad36e32cdd174e9532a07c7fe0dbde96f642138e8d3368a7b8a76efdb7c10447884426f09b5193df6a106321041b37a02e5ced1e8a444f0438ed210e64da6e13c44f693e60aa14ddc2204026e81c78b7a8a6d7c26473e58946fc86dac94d2d5b66f412a71f6722fc8d8073a5ddf46f2dae83b9a65d0327c9c68a0133ff0ea194c39723e7985cf9443900e5810479efced746d9db2db425882fed3a83981681348c25671194811f27bdf01aada183ccaa7dd6a244a3c42b4a780766b54ed16423f8113b972f81d94e9bd0df2eab834ca9a30fbec4e7f7e31fd1a0d578555e44d9616d44a36d6081679330a1f576dcb148afaa7da0ec3d8bd7bfacd75d71c48a60e9057f2ba01fa5d482100e7217c8b3876efc4d65ca61be4273749fe4e555f0e6e9d0fbfc8390a383a27034f05526cf836e952230a20238b7b2e1dfdf21e1e4b3c82bd805cb6dcb679ec0e369bf5a368d0f113f2dd9a8ffe12bd757b60c3cb0edb97021e51870918830601298433054a8d44aeef6fe6e9d48efd7c2851529424c13e50916f3de35f61d40a5791d2a04dbf08295880260b4df8819b17d46e4541fec6348599263e81199d08b20af4d85ab4ca62661e9e071b83ae6ae977e17ef5047ddf9cb63a7fd1d969095746d0ec8174a21ec34e020d8001d36792970f46a67c8329b7825f700e8b290b7394b1d310ca256fabcdfeab4413fe0c14c167c424b6c6f91f5704841c6f2305e4f44ea6866c0d7f10ab8a5e6a063ec6f7c3c01c5231d5fe064e28a116a10d0364310537ab16452c4461d9b8a96de398da6e25d8eb44e41ec0519652acf4850f0026c509b45dbf7f4145fdb37a0a5e0a28a52ac9ddd58b92f38ea0c76950df0f2088bab1b7845cc49f45ccbafd2dec5e7cf9d3546822ac002eea1a67484dc2068fad5e97de2f4e43412bb443cf086cfd67e8919edf5abb516f44989369024f0f0fa0019ed5c95217f60128ce5b6fa7c715ff10cdd86d1d948371ee181140560eb8cf8b7b5ced3b64c123f6b78c7c1a2a004ec09dd171e9a7f5988f461b1e5908d46f25aaad9cf27fba8efa83fa7abbff90bc4c4ea04cb89b2b6ff6b8fafddb09d9ff30bb206c36ac6c54b10208a54e6ea19a673a8553bf0140c42e253f83a500b58e45d413136a8b915f11bfd5ec98d691d4e365bca3744c14fea33b2b1cdb0b4e0f87300fc5b09ebdb4617997d94fc2946254458ffedc2429e72bd12046160063f24fa6faf16f42336b3de9384fceb9c95b987b8bdcf6be787acfa5475ee1082820ff11eac3e677f408f0b5836d435537b29b62038ba8fc4542742fa503be09837102beea919f05d9730ca0cb591b8c077429859e2a9925b3912a3ff180dd0224884e8713a467871129e1a6d1392665def6f559baa0dfb15edbe368fc6e8c059f1210a167caed7bcbb0f44658705fefe10872ca3f0f389486d11157cf79100759f8b45cb1065baeea4453cee3636a78ca34f6f427aac7766731b1866dc41f08e8b429ef479224b81760cac239df0f4d1af77d44f308198f1694d1596bed6a0b1fc8f1464ca9906de97666f49f446ac75c9abd317e909a278471f0bffb5a1b0834c7e8a884c3736570f3e59b462551a9b5b02d5540c9d518180fd5fe2bd40603293b40d4f943dcb206aa2a589f6c24abcb8f64d1bb8c9f724e52b066aaa09e0cfb542fce3ed6c495d93f5b05ef79d883ae944272e926c75be716ba53e389ab0342fb47659d0d778c23cc22f6a0e63485060b0e8d19d9ca13c6424fac02ec2408a9fb027de83ea10a2dc191e96b0eb9c4cca3e48ab00b9afbe8093006d7ca530a355da7a468411d3b6e6bdf54d3842e1618818d34e3096c311bf7260d15804d059f2a1d374479e78cdf9d489fcaa3573ca9a86a410151e2b39a481c05e1f2900d332e49330b84957ca04a6512b542588ca5c7f18beacfbaa653de1c320f300a0bdd8d1a44ff8cad8d33f652b85719c9c21df105509ed9166be620d26d1b435b0f635861d3aa592cc5fc8bd9a76232a93b4c3c39aa53d81793b3339790a83d840a27fce02eca27ba0945acab856751e74fc098bc918fb53e1c2544152c10005601222ae259b0da3596781ad3899b92826bcef4a4eb7fa7df861476d52433ac030b105f875040f2f74e196b17603138ca227f2889c3c1b4bbdc410c86d5871d7e0b7285b7ac487608a7c2ca53bd954a1bdd52ef707970c52a2d268d3d2fc7f6d50bbbff3ab6be05ecd65cc4c700dd0512008382603c91905726ac880f6955e93b00f12f5b31d9b81e57e848a41ef18089862bb28fbd1586e6bfaf48f94d20ae0a0e77469a610c68a8c99b7032903ec6d05f7a5cb0726601e81963b987773c5a3e0211d2c03d768b8f89e3129b2c0e37fa98f010f51821d59b01bce1b0f2e8f78509fd019abd2aa66ce446857ea6dfd0a0cf7a421abea5c5797fcb480c6cf5111508f6e545bb552349ff0f89acca8722dfd2fe9435320423091df3366d6de6734e0cc2e0fd3d1f3e7b6361a73de12eb0bdab2ac2b2f100a1f64b1710dd7e01603f04d599a506500c93d182101cafb9c04925630b60194880a0fff370904ff9aaba0c1caf111ecb223f26315454ae5f00600aa8c0672b64f927e7b0e9daec1f541a05da88fe5329608aee97dd3a805ea72929ebf6f8cbc896a770fc3fe85da7d8db0a938ae54b5333051684bf39184747aa914f0cf6ea24111c19f229212a4a85e20f11b006cfe02b08927e258164643c6d9881aeb706fbadc220bca5e3378ec78b0ac2c759374d11b7c64bdf6953d846ae4eca676e505b3910de864db7c73ed8e907c5120e89820106e1aedac16c4b756779878304b88cc1c3b0c3ea68b0fcaceb065161ea5fa85eb832bd36c38755a44cbc33cf2b698578d05c94455f910d3f9a0e81cb95f60af75cb935a9716f80ee05f81a4fcd93b59113d0e3bc865717cb920f4d2569cdc04965da758a5a1fa6c19587d50dbe332a20ea20a0c0679f0cbc2008b75e59a999b460149d718444dcec5d140b459211770682875f5f6ecd5ce4ca082e54b68e8d12942b659a65ee14c8ff569eaf942f1adba3fe824d9dde26b2210676146fad975e4a73a3f6e3ccf8d8400ec1efff6e061f800b7a5e0f37e9a7900326a4775b98addd55611879052dc40b77794fc9f2747739236b97945f8726c70894323e95d18c14eb0818df0ec0eba8e30451a4994b517700c1a475d1c457b70dfc89a3c4cee43694e78cb8d6f52312e6005545471fdc9abdb8603ac83aded10bc66c02f1e1ba916c1413db762515a54ad55610dc768f1d36a0596de9e9dd9b0fa35a4ac9dfb72c8b9ba0b815e42c96e5e03feee37051b382b352ec1fce21bc0aebc32f73a18619181626de88de8f083fcf1c018df20cbabba510653737f68b0c00194834d5b81dcef796a35cf9136ed644dbf3a7af6a17dadcb0088316973200e8955ec325c1e585b54e1236fbec8ee5999c96990809ef4b0d65c494d618740fa316f01c8eb9113eccb48d871b383f1074ac752fbf9e4093ca2b7baf3f13c3072f0a9b100962cc22bf7945f330cb80475314c916b957fac8473582da32157e0c00b001c768948c6668548ad7e87a30d253859d0cc3a409f8901869f7c9d9b60b5e4ed5496b0b39b7b44100c4348ec40754423da536e2db8c50ed7896317d4300f52773435f1134b43924cc0067aca737250ce06e8338447edfd8e05719fae804aafc8959269ad5382ad006f097567626c901c2abc0f65a62d2ee31e446ff200839f674d1535960401004695918bb956b97fd93bc6d1d43843e5cb8e3c9570609900ca709aadf4535838739af629d876213a83093cddcb7f18735b62511960d04dcc01df627dcc8d5e8f548c20e159bc5898d87791f6a6f5fa82652159589e909257aefd56938e13f4fd2339be7bb8bc40c2751d0a707d2a5e0a5d0f2c626f4020d3879162e8cac1ab67d87ac1aaedeb4fc5a2a5fbd98332ac2619107fd1ba10b53cf8b8ad8cbadeefa93bc1772366047737e2f5f1be7825f362dcff748abe306d64e9d03ac199398c5b1edb3df09e611666c7acf80b539bdb2098a8eb5a43c00239b3a2a810278b14bbd0b840a76d9d4e8a2fe3f1dfdec875d280fe032b8ac001d672d0b942d30747ef51d794b3331faee8ca7839d15188d820d07914099320799ca98eb325298c493591a5b8565943d3c9c11b177df4a1e656ef11462041d0da851c513077e9a0bdb72184a0774b3bef8b78822312ef18c1c4520dbf2765a053c9d7677daf8d774171884eeb27a3abcb3f2986fde744294a6a48a696c2c0f06561a02a6a8c087553d04224876d822cbb454daaa9fe5dba1574e2f64671ac105e40b9c0d0449425000b3a983229a708eabb875690e7b39d900a69773fdc32905bba68c1b6cbb786e2bca1a3d63cf9da1d1d75a0e39acc85eabfa0f7da280160f4795d92fcabdb616c60f2b8567f3cec074efe10655e906f9cd71d69df1e14300c2f48774589cc38ebcabb501d69c3c4a807fa640010598b167365deedba079063f07141583d2fe2b1f69b64dc6ac5fe684ca4b9c95e1d2bab5ad2f67508afd06bf05d0e8cbf7260b30c38775fc06085582c8371d7bc34f82139f366b084c4c05627c48d97325810392edc78eb9b10413eb53b7d12a5e0f2b6d9e22c0c909fe092310e1dc7fad74c0693a0a7cd2989b10fe3cf1363c79a3c186283492f8a37d04ce31750c61df35a145aff386c7320cd822f3b4a69cc320b4459cd28a50217d0839d0abb4c45fc31069e385399e9c6c64d5a0b5ae6e8648081570c28adca515062d651764d70464bcdc02fa47d4ccffd58b30bcca5746aab19bae03cd24f0110a9ef2ce5aa337ff5a88628dc07482b90b6f98caa3e8a600b1d0572f79b4686100fa51cc3ded69f0ead181823ce0b8e768c031accc39bb68908a1430b5e6e606084be6f90e92c2f4dc28f247d2940445404c7c12891e61dae812742e94fc4b4a0ba4920ca7cecae0bb71d3a5937b9e72970d84bdf9ce8ec42ebd352b0f54073d051729006c818569da022be90b9720cf34890f6ae613e5cd40ac543004d158c40d0fde6f9b5f97772a94662df34a758d69b8763dfa026bc2d54007117d439cc40942652308c113daba26714252f6a3c6de5798c3a916718967d45d4a7a7b22e50463434b83bdd1aba56274e630fd9c36971a796e626dc91c3f48f2fef546f8fe0883d1c95ae9c85233a71bc96f97fd6206541a087f57746e31d00ce1099eb8c50ee7a3d60c4011970f2cb3de5971942151ebb021eeae83177682c78cac5a05ce0201210bd261b543866e617189b1eed0c5b03ff4a086802d81b28e2b36df0807095ea25487c01bfe9ba4322c5edd6eae3b4cbeb69b02671c1a479c8237da3add0be838413df0a73c010d378d104eb8631cbf819d87d34ea7b3120103382ecad209c357af8a0841e1ea8fce3e1ea12ba97a041f98c7177bcdf3182e4474315f960d27893e688448b235450fa6d05bf1022b5a1b6744a05f5e6eb94ea361d0bd5b0f133bd23f1198b78402037bdb2c4868222dad75ec17895221cfde382ccf047a0df2043b25bf3636113af4f61e59829f8aee89e0cb9364d49720afe25f512d190d7bcd85bec38cc1be0f8638e31901ee7dbe51c4da99e40a3958b8d0edad49e60b3beab1127c1d7ee98ff4860f15d9aeeccdca5bc73672bd59cd2a7c17ed1f22085fbbe056a274dd9cf1c2801b65568371ef57ce5ec469667549e4cf40e4bd8f03757f86a328b7d49342ef8e625271f2caa3cda2840a7303114669f391f30f330a9cb81678ca4ec6ec680bc223b7da0dcdbd2d1e980187c42e4711a023dc808e0d9baa68d74176cc20f4265d74bb3fc235663d005f1523765b0626eaa042ec720a0d23f631e41fdf9737ee416c0cd2bf9569bbc1db51d633a01bf7467418fe1d0732c60219a6bd05a9360bca6b9dd6b8e96ee2afbed80d9e32afe896b1c76eac0b99af24db0577582a24d9784f3f47faca0e38be6c39c5613a521d9c0ff8497b04ad932ffe6280fb4113c3da7d26de82516846bc666a9d8bfa25c3e6298c61160f25aad526680eb7e7810ac9de75cd3598b48a2dab37307b5c9cf40814e33e8e019ca8020b0f722822b2ef9fae2aefd68232ae5072746d7a1232625ee2b2b6ca00127f4bc81f921f5a1e7dea1c81acd7b361e0ae3362376673027403d5e563eb0675d4822e2e8adaa9ce56724eb227a166b24c9acf86d4cdbeb8e32a03ee97d20ee2be910a6e5a92a67e0a134ddabe4d9caac9d29b02583e8c50b6f435b031700063678c631bc082f8ff24151d56331e9e327710603430801a0dada9c06f016d0366a5b9204e81efce489d6f2e326e2117b6f2631c505aec33f89fb9db647ff00ab523c3e62f7a2039f2c80c36f11b0b2930731e6336039afb0b1bee803ccfaf04059e95494e2d30c0aa8f83a28bcbf3057de13a7c02f26acf927a06e0eb8e120e41c4bdc959460cf8b6a309b09990af386e63565978dbf36efbe20e7f09c86f0aea23e2e7dbda5b9347c4101f1b8229d68f6e697af31543091691cb5412d88b0665867692d732c80242482d52954247234335b37f7ea5f9166932dc2fa9cbee0f61093b23bee3bbeea1024b6cdb8fbaa099c8d39abececb8bb37f51ffd4dc6f0d795fc2a792a3c258a4ac8d383d818d697cc2e6e61b7fc47df1f36dfdc377db009d77c82f109a12757ab0ea70ac0fcc8317522919bac13821e26702fcb7ae8505323a97a0b2794a1d0c988803d97f142da2ad49e2708f27b8b832e9e515a27005eeb115b91ef76484f4fe26956acaa8739e59cb98027a9c9dd7458c838654e905fc66445dd317e44d26e2766b84180d65f404121554197e230e71a7054733b80f28e70cc638496b9c1d2e02fe50818095365e6b5101ca8fb7ee48e40e4314950f900d48585e1d9c086d866e002fbd52739f23baa33ca9ce77bc05ec0ded6ad00bcd13a4a9017ac38666dceb3009a4a5147243c9a48e676a7e3e166dc892da900ef9d028e07e6bb8635095afaf315d23e3036510ce6632a6bf5949667ceb503a059f0927f16f8f3e8e2aa5ff90bdcd170f121c882fef757298778dcd6df8f7a408a86f0ba91d20da3e5cf33566f6eefaf3f0f33bed01c9b8a0ead303ebb12bf206e8b576aa0685577b8dc05b6c547511a17691384b212892a0b229fc0d17e83b0c92d0544bc0f0dfc42536b9152845a479a39dc7eb13861b2b26d19b104e37dc0165fe6fd1b9882bef23384841c93439c04c1fcb7310d59e3a2ce2356a9e11850d8160fc8f2a2d2120957263b81cb5be48e85c0b67de7f4a9fe640319a1126360c6702bbae5209b0d87859dfec521437992392f76914d0f2e77093616fabe6c60fa51921bd03d279e889cb746a91ab7125f4b6deab0bbda339f93152e4f999cd0f0f54842f2d9bec1b0e75c31e52b2a47beded9a69d6f6810d9ca662c311cb6506a48deb128716db8ffba751a5843ac6565029e944512c08506f6175e66e4962038923c0c5ce4fdf9507bedf70f2353c98e14b2aa814c7bbaa8d0abebd4d18bf04ba2d9332377e6b841cba98c2ca4be9ef8909ee0cfcf867ecb67ff88685746500ae404dfe17ea04e7168a3ba33da4cd1b3ff40a4774dd853d2d490a15a1f90a00097ba12e74d96f9927c7077795b5e8a6dcc33325d30a475a503d3c6582c4f40dab46938dccaffbd7fed9127f99898db8130edbe531c6a99e745f7985436bf70678724c84e3d5763388c9ff01532de46483135ddfa1c82e5840ad20a5abd083016c62a12b526f0e2f0ce4e7c90616a91200a3e8a0ac19e740f87e9122654660059121fc40794d4c6f0f1a9f986bcc4245cd653d1e70c3c8f5eda688e9294c950d0c2d6c7eca53212895dd8f8b52ab49ea77491cafa9a7a15d647a15fbb6574005079506cb59a4419744cf5366d677e4712eebb50298b92a9546e89af32d60150b2a07d502d14c28dfc30327f96e76dc146a246acb88f97a5b99d83a639a310004a3b8107156a9ebf9a6e65bdcd5375ff622f65d9da147f55ee1f0371625dd97039d455766aa423b15c3cf1f382a8bdbb9afcf32f563488c06f7a3488e89b53b0e008c21f7d3a7b812d8e1394d89569831456d3afff503674d89a2b0813a112e0173f07afa5edafcc232525aa682483f748531c9e79b1e2bfe998d361eb5d115068421ed4e4d5ed7ea78505612586e77371a8905d6031ffcdda8bc75985c20da03e46a7eacb81b877008b330eb530747f4a42f23e05c5dabf5c353584558cca70258943593808f62f99172a67fed90fb2d692b388a007581656efbf9959a950d0cf939d9ff5be84e1547072b4c7f506c31c9b24bed4e8ff2107c046c28df6f8301c67b4d400d601556059f7a48c513f336e79afa933b440f268ce21db7d0b12601eb7986b0709f882a2453b56e1775cb031d63153aae26066008b33c52bc1ec7046d8a8dd139f99af1313cd93aed03fb4467bedd95c9025220004999c8905efb065339e6cbf7922d08b7cfd9c4117ce44b7e68756242582fd31aba58344ba4fc002fe71e114cde454789e764aa5926a050e0d952aa6d7f3c8bfd2811e797bd960e29c30cd093ce6e417886128ead727d9a611748c2ccddd6a761b2f081e3c53102caa1df1b4cd79ba1640d7e9c63e3983705e8faeda916b36df71d52c951b5d50d3d0dbd6621f50ee4850c3aceb5e40e938f0d6e13d2707e694571b134ce3fea0c48b95b86cd0215379b80219d3c8613a45a5701e2b427703a8f7ea40004f07a067cb3f5ff709138e1d814b88d83a5aefaaf0935d588cb4d3fc9aeda9b7368290cb7ee921c6b3470399a1d3dda5ec7821cef8ae1cafc8349e4c6af798d6f6e060cc02fcb7fa43b1540aa7957e38e36b7e134751cc61dc22b16ffc8f69007444f09cd87b63590fc98d84fea7335605e53cfcfec45b64f68a13c3a780f59b52f69087e64f6a1058a5acb59b48c7c6a6d9152bd90485a4f032d88e2f58da9adafb504f6524b026548ecb5372d5870f7ceec4a6ffe44f94b1cbf8ff1b6e29e3ee3db004c985b52c5772a80d577489c7470a5ca2579f0fbcf1310b1ded4bf08f7f2d4046db03727f815c4b3cc19242607b448979558050da3552e9ed4a9bfe5d4b233023760fb7c903dfae1aa293f7bae6dc0bea11621ed7e1e04638dce08bc035ed000fcc14f749422f0e55445b4e475ec982f3172e3f819df58c8da3d808f3b35a50d30d53be74761a4f5334a6d447cf4b03e96513ec18cd3fee48e2d9f0ffab6a60b430e09a7bd203a5cbf8491252f3b2e7f1e4e7afd9f275a5691e0b18e6e6ccb07ad83d8b78b5b528ed9f42aa73f252e6d12965d7d56d8f7eddccbc226f7227b0e023ee11b5dbd0778fa5b2e395d4789689544f0b1e18317ab2dedf42646c8370605ae3dcc66b5b2616b26d6d57c07fbab3461cf1e0bdeead3a5a209c54eff320efe149754215cedfd60172c44fd61d7ee73333dd967a4edb48aa735bf4c2da00fed30612b4ffdf12578f72e875e2914f734999e4a8a92538f8538dde6a58e8c07390926d59c47b12de82e22178ee030c1bc5736423959ef28a4d72f47dc7cc00880b0838122f316764f551d9da9e4f5e4033475bbd460678e2a99d1a1ef00e303138923473ffeb28bab7d07c73b092beaa073d7f0c02c628bf61790dfa3adaa0ae5631395cc86737cac04ff1238f82111c1e9a5d9cc50333ba2f2f48abd49830707478666da01111f9afbd8fcca874b6fefc855fb57c0567901594abf4654450642920ef67d92c2baa5c75311e267925843fdd421b2c554e432fc357dcd24ce0525673973f8bd95965baf37c02c3ba25359b8efed243056c70df28e021fe05809ef60fc1e0de5103a95a0f0b6c9e79fba8c7ddeeab4fd5ac128f1ed14936fe104f3085e44104d12f5a00c27edf6509817e997709983148da5279f8e57cda2180d72efaaf0352ac7b663c2fa9547a8aa13d0947a0c26bfd92c3c33d4b68c173b087a142b44813457468137ea7f166e87074fa569b700112c04ff6c6165264e7406436e39f43215dd9a3532fb00c24a24b9cae8096d920a05366636b4948febd10ed8a3908a4ae5cfb59c6ed6c2f4586807310528273ca0b4c1860df8edd57f6909a612fdb94d822d3142e0761528227f2bcf62d3661708665730b5a14b3cadc40c4b3ab8aba6763324ef6fe082964a31579fcfab42865913210f3a6cadcd9c2b076bd949d4cb4211765ca64d0132809978669faf9b77b789b72d49ef470931160397f12b1460834c74f895e661125b3865f63366efef1e0579333d931a64a04d09dcc0ee9da7feb3bbbf4530aee1d2e5a53585070551434bb5b2c25ef18ea81b09ce870e6b6c5e51330e368050ffb2de67dd1ff43683b60e6b098ca0f30ee1a2072781031d1860769750d9c46e19acda977f55d5b05ad53c41770afa3a5ba8ed0ef81ae678a5f1ec1caa89aa27be20eaa5d1925f1a25fb89398c95e68de1b6b2092ad153187db035135d3d7ed19cdb42522eebc56c4a305bd4ab600bdfba32bc048162c3509ac85ac5539d98522a99aa223221f428b61130c8c582ab0f9d54aa0cb4d94178469b32cdceb70e0c0bab08c4fe749ca6bb7ba074c92c53e3dfa94007ac96d032297c550f711c3102636e237a626c64a7a45c113f10ec448dd929c90330d91e2e6954a6715ee9e139370cbf4ee1e5f899fbf8b86e1b1cd9c99699e1092147638fda5095cdba4983645b4cafda755b501f149f5b74e7e94c10a68fd70a474b36076f92ebaf2d499a30b505ac1f10cdf7467f73d0fbf160646b9de05b062cea4a805c27560e122afb35f2a9971e3eb16b536aa0a036bb07133e6e056108a9b3af7d6389a2cefec96af8bed89513c3fe72f7f26de72a4ee86773625c240e11b96ee95d94627cb5efc3980056f1b68ff0ac17d7ec09ab7081b6c9da1e6f0dd4f4aeb1dd3102e0e4034554c2695d658ea00d51a0c4167ae7a210da03459e00cf8edf4cf47797aeb0976d720b1e3a4efe1f9d8b962e04f2e87ab747edda430aad07b876632786eae3b06c5ca72a951799b86322b8921c9397d7fee6173e7f05ff9c0b361f745b25e4c15387365088df0e053c849adfcaccd8623e9dcd3b5f073a4923e21953f42a8f749e119a0b455f451618ea578b31561d510b6b1128af0025f0fb630cb49e9d187b04561c2cdc365b8f674c1ea0cad3784757c0c44e78078dc744425b024cb47455fc6b8efa7743cac1a683e422fd08a70068aa7690cb08b57d8ca992b37b317faef43c09e04440284d39cd0e2cc23dc9ef6c2812e04b0e253101c70bb4b902c477a21f7dd4825779ec7b2f7d6b161b219981f312ef200598dd0d55c8b685d7dc6372dd47a8789bcdfdd277ae28108b369fcdbc4734190edf04140e6c0bdbf49deb6dbdc69635d81376ecb0d74864827a2c0590fd7cda05c22dc6a418cd9b2743c9966e214ba41acd6c9c07caa1df113d3a6bf9b9305900c02cd28cbf6983faf3999aab0a961a42b7e62382af507194b1572680e43c520b0f98771016b0598a3037003a53325e4a010f7bfc49d0e00795cc327e8a27280f6342dbbff4d46dab80e5c48906f2f96edb68153b55fad54be2a7ce0b4aa54000a7b7537ab100446eae73a227364e3264230decc2d33270a72c86c4f088735c022aa0ae797a0c42265fea78343ad251197323d87aeab37c8ceba71a3e6f5e160737e0c5c4c139d1e8ba5623a3e0c35e365353e931f06391c6ca7397e53881260f5336bbbe5b44abafbfc6b6e0f0b9b8fe8a77f839529d34eb2d2f7b8eae568d0d6d43e38e7dd287a6a22dd5eecc26273b05e55c63592283c3544e7a7a8b29a609a81356b9346cb424d8976fb86e47b7babca3f7fa902e2966bb7bf913f45fe50cb78dddf22beda8fbfe6c7636c5a29f218d216eb701f49fb36a8f6f6cbe42100816dff087b6151d82c4cab0eddde305fb1a4a8610b0f06743186d7124e2a62c0642d7d0bfdf6d2c71171c91ae2a18dc15d3dbc7636de2c131bb83c0a38967610f33e9e0fc2de23dfd702fd929d0583bc0ccb0221bf1cd0e5868a7e3c0962cc20cf09e39e9b6cda4734b5ea11aa8cfca081482e1e832ead21e01968346143dea0b3daab007ee7a466a0b902e3dab6c0c175bdad2b3793508bfa324e180eb838302820ea69806ca1f1b5f582acf79ad4849772a0da926e4ab9ee07a69ef7ed9f30af8e52a10ee03b2a67c4c3ba63979ddba1198234bc4c4a5d3927206f5af6faf0725ccd7afe8b195e0dfb20251c8a8dd1d8f68168900c9ba5eec37476157fddb093667d3624d59936af044e1e3bb20c789b3c1b97e7a0c3c5077ee0a2ead140105c12c82fb4f421886fae1e8d079e8fcaa6ee26f96365ace1967a6bf87addfc707708a4a25d8522fb0f3e9d1c79154e9808c02757cc8a15dc9f5464a753488620ed43874141aa7114f2530a0a718944da0fd042f6ae95b2cebf3dfa90b6c07e70fdc5483f9031008f6fd5826ee3e5da7bcade7c2101ccecc718e3662d7c7e5600235856085e43c703402e094e7844995f9b2ae312c18aca0f1b0fd71f3ab1ac90c0cf02c8c5029fb294a98245288166fd39522d3e5737686ad8014fa60c52cab02906040e1b7eb4341dc615eea9100665dbe4881ba9342a05d6c4c75bcf1c1ca06bdd72266f9d3b4fe43406698ea6717c4a1bae53518160a2dedd880d0503ede03936bb74370ff4e50626044a7383d9e47120ae0cf29f60cee36dc0af939761f097a836804e1c1a3fe2e04e17656af52354c2fdaccc20c10208f7587efb5143d063273b49304325297f1991708065f51fd7f03006f83919e9e44e707163825f90c29b072bb7c78c852bb55ca12c2a126d5e1c281bcd70c32525f6866414be9eb04ff3fc91f16b7c8e0d46e030231822063a1537458a195eba88c0157acb0ff74040cd5c1e105e5d4b1af9bffd15387db22ad169a56d94c12c278f91a4b8a1b0203f9453774ca765be1519447cd1bc6f1f19860a21388ee56f08d17c4b57d0cff0e2059045dbdae4c3664f3c82ee4b10f23af59e3cf0698143993d9a3296466580ebbab6672e5661bd344b940e0689caa674c0034bb78bd1d585bc5491fb48174055f86c7002d3c8c48d26b51393fe6342104d8194b5da53b4992f767bbc13b12075481ef2c6113527923c7602323abffb3510f59577b208acdb238cce2ceec61031a8ffd68049654d6bc69f57a9f4fe53b520c0339f33717de01b356dff3a3a40624a1650fd650952a7a520eeb5c3c0df97eb163132f04fd18ad7a76e4bca4e905e95425b0763919e06e6f1fcf2442eaf4e8d0ac5d80598aae71c78a84040a2e05fdeb861cc81f6841649ec3ffd50e6dfcc33f496c63749f05d262c2662f8f2a0864dc6ef59a363eeaf7612654f33f22897bc2b937e8b5487f204ce60d4190740a92ab5133ce6519bf528bc419616b96be367030b3beec68415955134406c6860d3f958ed874fd8a8b19bbb07ee26abc6a29c4f30dcb60184d8b0cf3a51348790c205f92cf91c72da80cb0371c8c18fa6c1fbe4fd8ee70e7e3c6f9a68116ecc003bb2cd3a93c8a8b239a81c5c24afcc08bc7227584dbbb38965c1b2ef34e250504defea182225afedd6bda492f004598c94b0d999ca2542bacf3aa57849161b60dc57ae14f3800ef508edeb3e4fa47d0d190d98255104924760faec4e3921c45048169048af909b1ad4dfbe82ab516eec00ba2b2df38120446e974a72ec174ec01eaf7fc1678a09f481e0e3c0503570357c5a674a234fac75a6cb1c41af2a4940a6b720de9cdaeab61cc00906f05b5898fd3be7c08003927ee685930aa82080a09de665a84555cc87a5548910fcc8c4195998424858006f127be640fd8ab0c8c054456d2ecfba105f8a32c1cfce7728ab3e924d39958d5adfbf768204887f34704ac4ccb9982dd182781d6ce91343c6d74c75df75e66ab99ef66920f09b7cb8404bb605a88147ffb597faa08e9f2c10da640f2a1eef401d25796b47bd91340bc0ad1f76edc02b701126f08069736ed6271e11d0b7bf16f915cb9d707be971a2f0ec5520784c732cf7227e94069bb976581ecaee3a0d02210c13e94889b39d5f706e4501dadb05678bcdf8605ee1c97498085e0c70e3b52844ff972f2e140abad02dbf61915e86035511aec53d131195e6235ffbf7830a790efaad7f350ef66a60a6a3010d4b040672311f684edd117621c1c793593df270012e4534710bd26fc071bc44c758022717f4bbb5da463f8743044fd95e78e847adf96228f9f7757040945fb668f91bdd87eeaa305bb580dd2f2b619af0dcea391b4a5a4d03a63d6830a3837501518d1b1531f3178bb55fbfe44dabe132e318b073f911c124660bbd50e08e96a38ec742c375d0738197cc2ffe48b6964bf7c4e254c990bc362a0802e01fa75cb5ffddbb39a47af906ce3d1625c695b45a1f724d03a84c06924faeed209c34b445b76af6a6950cd82a1c695ce6506920002e2608e68debafa6cbd7eba06ee7f9eb870c184c4f100493baf593b5a9a94e2ebb3419ce87fa625a96f96cc095e31c525f39d9293a193c5033590a979456256bf14d91bc771e69b6fd794ce01205e3b1eba92ea1ea41d85c0afb9459bf1a829f9667ce18da1bf4d3fa1388703252c2bd949e7c91690d7fb5ab7f5571928c8a616e2b16780560fa8bb73431d0df286507e2ac6492332a4983841b146e849d782aa902b72a2fde87f323c960b0cb4dcbfc555590fac135028b230713a7dd035a70d2c43a29af945a437c93b1b0bc41b7430ee582b7eebc3ca85381aa372f5b599328c1ca75bffead3d0ec928e0fad1d86d9928425a68524ee82c527b263f27f7953f37d34679dd703f689bbda061320175cc822a801d06a8a2bf964cdcecf2bab58beaf7e3034a40c3576890c052d8881bd8e4c36aaae768227d5f82526569fdc38b1d0a6b5b64b3fd9d4d681069479310c1058cd7be4b8242d4d32dc180ffb71f6908879e509f8ea57680ef10519dd35e8a3ac0bd8fb27cdaaf4e9008eef8b96e86cecf122a899e0830d1ac20273f96ea75d036b9f5ae50e122264662cef6564a8dc504d0d0f11d54bfaf0d7052a87ca380c51b6e3b21e5eb15a59d7f9978cd49761dc6f2a819536c76b590e033d41ab79f8111ca580e4633822a7d525ebc5ce5e3ecd2d4da3fe2dffc50ad909a56a62a4eb5b7389ae226fa07406dae10833503cf186da4cf8e8b5ddbdd3910c true -check_ring_signature 2c90e7fa64e3394b661355d244e487d605a98aa968e773dfcf0300c574495a37 9251e741ee403f7dc7dea8d023248e56a2db5ab05c68c631b2eae5b3806a4498 15 02ed2deadbe1ab714be5d736eedfca6194f536fcc419e87cc0bc9cc872ec546c e261d83cae8f3b3d90e449085ad5e584019b8d3184610e2da83510ac87c137e0 7a3e1f7fbf3e0001bf888e5fd23028b1af07ed1e7e310eb595327f61a7b2a7e1 81840d9936d0cab5b5d27dfe06d9cf16b398b38674659a91d5f3cc5988dc9a14 fde159e2b86a7aded2cd747a05bc78bd8ff5a384f7f8d8237f4076c50edd16ca db3acf38cf38c729e91a0056ff1ca4b38c0a7aecb16555f476a44ab5e6f3308f bfe4886fb5829efe948da535694a95df2b7b01276c913ec45d39cc81e17b530e 3f097a3ca82ba61d21965d560701267c971244b99837ba0a7ffa87f1f7b1eefd f0a3c7483d48232c6a7771014d7de8e4bb23e6fbcc7493605dc0b464a30fcf51 4e957aa365f6ec443b3135986d8a7386f957bfaa443334647550be467092f202 3252bb5a65f9e4597596084bd45d12b94a95ebb1d50a1ee6b6e5da4f4b47c5c1 96c7cc5a84a5e9692939e24fbe01036dd156d46055f35b7e3d93d0e480c4ae30 a3b6a849784f571dbbbe21a59538e8e216490f1d16bb64866762f8b34cfd496a 98120ba24a86016ca522d06f809667264122158b4146716c667210f82f294f9f 168bb8a0c335337706efb1ff80cb75c7e3742de229e61b3b6bbd57c461c1e690 3989439ed9f869700e5e10ef4acdf9bbc75330cbddc66d0dbda27bcbbb46880e2927a985e825e353a6667ab63f817414bc8814b38ba285da4f72d9c01671c10a600905a76842dfdec7c3ef6730995eac8ab1de419743b0f01bdd6fc17dadba0d0c6e5a38eaec33c8dbdd7fc90cd0f58603009b53867ca84e67013d8762e7020f79752994761d49cb44644a6c57b80d659baecb6175bbb3070db2aefc3eb76d0c17201a9aef485ba82f38108be6ef53b5314c740181287dbdf3cb12406bf2b8039d1d5e1b554a393c49f408060c6b95c36dcd76ccc7e4485f285dd663b327b7081a028d1ca4b23922486ed0ff025ea03852646e98b9fc87cd2bf2394f2bd6ec03bd68f45cf4d736492fa64da9b91340237773fe33ec5151ed5c1ed3c9ca8c7901fb38d53f0ff14eea82a9a36f0c01abafa060b40a9ad601718ca4ceb9880ea7084c2420a952ae2ca57851ce247b911875d33a6eee6674f704ed1eec6cd292c30c378d9603749a2899821d00570e608d6ca070ab93a0e05cfe0aebee3b4d8b2003d0aaff6bb9e9367573ce87d7d2d86d12e37d85d774a728c3f927f2a84752600c58f8267ff0a2f9fbd2f29c5302eb4d1bafd342174b037c68b08517f606e09b010109873e3149a07b981251ab07f21ca5cf93fcee3b048aca3de1f2413dfd5307e03e4c38c8564e2f9b7f837ea7952c8f9c26d9ec347bca6358cb37ae8c39c80ef3f02ef276f4fd1fea1e18b889c14464c433908fd66a217197a3a3f2ff52b200755451f326b31ca06aa508bdfffb08407e2a9ab98cb3e671b4ec061980cac207a28c9ce789c7fb925d2d47bb7fd93cece7f403955cc748ae3e9c513b8a0d76049e110f4b0846f514ac85a1460b81b31fed54e4fde430e076781096a8451bbd04914268283425784257e56e5b5bf1225cd93607dbee7aea7b4e48ceda0415c707d6b52332dd2500108233176c3cdf2598366e8d2cb79c9b6b510fe571d0e6b102b8fb68cd36d4dfaa1cdb49ecad1edf315a0940f760b446b1ea5fd47c76695800f10085a8d0ea54530c26fc3f70c9425eccff32b3dd785407dcc1bfaa982f280452963dd4e9d1ebb35c8407ec7b6717268696ce9b0e40bafed20ae6271c83a908dd0a6c8deba1592ca06917b404a15c4cc9389a4b672165b3cf88a940e0cc4507efa4c120c006d9520e407618760d13f61336935d728c7e289763952c78cbff05ed0159b81ad1f0e594fb4e8213ec4f8b46fffd2bed9a6eab49bf3df2503d0f0cb3a6bc9e35ad57201ab9eac3e71beb40c61aa76629e6a54d02f4381b6af77e0546f672f660bfcea2aa67db4b8cc84bb7bfa248167f43aaa8bac8a6782ffca90c true -check_ring_signature cdcfdb5eb36863896efc98b5d018294013410b7f7b2f246afa16458e54838416 559ef8e8590b81fcda4f3313eb8bf3e82c8185217b73f18b857c05e3b27bd947 106 9ed9c8fa58ed0dbf86235629f72875edb6b9355048aa8ba3428107182744e9b5 e7bc43d2924437550423495aab3967d49ff430f9b4ef7f4469957b10f2e478d0 a47f9bdc0250765babd00114e6ae737de289ba4a61f79582b8bdb5d3c66276ec 64d25af001a321ae0c25c1283b21dba5e3b2e4c0ccc7570cd6b9b4ee8272e7f3 b8c02aeef2dbcb8396f9c31df44226da755bac45645b9af5fae6f38800f71355 627f6c43f92d267471f5cc25145436996d4b408c7bcaeae700e07e5feaebf417 0a896a4fcb3d184f592e1fabe39c101d0d464dbc434c83c65ee3dbff378d33dc b8d0f9332afe83bc4307a597922229706d568952ae249bed8182d1d6986a595d 3359ec0384052f5b26f71488c5c1e1afd71ac368e2da45bdb7d2a58c8783543d 7e682e2538470f41d68a4116e692e58336c0236cadd23548e3b765f0b343b8f5 c4e52f0a9b4f05ba42d471ecabf780ce5a093ce62065b033029cca2c45be90fc 97af89ca6484f919c9cd2a45c7da3d221abe4b8f7c576275205ec6aff94c30d0 ba2bfbc9880cdefa36f5e6355dcd11d9b4ce4aadab1535cc303a4d7d76085ff3 dbdc310b2c844e8e9c1d884ef6de8d88d581899a8299fc3b0e6743177b679811 b873a03c6fc1a3aa173263e4a519186b19ecd2b1c2d0ebc71fb6198f234523d3 7b2f53293323aea20aaad1c97f3737607139d419ad76150fad79ea21380ec5b5 50adbf6160eabcfd84e948a4c2d59ddbab5cca97c9fe3832c03db9112892ba94 7366702e3beeaa007fbb87b5ccb4dfd9b31e3f959a451e22426d330c9721e252 cb5a4f4e81356c4f4fe2a535ab30d6ba765c91a3feeda445350d47702139e8a8 ae8a013e0bc133d940c8a99b847bf7865ab1670654fbe8b73674163a6a574134 6578d1f6ce967d727486c32c82dcc24549d88a60376a0f98eda4bec9800ef8fd 8cdf67f8400552fe8d645f012b42b24119ebf6dc83b1d31bf9a8b9e10b7e01ad 2755e9a0b316fcb00c086c2fc138f01451f43b7c7a034b882c438ec46b288c57 538ec2496278be090e3d699879c3baf69ffd437ccf9bf932e671cfb3a14bbec7 56d89a096be320193ad7b63d7dd32ae7feaf41c3633f0f950933f0d268f9040b 058bcdfea84ea161142d6b3d9bf601f719e855f80c80f39c163c844ebbe50ac7 51863c1a31d104982e6645e23ca107e0c519b7e70dcfd561d9d680e3a0fe5f5b b326ebe51565bca048b67c4b755912a19f2d16bdaec3d26219fdb50cd3407e0a cf9eaa70fd005ed71006aa41bf727e026e7b950b8ecef6c1003e4848b01a129b 28e3011f9f629a3aaf425fcd5dda14f29afcfeff9eb41bcdc1e54b9dbd6d61d1 3b8cf7859ea72490a7ebf07edc58a9842bed34a3355b366737212695f72a25df a18a6485fb0019857d6ab543f33b62f965fbb90d8f260e8a87ec33c1f531d50e 630df3ca46b4f7220260b8e7b38cf4ce5ba0f4cacddef1e0db621d6fe8c26cb7 cf7d396a8a413b6f14608e8956b45236f352c295e06601ca7dc448ae578f8b46 c4daee0562496e23f964b12d6e7b4685f1299170ddbb6d45ebf392677c2a1cea d2e85b2311595b2f35e53c1f9b1f883dbd7595458cc8cbfd6b377c6fbe327d67 ad355e8bcf0199ba4ce00e651837c5c07351cc6b0e61a3a585194ddcc54372e2 241fb53830773c3034f407daf654bb5ae7263705066eb479f9a89d9eeb82c698 afa196147b509fb980656a2214555f0a7c2dc97186aeac01aa83ce88a75dcdf4 8abba5d549a1b60b7c249b40517f8f13112897c4715689e3ca0efe2999e8131d cf0b72c49deebdc2c247460da5c272d8049777643ee781ab0eda4134048fd8ca 5a3733455630a9ffe7a5dace6aad97071a0da4bf03aa8660c0f9f2aa39edee1c 98ce5ac494b54780e9c79dfc8d1811ebfa695ab92cb0f6726997a999b78306e2 ff18149f6a4a5811b8293af513fcb7bec9bbcdb1bdde45d4e23e47377cf0fab0 877a43055106b48607deda0fc8a26c46d35afd9534e5ec8db048dfb3273cbb05 9572dec37fcd8a7f2ed87e59448c05d42448d28eccbc3ff6489de32d3299bd3c 1665e49d00c27e1f54f0671ebd92fd658919898db4ec7fcd1f2237a38d8f3d14 a081a13707fe4b57084b9bc9b02cabaf52aa7a7ddd910aae6b79bb2cc221fba5 465c7d76cd07da90e97961b27ab2ee521061005bce4aebc64c517e238bac1fd3 fc51ea9a9752465f8a4fbe6ffafdb49089c233836b2a265012f4afb32edef207 19def2f824b1adcbe606112e64475dc8fa77ce2a7f5e38ade558c62ad3904856 8b0223760fda40f3ddf586aa227d681dca8ce1098910f6cdefcf69e28db4625a 3ff495513f5e08946e5a959ac0dd20801d7f09cd3d4e920cfaefbf1d9411882d a614d24965d83d97e2fca4ca6f2b230bdda94e660ddc1832f9e362d59aa88d57 875c5a2f1ea43c3eee0cf4d85b9aa5c96587ab515f53cd92338ae1a438f0a7ad d0d8af448ceb88417b972e2c976d05de3592474d44c28b8131cbacafc720aceb b996b33a47fd347a27f7b9975a4bc4b0c79e75c99898a836964cb500e6576cd0 4c26dba10e2761ae939ee180748a45c3b0194d714d6b67bb1faba11866472bcc 087ac74e3b2e382ecdccefbe438158ba867b8521f56ee61c2445800da3869942 f1fa5a0e5dd9f9830b1dceb775d7e3559da8343b0547e50d5d2347c562eab837 d35c0ee3d70836e895b9fe134fb74cf89f08414bfc691ec3bca3f0ce6878de22 99e7f270364c5b706e67d2b96add8c71d69b95177d76c4c0dd0335a955d99ce4 d13d8d2580abc7c823c1c8feb44e0157d2d0c88b2647f505dc5016a3f37d2f7d 1440a49d53b1312cf53acc653ef8af1fade7c18f1412ffa0d9b80e082b14e7e5 9a453bb9c2bc69a34275e896c015d1a0ea5757a0f7312d44909da9491f1286b3 4ea631093c2a63cacb7a5c16b4ad291c4365ffb2ee38a552e619c4c20d27ea8a 9f4023aa4f1da54310a904f7f0fb0059b17803dafa67375e6d5ed909de835c22 b0be523a032f0d2c359d139d9c1a78410967ef02bd39c374a208f56e3d14adfe ae2d55d96373500c3b6b5db4a868a0f8b785b7f75e855db17740c066c53e54ab 3cbbcdba7e8c586eccd1ed61b47744fb80ab8e6bf9d040b4c6b963fc8177b016 19e34d0f28f9895d48711e70f7196ee0bf412c4612e850f0179986f3496aa9d6 8aae3a7addc3de1e0b60302c85076c4bd009a684710279e03b84c960c65e502f f6e2e275676e9d623e0d5acf2d0571df70e44dbc6fd42b2c6b5bc1bc7314c4ba 76220bff83a0e12415ed7566ab3db84eda62546d4b0a6a94fe55706a5ac2d0ca b12912750394ec5d0d2924129c4684edbbd0b677e9d1c0d840aef956c8fca02b 729cafe12460725d3c86d81f491e3edc70b3abd19cc74604708ae97425df5f3e ee48177fc038b9ea25b0d7ebb84628857fe87f15e2fca1e79711b5d9af4576fe 484c454b3c3fc4a360311e915c76f18e110427996a0b168637412b6cdd509bb8 8f3799434f9a2cc23ed2a9d01d79ce294b9ec328c8b2ef010549a8bc5455534a a1e2c4b2d171499e28bf4feda715676472e477812ad742f04f42bacee4ee4759 6ce982fc00faca08ab6f120a80d44724f85c6894a6f36d97b7e15029626e13f5 0cb4656158201f7e42f7260c290569ef7abff3538dbaeeb15657dc1081fe828f eb9331e9cf7020b6bc1efa11e229b85b5b5f55198df96302b1894561e85cfa2f 5b3bc0b095164d8e9c852b988254805c39849e936ebc395a5a92d351fd0d9717 751e619dea94523461a032b1a5864c097e9b9ec1f29cf1670ff58bd8f281ca1c c10e088c83587811df782eb4a8a3c79c2e28b87caa63744e5371433c95887e49 b2d6858ba76ad720c879bcd8085b47366a37a27bfb50c9ed030953c101dc5ea3 ebd806a151394a75e164bb87de227231d520e378496d8e29fe02d44053f33d56 ff33459f963d9b5ca2b694721f29e1c4e8aa6ca71e740e1d3129a4c0bcbd468e 46dd0f46306b31ddc190d950a4e65728f6794328602dc08614c25832279cdca1 457a9bf934bd8297f884919d7623268375bf862d6c3e50f309224d7977ac0794 bf7f0acc25df581814ff2624ada93b9c35535e31942df56aaadd76eaa78a211d 3d4e11efe050e8becb903bc01a9c0b68f33cc78e269bb55e09b2da67d9e4c20a 773a9f78bfa57ba3485ac5a615cbcb786d1439e86481ff2b9799714f052808c2 a8f7c939cf35dfd7734fcb4472bb48f49ba08acff58a465c672005ca94676766 7f0a62002813fe991a0ce5a5d6934bfc7583cc2ba1745f3ec061049c9bdf6942 bf40fdf4093ffe5dc826d7fa71a26f86246204727094ac95bd74334ae467b769 70943d95f0830c03d9d0dc4547371589872017d33d977867971650254fbbc6b8 162cd4b91a7db76be8a3066169d2d0e3352a855f950e2834d3e82d764e7ffa80 23dba89e71a9f7895e88efb946b31ebc2647a46334c25c8f51766c49b0716895 80bc55152b115281739d0b6998981c9eb02d510f8fb2358aed1504c89ef4d222 7f0473285da1d6701afc1839979cf6ee0762c8da2160970c93526e1aa226786b 34457d9b32e186927fc7157ba5c9a6104f18e77b499852b23dd94450cc411640 89ae47dd58537d1a3e77cb698d652f42ccb018bfee57664fc048900d0838473f 80245858fe7b04c9e76a0531919046dbc5283f0015c41bfcd8673a821e79386d d655ea8eb7a4d4d95f4ec958df01cb0325866aa2c2fb282656bfc0ace0caea96 fd5f3519d7adaf882d726ffd15343cf0c27ba8cfc5c01b6ce55e1913d3a1930e2313cb1d5bffd26c32bece5765d13bc42be190312ddd092555d842d350398807c39d9476eda54e90b2b7629a3205a622a6230aff1cc8d3d05b81a8acbf006b01ea5f952c1ef187c0b51b376f5947e6063dc90e4d949a9b1cf966abccaf821e015afacad51fc2cbfc1a9fc8a1fef9bedde93db9bb0cdc230393473394d13c670ee5e809a133a843eba5e12abf2788165bc0fdd089c5e68a9110bde12c3cfac60c3cabf19c2c64df4bc9ab2a9696d5edbfc32fe74656e928cac207e66d3c90ba0476dcb46d53dadb01f42c244fb4a5ea75d1b79e4fd4fa2654e839506ece73ac0666e1c4085e5068ff0d093281119861e327041b81f785fd3b12a65b32e7aca109826c3d174e62e6a312597f8323c7553d37b10beefd3711733af1e55905483e01f778dafd16c0418274b11859ea15a15b67b84534c851c7d2555748edbc52640540dca4a0ede37dc0b9b1eec0a42ac6a92afc68ca02fa589c74c6b0bd2145d50109d8a2358ce1690b73edeb2c3ba5b9579163bf696e19284a5b113e9f6831c203b894e6857422e448cbccd98896285c1aec588de93a6de412727107f21449790dfbbabd8e4504ab3d892ce0047992f42c211370665d97a351d1de83c5f8f9a70b88a1b5288e487c7cd1806a29ef934deec378ef01c5d0e22153cb690a7f09dc0d35abeebfbccc0017ddd9f9100ebfb76455d2091f0edb4119b49b70b427117b03e176e5216297067b2efbad720be3214380997e723662fdaabf6732d21836080f2ae038e487d701b431606a1e74e852a7406947301ca1a33918f386a21181b807c99c5094c4fd50b6a2912623b6e36ea3cfd12e434edfb03c4abcbd26c5e96c0a6843b7ab1642523f084532311b35f8b09f595a197473e59028c72b3d4e3f1005fb6425973152a6f46f189ae713fd9c3f57c9594503b8e4b1dcf8bf04d8502901b5753483314fab0ecfa39b9c429f868ee0c8dd27101fac55dfee0642f713060ea3fec7f72e71469a51805cb7f8a1394c0620db873de2efa2f7cafd948b48e109fdfc053d445db70d7b4c70509841d4c56b93dc7fd5a0ecd17b3eacf51bce510909ee336f6f64e8c11dd2fe25d0e3e30561749269d9bd84d9f5cbf538ab9db3028f13886d1a8b9df7963595d102ea35b7a88c2d9148fdb33d4bd6aeb486c709065a28b0e1f8d01268ef0af01f55c46aa2b803fb1880ab927c4233520d6f154109591f7ed933c04a7ee7d0a00f9aa251cd60e001dfe719fcf7acd6c697ef884100054f870635f0226d340fe42253f691b02f93241ede408ec95b6ba8c8fef2bc00f6d0dd1684269ffbb8649e98b17e7782fe99956815b6844b18fb1ebaeec8e005884e598d4870efe86b186115489d6a56c1a87ad9389d46e0349fe115c32ca70a92b7f00c91a2d380eadd74e9388581f7df80e2d3053319f5969872220acd7205ec260bdd0340ed58b10777196b3da261f2542d87387fe77e205c701b864ec20236736ad88c67928b65a9f9f11f1499de3dddd10c2625dde749bcbac2650805046b7f75bfc114a7e1db66942938748f46b5dd660d5ec50e97c9a2b39604ed8a0ae6788be7dd95f3e56051805138a4b836f594e498fe53c0744f5ba7eb3126e00287f7d1f391020630e9e62251c934d25795c9ce68a3b6f8983feb8a4674ef610ae6047197428b3b010c44502294166b7e78c3a2aaeadb4f7c0076b4f13cb2aa0f8eb010f7185a1f190fb3358598c33b6413b00837529786a1a6214c1daca6cf0979fa14234aef0e662e27b75116394eebecba24d05f011fdf38498b88083191081e512df822c8b20dfe6fa176f7c2b3a4a9b315f5fbe6c7a79b0d23a4ea294c0549306b3f681521bd43dab2301ce40c5aa1cde06372b819d41ca12e8fcb543a0789612deb6b31f2bc3c99dde3f964ee1f57f0e21421c4201355fc3cc7b11bda0dfc6f2a1cab9e7513af8cced41e8e043d0a1df6e123067525aecaa5c37874c90e94ef31e0eaa3bf274c7bdf156318e302e6ab949148bad78a7beda1eca52b9b0fcb0b6405264835a5f46aa72d3d752987b58d8fe681eb653dba1586c2f447bb024a683599517a2d5cb3ed90e197c25e3f48b7fe463b95d25d6f4e379720cc080b5ecf5ab203770189a4b7b820b8a50d307df2e04e046a1f27b5ae784a4119790d9191ecaaff3d019dcb4af3f2d94de95ad82a65fb286010e2d894407e86c632043081064c63def056e87e8513a969a7bf50f0b7b1f9cc2f24a2f555ce800ec7044f0e371079c54b42bdaf95c19b97189268faea87444fb20ed30c0bfd180cd800b56a3509ac8290c0fe310253ce2cb1cbc4835f35597fda72e273603c7d75c907447bc64038448ffe89d97e3293b7396ba7d6e700e756def24a918412f17cea09fb9a7677d638600f3aeacb5c6515475bbd248472478b6a400c662f8901550d0146913a80bd56be4f7fec02aca63a19a747167247bf71cf38bc421e281e3c22044d016885ff31fd41a80d2b2683e38c1cf4013f09d60386bbccb461120a90e80e64c037e4740181c3e5c9a9dbfaaa7447476e24f97352b142c4168fbaf4d3ec0b8c8e9f4c09e7a9ec0e596b2e73e80010b7a57bfae8127dc4fc20d2652111be08d1f4b935af18f31867f365d35560248a9b397c75288b1f974820ec8350d81101679553ae6904e2370f1b854ff160cec62edd0d8577a40f5257a9bd93a29f270e503dc31a056a23cc936cad8333bdcd45ac9f48f240bf536ffc8796d39e21f30171dda89d75d2441116ed4043c8d896a5276c175d6c895741fdc6a7a4dce5ff0c01f76ad958ec6a7004c7982315f9763eabfc2f8375b381df794559da57164701d5306037b0406a09edb812cfc21c1e3bdcb1dfabc1a949c2a66f4d0e6323a50a695f9525dd78e554232ce7f637116405cea498944aab5e9909921c919b06dd09aa2111f94b44fb37439a24fb561c33e58057767ae28174742fdcb18dfabdb102bd3d5fe543078b901c8dd5d825da5d4d9c3da312d428ecf10ca5c9f037fa8e00746187eeb168cf3547b0ff4f47a13bb6652deffeae825ee4aa86ceab1fd2c3039ad9cb60db8126620f843d2f8db3c3f1e6623bc2154c4aa3f5f97933c715c80d6365c1e4da2d78b0977a1753657850ed85ec37b97209bbdf7e9c608f30c72b0580f9e8ea4520bc4c103aa1d41cb40505179e9798fd4769052ebda283b97e420035af80e1143ca96124153a2ba0b859e2af20dd3764e6adfeebb144763cdd140eca69141e8d8fb81df68f058534852f3c4f3077163055ca75ab86d147206cf90bcdf72c7eb1e6fbec95be5720374ae534700c4896bfb1f629482baa6b61e5e5079762f1f15430839bc7a1167dcb970a97cb9179d9ec5e19292e424fafac762d0befc544983907b471ecc842f32d85a0938b8852f93e8f00e7e0d3d6825ee3dc081445d18b4c8333fb415fd3f0560b2eba233aa9de92126a9551cd8ef114aa740fab27325b42554c52e6fb3eec176bce48c969c2b4d39120c3dbfd8a3fd1b6470d6520a27f5f0033ab6436c51a880bd399aacc3975785b6dc9be6817bbc36402062448aa145917b2795c366fcbe283c505e9b87dc58069b5403f6a65ca9535340d726c139639ed6d58debed1f8d54ebe9bceeffa6f4f2a1c2c3bcf4298d8140208e902ded75e8c7db027f055c705db42347edae4acb32d9db616f7fff28f7db50f7935d33a3ad54a72c96424a47e163ef89c7926e907d17b100fc5f44ac0c424043bbc8084202305046d078e95f002e1a22341a366b59f9fc99c6b15ebf8d0df096a9f6d307fc5e20f3dafcc3f96638e9dbbbe2672d31ad414edc676a9ecd3a403475ae67fb88d48fbeb5b16cfcff7c66d02662251f9df0bcfb2e36455e18235041dc9939ca9fb6fc9f8207a298a182daa9278026e0fbb7dcc4f02a18936b3b4007dd00cf2926681b118f79053b0b26cf552f1d244bf987139ef04bfc2121d910b7565c806f0a9793dd50f0688acc469ab31d20ea6a5dcae07a2e10c7bea9b8306fd48b9dc4a7589853f908c462688f96078682d7e980665769c3e2de3d4ceb00aaa9786a7713028acaeacc33d1daaca2f0ef751b767e4658916f8e383199d220651076387abe1b0c204f05c0303603abc0fb959d4a5e6ef4ce84061cbbaa8c2003af86a182d4f5190030b3a4ddb7d1d2d1ff28c3e9640de684b62542853e6de0eff8c8c2a03fe74764b09ba33f240bee19020e0ac2ba702ec447976ddb0924608bf7e69bf02c11d675164e3f780135f187b793a2008120b6f9a3829e91d9fc70f74c00bd626a3a99f01e899cdfc425493903d046db9912e8fc653f355e384040b7ef650a42d3da02b6514e3f2857e1ba00c2105944626a3dd6885a743c3948901d8b74d6a8cc9870b7bcf0cec85fa24dcbb5145d5fe7021ae2e229d6fa7a2890b4dd1941642f4beacd68d1afd9e80318f41a4e6045ebb58131e4200d1840988072bad3567aa8a97496a2c6597328e67b91966be5f7c4ac49ff88534c5343f710a2b1deea6d3d3e4e5765a057333e42b0c2835edf4bd96c86f054adf9033a38d07649e045b03dd80db442a917d9a1a6a16722b94ef0c28fef1c84c80ff04a35004af269c3ff60c1b18a1af4b0169f069a975e42c8a09280f92cefca0c09ab3d40593dff9bb98853b4a068ff992d81881aba0d6bf999719712fa92f2c37692b020f6779e4e21797b1d4ddeed72be79f1ed0305d4903025f837966580ded66dfac040b1a4bd56140069596b0ba14e1f8b54c9a32f19b64aeaa43bcf1567f96967102463aaed2a0235f3bec8e956f72f7e6bb3de61a414436ed8708ba567dfffa4a004a5fe123226687218e736945103d61d08850b9b3ec4153b4115881cc6a14d6032ef6e544e89a2978b66e50aa961a8fe997312cbd252843e86af6053dd556d40d43af5012c5ee29bcfca866c309fd04f795f4611d3fa6b0df3e1835bc8742b50ac8e33e69f1ad33839c9682391b598596afc985d549536d03f166a7ec6e6e9c0bc9df1d07437cfd5ef1ea0f14298bfbcca59968cec21dde62d1ac904cfd999407489d3c538d1c8d9552544e4ab193f05b441920467f2f5e01f9e4c59a08db740ec1d25bd2e65e2d474ac37c1ea19a599f1dd1859525c0e06fe85069007973c300875d31dfdeb629f715bf69201157e31bd641f7d86bb6ef1573921d82afa7b60351fa68ae8864a4ab49a39aa3f2af3b3dd593958e4cd945dac16580b9d2c8e2042e4456824392ca45f6f65f87f1b4faf965ffab27bef130afd3b1d451ec6b0d09c29697d1db64bf22c557c3d13f80231e906c3720cf6bd2fd330b68b37b23660443ce3e89259735dc7d73f9aa9bd126e92d0a61a8c6586250b3f4dab44dfe4d0cad154ace588d5ba3767ef618013fb05bb7e3dcda2343077e319688a7d708770686c4c53cb6d9044f82508ac898b8630ee7b5188276f22ca61b1910971e99310160c7c019727f982502ecd5ea226be630ab0208f3e380a4305e034fca7bb55007b769656722b3e0434151c5b22b20ee2aa286cd91e218cdc70e9c89c778106704c939602e53c7913edea98b7f5d56a98d4b0d81565fd4f144b6d203046572160098089cf53f1772d80f63c818f09eff924cddcaa0f3253beebf38639022743406e1b5828da057249fc3b54257ef625291366a7d023cc6ab507250713250a7360078873cd24c3def266a3473ef69d4e1946cf79237e917489c824be8eaca57c7095771f920c3f674111f7a48551c48b02db89afcf981aff4f0eab4048766ac580a52168ea7d7f46eab58f2884ebdf5a2d0700720b0da246c503ce429a9e6e53700faab59538fee049a5911e01500a0f6bdc57b07033583b7598e2a9cd3d5dc710bbac28d3b76ec2cbb0d487ac37facdbe8f3a27b76cfcdc4638fa5f9d76b764205d6eeccfee7d850d05ec2a27aa02fa84033aa5cb83aa633fa3fbc47cb9ade740f8a4860d4bff9a1a60620355cd3d63b95ab8d500618a1e049ba66ddd41dcd9f0b6c3116e2fdcf1ca6284197b75cd78174150905e0f87b3840587962c5b40def00ec267760b2b306ddf44d60332abe41859218c6b398c4bbad2fef0d91ecc3110ed8278b8f09a510456ffb48715e511907fbef26d4a296075aae0771b4f4be8c05d4705088b497471aed5ed9b580574ddb95b5297269bda91d38f763cb22784f073dc0173334a8a263fbbef151cdd7d1b17bc507499edb99324e427b11bc89980822c0032bdd1e5edebe3c49ee731f852c5cd202e3856408c82cd3cec790fab90af59c9e6536dee924248dbf544c8014538579f635af614c80a1e2cea1b012a50a6ca5e86dc06f4af5f2bdfdfda59ca00d9363a625009839b6e94cc3020d0be80905e5f8612abb0b9b3566c93208b87071bb0410d9e0ee88bd3f0a7d9be945450d1f6a467da85ef2808985b94f875624598371c768ca0128fd5424eec69ac0c4068bc1ac6e1c9d08d7d6b941fcdbbd7b3996387be94c153dfee5f87abf0ed9d90119d32cb717b9e7afe6e0f2e8560a5167741ae5fa97a74a2e55fa97d51ca4fb09a3728ee8e366632a6e8458595c6d170569eed600a574ba5d57c1ec5f4f57d9091b571936316891707c589de4f614b7f1e45ecc6d4180c0d8229fbdc3cb60c0042f1025313d6ab6f2cefe88a932b21e55fb3e206b36445bc8e56542f8b308e5030032f46059db1d2153e04405d7a2fcb18724975e0ceba05cb8f31b0e5526330c6ac02989d778861cec9f4eab4fc1daa66128749f0eca97f242071c978567960f10c4c43e1569403efb168b9f523735b5194e705926fcb45e68cb12abd4f7390def8e6ad2bf59ee29689de701ec90ac44ef52d939d1ca077b6dc077c651bd710bad7396eeac4f3687a1955927d2a865dca2cdb901335888a853933361bf40bf00b5b9a3f313e20a59ac3222e52e5f9e24313e0ae5d3a206f9a46005ad47d26908a8f845c81b933a8a4567b1489a8844c5e73991539d82ce7976c0e1637e63cf0a816440e63577aa7d11ce3584377d6ba55b119b4f3670705d0f6b1b21ace9a10f979254e0970d0c9e2e74f9eb023d5c2380bb2164534814c871e86834d8dddf0cfd1e43a5e3c995bdbcf09807450bc3ce0698e8b1af2cc09eb4eacd1ecb45900c824e6f7a00003d3a9bed0ed4246c66b09b23bd96df7dc19c2a0019336133f409828276659c592688b340be566e0d94e7f4a0dbff00490971bc151f7c1a0eef0df2370ad1948435b3859d1848fedf077578d385eb70968b5b15d66a18ca25ad0c8f35d2367e7f2fef7ed62d7b218f84a0296eb67268de53a1388119d973fad70799d8b06d86f7fa36c29889d8764ff0cfd907d5666a63eec51beed5fe230ee0501ef376976cded2acaf996852971a53063703d21e460b395cba5be410fe03e00f915dec85a98f65a5ffd71722fa854bc188b4669838330f7e8e6a1576d2ee5800a2d8596e12df21739460866d1a898e01dcf2494edbd8e785bc93a9fa075bbf04d6777af2adcdf9c07e8415e28c36ff053b89e9bb123099c6f727e401e520a5077565ef29862183d6b4e4105a5c2446514e5ef47b5014b2c815f75e5fa285b80fb9e66e54b3331bc48d1b98c2cfedfb3123c6cc60a12837d4bb680dc34e469c08c86ad28c71fb13dd04007534af664f49b0894392c6fa6b8bcd32ab35e857e60b1c2b60f366a11b7b4b7f07cbb04a33d5dd4dc4d4c6fee2ed6291491f904d2203f257809880d97376cb3fda11cfaff8dce8141657b1d8cb805cd1df8e6b2df207195acc3862bfb8f422fe89974185374bcb2bd8e63db52d48ef034d9819ab37098b554dc31b36998895ec9fa83d92e6335d9a0364f50950ebcd2ac2fb2eda4a080aab3698a6c5b4512b73f6a8b50a996f5d3fa73254284b19f6bf45f56e412801fe22175367f7dfdbd3c47b2f36626b467884e72604738dd9afeef3d9201d640ae1d54ddd36d81f84efd46f866bfff0a71cba2c2e665c99732f3334e1aeea0c0ecffd92ae31fcbb5f1ded3f6b448cc92608886f02934eddcbaa8c07a9d2c87f00d467a82908c2c395f03e39ee0eaf85a2eb409ec1425bf0a1ba03d9a2702ac60e7423be64d1a3a2d5cd05938cfcbdf4405ae5437aea60d4876f83903bc6fb22021a6bebb611042f6b58b3da8b497de23f287a7c37f6c84051bb7cf375a1e76b05440d54ae31418f8cf71a1d86a406aeabdda7554e6242d80903e64ffe35366a09534fc50b867d3a8e0d9f4f18b7047d6efaf950ae4bc203ce78ff548f8a543d00c42de44c49bc403bb0364c0d5db7ea62d3500aba5395848f015175361954bc0be87d7277c6976f59bff688691fea5602cb94763a3ba0d33c99b3c0bb2c7e90020255949f935b31f24389342a46e1841feb12e8864718a2901ff11d9918674b010beb41e47a9fec3ee607a27dcf6ef1993c1f943f93788198e6c3d1f05481110f4210c037de20d095c2fe760c81dc38b3d26741c987ac73fa2949818aaf0e47090dafe0929309b3754d6b53ccbddc00ac26745c8d4c481f8aa2b25db7ab73a501d9716c439f0f19a72b07ff1b14efabc4db1c6f2a0de22178dd6724d2759ab607bc72856f021412bc7221985e6ac1a846ef4cdedb7d05f1fd2a65fcacd8ee3b03b0547a9eb73f4319f05d71b5d1813dcc0360b5da1b488a9a92b4b8c1b823cc069b6a43316bb6bc42eecd2546cd435b4c9cec844964c0c5f77ada2936c013330296c3438a93a66adf10dbfd1d9d9772ecf1d00ec6d72a2a5d201260161fdce301bb7f3e1f8c3398b72d3501fe4e221a37c217d321c23130d52ef7ec0b158a2501ef2427f43b3385b98cb680e5c6b40ee98873f0b623076206de253eee84ec980006d9b07b93e460d89273f6ca27cb3d2a69ad508e5618fef6be609ed8adb114051c63feab454570ae6f574d1535b866ed3f30061ac14717e87ce745f3316859068b550f909f97a1484d51b5935e3598ede7ca67dd024b78e9b33d2e36ca2e93050feae9ab6590e84e9b5aa99ec3ca12a2f66e4e378a1f9fb5496a57f0885c4a02ab532b8fd12f90e9b2bcc39594015da67852b40969a8502cee2d4c1664f25d0998cd8fbe91685b45f993c0ca4ae319a3ba27cafce05835b77d5aa6a1f8fb550632f457455d85ebbc66afdccbedace6677d2adfad40844bb1b9833954f8ede202c688c436a3a0c0c7d3e21e4d17d988ef75d1450508748bae39a87d3b97236a0e64d56ccf8848de8d176eee3df2550d6aadfd40b9fef275c1044bff6e2dc659049852c8bee4b09cb431f90e9d297dcb9b7531ed939a8a6cbaa074950bf97e05038dd60be3b47717abccd66f46b05dfd13a2fadf45d3891585168b3f079be72703e56c85a97f6b67d061d315519717a0cc90e57ace23237eb642f5c6848731c003ab6e9958ff11f90991ec8ed3f2b96e47aa003dc493197928f1551402b193540a146bb0e272c63b27ee1152eeadd1d82d47c7b86c5fa08f1d251c701746a2b609ca0b1ef21dc8d1b00d88bc3844bfca17bba1a5cb456ff0ba51cf718da17a6406 false -check_ring_signature eb5a0d2fb0759e13cc724cdf0f840cea0ef06c544f76b728b56a32d9a3f888ad 9ca4f8a2166485e62f6f0d8d7b1ffc7c9e39d9b710d33a18a3ca72a6f6101326 76 751b4dd01e166b6ecd5107969934ac990026655a37bdca32ad17c124a610838c 0839f02dab1ab4e5674e8f82e5e9e61d7d25d414aa78b8071acfb08d1e9acf66 cc28590e9ae2f2597442ed990ab92f902170db16fdd91723ded39cdb13e7dbf8 4644e50e0e2e94df5bbf051ae9e1d487d6f4905f86f5b54d98d10e58ff9dbc12 cbdc6c08efe12bdcdc0c9695ebd6bf1b29aa860b38dfaad71198e064b72b2bda 2fb49fd3cf917e312b0b09035a1390f543b3ecf1eeb42c5e8bb24a99fe37a2e8 bd0eeb059f029bb8537030c94114b3dabc9902f5de136214fa87719bb9d8dc91 27864a780b0ea5be7c3c450dc9a2405d7dc68734adfc79d5bb215d96ee5494d9 6ee0a30d0f68bea7270e185d9ee6b51721e5b576f9d1b2d872144daf5e1fbeb0 98845dd51293e0927370d0c237f073a9b5eb893ad27e0e32379d4d1b23b97c39 7b1cc142b933a154379f65a583d173a4fa2567ee44ec987256e2a11c53edff9f 670ee1ec9d01914b08f510198d5b952271b0e2bdd70340a5b1ecb7aa9b178037 1754048b5fef34d2a0ecaaea91e9e47763162884e3e36d5a21fa51236877ee8a 926f5e525d9c2493a0f94560d5be3f0ee10b6a720e0cc88623fc481d1b3cc415 97d165833f4f7a33a2862200dd30ab60ac5442f2adac4976ba0a631980fe9d1b 1cdb7881d4ff3ba4b3f720889f86ef1cd9ac28c5f07336097602c1348b708371 66df1dd88d3748943a0715c09f2ddf3db839a5dbbda63f4dd25f4e39eb64a284 0383d483e548595a90fc86545a34fe9c0a8e3f8b5465435a31416be2c9625402 339ab1b70c94c14d9c8cfe18c7665725dd43751933258105059cd5ad275d28ab 8c1dd965d0efed274535c1fe8ce3963bcf1fad89e9736b7e10d0c46e985d3f40 caa5cdebaabc399862039952e6d7c64a32eb72fe7e58192f7dc7e0e2687e960c 11e29e5feace00a663c89ed4ece8b06c0b01cdd90b050951d5bb5ae08a736a47 f233ad66189b42ce52d25a1741eb8c0014dd6c8d83277c6ad8183628c95bc72c 23dbc06f94cd71d61ca58320362f0c4d90ecf1471d1c6c5884f610aabb564394 476348bf2025aea83796f488aec62756dd6ef571d105197778d45d512b67120b 9d43a9c18b078fe26df6fb0d77346ede46e97074fcda52960160820059aba14e 5c8601bd3147afe58b958a219c05541fbf4241869264d0a4c032e1b95a60a7b7 8207c8ae669521a178baf0276ddb5a4581f1eaa24e5fdd245c0926ddfc6daf78 a5140ec2206b65d7bc28e5200d7017ce122eb8c9201a0a19cf0ef44f8ba1c164 f1f8bb3945cb321b50a5d72eb0a09f5d2dbb6ce7bd76a44c8136d75e0a68a1bd 48f44d17876979b1bc0db8e4b4fbcd5f4b6e2c8b2169002f910047b238562b8c 16d371d3524191c468ea9749e8956cd84137493e4bac710d06b7b1f024cfc54d 5b347c32fa40bb7253f53eceb159873fc5542e79925acd81bbbfe8a7d9db53a0 c575f7613fc952ce210608a5d3710f837f0dc557ead1c266dc02fb4d4e60b5de 859b61de64696b60eb5e7a801e8414aa1f3057c95ac94b880e345587dc004fdf 37cbfe2d6a16ff231b8597012c538014708bd79d1cee18e30aff918b42ab2652 644cc43243533ccf766990db58e64e76dfe48e1b1c038559758911ba89a8c62f 154f95eb55460264817b1a580eb0204a849df613cf3f5e9ea46da546851e8e89 a749c1d4ce1ca6e52b38374c908ab5b2f3539c33b12bc03d7224bb20ec6ca95e 10c4dd0507834004fef2b0cc1c7f5bb120585142cda2061062c66a2694e901e8 014af8691e3f6d6d0ea71d24741ddb3047e524e07cb1528e8dafc20a6e61e732 60ea2445ff21b97bdb503e54d88be7000654a46dace3908e92f90484a2f77e05 0aaa73ade3bfc97a09c9da499fe9fad3445822b808b2e53635fedc39bc74f6bc 0ce94e85413137a099953537884093aeb3b9144ae93556a76e3c3bc5bf199636 9d06e50ec87f127efb1d6880772673b4bb3c2cb3eb55966b0daf67e9dfd446bd 7bc62b284a3865adcd1ddf07ccf5724049dc7bf4ee61a4c7dc79649c66043f98 1d227d0a973dc3de5cc915fa4a8bf37274e4756e645aa81668d33fc7961e0a2a e9336ccf08921864a3747041a439589c90be2ea9b76d68a07c20a2a7452973ca 40918051d4577557c67ba228218f5387f253194616f7ee4cf3c500774cc1e255 5ad2372438ea3a0df0eace2991e4b65938e71055fa534640f0b8e8cf7d3cbefd ee259251205800450353a29fc37db019110124b1a1567591dc81ef59e43abe96 598da5aef6f9afd7d9bda2b7e6174094f6f16541e20958b389f43c815004fef5 31c4b90e93449c6b3e21e36dc579eb9958ccf9322ee91e83fa246efa9842cff8 52949e838321273d8a7cd5d206070987fa4a9ffce5c0df9da849ca0dbe7eb1f3 ad14e4a666a8110e1a526f3f9cd0f893ecf93969322e8197c8ad71d86a7d6386 870e88631350b18cc9b53c4ed9e602dbcbbc9cbddd4ba54dab417ee68720cc1f 4be388cdf0ddbfd96364dae5772f9315892fee86297e6bc517b57e3d8b432bc4 7567d6de2aa43956faea680b155bd360e0de0897f983649fec7298a3f9a0aeb4 06c09f0424484e43d945b665c0c3820da4eb414564cd0d281fe22c20b38b4d33 de9099d964559b3b296ea992de026b45f356400b5c188aea416ab11ca132ce13 03dd0c7a65249f04a42b980a247efb7d745bd4743df9216bf4995a71c4d85f8c fbcb8a210b79daf1f1ef112d8d9f7c7bd4458cf8838bcfbe4c55e872ce689a4e a4463f51a8e39206b0973935bb0b568f59e88e69367c095d5a447c5b12599b59 a9d8f6de17e9af400e7199cc7dc98dd2c85f0b1fcfbc24fcac60895dc9640c0b 0f0c2e0dc92ce977186f14b3726d3e4a844aec7094617c6858f1aa3ff8cd59ae a0303adb10a8f3724b495911e2d25f4cee9a5731ef1545732a06e014f3ae0fd2 0b91f239bbc03b189b884ef281e75cf660371051f5b9f042f0ed1447842def78 ae6a9121474cc2ef9e3195b9bafffc441cc272311385877f6d314c5f1ffc8153 81a67dc84a2ab74c1faae38b120e3ae35cd4077e1671ac65d691ca1b1f5a494d cb9d60c438a839baca36372d1d957ed60691337b62f7f05f751e92d82c98c140 1bbd911b5bc46afb251b215e83007e6cd053a6dc0c5536851e9932d9a97a9c6f 92e5e35e04b06b15e532a87128f039eeb1f2ee477f3741870068d66902688cbc 9cd05a3ca4c1f7c64e8e3e89947847fe714d734b6ed46905542439f02830468c ceab488011b27385945cd44eede1239ca716fb75daeb068bd7f6b35a9b452fad 2414352d05ee0de0ca76df29caf3140ca8d086ebb08d79701f63ebc265e15c29 499b5f5cc0b62f34afa1c3f7102064b8dd12c5467d858f709561f4714fdaa67a 627fd1c46a4a424a2930240008135ec411e51d097f17a80d847b8784e5ea44064750e012c857b1cd6831cd891b9f05699abf51895c0fa6fddec9db73c3fd1d0a06163e544bd497f0c02682e572d3bd21dd513a3e1d254535c7ab428d06a7fb08cd6ec7f7e58017ce541f45c543c52a23b853c23cc86c7f5d04b52685f15cac034b985f79aa144fb4a36a4d21e59100fbeb59919d581a20914e23463f80628001b0fa678ad54630f68a54ad770675fa3dfe996a54a21ea83047bc986c3278cf0625e751e5f9e414b94814d20e92be241e2b78e8e9e5433e8f16c16d4017636206be3092bd7cd58efdebf1154b146bf2c4f6178aba5dbdb1b7d0852343d684f6071be7437bc21930a599f3983311e5d4b72bb2146ab2345817e828bbad1332ad02e36d0c95ab778548c4f41a1bb6ec64800d42af988de04356d01aa6067a7c550d78e57a8b09c13ce00044d36e1dee79552888566fdc447843a33fa7ff06b02003be1f0051ba3a95cbe5ccea426aaf9871d100a0e829c1fefd8056dade39620600e479c444611724bfa21a6c4da980ad4412d25117700f644412f8ccf006d0d30f14e009733126be10b0a88f4056ae127b25e3f23a559bf58dca6060873261300a1874c08de60470c09058223c5f0549174a886c9471a1f0d3f9699b8d7a86e00eb127bc9b062384d72a1c3fc3a5a6721c88c0db5225a9e989cd415a990c1ad10c599e04015d04ad6b071296d176e792dbd941e026b02b5514e20e5e300b3c7306f2e8efdea920c3f69bbe77cf1ff2f26af71bffabcbf3e28531a1fed4f736d7005e1e0f8ccd85f2cb041d60db6ab3f676fe311ae35bc401dd94e0ec81749e180443d3de29bf05269797fb07b137828c0eb9a583a20bf6f8079f85626e5e4daa059df8098988f409e02b0d48b223e63f6ba087b74875436ad15b97e801055db3039e68fe0dc161778bb48b8d08e15053d5fa0a16d74f07d061e729c17e6658880993f071960efdf624c5b6a308d20009bb61d61a7e9beaa1f28ea3514a31c5d2024d4efed721414feaae0671bdaed75d4aab8a4bad614f81ddffb257324768f40a94b2ee8680f450b5ae1ea838cfd8d203e179be9c5069aa3268cad75290f7670dd75c67694c74fe4e845b6021177368afbc968788842e4339cbd7e87d0600760c8c824ac0fb5e627f73199e0b62b1c934eb2210017aff0c91ec230dffbd458c089da55cd25d201436eece7545892d3cfbe41b9c1e6a296c49a5e7487b2c09510c308005fc3b6e23051a40f1faf35fc6cb8551708ea2e71b5a95dde619fa9aa40099636accec6e6234922a9c515c5032a94f645d00e600c4e60eaf075664a5bf0f8ba67d6cfc6b0396cc9872aec4d1fb9b9baed9fa3c98f7aaf3d0a6876fec8406c9dc38ea7ddcbaf761953af49a86cdce594c01947f0371aaf0957ad190969a02f1131217b3492b923a8b1da604bea8efd69cee2a9149e3391abbb460df6d0504dd8a15817162f2e7574b90836e45842041cbbf1a256eebc68bdf6cc952e36806bde7137cd9f64376bebcb0e72d5a79a86624805d0a03dbb755c4a6e1b9a1c808ba9f1dad4ec85319e056e47441e5d040445250fde977fd01510a48f95fa6d90d19c638d51487bad219cf79702739182db5dccd2be8f1b6caa3f9150daabccd0456695d9069fcd5d3daf4e40c26d9798f558a37c559b00b77c1fc33febae99201d8edd5409459c487f9d197172843d7733e7919fe872856c941ca832736da5b09b115fd6fc4399ca2223eb14403ab0573f3095c656c374e3c7fdc88420792960c0d5971b260f292441ac44d41bd14e621765b1e4f836bac34ec95a8b2f7f587074f7d82b0101c009bf1477c3a864d39d43291d31c72066d3ccf54745a59c39e01b9878b0fd5c058bc5e792596415658a531c4f4a5d57aade3e94ff0f029e7d70e40f3b6f80204ece4c9a457da02e4bebfd2d01ebe253c0f432ad33b52b9604d0c4fbf1b862e44472ce57a978beb758e399346ed9e99b9fd88b329eeeab78f8e09ad486d4921bc2b21123dd3b156b02894235d803e11b403edeb43b267984b7b005c1ab0f6b5541271dc1c4307075d4b4e55e03be158150b2b5b6351f11b0fe604bc043d83a2a389ee275030c0a04176b2134eae2cdb6def0971e68de8b49e24079ea12ef2cffbff1ed0a3e86eb36924bc59a10b008ad62690674ae5637dff7606b0b52083f3df8665bcb4bba2d6549f1f7c8d06ac151ba37b9ba407d3149caa0e5d8269214752009a2bc9dadf4c81d15da44bc1227775b3a1c4b703922effe0080bc67d40e78e5d47f902e246e48840fc7a3bce76803c0184158c658b12c35e0f3822f11e534c2d79e01c4064cd5b55ef9640ecab123d23f0ebfa8fac67e9a5027d0bb806820ef2ac1b7f4ce22f56f117c653b02f2af30bcd49d728e334c0a70b793d82c4e5b58c095738817db7ec0f56af48c4910a753d2a83426d7c563b4304c64bacf46665bdcd9448d7e9f5dfedda8b14534730da0268b3665d884cbaa801927532da879c5bfaaa6dd310b5a3d2d60191b0e676a89485a2cb37d8054837044be53150152b80d8e80b6da5b7664848101a8238db880927b17b30421e1df80adda0c675550e505dd799da5df603478db11fd78890191f014279ebea03a464062a0c553f9a4d790378b771681a3b5992611c56bd4608d537c00f79ef3a944e019616aaa416cb06ebd89ad1be6b205fd302127e3e7e7bf54d0156fb1b2f82960b56e463337b7583ae76d8a8c861b6e9b26fed6359f84bfae09614fcec0c6d4f07d70666b911f0e98d7836b8239a948f54468e11859e192a3fae343717641801076f2844324cc7972c348614738e3f798044dd75709ef0aca3a037f18da105210b0983287d355cbb84774453c9d16fa9bbeb8584967bb9b1ffba26bb78f99da5043d01687b1389f1f6ba95015bd919dc6e1a439b710464f6124483b3a91186cd05403c4c5d7e5e23cb6c2c88792ebca9e3825b22bbfbea1380b8c1fb635026cb06b4546790369686bd342b0b7a935c52f7138f94032a19dec9af5b8e9b0804db08978fa269f9940ca9a8eac3de8d2e4eda3a1535af1dbda776d330a9acc4e95404cd2ebf15677d57321d1786f1e03a6e8cbfd406f84e4187c4c87f43bb3337c20b535712dac1be470f4ae39da043309b415b3420a8ed89acdcee09790948074c0c1126ab2ee88e65d84e3353723fa485a4185c3c65e8e42eb5aa3249d3f381760fae71aff06e31fdcac1240f68f7f554fd279909a30a58b25062fe57492f9ec10e87d4edb13ff7a54cdd7cba72712c9f76dc208bcb94eb02a51d3c4a6b102195067e3d87d762de5875eeecc506857779cc55b243b39e9b6f448161fc6630a2530fe44d2c7122dcd59350be57afa7eb353da014b855e63e1c5acee9b063e9abff0913badfc3f65c35274031976a98e0009f4b8d079a8eee147574af49a8ae24490a045356e8759d7393fce862836fd9b55e68354f30e3c57a731caeb67ff5391d03faee57a57f7a151142b51162a8417fd8444af0b787a0e92d8eb1f7d7d94c03075cd846c88fe1d60eb6fc4b5ad9e8310369ad4229647093de3a45e28c6ae21d023b80ac5e7dfaec724de2b5ad063c3eb5bec37f00e13afa6f811655e8a1e483067b066f2b9a32f7859f7c839592cb540dd62a1b3f7482fd9358c4c77e55a0b104481287e743146be075d4a714747b57eb2a17495435a1e451af13b3123cffec0221f0a24bf894fb6b9b0cf23dbd92234267a7997903b1874eb2aa1f21bb8cc500ca68d09061dfeca7f7950f9bbb87f28c699706c489265e39a7708c980b6d7f0cfa522c40135a8a882530033858914092368215d2a1cc0192e9ec069106479f0b90358960c6fde78999bd0d10cb7b80aafd30b3e2507bad8736e2f0d1b42b60032b025b5213410dfdccf01d6a09e4e3d00fe319eb6ca8b612fcf413afc43667097145b482311c2ab57ab0fad0284298e728f9313d99ce8a4c374648b4e7fdb706b44af0471add08acb77924a1fa901a9563cadf3309629d1a848b9694a86fe008e5224e3a80feaba0376ba2ca84be576a024b1eed047b8d5f9328a4079f88240fb14a3e42048f001c145bad256cf0ef1a89b0353c157f2efddbec11ce721748033cd57fb6829af679435265cfd2660335407b57c3a7592bb8e58f86b21a979d09bc211e655b041f5e93806760432bc5607c3905beebfad9fc76e199541abb260809de1fef4417a21f1c00271052751893a563cb14b5938d64f0c57f7256b4f8006cd6b946b28064663e3500c6ac685c21854f9860bfdb1a07a1c496f34b06fb07bb3cd1c22b155071cd3c9424ae87a6390a29956e3fca0b70bb39fa4dad63230adb8fa9651b182ae53bf979199ff471a7c7e7cbbe67c0dcaf9fd5b1170dda650c4713638363363137ed244f1d507920984a44dde935e69a141b98aeb6137a7d0b1a134d768c3eb2e78746eae32d826414ea611207bec90b71c83ae49d9e153c0a5bd9f4d19301ae1e997bd19147505c4088f318de67230458a95f38918f9c11047e88281044e4edf32355b1dce9bf77f79a523f99187fbcbf363bf43bc3b53601bec3c969e0803af810552a83d5c7e7c13e2879d0d3e723b4d498c520ccabe70331ff920db36af544868182efb301e15906452c8dd9ca8274373a72c2b32e820dfbcb31606f297e4785789c7b120ce177e23f30d56aff77f8493ed3d792d3f5092b87c5808eee1ee7468a850d76c54bb0adb991f33dca8f43f6182b0ceb061e0ef83413a1d440b1fc17fb580f4a182f7d7097323600dde0cee72cdab23581970492746d95c221931e570e9eee0652ba3c8e29d5ace4f89f8e6cc8f1d52ec65c00010679452094bee55e373501b548b3f36a593828991d49f8e18eb9d5e16f0503a187050f17baaaa46fa0c09854bcae861484ecf4bf284a1d61dc99f8f486fc00f9be4171c6418cb9365c61602e50f5cf5e54dbba120ebd2b4324326432edf70304a1ef1c506ef47c2f4e2cd761c6869d6ff85370ede6fd5a0bcad175402c7100d02cea2bc4090fe0439558dd4beb45fc685d5900b12ef144904061f419dadc0e8f1e6381bc6a86661868bd54d97d0a37e71605c245ca5e12abf5047908f0c209b8c36c70435dded82ced520539a659ea5d705bbdb0194a439521a9b8cfd9a30ec8a62a87ad20a2e74954485b012d1bd5729b35fc835abeb6214fa3af5ff90f015a8169d3d82278ab240afc86736d538432d4fe5ddbe63f3252bc6517fe59300b1b0662b8fb130cd4e07ce8d65bbc3d77f4b3f66968df6152c4a0b297ea49d805f3477ca8de8ff2bd5077c2420ed6cbb6ea2f746a55eef348ebe0a95b9f97d4036fe4a768bcd3988454dfd889447dbdaf151e5ee4cad2099366404c475d2a3d0660d72327317d7b87587c2628a9561d016a93410441a76c45ce69874c1336000eb20bd9e281023cd83ae9e81574c8e40dac4c23938b984bde789ebee0c3e0560e7f9490a1c0ae48c876e87df4cd7b311b0d6a7635b5b7da34d471d8583620d70dbaa4a41e1f95ee9bf717f1ca88ad73b582c773f588b8154b47a4f08bd8d8b00c6ea170b6a4f59bf12cbb970cbe409b23614a0238c50b2d77ef5ff7ace4bbed0e12882ed1ae65dfd61032ed96e5bdbdd02baf5d143885f932a7a4ac35161da6055d1b75a94776fb63a6ee9002a5f4df8edad6322348d6f5f7f0a6d8a2e39efc0afc46da366dfdabe83c228b073aa17d840acc21bb5704d4b1f625d25a3b53c702540ba588c25e8d409ab70dcd22137076a72777befb5a55ed9d489c985f3be10618fefadba8bfe9b3875ad18b1ea4e84689ec92111153817560c621668723910f3009cf37813067014bf8541f14aa616911398a6636c37f2036894c3ac6ef450ee5ce9f22c97bb61036b2c61223b5fdd3e269cc91f56af1d70e1c00ff8715d802e1a1607aa85fae9eab8a624a0dfddd08689841538beee26a36b32a6b144baa00549bb9de72ea0f757f28a79fe2283c07d42c41d9ea53a89869aeb31147b8760da3199aa704a18a0c9146087ef95bad2d24484a810ef569d33732b80353bd480d6336b0c2c07801e0628e4629829616652dddf9de1a2b5d20c65444697e4ad5095d492098e32b401eb95ad16a1dbdf5e72928d44397113b89049494f627e79a0f76474f4c5e2929db6d0428595cbeccc339412bbccbd719cb91dee4daf8599e0016fa6d37eaf67a12ff04ea4aaef231667e25059394373d59beac440fac6e7900328e0bd723dad97d8fe706e6adb624962de7d83319af8ee1be021226d02b0a011f602e58788e0633584118f80ff91d1ece0944c26a98c556ae6d83b92ec1b80cedfa07c153bb9c201c69e3d7e2e70ea1e636dca2655a8675629ce349aae6280a2485a172f410cfe451122842b0625fbc1e3e61663896708e20c90e0a0fc7dc00ce0c9e565c5f31199714ac620d1ec0acb177887b956d5706e110a0cf63db77052181b6052351b7d8e932f08cb990d09663192479ce3391f44061e6ae303b470961fd5347a21d79004ef1acf4bf2774940078d61ea11d539267d8e3965ea16300c24c65d3845c39d7ff9d7e55ee05a98875a6676135d5dd4149a95877246f5d03adcaa05543cf4cb9e23f317c8e1ecb3bb3cf2d904c75db3a18b16ac5a35eea0e048cb8580ac3c96e275e6a4e76029c565d7a9dc88435ab60f4b63b4cd9cd62035e9777563e0905c904290a1050916298f0773ecee9748d70a2ede2c7fde6100c9b9d76be3cb8c9ff56e5785aeecaa24a1e81785bfce95c8e5fc3a72eb84385006bd5e7849c139af021f09673b63135d687c7f51ca1a52b4213a0e72cf22fba09 true -check_ring_signature b363adf84a09ed46f5f89aedb2a3bb2736a7b9655908452e96ed18b949808917 543c049192d1555ad5682cf8a8d3bc8b4f10f17a239c7fb2cf1c62624be2ca0a 50 a9728eb1965955588445226314fafdbd7595930517303b62561cd65330bb23f5 3a7432a9bca4bebb51c4dfd84d19d6c4bbb960b61121678ae6c1bcc2c8b8de06 2b17d4f11d86fafd11381e9ba93f08d15234738006df0dcbc1056fc3753ba6fd ae5daa8503a41060f2e290634e075aaf680ad9afa7e893dc63e3551346e5ba8b 1256389f1aef20bd0a7e058c6ff8d76d8a59cf8e2a3b9414ca4426fdd0238df2 b842dc5fbefa787d3716a717b34959f9a411e7f829b6dbba3fe2caaa394fb982 6ba2de19f2cc0dd4b6734dc3f130e46fc5e17fd7c15dd0e3970e65add4b2d740 eccfd2e5fb2e1c22ce6b3141d8332ebca586fdec03cf48ec4d9e80f57ecde26d 82d0783a72fd43bb96560edc73c63782203737d615446af0c14020809d20c78d 9fb69d9ef51ebe0e12711f19f65ba85fa4f35cb46a3f24ac0ac72d2b11d9b092 ed1e54c0507022dc454937d0c2217357807e39b19412f61e8e9bcdb985b5af96 59bd9cc06cbe74c97b2d29c56c50b57e76577b2ba8c66596e645d8e98c8cc08a acaedba8c72f06dfa40768538e21679c5a54ef05ddba35572fb13660734ef372 70aa7f26166c25655b0d10f570aa4031653f885adf7d5b1e38eb3c9dda50e2b9 d95c6900a394d4b3267afb73ea5fd7f1855065cfc32609cc87bcfbbd817ae5b9 91955c6e36164f944194be372fd01438deb00f2bd967cb90cb704390d6c34eec 4dfa7e942bf4d209533528ba176ca6b7d4bc0ab674f36987fe8b9699a50df8c1 7a5794dfda5a6734550427ecda9be6f4faf453926e3479d725fcf3bf8e2ab769 2e5efbd3eed3c140aed864a9fc34322129f005cfd61b4263961441b25b6f28bf ec44aa27132c059bfb6618333125bfe3bb63e3c7eaf6825df845f2315b6d3223 470aaf7edaafef96a2ee970a6bce2f3a366a95f1ee3258053ed70295d09f9b2e 27bd01db84a6d8c4e7bfb210cac3bc8cd33c1c3e85f9131b4e79bdf174829de5 70bddaa85e3181a1fe70ef5b27a0221a1d4b1df01279f2d0447f16bfe5b5cfc1 8e9ffd67c6b5fe42fb39d17613eeda72730c4ec750430568c5fa2459037ee266 b3a60e3148ee8a9c33b9c8201f3a943c8bd6e9cadfe05ac957215c256b8b0008 495b9939cf276b47d3786acba8f06a92c2b4cf390837710f23985714c40bbb9c c7ffefcff92c174a33c226546ce2dfca1872ee71f296b95b480225f3d03afe0a 66edb7b45b9e5848c99ee5ff88210db1928a55879beb1b961a90c04e9a79c222 568e7be366bd4906833015a8ef9c579dcb58a24b802f9e4d5a4de1de88f52471 1a3715e10b4babd3b3af54355708a565bbabdd14fccfe3526ce1f2178c456b7d 1593ba399e426894c9384948edf13a8f29c6c1564e947bcac4a15be12120abdf 845c83fdf69d5cbddf9d5f173bd5728222bb82f9c9d122bd275091294752cba6 18f3f851b26aaed482f6adb301c95678be99db2d5f3e6d512dd374a944996c7d 3f654f7ecfc8fe84b4a8266ec1b7d62c7d572b2ddfb0c424a74f6af2b0371bb6 cbe4b7b900414a870ccd8035af89c3b5cc98fc13b7eafffbe5768cd134d9e44f 37a083a2ce44882eaf7871c084d822e7fe446182b212495a48138e794540744b e0b4cae69a4d966add8ab9a15af6220071426866baae8087f89fc2d56c0b2549 1dd98c5d28113fa084cad7d8df9a9a8e1514a6d6eabd72f0d486ecc3c9351738 57321784392d76a1af1fa882a38977fe83d770f92766aaf7adbb84b75c85b7cf c17518930f8a6a417342d4beeb0100dd54ed7e7c0961c5a9c938753164017a23 cf8f8fc30b7cf61b5d46915f7f3a8741a3d55bce03a5f2a90757b1a1baa6b239 46d1714c6e9c5f2bc044a3306a48e4c52d2971cee768d94ffb97dbb5e264d6d9 d7d6c80cfaa7fd789aca7a9cf320287bf5519e7f5c183cebae2edd571835f80c f8e46926dc6f70fda49bb5fee5ebe9afb03c4a4ca1e4523460f43f6b8d814e27 b359df546107ee43507fc051a448a3c58fbf53aa4b4c94ad53f8376396ee9f76 310ed0a5a89bc845d24752e967970d019c8823c81c325ff7d74b718fc6b2d22e 1a937767b2d6357907a92b6261f75d427baba5aa3e0a3c23c05faa5b84f1309e a9d965d9edb2e1c55ba265e1e5ad541a47b678e76f7ddf26b52007107e5056c3 abcd3ebab7ccc77e4420079acae90c5b8e620a44a98f12f71e46c8bb0e284a72 c8005b2459e6767f1c290e68e88ab5daceefc47a6ce25ea996d035f9caf96823 9c8dae3a982dac57edc1c7949c4837aaef7a6a94d36e49a9b2e9290033e3cf0ac439db2f3a93825934e46b302407da618d632dba2e55c4d175ffb1eab573070a80c9c599b280bcfb0744deec407dc805bfcbc458e7c83359fa63640291b7bc07ea90a93d0960c0220f9ca1cde30b642799d01a59ef0157480a673d54f0b81004a5bc3bf84cc009530df02b2dea6a666b6aaf8c1fd128b48dd4dd2b0a2a8b0b0294b989ced88a7ac5bffa7bab9ab8dc953a6f979339c989b14e343c653691840e268f9ab5d9649edc0ed92d7103e828306086b1c3ca76de785ef6510931ed1f07348e486937405e8345ba46877cd047b4a8dcc257959ea1085bda275bc700c80346e39d72f0f692601cdf327726c833c673c85ac72aaf1a205760c4abd5af0aaa0ca9a8ed90e489fbd8ff4130396ea021a5e0c2a83e245474d14eea0c7f81b204e10361807aca1815d7cea5577e926e1282143171759283f609c4c67e93909a0b5ebbb80c6260897f42f58848b1b507280d7e11428810a6e743cf66af3fbb4b045138ff3aefde3213bd565f1bf1786abfd1af14ee0a386df32191735b720df40a248aa83598b16cb203ee070c80b37da0a3f68682548f27feccb47639e1b88e0f927978f6a62beb8b04e444a6039e910b425f8492e838a5b05408909ab77bfe0335d4db368b18916f79280116e3b1aff9f61c76f829c640a84fbd8ddbd006560fe582c969452753769a6caecafa9c37b075a3de24a563e7d69a3ddced4cdefb006d209b3b76f2b2bb7df6bb804acfc051e963f14c5c840e48d3c216eec7d6cd068d0b4c8ea06b423893d1b94aa1167b415ddfe6855054ff650ecc3b3b2ee72508512809a1d2fdb0677ca5661cf6ec50c7d166950c3b2fc53f8875874ec297d90a070fc265091d4f958637f8ffb177e7bc8a7084e945b5852adf9a98f26326e308abf8f0b5ad2f95e30cc0894f43eadb42f18ef5850cd76cb9f828401525df4d0540de190fccbb2161ed063b94a2527c1fa4f9c7289133c035225fb400be13050772e24ce4779cb0cf4a9d469fc6a8e2c7f6e5ef34718ef15993a4e939672578087a75975cfacf14eb69136989f53e95c1352a2cc81157236a3479939ad91accf50f656f80097967e3d4cd1df7a0f6f958fd8eeb9ee31aed911c5f79ac3667849d460c2e1aa9711ba149bc9e5f9805f34a7501dc8c2f313e76f043cbc7470bae0939516ba367387c65e10010f267a3668179b55fff6c6c24f8da3748210e62600388f9ce672881c79f8717718421326e4d6e29a5d2685c9b3b1f3d2bcef9d31601c8e0d6e81941e639226d5d1b60ddbe6a685500dcc5db64d05dcfae6fc18e550fbf6d01686bd9a5e9a6b58f17fa5b01f3400e989221f6ebcc1a5ff87845fb920ec40a2428e63eae5c2446a4786c1d073d142c38c7b2c4d67a49f57a3f70998206d3210719f3cbd2004ca46886373b8dcf7a2099052b45574651315af5ca0ab801200bf65ed58364968e61a7f67cb659c0b23f66f6a8ea3adf5bb91badb20f570023e41c9a432b114c797c0b3a22a50c3af95187994d1337b087cf3482610cf50d3742cbf37959732d52732abe3ef2c4119c52b9a423aae69f81aae7d183f3970c638a6db3fcb48556bd64c5c451b7e021c2fb10132301935d5b8ffc0a2c7ff90192376a3aa72d7d04d355e5bf9b5e3e44c17a327756e0683658c6141aec6d64055ecdbc62827b75aa96291d253c9cf93fe6a2f9b32e33ea19f7804bac9a98990595c25b22d847dbcca7a732a37b5db03e0964fab72459584a2198c4df91317b07b860f2090f922d02600562276a60a5e5071c16fb40a2e0c193b943cd3e021606a1941c087a1a17d4ac64862df2a9e412908e9ee67ee509908594f193cfcdec0dbe7acb896a27dfb3c1a048c24b44885fa6aad3835cf5c99c055fdd4faaff1708c3b9a2e2da103cd9957742f4f8e53f56f33159ef30aca9b7c468edbbeb2f440be30ef4c37f96c779618a5c435a866fa8616694f00d4cc7f8fb2c2769eee3b904fc808dfc6c2059b70b98390d836380ef7671911f5820c4306ce079208ccdf2062abb26f5a9f807510d4aaf7ff84dc80aa047efe346b1ab55f19d43b35ccc960a9ddbe3fc0f9f410ed35bde45bc49c33119bf37a720a55107acecd4f2c7f5f2049ab924e5ac8154b7228c58588eb5445756b9f12a53cb659a724c15e5977982009e5a8834841e0a24127e52b74d16e0c3443c82a213b16ec76126c0b02202600c793cb26d0351410cb8ae0c6615fcc8fc9d9b8a2effd83947d16c675b8a436406b85cf422bb79763373d1eac06d3b2dd5062790dd66d4e6aebb0d285ecfd4c10967bf9e1a5f852c1ba8dbb9ea1795968d78d15d36213bd3f80756799c669253085b150f54f11b55aa99730e7cd0ddd8435512e5a1b20ff93270b22b42a16d080cd8ff69cb69ffb36c25433b69254cef97c1dbe0ef7927a895680d1c3fd7913f079df7b062ec35d32ea2bc2a2555426a1ac828a246e49db870081e835272b2eb0b1446394e2399a1d03e32182b43e3008bfe969125fc1117d6afa4a5a65f1cab0962617487318a9d867ea7424a595f838685edf3e4869ce5fe003a2d8049479300ad35eceffd0a3ab4fb10767450451c451967d4e7ba58582cb31ee12026535f059ad3112273531dfb3691de3cf274b86c8038a3898ce85230a54a1375deaeb6059986fa0143453640239871e143d8b0b9a9630a3dbb3eb551ac0aef3eb213de0d66cf2851a3091c8f665bc4cfd3403df5a518fc5e0b61b960ebf567321eeecb0234e94d8a31b502b9af623bc3c144c69b648b4140ef174eb5a90034a3a678e8047a3114c35aed25fc0f03c6172b9b6c3e6fdcb878748f949d1e2540fb7a21950fd5af5eb762cffcf601bceda21553efd36c49badaee39ee174c342ba10d5cd807f2f943dd2b0ab0578a022b57226d2804e6aba1879c1cec3b178894ec74dbcf074c5dc11c2633d1a0dd6627344d5b401de76b2133e4a9bb29f5cc215f2557270e2048000c4f3c828110ec185e5da3d6c336c1c3c1ddf709f578ae0809ca2ee60b31adc4b13a6270222f4a87df8aae469239b8f39b34fbb3e410391ea73a09430cba9245738761a0f91abd43c09b0c86e743aaeaa3a4c37369638d73ac578151084185387806bdfd1725b18f5c9f398384212da6ca38ca737dc13903b708db7004ec032b4380f9abca712ada500ee3a4b9fe722005fd0c70bd9c983be792dbb30a087ead64690e800f0928c33517ff30142ea4fd2b23d277b53a6f565057e4e60cb29924781b5de887f7b4a666ab6659709e3b0656802c1c71dbe910b9f96cea01639e67d4577f545445e8ec78308d8d09da8feea42566adb4ad83749cd522db040523bb49ecfd766b6d71aa461a5352d9f85c0b26ddf81e66c5585fe98dfb4508428955141df57db62855212894197b69da2a7d7e4cd1fcbda2d49863b7eead0532518d372e5455ba116bb74a8306d120911de915718e7a5a703bd9f6e1821304e76edd8aaa4dc1e93c0d14af458d4c4716fc8f8f11f24de81c9a5ce9843e4b0732a09f0f16c13215001cf86ecccc984dde725726b8e2781cb7803768c1c4ee0edbd772f0a2f3528f3f53d16d54bd159591b0f50cb7333c7fbb1f73e9f5746803c1a035f06a72f036ac3cf8c996f7e9c85f15f2961ec23a31fd95983d71078308faf5cadbee04135a3fe38a5d0654664e87963115589f63cabb017d734a95450a1748cb757dc84416214aeef5b7ff0db2bc931b61304ab8df6d82c290072e03035e43ffcaea2bdccb170fd87f7daaedb303151100cb12fd2480b1fdbb80b83401569f2c3893c0bfb77078c1f767b663655996717f182bd2d48c65cc5c1af39778d362f516145ee9a01c6ec5404fb88225f5918d7c6bac23b6847ac52b6d0955029884eea14bdde12e7cf11f8dbb441cb135fd9da98ad072b3fb6903501c4b9501800b3f289fbbcdcd5bd54709e1f9214305fae13f76ded094496d495753a564018622c13ed95e42072c4f331d16693ba196b661319d40049337cd71bb056c12038752e921d617737313874bcc36355a776ca99be286ca86fa3ad30cf427a91e0fdceb93da8d6e8ac91f5196f8ba7f8cda4acc85fa4331eea82a29fc126619c20d638dae112db09b32a5e4eff1dd17e3e333f5b322681bdde0a1e890f6ed5688059dbe9007f5154c4550d16070641574779d16618194073d96b3049e31c4a4620d02ce6371f7f895cc2b31a65c1b4e72e7178e7ea05978b1fae8c70a1a0509060e5c7115cac53f88cf934cbfdc8efa275513669cc09b7da94415b0be44172dd1022fceb49c3c55d2807339efef01acc10e6ee16338502b7d8da22e07507231a4043d1d1b98502d1ad484794d972deaaae378e23e01721f3d338ff1cf49cec35a0d06778da9cd0ca0478c49917f635695202b777751d0266a2167244b177618ad02664deb8464ba64df95d6f7cef243c93fee19585645436aa982788ef66f279c08 false -check_ring_signature 3e076527ef0ab84a83851d8e234766a92f2dd499070c2318dbed207be5dc0573 bae8c0e791490012175a71ec066513849a2d670f1aff716ee2416937e5635c66 3 c2d0146763d8b75ea8fcd7088a8463d3949f57803d84f6a7c8ce530a9aff62e1 f0915d88a2e1aaf057c04d00f05854d3628a03ae15faf62465580408fb2708d0 b72b05dc5361cf06d6bcddc4bcac7b7c76f20d25d45f89def11a4914615a6ab6 91f2a1e72ea751963527a5c3ed4fcfa77073fddda3aaa90250dac6858f972e0d1f5c6f77880f09b55fafb756016d872d31f603ae904832c68821a4209995ad097491a2821c42490d1f05614ea5c4831da28cf4622ceda8a3f31044b039eb77145caf151e150b241846ca658d6e4a535379abf70ccb1976c6630623c87aa96e05003024d0e062f2124059ea708013ae4b17f5a2e00df45a45ed3a11a1907d6209cd519aff3b822b296edc780f6f90d3a75aa899ebf9d24de5843e6fafcaf9a202 false -check_ring_signature 6802c66f91a6cdf9422da4d64ae8693a2c05f73ca93b57e0eda74ec35572c03d 5b21d9c182fb33dc48b88987692728453972352ad659ab605bbb2928578abe19 1 2c27dbc63beb164ccffa9fa9f65ebaa78fa27a12139ddef555948af4177e4a1a 80697696d8e8ce11caf8fc54003ab618112b7ee1d37fe981bd4a77974cf0c624b4d073742901639dddca5a0186c7d28a086fa4aa9af1bb6f3bc342495d99cb05 false -check_ring_signature 2b3a4b1259dec4a2030c639fce817839dbb63266ef9adbfff0d0114057ad0961 524784b1bd8dc029637f58c3a52fa7864a91790a6b40515f1f0631185dd3ef1e 1 29c5b554f7e8971dda73ff863fbe21f48abf704e6bdaef097ee5dffeb8591ae7 01a96a0d4efa7f216e6ddb7979aa65404eb067f1bdd6aa780f794af3db662804ceed55345996b63c60717bc57f7231866220228e3d47eb9a8e05f377787ec16f false -check_ring_signature edabd596e76ab4f3ed255772da4924f1e8bdcc80b156890ce418c610297fa6a4 2365a9b1a96e64bfb53737bccebf694520ed2def59f70ab27a7b6eed594cf557 56 c13b6fcc773663602c5826107fa84fef70016259650bfa2ebcadec00ee27a552 7131ceb9c1605341146e51f2569cebdaa9e068ce6a6889c047c2df19c3504748 5464e4ca3936803e899e0af542a8eec7f2008925acbd19ba19140a64258dd640 1f59df8689f0343baee782b3c9da27cf341e855823bd6c8663d78fe1c2b65e1e e3e55e813f62ff06aa9faaf43022f582981f2f24d14fc919671fc78614c660c4 a3ab4112780cf9fb801e1b808f02e09bf9d4eed55c4816eb9b4dbbaa00833fc0 b074ab1ef7c756e5952fc2fecb7aa6572ef61ab922415724046ad2ae4e12c682 56bd2020e4cfb7fc2a46ee253bf1fc745506432e41eb44777974531dd36fed94 71421d4656ad4206e8d73757205ab6dbd93f9637ab7580bdc0178857690f8f77 862e01d0d874a2038fe192e1690dcb96f7b92a7126338ab98ec255ccbd2b0575 a30a36f63c8fd57091397c60a437c4df468fd4b512fc730c1a8cb0300da3da66 9f95ac73304cb2cf4a5d42f25fe0d178832827d3a948153e79a444d4bfcaf167 cb6577c16ee638fc2a01ceeca7f3439a1d4c847cac6ebba5db47bcd1827064cc f187e631f6973648f5aa0e6f60084323e36205ab4543bf614c02fde7ed4e6b6f 29cb5bad3068d255ed2d1c6175ea8106f579920bca151ec2c9d1425ccddac849 924d34a68d185cbd3f1ddfe3b26adc8d305bdc5f2769fc11816fab3a09e32775 cd80d97404bf93af32a43230b4a4c8a0eee073703629b20c8478a218c36613b3 5b6f12b6364d07a09063985c9c187213e8a2cc13706fdc5b8e83a5c6f07a25bc 0acf47d68ff65c483ae0c9c9222cd3dac7d6e390a77286ed6c5e56f4bc0918d9 0d75509349917df60b20ca3bd8d1dfbc364ea8643bbdf058c4b5f6b831cdab08 5c05ea355bf6dbb49e20f8dfb372962f998df5045fa05f248c6cfc5f7af123a1 3926b23d35c20038ff05310ee6ebf956f1c332922168869f10678e4022f72388 ade549b4bc312327e32757933fce2822b87631c688e815b766e21dfa495accaf 41b12033005b4a28cd15c42bd86af10f1143abb5d746ef5e6dcbb6146bd04891 6c83d7e2070eaff25fe88b364b7beb39baa1ce704be13a771924809d057b6fe7 87e769f8c02286c6530924e3da4e332f0a9eef2bc76fb1b421e751002e8b01c3 7acd62ddbeae2d96379e5d804862a427f508f03aa7b6bc247c6cab4e6296e622 9902d7f29422c2fc1a49e1ada7f39dff9e58e06b2d32f4d865e04c237bef6adb 96252c25ba5cc218c45372d75e98b988fc3f41c05f01fb43beec53a51a859f0b 99cf58906a788d69eb3b640d634117228c2a62d26e070fbaa25a72eadbfd6565 e3424844897221b67f8c5aa47c17799ca5251bc39aa7ccc6558027e2dfe64b01 c578f748fe72d4f0bf510ebf9a2b2362f5f5cd3f5585ddc6a4afb60e32ef96ca a8c64376202c9dfd1065202584d9ca6eb3bed1d702ec76ecc040f6e49994f9e6 24d066e038836d4f6b03678b86f44a8ed2588c744283484ca9fe0cb3da14feb2 12160074f6babdfae5c2719968144f5a3a2522fec0f557e8f7dc4cd1c3648047 cc8305cc149daad6c8e337f29cbcd78a25fdab1ccfb701febe19ad6e9cf37d67 690746c7452f55d7d285e5704b919c239ce042bfab85a2b6cb7deca607d4583b 01d24489cb6ed256347d0615a3fa3b407cf0c3587a82492827e1c061d00dd47d 0a23d52d412e8d7c2a1e9322a281c64b717499660174ffe94c2806b33b7672ac d6214de412b65376dae6eedbd818328f88a4e40747c54127a570b3b0215f6b0a 0ce938ef9025d5837c1f889d9a00209dd398e350604ea85acb36f77e4e3b53ec a6ba28ff734329d80373a718b23e3ba4755487c90c84e8c8791ee966e73eb9bd 05460df347929d4fdb8f3731c7e7c16fb07fee63b43c9e1c3520afc26a04168d 3840820c7ca3f68c111a10ada29fee864e4483dd771c411f38869ab2a71fb009 dd6c66bde28392165a76eb5c7f8e5cef71dea3cb57da8ee9714030a936651f26 a7791946fe06508fe6351e7d28cf0ba29e48dd80652b7451deccfc49618b6b2c 0b062c430ec7686336ae9285f1f8b4313ba7d8b651777bb84445eeffe5cf50bf cc30f77905dabc5c227d106c1b73c6518bfef20b771db2fd3840c8ba5db388a7 a6196c7e0c3c0da8d9ce5369b5a40cf0c8f26b5e04c531a50d6a5ed0cdeda821 7824f83701971f5d204102c9906c8e7172389e590a91e0b7d2cdc6faa48b43f7 0664aba9bef355c88d671e3b47cc12edf14910fe7ccc7c9f9bd1c7e7012257a2 57372ae9ff3a1c07daddeb0747696ded9dc7f10402bee3e11811c546744b0c82 c8422646a6d4db04d4422ef4654accbfa40c228506bd92475f9e6e3691fbe6fe 5617f15d5d0e5ea85bcf5720ee53a2070c88b4b7e2b497cf487c6a6811425312 e30128c9cfafb459a7fb2f117cbd82435a21195895087f9326879fa74fec56f8 40f5d81b17936ae4d9563c14d7ce7031db26d3460ca7afdf6bc2656efe2d5970 cf5bdf527c8f818a2d1c2a6ec280be421ccdd89a99054693b082e6ce0a485406d42dc20f333fd9962481c6359254cf504722f4ea1e4ec2f1c2cb8a7416afb906f306d0ccbc94a87d54d02bbbc800e508cf6cbc7ac270a149f1a22839037dc10f0ef5036c83da894028f45394d726e4f8f31ba5327ec96425ce677e5c4751af0649faedcfb38312b7bedb7597059d12a5600e34a779a385304da4e53fed67e80f96d4f73a954279353a5ed91a56457be966eaaaeab03ab8ea23e495f70d8a100e89fc1fa9369c4bd85a8251e936205f09de0e7e8c7dd3cd1fd47d025fd220c20ee8202862f717e2c5302972bb4a416bef73224e73bd719acaa5aba5eb83bc550f8e9f9aff2834d8bfd2195cdcf87b9b55fde98c36a147e646ed93c01e9c4c2603d327fb0c1c1903772c2c2ec3a12b5bda80b887023370215edb402270fb83cb0dfc2fd0a166327022fd90e615c0affd0e94c1c522d9b4862a1d0f5e73531f28018c6b9a941cddf9cee3926913277b04b428542136b873c41fd545f2b53b2d29074a9528d3deeecf5abd9bc94e29635946dd8d1c9dc5f11cc53820291e114aa50dc883e657329894b8686f2dcbdeb7f1f7faa863e2eef551022f6111ca4249f60c9c134e58c4f679b4aa3a94710e675351a5a3d032c0397d1bb7b3892eea7e7f05fc46b62f5deb4dbd20ef8d5b86463431b90ea08283780286618b7f65a66fbe07f3e19a28a5810b2fbabf378a1e942b1d8aee210eff15ec6ac41a1287c34db30736eeb5f1d79078eb17aa52c753466079cca3c47d7572a9e676681529ce6d7009fae74bc8706e333374bbe8ff5c874338c6efd57d1727a5d8bcf6df2875f4010fc636bb838dceca5e5e6f0021bb4f2c0d5b5a7de863d2a3a400d9bac3d359ee00bcb115794fb3d91a9cdb3b37ff6c3566198a84538f483ccfe238194d7cdf9b02e9ab47d04477728452500a6e8251f77271512be12aec33fdc17db62fd11a7604ed451484ac68c9ae81a153fde95f0da027deb6f8cfea552c6b4d9a6e9daf8c0bb2c33a934701f83d873986befd9cd5353789a231c0ffa473c537d7139f397701a3c36346c91efe89860d26769d57cd0af113efaf53661f7684e3045e8cdb2a0c092882be732e07b439cac9eeecfbf686e92ba143fc213f5dccdb73c8f7908c06d7781b525b7d14e285f8fafeecc5f7e05f48824dda67a5acde9b2c48e48d5b0d50a64e94c172edfc0de112eec558cbeb6fd0811476d5435fb697775dc60a89077d144e3964e56698d726ab1b8edd123c201d17534a2460197c28de887860ab0efbd067ea4a6fc808e7a510a600f290a0899597edfe5ce3d124b1637a1f9d670ae171650961d6a5287df5ed4c4dc10da37d2d9620e035e3db441f58b99473900a89c7f9280cba71195acc757f3970fed3f05d25b74e4647cf64b864150a223f01844c2e559746da1897b3fd133e38018d3d1c6599103e243ddfcf996d9d443b00453a37fcaf691cc69085795835ccff0c48e6fd908b932e99300072b1c36cc500295753099e280e66e6a4e08a364727a679161933e2e2910ad11658e470cad301b11cda1ddf347293a7605ffe64892129a7b4f234c794e0872d0596c043948200eef67404ed8542829812dfc7288dcea5e791bd32cdf23747c5c540f2b6c7ca01e78357b2e3e9de292c5026ec6e066136771740507bbc8f748d3010f5b39fde01aad41afc9a66d4daca75c275f29e2a644990edf8a39c79d45c1a6d15e026bb0cdb82bced16e030457818a4029256ffeaa96c702d33727ea31c0cb1a87d0ce5035b122f2da971f20402a43c93a27d884f201d1641b8563ab861f5325ae7d87b094bd7ce518ff4fe118e9aa88d40678055ea14c50d113bb90a3a45cdbd838d7d092ba86ebcf27193c40bb4f1575763364537108c2fc6ef8cd534573c91e1b8ae0716b4d76c7cc13c8f6031114302332411e4d8d4eb78ea706eaf6d5e8049e02b077204f7f19ef2108f80c9ba8eca0cbb125565b360415ca3de9fee8b4cfe0c7e0848447fc582ab8817ea5abe2022c5c33be22443311fda31e9929b701778f9de05c557c52f11536aa352cc0e1638fdb4248571e05e2c24c6388be010ad300901002bacf9734cf098b08a2573297b6fb72cfadd073cc5953be9c933abc7d6972f004063121564f23f77bb664e64626a15e9fcfedee42335336e09b8d16f1f742e0539f26c26f5f0f48cb0f66a5149f1a92139487ce3911b6c7cccff6fc6caeec60460fb97186a66c520124c2d743b82ed69616d3f126f270c1d6dda397aba45d80a6d454eb26e0485a70da341e9c29c89ad97e5a0c9481c1d390aeaada347b6ee0eb1e6e81a0d18c129d3ac27dd283373b1f21b2b1514d34db39a421dc2e35114025b1af9e8f2003bf4abd17a153dea40842f4c4d42f73772cc2eece84e8eaede0471601490055675eb6551a7aa96796e51f5aa4bb70d481c20da6f4d9424fcba0435960c4e865456715c90fc69ec421856050b3c511a1cbb9879802f2d4167f40fd9e589778c9f86d658c2993ac88d1cf2e161416f81129e540dbd449e291b760cc189ba00588fa362f986860b3a8d28bf4e7a6f54c43672e660dfb52a7dfddc0b59994c2aa3c6c179c6f5af7d9e4a738e46e39b3b8a5bdb5222c95267a1e13e0e4825233b5097af9b26f5d4fa62eaec9d36491d2922cfa64225cb30fa88938905c63580fb6e89bc677cd9f8e983cc0b8adaabeb00cd1bb11b0d090b46de664f020984ce6ed220670b8601208c26e5da400174f2ba11fea683304d014739ebeb0d614c257d8a5cfa8961591d8181703ea67efb5d3ff3c3a7f19e7003a9174671093198b9553915ba041854e5582a1163df484f9b56fe6841c5069884da7e2fa90e36d603225415ab4e95e06089bc9074a0327b73e981c0f7e25ef484061f3fec05e34326c5ca8ae4604260c3e209e5f2966dd56a52ae5abf47dae4a9cb005f0d0414a6355343b5987edd767d594697b88b8941b9f17f8a9c9328cd01bfcd5cb806fc7e764b6dd78e53d8d08a1aea4ae81f7d870c0a5c359d278920539427d1470ea7a719631dc9233d1299d4709f3f6b10770c547ba407789140d893d2f754ff0b0a4519be4c06b9a47391f93f350c9cdee1334d5af41be68a7b2d5e02db0bb1064eac7d45335e6019160f3418aa464d33299c3e4ed30117f75ede9da7cc060b02e18f5bb06d0df653d0ab4ae16678ba7f1dd42a91cb1cc45918eb3781089e2708ac8a72b9abd875d5cbb9f31bcf8932f42648e8bdd0aa33f5b3fdb449aa03c5052efce274c5859e5468a79c8d8ad2166456f8872929ff70c52ac7bd48dd8ecf072ac9da93e49de90c856409b8292f9baa85d2c30826e83c993a46972b857db80a6955490aaf63419d1cd78cadc6e63fc296052dcfa809a6bd052ba004d19027034005b799d93b6fdacd715ad509135f5957a9d8353287d456822169c194e5fc09c2acc5ef624e40c185bb6be8dbc917dfd932834d09dd774025ba2898de4d670d5110b3b0166b587e259500e44b93d1ef7039f5b4c705ab255253ea6d5d314d0fc9069bcf10a5c927e3d3e9638468ba1cd771049a70cd240b8d092650fb997003f31550f6c9d03d9d6052d54c5adb6f9aa78734dfd49771e95284e294aa62250186e1c6a65dafe32914a7b386ea0dd049264ae424979c265c3ae6b5b80b458d0227f8aaf3673697b28c88e6be7c55f9ee7ceb21605c11c04b0a5e89c31ffabe01af7f58c2a50b691b960d448cb03645dbcb7d55841dcb9b62eee2946e1d99b30ea2ee10536cdd6a2b0c8360007cab99bee938eaef1cf2dab3122a9571c31f6c0bb270d8ff1c0962a1e88317762c7000b48faed374c715a3befade31dfdec8f00f0260ea19c64d710baa05e51e505584534b2a26dec96cc1e29c3cdb6826ba00071530d1e8120755f5cb72b53a1939800bea89f6b59cf55d251aedbbc82ca2440739f6e652b0301d4e145a7f87d045655c29c18355e4bfd5d0283ee74d908d110a335909e649ca68706a0caf0350062b3e6cd38f1a0d3ac9dda42568cafa351a0d3dc476cbe18450663aee4e2a53c1fdfd36c71b2400773bd50c74bac0f8ec980b09d4e0f966e082f21351ddaf5b36d003e51e279b60bb569dcc119a875502a208d44fc472b86ad4ecc94fb4ca44cf371fd10ed110d435d75cc5bc8afd0a33d201530a83bc21c66fcedb5778b42e2fc38a4285c66854ca2c366d44dcaa68c5e407a545124dfa3948440980eb453e50b6efccfa802705c07f21d13b132cd6e6f400a9b1040ec44bfaf71e22bf65ddd7845829de5dd66143b875d5539e1389dbe10e48c18d9f2976722f4211c816f033b1b0ea760091e180082098aa3701fda89a094db890226719bb9795905362e16690e64ab918e7b79554cd3f9ad0232492720c8e4929e128d994ce5e382c0657db58059d523bfb1e0cb05728ec595493ab3d0e40f4c6009efedf26e4bf61fd3108f64f9c50cb9ffeffc9793b330eaee8e65002a119387a3e010b8957df2e35800760dd7bd3f19eff2a6abd56faafc37a4a3f0acba2e74aedcac852bd9c3b7fdce0244cc8fe949e724a3643b83e9bc27365570b454b074a7ad07bf1513e8139b29d488f0e8bb0329b1c5c73e05b5ba3f7d1c0048d3b3d3be251d8b38dce1f06ed67592b0c3700afbe0ff70386888bed63912d0d3c04ea63e332865e28d5fa06745a8f2fdb877b36db3f567d29c5e2aaac83d00835bbb204a7ed7d084bdea9869aa550c614b795aebf3bce3e1d3251168530c6041d2135504f73fcb6a0fe8fd38d4059485ab5a931238bec653cb86c099d2313014219d895aa7d76be7173c934313695a0d8cf93ae1cb15070584af4d601be3000fa9bdbf15532986fec5db345c1e07f91b5f2d63d8f938072c287181b9045e700ed28f5ed5b948cc50c245358f9ee8611a56eaaaf68e5407c99a48cf2df83e3057cbc5c40c5d13887742e44ca9ff1bb3915b40056a13762d06005a3ab8e12660903d755dd1ef635a9f0dd50b904f082a5f44b9ddeb615f5a109638a1552557e09 false -check_ring_signature f83f196da7286b04a03b6b1cb6c915d71bcf39de403eac81d12631848466e3fa f61e71903f65a963961124a3b9197f8cf8884516708196baf94d7628891d098d 14 073749a3b1ac7c4137ac60e0c94886bf201f44ffdf2061adeab8d146e000c64b d3d4ef48899ca1d3e0e3ca44f2aee80dd1df1d51a625214b19f03b43170f464c db9425b8538ac9b6e7ba3f3ede66c288d2e2cc72e62213423606e8932bbd71ca 51274f5223a3d711c11955435ab7e705ab6ef918b28841b35fa8f42e3d9a406a 0b95fa2651eebdcb58f950530798b5ed0697fb6f056366d66774055d1efe5ecf e01d707e9c50b287886eeb7e44e84718a6206d899f1484d74b56a203ce3b92f5 a5f7ab8e6aa7316b1e6d3fe96f72f1b31c78b66c95470f3f3bad05635dc4885a 951b81c6180423e751c12bb0d427fe5d9522d8c3ccab86399cde6ba9955dac36 607fba33b0ec458e342d48c512b47c2063bf098c43e351afa4f640c25fd24c6f 6c5c8eee093dc6d81d7890d452e6f4b622c3308b27da4b0f555f7f6159175184 5be10c59d846a5cf6edb31075b0013766446fbcb0d21ad4f75ad50f1f3728f7a d02aa95870189c94de6dcf5f19cfacc961dd536326b0749dbfced2896ec56465 8d2de4a7cc54212463edf06b28f0b8bfd6f5406c4eb674a427e15e6136ee4aef 15c953aab3ec61663937388386db1a8426e0738453b448f28e10746ca6ee9f17 90740432e39283632c0bc099f6bf3dcc88fc6b1c5966bf095c2de2b9657ef40d02959ebdadc033954679a38b92d66e149f0c18f1217b383c65f96995fd9bbd086d5006e915ce6cfff0c96cc13f44d57ca05a5b48b69e1b1c37d466d66f390e07c1f7db989efed65c43c04ac83bfaa131a0df810404068224e613c636cb0582c8d22d9ab2a010f8ec53d1465a32bfa119d7442fb5504ee087f43161b3c51bb303e9f58808a3b45ed6fba50ac803955ff30beadb5f90cd90c8889c34c9ccec28020cb84f52dfda5c973dd68972b557fa4d771125c5624eb704226b34fec1a84209b243bcc5df7991fb12296ed90058ce0ee9e2017fdd39fc2045a61573f157340acc1da0c24b264862d262b7fd771333acea08bcae82ae62ff1089861c996959051563d00a01f45f986428ec460588a44e07512f3afae79d6bae0a146759224e0338d2a1b722eaf89198d54b8bcee8508acaed8abe41cbd0ed10f325964548e90342858454f36bb15bf5a7c5533be874aef6868392d2660bf4e4587f1463fafa096320c326204fb98e13c8ed91aa5edc44ccf7992a7d51e06816a54ae0bf5078070136d838c62d5668ef896afc914848c0db3022c4e208ecc607c3c0ad1c28e0049f94803c8d3471f7d02b593360f53ac9362bfe1edd0f5cc6de41dddcbd0cb2093fb6c846215b9fbf5636a883fe1d977268be66393f3a265e5eb09a2e9341310a3cb4172748ee880e70ddadf42c3eefdd97a4262b5d57e3f7b1471257c4eb820f6faa2585a7b129808bb6df0ed2fcd98ef2cc296930e576937402bd085a9a8c018517d8ad8b3fcc86c9660e398534264c5ec619c8acb5893b55fc8c11c54371039b3df0afeca0131f124fb651e8949630ea2aeae1ef009c52acfea46c59948c07b5b088d5b462c4d81efd8327813680dad5f2bdff7b469934254b5f38395beb0c1df3682db05052a3c5b580f7f60e1e90223da0fcb1b3fcb73bdf247df47b0809b704da815a422ffafacaf42a0af56789b33be202ae0cbbc2891a94a5b12d7c05753b22750402154b583ab938313a5bfe417339885d284d54c1c1fb4ec3c1d60de714eb05062c8fd10baa17ae1cebf53a1dc194f9986a258444c3b746c6beb508e509e412f27a386ce098a0b844a943c9328b62e25e505765392fc713bad5930f94038fe6f4fb23dfdeb44ba09dbdf20fdbc466c3cae84da6a10557b85b04970122fa028bd9313dbb55dc3fb20ff9133bfee90fc52f363cdd32542ec1e4d53807 false -check_ring_signature de043e91fe973f74bc8ff5925ceea0c0577b2a70df3ba02fe88776cf64e53897 75d5b87ffb4b437bd9a693964b3dd41bcac6d97dd38993943af6e2bc9a9fae76 36 065c08f1cbea34b9f3ee53adf5dbef73333693d3f929a4b8b6b48526d4460535 7f0312cb8ee68eabc5c8b8fef5671dede23e20258e22458abfb0abe7a236e419 5a1b37366c7886d33cce99fa40c5298035453e082f9417fac7062da93f63207a a2e84aa4487ccd7e546ca9c9cba3d9fde836ee46f6233cea824ac2146d343b7d b89d7e3c8c6ffda89bb51269178aaccbc1e43867050793ac876c588de4fd5381 ad74b98047edf46262bd5372c2e10e7679a730a716e9fc25e5a971ac0dc0b1fb c6d5f401001d1be8c7960bf4622a5aa234cf9f47767ba49a886eabffd0564963 90a2aff61c86cc186ce4cff7da50e2d830cc47bdd41b7d99d92543cc33069e0f 166b5fabcf0b4aae6742edafb62e28113f52ed332fd28bf2462ad15be56a3e04 78f6a0b564117e48bcf1233fd947701f3a3a572fd5598a066986fe3726b9d187 e032e312a483068d1f6b1175b4d38f7a5b1d2b4c32eeb0eba8b34d5d6dd2844d 392bb5ed1df4a5bd4b30a22fb610de3122091b92b6bcd038cae6ddfc5e057caf cc3409a788d002cc81768f484db3a35c65e308721c537b7e9f2fd61299329169 1b8822721ca89e98f207ac8d44c65621f1f16fc1835be1c41b04ffc84417f472 7ea9bf66085566c3ad3d9ad11a69038f6327aa8ab7ee7cb04f3f342df3c1571f 32175592d803d0d85adf5ad2e7c5f78640c9830332457fecc373bff9435dd4e0 cc1e7286014b4bdbb7d78d4294b35ec2a5b95139cf3c90ee16ca0db6bcf92c33 02a68c9856a5639ede36104563bee4f4cde39556683c1a9fa48dd226001460e9 238815fae7fd2784d86bbaf22eed64e646b9c97dc186fb73ae1ffe4a9d615e23 be8b0251b5d5757017e94d65fdc8cfca9b9da38b9f6d9874ac998577575fe1b7 3bfbc1103a590e6a8ebe0b1ff20848eb54481d74ece08862f600e9c0d679caf4 44f6b81936291821096154be137fd0a6ede31beed997f515306a05e75babc58c 3a5102b80d0959599bfb4f4cc0e1422fdfa9af9eec3f0e7d6fd1465a03732186 7eb52f01a53cb396888ef78db3434f1612404bf5eca183896d6e007b58986ab7 239c827cb59cbe2b1e5f0822014f3ebf6b8fef692cefa29531b7c1586925c268 997d6491ec6cb6414019ada6bffe8256ea3a3607281af33c8d0e558864ebf1f9 c9264c4ecdbfb7391839e88475e335034aaea55cebe3d93645a8ec1755919401 b9586d01119d99a3576aef9160d38a02cdca79def23497555c0eae4c3a9983d5 8452334ac34a569e15872c3dcd7569caa4976e6fdb049b4771bd8aa9ae0247fb 825817cf153dd81423786c57bbaea0c569556fb547932694c1f19d614a708b98 6c86a24a68975e3b45aeca9851c941ba5077564f7c95d6468f86f52fbe5735cd 53037b41a379cbb3235321b80ddd9fdb0ab3a496303f1907b37346da068898c1 6e3bd2dcbe0a507f7df58d4bdd27e7ffb8d6828dc50c781c6b9de90ef40cc86c 1ef029766fd06fdc3f757ca57f1c0959059ff284004834d75871f5b307d5f559 e9682f032c46d443761796419aaac7b6b4ea05142c930cfec422533391e6f8db 87e1b69699f6e940dd5f5cd67b4213f16c9a272ba38c5c867f39349a7ae967c7 37a6330d9eb8af5cd876a84834475eb30878c7055b215a56eba591318f1b420038a3a523a586012fa5d238f1a1e2b70e791375a8e7526a081faab4c9fea5290ecac9ae82e15bc21a5c18930caa73882073b43a6232d25677bff87791c481750516e7ddd18d285a1e9f6ce1f247c82aa74d7ec2d235b36e52158a069b2021c805149110745585cea30f09844b5e2d2d17d6b14390a2dd079132d22fb6c00ea10f9a96359b2a8b302f92be55a106011057ca85b86c442dd11de7ed40580f390c04c8958c11eb06402863cfffa63d60d37e71ba32bccb9334b92c18d7748bb928051bc19a100e0a6531dee36a6557fe213bd777dba4f9a942bd06bc49b95f2b71057e8fb7ec49c41b3198056fd70f399994a28e1c47c96383375d00d18f9f06a60ad4eb184a4d375e5d7dc1697381aa8618412d624e9dd710aab317dd2d9ae46609814b44c091ea7b9c04cf2effb038e45d906d657f72f665215d25bea932d69504c62aa91e107135a4831f3ff6fb68731ce4e406d9a7dcc8f7500f5584eed367043f3e40639f0b6a5c0e991e2bc99d83c404b2c4b3a581da0e96fc5b7cc1b98209abedf5cbabd29357463e20f9cfce3baee36890cc3c9ddb54f681227cf6adcb08421ee92fe162fc73ab5bf1a157bb0b86a0a2b500ae04cb9498cc4da72d75f701bba31e6caa1b275a031fbadca6bb7e7dab92ff7c92a2f9c4f8504fff3a9e0b0ff2f4386f5a2e12bed6f245bb1fa655a6a6f07730d4314b68c76350b9e0388d023d08a928d85c7179f38293aee6247d7f7c88e7d3b576a13fc5e3c6871c0b9206d819ea73cf9e43fd4197090b269da676ca5d1bbfb6cde0f1ab700491c1ecec0d7151513c786b459707b78369d83877ed216524b23154388246fdc3da2839de07cb3a20cf0c14658d8d433b839c48804d39ff1c4a24e2880c00832ba79152910e56101908a90c1fe6d26c09dd8c25baa84555c10f888d37cb7c2307ac54cffd077ea6a2db6331de61a6211831e6db4df7df9917e75b3e4af8e5f2ae3aa214f71266be085af99cab315fe1a4e8352df428a7f2ddfb862474a8d33a46794a2c91084c8ac07117e2b7148c660353831a604d0d3b95f762f750235b5afc59267ac80c8d9f9cc7ca42a5eb5b34bd550a51596e399a2cf75810b5f9913d5fdd3631b50cdb0a1891f3a47e61ac0ec94cbb148b98a87c52bc8b8fb5193831f931fab77b0c0b5050f7c52220ec6cd129bc820aad86e17faed7c2e597617e714e6f4e05fb08e38e23953c0e5c0d46567e4e228bb4d15e4cd24dba167dea181e5279413fad0d119f483c2ae0ff8ee508a9a956520639466c1d91cfebd92974d1cb2c4133b703c1104dcc8044f3e6e7813bef60758c60c8109949998dbaad5f63e6d7e48e890557d7b05d1d129dd3b3bb9886d92c145b79ca25df62ed6be08893f5825c75890eb26738140d134877e161a189fc345211ef60446a436eec245e317b3ee41a8405becd5765758b7758061415961531c4fd266d4d9c39b116fc473fe336ee48f70a2ebd82699f061c3741d499341ea2a5f5b4952cce3175a0d596af68aed4a5d604676073836c799fc8f6b27325f3e0b440669afd5276db7e8f514ed30e578689048eb238a4226af90bf8b27aa5bef6396b137599e3c54ae38bc08f9435968187039bc5ad07ed4e118b6202dd9c2a9ca70d3fcc4028530d7579d74b4ee00ba9ae07782eab79c33e674a8b88d721e8626e6da5e602c99d8478b87095cf93da11f805c889e7e28d1e9f8dc1471bc3d553bb98cf3c1aa707cfd2fc6801bef129b53c0136854cab09dc6087dff3b0ffd76e05f1a57a72cc373d5a0e2af0041f7831280cdbffa37d70ee51fc2f64f90f1c1512ef5f9baf9a6b809d9efe62075a0cce0e0f802b3c6ceefd2384dd514c968c4ab3891b0ef8f980e77ad10bbbe92bf87176091f1c9cc3513f5c36954e76ee88833d35b232d7b526be7bc97744333f5c0fec082df6b57eeca6a468e8545bc4b1e5f965e6cf011e365bb7781d91453ed75ea80db9dc022771a48637262242cd9af8a73eb232cbf8a68d465f5d6e3a12c9be5b0d359f684ba42fabd5ec4e9824f81ab981a778df9cf240746c56591f8c6e29f10bc2f69f698272100cd82ffbf548cd62e96a2bf4cb4b6801dacd4d0316ae2f970dfed5fe37a38b6d3bae14a1a328b5fbaba7ef46b71e5bde851ed49d2231219b0e1ec2c994921c3ca5e001e58b98473cbd5c6f580c8a94a6de5f0ab60f4a419d081ac5c2f24dce50e30e57acc41bb93846ffb1b1737c1b7592eeaa8022bb0f300a6ed95cdcb9279054973d2be3e824aa0869ce7c61f4bfb77ed04e56fac0466100e4e623f05b32f688314ebd24629ba193660e7bce3601355ab578517c9b3c5107494c4edde0b16cc12e8aa688b04661d218ca5fedffb0df852facfddcbec08006cf39ff417e7ac40d4e85ea2845cbf57fca9f9b961ec104090a3d32ebca512e036e5c8b25a472490effd7b0f1dd0c1496cc9c022eb00fe411ba50484e5d281202c590e40a0884f9e6df9c54ef572cabbe339d40995691857b5a8a45b850b1dc0102654975d66fa4b9e98a00c24d9c29dff0159f438a5bd10febe5d408cb97360504c787869cd2f5d577f525b2a7a0ad919b2a5f3ed0cf5d025834de6e443c040051abe23c37147d095e014b10c2daa4870b1c1c3028f5da3ea11a204cb5583506634896215bda90e0d0ebb9b67399f2248ce8ed76fc0e72fadd3cecfd8e793705d5a0ef53b074c9cd2996ff4205a1bce9256fa8e0b6fd96777ba1ef95f5568e009141e39e29dced65bc5c35dd05401ab314e80d745b6a1a2d7fc9b2192cb99709d8ef1725498b8791ad640b103fc27b2acb30cf45cdf1b8ce9965aaffbfb02c0e9079b60e47b9a8320bb11ee324e1c6843d46fc27de4eb86dc121e9a1beacc90a2f3a02fad01b913f6f2ab2b0fa5eaea7982d280234a0fda0692eb6cde44e0406913efd9c78959a38316609b859f0c2ce5ca26b7033efaa0e63e8dd6c1e19ca044070118af68445696b3bc0b4ad2d5a99f70f7a33438459c62bbe7d99b83afc04491bb614cdf10a0f92cd42231098812294b44288443b2b4bee0929d67254630cb79c248e64359252fa7c16fd8f70fadfd427e4e23e4c265e295ada041866e106be54eddeb78556d5683d70ef8bbd9d1a1ffd6e39796bd5ff6e9d1bab5dcce908a5a2971de3b979b930fcd1263dfcc02f01f276dfa5057267f7427f3d29a6e80d false -check_ring_signature 974964dc7310e6f6e66c32e4bcc4daf5a6fc016d7eeedf2ec7579212bbd233c1 3eab301e1c611e8971a7a2d5b2eb5c26cda7935425646fb8e09e14ab3d2b4fbc 109 0ff37c6ea61d4e8dde69939cc9078ac8827ced8935fce3f67cbf1bf3d955c10b 6c164f38e29ce6a9115eafbee567c62b9e037d87ae6ece35254c845d73209d6c ec5aa434a1f063ff68435c98d01e02f0299e643be460f4ce4d401d61bea7ae9b 390c71a7f6b0d1e4af1c843b4930d4605abc12770312227628576adcc6093635 dd575561132b995763e2f765f3bb9894096f1a9e3ffdba801e10ff18e8ae83db 2c3ce4dd03e0284e43555cff9ea67a6f1667945fdedc7afc660981f9a0f36d69 94474f53b6c989e005b95eb949976668500f45b85a9bfd9469a62d0ef57ff7ce ebdf4242fc448ceb4ea83c5eb651b9c8115acd846d3e759a02820f7d76d00b72 1311c5ae11dce952c16df210494857ba10c07729dde823c54724b72eae1743ad 69c5736482a9e733c95f03b12b1493c81699bf8bc0152ceb1a94975fc363e9d1 b0d01959888623f37eeb3e557983b0d2348d7cafdcb0d6de779bcd759691b64a 9c0a62a4be570e92ee124840dc4288f61621e1bf017039c102cf6019582a604f a33061a354abe0b6fc3f5592944b8057e74584133517618ecbeec5f44d9d89bb c442838f34863730b16ef7a9c0b79c8099e1705f998f815e53ff8ae1276abb95 92e85c8d0e40d90db2b09e2c0ae1b3154944879d98cb87c11af307ebd3844026 6f250ab6381bfbc828673355a21283589cae5c43a045b16a303de87acd8ecd68 eb46243748cb36529a7f11824f0377eb3d75dcdae4d71b594c285b5b2a848a68 4b0bcc06c2a62f7e975377a30c849df788838f1975c423670676e7c1018fd799 3dde058364248310c988a5cbfa6ff30f3b905465605c423968570e4e3c709e5d 56027dadbc753b3e07fbcfdac8603ad49c02927ab61bc9a905c5aeaa127ebb39 ea619795a7fd3317e8aa7494f8e1e2db0f72e272630354d5f8f8b0b4338cf4ee ed442611bbe1ef47212d0459d3307bb40da628d61a4ce456d21df090d1a03680 b9ae6b455fdc5ab135f396c3fa9de48d6a0cf1effc29b94e506a1e27d305727f f8b1f555e794562c8ffc333b9b7d0cfa4e905e3693897f7e9d846396ad7ad1b4 0aa2294dfa051cac520e3e54809f70573ce60117b0f53ca5d53268f500a61c31 7b634e5a783a2d0ac8bd015115a6aeb9b6e5535d9f65548b1aabd3fa925a590b 333474f8afbdc9eaa25dca3c2e3c87060aaf1642128723aa9dadac9bf80e1393 2637bc0152fc015bbee3755d58b74afa613f237b7de2e8b9ced67c4a6344be57 6292ce5deb5b3513f755d6ec50a6c6860fe912cf1be253983e07b9598644e96d 2ab4d1cfd2885c63fe4ab9acf431c4be2c975d06277cfa5f695abf6af770793a c52ef5bc392012c98db26cde53d51652536a790e680df86b2d12de24c9041a48 76810cffcd7adfb166514f9c6fd801069a9fa139a990e5c256b8d72f4d035606 11b2bcab4930e67b0eeaf55d2fe6b851c5d23e3d8847f7886c27961d518407a8 1d741b098abbceaeb9ec885f84137950a7e193d57c6896429c1d5e2f9c58545f c5895aeeaebd9e34bb78f0d5f4495ae8bae64f21445f15eb2ac11360b4df6441 cbbc62512772284eb90e3669892176b0a66d147c0aa7f2033fd6638190bf8601 e08c974370db59f1409b38d1278c9d73e18412fe390c2d0efb49a0ab19118163 6c8f46051655236520dcda6ab64f3584cd9c3d34e2230ef11e8bca054ae18113 9fe2c790a8a7d34133138027205f28922d7765af5caced672d4ba312ef9dcb97 f1c79d472fd680c34b898f60870a29803b1b4435405888571194e2fa218335b3 4f13084fa4b109c8567d3812080460841c23fd2670fdcc9ba4bdf4e779fab331 be60cbae5126a93ae9fac47d1c5cc8fe4c69cef7cd724b7e3f7422c1cb5bde5b 54eda64402ef1eacfee1e7ab2c601132f3ff38ca74353f22272bd1c05a75b372 13f51eccdde24958e7b2f889dccaec07ece490b478df7862ec07c30134f8fab5 48112f44d5d1a0d8cb0232554e9c43c1a1ab493724e6fcf72008ca4a10c9074d bec4c2f76771e111bd05e68e9749dce76971ef6931e06d8d3c1dbe1da50cfa9a a4ac31cb1d7cf7704408edab586dc3620ee34db586ec0fa5b0ad3a4de7984769 d3f5a23cc45d767767e99865f208e6d28f7b46a65b749da3838ea0bfe2d45779 4e52c51239f72648524c77c366310f756cc9f9a6244b9d79c891499545af8a5c 42e8e46019c649b44dc011c625f0e68659ee1bb29416695b3397a57739686276 bef6ae11bb38bdb763e5d71d0dfaff6638b6c5c45ba758fbc9c4940d32517820 207fe81b3d417205dce66a1e2e841f88e93ca157d05ed9cc7bda074d3ede4b70 2bcb228bacd218f10720d1afa39262f0366533063e10a865aced4807d61ebab8 f1642fabe3a7f1ca41e2c7c0c04eac985bdc2b8ed95dee00e62d652600f445ac bf847881e8276a1e7017b3bc3ab32681eedfb151117d9ec41e0b8d62dad80a56 32e1826093d2ef6802ec7105a1f822a32b0fee8c600d5fd554e3c93ddd78cabf 7a6caa32bd20aa1b6aeee8ba1e927d6ae43229e2f68a410926c5b3a663bd8fab 4cac1c2c55730f514bd885a0881c7e064efb11c723d666ec549cfaf45958f80c 90e6f7b95eb39fbd9b4b749bfd15baa161cf7a1c0001d9a016cb61db5091c838 5b8efac640b030766236e6c7a431025952a1bfe17b0c1abe9ba63a6fe908d46c a24cc62be6835478722c7c9bfde9c7a06f4355e8ff6c8062ef40642cbd256d31 06641bd3f77e8c158461807c74c3a0182ab897216140d94984b7a06636b08937 9b4929283210bcd3b87b3b806f7069b6aba1b93f389595ee498dcc516cee6ddf 935cb8668aca68f09211e7c9425e7aa9b41e265af5a97f86f9c9a6eccccd7290 9fae5f6a2c68d8b1ec75bbfdc547b978027714740e89881119fa2186a1eb5303 78fefe7666a9b2f54524b68d39003a45a3252df06964bd90be26a80197ca1071 c6f024df3230e2f2d39e96982c5ca559e83757b44b89ce3827ee255f0204a3e5 7b1c31e0a4812d1d09c006fdc65f99ba5b9917ead8061c5ea83cd08dca050039 5a6a4adcbc9f2e8bd1eb6d9d73b466bb3c41ade82776b2419986f065b449c9fb 06f7d38e442fcfc197309fd134b5336002b2fe3c0b13cfc581c3bc461af4647c 63f308e14960e88312e70e03b7b6c795d3fe5951c4d85ea4c9bc66b01626f88e 1b7e6212c93cef0fba3afcdae70b408b0bd6529c16b13b9b55f553a65596da9c f1fa13f7d43759aaa6593c6d9da0f946ef6b0d3f9851c1822b3b346ea473ab5b f0b565b46ecd70522913a3d949fea12903f98468c1b711b92e6320580ace72d6 4681f23a7927a324bbcb849dd39b1009cb6364ba409611e4c92f9d60ab9575e3 8e858a7f25bf9e37feddbe7253a225e2fba88e8227c1e615bdfd18f118478c50 d2703de965283946e6b85eb0b1c75e8b1081cb44019b99e572732960057901ff 81a2bcf21576c18d681d909b885cb2444b5fab20be43501f28f50db957fcb948 119abc437211aec92fe0022202728ef7ad0aa3feacce5b4d91bde66468720c01 65e36d8543186cd748c6a90e15fba9fa071949e535affbf0c9fe894637388ffe b8c9ce7ef43fb7592eedb7b36744945ebc44ab97f356868a94912b6648742cab 462f1742c86a06867225c0f83492d1677d33b26e0104f8c9ef0c649de94829db 5c7a9f2bd7d81e9c02b6778fd72c3d175d95cff8c0458201ce20b70dfff8aa33 aa62bf7c6cc85bc3f1cc5e5cd725635ca36e59d78600ae5a0a8e521085a24a79 408cb93a760193029bbc0dea449588c7fa558aa5671fed8ad65f0657c26a4b7e 766333e855900f8781e76ff7d05defa8a037e63f7caafb8f664ddf1a6aae87d8 3a5f62b1b1b68185869f43ca6e5716df4959811400ba0b2c2be3810a55cc5165 c873dae4c3d84746e933bdf8f1ab97812281f8c810fd7f86612ac0e0a032c56d 077dac47be103cabe3b87fccc767ae3d4e8e20b179299a4bffb6557caed9f753 c9beaf09e66d70f5251878168fd35263dcec046cbcc3ed12192d13cab37309a6 6c514b6230bd6799ebd3bcb28dfec0f12b2f917b9470e54ed8671a3ab31b5129 ba9e39e4449c453b5be1f65c347ebeee15e5f0a7d6632e985a56599adeb5d796 6728a0c323b1a992fb561aba7b6b203f18149600955a2b745c46de930215dcf3 07038d891a47a1db034eb005e0bcf82e52a2d936225b94a5b03706d488ee232d 4eccce206785c02167ab4f7b62407e4d7d87152d72d2e9ed0255e6e43a80381d f85ff72e0f2e58a48da96e22479ba9d0f8614f20ca60e9e00e8c772f3a72bd4d 16fcb6a2b241a2a67a1cbe3e09d78401735c23bd025236ee184d21f6499d4b57 3efc4e785edc6182b258827fa076fc9d505ef2d2dde578918c6bb8f2254976c0 4e5a730da97f65ff881480ebd6274180494579cc86a75d6ddb81eea8ccf51fbd ebb32d29cd97f2bff455d8ebc1279e0338906551ac2fa633f70cbb5d785f0726 cdb6725a9ed940f5b1498f29dbf7e48dc58f9816a290234a66ca940b8d358656 72c517dc3b5033861a2e676ffba3bfacfbc72495459a3537c03b9bbbb47bdc31 deac49dda4b2b6eb469abcbf3e42f33dc07c90395080fe98ac18f2861ea1ed4a a620e99594df3ff475d8527ab9e7a8ad25bde3d6fb2577e2958656c6f31dffaf 4d98eee4fabcafb4cbd95c81f11a8691697416c3ff42396c4406c03ab75611b9 916cf0c34d376c96582579b39265f8acf3ffc0d7fda982a7de6bcf4a7fbb7d39 c2b50e5085be93b4eb18627b979af38cb8496dc375cadc07af8e1822aa5c7e4d c3de29468d675ec4823c173467a1b3e71c2c77a111c8da85797165ce91f4bfad 2dbdaab66020f2945d6ca719c84f7f4a37a10c371b4be15786e049e61705a284 c0fc3892c6f1faa2f7fa7b99a959e06c4f22331919bbb162d86c3bd864cd740cee6a9bed26a07643ef1a35bc518023bc98c99ad35c3d9d5237d5f75df8453402c2555e8cf8f897abc6d11a3266779cd6056d0f44796f2e509f8d631860630f0ad9e20e92b28eaada520dd57de9c052fd6c698db8d53696f574984688f615290e217fce00bbabe1cea938d7ffd5201a8bb972697bbd1786b14d8ae497dbb1f6016f2a44adb5399de8cb47cf9c144566c9efa67b30c3bae23d72315b36e8b028023e28302372ab1d14faee6e46ba57d41714caee74fba07e865d18073707394c00f27c6becab99320b9b7cdcf08a6b84557a527472f910db1ab900cd8391d0cd0d7fc0046284d2f0ed445a9b19e0e76bcdaae6b839a93b40d6bd6edf5db1c9570652df7258dfa4f0a1507255c0a4a2b4529244014ce6c66b5643deff20bc96480b89e58061c7ea41233646c7c1bdd6d91820db8931960466ffcc154bc35cec0500ba8885344318ced5e6b6815cb6be8fc425458d0fdb89ae46f008ecaf35420e0fd5e701391dbca338187fcbc0f2b9b63c54c8e64b37aa2c8d08f7f7d4bea84500839aaa923ccf4f31610431b5e23e3e51ffe99239551d6ad55c0703685107e504fb787fd348ad4220df3faeda5f15a48fa5b7cfa92256dcb116818a1f6e0c44043df368973fbb7a26fa395e4b3269f15bbc4667ea4fc0b192569f797be0781f0cb823c70b25e33645ac8399a2c6ba7d7acee8212710487d1728a44247f3a0c1055637c32fa10758ef79b1cbab61a1135ad23c4f879518ce7d0fb171e18a0ae404903b8b04172c6984b67cc3fc81912ac8df62ff795e5a5578244401d62b72140b71fe17f6c61e965c190a390d0dd0392b088d7bfd294cfd42e9f299ef5a155405f0b1f1c8024ebd2656de8707354562778bd1adcf8dc3f6315b9791150bd6b6026031117cdf55028c52e552c03c10f32f9db4a851c6378e84e95cf6d7fb56e802ec5361b6c4fc53c02d770f91129b05ca8b46c8d58cd11ab1f68620510dbab602ff988f679f4ca59bfb74a1322b1ddd2b3efad7ca321b1259e3fb3c8285efae09755d9b605a7bbde5c6b79cd2fc32d90b4654c9d351d0a4bbc6a836bee50e050f033f6411ad67baca7cc63cf2bdb4909b6b23a03d4deb73ed6aecb1ec2cbcb90dc47b434a63a6151bfe854e02e3fcdf3619409bc0e11ac04e7c2e3ece5a1d050f9fea454305fc2bffafde0328aaca2d4cbb6dd497263172da41f65cc96ce6f30ea4025a8b2f0a6c1b058bc144739da901f5ce0198bd70184d5c31b9afc0018501d86c9865a24f4eb5f258763aa06417ef13ec4312e6a277542474174fb89f7c0510b38104c3b823b6387ccb1a9579d86122c8697e17a0d1e0e02d5cb706fa9b0d33e3759d0293a4ee1cc31db40cf7f679e37538788e39bfabe3883f7b7482f40fed53a049e3a2d26af996ca6f8ed857cafcd4c114e6678f1e40137fa5c293610186d963db0eca4d19bb79ecd9baf3a21875bd15af0ceba331e878e8804adf04075eb70b7fa96510a10229bc5a02cc8343cfa13e409fbc0c1114ac349cea311807d67926ba569508af7770eedad5d08cdc33c8c136fb63c68323dd5ad741182b06096d50a4fbb54182f04c4ea707eda6e52992095eb12f25b76ff3e639b16f88065c83524357d14660e5e9f9c46161247687191b14a4a0ef20d2de8f6e4bc150005d9153aa9c78aa0e168204c02a186fbd8cdb6d988d80e34b9919938ee5992008c979e33eeed979812b8ac98a771817d60f3db85b4c7e4d7e7e816bc0d220ab09d92008f7395f0315af9f072e12b90f96d70c5f964edd3680b896df642db23500cfd93a496bce85d370e65aef9047520ac95ec9343c1431a97e4ac90dd1181300d139d273bf1c3d20433301ad9c37076151134e7ee867ca976e5ad4377bf3b0069f685354a6bc334bec49c726b81ceaa473016ae23fb590fbed427330e149f70844b675fd0cee169d1e7506088db58e5923809671f7c5340eb35b5b3ddfc9b90b7f3c9f180b0fa445c86d95bf84c408b5dc41aaa41468ebd314f1fdb68f86210f48ec835922009cf05fdc50fc76af4f63fe32074512b2ee35a5280dbee016e402c8ed1edfda5f9fc5d2567b9807767c014734e6096d4f90efbc5c2dc927935d08827d12cee2868b142a648877c621ea4c65b03e43f6e6094688c379bc666e280c1fca32204d2ba29dfa26bb6a64b5edb38ca9aa9bffc3aed851c78ce0dd65760d61c4a70d8be226e3575aad8465c9d44a5d2f40f685f8424c3934d833f04e060854b7ea3ef2aa833f8795736c33300a49a1016eb5163b66185253622081d1860239d313d06fb4f4928393c8dbaf57adde87668bc02a6ec1abfb34da44bd967201786d925e07b2df4c4e6180501186cf2b9c439375e1381f18e4e47782bc7ffd04ece5681a10ec12162bd1260788609d29fb93d263fa4040aef0ece9d7d18d7a0dc436e8dc35b57337c829659318d445b11cdc1cb9d3f78ea9a3101629b3e92800bfd4798a61e85e0418d8e2907431894c11e55162001726242d041080c706c608aec7e59a3cabb0857193d1d2dfa97eaca5bcc960a3267ed72c444bef34b9670225459dca70ae4725859b0cac038a1810c68bc5d6f2b1e2c07f1a22dbc9ea020a23f4f6d9d96552a32106497d225b65a14cfac6075fbaf3f3a3468bcbfc9d270ff9d3111e92926efd44a3a57e196e8027339d09fba0d8bf92515d8df1ec932c0854929e9fa0c64a18de35699624ae02d372b1caefa9a324eb2b3b2e4b0106880586925ba5bdba7ed5947bdd6fd4ac009a52f1cfb679b16a9de893f5e7c26c69021daf8dda6f9534bffd7ce0d34649105b170dfda4982708d9c95dac9d6000f400e0960f97331e475cab6200be2d08196fa427e26fbf8714ffba849ff54d4d0f011692c67bf45ce860fdd9f3d9eb7191391a69f5c7c593ad5eceb5cdb94c13b60a781249e812ed0a1d52b98739335114e09147942f39fb8194ed8167b9d6b352008f7e445622f01cf6d70316d318bcc5f5e18585f1b3256deafea4be0f4a1ae603f27db333b82e21a949d9827ebbfb6f0f5ee5b0defe330da6d08fea350d00390c9b9541f5478f74b7b6d7498b40e555ec0315af1e1eadffd9ed2f56678b1d1c0b626d7d4e80b9b54e5fe1a97712ba6d9119a35ca828ba38175682e0f84dafc200b41199e818123a343ea70b4eb7829c2e6c546cc7f3d8c1723152686103d6e00fb988580bed3e37dcdf93d97f978d5e53cc6452b08003900ced9ed23a7852a0054c2f3c487946e2e087ae0749066251d8b9d4f4db2837789318cf4db320fc6201f1839eddabc1b7d49e4b7713cbda5f97b3640573626ef86c9ed11a778c55db089a9ce915236a32f2a8300315f201b4c00eae4a8ec18d2e198c2d41eac70a3406c1eb65cf45af9f4df45dbbcbb6fda39ec362e05b6d1b374008d409f708c5a00e91e4edecd4d2e26d56ad153b5ed705931b8f1e4b192bbcd60c877b63fc88b8097ebe2b005832e28ffc15923486718d0159219dddac27f2aa3fa67cd5c2aec4006ff23f8e09d92d88918db8cb747d779806357088f20fafc9adfa38ec4364d60c7af0232eed793817b1438b8bc035bd514a986d280e67516c79025ae07c31a10777e77bb861ba470380f44b56c4b4911fae1dd42f39cbabb888daa68f4a10560395654a73ab47c1d20d76a9fc550f3fe50b67df60d7d672d11b1004f09d9bb50337e7ef4fc90af386f36a1feabb66f37cefba1b16ed962aab9da97ca904c83e0f60ad2bab3525a08055bcc06429c78dbea4de29546f4b6ab2f31093971892e60b97be1d7efd0f9fcb0984116f41678ddcfb6bdbcf52096b21cdead2a379866e032bb81663df1ca3ccce381d759224df6f2d0f13b2f72e8f70201535e25a2f8a081c5d56c80185a92053f0048d578dc34619fa4ee2666fb8f0928f9c771fdff4058f375ce0f8f88bb59415f0a66b24bf71518359013d0629b9068ff9c75938f00e67ef6cfc3de0849fe15b2c554b5bbb629bb90aa9dd6e88df35ae3c311fcab60f6382d8767a35fb3466a7af140ad750d6e55d7e1e30622ef3e59472f7481a040f23b344901d968705a1f9c94cdf58d9ead5053349022a97123deca0c02656890753b2f19e50e5d56d8899439df936e3a24d0c6cec866499e988d70dcb31ca740480d3d80d0e65d897fceedc92cbf31892b6121c073b58b6eb024fc4047d0a2202ab9e3d7bda77371f5ad44585a12e39df3c8901a10fe58c4476889100c42fc2073bafb320a1fd5fc0b281fc3a6cfe47df9061574ceaab77bfba7fdf0ccbe31400df33ad43189f95bb17bbe508cff623a421b0921efef48d14f97028c715c2970bde169dea312ee53918f69b22b36c14b4a0b7120eed17ee0e171fe1747cb2eb00749eb547f86de2d0641511e87ec2f8ab40a6df535560d6571df225afd519eb0305fc3df44ce5ab7ccd9313e97b37b3efcfee630c4e6bafb2ad1e785210b0c30141db62d293e0a755b4fae04cc4f6a1d9f94433478508034a0dc04a655f464d03e5a899e8496ff447803c442caf0575b3caa06870c12e5ca8ef376fa514827908bcfc8a887c1947a357f039a847286d8f3e14166c1586ec603a85aba1d69bbb0c5ef598f68bcc6314c3a8727eff55be3ca8587aab01711b598bba1a58735c77028055fb417a86425a062da2f5d2d12b773c38d727d4850edafbf9fec622dd030af2afbe387c35f118dea7ce8f0c0a748b416cfbfa9d4fbdd912c1a1f1f7b9de016ebb7a24571efa19a048cff413a67d7d3c1359a71e5f49434a2c831a2aa7710a1cb6343d302b1189256c211782015f4f0c841deba992f723a42772bd048845004a49f45a24e5fa3e2ffc36e6f7abec9b8eda60be3bbf4bbc0c7c4768cd5e060fcb4a7cf92f037759a0be005c1833eee309c0f07249686c4b4917e12cda389505b40d0f40ead2091eb24bbad12fb0e017f6dc23a18b336d91f4d3be2667a98a00da0f5467f235ef3664023557939fe8f48909a6fc450a9b1be15b34500c97e00d0f81d431bfa2b837af469eb87848d20013618664cc618c9674851fd9c16e610f1caec0fc60e7e8076b47fc7d22ed70964069de1131a3fb9daa23b5e1fc17f5071cca5c9c98bed065196da3df6367527b7f0e7fb0b1411a04d98e1a4670c8b60a24e8a7c8b61036ecfb272cb4120dd319aca62b011b324f14e027f149f292310d4e45aebcef7340df0a153600083a680a8dda9d377a3aac2da36af406002831085a60b568c2b8000ec815171a13cdb811b56f4d884f90d7edae12aa654bfccc06b5c4abc4d7543b10cde28bc8b613af86e07e80874d1c354bac3f864cd68adf01286b4da4f4efaa8af929c08a05e619a88be9220f14346a730ccb20ab9bc31e044fdca407e3e5a69bcd61673215ef84d762496b72994e256eaaac6869367e3e00e70772f48438b19982caea11f2814549bca183307d4fcc7c02eecf710754850284fa8d7fa8157c53684ba0c852e1d1c8b35ed58469336d816de9a8fbec965406f727d36f1282c304a701af60232b8ccfcfbe727803e0ec1d24fbb2b01c2a14004f56e0cc4fe17937bd0999cd892576bc55a9bb6e02d560184e0788a7afc4a90beb0ba40e02c6d1a61921d5aa3fe00b36ce102ce7855300ff078323134bd26100f02775f17322c921e7798a4d097c3b869c4395ba740a6775ddc48429cd7214069d6c221673e883dbaa15f78e74880ee1288ea4de701dbdf00567e9b12892a50440f4f28b3209f0a2dd0473bd2110ccf229fc8f39dbb0b3a96760f921ae8c8008b3bf3b08b5dcea37e6c7daf155b7779283029dfdd0621dd69fb6b2d7f4bf120262bbaa4b7a9c2ec1553e1912973cb30c363f204cf3b25df3bc094f33409f4f0f0055a9bf57557ae917d607268678a5a3369d83f26c27d13400e6c1194b78350ec8a211b23f4c5a7666d326bf3abff1787832db565c0cb3eb1ecbe349270fb606c0321363b599b39dc79d05363fc7ba8bebf35f9fcb34e1f845c79e308f28c5012c33773736c5a4ee9251bba4200e10445d49c7068dd3efde67b07b57c2a7bf03c723f507a11c7117345d32b560f662cf5a134a8ffec58cdb89d4b65830141002e246e1c0e4f7957d3af299dcaa30c615284bf5c6d8046a37479e858378070a08ebf9a7393ab10256044df7bde43b1f7d7d946ccf26d918269d899e52b4c75a017745972581ed46391d6f4dbd9784462f40faa0110c54772aae36f61608610b0ed82a560ace6dc727a836f315a7775b150f420421617ddeb80a3c3ee19efa3e076cc8a1240c8d6c3aecdeff55a83a9e2f786773f8f9dcbd63140700a931b96c0d87d7481905fdd1203b30ac5612052b333e59ffc52dcb6abe61591234c3bdf1033d6c6e01baeb353ccf8af740f9f639c1b286d0a6e43e8fbf54cfd762a0184e057c2caf84d29f442b27f53286f3d38be1b84c3982895a0030e5a2a1ade06a9b02d3f3a0dd9652434acfbad1c610e952aa96f49f6092cf263ee47d407459004201293900110b81f839ce82aeb9872d393d614d1558f083464b7ffa24db2959c70bbb1f1a2d350a677fdfc0db7df734627e99c150f00664e309549f6b0a01d5e3064144284abbab15fd94536f1cc81b79cb8dc18b1eb353955f0499aeb4d8f94406c853950f74791eb13faeef2b88bfc6d4ee32a281ca4f097b478fef6b9e79e00bb67b196aad356e16fd53f8da3d4d4298ae2e62c2aa0f7e31d74e329c226d6b0bfde5da51fb1bdae6a470a18576a0d0ce3f889ab2838e36cea7d939e43218350b84ae6f675d0efe12fbc91f77a07843a01a8e8dd43c119f0304fca6d14ab8ed0a669ea95cb4d6f5bbad496964165b34673c5b78bad26980c5dccd2912fedb320d2937ca9aaa26ccab9c809589af655d7af764a0764b2a5b4eee84991d6b39230680bd716601cd4c829c208abb728eba52dfaa4bf3e1efe1e5828f923d35a23d057c3d2913a5c26a628738cb4abcdbc3d1c4ac9809852ae72648ac259fcba386076c917565a236c94287748a98196f5f3dcf91a092082edb20be4cf26afe69a80a95c2947c0436cf57def1174f02cac6d64112ae10926a64cfeec5c0192a0daf015314c1aecfcc0ea8732ad314fe049bb0ee44bd80d5cdf0158a43b76b4f5b520d417c15fea31a098a658e0ebb29ba3f2e639113d7fb9b65f5c2d92918e88b2504495db8d1c14d4b3575fce1a0903c6744bae71a648a06b8ccda87e66e73abbf048d6aa01a66dbb10f9f61760d2e9b90cb996a51c6b394a05577cb23e02b6ac206aa567cdef5ec6b423722ad83801c6c444c3ff18bb5ccb0ef25293aa0e7fe7109dd7b117626da94083d4e762f3e21001af4581bfec645781dc4a8470fa7ab3d0efaa4a562595ce6bf66eebc5084cbc85473b818272f664e0b443b46fc57ce420dc9d0532df7926da718a9e26cf39fa2f43641106c20ebb24042dedf02f42313013d11bb3608d43b866a0be7dec3a889b0d8970f53bfaaacc78b97476dd2f8950c4ebe246c25eeab2b76d1d6287025c4e17749ff37340cb929d4e2beb938e85905a908992dcbf491d191375adf57115b4d4ba93485e18579c16c3bf3acede9e80b38be1df64755699720326a30c7ac47b93f4d95d2ad737d201b353906b63c4809f5ec2e2c048a2a0125d3d85948c51be82a6d5b570b2920c5d74692c1722e460529b8c6fb2351e870deb8138c8e55c4447b6cf52d29196728b23eb64db030f10edd8a98dc14228c35cf5505c42e224ddee676b779988e043f754e36ec0738910784289ff9d43f5680d0ef08eef18ed331f4a9a0ddd75c8ee9b2b6815c56f80e03c8741cd3096a310060084b91b31918b13248a1b233c058bdf549d1689e662b099d9ff87619df18b03631e0b104f5efc6442995134c7f7e99c97111328e2c2e08161f6e828d94c01f18e49ae63b61bbdda6ea123a90847f1e628f22eea91ef408baeeb6f5d297a34dad1e2555e24970a7dec9a371034aae1672eca12b2d0301009d105a6e14155ccbca92516ab982728f8fc0b73a5cc890d3e045e66c84241f0323fbce3b7bf485e02358e9b2ba1beee5ae7308a1ff8eedd8ac66a983728196044f7bc20a01483b49337e9aa3dcbed33f278af016addf37101d2350604afb9d03a59b2cc514f12b8c048ff4641dd71b18ef7d792da9f838a61c67392f3457c4002d23e1e9058b08bacad7a3e533e0b8bb7659c8c08d8e7acf0fb82782c4f9a30df852ce20f942faa0734eb554003bcc1700677118409fe39e0db7970066b60a0e99e48c1fa870b2b6b326273ee2b598a734754a624d93d4bd3bf45dcdd0977c0bb2ea6ccc382688ec5a022669ad5dbdd0d8f2e798344a9207eff6a44a4753dc015968fe0f41d91f8bb668ff0a26794caf40871e16f187a5b09fa7c80624292c04e5a03d8c6558f50fcb16bce6bc1fbf5d6d896cf3f4ffc1a18e53d306671cfd07aa1207ee08dfb85a23ce0f5d6dc9f6706a3ec78a6a7f3c5f7bc05117cb944a08b602f3aea76640a7660d7ca8796877f9a98c68570d65527da755378d7e62bf01dc7b9c01698cb899d516a1b07f718f0a1e90fee85e0528e90fa66811b2798e04e2120e70bc399ce9123a8936d30dbe1c562888f8e1de3352b8dac82522028b0cd8b100c910acab8ea3b70ca049852b6d6a45270733664592867f4fe36f74da014b97504d78812cb04c605add994b3936c11b5deba06bed48dcfdb57b8895f10c1b8d76621a1e63ab704862a9a641ba9afc6fe82e5d36c071db45216826904100a5591484546f19452acef0ceb9375ee8f6aa882e8b24a61b882743b9259e9e05c50a55345c2966a37bcddfe7d3851119554452b9f3e97c76c8c0da9d2443cd04e918d567cb3bbf7e6843fe2af2908367ca5f033a88272031fa1b8b98df86c009c5e2556621260ebc8be2e072022508254a597f9d1ddcfe67c09089752767a70c34b03f1880889e084f7917c5e61d4ff5855af0080938d3423a0f5027a577a305224e09670ce067fdf3220dc616d16d2cd9045a166350e3031711b0e73b3531039db7e4e89ad3f75d5279b03cfc22d65b455f46ea32055fac504a23b20bf8070c006e821e39ffed870b366eab3b0c467b4900ec03b0bca5f2313da234d3574300b4cff26bd1e221a24136dde0fc8de77b6e61e3e1af58e8cf43ecd9cdb7f59e04a37d065b331e56963202842ed80b91c0b7e756a33ca2b9546f4047f007be6609c228a1d4c3aacae4c068483f949e0aa84a439a9f40230eff2a0b57e902e0460fec7c7062505cc9e4c5d940809f6223c8c16ebf99a4709d0c488659205b7be80e2900af4f740e79498f07880a8ef08d2c3163ddba080260b573bb5670f88c0005f6cf198db0bcf2ea6b50b0a0ff9ee2b17b6dad3af69e22e6d76a61862379cf04a9153dbc3863e3b4b4847b4d4dfb9975fcc2520007e9c9d3657773e7e93e0f08843fed1bbdee76ee4bd935e217d6e17a257cb5d926dce0e0ba6163a64e142e083ad5101f149dd64df0195d7c47286f81f320a3993f55f90765a91a5d4952320d571bdfd615f26a6b2a62b75c9854218439cc2113d37abe3e7b7fbb2e12e7a507d76e5f2a490ab501483f0d0a301f79961acb2af4df8bc0807ac7b02ae22b420a4a358c2900fbcdd33d39a85b00a3eb5a5e8b71c8542037d2f46a091d22f309027667e3679a651926647c70e3eca57856d434646c16188c369d90e0e78951560ee937f32dd6f64dd80967f8844b60c96af391e42167e4ee56cb01fc68190db50336a5bc1deadd7690f418074b2386b602dc7ea3190e3e6d06bbb7a048c483840c true -check_ring_signature f6f64075258b34b65cffedf1124ccd59e42a65909e0900542f67807db93dc1d8 2ab1fce84e9880b0ac8e0d5be9773dec8273f1eb4a4d5c4728a4da4712b9bbac 8 28356197c5c07bc456cb73fe54d85eab83dc2219b2feeed6ad247b03e068ec1d 3d30329e1a86b202d07d98104b3f551a02ad6f2d055c9eacb544788d75731791 3a1bf2c78ab2088b5d114f2ea6353ed0b528d63dd2df6626130dd176a845b34f a5e73961e6252eb8b13b39cbd354371efddfa0105cd2442ce0cfe980365eec1c d19a7c8208a97b09fa7d87edd228b0c54018349eb3f8ea00e9f86f1975e23609 70c572b5d35fc42a5bf2c38462b63ec28a80099d9bde38783812b2c378a05dfc 4b89454ae105cd9d8ec2de3e85bf6640c4f170b6f65e9b748f6034f1941d05a3 72f6d884f1ced4c0fe6c7b61eb0a82867443461adc74955df659eec16f4b42a8 e492a5fb15f48f4cbb40db40b79be2ee2a8d75809acd25f7360178ff31c5f70760afea790494d55e6c24b71e54574ff7aefff458f45bf62d301c07da236b22082a17775648e11b414d1c19575d113b15352de20ad617cd506848769371868d0bffd4367d4a744c9ae58a046af9721e3e35e2380294473fa4dc709e35a5e0e1023c3b0028df5cc0564060ee596b8b581d13a28affd649d24b70bcbb36d724b1002cb2782df82647704f8f07e885968cd33b2b3c5cf7eae7f5b7b9303e56402305487827be332dabf5a60d85001432a70bd734a61f9e26a11f1798c6de01d3aa078e2bebd5ab301ce07edbf47479fc1f02d946c5b44669333923d06124c3a6930e075aa6bc99eb6013783eda5b39164e034f1d01d5b234c1ffde661b2cfef0b00e792de7f8e2c7d741f54114900c53de613166f58a04593328c46299283d2b9e0282ab0dc6acf8d6958d606b2b9fc9c5a2c1fa787d44d51c016f7f25988b8f1f061125c7e4ab585bbc6531681181cdf0780935c2665309ceb7b1152a0a58bd63041b9f478f7f48cc1f2505565cb05a9a333611d7858c57651a75fdd533e4ed2b06ef55a765819ecabfa26c42cf4e1bb8988b93d3ea479943c3f8472547ff4685000d18ac5db76149599a76781dc8a556725280748959bb2a7068905b35ed7ec409bc1449446a4de8f5837b9b53866b6f4dcb1ccd3493c4eb011ca13f8988cf450c false -check_ring_signature aabab27ecaf0bb3b44a9db01ada198b77132a22b25ad109feaaaddaf7da2e0a7 ad27667558b5a1b660f696b7b587bbbe0e35225418b8aaa3f5c5cdf1d7b06534 60 d78652152b7cd38bb05add11d47a04cbe957113db30601fe96768d1b177f7055 eb0a2c8be8d16be78d61bf07994ab39d36cec5d443ceeb614677df4b82db763b 57f6df19a25eacb7c7fe62bcdcc4b25d69494984fa2aa50516027e8f86f27d07 f24da61060260a01e32b1f5ebef3bb97d7f630801154754d88d4ab2426941dbf 45603611961b861bdb406acbf6056e466bc1e6db3d7cefe3b33093b8d70d837f 1811afbe32792074bc9bc3fe448c335572dbb0ecd274456b3c1b14ca9e29c3d3 d64f7c7823f88964482e90d45201c5a282360e68cd79e945fb590fb0e59233c4 348766a8191738c3e3c2b772f0238eb962196a6b8c08465537863fe19f98c0ea f59ad6704dcede914cd3c9245eaf6baff6e8006f08d1db19c2084a3118049cbc 014f0574bf4a9c06d3a19b2e676f357aa2dc03589cfce750c0fa4234d4a67d09 486604a08c9dd0968b0c34a75541095773b26725220597892140d4ce147c3881 c836fab19cb91d7285928136fcb573c2e622a8d15be55733ebb4cd75cac7b26a 4be32cca9c7ab1f3dfbf4b3e60ff00352941605e5b1472a9804137e4795afdd0 c77c1577ba70ece767e14640f858862fbad02cded0567c1d9a38c5a0715da5fe b22cae34dadef124f8a6b975d9723989476d40bd0ce47d40dc1e3a945cf35a9f f735a2e7c344f7f14dcce6c1d53fefc0a2cfd33fa69a0a21d8a8f0f6c4d3b435 f5d0c3f7640103ff6fa7d50d41ca30594554c1141a9adfd9128fba1dac293cfb f4e7d851a6d85c6ad2d0991938bd0959712c18f94d65925ea6c7192ec76aee5e ef6cff64108d7ba117ef0d15c4cf8f7a93ff2b2ccb01a38dad68910641e04409 02c4e0c40d5547027553c36254fefc510bf3b4ef88fa5934d3aaeef456eb8794 c17e9d64fed24bcc97d7769e92cc6c15485a9cbf90275ce1df8f0170961ad5ef 54ad8ada4093f15b4241a82f7e0188262fa92033d120c78f529a6955c765b550 f35f96e87a3755664d229dac43714131eb60a703598bcb9b892e9c0c540a1eac e09b628af1e11fedfc565f1e342ee91920dd90746930d03e5be3a62d39809fd6 d899eef0e864f44937a297d2e6984e0ddec17cf555d6e54918f529e0c9f89a95 f1f8a892127b545801c4ec95a8568c4ce46bfc4a27829c6d5f4e3594237240de e66ebc9b4cf69363b74e60c9edd507e01cec4971df0d3376f23f662320ccc57b 07e5e39d0549dc0370b22ecb03192eec50d3a7f8fdc89babd8522e37674ebf8e 67aba71ff9f103648390288fe7f3b4ddf63bfdea539c9534ceba07aea5400f9b 52130b3c60c920198511b8cc0a70dd15e7269b6a3403f9a6c757db0bdb2f66ab 425705d4f14cf23a2fb2b8dd371c6a7077da211e6bf5efae71c695ab1535073e 2d7dc2bcda6d535f4cfb06e25cb27fe7668f6d4dc66ad04cef77c61f08ca9077 30feb9702dd22fa11d8205eaf1188c56d747a4f1a873249411783adee6bd2847 18a13a7530812d7add7f015e734b1f5876ece89e1be6e363311c06546aebd028 329994162e18b35fa6024f50f9da760a15be0947e31ec2328b4988ff29a8af58 3ee85c672100dd7cbdd3039aff52cee703c5cddde1ced9435c6547cc384232fc bfed5251432b490f6a08d44511a1749fd8574c6a504c55fe1fa160c27faa4046 035d550ec8e3f791dee683e9136bec79ec433df6dfb86ae6232664b37307f11b 25b138da74af984dda60fc34fbb046f7e7d1891172f6dd71a2b7179337331fa3 2bd9ce1db29eefba2ae0f6245d3e87046eed634abd6367912dd2000e60ecf13f 9f7f9ec9c349d2d8fb741705ccb886f336348db7731336559a2c8570e29a4712 42bcedabc959052c84bbc396ddc5412f01e0d86cbc9d35de675ce1a1b96cf34f f17040830292b751d88cf84dd5c536d53b63f0910f8dc8a72760253f6e8461e1 3a908eac4bc72017c1f71bc6a56decf93b6a96a79df1f3f2dcba00f81247449d f473daf5618006b834296d1b2ddba7a55e4efba8d0dc3a042b639b94ba6656ea f17aab9c802a1a8a87d03d2da46c88a65e9d21af5ccdefe7209b3abb5e47a13c 30ca3152a58e93d819ecf2dde5eb084c3f4719f0ec4efe90761ef10a5f723aea 12e849d30e2e4107b42702540b50d151ef5c9281dbf33396b8da8ba2a428e9b0 f1738911fbf66b938121f1b91709ca5b192d67c25e5de6b95910a015b0b2b6ce 9b0634eb9d9cf30cfc6087fbeb73192ad283c2330ac7cd56a514dccff3d3d01d 31648b2318ee551b85d766c80733fc8b1035e403431f7ca66b1c01141aa98331 263d0c109c5fda2f6e455baa94c48fc5da79f0a727e86387f5e7195ffd8317bc 6d6087286bc4c9ba23f799145f20dc8a94aa4fbe51c8ba3387ab22e987df29ea afc268c4a001e9c4d232583d63b19ad084e8ba6bb31c66cc414a6329c8574356 3a3922f82fb19feb66f199480745cf05ae4cd145e8356347a1a7961d3430523d 7aba91b35f2ddfd127e0d13d6745c70b88c952412abe68f537873f8231b0751d 04ed629b819e685d5fc2508752170249633ddc1a1a1f5727b411958c95231750 2b01e3af0896350b449eb4d3ded5c8f8e7c409c760233bf95382f6f40ffdb22e 31086342424dabac53f3d37c79bdfead3fc2dcbce435cc39908e570673139773 9c79093f63f80df0dbffd406ce7b0a7999afb4e5c6ed5c52335c832be8142e4d aa86217db36da61e3ae63d6b9f5e472811052056f8dc3e1330078fca169f1d06a5f49a4f4598b88cfb8b6fa62655924019cec94f4998f76b62d93bbede69ce024f413d2c04b9e9872547e4a8fe1f003eaa18835bf37ad24979fb5ad73a155c0ed699743f22fe4116d6a5e8247095568573dd208d576d11861d826e675114f802c51ebf359d6381561c9c9648bcc843a276230271f8ef5fc33eac3e699264c700af606aa0397be5b78ed068f938b9841372e43cc6bc37df95d68a08bde1883d0c08fe8af8b7a7ed1c80ebfbde86de257929e869a56ac359f063ec781c3cc7e50946f026d8dae0bea630f0f6f1aad7e13d8b8c17173b114b131ca88ee8ef8e830ee85e6289945cc5089004097de1e4e3df9d05d5dd7d4ba2996bf8bba8e63abd07daabf7cdcbdfb8d71d91fe38464b113c6db088f11333f88ad7d9a695a2294402aa021a9fa54e5849a1651b8b4523c24f8bc80b6fca4cf703342cd4c94bfb0102002d721e93de30c9f9434b025d9a2c8e281cd46875e3a7f825b97b66c6ef4a06e7fa6ff515151218248f9e3422a7815a721ff87f245d30973e494c579a0fa7db7f1d3d7d43c136783ac5e9010259cc79ebbf6e8471a1443198fcade76a8be40f3fe45df573a15168eb21283d0c4ff53f8472482cbf3741cebfc23c827b98c907d3749ffb0188aa40eb21fb3841051e537ac75a441913ccd2bca37a2d2ea9810ed05a69b00087d287890bf1159fb94c4d63430bae964b93f666099a541ab9470a5e230979a55b21d456ff1bd91e1d394ba9edb665c87fb333a32167c9c1e80803ba4616e340c9fc2f78e118f1b3d460ac7ad41d8a898616c5761777b27d1f1e010a01ebadd37d396ae238dd0ab57ad392e817a0a910655a58f5285a378a3de40056c2679d4b7af2fb3ff33ce91b8c196c874623dfbfcf3601a44b9e32427b510b83619ed4ff7d58f3c7ff1d7456954bb9125a4e94484dad0aa7c548f88c5bf70d1647c9dfc4bf525c7072a26e3a075b913383d67a78be67210d3e18649e20ba0b87b187d000a8fd51ebeb406e4ce79455cda47186aa081292b7134923f6f21a0c83d1293b20f2e5fa713a3d4ca6ccf9565e78c5135365cfff94ea517a590e4309032ffaf913bd8d116160a9249d1aaaecca811b28e464c4b708bea7f49a34aa01d8153852e676b79c5a2fb2f461b1b2482abd784ef6665d00a43854ace1ad19030fd8f3d7bef0aecc138b82430a723e0453959ed8d11cfdd3d45076cc39bbc203813e0db406ee73eac1616df119468f550305867583ac3d6504da26faa09d1202a89b1195f2252ee7dfbdbf15bd0883063c92f1d263f4ba6b3d9dc3feca768406bb3d06f9ae7a671d08e8d83235a9dac49b73950a9a72cad20c4de0e0cf2a3f056420e0a7ba5020a4b57cfc18bd71600928ea201297ad4c971162fb608eb493030e26c7a7b69aae66c1226d988e6e1957d41dbdbbdbb5f8129b259fce8a3d6c02b75695573c0d5e8413ff9ab99d09ac4b8c6494057c776072b7d6bea74bfa8505f3dcd9d17fe10f12298b7800ce492aaf1652b8440525ec6535f98213dbb27707ff93156ef59c133dee405783fc4dee7d755318a63b9ed8f73a0b233d3904e10d7baeb5a02ea847c5078713805e48c19af0bd416a0c8946a211296c92abe2280c63c36592ef8d91a4ae63d4d524cb6c9d8a98f19c7f168cdf5bae957e1c1f8605700514687435e2a052a041369128a0ac4688be165db265bbba3179cee591a2068f78c3b057b54c9f93bbf55d6f5a2671b216725613edc3c909ecad248408ac05c3affdc859a844091a9062025e18ec837f88be3197e899cc6590ca6b3dc756086d499171cfeb3c7ca4766d17dbfaaa15cb8ab5ee59c777e4c7f0fc93ef08700cb9ee5dae327746c9f769d9d991b38e554533b7616a10902aafcec9ca75c8dc09b8c83d361752ba9cb0cb1068422761ce9d33f8ba911cbd49f1bd0f626802770a18a1b856423ee1b8200714220014eafe9a062bff200c52c733fe87a9fa122900672b01791d8d97de9747ad6b02b612863dfd0e71686887edbc52b56e5779760e47dad3a0abdff1ce256675d1cb775c804869ae9e32d6d81cf371e1aa17e53201e86384a66472f2ed3dbeb36977161206c581ac9403353b0068c498b23cf8f207a260a0997aa3cb1b275be57bad1851002ca446d41a3431171651680c1c88390cae5a3418feacafe59b608c9a40bc88b0f3f60653db0c0a87af38a3b6076dc606dc73f9af1ea43bf6144907426d3858da553abb793105069e4adcf59e6844a1070307f7ce3ce179529d57d868295b3c19d13498ede482b5d40502340c5a37fe042ff61c225f120b4670ee47abd60682828148713bda2d70771861c3ddb1f46501123b4fed5fce8481aff4291f8ccc8980f4d1ad8adc6780311d5139ad5e108e0b37be0f4cc974180f713e8955d4e8c4c2b7614d14a3db095f3119aee5eae07e0ed8ecad7b30a2e792f3035aa73b5eca8f5deba11f777ed32e1c1210192bdbe40ce7858ae9859b4c3d927ddf8936425ccbf78b74b630424e9f99646760201a49051e1078e74cfd3aaa298633868095528bf88830511107c904948c9d9a09a3bd01fbfad5a638fa9b02347e54d797198ebf2459246641d19058e525a11ba130440d88877cdda2dfcf84846634173f0112881dff9b66193b4adf8410b76a4c214d088e22d3cb08e5f0be6930fb55b29eaea06219574298aa69d06f0096e8856e07043ca2a87d24ca5a483f2eccbd72d14ec3acdfdc73552f44f4465a362cdb31ce01972588880bc1ccb64b08277616a56c15611ccb00a01207326055bffff26fc60db4dcd38eaaa4f874a3b4bc7183ea09a5afe4c2fb8a86718555b30dd47b63220a50aa5d4131e66716d28ecf7a6034d569490a6bbd23b06b5004dd31106cf6ca0d9705a95f48b25f823c685fbb557322bbf018306557eeea7be020bb6dc2bb550b48b85f9d15ed604578aa190cca82374e1ef4ef302fc531b172160125f9476f06990df91949809ef78398dfb901838d1cf35e6fa08c39971c4192c8e228cc8807ef41d43353b4b885eeba9833612dab1253bf0a14b783387cd4bf4280125a110acadc52f9b472b31d2e22e51f8bfd9b777b76754b15411bca44e8ff694882c00d05399342608f26ddaa309406033cb6ddbb26d248690859311c525b755aa6b6060917173503c9795fa91b86c9e154f21419790fa2ccb7e2d06709ed2e4577e3057c147720367bc370166fe22b586b8364443bc22f7c6d9aabbb6abaed73b3710fbf0b8d65cd38abe68d19077744d5ee7f2c92ccc7be050890803b17881099db092354eb678a3556cc6eec3ee0ead0eaae8c2aa428389c9b0f6b7c30080266b902890b729e5cac5db67a954196a638c5cb861742941461d51f1875db0bd89765072cb49fd2a3bbd1ddb206940b6e4d0c0fe4ac5706cf48458542eaffb93731190676cd69c0a594470546721e998c670ace28809743f3c925af341a26d84aca0e0aa6a42c80baf6e38ffe82a195cf6641a0ee81f7533edd85c6856d2978fa41e006484d3da12188c8c2b078ea5e968c81b9bad49709d33c710194b67b58f6a80000071a20613ad08fd76893e91ff6691faaa8971a71ad20645b210e74bcf77b3e0ee2050d6759a2032ff49b0815ba5d38c50f367183c3ad4471347147b376670a04dfb80ff3f548fec0910022b7b281a1f31f04a4dbfc8d0a4e3566963e9f3bc209f543d17abdd7c34da414067d56e6f6270b1390c1bd8a1654dff144e1f2f5d50a323c5ecc7b1257e53fa4bedaa5b90a1e881e1ce6fb39cec5fe9b4a2f8273690a5dce5edbe3792132655f2361876c0bd57c10e6155e03ff5fb4146c5dc212d80d7cd35c7d1525a6d8ff1e1be4d550b6268e9155f3b7985e08da4cdde82874170e88c1c9e94309e115b9eb50b17afb5d73c72174ac850728e0dae2be8b513d070d899f5a3fdbe9773e3b46a691f488e4f87da99f46b77f53ef38ffb2d2f4abe0031d064496a4961e47a5476dc843671756b3eb75fdc7164a83e15a3e6eee90a30668297cf16d73c7a46c6c132a088fd2b6ee71297a54bb6cfe0c0bacaf2109470f1f335472ba9b707f3aa05a4ce2b1c3854a501aa69ddff9b08c2ba377f69c4704e349469859c22125463c927fd0a32906c115f5aeb483997847a09cd42742520edb21709dbada4b38f1b5a3281658e9da7488418b46a444fffc9aec8064330803089b6bd8ffae7067cc89a94bbbb00087da8a69e48a6011194d2a33d2c8309f06695f3ce94bb67aea10356e820e338d5fe3b118558827ca8195bb9f8138a4ba004a8559684e3e2298ec95fb379f9f0dfd0c468256228cefb8d3745a18cde98e0ac436d92a333e3face460ce574898872ace834356ac8e10e267f19c81f893ca01a6599a24dc57e0d9fca486de7cf90f261263a8db82910425b39f72148b86700d4228f6bf11b022b605dd502097bbe8c2b6bbc3df8aa8f86debfda84299e6db0f611b470e495ec0799d89d0297b577c0d046ced81e4555c851518e5c348e4f50ada952d3210aeb1f399204ed540c1c8b91ae0e23b0a69393a3fe1b6592dbfc0054b2b540816bb0a48134b4228d282093849ce1b9917fab7f16dbc8c37a9599205235e397921baeaace9d11640ae09d2be5d8a69cb265cd772d6308cfe50afd802d1308baeb511e7affdd70ffed228fc53baaeb302e8155a674fb4e9807b198d0a676e60e04f728bc2b87b7471e805855a7f482cc9e402020844bfda5fa061090a7b5dadbe28bf589d0280fa49e8dc6e0bff3f469c13449806916cd22c5e23f70752904a526c91ff424bf08b60218e9473f3c07e069028ba01d0fa0b20ef515b03c9008f9010b3ee51f9fc41e6fe52cc16a6e398e0a8b74242888603487e95840d9254a30377bea4231cea444786b5abb147370ecd53040634db50231828b5860ea51f4425b9f43bafdcdaf335090d1816fd91c50c801c21683dece1e4bf7ee308012ca1e5078c66dd3160c840024caa1a0546eca404342453a70208827d3973011a25b993faab39bd4e72afacfdef2c535f7b0dff22848d1d8bef87e8ba93e00ebcc81939623cc1226534a034d586525c590aecfaefe9ce66055e4d20f7a4be0fe811ef90fec8de3d38b7a624ba3a8d37e6165af97ad23010b7122ff796ea450df3123afbaed7563887897862bf247a0d48d80a3d50bf79cb80c21498d5a3120a0f5e11cae0b2d2d466f5be821b0e2c81a88dff3024148e6478d0a5edcffa890218d1c0df479e085722922f0d631d216ed637e387c12408c36b20def6c2031609895d5e960ca937ec83ebe85f17e2f50b4178d025ca2bdb10708ebadb2de85c0a683ba18d7eca351a98bd9f3bffcadb4696b6ec45ed98c0d7981d78971b26eb0f false -check_ring_signature c2f790e80754164132f49e98638bfbca64c6f492ff863219d4c394cfdc039eb5 768452b696a78425e22488549a0ebd848d2ce944113cc8769e176bf555b9b353 3 9fee8b8b3c5a42db9e0f66e0c5f42ef5a3bee406a4a9ee1799ba335192e319bb 9184c8275b60922440bb5ecd0f6a2d8e9aa72ee3fc1a42bd9aacf0073841f3cd a0de237115b2b62bbea966be21ab254d64f7e96829813009ee965653f648d1bf d095da202d80cd6c65d49663e48201b6321719662004b2d5357b192b89704f0151f0ab969f87b26892496e32c94a214cae45860a9175946f7e0a008649b813008f63303f613721096deff08ec294182f36c5d51b56b858bc2f76fdac74d0bf0fc1e65b7f996545c3adc7db16902a22d2fb6320c75f1a6892c6a1f68e149b2e0e01a65f5e7ab7e0486f6dde05555c4aa349714205b52b10d9951866e81bb0760b0cbfd1526e2ce58bbf4bcc47a14155de6b0d22dc16846c49e9bd706e1eb98309 true -check_ring_signature c8512e63878c10d321eb43f99702a9e8fc744fcd7bac2886a5099970422802b6 d4f60c9096a3de61460f696ee12741d822963868f836f4af26ddda6013760b8f 29 7172b01846554f3c72c5a76e61a59d34922e9273de2b5da52dc98a3b1226bda7 dc2a5bb6b19c37b9c32142086957ed86019a96e5b11e7ad14f672b96269a4779 37635481043a62aef9efcc838fd9e00fb068ed281ba1f1eb0c6a3cf4f0a52057 7f37b77eb2ad35916e93cb817152ff0effe6a723bbeffa60deae63c5816c139e 6c76a2a4ea2709e2e01fdef911d4b32095cd463d1f9c8ee9a9911400ade15330 58e9376c7cd6fd0402a784426219d2160b1e68fa1e66a1380c58cc8114c080b7 957f819f31a208a27283c627ce11ee7daf98745dd5227010da1d4bdb1337b41e 336baf26fb4c866895f75ae58815cbfacdb08259371ba9847ae887361732463c 887e94c9a14ddee422ef5ad1be9a232a4dc8dd410f77365d794f5b83c15a307b 14749e0704bbb580b9c0416dc065236269d2645fabdf3edc6691b98a3faf0f7a ac1c47e401bb41a4e00b81d0f65d319ec8d72a0daef619610827fd964ec4728a cd64b0d9d68b44e32dcf0ac59f40c747b8350b1c9241c9ee4e308b1c3edef2a4 a61d0ac4c48b5961c3571276c997ca765021e69ebf841fd830d668bde5950382 9584b9d1885c06107842059cb97aae1a63036145befdc6574818c841e13fadf4 c578e6c76a18fc9fd47338e60433bcc6b8d6dd23ad717941c0ef8514e8e2a805 4cc1758c78003c20e8eb9bbf608801453e0a8d94e0257ac3f28ac1753649981d 37788a6b9ddb9bb9ef8ebd5776181a47736b0fb94c057234b03936aaf1f714e3 2fc75fb8d0e166610e155fb2f16358d0eedbb576ba0a181a9c96ef9863dc1513 a0bf1b1d696545eafd4641fa14298426e8de3fb5f88821670220d37f03458005 34491fb2917da13249c91e72446c8676cefc1a97c2d1c65012d03fa7380f2376 d1edfe384b7b96bab2ba10e75e6928eecbe5dbd0959a281d86b738d3ed78fb20 b2967aa7f799be8be676ff57de81b65a903985bd95100a42e77a87f33f71b529 db9b9b8e53056dd960ec1b7b30e3a971c2ee0a657054dec1ab9cab59ecd23c92 51dac253fd6c1e7a4a81cc6dfdbc5873a77982a2376049abec3f0929c92bb6c1 6f49ee906dc950219cf9947891b11c8a7b59d9186e5759d837c3e326c5c26d53 3b9bfa927933ebe0d2a6a03b1d7b1277694ddea6d7469cabf20b215105a8e7aa 5e8831769be02f172a3e170e4de98d8a08c76ce13e0e77eef3b589f3c915f111 aef18b17d5bacba6d4cd378b86bef609bc4df67c56cda0e81ac948e7e6d000e6 c2bdb81635933ad1f253c42d6cbd3c12d6cc8a09b47bf23e2c02672b3b6f8716 35420f5080147d95319f2aacd975a561565aff5f68303e8d4bc23d238ae644067f28742abc4057286f2f99db1667b54d26e80d00ff2e669005a64addcbf86d0299cdd01a24c41db53960704bb6ad229f1ab23b233ab5c97b6a0faea7324e4a0e2f3a12a8e1671157ca25cbb81715712ce3f02b5823b1f90d2f2e7ba4dabeb10aa46aa1708278693f47abfb3e2224dbe28614357b0277aee6616eb205a294b80bec4b8f043ba2ce9ddfe62577f59d61ff88243e1c3e268933d9d936cc851dcd02fd0f33479be4adad1d4680c0ccc5d48e5d59d84e1baaa9ce954e7bd84dc2960586ad67ce112c5d07a63a749c6b86d5aa8bc6402782cec314bf304d6ac387b00e1b3e8a86704dcc12e7daced2903f8ea1e80ba8d352e7bf081ee6267986f84f09075052ce0dea9198e2d794bff54b31425250419f37fe808fa3953b9945583e0b4babcf383ded534847436d0c178b8f305cbc70176c67770f72a25e79836bd00ee55ff8c12a1ce0a85e5e625d5b2703b41f9d65c722a5ecd9e98039d4e94ec8066fed7c82894700f46acb85dc20ed574cc7d011821bcb4be8c96a0b6d2896d8005dc52a249fae65af3c90562c1a2772e8510d0737a9d8248b695860d9b4c2740e9bca389bf9e96a5d386f27e477d425637fc30d35e5509e00db116cab536e660bde3ccf50ac5f2ff6f09d31d11d467b00bb58a1a091255bb0406534963d53ef0a6ff97162ce188ec8f548a351ff3a40a0a95a932eb5ec3a7b8211ec734226050f790c1a59e740ff20dbbe2afd113b04e1f32a9c506953928000f9f42ccfb72d0244c4db4c67c00c14dff5b815da290af017e611cb85a56f11accffe8e2d725306588383f75398378c0ca1861f80d76558ffde1f434f2c306b8790996927d92101c1f7467c43c13693e3bde927d11e58b8321c2df78b6df0c1ee38247d8d756e0cdd8d374505085bae98753dd876a4c1933ee3b2ba74464546fb6347265fdf5b0f79b575714275cb42970fd2af0cbdbbdbfaba6ff7c659bae09950a562bebf3c0ee1b25d15ffa72308cc7ce943fa3e15a7e4c2089b764b3d270d00f3ae952230012bac18767c329f689d5292364a2152d886ec6c9d268372b0cc79392ccafff5044f9c8b8b22da213c35dcfff5782b2a357ed604f23f24cc43b60b1eaaaba4d30ae48322cdc483c76f0b54ae6e65d74712d9d382369975db2cb98a44a9220f500a16e49adb4452a35fe72931b3ed97e8f2061863ada6a6a0acbc7c9b2f9608fc000565e00dd05041abd521587318559ac07ccaf51dd82f57b488bc739408434700544ab1a4e367d8a450cad3d31560665a8edd000d886a0b1b497e68d1beb9bd05cd0d9fe98424990de1c4c49fbaae7c4dcaeec67fdf10c1c72bb990902730a00d7034826d576190ef08f48d08fb8eea309e67ff8dee8f8c2f3cded3d5a3e6f300f48ba2ac472822844686aca4b456d7fcfdeffc1b59dbe5f2dcaf137a96c1c208ccec42ad1e761d023577f99f8fc069779df8d9d531884e15dee509b65c2bb30380d3fb5d46d3462fc56992bf0e52edadee7bf29c8edc1c885aec7525ec2e8807cacd5f2b99fc24e6900045910ddd65620dac098c4ec80d1e22dea27755d0cd0144cbe829e009e208ec8ad878977d53958716e41f57f89d3e764836ec5a73cc0dd2a4503c39de6d1f37ca9eb6c799e84eb408d3297eb2987bb07e3189d9bf8b0e5c404325c9edba6815eb108b6ab2aef7d47cda6e878953c1e210d6295ffe9f0ed90d417c1c87ea3ef95e06e0ad57ac84c7453988b2cd14b33b4955eed5248c0c3ae945f349dca243dce4fb145d16f047f4ffefe50693780263dd1d13096ba706349c85c4e36cbfa79b096b598b6ecec6c074cc9e5af80178107b5b20ad7678022f046bb042809c5432eb3797bf09e85b2f20482e829c7c992f5402cfae2f2b01cdbd06c0590f4850f290b4ff7ebcb7f01bac4913dbcf5671ab368123f3248b0b598f3fb95401ddbb92fef1b9e5a03391574d62c83df1a74f91abdecb4873330c16d1b94d325def46a67a8e09991dddefd08a68a637bf948df08f452ba333970d27b9d9dcc8d941f7d90e609112831fe0c79bb3fbbd8d738182574ebfa798010f16ee4769e15c135301b3c4005491bbbd1835dc8b2cd5e0ddba44b8dc41799701ecc979ac0d6fe7e3ce82f8ab235f4877a843933d68e1e8aad2b7d2deeb5f8506963fddeadc84fa5fe03d04650c9c60f5c5492307942b96464e90d53368cbc8056819fccae2ca80063a4810a405734d5f04d6d8661f15b397a2ee8a165cc6e2085517a86b0328afe4e3bd4e10f8e38baca70213df0b3a0aa0388780a8e756cd060c0c3999713d5a4cb13078a704e065bef1c3ca5117e621bbe21cd014d5278f0152006f194b98d04e2283981742c20a15d46b42e4c579106b340a83b49fda2d0bcc0d816bb7bd9d515ae55a05e308f570bf52b5b1d1e24520355bfc96de1256028469f20206a97da8b0926f817ecd67a1cc5227e79ba078e7fe6821bffc6e14054ef730627397175840e58c700566cb715d7aec742235e66daa0910b05ce1990facc82f386bc7b198e45c356970d1f367ec59d0fc00fe01277d10edb5dd74e100 true -check_ring_signature 99241ba57ac233202368fbce828b04b12ced5d628bea7bb2f8a20b05a5dd41ea 45281a9144d2287a0d7c79c7ab13727362951b5dd23fd33d36d03a56f3bc7625 13 55023074a9b1699f134ad4e9a3461b655eac7e9924bb3ae4d59b128fe7543475 b76d64d68f28b977d7cf42a52c5e29a8ba22fd6190ce239f3955760451189191 da5f8719adfea8c04af0172ce19de986ae1bff5a481147a1df11f42381976ea3 fe451b7be4d9c65d0b5170fb44ec19271713877192190fc68832363f1f809f76 8de8dc32db7beb7b7286e89436499ef037bf6eb9cdae214d5811d5adc2df6475 a20c3192fc85511ea7d3474be92b56e83828973eb4dac9b08e4392f2bdd7e9c8 e16d90facb71b8b9fe303855036205a79d5f976d9df79f2a4a427828438f6ab8 8ebdc33bb51637c30ca8fc471ccd17df5218141f9cf13c3cae8af6e497cd6a6d 8a2e2bffbbb341069c90155e58830918fba4b40f98e9c1260cbb7de7a257eb38 c0bf013a2affd71d87be69795b2657e6124fd7bd08a0cc5277d54c3c0ead85be 3ce6e2b2a7fe2b258610aa6a0525afd017075ec942451319af167a0fa921ad33 126c49fcfe35477fc64d2f5ae47ecc043a2ed9f38b3e42e5fa063ccdbb1273d4 3fe9274f3f832b02987fc1299c467303cc7c387b8e06efc743c913ea95f4feff e278d155519f54b332c32d4d7a738733356d424899a3d4f2f05f6285e4696f0f72ee070f6aed2e541845ef2938667c0772d6c6f3da5bdd9d4a564d0ea2f311019e202e52d54a08f841544b28191e907dd6f8ec464e64f1e75034a58951b08c0b26933b5257c3241c2a5bd8d85ab63674c21d3be1ba27fa92f48a6becdf08cb0af2f531f9f974215d842d2ca2ad9ac1e51faa555cb33c71e3fa3fe09de82ea404905a8d3cd360aaeeba3b74cd3834fe4d8df0cf961a41167243514496b2612db817104174863033b6db05a8ab24da0a5ff3e5cb287ce2439eefb376b3d2db520041d2c0c53d8c18b9203ea76f6a49e1a7ce73e6482e21489c4e2e47a7b1667c0f64c876a95c51619acd12e507072583d981dcd9d1a977889d6f475f5140df1d07d294c4ef9969ab9c4da2b98d305a3d05fd4c60912a82b9d43d19baaffacf6a01e3375dad98667d8f8e6cb49597caa7fef6097c3694b2b31a88310bb4b4faab0b4f5abbae3091aec05ad9dec4fba67b38a98b7a1a148764d3b5d9dfabfd7e870f372a4bf62449bc69a3650d022aec74e28a46270289246753e0fdad6a1f13910c9d8b17dc13b6349550973d641bb981d0b01b8bfbb74449887e98c861db84bb0a4f3b3c89f8b577d3b26612f93baa09f189c3750631425edb607241b91cdcc2077f34340574d3a23d46bda0222e7eea1e62f79d70899b8b65e2edba812449030af77db38f5aa089f2a191261b0fc5403aac90498280f2c859d2c99c5bcb81e50b67139917d877f45fb494bda1733b2cb4853f3d47bb8bed73f25c2fc44ce7cd04cba8fe62cad55fc2646bdc9dce7acff28afed2f2ff150f2772bfb27bf18ec20082af3edd6467f4e46ba376d35279df97c629122c003f2231bc5dddb23554848856e60b95cc5ee77a87dd2a77beda83457dcc72737ea2b8b627c9afc8aab27105dec032810cd2c4232fcb1ddfde2148a7a42c5c26307eb1729c69cfee6eeb40002bc1944628fa4b9a9a9a6fc7613756ff97a6bf7bb8ffde4e669a65483c57e8012211c04c56a8d5f1589b10f232197cf8ff694e6a2f817869ca8395b0da15ca005adf70a0364e8e5668d66bf63c011ae0c8c3d46c3a3f5d8c0b70a6b16c43afa18e25a6a5f9308de17e8eaac22c480ff5dc00a5ecafbc5b91b13a20fa89a1d50e false -check_ring_signature 60bf34b9b10bc665b0dfb9d0f02c79443a52114012d4a261922ce240b571ff86 7ba36f941cde4c60fa0da348b24666e36a08e9655427cd5efb38028232ecdc44 215 e3565e7368a493bcb111832244d2fe57924def87b097836325f38f7402eafb53 f32e0a8297629279fe8da10b857e3cc87c8514788816958e8f33a44ede8c67cb e22bfb1b3eebb2a75b360004337d8009ee73ed63678970f688c27c6e38416f9c e0f52f5b299dce7d10108a595179502706dfc1b6246cbc3b1a122802621c79e8 717bff956f2cd663ae3551b943d5294c237b75250a6f8fc26072eed29d48cc3e 68093fa85d11673d941b444e038fcc51ad7c3895fa15e327472ac7fa64454a15 c9d1a8ae15baec7de76349fae244485636bddff1266438883540c02c5867afce 47bc3d6ffe1f0c7808e02f46f0064cb6c127159d55c70cb4ad8ba6cfd68ad9fc fe796a5d43643bdae990e81eb652a4573c6c9c4301f3afcbfac2f00a54c119a0 2214f996c3e8b981e6d0f9be9a4b356ec9e9f11363147985f9fb39a91edccbe9 56e2cea54db5d570f1cbfc8d7d1a3ce08c58f374234e67b3053300f5f0413264 7a7c2035c599764b3c4c084eeb329fced470a7288a48e7e20eeef4fa8bc21fed 9ffb80386938c069691004b6bfe355fd6b0e56b05e15f5afe4de4151a4ff30d4 ab15e971de0d8ae9bf4014981b45c8e2d9bacbe1fe4a2dcfbce429e7aa150d08 dc9e36a085cdfecf3165344e445764c7584224556ea6f3b20620be61e6c98e9b f2d81a6bdd055017d40ace415df24117a143b6bc10480164bf87930056f637d4 78601d07d894a09aa1380c03ce6ea369094463e957e50d9f4c46c4840bfb80e6 1dd66897a38a8cb8690971267eaad86d8f67e357e457a86c1a2af05c87a94954 a8333190900d36f5a87151c3b6eff9f38f9b22448c10d5841b86338953dd987a e327e2c93db8b5a44596686bd379fc4ce363d5f10298a9ea22d76a89f3c282cf b23ad9ef970d6c327893ba0471d9bc7b211eafaf51cc059db07b735ecf0088a2 2cafaa904d846902a5f9582275901f8ba504bb4925f50dfa4ff09264320bc107 1e45d21defa9a3d3fa7ced6d311904a975c0b7cb642220b3e3d8facef315a45c f1b2dd18c264c9cc4ef3735427eb7fcc96baefaee1ce3f880ea52a67a1a2420a ba5ba0f64747a34c3e5bbdff1b50dc8d3843ee427d0fcf41cfda37731b35d28c 954173dc649c82ff88aaf9f7290c50a0455d509c99002f49134cf73cf8cee45d acb606883462453c1cd7b1643bd12ee3fb752b4f0c7816ddf804199e75fc9c5b 89b16a80ac51aec8c7b6575421a72d246fb6803fbc90cf54d74daf2477b7a14a ef7393bc3c254b94fb14271b733a2e887ca4fc9f03ab59a045bd0cc0daecf646 24fea43b38aa47de70a59e097579865921689a701fe7ee2c026ea34cd597a232 5ad9953eecc0d74aa0c71eb17acbe985eaab4063a1c80affa205a682bde5b4b2 720dce3b70a0221f05b156a91ab2dd46b17c900c91c23e4c58c9c1b8ee15d675 fa55091b126848f76d3fdfdfc95145ec8514aa3eca2e9cc2c37f5d44900a1164 112310b07a78d07d3dd605174c353bac3cebace64c1478cafbb2a635fe7d5acb 61ced52b9c610cbf39db039b0b287fa850187f5941b83862c76465e8263f5f91 d1fe609883ea7ee65ae1451480543bfdc66abbb8e1bc49750e7b7291a1672a48 f84ff294879a8763d5c8d6e34b4719e1ef71482b1c42876971559f3fc9f37145 8cff2e8d521cb2273c93b7d73fbd74159bc0ce6c82c99e19a51ec6578aff71d5 1020426d8ed789fcafbcbb2336738a850b268afec6a1ea0a49368b577356b0b0 c799982c335970fe41e8593c407c85d492386f4dbb500dfd14221eac71ec0cad 3194f1c3984bd209533f8ab2349ff13fc06c0e2d2270896752697ed09110a2e6 c65e7174279c84cdb2a230693e40bba151dcf928d1e2f303d8f5295c08c815cb 076cfc984f58cb492672de50f6f9e91980866949dd9be46bcd157a8315730ab3 84a41fa0d974e8bbe5f06ef7450775ae1db39fd5a2f317cd5f3c20f39565ec53 7d2047d911e764da173da7ea18becbe456393b08a03f823448700a62f77d2190 d546246ed191f18074cdeccaed15ebeca08067a52147128e24794628b60dab2a ddcf02a4c514b5376072bb3ca4ebca422f211b8166b9d95a1d3a752e976cf550 c9adc886a027f9b7bec154a597940c583258257c0be973a400d49cdeb1142f99 25c4c5d38509e305e725f7055a9eb805a80b37a16d583fcea8c76df2ec442982 1f2005babfc9ebb1ed1bd39ddc1495c8d3cb366f0b21d8bd1b7c7bf062a66e9d 52e59cf20d4bd0816583471e66e849886cf815cc9c0157c94ff829b76ee72019 22456e561c12832b5678b3ca28bb9f8ce935097b38f16be379577a0c370c7d96 3c844c625ff2d9409fce385dff8d7ba9f8ff436b441d7e81b5f500ae883977e0 dc73d1eb4446e81c302c4921d62d2949ac9522bba172950686e254322ff09d7a aba31761c1391402c2fd7a14ea14e1bdec3dd348e0673ccf668a15d8a361a71b 841ec7af2d31ad5f167994e76c3e54ee87316dd9ed177d7179e44dc16e4c92d2 e8f0632a9504ed38608460aa92c510d316a62f660c46c1895b4c1ee69dd2768e d70fc38f44ca218cfa862e5de8516c241769227b1d74ad4abec6e3fed6fccab5 3bf4a1560aaa9014f45251951753a996ec505f9a58cb39399511a86b19771fec dd7f9bdc13ef2b1aac7234ff41079c1703443909d80eebb5191bda5f7e4e0a59 8b728ec5bd8b71b3e5ab148b94ce88bcbe0ff182d4471d7dad96330368714807 061e5cb0b5af74a3f43b23cf663156102dd70e03c748548bfb17d5a01126b8be e65ec4ebc61d7f66a97e2cef83bc492f3aa2cda7e735bbc2fd97be9ebd36ea06 917a14426debb5b777ca4e580f4e492a896044402674904843d8e73b765b9812 a05a4f39e260d7149f4f592b515a61612ed0cde76fda446c970d5d6368326c52 a50ec9239247b10378d3e9953bba20b3b4d99a2cbc6eddab6baab1a84cf7d7a6 6596bd0960be3df9752cc64db501449d1e45575e67c5641c16e3d1c94b381870 a50420874f7a0f320b217c03878633c1f5eeb942689462b3551b6ecf4749cad1 f55970fb40f37d65a80abe7ed1180a7d0f7ee3208b6d8f1cbb68c6a3e87bff3d 343f514f795de85be5aa7529de01630494e12b123a3347d16fb00b19ac0124cf 4b28a2cd45b66974652b95c775968ea4f4090cd863007ea77ea8e325eab5d082 ef250a1e20091f8f508bb8978bed0fc8b8ff8bf81a04475632c6c42559c5b230 1386761bfc7462df5d9b123149392e63587e9d4321df13acd43bfa3cfbc0a3c0 e4ae62533cf9e6e442953306d605e7445318307e822fdff9262b249cdd700b71 ee23b6cb3cd161c50041c384c95e45b523ec1bdaa4cba031b30988c1c9d8bd7e 619ac0773f0073620a783fa458db34be562e6fc3ae3100011c275d4e76a03fd1 68f43ef4f44d112b1bf21bb24b02f51f70cccd0418e6b86efcde10b1e5cbd4e7 fffb005a0a3924b70f349d477b83ccf85b2f85f05b9145e40bb05ea93a8c296e f930ad8e323293dff6155bc4fd8ab399c275651a259182c9e932e6f97a039c02 2daeb9b386fb46d1da9524503cdadfb7f1dc1a720679718343e7664648922e65 836e9f45f2c91f6871852343d2be6b3ccd5ec2f3eee8f3ae0b6116c5160f2791 f85a70778f75d7bbb42d37f6668c55c58395d2513e9dc3f17233973724e174c0 2a3369483045f25dd4996bfd7ad9bdf801c72e513efbaf03e85c6b9f395c15bf a9d826147f9d386bbc34b917cb04db2caf836abe2991fe274da06102b280d63e 8c27847eba5728f488f9dc5fb1860aca419cb6eaf019ab8b3264e332e5e99cf7 38040518e4ccc916d7ffe7836544dd017780e73f201c29f13a6e6bdcb36e2721 0216b0817c4a2068762600b0dd76d2132f66f5efb506a60f1c37e875ddc56a17 979bef659b60a0e2f2f6f2562ebdb694b79aa93cebd603b36c0bb64f7c03d74f dea307afe00734983d2974fb767138e615f43170e05a62cca693861e8b7e493e 5fe7c2530891daedb91690704d52248258fcf8aa071d8b9fedae1aebb3d3aefe 137c4743540a824598e120873fe0883d642800e08c1429c6d75d76ad8451eb66 4c0cb0ac926b68259502a9213d788b85203bf9096cada80dec0bd4f873805dab 89ad7b5c96383aa2d76a553c8bdf67022164218d0d138650263e059f87f4bf88 7307339426dd034376c1b4dd879a663f0234ac280a2bbd5b4a34aa5a07903e6a 2037b793998e081ebd6a669bcb0a8d4b55a52a4bcd2589f2ce0006b401b3826e 9693e96307ca065634a402097e25e7fbf471e3dd6ce954a9c03f207e65035f59 38070d7f64d8fdc28356a9eabe8e90b652d505877ed640d9298ad0b8b7ba30fd 56d522b4a213da3ea77e069d2daa1e9719b26cee93eedd01a8d059ee485dbfa4 d4c425f8fd725787924d6c503116ee14e21251b486fc3b4819825c0fd39720e5 4e5505181757443dfef73c8e953dd2452d997dcb1f391cf88937bede98a73a50 54c2a4d7da5c8385a7e08572255a585ba6e2a834d1d345b9adb7481db4dca443 def08a7341949b0f621ba48e006872adad1ca514a3f9f0cbc9b48fd081165313 263ece8ab95b2eae878847399e50c27eed2ac9e8f893a152c3f9f53230fcaf55 4efe727bcdf8818064726136e5305e9ddad16b86833da2c270a749d0d7e977bf e3b7732fb24fca2d0da5d4484351d7efbe21315c1d3f07070cd94c0941b3982e c0c8efe5724ca8626565d3f03d4045c83f38c14c19445da524548160206aec0c 91cb1b611ae3eaf1e3e8536a2849e567d473d838fa77069d62ed0eb4685211f1 e33c31d5787c2710f709aaed219f22cf9ca28465a569e3750db2a91543d06c72 44b82406b27119d29cfe5791a4ddeff6c690696a6406fe31c60f52f07a8d1238 af56bac8d1dd22083d8d3fa32676b561d49cd2506111cecf6ed17d0d5ae3f296 98bed9371414a80ac90d5500f3870bbc42037896552efc1ae6ac63aaf4522715 ec1ca0c353f2851da63b71041126f4caa0586ced7f9f05ff438839c914901f24 2c4aba5d6ef5a74d4904e7781de7b75063bd186834246734e658edb5ba375919 cd74c3a74ff0e3999c24349cf837a0c7a036a88f38afb9c677553004ffbb15f6 8f25c86c0009b97b3345ac8eb9b169e11234c7ae0128fc11614a7025e9cae181 d688d5b5b2347699bf7275cb01b57c6b98b28a9f62d665b6881f2ecf101ebf81 c3a95ca5b728722c72c217fc91fc1d7573dc77246552ad4c02937a30ffa88b8f 037da9408287563c81e67a70656cbefa9e2d9d42fe4affaf0d82db5fdee9eb6e 17d4656084d31e3205e41c9e607bfa350da11a4f8225b7d5d54d8a0590f65775 df33421d91661ec5beced35718b5821ec99125d4ba4d2b813b7c6f57bd5c2f2f 8492c2cfe752a2d96d99672730dfb43d78a25017a147fc346205d8fe83486906 b6a3e138202b1b9612a0bfded3252234253dddc2a3f7b52e192e375ac662b053 ff2a024151e8871aabb40a2f491cd52a6d98fafedc13b82eda23e659805eb675 e7ed7daa0dbc87e08ffdb0e23e032767d0ba51f35003ce3a6b302be5cae3f793 d72532af99fffb6fe082b6ba64a704a28180b7a20ccd01f2fd6dc5e8b0fe310a 7f11f422f02d645125a577f6f37410ea77f80c8e5dd5f11eb773931e59ae5f8f 2e6179c03b58bdbeda3b19196fdc23a5cfb93be5c4b4870faa4ec34db3554594 52adf0502c35a9d97ef876baeb45b946759a802216187dbfe5a99bdbf8b54f21 619425972f24b594d9c011337ceabf3e6d1b016fc95b2ca2b9df2989996a39f0 bd5b1ea99617f0e30a156a46321ed6dc4b8311c20439b8bc86681e1e18725578 a168f66430aa1d6f7cd45f8b430d7bb052fcffe746204832bf5b1256a65a2806 7c401ae2137d83aa538c30724a23a41942c47564d6c2b263a4468fbd3a3df0c3 daf932ba2d244fd5d9e88b3b2206984a3fd63c13698c2253a9d51e4059547276 ea21ec331dc70b2c54f5b3ad6f692cc63189490b7d20d87b407a4a1f59cddbe0 dfe09b86f25bbce974fe4e0ca2694b04dc894455d9ae50f2cb7e653d79cd9aa2 0a9513c7ed9df0ad556c1144cb00ad9978aba45c7d76dbfa43b70a30b5add8fc 02cb815fa6b6863592e80c8e96f00b04a016c73bf4da0f9f11625aac44084c4b 14cb4ef10e138f3f0094f392a889cb45709c777bc4c9ecc249eb6aea59894b57 b9e0517fccfb47bba7a9e66f1dc60e6b0662ee75c6e1c7010dcc694ef7514133 6606da7f8bbc4a67d9e828947becea2ae3c886b7f2a17c843f065f092edcff66 ba27b9514b3cea67ae62d64e1909a26ff326c8f662b5734f6c06a4f2a0b487c4 d37216c8c988e2fe78b6d09a8dc240135d653f507c11a75df7944b04a639473e 02e524b9609e1fab314920ee426a573457973711b1b21337af7961d5b9816204 4ecdf3d9f52331a4daa503485037a1823d5ec1234f45baa95e4ec82176f67630 61134b60a245251d3f64146a225f328290e69e1eed17bd1e91208e09ea840083 edd32b4d67a861360312f8d27ad955447525838d55e3cca7522408c07cb827d1 cf9152a239b5b5667e39d931e42b1a72cf7d075d3984425d443276d3f334354a 95ab50fac531b24569eefcb87eb09663848d44ce237b93acf3f0243783d5eb6b c386227928436cdfcc9b0b899099dbcf4e6e1feaf6c860cc6852c18aa0fb45ae ce8314f08bafe663eb426e3a4270d4df713724d34d746045f4ac0c2ed088ae59 1e8875b42af2b58343be3233e83ad1907921c46e55ca4062e137f6068bdb241d 703739282202be5096e2a26b6cc593c8aa225dfd78e56a7efa87ac60d421c78a 69fb89151602f95a931f3e380d138bd941778586c118a2a13f3183e291d99c70 4959a4d03b793efb5ae8683bde57fbdeb5aa7e30b2d4328cf12d8d8db986c5b7 9e69571c013cb4cc718081756fd1151ffd2882dbc66115630fba60dfd6cb63dc cb917c129dc87a571fa1e5b29c3bd633b424c6c91ab140c683b2a21f0a205498 eef2e0f4d5d7109a073017129a1614199cc4849c82a783a7a772f4ea1f08303e c400191db500e9545c65f12f1a617e936f85a502bad9a512dcdacb2f145c09cd d1874693dd27b5e1ace07e32009e4a77e5b11493a172c471cd18ca4437c43b98 3a6d0542204587d81a70b7b3c1cb7bc871aaed02a684d021a40c98449cdf4dbf ee488b701a6e666cd00b390c8b7b86e61862ac80aec07c33e5115963b9a5df9c 3dd78c8f3bbe59e14e2eb6694c81d6770162ea86a2bdfd4aade4a048241192b7 3e4aaca9947b150c6a681731babf181d00ea045f6e11a9c9a1ba9c066f193bd3 844087e62be61c1b261f842ee1ab199890199f572c6510d13e6d13bcf3ec674f 5e106842887396a7a9ebfefa05f19d81b29294340d507ec84aad620d92def77c cc6a51e0c8d9b62f3cbeeb11e16c56cf1c539478eaeb0ca18a7b72c258f2fd34 eb5b9857ff7a848269a964f76e3edf10822c12654b69eda5524ca3cc8747fa0f 0a6eee07564c983f87b9994da254024f07712e1659ad170b3c35e0106aefdb1e 6998b45a864a94d1356a091f4317b27490bd2863fd9bf6b7ede46b42ebaa3703 3dfc2fe1547a58dd9d37a2080124fae7c1c723f41db49b09c1cf2ea824ad988a 79f5e689f799766a1173186bc51e44fa703768ec7e04d996a6383ce3785a166e d7b045685c685a2cd6369e8762dc74531926fdd082a02a3e0255d1b825b9b01c e9a93c60a626d29ab8975e34b4c61c592489751b455c213650cf1a379d1c3244 b2d29bba0b53adaee43a36ebd0c05aad574f5b767910fe761c4fc445694ecc53 e606cd78aa70cf493203d30b23f48a55b6c0f9d246e36fa6b213ff69318e9b79 3fdf3f0f7f09618b088142936f54f3f65b0f734785b221e3beb5a68a9f0b113d 822c31b1d947ce2cfe41601706dbf96290e07fb761d85986a7d960c2954453c6 80307ef4cdcac6e725a415901915ea3faf48278ec1b0d7ccc0d5a5a9a7be1520 e49b84387494245fe8280f98376539765d9ac83e85acffd8af297b925c9c3397 b99bd5378c0880ffcd17c423b2437a1f3ea8938c329c16e79c6b9eb7ca2b2182 2bec864c8af402a09a5133140e4e76a9bdad14fd2df62f7558a61b197da5a851 2ffb3f1a0cbd719c42094d15ed9f7f85f5d7ab93aa234acabc8e3174a640acd7 87a2b6f58667d7fce21c5033c492230f9bc8436c7acc6b9c63748fb6e1bdffb8 a6f6a285be2b16673ad01684dfde3aff272d8a50b21024f13a26046ed1b45b44 6e5fbdfb6d2e5512afc5c79bae92f117da4c6b91be3c6fcc3a352de41208af8a 4c656d8c64119e8542c435be10a6db984bb2e4d6471100507f14771fd4b828b3 a4dff87511cc71ab26b30703ccbad831f57131fd12ec28bd42c45150f3ec42fa 55f0dee0dff415434925c535e42ee6a74886a9502b2f1c79c09468fac8ccb42b a1d4cee3d707fcf9c074b05e890103ad3cbea4b4df687f1c921204e44ddf64bc 21d6a04c18196d452679ad0023d2993b548213fd67b080e303e40d9859c96c2a ca970e5eebb9f1eaa106da404d361936347d0713d25bbe14983867fb54825fea 217dcdb105747d748f5f98f44427c2c6190a68fe6beb5aeac9485fc587c663a8 c4fcb4f7d7533b698a0d7268aae30843f163a0442f988822fc358ffee7e12fed 09bfe3c6b57dc0699dbe4ea4b27bf4714841342b821b55ca43e9a14438bed39a 3674f1e3149f0a6d34918ec91303a94ab3bafd2969f343629df860aac5df3da2 fb4efa7cd374025148d2d5ab9fef1ccb90121efeea76dd1f2fe8591c4dee79e7 a52340838a694ddf04bef80a2840e31fcf16e4f5d1f4e98d140c6d33090272cb 6f8c7c3f1062fc480790555581791f170c5bab76b5527943ccfff9828899ff8f 0654cebe337674dde5c6efd0d3297dad299fa1847f563f8be94a8fb6e53792b2 8936584dd6727cd2dc20cdca377a73b608b84c580c16b95dc6343cb3ac0c2a81 e09c2d7dd0b982bfb5ffaa8c172027f4430693322ddf3a35d4da7660a96f77ee ccaca54aba7647523db86df9396767e721ada59fc1c7f8e489e6d58900ed6079 84a740db5f35616513567071d64548c2f98c1d6a49c3bde302828ce67a838d75 90a81cee2507a5fcc5fe69df7c3f5bf5cf00c9ac270f95c9bac7b799d0bd06c1 d0fc295d77346ab9787e21e5830941eb82a05c454d59b432c3c171ff69931ddd a8425b38a87eacb0f15bcd1bdf767d9c00b9179bc8cfeae87c4ed7679e3f61fc 996a0c2ec1089a573c5538b05c0ce95fef724cbad8c2a8efa0b06eeab4446597 de3c70ee61da2f3da2d002eb5b6701c6ba2452d17f693c36dd87a085f7f93f07 2fab43d3b863660f3831af35b28dfce41a9ea2a0c15fcdf1288a0f074c59376a 6fa0f1531f1e4b90d955dd4f35a208b6a79cf9eb85fffefd31b86319707d2670 e155e1df7a59d3552162748a67b251bc3d6d628f1f8d45d8218f6e90192c9e6d 334a59b71b100d087f2f72723d530ee5f7a13401c6693b458c7a18731f0671b7 e8e3e9ba4a711fec793073146a5157a703a908f1e1277f7f0fcd13eb8af8a37b 399de329f1782136f76c07e187a1d4dd3290137c91d72646a0a1ac41eb0b36fd d966e9433df14c43e3e828e0bee00d99f36406717ae6f6d4d8379e305f901c69 05c7261897869e40be8b7a0e8e445064de0a922ab77482f031ae45fcddd87d018aab65dd915913926c0c605bf2b0c6fcdcc6fd334db580bd0165e7e5bf998e01369a205fd7d66c49df62b3ea8c99080f33b636a62c4c41fe4c27597f961b5307e19b8e13d6d8abd12d16e40a0b7c9f1ea3a35a9de2c3c148e6ca0c101c0fea048073ef60d624a79b3077df0d8efe96c179c73ec610f200af6c65728f91a47205041bc111eb00ba7039b71620342a91e9bb0f01469f5fa77d59e9b62cf122e50d88ea74aef50a2e4695079a58d8adca6f841589c239641e0ef0503684e60c340f9148bcd5ec21a0bcee960ba6e781bf890fe03bdbae09093892ca48f03bf06500ff93ae9b0b749d1f342ff8e5c282c12107e6da17eacfa02ae412331b1c27c50b0cc6198d87ae3231b8f967bc148cd6b6de1c606c1aa42ce20c436cd3adf3f90d0f13adfce3a4101989ca0a48733b80af386cdc2142b034dc1c196c4951082d02b8272fc250e53af78d801399367bd10b8f95d531c3a5bdd9dc66d81ea6d96207a74b1829243284dc885c1ab4f0cc0f1b6cbe6be166bf8a433ebf89c1cd896d05f0b2b74f06919e856e47ccd4a199c2d02e2856920bbdb96cf912e54b79497e0295c8a45d5a2bddd41530dce713978ef0cb07c5cc2d809bacb2845c95a734a30752f7e95b0340c3bd8a19e0bbe27a8d717dd32e19c067acee4646badc74537b0ac6d16a5d52a3f3a1030306e9bde6962791880b360a147ca3714364cb6ccb2e0dffadef4410a7307e0f8984f868786c0800fa48a93fecf79695b741de55565509c1d85afcb61359a96a05c2cabff438740dd1ca390239e82d45c44f967dd344039f188edcda6f2b2eeab4b61c05893f5b95d688280f641cbebd3392f6546fbc061261d274109701325437065b73a3d0ceaefab04232b59262cbda8946d808870148612b282e9a338a1cb7b6990f85cb3c302cf4b0dcfdbcd18613b81a065bd001c381717c49b275de06182d172c6244a1c8e8904930c60a8d0c803c05e3fe1001f54452a22aaaba31f173b477bd0ff503dc3f7c755f3205587e3312838ea219095e78d21dfde258def2ebee51eb21745872feb05673cdf98c3c7166f897174607f784bf0b7c4f92d8c0a56c8d5c7450f9e449d8e2645d1883deb6bcc0e5d5790b7724fb9c7c9696695afd688ce98f0b2b7fde40778890cf408b221a750cc47b0ce8caa68810e5e4d5811ca6886851ea280f9a32664735e5c6479354d311fce201bbb32939ad13e92de2c706891801f85e999df0dbdb781f1da76b3a3533077903737cbec7069362b2a7a43162e3b689606e541d3de0bf66da1be4c59c16465b09e4a631cedb06d3be8cfe161ff566c7d3e88dc0cb9bb110dde25461cfcc5dc7036c5bb19f9a7d3ba689292e8a4baa7266f4cfed8bf62fd7e9ecda9f09c0912e08d2d085d7a8b81211bfe7e89949abc608222c443388f20fb41c1ffe968af7e20362ea148864b00615987ed1b1650063c9e354427e197c3d7aa20d860f8a2cbe0270e1f11dcb935aec964320ec3eefc4ef9975219b54d4e549b0c89843fce75d0f425003535760fcffb59e883a8076978b48562749502913deaeece5baf49ed60981eabbda9b7cf07ede0958d300526afe33a5ca63011acc761fdaabb7afd5c00218b4d0b30e76f68ef948bb968ff200011a232a05fbb7840339e46ddadc9fa70a3754d96ca9e7deaeeb341fc52124c03995fb9ddf66cabc164519896940ea900ed4b620b16f6bb654d48a1595f606eb109b300e66020071590288a3833a804806e3c30356ef40d88379cdf4655ddeb1a00b39ebf08f1362efea76cb0a5a041c0b79e5dfbc7a63c978f7885da30be4f6874518db3d921df557b15821d71622ac0f4d5539215a40560708e27b983ff8673b96d8e9c401b533a6d31a23a876f21a050c251518967127cb829c22e7e52ab53dd658cc301e1fa7722319ea09cf6ee90faee04e07afb2518929649a369cae20d7d3844de4213fd5e053b90c053307b00b86a9399d70f4317b672ec5e62b488a11343902a9a3095ffd4b91a63243756a0a038c9116e6b0a37482787c166f7d1bd4b21c994bdbfd78abf5ca9eb3cffeb90bb05bd8e32be0d5712f310c59b868213c33598857d47832cf667ebd986049630712f6fa9f4ac36a451d7a453782ca604e223a2fd77a823a8da910f39b0ecaa40cfbad4ed7e3212d10aecef10d19f628fc2d04a0a06bfd2d603109191605f705097df0b8e94c1c50ef6842bf50afa4719db8802f073e5d5c6d74fa7bf785b3e40e933bcbd60c302500420522625615805252804ba5c2e9e37b6956f446febab40e180530747c944a491488e2b4a0064ee0598515072690c34c2ef423dece56330256a2ef107b31e46d63823dbfed9de3328b66ef2a4ad640aa4b63ff5bea2ec50f31ada17e5b4a95b49a690d2ce0bf583ca3c0f583a7c0eac7578c151043a62a02254498dc9d43b9adc618aa9e1c630209d7dd2872cdde7fad632832a581719b03f1856874387a0286e360d6bc195ae8430d9b8b4aae048bf2893f9c4b4df4600ebd0df73a278fefc3192807a0cfe7c0ca2e3b253552c7d6855c0f3a5ca789a0057941a3ea48616cb815bca74dac7a396562aaa55bcffd922d44bfdcb98623f70baa1d7c8f509e5fc10a13e94e242fccb619d82bf373e52e657c9d3b804843b402cba428d6110e322c1bf61bc81606370104e9609b2a8f33ec5759f86ba1c988020323ef129381cd631dd4bfdb28e041c8f61b748146223cdb45e3a22487663807363b38126c778dab875c9b11897ac24bd72b5fd726979674ca1afdcbc3f3120e28bbb8def6e387f6a5f2ef5b0946716673af415b1e486911c45e4562fe3ee301195774bf0fad987b79a508d3069123db5a4c94240e795b0869d2779f5811e203fa89f633c3516cdebe8556ad03b6f473d9bae77ecc4fa112ad1fc56d32bf730a0fb01b53f2c4e45789bf3cafa070d135dcd5938cbbff42d21ea85d797bc48c06217f552a03e55085055e62fcbb4591ab44051ab6b176065515326e3fd21398080ad1cb1c772c6cdbe899d1d65f6045dc775d23197a4cb4797c42fd4fbebd9f0cf0e7508257d63b885f9a55ae774ce10d14f58da2ef69812cfaca6173bc57aa06363ad072e2fea0aabaee5dc344b02c2421e7f1b7667c937c404d3903cae7a10793d60bb6d1adb0ce5b8c52bcd897e0920d4fc22b78504c4f394dd4c1ea12a703bec983c1ec6d5796e23ed2b7c97a897cf6685b87134731c8f0991c27fb011e0d624cbb30d27a719ccd4c82e17df093a887b05a80a21fb6499405c343d740f208faba9cc05e39aefa83a146ca13367840dd9a3dd197b80be0f42e5318313893083edbbf38f64db229c04f618ead09692d143a79c9f4a17ae89da44e8dcc795e0b40a7d07ef8549a5109d0d4832dbd2d0e43ff4fc41210c5d4657b429d8982fb0930866f856642b6b4e88c501a37a8a427cfedcc3b6d6866d3dbf21dc80b0b3101c2b128b1ef2015a46f5950e0857357779357bdbfadb2ca17399e68a5186ef603a001793b9f91846ad80dfa36da35bc9c46c2d8801bcbd69a751f2e17a1041500a908fee134d3111d0dce560edad0faf472ec098d865f18c0d1fdb1830c68780530cefea68d71ad288baa25238eaa1d9148c9bc78a3fcbfd1849bfb85602046081163b46335411223208e15e6eedf3ec8f9ca293d36eb143d2ba6320971c6550b33bf85d4551b71055f5bfe2d9e66a5ee0b8c17d8b6369c8df690663e06263b0875fbd700022dde146ee9f703ec14fff43d3540ddc1c01928e02fb79913c96b069258c1a35d2709585509f023f2ebbf96dda936d75454b88a41da2aff5916c109b7326ca14b031adc812627e21b4b1081b6a8c04e98121fa224171a88e53814057d2a0d02cc34fb11a96503761b88a984932b88a3978fbc3391e8dc065ce95c0cf45aec0ae717eb103bab87a8bef3a51bd6cc88d287b4a364b7c815b1694d670429c2b4779ceeb0bf21f52db4b1904168819ece714424da661191e52b5e01c907b3048ff9380812dea345262537f1212000cd31dee2edb5518319ce77cea31c0bc368dc868fb6f574cc9bcfaa277b695ee0da667421c8f113e6c43aab031fd50a37a0c2b81a2dcfb97e1a286d3702efe58049858b032d654abef36c4b7aaab80b51673d17a655dbbb6f5714ade4bf3974312b1e17ca27f910b23435c3d9ca5e05575a35bd14fe5db26bcf948b1ed8cc631c2c7bcc7e6f24d306ca2d0b61365507c5dbf7df82c06852668bd6cbdf9bf4841f6b46fef700c98321c8f22071a620035b27fb56f1e0bccca22447e20c2a267d8da40dce27ed2b0c84e618453c430c05469adaad644e2a39a3575f9e80c2529e7c2e5d5d7b37bc1f33c2f9fa2caa9d08fa9bd5073600073e832ab1508dffde661ae13546cf4f65f68b3de46fd6ec560464bc846f9591dcd560d9cc75e815e5a2955cb6630331bb75ffed3f3c91ccbe05cf9fcf5088de4c3cced1d51c150310e5d429530b9e1539087f874d349e092b046647d850198305156ab2bc164ebd1b3398fafa536b046c7ad3159fade40bb40215b6e5e9f53d2fcc9660d334722a35581b34e6a354c9684178aad3a585529c07ab620a906b808aa1a8cb4acff7106c787a7a8fbdac1b5db1743f39d7019d3803f317a79df7568825f924643caa3179f3fb6e70363a84e6150ee31fd22e120509ffff81f892173f3b6f649b315e7f8fb50a9d3cdc07dc23cf69eb39bef4108d040f1fc3cf8eda62880ccc4dcf17bd1709e012a033d40182a390c450d27412320bf42cabdb084a77509b8819a2576452ef34d790f0fa7ca35559385e722a36ed0701613e09cb07d3314d882b465867047ed7f84c292aa7729273d82eb716ae1d021376f5b39d9cc3399c69a19262cd8d9520db64eb75ba1d40d4c62118b498fb0e69cc4671a067d824df3221053f4e252d6c60fa1815185d61b9b2699a59e9f304b81c0a8f871ae2941ff466eec291f88df3ede3758acee8375f5daebb1055fb007014bf63b886e17e8349db7bff72c86c975af362e3ca2b03425eefd663e71b0113fe86809b416a2e598445b5d6a7b713ffbf714a19390a7d6eec2fc9727de10dc21cf99cbc9e4e54b22f2f961fbdd945f09a9ad33cc1b0be94d0e13bc632d806e4be5b0f25f8a468f2d952698320001ba501acc84d8215afeb887fd1f3118a05cc7b0a89fcee3ce3307eae69e6eb761f3e16ece4e2b22e1fced783b7a9d40e0414d34f969958a1b1d3d488907aa829ad82b2f5576ab31b46d474f5d090b99904a721cdab6d0eaf3cd8bc14cc8f3d3198d516d87a2785ea606b884a20a769310668b7c761288283979b37a482dda2012645eeae4c9f0f23cb07b5594e0c8ce409c414f89379740bc10eefac31b9484f652d63179b1bfed809ccdc726cbd39aa0494ad1d5989d1d69922f66ab3442031b8c4e4e9f5a096749289faca2c56a53009cae87ef17d33ea61f33e4fdcb220506a7654ae90b4aee963ad6b7b4912e7bc09cc37add9a4ff5c806e1985869e26774d9abecde0c0df00e04889858dcc2d0a05e58b7fc04cb5a258fde98ce845c557bc1cc92ce3a61b8595c7f5a4dc2841bf05bd67d6c804cc2cf2482723c02aeb9428d29973b55bf994d36f6bddf1b8140b0d148fc6fb6952f6891224b9ed9af829fbddd62d9d3625adbcba5b337871bd1d0d5ca5afe8066c90f61af83886dc10caa990187bb5e2a8a068945ae5e41f98d00f70c27428be5cad9d0131b8e305895c534cc10e1a3f4d7fb35ea897925045f20302689e871bab4cd0859419ced2fe69150151ade8ceaaad9322b8c7a1c2e1890446382cc07b5029ce3102f947b1af4a940a800451adaa19c64d0789c1035dc3026dfbf896a648eb1325e15851899b88e477fea0e8f5917b3afa50600c63cbe30241bd4e32a80204a281b4787cdb7b015af8459f32ae79e75aeb1d7656a65b440dde4351caf8c3d044ade966f69a98efa7e20a8b81ff5f3d61d0659e744b612e07d2c31f0c738c4d8831817c545beb5e1d8bb64ba680d89269bf109fa1ae22340bd6cdeee55789ded295af32cf63bd3023a741ed1a118480f217e1c347c5f6b207acd9a8445ca42fdf5f61de21fb0eca0f524f849e1286166bb6698f4b84d36000a72f63fc83b3c45968960415bc087ca0adeea7577ec03a6bf65964018b79fc0fbe2c6dcfad6c5b698bddeab08ffd0e7c7ecbd9a57a49b889aca64f28a5479f055b403c70810d7a51b68c5d0e0e2d683ad4420d9d2a4d004eeb17d98f99e34d0de535342b855da3254a3e8c2b68d90610dd38d556b5ebb0b4a63d63191d55400d5e0981f12b4ff1d4c2fbbde6a3e314551733f2de3e62fac9769fb40bd1eb2e0650610caa3a485332be37c588385ce74fc3ff4316702ab1e0955fcccfe6bca20549effe3567d12a02bc047c84afbbbf79eab4cc6499cbdc940123586a0ebd0a0004068d2c52fecb352854f52b175f694c8dafb90c162eca2ef744e3c9142cd40cfb7361903c19a3ce5a07c1d6600d7504445d6d34bcaee574529212fc0d4898089e4574efe548d37b75140f6106db37dca4863d5c360d34df84fe5d8552186c01b67a0b21fcbf40fedf0293c1f3d1882e5bc83f7126050b14a00556111085e50e859bf0503e25b377c7411c5a8b8f156460ad96c29b2d386d85b252ecdf880b032761f3538a8fe1a4f74e21795856c8fdccbb845997639ab4ae0b65966762fd0b628117422aa26238228203237cde688f3828937e09cf013665f76d4cc7e8f3035c4fa11b1c4a4cc4be4bdd06fa79702f24fbb3e4c5cb206a4e3ac0c1c755f2032e62adce018911b60d48265783580787403e998e16838cc47df51ab7e6f70c0ec8ac7e2eb7ac0c91f7a3020e6a89bf964cb84d9b9024df08ec3c6db975b02f0a9b7cb4cb9ffd70bc709ae26385c3b6982c34fd712bd77238d075739e891e670e15a59e83398c834550c14aae1f6b1942c7d7943bceec089242358878c65a470890d79e4417738e796db0dac2f04bf2938c2c63b8f2838ea73aa3d92c8e18f70306a21c217c7c74887c6d6c7041668a3721087621c8347fbb17a8757e876a660129b8ebba0d5b5e14f2667e0d4cc0f8bef1b7d566ac9c2982a5f9f7d1d2530d02cf9f7f7d486a4e333113dbd2c98cee4595d7b75fd71e62c1138b5d9b9db770006604f14677176088dc6b90f9836508a7874e0104d9a7984af282dad0d45f1a065dd29819627d15357b3993064ec767b27544dead1f739b694959c49fbc787f0f9c098dfd9822850e037a596abf1e283b5503e369e1c620396a41e0471418d70b9afaa4e638b20f135103c29fd08d1977d3bbb7be916e780e37452bb379fbec04d6f89c166ff3c8f325f738c29e701f90d033109cc42e10e20f681a69d4e31301e81b5ea5b88d38af32137eaba9cb3efe21d0be4ed6e1d81254c9b008b4a4ea0730ec59ccb9dd09a306033c424eb47aaa6dfd939c9d5687e3f23ad057c9cede074de56dc5906ac7ddeea19c069be08020d78bcef0213442170b20b78718c3da018a18a539944fa50a155395bce4298a447371a5dae3c2f4a8490b323fe1266e0a04bdd74c8788155afcbca72c6333c8778dab4bbbe5caba84ae2ec5d83f5812088f3270dd4f1f1d90694a4cb760d259b4b02d257ce8d5f37983f51abf91f4fd0e6c8d233b79b885614d20dedd61faca3304bec895a0a5c4e500c8fd81035d84090f8bbe839eae741348a20035ffbead2ab4bed63d309c18c07bd6a9fef9d8060b62961d306900512c44d6158def911cf4717e5827f930ac2a310100c5d3f7a10d2698642598ffb94b0374b29c7a4bd066adf9ebe08f8e2da6b23c94fdde76a1014379646d7bf8357882ab854c1408494a536bc24b7a794f1257221c4402cbfc09983b1206d86b44ff54a7f98e0c7a241b116c2f47c9f7421944507f342b215d0cefe0ae738aa10a53b96782f634f6f8da15dae0c1fbfa07e5dad345964155b80f30435cecafcf1ccda67dd1772633db8481557fa2cf64dfd43972dd477291db0c6b9386e96905d6623bcc2fb818bc89dbaa02fcc15a5a98d199ef6097679117076ba10d43b8dba105bfea85f0d1a79e76f6217866154def91a3cd6fb7390c2a0989e45a811a9bb5d7b5761f2e7603733ebcda4d27bc3531952915f2c370b78f0c3a0ccd4d72698bd5bc51b644f9efcbacf127bcfa6c6760bebcb112fd0a03710770bab6a7a7fe559edf2b2e45ecc25f56a3697cbd510bc47b65002a756b6bc20c70357e86c5283a5a2718314e24bbb6bf141ae48802207de775afa4d39b22300e58930412f21df215016b1b269f5149f6af1ee37f065cdb7e421f1dd39a79490b5ec35143cc9039303fa3bbc640616360113fdb493b95c77c75b0ce0c1ceb0f0ad3540fba9305c1f27ce1e301654d7eaf17b4c65b339a89d74914e5e302ca170435caf6f2e3a9300784cb2f3b3886b5d5014f14c92cb58095f670ae7981a6790732fea2f6b8aa21e5211faae6e10d7053a35238e7d17e04bac27d63b75db981096617fe6ef05fdd19786e0d434fbb25de552dd4bd7ee5a1de8cc4cf434e11130c2eff0814d52006e4854f8bb55c1a317e9d243a236c7dc4245f7ad858096c5c0634916c54c814758d8e12d39ec9f0e97e138bbf62e38068f030ecf7fbc6f5a100e7b115048a17be2e455c2eee1fd7fabfd4935bcd2262727f6c1ae5b054a91801ae21ca6a85a165b8680d5361e1f2360f2b8f2b85720d8469bf419db2e8fc190d3310466a68da382f9790cb7fa16e852e205bc9829f9da8bcc42e738e995a3f025b6e566c09db70c1cba5929a37cb6a05f7aa02d6d91815e91e2715166b6cfb06992d89e53204b74c6b1f48238f2f9950523f33f99059df1113d7bf7883c246011866c9c3bcbe06e938386e9b8ec5ffc8875e5ff138433e898e53e7ec11dac60b4cdc3e2919375d16dc5f963fcae7832a0d9d68a02b449a33ccb718be7a67640d455b6be3ffe215a9e9b07b3b7a24a9b26c81fbc0518058861173c5a5b0d5da03ff254ac7f8d3ef45f88bf84467e2f26f5c8575e9d86fddaba06773c2d5b5690aaa5ea817394503cf81ee2d636d05be367a1e0d7a63abe81564d5c12213e6d60a22e627a564cf24f344058ae81a5885d1c8d1461a70a2a639752d76dde672110dc74a85d7f361ceebcf1ed1d3a7be21be7e51ce61ecd576bd3df17315d23e6b0a74eaca61d36c4be978e72009bfe9c6238f663e5ec6887bc7bc3268049b12de08d4c124345a94d7b490e1d67c909c857c0835abe9067f7f5b372665e4c46971034cfcdc2c973b1f7ea2b5db739748b9614b3940659339f865d28be5773c2f6c04cf48f52c6044e04ebcb70d717486a479556e6a5e582c56478cdbde425245eb031c18ab63fc9432e4c81f099893b1a53e786e75daf909551d09ff82aacdbeb107413960e4bd6c3cd4279507eda80793a43639da85d1ea7357a11ae4041dd6300e4294d9493c0d4eb71c01d054b0f118d3eb4ff81590c4495f148fde7b2099d30274b64658a9f56b20e47215bf297cd63707b4e5b74cceddf53ebebf05f9d0f1086fdeb8abfcd9ff768c8cdc684f12ed45e0e5155803536eb98b60304f37295b0a565f1fcdc786d62f022dbb8431f4a5e903b6b73b4f7e3194eebcbfab2c9f95013bde23bfe4e5a2308fa989ff58413fce5f570c84a2814865db37c122fa6e0608ecbdd08626d136b4a37695658ac7429c016bf84b77337a440831a30516d63c05f00df7ac3ff52c1b47de977dfb761c02a56c9895438b3002d8a30d8cd2a0bd0724b4fb8bf5a122093d389854d9a331d684ee1517ad504c6027dc68518831a3011fc3b3592919e0a2a3ae333d393828b69aabfde2ee7c91487a80ae88ed6ec20a2909a1979a9d1bb3802fa536f21571f55b7d89f58a9547f6ae6d38e10cb17e0e911773c20e33a0e9f431938fe51333a3cba8b959ddbb986e468069c0ef44390b99d15cd696ff5aa766a8e55cd2d6c4919607f2423f74986ddc015b5cbcdb000bb81d47e14f8d625b12ddb005c0af6dc33e9d9036286a619f7a3c187712b61f0300801b8479ded9cfa9c27cf5e1c6495f9cce30649aa1a00727020cba01917909a9c2f2dd7d8fbf4c50d2f98f6728b59187e577319b8d4ad29763732cc8a90a0080019aac203c850c0c73b65296fac71ca8674e776c5393ed03524338e082640e06d09d4a1780564aee3417782e4e4d9291b763bff0de6b08e84663db0a29ea031039a13b790b6d0eaa88d351abce46d7b4b6c097d2de2a5e63b5b317f32ff30745cb729f9a34d66d7eba658afc17486eae5ae5ed7ea6c17f3ad71a1ec96373087d805a8a57b661a7ac0efa8092298d1a8b8540c67892d2327d254dd9e1d5980a89233369d04c83623e057c57c9d984ea2287ba09d3edbb0778d4102f2a36fe04307e8509d6867760d121c727bf1f8deac3670cf72d84798c5333b8edfa7e640033ce07b3e3a75d32d62ccbd2c32727f1c767cec9155a637ad34db8ae486c44038f379cc52b5eff00dac76c014eb92a034e1697a92fc35e0ce7c8aa57957c8f033bb2b6f2bf8696a0aff02c5f0adc78752d766e630ad59a6c248b7b684d6c0009bbab250bddf205d052ce7c170fbb036ee7289a03bc4f10c4cac3c60566174f02be72ee55534697a5f2455fb044509b68b7168707e7bf21de5e5c87a71b53d107e3f969dfd54a7836cc690198a8ee7fc6fac5321f22585f3aa8a46f09e482df0cd8a4227450e36e15cf29af64bf480f36f4494d7086ae836ea681f3223da6170c8022ac64c98473ccfbe8ff0cb89ca96722c7884119b60f153010a9afb3b9d10b35d858af65a21c58b78a667eb3df5e75a6122aa3eac4467e492a66f0b0ed2e0051eeec230cd0f7f1dce088283f6e8deabaca0a94d46519b6a209cfe338eb9f0430baae5661318e0c9bf481de32cce6661c5b89f5bee27f3a5c3c0237446973054602eed42ddcf04a1fa0779d0a45a365bdf19e706876d0803fd2e4e869a5910e66ba3f0deb9ea2839fce859f4933743960006e22b65e1974bc471e124b7a730609165b1fddcf3a87f1e15d54389ac97f09668f6e060b5970df82d1f9028083048eb1a971b7302bf73deb283c97385068d5ddff5297ae836bb0b532e0919ebc0349ab86b8eb64c8b0e1540be8d5b5d4493f6d9d93cfbd17bd4f13e139ade9f70f538991661e7c22bdaba20e78f978884ce5ad1a70413b29242bc3ece037213a07f02f41a312bab6ba08ecd267d87ddcf9f67e8a755d9817953fc39abc92c3ad02d6bbb824ba237e0bc54ec5eb755ad18fb9d1b6e43b612f0401c88f897bded509404fc50cd6f563e48ab89856c19e32ce841755e993923b0e44255b38500195085628c4627b689e5c5ca31ebdd0997a1cd5957347aa6572e6617d15f7af15360d1bdffa4397c35000e86a13d97955b235f5dffd69bfeba3122f8875c7e8dc9b0ba82312b46c7d12cc19a6b690818cfc4c712d8c675da9334ab375f9ffe4accb09647fb355068065145639af9235593ab9479f894e1bc3c8ad33cd0d1d4a37a004bc3e54b9089f6a1ee38041837474f57ed99cc34c6d53dc682b4af63d19045100fbd79184be9ffde74ce4ccbbefa8098765e808fdd66a77d6ece9c3c37f4e4108c8fa67ae08168e841991a5d27ae804f91de710f70d6d099f02f58fd6e22ced0ce21dac068b247137da0399012a3f5772bf98974b709761427175408a2103f0099b72c0dc0ef3d47479d054ab8a573defca79ce31bd663ec821b0ee5d37a05900ecf06a6f5ec1afaceda9f445966af100ee627a6073bc84cfbd20380484cc4b0486f777ad13b7f30f3b4e2489188e2262d88452e47ae3c6473e5ff5b727a7b10d63973495e4284e05e55256d34d931873077bacd06f2a43c98714695fb3b21a0a1b90519907904a2946e10faaf08a9af06f4a24db799254a81f5e14447fec2e08bfffe29b1f89fb94826a6d4968314c191406968716e9aaffeed57e5b1c0f4b06dfd88c928ab386eb6b8f883ae0eff94e96d1669bb8ee5a0423f140ea13cb9b06be5ded502cd8dfc89e3923ef469e16a963ce91208a821bd0f5a0af0f0633de0ef4b5d08e1c29fd43f31da2c0ca8fbbb2bd9a8fabbf50134bdd0ae1cde72e880cec89a7ee6a902acf2f0a020dc47ae02be32380af625867f2f2e1f09ab409260cd2bdbbefc1a14ea87a4e17e1815a8f6848ccde7862fe3afc67ca683a4dd7ae09514fe82abab3103899e69f1c99c59c3f4cc4bc760c45b1f2f8ab963ae2f0f0035771b04a1aaea05d02a686f040d9d61b14b7684cb19f245f28ad7c5205df4f036ed0b5c30169baf1375abe7ab5d8a354ee3790f4751c5e75d1ebfbf3a8863c069f79cc705c402c72b9e0ddacc2c9a691be43d04d6834dc42dae74f24c7236609bf7c0d2e3468cab0c037e762ddcbd75371aa65bf3127b4b123b8c0b9872f1501a4b1f939049109d633e6f092c9c78eeb4e47a815c1cf476e1585b4c546922b06d2523040823f01a97df27011c5d7b9ca3d977fc7976c9353e2a2e229e92554043f097979f3362c44ffc43d2192e338220e0cb0dd2bd5d9aa6df6a88e7f88db0e8ed824a9bad54a3ad8bea7b8a2111f56fd520d3c7c7e814f22da135c33d35d061153b5a6192f733cc3022bff92a043426fc54374c8d0d9c131a05aad61fed2080947e28698e741e67bc5ee8df2b6b357bdb06bf53add22c94dff83c478fe2e0d6c4d9308e894179b9f427894feb91627c4aef78c875287842f0cf3cc6239f308f9cfb496556cf1fc54ac8e1991143d740d906ddb5b72c321eab125307f82020ec44c33804ce19f1593baa5e601a658114d8c696c2a7448712eed853ddf9b32071726d110416eb1a79f4ffd2cdfa9b291228697acc7d89c316e8091c10e4eed0ed274cda04e3296c52164a9c46846073f34ffb09ec2c3f64b23d26c919f34ec03a6c69ab00f71db5850d80bdfd3ab39581753c045cf4a034418e6f95d5763190b0c4f48135e49d63665e4ff237e865a726e86715d5dc7526d3f6fed76a5c4ff09ac7500b12eec1011512012f1b6925f32302196ca6992e8325c007fa5b9d73805530bcdb0451f195c7c833c1bbb8265eaac8ab8b00f1ca8b63727716766c7390501b0329124b468af903a490a95538b3b09ca73ea639b05138d129add5a22f607aca781f736ad686e02e527163d4ccc69ccd0986f10825d3ed716cff988dd93030cf64b0bf04f95ce2e2849d6cb0b17d296fdfc3c97fd0dd83d17264ab2851202d9bda70b489c16e1c4e2f3cb32b707a1bf94edccfa068103d95434fb4af7800a4e3d5b49a4d7a34111b29604078c9ebaa3705e43469d440a60ca7f174e68a70c7de4e3edeffdf2ad2eb8640bec011927841d00ce9582b1d5c8055f209d3d3e0ff7bb2b3998c6507286594b67f81a02b1c193198a194bc7df8f2fce5aa50c2e0df55aac55e5509bca51c5f7a4dee3f5211bf359a4845597e9e4edf5b1dce5be023e353313b1f0425f854e9a81992bfbc034c85c97cc96c1f76f2132b9f3731407c8c4ea717854ce40ee4726b9f4069e92671968c9e2ae3c3b92a42b8b220ce903e33b00040b3f9270d7efd0b64daf32219916f7212e2cc3376f0ae04ee73bd00c134cce8598b83359acd5bbe97ff58c6ad1f798b7a1d1c1d5b81a3f71bae9410b4ea9cfe793cf2dba09cbafcaa11c4c7d67eab06a1fa61588b9685d73aeecdc0b40a165791e4003df8c777fd50b2399b897636f3bfa6f48c31cdd4c16f0965a04d275fe07df2ccf161452a0d57d5616b28601d2735c61c22719cab7cb2272ec023142fb46cf5a99ef16c3d5d282bef81316543f150541b630de8859d28ada98062063c004a94f6901ea45b036ad22dd61242fc51943f0596bb77c9467a7b12c0e5bad842562d84b295c0a0e28c9b67da2ac6126a33da2dd769b2551127dcf22017c82a456c3ee28057fca0db867a63f4716fc19538f09017ca01bb4eea89b6104de3fa7b79d62b96b39b7c109edbda2e2fc79232234d51d7e60f356a80a6bcc036539a6fdb40787e3c116bb6ead9deda80a4b0f0f217b6942ea34d544325c4c075908f9cc2787013d1c0fb30c1bf6de8ad255c9ff1b8054b719ed780601acbd00acecb3b6a3b3521e905220d23359a3d60f42ffb55b4a5bb5e67f0f60e94842081d13355b2b5bf330792b1e219c8dc63eb52e02cfcd360849c17118ac2d1a070f7651f9efd17c5b17bd4d9a53c22d760ec29e3836015244542df2c2bea692c90c0e535be9a6a1e9b44538ecee2dadfa45213c41c68833f1df6f42c8a139414d042dabdb55ce95028cc2cb986d88c425c543035d79fe3239057c0f8d0a47557406e50647a91af979c0e0cff767bf32f54d39e8b4194c7a8745b1fcf471b220ce05cca8dde92cbd322d15c09751afc3c4fa0224673cfda513ca34509e299ec5c40b221e1268e802e891657a4c567813546e79571d377ca49f70547ace7213251e0c6cb1077bc4a6263063d1358a14f8e6e1b5ac324a2ca0c007b3332355a9462708598e7ea18e22b65b0d8f93edabb4511b2a470ca02357530d9e89e73faecb3509d7ddb526131c6da0176fbd613d5dadcd1b5e7cb0b29cf477319e274200486c087cf5b4003e28f4e58fafbdca3121d525df8dd301d9c402983b8c098a2b5a8c0af219ded4cf728a3d1d9c2c3721b320ac9f3890e024804b74d63344be15ddb503841e466ad2221e38a09ea648ad1aa3502af8ba064d249dc761e79ea35c47ab0893363b93a254d779f4a7114404090fe3f109bbca73ac0f6d9fc082fe4bd3190f8765b1e61c942a7f4e5e75e113c8acf0e66bc14a81be45e84f820547cb10370e160eac863882bd6eab14c14eba6270cf1ea89fd10d1d0fd866271e9caeca2001740cfa9f84d9880780337e197ab238bd455074c48a01d71dc98f85a0b477cc029da8fea44b8b1d5c7e5b86c3909e177294766283307657673620bdb9b6f51809a84f5074dc85f3cd184b3f5f02cf7a55e29687b9badd424d3cb32c669cd33b02ff484d8abe7fd2a8b4475578deec90e40ebfc80e0afd0a931187adca05ff7b05e92cb88a3d5b040d1e24b6545463809dbc3379c0ee136ca3eed5d65540d480006c9a14fbd61c44db98c6d9f38f22a5e109f39225d749ebe6acbe2df356c5b00cd400595449a68c64ac9297f54323611b46d6af46c84915367ea28f098e3b1409fff26f1c88e3c5651d382f214ad8bb127904ec9206cce797c178150da5963a0ae971d6f2eae6e2ce4f85c28b15e9baa49dbbecfd5ed7717a84c96142b32c3d00114c093a7d21aab54fd00fbbd72d17b8b842a9572e8e1dfff752e5a5cdae2407e1dfbc066051f8a8fbf64cb735d0ef5e7e137ec890debb2744f0ce6f6974380840a653fca5eb58fc9e05769e51b09d296447a830b6dea6e0e0649a9f2efaeb0ec31edef8acfdafe7f8e90bd133af9f47d758abd9c80ead04ffe6490513937402943d82625afdf5a4e24ef5d578140534972e1df735c76d96d0439107fd2cc90fbafcc683121e70d1ae6a3ac267aae2bb6f1b77446a9f6d06b3e8fba1b1fecd0d6b55535bddda928134f6de8a696fda475e5439f20cb4253f4ab2abb133b8a70fa2ea6af056c2b68ad65ccaf13ae7197caeed34284383cdab6159115f534c33092f7bb1d54f0d78249a596761ac668d140384a2a51edd475d10989c31b6ab670739e38f0ac99ff15da74325aa9d7d11fe01c979e532626539c25694243e2bbd01be5ba415608743a8d33eb2a50a08a5ccd729f10a7696696e659f36ccd59a180ea38d6f39eeff584e2a8c8e2d3d97818ba833503a736bd124f0332ba56e90480d63f64d5191430ce08f922f6cd1f9486c650f6a969e6c352a93f4b4666dd7ff00d1a81519bdeaf52e50cf311091bf14d2821063e54f244a17ebb6b1e6c1443408e48465a2bba84c402d92d4f091feb2ee13e2f61c683ddc880b5c3fcd03dae10d6f5f8b488f13d1478829ddb453381c94084df9cc68af3d00b3c2b20dd995b208d5fc54d10a895768f673c8abad552d52ccaa3a78eca5d449265df2bf5e05720a5041d8e2c871d2586e226ffc94a792604d535deeb89727f6eaf9a7df4f99fe055a0ee3f49471e50d917734c6498701877b8f9be436d6c7b92fb8c296d9d441067fc1943bac23c1ff4363416a26c1174edcc7928842060df4d91628b655a47701a981da5a5a2024ba9f5ad601cb1dd1a88123c648e098861c6e6ac004ce023b044d458e73a0d595b6d16f0144370ded7d7b1e022818ab79ef2f106c6abf2bcf0affe5034abbd65e4fdfafa83618bde59aef19888af9afd24b056027f3ae654d03b701b246ca040487b9e517dcb3010865bb33a47d3b0746c97ea259f233107202bc60fbd3f5e274789caedd548c8679ea6b4befcf123fed6b5153bda5e9ef7a0fd06140c85c0b5dbcaef7b3d200d4ee90a8249c9e1cdf8e92d79bb2c85f4b9e08467a89998491163e0238e4445b093c5884224498704003230c660511933f680b8c76508ac5ba16afcb66a0e78de102d3870797fcb0b920bd1c7b8a75a3d16d05b94fee589a49419012c9798e69daf2dc16c13c53845c4e88d85e67fb00ea7c033861a63e5b6ed57be925e9b80db780e795119e63b52e5c140b5fadb52a7694013aa1545b1ba096e01f974aab2d7da58d7932ef10b29f7f0e179a65c37bcab40b04b85395341bac5c9d9f44606094aa31532e14c8502e2b063406e763721cbb031e8f8f89da788f8227b8b8e5f301072cbc97dedd93eb85553f1b7417e53f1a0423bb007fd2fb4cb6d63910c1680dc7bc87167ca983c9a94b48f8c854fb64dd04f271037dd0076497fbec9e7d9c675942f9681c387dfcb527efad025dc2154f0d4e4986297d467556d2251565b8707c2b84ce889da99f11f0f824a29e1dc5560b754ec5c6899649a51c87373b0259d8260d13c3574ac8149d84ec6f249050d605a4332053a58592bdb75603f02070545d47a5d47503e799c5b6947ba1127a9c006b8e2b655bc756afcef946233249aa8005b2910ca286cb573f1f44c0af54c004614e9a0e1ee3797dfb1a8bdccf0975096c5d76ac18d221f677450e18128f9108cfb7d2f119b19e61ba43e84675eab4ef464b6969c3a69c4099b66b609701960a307133c2e69c7cd453c35a374bafbaac58148cfcfc08032a0f92349692808a05481a1dd5b2a3ff9f9b7d83aba158331d106a57fafd3da0dd42f0fb335d6ce808fd042bac3192fe91f1c51d1f7b8672a788367c7331db3f37130b13b5fb03110f19bdfc154625d8924fb8fdd7452dc09da3d15c0f9f51001e6541975dea50ed0a558d87cbe304bce9ded3d7032165880dcfd083a1edc80b78aeefcb5e15f7680a647c6f92f21b4a69b5379924386a9495da6e6575f9913e32f436c7feb5ace3007a27de0c60cb4d4cd95431bb972be52e84b7c7052669fb7e2bebb79d5484d004e215168e399fcf748568c564880049bf6b4011768dc0d8391e61aa4df68788080e26f2de6371da6defe64c9145fd2f2366343515e409f336aca01a1da93c9c041dada41c88d6f32b2869adda485c25676936a67a6ad045f8ad1983979055710c58628d9c8b76b777c3d725df2834dadb47d31daa94f58a754341213f071edc07761eb187d598c63cb131b4c3dfb9e75db5fc18fd1dfccc2c312a763139e1ca09b833dee4b3f56a0183488dbe861a2adf22a6eeb211c90e24c62404a5efbe6208a55e6d89269ff4b8d3340c5de4ba1e356a11f0ccceb30862b5ae7301dd4023078e81a9fec40aaeb33da11ecee01fa5adc6cbba2a3ea60760943e007eafbe9d06e31a6e43450fdea71fdba256b21405f675a2b3e6f2ad01457c8ff4330fd20807f9227e96533967cee1449c45c7e5657efefb8202019721b26729f5801f490705a9f85d727d99b78286135abcc50eac7158500710eee9d6148f360b27c558d10c3da0a7bfeae8e869e4c008000337af5cc7b19cbd803a1f71dcdad538742c960956e5acc9ce75e05736175a01c84935c724ab1584cda97ff557ce9e2f2626760c725e40802b6908051f928ef8e5c84785e763b23bc4daa44dae076d408f289606ce4ed1fb55cce38ef2f3d28190113280384ed4f1cc90dbf65d788ebd4453620c0bed02b6acf8797d3e414b2e74046735a291517de9b3fc7466178a9a15435b0e81c676dc29b80ed4d25062af964c0578ce0e670edf3fdcbf03e4496e2790a7099f9184ad2e07e409ce075089304731ad21cc0f78693e5d0c361f621901e345093cbe2ad4462657fa3c9ccfb9caedced748a476343c7aed7b34e325f5b5e83e0a56ff16ac225f83af502eba542908cc4ee3e138e3f34c6457cfd17cbb5541630fab601425b8692a4e5dba7954c4000d247d0ab6f31016fdb4cb6f5a4e3cd3880931f92bb201ba36e29ac5d5be99a2e3acb4a32a5911f696f03fb441e4cf20850cd53d725a7cc9ed083546e2884acdf82b00cd1bb17274ab27cc6cce5263838c0d4fc16cf7abb5f1eda114b1bca7edb0d1ddaa181f9c9f1a411f53eec8d1ae3007cbbaafe6846705fc7834f68bf4b5a47e922bce6a5d94e1b4fa80f840c654930542b796985b46d8c8de2b36847326bcd9ab32af10b32bcaf66e411cb985d7c503481740e4c26f8da831cc5eadb4600e0d1ab9f9fcce9fb0086115b5658a8e50026d0f86eabbde65250ed6bf70f0e4e00101e812fa83182fdc1b585c21fa700002b24edca4e4c2ac0ec802f43ed649bb876c90a267170895fe8cb48d798754ed09c00b2fbc68e30972e95ff2f51e88baf8fce71db37f7f0e9f9a6794a603d8450d19f535de170f849d953cc87b96836c9b3cc77e5f4377d4675c2ad32c642740061d9a81ab3340d3aab84e8f20ef0c1557e8035f222e440cc01424a1183f03f40e64b4bbf7dd2194b3d034c29a066cfa96951fadfe27b0ccb0647f9376b6e86b00c54b97fcfbcf7118912f4dee6a251cae34510543c4a94cd0d9e4e8cb3542c206168e20027998a1e4f99d71ccfa6429cd24857d3ebc749510a1c0cbc748d30a01fff2023157a48fa8fa072a5d278cefb402c94f5cfcba493a65320d9d13bdcd0fe74d563e4ec494353ee7926238889070be2d5856412ec92101aa406bd596280f62c41e57951d6ebbe2ce2652ec455e01929aa5868a1afdafe62cb7d42600fa0936e5eefd1cf4b96844873f6bb5f2e30f2a6e885f8d06cb64d7e65f73fda458016732ed8c105f8322132ba763a4f1281880816c34f41dea18e7d251605ff94a0615ef2c5afd66888390d2de2f72c95d64c8cc040ba087222754f85f286d903102771df3f40c9a113c78adac396f33a4d045e75fef537faf774497dd0e9eca5005 true -check_ring_signature 611269a13e3c438417e4fbfcb7085ff9bcc84919156a588b70dfc6dafeebede6 fe2ff6e8edf2cac7ff6c439f98ea1995e41f616beb26cee48751f01535274d31 1 55429425979d2e8b010138380f7bc1edc981ea672c6c9cd119ebfd6a3087e6f1 27d370988788301c5d0d9b94e7a03af72f8d21c0fd32d58252d14a9dca0d8c099bdf33a6c6ad897528520ed1a7c7ae4e686e21bbb9be91343089172401163e07 true -check_ring_signature 6c65c8d75d8c28fcb01b86581f314f1e83d6b2bd1446bd5c090c5e982f198cc7 1b67d8768633780de0b1448d84b76ba3f3ad102ba2ee9a7f2bd23cc6da5d8f31 8 7ad8ce9587f72063e1cd2ec5acd93b921e37b283cc0654411b39f96369f21531 4d96aa95e7b82b5b070103ac5cb342f653df3a2b5d074767ca6a9f865884d840 a99c8f2b3122aa8793e16e4a0b7fcdb79f9d81ceac0c3badacd07a6396df1dd9 18143e19c59192c0aef863837eb6a1051b1a5325265fcb9b547a6c591de3af95 d2bd71822a9631edb96e4693959e059e3f37696821dd0422b0f1ed1811f57549 be9a01579ddec9cd77298019b60717fdcc36effa4e65185da1201de32d321a38 8da72e60983c6daba3dfff244c337218b184b53c0017f3a249650e7af9e2c6cd 7955e8e7d29f9d0ccca2565961a1591d5836b01142e9cba4a8f92dc4487b636c 269e56d0979de46a2a89bbf181070745a700f08cf2e9d78d5137fefe64353905c58647f9db6291a38645a17dfbe4938e78dc8d3a1810677f547a09c767374b00707d39eda99cb71746d9137b75564772e2ea82c2db94676906c914d622b73f00fd073d38bd9140790eab8833eef7dedf2ec21a3e4f0e0bcdb8162063e91eb90bb1435735c9091ef62cb0aa988fc608f9c07ca5fe621c6a827509740a90a02b0ca84ac53850a7a14687d396e3f6e689102b14a33d16ef5d828c3d707ed11d940c9f194d21872291cf1094cb6e335de574abb85ff46e6a140665e019b466f8810a535ad88b2325a3d6d717e590aa89e8bf877988c6ff6c4c2a9e632ceffd244f072b00655863392598807df4899cd602e99b2450744b0f05d1851d2f83e893fd0fcc55b3c2b72311d8479a22afca93399abe8d077ecb467ebeedc192c7fb1f310e250c25c16bbef5a377218ef278e94e5baa7a99a471658d9077dfd63a0c5db0050b4896582812cf477c9d4629ae6f97a5ec3b591a4c8ad71370e53572eacd000b764fd602b66bc6deea4090bbdf8bf2697c995ebd0011901c054e0a0fbb1a8e0953b3380a659eb05d62a27c3057f39795f7325de0c61da6bb965e2bb0a0c1c0036882b9cfe6478f3fc28a6c3d720965f2e687d6b7c9172c892d0b0c6183484b05609bc489db92207c99d2a0314c4698b9405f54d7ee375a3fa304cac29f435804 false -check_ring_signature cd5cba83d4c92572fc57ff79dcb127d9986e5e78a1a363a83fde0992162ea2a6 576b576fc87ec4dcbbe6a1c23b8555b166c414eb7e2df53de7353469014bb8ca 2 faab83e3ef5f83be48c66bce02aeb63988da8a5d3821757fcc67c46c46cd4f7c bdf833bedfda892615d8d2bf5a8b8c3a5d8dae8127012c95f114984d948ab87f 6e12a5b505064eea7c23a126ea7614c499682878287181bdfd7cf32d07cb4408b8915180fc04feffe5433a66d65d5272dd69504725fcb2547fc842b6596aa406be36d19a8544903939569ceefa6668e8975ed895061d2ceb732887834f89ab0a48652b4a773adea9c154ecd50d8d7baca5daff070e3ea84a1754f2d91ae57004 false -check_ring_signature 82e2ce6819a21c949f58e9206de72da8009bc77a0d502ec2f82ff79b4e7db2de 01104ea90868e2c7e7a86cd31c89590653122b3fa68d22ba5986c2284efe00c0 3 71b71106e0c8547b36a6803a3559ff5c22df6f6ab92034f4104894a7bad067c6 66f1a86d393edde8847ff60adf646d7857643c24e06b9acb2fa36e419c35c545 e603f58ecd20859292ccdc9c547bf27294537f5379ddc05240b453a4a285589b 254e2a64b348871fa6646e0720b182e582d2527016ed08a8f4dbb4eae6ce6702c76e6dede6c4b9b7b77a1b4692ecf739b59767f94ca976a95b78df4bc9422509d6e535eba8443ce19f79cc1fe169ab3f41286ffbc8050ea1f3b5976a9ec3f30a5b36ed90dea5c1ded19f160848a7765c5ea844c2a38f975999b2901175f3630bef1e56e381c9bf7fe9f75fb32dda2f34d363b5ab61885621cf730e6b43d80d0ce2cd834a97a81566d29f3ccd9fbbd4ff5eb6383cb0d44de18bf187b3ae0bd98b false -check_ring_signature 6777347f9cc593e8677055eae788b28ce3f8f87641edde294d51e8376d904efa 1acb7efbded35e3bba9d24a46fa95b579d33801d76f77e162b1a3d863ee5f276 5 1f3010ab1fd56477399211558137b88226d01f2f65c908b3af578520b709360c 01579f0927fbc3b03bb8dabfd68a4075302af8d96e1fa8a68489a658b4782fd8 197079004d17d4747681d216940ed7a931b5403dfb5bd3f87b4c1c742dd56b1d 254b4caa4180536c52f45a90bf69b6d02046a2305f67ebe6cd236ab89bc53062 50b0be24ee72c866fb9e252153e6de06b67b0eb853a2d87b2ccb7c8776321a41 4c786ad46cb9faf1279958ba2a9cf4c753abc47ebeaea673f748c473e984fa0e3207fb2875350bad4f0c085cb273c976d3b17d19f8d82b75c6b0a925a159ed02183e7aa94930a2d5f3961219b51b51185636a7bdf1e20a24e6bc5fc0a6488d02dc7f8793a15d785e8b541bda49582c06f774c03f5dbb256455c4c98ae9fcad090d81206bcc2b4b8bc40836ab75b25afa1b0c59fa4782ce1d87da96ee22f004f22d1f4785904cdc4baa05db6ed4896fc23ad7755614ea5e2b1925288311a5a80b584314614772f053252c8e181fa7e23314aacaad2d49cc3375d69f2014ea640eaa0c95f02bc4a4923602ff1c3c4851da9019683d941821fdfb3592927f355d0e69b2513874c9555a9a6135d399f9feb9e44a1d431ceef44054192fd491048703af1911cdcec5c861d3b606e80f5ef69aec8f6a8921ad7a284f694c6442b9cb0c false -check_ring_signature b8ca2847f6d59f432ff02af7aa15c9f5cbd610769edc7e0050bb00d2c976cdc6 6011140ac9068f773c3ae5af0ed88184ebbfe3222b1a6b678669f50589f1f68d 1 0d765ed5bf59b20a6662d0137cd7be4ecd177eb0ee9dd74fd1f2345adbde3acf 95d58139c90c06ac380becd874e6dc6cc96a95e2c58b0c363affdd3be3234c056fec0aae91477e274d9e21b93b071d010ad020f67bf1155b236845797b41b602 true -check_ring_signature c7dcd314ad2d63694cc2c2e8598c83322ea13012a2d56bbb09f7ca41dd8e2ed3 769f0fa574d74e288a8b4ae96c55916f9c973a7ebd92f371a68aa7bb74b80ad6 2 f3e0d9084ae542fc89c942c54b2f07d0140e803b076f2f178f1eb2a7343e6b77 57e37ab04e0fc6d7766de31988c6ed10fe5540f60c95129b0f192b5ad89dbd87 f0067eb2e9c3a5613516c316e7142c88ba90dc10a81074afb5a6bb65343ac20e26445d28f68664e48c79414d68a70af0d358bba86dd8fef75a11fdedb2287e030651d033b880858748eba854ca808bec9e9d9688a8e15f5b63d8971a6b081c0461ae44175b9b214d9cad220537645dbef2a92b6ff6a7c8249e643bbb8520790f false -check_ring_signature 5ffed8b1c59afff8e17dad4e0fae2b27c0d42487b75d96d2027eb6e0d4421a46 189993d22cf4c736df5e1a5a7e7aadc51fe4e1b066d1df46d03f9e4e02fad411 2 a25510c0f68028d088fd50f2f2e2cb994be0e6e1e03e9bbeff7e1ef1e01fe55c cbadcb006cbd0ff262f4a06f76ad94d76df5cbcadaf2588205e948644f5e7b52 e5b028334412ea069b43f789b5b65563301c3ddb97de0dc4e7cfb63e52af5b0fa2fb01f038783144b8b41c44a3edf439d7bf51957aa26c46e423676935d0970731d1a551732857e9b7690a67304b8198498e1a9c2457780b2e1f210f9e02c4056c65ece29789a2d8d67c6fe62e1409018ac59151394c67d69fd638941c69dd05 true -check_ring_signature aa004468bab6aa8c3ac4766cc0535d27c1bc277aed7788f74c9f70e03adf4ae7 1d2c91d3d5b8c2972a844ef9d913ce0e3b57eecd7db2c8da4665de61d3e41615 1 0dba60fdb2a9ead456fc94ebcb2c53f0350077e47aee77092843e57caf76e5e1 fb597528c9321144fee6375f6511483ce2c5860fd29343cb8cb10e9e5670b30634ac6dd779eed44a6a0e3053ddc10e8f13aea5ee3c9422aec312ec63fbdf0d0e true -check_ring_signature e64cae40c6f6a78ebd03bfe56b13ad65f610c7c4f968d8979bcd67375df67b91 1c30950c42a1d2c63f11b24472a5768237e8507f26a5296c53309003f91fa876 2 dc41a2f5a4474d4e737f6d0f9b928fcc519085fa30f0238ed9819608479e72c7 5482c7000f5fed27dba1ecdf3ed9403042aa242a8d9893501b6764f2ef5a15e0 1c40a035ad693eda22ddcc875f6120576d4ca5d97875d5771bbb207b16afba90119df8a1f6768ab26f87461f2020fcdbf958a215361f622b7c46ba35b474ed044b3050109c449ffe995efeedfe71942bc0891c5007848e7fb7b5fbaf4934e702b34124c184082bb86cfb610d04020967438b062cf1ff5c6bf704612bffcbb90a false -check_ring_signature 9b42caab5a27f6ad5151876e9465d6091024996fd34b0b7cd65c713a5a3f7b14 eed8dd85fa98289f35f918c7b266f6f10f106e0520656a5c8c23cc19053820be 1 ec69efca4ad5506695058c53be8b54d26275f5b87c1291a0c1bb2cdc2e783c89 5a3aba237520d7297cdb40513f79d64d38bb5495e46f812f1665d794e5d7c804fd54470bc6503fbf60cfbb75932e3a6c1b9e67397333f52083b8d13a71f1a100 false -check_ring_signature 6e01d7be137ab83a0c0983a52d110cdff5acbf15edad0bf681a426fbbe19b6f0 894febffd9b06cf305ab145c0533395491030fa736d53006286ba92ebc086751 123 7057965971bd77adf9c04b1a10ca1cf2f71a8f9e7f3a4d0d378019bbb8f34c6e 4c1162000a384fe274e5aeaa6c619f6c5ea6091c280efddaef0d166b3da1486f 5ccafed9ceb3bc04b94c963d88023b198e770718c1b05a2007f9c59377a6d205 d12cb1ea3d971d403d3fb3926cc19e12ce45f68c3489ced2ca3c23fcb181faf0 9e338a21f5351682112b80dd594d322ce1a374b24205ee2f10f3f292cb2b2338 dbdd677b6af2a777784c8d1a9e63e61a2b3f5e6e756eee7fa72485fa03e0cee4 ea29ad140227eb3c42213d25653dac37b9896be877c0b6eb4e02f061e0d3875d 68dc8cbb87c80459a0d004897ace3f7f602d7b4e367116ddaddf153b66213d57 2d1231ecb21091a35a17d3715b3be8779570a9c5dc6f7dbf7aa9becd4b4608b0 f1e46fc5d30ae8c146b5bc9f1740877159a28dd52495a11ef4f2dd61e65ad1f4 cb8affe12d0b4882a2bb70d3bb5391583ab59a56b7aa3ab2bffa3f1cae75b4e3 ff479a9adea4194dc7fe5f51aafd59a9d095ec53688aa6dbf6a6fa06926671bd 526593ca415ae11b26ae9a0d3495c5e34b2cfbc92f088a41925251ccfa3f29bf 9704618e0265ad55cdf7f50fb3c69ba0302fb9e748d6a018f7c9b29a8a5cbb2a 65a75aab24eebfe3a496c2ef07f45d48b3078dc248be481efc6e87f334c7eb78 3dd17e4188eed6ff50465284d0cbb95337e6fa864dec37396a22bf974d9d63b0 8818023d2537f24b98854a4bbdf32ce557878c5b8a15d98990b8f94d6c15b505 64173b7d7d725b8131f19e1189901358069486cddc2557cf33822bcc0b8093c5 e5439e77b44a8f537e6c712c01ba4b1bd3598eb9bbf7cd4815c23d13fdac0512 544e5dce8125fd9fc314153192e9692affa2843505e33582bbcbd40810121562 462201db93886a8a49407e67182ab6371b379f55f6a603b2dbf7cebe957c1a73 859c5d3da61b4f1598426487f8fcf134c86ceddd73b635eda5e7ff08d54690b0 78d3235c7f546f4dc4787e1f51a6dd081697deee81d12c636e9beb42d32b538b fa6924c1d72685624a839d75a0a6bebdaec813f4574a0fedd7eb3aa23608aeb9 0602b9279dfa32608cf3e0e43b659ac86a5fdb06b23203ee137a8b4bb1af1ed8 67250e697471b18075d38ca88ea12bdba639c1365cbe279b325bbaace20ef64e 939d31e9a7ab0ca1fee54b15cfb7790b13ed3ef32231544e6b0b7e9a4667a52e 893700bb766a9ec2d1f888c77002316cd7d3e7cf8096bd24b05fa702d9d5745e a562bf79e1b7e09563824ce0cf92e1a2ad33a2f108d1caa916de1e1509cc820d aad041982a9050f56ca25245b49ba8ca0ce0ec049cd4fd1ead58602eb1665d36 a50f688cdd5110fe9c2e00903e83837e4cb2696943a12a2f814abd4821b6797b 0173ac617a089c8b91e9b01546e6a999dba058a0e1163c10224879e8b2e6576c a151dd55cba2c17f10b2ea7cf60306d29bd0065798c3a3abe403522f3df4448a 1a0b23db408cd36784418c27e907bc987166f5bd4c05b449d83c2282bd28597b 7e60fca4c1822a4106a47785483afbe0f1ed5c5995f1ebd47745f5cea93ed37a 7db3c96ad57245b5b500ddd3a4b846830c2db9ab901eaaff0ca61643548a38b0 24fa9c8b8e4818d6980b273764e4291082b9eeb7a57a1f808c3399376ffd3478 c5c522b49788a543f59aa83f62df6ef5a3b4b482c542d905a525212f08f65442 5f0ec75cd8884714532b898ad7c8d06d5496003eb76148cb317d3a0b48e956fa 387c177ca35211d77e26619f6b09beeaac33124383051fffccc6d0dafc37918c 88652839cece780ed04bc79f661ed8aad83797a352acc04cfd0cfd8beb8c4e16 03605dbb09ace3b8fb33ffa0f5c7feb382dddfe2dfbe7b307d1beb969b7f07c0 b7da003524a0e85cf5c7ceb6df202f90e0bc09b53876a0657e7165e177d48547 0b2e545f02caf690df30aededdbae03f98b76f9a9180dffe9126e94cced13d01 4f63fbf110a07eee6a0b47b9dea2be45e8d4bd0dd6734bcbb4bd86f837709de1 2225892497f62543fe0626c868a6304739ee984157a5ab0686ce646eff0e346f 03ccd2e1bf09d30fc2d19fd38d1e5332c94fa38e308ca49f23e6723e9b2efd29 c1ad5d39e4b07eace01c7729283b7ad11e62ef7102853c98d7cad671fc4d48c4 37be4e387cacb4e787bf5b8d437c0db6bd225d921eb4d7cd1a6fcf4bf5ad5cac 9ce9985d68d561e3c7c3d66f2389f007da3532d4d1231e92c6f381eeda81f725 686970ec7ae6af92714832f84187785cdc1153895bfd41fa92fcd37bbeaef7dd 399885128a03a8878d9f04e77a7b3625555f49284e3f3587f0cf333f27033572 c5c62c058e02535732c7545dbe08fcecb69af22d6e148c5786e0660c38e44276 4f1ce250fe94551977a9445b280a84abd1e11823b4900d4f931b9911fef23caa ae7b66033bd3bff3ac8b58ad18f867127c2cfad654be4ab8f4d605e488c2dda9 ba184674d5fb99989928e8bc2be80d6984376510acf6c95cc2f95f0f9a5da361 bd4008d3b9024cc302803a3812b7c968bb7804db8d5dea5e3550d22884782077 cd86f9f1ebde2927f949bef724c03fb0d927f269abbb9bd2fbc40449ab95fdc2 a60af536b9c6da13d9c9c1d226b883bc00ce4bcc0851edb0b2f3dc6213329878 3d93c0c28c7a91e5fe9b2126ebe8a292728b10c044e4ad3cdc5e3e1626e99a74 b1a7c3ffe717e82b06d144bd1f5bff985f4b368c405b47d30fe057470e6e122b 3ac67d66c0c18c4f3c74bd96c50b2a1a2dfb845c492e53359cc729bc1c8fd57c 61eee01e5b8979d50e81f9d5bd4c4c224a065e70304f5dfac040c213a52c15d7 a8de3affc224a337bae5e10e9f5c21f650d375e8917b5baf00b6b39d405b9680 a0e5245259bdda95acfed01dfa62e3ce6d60ebeeb77915a4d595c518031a03a7 0769c2fb45ce785b411ad332871e69c5543f08422fe8d50e8bd7043e3b7ebafc 74ca953449b4421059cf914891492b4908193be5590dfd11ba33b4b8d5fea16c 329745bba2b5ba4dafd845578e3a67d427d859170e77b9f9b5378c20ffe43076 6529eab15a325b8791a20515d4604e82ff5489ced12c07f28705b51405a9717c 77d220de93da09379dd13df02e2752f6a7ae48351ee0af3de497169706798e72 7f459880defa25dc35a208b2a00af9ae4a1c303e2f4c5998c36fc3c56cfa7517 e98d073d7c86348b8025aba5b6ae80dd647c985ec01d5da9d482675ed55c0c70 022b6529f38d8b59243616d6fc886e9aa161a09409d4df4c3ed7dadafb035def 57fbfffc15d02e51dd272189cac51e682c841b517ec6dca24e978c0de81a36ca d64a9c6cc52e20db4cb08d2253a104d9eacb6c235d92f86a59bdd640b37c0375 6968896bd4b7733a3d073212ede2f07bebe8c7afb7266e8a85d1d0b917a8791b af4745a349843f284a5d93347b6287ffaeba7f19b75aa310815de9d2fa42ffbc 59fea8c95b8555d3e7134c7587de1b25b243b5146046ed28d8aa53ef363a686e c9ae23826514021a40ca8f1d423d4c0b1af4e3381d2f59b898bedb5725069853 e5323dc780835423053259418862b8a418949bb2fb649f37a9f0f6786aaa48c6 ab1d8cc3279e81441e9957f69c427ac4eaf26cf7802f4192fbf2db7c6ad68cda cf31243fd628b8bb27623e1a4b17f2e4515c62e1a16acd24e17f1677042f3339 79021327dff2256e35bb37562a0ea8ca88fa628f8ebeb94d073db73bd4923438 d427dfd81413938fdd441dc2d24d1b287e6f9f7c0b0d0d1de692ab997afafa63 668c9e8e8f4637689f4c2be351059eefc285e511e7a2a5fc05cb64554204e4a6 e0dda41c8e636052aa9a40c83c3bdd8b85bdf5ed69d4610048dc1c7bba862a18 f01f69b3237706cf4e723ac4c745de17ad1cdba7ba79467adfe222fbbdddbd1c 2d46b6c549e8d085e35d1dd28ba1a5f15df19120dbf974572b9601629dae3c0c 06df3f8c9d76012afa14762b2a811cf2351c2416910f88138016852cf7a2886b 8bfceaa399091e2b635698428f3058ac3f482284d1e8602500139f69cb65e4fe 35f983f6d401020c8e5dca10535deeb1527ab2693dafaba0fcd971636873e612 f83dcf8974b06280d72d22301588271e140fbded622ab210550d0ec9c36094b3 c69b2f73136dd7347aeed352351cac2b866bbb4321aa341c76264a3590750471 05d14c30852c9b147b54dbdf2e871ce377d97167a0c149103eb1ff196b84b866 59695697c66b26511a590abf6d8bfc42a2a7c1dab5c9e43d02398664d7c0c3dc f57536e4f93e6468695978c91e4a8cdc4c42f2a7f7d6cce7c516f44fd94593ba 8f11c02e1790a0a6d2520e24b403f4a04b734756b8a97199028018cb297721b8 977dda918a198395a45abecedc3ad4600179a9e1efb40660f870066138d2d15a 68a5a898b18d3aff5f5bbd26d01077e3ae48b8b8894750c023926546ec49ab4f b60e41cd0dcdb27eb7ba576b27114aa47539990d7d5592f76fdf666e04c39b96 83931de442ee13c9742593577dd92b5752144172cb0ff12f7c80a0d0cb16b2b4 b57224a4e2acdb318e4948b56075d332304704ba9d3d6b5ede5a0a04506437f2 f65d73773eeeea242f5d2830a47776a6794da5bede7d5baa3ed64686508b3d28 219dff7378f07e38f67445e9d4dad39dd3a2cda74b411dcdea9eff2c570833f0 c67974e5f10ece883d5f6427de47a8ba0f091470d952258ce071d7bc1375b532 8535e5bdec3b3d660f9ffba962f5a9cc26ba5e3d4071716635f84ac7bed3b2eb 97da618a7a9b8951f9d42ca284cafe0a5e2793e965520a55b14cf282bf4347eb e2a1fc50a86c22c8125bbd162a0f29e71579ab97b1338e03b4cf3353ad4834b4 7f6e6846184b87ecd7d9dfa462c7f87c857e089d61b2beada190dc0431dcb3e5 ed4b29fcd67dc359854025ea08b9ffc54da7220e0d6c29a4c1c416800cdf9e1c a09402cdddc42f7ebe34df2cefe2b0aaa7ca519fabf055ae217915c1dd4ca9b4 d9d5dcc4374de196e9df29f3a85d514d0ed48062b5f401800e3d4809e164c1e6 2cb77ba8fbd26eb4a44a583889d53bcd38d867a6d7c6ac2d72d6721e6509579e d012e2298e9ba166abb03bad86fba856d37acd4c59453ed71b2cbd591fdfe676 b0862f31cf12d7a7e2692040be9a36359a4123abc899e8ae713edb2d8f9adbd2 76a3309289573c826ff2fd3240f3066431643ae4030eb1043acbc588e358a2af 30bbffd7fa7fd4fdeac715b251f5136ef65487a95e4b775938a37991c332d151 8a098269b4ae71bc0ece3803b0dd406e7e00481872bc2c17a50ed7701019e768 a8a0a44b9bce8b0759bda50f366e4939c44527491860f44d495015c96fbfb279 02e08a3fa93844d427739e550d486a52e4c6bb85f6a79dd20620c3f7b3baf702 e3c23cb8ede1412ae0ebff8bd6aa87e524ac7b7fd762136e6e20ee8589975403 482296ae6455d70fa267a933178dba75a4c42d251b3084716862093c07bb5fcc 685e18ae42b22c40037961ddbcdcb2f6dcbd2298491b9d379aebaf787ebfbbfc 67ae97301e0d52011907fe1821101e5063cb50368e624d8f2da28a7f3ba4d80c213e8812334f47a49492fc0e1d9e1df3a6cac49c1148ab1298d79f4878e72004fb7d2adcc5f02ee17ad745f06f989a3a7972fb95cd06ea5a503ac4b7095d690aa1f874dae8886ab5250c32ef6bd2e3f90440eead078b702a751236f330d4f50822055d6afcca5ce69d27e52a5fd1a708f30612e62c666a81d0ab47bf93474d0dbe35a0218458c1170b8dd992f99ae87648e75475a9816393fe79b3b6359ae00bc0a33954c2d0ade7b492fd6532f8945fb865ca406ef67de9f1d58c3c9b88ef06db1acd8124fb1bb60d813525f22d85934424d1a12317eb386a96f88149325505a06e31c60bd3b3a725c5b234bb3ae97e36f870060aa7f8284e32eba4a1710a0a1e2ed0cdce94f17e1debe4be190e0f637f3d44b48de5085ccdfe04a9ccf8ba0ed1c5ce46d6793290838b726e151bd81e457a7405504e2da148a29884fb385f07be79ec7e80b51e7adeae546c80625885e0cfb69af482f15072cf4dd3bc1195049103be5877fda66946a40577a688db23115087d06f47e3a653764b1b95e04e02ac9632cfdc9084e5413cfc40924d836d52fb0996a3f1162334e75d5fd4273a032f8037ce10ac1aea21aa2f357bf1d34dcafed9a8864455b1155dd5c20de78c0247f064d7ba057ac0b36796a793c3438a6f85bc4c23edfec3035fc73b388358054e6c42726a92d796ebe63adad52a7fe379760df7a7cb04cfb5a8f3ad9f8b8f0e9854300265208cee75db9ffea302a1a00e67c147f1d0b8b452e1ca6c2f2a6f09ae66c5b2cf263551dd03e55b7f7accfb6eec3ff9d23db877e16ededb9522c6007e7a2975d11ca54626ca17d9df1e4e0e3b117399e2ebef2e27f7635734a6ad02f418b7d5e5d92993eea6c31055c4b7b81dae06f140a785273eb71c22fab0810a2346da8b76bcc5b3f632c2c698d3923df069d6037b1e422b6a064d20a5587608a73cc70243b75651ec1861b1ace766b7c6a9609de6bc12dcfbef2fc4adf87d0f829745a05c1b4c014337c9a93360a9a20ff58a65eaac098b58663ff063d56b0dca34ac1cf8ee4cca39601bac9b46969a1d55560f9e022a8ec3688a1fa0cbcd0715df31facb20ccec8bf1741754678250e1da4b076b0fcbd0aaf522d7de578c01a3ecdb68cbc5a6723583b2eb83f6c4aa8eae63a560902877514b4e1c60cb7908a0298460791379eb07b6373bbe5e48294a79113fc1d3877bda4f19ffe2d8e20c97d36709c59490aaed36bb6344c704ba6dc8382305ed301642fe1f2dcc8377014367e4b9c372c3b76579e6652ee15a72aaaa5da42fec2872c53fdac455bc300becdd148354887befe103f1b21f640329ab3dae93ed238cc188cac4ab4476a80177228e87867cd38bc5dbad8d6297af53eb20690f40b422256f635ced39c22103b99047e878711bb97a530683ef174397c7408b46c14505eb04510b3cf7db2b0442896424c5a62344c26ee6e46847ccd0efd35659ce1c52735733a0bf63f9360e7551ecfb4539e43096c60d23c2e12ee6bb34aead06fdb090f73b3e2a6028a0030f9ca83c2c5d456c07e1dc80256b004d7152c68cae86b5850d4139de0b13f606c594369cd088c410449d16cbda740a1b5e3083647a539493d73d0762baf20d02a994d2e16d370a12660577fc6056c23d479fd0a45e303e5e15c5a6b84bee010e73f7b3aa210f37474cd66fe0a25a6d5e92cc9abd3cd8cb77bd12f86c73c9210abebdc7da173461970ea3041a93a00a162bfdbee8fe1a2bc7b5385856859a390c3fac9bb7698dfd554cb45a62bc019745e75c5a1d904f9811acbbc291fc9f1d0695d3871ab398c47a13320353f30159795a0571bdf594aa2e73e154cb2d37bb0bc6bccad7b268c1295daff2e43a47b3b23dd08ce73504f960115c96d8423e850233cd2786f8b7bc2290ac7995a898eb16aa7a20b62c87ff2898ff2504c909f4023723065fb1c73d7380409ba550de83e6378299f808de62d5e1136e6f7334f20cd229481481f4378b12d0a17a95360334f8b94c6f28ad4cf6c832b7b909947a03e8a83c89204a7a42ef32df9a3c9d95334e132edf24cad7aec7200c832118e505445817dfb01a858e3bf2477d97240b16ac21b5c89957b24323f9e6aa89bf83014d2fd5017064b264180c0764f234cc4d0e48fe36addade0c149cc8b824680f004a7d586760bb0192f8fba00baaccc456c7bfbaba7393f298825379814c763d0bcfb8951b3b7a238a55e6a8e73aa922300880df51e2158a39347227f0d4eeab09a0039f8f641f50c3f64ea2bec75adfafcffb50a19776af53c3938792f4e1940667c57cf5b3b6939dee54e1b5f5b0b0619bacda101ae8b960e3bf775eb0ca7a07c8e0c4e33dc6206d0779c09c01044800f75585e9209f23bdb0415651b0b3f906a383f1dfa876d5cdf0656b10a5839fe464b3383b608f3598b31ad6f6b6f64e03381b06287e4c92d07054da78d120b10047e9fd701c9aaebcf4909545e517740ff4d7d030d4ced5950fbd81f14e23348bf6af8b80ebe7a93255b9daacc50818075294d03d3174b27efd7ca24a61bf5bcbb19055b972034aaea46eab8d2b2c7b0ec921c7ed3e7c9ba00b84f3204700b5bc0003c46e7ad9067799ef7f7a385b650424b0e4dfa971215b837fd97636addfcb16356ac6a253cf68025361e3d37bd600210d3edf51ec8db7355f6d106700cda6119a3d6dc2ab10dec86332afff2ce8035a7423a15fdfcf4a0f49f92e9c72bd8a75093cc24bbabf3c24bfbe449bc14f04b463895d1590461e8e3f53d849c8109c0ec6001bd0b7497151b52d76bc38ff05d93c53143d845332e1f27d8a8c1c091fed3cad1b9d129dab169652f5cef857013c3c90d03c5776e71cfe9a3cdddfeac5ce6eef49066bf48a528f051d2aa7eb080ca5684f2e27c777afc969e2a48fc3246941946f50aacd9708e4d06b46786509f403f3c4e5aae9841e6f2251b173701398a027e556c82249b0ac9dec57a7640650d702dd4610dc51009a59ab4638d123440c114aef360fc2781ffc686c29c6021a1df7bfcb64bfb2bb2840c925d7263cde1ac9b4f4dbd79d2944a2752846dc030d206253da681a3e09c6f1cac74266896c61204c5864e2d4e28904d665fde2033962faa9a63ddda4c3dc7d717d7f93cc8f65ff8f36727b1176e446f5a6510e036b586256f7b50e296fe4673c1b15a9db6e1a98258d68bb69ee81092fd3a7ee05d946e5a5842336742e19bfaa815d8f93b73fac38007909e8b557ebbbc5745f0f5a50bb3cfd02d9738001dd026a10a9f2b202d236eed921bf9fdb5c7681ef530871bc59124877c523d1518d3a77d7be931139e0be71d33b0d729f3bd75d058c0dffc9710aeeb148321d0f18a5427d4ef68c30ce43e16876382be47a5a5c5edb06cac6e902c2bb00cf9cf9dd56336a6fc5f031ef4e0385362df48454090350b506d7dfd4bfe04268132ee178497a711b4db923c220b9a31a5304302b6b96ec6b0b649b32cbf1301cfbb61121970e3fd2391a7feae98805d6066811bd454da8b1060033042abd2f2b53bc75bb977a30072ef05a3f5c17945543d41871f93584e706ba3637f08b5cddc3cd4ca8d70e4a59fb482126f8384e9b8b95e361d808da73003341b43045deb9836e253288f76ee9f0190762b6c46f836ff88441d9b7fb1c05206df3caff60dc44fff00fa77acac166e9be1defdbc04ffc6554aaa9ab08890ad7c22457ef8c7ad4e091c95eade79144d08236f379970dfeed0c653c78fee40add4502379691263ae9be39bd25fc54af3fcbb70411a352ca879eec6fcaf62a0889f3f5c9da3a616a6dd68a81732ae87d6df4235d84716978d9577099f5dd4f03bf9c60c4cb30bc379a13ad6c5f9f4473a39c55adae2ec2fdc0fb34423ca4350e394fcbc1bb3fa928e97d144f849f14203e72c67f480cbb6de2eaa36f51f3b709d6be334c524316f942c8f597586774a3268ff0ef779d80fea6833c102fc31d0d7f3b8601070861ae28f7748bffcba3ea885b65349b7f99884bed1490c41b4a0120cc794996716e9bf813daea8b3442308051a45dbe4e1b06fd359c17c889c605ea65ad30c17b0b82ccab36d9e8a5c2d26e8ea07e4a50ed8117928159c1637d0956c21aa0b06ce40ab5613324512e7780cac122d6b69464c389c51eb16832c900c2cca0041939017df1e6ec9c4e2c868583ebb8060b28e67b7fe7b83c66460808e363ef87db0fbf7eed16a1e385564cd832e600ffc38cafc5612f93701da9d801387a307241f56199d10718b1db36a0639713be4d68746505b5d1bb5757df050325173db758928d6ed9b23a1ac2bc7a52a4402e97c83771bacbfc29cb21c3bb081dd9c683c71de5b3c5d6f395176adba7266e31271b9e58ccfdde8c89bed43f0338e8d63f40cf443c3ee89e8460fbc4cb9bb72cad23a7d000cc7db088bf70600db44591a72737ea0df8f53359d6dadb312b5ecfc0b77f1f8c6d1ded6c35e2d90e4f71622917cb732ce64faddbdf3deeba470f9e82841a5499e6953bc3ae484102eedd47d733eb6d9a47ba05e5d298ea03eaa12f653a86be1004b181265a66900b3509e212309034247a79eab07169a7289352e2ec2067f0f5d09e35fb607c170689b098c88314264e39b38449d3d972901eef84d4838ef49c4b9116df6815470caded67ec4be4f84f58c2978cffafeb94026ee7f05191bb3434627be09033da06b149dd0277cd4894f08759311c23e6754b7ff2ab785e48b46b6d972f842a0d03402d4ba1796c0e0580c07851e0062164b97a50d85ab1e7010b5d25d81eaae3066ebed73688e6a7b9d5d62743554b5fbd7845b23693d4ff2ace70296683b28501212353cc274d0b977589302b7d4acee0d29712de302429f6059d29a1112d2009a28acb4ad863f9e7ddedfaa8fcc3db6da297aa34b8775a2fc8274c2e56a4ab063ce504e93e5863380c552d7eb52b525ad6ac08b67cf6b48ac0a7a8961d8ce20abb6d9603c274dfae3f3191f9a3401c6e181e4014144b3f55f8a3b5dcfdb33c0d5b60427b5511af2140e634339b4e801945bf9eb8de5579489be116125ee0110792619bd33ca121307533fefed17bf929ce4380da6f4fb5a7fde2beb54185040489e17ca9754766099891c8b906bc2ecebecc33cff3251d6f4033f2676325790dc589ebd2365d4b422b0c4d587ed93fb095d522fa659f11b65315a1d6c9d312004378d7d0c3a1a8a5a82ec9699f8b9c932ffec23db9a6e807076959ee7415eb0f540613e47050379c73404fabf8f9c5af10db8c633b4cdc47fc08e12d332e6d0b24642a23895c5d14bc955509eb2cab1d4e414f5c87a7e05a1c61c73596639b06ef0a899174a7ce5acd5f6aec2d7fc2684ce1f2da046a8af1bfcf641619207906ea97b3e3e1e9d5408fd08294a28d57d5d14c2a4aceafe7ffcdcdc212bf8db90b11c1ca167717966883b4b0780a9c42d0e2dc3d5f47d33f747539506b243a8c06fc96296be1447153d685224f72da8f9d0de5a7ced8f75487ecc4f9faccfb25032522f9a1c858a3fec2a51db3ba1fb1ee63d4511a256eb39f6760f596d93fcd03704128db40a53726bc6b763884470b28ba3e78dc8d58b12c875667e10e391c0787728ec64cc05a67b44e8c69d1cd29522e99a6ba6a727dfb76a38a63e748060055d7ded19264fc34dd62f488031c6fee0991ea5ef33998cebc42c70c5b8af50a324fb9502f31709b8af3b106b8f63b60f74546efbb2b600a98bf3f8a4454140afe4ec4a307b3c83195cb6c6c8d89f7a62c5b83308253594a927617df03ba250df56cbfb2a9621bf5bda5a2eaacdfe41e4e2085209c5fda5ff6a822cfcfd57405b410c214d9834bdfcd4a7fc448239fdfad53532abb97f05fc6ff64563f53c10d3d0556da68bec907b1b1a400a29d0b8c72248fe254c62bc23131588227066d07235c2c52d54d103999cfc89a0d32093ae2911e0fb48206289f6e6503dcc7be0b54e9f7798a431e7986d5b558b1bc175f6ac190f9079029250b838a8c2a72a60e9cf3283eaf7e4c8b6c4ce992d9b668182d916e3461e5fb5b3cb09d9316c29e05c9039e97e21a199eafb77965aec224110856d13e6b77e0cadb11a556810b670dfa3164f5ec79fee4b3fbd4cd6739b8a5170ca839eeb830a809648204f31fee0daea9c5d7009812f0c61b52e87bb23d9cc7ccf2e902b84dae1533a09934b225056c705e291617371d43bd62d883baf39d169dd0f06d9a9204ffd73e089c56d80c147ca9a7845a55e3c9d932377064ecb0bb8ce128e48d859df3d0d17d4c81120d7a0367eaa1bc7d7a64d4b7ff7420c2f70befda9b50b881ed553f15c657e3ac05141600489b96ba7b474c9f09b37909538d9093f7af2464f235218813a9334d009a1a66aecf59473c5fe29b27133ba7003ac6293fb53f75771611a3966d4e1d0414bc9e64d9b07f4fa8fe32f07d59bde7e5b0c4904a18bd3a4803d2e1caff700dfecd103d523b4a0b1de06a602e274a8797be6046e57cecece65c5fda197b29070c92feafb52a65fe95476bdf67734bf00aa47b09256e50440620cc6b8384b502fbf84637353fc9dac36849ea99e6b7462fe9d27e3a75912f9c05ad14891722037ad00e7fc8d99715b93223682afee3d3381425314a0fdfb9b26eebb756374c08d3a3e166e7b5d01e8282dfd27e12b9d26ba676e5ee8228884db38435a352fe0a6f061811ad66493dc7fe38ff876bbee207743e2c60e039aa6ae0ec24e0ff4500f8d1b31d08e3ab274f63e60b8ee0e83f69f79aa94af165ac5c70c83b8007000fcecf256a44e769f4a2bee7c42dcb407c5bf1cd129891b3370ccaad0894896c0066980643383548128fc4f69de926bafaa98f61959c12c68882d46249b22d21004338dcd0adeb8611cd489267d50afc5c56107c22cc0270e7b003add74a55700d70789672f83cf68cc95e633a91cd493bed8781a1f66d180abde94f58ae7dc5084ad34c34772c0207458c1355196f0a388b2c3bfd4a4d2d38f37253159a529301544acdae88c02dad9dc21059a3aeea055b0b0ff0f769d20a8a93356417c1fa068a4108f3d782a1986a339127c71cc27cc29ca135b6b422b0d08240a08698880befbe52c9d0a75d2bd2190f8e8db0fcb2134933c24b4780d7a6b3496e97c00d0cd1f6bf42fce713bbf15c2cdfe3e6bf06302668320da0074fb9e37ca5069c6503c23fdff252f12d5ec040db845d2509922334f33b05a58a3c3e8bacac5878c10bfd83a03a7e82c9770bdd434e4f6ac6075475cf8b9a7726e5485bf166d276f60424bab2c3af71d55fcbcd59fd6ff2cf537def9bdb85303ffad1e51b784f3c2f0045c76e1559db5f9d57ba67d066e5ca1d1553e5cff15d2277d0626d9408b531081bab0759ab3c9be50c6dc1b5385a95875f4bb5e2bfc6cae968618f2fd66b2a0f8330b4c46b1442ff7a627eb12aef9e9a539798b9d5567bc3fa502699a9e2f3022c62adfd3aaeaeda9dfaa310da825e5086a08b0cb4cdf5b35ad591e831068f0aad26b130f3e1dafa1fc984ac3b517ee2b9b64a92a6107a6376d34c6b66c1dc0a209f5182f3f495cc33f9025573562df42dada1af3866324297d5ee14f2c9fa0485f88136629490eb8e422533d2ae1daa64cec264ca270f970b4428b6d50f150e116d6acea9b99e05415ccc0c4c6204b53f5168600441800188add76910684706035b890b193fadbed82a27f5a9269dcb1ff46c59e6011ef8ab17821b6ce05f053df0c054829150f37a3e0990af562f9ab0f8cfe7fbd701ddae407a609a406909f078b56b9bc9a8361bfb82092427d05d354705f77478b94eb578f98d367b7402f27370e5f5cd30e6ecb57e9c4759b8e2e569a0404d698f083b32ba427fffdb0decc08466e45b772cbd10848c89e1f49e76bd9c8dbf4e04dfba793fff55c25a0ff95785abb14367153b212b67794d67857bb84c6fc4099bd1efb71b1ce26d4d0b549c60d6d6027d4c22f9024c9f7b9583891ac1142182f24f30fa7cdad28c1c03f6a572eac2261f80569b62969d0579ee9e2e2bf0e1700b847103cdd6a8b7f002ced4a9ada1556f89ca9d4878533be0d3671ba63a6fa64ea32f6716ad883c7f0c927838034103db78a89a1fbe5f7a86b32635bff9c40c483173c3d259bbcc3507c99963f3294caff06dbaaf2a2f00ed388294fbd0a57c425135fd5a6935147e0f8a038462d9d7bf58ad8eacb4e560051fbfdfde1c06f9a504735139a6f746e7083ce84659a508ce53be7c39f31cc1d775d1c7bc0062dee483f3a4493928f2cd06cb5be0f7af9f579f114d8c28bbf7af5ea918ec10429a3de17d2248202772eb03ba759daf070f696b0db86b92b9687209f953d15adc06c7a3fe4e6590d25cd601d03639db21e4bb37f35786d62822149d69aee452755c3ed1f87e231f62b7d609eb4a96a2962b330c372185db6ebece228e53baea6673c29a0106903f491d8c0bcebf934b4d112c5c8ee77d7331c5c478af1f0d5f2f161c067bbd7d4c561f160a6c177783482252f25888ddb5136ff2b307ffca181d9eaa5a2484b87565c63809cf401ea574f9b25b322977259687e6cd160cba6d58a27ff15dde9a91b4ba970355f16404681b0530bb6e5f9e8fe6584c0a2f5a43e9ccae2a3f148c8a00b1d90cb15c135a9b548acecce5ddcb587940ddaae16d758b97adb7f533c9c516404e05eb1cb0e25864187270478345db98acd67e35e5120138b47f9f68d92288b83502b7513e7a3ba7fc8960e4970aa569c0cd3000f86a09ce77d74a29a9bbe891b3004a4924816c34e2f2dd4863eca86a2ff0fa81db0623c3445c90831ff90bf6da0f1935b32600cc7d8464c75e785cb43a131e99f15af72cc5753760079caf12b60ed5365e9647d51494c5cfb4f63a20481a9d1654e4bf3873e3cc75343d18a7d303c5575f734496be0de2901938108382a5c2735eea94584dd8177c336926ce320b9228e356633c4b82e4b0392564e26830db19312330ca99cfb812d93c1e8a4b08bfffe2082f923f6190c4d771927a457003d4d2156bd217d8be9c269135146102447db1ed0562d9aabbd6993387dd4f7ee8066e7077e13c74e39427f57af3770e7e4ec10b8487d2c13a3556fe61163a56fb077d3c0ab42d284a9c95a71d988b0d59aec121c19b2851f4c7906afb4571270ec75eb3d034e8fba1e0c3ef185254072c45207f7811fd9542a5625df0e4ab05bcbe7c8c663ffcb22a8362bc72325d0630aca13ceaffb00f4ba37b8120183f35df0b7dc063f972c0eda3b2d64c7c31075d8276a40b9dde863debbd55b4eef091a81a626f61f4264a83162d0b220a4305f6837ed224808d59855dd3135c5dc405487aa439437e309a272ddbe3f4af4103b43b57d176caf42c5fbd3d31b718276c20a873452be5f67c2ddcd233ac84ae0d7262eab9811b858b4687c5e1969b9c7ee20a2697c6e3867df677f67c660243062aa3ae1f536c5e480cf55eada8a37d50d46773eee53b808ab3f6e1ecab58fa001394f2769900cbc8fd9f2bd0776d05a2b886deabc705c4a34f0d948e32c50f08e15cfe57d2a8269c948df4fc59e8f3cbe493fdbd860b2663b2569c095639b6010ce5afe357b385b44b8d788eb6e2a83f7a7924ccc6417d304957123364935b02679fc40d3702b257ab7c8f61b8ac1ba513b04465d06ad254c8885bc7048c6809995bdb3414e744aba2cfa32056ba0016d5d07c0170d8f8d542760c97c8e9e40335b6b5b98e70a8cd71b526957e923fdb58f4cb87721a605e324efdeb8fc072091162288c2e49bfc8cef44ccbadd859d4649d4e64cbc44aec433583959002910e850ef07074a093c20c15a290a2a82ae9f0911b4203dc00cb7a7f45ce1cad7409a84e023c10e12facce2b1dec01e0429d7b6b9e351254962b94ce37163dd2500b95da63d5b96d8e942161a9f642331444833dda3c57a15c2ca80873367ccf72075b1b51688980abd0d47024bdd42a03e95c23701a2e83a749fe7859c1bd492d0d1c280977108731c9d54d3d2c6f38758b535f0dfd3a5fb35d5b15ba8e4851210700e19315a3f059b07fa459622ef478f6652c00e3a2c388642d68bb41e6cd780ed93ab74f9989e70ad2c0fdb6e20d284f60df0db7920828a37bbdca28139c5c0ac3bd7d04ec9a8954b22c2d65f53374a5da5c950fe34111174b192fa20725df0024d1482429e6c5085a33008213049beb8727ab9ff5ec31a440923e58f8e5480770a6540ed4e8873dd655da8535c5b9b42dc6e12fb735717aa79bf70c758bdb029a5ec479d086db7c0da03c6434c5842763e9529618e22aae393538108409080f24ad43e5d5acac1f00b2287a82ef2aac2f15da62c4f9e3d23baa847a5816c601f810e1737f30702257ddb1708b83cae0bd04d1a663e17534e11c6e6ec606bd0c51a5069619fc43a4bdda0becaf45e3bb8372819eef540809bc1ef1f00e1e37045146ac7f8cbe265a8086dafcd84db9b11429c367cc0721384a70025d3923250756b4a728d49322563217cb2b5ccf8284d20ceebba26144826eb56c90c6791d0071eb75c8a990605e34be4829188215ce0ca6683ecabd0a41f39ce42408d7ed01fa25f299743c5f75da3c264f7f7415f1a480d91becede66703bc20beecca3708a3a7f51043def0e16c2583f69e881418984959dbaac681cb7aabf23439315105fcea0f2c7707c6d5797a3fc109b12299fd17b19c96efba3cd6cbeb86d3825e040569ffa8cea2b2b623d9a75c1b47c6b8c490962f5a956cea7ba7c47a06661900649a84608063a9cfde2360ad4c90417ae06617e8f13c154716041af68ea7cd0ea63e2772d8521caa1c7ad580f0e999d213c63498849987afb27a101018ccd60ce648df90fcad3a4c23dd5626133f266e2869feaaf6f0ff88442463cd77a3b60d2f0a52b14a5970dfeb08d769fd74eb21f71c13d356b6f76771481c9241eb88025337b4d2cbea498074adda44b65b62aa6bf6ae876adcaeaeb5c8367919b5a9043165c8385d6166bf013182b69a60d53021042513bd03615bd636d2c5d261420e499ccfcd9f4194a6d540aab5abc06f323994f88d7b18e1e7f4fb57feb265410a true -check_ring_signature 6e8edd8c6f32d9be04ff8f5113a165735b60919452e14983682f39050354e691 1b5b58d6536f528ddb5af7d000a9054f047a0d62006c64b24898620dac50aa46 7 756f730286562b31a90a7043245152dd56c35ac034d6c2a11973d65e531c689f ee7a3e32aa975422019901a8bea3799c6e9f27c2ae58b02f7ef2d1c18065ea19 31a3664845707b5ea9f23fbd3c83b555561582b60889bd8ef3da492f5072c184 67380de7dd67f9dbf916c1d31cc433d5f372e1074ca78126ce62f37087a4ef87 c5fe9958f1703f84f5d59e830b967e72c07d9481025af17e05a85654ec1a780f f83abdd39b1c7aa8d4fbe6efbbcff97a9da46811b55407baa9c796f161b40a01 c205d2d11c681bde56b63d80adec8e34cb982aabec44a5c431783d17930d2582 3cfe1bfc70827363d77853b3d6bacde70e951d52e12e7c8444d38a0debdc0b0e21648983e97dbef1af0f2c5e1ca679d2d6ed7ca29f1888b58fec0c01a86de9004db278db6c573b14c2376fc2ebdac4428efb3795a497ec28199bb2b316ad9f0d521fb3b23a3851f9f81a11acebb2cdf44ae76307d93b3a3a7084dc0a57e35e03d4217ff8f958921153cb18ea9508f9d36fc143a178e738c9457ff55ad26c6102f64a01b3cc5542d725353584df31f775f54c1f58b696e37bea511abdd1e106074e4a5e2853c4081ba2cce544d8a6d8eac2756f2cf7267344c9d6041e77835f08dfe364a313bb916f7da1a60b48f2b739f28e8d14a60c58082eb2508e66028402202c462e00bd60687cc51fef7be7bc117ef6a99a7bbd1b3e7351f8b450e1a608dcdb1c95457bd9d07b719d7d2726dd82cac0d9b91184f109275f742fecf45101ac1f047cc9319128d3ea7a0fc31d7fb5f099d7f21d6b1ab7203d6ac5a07127090ec09b8fe78aa20f5c7f87f40c0702718a56a345e995ade13a2c99a6ed375eaf99fdb976da9af4d87e9494ef24e1f33d82ee69ac7875068ee56eb9a5a4ab419d26d90876077c136aca4b9346b2e9e47b036784d1ecdcb3a853741b26b2fee806 false -check_ring_signature 08fe3dcfae84e332cb3ecb2f8c57dea9f0302fcc44ffbf5bc1ea770dbe36b69f c645a53a812279c592f37862c19838ebe37e87fb32f7cf201a1e7487c1a4c1e8 7 0b0b244e515286dbb3e85db1b6c4c707789e02630c3860c8cfdd034251496bf7 3207e94e0a6d0905841cb751506776b7d0c70f60ee310e9abbe9412dc83e4b48 bfbe3ff6be26426dbf712c90432c44dc7692d6f27abdc39302ae1e60af104815 102387172043a3ceec2bd9699f8db317a8a7a13125345f747659e09f739bc804 e187533ba17dff7518a9eabcf9675fd58ac4e8cb5f571feb9de25d70a16d3691 ac3e7ffaf5cf6c9d882bded1ed868e873580a03b6c3345709e28fce7aded998d 07716034ed30af4979380ecf6560eba8e19ace81146d651f06ee3eb107929f18 3826bb9498602d18fc55717b3e30cf148f48dc8801bd662e9f6bd5dc003618070ee4fe311836847fe66b9c7b7fb80cb273261a728a3d861dfdd1f64fbf8e0f3eb04439f2513b8bfb621f5089309b083ad7ecf1957cf51b2699fb52ed9df8610957ffe7cdf42d8824a6b623e86d756d15c4ad67da8f1efda13c0df8ca681bef0748da651b4728e12f8e34f20df359ef05d4a3861c90e026c8fc7358e1643276019622f9e0ecc85e220f222838d0129e5a2bc88b107a20b098581d892615a51f0ba401ff064a622305c249e0ae10d606d65dfda902ffe662656b70d4c80f439e0eeb10c08b49898cf08d633a703a1b6e268d1973ca5e28b6576ca14251c2fd6c047b279868648077f47b3fd7994d53ec3316cef0970da51c8d4bf8ac3a825f2f0b5080a9725810d362fde2531499eb6b972dad2ea10d31938a9db5eb6368dba800e2330bbe6c72c840b27b01af6e9cfc1056397e7ab418bf637e2c7208730dbd0a7e080c372533674a75efbeeac05ba269bd4ba9460642f3b92afc21dae12b3c0b2e8039d6187c2ee960d036caa3f55edd5b386ff7527749c7fdf85783a42faa0038786e3693b6b7128a3a789479d7619ade3f0470f7393caf7ec6b348f151630c false -check_ring_signature 6284352f952eef235df2dd4837a6f9583ac04a56971d2e0b0f435e4bd719da7a edc291c9921001af9bc973b7c0243e3aa99be6c91eab58f4a3ba0eb96deddecf 8 5e040d0a7e385d73c4d4dee7ca625215f865e62afedaff905509e43c264a5dcf 907285665a46bca85cbfa45f6fe997f9c59d89f131a685c7502be9735bd40854 a11b8a469d6ad8daebb77e944b0c982f5e675755a2f6f7cd920b3eed16befe1a b90d16d8b799874ee6776e810355b6b04fdbecc53952c970d09a374021c9ab3c 3a77d853cf3a5c3acde950c3300a6be2b9b59036bc7723e7a1cb98ecf5dc3530 a1321fe3e5fec1d1a0dc1e4bc1ec92807d078fe41e55c02cb6946121f944de3c 34127cb3cc172498d20352e5427045725e0e215bdaff1dc1a3711d9b692b3fa3 b411fced2957a2770af542133683f85c5c1767a1ebc38c68f77b8efd8d988440 866236f72b1215d3bdeae69dd7c2633cc89de1469dc380a7017f823e83b1c2035249d9cf9dbb0b3e58366c2914464df814b72b0e14898476780dc87253899c0b9aa4e8a03bc67fd93896ddae2a1239f25dabb0e3963c6057e76596f6a7dc3908ce1df7479aa218291eb907f3a091df47c657f05313574a882f0fc66531496305dab918ef443c460bdd12ab79743a4d723dd01ffb3c1d351814493f461448f905a06da9254c691c3cc26f459ce9b3fa55962a20b6113d2bc813ae9b1076aa6a0f27c839aca1355e2985ff1ba1bba60bbd9c07333d908e9b6876a10108f5d63901c544a4426382065d22593fe6db2678c32a585ad996653426baa94ebe4fc01a0f06b8e7a808052e07444421fab762d43c3ed4a344a7225fdf57876403eb4a770ebf37a5400b6a6a069380ee711ed642cc03de7d69c3be08959bb2cf64d847280e607bee40036ad56193af2e0752d6a58f07e1754c95b1d433592315ead6d56000803cbd2d87f5bedcf7878484d4af8c4f4119532cbd423dd92300dca2b4a67403d986dec9cc3437bbd5b9acfee1b2ffe3546e24025a67d07ffe88f06257330d885ea3d6293c8992b540e0489fe1361e6baa326f7fe4dcd32e0ca7fac09825be0a7d6828443652996da4776663ecd65ff157880aa8b481f55fe3ff9f45e5818f2d7c114820052fb4e7458e0d7bee05fd0fdcbc14a50f10936e2fd6165ea096e00c false -check_ring_signature 75d957c00fad4f9e7c115b65ba14b8b5b24c4b43f1822d6d10bdd3e75ed6f9bf 3aec065302cd5592389c6677f899d385e98bb5e8b1c15ed05a2b9b234bf415a7 33 4528c87fef6227faf596fc6b817e1fc9c26f9c63ff1069f8fd293d05ffa2d0c7 5774a7172765357a90dbf8712aa022a31564b262497b2f01771e64025f4a4a81 954f79796f465964f1fcda4963d0a3591684fa2c2b737e28f04bf723bf1ece3a 2765d84f5e2da3902b8f1ede7567d1d52bc2e0c4306c989d3746aa51351fcdf6 f382ee65acdaec04133e9fbab360f066f5537812d4481171ef2e84263f65214c da41efdde540488725869fb6851ef07e2a799633b67a99bedb34db41b09019e8 40fae2a6922f71d95854cc04267ec3c71d2c9f1b6bf838a6f2da5b300ddf8b70 9023775b0d759cadd972174a011a116e744e65ebdf21b864d4d3d7789a77b88e f642815fda4b942e8f3b9eea9830241581840a1c17e0d7b45b7d14764ec1a602 b782d7f3fa6df16ad6bcdd905f041164b0d46fec37180e00944a0874a8340446 2779a6c1505d6805780657da8830db5fc0a3e25d720b3d1ac8ea5ff2392f25bd ae1519c32a685850a32da889557d6fc357ac9262b2e9a64f9cb4691c19acab3a 6ba5d582d2eb5ab6f35869fc093be9341a9bd7db23d840c0ab450189ecccb19c 3e11ceab1e01881fdc68cc6868d2eca1d8b3c2e2af228165336bf10d097f07ac f0ce495a056727b60b26f9f86fe47cf3b434195fd6c4f76a3c5ea74210beee75 e7e9bde3d1f466613a7a4be9c4e82042b5ca996582a60a615c2f69241e2263d8 b1ac94fb567c351d0c9aa295f66c425ade41dc615783aeb14f6604575c90ca43 08ecd2793a9e7677d0900bdcf86afafcab5b350d8a6413c4af92656919f241e3 38e2238abf3606baca956d07a9bb500b2fa2ec63c4f8cd33d2ca1dd30f84659c 89320ecab749d5a9f9fd59241f9300b4e8b36d754e48e3d8526d9c96a2e60b57 efdb2e573fd1f70cee877925c48c4596383c60e193668ca76329352d9bf5e37a a2a5a2095d7f2a0511595db497efcd7a02e19934110f523dce4a415c7e327cd5 d4f07a1a6111efa9bf13f0d12c187cd46a7a4994dd8d91b314ae4cd9a311dae4 275114d547f6906c3d645d03941c12b52d7f3e38099a9b665c71788415258948 b4dcd01c4d39c8a00277f8d13766fb91c735b82b32d3882bf91051ba18078a03 f7c210421fb721485468e13298b18f3d41c42da83ec8f0d2edb216cfd3b075da 9d4d628ff152b53ca92fd941f1c668b6622a2b10999d5f5f72328d0699daf655 22e108858949f0c0bcc15307b63a8a38ba327a6c2f81e5faa29bdaa8ad2d78d5 13cc037aaab54c5828023ad823024f630831b07f7591fb3160b4ec29faf431ca 9fcbe5571b689578711429777f03b8e17a15a5dfb926fa8713e5d78b73c2c325 c11912586b1869391a4fb48aed477fd737695c3c6664df2589c7857e49acec27 206595a4f1c1ecb89265c98c968deecf7ec1c0d049e4a9abdae02dbc7b529276 1e34e2c5342cbaecfed877dd9501a17990ee6e5a4c8f134a3c3ed297cf695da2 5a0c068e718b38ca1c1c59748602f078e4a02ab1412c382c27a9f3bb5b4f7d099e0f6db8c5dbee75b76c816e3bc01cadfbe3df273d4ca3761e6d8f9ff6754104e1842e26d10d9d3847207fb0ac5d3bad846675b636dde23b5fd5e4c36024f90a8865d667825e89f66eafbea198f14d345ee3812d01b4f03bcf23cfedf1b2a60089063ee5e240868e69ce47b3877729f3512cacb02c5d208d0827c89789d38b0a4f6c810ddca29746e2b0fe3de67885cd3eadde6e8e3d2f47c25d883b8cc7ab0081aacd2a7efd10c4f04a7db9abe3afeb7bc8a4c64acf00a2cc9873c3d8f2950d4be8d4ec97faf0b723f5cc6e016e68ab1c1b816dde517788b6d2cbddb827ce0edbdd397b599048c339b1d5f05c44a376479337de87bed08bb1464b18808a410b6e9b821a51c322a7f1363a06772882dd831d5a4f30d5a49883d8b48eb2c25a017e24c44c405f4e2e1b469edc95015362812e4148e89baf5c3f3d8b487319160ba6833f5aaea2252904eb40d5af43cb8c173f669a4f2ff452713d06392b3d930688fccb528ce4f92cbacc0c514ebd35b0df43ab301e228f8ca0216883c424f90c3eb527fe0f9b89b7819634db81182605e408bc24a2194d334d645f996f8b750b22b26e396d1304d85e505dd484b233befee103c9814f7e87551d32ba9dc8a50fe5f5cda7b6b36052847bad5d7578f89fb47fc18a15f972b1871e4c1f79856806ee39971591cde0ee27c3fe94ee64b3e1f8f2c5b84ad84bb7ca8f5963998d42072f34f65b3ca80a09ab7c8e6bed9abd73b03be7527630ab1d2040de981dcf090f36fdcfde6485f595236c47acad44c3d0dfb9a335d2dde9f98bc85a37c936b4068a5f599bcae77a9c54b94733234c90c21f235f0c75963c916cdb8043fcb51c068e695732927289a3da3ef3aad385f5aec5648ef5353466f650e55f0253ad830721d79a6d22e41b9c655b5b7a4c2003acd109cb7a4f5f0d3505fcb835e6853b0a94551157f0a7eb8ebb0d9387232ee7288b2e861ec18fe9f54bd93a80925db008879a00c7f3291dc6f461d6325a4dabbbeefacc8eaeb7fccfb27a07accf8a520b8b167b0ff4b72896add3e71b67583c0debb8d1f32ef485035356576782ebc70db5de75e19eecc85dbac3f3d8fc1e3498eecb4d47e2ee5c19698d88660ec2e402b349de6882bebe682db26167a553c7b4b430f36bf12fc16436c2801f8c5e7c09fdac78cff90ce5686f432dab5cf42996db5512b2efe8cd09dc12d87ef86f460a8caf1cd47eb05797a09af371129cec22b71c07ed9ec7063435986a397ffc1a0f86af9ca9ea7e676d1d9b82de3ccd67a5c186d9c3505e55c92f369ecac2c05a0621674ed0f426fc94318e5a64a8053cfade5fdd1cf450f0fe5c38c53b29de2401f8d7028bef141933ef06f4e226b4b20d3f419a618ba347c07207c5509267817f71d3341a1020f851c68c93d0173edaf5acf19eaa74080113337de750b70d0a0ae2de35f1bb4ca97a45fafca0c08e8b740110f4b4cb1ab0def81990a7b940f20a73d4af24f7b2c5ecf36bedbece9e62dde33c771a8997fcfcf8b57e79d877bc02f13b39a5f7f6f3f23dd30d520be2a6e0adf73088128e2ee6e86ecf69146366021b2abcd56ad0facf31815c58e7e8454a76f25957a95adad8a318a6c415ac5f0b639842361d57d5569b619d576c317db8b553b1c045ddc5f494df6f67e25860009e9ae5f642b3ebaef0d84311aae30f01391757926a141903390b053f0129b80109daae0165b6791bd70b8595643b4ec752c4e85e86f3834ae15af5681870910056be805e60c69888769ff9b771a32d6afaf4febd6d0f3e632f0252bf2a52800f377af6fb2f077d037729bf7b3d6f573f0d35f6a43f9ebc9216387588a72d4c0da544203c3c0ca4724a6f06fcf5b2d0b4acbec8a1b399170caedf3cc0605d4e046ffc26fe5592009f47131425933e80a54adc1ebcf4fcb71a489dcefb2a58ee0b849486384238c27d894a9b45d3118be9080f6bdf2acd32b6b5a39b0dc19e8e019449620035aee8deeb79afaa87283b90947a1db3c7584e3216af34d8065a110c600749177ecc8d316cb8eaf5f8820bb42cfdaa775c4565363649252ec75af30b537bb0cba083427a224d7b1b8ed24fca381ba66eb8333ccbe90f78d2e26dc8004e974639572615ddaae606f647b5fda1941ea6b05b20c21b19d2caebb68c280bb40268ac8b8aa7fe76d3010a8f925efd11baf3c2cad629c7cadf12069865840aee6f20ddb50f64ec360179e145726d5b979378800e0f9c3f9109bcd276febb0680e9c0a568c818325e1dd6e7e9411464aff42bfe1c23319ab87f30646a887101ab8e57ec73b9c3ca5801208b65728a1a1e33fd722b6f6c387bcdccbcfcd7cf07a5bd8f6b982c4c503044e058986d229ca3ba36263fa1028e8299d1b96d36ba0156c7c5c4fe08c8bf4c17e3f90220279473f260190d6c7f41b2351c3c6ebc3306b31d8dc5b5500c9ca8ae9f8e9a0eda3804003ff5fb28c7746c2b532154f27c0d96a6efb3fa301bd12323559daf3dc03f704ed1376b18a7fd958f7fcce6148a003ca8cb76d2f1e834ed6e958e2e09dcca5d1479370f1dcd99a9802c4fffc4c100edd6184ae27b4c9668e7ae9246f60e2e4d03ffd0be63601abe7960a17d49b40cbc789092d5337ecfc33e52c2e89d765f326a33ee4e75885fb181dc7e848df20f9b01ef9385d21cfed83b97f337bc3b8ebc7b7dd78b3f68ace9be1a22d53fce08b9690a88519c011eac9f4a703893e05920975530d8f9fd45e88e762bcebfd207a4364c2e44ece276788e958f4bf0395538f0c7087463220f179e289112a30100ec7d8412b410cdc1893598d84c788ea289d489034ce96c9267925d676470b50924fe35bc622973c6a74b5ff62d9422109c9aeeee5da61ab81c937ca3d1a73004859df9ead59a2ab9e553824dbf3c756669d0487c2031c1e2f1e5fa6710af8907 false -check_ring_signature c63ad92466e019bf27a46055b953c9554187e7a286c29c2b7d183fca23df2235 dfeea439b093c6caa642137987d54423c6e3bec0b85c142c5cde8d241b2f3ffb 8 d09ea529804a7878510c9596c26475bcdb294277f28b51771a800b9b19d0b464 1011ba53b4f1bf703d99533c2403231036dfa464ce020f5cfd0e775da9200f4a 2c6534c6fc812c36fa22d941626c70d26d6c1d7185d13298950a4430ed93f68d 107dcf8bd28f80db3a2825ccca3f259f03737ed19f147903df2aed2407a88abd 35d48ddcc1f6b08b5320e39631d8ee77235c46b7e50837705455c788a19c1378 431a117bd8fc970a5758755a938233a6f734028cb3651ed78d7059e0703455a3 0d7f9d0641ac8280b196c3b54eb308bcf7f7d7eeafb0e3bc5cd429ec38af773c cfabe6982c427c65763f54a50b376aa7503a47e235d91fed12b389e0cddac2cc 43a3fa89c2a929f6a15e3c4b9e4242546e68826a3ac419e9d3dcaf8853e9230b7c542799649437fb87021a3b9c176235e13e268509b06db4833590d4894aa80fb1ee46a9cb719bdf6367672ae3a0b8998c6c08115934cf1f9266045869160c0a8cea09d6ad04c119c0399aa992c1abffbeeaadb27815908a21019a44190e410db9585bb673424d4c7c5629283d876dffe09a287bc4a3bb67fb50c55f223fcf021ed8ba91ab04540364f324066bbd8bd24ee8738e05722a7c3cefd7462d69f90e38211380ad782dfacaf3fa43aea402751ffa3c830dfa8a2b4ae088a61cb2fa090e060ae3374d218b218712f33a43dd40c8f5c78659608f9df597393214d49c066b4da8030e9dd7087466e759c7b73e610177a5805f6b002351c6f1c2bf473401e248884039548276241fc73ed20e8a68c203936af90c12323aeedb5b2ed6f40b537b91d3ed8bc79658717d1e08006721f07975e606124beb6ff4d73a993f0a01062951732672c6c37770ea3bf4347d4759c4b5a61b6278d1eabf0c1656a93e01dc58f2c988406a408db82fc327c55c3053167975f102f7d0f37c6d652d0ce901f7408c4637d145600bdb4dae79d05198ee48e189e8665cf18fa96e42ba61c8080e3cb72b2e21d9c61ff3bd7cb4d3ac3948e460a7a3bb535e04181b86cb391f0e5ab7e1d7788cb9383b898c9913ee46190e13ace2c264c7c9883dbdd487786d04 false -check_ring_signature 5ed03db261fab595dad3eb78fb19d29fc1e86637857eebc7fe52cf97dee02c60 8210d5c0d11a5b6cb2db50d5aeeec428faa2876d078553b1187bb60ee5849414 1 b63c19d1db18df762d4f0302cc505957d923b678af7efc9481c5c94b7b8595d3 85c5bce35f0e0eae361d5fcecc6fd4003ef8d3017eff775cd6333a15577de504ef89030acad85cd85dfef6cb93f263eb778f4b2f4e458eefdfa943d4ce34560b false -check_ring_signature 858ba8efa80317feecd3eea54033ba0cf3737746741143f459deaa558ea08021 27f9f90bfb9fded74081e586c95114c0402530b9aa886b03eb718bc32d5457d8 2 b03aa5162ffd9b28312b1056bbb6bfd79643bdf4d9ee5c6520d5a27165f77ca3 5855a98fa55a983a726d6080066346de699e805045a7a8ab59b38584a1a0b85a ac762c1074bee8d96631461deda7a55440752c6beaa78c7e3cef2505ad3ba00f120ce3e554e691adb4a1fd9d5037d791140ae2994c68f3d43b04fe616cfbc1001cb6e22e18b8e35ac81a330816e5bd5e99ee4496c00bd5e3c713c80a1dc6a48cb41bd6f173863615f282024ed49717cb516b4ffc955da990ea21e8353d0a1705 false -check_ring_signature 2c487d9a5b0e75bb77f1af92c5477a80f6aa9987c6e0052387da4758ed084acc aa428ff062fb41faba0d2088677bddab2f4ccd42d91157e6ef6f4087d2b03434 116 b53edca81d3169fa2fb9aab9cff3f68f50bbea3c664ae300722a20c15ec34717 7f7fc2e3d40a71c0a906254171737884b0650097b8c813c334c92dc0690d11bf a1cbb88aee06e9d3b1519ff3ec0cb4f5e57814dc50510da061283ec2b043f968 df753cc5b9c8bd1830b91ada1b268144e6753631aa5ae4968a4e3e329cfa5250 1e53687703c40ba8a39cb7bd43900d12c08cbb3148f1ff6ef5fba42660d45e9f 05c2a51e4df4294844bc7379e99d3ae2d75ff808b2d9964ba1a46975eab6b2ee 53a1c6be34438d439618175af0731128ae086013fcdbcccfd12a775c2633c843 12540211257b7c61251dcd9cc8beae24a8174160b3d6fa7bea45caecbc8ed6e7 2aa00cc986dc375240e8618ae5cb43d2f7fee28ef7a1fe4b152b0d2dd31fdca4 bcddaf9a88fbff7b03ec31697be61eff742aa1f1704ed4661a611a95ed9a17d7 6443617c319409c8b0b3192a84f04e7e0aba79dc33cfb336ccd9f41fb2357ac0 eccad9bd5f0bb069adaf459dc72bf18f84d38f6f5a15070005b414d42df96473 fc16b772136d7a81eba466758515370eb506acfaf55a4830377df2fa87aba866 719f5ae42b61e13a93b779bfe7bc514e83b1a7347b7829bc4fcccbe2710dfb66 59c31cbf0e17b413e995ffd2e753b2461ba83c97991ceccbc0218483ab7e1ad4 99165d24417504cf6c56cee8ff890476ea065f11d3a769de92b97d4ec7db3acf a5598da857bd5096a62964d87fee6cf7c35395ecc8002ef8912a65fde8b7012e fb2ab2f01897178c29f6907a8ef9e7563f69dbe3cc13287d57fb3f6995edc76d 5a7a397ea20c430f2e39510f67272995f5f05296d3d75c010c433e7b9abe9486 db6e7042a0a198ce998f8bbea3d4d0438f607d74ba3725d3633de1cfe2c183c0 e109334ab385c1c0d5a8d4b353ecf703cf191b9c7f6cde5ce4c069ca0634de2d 0af5112c22c63ff82f9456c66017c5fc4c9112f6f12f6c9e8078693a3278a9e5 a721c40615b71a8544feb158207003a4c1e91650f7ff93e05195bff22667ad07 797fac8235097f87a4e67387164db93bd7a4bbf414fd32f77b26ce51df7ebf2a bb1eebc4452847d4b1ab752593693ba0f0aaee5fd197b829249e63e59ac5d352 140b19a304dda6c2303a8c6ba7b3e7dddb592a8b728722707a56f7ac24119024 c09657edf18f1fa35f2b4bbed7656edccfcc1d74a66ac385a75e678c3cdc1d59 cf1f65cc3f37094103654df523417cbbaee0f367b7697920fa47a42520cd9855 d556a74aa80de9419cda2a302911eee8145621be4564108274079d3c70581aaf 9863ea14a59f0a4871c0e329ae1b9fea37f9bab6d7da0d6a65ab6806be05e63a 4cafc67fbeb6ae47be71332198a96a97a03029125eb297d76697e1a2abe24474 91ba964cdc984eb9e952d6dd766b67b8b977d88c238ccfa7113d9aa0a29e7eec cc8c7b1934ca15ab25fed3b256743917eced71257aa9450e850c353507e3dd55 d8c7c2392d45b867a02edb19adaf90a0e6176bb840418580ec1944c57658bbd7 2507a8e42788b2cc587342153b67af8fdf61731cfeeb5de452a3cab365448a62 74be7e885aa7a51e8cb128bd0a05abe4274d840a578f773526439281088627b8 92acc1090d328d7440f870ffbc17e46c363968d9b837a68fef2adf910e981915 7b66de052229979f815e38453385930f0ad1301db5dc60ece9f958ce86660146 2224499a071efdec8064da5cefa7c3e0029ddaeb2f64a57ac2c13669872c9455 30d9ef253a82d99875e7d7d2da038d901e2a947d16f634dff92df51e85dc51fb d72b9b41718f71edcffc0b59d5f75fcc174cca7c9ec881b71540473a5e2bbb8c 734e7187bf4de6ea48c7391e9a0cc336826aad553325c9c15f5ce5dc28ded4ef 1b8f76901b5dd1c2ca323286ef9d5869b57d858a9cfae8178dc810dfa49240d6 ab53700d4da562b1dec33ca608d070ffafbe2e9d3304aeb158cbd2a9c5312763 bb4458793f4305898f4ab8d6dea792e73755b38e52b61ceb6badd40967f27e95 621ccd9a6393a19f259002b35fd1f11c2fe492a34aea441e8a5b4cc1589e27de 9163337968809feea11cfa4f36d957e28fe79e4407173e402b9e546a23faf8c4 960b878926c429452a353516153c60bbbbe6e5f2be89f664d0dc2fc273284bd4 14bb041a6e8e9314baa7589be7cea9417eca7fd600c35954888ff0130ab9e2cc 0003ae46230b95836a2986d7d73e90ee2726b4cba559fb5dce85eee1407cec7a 45e86f93fc28670979cef736efc72a431622e3d67d2bd0b151ca270478f23e7c 620bc9925b2b487618f7529dd83e74771314290f71bbeefc1c4c569df9d7a614 d74de6590e33d50bff59acf6627a3bb8a3fb4651cbce89a01522748d6b1ebdc9 be55d2c9c9cbb3a46741d8b6d3946a5848ca96f2e974ec69ec7bdd0b12cdf8fc af1babc6ea2601848dd9ff0f5f450c29ddcc3b0e0ee76f8ff6a2221ca116bf78 37e08639d2b5abb323a6247892644deb7f858a57e41d512ea4d205f262352a3a fd9aacfb37b1a0d42c7c8f7ead2388eab30f36015a110b7bc55e849a2618a4af f194dcc3acd648647b0d60b57c433e1eb7e41ad6eef8661bb46e7aeee7aca349 7d2b3343e954308827443e538c3ed67891f544d864c1bda3a448dd4fb8476518 e64c5fe6a26c25ec4cc0147bf04e13d88960174f72a0bea2e00b8253e8ed5fed bcfa1cd8dbcb2b5c4b76dda4d661945093b846b8b935c51f75472dfc4fbedb1f e1a8edb30ef7d7aefd4cd53f2e66af8082f1d35d9dd7e359a10aa070d00ff2fd 9e8afb55edcdd87d191b3812edbd8c7eb1d0d70f293774178f56dd3a04aa416f 8553ba78c5386528a1a02741b193b63807d8df07db30ac379d4b70da2824fcda 576a62ff235976ea435fead8afc7c670efd84f4fc107b40e98c1f3d8e2f8cfa8 56962cc49ee0da7d2eb37cfa82a799a3e166234e9fb51ddc105f72c964b4525b 4110ce727b5ff403fdd8060285f04bbd58867a5caaacf3c5077f2e0588bc4219 845501779022acb50493bccaaadeb844a746529325b868299a3adaec80c4f5fa f2f10531352de583ffedc013bb3caeb22ed698d8669811d5f65bbb2484cf0690 ddc6be9068ec590b5a18e1974b3ac2d0a7f76e513e4d2db9beead37074697c17 bf0e6b48e0471104901ef1b6710d8ecaac3db6fbef56121619b0b62b9898096a 8e9207127c06653fcc18b212c598f986a45f545d97ba1c8f592c61333b93c336 026b75ce9a173e77316cc4d543210c0b3dfac7e61aaab5ee808eeb77943b9e3f f38598103e7b8c0630630a94d55f8c5b7dca38c5d1512f7309a76c206e52291b bd83e3df310c14838c3118cb1c75ee6884b470dd7616df78a03babe5f5f9a127 fa935db7d6546699054862690b8e5a244f22d062f92e5ee049f1bb2660a89d69 8f2cfcfb3455cb76ee7dfc1d19fc4a5c6ec8b0729146e1873a13af8c69dcff06 8b4651168828fec3760a1248b0f67861f5f7162c67c8d6861598af5a3749c4dd 72fc8fe35d5a4e8754e8574561dc9df538f1aaf1c32c5bf174124b95139c7629 39892e97f3e5abdb6ef18ebe7e856eacf54bd73a42c1aed12c4a9346c7fde082 2c4ed349988ace4911db34e4357079d42643ea9ba1a1b65cb6b4d3941a1d9fbd d6205373374cd1187d83f585560885d32e111318d4c2a92d481e074e34ff88f0 8a9c12ff745947312b625ca51d3ed33e1027e006b9576a725d682189764fbbd4 638bb386c00128bc9c546a58d698e6730935f5fda375af7ca5405604c4188d72 dfd82b287bcd2e5a81d29275478d4cf1c5abbb66146103e87bed45d661d4624c 4ab27b10752f6ee68dd134b6ecfeca8cc7eef46f12468824db8d612febf5ebbb a23f099a8288a7a49db314f87637a33290ee4319ecec2cab09fc0e4ec4c03f15 c44599c5a52438655d39876e2bd7129b25ff77db34743ed91842e5bb766a00e6 1f0da9021808942bee18fa0a7a5eb373a490f1aadc2208bcf9865674c873d025 55a6c48a8bf6edd8e975b2dfcc24b1c594b0f3c9501dcae4aeb77bf039f3046d cadefe18d766bd53c9688dd92ba62a9092d0088bb31ab290f1e2df4de3112174 c812c9349c07544d0d8b79bfced49d5c062bee47dea8b18de77744ec18a97752 a3c71983866808422d44315eec3b714f1d326697047138e4b478ef38af09d24f b3151bb7b809b7a9f961982ce7d185fe62d7240d7d0afef2188a49e995cce820 d2d4b95b9101cbc7e0bb2aba863944984bba037ac1a1943b0629d398c61d0a3f db9ac4d706b004d80fa992183f060e4c027d8493432a27c801384a702a8c362a 161c0a1baf58498b1934852ed0e1829f49f9733f2a08724cdccefb79a6eab964 bfb3b0554aa83d794a35ef37d5685e537fe40500a675730ae64f57e130bfda86 e5704fdd7aa9a8b3d9a278b3a9e286e176e1266b7d7f7ce6075c439048ed7b5d 57a20640a8144a91b97e62b08b562a61e508662b6d53ea00c3120901dc972f11 8dec29b217e12051943964ef981160ef7588dede69669a72eb0def70fb5b6f94 42a23898b455094918724ffa8a3aff447a660b5d46692d6bc704d35c4f21d653 79b9180d2cf1fabaa3962e832aec3ecad8e25624501f871d9df41bc8a5c353d3 47da919dcb3c8342b1bddc08037df9ecface7f146bd7fe4df402046a40592c06 af5fa3baf4226257144ef64a0700efb421e31ff3caa5d86085b16f50ff5ed0f9 c9ea3fcf15f77ec8e2192b133615b1e966b103e820a80556b653b34a3511d997 344aa94226e7f1a1f680c51519016ecf126c7ba1dd5c3ce74002066f2c2a9283 8820345516085b5c9f689ec64ce3a91218163b20e9e19f18f33acc43c5e67207 0696a6f4650fcdca497007ed2487ad60813337771020d8524932d53b234077bd cf8103fb6606bea17661620d8cd87964a04b11c57393e352e8c910f7bc735650 380fc2b86711c8de3e10544fb1af7890a23ca5be363f87ce76512af87960126c b115935d5361490e1c451d3489c5aa0f7feeb8fdcfa21af345fcebc5601766f0 e81c0af298f20bc83b0dd8d207b98bb8010a8990fc67bae973dd65d67f4706cf 58595bc751621486a8536c7e701a548a521480cb18eebc83d2f224c8895b9869 7c97fb5e1806a3f87386762fa3255a021106363b32766dc460e4faebae182af8 2ec5463d1659bf1f310706d0d2e69d31d253d352e0d4a59f6ffe403eabbed8cc f951208d2cbb182f560b77f01b3832ac50fdafeec5ba5aa8dfac23eade456a04db75eaaa6659306428e665e248dad5af6f62e6d4c07a15e4312e702197e4bd0f3dcb00eb7a8d197f6dcbbd29d4d8ad883863f5cae0dd7502b3ff1da440d3b805ebfde6099728ba09991563a0df8e361543da98a8ac749ac16c273c50902d85011bd8dce403be41a38e19b799416265d6a326a70e1d664a6df54800718d0335057683e516556e359a1d9be66c9f4f61c96a5fcc99157ea2ca62d4de188ff37e09ac40357fe526298f54977446bb2a485c26eb7993f9c5d62066ed202417ed380c9612bb7acca52608545c2e152c2aa9753f11569617ebee28a0f62ccdddd35500355563f66589134e5537f8e0643cd306e947a373f336a6328efe35b884a18902c25ab9437fb6aeb7a2c17922eedfa1d0312ba78f4455b737bea3badd60d3ea0ac847ab4776708415c66f815706b3f660cc228f42d6a53c70728551510f4a460e04c07e43dd40d0f5064d5c74c4d90bdc355db6b644d88abcac2a7b684be2020b82aefe469fa234a241166e3b8a91c1c204f1538ac1c36c92fe2776e88e379a0dd1df7935bae7d3ecdf7709dc508e0cba6b9ba8ffb6625296c0939ab137fa450c978aab6062c3b8afee61de1ad61e677865317e99767df317c6b1b1ef3865ba05872de5ec0ef206d84839f4fb7bc77a0a55cac61d523285a8d296a670dfb519082fd9498af078a5e6e74ac3f1328267736050b832cee6c5584038e0347eff2403d1a6ac6906439e6881097e47e4ea4fc34d2ae3dae3139992dded27c01fbaa8093cb353679a71ba4fbd85e41eb580f2cbe716ba9bffd3d875dccfdb2fa223980b83460bda49522d9d2e10d3220ae2e66998db51b29c358067aca867720340210c1b5df88468ec4730b2263df6759f844fc3b667c1a90571938362f6120fcfb4053bc82b3861aa4808b9e7a17916514c91739dda75abfee26d362ae5fdcd925b047365ae20d8701aa06dbce7df6d16d2c420c58d5769fb88f46a56284f90895f092b3baeca5f3fc2ab56022b0e4f7a956eee9a70f58a57155c3810b89dd5a6360c469357a7f776ab0e892a2f0e699ab011f7e2f98d73da2b1adebae5f8ec465704c06bf298cb2dacec7fba718a01d751a4d756465c1e8da02e89ab4514d12cd60037f54737e1e5a9c0487a64a0513f2d4a26d8c9481be0251af16c59ad81d9aa0c3bae50a8636afeb2433c7fccb83d6158532a3925a4e799f726110ca49315bb01f0b340f00e8e1b3513c7869c108f7b62e13ed591b68851acbb4e91fe60f5110d7cf861dba9c0b50fe5ed59de82c9246f03775a1c514ee10c627f6d8bc014630c4c562bfa7a1add9640973db39f0ddb6428b6a61f8cd27da46364f335f9c6b50a6cae05a396728dcccbc39bb9b55df0bd253f0fd7327c5b8df47dacf861eaaf01127d2b8c7299b88571b306e1f8bf47355cbc6a639ea4e60aa0a52c6d68392c087c3825d03ba1038dbbf8117861ecb5ae9ab17361eba6e6176b8964d04372cc0e133a695ca1f3827c4c8064a59056fa5db5c14344a66092abe5a5e7265168cc010037176b3eae706b824666a053f4d25e30699dd1b69a3e5cbc353cbcbb84c3025f7dfcc96cddd9607b61443aa9bc4d356311f6c59eee0c304adb111fb0762d08f0e060f861ce0bc180edc40da82b03ab323d74d9e31fd489e5e84b359e88460f1233f01652439d6fe35905374ef04aa7a031decc84d641989d1fb8585629550cde1743ce50b1d9eedbd59d24cfcd6dee7c739f590e68c400f25bfa6e864b7702f41a1e30093557d39c522898f9da66cf98459fd677b91a2bd4c69d661e718f078559a69aaaf387ff415bb531c73e3eb6b6a90b49cb1aafe4f5e151b46b3bae043e1d245f7d4c62721d268ef57d5248c999e51f5fff1e46005c3ff174d6e0af0fc0d1f129ae0a0c14424a7ded6233d49d0b892a6fb5d7a737a8006fff36997a0af3f017a2dfe9a1ae565f67718e9b10a2126adfc79f4dffae660902df5d2da6077711db4215aac94aeed6306135894a7b1cf32ccf9104fde345ac13c48a25710d4e1b703b490cb6c1dbae602f61587d241225a4db711a05390f885929aae2130f36f19333ba7e1b6cacafd48b606d2da89d686761263b92c2b2b4a5c6b58f440812b4c4394f003cce1efeff2f09dbcad1ea471e2fcf61747786c9e3f071bde4052030715ed1173530eda027d4033523aba50adb2a63bd473724ef4ec5faa4d90681e61336856e7b3f1c5f31b6218171757264ef69dbc33f2dc257aae5780e7a08bda14e484b02d0098647e420f475dc7b19edf72a37a552deafce6c002ae7740401a7d191c82bb2d66e8e4b35d893fbdaf0cec5ca2a366728a06c0caa5851ee081353a15351aa8b45388fefbf1da470854c710255dd341118da8a52f63666420a340dedefb83cb8032aec4435858e550d9fe58c8d886dc0293fb38e4148caf209e33ca05885f8d9083182d57690c9420d78ddb96b29fbf8fd7ba65c158f75e70badc325d84b6dcd497ef9d0c190143bd82718738646b5e1ffbdef301d36a03e09c9703c71fadf39f9e70b4f49c873e90e652c4c4f9dcad4a727efebf66c320906405b9899e20edca61c104334b4ac611a5cf6413c3d854c1579cf72aaa12e760746a8239bd57c9085dc05b4b2652bcc745dba9c5c0650a040db4156c6fcb3b108d04229e2a4f52dc4b0acecb7214cc524fba03631b15fd632e584f58f7ed323094e5ddd4b00b3a06742e5386eebdfb44a3163ffc7e8a6a779d2080eca2536a40435069283b72ba51bcd7c038b384a9d75695bcb72492199b6377aefb2584d5d0e156146775d5f6f3f9616ba66815b9366d7bc315cebd37416bfceaad77e34600aeb306550e35cde213f549c1fe7fa67e1122dfcf4ace3248fdbadf9008539620fc567be47c2c2297aacd08b74576e5bcd3e176b839208df4a3492b91ca5efbe0c813b9930f48bf90900263430878d68f38e1c7050ed22d003f575deb018a45a02a02ee8ff126411d40aa66ce4b96fcce3cf52e2a4d5443f0384a32cc671add407d805fc81f2a1a8f5682b24fe9a0590bdc27c63f4b98ca0344fa23b746ecdb804c465000bc6d6a0bd78601927347529f6d86944371a5efb7c7621b275eed5850e57eef3e89dfbe4aea276e7f5cea78eab7f67356b15b2b4fb856209f18fa432014b6ec764d6c2b3628c5c6a47d3af8c5f49b40e940fcb9391a89baf8e233ff20e7b28f8be21db150fc74737c048b153bca577ef2214fbd1bca35df839b9e41f0e87e49c437ce9e29f7977c821784fbc70e42456c516bfe59a61b64ac35d015d075592db66659823bcd68694306d316552f14cfc12705b69292f8069be0f3fea088c81c6630dd55385da99281a14b9d6da2922c2b0ec08db83b7263272f7c352064210ca38c61f77539971cce80c5f05e036dfb1a97f6fb5b52fb04911cd0d68004467fa943c7e4f2f1ac68069d85a2a807b4a68c9a34b14cc069c17e4b1389f076b609180f656469a4e29b290301d9fdc0b25d6d2f3abe2875226fc92917aff02bba1c4ba69876b48689dd48761b9dfa614ddf6c24d1d81339201d19936b4300bb109e145e5408dd8a34d5fb6838987f0be396852c469e4ac550933dbcd8f7e020a110365373bc7094698a2fcf91a6c7b54c66aae10bb71fb363f19c0734951067ea71e92687147697031661d94c7f96bd098fa7dbaff8d578e8cc4f7411c1c0aad66a322a0e0aa7622c64af43ea3b3e7591181613245b2f3dbf70785cb3331015fa21cdd792cd024f5dc7f9981bb79cded13744066f068a18380c978236a6a0096775a9a7c112ca96263f8e5739b2dd6b25c9679d1260fac1b9fd1ac21b20307104c14afc9c443fcc586bd952583b2382c80a7771b56597170a4f9be55563b0682126ae451af0d2fbf1013b6eee3a0aa9efaf1630c8d3c8f291a93d7e1b18505d29dd40919277796b58b39e77dcd289940439609b7815ff4264ac8d0c116d50f8448c9bb5c51baa703f7844c5006fe435fb1679545af54e007a026ed4f0c3503d195bc42e9b945ccf729b0a4494b41903f90cb8668e016d2c79735012171b60a96ecf757070fea49e0a8d832a634efed9b8dc0fd58ae584ea8135c37a527780f157706dd25e5d25984746b2e9b1406b572472efa7aef8b3df312f00d9d2f9f07f4949023820eea1bd2661f375757a4e1a30d0f1867fee7142da08fe847aa4d0a20b76ea79590ae3f18ce58a2c97fa522f1d70bbce0913ba5e8579fd4e667790f6b781d2df86111ef46cb485f56423c6f21e95e30d6556ecfd64cc2ffdddc1f0859eb871278e6d9c289b56e47889a059c02ecce8ca40f1f1d77238dc20383a60c539f3668c4a25eb19f646dafda2fb02990f2fe91a1f1bbdaffc258badffcfc0cdeb58eb244094e2fb0ee778b6386a543149aa82c183482ab2bbe328df4da570075700b0cc075d0fd458798d18f1a29522acbd2eb3dabd52f78bd1a897a690203cade9ecbb9dac4add3763215bfa47dd508ceed5f3169723c774eabe324b08d0f36f85e0a4276e24eaea85d04d8102430d2b241ff7998029c7223064e790e5507813b54bee88111bac4c9cf78e3b7858a032bd2d237c81655f31324bf5b06220506b0cd6c21adab2a2aec9b4865f7b797509b95e31c16667be6ce13d30406700221f02f45865c584748f5a1285832a7786c02019fbeb45e9865343d4da333640681317f2bf5a14a119ba2739ce0f6a378e79b8b7a45b00e8e0ed1d9b04099300df1c815759fdb94eea65397a938f2f28e870b75bf671c11ec0f505b7e12177c0b0e3d372a67edd8abdc4f4327c9fa1087ec0f92e6b7617804285a8445d275ab0a2496447a012618110f8a61b43b866827ce97c82f60f4a483417479cbe5837d0a3d6c6f6892f54041f1c8e392c1a0482145fd59d42b15a8842f84a5d851a1bc09ad33b20f4d851920b204364a6e94ed9e514b32472ea32cb770ff44a56e1b4a0ca23758342a3db31facbd38c7a949cbd3f9b73d84ec3942b65b5fa1bce53e4d0a2a9d62e985ec5bf70c368cc54992b360d08f8c835cc7e11b9bfa0db8b0a4c2049fa730d68a737a47be647a9220f58be2e2f1b7dcfce472da5663d858b4447b0a73afa6363589b0c50d2e7c49e79444232654c4a6dc4358c2af95e56064682a097dce513aa9d6ef8b34d4848d60841bb5e15584c6d6fd1fd0e10df7b30b63d50d437e12784d630a9c640fabc9cf19c0ed5308db587f5c5b3b3e8e361b3ec8f20935e812ea4b9fdd931498536fcdea7cd7d2d1241c5a48d254ff9e0f8b749e15061319363c32f96060defb7ff7cb2980aba1f0a632add0ea335d3af327a0615307b28aefca6bf0dcbd341eecb48f49170cc07a4b11d0712e811eeea2b6d23f98044c1b9837505354d28e7c2273a129fb9dcea303810573d7b73a76df080fa1e9065d63bdb0f97359aca4d27f78dd75f8fbca2226c40ecdadf975ce09b4dd1a340bba16d138c2257b60e56106728b8153e870ffeaf03fa3bfabd3f5492706352c08f84ecf49da9348f3f3aeab33c128b4bcd1bcf0e0e0abb952553e1211fd446c021b2026c6c90f72c663685ebd5a11060c550ee94facda3457896bbe7c8aad3306fbbb07f62af86db567a5e338752066e093bfbdd6917b230cdd4d07e091023d08e9508e80ab8c5e760d5e449cbb1277f9b31ab0316cb1092978656aed74b0990168bc9d257be8b30b104c8da7b0e9061fbceb20148853dbe19a64f792527cf7061209c81e08441d666c931bdee1947f0e7eb6a44321d9eacfc51bfd1b19e1ac05f5c21c0170e2b9990032863ce13ff15291ea7e130529f9640d2f5e61aa593f0c7cc1427312a03a2574fc872aaeeb18685a60b2435e9c8137c428630d48b98d0dcce80ec24a68cee2c604e45b932a42571ab09a8287d7ec03755ca092a7584605671d944d37cff82c846f6056b24edcd0ef0e235f07415829926d3f30705385043a937d8053ae74935416d6d57d42d899e54bb2d397304acc6448509187e10506346b1b2e7336e1a8aeb627baed6d0cb1fc83b832c811cfa8216567a07224480d6cc6bc87c30e3dc0d02d5f11f9275a3d1e0424abbca8819491d98aee27f0a901410c0d6b6cbf1e29c261b6e2b531975f2eedd2d4a44d1bc2907872cec6a1070ffe4c7eed0958e5d35aee79b6a7ef30fb38fe1ab6108dc222ed7a5ecb2f8c110ea1fbaba91d0ba5d4473778ebaeb633b30955606a3dfbfd0fe797023d8b650d058eb79da3356d43046c98efdbcf58079d1e8b69e7d70c35e4934a9f9866b91c088173154a7cfed2f5b1da2358ca05efc5c1d4622ba47a526f4c70c9d7d1099c07ba86cba1acc3db4517c17e8da77a68a26cc2d5a58f54c1330ebf93a339c0c90efd11d6bb1098d3da4613535fcf6151a305e4e94cb2b641624c4a25f91c42f604c1064047102e30fb6dfbc963de23b2ae391384ac6dc649876381f6da7d85fb0e6437b5ca6327d2ac3cc5b7c1b4a6da77a498648365efc8ccf4f97e58e78b190dc3ac38461050bd804206a26ed5f3529f7c93f799906fdac45f9fc8887a6c700bb0b1ca8c974e52c53243f10a03b03682cf91de8e034e34a59af9b1a458930404fedf4bcbb2cbeb11063d85829336a9d5671fc6e39237ef42e3cb0a02ae216e017dd76b3c2fc2fe979eaff8b35424ab14a2d09b49368cff539d8e3338e7bb520ec6bfc07a7c980d5e80393a6dee5bd189427c9678014ee692600ecf64ab30090e9a3f570a9007767d67db4677a867fa89ca6beee77a98d6acc4094eed88218108d9f2bc55b5b57d0368774891acf495341234809ddafbf898c40badacca5e5a0a1297e03fc537214a381322d7870fb2bba5b6cca04be27fd09f64a6a412f345066bbf9d650afdae5191779d2fe3f68cfb4e9dbad72f9904086cb9daf425cfe50be96dfba5deb3ea6190a69ff9e91d6f1ef54495472c2565d278bedbd52edd5908720f4154c7015b9cb54e055e4966dd011e864500716fe7ecd9b8ac8a81a1990985aa8813d4e57902b10f7cd77e82dff8409956f259d177e37b446d49a013720ff7b06f1ac64e74e0e5cca51c7c8886c0436f7134e34dd8373d69bb5796fe600da493e248bcb5aa262b4be1d0f6b7f76a85718b86f62d1d6dd2e5c66b5e76190421c6635f844a4d135fda27f3f2ab0d1c0f05a5ca6579654dde1b4f5a9e42cf04d484b5721f11edaa503893a029b6dc65d329e95c58dc19f07e874f5cebcedc0f0a984497bfa08c84b0363f12236bdf1ee7f99a8fadb4ebe088a07ba940822602b186df2399e1f35ffb4c65e1cce6bd4fe4c8bd9a9e52c30179b595d97ad7fd02e0987577ed6dca1147d76f5bd2775764c38c6b7609bd12230f8d1c0d56e094053792a938509aebb631f6b2cbf61c6b131a2a2b43f4df5b04a8760be0521f600ca9e59956c1587443e2824341d0bc93857a187d4299960e133bffd472355ac801c36e666f8f10c5522763fc169aa646295989e5e4bfb78e6b8ae8882a4e078607411c539d4827d24570741c62e301f59cc7dbe333dad741b37e5d6d08175bcf0d4bdd63f63755b1cae21d2dabee295a2664a883cf0fa272d95fcffe291f823f05ddbf9249048cd9d7c4acd5770b20a92179d019ae8ba422b71d4f700c1167110b919e1a8f21bb824377f7e56088c3bda2a5c549d8b8a71babf60f790a269bd709f200e04352e0d9ef1602e02f6d2773ddd576fc8c2644385091834375fee53d0540a6c8ae7931be3ef707018fe1d03bb63466ea04a6e5164a7801c0bb3d09890d8bd86f735bb9d3b6053c9ecb909e330a670944d4721c7566eefcc9a1b6fb0405e13212964c95c9619f73b8f4be9478f9379014501b707681945178b5f51ed5042903385b3c6fb73f5f9b6d89a88096596547e9da19a8d133060ad225f6d4e600845919e171abf47940c146f107fdbb25a0b29ad206748ff26ea4508fe41a96019571857fe32ee9cc148497b481bd8689ce362fc4bff92e4831a41f34cd5aa10d89697552b0ec68745eeeff30543af2895ff6dbed93463635e7375252088de10c42be58d4d0cf2931b0abb5913b2193d009c3e839e0865390f916caaafa0a820daeb0b2a76a46c6975db9fba07f35f229115eaa95ead9df27f9fb549f0596160fdfebb2cda0dd9f27fa3e6693ecdd64001e6091b73677aa377f81dc602be63f0125dfe6f285b6bf98c031c5d79e6fe3cfa1bf7609f501b2c680e1ee4c2cd8a80e2cf3352a3769a8291cd18b668aa99cdf4d4aa708f184a5dbd08ad5e7687bc6059def435b6956ed03ad0347deb7c7d1ef4be07f28e9c62b52d09e3e1b508e410e6b629cc9c82b322f62d397707866b83df44156d99a3a79838d7e9251b233350588dd646553fd578019024dafa0441731828f0959d7b10bdc16e97d9fa717920ad7be8431b0e334a5f8c33aaad220edb6f13aa0d0f6d9cf0423f0f3911f20420f1c7156ab11f13ae951a49afc1f723f47ade2f90f3a828bd5a04e7247c0c92c05e4d1ac555de0655cbb694a671ff6b25b97754f1f172c575de06eca5140335a018b1f263a10eedea7c8cd27bd5559161787c9cd15d32eefaed2b0d8a820e6ab0c73052264f1fd598fc7fc615addcc12c788315e59d78465c848200ce95caca5020b1e16da778fdc94bb84393d2ed153a8e5a04fbb5d0962fa88d0ec8e1d998609dff4b21c417d8350f5554a7962a6bd624d68f23b1e85f13872f1ce42649b9908db4ccb160cbe0940d82ee09d80bd61e623290d8920fea1af83889eeecdb4820c6896c52da701433ac8b68afe80bf7a2b2ee6d198bc0178368e7ac684218be60ceb3ff654084936e05bf2145a9af9ba299b76596a019f668830eedbd4d57eb204a7ff12bdfd6d0bcd6bf3bce9588c5db0d0b594438b6c479a502181d0e8cf5b03fcddbd337c8afc04d27800009a166c322f2e4b67ae36c1f613a17e96a2414b0ac6655b8acdb9c66295be1764d898b151f8d8fc91883993aff464640f38628f0256a79222a35e1abd01ed4b1c28520d54bbcb70d99a9cdfa9acb7a1559ba5ef05fe4399ac3075dcf1149d07a02ac0bc13878d960e5c13ca4427587ed49f012c0ebf0d99f833b64c9e7596c4fb22e7b40906aa69fe08e2a1f7c32db0ad7a42990d2eb05183f051acb97c709c11d9e992fde7447d2707f44d9670b7baa9c672d90152d590a99930e49ac9be7dc20e22b20d6b58fc9cc798a11f0c7534f828631c063c749c4477548f7d38cdb6a9f58c35341b8a151d50b2aa286448893a2826630b1691c36c0836165a44db9233409134942fe4feaf70c1631cd253b7470b523407edf41e1223c1337e050b68cefa3476ca33155a9972e23a09f879d851df2eb2061644aba588481dbc5adeb61e4c45d8d901402d0ce47cb5a6271740ba622acf03ee3bee54cdce5251552681350a04cdbf69998483b51e967dcdbefdd383ebd20743c911075cf082cb6056e00290e6c7167831070c0141187fa180a3e19bed8809dea595b62a24bc7c41549aeda5ca23fe166f41e7fcbf5b64a404d285a748250c01a51b547bffd7553e840003d61d3e235c443fd29540b4dc5c5aba2324bd390157566da10ac8d6193de3e90680a63289953c77aa271721da5380798f634933034e296dc42ce4bc00bb4311750ea155fa19a54364b1c8c1c59f9176008c25650dd9c9a612aa76213d8f8416934d3406f8af29c728d694663b85cfa3f318dc210f1d9be0db9c104b85e743eab0e528fec605c11d6f8f2f592ee3d40ad326af3a07ed59f17e3a96b40c62818c7964d532be5f05fb0e59af76655ec846cdadf39306afe0ccc291a2780db22350afaccd63c08ab3731c0576156b642fe156010725071d7bad367e430f27e73621994d96282b3dc3380db575f14cae721c87b8fed50ab11a68c778a98092b804c75ddc17fe2b401041a06395aa5781222f1fe38f33064dc19997c64f999d2f5386b95119d67c620c2da974ba69ff803ee91408433b00ad5386596855080bd5baeca0d5c41bb493c8f0b70816b8e496dffa4c5037ca0ec212f5dc13462f4f95e9caf51de9ef6c341347af574d869d586a346fd5eaf40f61047ddba8e96389448fc849702e767439bd988e1ef14c1196097c6cfddd3a0415194ce1399acbd77b39ba1e9bf0422e5aead668f637062f301bd17ce8e19f04c315f1cba699345912e84790d81b19c12aacd8177c0b0497975552fa9919bc0b59ca81e4e9c7bd207d1a348f8319330623d0b009704b50f98e24bbd49dea480ead77a7fed179215a97e49ea8834a668ca21f3e09bd044e82ee357217ef37330e80c8b4df0736baf72f5bf7aee56d3f485b3e282347756e05a7b4eeec60c2680be736dd5ed39869d7e022c96d40d53cbb5d736ecdd427bea5cdc275359dfb0c0fafb58c15f70bac97fa1ef280fbde9c26274cf09c16b441abf14d469abf0d7407 false -check_ring_signature 2bf07c239c359e0f0393eb09cb320132e67c06d8a6b507e7f30ba83f20a1d7b4 6c8ba957187fbd628f252705c10aefe528a0f74972f8464aeb6d48c2ea08777f 4 bdbb6fe8aafd692f27eaaf24424525fbbcbbedac7a0325b9c838748ee40e76a2 28b6f2a7acb27a116144b3be5004732c8a34f9158b1724d7fd8827e9ca30edc4 b18054068121b208ae0dab9cb19c77dc0a4c1ba31caab71a4d30cb5d2a455e91 e5a723f4834923c71902ca2994fe724aa0be230c78a702158c50dfabb2fe3a44 a637c20f02db03504cf6e3206e8811c08a6b9eab6bc6019ebb0b38e42fc664058911a0b568ec27f96571afc5484693bbce6ccb3f1910fc37ac5c14c0ee38b00912f52365ccc300bc6c7694542f264f8ce35edff0739fdc86fdd07a5986e44a1e639cc1fd40323ebd1e3ee9e9d88118b45a1485d706ac10e817d8882585db9c021c3c865c725d376a4ef3c6580a4269aade1232aa569dd3c9070af03f40ed1500bba5f0f89eba7c43a7dd85284e1d08588451feee2aef30663ae4b7a523a90209a5449cebb56d1ee6b43d1731a21c4de6ac150a9c362b924bf961bf8d2f253e0bffd51488c1b7f7af50ce3c6e3f1297d1fb8f1ba50e4a3b0662e22b0f5f795801 false -check_ring_signature 09f33cb70795fd13838ee9d4041513ca2c1279b247ddec4784ed940c304c03f6 27456b497577b08aae2c088eee3318c6f80aed0aae6c75a16fb3201362295e3a 4 ebead7b85ed93a6dc7b2032a71d696bf7d2d201cc6a83ea8389f601f03bf72a0 c1a7d2d16e42a13e62ac6e82cb1bf3e29f7c0aefce46f2dfc3b8d599156d357d 3e26238b85581705bb1f18244cfe7e3f734f49cb1882b750c9641486c656e307 dd1b40046ad7b5a38e61c435fa40fdd582529b8162385b7eb0be1f33a79ba847 c2718a6bddfbad8c1290589d0fb49b5f7c9862d023acd2060ef27c3709799306683d1cdf0d651f3cf80be6c030ee2c441e0eb1ce076f7786e014f13890590c934d4b699c9d4810e4328de1beca7710b361d7bf99f0a621ed647903e5550fb40a02b58ff23751f72cebed3425b10ec45fbe2d7a0cadb38d895ed31d41cf1c12f91c8d331a98b2301313b770b5a98338a6b290486bf537cd26fb89fcc9afc9550b1d7c4a70ad2346dba87dd39d27c3f67a27deaf681f10852fda6a197331d1dd071725eca90f417d8bb1112c8a2158c6a72c51e18ff2021e470bec92d659d6ae68277f82d9e2e19e71efe6a83e80235288cc297081883faee8726b2ed66170ca00 false -check_ring_signature f0ff9ae6c5030e23836ba09a350fdfd9daf045f3464de486a31b7662987c6498 df4a5f1c0ebadea00188ab5823c6ba51adf867e73680f0b49a6809788b4abb1e 8 f7bf6fc682ec4cbdb1ae15bca39b5c66a82c2a437f17b4bf76be19920bb470a0 533408b75c3cae43456b13304e2d37cffe946a051af28d2113e3bce5af458418 3d01fc1d42784146dad0cf86cba00bd0ea1251a603309d45f9842d06d9517349 9cadc6e76489d5092fe4f672bc6dd2495dab84c350cde25c65d73a62e166e458 1f1e33af7bd48bf8d435f45dcc517bb8fe890b07f952c6d2a075b22cd7ab5f91 7f31e8fb5b97163089c78b8dfa0f341170769ca2780700ff3729a409ae0d4140 ded85764ddf0ac34b77231f74b2ce596644905e6438036f031600797694201c3 2acb3cfb1c6b4f03e481ae45e4a3da879ae469a1fc244258607fb2ff0e622603 91506027fc2a1287712b18a27a165855bba7baf6803e525003b59a0876302f1a7d733ea8c40fd742cf818a53ed3ab4d4c47d415bb661eecbbfa8bfd817dfd90a0ff194726112db48797fa59c1f7011523ba392c9308c8a8b7dccaf252091b60f71085fd6ab98fd3acae5c156e9eaeceab55a8a241eab897064b14502f98cd90e939a762e9a19539e849fd44c43cd1e858ffe802d21be85f218428ca57950bd0c76fb3e35ecd22028dd8b18d260b158fc74b57e5f854e33b1f5a4c5b7bb235c08881a3be3f2eee907d50677e68e22f90951bd0411e95cdda29d8b6011e8b3a90e260e2da4de0d59af8fb3ab6809fc7e3f0436e7077eacd908b48426a8fb156f0dfe4f3cf800aaa61dfa1348f486c290801a0222fffc2bfc7aa5f1b2f1f7a9bc0a809fe63ba1dd784dcd9bf8054d2058c081e21381b5d5fd2a68f3a71f94ba7801c07b379fa7d646bb32eb6b6a86f53ec4b32c89335be476545575c15a9c9eec03fc75a720bf84e6ba45ade8de4848c51b35ffc1e0d44555e5713fcecd54f501093214f23ab4dcd2917732c98ceff72d5b14b4e7732139b89b62b9cb02c4196d0c86d572d06a6ccb0286dc18cf4f9fa6ef6f3ce3275c597c13f06d40f96a0ef105d580b5aeb7b0e68c8a3033eeac40de492fc33fe40f50c0f94ac82c346a6d4c044891f3883be76a8b8b09a5be7de308b3276acd9153d46d7e246dc9c64be90505 false -check_ring_signature 945c28b1e0584642d4a9a4e36087b3d8b95b48bd261dffc70cc642568ba9981f ccb580995b4f3ac3c1670dcae38d06eb88769e7d986b54a67a377c5ebca3c102 2 fcf661c6291f5ea7df8f2237544d3afa23d7e0e4220611d4b5b63dbfe4553a21 b2b768217fdfb94d454fd855c0078849ffecde7c2be0d576b9737bcc54355ffc af37b321b3b12587aac8e3fa8df3264ab1e3861cc80b86d4031f0291eaf0850477f71a7372d09a787c5e1fd0c66342cd02f42eb16a72cd5cb47d0579ef988a011bb58559e70350b4741b5321ca1aac7c888c4ff5e2279ae733f574886609b6055718bb371691a8e9dd92a627799cfdbbd43fed4d695b915018a0f09f43d17c00 false -check_ring_signature cb400138f37ef094a1c9f6d80f2c85c03e8481631b525b4ce4886e84e7ef373b 973cf388cb25a469924c32824716a3b31fd7cf208a4a5eb2ab84b575a532d125 1 4025107ba9054f6e136473b8fed05229ed85d2f15a495ccd094e8a8e447f2dee 5783afb24c6d116f8c2e0cf7ac5a722f8b6a402540a480dbfb748966cbaed407b5088b078516180cb3e33cbb54829d0876ae48e7a88b61e5c6622dfec13a1507 false -check_ring_signature c2b37c956d3fd2182126dddb4cb28c11304f2008c252667179400220aac37ff6 dfeed78e608a514c50b201bfe0cec16190be3b56fb10e5c48d601b4ef0dfd2ff 15 7b6ab6675091e38838cdfa2eb09d67e2a4e5f8fff963269c5e2b3db8c4a5ec64 46e5a17c7e7f6df720ac4354feda5dde1ec32eeb28d5ff7bf57ff9daf350cbb9 b18b565f6552372eb2835f654fa289dddd9159d058ac9c967b31f38af0ed9a54 ee559deadfff02239834cac26f034b2294a0fa1c27c46ec1163105cdff14dc47 dfffbe297a4c5ae5b97e0c7e1181a080cb0bd064712c5cfa63ddba3a621072f4 2ce2023cca67352323eecfa6209f9c41d1dfb5466304847794dda0e3861f4bd5 ba844ab7625d0603623deccbb82145b5b50ae28357eac82ff671c67925bf0da3 479a42a0a609d1e7762e349df7fbfa6256a0efaf569988746fa6e56a12ba0c7d d701cb89a357a3f6298bca25248d8130b58008258fadb3bd345b0f46ac01fbfe 97013d9d1c8fe8cc6746fcbef1546b51cf07143ccb69aebe1b9d853e9ac7975d 4a978732ef9226e630ecacc6d5536a8880128012d71dcb77df19ad13055277af 1dd5a40188cd22aab62f15b0bd6b5200fdd0fc2c710f127fb3bd13b7a6911299 9bbaf33b5b55b8c7d4a4522f7c41f8bfb780bfbedb474ceab6645f5ca663d0d2 a1afd1fbce383e5f72d2093cf153fd96d9239bbb48b930cd68d2a9cfc3978541 afe568b76b12d66a828c4892c1bdda7527148de07fa60f62b1ef678aebca5fb6 4c05bb9ee4cf97630051ae86ccb6cbd120f25dae1276cee26c7d85a477f93309144aad94a597ef332d3d81e6456a192a7ea9c0729b337455c9adf52dec2f1c06f03326f472eacb3ae73a59e2144db6c456c17ab558cf8036443ebb494016840e93e6e6eafd661941edef46ab68e1ad6d33ae145a40f698fa52a2797de861090dc464f9f806ea58637f1aae6e1aef09b833060418424263ea39b3ad901fb09f0f82696b256d768697cf422d3efc134776fedeb2fbdedafa87cc844373f789d70d197ec09f1d60445652000c25a184e93e52ea120f135e2ced80f786746b01d305c8843f460fa3af2b3a4177a0ca7e9a7688024d94d2e5742605b4ec28234744094ed99ff5a938dfee21f2c0edc8bfad80e33927e293819141600855c166b9f60fb0d82347e145cf0ba575d868d122f8a8ef4d66d47fa89221fbdc8cee83a777037419e27201c1b5efa324d0e9dfa27d51ff51226bef324f885d0ee507b03d7b0946d337a2bb20745bd152271bc242459bee635177282026c540d4b1ffb1119007947524a44313c572c792cbff0a4d33c4138a9c74daa36338b253d5389d72010ecc72c898249173d1730efdf04146b948fdee9d5889a1b9fe2eb0c267a746910429816f5612209141594ddc04edd6ae147e926a916d6b449d66904b27b318df0f4b58996fadd1e4c7bd1070c1fb9b5f5dfb36bf9a88f2a10519d4be98399ae701474ed4b6f18a1faf0ca3045cab7b68292485e368158c63482c6921bf80a46b01b9af51b45242553644cb67f8b9fa5c3e9f3be77450c2a4a4a3ac17c508481301f2283a7814774ac83b185443f63ac4078fd25d24c7cdb4d96fd2fdc503c9bb088e767a79f63f10a4f533f056a4364ee4c834943f0c2a6b38be230a2ffff5dd0b137e44c10b05200bf808bbbbc707042ba93bf7c4a1c85ccff66a4b96b5f8e30516f1f343d684361ddf9093ceaaa90ced43bfaed1bce9903b0db6948fb12438002723f7676030fef694545e8dcbaf7ccdfca913578917b9f93bb69c441ca52a0d8b9ba8fefb4c036fe00095ab8d9abced961a21b6611e8bcd9eaf811523cba50f020010e942d3d732e457617dec14989ca8f1980d431792cfabe1e61d5eb727054568df4c1e79d827c814d850ad1328b9c738afc2da11fb5021227f068a5b170809ce3c3faf9bed2a1f5cdfb9c42e849c483ca443aec2edb444fd8302de7c200d159e828c04631d5f85fe3ee3e5c24f78c6246beec6b08d933432c4967737ea05d1257120294f25a1ed96e24afef0f73aa625524b2d57f2cd3ffb9bc334c5ea0044b310d07f4c182caedd3cfb28f5a78132dd00d262649671d6c118dbd5da780d false -check_ring_signature e3b26a6d96d6c2525f40eb3d0c33e36d4a4bcb17f64609a0965dd01103e535c4 515740f6f34a41ecb710b8f6ea6bedb68279b33ae06dae5d556947efbd7b930f 8 ff1300d474e3289e6de2ec26171fd51570aa4c9f06dcfad8ae0a8bcfca1d350a 63ca8ac6a509eb13c6c80cac57712736833c8c43a0a156a3920456dda9eeed9a 31a237f3da8b9295d2b8344ec69342a12cf8b72a165e4254288f109f7f9c3024 16e207b9111e06154c6ea14b8780b4f776f0b7e9d8cdf04e61cb07c56630ee38 172e9655335160e12ecfcc1770360d4050e03084fe67ca1409988109ff755dfe 9a96a577f9efddb51579c9840b18b006a4ddfed4c966785df33022b5e056ba01 5bb40dc0bb278592ea0dec6005fec9cab7c494b6c19d10cec6aa18d839b65843 9c3a7eae1da6a27b972c36571afc2c0dc065632489f8070ae578f5aae9f17e83 3cee9a369adbc618522bea7a51556d7700678b0c8aee6cdfb3f60af3221836026f8d28527cac1a7b7d00c6d4824191f519966260fc8810046cf6145d98f8a7007ded4fdf73d6c74596c6bbb5138f7b080a59301274753c5d5e68fb6a30960d0d2a25ab040eac9e5878ffdf34f1c6903dcaacbdf3d184fdb591c6770951170402c920346c006f2612543a27637414af3d374393ea9f5cc1f623a8a6faf754ee042f001bfd4210d33575f1633593083101dffefe79de44abe56485938d53b20d0423c4eb0b24d32936c720b67cdfe2bacea9af85cc739006abcc2944c2fd03ce05a0a99be60f0143183e93ea1cd8739849726ff429190dfd0ad024cd4c9952f00c1b7018f268a144c00642e9adfc65c44cf89d702ee70255f2dfbcaaaaad60fd01df8042cf19f820a9e423859c78fac476bbf1967dd956d40c6733561a85e3e5039f181da86160a61e5a38ffef4c7675c2f2e0c1f88d8a4505b4603a282a80810044b2ded79e77138d3d9cd96656e386380975d19311871ac7dbfa38bdbdeb3804f4bdf01313a79c733bdb934103f3c84c919dae8bb4e24d96d3d5752f2f54b904d367bcb4e20daceede7e3f83b78b14f4c0f74df532807c1029c7e227312fa80508a5a770b9d328ae393e186c45ce01c6c4ca35c58a24126961a3d83fc2337802c7c735375b7db4c29306a3c42a492d6a26556930b1a0e5a59759bd4c5dfd4908 true -check_ring_signature afea48181f93580aef8521c18b2c0b3ef818021ea36bf975c1055434bc4f30bc 8e66c5ec4c956d67be58edb1a56d1ae2a9d4719b4ece2b3129e58365e8964428 6 7d2760c6c04feb4a81b2655989b64021e477d975dc856b6f0073d5525b666e34 df0914a5f18ac1bca031c723ff75caddfe28afdae855f70831fc2a5cd9711dba 25d65c1cbbc03e83ea7ea244c0c4f10a56db67446b7afb8100d65d0e4ba8fc1c d444dc49f9c8f3a335a7fb1217558744875ae134ce779198d4cc48ce46abf258 b0963f7c94fd96db45cfabdf3e9814746f0701ba7886369bee729c02a6fa1551 724b45705f0eb536e1b21826bd973c0705ef435202744923c404f82725a72569 c47d7c7d518c0a8ef63a7aa8f8420aa69df09f63ac21ce9a3b91f927ea92860d875902e7e063296892aadcd9a7144af5879bdb58f0628fa948748f421bb2f704a7b01528cc2069f85d837c2994e3424d4be8d580c696c337e4291c4939eefe007bd9ad9a211bdc956e08f2d9fb2fe8c5c070c053ff9c848832b85e8807c63509fd4024495c63b62df04d927c6381276cd6473ac88cc4128402ff63716f2f43087ef25c750d7c0cd29888ebf76d97198ad7f768925c0981140d1cbd3c8e746049cd712df82ea032bd9f9b51056b6662bb478e8679686ca6daccc0da98a58572007b8b2bb967d184fc1c20c854a2d506b4145ae992fbef40397f881c34d3356a080a21ee1271d66a3359d002358e2d876ed665795278cecceb05e9803e8a70a70ef15e0cb3b3b48e49f64afdf8bfcb3abc7d211d984c8543da7b0a5819aa19610dcadd4f8741df1b6642a0d8017f7764614300212bfb43af8ee17a8cfa30eef80a8ed654ea5c1c17b29e7c7f33e3acb1f32db39fca025fc130d477ddaa6c4fc708 false -check_ring_signature 77f5477016a17d526f0461989040c1234ebdd59b4c9dfd9b211321d9f30e5a54 d3ee4b3eb7433647ff730ab55796497636cf6a83bceab34e186450c860b0475b 5 8af45ac86cbda7b49f2b1f3c0fbf2c44123dbb803222449368c99ff33228795d 25a0c125e8b93fe00c0f3626b9a4120858cc43339e5091a1bd3719a269287d83 af34c7606a34d91b49c211fd8a8e9a02c536f18c6271cdaad341058d48b5afd1 c7dfd882ee23baa475780a7888eb2617e148c878b5e1e63438dd0023b7866031 b8373137fc5a828ad45045f7c92f6a065b7397bde2376c3be4559deaeadda74e 3c3ed7553a9e8f6cb4537a8442d813451f608ff47bda12763aec982ac612f808971a648327b509f6b5c31c1dc48068e880aacb9e4ea86ac662c1ff8b423c730ecd3a662efa104ac2a9f80d6556f846c8d113f3aed0ae15fd70cff70b3c6a8604a685e2c7f75173963d5c5aef57a4bf9b52a42380093307eeecadd39a4673e80a65ed427a1aae9e5eb9114fd0d16bf56908e7ac5e632d1f25ec2aad9527e3b806f275c081ae4cab77cd93ab00d86beef1548cd5260dfd5ff2224b838bf190f1042c2a612b73f076ac7a441eb428a8fbd050c2ba98882a0d4c240e88c89ac64e090b8c13d9fd3fbf6287b065e40341b4bd1853661d9e8d5bf6e4a107bd79b7120f8db3ff7f6eeba736e7a05b07309f5b5046ba1568dfcb8325a1c09eb3502fac0326a4c4f876e9ba674f54d0bfdbf8fc3b8d48df3b10f155856d852c1c5113c40a false -check_ring_signature 9a50a390ffabd3f7fc14a73a1ce8f79dfade8114155f51998d1fe1e19fceef35 3aa09cd26a0585a2963df81ad80855fd2a33a5300dddefb64302bac13071829d 2 b06609155bbae9c1dd67cb2012a9f7a60db08e55dd0d08547b2c4e600538e597 dd43793433dfc7d6be762ebbbc443fb5ff9f80955c753d38767810a501c79b68 dfafd6e02aeab750bd569885013b877a1e256a3659e1a29db20a447026052805d697a142c279e954c2655311cf5664d333c6ba8a80ae2b8d03e7fb2e6229e90037cd7b7804e7f122333f7f2447788fcdb97c61f92e4028ee3e469c32a2f41e03fcca6a76e72032fdd266d595d20f0bc9b85393159390bdb3ffebaa42725daf0e true -check_ring_signature 3bd7cdfbcf5198e274d37807599472a5aaa4e8e11b0f8bdbf9bf5818e68f4994 45adbee75588c423a42e8ac5e7ed60050fc1eae099b7ea604834c3375ab0f964 4 69b21e5446071420a547426eda9e78390c092ac32cf709598fb6fd5bcfd445fd 74c69a93351faeeb0d035eae8b693861a75e17cff9845786834c1c744a64bb29 106d4d549e251fb375d013f0b750c635b6adcec60d0b1e68d47a0f7650e8b621 5522d0d68c4b5c288667cb0edeb0512fb2f1332a89d4930ceae5d3d979a5158b 4ab558e2755704d57f97973982a630f314f2ecbf0c387a3e8679a2970b18d50a4eea906568f0d9daf7601224bcb412b5c90acfa8b477a695b5c1025ac3b7520e82c7c1aef2a70f6bdcf76c4739338de89a9ddb7c22d8cb73fc47bd102fbdd3022f088d28e67e4ae95ff5e9b8bb9dae6ae80dcf68f72f92a235571a09526875d49b3850280cf997b642afa6c080491512d8a087e24859cb3d2f7fea0ffddd6e0f448f97cd48d8bfdc6f4ece7b81f149cd5332fa65dbdf2635cad2baafd11cc97f924316ca9171dad2bc7b20c86e9aed10e17e8a118da868dbe122131edd990e0cdb7b0b810eb357a315f70a966b2f67f7edac40ac2df6e1d90bbf43802e24e51f false -check_ring_signature ec4898e4ab85e27b99b09bd7e0d6a2d757fd383b395f86ab7c256a72748f34f3 ce730c57a7a476dcec0268a874c0ffc32b8c5361df95e291d159df944f0ebdfb 1 c6e58d2ea6eef733c80977e92c944dc38b5b972b4403560ed23810a67cb1a522 b45c389249cbe1e4c4c4a6a2a948b122f65265f2f4fd689ccd8788c6c5341d488c8fc2d4b85caa18cf0effe584e077268335999f9f84ccd38518a18357c2ae08 false -check_ring_signature 698fe679522b829972541abe830e1e8f147bde539d1476268829e3d5fb6e4e8d 6c7b07d77e0a6ad54a14ad8fb580c842fb83b973aaa7c2b844f911dc1179f25e 15 86c87b6d09d66e3e7fcde5952d6a268f33a9d46aae6604d7bffbe30715a11bf3 09b5537dd65661c642f60ed9c29b7ed639d146cabaa009ec3cfa9082fd3e3b9a 88e87a91418a3a33367fada2cfefd9988a138420b96c1cef4cc01ff76f6d0c58 b0c0c7f79447ac3bb40df2b16a2ce5feb8036ba2e86d431db7d71cbb859e4c9e 3da5f770aca7ac4cf49c6251e4ca2e08406693fc204b1d3c5b3575932af469f1 2387040e88d62c2e9775d480fa136ebd1cec81b27d3855e80f12fd43cedadbb9 aa586eff36b62d931d45d68d4c1e291f39e0d21decc8235b541c2d86d5b4f4f9 8971b9548c2546292416354f81b0b36e6bcd6aca4f5cf3ee86f984226357122f 52fbc969ad1f99e170a015287840852128b5b5234cb65c5462cfe2a0798cb0bb c065d0cc1f53fc5f4ed25ab3351659f5e33a5a6c6fb65c132e4d2c256b7f4d51 cea830820dfb6fb23cefd29b2cd3b27092da6d03377a1c55787f58ef1fdbe7a2 0159fd98627174f0c2fbd9ce4e098ff6d322e56fa571de124eab3e6858701b7d 55a20951969d0cf3befea893c0e46b7b48004457bbcbe5c3af80d027b27c9d7f 2d9843a014559572be4f5e65e03312b043cf4d5e2ab0709a47fc6ed832909014 59573aa1601ebabafe49d36a669c6218428dd02a7a7046f6be5b0dfb405a8051 887b50a8209973b6ce81a89bc8d052e3d096eb53b2ea82d6d1cb56e0f196700c72550e61bc9fb94de152e88b6c4bea0abdcbf9be8051258779fad06496cd050f1a3541c691ee85b44eb4a6417cb379894f81271fd92dbe54878bb2ef18ab450fc5638ff9f0eb39effd41bb8d6a0e8c45b9b57eadfd3fe35276cb10a89cc12a0c95b6e60277df49db6ca5bb8b9bcf251f936d754a9b6d0faf74badd7fc360ca01390e035c6a65a5aac3ada8a5f72a18761b463112d15de0ba20adbd2270b5b0068409595ce1f0274f97a329e5b823388135c6f01a168fda2136ff6ba643efbf0ad8cc8ea2681d2109ab8d7f146e8ff78317e69d06a2594510539be8e00eb6340aa19ecf84d2d89916d03971c4e0d348ab48a2b0fef591ec3a12a6a1b60a8a5509ab57d8c740c214afb5c9899b23b7f5695deafdd11c4e46e2d55cbacebb4c9002207ce4ee0d2ad0c6b001bffc8846e41e1f7512e8b2d24b4a17bfb6c8cad6ef05408be85bf976c72b0717b4ace913db71f2023765594853879e6f1a45ad683a063e4996d01ab36368844586ed0739edc70936a4818b42bc4ff69839245c5f8101863d0e0d3df8446d104ae2e010350e99aca33c3c6eaaae8c453972ad55d4a808d38ad7a56a530f040a04e7f8eb510ed18c5581ddf6a50a247b2d6c2116b2ae0e135d461a6da7beb4bfa2473b20284a72d483cfbe2910f16cd7c3381d683f3307e08c4e8826123e89e8617d1a57bd2b972b6108e64ea5d30336504f29f96c5d03aa93c824ce7003c16522f4d9f2ee6ac2c2fcadbc94ae592a1b54367f8768520630641a3622f18bbc09f00df8e37e53ab489ffe026a85f236ab107c4aed79a905084c550b64bf6f215cd859ccd0349b63f6806af1da25b1d0cbd7cf905c56af056503a1269b39e6e0742fbb06aefeb9b32a10e7b8725be71ddd1247a5e58a410e56e3c051c2ef17b653c92facc6080f5d594b883ee85b215fbd3f513f02487409beb603f678767c7147a977977080071745c085dafeb975be44052d21ddd886077e8e364634b6a9dcfabe404e061e4f2c3ff3a6e4b48f3bf922132a6b5271a7035d2fa62cd79a6902fc117d45b30618250b825871a5d2562a61995a41a74dc2005e81b7e177990425ae69e416c6ae7bfb875c97a7f8b42e7577dc1198899325089db44cf977853341baa859ce2933e18479905d33305b306d1ff1ad1365489d0d4320baaef6bb4659d0e89c2789f314fe4a3f8784b89546e8e5062bd40c01820d24176d546e96bdab552dedb67650f78e7bc87cc37c49debc4fb3b1e08495790f5c6eb2be80ba9efff7f55f2bee165aca494f4207f4c23023c36c09a8e1a0e80f true -check_ring_signature 17813344cdefd6450b37c9029860fac4f93188a98dc49038a0f04c2d3ba685bd 5aa4a8f62e0ff22d3960b77703b87514b9062badaa886ba4d88dac385332fd33 29 5c7188b528a7a8bdf7ba61ae3d4ed37b465d921d39c033ba9b939947d6372c64 13f1bcd7098339bd06cbaa1fff1357e715cba687871c1f5771812bc37124cdf7 ad22549a1e66bd8bb4a1f88c604ed3d9809448c7deb0a975360bad9523c2dce1 0e3b9464bac05c05729eca2d0ae3cde9284e3969f7b89545499cb4fc950fc7e3 0de4581a4095e3101facbc44a82f737c377379e684192c5a1589818cdda43e21 5b6f85b322d6cc6fc326275e313275385f2fa39f7483d02b5dd5a51df4416edb 9652f4932954fd99b7c9a9870a4783689e1736122ff8b85edda01d3bb4de172c 7e49ac0712175e17358335ebada2b9e05558fa793c04bab90c5119cb0ef2776a 66a31b9bd8483dd8cc9c0a8780882955d3b5286810d799a92a8d5695cf80f087 6cc1d2e0347da050f20096034d693eb754d0d013f53047f9ee473c877898a6b1 44023375ea6244fe05031af395c50ae0dd1cf5bcbf5064673c59b1fd3f5461f7 09eaf6fb00cc61f63dbfd7e69934ca1c38161e1999a029b1e561cdaef44aa83c 331bab468338c978598964e8457182653f58d209859e0be9c71464e5afc91e58 097d23adeb28cecb50f72addc9c742271795df7c470b4a72fec8a599fd4742eb 0a392f8796d6a670ee29e5b6ae72c60b3c465a26f2dec2bfb2386a4bde7f05b3 a4d7b341a2ab7c693fadd61d3f7ad1461f1d9c17513a5176c0b2e40fc31b664b a9eaa937920308d1014d823508b9844eece77c8a2f9be8c9652cd139a565daa6 f23a325d2e10b652b5625311d595590a4d6645b5892223e07877d66de070358b e0bedbd7946a9a4e955185023f0d5e82e83795d01db0404c9188da67ab337fcc 9a8752a11135a1a671c653c09af1cc9174a5c37e811b29b0ee5e264cbed1cc29 a9babc07c10204658976fc09b04134239efc337010ccc16d8aeb57c9d3bb518e 8b26b50fee51a5bb04c6bca79edf76cdd7156dfd1245fabc12fa12c82da1bcea dfea33d8c235900be68879c67ae3b493f48c291ddf2dcc9485de5bd9631a8950 ee84566df5238f78111b2c03e35028613224a5036d234c74a77a36e4a3852872 cad07d9e7654919bb5bd9c7def4fc1cbf6cd4cef0000f34045847a72aa5138ee a3720538ab7210a88bd6e6ea798fbe95c1377f1be4e914effaea1f66e3a4bf92 b193b16bbe757392e8de6dfb6cb05f3c649c09cd0f729b89d9dca22e5458bc67 6193e62b0ee80eeec11b7657caff21182ed8487ef6ef07c44620aacff4d8f517 8c89169c4d8351a48779dfc2f98d6bbc3129eecdd4fbaeec7916fdb7566d30f4 d3c847cbe1b14c8aeee39c3b3a4ac6cd7234b1406cfbfdb34abd8b2caa977800b51403c88b9a73d2dee1aac5f91e2056a35fcdafade426cb1db5aae99a709101a0b4bcaf31324842cf9cafc453afc8c04e9db68ee427c346cf2bb264a985a70774c855000c0efc808aa7acab74b2d15e71c869a058b938fd031877b15d1fbc0a3af465f0591ba303c2634553c0bb74ab213d0063ee9022c807a6e9b6d32abf03ca0e7b3aa3f77cb5da8aa847784976b972fd81e712986841eb98e74a8ad2300fea60b478462ad14d47b2ceb98a50804c98be32353f6446379836aa88969cfc01c43026a52600ba844ad103d0d948224321dd5dd6a111f833bc8a23aa8b9580053faa0e5e76d9216f56069f1e0e09792b64f5769973530f427605c0433ab8fb0ea06bf56eba70d11859717a22b1e4f807d3ec8bf988ced186b686a949dce5bf0b8d2af301c28857315c116b81a5e10b16d0094f976cccc3d2224f5d854eb7c805fb58a95656624e399d7e94b5e651be8303413578ceafa80bcc2ffc47e8699d0a87856f507b2618d4968b38237f33cb293f5f6b40c8edd1a9b2fbabf83ebab10a89d916273938a04ab21a6949e68c218704a79bdc4bb244c0a0b588e1cfc59d02a461c01b2751636b6f9866a920c3c95257237a66c77d800a3007a2e7235b1e0137c9c6313e3f146c11961145e15607c73a2239839e07c5f780b216ca19228e054a9b34f6ed1422dadbd48c0a062bdfee7a449e4d62b618c458d05e6a9c1a9405b84a03f160f4f0bea5d47dfde8e25120e595bbe38a4db9b0570ef89b0af4d003c693d39f04f0380ff6f16e03edcc0611516c8a346eb1222119c4e458569b2d098e0547ab93ac7f24ca984be6cdef52f545ded7247b03ce6842f0be23bd24df0d384e9ae67a751f05209dcbbd4c91840222e624bcef015b82cdf16f42810a590fa3beb21612202d4d704c8c44b85f693520a7171179d26bc9181eb285462d6d0a647d43910ef516824c6440b8156448152784ab1e8aa3b28da9d2c573d445680f91906e9a5c19ba470996ec2a54f9d8163fbada43b5d5869e98d463dcbad5700d77e39ecb545d9a7efc36ef5669ceddd83fb29a25b5b3430f79ef295a4c29e506dc7150cfb7249247b80ced4b96586c130b794aaf619e937acc11e394dcae4004cac7a6342341603b386906f549c729187006e7a9d3269c2447571ebb5152bf08bd7056af768561a381292d3970ca20b5cd0cf248cb38ae8e36dac32c00980d02f073cad07152ff9032a5efc9a2c93cc2a230f64bd18717603f39e3428cc954050073fd51ac60713c3ef8031c7274eddfa94d0a27d3103dc3367a614362e2e90274ae247c28a38b5b89d416b47e14b5e1c4b02578eed49c7fc43e4fa452748b09c988b370da27fac5d5375af25eb64cbb3c00419daec05e820ee240b121415b066251147df6337fd5e88bfd6ad40df897220aad9cf408313c501022f0355f1a02c05a7e86d5ea3b4b93b14b644fc05234eb6d75e2f184b0ec5be6f962ed7c900a6df82b5befdfe1d2a4d085b69b7c5a64448dc3f3a024933cab453a3eebce3b0ddce798d9c595317a4068e8e989cf307672318c5673a0b05b3c641766763674097a9c45253727624eedd0e1dd5a93dff20b1c83f8362957da70e232d8eded9809eb5b17098e5dc5592705bf8dcd8bfdd7c5a3d3c6debe474de2c26d3bee0ae40c2ed1d538aabc5641e87968eed564224f9471f0f88e013da87b423c4d5594cd0c4310f452d16523235e1a04dc9745f526b858a87593c0b1f7d6852c930522720c72fb19d46a051e4d7fcd7754a3379e1b2c0636e2d2974aaf8f34dfd00295cd0b95d4638160d18421154b6c2e3fa451b92e3bdf9775b885528797f1660174230455a3e02f29ecc88d72d1d9925908af85d7e3127848271b6156398a6c3d12c0078e4eb555c7ebf95bf447a551c212d6f8fd613ca6cdb4ec8bed9e033d2e06540d25ad14142b693bf741a9a6a020e78245c4f1c90846eaba8b6294858a30bdbd064a6dbd30ce2d74daf90b6af3ba1fdd80b74ddbeb48ea656b6e1c6a819c0ae601a7bfdee3862b94fb47233bf6077f8366b908f786e229a5ba107d719730aca60196b71cc3e9e942465d83b5a78561d01c3b3fbad7300ebf3c0acc3517e2302303723d5a429039bf30716a632e7fa54a7b8bf4859e6d38b933ddd78d5bfa4fa70f1b8764ee364c38016a531091181a135856581347bbc852fab8bdfa446135dd04b1b1a90e26707764f9b865ee22e7a0d9cb48ac5b568b07d440294b3ddea5680539086e9b8b7c1d104c925580f5b1cb30431e154bfe970711f09d96e02a8f1b04b418168f2e6ec1131b0f75a1d7ac004b0728f3b597b3af37a235b3d3ee3f3304aaca4bde67942d99433eefca38658c5dafe3a5a98e8c9f8cfcad30e053947105a94f923da1e8cddd727bb7faec0e38acabccb2c5c1cb270371d584fe3ec89c07b410377c6c748fac9dd250e5cfaf76f84c0434425fb77e56d283513354b731064a4a113141064208acf85354c8c44c15469e647ba5b77d62d5536bb8cc5cbc0616b981a91d7dc64d15098a5279ab3cb06761ce952ae5601524d89d2c0355380f true -check_ring_signature ffadb6dfa5782b1e0798b703ba5d0286ed7a9bc15dd8daf087a7c3b65442cd22 b25dfd704b1c798309edc0c33c0bdf79546f6ea90d3c8b2e581b9d073c7b4d25 2 10233b334f73bfba8db75dd6e5c866898df8d39b73a670e3f239e1e4c86bd763 4451b0996568047c386a06fa105f7cfb14bd7383be1ac8af0417f89095f474d9 4084de5f5c5df74781806985f0ee08446b3d47a6737421bead4b549c3a6ba104dc6875fb383c5ebe1c3b3c26b2c77f393a7d8dbbae1f127eec0ba609debfd30003854f68f6408594d509c637f828295b39ab651aa3ca49651f88fc8ae234be047f4114c2e74071d26394b9a9aae103be877856e737b7348ca023afd762c79c00 true -check_ring_signature 34fcdbafb9fa17b5433bc91630e2a4ace0312e36c030780bf4a176cfb47f464a 2135bb960824882783e69a54e98469e10676759f4cd3d51c307fd33a9b4dfdf3 25 80f450f692fb852a057663867923689bd1a39b689bc0b6f6d91d223edfd3c10f 4885ddde73aa19ebed9135c04b20554cc2ce5450f4365555a06dd53f34a2b11a 863495c95747ccb091ba7b1912d0d82bd548b394dacd1b24eee4e86019fa5bb0 caedc9120fb1ebfa014a77d6a16a64bcdcb7da583ab7a7cad203b330cb6d923c 4e5ee8acb41e7d8c9c0eb03271d6a3a4129fcc3861b2aa63b9dcaaed3f4c7080 16fb6e7d62ab0f57375e52db91169fda338cc6020e55fa4c7e20fb4eab111dfb 8c454f7f9767a8b550a5912ef625fa323fcaee82f555538a8ead20998bb1f455 74ae5a923e7c694d715fcd2460157f5acdc6f727c3c7a7e6f69565bfb94ace0a 31c4938d5e259a9fc5c2a8003b77543c87e1fd4309b58f457bd28dcdf95187f0 3ea032742a7bf318c6767bfd237c2654afa1ec3ccc5efa3b8adf7ffe2b59e2d4 031dfe6b51197725e0ad351db18865794a034dfdccc0f7bfae22e504f16bd5e1 1ebe35b459909030b35cdc6d2785fbc8dab7f964ef2776d1170c79ba31e48a15 ab35857a4a187fb8a4d90badebc9709d7d29c032c1dd225c004584fd9427ef3b adcd9976feee8074242089acab8159980e65487e9f4e9e2b33bbf37a9e407566 c0a0cb033af6c0237e2cb9b77757b1382babda22a77e8262b8f5ad56d0bdaa1e 716d6ec5119fa6d32cd117a2e3f5ead1b8f2867c1a4e6f4e3fcd10c9fd0481af 448e963dfd3858a5cb5181b04e4b7089640124ed82f26caa360e2a86c9cbd486 de3510bd53d185fb1b01844919fb104925032a24702443fb651deccd7f11a7c1 d58ea929e8482dfb17168ee1ed20a6131d72261527d1d735d9ec4c2cf62e764e 653e2ee6d7b411373ea88a844253bbfec271610e493168adc6aa96ac0fda358e 0d393959373af40f8a0cf2607044a0a360a8a772f0008195ff6699cbc6effc3b 5d9bc98e8ef038313426fe9a849df87e7d4e293dd4647a0b4419ac72e2932290 1c47ddbab6f0cea306b9849d40a853763d1b65a074c34c994ae072f699fc6658 9a9c6c79a54c038364b21411ad9c790a564b93ad4f8eb0fc68053bfdb565d6af 560fd2f811516935645ba863a7b4928a2fa8c7cf2fb5472717873251c322bf6f 428133a4c9f8289b3e9d0cfc3415dc3b84e38af683b98db67001faf09bfed3086a5063732bb8a10259b458c261472955dee6b2a33a58e401897ed9ebe67da70c953a547bfd8986fd20cd551efca10997d66ed1eb2070e4d6d69b9af0e48a940f2db7d232391438b6821e431ef60f8e0092ede3e4b121ac0f4e2be65066dfde0400c7648cbb47f90f9606c40152d92f66e5d0a0b8a38637193923b5ad08010604337734d48ec56d07920b30bd391e1b715287b0593551486d9100f4c232f9fd02b6d832f3fa91bd6c1c5128c19f9dd30a6d2fc5252bc216e63354353e2b90b40264865e426407ed9af9c5f879e481940377798f504b66202e2fc5275ef2aba80a5824fa5f4a281c3673438b99cc82dc958442a9cba4adc326cd362bacc1ff090acffe0ee6e114986076f9431c5510678a1d9d181c6222a7d4f261dc06ecbdd10e874b2d46117fcbb0f0d6f8748bd271437b793005ffdc2f062bf4f9f1bd595b0d31b3e40197c74fd59a6b367d43b18c0158d90c256e5da61719698c31fd76ca085a2329b13dbe370860f622b9ba504c754332a3d97784a80d8f844db372195e0ffffb7c529fe9ba102d73470d3efaaacc5f7a87ef2c54b2ebc5937a709100ce0b6f403e13e7aa230eae1addcd257654a709add6f92049981210a001291dd7710d277d62de3be2353ab01b33c9be906de5a26a8b445cee3d709238e9bbba40c20f9b89aa567d8940f684f3f6a23728bc92b7ba57ae5b1012e01d7da69a34dbfb0374d53f142b154afbcd1377ef2b6618391479299b666ee094bf230c3485590a0a266902603003480d777157ad9c540cc7461201b1b5d86cb70c57745c7fba100ec63f1eb6d1f2be66b116be45d28f105b5e9e42abb6f44c00d2d8d1aeefb2960d54857710e32935676568406487a301d5dc1e621fb767dc8ef2df0705560c060a136d26f573ceedb562590fa0faab044c9b28ddb5e082e2aa467d63452238ea025f25524484ebc809d3aa5a283a29d794d0521f0f02890b6541c8994c70a9920f9c105e9a899f6e018c9797663e1e42c3c898eb2275ffe7662bbcc40dafa6640f702a23551c491b4f8e02951be82f52bf75e422ff9c97b59e1a73e98180002a03fe77077d30b0742e6378b8871cf9da4bb4543fdaf3d19d6be6b9d73dab9cee0776fe8ac2f1bc9ebb21c36b5c9f92e0588bf3cc05d68f1e3904b7ebeb5a6b2f0bfcd7119a87476f3293b6a01f6506cecb8bbf09377f97886c2685d15406a7180adcf19472dc87856ba273116cba8166d2b867824f33768c8b85f88b274f116f0da796df6f1466070470d283e90ce37cc81dfa38e272c0c82347ef7c535da5c802ce256ce1c5cd404cb90bc39a23976d7a112a01b6290c59fa54332a7366b59106302ff82b3190108a82f239e01461c2d72aa77d03385d7d8e3f6483f320aea30ef9631b9c04bb1b869950bb173bae693df4725cc3083fdc414600e73fef7fdf053efa536576dadb7eb8f0cfcca53eb63727b46ee8bad6b908334899133c44ba09448ed0dfb9304476e57275bb61a8775cb8b5aaaa6431f77a6ae42b02945e2706a82f253ef24a9d40333dd996adc2fdc14c78d279837ccc72272623ffbb20870541d1675287aa220ab7f6d4fb078eaa56705f8169b82f10adb9842a76728fff0743ddcafdf439d3baee22269f0438199041275335481d82f29308fb411b14e70f15da534810713a823217d5051dc2fdf1e6e29d0711f33c87b6b0e1882f586506ac27e6d2e1f94a1a2176af6100b574710a85c6670c53f74ea0a151025bbab8050fbe06e8aebfc2cfbd6d6de14222b6a22ba98e751c21dcd6f98f62fd0e8ccf0c6954e952dc5772c0d4fd967828ac3485e958e7572c823ed8bc92245aa6fdd90c35d934fecba79ab78a299bb30aa053a7c6a16a9d6ee022bd7e9454c8ee0d1b04a4e7552d01674c68db631464a254a91cc64d0beb49298ae7bb4bb4920452320cda2496ded451502a3b3af269d6e0670b57088d9f3cb73ea1fe5a19bf7aa52407a763116f20eb8f2d559e8d09186340bcf83916cd04ac6d82685502d12ac5fc0009c66817ad45643323387de48ff323f43f0805711cf7bf2d057bf30d09c3e60529aeb40f0d43bde7243dbd6c4cb6723d699fef9f63c354fc8ac1a7b955e39a0ceaf4704d389f000dfe2c13bbbf6fee848b05d9c83194ae859ca2d4003d65470cba7c8164bb771f0af815a71bf4daccc895993645da57757d40586ded0d8c7602 false -check_ring_signature a66f703058d6cc5d7f7085ffa48db65329c0fa7dc52d47f7ff5770e07d41347b f9f49ecef16b88a3f2622295f6e1fa62d8887027d86f82299f66e155d677ffc4 30 4c6f9e3aa3d5fcb1c851ba1deb30b93f7274425a25efd56a4c07a113a76c9c7c 2ebd84b4f9b9427de7662761f4d84658292bd60aa7e4aed753be807b9dab0c42 de2fa90dd26fb8a2a150e1b10b46f1d1075669c44bd098051fccf8b6bae27ba1 b01241c3ce6310135fa1137069c05f548bb2b25c25ad74bcc7d4ae8cff5a6ee4 471653586b89e490734612ae5e05ddc1802e1a33563ce8a14650f0722a6f0368 06410ba4e91bb6db3219b7f78f08384ff4d05060cd09f25667dddb91c85465d0 fff2a9532d6f357651689fa490c74d53d118fe767d23db6c0346a415500b5487 abbbf79a440e8a244e3265869f68cc38144952666ff040c346ff8ffe96e0b442 f3fb5cc86510d7b773630c7fe7fdb3495d9f5f81e83730c21261e6000d689869 cd441cddb11bd34e80dc2c5c76d576796b4226a79a278aac986f2fe50cc05618 31df5d35c819fb323273ee09445ebd5e24c258af744c786825703c64d802ad69 dd62d156fc040a08d570a9f00d3c40928400d4605eff0481c4604703e75917b0 41486666b799e26df73f0a2229f3c1717bb6ca89932a2ab781ad21b670210b2a 04c688e2f6abedf3502c294f8ba622404c7d548a016014adfb7b769597847e9f 940b20aa745c3ce85b60902aa0a44dfef65e96d29209e51f71a360e1ee7f6f38 108f173f9146f49bca95daba781c80f55c77825bb71a358e8e4329e9554d42b7 dc01e8b2685be7b39ae72390cebc6151baa64719a9ed4b04e531c7caff9c14dc 7ed1c84de48d1a1143611b5aa4f87b3dd018f05f1d4e10a737774e368d10b7da 63aecd048adb38fa421fcda9de6505b44a4ec3c558b4a6b4aaee98683b7ff99e 938119524555125f6422d0b6b81193f06e2fd8814320678c8b32e916f22b89b7 254a721156f6e8b9cc70b44c2406bfe1e64638d360b1da8685288b8f263f745d dd470fd8e82d8de5585ae80474e304066ec3c9eeb96465e585851cadef13a8fc 97219a6aff0d26e486edff0ac55f1dc7fdbdaa4f2e73db2c51b7529fb6a82021 cb629a6df03f71753f727e0a37b473e03b6075883a45d52183d7f29cec7d6440 ef4de38da612cdc39cd24c5a0deb78e78f33fcd8747839f29a08f89eb168c588 c1feb1c058b8e435e57eb5a8f041ad9518445572d726a8697703463398781b05 67967747268dd14b681ce28c6ad2410db2a1dbe3630602a739849e4ce955b774 acbc03eace695fff94add2202ad86a59b142950acab4752162c092695369bcdd b400419349c2e500805a3c1127def83cdf600c7b5caa3e7607e883d636dac77b 2c8620e42d0bd76abf8339effb6a19bc6a93002c3498bae34bf32e6fd485b224 6f72a819046d00fd096d6110ad63cab86c46ce7dc26dd5a2b30a8fa73705380d9b96b9ad850754a6ceaa0ceeb386c9beaf2fdbbe6f7bf2770c03bcbbf9e8de0b5d6b1ed54af6d7bb35a062c2a35dd6ef27ed78c105ed32f3ad4cf8aaf219670c74681425e3013d288dc1a8b313d8bd2b5ec97c5c4ad4b72dc1a3f9ac7d8767080d62ff8ce47843fc06901f602516fb1099c8cc53c5015c4aac14c64436c1e9086631a44025ef52ebaac821ffaed105fd03c5255ce7ff76c900b1b8acc02dca0d52ea43207221db08371eeb27c2f377f31bd15a074cfca9c22d094fce8eca1b0200f64e3cbfc61c758709bcc213cbd0b5687cd4d3a2cff6aa8d4e076d6ea8e1044bbabbc958b9a775463ac472795c8771e373ccd3e95096293cee223e28ce4f0e81cd96a4a0458306076a56bfc9f0cfc98d029df773f5d71573c03a95a107390cf89e9b72b81f780843ce85f5fdb8d216ece94e6620f02bc9870cf0081fb9b709daa7576bb34adcd833528dc36892d0868245482cb4023c3271bca3d68664dc03f2c8c91570b454972ccaf966673c1378652852ed19c7fc837218a530880aa50c6a23c2391e0aba735eddbcfac00055e8d166d8810a5c5f05836cc02c960b76015e575b049ae68503d50467b0bbfac2c5665303be4b417d85bfd21072218d4b0b8041e70ddecc74d255bbb9b01eed194185206c941b8c5d7df6e2836083bae70b528cd7226a5a3689cbc3f16ef976063ead820a21dc7ebc4e0c2889d03c593c09048d04d7ca0303e3bfb75d020b272446d93ab625b9afed9cafcebe845aa47f0ca75f3a0f3ee896326b2aa8bbf6251a94e23cdb8ecf47f9dbe838855580bd5606a3f197ba373b5e2c181cf77af0439fdc710bb64ffc8e34226dac9d3125efa10a0380bad0c5aa553e8b462add5fce522a70f301d23827cd14da25f3c420baab0c0d06cc671b2161af209d60f34e9d2003a2859e8cb726ca0a0df55ff82a1ab5024be3c369f7ca4997c85fcaaff9831f6fccbb64afe8aec22575e202c7b4914b028e07555a6c61c1dfe0586475e33c46d3bbdd239881a6666d0778e784a5ec1e061c584d4be8150b788c6519ce39ea65f170af2c5f60dd123902450ac5d2b1810144578d94122c165460217dad0cc6ac20f83bcf19f84838f04f24bdba8663ad0a82a702458f60c2b6f8f067e73a016166fe25c2af4042bf8b57f1619d777e5408cc24afbd4e73c2276268eff6a41cdfa24dc7132341bfcff8b228ed8500498408988215470e54910c3b2f349129f39e354e60a8c29d77122989eaefe6513e72097758ef07ca9d857cad4d27908f1252fbdfd0f4f2ac526ec222055743d55691077366e693f5e455a25881e3e2ba77e9b36fec58a1cf5e59305a2b2c1b4040920b2eb2c2651f63ae44da78ea13259e87171a826eb8a456bc6d08afaa7699bb9b08db89f0f3954a522a93b758906d9f417523e1f7a0f68de8c72aeb4b365a9d7a0aa217e408852fdc1beae25df8dd1167887223c65ae13421c1e4a503210f78f30422c43cbeac798e93c3b8c80ff0accb1082dfddc1090f7070f773c399797b4809279eef31cf1aca6c16c0805746a08aa28a0e11b5a19991fd8574bbda77464e028e1d1f7b1f0e1842dd0b0e9d36f07fe84c7dc3e495775823044e19f9d3599e015285f88cd1dabc979def8de9da92b917eb828a36af43b3014ccdf7add950730cbace1b17c8059cca52925c980fe908fb629623d4d0e32540d9fd4db13252d90888b7dd722acefcde87ce234f5f33e765b79f04a28baafea1ba3c2479c42a1f0f81ec78f761e63310e227120f03f1f33cff8f6b86c63b7bb467dcc606fbc7cf09e5332208b11cee5a01ce0fadd0fb71774b7618d5d1bab55226f8ee6162aef40bc062d8f1fc10d66b7a3af979082944ac2a96c1ab070fa7e9b54598f54d927e013a3ad2c2d376b10eaee4c889fdd500763035c1455f94106a0c09a5eb23ee520cabb43684439e2bf9dd1aa7a07f79f4847f6afd147ecdfa9816a7d35b4661570d1e605e9b6be261d383d0a5dc133c413176c2d5aa57a1907f408562a590e5860d0ad3a069fca63f8e8db5e98719a74baad5a0f4140847aa0f2a3d24ef9328c806a2080785dfd03eb34c2999942099eda4724fcf650656827bb9985ebf3e1e5a0e58a0462841708474d6e3acaae4ffb0c8cbbd4380b523b9740d179ec59a56dc894ea23695877dfc946d7ddcaa929303bb99be4376389e8e5df6b1f3baf4bed40535f3c47c69dd77b7c4a8cdc1fa7d22f5baeb94f5e25824208b36e91e40fb4e027ef28d4d5d83d284c2feb5a8dfc9417ca5cfe7faa0b1495da9baecf90daf7a0e5fa50e144f0616b571420f78f079d5fa5dedb496dd6b161df169427ec29cbd049a391d8f8d3700954c097651abaa7b752ab0b36b62c84e383bd47aa9c1e9350c641828a3efef5b0c59e6bc1d78afa3b3ec5bfc57f1475bb140dc9682535e8f0ccd948887eb482a2107e885ce53c4c2a9d00d6fb7cb6d24d623b56aa7c403240b76f4134fe957eeb41fe4c990114c7fb587ec0efadac17e3723fe7f27f674ad0f54c976f072676244580da80c1a495ed2abc3ce9e972a8530fa2b904d7f780702773e4bfa7727508f228b4202d95da4922e05890a00695dc11bef9307707ae5029f9e8f601c9ad24d433b5ed4c82712fa4a3333884602559d784d0879552b1a01 false -check_ring_signature fd1213b93bcc191a39ab5bb17ed1b6ce138c359107f9f3acd98372ca86d1c7e2 dd4f8c9616f1ecf93c94dc98086d863376f55866aeb7bc8cbf369f376410c348 25 e0161d298c63720a9f1851e2494f5f8717ad5bc2f6a6aadfbf3be9878e74869b 2b9a1a8ba58a4aed43bd00e2c2eddcde852035581529889761ec5e880a999f6d 0c915fe47e2a82affa1998c302af3a129181f6e2d8099cbd628b50af926eea5c 7cbe02084879f56a1f64b381c2484f5a336e6b001cb571b95eb654facc110300 e5b1c6d26125ea96fd9868f375d01c66df6451fff85106b03cd8979bc0f04db2 8e0f094711967497a9248a8fc5d0e0a688ac780e49cfafefa1dd421133cd6926 99d3edd6f5279fa8ea40cf3bf991ef2aee9c11f4ce164beaddbe2a93dbecad85 de9cafbd89c921a87e13180c6d5cf3da64eb185d3656637dc45586197ab05a61 dfb945c837d89f7bc38256a3b3f188f8d301c6b3a0952c2831f836e06569eab4 c44b2c2ee5e98501c7b5439fd255fc8437edc847d747e004baab7f556b90cc09 123336f07074ba17040fe1177efccda0d1f31486478f320ce28098bdc7fe364a c156d3ea9ec8548400fd244a951e6a4195c486f1605572a1141d82030ef459be 514de4fadd924ea51165d658bba1d752b5216392627b64175356351a0e99f426 ebe028d200dc01e23a9169fbd2b83e49636651cf697aeb155c6eddde47776ba2 ead1271656503ee8d251babedf4b838ab6b0f27bf6294e7ff830814a44892a1f ef708541c041f7d25e17eeac0448b98c60aa966f17a323475ecd301745ed6e0a fa16ab3655f63b0f36262d814393b8daca1572e41c0d3b8eb5e24b740aa09665 964f05011f3cd3f2e84fd8f8d8faefeea13521ecbccdc2972958e9bd3a8f31b3 f9e569e7d8db17c3df8740ee9656f4adca5682cf8eaf97f1d00088e4f358d69b 5a41a2d5f088feecc625daf81d8cc8d38a99867eee13eebbb2646c6f222f4d5b 71ddcb1eed5ad1bbf3e7724f537a5ac7e35fa915055d9b0ea35de6485fa770bd d123266045fdcb5c84f2d120d016c86fc9b2810aebca09a2706619ee09883872 e1a99c837f0dbd016ce3102e6df8b1d6f253b3d575e51df83533d2c87e58b34f aa5cce9f33c18b2c56666d2834f4e55a8ae6bb8c39bb50c4e1971df530bc00c7 eb2a32336b142fd4a52fddf61a8a71cffb3359fe2b0e44642e72f94dd9d8f335 05539ec54f0a4e9f6575590c6ea09a87ecd6452f076e6258618cbf864ce2fc01750fe8614175694a28adae64316a542292ee7b879c2ef956cfd418f91fbf780c5a3b2fbdf4487920584389ad8793171b33a5f5af7fb0bd05095474b460cb830d6e21b7727bcd368cc5ad4025e42dd565ea8af35f1674ed86e1bef2008f941e0fca63a2c7ab9aca0ad514b7d0589fecf1286152407e00578c1a770bd7a1d3ec03fb583f82e2605fdce4f26b5720ef30ff8713feed270c224862d53ea490c6480a96bb16a63bfbe56674da91df1706b4d8bd52afeed4a478502602b4a06101b60c6ef8f85b082a0ce085b86db1b256d8595f715ed2562b81587791ad0f8107a10fdee54db225fbe7e679a0e191fbfd17205e3aa3bf326595e56d25b53d991fab09fb3e19c6cc156e42402bdc801fe064652ebf1cd878fe6e81c19806c77a7854043cd5ae6019956b64519ec962ff1625635861e429f1e1738516db3032c9a030098a1eb68284830922a4316c2eef2a6b941bffe8434e239452b14ed37be5bc380e19938e45be596caa0ba760f66699f660be5b3a75aeeb53a91a355218282a270ed7314261ec5c90f658ae277c460168210fb23527ecb42bc73e08656303733b060a3341129444dda1b7644a930d53a92085e0032a7e8cab8aeaf281704c933d00aea68c5f704273a03a27a6231581b4a8ecf0808e4b2d9f8d0c17a6ddb056320acb717e731b55f6d80dcee5939fb8720dc44c46d8c9157569c4c6c4a689ee2c0cd5e99ca62582d89e5ad22d7eca9b93694a40c98b94b5312e794ca9ef4d24020b8e600797b4e7eba9d3e164b150a71b60c311d906049ac8bff6274a4771ce710480b0efbb7ba62d98fff9a1fa951a0dc8f651c9ba112a9ee0e9895058b6c8500a2c89330469b77baf06d1a0e3fc82f902abc3ee8f3ae448344f59148eb5eb89055e8096d72bd01a38f7be72363db4b6747aaef31ce976873efa1afb9c295b7805e850bf6785b5706471aae3e8325c2033582b761926f834125dcb236475f4780deb898d31452c00cda71bdf8221d6cc2ec82a6051ac9ec59299c87162152121039e0c21ea7d11fab8349d38af41a211e8a11a5c288edf4f7ea24c129c41689804ddbcc93743cb5acb537e079dff2a94fb31ce11d25370fc22e1f0d36920f6980215095c5b50f4037a26c6a608ceb0ee4560a6cc7bc9f36f1e8bab738163384101ce0545a984928f9f536ff4788ec5e8d7abd520b2c6e1580bf91f63b60e0f3e08740d3fe23da9839a07d558441232f0100c57773ee721ad6f5e59ed6a02ace60736feba94acc6b0b7a106fe791c4c25f7a6104e57b4116b3c05c23d921615c20ed6c00dde7bf2ed9bed25449a6fbbf5561b2f895009a979fcb6f108862f0e820030c028988fb46dae4983dfb1aeeab7888ffd1368e92a7f72c34e9441fa034a075d6168770cae4b0a715fe648eb2c091beb99f2d0edfb381340ae8cc9294754066e398fc5701c99f7b8c6cda6142aa7561187157b39f8d33eb65d4e425da39c0351e0687ab2ec605b6552f8ccfb2b7a471fe918276fba1a18caaf8e8a09d8c701482f3300dd925a8c7ea9bdeb1dda0c8a3b3295fa810191ee6be967ffd4eb7a064ebd6237b97e9720f62601070491d4e3011e244ba2e3102d52c474bc4764e909b8427d17a436a181522354241ef12e7ce60ebd9f9dc66b20e54661124504e00bff5b83ddfa5562e93777f621e428c3ea0331064b7ef4289427ab788eb8a8300d35d795d340080349de9ae3563b8d11da172b9baf0875440d68a7c16d52c4df00e67dc556d0a053e802e0a661262e46396ddb8486fcb2f9eb6fda4b65ff00f90649cb43da62f649d372e9beabe1adf88c6a30ec87ab8a36e7e5e36998dc224e010fd3a7e13e9a1a05fa3dbb22caeae454831da7c51cab3953469a3f42082bc4037463af387d9be07cf95a284ea83e8358f9fb7d880e5d62f4d4c040c324b78f0df27b3e80110e7246ad3a967076a92354276c9afa4e80f3a37fa0841d4e1ccc0cd87eebcc2bb913d471784ad29acf1d31b46705f9ecac50f4430d21d0dab4a008b33127cdc68bc866aff63d8d564435f387b5318e400e1f3aec6ba01d0e51dc0175a7d7c8d83d161ceb37317e7698e549c876dd0afce568775489f5ce19b5e009ad20dabb438603bf1d3bea583aa930f1384c48f635ba65278c419a19f2f5870281064bb54dc8bf9ae1761c4b17b06e63d7a42d1b15e9a60d9e80c5c35eb78203 true -check_ring_signature 6068b29c8eb4a74e407534e4e3b0dd07c4e1ee631407fbcf56bc9d03d6ab5556 358e45531fc0f6b1049af0d5a5c3d66c1644551b1fb2ba93b9611c868c01532b 105 53f1731f9a7d343453ddc148b92c4747ae9d9af2613cafea51a6733fe470263e ba3edce6ff9330a0353ef26647fb0946a95ae18b9566ea34485be98550ecfb8e 484ba6e0539734a0478334667767cdb12a3f9c449c386056bf83cc65cf627abc 19437f927e230e3717f3a688aae48556be936f10c6a12862b7944a3f83cd21e0 002bd2769dd42622967bbee1eb1f2c259bf7f41baf884b97f24f55c72b1243cd 1bd67bed9358b66c12f72dabd34bd19fb11f07c1b7f4b38fcdcd7151c977d8c7 c844aec88d860c8c2b1fd55c2fd915f4f5b5b57e118725b09664153a42369cd4 9f0f14630d643c6cb9e206d90a17ff1c75fad66d8ac7b06bdabdcd3e54096444 a29f4d202f763e7b4f273aebdd1c6602352a4053d4b3ba1569474aaccf034601 e6fab9446cd5a1e4a69c543718b8eb8c1d0bc368c72ab9448fbfd64eacb41503 631871c4de3d775b510bba85e0dbc1741cf1176c24b22f3c115acb557719f3a6 17f7f11fec7d7760a28120e50009bb2ebb359e8293ed5687d8141ce803f417eb b9a50958d611b257ecf97ad712fbedad2801015af34bd5e478c2f748c1df589d 616de5ac68dc81d7520d3f0c2fdc597f1de61966bc317db2d2764e3e08b1dd68 79332d1091db8606a91a61f9fdf1a38ee306a8f81f54aad13ddcee5cf2eaf690 0f18f4a9bd289f1a27758bd1f90426d392efd0d112965fb58bd5d4e5e1e7a548 971caeaf4592419a0e426a3b03782cf7440654720dc072486b603a65e336a348 b3b09bc691157b8d481f2581461131f5115ce4beca6e74aa6ac005d214b2d801 bfa3a52bb693dacf3977bf6a7fb4dd738dd6a420285c9589c115b7d12477a31e 5de7c2e3225ca7d626daa1ab48fd7911a5735b17947fe467cc1f8dbb795bfe64 c7dd528f6f51fd93b6e9ab4caa8a7aa4c0c970b62a28ed9f26ac80ddddea4df6 264cd1af17d2499019e281078685fad477f00b11a71532f742b520317edfdf63 46baabce639b67ae26a52b99414e51aa34658fdc5c361b4248ab41deb9108275 9105e3c087000c115a8b4eb9a80b05a694c9782dab9762be83b3b580eebf7c51 29483e4f12e10c5f2b90d19725c37890b12f6c239da5b69613399b6cf68f4c03 c79143a100e75a91ca633d1fe6394f52c6f39fc19acf032243045a93d01eade7 4956c0e4e014a7c4462e895edca1b134b7fc49cfeebd7743e7e172008a107e30 72f9d0d674f402f2c47e10633f99117a7f12a0ff0e44b7340e632550d419245e c3cca439e099e3ecd0458bb947332f7f52d7656f3edfc8f6562be98cbb38c1da c1759f147bd4dce01c9bdc550dfa78bbd5dd20c9dcea6004d3b3677a82006be1 da0263096b36e571c1d6fe9d3579c1beeaed674be9022611a9175fc78e97662e 2c693e08b19381778ef0ab55103bae1000991ef3f6a5937233b9fac056f461d8 73ce196b896f12b880b38d5fbcd960ccbdcafd5546ed56629e51ee1fcd076fab 0b057e0cc7434bb0781b3370054bcd17e1f0366c218f542e87118902c767e9be 34d6ba64ac47047aba12d4d41a917bb39c2019b0240583ec3f856927c6498641 68c0dba5c361739ee6292063df752fa7c1d74a8d0d755bc6c498bbcc8010541f 78edef09fcad1dc2ebeb7f97f26635c750387d812e3ed7a4c12bb4c0d4f520b4 d59e9c3dd59458916bbb7f3e0b6e50e7e1322c9f040a9ae20135e48554be77cc a356d6c75faffca622a9318af489ad4e4da89dc0f2a3ba8898d675b457a34fa4 d2a9ce2394960b8c04e8f7735aa9878d572cb85c1e06443ed7eef308d1ffe7fe 527c4f0d21fbcc8e57688ca7dc79844612f0920c39137d097afa2853af4affcf 24f11caca5743957f765199b6f2d58a47bf17ad3ae75b311e1967a7d507fa928 c05557699e9c75be54e3a4843397e9b970d099ddae7e9b440aeb46f7dfc4a277 227931f30a95db6e2d764ac401e648249bc6ae863b31b70a6f47e2c9974cc1e0 483a266fe09d7342ba3fe54ae79624bb6842c76d3721bb1951063fba3f6a3322 f400ed4551a19d4cc3872119e37d0bc384bf9db62a388e5bdb32195db7d9ee54 4e7c9c77ab1b233bd53d4a3786408384dc390435ae18b7fa63e6fd47ccc10c75 1af2bec81adcf441e840e8654b2bebfdb96cf232f7cd454eb806d88e984ab1b3 768d6c36ce5f2d753437149d48bcfa219c910ed8e1c20f6794e1888ae7c7a504 3f78b08354371b2070dc1c29f85d72c489bcc46f9405b5a68d392b8365dd6938 e515aa7999a1ffe066d36430022c1d25f38d54dde69cad1ac74814e8d0b27ad3 b219f204eea7e237dca68b413126d11515ba388894aa2765478538845bbe9585 7eda4c86c0fcf58d920e1091003934c31c806a3e8e291ff8b7b509b3764a583d 59a74834628f61aa85f0ce2c248c76a145d791ee20584b27235190cb7f6da50b f86564ebc9b6e27106a0271913c76c897c5a77eeb8561a7919877b0d29e40da3 c605e41b6823e62fa895664453a6acb08f50ef95b366632c6445a771c41be244 9baff0c2e1a5d524aa715bf6e49018d8026b4d5006e9882a4f1b7bb265a0f2bf 7849d376f54103a610e8259b332442e3ea386a2eda7c95f7b3ead8b427819e76 de1149db681d6e990029c4363c516ad049a300a1923590651ea57d0a2a286989 eda471012927a7de0b57f3dc3675348a2b3622bad3b878bb0204ab8e4413a15a fc85b4ded483a17a130167b6a4025650767e2e4c050a8a9e247e2097292d5616 e1df1a8483e1860d3a49bc3804a4d0fdcc9f1b1acdd5c143e4145854809dfbb3 0249963bdcf0651eefe3270f1b2dcb9be6d1c82e2af3050c42dac73f4c1ed455 5633ce2fdf9250a5ca8d9301e40e76d843a8ee3e5f509db1d8337052c278f9d1 6963dc472a8e3fa9d60077a1b05cb20548b1aa29caa3a10211be336903bf0a3e 82c3453a19ef28870b0b0af20d1cbe206330a7943d86f52887870bb399fa78d0 a4915c6e499e7278264e92fcb9b1e58f95843036fa3a682960b0e4d54ea9dc8b 23380aa3dd78ad21da32e4545029d688a17bed2c5e85e01537e91d32a6d13e8d e4c6169d78b2aef74da3a0df6bc73071f63926d38a5dae2da3b83a2d1c9dd0f1 58b4326b231a8afec43d205eac534e7aa7365ded119bd7bb609e32d907a74495 7e9db076f7d910b88f5ba1283957ab3fb12ae9c1f0dedcba65ccef946d7d3adb ffd6ffcd8be519b10c992ebd3caac37f137f4cccd929283862eb8c82a62c7920 9a78e12b9fccd769ac5ab25eb0a4a645c4907e0d980d1fa4d41e434875b1b42d c3a30981033e5f48202622aa839a8e1986514964d5604a313c49b986163984c0 b8dd74dbdfb6be4f35f2c2f10718c522114f8f62354db5b53e3374d47a3ed14b 700741918a1f2643c6cc858d2cf6f8ca3b4bc98d34ece3cffeef4fc3968d54be 8aeeafb9bd74b45fe956ed0a36c19ec44beedacf415332f1c3e42513850cf471 4a65db8ee06c776515f1c39d04495123c3548eecb8b8df5045ba78a99f9ec6d7 30b4802bf990befee9ad116e2151d309f338d846d371fea0e45043849aad2d02 b25691b18de0b9808955e7e03b65a041922dd3a2decc24f063851f5d9159d67e d12f5f669f26c42fb663a7c81c66eda8a5f94edf0e60739a94bba662a598a987 7380c8e7c9c564a532266b3b055c65112c8400a72ab2afd3e2fe423480e42f12 0faf7e93da40445bae8198d11a7fcd520963174d7b17c140716478aad5a1e59b 4ad41020097e87c572dce4a466791ed5ddea4d56bc686947cffa4681bef559c0 90485844a0151a8ce9980bec28cc211b95bc67d65a4708635d16a1e4815b6abe b1d94e9831eb16709ad13342c1547e9cc8ca8b88c360f56fb955b7653e6a1cf8 d697a9eedff220801b794f905559aa37cd6d73bfc47780a7e16cd59a59d18296 fefcf01ed9219373d9e3c8b1a38cabeb96c74de1f44e21ddf6e0eb5ae94efc4a 2d4d65b4ef8f3c1930647a82b88c98d92038ec98636f1e662a8b89ba41f1d3e4 d7b21123a0769896df394f1c1d02a32733016b7b9be1108f8a97d42859da844c d4df4e8452b05f82b1c47ce167e539f436cde188e8872c32ebdabe158e825860 0a9631a9eb563a0b781a6e97806f6bc3345b20b7a308748c79b6086037788ea2 38170d87bac52016724d7b89604b7c14d76512506988c26d8af72db7c57f6746 542a66e14eac1172cae370da781f6b6c6954defa9ef4a445996d95b7f13098d0 1f2fe8a6214e2925bf1c4a4ca2241398f53f4d9a0bb5c9c9107c7115fc282496 e027cbb4fb5aa605e9d00ffc64896234bf8324d35a780e1bcfa1e62fbb7ce993 6645851394eaf383d174ee3c22943a54bfcefbbfccb4a112d576d68f117df52d 42f3f1c07c54fd8159f15f387b0a148b968dee334751ed49dc7a7488bd183c7d 7feab6d704c83dfc46bcfd60b467c91644e8675e32dae3271a9e9acb04636746 90be3df7b23b87f8ac31437ecf38dedf1be95b2c0bd9b7cc971efd842789b3e4 c7bd5104b1a4579df0b54f95478c943ad12f6936966b03bd2cd0ba3394f868df 8c8a51c03583581c5e9e0657c5e602b93762855309e4153e78b247f94a84fc72 57d8677125ae1e74661970394773eb0e3b31d59d6bd08cbee1a3dfda6470a8ad 209c38668a159a92bdc0b64346bf5fadbbb10a9d45a199b214da4cd1c64546a3 9a1d3b91fb5fa76de826e7093b69a6a4ceff9945bbe3b8932ab73fb63d8877bb c6106c0886e86ce2a81e8e430584db70635fd8e0a2ed1c12ef7165c895eed4064638e78f11e3d20d035b160cbd9e722d0e8ad144a6e1be1d8e1a29e18ebc6f0a44b8dc17da23107b42b1f270f62ef1f87d4d49a5794ed675673b8696968a5f009d2d2a95b9106200a906f18b8263caffa8694fd9f5da2ca297a8b60fefd4fe06293472c5cf9a1f73825e0ceef5495f1190124e5574dcf1b0c5b22aff5271560d7e9f1fba2d1fafa013620fec211606552517153b248b7da2b672b201e701230cff91da83a7f6ada94a3fdcf90af02f6586e65a1f2506a43bfaee1abd2db3960291540ac357953cfb42f3aa99c186f88b2c52b9ad4a779e89c842b73c8d5de50ac2e78083825f0462756ecdf566f94b43a1a5119733a38e29f69ad483b075060aeb01b88fbd973e25b51e00a7264e8853fe9805f7a258be15c6c03eadd96e14061ae597a87d7e1932cc6da1247eb913322f3ec26fa951c511f98c9b27cbd30b09eb45f4ede9f4c3f8c4f00794d6f703349e61de30bb5795bfe2a4d659965ad00c6167be8ac78638c1f1032d07a78bce26c48b35e12c883050f3cf32bb09a9620bc88f636d176db18761090d2cbef73c5ba46c7c49ca35103f55debed04bdbcb0b65d8b0dbd3f02bd81b32a8f36e75cfdb63f1bd416eecba6f811fb807936d8706b244e1bcbc393350555fb65ea50633ac8b465056a1303729abf00a98246ea00b0ee40b7c8a5a0578f6a89ce8fc358737c2150f498089b716e4ca836c5189c30b57a4cf5fbe5221bcce4ad1f19b69636b61f2c3afd1d8e1bcd4f579003cc59b053b2bc84757eb5b0841f590a840e6b65e75bfe81d4e51ed484c14472ca53a7a0897dce995cc545e0f3c1bd0747a3ff64ecd1979a79b7aae15450ba0d9739eb10aff5f1881ac3e58edc5e91a93fd77c4fc297383e85fd969582c15001884dc6d048d8770dd89d73f0c61d53fd4a108aa8a99ed436810267f06a6d7506d84279207afa03d478cde06fdf9fa90808e6ea1631977fcf67abfc9b5eb053a42a605a905bb91fdc289b6e8bcbca0c55088f46b05d610b1aa4c4c642ea80b980dfd164c09fef30beef4959165aa7eab162fc43ceb6afb3d447a292e0eb8943af7959934093d8949f7224833b61c1715c7979a741f51c3e376d92f4de2664ed50038f65d09c7482478b991869231b2a4fdd08bae1fdeb457f2a2dd8ccfbdee4965b95ec600955d46cbadd2ec8e7a9ef712cced279e6602c9fb49e252a90c6bc9dbc3c2350ea47bfe5950bc6e67dbafd8a0a4172cf708f09ad699e563ad9e3b379a32c0a707c1d15d1695a08aadf51c5024ff314c1ed51f31df27343a0b58c98c6073c68c000b3ec8ef4c54472740ad8ba5fed9b92c58956d37384fcbac28c1feb0a1e2f305477b5f8f79ff33b630e353aea35a41d34f30a8ece05c44ba88d60abe6cb7cd0acd76d3dc7a8838d20821636289e1cfeb7281870948658bb99438a2d9349bea0123c5c3a27501fbe72b15f53ce334ae92d16362a044ae42cc714d1a8d8d1abf052e028f63796003fc13fb229eee3026aa11d598b51e32b16a0a7944605e2d2c0b2f82ac0f90b23d0b9acc962e145389a126caae01371c0c3be93ff8cc3cb183067068f8b8b2787a5103d4457a3e8153b9352fdca9fbddd8540f5df02849ef320d658c490c838859191e65628184a6c4d4371202f6c9d0caf96cf16aaf7b393a0faa6319664032ebcb14bc79e8be6f63db5f36dec5603759bd393bdfca3ec671095f19133c6bb4604b075461c971981461b04663b90fbc1d5a076737b79196fd06ba5c768448f579986723d4078400220ba35e6b5a5abe5140246b4c9cd412c80ee770696662ed962de794dff071e8fb4371eef8ee07fbf1b0a573e85697b8a50f06da7b4aaa8177c0a962910aefeeb076d176963221ee501fa6dd2b74ad567206008915daf2f4eb461b368a781f6b146330381b9bb038ab970d17751ad791c90aa16d990ccafdb6b2aa3aafa2d8f2a66f433fccb00d2f12cce09cf8b3ff395807c154bcca6362872eece5801999d442e9596cd1300ef7457bb9e622a5f6d8ad06f8346e566f27581b22e11d3bcfc21f80d2f0cebf1b5fba0b64856a8ea1533807524e9bbd5aae2731721409af11d0ed34b5229cb9a70944a5d207916d629f2307d6aa52b8b07bdb100190403ebb234976c08ab8d359177eb7ac605dd6e5c7970f66240107b3c366cc042fd73ed0a68b20213d9062f3b812f6ec446b723e4ef30202b9e671bf168454d4dff00367acde4d07b943966c5cde9a7e293629dbdc5d0f00b73f080e694e4bfe4e788650d67d6c79a452d3124d0e15ff262ad7fff78b0d8e6a8a3dc1fec82db71f1b16d89f37664f61f8e8336455313738adbbe519f80a7637ac525897d09b56d9a87bb3cdb1f380ff5241caae9cca547ddaf95b4e6d0183897194f381dbc13c84f3ab62a61aadc240cf2e5b336360f749c03c9419880ff9486692b5ce3e5f86d390710933e8f09aa152b1fe83e97b7531f6bb4ef39a0bdcf55292e90dd027229c2c246bedf96b3f5a7dbd147e7d7af24f387441fbed02aa63d90d75c019721585c694d5758a7f7067e12164a96cbab2bc63bda1a62a07346682e6921e37d4105896cc87d4d14d77994f778e0b4f968a090267aeeefc078efb64e42bfb81ef517e247c75fd5d046a744be14d8eb66511bb99cbfdad2804d1d51bdb2fdc29f4adb54992b1658fc92b004843674f79bbec8aac0774bf98017487819ccab235bfd57da45e9891dc69e8e2f91dcbb205d36c62735f23fe900015a6d86b434838664add7c4b0a042728cb2de88db100881d3405a7fb1cdb750e481900a4f832b6612d79507190b00e065ec9d67ad27ec2fea6ec3529a1eedd0f1cc613ad0c9ada3b2e16f8b9264df1c89827a6b364f9313d65a181032a859f0ce36b2ae5d36728fd19497d80d6125b978680f3d1cfae60f82e2426bb706b670cc819f496fb709523fddbfbdf650ff59cc369cff7ec5111a209e36952a61cc10a4558bd640d78089334da9be95fe396785b75acb9a807f94c2ec69d3a51b17405cc74bc34c33b5f594807994bf31b8f749de86d1665ae53f1000d6fdb47303d0a3bfbfb9e7fa9e45d53bd23351576293e643d310cc8a60ef5ce2487ce2aaa0108673dedb94697beee244107000e79299b584c383702ba2e94ee9922fe4875a40ee2cd57fffabf022d04e9bdf0b7cdaa921669ee604da364e09e6e901df256860875e294bec20b566a6ac1e93a19c0d3c24b7b756215c896fd7c6f193d39ab2609f886b31c925a618a1f99fb21ca299d2613f66bb02a8e57fa96894719731fdb02b0bfe25afd7b0da413919351c47cba147a9ee25d2afd2e791c648e7756136c0a8bebaf05fe64d60c08f0138369f1798cd250820a1b538f5b8713d983c1570b001944437cfb7d6b2261b27a1c91730992946e0e9710f84bf1f7d1e6fd53ee5a09908ccb6a14b8e305c424d3b2418f62c4d8180125f14823089bb043ea11ea3d054fe8d42bb2048d5358f4ff986165a273551c1899b28962db2bc307341dfdde0915712e22a988c3317e2757c43d8db7a48c147393eb502fc680bd4d34692c340a0723ea964cebfbfeda554931bc1bcef407196f6649d517e0c21fc2ba07de80011c5bdfcc1ba9a2792ffec8ea3080a4c945892feb808e066194a4d44e9efb270b08b0bd3efedd549dfc43a2ba5f95a7d03c7af4e802947560e35eaafdd706070c47e1a16d02021d947f568784b2dd45241ae77e8babb9a969e60845ff92d4980430a174757387541708446aabe9449e8f39ac5d27bc1b2a07a6a20158767643022ae657bbd5ae2c72b209d83c5e90381e18459c159277b2f28e22bffa7fc22f06c05eb8206b8fdd09c00d1f8215b2a92e0236299ce74e259b37b5cb9d7c8edc006593e71e8c13fde9c02d95b3c169de05d6dd6186858d67ad9919a34b70023402abc706f07f19f55c2cd07a8fd2029517f2b439ed34b7a6541d6dbd87d4a6ec078a81c1fa60ab6f0c38eccb8ef262069cd4baf9f0423c5ca0a2f074b51e38ae071bfeec91ffc8b15413dc1cc532bd743e2a7aea3096c185719ce6494afa20a10ffaf531b177a47a6c21002edb78dd10ee3392ac0627be25612c68f5c82cd179088cc496f577091c75df83882712f00cbadcbdb9dd9504d214fd3184e9736b5804ee069e8e6f31fa5f1280e39a002388b74491f05007840a865260a4ed8b70b704a9c0b9fdbda3591f764e0beb2e33ef1b89fd15246c8e5d52347a68bb248976095db2e5c5905634b0c02a9b4191ba29288f0df863f3ae1724a1e178fd77c3ac0847647e42cf3710327331499c4ee684e1281f7c296bfb1ac61cc2e5dda8c5000ec40b581997c67e850063b713844d66764a2183050907727d9f921cbba4b5810ff5ce49c2798cf7f6091311da3a00ec25412253d7cf713ffe624ab1e85c6c550630384a595b9b65a4a97b2db843ad7bbe2c1d6f8e4fae862fd48d2d6db63da60b5ee808cb3eb58807a533e5d927e8bf848c213c19f9f8eb2f36877b9171481905fbf66dcff8bf7d69b1f7e6bd2b929830527ad637fe4410fa511747498f26da0276ad18efb3c3c539912dfbc8f9987e78f8a65e2be4d99a7f1f55132dd0bf2f04015b4677dc975d2bd0061bcf36991f99fce33dbafc7fdea09e097e394c03630e9bc1fe05fd9ffffa11e79d9ab442dee4c80812fa51e6cf3ba5f630648399a90b563ba301586e12a50d4aae534075487a8266c067c16ef8541972d4503b9b2e0a59814fa9b4ed4632f80b7329fec6c0a1c57efbc3af726081d881670e38e29b0d77f3e3df47498c1fe09339b93a86addc32ef6e6c6464dad41200f8d3c64cb801e24de7508925e0399c604e1f306761062e059a80cd85b341b49a53a855bed4070b2a12c8b3960a89bde4c47e7765031a7f70122fbfdf96784c538a2f898f44004697a7f0d65c768c0b2ba52a2db015ec4acbdd9efd604deeb3503f2f704d7a0cce46de7f5e91fbfefb51df1392cdce7aeb5ad5068c4f632d567d8ffb23b2df0a551f823f66421863cd6d8cf2c465000f7f30776f5f4b96b1a6cb602b562ee4038bcc69393e303352c1675269c9214caa6f988a760e39084a80bf935a0b85da0c241ea8645d37d494fce247b330cd4bb50b6e2664e8d0e7670c0629a5c229a103a0bb30f0741614a6fda3b0da7a5ec6a66e71e634423af6206061042061c56d0685cfc59016e1302fdacd474e5a7b6bda88bce16d5607d8a3bf8fe9798df4cd041ec8dddc14fac89204949c2a5629228273bc38359d65e0affa1757304856b0048bad2aef375081c883ba0b27e4ed0c948bc719d201eedac51823865e0d205a03f7412ddaac8833d244229c7ea29c36c776a6b73f7585f70d780dcb8f8515c50a10cef17c6086f785f9062f7a262e196fdcf466bce71efd8b35c6ae4328f2db074eaec9365927c246a180a8e103c9d8f1305ac202e5bc19e065296b683194d804f3313496f6be7f2023c23a51a5f736262f23a1fa59042811bf6524411d476c05bad4e2f40ea92c14a6f889531911381fcbf93d8466f38916525b8e1e00aa4c0ef6841333cf6a3da074f989a07e3bec4923ec9488320637cb4b6948921bac580249aca2ae0ecdf7f2c4361abda7ee9e9a2b6743d48ee6abf88f5c83f8de538a0ade57ac087871bd02889dab0f8790b1f2aacd23ae65746217de1b30187a4cc503302858d5f6eacc70207928720463798788d0d90f8731ffa425dfa825f003ee065fbd3e5fbc7bcecb30a7766676104c91964c17d1e67534b8fdd8c15bcd3ae30f3061cbafbe59086e085156fba97ff26c6506126335c659f1d0b6a4f4f0cd370f2a17568d9e92aeccfa8948e61098ed540be5545a48c0f74211ed8b61b16c8f02f414077cc47cf902fccec537dcd0b85368be95f6134e1d45c6e770b68af3ee019089d32ce33d68965a0486dcf76249c52173b579e2d3b55b956df10041d6260eb4164867dc0e70b51f9ae4498b3676fc285a3d6e2d76b526349543711b96ca0dcd535be1d37c829991464336c0d288b58ca554194b5a7f2ad8c90878a8c85704be4dd21afac44967cf4c5b07305f51557a9238bd08a581d2a92448c9fefad80deb1ebe964efa041821b72d40423043a7ff71e72b36faa9492a150785977161044f6441ff2e64ddf885f461fa08fa7827dfcb6f87a01aa9140994ac6b914c9a0b2ffefb87178fe5615ca699b732cbfc922c4e498571fb6c7c738cf78f1dfa77094bd3531e50200d88d9f0574272fa212780d60b49edf469f00164ae309e031c07ff0e90031b8e6654aa4545c4b43fdb8e652b726300ec75748aab28d1ca0a060107b12e6f74a807a65a06685dd5beb0ecc642da2d1de218e3a855b3016e6c7f0411e982904da1d06e25d5a294425fde8fa3862c6738914f4bac5d21bc0521330a5ae9c5ef7f4ed8b29748ab07da233d535476869a369901c88675fc6677f8670155a6a571f159ebdfbc4ee0c55840266d42a127e42b825a660814c508bbe28704cb5cd97bec233a1564f72232ff5e2cfa9b452401e33ba0ce83304ce0482cfc00e5ff118b010a1541d133bf37d4495fcac0cfbbdcf7f033cfff2b37523b8ddf0dafd79049abe59281856b3622eefd2ed8167134a6edfabd4e88e738bfe5f3c80b3b1783f477d3cfb98581a33276e2f21d60cbb13671e4322af0f10fd9b988580d7c84d8abd5e546d5f419132f4bed9da12555eddaba01d426e3f17b1727f6bc0351aa8a2c56a5e34dce589325e5750a7010bc96b44e384df574fbfbaba4734409e7ec65cad54e4cd9a9eb1687d7ae16cacdab37d00ef72568107493122c81cb0cf1f4fe0cd3b532592607d6bb1ef1f26f1e37da34065e1e29f02a74cd09efcf0e7db2859e33dee759cc8ff10fd12e26c242e13f559c4a0d6fc176e2386d95cc0c68530af752ab828ee046be52c5b470ce9a52b504943de8041f3daa6d0e9b2006ae70f3c357e0428431f5414e70c70f20fb0e17228f075a30fe5f1e9496e1800eefece545ec8acba4f37d5505302f21eb1b475843e1e9e04fcd10f0a80ebc1c0983a4469ae24e83768643ef50b0b48c3f3de65c5573ec9c0c212991da80195700c54448fcf246de1d533792548641114fdfcb8c4d61a4d877be9abb31056ba40bd90164e603fee57ee97137b91d42e998bade89494abfc1f566f5aac21b24a20518db7834d8605cb800c6eb584c0f2f30ecf1858ee6c1d81ec37e1768b50c2f0bee8f9107a80cbd8fa16bd6d98d45ccb874c09b1d87ef763ee0e2e5301940eb0114d73a3a80e7611b05149ed93f24f2c56bae90e0fffa632cba1afc9aef6a1505aa9295ea54272b4c8c5e5ea179a6a86631e6f4a084d6352277270d48bace280b9f8c7d7bd876a697f5ad487f7969590ad7103e7eec5c2931caf2fc7cb2af50005ce2100ad8fabb5758877e6a96d080854c0442bb46ecc4d475cd3e0b664b4104cb75589302b126a857712d91ddb16ef94bc87f8db6e27180edeb7d07092bcb0377674b471ffa077cc110b2197e62fc6e16c84737216318f6b0a8f5c8a8eebc0f086c69f76f4681dc270e34382518606d7b34fb3cc747ebfd08f786a9098b8400436532b87483d89215084246c250efcd0e401a13946927ed31be4cc6f514bf0cd6a0c167b5c030dbbfc9e3b1abf79cb07b8f3ce17e898a63c3f639a72eea9c0723d72e5297815bd0bc86b6abae6f9f73aa30267fddbfb60d3f0caefa5f63e30784fa146d182d8228bf61247ad8b319371312822f551405d5e2ff99a886738c0216a28f8adb551a564497a5a7c919503e2734032597f2258693d824596928b40c44f7e4ffa7a72e6f92e8fe0e5d93adbe5e1ffb5f7d035cfbd747e4dc02672606660eea8572837283ee25b0f3f85e058a69b13469edb7e149cfdb6b720fc02404c752d30103d55395a4b28786a821eb9aad120b3e608b5986a10d8fb16d41030cf57560381e7d08fc8ab608d2e6c35645914724d09a1029db64e610ac9852900275863a77efbfcdff8eb5bf007cb1696193d5322dfd071adb0027b67e4a4e810de67796709e6567f9dfe7771b2590f146de551cd4d2f9dfad1d919ef7d59b9408dd705c67175a5d5e0cab690ad3644c0a61d7ba0fbffe859a45ac634b1f1bd00188ea36f98d21ca4b4cc02b21d582c3de3031ab4792d602b0cb34dbbbd33c1204f27595d1d02f2bffd8866a0c0f9509b098b77adb360ea111ceb57e8d2c8b2b059b85200f96bd7a0f98888938ba615c960f5f03daa41a77a7dda3a721fa62100fc1353f63cbeb7bb489c8131e6687990f578aa9d98c68c3d18879149290dd6e09f8e17ccff87a62ac6b720f87c2a5b7b3f42046ac5f2d7874069303d5e4340704151151aa06f31e3939789bffd40242d4e245af566be853e9a36cd47b95da630535f506aa74ce3c71fb867d1f3386206d93594f102a4cc408b09aa6f18d634800ea4e863524f6623dc0f06272dc22830e7f30700ad03d283f257b0da242958b08fa1124dbf13f06f39afc615b9008b96859efe3f0fdaa6b64fe1b76c3b19f76034d63bd84f9028a42d6675d6cdda31f9bbd70c07bf433ee5cb14758d6cefca005e28432f4bed97192d9dd5c628c4d6c2080eeea8e75f2727e0af543cfb8af000d7ac02aeffb25666db9d64400c523946ee1100325300243f00453eac9bfe1150be8a74d7ef84eb594d4a9b31e162c2fa109bd50f6f86493ddef380e6707e3e30bbd1b966db5c6426411f4895dbd59143340d7a56f65187832637b2db28b1a4303a4d534e765e19852b51c2d637a1a23c364f2850dfd1da1a19801da3248a3e6029fa26d4e91c79c369dd4635cb97fe9772e0c2fd250851c478f4e1028f05b150e486e17a2df6cfb646138e221449dc88a8765ef48897cdc782c983bc1dac682009e4d766cde93cd2d8f3aef8e2c125a24b3509825fce9707f9e4b3b9cc84bce077d2e86655889fd1648c8ac81cd8e6200b63b6a2653e0cfb31ae809ffd347c3011e6b8703420afd181180db25eb0257e2780417e8c874655ba93c287eb09f4201b2f9b13add485a9bc9eead50cfd0c706195f9a0674c3e767e23e0bcd19237100951fe05b1f6a9b26d4ee6a471c144c0a90d48aaf5c11061c9f7c3f49fefb230b8eacbc9c938f46424b6288788801282c2f3d729f7eb6b4a070d394ec0411de0281101552a73242243b52d484c06713d3415c331ad86f967e8bc8340d920bb2097a7f1b91703feeee3aa72d2dbde33a8b3df14dbd6ae9a4869d69edc8c3a43e08d847163e9d5e9633ff91e82a9dcc9b1299f92a9d9641701a67982022a153430b93a6d22a399f205845a08832155d69e971c756f21f961bf77876fbf0806f290e4ffe8b86066a1d63f255523eca6a71ceb4d511b572fdaba6282d44e59e60e208145c8544bde0f9b21ac7141f5714aed5103afd6a478e08c14ae053a84bfc1b0e false -check_ring_signature bcc6a76d2916ab3315526c96a64a9ac8cb0530adc3551de67bafdc2bff0b66f5 0890dd526014601990d0d127ec0c2544252f7fa083678ee975f2818d8aaa6e2e 1 e78e7a04922ba985e7f1cb410d7552e8d84c65c01bd2337fb96df8df78dcd4a5 0e585dcc79e0c02a9d0f78d9ec1a5d93ae1b1491ff0ed48c68095d63ef8d6d0e7b399a8d7f56479d372d50b75861c7201010e683f9f94cb131268cc978b64ab4 false -check_ring_signature 880a0385cbf56e2495782255bf488315c853d57fb28430e64d3d2f2153697a3b 919227b81c61ce800ef7396a0b2442d10e852d319736214cca5d2adedb033e39 71 0c22efb1d1b8740395489144f123f1c64c3548c025f9dc8d2bdfe7013aa375df b0e357a1bf800fc1a6c573a6d22d1c109f8755bf1f698237e45d1b27f012e66a 8ae4d4781caf3fa7e0b4df6a938efceb0f2fd31f20b0a69cf498d03d2036f075 364114f9a2cec5d450275f492c477dc6718ec4cebc181f8663c5c6e6f02e36d9 8df004bac0e02f28d29cce0097fd1cf8601b9dd6132003dd3f682b944f031300 f7a13b21fb4de8493eb8f99c3b7f789b9bdb4cae1ede2c2b9b7fbb7e6c5a735d c6f9c20f61b774251aebfac477103d1dc755ca227cca3c97f615c5af6a252a1f cea968752c547f754fbcefec02ea2bf4e070a1449208edfac3cb4dd6e135db57 798344f16a9e3ea0272abd725d5ba90329775b49408fa42aae0cad58a83028db a87398fe7f05e3bdc2a4543f5936eefbbfb139104a80587647b86ca4908e7011 84638168da24979d3957360c21cc385ef3dcf3451bba0d5dccc20ee49b5712df 21ea931e7a0137c756e5917aa1b5f79fc2a25dce9b727156d9dfaa32fec8fc33 cab7059c8044af18e02522d0a89c4b12a9020e75fd5c696e1035ccb059e212ba f1b0991dc95b28041ce21a53408e17441fb784dfd3aaacba3abb26fb94fb18f1 263da60f3537687092fee79b24f1ad8ebea7fa89ed11ae69d123252eefc150e3 0177a768ab758c096dffb4ce004a28b99cc936cfbc8137a9dba3a3f00d79de06 0dbb1d8e341c934aa7f11ff9a697a7b8e36da219c3f0d56352c61439d753798b 19e6a4389d239b5634913cab6e2fb7d98cefb7fd67d4f93b465a25a6fc6d7e0d 376b76cd0138e1f04e0bc19177711cc8e26028d581e65a24cbdd83e7b4f786fe 2903f3b0a1b41c4a032b23c274d0d3f729d32749e0c681054b27a5c33ee76db8 0474d925e8f1bfb35fc7d237e57da4f2ad79b4e2d3659ac31e7b7236d11787f9 f2c32f01066fd6a9b5beb2959cfe3c05d65129fa2aea806d1a155ea61b287240 2040f67805840c3f77c6c27c53f086fd9be30952f9f995c4b2f58e8d8142249b 21c08d4de7b03da1c463f7da5b7f75ee764ccf5916b0695c4c9bc507d875a933 f9849864736ae3f73b8729f7bccf7c5bfbe59493c99dd7fd1f649919d4391042 3a9aaf0345ba81de65e1cc3041057fa50183a89d14c63732adf9c57eeb5d6496 1dd3725812d3836529ce040438706d0579a305c80cb8aeb02e21e1b97cf52367 a86cde4018879fa9f783bd0ce71f64aa3f7cb26322a98c8f490bd6dd590f8b8c 3952ec18a283f76d112a604820abf613e8336fd5eb91b0683eb375a563c131d7 eeca8c8deeb5207d4ac2f397ab3eca02aa34b870c1f3b2b6a1cc2d8b5dc89edd 0eedf3aea14dde4d71a81769b38c64420e98aa6e126368ef810daf0853096a89 24d11b11214c45bedb28a9f028adc9c6e04eedfadf18f45e7521096008cbbf9f a1fb82ed921952afc48f9d71fb93a6de941512cf37a4c76ab1dba9d8c246499a 0aee0524cee2205939859a79789b07dca7d0879b75bacdff73521dac832cb22e 53219f44570a6f8bbcdc291caedb82653bb8169f12df4447b59cfc2aea5e8f53 8d4a9c706f03c4390d371a88b395a96cd0b5af0493f18c16732179f475d46012 eae208f1cd48f1f5053e76fc549693adaae5b6d1301dcd629312836aac3de380 51abb05970900bea1a0ca877e22edafc9c9424cc3207855e2ce7d23d476833af 2b975533963223e4ed9e08b36a6b97c54620767d926c292c27ba64c0bc7286f6 6d97ca58c40736e3babd17ba48e0d2d586210151c8597ec3f0b7cd567e6df772 6dbca4bd1b5313a296b752f930fe4dee89aa8693ef6e167b317c8e9cfe081d9c 3b7f3d0f1090831c70b23c7b7320924c9448506984454b14a0e1d38c16b78645 4d48a5f1a0ba6663fe1792f6ddc04b3e3bd8826ec84ea2602c379bd2505adaf7 94351c71aadecbfc8bd3264fa751d45e0a89c79a05e501edb682c398dcc46103 094d5357845cddc0f18ddbf942e2c13061555484daf54caddef77af973cbe9cf 1a23da7d38589ad34c2f75db0b63618473f575d40e5fbe85eb6cbf8c8e4b35e4 50783502f05a926f1f3f0184c640815860c8951c658ac88048428a20bd356a4a 4069963f63f54ff6138ebe69f32b2a5ca5feceae858fc304d5c7ff67d885a292 dfff8f7c751a6363a59681921943fd1c855effc1e535bf7e1e4a9de35ab3f17b 7f3a0d231e4ede5f7050c4a412c9f15b950a5df82608a8cd0ec6aa404c9ebfea 64484f8002ae40c34956ce004b51b56adc8a10ddc15c7a29c125875dbc08e154 f1400142edb98912b16a68d00912e3d45ba6cd55ba4be0e468f8247c6f9798ca 382dc32f08d48cd45c27ffe20a915987fda2b6c3c05513d1bb10e5d52cab02f7 fda7cbeec1ec5f04860f26184b0e28453299c34c31c47a7bcc46ed49cfd6b4b5 deaf204732f45af977e039a42bacd559cfe3fcfb82f2457096ef0bc0bc9beea1 a455d12d490ad95d4e1aff5e9cd49b2a262b94700d5911f92b077c3792f46965 d6f9241309735afd5a1715d90834c7993fc0289c582605ce358e912d4920c563 d33a9d3fc09bd42c5f5ddaf262729478d4059bb26ec3b6d27ac6cb7b51dbe2cc e4794fcd8f625c2c1cbe997c53ceff58eddcbb0cab9d22e13e1de14be5ec05ee 85487fa25adf5208d0c7fdb42fbf113f14f8eba49015bde1f40fb81027923077 b2cc455f9e98383bbf0552bec3f68e34d08361b32185a41182517ddbf070d578 44d83a922a7aeea7ee0fda57e096b1d7d4e7b04df3b78d5df23c980b69b16097 1cf270ca8feca53814a80e1a74a16e3939f5056f725073f934064d5cdc16ab06 34651135fa77537850b50710e5c70c5a6e5624979985323af08f29de3a81b70d c67c45ed0e85db84c96492f0a886ec404afe02996282f50127bf44c8f784821f 3a8c7f55a76908aaec211c47e8e0e2f21b5012c61b7e9b30ba2b75c868e1bb6b b24540c726caa5e3d95e2dfc7a28181f98b383bfaa4d128c66d61de0b6b0e5a5 fa27e4403009fc9f01ceb8b7676739be5a2159367d6fb041fb749a6f7680d649 45933436f363b13052edf120f91fe4bfa56396e62a5b42d4f8284b0a72f9e9da 82fbbccf2d05b9a051c31998f887e0fb95645630948a27970afae4c7356a8850 417528c6cd059e75f0a9d25f45fe9ff2abe1fbce922985e2a4339a2536c0fdca 915cb9341690002a465b70e28e35aa106b304d7a45f4b3ca898ac3fc6a12d6094c24005259d6c91bd7df21eca6171a88a2b3b1da8c5c902b03b66649de52eb0234247752ff6c441afde355075f9c53a0e25a395ff2b0acdceb160048a43a1500a07b3d36678fc4838c558d124983e9b61ac89006c2408ab39785534a5eb091046cef2df6f28e674a6642a26f4da2bc0f2411a7b5c8235173fc5a04b9ab7ec90012007296ffd23adb3dc9b5ef2f346f416750c80f8e171d37a03fe4770a60a70b3ce29f8434a9b0badeb3d0cbf8301b46c2e16d12ac79165344f78e94093cff01d11d150867e3453f5951dcd22685f7281a55dd1f77878d9ba3f403c9b7180902cd103991a56d3bc88a2cc6e355bf384ae1a7db072eb5c2f8e8f2bdec6286150aff2e72a3cd7889bf5e01eb80cc37ad516f9b5ce9e67b2c171cc5ffb6756a5003d63f608d23a29b981ebe66adafc45818ccd1bc139da28fe87e1510f11850980b8c09e130ad59a3d8d01de219c0a8591c15747e0a7df26df5a873ba3f46fc2005b02d69353330e80e58886818789209a95bd1bc93f421fc86dc64f77307409306811bc0f28b0ec4f2a15885e4135e95f012e4410387ee7213ebd6635dc38d83013a6603900df17526fadc4d91838f6d4ad2b0b034d99d437f0bd89e3527b1b00ad4388ec49be6d59c686c97ec979818fea3068709ff9344f1c6054bd61b95c60edbf4c950117956c7c65770850a4379283f931059beb13d24aa1d887347eec30d0d29dfece47953ca9f09a545aaea1665fd0645062fca2a7aa7d7f584d8db9501332c842124c6346cd7acfef62669f6b44ecd3bc94df8406115b52907e6adb808c6aaa9e7e80518e7db7e27c7b2458cb999b4029b4ba29da04493223118da38069169453543ba12aeea22c150ec9e75b18089f610966febc3e4f441bf580f620e36e1cdf1c4218430c34f2912ed352ccf1c28add4f2d70604e2ed081013cbb30080fcee99403763f001fe9547128314cd35dfd14bdb9fa03ff8d02567a3c5620be06b414dab16c1130b3db6b3b7d74b1ad67de2828ff4226ab24eb73f928c610160785d2f48dcc60bfc661844ebea6ba1a998ff290836e0396d9e1c3aad530e0c11a24d28df334305383a6f5fae553eb8c68502fde2953194531e0d8501dfd00ee64963308d653a0847486e49e81481bea297c79e03e6baa26d2bbcbbaf52b8031bae6e38d7728fd3e33cd5e2663980a2872d34a5049ea430be9bab4763b4f20d620a694ef80da945eefdfa24c89f411ebf40914d4a904b1ec7878310eced410d7f5aedb7c8470b417472859bdad4bf742e2a8525fb7e1b84d9ed46b38b7f15000f055d562f78329d66ec8d3b83db069a47e6481760c58ddfe7a95817448fca07b1f8043857e457c3a06c5b4ddcb09d4d9df17e87e87f8b333e93e5a88ac7e80ef4cb3914e9739e4d0ed22177f1706e3dcda5ec264bd2e5dda99b1857661bc505c958e8cc36d76dbe04198eb129490e493970cc9be27753d7eba49c42ab0b660ae2f5c2f9635a17002c1740d990cc4d5cf3adde7bf9284f14c5d16b4db1b6570de945c53af90f6c80d75ec2dd209a9dd07bc0e0879b5abede6c827e21ca084a0b826388a89926722a0074f36738229761b0ee6eeb206eae0ecd6727ff30ac42056d55636e19026891c96e95b639c963b9ae29613678d6fd48746b15498395c10031edfe7ca353c540d17ba05078e4d81ce663f76a87e41280c48b193447efb00f833eeaa2a254e10a5c2b84bd845ce918dedcb71f618aeaa4261c815f13bab107e56712ff48117ed1f141b38914873df0e1289eae47b31162a67c7f34b3252d018a33dc8f72c49f00bf964de4b1967fae9e1c12e65a21de4d05e68b019bbdbd0e0d098b3ff956e54762805255629f66bedd9cf31831dad98e8d52ac184d4d860bed572b37a5116b335457bfd3902838c90235776e2e53fa6605d2691ae93aae0e62041b2231f9c5616a1a4f86f7aa2f7b8b98e68802de6be403072779a7b8280aa9f37cea834c91ade4c43ea95b624895c4d1cc1dfc9d7d6d4e0fc74f3dc6030899cf7dce29c0ade333cab82fb97d0a59885fd4222afa1d1ae0d7e11fb6df2e044809736c55b567d9375740f40e602168a393b39cf60ff37a543614bc8fa57a8f00e244250ddca9d901ad63453127b08e154adcacd722986009b9fe3521497d007352f48d771694afb3c731bf6d0400505dd817ca1c60739c32342b3ab35fee060780eb4046f558c019482cdde9eb10be1380a11332aadda295985d36cc3097040b358edbc36fcddd0e03b2b653fa1099d6f7c44557a36c93fd8d8ea1c52d7e0ad7eaaa98f8aea33be80beee79a5aea163618e4ab844e1d671a3981370a984b07b0b84c008c9aa287e57c595284c5c10228f293049bffe3fdd6c197cd33bc89099f216087fa53cbde170a1cf1300877650f7a1f9f75a6dfd441849e140dc42b08cb8d35f821f347b65e28214c36839ad66163ee61ea906a5792e56164fa7d880976107c09025419567f694ae4915b68ca2ef8d1f84144feff867c94d06edf6a073cd280f2cfd261942f86dc62aaf9c56334333d233106dc104d92ee28b9609b0ced1666580f7b1b3aeb429f5207062b85c3d74dcc461497be388b05a5cf3759023879fd3aad389011fb66f0bfad132ff768fa13e495988d4a4de8f5d8e9cca50d6259d2f8131a2a6702b103eb8e20b8b809e4d61d03b0f4ae1f2485339399b40905c2896ab0ce0dbae2c83fdbb65b06f73f44eb2313f89f5f779417162d3bf908740fa8964eb1db49c6a81121182b3a62905af7b852956b5490793e1d04217c0f03c671855ebff8b334e9f751768e6e93a51fe872cf47e82d39d29587375b490277267eb25780f59f37109e38ecc6dfa1aa80fe62594012ceedd2856dc3c94e02ea32f27b9770d65151c541e4dd946959c970bcec391d6c07699e5a11839f080984090370fc92d7a46f42ae18ce546468173b70b9d4c11901b5e030bd88169a06e7cd67e56e8bde5a25ad3d3078e9f2771ecf86c25df44850cc94993a7c32b808b72dbb7211529be87d5c4d7a5d6dd71d8c50eda5ea68ce756be255ffc599980621c1f282d6d1bdc96d74552a5cd4910e3e291524d7b37688afbab86795e70b0106a76528908ac22919d9b3a095c6331bb3c0c9738402375cf2d0af966d0886030a1f37f55ebf0d879b100e6778a16f2af3d06cf9ed1fde2a80a771d5aa043c0dc3781d170c92efdd0d41003c414f300fc8dcd8f3e09b42b24cfd15b88decfa0a7d228184139afb8a24ef0d8a73415b0c444c99a6c3e48db905d9c9509a48a8050434a3f72f3fe72f5423dd8f0368c169f2d2a8f19c3862c80a969a17fdcaeffc57742b30c195482b20cc9c28d3bd029430ab4fcd8a8bfb962fb2ee94c4f60f09b20848ccc302114fa69226031666be2cc357393a15e43230d7265e216f6a5c0b1139d87cb62806c931033c0695ae47cb4b1ff4b4fb950c6aebea4e0c06bf4a01a02c05515a0d029d039bd2462bc4dbe8f83be4f7965a6b57f2a12b0e004a850b2a0d521633555f25e5ccb10eb954bf4f3e01c000bfb93484534642c1a9d8dd0da26bca82e107893eac8548faf7f71ae0ad235173987bb199777f8ca79bb535082ea3ff1c85416d03551d2b4ca03d0b9d7d7fedf194777667f90c229c3bea7d08e500535ceaadcda9c78a6b3a2bb181b60e4143d268f00b967dc0dc6879ca6d01535490b940809061923fae5356a580e6c5da5368304c7d5f7118c23636c1020bd1438d3ef6026dfa53e93b173263ec55d41ec918e71f261b8ceb8ecb875c37099a2f063c40c741393e40f262d29ea96508a4168c95d612b7d7c751151eaff603526fb69e55830ed280a2c617c7b3ec310b28aaf9b4572e6e990d1fc20d7f4f050130bc27e04d19edac3d58eb1c1b05e86c02721b9a353651b710314a432b0c037353bac754473c908655cd519a8703782f1580596158f5981d7ac2c43c174c0a0554a91baf46e600ee94124c2092ad6bfa345043fbf56607d9b8203997e0a00ef09c2d1a20d56077147a27fbe814429cfd1e964555b68dffd393d6c25ce9c50832904897012660f79ef803471e51d9541f5b3c3d4d8462d4e7c869b9c1be7001f192176444d23a070cf45e89d001ab6009fa9c48c8e36cdda053632c03304f04778866a1be1496cc115adb7d2b073f1dd5c0cd5dc1bf457e4899e16bae3b140b48c60c76c1fd30c5ab01dd5a6bcf23cd12886b4f9f21cb46f574bc0a6f5a5008a0a4c7d88e3ef1a53259f874034b9e20217cef535433bf35ca529ce67793a30d76b70ff39a8adab6c16657074f20a34fb737392f1108bffbd671d1291f09890849155e72d265cc787853d73e62abc5a7d502acc3b95c5ca3a45a5eeffc22e4083a275745d4108a5b3a22cedff04f58de8b0afdab14c289b73f4947e535c7280a0d6c682e81f5076b181a04f43befaf1027face12f4cbf6cb5bb73fa16841aa07a1f7439cfb9a5d5928032216b18eb942e28aa09e6045b25e5b747b80d6f4160376be5e34aadc3af93ce09ccd97f870b3ec9f3c29af2fd4280287d0d37d452603b93c8a7793168fe541be72cd9110b8d1122c1e13d5edb68b24794a8924cd0109d93230ef9f6acd2e1b608e7b52dc572baab29e5832a1f9934b8685bedeaf510d7db9a62148a05a94d15e8439cee482aa7dc2f33716702beee71e7df0779c790814d4705f13323c91f110d4fe2c1d966f48306ae0fc928548b47edbb2937f750fc59e67bb353ba69c74a78ab91e4f2c34a7eac20c2706c53bc188e1703347fa036cef6d8c749453702732b95ed384901f3687d99021d04bf9cabda37f5a1f6a068702603338039d02244136a51804753da02938b1be5e37e5e063a59e2e346505e624df88ec66c26986809730ed4d33e4a210328e57748e2f611b049aa2939e091b18f8eb70b21d8f8e7fbb000f925c7c8a524932940ca440c4cd7fef9a624b0fecef75a868f66b7fb6ff208e387e0ba1614aef520d3aff155ee751d6f7e39f085210010bb67f0bfd7763b45b35f79457e8c355334e2cbdaf383deb8249b7c50afe22f757928f75f6ad8f5c5e0187ccbed3f1e94852593fd137f84af5877baf0ca80e392a2154f326938c8a801d7b9fff8c17596450e07cc437e315e2e3159007941ae7351edf57036c7a9c82d8636f5a21f8f4ff11e7b1d63fdb6069b625fb03dd624beb4e4e0fc2682ae9fb0e594fa3263b6423755a9907104c752cc3995b0e957c20dfa39c7b5d5234687ad3827ea5d9204799a54ea194697bb2593ad57d0fffcfb8f1b82eb6b57b1bac9e159e463bc0c3282037e01af85a0d3b123fd7a00e748e19442f968ed85403b75f6f463cf804e0c4c0272f7676357ddc76dd306d0c3f3a01e9d02d5389483d2fbce7f2935600ce58398ee26f145972f467b7f24a02ada70cc4fc41e1be4f51165fcee0747fb4b233a33b4755a670a2186633fe0100ba7c77a67faf037acc9cb93283fc6dc938525d5f2b1cd42867abceb30371910203ef1cc13e44052aab42903dbcc5125ddf92dccde6ef16e39a1544d8447a3908a1270b75962c460921a7934315e71ad96ad49e05642ce4d68710538a48e589078d4ad1036bddfb864d9e1a4457f6f62d1e8895cc7e1ca95c944a316f633f9c0975de29cbf8ec598e9cb4ab9c90d8249120725e43db98554735457f11dd227605ddb29dbcaab77457c469dd02f3a976ea38c15366a1f8c34c7438aeba04e7b305b8c4e025740a3906f8421f01f805aadd965d5afd58175c54e1ed8d106e64e50ce234c5005d2c06c004fe548916550501e8c87d182e6cab61b8f51ecaf9de1909153ce54015d0d1eb40ab3cbb637558edc9ca27ea0eed9726197c469acd1da90061399803d0f3d6a956d45cbf2b9c24dbac659fe99c57c786dcfbfb9571adcc045d78a8976ca6ee9549c35848fa6d65e901acb4935f15e84b8028da803514ea01ae26442b492b379077e58434457a3adbbbc676cc9090ffe3cf2ac2abe3d81d0e1b4a98628c7885a41dcff9a317b1052c98c15a772bc121fb108402a588b07006c9e89aab28fe43f1fde46636b7ce89bc5608b5924c6e432a532d14f582b4010669fbe84928217db6eff008adb04ea5fd777b87515d00fbad9b3055d9cef906025598df410cbdb4f8e7395119e89637d07ee9e2513e6e9f2d3f4d8a8f85145e0e13a790e949f1be6af02b5c0a4e202f70e072ad22b61988a57b39392540b6820b8853207b7a0e9e5e097b68fc6e5e83b99c4bc1f8150e270814a9eb3c3dc55e0786e9f0eac5cc622c8f6344b4ec9977f53f66f5e309413d89de2547664bd48b0ef23c39c91d581c6d1d95f86ad0b30dd2d6ede8620a2dc8f8b731be2f61bff104 false -check_ring_signature dfccf1df9294c224f387787a9fd1eb5bf7bc3fce87cef79f63e24cce553829e2 89fe78e69345346a9aad7b5696f568780eeedb4452f81c39baa8f58d18fdb5df 1 2e2c83a554b4360685592938b750f455131aec77e39b04b860c65b5e913edf99 2d5f193479dea35f21b0d6bddc87c612a1714d07e41b9a3853730b0ac9a5aa06fc0679b862814bd812b81825eee49418f7167b000cb112d8a76bd83133bff20f false -check_ring_signature 3d81cd490f17b34310366f418a9b1a42393fde06ec7c3e967559031bf6a8d406 87c75101ee3d3f762171f78d67b263d2d8e67d8aeb26fcc66ea42dedc815b27f 1 bcb4a6c1af17b3ea7a16ddae46510bec7d50accbfe94bf5aa48d1687a7dc2239 74f554eb2f9fc31bbf6e74e7cb8c201bc7ee6113a92bbf5b9714aca902aef3019092c7e1f382d6bc962f0b96ad26755b3bfc6327364d307934c2f2789687d30f false -check_ring_signature 5320e3bceadbab2832f82d18ca7b1dafea23acf63c7ba418bdd95bccc79c065b 618c0da43e2d1057382aba87210e99a90194b05946945ba9e69cf7c7ac2f0b92 3 d5ac9133ccd6e93b246e3982ae0dde6d0e7e3d3fbb374d714e4cd077e772b098 abe9ca08c08a72e40cfd334ef19d8f9bb1bfd6981ab24ed5a81a7c1ca6fe2aa7 b5757027ae2e1c772d8e9b672c08d3ee05a1379fa329596813fd5dbe5f123f77 0326fdc11e1ae6a38ec12248c326a7b31c609e6030994adc795fb3dcce57170bbce3299ae1f7a2fadfd8f6d326ad5f63091edee4700f51d6ee08b88a5007503b78ae84a3f40194013e3b7697fff36b39ac2247e10ce40d8a0541bbbd0cd073be0af240ab8aef375888543883d87b1617b4def4198860f632a80949774d9f068ccdef80e05d0c006bacfc32e1c71eb3a07d1bd18b96e9eb1b40c515fea74d48a7823a2501b59c70be79976faff6905065ea30eb85aeadf2d7abd4d245d371800e false -check_ring_signature 1d25abd61124a995852eb9acd502efe3ea9d4e08e2c56a4c7acc7d328f564431 2cb504dcc0937334a0832cb5583acbf752853a0135e18ea7b58f08185b0b9e28 31 9522b22c25b283e42e32890ea957c62a65bb109e4779a14359c638b08f8bf1fa 206afff6b90f24d0896f8a8e7e3307a4f2dca16cfa8c6e41b73a48bbc8c207f2 ed6a8037c004a521acde61ec81d473b1ea4e7b2ba908a590563ffd6319e4a0d0 3ae18e53c485b40720c1a88b07f8267370b669d2250cef7f8f36ad514dab9cd3 ca340206d24e793fdb99c98c462234dd134481b3dd7c326c35afeb52944d57c9 506bcb2b3295bb48e508b48a7b45322ad4afa35b3a07275f665840d779741713 d00fa7cce8e59db1b19b97cdb1ee357f81486fd197a6f9ce32fbf417913895a8 f9c837a48bc0db49499d1cf90ce8003d2ace3cb08b172075cc4d3afe8c943498 a3db896690560fd4f58810afc7811e01d55ef5daab37f91b8cb03c4bf4a18570 b0f781ff7abde074321aaccc2b8ee790fce77b6020fe25b12355c971816e0055 097f614b180d08d4dee44b50d4c50b6c7ba0e1e204b30efb2c1b2d0771e5d981 948bb6b125c4db2819910b42237a04d87c72de3fb71c56555af929aa8d15dca9 bb0f1e938a4d3dad796a6cea7cefafdc4ce350af6ec22516ee675ef672c734e9 e2ff91e5d32eabd6fce8400136bbe20eca8f83ea83718142232239ac7ad90b88 0e11410f60a8ca2c41f008139e2c4a68ae82fabc9cbd54de408226621267ad20 d83ca01ef437d27433b86230802b151caa9f4585ab80c4a6e95e2cf84798982c abb10d26e29b8cf2706dff9c276e77305b82bb9778b0acbb5530d7ef917bf12e 79f192c6c740b2e2934ca8072f761a9dad737b1a3e79ad90763cfb4c0583467c d349f8df36c364a6f0f768d9cdf5720f7be0e598da44926b42fd374443991d1a f3e32943ca9464d49318cf4318f7a3b0b4be57975b6e732c7cf62495f099a525 a7c21e8aea3f2681ca34822f1164ddb7a53f0bc509fd3e10de398ebbc29f6860 39315a7fca67ae21aa4482b807a41f11fbc2b23d289370fc604e3c2da7e3d43a 217b702d19c8f17ba7411602ffceb38a9b6a5eca2e9ddc1581f5a3b7113a02e0 0afd22392ea5d50e203a6f2a379d8d11e87c2c8a4323aaed9f938f80d86032bc 4fddeff015a6b77c1bf88a059c43012f6132a52d16640a20d33d0ac55db6464c a0aaf4ee336da9e2046aee0b98ce6b4baa4f443ea7df41787498bfa1f776a680 14c58f9c93a4674d1b5b848337d115f6756a444b3094ca1772e1e457c17cf488 897224af499cc0eedb8f7f0deba0ba4918b742a144d43a42ff652adf2cdaee78 5dee162b0507fc98a565a8166eefd6506e5ac976f0b99152e72f5a2db558e5db 39c5af7cd8df88ee614d0c80ca2dc011a5852e8cb7fde766381e719cff7db02b 42444f3df8387281b0dfc9d87d33cfa19a38b3b18fcef8866c68d8d44da76f1c 567fcdbda330220f3150eeceff5f731f2fe7769e94f076746518dd04400a240d12aade7ddaddaefe745090bf22e3bb7dec3943753b215237814b2d825e6478058f011c01f1c5c1ec2f573b5ff55ee96067d4305c1011d7be205d6c74b174bc019a84f19141788dd079b2d7a965dd69c2aae288433ab180337465a22f24c80a06081de8cfde105efe2ba8aca58db6fcbd5bcdd036ea61de8de0e54a33eddf650d2fdb70d7ad7fbb25c41447150fe2d6c4915a5dd8c2edbb95c128cf6560a7ce0aa3b21119b30c55ac3599ba5e4c4e5b25cd062fae1e37a7b265d28ea7f059a20a354a89ba4daaad6e1a959437299a0080bd6d4319fea4e95858479def53231a067a3cee91e1db388021ac1df99d9ee118244c8434a8cba95969cff982098fe8008fc86e80adc2a3795e7056baaadf92572d52464e090fdde1562b2c9eb7dc3d095431112dc5372e38c52900d992376bf6ff487bf9e2d44ef986e848dfeda8c400e0cab6c30ea2cd9539bf9ef6578c1be907ec8122b4bc91c7f962517acc9b3c034b05a9298414d581e20749b35f0445ec53e07caf9289d2d5c213cba7ef0d7105d4f67eb72f25ba5bcb0382c84f23d2434064e00d6b47d761005e3512fb42440abf2e1850d503c1ef1a0bba90195c9158b5637b3c38fa082b4a6121afa469f50431dbf9e9cb13c40fc34009540411fca39f9ce8eac8e2f5b594c407e7d178d8090d57ec5f7d76aeb16f4eb23c76eede3f1e606145c96dde83d8e6431209ef300f931d7cff6ce5b55575d7b131393aede83619bd45a5b92eb043477de11434920fc9c90d85306d0aded2bc866ebeb97312f5c65a43843b7e4dd1eafb327fd09609bcafe5a53aee09611c6b2e247c3a6874785a9abdefd199e1d4cec3fd50fc570588e4e59bed423a0299446bdd4cef78ff62d0b0aa698e00e663e09c9c099c180811e5cf175148385ebd735a16439c996fa8fadf1f23c9bbde8e8bff66bdee950aac7ec133b5d8a4415e3ca6ae9a4780a3a62240e99d8a9c46d9b34193ecc10a0fb2d093ac36c5db43ec5b18a834ca51d5b8ddb040bddccbd3d0fbad7a9261eb060d5151c2477bee31f622630e1c35c073f095a2c7954ee5a9af3e3d412fccf80f94fd98fb8dae0837f521560f874feda822595dd1abbc4ffd95b6a66fda49a100e0af0e9cdbfd764de7458d989ebeb29ae8e9554fad132ccee123b5947fd71d06731e282cd964b50d5d1d1fd2b8257306d206f3794751d6c50db44080ad7d590b021887e12ed081f8ae05ae9c93bd043df26c95b27feae0d81361f295e1303102eddaf5e0485271f55ff0fcdbc109b8102020131a21f145ff2021f4ea99b6b0047f6661f413aec3f5036618568df879fe7fe98e6a24e791682c53bb47004d360fa1789cad9dcdb7bc82bedfded416ae51a32c7874e0204e72bade553ff1f50007ba429f8046e4bfbd1ceb6c946e755ff445698aa7c55e7db549bab4c589095a0d69b6e4e406f44256c6f46f0a7565b7583739700fab771254f1d5dd9a29fb4a093a8cd5b2e1ee942054d82cff4540a24349e1fb0ade7944f12b9ed24982640702dbc7746d48cd83d468a57e416468f909bce862cb3743eb892d80dfd49d046d0958aa5b4bfb0a5e5640b1588eda2a1d1155c274fa385bfadd1f6ffa83da3b8f0ccadf0e54b9f3ec935b296852b9d68f5322bbd8aa7754b4d254fff50417125b0525644258035330254f579acc50b570cc8a32c4c10d0cf64fc3170a33118e8705c31dcaf3c1553507abca9dee1ae9006887e1e7988c8e5da5fe87403533c8d805847f800710d902e046f63d57566ac8268e89689777e695f70f3e1a0d27aee70eb59c4d26ecda37cd831c94321bdd4cc61846f709ed0ebac097526a1ea2b2ca050c11b2b96b7eadc8e2cac6f22fa4d6d97549c34952ce35f5cdc7520071ade60fb7866fc44faf6a223ab468cfffa971b4a34e8a74efff3b62f8baa695e28153081149df8f894f550acffbbc94a24ecfbc9a07b573f94e78b7b2e222b2ce82650c2755feaba66c92d49aad0a7851c15b9527f0f4238fddbe34c099ad6ddec8a10d0c33922c7b80a8a08eb03d35e07bf6daa469c47873ec53d084649eeaa9c51c00d49b97943708e31bad3600c006fbe92ba43043124e003ea924443b56af50590444005d1935c32d479cae09ef304845e4157537952e424114859be889d2eca6008fc73e0d48759a677ca7f22585df7173b7f63471b6b71b4c56a916aeeca5e40f7f080e74073d7ae9c712168080f253dadccab5a5ef2de0cb00418df310d69a018c96f086381632383b0f72a9fcbf9332e9d3e1b4dfd785209b354e0e1dbdfa002a9bd6153e619e7e50d42c76b84eba4098f256101214be60fb3de9bd4faea807f30e222c2a13418061cbc4fafc128f86842fa6d4a4a502db66a34e868f2e5c0c47b37eaf298328c65d30ed1bd37cd9dc0363b6e40a2f3d71fabbab88a9a1270bd5fac14b42be4d3a96ce517ecb8f33dafd080ecf05ca90189826cfce80360d0367e3981eea8c557559620241db7ee1e0b170f7d8b4b52153f588a54e68b7370f27ef6adbe62efa160eab5b1ac32191a2ec0a6c2adfb3a2fe97372ed9d945f90756c7c4be50c44666560e104ca58dfefe84170d5b15c797b74424bd6c0e08f20805ac261c16e23a9a2b2344ddbf533c27db7d61edc78cbd2adb805e0e78292e0c41b7c1ca5d2e831352c9bda7b9843d4f82ee19f255cf9c7a6846e637996d160be0da3d6cde90ad734876ad19e52e76cc8c88a1b303c9af49a87cff9d650e6303 false -check_ring_signature 496b29fbc5db2db983592bf01361a0ce21d49f0697746bfcbc4e2345123ac84b 1c65649c98be2e7dda7001ae4cda834522a3f11c1154886521e48cc9852a19d5 7 b99ab8223164481770d0402b2a75746c4184bbc245e239debd4073dafe57aa3e 7954edce13b6f8a0c01c6acb5f0ef3397bd0a4d7f1d1192b7ab8d539a19db1b0 e5edeb86874280e15d28d3d0dece7f6aa18aa689af455c79b5c768e40042a364 8188cf7ba684ae125877d174448fde8fbf9ea251521584cf4e0b74ea00eacb84 dc0e95e25d5840177caabec1f91155b6c3008544cab8f4bf72526a1415215f6e 1d30b0c98a069422506c3a1ddfa5d1535b8c4292c6d43fe7f90b04ea14e14217 88cb2a6ed896d46e5cc95e3db0558fb4a2207ea8ac782593b5613fb6220eb473 aace9a11c0c0f3138e6895408a3227fd4ad14852f399bc237915a06a14e145030bf5d3b302c4544e07e39f28acd69f3509f6264485575d21749f5a490852a60ee5f1c44248f18de43fe9e380fec75303462aeb0899c7f3f5bd01dedca485070044073d523cfb0de609ca6fca71b3a076dbe16b07a4957d6170b611abb20d5f0cda95034bb88665623f3044160fa0160dc1b3fd3eacef98c635f0c170094d9a029ee3b84eb524f858d7a604fbd8d89417c37878d4c37ed594aa86343726dead0a161077848f7ff21b04d6b65a2969ae6e41a325a0e6cce0d21f21c9328c1894a21824c1bc284d5e3ee13f0a68de4bb17fbca324a90a244487ba4a4c91c9fde80e575d2c39049913a798360c4abea558069f51659f5c28646dce5d9f20ec55110e68d93f5f2385885b508a32ef70e8e0538cc0b23a8ca91111f5ca0f07a2ff1f0ee85eb3730469fd6e95e5631a5c11dca98d971638bb5aff69da71b506613c7b0cfcea7112e36a4e78c378d21304a170a49700f51ae2226414abc8da2401d4d805d5997f5c70672ea7a679434c3e23eb99edd040974a264e9f19daac4617745f0191cb9858f8bdb5b6201247f7d87e4d8f69cf105817996fb2f7978b8404524a06 false -check_ring_signature fb97775216b61fa87e00e251ef5138eee6a6bd3bc5ffb3884ebf787248d9cf24 5fa0bd12b055aa70486f223195fb72d71e587132249820b23c29a654cd6d6585 2 3dcb8ea61a6e87b8ada5377c2bb3ce742986d6a2cb035a9eb8944b5e7e92e9d7 ac78220c27b398ba1e3b1252dfe6101da7fdfdd4228efc98f9fc5f68faf2bd66 edc343101cfe9c6fc611f48a6adc18b9f672d41ca3704204dc5ac24ec0739d009a7a01c32395ae3be0357f15f4d27e6bc9735436e43c710daf11f013ec55960eca16c9d5fdd32331343aa689c439950d1600f9c811d65009a0ada5c00686ae0da18b827a1f37a44908eaca4e78f2892cb22a4d881623ddbb33ba51359dd39808 true -check_ring_signature 9f3a1360eb221ddfe5a53da286baccfd8c720d324289a695dbffdfbe48a62379 54c36ac1772603daa61ea21495a010c6b9ac439732e8f35213d6ee08bba09a3c 119 5fc4a4c1abf904044bca230c84949806888a910a1a1407cf67cd523fe0ca7da2 6a4dff66873ea43744211ced5daaa1c7442c44b8e4769ebdba586fafbe201196 bb570efe26d1aad96deaf81183176462deafa3e0840e31c0527b16098b162f6d abe3527b5b5c896528368f22a494deb47fb5ed7ab2334893475dfdc13b36ecb9 c55a02b3f7f22f377b30803e6ad0dd642de808166848736799e29f44e0489d17 28390d964f4250670ba91f4c7356174c8d445e48f2d17ccb1b1f7f513d469a54 025dad9778df02f626f268d2729f5c6b6fc24ea5e09edf38bba7818127d38e07 e15c86936447dd9f12b29022551227ddee55108b5fb9faedb20a89d90b72bf0f 904dcf1662d4d3b27e85a09d256f828428d792f5a047cd6316bf3c11dca55303 c2f65f179e874bfe4033c8ee5a36140fe85e8906836b6f3a425ec72d101fe15b 023cf8405de2eecb090d27c1f19f51d2a69a979a9670387cb36f8c5cc109f2d4 71e335abc420e45eb7b0de8179a81112842652765ffb52e637524654dda5a176 362870c9498ea2616daf24db578199e82f82ef68117c1009d489dc6fbcef2890 19fbc9037ddb7a2b8f1918ba0d9ba873a2971d6dac508cd297970bcbf8f4e7a4 2dfc13148fb55c6249d5420a92e55b87678c0d970b7f5938ae84c18b3f6d4aac cfeef0924fcff5fa8d4b03354d7fb4301339d8ea61e72ba116f1bc9b91c280cc d1d97b2801c12a8218238a02afa8dd41c5c6cc99762a2de0d0ca9fde38a1b390 3e36df9024feb0e8aa48e11debe2862e9bab42e030bb98df4bfbf2b3efa6055b c54038d83c0abc82a54d4a0539ea6d0a39e1cfe2d08677800afa426fcce741db e4886b4a84061c626e4f0001e3d9084a42c628b57eac6f59ee74320cab86b740 dacb3966a35b61f487aa53faaa9d924c12d350916ac4ba49909b71833aac7f5c 27c11f7ea26740df8c52ad6f8def333f4209862e0b18f78a311b845d85c2d916 d1fc8acddea6f9415acc9f3e733cc853f8c3d87e263e565feba670b6d3bf1ad4 6a8f504e70add91f773ac09624447aa795f8bce901b55deb3418aec3edab22ef b1373791466deb9eff31324949469659ad1afe2f402246e4fd489fb5518b61d0 b1179e4d3fe40b4b8f4ae3dfc2cb3965515d3d41c80b76ea841e1230be59c914 bb0bd0aad5576405cbc5a007122b35ed2351945259ecc01b724cf67befb8ca25 d4df5933ecba172eca73c60471ab2853eb0f86c8c70d939567855f89be3d80d8 94e5acaf9131f28188107a4cddef3e0cf22b7b76e583e1e4777ded9fb6b58d05 e3aa848c1cfce8ba99ec6890de46e2cf0cc5ad878501f80205f46c9613f66c76 4787de096376d588de599f1b5905eec11debc10e711839e6e62510995e5cef84 bd10549ca865eb996ae08f3b78653eeb0a6a1fa81341dfda8b6e925d697fc8e8 92f5b17ef67595259cdbb4289188ca7a904d7f03b95eee0dea6179051c6e4929 c7c2b58d44037c774b98ebbfc13d7d3859bbc22389fa3269dabdfe5f4b00defa 324135270e999c2e4898c6d609820c947931fe2bcf096ac567166032f5e99753 c33187919e82a4380eec3769051e4984b4ea1fa41b8877b3d636e27e2ddd4011 0b0c4036e39ff8a158002b1613c84df31dd688095886a1e08d8b547203839ac3 8a61e4c74a34a3067fc648998a00283411ffa7c903a2e0fcc5e1cd4387c92299 c7078fa97e489fe3dedea94871a3f0e46fbe7c811d7b04ae5bb0dd87ac380f5d 59d5cc02af27a551636d492de28211d2cad5bb0acb462106f5a1f2f1ea71641e e9aff282305c2e3971aa4b6867cad5c682e384cd141093e0290d390c82684155 6477f7c167a55d2abff937565d9b66fa55355d6733280e7f5957169f3931086e f3e77fdc6d5c189ef8ff9a5e5ed88159216d44193aa0a1276b5fff2ebc72fde8 2bf5bd3c48f3e1bab2d1caa9e7e219173bdb34f21c67f105c569cc30d7cde469 53c57841962e743a85cdaa7a97ecd6c7302cc3d890556ad4dac7f26d07b62518 fb70f6500ffc20b70e232fa61762bc406f7eec222704e9855a46bccd03e2bc1c f3ae1ec0da1e6c93b2f9e6aca3ba08d2b143dda8b0dc55d91b73d94eb1fde0a1 197332b78efb7ff41e5258b50160ca0863fba3922eecd4494853ce2cc4ce2c99 94f0933a46681a3fca127be5c708a775a069406d138b58c9fc93e2ef6985c6c1 530a38de36e8b481e5d58ccd17374bee49d7a03d1320b1065866064db3155fe6 acfbd742587eaa2e301709ac69f002c2f92f7de9932cae515841fc4e002e2a7e 7b373d5b4ba8022f4890a45549ca5a4fa8c460ee1b2864ee02f8e579adc52ad0 676c98267ef9e1f59fa2065fb0e59a967a33ab9dfbdbc654c6f8430dff0f6fc6 bce26ba0d8062aa7ce97aa8ca7becd810300c497fd14306cd1a6144899e0501a 1293826700f4637c5c3763101699650d6c40df5ef1df17248cc126a4eba904d0 b2fcc97c08f8e9a2bb16cdef26f8ceb70e4b8e9b6d55396f3df186a108d1d6c4 d3367192a1ee434fb605770a886311860d2cb50ee968d276c5e6532e40b3d529 fd04aaa400be9e1c7da36a2821c565b1b96bd19381ae1710d593400a6b2b83e3 337f73db80ab42e863f311a055a1465f25840d07ee4732651867973400e7129b b43f2c8f88d5bd7014e955e1631d5b819b8b755ba703280660723e7587a32819 4df106f6b7c83e271553ccae0468cc285b3c4d6dffde72f63db17c4c16a2c973 1ea5c9c652fb777d631fdaebcf44ac074156fc1c28812d3b4b3e49aba07a80dc 0d43908876391f8106058088f6fd12ac29754c14d2c491423d73e0eb0a10c650 c6d46880f3ece9164ddbebc2498f5bd5977a468cc23fed0101819015a5e2779f eb1a03bb9cbb04bd17800fb818c7205647006111f5f6b5799873d625aa0e66a7 8cfe6042e9c88df5fad794e6f82d7f4eac4249a63ef66ee3a018c7ec263845ea 837836641c27ec575b25bfbebd4c611dda161055a97e00ebaebe6e9b1033bf07 e9a2b0ef89305a027dff55d5617d8e97d2f0dc4edcc318c7495da2b82e96f1a4 c064372bd15b9a5c41b82ffbdb57bdd881c351bfc64c5b27f6d8494d33410009 3554ee93bdddc7a8ba7cf0b5dd2aefa925feaa0972798583381794ef45a7d928 5ee55546c566435f79af3e2732630b5e98a39f0933e87a1d9bbcebe3857af9c2 ecdb0aecfd088eb9ce74a8eb01dcfbd954c1855fa8ac2436a1944aaddf052b37 bd922cb95ebb73062bbb5de25b2aa3452976b8216104a8e1d3132c12c489c87d a6ae845cfa840a1d73f96972f1cec3d1e4a93f310ee0e830f8ae3cde4abef1d9 1143de7666bd63a3e8076661fb804cdb57afdad45e33f8db9fb3847adba978bb e186d1f876743dd6206807eeee34eb3c65adb6560c74199b497eae7c462dcd72 700bdd4a50a03b4fdb927bd281eb0a6cbf3a36bd1ab313e62b4dfdb35c4efd3b 1159b205c0f1c083026166abebd7c207a82c4d9ff179f0529ab4d9829998d1bc 216627d363ed46b0dccb92181ac79ed3817461481157643414cf317ac252562e 60c11213d1c92450e47edf0c4dc2ffd95dd9f2283feccd73de250f39b6756650 177b56761f719e142333384470717fb3ff846ef20ca1c394dc3b74925f83e4d0 1df9ebb1ea88b8e30579b0f4e8ce604fa7b7949d0483ebd34472287ecac4df2f 98303b1463b8c42afd893b69f502ac4552509987e56731e967dcaff43fee728d 349efa8b98fe3a5886769c24442d5ba2fc824d81f12ff654b74ac4acb8d811ae 83fc30e9d57c8c0edb7d1a70d36df03df1c889ef615c37481f92ddcccd8fe2ea 65d82e5eaeae23be588fa2124c955342c8b4b9a9f18383f12b16621a3a98f0cd f978da297f3d6b94f8e2972989600375a82fee1a08a9ae4913b8723e9f8c9e04 bbf080397638b14beadb260eff930accf499c411691abde8de3abe9570906e57 226fc5138b24d78decf5fc241c07a920687c63d785d738b7beba8d6a60fbc4b2 17fafb6294ce4504ce0ebba8d3e1ae207b8acb8b48b83a46f564580e561d99f5 480bbc5eeb56ead8cd85cce1498530097aa80a94343a78a8a9dd8a56d5b4a13d 1a9987c14f3f90a8932f640994955092b3e8dcd8b96c7f1f22810680e5faaaa4 355a819a75027facc2313470632cb539f644dcdb6a8372117da727a40c005c4b d392c7965742dbfe43a6f82c3a035c7823785bfd0f2937fcaa0d8db03141941d 18ef88f2b46ac0ad70e1b403fcdba1872f446e60b38fa33c51880fba0bb06d60 82956d76a48ec0c17fff0d7ce84ac5c3e3e9f1183294eeca32333fec7097f3d0 42e738978c35ea973cd3d37bc7a71c9bc4289aeca8bf643aeed87bdd40f25c92 c6b407d9285b00439ba8eef30136f89d7c879bc3cb91b0f4f186ff96fee7a680 96f1db13d99d9cc69736355f907c7afe57ad4c74ff30ac61f7e749eb01f5c562 27ae835facdfd9d73a1111a4cf26a1ce972cfc5bf582c1de77844385720273a9 bcbe5b82510b9cd9ee0b71ae438b8ecceb877c4730c4e6edc4dae1fd0084a39f 96a8508462004aaa0423f0c642222b99f4c074ce8dd4f11b14aa0c21ab7dd820 a048af5066685d17832bedc0b385d6fd14d392f8374d6c7fac511936657bff4e 2e4a1d1903e13c9ba50b10fe91968543e9c54fd7e832304a25de134f293cdf63 a3aaa61839798859bd9c7e6f5d35fbc482212e5537a6cfa02140799f78aa5055 e144b05334ff81e901c3b007395e0d74a1e49e5e789c0b0f16a8c9cc194ff812 5c089efdc3a21bff7da052dffed709f3d2c8dffb8d4a1e3a6883dd737c0a6a40 328beb2eacdcc2d05c901ecfddb36eaff59de877846ac531941c79b829065571 6168bd16be72697c474c5679e5e081e4872fedcfb05e7c246937553cdb84bb3b 5d840031fc2e2a09fc5ce975c8113f447cc4078bce6e7cc69647a9cf24b4676a cfc6287d906b028fb034d10aa2e8d48ec205f30ff6cbb8c52991d06648cce5a8 ec864285122a065334ef80017d7ae840eb9ac9e79f6a029cedb40a0eec103db6 5523dbe3029a6015f6ccb5d161a369d1c806f2abaad3e4c8d930a8f6f9cb0010 512cfbcb4ca2f2be99947d6b24f933172f1c503212267e3e14513b7623e6eb99 cc8cab57a245a0c944f3d4acdbdd65c93c39d2e2db670096e4603ffa1692a062 34effc0adb2badd88c09241791acb7b59aa63c29ca4ce5521405896a3bd4fbea 0f0f4f05cc0e38c8ce9d5d01c1946a730641fee974d67a92d522bb65671437f8 d351ba9c638a00066be1bafe81a36abe47691ebbd828e0d70bf6209c42aca8c5 392466cabb87fa7a26ab14e305454dbccd4450b1a8ed2d401e1cac5b9572be9b 76bd584fcd16a61ed163ef6d8c72007b483719b92e011d420e686c98a36c7e017989ad31938f0403d129c587b8c88bb63418e26024b26298b5a0aede66af7d0adb1ac4184cf840aaf70fb8c3e5464ecf8b880ecbba1e5a7754507c0dadb58505823466749c205125f1ea41b4b17b3e5716af0054b54d480a0f004d2d1c23df0365cc72046f1368a296afab4c38155728ef9d2f13dbe210ebba04ddc9d9b47204856e0cc5984c21cfc2ce2aea4d3fa203212758163f3e877299a054e5b9d11c01a7901aead6ac5b89685bf69f0a53d3282c6a6cdb8416fdddf14909b1ec5d320320a80f8bd2672adca0fc4cfc521aa63960536170d810ee1f5bb08f4125b194089e766c6d0f81d9c13669fa6a2b8d4f982ef314db1e2244b13fe84957f8b8df079c6a099c8fa6cf391968a44aa195a754723f1d33a6c2fba60d2170f56736740cdcbe23d65b4e3ab8a161571a404f8fc3cff4f9aad24709b1afa64a341d55b6013af3af41c2e86bf73caa048e33cecc7777042f5a0b98ee93f53a85549dc34e001a3f67535d1580aed3ae9dec114a3037a75a10248711ee41fd4e3df738c59d068c4295acd60c2d22d9b2ec2f31c3fb01521a58367eb77b66f8193b3c4d7197011fb98571d63d94e69f9152a30880008606db68eb50d0ec725851cc59d904470229ae0cc40be6f863bb4d8be669fe196f006d54bead6a7a7d3663db3cee3dd80a0f4e373ceb70ce70a0d4b79034513fc5bbc629525f2687fe8fbe66c64c1b310fd5efaddf75eeda855e7ce50c5f687b1b78bcebdad43be76e3ae5c80013fa7a01bf1a6c98d845de90e1f3ef9ecf09b5d816fecaa88562fcff87f4f233c0d15e084095d77101e5c7f7d57e5a0c9be47e7472a87dc01669627fa830678904df5e08f6d179d7d4386aafaee9a09db115e957ccdbd844d75d30e69304136bb4edef0ba365ea85137ede60dec59428bc93b83c8e917e6d1769a7aa865b42c1b7137c093f8e461161a60910db10447f84f26181f55c97566451aa16eb92addf29baae0ba7e370b826e8d23e7ee6c005d0810a4d0f8bc6ee995dc78e34480ce58314d30551a25edaaf6ac67688c04d854915bdfde96c61c45c5f329f2819aa7dda18670536d383143cfcd61cfd5b0bf5fa976fe4217351b9718e662b08be5daa399a0f0ae52591f5b042993231c004290ecea7a578752a74db8bdc80e2889a434a00e102e1a0897fb9212fb365137df15b57ce96c632ef1d672de35a19542b0d76b4aa0cec4daa98d894c531dad1b2c6e73e087334fb0bd247beca3f34eab0aab13be5077adc568be2ab8381404938917099d0be85b7a5e5932ddae39c8acb4d94f5b90f115bf4da58cf44c194aeaa1884de8fd7977ddbec691d5e3bea8bd2f83ef87b057b6d819099e784355b92d3f50859f68c89be411761d048ceccf195f5222fb10fa282768889fe192ef70ad9bb7c8a5f864c04ede6af7e5694b796aa8b139d4e066fe4afb3b485c24e526edff0e731319c5367579edf9d0ff131d033aa5ac1d90cee052dfcfd5c16fc43ea793b15550d3b9676c96416b8441a530f6a0c0ee2a60047222c4976072f47e5204d8a7193b5e81c40dec5ce85bcecdee1af729752190c1f31d5f2d468de587d7584db8370e1ec62b81a51c46ae2dc242d98bc6d3aab059147c687f8ee4557158ab3b9eab1d76e1f1c879ab9ca44319d0535f97bed6c051591554ac3492641b0faa89675548eae22d406944c86d5b8289dfaf9b78fc6083fe56dd6504c8bebe3573793d37e6a558cf838eb634ad846b5361ccda0498707315bbe4570df686e379f9a676e685f390ac166c7f5beb1d105e8a0ffa2abb40cc09a7be296ccb14b7c4777a93a85ae9c13c4fa4c4c037e50e427b4522c283909211412bf3267ad5be9fc0d565c62bc66a3a5253394da5fa4c2c31656399ecc0599ea8fd5d05d2b2fa603a0c179376c1e0b4c9de9ff33fcb09769cc879406fd0a44c3692f47e5e84fe2af894630e9370048dca27e2d44ab723c7d6a0f3b0b8f0df9e40e8f9f793ade0c478e178139343239662cce7da2823ea3fec2ddeea73704510f58b1765e884853c2ef81226c18a88eeb8ab7c340900d639d439c7bcf100632b35f598f06cc6984b16b2c1a6554ce62d24ef8500c890ed03262ebf8946f0808e7f1966144f40f88ee0c44910c314b772b6e65c2adc6779300544b09b1d90d6719497dcbc1f70bebb907e3b496d943f518ea422ce9a06660d06c88ad445c0780b4af4e1d77fce80489725b6b8516fb315b17c39ae8fcdcb2ad22e867083c0cc722a5e7a1c7e20b58ea5986b6be5deb0a319001a1f722d915325a694116300ae1842353de7cfad24999af0434b00587590e606722396de52b916ca7662b020c69e17bbd18594a08e274fe41f2145cff26a0c185386b2046194761c0de6f450312c199a2144ee1f96a985855d3ea0889e112e99d311053d4c3a36721b820700e8e0a30081e06bd464bf78855b72ad212bdc215f6e9b129375e9ae29dc0e3bf04f380c5e85f315997b9ae2cac2189a8b294dab0a393f8e00da16bcfee76e80b0bd206a33f0e4f4d1f35f69f81a23de80fbe46b6ffd9372d72e8f252a7862b6804d2830d309fefeb15f48847f3f051a715ac049abfaa11a2cb061acf1e6abe7a00bba6bc99fa4ebd1031d2100ae126e1f50763aaf926435c4a938ad7ae61a2670d89ce16fbc7b0719e239d5264819c6f0c66cfc0ff471e07425c595616254bf703b56f1da5c1de802d155788fa9eb3a0ff75300ab4b29a805e2597b0c3a69273046c61cf7fe50185bd24e6382fbf25aab39528d677504c4a202cf317e8ef43220e3db0c82be9882c27b85347e862017facafe7d68968ddd4f7bead87276b1b2906e964513257ddb9dffeea2201afa713cff047e0c7f799c799bb05fcbea63cb90a440a8209aa03c2f56d2433e121bf7eed543c1ca773b7236a14ef722ce4265e020d12359ac5dae6833af5ad9cbc89482b28345b7a9a7008093e7ed010140b3300d27659259b9fc47878d0aecbe12d764a3bc444f3a1fdb44f7a20c04b60b45b03067bbe2e05697c0a41affce48ced472a12393dbcce676e72503d97f96708920f0bbffebe3f8aa0c9755342bae366d1733725deed6aeabef15678fd26c1765c0983671253e51a5a3d5d76a60d513905cf68362fff511a5cf167a2b96fb8308d02fc015c4cb1b4c5b465193bab24aacddde1ecbca32322efa083ee696df2ea4a0b199ad7c9dd4e0a0dfca4408a4959d40fce7b07e19075e9449897ddca2dfbc10b9bff7c6b5b55cbb05c917d0c86a7df9f61654aa19f5d2d545c84fd819db60e0bbb5732755adf5441d8758a424c091b2c7ac15d1b8c60c05a841bab740b914607be028621b2c7fd21b8844e3317dae5e729921e146ada57d4a3d3e1838ed90004732a95914a2b1e63fbbdfdcab7c755293feeeeef824374a329e934fcc9965e025c45f7919bc452b2ecaebc50d5e53f2e5541e4b26f12ab995b05a6236cbafc02326eac81f62225ec55411e27c95c3ab015fb061eab2fdec27c0c0ef7cc8817068f7c1dca93e65907af1ccffff5162ebb3a827f9c42d5122354ae5641ec8bb50f74187fd0b2608a666c70ddd55829e447ade296c5af8d32f2d6dd23e4b9835a090fda384d5a77c06349be72498dea8190edf3618d85f16f99b733c22fdbc4300d3c663713c6d23a92138366f15952f7b8ac4ba7ef3f53da63374bad907a18500bed585612c50526142447765706ca4ad735fe98d379d3d68875f53a6e04dd440d12b7439b3cd08c752ce67a7add3ad1e1cd15a8181c888419b3cb348411345d0e40b084ec36dc624dff606f2d31a6fb655b65bffccea95bf92bbebb3f548fc30c3e410ce6ad14cdc2f2184357f6f0b082bcbc16f19751f4c9fc9762ec857d5e02e4015fd34cfc63c66ce73c326c607191a195b72e7a7379dbfb024fbf578a9b0d252d0ba5959a727993709a794216ddb79cf6659c4a51f5af5e075e65ed5faf0ec42a7c01086d69c21944bfaa3eda2d895047b9835c2aca6d4c4302c3c4fcbc085d27ddcdd249af21efbd7ae368afc3d49fa7b7728c6daee414a8f28b7bbd8f0f2f82a4dcace389f20c474d229aaded0d79691ab36ebe386b625ec17e4581e309aec858e3bff7303f95ccfa154f55bdb738097c61220a375875df341e7a724605991739b9d5f38f129add58898ac8cf0297f4601f5c4a1990785533dea1a4180b3fbc6f0819fc5e113e6f4f7b878940400ee4925d0749c05f863aa961c9d9dc02a83a3439e9b096be9ac87aff6b13c861f43a2dea799cbc1b73429b326d30b402674bfea5ccf34c8768c124365367f0e9357b5f443a7bc1523272da5cd18a9806a0efb497ee8ea2a915f48cba401da3b5fdd86ce5f0764600c7e01855323a6e055ebf0d09f1841528723f76a8f11f04e55cc31f2d9a537d94277f10361df1ea09f4f7bd4d89b9ed21dc8982a048207221be62904bb18da5d493f063db147c840bffe367611fda81982f916076fb4ce784cbdcba6c03ea926aaedc47b693730f068f721fd3657beb00dd6b923d1a157d382f071923aa52349bb5e39e8ee6841f0ed79c5a063d45c3aa7536f6555f42606839eab7dcc1a3064b680640c64bc67c03aa1b59750a0dbfe6560408a2cb2698fd48a1c6ec7b3de8f0b89a3932824bd10cfa1013507637b0f65cbe5594eb39cbeb70bfde083565bba017a7635d3ca1490e66e8f785f3e27f6cf894eb2155aef45a0cab8b9387964486b93cd9094ea5040d0243a60d3a3b53aefd2aaf7626b09ec3223e060121c0a1b6072d82aaabe0c3090cbea67620cdec43d342d7739462b207450625baddb4388dec945d2e29f37e0c6a95d964d8687bb32bd0409b7b40134f0ad39a210f0d8be044c5a477298486078f9a6f53431c1196198c456d422730176020bc46be7845443608277e7635ab026945eaab0b9b81e1ce790ceb6517ed87b597eadb5f54ef9c71002a0d37e9ec0cd21675e947f96dc13fcbde4c4d65e49a89bd4fc1a5cb596b16f37acb39f9a0005a0243da8b576477c28a9724604ddd8303264ccca674c171cea459cf425b7104646ed8f3e3203587758c2d273309593e596fd18514217c4d13eac3fe0e9ebb062a08728ca1c27b9c8358830f480b293ce4f3ce5797898a3f4bcd179e82a79e0e972903f501ca25a08d6405001ada257b024a8f72962c79f9b88a86d1fc5e4f03e13019bdb5dae763bb00d01dc157798eaf5aff6a88c9986b3ddfd79a90e02f0d75465d09df2389a3289934c794c966f1fc14a2aa626fb03932d82dac56436d0f393765c989482b097daeeeff61878c93f8c2281a7ec03ec87d4ce1cfdcabd80655fb05615a529f94a5e53192c9bcb41ca76b550768d2933d6ecdae453e9b1c0a90a3712f4d8f6a7a8be412db05ff9af720eccf62b1b63c2c14f6a0b86d573403480bc019b70b91554453f16aa0079f5503ec802ff88917b0040ab6ac619beb02972f2731cfa1cbba9cc91db8f7975e67196ed6f8ca7d6976b35d48222f92e602a1607108eefe8f3b9dc0887547125416602d43d6f657b19fae118ba070a0390873249cee68ca1dc5c2d9f26d1bb8113e31ba2a4bdb74be4311473099f0e7ff01172a8b581221cc7b4c3f321fcd23ecdd0cbfa0aba41e41ac958600b599e3f20e0e1af6dab61a56d7b7d404c8576a5370d5d3d4bd343e162500d0ceee27f0c40a6c6db5887751c73888547e8f9f332431b72605be601564b84181e0cdc0cadf0883e23b85dc1f0e3600f30c7496bb7794a53217ec5ce95ebc37f77f269bd6e40e9b35eac84a280ebae3f9a8ecd782e44954467f50694c8ae54a20c28f80fcf00914e6e220d3648e36852a0c9d0c3a846273234a7ff9ec17038781e22a52104d0c9b3754504ff5cce648a2d5263c79cface83803127e3b5a77c2a1b0faac587e0de3340752d3e801829c424c566a280aae408126a1eea8292d04d4504300ac500f6bfce9c6c26f53db71cfcfe87e8e2c5c3828339804d64ed564ccb1b3e7834905b5624b573ef3d157f07ee7e9115dda9829c7b2ca1515a89fa91c00a60e617a0380004023ae886ef76fa52f111b1412c52dbda79f71e002ca6493e65d5640bb0ffe21ff6234e9e19c95ccac9200052f7c41e8a9dda445439fd520b85689871a09d0c64117a0af66fabeaf867f8252ce609938890f7a77b382553868488501c4045df266612874cf0df154ddaa7b95ffb739d27d674c3b9e0932e12c13727951072ee8561324185ed030bcc4c05f86fb1ef6b9c5b8e8ae1611be9e26f5e5917b08c5c06e2bfa9a6cfe9c5f70ba683ddf14265d1dab83cc3c4b2ee744ea2e69840bfc93d2575d685de89e1a1d6c15db1546b4dc68cae4becd2388bdbd6393b4500481666504abd73f7c0d40ad4bf759132a68e98831b7b4cd233d4564d057c9d209b40138287a724c36aae6ff1f20111e6111d65b5ba413b37762864f61206c19096339737e6487b89e28136acfd1c9b5a4c06b165c9bf23a25c4b5eb063295f00f8643c7e1dcb28d46f5a4eb5b7358be65e639c73009d452b7ad123baac8f332064a4239c07d8613b8598b43d7c169c99c74a5685ebf7da519a95d312938b88308364ff004c9f402416918a593405219d009019856c8597e62680d110e69229c0773fa218889a96e23a24e1d273e06f52c02659f3ce6397b8292a5b8c4e0cc9e0b1d6783d8c2c561386722bd5965f90d1414ec02d8a1ad267565b155be6bf8500345a697d900cbe029761d3fefa8d2d0fe84d45cadf77892c89f05e0a32921980793c91a29ea5f3577ade8c57018e45bc3dbd6c6d45c1d5cc0540cba75b82ed306c1486ffb8f88482d0956a0bafd4bbcb5823ba11e617620c6d7ab1e79b97e250eb995f79202a2a8e671b9271f0ef7b9668bb9b2be5445b90fcfa8c176b3050b0fbb72634e996c2b3d90fdd0e043110a3a532cf016d5f2f418deb51741830ab1020a7e77b7e8de38014a5740bfbe6549fac1c72630e57fe675ce59699e2d98490c47c4908df0cadcd28cb164924b06b98a71515248f9a3567ae2e305d99a540f0b39922affde1d3c056a91e6872e7d18dda6f1e9b5136bc2197b1ea29fa0349d03edecf14e5a8a76bbfd35946d820c076aeb4619413a61b15d18b234714d083108a4e643dd7c2a46b036c9c6a95e9b2f4b569170f485e817ab2ff5968d792d4a040ee31991e9c97fb8bce406e7b1f27ad98fb6e558bb7500c1f2577cee1371110fbe3b8749903f0fa560d3b47a13a30d23bf77e54303541afbc80df64a7d104600263772a6fcb86d27ba2a532dc49906fcf08e37695103f5ab04ab13a56c6e7d00bef3c581295cfa6f36b9634f2493f25ac669d9304b72e6feb73a91c8ea2fa30e0cc860ccc8c3b6a5abf18e29a48cf692aebbcaf536664e69cf7475971662a2040672e90e7b21a9a1a863f9230a842d9858a6b0fafb86369c0508047dedea90058104de284ff071e399f9d1e7fae161dc98129c3944dfe0978ecf5ccc3854780a891256cdd331f1cfb1f09bdc13800380448413c43f1da02946c2018f4566b108945b5afe9224cba4adf0bdb50c83d96a00c1929afe854cded227c504457ee70acea064a756778c8e421f99fd7d1e9b3a77f088440509ac0ee7259763dacc300e792645cdce8c5a71e7f276c3e5828388f25e8fd29032c28de0abc0fda34e7901053be7e770d79f66e42871a1fecba344571fa06f503b5649eb8ad9f9b65a710f4fd29a50b0b1da29bab7f44b3f2d83b4ed21eb0c697db962a90306157666240bc17b4d7b9bc994a014a2497e8c7f63c508a8dc89ff5bef9f3a06af5834d0f900a8e7f3bb2ada2f2f28d6fe997efb8132f5603264b7ef6ef9d8f0e07fde6ec8020a580620708b7a74fdb31fc0580cf10a1a3c5ec7422e2f39fe7ef5f71c89e10fd72d11d11a893a1c33fc8daa07e4ea67cc57b458c0dca65db04a9cc952cf730caae82fea7a8259d12d03392bd467095c0fdaf1835576953b5d0412e5894666091056a004b0e4a7945533af2f4fcf1355d9f499b1ddc7860f8b3b779b5892ac0912afe4c7cffbd90165642d49df21b8a708234118593bb730e0903bfeaa308d08f55b1617b6acfc75bb58259d799e2c57ce0cb2f1ba4650f14221b6508e86460d31963c781fe8e2908ef9c6720371a0a1ffcd6bfe0f0827dc2e7ecfedbf06bd00ea24346d1e35736adb7054344c59d225e42537fe4977b0e3b8edebfad6ab060a5084a7a87da2c87e71be0cf0b474a565f360ad489e22476a4220b250afdec80ad45813861406b0dc4631ded0ec7f903fc912048a56f16f8d2829ff371c8a540dd751b7dd4861fdd70bd54eaf73342a6123ccce5134fe4e4b0e4cf158364eb009a1b9cf922ab8a31ab7d40f2dd9ab3c3cc4da6a7a4fd225360b64aa35e5c0630e1384a7924f099c8432761138c62e576680fad52aceed47b0f9df9578f1bce80961145b8a409ba13b98ef29d318e83b73d75baac82806964186a954cab8206c09928be04b1f77cec3e13a3b492b9373503a748c7f59106b1a031585548e02ea0bab1fcc4854d95b1c2efd8b1ffc48c7c279c50ef577f846963f07cfc9b0be9906788289717cbf7cb09d4c9078bbcd99e0bf609bc6b94bf0556638dc9178dba60d6509a22eb60f8ed822daa6c7b216ccb28805e923ceba3df7252881bd09f52a0f2158e1972d6e9a44b0e7b0fa953602b25d50bcdae34cd28bf30a0d892153460ace4cfc295150e2dcc136853fd64e474a93c56a114901ad263d308099ab760c0a72902dec64476d7888375d666ba7c00cfee27d930376282c7fbe811037656f052744200ac22daac91504ac48a0362f965f3e2e8364c237ec80bdf0a5a4cf2e095f441cc39a08527d382d084b87a0dfe6308aa27768db3917b9a6144f8f17d50f7b6130474895ff99b0b78e73bd700587a8e55af56c2008fcef6e9a9a707fc00a6c7c0c13a5a3fd2391a8fd5d9381b9369ab6a4e36693e6de55a666eebaaf410fbf3198c3523e92b6138509e5affcd1d1886cd432341806935db78f12cc5cad08914e91e25bec9c6be0e9c1aa5b02c9bd54e4b813122cefd07522629b35251a0d26c0a5ebf6a4a4651bd6727c529d9c7fd09b3d790895b16b7d665c1553808d0180829d121324cb6094088ed7c8357a318cad67b4daeb0c2361a4523612227e080fa0eec2530d40c0cb2d8dcf5ea457054c66c33201205cc19f7bf36f8d15790c519741a3bbac3909dca5befb82dffe95d889ebee82b9b8420731f7d934016a02605d430b92cef25866507d1b47771eaf93a6f964d6b3e5630cb1cf504790d207fc6eecd570e0059bf16cb282d7a5267f19479e9bfcba84155c9aa28a570f4808214f07cd9b8b1b724ae69e1d57f1f5f36aa006cafb710aeadd5f37b7ab158a0a13795b8edccb6a22d15cf10ece1e4adf14e3b48839a1574c41c34f32de1ef305753c95ec06351339ee0ff5988c6b7d0d8503375c82f1d2ff21df4783c9e72a09e97c1572065a390fc071782628eae29bc6d10c4eed2647178f4cc7daf0baa10b719d79616c82bd2fa39887c9fd52e88cf3e9e84d823dfab9edad3c61c557ad0ee13eadd10b575b1ef3bddbafc21600ab8e8948a9c0b0081d9608acb15461060c65f69a0966efad697216a5451aab47c6eab1f90bb05dd7edef6ced1862ac1f002d0478ed08247fb60485af8ad78369863cadd872e27b913fd915983dc670b00d9fa436e56b6becfad0013da1faf5b86936502956a4604b4e170e0c63bf4ca50320a11eb957ffc4a7e38dc9217cb1ad3256ccd136eb87bc6d2af7980e6564600d2276ccb87cc603cc423b12f6a53f94d333e9efa955c8d9a1d8403797d216730e3585cf3fdd4243abaf77d47bc93430c816ec2086964a84e0e4a22d2e0538360af7cd70acc7166331203a4dd63a266af2b7bbd1f271e0856f6368e75dd76bf106b773780c6e67231caa57125df50527dc9964128564880d5938045a95b82dc40115720bed48db42a34ab658be6289844e80457c03b4418f5de303c6cbf8aded0b4f0ec0c9b0e173e7e33a6206586531840f44ae29b0dd5221e22dae1cbf373a037721a02acfe735d6b3f95e3bff3022b08793958ad40be388bed64d4f330ec8014e28e17ba2a36a6929e4d0d24f62ba49073d6a790774cd748b498ebd692639022020e9a8c0ff5ed66c1dee7e3168ca95b38ed5ba6244efa04e04a8569da9820ec7a4c3308f51154a4f2829cb1898e5c9f98296995507bb9cf11fa0b3a5e7ce0c785bff2d8d0289aea9ed72c08ae657c3d4c4e91d3d15edc72a0374e2dcad370c810b31fab220fd5eed1764d80ebffed7cd1e41cebd12eb416269c108a7845a054167039239c8190721a8da23f2cd459ae39f6d52de615586539665215bad3d0858da2ee5aa5bf81b98d2b9ee744662cd845610a9d9e2fda542c3ba453540c90bb425eeafb623d55627d06b64d07a9d1eeb262bbaa59133cd7e0f78cb2bfabe03856375a9b1c249a6d5b6b60b53cd8f47e8d2dadbdb3f5b63a34fde9dd755240ebf0ce63ec01335444f9c90fd5b7fbfd6a90247991dab5e51630b381140f08d021fce3df87780d0f0a4a83e6bdd7f75b1327af944b53d904aa011ae8fb395d10de89c9ff804bd1b33eb3baf673cfbd15ff3d32e4ef2648bf36408633a9d7fa60f4a65ed6b141f19001e0cc9a9c704803db7695c14e1d18cb2a9623f691186a307 true -check_ring_signature 9c2488b673cc8a4635dcd676518a7d19f9c3eff6908216e6a6d9f8ef6a772144 3639de8d0a368988da62b088d95a6c0e3687d3d1a7ad2e89462687aa58e4299b 2 a22def33307fdf52f7c6c34a671cdde4df5cf006f19ed9ec68d5448f1e9fe828 7c81766464a1abaa5ee46bcfea1a6fcbf859a7cfb41734fc632395feccf9d63f b4b17a29463f6ac3538520f76a3b9fbbf929ed7ce9e97812a01004899e1d7d0342661f28da5120da242b1103e34502cc68cfe611d7219a6aea752ff29c41a10f15d1ee78ca47fec8c9127f558b06e9ae028d99b4c96edfb79723d064def42500dbda4a3df9cd6a9777c1f946b5c2a63d6f4cf511e8ce776a284e47144ec8a003 false -check_ring_signature f2a98c35b3b6ba6c5e86357250c32afc78d6bfe9671b8e6c3453dbe9fcaf66f4 55d367ed34142748897ef9faebe62f90d8c7682c119e1fac8951bb432aef51d5 146 576704f00dec5f3bd729442cd97151e6e733f3235488a1bdc78ee03fb1f4ceac e6e5b3905973e25646b11b1e2be0c7354740f7d439b8f31b6e00cbe41bcb3b52 16562d06858588f1cfd6fb6e928b234a74a3709024af9b9af5ff13644fa42ed3 af2f1ecbdb70b99c1aaf60e39d5dbe58b916361eefc8d11e95f73fd2b1e7339c e065e1478a3f9b3c8c72eabfa1e0b3dc8d5c632aadfe63c9e5f00a50159d4846 d02a5710ad283bd840a1de7919dde60699e8656ff564f4081ad27c523ff65cc7 25a2020f8de33fefcae7a62751bd97db330a1b8b11ef81d17cc7a64ac79d02ff fd5f8560971ab0da247bb336c2398b2cf843a59b9324c1855f0fd44803f7ce51 1ff9a8da19665008583d8619d180d1b77bef737317e630d9743616c61b2eca98 3fdd55b51621f2d41eecaa7de1b9d9e417c6d05d2fa1124727e5bb154583dae3 1ee7ac765aa6ae5b69df70004efd422e8dcdbd2b57a284fdc9deeef031060b90 d6999a926955b03aee94c3aae996adbd2dc0be0377ba35e7c8bfb53adbd1734d 009990aac26814c0f6ee4147eaf14236f106be38120d16529b1f4f959f7fff19 3220ca9f5bfca59305c290841111edf17e28b3cc073211f1bc9de316a36a3f3a 2bdf757e5e9a18e5582f1af37d873a6016e6eeb3e6f56b40c003b9dff31ac1d0 5fb56c413540dd7cfc6c533f07ccdc8be3b8eb9acea5e2633b4d619df8701b9b 9ce25ed2749826869d799b109c1ebcdc4f2bc017de0427b5fad7e62817d09ad4 468c3a93125c2d2a9bda9a9e3affee58d280412279b264f27a86acd3aaa00587 c24c430cfd2c7ab1416c221f0b5e0ca622f512c7275b1cdcb640009a1c096e0d 2139e76d76ab994ff6d4e731dbb5eb15b9a2ec736e6553109dae3747b4845a9b 1032190fd80c41f6ed8572c52e0e7c950c91d0382f6ed433640543d40673f5ff 96226b72e5d133800b82415f4b1ae8a6de4c0f4b4a0166be28a95a8b109c890f f484eda8d865d01c716dfde5b8015c92c3a6c5f1234e33929a44481c88a68b70 6858369d12536bb557a37e526205a3eaa7a0c8df5829ad915a3004b00b3f6eca 6ae1a6d517be9cd74169fcfcb0521298fe3ef0683ba1de007b7b847c49712a68 a2beffa6a419f3549c32d4fbc7eb8e36e1c544f7d7f4364c83948813f756f037 b4ba5d2b4a4796d8e198ea7d6d70bcf5c5024a0e47409249bbe8379e9fa85724 d19e62a896ee7a41d801ef1be1af6f8c3c875e29f9e026e1b3c1355b5404ea32 fa54bdb82cac0d9fa361e45b2315c1a184ce05a85894eada37f0548e88720c6e c25e5cdef4d884e178d58b61527f0db48ddecad3cd35e2c89514a4c8bcf6d1d0 5d7bd5b724a988505e8439c321575d5549aba8bbee72647941a38f50908cb228 c4710837a8427fe5373a10186942f892854b69cb09d1f1efedd104e6666d26eb 9976d58946c542f2c837579b36ff3002aa77673fca6ff2368e6a195e622854bc 30cf978e05eac968a85cbf06ab2ddc8731ebcf154b2f96c429553cbb79f07806 e59303ffd6743b2741e8910a57d50f78972b7106b645d0f0ee4ff4412ef85ac8 0218565eb46ba6eb9162ddba493eeee365a3420532703e4be569aac6cd494762 41a371b40ab4325e6d5b8fe73162a22a2332de108a25f8a267af4fa7ad9c534e a50988990d1c012747d06b43a1f3b59bc1889a07fa170aee8113524c8c2746cc d11b0b35fafdc17ea1ead8009e4bb27cc528662e1f3786b1d284ee6d5415e23c 032ab1a6f5714a499818490693e35fba3cb0431848a46027cc0cec3e6225f14e 972be5fa33186e0d663928eee8a175235b540e93d0114a392ca02af9c124eb1b d50fdb5d394e6c67d8e64bdea034abd7766c0f1c58374e50768ff0cc3416516a 1a695c5a54be7f81331362b304a9376a56e7760e773825abba58b976cfb8cf66 7ea1cb60d3433a85fcab02b95f3a76c9a6040d67e50c1c59a0adcb90f16cccf8 ba00805931d3e10b8fa156e9a9b09affabb9f4d87fbbd5c403c8f93b6daf3e91 5a46a1635f15d802685e809c41a4f50a7da5dab80841020bce8dd50dca4095a3 ef61a6dc56f936045edef864a2ce398d921988e952850b4365708f24e583ed11 f38f1303a67fedfcec09759dff93abb1366d4145dda85757d1f7d58eb5ffc21f 72882e0b0d951d4859540c21fc8c00aeaaa671aeb9f6ff3591dbc98447663681 69d13ba9b9c1cf7898140acb88a2818ea503f8ba97fb311d48ea218273ab357d e2ecd50973ce9cdb1a758eef23194e12fc1b19f2b680c2fc91ba57acf99276d4 145f18dd7737ed9efe44eca3c6f75ad29c60fba0b13f1e53535c315918b0167d b2198cb3e6d646179054ec0b32a68b7a2f3eff0ecbd1ad773065df09f89d97cc af53a8fb1aeacf3a3a2ce130a15677960f91e919cf66bd3f7442fd5e99407437 12dce38ed42f91205c5971225ca75e7ee0ef942520a40289b410db35c12d980a 89eb05dbb8114ddcc6d88d464d551df693f73e44ac07c8d2db5d148b9a6c68df e053ed77d04aa1d3b290a0a40c82c3fd8bf9c3c3929b9b2b2aee5e52c8565b75 7145b38207fdf8c672302b754f67e011483ce43489e6a01376643eb374e38d7c fdda0b7331ea89b8e259a0b507b561b1a6070d2fdaa45bb04ceab8231f8671ec 77d9a219c6cd1e09bccc2bccf5e9846ffd4f1d85cbdaf3a8d5fd98ceb66035c1 28dec253efbc8cd9595f9da23a8bffbacf87e0ab912ebea7f681c0c2aad21aa2 9240309cb68a35381a9e9910f9d86d66409e18ba0e68b4d3ea5eff10fb1608a0 cf15de5f018f86ec6e88470bd06b5517260f75b600606d34be6bf112ff983464 bfa7a8c68d913afcb700dfcae55aede834a5cda77057bca086c0b5708082f760 cce933b466753585706535db771b4ae4729cb021d4052f61d8d6178adbb50015 c981eac71bcb316c3b8df42b893591afafd7e83242ac2b26328798fc0687bbfb 455365b857cd1e974afa64d39d170c14e3eafd98402e8319353c84046bd573b4 36b9a4436585a1ee3a4c0d88c0891b51ae4ba6d1c39ec54f43cb7e1834036e02 805e37434b03d25ea473819253c57ef786129cbeff2f1d872038bc47c6c185ce 806d031c6ce2d8d3596cc56f600e57428fe08422efe5f7acf99b7bcc15b311e4 0e180f1fd51997f290655e399032205e1f44c8f175199148edb78c14bbb50707 2827da8e5b9d3ad00d60f24c0cbe93bff22e877dda130bf6c6351b4412f8f9b0 7ee4a53632ddfaa461ac5dc6de9094634f2ea312ed1a6ca196a709e012a6bc18 6e810f5dcc5dce609bf70e7f399048abc03b4dd0067d37d59a050594c32717ee cbf591752070c9533d7af16b963cab27adddb982d10c87fcef5853be377e1241 6f477a53e555766261e78834b0d5d819967dbfcd91d45be13f0866b2fc23a521 eca0ba59ec4f7317716a3ca1d5ad6a78266969d12cbd80b757c95550d466f333 86bc88cc49e8bc1edc7cd2338f5dd2a0fa7586fdad24ef94467b695e52601f9d d8cc161e4d0fdf0da7f8c71872318647b35e2946ece89166d4a8636768c475d3 c915deee6de97910c9c14857719edcf46689e30256a511dbfb4eee32fdec03ce 23cfe1beac503a413f711e3291368e79fab6a7668a159ab27a414f2e2f3daf4f 777ff69f37c86b70c09c3337f48ab2cc1a1a04969ce00f99613712d0945349a6 d9e8f6f6ba627b5d7654ff77eeb9929923f07bc8b87a4de4a15a918a9cd32b15 d45f101c8d6fbf92eca1b0aa6ed6ae3b996899f217783c26df0573fa9065b07b e934b23800341cbdab821adce45d88a3cb86c6951cc2c29036f2f9a600aee87b 8ac81bd22a92c3f113cf6d17b39ed7dae923831aa44e78bfe38f2429ed5b5edb 41c6fa1683c09dda7f044fc879ce519d23184a230d7b1fca87c0e91294f256d4 86e8d30f81056a5b7bb93f51c984f60d1712034597a49a4e9c252dadb9ceee2b d716e97f49783a329cabb6bb506be11688ed19ba6ed667b3cc23f81ed1c07394 b86d20dd4b5d55eba7ed909fc6b3969f135a61989ef6d6c096b65025842a2774 576d6ba7eaee8d38cf6e7b1d9c32869ed465b7e786d3c489421d77dbea8f0958 f3c5376bb165975247ed2f3335fc4071f01ee46ebd5dc84e1573144ecc86e9ba 1f7e67cb8ff22194cbd87f49b62fb23bb117c6a563eb6abba56270809c132818 d7ff1cdddc984c01d7e15210e891f38d95b54f6404838a54c4ac4ab7b63ac1d9 dded9c3b6d042a82717bebfb090aaa7aea45cdf19ea8eb59fab356b6f2a9c9b6 a196a494d47666e0d518e6ef4aa115a86fb6ab3f3be2549ec48c28cd8649e51a 3b1183e8e52867ee84795ae28db3a4f04a58bb6e59082b222a85bb431b63e1e5 c9cb35f12f9959a9b77bfb5789b47419e1176836a5ca064c7a9bf3c1f25bae8d eb578bdc8b4ac96e8e068aad53b41e82798f72cd696e1905c7483d2f181933b4 17404074f2f9f9a281633f6761f6605ba0d26824df0e15686eafe5ee00231dd3 a958f5c6497ac9f0367017f387ae29d9b05f69d7c25509e616f80e5aa1906435 7393ac017311f21925a0377cf7e1017d6d86308b730295bc6e6058794a4b2537 39991c4955f754b7e6acfa7df06abe4686e98e8f69062ff822865e144281174e 503002017d400ca6f51769c3c0bd7a3f91cff638c7fa2b60baf86af3bb100ced a25e63c117b59a52dee32a90be19f66951480328bc6d7c9be9456b05467b4de2 b9bc497db432e7dba216a870ed482a2e806641631d700c7133dea1ece1264bcf da67a4c4a67a43cbebe24bc7c9157eefbbd264518e81f9b9bf575e78d795705a e690a95282ba80f1a051838c60087a0931290ec32616fc48e8de02392b557ee8 ed24b4e59fb22f774167bce935628410550a3f1432a24892d9ee8c167e1bc5a1 c322e227b46ab376d09ff1731ad61eaf639207dee2a9a68e2efc8095783fbe7f ce04d27cc4317ad33506d1714249be9e64e4f3fa6208a1f0aca62d19bcae0294 81f83a8110fff636fefb0f591eabafac806e48ef3326259dbacbf3b15932ef1b 84311134e3c1ec76af13733908b0cf3844826125592c894e9700d31faf21b325 8fe6a0409ca0d6189a46aa1f95f399b8633bd420c69722f99fac0fcb23b68ee8 5e14d2eee2f8090c874c479666a6cb9a57cc27a02959c1dbfa0af23629d35130 3f2a027f531ca1b67141e7ebad47383db458c94b13045e0a8c19ed382b89604f 8e39b1dcc1419e4bb29d12941d97d024f4f46f6e553f4d46f2eb192d8e041432 fed699263467365c65e3495c19617446b9e021cc035a5c13a0b085520bf9bfea 8916ddb73c2473cfb25293e476f0820ea93af65d94bd8b5317d05303d3812150 0a195a79ddc1ceab1ebc23612378b0fb5d9a3e5881fdb55bce3c64017200fd7f 1e7007825deb193cfeaf763f1293909196419b63a6cd4a6c446051d6e91dc328 e8fdf65588ae607a6aca2510a8ca18051d40a8f0ebde5a042a3aec55ded4e2b9 713482fc47f54a5e9b141a7d5cda3ebaf5b0de15ddc099e2b17841ba40d752f9 d69fb74d3ca28add269687a7d48088deb830fbaa0abcf5c1ad04455c8f4f3615 eac0e9146a8105ee2927f44cd54fa0763457e23dc5b600c2e8d26bbdd9b3a71e 42f2eaf02cd6a026c99153bd2a194329157f1e270c5bbf42c52aa9959706a739 c17559961a76d16bc7e139db2fe79a70284940c2ad0a30ea876acd7d7d4f4622 0abdfc892c06c4f7cfd2a0049aa3365eecafcd04c7c41794732e50aaf7cfd791 c6537aba57b1d44a8f74062f3abd92494268a39a7b22e5e1f07a68f5406c58b7 5fa5368f1f863996b9e88f3577065ae7264961bcf7ad9b30a7bb2fffacb2a967 6e54b981ec7a35df60c7443e788c71bd284c056d3bf7762f1bbca561d48b8669 79aa66eafed2ef9db921d394d5bc2eb6ee99a47d2d90c31bd944d1debc8b6bb3 a37298ebfc30a50becd54c03e205a01b07bbe2ad4d0c94b0a5ba917d82b7aeea c54f925d1dea0657d5fbab7fd7d7f10dcb0458edc8c277ed5c7aac9f57ffcaef cb5d1f806c2908edf6d9daa4736db6b75c08dc9fe8f32189fd935a97eabf43e1 ad59f12b1f3f0ba96dd5dfc5843859939b8460902fa4e7a7408f04d84c85db0b 3dcaf232a02ead95ce1605e7c28a95178347350746583ae21d47523a521b1b17 eeab68496bb0782313d5df3a3b0aecf9aa1ed62f28846281f36d4f8d7498a740 605549e3590e23f4728f0e2416e632cf45743416bdef50239991e1137b8e8b4b d3dbc373232e7198d31c0707c1f87d6370edc4328ea06937fda7296d20033d30 3131c12996d3b1cd478e55fe499790146f6cefabe1cfc867a92de6150cd2e42c 048a0f8003a2301512caab49475d4f027376f8b25f9f92a17d1dfd2eb050c441 d546d5e8a2a66265db9bd7c16d43d2d7302658e21bf91bb6233d4b9647251afd c10d9dd93d5de939905c1c613a3ee05d0bf367d114c36133c59bf3d05069d645 f2016f88b68403d8b551d1bf7909f94a36b3670136045e16433780f437377c6e ff91ae488072fbdaf979bf17942704515618a2d58e3c839e0594d66e141c15fa 29debd75a62c034e9b8a4ac2a12bb9f2f24382d50595cbcfa63cb022bae4ee0df5b0bc7bbcec26e742f83771c862408276c5975eda37efbfc1688f458cf6860f3edc663410c93ce5762a35d0e5ab614bd954d2c15026ebf08a4ae3bd9166850372f8b608d0da21fc0a5d119183bf770599e199666270a001cc5f56792d837d01cfcc46036c496ad526e72e84f07e004871ffcb038076cd232915ed41fa385306b34121a6d82768dc345cf9addb91f4c6f39752369f169b1dd0aff97d97f73c0dd27be6bde47817c6e5c4fc29199e56fa679b543370ee1eeba72edc7bde71960dab9a8ba56d85a7176df9221d9203acf59f1b3c755b2a9bd24a83f6ecdc38fd055cc2ae3a44e0760d7d780edc0c7268e3d0b2c25e81493fd0658ae0484632e70dcd9f52b458ae63fa7e065b0688799f14f78ac88f85d26f0a40dd44d65e551705907539be4933381edd8b8cfd0e80cbd420ed35c883c54a740023a09b89892e022dad10744308d723e443816b170da6681189dedf7fae7d3b8b42d101e82b720796acfb054217fc9e957657c40137880fc42115b1e3efd6070c74c2d77a77aa0c75785ed5479e1865283956712d7d9ce1184a14dcc3c781d3833f49a02ae7ee037f9f7f08a547eec8fcbfc355b30a8ada435ee38544ea5efe8445a4dda659ec0c0d5f0f0dc1c0b2e855bca76ca11fd28bab9a269a642b4813c22a803d0308d30c97c4551d7e3bc3f897c2da6b9166501b2bfbbe11f5918c2a938396232dffeb000c495feaa1dffea8e9ddc01b54f67fe64ba9add54d2bea3f41d3cd1b4cb1260b290112a9afb3d99386c7e3fce8c27a0638e7138519cf7ceca7c68e08102b7801c03b37dcb67a51dbe39a3fd338c12bf46943e544cbb7dec29cbe1b2d6ee7360189edcf970de91eece1f14ce3768441fab984e67c15bbcd23619ef51b63dfdf09ed0b9d9070560a020a4907bfb7d876cd4e67772e76a426b3a120cbacf4b03804fec2e2211a0b536e623059025ba1714d59378a29b76b39b7872671e1ea52b40be862307c33e021dac98c7cf0a062f7da1c4e69508e39fc6402c9f082c60a8a0ee1be0d8f2e114fce5d8040ba601914b45f47f314cade3d241621d18e6ebcfd01f16b0da2ff07e126d9684330ddb740fe9f20b144a55e5f60a8a88bfc60b07c0c0ce4d1247d1f7305c79526206bb6b2901cd3ae3cf64ee4837678c85a095ad707aacabd87e5ba033fcb22d8732951a9a1f6028ff37c61d5cd3caa4615419a180c87cea837c3496bcc906539ad18c87529550ca362c2ee48d9018835d554172a0d991c61b63e3a55a3b550151aed141d0e001b372f5c9736f3406daaac36197506664a383a126b265415eaa701ea5842d8710a43accee76d87287fbd868462c10a898c1f181fdfb616c73fb3b4d43c465b1d7ecad673de361f99d902324dbe600226b45f980e8f47b568e3037e140249fa67022dc841795c2702976fa686fc620a4c1345bffde267625d7f75065fad803e41e7fab27699eb784672b49f6e5c240b9d8dbbfebb152e203b4d670fb26960061a14afa01264cf0f3afce0ef51420803af6e086569ae1d1e385ae4d9312a0a0fa8dc9cd688c6393f5b99416b7d2ebf08c99846aaf0d42a27cef17467880e3ee482df19e09168bb614290652190a2dd0f173d184bb59ce722d25dc8350855b25b9e3b539592691a61b616a4b2b60d8e01fcd5aafe65e93bb1c8b3ee692c946e952b1a104c64a066d969a2c28d986bf60a4cc6aaf6e35bc74af597bd5e0d538df8661c0c454fbf497fea254558b7d71c0cb884d0277b945eb3c019778a1b0bdd9bec259bfe6da22c5c2ee56ed9d8d3040aaef8c85fbf8fcfe4484ef0b900fb42995e6c90363772b94354d06186aa3b8c0b03054aa28524458aeb3bdc605a6c379ed8bda634fbd5bce6f9752dbc36a860077b49ffbd7a03cca530ddba895b748f01fc7de66f4247a16be304283422b36a0691dcf13bfb426323cee26390564ab4751935e3bf122da3e8876c9b5ed91f7c0c37b53271fbcdf50c353c6bdfae398bb80983719913d475a2c68388d11e664102bd9e67bd8976434d68933c11d98ae00ec5f8ad69349e03124380ff1cd3ada309e75b4e164ce9250f25a9273f3c00c5c96f1d05326d06af1d32e27126b370d20285381bf9f8f1c64648e454d2091c6eb544984dc5a3ebebc600ca399a9065b001321baa808b0efd5083508f57f51ae7c38594a6dbec53be9b10f2131d55be16011ee39b29c2d16669641fd3e721840fd17a59d3119aba9330708f70f9a6c28e0c31e08060f99cd72a48390e2427eb1dd44aed0ef3611021799c6b7e1b65ca060f37a3ff2380d996aaea94c86e3df1e76e71757078ba2f3cd55e0c2aced8e2a3008b164882f2c93d0468eeccca1ff72cfc209c39b1ecea93e28bbb08363ae1ed0a37c852e8ba7203fe02859ed79a8422103835ec06e40295132d5f9ac86ad67003bb28f9ef3dacf487b6ca3211c2ff453c190a631028ee13ebe2d56134ad60770cad75edacb64d11d5606fb7a526a81593c49c61a6614bcb02e7ac429ebbe0670a817e33ef40a9e7d43e6d9741515647b9a8469b4cd82a9c00b4b40bbfa7ae220161660e9e8433b1ff44f496e10d9a03754acbf7ca9d14e8292eec98d613615f0ec61b04cd6381d2226ff53c2d14c73bced14bf1e3753d7461ae1401517822540b35743f1e153029e3d19a8915457bf548f58269b5354eb0edf0237547dc2e530bc33a505540044223c1e60fcabcb28ca843b0a9ff64ba6b1d7f09bd564f1dfa049fc6a29da3cdb660f69425bb36c7685d918fd0835e6e3d4c5ca87d80be616f0d43a257be29096109abb60170928ac6e954b073b83dc8b2a0f286a9140b23d809fce95549adabf0d812a844f48c4e6087b8a641056448510d1a14750c0c812e0970e48a9fe2d71ed95b0d5f374b63b93e8c2bd4bd07773738ab858c2b680c3d0a9f71ed1d1b92734f4da9a0609a0a4135d6b8be23bae5fb270cb138fc5382e00e13a8acdd394951369d81ae9e7e07c023e7c0ea3730e339ff365faa3cdd918a0a6d90d73d28709c503ab3ff83dd5f3a9588d2856415f5621518e8c0472cc86503b2d714b0eddd688fe38c371ff1670d69b3217f237cfa5f2821d1683fa2493b024be31ad2c07f9a3c2284caa49773556cfcef1366dd70b727d6c5891ce804eb0c13979c31f3c63150be313fcb44895eed76aad8037940d57ad87f0f10bbb1ae0f1ffc2fb57b5a7415415a9c7409850212f1eedf0325e5101b4cd0a5d50d7deb0aadccbdd03480c37c17ca157cb90b3521defe9958b136254eac5553989c576d05f1293d12de89cae06f6f18c1564603ff633d7bfe8da43cbcdd649b55fcb6fb03b433c244759b61b77e21b0bfb8cc48881acddd081e2db7b864dfa3a00eef830600b61910a495a7df375b80383334f77c2ecb4286b544d5774c26e10c1636ea06ae07a4ce672ee7738b017e517e4f1029deafac9d33cc259fdd1c34a8b8f5f209c334b5a64bb1d8c07ad551c6f88d0c3ac757114105b3a8df021a0fe487939509c492826b65d125017dad207bfb5a28bc492de256bfbff0d5f67b440d22650908e8d2bda4dac79b0422b7daed3ccc246baeb7a206cf6eca83faf641937035f5014452ad532860822d5304ccdf642611c5bc502a9a674cdbff351e80f010b97e045ca3d3b0ab07fcee0de7a6fb65cacec77afd987d3d93ef9c96ca46b8fef295034992a9704617aa1abaf5401185fffebe0daeaea4e29c2d05445ac6fc6fc7ae006b030f950d5aa097a7ce9061b433d7249654c233c124eae8b8c81297fdc3ff0d1c230be0b260688c4c5bd55ef0942e6a5063ad90adbd4e61c525110e5dd3d500d1e805b3e695aded231ebafe799105913d76ec0edced6490b2e307c38b08b603e98d2a7c185b35dfd40d7ccfab8cc0c67eb6748dc7c79e0f639b45623554610bf363778bb4214b5412559d84f239854196eaba88a65e02edb69db1fa1846f809c4e47ee627d486a4e16d1912fb9c5dabaa84461bcc8ecde33407eba52d22d308e3c9da3c9887b8c33063359ddc7870796e8c177959c8101a6db637397f731d077d4178d49aa9e6d51c35fa8b17f1ed7b6581ddeb9cd1951064e6840dee8024022ce4d1eafd0fe97b7ee1931c57d910b0e8020ee93a0dbf36398f6d90b616e00f4d8202b405eaa915dc7f0c13fe5c63e855eb8e57bb469c383f2164a026e0eb098216c6dc2459487c5a4ca42d805b209e7dd34b1757c4713c8076c8b42d1064080101d6794a9d6225f5e09f80929d5ccee9e3d2650042a564b91e406fcfe5e40533675c21fe2cdc6729cbedd20b0eab5253ea3770bcb09d0bb75fbe4fcf12a207bcba52dba2a06e85c711308b6ed95bda08f02f2fcc01a0384e403e978d88c608df848f41b866a2432e9e78013ae71354aae8e2677597f13d914fccaf6e008b0c06454a645af022ba484db81df1b53c38afa1773dad27433332e68ddaf6285e00112ebb8db63159c32dd18707523e670705caa40fbdbafcb32df1251fa923490112f4d7efa6b6a2844eaad96f8be87cccfd741fa5019d1204bcc903cc5d9bf40223b15980825f7b9b65d8a82a0943762e59b6e39d5ea75181020fc0ece59e6806d2dd8d7728e4c8902122e7f4565652c9d1f5d3617eea055ba8e116f0a245f404f4f0e138e40b4df5dec9b862465ad961d2b198bc99c5ad93340db9ac7bdc64058623f5595ff2b8c2aa8259864e3f2e6a3abf4953541da7e2a453b1896fa0040693c386a37797ba4c4b70c759ddffc4d968a088abc353f508f27286a76aab53008bce0d2351fa19df60b280634055a045c3317d4b41609607d72edd2c5301af0e20baadb2952d5e930affbeff0a896706b8e3dc688e87e9176ae0fa44511350002d644f4b522aa76dc333c5a88ca7a2a0454f5ef6480ed951431b8184fa97330e7447201a08f9f5a5309cc37f824f2b88cd3eed5cdab05eee2fbfe667884c430b7f4216bb0628225cbf712042079fa1fcdce7ff1017283553a6910d2a4c032e0f9ff2cbaba9de865c9156294a108e7e765250a60e307fc0e278b7a58498c8ba0aceb863811a68aefe32b8d937110262cf1f2a43e067ba71740916c80eb8b1a708c73a1d6f49315f01441723e243c6c3def2c687559c31f962390008f3282b71004455718ec83b1b23ecc999893d07b55d4a29424ec5b767db2c04773cb4b1a70c0f98d4fad41014016db1322950362ae06638f4e75d768bab4453d2231aba7b0731d58b673d940844e6cdd3b6dd46dd1886afeb5bbdd53bcf734d0f69f110730d963c404d711eb761eebdef0745b0e9a7d98873e3adab9c9ee3e65b99623677095cbd4c510242bb42d7bd895b6260c080a007a7260c09268b9ba715ea7ff25e0923c2bf0a62814b41005006b7e92c110a09bcb6cf5ea4e4b6b270e58476a6d10f18a08b95cd30c7f6293d912d3d8efa66afb65f7be980bc06566473e35964c703f344dfff305352dd76d48e1dd493411481efb2a7e3d8051c88178296b6eac80a53f13828e8974a5912272deaf1fd370f61e9caf6f33734de31475de40e71e000c7991802c3b8cc5e39d80714a8779d1ec902f87c697573bb63f59f7877a34c0f1ba64531317d499638107b31fd55c7d8d8d802dfec3391afbe9d366daf34fb0ce2f7587595c9bcd96fc61b7866c34c963bf9e180bb924eea6a9b758d5d77c20fdc38969f35f6f1f1f111aa4f4f50611c89ace0bc8e335be26b2ef31f51509b0db99d62724387b7fccede130e1738a0a37739f60fc28b390a3f516e5bfa569d0f8837539377632143825d03d3df1a37c2c56d1c5c34d853643bfd650e2d3b0d06a9f3c139b8e4759b93ecffc865c9cbd3ae81e89bbe13d1d46ca507b3db7aad0c978876ed7c98a0e732d2430d54f23fef2213339b6fac56093bc027bbaa2bdd05bce29f9e84ed9379ad0425fa7a49b942aed233e3cc507c8cb64e2c6a37e917082ef0823a05436d27cb39e47930f080e5ab72e1357eb1c52124308af9d20ec406cef44da111b35e98bd5fbae86de1836d4b10387d4556a7e7f30ef9c544d56207a80eb95450d42d1049ae39513a984de6de4f257ba618b75d1dc9a308f78f17088985e7fea11a92932f10420b2d5b625453eaa8073851c77bb681757a0003770ef49f64d683d8adba896f9d9bd84f98f235ae538f1088b409e97cd1df80c8d50eb6329a63461852b145e0fb8d9e196d12b64bb0af445f4645d25620b46861410fc9af716fd7d5c04d05799584bedda2af8f044267de1d1a1735a23afa933e9c016c624203f40b7feeb221f9428289d1005ce9656fefc0156eb45cd81aed06610d015a3d066a91de2ddbf67a66826685b99e7814114ddc128f5473c54d0c43ed0655927af0d7bf6a22230c932e95d6a2ac2eb422d29cd1e3e20440faf9f3016f067e91b2af45e217ae3fde88ef6d59118e307e5556ecebf4d51cb8c58e69a6700e0b81e1838b4fcb6d49565b1a84b3c678f5ef60294b7b172dc5ecbe1bd935200ca49245f614300d598aba584117c5458fc830e8977fd38f285ca8294b816d490106ebbbef9e9296c2c530578bf34e97b2f4004cf624976680b77bb53f62beea02e7706accdecc88b3410f5f35b903677231c0ef81e61bc889a0dc800c5d8f0b028b1a08bdae1188e671399b3d672f8c9b91a24846c42c4addfdedf0e3d95d490278247b13f00cbe24eb00378cf17e62586a3e5f7e8472c528b1274d057f47a30975cb0a901226e6174ea8cd1d28d3ae9fabe9f1724bd3efb5182c2fc79b17370997bc4d831b25200da090a7e73d0db94cc24e0e0ef3cb47420aef7b3813243d0b90d0cd35afb6b15e688974ce9929520718c6133dee3d3380b9998798478f0803c2502dc7c2646fa212cd8d8f524760b743fc87bc22a26b9f58328602eb291f07ac0b9a046538ed100341d73859cdacb845da7c7c0e9fa6858682c8885fa9d80295e62d820fd0fd3045bfe9d053e2ea73f7ad7984b5b4bb2bbdd6fa21d65fea09c301c86835a6543444ef38ad8550d8c7bb6d98b983b276b1827245288d780c06f652695971bb97f7fb43b722aa72746ceb16299e9b68b5aec0500164526f400eb791d3f469475c0ecbe5ea8bd5c80174cdac9e7183f10e0a72c5bf632474ff0fd02b1eb74da1b47d05c5a147c7a11d57df3cedfd0bee103d4478a8f74b4adc028daefdc32bee178bccc8b65364062260f840746bb104fac4f8278eba12a2490e76164d1a95ee7ddd28d113b3b74febc3a6c15da49d500b560c429d799248080ec14fa4df0d83527f33f06a9ac82319f4b2f2ea715209a60df2a6ede0da98c80003b178cf48d8827ff16b49beb18f08e067ca8b6cf639f162933c0c34f653670fecb60a3da155f09bd3130c4e311d68f904321d0044b08b891f9a4779d84a310294b7a4e4a23c9c89f6639385b4a0ce23ed1d3dbbe7270e3b609cac8891b7c70399446869f7d6bd93a81ee706aab3a8ad594fee9b368601f10644c57aa6668c0da02e5955d1648cd6248c9a650dd1db828709496b8fbc680bd6aa1a5ae1b0fb03ea6c3bc2d95918db433ed7084ad2d39c61d1af44c128971358e8db522cea70035d45fbb1481fcb716ef3182a63a2b1f9853d33a94173914551a32f0f94d07200aa28f7704c4c5f99d19077a1b50fef643f340404d6a35668ce941e274be26e0954de052c1ee490be849a10d194b0b24f7b5eef79eb0c55cef0d6670acd2a0908057bca188ad5ae90dbce728b9f679b0e786d7fa470a66f4d06ec91771a00db08fd8be6b4af73dbd82abba5b24c0e9cfb482098d69a6c85369cd3fcbbb3182100a4cfdc2a7e856d08d0498db8c8a6e58c25e929d870172db2e5493782d521b404df7ca8f2cd69fe0ef0858211d52003b2db1d8da18db2b167064d751f22a9270d1a03cf6ce6463864bc50751cfe540879afae2949654cb9c3054f73c8519f8306abe8ad32bcc86bcbed6397e2876b68d58fc3c386384f67a22a9cb5195e8e560c13ddf68312864ace47737b0dff230daf2cd84be4039c9cf1dcccd7a33c235e0e115ffab3a5ecbc0dda800f1c0b83d8ebadbae5f024602f2a8d43da7ac65e4d0ac84fec4ae601bacd001fab5a6b4cc436dcce9a24ebd8f65cfbfb87c4efdd82059bf1fb62dee0aaf7aa08190b7f264cb41a6851b2e73e942793456d57bf90060b716cb7751a09039f095a21802e81c15b710b92e9838921617f9382197c08ca04f00e267f56feb1222e3a6ce84ae20e28d4685f62d3557e5a9b309653984a130052de47429f07effb6f290e880028b07ddfc3a1545c57f18190f7cd73444b1801c56539e4d6bdf0384d64509d65de06c86ad75265f0995580caea78c9a96e2f0d23e360e264c08254f8811c81b2d11f57b972c42e8e84353dd5dcc1ae9c0ad401405435cdc0c90c378de1f4a96be8f862d212be5ddcf333bd4b12741c00a45c0ba6efaebf6301d1c4c634f717133b55d761a49a66c1075423d6843f1e47e7a704e6d6d9cc4dcc1623b9233a0ba9df05fa97ff2acc7738e2816a71d67fd8ba9e064581a70a05f81ae9b4f3f40465b65787e3f7f7d74621db76219ce12dceddb90c52aa996785b3df92e7a47623738ada36cd66be1bf167226653aaec4cca9ce605d92e475986140b40b5416294a440096bd258c2ec5632bf9e574329b0fc183e08f4819297e1746bd01bde9e2de0a28f14ddbba72d574c0e6279ebcc34ce7f3901038f6e5cd9375d5c9def4a698bddedb6ba07d89ed9a2dce43e98c2d726966e0150e356ad33958fdaec34fcc39aef69750c05086a8a54e862b88cc8a22b0b990671356be8f7c40025c1c080cbeeae2cd8297137d110e2f796570e53fcba039d0cd6baa5af5db56cf39d466376a52e1f21ae74159bfceb634d60210ea1956b3a008fe8a1e9986154c81eac78dea670366fc8cb84ee8433464a24ba24b1414c5108dda9c7baf5dbb8571d1d1ad7a035d505b28eecb5ec7ceb0bde5f52c8c50dde0b1d2c6039e2e6b190ac7558273dbd6538e30c3a672866cee618dccd2149ec160408a02b29b5dbcecf25a1bcf3e225a09203a987bdda690e78ecd2a4bffe4969050df74c1b73b703d12df2b6e6332b8f49a0bd8759d84f2b37e4f9755b63fe380e6faa230a93a972b0cb2f428bb8c5ca144670d6bd094bea672556055e0496690c95f63e336123c721ed380d5d7578d7fe0a6b69fe5ce88912c43845be66d6df05aa13c87856911d59b95a3b803fef77cd566205152a4cec36d0053f5c51ddf704adc022fab6057ebddf589b1d71066a9a3d075e15b7b85c3501c6cb10a2799c0c591c03f691612c550330e6d6214b1c94db316212cfabc0e4859a009bd35b37073fdb382d8858eefa434aec4a9dc2d3fd7ee2b65161bb92ccf783e2ec4a34f807496b021206b9fdbd72881ab30a6784bd3bf9fa225765017e766f9e4b29aaf30ada4a8238a5783514cca3e04b3ff7da48fb64de9adcb6ddb15ef9749da3ed9404b2060ba1dc5153f28dd3106782b9a1cb66400aea78c039832b3f09ab7d0b000e888dbf7dbae3b62c0c8ae550d87670146d437a441152a74ecd3b7d3730f4fc02920335bf113293de07f00b95afa8d16021f3d5e3cac9109df0b7ada76891c9024ca2592321ceb02f1ff0c6552914483243d9cb2aa81ad8d4dcc55e1d6e84c10f4a63a86781b437503b1cc1cfced09f2fb3807d42166095e0a05df3d2a348810d4285be7b4ab5137c376046694d0868240367221ee8e8cc1c4f82e9a8e2cd140359c95da165b1a7a13d5ae2e5058b15dd44f71868a5edaff17ade244ca45deb09fefbe3dc22249cc3e480f5cd8ba6ed0d70dc599a9d75bc0e0f932533ea1c4d08ffdfb13b3ceca52ca9a4724ab0fcaf2158fe6b25179686bdb454afe7260d8d0daa979d658b6773e6aac2dcbeffca43c01a7c201febbd29a24fa3088be2c5b702b6c244aa3798a9b88e6ebfd6c72a694a3a7b07bd81da7cd07229651c10fe2e000ef66ce023d1d5a3ec37da5f51adf2ef739b8efcf9f0de831d639a55ccd4eb07a8ac7a1b31684b4e12abe18d60aa2d58621a41701a855fad54ae75111934c80ea44217e2be5f06cdf8581c811704f78a0b59fcca6d634817e319f96db2d0b70554a771ad23c4dbb58aa63e2425a0baddc4bb4897964c41664804a602647dcc0bfeb09abc2207912c5d0089539b17b112deaa31add44217428e55ea3d124e7103c55d517432caf397e366448e69fc5663a9dfa03feaab2172392974f45b8a790231a7930928bc5032b936cacdcfe73cbfcfad8ba336c1347d2015c41301995205e736ebc13df63d52d805dd8586aec86f37eaedae9731b15a74c7f46af6b8180277ac37cc4c10a39c8fba0dd5647ea16941552e0c6d555614f3d065395a936706a66e4d20da9016d06c4c0845faec68b9426cdabe0ba8b16e03afaf0de081fb05dcf1c7d39d59a3ee36f5eaf720cfda64511a4617b91b15207af69e5147b6e6004ae1768753cc3ff170ca1615d5359f278927a50c95da0ad72c04073e02a9510cf2a1a8c3c2b4858481daff5d373b5925e0d1565071802da4441c58708415390bb88424102586cfd3816eddd797ec22d02d223ac547777484ee5a697304a5520e41b695d775cab23a891c156032aa54a9550f06a87774afb2e566e1ca57b2f106842d5cb858523d8feb7950a34a1dfba3be035eb8fbcf554be1377c7f9cfbc80db57fe24891d46e05060eed9af2eb41a288f46337111c674744b777b5db6f4b09ef1bf0c643968efe6d4b40f2e33580450cd86482f8c7466d7a72bd4e0aa81208a9e5f0baa9d0439405fb5e04ab8ca4c7c8b4d6e7a86c9d7b89b41b0736a7a601558c0f69eee9050049fe215367cfd1f967562ecdef4aef8ce51120fbfe89e3098d08d087e86c82b7853024d144df46847ee1facfdcccdac6e07b5eee830e4900fa79f0c71c1cad8d5db042fd824f823ad44a2744f8d3dc8885fa790c3a89e305b0a6eb2671bc9462f4406c91dc7d23845294d49792e568e415a345c085891209ba1b7030c8e3318b6e84dc16dce3ce954f1acce14cc57cc1ac04d001e904a60581df87431bcfd3f73e9dc75242e85c4b198968422044dd02cb5004ab37bd7f0841bca9416c14527a98afc1a74ed40082183d8cf830a0b8eb5fb199dfe4df0a06623e62fb0fcf173196b8ec2dba1ba0be2bfe8a881b14a6a812237a92973c7d074294c773019f5e555b932075fa85c26c767970475cacbd1298c04f617a91be0cf558fdb8d0612e6f4657abba2b0a2735bb848487832232a9674b1c5f0694d707d62b968510f0b705e0eb76556e2338793df9fbdf2516ffd594a400caed64570252482201088dd9f18590ad183de965e25c0eeaff037ac7f86a8393921e4ffa0a1c7d2b3cac267c4a3e9f9090f26e937f19c85aea4fd105047f4bca3152781c0f8d0010c15477ef14338633ac2d82d765e1cfcb2f10140c68953143a85906450b2b169c39ffe5096ca52da36c9629d315d0af77eb6f4dea6ff99113e64486440d2a44a6287f831a785df6a4dbd4004f33377d1379030dbcb9583790cbd7ac4300aaf5754824c7773a3fc0ca7b327c57140399e9f0f294f4722764658581e8680cd76ff114e5795aa8579f389fd839736b6bfcd65e3b15137ff2840c838570ae074f69653bae347f0bcc5cefc4de248bca86b6645671861129940d808b2325be0a3797b1092a32f8818928a35be99503b0d65998958a41786f2ebd371119cb4807c6b855365b5cce0fd27f7018a0b69129239319a57f01ed1d699a3190ef5de504585c0d6423c41c1a026c02ed1d5515d56ed05cea66c706f899dde8bf1e73700955fb8ad8acf895ee934f5641ff48afa1719efbfa1c0f454c20200f3aad3937047f3f03567eea2affbe539638ee0579a041e942cbed909a27dae1f088d3561b00393190af2b327338c3635ed7c0d9b65ef6b1a07cf46cbc2438cf0a5e15d7700d191e3786fc7abe98919e4695a6125aa4db4007a5fdf3fc6d308269b533692b011e6c39230a52be080ce2955e50d20ae7429b1936250ed228d2524a789e12070f5adf39e3c5144a9f26b22d79235b30c523054c301bea01d5f0b95f530630d9048b3ea59e1654a258ea8267f0988f6ff8ce93f1fe89da506ba50a810ad51bb30b9e2b0cdfeda9dad7d51c794ac81744d6ccd4e6eb6037d18e8b5bee5f561ceb00c5139502914025aff689fa1bf0c546a659349f9ffd3685a08b831b909ee65f0fa27919a488391ab4c6dcb02c6d0128e5cbda5e8891cda6d0288502e366811b06a140b26146d385dd1224e2714b425f3500d49af2147961ef0b338c1df61ea70b2b226477059e513fb3b8588ef626bdc1a3ff8e5c8c0203546d935a7ce8f2b90e451fbbc79a3419b59ff3ecbd2928e691c05f1dc8984f0d691a06e60966cd0c00acd26b2c5bf368d114e8b1221c032855332fa675bf986483fb2900d438d9dd04df40a3fca899ab387d0c09f320834d287e2ce3c928cdf7c0c7f141531c97c50117c7226d25860b3b6df55137209f151802b912dc8e25f7de54d46f351ecc1e0542bb3e30b6eb21976d526fcce318567622e06d1f31ad0fb7c93f33a6c996310679f231c7844f2a2167e469838b168b750a1c3bdeeb95073757fec287fea88c0c00da996c87494b4b0826e47cfe584f308d46de574ac51c2212b472f0a695f506a7b601804ba06fa1ae92cd7481ed24df2ea01cc279e73ab360f13f559ae1660ff27dfeb82999f3cb9cd977b11a0238ed3ca7d23d7af597582471f2b6ac6f5906523f24593e24a135eaec6672339584e8c86030d2a938fb0a6f6ca80ae45be70e2255efcb7aef4da75680b83fc7c4ba4f5a04eda839193f04a2ce0f660820cc05b3a3a4d32e491b3c7216c7c1478fc265f98082936c39555b83dae2dd4b32f8014fcee9cf1e72b7ac11773b58c787db8ee042a1dbe5f9bb4e5d992a715a987100cd7a72d16d2bb906e6d18997cc7e5ae9b137aa39ad51f953ff57ad386453870dc5d9f14d0a6f0003413f1d300ebbc6dfebef99da41b7e60e415f52da2582c907e7f3ec863e0afe1ad51ea0974cf66166dee932e9950a6b410f49713c90e3d00e422b46cb8e78122916a9ba6e03c0af1e87124dec538a1d1564453091162fad04 true -check_ring_signature 71bf293a1925fd492e5ff951c887728cb3d07a36e61254d57efa552ecb2418e6 930abaf4dbec1c734e95cd715a777e57a958592392789fb006b8b215e78cd1df 13 439ce137dec56e8d700dd49cd1a9e85cc7c58b06700cc3bf5e42ec31200b5baf a8590d1ad54f058820e11ac8ea4e59d97f1f5f4d5d3eb768ecc683181a603145 4178c5b63e955bda25404c40f05211c0ea80cd1b0d285589d2cbc590e1534e06 9c3011c5be6a6759337be0a625bd8780f3833b248d9ee3f5a3812e35944cdaae b049ebc63a40e8db88be3d63242da515f980e2d6cd5049fe60b3395d2087ad37 828cfe56ef3ecf540135c736d1c65c42b0bc943bc341ad56342018a6fca64888 85cc8c9196e11d5f5ed60e7ec4bc6be3ae8d4b239621a0ad177ab8ecb2943456 dfd093f5f50c3738e03a2e98c48ce7362bab55fa8eccb654012cb0c5ca99b267 32495961c0613d25fbb456b0046d0f0d866d8e7008fb368b1ffa1a007ca38056 529ab2cbf91b1a9d62b785a29d405234634a8cb31ac20e935468542f3cfe12cc 0f8faeeb08db9a9ed516c99b63a2a946f0e6d289f1bd4491f5087b1df1fc45cb 7087a48aa13354a9c87160791259627af885c7f3f0e4e38a43480409c855c8ed aa63f5fe66ebf8c38a186a153d1ebec9f5d3a2f577f42f3e88f9a5013d7aa989 1ad6a2636a3d9fdb7c3e121f3e925bb8d6d0fe888a8be7bc6a7d5ef0d68808005f91f4d03dfcfc5cba20bfaf3dcdaae11f55865ba30f129262dc58cb0870d1029d5a2ffcc156042db1ce1f8e9a4d889803fef00412d92136df30143f65d16b01c75deaae3e859f0af5b0cd2152cb8a0aaba049148322f62dc6694f7d31632306ae4086b3842c5204ff07761ea19f81015edf7fd88c5fc06dcd2caa345f1b9c00be110ca67838a9825ff69460811ef1fc7603493db3988ded0660748c872cac53c27493301b2336d8bd7d1139b747021d9749a9dc347d82990d2621cb24eaaf0909b6d543f29ce101c6de9c8b23b2b914a927429d525eeb6d267251ea594285062d5894133d516706ffd7afc4f624edc051a5bf4e7fbe8c1f680b2d2e22f0b60c96e78cfcdb293eb9324c9d5d696f80929d0bfbaf05cd6059c7d75e6a6ff2920967cc5470d09ee51be419e945e3c80c380d98e586fd8d5b24755b674339f9452d6b10fd1aa18187a3da0ce54e42dad26233a870578a35996e9aadc8a3562d8608a6c9c99be62a045dd6ce3244cc0dff9dbf110ae912a8f4d2b2c8bfe1c7ee4d0e26384f2a86f624ec1823feb59ec50f9d383fb6321dca1bed1fe32f280e9768028e93aaa96b5f2451c92448c128ebc71a2c04f7a60361a0de2d8d0f95eb0c380cba8376bccee6957652c32123fcd5528082c22b21ca54045b32e8eb2f8b64bb0c49c854e58f2cea4698c23268fc108cfcb3043959b8e2e638a85fcf3031662203594f32d87b20fc97b521614bdc7f2c74a5f7fc065db3e6ab6687763755330d07141e4a081fc7b378c3fdba152185b68219d09b3a3a58fd6971fbd15defca5b0a472d339cdf62089c36ad445021bbd2781e12f90462460f5b725397655fa02707690b42d3c0c76823e87a53ae01c4e32791b2d8199c5210cad00d1fa3bdd2620673b0f3ea4d7a1b8597fcae6b235e5070e81a030cebda195232a44e276d939502031600506053cbddadb44eb3597a38da235c76da0f3b855f3040341b8135470ed89cccac908f51116e5a8ec2832cf10bff86d2b2caa31cdd850477265caa91087403fc371c37639ce1a823db3785b3864b6dd24ee2be8468ea28b44e3fc86c06a5de803bafbe4644569249981b6f653f7482f460d22406588c8377d3e04c3e02 false -check_ring_signature b4c821980b30fad9ecbc63eb168121e97b1ea91cf65bf0a506139fabae5f0c5f 6725c06ce8890fa6f1c0e1d5b7a84274b8293ba47435c274ab00faa57f196387 12 20fc5a0dfc5b9e91333cadf13650211516be9a46f0e519229ca0d97c4bd1c7db 62e090a5a981a524234d3237405fb7ce16324a4011985d54c7d1980a44b94aa6 91ad4c9d120751965f8e8a019e2ec51d51da158ef10c6e6a5a301e4d3eed9711 3cbc866fc6e42a4cc06a598f254666fed09a6c502d93e6273a59ebd18084415a cd78de1d2a2adb29277529aba6f17dba036a2214c5e182d719a11643653e4d48 d417d1fb64440855a02e52be524667c70984876de3f534b3ca1720542ba11f3c 36c2944a4986779d15be326a3605bc44a75ebfba1efcd8bc6b752e536bee2183 08b75a5c54fd1dc9262221bf3fe59cdd8861e988ba55146b1175bf271a165116 ccc52226528282959124514e494c2fb68210683af786515860800dfecdc6efab 07586d009c96911abf3b031c5f517a6d9d78b15e7a0835c85de2f76a4b441b7b c360766466f150fbbdbd41bb1f60e193602ea3effad660d53129222ec3667cb2 e6bcea713bcfa155935a1c9b033d9c7666c5cad0b601aeb043f812a417a068ff 05fb171d324b9fe483feff7b9bf04dfbc5fe72309cb038190fe1010f19b41b07a514149581ce9ac8fd96f474ae9f74f9a4a67ef4ae01e8bbc762502548986e0f6f56c0335c4484608227caf3cb5ca282694967c3eaf6713d1ade4420edd4a5008f35570fd1cde55c431138eaba1eaab4ef0ae43729b90ae16e1061d97bc8970e881f36a3a94bc867c90d61026a8b7bae8c80c546fca4e54a07152400428c8107cbd7fe2246c563fcf836880dd5aa544d850647ec810d92f5814ac2e40044330f31f1f4ca6b726fa21665181c3793b0df6578c221e76ca9fcd206bcdb992bb70b94c3b2cd9be3eaaa57289234ac205f27f015154da810f707590f2af8a99f4707e54bc208bb29fb26d39240a9860b87bdb6e38b5d517e30cd620709e69f75a10fd22a45c7a0b0b3fc5c97437e2f80712da7375e65bc522266ad79c12e9bf16100aebfb51a5e328c84377ef750cdca15b01ca2e8e79f549393b26dbe023c5a3b08d5e9c83062aae8ebe5de2a917bf5e82bb51121d171713e371ffd288659b30905c81631e98b123eb7e1c07c15eb22004ca803f8d571fd621b3b6636c494ca2e0fc85236559921597ffe7e7eaf1b045f0009dfa4d2157263847ebaf8d572a0340c98bbd1fc11b9cf44151538f2de5ed09374e89e45793f4be613dd95c8ffc2540a76f1fe76393b09cb5f5c8d79c87684eff5f326a730d285adeb8847ae19240909e620540a12b9f6f30830aeda50ffdef860d9a83dbcb12c21bd213cb31eeb550e2d75ad2abaea50fc408c524b367bf4cb175a179271233588c11f4d9863b45e04fca1a579c33b0989e8003a9f9618896a27ff8454c1a05f3dfe215ecaf6d0e70f8ef31dbc2baa223936303d8c80c0b8c87de19e48ad3891a5f549006db438190e1d60edaaf141c613af3815b908fb5abe1bfe40f5e48b126750cfb80611f8d60330e485f2b8719cc16acdf0e592cea0b11e920f3ddee40fce0c6a75901ebb300f2209194a6e26ee64f63bdbb325ecdae60de54d676fa8ec94241e27e1f5aa8d0f9a58e46274058b415bf2bcbcad51cb9f2597f7c7feca024fd2e35a6b66aa350a false -check_ring_signature 505b67ff1b217da99325867e0b3b7750d16c52a055864cd2b70006171bf2814f c37d67c39b90dd6a5514ff4fce93caa98255693e6afbc715d98b9aabae4d2eaf 26 0bda1c54657c9ffc701a0993984094625ea74c984e322702fa1753d009437a3a 1df9c0daf7a30f45bf3ad0f8e0a222adeaa5700e1e4b439a4f9f33bbb3c39954 408f86c99b501be50c2324f5fecf0cabe7db755f7afca1dbd470a73754e6cd41 99cead7359d7911b9937b387a594887daf47bd76c75136334802c0089413ced6 a35226d2501d3e7c7271b95e03e0f80fd1b0009c466e97729bf316352bad0308 cc84ff4f2d05316908d2236fb8d83aef6c66217b6c04ecc55d4e5b3781b3cca0 98f42f07f45389729dc516320af1c776772e1aab23be726b0993c52140ed49f2 563c774bd9a10a4772ab8c37c1889d4b40509b46f8ad9351771b24cdb8805e2c a1c905212c379c5e30f24292f135aff4149ebb19dd229341729d35c4d5d2a858 c24f1d181fcdd358c1e9c87d0e489fb88c6ede53a3fdf99f539857983ed37ed8 b252481308b27721e61500d5e04ae72f2e8f4042032ffd0fdfe631e7b47e83ec 54e49d1a3617de291d0303f2442979e8e90b772cf430236d188b1e1d574cff25 2402da91f3a45476466567c8da11a08c8c58362013f9b4be941bc60fc94b8037 adc19b4b12e84a4519561be76a8a6c78b4bf81eaa3b2270263d19872350c6875 e5afff272c55e93a8dd3d66274f27314e77d0b74fe3dfe024bd721a92c3223b8 82f525ff2526d20e547f050af0cb92c9d43fee10b58c9f441b9b963027c402b1 cc26e8c249affc52f3c7cd1b30abcc1adba173f4b83e6138c726465597909eb6 3331681a599ac4bed6e7a792f4658eacf811f178dd13e853df10ccce10220633 9554b886ac0d57c271397c7baa1cbd908802ada3f52d78d832d7d81b147b9ac6 eee8c363d7e9796af2566b6fac42798d1456ce443875bc4d49bfb5d205853a77 f74237b976601d5c0568ccdb36818d72ea6847fb6456e069299044a97f7c23ee b3d59d7485611d16660d18a62b771086f831491c085669415c246c41411175a6 bca4e6ee8ace2f09a2517fcaf05be46ed93722a182612c692fa09f2e93ced4a3 5c52ed41f3d9301b2ba58ef44e19519b40f3a2d08501113d0031a0123577dd23 fd214d69ccfd7f3171d9fd2a500ea8f64ff8eab6bf2986705305b9b1fa310618 5cdc2e370749d96a7d82f368fa02e2a2d07ab3a2694be6c17e04e998075f574f d9b938dcf429b78d8641b6b1cd9229f52e8842eb2d408d6fbc840dd0d85bc002d42bac4b2d076bec8f2681fb10a02b0c08acacc46123b6cf37ab7ea965723705def28315505b0ce2d45b66e90146a53810be18003a3377adf2359fb3da931b0c2e8112ce2743ff1141e26f71e0be5bfd15a97cb41cf09cab301ca18f77dbb804288307a97bfc608058fc6a67169fa6f661a71984a9916b5704e8396a955f4a0ece0533638ec83520b0f637758a9c785b43e29009f55c7bdf8c0c3b9dee721a0167ff7b630fe88ec10b18afd7e0569b8ddec08be8d942cac7e2241e6aa2b79b0589275cebaa8bdf1370ed5d91fa86accc50d1636e596f2d013648da4943e73936b8f3e6c8a21e48cb707854ca340ba9cc899a9fc6246c6a2db74d3df6bc05f80e791ecc9265a5fd39915b758660d41d4f41317fb6b2eb5552dbfcdd9b9d87a30501e5989871c84181925b85db0dcf3b172646ae31ce4ce4164c31df42277ecb08b37e0e3889f22db8ddc1f40c410f94a41ed297f20fcceffa559605da6927c40c469bdc2c757f04cbf1225e5fbcff8fc2864abf2fa037613e382c5ce4bf90fd07cc6ce0d4d85dc6f5b4366b53e53c087b9aea9d96a2530b893a9b6621cf53a401ee71dc736c57e4a96a8c1d560c1a030ee79b21a785ea02c7f7c386078155ea0aa6b9584b9f221290af72094ef30887dd33770c3d35575461372c687361f06b0e37f6e5d365cf672ea3fc23ed8a668c5a106b0254952c401d3e8a1092d93cb80ba27dca78012c9da3905b19ab30a20fa9ed9d0666732e2ab940fc863aa84639095e4d51ba2d287d4d7bab7edb7504d86f6fb1ba1b6e437ea329c2a8f9725f4f027e3423781abe03680b0b5663b570552593984874e4f8552c5572879939272404ebb563a312b064cc40eca36aa20da53699519100a1d07f0d94d03559bb80b70ffa80fc0410d4d948fac1da9521769116c112d757a6ea9d2b929a2d102bb7db01404eb8b1d7218098dd1c61373d68f5a5b40235258c806131668e1f3723b53f0f48c809c2fe6dda4ad01aaa6285a70e28cd7e48a39b8dccb381a1c47a959d0209f037a54fe8a10cd0b92349c629921fd7bbd4f3e1529f92b86eedd14b64543e0129900deb1c4f7bc96a92f66f0509c54d9b06450942c2081e3a0a802f000bba0bcf87a6a002eb91d323a8a275ae550550d2d414a66da06330e660af74a88eeb06039fbda467a06c294f5235a46d2cb891e9f48cd6e1bd5aadb36e6732333cf008e9c3081b3f88bf1eb0a8a1545fa4b661d709639f94ab5a155a54ee33d8035d0a3a5e4a5f77ac670504f20ef06babe5b0ff43c81c6f87f4db93760698e2d2c50d38058b2c6d7c108c4b774ee113c654e13e82db140bba5793e43bce7b6e95dd04302844beaabb002fcbc379b1841f8c86ab7431b30d647cda619f8977b39e630926201bf472cf9cc43576eb52b3362b35cd523f1959279aa01750502fbe41ac01b76b500264cc63942a191c325959a625d617c6905abb201d9c6ff7af6a7e410f0a9d0cc8c0a6373b0e6ee25ada42e934d667afa671b6827773fb700ace292206ffe4e089f4336abe5514423885e1e8d5df1fe8d9108f6f3ae94705f309dd8c4a5697a285f0616632a9e8be77247545fee34c991687279954eea95c3fae46790962894635a38672254b79aa29e8b6dac4e2d59bbe0216bf2bd767c146d6f9330db53dbc4c22e240c15472c3e79be791f2bceb40adfd10706ade6ffc31ce98db085d90b5064201f7aeb15f2372471c3355fe77e70a70934bd292d6be7afaabfc0b8f3f8fd301c40b33f23ac99c79efaae5062def01b93eed1337c188fe7aec410ccc9112b8754df5acf1254afdc3f6dc318ed907630cc54bc89fe0acaabbfddc0d2d472962febb0f4747d03cbe9653080ea803e499ce7dbc12d8439a7eb3c36a0e7ce4029d634e426af90d90576e4faca20122a73f0c5af79b0a961bbc34ab590b684a7f0522c34882800acc73f92b013372d19148839c3b7e222937d6ad58850b493bc4dbd96ee3f899f45ba5303174ee0cf1ad6bc7defc6e9bcb7c5e5a69000203a07b8c4da24b0abc649df1c8265a05bd1b217b7261f8e5706b7e4a434fb409494526237fc22cd2a66e7bcc891ca2550ac3bca3cb126ecaf87e4fc7c87f47f985336a49923d862c6c7d5200b85bd928904eab2de558950ea98178a3bc8e3303761f56eff6fccd4a9ec13db13d6cf68a411b7b5208689c750805be2100a6d30f2fa03b05d665799a572442ee079f00f544b508e58a57f888202df8490efb1200e49d2efd1146c4d0de82ce473d76cd878a6cc50409e87476f967498deabdb606 false -check_ring_signature 74f8255d09c0b608955b14992072cdc629248bbd73b5a6c8bec88a53052f2d5d 2f77f83f8450b4936d5e656203e45395ebc552844b5908215c35d414e095c595 15 2e618cb74fe4da196aa60e6730f59efcad6b693e3bdadbc8a612e341e6c7c18a 162c424559679d94a85de220e82a5ae86cd6ee230bc076f846759b3b0ccd7245 1a270f4fbf472df33073b5c54a0547811386ff5d214b286bf1bc728b5cf82481 5bdf6cdb82a443d06fca339e0153770b06c1242d29b9ce6602f2dd842e400fff a2656154976d5da7e1b3b3a89a5db7f455c305c1ef4a30c76d2c9e1837ef1035 2d3df66b55dd7cd343609109a946e1affaa6e8863bff250128610586017b865d b6f74c15cc7b97800e882fba4605147f6095f4ae0a5b195783d1e673140b688c ba2ed386bf63158be1a76ad15bfd9ab58ed41496784fae26531f5251729c9456 1997c70b8b36d6264e24ffe385608068e93befe6d78ca86586dc8b5b68910ea0 517590af1716a7872d59a2a21c3ad7396834c68d28d2987fc5a60b962be8b78e 6b063693fe9a93802d75d3bf2261207665c2f4de0c2e378cbb06d8a94e2a1bc8 477b0ea7ab77df330b89592ab974a9758a2b5922e7b78be53901821b00b390e0 286b0feb37df8d8631db5694572477af9ba401588fcc0aa572612056937f95d0 824fe7a8ccee94dab34dd2d68dacbdef721162bec976c7cc2f0dac319998edf8 3794f9754f1bb7ad09252b3562ab9ede8c2201d4e86db50cb803002ef070964e addf0d57794709ca3a0dcb02d06024a4761e6ef3fd9c1fa39b756bc0cabc5f090f858a71ec098fb1898fa6e77946d1c2774c842feda570ba4e41365208a25709b0450484d8cd0504a61ebfdccbd99f86c5cadf834c04ed3bcaf1c416aa537a0db55a2ea3954f9eff25ad2c5c534e9ce24aa030d28bc10dec9723ce890a3ecf07193351c8e05562fe2c247e3ec8c07db18d11b8e4ac97ed7ba961d279f41ee503171c43a6c1e85c5c17b56cbbe8bb7b6484082d850537ea761b0062966d98260bde15214bc5a73a121a0f334d78a333bc467c738646100132cb3b9badb454b70fcffaf7613b8eff2e6db5c5165d064c2e4a375ff8c473dae8208697f6dc22790968d4dc703eee2c039a95aac96eff91dd10c3b4b540dc9edd1a237895cc7dfd0cb069d33c7c59863c6f5ade87a8b15ab91387d7da4349f86a077c44914ba97a085957e3155a23a398a7f5c4469ed8932ceae5d972d3046d2ea94b98dc5f04f40a470aaeba3b1b321a7a181e1f0e2b74531c6509c10fe17c478c3f1b631d6ef6000907443ae618ce39c0279a19aa0d1cbc7159a0cf07a91a1a55a5162cc663990ee392f093a1bf65a22500b298c5aad2ea2cccfbe00fcdef30396a720516ddac05047f258115be471a55bddfe7a4b74ffdd8e7e911863220f8c06ebfafabfb880cb8c40439560aaea5641b5178d9c42996017b600188322d66028655e6e40521011abaf7b66f8da95c58fa66c79b5390686067f2e0af1438cbb24f704184958f0173762a729b735b61c985da48377498894724c2c6b36267d8030fcb8032d0870955eb9ad275961862f0fb38c5527a9f8b2bb4fb88b24188e28ebff1becfae320868dda7c73a7f10d649400600c009ee0e9375e88895b6b37a35650babaea5ace29dd78e8dcd4b684a79820358b4f8637ea254d7d407187c216d3eb150ef013e044b34db2ea6ce56022abee7d0cc5869fa7d7f13b82f005aa10ddb840fc4ea19027addfef74a6a26e743250708f7753df101361d8bcd67b51fcd3829d6eb70260ce033658ab165e65b05581752cd510515f317222d7b52135ef54a474dcb0911097d29801a9b11d1eeff5fe74077e854243dd23a1a5b8ca7c100e23fe645a0e90e875d9a464ed080f5f831ea8de3bcabe417f33cc8e9ff0f88f2cc7b8a543d370b47aa0023d1dd3fe1022b096199f09953a619c8f3a6e0f2da1e51a99dfcde7507c59fd14b390d511119683e7598e61c03ffcc2a9d4ad2e561f769b5e4e457a5031832a47f72fc3180416df697fe40fd49519d36d2da5552a5a65e8102d89a98051ba053f2935b2efd33edd28ce0d5609e0b725a4a88e2a8c782f272ff39d1ac03 false -check_ring_signature 743b9016959ebdf4af8ab42458a97c82f44e0ad97f010b81fa36dde8217045f1 cd52c1c5c83bc2e5cb014f20b9d7e4af6b2a245f76a56c71a5ecbd7ef047010f 2 60296fdaa9a4fdd6249437eb9a8436748fff00fe8a49d5184f9302ce5e0cccb3 08998cde36988a096def6b2467fd71fca707c55b479a3e8670590df0d850ae37 e2f279cb5cfd645bcde3b6c70abe1a300fc886548047d4ef83b212adaeb18f22e5681546c1c1603820d164e59c8e7ab6b1a9e18ad42db12475bd4c15ab153d0dc7fd513c3bbc9503df4bdbcd1475230ac2f9590a07795e75b126931879c4d6098f8c1a53b271976e4ab4c7c8991006b5c12db0661a8f4faa45b565ecbf75890e false -check_ring_signature beb900f7d9dc330a16629489750320c55ecdf146c2757e5693c335f7bbfd16e4 63703a55175b840a25ee53e57487ed871feddf87798aa0e04e3a4311166910f6 138 b32cf373c744562a57843c76f2e2759fd3694c7ff45c1cf9b2405e4807facd04 4bc85d36804206fd0359c476ead568f0d97ff8e868e47f7e797d7b5d834a3836 4a532f69ad830b92348c04dcddb480f48bc7886322635b0a8849a6c49a344fd2 5c0baa875c057c339a1b48a98ea296bd781620763833a710289cb2bb4411e696 4562960b17c3e4f74616813dce901ccf8af34cff81b26df64f28457594bf1d5e 8832f7282f88caa952d59444402ba3712ca3dfe539bccaccb8de9c02853857c0 de58a64de59078fe512b88818c559ba003722d9fc99d2646cfa6660c34712bc7 f304fc4e968fbf6eb3f59dcad03ac66a3574253eeb5707e8514d7329bd0eac79 3f09db26d785bb1bfd9bc49499699883bf67bfdf82357de40595b61b9729a02a 0674492c6183de9af6bcdef367986c49bc60d5be3006b16fb7dc11b2791335ef d2b5618f0ffb0d1b73ba49309b55898d8726907a5871ed9537b285d4ee8a74d7 aae3a0200e49ddd82016ab4b10fecb8b0e994f49e8a6322e9f1b473b4864001b 8d5edda975ceb3a11aa5f43465b2c90f555e52c3d811b7459a9d8996eb4997b5 cc7c858d652a4f6f9e0ef7612caea21903f348f9f2cc6220038397e6f7d8ed52 01c73e97c0a6715cd2913dce9c7f7fa767d2f087851f1a807e42e85b82a65404 30ac5b3a5ed924a2ddfa07e56cfca5e14c85f842236008bd205993c74e3b7e73 e9e6bd082d9504f349ef329fbea1c5318e36f396a6ddf5c3f3ff3f4435f6e71f 04ddda42e84a800b4f709b5a29accc5012ba8a2b73d7c8645b2680a4d827944f 62c4ddfe89c0a061ce7fe6fd31ecc11843131705c622aefbb47fd27ee9be1b58 c11466c94f30b6a8bad0ad934740c00c725e537b1550913806d1149a99ff09dd 753790343e584794f0fb3ef86da5bb9268bdce82e8dc1e84f496ea4d2329abd8 d775d46dd3e449aef42d1ca18af45bf22397c458b2e80e4375bf31e1008138ae 0e0615ef7aa40260d00a4b03db65a839d9bac476b9e98703ef655014914e20a2 43aad12b5a56cf95fcc3f2093412a54acb9198e22b619147000b90ad53f4dcc6 fcf4fb4e00a089a3dae1c7e033564d4888e9730ea01ee461334e4128e39ffb95 6555191f8f8319934fd5fd7af505026c06b4a24fbb02494ab436b6ea76728db7 df8ef21270fae21f2162515a6dfc4ff21968daf0663182b3469318b24698f958 4f84748c37150951491565da3976d54fc4cee7ed3f5c1c88a3cb2a3e9c1c2bb2 c25092075e66da1b8334a669fa45e0d957ddb758a56963b5a2cf2549f333f3c3 fd489729b14ef0e3260fe67e25754bfc557b4e6199c9688c914a5932b233023d 14b07eee705fe561b5b5709697ec8a0acbfc75abe28f6d7741ccf4ca7bde3d23 e9f3e3d3d6b36173fdae6f450116ac1a820f9d062f8c6d470af4983568f8aaa1 513bd5ab15d27f46211432efbdefa46e373f3bac23ba30eb6c06560286d174cc ef6b486e9649668157d374fb3a0c0487755976af87105f5b4bd6306eb75db156 fc53d610e6d3998fbe120bb23aa6c999ab43913a3eefec5b8a632d430e50e1c2 59a1614c8cb7277db9b05efcb572b974ad72d909776b13cfc39a73a2776b20bf 19b40882d9d781e38d523e1ccdbc7b4a92ce82073ed457dc5b60d604cff116a8 f67c4a348065c57773ae525fd72696440d2efbb1ad38b7b4e61d34cd1742dab6 01c1578ba92a0f68d6ae349b544665371f53bf94bcbd4d1e8fbb6eb716c81f15 2f8346d07747f8271b501b40ec7211988726c38215da822e30d48f3f738f99c0 0073dac19ae2fd04d55f3f5b04677633fa01acd999d4ce146e62f80cf0c0d37b 9f01e6aebe6f414c3ff58ddc2deebcfbdd2816b93404488c9802f9caa9253625 bcc561de4c76c5a272f0efc1f9ba0c2c2381f1b22b775ef5afd33dc0805ca539 d6dd033b75a85ef1c0d5d5aa8305f39e191b4df3d03dab8a4c545c7999990c78 b82b6bfa0598cb8b7e1d4ce780a8bbefd017b29698cddffe24ba7d74a9d520a8 7e80c8e468f95bcad72d6c94b394f4fea4a52653bba795bafa0d8c748221c337 b04bb8907a00565f12ded44b124e902334d68012988414b86cb2b2de16e9d7c7 70e06774d302cd44c9c606353d63fa2f5dd09a111e34e4933a15e3c07057a889 998f17b52606a7ab70fca825dedb3ecc7e5d263172a8512dd2d28a36868750bd 229611ac9d00eb2db19a80f87d8ab77b2d61d010399490a6084c7803fa8debef c082218046715256c771b7e3f6ab93fa5c2cd0b174d581d55efd1db5fd574a12 b39cb069fce9f0d2a6153bfc95118394e216e7c5f2c45b46fcbef9b2bb2b0670 f7b2efe41790c399803acb42275ac36febd7aadf6dcaec27cfb794bb2159328c 4c74bfa98dd2d0615bf68ecbe0f6392da7296b263a5bcb04b880d411c3592a62 a27379bd15a412432a588c71130df553756a8e9d1d7dee7eacd59318cb0b809e bc6f9410a5dd08cf8db6c7d1c51af321cd5926b5192cbe4036f9efc7a57cc079 5b93dce76d204a5f1f18e6fbf3a163c2da0ee81314178f0b36e7c498b2b9f0b5 256e8dfd95b52944b88ba930e657c2e7ecb5865d0a6c9054dc31a2c057253aef a4a21eabcaa66988eaf4e395a148bd3b6b011779b9e9afd2a5b6513befa28b92 dfd0a7ec68539b3dc6aac3c0f18fb9a773c1cc59ab14b54bffed0127c98630f3 5d6de4890f897f66e3f4572793ac3019fcd68c2c30fcae04ad675fd2d0a5b319 b9cec173e1d6f1eca524fa616daa277c04d171c153ecddcfdd77e292e61a23f2 9bc7223765b76dcbbe67967bba74819158ceafbe04f73e801a39e970399008d2 c23e9eec53bf579290513f240116bc25688b69e255c25b1b7ace02698925223c 707bb04f60ac8077085d010ebdce12527b82640831210fedd419bdc566026e12 efe340d43b68a7b23ab1c98dc3250a1be5fb0517d80d8f939a08ffe464911368 ba93985ac32316dfd73f317a1232aed3ef13a3bec888f44bf9abbd142ce7b572 97a22dfefe8d89032b8e9f881b459274c5941b8f21bdf639c9fbfe548e354ad3 28d857c07f039a2be78394873394e014345d4cbd57c861697eb8c7ecc0d79a5e 848120daceed340cac84278e54fa1b68b1458ad15391ca89ecd3a6c3a48af52a 6f1763513a2af5a784dbb53e0694b7d5db384327aa5ad26548db189134c78fbc 4199726964d11d3246543e661f30cc523167abcc6a79d9c643299c1f9bac3800 7e5f769ac9741d5de03f39b0ae88dd1d5d2c4f886e3cd68cacda955410295ffa 492ef7c5a5b5dfea306cc2714eb1c987c7b2f509904051566aeaf12ff62e2fbb a4e39d2ac7912eded77881ccc7bc88a6e27d22e0e77e0239d8a7807e1c22fe5e 8d5ad83f61916d4d34f16b0863474dc35a547dcb65f99f59cc6fcaf256977ff7 7ca41d2ccac5df184f0d79a17f65281b7826d38734b83179a7caa3ebb69064a1 4ae82af13acdb9c8734434ff75dfa68a7506b7ad701dcc5e53b80cb4a97a9f92 2f4f90e0ad5e14056ce001b51b88d6a8153f0a69851115ab0fa209afa6508ae3 0414b304dc67a67c9986b91b4cc37e02aea62da8c3cae214f57e2fe11cb5762d c2a6f00276d0cd0727a4c698ea6ce4bed6125154caa03370fbf8dd81c0c9e502 9eda01724a4a609bac0b26745dc1e78088531ca2c2fd42d76b38e30d8dd13c89 34211d76917176dbb849e47d04978a551c829c53faa1e08667d46b6024338f3b fa2dc37934df30ff297556917121bc60827d03f650b49537f31e0be8b507b759 30ff77cb476405ca882f1ca9a97e9d4f734d6994846348a49d3b4e1056575084 24e05a32326b0567095e7c6b6a85cf39e0f01d5cb096404d4b96d247e99881b4 49db7601f50ef5a34f5f90919a51647b3c9a64111f5cbf9c570baa89564b8350 cdd7353cf6d28ed2cd8ecb6e6f5864d31ad9c7652b8af2bb4213d049cdfa2176 196e68b59df935003e971ca098f0dac7c82ebfe29d6f1c45dbd2edcacab07a48 3af7aeed25ab06b9fea96b9a68683ca1742477f373bb2b808c672f3c5dc89e05 3a9a4009da2f7cf314f0428d3cc73d9adbc29ecb3858a977fe200f73de927a9b 1854f570426399929abbbe8a3078dc405d78dc508682ec5ba3172e4afc203964 5706894b72837422a7ccc3ac07928ebc17fd20ec1bf5596bcfea5be54c60db8d aad7b60951787ec1af3bcf95b02458ce4088d1e5ec1b7b937fae3812b4ba3a00 19c21bb590a224025c914da3adadca50811d31a6411d73da4d0fc2d4eb69c2e4 9aede9399b69c568821d06c7c940f0cad042662af8c5b86323dfc059e81906ee a434e9b7f2fdbe8e33dbda5490ed39003276145993fad43821756f0a2b456491 87a5e3b089225a1206fa61dd51f8bc8d6dff41610d4a3951c83bb1a15b9b5ef5 ce11692958e2fd2fbaea60bf16c4c4aa6be298080dbcf13f572dbab947eea250 e529bef50fc9e4a666e1bbd5e64edd540ac60a63b914a5293c7d0a435f9a7e35 a617922c9b97ca3c467352d97c328edf8eb54d07284d1ab63a51323fee8671f7 d4991f689d67895c7782662eb3cc21913eec7cb8a8c69e45ed5948e580fc91d5 34e3dbe12e21bf70f27aaba99c8030ab04fbda118eacaab99be2398c6f227255 f7ec4cdddaa387b3e091beabff9091fa9b63e618c6fed642b7e5c425c6e0d464 0e48f97a3862685565f72df955e6476724c79b97880204a4726421855c72cdc0 682fbdd969a6c30a7f83de217a9557701b94d81b7a0ed34da9e3c421dda4564d 3702d667768af1f222717c5c00f381d2508f9e4fde76ecc2581bfba145d5ae74 b7a90ab681987ad8c52f35fedab7ef1f72ad3e760fd3b8b0e5d932bdaf505c55 595377b462fc6297e81c6399192d78e4eb5c255f3fb14ca1cfef8801d91e91dc a8b55a83bc84561a047ee2e521f35b628b91f2ed9014b4226d0628f4d158ab57 85f2238273249ad4d7c2b91e999a286b0dbc5ad92b24c1ef6eb23c27e446d465 aa44bcfde9c423fc8aa84c2c31728e4956d810f1e673515aee9569df0442212a fdf8eb966e2ed4039b84afa6a7d138f715f1c916528ea0ed0ef900c1af87c63c b1d4c013b6d7176262fb6b8dc6c9ea119ddb3203ffb521b363e912cd932f1256 d1492710c252f218d791f3e0655c630877fad4c53670df03c95fa3eda529b3d6 fc58284a50a91a212ac1f230b0b6f0527732e8ab2070f588d3b6ef38bd5d5393 e0c220e93aa95199177b714d5ebcb299aa0266ca31ebee655fda50716db11c03 845a897faa350563815bfb47e81da4024148d133669ae5514036cb5a308e7833 b1b37add12e341ecf3e7a69bf4fc249bc80b7f46e17318550abeeaa97d524cd6 c91fb1939777283eddbb9e1a20e54371b7712b12908a9162f9fcbf8130ea0a23 23daa38b5ba26908155f8ca01c1fb59f43fb2c6c5b87c8ad64b3589551cd0dc3 3c49e7458f1db6ae793444d23acf6f2a3479c091a7dbc0276587b2064b14b17b 989a0f534d605bc1b7fabe25098dbf717f6624da175dd49317cfe0842272d7b9 3f9c826058f0cd52e702b21e5321a780e235c4c348a6c56a47ec97a9c4bd611a a5646ea67ae694e8b01208c56ffd28775b44a2a97e01d1e779ee5731b6cb0ad8 a0ca70572453ad8ab6247149e16bf36973a593599c02e5e6f864c494033631d4 83ec049f4dbdd34c3c78acee48136ad99969318e6bdc5b2fb9a6054d80ec543e 35bfbdb34d67e96d7022859ae2b47f826dbb77cb783cdc044ba59eceacdc8c94 8a4efdbb8ec2f90731f268effe25e93b0280855a8687f28a3da7b54b428f230c eb1bcc881089c8dd6a5a64cff6e038a8a2c89d727d75d6985076c98e1b0d829d 019e7d8c8078fb3cf6f978149b282c7f46895fd3fccd835facbc7467fdd864a9 d3a52457e2a96da83854ce006742163d2a417899682108f9c0bdd9cc9f2c0260 89cee9ba195ecef374c49ce914cdcdceada53c090fb83aed39a8598a47c2d7d5 1c329e3c9f66bf1b8d7f68fd83fb20ef98e0863b353e960d2998c9506d5e5660 5e4552f30f50b035ebc0c9623a7f4e67738a89c1f3f442cb07ecb76f2f2f70d4 3ed17573f6bcf71109c30c59c59d742f247628090dfcbc58d64ecbba6f86313a 16fdee58fa6d3d9e383f34b06d3f39b6b5480df70d843c0a5369ac48df6544aa f79e24d569736d912881d3b42572eafb7f3f0099ee71d256b3bac7f90efdfe87 be366eb644067a437182f87d87fa9b6e07cf283bbffc9c8765000e26bc231f0c5370fcdad6f2bb44c1ffd9d89e48ef261d57c12664a552012d01dbc249be560ff51fca1974a6f47d381faad1eb372e544a2b3518f324b6c7cf17b127eb6bd308bbf45297ae2a300876501571dade2c2569431df0dbb1eaee31fdff7244c25a0cef53749697bfe657925ae7a00314847443776ab9d58ed8d930c5b0394d88b90396828f2232d12d22bd868483e334219a35486801d3ad29bf05967931c189c10cc2966aa8bada5c75dca22bbabb3b12ef1c387d62f5d5621d75c281577cd1b10e37f260ee09f92db6d6b2eeef994ecb68df181e06da6e4c929d879fd382754907e49f538724224de4de2322427b5899117812bcf677b9dbbdc1b89380834ba40e5b1b083261538a2c8062a0804a15d8cd3b8bd0cd040bafb6b96ef5246d280e09b8b531773e26b1ef77ad1a1c9b8f6e85204687e5712e4e36075b77e897cd460df3a6e5ed430ee37922c4f91b43f5a4643d1d6def7bb7db2dd44d2778d9ca4b013dadce4bb90db36f37db67e4acb63484fb51182291d4194c2ed6e46c5188270f35516782b1fd28b0a50acc35740fbda4c16250a0acee2ff8e3614131f2855e00cfe21151a09aa66a0ccc33def9fb1b492e7a97eb6d3fd329c9b83759d3089d0e6f3935cb5d002a7ba60417869224f841c4053de3f179f44a37fd59974e0590048c3026fc44b42ce4fef49fec674d6bc222aa3e226adbf1930d366e69d6eb870ad54d4962d8cb784823c816950ac1a695f2fad7f0a8a461ecd6458760eaa79601f66731c9b6176c941381c1b6996373e55640c762f28c5de29c09f7128930600490c956de7d991dbfae07b9030710bb0151f83ba89676253e674e3ecf1115f60f5ad54fd115375e50808207581ccfc2285d32e5107cae0fc195e3c40dc399d50d03c0acde32818763a8bc0871d400817e00dac48a389f711a70eb54f081996e0519a0bccff9a5a331bf8df859147d91dca33c815d19b6041771b248c9aaa29f062bd7b80a87834073e0778939bae0236153391b88e1e57ce8f417b64781a06b038b686572a89488d0349213259db35898d39d086be77deda099b344199607aa08ebdcd194ee7cc092855c66852d7a9963e2df2383857ca7bbc76d0a1bbbcbf9057feeb1fb083578b306ddfc4f69e057fa157e4a895b84db9ffe846fbb5f3e4805a54d4853e43f0536c71a6b6f3652c063c1e2f51f0ddf27b5217c419933b2350483e0f493c947943d34fff7789d4698e9522697fc383dc0e5ceb1d4c1baa1bc066acd4bc2f8f8e2974b78e211e72cb750b089be13069b4b83d6c7a3ddd896af011b5945b6faaa67aadd5b3679354c24fd998b79744877e31e064bd8b6ddb2d6014ea03d7076f003209364f7ec67e43d89a0462b872aa2af32e9d85d93d81e8403e776acd56ead9373080cdf0e78a0ccfc2826e03a4bbf27e56e0231d4b31ece05f1b8724bc12c00b80301f806a4f2b213a4f84e0c32a8359d9debc8f010ab2504d9211367b917bbbc44dd59ed1bcc42b198db8e68f8f50c47eb3b1f8a25c3610175b63b74ec0df50105ea2813cc3ce167a8f397ceddd292c75c6838857c085f0fd12fa2eb487072db76083be99a8ee1da0dc3e1248e39f90a80c386269e5f9c04dc269fec671476144d715ea7153d08fc5578420e4997e875577a5f5cf8baf60dfce24067a516865c8e263f39dccaf4944b85fd435f60f77a0e16b75478792c0177a7a8f86feb5aa40ac920cdc0a69f53c0a9e5b5bc2ae8fc90dd3dfc0df80b0db138ce364faca9223e4476dc422b4def843efc46d3d69391c6cc799bd0dc120ff655149d2bddf251ec921be1cf9a74871ca01ce0bae6a03f3cb1a9509574c70e86095c226db39595c04eea6e83ded5b04bc0c9f421e986b928a586ceb7837d0c4d889603fd9cd3b63d71faf1693009ad61cb65e2983a5ff6138c0e6e367bc7099a14372c80c1e8ee89a66601f88139ae18d6a9a22071bfddb917330f15f2d3023f7074cf87977c5cf0eb8bd2848a68d5472c503a96d914fab05aff8a51f5180035c1d787c8605b44f76da5eacb0dd2d7d53f2823c91bb7c2b847ea08f1cde90b2ac9df2e3a9dc906fd81046f243eba180e9a7881ed3f75dcb7c4f8b465c54f01b0a778f1504763bba034559a498884f646898bf2f23e2671b9550946bf2bd10483071d8ee84e8821b6717903f4b305593269566a166e06d38d5a01e98203c402df3c1538f64ebbb1b7f6c88ef33d17cb291c5c172cea7bc6f6396d9a7327370b2acc57b029e34334477a75a5175c3daf55da5cd905278bd31fef4f9c88a00f09ce1d73a8a1a460478170c3ace3e4d53dbe02bd701fa130454b7bfa9cd65d5f03ff5918a94af04dc11f6709a53007c76fbc061b65efad6945cf7f44c5bbebb9022997be12446d8611523b144b0a886a3d0384e9f3a3168a8599bbf95b6bd9270c07bee9eec32f9c448ee27cc9d7af02068d1dc1db85071a5b11542ef1f89e0606fafc3f4213d29060e0a086f0f2c31db733318e808796026d8de233f0a96d060f6aa7b32b0fe93ab22512e06ef02e233e23561dcea8f69f602464f19e35635c0745282c7bd90bd1b64cd18ff25759b4ef8196a2892f2bd724844c4c064da9b202a9d4eb4fa9dedf0e9eb11b07f3560d0b42f02cbcf5a069c3d79031662f6fec0ed937404c651fcd64791354a849e7d6363a4cf59ef8b87128235c3337b4fc010755ca3427b79ef6f9a9c920a542c45f997707825609e90807665cd6ffdc5c7d0278114db3b5ac4f55406d6494111c14e55fcd2a8a3370e9ad1ace93a57b64e20ac6b74830ad0839fa533bea32a1dd23ada3b87ec40aa93492aa8fbbf59290f300be01c892e427386c14d1daec07d8638b276c62fde2f87b75e45d9364d614630351a96c9889cf30a56448886928160491387ec71ffab67aee7e858b33ac37440fc4a998d8b955918ead76ee60e98c1e58bbcbd8ad24f0cd1011e76097a257660ce3dceea2f931fda4f72a5785d7fb0c0251fec427906f25ad074777779e05f50d38e74fabc73e5b8f7e6607f5c4864e8d371444170c1f916f60638762af77640080016dbb03467eebfae6d3a5ea618e6b3e677745c1cb481c40fd32dbbec09205296b2faf58d275cd682ccda5462903734628432ef01bd54b0d1b86ea9a995b01e9473f6099cdddbb584d78ea5ece13869da9a4ee08754fea24267f8f30e13203b04fb9c771765b3ee195bafa5d936c149a427a760a9aa4496c9fda4ae2e7d90fa54bff2d6f9aa922ba82360435572ca83e9931ec9cfc579ba52a8a553e8b4a0629b3d1db3c9ef377ba6783cbeed67495d5698ddd27790d447fcf4b2dbc34b400a344b6cb6ce86e69f0d7f13a14cdfd2c7f2d64a85939026ca3e1244d6d84160e0b1ba4ba6a2ee29971255c3dbd1cc925301d72538e0df059ffa67763caf98e0b174e27d5c7b4cdc669a27d59ae095347160e69b8117896558c748d71957cd107a6f441cef9ca87c8b27034137d062866dc0ee466efd7e18082633ca5ae0a0703333af230f1f59a1fc6fb1948fb2541e87009073c73042c108882389118899e00443f9445609571d4c9f4149de62220f933cfce650631418b7f9f63df98e5000e9c92cfe03f5a00e92f8420ab7b516ec438f62f064743cf794644693d31bcb606f8e72098410b3b457f161c0f29c67ecb8412f479b9262586f6296e0c40893c061f1512c2375f487048d0d1c49e315140f3c754bf6012b872d504cc78cb6324053192bca1286e3116baaa1de300d2c86ffb3c58fc3613fe075a1519569338550629355efc5299b51127151107871f4aa7a6c314e0e279650e0abd075e2c1bde0331609fc901791fcbe2bef67cedb430facd45ca2ce59af61b3552e2c4edaae704053d2ac6472a72128a8ad67841f49dc896bbebed52230705d78bdfc2c6881e0e0485c0c8097e06151daf9f423c52f1e6fce39bb82708b48df86c3ef1800cb20b068e8c2e52e665f0c771f52c8ae8306ea8dc189353985a15826deb5876c92503e5de58432817a5580026b5c18626f7f470e45fae0e6dbb1642ec2cf4f2266d063693030b66ad9ee35996113f4fd99e8bb825161d645738c542ba3bf4b599070504fecd6e2f168fdfaeb20aa58f22cc9eb7e98ccb4cf275490f013aa45f72910a4bf0e910a0d3acdaa7d4a0629237f659db20e8c8a54e17b92e090a01a4bb7405edd6eaeeabb16841616146686e4b5c545dc956a8aecb7e571406739d3341870dceda1da864e6293e28edbfcddb0710d7c6c760b7ec49073fe4c4cb945ac31f0879a0a1afe371d92a68bfd38273b1173a1a7fb821257b848a3c3660c4b980310c93a32f4374f2c98b1865c15ed737bef700926d0c427a1da3ea5db365a784ed056cf879f1bdde3d64c2f3450d97a152abe2ba2c74bebf3e7ab9740df000f1d404276ec01e0e3fe967c219e3df1c21dea38ec62e610732bc377e441aaf4caa38085c7accde4b55db836025a3cf2de0a8cb33203a67ea9f36d2f6c709e49765bd0c13a0c9e6c40c149e6eb06c2fe8243da851aae6cd89a4f9f764f1499f565ad60c3e6499561b040a93f909d1a82c3cba323dbf2e85f01cbea6f5a30f58341238046ac97e3f49241a56fd3a65827734338386f6efb195895708aadcbcc89b4e77007d4c4bdf2e3a55a5ff670eb0cc27a5e96234d626da5375c4d1084993c90ea40be2e6aee9a8bb4734e3ed96f9db49b3f2353ece4cf51f205c4829f199806c3d0635f2bd0967a98a867285ebddf7d3428336a5c3ff255f9253d98c152aefff990bfcf6d6e8089919ffc587fe1e9dc22e6daffc9e860892625ce21a4b958a3d24040f2f148dc1ccf61066505da174307d0030760c0fe5f670ef6b8b9fb5e44f77054e23d43c58cb50b75d74c8aa80001604fc528301ba58fc810aed6fefe5443e0c79243e25efede6d006fbc62f162b519956a772ed3be8f0e95f45f7c8eaa3630ecd840fa553a591fbcda75f54401004b8ff1c51c0d9a70d6f8cf5e402186c6302ed6d9c7827d1203569f56fd3b8d2407401b4622d849962f35fbcaa7542656b0fee3f68ea3fe65c14cee47a3dc4807ba3562324c1e6b2be1c9fa8834be1ef070d57e50d9573ecf47a70bdfad4ec3c700071f1ab58fe8cdb146c4129d937f0cc01ae437a030dceb4a706362b28f95405beae086b578b5c2e779c7bd979674de6002a96a70c26859dcaf3a16f350d99e4587059feaaaf51fafb33284bd086016b021d5f1b688dd9410eb25dfd1df8db518e94d7f0e89fcf204fb330c2d5df6165030a4cf494707115b5f761cd0af09104e6c88127744d98904b293a0ec006d8fe0e6e950e76f939e0458358fb9524a06608f47092034ff299917e57e40913f5dc009370440f6a2e460b5569db50411d59737a3f044ca9a33c8f80b0778240f4510d8b880a550dcf989054fa9b5b12906458dcee064df1a82669cfe69bbe9a7d550648d49d8d9a15ce7f51c8af62674c855495c582727b91a6d130b5d7127db83f065e47ea3bca0ed77cfe044c1369820ada72062c46aa91e4bca9303cd1f0f95408da63e9b05ae8b709f31fe4ffe3db7b6088aceb933ffaaa7cfb077c4bee1d3301d96777b265fbad918b24e90adb0cc90a0584f68c6e3cc657d76585cb35978a01588b9f0b22f88e48fc0d0787b6f1af93d607eae123b36fbc8d9ec7611be27f0e5260dc8c51cb6138b3e2a06e2d057b105d60fb80ef9d7cf48ccad32728fba40150d5ef2d0db0c714d229302c588558165dd57d021413691531b23136775f50034caa0c2b6aaa7b129edd14be666b22bccdd64dbc04bf7c622e5cbcc7105b580abc4fdca388063161334a0ca71f2448c30e5a899daa85c93dfa3028f7b5a2b60547fd9540e0034ca8d90d0a4ef62bfcecf82014b2b0219741bb9cd78c1169950c30aa52a9a6d49f26a04492311bf93648efa928af7ec7f237992ae819d75bac0ff158fb625051aac7f7bfb9a372fcf26f89fbe1065b3351cd8ca085244ed96b0d246f081d41fb2faea8818c1d896a501939db472733cd44ae68ba479fc1e7070d6bc1b8e84cff21f8ae67eee9e001b93b1f6d947790e56aa3483680e93ec96a0279ffc9e3cf5c80c3ab28c178d7773ef1e444619966804c2f5b182b2ca6983900ad89e660de75d707aaf29ef9dd2f342d522d1e98d7711465b851aabc75978e0ba8aba23f56b54a9c2c4cf16805a74bd945ef918b243cbd6d02264d0d2fb06d0471a25a345f2e11609e1b74f2bac7530517d25f6a18d350659141f4bf64e6010e92726098010290e23503b90d31cf1650dafbb49dc5619555d1d70ca12fd0390d200fdb79efe9c97f5f10e3171e34deaa44789b5d24987ba1f1149ceaff582d08ec2ee7bbfa2e89ea4a6f259ec3fa8f2f2d72b3bafa89bc786ec0c9839230ff00dec76ee42af2c401fb969de6e0a5afd1efa2a2176849a355c3cc1dce4076a506f448fa2c798a612e6025ed33e64dffd9ed3959fd1f0e0378678b2493bd385e0f086cc38776d6fe4e644650c7cfe3e2eed8f52138b0017dfc2aa0b8db19df5b05c856b0fe3ef35f0551de8e0e9a5642efc67fcd44433ae179b6cb02bacabb5c064b626ad92fe7e39f06e9e723e2fa87fd3cd036b651949ac0b4b8714e2b3bd201583e1bfc280fb6e47c43d5ab69e867d8cfae9fcb0eb3e97550bbc2ba164cf0069a3e62ebfa17b4697f1e5e9cf3c0c02e012e7024961a02df991dc18214702d07919e3a5b6215c49536d21c7c7b4af7007818ee29211af6beb093864207a9f00485ee02e4309b0b26199dbfb6fd28a8dad74a1b33629e48752c637c78a137990c523419458d13a43a9396565af966c77f84c40cdd3fa894b80f3797bd26db6403f26989fcafabbe037d37cd4e870c7f9295b184c0030b35c806e206456b1d1502e597166c39c09231b5a3a6b30c4efdb9be4ed50ea339c8476686f0d2c20686009bb9bae13d3e7700281fb70d90c989009eb5204f137b0f824e38bfa92e895703402c0660683b1f7d381ec936137df500f5c3fecb837d43ce3f406a30bc7c0f0882d5c87c41a8f717fb1310be3fd432e2a0a2c9a4d0296fb07014ec8be80ade02cc33b99ddc11cd5ecc01a77fe2a2f6ece8355a1003f945289ba6ea7e01cd50009e5d3adf810161efdd0108e5dcf8111668b6259d47990533a50192e9766b260da6ac217649933a29dd3e3b5588e000d1783ebb7ec482ae6f1ed83ef4fb4457018babf46c322f82c6af46aa13d30a49991f019599875495ad9d4dcb05a9bd6102167637925e9467794178197df05d9992af506154c068691d12f7e95dd7276505cab641f37c05e9dfce5b4587404270fb6189a231ccc1eaefc59af49ccb953e01d626888f27e77b43a91bb3752611385f5b765e70bcfa488560385b7eb1012806937b6ba3d5ccb21dc9a1de4c14843817b5b10031475bb7d5ac560bb65316e90f64d16d20cc2b33d2c5403a26467ea1534f9f39bf2050372acd5ca43a6aef3b048d65dbe02fe805d36158a22efff038f27de41f9cc994ee9ffb7f7e406090ec0269b4671d599e00e13a689441c31050165cb660e23add9a67f7d439e108d667063cf8d1b2ec40fd4f78a60a06cc634028dd78610a62ca3604e38ca35238ff980adba7b47986fad927dc68a120421fbe4c1311d85a01ae7728fd2a3218a6b3b401150a82d0e300a65a80e613674ba5641b17bb6f44b42caf90442d4673c619440371c70ed3af0d4b24b781927e0be18824f34b28b60cbdf710965646b840c7cb07183eec367a77818ba6677953394b193409a98c767ac93094d81ee63ef312330ad69aed08f98aaf90bce23eb648a25e253a017ff95b2a20c4adf388047ee70f000221d66b26b5ee18b1d4a2fc20fa4579d0f30564cea90fa2fee08a7bc97f800460bcd38e1ac197535b37b0bcea6652e6c296d51e0fde48f6c0aebcb39128ce09e39120112d7715c55e3deb900bdca764c1d280314de76a34ccc86d63b755a906700e3c41c29ef3d8461f1a31de9523ddb5a719b8b1efb027091b38409b95d506b8c5711fa0eef36e191f7d29a65d64c55b585179981f9a25eb91deac517a3c00c8e5e647a90b47c7b30f4abb488fa816f2864abc89b1a27b1133868c8336ca019123d6aed58defcae04d68cea635025075c5fe683b48d15037b8b89f019b5008d5fda1b244a8a0180a5b4142a0d35bf1e9d38cf270d47efdfe099d6d3847b503fea454e4ec28db7c9402c2bcda3ad8bff966ca4a761d9a258133da5a76bf460500d34cd155a3c6fc0a30e0587e0a76256198493281c90f69e4503f097c502501a46a70b0d5564e3fdf6941f524b69f050d13937f79dbee0e89f3c8b26edb7c0cad9598119ac2966c175a4dd96f6617569779252d16523d7462e7ef992ef3fb0180c4d775029054919282377d481b7296f6153c455e14f7be5560f465b60d5b05e03c21f841cb4f27e0bbf245ac14ae2f64aeffd4a2ad0e42fe6b7b7bb06844008f3013f4dbd49940f4f1100488f2dd9aa8db2b6797511bd3e90e6e1f9e5d850e47ba91ef7eb857909698b610ce9341e8286354ca344fa9cf37ed7ab9afbf9500325ca223f4c41cf3d8c8bcdf874a9be5affebd54a3e914316b56fac0fe1be703696f79171bc08bd2b9c9b7a035590a0b06966a0837099f72b08c108e776f7e00ce6d9d530780f45c541085c86ff2cc58d9124e4ba36ae683c522db50423d650baa3c228bc76e974012d6ae42087befcc64635cfdad0469d55d5fbe79b90f71005dbe9d17a2ed795a14e5a84c5dbd441e7bba4728fc117b9b71670b502e39c80ba074fda33d0cd1eb18165fe0e9ca5c0249f6e178b8a60725fb5f8bd8bc98ee0294364f5af8f1503cfab3cfccd94866d1c6fd9a84f3e1f192f1b176314c25ba0548e2594ed24ef668bcb657d29840f525c924701b9d668d04d35778b0c47ee408cfd8eb4fab59a44d71b66378b49075752a01fa37c5b4d27cb36cb3b6c188e60e0b615704352592ffe8c553a4660a477708d8ed24761ac5e65c719e1160edb304d010e48ec2900d8badb611bbb335e1a83bd092cffb35c71c5a33f9834b3d710fbabe1b2286a8e08ad603ac3b6091caa78538516c418d33e496b6d37d6155ff04624f7a1018f32c2ae379f09706c949f53fc7567189920d905a280f0c7f46db04f7714e75cbc3baa92db2bd0b8a428383aacda5c2b2786af81a5006af363860022efcd5c31a618b56da36739a73ddfe662af65a7f03e096506e684fe88ffb330b64b78ed097f9fc54df586d9d1f68fad5ca7c1335d12bc2c5d4538cd379143102ba820598261bfcc9db5239cd70bfe57cbb0d20f2f8c4bd33e19e3dc1f04d4e0f82641fcf9a18c1eefeb2bedb8b89d05e0cb1d2a5f5d4b99ea89f0f309efe370ceccd44840e58e66c38b2c92103d27cf73356945e27d704b71e9891f087c7b30b4cefb4eb065a5751a4e35010ae26f70e5ac377d3918c5b2d5440fb08544a1a0899f40ed42b541da58f305dd739db8207373020e7d5ca6d756b01a94f469fec0a91e79ee5bc151bb481910937b1d93eab9081ed04f6de7ba50c8f4d008cf51c076b55d9c56b4afbae56e425911b161904d4897989feb0d27e2bb83bea918bad0f84f8a793543a8cbeb148d5ac34c3fccf8e38c16370f831377beba9b77b3ad00c36d67a14ba37e074b50efe5ccc4fce737115cc17a0352bf374828ca99287240355e3c122559d717e12b1038e50f2569da68a5f4850c95e651688361b7ed4ba0923cd25c92cda2de51a3a74739b8ff947d68f79a676852dc67b8ee3043723300411d8957316a2af82e31805ddcd40d474bead5f31120835d5ff2536ad8b264f071efea9e2aa2b03a8c2527e72c45cbd2a39f38bd34fa7044d0f31ddc39064bf0dd2d10c8737b8beeee6ae70f82353f533d052f968b99077a65e2f4a275e932506a02452c6fa712dc5e90841ae1f6499f436e182c6801f89b71e14c6ad0911260507ebf3fcd3c45fe5f2406a8d9cb78ea329dda7695c54824140ec5fec79e5b9002228cfc0f90ac7b6f462baa1a245c344880502c103b0762a51604750cc57ed005940d1303990da28d4d91efc04d6113e382837d9b88564f2acb372ccbbf7f0044bc3af37ac0875c64d18d582dc41d1e553665941253d804359b4794222456304b1d5fcec7e0463380fe96f501c9f04534d145758b3e5b386ecd711c4c309d907c702356ad48e2100678e975ccc3e6e16a665a665eff13e57f5ceece06e3f6c0e6b1e3b7cdd01eb958f1a5688a3f3b70e0027036df26e96b6ae898024a2dbab0d19f4bfb4998a40d34c15c6c36cc0823c8d538ed75fb03df61265becdd91b9b0be46b6a2f0fe4b299969ee964b29d0f647e96ca9d906c6ff191149be7423eb90a37a06c82cd1f6a9563867aa8611cf47a012b3ff7897f73407281eb0fe886af096b8f6cf2a73c7b2a141aab69aada59f3f9923a88fdee5d6178418869703b92000dd484f5f0e4a070e8ca25abb1e62592275b45cebfbac19929bf3536e90db00cf58e30e2bbc6f45175aa06c6421d1d5b5ac88206265d1acdbe002d8da78d090611fc68fda3ce396a82e551d5d09efcd67120f7f0d00eada692ac4adb9df23508fb8d7ba55108d1a137ac5c49bc77a594c77ef418d82ce52caf9aca0a13c2ae0689b4acd3b588c1a9986389fcaaa7e116410f415d248f1cf8c399e49bdbdf030996cae828cb0326790c28ff0abad87f2d0ce08d451389585a157565b59d81c3057dd82587a4e36fb88441e64a875ef475a6d9a5a850d3e625e6baa3577b7d980cb1e2757947568db0f9cdf62685ab36ce2134133bf4bb93e733899364fe107c0d6b04c3c12bc885c4ff373772c5520164979b414479ab4eed289387c199e08a08b93bf4dd93705b4c7c0d30c6aebf9b9578758f4285351214a307f3834b5e3405a50976b169647d37c78d5f0faf4f6b2abbc787e33d53831c0ee6835c41463f0317c5702ca7a1d1b2afa168c8a2fe26637547a74a4a51f1674ac728e94285e001d0b2daef25484e85051d6b1d44ee22b4d0260aca228b222d10605d4ced1dd30a838b6a66edd92718f099a185f61e5943897abbca904114d298c74b925ed48201e5d93d0214bad9b6d83cec806abe2de2132fead09654c899810fa3f98c38c80cdce54be3aa7071e08d7a83aa5149ef5fe8c0ae4ae52c4466bd16b9b6a53a830cd2e104b587ee27a9c42aa7e4a93232d1ebbf8b97da73773a572e7f3d4497fe0b5c543e4231d9c73cc781fb462cba6c43d10be9a74e0b87ce91c7020201805e0fb54c2ee140a3fa347544052f7d0565965f784e6d6efade5c516bb7a9b96b9c09d9dbdf575eae5525feff20fa529e7c72d4889e18fe4951b4c2db0ad1c3492f01d2a6c925d8b02a4fc9f20025a7c0748d657df8aba5c7404bbf00d401a734cf0f75cef62bd8186c705ef1e00d25ba03cc8d85cf12c8c1564dc91afa9a9a5ff40fbedd7d6de90ecb275f44b062831aa71df4f42873a336e1cee75cd284240db50ee59371832cffd07f531f58fead43ed480880aaf0caf9b3fad41c5960624abc04a73adddbcab86a04f91a9409ab30b7d2b1f7483cf4da2e5d1c79d737e832460d66c0cb4e667b85624ec75628bc4f4250e754f18aad2bedff66b58b96c7833b07fa0944c8308c34f38633be18e03543ace2d915f1a59bb5c76a76c8d7d2698709e013676de159ff85b248d6c0e4b335013710eb8f8fb7b080505f796fa36de50f5106d773ea2060aad149aa51ba35744dbf131a54f5f42bda451e47102e872101e8f15df3fb482e176b43f8d8033364240925156bbbeaa3f106921ace9d5e5d02e42742d46de9977cc967c8879db500aafb7f83f9cfb48a6fa2180bf0f86fb90679324db2a21457b6a14549fe4f4cce18af6d0f6cc372a5c0446cb3d898943d027d0524cd0c177f412b7f842227609f1f9a7f24b8cf2a02105ac7886918c74901f6555be20b3209a159ed006ff0f68fe3e0e32879d594333aa877415cddfce0043f673cc5b081dba5d4d46e11ff57d6edef7789b3cce23d91cb27af6dd8fdd60086fa3206e84165709fa86886188c2634622cbf6ffca0714775422a03fa1f230c3d1d4100b9f05f6669555e52f703938d861460befd0264533852153806d94f0298a8fd764bd611cb674ee0791e181d67ec5c98136b063cf079289c52fb93010b5bea5534d7f738ee53c0d42760179ef1bb5aa3afa1c36e2a8f1815a58cecef0123897e3ca83ed6c5a002093a55d519f60da7b1e4493f819de575012bfa3c570106d02ae64cc46deaa22d4b2c39044eea977a46735ada52440ff5ad679911f80b2d8d439a24766222d9f2d05797eeb82e1f5391e65deac24ff3953003606fb4085474f8af6fb8fb7462da43a9ddc412aca43af8b63d9feca64a1bc16360766d0a true -check_ring_signature d9b378f98ec39f5fbe1b0805b104d338f187c9f87ec526bbd1b360dcb5910ce6 3df1acdf409943c78cdfacc524d786dae1b68a6a918f3f945cd94b498cf2a613 6 e70fff5532a1b5f0fc2ad9692054688146590752300f78ec46cb266534b4449c 7da13b7bef0ff397c3bf1ac4a629dbb68df7b9cb6cc266952e4299302bb96ee5 d8cd62163c49dd0fd017ecad7cc8f2d60adcdc89343a58fe80c3c5168578eceb 8f4e405b89174971c9b1ac15035ec490d56affd36373c7143690e98291f0b3c0 4da97a6be171c59f0a9f65ccbe338c60cc3901cbd84048dc8daaba80162e0431 8d794075830472f1967dad67e8e8b8725347208c7a0be028243308b2a6bd7051 c263845044be04b3df6059b8af15e45426756367447d500466671f6415a47b00704c2bb0c556b2809d1834cd1710adc1bb7e48ed87cd2250df3b0f60cd088601c8720289efb306eccdb524cb6fd951314793b88503183ef8c43088dfa7412103115fe79d9fff4ca2017d21a43ba77e3cd95e27a4c2dff15535642b01d0476a0037da20b397bd4ba949664a300f3aa9b3b21d805a9478e2485c215ff433440604270ca66c2555491202076510470458c671c7be40ee784a5a93c4b9bd6681ad075fd312f236da9d577a35165d9d294499bb9c75c868a790d284b59973472d3a0c08f18e2eefc9165bce78aee7a50925989c691014534a8254b0592c9c37cf110abe3f687f420e6bf7f38650929864853cebbeb1ffe16073f3d97ba538450b4800dabac9354009f5d805045c74fa3664d7098e29549588d924772c28fe6b29980cdeec627c000805cffaa334607782b2ad739d45e24c42164a0fc2b6c74dffec0c725ccf25f26923b03d185a84897371cf4776e4d69c480f42a1c37de21b2eba0b true -check_ring_signature 33e4339cad4154f05ef0d8e6b0f29fa50d1455051231546f474be865b8a64d98 0f443ea5c59dc6133e34999bf48604d1ceb07d5da8f981572eb152846476bb09 7 21542e5ebaea58b2c0564aec4c360163a7dbaf8b506e358310e1c54e7207047a ad57d850281b3667635e74f6ce07de10abafa5baef53ad15ab5e2be80959aec7 68897729a636f3ae80ab8ce6bf356d31a4c4ae3c6be51c684cc4972a6a44e952 38fc391516550b545a4f6491bc877a171ad9a3bbd5510902cc54c2657546e1f6 819d9b8c1c285a5159acb11104b28b0f0abeb120379e5481dc4d3d3909bb6a7b 6afe922c9ed69fd498dc2631dd8937e7fc5ebd24f2b144fc8cd5afd9885110d6 bba7e4262985129a981966530588319282b6341ffe4e5417672be9f77688974c aee76ea1dccd4630b8a7ea1649168fff067aad93236bcfbd77e2160983626406feb16b48e742867600fc6d4bc9810628d5dc53e8a19fedde893a610fe5235604f9b05aee47fb2230fcba8d723c04939d102d28ba2baef55c0b804ffe61f73908521f91c276c41ea99a4307a9d0f56def4cddd6de8d6feb5c5a2e0a71a5a6e5065aea4f2777e7bb2b6677e609b5264ac1edd0f41a881a011ee6d30ac643b4266ae768ec80503370f47754e2ffe6bd62cca88a70632c96204e0f0855b5fa22240783ac5770fea5bd89dda2b8d06e63362d9b427c4aeb9089ce3f1d484e27bec403022a2f6c2d8dc9922ec6735ab59f1db643f090e99a14743051832b262a41240edc4ee4a2fbceacb3792d9eb503a46204c7969608044e4893ee6540953b889b0b22aad80385c091f060323b7e36db03a026de09b66140792ea18914d18ba05c0fe5136289d83368a13d787c6479344404b1553c0e1c88bb6edb7d39bd19f81c60b7922eba59bcdf32e93084887f4c389707db90ea5d2df9f1bbec7ba083e495073179cd9591d8c1193400c69f93c372c425bff07588df1121575cbb1d2a7b8c07b9238707a4c30ddd20607da981ef444077e0d2d013d87463d6c6915d556dd208 false -check_ring_signature 0f6ea1872ecc1b7ef907a5dee621e1b4256df8a1cd9687c842ccab2952ed38ed 5c128f8ed459db263e0b5bd9446cc48dc576af7b0e7aee852ca086a7e4160826 22 cc4af10fd6205dfa4568a57dd3bfb46b6ae4d901d2e68565d3f08f0839fb5114 b0b407c060a4b283eda77ee5131272904eb002819d4af110fae29623be3f6474 a457b8aab594b8d43f52ebb6ca0c4ac9f248cfffda213bd43c50c2e777c1f546 6f1b3ec251308ff83741f6d5c3f1b89d2555fe345a940d75a4ed08fc7f0ee87f 3e807b70baded0adcfe8d90de3aa9d42159006b24c9ee7994289f370dca011ae 94e24f32f1e8c7db1839292791d06ca9741f2b34e9498cc58d30d1301f443501 63ffb73588486e4234c2355dbfe62b1a5f032bb096e510ce8ed2c50248003b96 031e71759e878780f61f216104ad8bf08713fd326183283ddee86fe53685a775 3f3823baa311dc9d4c6a7fc67360c7cee69c80ef6f1e16292fae976506fd0093 ef12420d89a7bc99e31c4be5ce18eafe3afa59331af01334f81efec231d55e51 482e10e35289dd65d3141e09420a38744c19d9b9395c48769829c645f30bb73f 3b04c28cf067b90e4def92370785349aef58efc6055e0dab5db6f8ca40d5bae7 08fe44181284a473ad74f4ed4911799afe3147569ae74bcfd14f1565450653ea 44b4e0a40f7f61f79d20c947080a26d72dddcd03dc777a7ebfaf0ade7f71134d fc0bb5a8fbb541b3543e94767945e9412e45cd0b493c97a58f22c42872f1327c 4529083fad0044d65efd8715e697d6ad34ec9e817f1fb297caaafc6bb4ad9372 703ac5d7eae49e50569e3b5e4211adb837bad1863e5d4cba01ec3cc96af6741c 16ba7bd75c1b67ed059f3574f8ce03399a66a2e56fcefdf81aa01d9dc338c074 0d5903bb98a2ba7709e08663d6ad46225a1f9de87a9f5bd4d04334c6a0a1ac1c e7996065ceeb6f08e56fde49ed8da8d24053ce9a8629882fbe22e7a6f76a7f81 1f9236f13b73d6263d7966346af5f04b359d5edd833f517d20bce7ed72a7fc53 6e9d88ec10792774d15eb9054915229c4f8e455d73f5e64c6af83b62bc49719b c1d767fb827e0e05e67bcfe026ef25388dd0c5ce8efd43d44ebc11c242d5570d83146f8abd688b6058895aa1572fc20179b617c330edb56ceff132cac259f5085ccfa902769f9b0fe5ca16203ad19bbe945c9f6bda55d512f8c2fe78a2187f0c7763cc7d0abc2f5f3f30aff522d25e1bb9da84b36694906fdf5eb8a8782dbd0abb6e13b7be7d87b8a06a94b9ff16f8bdbb7402d22f193c92e5a67658fad06803d0a00db6024e8b7328677a485b67cec63e5b342ed0efb37a56a8c8179a7bbd0b50ab5bad4e58e2a9063ff4a3eef3db96bf37ee3479f9d0ecbc4b5e144ea3db0c8251f6cc90bd371580d13f2eb505a0c3caa5b578c4fd86aa5a7c529b4d300101058ad82f885e307df4f4087f040a994a3e2c3abcc7e92df66c97d317c37e0b02cacc1b8de29d719c0bba4383bbe6543dfb85af02bb1ddd8e826f683aa7f19f073007f8b913c693db06232218c36a75871e2f42a4e5f693340a37642017aec00dfbd43524608998af8aab6308a65ca9d03817ff1c856e9b54a27b27e2339f4a0b5bb35a8132b2df92acfda5a5829d2f9b1dd9abc3cba9f0b27956f6c07780650f46a439cb2a856e3ac27192b882b203dcfe8251f9dbccda23d5bc7cb265a3120eb899d8b53b0e1fe27d68e5224f67d8616f59fdc46fe0270ce7c4446859b519012a7b961c2f9c8fc5413b500dda8972a38ae237918e00126d44d9c7dc6985f20c7719b13c25db32f001eb1050d380a8a8979a574763f77a776d4c83ae423cfc07353a6a74157c83fd89fee685f95d1f37fe47cc433fa93d71c40e3dcb69feb5029c31e897fe42fbb9356492699642a17f38b5b5e123700fa1161bf891d76b9f05c8cb47a1f6657c69272e0013dcbfd549ad043aaf9e3a6718623a8dc0646f9a067ce2da1a1e0efdeadb9969dde44da6674c85a83dad9bb9fa3cdb346d6e4f78055e323efac90df05e142ad4cbfad93c1633b8493a0e11a997628eac4dc29469060baf3739d7a738b8a940a111e6d3b8b38969d0ab84c48616cbd2eaaac50cf70be01351785cfd058800efc3aadf4ee6de27953c1fbce3b5bdc3f71f1101bca20a65b4fbc4ac06d65d9e17b4f9bb427ef501bbadcc5be6c884b16fa9e2af6ccd0c053654cd793091d7a124704e651281b4b848e2d76aea96aef3b58700767323064a7b8b6ffdc2bdb03e81df0baeda20bfe8c8521a921f02483669167d70ea610d8dd4a6b549be7873a891b6fa6c1acb14fe863f4ca71cbd029ab1891cac753b006766a448662b7d55ef8ee62d001ba0ed95f8a15464dd917bc05617f247b65a0af95cd92821445529951ad5046b60ffb598a6cd679ac51a50e6bd1052f6ffd10dc6a22d1dc9c572ecd6a0774fefdd12a07177330195c90edadf36ef204d772a0ced1cfb65eef09d4ab5cdaca877cf3b637402051fdef227402216716f3c395a02f4509016ad08ba7c82eae7979c2cf63ab5903dc40c7fd5c389a010c7fd651c0f9898ab6af4ff47b6f97c0b47f7656b1bbf2f29b048a87ba784f46f235f4be30e7a7eb9522f68aec387f7bd05cf1b1ae8aa4ddafe69701399242ad42e9224040b93d5ff486de21cc81d5ef9e867fe44df7cd1fbe94a9e152b1b943e16323a750829b044fc23a5675606948de5d061fd54b3dc531499c5268962bc8b3b83d0cc0e91950830fd6f46b0981c209ac2fdd976defee9aa5ffd95551ee306c69a844c01dd1e08fee6a7de5bcead49cb942eb6f7f46b9e719068515ed4de0d9d12ca8203582a8743bf77a9375606fb3a2c2750d321426afb5414d7cbdaa4030e46c2670d7c125d7ef0e6c3c55f5fef436aeb96c362be0614e446888c7ac8e8db72036500373fb289ee14cd87d5e18479a968fc0406e1a1b7ec1ac81db4ee1fa4d97bd90cf5a4b5507a5ab9cc36e22b28383a9f5c30171719938c896ec53a8eccc4af0c078b0755e4d999701d701b545346167ebee7e39cf1c934a31520f2f220a0d18b08 true -check_ring_signature 59e3dcf28498e1614e96b6f30f0203169c14cd8106dbc5a63eeea50300c7ecbf 615220bfb5c911b7e41c63421e4d6ff4475a66fe8b905d09fce1c631609dabf4 1 ff70fb1d5f1071a26d389d2401d21122f910daa7a5c4bd7fc13d1e7541579251 398ee72663cd5816dd919e54364344314ae55d7290b8175fbe4736ce93dd6d06c4bf83f1365611971e2137a31e965315a9bb1974816e4df8fecd8d995f4a000d true -check_ring_signature 1f4811f7d9f2a2585ead325d65dea6c8be19c6514c9ed94db973c24dda855bd5 bf53f2505f7296d622cc9747c99874a1fbfd27b30af5fa01f08bf0f4fca1a310 187 a81e3d5d7d6d12a4220d62e4bc64c7699fc8361e942b451f168cca9c265f56fe 58962983f753fcc6c153644962c5d73ad8fd4b30511d90c7ff1314615de542b3 c5ef5cbab07211bca6cbb23cc9eb165af2a3a5c2f076171aec205478849e8084 beb509ae74203595a691aab9dd43f70faedf318f9becaf55b2b71b61b52fae23 065a741d24434e45771a72eff61036f13ad5891703a50043bd19607a6a0e7295 ef78e5c4f3a754b4a67027029b179d9ff7eab92045b747c97be0abda72912c84 d226effcaf2a1cf81115278bd8fad0ace65ec4e2d973e41d5a50892f72e608bb 5cbe891a23cd6e33decf900ff7acfbe61c64c49b096680a4efbb75ef39cf385c 2e5ef1e1011df212b44916d99a4efef39bd4293ea5c53c67498667ddc4e3f2ec 00c62f4115b127709502737f2d12feeceec5c1def263d0a92c1957dcf8948132 abb6dae4e9909b95f95c83359fb92dfee06bd9e671759e677bca243b91b0ee46 143de49552c239637d717410e39bf6ad099dabc1925c48ddb395595a2be069ed a0f67ee6ab9b7124394475cf44d31c8d860e29c9750877fb761320eecfda6689 676c19bfc5311ebadd9d9c964ff252aa622fbd991b0fbc667c7a87ccda0e1c8f 59a75c1700928838785fe9cd0acf85d7325d20d666cdb468500a25e40a8ea268 00ca1fc954fd65c0d6d392673cde025f0e97298780c2c52f73b4459fc7c7854b c2e7b2c3dbded7fd806158dc9e618716fe18efbb158dfda979d55133e2e122c6 dcc8a7d26c4363da933a1471dd07286faa3b12c18096c58928099622a4ec39bf 978cdd1b0415665eb569a0267696ada48ece5e65c7c0eea08aa40e1154ff7aec 1fb46df3c66a6addc83f42bde07d844418fdcb3f9bed782f44a443a58c9bc0de 8f7a5118a45ab3bdc7f2b0ad26fb02ddbd0c7ae47a1a049f742c9192d88c759b dbca6f1236fb2ce0288f10f2bef3300cadb1d959d84718c6e30f842dde542094 88e8f4385a69fc5a74010a066a22be1578330143f2fe732f7ad86288112ee60c 44f2114734c7edac1fe3f32ffb20776a59bbf66fb245f98e07004c5b9b0d2bfe 37e5c8f6de49455e6fa1a73f0b718e54cf963c7a8991faf64f59d6900b5074af ffa2549108696c25eb16cb9e82a584bf86ba37afd9cbbdf252cb60952210a39c 6218b931e839f9abb849640b565c74b0e4343cbea2a49a326464a4a4dbefb1cc 71872e635809e479b9b82e181fa283b98db0faef9fd7b2ee5fd72e46d964241d 6fde1744683df0c1f4cfebe25e1f7e29268b2a0a482c8241e9a8bcba4a9324d3 dd51d845e665ebf6f8b928d3e106cb327108b2a2fa30949b4a7674ecfbe21e5a 27e3498052d7cadc463b5e17e9353fcc9996ef2834b9ef36f6ef137cd4455e64 adb85f7bb7f0f22833bc9895fef7f1ca034b76d7eeb2c21284edb283a9a3fe78 11838bbc2cb3206ba946a07684ebdf95d7913ac654996596206c500bc8cf31f0 4b9038c3ded33ef1f8a6516274d273f2731fe34a9cabefea8894164690da3680 698b1698f2cc543a2929a0a421c028c5d6b82b02305e6798796ea1e2a9745c77 e4b54dafa874fa79e951391737ad7a9e8c7dce11ed491b5b302c611624282d7a 62387b0ef87636ce8baab7d4886577ca744faab80e4f09c623476ea6328a6116 9e208630a7d113c73d124421c7c5454ce9ed0312419e7722d59f3a83187f774b 0acf4c4b6d1f83a44b60a5c9d214358fc7ce9493b83e3eb1a68a7abaacb1d831 12efc1b4c345d2cfcdc6a9e843c4b5b2a0b127e1acdccaf05ceff30a4d97970d bf457b2cc4b61d36b8a74077434db37186893f7180b1e58c496744e62e6bfd2c 1876330beace287b42082da440fb19bd5b52bbd24002ff64880f75e295f17d67 ae57a650d00971a8d87e2d36195a4fa043093c16032c3dff6944698940a516e1 b8bb2fbcc4606ee146c975b566938570ea7217e71a16f1009fdc672274aa8503 32fd85747b4cbd320bf460e1104568e58f15d7a7c60d0866db7b767e6f89c4b7 158d72d34a8b9526a00c920ffdffe5f177ca7377a4f3daf2e99f604df6303b78 0bc115e41ffa925c82aff0d59f5c58d8e50f36cf95ed803a6bd5a54495fd0b64 57a02569cdee5171ed3405930ea001332e5b3d0a5bbd85a6874f5678b6b0a813 471736f90ab2cfd07888e322d51b65daf7cf5e723da7cd0c1ae6c7594b2fdae1 a2c19444af0f260373335eeb50566daefd4e3bf4deb43b64795436c79032220c 641a91c14c5fb9e90f05c3cd4ce9e8758b5d28399887eef4bc7d2ad708c8566b 1672fb1ba356feaebdff9a7f108011b7f6c6cc54e107647c6a35cebb6f5894c8 41330d4d47c110612d253b6ff0b353c4af0af1e5dd6a6eea1a9b968e4a330448 2a439ea8abd33df3f0ba76f82fe50c636e3125e3d76917b8aca7fc8b3cfedd29 8aa09ff826afcde898b46a5f218855fbceafd3e6128ff618f99e7804a842c099 2c87e3a0ccb9e8a80f0fb38a2c5c6a1454e41d40142920a2a27d4923b0ca77db f79dfd6b13a6b29e3d54512a1b412ceb67d7d896a18fba6b319272e8dea048cc 5dd9a071be462707c49bfe33126a1d3e76e2a863afaadca34372e1043347cb5f 498ed7378b3cfd9010651d2d9ac6d06ada8781e8580be41eaf2f8d9c709b25c0 2a0bed203d8386dca7eecc6c18cdaea0f94c67eb450cd8f8e06b2d336bbfb224 c12d2e263c8aaf92acab080e7e3455fe9e324a47bc2d8585022b4b590afbeb64 7f60d2556caa2305ee8b37cc3d5b4fadda71f278cf5dca71107214099017cf85 64ab21d52aa35f1fe21a7177dbb248908f6fa274f148f0c110dd4e292487f98d 493bff46cbcfbe7efe0c404f103e44d030fd832024cbef22a71f27e05d9b7b2b 6107ae6831ed1697f0a4899c56fa1d117059978918dc0fc49ce9ac23ebf0d994 dedce38639ff34073eed209a7ae5716cea934c796754815260418a17680ae7c1 ac000546570ddfaeb903ac2508116f3801caca124d7faa31a6fbfc67c39207b4 4b6f52ca904cf3bdb4023437bf4a58338fe6434374dbda9f43784fa45e91fb4c b11c293494d677e4aec9f56e9da55cd628102a309cc58f46653c9e4c03cf79c5 94be4c04ebbe738286e28e259211e49a949f644396700f24896a8ced469e8f69 076687c8e26c686ac8c0df54037d6a644046bd491d34666a7e6902132b613bfc a118909da9ed5dd3a05b6e331f7b32eb0cf282abad036d936baa639ab4186267 c88c7c109b846f3d444b6245ef128ce10533014bf7a21ec855d921002de4bac3 9d845f90970e8035ca6c063bd6a63eaff2e87713a702fff7cf3d3df8dbe9c8e1 7930580148727ff9ced9d3189e8bc1e158df79216fa515e1b4883f59ffb8d9cb 80232af287fb1fd338a85f3ca7a2f822f55b357976d53dfda8fe0f931ba6ecab 02b8027a3a0a42a1e7683ab0869b1f35d6ccb0268db6eb1e90585c2e8b404671 30aa832f718a13156821c8d0a2d17066454f8b267b5bd9fe08654a7689b98298 7894bb312ee46aa7a3f5d84ae9ee06479993de0d6b4c1efade478908ebd63cf8 e995137a8d05fd04a9eeaf91b257f9684db14556b268b043137acbc99630899a 55a9f9da057626dab6bee33e3915365a390654c225f2cf2d26aeff4a3e31671e 3940719401b6aa30d484715759640074511ae565fcefa29e61e92f1904a32c02 ff74580377f8e6aa800584d0b5eb0617d8ec4c9e14babae34e6ae1d926ff5b89 f8315a1936799fedba28a74aada7ac453058aab9e3e2b5675495ab4fcc83a6fa 47f40a354859a9836b57736b3248cb46f6c01599796487fd4823b73242262ef2 5383a39ce159bfa29cfd04b26a5baddbafb7200eb38d6d7ec8cb4be9f16bd7e1 8c3676e4f47a3c7a81daa9e523efa5e8dc9c746f0733041f7d867ce2e36563c4 f84b7be7915acac1f8c06fb14f9e9190354429c7729c0ae8428178bf7f4e6107 b5221cf0dff1632e4ef69453b995ca53c121d2d7cc553468ad146bd1c9bbea06 50e76331d0a47fe66c1eb92f28676e3b3fa59f7d03eb5819d47a9480c8813c15 1b9eb0c9767f67b96f71c084e36798e3fa5116805a29fbb54f9b6567a778b37c 16e8195152c5346e573b392b3be1b5f306279491740ebcf63a9e950caefe9bb5 bb28375628245578e3b8d444cb21baa712fbe614fc4ecad8041abf3d60ecfc32 fbd6e611c71ba929090f611cb45965c517ad9eb6c6960955d065b1e9bb7f7737 5dc055eb960008f10f6bb8e9f9bb07b157aa5c52bde095518902889965735693 30f5f9cf77bc85fdf853320d4c136e6c6e490358ee36e26cbc60b3b486c29a34 f7d0aaea22f868bad4bb6e4c3fd0bdfd387ba6e63a3d969939c6f1bb6946c5a9 91891e9fe084baf620b64309a0470ab43973e8c1faaaeaf61720c64713b56f7b 42080faf8b7cc1a82e309882354c62c6481bd33d5b5e31946e997bbc953bfd6e 73f98ed1b23262abfbb41dee10f59a1bd0612892f35b3bb065461a8144d0ec2a e0d4f2da81242e2d852fa972322ae2a3957545467f9b841bda6b9b72fcce3906 d0045198cb0a34fcf6ce7993b939df0ea14aa466febfe38cfa0b13c71ae87726 37a46b32888686d4881b59ede91bce31f0a9d6eda17c5037efb39fe0aa1b4c8d 2be2e74cd4109a24f493f6039f0699165d5201fdbf7b57b5cc2766caf39ecf4b fdea9b04e7e32b7275b5e5ec567653054ee30d7183750298808eb5d4ea99e2ca d34a42af563a890c4e5d08fa2f28b3a5931e70f8b72bd6dc380c71b72d21c9af d2d8f4f42f70d3c792606246e83d97982e5da15aaf7c14a0828d817bee1f4dbc 76a3e530abd9c5da99e571dac8f4a9c643ce104762efcd402a25a37b3808446e d599464f22ec75de8b6bd7111dd263ffc92c7ea5de85460ffb09a8bbc9a3385b c618e0729c59ca44088b82e3d57c60fa375b553324d068f528c70d611d3d645c 30df121dc89e9880d0bb716b5e6ad4e865f684825eb59a91d5a5ab05a1890be4 20451d1368ac5d305fdfabd6cf34a4b01d62be4418835910cc320faaad80cf4d fb947300ba362156dbaaa81c2fa4558f4389fb50edb5c3d61ece79db447defb5 4e917ac1e7f9e6f5715dc09b7f586f1cedb7d4ea1bb7eaba6baa4efdae01d5e5 f7b97e964e4fddc89524cf41f6198bf9c54678a377b6646e923150c419b63573 cf4d56f2256c9582f403e75c677fe8e3562a4995ed135b41b6f51ab900fe3f1b ebda9db9737b1068d9e460fd5f80d0803fa513b69fcab7a1c934ffc42a123358 95feffe9fad0637ed98c574d3ddf5e82b6997b0fb7e4f8976c82021cf8447945 2cc8435f867c5edcceb1a511e9b71e1068efb84df489a242c9b4c504ff1d8b20 dd16a066821c4d4ed2ce355e0dc0b8ba4182366b188bcb9df690051ec428cc29 a4e49b864a85be313abb0a3c98a1d327f4f9c4befe902d7283067191eca90744 193b7bae7d994380c87d6c3ce8631fa4cbd0af02977405f17a289a3b127c7490 33ed9015dbaea1f9cf41645b79086b2e7a8ed64665ad160c23c60e929bcebde7 8d5603f8ae840815512e3f911e3a60e360846ee70f389f8545eae5a690a5f711 4bbf1861a273ad04ca19956389d9468251a6fb134e3679bf593f95e28ed5e227 2a25c313a4d8bde0f1cf51db91f47855147cef735fe8ae0ca402b5595621a5a1 fd1073caecf757eb2843b8b6ddfbc104f92a9995e00e238c29e634190adcd385 6da58071a57b68793f8f2d7d8a100df5754a3c999588481bb8bcea505ae7b51f e40082cb23b3cb00293b6ace968e80566e89af38ae5efc0e2e188c0664b52974 1c0d2e82ac56f470e56b2f6c3ed93adf11e09d60e8eb1e04190eb495ac9def3b 4d4bf8fa42fad36cf5f4fa3e6cbc85ce648b575c8e3a71a5149a2214b820182d 9f15df7b757818364914e37dc2334c6d3a890acbe8f09c25c0ea4222864982d9 4f83658cc4751a9804d1fb45d2ceaf92839add8311a3c32f77f46b9f7a36d363 144522c1aad24ceacbf56bb62df164adac321effe9f2d8503922c857bd3ddb86 0b60c005962177b6c6a4c5598fe5af6478376a6e4b2c121378b1a191d234b6ab b00e0d88c3edd6df52ed894968b6ee88e8acf85a6e71b00247df0d0fb95e21ab 69f8dee6d8185bc8883f39af4e9695d194bc8648794edfb788e2d669e464b64d d8c9c3d80941418db537ff66e8565d2c485487e1ced5d37971e8b5bc525fd5c2 ee2600a552dd8dc3dc446b675b432411edbecdc3d7d89e58b29661fec50efb07 bb58d378260584b5d8db3889751d8d5fd1c94afe2c2c5b64e2f22e3fe1566188 a8cfbef5194e1555db2354d49e14e63bd00caa96bbec45bed9b3515a49854a03 5528effcd7205ee73fdc0285cab598a7cc46e56dd5943e4523c8567664d9b577 1a81084425b4f039931ed57cced8678c32f068b4f1355bf10659aad23ba8f7d4 27fd5aed9b16ac1aa93a59708ccb376409ecbe4bfaccb69d2df4bca0c32432f9 8658944635ec3d7f96bacbaaf48c5782a78246a76fdfb8c48f04b24f85554286 49c1f6c14061053c4dd5464ad1839adfd4fc4835d93fe1d770139713741f49ec 91b9e761396f5cfa40aedadaddf48ad262feb0b4a6d20e9138b36abcb9af9604 43164e8d068f62dfe242cf651e5dd4420e1538805704b25a11b59418715c6b16 96883546b23f4e0e61abf504b408f0737d207c96be4e80afeacc86fbbf065081 384a7205b31f966fecabe561a81746d768effc6b330ad0fb5ecbd4753f367d12 72403aa68161df0e9c6c2a27c4d8ab02554ff3e5a0a4e8dde191e78c292eaabe 8e10af199ec9029475752948f7b49f0737a8398ca190f2bcf73d704094ec11c9 2d126eb36a38ab64f3cf84014874fb62711a723ea8ab39bdef529f2372a168b3 57bdcee0c618c5028fa2f4fe349290bb2bd4457c9e42dbe8d75c7dd4b469e1cc 78256417020d017381fe03dce4eda6ff9cef413b53b71d645765ab97703f0e92 6a74b3861bb621860e3876aa34d85777dd4dec6d04ec7c3c6f3191d823e061d8 f7d9638cfbaabe1a44a1d37b809f28aafcb5ad949e3ef898d37a1cee8e80d801 12f11335e31264cbb4b1c2276151fcdb6a06f16c49a58f508f3ae606d3cb9701 fd5516e0707378052cc8aed88c0c43abf0c62b971ca1f1b71eb446d05cda1a6d e343be94cc17e0c548caf20a6a20070f0c259338a91de9b8bbd7610cfb4d65ed f8390adc48a2be7eea498b1060b6d264c11d8c66243594f40eef26aac96e8da1 86f5b2ce25a4e3e639bc8b89788e91e3f7902bab59d5ce18d99aae1b2b4ed952 23257e23b618869b1b6209da4914f884abd0af494a58e8b4899bd0738616197f 330bd335f86968c3e3854c2d0e4e2ca25b449102fbbc98f603f2d1abe1650ce5 9160de498c4cf22ba92967bf655fe2d27b9f5970f45f4648e71826ed35c7b33d 7e8e206890b1ed26afc8d622c06958509bf55f4df14f765732a18944e182ea3f 293af774a40a417acf4b99696c94004fd522d469f15f63e88d2307da54002ee9 595d146fb7ae2ff82112f78b0ef05495b59012b5cdaabf71058a4111d04617b3 501aad7830362ab73d843fcb32be652c56aa6cfe80bd65cab9899fdb2ca31138 f1213011ecc07c4a6a1482ed00ad9ba8fe09ea16522784cd63d7a14927705a93 84aa0bf0d80453bedcc47140e336bfa67956e9fcaa887235fbd5eb41685bc062 7c9415e24cce6eb0a4aec7328914d0f950bdc6314378898d6a52785e0a945ac4 ba152ffd9b4d46f4a7b7a3ca135411174d3d53fbba2a61d2a2af608355ece7ed 802eb9032a81dbf06b4ef4e0adfc730e220020f26d83edc1eb75601fcae61f93 f61f204d5bea2e7be2de392bbe088836cb681149f14fd683dda5a0121e80d339 f4a6fbb4974eb758b7430057e8f8bb344ed9a4e53a44e2a076175d2493349bd3 b15cb31c86a7bf6e14b265c2b194c235a9dc40b1ac205e4d5adff4f04de7a791 a62db04983e50080ff8d55b3735e804470ff682e41b5160b3b87046593197255 4496afc569e0125c18b55b648ac726c90189fb737e1758c9ef787451bf332950 229792ca29b00b7f1d681d1fde90df5b60d8adb28828197c83404a315367c8c5 cc5f0e45265cd20028a439183c7f0d39ed099d181976cdc3dac0781a8d57deec d9841047daafdc460edcb40ec8c79ba83455aa8ba26b2c633049381acb2ab924 c1130e6b24d2b4b0baad27daabc2e00d01a7a613f5ae0b9b38ee95f929e286fd f103657a61d4d0a61dfa370888885630a39244f0ab4b517d51e54c3608cb0701 01f74871357d5d55a650c5a366f7bcdc645bb09188555d27f4af478257ae0a2d 15b0e13b301c6b16cf7ff5580749b1a3ee2c965a0887b4088141fe7840b9a153 7e3aa0764b8114bf53d9004b152c36f19e02da4b11e330add6e1d2b1d2817dfe fe144d553d4a70b2df24b74733ec7c8838843c2933d947bf068f448c2e03a601f514f0588905c40d11ac8a90d3e8fd58bc98bfc973c2cd74fde7d42f11a83b0fd9432e68b8d788d326540f56e13df5185046c93e37ec77b68f0dbb8991d269097d20664bbd94caaecfca68e5a1a625c9aad75f50edf3ec00a6e23b08ffb44d01057f8558252119e12833e4fb64890b220d2c4e6ae8df93d3b4f94a11d7d6650a6caa16ccbe805e119699645af2ac9e0715a6e1aff9c82038e32f8e3add7b1c0ce725cb241b3167b88da3ee803098ef7e56b80502c05cbe09e77a226176eb7905fe7f084c75eb7c7c23ed58493f778d9dbb1a6bc43d405eb47b800d75457cce063dbba78daeb70d2a56cc7e0940daaddde1ca8d61cea4380efcc49b12f3c10f09663377579374df8e8c11dba7529cd99a93ee60f9af01a8814ec71ac43ed733079b9484bbd6b22abca2ef575bb249eeca5045515d9f874b5d3445972b8b496c00475dcfc55fd7b16a0da04ea2e0458fd839cabf15e90f0b019ecb221a90e0210374a288a1ac550aeb3986d781b3d8bf303de91da8a7a13853ace62bc0a1faf20d5c71f10160717fbd21467e7a624f06a42e61924f9f29e1534ea34dbbac48570b5fa2adfbbefd81dca99359abd10a7681ee6f460e9eaa495d0d4ef7fa9306840624d8223d1bb3f039be5726925a1166856d3c5bf6815913f1479fd029bb1232075007ebdc400198dddc5b6a53dbc3b8741404e2ba21f47decc139b896a74d03021e33352e23de63d8a704975c9a78eb2ab1e7b33fc4cc488272aa2ecffb6d830ae83c85e9b66d341e1c7d35c41581f0ba0ee7e70e116eddbac2fea3cb01e32c0afe4697a4c139e3c011b655edd12d3a155075f23f2c77fbb21331a9b50f5762017f07753b24dc14ab0117915f1cad30c9c90d83949a9c6aac0e56978395b77c04a5205ff4536798239f7dc9a49d46f2fbe507980de8d172422844ba64a249f902aa181e52bfb3c72c9fe97a21d74fb8050b07d5d7e41fb34118e643f02ed169094e0d98adbde9503a18faa3e6ebc9ff7647655b9793e8ea1007400fed5b30a40449dab96e1b2b1a63c16c8053eb53ba000c9034f76d61cddf7d4c2adbbba1e105e9cad7ccb0f980ce6751b2e7c58aa76fa4bc76d653173690e24effe5ce869a0587372ac8f5f3314885dd031b2789a6f2026be5b0352b72808bb823b80f9b07007d2a3c51e187c0cc9ed997d194d4d0763bd32e3c2b1c5aaeeec20041a330d90cd0c3d0253d7a16d7a2076afcb428679c6e6e78063e19baa043ed398f1a79030e865efd567c31b37ee2bc86c5779c742f608a02cad9ed09ae4a80c203ed81720347e545f2de25c82dd7f7f2c892a5cb279e7dba58a5d7e654f577322e3b8dab08e060c83f7620e1a3858233bbcfd58927e7f82a9b2c9376782c940feaa667d908510fa0130a23b0336cc7cfd42e06dc03bd7817a5a9d617fb306e0b73b492380da4f0de1ae015de1688cec9dc626dd8236466981ceebd14e9a2cb1a9b4d8cfe06fc5a62cd46a05aa5e8fa3382e23c391f64b4396e2a20932b45136ff36a4e390dbd04813e80bed981c2ee5d225cbd6d1d113cae09e803614d3310980eecc2080ccfcd2eb3d2c14a5e2c15031aee250adfedd95801fd18400ae3de8de0b7824706c4cbbc259b8fa3450b6a520b3a81cd29044c1c023d095668ce99e06644d1c409fc453a4e9a281810c78062ae97a4bf9d9f3e7629a331ff1b4316ff6368b56502c14bcb33f6eb30bb39a0df501c700d0929c2696b2d538d47c05891a77e31cd0235b85c64c4d8559986b6de0b1a972a95d0c2844a1ee5596c92e85c2c7f24fb0e90ea228cf69dd0ad467cdd5b6ef6b10ff9cd858d7d4f145ff96af6506d34a404468112b3594502037d72da3d9628369968c5d5c2b675f9ed001b188dd34b6b067b182fb0048d31a9b4b90b4feb3637e55cdef83012754f3d95bfac6d0cea90022f3e9634c9abe34b0e7e94778f837105d9979b101e79f6d2d2573c07b9cb720fbb834cfa77195d9674ec3303245789f1220f53460192455c772c0ea0f6a95a02ac9886806ac5453bdfc7db45d52b1f291b1f2207e471e3a8cf102c4f50f0de01b27800d63bccec05e08a220326c7b8f7da02dea6a6f4cbbfbc31bdfcc334a104b869970cf3a8b33e2fef7c33824818d8f442ad527b6858a33ab282ab089b430bf46329aa0a661b0b9430ad7d55863d3855605aaa38f3e2197230b164625ec80acdb2fe7a0657bbf07bbc0d57d47cf79bd1e47821acfb898a6e51b424be934307339f4c78df6b244ef423542e7e7e8da98724ec565c4d2d0e7177d0a0111c6b0dbb91a54356ddbb97418170d1710307667f93461dbbe1a997cf6fc1f94c17ab0519cc62a676ddf742a229974b7d5444a19562309436941b2f4c8b807ca1537d076ff4b8f684a723ec293f8e1f903418b12d4d943137e74cda82a7b9b36ef8ff0a8167aa17061fbc697e7483e9c5f0b6dc2f1f886347f83d75553e6e6d30d31305ce58649412fbfacc5c7f83a3bb211cd53c54146fbf11ed91c4dbf62b962cd1083b91aa8f823718467db2b054d81117992492f8f6995d04cf349acec8dddeea08ae0ae1f15afc1a24f6bb46596b9eddc035ed41d61af087e5a532eb37881aec0744a8df468a50af43781290cf2125b9e40861f191e4094c867a5bbb964d8a2705609b03a5cadfbb2e967017d01bea1a2291fcc0392b643fce7aa7d8282ad8c20a2aba680c13085970f168513f4643b72ff92e1b95a0e68ea3ece5baccd8055308e0da1f8da941a2b1ff2f89a66411639aeafa5dc0ff83c2592f3ba33a6550d6069fddb7d2368d81d36a4cd1c32d0dce9235f8edf7260f22d0b046084d19ee820ee088085b9127133ce6650aa75b2d49654cd704efa0f6bc3084dc14b22abe3508af66ef531ef8d9b46f6b0c5b489bf0cf95497a87aca9e42cef278374cbc7310bc7eab2344418210f3a355105def79d61c88e8f64db9b47c5bcd902fb7b504b04caba90b4227fad90205bcf937f0aac8d4dd2b554399ce49510ca013135512f053494a95cf50c7a1987c6b9947b7b1430dd735442f653d156c2fcda4f5b66870d22ef25d0a48ac6f6d2946281b24a43d64d307c2c01e323ee3380a13a253c1e0e667b23ae49632778647bf6e82a9b87d8ce991a04dd71bdde8c861c43a7f1810d68bafd35e2fdacb9e3461e7f185a052b9b74ca1619b15eb2659017d2812f6606935f3745f80c46c9704b8a686d67986bdc715d093c7b8fb4aaedbe9c06c7370d145e7322beca1559db6561f1f2d07925dd0c684815da062b7e734626f32ae4005d50b706140fc4c151c1925ea62bf48841408a7d8b011c1d4889b27012387f0064191ca12579456caee50fc5d9bc9eb5f494d0acc94d1b9248e6cadd8437360bdc94a48332f944be1ddb66baae0e2caccc4834b84001ce3bf43391dc2f869909707e2bb147c44b73b1200751500fc30b2b5425ece5bb4026f4db056483907d04e7603990c6c9ac8350385ecaa4fdff8d47276b45a08ecc0b29040939c112c7012b2cafd68fa75e6ea0b89586f853a6b936966a57e99b77fe7d9593030a27c10e785c15d299d15840d7175c442d7ab911dd40e365510a5bd90eba2499437a920ea3322009a6d34969d5ecddfd41ebf518c9a103387822d05080da3efe995cf302f7148b031158804711e2e44ba940e03fe3a1ae3ecfc7bb9a5ff4d6e4fc8c0e0d2145c6ee9396814d1ac409a6fbcc7ace8c42d87d2b41bbb59882a3f784525d05d2a7bcf8b648e9204661a4571e947c938085bb103e23dd6e6a215fe9af49be02c49b6c3a09441d6893c4a99e32aafb26e3dd1b6dbb945526c33e77278702680bd4929b5ff42f37e5128259b228a2059f618a1acc8b2103d995c3f998def139035f18ec7d33b9055512842c6f0455bd7c5de85461a53668a65735ac4561a6c9071e021990007b57cdf69bd1bebaa2949906cf711419e9aafe345369c146ba430ea14fb9df83110dd4910f1a07b15aa02240a054e7a27dc69b84c9b7a55ee5d30388274a1242821807d88f29e29b801e55b155178e640a7870cfbddeb8122b9f080ef5c8c042e44be5eeeebcb0d65e65a822bc0c36ddfb49a0c6bcc79420bf80041e84ded13c6771b628cc2d9cb3dd8325d6893781940d4db15c46d24e2c417b0d9c4c82ddf367055564fac8bd98005407d45216675e7c0416ca705c0b7813fc0edf3f76de240156ebd8fbd0a179e9d88547ed4d17c32edf274b870f41da378402f09e35ede4a2d06673af713b97335f3373e837ae8428ee029249e7e602299d02ade5aa4489ee97285bbe1b193f733afa954e5f3cb8241737def455a763a9e909c60524fc4ea81e433755535a46efe0e00d754ce6b1b8831f191513b238915c0dbfa490833f7d32281684941fe2f0324f839e1a681e9f82980c1ff2ae43ac7c07971df86ed6b37967b3a25b19e93d6cd5473724ae1f06b76ef78eb39dfb6ade02c75efcdaff2b14d8c6d0c940d5f0dbf44f6529e90a51ab04ec67f99d3f1938012d295e82d43af11ff53a383117d6479c043c0f2ca5dfbc0a3f5a50cb07fb0906f9f747fa7d83b6f83cdb91e3f01faaa343b365c6b1d6b83ba2f9c73595c2710207d4c066c12839b7b4c4971f1fe6b91261315cafdf850f990966ab6dec11920b7054bc9937209aae738678aa533b6ff2e7aff850c74e740cf3d9de43366f20064f3ee98162b89834e74a19371b97fb68e474fc1d7feb63e992f0edae7c2d7905eb4a28713c8e7c5abf19b01af3167f5574bc545e5077441c20ed0ea125aa3d009c1baf4f4bec6a7eacea01df63c710c371994328b1fa5b09abf8468994f1d00e6b9cff230824567baab294c2c91222a159e84006846c8a08851fb8ff2a437001b7f9864f9460c57e86f081dee098c36ee89e041e30370b11355bbbd284732a0c9f9c966856c54255535d2950225221773b60fb0175f7f133d37c8c643cc634050514ca9af9fef9703c035500fdd75dadc2beae8f878ec6f3dfccbc131b57390c008638fb224928ac92307ded5214fe3dba1c8f6f02f9c038a32884a38f2df00a5e6145c75421f1ed01f7a93d91f4b8ad8ae88cd5693db3c84867808877dade0722714a5c8fc301f653c8e00cd4d374ee0503e065bdca7b59eb2920feb79ac00548e5dad1b4f8aeb5ca29612bbc155c625ab1e08dd418544a69d886313bfc7f0179860763bc2f72da8858d95c052057cadf45ca29f0a3525d1263bc581bfdf602cf57aef8523ecc9e99508a439b4468fad718dd03f51b187cfbec7e3a592ace07fa87ad4395b3d93adb50fc2407e9839976584991a7748ccc68abd91b17174108d214ba8d0f4457187b0ac701d7879d2c2c4b1d0965cdde058e7d105289a47f09e33595d850b5884a6029faf3f1a26f67f35f5f3d6fb9617693d70ed19c0aba0cabfab7535c2bdef8438682d5896af0e3ce3a449c451a7ad2044f91f1d8041e02dd1a54b30ac06e54d0932d914531f71f3362ccc5e7f1011bda41c944c4891e0485be9fdf1b8483524e972116428378530512fbf4ddc47ebc4a9fd6ef95947c06f8843f7ba293e6738eb3274e8d72b2d3c546ee22c18e92d019fd3f99146ca7089023b2fe35dc0642197380aab060f8e55972872dfef075b7877b3a14e6171a0882a17b9df509b8b130d968b5cd28c714a5586861036e009159858168d2478a0bbbe0a8181b9b21d0711eeb5ed17967d83e3f64a2285e3d6930a187449bcfb90498f65a172335278935191fd860e9ab691c8e1d82c60a258e7f396fe9c8f407076134dd5dff6908cf212929ba005f3a407565c54df3fb7af82e9615ceafabee074f44564d46ca9089d0cfb9c752cd774ccac92e2e35bbc74933279365dc29840672a55bd56f8e548b926bd6646d9b66316e9e7be4a41df28e10c29aee63bee30deb0091a9241837ae9fb7040daca79400c6a2a5e2f3cd0036557fb580ac1384057bbcc3d32bf383d1b7945fe7b79905d70f7de00ad32a42fdc9beefa8d2b0a60f1cfb945daa85612e17074284a6f87b5b5ed437ddce5623e1efcc40cf2e5bab07e35bfb5079ea20f391bb7728f592e4d9fbe3000309ae8f1f6dcb170f2f4efd0c1ed6524f8942d2cc867c6d57bd21921bee3b646888ba65e840591701e6f4960ae5628807ab2ae639e6a1b4828701b02c46cef43e6b88034c76635026a7ae900e8a0bf7f1b17112950c111958bd3db51f4857b6213e39510f58e0bfba9019e30dd7ba1a642ad690612b53afbadbdbd24957d958d54d003c3c73634b7605d6be0ea8d953eb836f0409d7030c103cdfba249117ff8f7f0fbed2827fa5b8c9d8e302fc2b45cbf98fb9cb219211dd6332a255d04c2321e101d7e3ae341679cac4ed03b914c0997970279c544d7a8accb905b2ce4bcb587fdcecbb96258d118a42c80d8e424cb61dd600f4f14fb17dcf8d18a39befda43b64167ef10991320a139c408c11da2ffc3b5b53011170d14c12e2630ee8de8c6287341d49b6e82e8fa9a050b4a755bf5a762e4a39e83e989a7f5ace3e393bc0cd7126e37dee1f93237290f0c2f8d151696d0940e04137b8c74bce268c50d4867d229c02f7e4c57d46e248f0ff91619d75abccd5f7b0b8cde4e34b668f4feaa0dfff16bbc4be73583b22f0b0ffcae0f21265dd0f4231874e9c58f59d3314d034234b2a9a592b5573d8344af0d20319de884a2f0d0af1785a2d119ff059916e0bcddfbf282262c5e7657e22a05ec2822060ed2088178eef96f4e7cd6b6a42ab3946732a9aac0effa670cb3ae0535b966fdcf2805c7b10adbab930e74cc5b3c2c57b5c6a5890405a459fe93260fe36a6fb6d8d2941e9641a8be401a7d27196f18a53357239090a426c719f3b305b990023ceb34d7509536a66cfe17f9ce90ce761549e33e5d34bd95a2d55a500d5a321002149fdc9f8112bd15003b1077840f2d177f95120fecb6a228fe8ab300e3fee4c9461c79ce42458f6daeabd482cfa282172bad82370aef675f302be90d288adabf9aefac06cfe002356ca21e41b85c9436cb4dcf0f8a76eb3ddf764002644a3eda6df41dbe2e5fd951d0927a2a6b07fbcdf51bcb18fd371704e9c58b0a8a686abc7f048b86b914adf071e7211a89d219e3ef3816f0b1064023908bec0a376a2184db019c1ad37e674dc4d61d219661c6cc70e7c17c825ee64b2f77c904fb931c9e3f90287ec929d1a09acba922cc6c9c04322d89c680b6bda36050730d05fc3d56cef9a93a2daf99c6045a8a51d6a80be2ebcce613124c82afc0786100725bf1480b16d88416eb9dc17e82fa337b8e5ece0cac071f9f6c7081750c3701f014a7b6a80db61bbcc655abb8f5a04b3f3f2664510d0a2a7bcd3945f1fe310b6826dd5cbc7be5b6b9e44cafd6bc1509897778cdaf6d2be7646461350624ac035669e198c5e161ace24107dfa7239dd871581e26c3753a4d6ff97f2a9a0d5f05e2aabe78d1361d90f6e961bb26c398390fbee103fc90c69ebeaaddb563d4530c06e6d00c80337430a03924be8e12903cdd1d80b195e43b98c636c303cd069e0a04f3ede39f661dd9d29f0e9ae1d8978ebbd2c4105c462c231cb2c69091d6a20d9cb7cafe9d68d8fd844093076b6a10d36621530c439ef37aadee1623661fe10632149e45b67040994416845746585eb6993ca115a4d1851a3a6a917651064903f9f39ebe6a9e58dbfc1866d3f48242e5a085506362a38fc6962403853e680d057fe7606a4d641e39b2c678f08cbc194847fe0f9fbe0b13a326c3d7e13f51980cd736805a64d40fa578cf1e5e820202811e899ed910508b330505c43347d71a076dfbbada33f13a0fee166ff321156e50066074f05fa708ddef9ef5a028836f03932fa4266b4d61f2cb40e5e8d0fcb6da4de2973351405ed3043a4a74551e3d09440004d71a46e982c83ea554305a00a758624e646cf8ea07ce9f5a73b174520aa9522c4315bded49ea7d3a90d96fa8de35ce596cacf32848a1f87d008a693a02cad1c603f2a7251d3fc0d45cb56a55d8f0e1248e67c31464281bd69c9f73600b6f332788674021c0c0ad9aee6545525933deaabcf5cd5d7e580957916399210bf1028fc19d68639ca0db6db83ff2818b9c7ddab6f029506fbe12b6272b8df808c1353ce080ba44f5acecb3dbac6ed766c74c9ded0e103f5b7778c18bb2794e0cf81253fcc395f13e8f527f4e14d641599a4b8f55ed49fe2a823af1fdb24f03046a08c9ba87051d39dc718fd601b1d25cd414282594b39fb46fae180978ab3d09f9e01221a0a0492a541920052d130509f0ba793d61b32ffcc523fef074c5460700513a4cbb1d34b3e3f8a9abcb95f15991ce6c5b0cfa81429f9609fd9456df0ca2e237eeda0dc6ef6cc2ee047787e8a479246570468ccfbdab912c7c296e9d08c514c76a4ed75d7b813bf64f2f8c575fa1942241e2bcf89df6f5b6aafd758a0ee38eabfbe7115870c17afbe47dcf03228a5bd8a2e52865216267b8d262d0420f726664e7559a710db1ec62ebbbdd1e08df8a58a1b320268f5ce358580675880db5945932a15e986fb3deb2e8a8e27f9e85e30b9cdd28f1b02255e98e23c1d10f31ed7db4a7c20061ea53272a9c0277f8803b751a38df1948fae8a5a9bf1d7f0e13ebb35f2e914c32dbf158f65b0283bab091d51b1932164a3103a8d290a144040fff7f4e5bef64915bb2a68a65afa4caa6217e8c6b0f706b3abc4a31dac1500026df1bf7510f6319816913bee17a87c7ae024860f48c262819b6e7ae7a9e4408738c5f1644ec66406fa4f238700dd7c39da8eea6347e7e829253877bdf896c049546c0d2d1aa641ec7a4f929f49146998d14a8e41bb7ae71c90f1b00a7826e050ad7281a48ec2460d8c9e5408b1cf2f9efe878d6fd6f3531229da337cb13d408264adb74d362a80e0f03c5b83d2ded2c35cca4b763980aadb75b22b48eb96d0d5f156e5f0849e9a71df592c350728d4277e519ff42e11561d10161ba6933b50823cc4c14391ff8613ac13f102f380b2ef89ef332403e405a2a7bd8a7a118370db0788968c4468d1cc3dc1416ead42e9fbd10c363489b70b0b2787241b8c9bd06c80ba075df3536eb9f54cc83034625a63445b662abc4f43b46f585242485a50b54d8c125e6f8c8e5693ed20a0f46d48242c3a9269e9bb2018bc4cb05e5306d079e17814af19dc0a80db62a8ed43303cfb1b416d921c840eb941ef6cc42b1c60a840c8e984a506d470b7eddd50fedb606a56903f12743681162d96202923c7f0a3d5d770ce0502e47d32f91c2ee7b048a39e84b19ec9901d5cc65b0d0508de10e86f75765c9f783e05edd62d9dcc0a40ea25478640bfac1a04c7c99fc834aee018b5fe05d89ea12da244c5031d767b23f0cb5f7e387950adc154c5b7747263e01621868eeb8029dccfea0d8a1f1fbe0a6f9384656c1b17cd45fa8860d69860803edbc2ca579d8396889ba7764f2b255b4254d9e59dafc21d4d81fd219136f9a0be9acf51c696faa37efb889b71432d08722f11d5e817e2ee4928d543b0de63f0eb586f0f854979d72b08aa105a374ce875447178242c97cded3cef0ca9503bf0fc040b7a6b1c286d890c68a1ea319448fe49112350756695f83eb91d20211090b69614092f619b68ac31c03d7663f1f2a2610074e788e2ba732553d863924750ca224b6696838343710c00f3b6271f594cf9adcd15083aaae99b36f4d6ef70f0999c096c4ad15ba9d6439338974617ef70f79d0050ae0598de7a33876ae92b405b36e19b9d94a2092ab58b3b8f6f2b20d78bea8562b4694f8f03eb056569b5405d96c8e60d29e995fb9eac4f26feb8c70f4b2923463b0dd5454282ec510d9d2082a64063375f513f1b8b8e41042a0c7f32b49966d184e31e3b46cbc6853ec320f40c028009a0670d553aa0f781b9ea2854f1f123bc54b4c9bd84e6651774f7c05dd3613e57cff0b04630266de5a02df62e0c5b158947f5aa3f558428e9bbabe07dfedfeffd9afd2fece22bf8c18bde5501e0b7f678072b770a3e8577ee4adbe0add55e0616658c4e4d5755c9ebb20ab32899320f4e261bd836c1dbf6c6077ba0245facad15aa0b78de5f6dd83be4cec1e9c343de646be1cedecc14bd997dab606870fc0507036f7b62cd7bfb9cfacf4ee4f98d794aa737d3ea11e300ebd23020645a2597cc468b2b150622032067a40e970a1949b1fb7b7f1efa535bc2778c106707281e3437f09c00e13cac0dc927d0d08b13be0dd14801b85a7557dc2d25a0f81de837f2dcdf6d824fd5b96f1f70d378383ed23a395c680312f149c73a4d1003e3312f1df85cccb8a384128396690aba933e7e55435e5c1a3e94c8b8ac62904f766fc93bd28e77a6ebcce9dcce239f5f05bf21e04c30693f93c48080125640f1845835857a257267c592723f48465657384895f92063d45367d4cb4e617550f0ee80f23ca2af82efe640e0317dc618bc9925401ec678c9726c21ae36e47020013f806b00255fc56ea7a127ec6d29024a564d2dfa1db8c93ca224f1087daec0902cf268c9570219730d77f4fc7d704bdb9e6a853b68a612f3532fed9d0efc301189b160a34333fe5403a9aefb23f28ce9b6085f860ed5e9edbc5dc98899b6707faaca9e46ca90305a4564e0420e1d2816b0c4df40408c0fea00d1f6549826009c91a5ff3450dd76c8740091b7d1e0da2393584e58bbdc3cca4ba600aea7d700c8e548ca16ff6e0c0b96b7c3665f0284a6c715ce0f0fa10db843f2a984ac66c0b34d359ab31894e4042b8f2d63796de876fe21b9e401d4ecf6f64fa6f37e96f0d45bd46d9c740dff07629fb27bb065be874c39ff056084516f60fc67ea19315095c045ddb33c66d66d85866f4444859d6af19f8ad33fc0e5cb366db66541a64093d57005f6902ca4b6bccf6249f1c037dc2f46e44a7417695e0798bbd95ea0f042f068428f840b205fb8c6464d46d78e61a5a752df72462f543f502757065a60ed15ca64aa6af287f26f1f291ef1cc6b944db1fc788c1cd5e59edfd96069986099ab3fdfb8c2629e3370782beb51091204a6196358ff9e6ae45a1653a23534d0c8b539a7af2f68082133856af7240b0c3bb9298d70cd2b9c4c30092e69d010905cb1b8af80832245acf0de0ba50b6613a271509d21510f470df7293b5e45def0e93c132627387c046723ea59c349bdb49a4eafbb69504860d02496d0220aa6906be75a2f7e5ebe3238ea2093e5bc746cf9174e36af4bdc36a714a1b9856a50201bbd72a15133c599d03a1019b1f671acf7f01dfe4b9dab911e1e19acdebba360f092c6b7ffc3552ae60e8eb3ba9aecdf65e36787cefbe2e236d8280c08ed9580766dde3ae3bed627df1ccbea0fea57fbed09c36e26b3a1633b659ca0a039c060a4fef2800db9641382eac66b45ea36a76ea173192b690414981277b4dce78a305cea0096864526f97f2e26e5688cc4876ef71f589b15a05269f1cdae4619cf10e18c37aa3b1cc6528eeba6ed41438c87fa7980317b1e49e8ee2b432783f73dd0a46282dc71c52041f9f79847eb5d2709549ca6320e0810ba1b716638f9289240d7ff0b6a4e0653620c5df3a86f61ac11c9e97f19d53a031e8f4dd0da7dda72005059eb322dff11296072509b8f247e9d49940edadb42b59719ab7968b6784b007e09ef3295aa4192f9a0635609029d0f042ba8933b9770a41516df42ed91c3b0756a3cd08f09b6e062da85f06a904c151316774a49715e428ac5878156e781a01d900e57ee0b4c637fe47d83d6631afac64103bb3530a0800108df5461669780ee01aa0df24993284c791ddacffc49fece95933b49f3aeeda71beaff0de4358057ad5d7486518aeb1066aa87eeb613bc93549100ce8213df1eaaf5c5566b8f101e3e5abdb734504c12d5bc9aca2d9d0a8da34003c1db09721b7118963ea19bc0946e1a4f0642ebf50dd3555041bf4e56b15de6a1796968dcf02509a6245a96402153b460b8fb1da2cd90f768f615996cd7634c4c6dfbe4b59d693fe6a792fe40c0ed9620663151229f50849813bc7c60f2f7445fc0a44985ed1b51e6f4cee990e98907a4a5bc4f783f3268f42a2a03e9fa043dcce95fd6db3576c462d8d254608735c39c825e54d1671588ee90509db736042f19043a255f31b8415caaac48704c6acef9faefa23c6c3d824869d6a89898d0a77088f468cedc5580787cad43b0b4658291806ce8ce358923a27e17a57b792df4e0d0f493d9d876f36981b620401d8e0753826bea031b7570783e39f302d64cb4d1b919a74b427b7b2c99adf1c07064dd670e355e1531d09ebf79b83e39b9ca8f894b71fca94747dcadec9b8bc00b63ce6236296c3d2cbc978fb31713b8211c58c256203b52ed0a5c1e811e2cb08eb980aeb1138240a0ac9bf69e3684a13223bf332cd1698dfb85336b322c52c01bbb010af348540d8adbc4c89b0215df4d8bf769914337ebadab0becc9005830275b4483b980d0b0b09edb29855a1b8fac78536b21002c07e4172564d834154e0f1fee57ec9a0ede24960b1c08d8a9d4efc2fedb641d35e37ccbee2e5e309fa085d2adc7f9018c114d7bced520374cbe67434372e2cbbf5a8963c95c16eeb9201f2630a82a07ec5c3e8e2872354184de9489b8a6d09d61177292702d3ca87560358a626bda9589729ef841d449cf1535f081ffba276f1b086af9d6bf5b7dae502cc6358a7194ac1682e659885c1d4b05a0b138ec5f8f78b04bbfbeafff33fe901a4b4bcf69936f1373ad80a0cf7c682c4dad8d019614ed994c8fe2f28cdfd9c0df6356729799d5e7b88057c3c80a587dddcd79d081336704584129fea1230f6095b2d109de57bcef6af5ae38ddd40cbd639d33c2ee049ce74c9fe758c92a853042f6f8212b897fbbac6d13d8f0f895c59162dacef9279fa2d93d80e35bd30e60b8385b709904a3305cab2ee8d0a32561e7f85495b2a7aaa8ec58f2b369001d7039a1d371bd7413208b8c19078925fb79213ec2dc2e5911540a09fc704f749ae085041be4f8f8ae25c5c1e6deb6e40230e56b113f7d3235810061ffb3beb41660410b59b83727dc970cc220eee9177be4519f789ff98f27c3645e99967814cc108165ec49d9706678d2c2a5bbf223711cd66086c01981253ab9cda5a1acdabc1008a4573ce88a3b4414c08c7fc87cee99692cbf6fad8ff04e7d69cfdf79cec370a9d904abcb37657bf7e1af117c4cf5979abdec2592ccf94936388f627d30c3e08987f664afdb75f487a1dff0b51aa89e06f825ef5f476abe5b67457c741fd14074d20db06e4a7078030b4d9aa6a60a074464a46b54a5f11b099ff9a82856ccc0dfae510ac7302bf4a1d504c5af1d33c4809dd87d92a8e114e13adef53cb0f79016772e42b62c1bc6afbe3e5882abe9b70719eb75348f967b4de19122856b54b08f2e23cfc0d3e4383a5b0da2564a609ac737433ce24d2eba836afab79c074cb0453d40a3b875077a009616e27a1d8b015fce8c0da14da830f21093dcf32ab1405cf06168a342e6441430da18fbd37a886dec4afd2a4ec566970cd5a4c4ad8ea0a1c13d76966a02d89ef9de931184323801678e6f29628c5efb2c26d85b6f1f4014758cdf77f2a53b13b3f47fbbccde542c74afd33ff74e7105188f46d499f8d08e1beb5b2275a7c20a228720f2c610f5a8a94499d6e6ca97ae3232c6bfe60f70de42b181cab021b4557b71d32c3c71debdb457078b32af05cdd68f006ac16250353a199793b6862d652629ac194e2ae07cff755ea9f657576fa94f4f89ebe560f88c1570aa5fc1dc5804329e22c0c2a1cf5422fbec4eda31621c130498e4c670ff339f63d36680200347e190288f6483742c91edd80663bb3fccab9ae63062e0b299303065a12064baef73889666f4e0e8ab33096f55fc10e518174af90995500d202b4d857f015e30cab803346c53ac820d95ca9f894e87a34f06ea21202a8000aaf73c827c1572c6439423e84b041bd9d764ceb3a3c78c687842ee5fa821f05affb83afb9e48bc82e3fba42717aafd3c2c0e26fdef0773f7691c3fd63959a0ffa3a8412d39f71d4e3411b14bc0b60730e49e69eb13df5b780fe5ddea889e503a140e7399e813a11d86c8356c8d3ccbafd4c28eec703f096d7fa8904195a3e0c03403f7eca0da0db6ba3c3e43224b6a15788f2ab967f712f0861df862df1ed061dbd0512f10c7307695262657d89e6cfd761982848d74b86f1fb6ab6ae6bc909a87b99016a9583eaaf46b4e5ea4328c27da5c2f0bb557062ee7bab314758aa02b2e8d7b7042177cb687c878129b16bb6d1a466648ac7dd71cc73d9deadd75606e68763bcd885abbdc0813dfda82794a0ee0c3205298af6bc565d8b82c0d4e00074efaa2dc8a91564ba346c30d2742ec05d21110b7d761252e6b8ec045c775b0f296ef8b673796abdf022cbab441ff8c5799f1c20c9d54064f7cf679a640e8d0193d85f2aa11ce7ab4830b2110dd8d8efa7dd4e6938a930daa448daa4803b7309e5ba0d9a0b11c054de910d0bc70efc4b1ce95f3e8f9eb1221a347098ce2f7a01b1e3b9d3aaf79b2a133e530e26dc366bd84d787446e64c21128de16b471750000bc756518b5e0a1dceff7714b9c6014a348cf82041747be0623457f099ebdc0d02c4078ee9962c7a37f936d3c8789ab89776e0e46b0284598717f4baea32d0070e95fdd5f394c3237fca4a1048d6161746f390e44b33c8ec0c65d1fd942827097d76eaaff3214fb289211eeb8acf0c80ffba9fb6f5759038368ee9e82ac1580e82a59137db3ec589e20571420eb7cebe9c25c8bbf571cb64b5cb72f6fb23bb0a0ddedff9a58034b19bd2490eb9a568c8a9299762865723fc67f8173ce637aa0bd134711b77eae43c5649e46b43467dd67fbf8da8e7410b12ad56f729ce747803a12631ab60e4ae58e5ea9a720f739daf8f5e4ae8e96f988d35ebf95b98ac43006fd61f1a734b915239ca2358b855b593bd391b6ff7262383ed51fbb6c57a810e56a3fa695c5bc4b3af32d434f589d3752c70caa66c16a51d83c78dbb204a810ee5bc7f8f7e95accde33f74d773c2de1c03e61f7d4dc5d24bee91d475d1d4560c058b50d49ceb451a4ed77520018a29c6aef33a57bca59e46c4b2bd5d5b033d04364660268f2be9f4b26f57f81b6d000519bc3c9ef76d915b8f5bd514f828fc09ccc816c4f866e5bf84045ff7a8607ce2c827e6353f62c831565c33570e9bde068cafeb6fe7ff2611e9c6099ca39f481b62d881c5f0cf3d37e79c86752171dd068aec1a6b032b1cbb6e64c6bea5436c7a0e0db044760bf0107b2548d4af4d710215efe65924923c798c68811ac2ab9117e27fbca5b68ac00b40d752bad3fc2f0b77848266c4f64b0834d4a391103680e6edc2841ae3598b910c8380c1a309b80538f1a3f62417c3da86f9000eadb610b099cbcc96a46bcfc20f0a24381659570beb41809979c464933cc1856aaee940e83cf7be7907d4960d6b9c6a33b6748d0ff15881c84d5979afc53061376e51cc4f692c1da476777df866d7a66484b88b09bdf0a4ec94b65a09647da3b367672360b172e681efde18f6b6acff3fb7a1d20b5ad827e6189c6e7e14297f1df3c12b99683523622f398f0be072c0964d8b530af9dcc4e716552e25b4b20a74b59834ef52e94c0ae04bff8d9150ced104a3ea093aa4b7837b177ea2b63c13e7358b897324bf4b9457fa44f16239a62ad8244b01ebe783cd22d2f9049b7b5c584d24e114280158cb09866ace77f8f2416761ac07f755b0d3ef85a3cfacab2a04713450137e681c34a50f2ea540df4711cd0e7b0f0228e4f23d0df37300c4c84651916fb44f9f7c0817cfbabcfe1fd40bcf3b9a08aaccb501d2e5141c799fabdcc70b1e3b57ae5672182c88bff69f054fa2448c0259c79f08a97aca3828de4c738d3bf8721edb61b29921298ce4bb91c326099e03e016c15db76c3b5041e2fd73ddf13952d3220f05c21f303fb8c8787e6253cd0a6cabde60fed3ef264e450565fe50a42bb920f7bd22342c52764ba33eebc44a0ac99e47cea225398ea25693135921b90dabcb22e0a1add250a7896482f1961b0a8c6f032f0c8b1833f8c31ea9e53d0a31aa55acc83dd6756835bea38be5a4190b12a12d533beb8672cda7c3da8b9a505bae69133be28d93dfd6c850276454eb07a62d5af717ce0ac7467b7556e1ecd30be66f0f6d16220e0df2cf4c550ce9dd0b3dd659b99b65a8e9515d81826c851e80f8170dfbd42e60cbea1d52f528329302d941290564515eded01284c39e0d626f92114630511dbb516ee904fe6fa97b05785d7fb46f2aa542957b5f9b7b055d730d0ec652a6d64e181c2416d98b4fae06decb1ee606563060c91c9d8f06aa21674a0f8088fcce3aee79f8645e51ae200266294ef3d80f84fc0f86bc86ee91ef94fc5c96d1b251967a9c2227d2c7d45305ec91c232643e5d80ffd450a45d74b13d538bac254c207bd94ac928c32e049d01beae9d6f0c9b5405994539dc386581ec04fff38442239f8a0c1b34e5ce63bf075938df828eafe6986ed907d28ab10a833e3860ef410240a56f38de4661df0c0f1b4fc31b85f1e6f6f8830f4ab9216ad8bed470dbc9f6c1feae7ec5a0e4a22f09fea2fab561acc4a57533a757b47d4f1312c0524d5fd04eef74a271f8f2d4e404c7087618074a41604cd00b0f26c91e49e95f9913db423fd558f5cfaaa678930c297be763bed3427a2fbc838141efaa141842ab8758cd6bcc1eb175a500466f01aaabd7d1676b000a3bb1e5a748e5111986d30c487d5b1c08e21e12f4e8e4080e673a6bc62af4d508c02b35908aadb9e53b71efe27f2ddef664f851683c3e7200 false -check_ring_signature b3f2caecb6abc30037fb1252f8d2c789b8cdfd96c37f0748bfc094b130ae348c 7c72b31ab3a96a3daa36d9038e2ce280835561447903251611a8767a9dcf6bcf 8 0f8f4cddd684b7b26eb2d682a913e9f952ffd202566df752446324a22b7f5929 6fcc8450d1aa843e26e86ad53c40ced5f560271fa09308e59a38d5ebf3f51e0e ba12bc283153691a60e3cf7edda48e228bf22226b460c2451a31597b0556efef 48dd2f12accb4bffb4a03955ade7e4c3ae2e786dffe193836c60c4a490dc1a9a 371a518670bbec00cbf3bee8e7f54fe5027b90f0d720db5b5f8b2ee615f65e0e 0d0709f1152c48d760bcbded025ce4cf06c8a4c268f45529c9d461ade21fe5b0 a81fda1b46c23c665f2cd94a784fdcd4667a2782f434991db519711fc116653a cfe0123505eccafba5a7b24e70acd4845ee5b16f06d04a255454f8452d05977d e83043ae2b76760ad5f48276d2cb0ba47cdab0f6917a7480dffa3f2b3577c40b4d051d3e6ebedf8f60bfaeeb4ffb4aaba81f6540fe4ee2514d50362e2be1f10cd6d9a38789ef9e2af62592eb730e725dabdc7215ed2b8f75a2af63a75967f50ecc67832906ec92dad26f26638eac01d237a085d846873afa312421e4a1141a0f88a5b87ad66bef5effbd848cdb85f63aefa87949d080f5adfc4099a55533340336b5aa84020a10e3f3613b3e102b36b90bf02275c154add6d7161787a6f16704dc67bb0cab596202305a6e0d9f5f152ac07c8d3ce6ce4976064633e05d0be90e631f1ddc28bd3a31d399b20ec2b3f5ff5305592570b51108bd3c9cc8bb4456022be422914c37ed32ef1d7aa228538a7e60bd872d2ec03c4c6179451f36811c06d27c383e4fe74f7da5ba50ba9735af80ceb20f69fe2062c980c3ef2b3dcac1029b8878e27dac4c34186a6a747cd7877632e5afa41e04ef7a41c1a0b2ad46e8010e41ccb2c2f81616b5a4b0873bb5ec127948141689c961bb8e1b425be4d1700afa0a9e2ed5a9540fbaeb78e80fc4348e2a9922a887279dd86ea12aa27e030604a3660b573d24146a94e82fa20a5030bda2750dd6218582e42bd29f3142efda068c8d9e59ad175e3b0b59c43b37432d16dd211e633fe5188ede7938b87a985300f362b8c6501f363defb4bf8e968554410d9d4f4734cb9f6241721f43f281ca07 false -check_ring_signature 85c8acf4daf26d41126e99594cd8ae6c5052c1db5e875999250a9fe0c9cbfc0e 81aadf32a80bf365586fbfda7ec1b63b49995a03e492e3c43a313f22b4ac198f 1 8ab345ee63cacac45b043210a46b52718a574cf055cb87bd77ee6043f9558bc1 ecaa4141bf22bf839687e76a39acbe4a36b8622ed5d44cd3291933b0a5a5b708da394213a4d45e86a0ee76a9ff4d466661b7334b0d9c44199d539b57a401685d false -check_ring_signature b5684af1884fd704b1d0829a876e7c476bde89e064cc28be12607efd9d859859 7de278c6138b336944b71196c53b8ef0e5a7c28bb6325eb2fd53a0de78f1c915 12 a7a9e866cee16f3158e00a1e65130b488addd54272314715d60ffc351e70c7df b3f44b82a167a8689a87d26e04b0c45c2baf62f3251a662f4d546d9f981fcb9d 82b91331a61cd341cca6eb71b58ddf48bf05b20a9a174481ee10494d9824815d ac02e6831a3e37ed954f097ef8f3e14992b81d09dda1106a169142e140e00f4d 21e54364bc73f1b1779f828c288987bce0253299ffafde66f9ad531ac35a06c5 3bc38510c646e521d90d01e8d59b51d23a155a4fb2819e837b8865bcb4bda85e 3cdeba0d1073e7222d6fad9acd0d0a3734eedfe32bbeebc6e2d2242b2d522f5e 47b719dd54a61f99b33eb9ef4f5cea49a23dc9935d843d3aea1d323570d75773 0c55d6f27ec652fdbdbceb42e1233dc3052702af913ac2685244bcad90917674 ddf901a11ef274b1bfabc4e03e9f31088932bad0f02b162e86842197589a03e6 6c098e8ecb124817004d511d134e42384fe7aee86ca8a09aa303e4dbb940113e c5465baa7194b07c12d6417bedfb0c5b0a68e9389277bcf81df6aee03264e276 a6139163f38772563dcd3b08232455493674126fc29bae0e6ef5a8d294ecdc0450d9b5a3c04a10a0d3a2c272a5f407e6cb5c8e0174353cb913e40bad48eb3108e03e98741410fb7aacb2227e475d4e1abfbf9f34d7533cb1c58df2f85603bb0f4772f130c499a22e9dd9cb07eb70c0e347c24a2330227fe97ce42b43ead0bf0ae67ae8ca4e42b81405b4fab5b396ccbd1e501780b3fe596a2498b353f515640b622cf2aba27e4504433550c8374b5b7671a68996758d0a08217a325bc3235b0a58b36e7713388a249bbbe8eb0fbfaa53fd9d0f82278a79f88be732d4dd28fc01671e231bc2c9019cb00739119ae2ab596cf5fa31ddc94eb5ed69644788edd00a79cf33fb2db76c3c933b5964156721e613b0c780e752217cbfecd4331c90ad071ea607310afd157b8995b3c69de7f950a5e5c7c1bfd5082447bed19951778206aab914414f85be83d307394aae5e60628559ca43b940bf7fb426bda3054b8d08aecbb8fffd557e26c7957be3560d8816fa143ea011b47f6524c0fc65d258ac0d0884460f1acc92eac6945cc7530ed2ad42546752a61539de35354836f6025a059855d2d592c5b47efe640c6da35598443f66286fc1d25402490759f397e6240fcfd7c05f280062c6460c5c254d446836738c6fe38d10ad18ac07a79cd3b77202b5c22e333d2d4aa5c151bcfb2ec214edfb135d775b2917c99e54a08de792ef0e25650d52d1f0110434883975b251167bdfba053650d8d8f7a5dc0ab513d3b70c6ee719bcf806732fd22e5fd718bb59e89d4bae8fa28bd8e4ec9238641e13db086c0163f4e4e77a294a010f477f9cf2af7a2bb7f7118f29d0a0b81b6772cb860e2c6816bb4aaafaa02d27e5f177be189423c2f19b8d3da2183c040ac9c995d80b37b7afe195873aee9e80c8218b9e23dacbb92a46845d12bef08d07f3c3d1a000776df6fa994ddb2e045cbb5ca3b74d5fd53d0f36fcc96ea635d1c789ad170c05748bc89db46b168970f594bedcbb86461cb693ae8b0f8d93f24f7e8ecdff4709c4616634f7efcd02ca4e4b7e94aa92e309f4ee2cd87de8dd85ecb97b1289fd0b false -check_ring_signature ad9702ee554d8405c8159ce21a54cddaa76764c16bc44da26b6cd278deac3145 f3d141eb3b03803723bb8f71b6130628795be958bb32a039e56fd42a68080dec 7 7579b434e187ffc1938d64ded25f50307f38c6c5279b8654d0896668dd865a10 58083a92fdb01e861e5088940118c1a44b93c60d87feb81c4cbb48aff9c56694 18f289d1d328c7098b3a0dc440bd0139d5b265e01327e1a10ea850998505832c d6654bcc38744c81ea256c4f17452f4e4bf5a136afe61324746ebc12839bc055 005ee962110f418965f4bbf670ce409df289a440cd9ff3cbd11c345b8a9621af 0c004ccc27cb3aaf0330b06d05705a351a6d5a15653581bebf38be7244bba30e 344fe7194637cd7ddf3946940eb18f906b8b19044f64d0f3bbf355e6d689f46e 829ee9db5094f04313927f3c4024a85607389f48bb801efa2d34f8656fd62d04455bb2e74405fe7f44ed398e8d607b4eeaecae12846217063f5e8e0db3092202d719baf36b15c6facc82f565a229051ec5c0d437c06dc4b46947689c8e4a3107e5f9c73cd38ae38321eb33ca8c948707948c5f735ae93888da2ee8a5b4a2430071587bb8915d4c5d18beaa004ce30b14e790903be22a7cf827da4b7b225e0c059863fda5f0f67219b8bbb153e5e4ee35d75323de33a9a0da37cccccd79516f04261c4e4a1c4e31b6ef0b7b61409466fe7a0b23ea6b352517b0ac086b5336000f96d58f530c78b325f5f9912146353d04bb93c6d9495649d3c9ba3fe9dff45e03bbc27dd684f3622b92e5006dc08079f2e6edbca121d2b06ea637f3a6342b230ce3a60ed47332fb5dd4863f48cbd3a70eefcae57d3080c2f32d996402802ae30a7dbc23f69e74057228e5a6a98cee60d09955d98bbb2fd742d9ac60bbd98c4e04641d4d27b231be17d8a5a97d17fe5eb2a4c2e0d737e2456c0a661b5aaa695208856c4230114d2fd35ac62759b5562e8e692e096a280766098eef7a8e724a870b5af33193a00b978124c3e580486396db8b015cebe1fb5866c8c886662e97fd0a false -check_ring_signature 7dc7bf04d107025ae3e0404b041292277711778426e28e201064e62dd5b0d68c 7160fdd25917d884b0569587cdea9ac82c4fce8518c554db55a1c31cae1a0a72 93 36c2063844f871307dd3dfdceef8c5a2d442667645c7c44a8a34c01cd81dcfdd 098abd9c8af0ee673f8ab6afdf115d1567e876e84a046422189446fe0634b74f c0942c02a1b6c84f8dd5b7a7b243344c2dcc147fe57a2c2d52db37140eb7c839 381da8e2434b1961f05bff165beed208ac749e4240e94bf1e9613cf4a574b4a3 13ac0fb33143559f978fd03d17054b17d9bd8833e05bb352e60b614eaf16ee13 38ea081a8a26f16f63c1390f15c0cd8c3c9f0f05cd98d30ba238862e609bf2f5 f560f3ebfbaba4a3f6076c29aabb5cd550d2765373ced04f432b5fdf4ccce0e0 6d8be6e488914e11ae229b19a64c8180cf156556aab07caf610f954d67d72c37 80093ea95ce05d5ea920692e07ae7869dfdef5e5c76846d6f30483cb29dd873d f9a945efdc25d099955fe2f3963e529d43183d5d035eee120f363320fba23fad 6a27534325e5099594bd2cd98d7004be7073d30429b539dbfc166b2d09128d8b ebf3b6d61a536f2f5d336140162c2a2d6ab3759cfaa0b65e822cd744542c2796 eb66e1d7e34a530bfd30c6231c5718937baaaa3b50899d0a4219db7118d2da43 51498f3be7fa7654a1e398272e3ddb5eb4d7a3293a169c273b53d9714161ef25 e380051c0571a821f1b29ab8981aa05190b60f1c2c9d959e312974ab35bca085 d099f571f952a545f126a33cf8e36dd7e7b482120d62bbe1d762a24ed2a1f605 d26ecedd243247e71ef996a52d2d5f19731aeec4cd84fa50dbf526ac7e8cd1bf a55dccd0145f4cb75fbf75eb3318ac0332783830e79060a92e188061168de060 1270e431a6b9628dafe96716122e66cd0065bf8d50eb7812fb9c53fcd9c52b1c 4fceb8d5bf61bdb0325dfe7ff626d2dc122afe52e2465c1473ba2e9d1d53f645 a79962309900e491d57e3dec306ed49e02615032249f05c1426694803f72912d d694032bdfa74d23a30525b8e06bf343123f845965f1947cf0855eb2c202992d ef6fbbeae2a90d8703bcadc0204b2a10a86e9720c50c0bf4494cef44b5b4b5aa b93bbf09993a0c87bacdace55a44e80fa559822736d535850c6ce6cd34675adb 2a2596bcadb5401afbd5b88f54de93d03922eed29d4ce9864a77a01da43e3537 91a5e59df92af0e933470d2b1d8fc785def2986033945e3cb97e0a66bc8aec65 447785c070e8c12707b38c5e71f51d65af897b04d42ca909131b6b6602fcc0ff 70e5c6b8271ca694ec950412e75749f312608999b037731c12603feedecf6df7 a5bf35b7b9457ac196f7c0f96f7bd085b5dee354413f726444e3779eee1f7f61 e00952f5488595fdfed73adc42b57a81514ac488e066fb62df685941745c958c b9f45a741ac7c5bafb86104615e153ccd872dfb09226b517b617eba9f97e4f0c 7fca19356e4b8b77e6ecbcccb38356300ba5858a0be0e3516fa3cc91186cabf3 77a2aaec538673e1e33087b69ce98fa3941978356f22217658b7a5c974e53f48 0dccf1118a7854808a74c9872d2551b5b9185eb831c1da6921cf2ba4f3b81fbe fbf264fff16b267de6955d91ac7a3a8146e6044db4d50b1ebae996370850ef4b 0e367dee178caec5e056eac560523b5dde63803db036e5af2b11377493e5ae3d 0b6aabb26f72b40a4837fc5b8e2628e16f13a106fb5b9f1ac59886d162397385 8173c0649b5a945555bbf56b1ca937ef3d3af292a36d9f33c2a66a295f9ab2dd 31345ba05c6915bd00e08367978a89b47978e1b6c1312085efb8d0aff7d7d918 a429fa86bfa95573cbdb9251028b772bb4ec319e75d085b6d582c4ea24a19e15 0223417101af94a4fb0176ecaa1680de48ef95665a5facfbb8ab8b9276bb3b7a 2e1d005d89ebfd4d8831aa842c524bbb655ec7d42fd185458fa865941d4d6b5a 027cec21650b7eedc47e0dbaa40584e639614451d471f3828827982f1a951b00 62286b3d4cb8a25ce2ced1f6888457e9ba81fba4dcc3c83a58a4e2a12bcc294d 65171b4d1e9811650329f07c40dcf6c93d75671d1a3c63e7d8701d3475702f3a afbc3107a8c986029f2e87df94f5ca7ccc00c0caee6af420b614b89b3f2becb2 7e1a9fdf4107613be8a64a79deddd449edb188be4dd5e60c8fc788596b0a0272 8d9759f7eaf4eedaf5ed795e00cbc1e5c9b84a106ff5ca504cbdfd2912bce5b0 c7f969d04eb5c7a476a74d03723685b485532d4c2a0e67a67e114c0ef46a02ec 4abd4f41ca0d4616d4397445611895bfdb3d1779b8914a3e3cc9e6a895e6adc4 69b316df39b188d9195df4dfe4f31194a1f09f8212847d238ef77c471b800cc0 bab05d117f045719758f1c5d709d3c519769f9dfaecadcfb2a278ba6a7bf8c35 38be27cf960fe6ca57a9db82ff420f145ccf01d835e0a2d33dd551cf23d3a964 6477e8190d46c1aee6fd62ea12efd793b58b4fd6c096a90a52d3e66db2b6ecf1 b445c019771a6f467bc43d5653f77047d672585d3ca52d13676cefa30af9837d f17c5d331b87baac43bc20ec7b6a3f746731bcff210e4d94254f9db6ac4c11e2 f97881bc6af359b7879fb4c4fabd603aaa50f3bbd24f742493ef1d05a71391ea c9cdaa0fe3b2b7cf6b9c6e8da19ab0c5462f56fc2045c766ef55b865c279d289 9ab98fe953749823dd12f4b324bc2b7f9a6990ef4b0d93c7a6caae021472198f 28d4657a7f0dd40d1f75bdd21144388d2e4c4d19ed0220e61480d66761c6ae01 a297f1c41c20cb99d1b4dcf9c450e3789bba204435ae4a5d6ba4a906356d5daa aebbfd74b9585bca8b1380c31f5953a0c8c69a315802a88fd37bc5eb5c55daeb 6132a7fca480553cc95819e48dbaaabbf3292981d10c98762e5e2a4da8de5563 2632b0330769236b67bdecaae3bcb7465f02da8bcfd2cad56e2d9745ebf22623 1ba74672e682a7f95e62d40d8b5619b2663b7e52f42cd03e58e3f57b01f94c9d 2810d984612f7a3cda042e6a8314a04e293cfdafe80e45ed0ffd302a6faf1fbe cb345e606ba51fc8dc451eac0cf791dbbd9ef55f1f5fb4efa07b4a89e1d05c60 0973f493594144bafb2ed818ac679a95cb66c75555f54bfb352882e43c94ec5e b67128e36309269c777e961bd31fb1c3d1ab56cc45cbba12201e4dcd260a6634 af0a5d1adffb467ddaddd021d35ac2e77503eff4a6016529b67f424d09ccf1cd 4f846118e6edb1e09de74502a5c925cb3a66934561c2187353634a0ee1582d3c 7d18ea87bd353dec8df027b0ea71dd205255a4e1b8ce8340b2164d50b0e29a9f 67fa253b12438322f774baa667e12c1c6f6c4e1a6b8edc68b8a7e933eb62adb0 ba6b45ef2a087923106070b807e4115c55c02f0b9ac3532fc5c639bec88f013c 9df4ff21a83ec4a852a13fb02ed964dcdbd6d0c9f292ed17195ee3376135c1f5 2f5344830744c2fdc359edf63621dd5a066dd8e6a8ee98096afdfaf49ae503c9 3297e75843c328e3f55e99c080d6f58be95b19a765182e23f584a8419be1f58c 22aaa014c34c223e3ea28c1d454938ac03fb6cdc94e1ce1b507b2b5711fcd9f0 f4805fcd5a0aa383cb1f782f9ce64783bbf9bf98b2e206914f6346924da93974 a4818c1d174100770c4a90e31f2536a2c99987ea7f515a7548e1a9fe2b6de704 748239a378dcaa05cb1e17b5d7ecc3dae8300fa7fd320ce4540124153500bc1f e0cdde02e168269ab6e7d4edf52bb41e084e8a8a4d6b2ba05d9333525e5aa8fa 3909e18b61daaef264eecb2246eacef78c3dfc4666647b62b3d7d3e3b8585f58 33046481ffbc427560358918a7572f7f2054a2504e2931509e304fffb81cfb24 b0a4371e8b19eec15e309d813ad3a8154b7f65eaab6a83c6f63749fb3080be6d cc5dcde1bc875108b2947d6b607187aa098de60ce855ef35d6ee7f3fc0309f68 b17038741dd89fc9dd4b6f5945f13d0cebcb2ae5eb5b99b825f93fde55f7b287 9e26c1a2ea69b60a05ad76807cf23ee2d82a890f796d0740b5e91f01330b29c3 a25ad6e8ba8cb4e0e404fa2717cf989d6e61a1ec65d4e266d57a6436ec357ca7 b1f7a89dbe15cdc2e14a2566f00209ebf44dcf8f802182380a3af74a24aec140 f6f312b9f834ebfd80ac24a979f974e208d37499375d50d90f23cff9114a3faa 3dbe8926a01e17575fb66f2ef1bdf745683ad2eb46ce1186e38b80c4220011a2 d9301b524ecf540d63aed208a89f41e1cee0ca1fe86419f5091ff2f6ae6dbf4a d6023099a19236372e79f306ec7b78c77bdf8dd7de6a1eddd6dd899c9724d604befe8c9985e81d3806a2257592bc4dbcc865fd2b429cb13c95cda1898911cf098d88d9e56e5d4d4e0d1abbe535b45eb200e9b679554ed0beffccfa6ec77d58038fb5e9d61aa09995049837704b74ca1d8208ad803af5ce6ec651d45e110f00008fea633d9691ac4902f52c3814121beb50164d248c1e8a571d0608a00439750b11402cf524e8ba38f58e01f395f7e2dbd7b5894a34d6e52ff9c5f0ea4e063606d95979ae4621acd993372d80e1b32947eacdb839ae86bb7e313e88449da1270c10fa8e4cdd945719676cf3609adb44e216a0c12bc308da4f076a1054021fe00664a81291c5d08b79c1f366051c0bf77ce4cfc60f2b3ace191ffa84f60d77990326135adfce69782b1e54d812eea135c1b024956d202fa991c34584bf1e5e320c9d6107454825e639ea02839e9b4ce18bbfab04ac6b2bb1b2347c91ca4082b7053a886643c07eaadd41034a88833ff4ee25055e759e90443fcab33a6815ea00064ed9a6ec6517a60fb6566cb309a2cfd0838dd470572c407bdb4d6af52cbd1309fca7dbd476e214e2f37379027f6e91a4bb534f25f096c07aafdd5ede368a5f0facd1ba2e5de7591c5709c2aee02ddb9586264924731e301fb9348c29b7e0b30a295d6d3ed90afeac037044433e60ea56a61e3199255cbe2cd88f4bc7362fcb09b6de792e563e06195ef27ec0a4ed65c20b58035a11f770ca555c6fb07b61600677a9572ddedcbbaf6911f25a8298f90164a99cb1066dfb2c71ef385797552607212de880eab8f3d50ee26b33eeed3913150d96ace73ca027e7b8e7200ac3530d3866e01878cc6eeb66f79c67cc00c7470e7128fe50b41b73d73315ead438c70daf62dfd71a91d5ec7ccb33b640a898690299e82ba9cd421ab2e4536dcc24010b972ab65f0c21b856a067b0cc131d7fc5458b90316ac756072af0c5717a2b6307fcc863ce70ed6de8eee248448bc316274ff3a96c3cd2e6e6dcea6971216bad05e31b26dc517a2d89519a1e8ac1a103870641fb50b19a7fcdb88d5c6088e1c501bec90d5bad5eafb67c72ae9991307e0c5f5a9ec087238a0dae69886b8c79560cfac77a3f8da898ca4c8db3ff7ce92ac023e88c4531b49202eb3f53de3881c109f9c0edaaa3aa37eaabf1e92bb05c9fee4649810fe57fb9b4ec27234d376369094a3e84cd62f7e090daf1b5e1a9cb0073a310d06eb992791ca07f9b40d434be0bc7672e0799dab786837cd5240ea3b582a73ba4bb53ec5110a584ea1844b7a0086bdb2c983367ff3c9f2993b86b550961bbaf44e40cf8994e57d72a2169326e060993f6926885fe9649975202e2154d5456233400f33a486b2b2834b9749a220a370bcefd56ab029c85d128e5d499d54fe3cb2da60a990390100f110902fb9905866d0cc0431930b1c8f3c494bdc0e9512c25bef93dee3abc2f5fc4c8080a820de5eb8435b55e3f3a868e3638d3b25a868c79f2ae85b9fad9a0c4016c9b49d402707c9bcfdfe3036329e081f1df0b842140c22371812320879e20d0e68f1b310bc3e8bfe68c2c9107e06474235438c31f2e4362d31102314abcc4223c2f47f905cf2b97c58f1202d07602d538e36b62225610ba7746a163e6b62aa8dc02279b09bab86e089dd7b0a7534313218391aeb3b0b869c56093de83b34c3986d1390d09d13c1be019925010dd0b89a9b5b5b3bd45df1f72744415d7fad784151daea704dc31de7b4a42cd7e7e8500a126491935c9a98a345bd5b16610b4c76776c1ac09a17a5bf8c7047d756f13e33480b92abcfc7b8c0714c253b34cb284376beea404ae9e938668f74ed68fd58c7e5d04fde1d1ff1263b8a5f8e780d5bfed2ddd4a02fedbc3251d8f6683bce0a4b66b2cefd007bab626097c8ef6287cc2278b2b940a9d0322e01bea84649ae5943996b55ea187aa1053ee62889bc1fbab840803870a9fa6878843b941066ea413fd5836c29bee4cb0d9622327997bc08820a00a7d0a5aa9acfdc931e7067f1d61c6bb6e2e28685b7560900510669410f79646a7ff0d35832290fde83f93771ebe7d6be9af418883aeaaa52fef5adba932a7637fdd04e8210b1d68581fec077a80709e6c8190564ea483c5aa47f2127604612499850ec8d957dd5ee868efe9f0b37c0e9fd8373a03e8f4e415bc06d0a01f06dda1380b60bf200b57b5f8b424e7b27a45de02dcf80725091e5d82c465afd4425ccbb4068d2b4935e0b1aa16adb61777b31d535769a6eec818abaa088bedd1190fe1d000503ffdae53edcd83787903fff375eb704308bdbc4a5dc28819f444548b33d50216328c5a6eee0f2b460a152d97342c39ca7d64ad94cbc5c92875d04ed2553e0bba73410f030544f6e7c5b2c695a5c18cd043b07284c3c6ddc0c741ba244ada0a99f093b95584a40d8146091057c41cab5f9c0f644254cd0cc9c11ae2d441500935091041a2a48993634523e5599c258b2f1a1f041fd78a682248b411b52aa803d7adaeaa8b78cfcb1fcc49b218c496af84bb5dfeb2d112cd5d9318f72bfd6f0bfbeb3f6fbccad45126811e7392a608ef55509fca54eedcd15b9489826e03b40d3606389afe0991db62df74e7eeb31155d3a79ab745b7c4b07710b2e07b5ec004ad9cae9ecd75a74406e69720e66a6634029f6c72e09a6ee32bbe0f5873a9b208dfd3eb28e0ec7bedaad1a4eb7569fe7a40033e7c83275b3e6ec7b7bdd9327c0e61a4be57b0a797fef478f9fb330b28457cdb6f4c0413189b432d1675349262015e1ef413805999043e1b7a2e707d0bf85e26acedfbd6ba6c51f894c47326fc0e914c08164dc898b05653eca7ce331b9eb0c6f9f67474d156b3ee1724734dc10a78c724f38b6b1101620da4b9f0b0876f69b74fdc61d0169d8b89570bf6aeaf0efe890199e09eddf6321b8eebc1caee0131353585f353f1b3bfd2ba9885b46702224b68a665d9b527445e2866d74fcde9148a68da547005f622840d595a74160402d23c3d8eb04760459e81454202e294046051f9fbe9b89de3e7876007a28f0e89904fba33c798f7c20a7bec8adcb2f77facd97eb23d480ab56d6efd1275d50d7ed41c55ecada61ddd4b3279ef8d7fb0c676ba820c0b9ca7535c5eae6caefd0c3b437fcac21c43fbeb29104c02528907ed859dbd2a8d34e5792114a81835380b3c29f72543fd54a450ee5436a4b586c9ca0bb25262f3aec7965a9192b30eb703b21a57c9e15bcdb791ed5923871a10368462b7e85a5c75d240c18d16c2bd2100c00dc9474720553bfbc14f170f5f2c0b64885d04542bccb7e7259167d89a2208189c8d63034def8c26554a72d369e0886b176ccce1b1fb0eb916393a40346d0234b29cea6ad55ceae6754bbe4a39c9a155d3617f8ce8deb1bcc29434cd041604a84ee0f1c4abb0bb9b93a3d889448a4b4f571ec0304199f9c783d267cd4c8109b350a8dd65ae0c2d1eda03622deb678d2e48c05c44e1eae222c4fa41fe5cec070a8924b9986660327014dbb360f4ad2ecf8766c05e839aa50f5d6df43afaa409ab6634e75804611d4f8551f774f1064f5bce455ce71d8614c420fb8ceecd2c003bf514ca60d3186f4ff8afa55e743fe1ac9c06bacc12ead75f182e0977f7c60176529b3a3ea38df8b56b949eae9c642b099bf1d2d4787f61fe33136a6fefec0ef0aef3ed59748b32bb497206f370f3e2b0391360d0e0b4d78b059a56aea347081f4f414bdd8340171376172366da32b59a8d3b5f5f938a1e590af7dc0c3242074e84ed90dba63c30502e33448b27118c8dbf0062a00079ba996b18c0bde9b20975a060bc9656b83ae4dfc188f730baa39d94dde50bd81fe3ccc1e83b6847ad002bc28dc0492638e19ec20f0da54f4fa7aa75afdbc61c33d15d9701b9cdcc180da4403f644ca53740f8175c3d397ffc9e9896c55c7473d8c0de9ed1a8a5ee8106905af6933d3d6d8bdabf4196da59106fb86d74b231440f9f2592b4d179ef5b00fe865ee672cedb6f84d3182ca783670c7e62a207597a9d85d118e82634046e0cf5f9f5e9e9d08b2aa903568f47f577b184b39eb94c1b9d85e06f41bb987977076514ceb87ced67020a3bc216dac3875787b21b2d45ba5dfdd011128c8f7df40967b92533f44aaf32fade3a6db332c5383abfef8925f0817024547e0e3f1f3204602a982b0dd9214d2c2056cd8e6880d1c477d5e44954678b8429e43c7f3eaa012093221a1993c9b717574bc3fdd7a22f699fb02535747e70dabdaa13f707a40447f0d5cf1b299970f1fc9218fb9288058ca679eee9f92a378de664b147849e037e67cddd3782e5bd3e1eb55d3f02d45de36ab90f0f8d7e8178e9f1ce5602bc073055073230e0c90d1168088cc27131c41eb70a0b625a653ef4525f3707eced07beb26bcb43981013e8b0849a984fa625b415640d3c49e8f2a1baae99c28d490ec476c28c7560bf3cba7043357ca9bef1bf8a8ad4e8618875fda00482e0e02e0e8714b589f3997f539e99d25d3582080fbf7703b9da6aebd17a267af0ff747c0eade8852a0710384f1675edc110d196f38f552758f376f0e066ca89525df3750c66d914122a7ede978cfb32e43801dfab309ff4010b4024e5a8a90a3962ebc4020402124a81e5ee3551218ebbb9990a84373e5b186f8d37dd6eb89487f8a4620e3dc651e644d05f654ee272ee56b791dd8ec62a76bef097b36c11e082a6a4bf0db6c80c759d05110c0e141cdd59e3f32316d62c7c3cc2a559c51173773b628904940ecabdba42e03b9caff3223e191281ff140474012d1e83056e40114ae45902d1bcbcf1e355f39dd142ad93d50d190b3c42c181f987aec066abbd964c427509d489ca58d66500a7532f50d6297fc655d6d182ec31054ff0c7ab097851f7b40bbb97b233bff7d870241379e62e812e30c60a89ce343e586fa331080002b2d4011e4aef896dadfc186094c01735be66e2b5dc8d36dcec84e452e68c86355f1601e777d0a074e1d6fc938050d9b3611c154a4c61ce67851a55d2d67e6025dffc0c9c3f16e44314a195040e9459e6b5d9a9fe2a5d569c7d38629934c26e1bd58902e5b76b501b6001da10895b398c1108abd3d031dd74ce000bf0c8faebaf66dc0913f9050e76eb5276855e493030a61daa66a84186d130b4f7592b758f6946ca0fc6aa70e6e8d3e8b99224b58fa56e3c09d735c064e72bd6619e65bb86f0a0710117e274d7c03a567f4404a9fa9e5d2d71ad290a644b05f8d59dc54170571534078e9b600c15f72dcfed0f7153ddc17e16fc49e9c0e1814659be228cb08c7c16024a605dc56f7b85d10bb1599ceb8a92a84df8df2aa5b0bd25414dcf022a33ab0064aaa3dfd7fa0d0e943c93254e6bcc89d0af0904f867e2b5a7888c2a6087630d4f899709f819f521d904577f5514364af6ca7a94f22c24e3c98eeb3ef7570207f3a6f2f1949c8e684bed03e6bd8a0cda67247058d1907e02e8c8aefedaf6df02661a17d573c5f40bd2d0d0952f8e7ffd3e2db14a80d1d2367cf4d0b2fee6a80c9edd352dedf8c894ec7f087fc42b9241311925a9e5e9a7de893cf1cf35aaf80221aeac9623e1fe9fdd1298c4ed1bb02f82fa9e401c6f7e067fad9fa151c42e0774d98f66b07e4105da558310093d24122a9f96ded919173e61cad8f9e3bff20b431b40b2074a9e0ebe33185c7f7b3728a20ee34083fc1da93322b1b4e654bb0b8f5d9cfb875220bb26da7b1ccabdd8d65b77f7088f3b834021d31fc0ed67140eee3cfc28ab29e1ddd7a0cea0639a7c958a2c6926c3846414b83fe2ff1b52000f169ea862ef4ce10e6c1fd95104eeb6fe9fd29bf872c915e41ce696c327c08501d7612102da347c9fa7f1f979d62e027ceb3db51ab0927c249b64d807d927980a89df9904c89934c7bc47bfe80c9a5c75edf5cbba863ce88a655fddeccbf52f0581f0d5f65d3a5b794d6a1583f6d5980bc9c79b10260a98c50da2b63ea8e0df0f689b2c94c19cd4c9b71b8cb692142fa338eb9b108684c1493161c954c26a30050e2839d16ade8a56a3ee3fb3fd0fe4f43fd2688d62cf00a690a1a7359779590263c91322d6e49e04ce42d27a563c33c89747258257e1ac10e8bca902dde7d3078091bccf59724530a91e6741eca80bc40c456ee3d6c2b1307dfb30b5d4f1c60f5ba007254f62482b66f8e5a234dd6eef5a6a0e72c8831f6ba85cf50c03eb15034377c523c3194c1ffa4fce23bf6173a91f536218e7af0cf5f8a0cf2609be2c0f1dc02bafc66fcdadf69719f3f59ebea0149651f975eb42670320130ef657670326276d46e7522f342d6d6e911b9be642645444f112d3f0b8b89c8b1a8631f602b5b32e235e554fef9461c383aa99a8f176c6697cb8a56e29438efce6bb64b50c59288abc13e00dd456425dc1a3a94e1398482a400ebd14c7261a2fd8af9fd90ab5cb0f48a6f1ef30a30db1bf1210100469efd1f01a3c7cfc0a048d55ed117a0a57812285ce0b682658ff36c3113198678a5b80ad1564e0514f87b1557e885b019c1a6fe57eb9a0a4667a98551f24e71bc90cb451096c2a993eb4c4e02bad620a9660ba9570ad1b4054ff6254dce6061034b0fc685e61207e4185e3c50ac7c80d3542759a05d34db9820c33c19761b2be57e2ccbce862a8515a4786f29414c606ef76e47c95910507cf927bca9ab0084c17b2080e497a1aac27b21e7ee898230c1fc549d672832efbc2137222af731b6e418b148c02a862247edbf33bf02986074e9645babae76309f27f8e0a9cae2ca99873afbc76f7c36625d69d391887370324f36d4714aa7f10193248f31581141c39b6d4189e73ea616c08eadc9701090b998a5a0e69fe3b04f9aa49d8cbe84039c005f78b64cabcbc954d9db23210030ecd47e6ee1a92e6567d67dc2364ee226102015260a082718d335718403ffbcb0d07b3408217d8a4e32bb536834da51c920ab36a93b140b8138a680789ac85d80f20e6fcae0d19fc36fc9b0083fb4b7821d7bd6e6797a33b606084a217c2f8080592ff33032ccb06f51d3ea11ab32cca5cd1f90d9532573b78a80962a81d15d405ead11097bd101738cd8c542e7a28cd0d5010be0b6c43257402d0978ee24dd706147e384d8cecd8bf501436cf876056db990fc7c43ff0fa285039f71be79ef90e95ed5b30994d14ec8aaed822c54f1234aa13e7a2a0245dea4064ffca80364f0a43be2bc3dce259f74ad1348cc0ac49f498d3a2c800b37f2ab2db061e980d0e04604d4ed1a3b7f65e274756078578504c5c6339ab6315bbad4a10f454fd8b670615e0438575853b1f7cc9f9fee97a696134465e3277da1f378b870ebb39eb12035f9a12df46f86cd7452ae9b383a1f23cd77837b6d825fc5373592905eb589509e5af0c9b7b306926b745b925116cc8ca39a8791b09d73c7a7a1a16c984d032055b5b7bbc3f5da029c94f5fc419b8357f2cb91b18d044bb0358a91dab72968a0128e130b1a53f289c833eff904a619620afc899a09efcff64ca9fa620534fb50170232e4faa2cf67b3f8e687c5d00d308445943b1f1d071f28075a87a16b9dd0823ed43d88c211c934541c274d463d198c7581630032753e4aaf96eb5e183e00f670e3ece4769b554663e620de7dc727d6340bac46896c9ae17a48be50bd43504ee20faf9696c8896ddfddf0d9f966bdcd719699bc907af3432ca52a1e356e20ddb57b64858bd3d4a41e142ad2bf0af1e22f1c77460052ef77db9ffa45ec3070157a283a31be38ef4f6f52dc432d9c4c1fe3c8bfe4194690019fecbba7ce27307415a2aa24ac64f56a0ffcd5d06e50f3f921c417ac1172a953e33f75413ad3802f70554a80c81e515dacf7b24f43664a0a3885f9b3a76bf0fedb2ab747a541a01f6f33a87668c1d8062ba4a05ab87fbe53cd2159a59deb78d02437688692b5f0e40a51cd37efd4ac7591f33491d84c45f999d2f84c66d022ef2ff286e75c3e00db0a055318fad9fcbd0db8a719d37ae96aa02d68008a49d1712d2430ede0be80135d7440f01f0bda86e4e7ae335c803b869574318751255a3108f4543c7b67c09161a81725149102d911cd64d0c872668e6bebf2feda4ef83349881f7a2d79d0974a8b235da87a22cc0fe4ead9ac020e10fe80e88c3785836eb44502f2fb0ea015d117b1da7a4e69a1483c614aacb7342d660cda39a2011c38ec242b25da20c0b1566af05ed71aa0db80b0897cbd3bbad32d1092d53a8a241e7a31cd9617a3a06ba8913628fd494479768d4b0381c52721bc0ca6a9561d9301d67238d6b50e40469e80d66d654a8411a9cc3e6b69496faa5c6d115ad822e20060ba380a49525001b6e48eb6a39f59ad2372ca187bd8c52db899f993065729e786fc61d00cf1707 false -check_ring_signature 1e7f3b6d17dd430d3a397c89985a3b0635ccf955a0353579902c07cec0202158 d319ea0255e029fe12e5cd4b89dae49e02881574962b2ce26101954ca712f59e 1 bd5c9dcc32de5a102cfe92c8b313a3a3eb59b111619fac1727d2ddc8df06b03d 8d0ff83602e677d6f27882ba15a3fc60f968754379588aae44c660ed9e94eb0a88fb4416740ff30c1770e9b32ad24086a195fa77047a6439a9dcf28a23d3d80c true -check_ring_signature 2963e0836b18177632b47b4253f81c3903cb6e46348d9502a6e8acc05f8ef389 5340ab106d58fbe6dfe669ffacf012320141c44ee466e5996cf6e868a2ed189d 211 c68512846fe1c64ad23346161adc677320675482d43899dfb03c85802d3dc7a9 00164e9c593c0c778ff35d5c70f7f6a3c941555c010569071d2278e604ecba5f b58c192f03336235839f2777354edc4441c0514451b000585730f8c2c99f6923 bfc4fe11571070b88a86f2b84cfdcbfe94a7dcfcc8dbe33b56e07de81186a69d 220b2841f183e4ee5d4fac9c86d89576b977242a9b4d4a38522402e20ea3d6ec ed8d5e26a4e74924d3c65f4897bb6d8072da246eacb0d9ca8cac3bf167468f8f 886ae84a8ae667cb43873481bdc7a95dac10fe5ac7ffe9eb3f0b36b6a5e5349d c22d86f669df2dcd73f7595513fb9e42cea506e9d4b46c661756e9fa00d30766 e4ed2d11ecd2e909ef1540d87b69ec109b2dd42db090cb8255d61ed52c9eb4f8 08bd2f86d1481fd10a95f2dc101c62a85de0baee43d235fb7fc9f05d3b472ad9 e0fd64a24f6d273c54920467c3cf5afead283616b8f17c533d20f0a7f90d8879 b999e1e410fe51d8440ac65c3d71bd8b742e234384664dc3f2e64da1634d0c39 a0a383bfac4464e1c96fec843e6f55284c1aea368b5a699137673ffd2cc3f421 0af8a0f076686073f4a4d47e0fa3c5efd43c68283cc036536232be060bf1dcd5 061bcce641cf99848a34bf1f961774fadfdbead32f6da2d99f8fb5d8fe4ca26e 90ef2b0bfa3902dce71c53eb98bfbd1bb5f6a7d993c158c5b6a6ceeb7182ad58 c307c7b36ea65bba2879dd4c32504c822999736c487974bc194860a7e622593a e9a1b58daf578876aace024704c6832b29d5c2540c57445a98edae74faab7a61 694b3baafa7f04776ba1afc26332998a64f3c1bf0b7c1536b76e43aadac99234 8525ac0d7521dbd854fd20b7aa8fd0248636fc9b60fb3512c01692d585ad8045 7c9df615253068644e7a4bf84bc6eea903fc9ea5c8c10d7bbd7148395eb93916 0048a717476148387d780f2a3991501985d9e3e79c5a68290ee450a1cb9585a2 c3642475b313d4750ad8c94b0099e4abffc837ec45d41468843bfa163bc10df6 be0879dd045bb96c96fcfb9496858903371a526f790c992434182e3a11807ef4 7a9c0e76524c15e3b53a016bad7110d5f03287e8b7577a43d4329e1c90b88b79 c96a87ce45465485af58bc23c6972b49e77adb8f98f4ef941d48dfa7184bec2e ac5560a5687ac3735578da1ebc81da654b86d526b8c03cb996a677541b0f4a5f 346c7b99563389a20e080729ca720dbc4958cc2abbbef054c1ebbf6256070c04 8da294ce76bcf2ef87ccc4bf40507727feaf24b70861d28147e220a12c774a75 3e42e859049336c7ceebd2d167fb1c4055cdc7f5ead1bc1c1914585fbb280715 a23871d49b80e391da3b55d0a0aa58262bb7fb14b809be3b289cb080c4e02ea9 551d38a76b6dcda8145c82713ae1683aae14e4b354ebeb5a95cb21ccc4ced744 768088d8b1452f7a83855da26e934022a4f238fe352ac35c58fd8eb72397fc91 81141df2e7ca91b284c8857e0556de378de945e92391ec649991d88b3ff6eabf bfe288708f82231e13a051ad0a215fb3b7c149557e9726e6f42eb14b146ea8e3 58e5ab3b4d1c71bde8e7e7284e8d6c15976012f5f7446204b44235ebb10fd135 bbd53e5db76a67caaf0b0cdcc51b24907ac4abed9dcd6581683943f00dd2893b 21f0a68cbb21b9ffdff9224c23ef355cbbe6aef1c1118c307dffec9114eb0241 976329832a11cc072c176d58bad960a62aa692094e2f0e8a49f3a59a375a9419 09a1adefb0e79835ae57c953d9821138f0c6c59ce86401a012b1a5d3add48612 37ad6f8d9d5f91b0fbf086fa87c02e1f110fad1f2b2d388a4d610209bd375e1b f9d38482b80986fff4db15e6e9143dba84cda17a546f2728d638bbd4f9e06863 44875446f3bf8f4b2b36ba5acf8cffc8afc60ff0ea0534c1e3c328f0c0524cdb 762910b2851d96b86c313e153e7ca063d9fc7a3ee64ff777926fc37bf28cbc9a 7059b0aeb0968ff2f9dcfbc249844a9acd620a2f5fd31eaffd45ad038d29a0ad 82cc75d2ac96b17d48800dc9b303de1d9e6b5d17c210cb99aeb34932412f8118 c3ca460cf83380d6583e775c652adff2bb38f2503fbb9736849cce418a2221c1 2f137c6e5dc2ca5319aa20f41b850960513dabc7f924ecfc469bc4c400b19a60 576ba933abf23743ada800e340b38d88d45e413e770032ff0f21fc4734e0c533 0262d2d79cbb87f62497b50d973247bc880410486329b9f7326bdc18d71e90a7 1e46e64adbd4f35eaef0098d7d5adbc7a9c46b2689a3b88539a536ee4e9a45c2 e13b62d101f2e0c5b768c13094720d7766a3442dcdddbef7a94c7ceca5e0dd03 502568317019cdf5f5cc465201a838345f6838032fa084c621031ac9d22c46f3 22536cd298c0f37abb389158659cbc32ebb11c6b53fad6c98caa6bf4fc8b6eb9 6e5d0f28a15d900d5645fc28c5ff2854ce28391dd79cab04e2a48e7d1e197e01 dec2c565467cce4dd3fdad91d2ee8a47ff02a6128f8fcf32ff4e44d52a79bfe1 8db35296114ed07d47dbc48344c3e4e8fa0e811b4adb8e134dcfe400a14e27a7 2f1cb8e8a7ca3b9d3746fc585f2a283f142c6f990b44d5454793d2650ab93d62 9385bfa35ff32d3d5cb2c75ae810d04e221e8b001f4d0ed82c4d18b68a3040d5 8c6b28e4699c83c8b7f55bf1a6077e13b9b32e1c3150b3e983101aad4e30857b 090755cf08df13bc1815afe1acbd2eec9712b63f647fbcf2210194402fb1ac8b 9df57202f377d18da02960bdb32835ddc23e518e9044fb852bba02af7b28c701 7499424a8a15a1e1c7944aee6d21e4adde8ca7ad8eb60e01ed718b7f9663efee b15b9128861dba5493e7d5168a52a3aad8918665eae505c91e1bb061c68a928d 68f2d0d94e535e85622dfdedda4ef5498aa389a05b99122d842edffe9c668b0d 9249ce6228b3f85fda086941a8f83b307a1d9e7b51da2eab009799bbf0c21b82 28b81ba6c8e0bcb5b098f0fe72739361abd219918524cae8615bc680fcc71263 f00428d1993fe6df4c1fa673a8ea65f400a573b492750eb49775f8106528f771 3064993881bf9fdfa4de6673807d4dc94bbc2a4ab1e223efe4c72e1ba365210c 0218efabfa9ebdf564652b4a9b7c6dbefbd5e490e97eee1e97ee3790bef2305d 0c1fb80cdda0e621258171d89c32ed33901e0a6b3677c468a258c0cfde397fef d8faffa942907e1eb1f4d05549981aea55bbf3726c8411472d5df7db2e476e74 ccb431a88d3a4c241bde9f1cca471eb8bd6399226a06e5fa0ee35a0fb260bcd1 0672142b7bb210525ab123266e415d36081122b4f3ee7147865ca927602ca4c3 8ab5f0811d0f498e8b89dc0e5397dc9e820f158a522095266f0114faf715e5b1 9d52321fb5179d08ab8876897f8f288ca7ba3650bf313bf8609541dbec747f28 a0162cd769ac471adfd4a361018d95582e0ee6eaddeda380b567c1be61d1cf37 d98b37bbece38d0d7c4675f06921f955efae0d43b010edaa7029d7a90a5d4cca b84aac404a7f8c3245dd6a8148cef55671cc6177beda8795fd839def6b6be9ab b6fcc84cdc45dbe61df3aa1d15ed32735cc0f540158f5c7e2dbc6f959bee46ca 4beeac3373ccc7fa7be52bda3cce518ac0b965e5da81845eedb1bb886c118a62 c64ec28f58c9be73fe7f2d54cecd57e1b18c6af47376ec1217a0a37febdf1a80 e2c771bc89d4fe8abbd1486b400389548e7db6fe58c0a2a10f10ddeb1764eb5a 7d46ca96ef16d9ec48e2241f5de896d4ad2f0f4d2eb572a933171c85be441aaa 2ee8a57061d0266a9a498656e289202713f0af2d6206fc9c33e8298896bcfeed bfaf7dd5e43a7e89b72cc0b4ad8417cbb92c51bfdbe459c6d32894ce84235107 933082fa83d6a79f18808d32a7b1cb24678d8ad89f6cc95d8a9daf92c7d54a53 ddaa96e97e05e91656dcc31eac2624a49e804f67a9214c0f3d21c198efbdf77e 01428c7f54146a1170e8139aaafc0394425e151842e038555b5a5b05e8b6c0f6 23f561e78a7600d45ae2dc33816b0b14acd98c0d0509dc5b845aba076716a51d ff19074fb5832d3e745f4937af3dafece77e7c73481e0edd94c15ba2038a7c12 79784306b8e3d05b48c9985b9ddccf1595bb038345573b89111fdfbf00890c49 3fbe56afc607dc1c6fb4010ad3999efd9dd4335033387cb379318f9c8f37b82a db267437649cd49cdb3a84d280dcec6cd095d25e19ee9a1940905800d169f79a 69cbcd3c53f47a2c6c90739ae49ae230f8accb521ffd784dc9c5a422f9b488dd 38f84a0518085c641d173e938535d2989a9cd62065e5f179cf2b6e13c3c85da7 8d307f0d2ef6ed714f68bb4bd066f220e7e7697fbc0da4235af8f6aa3b2a69a4 0010552657e75773fabfb9a19010fbce010f37f1acea9ab1bac0758efa776386 62944b63aa71f64dac251bf8d717c9ee01939a8edde74904189d7843173961cc 7cec5895e916edef5a16dceb34302ef46887ae0e45723593b54763cbbb1cc9db 44a2351de86ad4dcafab2086e21fe546e08700d6790435d7bb25433a97e954da 22396a4658c09b90de09fa1084518c65f0e6f09f4968453a0e01b9b135dc393f 7d80c9867caf37b963500da17523f34daa184c91bd36df2e88396e8bc9e3c37d 93d59589e58ce42710642c770412ffe70fba1cdbfddf273f505d199c9fe737d7 1e4957b8b394fd806b0b92515059e4e329c1332079ab8cb492ea776989b731cc 207e4f037b353054b902e01e9b3510df16a34ff00797e46c796ae3ebfe816bbb eab20647a9ad255d4096d900722b5aafb74817b34ab02ef1f29d2f58e549adaf 0262bcb44012dad9691c89b6d6363cfe07393b0962852b682b706e42dd57466b 77c46603cc151a44792861db2cdec00706799602c3dbda36fbdb82aed9beef14 77926ab79ab3d80948a3688fb06573c09f17c1e2b414c05fe598c574d6f7cdb7 99c4b8e10cb5efd8f538d45eb187a9c620ac73172907197ece10b0cd69f62427 049f711a5b8c4d70e39ffaa13af9e4361d4236b40a1ba4509e2f05d55ee315e6 b253045669271ff1757e49f694488ef694d40b4b9c007eed9f8f485f75310613 60ab4607c9bbad250505f43b082e4b03fb823a90564002bea49f43949f3adc22 132e1cb1668488a05f2ddcd821a426e2658236dc5e8d9a4bd4c8c3aa89faebd5 82be79d4d1a32313816e3cfe61dbd916d0bd71eb1ec1ef3a572f92ac3a4dca54 fb506fb508fc93025e1ce001245bf3705b5464c5276eb8e56a7d5081515b1f88 31a7c27eafc70b690e91afd46c1ac7914ea996ebeda68ccf671dd867c9355a9c d114bd0d5aa2c760163fd9718b9f820c23a41c14c7ad02daec2c876c8073b8de 7f2b5445cdc3f5536345ced2cdc6915adb027b9d057ad608c8aac327c29b20ba a1b8a9c5d2f2d116e646771229b551588da08a14f08359437cf7ba34a9a935b3 f42c5551ad4daea25b2954ca744c6d0252fe07ef6d0ac1914c1a498c36cac58b b5a2d7aafb34c21ab5cd22fcec1f25aa8eba2c1d7f5658c71f450c6140e77c58 960999a26b68fac352256a1258d87b153105aa97387c42612af6b6db4158af7d 8d1094b4c9967c8b69337e138541b262b56219e6147845376a30daa9f990a841 487130eb93cced7ddf7cf99a532517877a781bc0b27c5dd861f2515d335a6b52 18d650c8014498134151efbfa89c335980c6f48c9601af495957c5ee29ff8844 722f27dde4045d09f1d713a480e475ecea2b59f08f5765da9aa495309f1a8e4b 735f8c45423bbe7add306cc2a76fcb2d5cfea6aacf33171e880c6e26af1f9750 9c0a52e1fa541300eebfb087429eb57aec996ca339e71786d814270acf8dfe58 b5e4f5c2953a9e6c00ff6ab510336b92851b79c5f1450780793e013a05e57c62 38f0b496927afbb5a7a20784a4972ea0d7ac6241ed198de0e8f75396433a8db9 6b0ae3e67d84a17aa67059acfe35abebb09f85f3aee93f95884c9bd508be3733 a9a5c92ea8bfe5de14ac9aaaa357ce0ac8327e90b742028d75e0e8e83803cc8d 2d99c618d73fcbadb5df31b595b64621fc06444dc6b834e568657eb72fe6d4b7 051eab3505c92406bc8df96652c273e683ddff6a63e2d8f2792ab0e5ae305637 50786d2915b139579fd748aa04fd03607209a2605cf1f774a6c00fe8989b6627 21e2c96c082f755bebd42b6ffc21d3176983bf0b7116a4e46d23d14978afea50 554e3c593a5e7d9f4874782e445aaf83759adf000ad51ef912a64bcd786f1689 eeacd5b1875e105d828fcbbd2680613aa01e2fef11776ef3b2321675cd4e019a 76b12dd5e8dbc20c4ace7d4193b2d7af356b6ac625d6d13d03ecf9cf367cae0e f0398fa23f8ffc121d12616bf40e1976ca7358fb392f4356129de902c835140a 72c7f5a516cb4bee044c9d83fdffc879b6e9c3c0505d287d06e0f8ca44b06017 0c259829ad2333279f55dfebdbdd9adf6c4d496c46994db14012d0bdbb04a727 7ca7c42eca304d9310cdfc53c08b11bf83a0e25f8163cb1f932d43825d8e4c90 429f4bcad8e649a6f004161727d933df0fcf18266dd27f8531259b07d1b96841 1732e63bb13294d7a51819e1ef2fe9bbb61963ab007b0e5d77091bb0e8ecc78e 3d72c48cd23aa69f11077208dafa7973d50da7ab826413d2410f53d49b7d6584 27b3e7b2ee4bca29cf0479d3dc132f458d7975663f84394bdc10cb335b300e11 84fe692152d6e5d859e5684a25f5fe829c40c2bbc8573a2a655fab98a75caa78 46af56d350f1c860693fe1157050d28ce4d01e1b001a581a9d227a87dd9ddd91 2beab7d8199895f64fd8288c2815ff3b0aa5464442a702d32d1228d13ef2bfd5 59c9dd01a24799d56e85f6cbf650ac8d7ef2fec1382f7f4e76ab8d4418cd5adb 778d8f149a9ea7a5b2eb1fa36f45e14e1457980985dcf42d81c655b4e5c1395e 59a6eed4cb8306561522df1455a234f939972afc442d05a2ef1b408fca1d9554 9290969ab1706ec0dbad490958de6e98edf2c1779d36b3420fd7b029b0c14ab5 128d9133df2527d956ca9b24bd5dc976c75647f48bac2752c7bcc4df7ab5a1f8 7841f5096986ca9d8e3571fca8f8a0954d70664600bc8959e8f7e278e09b96e0 d7f55b5e3efe0811afa0721675a9a9a9a65aae6c0969547e000ade661cb098ad 5ff5ad03e8aa093ed283424ba6b4423e91debf42a43770d60be9ed6e608f02d6 79d34d76e5de9ff653ed4e87a356097ba179908c01131fb64fe0f6134e62816b c50afef45c82b63335361776a9911b01b20e9e3090bb307d1f3c841eb5a2671c 0a1ad4f68aacd9d736e2e40ef6647c35d30ab9702f00c83fec0320e061b9e78b 2b8f0d6a249dbfd1f8d6ccbe53a1241e219db1de938993f53c57838625ba9f76 cb3cfb79d40c8b1c2a539430c60f429cc49003834e0571cb0e5da21307ba4f73 b3f9e24a1fb7e750203d3f44140862f564dbed7732a7f40214ea686264045eb2 d2ec10d702c3c999c81739af30581c9920290abce7baefe7932664d53b16df78 fbebd769542ed57acda2ac24894f9f45ecaa9bec897efcac300b7bd3d4ea57aa de76c6eb858db56da75dbd59265644fb4b954e9166fff1f51ad63e0ba82ec79e 70c5ab9757e57c372533fb0d3118442439327ae2d66966d6b59013a170b107df ce6d1701c64b4a94bffc024b46df06a3ae6a103dfe713efa720fc230b99d40f0 9f751a4ccd5d4751727f7138e2223cffb006b76e4bedb03d9d4df22fda92ed2e 32061126f9607b4771f9cbf3430a247f8213e5f62de200655c91afcc11a18fa8 3553a2b32a9cd19debcbd42d1f0ba67d35a9e89dd5e8292e18095439f7ee8d69 b6ba5ff55ade8fe4813ec4fa6722ee1c2bed99678c3754df6c891260e01cf09b a8f5c6382040835164a0670571e83c97398b7ddaa5e84ee34a35dd5fc5e8ed7e 0c29f1c762c90bd16dbadfff7126acfdb372091c7734bf2e8db2bb67625d43c1 32346a80ec165fa1fc8aca8473fa92126f8909d3d41bd9d258af61b78681a1ce e1db6971b7fd3091aa881907ba6ef2bc88ca2a44039082746aaccc23067e375a 61e4ed02b4027eda5bbd667d6edf58447836b2e1b38e75685ce5b382379c2c6d 0fb4facf88ba22765187b28710bcbb0ebdc1d0e25ce13b1a92376c3dc0beb0e9 2429a8798ef30d2082f277337505bec0a92ac839bc18add1c6c7a98fca34b5f4 25a8f12d6696de52a88f645bb3b347c4e27e2f4012b8464f7de9b7f4b694dca4 ddd8dd1db96f8e1086b601416a4950d74d2ac3ba1e3c194bb2d1af1327b93a30 962232cbb8a32582ba4eb6f8a9ccc06c7d765b00e4c5b83e3040cacfb7a1e7a0 1d87c6709e0aab8427c237e8799af20b78c2f8d6bdaad96da0aa974b9fa0f32f 9e8656f7562d17ca7b5175ebbc3116fdc5e7e7a3b0e7e2fb8c27dee1c6e0694f 57242e48d2ec67757ad0e828add3fae8dce1ca8367cab6a1d3224aa316712cea 88cdb53c78f96070f653eb819fe5d485fa1ca519e06ffc3e4fbb526f03f0815a 72ece6c11dc919c8afff4b1958c1eb4629a295ed971c6ba0efc04e3731687ae0 f38373ea4f38e00f791aac3c45b40cd68cc250d78b3a41573138b8b3a2416dac 0f8b14442e021cd63336f38ecff750d5e553be68dc61486ee6a9fb13cbcacbad 1ab5fcfb2eb5fa23a4d190796bcacc1c78bd2ed95889b88fa0b1893a7ae79132 0804139c988ee130d1241a2bf9c95f766114654c7ba1a9641a626928a1e9dbb3 68af95e82b04ace27c9ce9d4011f4012b3d27f0549df209b590f13263d69237b ac4c59711ea0b514fe11de3ae0fd7cf2e8a869da15673599d68fb144c7a8d753 b77b7a8866d636aa85b576fecd1b0486528be5054d1ec31ac00a7291ceffb5b2 bbd00838a9dab4710982b24912473fb1571a4aee0e5d6c694dfe326619bf83cf a2e1b63bd69a0e441705e59502406212a60bcc47fca64b242cd5c9284ddcc13b 1c6d8c9c325932aa78ced7cdaf670374de8f441acc9b9b1bba10cefb9867728c 0466b908913b543270407e866444563dfaaa88a08d2514b1182236b8bb80a3c7 44c3e8527d00a65353654efac072f681765e94db75cb03acca969c675e7bab0b 9bc63baa8a1e7462556536a80ec67b48b5b7f5ebefa8309a8462fd81066bd432 9ce9277545475966292f8e82f14fc725689d25f2c2feb09d745bc57bc5e975a8 743f1127987b0a268b51c31dbd9a4535dc4dc05d5dadff3996f7f233f9fc07a5 3ea1eb9a3c6d6312f2aac9bf78816fcc378d96606e9e04b9e6024db640ea8a1c fb277721c9a2dc44e70ff1aa9ee06c409a9b097c6d8eba8423d59eea0de77b58 cfbc1e2fa6c16a303c983831cbf3e5bbfaa355338900956b56d8e4ae3fb8745c 9a3f89de4cb585b24b64545d3130e51750a4608a42d30f433bc6404802f8424c a49fbbdebcc097915aa669d0eb8a3748d9d3e0b082155624f3799af6d8ff801b ed86dfc1b7516531c8d7a843ae48de314bac0d47b898d9bd4c3a8604075c70fa a580095fd0129220913087c5682cb70451980433be80ed609ce057105b0940015155d2da4c625b892087cd90e8d1cb4f79683fc83122f3272478bac8c6a4630f933447b625b8a8d19a9da88c018745ab3a042ca2087c1c0254196852f802040029e3b4e5ca779d1d494d5939c556bea4d12be659242a7fbe44d443906b13980bcf0326cca17058f4489363204aca5e8319ff9441ad08df21f0c484cea5923f0c5981c0354471f722139bc286927b81b3dd74df904200e06d677518a0253c720fcea9454d6025718c35124c1b0bb073a409034e918e9dd86ddb4c4d747b9acf083bb5c6fc2ffc960decb4b0e92c2d54b348baa41af9ab47eccb3548ace27351080a20faeba21382f44fb511042e85efa4297204a0ddaac79c74b67232997c810455e6b50d7bfabe1a3d79786323d725978671066127267d76f1b68e7c5e695408fd856e2c885ec261bbecb4e40d5df5e4a56d909b6c66dd587479cd129e287c0a0b3b0bd44788fe75d675ecd3f250df87bad71f081e6b325726e98b704d8f6c0a1089b6129f6b8d483982b7ade92d24b6b7467dfa685a3977cffbad72c7aea804037fa35bdce553563a99b27bdd2ee7dec3049e9bb712145097b4a81e46d418038e329ba0e65c9fd6a93e24309670e3194b51d41e3d9ccf83243fdeb11a9aca075cd6790962160e57262d5533d030cc080ad5605ae636028bcb426e090db25300fe071b942d15145404d5674a67a15c8bf3435e8cecf7a5d78fe6f4a1a000a102261a5cd691d33a8619d2c702612b16ab108e19a31c406ec7bfee05e6f6e0440e5d29cdcbe37c344890cdd53af7e96e35debcb1e697e8ad9778f946d54719b30716a9eb0d8df845304be1970e185678cb407aa22e12a2101cb90c765e1e4d04064b464419212b2f2d3dcf4619c11db641712dcde592ae60cfd622f0701b82a4029876bb99e7e4d952362db3f69144b90533cb1158ea63c82f7a929c89dc89d108b4f0f93360619a53f4bcb2f55b9edd6e19474912713870b0ee9ae321d241b10706eb46b894449a9f37af918350eac6698f64eae05fc35acb68ef5f15ee8058015cc57712769b8e8ca0192d0abb1b2f4858d4f43bef983beb4a82cca3abd454062107573c3036f962aca41330d16c1d5d625d34bf97df38e2d82d0cd6da519d0bacb45b22a1dd37c2809eddce9d5b81a078e90b2d618c34623a878046d72c660b68dee8a8b1e35142767130ef01454ce339c2edccf37eead9ee5d0bb87af080016fc46cbe4b50d012a1becbe6bb4ae827f029ef6bb34046a43e52d216df2855075e23044934c71babb1e7c64b6a4ca344e00ae0ce3621824bd866d0975eac05051c7a49f40604ac0f1ad53ae6c84eafdabec89becf09b55af4ec9176173e7df0d566043181027c10185e723d845c5a55a098295e9419fc92c7e5c9435279d250354481341895094b99f9484d4438f632af2d48b3fd1163be64f812e507266c409b3cd6fa411840f319b05a47f036a340c9e9bb30c20ab90ed859ab5b5b63db30352a2411e5d8236e2ac758108829a049d30bbf1f066eadc7ef20349db86190c0f467b24d040266a91b31a8fe246728f6003a51ae3247012ed4e50c56173b5840e51c587a1fc161fc5f2089926ee396a81123f0e449511be9408a665ae1029bc02bd8210f4604e85dabf4e1561d2c64a1922cebb8d40a317a2e86a4b49f1b7100418d7a5359cd7cb6489576f5fa59690512d45d0002377661600414a7a0e61010a3552bffdcc7952afc21c887a2564995aff18cf18ea6e0656eb7831c90694350f21c4072771002d5140ca4601151a5f27efbf723e84975da2541f0a507c1d3d09e696abf78aa800cf7efff85f33ac596631643d20930c9985e8d2a94e6386840da075e8f96cfc309519330010eae4a0b11672e9a86d4e818dd0d2ed044051ed0d8a9c6e7d5db5f402fdefb88c062c996111bb4aa12aae4ca020abb20f62b91b02152c895271e232394815114acf10e22369201d89d3196a8293245bfe4dc9210035f939f1069bf350a36ceb9dc27da0278baf20dc6060901d850d6a7a0a78590f89726236eb3488cf09d62b36cb42fe1ec87f417d08b8530eeada47749a90ce071e0f97b26bacbcbf3cfa60b2a020b379aebea1732fef66eae16eb4305bd3ed030e6df178882033164aa29d513b0183ffb3f707bf7f25d57810de90909f1fd0000f44cde305c2699cca4da41a2fd440d3ced2cc0f59b44aa6b5f28957a924d30f5368a052156194c2859f6ca252f737e1b9af44dd4a339c72da579b5f5eadf60d9a15f1f4b32694620034f18692bbf86a6546591c813a7f788dcf20eadc747905e8abeb5d8554926cc79bed28b378f9157ca4b10618ae4f5411b8a043b1f7240773ad8176e79caef6a97ac81addee73cb066a8bcaa0c4627bdf61c66ef55c6e05047079caa116d60d83eac153b1605d51a65759b07fab64e4109f98a323959c0c8d421dbf5c68248982a1e9dceeb694f1352612fa7050433c9ed429cc57407e03be8040c62318e7b89f3d18bc33a0a941dad3565cbf9025577b2c622aed37cc0924b74c13ae4eada07b0add50085831ea14f707ac217e191e634e6bdff16b0c030f06455d6f0df715cf4e537019bc241bb0599b363533e94333de55c80b12f507a261d4da937842842476f025753fe310c454a2a932b563e510daf9c3d0bc4b06050cc866c948b99e88d7e08e5d6c40cd11027b6764e87cd82d0822a6092e0002ae4c96fc9f283345adcfa01ec3958339ee6e94d3ad055cede41dfb6ea1105c02d5fbfce16b338bfde64e9e31bbd0ff887eeb48bead8221bf26e5f3db63c94b085be5842a21aee0ea6b0f6330382f83826ddb68289f328dc2546c417a34ebac0e1e5a211a35e7df1f2157560ae18f58c17164f9e0a14b04aff14f88ff2e1ce001e746e544cb9ce2973a8391f244df80b2bb0ef45a88e4cf09892231854c81440c75319ede727d1552d21186d6d1500b615a39b652e1772566d178cf5b8c47d6001bba2d7793d3f4536561649b81b1610ddcd5311302ce2ac66231e52c9f23f408c10846abfb9a04b660e95776acc5e4b93a9c43bcf175b3ef9a6c565bdcae790be549de0f66f76d5c923d6ca9ee348fa34dade026848cb9820258b7c5a18423011008e67b0c2e408ef0cd0afa7ca18a312160d13e9dd979f3af5c6c1baa66bc0847bfa7f88d875f0132cf46a9cfe8979e008b7d8d67bd5071c607c14c9724c800db2fa38b0f622700226b8006396d888791a8cc4b6ce50bd6954133a41a3d320ee98e277cabcc1466795947454aea4ce29db76d2e5a6c9f56f1e78e69f447530788e1faad75e28f156be6ca7a10b7a1f835288031bfd96e0056002beba8786502b3b330a966e48e8530956050da1595acd179cd980402a9875d880e26a465470104588aea6030fae5b8d56258c6fe3f957c12a699e9e66854d1d98663fa0c350f934794397a6368565ed68341e2ff6d544c394d75953f79d62c0b581d4baa6b05f827239b535d1b1781e095109fc9b1b746a66c56456cccce36569b25d9dbc702e16d2a54d97b207cb791450d35977243ac72e1f48502a4212f2b8664fc59df09bdfbff824cfa01d923c69402e7ceea2a79665a45e72422b68e11f4d67dbbad0af637e3ac9126922077491b8330c8bb3221e0e7e8d9284bb4b1c9a205f6ea730e33e34e8c0ec45d9a1f1f93e5da323d28c9e32584ff08c7f9d08ab3e1b3c17f00a73f9b6f973cd3f05cbab6568b79068164af8afae79ffd360b2b3af145ca4d05cb8d0c4e15f56b5100576cb979a196b0986cb3a1e5dea621fc158b587795f10431daa78281b71e270c8473a107f738d638c48c55625573c68ba350d931e7ef09c07fb605757c8eb4a4255ac328932a73dd0b891d0eae53ab2f512232de6ef20db8a892f4b62c60365588c1b57f6aee84e2f6cedf5eeddbe1a3b1c045a2a4c802ad3adb7da2f92c438c2b4536f07a00965b56a5459af5559d416e430e3fa5ca038e81e54e89a45c411034a6faa1c6e738403c20afb9d6a6b56655c39792464a070cd10c038b19c6c99420f85746c07c56889f20e56146bb9c3741f35907ab7e067f9b18875beabb1adc1704be0463f39fefb925d7145a113b890c38ed39e08d0d0409c144bf567960a15115b8a75701c816b91dcc75c3a5f83ec1986e58cbf006a0513ad978f243cae9fe7d42d56311d183e0b3aaa057c1523823fabeb921d7016fbea0cc99b51c15f0aa8b3db7119eaba550f37a1fd100116154ffb1a8831c0b13bf1774899ee5e6ad63bf7b654c5596b8207239879d88d364ab99b42c82210f5a7a961683e1b25f0cc7877ffd0b585552cdc82ca8b453aea01733aadbe4100e4edc265587ebf3073984e14338c80bebf8caa96bcf89a2846ba817939c01540f44cc766820dc1b71f8b60b4675a16b6e242a2497350e1c529953848257aa120131009330f934bb0003783e9aff8fc805de44ae570c6eee4526c3caa14599800acff5b347dc3042e4f9204b63a5c468da077c3c6475a308ef799436ba8cac2e030ad4952cfc0db6c4aa0a6beaa24baa4e6bf9cd0e6540c84d12434997bbc240040d12388ef9d0512a38dfed6e44a2aece7513af9cf4b96a063f5f51d1adc7440c9d39e16cfdb3f56231aba195c54d1434dc62ccabb0a5588a85ecff6a97acf301dfe9256ed3d92bb04efb2e93a3d9a7072e8539bf1c3ed2204f16691fd8d8340fecba75bfecaae865a332b1cad2a4138cc3213ff5cb1661610aa7c35028713805f7359d56b26e2ae8547e3f2a716a365d7f10937e75f94599e6fb2e6204a7f00b0fff28284861e728294c592f87f1f8ba39eb26426bb00905748936cf4735f50cdb5017e1521cbea8b9e0709724fdc9f9d4c5ab57d4a0aa43d16ae9df6deab501234d705d316a8313fa786fc2d5245ff828f274e0fefefbb694b8c2561a73d901a278643c890deb9c18125eae23e48b66957a3a2a8c8444163636623dca09f40fbafae37d8960bc3be38da77924218c1f281c5cd37a74a9d2bfc574b5f377ce08d779b93492cded2b770b91848e16b812c48218c16b64dbfae6d7432fcbbe6a0adb187785706216801bc70eb3cf67209375896ddcdc622a3a3ee8d4c14640580bf88a682f9f45ca265044fec9b259d60f033a279670b0f48f486288daeddecb03bd19b7d2d1b32285cf483c2e35120abba54ade4a430a0be5652ef498d69f410ca7ff414a7e495254e306e0d69cb54f14ef8f32cdc2c8fe88b8dd9251948f5005f8bdd41ad9f76697e4dee9d34f7afcda71ebc0844584314d2b955deb0fd1660e27a2c7aa09e1e2145a115ce42fa2aa52216b68c4d6743bc8ee633c5425cd1301daf37bdc9495b10ddfb5cabd13a650f3bb553132482fb37e61af7b6d61389d0be74ef4a2c9448c138fe19c5e46c00cabb40107a24010712e9f1209e3e558d40d9ad694c443a2a007f561cc43b29f0e14ad9925c621acb2a131e89ebf0ee11b020463c1fd4c5d7c20b3cb6da750f8b82da2d59f158f526342da8ee41566e6820c2eec8824758b98f29434aaff74dad8a461b5b8ca0b154731d5eb40995c48820411c3b33df3a4598dac28a77f00eb4df42841c88068aa97ea011521b41556250f53a5f2883a66e7801064ecd08286c095fa5955c878701b40301323b53e1f540aa090e900f4aecce31705ca55a978a078878e25be57b6a292ee0cc772e043400043d22d25d4c67171916301fd17a237e0196f43567ccc0251332ef30aa8c6630254384e53a44499c40c3ff3f19cf7b1eddc5f44adddd0da8f2ab3245b6079bf035d9c006cdc73b9f6b4159093c58bec2f8d39643e5a13727fcbfb25af959681043731c46a3c7ed6517f4d27c2b51bb9a56b577a576fc22eea8882dd13254efd099db92ab7b0a539f94a86eadbde254c05f1d2d4ee402fe5092bbeed178d784b06ad04dc9624cfba6e17be997371281c52279fa9b5bbd268e4ee62d406d481550ada7d18c276494bd9064e40b65fc51ef579fb88d69da975ab2d19c2b7d30aed0aa92b9542d5251f53456a32421f965f32436a21995d5b37800eccef2c1966a70e0bb1f96eba5aaea5548434099bfc2e4b5f309001ff7328e0d1f6020aa8f2a10e809d779cb6943b39cfba109eec570e80ec147d4efc26cf204c3a2d2ee947a907952f967202f55a5736935255b7a255e4aaebb27ffb4563f38664076dc0a2550dd1b0002f126de2ffdcd448f2294670e8d19c6dea100b71c79eb119b6189f950b8ef390167337128c8ee7fab946857a38043ad227f2b88ee751c626e1fee2d00ac04bae0987f5b75d8e3f636f254dc202add8a13f9a4f7e98733c9bebd99c37007e525b6af9bbb66d796a80fb2d911324b1eeceb63e77cd4c4d185647e0b076007c3ced3b88247489dfa6f697cc37d41da36120dc19ee976218982a9596a3bf01dc72daf39595fe70ae31be892b9592c90531fd36e15482c9654dde3202f0360c3b0cee5fd4574ad81afd53ae9743f51ff1ab8b33cc92a9fa86958b7801b5ad04e7b2357343d7ad41c5d22e439331f9be0aac9171563d45ceec0962858632e70718cf32c9ccfb00bfc600874cfedce4cad23f4a6fee37e7d94785c3ad20a86c0fa7e60938cf1d29e40a9605249ab8aa841e94705779f3c63499b0d8ca4e8d380d784b1e8c2ed7650990f9e612ef7f149d8dbc74b9586b2c9cc07419b3c79fc00a500b4694a113a5d35d05d6da28584af03b2452eae6a1630dc6e72871ace3930923cb936d540e646e6b300d18ce1f6687d5703f9cfbaafcb6fe582ca7b2027008fd2a1d04b5e5ba5a24bc439249637d93d0ff079fa8c99f5ccf90ff4d875c6f0e06314ceedb36b4d3e6edb31723c0f63b454e2ca1bca7cbe1dac93218cbb8240a96de8df3873ca15e3cf91a9c5db5e94fb3d01cd6451772020396ab4a0a0f9e0c44638c5eb19347ab7b78df3bc242f90fa2efe0978c5089b2248bf6397a38880d69f5c334c7291272272e94fd588c32b4abb8cf180bcb12d64690e7e2e675ba001de5d689cfbde6aa1bbc8883052bf54d0c01ac859add22b2aeef36ba79e6e00eab6b4e1c9c80578f033259b06caf947a0bf71fe792dc27c06acfb078dfbe8004db2d6d642f6433e8fa6d73f956f251c48beffb23896e9631ac3539a107b5180c7b72c20c9a66fcdceecd286cda7cf6e608b6c73e9e4bd8f99b18a7ad81c5c50396ae487045fe524d9d69b26761f9813a27bf95dc45b0e6d463814e46238bd5047f6e76317735acc4311b04180a92561222d9435b10ff851075f4e918f6f3bc06effb13b915cc5fe3b8159fa3339856b31718200319f032d3347504b649ea3f00b59ea98f7ec10568b669da1b9ce5c37ddf67e20f679c233d3f40883fc49d1306ff4f1c207eb5f34e827652cb675609c9fcb7d121651c5c62a5789a057dbcd50ec032d4834dc71b53ba4a0887f40f003813cd5b05983bba80c3d45a02dcfb4605ad308bd25d8bfe4984e8de973fd15ebcf659f56adab0da34d363c587ad0f3b032df92212b45fa106736932360c25748c54f0a957465706f27498310a3bd68d0a3861796abc90435431f53f74ee44c92686d570aee5733cd8458171e17f9c1f0d346f43a0098b9656f6b7362a16d5ca74fee5a69a678818fccdc39236f2e9580b56498ce302ecd87027d4a570a5fcae352df2cd9f894063b9be215979482b5c02919ee096906eb513daea091044829542d4b85a6ce3f571428a74924616d1be0a15602f9b762dd2707c2429b8fd5dd75d97959196f4a514712943e37659504a00ab775e7ffff387a8cd88dd4a9d9fc720011628688c4a812b2d5ae206e358f50e3bb7550d174eab9cdda2822c865a361a09b89b93ed8e76e74eeeb40e24d2f407c20bda2445866fb2db59a1b036a3f2454bf47da8ba9f7387b909f7dd22cf4c06ede5a9d358a324802bcf545071be6eda6e2cebfed9adf32d7fa990d83f2f9c088a8121883c1338255a44e07d878d540b69d7ea8f1ac102bd83887c647dee4604f502e62767911ed062e7e2ba957c1c22918a1048eecd5e1d0f0d07a5de2ab30a9b0541fe4ebc86782d27209175176df1fe73dc3dfef41fbd57e504b51227f7006ca283089be856a83270606d34d25b7165ff0bf1bc6ecb44bc9dbe18231f600d35299dae43fd7d70f7a4c45528c784f6cacc63949187976cecac836d29126f06ea4ea4201ca956b92317a0f405fac12bd083eefa6e7d87805459d5d5cb7a6108fbf1e4faa22459aaf19cb8a0c01c63ce8f2281ee82a9a0d0fb5ba54e21afb8099e4b6d3d9588efdde4518c23e82f0943c14b63125a27f89674de9412c34fec0d4bdb401d069e221799c8f46cfea827aecc4228053f7a03f9a8f4fd4583362801722ff78135c3f061acaabe83c92db68ee2651fb0b82408371fe8552d2cc7fa0e8cafbead2abe45b8d81085c50669e800a8315e9b07a05213686f1521b157360d15d04c99eeda18881d868771a657d1413926bfe980e77410c11f96e470269b004c3cef00e7b0e4fb856b58d4d1aaa26a0a18a472d89c04f4fa1e31cb2dbb6007bdd3238c6242b030af324b94608d76652af71eb0fd4653cde190e323ff88530cef64f3f136a66eb754bb6cad0cbd1a2e695b50d045a27874cbcef643f0478a07492047acbccce6c4bc37798dcdfae8a26b97233000d0853612519f0f3a8e210af9b5b9894bb45fce31be9fef4e3f4390cba166d94e8985082e2b5c01715ffa0e6a9f35be18727cb52fe53252e8b29b997ef009e692aa658e235650faa845c203f2d8754c7077dc56efc37405a849336ec94897af9114dbd3730bdd06e750bc08b7b67711605bcb37f41280c2928bf0c00d866e13335a7bd0865935a3d3300d08717e1acf9c26bdf306cdd683abb06a75de401f28db24f13494ffd9c47c33dc063d794bb3613657e1ffe13fcd844d7afc8ced86f783b8ee901dbce1f5cd7d87032eec232fa46e62607b09bea47292c751e4685046e439c895e7f3f2ebd3e0660d2788d33934683e4510e7b641beb27fca8e9fec7c304f0bf0e4bd984fcfed4d0d9df3d6cb94a411603e94a24fc5fe18f2147ae27d00e089190b970d720d8bce0a69f972c67807b43938b94524ecbecb23e50e172a30dbaf2f33d6bc33e96a7a0a094741ca3f3cd6857d3f3ffe4d4c6c5002f5944508addcc0d5048b7e315c9d0380b7cbf6b798188fae75c6f447beb2385eb4831e0f60e7070d0184ca1ad9ac0c7bc06bc4711fa003bda14757633810c1a585cf6e8b336e754f9be6ca55a224025466e9313a6d76d55c654905ec02dcba67ce1b590539a5d2a15186f5d1549d07193b17559fe9aa4bec61480aff53c8ba8e1e688498c9d6ea5879d10e6d33f40ee596a4fc52563d6fcac245e06b8b13d8a4881e97365cde2c8ae5ef8ff95ae702d336633274054df16eb8db07d5c064f0a45649875a1ab6fa5647c2b1fe768d0d36b621a2c2bc64a915531f61fcc1be58896ff2bfcb093e17d267b433274d120fcf813bad0fd0456c2ad0ff328baaa53c8756b62d20e12f09a0c4d805470ac900706d1e5f8be77dd5807173905790576c8470e2615ded0ed1b015baa032626901308d76460a34e38293de0331350e160ac494850b1d7ba0bfbe2fc99e14068907b3ebd0de12e43ae595ac76ef79ea461117e0fc7e28430aca41f92663bf14b50bbd44fa8697be2a3531ff1d5a9c9b1d87e24fa5772399e4809f469ee9a7800c06fecad7fd0a4d4736d400a4f6e205d6b0972b5ba95e78e64288bf6e606d83c90b5c3f6cc180ae391d004aedea001c3690262a36383c5fe4e4bab855f3beeeeb05e1c4594320263429d360d987bdccc056ebd887b11bcf3b699bbda2ed9295d60d1fceef5adf21794ad699ff2b577610f69099efbc425117fea9f3e1bc4201170a1f9aeee0e84fa2ddb9af590e653196051caca2c4108efeb77b7a5a9b1d484406affd8ede8418299a3de25ca70a22cc1c7443e821cca9647805262910b8f91905f42ad2b7505ac8bf34eda9b45b7ec17b65cda54a9a78b31be1f3712bd6f48b0d2093ac256fcb1d83328540e36c4870dd67ba08d1eef9e83a2a2f7a1409a1980bbe624ec253aaf36e5a55d13be21e92374e93b0922aa83472eceecd8004983f0b25da7ab5000f366544fbded594b51588933a97e110bafdf5122408a54d088b0f7d5a3d0171e74137c06a8a5b50ad2cdd9cf85f1014c87f4da4c1e08b6bd0d00e69e5f53bb0ed8bcf9a92619029056c32257942b99848d3c7c9b25cd3912c42052ab9d0dbb050ba129528296eae2d806039b6af7af409e617c99a210d74f40d067a0498c5ca7c03f3a2ffda455d8a894c4d7b2d6c7d20e78de129cdc14efa310132f00701a06dbfdd642522591c214fa90a5dd0445b2ede91f38dde72bed87c0b52d329d2e727b3f2189b08b548d097f5443026fec3be4a14c289b3c5279f3f0017d71aad99b5963f05c89694849cccfcd2aa36134b0838b18b00d091c0b7aa04ff070733ae913a680c0274acfea9b3ba1606e1adc3473112708b4fdbe2ad5907a17cbb9f5a7669a2683687f56e18323e1491328381087f189d5ee50a713a80098851f4c48c6cf7f099598211aa6b62a35b763b43706c1f481cd8667af85ee000304664d2dd559c9a76f98d6d661fcf44c81c2f98e9b0456153d5a1d2ef7d50085022549d46675ce666e0b05761f3f489b44bd35b628cc5abb320704e2cbfcf04f0f276771c2f3ae508ddefc5d2fdc22d326c4a302d7db1ef5ea0ab1959beeb0a2c94e0c96dffca6302f049085f76023bef2e0369eb8f35b475586d7184f0450ded1bac7287ea9eb20f0c0a8d99136695860810204e899b0b2f080f51844dcb02abe839a2d367118f102d05ba4c6917132eaf9cdf053c274ca0bc4067023b3b0b829a2260bdfacd7ecc4b73167fcf68d8e6e6ebec95492573e99531148f38ee073afd1932f68b9bb8a5ff736c75b16f7cb6c721ed68d7be9aca43c4e24b39750c69ae03b6929bc14df184ee32fdab78a6256879bb3e31a44d964a8b9739ff840004d09767d95b796852bb49b6b833972b233dbde5b45aa9ad0baeb68c46516608fb6da45f85a1d04bdaa1052efb24fefe83f6578cdb7cc107c6535d026e44550cd81bb6a7a91eddafd00e40dafdc8265531ffd500c6091555a353458d2308b20fba8762a6baa01f8fd7bcdec94d2ff9bbfcf175e8fb66feb109e8a82aa31c390b126bd6ff17d076654c4ea881cf6ebba5d02f19f5d4fcab16ec97777c22f04e089251a7f303564b2aaa29f9ba9d2b8553ec868bb6364a843fcd2de724aa0ea0044504f8a68e36fdc7c3aa3611504536e833a192ad71e0fa73ffbce0a2589d540ae23b9de2c063c52e376946d816bb60ef9a18471c30c95faff0f4fc72bdab4d05de18684e180df5383c30786543fd9f2a9e62b2dea407b9aea7c1921a19a59109db789c1956fd8bb4e36b9d9a3e138fb2bf3ed0800d791d14746a312c90f62800489fc9f9d37fb6f1524f80ccee453a30b5c41b8a5409e49a99b1a33b97b0a809a286ffdd5746f8714e670c27ea95e52731a49a38ab57a21eb5fbec77713ab70aed13c550562b10b5c01ded86e17c8517b0b45b2647196f7bfc43ef4fb14b9008eca1fb81fce2d480338ee53561248d822dd24f393df993295fd00f19c8a562036f05fc272b9c112ef5b2d15b89d5a9832067dac389540c7d2e5859ad7caa660f65fc6708d1cbad6ba7f99280392b9b98542661d09a6f9b3057811385e75ce0010da59b008feaab8d0c45a43314281235d455dad78cc3185c4933428210927f0f2d5ff51ce744283dc62a3cb23027030753fdac6f3e5c9be8e32bcd7835e62a098224647ee7ae7bf03be2085eefee2f6d0f918aae5da92fc58601a00b7b8d410b73f2a7400fcfe1e7a8fa8e091bc466f1a32133ef228354dde77f66e96f70d70446702113df71e85a753ab4d374eb61050fc400f46354bcd3e7b6eeb1316f79060a89c6373a30a022792565704caa40875f915286b3115f1fcf286f76ca5c0a090f1a145f97a0b461a8972a2ec9f2e805fa05f24a92567c94ea97a5943248a200013f96bce0cb0a9975304f259bd61cccb9bc13b6bc033ffb8b3f453f239ecc002c8e44490433607ba57aea056bc89bb6fb8313c6d6a68600d4dfc284406ecf07b4c3d11fa8eb82d544c65bf391c7fd23eecb31f3b4ca2340fc6e1be0b111a30b40d059d8db7d7f28d6d2b5160f1a51963ca88d62e9608b420c89d77f18631b07143d258a8af5fb2099c96aa69cc7287605dc221fb2c50feaff5dc7972b9c4500501554a44994f385d0e725f551b900a665eef008646206e308df28731c6169045d09e65714d51e02967d5b1321f5292be0045400cc49ef1e20b0f787122ff900ed4d39d2819ed61096892d52e03c031e9eb97e4c0780c928b7b4c2b5ca4e4900b56d9cc2e758a4820bd0874ed9819b2b4f3d146d5b45fd328a164a442b30db0d0fe7c2a8c362cf1762bc7d48c90ba4c302a8bd1e1563616179f8baece397fc0d0a512d03bd3676f602c33c765957f22e4ddca7188ed9b4223dca079b1043700333cd06cf61067024d026cf8aa1b05bec3d9101413ab1c41ff939a90e4d8ae70e90a8d3c82f4ae5f31362cc93081089b94ef38fa440dd748bcf5e97f96ce03809d5f7f87b0b5039c19de4b86ddd036a3c5d8202d108cc021ac8885c32d57a430dac8550fb0334dc6a322803c6015e1f8f242b9447a6a2268e2a3b1860ec1ea604bab873a4af888fbfd7dc59a7632d92996587f610dde7620361b25c0ed6ffdf069547dd967a03f39b74998d462ac75a4e665e385231c870cdc90db499dfe3cd053aa777b91bb13473d788decdc20a537a6ec393fbe244c5337ef6982b8227eb0c842d2d168852cb26894069dbe0a40c8452238cdfc8309b35513fc7e61380dc0f313aef2eb3e8a290e51a688c5e1fb5d5c73c16ff65fbf00a5a1c310d21b1e704fec3c77ad94ca1eb55e880109d9c9443a09f1fba8f650a2b2cf9498f27cd640c035b4023b8c99fa5320eed7eacaf176d1490b2b34881700325b286cbc88f900bd0b982ae697b8e809f7aee1e8352e8ebe035193f0cd5963788053b3c5e36bb056b9ef3a2b036184d50f0a34afbd02ea1ecdc8c53bb927b1c9475d903c6189208953af0ca19a12e14c31d5dbf9df37a0050f0e2706b0156168589608a60d03e0b578be5f9592f39bf070c434cf049b28a60112cb3c748e54a63f8c77e3051810d7e741f59a7ceee48f4d1ad94583d69ee8f4b82c3495c95912a3e4953e591ab04b751037db60b5aca5be7f81adad0adf2b6472b906c30dd5deb21523ecf3c3105abb9134e8a6065fa2f40c5e6bf3b311bbb65d6a4171d68d500276d706ba93206d0733edd11287b7613b55982105f453fc5529824b76e2313beb66d9f6b28e706801ab30bc34d55e426c045494de651b58de6349d62da430afb58bdcc06408e0ab4b9ea4b2db3e46421f51f929e127fd9ed6d5c04f9e5de088eb821d4c6891a0415070142c48b7e3835eafc2a4e106e16a0febfaf01fe572a7cfe845af02520002cb7abcde05ecbdcb4e1060cf6ea0c8074ccfb0ed326abd1b95dd2f268b4770f91d1840d8806bf6ae42e996438c2b18e6719e207d09697b70ef2ecddfd3031003813882933fe192998fbdfbdf8c2ec9d4c67063a3a6460fe603528289c5cba0d94bf458861b8081d473031a245fd7d9f9fa284e28dd241e7fba67272ab03090a7dd24a09ae67809672cc0cf7e884792cf5bf98cbe3f52a6f61c5c7ae50252a0754f5dddcc110083ba8dbc56121914210f92f8a30fc72f80d6d43e941501d5f08db4850ca6e2235adc8a12eb76a60169bd773229aa40a3e1f7b533f2ba0c0b70efbd168764df4248aaa681d95e66f140454e0ed9620b86e88e6730a63035ff90f88df5710695b4f1a2505661bdf09eab14c5c5e5bd003e8d487358b534e94770d18483a1216af0a60c06967c468dac32a844b2591e2072d13f39af3456f29450bd892da0d22ea4a1af3ccc2da877093ef2d11a59aa3b592b2603dc54027b428079b0d18b03f116d505cb33b88dbc4b289dd620d5c7d61c5ace93ccfa5015a4c0be6035eff2db7ab521c2bbb198e4acf71dee5a9219fa08ff84c96aad942190302c368e0cd0c888cf991cd342aa00ba36ca283f24afeb289d4fbda2320211ca50629b61b32339288c8c5fbfaab2e659d03100d9d428d642d913b9d2b117f403309d8d83312a0971a63144794add9c417e53de976a650aa6f8f69833fdf3a9e3f0795eb2a3cf397c43c049bf9e481cf508a31854a2871d675c372f2c4457aec850905f0c8104f821a30e6c79cace172b0ceb5255bc500d35e6da3299902fa85120996bddc611b6e75d2bc65486836ae40fa306abd8a57b2a9d58492088176d2310193ca425abf819871e8c41611a3259c53cf17617b6ab54a3625deb53db4e8b00dd7f605069b62bc555983a1a9dc10d47a891ab119a98245b08bd085dbf43379059484843e0a3723ef21c3fc096fd8c91da87702119d844855e516fcb35cf2d306d200b031968afcb55aee91e990e065bca49cfc6b54f61a943f6b8c927cc063020e3894ae2a306eab078c6ef73a6bbaf806bd642af0ffba20e2cac0977f009f0269b8da71e0042261b8a117cbc363cee97af59686fc32cac9a0bc8bd56a72340e0b157d108ca46c2efad933d5bb0ac6b1342ea5debee777ab2b141a0289d5d90f2db41535e9020bc101be4c18f4695a0c74218c715927428b74e073bd81fb98080a4f035094250cf4278166a802f3489f0c9518ffa1b3e5ca344598118264450ef86e94430dd8e5ea810f9f376132a6896307676d9f6476f17b9bc768d5c76507fed183b69f10e5ee0c9f275b7566a6aed935850c7816d51c689cd433168c100c554dbe94b8543ee47d28c452f82453924eefb95e7c297a093a861021e681ef04ceb374d9ebec41385720e9f75dab43cc7cf3c45940e1e4100f236adf34c92f0c1c06d6541d6e16f364fe45a22260a631174ed2afa32ed2c9cc73cbf1ccdb2b0decb16ab60e18616122033bad6ee8a38624c552e4f2fbd43486084ccf70159301d2022d8b61ab576747d54f75ab71b0344321bd5dee76ea68687db3bd20a51b06f6cba5511635be4b1f37c4f2b46418369a19d0163f9284d8d768684fd05c1e06926021d0825769a6ef96809e199c742b5a42769eda4eb795669ad25a4f54710b8ecec50a739ccf38460bfad4f23a86321a7c49447479f2a46521c7ff8be97e045fffcbc498333018edbdc4908d3e7cf967a68ca778f5a84f4cd3cae6cb3cb8075231e069533f8cd515b0281e4737757ff3ab4fb26e0fa8ca097ae24c13d429066da0cbd15f2281ec13c4e1d633b5fcfd101d0e070ad0302ce67e86366f05290a1d40724687b146e2098e1a59134082a6da5f868289c5ceb3a394647648573405b851deab0b40ac63762ba28db730ccdac8b9d6d84626e036fb9a459046163b0ded3fbee3659a2e51ca32789caaa808cc2b32f5ff3e5c4637c8e0dfd8d70a8306f723498c552b9fbb61b6fa9deb81a34226ee6a43f7feafd7dd1f3f12131cee07d7e8d02d96e611c26d3948b31e8d2e9d0c2365448e68107ce3fcc3cf8da1ea04b6e7c6adcf66656d4676206d704553153ca359a903f8b4a69356dda1178bd80ddbad1a970d741f799413652310ed0c05cb8fa7b3e31af694d792b9e6b1f7ea052f9d3eb279894033f57445f271a5db3d3202babdb0c2a7e3be4b1c255cd3f40dabf285e2d05e92c22df4ee2e9e379ff73253a1f3505b5a63d0f89c2206b6930d0007a59d29c09e194da6eca4cf2084ddd8aef270c7b4c89f5d62c6dcd66a5507bd53de2c9facb2109cb1eb1ddb83926b89e2cb70c02df6145da0087928bc520a28a57a0fa17146c9d4bdeb21c66d1b0b9a9b2b2dc3fe7e744ea3e0aa40c2270696f1354a32585f76f21d956f248b016a6d35940e4f16d46166f1e41c4eb6f407ec176ccf2a2fc94374e49fd0841c60c24f65df0b353cb34dfaac95ff886a790d3c176897711598ff97981b9ab76d1a10b4963567a55cac2387444f496c04020af45824b0f35f2fd8e2193bfcc01fcbc762b8c462264d4703403fa81dde4f0805adde540227e63dad6b35a66d8d2d3a2dabba5f29f2112b30ac46ca323896590eefc205698a7d7b8ca89599a5956efcceb20ff378d144ff920758b813fbffa70549e1434676126048436b380b7f928c9e9e366a125818b234704233dd564cac0d67b2e9745643289518939697e46f3f7a0d25c05617b8d7997653e9195eb91d09946bb132de8ca2219f47dd8f8422d43443905c41989c0e39c7c8ebe13a9a930390e78a68878caeb99d1b79de866c7c24445006e9855a5816e02734b4e2aec300479ed60af66d6f7867d1acd030c908b870c3fe714365728e82a31c3f128be40cd376dec06f9ae53b041693f2d158b8f3d8a21173f0492c518759cafcdb8fd60e7a3c389e0a7e4278f87fd643aae2ee367bb6b550aaaf24b5882dcb666f4c92047c317cae3a4d7224faa3035fe31121ac1006f81f4e1e31a76e1fbd06b38ab006056175f63349dba2468321e616f1a05d33b83ed723e8cee46005c75f4632030c2bd82fe87a6b8281c700a4bb3fd3fdf9ac167867be0c168105b83331a0549e0859efde427bd232f74a62e840a177038c6f560a56f9a4d32414308bec5f61d006b5fac588c62710c5246ab39cbb48132dcafa38aff4b57e2107563e97360ffc0cf6d8c058c524eea0d56a5c85ca827ca99e042527886dc2ce340a4a2226a8a006b035f7f5d32a1b514169339b46b185cb79682f161772f3dfdd68e93434d49d064215c469bd7ca9ccefe2b2c42d3f21484c4e176e3055bae3e2da43cf167b9d057c6f612ad189d2fadd302aa92165f08828660f2507eee4384829bee4e0817e060d02be453864edc8f46d4a41c2e77f74319db960e34f34f8a1b1747cfe5d9f02e2aff62d17714bab92a32f44ece4b93d259a13ea09df6f05bc08eeb8f8e09a00bbdbbe65973efe5e6de020538861db496524d030aab5f5572d1c6e23cb48dd01ae0ee7ed7f68a119131a8da780d025f314b48162b879d4dcf2d20a8e744d9b00f87727bf6c06e9fbb1ec77303b3198d7b153886b0b0bc848d6071e2437517602ba2ddc8f7a0fe4d18248e054648a556b6ccc0f3f370d90a6525cdd94ea73c105bc0953fa2d64c4f849defd291f2945a27e8bab4f42c39abeb5f350e40efe39019c3880b3f2155e1fdbe340b145f069ad5d7208a848188e01bb46779f0b6e150220a30e0488b485d4c70b46cc3fb367c442d925537ed8d7d980e4b4631ae185052b2aa4481291667434c1ee99f4a3759df4d0ef5123e233607f621d4ea88d580ecf1d0448a0f84a9590b1fec8827da54a3ec191b3c0c126eae85688b8e7091b0655a36e6ae38558f3f702dd9eebec7d72af63f8c1f667ab449f1bf02810fb840b0b51ffa135e799cd639258ccdea500f4f4f2669c717fe136971b6cbb32defb0890f7209a86c51dd2f256f3b3f2e3c75c55708e5f13b0a58415f63e1a60fb5e056aaf261e7f6fb3d2bafc9fd38e3a0033eb1ad8021b08e548dec15c540c9297021d0ed6704f194473d49d1e852b012ca5dcf2f9cd3db3df48d1a197288a6dc105deb271370a2bfba13215a86efe45b856e1135c80e6259f45c8d9faa7bc60d507d82cad86edded1acd3b8b3e9ceed476ac60ff41f9ffcd97e44266cbf6e2d4b076c0d8da6a5f93ffe2386dc0da72649009f00fddfe56034ea14dcf87cd55cf20c51759b7c1da173a7550d830ba087e407e34346bf3768a7a50f96ebdb6523830539521ba6f28194bae8b4222ea0dc2578dd4fa35596d30594916c178287ea0e05d1ed776f17bd2a88e00c43c26e590971f4587d0b365b55239f9b36bd91d48b098e6ae30d91a9218cb98c480796f5be4e2fdcd30de915c8fd6d20c356e963df07a82e77643a26891484d170ec0e33db64efafdce0df3d010f5be918b62c0a600572afe3e357ad08bd76b186a1e090294b107624814dacb28a8cd896ae68b7bc0b0310bff7083554e52f5923b375ff1435dfe1fd4be82b3f8d1bc9334ced7dbc0b7efff2178cc5a95db1988810988c828796062e222e35aa168fbb9012da87450a416d5390beed43823116d6e23671d571bc90d0a26779ff998cd1e5681a1298044b40cebe9e6ff415b66213bbad29070edf1e6f619a3845890193ad032dab0404e7a66183e5160735f96954d497896baa8add47767fb9c3259ceeb345a17413066b58f273a733f6a1ca17f8938c2675482adfc5d5188e1719ebe335084d3ef00bbd4f9e7610f42e542ae9bf6ce7d1902a359b15973f8c3565f449bb7d0a8a4d038b50a3b59d91241cfea7566b22fe586810735780da4b9f622dca9ccb5a91bd05613428d39eef90336d3c2613b9956429ada0df2536fc7f82158f39d8a473f4057ed4f9148f2bbd87834079e9adfbccef758137b738677bd06a7b1b822133010e00541826a46ebcfa9fd5369f58ddb2724f04eae1463300a2f9d6ad278a890900508917c8a5e25081c6d44f97c76f9ab3bd82db70ff6815ceb36898a18277fe0c491c29200418d551bd74555b4129fe2b36b1f635c09c11d5f17299dd6e98920d01164542bf9a1d71baa728cf7836d816b60c315226ee2a608be2bb390a7b2c0420394d40a48bb8fd27f429480b1a828c628dc343e47307659d101b2dd7d86c04e5595df28f4e890b41a55dcc7f10f2a5556a010c2e9de661b04fa295b8659501409c489f2cde37da28b1a56904b5d87e6be8b72be3f84280f6362d7470cd06044bba4d7bc784550d35012962ae96fea11bd148badc4fb4f3a99100c2ff39310628ce3c26f66b59e70cce6b33787509b359e48caa01db2901c49b94dd540dfe0c87bc877c4faecc13269c3f9b353a90452335cd4bf0fade38324af8992cb37303805abff5223796c906f856703e0b3173006787eb129a947cfd4e9fdddb41e109 true -check_ring_signature ecd117b9cba669fa155d07fe42d13bab7c561fcc4c07c89dac85d83a36d4c3bb 5df8263ae413837d52aee51426aeab81d311cc950e50eda0831e479dabc6398c 90 846bc93eb4f19541c306f46ded1f57c4a85935a537a9643b06668c46dc1be6dd 21044116ae2744da80ed1fd9117e3ca68cfacdde262eb156e0a9c507d4056b49 a5d45fb258385ee581a0d66844f5fe1d3eb9d920d9854c00302343ce50256dfa a591e1d1bee4fb0da52440b6788feb3e5311902c4b0fc334a8d2f2e64d2db099 079cf4e82749b5d33e653b62c5ac1560ae5422ac69fb3f853d03f705233ca228 2969ea5b675ee89a0765d481ae48f0fe92e6913ec6a3bc4ecf41825c49c3c862 b13ea0c410ee533f04c2a514e2f20fbf1b85c1faef21ac2d1257df52b4fdd403 1441224516e5abe43949c036f42788b77aa404399d8895a6e22329c94489478a 7129ba11e9b5d4c0f200c7e0c1a9fe336acfbf2f8ecce5651f230c98b64674ef 432545e4cb4013b5e4d0569f3b58790823d6cc1d8dfdc5d025e4af69bb0b431a ca7743c146394bb3965b9aae6ee40ce3e46190d43e9f9b59b87e36e256f901cd 5f4471899f022431230127c00f43e83ecb0f7420bdbccca7e30c98befccae8bf 245e17d7122f167753a7263d355b4b33cd4554b98e7bf7ec70b2405002a484aa 68e83483bc1e3b5ca9d9ccfa8b4077311ac211e096667c0499d45be73d031eca e4afb453b1bd26bb3a3336e3aebf989cb6b08bd430af44978145ad0f6206a786 d556569a4e6a1a63b1c77cb4393f927c5f6c9bbd2180df3d90d9b369d98c23d0 d9b84f3ff98ae90e5b8d8121a8fd4961febe67b9ad4e4abee30979c4056aa5a7 0ff18aa5e71194c92e6fe005f3137d8926f9c8bacb3416b3e669353a83309df7 bd4fcfb7825c51dac364936c12fe6482d71fc6d43707e6864c8eca284cfac333 29a3d5e7966b2efdc51e1b0435875d2c55d90127bf9e07ba9e4eb5f756f4567d e8eb72d257106aad71a25960fcde7d55ccf857101ca17670c3ec67c7047bfcfd cc23a88265cd00c0fd73937ca3c1027d70c4671ad09fb2e780f76a1325a5334c 533bc67f525bab281aa8f5a8147a1f89e349001713f839439d6f70037ea55142 f288381df81b1e5511d0cafe7c1cff07fd5e20e767e4066fd0149929eb8e8e7e fc80b70f26cd53effd66ec84d88b5d66632bbb1903ab3fdd1b7546bb09d7e31f 1e3c239b82d1e7fbfaf1ae75d44b7151ddce9042bb5185eb9d33febdcf223e36 b0a08b4ca97467a8170a9a0bfa556becdca636fa8d012d5e73f16115b2252b85 d7c75cb2e521361a22ffeb7c8282c2457d7ac97319d93e9353e33170dd96aa7f 4db96ed8b1e603c5b67af9afd3b0d78e5fd446eb3b0a2b909dd8b896bd3a5f35 fe15476eeb260f74b284521525e606639028ae8ce5ae4429270ac29081d72a26 3b11acdd15bd3a2f4bedd4859396a7b8ac875037169658e75c2b27d7f94ac61f 5c786bab2da11e7232d7b1990289077f6659140fff9c7c93ad203c5ac68d7706 78ae2498b0116d496436dc6a0bd33b86f1326fc10be7051024cc277cf90a24a3 7059a3b573d478b06e7849dfd1041641d8552ca82d4e39cc3cc4896af6766869 70c016f18d72332b5a1a267166de7bae154cab5489fdd02cb3c226d0da5a3ca6 fb68fa7b9b0fa95bff77d52ed1e54a15bdd53394a9557c02ec749bee6a1b2296 a54226f8f88e0b2d69168339987e9ef56ad7d93af45c9e7edce1c998a9bc6b74 508a4237640a708b88b3e231d6e99960094774a3aaef9aaa381b8a76ee502750 8637b589e9b473bda5c4542f770538b58be15e6c0a3fbc1dbdfe56e997ae5a78 829125f8dd82981e7b9aa7d0ee5a1b1d281e07de78b7f8baf0cc58d42ac198be 2bcbc5a22396960eca2a0af282e60fed0f5acb68673459a0e90af34d94f6f274 dba587ff713ef45a383750d4f79acb840f34ed2f74c6d3344c2d3bccd3ef847b 116715f0f3e843062e68e52017aa0b47ea8413d9129a34af70831fa859b35ee4 bf7c461b93a705ca9b4b257cb1701f10453585b1b86bb776ac0945de65a18b20 670eb6f116e3a44d3ce961d6c75ea2c5e30647cb0132d86367537497ec8e870b 372ccacfa378470d9313c5e9cd160755f24dbc7cacc88d46985dfd5ec1fe6eff 4273e88e488329cc32a98eab80551d0d5a74b70b0996bd6988b321f565fc6f67 f47d632cbc11c0875266951a45ce0f3ae929c9707de247bdaa5b0444ea92207c 7ad7a54a5ad005a9d5d88ce51efde009d6eec0180fbc8f94565ff7bdf568a648 fcff32c154ad24a81b779e9c310e8a2fd7e5cbbf88aa1576e3f4d178e4094dc4 40d90dc913b1bf5a095caf58b21eec6c5279f79b783b7ba2b79a1c670e8a1788 76d8f28b989bbee21d73a9600f6f5015618635b5740a0cf6e301f614e872eafd ade6268c6570cf2ed6cefab774ff2ee9a18a86d48f338b86abb7c7fc3f5ce23b 88b9c8e8795c26aee9150d4997ff4ad62f161c86ce21c51741f32a0590e075d3 06ade0e368f1f8a8e2a25f475cb679ad18b2fd1efcc27857e69dbf543677906d 189e92e598194c23e712f0eb226d6ebd1cbe31e5736c978213d2b8409408c946 3be77dee4958cef85a853d881f7917c24efdc9688eaf146ac1b8b330e333add7 a067e574aff3f5a71d8fbd3b31587a09f959821a38c5910f3a75859e8189c92f cc371b1ec74cce7da1bba4d088b8bcb062b9c8a56df990402a9f0ea74e22fedf 93e57e6f11e38d725c56033dd39df67c91f86e88d4b06aed7eb459a15b4d825e d89d097bf7efe985380f68a04f70893fb9fd63c82a64ca062eca89217acb9b2c 02e07d579f2987faca7adf5e5679919b78856752ebdc13ac8330265fc33a7f91 4072b29c76b1bb1199b20ecca174d1a813f4064eb16fd6467d9ea3d9626c6616 e8f02e69629dbd4ed1e7ea8fb75c1bc577ebd9c46381d5f99092fadc29b97b81 c5813bb6cb2f63650e8e8d6565f4761be7f757ccebc09d9e2a398228e5f1c065 bb47636a3979fd4d7cc6a8701601d1ea70e06a9be32c56fc34355ca8297ae018 993520879730cf16654ba83b266232d13824fbf70b1d752b2a6f93daaee46ccc 5a1ad35e63502701024cb049acfa0045ab146c63f937c0ca3920eb6a816f07fa 711d9e8d1289d2c6170bc4db859971011256e9bc8bddcb42cada46bdaf274d1a ff1f8b7aa4a0edfba8335e614a4c159e376fe854ce46cee35672ba91739721b9 ec2674bcc1960fbbd299b2ca091888acd8db523916e1e422338642e4aa7d5788 baa8c1f9ecffdb817dee08b604041c02b0c6da5be527a9089ee985985557e79f 93c8b51698b9a896e380909d3a50c722a36ecee017250512305f68caf8d75c94 6c4da8687345230a08d257185f69215da1cc251d9de65dc70950922264b7a89c 33409f50ebf18687a91e5ea40cb247c6a1a7602028320bb5ec51871507cb68bc 26da711f063decec18b0e7ef97c69cf7a37ad970c959337c133b729c2dbcd77e c03803f85cc7494281366a65782e13b544c2aab1c724fb4bbd623ce20ca30474 8468a37bbe9a8f965f5192306a0d16e2c124c7bc0cc6b4cee1ba40a19ee95da0 7008476d37e516c71cf0721417fb7128e53895a65ccbfb022b2fb4651019afbe 2a3dc86085179c13f27f60bac0eaf6f65f67e3b49874332bcd86fff82360ff6e 192c6d5f415e17d015e21852674f1f82381fc27c8e2e60ed09bcfdcc835e4877 547f5bac8b71aa5b329a46bfc851301236b7bd8cb8f6e2f746614b6e13fb2027 c0e3c777ba99a4b2499b51efee37ca64f2df63ac08c2a2832d34c0949dc522f4 46aa86ed5904b7555931b103a66962ba8834f698144c1ec7b41e67b015ca693c e848add2aac5752b38ca72020fed13faddeb0533e7669b7a1849a2e03d8a464d 3feb46493e774ac3ecf1807dfb4c83dfdaed14057cc482054baad2549ee1d491 ee35e0189a09da93051b01a1feabfb7240b526e0752e1854e98d5f2f3991fb63 af381d721bc51f8c35312489021cb0fea20162269a874444ac5afeaeadb8f663 e8418cd4a1163eaf102d9ca8c4ed655b28b616f679eb69bc50f32219a6952fd6 3f1f5f135cc6e61d7d046b801eb6ccd2affff072b83361266c14e2fc14cb9eb9 d2a289252aef9a66a8968129ce5f36211b1b644983b729d5c6761ddd6898570bddeafbb19be2f3549cc6881dcd43930e7bb4871226a830ddc0b129c95ac90c009efb02223d1f734081adfbcdb8453a8c017eca2eea3597fd3b530bcb04d3dc025e2413cfe3f36f6d5134444b3eb9f3a813a4ff0c1ce9d98f25ffdfdf0483a1065c5f795722c7b4ba6ea1c38a0b2dd2184f27b8b60556d0706679799ac14b6b06e159712d77d9790f2e1d0130fa33075eaaa16f6365e10adc0170def3ea942202393c57dcbb17e88934513944feb09ac4e20a545bf8415e5adea1c0030de3f508f221b1650d3cc2170eb5351c2e5ea91ad1fba52a43175026831af8c1a0fdc30cb0d650fdba3f2f5701fa5bfa1a9b3d160b7cb4565f663d47cf47a2add01a3e034c4103cc6579f0136eee987b8ddb32a01bd8ece5c110b5100af55c74488653072238394857332e4433ef78d61e5ec7f1124cf0b1354ea966d5aaccd97c51b603f70a10b3f8c08602d94c045b161b41efd59fc64aded8cbd1d7a133a9e8cbdd0c3b04222b369b56b450169993f8a68c23865c5f4ec05be76aa44678d82701ef035fd26b6b2ea7d330b635a6dfccb5ba51f61178c730588970576cf83b133070034e85664acbb6540234da9b42fb0364258248ccd2fd743f6d2a57a74f2cadac03931c79262b60c110c2cb41d1903b6b1cb9ff9d3af1c92e42a7f1b71b46f0d50569d6b4d800c414fec037cbbad51a31d0f919cadfa0f69b726f30f5bd088464074a1e9e1138648f495f321d385378f5fc03f295907f8a434c521590cdea8a7006e478b5cc7cb8430c7fbae08d5886078f9be79d57559eba779ebe30e5cb5b270127df22097c5e1953a84bd09b29a7633501b735fb17cdfc44c504d0c401213b0a185735aedb6568e57719fa242c34936fec4cdbcf9c63831c70688b8721be1602c75e8f75907411352649c3c15d4a26fca93e2a924fd22701e565e1eb916cef0c24a8995aea797faa90f7c7d745e57fa5f7eaed5e740e554a62b773bdfe978d0c5b1e3d9727057af98742b41fed7255731d6e00852f7ad4379ba1859bd98fdb02449b97751a33b642be1d291d50fadf93987d3a27c0973222dbe95a0b40a76605f70f7e147d9af1198128532f650262a94380e89d5b20c7f431aae4e03268ed0b156b8ed566717f2decd587b1f515320e332903ab779a31f3428bdeeba4c8eb05f70f4d7788499f779cd4cc5cb120d67129b9678d644c178f5ab4bae3d2ca7f00bf93961a2825cf8fca91ee81c8d42a09768e57d5e0ccaea2664884f895c01f0dde29418afdb59a88bd3c6417374e4a42224a52c676a8f35815474eb234f7ed0d42ab36ed20e613fddbd463b421c6fe64e7dd0203ceed7a21495e61adf91416054f3e9452e38007edaf452fcc3f83b288f696018964d84836941d92f4f512d603aeed0b72a2b2676e48e829ca63f0fde88008ae900bfa0530307bd25bcae0810a0351f81f4c64b4dc1ed94b868039fd191001c4a82d7f98c48914fb2e6c1f64000a9a6885aacb1109fc6c9b7d2b9ae078dcdb79d7bd4640492cbcf8798a3e830f2cac108ad20cdf34748ae69f918445b07a2882983609149d774181333e09ee009690701a920bd1f973327ca3c9ea7d319e1573d6d200caaff9435502f5dd1c02f2e810aa5a64a8b9c7e2edc0d33999645a7aa42728b1358319b3ab2618de4105fc5662d841f5bae132b8603e9be463d201e4467a470b259d2d508e3df949ed02c084c7b8322e1c11b9f8f919d85f7732c26d1e03461474f99f33f0eab2a905025e71c9e47d053cd33d3f8565badd621850c4099b244f326e38301329fe38620e90fcdbccf98ad46cbcac706369be48cb4a9babb151818543ef4e7eb7c7c2f10b494b67ba304e3b6f7f660c4889cea8a835214a7642e475f32fec329d02c3230b716e93e8148de59c4bc69f2f45dff0b0503abb8727d5b58fdf4103b6b391040a4d5c5b143bfc6df353695e52ee1643b6641dc61cdba05375b0adc2fd7fbffb00dee66c3920e3fcbcd042c0477148a59094ad85f1c3d13f48834ac0b7c7faaa0b9b8fdb138b57d498960956f0a649ea3c39efef1a8271765485089e4e7bbad601b56408a6d90af88534cd8a2a55763e4bcafe8113e140aef68ed98615281d2c084d755cad2c6afc5f146912943f808d0421ca8a77991a4fa8fcc96dc5cde68702f797690692e24f52665d186a21c0fdde0767a083aebfd7916576c5cdec272a027bc1976a7e1df638a18ba8956fd964985682831d495c42b266d1710291d7cb0ff3093762bdd951f9b94f74581356e083672e7ef4b12c1ca0e4a5ab7be90e810d8c2a00548e2fdb3781f57855d21bf3a22a87c3394533e0f5d832f7bfdbf4d90c5740455a4ed40163da1111506fe0a16fbc9b55c67cfb68f1604307050bbe0d08cc0493b03b470abca23eaff953e8d5334c42d69b7ba1055c83f86f904b062c0bb70bbd6141efb649dd82f9febb36b33af234e2eb13f05051f723d7c65bdcff0d478c312962ad2c9f56b43c2f99937f6d186cff5ed904285b5fdd387ec3900d07725a7dde62e351c3584e5ce89c02b59ac1d5e8de4e0fe0e8f408ba88aee9790c1e91603abaaa6d626e6b9c4a59c94ed03e33f2639c6677380f1e8b8311384a0de0a7c0f39adac950f05586e5c134840fde01c13d0f087ffdd293041575a76b0a275e5385691b5ee2c9c1b880b40fe2693be43ad43d62a940d8cad9a2626ac908fdb5b9551f200bcff9be02083b2c5ceaeb4e38f2a475945c72c3a7c2647e3d0d20451eab6db5a1c63a05890e9e9ddf6c86c0d4e9c1863485dd912458c534560740408fca915d66f84f52960fb9176ba189675099ba8865a37c02b966b044e70e2ba1bd85b1b18d8c93e88fa554ad7ddbe5f1031d714f5337d281c58d59d7380ccb068573c786d09ddeaf338be6af592ab8ca190f88d09edba610bc361cba280edd9cd205ccce6c7f41afe77432d8704970170f315aee2ae785ea6397b338a901145ea25c2a3911037804cddcd07dcfe9da024d5694b5254b0f360a78cc0aaf0c2a88b629df10d41e2209d9d9fb4ae2a113b08796254241aab0d50ad5d7afb40d94ab667d04b24db34cc755ab7f1efcf3058e249a64d4f3a9e3401fdccbd95b06423efe281dce0f1b2471d207bf50acc5145eb41474e4ea23d7e1bed56297d10f0d365f99e059794f0d558cc8144dfc371607a9fcb413bcf1afece16353ad190877c700e5d0922cf3383ffac5f3a901976bce6802a09b6280f830c75fbd3ddc0d16b9983e83cc2d0ea7d3bfe94b231590b2ebed67497b7c119f149fbed4c572026fc32094c757bb6e8468abd193fd41677388b141b23440b2720d49365950460b370f47f805dc7df14af6d286f0a745b0b018d27bf94cc6b7818ea712a8758d0611a7cf9c4217b9208b3ee9158e6ddcf038c5324d7e26609cf43611677c3f7a0c8c4b7414f09d980bff0d56651b6c03248445e636eff8f435c1d8c2c4cd00090dedc6888e19ab5fcdc5dc301a9df7d4ba7e527a05a431f632a5a6d68c555a550d25a1aab0ec895a2627b289767a75a0bd173eadd86241e28f7c60b300b3e0bf006b2728ef76b4ab1dc71dd650378bd7db91b9e4b09bb2a0a8943034434538d20fe0b6c4c9330b66d2798b8a04e5341ac461c03e05687a5d39a9561f683bd5170da9435ff3d47f3479136ac870adf6abed6c2c38c01b577620f33e5f4c5721500427008a455c5deec142f047bf252de12bba400657e111e2fddaf60795caaa420a8bbcb54ef10b59c4ad12354cbfb09d53f00e993af460856df8c756f75124f50bf206f8abe3f257110f2a03b1212780a0435255d2fe3e30fbf741b5b0a7e18a0f1536518069b11c7918963f504a06dd6fc38493fb228e5584facb017cf6359d061ea903de5cc54b3511f6ab851e1adf24b4014a68a806ebb2cb6a1c8c4a3b190d767bd06877b26d26bc221cf31bf14627c2d08b5f29b27bc9c33792c1d99e3e09403caccaf2baba791bdfd42a072aa3f5fbc2c4e957c1471bb94c9c17b5a7110f480b211e95516c3e81626f435832dcaac0b385daab3c5125f680695c21789b09ad4fe67736722bb0d27b0eb34c580a93fa16b7052671e09c298200cd82871a0bac84574ea0e605384e820531ec5ff3af02acab81a7c673737aad070d11447d0cb3a90e156391de05773046fa46387d4dfc23dfd945fdc0fcb8ba7955377dc20c03954e0f6d28cc33d43b878d8243e23897a67b848726e8fe68e931359cf6cd0a27f76fda1dee52a74827483bd2f2d955c66459210e398e73afbf71f7091ba20ea9d3ec35e3a6442368dc301bcf8ea383b601470d11441aaebdb4882a5f518e0f997195ac970a1a79a51a24c19a4d3a94fa34e62a1e95219f043c481c73071f05a37b8350224e2a99cf4b4b2da0d4b33afd95d80061a1d727f08a23a4a0ca9c08325fa9cadf8898f322a1b436debd56183bb7cf9b351045f898dacafe7f3fd3039e919fd22d2f5b5f8dcfbcca4f8e6b4a11d6edca82fd4b291f8da62dc42238081dac6d447a37d23bcdd4d21e6f66f15b6e6e05f3457da99234f0c29e3774240fd6e5a15ad603d139734b9a39ce5d21a34d391e70429dc06433856ae490560c01f0e7b0307caaf8d0203be1f71eac7caa7c89be158896e00de8fd783c69e4280dd0f4bd901aef4dca556b397c358f419161274c52c29e6acffff066d663f19208297822115df2d1766d307e58cbfdb5f49c1b4110fc66b228fdbee059f0436002edf33831b738a62a9d79f4dbaeedf8ddeb358c06f3cbb8c135b65d4b3d53a3019230d285489e648ce51bd229ee9904ae151c2025703d5e392be2f8073921e408f037f09835ab2b7b45ab36e84c51896fe2b724770b643beb1b46f62e23011c0521718f0d6c71ec2dac4775fececeb08756d97f4aef59699e6849778af1cd310ac8562206cc9bd5883a676d8395f9233bc5dd21fa34840019ebf44cf46929020540e20a914e2816ad0563256c1bd8a27826955391870fbb1efb554044c0fbea0870d755973e66a3a0d4b18d72a2782f2c81cafaf1270d66787ce9762db3f83e0b97b5cd41f57d9f6bc5388ba822e0a77f9d97ce7a3a62e99427844b1fcf2275080ab87f937fbed009f58e825b538bb20b849cbcf1ac94ff6842b8e3a4ce9a8e00e413028d2c4480b0f65d8f00224925c6a562441e7c924b96d880e3ae6826fd02aca0cab8d5a3217c699882cca23ea77e7fb87e73d2c0a84087cd3d83abd6cc08da247798e9d42eecb6839e25afafabc67e7363a55dd1242553fe09d7de8cee03a1fae0b5bbe8edf4aa8d3c08d7f825912023226bc9690c625c0d62f91301260ba626db1266cc5bcaaff4d5d59b2e15e12907072fabad505bfaadb9f55353ae04dfdbd98ff37dbecd274f76506828605cd76f5819fcb71bec9bbab184741e490a151e3631019927c491cab526a1a0421820aa20144560eafcad9715278859e203b2b8f9fbe8bf6429c86c6c19d6c6d096ca1fda0893ad2f79a8545e280bccf40e91d67741f67b87afff6a9dabc5005425848474c82b2ffea66eaa7a580aa9be02594132dc07d966ec1e57be6e8c3a3918b7fd9ed59f565c8ac66e372cde983a07f74c744e0042866c5323fb1244e0091074c31c6c415f5f6b3404f17f4267d30758c2e68495445a6582839435035e38a93f4b89635b1e548f18a774a00d3afd086ecbd109e724269dba93ec1b843c131ac197adb76ca9eabd188245835c1ccf01e067c6b97e7a087e402c0a3955365972764c329cf5083e6bbda1da4560713f0875215c8978cd5265ff1844ec3e823d79e3d93e2c931e2ddbfd8dfa5befcf8e0ad58538f9f91347537e35c820a76f8abc978013e603e55efc376927fa3957b10f311b6afbd96cd29aca5586570272c170c24e30b6e5af5590a5906b37be39f20ba3fc0616d96b478377571a18de619c65a1408b4d9238276cdd204b8eee9702092b1557b76e9caa356b83b06af2254fffb8b0be887327dc9271653f3e57cc1706106c8645170329ae4dab0a60dbcf7e5361a29a858c87cfd6ede7f2bab5606d06e4d43622faf89795ea129a2876c503db574970d076589d3fbb7a281ee43e1509e09a9dd184a46e50f5d5f85f919c69eb68ac14704bef933de7fdbf934f821507fc84389583bf5b4797c25041abf78750489521e1349f8fe79987ff0139e7e402728892559c31063a61df8037a7b5a7a9d888f1b203415f616abba56c2dd73f0ad6bda538b781c5a89a1fdc6f8f114c53a2c18c832e60a5b1cdc77907d1cfe808730e2b4155bb695ec032d6b5d60fa9faa95a829c35c9660d931246ca7e082b07d6eb0dfef823944ba162fecb8c94bbaff640c3c4f72c4f692a90d1c8d65ef50ff613cd6f8439d46c30a88fce2e1e1a4a9d190cf3cf5563b4210638199330f00adcc6dd31b2df1a8b894c6072318672c03dbbae01001f855cd9a8fbd235a28e03842d64ba679628e19f605fc271d036397c38e253ed13479bf3da24c79cf125019789d0ef2157805273fdda97d1024d71b8ce9e06c99e1755d185924f028cf501524c5a46470c938ee03e2fce35d523d8134503058da542e3d759d9092feb640ca3761a9bb037c14a7b087741a811540c5f409f19159b0bd739ea8a8dd93b2d0ea508b664e508b9f448c691d3f4673e29a54040b31377d857a9818ed2a2661b0b4fe88e330f47611d8e668cb3877a3a37c308fbbbb75dc2f2bf5440050898de03a4f7b7bb0be97db67b2ec6a6ad6991cd6e617e2473584e9b7d24ee38c84484057f4c6df707ce037eb6b045901774d669801923004727d6e3546c4fa6f4bd7a0a3c1f6175b1cf42c25127db0be1d5683ffdc73b1a8d5ea13b62d18d6de9022407215fa1a9b59619f79765f0d46f2928bd6838190452eab0da1b795946a0101d072569f3fb7786c4ed1b3afeaf9fee174e8e63df779eee579dab76cd24e3d345003354608e845286575b3912eb9b29f00c0d8810761cc75dfe3712ef6770c31307b0fb6ccc72cb72cadc1a52a3d729cdbc28a66d392a30c9e80af8ae2adb81050253e97e91b10ed6fa3494e9de0b9d1574b7509530181492aebb03c51890b33501f5ba7f82b1ca90724fd6234cb3ac74d83231396040dbc6da6596ea635f6ca50358c2cd6c8994509eed0fcf3fc4798d1b7f76cb4411e87e50e595a7adb1df63006a0897fe2912a55a5a6eb6fe59e8143f132c15f4e4770e0e9d96acff33aef80d081a4e9bfd935ee7ed4120c8007ee77716392272dd7d1c28b810a4ea39fe7e036f889868bad96acbeb31c47c48b3722dcd9ed03fd997db00e9a24afb2c34d40bea8d488035237b8c2c0f1ae405e196441fdf26fdc292f13764e6a88b0335a004f9a690eb8e1cad07609aa80bc35fcfb93b52bde62f8ac72435d01939ab0e2d0fd6253666d1531fd01bdf33438c02c8b08640795c38d7829c6cbd512bac3cdb076a14a172c44adef3fba78bd3568e8e417117d55758a63ce8efc0c27b2d505e0b020c12d7ec572ef8478219bb8078b0e184ef5a9d8d0a1b0f3dd907ae6684380d0bffe390e04d185a88f940114f3fd37433c82637f70051b4422c0d8e9b4b5401e3845737e3638505bbee19949ddb259b01a8fe59b2a5b40edf922bde0ca3bf09874540f2c520fab19fa9aa3d31ada175ef36a589e858fb2bacf5a8a04319650bbd99bd25bbde7c72358c8e7ebce122f7e2fb3e114748ecc9f2c2e364f06a2c0f789e4b5183567068e90f2af4c31aba965f5034b6b8e225b07926e01e1a677d038f8f57cf16d224b97e9028158bab294cf5ddf03013c38296b33bead1233e4a02590ddf68cfa91f83be68e185c79ea307027626001aad28b32c18c3600471d80de46af5fc52cbf82581d6248fc5f140045f6a1b1d84b8240915366be7d5d58a058df386af97f09888623da5072edfad2d709399b9be60ab124442342f385d670bbb666a128b64f91d92e37151d0d47b23aa4cbec123f8f4959dc95feb3a258b0f0ab3d51f6db0fcbe80318ba457841b6aa858235d04129db1686c118b1706e00cda40041e50e3a85afa5a273ee0bb96bd9faee48d3d1e053e6209f6b80bfbae06 true -check_ring_signature 0880d195a58a524741d1c35b863129b114c3fa063b583ca04a8f161b7bfe742a c10bd82bcc50b7075e8ba8b60159bcdf6de919aa12c22f8bbe7cd00fbfd773d3 1 298ec7ec2b1d262ea326b35cb5629318e2829b8046cff0a5e464fe1a81480e24 03622ce0abcb565a040b9a68492cb4621329e0d8246a0e7f0d03513a5c4871002e50750495c75d6fd0885112124112d43105bcdd06801490301bbab4b3c64c0d true -check_ring_signature b3ad516f0a3a646220721ea9d5d34922575accd2ae904c59906c6e20f9aa1abe 167de63b4e3ca6eacad7e7e35a5afacc04c8464b14398f2235df1c95479ccbc3 1 de88a1d66467173d4274a58374cc8ce66a2e3c086ba92a392f7d306e6ef0d8b1 7551988d0535a080185aebc804323c28476184ee5e464e1fddd6d240611d610d88869ad5876e2cf50ec3e1b959c170b7277acd17a288f68da52f6c9dd81f8307 true -check_ring_signature d4dd7016a4024232c288263dbd436dcda483f350c1ca423403b11ca7ba82a9e6 b401d01c826389f312b28808139b1bd244e907b8352cbed25c8f33c06c90eb2a 2 d90a1b0f6dc4f621cf489e86692f4fbbbeefa686171f16a8971839d2e75ff215 336a5ec9fabc91469772187b80d2d7251f73626dd57146792f8be193709d31c0 30159cf46cdfb16227222c3d01187edd66c1f6234ef80d0b647755390257f8997df9abb80ace55d06a91dfcae5aba6fd5c846e2f51da8f637fa2ff8bed5fc6298b1b9c44428bcfbb8897f3d1c71f7a567534aa09879a6007a51fb59b0c351c04def28bc2988dfecd29e4e03f9e13413732f047f99edc2c420b57597f270b3701 false -check_ring_signature 8f4612d7d3061a179cb996f22680212a37e8d5a98637da7554b68e6b901b7473 c2f7c6996a5d29c820331615dfafc4f9a8f52317399af8792203807b4977f099 1 212df9b816ba752b16d4d3644af2a85d8746db5763e331153c709de87b4623ba 0fcb63a98892b4195015d8a774badd1752e261002dd93631fe0011f0892a0b0f39c6d9934305e302b7292e27a86032a4791df55fccef42998e142aa44646ba08 false -check_ring_signature e9ea656f300b1a17dce501def522fdb75dbc6930d59932b749aa9207057076fd 3be7b69aa31afb500bc24b471e0b8ee6ec32e84ce6464cbf45a23415cb2ee5ae 54 1ab8245d7b08cd45241983e02805a119f48b9a2e79d5d6b675202dda8db37f6e 30423cf88e2032a917d1babcd00a47eb474c807154d18d5a4927ce610062e191 3636e745e4fda31a07290034d2564fa8dc6f39127699d478655469d8bfb94988 b4518b5dbcf4029a783c87ecba320cdce82f9921ebffa5a3d873ad77aa177884 8b5cbeff7179af105015ad892a0b2c2ae1dc44c9f1ed4d998cd3a4e79b08ee39 f5933398c199a1eb11bad26c8b78afd53f5c5ba29bf7d824647856214edf493a 1a033c4759a82e08824864df61f14cdafdcceea2e6dd72e468b22325c1e00ba7 ed3723bb0721a5630847dacc47f6e1b99b869e354e9383408373917821ff2106 8533a51e77706eee5d856c3d54a47716856c327d3b4c67d74729f797344799ee 99af97dbb0ee2f1bf6fcdb34d49e23d5ea81c56c1484bffd9edea109488b707d b3d45b7762532725843d7c6d91e36469143e8bbb14154091fc001171293abc62 064c859b05ac09506a5488321501d490663f57804a0e2ced269e2c008c7c12a0 1e4dea3cb194f3fc93b0a90449a71ec692be9b66ec0d2fb0d218de678096db61 e323fca3f73145915999edc4a88b071f4d0553468c03d2d73d1f844de042c9a0 3707eae829714829b98151d026ae19d83c0ab8e1cb152023a03dd4c3981557a0 a4ed8042344af960f1d17ac448eeee49affa8239b0cc3dbcc8cdd7b248b04046 7ea0cb1889f7d80b1240d3e956e04c3134b85be0aedc041d6b8e99eb114532d7 7ef25b6b9019b9c6a9a4d2ee65a71536e5a7cc0ae44878242105c72bb7c97924 72463077774a9f208883514ac8bacdae97901e745f2317afff7497457ed0da7e 7cfbee36d03efece506a05fd76ae6dc7ba8e6cc11ab4447e4ed7c9c8dd8cfbb8 92d2349fce23dc72e5c25b83b802dff162d051057ef243ad7f86ccc548ae6939 d8810a45aa4b71058f0244ec7b3458e1ee855aec52ebb67dfc9faa260d3c0179 53c04ad53ca2d52c950fca9f7c1a56ce1936ea659b52b9bb4f106a6d8ba223f9 00336d88e4300694c4997ab15db22e4e6bf8e2ced1a3c543dc0dfa5bcf8bda9e 52888ed4909f66da45f4784aab5d07b1aacc5caa52af01a79292b2c3eeaf52ae d276a8ab7f69c7a8f00d33825014bcef778ebb3260d308fef89bdbf0878fa957 455a15ed239bedaaa32d1010e84164cf9f496162d3985f6bc212cf712accd960 7bb02b838fd886bf0d401a457c96a25fa8e128b1802eba1f3b6d85f4a86905f7 98f6a9fc190dbbfa976467a1db5a46036955fed2b79df66d13fbce8f8231c8b4 931737008870aaefd0894f9132cc2583e30872c575dd5224bed7966bf263b9ad a85bf1c0c29e59604194dceb26c8873032c816f3568bf6a7543449da3d3024df 4cf366d8be0df62d2fbc1d4e95e8684957c7091d3f8991fe5b7caed1a6ee5601 f5e67501503a6169186eed0cc28d6bc789fddb3fd59473327fd867a2d0aaf205 c79621d7312572bf400c546eb1ba22c5d9d952ff04ad19066dda80048e40d121 9ad6dfda3737d49054b3933fc5146991690af3a4ada863d13ce319aea6d1d4ed 298aa478f692779d8865740c16fe594676b2361301b773484f107bc729ab7895 d9f35dad28553b196903a3826f5a4950d5a793e7ba5ee227b4ecc4a1eb138949 0661d906e29adde8b8af1bb6cfa487f81cf80af0756c4b89d65ca80410a4a834 ac518d7939bb3ecf1562580a0d8a653c73c9390ab56f5840f311384fdfd1cce9 174872b41abec20719607e41a669476ee90476c79746901bede20a2bb212c171 a52c1089a318057f439b4cb67e0729dda02f23426a8355b1e07fa01fa841c90e 5782083dfc0910fd0a3593abe46890843de61886d9f10a7d42e2addad9373d47 bfb486ad268e69690279db8901d1cfe1c08b54a2f8247bbc99976ca296cf31d0 d9a22620f3bf6eb67d772cdad0afcbee5567bbb40de5d1f0be0a66022b80e5fb d1dba29b8918b09afeff1ca48d6c1c34679009ab428c5b183290ea84a2cd9990 7f1acf67dba2aebbc52073af214619b56562e60c4a7a90862ba8491ca41f4dc5 cd132ba4f9da51dce908d069bafdb88dd2766181524d3d1b108d4f43fde35117 0b5dd0381f8e46c831095a02d8b0d64723f33cc7829de750d34459b6f26a9988 70398accca020590f60b199e661f3ffe714ee17042ec70521ca7ab7fc10e8925 1aec3483fad914b214e1c74c76251a2461a510b50d9f336cbe476fe3975e5c45 7b1b51c5b88780ea77407c037fc013b51ec8490f276bedb1c490edfd9460b381 441ddcc419950242deca5d8864b9bc237b7c868e708474990c44c3c46793c5ac 25f53f95c10190e7a607f96e04d807178989ea6f2d91419562b9d5d1baafb6aa d6d1fef85423903f6444eb10a1e9d5a7ad8c979a1b0adee4f9602a74610f242f 2734cea13d3c9422ec79477314a252fc57dbf74e5c3603139eb1b4a0f00a180cfcd6dec027e7166dafffc4d292e683f76c9e58734585d9f58ff824f74fdc5b0446d9257c21ffa4dd5d7b06d358c48bb67c586c170939f5b742eb716e44f3e1049cb6bc01a49ba5a2306148d4c2922e3a864a66b521dfeeb6bff53b853e2b0508ea1a8aa46d37acc4f40987a01f0cb43175a91c4db2d1aa1990290e4338cc1c0bedb6c9a66b4f4413d9c81e5d32bf00997cf42086e9c483a585276b9f5245d507508c83930d17b8b378e088ec179fc9b202dcc2cdc3e05fe04ce3af8a1721c700cab541adf0d75d6a3c1a2427c47f606aa31acfffa112ca53b98d8f97a454160658fd6cbf11c58a07833e5f7bc480de75f2e2fd7e89e637fdde18f1aafe45c43cfdb628ba62d0395f945d185446192c18aa9e09b909f5905d3621d33d0ef4d60cd2a4b1e7bc7da1723ef6bbaca7ccf43127d4403bde64d83a14c03632fa3b330b59a376e207184e77ef20472a776fcd3f6bd514c953e4877ff56014cb2fb73d04382be43ccd42b7d50bc4f5009f203969bfb8ad8ee0c7010b95cf5c8ae4f82d0e4e0726e47b745eecbf1c4a9ba7f20edc853f5ab323486db59f2d5255fee9b70e328a37866428fe4964fe21be0cc8a584f3a11e249e96f151ceed72f0115d8e0c4a9650ad28759e82154fd5005de5f107afb73e2fe1d799c94929ba15456ec50a27fff4ee6d4beac382e0b7c31ca19ffc67d92a1265ea2e612394abd540057606cb40a3ef233f909fe40a7c2539b1ffc06d969c54fa0b9ee5f993b33d6c14ad0e59d291cf50c758cd1605809ed3bd7fd52b5562ee6c49f31ded3999fd9775130eeefedc80526897490cd60dc72c1e2319e35554db5d74c2f55403ec3fed86350907aadde541603b75f78b7ca4d8da9a2d50aa4d8d358fa45b048ac9053362f60dc8d89b0f674c778923fbcc7cf2c2f8862640aa7e04d7d3b06765934de6616b0f48854bc47997396a19dbc6f72f3e0b5024177287e0034ccfa89348ffcedb380849d4f81b54ba2d53a8d2495eeee270e6dacb43afd218ee50baa6018594dbb10571c2c505b67342d1c5d11648825feb785652d2c4fb05b64da77452ece5c26a0c8619b9ccf7575ed11ca98e34f1fe4bb56484e34cb88882035153a45e27d5470a5d41980ba5333c2bd581711eef08b383eb76eb1ac9645fd9d5ae12f5242b960ad0f45c8e8d34915dc3c0bf31aa2be22acd7e7ebc4cf642142a6574f7c82b9d01578bfdee92c1e8812ed529ddaac65eda3c917e57f32a23879b52a5bbe038ea004c0d7ef627b5d94ca7d9c8dd405076cff8b8f59b1e7d9c67fbb7dbeab020f003f3ff0a903418bf6e4a35675f7a67ff4503f87c041818fdd8f544fc8de675dc0bc8eb13c0c7b03fd84602774aa9d6d4a585b36aec63e3d494692716285d7b320eb84295ff2847ab7e3e7d568d6766d213bec25e12d2c5c10db0f8cf3751d5240e6759d93565df0229a046d41c60405cfc0049f31df1cb33fec8a9de6aee83a900a5ef37e39683a1aeee1a203a2796cd0573bdd00fe7f4cc11d17b34b79a8d7609f9d54741117c6e1a00525fdde1d30d93a29b2c2c298870be4fbb459894704a0af93474119112d4cf7014b53b202c172570f51973b3bac26d38d6a466823c6b0e2064a95f661d7e2059d1853b3bfb60d7865dc3524c32266b2c5abef62bf1d705ed9165a920d50a1e1b536590f4f7a4c565e8fc0219545e77163791653910560dc33d33f3427927e3f3d09afea279302b9b24f8698767e097aa6acadd7004f7084fb1242bb0c416e4cf94c2eb0ebee72cb05200bf0eced405fe77bb8861561c0ab2f63f4c003944a5d53e45204d6b8f6fda44efd40d166b52cbbd237d5654da0e5050b5d154dff76cc61ee38e07cb3307d6cbd8b0bc3e0c99ca06192ea55629080b1fde64e4f4bab65e2b5221f9fe1496f1072fba124cb0b3a223cad586a6ec05784700cda758f5348444a9890907481ca3fededa686745cb6849f78505f14d084d86d5ecc09b8765b25e0d7d295bd92577a150ed858dd812b2047fb5542379097f7c337c7dd18321337f794d8b24d96772f1b84f112cdb78cbb1f985225e55041e7d428656ee791e406e70e447f98af2de26d468f8b8dd3343c56ad828d6060b98d57af0e42803ca402b4276ca7007cb7156214848b44823929acf96f98d040aa5c7ec641b726ebafaa18e8d743e03ed2665e612474dd4353a3a172434f2f40760cf001483f9f50012cc7bdd45122ae2bbac42658fb5781db14b9619cb479705c0edefc1d058a0c9492d82959845be80c5854cab8578a9b52001a4fd7eac4501c7ba3c5d1a2ae539a38fa967e717277d2f74623a158ebdd7d398d1e7950be50d4217592c4891a87a3a2b21ae32f594db64aa80b288816867b104ca0fb911100ac9be718ef09f3704ae9b2003ec38f85d9f0c1a4e490faef293017ad3fb05110effa1128730aca267d1176d79273a542111b9b8a47954e1188b05d9716422560c4a54cd4973f20d421624c08385e5759dabe6b24bf3aa558e5a4f572603d9a800664bf46799eed785f0c51d713f13b5d767cd091bfaec06cf67bae84fe966b40d4fd378253cc5e2916311e35e07133b22fa34c2a09902df1285fb36ee57aca203744c890acc145fb1ced79e6019f89bd026d0b3a175398d36f8498c7d4494300a843519d4daf2260d5225e7146d3e3c9c67bea2ce801013b537012c68e566f807f1cbaa65d11dc777d10ee09a99f7e9de0ca311354eedef6e1bebb531a3f5c6039d0240a28f717e9fa9452410a1965ffefa7195ca57e61a54fcea17b07c9805068cf9143a9fcf72b79e7521c538e9a0f46cc5f6c6ec9b5779196139c3cae29e0b43d42403305e6637056d41169f4b83e6840c5a3f1e17fa5267141ae4789dba0c40eb003207e352a27899c5bdcaeaee48132902afa5321c8ea49de3fbd2b13200a319325741bc391c7b9712cfe98d9448697f45605a52b1641b5dc352e9804c0573acfb531c7a465beacff0f122752ca465069b62407a6dbadf42f4ba1c7d900886cee849d4d600b5b09c64ff9cec774ab6283b4b0c9d28bf355cc2946d6dfe03bf6b0049ac3122897f45320b6d202a7ff9e7eca90b5b0f209b6bc473e3428c0b4a61b3246e4dcd9578c4a12279747d2c02711b38786da0d727e211c18540a3010767b5c2792f3d605443d38b4c8ebb2ab5136795434caa9e0eef1a4338ebb808bd9b846b67e358ec395f3e49cf998ac0d33b0c6dfc888a3996f47eac7cc075006dd58d6b889e8c7c9b4df6f6e2f482e7190a6ab1264980376c186adb1395230627df635ff829d1d9541f03751d82e7b76b62d41369c385627d8962ec79274608d3b71398067e0c9f4439121936457de4dd50e00da2e3e3aaecf2593399f4140c50aa04a1afbb705a3ac76e86cd3d9c3eb8fee4a2f1b051210182d08e7693320bdd02eb4e526f5ed9e85e7b20894b2cf4b8c38d589f0733c4201fdf2d728a9404a10cec36df6653eba68b318bf675a739b41a54773621d7c2f4a91e40f8880f0188fa4042d418a7d7ba589220da73f94efd5c8dedf9d7a626f36b8ef698709100a1d8140dec0c89ce65ebaefbbbd7af68dcf6d115adbd8edf95c014390a91dd04bef2f11daccfb4775873b0f49a5337e88fe33d186e4c399f4c72918cffd850066598914eb242aee7299ba7c7cec240afaf1145a06aee265442c7f5c37b9ed301686ebcedc4188ef6e7d43039ad753ddfe0fac760f3dce0c1a06c672b34ee6306252aba1f9546af9e3e1b92228a238ed990d31ff222bdd9a50278fed2e4b1d80ad919bf4fa40a8e77ac092ba485283a7c42965c176e97f2bb019aa664f643900142a4377c032998636ab7ca01441a1d588984c569ecb9ccd70138013fc09e2c02a1955df7c6b71b44fd8c6c9849d8c707f88c9f237919cf52bfa9d51496f5cc0467b8d3daa2f900fd60f0d342f89789031bc0d64a19d22fee93f08a7e3830a60dafa05eca1dd101bfac8950a7d2b2ae1ff004b70a105fb84ab1e1b5f7844b7a073ca890e2a583df5c7e8b2f4c00a5aadcb55c39a4afc23c1c29f69448653a30007889aee546e69984938859ed7c4af8d1cb02a3addfaeb416f18e5bf13d3af608ae5259904184a2c712dead25988df8edb72ba6d73563870374832c8942b84c0950d29f1b7b364f27fa8fce5ab499a4a9d5ae1829b3d12d7abff611146aa5d00ce803924449f0840c2c3482b4ce0399240c47060ec50a27bc942b6011a9b6cc0da6694455e3d64e6b057fdb04e5baf318e70aac6da13ebd970a4d1491ff3fec0d4002a549809faea9e2bf3b854df3a1c246547206854cb34c2c13bfb489ee37046f671df39f557f394e8deac5381bdd962f942390e490b516b270ff76fe5d4e01bdffda89e6a2524e00bceb022d2784feac495c5a6ed2a95feb81232f5bee0f03a1baf21830f3353ff30fb8bf9bbbd248e433933b17826cbd17c9f9687fd373084841c656bca49e807b1c8f65be0c1a39243f1398dfe25785a70f6e023f9afd069003fb0af1bd85dff665d6795aed39c77ce005af8c3df73d258ad0332c1433022f8ebfc9cef079dc34bbf3ea154aae47644183aa8e85c3c74cd3e04f8525130c51335da22a943946d8f04428b4b3f11ea2bc9b98abcc2bbd8c98f1f33f7f5306024f6eb4872206a24dd76820aa44b47adb175a26f55b0bb5e75f3054ee14950c922d43e92709fc0f97d978b16af636d79c6440a35e90f1e05c52b8736edd9f0b13b81df7e3652dd30d03d253d772184e7c548197073f912aa7e779d39c9ebc0af5d8fe50d164125b97697a4d78b254c43c019615eb24f88585e2ad8c7b3dd804 false -check_ring_signature 5acf67496ffd02cd896fe2923228ab886ef50218d51ba44c63b71fd05e39daa2 9e06a35b6201c699013e26a7b09e7c922417d42768c210f74ee3cede1d8517a7 3 aadbe526967a079d22c1dbf928967006053adf999bee202f5b5c3ef26cf0d1c6 ccf2964a1991f4df48000a9429145c5741a59c0af64419f91b05faaba3319178 9c4176a1c577aec6592c3e2d43841f31df71c688e91c7489dd42c7f4e796c31a 61f5b348a9e81f7d8a45c5791d68a83f83cf0564838fce47da2af604f6c66b03dbfaebcc125fda8f1d0514b40fc7aed920961d119f8c3f6115ddd98e04eb456c62c9721e6e6ed2c81f9fe9c3cff4dae6b4011f15d3255eaf1363fbebee157871174e118337939dcbea52d883db80d4903ee0eb0177cfe85dc1aafc078dfc20096d4ed9e1e08772095b918528ebe8d9b50406bf71cab867fd91fd1ab7bf5ffd0ca98175082f28d464f3f75c47e52e26e582b3406da8b263e672859029dff4540e false -check_ring_signature 8123fb648893e65161711535dd42eea6cf2445f41443cd5de2db689afddc9249 2ca5859ce24e4feca006d62c916a44bd6c1f8bf7e7360c7adcff4bbbd30a89d9 56 7d4d9d5dfb8233232680c5664874733bf028c9e6d190eb8d7b5322ba47bd411a edcfb83f5aaff60e9f8e87677c3ef2db57d987bc1664498747eaa273bc246efe 768b4e34727fea072f6273edab040929eef7311ee946ceb44813c70f6b21fe7c 6fcc495811e378cca2728b5bcd51facfd169cdcee026ec3e50bdec117a2587dc 60b6d1da61bf6ee5f242178aaeaa0f251b6da44e8cbfd8a98be671ad094561b6 64160ab4acec9d8918d27ade93020cd2225532a3220cc591c81be06fecb90774 e474f84b9ff15d8cc1e01653b2ceeb042c4548101b926151b37486eebfa8552a bf4e1ed65781ad39119bb3a83c0304772b05ab0e9c172a67438e8cdb00314dc6 922c51ba0f729ce9fbb8d455dfd05a0f8e119223d28b5abfc48a0b9b4e7b1af8 fc093d79701ff6fdf6754a978187d6dcad9c97bd6f415769bbc50b019a348341 17b880c214d0986552eb4344c70b1a339bca7ad281fc6813f7204b90856994c2 b99a8639c7c26733d2d7a76aee8c7fa0794810a60229718fefc8bca12034d129 5bff27424ca7efa62d87e7acdd1d5e2b4638bfabf4e9de1c34d4769d2e0926c0 fdd85a2801c4ac4e40fccbf2cb4c5a753ad96890bca13a57198b858d2348940c 28a5c014ae5ef40da3194866f4a04fc007ff1be4198bc40c42bcf7f7775eac9d 5eb20fa5293341e2dd28c0d1adf4223a9efe6a73d769f9c4039c06058f2edc5f bce5b1d89bd389a787f552d5363edf8c5e7462399ed2e122b44054b8a6faaf4f 03259d4999a8292f1ffc83a77d2bfa5dd8aad7542114f72857a3c026f152a3bd e6fe3c19518ff019b844f63b6b18fab7f7770c84f21dc8dd5f52d4bdba367352 08c4ac890ebfba25c3f429ef1e11194784c1d57e2aaf6be776c409605b26f9da e256691be2a759a1a97bc649c5e8d9304ced1feb056d985d337fb8c112e6b4d2 2a6228bcb2a09a0e9aac7202b5c3a991bc9dfbbfcd474cd5d3668983ffd854a4 a65fb6055580545e17ac16b157caf846235f8d1c77284627a2ec135d92edc06d f36efa508ffe19e9070c4e21145e755736cae2fd936222d2d74ca19887ddd163 1cbe87f377f7239a2614e46bba7a74c3461ccb33a0da15f5ac81978ccd1464c8 baa6c166d697edf09064da90ce28abe1340307532335819d42d7513e66939c0c 18287664dbfe536a25264d8a63c02a41a8c931d620bc9c4feba6b2cd35d44e4e 8ccd89d9959222546af434fe7ee706a8441ea9fd75006de5d40170b509d85d2e 8e2b1944d46d23759262b6508fb03df2edc126585ee09d818dd17fde7757e909 878d4a2286140072c1f195f05e6aaad343a4e143716551837d6382fd0cfd46ba f903f15beb1dd0be58f44ac67269ad8bf5e83543343ff5b077625569400c4681 e8d49308a88d588036dacad375651c6d886d692a233d42fb2bd9fe788e9984da c8b75879cc10017b7e30102fff9eb77667f0aadb85672c9dcdae005b9ae27d09 90188f52457454492c0ab065f142b9ad46ccecef819af8ad7739a2f7749e2930 5712675b69f8d17ea18b2f837b6731d54bb3a477cfbad92a43e80ecb50dcaef9 e757d1a64f90f80590b577124f2b2bdabc3fb63f8e41d0134d4d6fa2084b3cdb 467140bc2c9f251c6bf586a02e56b3c5cef36e3f33f881d39367040e0fc78ece 664a4aaeac789b93754c0345c51f6182a87330fd8dd6210746b651c665803d56 8d4361d25da61596c40e738b22aad545c8a696601bbc79895cafe30e3f1901e2 547347dfcab2d47bb531c590aafa9161c58389dbc29f0e7745600fe697108cf5 d2e27c122ee1214718a539eedb6d9bb3fed6005a428bdf7a0bee4bc7bf653bdd 683ab487b100c60797524a725e331aa75fdb65bcecd49eff121b21afe22cfe83 393cd7e7bf3a0e12e3ba0e77b8a84cb31226e59e3aeb9ddae033db4063308dc3 e6eecb965c40eeb576e1ac107c4f647e07e3ef6ce8bd46991fa76ff3dee6ac29 1a392e3a808508d9fcb4b370accb496368d72d32efbf43b1e26f65af28f3f73f 6a1a8dcd766d1afb420fb220ef4e1655b500c2347c22e0def70d7e1913c31faf 4d1ae0d1c656a6f9709d224e2356dfbebf779c64c698500487f2ed87a9401655 0f72e71cb72c469c8586bf3b0c7da94ee7547b4623a82925bbeddccdaaeaf096 15ebca6bfea37e4ca6a2bd524bc68e9c1ebd6070268ee6f6a61cb956dd3814c0 c79a1bb59c01a67d5dff256643c03d9296fa7ad381875ada96107340142f6a46 65c5021f03bfd90aa43088eb68d5afcd229038f886ce0e8d273785bf1628994b a1f09843c64b4d89e075acd874c4395badbcf86ce8cd0bae2a607a81c8fa50c1 40b1403b81ac026c6faa8b7e83ad302a03a0ac2dc40329b44b19fad21424eebd 077d340112c95583f703e0d827bd5a27fc73da4288e81c8761be0a5d469b3d19 b5e1788c6db2c75b560278d449452c817720c6c7cfdb56d8165f35713230cadf c11123577a9375922bb82beb625e8be9bbd12e0dd97d4c9d0e66cc612c1c6071 df9d5f5e0ccf5868369a765e84c98b32eb0c5ade9170a8ce90cea507dea9a90d147904f2febb764bf1c1c825bdff6bee392ee292dede52e1810e1bd443606e0a7977e386b79eece5f98fcf412d141afd836d60769665976c100ec4b52792540919766ac8514f865de3fd80972544fa3f467da8bbaf8ec92d23973c31b1824b081b9a1df181c936105f0b3580da4fd5e3f8a20c8bcac68687bc19efee9057d906efcd13bc4b2b0496ec37ae16e18f77909f6b2d46a07e49e62bf4e715fba4a80d98ae7078ae10173bc727a964c8173d89e9474e806017f1a16b89f0d1906fac00d26410ab52953bd301824f25fd0b4c23fe2df8e5396a9ff6ef73ba4b9bbb8003e222e691e871bcb54963f8e50ff97892452960f3e7cd29a0630cb214ab40750e494796b3a1a738cc321f72ae53d5a84788eef9047c1b67a48b21c5fbc0df600c72fc9ec7eea1be666d3ac5274f1ab51d22544ec09f9347df6335b60d186948070393d5d050bf14e2c4765788706fba23099ac16bb0d6804936a62911eb360a0f782e8f99ca6593035ff8b87b73352476e98921bfa15b3457be3b51eaeac5b50de0ff9aaced7076195d7e7e2ddfcb78a1ab0cefec3931a16fb2b3851ab208440c8b470c638eb97ec03b65f5e346c5aa6837c84761b596e6c42c612c2f3451320909edbf74a008a4b664fdd27ed07b10c49ca0d1234661948cc98ed715f071f60e3ffe078d9f949125ed93b476a8b35f25d6570626e4848ee52875f0e7d420d7077788b0d6124c54358c4a5359bc85248a41f3a607771fac51cfa343dcaaf0830777a8eb022363b12759a9f6094b7ba368f8069639ce3308849304c77f9136d705034f40bb5b435bff1059fb3ff6c6fd210f9237005d4e5f4d62b891c90af2ab02f3a9971b6a1052702371d3584ff4d57adb3b222a142ee288cdabce4ed8ef790b4b9d13f348f40b1b2ce9a058edf5b8ffe0714f1044e9834604a2d14a6202830321299de6d9d659f1832bb14cc94a9603725167864bee4c61e4481e3e4264bc04ebb41485f62ab453a8058084e8c92cfaf703a2347c74d90de64dbfcb7fb0d40b5c84f8eb61bcf829abf3d03e92b7e3a0887bd9e258d61309c6eca2af49b91e0237254acecc5a6a11a68fe08dded89f28be48db91a5ae11c440d4443030b7c10f1ed8a6c9fd0c43208fc3f1bd2e0ee2c1b760f3079fb7fdbe51791f2941f32004bc213bef2244676f847f66c047d59fbb32e620ed9fea22ce2ed4222787b200097fc9ef52c194056f8ddb0c917102f02b65be59c4fa3cf60140ebca582ab8240faf34604ff69d0e5a00965ffb72fb677ed04fba93c299b6da8b9eece8473fd200706103b7bdb5d468d1bf1acde8333d4e75e92ba3c0529f9a147143dc0319100c417bb65acbbb1d0d1f1dad58d49dd868052e67526b28a3806dc7695b4ea93006994f36fde3ecbd686d682be2d88aeb164db245d7eb5fb6393bc110c6b8b9c60c459365fd11deca5b92d7e64c419c57d822b6925b64230560460856b9a70ea50703cd3dac3d53b852f979c877edfcd95417166d4212b7e938d2c54d5365e4370053f18815b138080c24c15c7e79684b70750b78be58071df086d0afcf2706620151d221550d07ec3c6e455d48c39d19bf377b24131cc6707badd3deb1c1fb48028b82782ed2d1bb719ec063744703d48c60a60a0f97e06ea806a477d93328e701f984d70c81e2365172176e3148b3a66fb030df625f3ac49ac6baee9d40d81d026906908113efe183f4d56c9838b7a093c37072224bbbbf6c7ba8b97db2d5aa0b830a18ab518e0c165c1d8fe73cc8a7e93a4f87b983b337c3c66c06b35bc672053ba3484a32ba7f8f6c9418b169b04d897825287712e253b9dc260f36fdd42800a19e68d1e0d191660ecf2249306de4938376cf6afa2d9e80bff6011d3039160e0e49962c53a2f44faaf9ae433a358797a3ec0eb7ef8f70cf6acb5fe511a06a0f2d06b5d2b708c71b613e02db478eeb1d0983031ed3c23f894da5826289458d037fde5f6299a289c7b13279cf5efd4a7cfe1fdeb6b9be7c23d6d4f60b8dc81e04d099544e00c884fc58eedaaeebab4bf88fd5ff982f01fa88d9e1ce7afd1cad0f7de52ce7c7d422503659cedbe7287c918b064bdc0df752a3854ef74e52b47409d2fb1e8b2a9e1947e3d15fead444489db5c7db71d39065e141973228439e9d057d6f33fdd5326ee2b1f83764f01a5cae41b6b070dc74626aef8537692a351405651ceedcad642b07c2faf006fc92057a31e3dad5cd9886c8eae1837fdc4f3f0d294fa5d17b786a2c627ad9294c0550909e035bb4e6dd418cb2500f97f5a2fb09249a4142cdfc26190abb7386c5f63d34f0d24dfc42c457c9483eb746e8cd1805bd3021f93553eed1f44263f21c60cbb7fd1ce9efa7debeb3729e0bac38660809b79a3c0edfa979cd629ffb7be577cdd7b76b5fc01b56e189fc4bb29c7e7d8d0a9eed68d2f6eedd8072805844c0e110a80da45933a73275074de19cc8eef14b0ca23e1b5c5a3071a02746a5c44f11cbd7a08b7c3c5be6b803f94c48a88ac7cd09f8f8ac4ae276fd6df5b84750434f04b5dd18617658c3970baaa5d308cbf5ac0f6a2fbbabffa9bb7c48f6a52539c50ae926c4211246a4a494f6e9148e20c20300660636a2dc474c4badb9d295c219eae9fd6c5c05fe06b07891fd518840ca7f0349f8d1ac253ea82be3f9ac3e368be0da1d30e1bfa5c6e0b0a5e885cc55ba780ad3bfc57191202c8500c573345fd50b7d2d6df58b8c82484eb4cd20d7959e740ff4d69923748be151c29a139c95f217d6dc9477404a58a3376dc7246c556844083dc94d4248fa1b7e75a642b44c132b445d6b89288d500ce1dd6a3820f74c3e0a2ab4b9e2e5b9b2aeef171787b8a161c46d1012657ea9ba16497efe1f82f6e406d22585adc8138964e32b44b26a60ddacd3ed95f0bc788c37315168f3fc76ff0250e4d4438b6cb104f23f94b13439f1ec8821d8b32478d258c069e70c0f25ba0da491a599f97f6b95f8e42f357944d319be74d5bdb6eb3029434f1861e67ec40bbc3daff4f95d6172405f31ead3519e0e4e39594cb227c0a75850bbb3918e7e01323481003086afd6e772ecd989da843559b4c8c8a4fc2b701bf33c41da174702de9ebd762f4c5123680280402f4c0b7d10980e64326631b00b65974f1e6bf40f919552985e1363ea282d45935441731cfc016ed907be9b9f01128e22566602089354175fed10c5fc4f68d77c3276eb67680ed8fcf3c5574ccd201b8b41cdc2052e58d9cd5477f72b6a790ad2f1a4d83191cdb4a320dd642edcce947ff420040697b7f083971875a1aab35bc090024f01eed37b329d456731af744c35d4ad5f00820105c8237d2ab5310b025a54c71464c7ad0594f26d8401a0aec2284c1d5106b5cd9064829157e42e235dbc5c886c6a008997b546861afd24c9b4aca7f3db068a234339105cf5d52060c879b47c3092d8a0e53ee8d13d2965282547f68f7a0ba2447762d513fda81c1a2d4ee6ae71c19f839dadca5d3c0de5166bd6aa693903439e208edd24b060c34e84fb707afc567e56c2809c5461575652cffe1b40460db09f745c6728f08985b0950fa43fe9160f70b25253200c0742a00301eb5e330a8b367bfd51221a784b53e87be0220d4405e689dbc74f0704909ad0c31b2ac8088f6de3d51eed6806c086cb6f3178b9953d2c013ffc853a8f6f8064c1f1706801817e0c537f45ce229c139a86fc79b446331bc06c690e16e657b50394179c0b002ef8c888e42ee1a4897927dd18db1c4ab647c00ba8e17d89b6eb46387e36e3012f0bc9aa16c94c6a922b846113ae210f69488ac2e1e31e36fd7114bc14c4450c83363a9d43edc77bceffd61e6f607f8d0b028806b511a3354b78e8a54686950a04cb795e5146f7f3f43265aec89b28e0daa446546a79c4fc9f14975abd1ce40a6a0952543231553b8fe349e56596c733f27c25e9b29f0051828677a953f7e20fcdae8deb6bed3a4b83066168e7fee29e2480081418aab26cd55f554c5392f8014ea4c101f2827918dce71df9fe189c82c79739d591661c44fe2dd353ed8e0c0890348d87799f68482e34b7b0cbf3a8e41a774ad48a9bc03092052ad0b1586b0af8a5c9cfa40d65ecc716dcc9556eb3a4ed737a2ba0eda07956d48d8081a99901c47eac3d771e1eba0b65c2135cde7a1bd4f8d9074a7fa6b9ceee11ebe331ffe2e62c93e38c4b0bf234b2fb80ec3adbe6dbfa6e6fea5d928f253e18e788432e0b6d981f5ec31372c5566ba087c77bab1bb05750da24fdac354f51936506350702f77dd295547bd81d2f3157dde5587ad28f1cdf39d501a330dce8a04021be1e03afb2b3f957056d2ffa612d80a40fe5bf50a5d1bfbce49aa088e00cba527a62095f5b8ad433db5be21c59d154969628061ee3c643a88cd05072096a70ceeee204604297f7f4a6e42ba2920abd6a588e311e6fdf793ae947325d82f4d934c92606d9a23d1c9465e6c084125b3f51086e85a16e9f0030189c26756b6e7200c25f04e392548b319d07329bc10ecd841dee9faa89897756a31c68ed9547917edc940d20d3d5d7682e6d05ab288adcf08388519457623dba98a166837df207fafcc60c4b2037915e29945f01cdb787d0ce4b83718ceb6fe70a29eecf2730f994ffdb02abf3cca4eb05f10f00c3bb2c9eee8d2c6f294708e3051f0334e89ee7954b82095f61ce6f7baddff713565b57e2adeaf35bdc97868da8a8d78293336b1e63fb0d9289f6663207407595986a0c8608f116ee69956c5335a2e4115739809dc41e05cc05c5989b8f6ceb239781505d3631812421693f7ce5c006798472b37eaeec0b4086fa3d31780308c75f02a35c5106f94ca4311fa110e6f939dc09fe4e667f091a09717b348c5cdf32a9f129dddbb31bf24ace4aa39c2433c52a1c2862386d0c11c52daa11237a6c5c91b5cfa9d024497dc0eb731a2350579cc98b000770db0bf77350a5b27a859a06c432645225b1bf5d7faf00e7a5abf0ca8a3a165e9df972 false -check_ring_signature aebbee7ac9ca6710b4b27c08457dfb2d2744f1da5617f7d6205ae9ea6369e6fd 7d68c929b02984ec467132b6275cca745b237d25415a41b744781753db91fbbc 9 95d9cc15407239099e078bdec305573a3fb97dab9d53e36a09fcef68585eebc8 5aa53ef7b4bfa3ac050bc7c31befd286cbfb7cd71f40e6943cafd655c1cd6253 edea39b9d67a557a225d5de0a221c30d35b58c586a58c82445722ab608236161 63f3a053c0c83b4063b915b2db3d962bd9f47ac9b91d1a71b73848f53b0403d4 5f33c75e9e4e2dc65ef0ab756cd45f4b48f2e869c530cc7345ed1f3b58efbd0b e01663c26938c75f5382b16ccab53e64b24d4d038c3494838cc6367b84446db6 644ecb5832ad9dd39767f28c97102498bbc5724d3ff3ba7f2be6a5101ecd7edf 38747f6edb65c6a5c57a4e0cb686a4a93809cee6edfaa55d3565f9de46d91993 3fe31533024ef63ca29cfa7b12db51766983ae9d28d9e00ebb28815c05a7abee 319d51335c0cbd57ef3fdc57cba2768c5ed70dd015bdcb94e5e624b25b32110bf479ff78f9c289282b5560cdec83639c21053f289e018e4ddef1424d50ad4107d90adbe2d832217f92ca2434faff167caaac5b76c5ff455389d65985cfdcbc00cce9cc00b0dfb7d0a0dc219f9233cfdde2439670538b0bb6de33d1c043d58b0fadde0c7b61372f40eae65b5378766049c22fbf0203622ba2550a86e02051150b8cb1f95ed0269d36897236ddfc45394e70e0036a8354af40493e16c6eaeb3e02201cd2fed9c00c4fc7ed17c659068fb414af63c3faa6aaf4b1464f579f818908af429a5dc21b81d7dbcd35e8a3e25595e2ad4107a267aa8d0e88503aecec98053b985a4b72ee89445b992f8af375f0948759c4d733266318b36f07f95f2c830cecac88eef6adf472f3588049dd3cab32f62098535a930fea994d8d9e9a22d10c0b36b7d96047814dec91277e43d83505cb975ed8c6b26ed2cf4839b6d85a050eb2c36ff41e02e3bda82b18578bac0db7e1596044689504b96457fe13564048020d61ae6c9b084309573fb1c76841d7ff56e94c79b0267082ca9ca02350eff507fc0a7c3c8f2386fdeefdcca70a6a9b5eb9ba555bf613f23223c5d564f5b05e0bc57bcddfa3082b65df765563c28aab58bd35b300592227f6aa2d3dc1cb7b0502d351f262b7d5f3851c0a1954107ce2f8cdc98620c7abcb7f058e69ea5385f30a5ccaf9c2a28b9f83c9f57beccdf77062a4c544a0d40ce6ff6d38adfa9305ea03424def07fd0909b4612f5b4f768b51a65336176e5fd4c86c30dc9c336ab85e89 false -check_ring_signature 82ba90dd1122252414485fb3b0072716faae463ca005537ce21e4c701c8a2e27 ce48d2239d85da8b96f2f509345d0b251fc060a93d03d9c042aa44ec3f2dd708 20 21e8c3e3120f2ae88941544cd85970ab16aa9834bc36a0638fcac4703b130162 e1d58926b1c1f614667b5fdb722731f36769a3efaa38b39a18e5c49c70d95d42 596d411d3b45f98f3f35537caee4514623dbc0557eb05e7b5a1ef45377abb99a 0920072cd7779f45ff16917a3e9287cf3f354badfc721e04b239cbb629ec422a df05524b232ab9be9c0ee0992ca288e6f95f00efa670cb1e8d7ec281afae3a36 3c246be46118b3daf99d0956055f0bd6dc6116a4f82c46324b248f1ca519c739 a93d5745acf5147ce63d79b4371bfa629b898fcc7a2ffc7cfc4afaa015e1f156 65e3e9f5ca2ad75b22a797d1a78fa5fc09f6b2617b017795c78b79fabb9229b8 4b852c33080315c933c49be3f4c7f67e3199ecee643adf759ca69e218a154b8f c0b17d627576f67759c523e6b8d6d9b5a24e8a5bbcadf81778642909269d4f0f b5ed5cf79db388b365674eb8f828148b70174f964a9035d248493a48fc8da499 2cfca5274aa5bd9fd6b2424b213abd5835213ed0a6821b66ab70056ba0f52e24 38ceff5973d23b4485b1f2c9f754c677b161d153c54560999b30120dfd865d2c 0e3f77962843f9adaa7b6219f21ffdf9f429fcfc0a699a116228c055a0440e1e 4561da3e06ccffc452eead6fade4dd594734451f0808b1379c614449c7b9d34c e0ac869760dce959613124cf21c542141520bcdc9549da716164c3c4fa1f9758 6e6b5c0b8de9153acc4058f92a33424d0439a0291485242d1dfee760387dc700 7d5a79b116b48bdb626c46663408d7353235789d7e436c5576fabfbbf4d72aa7 9214268496e70d4ecb4f94f29c3ee8810b7e5b32222b96b1892a2bcc1e49189b 651ee98d7af5363c951c54fe034a9345d34dcac4479c9c06e8e66be4bf745bf7 dd741cf901413ff4572d3b0c3e4d608f1bfcd113e5c9985dd2f7358a67c35d0ad35088269871ff5db7716f5b57a48dea32c500bf699fc5571820cdfa8a2a5a0ba9b2c096f65f8f9909c76518142f3440174e36ae051e0f43946d26624b7b28080059d7a6e9fd61707d502ed91ddbcff05763a821b8a64201bd5312fa131883009255451b977b391cd5da7edc480d0c3fe2a4d3c186c6ac88ee0b926309d3830b50f05f539365987b62a352f02e1d07225130409aab13a9e0cb495eb4962ad90343d383a5ea7fe0cb14f6bd99c127cdb57bec3c9289319c18c6adbbc92af6700f35fb45cca40e19e2128bd5ab39a158624cc51db6969c2153bd438194506b910949d6993c0cfe7e292f3fa0c7671dc5e5c89e2233e0b0983de0a3a301c075770a2d28f7aeaeb568a5b29d9358fdd7aa43864d3f6aca81a8c14525bdde98614f0e77559984dd34d166a5a55aefe83df55863a63e65417a0c52378acd35867a1a064098a8ecd7093b590e00faa603b8d59860fed2595600a6dcbe0bc7400cffef08131ff5483838ab3a056e184d59ce378b694ad0ed03b1d2251dda29d6f81f1d02640622ea51f76945f71f04d7e726a6f5171302b22b226ba9ecfe1790a1ea0b05a96d657e11be28db306979192196b3fe398a8ed5be1af03d87ead5a91bb9a606a21cadbc348a2a025cd356c2c3e3a4cea75f3f91977ad5771d57dd855af80d0b858e48089fc251dfdfb20f5ea7a6f0c068e12fdeda4885dd96874f4580ca62025f90964b5decf1af3aaca71423ca9b698e5f14dd61a23b1b2742014fe676520a24e734da171e541766bcdffb963b5a2a955b3e862ba8f3116d41e482874b8d0cd0da4af505f84a07ee3c416d2a8acfa8d29e990c00d5ab3ac1bf114389ec0a0d1e592898fd0afa223ab0800e4a4f22be42a31a1079f0eb830577614c03948d0b01509e1ef7916b2f65ea413673d1fdcc93e72835a2fbc6b6a3b95deef3b040e33a438c5d71058f15c72c34e6bbffc7608a30b14b2136978700c7c28063eba603625cc687a76604d4920005fff5b8058e513e687d2d131193b018134e1aaf6e0d319bd85ab3a6159783f489cf5e5d32c35f5547e2d3c63baa46c331f365a6b20fff9adefc5cd4a171766f31399b6329d525f37889a674658644132fd56329f906877718c11d29b01dfc26b1086dd34b002e3ebacc0983a7439d8d5a8321898b0eb76bef4466c82991699974b12bbffe65666ed93ea63fad07f1f64063812c770e4c743cd3ef622da16914197ab382a420a8a244f626dc1610a09c4e3330f63508dcb323da39fc864d919e34f790f56d00152dfc0a82a41ece2bffdafba9813808c08868caafc1157b5f0a121ebbf299823dd95562cfc8a1468ade8b971c4d1304eff9da2846eb267b7b1240963c0624c45212459e0c9acc1a7a3821f51d39020f9ede81c5aebc8d068519145ebbc7ae0b509f82ce4920ccfbc8ae0ba19b568206b504b85c08f6ad82a614d0acdb520752ab77babf08071fbe7d394c8a0f81ef0f77a6353391bf85c23bd7317470192a97ae9af718eea148e597d89d94ea0637067c67dfea27438fc0c67fd3731265e830cd117797540e4ebab13952c593ee310b5f81357677d81dd1f35473ca115a15f2a6568676a0d3d36c8467067ad8b69204dabe5f45b82789c3023c0f4e38da8afb4a25b76f158edce129753729c8db1701cba5d2dd82b1e9d0c0aa407d1035e2a84ee52ee16c67a38cf013a6ba1b68da01cdd052629feaadde49448ce5b6e13cd0ae83c6d261f90281d833dea68ac30b0e false -check_ring_signature 60c423f1480b8ef36712a612168b9f8c5fc2e76107b87a8372e355e2d3932e86 ec679f9891867d9e58e3954cfbda649cc64412b7ec45c6ce2b0708c9d54d85b4 2 cea5a1103a6aa2f7cfb29ae46178b85c2546182027290080f3892dbd3dfbb456 c317a82dff22bfd5efb1e00fb723e4010d3749bbf2cfe75e05db37dec9e075b0 202a76dfb03f26d01fb8c3cb089b509ca6c826fa00c98f674a67a9de1775b6049724ac2c222496013e0713f91c38967223961b428c7077abc34504611dda540206a75ec93a747c9d612492877be8b0384f6e134ce70bb69d9c68c672b2ced8009c68c7efaab7a5e440416b02353f310f1f1174d5852055eebaf527daf75e820a false -check_ring_signature 03dd1e84f4b1bdda480e09bb7ced4a201b7c5ca77c4622805bb044226d8a13e5 3eb665360679822a4c93a7191202e25dc0b7c32dbb457fb64b2faab5ab36591c 2 fad528e97e5867b82dcb68df80fc0cfbf6e60122de6ded0ee7ae762a2188a270 a5ccf747bb18b7055747c72bb6d706eff7868d9758bfc7f4f5632587a4de9710 4b06f6bf2390b89f385916123f63e0efa4f44a6999adcff01ae634db1ceb410a6dc076cabf554cf5954cdd1c1c5c63e65d0f9ebdf607432558b254911a7f990283946c31d7fa7f60d755c8f8beea3bbaf8ee27e19dad82a552bfebf2abc9280b72080ebb5cb66a071b0b7f66064f2c0e5816bcfd50e7f1cb67f8fa6a0ef090ac false -check_ring_signature 537940896fde7c021d1d0648d2042c8c4a16acd349d5f8ff218e0bcffc0ca102 32ed375ec47967e00ea0001f82e33d968180a8729e2e91c635e2569c0d9e05e0 139 0701a905634900b4975b5780cf4a06db948b6d4e5eb0ae947283bc5117470c17 45d1f4a0c4f5df845e5332520d0f6786298a545c7a6dd737dc85373e15050e3e c95ad190d6e1654631209b7a5c3515869f3a9dca90a26bf0deb6cef78e074cb6 2c9cccdbad6457c127489946b6e4ed376b9e51c4a5157fe1a8e4f54a39629861 e6c03101a69f901cdff39dda8b04ec4fc237b6de1cb4aaabcbf67bfb6344431a e41593de1b26448a3ad0cd1cb8560a9356e51fa0dfeacfdd4f7c1c91196c3897 850ba08132cfa7ce3ef2e5587d80cb5ce3566ece4b582ac5a33a37e2ddd4cb30 18dbd470e870425d0311c3e8ececd2547bec076e50ea0fb4338cfb5fb71c147d ca7caf54a54120ea49506a2423cbd5fbc20d048e05c36de13b09436b0cd47c38 a2593e0030114f074fe2e9e87eeece6eaf63bb09fe7e9459968e8b6a829ac132 5578256ccec5d6d3a3045bcd8be2f7a714312795e6e1bb9a90d4b99f29db5d3c 55261aa122220713bff4694375f904d0ab52257cfb337eaf2eba9a07268a1397 7c8060108ccc25be55bce77082e692280719ba04c23f9262520ed9e5d4f917fe 0b2cb7dde703b4f08b30676b91d8bada9477a6503b64fa34288375237ab833b1 714357877203e875d1e6b1496bc56343b61bd3e790203df0b71a25d3f19f82ba d607862354f5a8be3bab4bffeb998ba845c6306b52224abe396d86ac06a047a6 c6d42fd3eb1091b35a62dbf7ef01c408b0c5aaa28f70032a8ea42b6ff735b8eb 5ac573e51236b54b7a8727bcac9b79837a2c468bc983d462131db24805fed2bb 85de8ccd9d88cb9b1b34a5d4ec43cd0b843a79bca8ccd325a9c670e57619b571 8b48ec17615dc2ebac1d8455bff9cc9b5c1f600704a816b164f881ed4ecbc17b f044057e47b745550f640cb386ea201d2358ba97a726cd1aced99ea33bfd6650 17e12912a2c212ec2455b6ef0a6eec2f1993cea32c146bb5eeb0e3d9be6cb3ed 0dddcb172a0cebc7b4dce54ac1a1c1c6017c8002fc4ef6d15f8aa8fa72fe9dbb 6d46ba69055627a15ed32f4a8dfee44d72655635926497110b470443c4ff234a a5b91fb96700891817db85a876c6e58a06393ff2c6c7914ba058a313ac7a38f1 49e2dbfa47d0918e17cd9c4ad178d7d7e6a00b856b260584346f273e53e73224 5f9950b82d9df618b01da8aa51815948ae8c7e82224712a825f7bd21ce55a375 b2ded20c9d57e41b7c304c5dee80fa8a6251543c5e63888a766c16a933f0d309 eac5cd4814bc16c37d59b04a507626e0f4754623dd0473593126bc355eaf4fd1 80b910e96e37912be4a2aa0eb283a63f5a978c66d7aa69986bcc92c456b0c98e 180dc282cc7ca3bf4095c7e57d1fe1a4273282e52d08155a52d2d56512cf75b8 746439e3d956c1b9effd5807413d3b8ee08d8d53fd8d54139780941c9a48ecaf 23204a52abd1092b3eaa9aef35f151be4dc41b77f5d146a16e7a57bed66fb9db d7f7d27f1c8f25c4622d6f9208467eb77a405bb21f29b0569329578c2eaddec2 75426c9688b44f9db0451f70ca23652376c8a3a24d5e4f473c22316c309d1c7b f4d839ecddac4c98c97fed63d75668d0900856e929b27613ff04aaf167899e90 80528b415aacf13406aea64c77f899ba69a5dd6051a0fd6fb2b8b60e72d3c7b4 44ef73f0acf7005f36b52fd7353cda7c7e64735b6331ea8067aa1504bb205ae3 599b47a47767a5d03de9927a8306f65db24bafa7ce59ddacce8ff4c751ff7e8b 33d8ac2f71d39c6947f963d0d3ac353e27f0f3492cdee23f2cf7c860738361ec b416b904953d368f45a90901efe554aa7a31b94af54c9e6bcc73c2550bb68a64 c594356be646cb36d4cb022b89ba370f44d57fed67d8ad75fac364cc42f3d72f 6872cc51f61c55c2157297254a62f2529cde10039b5f4ea26dd0e5cd52a70fcc 8f12ef49fda591fcbe112d6fc8e3298107b155c56593fddd9bac584ff49ff659 351cd596b016a03b4fb3fedabdb640d1960afb8b172fe391d6444af7b8951d7a 3528ecdeeb083eb000a96f645bb8302c5e21e345938ccc39481b2431ed9da4ef b6fbffba64395c64437c907b955b14c665f106b76d90bcb58efde64f19d84103 4485cf4239142956e23570b6c118b59bbb30ef5931a4f6e04b1e7282439c6acc 2aea7d47e5c9b2aa5203a1604457c99412a6da2c3acf1491c5c139f2651fa924 69d915440aa9ae4e192094ab548b67e656d9247268a2857f6a7bd44226f33675 4f443142f659bddc0c66d4b583b912960b5182e03a702c09b62d14a48d6b2dab 1dc258e38e076b68e7438cf2d3d3cae8059d343e2e715b889df46ec49e79aa08 8f3421ab1ddcff7a984955aa52e0b3ef48ff85abe467b44a49a0b107440f9ff9 8388df6cb2d062f583fe71bf1c69796d30e1f8c5e1744009fd88f6157287c331 0914dabbbc006c367b2a4e322a8ed557f27002aff04107397bde3304abf68d54 b54147f5f4a13ce5195ef02ece8230758072b23f87f846825b32188b74cfa9a9 65b5d96cf53663980045298cd7c92b676091660e943b91461ebae39542167c5a 91faf326162df06d57e944acfa52bce64ed115c2f056714ec1ac3f8570d5b879 8ed8e747034b5456ed8b272a1b473015e2ce9951c6a018fba2d95c533f0c5669 97594a51ddcdba0a094caf30a8f77bd7449ae3292e9b233c54b1d756bedc0d77 ff3504220d7489eb9794cb72f6949e18ab105b644d320427719c82190e412f9d 0faaff6fb323f109ba7e6c7ab8cb79192854b5ccdd470ac18da488926c07e88c 669f5e66ee2b461c0d4650eaaad319cde9dfe4c03f810e22965ba11b177303b8 29805fdbec9d2751bf780e23001b1d4b085091df5169240b24c3d3b920c518ec 7b1a0c30f4e9ad9850c38c0e7841306b64dabccd75b8bc4a8d03cedcff8635db 865d48fb340bb766c79c27a6a24a288605f3f0e53dd569dcc1671ed0cc844ff7 9107779494db173f8bbcaf5f4417b13f3d25c3a9c67dfd5d6e554e212e283222 da7d119fbb805b4c4418335af420a6f18863f9071040cfcba4190e9152b4799c facf3a367f48fb8b451a7fb261e9799b432fbab4277533cd4bee9a99f849a274 80698e177371998af05bad4cf4789823b5055a09fd27c25c4bf0c35812cf8aa5 d8a6bf78e82e57c60549e2053c3fa8fc0d87d72cdfab9bb3ca73bb2ed624fc5e 6f217e4ea2e7a81aed33ffd58ae82e9dafb61cd7377c8b8d856e05cba8b1e4e9 0671a0dcd85ffabdc47663e76c4993b917b24a3c479bddce6c60e7f772e07f30 0d007441d4add1b2b1b60312d891556e4cba2374700cb8941831dbdb884980d6 5d60a06559766adcefd77907c6630407b88b22d4d1ed93b8c46c24df29b8280d 8bb0a30000649f6d64f25cf097e7e1be6e0704b4d5b9bbfa3fa2936c2ff6b109 b5c29e8ab1db44b17ce8b55272a528611a8aa7a4114578b83cecb132730242e9 4118461cf6c29250a89ecb561037de9f4d7bde9cb5e895d77499be8507117987 d9ca946b4863a0e37217dce588aa6599a992917a1ee22ad2726274127870ff57 70491abb66432074a33b026072086d51f4e5c50f1dbef26253a95187ede9e7f8 43ea09e1311312824fef31d393add552e820696ba917d4709b5e9b862c0d2d81 84703da65ef5fcd44810d0833976f9f49e15435ba3c39682d5b193fcf6cdb497 f2f35249762a3aab55ca0361195975515797f32567ffedb35a3bfa10a88c647b 679722be2a40776bdf02a784cfa506974b5e992990a6fc8f2c285205ae4b15be 06ec42f6d53cc3071700589a3c6f98d8373bbe4f202e0c8678e0f5a7c32b133f 41738152de5e5fbf3f044e1ca2c68156238f0cfea544b2146d60e7a466840f57 8520c293870afdbf7eee54222e7cfa5bbc05c2c8a13b737e11d872b418321599 cb7998506df52f0513b3880d37ec6449554297d7f56efe9420e31e3083c79cd8 9999c75e637acdf7cf5eabc7c0b11226a91e23f31c856668508dbdbfa325c6cb 183656f3fd1a4a0a5b6624a73f970bb855328ba85860be464bdc069cbd2fdc9e b15fa87ae89655fc0067e04d7c2035e86c96c8ffe97b8f109567de1952fdc89d d06b8da25f6535edcf4fbcdf6409f5a11370754ec33da51d773c7d813503159d 3a8013d8ba5663dcf7a06aac155e8706bd6b06a429f545db77908c43692759be 4c0accf5062c875143a342a53e72d18f32e9df87becd4c71d8d613d1a1d2c56a 8d5f26151a10d9bb0c1a4e3e5a9c4f2a732c16da2ca5e27d9f171baf22033912 54c76c657e3279f127cba82542c1ae5f5ad6dace76fc3c9b0281d009b09c5cb0 307e35074594dbba077f5325bdebd02bc490b4afdd83764a94f00b229e0474c8 5fa6171f68ab606315e6b98e7d2c2b409fbd00901d9794aedb596b2e49d2ec9e e251bd8dbb30d0081e7afb67f3f3e328ee8cb649e3af0b5367f74146c28cd921 e85eff0672d80f37232c91f328efdaa8c9e519c3fe227838529e99384f00f017 c339fd5396bbdd3a90b919015e74152715a3bd2139b9780aa5e650debf584192 6cb140f1b15be93322e38f94ff66dfe0664a2d9943be771ed4616d82ea99d5e3 aa84356bfc4ebc326563234a09aa1d5fec7c5c5e86db8e8b3569b19ff5a38c9c a3fd52cd67e039a6ccce67681a51f1fe95dcb1bb5cb481d5fe2471a875d75d1a 39fb0f18aaa715f4d9a242f97179a271488c54fe9839aaf766362672fc7cc46c da2bb544c444bef3475b05cbf5467ebd43d49f3502ec83a6c38ae8ec903b69ca d9a6eddda5928b87de1f0c85e744f8bd9717d3eede999078fd5a453688098152 5b18e514ead1fad51590945b23f01f8516471397b3675b99e43754a764cbc82d 5de73df961cfa3da339bb63c59da4a42dd0acae78ef9ab7085e958b7c32b4415 5917db87d039b6d33dea98f0269e0bc51e0d24a23edb46e8fec631e6232d0f56 424780b4cb92109a1778b72462b34017a90249d366988891d14bc2e779dfa054 97950c73c2303eec0343284a1a7e1922281016c5a7c13eba820e76648c543b0c 328a475db0ff75cc376de70a80089a071dd449ce72142c7ea2b742e9707915a9 eb46ad04dd794c6482ba03ebfb1de54693f15191c87897195e96f0f56e72c7c4 c7ba20fce7080033a586070f0ac64895338273fb81719cfbc8a7e43c562eb2d7 c906ad954120727c7885c0c594cd4ef28d297b7ed7417f266853fb33baedb03d 0366dbd841701dd1e472c5c66278ba758ff25754971e57f40cd46baface01fbc da9401e213b1050ce1619864e372ac7a1a42f54ebbaceba27a44a4ce11f8fd7d 95da639ac3f27ae0ae2bddf4043962bda8e3ac4e6cd422e89e10a1deeab222b4 91be488977d35e93003827b9a4a059e88448c1b56c97afdbde7592c18d2d344d 0daaa529479417611a5f694c27cc2a49fc578919b1319a191bb5b6017978fbc7 b94da1808b1688fd14cd1b3f5f4725e4943b5b6876aa9223850a4069ed6ed192 7cb0af41a9c57c93d77aedd91502587d996f798b11b3f4d0c8ea221713185569 fda637cc2b7103bb3e572239ce3ed857f83eeb83b889c204b727c4aa7a4e5983 e056da216f649369d1370767437c932e7002fa03b231e459d8af16377e69ae63 4cc0627da5fbfe0f52084ecf6cdec07f77a549ec2f36fa5705223b9ba42fa601 2a64122111a2aaf0d57103331670faa3caa4d46d2000e56e4660d20d47876aa0 80057de649edd14c97a98340383e14a83a62f9bffbaf92c6bf56ce9a5b983299 67c16884c354822bdf05999f3513f0bba9e446b3d324d060c1e2416539f02227 a948cc666746530be00e74f18a9a86f70dfe68da08bd861042ee3b6c43d58a91 548330d9ebd6da1eeb75c011f1708426ec7c070edc4ee383e7289d98f13acff1 0b18747f46cc8c95cefe26c1c60344a18dac0d443efdc95a30e83815985a1005 1e234dc9183cd785e5efb06c92f02078b7dc8a1ad8fe2aaabde5ced9287edba2 3e23ef3051a2513afadeb62e98fdb895ce819d09b1f38cbfa67f331618754070 6262eef572f317b0fd103d2704f59a37dc733b223d78bba72f2050a236db4cf0 06994040e9389358ad36eedaa9faf080d9840a7d0577a5ef801482130233c066 8d0667e17014cad227c3c662e61c7a604152663727d813796ca034e4d84e6a96 9d36d3e4291ae8d73ba85ea9eb2e1b9d0d2d0325791932473de1dc6730609cd6 45a230c3cbc63d3ae5835f25cbfdb56f1ab177ed80b8119632137c711b98fa2c cd521c2363cb58f3db3c3a0f9262171caf0f544a21243aeeb58446d1b1dc03076ec1c63d234bd446f3f8ba1c58cfb3d7c09b2901f30b384939cf509d45511c010c84561ac816a59505f2e3b28adc14ce36ffce908088d90bae01d942ae64610b77af84f9c366eb420d78927bd2f9221e1448ca48897da7c4a61f332f79a99805ab63dd9784249292a3e490348ebcfe78fd02f00825ddd4aeb5b51b6ebb5f5e0a1c9ee04ec0c20ef31c1eed492a4fbc40a1f8f451b98931a04ff8d6ecf1dc0008bb801ba7d0a3fe16600bff8e132a53168839a46771b0e2f083d44410db56f80b0314ec3c1329b2d647d7f6491338767b03f1cf701e6ddd63bb14043da7297900d4bdb5ae148b5d8771d2fa076b1db0a04927843bdc55478f0defb938e169080d92d34e2104a4ecdf999191e402a4d63b244cda2633259e715cd8e1a56e8f9002eab316e1d5f3499ac492a71d52fb446ee8175a803dcbdc68afc6eb0b011bcc0f47660e8b2a6f0595a14126c54928c1acd33392a4f18f0afd2a68eb3ebe08c40475da22ef0c5cea07bd8c4a16c105f2d3dc43976a1e7302bda3dcf1cb4d20f109a0f1fcafbdc7c7fc6158c2eae6a619d40dc73dfa5e5b0ca6fc339a18fedf250bcd079d51bfe3287e5c04b986f5959888ef06c74d7046d43e8d0df4faaa3a8d06a2ec9b2a6fd0fe2a6ec5e257b544d7f40f174e1a1272a8e1281f82b34dbfbe0d2cfef38dc103a031bd8ef952086048744ebf0e20ee1aafba69f0c1a31f932603f35d4af7c281ab719f752eb85052f7ff2f78b89e58fcaf1295deb3a65339190d9e96174f4278f94daa46378ac3af5fdf337b5f7cff8905df2b45b4df2e73cb0cea0d5f0a55b4a9b4506dec3650a52347f15abbc6f712f154870482cdf0e4cb0ee33693a4c6f1bd7725783d3ea47c3f23f939998eb736218e69d53cd9b62bd3078d2c73749bebe9974a0d483cd081344b1243d41f7c5fa8e72c0042ff5a251703ef97b0b5a74db0457828339dfba1703b087579576b3564bf13b2bead597e060cf83624c792ad7e9fd871b113100e1c9d0e71f61317de03de164054d0eec292039f3d67480eb487551f84e034540518ce1fb225ebdcdf0866a2ca41990153d20fa2cf188f2787baf27e17969b5d291126a5aad941b1ceda5716aee40c1ee69409247dfbc823458c1971157be28f7c7257d35c5a27953381e3575983575dd576049edc9b220415f335a8d2615aa788743149a6193910c6ce9ecaff4fac8ab1ff0c39e412c1073ad3f5e571bc19b61e89ac4167f914631df1551033c35e2a485e05c03d163959772a8e741a768eb40610d0738681d7e2a365a42b11592379c5240a992e154495e745b0bf1300e3bba872c9784b3fe3ef9423a1b79b10f80a15350c892215ffdeaf9b6b7e6a48d78bff4438e2dadb943252e31b61a380036d0ee50ff20541fd19f53aac8548fd0eb4f600c7cd24acd3fd3250f9091d67b0f80ff500bd1aaf56f56a5630023a0d2e445abc6512124965b6eeee7ae884aa045c93970ff288e3446f6f7fec432597cf27862a47ce01484dadc8640a5f3bbd8d14da800cf41d8fcb6e21dbc7fcc1d428b4e781d84fa79a81ffa0b4b013f08ea2661e6d0f159ffa84a4639da1c48b3e0a88348115de097974e522b0bfcbf6f95d3edb890ce20889db2a471376d08f3ea80a1faaa3c2cd0b15816929b07ab289f5cec8dd0578402759be3e13c52ae404be4ddbb43fa63ef7ac9bb929e1ab7e61117fa51c0ca4cb1f518fec6ce3b86d29dcd2182a364a52945cc1cb449653882707065f31020888f97bdeeddefa4d389edb6991b53cc36f573ec37c911e8f324d8f30605a0d03bed5a7941ed2390f72fe3fa2a2d323b715c118a2c9819831121d714ed0b604d27893dad2526c477b4d707ab58987bdbc2c3257ea2927f838a5967c40836c0d7504d910f4c88416a2672ed45244c31b2920fe325b2bbd2bfcbc5a43bdc0b203bc6659d11c9aaac3b2b58881fd3a34dd3c70ab7666d41a6ba44cbafb1839270339f5d884b98268e81f2e88d74170791b5e1b7bab98676f2f3cf6dbee339df10d524864599b1795b16feae0952994713be842cdccc3c4a5ee6d265e1fb55a930715882bc73a4f743dce4f385937890363e6f24290f2aeeab210ce88a40c62fc05bb42ab06cd72ab461494aa625cba5faeee87182436036e043f9d66717c64d1095d32db7384980e3b32df60987cf91ef9e9186946305d1cce52d72f252c53cd05adc302f60f151dc02cd538749517674d7a90827102623eb1e0a962699171fa065bf1f60cbcdd263ff3c15191994f79772eeb5459e3567a8ad8a13e844075350a75d3477c7eea41ba87c69539ca2675630dc4a5e514b3d056b6937f8fd0e75c02b2fb06a273f44e554f0624dc2343e7a85ad3e2fe3f47f10ba24fc7dbdcb93904c372cf37c176f495d06cd77d7d39cb995fcb924325a72779c35e899215998e0b04246e43fb97ee5b69590ed3ea3e8d25d1082e74e7c7abcf190cff5989629f05b6cfd9994886252b78a2cfe502f264d742254c22b28b1c7fe7b639c0de8a78037c441b18f854606f2b4b279ad820abb3f88ced0f5499aaf722885157784a3a0553445e9660305745f7fe5a63eeab450014053dc271a198d026520dfd1bac6e0cba85df2dbb03bd4cda640b898ccddcb79e809922899074375c4bf210d3b6850ab1d8ef500cfe5b77ff35237391c3c028fc8a8261605eaacb6f93756c9901e20fa048a2769b9ddcd5ac8cf3bcf103f3e0da635c5d9ee132b02b633a9a8d2f1a0c6cc792354b2a9ed497a25b04c59549ef4dc3c9dfc8d678105241b2ce4d6e7a0dc02b0c207f5329a8bb6b45f914e6e378fa6cd5ee794826c897fd9bf231ecee0044c12f41f31ead1fdebc0f101c913ffe01836bbb4b2f82f4c751441a8f6cee0bed0e503850734a90821d047a3e6850600c52d2b9af046a5ee60c66f6f28b230f4a8f19c795d955ceeeb91d353d09506d83f440b20d0e636e5d96c1dde03ec3320854074d304e1465110f704d9ea8716ea09fd5cf3c985e15b9e25de71dc58e015bccebc0ee3be606c700047a8614f195ae47b01f3f87060e6cde097c20ad95058308018de31cfe1e0d22509881c5409e5a8f9e6cf72acff785f7cb0751078e09df8c51ec02bdb1f6f81209624e147f824f823d6d500db8836e60affb21dc7a098906151a417527ae00561cc3b2b7fea4ad3c5f7ea06978438024854100cf490749b1ca96d0f7b25a0105ae278273399d60e1c52fab00ede12b69a6d4c1897d0a55b8cfb762220fa4cdb9abb2e9c24e36f6455b88e43872771c41931f9098e10ce94eac58fff9a4935c087042146c0412104ec8bc24597b7ffb1ca8979fd18f0dae151fa1a5b66f0cdc981f13bd29d5ef361a5e99560462c54f55629097a57c01c3bf8152be8c57c5e2acc4b1d0bea088928ac52017dabdd40ca119336d61840cd0fd7ef94077722ca382484b78a3d40638a8f4a570dcd4b0e2bc748bd4c22c056fefe3a2399539744bb96073095124a33ebeb9ef4fc5bcdc83e6d9415735a60cad1aa22efd777eff8003e423a15591b814c94e2d9e3d76c186329bba91ec750209688201ab548285052c2f635966e77e49eb5772d7656c47048debeda22f100593bec76abec64dcad9dac28bf2241eb1a17c2150f4f96c7c441a59df01d004028333e9e8a3b93728ea4619f3255ae2e39e50b7f6e24b4cdd67bb0d933c42c00d15e0e0bfe67f9395d3d8909110d824dba7fc475524054a7675cd1845b9580c00135cca82e552749549325063702a124b9bbae2585388b0a2780b45c14099620363ee0f8c7f0a48104da8760b7b5a8a9a1175495127a837919aaeb0f294ef3307ce906af737756323d07309243c8898d42207f0b61f51dcb5c38e0b4047281b00686da7520a684138f659db2f9273719c0265437ebf8c304bbef46d2a593a0502ac2208457f35cd5624a1a878c6784f1ca57e16a70833571034969be46070ec0335ecc7412e5f1e28ecedc50a5a7ca75301941139fb1fdb717f135a3a49e860059e57cfc83c3125912bb1cf5bd17fbcd99b42150a3da7a78777615df84367450264d65afe969465043a1943e7db2011878f3017b03b9d09ec79b2b4909ffa36027848a9c8ae56f0cdc0a39377e47cea5802b508b2c4e403bf61d598094249250507add795b6d0fe2727972dbb5e5d54f8be6f067c155d6778418cf802d369f705414d2e0d5daf6187f17ddec0a3438157f44eec0cfd1880392b36db9907a2c00665434afc4405cd9ab3d8ff20c28c4fbab777d16fea512f183cbaffd19a984a09f34d5a1e2fb417f42860af79661da6ce32260bae7d4953856e54aef4849dd50badb9e7632faed15810924cf5067bcd9213d9560fc10736ee5c399cfdfd76460fb0caaa56b3b6a5483ccc8e6e062ffa18c7431f6905b530eb7a9c53b51d54ff0a8c99a57d67b52af7002aa51795ccc4b875feb1b22815325a6c37e5b9d76e770a014294079f7504a65393fe28d622e2640fcce08aeda5b30bbeace2a34e01270ee15236ee51cd30eed55e54e59a63cd5c1472b263e0db9b330016ba25563ea50c4f9e9a96cdb7d3a73dac509d7ad8b7c057ed729abb2313dd2063074703652e097e4450b766ce6ab3ea9896596225dbe4bea4ff17b7a30afa6e0acf6911ef8b0c1c909ecebf9ea026f46a75811e815d5259076efc51b37483375cb4782fcaaa0ca50f23a669ab37e476bfff6e84c141e94fc8d095115ddcfa35bfd4a9febd4802df31f83f207f9e4e104be6859838ad4b86135fe51c96a6dd9eb4742b7f516f0071c938ad81d2a2ccb4fac917d6b7e9667dc9667b68daf058f2d92f5d38e80b0e909f8ffb21f503c8d18111edb48e1e5bd02cd315e197cdbfc777fcf77de679082173c246bf7e9c59e0379db93833acd1ec000c12ae1bf774ebc87e90d3eca9040663fb65d27e26a64a89fd5b53646e9bcd84405e2a331f195f4eac9f2df76c0404a55af91bcc40ea09178e629f6d016c2d60878197ef7639bc8eacd7d7d0900e8e8d67c5f04cbfe06ff24e7fc337e2e073558cd1b44166f1d26d2f4cbbd5730e7bad031e36de3dfc0c6bc48b74e1a1a4707fec66f91e5352953653fffc95cf07e666e8dd692d3503ff21cb9db8755502105e942500a1f8c896f7230caf56a30cdb0263ce9ea784af116f4824887fb435f9f4449d6b24a7124a9b3094a6b6e10b459860457f660342c1dd75bafba236d5e00fbb485670d767a37e7536e44fb50147f76e60a07c8ceb3d0242aebc3226de11e0f63521e41c9f4ef68ca09debfb06607fcce9e568f4b0d1ed428aae739149be532c8a27639221993e157c4bf49c05b93d41e28122456f782511b69aee4cc4c2ceee5b0b110696cd9904d558a0df0768eacc8e7c8f05832312344a419b0c7eab94e920e63a59894cb5243d6fdf7e0db3a5f044c86434fb7689196428c66bc249da34be38cab8bedf70a988c217a1067ebca12a0002b160f55164dc3dfac5760e39c7149258ee3934057ba1fe648209ac904a2d737fba2c5bf721d47466b3e41e51dd768a4eb1863ff4703b68b16206fe65bdd33e2bf548e31f5ca4026f23b2f91be7ea8f4c4d201c3914d55367ea0f40013ac602e4be803c0620098f6091a7cc8c9d1bb8f7316d9b392cc8eb234d034525e19bdad633e92ce443e635e4b0e2f84a222b91d0f3df19f5c197e2ef710c7b5a15e63ecfd8b5eed2b5ef61b8a9d6f72c78f3f3a6e5cf40fb468824ab070108207d8bbfccb164c86bbef78133d609767fc9b0d9fe07b11cd2930eb315d607df0aa86c81119ceb1d5431f3a4a836a8a24e81213db8947172bdd2a343518a0d05cbec5952ef734c9739fc0b5856c5d23d519230bd0e8bb4fe53a2169fadcc0cf0be0be64bae8e77a987ca46401d98e6f6a178745b9bcbfac9148fd09fde7d0a0ad832ae2432455f85f99ea02c7b9706b1cb7cb3640521b6f9817bffc5b2b40c97c39ee2e2b031f117e766b70f5e44abbce324cad23c2f233ff054a88a5e750dfe50650c2b1dabfdc4267a1540dccceabc3db54c5492efdea7963068c36af20b1186a672f221604b4a71edcd376108e6569384e5729ec35a1d1f7a165232fb0a059093781560001c2cc5e055f81ad49332ca2d46841667e7847cb264989a8f027a739b3591cbc8a1c21279e2949914f763c82df81249cef2fdcc4eb31e8af9027c963fd242e5e54aa2c6bd5aa75dd12370c2235a8dbccd94b6a1cc1a01fe620a6c21cde6ce6ca2ee9f7229447f31846c8c20d3bbfa8d2d372a045527437c9903924192706661c337296745dfe2b8fa80a03b1cce1e5d84b993de6b9769dc5307bb8c3dff68304619a8b4d8f7f7cdb025356bf4a1595f94e67b5802ac1587b00c377af452272a913eefef514336d9501800780250222db3a59a098fefd97c060b4fd3f9ee171e762fcad1ab35b699671a0495896b3b5aeda8c62948c56e719301536cf80f59b5c4772b67a6cccd5c31c3a146193035e76933e5e5523d61a5330cca7926621c31426f7e9dc803c5893dc4913eb45e879a2a1f44ae28a2db2ddd069a8a9c2eed6a1f29e73372d0075488036a950494441550a65d86377dc2270300764714cf7b9737620c0dff5df2caffdd34f827314b244413edcfb5b313482a0e531f1c2046474bc697999423bf1677ce5b41710949d0e9558f1e00efe6ee59093f47021f2287a77e3342db1e4998a395f6d8cc3980773f9c7347a74957272f05f5c9ec6526ac70256e7ee961a876840eec8719b976eb6d1745fa3319a347920231b397be3e5528e8d61a3f24e94d8731b33a35b160e1412e2f17fbb73baf7302320e6ed08b7fe1e09fd8cc5a894f6c9e432f9f7b9fabab5cf431718052a0bd045957a67cc5c0ef4f62500bd8f25406cdfecf00561e53a1c0237b35cb79f2ff003ac62dd4bf4c25da232095e4387c9b70da9a373cf65a105fccc0997655dfdd09b36807718f9c38e325821728ccaa224b32b446dc020a3bfef40eb24c2177cd0b220b49084cffb474c5b6766ad3163804128016164552716403ca75b383d2400f2e9344fd8162fdff224aedcc800fac5dd8479214c30f0e0d834ecf0b691ee10052d12d212f9c4908e8ca176b7d29f7f379f747b4c98db2be5258f1ba050e8809aa500295303381f1d7b33d2c95fd2348cb27c159f0e6d877edfeda63d10f2400eaee59fecfd207935c7c99b08ac926700ae66c7b31bec69007872a6f9f075d0b6fd0091cb160fc972334e799ce761705effa82745d12326ce328fee969b4bf06dc4eaf931cbd9f26e75d91dae4a358528f734835d0b113bc5e927fa1292db7097f2e134df2578713c18090a21afc71ec19d615c31d6d8af095972221db806204ffcd67f1cd8b7f177b7ac59637e91b4095d2cec55e411f82c9b13ed0a9c9b002d8fade96c084822a20e63ed0890773bd8251ab2e7e4a97cf3a6855faa4aecb0af9df60666e42b64c741e49e0614c0ddc9b744e658dcffae16b8ca539cc72030bdb51dc85a7e0f3a3ba5f8c9dda76de5f671d06f37768987505c1e7ee4c635303f93047e96dfb958356944f6c689d1866940b670f12844b637d0a919fdfb853069eb2d5ba60ac3d27ecbb388559009f14289e363c7389e8acafa2f5fbf26db7012dc5b820cc7ebede01169d433b62dfa326bd6fe2054c8128549ccf9e21b4fe0bb088041108c2f09710753866d95cf42c5c0cb30fffc404704d61a6f6e335f7010d8cbf91c1759a33ff97a3199d744faf8ef0026b4c29e4fab413338fb26ad30339c746b150feb450083a823d1de4c0c6b418f0da360d17350a4f8bf122d1d5029d5e1aff8085c0442f0c9210c2af97252b2ad58b0e32045886e13406cfd11c0f1fbf404c1046921c88dee2b266d14447600df7e1d8f100c54eaf613e7db7c0a29754a7c2c09bc90581721abe099931eb92920c8b5f21cfd5788c2a15a6b0630043afdfe38b83d72a2f6c7c71756f51e690cd19b1f32742070a4d145ab7543407906f05bf258bbafd7cddb71acb24218961a958273208ba082005cde57086ab06b4c4e3eff4a3da98c499b38eed49921c7c8eb0a7839d793d95dcdcbbbcbcb60aa5eabf19873ee4fdde98eee126d3f269dd66e58a0176364420742140d783ae079ccc378f910ca1854d05b6ec381858f150c2ae0138adc61ac3142adc004e1f00ca2b1d2829bd7fb34f6675dd58a8efed59bbf92fffb6e1eb29efe5e255dd88099b9e8d9f3b35f34c9a0168645f3b19135d6941f13ce7b477d8e9397287467008d231d47a470e3b5b8646c13c41a732890cd5d14459e76ac110c98403f5c3b406ed708dcae37f9de4216048b5a411f2035c78910edee09a0f22b43da240214e0997095e1e6aef8472b818654da3f9c1d020dd84ecedd54ed89e2035b26200440c8558d04e363a80ac06ecea6533c46fcd2002f7133fb3df8ca91218aad36cd107c549334a0666f1e4b9f1f5fdb49d1736c9ff3a816854c7e9ce874452b29fad0eeab748e0bf2950537d6bd248bea687ce107ad5bb6decf4062fd439a820d2ff0f36d4ac022e94f2bd1a1a2280f78fde460a6ed1e5910ef5d48a303771924d30044c02f8da248a565e2dba3d39dd560851931606bf84a2f532cc01f135c1ed880e75db0a4702e51bb92d250d3a0a4028bc7a72359f9c4e1268f01a2bcaa889f90708c5a5b0e9d88ff632f978297092255daef94b4f690761924e19a5c3a3379c0556a74b02260447dcebb11ee7cd738a7cdefb9ee344463a0079c2165a1a930c0d1f3f6890a891df708cfb8913d01e5742c920a4643233082c6265538aba127e0590ad56bfb40936f3dd1ba43b8bccbbfb4c63796293d6c814cc1845a42286550d634b32d5d01696c7b71e6a4d57fbcc6aade487ad0856dbeeea981ffe9aba57053d2ff3234171d7bf8cc08fd7a99be01b1c7263aeb56dea452f85f7b6ab22190f57c1d8fe14545ba0b5c0e467d86a36ccdda9704effc4fde0620979dd543b9b0b0324bc3bcf9533aa4baddc18f704d085729638cfc746159b427b3053fb7b1d04ee15e66ca20bfc69127f6ecdab6533b242b38a525cdc4f2ee1888d298682330abab7a558a1fa68320ec0e65578964aa72d7b31c282c8b8716478a208d03bd9075e0588fe7521d7591b4dc053996864b12f4847ed22eedf9e3ff40ea59ebb2f0aaab0beeea95ee0e9fb3a71a2abc6df81aaa3e0e450c41cacbcc032686e1f0a06a0e35a6ed9cf8b98e6bad3784ce5f4597a8bff9831676690ead1df78431a4b01b0bd384604df46f38e78fd0537cb9505167c74ba6c4ae71570726a02b5c72e08e437a5f2cbf2686cff854d73ffc1b15f724b3c0046c1007b05507b52d83d0503c8a17f341d9395fd060ef9e01c0e245133effbd9ab153d511bb3942979732303b7f772e2ff1e3f4358db5fa0fb7402cdf3013b2599f147522cee3efbc2e1bc06e06735034c0ffe272c66d7c2fb3d0a755a6820cc87975882705df822554de9008ac3c2d898cee763bd5021c405b9877bdc97a34a6aff3858786dbffd52277a054a85b62160edbef155cd85ecad402e0c7168d89ebc078f44db46dbf992b0820eeb3b74db814099a3b8e4ef09b5d8f27cf94e6f744cefd987fa017cb588e0700b5883d192f50f775f93db29ee41af492aadc2cc3c06fda8145d22c71f991a420ab243f850eddcd1fd27ee5753069f097cbade6f4ecdd3ca86fedcf2377d54d70e3f7c9c327d33cb42c59be09aca0ef0803910430baf19cc21f6558b70cea8d50b0d8b966d91986d86a674c94fff187f631f296963eeb90ed4d56dbe9fa4499b0e0e0dc5f517875d65e513cb8e946a11badf65c148eca4217ff626ea8955b08300ab676996562b195838f6b46fd592014ef8a4038db64840950209501edae44606988ec076e30923dd53cdccb1111321f719b00d0ed924f95c6e602b1c381ab40c984b818d657c43012e3822147c342a2676677b711af27e178d4869d13ec0ea03d9db6d21380a82b9535ed04c93c2a2f60870dca53ec8294eeb2d366eb05eb40a97949064307cb72b153a5a20594ed49857bf2aa408b44697046e65e7871e850fd20825ec9320929ca10c8e1532589aafc1964578e68a72fd2aa6b35a90e854011492c1ea03ae762b03bda6942642a5eb6212099690d2cf6464ba85c35004d305e44ebcb1882916afb42925d3e48f0dd70be57e19e81a5cac479bfc7329e28a015f73e74ae04ba05aef9fc4df47a8cc670395d0ebaa27e96eba9b8e93986f8d01db70dbb3bc890407b8591074ee5f476b275b11d0ff4d823838c0a4dbb200240ab4cd40d1d23b784f03941d824e204e77b952e43bbe7e8a6c83e1a80a857d3309126b9287830dcb479acded83a3d48d6875c9005c1cd95b474953e9ce59ae6906308a36df2caf225efbcdb6887fda4b47e681758dee902abb996cc72c6ea71905baa5acf99eb227135d581ac054c21ef317354e017dba3d9c200316db6f108f02772131ac5b858ab90f729b177323be1cd511290ff67fdca9aee0637420b3930df02002a8d5785209428709e2a263f3502b226a680ceb324c1f74d852e16c8a024696db53ff528e6ca4bcfcd71cc63f32bf43f7f86a9e1c7ec5dba7ea167da003db77c0b9a64dfc64cdad40435f014858d26ce2b6ace6fc65fe3336da36162d0f1f7d5f26379c7b1fe2685e8e5d5e0dddcdfc948b180f3c705f0397a898b95f0b350a5ad315995996f74d041fcbdad2889998ffa83d5b5da9859a0113274d010882b6d37da238465e69779320156bdef9b93bc9183c228c2339ff5c584cc3500f0ce8368351959e26912d0ccaa8e828c5d8aba0b6ac81b3afa90c61371c36180ceadefb9a784edeb724095177e8dda8a432fef9e6d6ef58c5be0c653e56fa210fd353dfede0cb98bec106ef011eb9f84d1f2ee4c209ced7133c5b95b60553e60cbe5edc2c52402181832045584daee6c9bbe8f5b68da85b639d366a8c2bc3ea0fbd2a2713d8f5577bf1de3caeb49545ff6f6ccbfcb3c343bf686aa920320e2f0022ae80a0451df8b785216e96ea8062e2b1bd6e7b46319b6025f7072130f5180cda31cc497d3d731163ff0907f31d7eb19e8218fca38186640ecc2586ad415606c4d5b9eadc6773ef88e8f1d596fc1ef4ce2a569a8d7d11df930479ce4bffbc0a1bcae83ccb695665d40325d9507dc75d12cc9f9453871e27c37ef5f48155cc0a4f35a6bf15a078348e5f67dff2624bdad7ca1d4de4506c6bd848e2bd699e8401d0e8d8a48580458d0687cf0ad54a4b7de2e761fc8e2574fa21ce5bb91b04ee032f39675464ea2f3da5d53201860f9b8d0458c953a77d3c1b1335dca75a9421084a664cf5e8677f1d83bfb006e70862ac28b0ee661f25f6e87b9cc73c46d90f061c16def0c9cd42755852a13946b8ba58f7d15608546c0395a2bb738166e4c50aa91337f2746158cc8f7ef3b2137ece37ec1868b9ccd228d11888dd429d25fe06f5301a67e47dc3222d0f80eb3f2a537d8de1ba8df1d78a5d10d295c6aa44150263a33a7df2744e67f6624df5592794ff9fd1b22cd016f9a3854caac6bf04b00433b271041e02c37054d82aeb054dc3ee6f289101ecc6629fdee1fec5ea41af0694d17a60d28516e52528cc853746b1ceec466cf8c985fcb5b27bd8cbd4648909b56345408b5a5950c8753c96cf63d22c1f7a51215a7c4c90e4f333178cc0f20b6000eadb56e3f86a60011136fac21d10a09c80380de2ef30c889420d03aa4101a3714422d7a945f6107bcfa1a191e28b598c7d3d1c4c3f63bd9cad3ae7c7c00dcd29bc684381dc38ad59dd65e3dac8c3097c4ca09bc19eee4e73cd39b083b20415cf4f914d2c2f73a15e8d35d845008a455b17b50453e84b5211fd7660c7630f57e59ab5d36526fad2ca466ae4881f1eb78da44145330e5956c841eb0f1a4903de2dc5f7f00e48ba5fa245f6ba13842e8faadd3eeb2496553a36e4bf63515f011b348a5fab9610e754a1f05b0d68359f558b5f8cac411233ee06328fe116a2043f7ed16bed81b4db74143f955617ff266098e4a1751c1a800ea8e490efdcea07730779e950aba0cdeb5a8d00450a805eba64d6ee8339428c2e0ccdf7d5ac370e5bb97e3cd94c5477414990bfb739c220190492e1e719cddb63293eefb65c360ec9baeb375c73017ae76ec0b2e67c0d89e01df2cf79031093baa49c8f2ed47a0411ae06e6cdb1b99836a1770240646cd685cf2723da835720c521111e49dddc08c512739d27590d672483cba1ced71e6ef79a2544e668b0a4a386c2e74f963000e21177a937552f05dc30f1daaa2ce11cea9179f7688e506411705341dba4910011859db05cf9feab903db7553bd5f259c33c3f4f49aa423157d0b07dd93a210b249ed46e956d2fe4598cb33be71a335d23b5e305369a5686d53bf98eb2ca8b07e33c2ad960100a0549d47f1fc04274fafc6d34e72d411b11ca43de52a5f2fc020ff71ea10cc055599855bf741631cec12a02aef4a40dcc0ee0a4e6f69a2d9c02 false -check_ring_signature da1fac3555a0dca81cc997c28c8d4acc56050cd612ddc533d1b648e6806a846c 2a19b82d94011db0f8b96fa2622f8e8cb4038cf1814df17160d7bb086d27f55b 1 82fc2cd8b21da114fd872921030660df9c1e36010ba6edc54539c15552905706 d43025c44e909df9f885a1328188baee9add69bf302603219d06ca3a9335000542144dbda7a6de91fa83d681487ff6a59f8c724e67e9b87a6843475b9d9b8e00 false -check_ring_signature d6f0143b793a29614488cd6e2fb788c5224f3f30f1636a0629502d7c36e59766 8863295066ac6613b7152222750bc8ece2ca76f1743f92caa7e89d245cd64930 30 528a165849657fb5e0ff4f51d5076c27667a4e116c65c80873f37fbb3d94f17b cfeb31e6cca35feab40e74bd154619454ddcf2c23aa6a8b449633154d11ad5ba 810e71bf58e6122bdaf30bd4c5b8b5d934ca64826b53b8d649d3f892d984adef c90ab22950ee2a8e5f274524f3c15637e550bc7772baa7d0e17b3132f51a7455 2720ec503a71c5b6e8eab9ea466911270a02dc0db3c7d5f123d36d117495d4c7 565ecda295863bb1109cf63df652867fc6ec9b06058ff5094eb542ec30c57c01 6b48901f42cffdce2f8ec9a22cfe3f2e5942216efbd4312b33eb0a0bbc7990c4 96d1e7ec108ff22785289ea27ef103a91e967b86b0b22f9a047b82db1b3b0fc8 0bdab80595f85be9e761ca3b0cebb92466cf2deb2ecba7eb9b9d1305831bdfb7 f8f50c972019dc0bd9d47562acd0136a1b2379ac96c9476cb97898599bb63c3c d50a4b922cb5032237e7f1725797c06d127f93ee4cc23ff0087111e5c21abbd5 0155f17e15fe3fd4c252f49b150e896eff8a1ca852780e2b7eaa6e329abf2b1b 4d83e80fd3593d9530afbd35aeb40dd72ae62de5367c39343af67a8a2d2a5c8d 6dc27324684c2eb3d669fdeec3c5e7c473ddeda246448f0f056f8020afece161 622552f6e2314e14b3793d9613a16820334184ab23b09d97f872a7bee3d60610 bf3f1c1f45c5e40be64ebc09d7091d3cfc1910b9dd6893cc420f752f44478bbc 320eb7d5ca954364adbab00f58b85efa0745d505c9d1a029747b24be91dedae4 74b68ab7e2e37289f406efed4a357cb55cd437226e8ac36d0583cbfe0375b6e5 a0d37496803130eec4cc597b3c257abee07b1e91205f4058d59ec3b160c5a8a3 be6022ddc2d16594377e2da99446c4a96a3f919ca0e05e1ba8b9cbfc91e29eb3 1057599582001dda431f86284017ac9a96f0ab166a15bcadd325c1e6d48146c2 2d706c3b64f4a32438ee88168df7d941d8c7f84f229aead45c14c935b080fded ad3ce7e7e79aa1a651f404bc4b86940e0b17afab3a7c555d09f56e8081fcb4e9 7c9845a7b482340a22160ef2dbc1ae988ecc88462bad02c12578616a71a384fa 2f2bedcfd8dce0bd4b1466d7e98b55c9c591b3c853bc6f7403f09d8713c5b068 bbd65fc2bc34ad4f34fb289eccf5437d16d7776780190fec25a71d80d6d6e3df ba465fc2a5db440fade5e45811a365b062c89f0032493607cc9089d46f485cc5 f63da2fb4877c2a1153587e08ec34219219df2b79dda446fdc53adfffea9e8d3 542aa5022b44d0395818271410600a3e870e9a17789283db685b6007fa060101 351dbb6288d08e1f3de47846dd8a4e7ddfd8a750f064103e25d6d68835d4dc1b 03395c70d86a7bc8d9df644684fd5442ff45470338f8c48b714c85188892e3048faa8fd180481ff289a1606dc65daeb1da35a784fa25895bf192a51819eeae089843edda1dd5ee8953249551dabb656c8b6414a810fec73f4f2a0159157e990365c464be7b57f0c711734e4414240919730858e26c9628069baa09067965bc0ea9e2cb61c5f9cc2cfab707b9f62bfea7cd28430d3fc1d8010eacd99fd8854908616dbab98849003ad25550b7ef4d18935f97a21afabeee7102aef21a2bf58b046c5d1d393c7651e3b9527786be88d266b7b488443a96c23266b869a310a857013c67ac89045befdcffcbc68c11b52a5b8eb9c28f17d5eb1d5ce83c56aefaa80ea21a5c6144937ebf73311ca6be582425d3dd8f581e8543fb5d197d0e4f5f690caf7f99a1aacf67b029ca6a460137a2a282f989b735133a960e1603c2265ffa0a88d90dd52d12a4336de38bda35b4425b2ad34351802ab47cff2381076636c20709678a0f015030798e4d10a10e9f30b7d4fbfd39b6b873ef705f7daed619790186ff36e0d300f5ba65149fdd6221ceacd3346af90e14fb63fa7084bc74cdc10f76ebf2e76c86ca3e001e5b35b6e35b65200bfa8b151763308de729f4e7d3ec025cc3552743399254fce1c5ae8b11435538b644972b261bb228950565eb844109976dab2083dea4c2168bc14dfbf853d67223bb25490c386af566ced77daf9f05187e8453b437fd615164ffd19461b995e912fd7958a4f53ed8fda3cd0f9f870685bfd8e2992a04c8d37baf492e5a9e56134d439e9028abb6638b69b33ff32e0701c2cc1cffe1db4039ba9e63c7e5d5610b2160cb4fb33f9be59cda890a0ef903ff992d8d5da74eebeb091cc60ecaf1d7e022f215fde91ef9cfc6eeacb5505b02189cc7ecd6b4b843d1c12c88002208476b5ee87d4b601eed6b524c3fc08b5c0246da96931b3de5edcab578564e2e03cacd6176963fd99c2af29975b467d199067d8c6c6c8f3c9d255b736593a6c20d8a9592baa1037d9f5e39aab72705abfa02f5303ff8910fbab217d0d5339aa36ea1553312fae41d06812c594bbb0dcc0e0bb71e53caf00239b39c197a6f2a6b2649d601bec5a110b0f3ee2ba75a4232430575b6686c08d70c97c4719e3f5375098787186465367779c58ed4ca8d21b916072e1a2c9fd9016b185227491a04bbb70ee13e00a64cd58602fc1e533271c06300b86431c17fdbaaa8872e8091ec62fa30046a0647ed6446532e5d98da54dd130f856dc471af11bc59811fd3859ffa1f1af63f30421c721fd872f625daa213d006962d867f96a1402f6b53b35361223f2376d7dbb251539ea792dc907e4d461303149cd558dce3e377133a53c1a945563280e2ba20030bae44dcf98c77d6cdc30accb4eb6539b7acddc52d959c2a1e3d18cad2e4dd6621f9a4de63eeb55fd8ed0ddd207f51ccbaef7d6ee693d94df18d65211d3360f26ac6dd8a8eab6c5581170f91bab4b92fe84b2f9bab7651b4a4f3d36a0082715659746d6a1ca464196bfb0e08ca7e08873b1e5f728a02924f0927adaa3ee371cd3edf62cd18873a891f6f0d501a8e7fde860d538dbce51b39d9e7f0b9b8f96d2d8d59b1bc1c5136c3478b01ac0373ddacabc428543572b0592162a9834e2e0484f33585cddc7c60f05eed0da5ca5936c8e075b5ffcebdbfccf9b9fdb73f90703c687ef8c79c03c21ce495051af15f6ca072aedcd508fef454b6376eb740f94f7597958d8b328c0d3411a00ae437461200363e4af7a558ef905b3eb296f563a9f1cf06c0c60e5151a36bf70cbe6390fac3fad4d93952d66fa8c2d6e14bfc0a0fd4b0e60fcecc925168a5e7009d8dddc9b4e0ab9e6857fd47c2a42198d1227371a235995b7c5ddeee91798b055274f4e79092c0f018e2b481088d81f0bc115453a69110bcc0f2e8c331292e09bf94123b5029e8536834bda018216b30655d6296602eaca56befa90e20c8140fe87c4e7d70bdddb479a6cd840afabbbbe430a4c1ad7f117d19a69fa49bb24302148f7db899194b54539fcdf17baa3667805cbcec0663617403d825209fc5ab0a90feedc7286d7ac39a1536f7494a046f85f38f57a39458ea3adaf7a27f1237002e46b1830c3bc62b8225175b9ad5c6c0f42528d79b3ec3f09d19abdfdf0fc40707438dbe060324adb9b41339ad2d0ed55409655119fe4235e761617d3b63b908fa2c60fc9f8f768ac2bb51d0c2af48a5cd71ceb3eec0d7c959b2d1e8d5468005a27923a807b9db78d76fa4c94d4d308ecbc19336b1fe3573b3e81ea5af097302f05328460433ee29261c9e98be50b7e10ba0307e9e58f27cc8850acc896b3f041a49b79b00c08f444027126844a8ba1053138eefe8019ed13d97dbb0d3fa070c2a79d6636e7c4b5ff563645a63706d29da82687da0b0e588b8b4205352185e01ccb5de5965e0ac836d73c73f8aa0990c5fc31983a52b35145ddcf2d8af24920afb71c3e1fa05dad9fe236c5e526160c80f89da982f2378511ac72783719d6502a78e2e410a74c5a60519f40b968a66e42dbecec12590f1c2f292de1c871dc5070f268e1b133468f36d3cbfde1bc6da4461a6046e8191990e79c98c11631b110537e992386173941bc797fe1dd695aa090257de3f5348d9bb76082e4773e38a0a0eff34358e8d9d3ae688d347d3a9f9bff6531518dcfb3edc63f73d219eceb207 false -check_ring_signature eddea6094d83b53e84b126b16f48b4cdcf203725a1c78ae33679d3961aa611aa 9197b81d1a763851d0b5dff8d7ebe19ead486a9f1215ad82e32fec18f1b6b30f 8 a2c464c8d765cef11e0903fb49798ee411c68c8c852108a0587cf85ae00dfa0c b9a4ab801fd636bc0519a69cf4210d39e3f8c23c7fb79bed7c824bf49ae824af c6589d52b306c99e58e240051c1692827bda60509be8bdac2cea7562cf624235 1830339295aafc556eb31448ff9ddaf20c2e58507ec3d5f16284a2fdd2b6679c a4653be1caecaf18cd95a30fe9207e81ef226bc831ac968a9994805539e0f8aa d527a05fe4e6d87327f7c8a40a0a2525a8d253b8ba4ac32d27e3474fb8f87f50 a2212dfc538f6910c32cbb7e5528b5a5024169b5c33d4316d5980232c852ed16 a9656e4b4e24be66205388f071d497ad982df7d90022c0e992a0974b5326dbfa 3fd96ac4525bfbbc8bb4f1908b55c34d719405d7ce64bd3fe8bf3b8b28f60c0345ccbcb569904ab481029ee1d2788e9c4ae7333e8ccefbebe18edc26f5f0c408c249677a834daf59f4be05eb28d33e5f03d7f8966e5026c487d359989a3e2009be1f098c579a3bea7290dfb1a09f80bb105ffbdd862fd12243b3472ff17b410f6603777c9122c4d15fa1a8917662c84be874ad94c93277d0e1080505eec8c30f14f770408b8378483af2c6c25f49f480f621df01bf16d010cad2e19809c7440f6d2025cac5113e306e94cfbbf8f6d329b2cb0681f59686294ed289abc8b98905062854b5cce0b0349885c6fc719f707a40580a28ce16a127306917a6f2e84b0f57dcfcd21ec51c323d93fe899fb6b78ab83e543c4c0f2db019b92eb1e2e512093f5db385c15f804cb63cfd1e88ef98a110dcb852d6074dcd8c6d37e816b4c4041969e875a91a7651b5470737266be63d42e7fea80b037e1609c7e78b612631073a271e40d85fa250f7868c1297961164a991f76c9439b226a5069f9f06085c04bc09bff4672ccc469a52412f79ca4951b7f0b2dea885bf20f30352f913ea0709b3bc4e82b4801faac7a0828a34cbd444f7409107b11fddeba1dcdcf840ad6b0beee1a7ef3030fba8702d9efdfb56a9ad3020fe2a7b22d93f32733f1681d62403d926c42dbe6f7d660cd2f800a675db60c8114449a0a954ceb25a37f7fab30528 false -check_ring_signature 79fe3cd41f7f15a91e5962214228b6da29fc998910bd8910c0e7af830cd50b3e f2b0846557c45fe434c72243ac351d01b654b1afa29f771d271b4065010500a7 29 39262070c50b513ab3cbfe19a873395e408c27c6e218f6939afbbf3db8f45167 7e088b3173925ff4b1828f215314a291c191289b1f91de344eb5a18bd94dbed6 eb2f64b4a9fc086a3d2fdb74ce1ec9285740df2f4672a2c74f515781cbe7690a b14244c6991357692540482dded0a3b9c3880729b01fcdff21bf1eb602590713 e278a890e6b0a1e440a7162b050060f0b73e0f37c61e5ed88fc7d9e93c3515ef de2ed8e32186342026bce2bf8a7e4adc71c0592bc9b4fef636270401cdfbb2e6 48a92c22a35c97b67b40dc91fdd29f46f459b7b02aaae34cdc5071fd0b9fa291 c4749918992a5d8503044eb7b7a22053c98a8ac246a82b8921cc72e0541751e4 2022d7a8f77be4e0bb2b89e768a53a0956a394cd2e6e722a5bc36e1824bb3298 8dad14ee3a94b39577148c33376c85e10bfdd5de39a5bec7edf340e1489cd108 645833272d16796e69acb34aafd7e596361d41710f43a41e9bcaf4aee541de2e 0cf05ebfd7b6e05d763e8e819597fe48ae2feb521b6878223e40abeb6dd6f49a c4ec7cf0786956718259d3db1466f9b723a74ac9eccdc52327269f11e9f2cdb9 b78fde318effec9f80864476b803a46ab3edea7d7837bd724ce372a6892a0f03 b10bc787880887aec1c9f0851bacb8104115196219e51db601288026efc925ad 0d1ab867fab4724aca396c8164066f152bdc906cd8dd9682caf787752ad18ff5 547cff9516b1dcf56b6149bfec8e3a7c9e5a3c0872ab909dbf06c57358a7fad1 fdd5cafbfb179dcd48b49d2bf0de9dc8796b15515538460c0978294cc7345b6f 76bc27f2b6fa2e4773100d5a7febebf4083af9fa0d21235cd0d14c5349f6f1ec 344ddb5d05f83b0aa2251749d4e5d622c954cceb1367299beccf0218895a5fe1 766ae96b0058f6f6e68ed337d86b7ab8d676449dd420bc7a4498d60297b8dabe ec10f979350c9b9a10213dfbb390abddb169bfbbe08bd7e79861006111a40d5b 19a3b077890f529311be62d1c9004141cd3104e60fe92c1158050f237852fcfe 196190ba41c722827836fa6d693ea24f64b576208722d79fe12ae8c43403ed76 90cda2beff73062166246598fc2572489ff97d1f733cd0dfce43a1a278a65310 36da6ac72ea8a75122a88c36bb0714e0782860054f00ad3bcd3315030f211515 0f9bf503b4dd2d2e2b911b639847828a77e31314cb6c654023f4ab9412d098b1 e37a7334f272385b16fcac7a972d12c2d9fa9b162345ad0ac603f010611499ce 44156c5418656d7008756478bb80f5d9aa4c803e6b13e56ae551c412298365e2 fe4613fa00c11fbd4dc6f7bed0aa85ae1f600d31ad11db2eb0f0a1ce10232003e57aae8dbc89c750f33d155338f9ecee01be36a7a22b45a9adb688782ca47b0904e81514e3eb016ffd47ec9388adab561e070146c3e9e8533e3b2f1fb5c557012dad3aed3d09e2c9fac1dda5a3e8fbffb1e7dced94b50b1b3045c6041f8f9d0cdbc0c21c00417fef8e02d9cfd4ee65faffbba7ec5183f86d24b4e80f8dbdcf06ab4fd4fc506e76da82f5b21a05077d3d98c850a97e39125c21d10ccd2e4a900a93cc7e49da3bbe462e651e26e7b7b15a233739ffabfca46ef0a69c7e9d18e002deb0ca9c773c7825c2e7562811b072ad77d2322137fb2ab672289bc59d1cf803d4885c2562cea1c134aede5722d890806d74c1afcaa15515060f78feee363c0afc2ce587430d5b4c2a2bac9d7e60acd2c48891e77fffe5f3ce81d4aa760470037e19df37fd08c77db3a65dc080bf1d522cdf9e671ef19aacf029f8c063c87e0c37cee4d3ffdfa46f104b2b5fbce1eda3c182ee5aebfe8524de297ddd19c8f9037105f364aa8a2579b223bef4dd1e22ef3525d05cf1a96ef1f731f5175b73800a9e2ab055a83cb1506ef3f9aad934b5a8ade3488b38931ffebceb492c93001b0e94a07dd7cd24bb612d6f2ad58d972e3753e4451728eb667e5877a70b546a030d81937c44fe3997f385b8ae7c0031df5911fc9c4513501c1ae6f49483bcaf690bd7a780ad306fc649be921a03eb0f743248ba208ee3f0f2abcaabf87ef9e6a501d297d34549279aebe44db43dc0f9d0e142f247cc05303b0b2b5589a29e292c053c1641d212da66bdb550299245f3bc122c25c247013283de2be074b5d3a198038e3585ce25668e0490421c8a740f65165b4f41ac8cc0d17f237549d7b1463807416edbbecd8fb370d8c809f3fc1eb76c8e4ba99c238b93a02c46bebd29484001f685355a613c94e070c29d26126eb3a89801aebfeff915237598f210d8802801c41a236db2c8109f6c59a9b149e7bf4827cb5c73f25dc0c67401cb1ee4a21504cad74f31ed2d492bf99dbbde1372afd6c5eb575f0cabc029440015d5d6db180000922cd6d622d15071ffceb2c4331c619b185a38ef465a97e7a59015d372610467d6ab48efbdecdb35c6687998e52d110592f04e3dc463747ac439dcb035220f514379f606c832e6dedc64dca4b5e3166d76e7a56e7ed7d7344b8fad92e6c40a39068b74c4fb0ac98d0e9d1f518cd7741169b9c47e57517c9fe68e74b32b84065126a7658c375a7e2e2835c4a135e7d07aed7709b9423297ae5fe830d63a470008b50ccdf24e2c7c13cb9bba9b4f19f5deb522a4f034595ccf6b856f6dc3f90a8085ebf87cebb260da5e67da27666846bc81f1e0c66bbd8657f93dfb3b48e10910a7926e2330b1158354c40f5b73c662622719e4ffafa6aead7a6e12fd5e87041bf735828f5689956707422fb37fe468f96c8e7d9961166fe8fe0c08801bea089507b04b1b4f0d42d0c424121d9eb76781fd77be5a32c948edd7acd0449e3501acdd608419780da4ef78798995b535009b1541b53642cd6884829783c895810263e7a91495e8161cc84d8957f8a3531de7cb4077fc114675b15abebdc3c0920bc7dd9818aaed2809e5fc9e03c8cfc7bdd9d567a263771dae03ed8527ee92330c969b877b95142ece05227084c3acaec07a78ec9148872d5237988b53182670023ec2882e2a2e0aaa8a9a12f57c9b6540a0545e00234bb6b6eb8446e173544d037696c7f9713de57333b5eab262a6f4f394eaa65473b467149c7aa09f2a58660a822514f09e1b04cf03620824ba678d47120ffec9ae8df45582dca8b3af0e820961a3562f8bd7e26de9a8f2d1041a2e46706a34e178fdc8d6df6fad3f2e71e40f7a44d7605262569d2a61e2dfa904a08049ed257c68c00ab055dcec27206b9605f15ed042c3d2bda75fc27a4ccc7fc22f47162ee18293e71d442145388611510b78619646776ace912d85b811292e9b73a3a7c183251ab565e108cfb485098e07f916d4e612e702ff873d84b359b67e1f2e00adddf747aa259e4f93d5a039e909b6d659f0a0881424d64cf5d0fe19dcaaea91a217a3045242b9499cb0b61d460f70edae8f07cfc3746ce5b83dbdb896e0b8fa64f292872f69c1f5768f51ba8d086e85a44c56e024e2af2625384c7cfccfa40f5449416f7dc442c531f2a810560f545050d64d9ee642709c0ebc509545490976824c4268a6639cdea0354565b203f079179d94f016c37ac75a8a79e30177a63c9e0581f92c4a953217db60e4070879a2c9cfb6f27915fb842b8a88eef593c6bd2065cd41c86ad6d6cd127a78320e512d6c999946287ed6a3431a6c0ed61135729d06f96c34ff9adca955ae52a205984253ed69c2ab04dbca930813e7a558c5bfe932d571df564150182f0e5baf003dc4c1a69ca4aa2346d959fd99eba7c00905dfce6d46c59a58dd6a5ac650a402c6a1b479cea0a37e853c73765637aea866c34174d13f8745c1b3edf7daaddb06309c2742310e8713ef8a7e5319342cf438d09cb56c14a14ef65fe03e7fb5dc07e3e72789ac5373ee044ffaa318d9a15cb24c1848546de6be3b8af65909e6210e true -check_ring_signature 9e44c17e3365ff6f0d2104158903de8e408b06b8747827705028138843cb706b 69450fd5e415813f0816125ad22eb95a2c128485c11109e73eb4c0033727a336 75 f994016b1f2adaa33c98ee39d465d45548612e49e35898afe682206bc780cff8 f371e52d781256a1611a909e38d16f4c4e71255c3525591f4095c3bb6582969c 276b42b754ee475f1dc926b11227f1d211eed83c711c24fffd4a0d81b58d127a 86429ff6125e816902a48f4b0d1b4d75d198f6210c6231e5056e25dc03e9c008 35d7f56b6b34700d47bb9e9102ff48861e381668a57758e53feb621d65634c90 e06afd0024092cdf4eea7b3ab5b87797527bf7204c428e9a5d21eeb9b843e51d e7afde0c85360c877ebb47b51f1db4284dbe2f960235182262eb73cbc1683a26 d65cff48aea6274097143f140fff7647f89f3e0eb4f29ad8d2cd51fe44fcdbee 9a1ea7287a378d24a37a14c3ffceb739dd87ba05fe2afb58b7541aaef300ce98 f6bbc331ecd153e6a9470960c92b54ee118d8a553a59cc147b9e0220990fd974 fe958276fdd3077d99e63f81f000ae98f9c3bc10e7aac54394bf856bcd2ba116 00a7ae663ba271e49428f2772eadaaf517fe7f81187eaf23b6f4196a559cf34a b51b6405bcd2ecd0d42b370815961b0ce9c7cfdd76cba5bc296db898d7eb54f5 ade578afb58e045055e469b2d9d822e712538f1fac330ba956e97905251b476d 8d847e1f6a2ded67bd43b92c9c9a2b3c123b040f2dd686c9aac45a1d32bc7288 649d6f3dac83ffb60b4d000c1c5018e150a3e856f37ba5d56ee66fffda3b7e3d 9f613a0e80282e10e82499e62639f92b32c49ec348cf719e8750974f96e00ab4 4afd88ca1c7af4f48c3e6b70acaa64e30053c1876c652cef7ad1a2c07f97f814 04659495ca28d1576868247fbbd6c529ee8a11debba0c0d96191dcc2be26c693 e330f14808caa6f16db02b8f94348636e945e4cffb57cd010586f2f41ce61335 701d945d8ac1d07c7c8582b1f7ca731c991e727ec767c1a46a197a7587fd86b0 fff49d3d6d59a94417b0b76ba46703af67145f2e91d8c23ebda4a0b29586898a 22f9bd4afdc195a7af88d469672cf6edae3148783b7dd0bc57d0731adb818a7b 6367680a308d69e272f69b23a554af0064b1a31f05d332179ace169fa862ca1c 2b80e2aca93acda60ab61e4219418a2644e2dadd48a1eb215cde96bf29582b58 9ae117879854c954cdd5f08086022ee612a792d73c556479c5e040b88431e3bd 3686dd10782353af22900dc4d3c9775fcfa271401b53c904a5b1fb03888e5191 2fdea503b61727edf05791c96fd7ca31bb2a8c2fda084f7447a3ab0fa84b66c4 9f900d6ae01d720d8c7edffc54e1347551e4f10ec2a8d57da5dfd9370fe3882e 19fa98ff09f7e9cb84e77125bd7d2b421261281aca346478ad388d8087447fdb 0b5c3156e18d7a79bff7772ef2558ad9074f36dc4bf905f0e57618da71cab3b3 306ef6d370002194eda107665f0b23162629ba193840836a77f26026a978292e c30c9df3b01c352ed1096e1c01b97d67563096bc301d4809d31f77c6b8d4c186 57bac8b6828392c2435cc20647bf1594bc545dea7eeb7744b7b80ead0c00bade 5f2a642e96cdc4ccd61c721d5be4be9e91ef155428268928e4331f3ac344677e 53dfff765e13576d2a137219113d7060518da9b65efd7bdd8713ae9f8522b4d3 b8aa9246c45cbd3e579d439423d45287ced3bf1e7b3eadf0bd9dc621e887b8f2 631c8a7bc81ce360c2cd9c1ae2130aa10658611e1bf5fd8c64ae69d91df64b48 5e6b68448b69b8c7247c07b1e16034bf3c05c70fec79c890c8aaa7cae332167f 0eed10bc9e1171c40868e30b321ccf6ad7baaf637bc4510330a93c1816c6af8e d9538eb5cd1280297383a73c4c9a3f784a7d84177889cd5f4ea59e74c3482122 02cba84a58cd7fb5efe4b8708234aa48b6073be6c099359fdfe72813a7f4fb22 a164321840497120ebfcdd83d2d88bb9c1eef20ede5747172841bcd95068d2b6 51a00da1eca80b921ceba75625d404ea154b6d89365a3d231756f779b92d4d91 0fea288dca9c10dfa6dee44b3f42a26c7acf831d8ae4dce781161ad263d9b0d0 d7fe11d56b42736c521184ec8a7b57088ed8a0f30e164bcb9eee097d34f19272 1dcd27e0394dcf03f71412b7b1f098789a55fef9f7fe3575aadbe47b4627b7f0 c57b3ecb129fdfc113cd07316e60ae414b726423363828b4502539d6277b041e 4e095bd7db753d42e3acb0f137b195ea7262ccc4d65c71c85baf920547064e5d 9cd6792695b039f478f4b6435d82f38798423ed0af489121dd4811b284fa2d10 df55d0244f7df64127948a76ed448c533ca6adf209bb615b613de64ac34544a2 5974be4637f7f46b3c54c4462c747d23c0b31c383dead9acb25c7e439a4461f4 3cd952d00941c0c33716735f0d9d0eb8cde6be0d91bad03e609952ca518951aa d4c88bd4f778b8776d5ed058ffa405f4d816f493aad73518190c70feab23df39 0f007e8540f24c3f574aa6cd2a63469d1b4f8f015316c0a2ec4a28e40d289076 cb8250c3a377f5f703d4b0a50faa60ce1238068f0a2d03a1806c606388ca0801 c43ee9525706dfde5e1ad1ac7d04b72fd49ca75b8a21bf7c6846e100ae902349 fe8c2564d7258dfd2042e8058b9913cda9c47f4df3e062bd76f58d7fd5fee2b4 441b2d669d90cc7f1fa441f570825792e12b3ae7e728d84de6f0cf841d7ca1bc 06212940981b5e4ceeded307f481f272e449f13c66577eca9bb513c20441fc1e ba88f50b769c57a19fb3b50926ee58f82cc48e61daed0f4c3da53084ed0e11c7 ea20642a59bb5e1ab43f5701df7f8593858d15cc05fbc16b14540ef827a138b1 469d710eff458df150f8d2cf7d378a51c759ada59947870f4b3a07488b9bb0fc 60a09f808e658a0a549be0dc84bb25a16abb4f52a1f54c3631b3f0c92ee88730 567799b2e8559aafbc1b1e306db90610761b2b15b81633856a565422c7b10195 e59bb7ce95e7ff0add0f181d655d9fb8e9fe540a23b26a0540f424d22d393629 ca65318b21f78050aae60ed58cc0ec4bc926dc9ac683459bb628f3488e163c78 87495927d6f84be3676e810817a9750fc349ff007fd3a757b92cbf26f0cf0218 4348beb3355118349d76ee08f2a817602c8285771e6da42a46c5992e09f136b8 33f45ea8002e551e2058be162bd7bd9b6fa0efdcae11903a57a8f66059b7a0e0 0880b00e6a447069bf02e6e36cf5802c07f05835582d5312892fb0bce09723f5 58791fa2b30bf4a9c2898d4a366e6cf7bc7084c9735bf7833de78f7110e326a5 3c05f1bb047a49e7bfab0349367c683579bb022c9340cf93a19eae8e19addb33 1d51e4bb4646b4b3b37590e4db398cc3b87fd9ce638238b6d53738828b203f7c f59fcd934d6c5bc6786c7ebeb84908233ee5fca56e9e49ad4b771cf95f2dd7a1 7511880e9147e9252e38fc2061b5ce6840816d3ea7df67127c82a1561816b30f6744466f183fc0de7a6ff9c76864974a2cbd103c97ca12f3dcfd1e7ed14ffd0d44df37d63a3f3dff9f339c97169343bc396574bdcf13c8f698c729ecaa3fe90ef9cbf9cab1cdd428c9a7aec2039fd71fe954b2daddf052009902d8fc6e4407080628b85d7d744063b1da58cb4b1ff3877c4fba9dc4f0de3713a34b8c3f84ae0a6e8ece855646753a3ad3288f36cbeadcedc882f11237bbc210ec174376f2bd033cdff62acdf8d9a99302e1d5395fa7ae21869a4214051609bcaec289f942ed0b72348c8da3483f24cfe97496c9e650ad20a812574cb0da4f20b81c57b663340bdb31b22dbe1f91086e51f5330ee3223c338a5e096ee8655bcf76b1e852db22087538ebb214758d272023bdfe8aa7647c9c0c91154ae4793c0f0829f472d22e06af0bcc0d6ab5f10d9ea5a781249a9a5454a8edbe4ded4e96b13ec9c0b1f57b08c2627e835dbdcab1e5dcc853c40f07a3eaf9a47c1f4a27a1c92470c2c6b843008b377c1a6c4d144e6452ff7193882930d9928a5efe772909414e113d89eab20b5e15fd1f1533ae42ef529fa1cf9d76743f113e0f32b0bc548218252af5824c02002982c6efc84d7a7d77c6abaf5929057521141882c34fefa110859580617b00a9a1ea66a5d943f12e765d63ef2868793fa82f34a576347e9b4d7b8efde111049860fa13250fff650fbf2b3420890d4e35b41282443f20da3f5d27aa8970ed07b4d193e9f3786271880d859d8be761a847bc2e974086cc590d56812fa8f5cc0ff82c3234c3082b34f1f5b3f0335f475370b3e4acebd52c10408c7d73c6b8f80b7121cd8ef72fd28128f1b8673d673504600b68c573b748f3b79fb322c1273f057acdea55b3ca369117453743aaca1fe8a4ed7b84946832be15328291c4565c0067d6e96575c975c4b785d30269b88a9db1bd2a1bd99282bd7eb30948e8432b0f7a9a380562c88831ee7d7993aecac80559a7519e8394a6252a380cc5f62e620b7da6897f145344bc81220cea4142e9ec08b7b15f15a669c62c98e323a17db4027e8fee14abffb8b9c2fd5497f458fea3ba8b38fcbb398faf8cbbb0064a1aaa02621b3a76310f69c5db40a2c7968ce798df2412bf0fc8f0437a1b6bb3e485300e888a2f23cdbf37761284e416348f8747535dfceea9183f9736657d26e19c9704cf56caeeeace317c5cb61af689b14dc59a512aadd9669f1c8235764d647d9603bea36be479832eac7b0d3fef0f7595d63b3a0c0754d149d84766eb4dde1e8b004d4f68416f65b284fedc0ac09dea4481f4d32da977b2300427d7e5d24cba370e622a19931933abe87d70786a95942426e4d361bbb0caa7843344d3ba42a1e205876165ffae3db67001643962973aed31b11e984e886063cbefa74384addb5d0df5895eb0ad7b065f633dcb0f2864cd6b607ee5c856e3e45cd39b2aa14ccf5b0c0b3d95015020b85c3bc8c4bc5814cc20cd1a112339abbdfd773d0e07e271c0068bbbe40d98c7b101ccaff63757b693b70978a79ce6194ff8da53977cb7408504d866a2e1c393520ff5f5ff09b17f5ad7614ce7b8962c2b7c0c917449b6508e0ecdf0cd3cc24c1180f974733cccee5ea26b4b3ccffbb93e0b2b67df1ec45e0b08e1bd7e085c509dd09f3ecd00f715d58a7f09b899687c66761083a2a0a3d6050c8f30d23cf9c710a514793c4ef92d2fc784ce1d400c5ebd800e57c04683f3360622fdcbb648250fe38255dda01657abadfad2c089bd8f09ed130e43739a53650de5434f36c6383cd56e462e129d799bf2ee149a3acbffc06cd47fc26fa97ecf033512ded28aed956020ace755ab416cab2860c16b4f49e625280bab377982950d850aadbdcfdfffbc2d1608232356758decb19c21d3c395f6aab3c5149955690701cbdcb80a8aef0d619207eb8d3ba75fd8d6b3b369c493aa4c3dd25f8a85730310776cb14b48e8f92b1358323be414769d65a36a2e4230f78c430e18b83fed0826a3f31c964898d74ba4f7910127a4afa5eac500282907512e2ae5054cac9708f1f87a5a638834914b0409a0b4458009b21c450b8dfa9a9a3d698bc287bfcb04a09ff76cdae30c15fc06425b72df539a859ccdeb9d47b380d02f98b92d5bb106fb515b6d8550b31407486912ec43958caa2f821fdc4ae63e19c817d7ff31ba09f22207e7aa8155b375f4e8defd7226ac1541e859591036db66947589cf9ec80ca6f3225c29a6388394c49f641031e93edddd9e78ab04caa54f8954d0ac232d0b876a0748153a1bab6e79d02d95eeacf4c76e486e137c8dcea87b310d5fa16c024b1d33e8396af095bb79eb5a143cbb5e8fc37fad92d2f6b7e027fc66a2cae90f791e0a038f513b30ed45958446a83330c65d3b03258966c6b9c31e9610377b021dce6b38a27554f97124f01e1e1799ffc892a0c65803a572619f9b7a04288600cf9fb733edf48faf97fed0364cacc08b120e5b3f13f777146b0954c6198ea60b4b05353160e2993d556c1d57fbe652d353e87f89a81c1e4c457086093b22250174b1fda92fba229307c2e708c5e5707d99a86169a50fbd6cb37b6af5d9def708a317e56152c63521822bab71165a9bafaee7385782da2edea7ca3464ac69260dae68be363e7f299b8e7fd0261868f8d302ae59f57890295360b3be059de7fb08dff42ebdc08555e7c3784a0f7c83f4a2290e1e1216db9902210c3d9374703c0227c6f1f4a6119fe40ba89cc5855265edc804adbc34fe8d556a2a0848bb7d9a01689c4b5949b7a17dc00a08d5f4c49e22227ab1f678b220366d081606a7a4f10814c816fac4847a16baabe038fe0fb1a1a58341b6f8b7c41ccb1914f3bbe3df0ac3f7226f7cdd65151d0d3f612fc329e740143633a5c1d909dddd37147b8f8c08e906aaed7a96a9fd2c955d6cef2fbe72a822ef459d8445861c1491f18d45220366497d7a9c1476fc403dc89b290b10896e2ae9d5766916c256a073292efbd6049e4ae68b208ea73c850e77c96a5686209791930e43f267b33be950b11b76b906d729c5399331fb0bd15746e8a0994835b9c8aa18cfd58ce42deb5b04957bcc07ae199d86ae4314513c18331811fe423411958d8cbf51ffad4a0e50a1fa85170be191a611e65b38c79a020b74eebaa847b44d3305f52f5f5e4732332dc4ca5209e2872d5deb8dfc86bb6eff8c3a61e71b9f996fcd479e48c2c5a9a67d291c3804936bda64653b7e4c7c64f91b0f5f1411a60a3652d1dd4e80aede9309e3b6ac0fb2cc1341cce7561a885a6513c689be5e6ae015330f896c9fe826611105296e001f16498073b26c08fdbdb22b767ba0c31039dc802ac7bbc92f5acf5452de3800618b44ea3b0b3dd263d37f4b5a55b73a32a9b0c1d61c0867d900ad4dadca96083677fd6bc91d3544e8a1c563ac017cd01dd7e7911202e5338c227024b890c2027e04b95f230bd007bc313a20399d83fb61a697664f5c13252091871d5ad2cd0368a02c7f323ed42684f4073827449bb6e4aea94048e7d37ea6853edc87d4960bdaab6559d10feb13f5a37e1a6c5fd845b0ad166308aa25a2720d57392241e8089396480211b33d450b24b0cf148fff3cb3aa71db1c9911b98a78d0bc81158e03fb1d6ad185740743f68949b2790465efcfdb62ce6e641cd18bd10f4b30bf9403612f51c4a365606619e748c151e0580369a1d8d3dbe2a6c3b7bfeb0421932f07e252e57f52a3d967ba5f068df267c39bc5336cb2ad49f95a5e484492915a8d00d92362e85c8265ca0ad26adfe90ee243ae3c9afd57492b8294db219c60bd570e00e6a32d952d9da317ae4347b47eb80f4196c084432a0bbfed89ab73ed98440bb7e276c9ef75c79f6f0a6b59e612e6b7cd1639bea27efcdf54affde1f385310be680244dc403fd2097e223d5a8c8e59c1aa0cfdc0fac5821f3c26f1074cd8c0f4c01ad15faf2d7639cd97fdca845fda91290148d0f44b4ada12fde26763b000ae42f4cf85b1e3e865d69b3a47740a3a99d8c01aaddb55d489eaf211421581e097feb88f6ff8340f9a593608869a2f867e70d66c1157e6b3e6674b7c8966da303f29a0eeb9f2980ef2935956d2e8ee21615a5d13bde81c36db993a15ce10b450ad7e4100d8c8122f6035469c5e776cd64c86ea3288cef8bd0ff034d66fca9e4004fdcd7e601da980667341a536b1e9eb3a4e56877b398d3bed416514ab9a3030f24d97c80db5eccae2688d1b7c5b51bff274e2557d103c06e969743915f13b708a6e35f18b580259865790881c2fdbfa182485a74495eacebf1d9c8156094f50a23beed77f8873e9d33f2c6cb188e7a3e8a53ba36bb84ff81b482a938b622c1091cc3d38b13e5894fef7c8e26b720cbf1625a9b6d2e3aaa730aa66fdf4261ca0431cf6bd934713d61cb66570d0bc38d47b344c0cdbd4432bbdf095cfbb45f480376338faf0e78f40fd38fed495c960d6d81019dd0b343adf30aab084c04472c0312cb008019b08a3e6f2acb4605b97a609d84fc2dc5f725b40acea2abc1597b035f2f78ec624408fcd1bb8c77d72a8ea85f0cce4847b8a52d9f0b6bc80e9e7b0422a245c04562f9ac2e9598228bec1ad7f4d63056139e7ac3720df1f0cb87230b35a9ffcefaad840e401232cd48fa798bd7d03dc1666f0571c7ae0e84bb21190e309bc1c709125df765adae81abdc6d7759648489c529e50cd88bf7314de6fb08cd0c35be1c6e8931684dad462945ec6b20e3ecf45a665490f488f6564fdb1d01e3287bba6121e4450b1f2b006222b736bca24895da8eb266ef5af7ca4e6e9407ed44fa6d37a46ac76cbb31ad89c096752c4b2ee6f51a98cd0fbd2f6fbf80ee0f63c9b94d2e7ee56d922eb1501743e8d1b5acc7dd60a10db80093562ebf926e03e68e852ed85e83d3ef3bbcbec169a70ecef6227a6d53e36e8d1af9fece1bf30c28d816bc0639aa640453d6122f4a77bd1d748e18631d8811bf13ba7ce601f8075bfe879f716eba259ec1a6d2b08484afb3935897d0fb1c10102927247d9c030789a56aeb6e957267ebd72b1dfd4a2266cb3782cf6d2d21820ef682c4448253033f190b66121c593a10a0cf7c8cbf554846da13ab403116be6c3f75c2794263054a711a5ecbd4a87188ebe8abc9bb89858a85ca87c36266d1a3f3fa837b7f7e0fadc0729bb8af20cdd8b2d00df5678e8b2afb2c2589c2bb10c9553e316671ff004bd06a86f81ddfda057f2e931ad6c554d97d40fb4883eea9f2dcec30bb4585090c1ec0c0a438773c3d5b22704a0af37e1528cd2c64c27515c3f09a549dd1f501b2fc49877b76f9fe8e71c0979357c19148c4729230740c828428b6d30f02bc0b3dca5925fdeff3bc0365daeefa6ef2168521154dba78fd146cb446fc85a63c0290063807780828b804c9ff633b1a25653ee436333d59fbf0423677c9e7bac70ce4e271ad9ad839f0a1f215536cb233ba9c22435bd69951d89db25022830aaa04299d388ad4726e254e99b4165c720c15ca580734670ced019c334ed12de15d0205211adbd648f634f208d38e145453ee7fef4b8839d9d42c369ef857063d620d652b4717f5a3eada6bbfa0c6f36e6bfa597f99880427caa45a2093db86ac2c0db1483d6ca1ae7ae992bb8942df1988f233b3d1a95a35efec9f24a06cadf9c00b50da458fe3c0e0bd394761be26a3de7ea858efada73150033de8662390bf1b08ecbce05a17583385334db6a5fcd4bed52dafa6078ddb5b223cfdc1aaf0ea3904a8b1b21e729bff22b952615d23eb947144e9e0a5e5127d746a9af3d907dd6f0fc7cec2669d1384e741a385e33ccd8a731e741b2f62f8d88a3497260b67616c06cad93360dd6caf204c61ffca93c00b3500079d9e9da28ce1617464504aec66098cb9cd4a9d50502a3854789900cc63f7daae68526c523f3da39b48ca7b714b0167ed91b72ae3bb44a2e41e139321e854cb610464dee0cb814fc49e2f16a5de092da3c737b50c76c5eac57a70dd9190c229d921f085defbad3e6f374330104008fc1410867b10ae28d1bd861b556437b8db55b9181e3c54e6a72a5aa6b156710b1a12b0bd3966a91fd4baa4ffd5e11928906c06eb1e07eb76fe1f2951f221ac05c3875e0899ea57aa150ff0fd7a8a8c03bef71366e7761b4f72e95ce16bd07101166f2bfe9dd38c19c04f044caea0424d91184694f3c639c935c930610d7d970da8d43f1ce055b459fd3652d0b5f19e50469beefd819b41c061a5dfdb85315c04bfe93e646d116e6d5bb088d32113d82123c8c689087d997668d6746af5646d0b69eb66bf4753baf9ee4ca3fc1090d0965bb17f1596a4d5ccdd70c86a08c14e020ecfdf9c55600035e222888d2d041a42564951e230a8bfe1256ddede06c5db04ea16a7016352ba7965eeea0998df91e130ffa093ffb9c1979e8fb69e4b8388010e5352e950b76855a198fe2a2d661e05f0b2c05e787a3e62d74900b608a85000acef78674182de2ae6a665b01a19aabd418a13fef60a0720b202398e27defa0fb1caf0abca769496d35d1ffde349a5c0bf1954abc8950822f06cba429516530439a51fd461caa76db8dd0be709a430a3efd1a58a71abd325138fded6857969024b344468e66659d2a7d220a3833af322632ee8aa1178554ca48eff2fdd51e0054198744e5361226dd176b38269291f02f80f9cd7d2ee13b692c0a975b947f20e9d39b133e92fa7320ec71c7cf0469193d1fdb2c183e7c3f93ffc62e5cfcedc05 false -check_ring_signature 768f9bd295407e7c620488bd7169bb81e2b4265811683454d894ee06bfaaa9c8 0d5bbb97e4292a78a2b38e75406451d4f090666e2ca35fb5df181e0d22f6d5e4 14 4032e7a274fceb8aa6cc5d5511f0967bcfe3ccd92ab3717614bbdad8d21fe47a 30e7c5648cc340626154fe7c4a2b37fd15ba012eada7f24fdd678bedf0bf83e3 3b8d1173268ac9c5db7695ceffb1f08f842c2c2e410d386d687dc77900d91622 a7885179b3abff177c41cceb8595eb50a814180ac8c1414722f5513a5747ceaf 7abdb00bf808e4f4149db0a1a2211bb57f38528eb9087d2e54e30bff6834aaae dcc1986fc812eece8a809d5fb2a01a77a73938e25e16d7100767379020ce3a0b 7c62b6be138c30fea287ae375c1f3240628c90f3747115f4a914afcf2058862e bd9bfdd5528be1c99e6e758ade6c011a333cb2992311ce807e6711092a85100d 1d30fe521ae7de90cd9c0dd1cd785ac450afb169da082221fed9556874301272 62a1359189ddcc0b1b659a74c3e93433ccce04af37b83a891345f2495afc9d0c 24bd4ca3c6bb880a87b005013ef41326c8eef4c3084af11af473584d0024ec40 94e6d4a4f5775c4f783b8c313ef8848b5b4d28890c64abc67d7b0b67db16755b 8788eac62628134ae67cd8a4204647a6aa40c96c4c9d9eb64f37ee0a0520b779 da487f260719f5fd1992019aaaafd885550bd0ac0ada573cafa625b937166373 2c072b57741717c843351276102cf3a71dcb9c42f37a03738e2e4d5205ff837b91bac55d765d8b85cb3c5bf23b051532b8139884d590cf2426ea8d6adfc5760a2aeeb2018fb8d3ab311421d9f8a334a66d9d87cd4c789e6962003bf346c0b20d91e0c037406b16950f437cd794a5ed943396972915501de045b319040f08a506b04572c316ce493cdbdcd764c2c792f41d1c99d7150901c6689adf9988c5430a99a1924e507efff3c015851127a00074ba1abf9ef22e8bbc418de801b628a3e4127f2d2bdff9da01f7aa81881af2da58daad8a820ccd8ba967281807a06ace0f5e06eb5758b9a38f2f104caf1fbf04796b85e65ad767e8917d375e491160ba0d555148e916185b006d1c69c839eb9a032ab7961b3eb021b3047dec9a02f8e90c8662588b230d9dd4038858cde7f4b3696121c680bd2c5c733a4a70fb113a680b8a86bcc0d4852bb1a54a133072481e897aecf962eba32f9fc0685a6382646700e1eb56c92aa5561eb72f189793e4744ae7c3d9dbc7e991419c2fd033d7c28904adba1e35f93f1936dd8e454304d37d142a30ff5042ec1e2ebde4f0b3fadca805dfcf6d12f3f82c761a493755e037596d4585e8531158dff712519fb77e55e50c5144e1b4054672c15b51513fef712e13082116b9a163bd5dd07c140a6afbe50c2dc23f01827f847c119359764bf4d74043605053cfcf527cedac39a794ecac0ec3a369a3cacdbaf48c6cdcc62cabbefc1f0737f7165e184ab97e5f1b4817830a80f787d31a0948115af348969cc952933515e69d6a92721fe8df0dd33428e40d97b7ca3e9eb48cebaafab032458178274e1c40d8171068d11ffef87f9b9b6106b2a2581a978576fb6623ce5aa0e7745b121b02aa312b02bd9d3f51314394950da80edd5138ac2f1abaa995bb7aebbb9dd65331c09fef6587a1622ccbd6d9db05472b5043defd93c84a0972b684e556ae334d05507dcd3224ed439d6a387f6c0f341f49cd96888ef25f666f2d593491f3017aa04757dce282975dacc8d8c23905762c4871b52d81158163f4a7363f245c0a7081374373da4816eed771bfacc506b3fe9c69f020775abfb97a6d91ad073c659419bf7f5fdf9c04baa8ff2ad36e0dd186605b7d81aa4b3ca90231b3a3417d0120e844c8df8c53208c8593ed7b8e0ac83c1f5e60e2fec85f705e75a51d480e8d056d87d8c282bc43524402d65d45010242d1f91086a3d5612483a2704caa171e1d4c309f81a098a0f6a4bb210b2506 false -check_ring_signature d4b797b9c63f8572b455bf038c1065babb278290a60addb318432b026872e757 4feb0d53a5744f0790b7b5b2e2290b108bafd1311a5639a065453126ef01125f 1 4cd35ddbee8ae48592c99785d9f68077003d8db6e8cd1a12fdd0c2dacf71e1ca 753af6153735ca73f2141a9ca32b5143fa1f6064f7d64a1772a2addc5b503d032cd5f42a9a8c529f3bb13abe6d7fc9e7b23dc4a02d2e306a5103d1b8f0949403 false -check_ring_signature e9a939952ad4b421cb7c3bdee935d51ce61d7263ff0d2a0622aa65ab44ab294a bda4a8174f3fabdd96e69082a319d06f75442a8892187f968388ca80394e01da 9 3b92165d2fc98dd074ce0da90f67cce4f833f42f9ac1442c9ce24b6c69540f31 bfe8bfaa0612398470656e55bd5137cd3c60ecf69b30894cec2e7cde0ada8c37 94e62d882b0d607a4b986be0517e54a64f04e19555122e3683c890b2e0215216 8585b663263da66aba8cc38dc453aeaba5e764c5d471a9c1e6123426307d3ebe 1181c74575fdd1fee856592c286e027cacaceaeda7acd17f5c783f82b4ac2e17 fea2729d6e67a6ff3721debee04df6653b2687e55bd1eeeb9a41aaba8aa45e26 d0f77dc97d0e7fc37836f3c70d771dda28e9509479eff4727969efe92c28a482 e01d543543e19ca5aaa80cf39dc499abad8d5bb09e56d6de8c0a748db837e943 7e27d8d0ca3f4ec2576e282017189eccb9f91f09bd478c2bded323f81feff2fb f2fb09042220c5eaa4c84a3072239c5df8d487ab1fd6e678d6d42a22b755450dfcd97c05512809459c6971aef36f82818d6b1d4e68b35e2fbb88cd3674cf6a061d3a949c00c293e9219add667a528c000dc993a0eed60ab6d3bd2f597760d2070070d9595db0c7e0e3a9737dfd3a1c1186ad0dfe9ebeabe16e2b25480183d0003970753e9100e7a3240fc92aa66aa0420dfc0c6f5e38481afea4de3f2fc4c70767e84290b632cd28b2c4fecc23799517da92139127f7de82a7f38884ec888605dde7eb8c44066a9202974c4e393e95734ecfdff322418e3867e93259efe2c103b0933c7043933135dd1511befc9b44b6b291fdb1d322d620387efb6ac741230f126873478af78fefd65ac9a2d05c8ef9da271a9d8fa4cccdee98c85221441101126edcbd9ea22fa22e4d60fd1c384e0b8ed6d1ca00c0bb625599d1c3466a57036b810b4dc5f60b9095b4662c010688cd8c5df0fa6b612ef44b31f7ff058df107065fd7738381c4249ff10da025bcefd8d307045089d652a9726f38a48dc44a0ace56ad9e4b747ec5a386714c1ae0869a704d643e1a8227f2544ae5838397230cfc01052b014257ec674cead9299c7dea6334a3e9821133c11cabd39cbcae1c0cae96b0624e0f7a3d198334f9af36c26922e3ef81ac8e0e3a5a3995db34fa6804d3b4f8463db69d82dec399be14a648077259d1b40dbea7706427db6f3634ff03a86e52ed1b419c6635c8f6639433afad56059c3bc67ca3295f1923592c7d8a02162bafbec5a20348d871de1aab6f0eeb04ae96ca0f83592c70ce0919de690c0a false -check_ring_signature 728920b1610e43f7c4aec79245009ed2171c82e9440102e5582c64eb7171a89d b2d7ebe629cbc0dfe01b3ce7a41bd618a7ee2097a0c912aea62feeac9df5bee5 4 167b38a0c54081c050395270390402468bac3031f3a4696bfa9fd62ef579ec87 cadf37e279f0c67cc639bd886430f752f59350640a80d7b6075853b28db08f87 6377d5402ebe27b30fa465e1d4783736a1f9480f5cb029bae2da813cc046510c 22bd331fd92bd184d97d8694b937bd133a75e104c353feb0145288625f585aa6 794e2fe398d7e040a4083949c95364cce2ab24ad382e87e4e842edf614a5a006b276c61a63f4d27ee09c7d2527370028fe5f6a0773dc58c2cf2080725315130825e1db190af69b0edddf73c828efcc7d9b9e6082dd57e1e5be7bf58f17dc4a0699ae811897f08602152ee631888eb6104a47b6d3783d2213fa25d93c53a4910cc0d53fa7ba9228e5efe49974cef1dd57cee1e21994b4187ac6a47be61a89310805e0c6d608f06b3601f9f8be1f05f26647abb1065a0a2fb91a59b348a2a05b01beda6aa995e0257873203f7ff235a113e0603996afb4c223309565686335110e22097b94072f107464f7892f38f972796e95341d4368773e8c6214360bcbf80c true -check_ring_signature acd4dd8537a944a38a652d97bfde716e3e93ed5061907b536f7a3ea51122ddaa 27c714d709bbb77feef1abf330bbf3581ec99d6f6c6b9b760e1d8015a89bf99b 2 2a3036bcc035f01df2ce4887bdf01434220fc7a50aa434e426e0b286930c90ca 162fb3e0c191f7ee7a9986d041c333a7ab1bfdefc3b8185822995d500bb0607e 3f6ff4df9b33df0b90c92398073b3bf6f264200b719512a0268c2c38efe2b10301dd2c1768ebbbb5be33d94b88f6dcd7d49b05645092b7abd46d41973d57c10fee7c9014db076cbd2b2d86d155a78677a08a7f057b6b3aafe888904e7d11510997ab53f62007d7a0c9f33ec0eaf941048ea82d163d3aa16b06cbbd6ff8d721c6 false -check_ring_signature c397b5ceea172995f0a955e68b184de45570b393f330c395c2e0d769e7715c8c 732b431ab01ab3b57bcadb3fd402a8b9b919c64b5fce1cbe5649b5267fc07699 58 2a3ae937fa2f57a28eba7c32b3810b7c65dca6854b7e36a4112488699b8f0aba affd9590d01d7bff9ba12ef509861c2462b78bf29567204bf6bbdc5324dc8d99 38f1ca591ed8827ac06a219ae415c9ae4df5a2e5f780bdfa89bf04792c047339 d707bac733f41e13fc5bdb4c464d0fd907ed253c776807bdc0d3f45c53f439f1 bd777f798afe75394c1ca08d75d572d6008ee5ff4027a78bb6814d22c0c8ffbe b3cd3a81685343234858a9a5fb7a04669ee7e50e2c5adece567b0d5d9cb447b6 746088dc2d2829bce8419963e7eaeafb8a766282d36e07314e6563a234c85864 967cfba782315dbcc21677b001072498f4c52f8afee2c1b2a1116e548afeed16 8495e3b774ae3ab9d8e05f1bb5243f3438a87a0d60bce95583e354e6733381e9 cad098114f196454439220774152dc6c68b09f08e7598b56e08fa03183225a6f 2cbfb14d0f7cf3282b73ec8b021f6594889a05e0172691cedeb7c6c8ee8b2be1 500a3a603b5ce1b402faf25128ecb92c63ac2848d984cdb36214bd95efe8ea91 a49ec87799bcd1e874813998c37651e01e4d1d1ffb12637c0db1fc0a0996a4db 0536de0b44fee68380bcfb71199547a86347cd213982971515c685160fa3f61c 771adfea2951abb04018f064df5b6da1252e213b89087510c4577c03211643cb a820f6103487d55a71da4ffb5ef8377c7cb4936f08576991f8fae8a75b3f4486 9bd4776d533aa2f9f7b55475b978ce5e2155b22521f32e62f0aefb87e329331b 00950afd5eebcdab93108eed16ccdb6fca0ceb854a5cb294f13afc511cc44578 51b9e2fc605aeb36921fba8c64156f287d05960aa4ba4d62578dabc699e191ea cef53f09bc0997438d22bf7ff4134d0893abdcee893c91c14a54c012e9a284c0 f699282b8cd299635af26f6e09c9df77f3209b41e5dbed388f6da5a0fe87defd b6316077dfb6fc5dfb367ab69c282890cfc3129cc3b2d9a2acf79f90f21d5c7b c5aa8d86b18002b58044ce731664d56dc55c3871b3e0362f643ec1f680f81a69 33f75a7ad812f414d744611791718beec4ecbf8f1093ab499ab98e00fd0f1435 8e2a1c962731b9f06a4763507a8feed4423cf37069e21ef5323ff52a3cea2276 648ccc39bc362a56f3086bd4d0a9afadad0953b4f8d76ab127248b4428e3a87a 58e548c386afb168a12697560d572cd37b7d15ea687d3037232898d3bc68cabc be50e56904f317b2ef5f6eab5bb5f46a582d2d9b539a44d62541c2a35fe849fd a490ffc6067fdcba8041600dbe96a6b4be192243e145845585b6d07dd382958b 1bfdec4336b76d64b04555c4d07cfccf998bde8821e70ce6b57d11977b3263fe 7f700d10f92a3bd909e62756677a5bdb05a98e1c1c5ff7b36c61908c67bb1592 c97401f01dd322a95f60fefac96918a7b101e6debd077460f7926cf5f6318a44 4b5098882dc9120534544726b682cae3de119cbe975961c03596184653587e60 bd13e87b4fce559722e43e046db47313d723642935b3d071712e0b2523cf422c 0437c0c0cf7584c205fa864b7e979fc6baba463e42830956e46693c5ae6967f0 17807798037ffd0d343f46831ebcfff041bc2972a922a461ebceaee59b688a1f b8f94eb96515739514908c4b5e05dd9af5b0a685a7d1227f239cd6688ec483ae 21c347ce196b9b032598ddd3f0d8d2642ffb7cb45b0604fbc92989044dcb7f05 aacf766cdca6443f0cc5ffb00963d96d035d67b032f93e8cd268806ca0dc4705 4b99d63959c7b0142eafdb3003562c17cf5881ae31056234e95b4d903826ced2 f0b2fdb55cdbb4e79c676980e2bbd79be80327b56b2bd3c7ed94ff5b20708551 dad31e4f3ba8c86a714d14534f60aa45f529b6ee19711f1e4f2b30208f78dfd0 0a77506c874a2a707642e9d61acdb1b18576a309c8fdd1e86b77de3e97db15c2 8fd90592d9ee34141be819e798aae7134211969d3d69c06b96efad98f2a04d83 de560ff497d7776287d27aff8d4179ea8af09a6ea3a901bb32faeef39ab15541 f82ae2696854c50c101aa4002ec0004aa554cb026ab992806c1cd6745a542356 36ab6712cf615b98d65d0986b4b6f8500d7f9eb94a845f731b1017af4fcd3db5 b3003a6f15f02bf1d5958ea29d5f5019952fcabc837cc848462804ef90c21ea8 4a1f4a7ca5c52ed95c06d635394c560ffc4e3c23f3ae50417d83d171048091c7 868d26496270d892f994eb4e0e9935790b1b7f9e2ff274ef616ea199919aac8c 653ed38da992d9ed05222eeab282800ca003bb5746b96bac004c5f33cc6f9184 1a4937180b27960c4a6584061564da47f755b728724e0b77ff4b27b4a0c2d148 7b370d87a4864ced763df999acb26204aa67b9819a2fb0d7314b7407c6f8c10f a7e4aee9f002eadb3ab757a8fbcb4f7a86507aabb2ed73ee28d7c57589fd6089 4056715cc30d0d673c0f5d8cdad605abe3a6382b506b9929962d3510d2f9ab61 9251664be97efa663c9e9d4f64548e3034cc07ca7c7c92b1470a720ad06148bc 5bc44db086857ea782ad8fd977e7c98be1dc33f5934d10b214f7765d934ac150 0541a41296ace5323a6415e9e91f8ecaa1630390ed3c7fdd74137af1f99aece4 6c3c07f97a3b8c218eb2b7af871fcb908ee0bf49ed452db205e639c81e73aa02d194d321b5743a17067d3be0b9e5209909ef11efbe68b7432d63e94b8f085d028434a426f27a35e758802c0050e6e794ee536b825443165c271278678cf06208d3474632f980712cc38fe6668914ba9bcd96a5d638d41b3dbffa41dcb6cc780cc206e3eaed6c89ac11fcfbfd07fb2214ad1851d2071bfecd4760ff053e786308453a6a16037f4bf3629decc45c7fba4f6f4a685f796548872bbc8e6b80dae2061fbaba3fcabab287b3497ea7ebc6135de3b5daf93a85189e3beaa0e150ea39006bdf05db5d1367cd767b6d228757e970293b0cc017b36b5cdf35e2a50ccdfc0190fde810d8dff1191c13e49de1d82ccb507cec5876f58d975f892c4de2c0130c002dfc807223a4246f5c3d8e7627cce3e2dec95c6d4ad7bec5ee328ca8ad3503226e4dd22dea7097d615045db981d766000803d3719f472b5baaf3fbe0cd150a88a2036ddc36a1f5df7992553f6cd04a35d63b12d851bb13be833523142202036495612eeba2c82a15a6db64d6292cacaf33bb70898eb573a60440cff8f6b703032181e93c8f4629cf94d12198e94719f923a6e59233153a538b6e0fe62e910451c85cdedbcf66971d0c47cfc25b91365f99e1d10882dd81c23df96f18fcd002507a8e42cc0d19d15c904e9454452993878d2b3ea169dceb0494e8ddeb417f057a658f2969be7284a10dc6f484ceaaa8aee2926562483fdf42ebcfee43a0c50f8b8a417a03299ab3ce50e0343ff37f08f70bdfe5370034c3adde03f4f946680d3f52d352cc139cb3d6e48384fd99b12c3fe93c1508f8ea1cae79dcaa46c29f04fa90a64a619f6873a05eb769265b19b7598587b974ac747dfb6279824c3f1f042c3a9b40052223207b9f91333b3bd97a1b971f26a4bd715da5e0e511bba62709b49a015fc9e672e6bdb187da4bbfd785a7d833b0abc22c926c19bfda10b2ba0ad5eeed5cbc457a0c293ccb30b22bab354104ba5fb61d3c979edc9d059a05790d1455331de12ce8eeb564cdb915e3e1826eb7acc55b3b6237cf0ffd90c4803f02aeb5b1396a301b0a760fe97e5b25b123af83145d3c6ca9bd82fa7234ed0e0c02b84965e5116285a4951b2a167dea7dec35ad0778f7a5e26ac85176c9cf66900895514c2edfbde573a8634f208f0b8c8f56c272bb4028a770ed77fd109653cd01598539681b1f3a83ddec936204d0c144fe6b63fdae282bc2a06aa70ff9a5a70005012e107c9f19ff4705a265a313293d35e276fc69246c22089038a84a3dab07e74b23f6e3158f6d91dc2453a93a63adee7a26fdaa71f6b02f30daca5d3de605f0bd9cf850e202cf459e35f3a1ebc551d4cf4072e0d5e027bb8f082acab4980cbd353b58360a0b7d9a4ec401559c4026b651ee62fb2d492a8dda488e3bd16807d74c1fdd2b98a53ab4340fa368292d4356d917ec2772ae6fc60a4d6b75981d02647efefbd183063f3ac246e179bcfe2827279186438e08f92c2e5e71a2c0720a6d38498b41e9acd3433a78f825d19fc1e6abc3a2a2977fc07777ccf079166f089d0fff73a2523d3393320d6bbb2fd03c3da7167c182c95156feb0f35c0ee770a3b4d6900290a49dd57336884fca3bbf583f63d474637a7e2d114fbdef129d20574f929f566cb2e3eb8bac65f5c3c6efeed698f23c97fc6d9776dd779859da10fc6c4c7f3cf42cd6f0a38c5153eb5361969e7b2ca9fae60052cf5f842c19c1f09f240869af8b94a53c0fe0d03aa39432d55fd33f9565e24cff015487eb5d34d020fbf57bd2b2d8785689c942dc58e730f18a2e39ee20b21c7e648c1c461cda8029ec958d2102d4f344ba65946e8f2d9e142f3c620d43b56ae93cc226635f531032fe7616b46ba557d70f16e456f9b0764939fdc799105b3f913e0809952769004ed944a6e80c188a17cb76c5361cccbeff897119411ac2aadb1e9c7cbc7c0590dd0e2f0f7885d38fe01a5adb4807ea650ae4949bd657616f0d792fd5317319b0a67e62aa8f4af83e351cb8e82dee6f4b6ea1dd5c9163943a62b8e47ff3cefb60f3c4841d9238c9cf090594d4429dcb0d6e5d4a04c3d07a27250c582f8dae2830d38e98ccc4a50d1da010a7f46844ae59040f6f3efb75207d3892a0573d706cf0bb9f07e8179d2c513e2c83a922fbc7873847590e17fe4f5f5f427b4e746a9500593baa2eb174b00bfa94b05b3864535785b525228cc638b05b3fa8cd0d106d506a1f827621f876721ef712d833e800a09bb284eadd130146dacdcbad1f9723306056a10042269aac22ede773384a7878bcd77b3a3c911417737fe8b13f5175905984f509888bfa4a10845ff1e9d63f90ea0cc1b45bc564074a25fc3a29026650d6e5fd6eaabc7f767502ab60ba62023429fc652d0035933650b005f2bc38482090e579bd582eae3a98ed7b04070261113599643f21577adc7b1262ba67ffb3b05dc991738fd51dc3262671799b4be8a6cabe1e03e7fafc640630c43542e1da40d7c2f3f546168431c399ec98ff654ab514ba16992ad6c10a8885a5b05a09aba0f6e83224a0591cd6dba70d6a0f20d95d8da220e3572c895f390e1e2a52afb94054e7084a5276a44fd5e9837b251753e6eb3703c63130bd76e9c69b9305f1f4e087c514ffe23301c885bb27d8d4c42005d567469ae4de302c34cad31a523cfb60e7039c4538dcc7be12f765e462da8ef25ea55a31dcf39d37bc0c64f294e05d70d86c989136163bd1e701ecf96dd5306cda94ef886522281724cecbf740db6900145aac3360825bfffbcfd898c5cd4f1dce53d93b777bf54f6a0b54974a092260c53ce34a2b614a949904f929a6c5910be2bc12d63ad3d0f18c934042462cb3302b1737651feea9fea39e74ce2249aab5da6ad5cff1798509e1d5b24c82c41ba037e11eaf8182f6d70e69b430b220f8f367099d8dd493b76cf2235044d0df41b092a3d53e0e56128a24b41cd3c86369bf36174d90653ccb4bc0824a8d1bec4350817ee370b19900dd781e66dc4f33cf2d8e7b52f142aed9c470dcc94c1997e6a038ba308b11bead1f293874abb26520ccc5a6907bb6a940ec238ad6caee7a5930b9b1ca9336b38c812a85a810eed180045f4ba1e501722ab97f146c78cf8840003b92737ba92d1b9530e7c36657a47e04b37cd27814b9faa09615cb21507472a08c5ae0767ec5a92e7c5a0b2694cb264a6f41c98af0b26953ab692e03e8fec270413614dde79e8da809262430f8488a95e14e80102cc1fa29ba25f0148a9cb620aeb8f24d2aaa37bf4316d2633e7276edcdf12dd9035e75332d96d3c47a0046c07e41047203ce95a28f705f4707878c2c322daf00bb9d5aa9a23a7af393e1ce703d8a22e969eb034e96f28a681bd8a42f1089fd535b01053178e0e81ea62a3c90683f860668121e1c0f9d274aeb5d7acdd4423a7c02f125ec20fef7c042469fe0ea384cabfa8c4e232c29462fab9365fd965fbd0192a4b267612a768e3f323f80883a4bedf7366b41a85d2e582fd930937f4fd79c46479fb130438c021127fcf0b23457100ef7e989a8232f5dd1389abefb5721cf8ec5914d1111ba33f5170270bd2196a94de5e5105ed01493c6f359d336329a6629dc48aa0a38be968a587dd081eff5d4c904fde610043da1334353bd7bf48891c9dbc03e57ee67dd11cc7c60386d14c48892aa9e308fa54b8e12de3e1a623413f9a11d953d46304f55ec9e502697029f977a71e17a12d3edd46520c864132b4cd24ddf9267b356c1f6bc46309cc16900f0d22492f1b1f1cfd8c8b02348a542646da4cca163e6ecc5b71548406b607dc4a79c507a4ad43a3096fb6d1c35d7ee895dae27f9c5ae7d9558df0c304013e24e21983c4d5991a0451c501d699d4c22f64b3049afaf77b67e49f48ba0a71df83621edbfe12ee6391c0ad26a80c3f56c83ca4bf32cf1628a515f33f050b130d1623aecfcbd8818738100c9f34032d415258f2373d06293742294261da020cb7ab36bd86c0c91e263ac86fde2c7cf3172795a8666be6839ac85a53305e0fab9b9dee9bd6dd0b4e5da3c51ce9ede46cf4291079701ce8848841d97754650f366ef9a2b58a06ffd6fdb324c396994e213b1dc807f1820ae5e08f2fb4ef1106fdf8f70c74e25288b7faba5fb667b78463adef8b7a4f57a7fdf01391e5980602e8df2671fcbcbb94da8ef13a5a7c54cc42d832eb76fab17bab300f57230ea10199aaed56459abf0c5cb6b183d8495b7e73473b09fa4c1f41832948f8b1a34b0565a297146d355833540200bbc22701575411f848a8c6af02931756301a8f3501b33962fef2e8fe5bd740449ebbafa61e2159318bb79e5acf097989f748efa904d0f553676bdcda8892c88f27a1a0a7155b60e7a493349ecb48f8e684d5209402c4064961070f80becb31a07a65b1de1160004edd464152fdbcd8fc273e099b0832982ef0da6fe3fd23c694ccae6a0318990455b82119d44f21240b68ace4aa07dc466264ccb7f6b3922d683ecde720af490e8bd56852832497a31a1234285900cb0092b5342026e3bb17faa3356090ecbf0381ed5531b9898a215348fa50f00a1f90434c8ad7fdb13a801bf3572040a6fd110f474f396eef96bf277e1d42aa0018ad806df5fd99c23a0686373df65e1c8d82ff0773221b83238b09710aa32607a57f61497512c98694646f92826a140b2352b8178b291ee55bb8ffeca283e10aacb4ff3f8fd075a200f82272226ecd4aceca2b369f0de9a1bfafb8ade830e70dbfc8a9a73e867be1ceda768dbd431b8ff639ad6fb671199cd96fcb9826a8600aa4427ad5be8b4b4af55639bd72ed845c7895e66609d3bbbb13bc87d68da10806dc1988342ea1a564a58c74297f7d6b6d50f1b2b615c0469cbcac4c6ec89746045ff595cf5067147dfb586e3a647bbf9b2586db4b1cdaaabd0bbfadf8ef465f0184461f6e090c2c31451862bf487d53f0188d797055417f3cab9fdd1c764f100180dc995e5552d99a2843b419fc54730fcbe2a84e0f3890692186796e4403fc0c4f2576106ab80cd840d06be676e0431c19b9ebe6141320c4684329afb69ebc0f3334405efb2390c36a27dbfd4af33a83f89d2d9b2500b940a6b971ff5c74de0a454ecb133863502a8e37db9db300c2507ba4845129dd7952f8278d57087e9c020ac8aafc1100e9d8b9cbfad041c5d61b758f06e4b3a9481ec188bc30e4981205 true -check_ring_signature 795fa61a2b145dd7742f0d619e7a378c857470239b1f2ff90329019d23d060ef c86ebb618570c7a1080de1b14a9a7f7b53f0dd0987165852521c1bf5259190db 1 ce71df83200fff0cdfd9e39dab5b14f3eaced4c74e99598ff32869edd1d38afa aff55d73f92b11e156e5ff00c6e1fd31d70b1daf9c5bd341298a51e91718e10e1260fbee8185145e07be83be0512a58b64702a5a63432858e28223e57676d70b false -check_ring_signature ec787436518054bf0df0bd3e1bb9542db28dd09fb60a407b1e57881d58ca19dc 96d3a743d45e54d0e7f35b560885107b016ef7d8f58663b81f913e4149b49812 1 90031c55ad78a182d2fcf052a7f6b24ded57cf82f48994731ec9bcfefd83b18e 7b5c75173557917b62c259a1288e09a8b1682070b09d778fa7ce4835a1080309992a1a68fb4fc8d133db23b89b131e6416c734059a6be4687cab30d967265200 true -check_ring_signature b02fbfa5e4f14108561c713a8d60972b21bbd739ddfa7763afa47b3ba69923b7 a32ac790defb9280e8514af441869f6253b5a38aa2b7877e6120078190cb9b57 1 c27a729c2b620fe360d1d4f9f0f0403417ea6f031683709622efaee3a482b32d 33bdf893b8ceca5ed4f79d9c2afcdf8a0e6f4c28420af55af8863c4d91f75201e4d79486a8a5273c4d7c3096e7ed6af25684343bf77f8cee62380a8b5650a4f8 false -check_ring_signature 18d7f068ca522ee0546204fb8eec961b2d7854ef549d8e777ec60e82952ee344 e3c34ed2f790b81a81b5d48ecb6c031ff99438bd225943dba053796507eb9698 4 3772017ebad0b97b25bdf2de75d5e3d732d79a6685c8bc29b62689ddaa8d71c9 c984840c76ff890e0df999035d14c2aac37fae0a4f19fe2c65be8cd38346a890 a93f8a4a6d73c81aab706502212b4d8bb83a2e503ab10b1175b414f19bbc22bd 859f2bfc4eb869c0daf71fb14036447c31213015e7b30b70fd20b1dd2cf1a8b7 c10cb8c2fd7cc4bf378c464c66ad794a36e8dbbdb3acacc7e4e4810d9aa33c0f6e2fa30afc4a0439f3d3ee1d48f4d781c44910486a2a7f3886fa5d19d8ca700b1b3bf75df342883288c41f62a6f256774ff8e989648ed8baab3e7b047469530ad535d120d65c1c87d0e14ea3ddc51c24a9581b187ae5a1e323bbd4fcd508aa031e383010c86ed5f6a02e9e6047bbe1b166700fee44348b7b874cdf910ae1660021d6bc5b8af92f675b5dc7ec97c596e3f50b27268becd58ba59d5b0cea59570b7f03b7a495edfd19e5db4a1464147bf012d361093d8f18527eb5bf0a3eda45f99da1e3233809692e6e528a611206557769a818bb4922ef252c570ec00e5cba03 false -check_ring_signature 511759551f25d100f1f557f0eab32275c6a8fb9125262831f0238198f0c7f2b8 a04385f838dde4ea2fae8bb886be9bb7e243fbbd46008287ec04b4d2c561c842 33 d1a94a8a77446165efce1b68c9b83c7c9e2f3f1f39ffe05e373fe524456f04bb 451626da8f6cf01b259aa7204f13f1f70ac1a21f4190d7581934a08baf53db11 b577942ed8fb17a17631730bed324bbe74bfed388110bef4cadc7846a4723d1c 63271c04fca28d0a34278220627956fd40659a94beda3b69fce8dd9eb68e9954 86f0e610a8651082ac6ca3b0763e33d4dcf07b1f56b4e0a5f2eb1d0483d9d188 3a5fdaa55ba84b9164cfce5210f8521321ffa8addc63b78ad0148b4f2bc2d7ff 3af94e1a170089d2bd27f43b11688895b084a1285ed6250c123cac4ddd1aa952 2c87ce303259943bab64490fc6e7734acec393c06a21b5851464a87e51e3e154 409f507c58cde6546892be0159a45c7c59bd5628ff24691cb54b4330eeaf221a 71970b64a01ebb426948e7eab4c48d50a56d18d988e48e675031d5defbfed813 6c25c4409b7ae0ae135d84bc034ab671aa057f12a96d48b61d9d785827ff457e 1f5d772c0bd54dda2ded3dfdb352ff4dce7697a68549a7f7c378567a86a0ef29 0c833f0b00ba3b96d9007b55b610abfdc54650b607ab5f89d3d242a294bbdca3 1e587f7a389309d8358911da769c33318846a938d75c1cb91956d88c1d157393 2be0a36b52cddd008a1d569f3b6bf6fcecd57cce3212ed074ae0f803b8a54775 3fb9a9687a572e34862fb1f79e484cd2998158144ba7c39726d1946b01a107d4 7444f93bd443b682c991d28c066c1fb3b748b780107bbb61361f8a2398aafc19 7ca000404a61889362b73a4ca594e458ec23f313d2fae644b9a3bf574ce8747c 558d2c405e051c6565b6f2440e62786edb7e8de138899419922cd631c38b42bd f8911f804e054b27949fc28a92044801247bc57597d123969c2bade557fead50 e35a5b0eb400443e09b8dd4751a401338504bf727809aa09f5f77c42f299284c 11a60804faecbcd5b1dd24674cd7f5544b08cba50d9930aa5698a0a0af73eb38 19a3de9c1693a252800f8a20f5184ebe07b125dcb731bbbd3d0f0c2fb9419cbd a9b0d6d052d6ff18e1e2bcb51ceff12151dc431074bed69ce50ac4ed74a60d92 bb88a408f255657291b2f9257d26c5bd3982ae8fd4adc0050eba90a710363d30 ad6a65ed319de7ac4d03c2a3d0b53d3968034754697b9580f99dc6e6e37939de fbeab3f6df372ff2271dd2e77ffdbb5420db5e24b81d5ad31495ad500d815874 039655bd9920743626a4a197b7b03d4c6beba96cee3c3848df8e2ab8a310e3f8 a75719da7465ee2825aca3010be339547fc25c8f3a1b582d850375cd735fa6a2 7e192e1d7a015af3247bc1cd1358b2b8356c24472eda851e224b957d1e250357 4764e60f81dc49dd913496253065156ece29e24d712c1f95ab95ddfe2075235a 321e1ebfee3914614ace179b0666eed064851e59f791ab0fe6076d5b42a3518a 3b7192daa7cf35e3ad61af04b886614f90ea4c5ca63b8657826004086244198b e66e8df66893447d7dcedc6da4adee46864f13a97b64519427a857873617990cf710dce0b4e8fa8c7dafe340b0e948eeeab00a9f7c945632402ba81d2502130c3a48ac08853a370d90de62edeedbf6243bb1a7c1db7755e13d7dac4bc392180bea7e7b38401b724d9dec58c3f118a0ed5a8d9c0e0996a7061ec74f32a80b1003ca422fb699cb96b1858f1baa67fa837e139681ae95c2960f8250ee9553b5c30d034a91101d015fa617eb6da381cdddbd039056d18b0b8df43981d9688aca5208022eec5c36b8e55b900212d70d06b928d5cd768677f9be8ba87a1cffc6649305459d202bc2e06e9387565c2baaffc59ec9e55886411486e44da4d581fe923a073a899cd169c82e669b36c8e58ea47cb8ebcccf1c5b84e8bd680d6b7f013aa604f913efd07da55648465370edf19380aab7bbb5fd1d0cb852300bfee723cf9701fad74691b51cf8ddf3e6a30778b387d76e4b41ec9ba3dcb68f6b61a157ba0d0561c65241326f707c54bcf157d86c0f27b58a29569afd51dd8843d9b103c288085f2b133bedb1737fd7048b4d8e16e78633a336213e23562bf643694be20f7c0a19f049c71fbbf2297cb799c2ca2b16f242f4e4e3e7bed34826743d7540cdb906ad89aef004f3bce9cc0d2b0dddb75e66cb8b873544748bedd19f05b33e04bb0d3c9d71babf4552604b747f4bfd547c807eee6525f8d3293e6f8cbf04d6c13c0c1f335714fabfd7cd06d8b11f4ebf0d9dcceb52ee81407181b15b22b2fc1f820f8bd8a835070afd323bba835227c49bcffb6c0d0585bf8954b5a2300cd5bd2807d4eb309ff7263fd7b9893a06ada9a9f93d4495cd5cf65ac349ec6b8932306609f68d333793de35c2f4d6322d40afff0847269bfe85484b6f27d4ecbc7b04ce0dbc3edf679e8de0340e4c8bc9d12fc4f7878b607fb42f4605f2a307b03f46050a26528ac11bfe2e3ac93aece5803c3379af6e8ab1a1988e6d91f63b454a8a030390c9311a2ea81bd3c7947be8f5c71b9d53c63cb392de16420bd06e627c95ea0e2b368a05d5814ac2ab31ded41c3cbce69a20436b4e1ba447029684ffd3d6d3025bf835ed0e4276b789f544b21255795b6b4572103b86e0f3e582dd82ddc0710e8c5a86ba04e6d97485e5640c5ce81d0674dc2d89994d2b30f86824d59c621b0c3a15fa5b87feecf85f4be147f5ccc727cccb206aa97e3e886e0bda602df1b5007122884d8962e5fe2de770eb43da86661c76ebf63ac1cb1fa06e0948c06bd300797484dc1aed39c97692c29d4522a2dbd65b6577e7c9c315503b2b5b31a7010bdaa4cb5f8239fd5e315d1be94bb6e4a75bd373639ee97678f18c4acb198fd301df21eb20e7b55dafdb606939233e33388f2670bdb5cd349253c0922e2445340e5aa38345e5f0856105cae290efbe1709fc2f108fcd5883fad56c1712d4b0a4049af7204a8566d43eadf7f803174e1c978cb300322aa682a1ce7a581f25f71f045794fc33d596565f9bc8e348750be95f7db8bc00b8994fad63035bef94c1bb0cba2306d18404e2f92577cc4a4de774309d2a9e827fe68b845538cb0df9833a07ebef33a0a019e0232088d8b9091605db8b9e1bee51c6069d7af0394346894107aec603458f34e25fa6473f15255c531d034436fa63701d55e79ee88e8e0761044b61525706ce223e9191cad0deeebb5aac225cc7fbdfa4e00b1c85c1637eb2092db4a03000b881fbd7663b51a08974ead8d628143e381a8bdc6dbc9fe22c5305ff2d95ab829b34d4ba4080a75261f441917d0188ef1a9272dd3a248e432257007757950e0e7714fc0d3819cc0aff77bd529a70f8fd99c1c12f85aef3d55943078c1142587a1b9206999d6743a62289e3f14e5b17464084135fe2ca254a1f5c0e862176812d138c25f277e5c0032cbd708db5630261dc7ff8f7fe8677b9702b070dbb3bafbeaa51f878cf5a912cc5af43b7363b846cfdf1643c85393ada76ca0d4e0bcdfc3fdd174c54c4eaae580e4e5b39f0f72f36678b5eebe2dba0d761940ea3dcde0fb4cd27bb696e4bb78d72baeeaeca2a81221ffafe890bfda36d747b02fdbbcbee422e360226f5ca9f5c32a552ef8a4be776bfc2b41d4c43fd951d9b0fa3ce62faf58ca06cb2c1dd6a713fcd07ade3bb68349bdd17c4272337c3f94f089bb3f06150e995095aa8b762ec322d79a2b05ab35362b77cf8e4f2aac99b1205601f6810746ac313293daef90b008699c523d6bfa4dfe2df16109972d9b16b067bbc31bdb609233dfb57f90c34aed21d98be043ce9c666a5fee9479bfb7b620b624b5aace2de8a9ea2cd78e5ba7640f383a2bc5394373ca026bbbe51c174a00ce3ac7ef20fc6c4d6719b76e19c96c44a6c8c1dd65699e606e495a56155c86e0120ac58214135b4e6bf931ee74a7777f3ca329215c5573a84a1476237cdb43b0bf450ad45c312ca37c4a1942e685aa6ca861cbe7dbd27d9655f9830b16ebf76044b086f79d897fa6b066e05cc483857fc74ae332dd4ceca8283ba6e84318d9b09e4e6d28bef8564220d82d2577fc71f073ef5efcd051b1658ec9e5e0288ada606febdf31b13b937ea1c242bbc2fda9341893a75b92da2d0b43bba7f0275c2d70f6d95ce16a5dfada95aaab245d88632594380e4977c270a94cd01f5a3a8558f0b4d8bb7553a28ea1292868a644c6694e6a78d6511fba06c7c23478d55eeb830040ba70de5d6c8b993bc713a1d91991887f30dc36ad47bca528c8d2a43d483c80f8635a7eeea9633d371ffaef04c8bcc74bb32939c2fde95ea2c56a5f23dde1d069381ba032614c4dc9a60f1c54d7c8ef703dc5542e5f2ac094602abf72221db00f175104a05954e4cb0154bca904ddd6cfddcf6732790e925c48d2df3aa11f5042abe4db6f858ee2a1b8a50056a163aa808876e7b6e67cf28214f1dfe34df0e0232c411547fda557adfac940fa6ba375e3bec5d14173c7a714de1cfa561684503 false -check_ring_signature 46a5627d4aaee743ef2918c34a97c0172fe9d9b1573d9847fbe79c42e65365a2 b8effab5f46cca119cb4b5a4ec57c4567b8a1623da3ad0987b20c1b9728d2db0 2 4ce6e0bd756b8cd19e68091ea194207ae4658ec71dfe9439d047215de044c416 0ed014cc3236acf342e88b167d30bd4f93f0acee1589387837b98c652d2ca5fc ff9b71e498ce329ad6ba002016eb1cc6d53418c359f431ce29d6d179f090fe57b2af7e5585de136c4336198646b8a2161c7978f9c0cdd7e9e1443b7dbe09e306db96fc59f746566f9cdf889fae37df2226958a3403846b0db7218b3d49b7d40bae5013b82f3bebfb00a1516f165a85907050861d251e9b31da9304713a48e705 false -check_ring_signature 21d8e4f2aabaa3cad341727182669f64acdf03dc3de787c8dd44e8b123443af0 6af23076fa3755eace13b61bb2544fa6bb93d19c0e7e5bb951a3f5bb5b52446d 1 f62c96bde8e6c26575b144d61509f7e779b1f2f51c698ee01c43bdabef7917d9 d8e2df7a27a136ff0be0ebe3c6ad517777b031456a60956df381f3078e64b90f19a475ebc4e0d8c4ceed2420a1aafe68462a386faaa68256dfe663fa36a1e20e false -check_ring_signature 0fabf192315a212c9b7318d2299ba2a36aaa5797b90b5de52d0e8befe563aac3 1e627ebd7cee1088f33aba3608e9c7b73445cabe54090196dfb9447046fc5ca7 5 b371831f16ae6cf259630c55145b55417f2efa27b85a72f41756a2267a9da1f0 7f9a121f063f934ff9b97caf3f246c1c825299b13f77c560140939f900524146 e7f68c423bf17bfe707e3a689654bb104339940e4ca5ea7e4bc4c28c25a82189 63bc4346c7dd99914e89ff66a48dc9e9265b0cc258824153ae567c7c3281f321 002289c079167ec3349c4c0f4726a0bb6fc027270fd29a0d4662da6673f3053d d22ee0bf5fbfb46b5501ed3b009e469f3b3c9ba936923f0b7f205c5031077d505e378a43f93369045a80951901c47630b8aa6cd55922bf138e0def5b299d420809e86e789f8f4db2756dedc68b5339bfabcdb76860cc937a41f0f707938a8d07b3e222f732a5990ff78166066d1bb00a81ad19d0d0251f6e9d248bb24642ec08cf4b1275bf92573d5bb723ac6be2f74ce827168f4037f81e61c7792e4310ae0d92f26897bf5820da71af31efd9ea2e47bfbc45d2056bfdef84bf4265c24a0c02cdcc49f1683af46a513500960baf9c1ddd25abd1154db8c2e968eb93a5772d0bbc494c9cef382e63fdba61ce07e808a1925871847f5f8fb7c0647a4db04d0a041541c50a99229f1d887b7328e75394c1607898ddc5db0f3b48220a4f23f08d08f006a86ae9c72f4ad045a598dec676bccee7206252d5baedeaa32f0e43fba98a false -check_ring_signature b857a01f60e51b4afe99f1d9eeb40916fe7c05ba15a208f06b9875a01d7a9fae 85e7506ee80795aa9f62ef55ecc2a690a24c3fa70d9c66d9be8ae3fe744a4755 233 47a611a6426a2e5eab07da9255de1dbcf356bce42ec145d2fdc11fd714721815 cc48a205e8d98862a27c94a98911ec3e25b76d4a6b71ab8f201cfd46b270c181 7f45e5fe637b1ddb58c89b61f2e3bf225a306c3d806e8371c71f4228a583632d aa59a0aa8b57403aa58b4546fea89979015f2cc790cc80f78578e38eaf210138 876fedefecf749a1a9318eb9ef6d1b70db63db5d795c2cc60b73b98add46f26a 84180e399925d8b5d260cb1a39aab04cd38eba73279a4c46be63f93e929f9f6a 7b15ca5167cde185bd5bee0131e01140ad05f7d0d42081b69d3539f1c77fc653 53f7b29daec64687804d98d3df6851799b4974f1a9744d282d0bf4584c650a40 f89a95e4280215c6990fb08c0059bebb394cb4f922adac3c45b1f830a6687697 0410ac23d98e46536e556951c590cb321f56f7be9f5981d85568265be99536fd 59e2dd60f3566f5e9fd0778730c073c088c7dc9edc8f5276de69a5163dae6386 74d80c92ec30260a6eb9f62fc0d14957bd5c883c95c1ba21b1e78a8d6c6b3489 1bf5f6c4124dcb8f8dcc3028aab92ef5e92fa31729c1b26d46de4ccbcf73725e 020f4140c10f52ab989d114551a72fee96f044a09f3c00786693e42e36d1470a adc78a1f42faacf11948b07b34ab3d7113f36db52300302912a6d019e4c94db6 79f2ac1eaf0a14957b1878fe1d5df2f402d615c43a2f7ad3ab49027269d82334 4d3083c171abc90a6d3a0024c1e70e6e829b0aeef98ac51ccd7c8b153078205a 3e47a3316d6964e6d0504af908287016c12a819b8c4af6ca652b150296ba0848 5d214582e809bacd99c5dbf351204dc73f74133c8c1669c000cba290e886cc65 6c27971ff97ad0ac8c0cd5a5db1ff2700955adb87fe2507ce0c8a968e3e30601 aaf0ac9058ecef5e9e61a701bccd28090bceb30819caf5ca4a8eb4cbfb9d43d6 be9f862761c2cc981eaeb38186351f47016a6693133c097930ae67682e119fc7 1af85d5d5558a3120cdd2e1d59fa443e9b073a11c427b692de7aee15813d498c 481fe5aeca9cd5bd91872fa0d6eae506d1ed06b14c8a8814e763f02e8c9526d7 21d55e6216e3b12a947b289ef928b12bdfbc5c8ce9a03456957e2df722329ea1 2cfc585d4cfb21672effdf7e3ffce70c001a63bfa2071d31decfaa3f5786a2db f294d5f24c7678ba4285a187267e3cc92a6aa797fbf500e140f1113eaf28b0c3 65256bc6b1a009ae209de92812969028d192976c91aa95938723fb3ce856bdf1 e7ed8e1775bd494e53f234276cc45a59297102c8571ac39b9df4db099dad24d2 39f33248bd50f842a16921d17ce1589b3e46a2ed315934066f24adeb9b5339fb 7a037f4518e4489bed5a4b8f9ddd681d0a815946f0e89346236be54f7d9cad60 742f67ab63270d79a3f27be894c79060f52fce5f20914d243aa74fae0079d1ac c986a0aab259c1f2607cea170574c6d20f0d5aa720a3de395c4262e6c99a75a4 cd482d70129f1d2110b731b8423647fa8bc0511c44ff8975b7c179030aa55762 bd98b3295291f1d8e9c14e0ed9b6361cb1735b00c4d8cfd16a9965ef6e01cfe7 91e88f9071bfb3b09c959f871009b26697fdd5c2c22b1e736444ee94583a62ab 801fe260f00ceffffb62a437eeab8421a1eb445f304d432869aadb008ecd1fc5 15df8fafdd35bd45c51f890963e61181b481ec1dce086a690176d150b5838ed5 fafd9aa939ccf9790315513c6f5e2a2ea3f34ab5ad41fb4f4c0dd03170225941 662f91466b679336e89e6806d66fb0f6e58a3231cf8953d0bd33ccf7c5becd22 fa5660f458fdcf33fde027804c9eee4ed6c90d938ae9f7b23eb1b87359d01c17 902214b99db2adf3f81f2fd3564649879d397087bb7c3e4236deb90b60e41449 7dad6e1586f78353f4fe9121bd3bfb409cb50ddefdeecf288c32dd52801fada3 5f27e29459022bd062c296fdd9677c8ae0b090772fa63af94b4fb3b4a2f50220 6cd1c5ffb3664fcf8674c2dbad3f6b3c2328f15f8f8c2f2b7304ea80b0028a1c c737c69f0d4c2b11428052a56053bd8536fa39b5b4a6a906bf646277e8911455 86802ea70c5a4700e2e15745f62ad902289304c0cb1a759614f294c32b85830b fe94753dc94a349e77b97fe8c6f1fb39882af2d50cc7261aca9f12058baa9d5b 640a9f85beaa9184a96cba300fc14ef5c004b302faeeefe14ae660d9751bfe31 a36c89b2382d218f1cb1554ef63f963b9b9664973e4133c9c32a5fbfc7202663 3757f3b628d8f8963ad216ad404b6adb3224501732a88881bef89cf5fb6c26a4 995fd0ce4581b738c5e1b7884537f2c41f090d6627877404d4bcbb77383dfd80 44aedd7ea16c3e2a250fe91a9e5c126346c3e2f1d74d04ed6a12e73b35b55490 2739e7a8ca07e311f68545158354d227ac3af58162e944302c2e4d4a47c89ced e02f54918b49f62ef7f90823b7ffc85fff9d2b1c0513acf0a139313cd02e3583 f3a2a4dcdb1d483e6fe581c3a820a5622f459e45b707c8f73f0c594f58fc10b0 2cfa986cb296d812f1c8cd7de7a6a2512695acfdcd7ef8bb9e015987f8d94eac d1d42cc1fde01ac81527494445e1bc7bcb7d715ef7fb5d399c222dbe5438b5fb d45e3c3bbb710361a87298947750d875f990e2c737c5bc41ad04c038f3b014ce 686928f1b97693ba924296b600b1a76937a15d5cc7dd133d3a268049f696a918 bef33261284483bbf10111b248d99035ba806bfa91e9486fee3b69f99e9cdd46 e3e7280da12a8bf92bdf3dac89c94c48dea5e05a9bb6cb234b948386c88e3d1d c4ecf85fa90db4010dd92b5ca1e1d25bf410a73368f5618eccc371a0b6bd449f 5da7e05a5b9f46af9dae0c9182950c41aae870e96ed45c39ba08330d83e4089e b3ef49a51c0715fc48cf8553842a647bf9cdf77b680505b8a33b40836fe177b7 bc07d0b063064e2d9ea3b7016b54107a298d6d6905a9aaaa78a8f02af91e78e4 18aed6d9f53becfac93d8bbe476e50fc27fd642088a28a99be9f92204a2e9e60 88947a7507b7ec9fa80e4306c5881dcedbfb928df316c92bffa4cb0b9da6edc2 ba2bae663c655ca8e835d6660e14c4269237b1e3f0b60a4fece5e41ca8c2e227 7dd27269930d29ad3c846f155147a029c875a81fc6d96bd1b88baad3facc1180 33eaea1ba1e1818a92d1c1d22a526604f02cdac8b9ab8be7d5ede91cbdd631e6 28d1d00dafc57872f136c9016394df22718322390bccd3ba17824213d147abc9 096bde5e9be6083e04c914e6c3f5d4a40d6134f0a2dc02a15edf765f794872e2 fbb186734320e772dfbb34b18664b6704083cad48b094637f8c12c8f79293ff0 0370968730ce2858646e49ec4a113c9a9cee2454be7b37a45bdae968400fee73 4031f6016bb734a05d1187684deebf4cfa6b5e65ba3f857dbe77761b4fe0c3d0 c4f236d0cbe16e99a28f80cfdad63be34b2d12a536250acf8c839563709e7de8 28e4eda2cd6dea505d197ad5875fe67d736cad2e4b42fddcee66ed43a5c0b49f aa936ac0541443bc8704a1eeac36d036d319130a145211f77b45ad651e02e622 7e6fb85128d889823febe4c4e7e80537a0284955b7f829378d3640e2147883af fd9323728ae7906865daa4d07a63c7438b5d88e283772ccfe909da3e56ba773f 5df2b543bc815c9a44851e6e136ee55eb1c7d241068cf56a55c2e4a7355dff9c 981e82319753c96e36538c449bc16b4dc24ffb0307932280a35e82245d39bce6 ff5a9d2f304fd4262a0f40082d11465193d87d6428dfdbf0e4d3e9f51cd85fd4 7ee676ad9a8e8c75c22c046391c26bfd50d58524c1e281defe20b935780cc1b8 3410f2dc80e4ad8de1892d37e486df50cae74087a20fb646ef6d039567005175 cf7840e46c14bf07b868d5010981f094b72435d78f4e2b43e3225a7e217f3d06 9895e71b6ba7b156bb71716017f31a8ba42df2db6126e7383651a18fb898f47a 1f805a1f12c81634ced4c156f1b2cf9499543b394b3f53925d2f9ba7047d3193 4b28a7683b90b941b44ca7f7f1ed348f6a7b430b964b3b1870561423750f9e08 1756dfe476bcc96f99f24aac69f79f4bf3d986790ad28228839955a0a09fcbf7 d855da39395f63e3a4f5728e06a940570cf1bdb880e66288cabf17b37a7b3eb4 6607ea38988780cdd389b632e5ca5423eda988097267027807f7ad4a5aba883c 21c52736e4669017af6755920e5af7b669fa659f367e8fc5d54dda5ced47fd68 2a1de25b4e879e886c45aab94aa6e2d959435e17658e9058a8d78125bc5cdf5f 3832e55af1a2c247f57def78173325cc6dc8f0b64816d965bab49d35785acd11 a16e2fd5106d96862801343884cfcbdcfb200c0d52080ccccd74cc1836f2054d 24d1c9a620b0ea6577074bcdab24f15211d742ec32b7b7d4c43d376ec562450d a838627bf4d6838bdf491864459e4f6cfa9988480c05327b106f43e9f00af319 76b98f193dc38f391579d0cad12cd440469057091168da0cbbbaae2590587c2b f306160fc6c6e6d358834eeaf9789bfcfcf652b6b07c091fe58505193c38613f 8fb0ca8772ee8cfd7dab531e4e6b64feb03bd0abc922f8f791f56059c7744cf7 b9e35d62b24c36cb017e8d2ba335da54dbf3fdf83435de204db580dd0b6548a2 faf27bf71f74c38c72a9f69d25d1ea4eaf356a729d0fb86bc770dfd20376b295 1ca338f5d50176a61a70d81da5e0ef729aab45642225cfb899e19d281f80f0d3 6abe397833fe1f5c2b7004aac6a3f71b032199a12b7ed42da227a42def1c42a3 5bb5790b6295d826791681b26b9af0ba5d0fabe2dabe64094d317048d0591389 1de406442685870ffc6a5767ac3dbb7875439012a013e9c4039596ccfe90565c a0a90b25bdcd88d8abb529fdc7af82a217cb2341b8115135e60e2398216acb8f deeec0ec611d562d560f21fcbde9933ccb82d89eafd4d81f3cd2bdddb2103b33 7d0002c5b664c50283887e41d0de0420b5bdb6834b4544d584894a1c7f6ab5e2 f6a75419312092cda8364d36199b4d7b31d38e2ce12e1955bd367684f1079fcb 2fca0547dae1c6f28a7d09f7a929128a013f5129b469b54a0216a47f66547037 d6e907e641dc5ee6e26c22023c331edf385d977d0139e3b8eb24165410041ec0 2a1a461e52cf573cc7991560bab6729d86e8c2d404649a7af1a6571bd0ad876c b790bce71d23089d4450a9c5a7f1c3206d929b553cd96d6dd95acb5bf1c30fb8 5ece39ac9da286f8fc7010253c2e10a9f138d74194f2c379b7bad3ff76fa5429 0add60022b5f631d57c1d7e24ac3921d5a9dfeb09b7bd6cffac0964235c5e305 6d4cd7762363df46c8e07aae06897678d7671b7320f6e65d02688c3e2dd513d0 4c75b9aa562ad5fb6ac897c2a74f718e0e571c73bd5b159e3de723c6f13af62b 28b01ef2d6c4546ba94aea1ee1547b80facf862fefe4c9271ebd7380398630b7 e11abc0e4a626b06081ed7c25addbfde0bc85d6fbd1720741f5d8259f7b0e6bb 23f70bba7ce67b03402e9c11b0ce6f721437691005b6c12d528044f9e2d3f1e4 0a0e90c974c24dfcf51a680826775c1b6a3efb3a28580e272bdbef339a0dbd37 464fbdddb53772745be594b19c4fd9fb47dc1bb7f12cb0dce62b0d15cc37b227 c6b1d78834ad8a567fa2c90229b439be69223327a6994807c4edc234c84c741e 48cb1d83fc456d42abde850fcc92f8a47af80371fe02ba7b065b6f0a157d1e04 d1d4b3e44e04010de6d9e2b5b457cb1d0c0725aa71b3185b49c06e84631dd4e6 ae296a13a786541c3faaf735c27c09be1946f65d371f770b19fdeb9042cbe4a2 7fd58d9d772718d5a5eb4e9789094440ecd51e33e94caabbae67060ad5f5640d c60f0e3c9a87dc1922cb69034839aa01d9f68085b7ca695b6b3b889e92bcf398 ea5c94b9ac1ff87570c5bf8e01357bfb43a9a705b28c3fc0ab4a2c3f67a4d2cc 0a8f23ce40796f5088d781d0d65978995e5569110f7884da4d2efd499a7c6828 11a867dfe7e7cd04de7594a01291eeececb996592a982ede1b3c0a87e0cd880a 7725c90efe76826fd5f0517e692c06ede284deb6e352aca304662e9addb590f9 cf7b7e54f1537e41746a01920d5828026f6ba54799dde8b6e74acce81db6c4f0 99fa6f7144db7850a63522caf2b78e7f115a1ec6a3bf9a676dc9baa9a1ff3b34 a33aa4fae6877252b045dd34729e72a7b40a089c728a48eaf39c185fe179442f 911dfa30e8a3e4b1c5b503645567d88dc0d169d7c0d5f2f3c65cea280c1868e6 e12b7da46d72ef36132c65a1871e0ec3ba1576d62935050bbb078d0025e8cb19 9e5f721fbe2efe62ac2f364d21d233f4670bb07a3dc2aebf1654d339152b5e3d 37ad1886d6ad7e045d16e5227793e60ffe2f0841d193a817c5a1c9c1a29796a4 863d5ae8480255845a7ceb82b6130ab7c11f798878406472d6f15dcd910d9ea3 1dddead2c3f6b40322e683ac185adf32cb4245ce4f38d275db3d103015e75233 30af80384d576a4bdb2563d74f9d784899a0d7e761cbc5e25a88539556c12089 531f93c09413d489d94f7e9a3f443d3c9b7bcfb4200b0bcdb057ee5b1169d085 94febd5b8ed64799ed8193d8b13047410454e60a60099583d5858a0f1617b60e 8db97e130dd3228f60231997387c61ae3b2fe9e92ba97ac4773e3f5460fededd f2750b81324467c44d6acc64f83e3f198f734d15f029066e6a3ed1529966fa55 b7b301aeebdd11c7067ad3bbb79cddca10fb3485a544a772d702d21ded579c40 0dd188b3d20eddd8c5f07184137b1540093d3580fe3d1f6ad897d11b999afd76 ba1b6ae406f751d18f565f2eb48ce8e531c72c35fffdeb329baa89617b296a8f 33f37137f1b5e53b4743d3b9278b81409efa1ef83a544bd0ce69fbf159172fd8 34fa092b4d53a11c7e807578dc1b242c2d48a50378d8e580503019c06ae2c63d be5919a6a91ae0f90de447203c566fcedee31a83653155719f2165161956242b a98d13fbe413ce86cc553efe2f027e68434628bf1912e0a390da7900227216ef 1537e4497d8c909c09bf96e1a5fc0e0a7ed3c1a996adc57946214a2f1db1c68f 364e1be5b8dd92a49543c70a318ecd4eaa684cd225608449867a52bf24eef873 d7bf314dc1c28c86fb3f198d3e379765156b2ed55485098c5263179916426fc3 000a1c7ce53671da3b0b3826ada617fc8e09f6097bfc928ff71fb157e0142c75 dc668622e2e37b5a0a48a84bb8d427d33ee0e3587f563d8452de51ea12f8e12f 23b1ac05fe023799f8d32135e9fdd5d6b97609c636534023930838225b859b81 8751b4081cef7a72c6fd0c0875ebed1af1f7bc9895858db2599b69d0832aa53b aa80c5ec708cb4ee5e8692f45a92d227bc5a2f100443c5c6eb62f93baf32168b e9fba754a36e093b29f250a040220354a0742ea76e6b9c707c9685ed6e8c5846 03b57689ef5ba76a4258da91cfbe758f495be1f57a4fa5843866d7ef9c92635f 8673b278ce6b4f7832bd9d6264bcc5a2facf70f7e8b0d4f0371de07d7d6ecca0 37c0fc987ba6797259ee3f56382ed6fe88ac791c634c905dbb4d3a1946d72514 4f6fd27b6cd9279479fc6bbf45e37b061931fcf0514bfd4c8e40ba96c29703c4 2301787c9ec4f170eda1e4a88eaa4f5f24e048146bf7dafcb39e1f5a1698c549 6e2d8fd0531c6a4199c3902045625934f23f66b8f8996aba31c420d77adc967f 8bdc7554cd1f8ff80647452de2caf511a410bb9f2a9e8445a444c074ce968a4e 95d1e6375a93e9b45751c14a15c1875f9a8b81f4805b6e36f8a67b2f4caaf856 6f14eca0c26fca56d3af3994d12472b86ba828c697b74e79fbf76093497d5b8f 4dc46c52a9516b46f8505af99b97e8b13930407948bc6fb0ba7cb3fb28ce8ac0 d7b726f8fa0864c3fb226af52edd00af0fb0476b493c0a309c440801134297aa aae604c1707ed94ab661275bfe8b4a3810043f50ad8814ea80d81ecfd6d4e8f5 b5ce9e036f980d6bb901b56cb4e593d993e3cc696b73f0144b84b3e68c114f9c 5e904b3ff4a5743640b8afc38e5582f77aea401e89f998b078c75afb9dee98af accdc9b793403fd51df4790fe7b03fc3bc53e4116edd44d8adde5291db14f3a1 bbd9df26c88d8128506b0ee9e25bc4e167066a100188c1bf03e7a789ddac7f6a 3c6736a3992d0aa64a27b1a7f1a24ec3bb4f628ddcd8180d248f149d4151dd8e c12e3eee7b24f68b8240f4bc4395d9eb41ea8717a498fe99660f2dcef9ec4083 3b3165a04eb8dedccf9d6f378c9c16d85c754168f80b7651cedbfd9b0b383d3a 7227cbe1cb2a366062df6ac62f3f0a12a97ace5fe91777c28d765807f7125b9c b0e49523298b43ec4e78006fbf76e421d6e0249014a9401709e3cbb2ed349ee2 68386bf2fcefa90d4248568dbb388eb66a51ae3d538b75e25d186444b9bc4c5f 1396582a0d2b10cb1f7435c30fdd01a24ce2f60b2595259ddee4ae75d78b388d c63e512eeebd78e1e03755c95232ac3bca95545c66f4ee23db50896226afe021 39e1270a96b1a8c503efb196773700b4637ce512aa59115c5133bcf21549959a ef1c189236099b94f04976d19f0bfd4f4b9e4cbd955f1c4e62f3a7b7643edec1 031c78fa9e7b8d233fb35ccc16e82b965cc9bbfe6ff8379c6106f94bc47743b7 5dd45981aa86a2b96f68cb5121d943d57eb3c285a49d3fc11aa8c6f21230ead9 33b0bf1443d1fb07e5b487bd4754a77fd3260c4940365fcff3ba297f73d376cd ab8a5a013e80dd1b85622642bb1f4f2876d6207294b18cecd51bf8168c577370 721e33d4745b026e648b7ee22cbb90f2f39cf7d1caaf31089b3cbb6ede51c9ea 0c09a66983339f9b87eee85c16dcf906f10f743f2ca61a2671bc68382f391dbf 222716e1ce2605bd5863b0d7119a29ba6599c23b495a3f12a46cca0fc3035336 2b4dfbb80935a5658a85b06d955dc3e368e922574ebbd7f8f48526f3ecd63fd0 4e3404172dbcd065c31340ec4f5843e77eaa4f999d7ccbde3551311fbdfd7046 4f64167ab5f019fd31774959cdfddc0a2ef09da3777e1889f820183b1f682666 fbcc2f3fbbfd829ff4c4db0b57d087e33c5ee946f29ed05a77fae354191f6c7b 47649773b9b844176b007d577b1cfce1eff428994d328184af0bbbb1f233bf9e fefdf5d8971c33ae596dbb0105a8ae6c15dce0ba9e5eb6cbcdeced779efa8d6c 5ebb1ae5c3117ca49f1189b59a73dc796cdcbb4c679b350d02a35725d95ec8a3 2444450c91f1798999758ad512faf87390b91c37ba76c62886b95681b7aabeed 3055c822b7590cff058a70fd6202851c1a8e043f1fc4f3f800579dc9ce79067a f9ed452766f1fcac38cd2a090671c62612da3a9f4789ee7bdafed20204bb40cc 81c8192272f4f12f721b0c8949cfce0f0484ac2a0b5f165fe5542b0646cfffa4 b682615ddbd139faa508b8a9ac7b99bde0ee5dce0d7b74320ac101cc228eb884 8587f199e2cdfa44040fece9b0d20e6198477b41293f6e92dc72b82ae8fdf943 84069d72b98c5daf474ff9382b7b2eb198da88a0e12285faac28739f04c2511b 80d7682f27cccb1d153fa504ed97fc5138ff69255c4b049f0495c182c37b7067 58203bc816c935c169fa9c98471b00305e554999139ba7352e16abfd67005aa9 ab47bf1c5df0ed6791af28e2c8a80b954b67c059038f6bbb28f35ae0bad7f3dc be8ce770a60bb5664c0570948ca53554fe06fd222bfd3484cd87ba5a08e02e31 bbd597ed8fa7980dc9cb1e24f4134fc7a8c7889503b9c94dc12a1442a50951b9 ce519eea2474931881489736ce287a284ab71f2bec2e6c706a5284778fe3b7b7 d33ecc1a5b0661d5b4eba84d658671a69431adafcbd080cd7117fcd58b06ff01 630a2129c6ca8967f98dcc195b51de0b4a1afa3168ea0dd38592a5d2e14047a1 67c039247fcf85f6fb465f7d34c3f3a84ea783734af0341c3bcb6dab304f79d5 e1416573c4302af6a43c9817531ac18087a1e45b8b5d0550e5db61c6bb73f9df 7af7a099878a04ad6eb97f31b254908e15774358f7c75dbd58beef233f7bc05d 9457feef5b7ae56624f659c9bc62c67ad81ecbf0eea35b833d639d5ec16ef51f a56d3fe51cc2f68fe2b3bf872009dd068e05ad5af567475a1e6ad78b1e4862be a934f84ae986211d47a39be19a4814d897e6377dd1461332fa1fbfe9ac7e8fdf 10b6355f71fcd036e64d12455e3866c496e453b227e0c9db08a6708a78b7bd07 4251f38a8b32c422266fb9771abff00fe1b046dff12d35aceeff93707bfc0b89 aaafef22b1fbbbda0f5ceeaa873b8eaa9e512b1701668e64630786d659263044 48c0246bb2ab2aeb79f319d3ecfc12a5a9106f46dae41e088e3c5d0b9b7fc5c4 2f06ace0c2df177ef6a7766a99387d3d7e9aee94aedcd73019b6b909cbdf738b 870e7a6fc23f9a8ccd146ed071308144e65096905b0e5c823dc045346b5b18bf 53fef4078c01dcd0f1a72ffcd5ce09b06b42d6f335081c9e95309c441e16112b c8755abeb2a5eb447dd8ff6bdf64e0416a4db5078de1d69f64dffb94286bbb0409086d5f18adb0d480bd5a7bd188a2420e65dc8399458e16c0071561d1c0620d9ae358194764c5ec0033916520e19a8fe26d126003671bddb7f637cf9ed99b0ae28c809a1f2fb5579ff8d5fee57326e522e4033428791d8561c41940e17be404b23c4db17adbc805201c3d74bf7742e49ae9989fbfcd633e964a17063abf980d004644eda3f20fb71fe4cfd643a1a63da188ca11ff05b5aeca87741def868f0dc442ff2db5a2f405a97ee046c993aafc26bb446d02d27894c4937222444976019782ff8eceabc4d6c14ca4b3bc19483948f1cb293247e1fa2b25dde1849f760302bd2caaaea128365ed378fe0cd20381fa693f1d82388c911216b0dd85b3d30c9e112fc85fe9faf6aac9a5b1d017b0fd4e6c34f462154c69be97eea7db627005c056239ebfedfbb8bb37633d7bcc718656163920963ba3badae40d0f5739e9090f9d33d58ee86215a90982e6d92565e43f5ad05bf9b52d7755592d5b47efe50357ab84fec10b1e16166459164f6e9b4d075bf536438c8aafe46f7d9503ee1e06fca21df3e8f3ec485aab830c5af2bad9450a78c540c0f177ffd95a2fee13c80fa66095a5ecdc54b580367b0d155ae23fd1c02b6c7afaa097264988ca05b41101be6090d01ad49d1c253c01557631fcc66f1ccabdd8a1d852ef5bc08e7f26b0006143a35bd3be5c000c7a115f288bdb35758273b9a38e99f478334423a5bd940200c9f10f6d30a5a2bffa75e15047e8530a697dd7f2046e6e31c996e2eeb7bf002feee4e14c931d05a4dcc24354b9d85a4c8a1d9dbb361393b9efdef1a36b3706a891c97e8f431bbe8bfad3ed2931c32005a8295ea44fc55c54605d29a288230461aee2c7ed34643429008e2d89be3a5225a47399a18e47582914bdaa54ff96067f89b4a6d1f88d00d00511323d66c0e80235ad967f6ab646c36fa0fcded6f10438bd5efb691f490d450e7d4e5f61e39066421e6ae74885969c2cbe91d304fb0c6c36ca8c199f4653f6c2545d601b5104ce21ec3ced06f8cd78faad20a53c61039c017460c792cb21aade7d8f921e0eb6be8a83c0c17e01e643ec2e5bfea6f70244c9222741d0e3c5289321e9fd2b4b38ee05d7ca3d801e9fcf4d8db331f5f30201e1e055feeeef20dff3b089e33ba52fffd781e3736233fe7693bbd12c0c3f0420cdd44f3e9f1168edeb0c813a9ddb172d9cbeb30bf516d705ae13d7e266f60970eabf5bd474ac1801cb43949e5e24cf378061658214446cd62de2b9c7925d0ae5f6edc5b424e3692ef9a8f56ee33af8d5f9c5aa11f11e5c27dcfa694f209e09e8406d19ba506c0b66aef6c1237d5fae98e1c3ced5a1b789bb2e61c4279cb00f1f4d37de54ca228a6a389453fdf1453524b1926b1f4664ca04c9264200eb9205181446a518470fb8bb0925958bed45b72bdfece5323506b9ea43823c80cdc80eb54b43d2570d48a1d39c6b25d27391b545c186e158083df2fa3ed89146532a099228ee92edc9c51607be5cc75055b39877eb41be10a00e095063b62c70cc5b0f7be0b9c832e061e61a07c7bebc001e58cda770cf8093f6661af46e9b9633f60f8902eaf568818e1c3955598938082c9130784c56b27c9337fdee72b7ed2cee06c26e620e215c45054b44daf3b1cb9ffc2d0495dfab086109e2af4e779694820ab64fc29b59e1fbd1e5f3a3098222758d5cbae2091647f3ff43924753c391f20daf6b8fb9da66beecb2eb9fae48976325eb98c47d9c0a4f42063a806ed5d17803e179209f1fdcab4bc3076d90fbfb631d3cd7dd5950b289535713d61829e875031b60f334d7cb61a5aa00d245ca851ee497ad4702a35745d0ac893f105100ba0e8ffdfa3d5a735832c86a42fc8274918e92de2f8837d06a0971bd63432372890f39b840a6f0632ff067791bdf8678481a2eccbad357dc2d7876c335860d094b0716b080787fe84da168ec5fa0bd4e46cfe5dad804f96cde0f6257a019b196fa01c862ed09da270940424262d742817c527a4b1614a4c5d20448bcb0f747d19701f574e08c81c9cc901146ee538e7fd737fb4ed00e00ef93952303d03a01cabf0702199fa48fa25c6b4ec0efde9dd878c358542af51bca76a33dfa82940ee031042f326c4322fb99f9caf50944604f17559bea3989fccaf473a5b65afe59801908dbaedbb5ec24dc84ad86310da18093baf391e0f1068f0389f70f2a4c924c1b01106f2e05cfdb64a04a69f0a527f06907de03fd8c50034343e6dc10d998514d0b7da16a120744b9fa1036fb0afe734b0128c4090992b8ab61957df46645b6dc0f9e84feb915687ef391d2b31a87dfd34fe9b6988b787d9091354fda851faa2200bd77eed4685fab029d41c7729a44c2685010a66e7a3f6baf2c902296a9eb05086fc04a5b2fa254a4282f788b8f675472c0b138b1032d5002fbae063a3ce82a04935ab7dd4541ca42171cb832febb5942b607ab601dc5483aaeedb638eebe130055c39eaa02f3da926453c7ce39fe4cb190e50c7e4c11d60ec069b51d6203690f9614afbc7506b099f90dfac89890d8b94a20e58aec04263bbfbdfb7154b39a022ed07abd56310d4a8b9f13f0fbd801bbb7d3859651b387b7c723c19b562b52053c6df09813c115899cd7183800e4c084947262a1b1f11e8a0092dfbb29e6ca043a0f9c978a2573abc3a93c2d2ae18e17b02cd629c4b4ba6a6ae886d42bd5f60bf8ef13f2b1cf026b4b1d8f830470c167bcdb00587a53c9a94d1197f897077704a314632c4a6496f2aaa742ff2ac87247ccea498a1187f71bae5ebee9ed243b0cd312f3f7a08101bdf46e886b08817d4a972a22eceda2144473f8bdfc626185094c967c70c99a5e2344c869781e36bec748da1aab74e88b88eed889aab878190d0701df3f6266a27b4084252ab76b041bcdd92ff5d962ff1117617fb94dbc7a0393b39a9ea87f532c57bf5a473abb413140bf8928ea261bc0321057214bb3060787e37e7fbac4d8d6b486a228312826d7eabdd9cb4a4f91a411b0408fd1bd7c06eb97f860c904bc26028d65552ff059e26284c5397c91832b65e61dfd17270504199b6c2c05e7ecf666ea38c42babe81cc11f37073e0790618dfbdb9b922a630929f93d60e5aa48c383f7e29337e8ea03fdf1006a9e839429dfa4f4509faa96050423a2692dc19c8aab1280c09afb0da602e936bc1ee538604901ffca397ef40aa7d4033d84d41c2ac833ce6a7cad6af9564be717c9091cf8565eec43188e8f04c5ba75f5debd6efc6e4e4b1327ed44dc683780ff668a5a5c6e19e03441d63704d53f578ea65b1f0e8ad14eefc3d8f134e8da19e40279b3e6c5641399930bee08a8d1ad721d13419d60e57a17c2ef4c757323737423f2f4517f3eabc5b86d7d0bc5ce4e863160f806ea088cdb71a489975a994c3ab830966435b35b16f4b1f2070d47941b895507e7634849e6fdd71eb8b7680b29b4cb8bf1f702cace541ebf0943385f6058621f681f6e7b0d030fc9ad5926fda04f62b74d0c002e1a2ec4b000292af4facd31f750fdff961bf2a3b96153571d6c3b2c4915020c658f11f77d0409b68241834ad23dc5e59e3ee8dac9b8e515eb3a9127b39a62028f2a019d100532da8fb813136223a741b2a64e30358c60d9e0302fcdbe9c77cc0246f90ed906857052d096d05a713addfa59b202a637688df86d5cd47aeb2f0ca8b113a09f0c19077e758e5f971d189214ea7e4cac966ea92523d34ad80e1ca296b9a2045a09434933e079ee5df65bbb28d5a3a8872127b75fdc8865c3046bcdf96ec5bc0507460b6ecfbdfad9c5523e582d7c706185c512c0f541b4ab851f764058a2e61100a6519ee66740d8aee8f695c92a6784442896d6054291f3b8e5db653d85013f0e11b8c155d2ab41c281ad1687b8cbb25a761e337c197868379a447a484161ac0512766fa13464731dfa774af2c692836c93be4d1108efa0992a4d195b4b2103078477c18a0a90ec87c1e29cf5c32d32b04a2878e891dd0289a87146b6ba8bd6050d4e5606a390e186bf206735a77b8a85b7189e4a80895f84c33f1b04faec2202beaa1ca42bcafbbfcd6a9cbaca44ea7d32dcc3ddc509e7df80cd753f6160cd0dea7907124544308c7768eb967ace92da97c5c1090ac64ad1122ffd1a39be4503cf35bfd52c70aa2e9494c4b42f08a1e8fa1499ca1c33dcd928e4e917eb62a20d858aa50f13dadc71b043652ec85c6d264fcf0351ca3eefdc33cdd17059786909cabb8ab0767a2ac1a4d44649dd5881eaa7c8f11bd5da6623a28b98f763afaf033c785536a9dabcce95accc44beb63afb46f5b041bbb0911a89d936b6df06190c188678afadb5a53c8119508b098efd9450b8329780b5cd8466727bcf26839203510c63d4b62c76ef55289ea0484a52d38d1b70820c0f9b3cd284d8a026a5670f5eda78a9c80a149cef58ab8fa6e9904489ac79b3d20ce7a5254c864b9a823602b11acc11f87fca10e8cf7b43f7139165dbc36ac26974963a47662c58e249990fb9d1dfa5d11334bffc38b0b654867c45db3fd3043e12b87550191d5d1d1a140e80ab02b07d55204e0d8ed8747409347a47230f695d61b1df8bb440f61817c20f3b184f5e16646be3eeeb13e05c148b74cbf00fd0ffe2e6539450d50afdad3900fe062b86c4347d8fbf55c6d476372afadfa53eef7f668bd37093d701785cd90bde57da7202a3e4ac5f9ba12524c87d7467220509f3ea6dc84950406964f8e305f807c0eaaad25b0f5acf834a850dee70e14fed080792260d69b2b2b0f2f7620ce95e779bfb9ac8d16cdbb765f327026cc5bc639d8e52a81682f2efa75c9e210a9bcee0751e5974acc0a1b63037e5afc6e4258afae17a38aad50eba6500244c01542d62b8641e4cc129da4c01fd1403eeffd7f7f62c28c5f1992130a83f11bc0796b5417805a07242fae81eb527d7da3df7a0e2308ddfb9fed15fe03d5a0f210dfee144746a33f04b90fc38b038c8b6c531329df82dbe9bdd705383953c1b1c0a2da9aaf6b5618be0e5bf23d56f5ae8a2732b571e1152b9eddf0c3e9171987b0d32c91955683d8527e6a797a30af5b4c31d7aa7004111c7d8e69c43c4188e02060bd4cb2085c7edebde4033edd9e95d78b7e2df20488a9cd3519f3dd2187f880bac9818e932533cb36bca7fc5aa37bde7a584b55941064e67233248172502e80daa9ec65fc788922317c0a8e9ae6b033af3e1c0e6a04c8ac4e71cc2938834e80324ef2c3b06aae9843ece054e677c918589d44e8e02e411f9c1ca20af64c7e40f17ea2d999483adbcaa209540ed2b362e151dfad97f243cf6e0c158fabfd65000bc5b82abbc2cddf6f1af0efba3165fa22d1ed4bf403140e19dc6d7a96ab4580e76830c426d630262a0ec1f1101ba67a24d3df0b6e765184baec34693e97a6505acd873a89aa06ae12b40912e4106a24ea2ac295674921b5dfde275a72fb94f08410448684e6fa1583bd7c3e28ea9d003b83b419312c1a71dd7565fd12c8a260a9ab569e26d96731de0fc4a2f60dab74509b28301ad0a32dff3edc8e56eabef0213cfdaf4a12ba60d504c5f96c917c81f63d72c11d38b46e1bc136114d0f59f0a596779240b52d0ddd68cbb734839628d0e6cc2b95492d0773d220e92647b0c02f2e94a06f375105663455ab5f63e31b21cd0b6fa0b39b0255451144a43377f02aa8cabf177bc920fdff88d293d3f52d332b85a436fae8a6c81da74810163e70e33f150aa1bb8acbe90a64e52894442f824f7fcb77882b4e5f2be2e8fab6d0d06bfe0c01d2570264670c32806013bd3af88133b03d7c4a39a8a7f5fb149e1030cee807791486c38d4f5460e2155b4d8d9714b9509d15376bd96c16d8a7ccfcf04e7b36fedc2a1db80383008baa2b07820b37cac4f9229dcfdf4605ec82344be0f9f0190c1f3c543a76c1164095b6ade06d2ad9ca33b61b5ae76b6ff25794de1018500fe339e097c43fbb952fe0f1884f45c9c031429690ddc4f4e233422af8b04942c79a1254f3faaae60d177d07c359c7d664f5e8c25cc623f7b97d8b13e340bb1874cbd22f100b45afef9bb1ced2d56fee5fd3cbb8af77df11cfe06c19eb10b3bf9f729b72f02b355e60b6a29885c4dfb0985605f72bc379696199f4d0b8c065906c1d5fbf1bdbe302160b75cde40dc31e784f3c94ce308b1f02b8e3cf58100b5ab77b92d4effbd985757431e88ca9d8709f2fcaf71dfd59360dfd46e40c50222bb58de058e8a92e264b310e4349738e53540f8694071be6b6849d3c117d1079d574bb92d6eff6131b4083f74a705dc561cd6c8071e4fdeaf9398d09e8030035c5912ff57f18780dc648676390c2940d88fc87646f9d3af9978cb0a4a5c8d0f39896a65e898fa0fe3fd4979d95614bd65d7cab4eca3032bed25ca465d69a902504541346b0fe12fae149839eb8feb4cd7eca5eefc3070c5e6e86cdf81afa102cc4b62c6fbbf04d5f9459838b4c69f2e61ac8118b39ea52dc081ad5e90709207ce5a80aa1157c5536b417edfdc0333a158ce7339e4882d67cfe0a171b4914a0e03e70805b0b58a8443792390a866192184927cee85f116f7f57967575037ff0acfd5ae7265df0d278e32a02e082bc0d43dd3c0950ac6dbbaece45a0ea8d24d03b36571c8a202bf447641e436b66ae08825f0a551eefc8e48cf2e44e53e137b027c253ccb98a9ec63dbfa84b6598cb1d695c85f8101edb79b9e3bcd842fdee607a03cdb825e609e76704d36f904dd8be89dd39d429aaddaff7c83550988fe090e79a259d5869e9e960fc81b46b1761646b1f7cf60e0f1846f19897684b6801f02621902276f9cd904d529327b3ccf89b2e536225a4c40e90d7a3197c9d621860249b2ea0a512e0f6991e0f1940f84d3d22d58443156255503b757a5d029c8660db230dec0d5b594fda639a42901351949411d6eb35c0ffc1f45c665337968890e7047eb7f2ba61547d9b648eb77306f5ea35ba3434a706682e08626bb8d721a00c83c395030c4f77a8acd4761e5fc43b816d9897450a3e516418c918ac9f5aa045d18b8f7b72d6a82e3ead5b82ce7732b466e669c56ed028126c7318354f7a5093ca9b268a7fab78370d500c032fc3c6bfd4987975e22fd06984b2a4ee4c8c808eeb35ee04e98ffd3608b41e8430c80962e0564fa07cc58f5ad8aa4d5aac04c09859e9ee6941da30dcc49aba19b93b2a503dcba7bb0322d1671f585aea45d210b363a37f99d8c5069c0388a19a234040b953c40113437f83ab62aa417f24ee4084a2cf8fea8afbf5e0173e1398c4f32855e2c58241c93bea3232f061b3794e101048bf581d4ea1389c87ea1d8e3e844cfcbd4c9b518a385ad427a1a68b4a0960794b57f1c19853cb6e9f354d8bcc2c2c22aa61171615f485e0b17ce30f8305a02231ff39b1c2036863b416d1ffa73f48ae924e2e7165357cf205c78732022e30757d73df8685c4d3ec3a462bea8c4a2093b0284383988baf5b92e096e388735037dd5b8a2c0d9a083706c37336e0a782dbcf3b62b3601be4c662b2b09fbbb58035a10f7e1edd01c30039467d599e727dc051c15c5a7a1c6c86f2cf3a45e833f0e2dfeeb11d1aea1550690ca118de3e2e7935be8ca407113654a81cf9c5bffaf07cf34b4d994b85655ce33e620655c954d803b4d6a3cca036472bd50b53c382b00aaf98a5317e778c77fc4f459b99e65d77fe0222352afd4520884f92b8f975905c488200e8fe642bc253360ce025f5ef4a52b45f89ba97eddf6ac5c9d1aa37107195a48e838a02cc6f1cba900592e1302c4709fd4b05f64a304d26932ffeb8e0b2053b29861469651a8b82a5eb1acb98ce556b810a52c1a4d27c187183508480880b0d1979bb18678600fc4e8ce74f129c197bae66519ab7f04b4f67614f75d092e8d873d1033d5242fdf7a847ebd9051327868c5153a4053a814f5c77086fc0b4d90f837122e49c6222f00125df0d7ae8948f3ea52b2d63ecd7143e127b9540125875d2487a8f3a74008fb5d7fb59111daa770127078c2cf9ca02e7fa93c3a0ad550691f0cc45c9d032b278d224a49164330ffed478de4c5cba396f27f9d3d0256b42c92dfb360e610a69dc5b1398238e0ff13144a4d32482e2e02d36e255a04cf3f613ac7e8979826eb57753724222bc88c2d50ed7efe13f3f1d795b8457a00a5ecb48408cf4f70f0c8fe8f71e93a1d4be9377fbe82fb9e2b594e6e5f726b02bd967b3a056b6ffed15bcbed879fe8dea2ffad6284ab3b28f3beaa6dcea5f50ff9c44282994562b364f4c51c06b9a52a6ccbaf853b35ea5bc0503feef5f75d0a7f5e6d87ee0c43fc477a970cdff3d558c14e17c08070fcca2466444d8fcc2600e57fa62a84737da701f22e1895fd2b0aaa67f5e16c39c02407c285255de0a9083400b8d687cc495347c10ec3de7100b96c0c1d1957a5e16f53c857376c8f420a0119d2177028e8653fb54744ec359021da78a9e074bd7a63ba1d4c334544ba0eaa42b913bef18b6d407974cb0b964467da7199ce5596eb286d585f78b90a720ad8046d2ff7d96fce74ad39f98871ae23db4b31c4764a6830542ce479183bd1086439965b6789742f84bf8bd161298760d54b26338120db343b8ed83dcfa7ac080a2097479c995bacd2a6e207f0fbebbba0e5fec3b5b5bf8055e03249313bca0475ba220e708c79973c1bc1e1b95122fac09d757ff8893b67fa92241a11cd410a9783595cbddf8e55f0b51f1fca89ee0bb0c25185e83d43f97f1aabe6e2c2060fb4458ba155dc97c4939d37c10d3a2622da1a15458087e50086e3b9bf3130c509752accbb673169c9bb519a8353f4669ca9a740bc720da5ac6f39b292bed7440003680ffc193bcbf0604d63b4454ca0c36edf6a8081520483a6ae0a8814f1750fbd49e08e6414988131991e678e960ab5c1dd1cc0e10a9cd5d51ff591168d3301db67ca2d61b7072d6b91cc3bf2a4953e46de920fe8a2d590e2a16e68bf62a703b6c734b1da89ae6dd0d78c47a4a764a207c1b25325903bd285ccee5d9ea30d0a2e04909deaff6aba28de8b92ddd2f1777465a5fad3a004500fb6d41f0acf130f45b3c6f6add26581f5698a54a5f15cad97faf7dc042892d5d35cef822d9ab70204e2bc6837d832b8894eb1fa1db7ef3b16f173be654f39fe84fed699dea00407763f75fa1b7713621f2e58de7cfe6c7f41b77c6a677f064ae9f9e1e4e10e2d07f8b350c80fd99e18d7128cf2bbdeffa424e973cbf93033a6de17f17f7f41d108ea77e1b4744f0c5d2570d9ce20bb16d618eeb59e544171d0788013bc4509ce0cdd95345898019c956b23879a269fe8790536db3df079f82c8c8de422172c7f0a2a8d3aefcce22ef8a5b9439d9054a15c36b07676f590c1b4c0db2554c27d0a069a61ef8224cc1551ac94d73d68a6a68eeedfd2acb2c2a826483b02d0368ab5026074ba6f38c192e780c34760668ac8a3054720cd29e2bd38a26ccd6d3771450f12c7a7413d360adf5ecd29ac6d8431de4d7908a8a94b3c291ddb4ad6cfc23d0c684a963eaa370aa715981657980280db88d38d96ecbbe477363ce27cdc1957007d17e62faf58932d295ae81f13a6d6d53f23c62d2c6e6d0714aa7c8218b39301ab74c902e784fdddbfb17ff32ec38b0cdc3fd7316b7efeb05e81e901f8e0870d10c128408e9a6a072961ca238f141303a0f39808d9688d8c84554bb00692750935c97f6fbee7309dabc32451a6a476ce4a73d555f959ddf446b74480474a35072eb81dc1f92def75b75f4f4e9ef1927dcf704a02da1ff49d5d892ebd0501170eaecb2acbdee52d75d32b6ac53aee9010d45e3b9850dfe973f41f6ab4768ad60548a056154b6cc30f3fef97fce49c87358dd668f089c25ccaa6daf9a7418c4006176a1bc5b20e9ed57313c41fa7c41e5ca2ab7bb3852c58a22d8c27134abb5d0c92bbe08bb622ee8229f2adbe352eabb4b568e75587f3a9d1974706b5b3adef06f6f62508e156f9c7e0ee1ed7fdc38ed79f326cfdd1b13d4bb479010e7756d001bdfd8ae02c346edf438537ceb36b757555564d1ffb1346c9d0e76e5f60c19b0b5649d8901af496688e57b68748aea82031b4114d4590a1e9d376928a028ec60913e74d6931132b97c0bc190ce68e165254423c9d05757275669967bfdf57f40e264ee58bbe0ef4c8222f5d79fc684b90cb9b8fc1b25f8b82049a91609077dc0b0e244996532ab2776ab706361d44925abb819417244c7f815f653a404457b80dc5724ee476804f59f7234cda3feb707edd5a3a3ad4a3e809c2732a790bf37e03260fa8c6cdb883b7276b9c2f327c881167a0ff1fa161b540f2502faadb642009004090740fd1b9b3dd10e38b3239d7787d325af9e1158cf5c3266500993d88083d8731deab0c7ead25e86977b0bd0c78b8b26e63075496e77fdded2b801bd9005110d882ba5f94470ae03d98286d50921c6774bec2e98566ba174519f8283909403062109d2620c5a71d308cdf122290b66e79677febb904a4cb1c0e61aea70c0b033155c5ef8a303c1fbb9ec3a784168c57889384e9d166417bf815821e2d097a8b88a0168082ba6d46b3a04854bc2c6b178edd44b45490d01ce00687b4390c1fe2b63bae044c446ff3e7a65e1c11f1e151d3e0e9f6f521c470c44442b3450cdb4170cfa5055bd2f66205c0724e45ba15c00caa1cbda2df233555c7b7bfa60cfcdf3cb9214696103a8775a5eca6919a068b39323bbe6b64bf42ad71650dc10e21a93a820db468059af54559b23ad062742fcce6fd238ea828772a87ff1db00d32efabb86447b89e374a17f7ecd9c917b8ad0b9828beed62586d5322e429530e845837ff34f8ea81cd93642b317b83d08565e0512a9699c6fa11d1f7a986f108781eea40945e11183421f3eea4b44754bfc0a24231432dab0863a5b1efc39308d7a4259e152b56c8cdf2d8ac5e839b55f3f94d8d775251b84919bd9fd68e4c021460fede42b53a89b4241f05eb51bdca9ce44c268e881e7472d31282edcc220028d7b63391f4800ce61eaa6c9fe229346b5bbbdef7f6570777dc900767618206c6d7623bed2edd2dafc8a05eb2d637e8539ebab58f2be5164099c479a309a1044c93013a9d16c9cdcb01cd19dfc7d52491c872037c4c1e4bf0c8eb437a44000ada8b3a1e556b66df681456ce0a45a8cee15c3935b7e247c71257432259865d0a263fc1400437fd7bf1d668ca21ab1a204741dbf5cfbc9d2a6d8d111599260e0b278460fadc66811f51cca8167cb8454bf99bd26022c211619a942aacf2b043095fafea3605579f87da4d28a5e2ff5eb68bd5ae1eadc1659b7a62026608c644039f10f65026420b45f5ffabc4ff468042492da05e28cebc6beeae0c5f6b0771083f1ef73a5e913b4f2790ecd69b89cabfa4b5c990cef678f085213145c05f0d069f331a41d1c50f9f46ff28ea2423755ba93f39d1dbce46974f5cdfdbbd035b0ce6b2b51a64e14dc831cb27c3e88e6c6546ebd485f0685513d737fd72ecc5db01b2a532596498ea2c7a47d5bcfe52e41457706b57af94bac9f5897463bca5030bc099ca182bc7f14b72747b764bb522f50385e499c4d6c6fa810e3976faeb680856555ec2e1f85780cfbecced72e257b3518d823c2326fa1558781629269bec0341ca92228d2ace5f91e4d323ebb0f473f628a70b56865eea5ea76971279dc80ecc44d6d9ed6dbe121d49c6f87495073d53c812db6332808ae432a4069700280d4bd3496f8992d20d84310bac150880ab75ef3844dc9115fb7a6c6eb21442720a52837178651e59be83696cfaca83eefde32a1a25d38db0bf01dca6601b238503bdf88392b724f7ca0704fef4eb9454adfb5ac49e5766ef378453047b663c0a0ce7bf4b345c7508cd4373da90e2aabeeab80df1cc1938845bba763c165ede3400d5458fd623c492af24a1f3d6150bd4f9d926f36e0277b0b1d3d32756be4eda0920d5c6790f92244fc2e87bffda3e85dcdaedaa533bc25abf6f00dbcd98f9040391328a571beb4187d55039fbaef3fe35aef154166639da09cb680b155589d309f8f6f1d7101e3bd637174b82a0e7853383b42e9906958f8844ac13473d7a3900dcc0fdfc23d507ff242252c0b8cf7df25e87f6719a42cd57521269375cb5660f514b6bd4d8509db783841782166932774978fa18d8383e3d92437a243ceb110da1f1436f7b48610c143949d7b4f06c1390f629b2f2fc07f1759734bf600d06038c5932a89050af8b2b1a2b6fe781350d4284b202a6d2aebd2c90b8c80743680794b525b7023509d6f5f9a7612e7b1f377e9a4e1072b8197cd37d2ac0de3850038d0f8049fb3c9e9b28af6301ba9f0298a58255890982218e16cdfcfc31da14094e523e4691594917dcd9cbe802319297ac8f8c5ab268976bf2ddd11958b9760b266b1d5fbb0d9ad025ccdf44ba5305615a93abc41d6829579c602f9af002c709b2fafa51f73f4d83c5bc4f5e7c2bc94690fce45ae2979aae4a80f224ace8a7064f2ae74137a4649707ed9f1d7e975c0b427feeda0c5e99657fe430559ff3db018793b656af0e655fa4792d411cdacf273273b986b98cf360b705c29739b9f403af6047bfe15d76f418427d075769c504f650b7736794ceab5dccdfaf0e6fdd0f4c137c9f828337d08e4c9ce4b1ed40ac789a3ce3f6d05b1f7350b69704e0cf01cfe7ec6faa90ee284a70d06b965e7fd50c6266bb5b73bcad46eb8bd348ada10856bebe528b3de6a4fffb3b5a0312ac53aba32cb608361c70bcece2a7af74ed0ec34d1866b134e078a40183255018e6abd5d639517de2cb09696ab059d7b80b011ebed8af53d241c6ddda9ca378903ac9fe3d210c11e3fe4667591a5dec79dc08150104a3bc7a6405e6f811f1cd1b620d2494103bf83863013fc2f5868446ce0c0a5da4542a1c34c9f739c1beae9776256358cd384cb2ec81cf73b3735845af0df2b6749921f55555135eca493758a674df0f6eed030c1be3a07c27b1a8239d057047530f7c127892900f52f49032d2fd3eaa0637f85387f56b0f344d57a56e0302525a5dbcac6c4b99fac383a039e13a0ef985ba91db9255f74008d9c424900ba39b1f98e8a984f7f1894d51470cbd3b226ca9c3f7321f0f07d48650e1393a0e5ae1420db52b16779dc7c16ccb366a19ad6f8e73a5b1bdcdf858239792a6430e0ec0ec5380228ca0e231eb2e7fc6e383280756c62ca04a1d98053ba95d9c0f03dbb4364e0f5d4a284de5bd4a6a5b9194fea59880e80a23ffefc340380806220e95d2d60b210949ecf2467b915077834afee9a36da3de59964763651e72517c0629dadbc3a39c4e0ba14aa1a3aef684bada7919035f8dc0a8861530dbe1df3e0fc4a886dcdb11fc7e5d3c56cca04fc245f3b12ca6fb4a0c17f7b392fe8186240ce5be1473572071ea9c23bcbe28de51a30baa4ef44a7bf1e5bc0ac92ca52b930342f4ebbec02877e7eae2054beee5cd653d95d0a08e60e8be667b5461a7344b08dd5660a0f8427710d1a73e3f3b28c682893a0d5c791b9d71cd902a66b168d3001350ff13b00c0499879996f7fd9b1659ac0b20720ddf4c63e7f0213ae088d60749a5a260cde7c782b59605d1353478f244b604be53a8373b8df8e5ed976ed204ea56844b8221e09c20250d019b03058b560ee3e1c23611e172c569dd6ea6ae08bfaae529d7dffe02256df3435fcb500818c2bebf0bae68f1d955878f976ad10c86609ecd706de2dd839ccff0ebcf5bf855d728b9bdc1f95e73a2062546f1ca0a571eeb370fdb9b2150ee08e3715c6600bb46a2ca0b8b4e12914912d36bc6eb0ecc5c3dc5f49fdf4048c3e1b108b99bcf9a1754bebc8739cf26b109dacaaa150aab055fab023f0a6b867adde689b1b11c54ef1a1cbf696c49c98a83fe9f3d060ec39e67a99e0883c258bee1a8a1b856362284cdc3f13c50e323d357617120fd0c7dcf5288909457bea42c8212a33a76f0192dd926c651b66ad9069b327aa26508013a685a6356d1733d568a6ae47df816898df72fd313a5851096f6e580426c02e17dbcda401890daef8022e0c2d293bdb8cf1540eb6cf440cef3a8b4a0d0470426e557b61bf6664609228306651c57696205e7de5bc841b83d0837e342ced401758136028331a80804f6960dc37c3378c253c459d4d54ad39d6910282110460f30bfc6e693fe579f5d2bca830ed1f925e886493fdbc11437e5e5fd4f0659e505802e6634584003d4cf50b7fe8ee8a722c5b13bd630111ea6e6bfe7a52eb19f00c5d3366657dd25413a1895da80a33f08402159db60f60704fd903970ca14ec08a086ad2a602023365d4d59a470415f4a6e127749329b9dbaeac62a4e7629ba00525f680d9565389f95f1f6ee0fff1f5ec3d78aecdc0bd709fa9bb41f67b6bd0568975259a68aac93928c1fc0f7b6a3eaaad03421f3957d436382adecb3a7bd061870e914d9fe5f4420171c61206b5c4269f202ea1eb44c223ecfe39e6ff29201897aafa986c136046fd0e5a324fad0c6974abd5479d03cfac5994561d490400fcc311175e761705a72443daa4adf3e5ca2c187b82e45dc3e7ec65b70fac55c0df21bac7369ac4d7a65331079515af417fd32eaacfe15416ea8915c850092720923ace3161281ad849f22d1a8e1ac5d21f17c2c8554d7210bb19ea4ba3838890601de61c813d20c94ec495b6fa12d8183fb1398dae62203a476e342181446400715da0c008ccc5a9cb7a086be6c7b496d0ba22cc5e3a1c8b91b78465599e9fd08d97bbe66ca22e1aae3d601daa1c73e5e08a73f5aa4714df63e5e989d593a9a01fc55bd7ffdf1789a3c636514cd91725e32147a601faf0f749c6752d7b8639f0277a732f3f434724be397b0d16a712ac81247c7a381e73964e219c1fe2417300e8355b2588e28936147adcf664c84eb13f2bfaba1c9ee5b5ae286e2fa3d25110d3fe2a8086cb5bc6c3c2f82762783c30434120b724d86db71b3287dffff71ca05d04602f9c4f74f6b812b927b17204352f2bba71ee39457cde51bd1808f7fca04dea4c38f44336c2b703ef1345c6ec6f949a5cca1f56fb70bc30cb9293ac0ee07372bd18a2422debd322398bcafa47ad1cb61da5dd9108662900bf740f530330064bc7f176e911ceb3b4cd1c233010be45ce08bedd19c104e268a7a5c91603003ac657e4ad70eaf94f4334b8c35344fa272f25b2575c6fd0d8257e522b0c8540cc284a71272fac6463fab48c7a0ee6c6300164a54ea2a75ccfe8d2401cb88460c800c590197c222b60a29a34b19bb3a04c61b21cf665a2b7e3e00bf62d470b00e8f7d6e74268235f1d7e49f79a00e67ff973197ac77489a92074f12562f5972023c8b27817ad9c7cc28c4fe613af01d0b5194e030576c6a9d79396727a3224d0e976d7485d695af3f5b6843d1dec97ca71f8e9a46323670ffb56b0275a36cf7043c64a518afac7a880dc553bc73125743757282dbce536604c1ac9641d7c39501cf2da56c089f588cfeb6d11b77665b2482adf179a3f7303931bd51be49a6970dd0a0eb8031ad681c657ed6f65bcfdba3a1a33915a3e4d43ad3606bbb25669b0058cc60b8c39ae98114f9160c89ca691ad5ba74787a9f881e1c1f186c8c32cb0c093bdbba9c153280cceccaea206ef9e181914e74083b87eebd872a260859310dcac7e1f35ac927451449decfa3374845ac5c96821721305d32dc743d2d03230b590f9197e475789067a99a1a3b0e0359239a763a867205debc0bc0889f07c70ab9d75119db2fceebab2f8e76eb3993a3cc4cfafe3393c82f6d3616d8905abf03958300845e49994d0d965594b30649cad8b456a59ab9aee50940eb44e0edb301dc27f9dabdb6e906d89519cb36d666fa191874462e7220906497ec18578f2d0a3748f8eedce8e4af65c917e12587b6853d26a6ccd59ddf744e9dcd71bb092d08c569591f625d26c109300d647be1f463d29acbe7dae4fadcec0b83ce65fe3a081f0f0e21dce6846946902b311fe462fda33eb66d216d3fa8169e1c94a93ff80c97b48f61392379f37bd9cf49c2c042cf5490267954ab4bdd5254ce80c4f5bc0cbb2d21afaf161b3eb12c0fd2df2331bf40057a993e74b7acecf1ad3e86ca6502758c915e373a5965a206c08653c87d5173a194028efa041747156dfbb3bb43006d85ba8a45d0a93e6758f235b5263909a6ef92cb0cb9bb5370890b3495e39a028ce92737701d3cc852e1c9277f2065c122a903713b9e04d26d673ac9438f1005656e2e27a8b6f0a09a456ad91b02f1a8c82ee3f5865658ad84bc6a397def4f0009bd585b016deef6d2d192dba52f68e914002f698307eef76d998725a89e94034dc8cfee43e6760fa900d91144a673d748630d135ad7d58c95c9bdf82dc9c70f27d1e83b40ea21358dd1fedccce1ea6ea94fdd9f6a354a27b6e32b6f28ead6010e0b822fbbb4e1d0b92918d6583b833c6011b115dea0b4b23b7c07dd1b53db0aed46983f8701daa73a842ee46954ab3c37f4c292b1cf3ee8c4e14570a0a56401b22b3fd5b7b79e98e939cd3c8ee0a72204a73b76a2f5356677a31af7629f8a0f871aa5fbfad55fe1372976327494efeaf5d1733f251bb4a8d0464b379c35b10625a973ffc3b5e548289a4e3638bc13e2c013e48e1fc97f142152aae04bd1400bf774c4713da1af65558541bbf7e1cf4fdf423b2090b4dafde6309638b920c1039a6cd088da20e5f452b7e030a16b54d2250997f71799f32b781583a98805a6023b32e583c372a90fc98a02a67f9a381eb8cd42a23ffce2bdd10c895fd5dd3f0efff20f7716898e138571dd167512b4fcb477451586b7e07e57ce6018f4aa980736630708d382fd80a12a42d13a9361fab3322a8daa76b4f77cfec215ef1133042a18f449e0f11356a3b2345ccc4d09b9ff1d0d27bef8a781489e0658b0e3a0081dbba18fed4dc56f0a517d6f5a33224da73e5cde5b55b72d356500ad3aa81e06b41e377b98095c7aa45647d148f3990c52a54db6e4233c386e3adc799aa1050527cc50e66b56bfe04d5361c58b1990a28461848e396140c7d37b9a617ef38a05732355536bd51f02a3509794c7892a5c60101e0288468a5f56d84d909b6dd707f80a10f50252552c88129dacf54f10cc476ba97bae7aa0cfc61804bc1271af0fbc6ca7ae18d79e7650af9d0db876f9cdee955f1084d7ef809c40a41e56eb0c0fa5722af7cadc0b0a8cc910c7d0bb10da82e4bd7b8948acf2c8d1115960fc1106a3bf3c5d083628b442d23c30846613647aa19a8f7db945b92f42fdff61558a0dbc15d4516598c366963745409a5405dd0b57a98d4b88db7c730b1d455f89b8071001a0d5567b1d2f3fbe493b2ffc3952752e595cada4bd57730ae0eab0dae306f6ae6f7fa0075a9cbca00a95d03fb70f510abe9f89692665395a27e4b6f0bd032c5d9e666dfa02515ce8d4645bbbe73ad3e23add5a601928dc151654edad9b0777c3fc7a095a8111c9df14f6da7e9ba0a2adbb48928b934fc1b89efade93230228ff758858adb3e8207299b58512aae57ed2d7d5c718e1f3012e3ce5779da50a392e66364b4f8ac7f6fc0c593ba0f6f7638c9112c8f113ffff1a3d91ac869507bedfd873c51355c6ab1548801754cbb214f645a5ebc33de6d515496f8c34320851ce1c964a23b804a74abd8b1d72c7dbd2cddafd7caa7a6ebe79208be0823d086b7a20dcbb0277f8c0c8d911e9fb2f3f781ec45adcb86b443d948e5d00664b0f2621713fcfe1ba9de9cfda7ba8dcb5ee5161a5f1290c27005cec668815cf3204fa1de05c9dfbc40411956fc57bae88a64aedd6ed0b8870193a68bb84f2987b0f6e6520dd4c08c8bd476776df9d995e13cf99ce46b27a0de91b98d8761412850be884d4043f637036538451bdd98d95f867006510c4f8c2bc2e55c1ed43d8e004c666184ebe5383ffeb3b08241cae4ae8f51c5b8a4efdb4e214a2936e3cc59f05b74e59f29717bb892750ccba9c4147569fab59840e6b15048d813f4eba843408fa24c8d5e73e4dbef8a5a2fa1b991bbe26fdc4d4541193a1098578268c44bd013c78ae3938bad097c84ce4ee28cc43730a8ccc6f82de1a18593adf171f02220e3e39806812cf13b299e91e83ac6b27afd00d32b8fe3f2b065408e13e1dc8aa05fff4f9ef3eab213194cb06cd39cae115e2411f9528844ce676c00b87fecdcf0b40754446633186bd42014072d0f43e93289331cf962a1047250ada9ccb35c50a7912879d689916d596049e79eb3d929f4a1ccc546e2eace6deeb649e1fb9e3087a80d95f3f69a26345de7cecfa5b67ca47305a621c2831274f7aef0820af11069a935e7df0708e7eba65039175aee8e1f0ce69205fde38e9d870aa56c619db0dcb615ef4acc43891c0d1d9be8acd435356a11b9fcd46518bc973d151d908060544882f1dd28bbd920f6e91ea19ef8870f439febc4870097c8ee5a162a63b1505eb7b1e234214aa0bc139a4765cba1dfd796bd6c810d3ebdf0f4fdd49929d21048bfb23994dc63fc94ff24153b8fb3106e9e020c581c3255da83154c778a9d30172cdc9260119d8f490422840490d61c14235f6d94ac6f2acdb68c53d7f5bc1093a5978695b8401859b4ad9300d615d8e4aa1f52531af363b160de75aa00e8c073e9c984aa68d49bf702eafda6157b3f676c71f38a566c4705f90cbe2d359bf073b66a2d7f1c02d3af58e1b63661e672538f5088ffdab937e8ca7491168770504ccc4efb67a7bc1cf4eb290996a1d9fc60ac1394f6d3dc7b050bf629ac632bc0d671ae81903f7bd48b2b06025213b8512d7bed90f6c07ef405c00fd0c96be1b084268670884341ac8fcb7665f841576ed93491c2b9aae37611a525d88b8a4920841937c9e09f788b904f31a095057c449080165401db23ba6837b32c185c0d9061a459526a17513a8c51388603d3b5dbe65ae019d5cc3f486fca55ce7a9e36603df8eafe37bc1924c3eb187aaa0614152a544c6394098ffb108fee516862cd001f8f27dab2b889e3656fe14197fe4252e3b99c103fb1a958d6dd6530d75336d0de82326797001b5aacad4141c225b4e69c8488e8c6519325f59a5275cb2fa1301263083e53ea8e4205e7bdab9358da5e01e22726830e0085673c3cd78266ee10c68f9862ae979c4de089b412bff3d461c65cf6c81dc107dd341172f3683241c0c0f54d6a8144800c9048b98c470bc388ea172df8ad021bb9ae46017947d458a07b8b3489c277bfb6ea7ebb8f7b1244fa70c706e8f8a3de86f59c5b556f7bd010e411e37deb2b79a3fb61a20b856ad3acda78bd5043237c141a03f88a44f56290784a9e52584146fa1f45f8bab4450cf02678190bf5c737d119e9f955a7406dc02d4a048b5cd7e096f56196cc930fe5313d742607cc1b89110b9f037f39116400a01ae4cd4b0a8747a5081648a9c325fd73edfd46868d6b33c44ae6ee9fdc9b50943f3ea18b564bd1d52251a29fedb36ecbe335b786908333b7f9c11f5378a770f3d7f23e8b637041a6e023e911ee879fd21ba7326b1711ad57d397affdd47610844d458a8e05bf5f48d7b299d804299bcc66c6b6971bb90417c20962ed4ca980de9ec68cfbfcf6be9b56f09b1f79d48d3e24a13c16656563b4952024f7cd1d408a12ccd096efc5036dbd6ff817384b71f8f4133ebb7651f4ab16d169d5499fd008645dd79f73e1130504989b5387b8c051f55d3e37158a4b4f4b33858cf6ac60d07b975ee951d1101fbe609c016d9bd7bf9398b5dc44b5a91aa82df3507780209d911f1436dd78b61552f2ed50fee0178c6259a181c50d7d52aee9468ae9c100d1013ee3c61d0206b0f50b55a2e2450b55fa93e1adbabbe19c1a0eb0b2ac64500cbd9791f7dedc50a5107abff6d81ec8ac25dc46ce5d32d228007f4279528380861cd17103a5073f6f056eda5e35fcba92c3f6ccbc0efb3c51bcb6b7f9cb21b0a4de73aea56b9f9cb7ebaeadb56480f6919e923ba2067c75f745ea41a48d44005da2361d6f02b53af0a1ed37492c92bc8afec498ec2de7a4efa74941a1b146d0d3cdcb4ff3c005bcb63d2c7c4ea403ab307b434b66b070e04b371ab194a88bb075d5f328cc03f4a1dc3743506b14ad00ee6ac37c14302cacfb09cc86a483ee70eac92dc45217f1695cc5216ae330f39f1f88fd1941a84399c96ad86f5281d6c03836e495e161f37da8d774f62bb40bfbff3e64de5c138d141fa95f2bf8eca56016dc79630de4ac43102fb52ea7fe3a02d3ef0a481cab9eec8172fb48f5272940ce9264b6a594513c718622cd35cd5cc0448e537a76008b72a3665796a6e464a08dd12860e989c1c70f42726fb2ce441f171fa42d4c13e4a047eb3ada4bbeeae074a4b6f529779046179fa7f94793996c7641819a23332a57f7867834fa5839d0fb2bc9e752513a797cb778f89cf5b692c0d820e550394e3c3b429157931b24b0b08f33a9ba7f8cb911477347967c51fba0f086655bc9e5262a8a29d451419bc040fc180546a884223b3e09745795712c0558dec7dbda93be5124d4008156e310208af960417df0ca25cfbf8800f3cdbfe12a9bafc0426216e1c6ea9c2637689026f289d74644b89c32072ebe5cedd1882a6805770ea33a73929fd44d07d9d3408d504da9a0257bc3a72aa156ee0f676722ebbd9d92281224d6d5afe2bbf9efb057c3f4f7eec3088c8dbf93d04318b2b1adc66ca720382c82b240c954f55995c0f0edf74d8f036f52547181e272ac0aedd527ddbc6fa303cd2c4b2da3e1ecb9109ed2d3cbf3b45dc68f6c2d1da3237714a3c5bba29ba18c939d99e9dea434ce20f5bffb5bb43e6a561c436aee90b84145f213a4334eca04fc4404302028620510421a929eefaac3e6daeb4cc3da27a99f9e8e7516e315cb62970da5af5a27b700a1bbecd6b4faa8f157e3b0aca1b05da21bbbb81de1f98d2a7e1c22b47821ffc0f1acac7a788c34a66a5283d71394141cd204309efc0c0c4f90145514fd754a400 true -check_ring_signature b02afe8d71a70f12e1ce71e5711c9e51c13ab2dc80f8a7acd5b86ffed3e0d905 2d133ea987e464b499fe2e79bbc90e2c19c1234cc377d290723c18627c5b371a 161 02a3330fbd0965404e000a932b0bead89248e34f239a85cc4c464c8b9e686a04 94b06f86c0132f6872d75699c2996ab0aaeb203d70290a319385356167353a68 972a7dbe7d70a627c7cce81bb47b21d0c755a84576efa35c797c012b8d2f9ddb 8cf8aa23cd064d0945698fef68547c0398dee37ecfb64fa07fb653a1cb77733a a89ac60f29d7ef66842c26e31f1c9c1276f204a71689810d6c5cfb94b0b420d9 00a42d0ae4c87624cc7524eaf9484f9391abeb482d80989b70faffac4c0292fc 6e233305d925c2d74cc983c72bda460ee6dd51e4a2ecfc0ae4a5330b6676238f fadff2caf56c1f1c3df78d1e0cda5f36b433a7e17637456c6cc410c16af8e923 fa817b69c9c9e29ba4af2c9540ed4ff9133818db66356d1204c50e6b1bf0945d e826e44aae9515ed974d6182e5bb1e9956b569befe275fe112f1024a0c16ab98 ddec67c4b6ddc7148a3e85f394b00658bf1d347d3190b2271b5acccfc0561548 6c21675b0a759182c1d866d8cd2f03a730979feb930cb7b9add19744db6096fd d82c3405b4189427d2e3d30a3417fd2317b51bcc176628f097b5019cd832bd59 36413010876558ac1b1ac4beb9512417242568103b9bd70f00b9a526516ebdaa 0e7af257d449dcbb2936f808b5f526124e219106e38977679c6d30ba610f797d 08aaf58e3f5b934dc1aaae7d93f37afe71765148c653c6cb9424d6717f39d2a9 2e066ac0e8f9f466c80bf4a8c9007643dded085404ee135965acd4d9249d964e f41377d4816fea4f2ec7e7c26a3f8ec1aa049f6ea423515196893f113869f7b2 e8cf1c8e47d548338d8cd931cb9020687a74ecfb3c760c6ddf0ebd96027f68f1 001de5ee29c0e74cadfc13bdf3fd9b10e579183443c2a39d00762354972a8023 40b998a79bdcd5a019064b73078fc5b762da8d7b2c83fd82e3de4584da56ee62 2a27417478f16a759e2e10bbc072f0aa083bae16efe2a9b4051cc7cc47e76243 6be2753a0e0c5872705d85d066ffafedb498faee96c2804862c04cbacbea9d99 76312c386b65a8e455a969365df9cabf3cd89f676ee9c5269a74c241d5664f35 51ab6dce8f11ff35820ee2abb65c639480791a9ec484067ac286997d0a35b685 1ce972303dba011bb5539d331f218b7983a4d87982342f1645cc749727641cb4 0546041794bc975d9f3ac24fb464d34678764b65e3b2fc308709f7b3cebf62c5 b5165f09af0f87a1d65d0dac57caed9605eed364e33e148ea76af34b0e87865a 5208b4833e5592042cde7e7c231a506e38717a5291bd5432591b6210541327c8 2481c5b0c42f963971e493959ea21a6b34a8a9a22f59aabd2dd07061db9bd6ae 8407464895c8a54531d22be99f049d6573ab3d38d06fd24639a24be6f5cee377 7c1fe0c02bf90aa03f1ad344ad16fd07b5c8d6aae4408782b39f9cb546eac681 939fb88fa2329e5141740416502fc65dc80216d9e9982f03c664071f15023ef1 7199800a716951ffc7c3cd269fb297d77dbe927b2d48ce4b05e517b333eff630 6709ebce099064e1d521521fee0c2433224f2e90a55e897e487d117ee8be3e57 3d72765dfff800f65e21eb0543804674fbcb0e5aace04dc8b5751974c0cd420f 1e06ad5eb5c2401d6ff5fb4a8431770a213b811bd39f69ff31c9a23196c9b507 a4337d6160be4ec515f80e00a05a355c39742ad34226d9289c915ac8cf53e12e 9423e787158aedd4ae60e6ce1a1110e59afe4f0e89410f9197e08e11414b3e71 f832b7b4c82a681bc9962e9356343b454ababc122ed16e7d7cdbfb86ed1e7b42 7effe2f249e1fcc37012b3d4457f14330f02d2fd660485b1ae7c2b9f0cad15bf 8d6859c30f98fbff5062e3596168c60ee99e4353745dbf78012688c345f6af30 f516f0cf086bc4903e87fb3afb4f9a2b1af58d78c0be85f081c38ce09a6220aa ba9fde69c747d93a6a438d7e82393c3da3ac12633b2fc2b040410ed001bab363 28d944fdba6343f3a8b677fd0a677fb48d558b932952fa370446c33ed00afcdb cf8589adc8fc7cd5f5beb03a9e1156b3efc7a6fd70b84b887d96d754a90a0989 c02ee844965283712628879a467b494f5933162cc5f5009ae864a29506c92951 8103112c705cbd980a743632349a3d0ae0322ae0c66cfb90ff55fd51ee047bc3 5817b5a5396bd77e5134dd3be43d2e9c10e6a3a2ddd15b925e678fa315afda60 d323b8742c3632564fcf96922db8c82bef5909ea17324c88eada6eb105f44d73 497ee66505f249fd1b9924b5a8cc48a6340a3ba42a986919402760928a09159b fe2870032aa8d93b22c68ac7880c413ffbfb8630fe44a2451ced0481cbf433ac 1214e7e74588650a00554f83cadd15e16640044d026fee4e84fcc6abbeaa8a77 cf9f343f7247b00726087c51731c1e7add039eb5b9d16be597f051682b831900 bcc1511d9b283d4413f4462859003f7cad735a56e8ebdfba575b210fdf870c8b 841ad686aead33ff4f65c8c7c58495263b61ff1186d113943b099dad11fe077b d9b474dadfed9ec160dd8eaef3a993e5b19e059d1c424c2fb0486a80aa8a7842 36c44c2435d71b52fce3d3b0485fe91e8896c687c905c9499b039ec24d0f3f06 8c00d0e51003aab8481f482404828425dae8e2b6b729ee4cbe4e389422263707 04e699bcf6668c6824a1f35e0cfc333ca852d6d1a2447307129ecd2ffcaca6b3 adaba25b24f0cdec844d94fa4d8bfd61f07d04117c1dadfed20fe394059fab98 be36052f1b923eb4452f0fe14a2d4633120de7a487ccdd83534e63769cfd9439 6142d7fb1a2cbd9c3ed98cbd6f5bfed863b81557db11c7ac89c7caf8a72ca25a 223d59739bcfe2011673140d088d032259e0fa45e7819f0cf3332b3f27cc6e81 dcd33fd1139ea9626d45cc56704011e3d1575e902ad376c562ffa8a374f1b463 27ac1f5159ed2fde9307034893c79e8ed45073aba44798bf4ea489e83d6d6380 5cf706d185898f989dc396f2b4d4ed13e211017bfce3316cb92397cf29cb0098 5dcf59e59b9bef265473c884698f9df3dbb88856b485159de3181a0fac7b00e8 59dc55380b3a7804cc3d7f8ce949119197e2b5619fc6a4ec54ece899b668e82e 8eb3b030a6c525fb379c80247423c65acad183183a617a480f2a15e622c205fe 59351d9dcd8146acb3db70d25193d9fbf345dee204d1038370c3bf864f858296 f48d8104c7a524cfae71de5dde9b8d49ed4eb0e551ce7c27ed94f80cf38c0d60 88d015ea2386b94c777901ac81491f69a6e600b5ac0ec35d02be90020770d60e 555344507c025c3a1dc0a611512c453aef4870909fe791f22b16337748293a30 4cbebd7f2c339825491eb52d5e05f9f8c035abd1b6d30c452b7de7a8c0bc0269 d3fd1244803052ed2193bf781b3b7bba02488fed36af6454bc0747fb1550e90b 27bfd85f3a8e410f99ba13a97e363d8c600c9b3d5d01e7769267ae4279f66c75 640db3aba34143473b2aa0b7bccc6e76f2bd6cf6eeeb4007736cb4e2d561a626 2ab6415f6ccc257ba42bc5f53279ab10b309907d60f099c4d1b35a7dbd7f2d5f a0086a8a57adf2fecaa8a3a599d0551a34db3ed07f5e8d4a2cbaddacc218ab0d ce4b61c1393e55663084035356a51a6767184b24f2adfd9ccedcec126098b2d1 d6fc5e574eb1e466e618cbc100cc34d4a776a324f91e5b18bd133b936390dd4d baa6f8dcd95c5673cbe19c37a7994ae24458a54324c913b8d9e695662d61f6f5 26b6c801da4731c7622ba007641d5bd69fe549bb998a1cb9b9904f4485287626 bd0c452a27d30668f049f4593b3c6c096b21fdc2f5cdd8e4248bf068e1bf46d1 4127cc5aac7fe711e7051f0b9c900c0b6f722c24fcfb101590fd5c926bb4870d 3cc0fb57b784d243313ec8ab0c0ddccad15d3cae906745d1ce106d86f01b2b9a 7e4cdc6ba93735ce81c5bc1bc764fc3899a8dec0c048243d89c31a99cae4ae61 c951b730100607d16f1bb2f9e769ee1a810e36a4f6ff65a623c8836cbf6526c4 a51f71f473e9f5c4c951f482af54281b4fba6ad4488f93c798f7e76de66ae62d 4e5b441ed8cdf6edd83d066628074b05a5bdd092c8218663d0a60b6f3c5693e3 bffceac3774c4a117095d4714907fe088c34e9e64f7a041b732ad359b29c1d1a 458c479292aa30feb2fa523970af1f6646661f81395cbd0289caf6514c06a5a8 c9d639a1f7a30d9cf57974f052ba9eb7a86ff7c937d212d5cb9bd3cf26cb37e4 94fd2c2d5269ad0dc9910146e0178234937c102c0e1d53b0583cf01b76fd1f28 7bbc897e703a56fa07fd74795121b8de1fcc711af46f02378f3ad78507b26659 0d48a03cf6be56224f1a488aed8dc9df2574477ada56bd1acd2d142dd8ad4847 47e3443a400b411228d44dc9cc29fe91f022b461eaa2cbecfe2d13bd6551fbdc ed8b8d58af6f78e26f5f44e0df148537b5b027fc45748fb9d325f0ca48470069 7d23b305d95d05053ee35ad05e3ac58eac1677c0ccc15a1195cbe6e6d0bb7378 23f6ab5fb433dbfa8d62a493f603175beab7c48ca276d12aa527311120d44945 a3f8b2504b5e56a3413d9c57304500ec999944bfc69ab812d2339276bc1e99d8 3a9829351999d497fa01cf9d8fe21283a53c8b864c4594d55c30d19c452625b8 77e6d796290ffe81ec6914dd93c1a7f32d77c9f4b78c199f21c73ac2f18a65fd 4aeeca1acc1bd2ccdce2dac2368129c5335211e1ad8c97e49cefa07080b2d957 6979073b8710b36e0135563803a7174e4c73a532ad6136cbbabe6261bc8f3646 5494e1ed87ba59c2a0fa9aff8bb2ab3fe14414d98a805249e1b6191be563e1b2 0de4edc7f30a333c8d14d526e5f05557776cb815b601b16c97d42f8fc506643e 7e23fdc6771718a012fe3f693e7d7468a8eb46c69823c13912c0ed7ebbbf4b68 9d06db081cde62d1fb951f60679d63afb696d3b88e654c312bcdae690ec0a431 2de7095a96c9f9a1dd0c2ff7556839f3a3c3222987dc94a5633082b1f75641dc 6f085b15c9ce6eab7b50f13d59139bc2fa3abbce458d75365c36a8c6bc015c3e 7028c0ffa6a1b86174dc46b99554da7e11aa99d7bea6e0f326bac8927b4cfbce dc3dc55d46e73298a8ef81e72be506fd5372d1b68dd3b864046af413715a6422 02ff3d62abc880b49329da084def06335e07c4f518e25199054b151fec84029f 0939ee4d34e9a16799c0da70131215f6cbb1af0099bf43a22fba928a30fdcf2e 153fd51d8a4cc4226ae530f9190767fb8548022f998724049dd28cf6af1a38bd f1e93d7ad707189da33db5a093af7e93ac74db3376470d432b3394251b951307 3cea5ba0a91d8ceed76a8f44983093b18420baa61aed1349e97dcce21be5fb5f b175ceb0f27535120582de0dc9f157745441477d3c910f92161eb882a7e7a678 9cdf19fba139c3a145af3f9fbdb0877cd8d4fa770f49ade66db30784a0a1e0a6 abdb2e461f3a4ef06e5bd7cab1d2437f7405f40f0fb384427b33e973cfa73085 815ecc842dfb28ef692e6e064133538b2d8c6e0b3a4032b95de5afdc9afa5604 fd20f0e7428410379aaefa85cb6d5fb46ebcc5b6a21097900c85094f8c997d3e a258f37cc75b3848cf36a5eddfbaf4f3ee2bf55fe6403f6a2d99a86d2a32ddb5 6f85b8f8cd7c292c2664b630f620cbcd74b4c3d5738a5a9a36d3b5a903617afb 587ac4b9eb000b7ad5284241ed3c9907f739e86237917a31d7300bc605b641b8 ce8b71dc84877b1f103fd56b27eed8a3de40ca1c03c113eae6c6ff7a41061bce 69c04c1a0bc5ee1e3e1f28c15979cc41dff45146b176d034a3d5021d41eb4ea7 721be45fa0d67d94d44b5d13b551833358afa651a6aa144d27a806c09bab4ace c55225cda40b7cad7776a0b9dbb342b6e8f5f55aabf788290ad27060d0689c28 7b066fc1852be120c22197dc6170fef1a6346b266c2c799af2bdc5706a8aa46e ec34b057fe324c9a60bc028b01ef7a79d36c0efaea780e6f9b573bb642282533 8ac23be51ba62043eba5488d08c2ea941ad14810c61c0da670b472bf1e31e0f2 a7c12460355fc020f0e031180c24265fb6f918a1f62bf5088554ce3e84382c53 7b9cdb52aa6a954eb80476ea215d79b531ee957cd9f9727cc7be32f71ced15ce 61ba5866574309dda6c2f722430c1ebf6434674747df28cf4c9bbfd759d7e433 6863070f166574f9e3148730d49b424c4c2534dab98687658d819f40491ae8a6 cd45604ac8cf618da974cc53eb785bf9282a263e9ad0d6033f83dbc673087c49 6c6c3a465eb92830339e8d100c0d36881a61d4f32658cfdf711e75ba2154448c 2cbd47b803aca2f39d1cd537ea3fb44df86f98b0324dbb0f0215d2131908a334 6a451a2d4ac60cc3116cc48b8c2142f58137ce007fa6d0d09b8242760c721123 68d632470a2ece49e37ee1331b890638ebd2d34eca59926f19fada132eb52982 d0596c014e947dfbbce2d2e4aa7389b418c74e02c849b9e252a0081256646e14 66deabbd11ffec53c48760aec569cdddd0dfd468f714f2c6d396087fec2a7a68 9cf3f9192df0e2382a812bcf7fc89be22d4c832dd62f0f5f1717aad5a76d85b2 cc2aa4685427c972bdd49be215232b6b1519302cd5c9e4cc0bbcb0f56befdeb1 3b449296c9a374bbf1f7cb4fcb8abd22e81301a75401dbf4218ba43d7593a454 7983a28f64b26d8d4a51b646bbc1d2075825cfdee1a8afdc279ef208680bbe09 2c1dc00546e2b7c100ca7adf77a01090635bd8d28e5ed0ffdfcfad0efc273ae6 439721aa0976500b6e349509bd7ffb1e2d3dfad2a668f5d4a0515311a08eb5ae 59f25c61d79d4cdb7b8cd3cb7da8443851e57f0027071ad947cd232f41818173 a6c60ce6b00a7c384e767fb6e806ab890a22aaf054c1b1e4afceb79aa203a7cb ff0cc03f73b92529be00db3796c45c7d74566998f67b6cc57530ca18d202b720 987976bafb446185fb1156a66aa2848f4678caf4bdd6fa0e31efe63fa529c4a9 8de1dfe5ca18e414d4931ca5a06df20c9919bc3b09c394399c89423c9252a684 aeb7326ec766d35d223d23bb775f21f44e1232315538c7e39f01152bbd09e1f7 41ad594614f2d947e57123f7124c37668947703266835aa39967616e5a51bc65 8f900fd3bfd9cd69d9ca1ebc9589cdcef5ecfe5d1cfb17f48c428bcaa9230f21 f3b028c37aa9d609cb10ef2c30376a7f4705b35cdbaec08c7deab8fb223f0349 f29aea977cb57a9c7f8f89e4b8e982eac795ea16c224908c70024938ddc9b0eb cc82f467184ee91a815a69c71a9cdd69d02c1987ddd02fccd1e69bc5dda34b023483b79105c7172f7a7e94a92b2c4bf0cd3c923d631352f143e7e0651e258807b8f009ea4c3056d96ca6118f19e2b4db6390d3ad14efbaa4f2cd10b486b49500792de24acbfb7207fead682205f944ff30fb4de5d15fbd08d3ffbe656987090f84ffe6633584932e6f0521a8ff35eb3444dc532a0e1c3a28ccd8d33a580f36035d317b1f86694cecef22797742fb2705c4702dfa6098d9eb48a83cc821e0100f84d469086ceb05560eac8e0f0012628e0f6fcb5ee3c8cdb9f14a2499fce6f102e1fab46794d0d34d8f234994f1280b8624cd53b77d77d3aa822e094ed76ed90f7d3c75a2ac120dc38d42c13f4eb7c0c1d79faa26cd3eabea1104c1b7d51c15024e113934ce98e068781fcacf2539a315e42f7ada38e4cd5518830ea101cd03059b52a31388e8306773dbf203b92c45c95885573977d69363bae4791ba828ba0540260c6343287927696f638b62bd5e4a4dd90c5dd4622040177eb1b9bf953b0221ea6739346316c3f22f5a78c414bb8dac09d0250b139b626f33268118bfc50c5b60a033ccc8eb727130ae1302177e9ab27c6eb6c239aa8171013187be0a5305fa83bc0b2a9718217f54e1ff71fb38e72b6fc71fb26be875039703819b962b0c5095b38e2307358a9a8fe1c3a787813d169747c34b5b3cc7285f9da877f18c065f516e36721855074e71a77c682073d396d823dcda8e8e1f605543a374b773075137b897d78beb9f9653a972ffc8a61e911ab9cde8e3794e4a8cda9f2dc61b0dcf65e58db94d0485f3447814755b7a90f8b584314568f592fbcde1c014df1407f87b0d6e968a047a1b97a110bcbd4600138bd91a145c7077cda2e6eb24338f04b4f3f4c53029b1aec6f73b334a1f85cd5e84f9896d01baad49f37d470056c0018a39a259604c3cbca0dd40c25a2e5abb186b5897c68faf439ac1b4f99ce17408e07352a5f4615ce76f72cdf8c1f358270509b872575db59e43a4005d52790200dbbe056de3905419adb42932a25f85fe93410a58ffc1d4aecaf19a77d6cb4803dd263b61378c40fcfcbde2f762ca52d71303eff01b625b5ba3b84c90bd35150aa9a674b4cf1a0bd2804c3f2f32376fd81b6c816e125799f9d7b6ef10335b730eb12ee2e122168dc8cbe84d4ba3075f370e160a503b02a34b02bcdb1039691e0cd832105f8f997acde695d764863fbef6bf2ed74ced0662bd6ad8ca19d040650bf61b9e6c3cf696d65209db4951ef319084007ce446e8ab4636dd368936f0070b8ad5ce81e583309ba1e09ae14b0db412b2140403420da5863f9d58ef05cd59024a96cfc54298a0a74273606679af0a26072341ebd375f9e5707c4bcabf9de500fb2b16bf0ab1abcd54978c8b4ce91a3c7b72788cb3507614b6a1260d6ea1940a5fb2f95bb28a74abd212cbbc70c981b5a49dd43d35ffcf39846b399fd1003303ae4d9e9e0d62ee1f02f3b61fef5ebd3c19f2f2a453c3d683a0d32584c8bcd008cfd8aca018912d03b3fcad65ae67c3b146c8db597589bd4b35c84ed1ea4bc30bd728f6a42232b3675dce1a03ed3d77e536c3f4efdbc56eefac40af1ebb3a4d0a08df9662d02e92bd5f9ed06dfe9d53d9f90f9db17b59e16251e57b2a66d42a018eda5ab16d54246371c7f2d21a4227f0e00788d51e50841d41600b08c647ae0e6995d7189e76323b084437c0a7385125f162af3a37e7f7006ab3d4af3df5ae0224ad418740958fdd9bf07ba496a717de8dd80339244cda24df82d9b70c6436002c87dd6ddec8e3c8eea969d81057ef6002d46db0c0a1db20260f026ad3d64703731e62942379e0375ed8b5dd35145a956df056746deeacb3a5f021c002533806abd082aab18f9675e9bed074b9718ad11fc5aa5dcfc0471a4ad160718cceb401738fa9a0be6503c0d6f13e6438ff79fcd8c6004d1cc1273596ca2b5adb6c0103d64f40fefccd86747506863dc142f98a0db41835a16cc2396aa97a6f272cd5009d925d4a97f57c94af494179baa15de4b189746598c64e38d0e8bf9eeebca906a4d06c6744b9361fba1b50122116948b01eed95eb2d55225cd43d723c45b4604b0679ae388451b95e79790b1b281ecf95d9b794a63c3ffe3cf8ad1c24496f20bbf36a90825737c96c6e81f6c54b8596325a0cf0cd3b42604728921903da5230bab2bac582841f674321c41f90a729a7170161339b172f251244e59f5b67387009d6dc2bc4dc88de97d7d4fd5aab8d6d9b7ef4f0169f89df365463c3fec92300cd6812375635be1b4adb9eb70237307fefad6fe7faa8d14b638e6e78174863b06bc97732b7dc7a3a8e4bd2b10d2e88a496ec72c0ddb23e046475e1fffe4237c02770e7a68768aeead084d324a9245006a0a4091ddc9bad287492583b05efaef0351ca52283ef1f4e29b8c1e1d4c8ff674b34dd0081e0c3d01c0ec42c0e1e5750d965ddc28861b0865d3fcd2040b759603eaaba302600f7e8bc39bf927925ef105217f0ac44f72d95a199057ced47590a4b4d10434c685a900b2d44c6ab81af807d1e8222b674e871ab95c429ad6b9e46e64b3b7fb8fa53e6d1d77eed5bb8e8704ffa2be08a37ce0ee5dc0012ebfae4f3bf61d8d5d0ebf25f79215185eb4757007d0dc8f8df25fe5afc43763b443fccc9d80042b11f4ba01d5520f1bf88e2a100bade26132eda43ebb16ab11c014353766529aafcf03792296adb44ac44c58a80a9947e3727a3252046daaaeb09b9a7647855cb096130f554fd5ddb3d01e96e30dd04177a93f04be1b4562995955cc6446dcfe0dcf76da0cae79aa390071f5ab06131fbf1e7ff70c67a05f1ccb29ccf86d0eeb280cafcb02cfbaf99c6bf8c48b0e0418df944e8706d24f569974394eb0c16e995ac137a1c9676a15d253bc27d807c36b747e7e8b4409fc4a376a7ff6dbabc78d248c6f07622a642ade5f99e28c09d10a764d573dc8d345552c8f4798fa1a56d8f9a3c6300eaf670805a437f60fbc748c2106ba6debba6abd8527ef2b549e2fced5c9a90940a687dc958127523d08c99ca9f91b56bab0759b7782ea754dd966e1ea35096c0505f6a3bdcd954dad0bd2d83f54535caa2e3007c0815f60a2bb295f86b007acfacf7fc89bd3eebc630295da64651e94f9813b92dd78313f5eb1969aa6aa2bb3d9919abb8ff3582d5d01af111485de3e52a41cb7fc5f1f7ea8be45ef4858b207a867fafd946b2fc13c073aebbdafb31e4f18acff549c3f2f17c43270141ff32524e3fe9be508f0b8c90d3a69523323603c9ef4f95048f6cdbed3f46baf45275de9e2f99e2974c36376022fac509687c063f4eda8bfc54ea1c5b50c1fc3f5c657dfe231eb382c55ca6c0a4195f1989131e1a9c1841f745b4c1c6cde77e50b66e960ea7095f48d0e426d05fdf5967732fb7370ae2cef97a62be3c74a0260d8ffd841ffe8056aa5d2711c0b15b8eb7623ae9a15613a0f0a68534eeb434caef22d0e74cb59bee7a3b1705f06488c3ad11e775e95797c76e143789401bd62c52eb3edb960f2fd59468550e900bdf56912c93368e1febd4d6d1c9a02ea8189b98518c2c63d828ac0da3c8ebf0005761a3181683f98fe9a840e7873bd448b304e6d84aa0abb81fe05e49650e207390ff8318f30c8d917393e5b35c73b93a542c356e34a93976f92f93539cada0dbc31eef249c9c8d2ecbe3b978173278a98afb8a642c9e5c88c6c14e826fca00a41fec3624e6ca0a5db5fdec677622e1c2ed7ef76b3d7c10b3468d31e3cce7b02e172a17cce1c5a1e578f2a22df5edc26c09e55ef7d8c5dfab89b8adc8691030114344b805003fff3068431d546edb4a76af32588c250f28577181f7bf2bcc50046f1a51b2c701b97e8d3afc7ed1e3fa6085933a8ad391671e653457b5663ca0987e12ecfea9701a7693790f563483eeadcc9ebb9c72030e2bbed53ee804e70065fdb068b4fe318a4eb91a4168411dd4b5d23461a3dc5b99d708891c20b5b280b85918369d8fb0c3559b0a1958ac5ed3ae06bc82779f8df78f86e260240b9970dfd4e35fea5a51bdd65ec193ced8d1e5766f7cab26022dbe19943ac0f5ac0a70dc6a568a796a8f32ab3148195fd2f8828bf4bb6563274f63f172452df8cb06a0bc93d9f38b2fce1db818afc8dc8e3913faaacb47b3be3c7a9de278f9f160a0c032c8fb6de2fdfe1a18be61833d0c325e08f95c1ee5cd5105a757c90b04e758f058c453f298e0c1e3d634697e9f1059cde4e8a3076f59d0b06dc6b133bc7d743066b9f00f969c899c4a818ee14aff844c831f557322452636c5aefdcd9a3a3b90f02659c9eb3e265ee319dd7f42fd83c6832ec6f98c1969bb92bfcacb934b9ea0df8a78045bd8fdba0d41579c65827c048e76e86bb9f7451940f79ea79e0c5270eb1cb24d88269cdaa0fd91bda236720078e1cb1f13d25276cae9abbe6abf90d0a537c068b108be15ccd0a16a364e2e22590f0f73d09d84d87a5f533fdc51f3f047c54722edc448e1d7965480279adeb56da940e4c5fa74f0f18809d23994eb606715a0da6eef2cafbb68ce35f40dbab8900503cd566e692573db693adeea30a0be4bfa47e34f8adde71e481d1127487cb967d5d2cbde53985d2796c086674c30971133329f2ab51e27ab2384c70a08e428c0a227b753b424c1d274cf83935ab08649b48a1342425dd435fadb87109bdfa1207504482eb96a5162d31374626ac05b652ec8d63cbe6e782eb18c746934a856176aa2bcaaed82ea7e89586cb2f6b0a300e5175f62a2dd7165bff6248e8a1950ee2caf63a9dff7a8d8284624a4719081a33f1b45bf50baffedb0274428d72cf39cebe25325615db37ca32ca94944805a2316d1c02d6a706f848077f54a9d3d51f1855dd28ace7cae1c44460922cc9051ab7279b5df98ba015066b27e6940672fea70e220e24f760d4f64c7ce5b64c0ff988e04d8b9d7b883a74740bdfe46c15a87b57e248f11f5a502ecbd14e8a6606dee5784ed4940d413d1023c98011b010037c7d22708f478b392db9b1d7205208fa450affddf33c6f1c2fa6e29058b3d21d1e0450f5a3b6f8b1af99082ccf230dcc7a93c3dd0c4f2e79d86a958456380d008067b989cfdd3b183007800fc0310ef74a05cece3f962b5fc179350d1b196c174381c111fb27b900e9ba93be6b2f09c7aba667cbef9cc868c0ad779b8d3a40d2e2be4d9c4551c61d091ee5ee3b170b4711467ecdac2d6abed9be065c4b30216840252b3e04e3f3302a62ca57fe2400df650fbe6bfda5f6f462c68ed2e4f94389a25f660dd3b810cd86342776134f0497d85efbea887416f1e22cdb2df2f5707808aa31660fc7c44d3f38bdbe4d9a0ed26004754fb8ff93240b765666c75b7028d41c8571654c82f1174d660a2ff80cdc732407616f3f720d7b1fb298527c0fd10a236d66020d94ac202007c687cf0fb7c5ed749a44194c390cd81587ace222c44f856fd54ed844e7e71a13cdf93a0703af5519032f13dc15c4fbfdfe27370da7e2124f17d57022b52277b1f6d44a05004ddbb28b71c988a478e2b02c70bb59d294f85535982c4f330795609e2bb30dbd57d20f95f5236be16edc2708d9de942667b68023c168490f2fb6b4e4c9520dbb799999390de3d059d3c3e7a9e788ec622d5fa3adef9d2570d3aaf7c1eb8c0f8ac383d2841d9954c247ce7ba46cc44cac948a31bd7ecba7e9da43539c028d0435d95986d3bfe253a09c0c5b986fa26bb7c419d58c16f20d30695baad64d73070ca35474621c8b6bdfbe457ea22311dbbba0e48caa28bd952d3843e6ced1c80bf41759dcf6fd14963ea0ba591c978bb0bb0cf882c1847f8d59c7c4198c02ae0b7a20c5c141568142c7ddca9e25b555d69a5f09c2db0a5498f14e0d3e26980b05c9b369c1d5e6766c8654185539c35a78bd79c861714a0111a612bda0d412940e3e099f5a303cf297f7d1cf3bb0d6609c6223ccdfa1d9c3aad2cdec98fe5bdc069350597d3e71ed195028716c307320f20501568c933180b3853c59bc383e77030e0c6e418223a26a70359866ccb43dd238722a05cd66bee41adc429367bd3a07122b2c3402e580ae618fbd61339c331212d4b8eeb8802840796df9a9e634dc04b8677da3ef64cd8370b37d1ad7755d921cfe9323b68d7e700513de62f2b4320534d9693c92fa05af8a60b04858318aa8017dd766f7f9340b4126c732d723230365b26714d0c4a27b4ff5266a29a81a4cbeaab6a9a6855989de10da53ed677908dcd5179952671cfc99fe8be79314384a33c45a1eec65522e3806970657cf50007340bd72193302bf6e1c52a13ed2b667aa63d20d0d2d039b3997de2ed199280fafa590a25695a1e828c4f76415d76dde53eca47f22b728a99a52958f5a1ea30d863fba83fd84a3d470d73e43cba8717899f043d20ba185053497ce90805a070139c61b6e850e83c8d6c407299bd298bd8e3d02a98038a03bb338eb1c09f79a00d9363511953598379b7b429cd2bb00e19b8d2f409f2b20cb3fc3681d59948a04644ff818adc4f6900cae55e7358ea33fd82066e0fc48c0b3a7f156e920358c003fa2b8e457a0b80c8a4513adfda01e71fb441e30f3197f3cca1cd0394a77510209c1190f01aeb02874a5ffa4f31c8e1d23432c74153b91dbc6959c45425814004a094b0432a7463199f08519b47bb55236ef9aeca61894aee89397553969ec0a227d6aa937408ce3cb634dc3ddf982b20d45695932d19ad5d0804b0c11970a0d3935c39fb68ba7d15ec04cf850b49ced367b2be2d8f445811e3c3a37a39f4f0bc98fbc7a753fcec7c793fc14e16dd4086642b4d5f1b461a24f08618fc514f20654ff11960dd0043a09b6c3ea51bf3d5234016ecd1baf57f4a86a61e579295a0ec5c0388b78dad3fa5c2a8593c6fe09318c98b6f49d4f0d638eedfd47fc3a090648245b8b299e2d59b9d2d13c9494bc7c21f17d638a8b277b573065e008d9e100744293a380c7a68e2de5c8a6822d95eb4af576bee38072febe528d8ec644510f4d96cbdb0a06a70e6faf0e4764747d9212b7ce550fffbd4361f6154ad74ce802b434353978dc3ca848541c557f143c3112c8f45dfc7fa6676e731ab64b988b04de0f337806d4cdd48837e50d717dfbb3f8d3492ee2e8bba02546a727d36baf04606cb5bb0f607e94b1cc7504132a2046e862b04c36d954ac4f131d3a23344f0ec3d4e767e7c6dc325728e402468934691d80a95eddd41b05c4b609c9ae671600a2f9696a5b51c0483736a29ed240d636a1eb3e5858be3d0d20e4ca4d72df7f025bd4fc7e9cd271778e841a920a6d9b122c5e084939d1c6121aeec466c11c8a0eb070b6fdbd8a63b312a50a9d113ab964a6da1b00dd0ac4348ad47c0a36da6901e6fcde63493f9801ca2831ee01d5437c9ef3979f884837724cd0945c0c6de7075cc5d9d706904d80e502efcece850fc6ffaaf1e3b6c5e805f3b1dddf76f1f300b471c8d05e8d3e87bdb5f8d5849ede3c5fdbd80d1964bf4a5c43e811463f9a06cd85f5efd85b0a4bbd9aa457b4d0199d7b50565716d78b4cbd36a9fb9f63f60aa3d41228b8c45ff7db2bc124ddc20b95a43cd2ec2b486ce21fefd5a307bcb80ab0adc67a5f3d9a4b4884d8239d6b08de64bf160c80788fb735c2a9bdd51f960e7a7578c2a69c5199257e8f3ca5f767c2839c969be6d9d69ff85cfe4469f1260d021ec7d62144c2b0904ccb27bd568abd14535e8b43525eaa6ef23653b7746d04e42b5206ced33106f2c93f834c597bd3070e4dfe0ae11e6cba2f66d41760f9064e78967cbefed9e2d816e42fd52630331e6b409ef5b968a38d853f26f45b3e031a99ed0197acb11bfa4f1484f0181ada1a0580e44774b00b01741c62d8888c03c3bce2f2c387cd6494af717fe013fdf15865e3ae76acbdea16f48c6623a41f0dce03108c4e7cb7fb64168c79f635482bbebe206fc151fa904273197648540f008137f53f5083f81e2ae1b4fa42063dc67b3dcecfb4ac5bb7e81819425e23e1002257723d52412d291ccd92038f68702d274d6cd1eb8110eebc135fd299359b0de7e4fd4d93b265ac8dac72f45690844d14f38941a1cd6253400a886b771fd9004ccf91638785bb8396e1924b634157c9e294c5dee74a76efce643bcb8852930fe4a36f5d4317409f08eb24d52a0f0b6cba6c97ad0fa16f5424a7505a5ce0780e6a29722eb5c08561bafc5b4ec0b436b0917a4b642b7ee894b1d031dcf0197b0948abc88b60f67d36342bdfc7de42ba4b31d391bd339b67bc67e481f27962bd0b746eb4febda680d3f082c2c69a1d9d5e15bf459cd7904a181d2d617320ed570b2f817f6fd78b366d564969c4ac819939469f118715e6d0dfe83abb18f4d71803e64770fb528b3cf7614e6d2f99308db4e460d9a3c6681a3795891d556fabfe014d5b30b60ef494013714cfb51ef1c221a801392c0d42611d1a1f7f06848ddd0906bb72baa2ccae82429cf4348fa9cd1fc9bdcd3284dae5f1144ae357cbac610dd16de3afaced854c523ea71846b719b47207e5da71e4847aeb2e821aeaa88506e56e9627f18a717b26942782242e4ce009c1ab6cfe93b0523a250b0c9573440dd9c28516aed0a7739eee64792cffc5717bdc59938dd8c826f3c0ae259af5740069f7b52dfc3997836ddbbb977114626471c269f100174622f70bb4db10b22f0b2aa218ae9cd8b1e90bc2e3138dcf3e1867a7290200e88757d7f1af2dc3379d01e4449e0c809d3f73120215dcb12392a977e1a303550e189a099cc955b514ae0421b4d046dfcfe5ec393ef9c5f35e333ca9caa67834538c3de7835536c366e80613d1e72e5d1af1d882732632a56c75fe7927941558439407b40c70ccd523c103cbfa1e6eb20e470bc8aa97d3440fe8a5675c413ed00e930f827f95cb8fede90c35b8ec53b9a84ea82fc56712e645f471af27387845b37be4382dd9866cd3e30d7b535e16fcba7294a97d6d8a4b8c29646df8cd215b1b7ac1110183a3e0fe190b640174c8d1a032954caf3744d2478c0f4178d0463cabd944b0b7b50252f6250e00e447bef327624ade6a103cf09924e729939dfc34314ce317f7db609f85e602a3ba7a6a38187f09d6c058a4850f2f545cd2f539a171e5b9728b351ca6962c002552ccc4f3c54d3dd8376a83951f22f8e8499108232e27d0e36a66eaf6814f04ab720e091e099f5c0a2f411281734eae7da3c9264c760c9f593d857af17e33060f991b7aa8d9309daa7f1d6152ca0a8353cd549e27905165e620ce2ee4e8020a785e4247b99abeb04871462a1265bfb88e9301c019f2dfe074f266b5fd47710efb08d87d5b06f8a375ea08db8efd45b9f9e6a27fc1f8c7952fdae29c03816b07d9677f823dc0e81e8b295103024a6e82a5e8f17f9d5c4ac47d5381127e1ee80f59296090846cfde2ad8dc5f3ff35d93de3cea835d36dedc2b11afa32e83c720a45d30557848bbab0db4b56863bb6d4596aebfba752946b7fc99b099416a2390ac0db035bfac942da4efa2fbde7cd8a44d89cf032d22d8896112f5da69a0c7a045ba009655be44630eb4943f3bb2a742ae2b7600339d35dc601153835d7397e05965697904a9835a2d05c08ec53066a7c72e87033a926bee7a53a7ff4a20d030efed7b719594152ccc15d0a7227bacdc146375339b842605cb8dd2e053f13960c384c4a0fb144918e8493ba4d1b19d202f114631822f42d1f68172032b4ed1a0513e6e12eecb3883464bc72d83d71d8af7b64e69f657bf54df8e686dc8421c200c6f593818ff9303f56af60d134ed7d51fac6efb0ba1f025ddc3d49c5b936280476c0d75e0295655b79d51ecb86c99891b4822ea2baf54cf279517674ce260b02d4f693090e8f4adc71a09f2651301596637afc6aec29b31a400b4b94c5d5ae0b360e4f2bfee65dc1bdfd27af0bf315bd4ee3e8203bfbd702e41cf839db932d0a7c441f7fefcf7c74c1ea11e3eebd2948bfec9bb43c9142ca7f35508069ebdf041d7986ef98ca2b97d7b0571ca25c04fe694e55ca04460b2b7f5879be67a0f0019817bcd020cd6846194cd5cc79fa4bddf6d97d2accc42152147ff11e91e3e20ed691f44972cd4b88014f5e3e2d4c1124e873fecd4e0d2107998575e780f3c00b6e11d7fd28cae9c4fa0141ad7df89f77093843a07010ec16191a27d5b39a5c00802a315182a88d07b6705388c71e1acfae50bd252859455cab8416fe86ae24013787b8858e828811f9e564c72465fa0b475b42093e4900dde3e1bd98a1e30b0bebb83bfa4c03fea65528f2eb05b51fe7e27b3816eca1f9580cfb03554f334007f4ef176a2ae4d04cdf22aeb9d04e36f01789b3bf7f369153ee9f750af36ca90a416294574e6dfaa7c09fd800548c70491b049f2c753a806a1c574de3f6a5ca091b4730d019f537d94e3f7d1a7d22aa94d2fa2104894f0be270ea00367801c50f3b5237dc9a6eaac7ea984c42366751026328b0d2f1fe5c65ed96d0eab29b500076af0b0a2e5e76f6bf3e505ed4cf42836cd2bf39ab7091cd24fa197db50d970804e486d2967b9cf440edb82a9bc27bb229cc56712a6e41f8025925d4e6fc970055ec99529c42018b095f3157d34cbf6b308b1e6bdc1be418947208317f7052007eb91233af5fcb985d14c190934cc91e9cb26c88cc204089d3a67f90080cc90aaaf0681e3ce68a478f8736e97cd0464ca893d3db505d5df321dc3b03a544b1037699afc182d61bf06b438719b4e3f834010dd82efef8ece904fabf75ffd02a0ab445ebd3512902a73a51ad9d790734fb0f17d4dc629dc4e3c08fd8b4736f510927af91532da73a113eabd8220def8466f5bdfd54bfbbd11435d04490979caa08c61c126253cd25897987dcc2e72be95c4c8f7e56b6a5aa9e553e7077febe6907f855434c48c549ea1e91e71cf7133466b5eafbb1f0fade23ce03a0f0ddde470ea643a323550ef2b64319f9e2c344a0664431c65e665340403f47ed5a42a346043bb1e4b7a2ad9fca71cf0d4ac1c781d968f54b99c7173277312e2ba5e9bede048b5cb1469591b7f0fedcd7d8dbe58cff69ce9191d96deee84d877229dba53308297a11103b375b13976457c99b09323be0042b927434bef294467a1851afd307b4a2d259ba3363bc0227482002bbedc9827f64bcb8cdc05070e8991943b47300ea341a5b972d8155f2fefa68c47a342848d2cfe2ed00620ed9fdf7f81cd5d70cee81f2ac0eb1cc098c4352a712ac1e60c0f49aaf501971a343ebccd4c673700a73916e2b270a76691e38fedd133af55502586c94a69a1f976cfabe992a70140734035ea6e956105623ca710e6410952859d3adf374360f720f059d46d6ae6f00a83d78f64c2f5914609ac34f81057113dea8bc59340f1afd96f3520f4039f307780722a89cc37397b81a3771822ac2621dd9ba6730d329670b57ac5c96d5bb03cd1864c92cc63611680e6942bd5a8522af8bfb0733d343a59763572d24d14f021d669d8192a3a24583e4fab8143157a70e4ce03c58547386cfd383737d9f8a097b6abfef828f89be0e23f529748aa505ed217e9aafec742c34b1b54eb468ea049f69958679fa2e0cf1719848edf000c0e0f1e061d93a8b0abc49dfd97ed78101d39c285d0d163e41e044a09167fcc14b58be43e82e12e9fdeea967e351bdb50977164ef07b94dae330bf40bae13511f98e88e5273f00a067ca06fef5afb1d20753ef8bd03471fc4a6df21d0581efcad325e78513f0664d38c063641f38d85709f0443f80bd18dfb06b03cbfa89104b84a8e196b735a3ba104aeb262e3b7d5408bd3d75fed8eed6db6b2cee42e39ff88eeec0c542b0e9eb915a097ef6c8b0e00d6af40af59e76481655e5b1d81cce728aec12fb03ee025bfa5c8d26480400430c26f8335fb6cec4fc94dd1089710448b6a07a061af6386202bb44d74a18ea510b5aeaf45f31494aa5f5489b1198ecdfd43af682bc05a97bddd7250f0af1f4c20dbfbe8c826eea9c3c1c65e1cf8d400f5b9c004ffc6e2d33f4b4a05a2ecc063d09ceabd26ae521e921dc54ca876e54cf01a41ca927ce77c9c45269882cd39a4502c7bba007ddb023bf1708dad6e97bcbc4fe98b9d5d3ee974685ed582d56bf6e0f56780c482898ce57f2bfe68a43c9480a9f3f400673a7b94ee1c4de6bbadd8b078f62bba5217de2b45c20a31f428a3795a948151364f249ce9beb2d8449aa7b0c3c4e40fad0eed29edc9a746e6f3434430f9af9b176ed937940156b27c5d05a09d1a5b476f05ed651031939fb5040fe852b986b060cfa256c75457b804cf53003ce8607d467eb745352516ce374b1ba4c40f2c511758698443688b96283f36800ae2ad824dc72243690a1b118c1383de5c052a13ec908b0b130a02f2a086aee02797795610bb809c991e982327bb2d23ea895afa2d739ec4b71d0d0f58a755d02da653edf6c5e5809b2806a54d6aa4b16c41567176819b3415f7147944d19e204515e4e12b4322172e3ba4412aea6f0a4dd8cebb3a1859d81a7a73b319158f101a4463e0443ecc6fc6687943b69fd8a7ff05eb5495f58cf5e4ce00ea7bed4ad0cb2662bf4539457c2f2ec478e5c7d4a9859bf1747b0f35b7fc545b1a1ef541a07803b8059047255b7fc60d36051ad3570f8b1f48a96c303197e3c8d0adb92c502694a5e0269cb4145857c302d83a629f2c8337f54ddd98c7f449a39e2da7550088524ae26ae6c4360ba407959359154e67fbf6010e160f69f2e42ec0197c55b021081acf35378f6c7727c78b94eaaa813c70a777eb4f3e1f714ffd442c489a30a646e46a1481fb90e9c6cd62d383796988d65600ec442d7c9f81b51b7ce77010bd8971ce926da5f4e865072805879dde9992235d587fb6fc44b28049fc1f3dc0269fbb4835d576900c387b283378f7cc858595bf1d743a49d7438a3f86933100ecbb5d003292f515b91c05cf5e42cf5c8f394ad330d00b036a5365defab5f1701fccb27664eef2896da75d3fb5a06b3b1d4bdbc442d7b782999950e2f604cf0016615f6cd7bd5b432ab1a4438c478cac4d9fb2c4503f409f5bc4779d3f0fe2d0b04bf157a82c99e1f6a64ea4d87cf5e3a14aba7699f9c01b21f92589f96ab110d1f6c42f6633638c5afc9713ab01a79069df4951e5ff07456e7a1fa8bc9ab3f07e1ae61057c7b0900fc1b26218c703fb1f4c5c329a6c20196fa20ff45a1851300172c7e5ec00ca6eef7bb4de6b3131d5ccddc8f591e7f8389ca67d50b1b7db60312d0e14705cba775f9f11f7f9c91de020ed4bc8a08c614b77d4419b872f4e507a25b2380bb7dca05f8d44bd1c5e7c7e63a056292c176adad64267cf834b3690fe8c0acd4da0962cf2b9a51841b1a687cb574ea2b0e32604cecbfeb5072b9e400cb142be9d7288736bc348dc9b36d421fca575390464325ac0c3d55d1d11de50ec343bf8c79adb1dc7c6761b7885f78dd40296906173954d10e534d28b1cc6c017631b6b4df39a3219b8b64c879db6516e000922d9ea333da5abf2fa982774205b07f44e632eebac2bf5798ae8ba641bd0d0f6052279da9115613c591bf05e40de5f04abac0f2208c061273c101b0f02c64dffc35e2025836f1c82d01f5d3f804a27110a3b1ef4b5f1fa98293e871646d1d14f160ac42c99a9d4e91cece5dd30da0098eda38982c545802a78bcbe703ee4837d1621e9d91d0ea51547ca96cff07d303c60284a9912fd71df388e1ba7d9797d141f71475763399fc6c5c6ae04a01d08dca88dacfeb56329dbccd346403968e5c158a85d0da72b7f84316bc7e92065c9b96ce61f0b08a8a5ed9eed78f24f583b387aa60901c3d5ce78af1ab593408518e1b2c164cc851fca081f5c37995ec1bd2fbd60304b757ed566e0e06a2d004b5353729b4632eef8df17d1a7d03d6b1fa339e065b790f19a46de944852c8901f35984d8e3ff2ff1628a48888d0fe40f8e76baa55568d205b0f3e232b3c93800aa31593ad222532048b7b4cd0b7dd5871ff93b53021ec7b673c935c5ae76aa07dbd0be324c25b6fffe5ea8880e4d42fd291c171a7aca137980da1d0250ac0a03d892bda42cc0dba9d958764c0df9d415ac953b57402f577356deab4fc769cf0eb808d89704b421421a8874089ba0c0d4860737d7050ffe06db691a8535a32f045c2ff8af8a32f20386761e6dd78b2efac736b44bd6e02997e55f09ea99a7880d20d52f25e4118b60691493a8dc6a93b6911af20d98d899eb38f83ba2ce63fe0983cefec47988de99a4e030c44b6b4ee7193d12de5892ad10983c7399ede3bb0aa0f5d30a0362b0487110297775c3d09736a7b9ccc0b5e9c376f3e6513c36420ee27e5aa8f18eef405636bc9c249bf81eecd3f92464dfa3db40f36c491c28b10054b60e3df2b09ce793ed0a573e0394859c956b181845c30b9fa4041fd17a750c1cdf6e7082405d3e424d5c01f4d181fa5846ed459cef86357f461e83ddb5f60f16a4e02ab4c89a7a2832b16c42c35d531efa0a80765bccb7a22374ef91732a0f false -check_ring_signature d8f9ebdad62a4a1936a7402859625fd36b0a0c51515a65ee0bb0b176eb1ea9ca 5022f0518c0d5c23ff02711067fc65b98c8cafeb4a28cb5ca170561deaede8b4 118 196cc80f65340c20d3ddf5d9cb1d198de4c39606db89fa23746bc53500096a51 9d853ac67b1dfedf98a899d3b9368c19d5e3ba7a9f88aef903cd6381eeff45a4 704459bd9c641e717ebd296b01b52de52b47b7c485dd769c22dbadfe5e26e432 23892b44d57d0ce05f9ffabcfa98b157e956a01740a8020facb33215ecc31a6d 24766b33a4c7c4681f140d7def192267a27331dfc1fc102e60b1626aa149fac1 34730ac57141c21542b72f4b6c58eff06841ae666b44d4ca8b25d18500da4626 59ebc1dd08813991accbf28eb3489741e903f33c04dab6f3414ceb85956dd983 8cd2a39d44e5406a219cef2296655779d620dd260b52658dd3643b3c3186e635 51ef48daffc4acd7009d6ac1ddbf1d0d445634c251a0afdc240634bc5365c317 60009fbf934df0d3fca8d64102e257962dfca62f2b108d4cec0b7f8cac332a3a a4e44ac37c38d570d83a8311c5b15411727795924eddffb0905e0e3d412255ac 139fd753067f3f38dda55562039a2460c16fd473c470b05868071a70ef3494bc fa863418929c5bf6b64988cd6b5038ff8ad2bded4a9783aa0631d28bf01c4633 9a5bdac51d3e463116a6eee0dd893f13d94337bb8f8f95f0305ec441f295f76b 354cc44ef767ad6daa6e6a94a1c5329e6b0df84b36125c9019326764d7802eec 9c97e18f7996267b3261a723bcf2e7aa2b0428c13963ba89e856c32ab69e70a5 421fdadb0f6dfdce1da18779ec7e6ef43e8cba2c34571acf4f73ce241933ff60 a3de26a03f2a9ae7d585d2a44365896e36bb42842effe7d8ddd78f4b45038ea7 ce0a3afb4dbf2f08394838292a93075539a4199eccdea1482ecd1432c09aaa64 eb7e9907b3aa9ecf855ad51114a036d932a14473058d1ba743f9a6ba74bd5c0d f2ec74698b7facb9a9260da39602eacc39b52edad536d435d9dd2a0fa3210cdf 9a5ec1c8408f5324dfbb94b9a2c087f74c19a829fc22ec764712483e55f8371b 921d52703cd2dd5c812d2246e2c91580214787726114dd3092e6e4b8d4fbdd47 e3f9a5db65885c5409d8fdb904a5a4eaf76b8020b780f85066cc7c21d258363b eb749b460a69b5177cf203ba287c327fdbbaf4fbf5678aea1cdbbcb86ae69b74 1a7b4e013d42d204338784db0088a856749e932e3c48c99a122fff7cfe3e3cf5 dd473f40a77a37052c81a37c6fe0356f6f960764b406a027fdaf0c1d6c050beb 7e24de16336873785000ea906c8bc10ff22fd1b6335962c84b944d30037f5f5d 899372de3c54a3971dbf47ab5de763810419e5212b13ffcfd0d31220599c9509 511dc0deaf3ba39aa84cda30dc56ae84c63d769038b02b48cccfb2dd9c3f4c38 539be87f22b1a3ccbf4f0116d5c02754ee1842a4b0c664cb96461330ba07106b ff8dda1a0abe2dca35c5862fd945cd3b9de179ef301925c431c6b417b40aff98 69bb7c634d596e5c60955717317fe721b1ed3ac29a9ef682bb09e5a123d2f42b a5b7b2ad4983b4b1ec1c0f6a39184bcbde3bfb66603ee8b12c38783d1723a470 8da2b2f79904a8818739bd80fd70ae6487a5022bf664cbd02c8f93d7682bf4d0 d3ea3f93ac451792a2068a0b41e4b87b8a680c994cc4440eb14e511b0a0871bc 8a69174859ecf55b245c7ba2833f658759014e2c7de2c685625cb860b9eb14ac fa84e14951b08b37964aa30dfb29824d203b675b7bf33a1ee5fb53de5913ba54 6468da5929f640e128409308cf42a6040a0ca07b3536555840b80e2aa40f4845 81da50018b5503110e9d1abdc7ca65ee10ac8973b54fa7a9c14f2735c966759c 2cb6b23b9fadefc28846558beaca989835633f7b265d8735b82ecd56890de6b5 1cb96bd599ef7f4cb75b09f3b72e5e4a878c9ee4b60406f33da225df0cfc2928 ea250fcb59a5ef9f3a75a4eacea5c6e0f440df75a40683a0abae90534c06bbb0 100b9917b4fbfd3869cc01995f5854b72594febfd961c1336eb1fe52af94baba 5f2d34f7e028ce246967a4e7867bb4f41cf72004f4c9dcc734b5b6ac05b86427 3332718b83733040c8ea493b53646285fdb72b0f3a143296b4d6557b144f3f2b ac17e982c416879d0f2a8313f87151ba329973cb0f1644ebd0269dde3d93bad0 b1f7b8df92b3759b3e64b6d72b18fbbcd9b234a1348aa3390e4648cce6895f6a cce07e553af9eb725abc95b4b4e9b31c717b169ab937b219671a43b10394585e b46b3aba6d0bb684661b13d8ba352d9932d2e0fea54b8cfcb847bf8dc5402394 c17eb48b8aedba41edefc11a206396a3d593374a74b75de352ce16bf6fac9479 7fd62945e7f39f60ffe8ea0e09b104596c0985dc7984d9037591e6c2977e868b 187e99fbbbd9d452bee42d6cb98f3f71981186747cb34d19e9c79de9e01c0e4a 770c83c8c7e0fa6d3a592647874bbf7cd1ed5670420099890f8cbdbfc18b9ef9 11c246120ada7d122cfb6ff2b9308b0ee3746c9e30ec893fe5a37633e7db70f8 0ff766ab0133ad6ecfbba1523c22e522530e9ce854b8456b4c7e98a3d265a062 5d0c6601daf000d3e858595a1b3c700ea741ec856d322395cb1172be72e0c628 64d0721465507d660e8102d9fb833e24e3fe8f1f2cef4fadd1554ba892df95c6 99982dc69fb287faabbed8221b1696de7c044e50e45485fdcda35badbde35611 a41493b1b565def20134d96b3acced5ab1c8c8aa63a26f4f119e595046ddcd98 0cf00ccc5bb9ff195a7b187f7f61ea0c2786948d1f0051f615d2f435d2f868de 519cef70517153750c9bdc8928873c10621e5c7d628586b6cbb9f0726d068cc9 bd7a1b65531646c6be87ef8f512d9cbab7ac7e776ac6e8291d808119e094a95c 6bdb74057b258d0a7e64504cf27435a4b6bc4395d839915a9235d34295f86879 3ae32d274a69dffe9a61f2c94332551c16449b49eb8d27059bde92358b56c681 e039396253f8fed43a92d7e740ebc4014172ba7567ac3bec589b759c1b59cb93 3bf502688867d7ceabaf3b3aecb1f7d634dd33dce6a0f35db680386b7f0fd1e7 e0e8a91a657f7d867f1464fb642534bec29eb165ac32a68298f7bf7b4706340a 83bf90cc9ada30701d4b0b9ff19626c4e694e9fcf429c73d0582d0a604c08f67 60448fde199743ad7775ed48bea4ff707de4550ebc383333fee0341ad396d71c fe3ecdcd96cfd29c8974a295c921bc79704bed7db894d3c08c593bfcc353ddb9 edd78e8d49cf6f4b7cba2290746b29304244678d19ee9b314ecc50973e060fa7 db5bb4ddc84603c48b3f973f28d60583e4429699863a685949b0009d220b156a 1cab22b666160f0142589d5e1d46d14ef09c1263e7375d434b9fe656c80f6db8 068baa525b9b3eb83e681fd7729dde0936105adeb6a77ca2b8c93860e2151286 050155a2b86da07fa86a451ef80bb64635bbadf357e1a7fe2be222873d7c85b3 e868ff1a9f85b3b53fcaa0413e68c6c9962ffb9d15e906efa37d089ed82586f7 11cb8df784ed387a548b9d52123c0c2aebd68c7e86d97f5a25057fca3e9399bd c5c038bb49c712302a039c1080342d898d5e2e0ceaa72d1675f3da859514276b c4e6684d8aff08f668a74c7621a16661edc06cb5b5d3af2014d0a8c93a4f7f30 823fde3b825760da1e2483d171041774baf41b19c4c08cf149bb13b4c264ac52 c6f3c5ccbc4b2856376069274d5cac063d9277210f266b2c4b94fa6b0134b824 9ff694c941c6584791dd8bfe79595c21d0c8982774ce2bf8709d39c064e9f886 54a29af7986db1d8a365ba029793dc55026ef4f8812f780b55a364f8d2697f30 07fd7d780e04f83f5811119916210b36b4a37b4f8754ba06313d65e740d531bb 701f12a7f469ea8d03c5dee3c25684e6071e6f1395ebcd8aa22efa7dd2086632 a2e1213c2d5d97ea132b442783e727f1e71ec684b52227e4cecbb67d9eed935e 55de41982aeaf0488fe8b06cdefde436b923d01fafcc028bd508d47d7b4a95e4 d5a04766aaf25330cfadf91bbcad907c5961e0579c0fff56da0de59843e24c99 405fe1a1872822a04a5c2e429942c03a3320e46f4d5635e0d87ad6ac5e0e6870 99b4c3ee509e3ca2c6a8cd317dfe99dd448c4b086b4d9272bced41aea15febee e2f5b4d192f735408f407b9416cba645e7703d626e3a1a286dbd68d5f981c37e 1965b4461b974c354ea09154d7273a1d81b8725b702d06bf1211eea203283173 6c4653330c9576197ff96f827ff4592ceae2577f33e5d29a5e6d0d08f4a3dfe8 51dc7a74403bf5d8e43264f55431fbba67d8344f1cb220f05a23d2db63648529 14cc483e6c433d79ea99a91188280bdcd5462d8f45324a0d9c8320c1a21d0ade 8eafe0b0289a2b80047cdd2f01ac6d84903ebc96422272b195ccbe857ee5a70c 725ee776679303933e16d1ce765d026129156855e49eb5ba39b3e64f186e0bef 16fabdf67614a382bb11e85742f357c8c9c4aecfbefbb3420ede76ed53c143a3 936314c9fd407960546b6d7d0f7dff859c40c9904d938c200998ff3292394915 7b997a305197985fc5a3c7ebbb7da9f515305970c62dd294a3ff8022de1a3411 32e8795ca3dc6b113caa1dc0a2906a0ebb209596ebdc9a68b4534e97530725d0 9f2d40218bfe204798fcbc991e15acdd8157149627bac0635995d78f578be2e2 8c04794e6c42a0fdb8c155d5538324b1e548425f759acebfff8aedefa5d15d98 48345eee35f4deaefbe0e81594495045a93aed2298db607f9b221c96743fbed0 ec51628fc76b53db951087ea0fbe09fdc7b3a07e1cfdb267813fc89821cf2f65 a9faa9bdb02a71292a0e59d314976f5e7fab11b3ef0659408775339d51ae0466 3b1799ab2b07b6a9f36232f37d73dfa131e34ac73faca5ad14af4c69d0624316 d7b8876214ce45e825ae3103e00029b7887d5414eff20dc0c43cb3cfdcc77ccf ecdd90414aa0a004b037851ba42196bc95e13824bb6f13ed21d03529ab988f06 d0b7d93c2b879ed57a30bbec59a3c836f8dda2f6b22ff8bd844cb3ab3dfc8edb 5628f830af0998fdde5e36a5fee15f4bfbbecdbfe1798b1170f76929e29a3249 39771ba107a7909b2942b4093a4746bffc0b3d8b66adb911094ae6ad4516f2a1 8b02e3d22fdebc86caee07fc6c61bb72963bbd6dc54475c08b50e4e3180459a6 348ca02e065750ae7f46c3e02e9c9a3be7472ea547bd6e61d9391a0db33463b1 3b130f1c4e48bd8817fa1fba6f357583e6b4ead331635953bc8dfe5da80a8193 04d08f146199aa1f890e4a094ac0d11f0027042b17a087d2ff29e5941e417721 b2a9a410e7362ad27d64b10338a5e07640a1e8115ae3cf4a9b73e6e0d350f7f9 99bd0519913e5b61247b67a7b3664924ff69aba5bd0c073239a3315488fd5906bcb865933f96c64dfb781743b89e22a6bda383fce28de584a95ae6224518b202b59e34640a466c4ad043c6d60f5b86cddc4ee071638b497f4e6efe0eafdd73044ce3af0e9ef21b364150325afa2fa63ecf14418d7b8f37f4ebc883a129c8ea089b9c0994959017701887bf5197f8710fac1d292ca7a1f26b916a70fc21e49902a4b9e484335a8503c8b166e61fbcac4ccc12ac77ce2a596ec64952ae2440430ee4183f35937608d4f1a32b6d15364e5e5e2ce720e9cdb547fd84883533efadea1f50ed4b189f9a9fbd84e46fbae3444cbc6720a8869aae4e4a50af8ccdfc9409b2edcb005908f1481bb27e7142b75f4138e182089fd54ee72025992c90f2ec01d7a22416c851d5a9fbe5abc8f3e60a554d11478beef5475ec0b2f7745d2e4e02859ee369c638edec140cc528c9a4801e28b7661c01787d4f311172a69f59ff0a586537ffe829ca795018be90b57b1618bf141dac7531f1ae142451aa4a0acb03137adbddfb05e4a4e999ecf555e163a3bffc3744569237b13a081bbb63e0de0672782040c11055982fea96da242c9d4582956fc538b56fe4b19ee648ed3c43158a2cbaee4264d8d43e8dec3386369d8c6ad5d8b2912606807c8a723344f9090d351c129da1b2f04d4e21c0650d3a583daab1d662418644e0315f489bbd752002b31b20ac103d9bb09f5ce4019b419f53ae3321ededd711beba9229caee4218013caad0b118cc8eced411dc42f4f077ad2915d81a0b884b4dd52d3f72a0976a0d17363d400fc80db0530c6467b9f78ff0bc732ac9ac3272bbf056b5f84dd997068df1e6e23a33f04c2b25969c3ea7e42e051e25c5118a2cffffca683e6ad66306279d1dff708992d2a17cfa117bfc93c181cb9efcf6fa1fc10e16f40086d40a02036f49b74b9250fe7a7599ad89e63a188ed9974ab436845f363a263f82365f0c494cea1bfb16f3a90275e1ea362ed276d4aff736394d3b4e72ea0be5b171a4064fcd8640afd3e17369fb12066d270d1514f1ef437806e2972bfbef1cc430e3050deea34a0a488c4bb46a9b23bc428c433909341d389c4be713e27f03b5ceb30c363afe3004d1888b879ef7b7167ff7761b943764d635775ee66b4379c2736b05ea16ff80d814a8d5365fc27ad3016208e4bc08d9194da8ec7b7e57f82e0ee9034896262a47f7fb29323219b1fdd79422e4726deb6208351730b1cfffd6feb407ad7ee26fda28e3dc69667d5be01c2d6068d9f905aa72cbcb2aefb7d7e0d1e809ac6d74e301a0e875d0deb82b0694e677c4b70ea35eab1382b63240f3f1f394096f74f3d6cf9995198a07cc3dc0d8d23b8f936250c5966a63488d7cc6c410e605af2e5030f45c464533fd7fe3587d908056640b00749aac656f7cd9776843a8030005ca2353436fa63eae72563172417bd6f372132b5e0b51bf7251baa584f80ae4d033ab4997969ba8dc5dcf7bf473e815332f18d694858335ea1cf48a42a4017c5d22a11227911f965efd28c037571288f36f98521e952e61f308617b940e08cc1eae59374dcb82a6ca5edf3b528348aab8504c638fda087cad71b0df8be000b304716d73769f5fef5a0d5715152d2649f92bb2739ff293709493070e28bf05779ebd4ca389c53466713e4f8ec0cf3f1cabc0ae6b70cac9fabc70307d4896071ec517b2af198f8fcc5fea91587ea32ba9c0c13b10e9b7bf1073dc9eaf72a00ca727e95d2d27f38fdfc3d106d675aa5d9b3661c2b6585b796fb6a7fcd2d7d8020b003619b2f0d26e4256bd99e76ada57324f3d651e050e0ca4a512374ad96f0189be31fe8616ddac37abf849a141331ef68532f852c53ed85225e06c8830890bdcebb4d4b07c6b0546d175ed7d924d9d7fcfc5b1231453481d48d85d194ffa079d0f246c19f36be49f2c45839ab6a05d34647561002724d4c2a3d63d77e9d4003c6f0e142fbe450351fd7efda9f44dd108f967918aae50da00abfeb9af547300b6da189e27812d068893d20c13f98b57aaec83fcdada6c35bd92b04e94affe080272ba5abaa20477fa0a57976888f96ab4644861eb75d8c6bd680e159a62e10c16bdc9ece6fd29df97a5bd9d7784439585a15a007b3da0bef704535d95a16105279299cd9c7067e39af107a79e412ad05de736d83d0d65671d075b08f318e5057fc8b63337b40d34ed2b7cd9ef38740df228e28534de3f3a35e98e0f3090930a09d8a92709872de3006db5169fec15b5b9b0a79d88c3dfcd11e02f48028a5a0935dfacb03f0340da06e4e035877f9d51128be18d4e18e4d2ccdc1104a4b3270ba2470e5ae35d0245d4c0f9414d9d3b5bed9a54f858b1c515121a841ee6593c08c54e0c86264592cd02f290731e0fcfdbc78c4dd4b99197303cc4db08c410910ff45a68fa9cb894b2ba9b23d081da8742e1caf6c5c064efd4c14be3a1931917008b246e4a2eb85cc234734a88b7c6fecb90c53d69aa57f2facbe74bbb4164d704c81899321944e70a86226459b5562acc387eda4b91a4bf06ac4fb78dc63bea0801319b8489129e8151be384c1b713364516fad43f542c78ab24732b702928f0fefdc6ba06b627a82fc26096d4e4a3b63742acb4c98621975920b00ca572e840d2794792affef9c99254406e08e59b58eb9cbfb55a5bf8a6559bf20eb3c8dc20c322fd68bc02afc3e889e3731d919d7e2fd9c951ea9f42190ce525e352092dc08125d8c76f89f34b597e2250478795a72775774cef9a906d0f6f15c0430321f08c84117b50394d8af7f1e0f185560625a6bb9671b929ab90ad0aa3dfff5f6a5043adadb2cf0398d4f8786a0a6b68356c8638dfb80e4dd2df595144d8ba32072061a93829e81ef8068dabeb010477f6ab450e0045d9a069e661eec3a9f86f017026689ed62d95c915783b3e9208c0a0cbbac8bcf5b20058e2ae2dc96f6d55e1d05fef1cb14e33a5f39fb92640ce49b64b31916e1b5f3928130284c4d721fda8006a25e9d057d9e21eb131ee7aed3da87841de0267422dec182e322e9caf2043f0c592d15d2abf7a4c97a2329b5aba3f47e868e0d3cdb942fcda203684fbf11fa02fc9090ceeafedaa9e55541e544e280526ef364ab31b5d10c7d33e37dfc1e420fc77851e3adbb6c1735982a36bccb60ee445948949833fd4da0ad375afdbc840f22ba7ca888734f0ba57dc0a0996d858b4f825ef7f19b7f271f76a9e25427a40ed5e09ae3702be99fde19133eb20b241b2f0cf382cdfd0560199341cf9019a20b0821e287d014c501661c5abb45d812d8dfb5795d64a9979acb53b1a5d9fb3e031f88ab8bdbc5f48b3243fe723d8e6a6253ef1b61fc67236d66c20ede36af2b0aff6beea0e47b81936363e640934123b357ee0102823b59228ff9099252f54003a1b3e7cd2d7ad94c6bd7eb811be1e21f005e0ee483c7f4dfc12456620a5b0008edaab0871dcbe5bef48ecda0d8f199d8a148971692a0dc7836c90ffdc669ea069a194a88c885f62833ec08aa69691425ba1faa24ce5480aeec76ef6135edb20272785fc3a6cdbd423f5b803c93648f91aebca5a33b45be0f0f5b1e9d6d7378055a987963c58d448271476753e78d6a0c951febe7b187a552afef10dae7b76a08524d582142e7e5977b5eb41b27a4e6812b33518dfd47e0c74c9097437638f20fbf5cb86f7991d7197861e0cef20cd6d57c44916c11f63e92405cd85e4028b608c1439dbbb9519e46beb346fc2c3948239872c1e165e4a24934af72d069b0bc067263da5e2247f27ca0d1da877ed1c3b7d3a2105e378ddb23c0f014bc63194f0fa1ded22a05fa73b7915272fddeeaa144733f0e347c1cb71da5f77b23920d8f020aa5e4009c8da61f4d8770d3f5e89aa43a7c9ebb5a0759df733d2bb7494f030f2884cfefe5fb708ab9a19ae0df83cf92484a0f051e5aec43e6c5445f06aec20ef9d33dd940ee79049389550d5e54b7d3abfed12ad656683ceb0789e92e6b4d0a52863411246390c771b230dead00c42ce3aa7083b35ef8b9631186ebeb3f670e16d08786a053f23079a7cd3a0f22f9033608834914e8b3130b7434302aba9b099ebb18b2220cbd0f709e24c93b343030eb05d9d262028199e87f29c6ba94c40b574c1960cdc26a7ceff31bd5d805b49254b64f1c9f3f1da7a3618b0a160f350c0eae982310b582ac445de46343e9a250bddfb7d022e387b30ac534f197a88e06630cdf81de2f4c82924778cc7c6bb4d9a912779c5d73f34858812feb84d84f0436520476a7f76957d4af94fcc176b2610609bede2f6320b2174921799aa45a09022ded00ae76b88706a5b684ba36d17f0a56d5be732f7699e7ee419d75363d00fbee4927954bd75dfa45d43b2063c0a6cd31fd46998b6a877be6cf9e3444d60e2458abfe1c3581043e4caff0d1e24ec85a707fb9fe1292efa0680803f3aebb061b90d4e462f523e6f26f4473069534c12dcd73ca3c6e817e9ca93a030048950c226935c49ebc0afbfafb77131311398554e91e6b3d05616f245af05161fb600098bc536eb4e98d4216c560b7130610eb6a445259e3bd878e3f81c9e53223cc0f5e10aa2f7097e99a17224bf35956cf0ed98e53e069d653f74c674de6f7deb705d7c2bfb04509460f9dc4e35f53cf6cdefaff75a368f1c9c29207f5473b87ed0bb2844affe571738dd1940d9cb394d9b78ba394d1d2caee76877f26003b330708c2916a698619b502ed04c4b50a8ecd9bc3cbee0a3d96793d3b5d9e118836ef0a6e918f8455acc8b09590fddaba1ba6510663f31350295287a4305d98046c7106d0cbda88bed2130076cadbc7165de808017c53656e7a19f04e6c9db8f4c2930b45cdbc7f7bef1274a76ecab579a9b917e6dca9299b02d572c979d0cc4f583407ca44a8cfe5426a2e8a4dbf133b857aef264db228d80f19a5b2c4c98826c2900ae3bc25e119df85a40ac596e1547edf95ad690d1d28f76a68275e66d615b1b70bb738fcc4e8b5f867eee92fc6b794582b14ef6294d7ce765a59c9c94828cf0b0889042e0fc4e00c12f94e94a42b533eb1812636db024b99f1e6bb0cadeda6e50d07ad8cefdf3dd3a6240360181af6f7a5b915a244d08ef45f00f605948b3ec601567c5a57260643fcc1a2bc008fd56534c4a974d146a57741fbe22df839f959040530014f8e79642bf035dd654b961be42a3f666d92c7d3b44b7dca6b6cdbc40b7f9f94bfda24ee26518835b5d3df3d4f9ac8f748e3a173a1ee40fdce555c1007af8580c9ec7f542e9a4c34f7b7ff80ed4a595106bc0d062917dfc7ff50d4b808e983cc0c9508b3d7be8b3a578676995fd6100ffbe70f51794cc47e4e5ee3460254af50565035edb8035a80724d02527f2ded09aaec7f9a28e63db71dd940f904334377b2f710b0561f493c7cb191e36ae855c59ff834c2acb9c880689cd63e01e3d0f69caf533a456d6883bd092b9fae347a8b4f92963b4ced578eac80d3e1008692bba28391ad3fc956434a4a9093ea0e1e92476fd5095ffb4d6ec7ef9ab105e834f083381b429283ec0e0cca2737545ae5883905cdd6dee49c29ffdc70eb0e1045a42156b03baa93adf726f5422b11c476c16296d68c6a171353a887a0a008a52b3444c5879b9abf4a77d9ff9b6a38d7efa3d8eb5e9cfbe614db23fa028a0e113cc73d7d7e5d5c322497e20d7fbcbaf25d88f74f5f5b1e7b3c307ef854350a5dad5c1c1aff950dd84df3b4bd6fe05cc5d838aded921acc98d4905ced1c2a02a49a00ed916c1cb0df94320d282a0c27493bfbe7b3742cba4c16ce6658300b05466e93d732d1018f7d7fff9d96f845adb3e4df3ba1cd8ff89684161b1062610f94ca2c189cd01015ca83192edf3c354f778f646826e7413a937d8b150d305800ba20fb951a8090481dac6afa0ecd7fecf8771f470a1fc1f842042baeaae529049840892802d04206466dcf2a06a1aeaf2f387b19477eb85d7de7659f026a9d01d28f184c37b81b2cea3d80052a06fc2a8ca30c62f4efe44d0b0acdc50feef207bad4f7c74d71ce172fb17ff8ced5f4437c80107602d590a7a3c0bcd8eaf8bc038466704ae00f41478fb00963a1ff8f998fbc401d18a889fa713925e13279f308bd8f901878d8489c191380977242a8d6d6bb5f63639fcd4a885414deaef0520fba6c1acc2202b51be265246533fa336f302b84ba3e424017e6272cc0d2bb340891967fbdec52dc8080d09748d8d38a4695c044934f6df1f399a7e095fff7c6052c1594bcb0889c7b933f2d7a7ef077d34c3a08de7351e0daf48def9f3067b406f2132802d51a4a00b4c144e2b22539b6cb8b40b4b23c837f0f155dcae7dc2607fa806466025ac2829a880a9dfc20222bb6d50c342f64e1634162b9f5e01080015511e65b06c908d7743fefd5fbedf4cc7e2b8f043e597521c3f83150d684440bec0192df2ed8cdd8dbd4e73f51c0773f29be69581424eebfd0582581b6da6a077fe678507d9e57572fc40c19cabfc81aa86c773b84024b084ed199c7e148e803853925dcb981ab5138b97525735929e9f4960393438787add6a2ca6e1ed60703fa6632a5e493018f58bebb2d07f58e8a405cfa0d06acda40d1e8de800b798d0e6dca8be1cf76f8b1c4068d06dd8d30f6abb8bc3afe4f918089ab8335cc308306649f2d633dab635fe23bb5ba80e7eef33a48fc71ffd91d52ac3724e2f376fc091da1c058bae1f68847ba4b697c1190c86f94e4d9c4811aba590b4a8d5f1365043cbf86eaee87b23c3901a098fbf1c4cc0b83418b50fbeac20823f35c62393c0eeb482c0dc388ee6fc2e92ae9d2fd58456f7166cada379f319676a0e87f55220b8cf204d8e8773c48b2e2d65f2917cf7641be63d956d0f5c0586d0d1f93531e080f72641252a1b1f7f294b4bf3624ea2faf2b630ace434e8603e0fbb381166406575264bb4a2ec0e0da8428911148aa5d3352e67ed3f849f848f98954d88c3d0d28f7e508a8ee6f08a71c285ddc5bd8836ae988fa94f621aa6578767be7d1f50af4435fb230bd84d1fe84b7ef8f89fb40c003e95a39d17036e173583edd5d880a8d41b97f4f6f529614066782e6fffce9a8a479df3abed6daafed15950d3c0206655b197aa0010a4b7eb5c0582378b944424a40e79c2026d78025bc9244576609c60a1a1bbbce3761c342ee7b210bf26f543b94126d4c0b96941284e8f1527f03a6dfbb7ad97b8efe548e37a58377dd3a7f60107c1893459faf08a03fc0e5e40beaaa2d591393f3a52c390db2f5601e72a74c02f136dba135b5ed26a92341710b415586a95c774b04f3db788d7f0c7255b8d88c076ccbbaea5e5772276621f20712e7ea702532e77fa98afd417bd2a4b383f64c8e99675c283b209f557b3d3804a3c08a5a2d3853ba9f330f0ebbbed71158b7dda91d2d21fec89b65f348af8f0eb24de65039aafd6ad047aaa906136e3ae86d893f31db601480a9e8a812025203afc5360d925161f8a505a8ba35134d5d5c25e8015fabf2ce745553bb33ac720692a14995194d27b1adcd1fc546b919a5be6a90ab7f64f8e83494a00db34716073cb1a4b84c83493eead499d72840f957eafc3b5635fe48a309f114bf2f2d2b05443952c307da29b1e848c88452b26723d0e9cdffe3c7e660f4be72f438c887003a450d5d583c2d657cf2c411aea09b24eced2105f51ce6ce9f9afa45ff358e0d73b1ae7eade560fbc18e0bf9c69d9f6639df3b3cbef662d2d9eac343503e130ada983cb07be80afa31a53113791ba5364a43b5c0b40c6c1c91cf500968bc3004656b35a854c5b1417400fd83c807d562886f918c2a64b650011a22621d28a90c3f7231ec6cab4d6dcec3311d80bbd194acfab4300fe0673a6f02f65a59fb290cae544638bb1a5330f52f7cf8bb357ff9477a4527616138e28058c83ff494c60e33b2fa87be2100d382888b110c7ee9921c23d79f711b82cb8868652f79a6550cb6ceb042fce3292a8e0c345fe9dc2ba68ed645b0823467c8bc407da180ae56078c25a6a6c2d462caf6b82311ba4d0a647e6c5f33148633923aa16aa3c6005d0df3bf8bdf44796549b0ba551595e084966888f9df273e45c2f4232a16a8451709e80faf272afc70d0546c7b1a1ef2f4e0a846f23492a58bc30b9267f056949c082025e5920563e2df6b83313e7053768eb02929bd95f8e5d67fa0121bb733a904c15befe6d863579d90583cbe613b98c78ad59157129a70f7421d7c1689427f0bfda393e40ed607292e753d07da2fd2a58fad8fc48897b4fa62140ed653f89c0b15d853c1ff830679244bde7de87cbbb6b4d5a1cdfd76d5123c72dca031aef20b9fd84ff1cbb43cf6feabdbd1c13c523a6e771c55e0c86b1071524ed5b50f350fc03eb783520ba314fe4e445443e45c2de82c3e46b88efe0e5ab7a6cd659170072df9369ae65221f1880952da98e1e9e3ee8898eef198d6bbfed9fafafd0ae906295dc749942b382d686500ea5fb892e6cea0e0dfc61bf336011b9817ac4036090921bff734cef1b1b564b0211524884834fbf973f7ef5b6a1e30b8310eeede01233f0ca31d2c918e0927e16acbbaa15759c382f2e4c8458442b53f0b9547500abac2e455201e5bcecdd9e1aac60abd908f5529a38055601e7c748888e39c470f6060046c0138b578b9ad21a291d8c8600325578b758609a545b9366dfcc49f0a291f81903da34a3e5572dc2e180b417401eefeb6db174e70935adc23d4567703cb95f83bdedd6455ecb5b6626f2d7c4aed6e86ff62ad519410c6003c60b1b80d30bbcd440b84f549da61d14b77c1e32c2e574a19b7f16c1e1a30d7aae702e7042976524d0518f1341b29b5d9d4e9d609a24b9e244d03704512b911d44af744070137f3fb6b2a3bd2206a076c7112752411bb940b43682aab223483c2390aa7027e5ff0a3ffb7bf167407f1f07fc4433a185a1c19f092ea0ff9021938e9b0ae0a8b40b7484a2ffe3f07fc76902576e9feb74a6686cc5eca26d8bb8cbbb751cc0b5eb56deeb93dbaf16c843da6181a99f56fd4e07ea7a199fa86cb9e3bdf89a40af7652ba391ede1516b415d67a595e858bb4d2bbfb8234fde95d8b460c90142092e0cb6bc20427dcc4062caa7944965a3bb81c3fb3aa4773fe0b1a4b75924c0037967d01b8cb3f70291bdea5a902a1238023107e8fd76ac185082531a2fae2a00d4f897ca49ff600f24ca9ad48f066e5d5d3bc09845eb44892e0be2655568230bde9c95317681223e928bf4d524e0d45d6bf7244fad4377aebdd0bba10c4b4c013a06a4929745cc3200bdc816936fdd7e52a6445f1af0649058661c20419e7408e23d6e5a8d9935e52478d2423bb3b3f4c96a52a15922b4f9c09cebd11ce05b04967a05711757b1ac6d42b3c38424d0546c01952fc0701010c62e24791a971c034175ad136af97ae6c236b578afa105ed47a6661ddbdf7440d279867ec0923a02efaded87ebbda888679febc02c1d0c5ad5fab9753bcb34f61b83d3a8982913016fa0be7843dd9c4a3724915bdafff4820b693432432989bf55da11c0437546006642b29a0ad46550699fc8d35887cd6393d74ebed62d1be7f51e5d8ecc5d4b03803e0a61880093277ae76f2b70e45474c36e99259698a892ba7029de0936a20294eb359df7cc249f6245b879ed4d821a243321bd8887e8f31f0ab64e19258c0603fccd4bda7eb86772c57b7208299a9cd78c58ea6ddabdd9b88e2da7d53bcc02d714c71fdd923a95cb65b1d7bde4ec45b80a1de60b4ad2744f15590f494f7a0faadc20489ab83843f15e6c3bbc8b46177a2461dc92fcee85047a8b6571f1dd063c2333bdffc792c45448e01a3e03fafebbe2cbc9a7f2b018c213db6aba23aa07beea7e75ef575ea45b279b48adbc63422a90a0284424ead8919cd9019ae55401410f0c3fec9f09cb2cc03f71c80f92ddfaa11f4147d16505c7af8751e881da03c0ceb08de0e5bbb5d9816bff797450f9c5085e28f8b7fd83f17339791a0ab70ddd78827d22a92fc04d35ff378f97219c1c61de92756eecea5be38c520464130fcf628a6705065907977961e1e1c383c78f80fadfded1a609b7262115c6200a064f96785ed7007774d4abac76b67a0cf2c26cb12770564339b86e8b4e5cb3fb0a79171c55a8cb19e9fe0c8cdcd62998eb359b26bb3f776fce318c1b9145984d00c52cfe53d3965afaeb8cea3ac6d2bf837213b76188aaecc494f6d70d965166014edc6a409c32714f7d59feac093ece6c6a33cfa41065f2b1edec7cb3026b5a0619ef6b354fb93c5c1e25fca1994dc49a0e7f9d8e90737dfff9d2ad2885f73e007af2c12a025951095ceec174d6f9d75c1170ccae815f36943d016c93e57e2f0a1198492d870a1edb4ae70c3998d887a4934e3f0dec41b84ad6a35b7872e1c609f9aa5f6caae117cec9d35e80cba6f8cbf8753f92a5d73eb01da0df301106790708d75c56716555ce7a83a15a8399ae6bd542084e6c29de494ed89406ff1917007817854842296b744a88cdb3b6b9824009f86edf6a8950cda1a48296bf723c087e9466b198108c505dc2e79e50a360c302961cbe6ca80a3da43075b78f241a085791a35c927f01670e773356ad672bd02dfc5d30c3136933dadafac0a87cc607 false -check_ring_signature 2907b72f626d471586535042e93b0d3e4c1732a347adf222d6ac80fb594bbc23 256f47ae8f4a891dcc0e7ee901100dfdef07d49d39f0e983ef40223c9403a7a3 2 4aea15c8dd117e401ca0cc7f9ca31925f037d6f03b2b32d115e4baae4ae2008b b5044cc1f58d29bbb70d342b04ead157556f1f6ed88466f7c5f91ab29af74f34 63540c377a45dbe1b1dcb5bf1195f07f6609561ba3415382bb0649c93f103f063291dd681738283a1497958d9ba6cb2d220a17762923f5af27a0d8d6c197e0048ba31fa8b7d30074f4e250d35c4434b8603e0522b8d9e997132617732d23a90ee67bfc3f70fd3ded5872bbb51259608031b984dfa6c0a009db4561eef96b79b0 false -check_ring_signature 73a0b9470d018f10d663ee6a9949dc2b770fdce537bdf7bdf65392f5d8b91953 28b3b382e815907909bb2a9d68bf50c9abd117cba530ec5104dbfd8b6ea40e41 65 c5ad963a5751ddf33586d3f74e07215ceb1ed4b49b52dfa99c806366d531a75d ad85bd40b9c23e65f8fa81d10561f5d470cff0adf3e57cc7e972376c814aa3f4 89b6554ee5195286cf29d818652d9eca46fa01bdd682825913b0d694de28942d e8a1ffbfc9111749221fff83433122de15b84484a8587eb5229f3a112f9bb7bf 4eec49bc577c264669994ccbae8098ce7811a3ff8f92b6cbeadc2e94433ac0a3 904f10d98b5807a75aa706ce5ce9697f80ca286dc648fcd5e14086dec45b31f3 3c24fcd2292966f80c68ff6f7c05880d2ceb53dd4df8bbdb09cd7fe946fb5d67 c89b803062da594b4c31ff01279d4c7058dc2aac3462a0628663d460cd3f12e1 73f5e943d8207a16591b30f01a43d042c898e308554d54971b442a510adbe7aa e78b9961ca1688b568c5443baa6f68fec3c5936883e7e5e369f0616522c2bac6 2898185b2deb82128bd552482c4b71ff688012f3cfdf784d95ddb3ee17341db6 d634fefa73be9ebc78a8de7f9ab3ec6868f36b9ad82cc047aba8b24234d7398e 65f800102ce9f62bf66ca9cc2c8cd5bda337b475dc26ae8e4565c5bcd957f09e 84e9f821ac5d8fc63caa542640e3183fb3e835cf20cfd608a8d6b18a6fc9dfb7 c07169659532033dbf514e34d92a399f859a073a1a6b32c0383e0a03b9960aaf ee7cb8e2a9f05b31ef215635b070506b7254af0a220a9899e9c0f9bbb27ad8b4 d99918ddee40d29bbc0090595d8039bfc1adbddec26665e7d36d5b1cbd29e127 95387693768343e512f89839113fb6e8a1a0eb56f3a460204076ea277cf2a9b4 dd463d3b9c236910dd041a6ede869fee576fd61ead7912f97a46053a0ecc2572 7494202c7dac5f7c3f9a382e69c554563c5634035e58b82e6051c66b618b7e97 0ef40e9dbfa99aa52886ec95d90defbb8708d3b8e44fbf246776a9ec9ebefdaa f28af8fb2d74a547c06ba160aca8883aac23b3a7ff4722efaa89ddf1a10d4e40 05a7263e86abd429f4dd3645fff6e96ce687c88669da024cc2a8f74b6b65bda0 ae0863efe81ea70405d7de90a4c03d7aada727c7fb47d51d365861c74213eb75 dc559c7e70c65480f695e58a41abd50c5c4ffd2c715c52af740982800336152a b977d28d5c502ee3bf61f26be7bb7b2caaee47e421588cff7ac8dd4113fc5346 89dfa11f46c2538753c535ea1f0ef8d57ea16f39bbf56c6d652d25e68e8bce14 e6515aa06451b6be10d0393c536df110beaa7c25fed4d978bfbc744914dd9a87 e67e36993374022faba43200a21b138decee0aa24eee79763c05009ee0883f60 c7bb36eecda00245c122f16cf5b1d0dd7748bd666496f4de525104e8b6826c87 21a83fcfc283326681769350131cd798bef606b334b15ef2892d18cba0d09298 640849cdaef5d0edd348fe3c083271b78fdb3cf2a197d4cbdd575e407d80b4c9 591be6b8bf5cdd8d1e65a182e98600f529edd6d4c0febf7e8e5860f9c03e581f 72476e614ca0ef425f89d1ede02120ea333f808d309ac94bab6cf9ddaf9f39be 1e504eedd6363ac0b0bd70dbce48e43f52d19ab06a867f776500bd48ffd63413 25d0431c271b34463c99a6a557715b07737833ce263feb579fa681f8f5b5abef dfe323473389b2166b09668d087d917a7fe526b75d69b47c80975b85463b4cc4 baa1a22b5c90ccc502c0ae2cb2231dbe11be11d2ed5670c2145e278a1fb9ded5 e411625a2cdab6515f7b7859d3bb023022ebcc0134b91b282834d232115bd140 ddbefa72ddf986159005da1684b7a48915059195514741db798370d655164da5 0f7978370955661411246924fbc307a417e3767ea2501fed7a2e8b358fd808d1 d4de5b072639e330943885e53f6c078e09fd5dd5c6c4ad551360ec695532e495 3e34e345f2bf3be45921b2df5bbfa7832d72d34202717fa25e64b6914b830d64 4ba16bee5f7b9c873e8c3ab6e1f1656e5d7942b3110c66937a34b1a04e9c217f 14fcd6120f77cdcead031a17cb77a3b22a326ed0992c5f0960aeac983991324d 998ca8b7277fa3992a245cecd8f168f8c5329659c789feb29c814cd14bfc6a5f f5885eb8a4c707fe9e81398293d76bf2acc06b270ae1ccf89ba43272c36caaba ba4fc55480afd863fa747945b0d7ee79c3dc0f587f839963ca89d4c24e092787 0f22e4b9304fc3c34c3b304444a7cb1e6598833560293634a44b17580c88acfe 6ae4455d40b84d49de2edc2928030752f66e77e399fff709d854c55b26232afd 21d75217460d4918d83b589f9fcef36c795b4f7d53873315b8dc710a03fa6ec7 af4bdd1025b6eec4966979c764a09e018eee1c23e3a7042f2cbf7c8800f7aa80 4dc2fec967284eb74e25c1ae6bd83c3ffd95ce67b35a322c74a51ef69b9c8109 90e8ad82728c759db4c8e66a14ed8780ccebdf55f3aa1ea9500d602f597c8b13 5e841a79975b17546878966c2317523c628026d78345d4d41cdfd296be5e0305 9a58b7b15c27ac0b4e354c25d48d13c74f7a869c10faec376d52252e11836276 ffe34396bace1cf95b6001d5506f1d775af01ba7cad59b8d0b48b17ee1c8a795 6096e00a46b3fc0dcf7a4c2f701bc95339f709c6c40c7ea1bbe066fc41192d35 932170c8d24ec1739b4b112528167398db6c4cf5318e743af07152cb9dd7f480 836cc039c150ef09ec559b896cba9d81c29cd49b5398028a38fc323293692374 72ed35780d5c7e1b4bbffedae24effb9ca5c755818d7a2ae8b4a87f93aeadf6e 9873e723254925e8e61f7c68d091a7a5df9000030aede66c588addf890b3334e 41fee2b40b31d6691a79feb07c75f12f83895ee5072990e3a2e51b985e229b54 4dc8b258c5f20d7a5addf00bbac2e7c9bde98352dc35514a0a7142b77ee8545e bf006f3d314e44c06a4ed4929cb2415fb5806fcf8b237e0012d1c804c2328a59 66f7337856e261d66d6c28f48a85b73be40026a3c913e5acb8b1538d1c6a2d0c93c48457374d87d04895ea918c599e061c214835c0d1812fc90a3f555751e709ff43a7b00b26ce636c50eaf0d06f3e8bf6de51eb3fbbbcd96f053ad82e3ec5062a7f845d09b06bfdf4c6bba9e63c48d391215c1d49d74ca9139702070a00b800c01c22457dd1d8775a1ae8df2749cf459a36df64d801201c5412796d525acc01508813f1628ee64e61ed3dfddb4eb439ee8c97b34e95ea76d68199d25b9b7b0bd2f9f7c95844eca3493e7408cbb7c5593113f5af0f2fbf7ce1cf582e5569530c5ae469ec1fdc8e38786d27cb610e6d1dd5133193fdeb8d6145021eb5c0b0380c619394de6da8c85c9a26fbb22f59547961aa96f35e1e872d9ac81c5079465e0801d1b3aeacd9a37864c82c63e7ce0b001dbd6c4bdc9c061ced12bda81106b579858fa902784c6f0579d0809c8892dca7370a93156d7ee0519ada91b9e065fe00742af18618aba86f17c160336ebe0d97ea3ee33c0becc40f1975fccd108dfe03e6ff5ba6d0bb9b91957afa93916b536825833f76799879c14b69fdca9ba592044c07caa9c860d90136d15490ca57fe2eb93f5cc77ac5f2607d2424f32f88390df295670d18a47484318a613a1deb4a267762848ffdf9c7480a139eaa70d456059be36e8068a5c445152be6edb866b8481724b9253358bd098bee6851d801130f984d8cb2162de9663e6da2b0b5bd9ce429b6bd537c2f061900c58895ac0f790bfe3c1b96f6aa9fe45dede2de77e622cf5d9d3d9358b495131e8fd661a4ab2503255398572c168d8db35160b1e4151e3a4fbd8be8034fec1e9df274cbea1e0703a7cfb35af59129d54beb38f9bb82827950140eeb378b2ce3040d8d6a6e852c07e0612ea52e1ef380b24edd8d422bc1242ac6d78885350245cd1fde02145a000e2c1adf4b2123ca3a4c4a2d815a0bfadc31737c1ec646969962292d0d622a4c0f136dc1c2b1fd609fc3d2e716bd67ae57146e29cbc5f335b5079160ce9a475d01c18da9b585181dfb05fac577baedae8eeedaacaec60c002378d6bf0ef0bd840a999dff53950a9e1715edaceadd18bc7a85f8af1700c589de47f721562a54da0ec7029e88f981b735d4a5891874a742984ef59cb5dd498173138e5e52efa3b000f766389ac13c59206631e37309ee9a4abef65d65b690c0941a036208a87a51050c5772326ace2bdf7dc6acaef47bcdccd0399dd94a2c8b8d135788f3ecae8908db10f2efb68247f73c297677ebe1861864975fef69925b7b202a83f54ffb860e2151a5661eab35e105a8ca1abb61f77d3f66093256cbc298039173b4a9ad060acb0728ac62b07efc8a293cfd90b494c2078984aea135b5a4f5170d389e2bb207542c24975d00e6b54f204dd85883810e56ee6c2e18184e3d0440dd15c870c70bbc8bac33621ff6d8ff914955cd6b6e1c3291b51b2a46ded30443b9c244d127072db7439d2e1d381620dac75adb347e8962df4ffa4433f69f5ce1a11ae29bbb07e64ecad6bfac66e2135bbaa0f4f8dd6c0cf43fab58de2f74aedc58240888a507750e24dbe98c9613a19e7c672c48815615e03a4dc49182874bedd31049f33e0b8a463053acc8169c389fe0ab7e304ba260fb93ae86b138de5cc8f312c12b430709defc03e14fc223c086c52ab054ddfc6b1dbb6b89785ea2021391d1fc675605b5df5527826be23f98c381e013fd16e28d05753774dabf258ceff749a27db70d0f785c71af36e9d356477a9ded2b208ada84f67b0e5687624b87ac1c12bd490eb8d0e430504b023329f9e1eff729a548777f25605fbcd261ac69f6ede3d71c07a854f29989d119326fe62586da872598f01e4c840e1c3f8b0ac0097706293d06a11a63a9c7c233232709a2890c373d3deb7e362ec04d8e5c445a20788dfffb0c3b8c2502b2f834de247f398da393b40b45e49e194ef20dda0722fd646f835302844502296508ce4ae4d684a8b44bc204d6636015311d9354761817f04c11e303fef40c2353697071ad8bc2d241a686aa8edf5e51f747cae01d6cc4366219280981505d01f53733c9df7c22cf18bcde72f8fc39159c44999e4a210c62373b21001c11ba4df8e1f32d241a5da050cb79fe0613d8e9e2072171396684069368fd06d7b94033a0d8c437438faf6fffe738880b65d5470db622b808648fc15114b70a0fc744fd353548491f037f420b9069b901d1a566021179b3f980918246142700780bd8782a813047031691b1c2a9c1c29ca2c58dd3aed130c8281d4a4b4cab068eeb0243b5d180cc9b8da6523bba01954c6143612cba9587a733520439f48906224d02808b4df34802b51ca69ea4c3aa048e7245b00db8ceb28975cbb3dd5d01661b878ff159efc8d92b2d9353c7d5698663a2fa8ab1041d32398bdd8e7bf902ca44f19d9646b029a54d70774227580e86c3fbeaa50807401d4a8b7d3392030982b4201b2879ebf46e3e89df11d6997ccdf0fbf0f6554a7cc6738de8570a280d80dbad2929b56774f7dd4e3795ca45f21d14548a06249265b6af6139410555000b88bd6070c0a996a365d4d61950de1593049036ff8ab9c2dd4f7fccef02ea0fd225f9e2b968b9829ab0e7ef761de49bc0387ebb96bc4b33ab25616af2582b0f86b9fe94e584657fafe5d8e80d2348b06dda953e8b245bf80a56f23b8250990dd46d8a40a9c592798c97e1875dcaf14aa7e9aa59f7a77cde24f1638fab3e0c006b3badb9f388c795282d84db91a2292a653db21c3927efd05810183918958e03034bfabcaaad41a2a3e08bc1dac2536d78de572411a0b9e00e41c1761f77bc02b57270b035c8883e96b73177129415045ee424311f45653bae087f7cdb1786019f9cb3665cacd950f9c7cadff74fb97faac1bb7a7c54ab7c1da9a96e3136e20f17ddb0925cd6cce9881ff28c9f1dab05cafd52ff90c350366d3ba45efd877e0ddc03ca692e39e7c87ccf12ae72224b5b858bd42b675b136b510fc5136f98c601ae5f58270f8b51085d3991e7fafc7d3ecc1a048547d6c559fb7aaf61abb53f0d270aabc45fe731cc0f14edb9c26bd1ab0049da3b911967da1844501bab7ab7086d9b6cec015af5fefe27e1abcc9a440600b9136196301cd3366db8130d6bd90d024fb4b91708edb951050aea266e9393762adf5492c4d879268d3ce4e45ab805fb8551e7c566eeb68986e870f7351a37ea66f0285a53e02406dcaed9b5de580c6783a289d74caff2ad750cfc14dd8725a71235b4410ea62d59b3b1fb00061d01c0c3ef98778ef5295515117ab4b457dceb502ed0a9eec3c992a9bab62cb0b804cbb3ebf7e738898d5e312fbd7694e1d92bb2d269a912ee2514f088f61ef7920125816237a9452e8210d06b61ac8a620703b6e69a0b2e4e181b7ba1274600ec0eef382791cf654a6e3f6feca45aadcf76afb263b82c000ebe7207f32126a7ba0c19ec29e1c09451390423b4dd610edd6a2f0228c036aeb554cb8be46859cbd20e0e3c3ebaf0cf4c893ebba7871a87633676233798e82e3cdeaba0ae5910d5720294c88ff6f03772bc05741ff926faf115f4dcd69f90f789953fb0db4a35962e0ae6350578b8676cc484dc0b526120ff2eb932a94f591b54d22fd1f48281dcac091a00574df84a175f43e8f407a3587905c11736e05d4f24e61ee27e0dbe55dc00c43831d59225dc480013280018055f84ff59fb857789db43ecc461602f3030003b26a44dec00e1fb3912c587f46fd1d6a4fe8430bd7b97ed6a0364c0ddbace064300c42264857be02d5e0964d1819d74779a3062390a15746699ae0f9a766c072c49e6a5dce6bd2937375a5f0b2d981c9a2558a40ef2cf90f3d897b0c207ce01076ecfd8e0ebecba49260191d7b3698ac400474a4fd4aedebb483e407759fe02b4f84b1654b5b9732e53087eaf9eab7247eb1d3b6f9b1f17a11901d9fb41590e35ebc810f3dc100eb478ff33ce7b6712092c5fe5cb4013af8e020c67db60280232f6b13dda468e73108f767dae8ea90321c790d17ec8ac6bec4cd609aeb8a2031d7fa127baff8534edca896da5a35d9a66f8b76c7c9849115f64e0c66f12fe04aa5db3ade8db713505d002eb15ee2a295f586400a251797a2bcfb9489e61700da355eaea65a1db95a1387f6a5775a66e8faa4d02253de4540d49b160ac2e8c0aef85850cae0d3132475abc1ac9135849e814aa74be24e225373db843a2c3750db317a47a7b69c18e908dda2e01c326ed5bfcb4aa422713e1f51ce7d67a12ea0ba5330d9d8f673a124f8c993fd7fd179b02c679476eb799572a5be2f24fa08d0a122152d74d61b51b0b8ef126b698a8c209c809b5825acbdd3d25f1b56d5ca40a68616f94ff7d5d7d09eed1ea31addc72a2720995a50b103e2408b401993d5b03e633d2f43d55661abad388a0af201a176c045f5f73285fb29c8142419ef15703de23f7f16d0549e7ca08c147845a6deb4f8ee804574213a62ba587eed954fe0186c408bdf0c2276c82f622f42cad49f2d02be3654da67aeaac8d30ddeb0f3d0c0422e7745b996c132fd4a2606569aa14a2697da3ffa16e20473984667bd6930d72d8886fe94c591658139fdfd1e4dfccd3e8be892a44cb298e197bdd136c050408a24aba64cd20c5931523ac669a497491b0d4145b8be70a69af15aa8bfa1d00a65cada924a443bc541b1beec6eb56b7b53484985590f66ad1d0594ed6d0420c8ce63cdc0c5182676ef0bda49c3268166464fafa8ae397c3efa71ba92f0e400a422857d344b3120f281e42836d1ae4af1cfd8372266b3140763f38f5c2fa2908979ef6be5f6e3bf73986481617c8ffc41db2af6cd49c5ca5ee291e4de459580614a39868263a4305a2db9ebeb502b32f8e218b3d623293b77d3c73affc801e0ef97181596bd36fdbfd877107ca5c6176ec782ee4ad5afe4ae2b4d23bd9a7ae06df80002c7d6f579e27734d09a06483b9692ce381ed5ae3234b77632a8965660385f81893b49718c490700af81610e716c344b524447a396adf22e15ca56f350a268cae5e8f37ba2412497e5d4ad8309f7ae65f5086320180507b5e216d73d00b72c02172b6da51385bb80f61d35854cd93a3414130ad0ee7774bfea8f995970d00697c5f3bc6a656dcfb36c11f40a27850b22d80d5af97dee55e48927d2cb00683a21ef0a5efba09effd1f8cef3ac117ebb0d6df4915a03b455a067523500503c689f01a492b8e77f0ec66a637f650b97ad0932bbdb9e72faacac205f958dd066f90c33fb995b216323f99ef04f4d3d6c44294dcb5946a258e1d4bc30834390dd6b7c47d3d719604058875962befca0ad70a9711fa6813d9953f28a105b6650180d623aef385cdee8ef085dcec570827d34705f242c5fdcb4caefad4b9dac503d45df72adaf2dad238576bed4e874364f1f1397da02b9bb7f972ce9b9b9bd105130d5299c7834896898e286dc0d239004a95655d3a62ed128a43ef2216415901d9d06b96eada95c5be8b393c42da0e78b5c92515cae55b8e4a90e7fbeb54870d3889f4466022126d1f3cc3c9ac7fd34be0cb292870e93dd85160d783ef1c3c0f125a628f0881eb13383d4eb00f08ec9ae96854ba43b67b03aef2336239ef1308493a28797e30517d98a238ca01f22eae0328748fddd431157147194d80feb50484bbb8a0902b64953a71242808eba4ee245486fe325a5544ed57fa364025ec040598d5a13da6a92395ca397c4c9cd2b631afa7dd3e50174cfecaf9565fb0a60c65c53fb3ee1ec267d3f64a60ba8c923e044dc1f0f4d2c3ee90cef7022439090c98d09b1398af42460fd6bbca77dcb72a70fe4f2292fbd24a19591f33729fd30b false -check_ring_signature 628aa2b9ce91d2ab3ed75fb72530c9badb0a2b254139a03b18219d3bd533f000 3318b4ff5a913fc709b4b1f538b6237d7a77e90a31de9c0c052836e2314103d6 12 bc2042d16a5b1428c5d1008014aa64fe3d88228ebe24873bf0f4769493e74869 140e705d7433883469802f370039d080202eb1c5a23134aa1a3fe8374341ab73 87a73a2034531edf41d875ca9740ea52cc41fd57b229a08300ec2a30eebfd560 f83699b2860c1417c4d84ec13782c67056a2b38e0133fc19a0c10a1e4e0db06b 944afe70b7cf50059337b22cc2df2a5dadc30cada1e6904ff571fb439c96134f e2652e868d25cf8016baf4165f4b68a293564a5b715ef7d0ed1c31bb659c1e48 d75cbd4490bbd6774724aa4e8cf24162b2b6327f62e37512b9cba692966ed396 88ee0610f2ae4b9318b3b10ce1aafe867b68aece82d80ee20c6a212a4c239e9c cfda86abd65ad4f8b6c503411fc0a1bfd0a6959c4e8821f6c37b4e9e53b0e42b 6c93189cec87607ce8d164efe52edff46a81c064104675d56088889be8c41613 140263d8ccc165f8fdaee820537359830e5a379ea86e5462cf80189f34d44fa1 daa12014693bc3371b2c3e364fe54d69b6c0513b428ae9f2c4e1fa4cb82d0ad3 360b3459635dff68bb913a9341b4ab89462a78748700a062d95a27b628989c0b0d444e2cd57d951fcc25d259373329d60681e5891ada48f1eb6fbd8d36605cc3c9e94a753f305c525cda103201a82c27b5d7cc74c3ac76740e9df0eea5c2f10b4a0fc97f262cb09654d700f28f5a45fcfbaf603b9398132d5129e22ce37c660df7e6ec87cf122793dba40732d4f1b7898e393c6f3ddcc0f17a9e24f345ff3a0c5af5516d593c4b3b55b728151afeb187acdc049ed5c60a90bc14d0b254c7ce0a89e4c1d50235f64bf5a2d33d1349b7d2a3c2d9515261c653c57c6bcabf4b320dc00a158d836e51a0f89b429a0281b27c6c9facaeed651160bcec793287249a011fa802e56a3153ccbfc3b470d97db80d46c1f1a7b8183f20c223e339220240022456ecbf7dde4a08bc67a2851877d8cd09a5b6ff9781fc6648ff73ccfcbfc609620598e27ba3b86bd5d9a61eca851538a72030f8ee825a321776924570281d00f77f090646866857bd90e05f4d5ad3f4583a7b65f65495569826c5568ac270061b16184e9ae5d772fbde31b3e5cfb87f208651d89744c334ef8302240d7fa2058ec963586029969b672a875dc4e257d2e457adbb040f7e5f3be67f9aca8208078cd910feef4dc012d1e3d9d284e249089850be3e6193581823a5f47214d5c50a1fdb26b7cebfa20f86e4d44cd8089e33613fec1e1f1a0e9f96c2f3ddc92da707ae20d831dcf1fc65f205d85c50d99b35c389377193ef4ae8c0ca631fe91b290e9f0d0ef335f15047a94adbb82e730dc0c245b2498ef8057ff6cc16c4b3e2fa0c88cba7ff8036ca38cbe3150049f7f6bf2ed00dd0e257b57a1ab21c3ead1b95039d739a07061bc1ff29d3ad952d3079eed7246a85513fefe7404ed486816a5d06169f0ff9e89b6801b2d5f14cd48cad4b1069e3480bd54ef0b4ef95bd8d699305cf6d6a21446363657d1a48ea70814e532ba312ad89e8d0558dbe2e0acbd04a03732394d1ca3fb47917e39a0b88a0981d87b406e7146cf3aaf3ef92ef471ec60104dd0eb7f459ffddea4db5a9919d70c24947ae485da26016e215434b3ad09206 false -check_ring_signature 1edcbf4ad5ea88cd579489315342f168dc189aa9dff54eabca169e79ce248f27 128413a4bf01fcfb3d6b28990dd8f48f4c89e4cfca46277bdc513acca55f6a5b 1 9b8ab2f6233f865b947f79f3fa5458fc99967527370c4e3ce59b2c0a28e539ab c91497a0847edc17b6f50bfa947fe2f37523f3d3f9a110cf94a4abf1460ff70c3f234ab464fe6e030430a818a6a0a83d13a33a6b41db663e10d30b0ab4d3f405 false -check_ring_signature 7f30a630f195aa14c7ac8644937c7458550d6fae10176136579e96a780a02056 32a7ca373d61d5da7d4c655ede97454e3b6ee70e2b9e1b70c9eff1c21efa47f1 14 284969bdc8d3b99392ca1a1be24191e6700fe599a8a465e84c884eab81e309cf f37b57204bd99601328d78ef31ab6d4e3cc71ae0f207595f638f59a15b243b21 8307bc0a377593a4cd71bfce28c74dc2b7fe2d72230d1c057ae08b8554b3230c 3037256e8d6552000f1a04598c8e143ac1bdc3779c5621f9bbce6cf191f55228 994209158bce1173fb9d9f83e15952f32dc100c5033aa9060545aa59bcf998cb f221fba7c76d16d550268acf1ca43c6492e1e4600351c93ccc68cbebca551408 cd7f3d9fbb29dee4beaccff2e149e8a84285eb75cb96869388469392084fd623 570ed5e1d711700d4434b32432d240380f9b456002114d7ed20fcdfed6ff247a 8e0af515bfe4c3c78c2524ff417a12b0c3cbfb00d4bf3537c0338ff07ef0fe50 659094825510b412923d838dd2a3402ab51af945890bfc3e8483c596e98f6ec6 953373d430449b09d9aee778f3903a9d2033fde30d0f93062ad78aadd7eba9f7 858728e3902edc306798269b2ac21f27ec15c1ce1b880e8b5973c16d8464d1a3 3843977d2bd0ab24b053b4981439afff7887ecf5b433e8f726c836fae6f9c0a5 ae2e317c29048713a2c284aaa629cffa75263054b20b8b1530ab539876c63cc8 551055e1f46dc32f72f6bc1f4eecb637106e383d15a42912dc44b873c46cb80ad00dc020a7ae90cad83b7ffd1b65c1d355aa3cc2191bec1aef30fd59fef86c027697d9d21ecea0163adfb09e89521b951a22274ca5896aca65bdffa86112b4002d8d6b461ce079742241615d3dde8f79bfe1808c3ae96a8d95c0315877d7d709235a6530047ec990e59d8a1890c204b9dc26b7f54301c16d3e2512d2de992802e0d74b8b578001746b0bb98ec9e44d292ab7ac36f1d5c8b44f9028729589ec0d2d2edf8b3fa10d829f88d0bfe669ce06d2db0026fe2e0f9f98ed5e4e6ed1480a76a185e934be624ae97032d07bb86fed87b4db586d1b91d10f9ab5ef7b64e801fb5b19e39462e506a0d65bedb1ef6c817583fe10e25ad2e99b740226d379510d4ff84b4a85b4db944dfa6315fcbaf607e0c7a9a1bc2c0916a210786407342407664d97f88c66951ff6160dfcb9023046aa212e0dd8f8f701dbaea8696699dc0d21de42e19ae832f807ebabf0dcd64346873c7a66617451b5f06906fd5c9c3f0d896818260a575c3c76b77d0d25b99af133e203226fe27f814875d0e136f9b602ad3d151751374873cf915b3354f42963a910b405d2d58d832e4610af22f009062036da190b41e6f880bf08946bac0cc9c8f121666462c2a271568267bdfea001225139c1ca892c3fc8de2b84d2fb2b8b96a808f9a0d945ddaf9c59fc8cbd980fc9000447048d223d21f7ff61362f0b59e64c774331dd38a97d109993bcd15b0f073ec02c671f78e4ba7902cd8556f7b4038fc64b6bd8d127e767446ed14e5e026f71bae0578225a0a59257bb5c6fb2915df5c5abd382f5650de9e2fb7633980623e39354b8a74055bf5d6d8a78621b6cb465008b50d4889a05d4b155d9f6bd0275f4ae2f0b2c246cb75260cfea13553f14eee9be3678f115dc352d526887220f9fb38643f79e268dfa9ea8b84834d9d48e32c8a223f16f51ffb0538eb88fae01eba23cfb066825c3b0993350c6aae9ca2b71dbdaf1b9f0d08b6e7ed68a2e110e12041d626785c16704f415f2efa030b0bec31ae81599c62e8dae0bec8cb35e050791b84300c2a88f405e9035d85ee431a728ac757c90345062ac693d2811da0a86c0b987fe3b7cda15e3300a3cf55ccbaa550f0b596c8be39821175ac962c90c9e0669af4493fbd2313e6ecaa7dc87082dbf52199519a4463f7c3c7c95ff7d0749456d6e5deeb5cecf0c438b65bc5d734d29fdba657de2f6cd47486a48d0ef09 false -check_ring_signature 01855f8572e67d9db7bc89b5b83c0cb6e69aeac876e7c6ddf7f25c4a4edcb507 d3fca1e687e75e53f2ccc1fa08be4ba0696cddff38299bb8b8ed771db1769dee 61 dc5a106fd99a9f62659a275773abf8720e3ac0ed99a7fb2e0b11cebdd37119e2 3e69811795819455f45d379df7301b1181f20e8a0433310463c391d6dc000716 52ebddde857af9c573cb7f873d3690568c5bac64bfd6a85133ab7cd619a43260 2a640f7a237b612c2084d8677ba813569e6c0fc6bbced5cc11a548c5892690a6 9bc12cbf5694f60564e8f95ddb2a1f5514f7d7d04aa03d5c8a93e7849c80c4d8 5c066be1f5e4751de5398259d14d9da5830f6cca3e5bf32dab3cd2940af6dada 7703c62a05b4175c91f8c9030bfcf7017305385f9dd2e595cee09b1196b36bae 19ae6fc57a81f573a3473a9cfb504e7c9c46a17e8ff75fe905a389ef73c15987 d5f9f69e55ef4279f4e4ba11b44beffd7178c9b8655f9bc37648cb68457c0d7b 0b35099276636f7b18dd8e0a546600c5e967d05d776ee5f82ea92ce3525fcb4e 3a697bd12e46f8dd43b50b3c0adcd7b7c5de67b3a35c77d0251a931226945b42 96e687b76cda7d7f8aa96a0400b3341c23542273ba6c6123d7c78e3ead4fb98f 15c41cbc20ff7bf9c4d05ce73284319e48fa6c819f57e04daf291a221923d460 b76110878c8bbe375c16a1259f55fcf1c9db057a1d0721f8f78d59bd78788615 51af667aa0977d7d2c3355baefdce796bbcc65e19f4584d8fc514064069eba53 854da2721da308221689238cd1b0965e278b85654f367ed3eb059075909f71db 27c1bd3eee306dfed513125a766a3c3072bfc10e2a19bd40a572d63f2a25a6aa 4e5e810756c8ebe9bdca8bb59b056b9122753a94f7e3a89a28db02f484277692 babcf0ec9a23c8e90e54161e1f1628097c4d0d62bf328f1db350a66d50b156c5 33caf93fea090ab925f026cb9b0e7643ac42b43df5efad4a92086f2c1d2efcfd add7476bc735e662de9be1df34ced3f5f3c2a4ae380020e9eefbb29444fcf49e ee42626f0b1fa30474e77e76a2acc8c99755aeede17015441fd14263ef2c2448 b66c05872de1796317dc5c8f478feb245eca2fa24d843771a72ddfe86aad7cb9 023166def14f74c3e53241d8d1b90a02685d36b2cbc2035da8f2b0191782460d 6daeaac747dd7e1c728ac7e5d1fcaef1eca0d0ea85e4c7736de279524d0678f2 c7617dfb9a98b39b48b87bc326120ae3813d0fb6fc94595e8bdca656db26bf08 c6fc7f885c12daf801fa80ccc80abf087a903a2fc20a4381368ed38f214ba340 ff0d4ce81102c4ab3cf2c1a1f6034f09e247dcc288bf192d6b0d6543e302ea0d a7576bd884f705d8cc4825411334c8f34c4c6b9844b11bb3c9df53255352fb95 b0d94020db0ad6195ea7bee23920e078d8431118a32a299887c5e425793aafff 5e68a3d4d6cfec4c750ca3c9e1b15371a125884d501e91c95553ced44e5ae437 c2617d9202811b5603a01f2402a2287b513757fd70c8d24185c82a81cebd0c6f f88aedf44d988a1ffbe7f232e215dec050e6163cb652acf4b00bf56f77b5a5a0 af72fe3845dc68ddb45e8b43dd932fecc5cab9986c9686d6411e0c65cb7f0644 dd92f8a9a1f2e3e80caff5553053596759311f4cd5932a7ffdcca392bb33fb4d 3f052198e404933689ed0f298058e9326a9f2d425c29b7a5e08e345535e6e49e 79b709b4e4a2f16de3f26bf1921d98538a72f0072b4b74b8a054b0e2a2b3a0cb da21f282feb9611e6d46dcdec42a08defc551c6c6878abf29df19e1f771f08d7 368d811125b9557b60af43c4644ed8f74feb05a845cb6285e9ee671141a0336e 035b5932d5663439f768ccc5740ad7f731a4567b133d8b2d47fac25bec54044c 89dc82d1081ce7a7d50246410452cd6912a22f4d5f17f1ad91222e0c2c1cc140 94d555160bcf463bd00f279706858b06def011e6b6a908801e19445f1eb612b8 5901552a5fea15a490f19cb150dda55a04d1e64ea5ecc9acba7cad7bdc679ab4 c30468cdf48e49efcabcedf9b7e8e3d77a920cd2b016bfb4225d45cbe947f602 a29cdc9a9b68387a29174a036e93fe90aac97c0c59648024c070fe3ee38ab853 b18794a43ca2e29565c47e6164cd812651b6e6e88937142269d9b3fbcbe2f24b 4ab4f2a18aab3cffbde0f052f982591490345d3c84c92066a1daa9bf21fd0fb5 5b23736f16697eca32a549e4c7bdebb8647fb509a323f5409cf0c6c34afc40fc 90bfcfa39d65427e0a3d2dbc7db67b6a4e2c1010d4e8f664281d067ec7c4dcad 2a31ba96c470360153e077bab0f5ee1fbdb1446a35ad0993cbb5017d6203ce81 c1d323679c1b0f698f405c41c5273dd9e828b568d382b605ebd7796613a30b50 9d076dbfa4626e8ef7cfab87586da2f288c5a66102fdfd72bbeeb58207a90a44 ca89cef63e3185a292b373d82b9960a15ad862eec450c53802503b86725bf60e f82da4659d9220c7020531ca9c023096b1441833c3a58038bcb3cfa77fcf99b1 7521017d752fc0eb3b97d8948abb99590437bc1bbdb607e5d59c4c62a843420c 806125978b85e2e547540fdf8274cf27db92fabd00e59fb7dadeaf3fee8d0f29 6c1b7ff0449cf80377fee01b77cf672b92127bf549977dd208182728fedb670d 9ada3e5d20ea53864a5666b863585b4b0e0a7443878fb5542ba7c5b4c6bfc2cb da68cefd1a7b2750db6b23c6f64865406b552c421790b1027e1768d48f6d4056 683cfc386a5a0b6dc9adb3b643fde2eedce43cfb7c4dda9e11ec3e103738d900 f6e1ec27146b5233d25dc2d2a078d7440f7647e29361f879558adb71c3510557 5ba5edf8dfc00683db58bc2b6df274c4d1849b6edd5a1671eb85ed678755c601062d643fb124e4a8d128e1913bf43cce24b7600dcc84449c47b96888c457de0272138137ffb866ec64db6815d231958c393804a3bb12881c8481e62e47cf50045b5c1ed7ffc5c2ed06816737565c1bd5d71178238ff1595889158e93ce8a130860ad9cdee111add8e988a6c08e5c18bf657e073327927cef3cbcba79a99e860255438960cde8ea0c72ff12cbd7ee6072d161e4dca65b332c351631a671f5e300b4a9a4fea3c7462c331a46afd0c5788c2c2668f73c384351806f168071cdce03da7bcbb09867210ec850a778b939d681496f6ac3aa4fed11af26e7d8537714089c37833c93adc61e1809826390260320e37c67d73eb1648be53de58c99bd8d05b0f46614af2d4f383e54017defcd0d5eaf7192791e435ffdd18884a609c8f40b1ad91c048c98beddee10d2af86144a0781691f84aeadb34e5fbfb5d7cc56bf084a004f29e51b68bc3985b5baabf33d091d60ac4fc7b0d2e7eb8503ff33e7240ba4b29dc3038b5207660fdcf1ab4812983360f0424f12fb7b700744bfd8ce9e0cfcfca815064cd883ea5c617ab8565e51abec66671bc14bdc860a430ac1894d082d1894f4dacfec71a8e4c8575de4d2dfca0424811257fa567bb9b23b07639b034526db03e8c8b884445ed0fc6d61a4a2e8f895b1fc15a96b732ec90a3e8de402903b5b56905aecba55d1395a723c49fc1a0219f1aa90dde60522d5dc4a77410f07704b825187b77d6234286e06e16eb33b18aa106a04d27afb2f377da454ad08142c0761e1406ae6c0bc7352b18d40bcce41cb73c68fe2a58ee40fd0fa1bcf0dc0c558ffe9eeb841e019033ee263b75275d5883932c47800260f0c6545305a0d5906b571af67e48d4c7726f230b83148099051a229c018eb7e5afee81ab5aa0c3cb5bb1efcd68cefebe9e7a6aa20fa941614fda84fc4469eef4b32bd0182d70f4314a743758d49c729fe328b831962604683f41df22cb22222a16f80515a73031d29334dccb51bc13fb7c86ecc12b58ce3f13ffeb297c102887d41892094e606937500d3eb6f4e2c516666b2f801ed5e2bb58d21fbf00e416b913b312015f105bf8739a56b851984244fd51fb953f62db05e40847d64ed410fd1106016b20f0c987bff7f95c30fa93cff1e599d55959f04210c33f35e58dc33500252ffc8d800196fe2779c9517d2ce84f771369df9e8e0fbabebe4f0fa62cd58f42988967006b6160012ce86c1fd69914e2bd8602f3e4399f7624f8b9bdfb0e8e65de5e5640c71d37221193406a88dfb9d593fa3777e48aa7ed88fa3c258c3967db466ffdd0deee012cb1414f11a05ef0afb8d26d33f5731f5764f0f6cce3de64d572fbfe10256f66f5a5efb27da13142f435c6d5616ca317afbc748edd83f9391734c523b059c4129f69518e063d007f66185552ccd1b8768ef3fa50b61df15f62ff3cb600c1f4fff5034e51b3cafaaaeaebf800d9ce1d43f6ef455d12fc00fe55e91a2880280f2935aab2805f9bf20c1297d829246876b8a3f15d02698908f5d3ec77e94009b4b6f81501844c1768db573a90b411c777de53f0bb343533f218e5c22d3050bd634a42fa58ef452bae08591b80b086622d8307372c9e33d693e23041f374f0af8f1304ee00cb73b576ee8790a1e7a20d7d458118444e8f2aea731d017a073094600f84f489be915383e624d47992c08df36d4191c0eb3c5eee6df3656c9da09c237cfaf1f1f814d1fd10887021087978364563f7c94bebeaf6cee65c814fd02522f4fb06c657ec6c97d715f38b0fb41a51db3be602bc1abcfd151ec7636bf0dd8a851d4828dccdfe7e843c3e97f98fab7fb2989fb2d2355c694130f7170f706ed6f877f424f2a77fa17f7590e42f06016df012d09c9e853109c38802e4df106aafad749035356c2730498a69666cdede0cf926c409b35f20a2d153b243bbd0e4790068706a7ea28edf4116898b29afbcfede774cd095938634d29e893d34d0fbec3b700d698667d7e1713b09991eb3d712bcda3c6d3481ab22c782b0277e9032e9ac1f7e4aec03531b0876bf6afc7a6ea56e64c9d226c67e43c7441e804bf085e3e2a2d18bb17f4c61459ab0bb014b688a4e51c8a251d5e6f752c24f4b6b2093a3aab45b09daf8be030582333749476fe3fa3f04c4921ca4287dc4c1e4efe00449ce70aa90179edab31d4eae669a825d85b42870f587d5e96d4eb768c1cbd03437ae77066183eb5349baf83792d9fc281e0a339ee92aaf95fd5519d083656035e4959579529414bc64b370c425ec7bcb9db411bd6ef5f7a058afd8eb1b7cb0916082466b944a0764a30b345571d1382267a2d4b096e4111ce90163aa1d76c05cbe589ca30fcae18ae17cfeb84d99392b3ceb4d99cdcaf9be2f25da90be5e90e1a2b508ba7489c0d1420251132a3d2ea12b0600f9efc5fdebad8c321f350d008d97c7ce79a6688235a54f01b7ed997ae9fe4e655bbfa33e3ea5d5f7ea6331f0a944d48edd79e7dd78c3056d12ae7ef662152000b53a7bf36c17f7de9430804096143552711da2aa6374ded45f48a8e363065d414f250eb562aca3b828f71200c3ac75e56296aea85decbf868576326e7a310af8c08aa3f3fdb7138e69c906505d8b5e35686c1a7496816222111bf9ac3301f731994744476ed0dbea0580b7e0e78452a0fe78899c45f00478c880c3e5eac7fd8b6b02d2afdef93fc2d8fe24f0ed06ceb05adcad9ba3e83c238dd70f2e5efc1877b42a9c9b440f3a257d84c1e0ebaaa4cd075c44aff98987ce8ab7017f8546694088ce0a18cad5473a5d998fa0eeae95d8e4de1cc2115d78a37c2db3a3c35692adddfecaf6ad474073eec4d580636fce532c0483ecd9b892a1c2950bb89592ee005858e819859a6eb0b3e659b041ab020c4ccd86234c8fcb9f16787ecd9ba56227a918d641f6f3157e26fd6be048f03842b26117740ade3fda3f21aa18ded5084f9a91bb87e1a8ce2743cdf1109e0ca0e101c5a505746aff4a6988f99416a1f7dcd9224751da901ae0bc8d71d0d5db79aa896bbc32fc695483ab176bf91fafa46379120eb774d8daf4b527cb50412e67434b5a2434307f000c9f79dc122218da05b2acffd20697c6961dcb62a05fc3464b54a491a3b26110d72d0f8da7dd5247a90e11525928941b79b29742906d7ccd827614c838728707a94a0fdcf2c904ed3d84fc904d0edd52e141c4f720132f8112c1bfdf764795769f50fe03266a09aa665aade480b914aa7e82534f303d2c8671e4fa0b9e8c7b718584e2c2cda046f7400ea0c93dba1949b14ebc374066344823fc4bce74d7bbdb1ca4b6f42470453ebf6fb3c5c3b2a68e6f53fe05500ed4e18d4c80176bd978bfeade08f43dd797aef7f83a938bca7734c40a1c67a015c4ffc15208b08b4d2bb65fb8171342cb99e79a31c04a2626808c3f3cb4d40004d004a64e7bad6190c4f89d3ced7d0371c6a8d3d238ffe2df3777ae9d5b1b404f27a78ee1a18a4b4c1f67a839ee6d436c4d223e84458d6e4cad89996801bea0bf3997faf0911d75410fc7c5812d4b8c9d46350535c7e306cc0e551a66a04d8056307b9048ab1626dca8393d365fddf886b7a5c67423403054f663d9c13778c0e4adc1d629290681e2f5bc6835b8ba80745b065568650f07b6c0a3508c86c4d085a08ed3f9f3f75331e90cd799a257e27c5f83a756f3c356d8b07259a445bc40802052621b3d8925807208a3c0972986f4ea51e756206a2642a775c6c61e3ec0565ca90a328cd5aaa794ffb0cb0172a49f18bc776c3009f73c49b69a09d652b0c149cb40bbe9a0dbb8edd2eb0755bfaf42eda4dc1a7265bb63536de5b7098170a04afa077ad5ffc21098362b61929324d5993169d7492a94efdaf3c4893d4ad046b56bb7ea632ba1470ffa9da584e34b3774222cd3ff19bbb1a1d86be734d9300bf33d62888129e3d21565c0d8affa1a1bec774b5ef48cfed21095392f9441b020673611749d446ee470fefebf931473394ccd1e83e17d89a60defc1306586007ba8664476e2971a0cb3b23a82beb71fffadd2fb83107829deeae9a07b3892e0ff117f3992c813286ba762ecccba8969d3233077d76877f01b3f8bd3e8a43860b5e6d76cb98a21262fc617a3c79e56a34aeaa6140564697bef3c08a9d1331c6026c1b02ad634e0df4c44e5768abf6d4f280632b814fd7c067aca7c1972a45250a3c1e11a2d27efb41a0875a9c559bd3b608f052dffea41ab1f4bd03be5bea070a703d8ac719ccbd7f0bf121898ba5954058c2709de1bb1935e1525e3ae2d602075d53fe614b838390107e3d10cbfd3b7c225005fc9d65274688ec7f88e12c2f0031e5c0e2ba250f9d4efbcbacebfeaf9472b88b3862392b6b6edcd2f5afd37206a606dacddd962bf62d60d45dcd99d7db82bda883eed9a6eabf3cc8f458bdc407265a2d891d253cb648a848cd8d841a536c19f5f14794068feb25994a0a595902cff2b6daa0a72ec1dadb927840d807e2b4172f5f1602006359474dd4569c7b048d6bd3feed66505f35d1bd5d93e2ce2e553a50158ba7c587eab361444d4c6c069dadf33f05a551f13b713f65a56be28a43b0638285763488dba942a7411f450b9a2da7c22bd24ef7f89a538937b3b70693835216cf8ba6f1e17a19a4ff8eb50180ad1d7141ab2417405950f2b4407fce2943113fd2e0b8ba24e27794f15c80049756342954b89b527eed5358e6909421851ef6c8224a3c3f282303d79ae70d0cbbcc9cc9a2fb6008dfa8d5df6de4d79f3ac7c3e2a05464bedb6f88a88770e40f30f76c89542aaf5554f694e640c0ad4bace556aebb1fe7cda63dfa9464b64107dd2f9342475cad4242250ad57846024ddc27600ded1b3f2f39c69be64b9d5f0cfe66acf766c920f25d583d2fd56e023211ca7f430b043c71536a16ad551cea0b0b80b0fc3b9dbfe3bbbeddf5ac396859c008c3ff54aa6e7ef5c8513eb5402e0dc987084f108f7175a0be0e4cdf6569618a332aba649cb58e4a8e7f832b506d0cf2a1516d3c94986e37b536430f2540a45010b38ac67feeca72ee2a8863b4ab0549562a19e6a9ca2f7a515162bf526b946c143a94fece9446b1d7ecc60545df0371177c63b20232057c3ec9941766eb2070a2535e54b7d6d2e4de4803e1819805fb15f534feed4b5166c7557481d4e3031170c6a575fd8e4213e44b1b1fc5e5044f7db51ac5455c5ddd001b810bd6def1f4042a62b142a8bfdc08a501febf700a273ba722f45685d417430e61530cca9257d178205f22fe92140e4cc0aa380708f9d31e8785a72e46276f6889df50d0151a8ba9f52f4132d3755b3e2af7187e0a96d79125a0460924f9b2106c55c2aaaa17d21fbea2e002bf0276ce060aa11502ec6fad43b6210b0932bb73ee2444aec13c605627b2d0fc1f0f57cfd767bd9303beef580980869e39969396861e7f6dcc6818ad0724c6d0b205ef4e0f592deb0e true -check_ring_signature 538e487233a1beb5907d94861ccc99b024217fdafefeebdb02189a47128fbe8e 67584e155717c4c058d64fb3a3bedae384a45ee9e9d60c76a5b4e1b1b0fa02b8 49 c947efe9590d83f52b874d4b3fa3b2fb75e590012dca859bd0888a3ffb57ea91 c3e7791af20876e23bab33f3faafd1fd0d013f711ffcd585ab68a814751895b3 4fb0d9a097ab9f436cdc1985997347ccbb14ebf7d2dbc69e2b7e104491bd63bd 99b4b93ff87446b605b85af8f089b492b00009f6a127b557df65b3e2105aadc7 27f80aa89688e70e4a21a255207a3004feb4a56eb50b4365bb66d0a60159bf39 6bd6abd9d399217da214fa29b681907233401719b912588c9d7873f163c8c26a d06618b975bb76c96f86bd8fe5da5314ec5661b1e1fd6c5f574527198fc0c662 f532d43d787408097aafaaa61424436f28ffaf2bdc6686d96e484a9df8b8046e 3bf6c5a9b7598b01aa035bcb84e3e5e695dbb1e4e081f976a4c154f5402d70d9 876e3f512eab03fcd17f91983c4eca30b57ab44cfb82f0af5e81741a5e0a9e17 fae9f896093d61f1cda7e5751a4cf3a90ea1d32f8ccf4ab2b62df841843a7202 5ea72819c58e051ae46a49e0715fc3e8c9fcafa20254d296e3e93bb90eeb6d6b c63267be18b62bc2ef27c086600ae23cdc040baae4721d2829d164620e1952aa c02c8012ccd872aa9cd4ccd8ec59c07cbab615e61a17c19d2ae5fa1db228a77c b82300ec3992334712dbc46c8ce5f5edfba98d30208785912d28908be5527446 98d18201c92a0244f5c8e52ff2fdf5a7b9aeecd69034701c2698eef590af92c9 249dfd4e8499e841449a9cf3ac478583b8da53e26b169fa577f006d389a495e3 20fc7c1ef13c55d3987444388756bc2cb2b9b70842874f129fd113fa68d24e59 6c4632d3a85cb1aad1dbaec333300ef30470dd6b239a13d3e6e9bb29ab6fad4f d7bd0d6b66bdb0773da62a208a013c62bdfbd37c5c6d0ebd4b6c8aa34429e44e 91fb976093118ca09603ddac3cc0f4dc61c6ef5cad4ad8a28d7d42fba1b6463c 34fe9dafce6c8cc52d5119560cf6c0eb1433cc7d74598ea2aec3ff20e46b2a89 b878138f014c1e8432c2ffc875e6893ba5d71d3406329857b883ce3e6ad38032 489d18ad128cd0bb4eb6991f840c8645bbfb2323e32e4b9bddbf7398a872d2ec b6d08a9f0b28bb41505191b7c2b4f7d72c64be5d1390c58e19a312ef372ecba2 3f8197dbbbd8038f81c2b4039cbecfd160ed7b15a11f6833b417d1164fa44c28 8fc7cee54d8c605ef00ff6edfab5fc9cca9e9a9b6623d08f13dc212edfc10cb2 1f2eef031750efcd1fc06fb24ebaa1e0016849e24863d8091227ae86c891b9e5 435f45eb1dd2e5d6d5c0ecda67089352966f3c848b7a3e75c2ccfceb218394bb b566e1aad254913d3ab7fb453cdf2c62380984bbcd73e7573667fb2323e268c4 ed0ba7266097314e971351c0c5fda0568a40ac6ee062cc80b966388a6342fefb 1f17164451dfb2f38b9f8529a8746a230dd7e01a6fec467b3037e9348e37816c 07afae0021aaa4e945d27bcd780a0579238773b83f2422b366c4189fccb38567 8f91435223a26bf3ea3c7df0b0911e90b0a5b9ead051624a43a4899d2ddbfbd2 a8193bcb993928b729e598e52309967708584362ed141c7de1d2cead68b011f6 4bc8126ebc392548c201e4a142b29165e7bef4fad542638d6df3e77184ae7a20 859dd96c1c24326164303555abfc022b9a259a3884b7a96a1fe823c695cbb7ca 897182d52bb43e04d6f1d781d20d13dd5dda9813461abb4a703fc20b20114266 805d79246ceb3db1d7bca74cf349a3d204fe38cc8f1d0f7548535d38e08331ce f2689adb2ca9d89d2ca2788f667b4501bf847e57d4fad83ea8aaff508de9c3e3 e20cdf31c19505ddad8326d882846d187994d2a103199e429dbe89c85e0147ca e12db6442b1d1f5b969348a55abc744abdb3cd0be402c05fc63dfa1bdcfef3ba 0c1ecc9eb0519c5b2b2f108ce23c42bdef7a3f4e9552210696d2ab3b38a4531d dc2c1193398f7f08626ac73e08eb39f5b00fa722c371f64977d91606c463fbde a8487a243191d4763df96e82b79b0b674ff2d99756af4b37c714a838022a56b8 01d95fd4850ba5ef134bc600707c384f04b3030b384877f89d1c4a1ccf233070 794fb8ff3a214c07fb6c7c7dd67cde36f2918e0f53c7363d7a13ca879a230576 d8e466e752c28ba80837d26979a1bebc6c6ef553b87fa20cad01a6aef89df947 01578bb6445d532adc6af33c9f88bb969c56644b0a0f9b13fdb0ed84d601ee0f 7bf6c5117c6c00a84c5b5deedb26c7c364b72cfb915b7c5cdf074c8c45bf9603751caf0de054d3e70fdd2c08b113e21aabcd947d8072e9c57cfb72ac9d83330cd61aae4dc5f4ee8455c1a89e804508157fd38200b5d2834af6274681d7b30a02b3b585e126e0dcc67b7c3e054d87eeb202eb92ce21e17d11db8db1f07e7d4b09eb081ada28b302dd6b89e2fe14907d1e607962176f14e4dfb24b4fc16846cd092647aebfabb19bead031f0a53598612c5de2fa9e0cce2aa0254913c06d35500a859edbc69f6c916030bf76e2865243bc0ff7749811de7973c4a2d8bfadfe3f0f12dd16c423c798be029fe917c9f22c822b4ecb47a1775fcfac962e3b866d130cf0d08fe4b0effda6972a31d93268ce24eb8806a3ef6329bfd45538696233f20dfc9a3578a87774ef6704423aa63ac83fe8d801f09a3888eca08d2cc63053cd0dd8b989abaa8c0a5c53b884e961817864f417088526e1c0639fef1d5a46f2b60f0a97f5e100921c3400c4438acf5a96d7bdd0c2c7268fa992e82967d0f5396b07bb91127ade6829611bb3d7741e2094de62dd74e2ae964b73241381c55b6de10b856e658ecb788ac28db28c099f707d77008dc361d9105ccc6d56bec3da6f09008744c860e0a76ef5bb5ec1f085e9d3b850310e2bbd6a9ae59b21e35980f6380bead3cd8fca66eea0fd4327313c10e8bce305838ed99afd8f4b5d191bc1480a0c34866082bf03556377960fcb52171ebab270f8273b76caa4678511c0a38dec07298f46d5959481368b55b7044ccee0548e09aaa3929fcee3d7db0b6c5a37130415aea6640a705ee0aedcfccb3e18e6de6d0a8ca05aab4a1e6de8469d956e4a0f5b43d257641cd88b8e6f0dcb02d8eabd02a2ba0b0e9118e70400f5b7a38d3801c2dbf2ec458f8956722152433f5c268efc58cedcfb9e64aa0f5fd6aef27d5309d9f5530d42eb7c0080ee316c7d851245636275d3f55d871ab147ee67e25d6f0cf4c9f138532369e66282c8f3dd9ce805f28214805e2ea02451e92f3e250c3206039ab65d5a59f368b2509472a67fa607fd0b29d35ed3e314741314746164c807b0a22d07843862a578d9c3f24a54da9b11e32b055df63e6dfa7bad648300580ae09c7f1fa4af9d0d7cf250e271b22b9dffad7a74201278d4ea622693e8c220075c9c40f4830d42b51594b2a4e381ac162d0b3f85033fddaa3d234e573b2f4e01896184b82f13d278586ae7747e02f484201e1359e75894c5c09add7239e8930d65ed4583ff43a404f365dd276440099a6c4f4dcf216b097fe30c6b2e3439ba022a1d36be3986688fbfdb06f18c588323efb265b57d33ae9f3c085c3eda40f00c1607dba9c9e5c0ed1a769e2c81b948e1dc395f569ddd81497876b26eb2a8fb0a55cf651b3d9b863fe673c211aea60791ad057fcb8238899c993e9d4f46c4010a38c09c346b034e588f089c35499305369b9bae08dea966c33df0b5e59a5f1702765d67b25a3c6f978f9ae5d7934bd8136cb709c3282b8316d54ff67b3be3520071fec86a907b0ecdf722e2f9c26552c5f31df1f5bbc97b6710149ccaf013e90b48327f44ec9902025b2a4826c444c158a86e549df8bcb7179b5c977cbd61d00244d974a3701f1ac6b147d5126bacf5faf02702dd747afe5a47a1781e1e91d10285a4e5a00e5061c0bb893f72f9ae0ecb4ca3f496d082e495c4c37e0430389f02356948956f777d68e7e5c050a34019bb2b0ea75f3196d5fd34434d2cb96be00511fb1640835186a7d1165fb4fbf5c8ea178bd64bdf88394e0395b9f62c76ec09ea8974a7427f7e21741571a693bc4909b54bc7ddab3969511d3215b7cc02d10d9859b9c0bcb5437f90dc2f8a07a4514603a047ca5a04376c565c7544bf99e200350fc25a550ffe4ea9bc3e3009b2645896e7c7bbdae62361a755949dfc0b810e546827246a181782929bc47937b0ecd52e0768dfdd42e16858bc1d861a472708744626cf8bcf6fb10a2ac99f736e53a7a0a394cdf855bbd5cda43716937b360ff8b239467fd171f25cb3f304b4b92afdafde031337874306b2fa533566de2c0e8105556782a129fbe003365f1144a69fdd3453917dc35e79900c65ab1e3639066cc565b04b9ed7417f695bee085212b40f3a531bdbc6483d241e84c488676e07689fdfc0b040e0c3015df67944e3dff1e11e066613d1890aa1dc04a78cb6260259bf797497b87d943cd36a4f4f316065e7858787d2d9e34811866edcdc8f6f029f41f8dc4132dff5a21ff4f4e7c7715d2e4df0294931e70f7f6abb9fbf9c640f23c1300550aba02d13a102f0fd9aab84bbbf0f7ebb9e00dd4c1c144a363ab40e26fb63a9ee457f5d073a7506dba237d44395880dffc1bc8a93f00527fcec2d082632c52ca159f6353ea533b2e168670d1d371078bb440a8ae8396f2653f2340b0043e4b2d7d030be55c19c95bf0a9b7e5f796998d1289498e7b86a6ecf1edf069ced561ffbb5356147eb72bf1d31b8b74191f469db9a8e86378c0ab738dc820207ac10578e3f919d05a8440fa133992258c2512ea26deaa519f2b72654330904d8332fdb9ce8d1a17bf0c431b41ea49a308394dc433a477b1a3f4e10f4d83b0aa6304ada778992798080615986c671e6de3f05a836ab6ce67cb1b8af03b5180383fee256ba73bbb5ec08e0f5f48df9d964a80ab22b9b0946c023902a9212190a4806331e0909641820a1f594582b43239ce18e3832ee28e46e9ab6d459445c0d146d9772fc77345ea2ed02da515b7addbdb8427f07a9d6ac379ea5d43eb9c1062f2dc447378f37e0faf8d8ec33f35f89b24e66b52ec15c2444e0017c8ae04c03aa3deed2c2d6a6caa8834cb1d58f29be5366fc88d0b98b6117b0ae1b182c490f329917b97d74e03b7e76ee8ed466a066ddf9bd5b92890c93e7378ca578cc940344caea1b682d69789b684feabdabdc9c666ae90ffe29266ca355771f5795ee0073e9b2f2a7983ceb631dab670525a53edd12325e1fe20da870ec06250c3a4f06ce04358ba653ed0367b2a149b166665e7f8a4b16b124add3f9c1b26d0389ed05bcfab50f069685ae40b876594496e197c48880a61f0dffe793b1d905679a9301b65ad5ce9add3abba03ea72b0c92c813ed546baaa0c35394df3829312ec7340fda681ab84536becff33c5ead6dd6b85cc10e7364fecf1e13559e3d2614629a04e1c59e98267bdeceea22dd8839d946bc3b0b0ca913dc388a38e8cf08afff110c3fba17ea642713c24feee5843e4a8fbb33d82bf5bd5d1983a9e3912b16c4df0b3f8d1ddc0276690b974a01e1c2cc86ef91d4a6fc8f6641ee9adaf873382d7d07a6d2a2085338f26a077eb41d36b96f1958c39aaac7a7abdedebf0b43d9544b06d1c0bcfc3ef000ca6329386261851f321933e3837c494d8e64f29ac4b1568a037d28c2ce492a94b20c6c3557a6fd18bd17d083b5960559335af60f43b784e90eb5d5a78010863ced27c99d6728b82ab7a708688f0348823ecc0fcc0b9806db0d474acefe352f2d91e7aa4ec765c3a47859daaef662291a80825b3ecd1c8ba30e4b52f7a244f20e3a95c281fd05b1a4319f8f587b5bf439266d4ee5c30e6b47082cedfddf445baa2539e0cb0979f6fe2cc89970f1e0afe31b8af38f7e411f5606f31fae7581aef403afdc11563bf40c2d365e663277bb73266c1b9e7175df180dbcebb6c6a052096dfcadf5e1bde11451fbaeb0aa6d169ecffac129faaa196f028f07c427568005f9a4c035a3f16f1d9659e437204e0dfdf78823650382fe730e92fd9603f2db5a2610d370092585eb93e321df2412214a59884d822753f2a1037677ae101a254ed3b4f4ae91ab0434ac5b93e22de9272389520b5bd8df8e530b990df6f2784d8d8d730b81ba00866809a4dac7b333bac8563c52716fb264a20f90292f68f3b2b6a8e012bd50241d583728808bffc898613211f0c801dddcbb09df2e246b5824a63401c9dce08adc1a77504471b4ce764d63124bd8d85e90300b1d19811bb0ffd2ed7a8ca5813d25354499ce47f570fb30a7a9cd5e1f46c9e7003cc0c6cf9d74dd2d6aefd1e0dea94885b3975ebc69e62d312ea2af9e537ef906106eb4a4c2222d4aed39a77c94ebd74710f0b7dec2331cde98ee8f231443d60767ba021b1034e03e8e00318a69eac3837a01717c6174c01386e596ed533ba70a6b6b7c12f0f577dac6fce657675a05288d80b24ca944afab374c096a6f2335006fa7458e83cba6a82cf53cc48d163269b2bca21d72baec058016b425d248150c75e32f2b57694bdb694f429fb46dfe4ae06d1d1051fdc7136ec3e976b30f080899885f1e9938a0b4db5dcbfab2e114da50b1c5404138d16876d2c0d511bba80c83434124699af4014e3701bb80f364fc539ec738416a9d46ca94b22ddf502b0a true -check_ring_signature 78aa10dffd27559c5513ac326486fd087c0954d5834e98a7cbc0ef9c297b10d0 db9cd7dbfdc3ac5b01f3fb9453075bbd939937271b935aca5bf819921f37f59d 1 c8c19e8e6b7474303f62f98de75236b526c41edf14219c7140c05906ea8ebef7 dace027796131c489a798aef55cb4e29f8f578af8b2eb25108c2b08b9ac8ec0fe56b11f7dfaa3d5a06e74448cbd9bf2389538b54692715c9c310be300560640b true -check_ring_signature a7a42886e057fb35a0280e940c9fda591276110f6d4a8bfd8d7c1b90e55c7793 47f076ab98ec60ce09c74adcfeff381c2b102e630bf0673c7c090735787b6236 70 3a56729a666a775be7cdb96505617affa1f540e49359f2182983a892501c3f5d c50c06ac3410c0bb90ab4a5f04942b38df1a9118f760d7cac98d45262f62824b f02a267d3953294204c1272ffb9cce0513d99e51a2e86aad2d24dfcb05f99810 af4a5e7c970e2c9b048d676d2b3dcb87e189804bb469e5654dee3dd4972475f7 63b2e30270bdc413f6b902fc7c252ea1a6c5da439324873ac5e32637047400d0 3f5bcbfd00f515b3b6527f4fd7621301cbf46c7739a5abdb7d841dc1f4ba3922 791939e1793ae29895455e9fcfe79ad5804bd4df8c9cecd48882e8bf5fe953da fa9e398b82094581e44eaeffd21aca89ffc837c24ca7d243b621026123ea5414 0e2ceee557ef93598a10016798e08d3d3f5c789251af0bc545d4f2af21544833 0414e4bf8e1a803dfb840e9736d0ae5631d3f2b4701af966a8e4080b9ef8e83a dbd1495c1071abb4872feb27529e92a15c9e0781bad43eb36293d094835a4544 34dc5474f60231541641a637b609ddfb634768cc62518428e3bace654b3d8e2d 604eb346b48a4040f79f6c0956199adf5018bd5a199d5c1dd5aec04025dd5442 8fbed33e6655e523bed4cb6df21de46f64655a823ad0a631a71d74c898e1c5f1 8ded35b96b654920ef5cc547f017fa4bea626d184660e57982fdbf2b1e4a33c9 9404a3d2a4019f91a8a6e296ea336ea7506b27f4fd98c9ca26d0e54d7b6a4357 7c8b7fcc565c126651f091fa9638b66974e844ce22ff7452e560922f8efbff2a e042041bc5a3861d96cf5e013a3a78e1456423659403edea6ba91d89e61629c3 700bccf8232e30015e021409d34651330b81b6ae8d7f3dd11de55babb7ba9349 df00c534616d0dc7fa5179c90a58f1de4be8acf5884e85f4c016a9dc04afe5d0 824beb56a3197995395e9ce7b75f13639ce93f117d0bff4312e94ce3afa70356 4183f5a64e0f7e2b4152a8be58fd44a2c1b0031eae6b60a8e62160c53eea7fc0 7aa12862cde23e101fc0f02e3958b0fa0e4386844ab81e16d67338353719e292 0b09c47f887deb80fd259131bedf3d082db58cb636d2cf00f3d3ad97cd4c1d3d 917064db46d5be487d963d5a75494381731948f0fe8e503094477829ecf5e12c 8eae073ef95e8a7e98d540fa3f83e0c532acb980379ae1cfab2f6af73bc2a4c1 914ccc7537646fe933644506adbe2fb2ef6a162a3af19507abdf93349ded9b32 4fec6270b74cb1ef3d2c8225398273996adc5fe5332a223fb4808fdcd490d2fc 2b7925036156b0f6db8aecd600efc13b1a0cd7ac93a86a0ee3195069629c2f6a 8a8f98a15cbdf680fd878125a32943250f9a55214bbf0e805e488d8e73d927c1 1481405c76cb275afa128f3bc5f63cb6dbf652b2905d0c68f5f37932abe9116c 91dedd4e12a396618b97aa422a78e90f5247a45bfb1bbb6bcc408a50bc4d6b65 c7fcb13458e9b9fa4bbd4b91cc4f32b7bfe846ce2c8e4b19f081bc6cbab469cc e8c30f4399c2a0b8474fe033f545b4b146a33c4c6a6f8798772a69b7171629af 89ef2297f4ce3b4d797a663f86159e0c0b902894abb9381337702a41c7a6e84c 86bbef281692096c8d0b7046fe225299d2ad8f2fdf9db712db3e295556bdb8a0 a0d5d349d0b53ec01e3201d9f35f6bd1d73ae5eeb18c241521f5dfeaf537c048 6df2a8826847aa70ebd4799b45ce6e567476105c02e2a7da910bb4295af44059 97ecf7d836918367f23dee7c5a9e30a8c95ddbf2d644d58ae7d5d6e5fa86d631 ddcab229a85766fb7edeac92e9e5d782856c58fb5af1cd2215918f58b6e47868 42d6a302b5d9755b5513b0ff8a796f1eec20d0d6833fe9ebff9578fb9e238ca4 3bc754eb48b5081393fcabfa7397f9eb8279548ff0f5ef80f23cecb8bd58d2f3 40d81f7e78f0f5dbf89f7fd23a9168f538c2812e3c12458159acb09761b7f2f0 a02ad27fd921c8cac9073a0d1bfbaa360bdb92ca261bce2fb5ba7169891fb2fc 00574cc318b43636f98d0f92cfedac2094bd0937a58bde54e9ab50b0c1234a7e 08c266f0ad2eabdc473bc2eab6396b445afe2662acbce5360aa61ea29a4319cf a1ef9dd10e1e6f27dd604592efd7975dd5879b85d4fedc08e7e6e2552644fe7c de48eda1bf4046879af1feecec61b65f3576dcdf77f3f4b9886dfd96b5445932 92b71e243dfe7a94fe35e2cb469e9c342491f3720eb6c2d478853cd23df78c1e d61ec4833c4d54e70df03fc381f4d865c92ca9ba447c94b3a4c7d195354084aa f909d68efe69830428bf6cf2c0388f05eee12acc91d6fedf0ef4329c3223766b 37f924b1a2ebbdcc2da75210885af2bee95276d733df48db06fac72e7a520469 8d7258d0de50d56bed1f373ec324b67748ed343af896fec99171b796754a3e12 4f595ffc95040b26b485c66737d1d0c639098531af2e29a353fe870b968bd889 ea005c4b06da63a4f338560286f92255aa9c7e812d5ac65a33ad4da08d8d4d3a b1a9a863360bc5dd7f40a3e045e4f9ea722657ad256ced4ce40431645c73e015 22f0e1b715f50e684e315476d6818c3159678f65e8b52c03fb977cce9023cb2d 0dbad0419adfc3fd9b77aa1de7880166f12a89ac20d2474eff970fcd02e5be32 24f9f1225f6eb2182b21b6333d027204a5eff891d122a5f30f379fd37d71a49a 8c753148daa7e78d50ac4fa7b1e7c9451f96ae9755b4906f85bee54db3c22e91 3613cf8a3b4d35f54e268938b984de243402a1fcc05218d340a58664bb897610 c3acd0f406d369ed37af96f5de68a03acf6ad6f74c223113ef3c626e77677171 d38c761ea5a58c73660b379248cd9fae3e98f699f197c58ff1e7b60c1cce1915 47dca9bc971631305734bb71cfe2d1d0cbd9c64c2a253095e83e7b150c964c73 f6110573e1ee9f70822d13b4acce578a1c7911d3e3f21e3f18c1fd64d94a0161 90378795059988f36afc2a53a8dda71ef7c35f4833718f6d361d538cdc9054be 2c4c9108f0e8cc1e409e80c9922ea16dcbba8a49830ae8600288278222e34fa1 4df72819b7d10cb4fa12e03c7efd5b0ee410b08b601423166b412fb2210fdcff 9469b3453df4c6e55bb3c3336b764856e9842e0aa5a22d850e2ec0bde73da247 7e26fbdf019c5e00f3c188cf319da73c7323d6a321ac052a3b153c043aa258b3 a08af577affe1abeb3fcc54493ce0dca7bcd36433ed0b368134d4ffc4156e00ab87465956397d5bf358effd4e810e25c2c04aa8f347df31ae670fb4b398c9b0db596592bb7ccfc6127c3468157dbcd96ce0f9614199f3ec2d008a12a8eedf20463d59fe4821ae26160d4d1bed106f29e73cf6309edf258d6874180eb9cf806082645a9b5070ef4dfa5170ed9dafa3791af0d668a26707201778ee848f7c4a905a11c1cc0961fe2a0fb5e6092c80f83bca9802aa8b701b5312adab5708ad16e038e9271d2cafbc029842b6307db0d2d1068741fdb04746e7c40b2d94cbe45140bdae8cfa3142056b64fa969c69c0dd4b6364fb2293a5fdc8fa5138780990ae6228c093ffccd593eaa04fb9d165069a237505aae75a3502d9f07a6669676cbcd0a7d97e267d78f8872415f86c42030e21805cb7c9f5364f50ae8ae1cf6d98bf20e6b4a10a46f15436644936663a3a039da2d59da6a33fba3b954396ef679f1bf0a637287346e75b2cf7397a29cefbeee09393e24731ff09ef73789d6bede6819050039fc1374b992d48a6476d152fa5ed739ef154b15faa810c26f686d903f640b9f918f0f866862189470de9e073074c5e9b1bc7c24af4bcb60f81157f12d460152544d8167430ab1088adb49a1c06767d1faac6eb0f6fdd6fd329fa2ac918d057ec8196e18db5cbe44fd57340aba44cc787e2ef590bdba45f9189400b8c61b0cbcc9aa691be7d4a2634cb3dc0de3b95e1f4e7fb8d448fba0ab75720b8b92200e2e9c7b2a29d421465ca151210c9413ab807e438158e36bf22c58b3a2e76f510be79ca25d8adf95ca30b467519c448ae81a787cfe57ba405423a6605c43f12c0304de4ed5805b71e9de664c9feb25c954b9471bdc57df2cb5b8a1444ef89b1002f1ea7701856c8168d64f78ed8ce9e1dbed29c6a8f7c6e8dd07b8d2e620d3b80fcb1fff8a2fd6d8574c2a3d5f30166496122594e2accafcea6d4bc7d40908300252fc82a076e6d0f5d4461b25c22bdbb72c2a4a5e208f00658b0360eef344450efffe35bd56b287ca244dbfd9fd9ca49fc56778369a8c5a39d5b9c6e3c48c9e03fe2031bfd749528a2ad6a53f4b691cb7c680b5536d40bee95915d8ed6c4ae909069ac45b53fb2fb17ac7993850a14339e336a2b6c6ea57096bbbcc22facfca06dece8237a80f845b8f52cd1e81eb60676c20ae026a00395042cff201f00555096cf842a78bc118f029615f1b166d96d89a0685370ebc319a49449f3ed055a604912bbe58256de0689e0a1ca73e7f80872a0fc3f3bbf3f55654d127584acc640e2b46a913d2bc7a487f509dbfd3dd96703b6000e8d8f4345065cb26329aa85207a8c64261c5bae293ff15eea86ea2140bf6e35b8b0f4a2bd62922330218a628095b0fdcc04e4d147bf6cd7ec335fab3705116ee160063ca61e593b2f45cdb6c07168ab706759c6736ad3f0bb869b33211f15b2ebdf0cce8e76da964a8d9935801aad8cc5bc7b5a6d1128a9a4e70042a7a2cb7d8838f13dec3951c0e1c7331680f2fec0b901f95354b29a75dcc04719de5364c6100809a4ca170e5357e70276408f2188d36103e7b162fff80876f0e4b9dff599957366d37f89cfdda4b3851e90e7d9a0b0fa2101ce1350ecbeba2fdcabe38d17e5f86fe96f9b85a86833c298902df031c7cfcf8100288307dc34e13872fd571fd282a8a25541175ea089073380e9426fcb037cd6ab8df99153215ecf20dc79faae8978c16dbb78fff759ad0c20c31a4b3a67050fede99e3ef4d1d82056c54e80465420f0e7c8d7fb26fa4480c0a97d0f68804be7eafb7955ffaef8d8a166061c6223a7fabe33424f5104db4a804e5779717ef1b63ab95ca66fc89600e826630a9518578a327c07fd9df49a0f4aa336e35800235c18ca754207b2828b929f9ab18e35fbc6c578e98af0e67fbf10239d0a96ae5e5c37d87be63d1b4f6ed56c9e7d8bc598402f4a66cd1f1ea55d4088254490480d7faed57c2404a4efae9f10748a8935ee9ded4c7826ae6ba2eba05253456ee6262a75601509382be5d98c9228ce08ddc7a9d1f88531e016b4a3f0352eaa0f73cd39666da305fb2b7755f7ceb91206d36974f8e87bbbef747fc80067be913f220c30385de17eaceaa20b8adc40851e16c2f7c4965fe3788bf8e940d478b784904ffeb490e2620c2fbaf8f9dec3d6644cb615d8b778bb7768f136f09e05af5d0c197a849aca96be07a0944a25a5a0d577b16b29554212fa1a4d7340d0b1758677f2c459e0714c8008f7fe691073dedc23871c5974398c7f98cdeac0511142392d4b8383aaf87e1053195bf1889a12ef7b65c65e8d8c3e0b2b7351a0fdbd580d0cba9ae90c6a98921e0a59761f5343bed0db999e5da3bae7df8fe910654fd47116af6eef0c2025a5af5241e542c0313b09b369b3d3fe5313b0056dd0c5ceb5bb145bc2d16592bb254f83be845163121516736ebc2787915c67d574304ef8ba338342b2cb7b6e40f1a83ba115a5d6ef20a057d3b5c545f85eaf9045d061d67836532e232be2b60340ba1545264542df0f37cec83c2e80b4eba6d025d08ec73fbfbe40e9caa9556cfa10f5976a5a4911e61bd4be0e43e58ea0a4510a30e119cc65a91c166f57ebec2aee163ac0fe4c22a51e8bd7d474aa9a776e8e26501b701e57a259d0720d98d9122895572ffa7fb8541187bd25be5be4aaadd734a0b735e17ec9ee95dfdd85fbd0a002eae97c29795e4ae20be827d23165f79a1bc03d958afd94abba470fa29e08d416b84e6737bb20f2c64907691d336ef9a3f8f0e8e13955d376326851e1b5153ac4baab828b080ca6237d05d8981b26f0f161f0ec964ad0457cd4b7138db267a199ba232e787dc100a4b0b8d395d75eb25813409713cb5af126874e18839fa0976a30907f3b83268148f10398bd6e4c79ab79f09043394c758211201087b2c43288487d8e1dd11cb235188c17162eead8896370dc5744037d8eca3b7a79dc439092d1ca7e15715cd2890d172c27c7ccade766a07e2254664b529ace22a7802184c3737dd4fb1f7712a28a2425d00377fc4aa4e0589ebc975c8acc3931fe8d0c272f59b579d6a5bf38dbae34ce859e1abf5165b02b8d0cd5c3d5f072ac65c7fece37c97df362a038038d296dc30180eea2c32850c8a39436f6234ce6b8cd29803e4703f9104dbf573e9259819d5063b7dcc7e120ef36390f58a4977d45cdee180760a86a16bdb91019ba8bf13d03ff2e7224f26031cbbbc0ba65b5c00ac2b7bd13e3fedd6f0b4163039ed27ace49324bfbc6c210c16b5f655386f49d1a560e9ed5313a54043aeee5958d28807f15c56d57d4d16006b2d08a619291302c76e979831f445f6bcd38f0bb90d99a11c9f536df328da0a11734505ba3293d1eeea53c7d3c8bfd12005f25d071dc3f05a15bf6aec8fb90200d4745e57a402b6745b416c0023d0ec1576654e478bf2757ef74fef86da4c09e62fc841b65fdedd2a6a7bac1b0d8fa014d0830d494852a53da968ca2710700d51b4aa201390c58de645a5696fc748dfd88e46b2b38711792e0ec7c894d35e0d6f2188ce90afb394a6ca4aadad81cd4462d4dd65516a16be0e3ebcb2c8285b0c82d76ae689bff48cd06dedbef27debf1ba8f8cf70372f57ff48fced83bf2e2021fafcd3201f8785700d61d3484d787ff6106a8a84bacba8c79def830ab52dd07c07cc87e9aecc25dcc5bd9ab507dee20101d236ea87b0a5ab568c99f755c0404f74b49547631368f7cd928d280890c249afc938491f9a0ab1b6e1a4ca83bd905811abff5443cd9b5121bad1a65a0d096565b14d2a32c12458e7d9306f0bab30ec8d49d50e838063262c78c58523ce74e6edd311992b853c935749ea178c95600d2a9aabb85231d3b79835410933094f726a87a1e953330f801047eae38877f0be9eec0f0c2b12aca1de4c473dfb45845e2a23602a4f8a18049692bd34e46420025675956accdea67efc5426c5f5e55618d9b88d6ca1aa34648ff8162a33f480ce9b8d39923b21a2224b7e2e1ed5b11ec1e250e19c2680cd2bdc57a91093a8807ff8c2068fc45f6e2bd5434175a888b630eeafe6caa7cf09ea6f5889bd78c640289b532d2427c63cf9ecb22e7dec22a84f59127f005d3d9592a00d76ec9a7330c8854e32cb5c4ff30458d60904c8876d9bc753236239a5ec8b33084f36dac4106a589171bbaccfcb6996ff7cf6973a3f5d4379cfdfdbbe5668abbe92401008b09407b6a0cfa38d6883a47ed534824a7cb9368da2e6c3bef1adf1ec3d72d726902faa0c03fe2d3967bdb36ccedf3d542f633c37863d0d7677890ff1c827153e1018628a88e88df9c69dd4b14a33c760e9779c3b4c1d6be70490155bea9c067bd0987a896126a64c46feb272987e59068d9f17589477b030e761beb82ab74dfc10ec993a78617b4bcc452dd14090ab7cd24928277c552c2cdc11b7744eaa9ff6d005793c2031cc7c43952e43b134e58908de8cef242107cc459e66ff145dfce0a093abcd25ed23e1b522a06996fa60aad7b389a81b87435a8c0fa1bb0fd37a5cf03b7f41f9eef1ca021001263e4385058c27a3890cdcb5fd583867327756750c50fa88af90f7133b9baf02720d91bee4189dd3783f543e6f215cdd3d7df040135065004d89b84ca00aa9c172bf25524f9dd23cad735d3771d701244e67203637f007273a542613f5942fefa6c4d8eabe0716cac0f67a00b96772e2e29d5bbab2208a9ec952f1edc6a0f66357a81074cab36c707d97549aef683e92a6de4e285b70269be84f4f6fe8d00a5952ce643575b5b49ddd909949e39850c74d371494e5d0bc30b9f75a0950a3bffd44cfdf962d367cf619e90dd6109d93755c5a042d5650ade34e8e3001b6b4ddc159cb375b1951a5a682a0185ea6a80b302ee2bf8998c0ee54c09224ac6d82297a45ade90950a562f3d9b643938d4b2b0eb200505a4f504ed9757c3e233f044adf807ccec509daf3b29a96cd39cbc766eea8ee69c83cc032f8a788f9e763edd4c5cc9bc3e53fb4370337b23991a26705ae67ff7ed26ed003b6dbaffeecf03bd5429d02b1cd01a28c62233fa70f1ddfebccf9c68eb2dee036e0076e00fc353a1bb7b43fad3c0dae325f501c3249fc66f43a8383301e5e006dd5adc92cf3063b6505ed485a1f4e5cfe97732cf2b3ab41e9f52a34cc12a9e041502f052d0d1c3a8ac90578554dfd122c0c5b5c5e901c971a3e63c12764b48071be1d2558f6c2d7bba4038b162689618a957049a7a3f9caaf53fc0dcdaec3a0633ddb51945aa81a9c4ca9044350b5a1e656cfaf6bcd7dd0e96f5b78e8bde1b0360541c20973154bfcf63f12a2c3aaae8b24e084c2e3387f9e67c1ce9bb6f920439d3c9a95dfec0299ee33eb67fa875118ac9abf1b6d581a6682230766ee4a100057729666961bdd13314506e065ef5b2dd0a1e8b55a4ad91e3f117e2c82d510b8c6d9b428c246808515c67985c8cb53b165e6963e748bfc79167cfac74b87302824211338e4b975b38edac9d88d22079350390c8683dd9bdf62c3445a8919f0f9b48b45ce2ebd318708c211160d63139802d222fca264f7b658828d183b0000d083dc5b623ec3b50582fed82642d388a69ff3918ff21ade89443f1b633ea1d016bc650a6be3f2a080461173e77594e2259788785eec3fe0e60f1a6a2af27700dd28f1346251b70b8ce8c2a3f5d9c54e592b88e761b5da6e72c59518635dd5c02877740e673f3ae3f31d6c8af3d0e4762eccf6f62622f7c9a78bc72f0519d3b0a0bc69382a6a7ec0f2c71fbbc075b42f00907bba0d99232b396b40dfb59b74c01b243d55015c1a3b3d4fd4237d33791d1b1dedf4b696ad4cd5eac7209ca7c560c8404f96baca9c972c622703eb53dbc124c6344644c772918ccc8cda1e5511b0490994f70a8551d69204a6f2372136c15bfd6ecbc9da87cda7c51b726595d630d72e7235183e166074be2f28c7e4567516d2f9a0ac34a0c8984866b52e8a84d0225dac83442a6dbaef2c7747c914cc6734173463ecd14053eaf8d2e93c05c69065c0a92184b89ea2dbcd03149770b3327e277971fe13a4a3facf8f4d0a2436b0c63e03a5fb5f379a89dd19dae22da8f8a1ad946457da6979c03d4b8954af66c026734828289289e9821c9e832de158a8f9aaff461fe8a711432af380998d2d60c45be6a60c86e48f83252f6526936ef1c8bf05fb9defd39747730652e9e7bd408d3fef0bcfa47131c25cf6ffa2a4f420ac1e3c1a3e6b61f9c04f0fb55ebcfee031566b48ff22da46892054ae8e1e54e2cbb3581ddcaa4e9764a8af86ee367a602 false -check_ring_signature eaf045b38f9d6bb0886d3ea35719da452755fe7a3a6d11fcf03d44f1f7517816 ab2dbdf977f499a0c1f78b2e54ed80e4514f182e1dda603b6f05cad089e60d05 6 e828f055cdc2b65dfc74620837aa09d84904d4c93056602119721a87592b0d54 758048bf309dff44b77ff576d8689798dc185be24d3aacd0b941631c12c39d7d 5c59df7aa734208be1360df167f64f904f960183ab76d1752de44057e7578076 05785c57dd5c94d7432942b3a77bf5f064d38867ee0590825f8b31b2d48c9ced c0ec67c289067780bd79e11b976ae48af1377c1e9739f3b247aa716acf3ea3ef aa8244a3d9ddb8b067fa79db50a7a4a9e3a34c75efe4db68c7c185a6fa49bc04 8f50f68767f994efc860bacf484e0b700c5c3abf37b8fcdb321def5ad29ff10eb22d8a468e62e896ddf7a85f9ada04075ccf53c9827975914beb217a504dd000fd1ef73671620a276dcb940c976fc002a1aed3462d9d518fd0d9f8c2d96dd30c2db61c82eb9cfdedcee458341d0baa8678c16ef0eec66cdd120722bd6c7ec40ffbef51d4105126bd2d15dde560d04eb64d6218ad1a0f208d977e32ef0c470107481fe723e711b03a9758aea4c718702feae508fe8c115bd5a09418162f35d60460a8cd8fecbd97f96983daadfc62b47abd7ac7d48326c57510f874cbf9874f0a717508c56c1d5269d598579845b041c4dd95e6a799671d429f35d5e551b57b08bc64669472242369e05254e4cd37f09a51f2c8f28396f0766872213305a804029463e768d758886b81befa6c2edecbc9c14026d10dafc748708097c3758d0706cd4ea7718a2c8ea53bf78eb7212b10f024c70b83b4403b14b5d01e489397a70600852faf9afe97d8b3ee520dcd641679b21f67a68729af13408fef07dbf55a01 false -check_ring_signature ddde0086b92184e8371d5fd3d748241257fea0cc6b7950e50dd3a92bc52cba22 ff24226047a92cd42a7bda63967a35ef537f301d90704a5ef59dc5305dd6d0fd 1 51916f6c8ead3ebdd015084b414a5507e6eaa70240e2fc84e1ddf4d575e36ef0 c8438d6ff8a5c89f8fe087ee25747e30b08f1b187bfcfe78e20e58a120dcc60d1da2ea2aa6bceaf3fce251114a9c06ed03efb4a63f3dffe6e53ba2014da4140b false -check_ring_signature 9a92846127a01551d4adffddeb12838a8f441fa1115cca876f07425c4f7c22c5 05576dffe75af10a99ee77433b6cd6c2da63f99acbda733ca4fb5f767f50df5b 7 1b1115a03fc245e39481bc6a9b518c2ef9708f4072cf3bcd0648ff56d6d95262 36627a207b61819ca2a3492b023c3fd73586515f7438331c303ba1e32f496439 a0d579d4ed9545429a9b46d7cae6b0aef8d212b1b601940cc282694c08784eca c632ef0a37784fc81d7fd79093a91d53147721bfaf1019179ffdad26962025da 7da788d026421c5b2bd97d86addac3e020f1e5506c546571d3e4335c3485f234 24a06f46ce6946d56a4800ae4d54b39aacdbebe15db4624ca04560dfb1d88005 ad46a160092f8baa97b97992123ee3249e3d15d635afe98981dbc5d03053326a f26692d0f70bd9c4d352e00ccf827809a1f36de841c07dcc2e8abe2c9d2bb80a0d89817a5d3646d7708332133a028e3577bb6487145aed62460129496175a70f20e735fdde60ba617733cc71d1c6555348c4d10f221e17601f6a97b56b32fa051291115f5fa523bf70431c58f31e1e588d4d73b55b5273257fee201913119501263e3c100378581aadfeeeee938887e6807337dad2a53b90baa3aac5657d04091b2546e1a309cb4cb7f3ee80115710b2369b68fddb9b53f7bec5c893d12b17ec4ff8ec7327fbe5d3cf0e9b663ee00fc20f702b977abc5d5ae716f54f0dbcaa5415b151a62ab9334c4c560b7b4c44c603cdd218493e086c70d16a4b6ad525690bf1cabbfcbf24d6438db686fb84c58f8f6a98e142b4b6f7ba70cc861de4c61009284f7e734f03d769effac2adc6469e268922a9dd2529055341854002711810098c0bebfc19bc7456ddb105fbd68e6e7e31ebb0cc4acaf485f76dd3f2692b3f24fb6cb6d67b3f79d9e88925423ee0a2f87d63c4729d6430bee9e0b959f732f5032feb0ec43ff7f1ca1f2713a94213453ecf490ca278f8002c95a2039d72c57108142ea50f27944fb218d707e1198b739567245da0b8978f0f588030d5895c950f false -check_ring_signature 9275ebaa65ef31590533eea0f478ed810de9c87de8eebefd438658adc00fb595 9a4eb79871ebcb294be6b6cdd0ecf349fee4d4e40d78ed7e660d28af8e6cc0f3 1 2bd93483e22b61dd2dd869c437ed251b61724780978bd49717e6fd6d0cda3bb1 73d7ee0f122d1aa267b87c83f04bcc77ef9eba14148441441c661160448cb3047bb996f4232f8e616b8d6922a975eef8974fc53b99ad46c66a8f0ccf402afbea false -check_ring_signature c3c2cd8cf9b5b80c3cd81d5b8d69ada01d28179afab73ab7d34fd00895c9e784 3155ac4b9e983549c26bcb3eac57e2c6bb4efbad11ba5dd201d8a75bdacbb468 6 49cd023bf3621cc0b5308156b9d8dfbc34279400c4453c516959265ee8a7d4ff f8373fb219423f92e07a65731064ceaf6cc0f122350bf6afe889b3098b881210 f52c80df12523512b3c20320c994e26ad877bc6f9df93911af17d0efa9b11ca8 7d4a7fdc4c1cd9548aad3293f636dfd2023365b2b8ecbf81fd82773d58488720 409ccb7a40114feb9bb23a875dba13516000646b48715a4bf1415e6b72e8112a 6d286ede8fd890249e14d186428154699c74618601ad3c9992b25f2efc9e3ea6 19d1a9548d6b503736aa10b34030ae9793541294daa66cd95759416974a50302fbe59eb274b3b0707fe9989a6ab1d19b2a9aa6ad4ac4b5690a8a426c62ba3d0eb06f308b2a19f6dcd8989b2e4b89a011b05b4694316e59ada3ad8e31f5bd370bec95b53f05b548b1f3f17a9ee4068494b3062b3734706a26f7b20dd63950a60d7ae2162999ff021b02d912c307e9cc536db1cc5a8ec44cda1e10e2b8b62e530166ae09960b2361b00297bb1deabaef6e437e0ed93e405d99c22b908aa804b30418173483647b4242c4bb9bd83ebb588c1a7a933ac799ca9eebd44d5d8a653d091c898e2dd2a7fa5831bdf92a81a884e020ea2b611dce4dbecd4a4bf145f2f70bbd54a13bcb0bdb4fc253a4087b2e632e4dd85b4fb741ddee6a187ae345c4660f15bb1629653a6fa526078177d7c73934494d5d08de39c4e53b1484297d1f640697e7ca5ae25fdd4719364e22f2f2a41aab1595a4ef84cc420717b3cae24ef90850283565d3f756e8a2e6ab1e918cbaae033bd318efb88dd83a64f06688d8fd09 false -check_ring_signature a510b33710d0009d34757d3b4e0103c125ebe9c5095eab8dda237fdc714c71d7 5361d31712cbae14b76cbf86bce296f76df282ac682b2dd7b3f697a1608feb2a 1 37b944ad2d010ed72fd4381c3f7bd3b2db61302500a8ab39b964ed9234a18bba 082ce03cfa6a5a373f0ac1adea870396482c3b76e3321714b5a17bdc00d9777270cbfb4b029c6f8e9723c6dc9085c77bce99a50efdea320939ec24b88f5e8106 false -check_ring_signature 3aa0655533545c823231ad9160094583a8e71e1f5a518edb828f0a9aecdac834 01cebb071eb7c2d65bb1d32d355404cd12cbdeb5d0c8eb2624dee06f126770d8 68 98a7eb8e7c12611a61419bac6dfd0193a645bccfc7267e10d1338c9501bc7292 d86da58b7aede38e7013baed6b1d050826029ad615af194d5eed8647c8f236a0 1111e5648fc53d286030defcd8ff6a60089a60a6117c82e6662a0c7eea7359cc fce8244148ea06c8034b1f77569306d93bf2353f9acabe0f0b347aac63463a6e b038773d36b42d60af9e485fc3dc5e1636334be12be8b3c60434c3c38501e2f8 f81bf266058df0018d15768369fe07f02d01daa8dc34da69ece1c0a58e4d376f b50d966258849a8cd660fb7557cd1bb9108337094c4e43c59a33ec86ea1dd9a7 b2f43374df12591a6cfdfdc487bafae6fa155ba10616ff6a1d2ca57cc44b4ee6 81de4dff455dccb71331cdc4c23329cf6b05cfbff64cf376f66a15276d827a1c fa71c954cdd2a60a6051c17922a7a539712b481c7a667365351f35a68d5e47fc 4ff474e5ff2bd71a5fbff84962ad5a34f3f3842946b5bd4dd999bc584232ea1b f1b52809df680f123d54b65b4db4af2ac33d31c5f8a8d8ad90a1001a4a81e3cb 6160df97c00f60d49743056ba73e8f887463f2f99ea80253a3788bdae5efcaa1 3087dea24f82fc1afd0fbc5c3aa617151946abddb62aeddaa1f26a9cfa4b00a4 96b482d64747d5b35137e8c9d95c324b5aa9f20c424affbc9ea8a60e8b0ec9f4 e15ea974956aa7d81d2913db3509cd33d460698a6a63832be79be2112e18ad55 a7608dc6030ef52ffe83a0aba6ba66eccd8dabfc7503a54cb5a7aed240d41631 eca153bebffbd0d07b0f0730ea86742194728b9a527a2c0d7e67708dd9ca7196 cb1ee98518e201e45ab869d2c1583bd1b4ec80885852bf3a901a20750e3fbc62 f311767e054ce3512e8cfe004f433c5f38feed7e7a95bee5f43c06c584aca650 f0201b4b6b4346b80074f792657f3c5c201650f3f8d61ffed324a98877e57325 9277aa5619b4589d4f66066a371a95d0b6169bcdf6e4ec2e48deb0835b7be4e9 36175914f2c475afb4444d22f1e577713a9d8cf684d5c3f24a308bfaf7dd6dad 2c8fb2146f2511b9265996c4d7e19b42917437ca39f88a716b3890eacd793fb1 12181dced537a1a6de142388b6f4bcb49dee53b80a87e9a083fdfaf5a63d8fe0 9cde7af830d69c6498aa8210561719f4a53130f4d3945969be42b8a43b7b83c8 b14488bc906beaf14c8f9728c94c7d3331d0b493c30bb118695e6a46fb1f7bca 65a9e2c21bc31e3fc94786e9a29ccfd665e924f79429ec13ff34419f3a76f4f3 81e3374c8053ac802409fcca9bd0398e8cdde0fe52dc03d5f238de381841965f d5f1e8f398075ee136217c8b8e419c76b1c72f88f6e467e02b9cf719fba57af4 ad59a999143d0922c381fe5bf0f1a5a1f424f45122cb7094a8be61af365b8cf5 ce610b3ef43660e75a139e2800bb5a904359b69c3e00a69381c2557f03af956d cb22b117935a4c2a09838a71897dbe946b8f3e4b0fd00be947d3db3f9586160d c58d8a87a54e3e0cb2e18156437a4220aa7b34e388acba47d8b922dc8b63d5a3 2ca1727bf6415e17425f7f3fef1c049007d7f83d0d322e0589223e26ed9147a5 30cd22a5401ac3e10e7c74b72eafdc682c16cf88df75e03702b34c3caa4d34d8 84ee267b29bc945dbabc579eaed4a2261594ec3135894493a81af89707d57243 8af410ab8613a3040ea97293ea41d716fbb7b57ae6f010dc6d29af4864b6a889 704859bf6f6a01711330735f3f7dee60c25f0f98ce25190c1f8cc9c0a474d5d3 3f561d6d5ad2d0218d48548ea4a9a9c62c266f6579c2a86ee12657cc0e2ba7aa fac68ecd87acc309ce8fb1166dda6058f3be279c8250a03f04f5a9d92913beac 572de24d62976116463376eaf93dad098910f3357442237a88c89c7b17243733 78f53d1b32a6897a25ceff23dc7da64da030ed76b029799700f060f750a9ca14 680f4fe78e53fbd9466c888cd290b41f34019be561b21a4c7461a9d5d96a0443 546deefe69e5f7c4011205b7af670d035227b4d5e1287a41b45d9890f6a934c1 869f85e332e7c7c2e6725b3a462c63cd513cb83f00e4a536e8ebbf2e26fa8257 372b8c8d67950a90d4e75456b941789395af3454e33502856ab3fe5479369df0 1a711303bfc7fbcadbf453b27b308b4a47360e4d609685d890442bcd28879e3c 92114e7c06e3e560b3035ce34a332778c791dfe96179b6e76b0686ee65613190 9a5956117f247a118b081b04d06a71333562995f87b2a721cadc2b78a469ccae 90dce7ed012756620526c92e65bc6fae5b813db6b59101dd8bacb56fb6b07691 c694f4b6663e31c93c8ce9b2d4d5cd43087d11cfcbf3c3633f381794761cef4b 3e027e4a1c00e81730ee50ac59e40ab14ca8079bee2e7c302d72a42036684848 7c4b1d9cead803a79114ba25c41605939ca1f4a23b21331027354d7c52c39930 cc870cbb77fc39f51aaedc2740ee3938a84f31f04ee497f67a309a41845fcd9a 2e04c8b1ec2f5a4f218ae5e48164074b5abd7c7dc58294342973c2726cfc2956 b1303c0066920e7f38e5f6024a18a757b5756c251a1131b0eb5ab959f80260b8 861355f58083f7025465e57f14c44898676c3f4df1612d8267bcbb723c4e620d 250fe05e46a306ca257366d060867909a4ae6a16cae2bedf296428de9b583566 ff29da43ad37d53d2885e4df4dd44077fe26b0542aa6796f90c7a832ed65e729 24a4c8cc238ae0a941786819568096ddb7c111954df27e2c4e3260f7e5f00bdb 0097658aa2be4c04951459e057d189b20ce242041c08e005919acdf18093ada1 f233c4b0105df5f9f974a42bbb78e838b435fd0442fbdf14365b4c2c262b92c1 321d9c076d9a6909b142b0153e0e3ca1857febfa4db63c8209450f71e57ec42c 12da6367e2bd388d5161be5e6990e9fa8678d935c7d8f15a2f5063709ff2f33b a16bc8fc892ae506d8b6afdba742cec52e1e0b3310cf8aa424953598f5570341 98d9be205b626da584b070b6efd04ddd48a77d4361c5915c25e412cb4dfff538 adcee3d782b4f6022e08d9ff49ffebaeb60baa1a7e18d9d2574dc41e025e895c c98a786ee287c7b1e33afe47e64ecd449dbff905857149bd7dccb92c15deb20b8f5d3f440ea402c3feda4fe4287791d5da3c85b5af85b34911470c2d44c62a0eec67965a0e7414ec14343e2f290752c9d4cebcb80f5df1842162ab3b2b55850c3d90193df3df456ad77dffdcfeaa3e712a5735866c09ca62b4ef181ac4ca5a0e73d25f50f7e66634d0decb3fde9f4d90888d480a26646fa32c0dff032e83490694a46861ad5d2b20e2b354126c528b55658eafa76c91c038d9e6d87584fa9e0e054e5997545148534998b513c143dadbd057929aae4ce117f5fa34d3af3fc40d20f277248f8c417ea1ec3dc778d08e5db61a0e06fc5f65855c349a219f4d8a05ebe4094856f25911eda074336470e89386f4fd1653b4eed9130ec45e0db62904002fcdbd65bcba85a4ff981f7df29c8e64e37b5a1205050741ae080ddf31f40f35f0d23db647a42249a6bb20b545114e79e5bc7f877ab5feb09e55e1f6fc8f0beba26a5060f258c83555d2c1e5f63f5202b7f937a5a946c7a74016acdbffd4041115f53cd7afa4e7e9bd02eb6af848f0e2baed03be9965626d06a079dd537607b680c5f21ec6a03f181ee9b6c2d7e3c80b0d51007ad13c14c98327d94b260905c2de4e3f03e252535b66475906d5a568a454f6db574aa1c6de0049549594f909f67dc88f96ae457271aac44f8267e2f088517c55017de3ad65fc7f263eed2cbe3e7b94a19d6486597aff983522686c12f5100035157e56d94e427bdebcef9d0eef039b080e07ae31e82355ecd73c3a6a52e709b88e8bef5db8667595eae257068194ccbc75fac77a514993d9de8100b4ae0779c5610e7b30dc2c3ada76a47b05b8e17e8a1b49367fe7373ddc09a62b0e6ae1b615c920e635114a387be7042404bc99f5e7db5593b9c75a6846a67f4952c7395b3407ab7ae3dc0ea78725c57834ee41bcb63ab46d935ce4205e25e6a2f1021efb93e218b4f2f053010189c0980087ad566e4946642ebaec792f70d260b2a1cd050f7525ee7862d86589d5e7bc050081aa5b17c13764f9313aad579f8e3cf5005d445c32d074fb087b246c08d70e7bdf65bf2af1221c7d2e681c23e3cf134018f5b3790e3c1f8fc61c1be4c40707ccd4799787ec37c1903b7eb3c4fb33918ea236e41468bf119549ad63cc9d1e091a939346628d17c2757444f73a3f8d4724ae58c0594e1b3906da909810d362022fa764eee943011843301bcefe2bf044d15a5cd32e38c8dd5e8c52e5c0896b022c659f22ff88d82d2c4d65b529793225737a69594613d83a19f2d62e2d5af4051057c53fef2e8de3fc519bee52e84624efe3d55f4017bbc2153b9da077ed42006a140d19098c6886715ad6ffa9cfd5c9a61e11221bb453e32092a094dc7ec0018daf79518c12c58598f97c860337322a18ff3f601c7473a93f0026f5093a4f04751fd6c150b0e1793fd3907043c6cf8d841736e4eb2e64382dd10140255e9801b5050989258c894c3aff138396fe3f89c06c807d57a63c4815441aefd9b9720808e48bef53817637a4fcfe5364ae19dcdb2c603d75cc2402cc4700940138360e3c7d72d46006f8fbe5eae4483449c00023e15df95a58508c3846daced7df480bc8cc291e4a2c3fda30da7b7b6da7a3272432ef67e67626804dc8c67b441cce01162c5c208ef8cb0a923caede3cb770b2917d3e3ee75ab0bddc9065ad31bccd076c58575daeea96c88a216ef497f9a43f1dd04a54c134891a9dab7120a08f4b0504062eb6bafcbffb2e1df9549ebbc08f3d0cea26146ab376aa0de2e70183d9081a803d34ca234fdfd176594a16158c0b03b5c5781dc10512afb7889c310d060e2d715fcaaae1dd8a56fcc4303e6dad763d16539f6aabedb1ffd35bf2b02e3503b76f889a9bd7d84efc5bbd68384563385964501ba7eaf0d56f72f0fdc9b61f0721cbc677bde80fba89cd959b3a79a1b18eccfaa2bb41cad83ffb2a648d8a490746870f154076e8ffb708504e31972d09eebc2946a16c3e4d2b38c0f6073c6800c59b4a3a76ababc32f7f4031935848756abbfd5ef6441e11afd3801137a8250dda4a165bb30881db1e2215d458d73ac7b09187399e75ed5b32f1e09b8b57fd04302b472907ed48735334759b9ace35076ba18ad5fe0b01228ab36c9392ed220c1e8859de052586dd1805335e94f3d07d9b6e188f17830c9e4af54e067035ca0d6fc48d14cd9d4c2e2f1c9e589d609912562e976ea211160e6e21c28ef8c2120521cf57f9c848c93eb8545ef25e8274713988d58b8df55ba46b5665976225300554ffebc165fdbb45c85429dc73ad3ec01c2d367b59ea0ff29ac048086a82dd59f802d6a252aea5d7e1bb5804ecd3031b7df6f8b82790f7d4d372d1099b546002f7cddcd0212571109c4dd876850390e9531e51c0832c2177aad65f3d13db190648126f909bcb38cf7ee5bf66e82a291949d9a235d0db88fa0be025cbf08bc302f6bb39190dea4f26a0e9ef571c010aa91d459ff215e97378d7507ce731b14d00e7afd5364911f3c090f05b27ffb189422511628b7dc2c37925c16d499646780fc7c4d90426b4838185234d3a4e86bba2aa6204f712678416165b8e972a82a001120e3df226197150ee01a3f0806f729da9ce33f2abe98b80226ff968a75ddb001d0334ecbaa1b4ee126b2d7c451134ed38248e29bc56434c95d006b3ee4c550c4e25af51211d1ea8cee7f71d59120cbbcf0027500f2c67000f098b9a7522eb02c7e60b0d0885825d3909e9312bce7b5789a6bd1adb11068ed6e3a561005d3f051ee1ad3510a63f50eb584c8a432cc02093688bee9b8f912addf6870e7e7f590e5c570a94d2d411dddb217403997f68f8831badeff2dfeb62dd6d0e55237cfe02ef240f4e1e316b7188d2bc2bb887f50f833bcc80019febc6b7997dc42b3eea0639c01844922ba89df46fac82cfd8f8c9cc8709bed5a30d85604d72b9fc447e0f7f25bc3fe3f50c7bdb47b1c8d877c0c720616ea5bda7f0f93e01716145041d016b0df1b68d3c2e8ec9940b11a049c55b8857be67f6ba2babf87214806ad91905b73790337c5ce59f63305059588cf6aac1f07bb5bc055b4f04a7dcf7cdabd406ada94f633c988d9b10a649642535fc64d28e69fd24c78339c16eb8c8fb7936035a699bd82ead0817eb95fd48ca5a8bb485b42308d23d157457708dd41b74dc0e624d67fb0e30f143450c8a687e10f4136b342f6c5a148589af546e2338dd990106a17570fdd6811185bc7147a08951f367d3f7fe31d153c0cb4fef61a21cd809397d4ab013f4844c72cab66f933cbed8c7592b572038bd88d754fff8fcb6a40a04a9a754e3708675f65006d59461f5401b2aabce2b563b4215235758ee188b0a7c14e58791de451a7b2d69c4df5a1d3a9b7e796bd8fc276f2948abbbff64f5086d654391c5cc2651d841f097aa0a40f1ee43aab13093534abff215322cce7d0fb94399568a76470b85b6815853a0efccfcaf2373ca3c7c64a2ba3344fcee830d15d924cfda5df4fc0ddfc7e921a05764dc777c2dd3d9352b7c9e221373258e06b3114f3d5441b03290e28fc07b2fb765e3bcff69e2a146e4231e7ec6f0f7bb0daad2ad1af786186fb51f9a16eb7b61b95bd6dfa65f6805d1e48ea3f091180007ba037ef8c4bc941e3f36ac4bf49df5793b28ebee9ff437567c4660b9e62bbe050eee9b8ff23f8ba1601ceb1189925cd74f5bd6e9c47a37381c32b420f24bfd0b3bd893b05759c8c6e7db4aad0d7203a4a8011f8855a0c5e2f82c5d2088d75d07cf414dc287cfeaf161c6f1d28a4bddf70330116236515fe63bf6a1e6d36bd8016aa586084e0ea9fa997fc1eb9d1d1c4b9a6060f7f52ef85ce780fed51094b201b5508abbc971c3ea2dd2375a06d1ae0ea6995ce3c107b00a521a6c55ae009b0632083f9a253738afc349b79f0cb7bfe3286dd0862de92d12dcc46512e3bbb5011c4178c67cc09373fa190e910a611e12dbdbf9d4c5a127fc7a308a58681a510ba97aeb9f5ca2a1cfbb579a64f989d7327456e3b0a9894a600c0b50add2dcef0be32d2d37e2929ced0b4d9a38a95fa53007da3228b0788eb41388113df1c1b308a121fece68ee55ccfa3ef5eddfb61105e218511367531764904f0ca4e23fa400dae3ce9ff031122d0504612cab278f1158cce7deab8f1c2783375db963ef6600a77b196005d68b9ddb2fff67e802f91bc584b12f2e95ada93cb4fc28394fe607af200d082a5a060ec559ef6f7b84a0b97b18ed4d8329f03628242bc19e83070e7f4c3a52b5726a25421b13875e9b4050f2e7455703a605aa8ab765d9f830db05c385a364c865e626ba2bf7085457733cf481a23e09c223e4068b601bf40b2f075954bfcba1801411169a83a7b00ca43514a64d549220aa2e4c72ad54751044001ce08b5aa8834755bad1fc4c1506b700af6e38ec8e7d6baaef42c9b9f2f3d808c194535122fcd704a89be3316730f1c8af06982d4f8e560fd6f7e65fd09b9c05f29398c68c4463fcce4d8745558cd0e177ee1c9187320a27335a7498a77a950e6e0e8ce2883db4c856871be21d459d8b6bf38a84fafcfa8e96814229e6992b0586e9810e0d2d2269cd7195e0507b2a4d8048785a1fa79285f5b1a69ab7561706d59f37ca2fe29c4f932a7b62f7ac385fe34bc7595466f8becbaf0b55dc76840571c301492366a79165263cf22e10bdead9e9fc20a9b92006f6426ef0273c7c05f4d1069928adf783eaea5ead7ad91a4d33f0aa77932b72ff28c4ecca74ff870501db58f150845a6bd2e69c1fb0f6f38182dbf76bade6b97f62ee1a82d18b73019ace75ff7055f8d734c256a77930214ac9f2e18ac98b0e9874c1e47a1b0fe20d7f232a5625197da8261475feb1fac4213aefd865d27f88e7a94878819d915f0f61b58adba834189bede3b7906dbff91302f6517973caaf452f9c243507d1f608ce7982f8a603b018f1b582aa18353419272c5351c91aa893488c8d252bbbd40acded99c2ea92935e50e8838a9d33a8e680d37d899518835b4a587042b94e340002852cef05a6d604a7475580e3a82e3ced08b2e635fcadaada947af84ba01b0062e4ea17f8856f2f9fcd3b7ca47d7248111c003679abfcf8b032b609af115e05952d3355311c2fe91aac51c19e1b85de4fa653484ba08795a43eec68f9230109ed0d098224629a9e2fba4209bc650661efc690a5a4dfd92803b90f3a37b7eb00203867595bc500dc2ac98a6321b7675560dfc2c3fa8141b7aef6e10deb5ca1071965cc06de4f6be191173f604222deab98e4f2706c5a3bac18ce74ba6ef909033cbae983c5c1b695514e07c703b578300a7480ece8177ffb5c30387628729c00822e990b8a9eb3c4be5bce2b98b806eae69dae67be3df9bf81011a371a76760ed2257577e4f5fd06c5b03e4737326fc2ed7b8540517e7cd95eef82ce150e930eee301eee398f53cfc6d021eb81bf4b6f30996b7bec5362eede488a74521ea200d701b7048ba9cb902cdb9b2e28d65a8dfb9d4c7a2e37c02eb556618fc8d0ca0744c56ea3a4a8975fd2a298b34921e8089c3ea18d40d0129ff249bff87b5a8703a62ada45113bea3059429d311af22393ce6e0bd666f8dfb9006f2ee6322dd005a35575afecabb113c239ade4eed3679a9d5554425e99c5a05ce49062234b180f402b27269dadc7ced163ded65149be0228cc9340af23722c91771e96d94294030d3153495285c31b52853cd6c0e4e8c9b41f135662d7fb7da30f24c1cc81d10e6fef04b7c0dfb099eeff6349a00da200691fb9171294512d9bd9de4456cc760e51d405077c444f47539830a4541f05473b856dba6df66e8aee1d1800d582e40257b73750ca796b390cbf8f28d8b1ee7ce01f47ffbdb32210f376eda2da0497000e117bf579fe62c0346a06086c302954fe6bc29760d3ffa45e1e4ee6c550fb0eb30185ce40f9b4e34270849bac5c5ba37a4d9ded1503b553946699a483751a06a7d2faf4620c4a3b5659586a537216de0ade80157fed7e7867979846cf97b50979237cd5b1020a15f2be82bc589fa4501bc23791a4c1f36154f7b609ee373a023914f1459c5f150aea225db54c7cd2779f1723cf1565fff4df3de6e50446a70e false -check_ring_signature d671fc2ac72862e5ef3b96afdb38a329e3c159a2968bf231b0276dc2e74465fd 8f01a72095176288bafcca5c269376fec6e6f077730ae0a327a2e2309a4f8baa 3 da41d95e1e256764d8af93c86ef6c4aae4c0c6236464c51553ecee182e350ba5 64b6961581055a972a55388deb364962ff79c06f0f744174e42e720c68470e9d 84e2556716c0940cc02e92082b62e92b3b8cd5a12b413e3aa13b55575ffdbe0b bed7d393070f0e8ee99b67574970a3fa4083b20c7e72f229dd5bf554759cec0b7d3b31d6383c09a19cd3175bd381467613261aae95d3362b98e341af985f440ffc9ea477ba973b6ffe3ff34e9dc41222e01ae4f3ab56cfd390940bcc77e8210d010b5d8981475a6ac1761038df36b6aa60cb9e5737291d8a43f13e0efbe6fd0ad1bad149dd3b50424ea857f978bc4efe267d392d8d5e6ae715c57734aa2081059c2ecdbfdcf8bbec51f04bc44457dba5912a35994921da81cac9191b2eef0c0b true -check_ring_signature 8e3fe2f1c61e15a9788ec809bf018393230907172d7c141a7685b689269d780a 080465fdfc3b98123ea89763eae971bb6f0223281a328bfb54164b5070fbdce7 3 4ce3c774a679669a66433eecb8ae8b4ad0ccf982bf15044e194c4a6d1f78fff3 f3f5f820b74c5291576f35154ab51005a7f1a9bd30a9a2aa686d6e4b062129ef ef9929b57d4fa211b90690e5cd84f928281707c587c37e09b526948d0b0471fb 0b2e0128a45ce7b6467f8e672a17360b210f6d1c837e74a4ab5a81cf177c7602b0f35f888413dfef8d2d5abf667abb4c629d840a908d12571f67635fdca0630dbf9e0ca509589a5e0860abf32e38680f1cdda8a8c5859124f92eead84f88a30f2f50084fb3effb4599319e55868ead5f42704a85a7f3b033cd9fa5099f9982046751983683466356dabd324cfa4a8d21f8eb33fd66b49532a2a4b91cdd353708fc2a6486622bc67aca1643aa036fcc0a1738a18f93d1fdee8cc7a3fbb6f02401 true -check_ring_signature 1a4978cc2de4abfa2fdd74056c9e7f158cbe5c2f206f9da49c0f8839929815a4 1daf2d4b87a71f74549086eaf311fecaf011b26d8216807fbee4b885f66a5742 4 3dde6e4b7fb32f6b1b1f13236221504fda2194b82b788ae204d5abc2ad00edaf b96e751d9a315c2a5ab4146f73363ac564b2ded962e7664b3209acc8031b9629 e1d7eb1e9dda3c47cb3319fa483f431749784c172e344e07ec7b604d01030e00 119ba6d29490fc3807f0ef552a68e662334f3e2b43d791bec7f76c84e85a56db d79a8adcff0aa2c0b6e28b59a36627d6f375e9d17d95392c82aeac5491a7b60b2952631efbbe6868f619b6928c905da02df2e9573580eb86417b4f79afb77506569ddee6cba11c0ee25569436369fa8b971ff6edb3d28ae158c7abf45ae752027fd1097c8e6c0d0ea220ddac14b15349f03686126bac5b878d95a0b8a52d450806f79b692042dbb37e23b33d33f53a00185bc9b864907b09bab316d0e4437a08219b050428997c99360b99affa55ea3ac72265020fd8c4cc48ab874721d3b503938d77e7bd4b3326f6ed8987b239124e80da38a2e4d5dd1676db4583a9fd120286c8725fa01ceb74c7b42193c783986edb7b65660b6703209a589591d63fac0a true -check_ring_signature 656fe200c94f50d436f03a44bb9ea811a1685fc7b34b3e37b29239184a1646e7 0e3122b3ac427e727c4700496fc34e126ab08935a79c0abd2780011c6686b4cb 193 26de92250c825398a02b8427fe648e05e3b9b47ff3aa58be4f483f7a4ccb91f0 1bb523301ad5d5200e0c368483e75492fcfd89b390fe775244ea76b36e3d4243 466ac9227b48c41b6f700f33a339df486daa2315a3c155a6032486c706127b9e 7afee995ceea7c39b0324acd9e5f0998c2d97152c560c1f60e6ed1919a2ed055 145eca4d19d684f6a101788a245d0d62e093a27441066aeaaf0580edd5c5bb63 be958c4e57ed7674cd01d7f03ec1c1a990d641a039edddb59e49075e194af44a 49190b36c7a1d121a8158023fcd8daa927e3286f434b31b82ae2a70a2c23db6f 5477631fc6323698bd26b80a9abc819b758c9c8b3231c076b101903ae40a452d 40d120b50e72acfe90c91b3a21f9f87a21a0c8e1e49359ea95c74aa1488e67d5 9be813c269ff4072f957c7319357a7926beb0d24ae5ba3e56a84c138f9e09baa 4baae017fcc2f511e97f1731a44f48446086f42cb9390f3108f488d1765a47a6 2e20f17c120ea61f3875bfe4621440761307ad4a20c138bd314a4721ab882c4c ffe491a25c31d3192b818a238a3634540ce6b697d28cc25164618fb85a41a4b9 f7b83a648a0593ff66e854947e54332cad793fd6c8c3cc46b2054f59fda97db6 aacabfa7505ca7af6c64ca310fecf375263716ebcd0b214bec194d5001989eb8 cd09fae52695dcd1c8cb450a93db86c0c76b662baf6848a507f184ca393ee864 3e75e0d458a2b4bab7b2441a1cd7519516f035869c80c7dd86b9264cfc257321 c6f5b7ede662504c337fc23c559094bf76dff011066e6315917b07ccce70eba0 5417f8e2bc4916bb3633f140af82601d6dae0396dd774917468eced851cb2b8f b0dbe42f0d82c44a6d07ad2da9df38af5bc08ab28f629779adc3726949471747 b7b5eda13d40c85c05038bba4c964003112631bf4363c139dbeae799ae42f08c 3300fe6b48064c2d51ff44c11cc3cc1fc642b68c5d604aac45e29b22d9e622e3 180017f8eddd46ab68eb522a9e061de6a42ca4ec954a021b7a06501dee9cfed4 840671a3285852709377741867f04e047f24c8a4dea977c9f718150c61dd3937 de0f7d9cd455b3b1522f45590a01b168b4070a1bb5af7bd95828a92792ddf6fd b504e617b95737c1c096dedca5dfb64bc49432aea02ef74bd1b54d7a59351b03 c5bc8e06447d94b7dbf3cf95d343d08f4c5af059225213321f98c25c86022397 550b679287870e8ad38305180a629af8da070c37c0fb21ec2d06e8f51772f873 3e9142a5cc9d627c83effdcd33c5fe0bb2f8f8f15ed66a1a363d4894385db23f a2420b67cffb1a224e2655105fbd550ef7d98d1af7c8c7920571667865f3bf48 912a134524ec987a50bfb41535b1a9abea3950faca175ed06aa85f42abf51c77 d51ebc850ca1d92d0d4216ee9a7c4a88e3884a6b413e066b71ebaf8f75a126df ce35c3232adb7f9d887adad495012fd3991512313f22d134e5b385958424f41c 8894cbe62e10d2b36f6b8d24e3de20c7a843e94134cfb7ba9f2e3d8177379489 a7abeecf90a1296baf8a2ed8097d4fdf8d78e3d97ba1a3198f7373ef2225539e ee80f215914142f52538645c3f6b9c9e707c568ca255e00167a721e0c82b38e0 00e2253bf85100d5cc5f90788928f45f824bd574529d23bb3f49f6f476d45138 2019fc573bd74aed19d8ffeba5795de9c3aa22c9286ec8743226f2745f463470 29d245f73fbff972d3bb4977e4c6f414fd2dbab8970367c900a57893ab6015b6 087d43ebe82ef06a9bc0803140f76a254d180f84539abb6bd8ce29d9e3cca3ea b73398d0863cb8671778439195d8fb10b53981cc9264f3ceedcb25bcb68f4d6c 1401f3fe5c534bef7e28aeb9e13d02c2d4474ce47d325a9c6e0197aa2e269d61 d756e58773e42bb4885944d4d5d71d8f529ef7702fc0c897bd300c3f08cf9b7b 96b05d3c71023aeb8e89a60392a0f8be0ebeb3e635f128ef765919893775467b 7270ef38c386a5f2c2ea909941a612bedfde6f30f30d6475b06d4897972145c1 e9feae63e6a5e5cadaa3664edca80201bcb309f103dc31ce09a3b1184ca69154 b4e5125a89eb96c80caebdc97c9c4f5d9735c0a51109060a4f023f3ebb9354be 7dff36a35cb185d38b1ef67cb977554346f19679101213ce5446cc4e8949902d def62127c2fdcf1cd5d0c1bcc9b515f79ac69f05e52aef024e4e6bb7ece5671a 1e66a44a38ca0f61b930051737114d3ab78d61a17bf23db097995cfc9afc579c 47181125be44621425ec88e083582260928371f7104021878aac3c2e95dc5e66 b7b37004b6307ba4c7403791af9b2fb8b24c1bc828e3c3cbaedd18b78cd15165 3d5f80f28751f1884a2bcb5fc35858cf606d910f8cf66d053fc231c62716c0e3 7e6d0a73f70f4b1fa9b88482337ee30cd6ed18972f77db192cdb3159a15a0da1 abf2184ec78fd284736385fef9a126c1c1dcc589977d25ceeefeefdcd89c0931 9d3f8193e8f07408072394188b0805c735ccb4d6f692039de638f6ddf2613a53 a91ad42e907c7c24ce6a84e45459b8f3c2730240c585c0952e7bf90c997c8341 bdf4c4648ac142bc537384261541c693e25bfaff7d31a33c013a4f159a7f2411 660ebfd2725513481a76442703310484dc304e6abf0fb9e6636ab50743febaa2 5c35a89a1cdf18e743b2bf5f279a48a9c69cd205a14aec9f45872979e0a6eaaa c101d747215367cea4a49d0d9b8f267bd6afcaad77f0594966587f197469b55c ef976c9f9e7aba4814e5c8bf2a1a4f473b07aae2d02f2c9a68010aca98b74f53 c5c6089d9399f4d043a9bfe29e3957edeb22db7ba8227883a64ced3966aab229 e2791e2b410f1dfefca4914b8921571d5e49d96de1410b1bd8303535fda54678 03d2ece9a4bd3015f1f6413cedca1243dc9bd72fb5791b82ef60e15a9a19ffda e7a6989f68c9dadfebb6dddbfb69e66f7c57c7e97c351ceb857fd4b6e9847406 6dab91f204dfa518265c61d7ee473a12efbd5e6c3f08dffca13de96400c66e31 0450b7e01fb4875b544d0647167e572a27d6850d5a05b04f9481a54b70d24288 4c7648d81e81d4eeec9c526591b101cf01b483c77ad9b0c088f92cbe7f2f296a eb023631166331c3960d1a91c4d2c235875fd7569919053e7b4153f9dfb98514 d9e079c76490813a996be1393575b77c490a53604729bad4979054750a1eb18d 0698ee4f8726557ffe6fb1494c08f306060f85665e663d36dceb35ff7ac736ee 4916f2ee3df0950e3b5f621dbf6cea29f89341acca8ff75f2419e65e003e25aa 24d6e82d3ff6b3cbca6457ba476e5ec1b08b92a2d100fb7f98f1aef40eb5789a 45a909919754c4701633a5f248c96d1d3096c83745d1ac31839a504191f8fe4d bdba90e99b4abd02a940b334906b938e1ab05be838446eb4e568bb6df4dfd458 a5f5dade60f562c3c6f8a12dfdb27fba95a9e90dc734da86b6605305b922ab7a fd8cfa3e449e358cfad453476a8102ac06c7e313a04d04891a2374277f774b37 3181dcdc75c62bdd25f2127362f246a4a9b5b0f17da0f7d01597e981a76546cd 7717df26da33817fdb1be7ab79745d725360125a91d2f56eb8af322aad5275ab 80b1896a36e3c6d44c05508d1f0ccabc37c9c0d96503ededf5680bcdf00d9b0f 2a2dc651fab7c7b9e7cde77a1b489087964c30084d08301bc559fec7adab1eb0 d54b4c2666f4b3594cffc1f7a7d0f4630c86a6a502db2da30aace5a92f6e3b94 e4ef7bb2f8cb52bacc347e5e9a9455c3fe102f4f3e2fa583f0c4ab76d69ed964 176988abf776baa09e5af0456ba5fa6c99911ec76d139d0e79afcee18e147100 a35d01b38be28afc33dd02eba518003c61792fe4b3044cb3edb4b12bf131b8de 6fb11c9ce9011c424325ea7317106e9e51a4558e293a18efa30938708ed4855c 72131044eed73d14a80163712637651055a4ed4542fbabff99eadddf249e1eb5 eaf8d06fde250db68a03e922ff532158a3bd6f11b279a185b24205b505d0ddb8 4917756124f490785f8821f1d38e9834a937434fbf90da1cf7d1520cbe9ea122 17b10717eb648dc1d0d5d0b097a1ac059f42f9ed06134b885e5e0471f628939f ec5d1402fd61ee2d1301bf3d6693c2f28170c2ca62ba2815f8f6198e1e56cf4d b4f9f5b94b08224cae9504560ae84b10a688d0a960e59b8b9f3243949e740802 22a008fdba0c757ec702ee9f651c21d3afd6a5c3f9debd2e92b0a18b3819e992 05e28f761a3a228d1de10fb0e9f3153bad293b570ad18bbea5cd3436149fc3bc cdce71a41bdac34970a41af2d7e2bf9201430c0b8e5bf49085e458f8363b54b0 e0d60b1b10f80710c95178e362b5b630d63dcfe50d85f5980420824096232937 e30d9468689868080dc063bf5e1955f3449e8acec61f979ab67bbc87081e10c5 9f5a4710b9f14aa7b4042307116d3f248f402aaadea8ab0e645998675e4c183e 2b4352d1949e998c13b49efccc204bcacd7ab39d0d6cea4c6da7427930ddd773 4923a612dd6f66791ad915b269b7e638c620b339adb8a6543196acec84fab2f5 c396389274aa56d9f987aa2bce6841dea2fa57a2ac2baaeae154fb11ba3d481e 5ca7563dd1dceb0c536b43208f7f95dfd675b71497e3869bcd7ef346a7300273 7e4dc5406629940385aaa978ebb9a59a9575c83c01819ba4d7b8fc4ff4810ee5 86b08fbb10772148d808409276bff84cf57dcfebba5e40e39f32903c39149d49 5c7b3671f75ba96d1728def2d4a55cf426c8dae84e7bf62586ce0f2b11975cf0 3483efaf5383a304c601f49ffc296067c928e58329aa1d8849c810df114a5516 70f59a9c34013c20e104d789d2f9e799f5f5cb7fcf50c5506921a19137263037 3e70d1a3a9d22673b58aad1f2ac7d7ad21644efe88a8d87f08cf799d15c26806 1e99d0ab1647f29439d7046dac709b4cd634a3703356b48dcb9744ca9969f26b 0cd7ea5f5bcfc6e07fdea1aa3e99838f221a926b515d5f23aa4cc211ae8656a7 dfff2b5a820b35732aba452c56d9a47d8e089f71ea2786d4c3d713d8cf97959a 3eae164d7aad8295ec39db9d37d82345dfd11b06fe18247f99e3e0cc2e1539a8 45c9e5a79fb69814669c824c6be6859ddd1bb342e795b67a79862fa588df536d bc26bba17a1a01eed0bef7ab130d1cfaf1a190fc8cff508570698e7bf5540f21 74e2e73157dc4bf737e0203ce99938bf24a266fc5e2d435c9a9b49c1fa64a43a 0ab8575d91015da4008437bdcb86f956e8c49465c0373c6c2c9e5d58ff51b921 59da30127f2f7e2181603db3e85c8acd8c74edd14445392f4acc9e0450a428f7 a6e9e584a4ba690649b00d4f3e30a140e5500e36d782e32314b92ef4442893b1 f55efcd4d19867c4d4dc709790acf5e7bf9540b7f33c42446878ea1cd2d5f539 ece4059cf27e2cff1e02b8904306cb82985de21365d6fa70d934554b33de3306 9afefb39ca0de24fa8694b8e5eddca64c3dccd0b79fffa84d9e919ae04a4a8b5 14c70df7ea80a49dcbc81b6adee6e94331944cbe68c6346d5c543eb8da29dc4a 28a832645820620c2cbb85dfe068bc2df8cd5a1e1640fad517c1324601f702e4 4d3305dcd760736fd70e6f9aacd9a1e11dfe33602ab5702fb4414b050d55d446 f4808e0dab8adf3e326acc2ef5c0bf50c929f1cbb3739b7160a981d484563f07 7467f0e0e7f7acefa8e9d57b53804f1fa6548b6c0154462e5af8734d34846a12 7cec4ed7d3fbb6d962f8afb13216cbd9ce32d61e732c81dd4ab62c48d061604d 3ce2279b56f258447ab0139e8aa77121b6c4cceef37ee5f842732bec5a1f3e7c 82feaf3e16ed7104b66469032a9add01c5003e50fcbc718384a86f1079ee1c24 76fd284c6e24919d99001368148ceb4c437bb7267073ca809bfdc1818352ea96 4d4a98486dbe1cdc81b76f54c2a7b0a913d6527ec402816d075b5b938cab54ed dbb0101edd04015e5e22b115628ad3f367f9d7d1ae0dcec0cb82e99bcef80393 593a14a3128bd057faa61dbb9edd4414824f7e93108d44bbff88c6d6a3c6ba28 5bab43b6465e07092615ae97019e8598370e9ed9d77ca7ca962598f828ade9f8 18683a156d7e70e6c1bda9f1edcbc53ec062d35f6b9fb0e7222728fd2427a9b5 9eb3bc6440067a8d972b1f2c6ef65b1a143d51ace39b44d7f89b12489d21395f 95aef2309b34730ab930c8788ed698d777c9eb09cf5426efc10240b9eb96246f d5281853f7ff2e710543c01cc27a8983e1aba1c9549592f2d11adfdba7144eef 02e8f49e70c4e120f8cf6b7ef8fadea57c861f885cbcab535890257026cdf5a0 13d5e4b72a6fee14f21c7586fdb4a1f026bb92f81e1044ceabf3485bd22d8436 6e08337c7dbe15a67082c64d27b71f7855554c2db6c86ca4eafdd13904e41345 3bf587ac5c7647e2eb526b71772b1fa3b05765715124852336d2726cb756b145 185fe3e46833803eac5ae0ec2707dc305f55856e96ac680451c05c40ad443c2c 8167a2efa46572faa6384c8cfdd6ba48476a6497bccf70eefec19b79095e5a67 4a08899530193530c0925a3186729d9224d5ada91eb29285dc24e6dd37f22613 8a0243363269381509eb77f46611468a2c59bacbd87dfa096737edda19b9e9e2 deb2df03b94322d053a608460b30be85465261c53473c4a01f41293361dae72d c12a6fbd1d67e3904481dc6b31b3897e84252dc5a5604683ba30653ae8454a31 aa2ffb179d9df25ccafcb0315a3cffc03ae519abc94aa2405a3a7677d3689221 727f0ad8aec7fafe0524525951bad6f2a85a8695f4a0a0e0cac5c28833a29a92 f9c2d00d7d913d1c5ca9047604f14dc074bea7184683e27d1749367a82a18fac 252fdd1a0d96b8f3c8abe689caf43c5184e51d9dbd1e0612a4f51858ef672b25 48c59ef7e6e45ca1e08e68b49f8007158e51385029eaaa3a23c470765c05a8b3 ec463482223a994bbac33c6b25a52813a1c60e18304d5e3d98372424fe01603d c00fdaa6dbd775573de56aecc10bb936f1a008aa49c6c10b0615f080a1dd221d 067f8d1a9162c5689a2b74dc868c21eb6827245d6ceb2214c1afcaee62080b49 a2adda7c12f785c189241e9c89f52385189cf17e57cc1f7d8dbfcd58f2700040 75d9ea530bd83a4ee830ef6e83c2167ca005b1c79a5cf0543d62da7ed639a58d 9fa98a963b7950ea3920476a960cd7fb1a728a082075c8849d698249eb848637 86aec24fb6d442ffcae3871f2dd9b7c5a0b1d9e9af25092bc6f21cd32fc32654 f2bb7c9ac048ba51473c65abffb5cf12ad82eac26876d8c68bd7565441ca17f1 6a20e550135ca1e6bfc8f8c58ed29998b2d2f44191a5d2919580f223dabe06b2 ab82503892bceae077e52d0d5919a9e06cf4c707bcdf7c73dc995afadf1f681d 5515d7fe58807c1ec9a64093bd54ef3591a9183adea86fd8a57260b147dd68ea 5e2c237fa7498a08950cfd6551973a3d06230cfeada70e6cb82bbc4cd40d563f ff96544869bf6c20d301d01cb997591cc01c0a265611f4cc89abe0bf65b3c667 839abff435d4a40e97d23cc90df0c187bffe068e2394acb3d0b1aa090385182c 6c4b0754f522cd2b2a78baf8a1c423e3df66f282c1dc43aebf640b391f7da616 6e5ec7ce3d24d7b575c2d4367da50d9efef927ff0796c45906458769872d1993 cce57110bd661bf6274a897a0586e8bb0f09e12e82b9aac109b32cd036717be7 557c95a69ef104b053d0f66eb4c8639a6dc5286742565b94ff1787224e575130 9729aa606d5d29b6b4f1ad7c0a465a02937f6046d71c06d6e42d479f2e5714b7 a20f11ddb89d360354402721f16e4e1e78e4230ac274a3d84f73b1c820c934b3 17aef392d8371c3af37d6671e809ea3901e76d173fdb78131ab355cd7698c2dc 97b38c668df44392b1d660495cbe87cf2ca0e4515e51756a5477c2fa29d238fe 5154c01d96eeb02ec9517df3765df8b0b8ad9b320db914e6b63c325715b1781e bab538cae4ef8f6de860d8531d1a7873acf5e65f06a0872fc98e2034795d624d 994093b7eb00f7355fc8e1f6b61bf22e0b89aff370db167b05d30712a7b8951d ebf90a64ad2201049952e6bdafd401b5b6b8fca3abf1d1836297249d26fbe271 148a9053ba826c8236a5166342cf79f3fbe6e52e2fd5f59b516f61a52d1be206 3d92e6faeeb5e8ebbb8fb2ac668569da71a5ec0d8c257bc8e2d7c6c755d85da4 d48753499e5d5237718b7943e7bfa9b3267b537c2c3bc1c13dde8ffb00eb65f1 acb876a0ac9466070e9d47aea25dd5560c836fe1e502168dbda47678fd5758c4 00bbe7bb690e8a81d1aad9632b2bbfb071fbfed37f32711f19527ab54d8f6f19 ed4826bb5bb7181a166bfb5d3db2d5af71830ac462afbc6fb7c3d0cb4a2335ed dfd72330269d62e329335463f771c6b0e02f0e6ef251e8064e27c320b04079db ed5a2bcb2d5fa08ec1ebcf7d9be7f14d674bcadcf8fa21b042af15f74bf8dcb0 349f9e4a721db05bdaa72a474d05ef69afee98f181726d751ca4aa2e4c6f63d9 f4e41a1afef18af2983292165e214b2e2892dc3afe012bda88695d61ad47ba9c 9cdfc4b6804264529fb7dbd9b25b289ba7cdd438b5e5c77c07552fcd2636b465 2455b0757ac8504621817c5a55362db6a679667915d932a52de2ca402d54017e 6cb23126da2a901a412ec5fe1a320b19a5012a6b17e64869da38511f522750a3 8aad7d7d986f8ce8422c95f5627488d6083f7d9b36065612b4e7eeb168f5a70789f1bf47a53da541f9acb9123c498ba5dce92f611fc7fbc997492e51b162bf0e4a1ea601a26d7fabaaf3e849c54630c861c671b85fe9e3103fee79b234e82701552848cafc03a7c9cc8f68851b5cbc95284506167a51bf5535bc4f4f77aabd025e7403f15a808fd80e7842f80ea5b409d80415ca0edb72bc19d2eacdad8035085c6f22f3589002c4bb50d6330a0a7637773986d2c22b6e2f3c0e4b206a4b7e0e29eefde150b61c816a8cb5672b53e9ae7b2cc971aad6abe44b4e7605741fa907dfaedb80c8d4f799007628c3b8e52877cda41a48c9d0dee76330d312aa10bc05eafdd698fc386848e159940f9103d94800a2a64ab1eb93367f1a78a84274ca0f95dd7261324776960f56bb8c0ff227f37bb698e67b0fa2c285fe87186445430554e3445242668147276af2c49389e70d27d2ed6d3d5470483f08d6af53c618084725a2faade0fd9a18f7edca9b41bcbf000da3efa84d7eaae2cb8391596e110849a8670334aad070b75794a1d5ec9a34b92f2450acb9a2c6a90fceb7dc3618044a17143b525846cb1cc376efc0922e15ba44af69bcefe7751721e7fdf5aa550071244d23beea847bf60907d4f9e2a77aed38e003f5dad3061c27400e4eb912080419cfb56e64f6544b86a6e12ad2fea8dae2a62d8712ba0863f5805e93bc6b0c555d487cf4d857648394b9132f96a099edfbef3300b32de5ffffb9e1105b760b2b55e4535f8c272a733c5b2f75a89a0d6f0ad6b0fc737a6718748937bcc1a204e08db53cfcbce5d2e8d5d1748491edaa7d972577eb03a52ae43d417f2446a809dffae37b0e9bdba48eaddfaee0bec1cb99728bf7bc687b88460fd0f64fd6a20785496e6e83a736e2a3ef5784125e5c058abbf082f4ab7cf55f2f1f7d479cde0c59bb3f1712b21529609c7abdbdc20c8c9329600ab7c3243dfabc9c9670ed600e645f7d54217ac7a54e197b0289e2c65bef2bb180fac4f9fe77ac5dd4b7c91005d102e130e66e0649b8a488c4520c7616345270343cfede08e75e1928afbf3b0dcf08c8b53c2cdaa57000eb5959e332ce702e3bdaad9d0c14f67cff2b39ab4d07300904b51c34e87e88d77d43eb11bcaf98908bd0b89b3752162df6a31b0cb50709581b164008ca8b13f777111bc0d6c7e66f97774d95492973b85ad783d6ce0c6271f003625e1bed27a600ac4a467a51a0ff97d16f84fe1c6d85ed9f0b1d350053b3aa62023538e861f90a3bd82834fdf604a7579a290daa5cf9a99c336e380ca32aa77a2bcf3cad55870729bf23ee3815d2b999b66e48aaecfdc83eba295b010cc673482f3c03e695c528cf6041cdc1db57cf69d29ab406dd46285235af660e9272d9697f12d65ff8d69d7b2da3ee305cf8e07d28153d135ba856fc59b8e402ef36d391d0f096970c7b169bd1832b3245b3bad511960aaaeb9978f73650d005f0c05694bf18de44adba3dafffbd6bdeb84f1d5af57e3bc9925e2a1a1c20a60eedaa891de4dc2956677ef10950fc75922dbd2321ba008ff05402a5819aa2d809ece98e0b28b3e6746b4818ae2367630e9ecefe5ad7a81365b0a812decf04d00058c54b8fdd882d553469daa076ff07b83d1724fea656b57df56d52a5a65dad0441b51d267d24a54b310411c224fdb8949e656d5e59d7deee521a6cfa75faed02bafc5787f461430394c90abd72e37ad83234445d87e6201214bf83ae5515b70daea5e1f089101b3dc6f845805a7aa7c40c793a90c8f0945f86b75c2bbc59640689164f679e7fc7d23a656534815c82b1120abe13e60baed648dabb9f0b98140a08d0c1351ac225192825f230976968c87cbb8bc7395b33a049500be43f0cad09b124bca4f32c18dae87a7e053f1e60a9027442dfe32e882c273ae24e276a320758d94b5a2194a1ffe877da6a97c43d716a4d6359c8381d26c63dac6f9a39320b8970a399cd27a3f845af2325cdb4ecc4c6ca33ad10cf8575a1b2e14a0ff64d027f84d45da00b727a271aa8efd6c7210d5383b1a6a5bf241f5f98514fe83cf2047aaf075e936878a4a4f81823f7ac62e194f89af83166ff4c12af317a0345ac0bde867f1e82bb72ef422e1a497a5916083c0ff6795c960c6b84b1f06a6466a201479bdb29c6547be9eec6560c567acf2b550045dfada9932badcdde9511f720010caaf1c574b3d9d54599e4ba75d4f68e3d2d73a8d641044a862391b22c0f8c0380152511425b8a8bbd2da53fa42213dd94a23cdb75526d91697d49f0926cda0b50f9709a3497a2ed7739cacf26fc1e30ea87253dc93c084b8b61293624a0690bcdcb827c557972749fa6139c9e342b355d20f378955457db1ee6851f3664f70cddf3bc0720f97c9b3f6ab9ff4fbdd8b19b3bb3f9214aa029debb118021554c0965e3c19fa08ff2dbc8f67f6eef97a065ba06d445089c82b595212b1c7b8d360ee4846a781ab6f9d5baf82f8e075405f9005532049fc79a1bdaf002d7f105170028e52e3ff4300c2860cf55467ca99075d4c099821120debfce6e37fbaa974e02c88d43ad83e20efa79a1fb1efc582739ebe7d5bd946d05f7a70bab6ee14869075817d8073eb12d7e95f15b8c571140f037aa7f2e9cb6df76a83e34ce4a91290d269520801a722e8aaf517b34fa158078bb81837bb902ccdad4f37e4609079604fe0d50978cf22d1b4a783e03da307d601b255c815d4fbac4f9ff429b116dfd08f2bb9b0e74b88cc3369113f535ceac5fa83be56208737beadf0991208e6d7d0db6f44469b1c691dbf6d995bf3aec59843ee1e077418cfc16557591024d3c6408a9c2727c93ce181c97481acedc4aae1010a588921fceca8e3c67f74ad0927a047620b165a59367e39700c9a0483714264c63b9f30c1d4fdc805d58b46a7e4201965d1d75ed6150c30d55fb8d0b4a978c08bfa6ebad4642df2b38fb2f0a2c5c0baa16639afd6626c4cdff1942f3b74cbd5fc7db9a5cfe9eddb7d72dc22e8bfc0e6c7da2195e61f80136e2d8fd1f5064a8bf4cd8662c2f3102dac450f683280f092f77eefed7830f1d83207e91a29115dd957fe682ba836e3dc38d17332c0be706f9e096848e37fb870a435fc4836656c549c094ee9a38a42c10cea887114a17090869a9ab4befd818231faf4bceafe3a81ceff42047d758c46903e6cf40d18701814aff7366270a12f3815da91f5be6630846310a772e9615cbe29ae71e99830d3e6396ae723ecabe509e69629e2c58265cb46b655cd1e79f5a8755691e40af0abd1f5d9268bede996cfb7e8aa27541ce2a8ccbc7e6264731b567112e668edc094c53c9e852337bd1ba3371b8d9a759281c10e1023be7a669ac9102129b570804fecdf67ee9af91e64ee746c2b71d9a540ae93bcf058468c837425a8d517dcc03d09aec54dbbe62b865245d6a64ca7dfa2e222a464b1a73d21e9e1673b85cf6094a4c1983055bebd6a58953b656f6ef9920f0f4c2505050e4050809206cef9b0cce326ffc6d4ec189ba719630ef7be5d802d160b9209d80ef963747ccff095002a60d37cccc9773ec99a298fae11a3530d05fb3ef51eab62296298e87d93dd704adce930ffc64f0a75eb369839c51a7d632a3ad72cb81dffecb68b5461cbf3b029004e3cbfc4cf6fa73af4bcc40df6c096c81f52c7961ce3c16b5e8b150ca500d919ad8e87b9d7b8e656d4dd3ea2c82811a16cdf6322780780418c46ebc2ba001975069d015a7b1a350838c727b488c1010f3ef397089c187c8cf6be27c0f300d037c8b69afd4143485dd06b5592db3414f1c98f360678b5d2a7577e541ec050b7ef30229f0e9733e7b6590d5dd997391d270a7f336bce8999b2ce65380138f0d0b56be752f9bbacad0311abe0a75877f3a547a93bd22d0f1cefc373c86a3d5068dc4e5bc15b2b89b69ca9203e3a2d940077f13f8b071c8de8b3a67d2b4443206a0feb8d14d2bda245be39d349047dd601b1ea9dbbe6641c72d4b71635ac5f702d06901c780ac240899cede8182fdb829616644aa7a35fbb4988fdd35da10cc0061d0afbb5def90429cf4cfee8a211dd712a40699027888ab717a27b9c2286e0639ef81b1b99a2740d2632230c97939bf52c96532ee86015c7eff4b7d2c38020cc067195269501e53b120817e8b88cf71c75c9d6d2d07dfd24dfcf5827f28f40a7448648dd3f9cacdd5fad11438f666c674eb0f57964306cfab9f3546e63cec0a91bbd307a0bac79d31fa69dfc3c29a86c1153668971e62e2be63965027a2780a76682489baf222b62b7323b6c04d49836a96403c02411b6f56717bff690eea0fc3589bb424a92647d27a554d53733375088da6e31541aad643e48ff18d06a004a7ccd97f0079b4042f404ce2bcf1032b619908fa145708f49184659b07437803f6f6cbe2900966fa9414d018027ab3bf3f295182d575f2fdbe26457612f30508c01e148b6a7208145dab2b210b95d9673d5f336f9f519c861fbeab6bdd9164059021481a9d25916716a4cee507a163a58149aa3ccf86d37df446741d6249960c12fedaf8be63d95126975f029574c9e85fcc1b52f24b791fb320147d4f65fd0f794fd60b00d9307b00c709c776dc4ee8fd0a3d56715a44c471b1f9080cec22017ece8fd5c5b1bca53d5eb026550061ff6dcdcf7eec212a0d3ed03edd9a55df02ed103ea65e364dbb53bf3a6db2acc83529179691a847998af3b2e58b2ee2c30b458328a3e207291cb1f972caa8a14bb0877df969f8b5c5c86762ba34d8b3010ac5a0b169ff74b4a20516d793bdd5b5342b8950bc6e720acf7e031de7f7da9b050b148b088daa0310f88b1416f843ae2626ef53c9f87d79c1761c1d5f29d51908fc8e219c26b2f3733d124d0664db7ecc0a497f971e60107860bfbfe9c32c3103e22479b0c55ba8c4018f90917959f5251da1484efa6add2e0f2a45ce81e5e801628544056d49ab838c999ef4501dbfecd767602cc4dbab8e526dd22923620503d44758c78d7111b745bbab5dbb74f226f28512cb31ed020719d4b7ba22002609b05af1d31f6725fb953ac5b1161d21719305c86f00e8f42a6dd3091ba5c5ab0e5694bea4a1aeb1aab9b3f0f4b48f22340a862ab11bc6962b33baffd7f342db0464aea79d4a09731dc45c822bafb2a2547db988dae6931cc3bfa0b0356df1f5002825e17b90527767a67a160cc0d142c239d8b1f3ad55a0e942d3db08ae941a0c8585ba381126fe7a538b5acf380671e3a3a7e05ce5f85e97f0d136e97a88240696e9401409840f914ee5628970b904ed9549520ea8e0b9b60d16e9324b01e20456145d0fd04dd5d723a011538d5fa65407bbb3e79e020930f4b0ae13ceee0f03cb0fe8550326223affa4cbf4f3e0ae3a96d43c9918893b53992b0fed6399b1096f7287384659cfd5f82ee7c7767448d67c68a571bba9f966177e0f60b8471f06695fd89f494f4122fde39c73381b315522ea2b01bcf2881eaabc45aebe3edb06dcd4d9bfd9d7164b7d3440bf9fd4c1f70220d3215a194aef7ebcaf848ed2a60bec9d3bad84f467f947760fc1f6b93c69d342a8ba80b194fe1e8bc9268a48130d31cc7bac83cacaeff8223693a85b540ee392c5228c0cd1ccbfed3932dfcb980a0aa8a5aa962b865cf95776aed6bfa4a796c75c595b7d823d86b030a345e2b80ebed6bdd6e644d00ddaa5a730d055e3beae64e055e43921a9e9b723377c195004d5c59aaadc2c8d297c4c2784bd108482f7b2bdb483ce330aad8c6cf08681ac030456becc41b95671bf882a598a90730be06f7bc302daef8125e6b147581bd20dd435394b19e01a75a0153dd3ea02ad5e9b887c4022e07a2d45ac205043def60242bdfdcaf6c3259002887c9365c1ec6601849ac681da08d3cd11c0800df57805c687c39352d14de8c1977e8a5fe3d63e45cbe540b95e079b75a105facc16bf0d0233a26e2bbdcff74f5dbffce5434c8bbd05f37b72e6bd155cd0f39237ebac0bb149b10913b170f4207b683c599dc4a043a4d2b0a567585089a4150093373c0de8dba5d49ab70e1c24dc3aec2d3fc2a10a1b2c6d2bfdd40b8f883a071ad1ac0e3835e7fab7b74a1434ae4489031e6d5131cc6a7cdfbb83d5f7d6022994727f09237e4757068c0ca7256eee05416839e15d89f561f0d2476182c3ce7ddc053200cc23aba761f9626b0cc60f23432104e8860938c6383af135a361f7d726ceb201124e4a8aa2b26bfffcedca9c0c99cb69fd2563ab02f541e5bebc89cc59d9bb07212cfe42941d4a5e4dcebb4f84522fc243062fea19a9da4995151d14818aa80f70bc78770e088dbb407ef8d46e98e99389aa0597d0616eda53d362cbf8daf50a93a64ea0d755b377427861ba41951ce0aa09024cc11e0e19d96a9b0e6f603707837f1c1e0f9e1bf1fe3465ee444e0092b2bcd3271d13fbcc96bad84424b6b20949c38533c260bf0b2a330a1dc6c1948f1ea1067c6e744140681df4ccd975ab0a23b4b38f4bd04a82364aa6d8de29c386c2d822824d297cb2a28d1485e3aaaf060a9403013644c9c1a0eec05531e4c33b78b9da12db24f9d64e6788976dd52d01ab65dc3c5f90fa8b592ff7aa666f4ed11175a31cd2e88f54f864bda0bcdb2d02dc445b9a6feab0060c74b2c94132194adc8346c40936709715de90258d1ef700eb15f3dccd89e75663c66ef25ee820484dca7f673f5db0da2b9ac3506b89900743a554ead2d9b1c0c8becdca0e358a2e7795e48112dd58812a7053570c655400e2a45dc1292889fcd927e0933b9b47e7570f858f2f24f7bef23f01d23d03f60cf415e11366b85f67213e7aa75397404925035da2b0cc47fe517aa79357355103795746faefa3d9aef6b77a3d1bcee5720e82838fae06f920fcf9833fec61cf0659e58c687a622a334a1ea566ab622fc8e54f9cde665d2a3fb6a5b87198dba6055d6727cf57c2d22bce17824186d0d5f0528f7ffea7e8a616afb3c3eeee24420ddc5a68b33a81dd55e54401534f8197d2a5be48ca519efb146e00d2881014540bac93fe6bb4db3328848d835e7d9fbe59ffd9cd83370011827e95df16615bdc0f229fcdcda0254709b53d99ef191490e096a6caf21572d506a8a5bab9c4146f018ea35a53fa0cf551127ccb94313d8bc7e4f125f9bdfcefb6f6d0e16dcd67df0dd2b9c4713838f6eb6f02ab2d1b3dd4a529e6a0875b428d69d7124d2fd35c2608037d6a3ed8b3f4754681a9b7a13f98303c860216b4846a506d29c0aa5713d90a05238894db38d0d36f207d26ef6ea33db7095b0f1355415c158fa90a473982051bcd6918366d9eef008ab867969716679faab84bdae9c128800e3d4a0357db0e7325ba0697c2a11d783962f18e85d69ca75eb2fa7850d5530127f08c9dc8cc01e59ce255bac069bcf67b9ab465e34da50c50260b7a5e736682611b45d48b090407500668b2f55180abe53f7db2a642bf4e9a2c8e8966b7af18709ac0a6917604ef935dbd2c9dba802803d8e5fb8ef0dedc22ecf2eee544f4507d4bfead9cf80b0d94da552694e42def6e77b59613994050eec8d45d205a9115b16042f1f72e046a1aaf087000fa1a6b0bfe59cc68f8d5aee66004b63b694d137deb6ca7051b0c86b357615181415d88c00fdc14e182cb90bc969f2c7428ee8175b6096939ca05a0565188cf86e48112dc76a9cdaca1e4527f756db1a423a81b83cc4dae769c0312b745762a3a4a2017ee238fbd7e738b3bed513343ebb53500651e5653ff1e0970c8697b7982f6aded6f7d2e157d88772e462e2af10cc63b9a07a820050a6e0f33add61162789dba8b34e0be6697c017a62c774e6e4c5c5b03fbf2c540c4f70c0b1d668e777e26173a5751e5184b951b42fd80221be2bab575b630e4e77c3c0700e643f6d96de361ed9b3319566dc339a321fcf605aead42fef7928357d77a02ccc4033254feb67b33611f4e372ef05beaabe30563c410d59136a67dccb4700c212b7457b302fe5c99e5daa31583a26e6923a03c59e19f95bf34f8ad463cfb0b9a8a8b2ce901141ad4b97a77bb575172730e1d8a45b1c02fe8b531c682de5308a11311fe43549007fdf5c744ece14472ee6c59abb32bf279cd533cba40db1d0c73087cf3f5eb52c2744d0424f2e900fdf837fb6af9a499ac7d401b07120d960bbeb1e52a4f9dde23cab5f4d7b91cfe02bc889ad4d63034425bc489d8b849be0188808d56f5de5fddf7bc689c312d0757fbe06eb00200dead944a5be8a13bf0083cd1f25162fc6c7d054ac8b356d1871fa0d72d310773eb9ca112bad4cbadb30d1720f8fcc45357d5488b499f3bcb609db9159d09e80aca0e9eff081d2b08d00000eef91e7dac389ec589e7c6d5d289eb6c45c7289442e0f528c1fa7f58a7b40b538bea9ee89873a8265ae61ec974abef7900b200e7d0038729ec0e013fff7f07cd240b35535e76b5ceaabbae8a77bda80185ab2fa4a452ea0a8508080ef4bc040799298902d6333ee3fa07fb4cd01fac545be2c1dc236e8949f197e7588b9f01812c27ecc9d266ed7ddefed0d70e66ad3c73e03f366b4994acd030c13be2c005c3398f47def0fd3149b523b5103e295e84170962fe21a76e88f878a24af4870d2e5cdf1cf3521af9a8ab6f24881da4e26b9446aeb716acae04d851390c5be2012a63304259db023487e5f753fd8b1ef1496dc5ad73889e7018d6fc99c3992b0d15a61c5d971e4e18d55260da7a617bc1ea22af47386106597057ee56441fda08fb11a71ddcbd77af4c30c02890212483144762d77d680faa8294e8b4122b98018bcbadfb0e291c997ab0b4d8ff60d2d9a6b57832cfc49bcfbe40e22af74e5b085e367770f7a7458f226e1ec7826ef6fc40c893ac9c70f4232474a7831c20360821f3b481d4d98458827b8be5d8be0d016a3b852de7880dcf2d63741036e0590b1f244e16eb2075ac64e2472ae3ec97fce7ebd788de8bc487f88816f09732f00bf685957f1deecb6674ac89992ec7df3c371af65295664ecd52a569683c66400574190c3b224346c29841e0e2a8ae8ee8881a118cd39fcf2f7a97079047af2807279e0e75864e3c09e690f1b170a8de69c23fc3861674ffd3adc18da992486500b5684ca228f086b89b913de885eccbcea8434c5c6ab669bba169d88742dc000861d6da1e6e79d7399410adf3dd53944d68ca8ebc68454040220f92ad6dd89804b9ece70ead929507104e7a6666fbbea2377421f5380c2fb1b530a54199c5880c59600868e95f7a91bf51560019778c47f47dd8d20f9de541409f4e0584445303cc5884ed9d30a11f7e947ceb81b3384207ac5409c1e7ec2f12ffc3649f10480d21a87f39ea33112d88ed1b58d1e3a38ec14466792b1c86da0099ffb0e7f9d9027165415131083ab39a69170439c636b6bfbd93e9f4b533c1e8181b8f1baf6608306e76b96d57f2db60646fcd8e082e92578934d7c197cb0e7e270de1deb6270bbb53c008f4c2d1704d71914117c11aa7aa371edcfaa0d99824aa3a738f51ed0832ea438e3a7b1203de70aa2fbd23b97582a25ba913e677a5be74837a2e39a50588c65b0e0d10a4fdb4acde80ccf9f58a57cc4768c0a6659c8e131293d9ff9c001815db6ad3f53e43dd89b1157fa1e23ccac975136612996c9fe29c8a2d8d080dd39e8fb5ae21941aa749598fb5165dc95a1f5adba33ab6e9d872f4bc81ea2c03694ef9807bd59f38a076eb55a3b444ed8ddd40c64287c3459868ba65f79fb50ae5dd5f4534a07434aebe6033a2742e0316f3ea9295feeda31d949ded4a67e6045e4044e989b32e3a10c5e42f202b27ef1eef7c78b43391f22ede0225e4cd01010d5dfabbfed9bfe334a5cb8e538d6083aaeb46f03ad41d0649df64ee01ff3408ddd575d116be9f30bcd4668ed10888904866de70ed5e71c04b777420ff557a0356ef1e18471eb272323eb19c3b6bea17a098e437d7c8678aeed88acf15571906274b308932162c9760ffe1417a2f209d709354734ea4bfd68183c67431e4b600cf9a1bd583921821c0815f4acb769561b3d706bdd09811c7a6cccb5bb292e205026b3cd97787605d2fed4fef3eaafb20380e43b39e255857c0ae509b18462603c67f433c5f5b309a5181e0599db7680830bb82f7cbe310a7b81ec40b481c4e01532d572e1b93f155ab1ce4be26674ad469b6d3b9c4dde2c6c018bbbc7ca681074a06177f04f3db12bd0f8b3f881f97934f0b5e792afff5239b769bbfacca670dbf883cafae95e566ddf03a60b13f0e30f572e981ac3e003d7267d8f3b2131d0bc5e8650e9bf1c97366f033a9dc72ed9852e111324b876779672cc2ce602c320644f01285d64ed3df948e144f27c911387a0ba5af79c67a51d3f643e1920d6a0d0569bd4603c076ee9f7778bce7929f85ff07238c9cbe7bf66df26ae296d8f90373fc3ff48ec5aa6459ef71d544e8a378f3b22561206b1d4dc6b70b2f7a9c3305b2fb3d923081dcad17aae15a7b774b8c424dc301a986d648b9607ed3fb2afb022e79ab67b39b4025d380be538db3a318f56aa9d7582a211d332e3b73a118a0040421dcf858c5f8aeb3c86f6efeabe98ecbc7610a2933d331b767964e64df80096922385713bd0f74937dd335508d1fa9e3ff97a98536fcb616a7ae3d609f5c09b0861a3ff8118de21c074a584443099d1f177d9c2fb119ccebf1f44ce533370a64390718e0a8fcfc8ea6445179c41ac026f5407d3a59367545db3ed83087e00864a51cf6d953271576962176839205f0b580c6c8708002aead9df65f4d6308085cc808f15848acf36e93b333390b9d45a14892a80b2e120acdaca0c1ff40b104a0dd29f9f70d418c3faf091fae838d261c2a96966d236d16c966836b3750750e12f8ffe1e3a4c4a5712d346adc5f1566aecaf5e0fd47e339d01dcfa6a593bd02831273f0ef6cdb43234dc0c5887eb06a5d026d63e1f7e3c895ebc5b9e1b3a204967a53616c40688479b31eeeee8f67552f3f622563c0bfd4ae4cc8a88affe307346df2ec51f1af54d6d5b596b3820d35916dc5f6512f1455d05479a8afedcb081380dfe41b569491d75673d08dde0649c4e422e6ab31a61cd3fe4b345a848f09717be91da0aec9b03074d4d4b2b0909365def5a3b45e0554a22c0932678b410f47079b6149334209a51bf99af422829d768e480b9859b05c3ca367c5c7d3f3076853d02230da533dfa3a81666c4f713dee7a77e5183a79eb859326a5ef6edf06f5f89cc926ddfff8c4108b9e049bef2ca74d585f233c41a6d41558b4e31b770c39ed285cfa85e7636e9bb4cddf2e23f349ebe276548900e923efbc9783876e04ddf597dd1707bc8fb6c7c3bd4b1936f7cee69aa57913e120be740a8fcd10a20553c78d4b396aed09c2f311f7b3c44d016d1bd72d94d1c3183e903cbf3e66c30782173294237ab12af1fb8532d4e1d9dea46a8c0b6fb478889dfa98f8972ccf09e23ca871c38ccd22096d876bb3a2c11f3cb11cc8cb237c1cac5dd3cf34038e039bbc3c52d0479e8234e987decc95eb69f126d9b7e5f01f35b5c8c7c639f9f60df9acf153fb00aec145b1ac99756d949ae7d9026248d2a156f6a33c127ee3770542ba202d71ab77b2a0e36074aa0bbf49af0577688dc56fad555a877f4f016c0ce3335e7f51eac496b30097e6e7e6fcf6c7b813797ac288bc3b2bfb40fbecd107e18d4f9adc6d8832d5eeca7db7be0884a6d882b6d57d11bd6d575776ae5617080a1b231840e3a3dd7fccada5734c0fdf7b62bc562b057b3e479cc6b4ded1bf05eb72c60abef6145e7d14e78c4ac4f798aa338da59ea4aec51f59cc35a290dd0b271a146185f88b0fc5f76428a1ead8fa49183bdd528529ca6fcb481730c91e0444d7783fbcc55394b41dcb21e4fd0c0781a70f98e7403fceea99102fca1a430552429cf00335e1bdf864cbb1006c0af23c60142959936161623daf08576fa302b0e673623298f396cbd5b3de7cdc94bd2e67a9e225402e0ae5b3282c5b6f1e0b0ad118285eab1975a47bcdd48695e51f140a2ac1461bf15596f816ccfb059e02a5bf5b7bd69475b38561a2afe95521d4efdc178a6bbcf8338b58b9787e86860ba3e0718edfd40960bf82c58308b7965d03c8bf0997e6ee58f0af11f4a0e45f05d84b42b49e15767d72e2ff45be2a5aea25df730bb45823c453d884dcd6a1580570b240dea8ed05ef2b212cc7481697d5eff703622dd1c8942481b30d42b337035169387b9157f051825209998a3e321799146d4533c3592bacd209d9dd999a0dfdd2103296a03ed5bb41064ce16ec884ef320b1293531f97dc6447cebcbeba0bc3f8091b46a562198f10fa26ecd19ff49fe100c934d47e945f69888a33abee002650a743affb76b233582d40693c9d97ac5a81394001e38d47dce30276b73109a575d65b02a6e91d69e3afc1a0bfa6493ac9d932d6c7b932428c3e33acf8570b7a347874268c1046f8e451267f400804b8c65f8dc0b3224d7bb460b0fe858306e1d48de285fe7ff5b70aad383621a11661d43eee8d41cb475e471bbdce91b606a61815dc06bbbe844e0ec03f144868b1d3e4e9a2fc45bb174572521766775d0829f20c1e2653b2e64c37b5ae8c4abaf404a79460c38054e25ce4c1b1148cb20827155a9668403e8acca7865e3107245a3dbf4bc8a9c9776b0292964be42278033065f000c8a39334c2932f0cdbe7c31b880a8fe87e0eec5ed443389c77248002b9f0100a91394eeaad61ceea31733a21904e352bd76c55e0bc8ddf8b7121b90f21f400ee3f78e1ea857672ba803b04985c02f8e21d5f9b4903f45fa76afe510f716df7d6e73a95697665e4a11dd55dc0ffaf3a60e13fb1c9e8ccc0bb2fb87c0c3c59f8b92be4dc5df5d5c817ad1272034b9605805fd11c34252c6fd48605ef00023609a3dd4e02e567c8ce3839122bd0a7b426a49961a49606e6466b96596002a3b7ee534204128fb6a782f59f5c954db7d870d4d1a89b7489dc9c716fcf170c4de256d1cc586d94106ab261cbd5ab403b6e6dc56804073d9d1b9f858493bf099aaf3ced6ed7d5de2a405a08957eaa20554835e11735f87b8a6e15421009230ec63e1155db53d279159e7a8964cd4634ef4559ff4e85ef3db7e6706b79b18200a31ad4606997eec83b6f369c568f84bcb0edff8d4f0ab7d5a7e9d493016cb90e82ab1541861961b00e22e81d497a12744106ac21ad801ddeb72b52d53ff0ae0e021e0d9ca68636f79fe4ccc4619f66c82d09075df44c60ad7159479867062901d676a3bf228408097a7987fa695b8cb789929c37f296a2356da719b598ad090f45d7b42c396a85735c492ccbc224077ea68ccdd14d53614e11313e03e1784d019b57eb41fa6be67c761ef614ca5ba937a033af268be8ed6965d60c1a2daab408db11cb7fdeff6442ed0d59a37f05e56aa58171974855f4bbc826e0f9c8d8770c3a5e8d0fc144f6a0c3ecf843d034da8cc04523f10df7b69036e7109171fdb301e2bedb5aa2aa88ebefc134ac3a7db07f375745ced7977b84305d777bea353700051f522e0200d639aeff598c8540ae5b597dd4ff5cd1327cb7b11f17dbd9b9049424c5945bb62dd457346967f375575fe4c3c455431f8a5905d8a30348cef2043bc3ed7f44f77191e0ec6ec996a764ef966103951188e0dc76b67bf3c540d70129eb889f77328b03297222ff7b7624b0ca64ba4179aec81ec2aa0eb9e0756808e2aeb0694217718da10460a14152a6611969112118c09421d5b8d4d7977c870b135f33c84a4e73e177928be95008f494a0946f63c037735fd6840dccf2d9630a804333f7f745f325579306dac4889b8bf0f19afac0b3dd4e55324a859e9233040dc58f46bbb70a25e302ea7535eb081722c9ef2e016622b2817ba3704e42b309d222c5d95d1aa043dd73c9b320f2021fa32baa80f94cca283e02056cb1b16f008713e392aaf47b0203bb47c67299531851d57eade0007c8657e187a1d8627f0f61eeb9cbfab059cd13e700b369fa74770ddc698383b1fbe0399b270ec99f8c00a6d207f8d22d53a3ba85c8ceae534613fdd048695863867b9182012a638c3e06bfa9bcc58d85b62354310d99d55c539f18437f4c8b062297c542e13861166807447eaf63394e926db7537160995bd5906afa651984cc836640fb5846e3342902b2ed154b18e8905904960b8587de5a711e26f5db83b0165183b32be06d787502103f8c2368c59bd3341c770d5785538f15461c2beb0a2c95082e166b2d480f0a87fbdbb3ccb204d39ab827462f136787ba28063b2cf566f7b23abab46cd1480ec8f0855e83f17b2f95f6aea274cc8fe571257aee700bac53d60a24ef78a3bd0d863a9b930a65dda5199dca85f2ab1fd22d615c97f525cd944262609606920b05c97cf2877175bd42975f1f62abe4b7dab784913f203770cd7bb13f2608ef010e1512794a28c8e33a2eed62dfa3a038cdc3be474b28f8edadd2ff23150e1d4a027081518717f870fcf5efccdf03a5914c5b54a070a9d91ced096560f87c7330052bef43893f1e2a9dc3dead07280c997dce97636ac9718af8d0153fe6a8ad460c06ec58ea0101f34dc9d33e5cc1566e0fd2f96b405eca800911d4a29fea25b80556bf0400cee40336e1032da2b130c45d7bcd0f7e31ae746c7b84bc9e9d1ac8097154ebb85844ed810599c51a811dbb76a6c63c706c3fe2ecd3ebd9acfb5d9707ff750f4ed20ff8b41d265030324c27baaec89003ec2447d20fa6d862e516a90e623c6f543362c163943d982771621311ea3d6ec06245fee9903907975f77d30711e38e1a73fb5426031965bfae3381e8c04f77ca7250316f1b9e77392829b101b1d7adf0b9e5d948c73ce0af90c9932bf9d1b5368890f09a2f36f7be3afbef078472a026466ab595132bcfe61400a05a273b3359f8005208a7b421f2e3f72301a1667d342336445755864ebc096d8fe268c0b92728c564079b7cc92b52e4f00d6c27af3156578a68d6b4e171b41da15d86a64133e9d3265021e3a1de1303c60f87d65b3a5e9fcc46f08ab82f1f3f11a199139ea24591bb09572fdccdb1495308a78da9dff1108bd8114b7c3026d1a6c61f04054342ad1c43b8a6a79762acf100901b9c18bc769c31ebbd39a06231370e518d9fe730bb1bc68308b9f3f88b990fecc8a0c4df5dcac9170da75937c37d4d330df47bcfee7939746f06ebf665fc02b351422b71a5d813f1d8343ea5cbc3e31fa28b887078e7e2ee6a03f195e4fb0e1e7ccc42b1aa2ed450a73bca657349cc68ed819f90e47fa52d95e53ff48cf40190ddd5fe1686b57fdd6dfaacffaf6c005384b3504152fe6e9a1b3ad1eb43c6060fca8114e788d3ab9cdf21e1d7421e534eff639f11d30be52be978df5107370cf8c68e0b89eb5f460e36181704ca04fc03f32578915d279de22e9ebcad7db60336e071042fcc03cd3f194b8c42c0b34b8df17c75cfaa2d2b8d99d1af7207360d87754dd42ec195b567afce6ce95cb8c2086f1cad7279c980ce0ab5b1b4d8c00bcb8f91f55a103b6df9dca3173323293d1a21f652ee0f6eda79f3b6f515eed50b59b511d6306919901dc982d8d84ada53109ebba1481c6958bc467e07fa131c06daac2fd7a521f15612a74b45309e8c481c1e1990efcbb43d25a3d12c225ff40f484304ad31cd776ee4386e91977b14af39426093afda4c04771bd41c85eb4d01febab59327136b6a6f895621c35dd51df657b286dcc4fdde96051a0d610c710e0c81ce07dd5eb1c1ed5f09bcf9228e265448551b97f938ab207ac2e2ebc68c0c1d3870d487e28a85042e6e05708dbff9d06be360b41de13d890792867d431f0a287844a953f330e6870b057b989a5adde64c07141bb1c3b8565b479003712d0d0fa585c2edc6a37639e8137eac47a375d84198f27e5262de650ea3ab323a7b0d158692babb5ccbda978eabf2a597e77c872664f3f1253292f1d46b71838fba06832a060cfc027392a0b8dd55b21f9482998ce453bf8b0828d3efab25d331950f36c0ae33a5bc81d68f4580887dd90aaa80ffb265a60cf9e4afa8bb338390a508e34edb6524dc5466386f14f93f6e15914dd5fd3698b80ce540123f9a69d1960a9e5cbef176a2db35c4d7c8d380e232d4b020747911315acf8ee13ee8c4a2390ad9b7be54ccb986930c77ff64f0f6a249fceb03680ca91be584817bcf5096450b10b430ebe9388a09086c94b6ae8e57b498aca9803309532453ba57329419970b8144f4ee0cd4a1847a7183792ba235a813dab01910f7a4e41c7e4f09a6f342071748d51899b80c85da83e7d55bd9cdb957700471f21f947b5ed44fa45822050698f5f5707fd71eedcea771883bdac60c17e1794de9558cdd4da4197c51cce50f709588d14c48ea910de2680709f1be9956617c39f339909144e5ccfa4baa660b3a1cd69fc734112283ff566bd7481dc5c557697023db225c8c5b0ad44a4396027340d283c74d3f634d0b48e27f5f8a1ce0e974cfa40ca8eda0e55f593690b603617fe51ee5b69cfabe3229dcb6ee573ffa4a8482d5cb2c7db3c811ac3d75f80714717e320c22fe741fb4628cf746b46620d7db8b1d88647358d8739feff04e0adefd2e151250c152437966b1fd16866844101f52d8661347efde33ab4ef19305c34d7497ff5e0ed27eb5d3db1ecb8d33ee6d6a929086c9ca4d43fa44e6a5d001fc22a42f5ac3850f1c1e10b4adc9f2f51814f6cb0e066648202666e77440fd050f1b3cb8387c69323b15b9aaccff4554ac598b7c0a3bb94b28837861a33cd70d809b4be2ef381d0f6ac80856a4d90bfb8f5ff9f6c164bbc1f8a537ad0ee2060eff828751639cd0de0442115de1bfb77e9aea4cf3686b96cd63751003e809df0c33a24597d44bbb4280d119f027321e1a78d536bda502ac9e0d3f99e45fd14e061993bb8ab1879349e5b9606d5da91ae1206d9cc5bb81ee963dc49165ef47be0453cca77587d7928955964be477dbf8b68a9e5fffa0d99c4227f4c59b070a01089a5aafb1e0acf113d7b19d97c3d06b1bf596859abc33e2ddfd54b872e4ce430437d7266f021ac919df88ac558954e53c07577f0984e46a7e827a70dfb11d900da08a881e231d903d3a287cd177993fd2b29a86d502914fa363547fa07b86020e0131efdd5d913163d38e8efb58968e2aa6cf3ca56539066c051975e108d2ab04472b2a532757f2f6ffcfa49e078de783cddae074c1af2822bc709cdf7d34d101189af3948dad00be2e0550453152289b81ce495901a6f31b5fb4a6a28c05360fb025986161dbc1383d1940e699b6f1a52051c42a73fbefe510fa4a82ce0d8d06d91fc397d464970fc91494fd03d1c4cb3ba134a9ad7860f6f4d80d66539c1108b936ad2c837f6a68b1f4329fddf5c684533242fd8e90c24321a4b2b835b8d602 false -check_ring_signature 69c5135ccc22328c45d9c12761348ed017d47d72b4eeafff9a7b8dca13630a54 d62f14c4ebae1f988d5d8b1567153ef33595bb9ed145b31e84f9ae60012ed6a8 56 72c6e20087c78b2997bf5f8ee9cd7b270e3705900c88b3a3793422c75b21fdde 8c8ff80e8485cb50fd67c2169116cf036409e3ef8cb52c92a7aeed2672fc50da f12038ed549a55c651e36c78a9c6bb7c5d2535445c5dc3cf475bdb0ce38e4af3 9a65ddbe5cd8a07f0548518fad7780a9fa719edb04801a40b13889748710d166 d6140c27318c140801df3907537d741ee036a143e2f97830febe31a052f20592 337beb0c7e386deab38e0dd0c604dd3472863b312c70a2d6245a830a6ec73773 a4b10b0274107f0facd711e2fb1666948bcd2fc0e563e6c0908680ee7faf1ff7 8e28b6ca9820a065ced2c72dcf0792988e469eca9d43f9c48c9a83e6644a5d61 358815fa94a8070170491d033ef579c2363607fdac2a659057ec83ea394651a4 3c452ee7479bb697914d9b4af7fa757eb884aa3ffc854d3911bac01fd66cbeb8 652a53b218f16c478867c5df964e1670e3add0733f4ab49bf42b830e1563a83c f225dbaba5f8134c9d08460c57d309dfe048a26674ebc942a0265e749c7a9105 5b1225b39605b97ec710789ac8a571da815e7d0bf1e5da425514a0fbcde68fcf 181c390024197def56113aec79b8a3e0db55d0e6d13e4e08c62e13573fd1ed66 5223700f5ca03f2bb50c9c5c788a9d67790db8ec88a2b0e58412315c31292dfc ac2fed5973fa821a69dd921dbe05c31850e37407edc2580f7de4ee6922777ddc 63c16882c0ae6c9c889f9d1cdffc5cc0003a29215c29f12f83783a8c2d066a8a b91beb1b0b7d9e293cf785329f80a0f86688da6eb88b3f4db349b58c64342a2e 58cee745e70058fadac37cfcb81a096c41fd77f7024e8a2c0907fae2abfed6bf 120d682e24349502b8c2f023f806e970834dfb33ffd1ca9bd688bb332ed76f89 fbac3ae10b2da60192ce11bf253f6ec75a7ccf2bc518b5aeb36c1917aed82ade 2128d767c930586d54ca1d58b9a58d42ba54e090f508882eb0731f0816994e7f 467d0b0cc71526d235c32629cc26538119e29fb0a60a359b907bcb77090e24ae dd60155cd0b047810be3ff7526b948330315644e09ea19b07ae073475789b9c6 2feebeb67c6aceb15cba1c06e9425fa419a4b1338aa5d9dc2a9044c39a78520a ceebbbb657cc8a3fce4638a20b716f1c7d78493d1d61ebbb8353393661488003 8a23a61d565ddbaaf8ee2e30f8df4be29a3bf8f1c1678682c793b8c4d06a0f7d 7aeeec7e44135c2a449707e184df5356a5ca9bba7ff3963fbd8172c8cbaa3514 6ff5cfc1f02eb3c17dea4ce5634055ed061d60515bf46994d8f604e1ec78db0e 139d72abe6c139d46feee000b642a809a722af06e0281e68e506e2c7e2da810d ac49368f91c074363a605e0b61c967dc7eebe5eca6d099ec64c260c09174e3b1 1a4f0b6d1e6e776ef23e49209cc43cae6f9e3988989eccb35d67e103454f8144 1b4006b16939cd1bc5013aecc49c193f56880b460d2b3051f8957376e93e2b85 7e9354d91cff1b894c2fdeee1117f1fe13873ea55d03f7a7bcfa88dcf222c391 bdf6ce303c9e02460bb0fef9b4224d3d1194a131473613c1117a3a3b8831f0d1 a81a135ffc7e58b70844932cbf7cef7d181a9221040ae29808a910e6eb921368 a9b6ce7bddaf8cb268ce0f2544970045148c5f89f51dec609aafc9b26d8e4a21 7af87d4949ed129f7e9d258a12c4b56313966440129559c7be3925d1b547ef29 feea7cec82c998b99bc091967501d7680ee9e19fe5fe30fce0f87a6c34bf964e 48d6f37f72d9e9adff4b0f19f5b263ead908e9642013a6d127b3c4238fc9fbe5 0d25f5bff24398a169f9f98cf8ab6febe0acf72ea4cd6617fcbef1aaf614a8d7 debfedbc758ef510e35437f942fc34a04d6f39bb366a0686d6e98ce2bdfbc94f 8c729e4c0c96ffcb17136d44f557c96d9fb9292ac43059b159f7c69b9e035483 f95c19776446509cddade64ca1f8e4618a308782774708ee276de03d46fa461e 63b9070ae5799503fa14c58a104c955d4e3b77bfa5d50de198c913349e139e01 8e8feab7dbf582b9e6b47a8744a2be8030f582cbc9dd87966abe737cf53bfe4c 8f2661f6a9d934f5057af05ddfa7af38b37932b256a1552ab5a9e64ace1ff348 9ed9e51660a1926f28647774cf15e736cee2ca49b7976339d491447767a7427b ddf004a59becb291a3cfc74e678014f6aa136f8a857d6e765e9c2646d4ebbe83 9a3e808f57347a2a4f23d1f4bd9f4935aa770e2c863a25920ae0ef3f006fc8f5 d1e1be2d3eb645f61532e762934e99ba150ec78f69678090e7c9c4ca09951378 b87c97c6de7831f1c11c4b3d9d6aca17ce853a8ca141fd12f4d064969dd10951 9996d7f5fc1a83165456ed98d4998f300578ec07727538507f8696705cfb3019 6b68cffc2d4f5c178a97c7e343b606584c5313f2d5aba38b68ac6ca8bc013c63 3b705ad4fd51070c906c12efce4b9a05844071a9bdd27998d188eaf1db948f73 6f6b81bb167ea703b71be4b011022cc2e5358a822ebe84b82593a143c835ad22 170fe2cc08491312794737d1f9625caeba9a77d8e537319885cc1c096c370a03506f1a2670750696b4100dbd9eb76554d1b35892859e34e28d2608beeef1370d68888573e11d1e0e6eb846ca816cf46d17e6f9cbd24c89da09a82390acdcde0f41ae2e9a9496b4a8dfa89d1c0790fdc6a3a782009a0c01afca91e47746655503fd0df078c189c646e911debc734b62484e59e75627f2efac53e59c42e1699605110737ba6420abba3324a7ffd74c8a5f936c587e73feae87a9c71c6874ec0b002812fcc5eeea834badd242068f5c6f5d02758ac1dbc1476b1fa7c1702309950742ffd9fdd1dd741f7371664579a939a1b30b857bb748fe4308d42491f4618d0717bb9a1fd40f1a6bc24b84d52062534c4d02f44fc2a4b011cbd30a0389c2380d4bba36162d6ec426044929e82936fe37839cce6d94ba86ba13ed6d8b866b3a01b3b54a9a13892b06c9388984baa8894d768963181e0006753069caa0057427043c5d150ffd041c85e1a1b92a3c0ab79b5b8a9f8f54c39c244ec323784ef76e01a8838f5334928fe0f5240a05d66a34989f1f1b204a4ab44f73a183cbc1ea7f0e04b700830cb0f101fed03c5c57d314741b9020971c7e794aa3223d858000fd0adfe7f6dfea687aa8440ceed3e13007876c915a5201981952e2618a0e22f4a5083e3165aa964aac24654d36b23321f520a7b86d8787be338922803be1e17afe049192359ada0bab8e494fbb988b79dfd14eb32be8876f5b74d7fe41d897d30901e7b5a8804024e7039ff7f66aae1fbb5b56fcb50a1172f604b61446780491880029a36349a9209dc60944559474fe00ef70778a06dff01cfef7c1088a0cce230904aa84034dc34808fc991612965d12f28840f2a27fea3838b2aafbb0f48a5d0503011c00981a67b63c57d5d9b64acb88fab6865adda0ce092ca7a6852ebc4e0e4703a4ceb55a1a1471df5f60ffae29f78c9b86da17def4f2dd96b001c2ce510fe405792703f79481fb5c544b8fe77ca252b80cd4f9a29b0e5543768222ba84083515bfee2966a1c880ac406fea550ce5225fbf6558da12f4b69ee51eaf16930be242081ebc9299b24a0a3af6a39f747f2b4b680d0a0a64a8855ee104455c3605a9c55bc35a19f01d7e341b6bd5f6e1b5329521f7b5e680a5effe40b27274090a61083142301b977ffe52f18ab72ec873ee4061a5198349829be05b21c8c16a0e9ae1b879af9971b97a0c290085f574a0bc21140889f156c50af0e3caceafae047be75d224a95cdaae0490a415cffafa8c872890c9b0e2c5347d9691123be4f0be26be9701d1fa5fde0578b2b2ccb7b2fbcf1ba891f2c854933bda53cdb9e5f0f529922a5dad99d118a1ce929a158a4deca21b774c0f0f2753c90a357b4d93f092e10ff1289ccfa21a70356d42882a826b9403c55611d0b133c994fb64f96560598c2f0e08a7cc9d621850d75780b33340153c57a77c8119c94adec61a8396f06159906a6c92fc8b5afdd9dc30ec8415fe9357ec5c97246e28216b3850d018f0740979ebf48585efa45341e21fe32b5fff6c4d842fcd4e266225adbf07356450918a747cb321906808df3604da727bfc437f62bf8c9b4f66a570be3b68fa7c00614e40c73ab7320747231a5c360e0ee2cfd76fb011ae96f89415b3e4beabaea0b449c64aff89a2da59adcb0372b2006b94979eddfd26d6b147af3334036f2b502d1aabcd8350317ced4235c1f41329cdafc41cecf91857f505f62d08359cfc00c125db3e82c60f1675cfab4b6caa9a2804bc371e3a874835265cae480bde495085f64043b791d98b20f30581caa3d69135a79a47ace815c10c83a801855b62c003d93fbf2aeb201d6550cd305a23a2d738bf36e9bf9d0c5741b886e3abfe69b080cab4e40bc75c10f60a878d8e53077b398cd4a9dcd6bdfc6f7a8c95b617c0f0d84cd615456d1cd29db380a81889bc552e8ff41ee494bf6fbdda3c8fe3c7e8c050cc87f185b04bb542a723cbf6eda1870aa24188756ca8fc48592a8c70cb9b00907381ec9210cbfdb1e750b66d738dd2635231451067e7fc843eb3029ac2f6407aef041e3a5744ceb9dfb6c6a52933a9330535c0b10173dfdd39d6456cc0e930f6751e14f04f06023a6f8ee1550cf0529923028295346fb6422fd4b79923356064a438348f02e1f41651b2ab78d1d4781c50a43d3edea377e1bb69c59d93197050b5860ee39798bdf890fecbe824f8ca31f66e3b49bfc21a44194cb804422fc00e94cf25d24126f77d36e17a44aea43a5a6118c03c9911c5e4744994f5bbe2003873433b0b1485915ec468c072577861a7bb755df75ae20a3540f90f84611cc08aa5785a61784ad4f230e21d1535339acc9216737e173dc1837f0a09f990eca0cd0f5350221420b81f37ba5ab8dbb464390856ba171a76a2b04d8692925fd9e06b9ba7fb4e8e2193a026f35ebcece0d9d454e77adc9a64a2e87f94b50424c3804b8099aad6eb9f40769b0093da5f5a32c6583b613e8b9f928793b495ee4fd6a0ec0ada75a3d1b6c5d605633024a989606dc747ceca53eea3d1c2422b231825a0bd84fab8242c8b23ff7e78ab01022b0a36d9f84258d646b5113d051048ad35e0bd25323e3cc52bc2c80700220727bcbaf592fbd5ee5c62d561811ccfca12b3505f54ffb40036602d38bb761bb1ffe281d2af4e473abd9022b239538b3c4f1fa04e60b797189210be29fc5cfbf864dc67b37124a8cc88498a066385c6fd68a0502af6e2f44806d05f407bf425ddceb71981ff8f21ed954c18706d7974ba918000215ec620b9113ffc78a2564cef20875f58c3b6458145f4c0a29f57dafc836d701fd96858526f951c99c316b7edbe96d551609c3881d35dc201ab009554e6dff08049e6b7f7703e893f1a1fe5af6ce839293535f4b5d4cdeb7c7bc328c12307a016773829791ecae643aa8235cdd14e703e755ff869cf31cb314ffedfef144900eac4e731807b8b9dbbf8693d106f462ca15b0fc5cec03b541a9239d202763a30b05413ca6226df41cb565e18b041708aff36eef7db39742e2e134850b2917b60b693e227205ad0b72592ee4ea18c6348142fcae2472438a1102f8f3fb8774c400696267f13bff67c5dd96d268bc1de2bf1387fcdf2c8f293a8d46a5ae23dec304e795650a603331487ff0548ed6b91cd37441d6c7d79ea1fabfd59a009f13a8018342fca0b06a6e697426b3d3f6ee96a74587057370a57f1b1cd3dc9850e501008633a4b688dfc771cb36592c951d7989c13e53db25e8fb6dea67c315eb6288045f145d89cc8ada9e2c6e173e725c515e9bd93caf48b86dac9015327c0d66040f0ac625ea13f87b23582fc0914d1e64742541b624966c7ccae18565e955b9cb0a19d6caade00971857124e94c55e65478f50ec4035047ccd843a9a0e974d9670ea8cc42bea976fdcd151412c39b9d3a023c1d08d8c33805b3bf10de0a72028104d3eac263071e81572276682de63eead4f08273c6783b2ac26e0ebdc08263660b8b57a79a7c96523757ff53b3face19ff56e595a97023cde7f0d0f74acd5e5b0d8d6f8b01042cf824d19cd6be3c76168b5cf4d90a3c9e5c897949fe8011401b0bae2dc768aad4d3efa959e0158ba6b6d3caa9e92b44960faeeead86692e780d00a76858fd2b5fcec40bdc6650e0bf485af1e97fc8a07698e04c86f9e39289080c22c76f915d922694c29a1e93e94d3ddd4c6693e7e8029df6e68385639f074104c5f5965d8736dfa31515f79fbe6d7073b866b42959c7750f149ee0356f0a9e0a9a6af3acf755f02ade801c064eb559628f5219e231a47a9706da9cee632d1d0cd252fa1c5decc5ee7ef407c5d3b4d503f69f3bf7227b15cd9c4f78546d771d00d640b457c9641ac8a3ea328e1350d90b6ee8208d384ab2c05a6ee1cc3268c907785ce90d3fa223cd2c02d46c89917bc53c7f182e6925a2f8a3d30a98dfa4450bc2cc00a9e58b6f9a0dda72e18357711415c006c75810572e47ad15a9239567091b7e0c1cd14c4ee1c86a6cc74ccd50a14520f6c7dc379af21e4ee1760f27b301defd75e77db7bc79e77a20be4f0b7f36bf2c5da9a56b8436facddc22b8fffd0e6c487e97b2c4f0a872552563db0b2f25093cb012aecadcd62c1779a20199430f8a47272d5157ac00db1915be28257c4614cc13ac69cc642b74bdf1f562673b05df4a9d57c54e74abc60aa4aa114ede6bf165f4f031bfe9d2296e5c231427e902e953c9d73c3605a403fa4419636e30f30e222cd6defd42be45dbd98c4131f30a37b588a6f5b32a41fe47849bd424a8191d601a466a1a57fdda58d2de328e350aaaeaaca8c75a6628605a5b66027d8822f07f6f59d630e153cacdcec7abc3f8018e25822e09bfa65a491126e37d5ca2d5ef6894c44d5f95ac3cb049445048a30d60754d0e1162f26d249c1818768aa6a71eec0226d92585be99cc097d2dbe9b05c7e2028b1a7eeae52edc07f20a23c099553e35edf7e0761fc076748d6f3f46058d54b090fe62eca92f48494b8b19c9eb8ae6f694e01aed3034a96b8bb7adf00db830bf85277c50c750d25b94db297acc3ab0851e990edd2266356054e0dc2b0f44a7e5498f8dfb469c0e8d68f856a1dffc2bbe64e1d5f24c9fb7e4ffeb2fb60e1c39956e8b2bcf732f989fc4fd76d07353c4aab2aacc530e8d0d3771514d980a8e7563c5e5a95362bdcc11dc2bac5064644b9552f7271c40e11f7d8e0157b306597da4f6b1d64b49b2818c9f2654a93e9124a77cb43b0428d31fdcc63ca40000486fc6dd197c6d566b96cfd2c46b5f52c8f3868a2280a012110f741fec560d0acf3c5f6bb129cb20c38673418164344b03d5c1d27e2371a33f3aa0d462bfc30202d8285c810cf3a3d13c623ab50d758335d4b4bc05ff6ca3587c2875c6fec20cca6e7e42cac14f9c31542e020601d099e705a98e7cb127c143eac67b93172f00e41ee698cf8bf4e7957578a72a0234988a8b02ef356e707829073b334b8ffb0ca361b30490368f93e77d8ab5856abc95fecf513b28f2ce6c9d8073196751e000 false -check_ring_signature 2c3da563ce7835e0175bd7edeccf241ad75fb7dc74dd321343a19b7114bb5dcd cb5a5442b75000f24bd7fba4445235ba335da1cd129b983cb38f8a994a98cb2c 8 bc3dd78755024b0baf7182df563017a315d362d76255278b5b2bd417e4d58c1b 571c5b81da370a82aee0c632f361f4d9e45637b08757d3e032ac074088fc56c2 71217c80aff69cd486aac07f0ac128283193daa5ea9abb6133fc9c72e144ffe1 0e98dbb2b05f1584721fe7b9164830d7c21141a2633a99f7afaaf3cc4fff760b 6e2947563b47560dc412da992c338e058dc2e4c6f6417e496fb97c24bf2795c9 a2549d69f5456aa43498a1eca55fb603e81e6b5788886499e8db40538ec6eba5 e021d811f9e6a2a7ca89680d441f17b651e874757bda4b792e41ad54ab5853cd a919b8e68a7b554c388a59ece89f72bf15aeb5a706449fc29d802c18178dfe21 db4ece5669f18d21bfc9aed38b7dbb8bb06976dc52fd3c7d4bb44333aa25be06cbd3005c71bc718bf8c9add40c2a61b99badf12adab80908fc053ed11a09450c856b2749ba2674bc4fc06e113e4674a8045737615519f088e32f8f9a43304e0b5bd20e823fb376726b0f1c44ec0c51ed8bc7e9bbb218eb11fed06d77299a530023729f6328ef351830c8f9c8b55219f57e1b4f440460c1e1e2282bb96f43390443425eb8cafed5a515b9f39ad3ba916ef1658b9550f4969d6957adc6c64681000b7dd50bc2b07bf6122b87ff1acdcd2fc95fbd96bcec10872ad6534aa192fb07dd55f6a1e8d4c6411dcf343d7267b08998627dc0380ce130aa02541de4d0ce065ffe3d3899e026d4366a51385c185394a97c6a5dcda58fc869cb402dab11900ec198c3ddaae947d8569f70bcc4f36a45a12d9903fa82735cfecc06b411567a0c8b3c560866db0e42d5c121dc1e19b6c5621e5b6d7db6f74834093686b3971a027c65286c1414d139d00e819cd13589e8df0a783e65de48e1fdbe2ee8488e170a2323e190023eac319e589f3bdb43498e81d342fbe9b5722229d275a839a9db05666daaa15d5a823702d8aa68eb10d2776fa1384ba4e443b0f2fb628bb7174a0a0a1c373feedd5641c1e6cde30271a9948194adb2c74849b8da3680ad6b08de0fa88decadcd30b445f00ec153a3c96445bbc872e1a2f8da702495187f45e23f0d true -check_ring_signature 473c5f8a19ede0fa39dd76a551d8221a1b5c78e3b9c459672669f634a0fe3a8d d6a77b7ed9b2c6cb1a368f0598335bf2abce8c9ff4d3e705bf619d4ebc6f05af 41 fb2d6710c69d1265352392db58dc55556dea90b35d33d2e06de8da38096b38d7 9ecbe854e55a3809c30930f3cde905e39995c777020d7527d31bee0bda265ba5 83edd629967d689ea4459aa152df8b499e9ffbcd9bb69c4d40bc886688c0c8be c895c7274323341290d3fbe7c946db534dff8d8386a88fa4f2131201a9b87957 70421cb9145b9627a826cc251a0483227294d8ef7b17c963fe3096f27d6b82af c097230c800162cca4ba076915cca6f78a8a733284be68eac07206e849d2a0f4 8e171a7dbc409fcb7bf9f37df4edb155cf20a864724643dd72d0c62061f037d5 53cb6576be0cb8174ad0af3812cd53ddb2c413fd843d6dff598732ccce3cce3c 7ed51d9eaf70f3a23986cba8909f2bc74bcd518895457d0fc6af602a34f15191 90ec38193fa25c0116a3a7025a19401dd53c1b22eca7e52467b06040858d82eb b25d722df24639c466cebd9056414955b1673e8ff32021f8c037700e0419121d 478e11fe8ce700c8569f46eb32325cbc6601449011f572e488de78f34421b830 01dd1d591bc77d7d6ab272f1f9df646c291ab683d284d2d9b18714f51f1c1c38 2d35fe8d85413df511dca5965a1ca04050a4cb7edf5010e8a2dac3b02f7ae86c d17154b70bf93771551b6cdce70dfe621da5468e02376a8c4bded258c6473089 ba929c8d8f17633ef8b503a98f163c0ef406a082c0fa9aa451f9fd92e9efec1f 947818d7f04caeeb50c404c3b5f869fa069736040189d3bdd6f704e5731779a2 623c5d205ec4c04f6349008236c95b70c4ade74c08c6abec57856ea179c52537 c913c2c74a85164e15e9aa698c11a959aac17f9203de0141ee78055665dddb86 0536dfc07f9ce1f1ed374690f894cac2b1c7a4b17c07a6a29c281b9459c83e86 db56bf7af91e67511f0c87cc4b698cf9030577d15bf611ec22729e0a894759c5 f869861f3dd0c637bf46c7797b3ee95ba4d0db10c364a55cd18d7fba01141428 26f252b227b95b63ca0c0b5c530a942d561105d44966639816a888a5054b536d 282406d7802c64260e50eb6b5434867f33cb5110b098322e3c001b192b05701b 3ca9842d5475d7a52944b499d56d558142fa52b8c54b19f2f8e5becbc086e5b9 dff42f383b5a988acf2226c24e7ae167b2f327f445440b2cb984a21db053f06b f0c1c7a6fefab6a6aaf1bb76b665549ed435b791114898da516b1f0a957f94cd 43550c4a4bcc54697c643763c10875dd7023b3f0dee12fdc7e62e0f1ac3d9597 490de981c35a43710753466863c3fb8f32413d38a0ede7e28476a4ff4177acfa 1a785360f1a68454484bf7b5bd604d73aa44a9120f0b78e5a596cefb1ff98f5a d9c1285972edb393e5b07408e965d3ed7380f38657d1d364cba51badc1b741af 9ad20e23d71019684b5062ac1099ef070da32838306f88f8cfb24353dfb30e5d 1fc81e27b2aea62b908b8de6a4ff64d9b83faaff4848cc869c7c75a234531f3e 05e6e63e1096b098cf79241a38302558714390bd5ba521f0d6099b503764d2f1 133e8c5d25c5ff83ab50441507b93663b49809dd705f3bcfe00028d6488ee6cd a65db1032d53d0764707d183ab8bbc8550df060b5cb3ee8bee46bc96167a87ec 4bdc9399ce63e92e865864a6375b7a01cd1b3f48d62dcd48d021d31a8324305c 24283672a40af4d547801fbda4d4aa7fa92df85de6b2c3bc757f637ed3d3568f 1776bc08c8d343f0d887090cd455ea894f9ddc14aca230f0d1ab142a52e9c7eb 3822b1d313a6bac7550af4433f7ab0812cb06e96e48cd2e8357f857ebb1082b8 fec8e259057d3ff660a8ec40587fcc4af2f8b3696d41fad45aa6ae23d12b20e5 2a65294447c858f0b990bcba3a92511af33eb242b9fc5b5d27837d6bddc9ac07e37b9eec9833cf15e6bb3c33e5c8a0b5ea173b7a76fbe6b975ce88b649db2f0451bc31b0be4ec5901e6788d0ac114dc9b0ce34fa22889388c9dabdc39ca4af012aa24d3ef4c584ee91473a713030f2b287b8965f9cb0272036823f6403b5a20dcd5f536f66d530540a312e58a50ba8f96ef3b9185f3021cd9b7e71d39e6dff0018e354c63392cbd39b614811c8a4ac79e8717f7b3cc75c764e52d23c62b8fe06e65c3813dbe1ee43ea503b2c0bd70f4c6e243a6c79714a2b8bc1d68e1611e90da22361b6fd631ff829664070392575da12e6c3d877ac836f73a5b1e532c107005e693b778dffed4ebe0b4f3cca9fbf9a8893370f528d0e922c72267dd390cd0f284c95d08238762dfda2a108b5002b95965a91455331a0849f1964c280e91200baea75f24bac4ccac757e22591a6ecb7e0babd294bc967fe03a1a43d366dad08b60e02f7bf21af3886683c94175567cf4352f07c29c6e983b642b7e0dd490702b71e4e406b58fd767e739914d66ac6ad8e79c283a28591938f735eda93147d088b386977f509ca702d79c5df40f862b5a6138b73d5cff93219057384c1d5080207026a1b9eb1f1ca901307f3921f2fd3333ea4edabf85d1ad0cf87fab972870886b8670b5114bce20cd5b263a86e382f0cfa49666b677444ae970687b7e899050174999e63c33a5c03546068990244fe217254b103b0a44ca4f37bab12f87006a1d8c7e60c34c5175e0aa81651a4f348449d74024cac0067ae717d66d80f50098147e5ec1c1e27eb081832e2e4e5b0ac95dae3f518308bb87f3bdadf00afe409d38d8832c21d40a5027a19261360a215d926bdf0112f5e6ea94e56a04db5da03b70ffc6fa0eaa8282af3e1095c9f20884a1aaba036f12600e06d950a421129017822e7b704880ecbc02bc660f346ff18523f305aff2842ac61671894ba5bc50fa34d0719934c25afca5186ede017fc512f9b935ad10fea6f66abaaa1cae0ee0ba22f3d8fba76e7d98147c3d13fe60096767bf3f7a1b4d6f1189beb25dcb8750466f10ffe0c46be44ba31a21549b614cee003205bba2e8c81ea07a534af84310164485db14dca11d481284b7457a612b4c613ff70662839459f8f438e02294a00a0dfaaab1e1ae956f283db3f48d61223d43c64283425bd0ac55addd1cdccbe060e06edc6f1c923d7381983d3e7dde20ff8d175cb6c85600e006a0f3d1c9f570b2d7926241f0b24983f09c17e6c462b9031ca75f8d98a2fc62b4111f7788a820a8ade6fc4f1281ba4c7e1521de3cbdd6e1b22618fe1f717608e602adfee3aaa01b9da7bc1ed9ebbd4f600775a2f325300e9a57edaca5b500e3afa470870191409273f207551389f411adbd906c04e820a3b1baa106c851f16aa5e43cc0444380d6435e5cbe7534cc4d07567423c5488ff7091e45ef16c7cf34aa63d85491ac109abd5debdbca5f5394b83a3d47ddd2d5dd1165b47247f785b9e7431f8052657014b3452c22446cf91c22eb6b74356e4887bcfc3b1d1154a8fedaea7c043bd290704755d82a2a9efbffe082228c40c92e650704cc9726cd09f271c3cb75c3aa30acf3dbf4af1da023cd77f0fb6e109c91c69c4798d8badb0a687433da580cc5c078bbf7bec5fb4024ce8575caed0d855321c6e2f92949b027fa3b0ab140ee104035f13d99ebc8b4b550b47f8e56b324d3b1ca163885a621a146100e99888995c00dcb949d4986bbdcd150869b6d129a98d47c1019359b187707cbc54fd1df448024ec9683d897e941157210987a465c045fdbab8ff6afdc33ccc0d8dacb68038074c7afd85c81dfd8ee683f0afffa64fe1f9b5936b02094b2b9bd4711cb396ec0781377ab9bed758919c258d24c9d04bfbdb3e363c7c596ed8d58fc24125588e0af0ef150e9a82c11d23d64887728fb566134912e450e05c41cbdbc7f92cd8dd07929e1a6ff61a0ad7f745453aa4ac674b8db66b7299287996dd2bc35111bad002cfe0e2d2481aa71da2844504d9e37a4c78b057f3957f6c0b31535e38c1e0d706f1e754a5d99642f1e96a2f02d7d5a0ef67f2e5203f93f97c797723b998a2a104f058b0c7c468f4d3f3dfff4096e678f316f742b817f5b9a4bceb2f6936b7410308b54a1d873b6cc4c8d7f860e661404b4111872e7b65573e52cf7014276ca402de01e8b7486bb6fd618e9c9d03ba62c5089d86eb64e3edb89022b56924baa50d80474d4020bd9844298d9083db61dad0a1d29ba6f11892e88152435110348f0bc16dd890eccd23cb44058b0ee9136ecd1988b22757caeedfa6baa22582bf710e0e62494607537d766b2aa21c4dcbd460fa83c3a9719a9b8cc3ab2801fc2d4e0aa495ec2305b691f6a675eef14a08e4b1aae01d7085448ebcfa102a34a07f0801e000fb4b119c159114232757cf0cc124ffc1e4b2284a40509cd81050ab1dcc028ede69b2e99c6301ab3a6f5d9b605984ce17883db4b6734b78b6c0b6f489df069efefff7acc29cba1220a804e89460cd54daede7bcaf7ab96930751e40c66e0029e55174b03949cb4ebd9a32fdd9cca007d68233e9e4641c2d025d9b79b47c01ebdb209be7fd2c6fe36397305a988a90051fe772d95ec708ee671e7f791d55091815fe18bb0ba6288456eb993fbb5a51427ec68c323c4a4d6c6241a9209dec08920722779f9c1574242034c022fe4e7caaf056bbcba30a6d4890aaa4d54ee80d296e587e4df980064ce74c3ae9a3fa91601d0912ea8981042faacdc35f49d2089a73f2fa129816c722442b49727e7355aa25f528081b4f420c20697cc575cb0ba87c73bbcb8e7b344736534fdb90867da9a6226d2963be06de4b99cabc61220e1aff6f68ba902e3a05868f12c7efe8682c35c4d1c3fb167108496d1347276f05074f4d43af23d2cdf71c6524a1cc059a623fb7c5f54acf9bf2c6ce9ffb9c9f0dc7aa687bc6ba811ee8c73aa2f193a4f0692eda6f08e077b4c379280205b642070f7006fd85d94985ad37495ed6d91fede19e9d82d3173eb3b34801c86d58580daeb521fcd8133a6e7299c1e464a7b00a9aa5c7adf8f3b91dd047c7f525ddf4059096922d9711f33482bc083ee193b5ce5ec1225ac5aaa2e2515aa0e349729806bc4cb24565c09712af19ef4ff3403aa4fcace11d7c0a75c5777261a12bff150ff6824074fcf7989bdabc370e32f69f253a3147d9d4032bde117962fd2d871106652cdfe4a386e2fd13d0e13daab8a9555f9a1f0e589761c1e80d70741639eb0ceb486dbeb062348e78e4c9d43f92c1a087d70fa75e5dc31da1ff1c071edc4304b3d67477892a1f72fc7eadc44a516fa58f748bd63de1e8da07228a54bf349d05e91265c56cf740454084f6716fe038ab0c8446eb2d4aa3f227037c6e45021009398bf37e09071b81f9a0c76bc8fb5455e4986cba32675614e85e46d85166bb00ac2031598ed7deb39f83127d0a10c8467ecc0d6bbc96a1cc3f0ea5491da87a0dfcf611bd3c2b3be6816aa0df3db16ddd43bf21e934e61e7e0905fff87ce3df074e8b0492242cd0ae3a411d32ef0ad445adcf1c535a28c1628f815e099be70800beb54990594490d390242318863273967bb830f6b0122d426741069c4c41a10ba67683ba57656a8104df288e9004d847418f76a467a4996b49f207d922e5a809 true -check_ring_signature 447439910aabca4192a2ced06c0fa6010cb8ef39be04a419185d18dc28930fb5 8ec2184ed5b9b65514b7fd1268e73f59f8eba1a0be6b00dbeb2a414eee2eb5d1 57 60a3e7ffee43c0c94a31cc2042b94dc7ba14603833d28fc9e4870cf2524c593c 616c745c8db1dca5e54f9a4e4637e49508c35f8c354d6f5d588981cdd1ae34ae a1fb9c4d82305ebc49ecaa5a8bd6dc5ceb8fae70dd7a25c2c7a20b62c81a5aa5 3d07d1f3a912ef59e48bc3f27872c2bf8592f704bc185eb0e737b3981f5c0d04 0eb78c7e7d4536d1a4d892e729f77aa6f7b9346637240125277176022795be09 b634c332d5ac279f2fa26002133cebb0dd54fbc340cda8d1f62dbc654bb10e9d 3b486b851dcf9a8297934996b62d210ce75e3a4940105702f236d4668fbfeeb8 223bc144dadbb7c25c027efb49fabf1147c1fec591c5cb1f9b7cae593b72f897 2a9cb84339a2aca74b1b09b12fbc9ecdb8350dc89dae562ba78a0d351ffb1567 c43e509b04effbc1a09bfc08cf906dfb68d8258e5824f2270ed455f0dfd37586 5eb5780f1afcfaab7140bc8f52d8d6348f7f39b2b6062b685797778d5b1a1bdd 1e10374034cafef0ab5744970df4ca23372d1f8b71ce3a49e0a53c9666c85649 1bf601e36a9ae7dccaf1c5c23e526dc115c75358617f9bcbebfb8bd764efa7fd d3db57ab0aa0b445a81f188eb57d6cbaab8c2890444743f403907d7f998818ea 4fcc49855c784024ef1fbc243711900073a5e794f5414033ae91960871a18f64 908fd31eb48362d427a68907a3862235c7d7d6205ed79323b7b23932fc3fe037 2513af6e7a7ccc96ca83ce6d08a49b5bfb9f39d54472fde4657fcd47ccbccba4 3cd69ef27365ae7cd1030c9e1b7edda6b8aaf02a4b7a3291367554f5031e2869 d425ddd17acc6fc57a1ec1a3afc0d957d8e7656f28f349ae4e8059274ce6833e 28e5430658539dc2f3a3e1ab8408b373f0223887eea391e058d6da9484ee3f59 7ab8e30103d9aef0d0e8a165da0bf5d0d9e9e76fd7fd33ade44b477674899512 1764ac9c9c560c5e717eddb6c97b011be167111624381c2dd698e7a718bd673b baba0ec57b866c797f1f62ef1e596b729895f857a4e72f4c0a7008575e0bebdb 19c13f4e0a1f4ca0f4ddf69522e9483da71bdab49fd76dbdf59aa6adbcdcdbaa 725bfcf0721028f928ce458faa02d9d7f0693e5083754e1d76f22a0c4d2c853a 8b1b9d32f78e7046de65a563469c15eebb65fe7e1d1e378fea76b2ba7862981b 9e0a25ec994e59ec895cac33baa132776a861bc975a71f18de30920c6b2ac0d8 6214bbdb0c360078aebb311b73add6493fd152c7c95c3994b7a4b1a7920e403b 12ea817ded97052d342c757efff0e903d53092e3e634f7628c273d4eee409d71 633895161bade24c05aacfd0e5b41e6c2fa4b462327fc35d21be9a1e3db7bc99 76537de015109e0c8afe0fd916becdc02278ca9961510e1bfe55fd2ed4736988 fc4699e122c92d4145dedeefdb662c1a6ad8b8ce581f666f2f51e4a9f1f866d2 4947384d610b70205012b8c4d4510a782edea35325b3e3f06d80ce0f390e5835 0abfba8dc889d9f9f5a1adeb7dcb5b7be1cad3cd8c937bf9d20a6140a3cd70ae e077c09c1d8cf300de3c26533b7a968e5c22891bb3e696e22da9100ddbc621d2 44140fce46d3149d8fa0b0f462229fcc6799fb372acfe044ef192253cf0c571c 18aadd1e8f93580a71c1450e4e5fe2f2ae07d312e0c59660afb3a9f8c1d35713 d13280c939d88598ecae8948bbb5572e5e7d8d7063e0aa5c9aabcb18204f05b9 dcd6bdf63d06c1bdc9e80bb1744c386f49bcca6cc14e95fc28aff48f616a0ede 38731858ab28144ba079bc515782767bc643368cf463134b73c451dece796a51 d855c8a7210ca4686d60826b50c81b29a38dc6bfd3e9590da4c60f4898550f3b f795b54d65ccdb5378a339c0a639960f4b7d69de2ca47ed13718cf7403b1b7d4 43143f17ec724111938d45116c5ecd6aa1f13aa0ca75c64296b4d41d58d539dc 69982eab099c2f8ff3070f06bd691e8f887e914beb5da85635cf0f7d02cd0937 daf45f2fd321ce8a88e24a4915a99d826ae32ee4d6f5cd251e4447717d4b9483 9d0b6c459d8ddbae5c0c23af9d77eaee367cc6d9c9b48a104b215f2d5c4b86af 606d3b8e6e3a95d8c33506be158129b1aae77968da6fb82951153b8249e1b9ed 6b0e93952049e7646c38ff5fbe18edc88bfd0fe8704e451cfb1df6a076cde27f 52df21101fbb4d1e3bb54b82ca43f309b7535214a316693e095f41477b04aaa6 a2e910451843cc651a29c48a18635e8c0fdfcbf291047a35eba4c3304552c324 aafaece166116a83784e49b83a1ee21365e480f75cd5fbb439c58bc25b0a7b23 e88e41845ed645aacbadf8ea41d6ce2f1b75ab8e87082a8725cb60d8c340bd8e 1bd5cae282a501fa69faddd36bb2ba5665f563c4dffbfbf0b0f0d353125a24c3 87bc737a42e6ddbd4d4cc4abaf1c77d3bb06d49c68ebe488ea58ac1065e05172 1df1cb3d6e523405afe40353008969ec068acb4e3d983967fae3e324595b9172 c275857013a2e23f03c7e35fdcb130bde10cd988a84a18f651b5aabc3f571a07 f2a20c41e2cae1af2b1db7e9f24681e1818fbd297d82c451aa26c30f99eae2f8 2f2a1fc32682ef3f2474d27cc2ee93eecdcbf3c3d8672dca3ea5a3d6d4b474041689cfde5fc7f0e0e4703d29528e1220c5d2aba43e5bf990b44351975ab58004a04bd4a98ffd9bcfa2096bcf5c9e9ab3bd3d5e6c54dd3cf26bafc9d379fcdc04b0ce1229f71226be0685b5b9fdca91f43b99cc32b9e7b99040f3ae559bc08f050eff1249a1ad3afbecffaa5b3095ce8e16ddb2f7461bd64bf54ec4daf441f805f41722deb562288b6d4707d695d62445e6f595e0f4e657d906d6667f3968cb0a317ed3517c9e38975bf9919c748bb6dc1420e25751ee7e7d7bdcd9d5c3ebe50de795662477446f48a03311acff04d8a7ca999f2077263e9901ca25e861e7270147e73af3af2178161027b5baffd28a262ffc1fe32b0222da17ccc067c696e504273bc920de17aabd8c6ae386ebbbfec600f04e2b8b979cecfb2aac921901f5056fa7efc33736ecf983b6522038f3a0df6bf2a9e364dba9146ca033be9210a50bfac0c94532bff26c5448eb66338121f2596c325b8fc01dacd52045942739b30c1552214545a3f8417adc3705ae63817cea25946d95cab9238c5a01c080aa0803a810099bb8b3139fb3b3f87f7313e326bae6d2aa05a7bf5c8c8100b953fc3d03baa166b873fc2f8e634f1143ced8d68351796a81eae273628b91e9833d155d088752b232a73b275b4f554fe3c034abce1622bda005ca49f8fbfc0ebbf147620b513c3ff235caad60789c5bea268bd16d707672edffbd5b35c87e4f492da85104506d4d7a64317c7cb494b34aea802c63958721c0b074d51c9226f23eb8e178080f38142c8dbb203b7da816a961117bb56c911b32ba527cce395f5fe15b713b0c3b8f8dea85cd2b6823ccf20a7f4dfd37b56a06aa015166f34ab6b2cfff70220b8b23d102f1873b843525f6988c4291fb24818c7308437105f2240d3d24472c010fd7e6c477f4af47892f684f7ba193979d826175505d1c7120dcc4013433380310213fb30b1d76ff6a9caf1b8927406a4776540ca45392069a84b5bbf0b967055db9dd36b8d1bc36d645e6d94569be1d0dd1845f57103b5be31c33bd8901b40fa89d9c40abe8192b4144876c82dcade218a0846f5fd1f90dd77f831d97b47d0b7651ef996e4115cae57bd4ac0f8d234d459a218d662aafb6aeec7ecd05adea0eccb93f46a959010b93fbf806b9086c5f57a9d14159c8fc6170cb6bd14c543e0214c5212e94988dd61e05b3302bdd204324502659e346bd4d6396e535ac0cfe08e46c54dde5d25a0c664d9d0728f593ce06743f936455e73acf3cb818dc47190dab188837ff159a2839a8a9e6b43971ad255862de87be64a2f9fcbf61bae22501996785f69d47e945ad53be4dc6cfd799b7ae80b9f4b06e9aced2241a16e1790a89d35b326a8af0591b6738e6a8e3feac48796cde058774c192ed31a7a1edcc0a65a8df96a660883fa92ed52abf71ecb90602d98817e891f47cddde88d25d4a0d780be109e283fa0e33957de459b7a6fdbba4c9913acdd99f422e4362e53a2856a078e81b66df1d476fe75751dd3c3480b5be749eca4c4e7e1d716f6a4f5a6005be539fceffc80909fe6c242bcca679295c81f343a7b2f5ceaf1ecd1c157e6e061d440a55aabbe47f148de37c9c3e6960947da708388764ee6abe0818dc7d9a09bce2e79247ead4aae935aaa3cfe63fe13061ea9babe78966aeabb8227ade5c0027a9028e9c7737736a0b54dc58f47c0ffc0224ecb3de923b1fc8536106359404b9894185084b27ff1dd1cabbef402f07351878c4d7aa379d73dacc2dc2bb540d8b1fadf10fc69c80ba8b10aa02fe8b1ae0a045897b7ecbb046cd55847111a70bff70440232d520b6973846ef02888477012d68e67a6e79abbd7e350d0744ca0f977c710e1a94285cc6996f78cad786173d8a648694faf1c71ec153f5fd11110e3a3b9602c360fe95095f2f51440d595bdf6565efcce42ca8137bf31ab6b7eb016f4f96e9b520754d19577fbf3d5685a4edbcf0f7c575b583de89e7edfe130b03c20993b910c74975521fc236d081f8d4c8dadcc80739c20794d5cecef2ef360921e22da90f348ada6082084a999fa872c7e2cf3b14dc3e4b92ac5b0a18c9570819885ee65688e729f2c5c3bea4b018d43edef6c1f437a63aa2acc74d89b3680a211107765ff0dc0c831ccd6b7a01ccedc660c6f4e5bcb0ba929d3baa8a8de2082ef67f001ab249959c73a222d3c6f125d654b9d5491592bae5c1da7923642a0a2fcae5edbab9060ddfc2623942e1a4dab65619fe1355934d1a61795d9a21800a82c8790e9f895c6b890bc6631aa648bd08b7a455a62091f2303015ef14263f0c442da542c085c2efb6bc2b4d424525e5fe8fd62fe069a2d11d776e3e883d1808da7d941609720e92483f8c14b81469c8455ed91156aca7662c0d47adc6592205cf31677493b9c41c28c6cc7582c7696748de8a7db05575c16bee15d1aa0aa60b0804e664af25aa0d839ad21b1c40a3a5b54d1e1a30cab90cd0f7e23cc67ae10610c3565a0c50058ee86a6e1fa8c6e0e086b0107679e608d58707c6cab61f110e0362e875ba78bb80de10d31bc655fb84aa09c299fd0d70dc0529f1364fd2620eb5f1270f6e4872d0e653bd32b6c2fba4ec554af98151926da31150f4da410308f96a2559f97d467485d21956aed57df0b2de1493e021913efa6b9406c6bdcc0e3e6c5133f63b0dc5f89736a54d24fae378c268aba0f02d6ce5fcbbc1ec55c0015629b826e8239dae7f5bba2aca3ca323ba7674220aee24c81a32d8e2b5e9d00dda6c132136a1e13ea097ebed8fbf463870430d171168c5d786108423fc27710e7bca7d02997eddba8249ac913967d51d192707dfd5efece7d23543143992f40682a6c3d0c1dc50c7917c6b437bf980569157f79fd5deb7718e02588e07f44e05c3586f79182881216745c66b846824969ac5676e40c9efabbfb9223045ab7a09d616f3f0625a9cacd9c2f16fb4fd8b22c4b580bd23f419d0738bd789f2af2f08aacd8d820f970a5e68ff13b1bddb67e206b0a6bc2293e69ebd136d510146ab0b10e9d5c61a742f5ffa3035c190b6a5316bff7136f11294451226275472b2970a17c1972adbd686575a8ca730884f13fca7e0d6e7bc0dcbc6734302958a716b0cb1e57cb4cd2cd16a63e2f1380c27419c48359135976aebf74dc47a56414ecf02abeafe54651cb4b2c2cd16f7aa62ce7ec8139340c38edcf67257fcc4a34757062efe07f1c851d85bb7b1838fa8f79220eb7022d4476c156a2f984fc0f780050bc89bc84ee32d9d4e513792c7d1b60ce249d6f95e256bef3840601e2911091e0cec322b4c8430199ecd639333ecb68baeb5d0efd1049bc4ae28c178eb12340a0708d4056e354bee7fea8911d0a98f037184757fabd7b3bd9a7de9c4e83dc0920e488854b8e7d520e11a71eace785c2a6fdf51525f85b869b4f5ba6c126e3d0e05685fc0d79e8aa7aa7525ce18fdd591324992b7206132115e803bd09a32f0dc0d7be929f2a88e3507aaa95edb3a58e12ffc7cbfd86b1c5e49fbd898a47ea8b8009adee2c840704fe48ff0577eba81b59b44e757a0f0864253affe595d399dec0e0ce7d307688cac09e161f2ed748f30795e8efe04726e7932f0373869adee0b0833c24b2b308f2d5403c4bd034f521fb461143926be24c5aa2211ce972b60c4021340da4d9799ee8e08f1d92acae2759d0e118f0c338eed0c4000be6651983f03dd58037523cc0bfe38b8c5b6133fcbdeda3b027d4799622c64e1a40c7223ac0f67ed7964d61635c0d80dccc52ecdc31af4ba2f1ebad1e4d42f1e58e642a6a8020c24dc6cbe5c61269657445e4b683bbfcef7c983fb87a24a4f71fa62d05a2f09dc79b6ff8cf2e4c0c6181bf74f9c7f8ac8d94f8fb8d5f9377e5b8ffd9aeeb50f23098c5d25a5ad46c9e2d9553fc9b0f5f454743232f70998cb302da75d1be904cf33541698e05a354b763cf723449ddcfb80d2e85e6085b131b682b71c7d210765e8758cad77df5dfe125c03c22beeee5f370b99ea17816637899498add9e4044eaeaee54515ad5885a6e68d24fce05cd5791301f53fc9f2500a7451e3ff500d8aa3cbf1bada30ab1a6f9b212d4e9e4334296a55d44d5558ef5eef3b783f8f0d8b15844642dd984d44c569c07000bcbf159ff30a6b1bf448669247be4982bb02c4121f9735b3acbca92a146ffb38b1f72457ada7dfee89dfd76d8a5c5fdc130b9ed82f6690ef1fb284f0b8f6a3594793e660599582b0d7a5032681c6e0bc990dad321f53427794fa1e4296820f525f88a22506649a36655566c77584af61e4037a41b70ba691d0dc3d887664f9aee104c9d0dfe40b92e3b86d07ca3186ce27002ac3df8c8b6b83e775110f8606a0e18becc3e8a5a53331088a5545ab6adaeb055091ba0714618669a2b5c7bbe4e8f18acec61151aa4adf694a2d30637cd6cd072b1f1b944f7376b0865e966906f5dd1b5469993684328ae65080ce5bb81e190d09249b9bd798a874835e046b2b106adff9a0ba91ec2117404c06d0b80dd973076b9d66f89785d4aaa49fac44ca7112c5d0fde4569f5969e819e18bc067c5f20d2ccf548d4db4a47d974d9d8fb1b97ec34136cbea51f5b06397e2eeffb3956101308a361ca150ecd324c303406952ced1e964b408bb255bddeeee4dd3503208016a3abe36f692704280748cd64b557a975a2b16e2748832c23626ec2f1a177c0201a20faaf842b68b09915ac8169415f6df88a3ba1c7f0143f0bd73bfabe7b308c2373c72e371d208d8199a7fd3b474200c966a6090fe1ba0ce2e98c9a07855026fb6253d451db0138ed59a3494244cd73138fc8b86a4f641b1aded681ff7a30cae15df7bf42593c595b4f59c6282d0ead317fbb49d5717ff354e1b3e0db1e4042db7ca70916a6273c13b8b8a0a5cdbf7b34322762c4037d7fe9ed948062f90034f7c5338e03a231fe2e3974dc2bfe971faa6fccc30235c5b97e7d63de353da05db8b34e8d5162eb3e986faa6a398dbe74ecd876252399b7953f28a34f38fcf092397e38b57904353f2115c09503ccc237db9f3a0143b0f37c5fa94fb8b36c107a40449b98974cde4892a6a28ca0a62b68eb35eb47aea7d34c977f696af6ddc04 false -check_ring_signature 1650a9d16006cdd8c6070d89e55e009dd4de5feb043a56d1d17268f5c663f237 4686450a956d79eefc545318c4d947ab9a4878b98abeae5cef2c49990ba2edf4 44 9da3f4923da7c8e56f84352abe5975456bf59e6019478b254b55b4eb2cf24a4d 63fdc293ca4cf2ccbfa8576f02c0b543ff972a3ec5f419e65a0a2f7604a065de c7960c004b4a18f3c286c3e81b3e107609ccb569d7e9cae61ea518f1bae7ca12 06f26d89ffc358e2d47ed00b687c4e0cbf543491b17cc046bacaa5cbd559178d ff989a1a1f4f92a50178debf0a0a805ac6282eda97fc3959eb0374f502f2acbb a48d81068b60171556fbae5e7078a134fb3988af6f89a078b72f20906a03e5fd ed11c00f9a0792b496c40d57853a7a9f5973b714ea8048d7aa18d0c683ecccda 57893a85ae5e9ffeb680622916d770d7103344024e9dcf57f685f0c6e77be3f6 bca9c5f215da950b035d20f10d58cd7aead149736bcf5bb6d508653f0fedfe9e 3cfc0b1a2afd81d9c49b471e85d72e401dca3236c8b73d38ac9d66fcb5517ebc 0bc537e57045a3d1b751ac1d738586ae5e63bd121c7938003589da266ee00e27 7183a6a6ef0ba9d4018b35e2ac408ad4054311670020803e9c4c68dc466ec31a eceb1d62ff7091af6b7c89bdf1485863d04a6b53c98957923334e4c6469789be c9dd77abf05b0b6de8c7c6a2c42ef9f9a6f62d50c73711cdb83d2a1eca91816b 9af17f90eb3685739a82dee0716d64eed75f0b37b5a9f5aacfc388ae5c587f60 33ab0c7d3b7191cd25933e07c368d8c9525bacbebfde2e24de21bb99b8460db3 e40e2c2028e1d3ca33290ce1efae9b6901ca1ac02ed536f396e774b92144df51 e5d04521b2b1ea6f1c194b6d754004ea992334c13cc92e97fb9864295b2d4166 308a16b37d55aa2b164e75339b51e390b0ce26c6fc65521f5c3d8cca510ce054 72e78b72c3b272e6fd3db641a29ac66dd159265d1c8009535d82dc04a4bcf716 7f12617016971d68a779facd7bc883edc08016f8d93294b74cfbb6985aff38b0 e52facc1ca6639fbd43a9881e6885a9a910976f2e82ad589cbb2b229f93bf249 3fd89ee0462a101c50b3c26055ecba13b75d547ba71633fc1927edc7efa86868 4191a1dec9f9758fff7b4edf7e2db5f137bff6e45f22d0ed0153b02d79be45b0 f265ee4416427e6422803ac080f56324f008444e885506ebd6bf4edf35412ad1 d2f541584e55bfe5626be2d0b71b122802b045df80e78b98f7aa591e64b80108 39bbe89f51ec983a0417429116d6d0574731f04f67888d01accb30e50eb71f67 39dff7a977b4c3489a3411c76b56dcdcb2d189513020a617e91be270ea954b77 6c8a5a7698dc587607bb3bb26b8fb233a5e6ae6c076de953743e04bb914d5d23 923968d74a2637131491791dbb6660ba945cf3747541e1b321822cb97b3b7cc1 bccc1c05192984d9e587b5a0a404229d42e54d3877e262a058a73d1d1261ce79 fb31be03446eefcd929c55643678045ca8b50d24e702daa4c5db9bf8a209d0ff 77c500108af5e1aac04e710fabb5fbd9567a0fb862a97f8b463423ddcf8e2a17 0614918891e7b90b25327d00b9b0e1ee78db12183cc2e6a440b75dc0a905aca5 2739771b976fd1d719411ef966d28bc87da51d17c2080a8059b105434f2d3ff4 9c0771b486eab598c0056f540d793a660f01e9ea57f056a86ce6ca6627ed11ad e86e699b54e60a61e96110cb865bb6f2ad28b1838522398cb39a4f38ea78dab3 89efd7929934fd5fde1dc8b2a573e5a354a1a4648aff39332d90ab13f577a49d c12258dc3c7994e1af7c1cfff85fc793a3d7463896ab115a239fb252eda5d7f4 e978f04235f718073fcf6a555e338b70dea251a361bc3dfa96b5f2eecca3cfc4 1613a21dd339f058610073de7c6d08bfe4a47c2151c163535d6c137c6bfea83d 8369a79f59649b11cd9122b7d780fff4414c4bace807c7a7241e9b88fd115ff3 7cdc6daadc98c8f3724eaeabd21b2d361b2648b7f62336e3f15106d191861666 2ae345015eb935114833cb82e14bcdc44995a56b0d4fcb96ea650ce60a4c7ed0 3294e2363a47695f25a6279fde3a5464fed9e174a994c83cbdfb847e7c8cdd003f74098d2a12c0f5ee73d7bdce990d862990d8072a6b5f927973c2cafe34e5015ca7e4369c90fcd08350ae7693d481984f13cd051465054cc471492b49df340b3e7f07e3a10a858c2c1a8528550ef3d6f8356ddbe4f1435ce3461e85b83cce0952d10be08a82049ab5b159c7c8851e9f03b05e8c008f8ab5ff136beff2ac7d08091f20f955ee7a2b20bd251d2b558262f780195b44f9e3bf923168e7e5c1ba0843c6faa5543be6a420ab39a3109528a0c491f9ded31b26b70ba5db6cfdadf90ed0e2a0946a3d5cde969b4ff474cc99a3a16a1cc71f717cfd47ff287b2b6b4400e4bfe55be10c77ac18b2b27d1523ca37f7b7f8d78c0a230b4c9e56513ab4830a9b0c1e256c978c25dd372bf03eb41951a7c2cb7e8d743278b1fc3ff3e542030fa3d29735b8d9dc0d5dd20ad533f2827ad9ab25fb5828f13c1a548e860c7d130a4a04cd5ba35233ba9262c573c1d19808b84b358ce5aa2dd1222811d3488df606e8aeba90d81bf8be181a03a85701c40c210911be8f448b18d70d116a56bbc30e1a2fef3e0a7f43d9e6227e0c65ccab4a91133c9055ccb2f5fda7d6e758fb88048b96473c23c0b1f76bf670b4929c28b5c4904cc9e4994af77c5253c8a65e830f9765df33037e1bc6dabcb13b190443f0c1efd1dbc82ec8ac5127de1fb493d90029187f2b08719a920c1a15dfa1ce943fac4f48f0516073ba124ac97edf2bbf0802828f1b8b580d1b7d6503f452493af1ea06492953c539dd3e47746fd00c6a0056ca4c1ab51f3ee45deb241aa05feef90fd8cb59bf60474191c9c351a3efff04c55b0569ecd31a1f22654583ee4a5c26a006018d311d965d032768310d464e0eb6e900606697f9230d4e9b58c401decd05df532c0a268ec42bd7714a8118770304ed3dd3e3b4e931fe82fc93ecdaa5f904c9552d7c97e341e34fd1dece21080e3f8aef8e9b8d96bb461478a384e11019d9a9b0b950013854685ebff42fa62d0fbaef8b930b538c1e989c596cd46bd666e5e4280d79503e3a7470b4193e2bb807da82efc6b11f86f927d9390a355391c0ab6ba4a7347a34d5d0dd695317a3430a9b44438bdfd44aaadfdb11dc09bd881a3204c24b8ce3c00380f11bb02d83f40385cbceba0fe211eacc666c0053b83d5d9ee37d09f7d6d5591094c7be13be0105477145807aea2f323001f77ebc18b6d38d08bb98cffd83838677b2a470d7d80914923e56e985bfaea5bb34e44ecf01a9755d3f0927762377f9767c15e6196e0787633e822a809eb1c5b8dbb437d4fa19aced6c606cdf6df497433307b027ad0f82d3a15fa70b25ca4928ef355fb59df29ebf0cb980f999f9b85930a7574f8409a2f5839613c85c0cc7c4a12298d51fc9d33a50a109f6cf95ed0c90f0beaa440a51c590c6e1fd77cfb806373536966ee9e87ccadb8a3fee5db6f4e547a8ef480d58fb6e5fcc3c180e5d30c07370b19ffd0727c99450c81ae9cb442ec73d027a096e7d01c2bb334a9abd8babde7ef54b3ead4e90abcb8e25259650bb8b8dc014027a6d2efdb03bdc8a97ac9c858272cfdcb5e9f4202461a3cf2a7ea6ded9825403a0de8265a5c78d96340532e004863d1dd3cd8e3b1db0093d77f250c9a2d4e40d76c1a9e8963ba3bf493d5efc348d86aac482d6b78903d54bcc057bf3d2a4ed0f2df5b8f135a72085d70a3ac376fcf292a0cb6df4523b613d20eaa21fcba8250dcb29eb777c7e3bf07eb9749184f79924f532d02ba29d8a8d9c1d40f3e06f040d3edb4e9c4517ceef84bb895a90923fb1dc2878bcf52f1614f24b31b622aee609e8fe6f51d82f744047e6bde0cc4f0214085410ab23d26b29fc722c7b216f80077b78605d8519de1e10707272d79bf23682a0011208d769bdc214c41cbecf910d18cc6a3aa979e30bbc16cc4a1c38aed112f70bb5c4dfad4331edf47b6320b40d1b5eb126bcca17bfde50e5c72dfe6353753a32a72832ade92caa0e31106be807363a2af1f86f493ac6c04c2efc1a5c906afe88e83566fe830a4c00d1c567e0034ca7c0caaa924d5eba440dead94dc21a2ed60488343d14b984eaeaf4d171040b7eee9f50d255dc47a43b6547287ee9f3dc356e52b6dbd8e46f19b7183990770d6378ba2542219d7bfc3c2ae10f9322838b87deb01fdc944cca212b763b3ce2087f01511bcfd67e2044f9a8e1d28f5163a6263614fe51de06c76fe95379f194076577498086b5bac95f051c8f33973b87bd3ba4c3c1c794d464ef57698b1a300f3211240eca973df48ce038c1a90d91a1d4c57f23b563e98e88408f8d257c690357303c09ebba940709dfebbf93520d49d2fdbfc6135f20a044b1abcdd48de208760422fd7920a3d8f0833a691660390e5ab8bbfb3167917986b1d0d02c1d8d01314c72eb6e0df4bb2c4f8463b7c6ccf967644f4ad6c7b1f53a50093a90e6f30f03f39e649ba378364d98a7c57d1937e4bcb0b8f90ffd38bcc624d06e07458b0f54c1a8e6e92f95281a8934d6d0e0e85730a119592f8d583916a28ff391f5a10a776ea81c612b174242b8181e00ebf1e5807792eb3b50aab32c724161d1531f09090998e4b2da799ecea0fc414c05e7c46fbe9ca2dcce187fefc87458589385083402ce22178c7b77996127c8be6e1d1d7f9d604811f60493fe7fd05963de0202e621b5bf712fceffaa0146deea2b90ad580154c90aa12a4e3a5c242906830106d075032f4d07ab8a171518b725f564febbca8aa213284996f4663ec7c8002e09666559100238fcc8816432a03e7e08700bc0b962d828716b22e950291562ee0b09f0c899d68dc21354b0554a5a9cf839ebf44e01c453e7b873a744fabb201a04939ab36151c8d2161f22f35d9f0d589bf628d8571bf5ba10b522fb928bc7d90cbcbfe0fc3a3a966d0e1e6555e77016a791bb7c3f7fe0e10c41b5ecc658542a05d11b4e055379e28131f9909bb78f4aa025764adfdaa64cbf3f59bfe11542960546333fa0dc3fb640afb24544116d91f109316da466def45aa951619c5df2c60d7b951e01fe5154b039e921e76c545143d0d266610239be06385e4071f6403d02b4888400d1dd10ec39057b6086e471de4186890da2ba82a644b12b95b5e0d70265d5686d5c19ed757505561ffa2c4d155cff4138584faa190b1b0e85eeae2a04bcbe4007ba4f939365dcd12bb011dfea3021d4d1bb141a7ca9d68af54ec14b07ad9f23cec837742d7b183143c3a128b6a7eab0650518350b19e32cece20bed005a2c6224364063b756ad1a8b3777cb1bf86cdd81fe8a6c1dbfaf5d8bb1713e0d98ff866e53c558ad314e5d54054b2513d02b3af14a4ee22b30d848c8c3bfef087b6bcdc4adebc3e3fcecd2d9a023879030641e512b22a3a56be7a190732b760bd8376f84a6f2699f4d08b050691546960b543996083a1b315961987b93c9ed023248ec0d217b70afc2a57c74c2479509d424200806d77a85e360320d16aa19080660de8f5e668871bb9457dbf15ded2d3b695f2e3fb8c3f014d4e3a9a001f00428674b54b242973056e8f01840bebbd5e405f9ccfd3759bec7c9bd24bec3b109a9a3292166e7486c71601a0fcb5d34613cbc881d9c0b01a48ed4f58aa17385078a3d7a9e939408753b35ff7c567ddf2a411353eb28663aa2603d568f6f384c0809f57a762ab0218ed89f5df0b4a42430f2a8c151691bbc8fa5823b91496c340339cfe8474607277d888781e8241a2995d7ddaee5daab999aa3bc2fd3671fb0031f588ccd64cd2f01a6de20e021181f8540ba250afc4ed2282524a5ee9939720d96fd3e946d48e9800ffa9bdf1534f2e5740acce3ad822863c004f4005416e5023013c280c43aabba522575f9ec13a8e4f7a774c7cb031e4d326e76361a6ac70d669ebe20fb489deb58d35896ee08fdb55a2a93f956af60a68fe2a45f93711e0a true -check_ring_signature 99879252b8f28c01dbc0b517368f447aabf75268451438e6fa8cbcb5a436fb32 40f5c8820c614d0917025c417025623c850d3bf0240ffe51369120bd23b49f1e 5 ece685c823873bae715851a85471b32c773655def4f2e6c169d8d9ed061be4a7 204afc111d79d78718dd8ba2f7be19ac59a28dcfacc7330457f9b0240e761867 323ae91b0906b1ca394a35cf665425592e4efc8e2eb2625cd090de8ef80ca97a 2abda9571783a274f90efe6ba7674e7ed614e1e69dbc9f827b865c35235bb086 58a21a5f226ba1e08ba650e3d12891c3d7944d698ac0f6f0d3830a03a4a9b403 8727220129e2c72cd3dee5d41c881dcbcc101cf3f25745adfa3fffe0876d05042ed0a9110802377394b329f82ea52f1f667363e7496b6d224103b93a5bbc981c9856dcbf9c013dbb485dd726e473d2437c53431d521fca21a78a84864191da0f927a88261f14ba1bb90f8a057f1836be560f704b466246dc81484bfe3e10a503ccb81b432d6ee0668f45fe563418bc43a48b2e1a35f6f2c31242bf3bc9915309dddcda67fcb04b2be8677366e28c25238e7a8df2d23d2b9e59aabf470e923b0541899a3b41520338c4016475b8e6d1f575e04ef9ec36ef0bb06ee0c5825dbf0a6c1aad6b5977616945c202c48009c823c3c26c154309d619ca3286e769ed1b02332eb19003feebbece7d81a08e75f350374ed167f880cd4883acbb75b86fe7085f5612c3939a773da98e3309501b1e1149c5e1484c310fc6c1e67af8ea633a09 false -check_ring_signature 419767a163715b1bbf7125f3d8c4ef55158e25dcffa09b36612304dea9229e4d ba0a3349d4bd093f7dd1b74bfb31bbe55f3cba41bda848c7d06760da421bdfb1 5 79ccbe2d5f04f749175040e877c86bd2affd305fe7109b5f20114d0c58119ac7 86b777c32ca6e6f8087a1bd7102be72a63d2cf70a3c278db40b5b9c5c86c980f d7cc566765238fb296385160cb5816de9955794b38ebe14dbbb61d5e9d358134 21a926ae1c2826d3b4ec5d9f014e8c3f744e55c0302b3792a92f2884e2eb61e2 e71b95c1aecf11c11651c7e72e13315e9bf21b6fb4e50bb3bc29f881c1186786 0a5091e5bb09f83a0dea69c595044a0488728427cc4f075c0b6780883444def5ff33e6049950a08f6d580d75ed42542549c8d0f42f4862d32875c3ce9cc7e901d250b548a6c111154ad87369347c4ddf20a469afd8668e8d5d59f3e2d6a78c0f9962658aa672a22faa8b4cf4ba5c16a02721175baec47dde62da64aaf53d420930366b06dea672a99e73e8afb004749069ab9c0617b677d5e5c80704cf078e007477decaca422d643a707f33f8a5ded2f4b9db2d7ab2693492e73350923b660f96936b1a230a1f40c93319a2564a6df8c41ae49cc839375250e70deee39f2d000501e9997b4873ce01e7141623ddfcb894fc0b39e8b3a5586923bcf4b1f3cb09ad40299622974a2308c8cf3c8559aa452af1bc1ef7c2fdb5057c57f9b51c8d0ff2be334ff3dd8d7caa47d4766c4b9b91989cbc699b497683296233601144820a false -check_ring_signature faecb4266110d43fa8cec3ddf66f7588127ce13e96b4f900298b6f9a7ccd8e0f e176b30714569d4fe5658be178330001eb35ef75f2a4310bb6512318b5ae54f9 178 3b7bd000981f5df1ba807c9b28ca333da30093c5f925745e79897dbd2c4b57c1 18ac8753b8b32f0ccde7c98fef4754579d3cb42c8128bfc2f5e2d97cb4e3c566 239b2914603d0b83255f25a1acf718e06e51c806b7adf3b0b1faf47c87ede7c5 7401035604f21971336ed99943b1d49917779d7bbabca7e9d8dbb48ad86c763a 283d9a7389ff11f7a207952568d922c2e8e4517aefaf38d48df242bcf48b1bbc e6fee2cf5bdcfa300b09ff237c9800d9f709eedd4d9de7c50f87ebc48266fb8e d99873154dbe924ef288d293a8c8c74115cb103cc759bec7633b7989b21bf823 3d8dd64b652c9d3661a7e2e36454eb7099534513e102773836106d2a7e6351bf ab7eee46a0124e20b41ac08367b02689fdef55d41bd59f284ff16533e5958a12 b9e138feeb8680f41e8d343acb4a930f63a1a813ca441b2751d2ab7b13f08c4d aa114b0aae0b32b513262be44706e34f146fa658165c1036fd86e05a5d6bd1f2 d9915c3aba2ee5842b6d5bda50be4471994ad3eec38142cec1b4be4e0557153e ce516e62afeeb7964862d1192071829b82d8eed0ea125a576c8de41f00a07799 43e637dd45d1fd1cb87908c7def3411ca5fd1881ab7d4a278b5cf112ab993eab 2747602a1fd796f7d79c84b91c17d50c9eb0f37e4f1a5ed9c05e89f30090979c 2e31e76adceac2e38f874937941f2d7dde46a64f78f3ea9eb0ac526de40e87b6 23720243a25c82542aac698175d6c875ac7e6e7f342fb3c4323f6554b4212c7c 7596add62302aa272efbb698d20d0856fcd021663fa510d3b90a5decda596844 1c5b6013b0b5e56baf6285f0aa7797f64dbca7b8c93eeb0d2ccbc393cdec354e b639bc0b28b3aba18cfc112bd1c112d262fbdac9728b6d0f9fd12362ee6a0373 ddda43c2fef0798e37604af265ab3416918848845111b55c305b8480910b7605 bbc3727bd349f9d72c48bd6d76b47e70506690929ba454149cb2c4534b5d6b8f dfa2c722ffaea0023a7270546f0e8c4c22ae99c688e92c5d9ebf881d311cacb6 7694845def4b6afead1d5d37ff58c5f48e2d36a9c67fce0c445a7e48134072c6 8a8e77b0cbdabdd4cddbda53b0972336253418177a352150eb4a426f32fbd128 99b285b9206af5a95ccc598ba6f2a17d3e4f740cbdfdb2fcd8d9b779bcb5a20b 94bb9cd010899912a9289e365cdf107ed925b774b2de7243a5d0809dd5adb74f 81375bceb20f1ffcb0b2be8bd3920e54566ededa4d9b31137751bc973b50e41c ce62618107904e3941647e177c888e0dcfb68f7d706d9a5fbdaccba8aa326fc7 5cbc834c34f16e08a43ba425cf54e893bb558e56c505ebe985762d0a86efdd9c 6ae08fa7237e54aac34684a72e7f686c14eb441fc2d217d4311691c3d4a6462f d2cad65416aa08463accdf530cf513cfa246e35fc4176165318c3bb393d1af85 7e3b246617bbe1017a6c711f40f8dc4e68bffc76ea5f89bf2fee1ed3be656d5a 5c1ecf477a32c7f53f64a2cbc7055225e844adbbba4ff73d051f94dcecfa0edd 6206f0ea9562286f0be73cec76cb1f40e8af088f288bb20ce175418ee9fe27ff 0c824f887671329e018167921e9afa4733834ab2cdeb88c31e25b18085694148 82323d8058350fc2ddc115a5327f4b7fa925cefd1fcf9082af10a2083c42183f 57b11af53722b4a299e8accb0d9ff3f7a4c1e1aaca41ff6d949cedf5bca50c3f 7fd632c1ccdc10665d0bf2d7c3a2f1cd2286cfde9630affdef48103cb01f9c9c 6761e7df5fee91a6d39cd52b8681e47bd78b33e509d17a4b8f77cccf02f0b6f6 ef871a87ca93de7c6b91cf4e4b9dc55abb494ecf9d4556392bc0bfa1dff0676e 1cb4cb88f23add01dd8c46e6caa1d51e8e4fb4239e78c559fcbbfb4da5d4426d b8329ed0a3d59c3012d4d0061155922ba4915f08d5a337c4884113cba5697338 b77829ce8a702534144bf01bb1139352ed9572743281f205766c4ec13807f75b 8718b18ce26fef4b49a2de0bab84bb00e2d41a9cf94f4d97182ecae4d68ef759 e306a792e750444a2431efdc10f7d46909bd1f586db4f2c5cb9fdceb3daac12a 0c692dea03e1e82956202ceee95139b151b52df02fa37f480c6cf3dd1d1fa989 adcdf8d1aa100dd1a1f07c28dc03efc1116f9df8c905c1eee3e3206aa60a6565 559622f3302ebd848fbf2bfc20687482490ed117df0476845902314639fa0d36 208770482b28dcd3e2b3bc3ac4fde6ee0a587c20ba11aa341aea87cdb968e344 7590f37689b672a4cb6867a4e23804153016228c2675c98c8795eacb4d9c606b 02a31b6cf2c3a807e97622f89955c55b8e65bed7987e125db0fa5c72795c75d1 a586045cbcdb166a330db1c1b47c828d8e1729c71cc86b138aa4e6adfeb2447a bd1c6863d90b239c737a1ad20faadd8d120f17aa06348f6f39dd021676837f10 c7b0832c8e6b172fff7f6355f4a7e5fba8d52cbf43e9fc6190629b19199d00ea 2b6ddeb8a95ef7d9c0201b3d7a2b596330fc3602cb9eeb46833121edd0c7e2bd 68b9801d12608504f7f23922878d7df2775ee85305970ac7b753908905a9bc19 d902ffa93ee605dc811eca093e3308ec23adac2bfe4d28f87e0df372d468c317 cb17d4f0faf4247b1a6efbdb4580009af37f0d09961287007461479172954b03 15f20f8f4ed5107e214d73e74e8046d3e47cddfadde77b7a413da22a0dea6de7 b8c96a49c8614ce6c3249269852f3f2a2c8c0b7d85224103f969d30ba171b6b3 2ea6adaf7e21eb8a473ed5dbf88e876bcace9aea3fab09e867b46b8672cf9e58 a470bcd2833c4c1dc5648f2f30522eec5a9dbc58967df22e9c088e18ad9cbfda a82396e9a91578027f6693000b957061ab68c46ab0d994a4613845318d3aad12 1354cb5aa006813a0a7bc70af2c1025cfb43cbe54b93199cc503a6002046beb4 2904d7fb09657897651654275c8f755e8ea5b8c0732405d6ead652541c2dcd60 a6e6aae5f687c0bb118a0a943b557f7cc2e8dd272e4a2a6a3af3acf1d447c021 1ce00f21905cc29fcbcfadb315167cbe8cf1f25d01438fb9cb29fb7015b6bc77 d8ea27f42170b0568cf695caf684718a6047c48f9c767666f1eb68897e634eed 4b1cf993bd9b104677b2bcc21e17df9b07fe9b24b7c7db1b8083911f76736b17 726adc26d7e1e07b05ab7a69605f440d3016378579334d2f41a126bd40b778da c3d5f03c6d78493e37aafda9f08c00854e83d7c9b5752dbfe2505ca66cb0cc1f 1c2a2ddd3c8f47a3d53ac3399792e737a5eb273df597b91c51e0980c00271a32 5c1497513b92af9952416f5d6389bf06e878d5b7c74a6be560c0f603e8a895f6 9d7e18494aa0eef02b472fb09dc5bb7e7382832470aa467ca9ef8c35312d5f26 443a2d14d1a80065c9ce7a49719233029ce2645ba38cc297cba3b1646a6344e5 719a47f2b49db1faf3f2004c621bf4882e85fbfeb9532afa46318523cd0a5384 63cdffb15f5d9b38132da9c5098857b8d057be9e93bc560e9f556819f0f44b31 007976cff6e1eeec071b20bfa34aa21c1327b58ea5998a21d89cae43cb16b3f2 e6b8bfbc7474f4eaeaa341c6bd9622dc7b3fc46298136f00364df3bb544d0eb8 2012bf2b166e0b3a4bd168fdb97bdc56250ea8fb46d2ca258a8bd68f764c4714 7d3261e494b882a80f676f26c592019609705ba973bdc96df761ef6c58a50589 0adcb2ec25944725f7ccae90684a72483e5a89136f6c1c35e7dd85408f049c70 d1449754f2ac778cf49fe469f3640b08620d9b0ebcb94cde699e5a7aa8f06927 99518ea793677e958511a5a83aa6abe5a3786bb2b5c6c52ff5cc45c19db52463 5dade6acee2be647056a284cae3929e68e0ca5656d3a765cea01464be980f8b8 c1f0e329bc18ded72fcf6bbe58ba4a20c9f44962da4d9c131fe406336078afe4 85acac8083b663e7bbd67e959aab47bc0233470e6677a0624d2d7a27bf1dc50c 2b941164b648882cc2deea6161654dfe77d795675ce1a2da4b9e3ae3b216882b 31a075ae784b2a251760c65c5c2636004298965c0d4dd83353a6527712e68990 80b4bdc83bab79c6d1c01061675165251921e36b8d348bdce743767256f41b2e 1ed01951754cc1082a6c9d6cc294ee6018362b4d73772c3cac130b419034359d 33e92acbb23316c5a5add50e91b94f19e140ddb490c5a6c308ee4ae4361dd327 c1859434f71ff05e002b05a8358fe182fc65ede36ebe7ebe4d4332abd12abd6f c1f528cf63af809a4b12ac3eb9826bfe0335c36dd3e5eb46cfc2d593e4e3baf3 af6ff8efb1727c509ef5c110f18bc3891b20c395a02743f921da0e96034b84e1 3cad88ae93f860fd8149a58eebf18cef6a809123bd4635500d44916ea0cec7b8 71b5dfa50f8aa7264b711cb2f09e4541e3af03a999e01518ae35122e76a0e53e 3e0d584e03548cff1507c29f42c2fd4b14ed4505b4976a4f2582eb9d48eecce7 c51d26effa49b2017db1a7929e21c689087edb713394454c6b48d3e39bec1524 fe0a302fdcd25f706f05ee98f44aab738d56bc328bbe23efbbd936a44818e0b8 c336bca41b1fccfa1d3e7190b2da2bfe0adc9cb7277e7503e3f882eae8db96a2 ab672b002f1d6ab12afb0cf7269fbed397a193d85ea20a4f0e8d10a7937d7cab 78a1be704a0a31bd80779ec75729f228c20715622b7137b13a465480848a9f91 3d6c057a59b7160b2845e6bab9b59a43aefd3ac825d9e00f150e88b1aebee8de d727fb0673e88ca958c3c20954cb517046d2e5bef4638e49c85593519418adbb e4b8326a9f23c5e0ec6926fdb8ff568302615068cb04be0c49ed24882b994d58 471f61f48610d11c12c4691cfa141262d67b3267c8b1756aa57af631e0e8d413 324c49124e55c3bd1c03c176db8e2d4d32e78487ab91edc3c22685038057de69 0a5b965391a173236da295912676e3b8a0ed4de4ed106c74ea85576c99b8ba68 1e936fbef8bc8d7b437975eefff0029aadf9d9deeaf69a35ff9ba8e0c3168138 b2e2bd865b699c5446da84ac73e2a398d58b8335131dc3e31945b962b2986430 f6e1ea52473351f8025f91599243a4789d4319382399897abf00ab4faa56929a 06cdef814943daf8cb7ee19bcca49f1116768019218e2b77a73f1041b6bb8752 deb834deade6c3d035f3d24c1037004b60cf75bb884d7960ae7545b12335ecf3 2e7f2934ff424f7b8c678a46c74e64cf6f8705243d062f7627258ef606ebe306 e6d3d8765858300f22a60009ad73d67b520451fba6e73acd3f7788af1602276a 3c7f9a0a17eb959d9f3ba0d7531ea45b97837bc5f9d2f8ffc1fd97d54a6c54bd 5a016a882c51883c6ac1ba5fb7fba578d9fd8dca572bb22faf23eb1713e94ed7 30de3d41aea630ca751a6b8ad2e65cebf2a2616f14747be992740c81b483d57f 80c6658a0cf2a3d293a506bc416bff387230b25a94ba1be9363d42ae3ff4afdb da1ce18fcaeeb8f9f01d5881289c959429ad0ba6ef191002ed855394e543de7a 90edde2c2aaca534be02420f3f6f0be42ab065ab7c6e8dfcab02ae0a7522571a c617b6b3577fffa1a9bc9070c08a791f1c04f9c7ae94e672e4753aa51711ce12 b06cf397c842382655d3c8dbdf9d824cdb110012ba5068caa32ba980c340a75f 3f6ffc5c17adab84140d2b8f1cf9a04ca83e84a5a3cffc9d45bab9b767bea964 e4543e2d0c967ad36ba017d67c4757c6adcff671af3c6215b9d38ff2f67288c7 8505beb62a505e5380ccad710755ff19e4a282289a0d13abf039a0df1c50c60a 531cb61693d7c5dc5382954d4e7456db282fc76e1e5598713fe2eb4a06249bd4 8266d0c300443f2a4016036982420fc48aa51f0a77382881b971403faf65e96e 4272c029fb83a33fcdabf3566e7f16ae5b1746ef6a5d02f82e80af7693e4fcc0 5f4eb25e7d78d334ee32195cc636f23a33fa8bb879dd485c1e5b463896e94f57 f03a846c12a554ab625bb1cff18b07c62853db2c3f7bc16cabeeeb4eb3e34269 d348eae2bb0faeb8a8be39877cea71d5b9a2110f337102614ea04271e61f6d12 d340b0f33c597f4d1dab312aedd812e41f34be78dddf4c81ab2e89c3a42174d9 f26b2e93deb83b99dfffd14ca920f5030f4cc409c6c3b518dea73274d53054a0 5f4beb0bd57966caba74fbdd96ab317cfc576bbce25b80904a34ecb66a6037c6 bb85ac7bb06ba7da9b04b0857677ce7d89852e6c8ee73d1afa4b02c7982bdf3b b939343697c1874c8c44b38e3e38c093b11c46fa53b7f6125b6d95791218401e b0aadc338b4fe90aaaf1df60f77f819f78a93f2f98380b757f8a496b4779e751 e53ecbc8fac6065961cc3e52c001cef50a55af18a29d9c2e364a4bcfe7d76c55 66a2b50c8fd2039e7600eb765233e8f10c415111aeb541b13957d69959079984 43a4b3c6268e4925031e2735573cc4b84cb200b1dad3e1402724a55e44b4b1c6 203d0ed9ea8d9bfba1b17b1a1d48685ba8bf9eebf27f773e500b5c155e797709 e662ea7531a3818699643edc4e7564a744d8197760d50e6f1d27aaf2f744fe50 4d781d2c3282926e3c3e5da802b652a85e01b8c09b8cfc968d34677795028e42 c2aaea2852a2d0d17c1bbf22ce6598d84375a9b8ef43b1623a613fe55b0b5830 10fae3b4f62c1d9fade4a07c78f704501bcab000aa6ebac8368346fc56e28442 8a12284a3ed5b9e9bf8240743b7ca62c97217c39074d3a786bdddf72f8c8ea63 d2d54d068be08a3e89d4d1ecefc5c26fc4fbedf6585796ff45534c3c45bf1966 1d074a2cf53bee087f916a5cdf83005125beb0ae93d857ff79ca40a3f9f3000f 0da1ca54515a8ad032ea7cd9f418d2633ecc70358f899e088d675f3c9bfe053d 2fa394a6ab15d042bd437b522d0943c7471d3132adebe6a9ee2af3bca7792ce8 1bf567e698803cba09fcf1644a326a3c372226eed4e224e1ca3d05c13718847a e5e6df62f7d008f69db94ece1195e0aaa977bc3ee6778f076d14ee0b69829eb0 fa9390a889424b18da602555b8f31f9b95e558e74d87007458ced0a08b01da9d 8600ce44d5b0438f26d8daa84e086aa2ba94362a978a014d720beb9aaa69f8cd c8ebf8b641a86ffbff88818f1b555adc5c0914c98c6de59ace34ea044c9ba2a4 a7165c5176759f59f7f20ae74936ab138e7e48e9f825fa89813dd8045750d9ca 242af7c2138dc97b1fbf8af9389b40fd25efa16a34a8a91b225a529f0850ce88 88b24b412a06675b15b030cff19b405efa4e4385e36e55c8db45c9c5034869d2 bfd03759a8802f4da3ace83cde1298b9c2fe35e5404a5923b34aef0558e9a866 2120db40a066b1a05fd049b813630c81604a178cfd789e88574b64504beea2ad e1e71a7485a87aa7f06825cb9b02aeacfacd5267ae116f453d76d3ef57989336 a856604fe37d91401d9e86f04afa89bb6d4e4e859d4d669a98675cab31d7a413 f2762ec47f68e6a59d835fb861b11bf38caf5072fdf72286d6c1a0a62cbf79d9 ec755d3a14e08db0c20da5eba89494e7f5fa2f5d4de8d4da5ea04d9928a2de63 a9f883fbc40bed5b98742833b275bc168cfbdb70b0d55bfe3f9fb359875927b8 bd7c48ef6ec7d2dc20fa203464ff79cf103f6fa0d26e940be2eaa9b8fe5192b3 ffa0a0749bfbd3b1809a5dbd67290fa5d5237fa2664ece8bf71cbb68344ab357 b9a2af0d3d6464cc871bebb7f7ec5d55be375f1225cf2c27a074135447ad4735 f245ff2559f3fe861601b2857597b88c58905f81f039b1ca477a70a82d324e7c d10056f70d78732046df829304072aea79754be04cf65b50a0c77a6536d6ea17 3626e20419d248f8487261d3a1d6d217c55e2a59a611ac9beaead117e668a88f 51f19f560267a65d3fec5a4f224b03b7f79443934ce00db7503d668a3c72c77b 7fbc0322e68beb26ee7804f11cb9d44108aa9b40264518d04afbb4bfbe435535 e4b9dff00050cad9498e425482e48a46492e60050744cd47e236587728fa293c 09c5098601a768d88b1d42670f8b1f2c029a87f097fb75a90105e97650ed717d 33e0a242dec84cb3fe3aba2d24bac8c818e51fee1691f3be32b508f5c504b8017156d52a1436ea2e47ea1acd4e7af345d6cf7e83882ea77468c3b8f445d43f074bf825dee386179331af9f127e5a86c7ee7801b7a7f02a34f71b7a739cfe520d6b0a4d2227ffd281c56d189afd4d81f7da516d3359e96927785575cd429eee0e46f45cbe6cfb4c6f360ad42869e5f49c57548f78d929993d377c1714e429330627b44e2b91f18889e8427ffeaeba943a282fa7fef8ccee02a8aedfbb275fd30c2235637d22df54a72c411d92284461e516056f80cf454fc33107c9f1108e88022d4f4bd7768aa23a5f9fc992ffa9ebcf00e3cc8c0394f7f5337f0595a669e3086d59b6991a2d73f0f525d4a5996cd38af05bd8e13da27662787ff0fb47f4e30019e3b49f7d563c4879d54b2954404dd4d27e93b22f8936fb3107f1f97caf3a0e5449ad5256e54ca24678042717d6e8de233f848bb2c0be24de62ef8588076f06d8656492734c01e95a194bcf89504196357f35bc83ff740d808e4948bf95400b80aa3aa8f3f371cf0bd3a0e5e01107a80d717f194bd7781ae0689a49dcbe530bbfa78f8ad292e9ab9c6907ff3005ad7604239637dc4af3eac8717132e0551c069a633a93864c6c5480577a1bdfb2de48b14209e5d2e815f8bcf9801b8f33fa0a8169083073efafdc4435de3b5db4c441566bf035a92a23924e778a2e0b1fd90956a0ef8723de2c26dfe5725788185c688ad27842fe88cf6ba45732f53b954c9b7ac0591f4c6937601abc4b8616bc5fd647c8c431deda68d0eac6ff450ebf48071f56fca2e2ad446110b8fdb522d2b63871713b0ab81de952498e70ac28fa4e03d842b08406cd8267462d3d46ee263c9f2cda23329006fc16e93e7ad14b3dbc04aa75e61be0b68ce7ddc3569f7339b69123d20654ceb3a80e927921d481bb030b12862f2bd30107b43802f6249677592ac72e689f0f590428d007e662b53dd1c047a11263792a9b6c75b3f4e5136d8475ec1648cd56f550f8ac1c9da8d89a6800c1e35fd8b5cbde9a1b8f5f06518f0ffd706c4aca9153d6c8a298bbf44fd48606ed6831239f23aa6c48f67115f0b625bdbc588bc9e315004d70494a425ad44b0379f8e920b69e0d2179fed508aab03f3a37811f97936e47e36e70f63333909403b3ccf7fda82d2faf697c12b11d04b2d075a8ff8c47da1bf02b4eead84a0f1206054ee36c705f28f3a6cdf0c21fc37058751f9bfd2a53fad84cf9618d7806b30c6c2a622a1c6c094597737caf48e4b585cb1ed4043a7ff446baa3d4e25af1580ead8ae2ef0775e769428efe265e1950ca145d95480ce233771a3bd6c2fbf55c09c6e09759e3dd479b8c5743889fab451778e762f56bc0d052d2a2f1569b8d410eac5be04222de2cc2a1bb6444b2e51e8fbe2ebc5affe6aa53bfdf5e54d32b280da65e66e9f1dde37b5830cb5b41d475bb701c4363b48e8f1777f45ef00f83b409f1f524a0aa8fab372482bebc2ebeb4869c9d696ae5c8d5f909b513b62ad4aa02188ad383e38a14f96949a25e2cf83d26c7d4b617bd838138c8ad646e50c00e0d3f049c74992285b924da86dfff1000ff72412cfb885c562d00a0d85c1f5aae0c3973816e3d823da52afac9f85f3c4daa40e6d89ce67eaaf7441ae638d432b5057175ae335acbad7b277d5be5af50b0286cffc07ee809acc430c09efe21f01c0a661f195627a178e0aed02472042faddc24c60bd126912649ec0f79c2d7046a0d84261d97b57ea07f40ce05a543b3a56ce8b1e4c8c35d2384a2cb121e31550e0897f9944b5eb06654b0434d619c92806fc81c44839565e8d94a44b1b5a78b3508922f6eef76883939b298c4f3b09383f96851424d5a96bdc3987a7e6119122308c17468b8252d500e3e314c888c0b55988707fca3258c0e65406c88024afbe50ae0c6783f9a9c92c5f1dc0a7f0501304ffc875d63120f4f2dff748fb35cd1a2012f0a91d7d35f461e6ae70d6af46c5800b7d348321efffeb790cd14f2fd4ba50d1c19a3319ec7f6aa99ed6e869d769ec60f3aea62af84be7e8a7e58b45ba0ec0116d96d6a1594a7c0cbcd49b743b05a36f61b35c28a7bd668fe8db118908a830d9649068e21dc29a08eb9348384f623b3e1dbcafca43d1fe56223c31fc95a4504affed9cc29727879cd8ae7b339f1c28d60291bdc5a82cdba8b202983c6ab79038cee68340989e659848e01b358a9abad09105688f4e5933a2b0d179cedc8a805a02bb38520b13d6f99a0218a1abe67d2f9ca265ca31a1aa14dcceb32b3b5690a4deb0d4e5df00f2dbe35d146ac4675ba1681fae50b4782da1d845713bf69380d3ea5cce3e4d77863c9ca52d101c04934bbc0dadddd6f36f8b37c266ddf533e077d35330a676b6d80ac01537bedd744ac7ffc65b6f1c83aaf978f59f60486f30128427c38aa9f33262f7c448c0c68d148f2040b82bd0740f5734569e9cec4260d860a9ebec4186c7c2af03bb8b1e9018fe4dd54f6f3d2781e0dbfc9155e41440d3322841a109be44de40044544016fb9d66a0789eb6f90710b0503c27fcff4f0d1ac629637b9f964b118192cbe55cf706d6976be328bb2040d52e5173b76452085bd38c6bb167dfdfa284a1f99cb1d6ca595d99fd36412f915c176b2d6bf964015b4d4e66a218a87c8af8f50754ade135745ab96809379463db2cd6bc0f08300e05512853031237f283d9b8a346ad328522935f09c7d8a1a2a8ec527d89b63b0218f8d09b233a7072be1c54ed32b7b3fb1a935fa4257f60e52bea8258446dee06fc5564df81a56e0aaacda40dc05b11bbeea4226ead0404f935b31c600bbff102df1625807ec4bd484004810e4f13392948fbbc3064fcb585039d5512c7ba060208e32aeaa9c81df12348a33e15932506194de209b64c4eb3e24e7378f2e147030001440dadbbdf540655bfd8b227ded5486373034db6ed91a509b7b59b14c9034e3b6cfdf5897769825868650d564d8be22e92f5cb37e84da84d0f4e33c1fb094df093b9c82f1b98f442f4964f8c81a38d61e707137561d98c509e666ea4fb0f5aa0bfd8223d20bfbaf07142fd8a23d7e1e79bdf140928134fe100dc92d24d05c1f3d25081ba9162e73e289df1b1ae2854b7fd0e2093b45410ff7c03ba64730c856cfe747f722239d6bf686b78579678c7b6e902e0f88894c84775a5de9cb30b277690ce8c41ca113f74f436344bac385c0f118875aaa4312dcf11b8addd0b016e8ce695f5b4bd2553b1b561fc2269b3d27d8e68545c3abbd02d13dfb155410fcea90e2fdd2b4d3a4220361be88140c97716acca860005e765314a85b7b59a07a1e8ee716c4b566af5216c2e7d15add7cc08862667555e0b640bf0d8ca5e570ff407f2787dc4483ac941e73a25c043e683c04cbc3bf481efbd095e0ac9182b0f3a1a9196bd7257e8e9b4e69bebfd286f3fcf1cf27f103db867aac4408032260a1acb03fe05787a607681991aeda834e39e01bc266fba88ee5eebbfd9e6891e0861f5f98eb029e3772d25f2454a987e2dc4191542f7002d15318d082f27c5b6088472291bbd6a62dec5efd8fe5e4c261fbfa739b16fbee744059a3d1453ace5068762e25445421259f05f0468b1086c143b8a798ec4a790828d4da57d402d6603a05509815dbac319b48b016ecbce896167436b952f1289cefd26c85716191c01267104ffa70768441233f02c0732693926fc2225aa6f1d1c803e3a7559457d05b52140691e6cf732de9989d9aa70a8a8b370cefa69f86e77c9d8bcbb341655071dee040b8276f48511db1d1040beeae4107837ea04bad584a6ea8ac098e39a0ec7c0ec6364df9b40a0626e786813b0923628d2c07090b09617f80c94c4b9200bfa8a0dc8f33bf3e51cf46f6f762c95d875656d636fc92717ffe1fdf836c743088cf9a5b2469853254664869dc44bf83401fa52cd3976f332c6438b0c0aaee403a55b4eaaac45ffab04f538a9cabcdbe89dbb8660c40f4a7eef1121ad1db1c70036a925a724cafa1bf8de621e116fb0ea0649025aceb2eff956e11037f70c7501dda09af5cfe304ddf711c2ddefadef3e9bd9bc8645637455a368e2bb6c50ad01e07832abc2757f1af9dfd6ce8857ed491abe29165461a2431eba6dd464bc6a09ce02f27e3bd73d19f38be109a549d299b122a29b6d1f5cdc24ad74a0475b8706bfc69fcfaa03860d4c144cbc4965f0d876569cd5fc3c221ce79257fdeba83001f3ebefdfb9aadcd041f135e41e9377bdf523c9167497f29e1df3e1a153d8cc09b36d40af8872827e30d5079e8d3262d44702adada61a80009048d53d29332f01fe5ed1b29d5dd6cc9310f68cb22b2866e9e927ca2ed4e9c169b28a07a6039300a919e8f0814feabe58a0801c3f267cec76dbd9d2392ddaff0bbeb3747e572a04d4d89366faf2266ebd625e88c86d74aca73e12f60e44e0d59bc0bc4e60fd0c05992a9d84c29e970ebf2a11b58bf9e086ef35bed0051118f21cd5e5475c48a40f5fb8d528bfbd1a729da19a2bc75dbdccb35f93956e728f6ba6ac88b6aa38ef0eadaae5ba9ec64469f315722aaae7d50fa46733104d1616cf01ab439f0b9e6c0dac271d9fc8b63483da3d9c3507a3bb9eea858f6bbe16347aeab045dbc5e6210946dc4e1b026ae36b864672487b5ac514227ac45582423b76bf2af79c64eecc07b359491d48c79bc97fa4bcfc1860f53f327838a21602b7349a40919545318b0fa19b7f78e776ec04767f2337aa5ed75f3c90e4078965bde27651edc22e542d028e5932694dd14226c1fcda27dc2b5f6b17cd142c9e3965478827edcf74e6d902f62afe08248e852069ee8a9fed8111fde93880b508c6f1758b1aff05a685f20906ba41266085d92c025bd0b2f1f435532d4438222ad32a42b9f781bc97656903f59df8c816b10e5ad1e897aef099cb5e38cbdc73c4a2df4800b043914e55440565b85464769258128f1e742d9559d4a4990b43c39d0a30e42a70d89e99b4250dbef2aa2d93cb293735b29da0b9d8b7454f4b71656d4b101b4ad0dfd384695b0ea37ccff01337d7d665e831c8f0fb3b873c82de4866e02c0e71e98fd6cb86610034bc02c07894b8780a0c6aef20749cff5a63a926907d761e382e6149732a0209e0c4d88fcd743655275de8c288057bc63deb79a98357787329f525b5d9c95b0a715167f42ce6582d420377536b7aba9cd985310412bf1e44dadad02df845350ba385f71bd3ab88d61396d22dbdea719e0395b0a44d1a5ee28ded569c71271c04465aac398d6e9219670ddfb8350ed5c6de210b5a0e2a56a62ed402d9aa1f7302fb8dd09c11f8514591cd8e4184120b4311e93bced1079815418dd8bc658f74025eba71a473989501513ed6f7eaad236da772e36a2356d66ab97e4b4de9dd080dea30726f28714fd82583775aafd32d37dfbf31a456572d3f3b1f7b6d9a54cc07ef15650a33ef623977796f6046d6e0317e89d07d142ca7ef05687247089e5a0d9becdb8609b42fe31f497c0606b50673da5cef04e24ef7cb3b204b2f8b82630e5558f30740e956b3fba74c183111ee1516d3deea66e09d5991df1dc78513e80ea7d1b7db6e3cd88ea8adce185986ebff69290ae153d6bc572b7023301118540237b0b139f8808a5322af331ec4f88fb9979862600a18f5405d22be8dac855e0cdc4722cbebbe3eefa2a2b5ac8cbd23e7b73cdd6f52d0c4380346004162b93c00f703c4aabdd6e3f563a1b8e2c73e6c419adbd15ac24d7c022c0a89b8e217f9054c4967e1d55a9923e2d5a6ac6f1e7ced972afa8394d926641255485e8cee3c05e827ef0fb56bae807232a57bbdf075362b0a2990f37baa57833a2511ef03f7087c3e249574c3cfe36ec734e8f44d1170c0a27ddd9a5dbf5f610e3541f0b4940ba3bf4b8ad5f9c8ae9ac712414103a3648f02cb27a5785fb31be0058233f4890293a227ee1208f74846ba447f32b5b61893c218580db3bd77531d555017289f0304066946fe0b9a4812cd5b84c98265d9750b6def03282cdec50ae5be74e95805f1b90c9a237e9b050b556b8e3caac9ab211d7587579f6198e03182762f361f069c87941b39462567d8bb5a8ab73b9c4ccafc88c652c5649e83d71638116933081f4d16b6fba047b68a004168c4a3e7177385af05119bf7ada9cc133a5d2d510660026ac195af83c4482d931640033463eaf05bc0d377b2d379d91b90d31e9501eeb79fe5e2ef6084ef462a24a5dc49c0c4894e09e4ea8f3ea80974503aef230ed9d6cfbf8021a7d7c5dee1578dbcd86c36308f77c5833becb50cc8cc2b1d2e07cebd92cd1a6780c1bd3ede3ef20df24005733f78394081c99728a0803f5ac60b92becce556c0592b904b994484d3698f34fbcf51dbdf3c6a8416bf022cb6290d49bb3d3a31e5360b0bc0bd4faa95bedb47f6f2fe67d642fa58bf9b7cc7abe50e86cc11abc2cfb003b4b43e7190face776329e02cf5d3248da09d2767f1767f03773eea5509f54ae99099c93a882a7889dba0b1ce5bc08234f3a33787f04fbb09bde3375171c684d2f1933d57c887cedec2a5cefa8cd0ba9db9615e36c752230ee5f1300a96a12c38dda6629810ec2f6496cb041a21d0d62736e380218102b30bbbcf068613e270f68093335004e45979dc52bae659e872b0b28751ff664e1a0acbb40a6c9347b6be272ba4c9325119bce183ed6511bbf865448729e1c1e3b40a7f2cca90d201f8b25573ea6a10feb4e00194473acd36e8998b240760b2955300ea2279339242d9dd230374a4b2c18b565691790f8487307c074419a04c3fae00a7f6f2b66b345b4ca4741cd31f98f04ad08dd5005da89e18ee8832cde88f800742d40048dada7c9b2a7e734a1a273f4e688ee430a203076a9a7335fe467c0b048a4acf0933f1ec7a5dc68bbc8abfde6320dc3b89cd9a87a0f29fceb61cafdc09d199872c37208a46d54774211e5823f647bd4f203f4e5207e4dc89fbeab86b0edf1b3b200c85c58ccf0616ac21232bac8f3ca0a1bf0c46036ce03ddbb6c3a40602936d5b3990fc528362f2e371bf19c8ab08cbf32327bdf716cd59bd55dd000010e3bce370b2770354348d199a6b14eb9a7b3a049ea41ad6616f2f6bd7255408c38ef4cb32152bbe11ccbf1087b3f1318032f041b76c199131a0113fb223040e744298962e7e9d5672ca572894e38806f7b441e8936344f4e3a14193c55b3201f9c7613ebbba30481d70129b2234d64286fd39aaa73bcc23b8f2a55c6d6c000512ae5ca1f4f6cf456a72dad77861054f7af60d20949c33c03a47e2f5b140380ae416e41040d90d6c633c04221be032b3915ff9c2c2e82c4fb88c0e6aa31986092d507d338d7d27cc313f5131905752e786e2054920f3fed6591ddc49d6df140b23f27215dc7c0993328fc14b4143768a3c8645f66ea3b55301dd2827b82f760970e746bf298b883cffa917a35e85c6c8801fdb4bb511599379217eefb0d17f04e9df8e3aab84ff09e5752b9d82d965da82682458f0b7103682c2da08e0cb2e0bdb0b77784b526ca73079b88209c484ee92f98e383835b091f1c9f9e77ddc1705b45892f6e2c4ff7839e5a034df9c8ee4ebcaa700d6f34133e3e69916326a9b061c996ba354292293c582619be009fda25413d9a5d81a6ecd12e040c19cf33a0dc0bf4f3aecbc6e9fe293c35b3633b8d891dc2e09acb39e9cb4d8f9dc95201c021700f38293893ea96c1778f9b631edc1599ff04127b836d0ddf3bbacb2703507b51153ffb276c84b4c6cfe4924fdf895b37ccc966223d8b66bd022fc76cd6006745cf15bf784bc9f8915ebec3227d120db8a4f0ec514362afd476afce9a2ae04d654901dbc9702f932aba139b527588127539b87fbb42f93752cdf9a87d09a0c7e80d586536dcab479ea06dd517eb213b76793cea9e94a02eabcb2d3afd3540e5ec438fe385fd1bd3cab7e99ea39d57765fab1d65d4d13976037db2e21bee40c8887a352bfd49ffba2fc618b8223ddf0a46598e760518e5d847a8d4986b53b0b8faf874ec5091a4753367968e6f783159279419241f722a71b0ade84a7df4e09fe50c31a259db0cab6dfec3e7a8835f604e9f6f755b2c63e3d4b159b4c87f20867394b5a2c5a7ee3a078a5b79dbdec2bfa2d78ce08a3d027978af0e5eec2280cf3effd5604d5a09d92b6304bab76e1ac2cc901a37d4a77c9c46e5d0849690200f7f77ce11d51c5ef773013e08dd81abd57c2afa9813ea7917cc92ea241ba360e80bfbce721f76ed51ec701a1fab26ed5927c14d0a4241888618b3269d451af05f8598c7ec8e3cbd4602943a68cd833af3403fd40caeaeb01684ae3647d1e08081cab107172baccbb564ddb285fb5957d619d368f9cf663f342183b5aa3b01a08edd40208be4cd9ed4fa617f9cb49484c2449580f53e799ca8d880bbcc6be5e0b65d7e69ecb5a3c6240a47bc8bef834b4bf70c832698637ec80ab705f4e02d0056409c03d4cfecfeee5228e76cb41998c75381dcf8a54119439fe654a6e3fb30e7a46c8d0b4d809ebb0b3451bc7a7e0739337638abd96ca7487e3daa9153505024afc4f7d88cf5185fe851966d1894c67352e81a0e5fafe2ea7c1d83e76bd5c03bf417b5bb2027bce42b5a7dc8814028212ded55dd0e871aa9d7b83cfc0c0e509532b2a9d5b08cea14a8ce07d9e47dd1962816ee98f5dce1d4707509d53afc30c5203aeab43f576f497b9fe3c640fb8190ee2fde7c02755232870c59a126c7a0b60cea194401971addc07f7097b4e18859dc494dd5f011ded59d163ffa0e3c90ce1c763690a0d45d89d72cef6c96357b999cf0cd4433384886ced84fd09232903ee8994c37ae8057da9a3e0edd9a55b5fa778242e9d85176387212ae43b822104b137a0689a13075731c002f749d42f0c5f32d8dce7f899690833c5c648e8d70f53d1c183299df1f734ecdeee09dfeeb084ece0a71829337055315141ab03f4088927007fd8ad480206d378b483eda6b29696cdc4d21f3f4ea127fbc3b89ac80597c6571833114284a646f12ff40e7ce4cea81e8522da91ba91cd1d057d34e003f0f57c2901505261d4e296560c74882c6b4f857d21f40bf77184f72d33ab750e45ea9da9aca56f7c85f9de47fa0e6130d043456ced3d4281e23a29d0f3f6e10fdb6ffb7fb15084adc96903be58a9c54ebf0c6a6d1969b439ad93ee1224c2f9087d8eb89f272d87503c70f03a0d940928bc0779036984bfd3870a7184d852a60135ec61513e78d7dfd3d49b3a2a12f7c1dbeace1806cdacc15253d02376dd200a73e393ecd6ff521dde5408e977ef1d57df375e7fd23ed78911b227d2e32e0105401c2ec32c98903a69368875024d0ebd3120fa510e4dad77c039f924d0bf7103cb1ce1ca715c896d1659239a733217791124fa2ae6940d8c6ad00469abb2090df59c4783ace298a30951a2fcc83e417b0b4f4a4287959d116292c6f766f0b90fe7d01c495276435f31db416080506435d61c422b0ce5c80a826cb566f5acaf094fafb2d2cd246deb4f020e6866b0fc5b18040a1ea20845f3f8423a43fbcf4001c2e8f3d7b484320c12b7cfdf65d5078b6a16fe63b145b10c06e3cafc9b7a42031fec69001e7b08f89e3f3571d068f753e9fcfee7aeecd3211992ee4924a10c0295d25b0731c05301dbb7553882d85511374c98251e505fae79d3c42a2cced506fb19ba4baa2872454b2dd85ae90f17c8d596024b913b80ac8eed6b17d7ef3d0911f55bdb63a8e6c2dbc0ed42c67a90cc1657d3ff11a9dd39d3aa4c9e88efac07e754ae5dc2484c3626d642481b578ebd9de25f0308f9554422a264791b031404d0b8bd150842b07af81f42b1a14841d06948d7ad20c15eb5af74622cef769e08e14bfc506c66fd0ff661bf6461c44be687134c94da8817b139f8796a8d54b50c9eafd44b461cc3bf0ddc045f41942cc0be6d4d06604429fcb1999d71df0f1902d53712b9ac7b140316e1a2e71fe51ed8019e481a6e05a1d3e02679bf4779c607c4c372a799b85cef0ecc776b11073832510251b6f361b1ace5b8100a8c15750cc8d3b9726afea46b807ff6c971ef3278472dec98cebe270d3153eb75660cd7022ffeee248a9bc549b90feed5b63e3a7ef92d4c39a487a0bfe6c20adfe819e5005586ae9680967b4f7bc11b72baa37899fd34d8cf08dbaa88de96371b2e2ad90c91bbc49604005d8dae7ccada2f2cf37f5f7475903e4d8ef05da9b3b037e1830a92aebe17893eb3163260c2bb96f6d2f76cf31ead2d6cf5baa0109058caf2690fa239a4806b2bfc5ea1870df9ee023f63cdfb7baa352d78482dec11d73612300ca8084a327a12229702a10ca5786d334584caf716f93802a297cad1affee0bd0db3cf79c9d549289586891dbe0abe1f1951be01fa29a53cb3bd0e4a21f090c00f1dec1380535e869d7735136df3a1b98791a81f1b1a3c8da23dbf7cd9d15ea30ad7fff3965b3e06ce280d788fe18662efb6fb53659f479ea1bae98ed013ee2a05c2e88f8fd89d1ec3cf0aeffc460bfdf1fae53450e685daf90d45ddcda6eb040f5f397e09b28e102d90bfe414c7bf9c7bd4cc5ee3dd56fb87ac728493d8612e07b233ed2f392dd7a4e14652a5223afe222ab1be773732eb15cba9cede242d3909da6e513231c25a3720cb2930682ba28be61f40344e2ec7739b2b831c6ee9a4054275cbbcd4ca95a53885087640f70306f37200617ae8834902b94681ee64a40596e45c860c86c67cafb3c1cb8c7c00998e2e12d4559d8ba34de32b27b5b4c2032afbe59d73a5957b1315e0a05a50963d8174aca28618a03a40871d77e97c2e0b8cb1daac2437184ee988e58cd8e164911c3bcfb3337f2c02aaab3f8d9cac50064f098694ef0f85f29e37cc7114793c1b7530c87475914bd178d0376d25030e09080641c5d671544b2877623261f1c5f6d1c271da3506c6bf62fc588516fde408df00c04213f6524f7fc761c3e34717511794c9672417aa8c182f2cb47264c00f54e7f182247e62a6bdd007dfae0c7ebc2460619a8ffe04a3f53173ac613dd9059d794351be009de7501e6a94d91736a2f4824a4312b6a2811950bb9f446aee0984da9201a9d517182b72bb75ad40074c2d4f49e8d5342442c908cfd2d5525c06badec95b5559edf212532773d10e099582e51afe02be16d5e765c352e00cc60b32f3971fc13c1e6dccf9e6a1279d46be708cd6a832d2ddc672a54fbdf081540d71a91599f0d3609627335f1fd655246ad55c0f0bf9ba0a51997a9f39f7e8c00db3b0bed85f36c1dc222f90944a9cc797a4731440c929a4cc49115b2702203901692a310cbde5ca42c5fae43d58c0dc456b9974d41fce0e95b106192427ebee04d2303e96cd44cba487045b21227caca993d99459f6443d85624855c3ea40e507496e42a42b3069431683c99c0298f7a19aa2012d2f823f8b585013295e1e450e8f06ddad5182bf99d976818d7da4202dd111ff49198da32ef0d22790f367010cdc9260d428cee85d25ac09b4e2cc1f0c697edabb3e7200790c3a0879b8eede0b5b23165ee702a7a1205ea7c93f68b89135830f217f98238496f86a5eb12eb3023eb72e9ef38b3532eb691aaa84d48db059ded7e311a6f0d3a83aa262a52ef3013fc5eb1c8832575c74d9a08a2721259fdcae66b028ab8e98e07e32d004b7290be08469ac8684d88dcc29b4ac8f3d1618010abda0519b68c99542951492766607e3f05c23820fa909a24e85b75b546f3d039d031eaa2ea84001ed16fcd52aba00c46cfa8dbb026a0482574f5de5461877230ec0d94a05069223f5e0950e691600948bb34b719a68b0ca6f2abb4e8a8d76c828979dc450ad0a937415a5ba3b0f0484e6fab805ce6bcd9a6cfb5a9a16e3778ca6d381d5f75fa58a4e61b61cd2070fe0f7443709dfe6f554d88df66130096462e553ed2c18e1f42cb315f3951d240dd5e330800b8a6dea7a8ec07cc614f7dc93c80bf773781b8ab568ed2425a9fe0fe6da880aa071553948d3184856cdd51ef6bb61296c7f6c6edcf1473ee990e605b2393a10d4e724e29e80a8ecfdf9ee301f9c3bbaa2cd9b1220031540acdacb082965056fcd946e1e0e52a13477bd808d7156cb799128752dba1e02b565b2e40a6ceb5d68b43131010d205ea7bf4b114214af8a06e18e9cfa8f18bfd1c10d4a03606f5720039f9dc5e52e386a35fa987648db5b9dbc1eadaab13e8c803da22606a1837d6a4b65e51030d7467e5c427e5e2c369ae924864ddb48f25c09fb77da05f2bac68e42380d873d4efe5d121e7ae2a3395348b1737bf68210dddb5e59570c72a542c20ff9da0d8041c9173b2abdf28f932a2c66c86651c853bb2cd075260931d9d9582c2a96e9cd97c8f693007dfcb2680264fdd53edb9cf21ef963f0af0155fa0d0d4255b5cf56afbc66b88fca599338ef3d891093bc60cf96e1db48d70e435e2186de05244eae2c171e036a819b46f4e755dd64e414a3838ed6138a9a0e798269b384077f0c2a1a646b027bb48b7dda9ab8ded3320063665b54d0b6160b4a9883d478381074494b8d1887d67ecbf90d769c53baa979d582d32e03e262011029fba5782a59e3cce0c2c10797e3b8a30ff7a513f34dbbee07dd231191090ed898353bb6a6778f6fef78e14d945c6a7d2459d55c13bb7d67c3d76b78ce480a43a730ad2cf6ddc2e159765cfe393d82b43a71b6284d6284904183ac2b4d000f8cc493f1d448d55394f9a97bb3ab840082e9b688fa57a99bf79528a50b72d90a91e10c7aa5b4c3bf39bed5e1cb6d71ff081b7b1afa8f2516d483aa10508ce40a07baf0408156756ab9d7af34812d076a07cc3f6a9ca6358f4cb968145b8b3409463f8f0da6ff4c6844877ff788f87fc1fff5c8f6c93c33b2cf2311658535f8076b1d11bff52509b3a4abe92454aab3afa53decfec62558734291f72ea30d6800156769844558abfe2f775ddbd0ef50634570a3f304271def36053ded618280080a21aa5ba9e2ec3a4d3e0b3d93a34aee8ecf1284f87802df07ac7ccd56ab6009f21c3ff54e0fb85df062d4ea2e76d936089addb7c9bf29ffa8ad6c5c48da5805063f186f2f240d1aab131850e093ec28a15c31c7054a4dd1fef23a98b01c5203a8e2d5207ad2e5c15f8cb5473a08c4f22be68e419b515647f718ab6e05c0dc058f7400a3f92732000770fcc5cf01f28e79ecf190ed64e39e9c09be483e011c0f568eed613512bf65f0a64404c5e124618117408aad5327654138df67eafa5207e931eb2959e0039127d9088a39c200ed0f998c4355a25a83022aaa6375cf2209618d5cb34ddf188c3d7d62711ae90418db8c0ff543fdc03dad68661f532db30f97c6d0bb1f1d642fad1b7c03ae7290835114dd3b97a25f250b68b5c9ce095002d866120383c2e6f4dbc89cf3db1b9c659b19d6117d508e084133fc605995ca04339420606156f3d5bbfb654c6230240a9bab327aceacc186ccaaaab6fb1c0e0bffc053df6c6b84233ef0e5009941497e14220959540b7dfddf4f455edca8c008094e59fbdfe58e185f4fbe355743bac74e7531f895d90adc49310d660721c704174d9e49c3973bbca2848f83332124410d7cf3fbf6f466571710d805174f6f0ccbfb539df3c6376c5f93b328647016b6d23ab7fee88c1e778c3dca4abad04b017e30441a474ddbd365f235ced7d3567b8dfd093bbcbe9dcdb1e5790180e621016805f186a4bb123cf98302f78e4a1ca65704269cdc2c0a1111c7ea6d9260ab0d6d1ac8751373859bcd058bfe7a1ee94c062a864eb82a7e24512b11298a9b8c03b7f7010867db7a5d6ae60e2b00932dd0839195c4a8c43269fe1d9e05774e4c081be974b076070ae02f3c16811a13ad919054fc657d7ae0f480e93c749cab5a08eeb13728166a7fca421c1c9d1705baba84c2add007ecc23db8c3d063987f6d0d9228b1ab89214c3264ff07e067fbfc02c415ee373c792c6aedc31cb0b330c6036180a931b133d24808b999267de47054f1924f140242a90a38cb0ef9fc3ac20047c9d189d9e2581bef1d5a57d6e45f24f0beef52c6ddc82d2963a1254eba900298d72d837b3725b3a83b1f5ad8cec9143ef051a589f2f4c325a14c843f0f93043e06d33823ff34a440e14c304b3353d2dffa6cd6d8dba1c437c3bedc5bea8f0e5afa2879967ec69c01f25efeac19d522d250b10fd31ab097ac122ad168a7270860b5dfdb5f3e991d8496e3575fb34cf37819a73d7fb1a170c0984ff6b43751013468e4dc8b21d1e32de0bae0f3aaf56ba5aeeb46d3b4a200643cfc244292fb022db5d25e858c19928bf3192394cb4239bae17ef928173b0f95225a12c6c3170471fb96489a6f2fc6eddbe78189ede0d937b86ec648735c8cf11a3aca3a913e0a26035a7712a41cd53bf94b6f65169933b501eb6647ac87d80bec62ad38fbad0aec7cd628902960025a6319515c9db33f997cd95a694d812864810f4d7e58360aaf96f79a9fee231a17f0876539eca2877c774bcbb161477b5dc718de5da4730a155daa6cbcc7e4392b9d66a61c182c313f8d9e023a39630f11914f2e124401085310cf4e56b9e632da729966148747aa5ad7c60072d939c11c5909f19f1aac0d9ea07025c9c9e795e85ec26dd1ccfad78625d00f197233552556431a92ea9e0021aad0873977f6dc691e4d3969640d6f91e36ce560bf14615f70220c3f02cf0c0fe44673b5497e8a3ad1592309f581bf1a8dbad2a25205c881848378f55de401bac91a4fef18fb4e8a285eab3ece02e20e8d17a21f38d7eca797ea20650c9606cf8cd4db835a3928f1659b6b2a2fad63f4b2eccea8d3f0322fed890dda6a3b0b4734a16119d9f3cd504922fc123c1a1e1e39f96255403d2996111a3a3ce1f908d6c4918aec65bd8b7659396df6e501e36f734ca16c76cc7a4e681b8d0025a80193c7ff9dbca5e75b107d51ac1384b73547bda57eb737a25abe3d197b1e9c790d98afa35aba44eb6c34e18886944fa6de51dcd94aaa094e59d73226bf657c3601c37d21685214ec2c7311e9bc185bc8ca421299e967024a09240e38aeb0684606c51e186dbb94ead9e3bd36c8588144e18038eb0e97f1a3a58ee3705acdb63001eda858605f486106ed996b1ecffc72d59d43b682f3a69de2f533befd6adf1c0d0ee0dd705c56a88a32bfb85dc2b1c64b5b000fc59f257da8d87be47a21e81202da5b428c94e6e088e50f03d26d11d2363b9d03b3d956540173952d512944d10cd776dd95804363f26e90f880cd234e35f738d989fd79fab3d6057afe8ecc4407a19191a997ebdda3a355f8c781965297193fd050b1c0b5b0dddfb2de8fd8c80241578e1519605d2bd9f7fc17c0ef5f0b29a2bf08ab0c4bb629d05382b79f4c0c5ddb6dae55d808e4ccf0ead6b63ae7b6a06c2a574dfa55cea12d7e3bdb383b0e0a300a67e5fca5a937160cc2243e2b1953df70a4a27489d6785f80e1174d2704520b00a6f58479bb74a1269ec3f50487991e806e71cc8c1f0c24d5d877ce6002fd7df83d707894df0d2db6426ddd97875a3d8f97f6f3f0e83c933ff49b6a4408dcdd61ed8fbaa5bfb320bbac824cad13d2e94680ce3a67fe56cd73929ca4770b4872add7e4c3a7623180fa8b97ac8561f99dbc448c8d5e65d95e68d17766fe013a8419fbef9d7c7e856e0db05b4d9d32dd26458add11df11ff508b0b8873b503f010824246afcacad91cdd3de743b1fdedeb7bbbc61aa636426be8989deda3006091ebc327e791d2f6bacccfb890894ca3602c547f06c1899b212ec70342270154b21ef149a00ebbbddaca02b81c8787e4f118d261c6b2e547217b16d4e3ee0db11a5bced0b754595db348c397edee29013601e747ce51f054cd318df43fbc036efedb3d083ea6587c47442dbee48a190b84d47ab4d487f36efc113cbe7a3601aef1b335c2d78612bed9ef6e2882d2e8606f7080b893f287fcc9415e7ee6820495e7732d0a5f0d7e24b152efd708bd6d0940275f1f8dfd13dfd20297167d1502199195fff40c8fe7e87afebe9736db77c58faf37f9dbf3b2752e0f5b2e440f09 false -check_ring_signature 5819cddb96c01b4fd4bf3955e5fd943e553ceb2aecae94897d117f08f64be682 790815d4f14b84159b501a68f667a3eaa9b86e3ed347f4c3ab07ab3d9bbc0cc7 27 3a164c960a159b4e3f7ca629b5f3d7c128bee6095b7fe8569387bdfb7b645848 8e336aa18858496b0fa0c223ae36ef9db67560ddcfec33548265f377717bcbc3 412294d2c7b97160338e69e74fbbd89e6cfff474ddb780fa0b2a3b775ef8561d a7e080a1813f19ccf685537649fe2de8f9694ec7b6471a108efea1e37a256311 caf0ad26187206f4536379a9242815a2f9c7cf5d58f6630dfbadc5b31e6d071e 39cc8dc803b19ffdcdf09e3ad41d4956929c64f45da3667188ccfabc5ccf5685 0ed2e4a04eb058b734bc38fba278877749c40c1bab8a4f011932474412fb9217 55063580eb175fdfd15bfeb0f646e8c79b99a59f5b7b3e826ceaaa5881095de1 f5c439328f8f3e673e7e1bf9233d711c1356b6dcc7765e7bd899983e477ddb02 8331dcc0eebd793e9de3e3a5e402fe405c196ce5fd9267a6d1a5579991e2b5ca c8ea9dec275695214fc6bb701ec20cf8016a87b88b7544349b4393d1fd100824 042d6b88c867b7de853c8c50792510e515b38855f7059067386aeba8c4f5e980 892aa44d79e307c18df31ab6b92d59ce475de919feabab9b26e0647eb0267eea d42296dbe1871f316245789cc54af9062a7f7889a8a290bbdcf274fff54e33a4 317d8a31467e93e61e6d184f8c3808633be3bbe37a7c1abb9d6f5945f01a1b7c 5fc5195ac9ca371214aeec335f0c8dd0788b562c6688b717093e8ec24d8f6f80 5818c6a2515febd9a4095eb001c0df305627ae623039be8257cba796e5f68e83 f4f2c133bb09e721b8a6bbc44afab6c0f7878b019269800ca810966d371259b7 72f040c9be8bc41ee6d386b8c2d2b945edd1a3b630565f74e5ec3730aa5c1f67 b507c3cbc05698a60b69d065b736bbaafdb82c99d4df78ff1fbef99026a0d27e 6eb4f4da68e7d6e6d53adce5a43b8ff1d85c23364ed32b5fe57d294cda366a18 3d190b0007fcb495bbf49de00b281fdb0bd598a79eba7ee604714f9f1611697c e5462f8a6e0eba04522df578264e562fbf5dab42a471b2c930ec56d58b5c70fc 0cefa740e55e76a6cde4cbb71c4f2c3683b66b5615c89d094ff72070d36b2102 fb2d72d19171f23cfb7ed8b903a212a5829844419cf08b4663395a5227b456ab 9592859bef90b6f7a49f3e17f61b1cf1eb8ab340d959a39c4ccfae1833adb5e3 d336050909061b378091ab1cc8bf65b5c11c7d27f631bac439a9179ce01fcc06 39574a4f3b3ce88b79c382800a6860a474d85e58382c8c5617d4ef8d7c6af80bff6a9379a0e02807ab44efe135c66b0032d3019d8896b72e73e85429e84acb0877421cf7b34cc95506a8ae7fa17c8d79c8336ec0cf6acfc19c44612e3705d10bc59047c6464a60f176cc3f6126c82b72313292c9dcbc1e7969a8aa200777060a6dc299a1074c563d96cd3bf64657a3c4926a23bcd8d64cddc37e7782cb24110b45490b9d0a146a4fd69d7452f4d4c49118c55da2411f04fc7d13aa822158b80f6b26319ef8f2d73f084eb94d9a7fdde6313d66de64a1e46927f15adb0e33bb0f490d42f95f95e8a085e38a131ea7fb07bb9f684e5eaceb0290016d962cc2ad0ecee59abbbb26aa5f8ae0977b1046de0a1ee8811576422f7c2974e7dbf1857f09b83467305dc34562b7fd6b3015fdf2307809a51865fff52f0bc6992bef2edf0e58f3bd22e87988d38a8fd815616867efd02b41597e4c24cdcd445d6b473e400250f178038c9b6d6248b94cd161e7e31bce31786f732012916e1cb2d7ed5d1e0824c6cde09b2385fca3f03f7a3593ad39c36f43ededb2cc4ae856c38a57c459036094b07e39a4e295d9fc229125f72fc7adb725f045c967bef6e1263050477209b6607e38174b50ab2a873bfa7920cd747d63f5d2fc55efcc31eb72df5e6f1b0eaf1418ef1a88479c4aac45a7a62a0849e75cdf7c3e627c56d6ef00b4c9cd6e074c36a6f31efd19658d4236f2d48906bbf7bac6d8922ebf07b39ca7c53ded150f872c508fe6cb2c6ff80de9b461d7ee68edffaf26f3ead93eafddfff582c72606e4e49d87c494b29aaa9d10a617c55e0a5de7cb1301a9ada2feabd6819d5983089daa586034905058a2eccafd991f7f7dea1993cdd3c555f32d15b33e298e4f07c600eaa27e93dbadbabcf9aa92065621ed6499f0b0ea41c8ef0aa016ac1162072003330b20cb6275db2f4b361813bdfa8265a66196ea8c6f3372f321949d7f013a0992277bb81098a6eb87eb0c6ff377589b68c0307997655724ee1224d98b07a3d47a79d19abf0658e159be90f6fad2bf3fdac520cd3845a1b98260141d9a0f4d915772966e4eaf6e081dda7ad3e3d355c24416797e3da3e2568cae35351a0f9ca60160d71ea9e27d5136da49b2561d249585a7dc5d7d51c6c9bfbc7e1ea608247384c07d05af442e0fd93e7160e1761fea55ca20c828f220a03b70e2911608f3a799e9d278fbc22a952e6a3482e3e5a8a3ff84cc4a5bc987c07d2956acd204bb79ed26e5ab201be29c5a5e138e86515ad6f3ea0fd0b30daf898a967bc2bd04040a0ae636cbeae498d022ac7897741229bcbc4a677e8d04fe3fd30dfc41a60e8b25ca4d58afeb6787b68ee9c830be0dd60d9525e7ffc376f3b52ad52d1c890367d73b2202cf2a8d276e301d2bbd682913aa793d2df3a1961c7a8a6e51fa650220067dfa2baab9ec6c2c190c56ee746369fe9f5b8340935294461d4f0fe2fe00110eda7c1df772f41a90d06d0d4ca57d095468a91f870a0f5523b30fe884d50750fcb995c06f1f24f49ef56cdece7957772e580a07e44beb1552737156ef390aaf9e1473ed93c428244b6dba8167d0de9f003088f6bdaf64f7af961ff4f06a052fe43e1510638cb694866736d48b9d1fe52b50178e270afb897c7c60a800fc07106de1245b50af7458939778da9c1c51528f727f1b2af2469f45c0caf781ca08f6d33f4d6941aa4615f30c15846e1d9e8f61ea3d755720b79353311ec01f770b92846f4d94bb0f8ef6f8ea3b882fb3341c5f52c7be9dee70e520cd685b4c9109b641e4a56d71c589cae43fe2acbcb96b2f6c00a9626cb635c00d94d14125b605125e573beb4176ba39d24caeb8b474fdf67047fdcaafd2433f637bdb18efc9019b1126cc958ab1d50a7c8e476fb82bcca2c67a52bf4703fbb27a6c4f7055a60e2293cfaa8751c6377db443f8ccbb83fa6247c5d4be7bcff117127d6184163602ecf3eb233394d33ca6dc40c74e21096ba5d31fd13c6f8b8403f10387cba4d403ce0c75dc447b1ada9a11c8f1a645b8123dd32db918375b9a0a5359c1f0b8880a9b46f62e7eb2c866110f3f1fbcbd314aec53fc571e7e29917e666dc2f708140aaa391f002ec9b8636ba0bf72202f9e7921b9366f1cfc8e17a3405bd886182602b6b6e637602290ea2b58300e54b6a08daed86d1a80da869a88e17dff1889170a119a20ce2cc0754c9f717feedd3b4c3b243f9f9f0edad15eb65e581a4672500fa851873e9188a9f6921133a7f8dce5dda0560d3edca1e49251ef8eba740c4a0c7b539f559bc29fbe39e690354bdb427f7ab39ca34ac485aa1859de92e34c280752f43b047549a8939475b8deeb6f4807e073a8a3152fcf78a64f80ee2ebe4a0b3fcda0274b297b4d7c86b207e026f849f9f74290c8fdcbf086dada09ca994f0f true -check_ring_signature f706daa639e2b3643c4b5224de1b1cf728bad407360dde8c67ab382feac0b54c f66f2fd09239f34536c9c05f8d1cc3721138fdec6e339d39403cdfd9dfc5157e 1 29d81ee6dfb0a38dbd43f74875f7e0795c6acd25efa2faa9e9a8473c78290dc7 6edd9994127ff2fca341b9d39bc36d5cf2049e5f172f8b0e74f2b245dac033fad16092ff2486b8730f2f7d9d3f6c932135cfdca1a4dfb7269db3db9170cb6a01 false -check_ring_signature c618559614fef01613303e814f237c0293abdd5de451e50b59f0ecfca62743c4 9c9b91ae4f599d07e3eb0738e7ea9fe530dde831ec8646e3f69f79dd539e6745 5 1f480cb58338df411f4a34de9aea4c9dc4c3b804ada3f5c46350a45d5af80133 213a96178146389d92080eae0098a1393fca757aab43458754d723d89051c664 6850e0565cadb6eb03f5f9811f8661ff1d53f953e11122a80e6ad642241c69ec aa8cc0e8d272e8ca8845481f014b09450ce2911909313d35d084cde54d58fc37 6a55c59dbb490a32d3a3a4c64bf182fbc41035fd853433fcb7d48516dc57c2f0 3a833fb01a0008919eb711a3d1c03b1947314beb2666e10f420e55e70dd42a03a28d6ed1fd0b5d9db25486c12b23c1fa2e6647374ad8e53c97c4cb8e83ebba0e55cd41f18b139c21ac9585e1b8cd8a0a3788077622bdcebcdbbc724d24772f0c6ea6e87be5ee62cf18f3e2eed6537cddd17c0674d9e8e955837910df922e440451f272ff11ff9d3a348a6ad4711a5f1418a888ca077cedeedfe09425b76a3a0d1aa704328565395ef324b5e7aa97f13229ab00811222687ac781e7e2e0f8fb0a34d4b9b1f1800ef15f041486c35d256a9178031ce5a2acb879b70f11001c5d0f896612c72cc4bc70bdc8b16e98d0228ac3eda3dfc622137fdbf5da9814a2c40bc608976d7aa85a60e4f2d4ed84973ac5a4ef13374fe0dee491a297281f817104435426a4a6999667bdd58b8aecdf8b52d23879349ce9e6bc0597fbdc02a0e90a true -check_ring_signature 67a92df2e29bf516e10056c70f4e05e1e91663415bdd81d4b7a4845ea0f8db42 5351b3cd1f369f5d7b66577e0d88f58cfc479e5215c2fece09e270f0542710e3 4 035bfea7f4240a6c4f70aa0fd755a2cea64267293d2b1ceb86f9e572598ecb8d 1aac18428d7ae7b196448ac461ebeba0e0a834b05f5c178cd94aac1f3c5c027a ae3e0a79a790bc99eeb1932e7764e351a0b92eebbc4fada0b3ec186c6cc0be0c 67624fdf652068bed58e0e7e3c9156a33ee549e2dcb5e19ae60a6132c71648b5 522c2c63627752a5de464f397b63aa0cfb6d47e43b018cb113ff735a712c514d2f5537f3e017d3424c05053bd4fa1eaf85aa8224339a846f48e9dcf90ab4a30fb33902baceafc9691763828635da863d5f560a9573babe842e9281e7c88c6b560a3950a4eb59943f530343e4d761f6dea88c11537c55a821856eefffcecb5d1732e6d9ad5123720ba10d371243c21bcab399d7dcfc71aa61b3fd01722836eb5741a86a838bfb9c2c907698a7ae4cef785872ab5bbd4207d6a9e7ed2b570bf0cf8a52564b098c271970b7e075db1a12d5bb8e37d49886d888f58e04bd80f16a07d4f413e3dc63d6b024564e0ae658f6b2e5bd58ba34d54a21072fc0b7ce9b3753 false -check_ring_signature bbf0234cf0fb4848fbec305e95c0eab794c2465fd9f73667fb95ed123b091121 9113d0bd90c3f6744fb66c3fa14c0c4c299aa5bc4d818afcb9bf3588ec461b3f 1 3929cb44a19f7941e0ceb1d66fa41561d374f9d6dac439f081eb47bb0a40d888 9c6eedd0a246dfd38fd1d312f8d188c57685a91a570f29d2981124d67e00e9d0f8dacd27999e5965ef9cc4ce5b5f45620a0b8e1a95fece1b143bb774e472acc5 false -check_ring_signature 261cbc73c6cba19857a10d239ddf64a541f1ae433fc81de5c62112c30709a228 97372c456fba1f719d7de3c390f30c81e66c996d7615056dfb4a6efd4a9f0f8a 16 1d0f0b8962f636c142f98fbc7e9602ee8d016ec052fe5abbaee053b4c9cedb91 40bab4623ba2de8f86fbfeb96a3b3d075582c06ec1c8f5424123e0f08d81326b 5f149e5594d45df4626f0db574c4cd5dbd3b66256891d89a3805c28c0a45c6c8 9f9369ac33bda6eb403ac199900e5176baee4935c0461a990c85124d37e605b9 ef29db8de8c9bee8c97510d5a2a751f1594787b24239a6496436681c7e88b36d 0d8198567f8d05a851a5de4a97830a57904baf961507d11223be53234cbf5b74 b20fa626efa303686356d4f7d71bc65a7fa9e7f0409e7b0e1d5bee9536d6684b 0b3fbde46fd1d5c916fa70fee273a2bf7e73e4a9ea4bcede79a66bee89c99f06 b327d18d0324200e2799272d1ad85e1761768268502e014a09d3a5004b2d63df c2ec7a0f690f5b3e5db2012828b3a8cba45e030cc7988baa4aa2d5c5c626c3a9 177d634bb214c8143a259b8a6b3747bf55c906f2d81d6bac5e3491422acbf161 f5b065d549208bb48854d9ae9c802ceb8bb988ec7a8405e8de5b2f1117c5396d e2fcbb000e09f1221c8de9b1b1256409fa8c8aeae97799aaf3bb72c7c5511a95 d49e63d214fde403836083d1984180a1448f0456a4f55dca3b8251752649432f 639eb48db5ebd3caa369e9307faa964a0c4bf69a35e387997855b4350e40d02f 6811637022eca172114d4a2102312d0af51250c28b9c0ea40b5b10bdfe3fc246 3a508cab5c586377ec3d78b5d8ef5d9c2012078e61f8f91aee481e5afead16079dcc09b60a0e74c11acfce1bf836dc6e6bab1c492005dae3ff43b2673eeaf30307a3b377d1e0acce7aa5df0522fc89668d71bd183e47cec6585b09d32265bc092c752be0611bbcdcf95c1e1a74fbc9f929e784b93ac30fc9cbc286d8d6418604c1d580bb2f2a3f9e7eb417910630b1de0d57f8399f6bfb19f9903db5b5638907aea96f92045998502247f8ee88fbf25b07273219eb986d6b91a32a37b7419505c3da29a8aca22420655f61c09f38ae3d23694da68f9ee2516a6e758e150ebf0e742309220f38207e052b9887d63ef117acf213dd9c67f6a93ba30ce27d9574097eac6c104adc56db5b6d92397ab9ad936ee30e211a887dc4920a6d8012f370082d229b34ce0b14fe2ffa436aa11c3ddfa1802b307d8cf87ea695619c3f4cd207dcf888f22004c2df74d06f2638605409ad69d2d1203d8788192cf9bd76cd240bcafe31376615e9fafa27802844d2591592f08e48623980ece29b3f5654a679078c7034ec0ca83bc3df6e00f05cea0f01a7d5fedb01c32d6a422a18c4bd241200d6819c9dec6b92036ba7217262e0b97c9628e1f7d8c133f400a0832a85cf6c037199696f3a0d4ba52b29948f01d527378d58ef6daec698139bbad84197ea680d4db0bc577fb6dd58358348cfe6292778c4fdb3beca866fe2bc619a956e4b0103b7e29e7d47d61b417399f8ebe302f97fc1cf80111bc5fe405fdb3fd72051cf05e2a4352199d95795133ef30d2d1b30eb8a9c61ec51b2c66b1cc5723ecc74820542bd3c5bd590b1d98cd3389f749787168604a1cef0c8ed823b0d1842d7507e02664656e9ff3aeefbb1783a3bed49d7bfd815d37f769fb1da71fa4d7f40cacb5649923950a44c3401c0a684603d6fee23e5462fa100feb8bcfb80e16c2abf710df59b325264e01ed31f4c81cfe7031ebe96130c5c614aa17ad804da08cc16350cf37036579f534adf88fb3925fbd8c95533406e32b327d301dfc6c16d30f09a04fad135ee00ea1ae217a86279a6848095ef7489763cfddfb167cc42e4b679ec0529838f63eea1cfefa3e75a2ef88de01505f414f41e89bba3112bb115547f8300e5f040c763afd46425ac9da0ace4eaa6aecfb558ca1218e4b8fafcbacdedd1023709dfe40d589f81bb8b162853f5e82e5d7bbe7505c323428399ab55a64e750f6e31304a71c3254172cf6eb3f227be5cc250131b5dd63a45f93d8fed55b0d60608a19aa38320b590d430c44f374018c1b3aca001468a731df8b254eed8555b0cbc549ef420aecabc53f208823093d91c0b857c4aa2de45e2773eb6a501d64003d7eb04fe6931aa77d9449ed08341b4d61e11fa24ed8fbddd83b1edb1822c9a0a63d3d6250e31970c20fbdfb47072e3fe540bc59acb849c4567469472b6b62f02 false -check_ring_signature 71f8d8daa61524ab76cc418ae84e09a066ff33f00e4e2e309b28658c2f7438ca d50476f5cbc8f7c7b1521791c215ea1769823db65779921178202a1eeeb6726f 48 34af26f740e9cbfd95eadd7dfb95e44d131c705922a2bb16bfd717a2d1006bbf 5481958096fea781ef34ada977b99f55df391dd71a6b08c98f7121a2a9e44f46 2b629e37575b61e54193f01aff8565e1a3ef4cd6c36848b815df025d54497b46 3eaa6431215a7a55c72e1e93d781f52af24806dec1dec0c8ed3b1d518772fa4d 9f968674d6a88adbcad60d75b4825b088158337a820623038642f285d67540f1 9155f6fcabf50db6bf6ab1142faf7b140772103e7206a88e47dcf3c4590fc811 ab14b971929d6997922c77700900f0f4a46135ae7565e26715eaaa6d666a3c58 7e37e757959f52929325f5a1554feca3c46587cfea95729230aca3f8942544f4 26b780254ae0e16287287e3b6171c5b1ee66916a1882ce00f25f82c664853a92 eeb3d2042b2ec79d0df729cdb6cf43d55964ff7ad99ba3b8aa34384b9e6be15f 4ff08f2840f74c659209226d7545b35d8ecc160f6f51ecac97a989731b35bec7 fc2c16f335fafbfa481a50079816bdfcf631d19d3b0d42f2d3e94ac840a5ef82 e25590e67f09c849cd9ad2a9cc7e2abba8b101837ef8d9c315fc9a2dce59d1e3 1172c26b4b5d6774fb8b1bc0fd78b66900a601638c9d3f7f04e297b88887970e 408de52d0f6239990abca7157360198beb3c4335483705e37e912c4aee448370 bdde3ba68463e22383a0c405ae3f3c215f796ec4e9f7c1184347e8f0fad3e729 862147e2a94317233d943af19d866e0b75569bf74b0219aad935b8272d25bd00 381123945ca3e7f832390d18f0e635f945e6525421b54181008ed51a3ea03d96 70a2f34ba9a426d1dacf2149bb834ca64d3f10ac2252d5dde79e9433e6cf3913 4edbdebd7d93c61ae953edafb7e6d6878dbe5b5eec6482db74485675f96ebaee 41b798f2f6ba24cc5e4f76b6f727f387bde343a4a80bc6b48f9ebdca87b77bfe b9d26f4fde1fc9f56b2bbe1da65f34310bbfa787e7a01af300274817a26f0de2 a8dc1ddda6f5e9e404dd6d4951c068a3cf4ac53af8840a8f931c98b4c8c9dc93 fe5bc11ef9c6dbb2a07ea8e8fdacabd955866bec9d1c278466d8da6042e993c6 573621763f784659d6c78e2654a05f6b9f603789f602fc9575e222bf3a6bf0c7 075aa106db00e9e374424dc14bab8f573788e5714ed939e5c82f7c7956627acb 49b9f44dfeab8dfdfa659de8641dfe28a0a17c7d8b90a4ae7cfa4d04c00ba3f1 86575c9c0a367171b548978941c5190f43c175f935cd90f28682f2bdf7258e27 efea497af8b77b603400db7f767d563aa4a710b91787c18f292c6de92f8457bd bedbdfe6118f3d35bb7f6eebd9d32be382de9227e8d97e81fe2998bfacbff9ba 6fdec39a2203439d2d1602f7c92bf1bc2ed2530e9db5afb1ba0634f3e1221580 aca40c7df3015806904c4a250da7f86a35f1199490df92a7b0c94e983bd6ed9d f7c2b84728e0457f13fcbd71bcaa73aa6dd2cbca3017b23ef49a63d5b03dc966 cccaca6b55ccd3c6f4f5b548acc2a2dced427ebed5d460316100b2b33fb8635e a1a30aaacc1c1248c891abfab4109d641095177950c369699a8792b29dd74143 4e47327c10f49b9aaf7939cb90135245e1c47e2454155ddc367fdffff6f5b753 d41382279ff8f3d46c17b89bb8d1a95284649f0ce14e46e264ceff1dd1ea7cbb 475477d3c6433ea55356aed85e6d1b492bce6a0ad2f4e537673a57138d650c0c 2f9511e499a5ae8276632c5df4880c99a180fa6bf72a269d207f233240b0f28f 42045ba8b020c12ed1ef7c8b2c8912aa83f6a22bb4c85731187dd6c66523b519 878bd6f0a07783bed9138bf437649fb3286f033d3a78289a7c45501ef19fdf6d 901e15594230152004fcc76ffa4652d0a5f49e8a9a167266ec98d41bfb1782cc c92c3947ecd83d0a8a79b115c9ff0093abc2126a90b976fa27600565ca66c799 832e0a1e17512ee9c297cd410f55f0e83e5a88a1d6dbba45f548baa104bdc826 26840854e0b705c5772607cc9babe3132f2be3a63fb3f63fdacdd34abec04db6 400f1e17b50a5254416c916bc8f81e701e05424b667b68035cb3be3a0949ec7f 690340499eed4976aecb863d49feaa6e9beea20679b1f8c01067b222fa3bdfc7 6676f22d8da7fa328e12e190152fceaec0ecf5d93f2b32f5b63c61a84aec1289 89cca5749b09c8dffbf5c96d81751ca8022810ca29fa5f11558dee754d697601d1a3a79e8de242e7db1378eab8534f62a623334c6b4d831648d4a0508ab63507037908fca940da1d29cd341fe0be854287f756f24890d97c00db844ec887e104f4fc60b3e32294ea713d230266279a41622ba6cc73788c5d4ea7282d4da50e023a1dc3dcc5f6a3c10bb90f28c2db20d8d1bf7238b8f8b8e236e9f1e99a41210c20b516f6c53acc607e002cdf37f502c84561600c99aa7dd08893bda0092d2a089a00915c35b1711240588d09dbd41c410fb31b0c54e0916b6d96b683529f32098fb1d90c50895dc377c05815dbe462f9a142b44db78dfccee12b61a86e1c3602194a3159750805bb5887721b74da57addbddcaddb90995d3e12a4cc6bad65a042f7552469187ae12baa521cac6d1be79d5495da1e360674e3276cf6c1c4c1603e0d2a3281ef76449bfe67f6770094513df06f81150ad830eac4862bd511388036ba8a4119817824997b4efefc30ed6b58386d1923652087fdd23dcb301927c07a407166b6e9f3597086b0ef9a588c67a6ee9ac6432502f53e2ce2a6662b9040a9fc5d21d57b0867c27e70bce97038f0f9d24b0f53b80d888e566e4430c015b088d67142d5681bf8164423bb9d8d7840b918b883c8c6d137868e5bfe2bd0f780e4c68ba06849263dc360d54dce3d7d65c065c81ff84da4c4fc79367f588fb220278c0d461127a1eea1da85b6cd722e7e2162ce74705c057ed6fec53ab626bfc0f24605d86a4e76b7a0520e5ad79563010f07b9e7853a9b5b360fcd29d0284d60ad1a821135f80e23114c5d5e3123b088e097ecb24ff2c3078d186958ba7cf8308897bda283c0c7cda7a45920833d5664ffdf2c763f05f25b623f744d9ea88fb074bc4d0df14db15460eb8ee3b932a8065cee9cb7b2df2ba11f79ef476a48f4d0255ec28a237088c7d76ba03068b1a0f3f64e569b0932adfdcae3fc29bedfc930984b60c41d523e021d69996669770cb4c9f74eeb0652690c9e138fa5d68ba6404a56e5acae78ef63a7974cc613cff04d92e8e581d6646501ab5c41b5701f5ed04b7b39f4092ef95f0ea7451cb62e6ba2d576c40b64fdfea9df0ebf49131fe5904714f3844895ffb00f8e09d14a541612c95d102f2723f0403ff56d4f9ba76d306d05584cc01671327d5bf241b002638bfd7eb4de6d19157f11560a3654788c60321c0dd20e55196dc6917bffc3e459a75b7c62070291821a4c4d97081087a1c04eeba8ba5f30c161f0474ea6960032f718955c8b3a45d2ffc2459bbfe01ecf50ad5a0b07128c0eebc056ae9fd8dbaef837853648d3c4c3321ca90b4ce5bd36a07069b536ec08cfcdce67423daa8a35fb9d51e1be2eca2122acd981f9d7ee91f031e280f266165fb94fffcf547b10945a0f21acd85ca50d769090f91f2ac686b0de75145a2789a418c06b5f9aab9cc46fe0a15097c4faca3d8d01969778319c30ae8b31abf1b427e64c53ebe2c26938419006ee7398657391c1c3b7b42a084e90de71d20986e63539e2fd3c1a2eb12cbcf4198b981974aa623de541c1fd9eb6d054b71e0a1f1b84a60d7c4a6e07ef7f5321b0eb5e306169d0a901ca0b297418b0a23f12b5dfc5ef6b9a061876ccf06fc6ed8a9815f246e47eae801a442bba9827f5c425246ae9abfa6f764368307a94c310b48228443c9beeb91fbc5e90af94800bf743da087401a8949e9a0b227d08a1722a4b37744cf9e1ccc06b2df94cd7307e7f8642071b7656bcb3a1d59138f8de6882e623497ee9a369776ed36092d8506b8af07b1f37f65842cb9cad81065e281ca3281a24c6f167aec1dda4cce50a90dce704a6ccff4f5f357a9c4987151e4370ba1bd28ecb99e1886452f6a9eca490be1b4da0ab97e6b6828fb1f9f2aba4776096a041ba0e0351ae8d18e0a1c0d9d04b9fe01c4e509c4c22f7f533a6179be30e6cd346d919ce37f4e1d111fb4d34f02ac89f3c523594439841f49a06d8a966eee2e771dcd14b22f5a8b5295a0ff3f038ef0654a3372bee6ebedc4eac9c24d2a390d68ed3ab40b532feb7dc59339bd0468d7d42ca98aed8650987af49cd8585777ff743b0564af84ec7c721859dfc605d4e17ec7f1d675d6cf3614c1c863dbb837d93e116d87bfbe0ada618fd120600f293c29e422ef158a6c8c8c6de5b37fd24469448cbe49474f2bc63b57e60629016fbb488cce7dc4e4ea32d795e2f2899f113998d9773b5f2bec5571ca67c98c099ffc9cd395ecbbac16b94884653ec521e413a53d711a7994550b07923fb94c0f4e3a4d03bdc8a23934906f07dbae9ec02d3ca5023c97a40092741591bf9b240a8826e048218b38d3ec5b1ca472b565fc8bae5fe95b0c09f0603ed8190682c306656179654aba0f1dabbf76ac2c7389de20e505db98dfe2a45eba53580dab0d03218a1aaba05e6b67700c3094391478e1dfb8c58e4e4db3bf3865ae95a8941b0b88c84873955219aaeb94c45ded6524aa1020b09c23b4b8e28cf04498c523790a139cb0699938dd6d87bac696842ff4583f0695736d6ee8e19c308ccfb7de0b05f0e8e059ab1bd598e17a5b17076f9f15bc85b6ce66ead9241efa0f87c561770ff25a54c5b6ec62a574d6cfa35d7d3ceb15f72169b1fa074d087bc513bc710f0b69cb1f8d3a192efaea5a46fdc7013e3380eb2a537d652fa7fdf78655159b4f0c084ec1c9912ae0d6c892d39871c08a738118433a83829d9d1999fe31b496d00c39cf5e16bee9fa319187d19a973ab02e246042b1e7d6656747bd35fc951db00d844252c89868007e1c0ad6552e8d328ed4eac1e3e31edc4c926b7ba670454704774f7264be26b15d1cdde0e255451c653b66637064d10e9672bf086014f49706e679f239597df12f249cc651e20a8882ba766186bc49df884290a0afb750db055ce813ab3830f44dbf84665fb4dfd4c7262c070ce1fde9fa658a8cb9287369051c91feee7d248bb5dda237a1e852e0decc6cbb8478583b3325822cd66bdceb0b4e43392e00bc5ca3635814f88dde74a797d250a8b6ddfafb3cf09d8c31407e0bf9eef43485a8cf53d143ce03a0625ebfa9aa466cfe853b47682dde10e6b9f006dd0db0ef403b7ec4bf4309195c956eb97e2836c52f25414cc252906e5f60d90b598301b59371261e8156b4a51f3290f773d0d93cb7262dccdb97297c90f22e0a6c6db69be0aee98b2d69162b73b4687a957ed748c7b9e3ce128892ba2b76e00d206d6113179bf0283f102bda0218142e890dee132b87b4e87f49d2a35bab080265a6e57e82122b5bf932ca646569d599f2fa0350001a5b167a79613de8d05f0bb28fa118fb3785ab33daaaff274024ea23fc90ae08ca934c8caa45b6fc8cbc04e400109554b7ad28a631838028fa8a2585435466ff867508aed006db115d2000071403d6763a2c8abf0db3c07de822acfcc581c33a631f0fa1d1b154f8da780283c295f435797831a841835d60c89ad3ac2b42708a0cb8bbfce678e2f8af6b0d377761a5a0616361dcf002528a4411dc0a8d357cae09a259bb85f49a3e42750409f8e9ebaac930b80a7b1a3b3cf23a9ef2e175c6cf5c2f35090e17ac97b82d0c42146e40da3768fa3988614e91b8806583563f5b3630bd7100825856692947088567c0a8ff7d5733fa25e4fc579b6641c0bcb3694dfbee111da8b79e594f4803df2e9caec18255a4b7686520fc0d89add67e3386200d0571d757e2daae157308e69f5bf357c76e0fefb7bb2a1b5271e3b898ead6c635e5e8711ce624ae77990d84847d38aa785777829d08d68774f620f1bed3037b253cadac5d8b4eb1bb2d087e0f3b1dc4a81692e1dea5af0bbcb63f4171a5d72f698531ca1b1133d50c52776a218a42c8fa14361c3d30b41efcfb7ed08d547d3607efb89c444a981a67570448ec6ae2c94ddfa99949ce37ef4366680dbc9f215074f51db33b2f249a35390593b8eb871b15f0b8eaf1e5e01dd7dc8aa0527df7d85cdb4f74b8dfd7271a830c630711ddb46e1efbb38ae4c5cfdbbf02222883b23256a25eac0b67494c97f708191b26b47b3ef242ced8e8413ff96b4df4077a49ab6e31948138e8c513e5a50f877e3edec0afef2370f2178163dc23f2f87ea70a5258e04054abe4b9ff1c320264555468c1a8a8d1db48a8a72c5769a2f6b4923cd1d4e533338b8fd426695a08239491873e5419c8f376020e5e4663e69ca2ef57df9d3aca19f078fe7d78f2042cf3957f92b99f993a325585913efb54a112da14abccd033da55ff256cf5b00f9927772e10f983a59c7a366d4ee188a06a01dfa123eeedfb55bd84b06558fa08 false -check_ring_signature 79017a0ec80d9b7764ae457179b6f9a344fc938611ecbc873da9c3812130532b 8df8311951e459e162c2c471ac1d4ce223839ec12eb290849db63b77c8af1848 1 c863f6e74e3b46671258ee374982d0fa5a8d8de970d51d0ab3693a9ea6288868 0c992da1bde89ac34aa6af197eebe328b536b35898a2a3195cdc617c9b0a020eddc406a2e4211e0916d6b31efca25dc45a7533956b34bf4dffc2cf9aae1ff1db false -check_ring_signature ef1266ea6ff03c6c0ef39de1e69600f16399258b360af377c216c09821913f53 ea82b1d2e4ebe6a323fee628a407d39aab3e21c05689f1746c722f17c879149d 1 91831205833118b71f600ed6b9b47b368115a5fdacb67cabe914ab61de58b33b 4f93fb62b5969720182a013ed47cd03908b8685b186258a733cff6e9bf122f0ca6ad6a91b08c22b3f580e05da1fb5a0ef93d26cdd647b6d9db535371b6aa9e07 false -check_ring_signature e6fab12e615f2aebfe80dd2e4fe9a4771d78a35cb9d399ff7cfa75fe1bbd6ba3 a355cc937e2d88ad3e215f3fe9a40d011d6ed51f8b1fe0c4b74ea70cb5ea305f 60 8cb4f7b8dfb719e34d887aaec0e5f9f52aaf942f2191e580ef115b205dc106c3 7f32bbedd85f6167854cedf749b5cefcfe2b9e672a95b1e9a2a7dc1f1966993d ef90c3258e58df60b5508e22d06c0e8b05668d9307aed15bca02455a322caf62 0d16b271ec1a31e2f95b736dde35cc9f0fe6dc662dfbd5582126e3dc65efe5ab 1742fe5a4d027aa8b3a4b82053216a484bd1e1e56bb8247533bf239cfe5af0d9 eee6b936dfa90c53247321c6e350e5176fcee7e0c8b371c9185a64170c95a9e0 010309f4ea85f8cd8defd23b73bd993fc28463b76c1d5f36b85973efedca7140 8dba7c4bb971c930c3258354dcd7a257d938a4d10d6e993ad2e0c601299260bf 0a6d8919fa5fc9e526acb7bd3e703e4df99a733fd8670312d021c8d8d579e4d6 4cb80d44ea00b2d973624b44afd76ffed2760aa0410e6c17b66c5c7da3b4f3db 5f3a6cf353dd32c76e8ca33da3e6f13ca3ba9676edd5df89777f61f8201a9f11 bdc7b96be937c264b4d2622b1449aa402d3babaa8d67f4344edede0052306dc5 c915b4a084b4c18a4c40b0e8a1c8fed0b392b17cd8865099b4917988a429e0c9 40239aa197de49970438310ff50ef8a4b0ba26af1f59a9be57331a9a110ad12e c2b9a81556e516eb203b7e58990f96103e6a623268998b5af8e0505fc574b2a7 c4d18c940bd8049f2e5e018d81c94512b34c3cad571967deed37bbb9156fc61a 5babba78a66daa6b9a2e71c8323bbf28ee353408f422898882cbccfe7cf15398 9e3dfb7332d0048ab1471d29809ece99d19bade9ad662c5935011505a57594f9 4a701e61f111690451ec789f75004ec9d65df27d73c180817333cc4f682e09b6 eb41c057f779cf07808884fe8051221574fd40ae915225e91ce02d5202b079d6 9fdd5b0511020c28669509adc55bb5d176f5c2e45cfa133d9f0012209f689b0c 8abd86393d7a86bfe3c8332c9e160066c169e034bc4d131d3ab9735a5b00d5b5 bd63f419f07ff0a43da4681006150f4d5f6a0391507656545f0759b6852a5ff2 d6942db20ab5549ca1ec81b5724a46c15ca78830b73915273d4710f6584d33bd e083a47ec2eac15161e95958c8282197fd6a1b79fcf5c400d9865864c7d974ba 89ab288ce443fdd38c55b6ba281cb66e7b505938f02738cd71c32addea90d0e9 ddf1dc57f1fcc7d1b14f498783e0c11fec12b858f875e2aa22fe90d8d232e8a9 672c9e36a384e31d50b90d7e5fa56117dec7ace7963dbbce397391fe7945b0a5 2113a24814bf5ede230df8ee0121bcf3723c1dd02b1fe5a3b7a6e8f767d3476e 55201a7480b4d34f84932a1cdf4a990cb98d1d5c7cad58dd8ca04fbf24e82639 4122c65772d20bfab010c7e1be41c7718c833f5bdb290368e0972ceda8a17cda 61e874fa44f3bd52bb54d932f251ec52bba03a8261ab2aff86888012b718f540 37ec951c64d177c34dcf79013ef00fb4a03bab4d1251840f82311d1ceb145d33 18bea10a61a0f6d5d4bdb944f6e1ab71fb4556d5e31790642a58d36ec5195c71 aa6f3dc72c89f0846e5463be254b98facd8f382651d63c8710370b7aedeeb785 a932e0606acc947a9ea06a0bcd3c6a445e361f9d1e90d3932b4532a1dd71f566 4655a926d7430f56255560984d037a5c4d7b4f1a4ab5c57113451a5ed2974992 041d4c185943c2227ee93e5217fd0db35e08236f7843420fb6e3fecca68f6503 b9ba15b95e5649cb255b13ec3ddec9eab28ae9f4379687f5ea13b79ac83c450e b329bfe1d95c0665f8758d090075706eba901f823bcae526b0e92f3c649568c3 e45b3f6c55e6cf16175a1e5c30dd6c6e02522330be5b3a425caa93a513cc5b3f f354590bda908155c6ce17cef217215498cccc3a0a0c51bade03d856da9578b6 8c67d1f0c1daa1851ffbdeffbae116eeb264d0a8c50653c2507408a73371d748 b3ff1c6a55dc8efc678cef10a94bcebba3824a683a99b4d6838bb3d6f4abc6bb 3c6d298c0c336df77fb8034ebfed15333a6df830324262237c6a4d544f8ec6d1 47ee704a2a39e2f050cfb769b737f547a08ee4c48905e6d5d8d2b0520b402997 c89b5b038d4ac10784290e44c68343a8d5fe3d2a0f4c23f298c02ff8a2fad06a 9cad252ff2231e116722fa489dd3605d87f7cccd4f63c820ac99a0d222ac0771 7bbce1c05b8360adb8bc4dfe8111f698025ebb4a6201039e6a48ae098ca9212b d9bded4fac1568a9d3d797661dc239d68f69f2727e5438bb5a7055855ad7c6fd 8d2f8761042d46339c39d9f2e75e1453bdfcef76828acea67ec9cb80da43496e 7039e11ac2c3cc7460c18abe50f01ecf32489ccefbd91ff2feb6a7c4629576fb dad06ce16f92b2210bfd38b21ad3cb0c763e56011a439c1537ba4b2bf6e7465c 8d611ffd1e3c4fee7f2e1fd8a2a41b38b8eb9fc652bf2a24ee6568c047311628 eda96b6d0eef0166db6173ed6123276796c42105611b8a78a74c7b7078e529f0 d81f2de6a6aa1a69be557f27cc68824ae7a093cafc5c29ee741c4a2b82dc0a38 6c3a9c0faefd76834f1820264254591b76efd7938df8bcb2195c57fc469708f4 d11e4ccd489505938a91e45740ff2b64fdefe4d4550d1c7feb0130ca58ae9269 ecce5856c3e7da77a05f486e96d1ca9306581ff667d34f71c074e0263cfba251 ad4e270ba272f8472bacd4eada0e0b7bc825509d38950950f013febbe1c00340 73b9c8c8077b9157926a959a43df92f096b509bbb666c81a7208de4f8c341c09a87da51c9bac8b95675ca1ac3bc73e792bcdb4b491d0e62c3b6b7ae738e9470d7ec3e9240d92358950ee36dc05b483b668642709110807065a0d9c8549fb8a004f177943f39c82bccecbf1bf298496a342d747310508f9cf1d67d16d24af720bf9cf57583255d2ffebb004d8c1bc8127be7e333425534c3bb419822585734b0ec108812fe0b257cd2cde6a6bc002aa7ac35fd76a563662fd59eea67a1e9dc00eaac31b6a3691ae67989f8c71e05d541bc965ca99536daae87d3a37a639bfeb06c84ae8dfc4c16dd1934f9c61c259fe27fbe2669318af9290fb8145108f4a600849f23d056d2ae466f3d5f5748f520c829224cdc6026315b78eb1cc22416c1c092f3cb5456ca5d6039acde93985ea7b3f6a065547af2baf6fc6d799f999db8803e5a4711fba1fdae92a3f7038ee51ca6df686c568e7d4c04d332663bdd6857d014614fa3d852308c8c3d08ca2dadc5fb6cff285ea079d9fcfc19ca05f90617306c009fe88f354036eebc14e372382f5b6ff1be73324927e2e767b192c96d82007dded04bbd7f41f4bc5d7e1858ef0991edb6779be1fc1f07e982bff7483fdd4084d4e61a8e460a635455ad22504d31d2113b5788cd954b0d60e114f3279055e0338b16789d579435f875b590d0763215764f854ea654feeb43d81834b2d599204f7e624d842e843f0db854cd2c9e77138b52554e818af3af58471ec1fce32130e1cffc61e87d63580b4a1953c20cf804885822b43f69cc707e5f10a4f15ae37055ff6a28017519e3d8551a72782ced427fa730c13fb6ac56403b7d5446355fa079524890ccb40a8339b341f2700e880ccede5efdf572abe7141bf854bfe0a7f0c85c399e1b5b2ba35527ff8b3ec63692148510318b73cc4825841a4aa609bda0ad58db66f922828f0cc047532027ff77697e36e16d39904f9ffae167fdf213107192b2dc28b21ba28580067f8ac4a15a8877bf95c01e0af2c6be73d605225ef02f7bb378711e96470f4d3b92f9464313a18cdaa018ba11ed80042fc78e3bd380f3b6e09f62c3314f13507ce277acb6c5ec5b929bb31130ed9f05e626df20ad80a37c09bbaee811a210756cb566dd760e0ad88530f6c1e51ac49d629752fa06a026c236a0ddc5093c927921c7ee11d5755820e5b089cfd913662b37ee2d0a6c3026346f9f73d81309e209bb152eb9ab5a4eeb9aee6d7652c67fd8c03f0ab105c038d17605a99cd4533a59fc9bfc543341ea7b659f939c71d6aeb9548a23a09d109e8a0f84bb82ec6d3e2061fcbeb74cf9053248d1ba9c63ac9274eb7b512efc60d6e83ad63a49781d7965b85421a75a03a5722f821c36d8b508311ed363ca4ca0d1ff9ba0e9d9c07ea4793a80afd7aaa668a5d6cc01593ac188d2ede0266ec890ced74abb263d5e77815f6d7de362748c75a5af1d1580774d0b8d132d76f168407297cfddb111d84be327221300750e5481461b8f206be7404d5c8cec7c01a930eac276779f96f8d17d141ab39c3f90ebd1db8949b92a30176200d019ac9a76109e787c77717680515262b52e63decc642ee647b0dff89a1648150dfa3e0e13c0b02b71e315a64815ec8872ab69a8b5b6db8d89cf218af6c78fa01e6b0bba0760bff4f65fc5dbeb592c52799f5c4bda6ae51ed74a9d252522ebcc30e4184ab100b6f7fe7a51b1f0721612916e97db61acddb8bf8c4790fcfae985a3c055a686203776048a531978b7672f65714f64ecf918ac435e7fa5021ed106ded699b3a1901589487444464f5682272dd868c1756dcfd64da2702d599aeb0ffa26c2bb4f60e75046e96203f7bff31904ac1050bed4ad9f05fd135bac3fc674cc6b6c9a0e60eb89575ffcc8c6333ac922e211b35daecec52c9c2845b78f511cc16881a153c05dff3c2349bd7951ffb952465dc702586abafd049b890c168f486a05af09a150063b9d5718817bad66d2da4679f3a7addce4686103a9a6ae303b435b5e434b8023f595624780e74c45f113f6749df8a6d9aa26f0b5184c7d1f5fd9b6c06ffb803c5810406aaa933c85b386d5881f3a63c612fe403fa88918561f8f19786ccf709f36413b38b1a8fc81147fa64cebdc1ce5aa7baf0fc76813ebceb4c7a867d100c10c8af501eaed7e32c592e7a499d0300400a422b57283118750acc541a6f4500333cd4d25affeab54d222662f10e7618618ae0913e992409d1acff06d286590d0c3410285de297c00fdf14b9d0cf40881c6b7ab963b8fdfd7b5e748061c9980e0e04b02983191667746839a432a8b9ef5af368d662d51c3e8fcd44379bba7b02d6dbeb9777528a5fe77640c8c6e36aedfe07a8aed7121c019660e29a4d252b0bb7d6462783ed3a41498e8726b7ca56200a9c19cf200dd0f22371f6c05270cc0ffefc214e62c1628c9d3dc6b3542bd0c29b6f12227338f60bb3980a969c095e0bf17b5804e469033e7c91bf1aa9171ece0552313e7a849755448e92125ef8980409d64057fa98759b9c5eb58a19536b49a263bfaec94d893053a707dcad89460351f244b3a516e256408621752be3486d093ea5b61f729f354b0f6b076a0e9e03290dcb12f2dfc8a40964959101103d75b36ba362f7f6122d70500199aed1e60495620528d6912888117437a3cb74c0ada38f3d17c2e61aaf8cdb190ec5ccea0df2061ebe0440428027181e96a66ed361c90fcce11bbd7a49159b420376d1fa0f2c15712e70d808ae99d02faf14cd943d3f026d1028af51378e216e1ed5ba8707d02b78ffd740e29c3198439f96ed0be833bc46e25235ba790a600ee9417b0d0851939605ce67a716770a0cb19f3bfa5a61891fe39a3e0b8ad5a211a376e9320fc54e0cbaa414d47c12c8aa37b156063b38ba09b1151b8f782eb59bd205f39e03508abfcc37b28e7751ba255a60665824e31d844b1719510b3f9064c9bf997c01c34d7a28b42fbab6956dab5385d239d1fc410d284cccc5f181bcf5dff1a1a700331a65f239bb73ccc0cd1785692b4b18f94f8f3cbe5d8251cd2f5990348bed01387b1c0ccdc94887476ac8cba0dcca06c67e4486c1ec8dd120cc10c1549f8407cb97a0034e4a9bfc128613c0dee8bad71bd014e6407a856b9931d0e6dd1e6a0c11eb796743e56620ceb4e47f7f8bc3aa3e382cbc9d82919170fa14f414eec60d20c6e2d4d8293c566d40da11a04b858766aec0173d2959857e54c9940d521f0f7f09ab74469d4e42205473a67d092fc2e096a117cd9abfba43ee56517255ba0fefc0eb5e3b0a2267b61d9d45c5f2f1ecde298aaee40f219d6ac0613cfd7e1d0937e8d297480a63ac9fb6ef0c23154a3275bacf1b8c6282dafb083e0fc0a23f00cf4b16d6a845f831b8b5e2ab0fc40249ebfbf43c709f816ee088eb1fa86d1a080698aee7500194a5e33c2ef2d2563bb036d834bfb6a7d4e41f6cdd548aafa20ae52806f103f49b9b46f546000e45547110f4dec8c7e12d5a240a30c57f300d0527de0295575416ad98ae6c2a5a8e4e4579cd688f107e6b60d801f284f7a8ee0257dfdc4dd2a4faffd59c2d2ea14ee9074a47f4b7d80d1b2abd32220b33656d0e3ae4a6b1069f4779ff04232618c72dc4dfe6e340aeac7872bb830206267d4709eaee8a8fd94fd91d19962c7dabb4d0a6fdfccdf659f0dee51246aef46d314c082385967a3f7f6ae8a648f647caee42cf8acc0e3eeb03389b76029adb22209700f728073ccb93cfd0fb9b61d35a76ea0d60daad9b67bebc53c35d96b258c0cc09d91d9948d6604cb1a57f8e068083ebfbdac1c9045013581ddb2f18bd2cdaf301de26c9127f13ba0131c5007491c0848996415a666456f0b0808dc836d5bf150924790fbf373dbccd38290c45164cccb6b849b66dc64c79e5fe563c40f5d7130f9527e76a43b77660f122d1b5372c39d92fe81de36734b443061c2f81160b7f09987df63420f0e25f265f6999a030727752d7601d2e949e3e8ed73bfc08138d0eefd4461772380721c7f6ed39e48c6db577d2ec0145db425bc6793be67bbc680730c72959708e867637a03dad67febe90b291f4b13cd884155e5740524696740b05a766ca04d7b2fc0941e903557e8ef92a99dbeb6889764d675e3ceb5291ee05555c5fd81f4741256341bf2a1d400505acf6e74f09e0dd5d10d5f7146ad915097e85a86e497f335e59283325ab8650ce6e2525129fb0b641a7f2380c3f32eb0f92bada3c5886f4fb9dad0b8887b86ac17fbc04142e0ea1909a9b120611d6910b99880316bac678dfc43ad1a75993a6c9946ce2b933abe2cad72f521bd0c5dd03aa8ba2a49662d5b1e77de03b4c6e446de0a035ca1a2e0731cf9e4f8f9194a50a55cb79b0afa3a7e05da3001c80f8e7a650f52c5a11e4064fc7a03b9eaabea5097020e51d7642a29b8c2f96e1b9a3c3b7cf23bb271c706cf90260722cea05af070aa7e3d7a86902c6310a13fcc2663c1d4cf9f7c0b7e6005df59f74acf205e30401ae6fc2060d7900377869c60c0dddba4ad19826266b8655d494a9a33909c000c9e24f16a749b6e8203017565496cf1085639026874004e6575e238793c0d20f33bfe7c871ff0296c728800d5dab5a7b5490231e32896384fae9bd37e850890732db303e1f3a80e8f80294aef0c3bfbb1e964583fb3d8fcd32c38f2d465aaf0438386c3b2200b6013a094e2c64621b2ac9a4f1381b87622dd13918e3f7b81a07f4c3e155dcc91423728b969cdd05dcaa907e1531e00cc215c9d911527d6c5f01bd752feca110a4b6d5a356a97c35c647eada943d91e866ff910e14cf5fe07c0b314e478a31973395ae4638d7ee12ef4f02594403ad01853ceaefc88c7afb3a0062296faea1112292a3b25f97d3bf930213fd89012fe75029124862d5b6a1010d2a7470aa251be467e56e21acc7b0681f0804ab68be69b54b23cd741c17574805978bc69b9251ec4e7cfc3792ed71089081679d186a0d6ca45dd93f12c41fc902cd14c0595e9724e32354a0ad5d858de981696758b117a94b75f3b877dee3f004f64207045a3aac5641b7151b3c6fa754edc4ce09d044d1ab1a3492b3bc8207070656994fc53dc2422c9c4dba9c890baf318b08b80a3a634260079e3b0850b50fb38fb3f73f426a59ac51c9ac55f72f0b8fdb68df7bdcdf5f0432b73199fd3f0d34e6a32d18ce70707828a94f8b95e8dcc9a1e09a3a6d5a5696ae3328bc6df90f79dc66619518ffada70f11540853c70e6dce4938472d2422db6c805293dd050bc7b805f3664dd1e1101b0883c8424062a9e0cf5862e91ada295e2aefc900e500def0ecf63658875da59ccab7f38faae62b6bc2e18f6cd6a7e4ccdf366c96290bd33717fdc998e12ae8f114ab33ffa9d45f07a6c507834ce26e6ace8666654a08 false -check_ring_signature b2f24ad96c026b26acba73487e926327347f2c71a4e747fc6b14d7b71f36aaaa e103fb226169b0a9a685af2c4d2433856617dd431693f51cb4b9622ae36bfe46 1 08c02196024efba6e67aeb8aa9e43856c68051ae5dbf005b59d16d8f96e3384f 1d1e31507e78df39ea6d8e97d729ec51939418a3440f0c03f859a2c26b85f40ad70e09e1431063436c3521af322ff7b23f4db214349076ccbe9fcdcd16e25308 true -check_ring_signature 35c23285c118f596f6a9d051136a637ed28ec94c82fb6a910e19292e5e84f63b acecd555272ca35dc206313562539ca30f4ac1e1271a9deb8dca8dd889787c53 1 acc1e1c9288ff97fc3bd379ca588dc12a058c303ae8706fe544ac293a10426cc 3f338444bd1ee49ee134dd9caec59011904a8b47c72d8c8674af097b4e0a850dc1d236948bac759269d710a10db7416cdb350b37e0ff54f144e1b04e7da70804 false -check_ring_signature 751d593c306818616dbc62fa0bae9e82d8f773f426bfe65b21ab7f54e14a6205 a7772c912707e772f16fe56cbe92f70de613db8243df81adaf36749a83a54235 1 95abbf5d696dcff836e7f217d253b9cc71880c2e4a18f01c66facc288cdc3de7 1e8438aa20f4a3fe4164ea016d535f2a181b1a2d1249e8d7c4af33434f144606f705b942dbf7b470319367e9ac71246522c076965e2b2b277bc2e39fbc8a060a false -check_ring_signature b7b62534ddb996f462247d36c91873405c64bc3f5f264bc7e81cd2c01fbe4031 394e98ecafc817dc1cf2611d81c2d9722780a0a865781b8a768e6ee600b3f3a9 9 914800e2200c42c637b44a83409918e3e25e8ac30fde8b9373ef05ba3c4590c5 84112dac3d135ae2e83431e64654a6ada5ba81451075f5c675090e2ebb990947 055aa3b854b4c2838f1a9e11f3ad6da3bd2e7112c956c9c8191b540efc1148e5 d61c68f0853122d4f3a0d84c01798eb5ac1b6c0dd44f1fa92dd6ae0453a4e47d c12dadb97754764785b64582a81fec700f311fa97eba6865c167f529fd63e8e5 d6c2364a31971b667ac68dbb5d9cb77c47186457242795903561f7d13669ca0d 14c446693a62c51378486e83acfe85791fadb8029b8e62bfe065ea99da51e026 df707143f3c899f6c51dc51fb41f9ae8276540bff724e4448d4c226d7f356bff 2cdf940f615ba6fe37f9da13e096b1581483db7a6a4094aa2b5c9567fedf69cb a33e093d98cad1c68d359680fb6e54e4bf47155fc5028c107902a1213b21610160e1d22f5f24333ed2b6a4b40b09f37b55175bf23821a89a2eb79a8d7498db079488788ca555540f8cbc4b1252840eb4c63f4d9221e400c8242da81cbbfb5c0a53e5f9a181ab3321b243db367fbef755a490d191ec73a9d0c4e9ebe6dbe22803f7b1d5e07734150cbb46197d9600ce7130c04e56bcb70e7730e2575148047705cbdad307804eda156c91f9a3d7f4b8a18b309f4abe2d09281b4d735294348e0f73b1bdf21297bbff7120e7044ee91f68751edb0dbdfcc7fccc6d70f028231a02767fc19ce3a9f44985b34d7f5fa98c8025d0b93de9f35cd9b61e429c85e72807c1ccb12add73c3986e503a08b4c9ca9ac9ec720616a595f3de4143bb1336fe0508c00f2d53d7c58180db24cee6cdbc9397bc47c4e0895abeb4d0ee77be7fc1036ea1b8e6de7b79449ad2898582891e7bcbf10c5c4a61277a82ef540c2b362d0cd828682627112b5fe5047212a7a3585824721cba5ac587a3d46d52879406550bc36831c161613d514ec278264323eeeb9c23693a32ebd2ce0f2731ca55775a0e53ad3a9b27b57e423af973a4772e1f11dfb38a613433c5076c07bfd2d7ee5a0f14f7282165cc1c45aca15491e91a155519be20dd80aef0708046ac9c472cd40268595b18b9f0071351829d7ce8e21937e4ddca1d16fd405f7b75d3a0d2b968033af946750fdf75da59f7e56a222759509d92f1c874ca85545a3fbe55a2329100290f98beb57d96814d64337636ab9ecaf09ee3a91c8dcfe783ba9cb675698707 true -check_ring_signature d9d3ea56e7085eb5aae480d7d4ed10a90c8b9f8d6d29b59fca05d36184f13a15 350e37458e6877fec8215379cd03da7afea19f450f7f3323a6d5e6ae94e60cc7 15 72d138f1ce587c64b85163d5ef917b9248eee7e01dd8593357b2df105e475ca3 97fa5080d95ae1dcc4ff26f87c59008635f24295feb89a4d945d045f6fc4b4c5 5643385f601316e4125dd38c31ebe1040e06da77380a6bc6e59ff04e29e95e1f 246a14f78c9e63dacda1597dff02d64e9e62b86a54672301daa180677c27ee72 84ec89b0fd9fb3f109209e8264f60e11fb2ba57f6b4ca4871ff241794d7805a2 8f7ccf3c9e5977d1b0400ed5980d523252e7c404ffdee1ae0ea817db427e97bc 3ce13485aed3c7393c57a2fd263f486c4222e78ae5848c16ac831b2516d4103b 00637395d5ee9d527b6c23cebeea7a33d792849b39d201f1eeb19b58bde33fbf b2f3037dd89babd02ef2d13b1a896b7bc5abdc281f02af7de8b8cfc52123224c 4cd97d4703033f763dc87b25b0de656770225818fbbc47c5a3a438dee8dcdd4f 3ade2506a99421896fbc8de3ec4ce47b9e4cd75f65ea74f5b69fca3e054f1a3f 41684706772035465f8a3bd62f358ac50d545931a8692e3a275d7ed6d21d5761 ccc747b7d30e5125f464844badd17b65343eb4490c17350887b7cf7bcead9137 b9d496e63c8868662b29829dc00ee314c42456b2b8fdbf44c08d63c25d5a476c f66a11415fcd3b0dae5b984beee3e9dc1494ff19b49db9fe78bb2d0cede00fa6 0d0a5a9d48c6a187cf18678a8f79454994cc424efc8039612f30583bcdb6a2056ca34b7ab2589e521a3194f77edd44e448f083a3570f679062893fbbf18452067721934ca8dbf617ff4245bd15086f5b5b8b5139a70c6cc881fdf390e280460a772bdfa6eeb4aa27fbb53401e04ba38dff3bc55d9c61b0e1ade0d02708d1c402b3ff0aeda56466d69ccf971b1e0553374f3d61c541c22c98ded43ece7b15af074b139e7f7921483fcee4bea1957f61434d4025707802d957846d7413b3308508a89a60283527cf6ee520d96153508ed84507c6eaa45b6e0c942508c1067c1506eff0e21afafa724e263b63341247c18c6b6c2ecaddef090f3828cc83fc0cc10457a9463ced0a2a818be85ce4cd46435b312fa82e421c45ab53c15d2657e6c70cf56f8e50e40e20aa209e67e3c559cdaacb2783d4cd718500f1652f1473c2160472c6e612b0c79106d5f4262b81178a714f6a45636d592ad2adc2b3dc9d16b60b4a2aac36243f95128e5d30c47a4e42f868208f33427a9cf2457015763f4e040909d9e4123b9708899c919600dca0962800ebe9e63a2c1237ad3910fd574c2a06dfd8a8767aaf9ebf1816097bf5e6ecc6ed12d4b8912712ef9b671d2205d4bb07372878561128890b00edbb35047cd2d9fbf236152cca1918913d946a4a570001211c2d38d1f9dbc2ff97892b9388ce9de130d512c1b97eb4fbba5b51ee99f00a4bc58791c70ac3e608d0961ff18dce28d2dc7c3d8f02b71fde4406cd6395bc02efcfd7df42983ba262c1a0fdd8090c7fb2f507d8de1913395c753ac2a2cb4e055143270b306f386900b08b1b550640944152ad27c9a8fd203c4c8ab7e5e91106b8c75b5276919eba21f74f75913d7076839802c53235aa674e3840afea8f7603c7beafe5f3ccd3219aaa2358d6534d3c51c6d4783c869b34e688d0809deb050b2ecaf456e4f2e209a71c99747e5742d1f4e2cbceb338f22b213836e31ad89100cf50c3ed43ed53eff946254510c3a1c05264c5a631268b9c8441577455792900a46b10e825d8922ae3b544d62a01f1b08f347fbe4af76c13af416b9cc4082301b7c2acb511f2172ffcffdf823264a7052d296333907b9447d201220223c04b05bd12933390f96d98e0269a899e8bb5610be20fe95850e7c04a182dc08e765308c5f6c54b5b988222dcec20aae5881f5fc17c174e07071ef9bf33f3e90e3f8900c4e360dd52c0ea1cd1ad6e5dc8ed3ab2e66e6c05926bcb07a08fa7e304880a0fcba31495576524aeee0082949c1c9651643bcbee5d6ed74dae6f5ab422e80d05a4cd6acd375c84a65b0fbde583d280e45cba0c50e390c96d7f2e2226b1a42708 true -check_ring_signature a4ff57efbec08d022e4167fa4245509da3eaf6d245d36b52c710ef88adc9f171 c8f6a5fe6e03d71134fdeafc5b0afd9dc42078968531118b79e113b85964b0ae 1 7cf9408e2c22855fcd4a06d5e48e2370550ba11fd2024adb016f6008069bb64c 0478cf751694133a96b2aceb15d0d7069078e372148f1c10b43378779b50fb0a002e9617655865434d54dae53e348db75559bd38091c338cdbc94567868c3000 true -check_ring_signature 097f7ed37df34c83b3d11e1cc7271bfea0eb2726432115b5400bc329a4db1228 b4abf921aed94913e18bcc27baa08a8420896858de4901058ac929b1171b3b05 4 58004f281f5b96ba21a0a3406bc4b9f03ecfc7529a9aeb33659d38b50910f4e8 76e0915e5aeb013b5f25130ebc434428eb7a0f4c9810d823577e700ec44d3e1f 3e2b9c7296eff4add870826f27fbecc0c891b4cd760944a0ca1f986ebd508d05 e06bd7a644197a106a418838e7028bd055d27669b4f9bd614ce1302086e741e9 f715b921bd2f793a70402acf356b1493c26dfce0c5fa82f531e831ff2678ea027b45a6936031ef7691c3c741e917d6999d5f1758f13fa55f3ec6cebba86942024200599754083b0889a3d8a9839472adec5dba2882fe6778c52b02a670564407851bc3de3994e13462572cf1f59b14ad893c6538b13ba597ffe099c1e4f9bc0603ffd035d3dd7574a1095f0678c3d416ab75a28f133f050aab2495891b0cf80475ba0ee15a39eda141d7f50670b58b93e3c9ae89b2dc05f8cd233143a6cd3203fb68b3f8c309d0cd841a38bce3bc5e0df30eb980e83aa4a4cee229f21b688d044194e1773f977c02ac53fd058730b2263468e10501c179bc1ead43d759fb3b02 false -check_ring_signature c8e125a2803015a0ec54f43d24127dc9138f64f7750b84d7555694bc6af3b59b a07f119b4c6790d6d619b2b2a6d3256eb0569bf9fcc086041695c45eef923af9 15 d790dfa0066405d5c64cbd00534741d9d6b2ef7be6762939b1cec92f7838e509 37c302f8d5bd9e1ff5ce9228159ef51cca7a7d5e141563e53bd9262210aa31c2 9bd435f8c89460f097bb35822a31190f8984657be1bf9edb3ee4836718da272f e548f0cf629d269e8c49c3a80010255de58bc8a12ba645dcf5b842b5640c7695 af8e08152e5a25a7ec6e4de5d01c7b781add77123eec67a88181959f919ae473 09370acf9fafda6d2c7820bcc7940d915e20d6b5844e41a0a38485af8150f6df 86722b839c1e4061594d87bb928c5af561a624045d3ffb807535e1dfa010bcb7 ad24c1a8c2a6576daa033082e9f5c0dc17ee50e8f124bb12aecc7f0e19ba37be 93ff392ce51c2cbcead618247b2b0a69f95dd93ebebbfb12bd98d73875d1c7b5 de5c854856ed021b46bda58cc54ae89c99e4e98a130c77c9df7ef784b76105b2 a22a01b682cba5e28f40ddfcaf2c36e4c3291640b2968c176d54da000681d990 cd66256d1fd4af41160b129350b09dbf6978691352e212f3e122634e5e99e355 4da8786c152dc6e7e01ea81f2f7595f4c56cdceb483c28d2b489c8b3c6cd21a5 3d5b930d797cf6bbec3014f769677ebf7e2476794db3a9e618f9020be1acfb09 019c6e93ca0560dcc0c0e03dc87a4cc3416bf0071ee0512dd697c928dea12732 1e88dddfbaa9c1d3b31e1ebfcb41baf90d793eb6df707c14eb5aa788791f0808dade2b288575deb7a87fc8fad42a588c143ae612155ac209c942d6c3c7a9920d40293f9babe5a66580a0c661111cfeff4cdd325b91d19eb3277e3234f07b2702fa6fded2a53ef06a75e8255aeda94f0349c7591109120fd6bcd405a0608bdf0e0126e4073f148d4c3d1e720241a2cff81310f852f342f19ac2e827ac5f00430868ab59353adfb050c29c05175514b744c00f3a2c479bf3bfb9ca41a9202ff507e821b7d7b29cb187fdfbb66b4c9001d2f29d62f79e7dd966865bc10ebcaff60ea7d29cab5f583a9a643e2d8873566e41cda922a8b9d02848ac554636d67af60920479ccd2928c86517b9f83e0c99ad4ad7f94118301491923a6ec91b13586c06741a69eddac2c0ee44e2ca20815bf47f8e415dfccef1883bbe19782bb3cb470b907146323f7eb45ea7a97462d95812150e56f31b9a0566387e7f1e7a7f59540ae022790516ac84183fe28dd311abfa4f1b5149ed12037f3dab7db7d3a282640a89d63f3b9553e76427922915c6a4967cdafb201de25258c6965b5acffc6f110151f861310b8fa7934789913fdca266447899ba7e5edca65d713764b453672806e6777194da9ac6af35b266ec8ef5076eb8826cd98271d8c637ee83d78fbf9c0fc651aa3c18b2a2950322d52933a71335d5409ef92a91d2f255759da1ccb9140eb604fd260b81e42668a8f0ef9c7dc6214571e2764ac7e11cf80e56a80d88bc05e5155c69b8491595e59de6435c734ba15d994311ec68d892e20bf759b59e7f0d4ac7c2fb9ef07cc849f0d7d83795658861a88852b27aa81455996b393158db0b6e184981f2d032970ed046c41de8855926787176d8e615682c3e9b287369950284040157c3c3ffaf34f65eb22df4b989e4b3fd2272a6a70316dee7a02507bb0097f5dc070ff05311a4507b10c72ded8069813f82512b68c85b11a352c3a9f10097107e2219febf83facc6854f464422488289c1d7b2c6e36c4b1d6832bc08b0dd555b1ed2c8200d5c02d04afb6b74d2829aa1c8034ec8a00556439f3bc7291b74e2a54f321d36fe412ab5b2e7c302385165d2407f2425b769e4e38d165f11d044d121c8a432b0e12bdc5c66265ff758564a15c6e945099610abcec7002509d02db644a74d8859c83d9e2d49f744b760f62a906ca4509159927fd81e2f51a9c055f7c9d2f83e05f89b38628ddcc20086573bb4875ed8094d1bf3c1f19bda84f02e0bd89176c8a177a5b7d4309bd7dfda0f527c63d3043471fc2d5165076942404aea89d530af16b210d49204c8ae1ab692b9976dc0a01a58601703198f5a5ce07 false -check_ring_signature c2026f2cf6d83ab34ff49b50704446e307cdbd0bba26ce77589fa597c75317c0 54f9c165bb53aaf198672bd4bfe1df319678e4583cc3a448b06ddbf424007a99 159 b208445a1de1894f94f09d7e0d5adf8b41c708caffdef1489630f465d8a39650 7c2b57c744e94a6961b7a8cb1f5ba3e02dc0c22a50e4a6767cc39403a2b70f75 8bc05d4ac4b1b5640085dbf9b157d61b40455603395936c9b32904b4102d949e f8f06b64e38d574ec8d78fff766606d145ba16a7aa6c39dd24f83507e38beabf 371025bf0304306132cb507e13b8c8a39e8960c2842f66cbf6a8dac2ecc45c93 87a1df7e107bea5448af05a5e436ed84543265dc8e2a6f3d8484954c9ac7565b 14b81ec06a00058566aab0903690ef94ceb086812d58805b4f606e9481c1d51f 49297bc2b83e1d7de220c7e13317a7d42e5d130120bab8bc037f7f13d30178e1 3ac866e6cd66bccd829f0483670e43d4d80b9638c0577064460648be84b15b3f f233b0053460eb824086d730d77005c4e2c08488fbba510e338183cf72537e5a 9992b7c3086230fc74ca35e901842a81f98afa2a1bc83d006d271945eff61c9c 79d3afef80ffe4a8d17590109d4b795cadee3ab1c52bfce42ca598706bef0a08 2dda3aaaa3e2bda7af3cfa262c8ee949b9eb046e39304ceff1ec0d5f81b3356d 55d52539860885b0a40cef360d35bcb8e646d6458ea0e0e89d1f1c2ab20ffac5 84917723c86e612ae0667dce3e22cbeb8b6fb56e02f0e32de668c58adad2cc54 15e33900665f9bdf03e5c5d9a9429e2d90e6abab254acd302fa141f4da5c4fcf d5a99aac6d8736d131f81a9c160445d090d630e1f3a25a1b397a84ea1a66ad05 38e979654824dd1af302a6fe98463bb127837a8029dcf1613041c5c526efead1 22418653c52aa045162f43a028b263f3ddfc99499ecd9addfa6d8516f8cbfd17 0a0d841c01aa095ea07faaecf24d50f34b59ae4050695e6cc198111a57c5839f f503ceb9a2c671a25f80a41d37440e33f7f2b3319b8b64412aa2d00551ae4373 39fbcc8487f141948ce537b454b6711e5ea22cda89c874f6e007dc85c175df8d bc80d8a475a862f48aabcabb807ffe3c28982e105995c2115958ccf1c76d71e6 b56c85f66559eae18ee3af30185b387e34971267842d35f11b7700a6dfb9b796 448dd3a88dceba1f8dfc1b03f168a2d54f819fff12c31f7c1cf6ac75e93c03ad 2b342dac7296df856513252d484497c9950f14c8161cfb625de02b1fcd0537fe 186bf4075dc04b128206b2785783f5c70e378e944177dece256f7cf5b930b161 2cc33a51fd3c6a981b0214a65fd0f161a65fe07a5cfc596b26b527d70776c874 0c885cee2313a6a17cca237371843064afe5ce70e6a719a46a602a9732f03330 77fdd331d431e2362fe2e725d473072cd1392cd3dd738d6bc5adbd1791788679 6018c510ae50c364f2c636cbbaa34cb13cf265bddf2d650011dedf98f7ec91ba 763953c92409c2af4e51b34b9bda6bdc23572e927f9e8345524b503d33454667 2024fa519a15dd1bd911b8ad6626192b4d7edcc1522dcae05fb8cfe65d6a7ac0 adf740f85004e4b3b13a38facbe22673dad9284aeedd8251b4513a77b10f97bf 4976d92b9ca2251d5bd3d88c7fa0776df8c22c5b165dfb35034df65eb60964dc b301e6e26e79e54f903c17d4ac5a2fe5cccee4152c64fbec5f76e9b7bda7241b 0854a2961b00dd31d0a6c2b2c525fdf3a7a60df4b0d183de23cb42c62b01b7b8 7964c007e946bd205db088b2c7388a56bb116607757c7e17cb2d48bd69032ab1 ba3bb4fe2213f1de956e2ac4ffa2aec483f39a5f19bf72a95e21768d3bbb71be 42d46ecbe76a0e4b16bf0e75aab91ad4719fc88a2a606b417a7d707c67d3f70f c0983224a0dba911c02c6a359e7af4ad983508737db43a2bedf39173170d36f2 c6ea7a1d219c492445d020ebf8ec198b4fb3fc05ecd0e43502c437f7e2a84d76 7c2096534bf97e75a6b7885458b7abf8b3957f101817b057873972728b0c763a 01c01099ebb5f6b7edee71f5425b50b571e8bf4a20674838fb8cec4f94cefc8a 17aaaf04a5eb3139316fdeb66a3f07b8b1c44cb323ed60b046159b8355cb9422 ffae97d5c86c91a6b716e088a1c4ae18649e97cf73ae2d0fc10dc725beac0d87 1a942e643d845a540497605ce28b7b3b2b0c8d4dabd38cc1da71f4c553d34fdc 628697f62de142d13606c6a66b8c4a5806324a5f817aa5886a3d890f55ae2b68 4b295cff0bf9e8f306e70fac79a5686e35b8ccf78a832df81e12b33bd8ee1cae 127d5bd0604912cc3457e3541b677a13199e0a587d79f8e07118c889c350db8f 87648199d6f9448e56a5c79ce7caf9c4fc9af8aae7d7d0c4d6cb58c85babb52d 770c9dd55dae262edd1bbf5378bcc262bc35f775f6b025bac4a97c0cde498028 a7936493c9da6288a0578cb64e3436fbf5553fd9a80708b701212a4cc2507352 5f1e10a2a441d44a9cd7fe2f907b82e276471ef6fd03af7a552fc67ebdfe9ca5 72969fec58ce915bbbb5713e92afb130137549a24514d765a97befcf3fde284e f6eab8ba461805a2cdfa3bcc0329f8833d1d997e733d829eb4fbe2b5a296b58c 72e2435d82d1ef9c329eb68e3d6331dc0b5d2edad747d57826231309dbfbf11a 87f413baeb34869ef7284f520853a02ad22a947900fbc5c4fc8ae528bf67868c 9fdf7456cf670a43642a0263e70ad3ecbca668ef9ef0f0f6ea23961410504a00 f44fbae50bf3f996c51f1cb32b565b66f5fe24d6823d6457c47967ce8bd8517c 194ae1c0dd025f7d672260e9cb59bccd779dc785a55cf98e92df33e53ddb623c e0b76e41f13551bfd158c7e99bcb1330042943a032c8caafc48c0473fa763ab7 cbdd95f9c577ea1cd8129151006a6e7b973464e9d8e05c06498434f784d07255 01c0fa2d6f68a3df93c9e4d4cf1c830a90108078c9741d04d44b8d2c0aac7b87 d104fcbcd129bc8a1194ccf87089c55f7685d79cfa06d1c3b5108f7d1b6d3957 51447feb03c02ada28d7e1009b781a5eacf659200974966f452f7156c8218867 f45b97416ca684d0e0d3c621a651c7ed5309c6b25cb50b5a054def1a6521b244 774df97d8dd0289896b2fad2c9c6dd0955cb0300ab528d8b9a9848eaf82e3079 e20cfbed4971276359b6f175e787665ac8171adcb9c268dd264253c043b9ef75 4519ea31675f888db6e9a1353b4d7aa49b8739c4d1b64f9114ec505eccc8fd51 a5114078f8d81aaee75dd98247600e3c9143a5b1d79fbafc7943ac5f568162fe d0ff873c399e22b5de7f7050c992f58a4fc7ec8f2fa233b64054845e9aa3df0b 6c35762ecff7ca098a78bbc26af0f06638163bf1a783017e1c2f97ddc21d33d6 99507f2b2a285eb5a110b4075682bfaad48789533a16583a1400fe8cd4e49238 8cbdd0750799cafc52972553dee7550e98e129b3fb05fb66854518ce2ae74e5d 011ef6162c15d4067307c738a1dd506c2c1e946a1d3a19ec9a853a858f9241e5 ae1fb0bb370982ed52033a98b7b5a1197eaa32f2de4dd16bdf0aeeb707e37856 9094af6e79dc37192790cfa85716ac6bafe1cb3daf459a5068a31a3a4c7a032b fb86480d8cf841dc1079ae02bf473c2d3c115422db77d1a322eccf4b4384d40b 780ebfd85a51db47ffb682297b38723c1c38fd4858f3d624d896106f05219dfd 195d0abd8174b7ed8ed2691834e82fb3382062b580020c9247399f14aab6fcad 5c15f70db83afe5e810fd7bc1855f402ec3b5add5bdd8fc01be2a2d4b3b4040e d443022fd837bd001f3dfa8ebd88af362bd290bd81782d0de0836d0079369dcd 0d29dbcad4ad15a0b0cbfbe947eb94e11386ff652194fb0ef1f3197588f29536 db172dc50ac9d130f44485b1e62c332f455b3450b7f8532a8d57a7a03936520c a70edecab88f6f020f2a8405aa362cc3860378d4223f8440113199a91a45a92d bf5dca10692fff5b738efa660a367404539f5b78a8d2d47ed81e241d2ea09fe7 1967783a950f676d62a3f1798f80eeaaeb04eb6a1655ffb37ee806ebc1d5a4d7 74166777cd2926431521df77f4d1cd0c02596784dc0f364f7ba313408acf5f55 cd17136683c976829030907622c977f175a2127f1922afed3b23e7e3fcaa3e5e 482cefb5fee0ed7f13e6227d70deede6e9d3dacd7a459badac46dfe1873c7eb4 6c9220ebadb02115a413c523cfa6b3ec5d9d7c2f3ea555aa247b0e8b5572cfc1 3093e21f459e6f8d974e0a8829aa2daf82ab1f8192990af57916a502ba2aa9a4 ff498b8bdfdcb51e4a9a6670d0594fe95dcc9002264553f9f6cfdd3dea2f4b3f 1466582785a4da5bbfd178a3fd930b33f355de2f9eb37fa83a56e50066a187f4 e3a8735b29c5fd2391e153e248fa586750d4e5ae3b12639befc5434ba79328b6 18a092285b90e421a139007bbfffcecbbb57234244b7c6e09c2da24f2ac95a46 fcdf65b015e67cbd5600b70d62da6829d919335862f54a367a947abba7a53a0f da30b43b48dcfc6aa0b35c322cad21e7dcb800071bdfb04cc179a279c3949713 4854da389f38496cc0f6194eeb8bd167b79600508ab704e8c749fa143e31d6f4 09e055e7a06868ee38720f7f814db87069d75f4afea4be78491f9069aaa9fcd9 b614e29e0aa8c41db7ca458df1a9fddaefc1c5df81aad8af97e8b8f1cc41386c 26e856b9050557a5dfd1ec2b251c78a2468008908d1fa1af21e951067c012d7c 61605333e975068a2884ff562d9deb46d6fd2baeda1b67f48f73060733496cb0 d9ef9458041193e8cadf2ebec55771951ea12c669d7dd54ec3bbe4424d8ad2ac 0610b03486c2ab1ba4d7795e6694995a2f3416921fbdc0be1e6f86ae0dfd0515 d747dfcd3c39fd20aa5523a0295007075fca7d9d293dfd1bbe913899e8bf170f 5f1f0729c682b35fc19c97d7062ae5e2acd5b5b4b6bcb58b6b86a31c44ee68a8 d08e2e74920d0aca41fe2b5b9bba639f62b98a064d10b867764a8115afbfd5c0 69680ea431989a87ea87dc3a90b6c8f287de8fb23318e4876136fa94a309adbc a53404826194f06da4a87b6b5b10abfa3ba79a28178178d8a922ab43c376da54 8d540325f9ae45ae9f522bccdc8412ed7ab24da2bd5c1dfdbf71e68a04fd8183 a7c71bd10ab2cb4cb17042db48671fef6fdc9ba6c63f673c3cc0244b4fac63d7 b6760ef922db86836db2a6865ace603e9a980d090941eb0f71eb96f5da7201b2 3c3fb05d3daf2519d4ee4d263a8de7c1c8af4d4818f9a72000fa1fc2be7196c3 6f0ee81964796be9ce8e403214dd4885d82b2f70f46307dac84e7452b8b5c3e5 9d9b89197f9613bdf847e536205666d5798cb15084e8c796ada2598ea6e162a8 72c949ca75a836d88c7c562e9b1b7269d709074077a570ee22bdbf31eabe151f 7d08e3435a79448cb63cc5d557184b8c4683f66a517df4a6c736fc0fc310f6bf a3ed4608d600f6e3db7472239ee3ef7efb8ced352028963a9bab730b8f773624 53f538a3fd3fe1a0fea67ed241836c9f80b61edc6c29b2fdeecfb26cc0ee0695 ab045c013ee9aebc579ac27965b9acbd3c1eb44d11f0dc64ff295d055753155e 1f6a41e55a3381e49e4fad505ef57dd133545e1051bb503435a599dedfbf5e7f 63562b54d6dcd7e549fd9d76e5b13aca368a4ff1349ce663e588535809f14250 94678b1de55c39238dc5ccf08b40f42cdaaf3d60fd6001d32eda98a92fc410d7 5f1273ca58604d06ebb827a157746575c7817eae146cefd94afc796b01b66c6b 243fffe48b3fbc8db2951f2ccdb37a2fb26224e3d8ef9eaf2aeff16df4a5866c ca15c538d451042ed33d772e28dc039e691f479a203a49539c65876167a9388b 4fa32f7876ca3383b8d13052feea7d15ad7ed67f3ddf51b79bee0c942ee38b26 2a7dddc4a77b0696418124f0b99430acdfccbd09a34e448f7cbf8e6413bf62fd bf7553dc4a20a6232f22f860c19eb74add5928523e45049318c55a40d95701c9 173399ad9236e5e4a92b1aacf494a9af7d658f31005107452d5ff5d61b26db0d d7ac9517d617dd6e836e8a53f5202f696f41f864e2d86878455bf6f3d8074431 6eb42682ba1d2f7b40c6b9496b1c447d7b3a7f49f616e1a4358c1acaf5c8bfb0 035bc9bc738c7875da9fbeb350d62421c01decc36aacf21fceb4c95abb116fec 64c0c34ce0060d2863f4b53765f808a2460c0559b69f9aa537d85c7fad97dd2d 6365c0a57470492f75093315cb46555bd3fa4df89fc0dc8459f818086da31252 d3b1e78d8488918682b6174a05eddc818df4bb5be87a2d63e50327bdb585f87b 3ad41c04d0d722774e791e9d694029d707d7be8f06109018cbfea8d05b686032 d374c5b1f4315e92c61389112efc6814a30b3d9340864b78789d411c5c4d9e0b 47479ad5267ad4657d551e6af80748528d490ab7a04d6aa15de41b520e65dc3f dcbc7c6d5c0c1d4e6f87e37748151629e58ef8a9dfbd13cfefb6c7fc8cef6574 0a42002eee4c5502961f6f112ecbc0c31742b93b8f8fd2311121ecdd424ce496 6d7a42dcae69452727a2791122d5ca011c96248f09d18da70681ab750873d838 da71fca55ff83f108a1955039af6a22d0dc9a4283ec6a6ee22bec91f07456408 80fc4514ba6ea17b86005fd550f73ec0c83a28b1bed5e9b31de053ee1e0ec314 9ffb95010591d3dce6f6a5bb50c5efd1f67086cd6af972700444fed3e3bde9cd 6c722cd0051d4e2946d349980de1f492fb3cfbaa3a305055eceafb4ae1a2e20c ce5a16bbf2dbf526b764729267fa0e2bd8b7d4fd2ff1dd52f5c6b7c4b41bac7d 12ba67dd3de09fd1daabd11f0031201a7b0a2d4202977c9c07abcae7c83c7010 b34a30f8b000a68b32d1da29ed85977eaa23cecfd422c671a0f4754cdadb7863 940f627dc218737630dc51e436dea7c871f1dcac4c15b0a093c011102e0c0c16 21b56f40da8b7cbe69ddf1f6576bb9f87bf9b1ff64a3a4c2278ff1e9e557ee5f d5202b5a55541083bf7ea2a4670206f3d9776039e94aaeafb05a3e4164ff313d 99f3a29d0aa324f346fa76dc14aa5afa73491b1a27f4015696def6e3c45c4cb7 209e899e71b13fcdc294a4f3e0ac9b6ed24c2abde5d8fcb2b10937dcabf8d66d e3f7dec0ecdd2aac89e0cd4af951b1cbe9b0d31051d67731dca2d903bc1e5ab3 8bdd24cc79f40d88c85287d94bc3f47aaaaf7ae3e5e9a8db980771310353634d 3d663d6e4c114d16d88278b49cf236db00d81538e0788ab74cf57a9c196832f5 f511798ffb1db60c157e5ff66b0e56a0f9cc9c88414f28245fd7930c28627b0ee068596b8bc82e1c888618ca0048458d488a0d81f79afb4458870131c234cc0b03b2f45fb3b782683c122006865b402cbeb0da01bdbbab87894ee7174bdde005620eacb88f5c3006eec7f3175e3cfbc5a1ce6d949a0fd8515235ea9dbc5be8020629411920219a60723b39d95f814463e0b87fa5a7d451337ecc4dc2b12ae206c95dee613797a002213f7e4fff9948fdee68b4fade539a5ce8fe4e12e3d1f60946d9e473a9b23660696cdef24bfb7b3434f8ec744a49f813cd2c0f6cfc76fa0759f020cf8c7f946485adcbe0a62eba7e68a44e106bef32c2cac17a33cff7250edeb7d0ae29ad4ba730c7ef48c0f7bc429bd6c612ddc64e3bf1b273f28b6b0c0a1d15bd321792a758af4807629d310432da05f5623f0c23ea7e294835dd726309e4d80d6844356aab4a091ae894d41e48506d647c1d00da28bba4873cc117df054d63bdc34f8bcf9b1cd639906be2d1d20e78bf92778b6876415e349a0b74d3078ac68449585193421f8a8f85f66e8d4c7d9e18c935e4bd333baa6918050201026166d1c3060bd6db8d8fb3d0c5461e0c71a3703de0ef3d0368b4a1cef015430be85f5775daf5e90eab5469528dad18fe07691d1757ba8c3143cd280ddd02730c7a510939b2b1124c2b09b71ab7451f420797a6b449d17d268407b4545a07fa0ec900f7a9b4eb1fa42c6bec3ed16010dd183abb59e79416b72d008a82928c8f0f4ed35c2382f6196865180d612b373b8467c8ae7ce304bd598de746bce052cd06962ebddc3dae1bd41fbf8ab69991ba21cc853394cb3e12bf33526626e3748e0770fc4d9a183d6e48422a2a9ebda1b8a507a50d1cf0d4b4b6096d045d1007540a287a2e33d2fa62d384d721bbc6f2fb72ceb99c94682598f093a0406b3dd48101a20827dc3a61ff5c32d6722e88d338c5cadd21e715189c4fe5e48d2e14983709f7261a801ea56a93a8c0366e2b631d9d0a2e9298da343da81f35fff1d31fb500baeb6f0b49dbd3e71c55e03f33fed2c2524825a8c2ca7f141dcccff7f1280e07ed172adf4711e120709608a2a091e76dc1b9cf35e3707f7d33529752cfe2430502b74f9f146220f371282960428d64da4b1abfc78251a119cd84fe8517096506329cd1c50b12efbb93119356de07f55728a39f19e292edaaa725c9890dc39a0e49b7ff3f2d5f6783e24cbdb9535de4ea81481a80e6d1363b80c4e515f5c3d40be71d8b662a983b6c89465ac7edb3d0f871bbfc71e85abad84beb057bc808ae0b9b0424f4dd2db71fe82a92577f70af0ca2e3815e731b33d872b3ebb3d6b39a0d2c80c41ade2540c93084263ba996192648f1f96fdf9a2efb71cdd02258063608dbef9739b2889575ee7224458c827fbbabdeca8a4246ed7cf927a7edabda860b84a3c5bbf4c66093b2a14c9c808bf2b89aebf02940d50b1f73cad5c9b3667505153d5c52a24bb5604a57dbe2d835a5453b31a239c46f6099f5e172d1143d9908ff433332fa05a15cc0360b7050cc848abe2204645a22b0faf417286f2f516f079ffadf5004aff8d0f349865f79bd582376e9641f70aab2e74d75e0215c879200546c86323ff5b4267d3a1c833c2bd382c6dfbe5bee80a786a460da67cc7fa90f02c895b13a0cc361ab27c17cdd10e315a61ec8727e6a71e44032d3335be12c0b6afad6cf6c4ea5f44641b5717578010ee54a70e82edfb1059a03f6e3d317740b5f50d2aea78b07a1c9172551d8c51d163bb2c1f714c4c3d618327b6364c97705777e689014095a250de23d3f0152be49d8860d4d2f232f78687f05de8206a6054efa50042f89d36559996f5b4f9e248a87b33278e305b95ea4625e6e0272c806aa68fb164b93ce3f6f0e529a40af7d4cfc4c5be911e57af9353f3d8c7ac2ed071f5d54bd492ff726257af0f38a768f4d6e789dbe39cb8f1d5e4c140b114a760d449237bab5e37fc4277772642387f5885384fbfd76b26edc505ad057958ecd0d44a8ca85cc6a708a458071739f8766034bf6c5ef55b296cdb43b8c7806488f08271b82362df9006bab7cac8701849adcfc02a5819857596e23b81690ad8ea10104fc34a470d1434b22b1873293d71859a3a407688f0700dfef292f893e69d1010f0f80616f60ceedc85ac788b71ae4530a450a84f88847b9ddee469dfdd2db0547d9b21054d7088a047d34e36eebd67171770323e67d6f83b73969916170c90368c7d8194c099ec74b5a29c5e3405eef335b587d088e13c6910849aa87b3f001bdf5042b9d2b99a94bbf001871da67b803a91383de8c8dd438d67a674826240811979a10678ce29f64bb0d9fcf1acc46dad83c072a75dbbcc14f84f23c2b6d065117bb2aa9bce959702476adc6bd7a353934efad815a2527d5939be25e51fc073834dde7b522e91c2ad689b3702ee86a4c4e31e40483b0d342035cf12e41ba0427a77760a7132ed957bf5b700de5b7f70b83c58f782acc6c2e29a1accd0bce0125fae5284be0eb6753c3a88c3b87b41f073d30ac8e7f1db1c1dc6004e3db8d088915b0960dc8024e2c1c27c78f6c0359e0996bd53f3693726ed37c2d939c3a00f0815177103129c55d62156d43b48a0fb43321ba49d8efbfc0ff248cd3e91d096a14377dcb4cc7cc2a7d13c0c29ec49f69e728c44a7e040cfdb865cc481add034dffde76065a188adb887abf34083a25f11cc061641d1341cf305b7d5182590be9cf04641e98e92722efd3a83ba3332e6be4fb40134bb88158dc9953577b4805c5e1ed41d3748c022056e6423e348e7b9c09f6cc73e00fb7df343a1e4d67e705b83e27fa75419b4219e2e6409d63539c9223baa0cb90c6a28caede70be33120d1ad324bbeab657d9346fee85225e987204f416f393fa24f0f45f45a4d71c7b0a83ebe757c824478c38fe5411e1b2e40107f9be2747e9b472ca00d93fba989c08f534eb3f9cacf31cd502024f889b6b786b82ee7c3eb8ee77a2e0a9ac4de85809a2176806ec4019175cf1208eb64555789bf3f18f78b996058651fe90e628590c11d670539a44244c67478961f1ae7aea8e8d958e7b5975d899a7374858a2b70c4d09256ea78191bff41bd3e5a7f2751028307589e1bdd7fbc031eabaaede0c011ca886bdb5023a0df474797df8b972571f14234257553ede88fb03b327910000efdc019b00f82d81e18c4e0a6d59cb7137e49053cf2c327c319440b0f01f1d04b300bbe2beca5fcdcfa79cf2b2f295b7168cf2ddcbd47f6b5202b2923540970c9cf5cb597633e95ccee4fb503a22cc239efa4c7446e4bf3aa6eacce2abeaa004fcd543af05e208b7ce8d6d8cb6355c5b8dfdc9204ee3416deafb57820d3a3c005409672f4aa242be1a355329752b52eb96ea6336db1267d03c82499ed83247036084c22c7b5c0f2d48271ec47e3567fbdcff3caf0c93f7e75f2fbfe0f5029407778d06a7e633f7fca47e5e94eeac5296c483138240c4be4c2ca71e9f84dc4901919aaf72c977508e08966587e2cfe35f8803d4b66d32d443492bc9c436c49b0fff5f84b8939d86a2b4c65f7a256705f608138f35559f02e3e6fe82fa33ac5e0102ed39822b2b91e46354a357602bcbe6dfe43e4ee28bbc359113e418f534950f275556456083ec3f61b832dd7c5d1f3c3022b29cb525d09d13626a6872edec090a72d7be94c2eae3e759c3aa70af88cc6aabee696f6949600ea23c61ae798a04dc7532389063d50fb99a8bcb81ea9b8f295169a45eec439b61afd677b962090267e909134e6cd67233b97079185aecf11a852f97aa7022a76ae6a8dc66bac5094507375bcd145b5bbe308f21536a3695c5c0d98bdf4e6725e8addc5fd2fb440d97bee2ef132927efa954633c089eff61174110ee84162fee13a571f39fe5560d9ac1cbd258eddf18a69730cc4959cb298c0fa10178ac671891d9a164c0af8506b7e4223470cd4d56c7c944aa4ef6cca69c52f863871b2824c609423d5f6a980a6ba0bd80d58bfb3d26238246cfa7220172b036ade57472603c6d61dd440ff1064e0a5833222e137cc59b189c6b99405a4198b33f839d86e1152b855a64e1600bc61e5c32a008bcf9f2b044f94f7d62c52eae5d8edaa9852dee9e50e3f5c5670c5582411c78807c9e3919e41d733023756e10fd71f72766872f68f905684e860eea657c164d7f489e792108661df5100fcb1e8db63229163277da69d95cfe440caae3dbb5427036b559bae68b7ea80393b5eb9c7918530cc53ef20c4ebd960106c501962f4aceac185f98b4b4d61b4a40eb29893a20d1d24b71a40a0d3c65a907ae03a4047aed74e4d3717504e631ba56aeb4ff5d40b1b4a45d5c3d09c49a7907ae28fc193c3710a3efa0ca2160f3671f027632273de44f9a350f9a75357deb04483e50f88f27f9c5c9a711d2be0d5adaca1c7e9012f3d572caacdb00b779900728538383ccbe5d43236a9e6e54058227d4dad977e281d849a4026372833ab3064a590aecb11fb95c24d7a33f045f16899a2a9860c821575dba99d1adcc01b4091eecea20d6e7f337d04a992b205da39de62bdc0334abb1e058ae92cae1ea1a076ac5b5d83ab61abbfa156348d3295117c602d1bc2907ea427b13671eac91700621a304b1f20b216dbbc5b6c58b03720aa03dcbc179e2847461826c9e2d505405c5438c7ad8aa1de21e7af29b4483770bb4578d38a137e1e27e524e868ec6d6039301f604182bf4aec14e86939e8f4bf1c8b2f9b7071608717ba9e9ba4b545809438e549b12bfe0f5f7f67fb8c43729913b8c747c0da1303a3c5c9cbd0a49850d049a2e517bb9ac131a88579d6d15130ac960dca9ff98c2cfa17ce0e930a9de06aa621eae68b4ba699158f7f4b030f8d35320447cb642a91234be3f4833839b0819568e70a4863e637592a2de6ca50ccd58701443a83eaf9d06029625e7886f094863fa1b7e1e36eff48c9f7c01df6328369b0dfc45bf3dfc5a5b26b8fdd5d30f04262e282d9049dc2573a354fd54e8f384f2d52a16be8a609a2b070f7f4ef5008672c93fa8e99ad1b5f414062172d4dbd05c7fcf2f2770bcb7bb6b4294415509452e9602eec9ab8af1a0ac0e97778e251404a36f50bd19f60fe84cd1390a440cceaa5d84fc5b0a601848e4e67327ca1a472845afe7c4d845092fe190b0ef09085b2aa1afbfe05253ff9b16c2240e2954a95cde9f0a3b8d643f68c427d908830fcc8920cc577cf5b1b5117140ab647e46583dcc228376de22ed691aa9fd03830abc8105c44569e79ab22ef6e4ece92701e2096dbed0a03b85fc9f0f4aa9c40f0f3565e2a92444700edabb5af93a599bd451007c72daf1e720ee2184af30f60e0dc0039fd48a3c579973d42cc761d3554a370da680d7d89ac43756d6d7c52430067eefc0999197b10425d4781bcef2e3230588d8a39749207bfe71d35ac6af9c09f811ac234c1e835af5ea2ca3d7dcdca03c59c3ff56eeca443620f9b8495837013eb6b67582eda22596fc037cb848e3cda9435fba44ed21bdf6209138274aff0c8549343bced296d2a19f351a8be7ec4c3d40723511774e9b05e7d6caf90cda0301c580fc4a7fc8e1844126c3a6321d46582ae48d787a148cd2171eea4e6b92025f38d712394fcf0ab9476be4386ecf425c62a144c72816f06619195e641a5e0861e8b5931417f3499c109c95b7264623af294c90fd4587b5fc41a9730b2de0052cdd9761861322786df888087362885a7c7a62c65fb557c28ae465b56a455002e5c6f82415b58c2ecec140b97b7f627f306923ccfbc39a81b9f9f50ae357a50749dbb615c243ae72b67d6a6f0367702a1b66b00e0dcb4774cf9b335778b43300c6bd99f4e849be86b4e36b1f070675af9112a5fcbc57d8e6868296e02b2ff30531053c0fb70feb4efaf91657813fa56015755d0920f4c747b21e508e248ed30505a5e239fa095ee2a37697445a11f38e5fe09d68e234f54fe7c7b466c50331029ce2add4cd7d97fffac29e1c1d19d35274e3f77f36974b2fd480618ab3742c0739e7c28c7c2a2b38e6bbb1dd4056e54edaf76da555fbad60874bea1f2ea37803b4943d19b8a0928695dc89cb96cbb8e6615b6abf180ef67e61d9b291f9365009d023354d3f399cc02ea4757ff78f0f89cdc9dad002fe3d86d923bba83c05c305157f09904c83b6e11c5bb2fe6d8b66456c4fbd97b3c7711ff254899a39f3a1083f34e8e3b3c697c46f1c7b2c2d42f8d508ddd37beca94e0c86820b779f87140bc01d719a8f976f22e38094662830fcea031b0eb3bc5fdc2ab206ee037e277d043cb647522a39a90e6cf56cc4070ac06ad83c9b124d98fded5942d9ae77f31e090e7cc64b0f784f029e2d28d67bfddd96a83c37047e567428205b6078a864ef0feefd38097b833b0277d26af39ec06978b3ec867027262841eab5f595fc446f04d58db3cffed46bef626e8cef56836375eec4db88e6382ddb5d55a36d46661504bca1a522986d4b4d099ea0c1d14933abe94564be48203a766d6be7624e18720fb3286a8871263718448724c7134e86e68951e40d75d7ccf8f56a85f025def50521044891b55625602455b11bfcb34b368312c42ec55b27bb09e5a2f76e89500e68980bf61bf643ae8a2605a7f34b701ed5baf428411390d8e0d2edf6338856058c949b937d730f5f35958a2be466315d0a49ac02b90b7cc896d3de355cba1005e249188cff7d3b8d95f71d80fa72314b00f6e70602da4aee0306064c87c51d069c10951d97bbb3180e70329336fd38aaa2ad2619355b065afade096d2eb47d02e2200bd62c4ef979cc7610fabe7cbe2a958d4254525f4765edf95b8fd0ea1d09505c94b25e45a6692895da6afcea7f2aba85ff1a296d4778ff7ec30ee91a02056acb912acc923cd1283d147174f34f14b99dda915b3e49c9cf45822133bb6f078575d028b12eedfe1c61169f2f14ce81c94afe0beb32ea0d6238add1355d8200a624f107d2d930620b7918e818bbc05d935cc11b2d79579ec58486c5c7babd06e9cf037a34061640eccac79093bd7013bbe21c555e5ffbf0def24ebfea92770a28e52ca866471fe89298363aa6437507a5745542d89ff744e7e353ecfbd422037fb761420c231d8e4f8c22ab2d327f3958dc722b6cde49a4545e40d88506c20cb6df010030dd69eff3c582a5dcb5353da1355cec556cdc6250f96ea15c249e008bf21bd1fc2f38528f38a3f1b09814d5f3074ad7acd06f54f2afe0ae996d900f883b409ce3eaf6affa96ea8bfc6e26cff824f01f03a20f6db6fbd2422e7723086a0cfe0354977f6e16a0100d9556c242b1b69b723b406fbb3ddf7c19fdde040f9770e1144c6c7cb2e6483c13f1826a83b08080c429da37781914ac7f09c69904f809c0b9a56e484b9b92c4298e3d25ccba9efea081aab99a56c1f4f64779d90cd8f0c9ec59848dc4fa5e4f65436e4bb377f88116c05ca646cdc26a5da6fa3c023182fb63bfbfd0105c3b73269cf7e8686521f83b3b64a428211d74043d468d0374345c1e722b72aff8bf1e7de6ba0c41e1f723ad1790249325558cc640f19b0510060fafcfccf1d9d68b8301514d633b9ff5bf368c19967868bbf12fe83ef50cd6cf06b0ca6bce8732c2b83adf3ba6d6bb101d98bdd702f5d78baf82a60078055d750d8b1be157cd78b737d376858bf2d12eb6db42fc6da6790d43d5bf67d70466835a42664cd440efca0c486a4c72bada7246d75a80ff34bd6664a530fbb7000f60614c5d1afd7d752d62f8e394b511e121c120e2d49c08ada35999fea67c04ebe0bfbfec16468826159b638e11ff4753e197ba2c7396c3b5169967b7a23f08ea7a2243b456b1333953612b6f9a451653f1d908291493a1c13ad39a328e1a0914680f9cbb2812257f946e407b6a304e4ded33b901c5b07a1a4d5f5f915d430f45b40adc3b2689a68ab59a4e5afd504f92b063647269c2129683f9fb8f3fd10d996cd53e428c7364436d1e191d1aeaf396dabe7851ab86d120d1a6dc5c980005115242d09d8d8eec79d88da96d1a7ce261bebdd120c551ea408012bb416cf70d2445678838d1620f8337d85dd4e16085dba0b1b27971a1aeb4a717e04067070839afa7d00f58de5cfb4d47f0f83dc53e06d8cd7ae216602cb233a956b3b1df08729dfbbd926ba1906111ce947fdc4f8a201206256f4cf58e7b28bcb9fc68390dba01fa5a322480fcd0504ddeb9145c23c3a8aec91d1d1db09f10274b5a73510211466a0296ede630e2f0aed6dc038c9ad2b39f40e74de3d70a817f03fb798507fea06f1a72f8a3d187b95d1c2f4b41f2c1d505a050e98e73dec80d240c15eb092a962e4494235a75ca7c763a0e542deaf93a62df82077cb1247821b4287f4006779ffadd8940efe2546e6b8fa314436b0e82c2bd42ea1e1e79847176d1b9b70b2cde024a6aaafef9c7eb2de1730db9940f2d7aa9656f79a8450eb5d0e70ff30fbda725f2b9a9c3c8c814857818d3a16f13215b8ab663f5ba401fe648e4f6e80136f8ddfce7c339785d57620e6d1edaae8d8f4b8a83ffb07734e27c7cbd7bd40264d35b816e2b95940f167080c0fe15c89a2be027be8d6030aaf5f13109dab10dcd084dcdca21123f2e573278f4c8c76b8027a76e78690b8006b4a0af4349890e47e9f1fe350b3a32eeb3f3a4bb019a446a45695d5318486fe1f7e79534c49c03ab1acff222353e51ca1ce5c06eeb0720308249325ec822a419a4ded80a91940b818e1a5cf95293ee6dd784388f315585465149330cd4f91266d4917c1b101902607e3dbb83672cf4abfc9686a747c49aeec6295bcf945032f9e3b15e60d42e0c6847c565091574a587c55d631d5c234d09544cb1bb6da143102c0e45dc47e9058c625c122a10227a9f4955523a265c1784e9c3c74554f25b1a6099f256b55507391e8886a4dd11e25ebf209ed91499e511e435a7de0f7042096786d08b40420edcda8d467e4d966895284b3e5096c93551382b7dc69a77de95d55ca0ea63f80eaf4bff879cbab701d0f1dd69f3201433e51a81eefa3e9c2e89aa3d274bd07a02747adf2a1db8dd84b73c53be2bc1d9ecde962be70073b7bab3f1f1ebeefbf40070959ff6ccf09d627ce5bd2c67568fb21f36434fbeed891c598eaf676ec2e40d85a78e8e0506afcfb388ce3f2c9e191ec073be86f49154272a4f361da23e390c0246f75bb76eabe862fcedc1acbd20004a795ef1400e8c0de3585c11968df30a94340e4f49b6a84881627e99e76c14d894310742d6b33392f07f427117461c0f0f9067eebffe6070ff468c627a8996c61cbe579c051be230b0e71533d482cb07056b2d3a2301232c6c4ac95ba386d8ed67d6e9420c4ae60e0bb40d50f41bc60b62a433627cbf9e59fd1ffd58b457fb7c1e2471be8db8e7b3818d334049554009619fc40c733423b3edad3a3b50ef8dd8e0c46ad3ea7722e2ba962054e7bf540ab288ba96214162c5a18bf4c9c67a9e9f80ff83997797cf0396f4002dae03f50f252b3b2ebe947265554410c80529f16f1ad5d37d23cd5f06ee17fdec07706c04315feb4f71466b2005ee97486c80d89488164ecaa1383e4cc96da3039b1ca30fadb79fbb0a36b3d2d069bf95d1862832de5950e735cf97b43be63a8cd5821902f88a9ee01fa184df576becdb3f03ee181ae794f68dd4a6ad0b22ac302fa6d10a4c5f205c2da404b3aa24ef99de22ad629379709ed8c27c76d5a33d15a2b05e0ebf62c7c47ff158ed6fa9849cc5f0c98b1674177c6efb116f5ae2db2a2455980c2bad789b50a66d2eb811be78cafbbb91afe13c5120d95e03e2a196f8341fc509a421c7632501ce89e39ba347bf5e1d9ca679b3f93e787c205f24014cdff5ea0fbc83282902fcb2378d84d3c81cf07641b95bc057a3263f7fd62ad669b4418702312272c349dfc479cf786b879d6bf23a034cb620aa6154ab44d81f771cbb960fff9bf0d9412e40ff10247a3260d5754baae44beeada7cd243c8c4bb43d05fd0712e172c7a7c7b2161d49f5262879991ef04a590b2701edfd9e1188171f626008d635d3408f802b10ebe3e66d3c1d019510bd17b3e657dddca54dd2d6e185e308576e650b50262145e7e25767b802e4fc9023d76c12223786e2eb71d6cb0854035218b770152f25efa0bc58d5beda4cc83576dfb5bd59a5e06af1aaed2f275a0a174f393d26e6376ecb97a7ad63a6443cd77ccfa694d89edcc231f6e495ef5b0fcfe7b4f3b347fce29fd9c32513fceea08a8b59862665f955dff6bc36e72b2a0fc7538e52409e86633aba75bb8f308d41a9bbd98effb3f849ddbb0a32a6467c02341a5947b8b129216250806e99c0c0780372abf3d987c5ebfa71673e60ca2d0f40ca60f00ba4e350c100d4d9e163585c37a47921662f7c967552708ea86e9c03634568b3b2c28f6787a186959c07f71e170383cfcb4e252a98931cb66715fc0fce90f434bc74aeecfe0bd14eaef32ca6cc0f75de1ef263046b3f8c85da458a019047e8026274ec96f211ce3196f90e5f7a059e78954b89ebb28c4ed584b7e1005966b354f9c4384e766cccadca8c15b672fb36ec28269ccc16e555156d819307256d4ae784b98fc702d742ed58dac4af93d5f926906e222d2d9081ba99031e04cd590571190670e2def6d07b84305652ecabd3437f1bef211c3d666e8ed3d80e87b40f7367e6b220af5e961f6d8a7ec1bc5b30a83402dbbeff3cf4fd5c12d90f040d36a762faa087bf11e08766916fe0cfd5ad21cc1702ea92ea15ff7bfc4a0b9db16bbe2c3e70de4617f85e7c71c385bc35734e0a37b93d63f23ee391bae70019b0481ff13b47fa46887c2ceb3452358f9b6bb5eb9c47d563c4ebe00beeff0f449c005d5c607bf024ae764b0097b50af2406bb67173c039b0e7ed93f121a9028948d1e7772496c14bf6ce52bb2804c14317ec382a6672c864393c8d99afbc08236ad5e416e384c1eb4ce86d961368d4d32b4548313e58cc6f3e82e4b7b6360c406832c911d084a487e7965a3f40d3b5e2b4d103b545e25c50d2bfa15e64d809388029b24e00f47dc2e1ba9b0cc1fed37bfc720d71b8796c36e5934dcc7aee0360b5290babb5a7dd3e45cce11be7e48b349468aa0d6438f8e746449ce7605003f580ad5568a1ef5e904c6a63b6a7b3672471b68ccf394c85ee6ca4190248300f0e943edb7980c1437925502cf0cca873798c301bcf4eae533786becf5cd3ea0e3e3d91735064bf1e34b95964b1eff468a74d7347840f554d375ff8218c5b49073005b2c9dbef1a98722ef1c870e190ec138fdb2efeff5bb74956789da15b560bc37185bb27254c581a1537ef279fb3e7c6a00bdd5ebbf9d996783d5048e6ac034d0579dc559604c363fd41ded75e3a715c10d62cea1e42e19fafd8ca1205f10e52c6cba5dd7a897e42020bccdf203fbcbbd6d24950c94c50b0c5adb79f74dc01386cb785902600a75ed0b75b594f5a0d45eeb60f697e9be0ceb62dbe792caa030f8cc53611db85b2fc968be859f8518182bd4bf905a9681c8c06056b4bf80707962c49f441af529842335ab22805d6a2df846915187d4d6a357868acc02f9703b41e21bbf4214919db66418e2c0a5d4d57c79e59f362b623693403187c998f070b010030c8edc05f69ddb652a58ae98d5ecb3092b37208af6ab93aae1fd2fc00371d7dabe876904ab1b032b5d7034bc624e11ee747565e181676f80699ef940f2f71efa07fa7af6ea8a1391d43c77634848abf3ac654d1b00aa0d788df2ad60029b2a2adb79cdf727b46ef8fe2f23c3030272d49beef87fcd2a119c3d9ca1d045fc7010958b953d1210e423d24196e97c839f236defd9ee6b75e444669c4ac0c23a585ee5a5b36a6634a033bc0ec438a87ea43064ca6088e4eb4584e713b85082663b91fbebd1ebc730824dbc7382ea7f3e68085e72d21499948a06d3ff42706248330eeb2454ddd5a1ed84d9fae81ec06484043703a67dccc0cc2cc81ad4a0c9c30736edd26078230254401a73d972da4233794bc780453e262cfe7b78c810d7172866b9578d577274b1a86ff0f10f3511c8129f60eb23292aa4ec0a6ab7e0480e3022e1cb4a3656536c6ee881dee8198e06e154e1241839545ca9a2565c608fd7dd22359e40fb98292122523d011a0b59b009c5d66d6a95ac93e9661b859031cba61b6b4d0a3b71bffcab07fa929ab656b1f7a13970af6875ae9e348c20a0fa78102fad7762385c3090cdd9ecbd1f83574b47025dc4e7a8096e9e0f2f2910662fd130323c3b110d8e6ac6dc6886fd39b9d1c577b1fd7864e27b93e122d7d0b23c4662f5f14372abb1a3af36f43b30cf71b598144f2b5e61e3ba28b83f33f0951714cd050bde0d37ca4ad6c5cddd3909450a7a4c610b578730d73f87da03a092a83a5753e7237859b938c962404b13d4c493334704c6b8574521638953d2302d48781751772752aca5e9ea9e0344212e4beff6669ccb786397e3af1436e7b073ad3b0f678d4bf9746e7a3a29790874481fa1b60512537246dc3a139d0e8bb03efe88b92ded7d5d3bfc664a1af9ec4c74e0545609ab2f252e338c2d5f5fce20dd2c878255f047a8e26b81d99c7e6411e604e2ff7616d993b0487db042d9e4c0aa0e65e30ef31326e05f477d07c5f0afe4c73b36404687e1e41f3fa62082df2048ef49bf4e3c79ce4630272fd9e1583f8f31f3789fc5ae3914ab9acf208e0070c9fd6d0a7edbb1e761ad23813faedbbd84cfbf3bb03409030944aca752c35b4001f6c670e3d3c23e12d8285754b0daf88cdfbdfe535c7e242f86bd23ee8daec04a86b38f83750ec8843c5944fd9c054c163fddafafa5cd532b9443b6355dc3706dd7d12c46d5865a3bda2916918bab3037009300616c9c5f523f44b0eeb5e0d0ed94f3fbf131028fd68dd4822e92c129bc6d4f9af5e46f120e072359a7969460a108ab13c6f5b5d410034f4f0af6284ac9752364e5a510025a01366047a004f0741a899cc8ec474f3c120cfd01c366b80b4507d7f9e53082a42c7c7ae9b6f5c0ee06ab5dadd4f542242419a5941a30efac93248b1899ccd3df79682fcedfe330a0c99c42471c89b67e73925bee854485656b72adf4a279acd98c09c01fe81c00a2d9ffcd87574c103f8e7d133fae99b22359f4d28e866782b7394c1eb67ac6c0eb29f009db8f6211c005f23808aff2004db004eac145961a9b5d4f72e64431f08bd8d20b5c711f7d98cadb4ce560f3221742697696d07ae464daa5bd539a81a02c87124d918c2e1c0648f78a9d95d6a873335804332f5d37495dc519665d1a50c5a6dcc104025645e7c47028d78b6d726b595581a23bd182052bc91f885ae7908d2c421c1ba70aed0bbb85f4b5f89867407209af0a0d7a703076f785158e83a0abbf4ecbe5f9f2c1739644cb31d7915b6befaf1521809f8a5736dbe461305a004f2c5a00ae4f0c446e983d7bf4f868bb340ef10cb4ce673b29d681e261520c90564b5a804bfb80e26a4de1030c151186d96c6c93a8b8252f93c235f66e537a50e53940f774d896b362bc48c835c7859534d1289eb7ed64e948417aae2b5838805f1a6de236d77bc6e51fdca9d0ebc24dfce50aa8496b6988dd238ba37a3df2d094eb5be4df75a43b3b188bff17f1b6c9867c7783407360de7a1a1a9106bc8d3052716c9e93988cbb8aad87b7b8da08566d0384afaa9612d3fb6cdc2e5785f8600d379d7ec9744804c409434fd07533b34d76efbc78c579eb042097ab3472c0208e86682e7467615e7510006a3e676761bdd39bd46ab682f3061d72f4cab2b940eb7371df10a069971e74224528b0ed0c67d0b575912d3bcb3eb5889f97f4a9d00b739e25c14971a60576d79f6aeed83a71b932e15dcf543e096139dc17730ae07ce9f563611569e7c73b158c70719624a6f9dceccc13bfca79f13d9c5a04c0a0e694cd16fa240aea89872d596210446f6c42bc77337df735fbbe8d05300330b0b2de87382b1c0518af76f0083b13ede17172398647320a3f1acd57583083e470bd0e8f18afa4e56b3e68d3be0995f26ce4a921cc9360ea534cee18cf3abe2270d1cd12ef388818572c2f3df1d2b8843a07eba75b6b396a867999f806aadc2800118390e0d6398368eb76d58f602f1fe43224b008c23f577e6de885268b11e4a05b27d29087885c7cbb33db3a0762d654f01b3d07cbaa697ce2dd99a1b90488006b5a780d3b50509ee9ce02161fe79ad16229e7eed786f48862908b16a6359ae0318e75d88551d2c7473cc1de71f1cd8eac34ea5d951aacfa70d0ff3926d3cc50867316cb3c94ad3b49925582b8220b4c7dfa51d49c3e4972292c664c87fc35c01 false -check_ring_signature 6d57effcf00feadfa3835b1ae9897312646e50b0c78e83fd915fdbf14c0348c9 9a1bf2443bd6fdc2468c50c9d5ca3d6719252db550f3585fbe4eda20f5800fa5 22 64d5280c45d12b1aa1e153839fd1e269dac6bd86e2df96cbfbd05234767741c5 2c991ac86d400bf9b39d4c54f5066683172a4df02f06d7e61037cd5289a0ccc2 3b0537296f38f058d39f939d6a4716ff7c7b11adbdf5722c9b394df63110a867 8e651b6b171723bfd5e065c413531d0ef01975c9968f788e6e4523a7eacc5afe fb75dc1b08ca88d9292bbfe7109c0bb3dc80a49ec63e9b224257ea1db09c90a1 4066d99aa2a29eb51fe56b486bd3cb2a3fadf386af9e1a894e7e2a8bfb85ecd3 cab92653c0461f5b09f001f0717307ec23fb9e270570bb047bc3809ab0aa8b87 78588e289cc2192cc018f2f6b518e0a77dce7e0f77ae19b6b78d6c77b5790873 9996e738f3f92769d369025d246c3cb06ef20c01019be184922d263c87aca7bc 502a33d204b5d5537459b69a249ef1679c687a066e3e3fb1442121ff3f6f78f1 72f5dc2bd6908d046aaf2338f73d8c830c6023918fb00f19500f5ce5dbf4c778 fd4589cf4a12070eb2c2f056d97caf39d9816a70bb0b42a2b5c120e1c2a2498b df4618a7f628c0c1d6add473401619ee98578d232e612389e9418479eb98c05a 58b613430ff8808cb77e48f697620a649b418d241db3bdffa3d92a919cd5df4f 0caa97c925c32243d36ca46a97e5dea2bddac333b30512721a04e6594d6bc039 79dc18a3e9796c954e4850be9119e7e2cb334bdfa06c8360b760bde6ad27a633 dc6e8ac123aea6e4344138f0cb62ca630695e594a9a2a7d5bc6827679b3a7579 6ed7da5e859bbda5473460d901be1d0d225ab08deac5dacf82f7edf1d1b30ccc b53e4c4d77d6718fc07ade62b6b69e655ac4dacc3dbdd0b94907ed84d7bf7eaa e318c52fc6ee5d734a6a6a050687ff5f70ce1be793f08f57324da6a8ec17545d 614763509c978dd6c081fd39c7232d3d2decbd519edaa362fd5c7f8a16f33c51 212ebfde55b6bcb21bd99babf3dfad826413c044b352305d2c5abf61367e4f86 0776b86076619927ac28322e0d7da032d557e8e63156bf392b4bb8e26628540b5ca653dd8b57f528084e4e1213282ba590123e7b36d904571c1ac6852d48ad0ffc98edccf205c13e8ed99062d06bdc71b6170c0e3477ba01600980d3e529a40e3549a9b487e01e91a10bb19435cbfd4038f09aa237d05ff9a3b2c87bc2cb99001bc30ed9d5ff4d0f2134d41c8ef7f6ae6c1d9a0d688db59501475bfd52815e083acd6f79c62b0ed83d549a21a3f0b4e3cea8647131f364bf18a76538d74936063609f68d56904255c98047aa5506caf74f59bf7631b8fbcc6c6ee1012d52e109f8450bff85f131b4a3c4f95079e1bf2f2fbe1a80802dd9c80168753e9188db0dcba0abff3d005b32df72440957be2066c727cf5409945c763bd8d61b1c0458051550f347f64523838628a990a9c5fa5f496ae336597e04de70f7915739fe9d06003658aaf26b90e3597c982282077a4abb92619edf303f6c07d1ce05deb00407ef11d7fea0f5545250fe857857aef4b037323a726034ad67fc0c3acae3847b03b7bf1acec2bdff3fef6a8cc032f21c56cf037f864f8d7f2afd407906d0b471005289f370eafb17e3ff14c4daecb78abaaf5b61c1ff87c1628bb5ecb8f6266d00632064883aac8afcaadba9833dc1d5a216b0c596f87b044a7a3e01be854d8e00820f01e8c4e93b80ed06a969ca0ee48936edf4d50dab952d4ae6f0e0e8653d0f7ea0317480bcd8f65cd2990285c150000a4108a0539b3328a9f0b61f5029ed0524711ad0268bc8e0763060f1bad7bd666a62e831ec23234187a40addcd429b031579a0dcd6762cc710bf204c4c616b837e6e63c6ce85c09011a756665689590a14dcf4a05efa6cc2d2a1101ca03e0189ccc34256e2edb9f00aba41c96504ea04d0d3f4f4c6aad741f72da85322318b3ef2dd7070725fa6abb9f001835fc1fe0d04cf2668b4f4c5581b702d0c99c1f703c3ae1a0fdfeb3c106da45cadb431300028bbc469dec4104a104e54c2da8d993af0281d79e8c3798fccdca6bce688710df912ddb205ca8ee25eafb9cadf65d0b784d238ee4bd9432f2f2546149364fb03b865de85ca4a4d6235aefc14157933c58faf6bbdac55d6c91fc2338ed7c2620d554c4e8b38f76430366078b6c6d5d7d42ed0477e4018c593d83e49be5205c002730c7e7aece78a01b11d5db2bcc80654ec31d7066989337632cc2d1fad19270cf3fd167929d3b2bc7e273a4b695cc93a674c4cb5f8704c1a0a47f66b216f270d25a4ee37fd27824b456a7eea518bd4af28842bf501f1803e7f19d897ab63740150f4b2f1cd4dfb35aae1b080b006005ff206ede245a924df7cba44c37aa21e04acae6761944be96ca6fd27e7b922b1b30667816f1d01ab98a75475e2c591af0967ba7f261062ba5afe53ee5429eeb31401fb27d6a3e8cf8ee8945823ae455d053aba7b7491a10715f785c35bdd6bf12b574c46be9ffc7897661dea774bee740ea100979ad981830595fc6926689350226181d3a51ea6dac8b58dec62dbdaf007bd2ca054c8e802386535797770db1ef18f141aea7deee9fcd7b13d8f9fa8db0eee31d1535302947231c2c6c3a510df8f0c31a56a4af93ae5bff25a1f5cade4035289a7dee6cd5a8763b4491de3d7685d3c559c827b4210aecd2f27cf91bff80343e9feefd22ab05dc77a7a3493876b0abd3499a6fa139d004bfed5caabd5e3093430215d436b2d5c37e02423b5b78e81a3cefe08f160fbf725e7ae2800e5a8099820e3a1d53ad50b02da3b6ccbf0d77f1a843b455c1903db8be50d4ad22090013781fb531031b853978a9beb3fa176af1bda02d0abf1d50aed72ce5276ff5800615ca556326e60cb1605476c7500bd85ede68a2ca51636841fcd6c4b0414060e723a5ab5df4a7d92b4f6f52d8bb444df79794afdb596c23730dcfa2215d6b2092f6543360f9a7997153a81382f5b0c9c746f30f7acdc389435b72378a2ffcd05 true -check_ring_signature 066e91b031013237423595fa5d24341ba7bf0cc41c39b5034817b15e81b276a1 457ce867da6b9caebbd3a1a29fc54cf712741bfe88c3a89eb76e8ad5203dada4 57 ababeb3522b7c7a42e9c7f8babc7cdc61456e811fec10c20a98e1579553e791d 18292e35123e807d412f5a00dada675a08c7754d30283a98996d8b1d96c44b8b cb7fd5d8f467bd0229ae9d74e6dee1cf2c5b3fbd87e36b38b1e82262a228f0f6 28224ec3d75595e5a82c6aa8fb5eb459dce1b08e13fbf913655f301e4bf56b49 4186464af1c14e9b7b406199b09649c683e137b23b26a167afc63e3dc3a4ecdd bd138fe85b1f0369d1f88b6be5257b5ad8fafed484f9e2ee6f14bef105382d72 4ab6a5a494c97106eb4e0e2820203d8997eb006dd7ad6a5b3fab29ede39ba23c cdbc52a1d35433e8c7703726dde984429eb4d38a6c93055bebb1bcb903b89575 50419f75c5e8c9c2f3104ea8356ccf658679eee06dab0a757c72c0ca66249c82 d700e94319b373adb8885c0063b7c19c4f50ef71f848b25a81ee0ae433c98f21 3e39edcf0c0cb9ee37f04fbe874be36f9ca61d5c4f083ded2f3c779565901cfa c85fb53e1830f83ed893fab71af108d523ed4819f7effa953fabbc0e812b24ac e27c71733d73a53ad3f2657e032a7189e28e96e02295e3d43592b9b364461f70 d4490843a53e2607b0c5e3ea24cc8347d2d84373247724df4a345618a2f2666a c4ad6e588d6a3864cc0ba9421ab570eaea7ead0d4893f37a60e56d21c1a87d93 f87f935fcc9da895345504903d0ec4309bbc627edc33b5a3f954952ac55f54b1 15622ba79b396831a19df4ee18defd935680c66f2f2d6ea5feb837abf5a0a180 4423e1776f45f8dee4589054321eda81feb61a4c1c5a318d409e37c9eee71aa7 c23c69a722749e7c5270031348c0e04787249f3057a8e2fcca4ed2dd574e47c7 5954e93f748d425188f3276879c37a3250fbb507eac1eacbe8892a39187f73d3 a5d0bebd71fe0b4d4c60c6aa7bb1beebe20595aa0171925bc5bd4a3e48751eae 3f4a8b00dc61e7fdba2034b08302449d0e8c2e2be17925f167be59b3b2d1fec8 6478fc4d45e8f4bf441109f8f6d9479786f1d687c91efb21b6f866fe010fa054 ad904b46ab7eb76ba25467091984f44c2a2eccf7b0a6517cb0d9c4b00a9a7b2e 4a67f25380397a18f252368f83ff79494e95ae58a1aeac84979929904e69acd2 bf9123aa3f9fd8f6f56b41dd4be55caf876b08eff463a37c220a5fc0cc028cba 86e6e2bedc7cce0e29caee15f7293f0692398bd55640ef027fa7233e3cc320e7 6a09a5034b5f08e84f0928ceb64f858bdeb90704dff47bf45f037dd9effb8912 107f61437510b9a267d1950ba48e068adb62e1a809e94915fbbb3e12afc3c46f d5f1d68e152a737707d784e7089f1aa66a419c931166b2c6106fa9bed05b4ffb 649b7c708c27a8ebfa0bc498072d14f56d28385b5c1a76bc22107699ff150dd2 163ae901b5d5d46e92ae3c0ac030ad83ffa7532c97105979a728171c63dae7bc 50ee816228a1ec477a6378bd9f58222d87bd4efdc350e48e485497001d38050d ad060850fa6553169e543ae23bed0f12af51d19002a6d254f0eb231b4ea72fac 4f2fd25dfbc1cd09f8a96abeb7f2b96acf1fb8c4dc6cb5a256a77759d4029bb2 77fbc2e41fff112f33b3e9ae0be5c7f6086512bb7f2e3cd5f50b3bb8a52667d5 72c9204f6529b8dd5b13c81b9d2b22869f8e4ddf9e5ad4deec83fcd55a3c185c 86ae221f89c839a025b1e773a332e3a5b8a2356bfd305667b9b47947408efe44 d73eb929f6901661c4e66580e2b162cc55d4e18bd520a9e3135724fb41d22790 0822d55cab8e848d9b3bc63f8d1adfbe7c6065eccd08ac97ea31f2f90c82b0ab 52f25f44677fce815c5e9ae860f9ced6e613cda3568dac755e117d2fd86c4e50 59072ee20d2269b571d0741139c629a9643b2827edd2c96956686de48c344a4b c33f71ed53442c4a0e2eb812c1c45e613dd08c2b601b45519745e1d96095ed93 b6cc32a505908d9fa896bdf2914c4e1693feeb2c519cd306f7ca76996e223888 f7e7d62c9a999ba3afbcc96f07922cc267017d077fc8f29e092e528e076cf7d1 9e202ee7ce7ec20e2f90c5bc24500d250134849903a910659e595532b07d5d73 dedcdc9fc43bbb3119d87c9cdd49f11baa40973c234f58ceae1b5111946863b4 2e51d7a4d4ca130bad2f0df988fe15f11cea9f054a33ef9b22e663f79e4d2da3 b56e141962fb203d51328c3669b1227ca4a65240d7c1d0a59977f70d150019bc 6eea2d753663c492d4c55693f5a999e82b54e210e2d1f46df2dbc0ca129b6904 975ee027b1eadaff57a5055e6d98b9054c494d670aba54106bf13f851a422e51 3f112d1a063c25219132175eb217399fff0befae0fc0fe202e2f1041c2d65685 7f0ac7a83fcbc60701a4c93b583e77831f0276beedc3f2d1f6ab096aff4f4826 097593aac3cd85b02fd7047e4e5265fb5bb1ad109e7411c30e9b46392b93a58c b884ba4a21c9d472752ccceb1ce11c0723ec44a205676c78750e4aaecb9c11ab a234bb8aa5c2cdd04faed745d91660a4c8d702660b557f06adf399776b39e478 e16b707685c867a7d3a84aad186572b537eb02c57ac0ea1b50dd56608b3e308f 050ef79d2eeee6a0f78ea934517b610f00ff1a99d551fe3f50a8f9ec0c66aa04dc95073818932188aabeb935f404fc13e2338b30b118b5300664a7ec0ba371071efce827a4be853b0e3389fafe3a747175c4f72150a7c208ee2fec89f5950308c2f87ba1a401aae72119f8e8175c78ebdcb32d287a9b45ef9023e4f45ec79c0bc5a255b3849ffc3316c17ba98dbc0ab0dd2b3258b24303d84797c777a25d610fed8cf867dd512abeb52f6a4efba22a80ce329cf91583eb3e60868e376c065001f1cb06af4eac4da008f01448252d0b86f668f79966f55a93955bbe5b97a0240d26c386158636caeb879bca152ffc0ab2798592202aaf188ec41bcb86ea869909345840c0c5ca97e3c8ef17140bd1c50364b889ecd3487642a72b015488d03a0bc2268c4330168bae3760d92823e809a4d18c046f86d6c58debcd7e74bbe5810e84bd7dd5e864d3089ded567012ab2d6dad1316ab5f0e849fbceb9108922f0f0b667e40249a99c4a79150794ac7edb63b2ca68ee79839115eb7b064a482c8d107c9c90399205a5a35e462c71649e0695916d4a7a04ed888ae51b945d46a5a0e0cc49a835794a442110b80fa82b8e34ebd8b467545c3e43cb4302ceb4ada49ea0f42c5ba1f9ef018f3224303b19a5ead22e3d775365c97224354f92e2c1b3a2905f7a7af25563ea9e048586daa6c711a860f55bc58170652bf9cf6fc91ef547408309970af138babc08fca18f902c6f0ba390616573af1ee9c7838e53eaa904e0dddc2639bb98cc70831e60ad74c7cfb5d40b1887d24c168574b097cfbf4d9da065a8875d24af137de3a7c441fb8813de955411c32ec22e10eba29778804118b0e27810e5727c5318a59c59f156af39074604333b69cb3b4ea87a868b0ad22970e80c3ffb708cf4b052c1669580cff363863934413534475e775dca73aee32750443c07f9b83369358a7d870ce1fed0c9a20d34a176b43215391be59e91fb17e05dee65f9dba0b3d87181201d83bee325de39dbb9834ac0d99f5fbf1342a0d57aa7462496b6d9e5e7b8ecd3c83c3a113bf04790c2db1f03bc82dd3b4fcc4e0a3034bd3fb7dc75a5f92ba18ba4f0bc6ff672c6338be80e42acda223ec89c415b1059ff1e5edffc3bc45b6324b7a84b33f238f5df10bda6dd308046ff5c9f6a76f0c031d0599ebca6a8394c3a260b3e655533d7f7380ae0f352500f99528b7613d041b8868e0d7aee543bc400a996ab5a7ca1fb92a644aba9e1f72f1b14da423a20afda8cd3facb9c783064f723fe70c99e38f2c876bd05deb1f389a2275da5d610446751b2754246e8755cdd0f5c00c18ef1f6abbccf8c779b20e3a08b662d5610e572303057ce13a904d499301135413d5bc14d4b06db0a5a4788efa9e6263f80bd7660bc77e5fa53da0453e074ec764933cd5c3bad79652fda56cc69fe650d9066c6a962ce7c65fa8ff135d93c4e5ff0c5d0f4a84ce150f2ea888bf013c9616069e00820cec707ed1d85d4923af2d175773764569040f26f783c2386af9b0c30285b331b4ee3b762b11b8f3d14b4c1a6b4c5cebba7eb1e332306c933cc79725018935c951c9c2353c9dee5f699c3c47bef4e7f5ac242daa9eb29f36ed9e169508dbccdf9be1eb55bc45b345d48984e418615f4b77f0ab9b664f03394ec7892a0b7f7b95a40013300d89ec3df7ba2e37383935873f678a87b2f3bc44f6f7d26d00ee887183828d3bf7339093848254f9fc04e6cbeb6f5418e413ba0e4a1a688c0084fb5d7ff46cf4efcc777404b07bfc5b6ae1892ddc1ff6adf25bd0e9f8ff6f0ceb7f9aa24c4abb45129f1b783e34eaac73a66cc85fee2cdbffe791cfb1f9d007086add86ce6e1829acc0890bf882c8a87a12d09d8affd9047f7b67af03713500f7bcea578cee55f64d3f8a2645438b817eb5b936381d884b70b58dd39c9583038770ecd65e81133281e9a6e9126766c29d6dde358f41285e10a9c6440e06bd0269b449ed806310329a6308bb8615ef1be87697bdaf9cfb0ab1d12d0d415d840f6702b8a2606b8796e6a49d85d0606160971ce60aac7cacb94181aa9c117d170b651c134510b8d95584c01dc823bd2bb1034a15a981811a80c919de7cc3aa930493881d035bf678cab29b988d83c8bb46553112d392497f45c7c26a4428fa9507bb15211c25fc11d0ae1d16b95f3b89ec4b5c2af9f19ece6372e7f27fdf61b50e781310e76af7adf0836f903db950baee8d06ca9776ab47a7c8ecae65960ce201c80b4de04ba04b3a95175c54a119a79a0f12ea6a036d5173ec95da071901700cd289b5615802facb33c09302f6422cbe9402c82de406f3aedd70fdfb3b9af30907d437e09327bfeb6cd6cdbef1cd923911a43753c0685ddfb6e5c2dd3edbb2097fc0bcf7ae50cbc274b6e3e5b9d51b40c1b1dac772d3f3103d3229c8fc3c2e0afa78740ca0daab649a756757bbb9ee72c8e2bdc7e0bb85ea3c04e0b6d8458c0393e3dd5d9b4e02da8be757a8ab52574a2b489058c3c4f1a7895bdb2de7ac54091381a748babf9ccf64948cd6c506942bfd41127d79eb06df078aa201b9e7f10d590bd110c768756f3d35f99210f75e88191bd6dc3200638a4baca0886500e309f00cfd11a1baa63c0a3fe4dde48ca424ce3bc2d2b34381548ed149e212e9c102b50517e89957dcffc5b5163d315842ae2a88abe25077c3524023ac47c6c96b09dd00cce58ce98c7a78ab35efe934a77dcd37e2a42691f6bf52679770a65e1b02b5dc4ea87b176ef433fd54b740b9042c130ced0f3011e505c14a54c65fc231020a5822e34be4be22901f473081cf142b9a51c7430c337d30a8a05b448f2306020dc51b2d0db4d9bb462259e1d6335495d309d388bc9ac3db2cae8fc3f60444060bbeae82a383f782d65f1c08e0bb813efed0f21f3fa83161fe42a265f30cd30dad1d5a4ef20e396382a647ac373ed829536a4860974cad9af99aa854f8efe10d588fe3b3848c72fa6e5342f422b4a8a3041928fac4f404bf9ac8df50d2698508e20784a9c56bee8c5a7eee438a854ce65a9193df879b489315301a8593c486061823eadcae0616dd8f46e8e533b19b47d248ce7aa19e38fa786ee16a669a420b948fd32473b0dacd10131b14c13754f71f2a351fb0062a8bbb380930f76f73030bf21f72ed1da448c639a0ce6e6af86ee5cab739f0cb8ae522f1b811958a3c036335bfafdfc3e880ad75b163d1df87d31b74c0c42c28c201a45572cb8437390f64a8b62ec2bcf13ab24fc5abf8c704046b2b9b225254083471149dd661a65c00515e74bd3cd19fe957356957c361d916a4bbf1fdb8f85a3d1a9b30a56812ad06ecd1fa26c3e69f0afc177f3182384d51045f4df90d47b01b3fa67eac627e65041187055be6608ffc78f44c1b91c6285827fe21ad078d1d9c3bacdb6c1956980d054e99c64a50e6043ceb310416948b7596d05a4ea47fe74a00f03d76aa9cd1051d875f6530a4cd89477b00033e7b539771c3811011cbb2df63a07fdea4b3530dffb4ac248096305cdc9e4dbad2d8cf30714a1599a421381dbac46edf7a6c6501f70050c47f5fa0fc9cc340b2a6bad4eb1afb83d37e58f241c294d8374c26d7069735a3a3ce1e1b01f76786ba113341c133bd5281500a688353c7f6cf665c7200c381179123850aa186af71274ba650e876e1afe0f2a685aa9d37ed3e5d1ac10a3427ab60fefd777b4153df00a7be004d5a5a05e70a976806dfaffb914f52ac0f80547f2158bdede434202ad1b635ff965ac3bb3c6c8a85bf34c6530ba9588a03c22ba2764151187d8df68a449965a1b0993a044af0f6fa52387f2f2eb20f0b096a56397ae777648c4996a39ff67fdad90af2e5029db61c6dd6d602915cb874095c61fff18444553c55577fe24ad469ed531aec53dc38768c830f235ef7918d02c904baa3b0f06b2166157667c76d5576ca4f8ceb98a2e3078955d6ea0564ea0ea0f18e224ed24c0264ced137189810fac979fa01a7248ebef51f23388c468f03d31ff9ea046e871504034827b6e46c43d9c7650430fac802393701100521880d44b590260d39b06478e865608f81e3c83c20c1962e17f90d2e5e41dfbc56cf05c5bfb18ff520012de455a1c602b2eff02347644de331e6d3f5c86a5c34d2bb0b4da179d48204422bc0fed5e70213c8589ca5b6bed55f80bb9b68d07e9f77a20b9a80ffdf334823c5662fca31a6af928cd787cb02863910c167ac61b54a08860c49d99d48c75a5772c665d518dc4aa514e43bad360f977c33cc197f7c279b2b094bff8baac2b5265ae915b476287528485a53802a4a4757304741c312156e850c61ea56ccc5e0862416988718b9933bb653768bbd0879397d6975aa92cbe5b6093287afb9843ccf5174fa446ffefbe8ccf3726eacab397e7cf64a723ff6b5ad047f4426b23e72452027d70ef4b90d71d884235656c3c6bd318ccccbd61cb16e0e0bc71a3af417818f8066ceafca56634840da6c35be53ba5cea4517183f896807d7e433f47ea59c33dbe437f144c566ea6d6ea64f4cbf8ad2eb1a6b672f40ce0a257384762f6de9406a94bed74abaf194db3ef9a84cb80dacbeed4289294cdb0707d1804c50289bc7767610ddef2d31afc02009bc1768a8aaf49bdf4fe8344e0a11de1eaf186e7c962f28218120176d7837fa10567f15379f371903ab5d16a906862678c5caa04e508a1eb92e902a984c3aa9742c6346fe24f732129b9be5940990b6cfd48f1c38b7dc8d532f456d53d3b2425ca35367891da3d636e7e71479093e707a98c55c0194a60eba178b950d479af6302411094c9bfade809c9d64e50a9a16197eeb32c85f153a31facc6bd2a355c9cdcf412331c3544a4672de9420079062823613f29ceca31107e078123168bb11d6822d21346d3bab7bb9ff7dd607718c2b97bc722699226e2d5fbbdaf73c29dfb68c840dd068a130803c5168900f935ddfbd0638c9c6f9672ce17f5e97cc0b444ccf9215c0c96278d11c162911027b3a36ba4b46968e9ebff511b9e255074056c66140f7c59e5d4228f53a447500a80b6785bebee438a68761feab79f15d93179c6521bd2564dffe1ef7c600c707e514bb9739452fdb0afca825b44a9eb9ed80b07a5ccd2ce889278c2c18aa480b false -check_ring_signature ec1c13f39c1bfdfc57dbedbee0db9e61c7e9d5b077696eeaef90850d08608ba4 79443c10cb66cd74c26fa0a760bcc9d1861bf67abe5099bc93de5d4ace1678fe 67 1f658bdfa721f80be87c84a9790b66b3beb3b846c5c34854bfaa0bd63cbd2f56 a329bab7acb23a00bcdf32fb3ac4b76b88993fa5df5138fe1fe25749a0d7b068 f990568d9565a66a06c1b22946f86955b88356f84286e4dc0e4d48008e141bd4 b8bf39b5a6cdeebad8a21d7382676db81ebe324abf4b8a9276c5071a0a1e31f4 0ef7d0c9e51e2b102c12129920fee25995e1d741657df92453740b1f5295fe73 f774824762971be797c87b8ad26eb62a12a81d2dc40f1e02447dcb496390cf11 4a0fe84e82576d339c1c0a26870ed2c3714079d45f2fa53e3ef806254fe8754c f2ed1b2a3ebc42a4568ab08c42da4d5e06c7143dddbdc3b37871d7221e620ea1 e534265ee7e4b6fc9625d8122784d5e2d0aba33611cb664eb81032a2983401d0 e67a2b8bd4eb3496eea1b6eb7061e71105e52152900e6d4ca72c7a4eec373d6b 146203d9d7b6b1a3cabdaa0ec4be2d40c3302201830351e0a84604edf1b48c6d 2767e9ff96dedbed6c6acba3492a6678364d4af6da5034a0f5f56f69553c26e3 c92566f5de8eb9a6b91c03d029caed381621fa3ec9c21103a151428ef75ea140 357bccb8f4da1174b5349b589be3f2c25169bc2ed2535abec6ffa32680e5b150 4607b6be2ee8bff1719e317adfe838d828e5a77bcd17b67ade5cdc70d0c1d7ab cb64fcf1cddc1966a711c7cd23e3e5b714c9a5a74e7a7bcc83445be016ef4575 90112d711e1bab98ca16b7f8d80070f5c46032ea2dba519d94024541cbf4f623 d491d94cb6dc4ce0188ca7acfe44231c93046883725348a27327e98b8dce6552 305e43fcc17bc4cc8319395355e7e4790d5cf89fe08ba3aeeed7833149225b01 0922eaa052b9f945b192aa5f6558a9d23bed8c3760d5cd8c59526292fe6dce15 c7e831f0c31b69a8a3fc5077f2701be7271f22054d5553263fb0a515abc5a1ad 2d995896e9b65a35d85d7b82baca0ac462c06cfaa5f511869edc32a939d41f49 27c21865d8470960c6b65f400fd29d7b81f679debe63613de07bf50fa1560078 05d8922c5f508777c403a096c3bc4e4504be14d9f8dff0b56d22f14d52dcd1cf 608c7c67000854ce16933908426bd0f1c207205d911d602c10f981ade7c0d083 60cdef78e8bbeb3c7410d7af3aea68d244b81be83e58683f5fb402b0f80671a3 9aa4f41f73832bbc63edc53e66f63747fcdc9dec2951d68a493fba2b1171a189 6a5a19c57478d2ecbc8050a516d1f34ebdf33427b89ad867cba83e516e7fc015 9b8ecaf1c9489bec3e48c3bf866bb768be21a50c9f3a8b1d96bfa85c60cb2a6b 549c13e435998faa2606151b9d5aba201683d5ca02179a4e665916c7c4fd7f17 38a7745171215295d34f7d15641f822a78471d75e519258378de1c7d27b45c01 b3d28d06fd9ac388c20abbb32912a5e8971e44029e185d7623858403aa5263a8 3bdd0cf53c7647c248b2a5ee978de875548c16471498418f33f86e5cb716e67c 7a86eaafeb5066f131514c646e5f8d58fad87a699b6038c3fbd01b20709519c3 a7aabdeefa18a62afdae2eb6bdad81cdeb52c4914d0ad6e468ea33aff783f423 9b570a6d8c4d3f48dd1033375aca2dd92166db4ae35c6c2c8576be249213169e 00634c4b9c927c2d663e3b2b40c2b8b7ef47ee6702bf2f9ef9d21e8a84d02d2e 111cfea95f9456612943445483cc87e9938be779de8a460b4188f6dd18793bcd 075aa192f79a0283c7d9fba88418617ef742828fcf875a5229fe648871468d61 33164ff7a22aa4df2c06561577f89a31a4aef12b27a044f261887e5e97f5c073 8ff2d09ea35fd1f80f5ba8875b344a1991bfaa372ce31203dea4a82dd348098d 261f09d370df0c107089ca88d76ab13cbc1762d85013b8599a3e669b4758d95a f57dc4867238e9bd9200cded08fa4d3346ef9d7884584b556c274a2a95572fef 05bbc6d500913edb491cfb5035c4393bc0f1eac11d278322aec3117785d6ced7 8772290b2ceeefa72e9f39664c87ceac82139ddd7e59cd444af9ccf2e4bbd584 e2dea1dbc2fd0a16d89f393c966ff652444bae2020f2b4cf853914078b730245 354343cb46363f8d8ccc6d274dab10ec143dd54f477a0da2fe23756601415e60 5e8d91fcba76e10f9e6ac134b47567d70b71d9b07bfc6644031bb11d5c0a7fc7 ed301799e433a2db0800344dfabb38813c38c3f567c7195cd6e9ef043925eebd e073d8ada144a5e0eda4e2a34f120c99435f264990cb9203dca673c546fd520b 5fb554b8166d6476e9e92ea7e9fc8a206b638d2ca1115faed8073a7eef42216d f0800cba485f7e49036cbe89a2fa44d0734152830f502448d1ce49517c8e2ae0 3c5840de7bec49a04702d6efaa1d5be4737f38bc83cf3f35838f111d00112df9 1fd08e1cb9560f5ee5ef217cc49409144c0a80871f57ad09341991f55f05319e 812dd0a55de1baef5f339f6b1ba482674f36b7650d2aacda67920e2f8035d723 6b243033cd0537abff615c9049f2f7644fe6436d69631aee767560dd8547e969 9b16536740c86c667ff14a5c396bd37788f0dc84b676b93466f47b43650ce193 bc9fb8eac96763ba7794f43387939b3684c4f8eea209068a27f8eab649440d1e 54a3fbc8c40a0bb0dcfdee045c36fb2e271348ad88a7119bae4192b28a54f7de 1fc23f3baba7352abcf8d6f3e7ce0c7b7124f10d7edee72ac6cacdb3c96f117d ca59622c35c23bbc0f7c5be592566e6554e33c39a0d1ce80ac21ac3aedc5a34c e47a49d7225a22ebc0b28c55d28b39a624c8e3b44bbed44655e96866430c4567 35b841aa68a7b2ae12478bf5e6cbfe9183af1afedcac914d8be8889b249d75a9 d72d55845ce363be8727399afe7faa4377501f156be93266685ac1e9cfbdc978 255784aa53b9b32570e930d3748c7f2235596689435835ceb1938eb695dae023 0cf7978424f6f8fbc85076b230a0573d8d6474dbfc85c76f4c8e87b3ec8efe59 4a1809b69b2505da74e40626f6f59d00d03c13549ac79d2bac458b56ab39b68a df4ee1416d7a952d7dce492686336d81f3ad1c02bdf286e05a6e17c5b1a96a0642e46163eb09f4ea006b25480816ad33cbc72f133198fbfbefe2ddbb8353fc0b235449b3ae2560fe8b91f2bccc64712390be62cfb19a453a26c9eb93ab8a2d026fe7186f57e2e6b15e9ba2ca19cef8beea5a44837a2359c9fa706f89faace008198a69937f5e382ca0f37db06814433ecd7e74673ace6f448a181a25e320a00b47928cbd7f98e888cff3d8eafd897e5e96c6cd903c517c97bb32a393062767019b57cfc2ec72f28f5c70b65b486e3ed48a52a857e9f50e24d1199707883660028ecd7427e772c82d13174b91c0d7a7cfb428351634d72b96a394c898cc68c90bd1dd381a33854b62687ee7522b224cc65879958ca82fa704502adcba32139d07f8b0d1d0aad2e553f3618e45d80d8afc9dd88b9a678fb3bb2333c8c50df6600fd3f9e5be40d921f4e378cef465282d82aafaf9c67cf74fe7754670854d325709a639ae7a54ab84266bf3c3f282ed8fffe8a0cd0dc1d805d21029f838bc02a703b8d81167e1ec5e524ebd8f4797a14c8cedc39759c3c0f7fa8aeb3f68e979e0048ad5efb74215042cd69461f6a2c027f8629e4b64a501b3ff65a014118a4bc308dcacf974f48a97d3e189e167e7c93ec9066196c4b0f34cbfeec761c9febaa10cc05cea57d074d838416bf3dfe18b8919b7b48d9b696721273a25abf001cda10daf5f77ffc4dcc22aec6075ecd74dbad2865f211ffef168b4d34ffbb75e12b00ea608f4245a60d71669698b62a3754e6cff475880162bfc9193c41906b3dc3e074fbaeadbedd40ee9d1ebb42b841c403f7d62a361b766c8d55eac0a7ea0812f098666dc14d88b5de570393f2eece16ee069369dc4ec6bc09a8d95491ec0b0cf09be7d58a81d53e0453b6689f81dd7135aae6f19bc51c148595e1d2dfd097b3e05d333e2f3de2d9837ba3aa8d4dec2ba168a8e3f3c8af54527fc7cf09ad041f00451f6e8a2482f79c983380fab64471564532b61e773f5d77f5e932e001a12ef0a6fc825ee3800be747341e6170256704c3af816a043998455d314c5e0c86c6507ffd62bcb5878e288e73722e22613e2e79f580c9230c6f7ce815247b56d732c04e98fe83585958b6eed8a5fb36a5b8fcbe6319de27ddd3180bdaf4d4be6094d0678915fb329d7a8a244a156435e068a53bb779c6198fe8aa5933c79e93c2520076f8dcc092e6ec4bbfbaaa37a13a055e38143e529190b46512abcd745a3723907cdc01a15ae7f2dcb7acbcb71600ca95631057ba70569b7dd8df1755da127b50dcc76a6335d7c6a9c252a7883adb7d9ee47e63295780e92440e916e95d41c6c078e76aca0f372fbb64ddfabe15e8426ba4dbc2b160d05ca7d256522ee58b896050fc0d1df6d4431a9b9471825ff39b5f6390c884f29b0f70b6bb254919b23a00edb567945b7fd08dbf093f91405822a5a29bdf898289693b110398b959360af0c242c3e8f74319c10bb9de767d5527bf557fb684a4e60efe83f9d84300a30870fd4856d16aad1555f2bae93c043d0d31b8aa374c70168bb29df6fffaf9a683c047a17958a95a47e42488f0a305eac2ae1c4a465e04aa386818c0cc187ca95d506c7b67defd6cc4314826cad03668e9e4182ccfec99f9150de94d4d3f2d84a68034a1ddfa2cf7cf289659b7b868b4de38d521d318b4aac0e0b1c9c4f4efb65390c9696dd35007a9053803dad9bf3476c3b044fd30b4e52ac4705c84129c40b580ef3641945bdbc9d6b68686b18cf0cba9d629ca1d555f9905a0f575d5ed7371a0ec07337f4fc63bb22121cdc72ca1e69cc10f7f7f55960aace0100b5d0f53f9906fa917dfcd1715040c3a59495bf893c7b3deb9619212773571781e9f205985c0804ef69be5d88d04095bd80192c620cd5f35537e9941dcbca8a7ac5811720b3037834a35008857e2c86615bf29423d6ea3261521968ed535425454958b11759099981c84b95498a3b47bb8af77eaf80c9e70b9c83253bc64fc7fd54737f391603c52784653628ecc2b074b47ed317610f99756a4ef928e9a49af30544e78f0207ca4bae961f9536ef4aa18a275b46f7e09f419f906e68f094db78b3601dec9d0f3806a34f35d364f0f430e31a68ed73d4110c4afab16a10de0f249cdeba6a2d03d039351826734031a4652c011972a0f5a04075985d44bb0b565869d1058146094b19d87d6b69fc55acf0fffe6a1ccf2a97c34b88ed5b501f27d698bd3c934401ca9bc8949dd47c006edc4c89f2bfeeb64fdc8e58c5849b34c8a642cef5a68a0edffb69c8b32259bf1fb99c441f23353060bdceacf8e087baf57fa4787eed3a0deacf5ffda78a41a77b6489fd2b7d6989bedce57210d7f2d2782e0ddefb348b05e9d726f26797cf0a6910ecee51d0bc28d487f08dd625063054a53f7b1a87820ce1f3d08fc80e84fc1090b64daa6f96cd42c0e8725703063d7b01b09f3fe89c09bd08c1d871499c51c31d4aa2f6cd84ea0e34b46cad6da62e5bb23a290a6d940d16158105fa87ec845799d6627ebd1fb79b708782ab9e4a0ef4eccb446ac2d805293e96b999ebaa0e20b7d2db572de83edc96030d0b30b80edcf5fe9c1cd7d60180251dc8617dc90c46912f1c3d72fb4f17e5e87de68878816c95568e7711200d72bd5593847948da6ed623e263e7569c45e967643e5d62d4b588eaf52c414d0f40e27cffec3bd532cf939042df9ebd222d2dddf7fc2dfd0743a31c046e397a04947cd9a9071ea7dbff6637d5e9fff0aac8d594fd96cd5d49e70f7e063660f60554920e498302964e4da414b3cbd965f057cf3a887111369e669b19693683ee0092d7ebf60a6983f496ceae3d59e7dff60e9046eb89000955924992d2c6d0440dcaeeddbc1710063d47b41b2ac1115ebb6cf03081ab6cb00204b915705294dd0049eb3363a6a9c53f4c40c373f1fba1f179470997b430616fa1c90665c7e31e0d78d8a5916143ca769493ec091d23e4fa959001587c4ff9006bbb4db9da01830d1d8ac6ce8da9f5199a9278fb4c34ba4165be7822ee793d7d75c05e9b6077180ccca4d1722f6e01e9f678a7bcf1770cb89321d7ddeea5cd6c2debaa979e23da0a5332c2f155e07c2acbc70450fdd8a1d10bae2042ac24fefc1a0c47f42bce930ff8cb45d4b3b441c90688c6de98e587bc08cb10559efd7b649d53415c358bd30ebf408687fb9a0d1b5b494678cb7c367326cb9c28a5b5f1dc8b5ca32d795222065504f3a0913b5d7bc85419f696fc80ece6ed01e5f21a126032b6f46148e813014996932909c0b63bab8868f5cfd50ffd97abb2d709ba833c28c9ff35c88794055b11f24cf4d78d7a0f05578f0623cbe841f173ae9a30a8218e0ca94b4523990258808e3ed8cad95df141ac165ad5991c76b50b4cf99ca145665a3c9dd903a909ccfaa73ced3843397c5d91b6a318718d02bea7ff3a27b9979930eed6706da2063c164e751e2b2126ac3bfdea6c62bba950dbf6123cf5d427051abc64054c68020a2c3029255d2df15ddaf8415f0ebe7b10504d791a5424fcf31684c5b5d26e09627206b9a28381d91372229dd64dcffa93fc5f9eb4ccf1624bc7260fe9772101f37533893e216f0847362fc0f29cbd2e4e060fb5b41448e88fc4f31d6913a9092dea2e7e8ff5efc0a1965fafd0047dfc058c4100e0b614a681d7c402311d7805a16183ce98a3099f7e433942ab34cd48e9eb9c9dfdd3a0a806d444e0a2ce23082ff4bebbfb6779944e8ccbdfb5bf1708eb7cabdf4bef6d92cf46072514b4d005b307fb33a8ad818aa590832d32f4ddba45e1a22078741ce867e1f4615d2f6e051f5f45133b108737d39a2a81250b39a3af7d3dac379c345f7a1972e8a7ecc3d35a9d0714eb2f48f911d2504caf5554d1ebe66d8f5fe42d395ed09eff37485e032d3dd955442a0610ea48df1a098ce50cd6ccd7ec8b0ee6b04ec51649eee64001d56bc8386787cd3c9b1bcacb64dbb20745251dbe21b6bd709c175fdfb8d9e200f87fc076efad51859d921a2b09efa82ecce6e5443bb31a971f47a6234176560d159423ca593dde2aca2b69aff4e5607ef85d269a0735f327684de3c120a75a041acf9f49e5e3a058622593baf54b29fc67605fb9eb3d88b929955f2afe07400fb61e002384b12c1c27722dee00bac896c5f278abebea92c01662884b13725d00a499505969bc2a513cbca668b055e8eafa76e787d90348096d284357685dd302037decf7b1a9bd36e62ea50d5e661fef78042385fe214a6abc396d4eb55e0407c0f137f62fd83b1d2f0d4e36e2b66bb9e78ef16b6b59fb453a9d79afc4b2b501dcf2f6057d0148063d049d344bf6fa9f8c2f5c006b1f7aa973a6f5ca74ae7208ae340e4a424e345625a9f071df364e2488f80305c5c2d25a9b0dd08408db9b0f34efd0d18149ac1a7d4cd9fdd6f6dec4e64c76d5a9ba9036f24b7fcd88727e09b0e2075eea4e4e82d70259e036b45e8120d26e9e2139d57f9321eeb93488b30784ea0f92c151f43cf5da2d78cbe69eff66ce0049d3f994073acb0c26c9b48505abf1b299ef53668a48ee01380048dc369873eaa07ff3d9f76bca08241805120e903039f0b50ccb635b66164f3b9963f77441b2c9303d4aae7571b0c68fd59c03c11ea6840ae4fe78a345ab8627fe52b939a88d8cf64044be7ea8636979b4280982a80f4b3c5cb715a2f2481ae5a458eedb6077259e6df2d6648f05c24f3b2a0826d51c3a05ffbef80ae2ff3e2f28db1a8e224388c4fbfdb80ba50f69f967a1032a07f29c9d74a95e86fd3c47f0694652fecb13d820cf154463aeb6985849d4090c442d9ef0fbb3a5ccc61f0ab48fff641139b8d63077b14bde8a48ac374a9a013b326257da940da705bab0e3ad6eee68c60e05d7f4addb53f13553c47e0414098232a1afd0b28c4c24c5b3aa3750548812b50c9e0fd67ce46bc8ef5c50fccf0c2547656be833202e378ebc76546a366fc90fbba06a6c2c209477b22470881504820b9a68e0eb478b7696eb6e1240be2c067b4e7bc3016c12785367bc1740e10bc6d3e6e9a11b51ddf42d69bad6655d5e910c8a67f0c162916cfec6199520e6043221009a42e0798a31b176c0b104056d1e7da85061c3271995a8712cb6b1260ec1496fc643cfadbbaf78bcf8ec0eeb01735b34cf392a9d5c2e58c57f3fb1d5057209f09d264e19c6420364ec02f4ca3f94a7c1063cff332d36b813a45df9ee3ec0dd3f93ffd51bd72e197d7f2469d119615e24547ada34a24135f2589db692086c9fd0e86b2f51bc3616c74b99434789c3fe671da6c53ca88f7f51017782540711d76c8ab62a16270b033523c278bb2b93897e17b5af26e17d961b0d8b34a908a44715a18bb1e3258ccc7e5ba6ac058258cf265faf13a6b9e3e61db8f725d1066aeaba020444165ebe9718eff6695d0f4f9aa32bcb4c96c9326d3104b5b65f081fbe8f3f0cc0e98f6a0980f443b7a8b1bd848b6f8e9cc717318ff26dadb5ab0ea7ef6b44094e8c58e82cf70f5ee76dd9f1c87a2996739794054b21b3c702ed0529941fc62984e83e2b42014403deeca94af3fdf1015fe2424f2f06d6e3bd1a05697be6c92495a35f9337cd48980fca2dd877851480fa741cf8fbda4341d1fc03d6e28963d16556e570dbb738ae9b7e774888c8906acc03b63ad6a7a2ffaa6e09ed1eb7e4e7b72ea0ef935ec166829a42a8375357f1f2107f84569642e2dc04086a402c43847f642c0770422a62b53f4b5ebe1c8d6c11897e6ca34de9028aac0bd6b92e38db4c033a3b188eee4a5be85ff4e6c493f851e21383ffd0891e1b9101bd78a4979d0f2006dd7916a4d8a47357ee750a7bbe8c50f4585377279019e60a58389e099bdc90e25c104ea7c9beb2ea52e334daa49c1234173b99810bd7e40372823889896c276bd059291df2aee99d0aee2f11a1234205027a8562521630014e6bb98037a68854171302277d8a46d6f868d9c89790a0e0d80b248bcd67e10f3d278d1153ba661c13b68b033892ab0372a58fad7979f0b8928f77cb1a9c5804 false -check_ring_signature 4de68dc68b62e0259230c7c1df3dfa84c41efa785e78f7cd738e8ce403ea9380 0abe3969755cbf7cc59d2a464730b0bae0ffa004ba01920ba7ebd27268f1a684 203 b75b080bc17f2e6acc6944a01dd246b8e7f7ee582828f55f6232a63c205a176c 44c1454134f636b18e27ecdc2154d56b824c3e7de3f8fe865fe3b4e6cd0a1c11 1621a86dde65bae041a8d385c3cdb58d94207f13d692103b59fa7d9a6f061745 53fe90c86706e66180df10cb97baea0215c1e46cc1cb3da6f797931f65214e1d ab0b1cccf15fa70a72f5db67614aca54abb4ae99fb197ed4f001fb09f5246e07 57c5a202a64a32276e6b921d91e588c86ab6ad49b3d4f90e236689d5f50f078e 87a8f0dc48892375615bf2858ddfc472dc66c87588a21b12f19025f484e9cf58 c4fadcefa66433829604733d8d69ec7cdda462dd7f36cd7ad4cde6da00008234 73ffd4e5266df43c12e6bbe84e9aec8d160dab8e5300338ead345fe7ee8bb0ec 1fac5e53ae66a36435e9a4a18a570c584359373d66d0685c1c32c1385db2e2ad 77d6e1d6f21059cec2b1e1ee9596b399a33331ab790e4d12a7ba3fa44d5459f6 8b27a00d519d65ddc78ce8669e3589c3080b17ed6d9e9899655e77bc66586275 3e5a87aad3ccfc65d1c43e9e3d7d76cedabd7e0d9cc58c894ed7b0ed6daa844f 2b56b02f38506856aab01566e17ad19df98a41e0e8fb22a8baabb284514f739f 5c66a56d46811a85f401a700d7eceb8312053ef4106ba9faba4d786e1b8f3cfe 9bb4da14239664ec71c98d8729209de6533cce204fa4617779aec13c321d8c24 767c090674b9c51f90b1924e27e09ff06c4d6c7aafeb2c58fedd6e28d8bb0c15 445b5725d839394dd0b97a62d0c60011313009945caeb134d3a943629dc888c6 0c0daa300ad4145c634e5f3f16d2caf223f7115af41f81df50a037eff6b3c2e5 070eb2c063327874ea6a9cfc840c03b4a240c321746d52e41b4be222c89b624c 2a3879b0fd9e83e37747a262f39ae015a0ba554faca349c1538ef6bb421bbd41 2b7352d7db7fbc7627c9e8349453c5f798156b003606f7109a7708db5ee28e71 7e13842914abbad80f96d842659f7a3babe9bc1f8b7c16a0cc66c05047ece04e 060a9e6de8c39a4a0e1b4739b70869ab49ffcc8531f3b3178973b500fec7b61d 44420559c8357595a8fe47b2feabbcb4a452571e2fdbd747427cff52dd68b13f 1f01289bfdd9e8260a0f4a345e52d1e8d3ff188fa9fdb1a83abc2d2058c3357f 4d6e7d585a56e3f1d8552fb670fd5585fd0900108facffdcfcda0db09666899f 721c0a6b5c056a5e891f6278b6de10f8acb4e373044b4d08f77e66eacdec5bc8 918c50c21e41c2c1c21859fac355b8a70181620e29c671cd93df8bd65b83c19f 6a81a8916eecd15b821e7ae610ef4f4ebe2d985f34b209440e49cbe2966c65ae 39f80f3de7ce7d4c3f07030a07b7efb1736bb71f9868f7ff7f047a97c8bdc7e9 1e41e6e8870d9958389a0d0205483873b845a231ef8475a7829b8a5e1564cddd 71e083d22c216223be418c552aafeda23d9e025b24e43bc0cb8269bd03c5b19b 9f9e842cade277e97f76e3d0cbe939048acc72b9a50463b322032fac4a84284d 63dec2151627f5cbb477b9811ebd038499b35c49402cff6d6aa0b6314dbddc87 a2bb1ec91c54baa5467c4047eabb186b3b01c1dc62ada14d9c81fa229873343c 7a9540aa0fc4c1a8b9763dcb46407906e1c5722ebbdc595eca8e1df8d417f2e5 1ddd2fe8a6339b637c0e081a5e9a75193a95a77b2086fefb15cbc7bdc1b8b132 9a7875d0ccbb8af5ccbf847cdc490a4c3aad70c851d024c8d2ae1e4fe42013e8 56411261e398015d250228deae0a6393bd193c9914aaf813cb1cb79b59f3bc12 bfe20525792007f4615f7b40a52def51045b5eab41d0774cf3e289c8af591b08 cd47cca3cce7b3be1ac8c1c253c3d32fb85fd584f7e1a2c136ebbe0747a5f5b5 bba6d503a104a171ea3a068a33f6888a20e8946050f23580903342a7acf64f5a c8b05bd887eec4a084f86580a2efc5dd23d39b27a70aa68c0a25ac455d434784 3bbb0c45c7afbcf82cf8967c102547f32d80a0fbc087902d0388a84216ac29e4 05e9a844168e6bf3efa137aff223e53d324d74ca09b533be28c9aa381f4f27af c0a28d4d9c98e5ad94a45a15552f4fcfe284b5b7096030298a898d47ce13f2f3 554c5a32a6ecce580bf3f910ed3470215a2c9fd351ce00a2b07986a4580a82ba 58ead4b3b18f1d9eada34bee1e714b776f11b0aefdf2f057b8456857713114ef a99229e7f85bf646f60455082d104a1d49bb287f8c951b86f4413bd4fdea3fdf 2fe251737643028ac9ec5fc713178b97ff295fc2b32b03a62c831596f86db512 98f27d5c7a473a5e8fcf7477212e7b32b15044ba57ad85d92752267b709a21e7 dd4f83cabae4386e8083a3fef74401e6e9c3e162cf26d764d75a48af773bb254 1a42cc8e8802613f9d7fc4b867aa8c7047c7142799f1ef82a4b5adb5be556fb0 4677ce02176416e983fd2386e56b61a90838dc5eeeef8bde15fe671f9f31c5bb 8905c1f8c5a3cc7a7ffee2d088a2d6f1a3a8a60b289f77125d1ba48d04d12949 23a36d4fb8b5d576cb5095c463b91bc485c9878a601adf8e0cf11dcb39ce9866 1ce281e3a987ece4c35cc1e486caccaeab6e2b6b50e98a147df813932821ca35 eecf51b47102810b8726764ca20ed9d1b02a530a7d2922940920ed81facbee2f 955628d12b9ce77a2ae14d2bbb2e97eb9cf159a02d36d1e82462cd8ab84a1e14 0624aa06b88e88f61af58b079ab04ccf9816149a88897e16fb974b985559b045 b3445d6263ae0b754e0c35a45b6ffc9aca81913c7ee43d31e19f5e20b90c85cd 0632590c256f9fc7a08eb49342f0fa217714d571835db34089cedf158df532f7 d1d6d8165ba2954d4ee1468a9a9937d1497c2cfd7b4862bcaa88c46096fff107 f8ca0bfcf60c790933b9f2970b71bdce46437d14615f5a631b1a8ab853390cf1 2613cb752a9cbdc5f9e5c1110b4f8cedce980e4d4d3aa0aac0eac831aef08ec3 fae9c5e8a72a8e676fc5661bef142376f97e35b92befc3ffc45b891bb6c3d73c 874f2b49114541a4fe35a7b26eab20fb732d37a8afecfad3bc7d334377d75928 1b8ab464725831ba71d202884b91b042abb13765f18c4f0253171cd8bf357020 1ab01b94d0dd1dabf8cce62d071ed7290fff9b6f1b2548c4c806e36ea6e18912 afc45472924bb58f187d4f6d5f5f650f74a3e830abc7d78f944df98bcc9bd070 cb1deefb24a16414188200a713665acc183e2114fa704931a3dfe869a6f9a6f0 cea75c542b5e2cdd411d5fed3695fa1de1890d27662fb013ab1e7eb51b0473d0 4af6e342e58a1b6f33e9b054b94d480444369712b1f1b862dfb8d1974071b2c0 1d774f95647b0f696adccc8b5eef1a316739953d4fe88784753f4920fadba82b f82d557d5c15c1b776290354ac30ff2d5e52b755061eca8e6feb4f3dc6e176da c495a01e77a7a105e2202e583e5e2d89d2cea6f6aa3c297f3aee2a05b27694c7 3e3fa1ac436f30046601f1d490999d268b6fb89c1ba34c5ea06df683c89046c7 1b2ce06c5d2945cb08d4e97d8938914dc911735df4d87824f4b380b341c36c72 5cff1bb9c732e112ce0d317d52ed51291b6dbb80b267bb5572b2f7d6e2b91866 3c4d48acbb6d031a59458ae1b37de659b51e772be4e370009d5eee14fc31adda 40e51482b2f4b09d9b5b2d0671cb45be177b3b417a9d3df317bc0b766a10215b 8231b8f9c31855bfa1de3e842049d5de637ed8757560fa347385d5767bc66dc1 ab30916a7080708937c11105b0c68e0265d47d087e5268ece8423303040ff998 43ccb9c14c010660051454de9409a8cfe71a354787f06f8ddd5ccfae9de43295 f83381681c1bc08de7a97ed1428420058ba35c93cd05ff60c40dff0a6576f3d4 25e698ce1371cfaf9041ff7d2a74388ecfea38a44151706f6247ff19c581f105 92f4e3f5b43e87080436202e848d9ab23fd5e113e3cd6bcacfffa7b0d4cb06ee 5a59189ecb983d707b6b998f3b5fa479562da230c4c6908361ec7e50badeab73 25afbd1ed477124ec2defd14433364253facde0ee282a4633b0faf604e48a17f 2b46b396135e4b3a386b90f24d4dd38d336273f101c4d4723e98baca7b1a6699 688e43964d1fab8605d122d2ac20251f5ca2d3771caff8b4d0202190cbdc8fa1 853aceb7754ca2e83db062d82eed64cb1027907fdf93f85a223f66aa08fec7e5 7126f17fde3afd0a833b4c4e44f9dd578b68443e88f4e3e73b941e934a73ccb5 9477998631dea01868827d5aff512b933d7b326d55301c6a9b81a2535ab7f52e 88529316c2c55edb062fab9476ad7b274a7e0647f40f701e2383e47115ab11f6 07f78f0f264ec9073a4149663fadb3a7d474cf747920343f849ca97eeaf538af 1c273ebf8db65cf5fc7635777b96bc9cce426848f8b82861d99e51a064666441 7acd19d50b3d7ffa082647aa8b110db044db709841ea99091a9c876660a2b36e 63f57bdce84a563c656b9b67aa0bd015ef83582c2c9cf4d60301c0d5f5d433f3 9cfce692c8a3d8a0c5f0d293a3114f8c65f0299c9b3934b86deaeb78b58533ec 271c10dd8571adbcc19b06af26fabd5729206f187bd3ff586b2c52fa4b13630a dbd2f96f9673dc0225d048e2651f9d74cc17630f73053736af13c52f954927d6 1604b8ee96ec3633f9bb92dd87d51d1f4696b1e726254fef0556425dc4521ffd ea1194d9748b5ff831dead31af98da05176092f210b1d329d73bde324f3b96c2 6dc0b832771f5c9f4fea806ddaf0efeb5f571240945354e0165f79bcd7712573 0ead41c86ac1b4c9a8e10d90542ef37f4478ed9bbba1b58eabf22e62b41cddba 9cfc8de60bea2675a2413e85974f1ec7f7f507f1ad6fb9454722948678f89ef5 e7de2edceb42f86210f6e94e6af716e897cd5ef214caa6eae4d9df784293e058 25346267cef9105d867eaa0300df874f4f736ad4ec1e4064cf836cebf0e33791 ec5ca8d31c87537cf99e82a7fcba7b71b9f341cc03b3dcd4adfa6bf7b5662463 416219b95cd1a52f3bd10b875ea7f5cf67e6bad5215f17dfc7fe81e1ab0a6724 5db4a45fd899b47d31597655080536c8688f2e4334384358c98a2b3ce4e99cff 7efae90f167967eb3827fb6c967fcae4454c3296ee1943bee07dc920de952a02 68e86ee00e4652cbc11c0ad08396bdf6bf4ed7b5c189772c3e71338bc2fab424 3a391fdd9e43920c67f683359bdc89b4e78f1192f3fb6f4a2385509f348aa320 b85faad7200f1ab40983722e1e3b502fa779ae1aeedc0b2bb601780fa6845a27 9c9f9364542522ca35cf90927bca653e277eb91f40bf33a29cf0a8d23f2f5c99 0d445438300aae18272eb6713d9af42ee81ba67b690e257866954aa89c392022 3498a3884892774faf0743d75adedf305942b0cdea05a1ee97c1e02ebb6a442f 93faebe4ba93f93d53dcfc7a1eacc9a63f682a9c3fef10f8a72038bbdcbc3613 0a321d25cd488426a94cab8744a9eb79c0e3fc376e2ec3633347f1c4b9491ef6 70acb5179a8e14aa856e6fc61514966e186e864b316bda163ba379e9c4075418 ddefaa5f3fbbc6119f8a36cb646238e7528022f247baaa430c18bae098118293 57392fc8400ad69d2386b76c5665fbf30998a36d54d7adfaf7e6eab4498fd71f 013d00c06f110ff8a6ea37fad33a8bcbb2f7427ccd586dd5431a66d275fd998c 59727316c91d7772588aca3e231b14b6918537647f6090d1479df9e8a37d78cb f0f5c2aba2644b6731c5d3640ffeb248269f7b08500b9822b3c8d5a569ff188b cd6fcd8e0a4fc46e00f49d1336ac3ccea78710f02b369554e2bc76de98ca8abb 27a184e515935acde09b3dc5730ee53f2f7ce3a6aa8024b493f2e07b1765e8e3 b536d22154b4b8e4668be3fab8ac073b3928619eabe8f6cc2deed2946918a37d 84cae97b1146c5879200913680048e6777c763079cd838d0bd95a95bfd980f63 b996cd1d8da842dddc7f14fd9bb2a27742905b721bbe72681eaaa98d298703fa 692c6135110fe145a397c358428ec744d6a8bef7b75e30fc4d4dc9c98e8e2a16 f595788cd565b86973f5061f5085f658d4a96dc8999dc6ff4d9eacb934b0ddf6 141ea9aff274c346670a970ec8ade65a5eccb019611ab5559ba3b0ab831a4013 0919ecdb192c20aae0c4f7f7a6d833751b26c29cb65a6f774713d497a59ae4d8 e060e6ec4354df48cba79fff87e7472a4a895d3323fc82ed332aa29f9b69bd37 60f71db1842eba0a5e6748a02683a2221fb7c42feec9a76ba24ab34095b22d76 a4ce09bf692cf74e863af7b06e634f917cd72bc2c24c41e931b15f2cfda49eb8 8eba1f0545344b2e27d8d01ea79489e212ffaff66115ab7bcc822361f98e7dc7 551482f58995e61fa72eab77b26a5ae81257f201c01ba55c8941e7aa6c70d1a7 087938db1ce88b8e59c7c546f457c46eff92c0addd5c64075c5edb789b9a33da 7722857dfa1cb7872634ec395b478ecb4b0376b6063203b82fc761292d2caab5 5a1b6463458a79ae90bb1b795b01e4f0043adf19306e658c297b72368eb8e359 b7a0e91985576bf7fb8a1ee0ee7e0657a9c71637202f022fd36b060966f14fb1 e53f1feaf04801f020f215a3605d337df20167a4bd0bc0398b6ddc2991346119 b29329d9d2bfeb1edb468c59266fd6d68c5b0aa1a19b530d5caf7399ae9095c2 b52622a17d71a62e67b7e30fe3d01a9582fa9547f9cf8710886da16cf70423f5 999c10a970e8d0311cb2b9dca2dbf77201f862a8531287888467472e4bd8ca38 8fed0df31d476eb84063fedb48af6455e3c3ca16a6c37b73432a6a88930b9fde 944142d24bc6c660146b51af73296a98930b320454f6106809270ea1ee79e3f8 41233603565217d2523439b66aa68aa352a035d6869cbf68bd069f5e932ffa3c 77746dd3152e0bcf9a993fad2ce0d47a32317bbaf05ccd486edf9f3bccaae362 e68a6b6c3969d8961b200344d2adb687843d8773db7c3ea78559871e4501a434 7caa9e990dfc46936aec3aa779e82131cd41cc91bf4385a55f55ffb09ce23fef cbc0532c27ceebf68c4e9a6c24b0631cc23fc3a36ced43082d979bbd1c497b72 fdb1b5baa7cf95b2c6a8d4c5a0cae0cf093ef22a1c4b61ad32ffc5a94c8d28e2 4524d5a4abfd6164e3689a3826590fda6c6515ed7f87a685b9e30fb6a70fa2fd a388849e91c8bdc522e3a7b2d23bb21ec484dcfb282bc889c393779ce4fa58ab 75f35ecf1733e81fa74aa5bb79683415304d8946a03efb0402ad2c3b436b603d 72576f0224cee12e95b0948f9ba9c765739eec90448029a3cea9a86a0a657de3 f943a601dc503f89b345438c297dc9623189f5445142fd0c449cbbccf04855f3 38c39d37b9ec2a48140f93549b965e3287941fa0f8450559f53534df80145271 7802ff13e03c58473c649f25a7a94a9a4a2e8ae3f2cafa7f635b7b26f3be25c9 f431dc549f052053e2e713c5457062543a9949c038dd0e6c0b7ffed4693642cc 1b2276b766bda49840967be80f7ddd2a2341e28431158b86518a082056ffd76b 097cda1a595b2e048b8a5191b63451ba79d670310a4a0269646e40339dba42d1 c31dea7907b7d1416c6efa70f378ea395fe3698684bd3d0754fddd5a164f74ec 5b92571917b9aba5d8381212bf8fa5d21ac93e37a32f2532f7d4ba883408174d 1d59f7149994f0fca7a752d737bcafe9d01fc3f591c6d3898ee82adacf9bf6f1 22edb6e7522941607d4476c97f0af6524efebbac90d724211def1c55c37574af ee00848f29b8491bb6780eb22ba32a7996e251dd66c7a776f5a491798b014b16 e99146974c729ed79801e2518b438238e03fb1937c40a801ebbb31dbd8a9892f bf7226ec930da40f41e8a3d8c784865bf7cd2d696dab79e140ff8ed18f1545ca a83b98b9cfe9434ec32ee248759891416d7376a06b9381d1e54aa2d1b6154c67 14025c19a8d54897d33da8574f7ece8ff89bfda5893052454bcc60168a00e24f 8cfef54fd04042c4dc8ebf16776b5982e299cb585c8f1bba37131c823749c414 88dd029def6e9d26934e4d56d6270d013078785ca4b33f73d100bb826339d43c 052a5ea8689e58640a6a20668a4e9581ab06247273d9e752a369aa97bf0607af 0a4ad5721b54289a337baacbd884d2eb7567f8e982bd7675c912142b065f8845 b489c42ad89b42375408ff9f783adfaeb38f4e60f5c55c56952970c25b507913 dedb8df6393a86711a5e366b3e2a52149d882a9114bc99542dd84539e786bc29 26287902c7c8132f0ebb70b9930243d05a5b6b216b1bf7385e7e3c624407bfb8 9968ae84842b9b432742cb68ba8118551564824de13b53d0cb44d30ad499529c d4048ad10bf8252896f2a5d34b2813d9aa2f133d371e86c8feab312393564c6e 067e259657ef513a55cbd8c1d19cd4d932c9e39ff04d20453e6fee5c6c3a7f70 2ed46f192283b8751950c90d94edf6da6610e419421f8bb1b523a89f7a0fac5d bdd287e8a065b74e9a8767c1f20e017a8341c1a549baf8586367ebda0b3be75a 705e1a597978e295343ce64e3435638d7b0d11c0f5726adc683255955d1f1f4f 0980d18e06cbc9478816148c54cf21150419ec604d695d90574064919ea30706 ea9de06e428dff51f31e8c87e27c8a02ce3a239b14c37d9acc565704605c84ca d84875d102e95c8a2f7deaaaa167925f537f3da7ead75de12deac06028e5772e 244f4512ac28c867d1915d7457563ac01c696b7807667b0c9fca84546f774bcf 1445414e0faa568fcc8e4251e085468843e27b35682e96e7423d9532cd4bc946 8400785d5fa3815f39bea05f4cf34f0f9bdc1574215c90a58462a1d437a4dd4f 9bc65afa168db715b2e0ad116cb0dea3b4946fde3e5d5ac77119fa33f0a5a29b 3baef1ce60504769ca6e32a85b565df8c50e998d9cd35e3e1eb71bd564c0c211 8be3b6d6b929748607d8ff0b90e745e10e87f1a8689865ab45019ad5ab7fb73d 1bcb39f613a0566f8c57f01fdbf0cc9e016b3e1791483229d60c4614e715cf17 31880b2fd51ad6cf58580634ee400eee76bdaf9655a913c438d5a2d5e1353651 43452147bec295b25dfc466630d04e05522fbddf3dc4365e2a151dfba15ce263 c9ab5d3b0cf25a0993789b65ea0d8bdb7fc5ed0e75fb957808584cd867d61e05 653d427ca8dfcbcf40f70260a6b20f53d7a17d28d314ab33d1604100583a6102e321f8ec96842c6b2dc96b1facfc5aa763f27b4acb0af67441d2b51a90dc21025fa2b691db9068ff989136f4597f62448c1aa88d8f892a0f9c9d52b952d1700fa0b09d2186a394b840a0603943d8b9b716f516c662d694a6ba876026b4e6e509a38ddc71b0c561fc98db8ed5badd542ac65572b8b4ff7171e91e2a39dbe83c0a4c36653c8cfe9ab02f0d1808c95fc3ff6e416de260def85d60f0b7beb927730033d832a1dbc26ad6b340c0d8a45ebeac240ae6b266315159bbcad5d5ff7d1f064418ce5504a622db9d15481680a8e6d65c4050e062723b99a761e197ffa4fe04d368f7ed0d287323d8d05ebd32a87ac308d960b32e07da6f0904604ae410fc0b5496192f5aa9ac4f0d488b69e8d2ad0ac16295cad84bb16a48c5b390382c69089c32953d4670478f5dc28984aba1e9a53730cec6af659621915c4ad69b9a2400f19d183aa8b45b6f2c9fb18f20e8efbf8fe4b9d0d45e5957efce334257bfbc0a13c3fc7f40282c6c6683df5915c7b7cfd919b63440b3a949e4aea32ddf0e8401a4d2f6aad8a114308cb8c4e34295e71ae6df034b54e4f891018405bfb4318e0f10bb00e5a8b7fe9550c83e439dcc41b9c34df31e4c63dcd4eedca4d2efac77075abf45e13cd1318b685a6f919a154e2cc7be2dc458edf1427d36ddb8f8161d01c4e0db6dfbc20fc11c31870476cf8c2c6a94a7a384c4bb3f836d1e0baff07a0ff4649b5a524232a5237fcba4405fd695ba25d604dd32d65c41eb43b7825e630d304618400e964bf3570a5d44ae9929e10bd5dae418dbd9cbc1bc169b316cb90bae88ceb5e698a239eca84a8df4d79049c10160755f7fea9b5c854c292bb00c080af42cc44957f921731f21aa1008a50d0e53100bdef938bac022e7a51438800bf79c5ef8804059fd37bda923aa900452c0e1a3bb52ad0f8c6ef5ea150a167c01299f89ab0e79836ed364b1dd5cbec1b907e979a5ecb532b4e22b211b13bb3505ffcae9dfdda8cfb5aeece5d33b2a8be3c44115313c40087b318a087933a5ae06133f725fdc348a3ad6804394241b93cb4efeea60a26a5b7bab16d1797f8a2e028c3153cdb774c1b1d9283fef38c59d04837bee07db0fb2995e8cb0850823e40a27d5cdf09e3c9f846b990f8514a1e12a2484d68947c852799d39e1f5df949004a730a49a6a5c5941d24c1f714b5e5a9fb9ec5ef30e2d1b2faa26b6ad2c74280e93c16fc24a109b0b2f706c8ae439c874bc760c76d57fbe0193a618419f1d3a03a633de7084532260aa32d294d3d5c1a3c89bf3265e5172bb103b67b5b991890f67d8b88164f87cad09a7612ac23020867f352b2afdcb34aab5ad1dd27abe4e0178416ab28c93a8e3360dac0b1f808ad3e97ec76e4d73e66ae22f8e4f99bc840ac4841a9ae2f9ab81eec038f5503093d86be627c87d9fb2f29687b541fda90a0f8ce4aa1ce19b911f43ff1f2746da1cb8ddc43e26244558c711fe8cb0618937000c90f0015181ae5ab63539e691dde458ed70aaec59a7a861cb86a368ed64f206ac997a40f833876cb17ded245e599fbc788a327fd0ffa29cffe53f0973b8f60dd4f684ab63d1286c1f0116b3fa02d4048ce33e9cc0fc924a8d531d3d22a799045549cf3e07b1adb0810c1a18a0e52f5350542740cddd26e254d38b88b317260b77caf9ed93556d6d005e12a0a1818db103a394cc035a9e13f724e91835b1f4072a59e7246d6c9b279e4690f80d9fd5f442e761ae745dcf4723fe45b556c7c9087b2e2442652ae73aa274f88045796e223815c269ef25118e624a095bc46ece057f43a2df87c96363f48e627a33c89d2c832fe2db01081b548795cf6d24d588065ae4fb2fd3db39dfc7a1f9bb0dbc31627d72030f632130fdfdd12cf6efe7674531cf5dcbb45a35c51c34e29af4ccf79187aec320c452bd517488b454f34c8e09b418fd6c87cf0a7a339c2b5cc886cdf46d45f1059d6df6338797ba97a1526807f11986c6a14f6b344ac0e91b8f35169053050e5959bea0a1a0e3777100af4609b3750f965205505e8e9d891841c1c24d7b9b450016b38c470dfb55066affb400927f8e0bdd4f20839f2adc8a1175858465b91d8339f43fb65c6b38f1e760bf05d5fd03c8d220c908e00e0730a0519a7292c1b73dbd57bb700fa9e759117ad10f5d5d96c6b016076c5c090cf0bf73ae00ef8b9f84996bc48a7186ff92eedf3c0f132cd959610a7fb8cdff67708619bd5911f71241dbda860c1f6b922336630903a4dbb54372c30ed3330f8be79d458e3771f71f863cae23162e4764cb5de3e900d982d64324b8ca295551859600870c36d93787b6de1f3644009d16b7a9dcfa037a8fa3c49b0a24bfd0e1437e3ea6b8e9e099f3a7d7b57f6140cdb2055746670aa9e83c6ac515f863f3d0f99b1939a143b90ae1c4763c6a85e1300e9c360394fea63a508f7283503642053dfb99d82312aa51d83ae7cc4a54c308b3ed526689069d9b6c67ad0e4b472580a82f35016fcfc1b1aebf276873b5dafdd55ac4040b092e9011923f6c3449c8819a147df8b93885e16a4cde53d5a5b5f03e6f3436f80a876a3ef7d6613035e3958582cde7cce92c3e0d110218ce501350316b44c14708d3bebcd515d43a4d5cb0179b42a5496151c7ade93efccc25fa96a9846f1d850cd0b69b4acc2634d25e992a80c93fbcfc06595de3bef683c5c3866dff134e090eb60c3fdc22bbc99d279300e1bccf388e607255f8a6f29a6685c6cdbadeabf00820308154bee6c3357127f165b0d736d087ed6360cc325cb50f3f317fb300390271c40630e31a333f1e5a41a25cee9c246fc231eab48b2ba6573c107b8924fc0863797b0ada77203857d081906e1dbb1dd144508a2a240144e9f44d8f985fd30399768e58637cd14e0beb299d23cca53f5949e8bd13750f0625a5a0f8d6137a0dc6ff3606ddfb347b8d210190ca436ba723c0a86dfb278f47d171f9f483eb820af0f0b809f1a7d01d731f5d8ca479c712164d83c0f06bcf6902f4969c2d909a0c10ec53bde84f2ecc86e15762004ca703a7d0c36854b4e29419aff623a360e003820641f2e8a59cea9194f2e1cc4c9ed8a07462b13636149cb18a8c6fd13ac20b550048d5740ed4e8eba5fa8113d85fe5436f1a35b2dfd8433dae8fe2ae8c39074c0ab24c846dcab79d621326705c1296072f7ca4abae7f582447b88b66859d03e64e37e012866f88475aa868ad955d07662b5611b8c0277d1439be72bd8aea078d2336535468e93bb3ea9163853f2316c592821c9188aa7111a5dfbf166be705600f20a61b08478d3fdbc33e69bf344de80911af92cdff07ebed22869343100ea6704fe63d7ea2dd52b6389b2d5d5d64a9a986ee61d3438faea7eb9948cd8b0bf5fadab4b9cb39c230017c98ad8a4dd114c0a6eb71e2267f737475d02362ba0b41b904632be67a589ea906122d7757f8a25c6b8d2e5c4e894abac460756ca209bc43c78fbf8b6d4ce0928e3438ba24854c2124a089de603dc8107b59a2429b0295169d8b4071f46583cd4f8defd8073fa1db04307c4bee16a3ae1a6de4ae1e055e69c2210ae3202d169f96b34c786ed938fd9db7f8583991d2addfc6180fdd0086e24c0f8c5505d5608b51a6a7bdeaa8196bd51050bbaf86d467d2159bc6bf07c418de84b3813b6d8f897a071436ae7cb4bfe37fe196b55966b482b651c9b30ef19d4461e60490259bcc546c2de8eb8d028511935a5b3c1bb2495c946fc1380e0c01905ca23b99ed811b3dfc40cd15b0bc072baf9ddf2c18b25ba6dccd01b90ef519a96d4292ada97a0a738175a24f33c21315049473e21ca17f523ad9173e0ef94d08a12caf6d2967f0f60cd16937bf2cf54f15753b547481220b129cb77908841782c7d1229f2a6ab5c415a6d46bb7cfe894f28904f03e8d2133a3b97be509287872cbe0e0a634ef78cfb7b1ccd303866a97f5ebe8e707eb28a3e1fbce870c4e970e6ac8b2d05cfae0df869125e688c590ee46c3f6d4879afa05ff88e211078391d44b2c5d0561481e06b1cb7b6c6ec853cea2b2e33f97166dc3f9e7a7f3030c01d443e88b0400ae68b3ba395d9e027041baddd0639452c3f7485c6e4cdb056f7d0109c52deebeb59502e24309cf610ce32c21bd788fb5822219f6c5b0010416b72d38d01ef16bc433f7683516309d10b67e39f7afb407069a7b8bc06d1c00bd4e5fb94ee009a9f22a8345aba487ee625b7436a9b82cc32440c6fd71c967055ba367f0f63b4739641f70f535eb2e7de80299826597be687b276c7661c7cb028a4d7b52e42d7d2b8de56c8c9093df2b34532bcf0d0be34a22fd650019d6fd083e52c1d89b9d0443b9eee677d1c5ce7365d48862f35fd6bf7477ff18ded3c104a19a528b6010ffb7aaf7b1012caa02f767ed1c104670433ea21b52cbe434aa01d216c955ac5abdb8a9e0567dc464d16d57b77201b832694d18ffccfa3dff800481e8e2187b761f82eb9138cd29249352c723d7255131b9910d979b73bbeca407bae44aa132589314bdf784760b9304fdf8fe09bb982504f9741818c2784eeb035e77323b031152f304de67a41272512e0b27a641024e66d55a136c072dca8101bfc57d37569a5a7920ebc0152822aa30facbda6bf98e7fa505438ae12d01fc00d56349553f3c886990309c0fb81ffce45b55c36565639d8149634379eb30c702e8b532c5212c4bd01a472e73a3ed8e1acd91cc006e2223ab9f0446b22affb806d2c732e39e3b4294988e22d18de1d20e883265ee3f30710a95896b695532e5041ef754d97038cb993170df1da9205249b6340be91497c65000c909f1658c7c006ac2be26530829f2a6e212736d2d9930cc129aa990a827c19d8a26798a3b6907924805a7be40f157058fb56ae352a46948153cfcb56fca8b2498d68a29bd290d64946de6fa58aab0a48c2a1f935a8e8399017f631dd26585d3011fe67017490eef81545971c09bc539badb6584e1c3eb348dfbebcdf0c985ce1d033e0ccad3089c4c68590d09931078a3578131ba60bc99ba9f97ccb126f09a1f4e830da46e08463c73779e7825a262f58485ef1f4b646480917554c918e01883746a9bf01304f5166f960e1f9f526090062ba014409daa902df0bda3117348d41367473347005509d37d8e76ca9b584d1a891df3ffdc15a9af42642e50af7facc7e05051f503f854aca0adfa47e0e577cad58f7e4d1b832b9758a271431d288177f98a4f7e01f7b7b38fa5ca364711c3fd6afa821ce30d7b7904dadd7eba097a1c45388af109074bc49a37bf052d0159fe6d387a5f189e9e80a1e8a427e3fbea43d9e3a63109978de548f730de936aa4bf6523b8f3f5813db40bd50307f9c01217211ccea906b75c6b9ece3f21dff2743a64e43a4e8761ea7441c9ccb84f690ac28bbbf9330a97c2292eb0f4e93fd23ae4d80e411173a8852fa8777a7686a6a7b36dc4a1ba0c8b47d49ab1fdd441e7f11ff55ac69a0100ed5f3c2620ad424e8dd2e8d904cf09abca480a521327402441f13218c261d203dcf749306bcb8d2ba5964868b6f40c9eadf2b1293708141555f344e2ca6cc9d70da41a436d50146939932113b0d80278ee91d52594ed70ac2941635e5de065f80d3c36a9454c43880021d2b7acb00cb25642d2e96220fc5c55dd0538f2d99cd4f016af5b3d9fb66a1f036dd35e680d0f1be30a82278aa1c31e2d1c2030fcabe09bd4ae74968bb090ab9ca456e318020fa04f52ccb7029a001f7dfafcbd78bdb4f13df1af294cf1aa6b47995c3c2709bb3d25004b830a2301f35883489edf68f66692dfc67d9a31c7d7bd95aa49e5089a68e3c858581963ba06696f385b757df19a75ccc3ae4d9052ffaa082e507c0ea0ed36dd1d636891d4c52d15121ec425699d38397429efa5879d24d773880801eef6a2f1840446e32c4e802b94bf5d06f2c1531a969a9d8b2a27cb781637130bd8abeae7667325fb549bcbe3c5f7f8c04895da17743e3a59eb9a17cf5361460a1fa250d6a2b1f59b849f878d62c4d6a415b78183c4779cb75793d62cabbc3107533dfd11666d749174184240c7fe866348defd6ad74285017bd88f56bcf3d301fe58d8f1cc3e79e2fa79d525e018b7314eeba6aced67b6ca3ce2957f86a16c0b8856e860a3192d04f8d0051f462f10bf92303dda65992247b9e5169b9dc4b600b73c6ff4d0cb40dbfac67a172f432ab291dbf93935528f97e7e156b6ed75c80fa971088d66e9d4be33ffa68c69f3a18c9734ccaba7774b2772a5c6ebad0008094813e6c1580f6da36c8622ab0075ee569d060c4e1dc5a6406dd2ce7ca3a7340f8fa990be208626fb0ddc17599457959fc114c9f438da2656517b8f80842ca10a4ba67ecc7e375f6097ab5e8163828585729f14c5d4736f7873c8f15aa621a10366d64e83babb0d1386c08258ca9466d92603d092042cd5aab3dce296e2af7506e32f51c070bd02813fb3df63f6f4a9528610e4a3220db692e2d19593324adf0dd3b9bd0895ab9debc5c7f137fe2047ccd8b787d2336f0666157dcdb2ba80360a1ceac615d003532b960f0310bdf8140c26d4f43711e1b07d498a6f9ca5fc280bdbdf8ee6046023ea914f73c99d21bcb2011070ea45894aa9f0c16489f3ecca0d4f9afceea617ce63a823de7a3c89766d00f6f8617de9b5e29834b9f3601b4d030beb2df0ffe8e35e58d8f3eabba139cc0f55b95cb52684387c6bc70afcbd300d606295f43bf5cc0d5ab3a13a586941012c618c1516c1356c3dba5a95359836082c3d5e8f67f10899b6b272a09c32fa53fd4dc6992b1f466f0698f4a2e0e2ca09bf9b17dfbbc87e6ce816df803949128e06af1701a6f7a3751719820a015c5e06189c26187f8e0eafcc99682b4521647ba0cd59330f6ee4d40612400f4a74ef0f3e56259d9460fc733321956f051c6c0001df6d66eb61f0955379fe835b220f0c7d8b7d86d30b09993b83b08325ea1d2e8c15c09adaa672da7514afb5cb8ca107326bd69f3af5ea9c7c7b94e1fc71b338914df206f133945f89e82d14dea0970a13f3e6e47bb2d8a7e2713b062a770f0d628779eea0a06ac2efa766cfb08bfa0bcbc052729652a37c5be3126882dce753c359dc8e259c50086653fb4a74e7bb0ee1337437526c57ff4fc3d3b8933168ce0545a1c310f3a255d7f2e0a8dae8d0061c1ea7082e925530617db21e2ca6d8a849bf1376715c2d6cfb15d3310037df0b761b1986ac578a22ef12aed1382c5d4c5df21a3f2acdf23abfa969526b020601506f52b020582af4c68e6e28554b50117f30a8e12ce995183d95ff6d39436f0c5032ffd8c68780b73e7355f9ab1669b6d411be65c59117c4510d7e86e7decf050913380037b4d37d12bd6f064a754d6fbb70658dc5ddced808f975f949e201067ac60759870bce1f2abdfe0afa01821e9e4d33524af346ca33dd83887b9e8b0afdbf177851a9af9d1a0069f14f9f1ba872c157f350356040d44833dc373a3203eb8406692d5faff6c4c6a5a284c93307855835f99066f7e2b9199d1b2decb908e349e92c810a3f8ba2c09b6d9b2bb4ef3c127892fe7ef224acdee1b0f6f24b0ea43df5af11f743496a2144aff5d684c06f4b7db0dce6214836f3a34a4f820703acb601f832271af4cbfac331e53a76137dbb27647dc68a07b40f3c9bc5efdb06fa0ff652dc4a672d499a064267eb2509924b0d32daaa194683de9e8e88d24009011396fd3f340dd5441b28884fc1b884577f4425d0e34e44e4965626df314607fa22b0458a8a1c30ecdacd8f2b945435c70ad98bce72e1bad19230b3bf11370a328282a0a27c21ffaf5c6efc1cc67c2cee9c82d47de0a4c695b9f9a9ff686b02eb657b035f3e667d4854e193faa8be2d9fe7dd8af4cfe8e0295158184a6fbd097edea93c5b08d32f78af9949bd04bfa81ee751b26d9d5ef3d05e3ffcb92bcb004ad171501fff98a096afb3362dd2a5da9ab49008f70b0982d33ac1f146151201789deb3901e8a893060b4257235ff0af44c19096538c65a26339eee26fdca908cd4d6984f7ce2a55736d1447f87b8ad7e4ddf090e0121459012178d058194109805869f3663bff7ec96c3e6d82b3578ff7d034ef90e0df09bed1b98bc4ef440e48da9a20057613badd801e5d171b5dd63376ca7dfccce8624fecbd2127c4cd0ca7f6e233e6cb82ff60c7df350e93913200bd38d790a3a96917621c9f7909fd03cb31b0ed286f507943e61f335d94520403da1dec6001417c28aa8ec4851f320385512da3f4b365e8d099c79a3863063ddee8a7dd031df2a9a16fcbc10aea500de691fe6aaebffc75ca6cfaf154d82c703651cbb7cdb3f49d1bd13780cea4420da99f576492ab2fc2b19486dea1efb8b9827f20d4653c16ae805db5045995350226bc02642980fa4bce81034f2ac0f1136b2bc4361311a76afa46b6b05f5ea80ef72801c73b8fa9285d3f017cc0d73196caf1ca12cd739fa1165d91551628f0058c19c4be27aa6c2f11353325d84b8bc52b7f7441fba4f7de0252493dca111f02037bd02c0ede74c46f95bcbd46a350bb6b182b02f073ad5d9423cd52f8d2be04814ddb688f26343e5c1a26e15bbd3fa2999fb8f7b71a04b7ab32873751fd550c2c836de2c08b33041c6c573ad07f8e892150f7cfea1aad09ce94bf7c9247aa07bde6cfec771b72a6ec8de0bf34150358b9baa70d9dd8e21ab2e52b8b4c2f2d03c6b73eed34b8bc7fba784ac945bb60f8bccdd0701f080565df8a6a5809e1bb0eb59ad6ef694394a201ee15a52e4e0467f32a1132335a6cada4afa022edd1c90da58872ef0d308f23e34e759a38e3bc89589b039a4d834acbdd1db0ef7b560407953e4b4e0393598b324e4fa1309aeb5ecdd627bd99fba74a118be64693a6f7051097109e9106213b7d1d2b81e4bd0f3f3a7580f192504315610f78aec364280d127d1072c2f36d82d86c525ed4eee57eb82ec72ad55b411a40d5a947918fd00309078941b3141d0e91da75978fcd315b23067afa27a653b8767ba9f621df2903bd49c9c39d1fc496cfa1e04e255c1dd8fb2048bdf9b0b2279f3cd888378fea0e756340bd3e05a7b5efffd61b23644f60e74d81db64b0d65c3062d386e2606a003b2074281236064d1e1c4e8789d7415101e94ff4fbfe07af612131337feb4100d3376c4da40f3dbce96b2556b214a6fc41e3a1bbadd56ec7dd9a64bdbc6e0d0a182993b1eef921982b0d646a07148f6fc9bfc1521533de376803de69c1f9200fdfc0347fbe52babefed8768c51810f62bdc73608fc748f4555208887acbed20a7f01ebe555ad7e1912a6d5b5873f86d698f1d2c88616143bfd6c1b4cb26a6b0639e5e4ee7713cdd1ff56e5af720a707af43b6c6feae9a6e6a90ed5694ea85207e64f8f02adaacd3ad35d57c352c2a845b68a2a79fd589d1fba701d9d4068ea06a9b80bd604373ceffc04298ba0cab2712711e017c78eb4852d0f7f18c7658304abddf9170054872257e463958ea0a956bd3784b3e8b94380b9b6eabe8610ae0f02d1e25724c69561a2767c585acf832237fd883b80faa3a033f11e35fc70ac07c00969f7e3decf94567de3cae901b8cf206a8c5c2c644529c6c1322ec09c680c328390cd0db82aa329aa58f696798ce968367efbf5c3771839c57cccbecdca038959c5d32e68dec1d03d2a1f61cdcc2f1e9b6e93cad0ea0653490bdab3030b0ff6602c4e1e5599927b7b6cd4c29a2e5861a247f6f4cf492cf3ea147e3f7ba60d54670eb3ad5f3075eeb0d66a925f9fb45c75d1fd4a0f7b431ca2530ee83f1b021469b3f52083ca2c38ef1fa82b4ecf1001947d1faf9485c537fae73cc125dd0a43bece5cba59101a7e91a5f56665a251c9276fc5f60575c1bc70cb3394f7780e9f4617ccc73176cecac344a998a00c13b5f6442a6fe813a549a6529a7e1be9049385ec0b23040cb37b06951f1dc79b1abb726765d1c2e2d96615e7ebe8ffc40e066e16222ecf1f2c0537a3f07ac27b45bd910fb84abc3564ae0957e9bef654042a674692bef3f71fbce8b97ea9cf4ba6527033038da372ffbd973e25e9481f0f9e7215f37546521dc0e7a1453e0a412e44325999d5d8e41c7ffb0662aadcaf0ddf7646b72eeb7617fdadb7ef66984b2f905266b0b856a69f31d864bc3a2b3e0861db396f4624a05c13b79dbb53f604c2dca13e9c995fb278c84ee28fd7e79d0990e25a4a94bd02fa5bba97bcdfc494b3b0b01c1434f5f1101b7eb9ae36af9f09676890a824cb595a77e9d923aa8c85e7f596d2400c7c21d95e4eac852890a10c16b49895722aefa3449d221e32ef63eb9df497d440f7437fcda6ec955840cd01a81812f0f5236bfb36214f03c4214ed4a617d2b77430fd1c3d8f2fb5b190750e13a68f80cf2ff293e684b53b1518c8ec3ffc2fde84d0313b35fed4b5ddd61204bd6d0a690ba2eb9c9a1eeb3384e5683db6413836dbf8180949494f82a1a28808ce4ae9476697d8396d5a46a134b972664e9e9931d235ecab00900ccd8e7a2400453b9bb47eae1387623f5117a2833cb112ead35044b3b59df7e149b0ea6fd10b2c096f1b054afbc389b7b6819434651ddcc59dd82426fd753790715485410106d9e67be0155345d926b134463529dab9d56f6e62ce3761d5d6a38e0d1b1ca40e6ce677ccff063b231ea9d716c55b948259448ff7bf71d32d87927fa146bae20ec7c6896dc3fcd3b009cf77e49a3ae99d6e7595bf6d2ed8581b19f04c9088080adc28ea2f7897fa89739141812628ae4ee19bf8d9b94ba0866881e20d9ab39b0126a230f8ce41a12a60a928a6f2f8a1239c495b07f7ae0b50a55f1a863c518a008f47c2248570720c7773f69ce538a419ee748e5a52db687ed55cb1b3aa5d8a001baf20830ab642bea17700fa09d2e78cce62f67834b9e2600f5d8e4fa4b5100c5eb84e0a49e31db355af53ef186609d770f32d689485b7e83127256f0ce545043ed3ccda06de21a78de04e8ddeb0587e59b349df9d012783c56798d5fd47f4093bc32a078654bbdae7704f4e1656585289f806ec311b985db81b2c24826737086078c347fb5402d842116c16db3910c8664dfab4b745b6752bf4f37c24699e045c0e2027d39f2686a0827a5eaec5fed6716ff1b579e0359b63f5290871a636041816e257e0224d92f892178a568ec2aecb13baf7dd76a72e80d8cd4360228d0a256672ad3f92c9c3d7b283c13563b987565ddee16637f42ee1719b4913c6b40def2dff40e6db8e4766f7991737c4bda6ac073b6ae71d238bbb588abbc47c3402d5c3155ebbf68eaa5d9c3113c83849f16f6b717ac7fd1428cdcf872bcab21402c8e2f869421a91003aa01ffc714aa0b5761bd1acbba2ab7162ce195e548f4006bb8121f873a5d788124cc544fe2dc9c6af36ef5c58dfe2fe3719c802f04a610a0d14d2ca564c5a19219eea89dfa86971dbeb7e285b56f2979767b1c1676e760df129ef3ee0ee07c6833586dfa1ba87b10145fa71cd9bd4eb350b566df2728e0004f4e04341aecd0a13c8e867d0ee36654bba598ec159c574eafb2cc0430144092cad78504dc2bc537740df4487df5867d5f0eadb277503e698523ebf8e831b0ad3543bf99a58393e38502931a2b09ff090b4ac839ce5ebf43efd914d15c16208dbc6f3c08b68f45746c58a801ad8137bf4200a4e33e345d3eb0f732395e8210c250d90a0727d65b861a0b305b5b1ce363551f263cb1493f38f1f7bbf32f75203f04554100d01d2320b71f40adffe6d3c81d183a0cc1c5027aa72eda1296cfd002bdf55276c083a4a2a7992d78eaf44eebc833d9a50a8ccad7114dff61aa1b304ee4b31b7a0849d44486e2b4bb230988d673145ee9c44a47a14d846dfbd91160b08864c7e427b5b685814b5761b0f18606f58efdefc770b5b04dc275f0b18820901e0822be8fbcca957a837ae2551d9d31f76fef69d6de2f02bc62dfe85dc7d02cd96cc231f6e84a75f25cab604af30055088596ae0012967191f77309389eb0154bee63a985bf91a1c8dc35d61a049e742ab84c74233a75571d04bfe707ea20d0a4019e66b80828bb62f6b19ae2432c5ca9e8a5536cbd786637a3662091df70c2251ce5472ece9e4e969bb94307a0c935ef0a76aad894100b5acbd1a5b458f01e63dfbf03afc4483bc36dbe83f171204b54745988d85068e2f6b03be20be1d0e6e1ff71778967bda7ddf9ef9532ab5375eb597ad0ea8ef66182f96b2b53d7c054a3fffaa57f76ec2685930dbbb04e43ce5cc7ec9d9b94fd1ab948bf1ad9efe0f7666873e17f0c716059dd9e7552dd709a19df5deb88f9c48942844b650c12804c323f193b2818dec05f1b20caf9a0ed33a4e233ef485bbf42cbaa61d05c5e30f8d6d99bb15f98acdf03e21f83acaf2876ed7aedadd6078700f309ec14ad37d0e85ecd77834062c03ced5988c0cde6c728079994ce206aee5efacfa98cbc91b0de9a2f461eb24a0b4f48d1ab247f91e124c2ff5210e1e9116684389f7305c690d2f4fa4424b5687c689b794b3ed6c6447b231f58cc78359cf9787654a0f7d400de87b1085c892d1c3c638d7e2a913e729fce10b3f3fac26bfafc36285bc19f20d336eadb95c3855debeffd37893871ed2f9b3badf4f907cec4e725506920ea20793364b225fe0a4efbd7b3b4c653aed090719be2e2ce2da2bcf32aeb8eb3539094341d57458ccc8d02809195f2eb26fd357eba685a273196d93be7a40fc5b5e0bb71305cd514116038fba23a6d5b4af5e3f8ea4749bc4e98e571d4c245623500f6b891e1dcb43d98e686baa2812ce1e3df8171eda0d7edf598896113de968fb0e2cc3dbacc3b11f2a8a0c51f9027c42d5413d7224a43aa743d86eb68eb5570702829b6e781808fa974ddde76308cb52fb468c95395631e4aa77756fac7e728009ee83cb4b32184debdb081ef686fc5be197436af6895f68547ed4310c4bffef01217611a49f4dfbb6558bc921b2104b93d6f9dc6eccb857efaae17c6857d6c002cdf69e9638c3a3d39d7eac417601e184af59276eba78393e3694a51e88af2b0b71c65dd68ead6e1781ad934966c49ba397d5b2e29af1750a49e78f52b927ad06948d0d50f41f6826919f381181320c93d40b116af14915627252402d5c142d0c04f1ff883d1c8d038f81671bcfe6c2d8dfa421e650e08bb693fe8c74ed7aa40f725c6598ff7adfa607cb54633837a42b5630789cec4ff96265245a4c17fe1604313d21a1b2014f97b6715d49aea09db9fc72b9779676946cf548713119731d00ffe61731f274baa4b64c917262fc989ba18e2ad56d2cce7dd5f9bbb687bcea0fe2ca5208b9d411a278da2ce1e7b7be02010d2eea3359793112cf85bfd3e9bd011033c1cbab13c93446130dd96963af5adef736c92ea48e670d2b83e964db8c0e91d75087462bffb0c9bb07bc9866657c58b3e435e067624b3c76a4b7eacdc4045f3d5b1eeca6fe8e79a69be8def414f69a64e027037adfbdb9ffa047eaa6d00cc8e019c93e586c1d49aae48448fe4615c94a4a87f133b485f3c2944cd5ccf30c8db78170bec9bf9d735c9c01c7448b59284db2edeced56662d1bc47ce447b209e0cd5ef5c485f95750989faa775cbf7502d7f1366ea174773fbedd1add67ef007d607827ce56314674fdd343bfc479829378ec85d74f81b3f6bb96a937c10505938d31fa798ef0c4cdde3bc45441f86a3ea8aba63b306f71a7883ecac510c40290f969359906dd6e1da470a40657c3e5d6be1a4412778b98a6b23f6e71006d04be8e6c0e5f1e58b4b65e226ad68d9232492d242c79cbd011480c7f363c225a0425ea3d411dd90356e7a501de70ac66dbf54080ce7e9780dd96092dfd1f7fcf0b4036f1f16b37a6a0714ce169b85e5d4c63ef9aec9070614edf0689b09cd8ad074a1625c02ff6c6e0bc5ec584f8b0400ccb8cd00c886b6a00321e3ff8945a8605eacf9576dd04fc7e9c4e5a668bea8bf9345fc721efd5cbf955e563d983418e09cd27ba375275b6a59bd5089b4ead9d9a832b27574e19183648865967354293002352ca8b1154bcebbeae7ea7d6735d0a89b86b7c13766f25e4fb779a6114f4069149550982b9672c3fc2f3b6d2f34fe98f3dee730f08885c06a6e2259dc98c0f8c6edeee1fda2cf82147f69fb16074a590b2f687025ef5fa3a18d84c60548009b14be88d4f02909bdcea592bdf333be5498c0a3b80a9a8c81125447787058600523b3ce98cabe427651926bcc360cce67a6d3801afdf42ecc81d4b775521ac0cf40b7420996926fc4dbde204e2e3e817c5b21ebe51fb136ced553feacb1a310c9902a058e2bcb4d8f95479d03870a7c57a612b34f51e1854ec0b9e08bcafe10623d0295c45d6ea0f3b2ba25d6353e85970947d6ff9dc59db086d9c7a835e21023196948e468e84c66c0d739764d129afc88546de80b1fe25e8dd918f39495b0fbe8b695a33e456a5d42b53173657d19b3e915dbc2b4bea4bcdbacb68fef249071f211627d2bd59b2ff728e23c9c155887ba4599ab8e9dcb42e9c93b83e4a1e094a46baa7a488a02f08dd49f704bb0be385a143f5ae16d98be3df2ca969ebc8031e8cee1c2284366cd16ae856677017066c65ae3df55edf43c78724bde7223b0862b7b987529c659a5c77986c7c695f2d7a6c5360ef32019f3fb7bdcae141850da0e4b60bbf9690f3ab38a1b1d080630b2f412755aa0067af9db16bc2ef772b0d5e76749e44487071b0bf5a38dad463a7e667eda89d49a44239022b030afcda08ae94fe5782f0bf3b2b13bd802cf0d8436f77d758b4e85e24aa176ea6a843c40635dca6ea99b2819db3be78b5b51a086f7df513ede30b8f87b7a74d0eff730e06183915a7595218b804d43c860a00fb1163f43cf6856927fe5340b14737f011085765d064de3eaab2f9f2008875e2bab7650724974c1a3e25fb17583f0c0e480888fb6ad90ba8ecdf7de017fd0872ad7081be6d0578454594fcd6a357b375670c2eeb9045f3b6c41749d07092f82398b6352f421ba8f001b20d8f56010f1801058363109e1176d3a52e5bf39a90d410909d104348c07992ddf710d0c6c37aff069538579928b2fad61bf6547ba9a4f76e97484c098837d13521ae9a28cb7c950eeb8da6eae290a3807d0225ae6eeaeeaa5fb92b4f8cdd7f58cd997dd413d0ac019d3c3ac81999adc15b2f3f477f697f9a10725910a272aae169ffb6e66cf9f0036b8bc2f6ddc00a9dd7fdb195e04ff82b607277061551cf7b9d9608660dec920214631a4633a6c7172fac759bcada8062fe049b069a11907d7ad245ca60af38052d8a1562e566216bcf0aa0c3101b10fb755373a902d0019e13662aa64dee1003e5e187af39eb45a503dccba6523b377b4037c881ef1683f7a0701d5aa65eac07eca2f8928a0247229203a193006099d0916efe35d537c96690e1e74277b9dc0f7a2f243145b5a19aea7f94baecd1e6a9ed45345b7dac7dc45a7080849a3ccc07e8b5149d6d9eda453c2c5e4a6fc28f02371529a4bd08a7625a92edf5f72dbd0a4495849d925f4c5c230a04b93c404e97bbd727ea4bad28fcec45c57ea07b1207323642bf00300642a13d614b079348459c1b55dad50b33ba50788a0fa2d85c077d6994a5e37d9df974476f6a87dfc9b815ac278fa56f93ec55adb71edcac530b1b3f906d612911686874377d9b21c7bc2ccb177f059e7e681b158dd70368cf042fed2098ef224527a36c08da0efeec96b75fb0fd5abbb26a400b2e1a05decb0e80dca881b5892ab27cf119d66b46ce39a46d8faf38dcb8409cca5a64ede91605393f409913546c6be6b02faf1df09dae9fa50c745fa37a88dff98fc1916c5a06706111febfe51bbb18a27385fed2af8c4dab29d3165cc8efd8d786b44cf352057af67c11d0e0bb38cfaac45deb03f82aaee26fff01b0c84c5c07ed9240fbcd05f5b431a3a6e49c5397de0b1b9ff9f8d7ac52bf68b0227f71832a7bf24b62960eaeacc7c7a74bd5ef3aae2663be2c96e97ba6ebf1b0d59104b8b450804b86b00052b321f4b05dcffa8ae6ad5fa28ed7e4a55e9724b1dd8ad5e234d24ee45d2d025e7a5153df09ab8dd4db0754ceb1ddd6b9b0d442149b64f40debe1b2b2fca403e9e19fa2b6a073b197235f4785d0eb642afe54019c20aebf3c012d257aa5b2024845a3cddc30a0551cc355d5b95b0797f607c2d0ca87d6a1cdf55ee216ebcb0b11ecc328d5dc268f7d427d718a171a468c09e58413008b885f449e9d09036b0b5260489da9872f0fd20984c95ca31630ab5ce8ad4dd9660986f841df7d775905cac8298f1827b57c8afe889818c4b7f149e6a1d470ce6b4b98835c5a2368e30e1eff8f5857f6fc965c3a39992ef36b685c9c7d7fbf031fbf96c18ffaaaef2800749bef99e4252d8c7b0a54c4c3c9a62d8f4273fe0c20424a094ef86b2ac2340e7a9c23e63f6f504c57c3853cd23d8baba31c46af8db0b0d914f7d1bd86df36094725758920d134d6f08cc1dfbf1af57c3134edb14aa3896ae61a71f25fb70b09ccfc895e726e2fca22223522d31b3568ca528d7a2186ece2e925abc2ab82000adffe11eddbc7ff6e41de5f3c017f1bbfe6dcbde1a86f18eff496e9c7c6da8e04bd1b278a0853578357812f9bd6f7a6c9afa51978eb52f2d4b47dabb6ef5d950c6679eeca48606673e39a28c934a9ae81b45126e9a1ba087258b873689516e40ac1b658269f20085bd9eecb9a2a98ce10dc4fe9a415da3d79574b1355837fbb01bf7697b02bd83e73ca0fd8b0f8099aafca372f1f24a0ea0abf14edeb9955dd07acef72619beac0f988a6eae3d31f73444504e8ab4c99f76a5abad30dea1b9300bb00c08e7adac621a4c7e22e22c27d204dc376bb1e1aa22eb0b109e68d9d490cb391b6951e70f927b3f21954c60e355c53f02043f962f353cedc7df93e975c06ad74d864ef0f9a51c0f2ec40d556428395e9093b4202e3153c8494734caec40ef37ab4935397810a02b2c74190076600fb9625c6659656ef614d09a85b294b0c46264950573dfc85220148e8850831465e07b7f7977f2e4bbe22c4ed7dfe5500f6fa2e040a0b64150b73c309ff3b173280b3cc483ec3e0119190b082d27a7404b91a315570fa3b7ff11ca5fce457981bd2cb94ebd4cabaa400312fc52921ad04afc00335b2308ac511d175fe06135666d78b92f3e9626ddd6cadca00483dba0c4d05e2f5aebf6ac791d32c5053ad86e333b4e658b821f6b3e21931dcdb826b06a44bb0871e13ed113e0e85bec04ff2d0c8e04ff5f88f7cd8cdc8358ef05595000ee54f23593efa4274066737dc3217ff3dc5136b2c1297f1370f09a156c8c70cc40d6229257898aaa1f58748a6213f0b129a49c22055f2603cb1340505c8370db63cd38f0989d750cc6246e455b8cfae20d61e2c7a23e16fc0f57ca79fdb610d90dcf14485338bbfa34095d30b371062d4e3d16386e3e67bd2630f4c4c6a6706d7759dc6ff470712698a5221e2a6ec65bfc5d402fe80757932871b4f6985670fbcbfdadb9679734422abd06d7a3cbce5b9cc7ea742a20a70aba43f4708525e06aadeea955a5766f9e2f12e73fe41f2d2f8fe7d226f7ea5fda710ec40f505f20dddac3b286875376e9a85b922fe6538e0c5a8d74968be2362ec9593ca1e663803493abf4910c6c125efe63723d547407b72532855928574b9936d43c737e8e60557be2c442536e687b47586f4de00c0008fad967fe2e215ca4f82a3b226064702a7d97f702a41d46ec8d0c1a1a78f9a64825286c10f78be2a63e8948465fdf4087d2651ba98c8ea02c65d118cbf42ed463adb57e4ef6c72e4161c277b90862f0be4efab72419902c897a2be6f1acbb7168e1de4cb608f8ed0dddc160514067901510ff35c6eb5be79c435fb61d980d11c536cdf1486877c8683227a1a1c77eb0a89ada8db579fdb8d510b55772ebde709f0f4e3fd2f0a2dfbcaec60736429380175fbaf363cc1da4e0e41b2a8411a0582060a319c0e380ed9ec16f44ad0a0a5005b0daee73b4023792e95b371bbfe18934a0c60eaf7a855fb19c671ca11e2240e461a23985e865131ec5a53a64674d851f0c4da77fdefd83ba3b1150ea2215f041ad4cd6bdf0c7475b833dd6ce418040c72dd10db25962e8dcd488ecf7c143d0219c48d85902a1f2f4bde81768db0ccd94c48e070dd5ad46594eda7efe5395b0e700681a35dfbb90f8f8876c97acd53ea2cc7004127e616788ee07942e00d2c0cfbba3ddc0a0686cfecfd5f825ac29ff98c042f5e4455be11e690a9a56cf53b01 false -check_ring_signature 9d6104b274f820f1b92f67104ca896d57e6c270b4ae1e481b5411a1c66a26e30 ef2aa9292397f05d749110faa3a4eff6ef24d9ba83355f5b3ec889ca0835e734 21 7afac15970f637c02ff1a4df0f30370f9abeced418a4ede5cd749bb639177187 ff418f12c0341173d7a3d3badfc8d73b2bb674571afda1c5509eede52e23d0b4 8a1082e2b21ec2aca3081ac724e7ea52549e74fdde09f9b71f6a796cdb152398 714ce9150647fa5ea47185f5ad7c0ed7238b759c1f7823fcb7800bf83b598502 456c3070462bcc42bf9a3fc93fd367a8d4302bb7997ee4d93843cbb8efe0bcff 3b2b1cfb4770065c8b76990a6dac9abe259492314715df543a2fcbcbcedd99df 9bb622cbd7ff35214e3b8abbc25d01c089db7a42dbd62d69a3564d26cb9f0d55 d551a239b3944c2a3a277c039afad1be8ed9aa9ece970323e50262d522b23142 87f2d120bfd7c989e81fb53ddf288f845a6643aca5710a03b5f60dea432c41ba db78036eabce37f47f20a00bc7e657b55513f997fa7b600ea2504fc09798f883 1cc7ab351fc36c3f71748828bbb062df9cf38e3b096f4bbf6073a48e3c87a46e 73dfc0594c3b43770b2f68f1ec8d12d9c3b3da89144fd9814d09dc606064d322 34b1331598d215607d7f715a0fd7a9f351d31c937ba1e8c481a37d9805375d90 eb74c4dc38eba5380a57b2039bd6c34749975ec9986c95cd4b65e27e7e682898 0c115c4bff4c604163a191c5c48097c42e91896dca2f33a8d2f0e6931f43fbd4 e217dd7c45b67d82d272c6bd73c1bf25ecabd13c1de987e2e2dfe7c6146f0d48 c96bb617bfbbb3dad8cfce4c7dfc92a1c74de2e83cc6cbd83bc6967e8e906df5 6dc779cf8d2cbb00197efc30b7293d26ed23265c0da73b0b7718e6b484414a71 fa2b4ccd43e8887f343d8189b241879498a75b1082716027b0bfdfd5d07906ba 97e1045c7698ff582442c44901db6c5343ad4a658dcc6473d206c02f72903ac0 4e0829bfe9503470c6f2e1ffd282a6ec45b1d06421b7be7bf360720301f1ac8a a65032f7122d4d1452abe4e9839a155b85d0ead0535f4ebc82262096de89370017220216c16baaf9ef52addfcde2a4c336b556455cb97ce863e140616922a90cd800afe6ab62bd7d68a78dbdb6b83de0044dd6bb3dddcbf17586c7ddfc987d059afd2bbce4f948b33e374cf6717846a5658b436d7a15dd7b30f9b59ef8a7390dad51290521980588815977480b221294d0998537a26d926d4db2da9cf069ac0f91c4f4e6af9b20f9702feb78f8c5609e70a6e2777ab9b752f8a442a83776310fc39546f1e803cd1a00c64578ef392046a602fc4dc6fcc78c530d7b55a0ec220b207b5800e9097a7cb328179f731710bfcea8f0f70d9e0d20ecc806cd3fa01d0a9cfdcc680011733520e8ef391f2db6a1b3cb03043a99c9fa57fd00c3551803069eadcbf36ce1ba125c67130f0369d8b958190ec258ac92f09ce9c3e08f8cab01b37e973357a165e0850b858e59d9c63ef16516484f9dd18fcbc8637cbcf006097316f75f69783b319b3a491dcb60ab56d6bd0a0492d5e7c943c9e830e78c0e099c937eb89745729804a004c5f96857d838e49ad849845779ff50fd0e7802cb084b7e477329d88bd70c22c3f031df492d35ceddd67e55986aaf37b1dfd10c4409786839d0bd62de0c4331ea45764a0dc05cbfb5f5527059f22dc63724dca1ae02e41e4a8f64c0658465dde4a133ae58e770686885998058e015ab893f4b2cae04e2c41f0b405e8f406cf76dd0607017616d2c50be41359f19228ddb643f27c1074e4f3d736ab94441f896668265a14f54ace240c181280f409ee675b6910766008d1a88d15fa22319fd9ca3c5616613d4ea36843ca31a2acfc49509de04a11302c9ce05456099ce374c2f4d71fd2a9cc097b480102f5e190507905ca93bec5706b13f6c0467a51ae02de491b1400c14f67c22c046107d099ea2e3b2621a787904cf5920b52479ef66941e95bf37761009c3205f23678d8c46d6642464e6514e0919755d013ae8d30207c5f53e9e9e6c92446524338d434b24a9516f9d8a3b050cd3bc7d91ce977487844731fbb7c37c33da3f640bbf2a7101f7f61a46e817ff0ce7532cb92127ece67accb71983f635ba59fa3be2f8608663b7c1dcb6c345840fe33a86a1ac17b3b233a9ddbc5b6b5dbc1da384ee38eb2f3d57ed8bd9b66f6202916ba633a2f4a7b02acfb06f9a047c262eb0cc1c5e231146d7eab2f3fba29c0627f3926341a42b9939219fb75bcb554f846d8468cf79883bb07fb34f53c474099c73c35b47e54fdc7ce6d1922531eed382051a919c7c5a940cd081709db7e10394ba1adc1d507bd339e07b70a3cb3e36432cdd4ff3de74e324865756bcaa560e9908db6bd03d185a883bf5f753f80aa8c499fe2b503e5ef20ed60989892eed0c160dbdcea133b3f54470a773b2f290cd696ef84d71d267650fdddd992fc86c0c729e221242ebfb65e980cb09f9570c837a8ed07e6fb20fa7af9288a7b5cf6900cc8b69c50b2eaf975b17254525ceba8569f4e7a970757ba3c8de2667b8c7120c70ef891285118a0028bd81411030be6f6915fdc000e3fc268861492ec4648201e0706d4ed3993dcb5bb863c0717c29eaa7e0be520724ba70d0deceafd3e6380e9c661f6373e23cb846658922f855bb276cd8434355eb693e4ca5b58f36d29e07333b9995c7c5d36f701857dd4922363a6214da87e5842831c353b34fb9148a008c3d27e64cdb186f511a8774b7664d54dc2602748ba0d47ef89f0f355ab9330d6c4f9fde0e552b91c6d632e3210891f7544dbaeef1cbe580c6a6d2c01eb8b8084aba68c6175d222d8cb601d15598498747c75edbcda30435bd704deab82ac10c6594e1ee874a00c1d74bba5229ef35b71e5702ee57099f81ab5dfdc65fffb802 true -check_ring_signature d978426023000a18e46a7fea1de5ce88b101951b5c77c55851fd88eca4a7f8fc 3e8105536d561a0b0d0f624cf5d148bcce6093f89601a5fdd7d7189ff21884ae 2 7cc259238dfa4b3d10c9d756f615679581a5d3cb78a2fcacaa18e95586058fb4 7af506dc07b507b79d29936e1a1e7590e8487e5e505187f0621dc390111af72e 2e6bfe68264cfcac935dda52eb71a85f933ad93eecb514615691c97c9147b0098be7ca921fc87255acd77d0e4db9a0c0f525923e02bbd3f7ef46e4380c5f3901bbfb2873800f5403f31f1f955f37a927a2d08d781956091add83c11f6e8da509504f7c23674839d5df464def9d127df6ed48b8c0b634afa02a276a4bc95eb70a false -check_ring_signature a2affc7b38d0bdac368bc43ec4890b7897ae979e06c47f9cb0fee835b80f17b6 941ddf030084ca8c6e7e5f82a8dfba10df42b913848fb94fb84e145f5fad2bb9 1 62008968e26414676ba91c2bb58de00ecae3dca15fda5411f26396b158314c0c d025dda47d55571de30ceb483af6a3cdc82ce3901abf95031e2c79d4b0bf6806a585cc50f7552fa9d21d4c56075f9a384c5a4d4f9ca09f93d4944d12cedc3e02 true -check_ring_signature c907a24644f1f30905f62bfc54c6bc2fdac09a60b7bf5f7fdab86f7ee845cdcb 58a0ed1cba230a59152967a26263d9940f7fea8e2371ac5ca6163a6e65d7be4c 3 d1880852612c340edcad20ac5ed1ef95a6397e6b4ffaaeef8ac7d3962a8f54e2 96e44054f2130f85503f4e660294c3ee11e7d5e895b069bc135ba8359b525ed4 d0b58a291b73eceac3977ad206e1cb1bf1b3a7b0694becb98a6b8700c11f6bbf 5bb8480e32770d09c7f4309c31c9ef563f9fd526bda8aab70781d9980272b807f6fbd299ad1a235f5984ed027f5457f16d8de3b66a1fb0e6a96d11739932990bb765dbbb37669c179633d8de4de20973e57df069c56182e5118510afd8a2bc019d41e8cdf824890698a8726f4c93a380795afd2b5e5c7c3249b9c6fe080d8b0d83769f1fd3b0003d3538ffdb3ea85e680882b2711a13d3056972a03f8dfa080bd7bdd105fec4643f3d149752011002e390cb6a0aad94c33487858fb368f82609 true -check_ring_signature 9f3c100b7178c342631ba324e262e513fe418caff15c78773750658ef106fc80 f14b87673b43e16540d66fb7c021b4700f3bae0a46b9b7124dd2802a4350fd3b 41 ea22a1c2e1fdc3f1a8e1c729853196efeaba58ffa8b86216f69f41f1406386df eeb99474f478324240d09899c27607fb151f8c3d9d351981408268e337085b07 1a157eff031cb14bab69787c6c831e5a0494915193e342677af754f0ce85beff ed9e46f657582991d363adae6c44bad72fde0cc7aeecfd5c12e998a7816f6302 97c10d0ea2eecc6f52e3c2b5c09671d29a9a20e34e801b41b3ac6473c907112d 6c625cd80cc488e55cbefaac76536e0ebf10c5c7e2bcee727951f932e6ae8a1f c2bd4303890aa531fbe9459d1718ce98fbfb2512878cff3682da4ff8726ab302 552bb30775ea3dd6f164300bc0f3aa948873204152f737619f17a9bb18df268d 6ec3c1cae9c2a7428c60ff7bab06452a753f2591f4673bacfc6a1ae2159c3ce8 9623608bb24137da9a99ad243f294b30cd664ab1b08eae8cb28b3c840df404be b6f2d634dd0185f0d7d99c7030c130833a853ba7f2f23ab9f7feb50a71a0486c eaf2ba47497f904b3b6e03a90d7adcc03acf3d0333ac6a0b95a0d4dea8796a26 ca3a3426d6e5e3517a28a6eebd1330471624235f9cedbd57bd61fc311cf6f912 9de8366691ce98095932704a8fbfd232c49468d8352df49d35b2d83addfeb5b5 0082de5c6a825290197a93a20b6a92f2a34344155822ea910cc3ec511a718ddd 83092700eb016a72452026e64790b823b839a185448d3292939be0daddeda4f4 d42a296c4259762652c5e35f85badcf7f0d15c2b55df769302372e3c92a0b0b1 4d1de3855183ad073c3d8074462810c971027fc6495e16522fbde46a44cba9f9 0d5f63d2446fcf4033338475158345bd0f82198d42c3d2df6c44194308c979f6 ea7ce557ee64d243d89201b5872cb0df0388819f9219bf727347b7d55b5d5823 8731e3a4eb3d0253966dc749e93b58f96dcc6e902ae1b3b6b824f62da60a8bbd 8b5c7815155caf9007fd3a334d038732fb280f4ef3aadde2d6cd156f7256ef36 3b84823ec3547d86b09a2e3a48956f60cebbb17b5431e34707f18cd0c3835b61 bc5e9041136091bca4bc4c73b22a9282ecdbb7e003454f88cb5c54b98a26a7a4 eb7e1007e966da4950f5a56983066f6c9f727b3482068800cfa5710140e9a5a0 fcfafb2f277c66973c09c643d927003a565623b2f4f8228cf94ec377a2e5c363 083334f776386c62a9979af91ee9da6ee0473848c0087857583a7887bb0f8054 260fa1cca3e52434a7121643f880427adcb9e7bb9fe69b918cebe2f8c75128a4 7d91c759d81ff6682039c9a23df1b68c4ef5209ac128120688b07acd1f42d2b6 b2582129a084a6441e2b0bb386afc548dc1bf4a56894819b06d26eb45b48f9ec e794fce7e0ed0f344a41f95d47486ef0adf7f213cfd5cd1fb232c000e21c2c9c bfed0e0fe60a78cb76c87d9db29caa0136e156aab4cafeed8324818c5f5d4d57 1620e1b66b898feeddf43d5d929b5e6980f92ecd6c473ee8abf63ce0efdb8d69 869d18ef5019760f0edcb2af4b7303068faf9c870d54e678bf01c50810935470 7bbc678f366ee5e55b1d1335910f9aa0da15f7272e32f60b86d0d507486028c4 aa68ce5c0de747c18535f11d668d55cf52c3f02e69f4cf18168baa66111daf96 762b0b7526c27b2a5033439a2c5a70c5de02768c7a378da50e939964a6a03f91 598d77034407f6720ac1ed6161de5a2a11b953038eaf65d0b3fd4660e8c05f0b 9a1d45f7bc58998aa036b131d4de9fb308e07321583e764daec72595de0cf346 1628431428a5562d3470bdad7df1fcf2a362e354ed8bd28b85772fa57288e875 685c47fd5c1e8d1eac525b95ca44c8e530f3ddb1b7672a41f2b0a72ef4c778c0 ade158a4efd6a12ac334902626367f8ef69e6eb908807a403f06653665e48e0144333aab90067c10fd1620241de910585f58636e73fe4c37554e7dd77fc77c00a4c27b1de47fd0cd3214435e000f4c9dac11a5b711e11c7479fcffedea5e8f0a136967d33c75c47408940d55498d25f736b217e401b7ca0bdbfce945b493f201dba70db86e94863a98d70afee365c088539f56413486415298fa8af518990c04395ae8bc64da4fd32811a696407d3e0a89eb0355acb5dbd2c1900ef29a64fc0d64a0764cda9bce6fb3026d470e6c406bbce4611b07ab0abf8c85375f9825f30e67b70becad5bf35f1a81dd2b45c976c39917a48142e6e413a85a98c17b38fa01e2710f840d78a9fff998f4727a5a6ad60a4c9d4d8f9950cf77cff6308ea2d206d67f59d9f563a3695923ef2a2c1b702b469553e7b2948c079153034119136302029233733278441aeac8ab88fca803e41bc1b46b078324edf8bbf9521caa510376c03d59e703ad4bc4f9823c6a5d9cec36008235cbf5760fcc29112a2a10aa0dc6976289d24fd5c1fe173690b3e6287ad799e893fb605d588701324391191406e6639880af01ff4c5e0c268475887f82ad0ab9958bab45f4be61a5ad145c520ca567e67ca47708ec1270fb98401096e3f6dfb8e2eeab0fd8061fd978443a710bd9c3e139f7abfa0d6b71d312e1d464beaebf74359cda13ab91a9d3a8729b4e0f12277c44b7762283b74217c84ca864b107a0bb6cfcff57bf527a4cfba7e8e50d6428c16820db982554a336f9d3b19792e2a517ab0915b143ec381550b6b75200cda52250132d5c2a5c082f0ceb6d6f1fa026d20ae80730528771991c2cba62010961017c603c6e5fe4035550af0ddf3641503a4606423c768f0f3f55cbca270c392df026cda286cc8c231d8f7b1bc8b1e3d6ade4600537e6a8128e413a670000ab7a61edec6dfa3902c8da1f3bf749ff08b4926f2565c95fb7b71945733e250a06f100264829c5b36cce96ac3e81b0bbae8a587862a38d13ca910a55dce9c90fc9697d4f7472c75a93b579c2438e0c95d74756145e621018f8902ce96d596309d753520653ae4ca61b1be1ce809fbe32fc8dae438907b94ec7b8732ea1cc940993c16f966f47e462d8bc1bf4588680521d4d4f05c46c3da78b17f1a8a3ad58020ba22cbb8952207d700a5a35dff25a092af083267553603c8e229220aa85af07fc21a1f459522741cfbb9328fac23ba73d05e487427943bacb1352105144eb0afa381b4a6bb3738f463bae20cab2bb0f124ec042ec5606e05d4d2d8a614b55001bd5a2d9877a6c6f7efc62ab82953ba232529f8919f2a9cbdbca10fb36a3280d4327e45960fdca77b1161a0cff18b13716b33a9efc5734df7b0cae3ce8958601b673f2f27532e67a54af773de7bb96a14a95c49a9fe8d4778f5594bd278f030b8a7963740a6468d5084348b0236dd91b6cb6453c41831df716e7bc1b919f8a0ecff32f92ce90c514a31a40b78bf314a2148482ea343fecae06d4f3b3d6fcf10ce2cd8c67ebab8655f4866413dcfa181a5b9aa86d1ce5aef5aa669e95136d80025099b85c954d2ae459938747accf28175ac2ef90aae6924b42cf630478fe6f016f49c93eb2d40f40baa9b2126006a701a812e09f0bfbb470757644613867c20100b4e802f74cb21f114abd123169f0f77ea2201e3023e68ae845168b67ba3b01d34959456a9b8fc96ec033c7ace59841effaaf8bbed767056fced6c59e8dd400e429bf2f6eca347231dc174d4515ecab8a74de2a4aa110e8baa5cf9b4418460dcba52177e697cf8b04bf4efdb955caec66ab14457068d78f82bffe6196bb390e49477c9e2b6fc73b63dba01785be9721533624eca4ce1799f0dec887a8854008882f89b842d319d09b9cc33d01ec019df23f55a0680b775fb5a5150f5513ba03e8d80fe0436ce6d03a514250e53ad091cdf5f38a86d8bc1e6383aefaed921700d88caa9e8b64eeea1a2fd1b15141806dceb583331aa2dd24696063ea69154a01552dbe6ab73072e352733723b3e06da0c87def76201ca70e89fdf6cc079f1c09af217d948457f5430c787c328bde0ad939134c2af97ea1538efb6524eeff030e70630d37caead9b9d2a3e874ce11b7a03649f200574cee9c6301cc6ad334650b62ea6155b9f5f2c0db3c1745e3ed9013282b85b1e05ac9c0f809df7369aaa50f415fe648fb2bb76a56ab1aab292b271f4cbdfdbe8142d171e6be99b87c6105076fb7f96df23f282f85d9c5a4977a420b677e58909ba819e44f3e1985d995a002920ee42aa24a9295dca4f181abeb3045c72d910b5a6c97a7d63389f017f2ef0b278740db67e197523ddae46b74781948df93506739ad1419e686bd81b5737d0d1a683651f5e763ba5697f61990b80eeea1100a29daa889825b589a2b2f94b40a5023b48776664c743acb1815ab5f8611ba2f7bec1e5c59ac36698e21a736f90ad85869f97f47c0aaadfd3875f4733af09299e645c70202928dff02afdbe01609a2b53930e7639434f934810ca847d4d6e4295e1046481e9c8c4f4dac7b286c0846d37ad54122fbcc69db61227c60f19a4e96ff722f0a7b655095945ecccad4003e4bee65bca3c3897853968a6fba22422dc01d109fb640fef13e13b3b9dd290fe3b48cdab10f3d9fdc4e2a3c25bbb53f7e58d13b4f3bd7dcedbba76f9e3ec902f95f555c48041c1bc19655474ce0015f2f2f309f7cc5926faa584e9ecf8e1c04a06d9c7036370eb8946c591c710dfabef87c214e4e7ae7479b8472cf02cf1e05748eeeb92ea6331650b052873d08f0c7b093aa16d0bb88b83374a917fbe87005386ed43741549794c55f5017110277140d011f1d8ddd0068f289c1e1a7d4c10da7885c08063ca3fb310f897e37088b7b9478eac3aab1eb570da9597676aa670fd589f7c142a2c31a208df713ca4ccf2ad3d3b68fa0d3f9b5e2d7cd603a41d306e684adc132611a0efddb35b6b58fc0f37b39be6b5a65a3821c4d91401b434e0ccbe7eaf1300aa0a98b09058aeb85fa1d347728b9ba8f8c997cfc8d204f34ff0316818415761277237b977c09d3fd89abff22a7f2938179a85cb901111ec3ec06e2767bb9703f763fd9d5d8fafa9fe2aa765f5f0a8cd3a4b16685dad3eb132807a7a25e8e0953965a9d2d5c7eac3b999721f74d2d4e676f29c18c6395ef46ad052ac32becbb2f1615bd9ede698a50a651708da99ba468962269d7408f88a02f07c891539198e3df49ab85ef3d22a40201eced2268ad7d6902d5dd4cbcc5e61c0692441c34c21961c7c1d2ade9dd6302cd79d8043deb75380ee577651f3f738804ea3da271657a1485f90a2d67d0fe7dfbcedb5fc9abd7665c0246849cf0a87f036e0f50ebf4b792141d61aa5b05553ca0fe8a5ae5a41733317f3a7c1da9c7a80f8e24d878335c8ed97288e3f86f34a312be19bcc182382cf5d4d44b84ec9b2001b89284a594fb6bc24ec6997b2d20dce7cab11649e519d2ae073fd92f2a3f7707d7b03768a70c355cf0d055c0508c61e5525291350205ba495a52e3560ffeff02970167320822096576714895645c2dc2e7153ee7fa19b6c96dd3c0d443693b093c2a8e5adbbedb3c799a3e94d9ede8a30f6df139814da8478ff19bc066efd60f4de1527c6a882ac6f9800e318452ad37243e38bc92fa0e61e6d92ee1ad2f2001 false -check_ring_signature 91c6e6539650dbf1339b84385e7a6115fef07da86ab8ef12c75ebd31b060ff89 a7cdeb0f7d5864652677af743dd3450d351c6ecf11a9c599b98242c14f298481 213 34e986d77959c68b9b47371bc2d3b358d5abce006184748564e779ff0da1df8c ce43ecf0310dc3ceb95ee816f4f5b4b3bd161df00f982dafb53e49693ab62564 890fe8c05c6950c9e43c67940e635f5f1f9d2fd87cb9c0c43094f06f82a1e4a2 3f38bc87dab0615578f271df3499b50163720254e25808936b11c985e1696f66 31b8add7deababd3d042cdcde427acfd770953388280ff7f6d01ce7dec689afd 79a8b10d394ff19c03323294f6deeccdd54da0da1bc05e968f76f13859393071 7f76ee86e34c11449eb994668cffec36a2062d3a4d848988c48b81aa354899ba 8ebb0d6ccfdabb7c279298027640f9afa428400a511683216c29a5bfe1d91aab 3ef0493e6807cf1ae566bdc3fa8b2be67badc7617ba55e36d87bd9be16719d62 b87630e2bb87928fcbd0553bbed6180a56bab597813d04ceb9d20f05027e2437 898cc50015d47c50dd19feac0c4bdd347069d9e9a23c5c1eebcbd8e472d08a95 a405c187eed28f626b641bdeff25c39f842734b18a337a0f230759c005253a76 bec7f59b107eb87f60697e697e129ac9f835ae8f512f787dbbe003cabd7d0183 f74624b429fcbc6046dc0e62d16be7ea77ad442a6df7066589b749f3178a0448 02a50f2e3c706ea8747e4b1eebded246c27545622905227b039268c7529b9a1b 89840033349daade3e6d7363170455e7dbb4bfa05ee4e50b427398e428754c7d c317f8fac48e069eed9b2f5cff5b491d484b8df92c1fad6b0776e7a90c4c82d1 55b49326fa362c0eed59f56acdd1d6cd96b0683a5168344e300d7f430d3752c0 7d3040d4f6e7a100b68a9cc781710ceaa1baff367373a1d0bc69ef6da65996c8 7e8665d620b4bb4dfd05085c4d1b0279c6eb8af2275d1ecaf7e663dcb433bba9 fb0650b8116b9112eb3dbda47cf4538b70f5e503504936d57a994c35acb9784e c8839b63e395eb7f4cfd2c454269576924e8d90ae3283662594196ffa7b99df3 488a4afbad2c28d9bc17a0634a64c415ea60ab6db3c304ac50b1444185983da4 2bcf64befe4c46d86888ec6a2e01fc1ffcf35370e0d6856713c03bcbecab4322 ab092bdb3d26c035c2676de771bbb02c45801f20240eb9dd24452d17022a9d8a 3d0883e8a19061cd0ee9dfb80618e1b5d176085786eca13303d3cb55706061e1 6164b7c7e331b137e3bfac58023e39b5a18aabcaf90a622b70aab681972cc516 ec0a18082ab1370de6dcc63aea2352e6ad8fd613f3f42af5e2f6c4b8206fce2b 206311ea09da21ee292bd221acf54c824895edf71dc7204804f8ac117c49024b 715c09c5d12523264d804b858ecda38ee27ace7244f1478a3cbb5464a451fc25 d5d29379666c4f2bfb3ad916737e0470d7898411eaf60dcb2d859d48ce62c611 689d8556eacaf33050581aea04f6199ee1e4f24f85808502b0c82e99d50fc124 8e5b0fae66ed83779e4b79fff0a218d5967dcc483fb273e9ac005ec4651a73a4 60687fab1d18389f22a1154d448dfd8d52533401faf6ff0319290b2edcd8eeb9 c42ef98fe3cf3337a270dcdea9fde6d93d98ce7e08ea4b3775f9c09d0dafd1ff e1720a4bb31aa3bafe2e7308c1e265abd20c1f8376adbd4d4da4255bd8b7ca18 fff4c2bcaf52673dc7dba8a5ad59a08c95166243a72ee5dac33ff496df62a20c a12385b88790ede4cf696a072a3400078d92de1eb8efaf33e69ebdeb9ec6e3e3 544949797a87eac97181cae70270e235005bbb7c48388b573b0f6e8d381a52bc de84f0e0aa73993e4eb9e709d0a8f4c6f89d8294ab240268be0e8f01e804eb7e 33ab88403fd8e471a6cb809998334d8507b1966f2cee0a2cc61808d2d63f80a7 180787f77a4e87ac2999934e613c060539af81b08bae4a92f8331c09b4c77b92 4e7861750ddf9cd8f9100bf94de8f8e8b5e7dfc3a5352bbe0cac8154570327b9 a803e80f8ec38f8eb5d443cf8322f0002f423fde7f82534bdb25aa428a6b1c49 869c3e49a8b747dc0a930ff6b2793f1a4d694701b7db39f40a75de4d6b7d0fa8 26454b91bcf83ddc7ca6155cd086e42145681e2c9ae179a58bfb6b6f2308916d 120ecd3ad7db97092a4631dfe1d3ef2e8214d75e9c76e358e4afd2da5eb510e4 c87fcbc8c52acc66efca1877ec539096065bc83c5aecdab5ec61d8ed3c5226fd c41801ad74b47391541e01f9df8ccef7f889e4bb330561c07471abc9c535ff5c 19f530730d87be8bdb0bef901f056efba0c741a4b7516904a3fe0cf0a6e3e858 172a9e1af91e050d0baa376098f0c03b43bb31faec46d6c351f7b87c337ce808 d81e0afef679a67fc11166d069b978571d4e5c4a477e3623f312096b38ea7197 b52ffeb78c1c1c97b465cdeef5ea7f1c760a1892b5749ab94e628e43a3850ebf 85b05a3857ca995e63fd8acb6a8796d39d567d9136acd80dae4ea5830ceff5aa 2f7c9e105d2942152342efdf3a8c9a8ccdc2cc2dc56811cf6a7024a5c9ffc634 bba76dbdb3b2fe970f02f4f3b054d0b6cec19e30e29c8a627fb146ecd11da08d 719617ed13af90b7421bc9827c288b4c8676fdb17d2b11c332760e401f438312 196ee89f2129c74e5375ef2dd218b7ca4d364f5a6fe94ef7d477b345bd982cd1 564d9a5fd835361fe302d80d41daf6be5740dc10a25528a9ddf664d6ebe74234 15ec5d61ef0d00c9f8acd1fb1f3cd95ba317485b475e69a7f352480c103ecc72 844989979e7be24807d97fd60d9967eea07add02ef6140df6d598d608b1ea042 ecf4eb609b97ef4c8c596b01b52d7add09cb3c31edd875eae39ed1058b3b0a3b 5afb00793ca580fec57a0a230a6ac60046baf94d83e48e950ab75b5901d93d5d b66912262c7d42fc777c947b4b797bad912bca073bfccf90334016342b183148 86734aae67782b54c7f3b0c7cabd25df4141ca0c3c12d5626ecd3cc4dcb54be0 752e93a149039c58eac3ff4e84d1daf439f9c54a3984590d47a3a3e5ec147f77 f132719a087b534118bb7ebb3e7e2560571aa7fd8d55c0fb0dc711bb98ec4f66 9928b5f32bd682fc8df0e9e873c261ea3c34aedddfd693b4b9bad72814fcc6e1 256e13b3ea8e020df465ad0b4334fbe36bc6f158e8a4c9ea6cde79cfeea7a963 faf7c2c4fd18bf1389c8145f5eb16ed2c99c006997491450ac801be9fbeea032 eb7042885e554198ff2aa47e935e02528867fa5c3ff0c60084589c1b0de5db5e d53ef4bd2340f64798b65d95dbd7b7539a21550dbf0d49c66619f708a12bc3e6 d92fd78895ed2d9d6b376ba14548af89659536a4bc81c5c414e5bdc6aa987680 851754dacdeea213274dfa061c31b1d8fb925e6f45a16b2e5336fe5d4ed095c1 a236ba657a6be4ba872e05d422f77aad17f85afd74bc6b6681b857adb4aba4b1 56b9dde7e521b14f110f868b80eda805fc92c2ff58ea4d694f63b7b1db66788d c26ca3d93d34c2ce19bc03206b674bb0ec277b03481b6bd2f6d4c8cdc5400aeb 3805a75e3d1acb904fd1c47a697483d000d016d8c3bf56b02797b1652514bd89 078bda77a8e3b1c7caea0690e0b3f55cf05eadfa4fba5da3ff0c0910fe514475 fb56e79864f8ecfefb2ee5830a828c01fd563ad0ad5edd6f391048abd0bc1276 04cbc96eb4d359210d8199d124ddc52e6b74c1a619d0c1d063c12613d59b7521 1d52268cb6d9ba9cbac6f6c6ae07e08d3e5cc1c350b48a8ab5ec4d935fb2d531 f30698c6f8f477c707d7fc4226cf5b3d4e883bb93ff2f226955aea1570e07c49 3b1aa97bd92de4eb76d715a486153e7772195ad3c613fcde4e531214e2e27d2a 5d3431e2480821a0e42ce532f0847a669b083a322d12c16b053ce534f2053565 77317d5939ff631a25476e5758178b03d9076a91be58c6185779ba64d12e6057 da082a14008e1ea1b18ee431842746eef76c83698008a5d01b345b09d221d78c 2da601f934dc94eba7f16f1a8401db1dbd0e7d86c8564d7930d7e79731e1943e 11a3c885c61db45da1d635fb0bdf4eed36292be25d76a4f33114b7e7b06a2b4e d9830c7546dd2ea96c6eb9da39fb9b87e8373c75abdf468d72af5ad2b0888f46 336c1030f13c8f64f1404b6576bd7de30d757c39e6d287c86bfb758004dd3279 3211d2770d1c459384d98cfcc2cd06e71790ac9ece4c27a90472ec95bc92517f c59d40958a3586bd39846eee9cb3b4c6e63d31048d329fd8ad25429607bc66ae 7dd3e9d787195a015261bdec28dd103504d7b8432189eb3ed1ccddcca47f7c57 861567ce9e932b7f71e5b17860b07b197666018960b92aa12c292409d72bed4d 45575ab63746296c5a318336b4fff222a090e74baca47d8d2235fee69b63b48f a550e636972038437d58d660476689e843a6aa561d259cbd9d82b52c180734de 2960e2a30a4ccf407d1fbea51217407db089faf4087418171cf79ae65d64b713 528dfda5c1b98080e8fd2f912f30b7720c52ab680ce3757c32704b511a022167 a744a0d92584218b5e927a33b8e0909f0464c8169a9760030eec5ed97c1a7ebe bca0fc9680b54a3cebd9ce62e79b9cc886e0b983725f71e9666a4b71f1f8a0d9 feae1ba266c4fca81075c37df2810e7cef6a6aa27391016a3c6ea910dcc26172 039f053497464d964cfd83897896a4ac9fcf75ad4773357f6fb1879c93252b61 3bd5778910009d03ad855cc01dd92431b5dfb01c0c3e5722edd34c88b271edb4 ff0901f32931467d6ba42b07cd9155e5f40df99260f04718ad3aac693a5d7965 44228dd14649f6471b8b6925a919d11306f26ba24a8e2cfd6536b051e7a1707f 9c714457259fa5ad18b5ecca5327b082d99a138ca22a80f14b6e3688067c62ef c9f000d42f1907f2cecac873edcc2ae8c85f20c88dc5dc6ff415a8517a7e7538 300ea29e48ec3e87c4624f648fd4d0a79246a21161583622f2b74286715e15ba c3b1ac285b35ba3bd10cdc3e6cce747fd2d18f10f80f3d8e66c12f3fc13e9d10 97fb4052b1df4532c220239a2d945993189ed6b24d73059e5684733cfffdda4b 66ea9f9b3fa47c65c9860a00bab242cf33c9b4728975e6e851c30521e21d2ccc 62bde8539a5a7c2ba322430d270540e1e5b02ff5d6eb3d1bc112440557d3cfe4 488128499ed67275941c37ee1f84aec3281c0214a93d7681be71775b04ad6aad 543a4f4605e3056472f99553d3fcacbf4f17b7950e4018ead97c5a31b324d053 43667e33a70c87322bab49653805f5774cb3723dbe38c1bc988ba33425095dc8 9067acd7b0ee821e76b11bad7d66f8a4afad461c47fa78e47e1624ee5fbed87d b8448bc34972d670a1d8857a59aa7f90a6349a6c0397f9a3a296295958839c06 3debf6b475a8467e07608ef05207026bd374f15a2ddf7fea1dc93709bffc75c7 d19fc2ba42fe87c2238caa515d1297ee9e0c55e95c5bf38b3324efd73c55a2b2 8e66921bddb9b7faf7fbd0f1463dfc6e7c3a20469e11419487db99c516a0b964 3d9fe18e95fb9c58236963dee32b088f56bfe1c66718c19ba2393b19b970b807 af3cd440efe07cc99d55cc4273436da39567dd30132871a5212eaa0da79e7edb de123bdb95e3e6ce3b01b38f38da17a3a10faf1bf46d2df0499d4f2e5e6298b5 2557decc2aff38c54d91288c36be8ec78f941b832645f2e5beba13e2d194b9e6 c42cffca0b7f74527464ae2868206f70c46fb6d145445182dc31254f4d7b5ac0 972a7f39947d4b7f9eb6d4ed3ba8e56a8e12f4f4d163872b1b77eac6a1f5a16f 6a528abdad05639f228295f4a5a7da3c4ab0a16dc8cc3b17e1b4025cbb2fc7cd d4ec84b00dfcd49ddff6f660b94ab0aa9d86b415a73676ca8408112ab8e5f9ba 114920e19d56d605c1ffa838ac15c7c39bf2f3d2a3f5c8ba12803a82d6c5bbe2 ecd13a64136df28dad65de1c07347c95a27b2dcb88b1bc3946dee47bc0364992 a80a99b706a56e93916bea5305abc4b4e4a335a5b7364cb86d2ed5145c697a19 c885e3be51e519e940b54a27abe66efd6d9e91e1b6eebdfe3149b136f000e183 1b2a2a8fdeb294c2139a997377a1ebc6bb2d3ae2df77bf437f4cede66ed2dfb3 14d8e828006c82139c9cd0fb4d8514e2c032cffc002ac6660c78023bde58c248 64bf32bb6c0b230dc5b60d40ecba7d4a002342d54b078194675c7e97abb23a04 f4e15842c1b021bb7d8de5f6eb19c886cf2892b0a65c190029e3b59c4a385b8e 295dfe7419f1af9a050333f2f33335b0c5dc788a7e3c62f3ab6be4d5a4276bc2 934d8d450daf60fafee19723fee3fe39f68128dd2a223541800919eb5129bc14 3ce9cc408b3e5fa1be33f81c4ed02e7b382e51d06cbaa7b471fe062673b1d37e 82f4e5a1923302b60ef5096a876b7ad56d59cd801fb9bedee0828bce68b78159 3fb3ae690f1018e87dcf922f39b2562f4050af4c480be441105cdcbcd2716acc b2f7ca1dcc5ad5169bb43235598ab360308c97b8fe65c9e32c834a72ae2eb9b4 4798074a51e05574bc6aab1a8d39a6ed696f87138f5769a9d6f42256a5ee4f4b 3364e1d7c9db162c17e83d164da90dc97c76719f35b6fe5bf40cae966c21d1fe 606afa6039fd08cd9304632b0dfa8714bc614ffb3129325938a5e8d10d8838df 9d652bd9e4350a67a077763bf4b1f16003cc60cec38a8a4d3e29bb6738adf50b 6b676fb7a8599952bcb0b7e6dc5d58b4050d1126ce2af3da5efb945f1cfd2c94 667b57119caa83119272dfddb503e5f5f02d7de8372aa7de71111a74ef5e2e60 95e5671e113ebabeadafe6b76273be1c0befbf6873736f32b0c2ba06c6c2923e f0d10bc494bc90521135eb5aa0e5a3d5b3a8dbade4e9f6ff3f2b2068f3740a2b 7fd8a918da0ddd5c0a6996a884221d3a87bebf87180e42ace95eb0b08aedc169 14acba58a7463f1183c958d8585837d51a17faa0e9c3e10fe47b81f457dfd18c 1574433739dd481dc2566766370833324d088da0d366f44deadb93a91ef67517 91507082a1acb56a11f6a47584209b3347f391590b505a3b6ea4f633567c108d 55f8da9252ff9b89ec3ea357b1699a79cbb457c0f788d53f24256b042f72d334 d4c333434acb242dd0c02e6d2d0256d700fdf21ea3e369235dfdce8f5f01eda1 26fefab2cc676e27474112530a01bfe66ac26b0fd1ae9b0b1af158383579fb91 66e3754d1b9a3753641fe8d713534d17e3757b93af8a61639619b4bb1bf35d8c 5bf2ec44259fb6a86f7542e24831d92aad28ef9196725ea65081b99b5ade6c96 9666b2e7df6f11b6d667d699ae879b1f6515cb65be5d2c421f7e0e006c964580 85d50aabeb1eb771c6acebc65df434ba538060e5ca7510666012b8be339191ae 86bd9dbdc18ad822624ff81fee9f8aa4e2a67cbbe6baaa776362c09f5345c53e 25e5d2312112fad46920e2d58ea6268fca18c380382b5021df8358ac0b4b6339 0c6c46efeee83be0e06c250a4f855b8031c3b87337ee9b35c2ebfb1464ef4e93 e5c7e48339a212e064708751ebb2610b92a90da555361c12503c1ba2b94f773b c532bbfa5ac8debb63e4f829ae4425d47290b25e9f8e535327a6896cd8ed8c11 a174154871e9032c25a20e755763ea0b7d30d48d4a742a451667862059c1ae00 7fa286cd3e004de47b64321267e483db374447e61f0d744cb135f87b05d4acca 1e68cec5a91a996971e96bda295445c6ac4c2a45676fdfa0b7656e3de5613a8c 258bd5a7f86ccd162bced20518d6801c078922e0a07331fff0410a6b03b42fdb 8e9a21ce9025eea1548a80a65457619fc1f754fbb61ef38fc1a6632ac3e6aa44 104295ac862ff8d59726746374deb516cd6ca8dbfac1743316ab839559294922 14bf9587ab5574183d825eaa6517c2fd10d2aaa754195d4b8f194f862bd1df96 f01b0853a90b32c3687e44857c8424532072100e8f5dc3a446369eb333b96966 da5ed5761e8203f3a3710dcf8141a3504ae78bea0ccc10fcb8af9712760bdf4b 86e442f6de922262310a0c5c6e66ad12a81e9886d9c2d44c6a769bb38ea6c542 c93c54d5737677dfc366cb4f0cba5b896f181412701bc06c38f854b9e72da88d c8a8056feb3e7e740b16cd36ca3389b76c791cebc0f8c89a07629c3559b3b1c7 624bbf542b00e979c47b0a5d42ee218762e74b69bf6a87127a7fef56891d4e29 ca098955eb1c82695eef2e33fe27abd54363a741f79a326a18624a59fae461f2 c35f574285183daf962f7ead5376e6b29e4f075d446b7c24144274eba52df0ba a261e3db11ee1b980c2410ebff269d73bc917ec2f488ffe68abde46ad244ba48 b06418693cd448500bbcec2009e508a81582aea9aaf4b43e1b8432bea8958c6b 14c3ab44093d18929e2d9f8c8a69c3ae0eeeea3560e16fe6bb2ecb9787a542ac 7105569d9955001b4b555e7f260909b460aac6b7d8766eb9e5eac970504a8bcd 7569bfab44b5fd6e4be0daacd0f6408eb71bffb223b5b2cb3fc8094e4d9d934f 1d37a3767f5aef69c068cf1e6b770c82a2f757b1a0231c25f2f855937ef5c616 20cdfc81ece21a0736e53b7a114a2b8d245355436dec806e07c027ef5e2a3e00 9daa4cd2c338e3c36b651c9820ba4d932b058b29184c4ba2f3dbd599a4befaa7 6f94000a6d3ca7e59ff1c0d654c394b006a80b9a940907875bdc5ebe4874da93 7d1438cc49457d22b33af5c8a253364864a053bf75c1658861552b3e407bb846 3622f3de3ef055ec9ca91129d4a19580880e4dc528120d0306e3c1b944ea5d14 340425c36a9f3a07f09ca5e5db80be53d1dfa72370542fac9470be5926e7be61 5b4138ba4f1bbd297b882834bc0d438888b93c11eaf7b273a8b0a665b51e745b 3932ff0fc14cae57a354bb438908c127902f5699e9331257afe27b20a5ee917c 700de789c2ac3265e395713eb5a84b8a50d614c1faac713b6f039a4c128d3b46 0f4607e43bb366b8eb649b084982c7b74a7003d8d01ace55c6f0ae5b141b5b68 9bebfb7e19d63b6b2214582e67e50b88011e52a2c67dc1bfbaba98484ee0043f 85c70ebd5862a5e0c764b80c6ef0fc164db266508006a21fc8c2b056f2d4f568 d86915c321a29d6999cbbc58181bec05b1f08add80ec1141b96a951c696bd5ad 5f09537bb43a919e908cf3e0c2da6a9bff5444fd5eb9b61cc3c243df8c52f26f cb010236cf55484a94cf7110e9e50e3a70e6e44a781b7ebba754b4abf4f67c7e 5b4d1cca3cfbbd257d636de3779129da239f270cfbce4740774524ebd76cdf34 4630b49c3274a059485c01a93c535c957b7acf28395a5699013a81222ef4a6d6 598f624607edf7d34e2f95ebd08c36f7d82a441011e9aa57a0ed16eb1d2e166f 6039008f442ba4ed4fc015d360b13c16e1ab4d779d9bc50c1717c0ca6ddf8a4c b53d5340896cf020142ffa7958bc3956074cf1c13b388d467953b58bd4b85f59 0fe7b691e8e1aded6922ac70166d221b2981e1dec67affca36bc4a9daa912cf0 e4fe1b0138085f27bb385820733072dd9d1e72a28a1690dd7a924ec47247d25f 1271e12651a36cc677e246a8a3db9acc4297e338cf931d7cd15ec2a8e999d930 319514a32fcb94e24e77e46828af845c061fa0c6da698baa6f8ceb14519ab1b2 d78f4dc2ee3f6cadd9d3a7eea8d13e7cd3833dba695754b1ac7d6fc2d71dcd7e 67a66b46f9253701aa8ffede0b0f5d28f42835f448b40242d2a63eef2cd513041f05c706c25e0de30c0597db02f3e1ab4080a1226767b27ea03d5294b563bc07db16ae5e8365131ab88f9ca3fc8208d6848965dd074ddbf204e4fca28db7140e4a0028975f3e0eebcfb5480bf4ebf96baab8aa88c1fa88f509ce892080d3030cf3df4929b94ab517988c57757f9479707583eaeb5bf5f249b2d96a00e4cf840a68c9d418541d5223376b8a7145668728ca27b1d38fd5668cfdbf9cf14159fe0a037b8d5ce3ec2059808a40d0f655804ee75a4b3071d490923ed6979c0687c30b6167903fda10733be7ab86dd95b5d8e2a98923ab6eff850403432a5dfbf6c40d26ba0af144cf00fa924cacc982a349065ba48d877bc90771c7d75aa5f6f13b0c19e0f333b346778e24897bb987ba2031a550719efed58dfeeb9b3558745435038a3f6ddbad3babd189e1ecdf79864252aeb091413f88349346bd1e2e1b0885033b9d4ad87bc0d0c711a98d474dc1ab5a28e29cb3a926b71fa3d4efc1f5b0fb0fce08410c55c37756c1ed8b58750091a57caca759e8dc158c8394b486a4069a0c5922b6b7d73f9b23d2174e2198df670cb1ee94ab224519fa317fe7f0414ea6083a9855c294ce02f1a99b1e1415816efcecb9d566e161622db6d5f8dea2bd680ee47e8431ecb10c3c2d55f7bf35b5deaf956040c5372b8ecbf53a551ce3b222094b628c9b2167b7320f6bc86ebd49da37515243357d3dcf68c2019f37f74b660c69d9a5f55c3c6965d478188ebea9af6782cc791318419bce55e43477a32384045613f93c997e8810cc91bb6e2423007ffb1ac05bfb9d196ed57eb2a1ee0c3206822dfb0a167b3c622991a08e5ccdc5519cd4813eb94e3ca7e739c6afb4b0200a546fc2ee00044019736e60e518dd119a9e9c08951bb2b3b28c6bbae0f9d7f90b6530ede4fcf500a4e4fefa05e65c6654d5bc5999dbccea8990a7423d2977f700f81e916836f91ea46e0848ba37ee7a1ab43a0b4e601bb3e1d001e64d1ca62d0f8696ead7b211d6e850403d396dee49428cc0e3593d646085d41bc9c27b1e170fdb57443babba51d76f4fdf5f61b047d3f18367c64aacdc8bb04c1afafbf77404a1deeaacda5431d77422ef5d8eedc45c3a45d6c895c24f061286f5fd6cffff0e6e29b109a986ce9f4e67f2bf3b27bdae15772bacb59cff60903b84ef5ad5290195b85b441388c366cc1a63600c0cd30e2eb6c0b7393e8eb4996538a30be1ac05058a5acb72d3eea4f03e08424859d9e8835895dc6ff40b0f727544bbe17516095564e4b89cdb5e3295e81dd3c6174b50250dab94d1a0d0c9dd30183012c47f0f390e6e44baabe5d473f20319a74f41e54e56fa6ea7f3e5e8daa4c196f105d104f028da9f85e3d5be00aec58e8eb352aad252520945321c4203abea7e55f9220d4ae310a2f5dcb53dadd6c0ed8cc7cfaf07a9506f44617155c58d05c8175be1042e33de24ad9a1be39dfc0aa1dd48dcd167b61acada82649f7908c4a47246b607000d25cc4d9819c4d5f65a3d1520e2b0b0c48ded675e8fa57f252d82399e100c7dd2e511a035d5450ec5e3744e170d2871ddfabb24130630037d704bbe10620cd121d4c7b24efb9533ac546eae70569fa6db3ae5fea7c2f4d22baa102548a30782b8159396201e7cf76b4a3951c2e36a1bd5a27349c3f77ad7f2dd35529f71067874cc79b510207b6abf679abb86b0d1136836a51aacf8d9f2d946043085b70e8424accec60d634e2cc7bc722929d8ecc438aca3a029d51f4b270be76f52cd0539b9ce1ff8dec35a54b5d9a42c493ffae755631538becfb779d17893e5be91068867e204951cdfa5a37aa44ee10172174b1580abfd29c4b4c86a2732cb301906644f5b2975dccf1fe6e3d8a9d579b3784a6cc3bf212c29be57aa92fbdf04f50a280774fae41a086fabc5b67ffe6dee57e240b411a3f074383efe30bd63d8c4002120639fc145a2ff9eb6f4af426a9a8d069aba3292782d6e418169ed96565d0df2392c719d5e83bd59e9e4ae8cf5c4c1f379e39a15b46e7dbaaef14d29648b0f43947edbd86377eb69fc2919a680eb59e6bfa0e0b4958f0df5c230ac638ba4084d01d10b6ba75c2ef643d0e93c17e07393c63e60eef8b626e923746cd159f30cd9ccd9aec98c61ff1c051421d5a7565813cf95e77cf915cc9f21339a66c72d09c59bb4bb0be0fe7ada70b6b982587ac7bb3f1e1a04b4c3ec944a688ec4873a0bef99c8bb09c798c113c58c1272fdcb9ba569bd2b64bdba2b8f29bd768a2b9a076e64da91e3fca25b83a86187feb7cf78f803c9c49c20d88d8716da7590fea807dceac7bf1f5eb5e149d2f04e3c39ce45dc199c536a1b0719494f83bcf7ff8905df5fdf348f4b4ce58a9acdba151d4d1d0dd2214036a2ef1c56692c7c8a19c20b73dd1a864b51bb4ad10b5d157066cde65485d83e2b7ff71138280c557aed020569e465693bfb3e2c44a863dbdd57a111ecee8840fe248280c4f6feb6cf9a7b056d0de2f393fda5bc6aae47bb9060422715acf133b27e5c779fed66cce71166075e430ba85576dcfa412f2a785448152c5cad8ab5fca0b55486abda27055cad0215bfc74cc0260d6618d131215af1d8905b1b78e89b9902484927d157156ebb02941742eac3f88ca03a3178df5fb40ffb9bb5d9f99c1e4ab6dd4d0c01e6d8ea085aa28dc0b8e9fb94970ce19c9529988297f083687b60df7939484558cbb7770f99b175022be768f23a7f753bf345cae2042cfb21c9d2f1ff149940578c43c80f5cf97a1c45975123f243173b2e74dcc08b114161085196e4d5a9c0732943560a819562f1d04607f2829a451956f7465edd750ffbaa67827f413a6e999fa4bd0ccb83ab89edecdd7605ab695211313b5e336aa240e649b7545465c6425970b40491f413f513533169617fab9c320f950919475936b1a1af7030ba4c6f498ddf02c64accc2e0bd42059fbb1d86c25d649d4bf419bb8578c7c0cdc60452a7c71f0e68455e1e0e830a09a47fe5fdda77733c2136ca4d288db332abd55b5a7c8d800055ca9234f1bf09895018b3785c610ccf0062d43812c85856b0835513478cf40422d53f89c765f16e5b778f7b3778e193d05c70b93f00aa9137c9063b5f904004d4d71268fbdec28f75beed14e91e55f22ac97c517c7243d075ecea51c84d8c04859b2953f06b05244e69c805b795c2d23fc3b3cabb2c510945b7286143dbdc0cf7668068422548afa63b8f7d3c4253f8a51515c7f52688f2e7e51880519efe0f434028161aba5748ac3d43f3e7e908c5455e840c3c95a67f7d36bf9bfafe4e03eddf5910d5a7c776d32bbb17708fb3835e48b79f18a85e4ecad12cd5c1e0ee03fc7986246c5dbef07b734a7716bb204e0f883d8c896fc74603ba3a5c4876c4047719a04bd9c0ca8b6cc7c37f40660717eff770f3a5722b329fdc2a151be2610a06992f5823548a434c1930eeb3a4b6e36bc9fd425a385f61a78b76865ecb2b085f4d0b29b22fadf716c8d2553ebe188273c4902184480ddbc4d4a1e119678c05065cf7502aa261889fb8962b448abbe97d54af2b47ff738a8a457be38601dd031547c373821ed8bfabdcc9a37853a88fd026c55173d3c6fe12a1c7e69d67430b5d79d428a559c9824f93ce36b82570709eaad0a254f9f70475a4c07635d953001ed40224c30fa7fd5fab6fcd050c5a34372d8f0210aa9e390f1ee5f471582507507f0dd7ee76b5249e73e7d734709500b0051a45a6fcf4a33d535d4a431254056e07f2929a06979e6f289578e1648c0466067ddbee7f743f80eacaba096b370b05faea59e7f5386d2d9928cc5efc69c1eae09ba9a7c6c23a00050e19af4bd40744f908a29fd919079576e4bec77464b118a75153b95ab164d1cdd7a2c961e30e0a36e63edde2936dcca63df039b3b8189e9d4b50798666ec085b741f0f656a0a25e09f27b81a2ed909934c2ce415ec71e29c08be38bc5d2c53d2656feeebca0801847c72ec585877171463cde0e5086a1126124a3bcc4bb2c560a04a92cf8a0d65f3862bb04021bfd64a55825f5839d8f5f204ec543fea81fd51074cbea02906dd8dc2f6dbc2740eca162f424bd96f7fb9e62be24d2fa7a4d07083dee11e770d637080d60bd9fdb9bcb47439ea5106e7eff89975e9213dde783c4bf6d6eeff0e734dd6a4796e9118f00cd09f8becfec3c02108120341a561cb66ddbca8a0e50ca32b0824d11d6b0120ad8b776ba980f3787763e1ee8c22592274c6fdcc7c410bba5c52f42feb0f0c648e47ec9416f9acc4070227a965aa8065bee3ad0e757d06bcc5edf031b69ca87d97c2ee2d2191cb9ec693e029408ce8f9c7504cac2255065c4e13400465b070eebc80130d2016c3e68f67faa238db833c3745337c588e00b6e29a6c3292ce8cb725671f78f4ca1ce8d14a208590d4ecdc2be66cea4c020d0251adf06557cd7d902ff10294900b168ee32de82fc90a012e42bc4626950a0ca7f8f776163753a275aa1ab28097f405d92714c453a0e833f930f76ab2ffb1081d31aeb090f51768f7b316ecdb5e0928cbf10259617980c16be0c59c50f7ef00d2dec56afd802e249ed2cf06511d7e057de9e8e0d0156531b12bdfed4ac6790bce390b278b25f7029deb7a06c2a307cd3e2a418c0a22ed2794de9ed737767c0dbd282263cc2645ef06aeb880f51a8e4776a37e3b02639cc27c8ee02300f8a2005e66fc634f5d78792dd4acfc6fb7f64755589f5b9312e2498f83a6be68e4630079b8a6df03d87c02fbc64f049bd600fdcbed79be5a9753e017337d5d6210f401bff17e4782d677669e755a57ea1e6612857a93ed524eb4afe9e95ef02e9f7308e2c41ffe810e52fe34d0002e7b2b29902ffdf238307eae6a2cb39a18d14d48061929ff9dc3c83e71c5385a5efe3bd96f0c8b1e13ea0414c752863bff02e0160e4cbddecf898bd636436d247846d911b98c9c69e24d13f39e373471ace8a20e0c83a28686774162093a78eb41987481e81752ef5155d5e609315d6fc6bacba7002b26681141a33aca8618f6d5b836a0dd9e8676685d2f260c20adb692b51bfc0b7f6b7a89005625c6a4a9cc0c24dae367a0324130e50ab2140169e8386936390b79e8b1433830afc36bae1cbd271e7ddde50384a2725f81c914ce9e1ef5816e013ca022fd52ab95da7127c42bba99f01b4f0dc4f418cd27438419f1e87497e70967d77f9609481dd8ca8fd8dc35fbe1f36e545655f0210d3e560fb097606c9d04fc02505d6055d0ba0762c6139a575894f3f253994d762a71000ba4f763aea2005bf5729fd5b5c336216da5ab7ca92b05d541c67ddd1b2a1cde6c0c715593020cc0e0e9045c416d13173dfe07b25aca65ace665aa0da99b16c5ab64affd90b1012866a7afed7c44670773cb1f2a9295ba0131f675124088cedb82b13d5a70200f8c96c54eb1a058e88d0bb70e5a050fb9dac2a7f15d325e7169d6fa50ecdc22030f7a42648adc1857aa1c3b8915549516ac22c96a1898be90df4c32adf493300a76303641be6d5b7103c279854102a95a06bb28116b70f38cd96f3420094476080d638497cdc12004f24dbc9427aae94776c2989f9ea304bd005bd43c1279cc017c7786e491792795ac9f89d4b8bf28ea076f4a5f4a2be3b139be8074518415069ee603fed930ac2e02540ac361dc109b218629f297c60a9e586bcc3bb144b40776f0660e96dd3fe59808a0b0c62307df3c95be9cfedba1c3dc3a7c8705d5ca03251d43006d0d6d67eb7356a1031ba9ee4004e989620b1d39aef71c8593ef7e0bed37f0ab2662cb77a7e5f3b6b51e01cc2492e6c251ef9ca3fd252dcaf6a6ed07544683cafbbd1a601bfdcc25db778c292bc8a2b7068dad5cacb7600e2ddd4c00a9478fdc42cf83c63f10fac6686e156b60d5ef8e303900d1cca1dd34de55400f46735787668d5aabe394533014c6c324b80c38e24b5915c846c033bd494e5f061d58469a66363f9367f8a5709a4b6c6acbbea4338fef046bd4ef5f8e140c11f23257e26f6e4b897444bbb956197ce50ff10cf1ba0bbcda1d7732263a37cb98034f2225e92f048a454bb2982c86922402c49cec92d9be239c592238eac55f600a7aa5917973024c4fa0a782c97b4c66ea63e16b51f23d39732321cfef84191f0b883f624c4a13f838d0d6321ad4a4be1af69547c0e01cb28efc05bac616cf8204832ba5e75e543935c02a0b5e5dbf9698b6f8c4226a9cc22b31b8d6df1c504d02b47c787c27f40647679b172edcfb3347590aff3ab734764e3cef493b68b28c046ce30c7a799bbf7ad670b13a1c9345bd9ef61f8c92c354fd8e73ff42bca98b0b2a0459e7118e868dad466d70459c33704c2ab35677ffa7217c84147eeec68f068569ecc92f0b1a963e8e6ebfa713f34fcfbf166d8c39dae911e4a8f607f2d0053dec21812e8be737baaee071433d59737638e5e9cb0999bdf5e3daa1f379d7084d2b219d8f9931ce9f63e87937e0dd44d5072ee98b4fae164ef0fe8219d691056fcec624fcaa628a88b9cbbab8fd95f580d3c5a02f8449ee8606bd44f4a2ca064307f6ff1984cf6bfb993c18d4dff8a46ac9d863dc88d45f269b5ba68766640b067270549f70f50e27386dc92c3bc892b1a3d7be8a7918dd1ca7ad6c9d36d40edf9757d0374dc982304c122fb35b82df09b42ca2addd76f7ae6ffa2085a56004e412a772162551207002e6c9a1c0dd51b24b430a867edc3027724c634ac72c0ff416156da12191e3d7c54a32044ba2eddc8e40376ec37e2fc568feb068c33a035d67f4db0e4db0834905e589f4d7eb0cacba0de21032e0f1d66d0f00a267e80a5175c939ae56e33cde5c5d3559b8bcb7d5ff8b7284879ad3dbf2a8e81253ee0d1e24d620cc5870fede34ef47c3ae6d761acdcccd3f67bb85584a2b1d8d87db0daff1330b41a5bdc8eba081c75360583a8942de6750ab0292b21d65a00943cc02ef737fdf3ed93d8a5356d7e98b1bbc1a7f13af30fbba32728a4fb159e49a0b0be1f9a2b5734129ae0e860fd14e10f71b910eedccee2cf16c42ae214131c8bd056b37b27f49ac4cacf9ff71676fba9eadb16ac3c0353b016675250f1189288d01e6f22068c9a534b14a605aee26a837f59810433a172be4cf25b392cd8114b40af155c96cb7f96b3af6ea93d4e0fa757bf21030a3a94f82eeda910e94f5a74e04f4f327ab4531b1200f60e3cf42e8300b9115904a8943846e58e7c0067d6c0806001a8331019e2877a5e10481d87be8574aa25922bcc39eafe90a34b29181b00213a6c000fe58b30aa022f75ee2413f22be6ef39fde607c7e1c6ad7af1db47e04ab0abedb74a3eeafc897f3507e2a42a05965670aefa5498a8a10efd46cfda20907f2683b93174f9d6173085a76bd15776ce22c50c2088764f58dee00503e630f640168453ce488293bf8b77c6f2a6b9d0c4a982c69168a07860551e997bd540dea8c824530625fa8f2af4dded19177cfcbe0c68c59aafe8900872e28e993f80138b70ee2e896eb7a253a3f208fe64a6c6f06a99decabd14f77f92a0dcee2de05bbbdc4a8b604faf07612f4dd40d2bc9430403802d5d6250f2e52350c59385e0ac927a89c57bdc5f1b1dd7ae9f55b0255bbc9830f3fb5b3af50bdf37de9828d017fd91a21112a74bc736facbf345cc844f397923cc7efdb5b11816c7b738c8c0679b290620b6c92992f8b2e9d00e3d2719f416363e8a982f397e683be282d66022fc9834f35415b6a06174fb443c4e1057d20c67f221438de0490585ce2025201accb51772d90d9116f40ac69fb50e8d2c838fd03effc34cb95c691c8e2db020a6ecd22802675140d963f8045381db93507d1a32e21efd218175610cbd7dd8b0d0bbb2545d12a07d00c677e13ac417ed54ff05d04650ff952c7cfb0e61423500e7e3212a3d3da8330773c485cb3943dc58cf05f0f8739b5d045c38e208f7a7f0e7c96e8b2662eff2d75df39601c765f0199c7a0a286b2f7992f5bc18981df980e3b7b6e969662538e82f4898a2112c859fe6ee8ea2338a3e7acd73796d4473f0f029c59c76dbc2afe24eccf639fec4a3937701abc3200e05783771de8573e4105996e944d9f90bd8a11f2e8f05601d2e3ae0e26978ef8ac616704cc4221a3950d260a600e7665f113d3929d69370eabd2eca1f2587c3d02e6eed9e0bc6f315d047387e58cbca3667d2300139c1546da0453fc98ea3d1d31c806457e95e575be069bb9cb9ac4d4ba1ac899e0ce17a8096cc1cf9711a149b6832a9737f756a045078cbf208b6ba0a958f18ac27f9470b2ab112e30d57245827ba8faf4e98681d306e5f1207cdcf101ae70a2b40d3b1c6a4f708e3af6f458fbbc8ad76efbcc3132054bd5384f86ee49d96226a89c6c42395b6a5e0632ce05d95c1b35a7d57133b40f580dd01608571b2fadfed578998a96623dc5361a56c22a0c571db35a2421fe01fbd89cb7ab8690b0709f29cbacd71a25104e698912cebcbfd8ad6604e4a264004e497751a5d5f24c5f18129c37aae9debfa987ec661a860771dd76f65f6e260e0c0b1dbf32bf510402696ca70297d3e35cb179c257ed934f31a0ca1204673e092b1f39077a1e41836e3b17a2cd1f009ca8bd0b58a6d53dbebf8eca7a15823e03075e2b2c2727d37f2d4d30f870e7f41187274bf3ff3e24468734bf74839b19044fc441cc7108884d6ef3091c8d0635a4ceb507ffe636b30a1672c022c06bea0969c08f68f9478d654a6e7a0832ef6205a36e1eb5a6b471af93831ae2851ab1074245c9ae3c8f57e07cd4a444be8f9588b7bdc5237b74163545aad26a496a050f00e0e7c8d825909296f55f8cf3b4544d02bd69a78c2e019851902528ccc3e505cd3f7786493f55a2b6eb37ccb430ed71315cd08c8a513ea6bc21402af78f3a09295322763da29301b7aaf8418969e8d9b83f072c3afd6d0c68fee7acf0d60a0501aca6df6fc98f8808ffb9ac6a206682035761ada01284a279bc57e5ad68530d03731b8dab842078f7cc260730fc82f33dd5513564dadd8aedb8d5cb9a3285074a8af556c7ebe445ea4c565f16dd9bb5695a7686d325c137327ba32bac025d00737b20d2c66b3f6ade401ada1e8b1d5230417aa13e818f211d5792c524ab9b0150c7518d57c5676466e87c3d39838020ec52cfb4d91b207b7ef9d5920312c30e8e4e9684b48a78ad38fec9cee3351a52245b93bf31d8134e2923139d5bb69f0940e525b9047453a41c90b269d4e13da30aafc036ceb54c104b0d494bf25a4508314db00450c636c340d14e0f53c01f2a03451dfa313ffcca7dd76e21ce63890116e23459741d25c291a55637e6ba3b99f39a30e741223d1c2b2512c9d75ccc074d2396b101f3f8ba58206c26eb0de29d6d9d9f944ed5874fa87ba13320ea510db10602fc3f8932fdf512619b20b6095e70902863e4f7a084d109ded68a4f5b0cc9bd269b44ff44f4f652893391d853ca5c7e00dd57f6528b6a4bdc986ab5030b5393abdc02af04ab3da83fdc874964d0f62a3997259fad7d5951ca3be1cd1f03f2a59101e022915b833a006ca4777ff1b646a6a3aa903dd4c4df23cac78cfe0c6c20b67102b3d3065abd113ad64e1170c9fbff9d84dbb306c5076e298d9de70f3b32a793f4051853daaa8e936ef25fb2e0312ac4621733394df708d10cb2910d293fc55e2973a6af9367d129a2eb3b8f3412e581bf5318af9e928671346f910cab4243a0db4aaebb48c376332bc0e1de562cdf1d3558b57b176ff97e087b210bdeafd3e61302b4c4e1725078e1d708fb505c1e7596f05eeea18d1e8a2172390c4b0407bba832f66a196e0e350891fbee67a2b562e913b285a11609562fbe9c056150195f47e9b10412782ef1d2b3fbbada92e32f610d7beba5fb76ce9102e407e8b450bf16b6edbf39235d000c55813c5f45ec4f7d3c5f3570ac77a251f4c90a92639cd7adc470f775b589cb300c4a2e723b88943d828d5a5fb70b63dcb3630ecb7d186f0d6ed4fc6762fab53bffc47e0fbf6e81a466f5a95eb772d24b3bca0e4279a07324660987a8389865a23aa168aed682107c021cde817f8393f7c0b90e6896019324e31e905a457960cf9a8730d646686bb85af9deabaf24fcf43928081e16d34375671daad064580f45e34f1231a49d2b9e6f8e54172a61e40f7306055cea4877f4cffc878497244bcbdcc93f2584d124237b5347197db876c21a75076a507347835452d9121c5fbc0030477f85b035fcaabd8030e449e994a271bd00f0eed42b08c7d5b52038d500b4099c38657ec1ec79755ab47ca9de84c9e6ae0a0ed87d9da0bc22d49dc6deac1a76b591d37f62abeda4361f21614334bbf6b50d42a24fb73c5f4ab95b43aef52578f4ba0dc8d0fe722211d636ef0484b47b7e06d8f43d5d5ae07ff34cc1c4a5eec850d6fa90e7565db3bc336ebeda015057c80d727db7d0b68dbcf860020b2ed0f0106c8801f85428214ef637ac02e663bee4013be8b6f3e410f0f06222aac79978547ae3d9df159d93f398a4a8f55e6fca740a04e4ef8f815694e7ff3c83183f9d7fda92ab23d42732a3f774e726606af47400c375808a3ec0e6911561151fecf8bedb10a12d1c8c8571fdbaf417b6f8c1ef04e1500edf980e7ab40cfcf391a799f4a1479176e6614d60fe4577c7aa52248d09c2d9005042454a36e888d39597a76d5b81f14b0a979d3b2ea5f39a2249a301007a7dc4bf8e06d32aef0cb0e9d9a4538830fdd8fd40a05eac8bbf40216a7b6e0ecbb7664ff8b800d832e5b24d4e8b61c097a8d1e7f6b1401625534ab3e931ab0c8aa8e2dc4e0bd9f6597741c8783899e610fdaa8bf132ac553eb9811c46861305ea95a0c495010cebbd9824de0efbc0e95b8dbe7e6cffbe67b2a6fc8f3db9080ca60fdc4ba1675b94bfdbb9207bb3f3e8a35459130507d3c0503db0e5157355019deeb3a7db460a1725e1ebb12044b624b22beede6414f2bc1e5fc3b962a3c702ff68e741653a6d9c6706e99e55aef23f16192d0d246f9318a8d9bded1b57590e1ae0ea5ee10caa90abcf02899339a4faa10ea307900f7d2cf24aa6ebb71ba404b6a86d361deb2e2515a735a45ca1f4ee823bfce1a87cc8dbcd0e150b59d8bc0ae548d8a94586dcfa024a9fa00617793aa349dbf4b1ca60613c2b23e7ceccd409c65e8aa0c8cb7fc21516d4e8a2f6d87be9e96267663edb596706ce83c868220761242e3f8e930fa3cee343f5ed9394af8fcf0208dbc2fb686d8b2b05fd26ad0e584721832fa90b9b0989ed0103a91e03ec98cdbed68ffa45267a230f9f9b1600951c4fc62699911d6281cccc582f06ba259f824d4b17c69fea191801eb43fd06cde2eed5067bcd7b6be5b1b409ed7eff2463512060e22d0e2b979c74240b4c021c2724558c8d149de3b7be4a65e9851e4230704fa8ad9db38ea4e9db6f7ced07b4785e39ef962a7cfcd3448da3c465767daa41b3e420f6d5d14270025155fb0600da1cf481da5b2df04baecf47505328c54758be5ada6097780e858bce42bd0b6628b742b725c886d37130e54793588d355b52e9c67e73759ad6a9c9242ff20e4917752bed3f591966528052a0e2cf3cca55da0265e4889bd54c60a4db29e6060202c80e9ccb95654e0e97b1fe45cc61d31d592ec06a858bb58566e0558f2709b02ac67f30698714137ca14bfe90fc63a44e9d4e0669a9615e9fc8f627719e0b34698c324ac6d602a41b5beb6f7cd64929075127a8935fe267b54af319d7900b32a6085a5dadad176159c04724865c5450e2b216b7d562feafa68e97e853310e76aa025edfc274d5d7e92bb36130eea5cfdba7d5e7dda855069871e550255b0edecaaf7f6981f3605e394c9e83f4ce418cba63004bf5b0efd5878f26b0cfdf039ae053fef102698a5b0c25d7be22bcd33b484ccde17598e5e9e45731c084360d71c60a93aa2b715436ad41022ce843930352cdb31e0843ba9c93cda7face5e0f453dc977bdb4294d5228b4a726acba349cb28a7098873cbb91846284e6126e042a1859f8bc4c7c9b0136635631391c0e3791dac8dd132fe3e0650ac771ae1307f8bc4586bff0ba3bf9e9bf085113713d6930b868422d527c3d9c5663e812070f425542825d20b685deafce2b8d9bec1f9af7ed30b36f2ed78dcb1247820ef30a7b69775b9e6ae319d55393889797a66a7445fd608fc5c9df4022fb1de9c78c020ea3da912c89498bf5b5bacb5530fafb5ce1d4d6d0eef57fecaa78c3f2f81607ba3d2466e5d189d4547089a9a2b3794217f0f5768273d447cdf2dec542e73301b57fb82188f2b0d547f4089917759a0fe9af5614d97b4201806f10c20503ca0197e1f93b6bc0d7080d3805ae9b872149a0339fdcf4a2595a2b4595765cfc2c0155a70d31e0f5df082aa90922d8feecfda19ef7550b3d06c7ef5825dd98f3810246e6df743f54acca1c6d8d0e7b90be3f212387ddf0f85db6d86179de535919069b5fadb13ccc677ec0a1402f6acab23f534dbed33c429fba5b0294be07630a0c03f08728a1d9beb172345510654dd371ad69649c7cd3bc860384827be2791c0039831d4f3dcaaa42b60e31e5976408417031eecc6730abe676f66e9d2c6eac09e0dda29ac1698335a6c598b00c866c237dd1106963d7c878d511b7788d0d870f687581f4ec9e383ac3b41f6d87a6d340fc9ba07590cbd236c3e99b3a045e6f01d161ad44d4c004d40301bfbab34d012b60c982f4b2d16513753e5637bd30ff0ca7a02eafbb15efe6e265d0d542c7d2a934ff9ca3e041b7355d7a6b61319eae03497a007c77a2c55e543212611cd603f35823c53aa9b36a5b855f64bbc302dc0ff3f85df515eb23af7483e5851755804d661f95745eeb7ce1ef97f7efda7fda02dda5f45c7f18a59e3044e250b0d43224c48a4cb67121d629439f1fb8d087a8098fc5ad4779004bab4ca6d4ac5a7cf16bd0f8edb8c0e9db502873e6c35baf800ff8adf2f5983cbafe9bc47bd4dedfe3dda93f3b1b3f79e6ddd386f0b4e48ad204ffb58795b056ef4d71c9487bb4c30feb631aaa293c9e18d8b149db096d8d96059ebb76bbaa3b548fe58539b47c1f142cd86101a3d0128dfb352718efb5f56e003e3b48e83d10275a1008daca2576bd10de815ac4529691950bef1bf78a12a10a9d29e487a807a9aca0e2ea7f18d83926e400044c9d887a6950c0aceef15b70053d96cdeaa6cddf8e1084d7923cb1fa9c77bf399ddc7818439ebda41e4db0eb01abca5c5859fc8fea86b702411cf1035eb939c760171a28c41c62e85301e9720c81b1ccc078cd2160a2bebde66a8513e9786262d70637bb718eff407985d8ca028e8c1ce89c0492274be7203d0500cac4d3656e6745871a8208decfbc080547030982ca09e23e746434d7fe464369840a2d47cd1d4f4b0f30262e5ce91e522c0946bb5b6b4e9b8a1fe565e32979c2ab867e6f95116dba2b23a683bc91e42abf0d836aafb8b650646dee78353938a837d0c9ac2b447f0823b499181e8dea149908439258d7ec5c63c8c00f796788206281dcb97e82fc347498263e145032f2030f09ca949481de4e848314950ddc038b88d10318490e9d46c265b19fa182d8ea08a46c019df4f540ddddeecec9ef7024edabe0badd99663d9102473979d885990bb84289242bff96e2fba8077c3d5b4151b1b8898fa2ef900a08255b4dd7eb2c005845dc3501b4ba419347ef75b1f2969c1d234a2d9558b195b306ca077fa3ee0931e8ba0382f75debb956736fcb6f7ae7695c2f37ff5ceeaedd23a5edef31f6071c1a8fb1267fb5935595a999a4cfe3c10f9fe5ba091a0714e06ee880b405d10c17d1d4168fe7f4de249c8dad1a04d12d0366fa11ba9e21c6691251469c089f0ddc5380813f6363d00983c418b57676e16fd8d4e6c15c5e1c09d4e0c6176947044da6f2e0ae17caabab9b21fce112ea0463b6ff2cb49beec0da6ba512bc120200b63b9bd2457bd454f6731948c74798cfa28ce3f1dd68a337e7e9c70bc8fab709dfe51f30acca7aa40121b9c04eb97dada36eb42e9df36f7fd1081f26aec39c0935526ba1cb5bb0ed99090411d995aa00b9d4cb83b1ca20a23e0c9500df237f0835f530b5678b05ff0b36995402213773d3b9a19094988badc365794ba79a2b0b98ea54864c7851b4a6fafb73c5119e63096e6316e4bd1f078a6b7f5f0da1740185f37e12ed4d14140822e235c9d2472fe333a0e77ce31cc1a556e268f08b420e2d864fd6c3f9842f530a20f2302cc9eebce414ce1ac084e15aae6c77701cb20cd695c29516cd5de56b1cf3d38b772236b57c9016f01306336787c52246ad16055ad2409b10f675a1507947e6a10dd56941d3ead38c9225cde847943dfb43280ab2155983623777e2192b4f5405c418ad4f2a8ab167104399ef5e385cbb24940a12cc1de0204dee3054c3325df5bce2d026a8af71e58a9bc211e514c98f8d9d0e2c81932857698396321a4a2ba24526453bd133f56ed149aa2a2645b83730380d378460ed9bb7acba76288e25657e8e3260e689e570d0b916849e3dda5fc9b407e305f24a4a736eeaf550085e510f98d6c126377e055e0a04d78295b77db7ca0524ee26fdda91894c7471b843b052bb5356f4ddcf4be6e79f629754efb252560a94ce70d1cf5a8956e805f4f61d2d4148e26dcce62dd028bd46b59e6ccd64fa0f07418b180cfcd4fe510e368e99bc17aebd2568cc2165e97f63247a8573f5a009365b2cb62a3b7a1b06082b33dadeeab7852b89cfa9f0f4bdba8406406b547f0766a1d899ea924f12016384f7d808e0720d27f26dbd193e320361ae02d50119006ace158ebcfb211ed8bd320b57bfab021589f2d70ebfbd90d1fdedca8cca760091405f2617433d57e75c6e915c2243a95f8cc8279305debcc3d6e6b2aa045904ebfcd6adc8d39f853dacae4ff7912721b8003cab0f9cac8476f1dbd9cbe1520f33d385c29da12755b323300da3b2da305fb42de96b69adce75ee7d5a08b644045b9a333884808f5feb06b1c38838c6ca54b2f8e0333dafffa13633c9ef4efa08cc854465cbf05724d3b9626b90c1c9aa0ec319e20e79c85c5c2f1ce960713304c2f4720d00c54b899e67aedfdc2113d1c34d2f96f6c5547c9e3cd60911a41503e84d4e27d7a4b63a52d499c45d57ea053d935372b3f8df0ba3531ebe3d74b30891a3bfb17c1cea42404170311c2c056f3c0b9650d311ab0a4247b1b64246d806a3758d77d298b9c1ea2b6595651295a27db2909f381e8ac6561fcb315db493079ac0ff15d87bc17cf484a7e11b490ccd89e17696ca645e10f433ac1c9085c80beec5ac1585587a6f4a5e60ec57ec99efbf99cfd346e3ca87a93c88654cd2c2069d9c18f111c71f644875c019c2beb6819a510fee747df2874d623829f12b1301a73a5acf8e5b659d12e8e55b4a34914193e107517a5067b528c4418f07f90b04f2741c575eba3bdae0fc148cced0c3e61c6503345eb35a992aa4aedf01e8af0864c4b0334ee55743c1d90583a604e98c58a545ac52ff65685a829bd75ca1f30a6e78dd5392bc097d70585e63842c460b67f6e8e5692a7eff77c3f8f39395830e31a4441b669f0fba01812f899d0b41986e87756967f9dbea112a96ba500cc90d31591b167e866ca4653c6fbe6083a252631885cf1706315afcafe3613ab172033d89fcd72a5790b015dbc794189e8a4570bfeaced57824580dc37b658fe4ad0950be6cadffeec2868a3d5dd456b0dfe2614641a8b51db500ebf2e2085c5fff07fc9457ce4a99038a6aceb8010f22bf848c38beaea9f7b0b12eee046b84c8cb09074ae0312fe9554a2afdb9a7ff0e7e8025037c6565f7003d58453d8390af4209199a54c99f90333777cba1414a9596f2f96e6f838338552026b72204708a55053c380e11b76f4232d944415501c1b0ffeda07a39c31cd9f0aa3fd81b59f23d0113deeb5aba2c1531639548a5eaf45a961ea33cfd00c46e149d416137af521d067c6868531364456c3bd95db0df279cf16188115c980217d001085c7fb7ebf50d10cad66e33c53fb9f3fbdb1bac3b5aa023730adf0fd006320427660a08bce204ac8a8d0db33f07f6465e29959ac5a02dee277c81a45c16f1bedd95a3e6ca45051506f54a619c5caa735176eb54afe54dc63226abe23a11509538508f99a50e001196a2af5f75ded309e1d32c22a109ca2d4c0e394e5e18d1cad20c6874d5df070fbf50f73a010afb2f65f29e5fd774b9279901d43f22ecae255e1a8f8801760654378e42409043205d8c1245c5747f1618be24f988a2b2ce466ff2b528177c0476a10d2e35c0f373149d560099332d00acffb61148e2ff3096a362d9db270b05b3c4c78ffd1965a7fc1a33ef74e6328c4a48045f75cd04e0d9d288f0362ed3095693a95b7f241faa605b463c36d0f0a79c43f2f85bca0f89b25f20fb3119d909a1b0a2ba2714f20527e4b2ae2c6e3471d2d2d78c7167bd94b7735e2c58088606bd6beddb72f52bb4312f2111b389f9cbde833b639846145360ea3f8788a7240cb4af645101fea5802e4daab12237fa3f1b45880a812a43ba46fc9bd3503c080f82b1834fc69ac4daff0bcf1343676f45602f56c9c3b73eee4d4d952c1f0dc30e64ddaa21b034bd9a140d5df1840bd00a2d8bfbe781149bb1b09a5975eb4079082516b6b2da1fa46ba203716b8b3ba492285e7ac8f1eaed8224530d2909027502844f020ef9cea6c22770faafc735a83ccff5d15f1ed43c697682f9516ce6430eaa3a4d327e49560c61310ea69cd6fa9a085a1cf8914cdfb61dc9c1aec427ee0733935857ad2458d931fa15af17e6d801c0e6932ed4d95d20d4949285b4ba1e0a6337cd766bf94aad3c6e54538c1d6f56a887c5e7d6f61e1c7b6d63657b3e360c148e62199a425a616e19e54cea9f62b4ca8464be2188fbaf41ea2c03f03524019484fd3ad85a1727261f107ed1ee14ae6690caf9b287ecdb5fc5d8a6cb161f07fecfdfbadd488b0f65af5b84bbee78619891678de751179e0ca72ac81a876c07921bca020530b5c7b2d5fb4f41833e0d153cb523c6c93480648a6d05db937309d3df2609c2bbd641756d9fdfa80dc110405a13c5f25327b2b9d4a2d63c84b909efb6c9c8586c422dce178b56ec342f99b290bfa5d131be3d66319f1a5dbf11001ff183d153c43e2a66a8a92acc6d1fa7157f888a4c03b155c5b2e7cee763e90b0ee32ddde7b96e6c1ca00a79ae9f252cd35973810eaaf9bdc0958a77d4b5210c8a618b5e6c89af42b376a6b20330e77f5b05b1e046f538ab0e2edec6fa8582078b8f6988e7cb92b7493d93c456d04f5038588c46e7a2742e1a613573e99e5e0cd002c2e0b6a7b363b8c22de80283fef8255368beef8ff433815d1fa1851c6f0dd3592cbdc69f8d94ff57cb6ad7f7f0d51a46f8e5ada8846463ff8bb0d0aecc06b8bd27247cee1ca612efcd88d825b10bba99bc27a93efb6214ef18765b351603591f837f4fc8456417e6aad96d3d0c53e325e069c6a7cb604943c314e633b60c00eecee8d17cccd295195f526f47402a7f01bc66df199a31fea779734ff3d90fe478f4ed6a30dfa7c0e1b3f68ab2a59144ba8eecd7f66682e3b3cbd3b9cf4e05061c1fc912424d1a903fb78f4d1de10ee804a0d6aac755f785df6b152f0c8a0c7dfc454780c6d49e45723ca134b648bc86f8336c961e5fef2f22ab971c78a10bb39850b5dda4b1879b8912b3059c1c27c79b37790c53180611de10101a4af80d2ce5d7fa275bf8a50e3905f5c5309ea3d0bf0ffc5a7ca1b6034d51a09f294005b9d4e9d846f57f0ce6d4e484198ada6401584c80901e7eac66ce6b0dd7a8010d704e6d0ff9038ea89e9d0ba75998fccfefe953cad4cbe4df71b78ff5e313c701a84413078b802de98a81f1344d6e038030b0b2ee2de700d04d153cdffee16e09800c51c1fe9a725990a126295fa2f23abac7dbb386d867589ee236972a40330e3ef788f1bf6a8efb76731dc64ddc8df88b3ae8bfa91d5046dc9b531c7e262b0b633dca4609d634a2cd91b987fce7319f6295b8fc33a03709a1733f965a184f0cbb77dc919d661a309e2bda65cc93fadb52f0ebf0b760d37a72498919eaf804088cf713c36bb4b0996d0537021c1a1c55705b5da288efbcfe7bdf2992bb5926052f9674904ef2436e3b611537433f6b6047601d43f414d3836379af9c3bd7100c1c980c7a9c1c508b60db34468fa30df21413ddc10565e8a2544c66b8668d630e439c3e102cb93c0fff37087076b997b540dae5707995a003b34a11c830c3660660fe043b66cadaae2cbfec3b30f5d5706a55aa9ea8d355ef2abed3c4074d10051ea4cb9d98852c64bfabbf23e88fe21821fae48be8a66bf895a80e69f74ac40ff39f528532c5441fd27ebd8add8b56d0daab07473d1d886e21e3d0b83ffe7506b96ccfd19c53dff36a560c7c6be1b5be2af85777fd57b56d4165a2078b359600c42e035aeaeea38f40b326e5831cbd5ec3535345674f279e99037e2a6d2bbf09c8c617df1ae8f7bae629de6f086cb1f564caf6e45362b82a5489d6a0d507f400046a2873e12957b5864aed560f252559500728aa69a74f934665f6b7ca9e6401ca05380c55e2089cfd30fdcd445160b381f1024e7b3c142eff1ca2dceb67c009694821679ab1ff7883e3adf01b92bd14395ebae81d44d48b12421596d84da808a66070aafb99773c61cff3cb5a8fcfc42ad726428eaf3ee8c5f2ced6735719071108302149fb3ec1fe8968424a203d9a95e9ffca21312c404c1e2acbfaf7260a45ddcef31fe4800cc50c915bf6f3abd9f83f1a2eabc3b12fc4091cd81de53803cac32c5ee9bfaa63cb492c2eefb415b1feb3058e840c3dc21ad008ec7bec5c02ceac4b6eb05e37d1181e008645d14ad6703a8ce3380a0ec45f2eeea3e5a2150a79e6546bdbcc7354ed6f648091e45c4afbc3cc500cb25ab6154abb4e1c47fc087cea0efa184e391b56689ab974c8b5916e54209a88b16daed48a541bba85d502f56ca433321199eeb5658507809284f96329f8dbc35c49a5d59b49465e90c7008d4e3aa548f43a4010b63523ac478be526b0e337c7d596a36ef65cc46635cc069e87582b3c47c0ea66a0279b51d5fce634617e86fffb1e78c8cea6d6c99b8002dccd11e97722711e1a6f58f9cdd97492be42c159c818d2fc6f75b2166ce35f0e false -check_ring_signature f44c8ece36fc8fb77bdcf35c9857c47bbd755742b769aa623493ffc5a23d93a9 6665763260dd429829ed7a7b737e4d0e84d7a12dffcbbe5aec6a964345473d71 1 d93c185315cfe23ba703308a48dcbafe8f2e5a23a27d87a61082f74ead702596 007fb7c2c258b7a7bf96c557a2a78ca198bd39d625525c174233af16b617ff05574372af64ee5d6698f2c4e2b6f75645a0200b6e8cc9d6d2f4d1793198f6a908 true -check_ring_signature cebd41abfd2e2cddf8ada7e0e09821fbca1b777fc2ed947efe75d9363d60e913 460e64c9cf65d37f53cb3e0dbc6a7c6b775c0588b19891d12c0ae10b260906b9 54 7b95dafb49215eee664789e745e2f03e59ec683a45e2dffc796115a9bac71204 6d593b0789fa3042bdfc525334daf95f7bfd26f6db0182709595e688f445c109 6f1cf00930f36c2a69c50ee5be22d36cc2db3d9b1b714350d531568c024c3214 230ff5d6b543c2dc272b83cac41f45a416b1420592f890a2e3b6e80aa4619513 ecb2053f94f7fe59135c93893881025c5d2257d73c85d3fc8c56aa22562bd0e1 e1ee16c650ab3babc82aacad4ea940ff35aa7cef4867c1ff47a4602c7d00a64a c0ede4586578a2f88eecb911cea9c59c1cef7689db441cbf2d73f702f81d8903 a9139cac9fc6809e367d2c2b82112ef67257a44effc821339ab9c5cf05c8cc97 a1c3e63492ceedc6d411447a4b6fc3a9b03a0f4a8fea8553de2a65a8c1749ac1 2a347f4f1569c1f53f41159cd7cd9969e18154bb6d9accba1282b366b07353bd c2cf8e0d26851d571d503b1da6516d806946508fe3cd79bd9255774be0d1edd8 42eb8c67c267234777b4db7f09c771caf31a9a8899ab7cdb98781ac6c8b50e54 8dc49ec2050e4873486996289a908da507c0d94ea629bba61490eb444f31bdd3 59922ab00b6a1dea1ea58dfa9ad55250a272085400b2a47c937e8f5e6d1944b0 875a5c4a68ebdcbea8b4aeaafb9aaaa240479004e09c851d37f84e927c25ca79 76726d10fa76aabcefe1606ce986da136cf4edc5b0415e570e3a818e0227323d 7ccdcdb52c08132220b4cacd4cb07d7f081b53048f7e12d9a4d00b9d99ed3812 1d6e234b81f46a94ab24413d370afd08e582564bc711affe579e7f5a4d7c6dc1 769cdca43d601c886593ad6292813051d9290956fb452204773488a8967932c0 76123e37b3c65ccf573c7693148242370d6ba48c1d79d9d2ac730c65228548ca 53da66849d6d083d34dbeabd45f25cddaa3227281443b7af599ff5cb97c18cb2 a928b7d5d69ea908cde99d05b0c470848ec9a77226e7adbe5296ddc72c25d64f 9dbfe6264045502d1ce03517444313e4fe8518c57af71efe39ad3b080f9fb578 654735312db84b3e9c80da3dd2824f41b1f443681126c3f7d5f1afd02b444f23 01450a21bb3ad859e0bf7e1f726f9016a7b218cdea3e9ba5b2cc947fb7e3e705 81e191fa0a5290702e6368c678bcab21f42a21c3d9b9e726a0431ce893592d88 5896bdfbddc842942a70d82e3534f4fac9eb7d8fe778e1ec6feb2ed3a671fecb ab1cf5d7ba8ad428897815e782aafa16b76dc2f6d46b888fc56910d50889578e 7089efe5451b8129fc7626707f7c28e762a500d7ef73ef7a439b7723c1f41774 339581f84220094e03ad93908572cfe231ac07844b6740a6120d5b4c86e832d0 0c5005993b5082fe9b848668f52fd81fb778ee3dd7952ea1b1c6a10e50df35ce c485b7cc1ca991312ac2cf9e2955bf6699802d4dcdd0cdde715891623ef3c60c 2ceccc8b2c83af7527e4176feca1817e62833fc4cb9259c1f1f870176e9714bc 9d77d0c32e0fb1e55bedb2f799ece6a7ed816ba34172f77afbf5b51b3abf24cc ee8ff3706da02a6c4fa25143105d814352a3a460291866433a1149b127d7f22f e51c1632cb862b4df083d0cbe5ba5b2781752b713d56e03e577d9abeab97077e c9416a4f5497a0a4fb6eb35546c43228d1d9f4b35989080f4ea68a2d89ab2bb7 a39a3b66de75d0a3d7fde73d6ffe57b0d8de290f6ab3d2ecd3913ae4865f0963 9c85df951c5a87c9dedff57b77cd87af9c345b16708578090295443d706645a8 9d95ab81dc89b5ecd35394bfea0555638e34bb4e79b1fc5a97c4a481bdc21cac a82ca8e2e2877f340d4c4842ac444adf249a8f1293dda09a83efb5425eda1ba0 0bd24f21f5b4fd9f5a2ea436c4158b8f3ab7686813ffddf6a1e8225cac12b45e 85cfa257b0302431c613a6747710aac7447d7d6e2e67b22da509d68ac14b9e0b 6e731b20799b3af21a2d312edce74b86a9fa846cd4ea85ccb905a0a7c9b6c12c 07cb11c4cf200e0cfe1172e89c5feb5a494ae2938def00c5b3b3eabdc062c0fd ccd6523d8e0685ccff6dc45795e8216e09d25b28744a207b1307746177689361 448b3cc1828f33f97f545609b8b1c5658028af68ac69e5a3b942e0a6b1619bfe 694bc5bfbdccbbf04af7060a24f34a38435c22d608d42a2f63d746f331b06f02 2fd5e57504921719a88c5412ba998aa861fe4453d60715c6136e0ab8b9ab2a35 0b368ede61c51cedb750ba4df2badb65e1fa2825ee147078d502474041a992e1 98c6d1b8210784b01e1215027e828350f0c3f62b10993a585a71361b0c2f9166 59c329ed2862ac6ae90e8ede944c6967ad80c297c6334781d503f54e8caef5f3 bed54b5af765e20a3382757846de701ebcb727cd5238233ab416618630f6f04d ec97388a0f5eb6cdd158af340df07e1f3397cf137aaf3ca423f3b8022ce7440b 34875c33faaae405d7bfb84e205868ef7b97da7726d7ba0d209b09264339b803f59e15405d7d7f98257416235f1a3dbeff89e64a008b813ba1fa25954a72f00e26f21507dbc377e4a8cb7fdd686f543fe993fd208917922e002fc64dde504c0dba8f2134bc99a1b2c8c38e15f86b2c6aa58076413b397aa9729662ee5da6c600b1c61f0484f6620f810c19ce9d15f919b25d16edb5bac89c2fb308767ad9810fc34adcf488a7fdf2916e1e446c982c1a79efb3669513454a1a6cff47ca40a50f5da4b394a68252c9f13a410e4eaf66876c6e6f50a9bf5fff9f4d0e010757df04a40416d6797a9982bc1dc69b09fa19455302a5a84bb7b09e24ca930da531f100198fb227b8badbdc709e4b1bb23f5dc503261ca7cacc2d2b8c3bb1f4a20b7c04d2f476de2b87ad76d2ce0a49dd11b5df05d66126146d778a66fb6898e69a63013d348d97e46e9e958fed2c89759c6dc61fcd159cdc92364ca8c90c358eb5bc04812fb69dd86891f78af9dd25fc88606e6e489034c4dff1447f02ef5a3e9b1902c4af4c835e9dbfbf232fa2e7e5fe8128321d74b11d81da9702a566a44b76bb02e5e260eb1add18035be1bab253ba18b9b7f09629adb003a73741f5cad1681e01b5c468d7285466d17a48dec00a886927956f47a0a35271b2f271846174e66a034230223b0d76d2fe08de30a6013939992b21cf4e286f5b1eb1bbe1d96e7a9a048dc3dc92a24e36a087506623da174b43594e304c548e3ba9ff48451a96410a0abaa9594ca4d1f75d2ceaaa51bb1359578a6f6aebeffca184d291758164f6cf07844c40faf244931386e337dad88ea7a36349bdf8792302a8ff40555a851b2907c7d689e718f3a743e8dce39267b39192a2b624157d79feda20a6a4879f5f3a04a8a41ad0cbc92bc39bdd325c559eb227bd61d00f8c42d90f3c41bcb533380405841ca56d4b59ce290d767d6408125572d4f0c5ffd08ed9d67f1333d100fa0f09eaf66495f8020b659f6c779ad66d8e0b1e2987a091515f7583db8e227c16510e5fbd015c79dc48e830dd7969ba2b902a9dfeef2982577d2c254578051b5df303bb427137e2aa52cd9241fe517fedb874ee15e601a1b0eedb338cd595da712f095e13f1af42137e52cb3f87a8631ca9e8da84157c0aac2cfd3e743753838f380cb134aadbe65f337eab6c1fac23ff9173628937176627abc7b68158115134580800a949e41ecbce7ad49027b809afd8b0cb04676bae70d51ff05898771fd1cd079ae9854d1e2d053684ca1db8a7617244f9b7aa8e70755bfb2cd0670490b5390303318189f6e7610ad4957cc9cef315aeabda8355d5268df4ab6764997c181508f456e8fe88e9fec4e967bab33cecb10ed0c2b51a8c579af3bb0e341c4caa410a2bdc5a8924f501a34c53c2e4231ae3a6f2f7c0009d66a0501f18261e2b840309d69f56ecfdaf4eac9c66b688652c7507b71854d5b8c46101f557f410531c800dab51c8a079d8a6860d15ca43b8e246fd7cbba80e6647e74bc257b68add63df0fc64f6885d40579b33d920eec8eb3ce80c1c5799d577cedaf4dab6834e35c7700624feb7cf26b9c02c8c3d63669e4752391f3276760cdbe65077ea1b3a1e1010d2d059b382427060989b308d9023d2147b2311fa98da256d65c4ccca9c0d977099eadcbdcf0aa12bf5a7d4eb68d578cad40b4d7ba8af999482e81252c1c8c7f0022e0ccc6d6ab4100d74bc8d0bb7f166530c4ab3bf04e9cf4d09e044c055a2c0919c24b131cadb2a7da7f6a4ba835fce8cb1c779fa375af26741771238cb7f205ae265807ce3637ebb149b864289818bab39df27127fc26ba2274f5e6ee78bc029440efa4f4b859559d81a5fb1103d57bf3da32e4f121f13f3b2aef687e99e304f94d0ba3dce092288bf0d35b5361beced2253702a3e721904ec42b8e6a674a037b0fd73e1ee6936dce5d4bace4542c694e114848386caf6ec5ba600c13fbf907b812076f12e122855fcccb54eb7b61c53596c0c00fc1120a48382198637e310dff7a519c530333d0896f11e0d0efe17f6d026a4a323030d0683fc77b7b88950ca2ae116442caf5ddce4e527693602d1a2207cf18e6c2367d62259d2f959d78057ff0e3f82301975246accc8a7a2c1b50d32a3e13da67695f8cb9f1d4355ad50b2c34c52da57d547d7c59ec6c5ef23c8805accf99c9238ad7bd187f730a05020ee1e9a8f722fe369824cb61f80783e91f4748df71d16ee56a31928fe9515aac0fbd2d8527b665167d73230540842656ecce3f07d01e5e61c49ebf29361f0d61093f530815b6c83b594cbb579666d8b5b1a392d758959e882fad45f85b111143063f7e4f4a941c22addeca01585ed63f65b22019b5e8d9b286d2a027246d51860aca0917267d092c5e9c7959db68210101a214b442e70469ef7c0b73df14ecb1065454d0e8d48f885651d9302258e85161c44cbf8dc71917c6c903cfd0881f5c0a452fefd2a2c0fec658c485450c5328188e8d2ec41805f11ef01d1fd2e9fa9103f7a956b31b5ee279c7bacfde49f8346df39b88cfec9a8e969c440eff45679905f62926847f77f0aec76d2e33c04d265024b98ec98648f8d36b01a9c52f314503c2e69df6cca0e19c1258ade355b2e4f7a0a70ba1b556dd0586efbc99da61f00fe2cb7d8cf1e56c12f2e3351fbd8392fe7b9fec86600a1c16d9ac44467bd90b09a3b3e0defedc6b1429ef16e3ec072a7600bfbe59f8d2c1861a539c2470d110021325e1e0320e2edb3380fe714a13cf1f7dc31b7867fc6de89c6fd53ac14d240b2e95a4e37b73febe3ff1d2fa47afa97159f627f5df5da8251a337a40aacdb60bef2e6cdbbab51eb1d3171a9fd74d814f900a0a5d6b8525bed9f22af6548ef00043bfdaf87a66667130041bc69a305074688696da962210e7b76f56cc535f5b05982187916d56c78ec97cfac75981dcc2e56f41a694471749865ac5190457dc0e0e0fddf3ee2a423f17c559e640abc8dbfb4cd6f6d9a62387023803914920e606592be26dd5d9fff30c178a8b0b23180f872a9c32c06e262236239926b842a70049e13bef2ecc02df761da5f30e7db596978c2a65c2420c23e85ccf7646df0b0dd20ee9b7245e14464fe89e4c5c6756cd0bf45f43091bc08e824c869956cd9806cbbd27c17ceabf4421ffff2e83769d5c9b2430d8c6d76db70a889a2e11baef0d79b1b5dc4ebd57a532d48e65dd95aad22c6e9a7a2afd7c49740e2a5e62ae49077e706f87bc435f4f1e05665490b8dc6e1470e249fbabe5a8adb9a0c686d04e071d494526a31a6e2d8bae1d5cad58f1abde84f86bfff04a5bb59841b9a1b43d0770c05080fe63a7d8548a76cf95dcc1f678ac3906a441879af0fc9aa3d9b2740940758e88e8506140313d0ca3cb725cbc584cec0720fb6ec4c76a2abd09ca7709532611ecd38a82cd3b765bcb35a997c087262cfc3f77b83ee7ece4b1d0631d00a43f5e1e81c5f074aadcc159e3ec64471164b541219552bd5e7da0bc935be00ab9934bf821d0c45793338079c870dd56b0ddc8fdf5005f0a5810d6c386661705169829f5b0d33612a483e26c6160a4536747e68a768c73130558e1274aa41d063556fbae966414f0f0ef2735eebebcdaaf23b2a16790543fc72f7fa7d6d87b0cf0e4a116afa3b34f3d2b188ce279d1c9c0ec92af639b0d571960ebdf0da6e00bd5823b48094605fef1f246f9e120d5442ef2c91eeb4c784e16b64d483566d20b2762edf8fb2736888f5d0bc3e9f0faa57e5fb0bfe892d25375b17e8f6edd1c07b027cc2eea57727e059084858594af26af4f29f98bf750d62199bc16332db30e08b296a6e5242d88b09b3ce15b2bb6f2cb0823b6ae6549dc13495e3384a68f017427d412734a3b7bd758eed0628c8baf79ace2ec0f281dd9ee9995526fd969063085b0ae43f90a2da35a74dfe2024b68ab08158784eb5b575cf802ac8d9e7f0060c0a831c19ec238db34805ad0bf4cc447ef9a6251d40b8a596547ccf9a45204749a22d52fb79e6c84374e3f5ed8024f69aebcb8fd65b898e2f6ccc2cabf9b0050235971481a5c52ebb71495f5dedcd5d5ef3d7da015576b9ae9cc9588a4b80d684219fb9fa76c931c486b170e4f2d4aa40d3fe4d08d4523f8dfe8932a34cf0c831be054d140ebedb1cf9a8d75218eca3972226da969d72e20dcd5c6ddd507072578ae43cdef2100c10479c69b648267054bd7d334651947a1fe55d887289f0d95638ae7aab69f4ffaadfafe90fd064fb3712ee17b70be88d25fadd11865fe004dd79bece1dd4c834d7b2616c26cef31232dc5f6c3f03a4a36a41e6c73e9a105ab36c9412d71cb7e4d21740f03585ee368692a6020ef4f3aa872de685f8b280ac155dd574a6523b2ecf1287eae9a636091c0760d13e96b07e287b5ffe5c8cc025dc1a8c49db5654d5de6ae5f193d6f6c0c1c71da2e80b196ceadbc38274b440279e14600a3e6afbcd4ed763bc9abd51ad7390b66c7c6afd51ef3c0b5f56ea20589f5f21fa45fdee1dc647171c7edfac101c70f9f97091edddb69bbb4f06fc20d051472bd819fd2f213373289c3a7bb4e12b65c2384fd3d50efba5ca5f8bd4000a6fb8db7df9b742c29a59aa934a9083fc50eab52e8fc4985cf7cdefa4f4101a754df63899757b322d5ebea3276425c8196fab1be0b647279bd3d01938a18f603bdf10eaab353fc32767392cb813cd3d53bfc7868f418f592dda5b745d46a980bb234abf9992b571be1a98c58ba342bb7f010149ebe27ce925d926b29f4bc9f0b0826b2f867f42d0a5f9c82d4b3358b28f8252008b22cada37b5be46556795800dd34f19aabaca9544b1d0233423148866dd3f825d81d3966978e3df2ce0bff02 false -check_ring_signature 383b72ba66bd7872cbcdbdcd457d42c0e7e53be351f319f54f39b373dcf24724 494ae741cc018ebb61ffe610fbaafbdeacd4a43776757d42946af85c007e4f2b 2 d3039b165f2a20850c9c562e8cab735c5adfc19b2e3c2f4db4fc580c47a9a4c6 d846329c0e2ecbdb44af6b4a9d54de31cd059675c80123f8a47b828e138fb660 4b8925f2f5fcaf280e61f94bdbd5661c02b791732328decdd4dd84700747b702905e9f832b1ffdd9b8a05f702541df0db266b9d420bb5742bc4bf6dd5eef7200fb084fb89274b7c9db2284f7614bb4c83970ea2935bfa04d5bd72c18ea4e7604651392768eecd6b1acccfc34414daa12ac6c65401134089012cb3e8899ab650a true -check_ring_signature 399b69e3090864140800c7d300fbde5236420367bb6d16cb5509e82ea650628d 254cdaa5173aa49d68a872b3a85fccce29d80f97a9d2a685d38baf1cae3a48cf 3 a4f76b6915b5c7b4371a9a6ffc9279935ba0e2fc73ea33cb7346195beba45597 b5757f9ccaaa4d3dafc36cea62658f68a8f89d86a57fb7028a91c5f6a1374450 64d1e13aa62be2337256eed75e53a82cccd7489bf2eadf86f967711943253ab0 611c4894c9247dfaa2547bd2edf8439ba599c7c7d97d4343c4a85ddf87e0170f6974dc36b29f80eff8261f5118199cc05a78a77c02f322b8e729f3e1df039b05c6b9ddc35b74ba2c9840bb2e1b6ec843ba329297d6182eac60bd8e7acfa67202a24d432b6278fb5c11b72a713e1dffb702c3d4bde69591ac1ac2da5168636803fc4f66c0d7fde47648204cf01acf002e7ac0eb53847f00f873193ed2ea926f029997fe7050fec471b2db85a31727e6416dc78664995ef39c8d3adf299b65b503 true -check_ring_signature c5ac742c6968a3b89f17a49cd42003a06b3821781357e36878158b4c7296a30e 178f086db2ddec420679c9187bd192d2e55aaf08b08b5d3793cead7c8af60153 219 231aaebea7ebc36defe8d283edbd2793b00ec9264d7dd7f425c908d6f89cdb87 59d3065fa58aed0f53ebf5f9784fe3a05143b421bb2eb740ffd7069892589cd2 98eca907988e7762ea7376c0d658b8a94267e26d77d42d4e88ea015b332fac44 e6a7dba7a4b836d44284e90ce8e8fd8d121256bd29f00affb3cdf2307f0ea898 743ef3d1f277eb9d8af4f423028abde54918d1bafc99d36ff0bf44c2e797a362 7e0f9b7d3185b4ec37f1219285c710ff4dd6487b5b6003827fa2543cca3172cb 51ed4aca2c419ecfe252204c171527d282785f15ad2ec9ed42296f1fbfc92187 94926c511d68821c9597fc3ec71ca351a69cb29dbcae94e7cf9a230f47394a20 a940e45cd687f932abbe72e9de6662d75b67cd725ab14305336c6f74ebc4f689 2900cc703007f17fe8c8162efeb1e91065c9f8bda545dec1f94e3998ab394b79 dedd33e86913c3a4a42ae63d9bd6ff5fe478e066073f4d35322a1cd52915022f 5993dd7efcef933ae014c82b8869f2384bcdcd839cb4a67c61f9dda6622d3f46 a4afd67e8e3f12af1165e256159a22de4097a462d1756089a089378c8ceee8e9 552b832d09fd03b8466135f445949d25165bdf432781a6ecd2f579fc7f2a456e 543761a8de540081fe650e82f2ea44f328465be6d6b23ddae21c8f692af3dbf6 15aa0f052eb5e1e41d12aa189137ace56af59502f7c5a1841b1f9f76168d83ee 6d9ec3f764408067318313bbc4041214e1685f03638764f6ed59c22b6ddba72b ad1ad820a0386b0535b54b1e6beb079f7053ceac1e88d22acb4731cd0373e603 ada29e4ff9f455259bb8c23f82eb24ca80ce08bb96e36d27ce086ee5e7f4e98a 312f6b10c6b9915d116e1d2c699b0ddd95bbb3cc5c24e33201e1969197e49455 bcdf347971928aa9773f817d7d9d99672fe3697e51fc1894283fbadef128a879 aecadfefd9720b85f72a634b08887c48bbc0c59dc87a9be6da0e6b8879b6100c 350c96ab6ef3209d2cdbf305960e0334931a68a3708ad463d1a38d8cb7c12513 0c67d635b8abb4cf0644609105626de85be2cc83c9ad66a91d1f452243443aca abcbd3657fd4a951fb64883ecbb4d5086a0255582655f329fa3e3864b6eaebb7 dd9f8967a4dd1523985e6ab43ce81f1038a94f1e142e54bd214b03b8adced1b9 b0e5607797beef4b4516b92ce9e688238c2c1a7cc54282672284b4d251eac28e e0e757986d2b486a1340c762e6ea19f70130d6003d81837dfafd6e830f8282fa c7af1ba2c0f3da720e56f1b6dff7f46f911a12b9a2ade598cbb070f9b7e4dbc6 843a8e2bd7bf801adcf9a8c3cc5c5bf9e12febea557e70f695296a0f9fd6d813 ce5042232b2b921579425fee7960de012c360e39adef0869d2005b8754723400 aa7e6f91bda21d9500c30bd1cb15b4d032ecb2499cf65b658d650ef6fd99bc12 30040d0a5aa592001cfa7534c3c0a0ea30bb299e5ccf37b59140b3c6efd2239f 92b0b04d377e996f345ebc11269e9b2020a0c6546074f2a12fe08f1df00e6963 599bbb01ca7184a2c01a5a7b0e5b520c5813b1e6d5d0b928d4d4cf8802a5dc36 1b18add0b3a8bd698cc052553dda5e35a25bf4d716fa5641add98317f5f2a054 fd824e86356d0285765524c7245503b3051832e1d49ad8f4babfce84366b133d 7d62df3cfdb723e3749c0e71ce8b4abdf4dc642254ddee5e710a6b8f3ec03fec a4dddc0e288c09a635dc01a1af132d0da9624b5762e9f598717bf9fcf30c3bcb ae37b212b822793460112292422ad485c7a0a3e68c073668755bda80dfc3ed2c 0926115b10ea6bc35b0c3e99e100dde105450db65a1c13cd72bbc6dcac5d87af 3b9154caaf2fd18f44c29f6442aefca19d264c461583d1e6023ce0ef8932bf51 73d9b87675fa9fa091677d6ab94a4b4b70007b0f9dde3e39e8872e26aa45728f 4df0c76750ab765544c299a9c2709a742e25b5a14cf752028c213c9e52e0e196 44784df61b7a5eb439aa83c451730f17a5b57cfedf53df9503b88dd5f2331d29 20ee06829df8aee2b1888fa6b33cda080d39be55288a81e3d460f25ce4c80a6c b598c88bbcbc8fbc9911031ba83d986a738939e940513eceaaf6601bdc9b4a80 6d1eb3f67893409e2371091384214177001252635d03db7edfd26f16b99d40eb 871b65ad28984e3fd9e90f9c5635857b4fec55a80356fe0c2f39865eb2da7ec1 5fffce42eb12c5475c31dcecbbe441b767ef0aeca9fd803a7b6237957c51ed34 dab311fc769661afc3919a65d16356cb064667af3b618edd499aad3fbff337cd 614f983f017483b196e374e6adec6441be9e1a5a0bdd332460052d3c9ea89104 de7a6852e925ff89b7f61ac765f8d8309b5d632b16fd172935bd8adc1e2cf005 81fe900d98a0b11d0aa8ccbd80b13c81791e7f57cee394800ecbe07db1e06ff6 fc49bf59cc01263454b048baf0df3f83d8c7add94dcba83382ed8a64df99bb90 cfc6bd3ddb55a4287d4f1f828651d7b7b8ef7a43bd6cfb5accb7e058d538746d 007c063805f4059c97a09fc3a678a356e70debf62c33785831213fea85f92866 ff52b7782b7117ecd9becc8c65ee745f1e76077a23691d99da2ee8d8e4a6b297 84710897b8cb2050c1c66f484b006bd75a90df9cc5043befccd122568413b7aa 6e1f93313570e61e314d09e9ae84032598edcee3fd623df1045cfb89e309afa2 34551eae9a1a836ac3e84928ee06beb00e21835ba54c9d4bb2cdc276732e0d96 1d48931290fb5df0d8eb7969c69a3de75af5e7e03d505d4b25966dc14471733b 6c646ba3c38cbaad0709f253644cb61c689184c501e90ecdecb20f8da3ad7754 03cbded6880367523cd72d4dd457cda3f2b99ce8bb5f67aadc660e00d42526a3 e16273e7fb72b85f3740c3461d4674bb6b2f58a1f9c8c8046e8def700541a6d4 22b0302ccd9ccf6897a31c22abb46638a3e8e300568cbd6cecb00ee6ada7a155 5479bc10355d3f3baf47d276d341afa6ec84f084a7d58015576ec0f6aeab40e1 fe65062283a55e4e3e7ce41f7b7bc0ecdafd9d67936732e2448b8d6dcfca1e86 00f63397bc8063fccfaf8e0c684f3692d20c3de02abf76823f2d9ab748cb61c6 3569c0f98c8b1277bb90ed864c7b46e9e9fad108cf22f5bb6ea449d041bd7d71 85bcfa0861dfceb9c31abf57a239d810ee086b940b99a7fb37ec012b88cc33eb a088e4c7ce225a1dd346a97ef7a3c0586bfe272eb5681b8d6bdadccf476dc25f a0baa8ba3eb3f400c4a10cd44ebb5f640c2b0ebafd385d0209bd594a98473b0c 8fc0212db8221a465dfe71511b2afb1f35be5abccb6b0ada065a52c92a0f2b37 a3e468c3c57d53530df622b72576d55fc2a5c329d4a60e110379c2e228d63acf a94e63577ab74ca07b92b582b74fe04f4b7c81dfd9c339c080c552cdb511eafd 5f6ac243ab6105d98e7a51cc01cea5cee68cecd015e9f61bee355942678254ff 4b799d372809bbcd9b4a8c9765a3bcbede3bcb598655b6e9daa7f4ceaada3ab8 c1ee82a920d2c5e64b2f3469b56dde3cce5dd334f137b81c15c5e2d979b7a8e1 3c0ee3744fa5659f10f7bbeef5bad8076fede6d191b6158a334c6896c5a7d1e9 f0091ac70f7f58064c76ed7400a88601a860e09f4c1806a26a08f2e9aae40ce8 4696dc0e6dfd2ea5249436bfb8bd3495f2c6e2e1170e38f9ceb4235f5ff4a64f d3d1ac4434980c3f35d02f016c8a7334628da375ecf9923e257ebdf49bc0b62d 85cad285d719283f8b2df817aa49aeeab40bdf1f058b2f4fff33aa901414ef32 48be9329e52f0fabfb88fd9eaac9b705239bc0559d59f3a25a22f437ba5cea7b 6ef8cc1759690bec9c54a015b02ce1f860e314f2efa84bc2a58a4340bf2a859b 12ae2b17bd4c607328559aee3312a7fceb11c3f28497f8a90b0c9bf18843ec71 c881efaca0865554c1fccf5c2af91b2b3f99140d74406883e5c9006577748ddc cdd334e907c6fd8500d6b402a956441933e598f9073f2742d773a53929e58b11 39177d3f3ef0a8fc8881105918798dcabb9c3dfb4197b555481175ced3720d3f 48f46c684db10ef67e428dd0ad0048c9824bef73cef432e43abbce589151b84f 029894e23a655dfe17857df68b85819b8af1223eaae7d078a3ca4e155c92c58d d9554c0c0f2748caacee7b7b3d9b0872af5b6fff044128dc8e9582bc7940d982 ed5efd5b8a1b121e5ab53266849a617abb1173cff289c0ebc133a4f59ff28e44 c0fc5308e969004a04097b10d2783d3221721b7088b2bc4d45d8db7ce0e6205b dd6e3cdb6281b67d9d8e8437979521fd6e0433957210b2756c69ee2768505e56 6f66554e111ece74a3dc35be365efb4d7d5a95a82f1d4429d6a3c2ab2cc9834b d24eab5eff7270cb40fdc620dbc3a224c26575c70b039fa7042729737ca17c0e 8d3c6bc9bf9e96bbb06320779049a633ddb42ce331c45e38339ef21a67fc460f 8190d3a3c90a1c7244f691c8fe506b05cd0e85dc3bb16cf19bbd9be0e099bb6a 22787098161c1d4fb63cacd59f0c538637436f52648906e95b5faa2010553584 d5624b548acf6c6c88c69584b6a4734f83883fc3e78d6f3fefe9179cb1811213 c234c08da070769be293583c638f04eda2111d4cdf0a3b6f616220b11b405a34 0b40259fb4ef668b61739cdf721e7617c4358be60e93018ff5b01163b64ca1a8 42176dee65bde9bde033ab96c0cfa7a649910c72d2b7f5dde71231a1071004d9 9b137bf55ad77652dad0e14ce1477ae7107bdbbc687e75382a556c2b9bb8b913 6f559d5b4e4071e7b88df397f689f0aab62f0bb3e9ccdf7730d68ed9db281553 a54872394a874eb4f80e1b28e639cf7c8bd59293a278ac791a748022aff05e23 a59a8705174031faeea0cbe6ab6ab83d6d5c54cf521669f79d89bc580d78f514 c3ff24679361f8d6b37ed7867085de611475a48af92aebc92569b6b594ede367 88200bb4ee76a18bd2065dfe0a632c451b35dded391828e018f501c453165376 3019aca47de15ba578cc27a735d51e59c4c55deb34cfe0c0b761947595424c18 eb907474cbf1f587cdcde446bda1bab430e267cf83534fe78d5747867bd595b7 95bdb12925342824d09ce53b9bfb5316d9710554e48ad880256e59659785368f 2c1a03141f8e2fea2596c2263a7d927da6918231d99c12d228bb9e3740f2af12 bc384fc2cc815271db4221425edce7dfcc78330de86db13a1fdca4ba5a274102 36d1bd93e872e7c594d5a0736a1a9e7d8261a9d31d77d6167683880b6b0e3121 5b56b6cb78e27bee22542a2829c249b34bb814f4cc5372373819f904cef359bc 40621c1d141609ca4e8a425f180f56c004783f5dde5713dd2903b6539eea5f27 92c81270a8b1fd324c47075dd651abc314d099fd55bdf9154d8861346456bb90 1002f94bbf0552fc17f760bd2718740e9d6b0579f99454dc8f5efed7ff87463b e8450c509f5ce17007f444a852a0c837ce9f9ea01169fa961cfd827af5443344 a217a4a1d65528cb593d9d4db5af430d67af4801be879dbe68e322adfb329642 2665a02190b283f33827644a277ca9da1ba4a6d825b1e1d1e0908d1ff842b175 8a2b7079e8b674a016d948d571b34f455c3b7f4e47e019aef6b670eaa9ae036e aeddf32334d4f85532369419b6543088e897a878dff4e9ae780f024858437dee 317c7bf738951941f67603e3eb35761ae0e30d1ca87e98ab56054ffd2da5f33f e1b7fb6c2ec6bbc8be35cc3897bf60cc20efd550ee418270744c67377b49a5b3 614e793f5d9c3510c64376ac06db249dd9f364ec045a2c0bb24ea080f037bb70 0471a8d88c102f73aebc52658c447475289d3c582a1582e4dc9847f6757570f7 cd8c95055ef12335d8906da6f478a9feb7ddd56ac0fb40861d24b9c3330f3c53 9d9360bbe89d41b4a01882286c8ba51a7cb3f8ebb3149216ead48253f783bbea cce599487d3fcb91c109fc6fc52c9938e4a72af58484b515ace408ff399db082 b576423526130d770fd58de7321cdd3803b55689bb972c96eea00cc6ff9b9a7f 658d14fef9929d576dce7492df7a569cf135347cd977c9864f1e40afbf92f2ca f27cfa892cea4ae5ae44b8c5ca52aa4ca6373de9817dab3d0ac38465b07eb68d 9ee14c4680ddc9775547eaf36a807dbda0b2a9a1fad7cd5a2919f03edeeaa5ea 423ad0e2bbab601259a31d1eeea1e449c71282663444965ed4b88bcdcc06e84a 77f1a7e4ac8ed916db9ac731f9b281012ea56c1026c6e805bb40eda4530ea10a 2da8667e97c8ab9e8911b6bedff0654e808f68106ddd88a27554e25e1a81c4be 2235d6c8e7e18c8e346198ba7f89a4560ad5f01e013182aa6a87c23be37fda42 be8369117d0e0daaf24eacbf3ae56ff41ab208347a6a5b6705733a922283c86e bfa2686dd9439068a977d763b712c0326a75d6bb93622e529cc993454e3cf31e c7d9937bffdbc9504dac5703834cd56cf7445035c7695ce846b8b301077a6de2 77959bbf05d541b81a617a1730411f1d1a391ea385976e87ea2e8d0934d69527 4b150b587138840da201e9f856c9ad21d231fe89fc86561fb3cd5d0208b43362 93f1ed08cea8c2bc1252b05dcbee139e4f021ab3b924dfaa882b9ef2a8239b31 010f0ca38a69129fe4c70eaa91aa1bbcafcd1c75c4ca75369a39834abeba12ea f74eb6f37dcf1f6080ea2f203e013b674e2cfba1fcb30a08a9bc44173398e856 da72ca2931e0e1cd452f607353a094dbb282f6fd2119f2dd7cb050afdce3609f d5ef50130c4c8435a65754d2e876294ad8dda44c0b2d8693dda61bcd43317f14 14472f9cfdc3a4b118722a1ea504e7fcdda3f220c53a52715ab77adb561e496d 3eed3e8fdb79ac013045ddbda6f80b3ffffaa3e492c5f55093427c2410feb6af 2275a92d24f4e8d5f25f757b1d6666984d35e886b577905b1a64963400dda32b d4e7fb2d327a7693e358ddad0334e9ef285766a207295a8832f953a01c8beffd c8ae37d7cabe165d238ba955820bd4a5b62a9cf0727e5a949c964663df3c086b 8baa6fdf1503f942a1177550aa2d4bca5f40d8872a022535d92b1868d36eec96 cf608c664cbaa6ba583e9eba2afee7e0885ea7eb3a5c700bd576288d6169b150 8fd58215e6bc0f573c69deebe830a6c5a078db95aa3e775bdcd74166b4fdc7e3 92dc54cea090309f075739f731b7d22eb8d7cbc2d863e2ed754adc5cbc0117f0 75b2d6c24e852258b3ba81673e98193de875779359fa5c94538ef8d19f771979 7fd2d37aff341d098cc6cc1721e671c6463a99c3ec14ab976ff817f76ebf7265 153cd3b6b8468d930c7acc1fec7a1b1422c10e1e1e51e57ba81064fb41674604 965ea235e8820148b4e67951c8c9cd002200f53c8547dfa3781717284e1dc7fc 9aa0c042c64c5039e609bcf7c1a33d0e163c61718d5542e8765db6cd9ad62c5c a56b66ffb04b6de482e0b5371495beab59c8d9dc9b76cbbe93cf2ac32d70b682 fe36af8c0f0853ca10781754730e4348479684c4d68036e540482585cfa2504e d55af413a247d527f1573bd8b247e083242c7a07f88181c72cb070b3a080927e 482f5c744eda3afc5e1642e53d9ee661eb2a4dc75bf8e1c63c90505c5be7fa55 960c4c38aeda8714548b1c9d901934efd968dde9014fcd6b0da5a7121ad90d3e b0c81379ca8d12843ac1ba556a6ff7258139f9d4b203d3a0e1b96db64461e6a6 8d248a27a189356baa1022161153672005b0b74cc74070e27d23d4deb01e468a 4ec2e909a7e7f64c451cd002052c7de618a27ed3057342380285ff9a2382c6f5 530f3c1fd5368ae1051e8d97df1a2d1345c7017a181889560195a90921b88127 4937d542a8ca65e8371231cd7e4e13c7a71bbde3a54a934a3fcc9316a39d94e5 3a664ab2a85e15299670a8141280ca1e5c97eac65e4b0518994648053fe1e79b 7ad426cb116b8a95ec4c8c8416b71b10d9073ee20d28bd6fbcfdd557faae13d2 6e93595c319ec3f2efd8396f6f6644709fc069d3bd013d1bf3af6963198675d8 ae227b7f4495f4a2a2261afb434c5907295badcbff867e16e59e5284b0cbf9e6 f163e65cb546244bcdf2e271ab0ee62cf94ce4480de8fcc274224dd68dfb3165 8567bb12ab6772890adac0b2efc83d3626ce1e92342121c6dffc2bcf1b695109 e2315f1693e5d449ba3725ab203298c5d1248da8ca61ff53c5ca3e65be9097f1 92007b3811851eedf14e04c33bd5f399d17db2ae0aeb63b1b4c66665ccb59ab4 f7b79c944cca90f6ed2f4f677fe05a9a83b556a82a6dd55b8b6bbe047f9e9e4d 512f5d1d5b4f5e9809f0838c0beba560b39dba4335deeb9a674363b373aeff2c 9087f096cba590c70d7f48396c4e9f615b74ba0ad032ff3fe4033f6e736717da 934f3739060324e1e3e0113815ff101f6b30393c8f07844913c816e4410bdb6f 364e764c2b7b8e954bdc010c701242acd055a47ab23e3d67795b4efaca8207af 13a15b6039d4afd9fcb15be5067529e9066907a9d2d925c02760ee116ecb3268 1cc4a558343d19973ed254398259b7b4a53b98d6856061681797e132d4de8e79 3d38d83c9b782dda16f87e6301cf7ffcbd43d1a95c45a542a0f58747e801643a d5c680813e6e61579981bf995007e8b3580bf77fd75cd4047a1cca466e22edd3 d93dce8d6748951167ce0a0377898257bd8eaf7443ff2d91f6e2e26f36c9926d 15f76cc9b74e8e595a5d2d536743fd2ec9154298c99dd92d751d2f51563d3619 a03a9f76205d2ff3450b34df6ef4322667689ddf97f3440570bbf53e1d7419bf 1eee7678708adf6a5ccf25be3bb9e778fc8430c2ed9c2d6b9dc4e1423032a0df 795ca0bf075ea02c1705c1e50e164518754c412a87d3be2e8d200a00c8606f9e 4893fcea2888a5846da225d3d5ed42d5b7c7bbdd09f2a547ed47b384c6fcac2e 84debf4c6d48757415d878ff79b2b2d6eee891985aaf3192ebcd8b74bb73664c c9d867ab2e1852c3689c23b0cae130b694f5f1a6e96fb26ae8d1f1ea633cbce7 11050af3125002ca70d8dda2441309687323e1e7e9aee34cececb1a3db828454 d9664869b0e75830904457adcee41c90b667a2ecdc628b54489d52d94d33bca5 4aee0b499912363e58f14eb6d91d59105e2a271cb480e97f4a2a8f7c3a9542ff 7eb101945064adf0277c8d51d1eea97306b011f6fef8bc91a90e7b8c683a5a14 0cf939cf22b264a0b2f9f955b2ae33e2a91747c9152d97948053fc5c4d5e6362 655f319a53bc6c797af4780bffad6fafa19c52bb9bba9ec5cbd91b2a48893795 ab82b35588bdeab76ee638e934936a45d1bfd47e3d815acce299d1626f245cd0 97f7d109f5ddf0bab8cba6eaea5d25eec44971356eb1ea32af6afcbbdc43b4a9 3a04386821da1ac8fdead5c53cc8e54af65bd3cbdc5b3f0713a2e800c47f2e24 99fc9eb7c216d29214c43ba4250d01264456f2c1d4e482c01779af8ee7a67da4 e61b71ca0199e549f6d8651f6eb9a992f35efe04b8d70029e31b263dee31ece4 8ff9b8eaed947388063c0733323e38d6228ddcbe4688a9b736ae8a6121df6224 1a6349072c27e30aae97349ae822ba5559c67c4c43b34659ef41f97976017c8e c0eb81d2431375a89ba49e5c10b6c45cc5b2cf1d3e318e8ebfebf4b434fcf1e9 8f6718f5bbc029f3dae46282fe2bc97316fb2149dd6a15b928df8f5141a3a560 8ed69c47fcf3fca40679ca5946400fb0c3fcbd615e84b075a3a020858340ca9d dc5f0bf7261ef61f2399b1ec4c96963f6ddfb1407672508bbb91259c975a818d 14ec4622fd5f76efefa7bfeb070ced98c7c8528ddb2d50e9b3209f1943c81770 6de5b17dec6d7ef44e127c2851d03bfb20f7591cafaba2c3a3ddb1de1cdca4af ea395955855b7e7e513b4f7acedf6215e8020c3874a80ea3e4d2d194121878047d6ab23b138d2db6a3dd3d69ca8edf9a188423335cb9435ef0b58523f9fc69064d3a94724004e46cc69024108ce679cf19f5cad2d7d7e151768ad1b7f094f60645ecfbf1cbd0a2019abcac0a89942b270771211fe66242630b1e8ce9bd384c0ff44b2cb71ee55eba78257257a9ec609fa70610ab5a6bad7a3364d2502cd17c03c9f30a8f8148c58373cc13853a6ffda9361245882478943f5dae410e6ea5860a6761ac833dd49848e7dfc2732f345aa10fe87ba63065db92345e7451e16ceb0b565003a07a261020637f57fa8e3bee56172de0cd733e17bbd5fd697f9129da0ec3f6da9267649279a3bd3024a0f7bd6fc5e8d634854801c4a80b66750aa3380ded2597ccb8773a7ed901f1470f60877b81dec7a3d24ebf0c55b075e60b27730aba0dd185f9eb8b7817af10f42d0a0c9a7bc33e026eef59da7c9fe9dc05f7bb0d51ee54da41a0732fcf9f61b41a9ff44194990cad397c573626cdb3b267674e017a6a975448712d252330e34e0957313a1804ae2a006ba752c0d864892d79d8021eff77854bb31ffa1bd64567a443917304dd4d074e475c98e44fb222ce448a0748ffad353743502def9d6a56a3e60835179018ef8b0cb9c8afa37b4402352c0eb10a98f888b3af9de5d5b61400130e2eb7e47f1bd6a23fc445a17fcfb1fae00b95fffd7d441003563af4787e4b1c13a57e2c1e713a1bbd06d897e702e935540a71e22dbef16706e5a80e9e34e6672d96564c38a5be2049ce951001178fc2b8045c96ecaacb4663a2169930a2ab3033c92e0c61af8ac6fdfa699a4f2a4b4c4804e81b643c8278435eb6189659568e5a242fdd4d6e4fca1825d65e15c12282b30abb130ff1259087b31fcc9ac23ae151d27c0996a9537fd319f780c80f677aa70760eaa66ffcf9c48c940aea7d67c4c49cbee8fae6448d02188abbdcb575fc3407f8eda56cdcfad71159cd9194b039824ed8c076da44dc40f3327ba6d00641c70d348445c743cba1097d7446ba539e1712c9cf62359a8f99f331a76af6bc89320c1fc9e90bad6410d017084b9241ffc087442e3b3c97332c6d26d2c37ff4f6260e24f9aaab6fd4bd76dc2c0dd516376a508855bad5d004c857bfbb84dcdf51d0c9cd303dfb59291bfe915893bdb085fbb22fc1f99998750d835dbf474cfaba080ff2b1aa2068f728fba6148dc0f4ef91a9ebbbb2a485221aabbb9add02c08e8e093e2ad66cb25d4f194029d61e2883ca96c713ad03df21a5429c5e3e817cfc1e00d61f9f20f2581053f559a047d9151a745a707433a9f193032c15d63620b55807a2c199aa078512329780d6b9b317964c3c2da6592945a1959136acda6738f20bbd56c0d973515df344dd107c066b19fbde4fef817d2b584340d99a97098d1c026fdce928432f8c3fef43da01d82a4f61b3d249c18ed0ca7ab7a727954a0b8f0c75cf71dea83f3ccfa258d267c24e86cdc0c39955e7d458e314e6f1b6bad9680c7a75768ccec7f4458fc2bceed038362e5baeff84db87ec3686bae7ac4022d40d79733a4469351d13801794c38f5afb262e3c1a68dee0d8e14c7d14bbb4518f00e1dbe479e15d625bb879508346ecf3689722933ede986eda6717f7a6e9be370225bf34785a4ab749719c52c330c5af526a6a371d3978d75e7d1e1b64d2f83e0834dd2a68cd1faee60693c88f0cea331874a589a933c91204d1fa769a325af8013c471de28c629440c773c15043b1d93c0e28dd246ed85f48e791a92132fc150afc02b729d05866b76697d428012339372246f3ac5d74aa00efda7ebfafa8d1064d14cc91f884b0929326b3c8da12cc9290db6eb3ae9c999584ea2da935d23a075f4c8cb10bc4cb5b841f9d74fc69021a554d4a1f3ca6efe0578af2efb8825c043059e73810af218f5471201d0cc5524d1be57ae45f6178dc0b6951b235540702d269dab9366a3b03ceb95ce2a8df029c92d1d95c4cba17e09f7f67aa6d6d4f023266fb01b796dd58110d55d945609e4db5cc6f3de83d508a45ba1b0515467b0153ac7760e3ac49c85572b23f4a7908726d6f640a28b8edce0ef8d5657d474b0a853f7e2e5df6c41fdf78d32cfa4914ef98d2e9828cd1f604c9a6a2c06bf56808a60174a602163c7f049e41047805cb4e7101dbb523ed2ab50928c81c9522180e142d58ac9cd9f9e93b1ed18f094eeb7c3bf62e1c2eab57bc6c88b0e1a3a890096fb1d1b5b339f0035d0db4f9d43734554c81cc5f3c36072adb32c4d3bccc2e00fd25552c78e6acfdfe72950166173b81aa73e77856ff560dba76979303e7f407591126a3a73c94c95888428a45929eca8f7109ae0f221a083e9b85015042300675fbb84f5f4574a3b68e5a28024569102f83a44977f62f1cf23563da4e697c05052b68b9842755f3b49e8841516f8b599496ef10b13f9b44125a3f4d0bd8730ec9e4502c6a8af3a61b9457d98c1bd92ceddd85a7310fee453258b863ae3ef60ee1ff44348ca08621a45cd0a1bdf7e200e9741f1dbbdcf119092a5612cfdec507e98e0d77c1c08084c295837244b2aa5551db231864ce049d0801eae3b79803050da2ea75626aa8fa5cc1e6b3d96a3eea48f58675c146043af6aaae95f29a16b0b480a0bd3ee19895edc911d8a1e6d1b5ef8e3303e0cdcd79b401467f9753c4026b3c85123a61486e75f5a2abefe300e88c403124d171fd9b16c9c9cf6ae1d700e460854aee779df30f7367269c2fb9c8362639470d1f12f2e5d15547c39ca802ab691cefa251de7349b3de8f8fe49aeadaa32829dd8164f8d3cae2eb960a2d025ac7a1ea4553db0c55663f371e05c9267cc92c01827a832e9c291e05a845cd0808ca8ed9ad9c141347123fac9c04a2f33d4f41c9891cb87eb9a97ad9ab7af80a53194ddee15c0909211aec1bc7aa62b90bd7b97e1dee87011d78febd3164a80205a39795fe4709a562ae6b69867f1fc96a6e99e1eb2b35747368be4fafd54e0fb00dc695db90a84b4be72ddc148bd5285c6d380c547dc60fbb8b546e40fe990e76f7cb8fe9d198e84449a4d4edbc1f8815f122033ea4fb698cc496e235169d0c1b3ea2ea6eec166f7f788412e9c58c0131860085aa147fa2eb1941394abfa9062c9085876e5523dffce5263cd4612c999d2ac1ad643bdfca28933d2bb9069b066866b94e2e8528cb7af68c7aedd42ee79b0a59c768ffbaa4a92591339b329f074bd6e4249066676188ea9c1b603d7e3c664d001c40e81d5f43432520c33f690af200cf4ec9ad498ee22b9a2a75cca31b1f5ff47220f5dcd4a71f5cd1cc2c9a031e10a78d28fc0e99cf51abe52f82f33c8e9485a23575a7c9249a7c53ffaf31035f9e7da0c62ff136dc92f4e0c99817205a36e80ed1fc543582e1c4b6143a1f0cd5e49e27216dbda70fd6c75aa8f7025644faeb4f9e110c85abf114eaf45a7b0ae466d0af4e76e20e5331df157c1f3dc74844a4a23bdf7c2f1c7baea45d793508468d5a24cd4bca1a280d0f43b49e72963cbc056173e91028b32d0d5e669733043f8861f2613ce572ae36007c92ffdc7c005175490855ccf62b36d4711746a50d806aadd85560068026ac0434b7f10c216b6635c248517e87e524194d1bbfc5067c7a5a3795db7d1769d5af895956a936edc1d8bb99bb2060c1e11bb2aee94d077816afc5796584fc1a5cfc2bea26849fb846f156d82697483cc04e6e646f880f7370fe80565264d64410703fd0d8f9ff91b34221c811a4ba30cdd3814dea7200fcdcae2b66aaa3716d1d2b5b5fd39bfbe82b4b90f3bdb6590fc21db75449f80bd28ca3123f7dbbb67aa3f206c687b341079033f2db248179cb9b8a477b7a9e050613c2a616586292caf65d11b28dade956a89d1c19f9a05642c5819a7c6ca60067f1167dbba7f93bc8c1f05c64066848711ab7505566b4481436c7fd2ce6b2097e2c3b60a9ac87e2ff15d0df62bcb46eaf08b8e6d08e16c96783d6690f5b23058e6603c362b8ed7387dc55bc745c362b4a1876bde42db1966e4c1eb69610630ef71dade6d6cc90d000071c67b09d434f9764ced8453891abd55acbbd4b89d4010e267a648543a24fcfe875190fe14cfff76fa3f5e0269d70a22f37254f269806a1657a6f5780f87ba4504b8c9897a4ad76ec37d2baac95adcc58180d7273510a47f7d04df062c027950a69a5316c7407e3aac3b9801102ea3f7d4003e2698b0d20da0afd1828bf58658e53df57aa419bdef841847947c99f069bc326bc4b5801ac723e7da8b4cff53936f9dc31ca2f712fa40f4e94d7f7ddfe3a63ae00b2ac0679d530e23ebd7a9174a45779fdcd493a987aab6535ec22628e3c0beb7fc06f0a6f431b1182aaacdf33ecd3192106e965fa94fa1f0a4639c56a9b52754a8934016afceaf5632a1c757b7ad1fbe1f73aa40f4cffa277cbfb5ef94e3f6f8ad1ee074fbb88adccf4340115d97e38b8595150b5e6e545eae56181a81a5598dc365706c77dd4cfa25600491305f4b9c26e2e7339b417c2efea97e9ef4a47b05eed610053b7da6ca9e29e1afbb68b46e81ba32de8aa40778596bd84343db23bbe6fde03fac04c337ffe6fc463b45302b8176d9a444a77114499132327641c90b1fcf8049ed4d1e2f703973164844f838cf2d202f30348b6b20d366cc1cbe1f00bb41a0504dc36d43632b498291bc46fe0d602459fbfaeae22d3b384181c1a78d0c69a0f24433240d764aa73a63238151c8226b3c1190a9fb2f405369ebadd2ccda78c0cba92f566a372b31bbd0cf799000027a7c3888df75cced1cdf5123f582fe6b30d1a882145cd7f4dedf31841015afa5484c716789cd4e27fbd859fe81da892f609afe2aa2ceadfcfa7507e6a0febbed5b4779f90483a2a078551bde020988b730e5fd2dc0b1099cf7cbfbb017a5c1cb3c9d198c775dbe6c1f2496b2c74133ea0006d58e5e2afc864ea726caa35159d4856076e05b3ef58dfc1a7d613568ae0ba0d5475c858f95b1a6196aad55b6052988a0bfffd950eaf16a3466d3d70dab7510f6bb5a6a3e8995940c8c03a3e19f99ba62f639e4cf78189155bb746421045dc04b5d79761909154e290e118f777fd9af805b656e7f5961b67e185b5c437a94002a3528bfc0262c088e071bec3573ba1ee5e5b4bcc7cf8d4cd876d4edf651b35030182d2136dfb4804bbe224c9faa27fdccdfef7b81ee0b739cfdb34a0856847038a6cc0f1fd4e44644f9e323d90455eaa5684003685000756f67f489299de490afb696eb1f70240a4f9621921d3f8bdba4a60a4c6681932f3b2db7c612983a30445d1d80a745ae62c34fc81b7192c98b79151a0b05e6a2bde49a935e09c44dd0c33e72f3c5d9a36a0e008ee039b882dc2162301235733a8c71faade4792b4d9050fd9164e35c4862972e197e44366f9d38c0f06d11d0dfaad6eb0c3cb6662b609206ed8b440171a4b09902a55d96637c1acdac1498d17f5ade422fd5037ac8603901af33f816de126ad0027d010892a47b5a7cf3dfc5abfdab3da1bb15fcb4c0c7840626819dd423e9c4b25891ce29bbd2317a6ba6247b6af66bb05b1749005038b62daab501e065e149ccf582f67978deb7e54f884f07cf6e593a269d619430c159e8a060b75f013021dad548c47f019ebd5c83d46a44c71d1eba98a71d6270088d1f534d6abc11fe1da372088acd9142d436e88d4247b7c924691fa63ebfc09dab52b9c554d148721ffd4c4558a58da5e77a57e03cc57902f3317972aa0f20caca5963e668ab5ccfeadd430b345764c2e98ae802d9b7d47e5b53a410bd5440a2f3b9f4d6cb57ab92578a84f7b7d55b5bbd4641d6ae39527dc773e64af59be042517b8534bac92ab128080b2fe3392227841c517a12d9cdf6db829ba6f63be0ad7cd80d971fb66f66de1d8cb34d62be027a01604a346070229ff04fb4bbc360667ad1edc040f7604b807036c151105fcffa4777a9512239b4fc5b0a518e0de058dd3a580328ca181acca6ea8af05397fee3e76d610d055330d58d98d86bc500aa8079e40629dd6221c0316eee15db7dabfc824825af1017ce6018546bb5e590d3a2dc6e29a758dfb1dae539e404e313c53219d2130f821e82503814b31f5bb0c46c45c77ecaad9bb67e62a7b9fca2e4cb95f5be0b7bbdb9cf3afbdd493000301c8f840a9d5c5b3225f6b2ec2730526dcb1826070dad700737387e5993238a0a368a3abdbd119eee01bfc64239be450153157d39a4b7f1827ecedff1d4be1b20b70ed5d8f12788e941c7919e30d25b57e6186b8e956f3614f8e6eebb15812480a3c3e3a6b21bcd18051d3c381a7556f182d00d53ef8b4f77d89fdfbe12ec65005a324e77129d79fea2280e92596c6428510932a824099bafa1f4d34bcfc7dab00ddff97d99a95abfa9c3b08668c5a56a104cfc7c03cef7697edfaf38db404cc0cc0fd1d1a8ddabe94d1b9fe6f0b4a6b71a51482eae93d5bfa6d99749076e2ca0a361ccb019049e51c1e0a9f50f440b3f404614aff61d38521630f0ab99172920fbbbed3e8d0f79b1705d1041592e891dcf2ff653c5848116334043a269d762100f8c22909b75cbb51d980668a9d791389607cbc0711ce986958cfbdec176f88044758dc3ea42e7751b3f347526c7512b8b5882eb0a1816bd22d3c2784625f140711297c05f3a446a44ea11eb076ca41a440f8885128d58e69b468ecb7de182b036c9cfb86f35e0d8b31e48d33e526cd47b9c053a214d7ed7f88ef4da09224e102493e73a75be7db97814dcb41f65b68219e56f8fb563507d6f31d5c7798000f027547df70e3d5d09ab2d9ca93236ca62db3fa70356521f30aaf629c54ee893e0ab80680524e24167c340d317c3a4f18898429df0a2bf173f39c94c4069d62d90f762a8161a72a80a3f5ca0c7694de5b8a0ea6679078d463afc833a66e73a5df0872392fe33454910fa34a3948e843d2da9bb715185535605124328e1d4a0683055a2bd9b2fa8eacc744a2802672bf9c6a8ce44c516e1e0c669c62e835beb096072c1da2cea631624834a5004d8067cbd0c2f0a05050b9611e8db199d5dcd4ff01ca9b2cf9377574eadf6c752b5406eeac40ebeb3fb76f698a59a1b5b9fc25ef0e3688e51f54e9300c9e3a65c3021d41ffe3ebbfd755d04b6d4612b446f949b607d7cbd71f1ca13c939b3c27f8ee5bd4653645ee69232afbe5a7411353db09720087c07a864cd17e40ebf95e9bb2a74aead6515c6dc0869c1a76f505a96897420de29a9c1ab7ac94aa160316f33079315166f84ac30a7048124dc55f2044150006c48058599c03176d1bc03afafc93177ed95dece3cbecddff36f00b96e93100097dbd99c4bf1e663c8d30776897e5eb57a721f996df65049210caa5da618f7f0cc0fbf82323f9164639e0be71c4bf84e9138e3c3a637bb4372cc4361a5df8ce0c4acd6cf22828a1849126e3a41c118cd7f7acaa058068745b55c92e43c7829907091013442e024aa30196b38bcb5b49451f5dcebe4bdfd91a1832eb0c6f7df00a26165343103754466eb79de72d09e5a9a0cef6cdbdf8c75b6afa6a1b98685a05796e784bb4118a120c318562ae407919fe2e0f1542d481c1e3ef68b64de2490d6a94b25aa174f70733726b6293a5623aef5ed6ce5dd9d7da7fe0fb3a7588e30498837eda116d3fbfb977d292a12571f7c71e10948826779fc54173dd031bde012c9acf9696f836f03eb5dd77f080fde4ee77c22049dfb6137dc04d2778099b0c36fa44f420462f518e06366a219fe81198ea34279ba20e78d837173ac57eed071787e7fd9a39cf2e3d1c9dea308f2796546193da5e7f68ddfc1b5bcf06937d0a59cce820527a1d30d83a0ed43478b31a973abbe9cd7f1ada136de748b677940d9b173fa4e8bb79d47ce0b531cfad1d33f2b806505a96a21dd52493a536ddca0167d93bed9391dae46622011d82b5659d810763419703beb994077f4d5e90210c7dafee03661f70fa7def015b7edc2a7d8991929ed0e45dff4ea743d13412ef0fe58eeb0f761efc7db6c70b41076f934a5450cbe68f58514a5341fe288b81c107158843a60cd8f6754ed47760de03239569252cd91d2f40eeb577bc1c309d0b0d67e94bd1dcc537edb5163d7771b3f3c68372546f907d0feadd6fa099f8e4950a03c4264f26d734a58eddae35e521a9a44d02cd314f777cc1b6c4fcd54b66db0c3dc0be3ce2d8090b6712d2abc389113b86c1320e1a17511feba3d9186ce8710e9802a9c6dd6009b16dff0375489629b70624155f39c09c4dee3a8cc34752b207145f3572a4fefe7d594e5fb5fb8fa8bef7d83b8dbfafe2c5fdaae6ac7d12370e04c6feff16cb1fe4fa50ed7c091d465a45161071a62096df7d278e33b4d2ee0972b66ed97f34b40d8e83ddc1ea9ab8f909bc314bf69c404f3cb634835bc54401b6e2bb05702230ccc3694b8de1c12d1c7b54bdb9f7731fa4c911fd2072afbf01d5f6c6e30f2f1c77467f62f978a07f4949139a85004bcc60635813c6d7c222084034ed1a176e23622f16cd87dc1bce7d24a55d873a27d49092016ca4de94f00ecc96eeffbca1df6e170b005c554213ee1621f9aaa6c0af2e06fcb6c9f105f10a1c4ce6ff0b1096a4eedb2cb26b9e9b9734718a99edad0b665659ee3ea28efb0c4ce2bf6976843e385f3876723c9771361eee57ce3aab195807506c04d0b1ce029fed2319fe20f45712c6c5aa45b2444e21b5aa72f4084cabc1ccc1f58634cf0e720e36e729dd18e8bddfeb5f60e8f612995647ab7217fb9b4f1f7391e4d710022e950353d2b677d6989187bf822c377faa9c1b37d844aa8c6878ce4f49af14000c2f7a1d102b59feeabdcaffde595a32886439ff5d1d7cefa409a12aa0e9110c45b9023746f4452008530149bb025129884666b0b471682362b3f7b9a883950cc6c1a564fa6ff052660a0e8e81d11ac0ecad7753cd2d0e174252fa8079ab3509d5665406dea568410a584a505669056d34d02d7e612cd9e2e9ded25bf235b70feedf896b17f30de391a961be3ec7785d9c9c0c1b2581b0e39be9cb03e8805407d5b9d5771da38b5275caf7d2a997491ecf0a3e59dcc2c690fc46ecdf896e6006e689c453a094e9bac39942947de9f9118ffa65ed1414474eec55ef26fdf8d70cd82bfc17d3fc0943a0d124573b3e8f26afb2630054e1aa1a7bf37e6c426b240e42f8fb5b9c8c4f2fb3e2b9efa90a98e45ab49c0ab46bce1c2656ce917c3b6d0ac18afed8f90f31ce6f2aff0209eea60485341151de78c9ac20d0d5f3ec362c0541f5053ac00782cdf288e4b5620128fcace9e2a03a46800a499646f8ff70950a23f8b28417cdad1cd4bbbb6d3a9d8f144013a8893b7c7d979d67ff47bc22cf0952cc27ab15c18312bdcc5a7bcc5e8703388a2af8db2e684d5f2a78cd5c42840afdab68984338ede489bb13c2cb5d75ca2ba486ca91a9349b028dbad53f474508b322952a9540606031997a89d30f65901d1df617f724b1ce7228d1bd82da9b04e2103ccb6e33e94a1445d30a208ff9995ebba0bd90f0e9f1593ac633c91ce30229e999d037897368f81f93ef9c2f93767fd66cbd62372b7940df269eb97d870d9f9a995527adb0e86a32a1aa65ef24e18f2922de3c6dc48a918228afce6778002255728ded73461a331bf4b014db844a1881f8336c00098b3db2dcf511bc760dcea384aa30efff81099147d968719f874a163ce4ad6e37fe6951ddcfcabdb20acec9dc7cbb6e875879cd17c4fcb26f5db96f6a6402c6fd059e28c8f4f4a27c0eaab245b7a70543f45a0462ec2ecd8c6b4be47fcbae75eaaa190779a4f744fa0d0d41cb46c4cc40c4a7cadd178e6f152fa2234f4ef65a76bf729d6da0629a530f074ac49eeac5c3c9b387ca786a69c353c6b0336b99dd64779a68ea41e92137070cab91a8157ea4302d9ce689cc3a464d843700a64a3341da2ec672371080a10ea30f3225c9d45aceca8cce6518149a477de4e1c8ce20293c5152047a5bb08b0fb4b488f22141f8ecb25d5f9eb2ee9cab80f43bc85613922b42836da4676a21064764d677c22b2c7f695758b0f9f5c3d634459c8079bb94dbb76d83311c5e290e685139ec009a2d7ea08c0850e357342ba5dfc1456e24855c2069425056bbca0ae966891dd9e84cda3106dfe9d75197206ed8adc07813c0ca5785f4910183da078188de9f7ce4f202c91fc63413479327186528fd64f1148c7d25e412f9c5fb04e738789160ac201a32fba21ab90e67f6c52513479bb0e94eac7e6eaae41b8808d53e0559d10616c0afd267e7bfe8103303106dfc9ae2e3380ed833e1b49c0e0e156076ed9e835546293f2db7aa3bdb6aab98eaaa9d62e35c918dc4c6d4c5bb098cbf5e3924fdc87b816b4f8f4d3e0b294e6d7baf3a1dc52d13ef30a25de80e0c127b15d4cfc3eaf904cf4786a4e5ddbce502cac08f280815e0cdf9cde00080042befb19838c4bc67f5990ccd298aa07ac319b9c4af07adab3118cb2aeec43803c02145fc8b2b93cc02adf6636dbb74c8762da4bb768e56db07dc3f4bc007bc04bcad87d32e99678049c858d0fb69a9bd3745afdf7c40ed4999710eb7a7602a0fb2cdd0231525feb65dfd0d81a1b036daa3aa3437a8a1f82deea3c2a8afb21b059bc53cd2c78ed293d34d4b697701ca08696c5422ac2a45177f36d7e7d41c230701c2b0fb11e63760e0e4e97422b4aad03ef95d7d7a4f9f9f093034336b82810b746bf1f0b03b7589b44c28bc9900f986b6dbd552849631b7c2dea7cc0a1e9e088dfa3b5e19c71676484db34efde00ce947ae71275441e0acaeb756d606c2b30a72d8c9038b8f3ae969c0e9f27a414e2b6199f58940cca10386c76435446df3055bacc5dab3938c48843a6d1a11d87d3cf1861dc3f40713ff48f034a855c7e1075a2177c1b2a8a265e13a3eaa08d364142f468c63380b848dc015ae25387ed20bcaaf65205735219f62bb8149e388b2f41fdc5158653f09aacb839ed72658b403e28f2b936df7a875b87fbdee9f1c0837d45d9f0a7db310c874b8ed41bfdeb501346b30015b8674be211e0b8e9e5d5cbdd524d38bc9b4569d7b8d747c8fc57f49c344c1be38f23bdbcf87114b2fc9c01a201c5bcaecf208923160452b2d024404af9ed688847d852a3c5fe3abfd4c7e5b34d3dda7ad13628b15d778148f7bf004dc28701096643e446a8499abbc24c86fc483c5b4c1b6b3a56123af7a5976c7079a12eb4a58cde32c949ddda0d57021c132d7b4f2cf5f1b41f9889e1e14a3d60eb74019fe1a3be64fe899ecf3aac336730a8f3b654bc98a3560d929a723d4890679907a5a26503cca357f40c602140e53330aab37466a9925b936f518622ac20152090bcc35f633ec5adbf10387aca721b9baaf6c164f3e34df6530790bfc0c05851f4479fe4386c7ea0655e331a12928b10aa2386a1f20b3f677ee900e1b8f084f5872fe355d70bc4339eeb181c9c6d74390c2c70989596614a8d5fe499e8c0b2788c4c6632bcabf2c69ee5abde9fd0ae8664c310fc4e2e6893e7ef3517c880f52ffded91cc94e313ce89381e491e702e851ea9832cb2a6de881ee6779e5a9050d34e9493043405945c17cbd9698049ac6517d7424ee2a7a07f43d1301e47907accd025c6c5b5a90355254ff183e49b817d7c789825976cb17d5cb4051f0160e148dbfccdfecbfd8210242ed016531d814766e5e1b2d3661fdccfa55c7900b09dd454b382065cd96121117f75a57ba4b99bea912308a4369848c719a90f9d10cd3b30dbf2bedd37c146d95eae88df078551175c52c570b46ef97c55b607ac10293890f535093a55bae04c5a59d96e7228f439ee7bc9c32be73fbc30297a86109273c299b7c79283654f133703df20615e4aa7412faafa1239d6d15a38a81c40b546e50ba155acb99cc7301b9183ae4b1cbcc6aefc82c83de34c7b674cc9515010a6de2af837e1e039eacc8cb5bb43a13d596a967d35e4df31c641e07714844064a2573f222acd6c837fb6d40a3162343ff0d15d1aa4e3454240566e3060c6905e81c4a8b5ad8292af311499b1aa9a6fe3e11c2af9f9ccc9ab0a922481064be0999bae0be3f14dbd3b7719c3798c6650ad04a26ad624a3c48a350fa1888e61b0637d85659195321400b99bbf2cf3747d3093d5fb1e4022f4128bb2c6143feda0a30bb5961ed92f7882c4db86d208c789defa1d52caf4f24a76ce46e6a41251a0845fa9fa98e7e44bdd0021e29c7e0a7d3f22d7c8b213f1643080e3e0041cd7b071fee4201b208cf8776d217ef6f5929e18ea093479914000dc93774223482eb05175287eb03995e26996ed255a7d131b283fd3c32c4a0e0e25da788d89dbb7202ccb0ad9e2d61c6d773d62237978a017be6506130a1aaa789478f5416c49ad8015f10e94a13b4519bd3469bba7c00111404c2b52f720c4fcaf4a9c0e7f822ef050b394ff64ece71da4573b6c3d851bf2caa641ce2efc6b09a3a604726e5b010051f8cbbcd177482f7697ce8dcc2db34a3fa9c73022c3d7d04c9f5cb03eff17c0eaca75bf624a814a866f9b5d7f1801f4ea7f6c0af817ae06aaf5a2c4d5e20450a47576c6a8ceef6d92421d0fdc21bb549f986d503ece9d87682a58547d75b4e0efa6e8f1836a1d15cb796c5d433fdbdf043028a61e3ba5e60cc8deb031a36b2055102218e62523c5b14e313757d1c360b96755a523747e428b149c8836b89030d013b3ab41d1e1af687be5d007c4092902cf73122716108f876bae5f673299c0d13fedf11e153c10c714424670a44213ec7cb38ed1e0a31ee629ce4e7c95f230eb4122f4033dad6644131490d9a963da3198ab8c0b6801687b7d5275a51a8ab0da2fde13896e727861f1b3d1db0ed5e33dcfb7f82ae6971745080b466d8a3b50fbfc0161ad23b804e3073d3d2e74856935b25fb66e4f734de87985d3310089506c2687b69f7015b69fa5ad3982fb46dadf4c2ee259ef1df2c4d9aed48d161650209bd813351fbf4fc653fc00b16133067ff9078b9a226f40c29aedb94f7210c0bb9c8aa4ddb2efd8b8e60395d0f981ac6bd9dcfe8e54ea609b40c8da9e9c45208cd92e9ad5539679c90d3453452b7b5bc20bba39b389fdb60fed4ed9aee8d2603eb238574b538aaa797d74323db912230847f34cc8de237b6b7717f214768890231e4beb29d99399adba94595ce436204bcb21b22655b64751dc07e87b660f90ca9d72749590a286afd81d3ff1a5f78983bbebfc65aec49f4173b6d756261bd085c6000377121d3a067534e5bf4d57d7bce99a19797cabe9fc13982edaccacf0589f6804df548e50eec5c65a5fa5028f9f2d5f0ae6c275f29a74bde3ff27bab027774912c6710398715649523b9d2d2a9800e1236fc726dccb0b4ffc3bf778406ff22bb6ab3f792fcc7336bf07aadd2b8752f25b064eb740ed9bd610271c25c04a4e3aa35818236c144d13bee018d107e145a22f9a7d4fd2732ba58ffccd919052c4416c88e7df6e59f2cb6f8e6ef65e998cad504ee72fd0b1340b2616228f6008861924b426ec895c7ea4881548dd331f4d4fa56cc241721dc3ab2769a31cd047c69759ff6d1dfeb4a7e86103b61cc66881563521bc72d745dd6f50f50471e09c54e39939ae8dbfb54b2b4e03286ededae8301079acdf195c3606e0284ac4f067e134af5766b04f4d095f6b8044060ce05854c09874f965bf883d4819ba5d6097d5f597b335767802bef330199c8911a3738b4e40fcff0f5e93b5c39cd1f7b0ab9077d927ad5caeba7db0f3c8ff5e46d7ce4723f3c4557812812f3ad7c6ab10c720078c475852a4aebc74402ab3df1b767720b816d219ac7ecad8a144f79b408a38652fa90c19256cc647ca06ed1c1e1a88fb9a3e844b6ecd2a08fa863a4cf08970a78020803fd9015046043401b270644236042d45ae51452a5d09a87afb20261b2e91ab0848ac95d7cf603302389498ca259a33934ead7b112d01391243c05d7e9f603924ec65641d616484e7661e3eb3a20378117b3403c10377c0848050707066077bf695f6d418b345a68ea10ef72fba4acc56b2d5fcee815fda3cb940a25fd3c0518626d94a8a7075d6b2bae3c158c3358275f421625984a6ade81720815570e561115c0cada65526bee7f260e922ddd963a6ed0c6aeddc18a691a2b00ad9b58c103ed56693dc95504bcf3348fc854a179e8e81a8d6557f6359802180abd80e8e05e2f9ca5d72a21fb5ed41e24d9fd17b1e8624ed25d60f3cc38ddf60c725ce8fa5e2947bf19c41abb5aacb0d7d33924c293d483d87029ce80bd07a408351bc6cadc556397e9120e192f920ddfa30e02366cca3e948f153e888700ac0852e7f605f09db17b4903b6d24283680ce07c6d7eb2d8731eb02213acf799960a02c50bb33a145cc0079686b63d048473b4fec68d209dcd47bad0bb0bd9e6160f92a53d4289ef69a13dd1cf9457bf423c001c3f5c1c089eff67a96f1b97323c0adec58dd15f0fdd60f7f0494747291c6ae008ed2342776a308da54eb48551f902d5e4e2269fcb92d364feebbfcba180ad279745b36dc84134995ee244ae9e340ef4388a341b23d4a1f45dd175d30fc3d078bbfa636400ce957dbb9fbd26b9220aa4ab12bd7e569346c26b82ec5bab7a18bd7fb817836620b1c83111739cbee007b87c0ac31a4327d3887f788f8d0dd73495c9f6b0e45dead07ca6b5981ce1790b7ffecdc823995305a20efe560ea64168d814d00026cc82e76b544f1b84eb5c0dd556fec30331bc6dae91f458f5e93fd724d28e2586612a68256a0763517e8f0c369965edc8d00e39c6db1c522f9c99168c7138245d4aeffcdf198d44f726b004d41e80cefc3695fe8327effb0102b12de40b0a18cca45821b550ff4805540d0928456a435ad80c341f0a3393453af1e1a6146130fe165c2a719564f6600c6c064b51259fe0e17ae9daa6f0d2b9fa4418ac5bbad10fdc0dcd3bf64b54349d9c0b70b653d08e3c65f907659027e1785f63de63b49357a9b1236f53b49992d04eebb286ac11ece1cd2bcdd926999c24ca28e49cc43a481ad259c63e5a3418a547042f01bac19d5c232c9d7839a796a525d98f7d5d9315931349c233a75df3422605c5d8171e1a862b1ace955ec8d51388d9493eabbee4f8fc71dda5762af188f0084b796ea4f7ec5dd9bb390b4113a793e456e59cec62f8b67b1df96c793a077601aa8de592dc4c46f5bb54aa97377b5ec9ab0c4a14b0621fdb87a0cf13f842f70182a41a7ffc55b98d6a21c7966a3a4808083cc2fd17bdaa2bd5afda7719dc44081fe56bd5c6f4dd2ae1bd71191c6e9758520b435f7c8403d7465c5c7d8f3eac0b65f7b68181868b602c185c87893926e6f393b4c68cfe64a5af0a139d51c5ea08b74fbaf0e56b3a2591bd17ba77e5fdac07ab496bc457da14b46c5e223b6df90627670fd048007fe565a19260f2a93fea8c012d38190c11997597bd9483179606496954204257b654096c6cc5d99cff26dad26d602ae0142a2f4ebf1523af3d0afc453ee7bca45a4665ebc8a089b20b4c78701602c8e6e05fe828a1e49b052104f98d34100a61e292a90857b78687de80c8584d8a55846052ed3b8e3cd4fb4a04f2fb8fb355abb51816cfbfcbe37fb627ef60c4e409c8f6ecdb515b58e0471c04cf979cad3f9da245fb39a23499c46d289f0982e465e13054789071c4eb491906c94f05b8e01557b3da8641f331f6a32fde623fb06dafdade7b5ee08bc36e2c099a4716474273d94adff7c09e273a72026dcebe50041d1c93f738b61c5c255f075ac88def556c3aff35350ea5f8ddc9fe0a5f1fdccd936b37c21544d22d599e0177bdf21efddfa851718e87c1e6da16a0d4b4d87ef77e7bfb81c5425a14e0970c39ad31bc5ab5d8e46712343ea11585c992b7211d666c8b533ad8b1d6ffc1b10b453ee418ee0833fe403c4abf12af0885117fc5be764d470497e474be0d4b76074f03880438e1964e5a6b509e2229c887d0561ee0e0cac07bcee8e0238a7281060e3eed30f28dace7d3561c530e31eecfa514ef7b275a8ed259c6ca68f81a0c04eef257ac53e0d67dfd6c24415788b7b24dda92120dae5da2c043421b23f6bc0b29e639a99e4dede7830f29ddcf1423006a6821d8726fc4b9aef5674368ac1e01cc251cb80b1d91616dca543cf4e85f54aae97014482cadf1cc8c3a3e01af2f09a96df41c73a6c5171012dea698a5b0c1dde8be98f14ef9dee7adc3baa1d8e306841a65792aa9ba8c904e7f9158bf1266bf63632e1f56a9e0343af50b8366cd0a35e2badd9346e9ee4b8ecb8b9ecd66f2c86a74061f142649ce09650f3fe16e0bb4f83d42056a04ed47a5aa251c205683083726c591d2ddecb0be0ec8d3981d06b97a691652c00f64654022d44e93075f36e79924dd61e1d0ce602edeec23900524a216dd67cfb8eeac19090dd327434b225d091bb640b686b15d836fbc10b20a2a4691ba18a2afd769b9966467ed9c6cc7717e8fb3844f04786a106918e63e0120bbaf6c56b83ec182b9413ff19c62ec319f025fb99af6c335a80badae20b30fd33548b0c5c8e9a6c1b9a96588367b2db1ac90c2cb5db54349f4996d73c8f504b230d19b82b4319fc464176d2fc9ce20abc48805b629f7f970a05f4f41af8c02481749057f84d86ac8faef2d55d9fa9313df01674e7641a4b0dc28bb9e6cc20dfcb3061601ce5a4063634e85aeca9f12c39d6078cab880d0df28de9a6dbdb20a6611b865f8111088bb50eec7a3e25cd867bd5760d05aa62a9a836aaa8ed3790a5b2e4d411aa018427e293baf7d9c56d9621855b26c1e761a208402475fe9520b452f20d653598086ef6eeb03088571c5d0b5bfad687ae95e420f99a75355eb0feb08124b0c6ef38c9898d08004167db2f81b967f45e8e1d607001008a969ca0e316c74ee8563d7296d71583799256e7459b96834eff7c33febb66ec06398660499784adc6706587aa0c46b00e92dd8cb03bea766a4c720be53e81470630dd501784c3ed686c7929922b2f41f9e55b6cae12307bf0c611138c81e91f1f9490f05ef8f90baed2cb904866f2f158b1421da26f8f34d29a07a64e1e9dc4070f088044ba669f016b0e65dd2d29d4c0833cb1384e6babc189c5be4369d4e18590c770f961820a978db6dac71590d56e2fb29015c5f72a38eeb3a08c655a9e6281bef01c13c96290762cf93bdd433d0e2b0125eec4ab09b57ccc5901ea6c084a18c9d039ff93f1a29aec365c18f8812ad8f0bd5c848ec0ba84dfdeba71d689109de460235863fc664061a927e31222390b456afdc36a985987a29359239c057e75fd608f0930b37d9d41bc4f719d19434996860923179e775855c6ba0598a4460ae330356b97adf7bf72345b3a7e49020368484771e1216bc62d1fe5fb4fc05541c1200879dd81c1ff08bbefb37a0abb9f9a455b68d5e5afa70817dac6125f6a3bb5c0dcefdac4456f48044e6b3af9ac64980bd37fad046fded16a96e16c972f79ca403b366b129d0d20d965f4dabc7f25b4e4a845243804f183c0725f3d81cc846d4011e1ef8f92f50148cfd6ccc73e739d50936e4db83ba372b2c10f3657afb4c8f013ccdcd38724d1eddcf50275255bd4ca58f7487e509132993ce1fb377f50ab907aec9f97b5bb0019ff64877966e4b2974647656f03f5266dae814984605e42a05b0bd7a5b2162a86f25a9721b3fd97a60725103e84c8aaf860e8d640cdc773f0d50e3bb02157c988dbd72f14f6684dfa42458a5ee4d09943ba527d8e55d06860572688d7d7ad2289013e94c2b8d0d9d850af725f8155a7b03c21baff53f0e850fee47c581c26258fe6e550d36a8781b5c7d5627e65006b7aa61c7b4d1abd8ad09f26d825187bab4c241d064b6621bfaff00db1f6c7a2859237325e4c5d03abf0a02da49435e381a5d621d2eb19f71e93b262d72a11b3c65257e599dfb1d58600ed12588a2acddf1c32517c0ac60d94c8ebefb3f1eaedb9415ea19432aa96265000f0d528f1555ed5b3b5df5f927140d6dd7477f90292bb22c1a804c82e8c5f5006cfdc8364a794c14307729d296899d99b57bc974c5e40ed2ac6b7d745bc7b10cd148d141f3147c6e8ee01600dfdb3f606a7aa3a3ad76ee413ceb5d67abb82909c6edd078baa3152f1abaa1490d961d9186c93b58f6f8319c7aaa6736575000003438eedf2843e31f71c0fb0f3ca49309f42ecb3f90fa652c961569e6d7f330087a4184fd06bcc88bdec8656f29d8a79371c04ef2ff2c7b8b1b9df4134b3cc707985cb60af62dbd41e0e3d2a6c7ac83a84da230e042d90226fceb4ee57ac0190bcc32c37e298335f9b091603988addae6f543759d69e150bb22d5109b2977370467ad5441632bb00d89a19247f2660e98e88f510c3d84ec0fe97a9491a5ada307b593889c87c465c1e199198c1e3351bff3abecb0c6bfccbe40b8d2ea7895e6022c755dac8fa8b2435aa2b0b20937b1728762f1461256b6ba1d990d7caec71007949a63c0293375c1d6ecb561b94f56b395d4a7b488da326441cb1a772bfe0d0d946dd9897c321d7fb2b74d663f47a60fcf8d5d7130e2783d4f0ce198182ae303da293da14d68691baddee635105f838921d9d5ad09d65824dd32e4532466070fb15802f9a932835b79e08839c977903fe0687ad43a9ce47577fdddbb8995f80cd8b17f43f223149d0673bde4ed2653926022d9d563159cf1a1b57951a93dc60ef8eb21b9e1f6956210508ab3c4e6f9a2b73939f07acedb42115df49f741358073c36f27aa1ee857908699d4a4990cb778419492d46fb25fdb425b7d1acd580054b97f6e98b781dabe97d4292d4afee4d9cc0b1db926252a5ec5b3d235742360c9f0028924e90984fd342aa0018f755f1320c1bd0e6bc27f2223f309fad26760b428fda395cf98401b4a2cfd44a4735defc118c4f1eff7711e01a7968a410e20300b5fd3c9f520df010372b56fd06e0ba95506a22afe2ed4205a9fb9286206b004aa93cff14c6619fc6be9e3c43984b55cf430f36f86edaeb225e34bbd944480fd6040f4868d42c9bd424f21dfcb3f544742be31f794b4a034dc5630c78e5f8080f0a64de30b0622d9f9d1f4385d6155c8a37f2aa5f6a9e9b2ff508e746ed36026ee03c85892d96a0c53fb78a648a428be9324a146d3b941409a3bab15993bb0e9fa558831eb1009c928d6e86cbbcf49ca992367be4597681f169e74a23732d0e7b3cd89707ccdf130180e0290b79e32a15962a0a3e664e43971e650dc021fb09c6a3e3a2d8f4927f573739837fc48a602bedd81af8bf395ac5a1359236b81d06f94646e9cc597967adb1b13aa46783d9ad6f066882bc836d966f6f53f8ea61077b2e55ec430239ad8ba4049509aa022b06228431f689f22ab1dce25ea327a70e6395a47e98a56c21ba52cf271018babfe4fee5d3192d899bfce64bc7dfed5d03a27f743c8a92f9946b26cea05ee07b0f721c7c0e7e267871f66e673247bedc0c9b1fe4d7fe26e80e714d9f904291aa8ef01cbcf8aee78fc9d4df8178c41928088e8b1d7888d2e40dd393693b1205877162503b34d588d682f4ca33334c66730d2db053f009f6cacd14944420456b1d9ed3f2dda315048c2022aff7ebc047ab0feb4634952b625cd012dc4b6c3cbd512be9f2e580dd1678afaeb178b426622f09487020519429688908edf36b9a0f5fba3e12f12e127e557d0b4b9ec4c69eb20a966ef34a9d093ebf4e97f7e922ec737b7f784d880b379b5da32d3ec96cf85408 false -check_ring_signature 520171b909a4f1ffd9cdb140f8bb70e5db3e4411628c0e2b67329ce6219719ab 064732c259361b0f49a77a081f6497618a3aa6e2a22a92cfa422996d7fbd79b4 178 039d36febfcf54fee67a92963cdd9a320fd43887866b17eb240508c3ffefd896 3203549442b267250a05c6686e12e924723d0680f62039acc2dbb440772645b8 fb70c156a2096383396426a718ed215f9382ee13c56c14b46c352a1eb13a95b8 883eb9b00bd1208da587e8f1f5dbcb797ac671d5520d05187d9bacc421fca3b4 166db1e199c0b78dafd4442c04ba3d5d6093b53efd8356e5b3d8a182b2eb7ab6 4b035d7097898013ddabaf64a935e685940d5049743c2846d5815a624da7ef87 006aaa5d57c9a29ba2b3155240039a69bf4828ea0242007318ffd218ab836d2b afdae1d73c62b49b5a4ed5aceaa2571e6ef98d2f48b217385b5e2cdbb9947fec b2090ca2c67d370b4d6e5c7a1125f6ebdead04d03666650abe91a0265ad270b9 af78b3c858ef2617cc805904039f0678db8a36f1910a7f1a5d0ea9c03a7304f6 f704e3c583284ce65938d019e7da9d3b2abe33370a16b566675e5e048745f7fc 64f2cd1202272f45a0f208800f549014c250b098d048279eefa8b8490fb64e30 dbe17de2ad4b0718ae37950f52fa622340eee8184139c2682732a5b58fde7ff5 78b07cd1550e9e82a5dff61536cdc9c98c921eefb68fb8e8af613fa77d1d45a8 febaffd535d872cb4c55c77b0780dea164cde1d04099fee2be1fbe889730a3c5 019210b304785cce66a2faa78ec0072b24baad9f0e5ad21c02de87a17b2c0378 8393676ba43a61b5f3fdfdc6755ef8078b923585fda50de9c3ce43489abc6cd8 fca788765b638b9d44cb85e0e56ab2eed61af795ca16195fdc26f7becc6ada65 1bdb6d49fa3adaf2d962704d6fe82513db2a5851da516a928883d429804acf3c cb8f038b36a2e681128e036b1d426b355bd57ad88d9e566a2af53b237556cebc f175052fa977dac291ed5419a8f7a957607519ff37adaf723df790fe082e6072 dcdbf162e3cf6ca6173bdea93df752b21664775c5261327bc985bad5e97970d2 ef639ae2ae165e0fdfb854b88f5d72efc00cd59d6c0f4eef0dc8e8f171104114 f803da8a754a68fb46a2ea203983f4c006e8aaa27526b15958397a8d459da491 3f3f0842b9ce3dd30ebd3059800873871fce588a8b5d57fa47be08491a9b72aa 9ef8c4f987748a21a6f08c737db7f1af46534eb35063374888e89e632c615761 58a56dd7602b9f74a551a25a0cc411d0064990f5a8290a0e3a6c0c2f5a2c6c0d ace76a7d7d2f67e55a43a645b340773eb63fb1568747bf387d0d6947b7ce1dbb 43c789e3346d94e0b7b9e6fecd46ad3a18fd9b26938fdcaff1ef2c7d3c6dfc06 28633c76535be6abcc8e1467be812d077791ee2d23118c0e737941e721a2d2f9 deea8a8fb342687df661ccb4a4b5a206ecaa8ce4ee2adbec318b8a8ba554cb5a 82cf69c62c874fe2fea8c14d7deb153f604cf96a8a8492bf9c09e61b2b142c08 b5fff3a8b1bae2732d73c9646fe51d8f527bc35301d126ba374dda2d3732bace f1ae0b5f23fc87a0b494e54acd5fc4aefee8544596624d0e8e3a92b5d44c8438 9afd72b1da617ae3087dcba77dc5c9c48fa2cebe01867435ee2bf2596f427182 b0359a03163bc7e003f16a82735b7ba10ebf9523b37d7449d5e90f5c292495ff 0eecfd2bf9a6fa6ac5bd75a45abbca0374fa2e7d056b9cfd237cbcf2dd35b7c0 f20a7a534118a0e0b84ab5a15c35ac8988bee3fd6058218848759608306597aa 601c5f225a69cf3a693630a74b491d4df2f0c76254922c66e3b3279dcafce0d2 c59dd25672dfd2b4a473e679d5a3fb6da716f249aac7b88fe47c58de2d07ea7e 5b34de888fa72c21f623c2c3c6916e566b6c33cfa5e8891591cea78ab6f30bc0 4a3fc2fa42fe307ca6c248f48ec294bf490996506bd9ed5e11806d3356bb7d70 346d28aabb88fc957fa31be24ac43e3c6463e4afe7a4af3d6e15ccfc99c0db42 ce6a159af406a01937f35740dc0b36c236a1aa0f37e2d3a4e175288836dd7cfc 90d6c15e734f4586a9a0c5f9ac978d7a10001916154e2c1af7674b850c5dbc1a 387269426b85359ac9e34bf688a1e3a92a07d1a74bdc437161d520b4636c4c6d f273954d59b42e16933ab44d8b69b16bca7e92b917d8d6f3579cd7796820780c 815dd871d5b16322580e455826e4eb4704d8a82782ec4b88ac9542c193a679cb 60caa48ac773bab851ae598af796b0d5ba116ed225c63e6fd0b6deb4a8d35d8e fa7a2b0fc1004f8dd2a5d2463bdb7098084710604199962f9e0a6fcb950243b4 3f0477f3307f3505814c5ca9efd0d92348a73d7beae53a11e69fe486f2a7e6bd f9efed3e25dd2f21cc1dea7eaaef104721c3b143c02fde3b0f59462c4029bacd 46fc6f6a32b7fa9954de59a615f839c6fb7671888dd4043f92dda93642f35964 cb392b3f05903408cd3c68a60f6f5ea6e8d69694da0cc2cf7e6db4407f170c29 b3f5aa092f63d7312c72d9506551f60df6ea3bc1234474c539c09eb9f8567338 03b2d66acc27664ba101d449c02478cd2da154735fca5e3a61a44269942e5f25 0bd4d607e581888cd7cb6c421b5d0dca1073bfb49ff27b34e06df960e0720acb 5d45c804dc0770a41c9436c775cebaf114c7db682b5b82f758f3780ce7def83d ee673d27f7f8d34995ca261e2e4305bfb1409a31b2e40f8113557bd4af142ce5 2ab05284492989b36fb12cfd6c2e3439de2be5dfc10a1838c1386acf97c447cf f87ab5ff7fce6d94285d69ce3a974f233b42b28b4a2bc5f3d1cfe2c90afc024b 54b237b7b9c0e779c832dc98d5d8ebf57e17991598947e786921b8712198b927 b3341af450e500dd71d1ac337bd50cfba8f40dd058d9f3a7f3d10dff76452169 886aa1bdbbfe0111b7809c15e6337e050da7cd9df997b4b88591ff6c1c5f2056 d745b84a76a9e9f13325b0d290b467673d0ef3b1ac6e5887580172eaf1a553db ef5e52cee08b8f0db7076b21b6bd4cdd58ae1736fab0a60661368131aff5f51c b85a890d77c9b050cf7605215f439ea87bbb12010a49f65cda05a89512d6eee2 708fbccc801e500f7b614e283bbee3da270cf0e73477f33289bfb240031509cc cfd925f51ce103faf9b99f6bb0f5aeab5b92e405f46a55297ad3720271b7a813 48b8e7b4699d247aa1cff2ace54de06fcb17dffd754397fbd8455b2d74fd2b1b 372f35573bdfb5d233549fff1a5df10294ad4c506f37655d487078b2f8a4712d b8bfcd9312e76c27de58adb14dc8b47385a2ff53b5415e116773bcd723812bbc 642084bef8e98651c13824133875a650e12af50c36a000e1a24e74f1e52a9307 6f07b4a0360937f1e6ef1dac02f1593c4de6f9b866b5d99e3dbd40f81575d542 cccb3ae812a9cb8cc50d54a51af04fd6edce372049e3aa9616c3d2fac21413e1 a44458722996ccb02c59311c5b457f099de025f5e0f3f7484acbd4986552b64f 0c3580b087589a1e524f3c17be57385a7e7b40b77ba4945641b550d14806c870 e9c5bd2428caebd09d48e4c1a57b076e33233883eaecb1313711e85ba33ec4b1 3b079e840908699ffea287c932855dc35dac3cf8ead627ff1ca731c7fc6e580a a8b59f0ebd00a2fc8f81ecf8578b39719b9d1f87ca1bb8da2faeca3a1db68cb4 706b80e53d1c47942a8ad866c350786a6d8163772835f7b077e4ef947251fc44 50157c2444a432bf5fa9d00c845f8ff7f2f8cdc0223164066ca5ee1b8ddd2fd7 d0ad17cb51d35997c82270c2e61c475b253a2a3020e4558793c4335b92e32cd9 eca4f4227de617896c4c58b7ba524b5c0653546fd46bcafb3897811139fb8219 ff57b73c1933772bcbe877b250496e7987d685064561fa0f73b766980caafb9d 65614f7462b1505cf9f1963ba77d0321a3a6667253dd5750ac276c1583ea4596 efe3487b7138b956014eda81eee9bb268a0068f0c49a0b8435d90d83fd7f1fdf 6437cf11c294578535198dbf595116492fc47e8c6874de7470dab2af26925036 4bab746a010a03e9a98dfbca2c721a56aed6ded924868485c162bf9df761c619 4de9e0d247a7e921b21fec3fac5826db088dfd20a69e4f1b7b06ccb07568b6e5 1824f31d869d461fec04d9152819cf904f15194975f0490bc34a4fe1dddcd171 86a155b280b2c101e950974cae0aae6919312f1fca4fc8af906ef54c90849f39 a368e2e5f6c87d2bec273f39727123aff3b4249adaf019ef153139b36cd6062b c69b3369a488d39c28e16ad5405043515dfc05d2c366fcc222c71c79d9470f19 7fc4829338b79bbe7bc6007246d0e58aa7865b00a7a983ff42ec5eddbc306b8a 8d34030e195451e87778598ce027c669d9d70bd86958e00c9ba8f56bf48aa5ae e621eac1258640a97ffa740f92ce6e22c45ae0f14ea34c1190dc39e27dfd5d19 352143c669423735e9885f12112b4e5e0ebf1036b0a514fb13a5a489666ea528 2fd95cdeb53699e3a9029f5b386ebf3af71a93374b4ba10dc0c84249e276d8bf e00e5e0a8162b48b8b2a88008a22ad95ea5f899388b6ec1635c26059558d9d62 88255b8e8d14c1d176438c88d948f7a97c9bb967eac93af63383469011c27138 eacf288d4ca29655f869dcce3ae6afdd8848fb64921ebdc5bf633dad50080d3c 181b987fcd8869af35e5d882f926042bc9f20007aa3c764b5a682638a1da4f81 dc547b5bbeca9441edcf5812ab74cb1eed150f67793477003eb77a74a81180bb 1cef33c396759aace8ab372d4d5625990cad0030e695a3acde6098813e697d82 186bed7bb1c55f82c39a301a1176ac4d64ccb754db9380846c1bcdd13ff47dc5 4844698d4f19e755a0011d188da0a4e0a3ef280a2e0946cc37b5d0f444a4b6a6 18761e8cfb0d2ae0e29d5ee0188035ab89ce4f63507b544e07476f64e0c4d3b8 e2ebc69255dac8590fd50ce1e9988856496358608f81e120ee8c44cd8781add7 fc8f54cbad2789134635836aecd42cd0fcf9e7790c1a8c75e6eaa531dfa788c9 9f77c175ae99acfb658fbbd8328a4806b3c2bbbd0f42b3bd70b3f5ddec440345 92ef7a02fb9e8ebfc1c75ef8f8ef5b50a8dffb8f7703f8da7101a1fda478724c 41fefdc4781b0393a78133440dacc5482f42e211409fd38088673bf76a5edf96 a3e97cac36cdc6b89f3e86ba3d7b8afaeba6cfede66652f97eb35ea36fbaae4c 8649440ae4be400b7b346665f2d9bd3f2dfec48926b21d64fcd40d3d0e4f20fb 65f41f1fd0e54208c797bbc8587b48ee14d234d3eba168e4de5eaea0f1cff2b3 461126d46b1804f8f1bd21962627c84907c2835f3aa610beac2e1898f5406eba ae835317f122311632b6c2e5d7b197294ed962b14f1cbb9d282c0f520bb210d0 2b19de6e6c87ce4f0ef958e093e2898f09f2ffec2d35ae3e5936465febc2724e 3eedc1e05c73e630cdd2b40d6d8c28f06773b674f0bb5b21ae136f609cb9a6a4 2fbc22b35f615e77eb79af1e8f4bb385afe97d2b598f006cda80c14f72ad03c7 4d2f5f12a212ff7050bef4f9c3ba30a9c36ac868b6b9ecd3dec32953781ad119 f832d46c9df5638dee7b0475291e7e77e1c0af33e6ebb34a162e3dbad2f88390 2920f172cc581958795c15f3dd19802d80c6f71ee4215452146695a75dbd959a cecd48260e5721ff0ca75a9c795eb36dda610ccd281a8ef4863e3f4a22231339 5db769880f615c6ea4892aa46173e6b50bd0cf6f8c092966b36d8bbaa00e32d5 2b6925c58b5e87fb0b7e639b6c3848380989f83bc6d212882fced8ca42ba35ec 0c3f95bd75cb6852fc3e9e3451759847bb07d52f09128377a12e579c0d9f98f7 b0e0ce098584bf6fafef871e2f2208696a17b9ea70bfbd75b7b8a88230025982 6bdb048b480e299ce78e84d1d7d7fe1724550a4e9b415b5dfa5f6d975134be56 5b063ebfa63c48e76ccf94f65438e436fa12577b1df7892b2f9a07a54b9711b5 fb7dda4a4857ce88ceefece7003e89723d4a1835c12362d43fab19854fdbf20b eb683841683f6bec33dc97ce92b7d2610da7461b421f62714f95ca69d5c664cf 824afe07bba7cfa2952e42f1394d05cc71e185b5cd968e7ad410625122a3cb3e 6f29222d0d2992040b06da0ac816668f6af601333bebb38ddc85800ada994f30 3142ecfe688474ea315255be72e7d1f89facfccd20be0652f64bb16e3dbc3178 f076721ffb66f9940563e33bed9aa26348e388dc9bb868c553f279c88c25ee6e e8a3d3d043f4ef5a2101149150a6eeaf58405da4464dd392a85b68bd13514d5f 6511755d6d1994f2cbb4d3ada8addd8cece01d118e0a88b63020f6cd973b432d c13a476bbab45b25bc3fbbb2da1ad1591e55a169c8f4cd5160ada1bc6e07ad35 e009fa2095e5fa7e2299bc7c060096d14b2ffc735881b5b698ae0f5385a71178 bb70f20bd4963117c81eb2b1eff23c06db4ffe8ac420a409112778e14fd086e9 31e2608e9fc12468b0b60431955adc54b613c5f22a7e75adcf27774d1bc52057 71b7d0e969d4cb4efad803991bc3e879588d92782d03b0d4a2f63d5ef61f8cc1 a0f5e3f346433d02d8898a2b58cf76300106e738439855a7dae44d7b5ef028d2 d5cd921a184a1bb153e311fc8f04d6b9213961c66b87dc53a3a0e81d6053d5b1 3a4c4b6f1d754c723e38288c9f40a7656a34549ca441e750927c37f532baee43 9cd39a34bcfd84843858f6ee1700d40dfb06e3ba1d22547dd08e03c26e3d3984 d43fb358431b7c2ca44f15061125e7db57ea4b6b44fb1ad7d0b0a0beb0921c5a bf600661ef3f8b14e691ba2fbbf1d36e8cd3a9e32753f1a62f4db8b357e8b06a f307e8fb75f6bf2211bd0d764805589b11119edae9f350520b9a7793fd2ce78a 8bc26d6d3021e7a4db5c3039003f30e8faca443f9b55bdfde87f0583045dab85 8d9c7dfc56ac0eb0d2638efd7c1bb46496d2e07deecfb74876ba19e81e5d88e2 205102455e2f9f5d68ab39ec281ea8d952e20e5f0899553ba4bc5b18cbf08816 184d8cbfc9e57c097e17f8b64c36339fb81209f1ff630d42845d196b24548b79 c90cc3b2119d2f45e367b817f691648427125743415e232ad11aae4ef169dbb9 ec392a4afa99fdc80a9ada5913b087ed412ffac5a46c20681bc2d03e44c462b8 06e20195d0bbb2b76c603b130c17ba0872ff4bd15c8e965b58e373dfc474536a 004272919acd27caea14bb505c6ed5497efae064df6d1272654aa8c129a6d841 9eb811dab600777e4c304447675951efca0e30cb963a19f9623f82aae4247004 ce5c8adb6c2c37bdada997667b2ea3a0772fe7738ef697a9538ce20d00136353 e3ada26b0a1645fcf113907b69400ef4a3105af761debaffe7e1629668d64b80 5dc071fe44dfaa9ef769ebbda25167d884625048a4a57092d9bc9c8c4ff6ad3c 27170a827b301df3f9acf3b65e060c6e6c6662f3c57b4e208616f6de64be5501 184cbaae911ff3ef62a435bee9fe8271e142804d4d9aab5c395dedb74109eced e1a58f19eb2512676263ae6a3da2b1d0d3952b51e0f7833efc1a165f2bb3a750 f1708644fba2fe87658180aced370e95836faca952fc72b6ced8dfe23c4d49d5 1cfd5cef9589f9c6171b69d0386ce53548833742c3c45148f4b26cc7e1184b42 c9d921a095cc9264d9e8874b702ba1e1bbfbad9a7b5ebd6a8837b012116ce13e ca0116966afab32666a9e1a8c12764bcd8ae7f893ba832250f6688dff6980673 f5841909da0b6706dbea187b578be3beb4858647a0448895aaf6ef3fd6562666 b094b407663660889379eaac5328dec07dc6698b58a7baf36fb7c03aa8ea444d 58678b05c4074f0b107ad8970ce088274f070f9b70d969d9c8f4d309b4c3c0f0 124b6a7e0aaa95b3e6704a51dfdba6abe8ee2ef6d58ab1b6e56007731e3f6a31 23811181cc59bfce47708258d4af4c8a12be344851a86da134cf5e6b327384f9 7b0857e29b97b16855614363620b1b522e9badec5cec229365150ee56b60ec7b 8c4fcf9f27bd07b3573bddfc83760e1cd7ffa2a157821129e881b6b036b83f30 322fc0bc124b4b06667baeeae69df8e234a871f5b2c8337d339826fe555d935d 29075ea53abd93bdddd4ccf30badc90cb04d0b91a3e938559456d6cea7b7930716deb3f2de749a8a0e083128ea1a9ce74d6f2ff7891a31ac30675187a6bbf70b969835ae1c18f5f6a2ae5c3e21fe1d49f64ea3fd207d1ff6142e27e199963f0b90058f36c542064fbd6bc7c4bdc087e8ae053667b572c50dfdb75adb5501780bfe13353e0610f12c3a4577ece08e8b98a70a4ad5d7f042dcce015f09588bd00c420e018f5d7b42a107b934dfb9f1e3c00a4ea95981b48bcc75491b3c61b1590a1f82d30811391482961be0ce05f4c1f4025e18d8cb0b8a9ae2c9ac1fd778dc08e0249f6162daf6638f9b22243150c56e500779427010ec12af4916a7691a71029acf3396249d4731fad3f5f5eacadf8b50b798077cfdd450d3025cda4558ef058aec70be78efa4f4d2bda705c5dcc14421cb7320a77eef4a577023e370d9580da9be2d513caf0e76223054c676e52468de66504959a4f56272e4206ec7a8f7064907e2f57c9adb247bea6373907ffce73c326bbd33b568bad54994f4676440054f1bb0c06ab55599eed79af01a5890a5317164e3ce97d6aef4cb92a47a4c98014b9d076b3d069c357823dc34e55aac8a635ab66b0a28ca5d504c01049743f806b5c1a9e80a754c6c083297ef0eff9624dc65f2f595cd5ba112b387cfef610b0c3b8b79cbbabd689ee28aad656a949fd46024a3a3d891cfa42e2e4b124d74e0034078f7a53454e824985ecae2ca687360ba9c15036c22c9ca0ed2d0d2371c3b032287e8a72eb837ff08d0c3edf2be1c452737d05f7d99ab307900fcfe4f29d6048a654ad110dc9003761d90ecb665a1b845bde04ccd26a790a423668423a19205d1aad8b31e26e78989806c48d553504c9702e0c21fdf1ac23df9aab7820d4a0b5c7517e7851a1b41758f4f6f4e17f849e6b5bfbee683bd5de8d979da618e110f83432596b86906c6ab1a55b691fe38c5f6786989737e376165c1d935b4fd370fdd10ae45816b28f2b4f9675f5e6d379cca2fdc45569d1b752338dc0c2793230ec4281e233f8cf5a0acda7496dda357661a49747753a3c7aecfeb928abdc33b0e774d6e4d2effd9951a94165f2952b27b2f51bdcaae6ff8aca2dfa5657c5a2c0df3fcced0b73f2751ee1d6f4f829a70d060bb973552b539521bbca35283cea0024d9c9ea3bf8805e4f4d6c99cd3cacbaec0c66d5cd14b0c25b80d49c19b2d75008bafd68e34c81b3f6c830d10846d6c9d6cc91dc7cb30bd3ccab1e9bfaf490406463263e36675a6d4d7640eb5eea85c233c2730862dff239a5da4684c31d7770cfa35bb56150c7e2b1d34bf66aa344d5cea78a5d2626b9f103faba9a0956c850198228b2e7a78d241b97b1b7927391bf1ef38eb7929dd61e3651322636dd34706b0fcdb8fe98ff4f255114858df30852e49fbfc19ddc2fa3cf959d02dcc177b04a5347c22de50f310825d4dc87bed1047eb3cbaa054c51aebeddc0a86dbbe8b0a08312690d5ea143b1b5b8399a3380e3bee92912171f049d6e08205f922aa1d0757b601c252c075027c8c3a0c303a0c415644ebe6b68d2a459bde9d498a284000656d37b2fb10ab34030563f3823baab050ae561ed4b6a4c26c6ec0811a515109d34513e3971f3fb57ff56a9d0d0ac6009f74b86f2c6a993fb43776e5eccfba04d1f6506450ee96337d33a7f62366470910d6b44a5a5365c310b500c2e04c4404375429f655c1b5e4edc0457a927480011b182d5de6a9a63478f76f7a4cc7a705529f42c08b59c412affcdec4c44fddfa04f4980e263ed0cc17a04dab0ca6360da1b1630395b1cccbda72d8252828eeff3fff7f21aa826922309930bae369050e3ee2fde2be54af6da4a90f41e5f9b7783218d62a8fc7a3bc42acbb95e4d31c05a0950876070e8b7d84dde061f6d7af1020c61f6c818271d76a3e8ac3b20a4808b2308308367de13f7596c4aff1938f55530c0c1273cff0dfb9f07967976640003d690a8cfb709645eefb84ac7d7392fa1bb3657609398c38f0d717f10c383b0699abe5d2672670b02df64493b376f07ec62fa8e26a04167ae370038b0e9a45011411dfb6141809b9a9867274a370d24aaf001adb782acfd3ecfee28d3d22f800651efa297039e1216b5fb016d53a1440745bdda0cc8a160e2d145e574285c8033dbdd496107a6ee6b09b69ca2bac8c24ce25eeb03b350cf0d48d937601ea99084d47cf4a80957f9fc7b788a46be6fe18f7788e23444a8515e7b59025a48b5c01d9b4fe5deb92c1a118d05a8e4a06ebd25598ff40d10763dffc5ca9ff9dabc400f4a5ae7edfe06ee663296a3253ad85b3d61b622960aa0044f214ebf0589b570a796dcccf3dcd50294de4eb48f2de6e068a9bcc77e5981f79f67f0242cde99c0bd77843cff851615e4b0dae69f14b8a761ac89093de9fbcb525deeab9c9a8fb08926098d5d96f2dee75b4fc502ef0863afcf8951337b33fb6a08c2be796b4a7036fb4eb802185e16c84447a0bc6b2a0f07de7825b47b996f420483851409c840899fa7fae8acf68201cd8ed671734d9d60897e6ff5d80d871033fcd6f75882a092fd2a3ab052d85d08635fdd1cead9c2b58498cb2063f5d27d3554e8ec30a9d02bf4518ad7c05a7989d77c3feeb6017cb009a5fd21f9f359ac2dd50d65d656e0ad0454e38c6a6b065853b227c137d1e24430a7c330c9d36833121fd5518dba207e5ce3c62111e1da8724e0f00c90a3f515e174f6368cac52342725ed94718540827e28f1c74259c5c72a4b4bb4cf7d527da2ec7b5af69fb070ce04cb8f4cf220f827ea578951839c061323bb5dd64a149a854dafccae1b68654a1da38d8c1f804f451d65d72a9783c7588c7a7e609f5a2a814ffe61cd6f449f471225efce5300afeeef32c1f489a5d179ee68cb020c0fb952e7fed9bc56687fbb2a2648b633c0f93d4c0740fde02e9ef91739a4b0d8870a366012db5687833780d181b54bbe705dd98bb8661e497a847349f4e8189d6902f1955636851a533d624df3fc47a630e160fc7a5a9b6d1ca4890cf80652cd18da0441eec77cfc66a903fc69c1cc9c2025381f63bc24dbba6a54646398d22288791ff383430ce81de90c26c0f3856b00f0adf10db689c31257fb9f5e7b224712a1a47392ac54e298fc069cafe30986e0870671242c9b2e2b98e7398edb5443c7a197bec21a155fc2690778249f6a5610dc79010a8fd365058f8e92b78962c6e7db8a895ed80e316edfef6232128cebf0b0e0219ba81ee9e5afa9df92c27ee51dc0553c610ba5254d95a4596d743ecef0b4e3c9981e4bbb21df3dd3c9f6e51ff83d0e28ee9c8591a45145b644f7792ab079af08145b4c3316af79322e560a570753b0e9749c5967b80c0b86f85f997530a6d41bb96015c65d74b52e1638993c3707ed43e67f45a4c479a72f1f24a08a00a050e267d9ad8c7fb0edc7507861bd049efb827ad8e9fe04bde10151a56feec0e5d1c1f2f21c28353eef91e9c082e7f20517e6d59c08e0237c575e987aba07e01d4ef1eae4d3a224e615d44dfe4ee76ab47b2e0de5268b1fc1a72d125aea517029636742c7177210419f6ea925f546516ea248fdfb45749c3cf0d7b989145d403cb5c74411b8b53f27aabd7c5d0dd2b81cb36e309f224509ab1b9d6e8c08a85033715fc918774657d9352d7bb8997e2850d819a569a3fe990b15070b8bf007006e6caac470e8f3afb30c359729223d885ccbd80448b422b485be357770701120f50bd52a5eb198bbdbe168957660449541e1bb683ca856af3ad5a0769f337850e241bfb0a5b9ce910d09b4cac825edbe7ae20d83c266ec6e254808235377a5f082bf7ba42ffdd06ab8d977a1daae8e3de0636aa198047ec37a03a0bc8520caf09649013355334768d8b983dc8472cce5d89126301dcffd89a801aaceb0032a4034891d142f74f9cf4eecc1f6b34f5d93a5e8bab96ab2c2e0553348d3b949bd30406025d6e3ba79f32ae5283f2291e0789077a1cd0d899a72c146a81e4fea05d081f08924adb1d27744c1c2e4cb30e8477e4e262e7d639a17aee80789e7a68d70da3e454f541186e0ad0c92981c0e0b500d3e32597ffe4baecd0682ca7e2a3950527111985c9ee86c6beb734fa2e31fd8479d181f77b19c804cad611d583450b0b11b6a16b3a5e0638cb711466259a56429b14cf92d072f7f172b2b72b85159008369a179f55555953e32a79b61fe437754ced4b921b44e77c463bdd5b3a516b0c98c0423eb784334000a82a377ea37edf1350335c1c6b4103f2007cdc3049860a1b3c1c526cc022aa840d95466722a325bd85318abf53c4c4245e099ef97f40052781145afcc3bf311d3e66be2a060f25328fdc66c1dc6efb78a7410d0d0624017b5795ad3e33b3b689ab2768b1ceb6de8b51cbdd795d2b693cef8c0e5fc2b9006804866b8463812d3a20fc1eaf5e702af7e1ebe53768660d0967ee1a15b5dd03c5d9b5f6a8d580c6d42439c400799a17118c15a3170f8088ca0eb1c4c027a603a922ea11b69eaedeaa2a6d58c7053de0961cb6623c7d22a95afcc3c6d65a8907cdbb22dbecfc8e40e8a65fcaeda527bb4c4a140b8ca46c9888ce3209912b2a0bbf3d22c1b998ba24dc84e08830006e41ff44f0e3607dab2baafc1dcfcda5430c094858f47c1c1f951868d9ef93cfe564b294b9b641f4c0bcc7f9f7076f00e407d0ff4b20d7ec4eb62ec1c070a56775e69c9ee14c2cd8655914eecfaff6eb2f0fb1cd31a3a994e81bd686627f6029acc8e7efd0d2c21c2a9b5effae622133800ff8b42268aa5132cc69fdf9d759d6b0ed71fb0a55f97c22f6ec4ff5c31bcbc708c0fa83c1a87f13856735fcb9a4a5764414e4925a6bb558e44d291f1b1d373c08bb60d11ab0acec79d7fb96696dbe7664331ead5f618d158cbcef105f033ac9078b70deba61c0dbbdeaf663b31f4890157b1c0fd41b205f7e779943fddb009009c42ec5ffca5f452b669a72503712b25469b91a58380786eec793214703cfa808567fa54d00dd73ed9dd35b1b656c30a202e93c808f00ef3a3feed9fefe06170b942b6316bcde972b0f25be5baa176f1d92c88a72c894bcc81263f0007e150a0eb473420fa32ca179b79edeae42574fcad109affaf43a79659037743fedae9e0c5b3efdaadcedef8dc126e25f7ae6ca01479a169d3120f6a80a69b6e212acd0067ea0aa1587b40646d79a0db6d2f269a18f6e0e693a3832ce9f2ca43fdeea0709cc68327a8c98f80da4ccdb09962623fa346a1aafe2018bf55611dc3518b5b2069d46f51d0185fc1e81b291f1a38c581274676d5850ccb6f4d390db47e85aaf0fb1c9a8ca5724e5f667ba63af9f01fde05be01269271070814b77e323968ab804f6f75da6c236d916e60898d1fbddd2b17443c35946a6b9ed1070700668afe60e576f250dc4be110490aa89a0e1c3deb1e139c257b64576a62add3d1ec4bfcf03a0f537f6884bfbb8e0bb595fb5106bd63a4f1ac01c9cfcf6daf9a961418b5806c8dc4e83f821cf1f2646204470fc7a16e811979f6d3b807ea4e2327292f0270251883945ab5e69a5b7ee2aacc3e4648038cafe4c557f76ea4e2ef9f0c3d5580abfbe69803f191458676ca79b6584cbf4219748be31760426617cddec3e37dc016e71806d361f21fdb609eb650b3a70f86c9f07197d9e3045b644be1fe2ef4f094b89a34d34c938ffc370148a9854e879dbc39011ea49e690e1ace3b7ac82fc0a594f9234e8d447a161474215150505ff7008c29d587e2feeb92242ae804be705a2f03fd18da30781ebd9908087c2bd702d2b71692d148a5fa06768423e927408a092a32a6d62376e0514a40c1cdc1140b1e6866e015229182ff5f0206f0b160521e33bfe9e476f3227de6894b402117ecace818011f87a72406d8ab73cda1007cbeb3027d7266552a25adc3c3e4cd360e3c50a8d969a6a2108204d2f41aec402304613c1c7df5023ab64e04df5f884238a210a2bec589d2ba04b8165d7172f092221bed19848542852d176072b56b3aa405cdc9add2799856619c1b1d6cc8d0ef3c356a0ca7f3c75ae8722a0b4936c7bf16445876d77529e3553c97e2b7e540f9fdef4cc4cf4c07e000e3e86c772caf7a9c788fc32384ab157560d1793b9bc00aa1664caaee955e4d0c80a308147560846595ba78ea7c90bf5bf34074a48d707487ff37c30a4d8ae5b08a84f2b3a8d1d63a8c0e62cb6303b6e12f06735b0f500de96826b2baa305e31f9d027adb152a52dd05456cae99c6f826dfddad876900c293ef3b1cba080bb30fddbb4e95684270f1626c40bcafd6977d192b80e014b0d1433dd4cd1e49d148349ec702923c4a1923cf95f90f7a738fd947907a5ecdb0b2399cb647aee893847cd311cb9d02cbccedd75d5164e8c915b4047ec547f490d8b49ac551001770ba6e069e8741535f9763dacfba01bff14a721444df5a31b0ac44c0bd2fbca7ffac76b773262476b3dfa6cddf540e8a375394a6a8c7dfdd009311d6ac9e840ed32964c8b866d56726580224c6408d1871e9702740758165908330873b77d43d6d63087d338ee8dd226da0c4ddf8a0a6dac03b7be8ace9e170020ea824039cf8d67f525f92c36b063c703323eef47f79f50f97028ae387b4608ba81f982c174e07be75bcf596e022f87cd2cb0ca964165868c6e606e36e1840a3a04efe24305d743b9dbcddf182dbbd4f6c38bd7c7e5bda768f10f0bd189eb0e36852ee31543c43b6123cd541711ca8d8a4b782a01a215e46d84f0e0e0ea640db29823e6b2e084e71ea6fcde2e9a866c431eb14aef6e3efbb965caf0243ebc0ddd2a7b976daca0d181d37bd097fc46f807d3a2c4c3c554ac5bae3e7e880a4e013fc3727d1a7d45f04abd4629d94f133645775508eedfcdcececb402a3252b30992c0c19cc41e0588c74b19a12b17b573ee7bc0530f74070275b0a31436224905fbb8230ac9b05fdab709c06dfd3e115a340a3452a661f70d8eb4a532cac5d1093a374e995a3c1556625b8ddbf673b7a26a35877b70e8df9813f23cc5b83f1c0b2d66b568f8ced651adebea5d1523090d21b03b71a6c0a98c90438f34036f110e673233691a707d7762a960d2d68df2b4d1e690d4b41aee4e23bbddcd047de40e812cddcc65beeb4711c627835b332837df240cbd7700859f43bbaa01c67490000efd397840e6c017c1979898fe66c4b122722ea01e3f2472ce4fce6ba6b44b0edb3706263a0b54160d9b86c3740cd23d5210eb663b6d71f7f83c1cff2916590fc0209953a5be73ad87037bab98448151b01d74cf0c9e3acdf1ef7f80013d8306c443ac565835f69d46277bff7ddb23cae74388e1735fad7858597e5c39cde108a4b5a246746702f256ea6a9018f779af6b22190b7dc37f457e0603fd2e595f0f6e87764af3a60265a725f21003cdde0571e4e221a880570ca4b2bf16b9b9aa0a6cc10009b5126c9b82a2ad63fa1d2e36c1affa1291480f736ed23071c0db460bc11f9958ba15c3d8846a3a47cef11ccba3ead57cdd472bfd69841968e8989201fcd6bff816401d1dc4ed030f0b7409342845e7ff747d9cbe8436e3e50292f2009fb2d95f0596b7c574606a9ddc504ec6d3595278111359d9a5e59fcf8d35c307ba1ffdbe5f7e1818655d428a369aed7553ff4a6aa1e4c3713a90c6a2c68631066690d961771f2536c1a8e12bf135557af972a6918fbfd2a08889d2d7b3937e04ba3234358adc44df0930f89d2b401f2c2f85d84b5fcaf5a00e9e1b5a79d77904408080d4174aed181be989ce35354d251462fcc18907bf8ed3b94d029766090056b83b500af162da082c412a1492b1c21adaf30e4cc92685ec39c54b6a34520beaa2a2223e6b936170ece6247d41b1d0d3592283f34d6a14389e069abd927c0dd36442c3267d5386dddb25cc8e192d23fb3f65d6e9b0e7495d709a09dccd750177b5d89f97897fd9a87467f28c4049a8e1c0934afb5201027ff5be3aef2ac7032db3409c6713852918d1a902d3c6b58c700ecaf05a5e232f148f92d986d3a0012187917d072ca0bd842606650cf4a4037299881e922cf07d8251f3672560710c9001341b3d8e6346247d10211d1adbe250c1e008459f868d288e2b6605288b014ff54a9c7517e2d4c4029d7313ab5f3597b30e2be615e3cc1963d359f83a360c2fc892a382f8c948ab954d5ef914dc5e3e62368677464827affc24a83827ec0065e7bd9d924456f3d7693c50f89fb0bc3fbb971bb6f4ae95e8012643ba56c505b6122e8a7cc21d8b2d6ccb6e9d3d9fbb63ef004122c06906abcb8192c4874c02bc8448b5d00c0405dfa23e2eab61f72e5b448b966b6a1b96a5d0682d0e99b908e46461d988e2f8ea0e9be50e5d7bc8a6532dc6ff10810db07c26552ef28add0074be01c83ca346c9ac512b916eb25353ccc40d6e654d63516bb6cfbae6f68e0382117d2d9a4eae4025895808e33a0dbf5797aba57d648e5faef36a390809d703067081ad731ae6a8ea608c985163bd55017869915863799f3ec1f6d267446109a6b61c9e9cd57ec29c11f40f44aeb4864bccae718c539737295f6d4268b23e044aaefff14fd72a3a8b60e8ffb75b9e8f93308f85952922aa41670cf72e6b1d0e3626cb4482f62cf795eb4a9ec5fc1d430dbe2c151e0b10aac270a46e2e7b4b06b5217a0e109ec387a96b80ebe2ec9421d0fe191d48af1bfe5af3f580a68d780547e6596f51aadc1d0ac676fb6f3f13ea090415404d8f1456b80703d6f1e17d09d5ed9f7b3e706a79781bd8438f57d9b6771fc1868e05001df863e93c33896e01c7dc5e018420b5c8a16c6155fe79bf7cb1c072da507955545e396ed0c0c0200fd3080cb3c4a14b2464864f24d847ab00552d0ccf79d726feb69dad13fa6d6502edc322d48f69506aa21c5cf7f5e209636afa0c58e546ddc30bf3591895377308c351263125480274f344749c4b1523f159d2bcd5a169685e8590fd9b825cf50c0d7cbc5cc4226526c130df53da3f95492cbbf922887823505726d1794079fb0e0118a4baf3a055fc2071ac1423e381183bc300382bc0d79c08f4f54c908d6e0e4419ff4c6c1f6201b439d10f5db792978867d119da58f3c8fe3acc6d48fa0606d46829d034a0602b7a5115074b8fbebf0128d06f2555ed4d4bdc89a1adee690572459f9d66c0165f15a231484475a0f31ae4c83c509f002b322c9ac26c0c690ecefb1e6735e4d6fa48509ad7bed6f64a9a7292b91978edb3ff3163c2247b26060bc03439b9e0ea8736ad5f1fbb17a3a9367b9addbde5eb1bbfcc2c63fafb2606417cf27effff57830829c5ca8607e13d59f3f0744df20f96de5eac4571d8050f2775dd577626a04a6b6139a5b8bbdeabd754a7857dc5b3cdeed271f74693ef08e5292a7c9654ca484172a3b0b5a96ddcf6964b01cf09220f54798d9e50c2830de8368cea8d04aa6040cc51e49decef02119b8693e17a769461ab72770587530a15136dec217b355687aa831cafe42ba9fa54d5e193b160d5c4fe4f9069e70702ff015c230c320500cee4083f63730a4f953ebd7e201230906b85af181988d30fb744f13bbf8815447158417758ca84892728c9b9ea06468aaa3d0ccb8be24c00e9bbe690fa39252e88ee832e366f9fed6588867b0c1d2e35efe646cbe1e80d0dc76f28d7e91b7b8c0d34a9dda3fd48f641c4dfad099e140aa5e696ba0d5b8e09df91c1985b856386d5da415602d89f20031a5cfe756a5604557151c26f29d40c0884a810635e35ea024c6bf78d59275a1bf88bea149d5410ea65e0373634e10e51fa76be916dee434a1e0c35f8467040c5ec6e5f279295b304475a85aca8f308471634d27f44b2f79495c569d1beb8d8a691d747c495f88b7d03748c47fd1f09a8054ad3e70c61094a468e7d6d3b6bc0bd00c4fa274209278539551d0ce70d0b24eb3e9c01acbbf7b140c01a9c3f41e0e242390c92ace6d5b4b18a299e917a083864065c0173a04dfbe6afb9e17e711855f8f7524b556a7a8a8478c9bdf76301c16354a0783f2de14b29c33e4eec0ab382a6080e91b552191791f3c9495846022ebec9364288250556f1438f624386811b8cb874c44c38beda776c79943e390f476709a0bb3ff8b4f9f43ef9ddeb89bd51f4dcebdca5f82570cef8022e136a0d19f88e8fc5c67314b7d84f1268c8c7486ffaf7f6c947c421e274cb5a610b27069d0362454deaa201516ee8b6de5349757e75a3dd84d82cea70057eb468a6450eced8913204bd09b72c1bcb17f8cf665bd4a2ebe704463f81b080206d754fab0f2d3aafbbb525cba263fae4f179c2316847a71165e708d7f2cd8d4755464a2700e87356c18acf3e2d7a8ad6583143ed62e4b52d5de88193f055682852d90fa201ace8889c442da7abe320aa3aa5ef1c2b5699334d7b6a1ebd7a6bebf522f2fe0bb5abab22ba7e76bece19ba1921727184ccfec14e303d2217db585267d84df70bc1b4542125dff4d698de7d0d7c9edebb9eb7c2a6c6d9793d6e4c987fdf50e60667e1683db681a1137d927bc2177db14f429d5b0bc28066e3bf5f6d6aa104b70178d1b984bd1db6be4de3027e0adb99080ef85c06da91041aed5d6b76e6ab220c965061ad6c2b0429aa1ee4b243278e97595421ed334c55dc95e8c324fca2430c5c8573dc987f1ddf1b62c470441cb7da870666307adc3d9681827eac056ccc0683d8d8ccf6ca420615a95a11cebe3fc46a96ae14e3f3e3349b5c17b24d44360689c235b57450591c4eeea5c817f55ac72af806a8aef4300b64935c3b2d8cac0215137813d27c8bdd32e1552262dfa1ace9e5b35f29d08bf09055c4230b9a280d401a6ab8dc5e29429c299e10b1c2e6981ebc0fde4b137c6e2711fc1fa09b35095d7593ce29ac59c5568a331ccf1ea0fb73a81df87da5712e1d566dcfb0d441094dd0d967a7e3b11b58ec87b0b1303b49eea909e60026b0d40b8f6b47f5e4dc0afa512fc3dffbbca5130b6b512106dca3e516278c08114f5477827010239d000a9e0ef4cc264deee48bd43861d7efc4dfb7ec5df795f5986715f1019f17d2e50bfc1845077711490e9fcee24f9ffb62ce4b1e87bd24c8234f4163b5585eed9d0b3f2bd24167e95ab13ffda385d4b0c9f6b6874c162410d6fb0d22fdf617a55d0ed4a7c24ab6b52855ff7aef190f4f5d8be20f066611f4261d35cc60fa750f82063b71648902a71f4a4170d0236c9b79374b8d81fa4f1c712e5ec009d5d5a6e503649f4d8d264be9f87f4b8e2258de36141c388c8b518cfa9cfbe279c59b6860022cadfff5f6f054d7c749f3dc33aa1626115f2bf589b63fcf481f7b7ad65d8102dad075e40898b0ce7c6995eda9ca8e5f003b013c25d39829c2029112946a590d38be590305ad698296e3f5f875bd30db7beb3371d499fa7096d56996ed7de2058e6fcff4289ea480253413789c8fe783488fd3d35d42ad0e573792bc65c50401a209dfb21009ea1f9a23a8be849f60e9c80adc2d45a6581d06dd190e52bd860093f02d60e678523273c84a0a95a68304cb354f27132f4f831204bbe34f176a0e8014d222eac464d509646225cf93c08de597487b92b41f357e9ca28594f6df0ad6c9d77caf033721ff8c0386297c7f79f0c29d63b271b2a38c4e84f5c51fdf0ad57023d7010662a3d364be2c0eb599969f7bf50cbb7c68db2ba09b0925939a04c047e8a897530e7ca52a2ede1a350d8d5bc35b64913177496ff5ea5fa968f90afe922cf2eab49bf99c1a3ad96ec26f7fe095632553d6104331838b7a1af92c03d0ee39bdd17d54db85d13edebec0abbf1a5ae2954b53fafa3978ed59fffd460dec8d86a0e5874c7d7a6631a984a5cce6a45e07e0be15f719cb89a6b0b0425a0732e6172b4e11be38037db98e748245786667dfa5ad262a1bd8272236d9eeb3069a02e1f8d375dcfbfcaa78f974eb013db6853ae2a3184c9460d1893b27ec4f0df8523adbc02b468d391f092d5b537684a6a32bfa5ae74e7b39ed563a5e7b470e0596de12db5ce1a9990a590d77c9cc4fa84140654df320e7c2381a0296686c0ec3204d974f91481d925f2e4ed28a591ef3f90dda1f1c0cf8962701c036055308753c67b864c32199cba383acd5ffbf67abaa8a9edeec710aab0e16c032a9b0000a94fa13f5ca1c718b4d088a4cef2e0eabdb0e498138bc97590ebfd95924d2040ff20633910c8fbd743d31bc33f5b51aab44e671f3739841eb22421179f758021563a70f0d12cf544f7a7d41b72f70cb89a7816f53cc2c1a342cf487097625002c92094e45ee2a0d9b239814a16983d8f7f5f8a2f4a652a13785ba0966e5b602535b21a0b0843937d38fde78e2b9f9ad687fee9552803975fbe84a61c417d10a66be0e62d0af9153c21728318a69030aee1d0613ee2c3c013104a6387a40fc03174c1eebd8b62426217a34f199daa5024f3549ac9bcf1ad48b890eca21594f059ff92e567633a2b41485e22bf80121ad3c05a5fc04292d078a0ad6731a8ff3092962e114d1916bbedc332080f57eb4d782c9bcfd9b0f58dbee881a1bcbb479078a4c0430f34494f3b383f1f65e6342973ca0ccdd25c977081754e55bdc309a0f20358f23b1149e8b8a2b2d2d79395a7509cdd67a2ec8e5ee2cf23fac4a9bef043aa486010709ff57a1ebe5e22b59a051a02463fc2da3c0a98a62b7546fd9d70308310159b93357c27fdd1581a2064316b922a254e00f420153819ef13713260a530126ad6c9c4b14905a53cb12a85c4e07e872acb632209c618e0e0d5d75f406f2ba91a38f2f0f1a8df78905be5dcf4bb748c016b8407e9e9fe462e43eba460034ab8e7931549023fe5a02b7a94f9ae2737d581d7a9bebb09ea8e9e11282dd00c881e811016f17058d25ab512044bbfaa8616ff25c4b004379bb97f51bc037032ada36c0e3bbcd17f85b89c185a9273d2f3cef9474f3f1878662cc8979f27405c824a82a1a244ce15bcf9f46af3fbc75baed709559b8a4e6f01af0ff5b1dc9001c547c5fe4b6baf31d8a6508093c575686c10b115c62df7b2ae6489fcc39290aadfd180b1866f6893d67421868ca7f8b2fa31e041e8ed01f28b439d48f1498034a58abfa14ea4e9a0a5e632828a75f10c92f7f2e26c3647d6674585cf5dbd30672158f0f511dc5dd19aad79201c54d84fefd3f26d701ac92a3ad4121dec7ea0c9c73017b51050fa4e878bd89f12c6a31233dde4b4211dff95966a1da994093023db13b193ab6172c01d99b589ab8b9ef20465fa3a00e9c18e53e3067c3736e0d73a2397f0ceb8258dd1dbb1e866eaae7dbfba3069e065b38610eae179c174607604ec5b6e74081ea3b505912c8256264a2a74ba85fbd53ac87dcc8645afb0c0c85d935cec97f027aa3077f669dfde7cf64bf3da3695946d55dc389dea25c8a057fe2f01456a5543c0cdd61d3ce266b84bfc7503e44c6abc018bc6d9ced05e80969d4e2733e29dc386930f834910ecf5422f44933c80f62b64c4a497bbb1481016dc88194f2d90d03cda5eaa576dd50c7b16d86d9506e88d140fb14db5f0c200d6df0f31f40086bc5e4d6fbcad089e6271cda42128e8438c550cbdcdacf1cd7017af6df8a1e07fc4f95c30a196fa63d2c96c46d4843df6a945f0b63cb55600a010da79dc7dbed4da72ec117dfbc67f3477abe0dc90e84a4b12b7ae75d8590ef05af26b896e8e05e07d45e8d98b3ec45ce44b3969dc4c805ae01d175d2ef1cf40bbb5fc426f5157ff939e4decb4345c08d911f2c731892c6906767401d54da1c06bee8fadc48a0d3815f62991e0caf20513aebc7bb461797e74804a46c44ebee0fb62112594ea8aa0007029a23f443f00bc6e813d1931b05af9df78bbf9bf34106cabab9cb0a322269625fd90a549c02ed7f67b2ee57ea931a3100fb133d269001b07ad90b5edfd09d6abc6b1adb55fb58c5fcce3fc3db45847d8722edc87b24026ea3c07c3ae4030317533429ad8ef7c0d64151ec4503539d901b7218c4457105a79b887e0b9c9a840482432a7de0c8bab53a833fa900e318fcddfd2a29691d0b403d6e4522e0d6cb9f6bdc4eb90fcef19be98884bd22ec0293e815c3c48d190ab2c8947c71b1a56a5eb517166429ed4b2ac0daa5ad646a4e8c377065a0d7630b50305a84ef8405512d04b5697904ab05ddde0ed8b00c40b81881b96c439da40d64bbe9f4daa1cf37aef6eee18661652293b3a797630d66bfb30c42fd58873b098514a4dec94d885949265cb6aa7e9f5a067ee0618098e32cd68b560a6169d602727136ef2fbbeeaec331673e5eef864210383fbbc5703bd126fe8d49a8ba46098a69cbf0e8199ad8e3074922da1e071b172122ef7e03c0ddffca7f90f7e5050396cff1a8a1dc57f5bd51e2fd58ed7fa5592876fdefab64578e958b93e4b8da042a6731715db21e82f2e6d44ebe004ae1a7964c1434357948546cdf4fced669010218a35b3f126724a3550ce5d84455bd9bed9b9382597d3d1d4a286b83de9006495fa9157f9ad752d417dfe22065342e48392bd5fac78b635a2b0bc0b39e390aa7a2125e09ab88b5a5296de854407fa15ae7bedc3bfc707714815809ac107b02fb784ff41071904ac5e30a97034103657429248337b9a2d71b7f6b6f3bf35d02610c842e39bc4c1cc3acddc577ad981e7f8ac66fa1ae5ded8adf644718ac0707585fb4e7c7dfe2af886d73c3ca3e22340cd5482158f94c97424640697d7fe40d7e5f88a4775f051ad39bfad1315d4e4fe25e582f3601c5e98ab64940095ce90f6f38c1e0245404f0fb6dbe53af492ed42220576ded1a91bf69c7b7334e389b020fff52cd8d02efb63e5294aad3b251ffc5c5589a54bcb88988a52430f0d6470f0670e9474509c95a27a7f48274d0b19cb646344bf152477518e4c2569174d4067a2454ecf7e49f1572a3fb2268c9c42e74370caa813dcbf1df5bb3ea3843d805e2f5aa53280d4cdfec3cecf93cb241338efa966dfe9d6f7cdd21dd7f1e214907e0a5e36b0f5cde5ba97fdf44921165c89d0b1133503e7845d615bc823d590b0a8c1ce106e786adb7057f71bb703869718e898da95f98e40247f8fbced78b7009136c2acff9b945e65b8f6ff12e75c2976ca6003a80daeef51a4b5a2a342f740faa046e3a2c471464070951d4d0982892434691633ed793868f4ab62badc1b40ebfdc189119b176eabda0a8aac82b867b3550b813990ce55779873751b580090febac9f325d05491c86c82c501cdd5b1c89ffda7523509b722b7977ffa59c1402bebfb8abd72c6277b5b007b0bc9544b87b1fde633ad9937bb3a4d3dfe29ba40be0ee20ec27b14e88b2539f3e6ca52365666ace591646f8de1c4d2255eecafc070f935ac86a9d35b13d955a502ed3aeba5f1d693b78dd3100158e47994eb7cb0ca210c82b90fe1078d066d9962d232c6b4806cad2d1d912c4645c2b85ca23590f5bced1eb5022665726cfdacd350e5ab495e4b355bcb3aa89c36d8f259809710c1b2b8fc2f19971d7e3c97de0497bfa4c5ee4ead192da88db45186c1b57e21304193dfbea6fad818bd06fddf73557a4621067d13d5d2bf136d8eb60bf55289e0387fa426ffd9a8d1ba5fd9a8c1e1277c72634bb454efae46a5d7b7c450fbb030804a9f488f08e586432b245e31f0888c3b04b042588fe882465e802cb2a022a0752cd061bc068752d716ef185ecb975d95b1720d95d97bb8d9e96a031f174b00395ed237bc19835b39868247364a487bf9935034d5dcd63d74462434bc5d12c017101445b4c30d793803e28eb06cc735b6293ecb696369c6f6a43601da9824b0f30279fc1c889202a4b6dbe2e049b84bc221ddbda2978e019e5d2563fc750c109f1e23cbc7afc6c090fb8e4dcda0621f59ba3fdf52283e94fed2418f7be7aed0ceddb81d3391de562de7d929f1d5a45b274367a88d628fa75032d229b6c1acf05be9318201584a12c66ff0098140081b2ac0eb2720d6cd978ce58de48411861039b74b52ce8ce09b245318812692fb89c3e1262c6e0fc9cf3adc2a989d5eff90b9936dfd48d37472d9300712783489cb2761a4b9f8e39d279e933bb97b16d6f0f false -check_ring_signature 08c1a1e9dcb1868d9f2b2893d910bad5f9209cd12a816bed7546afd83d53f313 0f1ca4c18e93e0b5ebe2b47b0954b081620a98e510b8bc114385fafae7f9fc38 2 d9156926535b2e93f11b45b1d13c6767cd8102d59f2c6570f38a70fca8b68cf0 7a71b232f0cab46fb8dbf184b91e6c4ee5066b81f8fda89fec869d1b7739018a 16897b1fa157c3e8be91e304ac5ee07e36d08d2e4c75ae38968bbd819b9dd1eb41dd2c6fa961f962655e923c166548b59aab437bab19c4349fea0fedce00a89ad15b6f97f6f72f686d49050ea7c1e564adc607afc38a13ea3d529bbe6a33680efe6a28d710934857a871fb1a8d246edd7de3e6ee8ea1f9ed6d6b6eef6571160e false -check_ring_signature 851ae2133f5d5c3aa5650eb65d3154d74102801992af82a33decb96760e8a44e bf428475316acce11f1dcfe5b25e416ad1c740ae8122c971ab92538f76c2c5ff 11 ae01898c4d200e7a0de47fbbdd4a647daa83ec88b308e7d2edc2ce0ff0e48e3d 3e012d2b2e3d1ea493a4a95da8d21e42c70f818079edd2357e3bca7111c7425a fed155d339c7aa61786a329dde49735845033b4c1abfededbd58ddb1eb88db03 957f97204c09961060437221ccebf4c361216dbb427a149589ebe3a317ec20e1 c4f944b04072c0f48468aa0788e9b15f2df36c563dd445b582a7b36b4d731c23 d375283255eb2b68c3ece8b56d1fd91150c5eef4fa0db300a4f37fc9f4f01446 f28753a6f8e1428ac1ce418bb8d4f44f49cfe1540c12637fff2c6bb5c5ec6faa 0f80af95238c8a1a90b867c586c66a27f2b349ef7e7a63eb644b60f13708fa78 34eb620e6ec9ed5bae1a2b1b08564125dc04d1ad2586510bace8b5498f9c6c5f de0b85b1066398c786be1d15c9867d1815bac9f5567fccdbf3c853bfce6cbcee 8d8a93277f1ad365246bc4930736605b1068d3205bf7a415cb29ce673ce7c134 dcef624abdafa8d8e14a0fca57345eceb2aebaf27d3432096479f034982d220f7d1059d616e7e65cdb25f90c634ca4dee0d71ac22750f0641b6e9c6b6f5a9907d7c9ba49d63cddf8602f6170154e5580b80b2eccf1a4e68a7dfeec21a9686804f1fb4478ab0ab7aa5db12a4d40b5a24e6a4412a34c013c0a73163b4a143e9900bc988880e2675c299d72e31c3c0263236c98d67c6aa5956bb0350819e46d550c4cd7f135a319b45634a4182e320525666c67f89f4fe31f20b25e419c7d16850dd3340febddbae68b2b3849aa964c4436f473f293d2e6649f6953b2e2d3f5ae0bb5b8e3c1452cb06a6612e88710b5badbdb086b1d182dd6b57d6fbe33fa6a2e017e605e3e0d35490025cd50c16d8d2f5e3da1a96541688225ea5db933e6d4ff07ea5464eaceb9b743ad9932b6765155f1f989967e68ac917e2ef9c151b866ac0f788c761a101d2b2cf85ac03d323aa4fd2453b60bfc26002d64260659f3df8b01126f5189616c30947766bd870982f4b52d64f8aab5cbf496c02ed863c676ee5e969946f48b35d5a5f69ca855cb90d0fb8216999495266184a1a239a256911101e688f6cbcea5c3698cd8c0dc6c91939ac920ad879a2be753804c42dd299ee7048ed594a63c9f131bac77c257c24b161f12bd2fa2298fda9eae55d43236eb8c0a603640c8c208c0409a7c5a8d8fe8593146fd43f47063bd33e483aa190c19be0633954e6821a83ada6d5ea0eeb88278bb90d5997dd7d4f42edc83fb85c181b3057d857623d8750b16b5f526febf440e729355527daaf4102f093557afed7c2007f9d5c8e60240da97892f7238f530efd8d6593e882788a6a04c732b3fe19dde0864e6be18e80b34b73c129d395923c2a0f5d4ca7a25d617937d70a84d67cfc909d13977351a728b5654a716a9f1852ac4e9835849b693604f062b2bd4a6a020098e649a8cdd6f96042e57cba884b1347c8ac7f330cd5d7c1d2b417c23562a7c0e false -check_ring_signature ec82f600d3fd192a3591adbf58b6ce681982e97e0e4362c658d1c99cdba3872c 787fc47e17fb87ac653d3b3470e7fcd7407122f84d49f56e2a9f610079a34bbb 1 8871f30ee70e3eb128b04dfab422c1ba12f89bad9f926426bd7cce4efc8276fd e73c79137e8c5447cdb13a94f1a2a45a3b7097a54854b44505723c08125e6007c0bb9aa87968b16752000fdf1e6c06b30c5d75e2abbb6af1436a84f8f22c2202 true -check_ring_signature 1ef775524dee0986f1699979f5ed0a7fc31135d511f3c1db9b33352a60341ab2 c8fa887eec47d60da619c2b688bd0e8c69a645d21c7209e8941ea75f63b2e175 27 9ab6d54fca10e28c3b57ae94a3e3a1672ba875a87fbb424b99fd9de12a897b7a 989a0f54c800cd01fbe11d9e763990e56d4e0181971e1ec6293cedae57261bbe 42b838f920cc2fdeeb34fc730910a7389474c1599c92bb502ea7026315370079 8580e475f0d0f635da3275d1798744e3772cfdc1519b94621a01c9eaf1768b7d 68c9fc9908d62a0b2719487c1fa8b2653511aa3d31cbabbaf171a52875c950bf e0cb1f0c7ecae7c5620483b99b06b507fd1ff0c6f1e2bbbbef196a22b5741512 1cfb0dd29e439342fe268180b52ee613101e200838c9bdec8da0e0ec8e55b5f2 e7fa79bc9feef6edd0610c91bf503a2b0bd4f2d246e0abc10f175c713bf2f984 fb902216dcb5b5a16cd3954d9ac8c04aa5f8935cbcbf33ac18344a7d124a8606 08fabed3f2479921979dfa9a4eadfacc93a85638c50899ef8659a9c6d425ae3b 58754fcc86caabba9a1fdae367c4c5045f4e32a059d35493b08e255f146cc3af 3c095e7a9a4353c83a0d21db0efd077a348cef7bd48ba6890d29e801bdeff924 d8b88be1917500306b9cc7d82648de935094b50d36fa6697da71a34a77ae7e96 93272a3eb6acc5d6c600461bbd0082b1c660a0a67b776ab9af62be03bd2a9e7e ea7c672e8db2a62d48c1638535c4958cfb4f556bb775f3db0a4c2d4570a18f47 ded4cc29ab55285dbdefb2e4749d3df7d6381ad3b481528bb43a76d6c3bd5365 56330ce1688a4f3ef564493371b8cf212c32809a864ed76bdffa1ebe0ba9c7ac 61fd2c348c73583d78f203d4ee978b34920618e88e1f913f02c9d8bf743f67e3 9df1533256dcc505d7c343d34fbf5aaa4291bbce99b15ad8647237f05beb3507 22f9dee094040f7f5cd850933aa253209b1e5efe4f61e30b537ab9062a166ebf bb1aac221109c19bbfb8ff69395071e34d483ed353e953eb6a0a4c27662dd30f daa0a9477d4ac2d252753cc4e0254f184fae92324f51d5235c0d7fb8b3def0fb c6c8e4b26796c2aba6fd2826273825721233bf3131c2ca83607e35de875e8bbe f27082af0f8a4fcf1f5c10fdf6d7572a3a4437ba3f05e0a3988f7301bfb193ee 58a1fc00e34fc2a2ce4ee693452ff38490844ea18c537003f50e8260643d62ea 884b82a2dbb781a46426dfeab3d8f0c646302622dc8e7680d6011ddd41d8eabc 93fb28e09e0a6e3c662f01c8927581e3c748f51d3508d1e5657436623311acd4 085c6953e7d6f717f422e01a95bcddc9a70b3825635bb586a23b82e116a12c0d7f3960d7dab05f8081bd4233aa56c48d96c5a4a26aa87f516482e9719eef6b0d4ab93f1878f388c6514b4f741be5c513d74f6ccde5c2c33b877d5d9756f05807cf8a6f4cf63177ca795bbfbf050c709806dc47355beeea6673b7a6ab078b6602edbfb16edc9aa08170e94aa652a97e05a4bc4991f4a646ee5e465cb704c76b011391d57a1787e9f44ea56e298721ac56b6e038cca99ab895706ded9a5b706b04bc5f63857d80b573bdc12fe438b7769e3535b63a3f2ed8fc7c24b4bc3cfdb108b879df480aabfbac9073c8c17e0b8a86dbfb92b759c0715c99c8eb2bfaa77601cd2bd81e14cf994913b5d04ca7e1a08eed1a68d451933851ff711bd5a47f990c32014e6948591ecf377b8ad46f973e6aeecaa3e9f7728b0d6261ba9bf976c90476e344f7e0fc1cf2c7b19e6371f6da7b1b29e1aed4b1b99f4dcdf1541cb835075224b5d292db8acb2bd72eca0c646b2e8d25533f226a04952046cfb3b3f80101be623ac87f410b4f7c957b394b37e70f7406c3b154a98a40054aebccaa97ec0232d7edfe1590a44aefdb145c98dd09649d564cda7cc05114b48d978611c2410975f82d028077d0cdd317f22a41874cd57eed50bfb612bea6a9cd68d4d79322098865ffbc536ea6e501efe1e8334e22ae8b7515d92efe89e9731acc05b958b00b07adb9b87bbdeef4779b099809f906d5e4ebcdc27b86c08a2c2e1f0d7ff1670e6c3747176e2ebf6819bb3e25437c32f6ac1119c2a9b5e49da08d173f324f120e21b306303b91696635ad9f08874466f0e603e4ed451f9c75ce79cd6215dcf30bba16bd9dd042b59d556105e763e2ca0217341b73975ba5bfdae4fec93120700b26e25a9158fffc6ed37513134be859b8863aba45872e1c9cda45892afba66403161cc67453b7b66c290fa7989a49cd3512dd1219b2e0441ab3d1cfb8f8006e003b19f18a1d243cab5dc84b7823b8995a3eb833635ee36bbb2878162d25c596058c98773364f96b7b7757a00dea3a36c539005cfcdc3676b717a8f4b2ceffd50f3a9c3ffd45999c53b54a8c4d850267529d6c401aaaba1978252b14ddc8c38e07a6bbd8523ffdefeba44fdb250cd43a1c6409b4b8b622e3860b7f44ffae307d090dd02de4240763441a319c2fbf362282174eaa3d05a23a802561bb4adcbf4501c75079c379785ebc78a87803b6992ee0466a9b7478663bbfde777a34a7b86b04cd33f71fa3233f89b33781bdec955783aade04df0b3422610d673e60230b6b04ed565a3f49ac52d112ebe84013ca1e273580cc96695f8a2d88ecb884f36c8f00bade98a498ff1278b1caf13a7a51a702922e0d578d96e4ee5cd946febd1d9508a5da48c72fb1f847ff0764223d503488d2b16734748df8d559b6bb8adf2de901544c470fae3c405341e88b6f488c8ebacd59dd166bdb9822adfbb916c1d53404bc58adb2f2e2e24426984fd2d041b6e5c148f2d8dc7f8833e0013af02a780f0b2dd59603a22c68f21e49cfbb2b63e55740c1e62942b9fc4ad38ecba73e92930bd152fdc8ec135421cb8fbda73c27ee719e6cbc9c1629dbe55a0f3a3c46b09d04024f073c30b1db390449c14522bcf324ce2c610f56986d1be58e274af8303909ba7e4d1ed92d082f2f615e5b5473da38dd93dd6a1daa0c2f0e9ebb5b8236bb0d10a09bfdb1550b9e3ad9fe2119638f4d311c52d3c729459e1d88cb6adb55a6009f09e5dd0cfa126e585501c98e134028d282d56db839b36e66a1437e1920ae0bca4dcb43735ac94357284835824131921e45296cd778574a0b31eeefc67f51099f3b0bb3ccee78e3f5a1ab5f8d62135ea11fa42e920eb0a40de3a1df52914e03f24686315bfff704d5a50a5ff340442fc006a8c8ae6de254c61fdde59285ea017e6c2b991eeca3f15f809d0c66aa0391ab4a9335b28374f2c86ca8699de6e60a85c74392a471e9ea26f6b31e044b9ecd683e6a40373d8dc42f8b67325d80f70e74f3e99821214045bb41991fb96d37135a55ad63182da82415bf5242b31e530bf78cf6c3ed6da0ec0fe9e20113c2c848a108e48513ae833aa050a27b37aefb0096e73b777a263a39ab45cf4848389f4a9db79236177b3381f72cb32806ecee0565cc2b6bc5b733436716ac30a4ae4aaf0ea4e64174964b68bdd74d916057cf09d4bc5735f919d11971dd1bf89d313e970cf0207a9ec53d6235ff5f55641b176546e004ddcd7acf35ff93aa55a84e3993533d5c25a049999bc1776353629633079d1b5a684df8e854533151d8a636aa1c0938c6272c4a0d7bc4f74890378cf207726f3d03733922902f326eded3611039bccff42b11aaed0cffdabeff04f76207389559ef652e6594014a2e17cb1e13cb9d7dfb2d92072b495f181ad2d3085800 false -check_ring_signature fd26862d02ef1cc4191e686a39c09642cecc3a34d134c07d55a23f6336c7c216 6ef14dd0ba2d32b934f412240d739c0b146d0e5be0d9936d9da38727306356b0 25 777f0e853f4a153a54539aafbcd31b0e9d73be480eadef6fbdd19b599ac57d98 32280ddb6aeb7ff981ecad14f4977d1574ebc995081071440c1f514fad3db52b 6276d6004b4b2b76044625d9799af1b762675c061992e71f0fe2f79f643409f6 cbb7969899c584832cbc60102ccb22c1f2744fe5444a93106319c36ef28e2b3a ec3e90879d3871d657325719c716c30e6edf8af46dfb81b3bc214de1404fd9da 493cd99833f66d8d4ae1622d5cab509d0c6113b150fbb2575168671392d58327 b579485cf45ece8f08ee2177f146e15169e0b7ca13c522d0ce8139e95619e346 527f7718d330acb16bcece078b56867472b7a4756090a98083f5aae6dc9ebb04 2f9b4d2e19983d89d1078adf1d1a1fb1b6feaac542fa6238607e7fa606c19f84 f19be184fe21cd1d9c5e44109cee8ff09ac075f4ce80601043117def3a122d8b 4ab51a91f25f39d7af2a62129517f2048b207bcdb2e839a084846a159ac0a2de 132d32438257c3e1784fd18baf5d955d91895cc5624acb3a582181ee092393d1 a185387ba561b38b76be2acdaac90856a5637261620f281fa37e856f65a88635 4bd147abd02efb8ffee3d05ff3ec80527c75dbe69245b7e63bd9e7fd630f746f 3170460ae215c577123e73233d9e76695ce213b241fd81f175e69623c86b2c95 5e00870926f45e48578272ce228c4a7342aea826975850e6fd71052488041cf0 20c4c4009a0f20fe62a8ab997505f9b8a4372886fab70a98b91ebc30095f3b65 969697d27638f73a3b6762bdeba8847547593d1bb2a09677fe558e180217fcd2 7dfe192ae823e2dbe9362d636c857116c279d4a021ea4939ae780d1c847d97bd 6667895143587ec1f566d31e1c1838e4e67b5852fdc86d4347326efb2e7a4de9 47f114828d7a1048da6ceb4ad277f794b6d31e328513f7075269e70bb4d7271e 387d486c62e07a51d6fe29bc1cb21d00ab2903cecbb9a94c9da281222cf82455 3ad0ea1c091ee5f2504da699835832cca18f77cc35397a6efc658e67b213e4d0 fd4c9eab311376f58997689c4257efb968119a56b7dd1fd2971af81b8d9f196d 531d0f594b7e065b63bd658a09042ccf9842e2566c41815c6b7e798443506402 46d54429d47c465169e182ccfe6fb86bddcdbf12ba7fe1569a87ccdc8834a10d4b342f86785bc4bfe7c9b24c42ce841273cd829fc29284bea40b20d5bde72b005e7ae4431551336983874e0337ce4aa62994519f2a4617c79cc3f1590b514e0c04d1edb4f0af5b4063c7f9e1bf94c89fd94a2924d218b7c7407a2ad91b086c0ecd4ebd38daf73dda997d071f2d3f3420592fcbd98c13f823bd6e664e8519020111cbbebf8fec513a2c822c214f8e8539e916ddc6cfa36271a955d1586492d009c9853431acc516317ea33753f97aa2624d32effc221b68e618da31a2185fc40063020e049031c8adc08c849f1635b4368b04d0a1b5357c3df9fb94622e73f009d723cf980dce14fa8531cec6889bf59ecebf7cb7dc95cd0fc97312a8c1ffd50f4e387f5842607a48b3eb2afd08961a8a72154d4bdf039b3228f4985ed5ad8502e100eada4c84c0e652fcb4ecf0edde1536f64faf299c4085d145589d571f9a06a1711e22176b03f3056e957a730cf6104f05849d09a6bc1dea79a82adebcb802f983a96625e5862070b35d2e6d1bb449504023f0b23191c9fcabeb6c31b5dc04c6f1427df9bb79defd03b52bfe3e03bd1dfb1de80b16f31717062bf3ea801101f6aba61aa3137a014837e82ffdd5c31f4f7065af9c28c76ec0e13f29f76d1c076b0d24f2f1c2bbf65d28619e3c327e8e4a422a0f0329339edaca4e1b3cfe070d2ef59ca1930a4570bf6665d0793d8e997089e6141a318141020864bce954980ae6206c855b08d3156f5b626b491be40fe641a4ff1d07ee7e0133c0bb2af3b103f64f5603e3e9ef29685eaea4affd9138b3cd89d47b7d1f0c9bac8608ddd9c00e68d57e577004b1ab2b31bd2a7b301672efc59f97d7d0f56c9d1438d36e23a40469ef06c60881f300d8fa18da0f227fe7c847c21ec3827eb4925e5cb560d689097d2efb0c969dbdd77c83f5144e79bc901365c81593e48402ae10c8e8d6bf9a0d8b8a662079fd45cc0a893a65612c2715eeee344134b7eb292f26401afb982906987b1f9de49286720e32fea89832e9f1559bc6cbd14e7ec47cf5817a26c9d7005cce2bd1981e3ae50b899e8bd593d5b13a1dd89fc3b87f1d4511109415c9de0463f624b3cef3fb050d3a8dc1d71d15b895545e7f4811d0dcc922573bae4432021bd2a0a4c72c16b498ed8e50bc7a0c08c22d31639508552d8e6c8644812952070d3c200022fa817f83eb26f08de8d7739bba08e40074433bc9f995011f4eb9046ee01894db33576c520220cd077b6958ceeea61a3023d5b577fcc286ce51da2a486f9b62e02c7f2a2a2a9a7496b04b23761a34142178de1f282a457ab4585f04b53b6cd09685052226b70df5fc2689a6762b4ab057d72fc1eac1c334a6ff970d9da40bbddba2160a73fdbda9368d2c947d90fb2ba3a8ee51132681f75542f40941072772a5472e5490ce888d0e1035832c6302e9644bf9d228df982521162c075e30ed7a4a4092138be0f2fd26a7036fb6c42abfd60663e360079d923da5600b343c06b6607b0c33a0bdef84631847c2ae449fe1f2fc93ce919504e504ebed0703741c8c2cc3ddc5d9537e55f7cf0e2789f05458d39b97c771558a45fecbb60ef6006ca2ea027745a53c619b4253a4235b90fb1b6749341c114680f9d6598e002acb7e92a87464ad88cd7eccf58113318bb23715b853fc704c31f6241fc2100a3905d0392d9ad7ad85926e861c884a23965a093b125c25aa371a4afab0ea780d2d22ddd37f887f2881c8ec5d052936c368e6d65c2aedb910953ceaaa3e20e40cd007f0d7a7f7f47882b860ca6dce8df3c356956eae4920101e45960877bb1d09dda7bea1f0b54ffdd8fb021b9903a451dc445e79bd1618e267265778ba14df09984007065fe5cdbddb51d034e11aa5755b46d3cee1065e1d9d69d3ff6808f406cd1b90d3065a78d9f1ae93a0261904deef7893624d0accb9a06d3c787279410be20ebc3a1c1ef99b85868dbc50d724e0900e41d8f51bde507180acec67f02a08a503ec995d22f266b6078a36fcfa16298b2d8d9edeaae70c05ab6d38580b7c022884d0f242dd05c55d502fb8a17abd2e89c394bc9cf146b87ef649635b5dbd0b72acb32ed5803528b2c010d7f18a1953cc237ad4722f4eca00ab4a3f93427d0b570080984851fc68b63cb34e4f41f3818da7ae7024f958f7216169121e63fb0334446b1d5d02ec62da3a740ff8219a6df2e6869d7defc61f38dec301cc9a5f04 false -check_ring_signature 90d25b9a4a017cf0aa768956214170a85cae648a5ce6983abb94427742837b36 13bbc4d5b7324dd2867d6c8233fc35486193b864ed8c79227dc74111a5c8e439 4 0274b9dac9aba74323b752727903e0bb0534a35ab2388a0a67337047bf50e161 397fe13dde690f5862f061615cacc430e1767747d8eeab822f0daf31d1cef5db 75988c2ea447a9f1dfbe1ed6fb2829aef5c01981496fa081cb6b45972d4f47cf 4755119f31b8b3e0cfae86c3d969199ab180a8fb5b46f47e20902ce2cc9093ec f8ea95f4d501376866fdce321bb1985618859b32fa6b813c6c43eb41a5913801804406f0fbeb417a415fd6cdf38b4c4a21265f4ff72f59ef3a7226f30e3ae3002fcd65fdd98f341140423e7c287e6646f3d88aeb1ad9a6be0a9dfc2cc8f758d3c7a8f8fd6d52ef38a9da46de47078b2b6c4856808b8bf5db505375d1b859870e97307160ce678228c6efe766acf8428ebcd2fe41d1752dcb8d8d31d3189640016426d0f542bced454031a1d22b7f8ff6b01db99cdf445d76b5a226a7cfb4f60986e05b3af724d0d2127d7a524b3f0eebed5bb095824fbc777dd158bab174bb08f566079d58faed3afe3c0beb2e45b7c376e04d74f84ba5ea65570bba024d7b0e false -check_ring_signature e158c719111bea4bf991ae3cd9026c71a2a00916b128fdcee2ce6ebe5e0aa132 3048ceb92dfdc34eeabd14f5254ff3ec93e7c69b52a55f5f40f619c7d58f6d49 4 9ce90ed16e539c8e12df474f423008963a809f5041329d36385e19e3b1b2df20 124754e9cec96cadba170b5f7ef069bce77317d77de37e0cc6c401115cb22269 22d8e119d9b8fc45cc05edaf8d6f934c2ded3827acbd40d339bc096bf577f070 0073e0739d5398f920e96eabd21200e98407440989805affb370814e4eb3ed5a d32bc7c858c63ddf15c0cdfa82e908c7ec77c90e18b149d08d339c88e0a0e0063aab5ef30bbe6888d5aa77418b89f8822257345acafbb2d201a00559dbfcd10a9622e19e634e0cacc813040c1b34d515b41ae3503d6c47cf26eb19b9dcbe9c061dadcf146b7d88c49a049386a43adb8dba1a08b96ed26010c91be9ad1e208d0b01ed0fb577ccaddc19dcd541d94ac342d4e592955876e9fcc6a164ef2fbb740518d463e23f2c2ef625d849ebd4ed0d02ddd51e872a06948562b0afe6f143f103cb63b3bcc026a46820de30e7007027b3259b9c97c4644c23b8d5171710441e0997df1701827929b5bedca6fb4c3db818fa41edec64e08c3ae663dde4f8c73407 false -check_ring_signature 6e02e7ca484aa78a1ce69620adb6b197f373375cc0aa618cfff0a2bd20490651 06a95d6a9945e9c8d1ff0fa1542722074b4ea6697944aef0818f5f8d01617d40 1 eb59fa8031ee57ab421a45e1bf90ff33c19371a6f525dcddb2c08b98d3f1b207 0ea3cb7964060fe784a34e30a0b5f27b3fc0e8cacff15da3b4356ccb0051a50e8e034b7b3f2f3bb4c4283a19be690cda73a3d0ee7f686716b3c91742e7801b02 false -check_ring_signature ba7c2f279a014016e39b33c08303d749abc4348ac67ecf4fed9597a60dbdfc59 26844d293a07909403f0c735b02c973cb49257ce5422599c7d6503ed4535ec38 1 94bee0729a7721833122adc19a53340cad6b490c070bafef4967a575556e2096 cdf095bfa922bb2ff5481c8412462fbe0ad16f734adddbf8384f2e830c4505737005c7cc1072eda449eff3519edce5a65820d2a15e615872f17abb20993e720d false -check_ring_signature 4966d4871e23ea3b6f6fa9988c35c1f5b07067e4439c384d593e7ef4670e691c 489c327d6b071788c9fa8abfd9d31e5f8356e280a6bccd0d2ee55e6b94482a84 1 58a2d874b8f094bc1dbd9fda7e63d0e76668202a1aa4aa0b8e836d119b74da44 5c79bc9c47532459764d2d2fba542652134911806c2e91a10cb0f24a6231d50144bc010d88b58bb25227f091a0ff0049c9e0b27a1316e107acf3f9c387845001 true -check_ring_signature a80d534a83451d2e321ddbf87124e951657439e7d832769d8c0c4a582cb1ec11 83d90dcacefe4b4f3114ac37e99b187285846f24a1fd953aa528ee7e6f62c7d8 2 73c57a5c695353a6d008b3de691818f9ba7cb41c3b950aa6247c09f982f4e452 a71a40b0df06f164b5f4d81a4272f05df1bc32b0460009f1372a2dbccccadc66 a8f9c3d49742e744f46a0a99b6a8469af9d389c4edf2201ff6f6e698e216bd0795cf778d9f7d9f3ab49a05da89ae8f9c56c98cdf3e8fdda839a747fa29aaf70c450039fe24dfe203982804c26b0200a8a2da06a1ca910feef7d8ecbcbbe4920909c111370638354ef0cca6eea6e4fc3660eb1e44f99c052083e3522654c166d8 false -check_ring_signature f40f4c7787b02ad24777d52e440a564e1daa42d41cd929ae2577fa9569767501 7b95237e6b1bafa84d643410090ef1f80a9490e660f0f42a7238daf87c781262 9 1ffd3738fabc1ddf04a81ce5b89b936da26a0e87bee7ee3c1a89bc4d50d8e27c ce5ec306657df5553e6a5774e0d28268724c21942dbd8e0daa2d9d7b5b1696d6 6ee09c92ce713f96305de9af2c6514773576d2ea8bd5299880e807264c67729c 09ce25359d881200bba71114e3ac13569928501fdea849fe26c7110555b4d06b f60d3a3082136789787f9b2fb89885421a1458c8a7be1a1c79df7e55d41e8040 3b0a023a6b9cf059e890f9d0d5f58c6152f0bcbab4a3d0c9cca0a9a8999f1036 ef22ace07f175ad12c98124c86b80e73e3751eecd2d919b3dc2c14b332094b88 e1d280ab4d0dca5c43011ec6a364125e1ce011732e3daaf48fc9d013ad22a926 9ed692d571566bf75a7e9ac4fec948431793e07d76da8897c459ec938955ece7 1da48477c9e94526cf6dfc7d27323567e29ab06b7b8f66ca494c33e1ef48940ae7d524a3ba0c06918db1adde124378975bf96521bb0df5c3cd16cfac37a98a00dffb2e181fed2c2cfb041a7a31de99a918273e280dd37c25c4ef7dda2fd95a0b3390134a8849eacc3f6543f9ee567c8a0d146674dc01b1ac812063269867810de16ea0a6c22d558ade2028ce6151021e6f7fec6b3cbc49fddc75def949aafb09766b3c6de49311612d623a06724c7e81b01a62d68588cf78168d0c8ec65897008953d085ffdf993a05251e58aa66e819eaef76ba7d4157d69c377e5b50d6df0f1cdb6eccad56cbd68f21a8433399f0247512df1d0fa8979b7ff54c44bc49f10d96376d4b393a25b4971a635ae42f9246913e5b94d2cb7094578a45826d3b6b0c9075b9029a601f0dd8a52d6937e4fe5865e33d798f4a4d62ac044c5958d28b012d1d1dda1c13c887d976c75fa026ddad8e71564d157351dcdd7a0c7446d3a604c3e488827f93aa9d50af61e077f8b622c2ab69b46ea1ff615cedf62dcb5c280adefa71bbb568102135423d43ee79ece47e149d9788456d45d3aacaefc095a700c41f2bf76df792f24992269b1b8141c7c171086e3025e8dc3e20a93a06f63206c499e3458f386e88d4d141f1b7833cfb8435f4281cc8aacb14ffcae29a7ebf0ee5f6249ecf1134139d08188c34d0848b40e3667c9e90bdfee03abf2dec49240e8b99e0545704c65a65753f8333627f95614fce8aa37fa92e39c376935b59310011c04d863a48081729b6185857dba887687aa0b2789ae7dd5af41a45e56eaa09 true -check_ring_signature b2af2f85b6968903516d7deaf2702f94c0678a8aaf3c908e27816f59219e0823 cb424bf1cd2881b1a245c3629fb075f7f98f7ac9dfdaa0ab812f8dda129aa0d8 38 311069e46e6e03a1359831787b0508749270e6afa5aa557d2c91d43a69e38b66 42b5b0fd44657bc475495afe8c920f1e1d0dc72e41e3cc79e208675cb4692ec7 8ec63623006381f135edf90b5ba32bb279704b45619e0cf1a179efb1c8eb7633 8fe05dcc8ffba82d930a7050a8c6e32b27f441b0429573d9820e14fabe8d1e0c 98211d303847f5c72307f7522b470b58b832fc3175795f2b5c88add2191b889b ec329c5b5affa409e2ef18a7efe729acce8996a4b752b1280c855d46f03f2f4c 5e460496a191ba4f7d8efdaf1ea18d3b10abf923fd3b27ad571cbb5b2d1daa3b 1e3890b6c8adb22b6090fb82ae807b114ca345e761bdb6bd34729be97721a8d7 fadb01c17e0233ccd004c61168cd4b4e9df6a0d106bdd5c53a9d074fae78e42a 4ce058c2cef15db6e4fbfb2a5dd4d7454016a12dd1518d73a8e11b0adfb450ed e97ce2d0434c1d3aa97abfa9e68b28f2aa0acae16ebfed499eabb06b9bccdc28 25423e97d6029b99a7ff8347ea1b423a67c0004c29d2ada0e500f2865e0e1ee8 329208abae261abdc158df781ae90be780498c9a262e32c756df1d6664e3e926 dc0162a35a1fb60b358dace342405111b9641bd9cbf079d9cbd60c36b94cace5 7131fee6a14ec2edf4f60529ac58dcbacf28fa04838161d3b9d6777a42e2ad85 efcf8ab5eeaa9b41e771131c3e8c1d05b70a638d0801cff44396459080f42030 18ff4f2a22736798bd0aac90c487df66a98285b53146447a56bc2a6afcad0e34 0cd8977a1899950bf24ba87561af0522f74b33672b08e28fdc5306d013971e57 6e982ea1fb2563e51b4ff07e0ffcac6b769b0b615de0d29de7345741d9da4595 6ff727b7384d7b4482ec33c223c466c653704b7b44ef244b9b3cd4aab049b4b3 7531660f0fd1d12895fd4b0ff8f6df12493c10a0356fae03c1b3b42c84064e48 d5298b342dfc622468c20b5d568b07ce4aee6f3f065e8f53e454fa09541eafd6 0c2ff02e03a07af89c0ec99aa4280734edda002154c580d5c9670ef3f16fb197 61795ac256a711a7e6cbbf52ad2eb5b3b773f101ec66f6c0b24fcaa033df95da e63331e88eb306bde6f88b7c38d6d583570775d67cfc42647e5e58a44a85b2a6 ff8fec6a31766a86001d21a673b7fd16328f68854145a14339c0f8ab3bac5852 3a4bf9eef3a4742a569c7dcacce419cbd7147c7d80b8f468e86a1ccfa26a7fa6 92ce13389dd1c65aa5c45827d44eadf1995e1225f2033f089a1496de67b50b44 9a9fe31e9ba1d112f03cbdd8838f6e2f1f112652c9e062deaa0347e4cc21d10f 65ca3fc6725bd7d525967721a9aee349815636ff119087e1176a769d630f513c a8bee595a858ac47f37acdf91f6858fafa2525621eca6d398ac25263a1b26337 eb4085066c15286618b0bb2a488b21668d3164cf0cdd324887964f266359d376 3e01284cd8279e64edee87ecfb7ed59a520097cc84a4d8531246dbb526e93e65 052b41a2c7e62f29300b3134d2cb1614c6e192b65704601db148a49749ac1e45 cf867f74ab92dd71a90ca04458aaed176971b0e24f898b106491746a6270500c 2e405cfeddc20532f875f477308634b0d2e90a56e68bb2bb4b07bc3289d42dee 1d4854bbbf7688ba1df837eb1d287f1528fbd0eee697621bfa8585222faab926 fa2c909bfbca451e0e955f807d7bb83439109dface5337f7dffc78950def6b9b e4dbf687688e40d08a2ae4bcbd36d3f9551dc9cfc831deb9e84df5302c4b2c0201f0ae05453e53f547fe392aeea04e4f12a09334472604a53f9ba99acaa5610e9bc380acafa82e398e4f0521cc491d09a54e2f9f2ce7a01fbd26f3d5209314025b745b029a3ca208eb2d6191ca814b5fd7fd5d3688bfab664303e70aa971cd00f7a6607c39e54556aa4f2a53f06c717b360f6677733cc1d60c67d252ef69590e066b7cc1b7aaad4c0374f4d9c75ca672a0e2ec1acb4620599da0bacfd28fb8008921728007738fbdbd36662189905596325d80ba8da1fee1ba5fbe68ec00c200d4bdb5840207f93eb34c86bbcd7602580972d01c613ed9987b99286515bea307191c5d9d84993f7df24ba78bd88a790cdf03cafd4fa9667985ca47114a77ba0bf756e9d72f5b300fcebaf201d06c93c2c9f292854d9f9a7cef4498e9c3a6320b2b3df7309a933b106baff4331a66c07c2af5337d56949a07fceba0801e955608efbe8b30e26b6376f0b183369cb721ce5b399b4ab9e7da8ef6969fc54dfaa2097cc98add57111e8af9bddb16d533acccc322c50660f435bdbd7db20e68f0060f5332c18ce7f102722439e0b8b6149327e177db61d633999b7c63f53a6a43480fd0e764e7cb12cd8f2f3f6ed2de51bf08c30b6c30580b7f7cb01f749f4d0efa0ae0a65443dfa5f1c00f36b50fd8880704713cce15936141e90a39ecbe95a21804dfd71b9869002d72d900906e2f0ecea60e2fcdeb931353f60609447067204001aed450690a46374be3cf638875a1c4c914fc6786a8b06922e8372fb4f1028604d4b2b8976ac6437554a85bb448d3c1316cefda2f8efe0dbb2fe66d912c1b5007f5021710e6c2d776cf1b92d87ed9775c65d020375e894675f807699424cedd0531e0d02dc5d9bd4191bc7c77337ef7590d39ee42f1377e6a5734176c1531c10e21fa1ed381defd924127283d97f7eac3b4404996e72b91d3e9503828ab9b200a1841bbe23e0c744ce3fe8c164a8203b63a67a4c0f1063193ce3295ef56ec9f0e44c401e8c1e743bd6febd7d422d368341a8e6dda8b8fe2dcbcb629868eb33e0cd9b602fd5ec813a0fa2f2ec0d39c80cdd076a8c471732baa327b50ca8b5abe04be123ba9d064745a2d29cbac50ceff7922615ab132621b4f8e2cf3bc4d9dde02bb7ebed841ac08b15481ee154c57414c2df2ad126890465a8bcbad854ad4e80a45f2defc388aab02b1d1c9a43ee0bc1f18210f51791d58e6366828f5b6cebd0eee81914a44f5296d233534ce2d88511623d8fa3e4067b0631403b4255b7ae70e840154696e864fc011ed0c0bb291bfa3be807d25bc98081a7269adb7823f0e004f89a5436196db49a01dea64530bf97464b2d2258cbf9497b5a7259d513a4b03a0c59f55f27538564c38cedb49351bcc69d43bff2db140456d27ef00c6f6e504bbe71037afa4f886959dc1d58d1f70bbd3fd4c89c6e35e188233e1a7e98cef009e266c1b66f021739bacdf93c4cf733b958e08a8eb3de51012ecfa3755e85e09107456c5ae7341a80a40d5e686de41cd60654d9946047cf54e1498fc60904109ea0c193f02bf2564ee1ffe3b0810e30bc47da5a2b07f5cf07645d324428a7703bb7c57b0431dc1aa5296a6d8b2c8f07a7607bc1c8cdee713ef72b02df494a9072d9910735f07e87edb64300efc8ce9692476d528f627800609da85503235c90a9eebd988bb0b20c0e162f4dd1002c9f51dd2ff3e0708b0a2b281d6367617ea0a9f9e1e32438bb6baf79503706c508c20c316ad8eed1f3db22907dd52f2118b0d38d515078807a6663bd4e569107f6be0be43f00d6998c8f0aedb305afbb4f5066fb55a32abd963f4886aecdbe96e5b23fa637d2c9119dea336d9456d1106750868c45af0f3c6cf65d3f4b3674dd369764d757c3ba63bb236f50db233f0dde60c8b5222206af3c6a33fb4f9b68c83ba7ba451ac172c168fd11114fad14e75a401f3c97a2f01b653d7161f5f791cc2b7ed11e475af6134093dafefd50300a8dc0e965c4bc18e29a437e1ec5c884b06676fcb0c65aac2d6bb0ff1fb6ea7991a4b00ddcd26f2a20214f3c566d5c797db4a443e413e995a98956f3eb51aacceb1b102f958dd1c9696d85639416851863252f75bc6961b5af298e2ff64e507a57412087fd7c3862388727fd7ffc8fcaec8ae7083681994f126158bf506822431b5a90ba77eb0b45ae04cab65fc0b1c6c5bb23d4542406c1e132653bd4151a7bf928c0026a68fae7a22f8afaf86d878dcd8569277ca02e76af58dabcc81a0f09590e30c79c8596ab8c6ed2ceacb85ec0311f0ef761dcb49c4ad7a1c97e816baeb45ef097b7220a84b2626183ab8bad394b3cf5fd7f5ee53c2ee7754b4b6d190f16cea03101d3f3bf2f19213bedb7448c40e346f1d57cead28e565d9f8517f561b82bd0684f24024ddc3e8bf41238a3b389e04727e8898776e821698ce4ed76aa8140b03807cbb3b49b6815ec342a5ad5ceb3c9385566271b9d7799d18e0f0724b476f0e9257e5d68f6658bd6004a81c444fc620e766a2697daf1fd687069f2b8cd50700dc90a216481e7013778fc89f219cecc52526988bdd5532891e6193422f4c840e5417bb42ceb88c345881f0ebb41820c2b3cd5e85ff9639f87a8790b9d487eb0b2489db45323a7a29f220ef8f42f3ab889905bbf7a62399a13bbc8f367ed27006b6b7c958b74eb7cfa15e0829a905cd80cb3a357fe3fe30a71d3558656c13a4090e199e4030e8c05012f468e6ea61a40ab5c987372643edec7486e0b115858602cf555252d0a31e82fa7a770b2a55a1b3c76c51f3832447771c34fcd43c07170bbcad0667ecf3c1fe93d54ddda1274f5a633d7d8c3b6db1f8244685ef872a3d0323c96d64d80c43ea48f7f6591f99c5a9b1ff682227a25a41bb2f633023eb870f6d6684c073c179662fbce59e07bc4e6ac9c174036a9a5352e61dbb2d6509020eb30589b3c704ab758b2d5985863bc397d7cb1cc9e629bd15e10b997a88560c06ef415b43c316c743cb146c2caba9b049a8750cb3931f40f552a529cf8bc12baeb6bbe255ace035160edf9f602d67a7261a108cee238a73e9dca2f1932aac4402d2fb2fd5830f11c28c13ea20a97b746f0db27f0e62711c688a7764285c779801dcdb2d03003db2a7863910390866ee04a6718312323d27245e5b24644d261a053f9f0664a6fc23269f657caecd4388e82e710c04c0d9355cd779d4799812270a9bc861e80803067c099a29b7823cc45643606e45b489c916157cdf4644c3ec07677bcadf8d2dfc2d9ba8e84e71031f434625a93f3eeaa35342c93d8ee89da804de18416564e608aa5202ab6fa514b46e8c927fdcc3b47b04d5d28b2c4231e0fd39cbbf6ab43f081ef7ece41aecee7adfca940db9316b3f5e1887b419075b460b false -check_ring_signature 1856c129fb7aaf86ab7c892c77ecce44eafe2a65b3f4e37ef9380335f959ae1c eb0b3385d2306ffccd3ba570dc95441f8fac3f7e4e31fdf42d3ec43958b8a450 3 58f5e18baf4e13726b4f774c5aea3a2e9a06670f43c1c9a0171cddc88e10c63a a5b0d8838310c01c6caf0247e2c4b3c24b684b2a3f2c30192af3fa891cab7b9b b237090d98078eba9eec13177dc4cc7bcfaf15ea0bee668e25e0ecc11cd444fd 1d633fa9a0d2e90af816ef7658cfae9852c3eab07f700940b8641de389a5800e17a6ec08175e51df5eadfea76166bc1e2afa28182b2b2d2c55e9c7a06bb48807aeb57852750ee5a5f9cb7b0458738efd42a9844e3004ee54d6a25705f1496908e45e02d2c4b0fe73b5fc450bc6e6dc180ab586998b55adef3b8d3b3ba60fa4064424397b8a8e8ad8de1a46a77b2c255c978fd850684bad05fbffb6fec7d4760d10fa69d26b5bd25376e61c98b5f0230b8987c4000307d510d5b836b951ff2b0c true -check_ring_signature 9aa62f7f8d790f40bbbb33f0ecab2e9287527bcbdec19357a39aeff65bd03e5d de5826b427c3b4fbd78e2e2791d80543959ed03eef89eaf59943b30562a08b5c 4 8983b2d199a4d3c5cd861b64f71802127b0142fe0ab3c31f6152d61d3b57b27a 54ef4b6c945a6f9f8859b4ac711263ae3368ad245b255eebbac4d3b68c04f62a be7e9ec76ee2289158cba8e2450d1ab2e27185e426af12587a6a256357b42d9d 641869758584959720f02aa7bcf8d8677cb24593b0efa615f1bcb4b900bddb9a 40b3b5cfa8662b0e841464fc7c324add1575aec20367ced95ccb88fc5b72a30b469e900e8241fc8a3d2c9d23eadc21ade7fdcde070866005c356285f4f791c033e2bd408610448958ef090f2cac7a42c593059b59306925080779410e976a5b4434b1f7ba075e6fce1a6242c2c1a16e241bee12f9c532d0b2880b947b1b767069ed613892889398b68ca4d21c08f0a73a97a5537960cd5fcb96bd8b59c05800d8a24cc6e04340ee2a21d8ff52cb42845a976ebd396976f61caf5c8c07c4aaa828b9653bd44982370fc86ac2adc97f06b0a3f41275c7fd6df3e5af6a725a2a20e9ea24c6bc52931b6c513c6a7d5bf5136c3794d64ff661c4f88c3127def552303 false -check_ring_signature 8fa92088028e148abbdb2682fb3b70938c04ff75bbd6c9b14bf24fc61e997e3d e5c1116083bba417f471a5f611197dc9367591fd79edb49c320544acac08bb11 31 ec2693d884fadba69ff8a5e0cf9b11c9eeb459946d51ac0c0843f15609b76720 153158589f48811b03e767572c4e05fc639a85139f2f4599c74b8f3151c926e5 b60998abddd4135722c931f08418f1846d361c185907c5ebe07f53ea83bb86a5 582ba2eea9ef933828faf99d60602f22806cd23b54d5e8308dbca893b0fcc292 e855a61ff5175df7f40cdeaec0620658ec673c7b4c10b4a62cf05b7fe37e270c c60bf89586317b563ff70903cc3ae59fa4cf36d2b47599f04d453050cb44a587 98563be0a418afb0aa5936d233bd835daf004a70cbea052dcf0fb47ad1ef1acc a9aee55703ed5f45618baccc4fe6fcc185d8c872cf8b033771a7409e9cfa3677 6415a75de488a1bb69722e0cced481d440849f7f07496592f2db66c6d5991a93 0beab77ceb88c661040dfcac456cc8d34f3f96a99a5a6a02206c9751da6a89e9 e0c47c08b65fd25b50c83f316ad40148c68c539d982e67f801f82ff87632a915 befd29898672b8efcf7180eb133b1bddd08c92ca94f17462bdcba49ba25536a8 201b4776e9160f352c8376e095e13bab910b24f0ddb6e6dfc45c9673e0d59270 e5dccf25f76bd3e0806b41211a4a73f49b51aa539eeacad5c9917b861bf52aad d0726fc4df358a1ccd2477ded55f28a1c8bbeb6b5fe82d920b083dbd541095ee 4c23acefd931356701444c7d6bdef37f5217f6fc097f45811b712c9257c905e5 61ff6dd7cb4a25b180ff0625c6dffb1086885d0aeb5cf6e73c5603004d46db05 8c8aa2413112a7f0ca42006c676f102d380424117c6525206429a7fd16e69930 382b68a7867486a59f9677af8141a58363e2304d5c88b2fcefca76500ca6318e 7b410b916ace4dd42778f61d4386a764396daff37a292d5ce44f1d828c90cc0b 7a95e28a482b2b4e77eab221de0b21692ec149b5868bb5783ee059a47929e7c0 3341de3ef250e386e4b1fe5271fc219c96496f3b0fe1ec90953ce551e77854be 73d193286fc1238c6c079319945df31cbc20b4caab0b65e1ea6812948a2b1083 a61db7787cb637ecbdf5b7238c352a355f78aeedbf168484feee22d098348730 b224ca5a3407ce6f2ab5ac282c589ebb81f33dbb53dd1a3ad673f11b77b2359f 94bd5c6b3fcbd15ef96dc3a792c820c3b7847a6704e2e5e1e5a8a7da1675bde5 25718cf84045e1ca40ab2e286e93e23c9b9417e9dd7eae042e334533621a4bb7 b8dd83e79c492cb08a80ebc52f09de77e752defbe63fba8db6a7468c8765e167 f6833fb80af2fbcd3e6e614d0c2d24f8d20d6f3c8f1c68badd26d037c21e7974 a1dba227e90506ae1254775478227ce59f5318009b7a351d8337c9c22cba76d3 6a38f2c3dea4211a871fb7034bc58721094b24ae2225e05dc8c065ef8f831560 dbdd2a3ceb188d19187acf55ece0e3d8c067c2943adc0ec65e7915064b23d10e2e98b0b064d481ddc99519944847756a6dd260487e81f1d22fcc6aa7833e470860717f1cb108742f533c94a152ddac748e6b93b01c120d5b40bd40824af9880aec7f2d7cd4d24d0521ccdfd2adfd8358682c22cd8d16c87a40a5c80ad2915b091424aa99c0fc710d81398b1e842b17bfe288ebdfbb80ea6bdd11d81ad36a9303a538fc00ce7405e85d59b56b0c54ab5aad8655ab2d3de064f079b98f419c60009021aef495d52cc600ec7b62ff36c7ec7c9d5ea77b0d9771375f9a0a1a9de80ead8577a41748e141d2d70afc1b18ec8fd3472c8fc9c42a9909990a625f2bd401f3b0cc29a95a16055816a41eba42956f33a6feecb97d3ff5ba98964894b2990e9b606c1d2551570ffadff2755d3c8e1788512f3a8092f478ce9c7b7180e026098c4be090820a580c554e4bfc2a15a03d49bfdbd8002738f1504bd6bff6c0090cbc395288e96af28c49517c82718b9912d583fc988cda2b2850b0144a344c41072ed3b7ef5280ed2044400c4feb7c0f2a59774528c1d404c3cb029d3fde3a1506864a28ed05cce66a8674473d2ee9b514738b61816d918e473d4a3975bfe77705b939ea706996c0b7318e6fd80b5162660442d25b390123c84a22b7a0eb15a303cfb2808fab818d167e3b822e95dbb7bb579fc3dce7e9d1dbafda6c13e689100ea7f0467df472b8dee9376fb9d29ac5dc6d14e51f285cf99e95a5c49e7b4f310b41c506ebe1c07222a9f1b5c149a82d6ee1c85679e9cb4b0c563841aeab76280e5d27b54ed5f10aaf54afc4d703b832511ed9ef468242a5016b8e10b5bbda0605c938c5c7d97331f5bd021cca46aa3ebcd3f2ef86ceb3849bafd84342cdca1307c57b27851383d7f1122b82db358af15d341d6db68e47cf4836da4df3bfd0b800659441812594d4728ed9f6e1d5397d4bdfe1722faa8c878aa3e10caee28c580b13d4aff1c8ffd5f37e112b973dc0b3f51312285bc79e3b00ffc43477c4c9b60b3fe0c66f5a370395aada377d5b930ebe32e538e14a09422961c46b3a83e119027ccfd407381ed78d1ae16e8055dd6b9f14a22bacae946d81e9cad8e315109d0421739c85bc3c4105851145429d49caa99b927205d9423afb14256f37e925f7030954d56d4a0f6b5e19edc03cf7426da62a8cd3fa5948b0cde8d921df9677280fa9afdd3992491ec55c4b15ffe1b83a2ee1679fb2625110167aebeb99d251820ee679480232b1589f290a8ed95714963b85cae18698898f90c8c74b8b8d38f60c7bc1547ee2b08677fb8bc06795b69684890df8caabb02992d87daa6abcb36d052e5283829533ec19adda930cec2363a24f5b5c8f086f7e0cbf80d8d3066b490626a0ea01e1bced10eb0081e077d2619a78e1f842976e41e7cc2cb58e74c92f0341430d07c7e071edf6990d642a91b7e09c772abd2d737f2a40cf1a0d7a229a0bab5825b05bbcf5c2e13d28352d84d8d0ce574dfe52ae69395c32a4ec7de8140852edb3abb05bfa80f7d2c63bc4ce0f1c513320dd959037d86f5e64b0102d57023bddaa8db2f97f054676335c0aa1465264639e9c7e5c9ad61ed1a6cfbd8e9c0fb551c02134203b5b63ee63d1644d1b23dd1da19e9588f0e6aaa68e70baad6b0bc465c43ab3c5fe06f450055d75f8452bda4b423cd161d8963c00ea58f82bbb0974e5f653d53e9e910e95a2c71f3f6e8e9399f5029c2a41cdb9329cf550e2670c2ba45e94172e9c327769d2e1658d87269ef9fe845461cac914b4c2eb083acc0bcb957bf119fd5a49fca10f77a7fec3876d69fb8654109f25d5c096eb20ee8f0fd63f06e540fac2e51c90304892fc796ca7fc95c8286645fe9b8ee16d4c53f000e14e3754483117c82fea4bce4159c17fac80ddba721de884bcd6eee7e5427201ad0a7d4f0902432cf3022b34a22b697c851540eb33dc7814d14d8ba4b9fd3208674b20c9a799b1461dca994f3e0b1c43101c10d894572fc0f1a3863a663cbb066a0601afb30062ebea38fae3fb111759af8ece9bdc7bf5873023c89bca489d918d856f1aba4e522b64c016718f7a771f56bc5e6912a7cfa6e1eb1fdd88df7f0dec34191aabc1b0c3407a022ba4d0b6e12e4af9e4c0b4c663ad5a705c7c45ab022b196cae78f57fbc4697558c46c8e2747585470ce9088ed0b6c3bb1653f45c062f2d447eee26c346c60125f93875df71f6c89bb4cf0b45928ec0f44737bb39012d117731ad88778277354567e2dac4e046f762d73fc6b90af4834e57965c8c09a8b6a8f1eef1491c8416e75cfd3a86aaa31e911596ace2773ad074c0fe7efc0e84ae0d96e28c36b46b79f7cef95b55f5e5259a5998962165e7b8c5ca7adeb80e6822c1bc4dc9ee657c9706ea36083ad24b57505a4ed5fb75e377a577147f270d6814ef809c44a93f654f77dd67a0a6b96dab3c9791830610bc9de19f8a5bb90f15c886c30ebad9bd47062b01f3a5f40003ded644e8ec2ead6a1bcd0c2e5c5d0087cfa3a88862af49a22bd2ae533c5159be2f2318f5a781a2f0c85099931e890a6a3c24fbca2c81714bbb4b4cd3dbdcff4d5a67d1d5615572c3601086fd36600a3619ebd99ad6ac3d3ee567c796a822ec4fc577363c9ff2f6cbd7f2a0e092a90959404e67ca2ec57052b9773233880f3b14ae28e8295337514a71e614ee8d230f7fbf7cd7a9500fdbcbb1f835964bdebbafa1616c8208e1f999c9fe8f06c0290e919555cb1289290a2b871c0982e11a5fa1d09a120d5654380757b40e524b8006 false -check_ring_signature 304466589f3a147a0f19423c78d70e46d3cb41891ed470ae6600408561c49f32 02d88c71300367404a7830c57d3f75da70fe741744b9b2b661c038e9b6f72cd0 57 9514cef6ea80dba88af5a0a3603c0bc1f9e9be656e2bf42e2ae28758f5ef9c97 5561a2e2241b83de4ad4bdfb9d73df254b2f6c498a1b468ae64d39fe1e5748be e0be8255113cd3d504f7026f9795c6f2a7ac5202d564a0f96c3fe01ea9f88c60 b057576916ac9a9fa61a2e1633a990d9b1d1076ca17995ea16ce1479d2d1032d c378eb8b42d675a6472c7f4131f8ce3faa88fda464ea79d82fe63a5f90679ec3 381228eacbaee2efa89e73172c3449c695fe8e6b87b42b28d38de5009b748194 ee462bb52976f4f11015e669ac8f84c32352b5c02adce8e0b725a4cc3d5788f6 1b18503045bf82b5ecb551c474a681208140c4b86f92e61e728bdbccab6d2184 40f8788e182c8f3ad4d9a6df02685032191d63e09b6a3e75a381267582a8e763 5ee1704dc7d4ceb4c166ee17c8f5b041bb0440fddb8150b69bcdc498e3c84b92 e3b4753056c153850abb3a1ec730c35886d44df8bdde1475000c29016d9efa13 7ac56c546efb2a236f5420c4ec8011998fa93ed7ba72c505783f57b179349b42 40d75520c3eded85ddcb43b36b1c88e80ba6df3b5daf3098291bb90fb912e815 37f9eb984134959c3dce54adfec4793d28f5eb68247b88551f8c0ba38b087506 b4540e87f99e3e61c4c66b868ce079a431664f614c0b6776c626ed47192b541a a07ab886accb6e67a75b979b29be7157bd856c2f1288f8862a57a3cc86ffe8bc 21341f537a8407a4545f015e8341064a6095a404bc1ea88bdede2681f2bf433b a377439d73f35b53298fea7536db78a943d2932015545c4f6055d7fc1d2df07e 43af19a633aea11835067fc8b9e6a13fa0ec96d2f6f38345ec2bb7e45d15d894 a7ae35fd3ebb2efa6f37c092aae3213dab39ab44c5be4c3d2b54d7cdff078998 a4debf8a9560b98755edc3bcbb1debd5d5718850e4a268487f0534b3b9cce36e 87560ee04beb47e2865d1eb6c31d1c3154eccf0c3573c94fe29898af91bf50a3 dacdd8ebbe66f8f12ff3884b308735429682e54448cc946ff2401954a6d97f19 b8909fe575f412848629388ec18a18f23c91a738261250736ac3de2c438f0b45 460543011f75ec3234b80ff0c08b4aecafd5044b02acd09e4f07e146bbcd2455 b19ead66d7344e7794179ea18a9cb72defc484967c562dc3c1abf79cb3f1d4c7 5dbd0c444310c9ad7f54b0ce9e0f22f4d4a3e7ccaf52a4489ae13de42a28d1de 095bfe84045d378bb64bc5fda5b4e424ed8bc992e1ea8f2721820a1fd1430dcd 147b2b820502e6fcd3fa9126e5419291801db02a0c09e2f3e7894fe3d0d2b880 d8035759ec3dd1cad90ce4d8a1cdebdfe9aeef48435221dc9d462b9a24cbe98a e523aa0b066bd930f3037aee7edd64a4a61b85599d423f5303d95acc87c02186 a9f58aefa7d9a7c8cce4c8fd01d9a3f79c3d2f69329e883ff6ebae295dda0085 c62b1fb01d6742bd19427ce3f27bcae49f32ede7b7610bae52ea942c427c9b18 8d8b43d82a10f139d8d2ccb41af6cacb8fd6248399149053d70ffb89899fd9bd e594b6a10fa3a836eeae2a07e91e44f43841fea424027d8d8dedb6c64e4afa15 1b3d8240c6923100483d0fda877a70b906d83daa6ccc0506acc91fb00c27fc2a 8164155e515a0dcff9c94defc6dd9215e6c0f028b6df74074d5659dc86333542 23403200656742dbc34be27a8c39a0e88fdfbe2cf6b3ab198d26b7221c04dad1 07fc55234ebdd230db04b34ef9f62880b955504af5cf6e1cf47278a167c60c49 2aa9da0e489030abead8f36d56c88c860213899e29e395565dcec37623d704d4 ebd06e1a41c9928b6ddf333331ffbb3906eb7ca9c67334b9501806ddb38e6a26 01d97750556d0ecbcb68afe5679e249df429b7cb8d659954baabc0cb3f763bf9 08fc3fb271d28428d304b4df0978a55924a0a0f4407ad3e16d869b30610de4b6 d18a40fa7d314674b3feb1ce5b986d06ec86d9f2e9159f4fd7ee19dbcc6c6051 539630e696f4b68fdfb323942c5a17d30cdc96e24462c86cb2dc97964d7e4a58 4a190a875280e8cde8ec67c2235430e179bad8e1ee8ed8ea2654f60a5b44b742 bc33d5aa04e727958f681757421800b9998711149e6269ae8d834923d06043f3 ff277cea08a1eb7bf1dd4e77ee3bf4eddddb2eeed0ffa035418683b26300f35b 9fc2f984a716d133691970251ff4df3c028b0be9a87e10c9663db6bfed8eb7ef b8817c7dc6f135c381dbd01a9902c244a719a96d1ecf878e593c90c2114581b9 ba9229463018229a3e7f42a1e68031bf1e6793b59cc4e565499b9cb9a438d8f2 aa0a5957aa69102e9dd26e7214ffd7c19cc0c6e3925818afcb2317d7dcfbb216 f0e73c5550d7194c0fc204fab223f622baecd538fa43f3384cc9eb026ba808e3 d6aa2217e8549a417742eb1fd25005171a3f6da7589a794f1adc9e645902e96d db8441cdaffd2122ccaf9a7220a0bdeb81db5c9289303f77461782d81385dd54 8e8fd0d97b5d59cc2b38f4845f37daeaf3462298eff5eec0bc77debe809a0371 8b007be8f1c084908435c98c280d79531f13e9072e263228c997fc742b51e414 20dc16f45610fb27632988c2a812625b0913121acee12390280b45d698b7e10228f7e4ed4398607dcbde9138bdb66a023a804ec9f07c77af9b10c0dbab27940cc05fcbed2b74084db6be10323f3f0caad8f0763f1a8e5e43eba802a832c10f0bba4a58ab6f98c4dcd64bf138fb4d78991c7a14426809121ed4e28122bd19cf0ae190f3f1e9efbd1d13090f9a0688b1b5b83d4d589af5b61bde1223518ed37c002dd5f63bbdcab45dc58b58a0d79a88bc1a1e6ed91816c8be9661f47de5cbad05e5698579dbf501c1413dad494de6327c5c0ac392e208bd5cd711377a44043109666b18fc0a6554de16e343332377341daa703d65bdacd5988863460e03c3e203a9998b3350fdcf8cd28692873b24ba12bd72745ca9e3a615ac0ce65a80db0f02d642d2bacd09b4f3251304eefc2694a0ae6e25fc12578e495a9f3f1cf34f8109d49bb06bc962c35261e73ef2506f00e0bbecd8b4704769f339392a03fc90e90d23060af37b9e0102297434fbb875f6d31c20a526c679eba9e70d92750a514b078c3642ed1e2971b199c7de33e8c0a27c5a218ea52fb4d26666a439bdeb6a140e243732b6c55dc08baaf44f1d65ba6b16a3e6e7a7e03a8859b4fd12ada768bc05b9715175c5c73475168e687d0462c78e9e20b49fcfacae1960c949a8c2085b0398b385c6a2ac12da601f2c658143bd28648b3f76a380da382e79883a932f2c06adb157980b704815664b04540b7e7add58a7e365423b7308ec73f9bfa215ce0749d0b84bcaa1ba10e11fa567e263ceb3c32686247ea5752cf331ccbd53f6c90bd3a6aeeaeb4333b1954cad22a43b8c5a870bd3cc3fe4a3d504330d86f4a5c00ebb6114898cfce9055b876748896667558d26c34d2d78d4a9aa00c94b2bb1080b233eb75e5688b70a266d523fdf5950b316c55b8efbcf61fb44a5b4b3d49be50f01a97b55c34ce9e19e16137f114d631fda88de4059b86bcea0c63b6c0614ab003472c0a8d86b3a834ec4bbf8bbfc7bb713c04965e2fce693cd6c639217c4d9080f02543f0fe9ada142b64245d32fb98415ac34f3938048388bb0f52fb70699044b4397462b387e8a8b886790de7a64357376810981d1780c77df5829c12c97026f9b60925614b41a6456ba970158c1511651a4ba7590be298f46f950dace180b35b057432f52e2890a324248debedc7d124f8d60499b5f80870d15fe0b024905b3ecdfd7f6406047be4720710350b3e528fa2ae0b13941a6a0bd96554d30870a55b62d3b4b659ae9a996e1d4f0feb4bb74a91a43feed042246da67e23550f403e3df165ebb8e563665b19941eca17f46dc62613119b8598714c0388ac30e030f544150e0d4c0e0b04d03e52f60707cc97266849baa0bd53b774b0076f412020662ff354b229b92d71aec5cb9b0c085f5fe97383c27e6e492394af245d2dae20ad3fd77eaf6ac26549412711aff34ba61d3ad67030529b795ba3a996d634eed072f79984509290091fcd68a52bfa47c77f3d7fe252e1bc328e748c587af4ee30799b3fb88e7830df7bfffa640f0e87043929b3fb52a202b21ad50ee93f25b5a05abbe8d27c06a93596e8d3a04de69819a613c0dcfee049da0821b0a42ab1b9b014878703b53d12cc76883cd674e959ada1a81a7276f07cb2c467cb26c841a85076e99497b736db37185a588bb9d0184de030b2da4733eafa0864ca29cb4a44b0ac551c148aabd46cad5842f571bc0ce8f4b1efdd9c984c064fb0a93795b5dae078c803d20cf243b900e1aa308de4fad00471b7536caa364c6881aa0d944b325d6a94fd1ff83ab1b1a03cc1c2bee2100979dae76334ebe2f588c1a492f14dc180f4354df8f4d5849ca4478f675fb0e27c2b00d14fac8511d2e653cc2b0a6aedf063ede713c6e85eaa3bf1b55ac9ecacdb16fa354cead490858bc4244387acb2006a3d6010804e92de873c45ed7609a63fdd3ef3a39391c91a5b9c5ec2187cccc0c2e65868c240830a8cb2ea3a750319c563899f7d22b81cd73c829ea9e9c8f580ba89c988d23c907fa4b02b86e2f40ec80a07f4c429cc57373ee25b5802c1577091e70d2ceffcdea0b4d389ab3813e153fb4cf1d0c1d6765bb170f0e6ce21d2f006d5baa3b5754d8a20569f80c27da1ffd7adffe9c14ee6458f3dee0a9c45de5086d6a3cb893c139c59bcb70494629c92aff5556b2ae9b95d0c4944476a3d7c9003717ddec040beda320d938e5fd6b14c2102f5d249225d4dcfa0af7e6bf39310d95081b89687aea975ea248f303fadef6ea8857edb54154c6fb8b9a8fc56fa30f1a3769509d290ea3b80dc79bd8b7a17552d8b405ad4882fcbc932010208eed072ba2042120ea7f35ead81bdeaa6b3197639f62f22090795e0693ef6f90196c0cb133a364e501d48112be1c825e460edebaac50323ec0c18a5a810a3d8054ab017c61c321ba36c2e23007e8aa6beefebd7ec83299b775c4c6d9bbe6c5348160086d119a7b607149f4be6851f44cad06309370d195f2db81adc0f21542a68dd3000f7c8d9a381bc52e1b4c48444ec5c67b409e5baf2fb3dda7cb746bf26d042c04c64c03bac96b20e187714d3e90667fc7f9e64273babc55d6a9030309551fb20f29951702d7e75ab243c89b52b4a574f524901bcce49b63e9c3f9cc6be0d32f0f0e9f457bfb2007d413db25cf0684974cbf8107c8171f20d6ae8f6a98afd6fc065361086e977b37511cae5fd0dcece8e7bacfe4d8c2dbe8d575b25dd0ca519c0cfe340995ed3bb48d8c3bf3bd29a89a1c295dfd1b0fc91f56b83f05f0acbec8069e8c81e5a38ce543d613d0b3f9789789ca7e8aece8e6c75a9afb3b4fc5209601231f1be97f28dc1024cde82d913f7be98e57f51fdb21f7c73347c7a5eca4df021fd01ec93296c3c93a2b721b0657a8a353a41696e627907b0aaf64b78db39f02071ed5f0a8287a079cb3bad514d22a132e99d0e8703c815bd0d164ac63d78308f017d3d721b415db6512bfdb9cf339f904e074fa7689502283c1d23ea5c0b6033d280aa85ed1a85892a0d9c13a638e33856ce6f75ef158d727554ae79e85ba04b8edb8acb39f963439a61cc2e2628ba08e52e2069cd84b5f0e9df07f9bd8e707653843b959bd95891fe5910c9474b1cf2feae45e5403281d219443409c076e0dddc702465c2445fb3ffd4e7559a8f96ccffa4b160b94e1eafe715a88c755c3029b491455805758ad87f727ac2b0cc9fd2504faf2b6c4fed278a9be55b8145f014225e09b7afc58fb274e6d7f7b309e471e90847a65535f15c6a06df2caaff007bb23bec9f4611d9f6c40d2e52bea69a65c5cb1935685639ad299f0e4e264bd0dc1dab13303d12bfbdccfad639b9d568a5187751590eb077c8aaf645383c4fb004a359b747e15d0063fffbe919bfc1859575f3e223c13dacb495b2eb5eed6ff0e54a432c623cb0cce8083fce2ec93075c2a72153357ce4c7324d0d458612a8a08ab7cbd185cd613a54c3813b03c31426ab37dabe11ed198af1c97b1adabdb4b07f0404bde517a8f29e9547c25205cbf49b1ac0312d3a784bcdbad372345c7020acccaafea1ee3a47a8dbf3534288dff96984f81d58388838072c821e7139f870eaefe114a1babf966a6664fe9912cfe8c2836c941cf26615fa2bbd0155a94a30a2f707672744e6240d538f481c82839da67e01e6e0c8ff4b8ee2431bab2639200fdf9dc1bbe27811ac5f81abde60c8e633546aa6e1191b1177144c23e823bf00bf768193311ddb6cc88a355ab983ffa63308de0d9f25ae398558160913b2e650fe0e6c0f76ef6dc05e1ead9b0ecfa0ca07da0c92b0b26a2102d42b97f078d3008fa9a329b361d99dd1ae4358e0d438c000f5e8a1ba8755d95829baada075d020c2c3debb7907cfe1a3b4c4ff0a3d4af754b2e417acea765a0efcc6ff3e43a020d6f2463e190b950867bdf0a951675cec989a3f3249ed58876770fce780120770d416ef5144fd5979208cfeda557b92f22f9cac4b4e25e981fa955b509ffa6c6058a927bb515331906817f88375bf4521ca9a47e51aff26110469efd524b78160d67cd71cb664cb656a9f4f25e136ef35a4adb8fc5fd588fa992d43b6f37cd5706ba3cfdd3d862466212f0c658409a86c882ec07bf7de9cd81cfb6ff8515b61d080da2edb1cec1eeea31203c050337deda7b357370ab8e722adb91243b545d0203783fa66a18140022fc44911b202a2fb625ed6e9eaace984da689f7ecd5dbf30b4b8b4fb647336e619730d402b1d1213a67a00fadc873710250b9cff9250a130448186c04312a5055a2c3aa1c64c7ccb60d80d7e90127ed44435902dce30a5d0b7a7a903daa6079c7efd8c60f8b8b0ab803f7ba961116f85eb95b783d549ac9046339625f009d8229f4152dd8022c1948586cbec7ebd42eaf96a6406040d62907c1690c44d252e2c0071ff96d04b2695bf4a74a1206e3c5726912b5ecf853e40d1e52008e5f58a51eee402aaba201af5130a143cfae188001e3ff3387554d0f032dfc979143f2b320407a7906af0157eb433c84284dfaa6140f095b09890d7b0d2e507211e5a516c8f4f8ef4a2bc47b0ac29afe3cc9214ddd31b0c3ea78f4260c4583fbb5aa377a90e29d1cd3d87f0d62f39439f5d3a86a98ffbccde452e76d0ba439bd923736e933b4c332d484a245a17b602061057ff040867da17c4e19cb9597d0f4dd9ab9ec6d7a41f0a2c9827fece1e6adad77e3b4e02f4c13edf64a590605fd288da51143ceca6a5fb14c6be0ebda58b68bef8d2895f52cfaea54abf7003f8f27d0d405d121c9e4b7487023520c3911c82b5e3b322ec86152d7d4a0d501eee1d9fdb98c8aa7c3ad163d93ca841bd7c1ed6709fec3db9baa9dc9ae7a3e02b9b754d0ef20c4c1e682f2c03f34b2cd6dcb09c7cf3ea865bdb834c75dcfa50832e304c923e9b81f5709e0181b21292faa7587e216ef2f50f56d214e459d1f05494bd6520df84d00606493ab0e1a9d124e31f62a185bd4cda99b19a43022be0c0b4a900bb19adbc304ef9b45cd114cb10bea001f4d916502558689627867150c14f44c9d1062a4d76c37201e78f9f21152a38eb9fa38e45548b9fade5036880f12e2402e93621c1548bf81518b8e17302b0ca4edf7f25dcb47d4823d0b5afb0e false -check_ring_signature 935bb65750f98bc8599fa9b85553b252648af5b320f6ff02dfdcf4379f527d84 1cb0d0770fa859327eef709b22703b125940b0bb406708d37d70bf9b74435ce8 1 0a4db2f3df96920be87b4b6269e32c4d2916d99cb275062ed4da62398c1569e0 367f3a39b73cc6eace72ea5ef1f0c20942dd98da625b83c62d238aa1d5def509e55ef0b7b7004767528a7b6d5e7b85ee8ecd4569fdbba78fdf3493dddeda080a true -check_ring_signature b16be7b6bc89b9b1fa2229ddab3100129ce3cea28b3a6ea0a0eb61e718b83ccf 2005c8fb10bed64ec7fcc2acdafbd48e96ff073ce84791cb937994455aff1a1f 2 e6ba1fd0a1089bc8ec11e4182be3eecd7aa590d4b02436d717e69173a96afaf6 51fca5f4b94b293e418e807ada2d70bf7df87ee6c935eb0fc2c12cf8eca3464a 9bc65c66aecc17df955cb735caca4c734dc5a89c8c766377c1f5345c656b250d54083fc9b0b8358622b0d14412dcd0ec9b1d929a1e022a804de8a3a866a4a2a6b7569ff0c1c8a938f5f8cf2daa7a042c194d0d6af029ab424b88ce5faa7d21080e3e1c4677ae1027aa614616edb433df4232bdab067263326fd3a674b7d48408 false -check_ring_signature 7a962f221a1e70fde9a62dd05617bfa3b8e40c760a19ed878baf62e788b370bf 145010554077e14b6d2c45e377a8b0fd3be623935a5a7617d8a406dbb9df5a1c 4 856648e5f06812a16094d7f765ce6a5c548c40a1989d93d32d58519b8cafbc13 eddfcee250a1f1d90f61d134a5f25f0329d4146ec13cfafb6b790ec236efb2d5 fc3a4164aeb60d983495ce926dee2136b0af9f5114a373c04ebb0cad12e6fbfb 7ee33da7bfc7314e89338b7680a43b20627d89931cd6bb4f4061d456c4955f92 1848f1e095ca5c5e72cb4ea8774e21ee650fa47eca706978e14b8df7dc5e8807930d781d11780b61edb3aa8ab576b816108596bb07ce8b1b040a46cbc6a68d0e130e560d38b2fea21eca04c244758506d832ca6c7b616ccc67a7bbb7a6e6110e69a8b9641960a7df3efa653a7ba75a91ec99dd4e56665363d298d01ee1063805d58837334508e435d24870420450b0a07377e3cca85288f68ccbcd7ac4e4830f8656beae4245ba875610b743d10831231e2ca9292d1b9929889a3f3ade79d60ed4d46b3224d4293a28231f294650528f11bca024d2aed531ea8691cea964d203d87c6ce32885ee2a8b9c96e8aff7f44bdd481d53a21fc3240d6c660685674a0a true -check_ring_signature c28aa4088bf60d5aa7d5f45d85bfe57d0d0949042602bab63cc9848d23a74371 88535d0fcf45db8f200681f1c390a02aa5a4cf849642eb36612fe1336ebc069b 50 57efe91c1b0bc1605eb5e82016a0246a5968a418002f63779d6122cf507d4ace b2669c272b35e4b88eb9d9ae636f62fba676f838a98e5ad80786a3441402280f 8b6ce03b081e010b0d19063940acf68fc3964bf7a092149e91da1ec0ebaf26d9 4e224c29fb18a3114e54bc22e4a9d6377658afc2afc7628f056981242ae15df9 0c6f0beddfa0b35464d6506fe749b61faeee0ffd60e62a3187cbcfb0362a5ec4 3c15ed5e82d5851ce1d8215d938cf564e6cf944b96cca22803372737379b71cc d2e9ee4e7ddce90ba1e4d94bdb5a60be0950af44a87e16b2c6070e53b7296571 ed89464722db6aa1a0f9f668c147c3edb5c99302b78af1a0622214f9781594af 4b9930a13ca41c28b11c42c7c755cb2abab76cb5f64719600819e73478748603 e41c50f979493bea67a9bea654b3c040972d776026675487fea3fd80c7b8085d 48d61260bccba707c357344c82935da57c94b21268ffa8c7bc96970b98438f8d 502bc8ab81ebac530e53fc5b67343b18ff1befde1df6e742cc7c936fcebff712 81dd829425621f62f7cac8e527ed104e4baa34794dee8b3c2a5b60e1760c2a47 390f6dd060d7c3890fdc6def37e1330a7c1b1eca2fe32d3158b8f16ecdb74812 631cd42685ba268a035dad07f315724cd14a40a1004cf3dd4a5b5f150f98a447 7ee8e9822e8beb5442ae9ffad73a389e499aa62cd2060c52131346b488e82c5b 916f3cd61a24d4b2583a61c0eb7acb24974a22e471f0066b2432619ad85dd051 0f22d42bbbee98e715de15e857a10982e1a13d5ce3955666ad57ae4cef75b6fa fb67fb5bf5d1e9b8b6c5980e16611bdbfb4ba4dd08259162883f41cf35a06122 4604a768fcfae7afddb012e66f4986823023054e1c1c61294c55924131d335be f675d78cd4cb559a6ddae02fbb9483421229b7af92595c8225f06bb5b28412c9 1d823b32b5eb6f3f9413ec599fb2564162a2f7729569bc33976f4e7294326a2a 411dd33bc5741f5622c2f38b1bdbe7a8bb053d44ea41262b7cb9b0e2d70d2122 d9ff9ccc3691d0260e7f2b932e94e975a3a019b0623eb86b48aa846067e6dc28 d59506d88c2adc765d3bd8951273c02e6269d44aedc02ada858e841f39b6747b 8425e0985a16a6f398d35e9b4f0dac9730556fe1362a73aacaf32e93ba2711ab 70a9fae175fe7020724019c51b7ee8aa51d1f499061ad120dbbb47830c919ed7 f3adf77877e214569555d29a774258e9d82884f4257addb25925c6d81a2c566a 2db29e3263bbc8e511fcf1d710713b05035b37d2bdab441c7e47e7d63c2bf193 f9e15dee2b3fc98a12800b8e209006038982291c958b127794e8514b6aa6c2f4 19568fdc98cbaaf7fbcd84f3a9811dac656e294ac13ddad1abc94b514eb318ae 190928e09c16e6c1e3ce8d912438c7878b7f3b8473742d7c742267a22f064688 e6028e18e991da65ca2cd4f2ca7b27158687d54f199f964bfb2ddb1fc71e671a a08ce70912fa114fb87b7c6bf176bbf3c11b7021afc6b70833c05428ad50647c c05dca1c4f99e439b0d0be7e5a78d32989036088db24c66217de53870cdbe53c d1f7334d110e2c48d05e15118a03ec2be44d1721f5c6da138874de641eb830a8 d572779c680f7d3001902345f558ccae1e6524c955c524abce50c7e59f70f26c 960e411354f5c12eaa9237c1f68f858ecaf8f866f53e3c114d31fbc75004dc8a 899d77bd7eca669d5359e27399609392bf4fb62d799393ef291693125288fcbe f9bcc1fdfbf33a4d79ce71adf882236a721c5dcabdaabbc020e6132ae8fa06c7 75d04f787228606c1588d68dd2c9c072640bc9aa31aa7f84b6734b7f8f161e21 08745211cb7f6b99a273eb975fb8a20bb39ace7e5bdc8e873ded38a2c76ddabe 3f08eca67dd097cc91efd169f3b4966aabf1f29aa59bbca0b246205596d056b1 23614c16b68f86664d7eb9db35adc42e84436d971f321920a88ea4458057a9de e8b400811bcda68458c9ba84392924c8652b0f1f49cd083f7bea49d432fef97c 8047929ce71319f33d770d73bd91ebc6c155f7634b8fb0eaaa0d7c17c53d5f09 388c982c4ade58a9644a058f9b10f2b708650209bd21e4593f7bfd7736a3ae63 6281f19259fbc137f61b2c118bd21201bf40e892aec5a3707c76629e187914dc 7a88198b7621fc37dc4905dfc1d81df3778854da17038eada4e8a2cf25e4671d ef85daaf57d66dfc9dd6a0c15d2537ef048a831316b69a1e203507c88bcf935f 59d4c75c42886b1143fef40008f4bbfff483e3a361bd9de916996cb19c893e0b3c190b7c3ce51f14e16d57f9720d1d060187fa4cbb2bc4e2e3dec57b254c580f756ef8ee5074087df882b8372a8ae910b5ece2d4289b9cc1d448d405ad0bc9076de246c3483536b72f3178ee935bf2087567ee701271e145c6108a8b54f15902de571f290185c91d4da024c86738646d4c6b68386866173d6f327f8245fb9902d6eb9a41b86f35fac97434a2f376aa3548c150ca8bfe319ec58b3a795ad5e90cf4ef0f9a72ad468a03a2bab6d26687ae885a6a7f3d33d4b8c3ee8c3043b99a02f9643f635238cbfcb037ea4c0ecf32584c30b3e77acb594d9a2728a847e6a005656a5c99e988bd3e47ebf7f811203752ed3861318771573721dbbb103ee9b004322583df17484d50d92b3705bb4e856b5d6cb8c826f63e2f2f146e583cb9bc08cf20aee06d7de242f78654248ceb04258e7a89f2c39f9f12b3bee0815336a10fa231103945a2d0e98a5ee7d43bbdc5b753035166fc539a4d82179d1809e07b066e97ee90d5a404ecaf2e1782395a39877171e0d1b87853dbfe549bb947c8e80126a02d68a20dcba06643758b8d261013daa5e1cdfa7421e413b3cc3de222080299fcb396c2f8b876888f931080a398ac5f518391ae9c6527532ec935b2f3ef0182ac28b9e1fd882728a3fbc9db9e982b87b627082eca54cd20c6d54a374abf07a04a68e8beeae095524c4a3854e05eb5474ce86fa69d32f9e850e7eeadf0f503b5916cc793d05d31761118c7ed6c50eaf6fac224f79c3a9163d963bb12c9300d73e8f6972d96d7cb30a948a2ef6b91f0d9a6ccb946a51a5bf759c565a42bc80944528e15cd32f5adf1492a6fd791806cd2b319ff5c83cad507e1ff716794830998e15aa7a661f4e5ba4107ddee3d6558cf6f9f19ae5cf8a0f72d416521ed0f095e4020cda97b40a7b40049624845f304c0e6fe682773112609dee5adc0c81a0064e83abc7817b285d3ec20e9cec84d2503d7527dd0c0606c37b88c655aa32a0dc637c1438aefb76dd319a68c3ac965a3939d7f52853276b6c734be5bb7ef4a00abc49acfec57237acd044b8d1bebae9c9cb9c868c433cae172de9b5864dba3084b52bfc6eec720debb1e88b03c3a292d5fef61ffeb84f39a50cc7ca0b028bb091764466b628ee4119549a41d42a222a108d2c57e72b09d7485b769584f999f08719c5416e533bb0c44a110b9cc6c89e5daa39b4a1521c2c363406465ce5f820231a012abc8b6774bc2dfd72a0424e96382ca95a30b38a1306aaf5bcf7b6ee0063c66c7783e4f558427dfeabe78a39ea8f05203be146ebf183181d19eb7d8b10e445f5a8860426ed4dd8231699854e5de970dab1f5b0f4645756f98919ca5e2044daa0fc0d38fe06f0953209e5b5922c2f313fd0de247f65d66c61c1a4fdf6901fc815fbafebc4bb5f15be58f3c8f51273e33c8b7c389909b5d09a6d12444b100cbdbfb93522b83c352e36ddbd51e7b15d7bb24e27cda26a19915e9dd9cf9af062fb2afa9007629e72a2d400a73e2c29321ba39627891b5f853af802da083c50dfd11b01dfd60b1c23cf6662abef6d5b22e8a3c180ebb78a1e9d15af347f867081567cbebfc29f42b3de9542dab3de39ba75a204bf6fd6fa4236319327470fa0c9bb730efe77d4d04eb9fd93888daf3ea09ea9ec71bec5415fc9aaec85811a1009cf5747b887b2a27e00be04a83a0433dee04d2b1432e4b31da44004b37948d02f6a158af3b7e209ab14e6fd3b03f4a65e6ca1e84127580804667fe80b3c86c061c2235c3cb06c721b99ce7f376c7642ef6135d0921af1679bf7fa4f6c1cb590fb0fa4e34c3718a114c1c84013d8ba820a2df724333e87f71edec351374b52b057d91b526f467b4524c34f818511e326fc3430e637f74ddd6b12d90fc711f850bc39eb9e362b79b32c07ca4a24c565c15bfc0ec3cbb32258ebdff57f341cd090e53d839eaace0ce3446d796b48713c51df732b6a36cb1e0d23ff36477fb84300dd1c107aace2b82478468b8045b2654da7696f3cca6cc86613c40929d13f2fa05ab590f510004f5b649c2057f40b84745ea75964774e11e648f8c0f1663c8ad07ade51b3aeb2e0f8b79016e74b36b3989473c701202fdf4097943e015c1a1e507815d5729b2617bc17ca9dc785c1a6b623eeff2e36ebcaa519b7d7a7438c85507041e7af135a4562682db07f06fe97080812fa6c8429d203d5c40d1901044e00c9ed7a1751b848b9ed9fab5765247e1d42fcffd95ac99c9b04bdbe585c5846407b29cfc49f7185f0f165c26deb84c757ce4e6a24fe9e979e5432cc6c4eaa638080bd4be7086e1227f5bd43d65b5e99542ff4d7e6c773814c973ffabcbdd53b607209f83c277af6161d04303621cdee23bf9d7742bc554dc14ff97740f13b03a068fe0f4afdc85872b6d9c2d39f5215381d6c0993a3471cb696b85f17f024ecd0431773192ba98652806dba4776b789c40911c575da16dee8f446d11cf770ca90d9d1fd40677a954a5c73afce53951d17e03a420c55db88a16fc0f43c0a1fc690fd1226ad5440d79b3c907b166411518354db22b08d1d5ff2351987e185b124d005a5cdceee1d1edc54610224cd356ff312b8f4a1fc298cc8e8e2253ed37273509292c21d45bb6d362350bf60b73d2e22edc29ab5f917886e87b28c28c9dfb500a61e9950252c700ef8de9b60c5a078f03f85b68f1235e01a276ad439225c6e1048f2cb0273ceaa4b8a1b27fff5360db125940be687219045acbbe789fed5fc20c4fcfc190d072eabbd137761291515d05f766f699da3ec829fb15a9fb5c2b6300f86068ec5ead6cc8ad8fa46284222ec3d7271039b73d25246bb31f65892b110d5f02fa88f94b5df6ba0b343c1f2ff73814a0dd79b063afbb412cc4a029b8650ed2e06049098ed39e3647966d704c5fd354cc1d3c51053bae976707b1767829008c16e60e40ad101c38e381f7e10fc3bdfc63bd0178cceb4fc8f9c0dd4be6f4011494b2d225bf3c8a4e4190a5cc488b6a2098ce1e940ff29eea71d7bef0155c01b76bfa7789481a4b6529f44677b165c72b7b09f95c7ccac4befccac7a8b3cf0906cd6356a1892c167e2befd4d3ca593fee3d88a525748434e50f386e19f2e10569a186b30d649ea5d3798dffec713915753bb84034c60edab2bf69e63b184f07afa33c22dd03c2b214c8e0e5b67d69fe71f611bbf422a186efcc21bdbbba7782e71aa8f06eb55bc2ca9ae817e100218ee3b7db57091c694677183fa61077f40083afbaf12888f865d6544c5f8d4dac1d2a53b232ea9cb87b57f9460810d5c30f140d92434c329d29d26960d274616e198dbfb5ed514db1b1aee4e00d37b27e04c45ae48b4812d08e915890111f7275eb9ab6690a28e0e74d4457320396f9a20f40680fd7a9955bb6c9f7444cbc8c63cadef0f4150d8158f5f6f0d26818b7f20fa1dbfa5df87e2853a02b6a764fd39bb429a9811af8169de80cb344f4bea9d108496cc818b3757b21b3de1ac639b5100c2f3bd6b720dd4cf8173f92935e668506a4c36b039a7e7ddc8c57852876bc96903c20973353fd88fbc6f41777143a1e0e4c65e32481ba7c30cea59f3b717881c74ff576b86563b5861d470a67f34bda00af8ccd2c6d48c7fe6d05f4cd43a5c99f401267a37a12fdac48a2cad5f215fc00730b13f054bf36dc60c7616ef9485435b3fbc7aa2964926e2e60b067f73bbd0e44757367cb4a793328951aea95babfb2fb80d12975f5459ef8ed0ce7965d9905de875e70012567fbfca7339f85e7ca01fda375eb3f57c1ae00d703fd774be9078e61ecb5c13e265c9f30a0b3ac310c43b56eebabfb7e8d02a8496fdc8c4bcd0ccf8716d9365dd14282613b1244c9f47f4143dbc6b3583136924d7d50d5055207d180fc1cadd2af61072f48e771386ce0f24d154735717bd6471629701a489005376da4a55491c4e13595f151452a2dbf31c0914416662a530ad65af9cf4e4f045a0341a81d718724b69668e2a8b532e7c141bc3dd97837778190691835eb5e032fdff479be6c9ab86d1fcf54648eff8f0703aa33affb5a688ca5d70e5d2f720c1fbeb01ccef0a4814fa6874b063083bfd3bfa0785a9b28fa771af2d144a53601664f4b4bee2d8b5cb6603527f7820be9f4b21c4f6659be783dd2019e94c72002fd3773ab5970ea0c3a7d0a4dbcc7cd2f663e3bf2245bc007bd6e7a4e61a154062d8a8de08194844437514274be05e19da042787ce2ce221c911d7d20c384df0d344c5c4a69f4a7b7b4d606b22563112fc6e3351df979cea39d1b9fb71949600c16647c7df3e0e0b06b7fa7cf691a7e6f45148164eb4e906887270410bda31105b783deb92172c63ff55e6988deb2b472ee16f1ef484cd70c11b2e41edaf714010e1cb0a268f1ae6e0d65974efdd47947b572d8fd527888a612e0efa68f26be0bfab5754023ca8ca48b1da18fc740bf44a86168a59dba90f35739fdf619be1107 false -check_ring_signature 8beb4f687f290b1bbf5e16b899ec1b0812dd704846b3f91cbbcacabd1b2e9d80 4804c730d5297adcfb4afd4f555bd972bc90b4a5b836b6d4ba2ac4e85e0ad902 1 ca814a280a4e3bc1e6353fccbffb35eee41d4cc0ad878163333c8c38e87a0431 02b98557516bebf7044b6667f57f2e0c216b8bfd3c8f0ac76879972d76b3e20b09f263cd105533a10ab4d3f547a01e08d2c979b44d3937105fbb7380cd58d30d false -check_ring_signature 164088d00e9a0a9796ef06d24da3fe7acd1c99950d7b93c55ddd507735e2e36b 6659910e00973429b4684b01636ae1b2a106c9495b2d5779ae91d9e53dc5aad4 1 1ee95758de4274a0864e926559c044248f97711a442e8fdd54d474ffa6769ca6 2876bfd59057b62de7b70c4fe463dba8d379a7426bcad6662c1b61b5c7cdfa073ef46e34f364e352db87518a4551a649baf1de90c5cce144eb10f1b117033c6c false -check_ring_signature 7432f74bd684938de1609fb48eaaae049b21f053a433b0ac1f3b3bc87cf3a57d 1c435712256e9c219b72d8c2b3f0711d5d3a26b94b4e8c512d337f2cd0ae5cc4 248 e477b39e6c332608a2016ce9fb35e3654a1de4b9a319a5171b700913f9a15258 0ccf361758fb2c19b92a1f4da4e215347d129ea56d52b501ebeca8c40dd54e1e 2275966692aca85f11c9f8924f9089ed756e3afbecfe39f8727617a8ba6c4f16 4e9394ada7e4b42e81a0ba6c2ffb5feffa804e221c185978a7bd4a684c0aadcf 7a7ac23187c35563ed3088a1347165bea44e59023f437346d2fad34644ebbae9 b37ee8b02dea3a54e4a22a7f922e68f0774001cea66869108c73c0982b5a5953 df9781cbc4fb37518445b217b8e7d612533b9bab7eb12f8a362a54c68736788e 697cc2deec1d0ff7b24fd08cb7ed77abd125a1db9d16c534c3c669835833dd8a 7afe9498acf21808798271e8ad41b98370db93870868da073201506a82d0141c d57732fe5ebd927b3bfabeaf1dcb48eeeb2466b5b2a047105cabb6bc2191d60f 6c52d7850cab5828968e6e686ccc2fffecd10fb5ac032b599d8d5d23a673a993 71a6bd92d1e819769661e54b368889a559a3d4e49427e9ad3a99915a8cd9db6c 5f317089bcc9dbcd4abe9e45afc598ae4697570190bcff4483f718cdbafb78b6 4bd104336b2cf7248635efcf73e6593ab80796f2343dd53c30e0e95fb48149a2 209211a7159d14dad0b6aabd94ef690a3aa766b55589878e8e5d22a6768c4e4f 37b8ec0c0821198e2335975d2b561cf9b8ae090a94ee05180983ebe930a26536 3fb7876cb7136892ec7d0a6b0a89547865e425d1cff62b74d865bc316db8497d 46c0ff429b7c1b5d4b75ba6984c5ba7c776629b082e7fd888ae8c8c6c048fe5b 30238073e013ff87e1967941748028527f8220dad0547cda900d5f18addcac5a 47f4b7a6a64dd839736ec2215cd2a1fc2357cf66f7f236ed18789c8177f5eff4 c03baf3e86bfe54627e328529b50764cef3c7165f9c71eb2aaa5b44408689608 1864eb175b2a7e64114000e44e05970b9a4b9423cc347024863b082227ebe908 a0088d067bd4c74591221b8d4b07e7b3b26806b026514907acac5fa6e8c81dac 00361790a393b3ac41313ade4865bbb641c79108767879f42f42e66c6128709b efbc74c6cb1a7b5571f37a9c33c1559ad50b107f680ea086a970078e2d2dd7fe 9bd279bfcb403ee4fd53226ca01174aed392fa6198a7ef4da3e99c236f2eb856 898366747b3586ab2e36491795d1e145375d230c2381d4d95d4dca5df690050f c2e7c432d5ec701251fd5482bff0c39cf1b29523ddaae04ee317382c5984770e 5c94382917c0a0c0578887c9587413d7935b734da1caa01e213893ad59b46ff3 7f9d0fd25de8e8a05598e219658ede5e28829e7d9bcdc7edf338fe1ebdf09c55 b4a3533afcf0f2e4606e56f87ece91834ed98cec55c48994f2f6b4b336e3a81f 317a6208a5bcb87875861a606798e5db11ed37d5d010c95a4841eefa32ddd15e f4a30a8f40d358367a0374d656e40dd7258a4c51545beaa7ba5bc40843f4d97c 89407c57cbd95b10d3d75ce16398323a5d51e1286b7d80432804d718a3109d2c 6ef4cadcaee6822108aa36396166ab0d55195c5fc4030f22292383c1ff2982bf 994eff3256447f7f8d202c9bdbeb218f0989fbfcac1717dfb20b8c42f2c76945 ea63342589c173067eb5c36c04b7ce8ba9c176a28c91dc46d6025059388e5d70 ab22b20dfc1821a15c27c7c85beddf86f9666e5f8cef1225711b4abeaf54178c 55957a603b02d53fb6bc82ead879b3cf72cee7a9092c31856ba7d28fd6db95d3 10d17133b76abf511f25e9905ca39f15a028768b598a370cc5321cf4fa32dc62 391e8e3a7cdfa7479c32c0e1710b669752afa59e5228a32de4ea24f864c7f4df e5703d4be4d503fcdc464a5082903024f161097eb9804e1492ac45816c48f472 d29fac769c6051a19f2867b53127ac2a535b2a26b1f17a243b9cb9d75012817a 41ef1a339773eb62b932e16c619e1f3388f767b391474da5696894c7552e415a 2504e40d837e5e431365f2c4cdabe369e62d8d4ac7903685e2371257b89b76a2 c392bcde6d3488327ba55ddffb7720217a8eec15a65832946443367813494af4 0bf21bea92ca116a420e197720fd86a960fe5c8bcd54fc8b576dffe3527f86c8 5c74ea4a72400081c0118aba027cc299a8c73f384a4ced45348ec1325758707b 5a6a77430995dbd92ac7c5bf97b039dc2e6115a50cbb2808cdbcd8c159675ae3 0783c4c72669b833c2bba3be8fd6ee771bca45a8b0f635b7cf7f2f24002fce68 a476a88e027f8b65a5dd8c6b534bd05b741725268df24c86d27a6d43012c3946 edc0e9fed82164020c8966acaffd51cb754eca6e6df781f33f82f7d167c15a5e 08cda4306129e304dbe5eecd707203482adc18ecdbcc7e523232dd970a0bc24e 73969c6672980d68c7d6f5b2a372b940fffc1ba60041dedee538cca005172ee3 b97f1badbf61d0cb0da9b8ba68fa6b4417e47ead8fe73d44bc31073b19ea43fb 1017ab7daf29f09788e24ade7dcb517f89d158100dddfcc9ba3735dc027caa77 6bc605d01d1a43dc237f02925a185ccb14d789aa7f8e402d25ca8b22f8250b74 cf33a2db8516b2eed2a55ba6a370a5fdf7a26285e72032234922ed23312a03e0 1024d9a5f83000f47db5ebf8d82e85f007325925793efb597b90f376e1732813 3fd5cbd5d05511334c780de73912b308043b5837b66cc57138422d1bb6789b64 dc4a977a035cfe42799d8bcd1dd8bd404c3accc48f1540d8a984c72125b8dd3c 39bd4e19c0cb74199e2af1022c46a1a4e5562a71833e702da8c0254a3aae83c3 1a752f07885ce480039b574c24a8036bcffbc8bd1add5538fa9cc21c052cf837 0fea1e73c434f62b3c46fae30a93a3d4887d7187ab8443dfdde59c1772958556 0bccb71665dc47429a3a97abb2ac9ee1fd9bc73bf021ef03c49936a961365ee6 16efd7dc0d38534038f0673feb6936193ea3852c11abdbb56163dfd4422d2544 28511074480a27d04db13fbdc68f5c48579a702a0aa32dcc3331a1f9fb7d0331 4e65d8f5d6f1e4a27853468cbe6db4ffadfe41dca8cc27afdee453561b7ace3c a2f293ec982b1563064f23a213100ef8a2cfff842495ad7be6aa5e866bcfb056 512167717f0affd85f08dc0ab84d8b267871a92304d43753d4a18ea2bd98778c 6838e2e57adc0957df325752af62b53435e48fcf1c4eaca24a5c1ead63f137f4 e7a13c4898d4352e8726497c2ad06a21c6288c0947e753145f9a44de46f2e600 ffe17f554c6f880ff5c96d7bf042498220bfa15553b90e7d5e5e51f48fb9dde8 e19ebd95dbc197f883dc1892bb203dd2158c9ac97ce133da3a7799bdab7f9f3d 809aba9f0f30cfe13d897537d49f210429c208bfddabea34e08a1c61ace71423 e95bf074b90d2851c41e88977b47f93944d71a3979145eb7b221a4bfbad4dab1 89049cc137e3a91d9af5c02d9e324cb17e809a5e149969f814559dd1a9bd8587 ba4234900371b9c5f2214bb7e5bd739dde0d95b499706f71d7b163e3ad315408 bc42c1a91c86515aae9e381314f9520837ec5c1fb2c84d6e33d5de66aae99136 418002a65fb6926e9485aaabdac379f56b04b65373033b95a957d7bf217e3e66 c48459714205baccc8c9e3536bcc03b8460b09e4b94bd8e15199be78d8644ac5 23455a2cf6cedc715340f7e6ea376c01b9070b486f76c5ca25b5ffdfb63009b8 aeb174162c21e92ac59a65a69a9b5a85112a9e52ed5fc69a80eb7c3be91dbe83 a1dafc41da5c01f04c0ac36ec7eb93cff83912e06f1521fb07e3444e0b596158 4d49a3a4b16234b6ce8bfead50800ee69504f527994a65451e4241c544bc3c0b 9fb9ca85e0886ce8651d9353fab99028d51f96ecfb0e041d0449e50bb85aa040 b410a51f791276de8390c0aa7f0cf52e46c2260d302a6d2ca029db18ab0f0878 db3a8f642d12f7b65c8be5ec76b9fe9065b1d764d1073c5f0e023018e356713e 0df18d1b9b603b91991114646a6c52b49ec8f453221e21b614c27fc4277a9915 042906b046b69e45bf79f724ec31a5e04a4803ccf067a79131fe6ba823ca8169 1cd1c63a5c21ee0f9934ccddf21db36d5180b98bdfefd3355bd054c17fb827fc bfddc484ce99aceef7cf7ae38bc5eaa595db620f3e49b6ba503a63492ec100a5 6ac92d417660ed3cf26ca157c723317e1ea3cd80b0414589ba4565e2686f482b 72a829177be8dfcbb5cb74f19963405231e139f0ff68f7d0ceb4265856c6c8d1 a0d4cdeec417d55ca10e433803a47c6ab37fcb0e63321a2c004fbbd512221650 155550aa3a53484662a4000cba58f56c38e0f863a4f0928472a9c0d31d35bcbf 17717feed5fe37d081d8fa8a3600e73b4e2f14b42f9f1fe2dbe6b1d63cba5a70 4dbe7ad7d1cc012bacd62cf8859329d46ca943d8acd49dcd08338facdcf23339 ac3ec33ec7947d10556ef1bad569a67a2d6dcfa01dacea95ad59c45e083e736a 79538f809ec15936244d88bcf9646abab048f62899a200c2a29b0fcf94481c82 fd3d834a3131c9188693681bbeb955b2f7f48aacb03323904083d74df806a8de 24c5b9621dfd8f77af2f0e38eed7786042e0033b457ffdb9e9a39ce0d32723df 7cb0da7adf7056e8a1b92a2cde7055da50b62bba6bdd7eea111cfa873a39dccc a42bb1002a1c988c20d3b26a2d195d471686cdba2e5bdc68473c528b2c2086ae 715d6602b0b366dfe3608fcfa0d7347cc10d6339c13ed365218043c152630220 889377c3f4bb16c0a42ea53344dd9f830279c76058761ab0f577dd41392b722c f07e51893794d0d981242e3f237d12b1c6404330f0354959dc47158a831d2d01 3e268da5717ade97b239e1216020a5933f5b7446581577410413c71552a3da30 a7ac844daad31fe8b73f7e276fac6c661058c423dbcc198dc2b419d03ea42b5e 66334ade25e426d3cb8bd092e1627a8536735b364f3883e4d45d42c2e3165332 9302d2262a101707c971440490f1aaaa7b522690562822513504e9dcefd14e24 f0fa79c55e77f0032deff757e434436e66eb96a05abc87e872037dfcad105185 1ad0807ce9c08dd4fd166236f5cd56c7bf815f2aa2d87ac692610f8a8cdad505 6c84e2e9bcde2f9bb30f8295a1a5019f918832a6984b078bcd4a099eca42b01e 02b7a89551f121d61d6e8ba9bc462511b2f4796d20ff589fbf2f67ded6d84db6 de126ea11e32599695b7835ab8ceeccaf6128260b90153ec9511397315faf209 f6090902ba44cc3c5cb92e24be0f9b16967352bb07aa04a7c6f4065ddeacb95d 95808db2e2217c2f07ee235371ad4a7db5c756abe8c7e4e04d379d693379d34e 3d349654b2fcbd836a5f46d0853d746301be9a689278dbead3bb9db423f79ecb d45e159859b1690e96e88648fbe2b250f5864272e1ef74675a8fdde5065836ed ae53e619ea95220e26b433e89b2103841b772597d49537355f9c515a0dd2c80b 76c01dbe7931e3c617c32680b50960e0fab94c4ac35708f431569f0845f4774c db8c7eb7253d70f4384a17641581db65b1374e9dfe11ee13ae21a8d4886c97ff 6fc49b060a3c55f4783d24deef066d78d01940fb1f8f53331458751910fce7a6 e1d2b0d3db3a06048d73abac3399a914b6dea67a8465afdbc84eebaaa87f7279 6b31f38c7e4f199673f406c817a7e8cc879cdbbad124e2acafbdb65ff2521eb6 2c37aa39fa15bbbbd853b89d8c2a5d9385288c8f228372ed77ac7ce327798472 a46e69c2dfe11c9791c7b4f947e004d2e982a0b76bf455d9eca416462f2120df 47cc553296a74e7a60ed46426c6f32520b8e682b8c8ba68407e435a2230d76f0 169b67903b42cf39d7217fcf80b704d5a37c4985edb964aa2ed12111c27929e6 227d4a9687a936752ead10244d5d933ad817c1ffc0b3413f44286b1401e90b86 9e283bfe44b7393b3a2001d06ba9f73f29ace7e34f7d1373acf117c4205c7145 a39edf76bb8ce9dd3ebfd7bbfaba9da4e961d8b87886893f9b317ad950220057 28240899d96af46cb76ebc19c74f21b149e41af768855dff9d68fad736a159ff 15c2b4b33e98457349463bc4f363ea7136592a5d1e61e5944437fc2995496c9b f7758e14ef9933903929c8f3f164373c209da8e47bd2a77c11205aaa33a48d2a 078eb63eb31fa429817631ec2f2455330131318a71e43e6f388115650db7c1dd b1914da5f3e861076aa106427ab624cf8074d1ecff2f652f8425258fb4c13268 4cb74c2d2d0c8d45305a1d666257d59747f99f56565d03b704e4111c8638f995 b5ecd1209aa6470ed2c3deb9b49fa9a4893c9df9bb25a39e22225c864836fa4a a2b942130e48dc09dab529251bd00a568db53c65652230b2d88c8af95135744e 751b7d695a2d7826b97aa4be62fd98351c35a09fe76b3068f3cfadefd0e4842e fb36d9432e9926f53577adc0fda23a90b24e21825e93f01e6510c358cc6dd9ac 71defdbbe88b14326c80d596ba53b66abfd262a0e8dccb56fc5ae7546f6ee0b2 f6615c68d9d75c4101785cb0e494b98cead4a6b543b81b71f7a1be2d2896a2c1 b29d964059aa59b3686e585384c09ef87d3b7f9e2cbbf1759f707ad945269781 671f04a077d8be18f9fd91d10a5e8b3586e909aeffc239acc76e5c0007615793 2757a653435bc1d91673f823f0cdd3a9cb4b086956cf8c6bf34f5af66bf320dd 18cfb58d90324f4dcb9c617cba7c0be0861cc7975a0c0307264de35f91de78cf 4504a9d1159dbb8c32cbc945fb5cb4580894c7c6f09563ad87f582daebe1cbc6 dbd3a6c832a8d340a6b5f114f7af9180184c424ff99c242b7eca8d79b8334785 d8dcaffb55b9074bb45af1a5b0265d1b89f2316482ad2ee73e39301979e6ff00 53e1e451dc28e60e168a85b52bca764a7e6afd055b863fad8b8670a493d654a3 96684d116c72bc80440ee8808685309b772d127814c5de76774abb4a5842a4f5 da52a621abb93f8e58f27abd707b5e4b6a14720300a95fb60d0e6533e42dbc18 2e6c84e94018cd31fc36c1d5f23b7f72b6d2394723d09f87a6770ebc0bdd212f 53038548bcceccfb14e03c0bd25a8ec946cffc78d85db8340b1765eb0054b912 f0d0d8edcf329f89f770191e8debee6c71a564b33a0358ad027925b8cd5205cf 139ccb3ab74f797ddc856a958d46c8debf779217aceefbafe464501dcfc2fa1a 6dd79d71131a6ce22a10ddb8c0b60bb2c140e58139d7aed696c92a528d4284eb 54856532594f11ea7910dc102556bab5e19141360363a3a1d51df91ecebe3790 4c45248644a4780883ba69574a6bac2ad7a4610d58279bc1e1befe537982324f 2c767dc1e24bed280379d88785f68b7cc4a10a0fa1e0aaa6c547238914247ea5 e8190d6eacae2b325ceb0e46e2175c84c052b1cd50d270efb1731e47d1f071e5 fb1b57890b6855f044519120464eb0782e05927bbcaaf793669debe69d2e7616 f11a9a6ea73c6cca86211e2d49538ba552f4b543a70dccb9f556e91643d6fd5e 55626f435b2f62c97d5a0225b24d6f953507cc8fc1693a45fcb3b0d3a6882d94 6f3774bd5477ca11213022c7146495002badbb9ebf80fdc548bf9f6473d6bf12 b065da7f24a8a421a09579117c55218a56f1fbf6ae39b83595b4d7da1e798818 d6de8394447ebd655daa9323464ef5bc15e5a3bd4fa240a28eda03653d629b3f a8b259ba6c83e3c5fb9618876d3adbf3c8eb22c619383d9176e6def5772867ad ea2529c7e919e6d567460a7a1430a41cf68056c5cb297b6bc9395625d77862df 091408d5b68c89363adb889a30b701bd442f9fded54173eafc3fce6cd37d796f 4ecc7f9fb33c7013149c093937922935d4d7bbfb1dfaa8dd2e62cbd2bb8389d0 c3ec0e9538814e7a48854d4939c3ad2b313d17cb9bb81d82ffa1d07294c0bfa4 cc6f75cefc5683050d439a723315aab72f16d1f75bb94d9d7813a0ae76cdd137 6e93bdb697a6f29bcd4b18b8d761707fa510c7c6c7ce3bcc61b43e2d2e8b2f00 dfe20ffa77cc643e88b5fd0b5429fd0e594316beb3d4bc7a5642fb967bd3c970 def51cfc7b8c8f243b7c9a85a0fcaab1d23520f81becc77b3a0d2c715fb73e32 7bea16877f2fb9dee2074b39a802779e1c0df74f4f157245abad33d8b295694b 5afaab6e0083abf555d537dc1e7fac53b31f24f2dcbc8d19e22d5dea2fd691e4 8d8a89d6d99a7b8dff48ef7b0c58c4d118fc3b11bfece5dee299bf1a039afe83 887a95144a49dbe8e9ac7ebf6b694d79ed70ece99bf257158c64437ea812d6c4 507068919e0b55050cf42bf754836453f68ce59ef333ed43715b24cdab623b49 687f6e775e5cfbf8c38f3320fe7510d5431e917f41a3b4140be760789f53271a 12430ce372410fcae348f50f9de4ce84c7de7b122ad0ebbec305e3e8e1205d74 8cac3795fa1fbec69a6ae71dd1f0877e7b26ca80d9781bb57bc33d7ffd96a3cd 5217717d5ee6b930f849f7e3cddacf9cd511b9fcb3da99ba5885754778fbf0ec de8fd7e9ee1badd6d9b98a10c7a1fc25cea56d43af611f3e7beed54242b141fd b51fafd6c4f503749184f6bb10aec30af8e6ca3faca8c2ddf88a3edff5b37f5a 93d6cea9ea7ebfbc9cab8305eecf2253498f27f8941580cf69df35de69b71ab8 910ede97ebd107d15759b47cb34f6664a94fe48199e1c6f488e9064a4db32b5d f0d22ea3bdbcc583cecce4f32a00a15ea829db74716c8122c210da0a32edc926 df709a0f77b6c10d0c13ce7ec7bfd931b19ba8ff4ae0de19d14d9edd7d804f45 f3b92d264e5fba1361c6baed4c0763680be2541f0241189a37752fedbd703d2b 7495a79495d3bca0a0703716b5f5b9855c575e3252bd367d16aaf18418738a79 04d0a745dd0d32673049b4ff1b511b26405298ac39474f941fef345aec9b7f54 af5c6c826b699097be5896a701ec7e95c055393166863d69aa643a4d0f3ec785 0208d675aef95d514a847da96c4f61ed2e0315e29c43a664b801aa43301a6035 25a4e0464fde6599cdb84ff1412a34a417ac211ac34a01477fea626a2a76894c f9ce11c96f1d9d747cbd9cb050261bbfa1d371a40287566c8cc5f9b62ca9055f b390ef3ee27d3df17377f0d7072c567ceb0b7113ddfa4e03a9da34c7960f4f08 5a7c23ef1b1f68b3d7ed62afbfa467d74f2eb8f877768801f6229caf6200c254 d88184f9d53e18d422ce3c5d5eb4e9d6a561a372eeef93051f87d313528efb05 5eccbc2d83110d490d37b7394f7e63ec4615aeb89095f2468639d22bba169430 d2ed73682d1a62f774ac871b6650c49d5a0efcdaee4445a8d2a283dd624704a7 659f4582095fb5b2bd83afec18ec0c1b5edf1d6083361a2be98a8af115166e0a ffc757c691fff23e825f99292e2cb01ebe82ab2f2c03b04df99005fab7de9b99 861287c64712c3c61ee0dcbe9e2ea0a6299d946237cfccefbb66d3dfd6cf94fa 9eb8706b51bca5e1afc3e07e7664852a1daea60fbc7c01fc0ff3f27f212d627a c0346e3de2c40bf0ce81ccb2fd3a8618285fc705bf242da74e7f2b0eb8b81ac3 29e44d634408a919254f31b76c89788f4052b11858349417782be572c8d36138 e9fd5b9d4dbfff5ddacc908f3b5f11453a1ce597d009bd2a15803b783cddb2de 438ddad3b0c7933f8bc2cf0a6a3f4cf74c08af43de6a4aa0242629f5f85961c3 b06f70c26237da649e265bf79425a40de56755420bd1b23aa631eb01ef0fa622 9ead836f0777be16394e4e0777cd0bdd3ef607946065ce47f29b7580c59d91a3 2ce2b06f1f7b48388930d49d512967cde1abc274c20a7c533f1c1542135228ba 6f2304cadffaf093db90132de13311448d143249bf04f54bb2ecb0f9f4722a52 3ba605ce239474ec5ce4d94b4c72a67c232db8c3614b9b6c0ec4a78418e91cc2 fb2050b7164ea00d158550d7cc4ae17c131124ab759a86a7d6e2821619ac90e4 e3809490b963a9dd37da385f4536fae54b7983ab80a45cb33112f78efa2f4a96 1c771fb6ecc10cd8236cdc4f58d6a2db0cc545123cdd19d976e0812c02984617 db1d552b6e8c30348bf1f413dd65a8ebab063e911f213ea25484b4006204c359 c2b60917e91eecbc3858dff1818abf8d030b4172531d8ccf317a8449d5c40791 d3cf3351bc70c0c5c63d05caa866fb86da42090b615442b6b70fc168e9291bb8 f4c22f7d248954ff9655d729e69378594148bade8e9df2c8048ee82e0c59f623 c825a13246f745284822eadda2511adbab8809b10ae2213f473eb710b9950b20 f6098ea86f59bef9e6c576b1da8dd8dfe4cbd2211a8ace19a2998a98b7ac1b7f 9443ae55552d920e801e217c04acfdd593962f3f334df3a91bb9ceffb29e4ea9 1ce61b423a89716d59dd4ebcee6d9a14f92f0f5bc694f77e429295f2986916f4 730a6dad589ac48c1b4720487240394a7faecaa95c0b17556087a1a1d1df7568 94c781f41226b5eedc4a231bf054b3345111a33a7a381a8bb951f81ea1e2e0f1 c67d29cc39d9459d7178e0ad5c40a4ce0a0a54f56b7bb3154f00ffc8e5518a4b d67352ef7dad03a9f40abb2a59a6409d21959698bef896b680b585224379a57e 2746d36722bab3b82c2d4027cf6b3e19a3305622dee733981f30a83c547e2633 0aec4fd74a92bc4d3420896a66aaa22a19efe9c7792248cf0db62a6b3037796e 986e4e19b9fd3512a4573cb196a8f5c19f40aa3d168f2128786a6dfec0a665d3 2a41d832bdb2ef8406e4740dc2fa9f82fcd9444501ec36d5f7cf8d87d2976005 9cd46bf969cf4247869fdf4b8c6def9b941985148c397566b8177c70c33d6334 adfec1d5fd4e8ac8cc79c61048c9105b3af38aeb388d563f8c13097c8b99bc8a b4ea2c2162da325352ee7d9ce99289ded362c9a1200de4fb5ab1caabdfc2560c f39410d51d137850e169660fb816977698f1f9d9b075f01f169416266fc7e7d0 7727492732fa3b975446bc52ae5f3b1ff1d802a792a5a71e0aa8ac6efd81b52b fe8c2e5f02a1bbb8736ae79b97b41a4d36ecce2f268243b9cb6c7a959e4deb64 74fcde13b8edc5cc3e85401828eff191dcacbd1d415adc2f7128f903ec08bde7 988fde45f8b76cfb13fc8ad1ad9f2eb5dbd67a117058577ac5abe6cba553e585 84cb23d58ae64b3cbe88fd7486b215e6572101fc759ae52fd961287d4abcd4ad d3bae829d00ae447b0ff0e5a219758f796436ac5c46100b8e539f293bbaf1b62 580d37aeb1743d494c71d50ad8bcc44de1940a4728d7cf8ea489ba09fc305c0a7a80cfa669677922d967cc62d543e074e42be6a381fccecbba4f347f6ddc7a0fd09abb0cb35cf604f14a4bf11d027d1b21a384591fcde844d5de6a82d5d4450834edcb26f67d009892641c70f0a36d3889b55bef9d3b3b324dc2431da5ca460fa57d9cc100c3b45bb600cf053420cb494b92cbe4c886511a28fb2a62a3af3502a957b82bf2d7ee1c0ea27af560c2402e0d430c7bd3d5bfaf6b0d1e38322bdd05f55727cfd00ecffd6560f72d14089a6e37544a50a45c3e857ed8b127d47f430288decec2be146e50efed54431f4640822038864b06727e2059bc0f4db2bb5a03ec48ef79d113eb3a09395407d5635f4d9fb7f0c59ccaee374821e5801d3d13082a5c6a94704f52bc043882d8b483dfeca26c6f8da06f2f8ebf760afa4f638b052251f7112acd15e9993dd90a6334216b7a2f76a30fd26e252ee124a5af719a0d61204d2f3d44362742e4f8e60f868d9b11e38250d7238821717bfa14f10b9401c19925222b7f07b98fe7129cf9cc11f92783732b8a962bff36236f0af7ca8604de272cc54c35638730e070ba10f8766f45e8ef503a8fec77a9c57e01732fc00ba60648e026d86f98e5c192a6a0c293f2a9a354d7720b8376c62ea33e2248b705d70cdf6033cca1fbc5d2e627fbe59a4be807b6649eaf8ac539e9903e1ede4505a1104d9932d5f70a2f5d2e09f27eb94814f33d55447413286f71532999a8410e61acb3df19c8074994fc880dcb680e7ff6616b83c7bc185cdb00fdc296dbc10cb51289343f3dbbb1243aa8bd9d8576ca55996517c88e4f142045dd8cc8039a05b924ceccdc6da37790b8dc3c31efc140fcce8a2b0b5c8c775bb06fc4b496ab011893b273b6abd362e1ddbf3414859c1bfa395ceb19b684386ff2729bc1824408d7a93c8219ccfc2a2d77382a6067c42fc3e3ca210afe7a050119ea6db9be5f055d174d7f19d11ed5b08beffe997d4dc0071c30783b54b7f6e15e6bb5f5a4e904b50a2e9f1ec7ccb73fd2101e84cfbaa9446307596c9451a474f5c17aac2be90b7f242fc40180e69e571dd30ccaf69fc464c09e0dff2119840a42dadeca1643012b4c6374ca8446a8e1ed730bbf6615b0cf13e49147f7c3033cc107594b85830fa1a239d476402ef7e38d8c17f61f46c463a36ddb313296c5d9e31c595e89180fefe4b78574b5fcda94d26b923ee570a427d32a0bcde07b96fe9141766c402d08d8c130f1b1a5a3903dd0bc3f25de12ab176cae9b456e7f18b289d386e9658500c2f0f0fb7d0c7ff3f14ef326785a53a2bd252f40a248de97a7e0db351672a7010f8568d159663f9f46f19246f6235a54ebe78e425cac14c9f85c397cb6266d05c4546ae1611e3f6f775e9d95cf156467f06c132814be11d15724752a95aac80b20a129445e1c23bfc5e7bfd4a9f9b10ee27278e9de73f272176d4f9ef4fad109077eaed33cc62aacf970d74d44155c762da25cb7af811fb77b420f1b76f13908d2ca5cc03f2251f798b318c0a4685bf26d64637ea8f954fca4fc37ed04c5030b80738f50ed9b8dd471ca90224a6dd1a17312d7cbe00b37147abf677fa2950f0b239d17074965c97ae5bd15e706ab518d1b55a6b77fa873154b7f3ea8d5d5c000d301e5555e78243fd709b7c20c3ca9ca7891be0e5d2cd69a973fcfe098b00e007a35ecc06d5e3a11173f18919d98463f7328069a258074cbc69966c6b6543d08c9c96c0d1c87884fb5d49a2793104d30209b7dd392178b8fdd43391da8ec8d0f20910f2b4cbaf7d184e7e0a30f88a13cb18af33804eed2aae7dec51a99471d0fff244010ebb2009ae47b8651d57c8e2269c13c954829ccae83e42ed1606eb80992e2608ac011d54ba68b77d2d70ef8b93253229404982729d96cf9f5c66a350dc149ea993f8ee2bf1cdc44e5de97db27df74112d16b24105fcdaa547eaed020763e1aa0e10b8282d5a203b5062fa18233e9305a3d70a80b1e8b9c38c2b3ca90c94d3b5e4ee0c38a34fa6e238bb12a1403d93599e1b23575a2f8c69d046b5640de7bf9beee2631e0b075ebfba8b59a02ba86c8098c59bb6b1b1c7a7d299327503b77a392d66e6cbbb2b3e0ec0c460e97a09bc34ea74a62da9074ce39f9b220f0cfbdcf087e9446bee7fe96e507a5dcd71154d0a3331957812f38d5384832b4908c43fec286322fc1fd20dd456f6d7de27cc4a2ed7b5ccdab684dee9deb3424e06d50b58d202b7e548533be42e3d201d92eae7b61662565412cc67b8c504646205e1879ac05ab856a373b20fa578229aafbe9452b496ea89fbb9f8be0e1f0afd0fc9ae4de64e4e215fc550d3037b836302a86b443f2cc7863cf5ee0a3653206f0f17f210a276f2eba7a8f2cd1eec10ec28cbdaa3f1ad94c07030d627bf53795905330afdeb96e1d9a730a053ec6683744d98343bc3a4c66099c7c451517cb12c0f6ea99f19b38560a2029a7c417af43980488621514748021b3fe3bffa94f43e04f7397fc7dc27c72f51c209739956b5bab30d717eaa5cfd3f54c9ef521ca5da0ed9ffcda38706a0c8de58c704b36bc49b73718ce446216b0db58939bfba710b0700a59c20d5b30a5eef16cb6a93402a0fe79aafded3604a963d0503f5f88c830b6c49a2aabbf2539808c11d9592bbd22ae8325e3df92d877792d98c23d3d2d403c4d8c546f4e5ad7f068ebfb17eb5b1017dc28d3cb124b48b6b57f82732785b0d66739f9df89f50e65db0978d2fc56c434b5d58ecc6ca42ed95a6818722072f0126b95abd68b33a4899300bcd2ca0be04a04e092dc89df896c0aa7a59766ea80d679171b161d2891b222ba09f9aea0bf427177d474b1665f1577ddf9364b6bf0d721e0adefc47be29d12d28cb1c272377b112a91e1b1ee23723fb729a4eaba30d7f9fb19b2c5c5b4f74a3a19d116e4311b53328c888f8ef45bf3b7120d594360ec2ca649ebc72301c3147b7b237ab17d728df6ee61626d774f3b2dc44b13c6a0e409c32935812a2365088988c162cca1d18efcbe29a497b26d8d0dae62a38610342aa96942162c7786dce1bd29f43730553f026bc19d2e00fef63e92132946b063a3641adf87d8b2daee2f3a626a5c8152d50fd733646720466a0f85e6f64b4046676d5ac058aac2d92c4351b06f7b41cb02e668a53c57e061bc2bb7730b3330a0727392fcc8b0911e9991ac1e5f34371a6e80ba739c7020e35d3861bce765e088da76fe0834efec965e6af4690e004936a04540cc5cd5006992425203d807f01a02c22f4de3583b5e20ef4e53380b6be88e04bb8eef9d0a24146a2eb34263c0b298d27acd1d4de5b9265f702e508127c2c31509b5260a0be70b0bf779f8167047d0199e179439b4245c66e776f1247a521e9dd592e074c3ccdb71d14f19a590ca19ad22d78ec469d7b71ac8d8d035d3c91909ec29a95fa063548da339e8024033b9a75a5f696f8db614de5fc41c52fed5866ed690766ef435a8b3cbb83fa880453f71790b704092f7c58d9709fd99120e5c8b0c7c9dd76a1f8cf79402994ad091f1101b2815a2bd5c3bf3c2009e50a5f462416bf750c0262a23346e1f2afd8038f839d72a22d2d76707818acdb82fe5328fd9925499cfdf3a63237a685b6b70182ee7d35b73ad990b78b4078e79234718436f59214054bf48b557e5d6015590919b1e3a102f3353764adbe56fd6f2c741c1cf4545cc00019bc676c234195440289454a0c237548ea16c93c44fbe882ebcb09a9fc12830a8d2ea6a41cec4ff503d2293b46c0059473d70938337e1c33d6a31560288f1799b94d40d456c966c206dead33c8f5a22bc2bfaf151d449cf50fcd6b656cd109102c0325e3797360b405dd31b499ad2918e57b3cbd71cfed3cb3bfb6672ecbaa7f1bff4503e861f4150710604a4c2e338f1be21d23329b98109c2ce28a8870e1f894ef66d7b9ef41350149b0333978b282c827ed95ab1a3699b80eafdf02c24d01dac01e9d3eb4fb7a043bd0d95152cfadfc6a2905e0b34cba5eb7acdbfbc29b6e87d4633536a3b47c067fdc2fbad6155dc5e673d930b3e5a1e5adf0871b8ccb2696c11dee1effc3a205db753a8c1cc0030f8c6f32a5bfce2f8a8f5970c0a06226ef092a126f94a0620644a2a27db8ba2c975e573654bd02f90ac3228764c3d2fc7802c58fd929a6170fe44c196be3dd62c51f354d4616dcda6b81f3b60b0588b3e3c9e011f81314210ea73f825201647a818f172f4a7f68a515fa23696132ddbac91d74b53c61eaf40512f2b30af9be819a01922cc8cfe17cdf2b6e6a6ce819b832fbd01d7b1265bd0e27c9aa03eb2a2e144c4a3a10caa1e49ba2cd50a53b926804f0a685295354710c57eb057870aaa024801c816626b8023dccdc1e769806662d813e96c0a9df9b06537b8472e2fde6f0e73e5ec53b5fe900a5f158f16d470f108bb595e761452c05156cebdcfb7345ca14ccd4acb799087f82cb64d8e1d754b290e8d8305a63700d53aeb36eb5ad9048fee8e1dea1c99572c5f24918b829157ab0ded3a329391f077675a2f6ca7b7cdd59c705742868eafb408d19f307343746506b40245c94d700498f7d8138b7e2a716ce509078b74d0c0b4c6e53a9e1743657dbc4b50e0cec087420c8475126abeae77b116d4a7c2ab980599a1671550392f2c3ce9b9f18990ef566326009f88980dd85810d845e87faad29d297d84aeb2358cdfc215e55c80fe346b027bd0c4cd14a9c38512831bc050653a3b0c8d0c6c67e421703545c1e0fd828a142f9f398041b1e7274a1b2136513d1d2095d1e474b6a6bdbe9ebbfcb0845b2c761d665af9cb1eec1816247cac19fea6847a703d0ff07b1f63debede20ff6d20468f7131c1e551ee0dd8dda00ca95000d343f8afff03f199da7a04ccb0ac4a47d9126a00063a1b613acd23d718efd7a36577135efc8420e96861c0a5b04749346ed9b6fef363fbb5e7b215db89b54ace79787117aa87f68109c628be703d79d3d12cae5ed54b5b5406bcf528cdfa24f21089a86a229e76d76a60414b20b9c71ffb60d890a2af34e2eb41ee33865cdf515371c8251f66c848c71b250bf02c7f13766e9d2d5f36642724bb43c04cd80686c21de5d590b7528fb55b9ace10cfa44d7a0a282960081ab3dae6778ebf660940dd0132f81182f74acee59ba3a02e4b0e28399d3bf67d29f0248857f440edc49a586503278e031262699e523ad02e547720a9cf62d9dbeeca8101a023a5cdf2db8b122100968e57249329d61bb09500b49dc4339468469172ff41f540d8ca328b2a6eefb7e97294dfe7bbb52b00809279a0460c5ba0a6fea131c7331caebfe1aae083e5bd2682fdeb7f3c16d0b0fce314f5b35d52302fe420e70448f149ec645cbf3b5aa69b30165fb9f550e100f88129f2f3be89a5f0ef0fa79b89ace98887cc30da59df2f532369fab4784910b159a6313291a04fe26097c2e140ca92c5a3108276dbe1cfb11562a6f6418a8098a3507607475385745c71700ac19ab6e3fd374d412698c0cf1cdc1b676fdd2060529bb6361b42d752431859fb2400dbb84cefbc3e39473b21cc5a49fd16598048bf4c8dbc6081ea439d83a526d6765616e0424a75b7ec26a650593b2d86c54084bbf5c8231c579999e2b623b9da43a1e286ce379acda2e4bcb082508ba61d505e88bfa1a41259c3dd05132c7ac7d050275f502de522193ccd367df677b95620c44fed895ab84a8fc01add0431103b08dcf12aebbdf3d7b1326335d5e0740a4065694dcf4ff7c45c9462496d457de38694eae6992d4a3d22251204239a1f10a038642b395ac95d0309d43b229bd71bde433354569ccc62c7fdb3947885f54c203b8d2b18b8857c0a8fa18709b39a6cb796f400f293d9108f2cbe199e92f8a8d06949b9dc77f2b5dfe31dd1d422c35072e6cf8ac3dc18bab612663bd0b0829ed07e11472dd8548b18d56346371b1228d39b859f1fa810fd3584bd3813ec751a302f9b7ce78ddbbd620e79c56c973b2820a65a2f59a0a781453dccf1ad77a2a240bcd8afee44effe2f4e8f47df1c272c4820698c5af99a2ec8acc11c7f41375a50f0a9157879e7a9ffe41e53e6ec28761bf27eed9918fc7ff6c49bda55967ae8607b0bfc5e8f2ee73d278df904f0475c9fa509d33cfdac31863b9441a31bda9aa0a3683556e9ae942199b539cb0162ae922caeaf54c728b01b85b3eb74db24bee086cb39bce7eb976e0d535dcee72b73b1201fbfcbcd288737372642c08106c980e66f337cf1ff4b98fb90886117f2258301d8898139663193f92713d79db73bd0fdef151354f0017883253e4dd4e49a1a3a13e254f416e2766ed64bb794c9c9206d0dee94976dbcc97cfcd15c4a1ebc8dcca025371607b3b5a8496ffa4bc5f750808ea656d960d0e62da6602ff3152d69ec4d5fdd528c4eed215d240129d378a04f0ff8fa946b0660ae2bb08d305c9b67f45111fc5ff76c80fb7f8128bdddf7a0ab2089591b08e143b981a076a19a8aea00328dff8ab96d57fe3bec75d429292036a24fd858429fcceee4f3ba75a8ef0d88e0a98acdbed09323e1291c146320602ef48a7dea6cf172d950b91912dfa005dc55b0456b2288dccfba6204f0b660502576c46012612638894ca739fea9772ab1df850e234f3e9906168c99e353dd00b1bd516fd8ee6e6f1c391e5c4c9769986fe8065954c9e2e3209d8ac2915e5a706caa3670ec0e17d64f05a4bfb192d1543bbbe99e988ac54060458b236a97d490513fdba5d5b6ee2fcae45558cc0fe2e42e38110022ec4300a951d679e8d43e6047b95165cdd81586ccf4b21e2f2ef234a46c1caef5942b34728effcac90ebac07513a305b0e8ff7230ee5ad20333aed7ecdffc499deee5e39b7ff2ed841b7010669cc2a41a3c4e7af69cdb95153975fdce72718b2e63cca6f316fb41ecb8fd40336f2783bccb72428ea626034febb2fbd1083cd4d36be4ba0a6b6d9b3eae96c0cadd672d9aea395c6168474387fa36d67094a36884688de626a3844c3e4ff1300210ed41eafcb2edee9731adda2df4bc3ebbd8c6c13d216c8d0ca4f7d7d314601c6863e45665de971f951c667d391513c4c1c6e292a593787ba3413d58d846002f245b1b93872edd2b67b3d3178a931f3c9c9a1ea51e5d65cfc695d43fe09bb09a86d33bd58cd40c818751db74b5398882bd5e222c6dbb1fc77464c54297eb2018eaa7daed06efcde806f8a63d811a69dc34b79a071278aa39202090daccaf10a12352074a88a1537a7301f446882137f21c195b8b97bc01af68b1e759479cd0a4b254ffa38755ae3ac7897d0f0b759bcb72cb89893fb02b1cdc6910aa903c30e17abe24a81c4ed9685cc65bd865e42993fad567b91feb103dae5a14b76fee50930d03e18957d1f9b88382f97fb10dd169f6f35d9d428576e1c82be4c52c276047d2bdc0481ae0f5b442b18aa897d857c7dcffe0dd59c558314fe688ef680c8072a99a7389021fa9972a0a43842d0c8ac1afd2a90c5d8ef7d76421c178a41a10e784df3d755213e619b9e336c3a2b567edcfa93c955162865ffc073b380d09c0ba9e89a8eb7b0531f345f27302dd22c46dc2bdaec5a4e0b3217b496a585f59e0e63d9b1f230a8ba81eca37be7d8dc51334af166123ac65527dab7fd2e0b6a8101d3eeeb4e85ef15dc137baa6bb65b8da4b4c19e48267da4df646ef7e2db9b4f04dc14b73b30da1f28f2cedcbe45e02291fd926c02feeeb484a91786306530120dcaaace7946699217d22a840a578380203bd53eef97cf38f54c755d2f1990870279eed656ad40a45d310b9e5e18b6e9adfd08c8eba5ce7ed72191a38d9667620a910431fffa4f65424ea6752be88beaf70bdd0d5a1976bcbb1f533681d0dba3069b08ae0a507e7fa9bbdb1b2bb9b5fff214182ef1dbb806295ea1bcf49b54cb0fd60c09335cf514cf54a7e42fab855ea5b9625f2499adfde5dd84e381ef0c030cabb6ea5b9a5402bf81bb54f0e686744b5f3496416f0ff9de884a7c51613654096cb98d93f3c2779d9941d10203cb030b085041cccc95f3efa0ba4561f7011a04dba4d622d9992c1dd477e88f2cbf14e697fcb5c9c8cdf0b3a8a2375f7a3696017ffa268081736394ce55e07e5dac27c82c167f78c670e90af3674265bcce46045eff17ce18e7a58f2b06f3192da486202a28fa413c4611f2dac56d7720ce330d9c6a679adacd366a97752036faf2e267507a7967a328d723ddac872d78706f0fada9dfdcd1c5c9dea541807bdc62c83304c9158242a1ffbfe6073b139357db0328febd7cedae287e30ca1a6458a1babb50cb68ee9809c8ed465bb82f173b080ca0c8310273eae82860cb3de2c80c11d1ab3d54a9eda8ca5b4432e983341c7c0236fd310e29252c67d4bf90787c6de5a72892b0b4543d13d98b42d4d3fe96f006c7ab20f57bb965b73276d713364c7ef0f6dfce22ce3405685ca028c59d96320162fe9542f69c0e5e1c764207f13748211de98bdb45dc2c1c58f4dd4032536d04d852327cb0eedc646c53bd911941cdf36ba60a09ab845fe47307524999d13005aa0d22eba605a918b1ea5eadb83ba457ae88988c9082f944599bdad6501fbb096702a167bb8049b57995f7a6e27ea00c5128e6ac4cea6b2c17b49db4a0605b07a799b65ceab58d1bbd5b698998d3bcfa2f7c68c4a3dada1ff17ccf5015b62e0d02286f4a32764fec810e635a19fb4b708a30eced8a93e0a16ea55b5dfab2b505cd1b962dc9589cfb1e91a0569dd25f900b3fbf84111608021493fe37a62c4e083ddad92ae2b9c89a8a0080b30cca81ac98a6cb59880b7dc2c52101305d65e3050c27ae06103014f71e5dad215356824c792e0daf9fa6699d407e6aa6ac55090b1ddeb7ec945f43b75e3238ad5cb6ebcbd6c32386e41c21ad64b903d535ab640c01f8b5f6e1c86e4b0dd650b4419958c947a4d65b2f6e106803b383c68b41f80c5c94106bc20e16598cdef540b86d5474e3eb557fbfb988398f0794174b04db0e9e2605eed8800cc788699a5a38ff48249eee8f0a9370007b69b426c72366ab0385f246a78799bcd061e477adfad597013ba3a7449b4c1388eab02a71bf230302b7869c3153bc7f295cc22ac3c08f2c724de8c44ed083b42e82e6e6f8fc95d605544bbb917adcb019b4dabc0ac96e9948204682ad68a21e0eadfcd8c78b4b910366e3655ee51f0120a4a08956e06c7ac480ba777eff2c2c59a8e68e278ad10b0ab997033b863d6f98e1d60e5f45b82a0a3d55f96946c5e4fbd29902078889860fcf3b1dccfa2b12e2d76e2a36ef1564d60ae7300088036871df99e129d971ee07c9dd74c09a5862154c5aba33100177743ff5500ae299df4099fd814ab3788e053839f97f5c71eda5564014ab35451bc78726cbfe586470e7ff9dad5e543ccd078066f4beb820061be19f3b90a2c28f1cfaf7fa2a768e10164d8286b40eb6200c07ef7d54fe1b9d01a5b13ed5603c23bbf0d231d1842fe721c220037b8ed035032b24b6150fc4b08a84dfdd6b33e3553c2c5f4ea272804dca65844f0763213a0d9e58f6e5c4f2ea5db0b01cbbb69fac65ffd948c79a680cb83c32f0042ac19005a7c8691f70215bc0fb66256b12f35030b6974ebf7ec4776e0dbf31b57e7357095fd12357999b66a163f0d13c518335aed1269c2233bf4b31cab1a99e62abe207e12df61a84c0dc7eb6033827fa723a44092d139497bb5d45296e6fd0ea66f20bd72fa2d10b2d0593240dab8e7e6ce8a3399f31713ecf4f3739ff7ef3818f97089c86c875ac467aa2e692c80083668936d57ce917a450182e7e23716d8e26a400f8d15d9dc687699dbb609f6bcd1d7d3fbf464724d312a7cadc8da361532d0d02ca4fb8cafc9f12368d921fe8f5f00046958afc5498cf2ffe677bc469996a5b0a473cb745eb78ef67e9535a137b4e9edc56de812969bdc4f8638c17db9670d10d01bedd69f58f134ea25a3bd1b5914ba7e8a1f75f1ceb47453f18cc39cc433a0393719e136ae6730af0f5eee4402089e539ee6bf9aa48d4d8c8cac263283b59034df229bab947555f92448934bdcd7dd973b1e3c8961e96729d93bb8c04c8eb042e9f66b951dd7a812ebbe071ea82e051ae270599ea660a53f914c0bf17a8d30ecf2185a9ecd4b5c5c4c33ed4ab11f933b58b8b6ba8725ed0c41634d5aeff4103ddfa4e8ccddd72891699346777182d64c388bd62ba2e70a22f99bf2ea3fdc803922180bacb525e4defe4df820f7560d4a1b401a68bbefe903edfb6606ba4ed0b4d72114b2b2880109a0f34acdbcff4406fc2a6960494f3e426e7e3f125ecbb06bcb17e0a773d3c7ab186608aaf8010a4ba2386888449ffcab6f5a78b572a300c0e38c3ccb879ec90e1748ef1f0afbb9005458ff79f79181db3bc4b2e5a2dff071fd48cd29e8ae7e1b9a9c79cd0940b2ab8cc4dfbabe1f69a25f45197fa8c3a0b98f158cfba48e06c32e96287535a4d986ebbda74cbf5f74e892b5923946eaa07634d6eb1964b7b0fc9dcf04748d99803f49ee0af93f6a182f6e727701d612809484337a270cbcfbb2a461b37fa6d6f2778425cd9348048830d5f824867e713062c67e3952809939dbfcc1cf5476b982f2f942c7b552b65a6b871d6e314ecec034fd0f187f342090d344162d7d052fe909f63a8183061e7bf116d0b24e6f98401f8459888305f8998f0540a9e657e731e9f43ba4e23f031fbea4edaa3c9e103097e2b9423c5fa55b55e25f572dd0b40895462c814b1424f5ce26b59755c1327036bc3bc996a4296cbec827f157409b5fd4abe66b4cda575a6665e3875b4761d0f6ee8af83097fbd2b26cea731771823a7c0f76bd88bfb3822a8c16c350db4eb02fa85578a31236c931da8f10127eff88305850b05a64464aa9d223f88a1ac68089d6fec6b89bc09b3464565badeb10be8c0f3359cf495e7b38c2b7f08cd734c03b7695f72f3969a9d43ba557770d711c53293150ac68b489f5164204e9df3770fabf10e751fbe5fd91944cc5f2723d4abd3db735a4b64754cc76df8a0a4811b0a00806393b551c96e8aef089a346f781befba276e70069645241b9d4fc69066073b7a0282a0b67671e3dae01a8dead62ca7057e97d7dab0bd4812366364f2cb0cf022f529bc624643a1bf02fa0d3ddeb7c7323e8b25116a43b8d6f69466431607620f3edc8de442e08b3f57f422f17268ee73fb4e015b39ce3e0a0f6b13b5770a331991df03e301f8f57ebfbad1e4c544816bcbafba7d48c6f0cd1cb9d71818022c466cb42e2063a8503770fac5d2cff6a17f68bd689815d4e64ac81f53f4140847732f8f267b00a7249a3ff0b150bd5a6e500005fbc664fdbcedd5b7990981004e43de4444fa256679090971ce91f62553765bd64dcaef79bc08867c567ed204e3b21d508c3603eacd92a832ee10271c67f0c1649dffc855af2c93edad31cc0485ccd49fe91d3995f60693a9182c90cabd13340f8111e848e528cef40796ff001e0cd0581fb511b3b97bf4bca28ede4d007a83ddabf5318821e6c0c62621310a9b1bf9e22b681139935348c12953b97c6b3ed8b41ea1b153829e3ecdf2d73304a628634972dac09571f8f86f3f4362f22a38a832d1a5fc2c6d0eb862c506650db2e8720bfa3586b226ad66d30d9376688c06c6cb8ff80a0db4873a85eb586801151bff58033992022c9a6e6585dc5f5097036720e9c3f42c669c9d8d3e260f020ee9788ea4af7ae0d6144a32f905c0e3b5a0fca0172b9f4a4abbafa6ff406e0946a6c763293b97959617f3b4bf1e680b9332a244e2c745c42532fc251490c407ba16ba7b63ce74f7dd72896b74e24b9ca4f2374442dac3916e830bccd32e9e059fa2a3629e3192864eba2fef486e9915b9f590d607c8d16ec1d31b66344484043404dfa239f7a6f8f10c659e4ccbdf991a6497ba891ee8c0066a4ce023a7880a464aea545fd55287a55f07390125d23566bdd811450dce6606e348d69c954a08d63856aaac3e08a1225f85d4f68bffdbcf36a7d79f1a49c31e36144d9e20a002e821aae0f48104e2fc369274b5e07d8a522200725a00ea77f39a7a6b07afd606b794af46ff0b4b459aa4b6515418591607ba0c10eb7dd6d0df569e94ec571c04cb2cee3591fae0702bbeb0d98453755a3a8281218f3114c6501c64bf13af3908f149dc89e427f7f8c793db07056459ee6bcb4dfdf9a18572f1b2033fdd04c9066671c25dc115f87d562ba433606906e80de44e0826eb07e740d21bbf4dd97e05cbf4985f40693e23c0d1211e47dbacee3a1967c16f5f3328efbe4db9924ae40bed6b9bec39851b5a28683599f935df322f2a851fa00fdefbebc4f4458de24304543b074df7bac5b0f0905e87ebe425f597b7b9ef231346f0d5570a3068c9370949dc854ed77514729ed20c1ac1e9a4bd0c6a1c1a9ae2dbd91fa461b375b1af0e7f78835ed6deea673d30c7387eabf7ba1c005aefc7da43147dc9efa491dfee0e4746784f8b888f464ab086ab7083b7133431f3feeca14ab23f1104d276252f0b89a8b030e3d0d9522497cccb756ca9f9c4366843c5c17cb38d334a84cc027d028aead4b4e589cbf4bb108fd84d9a55af240b706b9e72f015d56d278d6183a14addf907c3f3a779d2425e56b587a85157c70cffe32373ab6a2c63fb3ac3fdab0cb7658bfb9316cea6fbd63561a7625b1f372b939c07c53e1aed23a7ba92daae098e4e25e9ad12562f5a3793d5a7e3da02076fe56e933cc326de2a5672c83d840b42b9b249a73aca01f850c920b15ac80007b37937e2cbfc477665b043c332940192c221d62585be4a259fb514a3f06ff8795e515784016a196790ba492b7a380d0a4bfeef0a5377a393f499852e631ff15b8cb3848037f6de53c5880fe10eba0cb49603ca50a8c7915b68ae85ff8f08c3de6bc880f63a6af1624d51d72ef1590c17ce2b9b051c438c6a164cd07ff4ecd1d8c4eb1b7faf386139623b85bd38a60277a5b2771494712c020bf38a491d52b7796e74c037d9180ec6847a38dd8efc000fe6c5e22fae7e77e9505e342af245638657bb6ead043a5574b5b2cba8bad7056a353cdb13d9a39cbf254597f0008c39f6eade95b11c3093eb294db0f458714f62149b2ea5e45f47d3788dad6bde98391292c325869ce6d83a56ef4c9128990a6dd241899a18b5318143f339aaf44c68f03a7c23b66050932bccc51cba004f038db195f36a735297c5e6db4c1da289ac38859282a13e1a1f46f3e90cceb1c70e208201df01df87d891ecfd17fa987c40cd5f95d02518954bc23ad74fc8dd8b0c2ef5553f4eaf2c2fe5dc9a05542fb4238e8c57109a8cfdac527d1af17508f600e5e6aa45357a0527df93ebfdb2af0795c54b902a934812436ffdd816c6444e06cc1c1758a2cfe0a62e9a56f6f5a80e55f3536d60348422605b4c463670c04d0358d6d305eedd16d1dbeb072a65a0460fe09faacc2f68049ba55167d8f5453d0ca53d79497d0f2066e268307a86aff46860ddb7e8dca9a3c6c1b8c74a3a266f0b16d158cbd644fb570c7be41161d577425ceb6ed4a46b1c1408f8abfb8a84fe01112ba839cc863302d37b7cca5964993f127b9e2ef231c8e0aae339e01583ab03bfd8caab4b0c246cb25e65f68fe0ff17bdf72aef4747eccdc5cbbbb015d2ad0a35f356006fa1aca61a820b6dbcb2096486ab3edba8eb2930d11dfc0759ecb101218ae639904dc19bd1fce83cf594fc86adaa13b3b37d24567f615e5e8731a104158a83c65cf2b6da28e0e15d3e35c8827f6d6be1e09543c80fc38bfdd47c8b0e02c4529fe4d2e110836f432ecff65f846513bcf9f3312759c451426bde3085053afa5af4176a8f3ab6216f763482b6ce9afefb0c802552ca4ee83a8ab448660462e5fd11be375703edb6347e55c7cc703dc202bdcddc0ac71969bc04376ef208dfd865b1d0762e6c76af6afd4927c476c2334e0de4eb0aedd547286466a8e80725521bc0faa6fb3e487f27e41eba7c615117de989d4c1214ef7e1b77d7135307e963fa30afc877485950676df82d54dff6aa63bb2aa7e7be0638f12520042b028bc86782b021ba433e2203ea80477a8278cf1eadaabb6b13e41a3d66b7b61f0ac65c56024ee9b46a0405270bd1e35a3ac393ad6d72fa17efd515ed92c5ba47023a56be89425c39c0aa0e3ee9c3c4f4655984c00bc061583a3006682e98045a07bbb71e8e119451f5ba9a2aeeefe0163e69a6dd0dbd81f438c70e9291bb308002c7b103bf699b5ccc538e38316a165a0ce4f0b14c33024d4c705c3607fed7a1066c015ff5700ae1ef09a19ffa5c3617180827a5fa0c2b075e42b748270d18cc0b9180ce798842640f40401f86d2ec7511bf22db151cd98d12c6ddf8dd9d7cf1046ecfbc688eed7b138ef91259e8ec2d06c6226049aed286055677f9c2d2b9100ad2aa2f7a597e1eb884ce5ec761fe37ca3483e259c7d8b159121de8db67d243081d8ec06cbb011631829be5f27dc90c1845458b2434472419e0a7a04347c51d0dfeca95e5ab5ed96b456f75e312d1c5d590586324d19c7eb75ac0e2386e544907b9434dddd086d7383b562018774beae195cada8c3f50b7ef6c33e678f91e3209b056bdeac760f80db65f082e8a485c3d11f0f04affe07220647c87452e3834003917be321662e10cbfd098ed644ca654b7e8af1640bd87ea8fe94cd884671b04257fe717a5a317ba447fa2302cf2e3795e5f4037109cf542b9f40d04b166a3046b6c46ee5d363b6a864df7dc5105ec9faf08171ba440006c5c7265fcfe196408a31fd95ae13d7fa4e03057a337afdccd7aa573e290fce023b5c558b53b386409a263e32fda17289c832e3bcb40d3557fa59eae9131954726080509216b2c820c3e9400ac4d482a8b390ce13bef58582ce82f207d41bfe57dc766f2ed136bae08c063ceed2b0205f11fea72d1f84eadbde27586d01d11436b3c48e75bbc982b06d536862e4979745b6c751e725e0bb32521994d75cf051feeb1d47e03d0c26b049a2627f7548b77b06a1a562c4c96fde5fd94d8d533c79dc495a21ec051536e0a5f17926d9184a34b6162b70fb5ffafba77f5113bd9fe4d04187186e03a57ec012fa85c364cc406261cd05189ae6497ae200bae5b520008e5ddbe7dab7b371005a195faf4fcf3418247a8845cdb9daefb4abb9cd41b6c2afd75861067c0ff9b08354ebae212ba5ada2a9dfa85df1865200119f415871cafee7c22c54de7c6d10782d27da4f9c13ac03b2dd91eeda425cee41598dafa836c8b940f6232f19dff046898969f12695a402d769da658f5c9bbe7b6069e58b7fb294c1d86c0383c000b91909960e84144ef00224f459240c3d8171b013094f56e88e55f173214367f06b730c7f5196b80242ffd58f84b5e2e294f9d09c9cd82e9f10b9050cdf22ca909c88bf412cea1530c7e2976c1ad4be8cd80e6d8836f9ac514a3ef86bb93ccbb07484908aa9a099cd8744afa12b815d7b9a648437151a1c0a77885dd50879f51087eac64e76f95700b193d11fa2a3b52232a4db0b0d9ce667c148461dd4034d1085e902b66d297496f8261a22627eae4c64e3f75c0db620b8b5570e8a6b79336032d0029ded17eaa7c03fba906b172b28f4de15a423fabb9b9fcef28ac8a7fab06d9507999f4630b05eff7d26953dbbb4c7300cf0840504bb58a0d28b1472ec903ff17584511b6bead73a90acaaab5e0372b7932a7aa021ec0182dbb1c1f409c0715c6920d4232706c53c1b61140e5d88617c3f6ce4ec68a0d765267e70c76be0385aa24be9866ec4dd1acce0787d5336ffc99e8af8e91158488dc0e200a20e70aabe55999db2c1b954f8f476edce999a80ee167339bce2fd218396d77aad4df0ac6f805c5710c128fa0fda26614ab87aeb6408e435a9bcc38d56ce2577ea19a0c6fdf37dd2f78508e6b14a88f1551df53554ed1d8213ca3cea0fb2d07918d860c688dd62daaf49168ed6ae4e8eb4afab0c3d3d07e0c640cb7eb4fcfa5de7fe7015ab8e74b020c5f685ee939bcb06064f47ccb1d2a628d82f73ee7f0d1269143040a2b7c3b0d7948e490b1870cff983059f7a8c93da9bf3b2c7ada601b2ffa310dfb84db7e3a846e02493514ed900e9ad93f7e042d8cfc3eaec15ec67a76d1480ed16e8667cb70e418b6f8757536c5f8139cd47ec6a7ed1a935e66b0105b64920987993dee0086b59569a4add7a0fcac795371f463d6531342a7dccb34a14aa703e261530cff35b2b88c9e0f97bc49dd804b514b146e0e0d033baac90c524c4903cb4973abaff133fa3609ca92e388892b26085b62845a5fd01ee2d9f11681e209b1fce0bc5c69569ea8279736e03e98d363e09a527a4e3cb8e89f2b9dda88500a8363aa56289d1e1be4e4425445a8b458ff22a799f179e4aca40f16a9812e560c8e7244ecc3cb321aa68ee84870bde6e85aa78cbc756530a59ad70fe1141aa307aebe94f6de3afc3cf1a0f222013ab00c6d58ca2968bf905241e53a215339be051750ce2469f3984df0c57e8df4490b34fbfdd695a46f9ec3125cda1d52fd846300c25b7efe334b1c4658ce9167ce1d7eda841b2d31637145cff36be007fa440b3bd21da2edeeb02dba8697e119c64072ef0d7c7282df292bb39be3423532180ccaed81b7692c760938bd47b886b92b79856b8d458c768f0259a92d98c7937a0f42440b0e3554c06fdaae28ffa40364746defaa2310ea1abbb7981370b433f10e0a0bf8770b589bfac3706b59389321f1a8fdf6a8b7abc3e950f3e4c36c00e504ee55278afed0edae6a9386aec771a1802705315cd0d59b2b0b0be42d7e9efc09e9bfc79c2f6f6c6b3ac6def83e9dbfbbf87712d317c3b2679f796cfa2d9266036b3f23a4fee5a2c84e8da81d2a41da94fada28b2a482091bcb4d789a5037910300ed2386cf45c658b459f7937b66d3b3e6e384797a4b882fbc42d488969fc007cc70a183828649d6832c4a63e782eded802f2b9541d6818aa97ba4ea7383f0028972ad184f699f828eee4add58648632d44aa24a1958c7996e14d6fa1a56180ee3ae76cb9856d0e883def0879f3782bff41e27a0f6f075673514319afc9a380fa3a131ea7dfe0ffac8ae05f362b75225a96672e39448f2adbbfde907e189b70191cc25dde2e9e9369fd7847602304fad54106391090dfea779e15170a449a8087efa5533f05766e83ffa70771cfdc041646dbd4069fddd48e82ea7b9c3316a0d0f40a91b6839567bb5e8758697133e9712d44f6efc02cbfab48fd7dabd6bfe0e0cb5c8f25c30149c75b708c7ff236e02ad8048e01ce42e019bcfb55fc174e50e9337fe6f29561254a5ad40ab8601fb21af5b0dc2992ceb867e5401e4a605b60e053730115305f55735913c6e5d1d0b60758ca7e00661c92a78eff3332a28a00e00064d7df5e4b118510f75d9b3f2408bf9c4564d1e81ea82df032d0cff63660a3dba8b51f8b82fe96a0849e4bbfcfb2da4a139b016c15797937476b7bd03a401662d327d82f364d3c392da110ee822754e9170c4ddd786987143430221370e0c1e11f615137e5631bb1634fdbfe0f1ee819dfecf45f7e62b4ec6f4083b255302bcc1effd7f0b7c0bb1dc9cf4e9b7df9ccb358f8c6ee5b96f6ad1d3157a86d901d8a09078cd4f3e3c7e902c8e3e2e0fa7797ee1459b97d65b910c5d5aae742e0c7551d1d9ea79dd7556904adb836f7192a823998454cb167344b7f98aba99ad0b2d3ee0cc1dfb74b3bfef40ba102ec3bf1c5997c9c6f7e1c57d4c8cf7010e2806567b50c536bf602bbddcad53115ba26f68f9553f2d5a5e8846675ab9f3619e09ab694db4956538e3033b8987e414deff692b93a7e35edb3ce4674929521bfb0e8b28460c23babeca55dcb44ad477faece2b85837c4259a601651145cb769d501fb91dabb276707afcecd2a3f0fdcf957e70fe2f7827f862d4b436f5a4a14e407d7c8c4dee9e7eebc4cd21da47ec780318524e11bbff77b9ac914866deea78d09d66e80cecc197c0d77b0ff66a9fe197074b7cb28a3085556dff755cb7586de0e18edf965f0b6fc6950c1e026bad6aec6105332f0051426080ec4bf71e4da06056de0fe1f1990152c335f5d5df3501810ebbe4a5b1a8a440edd80024501b2a102a0de054fb29d476416f1b36f921e616ef2b6db3009d09c2fdaec41b157c1d10ccb1753386db1a6f3242c58a3134f5e171257efb4c43029e2043a61d5aeb84e0b67b7d82de63c4810be2af3004d01867ecdd5c3f96da4739d7ec5099c4174060975d2500678d89392da960466af18282924574db9f92200516ecd7734bd76c40608eaa326d10017c9b0984e222f4ea612446779816ab1b54268cd44e42b38720aeb49df59417aa1f61e51e5e8b3a72f62f23cba84ad2c3d5e02026d8ae5655c0d6d3617c1b6575193d26c1fb3cc37c99be9c8ad2735ef1c86078b1f6fbdc16e0c427f52b91f0d3bd65f90b56195df9d7429969b4b30553eb49a9fb1a1feb5750e5024895a129c4da56f1b455b0baac80ab15ea7673b8c138779f5c31be637aa0bfa49a44f73b69ecad77d3540ae6b5ed27173e7ee27b433769951b0b654fb3906c816bb7878600fef36bb947ffaf3fa7786c81b19dbdb7a0a94983ec31e20d00658263a56222c75025571d92c42f25179393755eb8111d22dc9e868e89d30300d0afbabaed70bf4608c1bc51cbc280a1a423032c248120098c68b8fe4a1ab4d0bc504a2f7473987ac0cd32724d4ccaff1d31c7abff0eea41875080faf14e7c208755146cb63cc3bcdb8706e51b2923bf0a36e20e748fa0fee91c08cd894a52102f1be86f0487f471d7565094851cd08db6d87ed117abcdee928cf05752f4c590284ca88fd4f714e614230b4f87769b17f13f0c3f2ebeaf720f13563750706e00efec753c3e0d4eeaed3c283f7986aefb99426ea269358c3ab9f9e6dcefd751c0a28bb9b2152d5d909ee92dd5b8b168894a19655867bb78b3c4d6d7dd684f91504f525bb4b0345737dec1b4977fd0b16b1948d1bbaa7b6578c12d20d845f8a9003411ec3a3b8354fa300e95608afa8cf0ff83110f3bed6272e9da5a2c1d0366a053f171e927d85fbb82b29683793a32a97a05d06f1d87cbe4552d588ce08682904558ed8590ecb8c83c06cf6a3268f31a934cfb6a2b5692bc5c2d0b2d3d0e4a903d2e90776f85473fb7346369ecaf9cf3ccd887d4fdd335ca9c47818aedfbdbb0cd744e78b96c6d8be845022f5fa00df0f414b1f17b24c6d4c29385d6e37ca7a09c7ababfb226483ee4fa47cc2d76a0d08d072c3536de062de8ee0c391ea908d062b5114501649c58c7625d174e25f73ac43ddfefedd31bb1adc3bbf9e9679b40cfc294c7b35ceca3b127c1a92d5f567dcad9edf8f2ba6f4fe9ef3cf0bdfd782009904de7d1dd087d76dab27322ddc6d9c93b1485487c05cda9ca83120142a1e02de07cd39173b4a8a866ffd8a315b963fcbc5a9765c9c8d8c3c33d5238424d20ea908ffa889c92a25be4d4ae5005e820b967fddbe882a16fdb0698644f1dc7208b39933c590ec318fa129e19a35a6e437424aba404dd30d56bce0f9f659a7bd062df9a0d680af8be39de0f68294839f3c7b5cf8052b9e492af136e78cae102a02d9c1daa7bc74a928c1158aa4bff5c0340b72cd3fa9fafc5b58c4c2bd3d727c08a77db5bb0c302ded72a041b115d5e31943ea4771134f07217a96ef60bcc4bc08e5f2699a8ad3fd1dcac74d5e22d9f597cd1769ba9ebab19b3ca9167ed60b770cc96647d4bc717cf461bb2d438127b8c2ffbcfa2b2d846c10d6a66a197ccc7d018aec1a271b9121663eb9adeb2d3868a8000c1cc0f2e47e7469e14c2f9693d509effb86af07324ec05725ae573336e763566491849a90741ba1e25c95eb079d048118a2c0fc5ace9ff19d86e0fbdb306e6901e37dbbb56c64c7f894a8ab3dd609fcc030eea8cdd4c1dac53353efdaefa2ec10a4e4b3c01289eab163549ab51c00cd66077340c93efe517921bb0176b87a40e5b59ecb0e6af08adaf06c9b479708869f833da6164e40dbee4379bfc90a79475575a6e740eccc4fe4ff9fd50dd00ba4ddf58012827a941c1e999466b5aa2bb2b0128cad7d533b0c555742dca24a0801700a264bed9cd85c96fac12d3b196d54937a9fe6550969de8f6fe89f6a30039fb0b12fe0522003fc8f9fe9fa1cd2dd8374e1cfb5929d07da3a2c683a02b001ffb6a056e88fc302d64a4bce1368da0d8164c851fbd4c2126604e278146de303d8e8cad3b4580b26a1729dfa0de6b759517ca0305b9db9194c25fa7a1005ff0ddc2d2fcb42c55ab9457e8f9cd69702de5a032186800bc6b302f0697f191e6500abc1316a2aec200a8cf43474310d1209b9e2a77a8fc41b92f5e66f31251dc90a3031c337798e91fa43e7e7aac5402512cd7a72b99941d32075a808bdfb3d2b02060011a8c9a6637e48e7663f4f0cddde58a41477971703109c77b3456439a00fcca43a6115d24117989b6daafe6d9db2e47568eaac904b24a9fc78b6693fad009cf1de9e621c17ebe947ac6b6d22c1dc4a9ebd2f5296c58eb66626f8521d9b081477ae5356c56f38d7bf81acc3a74d1013671f37e83c669ae83759dc99dc3d0e72c516ecfe5da0ea1321a762d804684b85760ea99d1bf3152f68aa757a1a870faad123106708e7302cc4dbbf46a92ace74d42727b2decfb5e707a7699b7a180b66c026d9efa39629380c7b8610d602484ce343a774d93d473882a9009bec440c64f598a0fd9141bbd9a5c6d33fbb881dbb206de87f11beddd5f7ca7d24c11b076df3bdb018b6193817be37df4895c3ddfe5c6f2feb320f5963b9796e08ccc10ef13791db59d6fcc74ef33bb13aad8bf86f6ebdea1b475b47d976a742f1277e05d5e6ea19aaa92d49a6f3c065cdbffadf8c5d418e8cfd09e95ae9da55a3b7c001847fcfd394b13938e9891cb6380ee6b55817b8d1b036204b1692c159e84aa709a73cdf8b44086b472ca9b8039d17cc50bd456d3fed5c3b81a55fc7191ac70e04479337df38f4b59af505d386bc1510655538507cc24be0c55273dbf9aaf0c401ab45d372331f4aea4a19e81c3ce1176d37f14ac1f2fb8985a71485ee70252204c573dfb1aeef5bddce825eba8c1bebff907b2bc31f0437b9f60442de809c1303dd956f54e33886d85e1c32048d41a670c82a5bd60934a8acc8267478d111050ce081e0c4f56e47db9a002a9b083f562b3ca30b232e00676cb7341f49849fe90d0556033191a5e7744ebfa7796cd1dbdc070e18b1c001236936d3d4e3ab34a40f026ca382a0724f1fc6a95b862b64c86659a7a69b1343b6d322adc13fa5dd40058a28e11681986eba14314020415f08106c1b050a8de8200ebd9ed0f8dccbdc00dc07bc0ac30a8e5323dc79b7ca0351fff248aee1081b1ff8a7ab7f5c8acbe208096d658b55cf4fb5e251f4127d8311ee0958fbec0ca480dabf87a0d722b89d0ba8036625b6ede04229a06acae5595abbb0c5021c4e616c897754dc53c255ca02f0f146ea74643c6fd3b08ca7828e967970ebd3947bfd11d4d47a661c5a2035043b2289d14be2551b6801b3629159201cbe3d486669c39ff779b7cf3c44d57e01df988d607d02b83b3cf85c10a22b8a9cf3c24c339681796ce6619d14dbc49b05d83a54300d70cbe9e9cc5911f2052a757fe0ef97817850bbfc16fc9bab7a6000b029c38aa84cb4c79ec0d33577ce30434d0fa92884bc56ccb21ef67401ad120a5b0da02ecd25400c3b5cc346d1874f294c391603f17cce878a80469abf29ad01b5d929920a893c3c547030810e9c4d24395f41bae6ec8a16dd1549d0c459fc038b0f125e652d5d5cd94f43e02ff9de74c2755c44bf7692c35c0ca39444be180c73abd465a7ac02d23c4707e8c62ec1e30c4bcd2105eff2b097d85f4a236f660e078f7826f00a687023888083ec179f7eec9aff8dd02a74bf491ce22f8341df065ff91f45acf7235bcb9b183b269e2515ce5d880b45edbed5710e1dfad27e0a01cb7c594dac5f296f02e06605f65f57826b54493dfc5e77b8d2fca904e69a1b04030dd7179777496bf2e0ff75c2b0f257707310e8b4bf3e49e109f0f0d844300ebeb659749ebec82443b087400c10124723fd2671efe566866ab2d0527d21c50b187d243cbebcc1abaae3a996b5e1070014fe36b7fa7212eaa98913d98a0bcd043ed1dc050d4fb754c66d08099dc826c1fd633effcd36567f896f474f1ccdb00c237d2815a0405e19e7d90496cfd5c9408908959407e88fd7341d03b6aeaa4b05d1bdd678fcc2fcfb8c5e77386ec349a3c90f4cb58b6a345bcf3ce6a10affe6054ab18c3033d0a41f27f9cfbf41c74012c54796e54639862bb50b883e40558e0d false -check_ring_signature 3804e3ce64eb4f58b5facf8f375a748e0d35dd2e9185d42b90f731b82e8ce1c6 195e4b7ce681e7e21c4603a9bbadc15408b9f965d7270519dabdd563a67d0fa0 29 71bbda634cf7b538d726660fba110396267882fa940ad2f6c4b94690852b8a3f 207a0efe9719ec436e68f714448984369ce497e1548ce488f07946bf229dd0b2 387e4c722aaba0e4db2b3405160ef430f028ff87027ed9ad1c14048ed6cb588f c0c6a911be68886a521dd28ad44617ebe57ebd7bf5bfa60db098b8a13cf8e470 a72536c759a5661fa5b4a164f46301d460490cd5a85fa1dd0239a33c7216b5d6 f942d45f258a204109848e6a314941a03cebe9489e5fc89cae8eabceb0fd7586 a671719b289a2d52b872bb9b2c1fe372399e5cbd76fb4a31c7801880d224e0b7 20487a630f755b34010987328f96931acd7d07937319850d540ade86d2c81e9f 229e84418c0061421ace44a385565f4f0ac5e6f3f96be5e2e1053dd2d6366005 33c0255e4f1598858cc87782470cfb9fde9eaec3980f0982b2c82bcf345c9278 c841e7cfb1c094adac56771b1ee15f2f5f1029cbc57c3cc558a13d571bffe0a0 4da51e519649c6b8f9bd3bb788fbbdb374534c9243c9c3f9d0fb61562c4ef2c1 7c58398abe4da8f286a294aad4d0b704da3facc37c15515c478850edba9a2f06 4402adb3b4c420a38e20a2bf6fb2c8f330d6994642cdf3a9b36389213e1b4244 e0481dfc836faee67c461d86e6de1f3a0fa08cc018519dc83ce9f2510c99ee20 a13c0af7f527d5352a68053b2510a228ce355bf02d578cf77a148dd0c24c6d50 77ee8703b5d71295ce3e93c4da0da34d50aa0e5057abb46b61f2d411564e5cde c8e5b1efb802c7ab87fed78ca4adb41f42bbdf674354123ca9cab8200f166244 1b8c0d358c8f6343b3b59d4a333bfcc62eec6de73fdd343421234b04c50855f2 d7cef5c26c9a12461f56dec6cbbed3b6ab24f959409dbeb100ea432e9695744c d61155a9b6b525d0ea8fb431996c19be0e7e2078d8751ae4301d1e82d943536b b4e2ee9e337394bf5e411d5233a47815299370abb3b015d477213c13924cddc4 6d57bdaf87bbc3d675842677d682da1a5e9ddca389104801f1068a827ce73aab a70e5ce351e45b605545d2397a0bb1a9a6ff0172804218c220a4ca21683119e7 6890483c8e68ccfc0f4cd13a0db75c62b4f04a49197f689d59a8bced03f29a69 526cee9210b1f327b319bf488fc6433bbe47e9817624f66861f01d99e5848960 ac07c601f4e5b92dbcaafb70e85c5412cdf4770247e7f6022ad5bbb2d847b051 c59111dcb145a224822fde8edbcb005887e8e0d52beb0fd9c6f5918c1cf0cf72 2e8cd6379008717db437786f38c2a2606c6b79116d6b1414bbc4c39b24429e01 5fbe4edf6348b536f219a0992855c00554fe8c4dbb3d2a05cf22021732863e02174373a71c26e61b80baf102c7637dec56e05b07cb3cf086b26aa9ff96d3b00be6abb2e3f44b75ae6eacba07064c6b7256e0bad1a8230a17005f075abdb503093ab6bdb6cc84a433cc414945697a829b9ff1c58c5040331b87049dc0641065016add3cd08e2d56b770cce664297d399e71e22e0095d76d7e02001c8bc2d7520f354ce448a3c2d8aa07fc06e0211c70d5b5fd92b8f571d9fdfe8b88d9794e130b1b17a71a369509d217f4c3e9d94843d473901249d35efc77a3d4ba5c4952420d5fccc764bd0e6b949174fcb570ec2f66bfc85183ce4ee51f4dc5cf9b93bea40bb43ce5f9a1d29389c4faea616c94697aeb5326f7f6b41fac204af9f902fd4b03da01e06722d72055b286740c432e7425cfba9078d55074816bb62b20d7e95e0d13249047666b2918e01e51537fac8ecf05f088aae586741d5384f2799ae07f0f5ce5644121f311274b140061dc2c69e61f1d0caa3dbbaff4bbf169dede09f0059d9e5f3d787c778113f00f5c8ff852cc2a4fe999f7812a893f927954962e9c0e70480fdcb13717adca48be3171b0fac8d3f92053426a1c39032e03fffa7f9f05ab14b0e9dcaa4cce32615191e5b51fed7bb42e6ed136f3b703b8c0d2f6b7e80d6a9b03a1bc8db961d691a361c3f31872f0da5879b791cea60e500631a5da3c05d11ee883a33b7a8f8395727f36665ae8a951686889f4d7ccee8a2344c9ce290c956306fccffb45706143cbd1e02f30b7cdf8d91b7d6d8fda127f78798642090c17959d33d60e0650fa428700c7541341c847b4116c65030cdc81eb6ccc990f0ced41ecb71d928caa5e3ac7bf8ba2831c8973ae39d09fc854b28d65e81a152d0bab30cd01437e3b0ea91b8b3d06c541b2fb46def8703c34e738fc8e90f9607905bde81b824b522f3f9bd552aef98fb3042dbdde665e1963f1e5f88d59f864530da934a0870055884dc8fee3dfb53aeb06cef76a0f956f183109186818695a2707931ef86c7f09ada808a204dd47ebc25929cfcb5c4b9b1ca17830b512fb1878000fa1effba959d2d0b9fca2c9238298d417ec5b837d3dd315506346ba33393b0c901d506bd41229e7360dc4d4a1cd7fff5497276febcff52fbf1867afed1267069b0438e54d6addfb7d6ff6fb956eae53946ca8b8a13085641978031c6213a50b7f9a6434a8259bb9d429e5246e9a700b01ef32242bbba2116c12008f64b66001c93f015a158e5eeb47532d094308651e9f154871a868865a0775c7b24aec960606fdb3d5b3cac67ecc4913495beebe2c44460f8e38f5e66fa1adeba4b583d90713565e857da6a3bcedbc60003ac0d1c5adca5af4cf419d7f9cfc374ca8ecdc0781ba0d50672c199615bbf756858b4f30b50b3c8470ba8c2fb64f555e940ce50fd3d45e14c69192cfd82a0cf1339fd21f41c19ef683270ae880f632e3b7409e00ca22cbcc2d342dabafdc4d4920dec2421c9dddb087a57606d785670ffaceed08c9842b43f71db269f4a5181d26a5ce8eae27ea929b4a1c4ffcaa2aa96c46a404b2dea6815d8b6acc72e40e9816bc92b9bb4b615e810e844118350aba64375a0ecf62a9ed8726afb6107c07761ebbe62314b933b62d699ce9934469a98291be0dfb15703006e292d366fa3a6802b9e1ae7c49e066b409863d0a172cfc0887720356bd724688aa203babeb973e8ba191b70a558955a89ed09e4a1c56d1a667420c01ef68101de93abff70ee04d31bc619b446861a54a0def9994a76451ad53e80f3eeb5927ac7fc7d3a059341c52201ba5a471819b2be6f1616a11ee03755c7a0b612ef35e6877cd58093010310751bf8e4b427bdeacd10bfae6a6056293a7a20f59ff02c0f4e6c2faf03e309ab77d785909dca2d740c5b2e440706749c4a7010baadac71f9bedb88ac28b2bbad95509c190c6a0d9aa184d04c08258c211e5320bbe1488b2369da67242d9d3d197b21e9d017a5604baa8d0b8ef8201cb014151076245320e58cb040b1efa6f5ac5b6ea3a67a0f66277f234ddba141bda79cdd008634c922399bc265fb1123d712804b6f3f3ea4083b48780881a41835ff22fa9069ec431df4938e2afe6174727928df3e5c7813b2c9b34457152404a7d5834a80d337d5af24cfe5cf74d82abdb5f360e757417628391cd5b626912e1d6762d140fa5c6a2bbf63ca0ef7e627734e36cfbca16f390187834c19d7fadc979a04b8002955717fe583064ab4400e72921d82d39de259ac3bc65c1219401789519f525045cae7109fe009417b1278cd11ba2f54e61475ac7f00e875c26726d378f72f50d8734234d817263e4b6395118de186b995ff4fb34e895b24ff37bc66c6263fa0ade7f64df12571fb473fee2d8476dad9151216debc056277297f6c089789296092c5479f97ac53864cf5414663597cebac4ae14ba1ef7def0a0129f85bd168f05fb837ca8d7246a9ffe28ed80697726f5d1ba4fa13312c133a9e9a496bdd4820c32f2d77b8d013aafec47aa1f6880128fee60be25ad62a3f900cf24223dfcc10fe6269756aba847c71c1cbc9f0318812ebd8855a9b5f27ae951e948480be4ac01 false -check_ring_signature af2e9ad3bd2aeffc6ac58a648662b3aeb5ddc61e5abd770e08ca61716466eb23 433e62715a35c25551939e8cee7a69e88bd8d96d60556225244010f085ae7798 5 5151a9ec3a7dcf8a805ea7cb6c3edd64e65a2c64ffc7a699c180f0df340e592e efed2585560c1d04acf587163d2e2c7c98e397f51b095f67503c17f3c4872b96 5ea18d3c7c6a30fa07db81a9abaf5235f0cf5bde9107c8f2d3ff7f357ab6187d 84e99a523d08a57cf7acf57167249e61122b9abe316fe253b12bcde49b8d5093 ba4e4a8c07c4b330f5b33611a7401a60cbbe1bcac56cb981c7c16fb5f02c979a 6152e0b0ec233982cd2e4c3eb7bcbab0f0fc2d4c51a95be53f237d375c2c6b0af4df55bcb882bc3e35d73889f786682ccc8488bcc730a95bd5da4fe824e0525c6e007d0361ecb6d741ba8fafa14b8e00cd1bb2d47d00cc513badb64daa507b081601f8a9b9df3b9bfd34475976bdf3c774679cf899b7bd7001a6db640197be08d77bd2ec57e0a5447ea2745ed65af07a13f7e69b56d2b639eb5fa27803dd88762b6d95be4cd59bcffe8eed827c3afcffb9bf7e57980054923190f1ed3cd1ed05a18b4e34e6610e64fa56e2f6c157b6b510618390ffd39622377c3cf7b24772ee78da4f0cb75b8d78ffdabee3e706f41a21e76fc88789e4755c4a695bd393c9019c9722a0e42a7c3594cd7a8a4626dd42386b9813786ec601fbef93d01148df079fa5548bbd88f2a1782bee60dc66919c6401aae0f404b2010b2d43021abeac06 false -check_ring_signature a75eb160fc276c70f60d236908b9c8e0067a08df5c722a38010e047ab7196a1e 156e4fb8de040c3b5c0e5fa2817ad2eaf4af27b97d207caa5d446e4581d6260e 60 53b0e89a52129c04adcd6956ab360c8bc844c082926a85f3defb59ef8c02bb63 c6b3e0a60cbfb307a5c10e0a2b059ff5ce48ceef49a9bc5d4ecd83eee5fb1bb1 5bd9deb979db6c4d3be05c5225871e8054bc8a28986fd066afecedfe3367ca85 acd1518c2c5e5d9fbd3e74744639df1d917cd2efc93e8b2e83d634312af9e410 2d4e97d673b28115311fe530fa2b6cfab76fc5a1354817406441854b4c5f859f 4c1c87efab6663b76f26725aba2f80993a6f80398e77872e32e7c88bc94278ea d0dd3fe51d87edc1672b2f382e71d9ec34929ee4b80dd556cb44860954815738 794bcb37fbb489cc14a87af0f0cb1bcd045f9439e4d2eb76ad3ebee0c1c847fa 0b5f1f6ca588df86cd6abb5c9247ae57fbf2d7d8b5b118c58e3a57f60df8e308 c3c5348ae574e176236258281638e45485bb2bafec287abe40a84e8a528b9292 c0f62a8f92b8fa5c74224571de7d0f67dc8eec1c43e6fd94810deff46e5961e1 53b8170327a7addb0655fa6f1d7300c5c46e1ad46c418f4d2622a91ec0b86ff6 1163a31a8ef738dd748725596cab127093d1cdcfbe0c2892c4ba79f545dfa187 912389d8c9afecfdecc21ed4a1e2f08c8f74f08cd5acdfc5ef66693db73328b7 b2bae3cd2e1a3adb11b9c01bac990d50d7ec9619f3be3ce6f2681f57c193d3a0 d6dcf976b7138c4108040412d397a8dcad97b05140ab54cbd6744887d2fa2291 b9ab43cd88eb8675fcd54ff1d902782df47afbed2d93a247dfd59ab29bccf5ca c29df281ddf809fec2bb6431e2a785bc4691ae91ffd86903cdb731ee10873742 d83a3168ab6580720a21593ac82dba8cf8caa46e90c48f24d50f5d0d5d89b9bc c523bf72843c7211592264cbdcc6ef5db0380429c5a387abfaaa8a5f18245b65 0bdd52cb617f4f8b145987e1efb0b464dabaafd910980db6ede238af9b2c5399 5bdf2221245792ef50bb7fae5a61c0f81e823117b20884fb7ed073f61ba8beeb ad758e0d90301b068d5199c9fb88153148dd3fb37e2ffd564c80a9be581f85b2 253931b7b621d6995d870a5526e77989b26a1c29f3f0fd47a5c9c8c91b65071a 158cae11092d4e849388b96b7d065ab96d760b1e466f8b77095fd1c4f0004100 11be599e351aa2f47479bde33b75d72c8f74c89f977641b4550090fe504b2797 c3a69c21725504b87bf66428d32845e21782e44c4c6677b895d0a8e50576374a 13f8ea5c44026efd1b5e250aeeb3c6c8dfaae337696dc95117ab316661ebd957 8d2acab9f3e7d6810821f693efe9800b17dcb80e5f078485568ccb3e795c773d 9bdfafd8e061f8bf20e4ebcc2f14144c208fb17ecb25942fbb317193d1c8eb6c ae06af3e4fd74ecd95ec03ef9be42a22a0d72d9d258e5e0d14d54d07e4313b04 40a498f7ef05547debf613790f686cbdaee4bda1eddab45df009ebe0c05334c4 aea2c0da7f562e05364b8f1983dabe35803eae0a4bcecb1e004a1acc89dd1cf0 a853e2ad8902cd2bcbb97e6420f539d9382f51590e58bc4ed4f3fdc3871952c6 9a93d1bf87368bfa9d0e5f620a09856ac0b7dc1a066ca373e7f42ae4f377c6a8 38f14ce961bd77b5b581c4b71c13b1d39f1f489b5d3ac6ad02241c54a264d9ec 1c32a0c428464e80bb64f8ef8fba9fdc1f44263d3bdfd25195d8df8a0e502f95 a87c70ef8b05569db14e53c663b8b6c750f37406db6459d1921fda2caad11baa 30da86cfeee5ae7a94982d63ac6e97888459ba64e2952533bd571b7b8296371a 70bec255729b4566433a45f62927d5e1d89758c6329cefeee80f7592a6b1db97 e4d422bab66f2e8c0ebc3227b5de65db36e05f6c94dd7850f5dc937dd4e55f35 433bd72d7f0442c39a19c7a5eebe0ea8bcbbdc2876788ab9c32e0ad2f7a75cf3 0dc71cae949803e5a3419eb307a24bbbe7fd6155d423b1e0e6ccad3048fa063c 1c59203043246735154fcd1d5ccdc30210169dbef0fcf894713869f3d54b5359 564fbee60b9b3197458cf48e5b0f14f0ce99d5a984b8e0dfd3f4a115b5c4b69f 0903a0f14017670b22ec8468853809d9304daa5477b8d4d59b51673109271a5d 911aebca824070bfe53aeb4a9c86462e59e81e1cbfccf45a9b4520facfd2c3e1 b6ea4e0d690495091a7d69f703a02ce5d5e782b477cfd2db21cfb045185b85b6 6d68450c6308362476c870bd54070d4878362882ee97e44725488e1a5502b976 8bc364e1e3bceafab76f2a75b614aae210869f65b5930bcd42ab03814d51ad8b 4a29c937a942bae6f485c69db009087bc5af6076870c937ec692af6d6d846f7e dba65a71d7a17f14d02275f5341f613e47dea5920e596ace5796fc3a0fceba65 13f012e3e3c430fe358ea0488c260003f06d3ae733220c90f716e805ff4bafa3 937cd790897473507dadc8b3d0e9bfee291eb15b8219806030a8dec8c4a0b364 89ae25780e1a78d22a6dc4b510a9002ac006eb5a4ce47ba965e092f0df69bcff c28b099eef44aba22b4bc828ba0179c54781023028beec283b9d44a6ec5a25c5 85bea60de9e90819bcad123b4298c00b6a313064805760f669da67853ca8ddc1 c24cd57846f1446fc6d025bb68ffa63fca4251e881354df6f4f9e56e0b47af70 5e55e5f957f4fb9ce456e19538f4866c6bf0aa0652d326b129d21af4ba129019 d192855402e32d8e3a7f01cd33d1a772fc58bca4770901ff53ebe87e98cdd79a bbe473bd16e1321812938de82be86b8295ae3573fa9f9b5939202a7df53acb03d6fe5e7310081b13aab7baef4bd9eb38f77f5e62d42c7e949e660403c77cdf0095bae45813048a4c31a78728a9fb15cfaf3fd4f003b808e19c52e77fabc0a40757201cfd853f6b76793dfedaeb4e832b28c66b36f81c54782875e6a812122f08a1668250a57828987674a10f273d0cd247fc2a72b6838cc2564ca7277634350a6472b0b78312f2ed5ba5114035707e702439ef9f6b2dd466a264822c1ca0e6050fcf1da20932f59dd15f747a668e00c275a828821f140e8dbf45ccd645567e04e8028c248ea852f83b292f9cfa87b00a2a8bc112001b1cbb0fd89bf604e3a20e591e420a9d9877e29bed5b8f6294b37fb05bc718242a92f5175ac319e8f822023189451cb31807654a0c2e39b674735607085c27980b4bdb79501c3df18bb9030f4bd5274ccc533bacc21b0191c64d4d0646f0c29a7b443b2d1a809aad3b9d0dbdd61732cb94fb240e6215c01091381d687fe36b1d9f1952854cbe6ab62f50075d86740600be32f09e2a8a502237258e9cf79ff12c82be1cd56eb04cdf7cbf0c71afcbfe244993f73b472cf634034b3fe8800677ed2259a4a57debe6f3d1590d921e12e9133207efd093fb7ed75a12e18f2e02a94f683063121c58f16d9c6b087c2bd5b24feebd034cd9d1156e25c3703b36cc97a2ad362f8926f891f8e0d108d61002ad60fb39cb8c529e46bd4c854b6b62e4334e0f11f50d31049aeddded0a6a0544a849493dfdb17a0908de82cdfad5220e6e1d45b8f81ccb45b5514e2b05be8590c021b92339f9ae4868be243973abf6830dc7aefae42a44a09208b2050a96d6ac2ce754c9e60b158a820bde259e879ee4d571d3f93d3c63060cd5d2960e77f0fedd79bf143c70afa71fbf36098dbe09cf74889e576b9db6781208f3180085fb58afa12818432ba23b0b5e37c4c5dd4447cf33a2c8f5ae49fbbc0d0ff10d086ecc5f8c74b1d4ef65837d2f7660cf069e205485acbc312bf8de838b9c57031c9ab4d29d769f5ac3a66f0d13cea3d883834deae3d635ce2a83b63ace242604982ce17d823fc1168d19bd42367a24957251f5fe3747ed14651309a89f2aa2025322d64e1651f1ebb406e4afe65f6492c02f612d67d23c34bae114ace1e57e09d44cff4bcddc01acab4206f397b7f7e29063f26195eef0d6511aa87526631d0f23266f9bfb579a95d979971924ef4b858237cac834c6c1c7ca1c6d24fea8bd05961ef6fb6ff474bc785ea93d3486232f46a06d88e6783def850019f21e2cf20eb12653bc482ea9604d101b278059cae00d99efaf2d4a76bf546d42b9c8e68a0379d1f91c87df0be8bb3c48d76df3825ed2d8f99eb9b677d6adb2b5d0b7328d0609541ba8807ed24379e9d2841b8c2ef51ed0e2af833d99e67fb01b0f40276a072de5abdba55dfd00987f0f65e9d41bd61ba4951aa296f45a1f682e2826cdd60e8c00bc26a84fe870a44a847b27fc092b15deb1ec058f9b40301ba64a47887109ba9724f952fd3125d804b4351906f8ac8b745c6d3655fd3d8a91cc4d38fc1e02c341ed23e91e8584eea12ad661beb32b9afa5b648ac052777f33d9002c70a80cd87d5d61560da056e029522a92c233f2283b38f2d1fbaf9b38b7c162c08f2d03ceb58424d5e58d17caa57bce752409d4756d7617afd9d753c474b0736b72690acf197e8bc8d8a7c137d5b3462dbc7f3b5831fda62fb03049efbd918da2c2f1016df029af3597948694414748ed9e7e7493d80526e05aa56a77e1b876d1da760e411966774f3b74056238f2fb5ce250fd74184c5d94b118305625b5436748aa0d33ceef9841491605e37a7c49f195f2f317128961f5a9e345190dd009a4ab20051c6d1aaca533060edb11ae4a11f629ab1e3adcb9b429fa18cd26e11d4709b40e4ce6d2bc71a52e2420432369a063f04f3247283ae1f8830cbb477e5659cf5f077d5f6f6a0d07d774cffb6f5746c91f2686311de46267a8f44200a23c938e9304119e2e5a5c757fb6ebb4313b8f879d8799ea67c27772e010121782edeecaf705c50dac0bae627e732e96e86674f146ca816649e0f52564caffdac6b6a41707093ebb9fcf25774b50a185bd45f492609dbbe772cad1dbef8cc93bf13178eaef09546696405f8f4f0e470e932e08c857ce10a9fc82ef760ea4f910d769d715b50fd9a822278d9567628e9c5480b71308745e922b76a94a29dd0c0349cba4051a0ad414e353de6350280ea233377daa5d9db05696f8cfdbf804b23c94bdd63b540c3d97ec29f7f0383b07d366947923e9f6bb8956cab686ca8b7791b6151ace23092b6c414c902ca0a628ee00106939cba354d8c02a76c72a203bf12611dc4e1c0be801001745b5b0e6a17aae7c6b44201ba4b3916edec7a1fb70cc4b79f0772f0d32856af6d625b650f5e3de4526b6407e7295fd0555c91e2a9ae44b4534050701cab27bdb49f392cac2bf35a9a7c27d60d91eaee9d83c69d301ddaabbb185d90b9aa66daebdce281c0843360f19e74310e8f9c2ed39eccf3b043b226c6e22d505ca4b1c22a9c05231cff788a23f12a3e715d363d96cd7eed57e6aebef6cadfd056c48fad099c0a464e1a491c0f5a369602d7e01ef88ca9594101d0e948ab4bb04e8e2d5e2369f37591ab49c9bf8068c47136a970d3d5581753feae53d31bdc90e3fb46fbd91e4e227be2ac606f536a1dcd493b9491082e2c46b11bc50c51fa006c701927e0d47bde61de4482c5189bd78716e1fc48869b5a291b5231a2dbbf10ef066768ee7a817bb743f4de89b52d1432ee6da15b4493b764a97314141103d0d691617ced074afae83c61432914703ec255033e5fb9b5eae14bdd1fd28df2e012694cf1c25a4be4b4fa91fcc2702685081e74502e9b77cbd54eddf88db50ca094d6f80f9896894c057b06ddd1fce5e7eff2aec24c29a7e5af904a79d9689de0edad099c87708d08cde65d13a429723fd7756a5be47ae8b87313b0af272c24b0223d8db323b04aebb4dabace8f93b52b4ca01b0abd0ca708f6be90bee9f013a09cf855d8a96b8f3a9a23ece256f28b5dcb9ed598d7127d8cd256699ff11e02f060ec3eb2d9f78cb8abb667d96ea473ae46845e211991da21bf3297ae46d41550e2058867373a9c22e22c3733c5728f6998fead312c168b0c3a00f9460fa36c70465020668d57b550ace30caeb46d07b5dd979a87bf22255a4fa997905ff642c062c16b46c9d47879cb049a88584145315621fa98a406c86b8dfc2e22cb28cde099cc80fd742c373f248cd0725102e7aebe962b7bfca3b27eb8c0ac00dbf7f580e0a87503838aca375ae19040d7ea6fae71ec37b7ecb8ffaf9b1beeed536ed730bb32c63f9de37e761b0a8e4e21e6c15643ce6631ebedf48f6bc3d0aca0a7498072287bbffea67d787f150a2f627a6807c2823bfafae65b069a43cd01edddfc90bbe1d9fda447727e5e7ab34ede539ae9e3fe130d5e4e5bfe6eb6260adc6737d00ad198019b38fd336b9a6bad879ac4623108af858a631af963bdf96c3683d830b88a1fc4efc45c2e4e96931201e883183f6168b1a8c90be29dc4ff5259d18200092c21a5b1250cb2c409f42fa6663f99e9fcd20df96a6123902ddd8c7f85d2d0af1b1ba43b9b768bba88dea0738b82d8ec7759bcef56930f6c367a7f0282b300e628566683fca934695858d51f77f80a047a5ebd8cbae3f2fdfe3f0f73da01f03c09eb59e7cd62b3f8d797f44c51d1305391b6054f5f7eda6ca723aa33a1d4406a0bd31d71ccd1d789f9172848582e5dec9d48198708d3e4865c2dd573ed9810ba57682569750b8778c0f3b92d105e40de40b3dbb57535a1ad7c22ac4737a90036f2c3308fcc3640bfbba533452bec2f0e0389baacd6f5e8097deed3ca5ee9d01b0e8c4d8a500a64b5b642df95e56d803c97295981068394d98454d98efe03201df5957c293a3aaf7c98f6c1ebf5b09c4a698050e0968e6e5ac10de2844f0f40e7870873a773c16a496ae2e4ab7bfccbc24e286c6f921a58b230622893552ed0333c0b8e43f66e8cc7e0fd15582b51d3e8336d06163b96b7f4bb427eb5ed9f705b3ecdbc45ff6250a39bd972df16f8f00ca07706c4683decc14a91aa2a03dfa0f70af012fa5b8183df57b7dc4928fa42f0cff401cce57e63ae7f56e52c41d20010888a25b614d1222a8a5c76bf5e23a87fde0cd716be387030f4aa56e5679980a0c9f42e680e94f43e741c1be3aadf27799922384e2ca410d46a58aa2b1b64700a62039d03c012a246f3ea49331cce7f0e456b6a384eb62a6f35044192b4c9f0b7420972a2d024c2901763c3269a3979fa6fe3ffa23552c6f83de45a6c3deec0ddd2f72c2642f7cda366b6c3d9fdfd419e730be2d35014119ce2ba2614499010b8cfb1d44794e33b3d985eb1103adc7bb9f2ea09d1dd498dc90fd42c416d49d02a71a4320da3cf03b005973a7586728322afd0469feb1857dbcb94c1e15402d0cae4f4e19e5c6ef18d0ad36e920a042d213eefeb9db67775a3abbcfe32c395608fe348fb346f10eeddd4663988c58a56f695745d652841719a843bfc877a99700e7d07325ae46a4509c1216383c865f325e3cd54fc2537b7368462023e956e6055d90fd4f0446da6fc36ab547cd614864d488fa828eb62335c4f63275871e260f65f6dbf3aeeee8f1cb3dcc9769625a2250eae429eeee2cf31a4cc80ed103ea0f60e0dc11cbc2487dc11f109e22dc3a1ce287d32281bb5391e4ed5695fc46c90596d5bc4c5f12535bba396c592d003108a1e477e929efba763f823b73d7e4860c810eaced498d7606913386eab047c2927cc369367ade474cf3a419abd07aa507d70b10c4722485a7e356e805c61d913be73919e2b968dc4a7290bb317b42710d1054c54017a81023046bd7cd1950c44e84a4218552c64fda9caead073e011506a1271b19f24f627e0f7e6b9f56c284c895158d6f21499b6ba15ef04d21644f0df10519550ef3ba450889977b6a47ee5d1982458d5f929bab4d232e179f85a80daa3cc966487c14ab572dfdd6779b7ed0b7892d75246be7a682051b0ecb6a8200b22cfab023d5c9b4a64c59dd9208ad1df1524b32d60a5202621827b326f3210d08dc82acc4cd5190982dc2c2991b37fe4c6be99d6545fb488df21cedc55b880702b17594ca217ee56aac9c17c68baf78af529c74460d641ce225e23503c2cb01c6c04ac3f8d1f2beb90e499cbb1b385a35860dc8567421f4d350da3b57f99b0dedbcaff2310221142f88f36e1bb4d51992f5cf9e5cb379933265b62b7c83e10c71ee116a5ecb6b1abbd6affde2fca22e3cc7aaf3df95e4ddd2993050df305c0cf73404b4d36d9d1f039b9a37097c94bb7d75a84bf045485f9591f50f44011c09 true -check_ring_signature 1bc1e34953b1a5c0fa5a876747ad2d10bcd4bffc346481bd7251adce6fbfe9ff 1dbbd67159a4d55467bda62994af30c2e32c330e8e23f3b87cfe4f39f86d981f 24 d7848287898f786f9f757daaf53b9a8685dd09da9973a4b9e478148833cc884b 419d66d4b1a68feb261543645dd893b4a1266a9fe680e3e5d47ab85bdfa42eff e2e6c11c5533d569757a9c4a8e5c3b1600936822adae48d0da2bef3e73a8fa89 ea384d93a185dd63a46911bef2d61bef8083c4028801a10999ca1849a6443809 73236a53ba24ef2312b57b93550cf852de97742f21b5fad9e015b70b8c0f8ad3 b62001595fbd462532c60fcfea26b6ebefcb2fe67572eba334dd92ce7fcae3d5 ba7ed48bb4dcf39054241c219555ae79b3f8859bcd385e30eef66b7f14818a25 9f025b02c57a912213945f72d14b4c93a0dc01ff4e23367506ab2ac6a4f7f252 c33fa2823b9872dc70c37300ceffe176805b55842897b71230cc4432825a8525 cb1ef070b1af3efcbc33925f9b5c8fa213a1c61b3b8e0c65477160b2ab6f17f7 617b6c4782faed81027b2168b03c0ae55c3f3e6801fce54ce47220cf82c40068 bb1483bbbf4b17549992e1061ecca913681ec6ea16ccb12d5cd7187d12511c76 18ec9d21b99a5a681c3cc9309fe954ae0ea5556a47fa99148475ec24395c6a34 49365d7fca7af2b6a2b74b31e867e4f10d8d94b580e08d93e560304042d4c2fc f6b3dbeaa1a8c043204ca265f56b7f04ebd9f19611ec9c02e60dbd2012b337e3 f21c483d88a9a3af793219a877bd0626ffde3e959e50a2e571d5b913fc5d6994 4256a991e9a94353758add618e55a752c25d56f5af80f70354ccb913031242a5 00b7952b1d040c4ddb465fa627564b0be0b56073e4f1fd1a740c463f53e07795 4c8f3034e5fc6cf756da1b3b232d1a6ec8d0dcea3b8ac53cf897b49a1f297417 e76770c539a9eba7158e822925644218aba3b9e175a34ec1fd195a75d62a95e7 f039a29b773501fc7be0b1b2231e79d2177b08c79d700967ce690255e2891fd5 d6ce39e46abe71bbd7eb7e45b0282b3ec5cbab753cd7f5cf458f3d43fe2aef60 d002a87d7d1d09bf994e735e2901551770e31a3d24ce9dec005a3091fdd19ce8 a37a44f7214027ab29fe7d197669d06e575d094bd4e86bf86304444559c4c1b3 26fdf4d9dd44c1e91c93df4df1bf7f5b07e394fde554cb6084b15a37aff7f00864b47815a39862f9e39a4d3ef84ea30ff4f6e7f0e20402ea1586071994cd328439da96ff4a55158c211772a6d15505ce4895ba5acc1103d06cd031ff12354404b2996b7968d935bcd0879ce4cbf94780c8da7d6dee69970ad926cdcb5b9bf707827274cd534d61594d93ded6526d6be4e9995777070dc51b03e156b597169703c41d68bb44a9a78dc2d63bb1cc325acd4fe0eb1cc9b50484d4b40233e443850edab24da4d7d9d62640ff8b13282dc04733d1f6a6b1cb079e8b777653abcabc096d6b6fd64f55583db8278f3f2754c49be19dbc84886e8f8860ff1605883b6401bc2c200946c855b023557b4506658e71a4fdedbbf2a42f2ef80b234f31088c0479db8f7b7253f2e893db08c92cf1963d8c94b69801e37e2aeb8e89d55ab18e16e256dd3b4336b23ba72241de9bac22166999a89270625d2af89441e2c12dfb01d08d37c38150028eeea4ce29c6ff09174fdade51c9d2daf63abc0a1af9ff3805e1f4a7738adb81519cd0dbd340d06e1f900f7c0a2639a68cc4a39ac62aa91f06268f88651eb023dcd8ad7e6dc43c49c0756010075ef7825d0819320c2ddaae0024b12363878fe2f0b602494d7c553e94379a4e9742802e88c8fcb9cd24340302138fb4cbfff34a90ca834fa413c6cac313d7ba9b70f0829f50a97fe169948e04ccb5d7bad269d128b2f6450e5b4e8da57c656ad8e4c1f471dae9dd5cfaf021060db4aa9b7932c90f4d206a0f05e9578b78453974072263748287a4ebc2d7f80e6013cc2cf87704bb74c0cabc855e3c569da173b47f8fd0ca8159095836a9f70171b3c211447e98f8086170c3d406b0ae4f19ca69d3405d121d2d533e1c2f9700e99c72c159d29f88a27ceb176d80b72fdf407471f9c0f744d9a288206a5831080a9aded82f02b3228424357b6840c10ff50516d956a3ac7e23c415333c86320c07885dfe980b0aef6bcac40739e272f6e5ba5c0e58d0445bd8046f10a7881e003d0f286be603221aec0b6368996cc7f2cc98f6c99139bafb1ad35af738259d0a64aba9264cc9a2b6b8d383b627945eb7c3128852d260055acddfb009080db7adf43e92644f6c1951882262a77474a8f2d32fb1fb9825c20c729bed37f0457109109faa293b1b84ac2e54e8ee75da11bb66098dc0cf9ccecca03f10ea0f8c470c5670f4fcffe269ec7bb517e3f3b08c92e632c28b98e9ea0e5f79dea1f95450079269354498d39a983b6d80350e3a44f8a191d8884dac5ba8aa831aed04baa00115b2f9d622e4ffbbd7ab5e1395990b6a859a8925755214d92e2428edd4bc3e032137d6cee7f01fde0777cca4e9a34437a6a3cbe981a3162ea06469cd4d650c04abe0cc6db517ec53616e87c19ece8d9be2b938089cbec5b598a4a00b0bd4f405b49d9a256e7bf26b5e3f0bcd8647879d4a480e835ceb6c498faf01f76792e80cb41f5c8300fc585eb4c556545ed333a9e1d966dfed27e85227fc49e3fb2ad901336c083ee36dba23aa58f7ef5e96aa4974a7c65c22eaedcc512b6003c6e4850eb7407862b5baefabddc6ba97f63beca06fdf64b36bad8d3a5f190ff3e3dc600ae0808139af22b063df18b456a4aa8584111b644db30f8e827f5df99c2f7297080afe4fe984c2306ef6036df7ffec21d0c1405c350caa5f05a9dc59033cbb270ac8b0e426db5a0b1ad46be7b42632fbfcd48bb6ba22e2a80675587e556fada9065e3c79bef5815c468d9862ee21ebb9227459d955b7302a0022e1a74c7af8f2065ea5bc5fa5f4f6d6284beda8d779015d2651d48b7d635baca1e0de046d1297856d0c2e745648d84da8714002d3590ed87d4783fff98e2368bf3e74dbd538b205ca29c7b7ea039347099bbaa9548684a0d10fce9a41aad77f85f50555c4f1310041fa124dab066925c19d6920f033d1f1384d3a4dbd01a4b39ea4291555a8bb0593a346fd329118afea095e083213d0b1ddcb4f84f1fa4aaa3af9c18ab3e9890bc237a9d81637c2733be73474694d5f8fb4819cf9041fe3ba152d428cffd8ff0e8640d7459a0654dc243697d807a777743077c9457425934539c40aa430e533043d67b4a286ff353948b07b3f75d44147012e061d43db4587b85a51fd407ce609 false -check_ring_signature 922c1005f7c9608767ccde50356845d02011aa329ce895b801197ddace05b92c 84e95bad4f57a2c52ba540ecfc59abb91aa6a3fd9822033296fa35ba4edfa074 1 9467f44505ca04ee7b059497b21035bfb83b8dde89f4e1a618f9a99af6d6f5cf d65c57097f54db6f3f4c950c99f0e6c9aaa5f4c8dd3556db0856139303d19100a6b67bffd0d02a2ed2759a703684ce00b15e66bba3d8f2aedc6b216508c1ba09 true -check_ring_signature e23bfe56483ea7a3b4dddd34100a4e0c7d0c41599c2fb73e402d05d6f06b00ac 8a95ed61d724682793efbe92c223398aea04d360902902c8d42565e3ea10c6d8 1 25e4259548c4033d66124ca8bc3ffd22df8b691c574cddbe6162266eb4d4bb52 12882ad8dad52763bd7a256946b3b8ce4b5be1669ae43fa4e8915092c9e76a9694e1291c43a406ad1d70f0dacbb4a5e811d0d5c71f7b9100274f30b262c77222 false -check_ring_signature 74fc7ced9c49e39a978630fe09c8ba103207be81e1f5436998693879bb4fdf7a 4c1797ea2172c2e77dc1a7407dba97f8c59f519e6c98b27398ff1ab0304cb5db 2 ecdb7a473c54a65c05ce26155a09ceb2dc5d9418b7795ee8d81b5a6ef6c71a21 da95433c5754feff66112d1839c492a47eaaa16954c59483c33d8dc9a83ffef0 4f0db9ef9be2d1e16ae3f555e80978b93892a7657312631080aee4835a9ece03b2c399041b142fd929c80156e1784aa3511df49485c7a55915fafbafbc28983653bde6075697fac9e21a7cc916f89a2d08f1e4e6de9f8849df8774c10fb62270d931e77ef29869b0f3ed03e9479424789f45fa35bc47611705097972b4ab42a2 false -check_ring_signature 8cd5cc3d0cf4b2ee24e61036d43d2ca81e7b11cce60fc503ec9ba371361393a2 6b3415c30ceccf0a75a52b70b5a8e280f8bfaab6ebcc45f25cf877706fe3e561 5 6e1673c408e012893487a8b6ce9fa6b610c4dfd068eb45182964dec2559930fa a6868ae9740e66d9a9e959b18f9876096dd0c46ce5cfa949e37d49252a361f35 1787a7fa974d42a1bd7b4135f5ba6745947eaf1273b449d6e2b6fa15099c89d0 794745e63c881f4797ca7c4017e8214419a3a424988cb2f1b66648a3b638e57e 198497f75a8123cf4b6975696e8d8b4aed43823e18097a7d064397c6dd7b0d70 a3e4d6663f5d9f43c4d21acead4f32f7a6a52d697e2ebe62935ef9efd6689e09777d208f900fa053b3a1cbd03562b41e58315e7c33aa58d22e4b8a22eb68b70d0022d14e033ed8305c112723fcb61dcce26466136f488557fddc8cc0be711206d1a6570e9fe1b21d11f826a9b97c876593eaac58a1cf46bef08e76f3280c7707ee5674bb21aac36945e360440b5595f0b6f922e4e14c28776dc26b93d29ff202ef1db1df45cec0ebf3391fc40458a33398ebf96dbea4d54f6418a10ae261ac0c015df754b8cdd6c61314ad6f658a7f00befde96d2a071b0e7d59d950052ac10bc36fdb280892ba24679da65907d42d1fc4c354e0d6d1df285e5de2d7541a890f40aac5839d4cc057815264242081be67efa14dda156d0e6d90ba3b8befab9c019797f614c07f0c2e5db6dadd987a000c171686b9e134a9b0b68fc447d805bc09 true -check_ring_signature 2b96105d783472f03274f060a7762f45a962f44d234022dc73928e9a4c6017b2 7a92b7a4998e446c3e8e2e97d406aed5c06c732de2986219614bdae91651ee95 1 5fd6bc5c24414c29b21b01591e6ac03f8b57ab9c0f2bf08f351c2a3086451528 5a04836171183ca9c59c64c89d4136ca9be321bde2c7dc352d9e90abdf5b13aecdb22ba12c174aac6935afc792cff65ca5f991239110b38ad0b57aa0c5249f0e false -check_ring_signature 8427e7179050bc38d5c25e68f70c2c98990042388e326d6bebd5674ca8d34840 5680400f955da51e5b6c09685f18bcceda482c34b4b17b375b7340845ac486b9 7 250a755ae6c51559860d77e7e029e7619f3216a657cb2bc0f7e4faca8e890b51 649be8fc8a9a5bb7d159ebdd2457e7ab16f7c5d82d91dc276fdc802b761a4e53 852a270ead33900547349658566b553356e17230ecd94c8cfa79a8244b4ab271 11ff283f14fcbd714ca4f453dc361cc77ce3e639f9ee206b1c923548e2feaefc fa73655a834b064343b4f2b1ef9a3a8b853f525d7b9ab3a3f952363652954655 c2c5804558610f4b5b1d50389cededf481c7c7f39b009b1535111e71963fe702 32f44c3e26156a75e661d9e707b7cdccb95ac714a5b2017d2c707827be504dcb eacd6cf828cf27cd1d8da461c3c9630a0a696b1ea92dde4cc22423b68420790201a7fa68060ad3ad8ed81e554bc00ce7f641ff0963576018ab7834bd4935a20de3b18e0abe94e77cb6aa0e67e204988beb3275947f9bf99db589fd62ca3b410cf0fc6e297e228ca15f19fb4aacb2a2ffcb4ec7f8ebdb1b26e156c6b0a191890aeaeb35592d4ef0bdf9322e6b0dc3c0edd29f447d22378809a14b88b9ceab0d029e48fc1fae0e932c86c4c0d6ae45669a8daf3426adb70850574206f041497f0b485af0906bc0cf415fff05b0d0c438edba594fb66f0c0de0843dbccd81432d03743c9bb9cbdd4055b6f3ad584ef2330bacd73406961a76fa8cf1a9ace4d0d30c181fd164b105be70d161dc08dc0b8735d23e3eda3e10b946d3547b555578cf0b5619879e79d179d3710ce8cd9c1e32a0a363670b362a78a453def7ab21e37d0eb58a7cc7f03a6e0ea9df9b75cb1848e405e81fcf38d18eeb6553f62124e4ce0b0762b25c6ba5befebad857194696508e3829ed5e8e449284d795a3d05100690bc72126a83afcffd33a9e7a9d3289f6218b9680682d0c2855ab7015f3998fba0295a05f2f8c255ff420c5e13b427ac836507edcd1ac34ebd59515d783450c500f true -check_ring_signature f3d2b5b25d663325acca133163bbf3219f1b22fea6bd6d6e3194db8bc30dc6fa 448071ee63780f0fcfe35245353e4fd28f5c5362d9a5f3d74e5bd6685986729f 6 fb706555f8358ac3db60d9a52eb4981f91d28cc4d518c1a5c988ce94c7051379 e07dbe16cee565a221af2353c4761cdb7c7fab5880372b0e46d49ab4842d3b4c 05db3f4b53f17fe0525e2b5002664d9b0d5680c10146640cbbf23a118d6d88fb a446a6e907f653e0db5888927971d0dfd5c854d2b04f02367e18b02378271b11 72ce0ff9a80faaf2ea826cfe8244cc2d345a7c887143e481ffe826b8630b6f93 d17c0b9ba4aeb04ddb8288222a0d5d22abe327981786f790cf8b9ee8831090a7 8a11ee60dd8470e1bd447f472b60723d8a30ac8a84fb4bd98580b2f3ddfc32027193f368c87e02deb9219eb50e0a9b2d9d43bab27c14ac4be23640a1fca01a0dd12337332fdfe9f3aef0235281614f3e94cf7902486b8a5b76444e9a56e21a09beab12ff902fdc05b16a2ad417d92711107bcf7a554cf82fa069e9432874680dce00e8a862e417b2efaa66cb9e4693e00b7d6cd1c452fc71630473799fdc7603080d006b562aafe0e75e456e6e09f1a655dcc40e296f0f3083c5f58c75e9dd03c61115f05d75c65e04f2d02c6232c72c99a32e9f8c851b23219e2dbcdbd6190de6c9ae374530c0f268758611fffac7dbb8f16d8c71a0da950ae12603d942b808146f9131049a75364f45cc606ab4d6882a137999c163312c87fb3d8e78ef3406b97dbd90bbdd20a8025d4ba438491ff0923da5055b7c3ee446b7ddac341f350056efeaa3f706fa2ac8a6d02d5f2a4cd5644af7f9a48a699801469d33601667081fa0971b1b6dc3c86d539197a531a76ebe611fc967d26067f2c2f0d879ef0309 false -check_ring_signature 07a23b78f73ca487ec5ab0f4d7725d7ffb547543ae4f96e30df871c2241489ca 2073d5a5ccb03402ded68c31d3de658f7c5be2265bec656a12212c83e2499a75 9 ce3fe390c5309c0aa6c0a1e4dc29ef63fdf55ad2fef737b775bae9857c666966 08088bfe1f076131d82458ef08e0a6d8003d26d824360033895e62409fceadba 4faffacfacc069e09f80a0249daf97a40b53a64ab870c62dfd08998d382dba52 386e435138fba8c063966d8308927f8c8788782a3a263500133325c9c82d38a3 efd955c96135d72fee34c765998cf714f37365af8f77cae145ba5d126f1fe914 60dbea373e81c0276c4fbb83ca1fcbb647e2fa11a8bbc62c8e56d4d147b39bd5 1495004110d8e2fe1774ba6eb9492b1bbc54f674ed2082401cd6eab71ad9dc40 7a2af9f6dad76479c5f3345ffd250f55b234682b4fd51d3ee9ae8da2e5a35cb2 02548abda0688fc63cc485b956fd4303bf0dfc43a581e01c59c62243461ea348 e784e4e19099667803c6f08fd0877e85937bec50ab02f75b6f2e3dd61876b40510bf63faa2346e3b35b0ade96bfef145dd17b92ebdb1cf96899e10e3442aaf0d782eafc8be306390ffcda93025f6832a2d4c4e4d1c2b56add53550b3b512b71af959bc1b5902a7b628eae1d16d242c099fb4ce33a4bee12af49a41aab958940910b0d0ef596b753688f228a7184e38e7df8644cbecfa658d721736ed2882e20f9a2fb61818c0060b3fd4b2ced7984bb17b10381efde2330bbb1de208655e080cd33d9e5d5d853a8875623195ed30e2c2e475ebe4ac97e3c5216520b0a201a608b6c6013266ab0e63a461dae171af4c3a14a5fa8c4c33ca25b84540e32efcf302b18e610545ff35d45dae4116ba6d51ac9125cdb7f681739744237827e768bb0487b6b0a1d9e8d5ce809b9e17a6c32df9ef77a26af987b2348c748cba73e917034e21609b0d92a5b7106d39c8de2f057013bd347be67e553e608cbb70683f2e04ca7ed84ed7f4c671efac5deb3db17498f23170cc8deb4596d1ea958caa7e4e06204833104aed36ee0b7808dba1194cc374c193e4e926832ac171d03f3abe6607803d6b33a1350ec428afc88977eb411505bcb54113da91f63fdd2bb85a08140cbc040fadb23ba1c92fd3bad8c4930f36d37403f8e9d4b87bf3636ccffc53c30cc4eeb9945214f9d8e288edbfa1d7546baae6860f9f56af7d79f9349c104579064d404a3d7a893d64fbcca32c5d4e6158d92cd87c8a78bb3a61eb55ec92a7ee0aaabe59d33af5534ae258f48bca9d5459e0708ebf9289d9bb22acb540b0f39003 false -check_ring_signature 0e6194f7b3ec1594e6b727b990c6bf65a2b1eb1a9b73ea00d18a709ffdee1276 f931b871addf92f407f087ae176804734ba35fd65086f6c3e07d9d7b001be265 51 8daf0c2e434171ef0e31f1fd17307a30690639fc7fef1a85a9a7858868924c1d 93edf23dcd46477698f2e4795ab9e4e75ef04c8ca561670c22d0a379f7cc9d12 5fb23ddfd7bc6db6798d0dfadc6accc8c7fd75adb090ecb6bad2021bc3bac5da d093463f76e271597f6cddc74b5685b0c4d4ab6b6f6bae4b0217c7097ced9bb6 1504b6cab3b2454a066fe9462be135eda844480da85ad3baf67fbf39679cdbf0 e812fa7c7f0b6e11dd0d87fd88767a768f9e89f2c54396a9a53d443d0edafdcf 6570828a5e056a4e035b1042b80007a872c71545ea225feb982d24eea14373f0 a3adddfa513dca877eca62e12255c981b148a605c998458c6786aba6aaf1207a 43f3fb57f9a0c90200a940217893685296537f34041fd9586586937386f8d33d 9ff696ea156a4468e1d0d32590fcc865f491d821254594535c1694d7eb0102c2 3f0245304a5047ce42fb0b36544220d7df36c919aa2321bf39e83fa71c4de21c 01c527e626c1b5c928058d7ea772cda93833ba111792da212a5eae0041ccae51 61a79b26403f88acf0d4a7bf86efff4bd9f32e05e9a900e7d890ce36ab8abbef be302d5d4b83cd447536c08dfe66a32fab021889d9eb7a8621e59a6e3756b14a 693a9d20a1d12828a94d01a2bdc856f3499745e4c5830cda407962d2b6953784 62438ad0ca1dc718def66c97d8a4b2a1c4e61322c7cb8c3b904177cc8d8fbcaf 88dbaf2eab9896bac05f93c953c548de45c032d40b7c7e452c17ac73606b942b 894905861e3c952ab0e350b12900b454b3870ce3e9b590f94203ce2bbc41f944 cbf933f67076f66793bf06f2a5008bd01b36b6aa094483269da52bbba7465580 557046df81dbcfe18c65e1364448c8d94c710cd1fbf8a3c10550254d97afcdf7 7b24c0ee3a7836c1bef02956b81358d8f698da40f0ccd706daf3038cb0b28793 28ddf7553a328fe5ef8c8a6abd52ba6d725240f1511665884cb68c96d46d92d5 ffd267319c8ed9e424371ef74e5301102997c26265a2044bbbe05ee291f5bcc9 d467502be7c3753d9e4d2a147d3a40d1d817a865184030870bf97ef6f1610da7 b1ec5f6565cc19a6d015696594ada55b5fbe82108fcec0d54fb11b05bfd37408 19e7e65cdd957ab0d25dfc9e4e1449b1d4283d2561754fc147adb51e057dc7ba c5d4274eef9f5d92e7a8dda59de38f5728d60350d8e98871968765925d18ed09 a44814c8f2912d13c09242b0b75dad90a6357b844e021dc96668c45e311b64b3 cd88baa50640c7409a98921e0ad1e7f2496b39a3b07544eeef2455fc5018b17e 9ab6f34f081bccc8d8b29513851a402759c96b8d95d836db977e24e595deb2fa 56123960591257a70a334628d9868505176533debe06cdfc24f906ec8a997efd a37b03d5446abb9a3fb7aee94d6740d85f984fb363a9e78a9d363d5882cd2823 bd9607c5e6c167f5eb6a289be620592c7e850590393787d01acd958fd717b0c2 d0d69b420c93d9b1b8eee6796db2c7cc2110dc78b624396e144c135fe69c812d b6a2ee98807a00bcff0fa5403102c9a628948d3478234cdde85e5effbefcf204 e183a5d8daabdea01f7cd7f0960b75566aec78874cc933f5acc3dce0512ab335 ea7f86e49af2a4883e11b3362127dfdb14f0f517170ddb4338a5a38d2c92566a 4ea9d24ebb852962e745d4ec30679bed40f581d1ccf6825e80ddb38759025ac7 d0c887ff472e65d7fcb502988dfb0c919c8d23c2897d49b8c8de90d725604ea8 0177735a5271e254705979ef38a0737897f608aa144f4c4ed97e220dcb32f1af fc49aac2eaf95de87156c0348381d78d9b468a1da3eae6cc25bb398563b0bdad 84a98d5f20b608d31af41925052dfd378f0252e1bf7c13cf9b773e0cd4b95a52 8fd6df156a95abf7ca37fa1d65e820b56f2498c9ada0a4027f0dbc285b9a9b86 f7bcadf3751cdc85db850619a857288d1291c2f403732d79daec6eb774fd26cb c87e4373dadc7a65f53df413d22b3370f7a8c47a929ded626f3f71f3b249eb4d 02398630b92a7f782dd0153d39a56123ec7ea8d5a0fde77aebd302e641de2a54 f655dea86af87ed4d93ea10c2ffd5e52aff83a6fb5fab65a1ab67a02cfae6523 990747d3687fcc37ba0fe4845f9b247ebddd0d0a5a61f2637bb498f0b05e9fb3 38050abbc28c5847708f0142d8e89d44dad6d0ee8bd60b5771bd03d590a7ff85 867b6cdc08dbecaf1b6f9ee2add2d2c9dfe18474a805a028533b619f52abf6b3 0bda8ca264a6747f18195bbf56fafe96c9a0c8e6557f1b17c50eec71ae52fb50 cc925498a1aaccee819efd183df25795472fda1c7abbad6eaf42b00192c1e7005cacf5a965e831702169f97d374b7b30a7cb3343170c89c325090815e663a805f1d64336bfcdc25794467e68c093403faa50071b992863da69b6399f8bd33308abac8a69fe14a4a3de62ccb4bb5b49f760d1567fb1c966881476f7c5cdd20d0738b0452fa686ec759a6f19f50f1cee1ef06d69162d6d9c6d97055ce7b746a7038ad3913cf9c92a4a5faebecc5417ee900e8cb9e58c35d06e7b554a38815efa05a2f578665a2f96fff87bc9c025b0a23775a8e079f98805a350cac06456ed230f3bf10e3a133abd448f811ed82663f4bfd3fd6492d3ff6e49625b1efc8b9a090bbd70ee46aba76648c7e899cc19433a230f59314dc72d10d7c5dff9c9e2b79907161b00e307b10a9f53332a325c305744d2b96229831fefd891fa1f0776278005fc792d7d1882368def90bb0c75c927464eb4a95ad133e9cca7c57708c620520cb4a4a25880d80cbd1101d0a78e5cc7cf9aae6fe52449911be59a405f35356709977a9532074a05627c13787192cdad3b40f9434d469f189e311c0d917d788d011693c8319cc0b0c4c0ef4fdf08f945be0c6e65fd1c548bba7608a128c3a327020818f93aad968cd094149dbe064e3873b4d704bd0a5ee97fe040944de667a3063ecf7be8aedfd54bd73d13a56b944c546c4c6fb7cc5a9c74a6508c8af26410095f34c6787f38abd729bbb728a4213e9e079b98b81d5f6562f29ebf21ac85010fd4d83ffb1851ed601ae7bc93ae5b36da273101f7485a2d38ed29f68636dd5700b2fcdc9709461a027e02850560fc3e3f1c6fd8dbf4cf1084e059bdddcb8d450f8f3bc1834060ec6375cb94cab89cfce4aabcfc3ef1bf13018535607234e63800d9f6d20030fab8384f287271183563e0730708203261ebbcc0531f4637e0540c7354ffb862e9c435afcf4a11313a2c60f8fccc6483cb70b3dd7b1e7abdbc8e0779dae0268351518fe07b03fe764675ba91f7e612c6d07231e6deab9a909bdd0f419adc092fe62fb5378be479f7cde0e80eb642aa89441feb6b0119595bd7770446d253edeaf3c0dee883f046f0f7e1d2a26701eff4d7b3e9c5f76e27bed51002ee4a57c4e51ee3d2bece02c959ddf90e422f739f1f3b628b5be04f8845f06b00fc891ff96e22af1869f22c1b79afa8ce7eaa4afa70158cde42310f312f92d8013e3aeca05cceb2ee5bcf0a50a5c267f4e548bf32e7cb935ee28c644511beaa071923a1d8b15b2e72b864cf5a4e3f4c290467c9a70dcf924345cd82c2d1f68c0e031f05602b53686d50a7a7bc429e1b54287fbedd16c6cc4aedfc9d66bdf31f0959689980fba888d99ffc6d40aa6f7914bbf593fe736e9edad1a4fc7811853e03f927c357cf3d5dbf58327d4b43ce3f8797063492961a04c30995bbddfe281b0b153043fe51178ff43933a0699ab3a2d995457e7cfba7149ca8d3da1e99d2280bf505e8bed2cdb4f037c2d5c1efd00cda5db5cede0d37a3ebdd94b0f12c457b05f546748f69c5212c58f028bc6a29e11ddb4647e58414ee25060e47506fdb7a0232a7ac6df4481bde4ca5f3ee88a97dd96396ad4727b076743e64e8ee1eaeff0b2532804e4cc7a28306c80b9498eeaaf5be9087caa51807bca9377f76ef6a500cadc72f6f95aea184d559be04f14bb0b05f0ce303fb68cb7091ac96007264ec05c442a68d867351823252d8951d7f6b9671b857244d477e75466c3017f8621808fd511a14c5dab254e5d685d08a922317dadc6fa90b6b9df5b2d23dd392ac910bc8fec0ac56c9ee5d491d9685709fb0d3683905fa390472ecaa01090bd4969c0df17f699e6c78942625672228e7474c37db2336b1136b4dea290940f4a35285013264411791a89ae770a63257478339572c85e7b838078a56db7c97a029a04e0ff040a4d5f08fa4e4a0933b7566d8dce9970eb471b270b70970b5ea19124e0d09e643c89b98f199fae32c088512e6839c33ebd646920e360294d869ad6bffa30de976920a838bc8b88025514d0312f6f28066aec9b4d2d9253ce94e85358b54078ce1b56aed1de3f9071419e2d3590df4d3072ea1d93f11d1c9bb011097f2380ab7341e0b1c7a2d4de64e82f4203a87c9805e1298d69f14b3911622f1119a900d49de89000147275fc18aa830228bafb687e8cc9eb8ca4c2ec4c90bb33fe25201417fd7408111e0efdb16d59412852deac263800de4fcb96b19a2ebbbe4d719051ae02b8b3c4292f097380c9587e96bdfd82037c54649154034d95057e883e00cfe4250f079e2a5460a06aeb4103e6c4b7b64a38a851e1650d758e9a5bc12d3093fc4e9f701612625c969d7dbfc1369349c255f883dc436abaef5c16e453d7a0571cf9797a89446077045d3eb3169261025698dbbff5f1354426e7375bf04ed07b930b24bf02049e577c98bd7abe8c5903f48af2eb03686166364ce54aaf9c20a9774b7b04b922ad510182f41a8b84e0b5e8e8cb404e775f9cf146dcae0fa5f0460da3dfb2ecabe850b5dda46d54c8375003ffaa9f7efd508ffc799523c6c1b0ec1406363554dce15ecd47d91936292b448022b5f162ec09937f50f86d1e50908762bc5330f51c639e035dd07b227e6f499884b51829d920ebb517c3732044500150323d68280d23c6711473abe8d5d835c5f071779af65647334d656caded90a6e66585fa0cc6806375853481eb0b409211473afdd46d6686c8ab95c27aee304a048fadac986152ba89037f084dee9368e5768434610f7904a3552d0d1bcdd06ee33c31485d28838266987b1584ee02a1ff0383a134d11114bbd11b5f8e0b10cd19a175be6520ec55c0380478f723bfc1f0ad1f14ee839547657979ee73e9907939245c535483d3fd79ccc65d776903ebd014303ee2c508d87f053666b42200e8731cb3e79f0a543673cbe1acd23740c442a00db5c6c23c06d008029184dea0970641273d935bc76bdef35f483052e57cfd00b1188c54948f7856857dcc58f05c0735df9a9896da2fc8b82b5c3ceaf98db68508a1004230e0f8f78595da2b702508216f0ba4d65d5dfa14f0cf2d54c67ab4bb54eb4e820c5af761978d0531b0a25a136f6bca068eeafb0828bb76bf4b8bae4cbfce93e8cf9a4c5ce2a1d00c20c2a713c981bd9e46e48dc3e90d2a09e40722212dc3d46c7ec98c830af1545dd014cddd1f0492fc19c597f24acdb0cb9836bf16507be54d9eb229d6b778c20370149e7ee1d371105f331f829e9b611340e1120ee6ce4bffe9c3a7f96f1adf2b802a62f4331c3052309a74465d7f3ac8059bb7b1e0093977c1ce177c04a4be8060447c3a9a49e13f4a2f8579668d929b6782c76b41ef127b21cf91c766de44c930005089f2fad932b12ec662c1bfd7d17c8384b98db2a64ff3b952034131cfa370b5ed0b3bc48d3fd66cf1200984c8929298bf7102932b658432ba2e93d0c01400a75775b1dbea79c9acb1b3348ba24a454f104b72c39ffff01a56d60fe2084af02078204e1c51773f8a91aac91564e8b5e069ce2e88319e47e65f01965743ace06f6540d22fc0e0e2038173960189818af53095c82b0726b6ddeeef14edb01950e7c381d852224666623294305816e713561e1146a37a1212b41df75079a26e601ae81025abd07cd3bfe475f3c44db05a2c715ed902bb43cdcf4e505925d3e18006518b18a46dd21adb117bfb342c68b96f974f1be18c47e7bd5f00a90c225500aa0d5c11207714e4bc729f3662664534064a6fdae4c03456e9e7a75821eefbc0b9cfa05ec403734d1ac35e3a03ca5bf085d0473195dd9490375d241c73ef5d50bf79ed1fe14e93d0710ebf915f8f52d0062b7986129ae8903f78a4b449b7ce7013f5b2aebbd5fcc76b71d0c36dbbb24cb6ed6cb50de9b06a01ba11de6025f2d0c4af71c28d9eb53d9ab885981e27554255cd0aed13438469d4713c0e37fb8ad0c751d50fd4db099dac66358fbada177b5f69f3a6518216f0eb7c92878f94da6d6c18a4c5606f55afb9e66610ef4a41953cff6f1ac7181b6f76ee54aa728466f0c73129d70c6affca90b8931deef27bb1185e7097a93b12b3d389388e1ad1adc07dfd96e6dc926eefdcf373c64566f690b970717afd1df3e4326d73c6d1cc5330c8fdee071684fe4e042b8aec497e2e4fc1098149d0707ce95b550b63fa74cd808ae6a0059002b016d814e0a6e716dde400e5b3716ae33fc8c92915488ce7fe50d94c19f927482e0d6b3cc2dcd9e5fb9d741aeb81c07a9dbad27b926257895af087791a6b9c4048f17832148be5c58f89e4877e22c5087ddbf0a7bf273ae45a50b71d9305c8813c886ea454d6c24174f143856abadc9319d6eabd8d48ec3c66a031394f78c9c9cdb2d0de8f44a932d30a6b913378999ebf0560c1c5f7c4098ce06f117b53db72a4436c885eb9c748deb2587ca0904743138de7e98bc3b0494e40a5a56d71f4204e8b8cad941bfd3abd384b976baa2f6b88c987e61f049f80c890df6470aad55283b8b891b0707aa22089351441fac575587b2ea4ac2f9f9c38b071fdbcc7f5efab14d86b22181e741ed824447a2facb46a63e91538af78c5fdf02 false +hash_to_ec e2e950176bc8cd0b171f41547373f8a57c0cfb5802b6750f7b0a0c8e3aaf3d3b 681daa57e7ce6612245e3e6c3942e04cd9a66ba9f13d66fbed725cc0127e52f4 +hash_to_ec 87dee7c0578d3252c64379083bc84df6e34427b0a5dc3540c7d30a026426f801 b86c6d48e65c0d645461850a8f1d95110a0febb014602082f00ca5ba9fdd9fcb +hash_to_ec acecb9543342e78ee86949e1447bb5a13edf6598a492517773c8fd6c15c07b54 9935a51c0c9675e0e33a63bfef1c2bd344f1fe62e3c79a955c6bb929d0ed6802 +hash_to_ec 6a19c7725df06a678f5aa3d4b4036ca72b6a83a2b9f67405dba766338436357e c4f3da410e6931ebdbb5760242e8928298d7252bfa13e7b92a559ea84f56d07f +hash_to_ec 6c3d54c713d70c456185a1bde120c543cc68b37069d92b19cd8c3350d01ed4f8 95119e72336886fe2233638c4e3562e6a83291820c7c9f5a36d28ce954deabd9 +hash_to_ec ea8695c5bf16b6b2571f9fe75e5e3039dce544502c81d1556e2ee4bdc85ca240 8039009b8712835f9835f9db597d36257f5d362a60d9b7f451bb2296ef4952d4 +hash_to_ec 1fa2aeadf35f388f465aa493c77feb2a8beb28476f0ee7e00221ca6537650da8 d0c04bda4f419f65af26738bcedfded67a5e49b837c9a00f88a9ed275d7c2cf4 +hash_to_ec 9f0d61074912293a625c9eaa0fbcb0bce709b38625839f6b1ff6c22b93b3d654 e9084955dbf8d31c0eba7da0e5dc0b72cf141e28fcf7caca9f14578661c4657c +hash_to_ec 8831e4a3dd43260bcd6b98e6a40e9cade299abf0ee3ec2decfc375bbd39a7d0c e9c0cb87c16c123e94cf9cfadef3d216cb9f0d05a3950afab012f832dc8a8d47 +hash_to_ec 2d5880e3653fedb308bee26f963437c867b03b6f37d41809e90704e188bd2dd9 4983ca0545f70b67b016a9e25de96d1ac39967ad24193fb18cf7580263e5b65b +hash_to_ec f8e6429d40352d5c651eaacbc22a00e345d9804e5365b7e4ad02856f712e113f 5c784142f63b2f8191586d2193ad7abf40b2212880e3bc6a0d1de28f110b9bb2 +hash_to_ec 0c76de216dcae0d84f7d733057056a0b23b26d96d5f6a0170330e2b0b6273eba 7eb408564abd75251f295f5d277784abcd7c0866a4be91426763d2db1313a419 +hash_to_ec 7cd6e4feeb23631d2a2b7ff09ed9e06d17818e8b5296f105863393428242e3c5 238f17222b963e2522dd5e9a8b31c8305819cc7a51ca0a1b5e3f1be38037ba65 +hash_to_ec 457a19d579b247efbe3f1129017788f1a8f2765a0006e84cb8329c9325841081 2c2e35dce688fbdad2955187199c276c8d63b026a586701ce5e94c4dcd118aab +hash_to_ec 0094146ec378c560f0957f3064f25228a337cca73eb32a7e7022c242c270df11 0dbe5d5335e12f6ce7a3e20d44e5afb1802ea2fbccd0fb3193b9a89924c7db0d +hash_to_ec b35314cef68bd6c380cc43d022a8d9d8638faa41105a5f095b69b4dc8bf3d22f 36f8813eb98845d8720d5a255200a8742319766fa5578f3f77e11c5d263f8367 +hash_to_ec 356024ff1f115128ece2d3df03ce90fbba6863f998109c914047d994f9d82c3f c5ccef42f5544cf07d75adde27f282c4f17a8eeb0d12ebcab19646feb1662783 +hash_to_ec 4f37af311cb5fcd83510b2cc2c9086238873c5774dfdad8b62d93c6f8be44fd6 cddd79d1351ff988039f64ec38055c51f4fa1456231da999119ffc4c865c2d3d +hash_to_ec d2e793ef2c448639f732ccd4c7cf9513fcc4bd82add4a1c23cad2c3435d73800 0473fd51360e63d3b7972fdb388a727e6768af982c69c25591fe5068c67f618d +hash_to_ec 0a8567973ecb91ae2470f9278ebdeceae7184fc3a8537269047dc993a529d3b3 1fbf0021fa8e88b97c2808949b66c66eaa56790475e125ca62ef7eefa2b9fbd2 +hash_to_ec d8f008c6d616387843020fd8c054572752511b85334b0db803f62c5a317d8087 762bcdb967c29a4280ea5afb02fdda30250af4dc8064455a3f4388d3bf75a286 +hash_to_ec 74f3cacef5e61ba45bf476367fb68ada53a09cce2b429f609dd9533a2dc79812 4ea7edc0034881e9482ce24592951850d43f74fd8cf9877bbfe27206cab7cd79 +hash_to_ec 3c07a8a56d070ff9294afd5cc43fa1c559b8ffc03f01cc464735927d2bb75ed1 9eaef762b3d4c11b39464f1a1d6e5a27e051da2dc3fb9159fc5d1e368d97f848 +hash_to_ec 56991f04b36be1acde7157b65a1276a22641b9ed48c8e878af673c83a4c03432 45b176530d46914c287e1a1f19defd5335d38f04b9fb298dcb2db0d582c5b91e +hash_to_ec 3a84ab38670a79dd1d82cac3ec92b96fbf87a39747ecbc38bc33dd19a8ab7153 6b9fe579e7a18bff4d1532e59b32d63d435d2ef53170178728b1ae448eb2d619 +hash_to_ec 265cf608aed03413425a2d0ee5b4b9175c3d862617003c1a4a5c6f500f258529 cd65c53d1e05901ec76c839dee6aed29b4525144b3e93cdcb67da8c81cdf321a +hash_to_ec 1de2c52d5c560048a96165be85a2d843bbe4faf8bfc18f80c2d9cd64f943a314 e002166565798cbab2b867aaefaeccf953491bd9469850b4beae761dacb069a0 +hash_to_ec 5e2c572841c3452a1b88ce76129130ff8065ec46708b1ea390869ae81a1ead21 a8d53a9806100cfb179d692ef1257b677a759f029f39e408ef6f3289be750a70 +hash_to_ec b05441496689e45c08a14c3d628c0de2de8bfddd303d617d9a75f1de8ab3fa99 d22a0efb56e8c9ed6f96fa6135dd6b248cb5490e335647e38a48a49b39545239 +hash_to_ec 5b5c31fc275f2d42209ce332e7081ea60db53bf09d939545db44e6cb44536c12 cc1b859699724bacd3b0ecb74ea5d44b2213f23be54f21e7c857810f5c43d3ca +hash_to_ec ee3ff180d0e0d3fd3ea3daf73d2c0ce26c0e35e9e4bd0b321cf4a7d64c7cb4f3 d67a712550129ae0c1378887bd9f7fef84137b3b5246a3be01d388d7a2c5d5a7 +hash_to_ec 5f41186832f49d93d185170f2f9f14f0cd4b738836d7e7e9b218581618b42cee 7ceb32423b6e4c3aefc526ff2ec6d4d99fcd8d8e038dcedfd2e05c8b8aa81d77 +hash_to_ec a6b681457e4940d2952f85da426388f896b82334b4d2895b23e9ec4a3fff813d bd577d0852e0c3fa2a4363b0a28027dd78ae2ba0259e687718b8dc2d165b661b +hash_to_ec 922973fd7ea410f0076d82a6e97bb63c8b93736a66aabf096303f0eb0a2d8888 adc179e786d2eb5a975c799088516be53d7c4845c1b9f739432990acbd21557d +hash_to_ec 907f6fb28477dabf87789b96f813213e7d1fa698b2abf721c3e2e1a93a006ea3 73d2479c3f3dffb8e17a5bb839e890f8dcffa5216d61828d012e1066718129f1 +hash_to_ec 662f1daf8a6294a6820139cfe4da5353081602556746241f1dbbe530c584ac68 e62d122e625cd27fdd9e0786c493a301dbc85d961c185e921b9920ae0a62e875 +hash_to_ec da6876618805f2276085dc90d46ba7f23d9fe1aad478cc5472e130fa79f5e5d6 8d9dab073f44079ccf8080ed0cc785593d9fe8c6443ce69103e2a52c88e7e006 +hash_to_ec ea2596923bee5c2e4defafa435859a47c9c83225cbb1d5f89c16541cd5733d71 3cb6b4bbac5b8803def92108110432ccb98c43219082e04264b2db5828e51c2f +hash_to_ec 27239325738e5edcc161b1dc26a9c9ebac4e70d8ab367680ccbe76ed9205d641 aeef0387aeec2dbe6ea1b97260752b543178226da49583d98fd456e441a390c6 +hash_to_ec d85ce8463cd9bc166155af7d1357593468644e201ea54cf026ce879dc9190c75 d383a5f9af1fff9eff5f4670fd0619e172d5218fb61dda5fdd8ba2abfa1e7a7d +hash_to_ec 76a4fae0ceca48632ff7ace170653e602937727a1098b63df7605780fd8e6dfa cf07b707f4b963cfdabbc7bc6c3cb9c318d70ce1a653d7e1e062d8ac13d6c013 +hash_to_ec f9e240044d4946058c03edd7d405706e054ecbf1871853a15e13103fcdc6427f 3f208831c2546ff2d69a1872277e0d81b87e04e584e30ef61e73444915b48795 +hash_to_ec 390ad2104b94043b3eec2ab33e97e53b2d22e54f9081ffa757ccc42e579c2672 fc97d550e746f227ce899983db248b8b3331e85b18d87c853d09b7409552bd8c +hash_to_ec ff5dfb093bac764bddeadd2ba20e882649fcd0a3d28b2f64b628b51d706e4586 bdf390e23de218148ca98479282213669e3fed3eba9fc1e83e3aeab439010494 +hash_to_ec b06f2a8cb0e0a5055b7c68f43900f3a13feaf4fed7ee01abc27143321487ecbf 6dd553db45813adf097c97bc494bd7541606e5ea66ce061be1f735237fc4d814 +hash_to_ec b971e25d43fafa5c0b514ec988571390777efc40011257a037113ade03e7e606 c361b71450012fb6b3b65f1f8ec14dbce3f95a6dbd6c88442b8a9f1cd4e0091a +hash_to_ec 3214f26e372584a0165673159646ee0fb9215fff96e35bb510473851a513819c 720754cb407ff72e42e3ed5b9ce614a14bcc5fae517d0fd13f23d3d07f6dbb96 +hash_to_ec 85a5f6cd50cf40b41c1cdfe992efb9bc1f35394dab6bc26bf81ce4d11326172b e8e09d40b8715e74b4f869553129b636d18dcdc0ed53282b7594dff65b9d39e9 +hash_to_ec dc5e0fca83f3e77b7d183c9a68f6b92be69eae4cea4229ebd58fb1268f655f72 ad0c3d65f149ba4eb8c7466a5f6a458e168d7ee5df21a7455f9e57d9fd3e1036 +hash_to_ec ad504d565e5446e127ac62a224260ed458338b34d0ae4a5a4aa3cb267d707e95 435b430f36cfa6b516b54357bd485079e278d9089b106e84243b6c5a6251c7aa +hash_to_ec 4a3db27f7203b229761e00bf2504456430a44ca0a31a45aa9e17ad035efbcb23 14b6e147502114df5e93952a1802118e206afca90e096a77d4d13e8f8d82efb2 +hash_to_ec 21165cb36895cea5af50f382dc0d50dc9b2245de63156c04a1bcac5c1a18ce74 d27362b710bf379d7c298c1d52114d7f5397d5abfb31eab7eb5736b9f1d38c8e +hash_to_ec 09f344d31b09785091b9532f633499bc123bf4563aca4643988b61a1b86d2fba 88fa3500360aa88f3e28200ea253f809fee8e86a5ebd3cd1c7ce737c35042101 +hash_to_ec 87d112c3a46055fc59efab3b9bbbfbe1cf36d50a688c415fca2fd73ba6d0d204 d0f8ae6945d26c79ce0980df5de1125fceaac77b6aeaa1b6a336e24aff338364 +hash_to_ec f4cd1983bcfe07577d8ce2bb3dc94cab70fdae98a46f844e03ad2fee98f8120e d1806699ba08dfa54fa2be6abcf15168ca3fea96eb68fde8794f3a1dd52e2a4d +hash_to_ec 5d98be065788b4ef5c73dd23e039eac2857b9d2965338ffc6d4b51c13983baae 1c57c1fee50ac08db039d03897e8d13981f5220c0db1074e61d255a30b61a0a5 +hash_to_ec e0e8a27e47335796a2fc1ae87a2cafc278caf186bb76518ffa75aca1112d18da 21f5b72b9631d195c30ec724fb4a0f37230b8f36c3a5c5915abf1a7f54d67c5e +hash_to_ec 4e80d7f9749bad865c5d9a46eae35b078fddbac2812c75221c8d886f8bed7d99 ac89c88ce709d5da796413a3c074ff115e8f2c2890235c7ceacf8cb95f03afb0 +hash_to_ec e42fb397704c76a02007a53b92ea52638042552cad29903a5d18489d6a6bc47e 7a756f2d3d9d169b199dd60f512387abbff78b9f8408897777f38738c50d8e04 +hash_to_ec cb3262e4b68d98e87f746a27683bc2d96295341785538cc706148c1dc33f4f68 b03ac5392b9b97dbc9e2936e9cb598ca5deb75a313578bf98f406f00d2cb2ee6 +hash_to_ec eefe72cb128278c1eb1fd1707ffa835e479c5ce87422ef34fc414109910c9977 921af8bcf43db3abd5187a1a30f073523c877368888326ebe58514b33302df80 +hash_to_ec 1433ac32a98733e43beb1e4a9a8ac2ef2256b62c03a81907188b3917b0529082 5fc165a2371b1e1b8b4a1e266b64b95f82dbb762f634923401f58a00145d5941 +hash_to_ec 0d17d2811222ce83e7a8b8ff4adbf960be6e6f116fb6a9b90691e896e2720287 d7e02bb9c8dee2fac5bc9694cc6071f07c1b4341c697c854242f10ba9a781b9a +hash_to_ec fd5867a1885d35714386568dfce15047dc7134aaacf773c925c2069b09de2a17 79d76a034b7f22e38a037d0d382acf6ed21f46eede9eafb95098a78aa0286221 +hash_to_ec 15fc8a000d2aae465d83abfa5766a1642877b778ebe10aea13718687533eb324 bf9d81060c51f76a208b890f23ce553732fbdeacc70a6c599fb4f0098d366cb2 +hash_to_ec 2257bf3a34f707acf49ad4e4b01ab24ff54865114c2c8f58278acc65470407cc 41472da74778f5275bb9fc9f29c931449b4ece3fff88dde926ce6427ddaa0eec +hash_to_ec 38ce4bbb3dc086f4594e673393bb66e02f1fec630de660e6e9381f004d132646 cb47bb4d85c4b0081ab01601f076a3abbd5bff01b1d9d2d9190c63a60932cb09 +hash_to_ec 0c36b13158b6fe4a968062f2ee9935d4a71928a326e1574e20a537a28d655140 360f0aaa916b4e19591c5a657d64a1bf6617aa3ec481c37d1d8eefe519491f89 +hash_to_ec 660775ef3eb470fca63826d664e0e5da87b67ec90dab667972ea9ff4cb645e54 cd2bf6eefbbe6b26daf80f175b0272b7da198fe9b70b95ba203b0b7a01f291ec +hash_to_ec c39f6548adaf8d73b802937f4d15d832b234ed25e40f6b67e4ee41dbbb5aa277 9f5d13838bc1f7dfbd4a0345b22f95f3874b7eae6c5848e58f5b889e8b3d78a1 +hash_to_ec 977979485d4a99efc2d58611b545cb72d6b75f1d1fbfdcdc876aa42702b7f14f 463d75ebe9e9b70d6b19c0681ad5f33c42cf419f07fe0623626d7f4e6358fd14 +hash_to_ec a04d228daf134eaecefe8369f836d8529c71d49afd6553f2e6e1eaa29a28fb4b e4c4b71155930213e1c22c1a5b9c31abb185c09606901758cfc58292466953d1 +hash_to_ec ac3af37b2ef423c5efc0e9eb807f2eba5ee6f3c22a0d35131bd196c6f0fd7a75 6eafe0c46063ae29cbd4dba9090c7d81ab9841257f25c5b68c644c28abd7aaa0 +hash_to_ec 992d79b9855d69957004b081e2c3034d95730a431c84a99a72711a3c6ff61b1b 9c1e4b659407509a0c74e3b1b168b84f24046e1d5ef363111e1198d3f2ee4ead +hash_to_ec ad23be59e63c5a522ebcb52af88544f346d9f826848eb876c39fc60574cb56c7 74272c7e8c776b5948f44405a01527061575603b5f2c6815d22950d84517623a +hash_to_ec 5227f1b2d4ae913edb6165f0699307d4409b60f5f817e501d52082f3acc229d6 9c6eadeb91983d07991a4729563642c9a737bc728b070539278134f99ba68b70 +hash_to_ec 4732891929b3c834c6b7988ff5f1e0a056fe96e14fb5016403b5e2eb2e2e3143 26273b97eed278821424e9c99fdcf2da0ee6530fe4d52d3513a6b6434a746110 +hash_to_ec 84f289b6caa3d58ed33789af5d5c87c198e59a309f9c38765d34a96d761527ba eb4638e2defed5bff310302e62eb89db4a02ec63c7d5e59d32d9439ac6ff96dd +hash_to_ec 57aa910b2bbf04b251d77f15ae6783b802cd8d141d566fd9f4554cc87d43d7f1 1afd62c790739c03f375a41897143e94eb5da85848346acb99e8e933fcc4dabc +hash_to_ec d09d6cb0a369d9793e6f1a28dfd2813287d59e7eb0f58f923ead4ec1b06ca37f 6f5bda381a1b5bc7a326dfd7a1ee543cc3643ccbd2f46f103acbf818ad7583c1 +hash_to_ec 2ab67443fd6b081ec3e0c6fb80be3f0e158824d18d340def031c1d57af0a2d06 9d753842a42d0ceffda28ba86f80f919d347774c2fa63c9ac183a4c9885fcd64 +hash_to_ec f46f506a5492060ea8c66cb1d28b4f1ef528baa54f1991e97397ab5f1b5832c0 34753e4bbaa913210d5ca058d67fe0e7fea0b1ac5d7500923b849bfc1fa92702 +hash_to_ec f1be1d69bb39e92782c9ba1f9c39a5d63f6ff5fab4348bab8d5469eb82e59abb e8d38787fe18e2bebb19a4e35b3568ee60a2b9b126308b13f4d24f87032e611e +hash_to_ec 0c06150d1dbc1fb81f3f0edd072e35b0f0f9cb2a48f169746c0f4aba0d1dc8c4 ba2685c8619e93920388e17ca51d62b9f855b8b53996269a337f23aa999d0481 +hash_to_ec 6b3461885653f5897cb187ec9ced8c084374456a4bc893a447852a680ed85157 cf2cc4e9545eb664d386226fec5fbd69170dc0506f0445c3e7936711d6cd2b52 +hash_to_ec 792d957b485515f62047f75ae3b3593425a2c6b3ee52ff9776337203f5a0c032 dc5f486e71dc07550054a48128e4ab3232a4ecd29e74899a48fb7d4992a29d7d +hash_to_ec cd6ba151d13fcb266f3094996177a1ec0f628aaab15ff286a6f73427670e92fd d8f4c5c53490a28106370f35126d2a2a4e820389a6adf350b4776841dd51e3ee +hash_to_ec 4c5166f019fba22565c0a79611a284ad06e638b2ebacb7438f0cc3afba36dc92 4ba4c56ecdeafadf7be5232dcce32f66399cc01f1b3833c1bdea507969c47f94 +hash_to_ec a151eceaf6f2baed03f12bf98c1caa5d23116949f0f4fa7f80d87688c2170d2e 634cf1a592c87d0ca8907a50213fd26c66f1b2380fa81e1413b6bd2a81beeb5b +hash_to_ec 61d18716bb2579946feb1760aa40eaa564333ee39ed340db6450d8e00ba1568b be32aa276958da2f0a7ce8798dc1347f1d811dfbeed5c0ac6819453b2276936c +hash_to_ec 7e223f9c8e5267cc954245fd7c7b4f7a7730157d5f04a26f49c46067a1137309 ac48e6979af1872032831658b6c7ffd9438d3bd291bb9d945368cae2e6ac3b97 +hash_to_ec 21f776979a8872ec2ebcd8606610f845c1f261e4c831f8215fa1ece62c8ab32f c9c7436c5032d09f874ddee1ebf88a5efd311ba0345a9a62156f759597f93c44 +hash_to_ec f0ea49025426a5b150cb94b9c2f3b1651ce55ce1e775e944935f73592463b98d 1f7692a0a421f40b934516259980b1e7077b5a370706137e434c313666d08c17 +hash_to_ec 6870250c655af9199ce4f558cb407efb10697824f53d531d0a5675dbef001fb9 1ead6efd80d296ba2293a651087b95ea34728f82d7fb07616ff109f0c9da4ab5 +hash_to_ec 887f88e8f900762b98ec92ecdc3d5580d18bab23f4896af5cb49577b8ea5a435 785a9ab59d2a6297c75bf23c6ecd0a38f94b8481195a4ce6cf82f53e1d04a9fb +hash_to_ec 3cf3468d380ba6dce4a67ebc5e3f42cf31970760956651455f2c97354fda1c7f f3ac54d486dba36fe88e715fecffb17df8be9455568d2c668a30d0649c907726 +hash_to_ec de4a849fbc673c3278231deb698bc927004f164a22f78ff2c86a4fed5304207f 81c82b737c4f78cee8913cdc5a697c4201c1ba0068188048ea21b2f5413c8096 +hash_to_ec aef0f3fe037ecbaa0cbccef36624e454d7c279358984b8c935182d34217f9ac0 7c65cc985dcddda7bfa6ff57db0be62e7098a81d66c2fdf99cc834e1e021b231 +hash_to_ec 4320282412a1ad0f070a06816e41004a59dcf205c1c5f664ab0aab8918dd5c74 da124aab1e57c0c1b7c33d5e9c61581f5073350e6dcff5478ecee49c057360c8 +hash_to_ec 9d788ee1edcc2f57026e5309fe9b53d25e18cb1e536675dba62a7ad390beb8dd 22e896869ffdd84c094d2c85169c8053e8dce09c9e4042e2ec3403f2d25ae2f0 +hash_to_ec 5fab6276ed4f8eca0082b2083d8a22a381f6fa17b8521d3b53e9fad65ab6ff8d 5931fee6592b90bcda4f8583ef3811e342059d5a22957291d0a5333f4e587b8a +hash_to_ec aecb43d5161693e7ed70f683b93ce43d6df6a2cc70af7fa514b00fcebf385f04 9a224631fcef89b4159ca05c449f47f83f8ced5896fbdd6e600e05c517b1e70f +hash_to_ec 0170fd0b27135aaaf0d3e7038788b39b6073dd39b752bce7b4e94b46a2f6be1d ba4d9364f5b6665d8a66d78d6ebfba834ba6551f349e9b31135e38691a8016a0 +hash_to_ec d3d73230249aed6bf8f2a0646556ef8e8569a79b597a67e24c3334321a34faad a0d3f5966ac8ed3a7514b8ee2f86caa110252e3e4b51b784ffb5ad62c2991e81 +hash_to_ec 07e4b899aec45d3c7e2d01cbc2d75335a698f23b7682c6678e7bbb6e9792aafa 1f9b35951fa6544ccf7dba1a095adda046bfa2a4d9a9a077549d3f721a8ac581 +hash_to_ec 4866bd71b2e829b58f0fbfcae8bfc98de8611341287147dfc4216122d8f242ef 0baa06307e74a9bda6ae53067093e2c8996b5175fbe687324a6e2fe401d60753 +hash_to_ec 29b95c3f4dc04c5187cb92c622b77fac7de2b81e7eb4702f70e561ca841509bc a25f644283d0f2a18d67473da5343096c7fbd67f2dc6dcd2184a8bc258691462 +hash_to_ec bb70c0de6e6e9f538c0181d9543681175666758d2df3bfb9c2e88df37e76a2d1 7e34027e5907c9d5256c4c32f33558d541884769449520eeb657a30443684f1e +hash_to_ec 4d314fd7a17c0493012fb37e90a7b9225f980494858895d9a83d8ed4328d265c b438b25d87f7275483366375c36f83fe46ac80b474259d2144781d25b8a678fe +hash_to_ec 607b4c69958849e93501de542719d0c6c76f8085ee533138b130058099f00081 c1ce7d53f6f624a8cf70cf306844a70fa714759e99b3c7dbafb83ab0713878f9 +hash_to_ec 51921545d5fd77d4fb21ad333c51f2e7961f433bde4663ce4a78c0a97cf57a3b 4ae08c1dac951d78ee74c04034b06b53d84d4de4bf7416bfab4e8d1698d389d4 +hash_to_ec 4def42a1757870d43ee560de9f46134a3680133a22b683e372ca68933d0af967 239870f42c5d06f2a96f4268647e2df12004825587cc4a3eaba3b51f83b01468 +hash_to_ec fd04b2fdfc2d010cc524c664b029c2306f516b8e06de3442776d8ec93ec6a0cc a95b114c3c32614a6d29aa24f78ac372f6f5e5fa7c9ed041733f057bf1711d9d +hash_to_ec 78366ad971d62cb80180513ed8adad48e379c836e185f4b77ba50c464dc3126f 713cd12cce99a4d3c3e81a9b6e4b0a0b1a38681de51ed1325a1f9d7aaced4a49 +hash_to_ec bba328ef7776775a2c225264373f8a0f4172ee2a20711e2df213ecff38bf3f50 d0497e679c88c53e293476fa5dab01e4d71a5ebb98e99e3f16716314a0698b2f +hash_to_ec de33d5e52566415e40f8c081b8b2a1256b6ab6df6a0ba13ac3edd071c580d018 e3910bb4b769303b751481455cc88172b18cf403b5df852e2f48a26b44ed12ee +hash_to_ec 3aaced698e6ede564a9c96db975b706df14104593eb68530e962c3b056d5c2b4 515abdb9fd1057278cb51704b1fcad054a35e98fb98c79d221e4bb1ff057be25 +hash_to_ec ec79fb6ee4495ffed3f71ca0578232c5e5e178ab9b12a581162d142c18b03848 5286aac5ab019d340a525491c6544d0ff6679c11075e6ee2b7e3276a9804888d +hash_to_ec 446cd6f3d831aacbac3e5a38dd156dba42770e07c1ebc899c7355bb6ecac7563 f5917bc00788b662ede1571afd06a9f3827eedc2029a2dd3d9be8aa7eec646b1 +hash_to_ec 4439125c740b3ca30e8f945afab00d2346871281021bd9fc2bc907500433d1f6 bebe3840e1d6dd4cee08fc9dfe96327b346cba2e2bf4f54e44c585bd3bfa7234 +hash_to_ec 98b29c8d4e865cf5ce4bb22cbe53d19fd08b15037d4c610c8e0acc7b1bda8a20 517d05c10f71ef9be242e0290f42a5d10414ee34e47f1350eed4efd79304114f +hash_to_ec db58e51166dde2dc32c34da7c64a72a01cf04c847131496daed3293ffa1d4361 98f310e753de49119f2f1617df2b3bd7828e59092953706d34af4e7f6e392f40 +hash_to_ec 1eb651f3dcbd7317ad2eb2771abf7f2543c25b8cc559cdb3a14457d3ae1419dc 7d43510fa20586d41580dc115a6a6ff8752f091cfa382e69bef5e02b5f6221b7 +hash_to_ec aae9891157f4481b073145e43ffddf732fe99122c31d304fcbfae1b63d4a1124 82eff2cd3743ddb81a7926ea758c347e82a96ba5174d2c49295916c0f2074c56 +hash_to_ec ac18cfc15e7a724ce503f2e1075c936491cf26a9862f575d0b8202b6d64ef5d2 6199d4b43298947d7097269600cb78a77d8c1cf24d118624972a20949619fd11 +hash_to_ec 9ca2e77ce048d105c369a106b321e2f77086eb2a972878dad2529b9bcd69a148 ba6c9b42976492ba7de8d4a02b8eaa52a314348a5ae8feb4fa1f1c35f07519df +hash_to_ec 001d243bf34732a8cbe23e8bd9756b6547807c3b71acbf10d4843551c42c91dc e95874c08499443f1703c0cf97f3ebb3607ebc26faa01954352c7d1a7e570cd8 +hash_to_ec 90c052ece0ab8a7a2c0c3ba4b83d9cffa224b048f0c075f6fa6629cfc90036f4 6d054c5a8f5e2788fa91fd23e154574f978eb59d81ca09f543f0d6bf4ee9c9c4 +hash_to_ec 3533c5eadddbf27ee237030b97d1a194c634fb2db891f434373b214bbd8d2892 dcd25a4d1f8b4da796c8ddd8219a611efd9c2be12422ecaa2bcca7638b80d661 +hash_to_ec 7c85a52ca78c1c8e0cde95370386dcdb26e3c3b7d3091a7393efa31283870f50 1ecde36a1cbed9f7a30f792827b07fe63c4b8585aef428ce80aaa7e1dc7c98c5 +hash_to_ec b6e3e5c1f0133f243ec194a6c44ec5436a2dbbe7f92770777193848e46d96353 e20324b1df5febfaea7ab5bbc1089e87bda2e3acb9c051ee9f1626e8c2cb4da4 +hash_to_ec c149fd58d9c128b7f0d8fe38bfcdd3a94b6708b5d9b40cd128c5cc528b2dc279 59052e054ba79d32c970317376b49a14f126842938c735918fff46dbe80159eb +hash_to_ec de942b9846326b5caefceee7b7651fd79ec11fe19b92d8cd7334b9818f6a0be0 748de75d1bed492d73338dd5efcfd899da91d20979a65688367c2ad131b3cbaf +hash_to_ec 0dea577c495c9f24d8371391e953b4bef9b27b072c8820265a45aed9b8a873fc b148dee322c5891bbdf8e34a183547005d54490096aa0128d12648ab76ba6501 +hash_to_ec cd68f4dddcedf7206f4928e50477501c1efd04ac95c240bcdb1311abe7d782d8 3c10e30bbc09074e0e3566c2c85eeecd0eb619fb4d71c3ce09d2d7cabc2ca88c +hash_to_ec 6f2cee19fa0019fdd5680e9f5bc9a0a19fd20fe801693646eb80029cd7215028 0b38c0dabec6c4b039868bac1357651a8f9a1183e083ac5bef5c497dca366bfb +hash_to_ec c84c1470faabaa794544bdc8cd2c711465def027442c68cbb51d8b26a173cd8f 833512e67f5b65ebe8cd340f766f3258fedfa2e76bbb023c34ef21fb37526b91 +hash_to_ec 47f244cc2c434906ff8a3b05cb0cc6237dfca4df80f54f737ed73a8de6c916c6 e911649d4ed10f07ed5235cdc95affd3c5ae0989fdb9590145f1d6871a7f8aba +hash_to_ec 996ca5f5897beaf2cac782dfae6d897b54184850dc4e37e1e9df03fb524d1dcb 19aa3501778956c0055d3048ae0cdadc1382df722c126660ec8aec2d7e5adea2 +hash_to_ec 0b71fcb114db2922d51ff6ce2243fee94e0b2b325539c7ca31701a291b9bf893 9e6f2722a8cea47d58fe15ae46532a770c6a7dca974f0f396fa9737828a6b153 +hash_to_ec 5af82ec3e9e25665a07e742a0db3ab87cac8f9aa8765bf50edde6b0393703de4 e40649d2995f93d7736693557eab99be1edbd95c6386fa5420efa80fba3dbbd6 +hash_to_ec b2e631d8f1e4b2162c48700cebb5761d841b45a627e959cf13c752ff82e427a2 1fd7ed39faef882e4abd1d96d80b54ddd1ae44912ffb7c405b7f158da0853054 +hash_to_ec 042ef2fe9c3cabe3092cae589b3ac93bc006c44191ec547645ccf67afdfffd68 33f5c56ebed5de852c2ef2fe747b75f342eea021925e5875f0b09d650e6947ed +hash_to_ec ae720fac843a3d0f8de95a461221e79715d7ad4be06c08f8c59a24526ccee2d9 10d9bee096f384e34e8fd219e85de49d41a6ca5f7b54b46dcb395bc130dfc7ae +hash_to_ec 580055cadbb839eee43d38dddb20d970ab5c1a9e2f05c553d299cf495774c09b 0fcfd52a31c7a7b1d983fb14169f3265d8c8d19fbbdfd46c375399711d567f0a +hash_to_ec 584501645cd7e5c216392bbbb49c7b23f3fe1a3082ccc4f3729fed43f3bbe3c3 252188188f9a5972511f5f933f3d48f90f946d1a796f1caa8ed1516e239a4eeb +hash_to_ec 916336f5840c0e24e3958f76ba762ab294d105326ea24ccacb7f2a3e3817472c 37b5254d2a96d79bc8c78aabdb5851b9fa76242c8fd3fbd9c8c806c6b7030a7d +hash_to_ec 0e6c59e4c61c3e79b7e72c49fb4e7c5bae309af86328dedea3f8a0cb1b045ac2 7b98633ec303ee441fd3cdff4a521054b8c5b42e1f5d58984bdb72844c5d7229 +hash_to_ec ceeb597d189dc18be59daa68d39bfc2361b820524631bc05e22b78ff775e96dc f17a79c7013f0b401166550c2b8e5713c9e5a55ecc40a7dc643634f59c544515 +hash_to_ec 7f73d0d54237460f950433f02b23a8b73fd686db4872dbbaccfd93e4962dbbc5 93b62b662e7b3ccf64d094a1cf44ac6851bb527f46bbd30fb9162129c353ec5c +hash_to_ec 40e7beb6f3eb1bb581df2b8d58918c2a6016e9df6613d843225e7c9079a47fd3 5e116e9caf7b7a2779417a0b1cecd3110036c2f5ec794813a2ced8fa0088cb62 +hash_to_ec 823cd0caaa45a4421417be16003f4d7fd11cef68de190c74b212c023b362669c 5c4ebeae5380c5a8232ac3f68e75d662dd14e18716c9e5cc1cc4426f5a0bb430 +hash_to_ec e9019d829c44b098df7c656c02aa87e0741b00321a9d95f4265c3ca423305efd 15f2ef64e84bd278f50788bba845486f63a50192b10175016a1c86f2a3d29933 +hash_to_ec 0c9703fc2e020379534b469c526171091147c59d2552a871823c11821e6c5fca 609b3fd21aa8a2754904e4cf01c2348f775c292e9d5ffce02570a874479b8913 +hash_to_ec 8c2fc34c9419695fd9ef7b869b3420490a349147698ec8b7d6e1bc7e1c85943d 95cdafab2aaa3243c363d300edeb4f3620f4fbf63aafb294dcf374f8f987d6e6 +hash_to_ec ec520fb3250395785dc104e5b914ff88a4d7f9da7e07da6c0830b2f3a1c2560f 9163b02b08dcf41b963d4f3b3deb65220470762d42f0c325ca572a7da72e2030 +hash_to_ec 2fc915123fafdd77a6aae076e21cd73d8203a25b53ae05e47bb7950c7f793b13 d61ec8381f7ed13a950420308881c3a6acac740943d0a9d0586fa59b4a6e2c05 +hash_to_ec 35f7c5c330d52aa67e61bf86c066258b7bcf9c5e03155cb9eecb78392fe4dcda 864a1ee8d668f7dbeb811a9a11e364f6c3f2508b913f0f0c491c0610e01a6dc8 +hash_to_ec ddf2f2fbf1c0c8ec3873285ffd498b594cb5714c851c27e1397d386e12bb81ae ff9209ea3b1af015e90b4cfccff2ab57b159b72a49b79dbb0c071423e2c9cb77 +hash_to_ec 4055041ca9d0d4d76dcedc80479a2918e4024b18330c47f6a3452ad99a4efe28 2cb567c3f2ffbdb2bd918ac23ba3085e27dd59dea0b21fd9466cd045f7689163 +hash_to_ec 7bd477de8c24b721bdce47fec219e51a186181af131a82b80fa5edc49c77e409 bbce754fadec38a59acacf710961157392c68994d033828f70f67c6232766987 +hash_to_ec bc5289dd1c81b226aae0d368fab0097c29c5c2b71ddc7c4dc0fea98db0f985be 6ea7f2315f5354e5a6cf0c7821748eb2de86380b765a5e01d894c6637909dbbe +hash_to_ec ed958e98cd29d662af3b22b698a433ec5e3b7a2ab6b94be01a7964f55b427c27 9238905f8414f4f05b1b6d6693810f74aa7af3c9958da26ee4b62a75959e0b84 +hash_to_ec e1763c0bdb20cd468a13560fd0ec0576d167a3d3d62523067e8e23a2ece46254 7410562eca3616e5aca81ad6bc87329f252957bc2aaaa0c8dcabfa9be83a0b8a +hash_to_ec 79e99afd53a7d95190e4e3f7d3b7d67a71071f3894246f7e7885136ad0d19080 59974987b4e745e68de1d5d263069c85c8920a8ef39637044750dc9c138b3932 +hash_to_ec 8fcc3267c854025ec6c917b696c1905e23282ccc042d81ab3e37782380c7d86b e4e0389a919ce3d76086740576217f072b4197e023be71f41f55ed24ad275128 +hash_to_ec aafc5adb032b78d5a7e2047b619544d0d97236d79002d3a091897fa0c48dd1f6 c00874a1aa6637071b0dba73b10d35607016a708f60961906acfcf47b7617de0 +hash_to_ec 06713f84fa6e3bcef13946203f85dfdddac7a471ccc417a65fb929c52d3b6ae6 f37451cdbafc995617f57403dbee12a058f2f1c9d4edd206876995148c6264ba +hash_to_ec 8e3fb2c5459ad65089ee866352d5df1ddddf3444481368cbf0eafc202a7587ac ec7343525960621f2a2217a41acd9c78b7ce0fc7dc07a97576030b9f7f6468dd +hash_to_ec 85b1c517e90e61b56bc0ad3e17c87d7723eab3ab3a543778bad51b33325ebaab 56e95c977e72f42929ae043f0b17b2ab1ecd64b05a1430d92fa065b96ac5f5e6 +hash_to_ec 9cef8a13f250cd31c9f4cf7b4a72a2ede6237732dd8c6333125577e5d6839cbf 60324634cddddf737e97dcba4bdb4fa4d848d16ddb064b3b197b907747417f28 +hash_to_ec ea6a619b21eab54c9489cb6809d22a3900ebd6c1edc74bffc53b22850bd223a2 5f3e4e62352652b07ca9dc76af7d33829bf5f0b7483f0194f51188c3b64a7256 +hash_to_ec 290c4617cca3a49b05bdd80d98dea8ce49f8cdfb7405b8562943a5721290ab7b 43161abc191706a3f02425f4bd0342524445c3a28f902a3475ec2a9302771a9d +hash_to_ec 773ac67fb198cd86d94f52d8949d3a44bf213980e2e9355451e65d5d38886a77 4d3a22f05a68ca903941d77eb83b6f999389ee22c51e5f92e64b6fb6a6392c2d +hash_to_ec c7978799ecc60522c1d2e102a650bf50458fedcce97029592c0722c418d83f34 4a5640fc59a873e5fca9e1684b9d02b407624458f93e710b3e73ba6b43c572fd +hash_to_ec fa097442f932e1e25123d663672e2de17b2385b8c41a6a509c46f89e324a95c3 8e0f3065e6a566fe9d44eb70298c2fab5924f843b51c0e6a32f5afc6a62d37ab +hash_to_ec 3cb8432db7652e4a69e97d23606afb931c5fc5501aea95e9899948f2bfadf2c8 6d0469001caf02061d624761cbf4fa0d4d67eac8838353430471e61a50db05da +hash_to_ec 2efcfea92b31b0c8f45a10e0b74c5b35da005dd6f6426d8862b3b79bba8b3954 00bc5a76c0b3b306613213f5e85e4f7bd9934c1ea634d3ffb862965779265345 +hash_to_ec 249715649583e496f21fec59eb4c35553761ab82b3bf85ce18e67c184ade4e2f 7be3b638cf81014f12ceb6f2a032910ce3c87dda3167cb36467e743da6f4080a +hash_to_ec dc90411660b94a8b846aefe7cde207cc29731aff48fdfb0dbb4cd834501886a4 2b55e784149b86642ec9b8d59d19459cdcfa8f1daffeb5017ba84f6bcd081aec +hash_to_ec 6195388d01292aa48333f6755d38709afefecbf0f35b8de1bfe2de256cdabeb9 bb47f9bd4a8ef9faceae92dc3a6fb6f6913579af2ba300797db1945d00682422 +hash_to_ec 38142360b3b1f0933990a5c0e65180d77c2923b2aa2a3f6250c153c8dd13d486 6b6801db6afea8bde4ef571dc953e32c2e810bbc90c8f7b67a2c0860f069bde1 +hash_to_ec e08321679d23a6ce431298f362cc56ef5c861913bcd2cb56b77ecee88016b1b5 3f56919fd6d84709aa54a92a036fae6e4021f87126792ffc293c8efe12eab64e +hash_to_ec 3bb83f86cf5c74904834b9ff41a595a4a4a49d712a05586c23b8fa7e8a32d831 6cebbc1b417f99976d3f0cef3a391d2f3f6486b339e4370eef6cfa4bed1c682e +hash_to_ec 0660fe08469488e04b7988cf8614394478eee0dabb319a17e965e6a50de9c19b dce61553d32a651791b8ca3597faac2ea597e09db67d9424b9b14021052b675f +hash_to_ec 8ec7a7263f0392f9b1916d8dd635fd1e82f36540f41ddd9a5d81d243d9c2e86e 5fae692dc4c3c17f614f5beba6eae669fc8cdc35de340265d04a5e19fa8270e9 +hash_to_ec a677e205f9ad968df137584d4c90438505a629dd6b5f3c4f822564b41c8f903e cfa9c19797994f671d472f07bfeb3bd8035646520017ef8c9292b867f236d152 +hash_to_ec a1173296e05bc70d6ecaab59f1a6ae55ba8b428d5ea9a3a5e4217ab5dd49182c a4b6ec11ead4bc5c714519eadea506517c5ee1d0b4b16884974d4f24537d3c4a +hash_to_ec a768c94bd1de90ccba4ff6a33d17c0dff5a118da9731e40747668e1bd959aba3 4072763ae107e9f4ac1f64b17c7cbc752a7e476252e49deef9700a3478108151 +hash_to_ec 26ca541d77ebc99dda5c1ecc21a07010c2945ce9d5d18a2bc753fcc4156ccc00 035877d659436cb236b985dcaa7ad05db332a15273f1e784305e6fa708c6ccc0 +hash_to_ec 7e712cac3f9d64f16ed07dadddf934b1b9373247e0e8d350e5a2551ee0b71420 b9a5d8273a8a001058486fbb4b52887f96201aee7c601436c30681277b7242a1 +hash_to_ec a066b925656c49345f6ce7a277839345e582b05f1188debbce763eb58959658b 41f1e3957e55d1e1e4bc8386c9c7eebf6ee30c82128cbbee60de92c654b860e8 +hash_to_ec 34f8f8f575805b075dd65b5493b737b1d8b6ba122a55a25f7053f23da8da146f 3763f0d4aeb144b20f83826311985276c7597f541ea4ea0011d0609a021cd18d +hash_to_ec 311fe86024f1a81034d6c9bda14c3d4728fdbc030901a1ad577bef3ca5a09eb5 3fb85344506d29bc22bfbffc00cf4639539401ad4d0e671415dd501bb7b0377c +hash_to_ec 7773a0b75cd080f556f344bf920896b4eb691bc851e5a31fc9b253fb1ae84d1d e99951967e8d017a19d4c9f08a0cbbc572622ebde69a5699c06ee7b02132d6de +hash_to_ec 2758002566d15055dc51f1386907f7104dd011c8337f1b36fb29e881bdd60c90 4aac7d86225ceb964b2e05446034d37ce76c04c08015b98bb2766d71abc150ea +hash_to_ec d65ca0d89c7835d5cc8f185c6abefcc68c0fde058a516ddc19aae133f6af548f f1ca676a3c94613fceda48ae9dd871f09f5cd3a1aa5d5c7b463f74e23ed891ee +hash_to_ec 850b0f17d7b21e8c1a0d77785253b05737045b944364c157b942d094e5b2c8b1 7ce62cc8b4a86b3d6a3741ce20b2759dc6186c52c45b7c66850a907c5e262783 +hash_to_ec 4effe269544ceb8e2bfca70881c8a5de7fe2c4a659b04155911793a5fcc51cc4 12f2b13cd0bd3d892b7eaa87eb3a618c31ef5931d1d31a37aa1947646de94a67 +hash_to_ec b6aab0f83b9c3eb43d58839969d0d9879640e189d67405f0dc8ef806901f255d 24ba57b1da88576f70b21693ec32db66b63b305f9207dbcb75c7ba808c8eb7d3 +hash_to_ec 74bfd0713956e8454ca5caa468c654cfc5e490603ba11f6f7fc7f4b6b71dfd03 667448dee26d2157c51e379effa365be748587117097e08c9ecb579c7c661731 +hash_to_ec eb41996da142f51255db3a89113a0d424ea610bc09e94c8af5febf7030227c33 21f283a221d71a8406464bd1eb2e4f2d74fd90663c0ba36b490f8b6022d5340f +hash_to_ec b5453122238d7992398e95e2e5c3497bc50c68ba56704f304412a91c2c5b5ece f6a4856abeff51ed59df4ab9ac1aa91099bec23d9e679edfc9c3496e72e4e0b8 +hash_to_ec 9e7ca0757f3342408b957d8a0848fdee5938251626670255ebb0a75ae1018717 3f6f0c7ec1cb80df48f66dde1b6aec8277c4ebf5e1ca1a8c2b0e47b7b6aba02a +hash_to_ec 6015c1386bbcb39360cb59710c6c850682decfb0136aed2ee178e8a8ca472a71 a1af7d00e8f3a24f6006a9c753ac8b7ef03317cf601fc7e15023e1175948fa89 +hash_to_ec 2fe27d728377240be0fdb7cb90aba314f1504da763282635dd105853a9edcfc2 318edec70eb54183e22a41b61ee8e5e27a34c02955d4a8d44aff40ae3f311c23 +hash_to_ec c81801b58b99d1238f2253651b143d8e3ec4cc69f0ef5fd5483d216e1319c823 4854377819a340e79b4d2af7f90432755895fd54174d2e9dafad9105855160cd +hash_to_ec 2de934f0e0bd796d4ed28d77df071b03e6d3b394c81599b31a196ce6842a7bca 6adb6a2aea1acc2930d699e088c94345d42870ccda43388d7d9cf6109be38c3e +hash_to_ec 5f9c54e6f11d187f2c65a0d83594d04654cbaaa53599351f8cbb146c674ad257 7fdc544dc786e8ea11bdd7dccad176dab430eb31b14604cb1e24070e3201a70d +hash_to_ec cc292ffaecc774f6632caa8b2e0e248d4406e6d729cea274c3b6d20bf8a930e6 eb8527f7bd6e79040d1863e711ccc11c400c31b36859697939e7914a99bbc8c7 +hash_to_ec a649c475360dbe439c1ea5465c2be91b6782355736be456c6a57c6a11c9fac41 520c7edf3934043d0b7fa94fc5d87e06819c97e4222b1d6629492c260b117079 +hash_to_ec 68afb6887345c8491a395b777fc2ce312372c6655295faed65742ac69bfe052f 1ebb998c458977a54542240be0aa0d2daf3aaa522d894a2e4ee9de68e743094f +hash_to_ec 335979b474db10d62e368bd14c7fa3681e2aa3310ab5175d47b9b23d486113c9 f9721c0eeaf330c3aad36e9bad6d27dd3fb84910fece92ac988f76980bbdf2fb +hash_to_ec 51d604b7c4fce9dec9dc73d4dd356da9a30ab8c18e1418b4ea8bd6fedcbd3e70 e12266199e1a20a7652eeb7066d32d64cbf077b6ea83bc2bd6a183e844aa0e89 +hash_to_ec 7dbd87d021bc32225b48d1ab5a6bd8aca05afac0b3dc79cbd62a1d642af5edaa c3c7700054a480fb614b9759352dcd07a76e9b45301a28017d7afbf68151f6b0 +hash_to_ec b25b1770086a80a12a9436bbc88d3e63193ff1fd718eec1b9cb517d49daca44c 338917ae018c9a5e2ad2641df9aacf81203779d36e9e907508b0ed57be1d1d24 +hash_to_ec 17678774af217b60619dc114a83a40f7b1e6d10eff3fdeab3c6809a187236568 eb923911adf2ec11c4f2ca2118bef09607f59f68806b3a4fca53abf97ab44719 +hash_to_ec 00d75c31f4477587aa51543eabea14182cfe3289b966eb7cd40f54cc1027da15 40f56518ca0dfd937acf068eea98ea956d35b940b2251edf5540dd12ee262eed +hash_to_ec 1c45da028df365edf5ffb819db00f38ceb10fa687d629f958d653c7f77534d94 c6477d30e3bf5fa7baf6176b04cb8a7777ddb2b36fe072755fb490421d59873e +hash_to_ec b603b332438baa891923268ef257439a437e5b3de2913614a60e6428a17fa53f 4b9ede87bbbe2b907385ec47a88d18aef57ffbf956faaf725c93917c6a542b03 +hash_to_ec 15dc605f26f9bcc7f9e0b1989ab3edc37a8e4fd9e688ed051237d89ac8773407 deb63f280fc9d46874ffbea0517da9a0f4e9d0313dfab857c2157886a7109848 +hash_to_ec 92cf56855e0531ce7cdf4e564130b17e3eb2af2d5124e6a50b612e9bdc1eeef4 aac1bcc14a575ea462f6b8c11a2d9f1c32c19a23d0f24f67f03d744812c1b516 +hash_to_ec 1fcc8223cebcd44a69a187b2786932bdecd8c1ce523255c1c51826781de253a7 2b51375f7de62464d2b69c816b520b4cb5722de9fdc987d1a01355901a263e76 +hash_to_ec d4ccbe66d21c8cb2205ee3d6827081d7a2965e11a6c2e3946548c313bcf5fc18 1821719bfd77cf97c4f05142067215a5aef2ddb4678d41f4c7f78e36bbb7399a +hash_to_ec 9155e21d890b4318328fe8b60d2909653d228fb6b445d87b6535482cd47427d1 68e1532d73abe4b7fa974587c23a4d6397efced109addb98060acae6a25fe51d +hash_to_ec bae073e47e1a5c30189fa502eed752a6103bb7912096370e2656e537821273be e22d97b4c60e6bafaeddf65146d0ea61458e9dac828ddbd4e44835ccd66d045f +hash_to_ec 17f992b69e3a8d39e250c84d3cd202b9953356d4d5efadb46865b65ef7fe96cc 0cd814c46b1c3d502dbd246affa9bf5a75bf4d9f6cf322b52e60761b6ba363aa +hash_to_ec 1216cc5e4dbd872b783181fc19b8c838881b8c7feeb7b679bfa700c5f00ff7b1 b99f072e8851faabaa1127a4ee934e88d5f9cac68b81f55d2ee033a163e2dd6a +hash_to_ec 999c16cad24b06bfd38f4aed3fb09e472e142e028f15a4592f0b6462fd067cd1 17637999782ce48fec27ac84e49ba2c9fcb7e3a9cf57f57db8edc63098a47e40 +hash_to_ec f8d8d410727f698344007fde07f7bb260eff705a031fcca93387c05381361a01 4c33cbdfca38ea4ee23d551846a141d57710f8082048ac5e8369b334c2306135 +hash_to_ec 077f8a2e54ddf88b9236b89e68d4b1b047fa077798740bf878b6d56d98d1d0f9 ac4a11b607cf700a6c764e162e1cc25248977607c525fe9dd55e19a1b46e22eb +hash_to_ec 20313c03c1b819bb5488ea1fb2b3043ba81e5a555dc523c2d640a626207fa68e 03267a7f7b7425d53802f003ca5df679ac1829f0abcb0c1a1e898f68585227d2 +hash_to_ec 92a9b334780a36990057be799cd2d96d9544b3388563fac448e2a8e61ba5ea19 bd37ec58e328c99d201b57fa1a548c528238a0d7b910d42c9680f34a41c14366 +hash_to_ec a285efc66505596d3af4f9de46794bc83efffccbfc199da6e9d6d127d1aeba1a ad4d384e8cf2b9d2b25e8163649d741b7bf85e5fcab8cf9325444adc606c1257 +hash_to_ec 2387f35f91c4fd41ddc6cf8680a45aedc31df62183a57f51c24890c60cec6349 79775bfe9bd5c3a9452b3d4efa04a5d48db34d4b47caea15790ea33b3cef6d4d +hash_to_ec 964aa8486223d4b70c8b8fe69145525bd91ef6628e1c6cdd2ce0ba04c266f9c2 0c2ea869079a2a6765a274e854deba614a929631d603f1b79a5cecaddee7c60e +hash_to_ec 5f938c827abf86e0925c6b088ed94365f866f2f661eadab30b499b26be70290f 6c8f66e1f8bc78ae33de41da3f961d660eaa53433166bb441ed54270962fe121 +hash_to_ec d6198640d9e511867fc6f6eb733728d4087cbf803cd6fda69743415dfa0a0b5a 64e8400fac1536b3fe7de4adc715dfb14be121c5fc34de30976c7eb3599d8303 +hash_to_ec 3a445240b4151eaf074d6be3c50bc54fbb69db6f1eae09b6c146e81f87d2a70b a11d468dd182edda33036c14937c692c0f61c2c539e299c7d2ed9962f2a51691 +hash_to_ec 677d820274a39f795af23e660a8fa619f6213d0c99051f93006a472105a1da94 07d0b8e8603af2efebde7eb68d7c2209f6d13842ea625c8156537367a1111ca8 +hash_to_ec 7bc68bb177db418974a50388357ba6c0c7f7d1c269c6c7e7ac12013aa388b8ff d13d1f354329b8c7c55f3e397d1e66efbde68ccdaf4464fa5c3502fccc72a9d8 +hash_to_ec ace27affd9eabd46e85658c00b2d1a187938e70aaa0401ce2645e1faadc9c288 1ea58ef451848e1d202d9cb5513b38ea1fb0746e0d6b7b70d471236f05a35b34 +hash_to_ec 1ef472c3fbfbc2837ff34ce85947b507440b9c77f9c385a3206fa44f503f8fb5 03ae78eaaab4833db19a9f93f25415ce7a7936727a21188a1675ace048a7ccf0 +hash_to_ec 16a1ce7f2964c31a21041ca6acc2592af6404f42b87ccf79ec5fc30a4646cf27 c8f41b6901241ce62e5f6f4db7a6a7daff3f2698d8eba74fe7c914ea32dd54e2 +hash_to_ec 0230af5b96944486c84067b29dc71313a75dd6c7d6b5478e5abce4c406980195 3659c56ee2777bd01c6daf851814b992d3f300cef76a38affb1980ec9321a987 +hash_to_ec 5f7d9ef2244122bc256f431b58a53f348af292547b970214c78677d96748933b 5db8f5508e747947cfda6f0b9cbad869f3c8639d65ea34ea00935c7026ca990d +hash_to_ec 5176379bd48f34ef5acbf25898ddde6fc2de80783d71d01dda12f38a85e5c4e9 58fca0cf8f3940b9741973dd63fcb7b6e42ba766ae6993404a5e5ae0a0280471 +hash_to_ec 8da2f6c26bec3eb9d0c8e54d7169a780b4eb3a67f60a7be45b3b347950c55f19 79f60229175d19f79a04d679dece3f7d58b5aa57f0082398e7fe0c939b45bd1a +hash_to_ec c3a5d8dbf5360ae0c5cc991ab8e0ac2b2979d08f17979b7a770fa4891654a45e ed2a715a0a23ec29411c5470ea66f39b2fe15e90a19759953950c5196b8490a6 +hash_to_ec a60b0b829872f476d91eec85e0288d19768cde0315f3ba044dd9cf82fba580be b733fc3c88842646786a30be44b7346750f8ea40547a468653755ad818985ce1 +hash_to_ec f307d27a0e5211dcae0fab62d77090ba9c948496f2a472f63ecd6a10c5cc013d 65829b910ae365d9e181d7cd807d3c58dfd56ea1ec638d3335958432303ecc09 +hash_to_ec dd5b8c2b244dbe0f879d8822a1183ff8c71c152dc4541a6d9e263cf44853ccc9 59a4b366c18ac801be8d1f46a40a79400a6729811652fcebeaa484654b15b570 +hash_to_ec 98e5dc245cedcc864285e40a6453fd2e443a687e0695e1f36887168dc5a2f145 0bff39a74b094840fc42d3c13a2d283932e15080864dd33af39e111602dbcc6d +hash_to_ec 5bf4bf9d0514ef8f69abba93cf993c4731598e9bef7ac7d1df9b5cd868b05710 7b9cee0d41a846b4298994b5e545efe88725b5d8c4c45deb22f7d96f980537bb +hash_to_ec fb3e43a59d168e3781476982bad023ac7d4d41fbcff691605414e97d0a3c5386 be4b26e22f23a66a23cac5fe29de6b6a8a1d0c1ea8dbf355e56f1ac1caf77851 +hash_to_ec 473c291cbe26573b1bff53aac6d066f81dd729df3138151bdf4beec11b72dedc ae3dbe401579f2981288d1352e189836e3cce30a06867f3fd2f4c2ddaca46512 +generate_key_image 1570eb695fa38fa7c395ddcb90e53e9b4d366a920e9c4b3ec988807d6f21914d 8f5b3b5407d40d99d7c1e6b61b022b2f18878c2b24d6d247dbd865f6fc80400b e744a16a913da96bc29c5197d01bb00c4e59990bbccd7785ef413d98cae1696a +generate_key_image b48dbda2574f5c4f8e65c8e826948be67b6ab6ed0a46876c2ba74781eea27095 ff8c20da1d1fe90e130c833e98fb8923bd0acd37cc67f06d70ea0807711ef007 695c000885bb51a7f3721b668ad747d6f6dbae02fa2c1816fb8bc0aa47a50f09 +generate_key_image 780d3ade933ffb95d4fb4ca92b976547c00aa55078621ec07bbeef306bd85a84 a6878618b8296d349bea9eda9c9d2615e91ecaa34884b4e2e0e28596c7ca6107 be757962b84250b54bc6127bfa71fb59b1464282fc2dc0e9f39d207daa089649 +generate_key_image f9fbe125e540273849d93de90c7cf208dd0047e69b466bb5e699c4c377308271 92822dd0f80bf92406f243428c75457ecc789c30f6f27b38ab1b4d884a2b5408 28f864dce34cc05dd8780da26eb731bcd09638b90294286720f99abfc209cc0e +generate_key_image 0f3177da65d5940b9ad76758d31fb2d6514e0fa1c27c6175a962c7af2cf81646 cd7869b98766bfc9dff453e95605d97d243cec6dbe36746dcfabbad3fa37dc0f 6aa56de93c3946a875e169a935160ea813d9827363d88245618c3378821458f8 +generate_key_image f0b05d2832cf1aef343ae9ccad6eae90ab1410307bd89a2c253653676bb80240 1461243ab5cb04f80d1911a8df74695a5d93ce54874486d22d1b3eadea01d50e ccd6733f3fdf89066dfada611902859dad23db726649e1631e97c78813949771 +generate_key_image 8acb3a2dad555d73c46bb77d06c82cf8e28110da01be061506704e0ea85c514a 35e417253c6b38b72e9fef0bdcae35355a6ccb488198585594287c1b11b7f50c 847d1a6f068296a9859f99051b7af56f05d0e2ed3ef75b113e906da19607e9ae +generate_key_image c6a04322f13531adc1f6fa9262a48c1d73a8cc9d59a1d426fcf9f98e7f72f1a2 520e7d99e5dcb4b57697fdde6e3b9f5727573817e2d5d8ecc152929fcfb0ba0a 5d26cec81c1ed4cfade0f66cf43ed8403ad7feac89e6de9b2e4f2299683bcd78 +generate_key_image 167739cfef153151cf7174b0460810996c4ca02f750ba5a8647315e71f144674 1f7de34fb8aca5dff5f177e569868f1d522ffe3f706296695897942cc7d41b06 d8ee3d1ca67c1f2baf6d6686675157e586e74d3b427c74413147b73f55cabe29 +generate_key_image 8c0a31aa718b1a62da8c19af6a2a4b1ab4198c83ef3d8971cb022a20da6525d0 7692f2253ef30c1dc82f2345f11d66a452ed6bc9d8ab06d3b685b2049855a50f 59e703d0295cf3e2daf2527f7c38a55bd927441edf248d34aaa8d3ec852e6e71 +generate_key_image d1248b0ba86bd759e038a466930751c316296e8e536454737f11ec46672fd7a9 cb2426c2eadc68f9623e4181d4fc8c166900cca9d00c7a2636ef85da53322703 285fafc2c80ec1a7a4241b397992c5a6215e1ab8b3d6019d776c5020dbfad307 +generate_key_image 3c1ba871c71681c34e245ea3efdf97faab6254e295801d217726b2818f5058fa df0f82275fb35a21d788e04300618a371b60d9fc1cd057f300edcfad62d29604 968b448056bb9a441287b9a803b88f71b6a386ae94ca22c0593cc54ff11fcade +generate_key_image 6debf44c46dc0060f266b64ca14a66c9d8624cef083ee3a8cd4d7d7888f60ac8 27c293ca542bf2b913652b85f4773dfb181e690e00dd08d07e3c3d15b7d79e01 949f9e654328308f68527d1dd59d07484fc826355204685866275f1f46d0ed21 +generate_key_image 22040390d7de3999263b6fa239a20d7744b540c115bb3310afde1568b175bd41 6ecf36b1a7a4a5f8ef400c013e0028472651d7aea52e5f3b2c26eb043d2f270f 803b490726fdb4435a5c01fd67681a48be0e1e721f2052b37fd78fe20de861a3 +generate_key_image cd70af227313f9a386e24044f337ef0d27d61c13d680e566bfa55556635681b1 5a7659e08bf999923c0c944733cbb3b0f436fcf0d6ff5cb23b195fc0ff528108 c85a61df14fe43b819d24bf167b66b0eb0b480bfe4d8efb66ef36a64afede3f2 +generate_key_image 8c97ee1f1fe1cbfd85d08d84c7816ccbb80f2be54ce80740556e5388dddd6f2c cd50c01b3d3902112a1914120fbc8e6e4ce950fd8aae873159d79d8ba1691800 18840016f2135143e0700bbf5f34127a1f69d7858332dd4c3bb47efdb11f40ec +generate_key_image 77da942da0fae0eccc3284937ecba40a3e6ed63a92189c2bc4ad7b3c7f448ae2 4fdd3da56408769fae4911c934f61635ebc3f27b12ea093f13ff42e853190e0d ddf2d7da0c346050fcbcbba5fed0bb24e0e09f5be7a61ea006a2362181be6aa2 +generate_key_image ac981ebfd0b9f672a04cc59433c53d4ca7fda1aa15b9370cb1b677a9d78ccf10 b457000686260bec7496a0c7f6596fe819bc9962d9856e642f670cd7d4b8a80f 8d368722e8f6804562def6fd9657552edbe2776d538287f1952206a536ae60c9 +generate_key_image 1845c58a75474e0f1266308df8f0b35498317f963817139cbf6a6b142ed63409 ca754c6225e3813e03f8bdcb967f28864c57d4214188127fb877db7314ed7b05 7e4f28e98333922c5a604d24ef07ed514e07cc3ce1cfe8401dd0b7faf736da0c +generate_key_image 48fceea5f71230bc820d71e1b0e8df968109b40ba74e389f7052424d35de0f47 b18148ff2e2bf7e537ada37e676081a13f91f475649564a1dfd4c51946ac7e05 3bbd6a6631c2d4a04d89aaadd43e7c4afbb482f18d5adfd50449387c7e2946c4 +generate_key_image 15b6721ff44d0048677148ce8f5151b87e8569e6a160d9bbaee0f9e4d31c07ce 1fc5c4da9330b614d07aa76f6be7296fdb6856599ef0f34abfc4ad559db4b907 7bd4cedf4fd7baa5a4b908dec6202347025e713a96b4da795abe57dcd38f4da4 +generate_key_image 429185f31131a8b814b5edd2442102db662505f16f30ff3546b9f9d98afa4425 bdf65597fbc624517fb4a31d4bba66a6515fd0687a243ddfc08b6f0dcd8f140b 5f50bdc89c059263cfd1f33d012f53108bd70e9408c7278c5962e138d48bdbba +generate_key_image facf10d37648b7cac9b871e07caecdf0a03dfd5ce6099961504810a63908163c 6b26a6ef09c1bb60b57b7846ea4179905a72daa8a956bd75c1eb8a1ecd0bdc09 55c071d6c3d39b684cd3ca46b69ff8dfbdc249fc1b6c1f488866eff86805545f +generate_key_image 8c306a5b07dec2b446d41251a6f7e88d1306c4666de136798741bbe740b5a320 10944f14e3c96084474f4a11ca9266f5dcb9a403ad04d40c0643ae779b0ee700 473960a1c5bbe52f9d74ba286a25db824ce300ea1ce85d47435e98384c75fa7d +generate_key_image beeeada07d04118b577db0ff990b33fbe1645eccf13f127349fc6e0d52612d9c f8736506bccbf500839a8dd419969898d370ab5271e0926bcbc00167922ffc0f c4a33624da1f9c18810a91b27c53995941a8cf16819afc3112405d85d808ce46 +generate_key_image c1ca03eacd9f50e4ec8bc39205ac99a4ea0bd07558fa147090517f13f7b76cc1 2747574adda2cc7b98ec227e6e863c7761e5c6e9f6d3ce807cd9aac2037c5401 d86febf196bd16b9a50f50b22216b767c44b776e6f289594b0483ee39a539cbe +generate_key_image ec50ce701bcbb814dfbbc51675d0fbe0f81b0afe8c8f84d95a98425d0f502c52 6d9d31733d3d447a601c92f3ca0c1d0b4e350bdbc79272edd3596816179b9f0b e5f79599d55e25691c49ac9c185a8dcd2bf46c89df8b7f795a5d92abbb8a7816 +generate_key_image 65d5bc0167354caebe1ff4b1103b2ceb192a963907cd01355803c4c7a64dcd33 2b760ea01bcd695e43d68f168c89b0bd6966f39d83c2452f04070c73b953e40e 7e5a6b19aae65fca79129ddf279a9852b39c2d4293ee0f9383da3cafc78af297 +generate_key_image 66add5e94bf5ff724000aa6682e0535115abf1e31025775fbd4eff7677b5896d 2c4b25411d904e67cb9308334bb1145a43b32dce55c063449cc2fdc88edd1308 2bd0bbcd488071eb5f08533115bcbfcd6f6144babc626375d64b9e28af968be3 +generate_key_image a581bf63591ed69a2871bf19f734f3a59ecb80196fac78ef5a83fc4bf823faac 6997af12fb37427102774f008324f0da7ba12de39c535f8019476b072cfaa00c e22c98b56cfafad8657a6fbf9818080116c50f6d671f5f066b4f0dc2e6b56c77 +generate_key_image 0063404f013185853e09382159c32a12f21fef9cac3dbfae943dbce385c4b36c 6c74b2ed94f371e58535c188f535b45651897ddc867d0c7e1a997347b2a28b04 56c57af5f147a8d98e22a4ad23e7708f429be3bbe8fbf8322a7b47551da29414 +generate_key_image 35cb7be531defbb5db50a5c049b45cfb302fe1255ffcef100e384cb2c69ef412 77bae260cc0a0b2519d1d9ab5e3060fa1364e62684aec3f352de38c32f704502 70fd0ea28386e39adfce7a645fbcff71ea810d4ebcab39a21b567e6ec5afdfd8 +generate_key_image 8a0012e826739d6a53f8710f07a62375aac3afae489309fdf1965b7c7ac4461e 22353a5a5c6554c5bb180ee738f95f28344dd2a000d6824d2745e3151bfb8f0c 08535adb7a2da906cdcb22dbf33e3d7b18a171d63d8e48e8171360d507170bd6 +generate_key_image c7e511a96f86c6856abf46939b867fe4f1e3cad500a49e1014ba9f919fd0126b 5eecd534b47bff4d04b38ad7f21be098581141c15f7950b434d0a16341b45f03 153dda8a1bdb883b2476a15c8e51e46debf8ae0a5572be64e6f2e404e11f8b74 +generate_key_image bf5fa07943aa049d9771b75b95132adcfd395147a5911c8860853fe85a702fd4 51e910c17f3e050c635b951cfbdd8d51ee1c13503a051463d88b8bbcf9ba010e 1674b1ab77b30fe7041190b99103c6a2d666f316780333c619afb975796948d6 +generate_key_image 33102b6bf653514d18035d2335bf65014b0c2f9ffcf07d32e4dae52dd084cf5a cb005870f125e7363abececc03607454133497ae131fb88ed9ecc523fbd0b20c d8078d9f1c813f906b635b76f31e42911c576fab603ca2ca0587f015ea48b236 +generate_key_image 9957fadb5ae3eb5b85c49a372d24f90b8871e0514ec1ec500bd7319555a780d6 2f735e13fe8943df474789062a585a2842b32d7e1d9d0b116c0858b6e562700d 7e99b9d826cfe024dc0a0843bfff0f1f0bb82438b71d39e7f1e471d97448701d +generate_key_image ba3bf6c9566af027de6642f1cf0261e243cfffbc0e5c6a98b3a7cc3cf4c21115 c6fc26803413fd7212878188b33fefd5867ee94d29b2d88004bfbe141e5b060a e8205ace5582a7cb31a5fa9894e3e6f7c82a05cfff34207616a28cb1176c11ec +generate_key_image 349fb9b84f42c7fb018320b9b69c261498377c4e6eb83d8e2dba00a284b14fe9 7a0b39d10276ffaf99d518a212da50980cb877ef192fa06159ee9298e6724c0f 8d73b5b02509552494d9190813e3c39baa16a73d8c2b0d840b337cc2e73ec5f7 +generate_key_image 048f695a612fed662709b4f7ccf698af91a594fea18c495807add86a9b4aeacf 4acb9a1f18bf706c73f6bac1e0b2ba3a36e7235530527736e3f28dc039a9cf04 442463c050039a8067fc9129c5fb1a8f16dc050db5cb50bbe98e8384aad3fe53 +generate_key_image cbbabb53fa0ccc6588052ea9d46272eb2bcc370dc3a0ebe384a9fe697954c7de abd703722676224c6c6b9fd07bd0e7240024179fba08f3b57eee6a2fed487702 bd9cf7d9b49ec7dd481953e441f329ebd7cde8ead4195fb56592801b129ee18a +generate_key_image 36364c61af2439ff8c7ec9d7d50a8380d2bd82f08081bed96340383b80d69d64 615a5ad52f9590854b704772a23764c20eecbe1540b04b9428892ab637cc4807 dbcfd9c3d57464f294251f391d3d251486e716dcd442a3b7c54acbe1ac96de9d +generate_key_image 329c93c60a643299703420b7491334c6cf72ed7a2061c04aa4234e86d50106ab eb6f3a07d08df15e161541e4c47e8379aa59bf490a05a1ce1c5a71d725bd4605 f58fe3c668f6657c0f30d4275a8db28b85bd510caa3f0cc2d92e41d7aa36fc7f +generate_key_image 4f8c8597763b236d5e8f59b1c73edfa7a2cdcc2ad1295c5b41ada4d427a57dba 7639924bf577bcf07b194b8c1f58e1b91f391e41d81997fd6d57e9469c77ae00 45a05febb5bb59f0a6efb582b29c9aad580749c4c64b88b6e5274c27a82e5ad9 +generate_key_image 2ddd8c2eda8e2cdf3c8853dce176a29232514342ced5510286f715e6fa028931 b5176906d4a2a281d04f94f601e537908273d4ab9e9ad64f624ec56ebf2fdf0c 0816cd93331e119ac938733c1f062f36c8998f97a1a85a97ccce1237d1f60e4b +generate_key_image 864b568bc5e4c1dc5ee0fe4dba26bb58fbaac9d62c2ab3dc40794a5e7bc006e0 8269c8a0ca3eeba551c7c6456c719134389c0986afc4cc68473a1d8043c5880b 120c16006a28ed543b1deb45d53fb69da16d40a479ad4374d15ace18c34b5c69 +generate_key_image 67cabf96e39bf1ca35ee48779bf5c24256949a7f79ff5a1db28ef404e2dd91db 59086ae2a22fd5ae314e03992baea9bdbe831bb99f7209a7de953331eca6b405 025fdcb5658a719b75e3bcd8f52babed9367086f041dd46859786d66373d8c28 +generate_key_image 0a66c774e75a78c5a46b6446e9e101271a392ac4a622a0218c8cdfadc16d5d48 59b7600e6bcaf235790a192f1c2ecb3f8e55b0acc3edc617ff9c2d03cb493001 cf1e730b2440934e922988b3659e63403fd3fa029530ffd95f7ddfaad83c4243 +generate_key_image 48171cf123f80fe68fcfb110be0a5cd90379d7a1c9e0351e82830a5c8adc317e 15084959a5670161b832c1cd51b13d4786a01878bd56591bf043214485dce804 b5fc5eede6a02f70cce1cfe70176f9eff8d99930aa6d04a9fff66478716177b8 +generate_key_image cd5fdc48712226a4976f9d2ea881c8c67951519374b330915a606ea1877ceeb1 7f952d733ef6eb0ac42924d90da8838a123a3009591f0b2b175510161828fc07 95901527a10493a7da358528d3f8587c29b01febeda938429413aa2c11c01853 +generate_key_image cd17f7a25be4591a37df8b49320dce4ffd1b146a200104f875a3436c4a732e63 e0d5576363329dbe1d45050be8b28f857a4a374836e736f22eade1b83220bb00 7a957d09ec1b963b5ec11cc1da2eee6f358c868cdc6617e8a162b7393c07b21c +generate_key_image c90b82fb5009e3173b1b0f0dc9de8f86e0ed746ecdb8dfbf986fe20a4eebee71 7e53f37adc93efe95fd9934b21d42514cc5006532e5c1a9bd1a4c1363c0da50e e1357277d441d316b9c4c88e150bd15eb10d3d2a2d3b5aa271eb9fd99f8d3fcb +generate_key_image 919721be8ee53fde12ed5e9885ef3d5d40ad76fc9ddd1bdfd71e735cac2427f6 d749897c987daf0278534ad9e27e5f363f6c2896f9b83781f4f6111368cf380f 007c4db298166adee32e76e49aa26101c33d91d3224d9a53944f3885a87e2318 +generate_key_image a67226d8e5863846ac4e20f80de4173a4c945afe895f14ea4b9639ff2ffef8f3 a850b38d8641e7d513897b94493ffa02cd2048db090c1388f8da04eb3a998f04 b4157b17aeac3b77d34214f5d62d62666b0251c89f6778017c921a4fa45653f0 +generate_key_image 4b69831da867bd0cb0ee9b359e64991f4043e6362fe5b08aef7f0c7f509f0ecc e08da04019765119d4f4adc28f8dddeeea316052fb1c1d64e547c559dd48c406 4918b1402c607c2c3bd447c2296c1f003ac1dffa456ddcbb27a4e1a50648eb07 +generate_key_image 67e82e18c2e8f15c5487a9d1c63aa189cc8ffa7362b68b39e8b3aa78318c2080 7e2e69bae1728530e8abacae38497611e0c26265434fcbe77492bcf681bf1907 a28eaf0df10e34276063848c0d8c1789ea260731e71f43f7980c4f66a7ed6d52 +generate_key_image c484bf24afaf4c57c384f27013639fba5c650638e174fe256524012fa8e17c12 cb495d00a1a2995792b1a52a3fb2c26c625a07f8ecc16f7d5b513bbb70f0cb0a b255443dd3f7022bdec7dd2bfa5adec37a50c590badd260e06236d233ff2afbc +generate_key_image 7c9942da5457e5f56e9eb3445b0fcaff14ba28e20f06976ad141beb712adeb8a dfcddcd52a3f0c34592d6c7ee17795b882cbecf4ba495e0353873912adf9c801 d4feaf7212a45a2ee76c4561e99bf6d3533267770398566379799aacc316fa0f +generate_key_image 54f8bb8bb45004259e6389d0f3708c35402cbb9cf6054f2e00e7c23c7a690386 83123acf9af4b510a70f6a59270f4a13c6b25b5d610d3d82f157fb0ec1958d00 2be22f9a10b196b4921e0fbd7d92962c5fac045fae8e33115e3693983b0f14a6 +generate_key_image cadb046e9bf6d54a71826aac01392ae6523483ff0d4396ecd81d2a74f1a5847b 2af7bc878bc87e45dafe827c37de896fe6b8f0a8e03e3cf62e3bb23f8adef10e 5b38c34f118557e873c214c48d5db6d3eeab3f0139a015468c3782306061bc20 +generate_key_image 3a69941b7860c5f55bb18522fbf2dfbe31fb1dfaf037d06fc100dbbbd23875eb a4b2aac615240a8691473115d3b2764cc43b6bef9b523eea3cd87c39f712be04 a445826ca9fbaf1a8ed8a5576042e2d0f0148fbc0f5aa091c19edf0f15b98c5f +generate_key_image 00026e22c713b91143b69fba0dd34f81de60537e47446e293668cbeca86a9f3b 3e9bf60de654553dbb1f53b4830dd660b9c0735e4907175c4270238e60e0c205 7d62ac6478b4614c8e8f0958e69d3740c6655be094729bf7c2b693073a0af0a5 +generate_key_image 5a8b8c887deb18cf2157ee90799f26f5283fbfa00e73566b82c6197235c76745 160e2b62e3de248b55cfdb6000ac6d30661807eb977ca5f545cb671d993c6a01 e80c299db7b93d8cd750fcd67264c4b0ce689422fe54f5e06dd2afccc99b6361 +generate_key_image 73d2eadd965480f3e5b69a1295257381fd7f815673701bac1b65c4a06ee4c818 67f22bac23324f914870b6da68e4474ca325ea5817956773ac02942aa922ae07 c9c0e04e83c5359a77e83971ce88d0292d4b467978c3e96a52b245fc6edfadb9 +generate_key_image ca328625d0bde3a8e00838fb4a3e1f1c972c1f9c1bb544370b706bae5c907242 6b6561d30395c932cf5cadf827d9cdf8e7275a9349dd6fa3e0eea0f3fbb42e0b 418d7299ede420228a1f3711ee686593c3fbf9b069d3cc9b50461bcec186f9ae +generate_key_image 2531db7bd6eeaa912005d7e7965ee153d5abc86047e8a26a3419193b33a6529b 6852235b899571ca20cab1e9b9ab0ef4c23a4a4c0d09865675e55501e9a3dd0b 6fbe3a3209435a7d865c976a67e263b41d12504eeb310fc5d7217e2b7c4434b3 +generate_key_image b3ee5d63a4dd04ff090906abcc8f9cd676737fefb7d31a053db55315633d9f36 fedd6f29ee6512cd3b3d904e224b966d5e508fddea2ec0fd7084e300d223a80f 1832b996522daa066b26d72eeb8330b1b8c39c7b93669c359accd7b6832c2a60 +generate_key_image 1424a4009a83fecd1b66b80008abdcf4a6c40dafcfd3a38704e99921720c1bc7 d91ff305067cafb8f9837748bdff15ef26de787fbf45eac54e2735575d58ff0f 023acca500de9bd2061fd06b292073bc8017e600c9b152ae94f2b9431313a04b +generate_key_image 7ea7399d6d8cffc087d0a603a6b057836806b21c1ff8e00cb01d41dc1e52c27d d6219502702d1e256f6f3fea1fbd12d77f1f3ec5e6765e5e486cbab29d896f01 704d84c2744b1926c2a5e3480bb2033fb4ae81cdd41c9a881980d7fb1d456f1d +generate_key_image 5eea4eee7e634a8cdc1dd078f5abb4fe865886fe54d8deedb00b4c3518c14c34 bf638cb4242b58c7971d9f432c874e1213d75a151a6db6c969037fa682e7200b 0cfe1256ec961b340c3bc72627833d5f32daf038839b1e2d7cf0b11fe0707075 +generate_key_image 3b048f42816324f7a0049a0e7b87f70a4c0e2fd1ea54a556e1dedd7bc82b5b9e 97748bafb792c338e351ea459ebaf1b7b95e7a6653f055b80936d479f46a8103 28297376a03e0f99ebcb6f3ab7ad8055fe69da9c3ef97c48a2b149d9a06ba8b6 +generate_key_image 08e82f79fb6b167f79ad1bbaf2d79114fa534c06508d197ee6c678a4972b16e9 885507fea302e00d9c33a171fca0dfd97725e2bcbf70bf220fe56a5d5d311509 ea1f05c9cf991c906f8a2b74a77c18e972471f59a1127636dc3458a2737b147f +generate_key_image f44b170ea949a979225e6443cbea05a76f1518b7ace3e668be26f7852aac8b15 e0a51dde0187dc964fa8819f08b12e626071d63af4e0e868b5930858612cf201 08498924cc17ab0c50f5f71381571a69eec03dfc22f76d56960ab330659598ae +generate_key_image 31c76d8fc1641fd278718255b73b19c8153200a927cc36d5f257a0b547880703 352f528ba2fb31ef2f05a3d3e34d84812d20d546953c8d446b4f9a2308f5fa0b 1f040f539f478ed66a21d58d085ef89471ef39dc399e074756b8fdb6ba18613c +generate_key_image 48111556c3505f87f5c9cd6f23fd329785e7ebbeee6503bf7260c9b19e1bcd66 bc5e2cb704b9cf8d52001694adb2c8b022e28038c61a8c36ddbb3ecfa0bab001 344689bda52380d9758d959874bdabd8da93009e9c35f880391d53837bde02c0 +generate_key_image 43503a06fc017428b8d201b0227975b36f8761af40dbb1857ce41f6fce7ad39a 8dbe2c27deabf47475d2b0bdea3949d0ee4fe71e9e880400281455902ec18603 0bc59175e6409f9b5fc4ec04c20437a53be0bdc9b0e30056bf7947e1405c1f3a +generate_key_image 7722d6efd52d293cb5cbd76c52cbe689b56b64345e16976532c2cb77557f312c af694f4661aa26f40b6b25f07bf3984b258187d943cc2b1a5048a9aae293480e feb28c61d2f23f77d157948eeb2159aae06e76a00fe89b6415d6195ce5918cfa +generate_key_image 72b1ea009232b941618a7040dfa26ee78e200c2b399e52922fcde1f71d547b7c a208a5fe83355f41093728aa290cc29c56080b5a0b5a2e7b836eff6f0e81ee02 a95d86fd783d9a586e95cd8f334019fcb65d2a569b66b68b3b06705367729dd4 +generate_key_image 98599fcc07aa0c9c45778eadb9863d4838c3930ec957b0f22ff7a3af031c0e10 ae88670a503ed3a7ff93155740cdd1e5a415222b64889dc08ac26984ecdb3705 46301290b701d7ee50e75a02ecde1a19645e00442d79d576b886e73d45afbe8c +generate_key_image c9ffa616fa8f0b196cbf00cb4cb2dc99b9688ce9db9cd935a62dd8d4693730b8 4e5c73fc138056653508ca1c09442a6c03d575562c4c30cdcce680fbb4079700 a249fe68d8fd6238ae255dc965a3a92da01f469b310f09d7ce4ca549746f1d3e +generate_key_image 336dafd7e9dd9e22982e6043a06c5208474b876a61d7b8b5ff5e112a0a3d86d6 3ede7ce4090ba3c5a789c981a9b81927bcd5d9f54207b092d4018685ff2a6808 2ce4fb2ff10d3db63cf3dc9d73aea45862f34c6758f368b6ca0ee751b4fa6748 +generate_key_image 27599ddb0a8dcbfd3c28c61652629de1ebbc756872ad5d303e079fb4f1e238fc 8726b923960de21943331958bc53af4220670e0a9bda7ecefc7ecf301d5d0003 6283babedebf24e8c80e80d518a870a88d455a704ba1d8c443047e5efcdb3b84 +generate_key_image 12f8cdb2bca20033d8a97c1159caa3b5bfc6c59a993e567bb24f087796192e69 dbe9f653af267d15816a31bf33a845c3deb105cca2a49c312a692a4aeabd9d0f 36149cded2204a222fe89eb8fc3bcfc3606e82c0fb44bb60048bbc3bec0988c8 +generate_key_image b8b91a0ce96f560b99ee95820830540ce5d787cf3173b08ba0c9decaefe89ee7 d91a464c2cbc9b54bad10e46bd2bd2eb0053daf7a9fd4bf819dd5025f1ed0202 0142dfb952f28f9abab31b1a620bf6dc10c0813ff476379c4cf60fd5aa1005b6 +generate_key_image 98d64df05cc1d43daff4a74ee9ee546153ec44e499a0b7882d27135b5b54c7c1 8e7aa8f901a18fbf0b053b718bdf50900a778d4ad3d52f58b08a813d60f5410d 7c0a828f08002d3abbb312e18c067cdd1a5995a5fdd62778e3b6ea7c28e33858 +generate_key_image 9e5c30b6e3e256e3d5623d22d13e80a0000cc271f5b2c1a70b8b9b2a535e2b00 51bd38bd09ad8edec5882829dfc689232657b7e09bf31a69a6f1248b64826707 83953b610f35f530f91cc416682d16140b36b91a225008acfb6981b11731d332 +generate_key_image 32b83e7f291241378fd14465d0072826f40805900073b32492f3003f7806dc3a d0b1cc75a723a729d01b3496b8c59fcea8c0affb30bd0b90e2d19c719537c209 40c15985f9a34bc31145572b16b47fd68e7e58dc51a87c09a5705b953882d9de +generate_key_image c1c85944d72b97136f62df42e31b19fe0aaa38a5ae7ee24c8f04b8f467b6d3bc 79fc2eeda554742eb352285af7b40a2e0de76f18b59b85f92c75d8fd2de10e06 3eed54bf8bd7f5fe171446fd7ced3cfade0ea19b7958b7d4b2f354703f064f1b +generate_key_image 2dbb89c2dc649ae8e575384206c839ce9bea8b4026b27fc434e54ee905024b19 21a83cff005f68acb7a4111dbee8835b554d0412fdd1500e4bde67f0fa3a4409 38f99bceb3a76cd672b1be354ee590a11e24f28764c26610dfff4e616991b733 +generate_key_image a5e067ee41294c2e1392d772710268ceb4a27dc21ecd3eb9752535d54b60acf1 e5d0ba197c8bce51d697325484beaddc467bcc9a1cf28b921993edd63e91a707 e8cd0b15b488ffc3f74465fb6e04f60b6583a5288dbf19753639a2fd5873a135 +generate_key_image d82069af548881a4c2fc34cfeb64f29a727c38d4628d690d0e66d8e861db08ec 4382fa0a6bdf8eb47cefc708cce9c6f73bd676a80c7b87a5dd51c0989c3a4007 a0cce51ecdc8936c5e1d1bd3a32e2a1224a856309b505c8d0fe8899490c2734d +generate_key_image d651cfe48544f28148726e77eddc13c37ab18f10b24a2ac2244bc004ec7a4e0b 40b09961722a123d3511fa5fb2f9b79201c22c730d48ea502fd0ca223b26a407 31e6fa584e8a8c6e88a98ea3dbb24697772a82f5fd1166851cd9bcda44927e24 +generate_key_image b65af71399b0f27aa25f3cc046ec40d2f30abb66fd8e2080d308a823c3a90ce2 d3528fa842c040e226aed5ac2b734490707783b85f981c8ed2273874bae1490f 0cd83354d009b788e077c5370a41dad756a2841d832bb40f1d1804d0e3ffc939 +generate_key_image 93a622c4c0c4a057593887b74c55eaf7241598779587a38928ce0d04a06ef4b9 3ae0f6eaca760c23530b1e2d40b63b6c246f878f0a193e23c2415a81a0358e04 43a4808e314bc8e505b7fbe2e7e78e208c632c67228ff9bd06aa9961b7d2271a +generate_key_image 1d44661d7c3c4f6fbd20ee954cdd7cfb0dd81bffdaf185bcda52accb930d22f5 60db86baa9ea3fa0f9e49873fad54ceae44d125905da4d3f4c7c454c3deae000 6b335653b97e28535d24636976071f264874b45dd7e5cb6a7c86923994cdac0e +generate_key_image 8f10d5a9a84982e6660c0fec4d359445d6d1d8e9909d0abd67894a2682cf39a6 ee5b96189e621e894f20a36bb86a58def492cbe4b16aa3ad80693882c943cb02 1eb779665dba7766ce5b69b0ba7b49b76d48e898de0ac1baeea61782cc31fd80 +generate_key_image dfba6181df23dad38a04440f4d41b7f5f9c007afadf6fcda0d4bbb55f622c365 d56eebe45521d98061f89b9f7577a66f53516a0972d11ee5e7935b7f41e66c0f 6b67f39538a75ca011a1377855cf15c24d1ea68528941a18bd3a6e309fa9e1cc +generate_key_image 5095394b6c1b40be0e45eacd8fc5d8313c9bdf249f4a982a3cbd28b684acf85c 6aee710244da98c10da22fc3d2069d7036343afaa14d8de0fd72f8aa46ef5d02 5bff549ee53335a5076822896bd3b7a985f7964d7c563a699dbd177903779a03 +generate_key_image 8396217d7069c8079634747a36a6d81cd6c6e110066100572a6ffbeb792c8960 1073c723238f2c0527ad4abcbd5964b4f29eaf9e54af04f46efc524c66e95a07 0bdf7f8f4adcc1f7e69c61853eca9dca34075fee3736ba53d5cfc8847329a014 +generate_key_image 61b96a95261ccdf103cf456b9bae624230d0ed9b41664b695d5c10b73ad21975 fa2aa4f31357dc8866c15f9a5751db1aeab8f2da1be10ee9cabd54f88ba3650c f074eb80abf54432fa4f4855e43390f4bdbf6c80f83d26c412bea047bb430c14 +generate_key_image ba689913b2d9b72e673f5da79a90c935b01f631bd0ce7a78a68fa2c110c22abe f86a03ac61fa5f8338d5865b7bf0c73ee86b39c585b754558643d74f9e1b9005 ea60c02c28f6b8f995e7df534544fffcf92844da4a77419649d78c4d9a66f04e +generate_key_image bd26e5b5567656be7d34a0d78395f96ed9aeee9c7be34dd2af102640c16be177 0ada44d39adaf5d03e73d88b5ff65a4214fdb0bc59632c22a704f13d1ad60703 67b6808d70a8da6317fe77d575356f44c53d392d1a53eb764c2eb2a11e4cd2c8 +generate_key_image 89e288ace50ce1a9e834c6ea348fb40effd12a2656a7e9295bc4da8b32254602 e681e04df11675cdeecde7942aa04c9cb81ac04bc0c9d068ddbb7cf9324bad06 39a88dcaffff068f06cbde4978a5d5b97b268e05ef4e9c83a292b2129232b03d +generate_key_image c08d6a93343fcbe4304683e45411b097e0ae7bc4cb65c7eb466a62f864820488 a0238bef3d9cfd88ab0e56ed970650f355ffc6964825ac1bed4818b9462d240a 5f17743c979fe330a517cba751512db69c19e1f52de59a159284a50c0ce6cf56 +generate_key_image 426554d80e47258c4576647076c3c09247cff3af8ed3ba11618194d7533e7dc1 3f8f0947fa8c7b7d18a3c2c93af34922cdad435edf45aef256da51fa528a2806 4c259c7bb2eb963b5150c9fb5ed7141edc9f9c120f7c578cb37b0bcb2025f359 +generate_key_image 2c2292d7fff705744eae65cbb1b16b3b546922f69bdf696dcf7cf5d8c82f3a0d e96be718c8592a61f0876c21d421849096ea8c56f2262280f2bd1a7cf65a4c03 c2bf6afaed10d2d6b65d6374bed8b82bab2161664bb306d7ce7db118011fd226 +generate_key_image b1053cad8d92d17085f477c0d2f177269fb91c81f51c053d590323110cacd06f 81ef71697d7a36c44714090f86662af404112431b52c5277fd0acffb029a0e00 a97dfe819937305c84aaa82050d7380b27c2a3f767ea047bce0676460cbbc519 +generate_key_image aa17b5fb7ef6d8e7401bb16e439989429a5e21cc6b2454fe46e53fa8c2126602 f3e3a80190847590ccb4e6abf5f2331e37f537b139e5ac5ff8c8a24fc37d0f0f 2a1c9c20d0d9bd743ea7f88b49339fcaa61f6132c4ef1b0a2fa33c288e6d38fe +generate_key_image fffa647b5a4f9f50966afef29c84221c43569d7fa5bdc955780630e2380c526e 32ef1e32fe046825f5c8aca5b915a064a667fac6a3f93a1306345b8ba5e5f007 0d485574ed0b5db67ba76ecd880c53bb09ca725d00e06e5675c478cfa186618a +generate_key_image 197c33ebf31423b7921df29a9273306f09bf60f44c1015e8c12142186f53c4a9 ab6a95a44f1e6fdc0da0b314e9683153f82645c8adb4351c42f85e8b2e3b710c 0f7a07482e171257e3d36c135b41e2245fdee8966cc1555693e4a8ae282d4436 +generate_key_image 75ec94479996e499757c23e6258c0f43f9948a946641fa5a0f5a3f903528634e e7ce4515db3f8b91bb0da0803be38069d2c40379cb4cccbaacfbc15217cbfa03 8e241075e217adb7bb29431212181017649b7144e91c74c7700884d300e63148 +generate_key_image 932dbddf733fbed796bff98231d3b3c265f6a93b0fdb3c9e5e87dbc009f127af b9cdabb2fd5672e522bdc07fb58d4f9f1da56ae7d351cd230a375bfe75d7b202 6ea79a2ae3f60e39404b749d61f41f8619adaef04c70058cfe41259b772777f0 +generate_key_image 1f081339d937920c6beee942ac78cb2ae40aa36384748e717fd00f20f6fff1d9 f4fda7bc994e07f32e34df0fd2b509eaa88a41e178c9d5b779c67b447717a408 4eaed3a038e141c0b0efee2edc97133c3f56ce5a0a2f0844a97c604f5a6e4bbc +generate_key_image 420e46034f0448f97047b5599bf6143a9f5266419401ef5a6e2750f87a975dc9 deca2ee8dca1aa24cfcfbc057e6751ca5cef0b26cdd7726b974d14be6f487c0e 022d34d30ff34a84ecfd3ec815c33e7d7c272b22dd2c8999495047a6d4504fc4 +generate_key_image 0545ce7458613e0e0b511299d0ff85ebd0eb4349f863d0d098e667b8646b848f 6a0de0aa54fb6519facdbf7ce34baaa8c22845f2757fd534001eb570e603a305 977681f9bfa53d5fcb7eac0039b80b3e5c441bb2b661c184537db5bcb1c8bc14 +generate_key_image 455844f4e443d0beb05a15bd5d1f4ba07c6b36ab22841e922afdee4f6426843b 307f2276aeb9aefd921e52e93cccbd8e6b1c59581a0a91a873ff1e1219c3f00e 11e1c32708ea307d2b8cbf0811c24426e9ee6c96a781a1008314ef56ffb00557 +generate_key_image 357d03f84bcdf84859ae5822d15ff3fdd0dfce1921d3c63af7a074c947f10fce a3e480a02276eaced786df894dbbf9fe70a9b5de64bae0a9853e6647a7dd7f00 95aa83ce9f126c8b17d7715f72482314c3bf7f132d8ae8ee28dd2833dcd18cd4 +generate_key_image 006324ebd2114880862868093823b7541ff603cb36c5de31a1515026c1d21865 e40fc115c2d7a73f1973ebd5b85b4754163774bf9dcb1827f94e1b948856df0e 25e236cbad063f4a93dce8c8e31cd989820b5df69027ae0c6121a8f46dd7aa1d +generate_key_image 21c63d183c181cd8686bb0b90fc52b4013bdbeec9f1b85e36111a8f5caf71bab 3aabd1b1da5d86671e52a7532414e601d33404540d3f44c0f4c0b563443a2e0d e0c9cacb1f65152e7ab15386df347ae766537e769e42c77fb1441f82dc34a2b2 +generate_key_image 94f0b3fce03a966d48986213ac7dcbda3b4f8012801cf885397712dbd4aec932 94ab640ad4a0d74e01e50e603c0ad87857fd90dbf3086ebe6cb501e8f1501c00 fd2ca148b51640011d1dc0a9bcf523fd11ea93851b20e6d4ab78f3b7e3884e5b +generate_key_image f2a411f5ca7434858429f9cbec159f13e4b04c602b84eb53ace23a23875d0af9 bbc4e3caab2f04fbbe258b0e46892b5633133ce0109624dc0dec0049d3c95301 67f70730161ff8008373e5c7a37acc577534e42baaeaff2c5ab04cafe40ce5b1 +generate_key_image b2e1cc09b83eb7d88fa0418616092d5f3beccc571b617955eca96f29bf220f47 3aef865ba193e6094eb7351ed486edacf444971a9410f3077b322ee1ccf8c302 e2b13fc6f1153468d4df2b497c909e64e88c1cac270f1765e3032358c464d5a9 +generate_key_image 7190e9b402d4d516f4fa7031688fd6c79332116e9e88917437727ae10f73c69e 325c75a552e3f1a7cffcbdc8cc07d2035b6f925c5b2a5faa0b26c83a87a9fc04 23afa755c33230b2226a790b7fda0669bb5a75cf7331ad3e47dcc7d0669819d9 +generate_key_image 36e2911758b49edd177b089a4a9d23757ec159b05833272281522b81df3fb8b1 640c26a4c1c622a46b7ca6e4f6d49e17605213ef8442313377fed55414e3af0d 905643d814aba88cb37aad4f02b809a5a18d2071a91d720110ebfe35dd750712 +generate_key_image 5e345fa236c35cea66f949629259f5a9d7efe34127531da965b25be36a8164be 36eb84cd6e0214f3a73cd09876132eaf1b585d3786bd308fb9b7ef86331a3b01 135957da451f77f190db8ebb4dc4162432b8d68d7b7300304c352ba2b373becd +generate_key_image f5aa929af04705111d35284a27d9e2fa97137cba392089070d63bff5866725b0 0a3cee69a6a38c591f29aa40decfcb6d424dd2a53db08b9cacf4909a8ae13701 b8fa00295302201ca32100518e81831ebdba0287ecf081edc2f7143140801d94 +generate_key_image 82c4e6eaf6fba670cef8d596511f2615980bca370dd35333595e59b0d7fdbb03 0ee9ac5c35d3be4730971d1e92648fc348d0a0ff5c67fd4641f8e4f625accd07 1489f134a294cc30572f942f49c64d152aee3e24ff92c29ee7acd2c799638ea7 +generate_key_image 9728b188aba4493378aad11630ef914e8ca48914c59fb332e37146a97dc693b7 8b9b910ad0551798ec23732c49486ff02a9fa04d8fd3adac327167605240600b acb4810b4b70bc5948181e29dac147cbfe7f94e3167f7d2e98a3dbf123d056e9 +generate_key_image 9bd71cd7875bed4b58af73b1ee630c5a8eabb8dc4f07d510b5541ada1ea26c9a 53f22d05a465aa8c0c329ec4ad515b28400e42f66431606df5436ddb2535c202 d8138011c98928ffdce93e0b8e3173a38e095ccab669698d613309e392e67449 +generate_key_image 0accd75d9db37c66deb35c4cce9c108a3b235ad170d8547ad878aaf29fd6e889 77f744f7991cc52ca9e819907d80e9d0735664df1bf89242b332bca42bdff60d 302e26ebc2b0e0576d1eb2ee90bcc4ac9578a97cd837129368691fdcbbf39f44 +generate_key_image 3b45feb915165081ab9b90086d0ae1678029dbddb828403eb297d8516c6ee27e e33e18b948129f8087d4de6cfccf88c9905df1e26b0a1e5849d047d4e2b38500 c8dd48b59788b3f33a6ce88b8b31ae612b8466140ff8dfad97a17ea46e6c64e5 +generate_key_image 2f7d77e342bb2f35a30fb61ef7d9c0a92f963f410b31b699fd7c1f5585dab155 ffed39a07db64ad3677bc662d65092c3ca9d438f70959fdf6183f3d40b70b704 5df6bc0b073967a6b355dcd39c69df066f5ab52748a3944548dfdb31189d2de9 +generate_key_image a06828b1200c0bc87d1d19c1a99fe3f4e940ab16beabacbc5057767c17df6211 3c62f028cba27c7fce83e7ae017a0a4ecd9a73a656c7afce369012d0bf457e0f 5907ff3a9bfab097ed035f8246c17ee3dfa15c704c9313c53c1f5b6cc457dc63 +generate_key_image 3b70ab1a3e38bd6bc9d91cd91231e9e53b904e2feea92e020c00144eee8d0733 42166a622066940caa3185444ded1e927f0784dd6384df81850bccf416c63b07 f5a8e505d8124d41a2241c3b558b443aeac735fb77dd61304c604b76e11e99c2 +generate_key_image 99ea4080783426aa16fdd6f2f7951b3a16a88636ea6ea4c8ff54ee1631ced404 1ae17a4e8f46a61efc7255509662bd25c8f0e18eddcbdf14028b7f1ff1813106 941bbbe8ccc87a900ff57c1ace87dedf403dceb8676a10811d750dde61be0a88 +generate_key_image de523b296db4575e2484af664b071a1c47c29ea13796fb1d896d0d9c766cf098 ad8023f24f0d090b7d55cf22efe691d58cd9ae430c76a4ca79ef89210a86530f 425f15b46f30b565ad1981bb485096b4bd337791829826fb1d283acbbbdc5c61 +generate_key_image 790749787efb5e5dd85009e97620270625d2c0ed60a4cd0cc92eab8e1aca5251 2716467bda663b5da9177e82a462fda8c569214039cd07e8956c648ba8e75f09 e034e387d9dbf6f58e40dfa64c911788907890fb13e87066a37047cc2d348516 +generate_key_image f4c514c609a3b99d4b01204ace1fac240bee467d0f23e5e075b39b73835623ad 06fd2222e491d0a9d449079a42b7ecf592e56910d928063d3d1f885487a94409 15b0dfe5af5e7ff70fa7bbab8bdbc3a9ab12687711bf3ed5397ed798fcd70996 +generate_key_image 2dad73b0b5fbc26fe102303cd4028a54f3eefeeed17d590b7161f9299e601b88 3be05a8d5b81371e5a415803b70a45d729c13050b9cd5fd15768dba218612a04 16ebebdff30a18267ab9734ce3b693005116a3c7e5e2b5e7640e6e22970bc00d +generate_key_image 5634743ab7cc60de177207a6d2aede495e45863ce8df4fa2dd988e3170ec2a5f 4ecbd57aef5bbce4b9a6246595fb008e0c043144e2c2865793a7b5b3bfc72b05 b2ff81cbcfb8f51b2652b096751fd66a1f278224ed706070b472f0c4f88b33f2 +generate_key_image f987315a69ded4d3b3556886289800b21a22eab53d86d807a19096671098360a f994dad0c38ae49d857abb7711f7a26e4728ff1a940dda7ef9c1609bc750580b 4154c743614834f796a392878330b5b1dc82677c7bd40c503e41986e9b846416 +generate_key_image 82f8585b5951329b8f9672bf18174ba4c71de104a95da08b1bc189deaa21e3d0 50e22af61d70a57bdc63df2bbe58bc1a0131b9f3febe08f0c8f6adb11e406304 ce22899f469cd1ed358221aa56589c7481cdfdecd0b2b527043db5214da0bb0d +generate_key_image cfc679298a651dbd981dcdff4f47f6522932ef85f5024a85c534647af44ca2bb 432627d6cfb5072646ffcacae9d5e39f031bd752f494f870b1bdc32105d28807 8d4788f7327adb2a634a150601f2a3d51477fdb871c87b95c6c949c6d5ab1516 +generate_key_image f5b600d64b444bf65f7a23ffff97ea03365651ab8c826b23a0fa67dc487a922f d4958cdc0398d75ef0ce74f41510024f1fed1d7b0b2b54023297a29abc898f06 1b2e08e2312214210df8adc48fb96693305742209fa761c5f037ed899fb93486 +generate_key_image cbd79c8fdeee5d532eb60827f6391d7d9f310f5a67947ab49ea76ad3adbcc8e7 31bc888cafbdb2564ec1e44626389ccdc7fe77a423d21807d04eef20996a6a0c c6be23a6cd9783af42c96b445a059c3fdf54682c1cd9e8c18797534c604840c8 +generate_key_image 7e7da45ba46679534725f591bcba23bce8813beec2bf2aed957d7c3b4efa6dc1 b27fe1cd2bc408c07ec92a05047711b1b3549edd3d6a64b5bd06fc61d59ebe06 5722c7ea7b1c34a6d9388b76a82ec23c56278dd604929c75855b1f2b88f80406 +generate_key_image bca5204ed30c912424f7c68cdc229a28b796f999f1c950a6d39e6c08813c3daa 0d40a704a64f3eeb3c5006f236bcb48221938782f11a51f61947a95bdc770b02 0055afb7f9c70e965b63f0a65acb775f3026dd41c390d81487b1a17af963949e +generate_key_image a7c5e405f0f06bae83e5e2d55b2f3c685a962bd874a62ab7196fc21e66aeb249 bd921c709b9c1ec419886619ea2a22681fc7e7c833bb8daf9e989c07e70e5b0a 6f87e33c854a63d9b5a174fdd456f3511283a66fa2e1c0991fdf6a3721f53dcf +generate_key_image aed708ae3206df9dfa11b4176ce4ae1c0e08e1b1a0ea60294682aa70e3d9349e 83c423c730d79a7fca4445ab59a68293a79dd6b45c9b03f60ed6d42aa2751605 00bffe40b382184f2ceafb25204b3e3a07d0abc1583420a430bac2ff4322568a +generate_key_image 1772cada1b0c2441e71333f53c43c9cd43db8c5b337e1dda4c6085494de50504 958de4e8aa0e3f89b73b54dba2715b4447d163e0946ab2691115f3678c734403 de0cfd0f3875a5d6f8c08240821561220b941a75c74d4998da53c13d3816d1d2 +generate_key_image 34f8f5342d57681814403a652a3918b42d4b6a07fb37061191b45da5bb5f9dc5 e909f628704a460eae04258eb07c53f8929557e65dabb7179eebf66c6ead5a0f 5e769c51fad94e05a1122d3de78d49be93b95696edf18f014fcff84c53c58566 +generate_key_image a4501f8f521385ca375a122bceaf25fbfb0f7a716dcd4e03ddb848af966ff136 3a575c630b48e7ce043f29504841b52c9f05f9eb27fb2434b00489b8be6b3405 b347656650eb25730cd40b1c6788dffe47b6c74094651140a90747340c3f5f7f +generate_key_image 7eb06e3f230577dde095696ab81703ef8b1eab884e5a7e322ebe66683be8fb65 19f9da3129cabba3b904ad6eff2a9d6a6687e366147332de177724d70e5d2a0a b3800e5a020629080d8174e15f5a96b3af17ba25c992b2b712bfbb18067e7304 +generate_key_image 5c2ec48dd2d381175cac4c72e5a8b12009e000cfd4a99a4977e8e6f7abe6d2f6 91814295435e9068fae1416b79360377c63c5b9594c549bc24459c0c72f3e209 0e336c0e6e560d9dfcfc3f5b3c92c3041bb9349e9d5557b08552b45a8a67c3ed +generate_key_image c3c0a5eabeb5b1c1ba07fa0ac14b4b76d0f8c34c3935e6ee81bb0b6f482e245d 89a70895c07e3d4880c8b7939ac2e0b7451f6f5827ae975afd970a18bd253c0d a8655360fad63b8c2da7b687550a34b89e30f515c6f60d92ec8cff554f78ada9 +generate_key_image 723b864529fa369a594d3d5b92c4289a5bc778329a4ab4f6f963471ed92aca9f 0763995919f627db5298415d9d0b382770b9a7d9b9b7d6d601c75025f237d701 ed5d568021b96816ebb63f8719401b2ba28659778ff7d67b506f4ba50d307215 +generate_key_image 070dbfafdeb251961e82c0f919455fa2186aad52929affe528cbd085e7ffc882 4e7d642935c661fe41261be0f4b860de000e70ee3d53c1399f281e03597a4409 dc8a362a93b299bd4c4ad1d6e770450623ab2d6bcbe179ee24927c6e30bd9ddb +generate_key_image ddda17d0d575f4c6de696f08b69b0c4d31517c277b917dfcb67902f651a2a1bf 181a7b0b08f8ffe829e975d255ff9e0907af7fb26499179db2d8c0c70d6c7302 6be37584efef6478112d6f53c3335400b92f3ed90fe88644cd5bc76c916032fe +generate_key_image 095998d9957fd5e26f1b241f3b5847fa5baa4051d584636329f880d86d0c0366 175482bc1dfb380877e9eca0276165f573bb09a7c365bcf3fc815a23a2c24701 89f672f568040e20808da73bd90e25e15922575f2870a61bba80bd5150f798e0 +generate_key_image cc2f299988f88bceefd5d75f7078e9fcc3d43a9667b9c2520bf0d2813ef3a606 f528d106436ed536d07c427ff8119f3f887b1b0166b4f7985e421bbb0c6da90a 3fb3f20a4b192d2b7238b95e325daa05d2be2f3afb85fbab87fc29a58752b850 +generate_key_image e32ce30c291c181fe6e91a3a92a1b7094d716e11a90558d678c48a3f3b5502a3 3b9b4ae6c1a07849ca766c1c12abdc44d2d08b9575f85dbeabdfcaee374b5502 f54266e717b2cc44b2bcdfefa4d42fa1a1ef9a6f7bae9df1d150094e426d66f6 +generate_key_image 3759d4727328d9f9d98dd68f512373c417ea18ea764d6212550fb8bb2aacb38c b914f49b673abaf0817de7ecc4f5e4bbc57a7e829e08b6be94bab9b4040d9707 7b9c0b8679ec5972c0d8a17c7bd87c44463b3f550c35171ebc5c0748a64809c6 +generate_key_image 6b14c13fd0acf07b854574270af444913bd4f22a3b4c9ad6fd702c00efd56e2a 77cc20b322aabf7c4b3ebe7d5dd40b6bf7ac7f47dfc80b3b502fa42d11259f0f 1e174de16be9d586bbfde480a1b05a3d666830d4eac849b27864e2a77537f9b3 +generate_key_image 015d6f9accfb34eecf2499c486bf03f289013872ef68de3b665ae946474efe7a a5bbaabfafc41a39a08c320b2935ccd3b88b7475753c3c66b1ca992fcd325a05 bf181e73245cf8c8cb560b23163a890c390535de3874c1cd0d30e451215fefd3 +generate_key_image 5c96001563ed0d2c28b96fb78b0be56dc90167aa43fd02b73b63ff09f9116f98 306a7880ec7576473854a0f8cfc4457a85843f0bacdb1797cb715d578db10406 2185b8a60b9af763693ec8363017a3f9e3f4d0ae0a07118dfe7ab39e02501403 +generate_key_image f992996580ad840279a643a0c89779a03f5b3b2554bc86c917345152f0c3d463 1b90647bb4b007c3f67f1be1bee6e43ab93241760fdfa8bcc7aa604b6db9c602 bacbd5f41ea80c812e45a32da9747ea81731e887390e8c2aa74a8ee04867baf2 +generate_key_image 9e653b53e48b3a9069da8e6d4e1a903c4bee92e9d59a812fc21e6b099869bf90 95ae483fcb72d94a9e5f9c72f20c2a8fa9571569d07f513f9d0c2b1934abbe0b 693a8bf158342a5041eb0d56ce17be863e7864e26cc0fc99e4b64b969386812c +generate_key_image 089ec509c466ff959999e24ddbecfce419e0ba37c92dfb09d8461d55b3b32995 151931ae2f822a3e660f901de5e124670377188dd1de21fb0a307c78730f7a02 2647f53059b463a092b4e8d30bb6ba1a09ea4aea710c55118f98b5c1a507bc71 +generate_key_image 5356fa5d36e9ce47d352076fdfb26cd73e27793adef101b99efbaf101171026a ed28a583eabedb99318efe6cb52175d3b8263497e5967d22df355422ec7d0107 a6bbad4f9894a13ed1b4a4993aa3159a0c0ad57ecedf5ee4af5f547c66a6445a +generate_key_image 96913905a9349309d70c46d7af2dcf9aceca7e28b3191381d8302bd103d93363 b9eb14d525581c194bb1fa5e7d2e648de4264c25b6a4065a3a71a94a8710e107 a851ea1e194caf5563f9e0a85778cac1a72261287215b996bdb1ba3977b7d660 +generate_key_image 9ff97a36f69b41c5cf483f0569e5d9d5444b8e0936a234cb71b61d0f9b603409 3f2891bb830b13b3d54fa269d888c9ffd5391b130a2cddd80b65ae2c0ddc1d04 1a7453473532a18b0419e86d276740a4ddce23365fba41db340eb68ba65e79a3 +generate_key_image 308f48c6eeecb67e7939aff2a32f55d097b9a850e6455ae81ee4c5323ddeb3de c215cb3ac02033826cdcd16237ece34d3ef0b1268482b435620400ca7dfe2a09 4c67a1b01957b8f3a9083689e5cc3a279b05f1960e66e541808fb532268c424f +generate_key_image 58b9dde692bdceee13c8453fb91c07a7ab3787dda0fbfe3532fd8bdee6b0e3cc 533bf594c8db27045d2f9b92002ecc15ef9579898be368b3b6d19976546a4801 ab9f67a20e0ac4648e5192d534929b4bbe79b2502ffb164290820a376de22d4b +generate_key_image 0b91b0dbd6b6ba7a38336c46a1664537f4db1016011649866d59cee337e7249e c16736748550440bb61bd19b566add4c26149ebd6d241742f8211884d94f1f0b 9fad9afc9e7e529a967ec3e2d8dd9837b668cdb3eb88adddebc70a8c468e538e +generate_key_image 7a19aed98efca899d0cd4a899b6e371405edad82163d27bdd20efd98881f6747 37a736fe4748ae672bd7ff889649c164967a2e399e87a1e20feaf68d67238e01 fad6fad80c10d54e16d5904859c8d2f052b1861aea6954158e3e3ff556cd6897 +generate_key_image 7df389078a966f31eddefb9f87f9245dd177eb55226a86ddfe7c7ab445dbc191 af3ea081da20869e1aed684a930d35a49c4e1ad01c2924e620352ce0b1396600 4f7398cf4011a2616d3b70c1e8d3c01c4ea58a5dd5b7957bec42d9a1a493a698 +generate_key_image e483ce2877579b15e7c205fe9e048f442486f1ee29e9bc552878431a6f720ee3 fc1179dffc98b3a369c891c71837a6c6d934a82fe8820782e82dc5ed425d980d b492174b2b9eec32aeb764500e065fa8c043f61024ab0f70388ccd7956a5e8ca +generate_key_image ec4bc33ce6a74ac8ab28d860ad69ea91048c1f9578036d58b08ae31bde237b8e f4a8d67d23dec8ff579eb0f99a6149f712411611a76fd2f68cd99c680d95410c c44f309ecdaf3f0b8d8567874069d364661be8af6146557d983f6c37e34a2c12 +generate_key_image c2fc27237dfb2b0eb8c84f2f7e28e96212768f4be7175db274f5bd6ca1f6016a 46fa4d0c96156553f34978766b8f55ab02877fbd3bc47109f24631c242152b03 ed73853fceea3661ed665dd7d21985afdd80e9443b8dbb7071226ded65be9822 +generate_key_image 1f072a3ef8580a82be5a9e8e9a29fac626bca696f82d8600bcd2514d4a236a01 9109ad639129912f84d8b44c61b41174cef05e86d0f8e5f31f0c66213a4d7a0a d1c8b19a4e338a633919da4490999c3c87129d5228b7811170e0ec6da06eee7c +generate_key_image 49183c099870a20838aeaeb2faf445ad3b702d3ba2e8c7c5e7370efdeb5a659f 01603b4eb2b5dca2b35348dabd69b899f1f83416ab83487c767bba7cdd904904 0e339b14f5e3a9b22f63157f93a0a9ad20c558972bf91eabdb71537c410f6a53 +generate_key_image 7ba887c3b6fc23618f9a32c25e7491c96daacf7214937af814f7dea19bdd624e 62c1f9de94155a8b102ba2a3cf287d4cec4bb77abf69187ae1e74d23ee57b606 97ef9a0e62ccdacc08dd3eb4ff2e4b90595ef7f2d36ae4a9fd6eaae76ce1e37b +generate_key_image 76628e31e978bc2b7e02c4dc3e229679af68cc4576c9c03471a5c700b4fd20f5 470a8df715e85d015aa5871e5b56361aef4dc387caec99138b5cfb6c020a360b f39cc6fd75b81af0564e422f1215d8fd19ac56132b3484dcd8a808cbb1870b05 +generate_key_image 0142001f0c31ec4dbd9d8788b697e7326639c84f68c058692db4e81cf3bbf5f0 32a6d5fc391c7f7d6bf26609f4b6a494fdafe19a9a7b36c2c37f3d7f7eb67305 bc3b3f3972f7ad92bed5c5df14426818c00e06b75be6a56400d1aabe1f3a0b75 +generate_key_image f4f817a816cd34ec02926e1da3595bea5c8d35b3d0fe6fff6840e44ce197e3c0 b8c9968102dee2b3ba7231e444e5010067d70bea30707fb08b4d4500403c4d0b ed650c87c97a84f6bfd4cb863d5452635c1b58430d08a661cd063fd57a0b1b1a +generate_key_image 553fafe836540948071e213b7eeccc6ac6cc9ea52933398f3106f546300ce26d f8ee236931a241dff8bead485592de42e414102e2ac4a180908a528e0f874d07 f277b293d8864c9c5b1fdb414b0c0301dc27413d7ac0f5a4c88e57b39288109d +generate_key_image cf430e5ff7c26a2933585afe479f96ccda666b516b1fcb7e3155386777a27c81 9661c0898b5656a696bd993db35f83a21d5d34b5e5a7ee2c02a201c188ff930b 820d4926146c19a6c562adf14f21782836bf635f4f0f359572ab542b152facb4 +generate_key_image 226152422234e34334e62275cba878c5c180b83e4c0d87a36c2a57034e739a26 00fd8e56825ae803dab5d8f84ea21c78628260117393eecb7f92ba6c754e6708 e89e2ac027238b1e777d1148b1785aff93d7537fdf851e3b78711a35b2fbd8b6 +generate_key_image 6515422ae4af534f3994c1923d191dc121259cb98e8a747b88a36b0f7ffb3513 97b599b1197aed22847771cfadd1abcbedf6767795ec4c875e122222fba2cd03 37d777f120b55adb9f8459b48734029faf237ba905f2dd1318533f9f59eb8654 +generate_key_image 827380a8c978ff5837af1d71b1a1a8a5175023c8ee7e90ad1cb36523d8e4a540 7c8677020dee2e6f293594e54fdc87b74dc413be5ee59218c11cb329ed80fb00 553ef344f8587ce25d95a9633164745f289424c18b864d85282871b4d84b6476 +generate_key_image 0760837e829a24d8e179aa28deffd9c92507de90256680bf3a8aa389d0142073 0aa781bac7debcbcb40ed571177d2f44fa3759333a9642eaa150f569bda7d905 245e7bc7fc6d02544baac3163fa4a396758ec314d35a57ef1ec2d604be87ba95 +generate_key_image 1637b9af4d66f617b094150fbdc5e596eb37d546d8fc83bd01dc08c1ce07dd64 e035996d1e4f5735eb7168703562da24ddd6d1cad76d83f5bf26017d14b39505 82418cb53f92a18d095d5302d5550fbed6cbf49a90f5f6d8e20a8c68b81a2a7e +generate_key_image 558bd431bf54fe33a10c492b8ff6754fec0d4d53db6b2d27e3d2eece8157770c 18de0625dcb80e22c1e4b11d6ad7843b7fa80639ce98e8e3c2451553b9605600 3ddb34b6ede5a28c3f91157526cc0510bb6b9f6a1b577d7ddbb469d2dc810030 +generate_key_image 9560c33f98e716c9cf008bb6772329c744925e42c988c162977013aeb3efd7e8 c485c5af62f19d92a29c5944e6ab714b975e6106b7b472b82952e1d7ec891e04 485ff53d0b3dd120fec9192f4555b774f9544e3735f948ed971f4c59d133ef6b +generate_key_image 107bd0ec62ba58ad6f2db7913996058b410274bced108c081ce5365115b81080 66c5bb725b3e8263e95885d957eeac8232be1b2be5c907a91d3a3c6bfa44ac08 310fc3809fd8373a20eb1d8169855ac2d2ec8cd93358bd65a224350c6f5d6fe6 +generate_key_image 3f7ba67800c6bf8644d8a422b1c45bfc8c30d788ea93c861a62a1461577ad605 df4be6c62203a6e7bae3e0989eea8dc808db917545639662acd7b9203bb17003 7702faea01856490a8a1378f93831211e4f21184a2a2728a3a0390ed7f2b28e6 +generate_key_image 56d48b07b434056f4075b2b33689fefb8deb237bf6114b4ab598f6ab9df93b95 4042f9d9796182214d68e41c2bcd439a88955c39d66b5a01f2a012bc56e5c100 c44e5b533c7bc198907e3b9abe50c31ceaade094ead51762b2701e93f0220118 +generate_key_image 44d3e87469859ba8fa7234577f8d008b9526ecf32a63597b5dd754b0ddb7cf4e 16d8ed5af06d2ea66a53342fb2e22a9b9b578b9cdaa119ac66b684a8da676209 6f66f96b5487d079f49d6ff1267c4f122c58c155c8a92f35c163a6293221f0e3 +generate_key_image 192bb856e5fa3fdb307d6aec87cc009eb55006f8695c245032c5dc14fb3c99bb f239cf46bf56569d9c06c35cc67264dbc41f9f24b7c51a047f0280ab2fced00b a28008ae62c136cd3029d7fe883591f22b871bf41521f686529138d5519753c3 +generate_key_image e013d78282b34b720036a2c0d4c52d9490a118be4225df8ad5606dca8571795f a4cb6a87231a296ba8b4bec3a01fdc85ddbb3ec76aa5ade68a24bb227cc7620b 522fffd954699375a0de7de49c025548424d832f64455dba40b5210fdeeb013b +generate_key_image d7ec24756c1c8ff255e40956d0905bd12797410d175add18074d08017e71db4b e477585112c62e6dfe395c2b319ac44313f979c5149b61e0117c8980e63a5403 1e7e34d6974d5b492e06bcf505106c8f45c3e9d0307beb152312d8bc667e0ef7 +generate_key_image 985333b4f9e7ad9958e54f1de6f3ec8f2997320cfa4b4fba4598e1b8d4419833 99d5c2e3443ac995ba838b1b300c904139fa964d2e5bae896aa3ae8bcb117105 b69a65a68e4999b1178f427b32cc07a85a4239a3f02d574e285dd2c4848674dc +generate_key_image 301c097e392106bfc8716ad3497b8c0b2905d398f5fb13d2dd84b1af9b884fff aa5e87f971b1cbd13f2b7eecbaca4a3b9da7c879ba039eafe6e0454f93435b0e 2bdd678aa47e18c53f06383bea6079ca036a6d3b618377fb20f4dcd10500cf6a +generate_key_image 9e5be035daf3455cd8c17575d4f3c7b0042d710258b02ba7d161a633eb101556 52da33d3a8e2fcd2bfa0281322d931a9f29dc367f5b16cb8a6f1016886024306 a9d2251b6374950dddc04b0ac0116c4234f1605b65600d3b7342d2e5eec30984 +generate_key_image a1bf2b309b91fdc8878217f0183958df2967523c180edf46521026d064914692 4d905a44d46bb7c37ed759b6d32d2e96bce6b940054e3cef059c1b0df994a101 539a9fb09246f9a9af66d0d169883ca3e5874d73b43a58771b79ffe3b86a2def +generate_key_image 83f07325f5044d34fdf25c63c4db683baab4fe2f42cf24f45eaf819957800350 e19571cae741f1cc5bb58fe2b386cfcc94f179339206c9920cf2c54c280e4701 6fd6e1cf159988d7aac166a858a9848cd7b58aa5274c63bca00ee91475606753 +generate_key_image 03f69930fd4e5124db40cb8199a809cee4384af97029349678795016d3e77e44 07a4fb0104b6b0bda8d523424075a81c71edf3cf9d5dc74b335a77c5b7e3fd05 bc0a60f3bca3e3228678a4ea2478cc878e9ec09d2d670f510e45c9be1f2e61f7 +generate_key_image 5dd72a2b4ac792bf364370e2ec1843bcb0005f61b0e66c35a345a2e777bc22c4 7a510a7f79b1c21c2c6fb46d27a46a3c84122f74d261466b98bdbdc9b9db1005 83ec1cf55e2b2ecc271f05670f1ea1f9b9b3dbebfc2988c11de7a89a1810467c +generate_key_image 2c465e4ccfdb9fbe7e50023debe1e3436afa2467bdcd01e4f3c471954967c884 b2cf3febf09c481890546f34059c366b2df91da2d7a527ea8b8dd6781f8c270e 358e8d7a5175c1a02d1921e69ef473abe266a417fa558e21864897c894ee527e +generate_key_image 37a3cb5553015b44f6d6ebf0fe210b2ea597b440042e341d97136e20039cae1b 445e51f862566f5a97ee5dac768418501cb0ccd2737bc63c928e71d35628ca01 f8d61e4aec45152beac33168ad34a212aac7bcd1545831b15940d39cd3b39a7b +generate_key_image 7e9095f471e1d0209fc60b183c6e7dea7f303a6d2b912a65a8da94fa2638f95d 9fd14ac81760886dc0bb0a4c5b27f52a086f3389748939eece236b4972dfb703 d68bb883dfbe1a45d7bf32cbc5c3ab2cfb0beff65bf74dddc7545458af44ee6e +generate_key_image 2a290ab5b2a93d7ff8b6e5413b4e24c2db27a22184c6bd7639049d5bfa0585a6 dfd5f0a9fb9e8e4acfff1491dae9a02c319c4f51e98ff5c4fe2749533a576f04 fabaa7d80074339d33d9ba018e291bac0cbbc3511f186de5d7c86c3d643e85ae +generate_key_image 4157d350ae4a9a264e9e1cbfbaaae245d5c8164819220586664fffa4518bc25f ad76fdbfa3e49e5f515f0386b965930cc5baa9987f46e9868d3635cf3e282d00 6eb728845a82688bea5d29ccaae4199cca9cce5c222c2278114f68b84abfc691 +generate_key_image 1ceae51f2c181f59380cc84bf292ba44cdbcae3812e6da88fc8eec826e816989 691eb9e79cd949ba3ce4739c69d59b6708bdfd49451e0e64183119e94ef79301 27a6745d3c3ed6c492a37453de6d3ca4074da79f68ca86e834f757e4511d56df +generate_key_image a0e1d97585c524a5dddaaab6b6522e057553c409c95f21e983553cac0cc1ef87 1cb206dfab5d689a084b160ba9e0aa26015e76e2a83ed1bf49eff92cb1e16c0f 4796e4f7a6a85132457b90dc059d2af6a55e1761cd10fd5a816d5f1bfeae04ae +generate_key_image 0219faaf779482b10d0bca707497352db07b3853e6f004b8ae06638fdce612b9 44af9ba920a0ae9d1710fb8a09fc20a8cf70aa1c673cb942b39a678cff41ae03 9b4f60347f015fdeb0b8996c13946fa684c2e1eaaf7240097440beee00dc8660 +generate_key_image b3560983a712a12acdc12f21ef2bddb9feafbf011e654d3d9aed3768e2bab8e7 26445733eec07c2e13dd49cbd9ae317a12d5e4d08fd231ecc359456bd90d9d08 1b06a380a333714b435493442575d9db05bfbebd57d28a38752f7642b7f35a1a +generate_key_image 8ae81e3900184c8a1190d6f31c1b525855a2ad1001c49f7ba04eba8f1948c8c3 60a2f17db1f778287231364d506a324b3fa40a76183988dc455288ca8a443106 08164f6996327c0d656a6d764260eceff6d8fb3e0b84eb7d223897e03e5355ac +generate_key_image cbfd9fd4ec6bd8650ef685191160146dfdc011bb53af517e01e066ba5d477eeb 707104eb9dae0cafde0ede947c06fcf30eef3907a3df91252ed95aba2bfb3201 3025729d9dcee38f4875ffa0f6b5138b1a8189d686f15b96b91c23157db64c8e +generate_key_image 0d3914286565aef4b96cfbd98918c1c0fe58d2cf146ca64fbb158dff925ea91e 43bb9352eadd356993edfe7ef14d02849561c482fe8b87f2321ead8e84856000 a2e819b083f937cac944dc6c62ef8ebd4124de7abca4966859da82e7b4118021 +generate_key_image c617721a7059441aa46233a9bc7a81c0e9acc24aebbb4782c5cb553cb4970312 aa8dce95f681774f22ff00c86bf481b311134951596737490b5a1cbeb1790c02 e0c01ccd2e9a7d3cab1ce75f832ad1f985892b1133ebd5d7bdef96cb74ce48b3 +generate_key_image d709f482386033c7a6930c462ebfe91f996487a778db1f1a04a00f6a21300208 1bb198165541d78608f1bf224f38c38febc45a9fc50dce0f815943beaed5b60d 647e6a361d4d4efca8b957f0af9f5cbb17a407c4c9b38dd193c6f281441ae19f +generate_key_image c19e7fb2b68a9984567d3ce222beb83ab12fdc55081440081b020e5d025b375a e98b0075b07b1dc5d88eaa06e8db2bfa91eadf1f5026394c9932c18cde08f504 a99d2f4ac4f2e8937c74538b5f3c19c819fc50956bc925778787bc6e326f8845 +generate_key_image 7ea9abdfcd6ad456ecc59d6f3056cf24503b671335f4083e34194b2cef33eaee d32b03eb1bef6330eac1b05cd6a3702a4febc8e99b274e0c81d753e4947a5a04 accba93a8e048731ffb7ad1a77432b31936d02d16c08de5f96a248120de12c8e +generate_key_image f534090a6c2bf37a9d661233438e93fdabc08fc879651feaf98305103031cdcf 9761ff7c9160cad664adc26eccf54f02e17c158723d13071b718003599a22809 3f73beb8f24ac24c2cf1ed2c2e4c763658753b8d41d072b1e766adc06e413fdd +generate_key_image 1bfe9c651b5f94a3065e3e2a6262b542e114c882bbf77a204e5f242156ca604e cdc201c930c0b04cf9c5fbb7519ce6eca7b9e123c480ea5256cb138966b08702 c7512d358021da8479c407f7c9ee02c4f3ade3d9069d7fd4a8e6cc55a7a21957 +generate_key_image cfbb70c6395e1b21608f61489867b6751cadbfb3710d871c67c5f13c0178121b 924011fea01c84290fe4342f8c29851289e127ce610ce1079f1adfd5ed9b2903 5837aaa21b0c3ad7b357cf6729a5077664050639a66decbedd58a5fecf37b5ce +generate_key_image 937678162ea172f1f72a4519c4a6b007ab4a167d436812c3f3015600bfea7055 fc2d4909bc57b31b01367410b3da56edf97aab0aea8a4178d97aa7b83c292508 e01a905a5f55e09b11d9289a20dc93fdc5ac35633d0d5ef55f7dd5fd557fd348 +generate_key_image e59c5b3bb61b88f83b0de1bfb43f320638ef378fd63c5c9c4c2c6754d1045993 739523aa67739790755baf995594e0c41c521af3a1ef7e4288c90aaec1d5b306 0b920663635b009623aa4d75efd8894b416e5c629a8bfea7870ecf5275fe922b +generate_key_image a5683272afb0b930c992e6bebc75b9f27adca9b3c9274e9464fd22b0ad7c6d04 24a8e8e372fe533483d5f2ca09f4e1b09437e48b0e0ab0fca0a58cf8bf6fe401 ba54d02e2c03ff935d3d69fb3be705a326af04d9098aca5925bb475883c0a0be +generate_key_image c6910cbc90269606ead23089f158315df303e9161ac7b9c19d5207e83ef63ee6 6f4a5e38c6ae3e0547e595ef1cf16f99024c9b627a8e32f19c8ad8c16440040a 7d24f247e62e620ee28e1c76e3ebc20d0a95babf814b35f0d04a8a15d54bef88 +generate_key_image 7c3fc03bd3e3fd48842dd16338fccddf7d142baf71e39c5dbe56a729f54967cd 0c63b98955c9aa249631be114b000d32cd9777241548ad2096b496ab0fb8a009 331991e8c9e2fc29ade2de26c490024c4680cb16f3139083a5f9736760d2fe13 +generate_key_image 6cfb9a8d4dcd0a7d2c9c2a67afe48a215048b0172b1566cbb369a6336059e243 6538c2e78582c50f6d64c7579802d6d1eb9ec531f5e51255a2c0eef0a4c4eb07 5794b81c9e0e1e6113c58e1b69b501abd4078ff5c451f9a6d7f6ac96b3b5e318 +generate_key_image 09ad9fa5d73a6f85e44b675bbffa877bc1768383319ff97712d11898915247fd cf9e21f9b3c28bf09e26fd8d3e98d9cab2c1fc3ecc81dae013e20b9614b4d60a cc0b0abb5928d6b9d1720f8479fa2e505854fc265934febc2c365fc90390d29f +generate_key_image b4c2d304e0e38f339f800af867925b61cb473c9eb529c03cd90190a90cf3c608 320ce64945541f7dbb0667a3754648fa95ec418001bb4d1d56658d4c90fd5703 5e79f062c5559646940d2747d9d22c98e6486170760200181d732c15f0c0b988 +generate_key_image 3697c97c16ea40c9f4788ea72f9113dd3af61dc1faa9e1fcb029e049744ff60b 8714e4f3dfd048ec2bc30b1bc64f7f9c75ce0f8b1e692389b52da2cb5950ac06 6c390f7b9425c7de137f4d76a24fbaf2666e077073ad8782d414c14f1811e3ff +generate_key_image a9ea75a00d3c2d7875e3c62dff94f84b76b8c1c2526325b82d9ebf5120fe75cf 9e39469f615be755014dc08df3f072bd5c3a28c1a206d8ab7e755dfb7ac5dc07 3252e04723ead07e5778981a24d054a9f7227976404b33429ce17415f375aec2 +generate_key_image 39ca6ef866635d5c902d420581c0f4afe314edb9b5221eae074db3a84ba6efdd ca3eaee74049a7b959013da89c253d2d8df17d418b65c8f628112beaffe52300 a42c72f7e2c2521073cc05a09974b2d1fd0ed827b9367731b98ef77bc121e149 +generate_key_image ac9410d3f812ccb8aa08f03ba5a49b314a173c1c28145e638b26e25481e12a81 57bd6d4398ff3d0af7b3b3b6b94bfcb01e5ea0831b9faa312b562a7ad262f509 7af0f0fb405ef3d2312b3523bb14b4453c87838264d672710ddad712352ddded +generate_key_image 034c4dcff817e848fd1d4e9c44e23e2bcafb2723ccfffda69f7ebfbebb0fb4d8 2720760e66746bbecea19c10c128102628188885d0b81e1111f8e2f08f8fde0b 665a058fd0ed11c32d229ef761d31fd56d53842832b2b08ab09f9c3bbb032dff +generate_key_image 50f9b621ac02c80339594d50fcaa0cf73441aef1c312ae5f8b81d4a5b4725468 23a960b9623efe3009e68dcd3e350b3d52c799085e181f433a154259bd61b205 50fc014046b0541aa28d55662caed81dff2d0bbcfc07cd5355a510da19824efe +generate_key_image 56b006440f378f6ada78819683046f7c7041ee30ce967d1fe927a675a627bed0 76d8f12a94e0096f4f6d01e4407c4d04acbb3098f6e06922407ddace7d316909 c901dbc238aa44c8a52b90b6f22b813561d0f9c871a2a10889cdea54c9874919 +generate_key_image 2fb2ed504d151c023d64d66929b25c27943ca06370933a8163c76b64c318eff6 9edf2941e15e49b3d9b26b53c7138377168d00eae139a6660f646fb7c9f2200b 942d7a64f6637af06d635711e7ea3434bfd554573451c2de2594812f0194cefd +generate_key_image 29a13005cd31f8ff582708ffc7a79b0e151946fdbf6561e02c6b751a100495f5 6d250a4b78c63e4198074db43db27657e893cf3bcd7d580c78e11161764db80a 8f26fdc11d4e34f7b22a3275f5c4d861d09edbd70fcbe39948b09eec56db6402 +generate_key_image 6c92fbee4b167255b548f0a1f246650b0b2f95f75059eaac06ff23e977a34a25 4495eaa4ca98756e8defa63ff47d5fb6e1a028948b297ca2a554d11d3940500a ae5351770cb475059e68451e86f6fbdee51efe4be94cc9ce2b68911d70ff4e6c +generate_key_image 4d11158be6eba3a3110dec00364088f3370d70b0519be58d00797bc724ec6973 a046d6c805bc1f19ba77dc8231818f7a3fad573db584ee27d488503b50e0bc01 65dfbcf70be8dcda35ac5677e84e1b30fefa7c54ec3b7ae688556257be83ec60 +generate_key_image 455b0ba3e1333def3023fad20ef415f1321fbbf0d231765e97d53059f6530784 d0f3f3130efcd6e9dab1feb782e3e99fab2163203e615d844518852dbb944e07 8166be3bc6935ba300ffce886988b3a593030258a804fde219ec2b506bcdc5fe +generate_key_image 4a9241761a590453ae54b7f8a9de4f59d3e2acbdff32c4ac0781fd59f63e2e23 d966875a3de5f674d4be95b8571fdc72c4b8e9a20696efeacf6df69aab35e001 0924fed862948b1fc8a0b207af188cb877c6a25fa34e154cdf649961afac876d +generate_key_image 8752e58b312c5efd1bc4e280a21bbe745046355503d98ebb04b227b655cd2875 43c8cb8ecbe413dff8607b1d8eaadca70932ee8d85d73561f4970e86d0558a0d 907e04cdbf09616f828bde162a42fb2b83b8489df35bcf702c986e61e8dfd9ec +generate_key_image 95377fe6ca70ee381dff2fe3d1bf72f6a6393f415db3719bb1294f9c1660657d 7f881b069196a1e4cf2574e84bf29632b3eab7c31988f8db5e3fff6f6876120d 56db6576d01e76e6678d5fba7f4d73781cda5b28638a2530d558dffe2c7c8c3b +generate_key_image c6eb494bac5e8ff971dbc07d5e07bda564ca0817cf0cc9b1bd16bc7f2c44806d 3c75b232a9868d98dc492a0a532d50bb196f8ff873da245d79c778208ad43e0a 9a8bc70599d0f7aadc5fb07071e7df61810f84caa8dea626927203102c00c238 +generate_key_image 8e2396439e6ded0ef7ee22a5055c8a27e23b69eccec62d4ab44493131aaf5675 2df4139d0a58b860e8ac01d3c4ed1136166f41df822ca4ed797d5c080a560206 4f32df524058c790a0e0d085dc0a74a8ccf28ed48940b765c888c274fdeea41e +generate_key_image 82b9dd66c85133266d84e6b2ed0accda9bf794785aeb696581be4d21149086b9 d62428e42049aff75ced944e9324272ccfdbc81fb8dced7e9f8f85ae682b3700 e84dcdc846ce4d8b22f9a5908b480dd94c629dd2ebb6dfd762f33a7a53f4498f +generate_key_image 6a5410b73a02d2d70e6b403563fb0acb85a2c8e74f59f1fc7c3f5d08d9ea7b0d ba1a183abf4a128f42e9788f2adb1e94fbe648f884573aaa4f680033eb3d9a00 adf420ee5206a2f841c3cd4d0e3e2ae09ff9923ec4fcb85806f07c34292d3512 +generate_key_image 626b0db85ce194240a8565def7f1db64de734fe6ad30b6032f1f38d8b026bfde 8c2dbef79033ceeaad7c230bdec4428abe29540c6f3f389841f2c8928d42380c a0eed662238a7bcf051c062f86d3249a44c50c5973ff8682caa0c64d55ab8ff5 +generate_key_image e5c19ba2cdf50b5edf7717be1327bee5b955016414a29fb77d76f9224f05be0b da6eb0a8419150aaa274d7335f9508d9ef4e2faedf59d04dadd6df8b546bd702 cb0c6f312d70ad0cf94d79b0ba005341194f4f4e7f3f8a763c918ad28900d7d3 +generate_ring_signature c163cfc7e5c5d7b136155d26b79d91d380c7d7bd7bc9c608ba5ad9f9bb12dc4b c713c67710f09280417aedd46d8c8e65957e7f363c2c1a9929733227e5997836 2 a1065e0d19926521b8af3859eacf90abe26336b7e90ad649aaa860884cb16127 6d63e898708022da25c4cefab9d3da5940e5e9831ef336befc3c8b8712df63f1 2126bd4fbb4a1e9f82c176d0ab594ff9cd6cdf13bf9c2416800cd67f3a249405 1 affedcb553dd79b07e24cdc8dcaeff9eb6bfbdc7e69bed31e1a947d52d82c20de0d21492ce5f5f624c401f8109471ca96a98f99e30005c619ca940d90222af06db0b95b7df2b8e519894673afe9a3aa283b3142f078f6097ec15d1b703a712079ae287da2ec513f853d386608d60e85e5847e44b0521121543b48bd88b3d040a +generate_ring_signature fcb147f84eff3cccee07d4a31ea9532af17186ec0b707bfcc02f99dd86efe496 8894d372805815ccbb248001f0a1437cebd547b011036c35e08b6e93557a03f5 4 2002f890cd8df891214c2aea57e9526ba37b168af6d4ba13374df6ad854c188b 61c44efd49b2f0a38e0ce16678b244e729116bc0c9f518a42470aaf54f709a1b 4318f450b54d77a928cb7dd5b701c4f66d5a12a2df3df13253ee130c48b664ac 193abd612b79c65361406b3e554a22fe34cbfc974d3a1fef7d5ee2dcc0f75864 c1018821cc9f7657c402929e1c2bf848c7f86e6258bf43075966241c7b94950c 0 28444d8ad7fe727eb4dd3441d557f0387b34537304c10d8ef914f14452e46e07e8c71db1941b935c05da2dc742fcb6a4990e992ad3ad44ed2824b306c52e4201e2d2449cf18c587de72a07be09dc2b47f1fac913d32002b12df7db66c8b85c06c07c46cb7f7c102801a21cd4b606991a1e5465c9bd9d91eff876f3e22364d70ad7b7da047d45e44b49468e430d1d371808c82bc990fc9eda298526aa867a030650e07e9d41a22f313c957d6235609a340f93dc833c56fe54c83b6d87a7480200542f37093a3a948c87dc86767eca8c2a1f235d45d9f923bfc276e46467dc50072f2522498b02c4f92fcc02109bfb8f808941d585917b281524d701da984e6202 +generate_ring_signature 3e24d70d4ce766a3939f0510e9d5a407c68fb290978128d80cdf54f3875e5142 f20f159296aef881b42fb8fa8ddede210752ad25edfa8a02116f69a6879522bd 205 248e07525d887539aa39b3f59e3819bf2b899330951dfb06bf29c57bb6ad03ec 4bf7bd7925749c285cb56b04e3a11cc422f200fd5d82189ff73907c4e44ab2b0 45ec6aceaf4358927b08b321fbb9c0215f410a585a2696fc55d3572639759a67 e7b23621577d5c9308f683e1925974808b7c98c66cf8707e1c44cfbce7be2139 9f7591b424254a0add41690908429a4349aa13813cb4b18b0865901751581618 1971d8032467799bfff92ff1f4cf51cf53612db6d451e939c908266425ebe2d6 e9c197b8e05848a77ebae6530b5cbb1d1f479109604dd9491182c4f4537f3d85 6868375c1dfecb6d36c025378dd87880402a8ea775376954bad2bdd78ba5614c 793c0f9c8c13fc697634850f0de80ada32d7bf7e36c33c13615f8ce32e08ddb9 3efad04980172a5304befb1ceefbcc22bf34479c924f29afd264a3ac3b426cbe f3df1e19037bdbe1fa6447623cc54d957d4870e2be682aabc434db57e31db09b 1472774c8516b97304021858a30b7684aaf919b8eeece718b759417031827b70 9bc562dfaf23e1141711a551670210262fa8a59271dce5a05939d906a5d3cd7e c5277c1da5969dde51d1fac502a370b8bf4f40d13824fc919670d4671b402a29 3bab31939a738ec5605320e83f6fd439853de054cf99d9b320916ca3c26c53b9 14e6aa490dfe036e5aa38ae3ec3cc7aaa25521b1f026d6768167329babe1c809 204dcc74b8039c46d651fc8852861a974c0f877d573cef9b5d0d7313ee397344 88eb7764c126d840d38fa305151e8873b14a441f42c864d873ffd7aa2c7c2781 4400568053d07f4c60d0e7cdea942416650ac083123644d8b2149af880046c08 d2e045d4a46a3fea754c9c2eb7e38905890909ac47fe56619dd51590f58f8350 f87e3460a6146e082b11765aa4b44e358ff50b24024b75d5ff8218178e60489b a0227efd99828787793865161872024f6ba77dba847f0873be2562afd85a64bf 2aa846464869df42be42238cd9201d604007529c13ec95b81cebb299905d4f44 28bb637c468f1df6bc7f2a2ca7a7d00f475fa487409a0f4155d2bcb2db1e9b00 7cd9368b837e9e1ff069f8dbf6e1ab2e9742515d37b00f53ac84e30c5d3b42fe efa2fdeed740b39d643696c1517209c946e7e797b4c1cee0b339464c9e8b13ae 28d9434afea0f082e62773fd1cd40a763e8a2a513c6dba87aef9159e6ed5855b c7c58f516cb3621ef2364d2a8a9f38aabb9b624b932300046569e9fb026d36c7 3eca6375af3115f7a4b63d229c5182da0a3f624de6c7e2b50e13a7da14a322f0 de695954187146996c40e7e9570671539d471235c7baef7387ba12f1fc06602f c973a3ffa8ddae2cda3fa2d25bb4a412f4aedcdd206274bcdbae18a3d6b4c25d 31ff577f90124c68d03ba3fd815102f4ec1e16016e6335ed47d9637771c426c4 9dfd87b1b7be1cd7fcf6cdc92f9e820f6ed806153ab3ad9312059c6b7f284def 369148b83e19a1d656e101999c3b1457e995d8a0977c18bed779deb21ef832e7 d0934e5e0dcfcdf78c7fe82f7fd14a6766d4cf608d6eb92af30bf11ce79348bb a2fde41de4d769c7eb0e4b9264fc02026795fa317a48ce1f8f8f8f6134025460 def1fdcad431534ba6433a21c71aa85db97af80edb59a4f1b5fddd853fee098c d1c2f6cb0b2e54eff1197da8808d6a656f7f2d97967015f350f81fc2a16cc8a7 ea3ca0445c0634672a39f8f196a86e8afd3028b1ee852026e53b57ab5dbfb360 24bb3801bb6e9ad05779316f57bd3ccc68f2b3d92b574f49a4260d6ab7a632ba eebf5bcbc984b77938a69db65b4f705507f9a052aea980fb5827062570470bf1 ef72f29252b3f1ae595027df180e6d1ec987e7d4faaa5832d72c759f6c4b33fb 5bc07dae41d01e1fa1f1685c7dbd8638ac8e638feadb14a7b89933c3d8d93696 2a7fd6ff576aca378d15d6abced7fecc79e1f7bca94d7793e11cc274079c1c80 03f6dff87242ede624cf6b0648b2219d84e2800f86295b45b2d62c400911936b 00d23e8eecc6d188b2593f276c9d8af38786e8dc83fc3f4888357576e0ce1f81 8cc6beac7149d2141c2235648ac1deece301e3d43ee0434a0515a70b16a10084 3b144ea00c9529bccc292dbb9f38d2c53d3476f9dd2b2e0bfce2df2fed9f9487 64b572680f867185b15807549d9248eec40382c0c2efdddd0b6c18a884ab811b fc56919223390f5c7be19c6f50c0645efd3d416b1c740d38d7f95fc6983f5297 b1deca7db07cd5df404bd8a85de71d904cb42f7cabcc18abbb15673da399fb7d aaac03ec8d87494de9d4062f589bd263b6fc826dd85c6b81b66583d6abb6fb6c d5954dd296a353830ac2b086e36537b57320da789652df3df9b6bbe6f963e9cd 6941758aefea63b1e1814431db49e0d65e15fad87be16247fc9169fcd886a912 92ec9eea5d501ec3d35f585065742ea9b6d4bebdc92dc9556b3760212bbfe89d d2af164c401667aa3f7782ca684a2ee67ca605f8fb0dbd5656240bdb70bded19 1b1fad3503db289875715ebfe9795fe7239f155cf168799bf5dfa66fae68a907 346f8fc03505d438e1f2f53e397a54be6a6735bcb6eaca95ed2fb24216b4e648 20833bf69f4ff4e5f456de27820886b55171897347790788910da5283ea9ec2c 560d4e3b288c01afa67db2c614bbe5bff09a31475bc1a9043e63301bbd6c4612 d440f85fd7cea8e03ae4a7179e71a1a4073e2d9682fad350b5a4019681234809 fd4a5028a02fc5f5662db2f9fb3552838e0c821681e4ad787cdeca526291f36a a95deca478682b5c829fe9fc6c2ce0a94278b502890f310c8874d59f11bb740d d7efe818d37558b3fa9f0954bd5df3f56460d3532c71df732aeb1460f8f164d4 af501c86dcc8bec85ff18e4f497564aed42f5bc8852ee46baca01beafa9ac03d 9622638ddc97553bbc19971229a7b25fe3e4bfabd413b368ae1c27231c37e2ce 93a4ef96a25f29ed78dc237736158e94fa09f0cec4fba7535cb51c7e22b465b2 473af0fd60f2c6c7364c31861bec5a6263769174f8701795b0cf315137fdbdec a8e22c3f087f333b762bbfacab491cfff64e73526d9b6982a9765e79f9339088 766dbd8be5c21a5c33eb21faf849d022866a605febc25251dac1040e93408434 efe4d0556f7822ff772ef067f6e90f98341f61c07fe60259c36d3cd24b753daa 651e89d82e888aa4bc71901d0e28c5f43721a4ae21ef93fed983af1df1003ac7 5b4fbb6dcaf62bc4b6b74514ac1bdaf8348424f15bbbb6cf29010cd75d9f97fc 1cdb48d95cfe3b656a9881f344c6901ea8ba2f9c1663179b2cce8e1eec3d36cf 8ffa6aa23ae54e660d7d914d99bc2789f558269bbf2fc99407ec0c070a53adf7 797e3cd559fcb1bd172e1077b95e4c447250ffde4bd8cf329884d97c5c98b4cc 28fcc4460f820cffa414f96af6c96081afdf43af0743f58c9ead5fc0fb2df34d 49fadac18bd2d83030496b54d969971fe9fd2e8eb8dfcf620e80ca01990b7fed 3cc500334d455b782865783d6fe4babf3ba075928168d258e1cde2c8fa7c8551 378e884b66a96975db5233c10ee84cccc9b3698d1d531c01887a9061c428f21b 33f60795d21a93b6f67f42d4151a862dd4c789acf889fdfb8bd4226679ddd158 ab0f1a3d38549f5939bd9ca58ff9b79ae2d21a61a9c0b835b8bf366ea886342b a4d793095f11636377242f946c6424e344faf2e34894b162a5ee2866309fd249 0098a4b1ff11fa22051c219672db7da71c7842b671d214d01f69eea96f2a7c83 da55f7748b087b49e8feda4b7e909c49ed7b81762ef490e62827893be7827d1b 60f1506000e1f92e2b9ea8e19a16a05490224597b9d110ccdf641db9b996a1a5 089c0205dc2b26dd5aa0191d86085694633b2da106811a1634d77119e9a2259d ce42b12e352eded71fa338b332558b8cdcbb64eb0be444fff604290eaa8f8b6f 32af31a51455b00dc894bb3d35f57141fbe94f61f3896e4852114697c6b4d962 354279a84c351e66200d15992399e8a8ab289a614fc0132f5357fdf58141237f 3b85a5ade03c2812cd821d43bbfab531acc485e80cb4504720787dab0aadd6c7 444b3dcdd13ec1cbd83b606232d2deae2b9f8def7d2cedea343b30f3bad47473 1c2ca2cd44d9b3138dbed7ff2bff078ba5e4fb69b54e982c3af7e42cb7aaa8c9 9ab45c2717d792b992cf560dc9be34a36f7c8c25a235210deef19fbfd35bbd78 008db3cd774ba89c1a477a07a757ac2fcdf0b8d927cbc5af770c0be424b18fb2 37c31257e706a47dd9cd05fd9ebcbb46039de9332da4adddc745d6d1943134a0 7d56b638732f7eb14f4c0dbe75c136d21c41b67bbbaf8250fc90e025d57b1969 18ab06ae6d9bb16646f6befff650262f915ece5813cd611935b5b0bd0de1bbd3 3f28335513bbeeb54ea06e0866b2c9481a61557b22e9df90074d35711dd7d7f5 78e9a3cd4030247ae8329f7c4bcc773cdc36b46cef2f75b5723d306ce3352900 4361fdd67d8112b50dfb08dd5a95f481e7363f75c5470aed19c55a941457bd5d 6b413706cbe50e4670cc0ea5b6afa3a167f970a7f8287232b0ede7aaa37a4e2e 08cedfbcfa0b5936d0c03afa776bd4d73232c3cfbe811d36d893a41bf7744e3f 04a52105dca47ff5a6ec7255fbce0ae0c3bc1c9bd29eb34a22e9b1a7fced8dc0 66113331cead2e30a473defe98571b3357504f992af49afe49888c5ca2d73b4d 7c711294db896ca134a525ed8d411c1f1dcf7791b8bb0a6afea6a2504439337b df3b9b02aaeee398491f5202cc5b6ffd99d5bb5cdb83ae09a10519bd063b9bb7 bfb0a56eb36f242be86477c11ffcd3a15426f6b10348c6355388c14b5e9cc483 5f8834f7909ff179da956910de15cff4d33bbace6536a0724dcfff9bab9b66e0 957cb4a10c6a9c3c17dc8c391a164ed9eeb5d66f5d4c7f8804e794f1d4b36eee 19545545e0848d86dbc4d81f9d2772a14f461a65a87ecba40c9d8e4ea14949c0 2a542bd907fc60c27de82ead1dcfcc94fb4c305c972c938b5f1e2736c5aaa89e 16a4b802b609120eb0addc6742c6fc68d572786833f1c0d9f50622bc28634409 cdf36296b76ccd4a3b8e9ff98714e5872eabf108a10278197f5f16be71ee5bbc 7610e87e742f216aa534f2d99aa1554a1bbc3d36a72ef4ae33cdc1d2932cc212 f45ce29c708060fa144f4ebddcfb546460549902984a99acdd5fb470fa385105 ba549cba3d0fc17f70d2615c91f8487de469316913977e7483f25d05234148b8 8d26fd53e3f3934502cd794dd599e9533e7eb3a7d5223062be11857b40ce5ff9 2f3483e24c8c14b74ae29efdad531a5b77f17a997ab9b82a0b5b3ed0df69befb ba3b44f5f33dd018965ff6af71834ecfaede9cfc8ab2ac5c35f3f43abccea9d0 9f1e175cfcc0bdc8d8a8e2d1f506901ea896c1077dbd6332e014d94486ea6362 c5f7fdbc1e6d871a7903f6f7d1b95e0e5d156ad8a6af949010c5df4c78412e7a c161aa07ca2116788ea29ea109948de41971dfed6be5ec4feb870ba492b67b4e 22da8752fdc5e796905f0571e6adb61d1b5bbb6704fa54da6baa994efd068752 67ad5f2a385ab9b684da34486c6fc24c188b212e54799d50453f396d72ec6b71 99cca7bfd2b23e1afe56153a2e68f616d8c9f7fe58f2c701c8a4737b8364df13 27cc7115231d5999237f9e7f52e35957e077f2f3f4bb72fcd314a7f2e4ce6ae0 c654227c51901929f466faaa822f28018ab7adde76b0806b67b66a47bf1326d7 7ee07234ce18647c3cf57d8c2f590d577c824568e6f9e43840f2831b700afeb4 bc619ed216f562d4f2d5fe8c44b1396b26b464d55d7ba1ffbf80e9f30774c107 1f1ca70a52e897706eb6330406232d92c7282fc3dac5edf40f79b9e611d84515 3fffa6347a3f05e4af6fdf62568685005014f585141bcfbc70648f7fa37473c8 cd3d4088bc6c8dd30fdaebd2563feeffa5cf993604de36ae75a872eedad632ae fcd3ed63f7a7e3f5dcdb087360e239774bc0a10ad022a1a6ac9da85921a5bb02 af82a850eb9510aa301a16e566e13654157df7f7d93b021b10d3305de4b77985 6310d4a313ae1d91f7a06848cb425bd235af0186b5714de933b019604040db3a 3eda24385ee378278de835905cb765dd57fab138e4e204a7f15729a8f40b0e99 03c02568e74162c3da637e86c56f7a53281dff1c0eb738fce6eb4a23f9dda79d c317236fb3db5710cff8fee1d08f734bf22bf1bb7ff23fe5de19f1439e8dc40b fd35c09997be354538ae531cf74dc7342ea9bd17d5b7760c6d749b6267e8d2f5 0a92253f78d10081d0aa77704ef2d645d16988861a6140b5d8d2b23b2d3c1d8b 61298e0589cc68f644d945891a4a0a996680c4a7de067bc54f6d2030465dd4e7 62c89674499856439d465ae171b3ce794dedaa359b3db6e45640da4e9ff3b2da 5c7ed377ef472b4487e182498e0e7b7ed3177f4b87748e8340ff71e8b8ebeacc 818aa8950b67b2c8709095f5e6c43e0bf82c6066ce9ad23d6986c09a25ad36f9 383925a1028950bb4f241a6907e6f9aaa5e26775d841cdaed1570813dfaa2540 83648f24280a7a3d4c5171440bd7a7180da0e144603138a6799c8b357da9311b 33015bf374d44e55ea2fc93e0a1166311fa41fc0deb9196d2d67ca8347059f8e 07deaa00fae099f893597b6e4f37a9f9c3c2f075c173c7d0fb186f3712d88bc7 68b0c9bc381af50d263ac2191cf5454fcee6786f45852d02d9295286e33c638b 4c0d47ee49e593280e816ec768bd16a7ebc65342a3430484f34da3de49015bc3 5b1380ab2992e45047b9b3be333e57eee693e0a01c76ac98730c11475d9d0c46 169099a7b473b0a9304655069cfeafd6970981724c1dcd04d7c1f66398511388 d36e08923ab12310ac47c11eec6e239ad5fd55fb3e98b41ec613682f9c44378b af999081e36529289f15fbe9f301aafb627e1a6dbd232546f78a47ec43ba6648 73422f152ff3bd17bbd63d5c92d6b2b66995ec64e58e76eea051accf9fdc339f 8c910449bb7df823c2efdd30d3872443e49ca120f1866446059625fdf080061a 658b106d149bac894e9345dbc910ba375c0b21ad6242c71eca0e76e3148c8449 2c3d51b874eac055b5af829482bf04b072c8776aaebcf493fd81a3216bae8dfb 140a92dcde3da0c68ab50bd3f5b0ba5e81288c12241bca9fbac4477c0d4e79b6 684101b3adfe8a64478bf7532f9a504142d121a6ad2c6861c4c85c4c59098513 fb2b7449cf3313e5ac1e24333a472e71027f7c0e400dac994884528fb61f753f 5382dc4a4ae7617b18b0da3f776d42abd8aa9c5a69268d55c385cb665a4cdb14 fc8e2bf6119fa008003f12108ec7ab63cf4a9ca9c2dc7b2c416269a8ebca2cd4 2845e0ac960cc22d22b2406b9060219c28c72360ade312ea13e8cabd446bede4 ed271db1db4c35f335c763913cd1c3f8d37690b2912572cd96423fd447885b5e e91266e7d53cdca9de50c0bf847f77382f072e1076ec61ee9a4308f6c822dfdd 9e42851d1bcb10d4aeda9ab39c0242565efd8beeda746839c176cbb9ba60dd7e 76d723082f96101312cccf5a99fe6a123f310c9483af056e75aa4a8552e83743 861dd3db66a8d15476fcd02c6c20424e2bc1537ee2a99af194617a7bad01b8de 123bbedef20019a367c7eb498c5ac0288593ce580a5ebae5e9f8593fe2d337b7 34636385378513669cacedbb932ceadcd2129f48915ebc2f82ad6cf703403f96 0c785fee26dac9ac5a185e772b51a0f70c432a2244db32e3835b9f1890369c75 149cf27a86aed3d139540f1867572868074587436147a3389808daec086d0321 ddfc21acd87463b68e8b2457c4a8a66e009f7a81f01da5623138c4c4a9524015 9331de97c46cdae111de844aa11fe0d24cbe4af03e1e270b5d6fcc68442bb87f a4f2cc94624eb6c8fe7d3996aa77d7d4b240c632c4dadb97b1d2da8134326733 f873301776db777d97f1a01e8bc274c1c5c34c7ac04076a80371bd1180adab0c 98f1b563d8b5d3ed1855c822c42eb6683357c3cc763d4acb1a74b6007b90bc7f 75a5d8e7b46f3bbde0fdde35ff590631d6efa95700beaf7f7035e3a34fee0ac1 85df90dea718acb73214862c39c259ae0acf8456779eaf95a1417500a4418138 a4a8dccf07e353badb78c3d70f74a836b8accdcfc7933f25cab3dffa780e3c1c 6c1d76592b02a6b0c7d3980ff9fe9d53afa4fdeecc773b975a609829495c64a8 1674111e0138ad736e822d113c5aa3743fba938a55903e2858602d4d47ca0a2b 789d0056856df2e77f8289d13f312ecb48395b20897c603f547c3b837cd22565 11c614cd07f05106739dcbbe5c0b2f08f0ad5b1da30e20065d45236ad246c746 2ff02d32fed4926e1809e8254a9389f22c622cece744f42d209deb9285595a63 f41f21d195c877090cdd03b4e50f33af39252993fd7f6dff5f269564a0bee86a 430efaac5bf5cd42b1015565bd72e44229c7ae939365fc1d5edbb52b3e7cb545 0edd0096fc81b2f53163a00a6c31eef9b7e57e7d60f20a40553dafc9afe97392 b5e31710bb0058cc4cd2f46cf0a41002d0d1f774d2fb8ed0d3a91437b6b7f08a 6e94879f15a91839b51131eaf000810349a3315b63e00f8c2b599b6a3b36b706 ab4f8a7e9b975076d3464b47163399e0b81365708af5fce31421ed3b31251ebf ab9b7eea60cabe2b2c064544c14c130ca1bf8ebf2782ec27fd5908e9aac72ab8 dd5bde69efc8b25905f2e5d1feed5def9cfdb780a925c0a4a2177f3f8c1b6947 23b6849463b9b5ad59e9328739e069e399fd9831f50ed68ba78abefef71a7e4a ca011ec31cb62945b0e1d292e059c160cc5196b887b68d5dd3f6baf642dd6a3d 6bbb987b2b533ccb2f6be65bef7eb9d50708c9b238706bee020d6e0a3b092977 e460743409b5012a80e9eee1f0dfce60aaf1366abe86648e858549a1793b2023 dfd3bd2fbf16ea68b45a4903eceb3b1540a1de4d1e65f268f44b6ff02011b934 2ccd4291aefb9e28ecf4a733f195e8750eb54769565789701201c312be1f82d7 734d38cc2bab7bbdf4dc605348d8569e947293f811803b0cd11e07ed15407f9b 98d61ab063209d713046f18cb412dd41cc0129522c9f6761b0484adf549f2138 67f3f0bdd86d02fd584827872a51a1dc636f52cb4fb141e86d4b96167514bddb 086e0cdcaa3840fa1c70cf20899349bf12d8aef114ecc59451140f8a3861b772 81d325feaf01e3745eaf3225c8e51ec33e9c8b43164169b1a10b74e966c17c0d 156 a16263d459cb9fc5a5ba1c3f771c9f887e364b2d15e5d460bcdcb57bdbc8b30b7d32ee6f19d75dac8608e9c18567007862fe13a1376f306136e0a82354d7bb02a01a8d2255149e6de98e90ab3db43afa5eb64644f70c5204c27a066c15bd4809f2977337b5908177d2da3861db29d7c7d8afc6892ca9957b29a189c8ff6cea078917039ef19aa9aef4bd5afa4c462fd3f2f44c1dc4b6009867259b61c6eff80163d876d1c2a4406d83bab0fda4a6d1685a4eb6fe185eb15cab654c5b32c6370c8229070b7cb528a5e7d5e69572d376baad2163f9d19d08c5552f7897b2f85802382928f03988b8f34e43b9d47d695872b3f6522ff27c9a68ca507257f3756c06bcf4ec50a40c79d0a15c973be4c197da89fb72a4cce942fa57eb517c33ce120199b0fa3a7ed9ebe7e55fb7b20f65ed1d357cd837ece71c587ba89bf862aba30e16b004a0ce65d06ad7d4941d5546b00de2135433d1392397035336627aea4002ff7752b457b4202dc73c4603d2ebaa7164d3c140cdc2a65ed8cdb97543d9a10df2eb1dde44dd0c91db6ed27e71cfe30c6533bbfe0def81a0fd854871e8313e0d77067507be6bbc75e97b616ae568b44faa7dc5ece426da412336b34ce8042d049ac06e7d934cf9401d10edc29df614dc01081d719ea28cc79078290366604f0d99ef213d7eb8266c8fa24e4a393f5fb4a6aff00446a24809ecf3fd448b66b50ef06493484fd822eccb715b1846a2875ad18b51ab8e326ad2fc93ffa24ba080082a90f4a0b4c0af4dd40162c0b890931924b41418437433446bbc5c3e1d3d7707ce09679004ce98bc2c43b7c464f248a7645d5048c62634aa54bcfa193ec6420ce869d7e9613a459c93fd7641cde7e72760a13a8fd02aba9dde33f38b5118260b152501f7af3e0d56635f304447204fd4b6c26dccdf4a9f24c63cd53c48e3b201b75835eb2db9033ea11f871e0e161a1e30492a62f3eb7a82c8c121831dd38e05eb354cae51a82957662bb379e4f64fbdddf3cbde4ef12f78110a2c5ae9d9d606413d1b41e36b884279e4c5ddc3bfe9cdea73c7f42f6f550bad3a09aa91c4210e3413c4ac094c8ac9afef6b468ffbcdd9780b8bd5662235fc146222bb8d3f6b0877f987adcfe9fed34dd6f83754eb1918965f353f260424780f7b99b960b1e9054a514bdcbc51fcd96c30a3758eb36726b4f03556166fa0407f571e2bd9a5690f5d30d97f2659918ca6f0833a57ba69f97523faf2fb9970c0a86e3577b090710fa70ec8bf83577ab31306d08eef1472dbc63c6b6e7c7686c3c09895292ce60e0ba631c2ba62745ccc576a9b78d10ea99c1c98291dde7313848a7af94e7b56260424e0580e81e121c9d607d96efd7d7b71a9814ab13cce2d82c2575563d01bb502cd1ff804bb109d6e8656265e334c07b101d65e853307478ab706825b210af7058c2915b290bdcb275ea3b64a80e36cad7dff6c9ac32a4155175ffe8670f5a20b33f4ede23397ba01ff0a878fee93e2d5053499f6ab6ad6fcbecd096c193395070cc161d27454221b968df1d610c503c311e21d2fe42dc1870a82bbb72540da0a7abb497d9038807b250a33b2dbd72ba7fd29bf3e301dbe556dd9d3227cfff70893d2ea2c326b4e33375a70bda07a49a43e2c2a6fd07c9070791f40257ec9c6037f1d66593213614c925b31a095b0cee5cc845b59540e2027b5e093c832172202794ac7433fa1fb142f10d5979e0f45b52acb38f03b6109e1b6447fa86ead550c7c24ad96f4571a7f0ffa253604e78a43f85a1b401aa59b8482c5c14b114ef6075147d57d14feb9e2daab74934fb1da4ae05875d346531d6c52fafadfdce8e4014c24e5a07f3c3114d8910fe9317b24dd32a2b128511f3e2a8299511e1de581014a64e7e8bb4025839c7dd32cb19e267cd32ae2628ef20686e5f9eb78772c2700d0c39ee8d4d8957285f9bf4c28ac6cc7e3c387bdde84d022d8c6b485addebc0c1683732097fa0b2c8259a1ed98f932f4359f5795af3fd39250580a9f6f9a9f0f968767eaf031cb63bcc56335d515cd08b49ee8e1523ea143f7c245853703e80a723cdb1cac44cb4bdba56212042fd5ba77f2ed5a703bf9a9c6ae14961ac59f08b3076d7ce9370978cb40bb9f3f8f701a49c0ad9e8d27937572f003e3af3a5d0d3bf9201e2dc6172c9e4cc2eb6fe267716386f6d94d7802d2abae49c2a819670d412aa017abe97cb209625f197dae2b264afa234605e4fade8682cf9d78e67c0bf43b88b960f0c3150a81bcb1ce6b66f63577b6062cf9514ade291742317a700a1da07fc71fa9e142da0370a71e6284108b1ed117a33067cf87d99cfb2d4e5b028abc7868dd7de8653814312c1d3c73875f6eca711c5a3ff1e5504126806d6d0bf2d4064438838dfc65286b4c52377cd527c8b2a31d6088083c28f9f932b26002956dafa3bdb0b6a82f5a39f9030d4f161635e8ddc3aa1ca94c7c53ae5d5af806282ab02bb5068d2e80a819961383deb114bff14c7ad98b77beb839975b0fd20b34b75afe65ce8869ba88569b752060a6f1e38064d118cdc8ce8be501e7271d03459bf0f1efeddc534bb72cbfa0a41b594abdbb66ca76539e0147c23f6285f20050ff6be7e7944fc1b35bb5fffd3c3a2ccb773cdd7de1670f66d8be3bb3567c09358e7327cb7a69dd35657195d6c915a3eb3b7656dd4f8e2f5e2d8cfa30eddd059582885a9efb7209dfc5642d730f143b5f6feca4e3f5cc77553f6e5b46687a00477277e74d04ae0f478ee5027d63f85ed04491b142b24751656d64d3420748023f4ee9c3184fe767e644d51d013178c7265c8d2bbce03eddb1d02b451d2b130cc5e4350647679e1673fa093fd1a292b4bf9b18609e4ecca1c497d8ba9117d309e9b5fef34b4b514b107f32a146b1049d585cb642fa5bcda07eeee748573a550e5bc4e7bcad958ab167edcc341976c893be5fa4c570a25bb5bde4d2d1cde1cb0071bf313349eb39a85d5eaab995086b06fda7f4f5db6115a231a9865f4e3eee01e08a2709806ca016fc9efb5c434cd8624b024c8f55a1c29f2395188044018b02e34e62ec8c2aded6219cb289e3012ef396cc7d8096c92f937375381fed0c730ac5e13ed0794d54347bf27cc803d26b0b436b35f929d9acf6e4df72d2fd9f78072bc79b1686bbbbc2111fca40ba9c24895069a4dacf2c467e68fc1c087c45bc0cf3fc82ffa8fa4230f9fe5f8693f7c51c5f1bd22dbe45bfa56c2961f41ed7ca07f16482cc2487d436d0c1697bd18927f63e56f3f458a2e357dc4ad8119312490fb7433a82bd282c16bfdf9b518d7844e3c32af6dcda1226c741e84582a7c5d402a0562e188ea1fe8804e97e9262f069ea220dab019ee6dbbde992e6dccfdaa60789496ec7ca525caccf96289aeaeccb33944429a3443a637aa3b39c34c1ff6d0c9b51c45a5d6140111b70fd4d1c7c40cfe1ffab26147a0efc989415bbdf80a307a5d2b253e55eb446a10f287473e2cae26b21632481809b1aad2adc773d89c70639e152ac709897e29913e0d60b81c3e50cb19d936dcdee93ce1bffb67a8a060a3a1f9d1e454aa3d1805e2d5968d3649639926c895c3dce199ebb89fe1bd4770c4085268a84b4eedffa7162b84b095a8a4f08774ffe0bd5edb7dc2df0c3c44608ff53e4d23f1033967ffda0d018b82049caa9de74f75866fd86aa63abde4e7b01a340c4c2fdc3cee177edf854de928542278c50c372b42fd32c5a6bf891bc0401c12b3433584290676c4d25b80d6bd27c4a7d161d6a3ef2e0741d63979acdba03b038d248a929132e21f0fca03439e1b512b9c4c97eaa53a29626170d5132e2022c9efc0bc7d6959755cc6411d82528aa38e00e460be8be9c6ff0bb57d33d3a01f7999b2f63d5421e8d49e372cd540481733fe63b7ecb8a3613746fb6a6dd5005847f37155633ed2847aa50bebbb091df9e902350ebb08b788d8392c846ee74049f78e675ee31bb9c8ea6ea60dbecd36ca5ce7458a3bca8b6ae5e48444343730eae630650c0ea897dc441415801f07574c0b4155ad141da47ab0e7602d7325e05f3f162a545d3739166c40d8acd436e8424fedcf0e130587dbcb51b3b806dfa0563094444c2ea3bd0088ac4ea75b938f09280ad02fca5016b1c75ce19acf83604be8900c5467c3e9a9d9db6c6ebd0e7110809c84c39ec186d813f232353de110dd64d2fb5a342abc7ca87572eec1d1039af55f2d86ef0999b706b1c1903ee8b0fcd4a5f571efad26a4c4e9f3a91f18b45325ea5cc200ce7a9c78a83d168954e027e733b855f789f39e8659280d907ee6cc118b3bffec532c841b1da793163640d373e54ceefe55b09833b975b5a8ea0e1747c6f0c79544b15871891554b22bb0a6057bd214421b901624c04f12e2e9ede4707c799c1bb787fd68a520aababc30dd5dfe441ea5d6754003d0fdb636dc302eca3b466978dbc4c4cd1b7a23d8f9f075f99679b5a5f3021b61b46f3745ce66db2a916b528c24229c251d014ef3ef60c7f537696f5c6e0405115ee3eae433fc9242ff5750a9c208767f9f4d58616ab070fbdf3c53072db3aed8274e9f5319e6bcbe633058c69b4b836e642938251ef0ab0a3514877496960e99a75acf845c2fedcb29edbba60f7a94e7441ffad020202aeee6507a7169d1d5bc187acdb8ceeafcc2142cbe1575d6858262e610e20780e662d6bcf9fcba7afff58f86f373f32801e8be69356e8bd7d175384481db43a067e0dfa9a3059f478ed2d5c0747d7ffa130960a667fefd1af3a9a07aa11481903948d850619b10839151f5a178d704ee84993e1ef350b70000481e5e9cd6d5603f2d55e2b3d1c0de5a879ecce15b0a98c9497a864fd4dc4520cdec736c6d83a09cfdaea2aae116bac1d815f8d5fad54d0d7cab0d2ec9d7149b43bf0385e6ba808c887d4a44f0dcb7d9e39458165e21011cb1c9f74f449d80a9ab34a268e285e021dcc45921c9404351b73a44e97aea1e3d2852fcc787aa959614d2119bcb8d908da03d90c615516a83ded9bea7b894854723ec9a38c4f6f2709c78596e982a305e3e262ccdffc374889c359862049b15731e00d963761c43eca5a68e7f931100a6b174534f5f2bc8ab8734a00c8c25f851d720a6b0048983da99425f43b84950c7e8ec25bccc59c2cbf56293b29a37f24e51a79d24120d0437893384dd7e1310295fab57e1fbc60e1a08adddefeaa7f45ad341008960ed711db7494e00c444a09642b9f1fd1353f84f782a6e8953e036aa7ec9a0c867885b2ceaed90516f55e07ef21dc777d2e9c9558dee7ab71084de7340af774848eece1f3b7c5f58897940ea7b479dc695b44e51e1c39d12f2928b9aff1e574a15260cfe8350315061d8804640b1c9e57986287b13c37ea88fa02369d1b870466207b44e5753aabb977ab0a4205628bc86492bdbadfe2da4eb94d38da383c2fca9e58780cf66ae67fe0540d168b000f1e91cd2015f9f78c8532baa59e62da2b75628e292bb18003e4f367018f9ca4c7fa3088a3145a9c7e433186267468ee5303d7f74684206a208833a50d809e9f0d9ed388351eb3ef0d04235ef195b61207a6bf219c8b90178dd28ad40229cc64aa06c35debd86e4f8ad75fec204421cdddea1d09245f2733bb91a0c902bddbf2bf2852213a17c06f22a9e5d28c16f1f240d3b74f9bacb1578e74d7f00df2f7eb4b0111e46bf06d5140df84bf0b162560ccce4caf24f7bfa7f30df46904867128ed1956365720c94fdb396e8d065f73e27bb95e9efe814edb3e38efef0dbc71aa99bce22aabfd9fce37eaf98f49adac76e59eebb8007f3482f49eec2703e4c99a37f2c2a2af3bead46c8fee0acbe809a68ed8986af2c18be60ef1bd5107c8fc9a28a52054832ca9e3ffb666fc51d4918dcc3ae2ae296915cd312ad322028c4d1b566bc22b47eab3f72a8c3345fca5e002e67019b312c6b1815a0876e7059616464aab7a7b4e8cf8611b18fa4337e98a3a4abd68caa4884b23540d0d9905d6e2dc05485ac27eb6b0dc2564ab8f1b2daa7b458c49ccdf7e6dba0d911be20da035144fb13d7546bc5876120a89af15f29fd07ef7aa3a621564547945fcd600b1eeb81010955afa2c28a95f262afecc7a6c462c8f46772cb1f4bccf98a7cc03dc10e41f0eb6ba3d08aab2c3e124ca859e21ecab7326edc131d696af77086f070d561f43d9e93974df09b14f8ae09b09141486ab7d665b7cb98be3b7592a180ebcc99edeb9a7e28e8db7aceba868fffa33ac8cdcb63d09aabd78432afda26409d2fec20b8fc44b2eede0f1688a7e6d82f2b2608c83e551a3619f1af2cf16940495dffd471eae478976995c4578ca02e0034a55e9240fcb2499cc6c13c8191e0a5e2d7f70bc4efba9703ae1b74bafae9d2520710e59f68d19e175fd712fe0db029e5bb52547651112f116f9af54a177d688c3ffb8d6fd841226353ecdae51b902575ab58cf75bb598628cafb147d192bab37a1fcff54d530f05ee9fb35a66090476e1b33761aaea9662b6cce09ad73effadbec8fcb712536f456333740bf1d905bf08b21325bf4123d18bbeae96d9acd4e86bbbb0d9466754c8d2c6a4af73f2084b8f8c34ba2382bbc7c8c2d042a85d6309842c6e24f8b688641620b0739b3b012612ab1e152d95c1ac94c7cbae447d1a4d512910283a7bcf467719fe0e83ed0edcf4339027d02d5d8bf7ceadd0567ca29d9198a7d5473f591b5b75867d99950552ea04711188e46fefcf18806ffd47e52ef15e0510abb269d0fbaa3a62a8d0021e8342396fe3998876c85a1c8c48a4885f7be38609980bb5db5481f6c47b9106836b95044d7270d788add03203b4737a27017532de34a75b783ea431babf8c09148ef641a25721fa5ac8419cb6767867326d1deb850228fb581cf708d9b5a30ddd6304700c4b5c19a215e964659dd51dba3d2d80a47521f3a0c7d17573548e0d7c5ae696de512db9222ce2713443a6b45209181efed64ae7aaad6fcd76f8e907b1f432c7e63e08d77dd0a17d79d33c7785003fdce41ea8aa2f519ba379c7c90829c600f706c9560a4a1ef984637c8166bbf3cf6fd6ea9dbc9852f2e714a7a603c97462cc5e3dcd17106cbacd2211d19279f25cf93dc434994da2f6024e640e0d215e241d3a767e21cc60841adc8d35bc498d75732f66c53bbedd805602c8a20d55aa21ff60c44aea59902a359525c76aa87bf370c7a72a3c44d70b13239eba085802771a96a9f2bab36c378183bddb6f594e3e64d6d895406e60bbc6a9ef91005a36d5b0174fc482a942bf08b0a2503c238b3c8d949525673e932815a2fb84026faa3ebb256fbf2bbbb316ebc2a34d5f9d60837b01d2b14930d1f680bd65e707ee2740309bbee058b3e37f97afda465325c6748bfa7c5fd70fc2974bf597aa0b75872153f50818b1fe9a51f8574423df10f04b4240f78b6a6cacba350e786a08ebf219565729de3690e6b558c28db7f63fd71eb32287028c4856a7854426e500ba6d68e0533171b226c20d178382714335f4a7680ce5073bfe20c5a9ebdd3d087f11ab74ae5aad90c4f40463c94baeb07e68e119f1f3bc87198f517bfdb3ce0f0b09fdc435f87e12ea0cb724df7337c684bc25d181e3fe8f46753ff69b39ca06d66570d3715615893bd802dce16400bdf3657d840b5af3124695d5b4482b7e0d04a0bbc426e0b877fcf16214702049bc6829a048dd294b1a549b60bec97eff098d8711a161dd3f361acfe466ead7d158a0adadeb435f37bc5ee308957931c10584b99b38d3e7a0cd1fcf0319d1f9b8d202e9f6b48ad0ea8ebc2b383aeb77010e603c83cb8daf66cfbba3f92bc4ca3a22e760e0b351ff87e6818887a4dcab9308278ab0e6b03c2c8644466f8756af3db27b51e8d4c8eeb87720602216ce9e4f0dbcf83cbf3800b40b1d5fa799f092c5c6e733b34aed2ef44192095f78a24c6609beea13b5c5e9ac399da69fc2f9efd80d81cd683ffb40c7376d476c5acbf6e206ab73f12c27687f464c867ebd11e26f64ed7f15ff5a4e9e95ae42b495eb7e970f6914e2a122c0bc5b01581094994029810ac3e1be5de80931604a381ea637a00e9f9ccbc1bfd313328323e8e689b0610f48e4ded3a2a229e5591f6ceba6020d06159ec04f6255568540d20e1c4a6e07f6146adf1d5f0c8110810ffcefa152ce00d7d8dd42cb9e4283b7ef014d9c0291dfac686c406bf75bf2cb2771fc65c1fb070f034317b287ea557a923c90f2d9976540f3883b43cc274389b55cf89e54ee072c35559b30ebe318f996e71317de3f911ae857da27f84e013aae439087b6db07e412f062b34a95cef4a0b2749721147b878e66c8d0d37e5a7b136b4a3effae06c5550a3a06fdf4f2a341a0aea974e394bb8e0489d8f29b9f0106f31861e2c20ce5aa8b55c36defe6678b6621e9b90b4f095760c271f2c3da6150e98527272f024bbc856b6671ddc0e661fe708e516397053652ef2cfde8c96549eca5fa24a400c592347e08f9b375034fc4f0f3cc9e533ca769024c222ef0f4cb11a256dd180e69e2f49cd09525f529554aa59cc5382aaa68933d886b4b9f9f4a3423f931b802aed7fa873d4facdb9e975ec9b70ccc13bd9608513d4862e6527d1e0b8163e20840bb08e88ceee25c5e65a494b06e14a9b8a3a921fb87c2ec354ef4763ec1c7085bea1b689798e8dd2809f20e29be3cc8af181ecc68c1353f01c5bebd7289060b95aeffb47aa892614c98b7e46a7ca0d15c352daba0c2440cef76c6a52278f8091f1c799ff172f4fe35ce2ea03c396b6cc576efe029c1dcbf66d88fefb676840b81ffa401c2f7e225ec135a75542debc86bc92385c2d028b8e6ebe11a3ff0930043ba76e56a7c83241b1ec7918f4c7e0026d3ecb1b91472f5786945d59889d50f39104b68e1f812b43e28981f8e231a3a5e900dba520aa04491d251f7838705048f5cc9f90aa88daf908aa1348e7b1faf16b7c3af1d7a99dc628d1256ce98de052357f8a9694a0fb9a520a6e0ccc8dcf696397a7a68ebd2faa873da7d1a5d570accb370f559f8dd4505fb6d03fe4d6b795cf9d31080a9564bfa098af7813f320908f4ec36fd423712f645df0735f78744b786e379d3cbcbe2761de75976f39f04a36dcd7a40dcdfc7a8b3f46ca8f1a0134e64427bd6b5d46381da2ad0afe84b09ce392c13cf4a7574dcb9c068cb0ab43eb359091279e951e94abcc2db31be910d0557b55ac8d0512501bde8057b81123ca26d2ebe398b605dc3ec4ef5df63b7026f9867d352e140773fe8cc491c48b2ed08afc5fbd52917c742e854e8ca94100083976f42de00fdbac1457b8c1f85c77274776a1587755383fc8bce2a4de56a0d51513a59b7bfdf5dc9dbc3b389f3671687f1d4e27dd6fd951118447f88bee901911dd4085a3a151598738f6df3f244d871d4943f309aa033920caede024fe80962287fad76013da5afdee2d1a484ac2711a5f3d2264e16e76c79ddb82f68a50e18f9f118c29211395cbec8a56dfad2ab6c236f8b47379ebd3d3fbc74a4035403638d9fcce69d5e628a9f376aa7fdd3ff167e127df1b8ae6d79f46bc18d8e6e0d745e3d0cab5c3c68c4b7e05a504a9aeb8d76723afb644510856412c589336708844f66747904f7e331a380dbd11aecbd25d9f772f01f8d20219d07342b2de80fa85d6ad496f6fd0919d54a01e98a487d4af76827babcf0cf049fab42a82cfb082e9202f8a606532e22d0ef5dabbe734ee930cdaa92b7a6cee218b9ee7347420cb66cf3b95810eba9392c1b34c543afda1db2d29e07493b8eb48bb3f3713a4604d19a7fa2fd127419327888b5f045c0e17b18c0c48c0b33fbdf3c9dc15cec240ad47bda4032e139c2d64fe54c8df648e6c703302e8cd9a30ff53ae771079c6d07bd44caa1f14e7f548d18a914e7bccdac055ba6e9ea84786641e743b7e2e1d50f1941ef714ce9985f796904926467a1838939147b9202624e5be5082fac7459078b95dfcf28f90bd96410ca29f2512ffe30f3fe5a87f957f088948988609f870e36d199338cd18fef7eba8d5736417cede41b67efbe8e0cf0cf3ba2af5b7d1603ceaf9d884cf0aa666ebbd63eae33775e78a0f43b14969a9682af483247e3fe0c7f9f3804cdc32fb38190883fd3e429130afe57c429b80d6f0f3bda8ec18f680b997529a6e3b9a5f7a9aa6b11d1834656300061a600f9b5bab5f186dbcf4e39043bfcd3e9104ed1f4dca438063c28c5951b96e78aa496830bad6dd42fcfba6f06e568071c1ee14d3eea8fdeb15947d7f1ee1b0e86dd52c43b00dad8682312a40b88d2e052aa921e3608111e235e166fea05588fbc0f29d2d2e16e2cc5e1b6030d3cb01044eb67570bb8bc04c51a3bda5cf8fe1c4ffb48ba3b1ad2c15bbc863608c83befb1e7be2fc268e92b565c5f20fc4f0d34f48685f9c175132074c627da0b4afca6d90ec1126837c6a55004eec0cc65b55ae316789ff06bad7bb03173ae0690db563b457f113fef4584dac3c11d887623d0df8e117b66f4d407935795af00b52680bfafb9c789543d51dd34f5751673f394cff752b4c5d293125688b4cd0efe5c2537222f2ef3060f1d4962e55587d543b6b78fe77b4b921f89302cf72404ab1ea9d18b3fac81a3d8936cb8ac3a413bdfa44eefec634fdd8c809cefb5c40b5507e7503b0c406e6b2e91832c509d50c356d297a7178ca2a5a6838ca7aff5084f3d44b15986143a8e52095e9c78268ec72f9277cfee4df4176833a79f9436027e49100c0c1111c60763d87bbc95306a924dcdef3afd46e094048b818753c8005438cb7e29fb9a8e30f53418417eab185db2b510125889d85de249b36980fe0c906e879a37171b5de871d85fe0a7b4f798dab27879b65ce8b5d960585f3f6509cb42d4bced4d03b90221cea8c672cf438a1064248a450c38d6d55edd94c86607d0863835088063579baa629fd6718d2f6209b872ad85836152bb4439d190ac0ffff90f6f52bed77ea0374002f2882402261c4fcf9d379eabbb904b9e5aea1706675560e844ff8b691ec546d021cc49da04a44df231b3b0ba0d0c1b37fd50bb07661415b05eaaae48a8bf3c9d9ff3b52bd3663e242c86bdfba6df41b0b90ab30c345ce4f19e3819707855dc371bec54e903b16a77a404bb3d547c109a20ae0607b990464af8cb4ddf8179f5ed475ced799c9dda56fa053f761ae213d69599b0018704c8e13cf7ecea102986d7ef3f5b9ce0e5fad5d4514f4e6adbcc2c1a8f6e005eef1d6cc9a25c142c612aad90634a8ebc9b96eef4ddb9ed80b3d2fb19da6d02d6abd831ed6e7d3dd80c740caa3707d4a899f97f72b411675b658ccdcde3d70ae5f4746de7fe17167505f30cd77b18c96d16a2b747bb8d417980392c4e43240ffc1884f82bd527355a8329744a71d416bdd663fe4acb3de23cd9aef63b3add0e97028d4b6b343f61d3e7e112669af4ae2c12ceff995f4fbc0a8ef2d8dd967b0b6e581d169ca97729a9d0330a3842e4bd7895ec882c4b5e6438c6d6556758380f29d7c70d4257bf352c7eab60990d4ec04edc64bc403014bd329e9d061865d40baf3d87cfa306d2f164c1da124c018fefd04741c0bdb60682f60dade2385c770965777c40a1e92cc0e8bb5abee655abf708c0e6ffbf7e07cb68265e059cc488053ea02c5c2d505bef9beaaf06c2681183ca7a4f0d99adda5365e5b7132995f7013e9e6925c547de1a31147307083c58f8c3657c6b07d238e61a34e472d3caab022b5d07ce468dc433b1fe38c447ae6890cb3c4efdf943a390671be44b4712b00d5fa927f0987a441b9c739ed2137f0503e6b8ffe47e328fdde893ca2985457705a86923187028b4e3d39aaa38e2eea3601d2eb55e8f534eab5dfc707c03fe6a08cc0638807ff6660dadccb15c3a34eea613e03ca745c68d63c02dbfd253da5e03166159c0eefbc36e3890c65debd77b8617a2d718be4a9a0be97fa1860d6997010f525ca0a095363beefde5cc2b8e6e33b66b3a0f0b272d62f8da8e9a891dc40cd3718994f63739c02fd47d39c457987b91ea3a46213d281fcce809cd13f24808d8277859ad4382da3bd5067fa847e19a0c8194e117c2b0d9fe1713332a2f7505d7e993ab04668936c3a14920257b3c99e8a168194f534802311ed95ad185890e07f6fb450fac842f35e10922094e5fac0c793b91ddb77dd9ff3b73d3b7af2609ba4a9054415d64fb37ce73bbdc3fcaa9748db7e6d4ede907d6f49ea4a8fcd50190fac0f858a2bbc9ed4b80f1a4e53dca6eafad61e2d1daed311e627c9d9e6808c52051e248150cffab2c0928277b545f905553f81d21ac5df9411097638a940bdaa3da1a6c4ada362e676b8d0d278e2624cfe3a61afe9f45112fb470e8fe7f0cab63492a0b36867b59ff5e05fa7596ca0b9d568501f653576a4603b871ea7403940552ff3a1cf24ede2c4fb63d109f7e84725312e0ca59cd812db1efef6dec07a5bcd21c3f74542350102586dcee394d38aeb657ecc1f58f2bddeb067f796e05bb4320e52d67d10a854822c8ec4ca7ac4233f17215b61e01e997169547938705465017535f54c1158932f685d4b1ed5c61ce4580705c0b6405dc1c3c87c2ac056201394258574fdaf796452c081ffa8fb3f85b68a21b0aaddb53aeadc764c70e63c5357e729173f8e1bf9dfecb7cc1e6efa85290a2d2c9f1a2d2e0d02018180f14fed9384b3d83197a3e661fc5f8d2932f75c2cd7e1aed049ef0f96044393c0eea32250ee9aaabd5b21523411060dcd4d361f00c0aa895f0d14eaaa8d494fc0548a60bd8e75b18f10be28b66f44690c934859520a5b850cee7ce45f72c281303cf5b47b5babeec770652792a3d011dc09b233cc0c3ee7e4abdc2d200d68add078c57516ece2fa372b7e4326a176771a6899e9d435b0df7efcb041a939fcbd507e632aae3816746fe56b27ad383a7566235884c7179fa3a1ba4e6c932bd5e080f914d5f7cc587a283971b3e99acb9e001da9337e0a386aee95ccd32d86caf800d393ce7881002d2cb1040f3b41c73770a0ac0d65eaf5a9df599373a08806ea905e0c840977bc76e1e2d5e13cef5e9e82448f4b34f60f4fb57ce8b9dcf7ee3640f07c375f1f1581804ee80dc6ae8fc20355b2090b464a36af77cab52e98bb07d0dff57c9383e848853a814c60e6bbe5f366b7741de1d79f4010d1b51ac61ec1507e68a2f0ce7e8ebb5ab30c2d0f41d7a3ba426d4249b0e19d14cec08951638d8019a8f75e2c2a1fdc8c912815fc043d9b31959d739d71aba09af9254c00ddc120720cebba9edbbfda84c14e493cc2c887f8f7686406041451622e6c6e3f21d1906cc46080bce8cc05d16602ef6f585ec8504f17be5a33be345eb47fef024a1d50e4c4ad67119cc2412dc504de07c153178a4c85ff155be2d47f2d553e6ffc0250507695406b3feeb513bb45077f229fbc36e6ab3f9a644b0a500ef52ad29b86705af22431ed16313f9e97f5eec4c73166812ef8c0a33e6332432569aa5cf73e8089880c1171c18d2ca6e29c63cf730d87c47725577f3a4e9a99c6a17c58d13e60b2dfe1d521b35ea1f6d678510b35906e1dd71ab47087aff4a98fa8b3266a2f20dcfc2f564cec124d2f1f3cdf609b180a0c5b99389136c73f27c056b53ec762d0245e26ca3c2a988688a8bc9e1f87488dd0ee25839c8809456f52ba54269b8220f86acb816b1771c4eed565c99a3101f6d9bd61db6a4e24c19a9b6958d7164b2026e3172a4bb4f64136c186aeeda3e42a75fe1b7cb2bb2efec5c3c84670bc56300d44ba424f5521985da79fcb35c90d9555684607af47500b14ac288bbcfda4a0ed772ada082e1ebcf9d921fd8aa704755b45d7acbe1368cbf0d16f44a2f83f10b44099eeab7f5cfec2e548136fe0a95fdedabf2f04866f03c47e0d0a7b0118c081956819be483eb72351956a4711251033ea7e873862effe5c9499b63c6990003a37398f20dd6dcbaa2267e4bd7b8940235f7f11d4b4d69065699087736a64f0e662e06c141f7a833876a91e67bccc27ded50f0992d3cc220e77a214ecd78700123523066cbc1036f27df95a1b0a514dc689fe366ae7770cc367e52f8a1c87802b2d82f102c06c6a27c62aa05f6b8813a6757eb61530df1d31c0d81b6832d8d0bbb15bd05bdae5b076620108b55ce226566c29aa2f78c2d8ef4be3f598aabae0b419b1e6dcd67d33f5669bbe6806f5268c5f4bca38c2cd3f880b55158d1e0040aad724c53f3c1372c5e2e76ace5095e214a03753c8142744013da4ee633f67300b942590fd6d3cefd2ef4c9969fef3f7c73bf6007c3dadc4a33253e278e28980fab26be747998735e8ef3dc2b914f6058f4ae4158a3d3527a81a4a5ad8a2364011b9d45fe2d7d6bae7fe6241c7bee219d393050ef73294c3b6b4eaba248ff70051590a1319597f7c5518200dd97ecfe3960febd98e8505f23355e6df9d469960d6d898c2c31d41dca0eefdf02ed11b370fd003928bb19e937305d00453984ba0d8caedef7ca3b6577d10441aaf4d0740edcce44f9bb7b5f0769b5b51ff1217f0b2ff5bb09cbbe0d1690e8cf70979155cd84d77c2274dc7f8cf0c296af3349b302cf3199814f79944523988a42235281a66a9850224cdb049900b9b3a9dd62680abd3fcb20a5f6f89d2a9b5e4157e086a1b4f0d5b7e4ee599d3075b8916d5ddc0f79ef1fc8ff67a3b257861de6d9a306beb1af563107f1540ee2b069f960061802cb411ebca70da445d421086a98d797e8a446d462c216f79cfb3a979e6cb0f403b0ea86679812e08d5f64db0104eaefc5f36d1199543c52d75c1e59b50c9e1b0ea6c8c670fc2d7e427351bffbe4376f9f88901203bd8b386a3e0e0140aa85810fc00aa5cf9a14e3610e4b3a9a81d4b7baab69ffb47f94387da5f2efea469e4f05ff972efb43303b56243fac2564d3782b679782af3f9d23b2a30fa17b349f0b0d1810c942ef3f3c7674e89601f12cf1c81e939db1e0918b3406257b3ffc164c0f108cf50c59a33d552fc5d11f30a8dea5e7be66e50ce8ed4a97db5aca6a9d630a6eacd36bf97538b473a87cd286a48560ba3df99711eeffb71ee53f3541012e0b7951d7aab2aa0832907c0583f826d44a02447757b4e0213882d77b1abd8c400e2095c214b39b384f75a6a73d7473ae91e74b38852c9a5210bf0eedc90ed32c08511453c2c67161f1bded8b65fe14ccae2b4ae2c25b6ba64c9f455ff301c98e0b24b9881dfb952ab2b85602776169a015992377f160181bfe46cd3b48f65be903604cdd7f03061ccf781ed98eba685e86d7703e81cf1216d2bddcc27a9e0265090be0297560a297d03adcfecf7c296261983066c789aa5c14e9342d9ce24b73073b3596d1aec5623a7814b63a6766941f66ac706001de2c91b10ce87962e6c10a9162f2818fd3a147da03b70d0b33e5889f73643f8ed5d8352f6d410583d7880b1cc4a805a1f367668c8914d368456e292a93a380aa80651976930d3c76547c0621a4f698b4dead900445c98226d579dc1552f9577d5acb20c89dc763ce97a209257fe1011bca9c185bb04a0a9da135aec7ff9a23f586fc3092cd6581bdbe220c776ed4d2a8a1068856df596eaf870435828edf48fd1df34b6a607583ccf594033009cff299795d2389e65cdaf0ec05dcbd503ba9997c1059e2147894398f430301fe5e754496d82c1f1141a7a98a047e4a1c085ba7fb069055d662e002c7650f3b0cc7d756d78cecc0243d276b222b418d46033c981a381a4d35bf333accfa0b329bfeee45bda0f15671da2e64a15192e8a9ea31b848266d699c1ea40e8d19065ca7b23b7f9d35080b1dde223d95a5f30323044cce11a96cd9cd8528cf8c1c0e722fc4cd7f7ee88bf1c4cadfe3f42fbde9a5b2ca49d9a2caaaea548fcd1fbf02ab66589356e86f53f6a410f0cbfe041a56a3bdcc7f0f8ae2116eeef37290440a186b10d28c1acad1003b379a655560ab01f41acd2b9f12b7c68f10c0279f5204e33b3b0462d9a7d096fcf17f90eec48762e2b8f4bcb9022f1e47c7fa6b5373017406ff82bcb98e2e0abdc188df21a8f5c064b7d7669dc7ec466731b8211a1c077ae29c1600aad3c3801051d93ba9bff5f37dc1ce8d691d733d6b4d3fca1bfa0bfa3f3c751d4dc1c470666f55e8a25d5366efe2d8c6232795f85e8c4e0bd4e4099422b64d813d46f2927065ed5fe05f916f47be19acc373feddb0123c8a05220239e517f8f070ec312685674bc90311dd5afa718446ae16e053a1afeb2449bf0fa4a94dca3415a654fdda7671dc774ef638000588af01561942b05d3ccfd61704dc5055c27d9bad21db376821e598911c73412479ba21e897ce672f74dd1e7805e07993658bbc699d575071c54bdbe291e0213adf35bc04876510ab0ce873020d198045e346929648738adfccd1135838a5a1621f9f355c7b53ea7e815478c30e675760e85275877d1b18e39468eb039d1902e12f22ef2b6c34f6616ad31f00009aa0f2a6593fa577337ba257dfbbdd578ae1dc88b26026dfa1c9c7b327387c002fc296c6bbfd740c883a7b93572be85e5e130912a3efa5088c11a1eb3bd008081c87c20f3032ec1ec7f9542f650b059db5e53ec8f7bce79419665d79255df30a0c471aa6eb5e9acfccaf29a5aedd0b997741adb50d7ba2b82a6b96b693d1d00e3fd83fb16aba5398334ac101c56ea37ca42eb8a50fb1bf193b9503ec030f5c0b3563434c1473a9ed8bf0e9b0b6ac5c2ae251a511207fb9a153e39b6563cdcb01fc58aea75c78379b388b931e8d48e287c89988f2f404a010e3f1a05109dfcf088c75bade5c6dccd74846f389c9ced08656d37bd72772b61528251088a77a8c03ac60327ecb58208ae622ac2161b54d411f052701844bfd818901771659420e0b678eae8de139c182bde3b5d0a860b58b19501143b7d86e998c4afe013aa1140d9eb9c6c50743da686a72ad3d660be2d250874c3d4df077871694f9c8d262e704f2815784dd9d18730b34e781d9c9064a46b76290a66d5807765c18208eaade0c4281bc3d3a46785424f30b9f8db77df811576c6c57a456df3aa997947aabac0c9e4a1b74a033a8030c12d5ad5a322a10224fdf1636fd966e6fb1641c4668db0bb650237ac1236e42ad06763af29eb93f7598e2b30f476006876d8d36371c24076be79594ac94a2d78cb4bde2bae735e7e8e3e3062e7b20d1fb791f1a560a870ceda6ed84ed7dd8bec813becbfca631be3364766ed49ac144ad74649e043279054627dfa8f5c47f41eda9ff14e242ef8bb3c0ae7cad354261fe106c434229990ccb89ba7d99adc20a14c503ae49ce5644a07d9dccd066e9bf034a4df755ddff0cc84fe104ac67e0a9e2b446fc3eff80e6ceffc917a51c5bd7deb2ecdec9ca0d04cd51cd3952baf89ca71e9090f06896226e4b9eb27cf3d3108466db0b9848fe06a8b73a11055069a8a0507fff8e0bda43e14f98364ef89dfec2b2e60d48074407949e493776fd478e0e718973b75abc1cb43def9447d1552bb76abfc80247690a455763cc7001faa65f002e1afb18e3d594fb64aa3e94e5f03e54ad17c83b210f80fe0bd73b2e4ddbba70dd2b98fbb3fd74c8a2951487cc4cfa3d2f2dd036e7049adb530148d39f87857e7c1de27c47e95a00254689be705ea93ebf70622dc10b6d97df1233af52571e9b45ba178640c4cb37992bc1ef7804ae453ec0e576160ffaca3b809d74e59651a368c088abfbeb7a69be9178832acdd935b3c0f2171d07e35a6fb4decbcc2820b0cdcb980ea7448a28e2592f0f07eae160ee2569d07405b33e19d105647f16a5c9ec39b5d220f11b678f8845f3c4bb93b90afeed944e0860f9ccc0ef52b3599e951be761aff6b0a715c78e447cc649fc535813c1046402e7b8e4e12c7a4d7b1114f4117792de9c64ee113babba10db7f22e771ab99620a1ed8df6297c39134b42863ff63c1b241fe652155d7c960bc9203424db29dcb0b97755bea4e9c7e65426410bd56ba78d63fad168bcbb0130ba9851384b4a8ad06891a7e91789b5e9c168d8f8669165cb427de0f9a770160f8e3817523566ef400faed35f630621bbb7448fcd55619b6b73e6d86a5b87f20fc318608d1ea1acb0669d2c9e30f67adc65b4e028fcdfc1828d37c7c80f9f116742fd53ded8c64ff0df2eef69d68d1cb28eb5bd8a2b9738e1b3f6dfd2e1e47aa1340b2306142383b0a3cf1f5347e9e614be8cc4a8a097870910fa6703ae177c108274397b36679530e79c288087c1b754ce87e9078375c447ab33616c54d11e784e08f6fb57d084807af9daaa78e3e68e3e017a6a9ee9eb3f4e27b5d26b959eee150c44ef58ac5870e0d0bc39ea0a0bb1b0487f4d8d923afe74ff85d1e6060c1ec634d0184bc53ed0b4326faa5d9de64bbd311c6b0f116f07fce479b9ae2ac4224a2acf53a22f077053347362de8a431c6c754cc567fe338e361d3e6d3cb91e5cf1a4ff58c63e10307336e3d03597ab7df3f62470953ba2b5a19ec4f5ad7ee60a4baac399556807e0b +generate_ring_signature fd1f64844a7d6a9f74fc2141bceba9d9d69b1fd6104f93bfa42a6d708a6ab22c e7d85d6e81512c5650adce0499d6c17a83e2e29a05c1166cd2171b6b9288b3c4 1 15e3cc7cdb904d62f7c20d7fa51923fa2839f9e0a92ff0eddf8c12bd09089c15 fd8de2e12410e3da20350e6f4083b73cc3396b4812c09af633d7c223bfb2560b 0 118285cd706468f6aa7efe620162efd2d271fb01f31fb0d18942f8e23a4de800782f2908501366c7db62f01df5c2441f60ef9a9d41f4ce4efcfbce0cc94bca0e +generate_ring_signature 37da3924630a454d901ab6c14f0c9ce5941edcaa7dcaa76ed065355f9b052910 4ed23640efc9ac218ee313367a9a8f0c8507b4e0158f04f0125cf58946f5b675 52 dce177a7e46ea65416bad5ccffdb8248c40c7f60d5f9aec7f030a171b50afe9c 2513eed374fa8702a375d6fa283545d041369b42277c8bd4ee37f9d27d6a97f1 f8ac281b2931b738adc19a29376e0cdbb5d956511775dfdfa83c662a4816738e 5e976360555109c582cd4d70f883b657dc146176fa812654ac34283098bc6bff 958b3cda77650a31f4987945a5c8f3853d4f6efe5326e67be2c3a9267cbb0d46 00aa88ca920a518e7fbe1d1c96f446b79547c9ccc3ee7ca26fdcdcb60f63e6f4 f3356b1bdbe5c06cf3b2dbde4d65d5b20b64336ad7fce40eb09f0630b58f1f9e 652654b4f62198b37d2b20da10e32817a87676b1fc2b3ef4f3e30e4c54180ffc 1c9ee3414e81f40a26b10e2ed8755555176cc1ed689971f9561392b5f6b0be33 32d27531fe39c30b1ae2ee9160c0c9ee108eba119079b0ed8742016ef7aaa2ca 4a107a27322c8bb03ee5d0bc48adc23f63fcf82d6f8168bdd7569832b3edf584 f47ebc0fd3715c259a51c83b7e83cac432554344e40c9608370ece133b2e2dc9 f12f76e4562cb305b8960f1f554abf7ae0ffdc4e6badb166b957f6717f073667 fd12a1b7da7c34e112689a41f39073467022d04cc66b86ff7a497bf0cc1d0616 b53f4bf7770da268736fc8114b6dacfa1dc088858175f9b86fbc9fed556a3d88 534bb202a19ff9604f8db2ada25f185f88ca01fc7088ad340f1e690bf6f6ca15 346de2fef27100873af6d66821c8f5251741200768ee23818b3fa9a42a5414b6 c75757bec6a5a08f65ff1807cc925619091b4d5b24981dfb61f482cc77da7039 3b8645ed5908ad21e94c6eae43f349d5d468c3cb0d92fea222791090c2508ee0 e98040f7f14020c28334b40d70771a3c6cbbe8a2df50101064d859d5df2b60bc df61f3eef4d503cbb9aa1176221ba8cd9ffd65f337be6055d257451b379c0031 ee061eded6f5d9c1652c6805c1ee84826142107b58fb73c267804ae5123efc63 97ce4d296b1ce75b6e431bc2434803aafc379c34f3f848cdef948fe694547dc9 f608c9f72d8866ff83ecad4eb20cd6b70f1c4f6d442e78a7942759f6899081c2 46ba3c0daf0290ec20d167d5170f13d12f35096681edcbfccebfe30a71dcad2f 775ff72487b55af6412c08d132ce951305d0db664fe4912a1321f5c10ff4b3e8 4e18cb336af60fb0a2ecef928495b421ba464d4de1c5d19047982a8707c02fe4 1501fef541bbb71384ef4aa7099acb94bac85b22b616cd89bf1a700c2a7ca4ed 9c73e72776befcdc6878bd812e0c200ca8a28084c73379d2bf1625ccbd271d41 d6352a620657deabae68b4afe1402a3b85c086a3dfa02a80e421f9a6171d7fc0 26b0dabd363366bd830453ef7c2641078028a994a39fba1d42c6f017b97e9c0b c6a3c70fff1a04023682db53e2a6150e836e8030b40a720f32f4d2a95b46974f c9b81336b4ee129ec8b9a9cbd67048199091bbc9629696475427ba97368ae42a 637632ee0b3f5847aed4ec0678786f0c559199c2822015e3fc8429c82c002096 0dfd9a1facbe54eff5ad27e97cf9f9073adefd959ac4f4cbb7eb8ded0a5210db aac1f82782ac3a5bd6d2c8c40cf885510498a4c1f9d6e476325e124ce540cea0 7ffb26e51e6b8610adfde7831adaae8ad6a5c96ddaef2565009a9b94ad6d6e15 0795b0b416b5666efe4c24a7480e53867f8bf140ee169ba7fc885813721701b1 7f3e84793d942fa1bcc7b37af3b41f5dbfb990ba67b32f1b81616b69b792883e 0c0900da46ff579633f3bf33ddc1868cc57e50e49b59b190371c1c413ac76581 6e6dd9a109566ae0bf0a4fe9a66500af21761416f534c17ee6c022845515641d e612814888b173a0cb7f769123c4f7bcf8ea137d4f74fc9e95fc2f5c8036e264 07bbf912e64d52ec143d0db020ffe219bf0c1d75328cf3f5292a6058bf6bd67f 6e44bbbcef395119dfe575d3672577dcee624e475df95ed408f87f775a10954b f38afe8b715f309517f7af43a6299f276cb1b60f0e21f4b74e4d276f688f952d 7700034a145ec5b3a1e1d20813c3b53d74d72a2bd7b84731186d10d7de09d756 36fb88f8b181ea82a82c4fe88ae294cc6372651e58c32c787954bcdd668de009 29c19f352e344ab6ef106795be4c622aeba90c5764760578d138bc6f6f5ac7f8 ac16de75e444c017ceb9076c97421a503cdf4e0a963d09e33adda1240dc1892c 4dc1bd8b1ddaca3c1353af08a5bab3cbaafbf2a90c811c8d33634fa75b3d0486 770deaf78bb43412c35035c4e7830a96eea49b7a3886b164be8223e7a0f18d4e 7e03db7f087d5b7eace9059a859c0ba5e6c061f7534c10c21e5700949f513c01 07a7000f6e7c1bc1a69ef1fbefec595f9c6aeff41abbbeb7b4507da7ef5fa80f 8 c7ed5089c8876973a28cdfcfb3fb762892ba05e5352cc270b19ed26919b51d0687bc152ff601789580adfb0d47ec88ccee6a7a23d1762178ae618b56d321ab037caf3bc810835e9ef4a11301954825501a017aefb8f7c7340d435708d4957003cc30d93e102172078625d88332ff499c55f3172c19e2a6ff3378060808a0510f460557e634148037267376e4afb5ca277ca9895bdd8512fd19d3c2135444f508250d91819e41f33532b972cf5f4096906b1dfe1070cd43c72773f531c770a705f009192e6e6f88486a079de3122582ef6717c64c8b83df7b4adf4d37ce842107378acfecd299fe4ce11a4484ebb4487a6be6dafa7e6751c2b981e5f48941a10028652fae7ad60648717647762447819c09d16a94001dea7d51393497296aa1062ecbf2a921ab3a53b7bfbd8ca7e938fee0540d0ce299cf36ee2810dd12c0de0af1aca421b78d986ee148c444a61b4f47454b5984321f47a0b7db9cf639571704883db7252edc2a4d6690c5c5d8e901b388148af9c9f9e4dd17e0661829e535028ffa5a1938d62499358b0eefb568379231c3a6b9281b9ca8a4c9ce31aad3750671918d37828e05f4240e06f4f3c34f005e8da54786c5708ea3cca41169c603061db06dfa14ecab857c39c1d922ff2a0e18fc4553e6dec474e4d7571db54e030488f7c7982519c2bcbff4e599f8b931d2375b77ab364eff362237fa2d4f1fca0aa0e6ae45108f4c58da0ff59f23e5fd8c835dff229da349904b3789cdb2e0160068980ec0bb674355afb72b6062e4e7564cb5d8bfb086f7a11f2bd2227820360c38f8de53f24d1c04bd02ef0ee01248fceb462993512a5dde379f9f47c624940d1981904ef782295d815ea00979d4f7bb922d881f8b5a0bdfee7f8077125c08014399ea0cb67afc9da4de2a64c0ee51486b30a1b346e47490e62a252db166f60b66deb9dd6f6b6eab6fdd321a916e373451bedce7f774edbb45bffbe1edc9a807602acd39a13ae7331e5f89509403ccb5fb14319c0e4291b99deb5e18ad01bb02f7d8777e183cc1dbf63f7467541ca3a56b2e8ccf4f2a0fbb34586e5afdf9e7023db11b276404c876e5c87c210d08b02621158362bcfe839e5184710f185c5f013df2a3be8961e5cf80423ef000e8a9822ea0016c6ed89f91e75fdd56d9abc60d2eaa41dd80c51c7f704e28ef6d7a0a6438d7e5f276191317317390954b88180b297bb66054f0b77c746bc6352d49e35bf9dfa0d5ffb468f186d1bb91613c3d05aa6ffddae8e1dc943b5e316994b9b8c0542a29be857105ae973a0e101f11b80add24f608f642e445c08bf1317de2fa2bf7e1ab714b5d06f53eac26a72cbe5108f423e698435311052e9c1a3b8a813a8f9acb341dc7761e11693d728efb50f50cfad9f7a607be8faab179dda2308f6cc7764ae92d9182cb961090f9b93ee04a0b9f5f720b93144d8a87c33802934cac5c552b593aa345609122f2b0121716310da48e9081a453e6c8feb7cf95eacaff94c80df844aefde081eaeca626bf27630cfe438d8b387eb1dafbf32ca80e8ab7f4927744f936407ddbe6c791e91ab1e90518b4d492a97260a9ec607119356b20b623b9eea46f9e80e24a52f4905e3c6e0809bb28901fb11ef8d4b4867a3e17c3b098fa56a51d9a22286fde67293fed6e04b0e5e90d3023c964f0ad72285df784efa9629db04985c012a19881c37deb4f0341580ba2524410e45c08f2411b0bfb209e9a43b317c648ff507032fbd7cce2000048fcb60f3b77ea6b1b4c6498823fdd6e3b851b22dc180c3e870a8b90d9f003b01a13da91caf0d9351e21063b7f7c563136ff9979c1f684408675ddb032b60442c2a92d8e3915b4c24dcbb6c6c038544f88946cc6989262c62a954369fdd20e1b3684d440849d27b83895724ce9588bead187a8163a4e84dedaa75e8c55720234dd026a9fcd5c55a42f9d197eadf48d3e94bb9ec9785354f02652dcc66a8c0cce98b1bd5dcabd1a4bede01a20055f9ebb18a43bde6b9a4f896d60f7d203e2069de7312a97c8d72c1b474b2c1e6cbca04a9b52f4a0a368844dea6396a2a64f06aa5373ae0e78f8170394287177077b7d1ed532b662626dd79a95eba3b3eae30274fc12bbc2d21df776763402b12d9d4a5001761d066d9ae0ca38e3395709be0aec3811a953bfdc8708d005b8ea1aecbf052298c93d13147a5a34434e551ab504a4ba17dd4dbbe8d3318277409e6c3a62a43d0ae0b929aa8bbd27a716ff7a6d077afb249369383584297bb3991d989d9e6c4405f694af4de1aa5a47357ff8e30344ef8caef73777df3171f4ae23f2740ed4a6deb46e9da8cb514020bbb685cc0d0f366e21b3443c0f4b113d02a78c77b995f85d821be6f6b1e0da439b512b8600740a67cd5ce22e4d13bbf502c44119f1b31685ebd198c585bb72ad0e33e5d90d9906e9ecc3216dadc68c4362e72084170dc1db57e0e713785ea8fb5676ee720df5b8acbf11734bc5bfff1176a321dbe70ef27990a5cca4e53356affc9a79300aba9e32c84ba408256dca1be92d67a8add78c98e86b6d6921b0e8461047049503043943a7a7b6775d6a525471e377dcaabbd7fc51d45501eedb22f9ba62b21f022d2ac7841d509615f6b803f257d5a7a89b7ba5407c1513ad0b3da1203dcbf207fe4123984458cdf767c5130e97a42c61be4a9e111dddb90f2dbdf7cdb3e5c00a6df4177099170a7b6dadea1445491809790d6a0a3fcc0e11c13db86496696509196a005901e71eba6b52e45c0a091949df2413f72509bf1431410078a4bfc50dcc44a54d04bca020caf1e376034e54f2667272cc45dc65fe1e7ec175fa2e400f4a3618475eaaad6fe46008f85efca36930a9db4ee733470280ae462cdd11ce0cfeb7cf23aaab7bc10bc9967d49e2dc61e326b6208cd057b4464a089c39be570d44e1800b9b15a4e4b39899045223a2d1c9cf5764580ec0b1142de0a5b03be20bc15ddafa91f9b2737eea1e4e90a7bf791b8537fbba45a93d3350ded03513b1021b14e711cb08291f9df0f051c5a24c875551e6e6d735a30d17cbe6e13ce9b40721c44b880f5613024cd91862839610fee3447720e8ded350955b7d069df7cf042bb16c47455c729b2631270c9781d6a3eae40206f3cc8b1ed924ca258034810a7e19f54de6a21ac2b82e9cfc8df42fa138db06550fc3bc78d69c2c4677ba37016594489990a2ceb778fc984113f789c2844c47c2e65b748b4a390bec4ee7d308dddd39d5d0591ac0381880bd6ba0eab2243345f922466ba4bf3bc910c2ac2e092ae0775e45c6cfb707f96babe5bf44e9ebd458250b63f99df848b9503744740ab032e58fd6587831185daef5185004237463e7c82580e975e5c13b76fd562e0cecb110b781c47fc8c51e5c1e3094883c384a2af0d810947c85040db0ed353c01b1bd4d39763fef8e6c1db3735cb0d7cfc10a6442c5df12157d28a534a801320d877f27244c3d4e8fbc0f20c88b0661bd1b3b7adafe15540c26e0f1824570cb0728962e26c903819be14243d51b253e8f22f029664bb8df535f615819740aca078812107793df667408ccda6938d6cb4f2ecc5e257384c1f911147fa5799753098e15912016367c6f70c10ac8b6548ba5dabadf952734b008f7e9e44ff4fa6e076a7fe2243e4ad48beb7f36f47cea5984cb3ea801bd20471d089dbeb3c21745018b6c1affce81843cfce5dfc741bed10af6db606a7d9becddc5d5f2bf7097a00bcfa682bf089220d12ce471ee0ee94c520754c48458bcc6b3976724a7a2efc6032c63fde3856c63d333563f2570e5b9f6df8ca848b939bd3fcda9213168c40e06f7f2ffcc8aad216b107c5f77b38d3e563c200dd2286e09dd31238de68dd2080b131937e25a9feb207f48a36a1e27f28c222e5acb1148c525f106d0b28f3aea039a02bd85878b849176947d2baf39b1db58b745a70216576d1c087b0ac5cf3a0278ead0f1fafd1b4a5cf3bf711d0384fd8f8320fd6fa4a7b82017667d16438f0a1d0046a35356849982cbf07c8f44ad3bb9e2f199e2dc694729daaecd7de9c80325e0c7cfe150224d662e2e7f4de9ef4c41b4609e2d5b0ee528b7126144811c01e6a85bf6d017def3063ce2cc76ed8acb043c78521dbbb9d0394c2af8189e5f095cf2dc216ec58d34a4dd46bae6e5727acea6a4ce117b6c26d2b64efe444f6c00b396d3fdfe199b98dff5eb513a38dcfc2255c8d65dea605d8f984e706899220329d450860b847a587d25c46083564ab90e35da1f866000a85546ae1959f99805a84722b92a2a46c5b1ed26e07e206b3e1c908e8d64397926966f2a4aa397b3023c32f38d4b01b3d8cb97f53a16f7380fbfde9ade35a71dc6c53e17143ca53309a23f19c5e765e4a3d41fc47ac471665a2147dd9e819264819b10f7a4a4e1c0040442aa038216de6dec12d8024f2b6c555c3d7eb778b32901e07afe5d97eb8b004a153c768b121f1d9220e22d590b6bba02e82d360d3e3ae224c19daa68aa080e7bc977ad9e9b09d2c648123ed96d8276578b50f8d1a079b37772ead24ecf210aefe0a5374e3a257ed756ac58abb27ba75f0c50b6b79822d4db3eae761dc74307a1e9c074c7326a57f956fd6492c6a1e101e0766a122692270d019f65c81b8e0bf0c13cf5c68b194b396bc139cbe3f50509b7e65c4e8f47a8a1be88f1acd16206 +generate_ring_signature 84a4d5d99e39f4a3d17614b966ad092890c33bbd54e3fe769685a77c93acb4e0 8ac2bc003b0b68fef98ccdd5d64bc3dfd8b4422d8d64efdeedb8f9c5c2d651ef 2 3f7485055a4139a2f7270d864b8ebc64a6fcda98413bd01a95b261e2524aca10 0ae1f6afde29a4e6ec0225517f8a07d09ae66b98d70c9324162c524a51d3d846 85e95fa17eb2acb5245f2caac9339e9e93fae0c023d5b9bc3ecd8841ffa90905 0 86debfc4f8d621aab9c871b8dd73d6977df0b1b6b20186511df2d657dca0dd02d72f1cdaf70664e6394a66867be17df57e68f947170fae05895b9d280bdc730d9410513d199d102dbcabd9653f5ae2d30e61703840f11ac0665051ad480ede012a10e41188231ec887679040e5e0886101ffef9a6b9cbc876dbbb52ee19b880d +generate_ring_signature 98cc762ace877770ba5894bb90a503bcbfffdb7c4fbb77c4c50f4f4d9d77cacd c253017f5488ff2ee85c4d64bc0d5d2c81dfa2576e0c4fe85d6d26fb148aba9f 57 b26711e6a6fa493562478472e2c78cf5de9481f7dda1346eb16e1ce6c5cc1d11 5ef96e5f7612841b4331d282de5465e5fe515ea4157865252e9fe6500c9176b3 cef6169fbad4b7b8f871554e5cefbf6bed4f7654743320a4d3400d8acdb5f4c0 43433e4373f1d3b4dd3d63caea19a34cf4835989d98e65c2789604474fdf3905 80e8558e76a7073e6d70b07b20f92914a102cec3283c54a19ded466c1185e285 e4e8cdf44315ad5060055bbbffb4bde43284165a5c26ce2c89589e6b9f11c182 bcf85b074b5f406bbce15cc34ff05f9956188e3fc44c2c42d8bfe2217bb800c7 5c34fb644cf4c7d82da7b8a469b5264b1bcb02389210d4121bb08f0649545115 800c35badc02c1758edf2eabbdf6d93a4815337a2957ee647da7b081b0d84ca6 2e6511419aaf7a9d8ef07a5677dc458c4cbd214cd78ba81184615f391abbd9a3 2e6bcd8b8fba0af9715bd8d6e04e94d8b99dac37a0459f80f7ebd0e116c53983 43e49981174384a0c5694054a0ced9233c50ab81dbd6bfeda2ec4be6e403eafe 32f9c487ec842e033fa42c99ac2327c458de4ebe8d47be4f6bede9c2a0b9c23b 9cadc6b73870347f286f9d5a77196e52e615d105d8c19030190cc7c429f9c926 50ac87f9138ed330cebbee46934bbd660467bdb79562e76753cc0879bb6bb0dc 94a52d00756f3f1d80e25fce545c82fd4c03bbd6e41c902b31db2ba19484b174 3014637f239779ba742ec30a45213165880b523c90fd0d3d4c7b63d0994a5f2f 219031cb629eb41b4b10a32d1651e05aaf9495a03b08338bcd890e47efc6cac6 f19a5012590691c3de2e2b2e792e9bcbdca5c32f87787789c6b4977ffb51485a 4c72768293bbc326d62d667117dc8b4f6d6bd300e665e67dc29b59484681b478 1bdfa23178fb3d365d052f13f8fdc38f1881d770b64aeefd1cc2e9b7bb0ac62a e37a74fbf082fe534b22196042d8362da761d745fdd985427d69562f09dec87a 3878b60fb9c067c053258f63440edb0b1f46c1d443c5837f032169220477ed3e f2457b606349fb46ea57d6a2abee2c5128345fca3cdd9b2efdd797583d070236 8558185ec4a0f3fc4e396a291d329adf173baa9aa72e1395cf12e8b6621cbefd 29900f50b91034891cf03f4b826a734523cbafd7cb57c6320b80d5fcc90b3d9a 6100b127f116c2a73f82dda26ada835f874cb761ca5f1d965e6150e9e6f33a45 95e3ff3e8a0a1224f50806953f37ffa5fb0b9c10663a1bb231bcb124228ba020 ac46f26d6312d2057a61e485e8c545e1d31d54e30032eaa16744c55b11a2ce0e 2fbb3ca4ecacf9b9841a8388e87027581ca524ce874e201d13b2e67d15eb2bda a43eba9c87fd83c8d8280b21b361fb0f77b11b0d6adc23348f9cf88aa27ebf3c 58baf0f379c44ad9638002584cac961fcb2b787b45008620482c670cd6d61577 f5cd1ff05426ae925683ac9cbc77b510c46ab5c1b94a163d18f876b5f8e20d1b ce57cb1befd582d491ab51a743b426a4ab8610b7595300528673a3c20d6c8719 760eb4f02f63a25fb6bd7d4771da66e35ef44b3f5e51413a925b2a095c76d434 6d54f204b8a21b7f0e23db8b2b2bd423745364527563461c9af0470df5a02cf4 97aedc73d86d18e4fff5784d3440a98e24cea703faa7658fb7f06c71f16adaec 3881a374b9c8ac18708d67cca7da0bdbb85861d2456f03285ee4ee708fe6e42b 6f59f705f8d55b860a355227987b76dfb347cf74e001b64489db9bf909ab3820 d2e35d572eee7decaed6eea7092152205c8331e89aa2681cfd98d756faafc74b aff4d0497c0269b416e5bee6fc848aafc0b88afa42ee7533da65e6281a30f246 9ef005ff65b9e1b8aa7cc7b89198d304fcad15a6e09b6103aec95cd43a0309f0 07cc1270564a964093ed0950d286899e7c01efe9abb83e06dcbc0ece950d312c 4216dc45cc959b3ed26d915348945dd4132d033b6a818710e1843428d61288c4 b4a505901bfd89d24ab98e077f6d83f667954849d6d42274fca279cec2813083 83bc5fc5867892c7c340715e1b2b158ac24ae03003b50e4d7c50bcaf3859724e 7c1bee87d571eb5b135ffbb4ca4dbddc5e8160a25297287ee19e711ea2339c20 35ad5c41d44ff3cb878da7130992166efffb89f92edc525b25526e160aff35da 8849747c3c4479e7c9ee33db965e200db2cf955a1898e6d7b14a40b1d157e134 63ad50574678f7630718e139f6f6f511fd9d3043c670fd9294f583735ff5abff c826d79095d396632e101cfd6ef026c332728956ed540a8e51388e42bd9c16a1 9732d7812026a82e050376098cdeff6f2e68017308d660d175d242cf8fc6d3e2 f99d9b8630799abe139e308c41d75ceb1d28967dd89c1a72eeabc0af44d96c30 e5afc216446f0b207c89050b7f1e667e36ddfe6159656ad56aa6dd6ee738eed3 f2d41e2b001c505f84ecd63d7c96c51211336b940ce27d98ec61e2540fc11921 1db106c011270b498f23ec26658766f3ab917f5601cb16722b32f575247cc37a 81f893fc5366841ea883878fba0a03512e498676cdfb032c6e5e30480cf514e9 4077feab1b79dfbd60da792944303df8d089ed15fa6056b676e16d4a328cb308 6 38f7787e1604f67d3e4cbbd58e642ba98e005de3b2a008175d9841c5e66e650a6d5bc6168b05cf6b9e478f9df14949dba25009185ca17b413160be80c1620c0bfbef130f96ca7b4530e9cd74ab162adfbd0c85190e649197a7dcf2856a6ecb079098757a1419a6f69559b396a692df7908ee79beacf599d3ccb0df6f68df6a09eef4aacee38f57d5ee837c49fbfc55ab3a4f4209b9d6e74fc79c8c4d5b11db078a6da069082638a9885f5678733dcd7cdfa3cc097cbc425e6fbfd9b74128fb0eb9e109d219f4f0a8e602da357a848b10179767403fcde486aa5c4afbb28bfb0c5661493b9d5cf7f60444464c0195969b47d9f0b01545baa940e8296da7be9d01ffa6605f6997cdf28b40288f7c697f5bcc150285a03377f0254c8c8f379e6d044c9bb7717edc2aec6d8f69e83be85bf9b2719c6b68060daf13eb8ca5f8ee8d02f11dbfd8afa4da5c07a74f39c0e86dc6fdba091dee398530f4434659a236cd0a8e83ec1e9dd9ada1c63dabe878ad926b40dbd4902470b98b964e0bf98bbb090aa614c59a79237c84568c79f9da5541c905652a5eea5e89b1198961fe8b810e04b00d0928e74ea7bcddc189c04dd1b8959acb93b3559e68b075e686b061888f0f692ea047d6183e3606aa8a1b2957a834e1bd392e6309fd046184d258c624e202336a5d6d2fa5cab6dc9cbbcc28122b0d38bfd0115c062e586f1cb57c50889e0c47134e34d3c0725dd05aa47ed0745e26296bc91e353731c3d1b859fc3005e304a3c9d9cc31cc0a7e55742acc6d5d4f8b73d25f86131cb2d166640db6be857c018a77a107e849f6ea6593a0fb13096b9c8d41e60bd4b909bf6639c301d21246087303d32d450c23f9bf9b4a5d77055945abc2c701003e24ff23d82f2b1d16110f41553aced880f5863a73cb6c2d159a823ba43f2d66c73b51788109571956300cb2f88055763c463f5be4a9038d32f39f50ebfd60ef1d942dbef1b12f6764d90330a6be554f7e9b10027c9179037dd6dabff94c39abf4903224c7cc5e9cffa903738e76bbabd90e25cb144893225412ac99cd09e4d6506c627c5226ed64265808dc2c29446e4a0c5c72bd517f42b12c474a9cb49d95ac77797ea5ae0c0afa8f00751c2b6099670551a1b3fa98e454495451cfea31b571c8f30e366dd765cf080c0072c348b16986bd21f3ed367d679423d9fb2d1c3ca58f6f4b1453b0329c9d03b2eed81cb9b89365db906491f091463d3dc5cf149e3c204e075b3febb410da0f56907dab42fa94aa033e6df957171b8569f57aca116c06aba9b6d3b8c24c1d0847188b8ae40d6c2651482dc88b0ccef6092c39b64943b9bd9f6f61a753658b0b428b31a65920da33604b7ee04b5a6ea512036488afc7827f88bd94cf62ef150b51980e963a6ace6b93d716c929b6740e314d37ac15c0fe9e394a399bd8d5eb069fae1bdc066e1db83aa723d78118986dfa6f661a8686f21b3bcc9e6b232f2c0469bec005a366dd47c243cb9254ecadfa500e64bfeb293b48d0d6656569b89a07451fb71513ddc2beeb59a6d66ed6f12a2cc5aea24c0ba95870a5a0c6a29eb50aa9316064132bf5d170216ea8fafde49b531602339e68f1671376ecb50815af0b694ffe3ce6d242c25af4975e6ed6a24175c6f99e34b1849ca4b1565ccd5a0a02d9cb39c0262dd7e2fbb88d3b62f3e9b5f6cffab227d4d3f1f84164a5e9e34c00bec9794977fa05032249fd4f1de5dae5e132fd96ee8fde292b78511cd8e1220c6062c0279f1bfdab5f8860c00182140c277f5df8b0a851b70ab491896ebc460923efeedb57eb2a31d42b982da27bc46badf52f754ea83b59aa8a29ee6d536101a6e7288f9bd18bbaa632a1c2916fa565d842af56b5fdf4e1c6c4121582b58a045e2b6096c0d122f9a0a9167504a69284dc6c944c3ee6b6a5fafc85cf81c8ed0c78f5e701140055c745851840e7014d50c1e091e506e1700cdc2d3cbeb8f307080ae06eaa141187fba043d4ffc00bb5368cfd4d5c3ff2e40e7fdbd5bce31ea606529668096df2abb4f346de2bf9ec9d7767f311f722bc40f0637590ecde63c00f20a68cbd8745db16018eeeb26c5fad6b52777bde926867ad4a92a3afe59a4f0c4655f0035f63687cef766e390f59358a967ae931bad0fa511e86d5ecc0f4b20625ca6cd9691498b47007be56e5be857857f20a51bfa441a94a8c74e869f3420586df0a9e3b1f20071700c5e81d07a89e333478d0ef724199188ff3ab9337780300a7f50d52a6e682cb6185b689ba07d36616449a42a1cf6a50165b7b06492b0a1d7d37512c0e9391b5bd7da39ed336f3aa93290d32de2335ca2d1b3108c34d076b9258ae6de2690c1a6e9dc308e38d261f68aeedca5342740cd2c2739ac6ea0f0605b0a9ed5643698e22722b99f4e2c8e827f0e3cb63351700abe35d8733ba0bd3cec9d1a73086b83e469fba89c32abe99cdc472a13a6265161a98b704fe1504dbef318b31348f5830750ba6567749471498717e6f6aaa14791ce3cd47d9030741ea9802ccf55587ce46535beb744b70af680638fbbb7566062f450286505f02cafc16d4c8cd98e832f3e46ad7cb6cb3da45d81354408850211c11a942002d0b9fd7ddb07b9db67e4ed3ebb7446cbcdbc1cb2dcdc45d74e2f8c48b5654b0e709d6af4d2a44bdf1d0a6f1761af7f9372cb8c5312a8dbdca72eb2f0215f8041d003101ffe0701c20d4a5145ccb49c461199196428b517bb9f92f7d39661d02710da20ed1dedeabe16bc7daadb3e8496ad9bdbf07ea78a0ec3e60324d71d944b00c237c75622ee016abb732956511ebbfd9e0218c62754c0316f99e198f446c2d0a99d37685254b64ac1588eabf8bace118967e6293d0d8c52c514b71b6be540e0d658ba5e21baa8a28fccf6a444dd719d5823069a4b8f556886308e9dc6ddf140831f5d759e176dea560fbdba607e00d2b52ceec030e2b4f8b0cca131ed6afb202f498f7e99aadf966607058a1e43ec6a4de776041dea9bc196ae4199d2a6fd80aaae16903f20ced8550da409302270426506b06f35a8dd51c9ac8112f939c2c0ca22dcb7f20a96c788f334d3054bf63ad713315ddb0225eba607e808ce615eb05c282db743389120a09339d2831366a324455ee2c0df9e0704bd98219764db602bbbddd96858c7ecdd31d05bbe37bbff69e9703ec7c3111ae8d4a923558f1230b885bedaeae58aaffb7c50774861f37beb840ae6ceffa4067e4ff91b03f41980e7c07eaa8afefb57b1267fe166f1ea931de0cabe668cd8714fa913a525293ea0adf07d56a404c1b6264c87109d5945bd23c4313d9641e96bfc123180bdae3ac0dc02c2a52510a8591f5435e6a003e7ca089a312a26e0a188435f40bf4abf4db0239ec20a91f353af812389d537890f73c13734376805779129e28fb5e49eb090b41a741b230809f4f07aafd32b7968e4fc70aad4c48e279e2ff36934741e2fc0f7db8a3113c765049ddb10831b0fcd7c43f1c322451d6707a9490ad429412f00f55f788e6a57618c0a24badbcc7c8cb17f9fc04c59519f212181edb1e59aa6b0b1d81eb2a07d3bcd5527d2a3fda83be228c5d5a69e643cabfee840c36a15e7e0bcc4e6f17b66237a7ad4762362a2f367be28b27b01657bc96c7e01899cea8250656e6b6cd92e056c79d93d33b12d6e7d5d43b4008708d8ebaf0710668c0887e01b742e1a3d5f79a9212e36a432a659d37b9a89670e1571ece3784a3dd39f15902af762954c4c7c0bfbe383bff410d97de9f9f7408f612fc9cdbb2fca93a11f40d088d90f0669fc0b7427edf1796e5806d4665e53b1de0a774efc5864ccf3d8903809f03bf8ebc9f0f4a774a19b3cd84e9fa50dbdbf5da1ace4472c8e3a5c84a03d652daeb1a3960d9338e89a1d350ff24699d0567c540787058f847fa237c9407f09ae6250ab959988ea4760e5c8ba12870c392428a1b56d40ba56ee1c9df030dc7f93c4113ddd313b72711c6d05405ab5afcd51db32fc18285356dd27e3685087817d41b5be3470889aee80a89276acccc57ffbb8236fb6c94c7a91a08e22d05205e4afc8524a1d2d676c20042b25e455a76b2232d45f4bb11b4d590e0d97703a3dfe4a662947d77fde5232e1ce6479cabc731a38972b7e697f69c74798e2d07b89cca2fea581517e3f54c6959e502af62cddea4f3142cb195566263f224400fc2341be8a6fed59c44d79afc01adf16d70c20f6d5c8bfcd7c7646bbad486b107921da2e08f2101dee74dbce109af34fae3a1649d631803017836053b564c1f0b5844266d8ef7b165352b0eccc8c70edaa2cf55a0a2ddf8a813cf02101e8f0e0992874df4fb0ad12d487487a29b1ff8be88c90ef533e69967d2f7140d747a180ac83b0f23e311db182e6328187d5b0941780790a8b3e3f19f776b482dabaf11095cdb5194451bded33240215291ccefc7347eb50c2643c4a7a686481fb94b7d0c876337ba13e9f6787a99ba7994cbb27e22fe7ad06d72e399db711a01c657760b91cae550246e2e33499c745934bf8f164d5953fe138a999cdfe179f0919aa9008116b18ade72b39899ebe3be7826efd0deb7a1b93754ae9d328c75d0ae558107f67f73c3527eeccb7cfb063571d81480a8c9537eb476e0a803e602d57fb0b90cff36120816f1200c5dca1fe307aec761d553484956e58deb0bb6f99038313107af30c66d5dea9aedcfab6c4ff33adaf35d5d2bd30776051dc6e91868d0c23d06af7370f9c97fe7a8799794db0926fd427e9c04929a11167c8fb0a43f47c40505ad6bcb207741e965c6183dcf60fa8711a1959fdae2f3f6bcd006ffdc857be1003f5c35620b804dc791155957af884c38a0b0bf8d1f5a95cb6deea176a25ae00b70f5063d3eca1fb08741d48d6dd2bbde3aa5be24d2af75758014f7eb37b1ff05258bd97dbf082a3e2938841d5af30e4feec2395c9059f6943371f3ffbba7d104cc92f0358d4dfbe8bb8424c37f2ab65080219160f49519ea722e0a5b9041fd01e342d834febbc4665eb109c88c522c4275a96b0e51b64d718d494db4ca76c105b5191c83523aa10bef19a634d35327769faac822d98540e129436b03144fef050e40f03641c0dac4232d2da6a45ffe82f41bf4740ba6bf30992bb0e4b78c850c +generate_ring_signature f8021b956640916f8b67535551725d2d151c3f52ab4aa44b1272395ac5798092 f41efd74ff77da65181aac81b061425bcdf47a308bab2c8c3b785a35ef2a436d 14 1765280a344f3900ae1e5d549cd03b81688e8209f98785e978a4f8581b067473 66951c8dd507262b0390b5693ed54d1334cad699a0fba5186d2cc826d95ed049 6370789fb9309d57e2a3bac90d7da8f5bdc989d17889d89afe650c633270218c 073921e23838915ae1945b040803a32c6cd27953665fb61c4594e807b14c3376 df4d27a780aaf130d9177a32a24312d8ea3751b30fbf1752d0c591f3433c9709 8be8871278586aefdb951ac11db2cb9997d3ed738b9bf94b6f3286efffb573f6 1d8acda75d4757257e7ed0d670a86031e0e9667dcc25b682a3fa2241285174ea 8d1219fce7257ef50e6f77a4105ab906784ddf5a7bf8f2a47d5b1d3ea65af716 8978bb414eb34254bf87d378e75707c16303016b5e37f3029ebbcab04da8f426 09ad39a25d4ee56dd583e29a9e5d4c27ffbc25125c232d012df305a23d6efec4 a422d55e14409f5148c3f1aa3c3bcf4cf64573c1ca644a21675ad3c330d1d9b5 6ece1c52e49a7ce6a527d087174b55a1ac25a5d25544ee8954698e2c10ae223a 118aba8f92ad1a55b5064bbf76b09ac83e619b5629f55e497651efb603f6b101 71ebbc12a23a3ad0a3ca34d463536fa28698188d286ffa82a15518ec932159e4 76f514c6fad6abceb7e4a8ea9d07e60415faf68145c348273d68dc2326991300 9 43430a03fe1704f1cce943771bc9f6602bb2eb4f2cf4bf0735e0f2f4150e9b0c74494c7054fb5311e3284a5d1af9d01c308301546329c95e101d52ee1b102505b92dbadedd0d885a981da3b9ada143cb500716284ec21e017c8dc39ed717680894df38eae595aa2286d6dd2b14a8da0f26cb6d0ff5d783183c284330ea7c9c0fcd2bdd1527d23474847bd66a43bd1309f114f1519efa73e2653848ab53fe4a01c67b7b26928bd52b9b8a0d432fab24c7a36715da4836e09f4c7de7845bbf11017c2a1db545162e57c961fed4ef70eb1e15b62da0ee389d6e68c7ef9b6201b604167ae60f2f9c61a695735bb0b848a1d6123a5b95e5fc3f2f66884545a5de0607a9e0c7df4a2e61dd721260d220b664518794575dc42c073aabb73f02dc94d0089994042233f947623c71e8bdc795c44f817fa34f201ff25894147f2ebceb250a8465e2c88933f35063a72b990fee8d504d8ee6dd68e666c0963b9388f272a60ee0eaa967b5b35d5c70e37e6f1deac5f6dbf7139cace61df92d83860ce6290a0e3119a24028001cef921c67cae2d3e1580db747f052837e64d9fe76b8a96b590b5cbc578f927cecbe3a6c88eab1a0190b77b9ac3c96e9a36fb2d27dd107931e025fa6a12bc6d5e6598e457c4e284c63bba709e73ae80b1976e720f8884ae60b05022c1fc6a4eb84061db4343232dfb05d5e5036292bfd28ae7ebbd0de1823640f738d0b50416ac10f080bd0346c0dd66cc11140eaa831c18f1eddbfd6a2d33507cf0a8a7f671c7e18c444aaf949d9aa6feb48e8fdf1416dd049186cd8a293a70ea1db0df90f690b2e304232b7ac8652635511da24682b963a258ca5add7ffa502ddf62d37a2c18d2254f47f4baecf2537aa898412d0d6e3755babe13c748ebf0d98774bce4213c25585f2f5e145d5a60e5ffb11306fd06f0fdc6fc7f948046a0637523c29a73286da1f23e8e22c40175de2cf53cd871a106d1d4fae99def6290e6fa8419efbf8d7a2a646ae21bf1742333c32962bb546709da470e89fb8681d0a6187db08f2382def7c37f853a7520b96cf7afeaa4ee3b2839b5cab53e5f9490cef42ecc1438d525b97c3226407057217ab2711cbc40e18c60dfcb1537816090fbb4e218c0569b91f480e3e123c1b3f4ee9efcade4a09368dddf96e8ad06ccb0e8236c7043b9e8a86388c65a6684e0e8509ca555fd6b0107d1b6a9f5a6ff43c044760eef66c9d48b0216c97036ffbe2785a860850ef403b065dcc71589ffef400 +generate_ring_signature cf5db39c7a9965f9f90fa5743c7769b46b2f713ede01930a70d32e75eaa26511 47cdcb38509f6bd97a8f4169fbeb7f22375afbec4e886b2be889b02911b0cc20 8 5547b57ff0b3ca6295390434dcf8874e91cb046b4a5101e0b8b14fe447ea11d7 a4db0b5ec78693ba8b68d4c52d569772840dda48ff5fb748ffa833e5392392c5 203b1335e258caa68a2c25529b6d845e6477ddd241646d89135d3e860f913858 2b0e21566f2b26d9dfec85e1f22df42eaeb1b73b760e53fb6d39ba6e78809058 ffcf86ca2bf7b86cf0e0af57a2c1c40e47e3882677f4e6a18a868b7b93bc70f9 a91c3dacf5176a176ba61dc649f8166f83531bea6b50e3ae1edd2e423480aaa1 6af47bcf86e848206ad1ca768d3b3ba758b542ae00de5efed8039d7b3023d9a7 3fed1496fe25ca5baae2ae89f703b0d17d0e9ab8c314eb69ee1b5ad3ccc2f178 d15c8bd262431351d68281f5dd724e95ddb13af4ce741b33fa11a182c711540b 6 ab490453b5a76765caeaeae0c801de6627096f712ca6b558cbe98123fa56540c81383ab0993691308e37516bce49002e0ad969d4d3b50415bb46b36735ab9d0a43e1c96b89b3ced88b8c16c561026bdc482cbf4b4db50225f0f397eab9b5290b80b1de208f454bb91862e6056c015cd3cf0c5b300e03c30cba99f2c077fbca0cb699454771303d87ddbb1471ee73698a54003dee6495818f106cc2145dc36403327f74d694c2f94dfb2daaee027e5260c23fe7d588a9a0ce8952d2a6e4d5a507e0abe84de92c34056182e67fe8d2536059f55be02318510b0dbdfeb59e213b07686164ff79d806b3354acf8e2c1343b4c894a56ad9911759256c9dba3975550683fdbdbdd56e44d181c675cc192901fe09135dccaadb4ed8df6354658a023c0595fe83364b993a246179620aad19fc50f5de39d1907031143c48706b75cf3e082f2294558d89001c15f618a76c72947717ce8bf433485dcb298bd0dcdb3f0b0b546f312a5e3cc4c5d220b4006e0043338e9f660ecb59a5642e0b03461706510b7c1af3a419d04fe5076e5d2c0023411651a12ee21af115683eda3938896a80085b0fbc06bdc15a9e9e40f5f9fec2d55285ceca9bf1642f4e9dea9eeadc934c085a5e1a63fa03110c823284370a36b817875911f38a29d5b4d457a37c80136b08e60fc9c934e31f06fcfd5d543234ebdd6e322d5190fe0222a868128b54787c0b +generate_ring_signature 29730c0e4a0a86f992f0a1d76520af91a8e1cba86a9670bacfa0da5b14922e59 eded52340dbe3f81ec4e76272266530395b9f57d07852e80d2b7381db005ec8a 2 52c994e1d05f8042e02e0b2add412233154e8b3d9e6cf3de02edc0e442527f11 91b33bbcf1750a94f7844dcac1353cf79e43648d815fdcbcf8a660fc0fc46450 9958e9c2a5c6533d942c10876f57b104c9c937864315ce25b5a22a0352a08907 0 4d4681ad8b6af5db87fb18cbc6687ec500802f330293895083aaa6956c6752089c5ef1f0963b86939f740beeb7e40fcdf0d12be197012fd36d045c742afaec07f58c2df7a2599da9334e4a7af08f01702250a6fedb94e00a24503ba9035068074400bd4fd50b929ec6e82b0cfc0059d3fcff52120518bb1b303e0b7d02cb6b03 +generate_ring_signature 88aa6c44a3d4692b8a4864098e6bfd0a9a885d0668c2231468847c15c485043f b8454aa89d230204cd44dc0875dfb6f7e322747e758e9db7385b82fdff097016 38 efc364a054696074d3b326c1cfa63ac6867ee056d5839e4f7d322b6168b6db76 a67c0ec60d36d0dc1dff42afcca8491d660853e8de3fc676f3589a1573f9570b 4355ef8717d216ed47a0a26a0152ceee02422d311e0b506f979e16ba48495f4e 34641ca8770e3ea4e3af0dd0c85ffc96f49e47fbc7bde10e51e743642b4fbaa1 6be5b374bc0da6cfdcf19639e9e8dc31e7202ec8650ca2a0b7be6819c0708ab5 507e27affde7a1851d06f55abc0f97946c0347cc9d876be0c3ad76abe5b79853 7a9b7e233e7839301986b17e56d487bdc7fcf5b0dd27cd4e9d9365f8641c9ae7 49fe924ebbe254c8adb0850d42fb96cea13a6f343cc60b949364b3bad253845b c9d503c365581015e2c1e3615c9ce07e26d1f8dda3f48d07dfb5e8058f226dac 36377b268d3d16f32e9575ec38b0d99140ff29d65d350423b04e66e84231dfc9 38d44e86217b1ef3890d2a114635aa9a220a32ec90b553401dde65aad328abab a302678dd1eef42c38719951b19d7c7fa9496304826e1b9d64e31bd61547d576 0e7e14ec926e76a72c69f78e51d03937bdd5504b4ec62557d9ada2fb2027669d 137fd57499f20d99887f58aab4dfd729b3f4c0c7569932c99fa75b1ad6fe7577 3e8c19a179ba2f1ee2229e0ba446f69b303e0371a6be02de7dca24618fc96108 577d25a2e25d6e303a8187463f59d006cddafa4d2b2d31a4780d735a05624c4c a4db108d261cb7776646f63358dce5435e5b28fc796c1a85365f79679b8c6b4d d1fecd506072bc2c624fee22f8ae3d57420f375dfd895309cf72f03ad4995f54 bb83e240e88d6d90ee540a4715f05b0e67afd24824a47b4f1839785924f63ca8 7d86d996d3da082ae3878c3e399d7837a7071925c88d25931c9d38b34e072db1 dd97b47a2d34a4fedb8e312735d3f485550adada47839b3d5940ad0b13552730 5487d0c5067f9e2ee76e302523bd509e60d718b68f03bf555894016e03efb238 13a4a71d2fe8f4c04eb79686881f8fee4b472a18ab77b725e298fc2f103b68e9 7264f5de6be85cae40a084a3b07622b057dc25d698a5ca15d7845e80184b8984 386aae25bba4175fa15171f087354ce1835e036ec515c988de938aa7141c7d87 c0eaa2c4e0cef178ba89fc53b84ab3aae8e7c2b780d034bb711d24ebec33bb52 e93a0ebfa15bbe9dc5bda7abb2384a752e93c2c62e30c24dae17ef3dcf965cd9 fc163d2d40f589ca41aaedb7db1612e5040ee64bcf1bf47a74b304bd8c4f39b5 1bdfb9c93303b6ac3454266bd5c3fbb109ee35bf8383f8a5a5b28e9248a27654 42edcf8174909618bc585dae6a6f44d7a230052a1a0c74a2f4dc4d2219c990c6 8ab8255c3203b5cd4739009cea042675ae2c52ecbf84d0905d6feb43f1c89ab0 5ba3969ffd36118d607e7bad4e8cc032aaceacad7f4c3ab27fafccd681ae7594 1621ee4952f70c38f3274f2974c17774802c274c794f45767af51db3974afdda 991e17ffff10faca5e239a7946070f8b971c236685ac1fbe39c419e6c1393d9e 2366aa3351fcca201969045e8f600cfe68c75b7435b9a393d4edf7991775cd3e 5d715c04fbd1d3b1ef5f59bb8ab20c40ac5a20a1233023834dc53a0a2c472988 27e04de9780b2c3a6167c68566969ca7b61f62ffa08f54f5f60cd2c542625409 e2cf9a942830562f53245845c18cc3c7077151ef5634b61ffda405edfb37add3 8e44074d410f183a1c05092668d67b058b03715708f6bcf757a6375537a6df00 34 91c5ac2b6424bcca2d675de6593afcf0626d3bfa5b32910cc7747d642a6808015c2151a4f55073be936d174b4707e3c9432ba8167bba182d6e212dc3ae958103012995cb62823e7cd16f3ddc69d47c50acc91145a92c97cbf85f1743bf5ff20f8d0b4afcf5535714f646aa72d9df3ad68949c45a2c3d5376ecd994af943fea09b9c2e53bcad6d593ffcb2af88bcc506430990432f5678a5a492e99f24c2f0901a240022e3effb15abae8819b4c43abe7a4e0297a59de84de994a1e9353072b0fda39948dcd2caae2606f7918d2ca2b54e0822eadc43ae6207ee2220a908972088611ab66e0cc228f1fd9214fc08cec4f9c30acb43e7ad1efe0d977603ac0490e2366104b58da04b21118b9b7b8d749a87ce09ebcc08702b2f6938a8d982ec208797c2f25b443860f2a8d7478f0af6686227170d6867a7ab9409f7fc6d6f5d00eed9662f71295d3fc7d1fe9cf1898409c0680745b9ce2ea86296d39f542b98104125bb32062bef3ae85734d336bbff98394a8986bbe58412d829bfc19e3ea3d0a5bedf8bd98e1b5e393974fa97ef86e4789c17445595784badbb7d2436e23190cf886ac79884931a6fa211fe0403a59b392246992247a0dd01bf9883c5b05ce0e745fbc04950e5956f383f256f02b2355017450651b90449efb12b54ccf01be0facf9b6ae1bfe41a645c8f03594a2a7bf00eab17ab97083a6ba9196ee1cf0050c98fdb7ec8cdb66d0e998eeee561a92538e2f7212897c2660b7b4ad0b7d625c0428c6d3a9e206b6b5879a64bb250535a3e3095b8a963cff9323420e275c8fc5036499633f91b5ecd9b733705f582e00062b00ae73f7e779f0740d599228ebf407e6a62e55627df0940f5b5877d3dda149a6414a4d115a35519b0eda04d9b20c03224d90b26eb014c2e121997695c5cbb77b2a53502396e62fb1450e0430bea400c8e336845a7101d853f41e9766e2d9eb5d9b3270bcf659d59f194946baa49602d1f252c76939438f8ddf434b04089d6fd2053131266b2ce8893b101a8c01be019d179f19d3d682ffdb8c635d10c717dad9801b3d8b574ac7e215544310e91e008a9b844d49d9177f8fff32cbfd1c440476fdbcf479f91c787743bbb985dc9508ae6096fe6c256448b3a726d97f2afcddc8dcfd677761a9a9e1c09b10c54713037cdfd38b4f85c84580153f3357c07d7a78c19951e63167bce99c692ac6c96701cc82214a64da261b7bbce4fd553f9476aef47ca97f70127fa49d49ed3138040416223e6a72427e1f8d03644bfd12753d0aa76c5656b0c169698acdbab6cfee01aaef743ef255c4c54bb200679f2706c1aacaff2c38a3a9169bcd986af5823d02ccb76748fd5499b7ed8cd2df93b3498ab3ffea98c46a457b06c562b4acbf0508cea81b899cf7bc41d4003799d0a48bd6b55b5d327c1523ed4d289a87cc4f20045eb8cb6549c30498584a979c4097ada465eb9e4ca1d66c445af6e693fcc078072852b4c883d4ab9de26baa30b00d388a1b4533d336b6c9c466d8022cdca0140988abad4b0980fa93010ff27616d3dea56a5f540d71bfd81504a8986f825cfb06ee66bff3adfaa5453b8cfaa6072c4bedf853fb77616103e3d0cb47385aa7ed04bce61fc68857f8237ba958ffc768a09e01a8e9592704a282db7d632e944b1b06e51d9fd7609885d6fb1c2b7abda827a165b48b024ef02c174f58cc29fed34b09b6f96307350c9ab4ca33429c510521f9cb5e51b36bab079d5b4447179bfd6101c92046688a236bf1e7798dd2952ee18837412c7bef93491d5c672534f3e02f0944d3aba49394e0efa523f4d8b2a3c3107eaf4af501ac873cae321530c105f404e41450a60c473297b87ee2be12a51dd91ff9cc170b94869e58c6cea79d003603e7d762a5ab0b3703d4b383eff0d64a49460c51241ab61446df1c5c3ba26fb302062b9496b092aa6ff60c8db23d5922f83fdf540a5b905374ef59da3d84bf8f0f18f1668596e7797deecdebd8426e74f6fd002dcc8b5223cf44d6bbde992acd05978ebd0cd48039c07c5f1de96b9977b37dde313e309eaa6cfad70eb664709d011080af89d33eebed29ec9e4b603bed724ffd658e23c6c9a97bfbbc8597091e03fb1c815a41671df4169663d64c838622c26dae3b9270eb2b6e7845867678de0dd43add0ab5aada4c636548732f95318b37d25f1fd73569d4b9359951c781fe097931899dc46f83942e2d59f7dee33d72cd07686c7278332a6701b102c0957104f687b7a531e785fd691095dd0c37a67643c2c31e8a8c36daee9082a02accd5033d0f5e44fa72867aaaad34f7d8c90f0fcae6f0f3489c798db614414506d0f4029d26b94383546ca456ed2afebe6a646c9102e1439bb2fc62e8c1881bb7f9ab0ffd8dc727dc640ff2e289de780dba298dc99ff0ab3c7eb2c16421beb67015940e99fc0ccedfaa196e5f7ee378110979e124b8050d07826fdc723feae458a62706fda2c2dd41a83b02f5c0110b9ec99175a89687bb291efb51df102c9e9df5640e13bc50aa097467f28c6de314075bed69c68e5693b55817cbf902ec6fab68c506fa34f2faff9aec2c57877d7706431ab1ce8a24fb517bc2c38f2c204db8c8130aa744fc3b103ad2eab62e6c7a9f7509d38d50a8f1ec165a8f4e34deba1d6d4f0f51ad35eec8a37b695fe7afca3ee5ca0518cb2b00e0223416584d0b2ec50da50926bac0c81949324f1416aa571cc18170f1f6f29dfd1d4409a6d6e609250ee60940248619d8e0beaac465b6b7c0009bf1ac54220ab2aa05afdd8e9fac57774206d8b3e2daaea7bb89f18d7d73054b3e127edc50fb787c186a7d0389808fe76b0c188b6d842ecd206581f27c4d5916adf4ea54662bcca0feb7ec32b3d5ee189d099ddba3b3968d212f33a7be7072d9d31b151cc20b455702c515f88783bb2ad60e4b1e6378cdc2ccf43a1f93fb5a159d7487ccf5a92c8840f8868984a2ae0b4b0fea2e5f38050239d2d30f5fbc94bc1cda52eddb139ae146accf604d15d8ff9d07ae1f45a766a02a7599810e1598240d46561543581fc919fb8826bfe528a633024cab07669345f82421c39828bcb1f50423ae5999ba11dda5ac8d4c8d37aa050b69265e91ffb54c7c593d42cbcebda23a92fa1fa5c85b73bddba755b3a2241e07405a02573ed3f799b1715b967b057f8eaf9cd8754cddf0b6a5998920f87df50675fd77fb62a5b62499959f5eaffea9f788c8e16479cb36944597ce5050ce750fda2699d86395baf9b13ca5cbfd9b30db278153166c133a30f87b35db1bba65030e59ddfd85506226ea5198eca52eb6bafdd371b0221129fb757086304978ab0fcf9b6fc4ee8e18c931347e73946d62de1ad16c0da9ba964e9a8068a0066fa201485c706291b0b29dde1afd06a1b29c3107854e6e81639d25d6a03d61fb12b907 +generate_ring_signature a1803f6040067279dc5927bbb9f6230982575c18de3805468fb1789f0c3c14fc 7c2340cbcdfbf891984925e0929d9ba05d98a7245b79316ea01fc367e4affc7a 2 df2739b64dba204fd3f08c6d7bc71c67f8d8729ec4b97b1d1e15ee357aa36e9e f3a9ad283c693a03940cd0737b5bedfaf0dc83ef1c37b413020f171d79ba24ed 7152de0548821248d153e95c40a6f49d7fe4b3b756ba75407d92efcdf1ea0005 0 0337d5f65e0d6616f0d53f58a4f967725a594489b7c69dbf470615a79f0d4a033dccf039999b585e42fb424072276bc9ba689ef0289500149c69b6f39858ac0ea7c55e313b206c6f31bfa843edf515b2e822d48af55ac5060e711ad08028030c32b517b73ddfd3f639ee3b4559f3f154228eb11e6fb908550e2d30318fa3dc02 +generate_ring_signature cb063a641aa8df67a1356c7a5f38ef3c144e75fc19c19173b5ca644b11baf02b 7eb64e0de454d4408a46dbdbe1d1f9062b4a97b747450a38e93e50dacb831fca 37 bea32eea11374a3f226e6ee61c1a0476bb43b653b46d6524f2d14db679b45ca8 67663875f5ad04d2335076d008889ef313598167135e26e92ca1f91fe40db049 785410d39972d9719ae962ede5f5add5a5643b25e1ae55f52539d2f05f886a8f 4a489e3c219fcff88d16c983a6286ba6107c0f47230cf037bae38c2253149052 bf16a1208084cc336087f07c5f340a3df9ab09764976bda3ac5d108f3648c2fd b828ee1c839d99a23e7df25b14c4370456f68d8beef3fd81cba3cefb5b237a67 e80e93a49138123d25bbd5ab4577bf09b11a38d952ca9d3b00cfb9bae2ee05cd d996284a1dbf30b0fb3efec2561447ce073129cf7519e6c7191f3b2087d0a882 0d91c7da38943f59ae14c4293fd14757b164d6add4b06f4928a5e20b247e15bc 8cb58466a65f9984531d4a830b841ad0edc0aa7121ec5414fe5364d63b9e1e0a 14b9ebb693dca47d84e934db186509552aa6b8c46c45513db6e7bb4c81f21e46 b833d9eb4b1caa083c36fe751f9142a331c2ef2b57f6628d3a56d55e93afd94b f3e3ec18a96329bac49b2452ba9f7cd60ac238282b126da161c4c174ed09c498 093627efbf3e5d22edd8e3249928244f987fb777fffe999e4a05efd716c5c5c6 abd53b796a62acd19c0ca505e3de1bf94d31157efd7c2fcd3ed384b986706f21 673ae9112c2d8b2116bf3cf9051b5d7b12899d6f21fc9a3bb917a53f72413f59 6c30c4060e65727ce9123165017a14de5035e68a5e7c6fdecfd8f970b14ee99f 863f056dbe98c04d18a45f06592bfc4ee665c62c7831fdd1d19318ef34da8627 2a98d0bbff9074465ae8b5cf89049b21ed211fdde762e788dc76ce7b35cf16b9 06e301d7f8099cc71a8fe16c31422aeddeaf356e8735b8ae07fed650ffa92059 d2076a3a939bdc87d5c8f014d0418b79ef6ff252e18dc179075de022e522f6d4 377f06d2d146b27026a64cca40bfbcd554c8a4e7c9023091c4546dee2749a777 9386b6e00244ca6ee5a158b7932fe3dfee07a4515b503e55201216c24d81c084 cd71ec20512fcb6c485aecef5c4742ba4ba8e2549a4714d538f0f5f38246bb74 9606fade4bb56522866c0aabe43efb570a1871d5103dd15349db47c5f43a8aac 8137a0f5416a14802100f19a9f6a6998cfbe7dbbcb2761979fb60ea9c2d681a9 98c39b8f424d5eb66fcdbacea0799339f102220052f5592a94f9d7972d2e2355 933c95a86f595f22ae501ae22d78381689f6116cce22892c30659f70e7a5504d b900f199f2559f69affbf2e3903d859c1e5b4e412b4ab26be7ed13022f771806 748a52a04ffac29a5466a162c5a96f88b8e7d8369f5fd1a576983d8f0cccc398 214998d357d9ac9b62adde160fa689941b76d1ec1bee49b8416e793b79632d13 18c092f03a303e7d4d2f1872c6b2450fdef57f63714d44867720d6d7e977ed25 b697a9c18a59efbfa24a3f586d90dafa6bb0f8fba5250c0d8c275e6f4578daac 42d1207cb6f3fab2994fe619db9f1d2f502e28bfdc81480dc47ea4dc10fdc599 d4b378266c56ab3b4d843fdb8550b0caee3ff8b0e1b32da87b488505bc505f5d 7b0b85bed177025b0ec2fe5984ef2a84dd2d837fab9a705541596fbe97fe011c e6bd6ba6dd7de88957d71ccd2f0c7eac1347265e29c8d9ac50afac4694e8d7d4 aa38ff0300867a5e9a2a1f2f15c2dc677decb72196dbb5f5562d5d512493390e 21 175bd7bd2143826d695b9b226d926f2e424ea9dc210a740f107198cc2b6e920d299af685c2eb899a6c41dc1b24be3faa07119f312a27bdbfcb90f649b6f2490e3f81f3787402cd53dbe6f79be00d0485b2ee94bade037a53af6e664fc4e1dc06fb1f6d14de94fbc9e49632e790c047926a1e80cd507437fee60f998aff452409eeeb22359a519526dcef52b970a7e3ba7c9c9fd9423e6234da49e79d1d5f510041de22287070118b9d7d4b5bf4285c9af2fdd7bfa3fad891c8b176c861ae2b0ff3de96d550c373c8f6d06d80be3b9d59bb67fc0701390da0f346257e32c40309a217de45860e1e4ca76d575ebdc33066d372d991bb25428def2ed03cdc08f70bf350884cf037e5c2f3b7e6aa69e490ea53512485584b6e1ae386499772708d0ed4be823e0713c8b6fb9147c4eb0b15db32638838c47716dfd1ea98f7f35c660731806ce6f0e7acc629f7a8865c32d9f24a89dbd4f29c911ce88b390deeb3b3050ec69ebd137fad32282515bc74e8337c0fe1d0b0c692fa2a69dcb222bd0f5f001331112f82f12ef708b81fb5529f1ba49cb5783100ca67418a4444be02c2710c869d6535917e8f9235b14bf50e63221356ccb9315fb500b2ed695479ab60ab016e88d9d7e6d2e073497268e2fb04c5bbba99ce351d3ec871db18dd13fdd4f0017f48ee6f0037387e9951173ad58cf76dce65a155f0732c85a1510132e87d750c1dbb453e5b453701c3338eef3ef68db6ee035086bfc8410913627dd0c7d3720a1df862e5c6f6a8f6d12b125f95c0012c088b040dcac9cb7407a105fcac15e003d7d642a147325243afaa4c2bcebfcf6fd1ca10e87a0462b5d6db60fc76025e01760602341861b338a0ef7cb7af886b34c7b9d4182663afdc031d38851ec783058045c1e0ab105ddac7f3ad790b91df1d409b53f098db27402cafa868f9f4a302071196df3342bb5fd7c0021b880d1990f688cb6ee09a118c20b06970dfc8920c0bc04c1a4555272699e5ce5555b5794a6ef0faaccee5f0108fb98231a754f4000995148ab1927c15b1f43a4d0a4cf8191a6bfce7611ba96ef87453bc025fd40e429234b1864904668693d011b4ba2e6e3a1b2bb36be2254612a489c03a9b080c88f16c7204fc6670552455198feb84383067b8fc201d66e494d8732c9572a40b5d9028b2638c0eb3104747b6ab09a65e174b1ef8c7f8d83e465dd2fc0c53be059158ccc2d472e28a8d2f1e6ed3b62b39a91524d2f6520accefcc6cec8aca100cdc94b436ed91c91834beb2a6ee4a53e9713ff9921a09779a4f45785101684509e487c496f8c1e22a514a9465c401dbc71489cace0b22da0adbd67e51547c7303535d60b04e6ba6ae10dba5f8aff7abfd7609862b0f0d2bb80cc0a5ee7f15340658f3c13fde8dcbdf4eeca60ffd70ef863b1b6d58a886a6e38619b211943bf00fd45592662ba19ccd03a07816c46e81ce065bdcb3e3fe812f6800d285f6fc5f00100afd0dfdf73ac374ce5e947b3554166fb4cb29b109e9ae9a11c7cf2588180273309c202317a6be74cf8a7fa81222889828df87e6e925f5a072e033225f410532e10dbc765cdf29629228ee600b1651dc2feae0689182015be8b9031c3e6902cbf363a740f5642f15b45af5bbfb6f0678ae2ef1bdf9e2dbdb9fa9346382fa03ccca7828983bcbcff1304b5f18614fe5ab4d4b2f19f54fcf3750692b64045c092e0b11e9a4eeea6e81fc1e1c5ccb1d092bcee755ec785d9620e4f9fd05432a0128d9faeb2620045995d1dd25d4c20ca3ee5a47a80a5b2478e8863f26a3f4d303b9473b92b277d99ee47366593632b30fa7e9540ba2fc4006b9156ff826d89609238e76ac26af11f00bd2ffe82fba9bd21449cc859e7bb526074459638a4d810128ba034451f4e263fa1d01dcf872c4ee859f16af9d2779fc6701191e3abe4c00413ac3c96c60629f6d2300ea4364427138013098ef3fb0cdd687366bfc5ced04852255cd2ce65b29b1970e1d7c111cc6ad347931f75951c7751c5a89f43ed5040bf8d430488768766be16f689d306d4ebbb432c2bb69c99a067d34038d43520a9276cbfe1019c9e467cca3c690919d223bcd236ef486ff913cd0b1a4c9f0f60922f1977788912619cdc1dbd4a87a1023103a4db40b4c59c2fa3f28582457fb062d79407e3249dd62667d8168b16c9639acbde74d28e35b3e2ebb834776c4be00c3c78ddcf13afb883a6423595702700557b0fe2586bff9114e5fb5401eaecf0b3270c59b8ba28ce9f2974c052371041086564a4938bec60051695327069df502cf9f127d98fd41febb2277740b83b006602a412a80a8e3503d88b696911ff10248b6fd21283de64f9627e26cf9f6932b0541388604aa92d04d33c6d9226cf80d79854968d21818c1eee82a6fffa31a4b76d294e7c0673200aeb87d3becce6f03a289ed0c99c378151119e7d66b9b4f268a73a39fad0f56f06c8be76d3333f5011ead9d8963a0d0bf96156bc09288e710c3d592981937a61eff462bed4cbdf802943de30091af3c556b6c74c0c64ec6aaf7b99c38fb3f4300ef3283ccff7a4106e490326613739436a1a8dee92d8b0a875c98a0e4414218c181269df48cd8710f764f5fcbf629811d9ed1e4ceaa5f4f129ff85accf72b8bda8a585af5a2c1ff0b6b054111ba27a1f19ba4138dd8169457469af0cdde9391e645ca3b735894f005edfeeda1a89e2c4c800abcd66b66681cdf91fd76ab39ee14a92a8c7f0548f30ff0c48e0bfe296559331c74630e563e53775fc9e52c7393b98e122f88665cfb0879db3561bd35ab60355ea5564fa01f4d589243d88b7e84e77e4849117f535f0c27823cba793eda431937a41e267446d804001d14ce7c69038900baa8cab6df0058b392282e1fd7edd2eab20d02c925cf56a9138336cb94665e28515cc22df80a5c5ec49ddef579e850de1020c62b4bfc1515a7b0ada8e4aef523f28f13570f0feda6729cf321e81f0ed00e4aad6f114b3fb3b9ac1e05af44e00955b5f4838c0cae79ad0b788a8f283442f945e6abd9f4d6ec9c55aef1d98eecc58caa1f52c00a332552899fdb8f762008bdd8d592cb07847e2af86f3abe351410534c64e96d01e2194630439fd460cd32cc3f36f65e86ece252590323261c38e771a0f5df71073d353893311c86f57b8dc7140aff5cec30fa1a154b8fbddd1412838bc127140d9b63853ccd43609d3e1a403c3bb84f4ce4a878b5ccaa981c860d3c3c9d47a3086830224470e96cc046c29716e7a65f47687c9ab7a357a8491c8d4e4c2fb65a0cbadce0d901ae03a007a4a4cf682a82a044194d5c5c0337828e4a7fde5e9b6b0b +generate_ring_signature a093f0d4b9d713c07199c877ba5c154cee5a2e3de853bf6c957a1dac0a3f349e a271bb82e110a687fd02465a362586bb5819aa700f262af5e463347ead46ea10 1 6c572ba2e66b6a78502ec9804963a986ae4caa5c75dacff1f9952d6f35d3bf00 e3a16d2c1ac4a8ff6b3a46de49a35bc0d8b477fb1b4f42edbaf632f201aa6e04 0 98639c1b1d1aeac100eb42b6fc4b0c5170740da34b8bd7c93c1112ae3100630fe9049e16a200e72c7324966c74bc5e283329badc55a48f1a65720764fd18ff09 +generate_ring_signature d3e6420d487ccd0c6e5965bfa8250215897f5b31f7592f20729b8d3f6bd7447a 7c5578cd6fe3176c637446d7a05911c1d7d6c3ea4681a8736debec80f63e2123 6 2eb8304fb7127a66adcb213a7a0650b42562528197f84bd9522ea3051ed5cd24 5415fca9190b3f5fa2b229f8ebca3aab2e084f01d0638afa99edd2c817f4a71c 52bd92541494e4a389f667ca59f34923911e17ef4ba923a04d238f89401d8ae0 b7bba43ec4b225e4829ffa00d0fa6f995f4ff93ce9237c645a7d7708b0783a55 7a787c82c233a80ae584187d8fb27f351fd70c1e0180d7b89fd107b4de95b249 4283651133f5a2653a3986e50527205d012b8efec760553b61b4884da0a1efff b937b90b65de6579890f0db32d491677cf8fd5f44db6c7d7a9ad0cd1b659e700 5 e3190ff2c3622f770edc4ead8ac109f038d44cb9311cab30095a33e893333406b8a304e0ae4f29be19a320b7d796e147f052b381f0579a6bbcf2a4698e08290013b8dc95cc284975dd341c3f319104228625438dd2a9153af41df0228de00c0751babc5add668d813882829fb81dea9ab078a265f4f59bc421ccc9983e66d6016285d5a21ef8c3856a65d2fde1225cf4e9149911fa10cc95805c94c5b7bf1006d9697854d8ee4ae18245be716527e327fe53a23938d33466d4ba2e4bddd401067907bb3dc006181c4218c1ebf15b74acc30dbd8f800d4c3dfb70b32591426d09df03f44f9a4381f70793b23b558c24e029f61d176a25c49657af21c8bc2f450ebd76778d65a482c912a829f786a6fd013c2c61fce6821f37d231154db25d540c14d5405aa7c44070336cb77b759868cc41deb4a10f0289c774d819030c2710020eb00ace129cd10063bd9fb82c112cd1e5f272598b274ef12de4b429acd28901dc14b23babf4db06752fa900dbbd2cb72ffc95ff4b674cc9740b719c34ff0e06 +generate_ring_signature ce2cec55abe14f6c5d3681e6a3da9e3bd75442369ace34915c8a725ec8806160 87b8f46d7b275c9b5369af86eed5f501ac0b81ebac648fe8f36d9d915fdff948 1 486357431c73d5cf1397298b1cd50383f24fc35c1be97d8aedbab99aa8ac723f a12a31c75f64a774be1d6b3ed7fab33103861cb483a53816dc9a4e1b3b286208 0 c5165984b9e86db7f7088fb81f54921891e01710e34e62191332840dd9e52e0b850df1e1fd39606b5106193672b9b238db2e0bcab46895faccc809631e642902 +generate_ring_signature 52d3b9d8dbbf600a66330ce684b23b97512b3ceffb06f10b5e2325830f7fb6f1 1415abab30c73c95b26fa65e654eb1bba5f14ceafd8d854919a7a19116171100 32 2bf9a5ce4f74a73ce77dcc1d01e8956323544e4a449492e8dbf1de903ba97ce9 2fd5c38c135dbaca74ca91390981e5792951dfb4c83d71d06529f02e816e4d4c 89cec6a06e173bc28b00d9d3ef3c51ce7b01616cc55c8a9d45c128fffdb101e6 b5f44456c90a7a249646885d35d5db65184cec7dd0be9cb2c18fa6352a74d589 161ade23e6cd20343d1dd48ce74814a8730f23c094a8015746a3382099c46453 94a417be7fb630297608ffc3712413d82d544bb9cded0d94d7bdafdaff4988a8 eee3fc82e1af8f3df262e3b314d36ed9f83bb7759b7b02f0c41aac201cda1949 284605ddba8dda759414f99e51adfec59e7ce81dde419c562435ef823b0c9b6d 830d6a4682dc2665251ec970d5a8f96fd042f5d025ef91a481f34996bf28de40 7498eb23e50dc103c4f519bbcc9381a5b60029041246dc1dc553bb689e2eee6a 44d6a522e583ffd8cf9329648694b9266cbad576b816967df7f3b756d9c913b6 f34f3df8f6d8c1514c38f752da5b864baf4788362c64bc542ef7dd2751a2c635 acd62fdb8c5efdb8c696c946bb3dabceeb590f939f4b6356dd9369ed21e4530b 1337c53da0f8a6dcdbcc63e887517f84c0a089b58355495659a652e3dfeb411f 917f489fc0c2b6f3528587698ebb8677f881c53608b53f3f2bd905d70a06fae7 dada2924731942c61c466d9c6de640868dae156d194540302f79a82702bf0429 fa3b6bf5bd61feffbd54c112865a7beb659dbffbb38f9b0e1a0987a9ebcee1b8 040af4ee043537dbea34833e9c2180dc376dedc18690cc74f989073ff33e6e37 17bfa3529d2aa80ba839df4007f84e9bf84261b3245187fffce49b4d7d6291e0 54b895f65b6034f0b489f46a1ab45b2d333d8898ce166fb7a8498f4e1bd528ac 6df1531b769ca5959584a3b3a68950800f2b8e4ae844e015f1cc2b62aace0c92 f4f4cae7b8d9d21abe554d719cced2c68e54251c6d3048bf659ce0353cbe578e f80112baca356e5310a7e39c26f0d09499765bd34d68ee5acd66d82e4fe98dfc aaaba3430a132804726b2cc82f545d14f5627ed2db6d741841260c43c816456c 4e7e91e888fd06e26c6c89660dfe245581ff809d7ddbff11c330d3174fa04d1b 2198d8a1b44ba6e477169fc3075d8646c083638f30545a7839929b4140bacf56 70d3df3308e7e6c5171182c853f3d9be9bcbc4482a8b7630830d6b5cfbf0d627 986424a6e7f49dab7690757687ee49ef7970dd45543d2a077121a87ab4e94752 6852bfc54d20b1814abdbf21c7683e2545c016131950a2d77b80e80ff4728d68 e5121570eac98b534ef5ac593fc547422c68ca0106a05f53ae5312ca455b1b60 11256dec39f616a8dc40887100d249a831b7296fd9410148dcce5f3551095bbc abaedceaab6087d7bbc1bf3ffa0ee012b872a4a8adaf7f489ee9312957f9af68 f5b6619b2815de2835a3c49b488ecd140158665d1a04bf28c5ccf8775a0da206 3 3421686cdf056e64f90bad2ec4a177b148237fb2aaff04cbe38d9588b1abd904fd98756d9e1aa828598d43197c7537f49cfebd6529601094008ca2d8df8da1090bb1052c48fbdb9deb37dd2dc8fa3c2ea0ffd41cfec958e66d6eb20d49a95a0f75b00635e7f96b1b4e1639469acca8af01b924549fd91d7d63efb7693beb14010cab2b472511c90c1cf3e9f028e63a4f9abb14ac117e0646adf8a99db782820cc3a5330a39979cd7d37fec6e8ce55570f862a4dec38d0fa538310a1c94cc5608e6a468cd79d2bdbc4373e89e5f06b581ad7a61866404842dbb904f8e5e9c30061e5960cccd541eb6a847f8c4106d4427ae4a32f7a5eec1af5b221b162522b201cd1c158709012390a9dd277f346732de4c2fd6e07f00e9d694d1cb3feb864f07c62a65cbc983b2767a16dd1c9750073faff9d89fedc00e1a81ab55931398ff0ccb8f9b664872706aafc2daa31f7de513aa1cca67b7df1108a027a41c7e80d302a8c229b2adb12e8441ed7b08b55301c34a9b7b8b87fd5ea2aa9759ffa372170c8e20039b8bb541b48cc34dfda93eb75211be2d7f8a0e3feab0ea9eeb1901930a3fa974bb0f22ca70df617a1fcb779e74b346ac08e18342fcfa7a46e78be043052efd3d4b33669dc59819f67978b51a68b5951d3d10a3a60d71e600bab92bfa06921f565c86f38c7e3d3c359c8d7a8709a320948d0b56568509c56fc0d6c841037c67b6011b7fc69a1e8cd90abbe04fed04e1e12e975baadf3fd822e869b95f0d98fb9214f10915985bb805fa4ea72e0e19992c16f928aa3bbb0fe31a59af080224ae8f451642d5ec00fa7923dcd5b834f28d6ad1faaaa934899da3ea8577c309d1c17b50a81b37cc36ed2578950ae9ef395d87263a1c0814a91008ab7ee2ea0910022af65c5d48942efcf5dbffd00aba132290d2488e45dba7044cfb490f3d0898dc16e4e66abfcb3b2134ed597c038664b058e975da030e51c5a0ceb0f04e01d5a39c68644f7add4a3d7848761bb19cdf0f15710ff473eaab93842c7eacf90c73d30274f820d76463daca841e5fa10b290ada07ee23f8e1296398d5fb44bf0a33990c969708a0441f33ef0a2a6b7d1a03f84fb922006f7df8f13e7ab284c50302b7ff4cce84662b038f824d97dff93f9035ab8e5820fc4c350d66d340d3670599cc54b25024ecbd19196095b1fb5ff173c51c95f9e0208e9f5725d07bfe3e0f3f5469a7c3cac1918b8479ac0a4d9dbd19bb35f54a71e4c6bd3f6dde52d71b0cb26c56d1e3fac1e2fbe60f7f462e5e462b4acbe9b0325b07a8dfcf6fa1c2f80bd5698aa3d4f75431da2d2d2b734a32db55e5c14abe4dc91a7157e2864c39d70d88b45da3e129ba961e37fbab534d3e66f1c98d307453dee55b6a70718bec020364cc645111834de950943063a26978891a6ef015a56945aed6101bfb9e6e920f8869dd677f3a56239b34e61ac27adc82c46e24898824bb7cfb8c5a7e57fb2d0130f9f19f1c2ee09e62895359e87288c2997c6c6c40a934b1658b3b288fea9a08abbb3020a289d4457962d21d82f8e9f1d6d463eacd1137d6c8c654db124fd30a91afe1be9c5c9e568b320d21fa6480df835af06375aa1f4f6715c499d7d55009e349049fd500fe0786aa1e1657f16cdfce0bfad5525f9d355d4f790a3f36d9095216b9571c5240b571eb16b0ff9f40aaa49dd7683190070ec8e87589d3b2820aa13fc7beaa89b59d35ea92527947698d20279f8c653540cebf1f1ae3b581c701ff9963fed44b640d17151a9b80b15d778f5f77687ca16545c15f17a964698d06b0052e6e9d45cf3868233b7891dd8f011e50567a3cfc8db0ff0ff7f0941f3c07f571f28e53c19f580336fdd8cb6bcd4604d8dda25a95e4b5645b7a9772caf6024bd563f1fc7add6fe79cf861dba8e9825290ca5088083f32f3bc1c1cfb0e6f0898452301b847f4c823611f411702f759e127d49ebe27cc8deabab318e6e913076476d255a53682510e0e9a5ec2958d9fddf913e0696a22c26cdaf0aa06aebe0786dd8a412fa44f71d4c4f68e8bdecb17cad9dec5466d488625d6ecb49628dd076420e531e0cfa87d8b3f58d59f15878e61906216ffb4f9998edd44f86ff492064773893049c777a6417c7ca3a527e54f1b3e182fb8448d967786a57543082f046edc950791b7d9a861dc5a277c82d05809c9e426d7c1ba703403e2d2706fb204d0bc7a1fb89225bad1d781868235e2a476aec7068634aa8bb67eb5bbda66fe0895436232b3ace24c5342cf7892293dce55a990c8727b17e897bead7d1ff0a60c1f3591f9be92c1ffbc2723e1fb64d043c9414409ac8920d7068e6781245bb306a9a8abe0f8d61a8f6bcc9ae7ea8fabee336c9bce02a85b3d0aeb20688a2606092e60e8ea42e2549a09ce10e35afaeb2a895deb135fb33869e08d33c4fd52300ab03934befd5ec88564e85daf1d2fcb56380235f5e51ec3d87eb0f2ea310a4b083465e36d61f7ca0857cad896bf9c8f7af34498bc5e4ab60d3ce0bf0c8ddd2e0baf7eac95f6cdc8f777839e49765beedddafccae6ceafdd896b4d9e89ab2ee4067291d4a11ee48e0f58f194676141496a096744de5f49a1b3d737f94b71c6d102899d17ca8b17b3a0db2759f57537ffa04fda039bf327f78f9daa8de1343a6405a5d59a336002fa749f681644e90328cbb4db39d97fda61f173ed3cbc9c0410057174b81f73b27320cca71d90334229887739685a437369654cbf41173b1d610eb58eddbd25b42a08dbf00863f2677230989035163411f97c70e91723a6b6900feb80e4eb0a63fc662b24f8ddf4d33e388b183f791db8d81e8e27973905d61e0aa2c58fb5b47c297b98fc613d6713875dabf1894258e3da42cdc65137f6633f04 +generate_ring_signature c6cb846df26dbec64853a2633f6c5820b02be6d9dbf8041ec71f6dda17ef3f97 baeac068e31625956ecb6e9c1cc89fe5de0ee0be738481a627d58d021b943464 2 fbe33784ba208567fdcd192af63afbe69ff44ac993e3ab97cc4a83567db265fe 29e581cd6f72a9c25ecaa6005d3633fc4c9434ea7d9a538ee2ae8b0b6aed877e cee91f055dbfc3c6b3ac00812d2ac2093a3091a8bd7bf81e4aabe6b72ce07400 0 c582602feaad9021b86c7edc270a7ca84740784ed45b31c34715a531c2286b03ce72627327e9b10f7b05374cecadbbdf39d9fc2b5a87f096f69119c48e370b074a8fa0da44c5c2804425fcd570738456114407175eb712fe6218b4401bb3c9074ce193daf711cd794de612e1a62dea386cc12ef45a7cf15bdf91785012610300 +generate_ring_signature 212020c72cd77889cff95909172b40cc321c2d1f398e2307ca286bfb262ab2ef 16c4765390424236ee8b8f3a3473ffdf37544f506ffa1034f75838e71487fc49 2 e27333cda1b2acdf76e2255a394500cb50023abe01ffd64a273d63fff538d706 8bcfff9e7c8b5b0ae10adcae84a40389a5a7fa444a28b3ad46d4312689aafd6f ddae576e00d8c299fa1337424ca2ef9335db885e796e21b5f2eaf89f6dc51c02 0 3a9f454d51cb74426d0d4f03840bd9c56cb04c6abf7c6cc95457d09ae6e081026726afdc9025852ac37f588ea74e6d0a9c9f561f2961979c304a77ff822e82091b56e8841bdf1eeb3ff44cc2cfaf5449ab94dac5d27995415974ab21ceec3206e4bf6d191ca610fbb0add46a302b245a07b58fbb5a329847d5050f38ba2bcd05 +generate_ring_signature 7d30ed44e00c75e96c33f03760a90432a1690a1193ff876d417d21e8487ba912 a30db3df8d6b97610ecb9cfda7341f579fb9e3f7bc08eb1a43a051971d2e581b 1 334075594376234d9b490e73726104d68d6d8ec8e1d25bb3b6683ae21b59bfdc 13788a5633b82e630c99832a6603b555153598e21dc174e4b891806baafcc20c 0 c6a62f75b434aaa8d168641f2a004ca07a122363faa37e19716d1b17f0ef1d060481f7cba711ca48b77860891a9d79cc3bf05d7d047f0af4edb1eec05493030c +generate_ring_signature 417a7f59ca93a0ab23774832f6cc9b0b364a7c5683ff07aacaa558944327a74e 03a8725330e3b0fe7ab7b59082bea85127f4259ea7788e48f38ddf281531f0db 1 5c784b3fe02b10f934981a64b1849875426808841eb0197dcacdf313f1dcf5f3 3d7d7e74516a2e65f8350479b3f6d49358ea00b945fb77a64276d8e3078c3e00 0 c2dcd8b7ba07a2d6c683045a53351c2fbff81dd3ff0aee65c154710fa930490908806ed0f74ffa0f88ffa1616b6328bb80d958b5907e38fda7edaf49816fc80d +generate_ring_signature ce1768e6409a1facfb918093bcd3a9f13c529a3b2022210a0b90154ae856dbe0 5f50ed7be091ba68745160cbd3cc5774597df22cf57d213f70d67996918df80f 10 0a6ec85766014ccb526265e465edb96128bfa65d4ee2bd4704369ea75dd83c55 e957cef33f8ebe828959156ed52c8d6d36d9ef391a0275af2ebe3db96deac8ad a72e686da3fec25bc35d011e309eae4947d4967c5562512f4aaf9fc72c161d1d 5503cc0fca18a18fb66b674d5cf561c69d437d3031956d9f8254979e92a4487d 9b1bde104409ddb6fd070655d29b700b37c4c84dc839ccb0b74213112a583cfa 0da2cacfdaff75254d7cd4f9cf883cb3aafe7bda2b3a188af2ecdcf520df282d 405df4b44997ca729aaf3574887dbf578019b9680535f343a3fa37d96cfc4b8a 225febb9dbe825f0c16c683454633c4fbc35fa6ae5f723278b89a1709d523b9f be55882512602e01961a08f0099f1d3dcfa6531636a9caab45760ac3b1ce72a2 ab4986ddede15ac776b62a24635a6d4407d1463f351f960e17d653f7625b33be dfce09422a606d016d7f0eff23b58f28d944ea157916cf59c0792b17ca665f06 9 24145aeaea0d4274c1987663eeb3ef746aa0ee1887919acd175b1f26f876e90bf28e5a647318394508d41a13808f4f9c0cad50c739dd633d5e52852d3c2b710298de5aeaba96e903c64df1b40e50cf13ccab459e821add173a4c84fc9b325d0504c57eefc06fea70696eafead4c964ffa616cfdf673ee8202a8e606530fbf60da9625a431102ce91934de93985de674d45480a7172f3263e596e1389765845036e83ae3f65958d4db1464bf2da3d1b410deaa61d3cbcd4c7ad70034656a7ed00f0ec09fac7ad07283e88935b55a5b230802e3dc13ca6c8b543aeafbe6af62d0a8b49ae2d1946638ec96293943ce7152a5c18a3e4c675f01cb1e8e10247c1d70d4ee992e8bd8890371ff53f424f49786a3bb82201c25f2b6dcc33c574686f2d0d6f826f8fc59133b56a3529b1f1cfab95c31d02190047e97e3d566dbe122a180510c5203984a916d97f49e18b2767435cdda5b822dabfb72d5d23f1f5c7b09c01b795ff59d95270d4b76a8be650b7b59293ba2ca576d60b76a2cfcd070ca71e0c43a07da3b3c8ba581efac13b4a9f6f7b291c98e19c0173f60b50c57c7b88ba0e575c41be6b3d499ecf03ba6acac4b2ffbba6f8ef2f16c5be9cb8a3d0ebbd3b0f204b1183021ae79cd1abe7c375a52d3333e58e27cf3e5de32c8a2581da651b00a4d731b0e214bc6e568344914483e9dc921a42df8d3644d7f917e705bf61c10941c17a189502b1bead4572e6e8cd744de12b10d68969c49af7f0eb3e87a37b0e8cc7a68596cacb7aee855c839d70aa17fab935e23862f299ac4e0e48671c1502ace3cd7c7056890efca0d0f7bbfc664ba01b4650904d675dd23ad86a43a1ac092543309458deeb6d764d4c04bf9d9352afcb92904101ec511e73f2bbed4d380e +generate_ring_signature 38020549eb21bc2738ed2e3b455766dd4751057083d229edf76ec3f8ff0f21e7 754af5a4eadd0e75c90361726420cc22fbe8b5da269867ae6e336c78a9fbe7c1 2 7711aab05c8bb3545e3314b844e64a7b46d50cf7c3cd4125bbcd3d95798e6215 9726a85a1d53c159c5b4169582a503640925869bd89fd7b2221e416426b8d4a4 6e9413a31327fcef3bd4c807ad0cf675c20c5271eefb4d47e0e9115551398e07 1 9a76ca2d819698779d7e109d8941f0076b286a7f09542dcf018093e35414df0bd93b1bf95f6c123082adc4c845b115a3e27dbbeb87c7b65f6424bde7e9e2b90382c03b8089b11671bb7f918b2f61f15fe616f681e449fe1fe00a471f803fcf0a1e41abc697c3b784f9aa9c90f3c40e3faf4793d2b04c0a9aa069d4a987948401 +generate_ring_signature 41206982bf7c7446f432712e311d4a8772358ef1889e27d829ad4a7e956f9455 4cda3eac3ab8a8f2ba1f84c763c5f651cac516f91ac9a0a4e8d02a17dda1c6a9 2 2492a8961086b5bdf03c006f80ccf55395e4e754f4028fd369d3614e8ca36048 5a99a50342a7735c47da129566289c663e27d2b60b575517a6d4b255ed6aac0b 305b0c14fcf85fff69b9dd19962c1c9ecff50e6fea3e427b15577abe294fb308 1 26adfc04d6feb68bf1d2eebbc9fdb61aa949d206b79f38e7ba47dbdee6c06b008ecf279b4eea3103297e63623d74538a05643d7807430b482087286bf9a32209fdf1f722eb33f76b8f7ac83c20ba657fc922bcdbd37ecbd8f1f762160115cd03b5e62fbe241bb44e02a72daab1f9491563d43ba3ab356e8c50b03b7dcb1b7709 +generate_ring_signature dd4c4b8ab2d3fa5d2abb3f4ac9197100c054befbe3054aa2cdc568a46f328798 eda1f8350625aa89df7b330757add945a188c4399ea7babec5ce9d7e08eefda8 7 75016e708d3a8ab7cc5f04fe444eb42ab09c40cb290775e56505cd3289953a87 1d1d41d7eb3b17734a9b15334dc410e9d163c408cbeecf1261e348d36b9a6cf3 019b596ae2d5d5c5c65f68f435fc96682c0c567d3eb8ad1e16c09dc380e99be1 0c50ae2d70f84a5dee719291c4304d4d3b802dadaae9018ecc3df7be02374fd6 84a5ef4239326f027bb248c8cf70937903a52bf26bb616e68917df76e6180bc5 5033ce3877912111a421eadc438c77b708c42aac0d675e7c6d65eb15520d4825 8f603f41f884be2ee0a5cf5145bd6b0b77e1d5dbb74f6080c666edadcb348e8e 31288bcab0a8cb84e43717a03bc07d9a336acf95e8ec34267aa4d2defc3b830c 0 f2d3e2bad9ad99d2372349cf8b935a92a19766c546d4f283f66000761810900bd4791ded4d83f7c52373fa1bdec66c4f7efa602b588db169632265ac044fa106b1f7c9597835ef53c14ed4bb8cbf95b8b7cfdca7513ed88ce79cf3c20927f40677972391cf0064d2a7c2bd32f7f61e36cfe6df47207c0321761fbb78811ce20bb1b4606d04ae423b8eb4139fc299ad57131d47d5fda5e1394f6859b103d4410f7277dacd7dc7f8bd5cc5e187d48e2425942fc4449693d71655c17eec0e3b1d03e37ac701c7a2c7e14e00871bf5bb6e2146074778fbb1df6304c31496ef2b5805cd3bee107913ac6a385cf0b1a111a20afad8c1284fc474c22368d1dcc1ec280c22a18b3a192d6ca4ff9310130dc040096356e47f5f450e89f94cdd49f562690c4c17bd83fd87181a0b395f1ec131dd40380d0bdc2e059acb40d7a1e92cd8540c7dd6b7c8321622a8af07be39aa407c380713c3269aa008ccc9129d504c8f5a0fac070ebf334ae2581933cc7f0146bc5cb2042efc526afa694bbdbe0f8518280ae562cb65473724a3d86da4c7f6127271455435c30720d751eb1323ae2390b8083556bf4d247e3dd8fdc831ae8c94d62450fb3ddba477f5c8f0dbb1c6762af105 +generate_ring_signature ab60bd84804c37ac1c3da382b238d0c538471ec1ad13fe2c95491d7114f4b29b 0ba06f3e6b5c1f667070785f68c7bd5cfbc711031cf563fd99e89954e80ec17b 1 a3254e7a0d6c3aec01f6410546e32d4e0d95bd6cfcd62645f6eb7758b4074e66 c6e7898572f548516597033890cc405c5b1747687b57323e6b6f2ea2cc930c0f 0 2f26ce6656c6aafa42fa087f0927a1676363794fb42866bd1e66ff59dd5c500a92138d3a54cd0d749f7ff3fa11e12cd8fb0401ddca292feff89f434bf7d6b604 +generate_ring_signature acec5120e70772b8c641bb4093292e37bafdf3acab0fa9c9e4df54ff51745cd5 fee50b9453204f5c093b5d63a2166a3038877f8d6361eb92cce2a54e0615f496 4 ee2c315d5720d6af7bcbd0e36857275f501edb9e085735bf0cb3c20669dae14c 9f3e3b8c63b1a69d636506e772e1b51ed43e5313019bc42706c9dba42bbc80b7 c5eb834fee9b2f434f5a97f8ece02f927e0ef966f282daa5541ec3282869d207 2859b2bc16ec1533dcd113ca0172bc9efd09aa63a64121ef73fdd5e5a98610a5 3b8b79055b8ef20989c04067cf4ff9442b7890b663b4f91c6006c31834fcc90a 0 bba428229828dfcc410fb65e25895f85f3c4c9f5333f9e75e0d8e9b6b5d1d607ca175f864248afd91471818123016d86ac186fbde6d76d108f206c38b4cb1d0e8b4a7f31f920c49d8b6df883c561a605829df89c8334e5bf468bf2e48190b807657f408497aac770338cc5766e61017fd290fb91eab9c78ae1b0831f2e614e0e55d69cfddb5a386212d8c3daeaa2ca1de208a0f67077e6e31dd89b7dfaea940451201d0a679111beac286bf041fac6e5c17f0b51fca4b65073eb1ec4debcf7062a9b2361077fc15990c41c05d364158ee37e6399a2e3a78194223e09b9ad7c07eabde4d91eaff9d22b761d34bda9b72129ee966882bb27fbf7d711ee37ca450f +generate_ring_signature 3eb5c92e9f2983038e19a90b4576b639f7e829436e17d47579fc08bad40e2891 4022b73b2b5a32c775247c7018a1083189605539aa330c47165f05e175ae91a4 59 13647b56c4dd07d1a3be1957cc06c0e403f0a47ced1ee2c6922407af5afd5c2c aef99f6375c028357762a9ac22cc77b981f616fc732e373c016ebaf3b627a3c9 4e89cc20ce95507795fa47ef2a123e4e1c4fde8cb35d425bbd20333cdaf2ec9f 8e6375ee797f82d7b7792f8aa1538b0efdbba3e1dd3fa82a09051af9a3ff4502 e2baaca70695b81ccb77d0cbaf305e42979757deb841edcf5fe83c185105e38a 5b227b26f19ad2ce1f2cacb5e9ee5a31b73d7a3ae1cb32ad91bc5ee6e3a84d01 687998493679c71286f347dbc2e75f3780ce4a2e330a4dbaf45010d3372abda8 1f9c7a38dad597e31e00a38a7974e93f6bf90e386fc9613cde5817f980b028a3 11e98d29cbcfe7c2a1218898d327efd81751c493b4ffb82401812592ae58745d dcd893e1527758a778697391cdcd817e5d01c2f8f6923e511069ca55c8a08420 7c3f4731a327de73a4bad40a9debf88084afb41fc1f4992d90a55ab89d4a3bf1 481aed3d6b54fa3200676406837aeedb1dc17e95fcc72313dc53db59510eb962 5590cc7f5388cf193f23b8f84f7e17ef4b1a77559b4fdf1cfa8346a5a959265f a87d17fd3c608f7b9c7a0bade7c10cf04c0305062e745007afbedb3f6a7cdddb ce2591db99f93ff81a30aa17102f9f38863e5c3eca952654755a2d9c6638ed8d 760221b7bfac64a8812d611d21048ce08dc2b7eb59affd41635c84d675a917dc e745c84e1eef361872d963b389ebfbabc079da260f9d1c8491dd1b460605091a 47a896b13007306131e0d77d7bc52f1a992f9b83dbe8d0d3273832c09bbce450 8ab4aab81346fa1754f837a2efa7334dffe834d1eb3234cf81a0c7d41b7c30a7 591c5f054194d2f0bd8daf94344ad85cbfaec092d387c3b50181e3a22c7d2063 43d6a2c025b90b002e5b39dd77c290849fcd1614794f157134f1b18839446bf4 25f870c1302d95bab461349d7fe69964ee9f7a7f5a7a428164400ec64c3dbe61 33ec478924318e0496011d50137f6fac7d7a016d2386c5cf26b3c98d82c9d3d2 92ba649e91125ef13af832881736b198ce9bf4358008baf5935b82a611af54be 4ab204a91d40e212c583135b8a5f2360d6eeb92514e9cf8173ea41267119567f 0784f247966f4e24f6035cb88d2c0fd0ad6967421e09ef3d4793d27b1c1cbcfa 5dfd22b4e8f9ef1c1757c48b3e4ca3a9ef5ac119c32a2476d346a7eb30af96bd 13b90f32f486384146092dab1dacce0a5d8cdb058b012de44e8aeae277415618 d2265228b62819e32f57f76dca6fbac57ec1f4bce7b2f7bfcfaceddf8eae7a0b cb22a2d8801ecc3092edddc0f4f36e1f96b6ea857223ec4e3f3d44e9e47daaef 0aefc894e6288ff9b42125d36f42690362e469355f7ae0672ddd441308097d9a 531a1c278094fe167861961d1edf60a34ab504b183bffe43990b8d1ddd728da3 cbc7a79d6fadc954f8a0c4ee376e7bea7d63697fb06a39f34c56476b51e78c29 a0115cc540424d46d01e5cfaae3e5262c2dcf78e2d597f43af5005e53270e88d f0a10518c98e003e5b246b3ca87d71d74b1ae7d5d18606191c296aa63af3c4da 1af2eee820f269c85acd7ec3bb67ddb1092926e20c15ca01dcbd1a3f18ca5bb8 5df09b7e03c63842126cb4f9f0cb8d47afafa5f2b28161014a1d7240121510ff a5ed96352fe04edd61ea229e8af24aa566a5357f5134c2091f420c05f2064f2a ced879c06b2e47565f587929157db4fcfdcdd200bb0b02b2a9f3707c13e2f223 8acc0a92dd5148a900eaf826e7d061c20ccfd1f5c5c576a0e47ccdb1ae0a5a19 b2a7ee0cea0fc8ff11f01981e7ded25063e32ffa9a71396760fcc36edc9b9d7e 4059808a9b1167c3ddf470a7db7e08844c908af6080164dfd991addec83a31ef 8fc83bec1f347110ebe5928f6e556e5bd15496a376e794d8b40209ef2d406579 131ba29b0045d1b295d3f874ada55debad61b7a42407cdb1ee3c24961b617d00 b5de6eea2aa4dd239ce37cc7e5577eb0ae66d8fcb66340b4c4c53fa9cc5c18f6 8db731a01204ca089d42cec66601eb854761af42277da0713a18b023cdce6599 196d26f29bb359a213b0d79ef609492027ca6350a176f22c43413a7f2c3da416 42b03a78abdd9fa371d696a00075b6d6a9d3e2e6b169f3f76f35e29811ed8cfe 4c9f0d7dd49d545bc9ee1089771e974c4d69f2cb234c1bd9293637c3a5185d26 920db0766e3c57e32a7a6a95e0f5c9b61be83d0e70b34b872b157567316cb121 da6bbc3ede09295135e3eac791ccd93c2260e8a74b0284fc7d0f28f4613f6d86 54a45359dc0cecc8164461ccc2b00db1239a51600f16e70b0efe1e078f6b23c0 eb074de0537d8ce9db85b649368daa586014bda303f3e7f46db646cd90138f12 468b25f2155dab0c1cf7d10dfff04e6eb258f6189e7a953b5246dbb97259e7b2 40b9e105efd16fdbaffba39a1ae066e6a8937d7c6ddbdc5ed7a59ff2fba80b6c 631214294ce039af268a3171f451d53c602d5f4a6b88463e20f8d5964ed26d1e 43e6e58252d8254c163da8dd027d5c3a0583f8c4eb8b58cdaace78da77c5ea7c 62a3747cdea8fc87d4f77d91fa778ee104f0dc1980d56ab2846433519dc9d0f1 afcfe81399f49ec04fa6e2bc9caa086c2d201387bd1560982c2a170379b3f644 1850f27d15c0120ca3475111c88426aa486769c986cac3c09800b9531c281002 17 b92ca806cbab085243d59d96959079d772371af4ae5b98b8444929ac4e133f05c78d87d4a0f5e6f7c1e15d458ab35fc83ce3ec81f12d41590faa86c0b034620cbfe9863b4f621c6071041962413f88ca5c4f507cc272dcd3a9addd3f5a19bc042559289b6e626be50b6783a5cdebd689a5f6ea4f199b2bde17f5e03979bebe080ffb155f1ba74f69fe0a5a84484816e5de5a4dc83bb97faaa942facd63ac760ea865dd813fcdb366199949668db87b9affeb05f01bbdc704632ab117fc2dc60abd5895dd445cf05df41c3fd781d05b12fd61c86f0b4a28d596693acc0d157f03bf8ec13e9c3f8fcefc83b5888fc530b1cca18e65f385629208edd28899a7370963f280a9d6309b7006ab401e3dd1d7fddab92ae664147667fefb81dcf8d0600bd41e74cf83c882d58821c990322f66be642e16d8e698bef9e021f7f88719780856ff5ab83ad6f95da7104a69d3695bb7efe629e03a7ac46955450ae6506edf0fb3df0aa725fffd0030fee58cf7922e9cc73fe03f8f6fb45c986421602d3e71078341944963149897c98f21814d263bd8508a84971b7e3320771293532c4a810fa353a2178a481b9e17dd90f2bb4b6fd927bc6eeb05f478c6a686dc8d025e280b5323f7395226bb3ad24d662217f4f211b2ef8cda3f6ef853fcfe45a51cd952021ea7a513aaf49733e36fd63ebcc3c99ad3a5060ebb3a7be2ede8bfbf32370b01c9030a51553904016ed685bdda8add6689cee72e4863045d78f6d9e67948c30ccfa8a084aa85073d50a7c19866f4e2f11677ac50e5abd287fc2ed28ecb388205aa6b1cf05ab05992fa70b5820722098e65e1c648955e2640c91e379e1312870966c0a8623b1b2a3b31a5464cf77113346eedb18e1cd6b44a8496f6dc66a76f088289a105cd2845907d5435557db9b2b1ff0bc4368b9b658643f9a6b87d8fe20a71411871d5a64115e371d8a41478542d1e831ca466f17ecfbe7f5da03499fd07fe70dad3a328cfc7715769dc44637eab186d71ad70df0211acf22f294713b2081aab4881659f712d9ae16fde6db666112ce6dedd41d09de3734d7d263fa8920afc075e4d678d6b4da24a8014b027ca1f282507a5b2818edee2bb0cc8e13bca0218ba7882c8e582ab2ad660e8f6ecd6736002c4906d3b8e813751aaca79a5450cf619aa4f11b319552653a09e7a709604c4a6cf8783087a4e9649b05d07b5090e71648937f3ec8a3025d5f4915e8fd99b04884baee319f3e0b023eda1a1f8770b69a7ee115017970591d38a0fa4c7ecd442fb961a2f87ca072962c44574215d09da1f4834408a7c69fcd60a2f9a37075303a082f98b7f61498ff9e0535fa1c90b2b1c80e763570538991c6224c7c3192de11ac8204f51cb6ee78e508431baaa03291041dfc06445cd470d48d6b779cfb3c0c021a62fffd8c725c918909dfdcb0a22aec472d2d6e30c854ddb7577add0f308d63c830164c99825af1533808a2a093085ff3cc47a3ca0233019832f9f267d71497483f1d8a53658d9bfeb879d3309acf4f878896593b27c98c89319f5070c1ce5db419903d2256e60baaf916ab10626cbe482e7a2bf8c32b767821fc4ef8f71dfca584f3093f45982e20716430b00f76e181a05700e2d1224117a9ba5dbe5d88232f725772652ef8a0f03b42f370665e560e6328855b0d37c5e78c0edba7b353c4487c20baf0b7d05d56e14d8c5067eed1ff5c63e2863a3e98159317b0c45950a51efbf4784797ed8e38dd59ffd0bd72f1b6a6e277df635326f9a074c45855919d57fe71c85ac7b2a3e2d0d143d09488dc486fab3e80b611403a91fd8e244f224486d809def44b1d336b3c295db0d6d877bc4f876985931721ecc7339006fa33feb7fd2be4d1d71317f40d8e95c0e2f04f2b0ce5de9408d2f8a68d49425c7ab2671a56b45a316348eb26290af400fcd8442afbc65031ef3baf2f36ea69f665a0ce5abe9da16f39460fd44b698820e11b8e5b34e39c96f8babde5d332b325038e436139da121b919de3ca705d0fa0bf3cfbec21eeadd8d93cde64eaed00613eec25065526013110a9beb87b54399097d016da0805e3e13806487dcb8ff58e889decab6e9bfc0fbb7a26ac47ac2440db66a2d5805b22fed09cedcb53f45d2fdc22755d139f276c2af8980c8e154350c535f35bc8c032cafb2e88b6bef6ad683ada2f451480fa06f268706f271bc220954c13ccc397e76d83b2cde28aa541b3093301bc990e120ad1f9bd04e43d8080c8172335ad937f7877b63648d39780cd556e2dfef45b4bd663b8b4c1e59e39f08e9f3d2ada848dcd9e281ad5ee2b2b2726abe327db073726ec0384cfa6de60f03359ae2cf7b6cd6de262dcc776e76dd7a89fcf2b4737c10362553bc600675630bb749182f2f9cbd1b4940654b31a683c43c18ff4fc769f898179b7740b27f7906a924e2a3ffd7ceb8819550c67def1fd37e80e4281a3913ac6068eaaae810a00b333cf91fe35d4804a91ec9cb86330495e49082718b872e4fc091e5ae92299705736b566b3c41f37d5c0a1020eb1fcfa6e17d873a5835fb92cf196807c61042089c754f9604372e933e301551013a7f489eac9e1518918073be3ee6118d21fc070e01cb942e132cbb73b03d52bb7cf1e82f6cc70fd79415348daa5cf40d5b4104a8516a3701f5c5221f10a23b7a021aa0ba254ea048d06354636b6bf66eae1e06a61835f1657801342db1afc27d4485a15d5d3e97002a7b3bc23b9ba55dbaaa0eb87c2870cee5208baddc484864878c5da9e3b41fdd996c7fddc524801fc1b50e192cf472ccd9c5a99a5ef347dfef549fe0d9192049b9a8e51658a8b88633d907681b717ae3cd93cb0c31d98ffecda97d11bf4226b44870671920e8f0c7a905012df60e1443b9f801b4de66b7f1f6d6fd9503fd7f00f50b282ef87fecf7b73a06d004cce7e27b1dbb1ef7b16d145324c50e589ff4dfe3a94d122843c26ac6b70a5befe1c84818938a0fbcf22c9a8f99d02ee094cd22937427c0652d1ab3107d009911ed93ce7b4f939ae87304b2d127dba37dd04ee725f68d4596ce566846830eed57aba8bc9c8a622a602a00ec1ade7023d3d909740d43d62ba98a9cafadd207892361069d8ef820d53a05d08ebddf4d0b7ac5e63d38b10cc9fa21fb306f9606e7c34220c1ce011543d2bc2dcdacdb7b9f7d304fb12b247934dfda688e721405004c7249f9592a5c200cf79af508b8943b1197b968d5e1380129f27e29b6f70be06a000d631e6aef836c2d7057ab2539f01751b4ade57178098c1ee122b18703bcde5097dbdd644631587660572b52176da1abe6d9c86fbac27808761ad424077520164f0dc7c659bee7f07b80fe9dc409ea81e605af0d35f48c8681d6b95601291cedd9ae8ea32bd2e6eb4faf6de1e80c345ed46c54df065e510be45a27c90d39f47b8ca6868db6633dde1974839825e8b0e33bb2390bf7ea5fb7f9dc51af0f848a6883ec5c3ef3ac83a85b95b14d065428d0adcb8cb05e5310c9b85fc06600c3cc66f16d67d1356f8322e3f880621f0074185a1bb82fd55707765befe5ae0634acb390f51fd3c7a2cee5a46ee4f630682161cfd26fcdc540ce1a3ed957c20746e0bfff648a5f3c0e980885ad5341b25d666e07969be153284be6ff0ce3f20d8056f9981f892f8acda992938c7f165b9f7717cfccc3f03b3f50a39ef3402a09aba912639a04dd908c0ecdbe551c46d62ed4eb66d0f5ccbd0be7d181e7667100aa16b2510be007be044c753e27fb6c5af275f50079093deb004db272852d460b43a1dd75c2d62f09db0c4216b0f9006fdfe6289943806d2e0f5819037f555505c2e87d593f1eb17ec3d18e46bff12b9662b201d0eda9a22007857efe426cd70b7f4eaeab6d4374e159ddaadbc869d8e11e8884a09b3952af49c2ae830073780c506018aee759c27cdec91f0424032272bd8ac179c31f29d3cea185b358faa9060d7910b0685a404a03387acfbcd43f9fb97412a392da33f16304a25e7c0e310838154d8fdca427cb3fe3f9fb5e10e3cd976c16c13bd47ac85aa19fb7e0f3dd045d41b060684997bd42e90904881f3f57d37c5ba5ca7c272a3b0f5585a7397903e1f24c8c3bdd3effe0ae378188d9b562598764dc7154f46b04676d2a24d52d0716470229f6c8ef0548d5cf9c4bdf4462a4000693b76901d25deea266060de406089cdd7565b4c6d4884292251e565c9fb219a6a9afdd51451e9c01d49a90d50e7de6d0094b84f48b86ad46ec3f831cff23a483079446725c475f9f5365988709c4f049c1f77207e30c11538800bcebd3d013e4968cf7db99d5427a211eafb400e29f5d026a61e88f6f279cf3aa1f187a7fd996ad0a839c9c7c459f6dbd75e10c0a5c9486ca13e46e768443ed4d1b92b4e43636c94ac367e6d017660ef93cce00444971cf997866f0140ed74704efce4ca0fd1e870373170115cf9e3d377fe50eff5dbe101abf556aaa5be5c69eb30d11ef06ce36dc66fde75890cace0905d6010ca378d874b938eaee0373ff6e163576deb932c3c46ae3945fb7c4ded54a83096e0c5cdf43aac5d83671a0c0e6ea33feefa3e3962c126afb44b9c9248525c50d8eba6e4faf6e543b17fe5804ad4bb47586935c8dbd3d206271d59f269328960dfa962f7f659a01c5882f4c15590cce808ebb8f718b3eb76723b5c2d971ab1e0e3297bb026de2506406e5b6c872be490c876d3ba21a29b30fd22b6f1f09821804dcd09357b61432a8d13f810dd22993be17249b4c18e46f461555dd1a3d57e906c6a7157d8c98cb1391373844ecbe6a20f185f8d89b35b7fa9de11456a91a2403e6f6ccc28f4aa6f92210e31c49923679d3880ce5a30207379cb49ca3796d1c0defc9d86f634dfbe99d38c7ede679cf9aabcf8923b087fd63255cf022bb76820f55ae91e6c502969183a69e8e9565019a0a426ab5f4f8a76631d8e8d11a1b6f023b65da15f2d32cd800b2300189b4ed61aaa4ddd966bade4b292ec244dbc5c803b61f3759a40b0de19a82ef0a39693382b5ae3450c8ed3b74d49bf21c7dfc680c6828f6ffa6a87499cea94864f4fe638ad816fb9ff14bcc6f2cf9afb2a3c4e30a6f90b6b952a59309823a8bad9136130f5cb910132cab24ef2d9f6ff500c26c0387f03d7f562ee284551adf92ce3e41e1ae649c71e44733d85ead9dafea71e603ad6d7eca59f5df521e135eb312d309c0f3cdf9452ead4fb4ff6f818e86ff0d04e21424803899cefa5206fb71880cb7e427e0cfb19997658de993ed033cdf460ab65d6462a3c3b993db9e53e089fe92db22782c510ea87a98b8454f10c185f70d +generate_ring_signature 09c5d60dc6eca2f8b893834cd0c4cb092054761bc639447ac9948f5c51145d80 9b6ea09c23150aa87ba74aeecc02e92322fed90feccdfe1d37929f0f4616b427 1 3241cc28a5d21be704999d3fa0351b5e29b41518f5be253230ed505872137ca5 408c97dc3328a893159101f60ba0d1b8e6e6e9764b7cc802c1096a131d115300 0 20020ea91dc658eae5ccfe198039fdcb119d7f8717574f8b2c85c4bd30faad09043ae8730d9991a29789f7f4d55f29e7d0ded12ce2fa4faaf80c7ed41259e40f +generate_ring_signature fbc6f82b559bd067dbde93f88cc9c085f5a31558542986a7b988e7f29f7e2c3f 81f12c89146742c1a4b25f824412c1ddd65594b20f97dd18043a0fe99b6ab155 1 97dfaed2ad14bc37745a564fe7c42c553ed59a82f6e2d4cdd797b2edc3dbe43c 754e835d11ca25fe843be0ad7f7515f8778fa31c8a276c47de6635780dbc7a05 0 b1b059a80470107fd802ff310ef3e89bc0ff66881f534e92cba24291dc6efe0fa2cd1d802c6710bec29786cb105fed8e5eab0e90a86854ce069000a6db8db80e +generate_ring_signature a33cc500ffc4a8ff005d7cdd0af7ce24687c2cee205653f4a3ab2bfaab3e885d 5526a2e2199dc9a8ee367e62b66f922c04a518aa9fedce5549dd8882244e41f1 1 0e3bce482f1b1c0a1d192c786798527a2543c55336a982534085544a3a9e61e5 df11554d3e91370502ef0af4271169a151ccee8d773fb222ff7f0a8ab21fbf01 0 88e1a01c9e89c7b7e0586c9c01f676b628c9b798beeaa72f2527d7d283874b0bd430484b077596ac30a227012dce6f66eebbc19b8d5e07f095a494c367004303 +generate_ring_signature b646ecacccb8dc01994acf9d8fdde9a5637d396d98d9a1be23d613995b4141a1 0573298c8d882c3575a6d92ea65c40388338a63622ee405ea99e612e785091a5 7 9f5f6349ce123b0a30ab5100615fb9c760502af5bd01024b414aa8e6ec3291d2 3c2a7d6fd2b179aaf9adf4a6d93288645b9930583239c496b56ee4f80175aedc 6e9c2e60dafdd0bd6b38680b0f8fdfa2aa437e0c5a5b4df59444088f8aaf1a0a 2c0f059f103ca095f95b7c897b8f43c10b8e254c00f44dee68dfbe82069cb42b fcd9395ad709fe0e0cee89865754686a7157bd5b7abb0dbf254d14383ec15806 1c39beb909e371c736bc053821894e515d7a1e3503894c0cce77161656f99999 456d12c6823d3135b44f0af29cb7454bd80c1dae0d95bfd6fded8ecb04a94bbf 234837ca7a4a9c4473d4fb033a6a816bf48f2b631705447c3ca76a2e57b41009 2 dd97d12a4258c3ecd851cae914eeaead6bb0e5b904e5e06c424e699f6e94140fee23bb4603a5ad3bc75b60895892fe85db3faa3da6fb6fd4fea8b9702b2a8006346ea3aa03832b1027b3843a494a4213c902be374fc8619c46d6723620d74c0af51a78319b295c352647b3e3388b9df28dc9822dd90afc7aaf05dfed909c7e05caa1400006e49d4de0646cc43779cea96b1918c20ac0955ba060589500cb250be97ea230e1c53c16a7d2376157f28a0d44f4f3f10af6d655c686f77e09ae4b0e4350c55706487155f0dbfcaded3d867a0483a34a84fba01e8e707f498635b80d15adc1ce74a71b62febac5d48a4ae9ea593ae426f3f3e52b38a681ce5e34cf083880563fd3daab48999affcf5f0b52c8deeebbed576123ea92c91eb6d6b2b708807301bf8a0960b64414fd05d79e7d0a7f5cc197d00961acd603a50f87073d08b9de47691f2802d41c8fcde1f04a43081b9494cba64b382c2c30f663ca41a1004a338440b7592ac8ed7cdc164123036b5e216a06cd3ed724455edb1acb76f10dd30ede86a50639f425f2ceda88f63a79ee99ff28127e34eab324b698587d6900d674fe4655a3d6d61092435e3dd857cb1f13b9cf86e73a3be86e1c036154020b +generate_ring_signature f3a6418b0cc46540ded1aaca40e79d9c4e905fb4ab830f664b169b71ec70ce84 6011345092e5ae114e968f85d1e8b0f77d0a36e59e13934cc56d3388a590521e 4 2db8f2f1ad8248f695f356d4bea173458df3393b2dd1962201a18db6a386795e 7593090195228492646bcee10c4bb544a374409d383b51e4a65dfd6cd7ba6b8e 7cdf8fdf9bfe5a76db674f41cc3cc0ac3ddd27f2c718f8d11c2aeb1438850826 6ba1dffb76f0b5237be198f84e5673a0ccf5510da6661f08a68e0af74bde84f2 135922d821edbecf8600549570415c2e67bd27801386dafec120bcc479b5d800 1 75f32ac2e846b8c7a2a2e2aa6238dfce42f2d2f4645217da69c5130d708a17048e010d4bf4f49085fb9d4a490f89c4aca0e376c8449e8180747d4fb5189c1a0a87492873ae05a5da960f125e2fae14c3f9738f304209d6032ef44b7ce72850084b227515a843e9c03c54982cccda7488b436629273a8bc050f79ae4617b9630051b5d3d6a17dcf010a85b2700bb0d2864bc8dbc2aa0813d14e7fd6d5eb798f07ec4ae038132d693a3c196010ff7f397637292eaa4db18608b5fb236f9123950eecab4d4da3cede799b5b09437fdcf4b69c0736e92e0847ca29446b5c09c9cc0b479943c47d49a2ca12f27a5fd3de0fdebacbaeeef0544a7e3d675ec12bf59c0b +generate_ring_signature 76cea43eb129c1fac6332c3e4e81c027bb363acca98a8cd26c840e79a48e60c1 ce180508109e68586e57664829e97d5e196672abe8ab02a03dee036162778e6d 97 ea5d65f53fe0ed45d3f3ee33cc527df1aefeb42d0201cb9778a38097e92c72de 761bc82d294f654ea86b01e410dbe51948afc3a0cec46d71d14fcfadb97a5d24 8e874f511d2799c3ceb90c4d2daea5d9262a0639bc6ed6e538f93587dd403595 47a1c65c1579551370e8f4e0876c8adda48d32bfe3a734853ef52ad9dc6ef847 5efdb45452cf6fe40a1868939527718fa7313b766ca2f775f831ba89357bff37 fa6978d5f2e8fbf22cdc59635abc4535a5d660b5ebcf4f9a2c6aeffa18a7e42b 1e0a9b9ff6d3522ce4e34e7037b4109462daf17af289a19f0f1f30b8015c96a3 f1bf3e392b4367293e1a989e7f886edb37f5d61e7c2911d994fdb89c4d43cf41 4d0bc6ab47eff3ef204766565b49089fb2d5dd8782bf6fa0d14315231052f6c2 752f38462c1e7aaab2ba876eda8998be437196f1bd2f6a5879169d99598445e1 cc3d43881b4af06d1bd5424d43e0f148c018828f265f81a8d1f4e8e81caf55a4 8855e3918cc364b9cf51580538a61c88e28315c8dddd9242ce522ed299852cd4 67e2c6b9c08fd44ba829925f3cf67fb5b280dd895bfa803495e77d7743bdf383 97294c95c11084df8e138ceba2ae5d470515d3434b1be826d401be0dab311514 5f39c1de4928d3734a13f73141f4d3b4dfbbd0828cbb55e5d35ff7ff0747b504 964627d7a78fc03fffd832b4dce69029e67b2d021400f94e2bccb1dd6477a669 dc5ded860fa59e58bf3a84dff9c422105e58f65111afeb9854013487739f54bc 3c9b181dfd616193005fb2dff907030f4d92fdccc4a75a6439609a26944311c1 ce0b43494bdd7dae9a3742cf89a51d2a8edcb138bb834ab74de0d3b3383f509b 1720e96f5aa4b90da860476d4831f9b74c4935bcd86af51c5b68f8daebb7d70c 8905a2006ac37d08ec419d016058862c93e20ac31b756ea761663723b907b356 059d5873bc676fd2066e3e350a7c5e58df1cc8b71c1e9b2fa7d0489ae3cdf336 44d52494895e7d11b2627b1f767e81911560d50383800057b976c45a40313645 d3247a47dd072d52447d7b83c10d2c5c01d909178e1ee8a3807c8548a136b8c6 a91890be99cd5bcc15581c7bfccba68a6484364285e00d92673f1de161592e10 b10741adc2e5700799eebe1b57702ac98b08873df31b7168eef5dbdd828ee450 38b4eca6cf64f6464f31b241612b2c656a7ec574b9deffb15ff7b67472bc81e7 c730b332086088b051caa7af91e6693a612d11e0bf4bba20efa865779ab9639f ddcfa2794f080a12928214fb77ce98d184a2c4d21cb28d0068f9f59d71d9292e e52f3ff502b5e5698160f36183f690d27d0f39dfa4e7cf92485da7523507c41a c72650a4652195d1cb63c1648e186fbcccde634ce4405277acc2d36f950c7029 ccf0a5aa30cdf9934ce29947cb4a3cb15279dd2bd30c9896e8421fa45bebe3e4 d3ad1672c54f6b60d7ac391fe8a1ecc0377a164b15d0a8bd54e3044dc200b1a1 7be5102c5a6c95a17735349c21cd5def30c5c9841e4636c3342d4142f08262fd 473aa610c158658b82214c29afb690e6f378890d2a737d3704a13cf8ffddc6a2 29ec47b16fe74ff89d050030d5b44f3123c00b8555cc66fd7959d50c275e220b b8785437a7b05007bb149e6493afb2d6ae7b459b2f9be275ba28a3e2de6e028a e99b99f56cfc4a5544819e4a228bb17f7172543cee481b52438a0007f72a54a5 37361693120bd132f0530f796f7f79b8adb3c1ca904f98fb24376fd0c0bf2a90 91e57baf750d2b067b5ade9e2d1fb5091b8cc88b89cdc74927ea41954023bf21 ea45a4f208a7aedb78380ea7b18bdedb1c6376d372f118f49183cc43d036b890 1a47a8117af4dc9bd9f684e1c14671f0889de185a2c351ef588fa45fcfb42ceb 564527b17efb903e14e9ac007171febd54d1c6eeea6f616a41f3684a955bd63b 5d7a08dc96ae8d2d6a04ba481fbe5624a136b0848e29ede7f56a99e46de2d772 c09631fc37cf7c50e4e1faba3331a3afb17537797f1ff27b9766160c0b803aab 5f0907e39b3db573f4789efcc9d3fc07edcfa3ea017236fe190e78cbf45353e1 d7eb3c1ad212d11020aad746e8e5c1848b396b48ae29e818d05374478bfb0883 eb5c508b772868127202a0d246902c7c53ecd250d827d876bbd163ba0c8d034d 4fe7a134634f8f7c58fcd17fa8dc9afc17bb42e86443286f4e7d0516e7a20cb1 5a460daf50320d47b8afd80bf1858dede68cf33b3364726e30079f752b04fd47 5b0333d3e7bf124b214b661c0f81c6e428a8428041933a5bb2bae2f73feb2338 e6fd1351b2eafc19d0e27b38fa5a940d55d35ee13f9ecce2271454200bf03ec5 f333990bb1f39bcab514dbd737daaf5f41f53b0fad8120da35a57b3175c9f75b bb122e489af633e96fc7388b51f0887dfa4ef8e5bddab7f502dfd6141e861527 7c74786e01fc74507a86bfec3bfa805e79ba36b17ac8cd9e0e392a91e28b3806 7e8354f865ad563495b31e605ea866e8daa870ec89c23ec47dad0f2b8335996f 2100fc01bbcfd2ffec9a170b44c21d656358bd95641a8e5f8f08fe7c90249671 44faaa073ebc6f2eab16334324b979b699cdcf5d80a1fd4edf75a11bcd98f6ef 76ab8624383338429b22dd5325ac9643dc55871f4f886102082f58a1c4b5dd10 acc0f182c5c07ee80b62fbebf781e119ac8adf001e17b9e1b190bd4b60531f8a 1f5315cd2a745679cca20e8bb97ee7f038e79a78f8ee05bb58d491ec5ab3645e 99d1a7aa22b5406a148b786053d90ef7d2cea0a3eda87ad31a9876155a5bd069 e077a4b071bdf882fc245f87eaecbc48c7dd12db6cddbb615e402b9d5522898c 3deeddf806142ba96d186487d9b4fd01adc27abc0cdff0488d430bf3183fe755 7d703ac969a79a9e3f8cf78d4fab39e5ba90982c85b8ddd2c3880eea3a8adbed f56e8a30c3a9d72a37e2cb73003916d53a2413e6828755bbb7533a5570940a45 8c706f2a9c58aef3cfe9bcbcc4e2001fc4ea39e20e26d5295703147e6f840e31 202581aa0dfeef8fc991f81b0b5aa1242bd58bf60fe9c30c352434a84891a1c7 161bf71374b025f7809ef91bc17d17b2f9591253c6af9cc8beb6e554c9993360 9e95b9fcc98dd2efe484ca6eed077e756c9007ca904577231a5c27840fa8f322 6e903532e6d30cfbe1f229f2abf442395fd50c70a12accb4530f284ac0678b70 05d9686ba380bef9da1e1ef11f99619fd10ae49b54145aa69276ecff74058fbe 21c5e602a95958df1af5744c876ac34d88c706cd414675716d520defc907a4bb b1e171e1bd44c4654c3b146ff0cef80d02d7296e22e47ff408559133b9dc809e c186bb9fb192b42d85a831e61fe98b8842b843ffd9813318d7b343a6f085af80 b78767a2f6d410ef77eec224d248dd3a3996416841f20f3dea1b118372569b70 ab70e6a84c798836f735736938723a7f72b4f40825e1e7447b54b69e9b968272 65272fbfb808f5268d2f00b769b9502a316248ae7d24a995f4aa48f180534969 5af13b319e0770e198ad686374fe46d80dff616d14a40be69306b9aab48693e1 f34068ae26b56e75d7fb78b4610964327df38bc2023ff41654c7fc260d15ff2d c3c69a0d945da564ed91fa26fc3187531bc3f4e060e1e52ee7a328d37ee34542 d56db5a940e94d4d743d84b11088e8f3a7aaf43481eaa5526751b541a7435288 9ec0631eb6101d11c780a2c45e07baf4e034f9c10c226c1a7b60976fcdea4c60 22e5db32f538b7dea4bf5a83f58c9bbe673f89d22b0a45ffab330c8eeb3a8c69 0356d2716c4dd590f5de57e0bec44227116c96fc586b3b5d92c0c995ff4e1902 1f1057b4e818e332d5bd2beb436501b61bd3af4162fa17b6b1570f455f0b5406 7c3c27354dacd9b31bd72d7e7087e7f8ea8c5a3471c7cd44e9195d5d362a8c74 76db665efd422df3a687ca5eb9356eacd21e87b8cd351878f442d828e94f1fc1 4a268e9468d9fafd2f1be4c9c3f8de133b3fac53138187296f7f33aeec83648e c7a208ce9094ac3214072575f9580c5a6d94c0591fb27a0291dd4012ddd204c0 bba051684d0977f02c94ac13ec752fea6c4f3abddf18de66a9d453ef91b1d978 19dde296368fe4a5963f9b48a8135a0b7ae68c2df7bd2abfad0cacbe684498c9 9fc0b393ee34285369ed68faf9431d9b0131c49b839cf2f5c46c04321e17678d 5487203a98bca6cb55d5ee080c40ee427df8125ff4f487c1eee1e20e6e87956a 3606d944f0eb2136e5c828f2676a8dbab4c17255e509bcf39232ed6e2374c3db b949f3d97d5a080a4838552d4bf82259df8802b9a3198fe65311868cacd2de28 7c553a1fe0be6e578db32c86dc5028c8b58345d880957b2d04370c19050b0068 c90e5fb2d096fbb2ac10ca5a7dd398ee03d40ffa1dbeef82e24b409b6bafd107 44 582eb159bccbecc8332528b4520af84d71fe712959f2974beeccd0d9d8c202024644b43108dc97100d8a9e0d47a2c616a99a7e26206bd576fa6904821b306603691457810b69699b4ccd3e84d30c3920f498322dea1785a95f92b971de5b2a0573c1cb98c4c044986f8bc9d54dd616421a166da6b9cefea591968d8822419a0058ca640876ad6ebd8d897fba0f145468901609a640be20dfb0c9b3d52d5c59026436a360d0651bcfc68d14d42c527fd9a06b6a52ae3220ea79392bf7500f1d07e0afa5d731ff1e30490c0e0c432ed6fb7985fdd9fa9fff3610ab22ce63596e04246c8e00774ff08b625e51a894304f0f0ac55c1b35d8cdf63d05756542c5ac0e83013e1e811729d4b231eea79e8bb2f8b669952670e2d617950b3785f8dbc60bad56e8c30e471e6244f24d1320769a2914522368347a06286f7c0c5636438101b6307657fd65e1690b082192a0413e99443cde808c5ad6152e8d13724e224a0b7dc2a8fadb407a1e53843298252a47c31247370656fbadc9850d51306637ce0030c28e0c51fe8189888084d991426cd20ea2fe57f92a703f0118e8a8eabd3f07d7c78fb1822f2c0d538f0614c9102d7a1323f381c21cb871457377aea0eb6b0a495db2cb351311f23a2839ad436214208a21b56895e54389d8d90ecc1a2ca40778dd5d97203a89d0362e001bac8d7f374c2fbb3b5fcbd7c3cd0a9dbe77f0740d6ba5973d81861c8866b7fdd244460eff2122d8931fbb2e9ad8c95de04a64b5095eb799f48b4c1a95498c26822cd4c690574569d9e2821238e8c7c5c9e73746045b01e81db6aafe563f7228285623bbe190c3e12fd7273598663ca300e915ff04d1fdb39f160a60b5e8f06dcbd787e4e375bd1dd92cf9667cf244316df81e27025dd1f6f711509e61e9cdae0d27b791f0b7123ffc6b0f5f888506e60f19245a07eda27c94ae62a332a5bb5212b0fb40d252b60a9b562b63183533c29679d5890cf3d1cfa01bfbf251fa8e0316a3d9ae70e0b1fd970097ddacffdd307e978fac02abf37720598cd5bc0dfbbbab98e0b8260d25e2d8d389a32923d2a023d424440ea2d0feb573a2f600f28e6ac47ce0dd05f9db5e303913bd52e8fa95839baed2037597d778f0939a9b7a5d92f2361aa7eafe870493e059f4d405498889d3335006379dd3e0069f6411e87f4bce1f867789efb501a321a93dd89931074c4e9ac1079282e4df4a2cfec276390dac297efa5a9ebab5486bd20daef7136f3d24eaea04c19f710645bb1a9fed6eff3a36b5508dc90f5e1df6b181b021bbbbd71db6f404b5340bf4f2aa714c0c563217b9245e605dab20470d0faa473cdca9b85107c10ea0119ecbff310da10b3c166f077b568b5678111e59cc4f346a503906212111014419d27881673994835d131c06a63cd422dcd0c39fec821946e2e050fac920016f4df082b33c586fb38da1ca2a42a7367b0379f05ad12544be3d80f13ea020076eeb6736cc0296ac915aa04622ca0723e9a14fc5bcbd0fab8c872754e9e5de0fc3766b5161ae29015041290c848cc6d05dd0ce0c1a6f9c9497d2e64777ca3c0dee4033d093b2b9a302264cb9f3b5d2d0d33698e92f83ec687bf4a60800914801f37c838a9c0f16375bff1c0b42593b291350a5e08d9cfbf50b151efcc650fc0dd6b43a3cb00f0968e38f2d60cabc0548f22888c4582c08b0bd684065cc0f61023c11cc8013af8b87c976973ccbed9fcd4144526adb4687c54ce47cd339a7ee01adaf0764ffbfd568653918465db8d1374d7e58ecb1b3884f00443eb116fb3907b1c58f2f56f11875d38ac248c425fd4434067bf521e98804402a58fe463ad308f9e707d4ec0845ea27fecd59c2f7915debb858ea5bf0b03058eb401e400e8d03ddefd70356602204cb81b31ed6183d086d6a7033c52d890c026b51f31a30f00a132f2dd73434a877a18836e9108be75043a322e439f7c6e8304b36242a7d6101029553327772cc10d5e500a6f958f97a9eaa71ac07b031bdbfa58c779d2cd70f0013da017e61ef7d217eb407d386f6944b6343846199467dec9653d5504b110cce737b0243f6a78b2eb07b34d1c04d274897929f0b03f4102298c1ff2555bd0f0d48581ff16b617c70d25492b36384de77ce64dafde048eac49dae8cd692e4014a8fe743c6b194b30fadb4a9f86c2e423212f481facb3f954ca993d24d449a09fa2512bf1af06b2127b8dce0d4f4f8c9a0352483c7bc7f4c43f16b7cc234c8005fa60227ea2f3a6fa17fcaf6ac912c284a982dd2b1c10b8812d68886f70f1b0eb0917c3b879a7741dc3b0bdfab218e774e59856e85c899db51e3e5da3fc2310533a46937e7347d10e0072fc1675bf81dd0054057415bc024fde909571b90c4087a7edb9ca7b4ec122a3f4c514555b4486845cfe4aee79215a7d2700ba18beb03b76c7803e3ecea3238770419f97d80b8bd7113fe845f6194291f682e37a1dc056f604315fd9ea558d58c2037ac99c308ad4a3bbcb43d5eb357eabbe2468df90f7855803d50255c7722421ffef6214f4dab1f29d487e9ce584715fe2be34eb40a20a488ddb093bd3ebaab1890863d2214d5de911a2edf242690d298a060a2bc04bbf83cb61a541598df81c177f59490bf14efd25abcec89701bc32098e4cfa0025bcdc8e8fa2d59004eda679225544c2865346cb623c5da50e42fffdde0f2580d1701ac4ab76b4d1392af4f54b55c2ba585b7b35b92c22055a05f20a2d4e7e103a9864593cdad36898db101168c5bbb10a6c865217eb5b3e2363753098f9d4b0cbb1dbacae892679ee38582da093baf1b0a2238f2bc59d4899380c322a14dac00e240513dfa273e97f215c7ca4dedffb3b526496148672a70498d973e22c6aa014976cfc87bfd136dffd4f22135c3095bc07f54a6b192e27b8fd4f37e2b227102db03e27d035784b8f678a5eca6fbdf526ea2d1b547ca26a39c8dff51949bef0b1f690ae18a652318beb584ad09d573e8b96ed1df04a0be3ab7de3a4fba31440afeb8a3582689fbb5397faa8dd8470deb0c950630ea6c1cd4f37fb51c80c2bf06d31acb716d6943ffc12c8240baa47b00305ff00bdc42fa95ecbf9758ee08d00f006443e69517f9f1f63e1732ac8965ca9858a4b7d1f1fa375c435f9e59f745018b41f23665600a89cce7c54d2f0c491cdf9e2fa07dd65d376be3a917ab5d5509e176d94a11cda96b751ce2f89f28924846ac0adf2d0db9d4875ce2ba19b76a0693c4f037fa65e50b8ad4f1274bdc6866307e075bb423dff1f334f070fc23b5020456aa93c6e5474921fe4756a527cad37b3d860276a1e6d4eba66258b6616403f6991d6d211ca56cd79b21f4d1df68462835305e24339f8f34eb5096a7e7b101c6ae8d864f9c363d2f1096cb974ff57bd79058687346e563016d20ac24c4730c90761a375961e925fc129405fb6dab75092d93494697dd2537a61dd3d1f88704a4177bef7378c0694c816997d23f23a74905031eb1ec35260618539e63a4db01cb48b5429b098b2955ab5efb37296f1471170f9eb7d9a591ba9727f8cad1cf0070f54b5b7f36f38d95acef30859f7ccccfee3ff91fd6eba3dca73bbded16a50cac49a4851e13f7c666c6ba514619eb351ab6722b5723897174adbe201ff5350641d07619fed76e5689a0d953fcdf325396980ea911d1e2025b7454d07fa0d90f9ee3e1bc184167ee87adaf809b9b1be3c1d1dfee5f056391150a687bded3000598c01f4ab92d99275feedd27da3ad8a306fa23f98b67e36bd5ac20ed2e72170e83033e72bd47eb011f9199f7c0fab0188e0a28ad2813299824ad6bd38be6840f05702f288b317a475d8fac21fe6e36be3fd56bc8ad579a31c48391edcf67770f466cd500fe8068ac365e20d5673b465efa2a2b293b11b7f169f96e646a83610a7bdf1cf0f29d5bb681f55bef1dd5f2c7ec22baada3b75f537fcc6655f09a2e044306d8fede11ab113985470498ac112719048269f893c5e2bceff7f6ad18070404820943bf423d27edb9074bc789747ceadba6c0201b32f8c6bf678fc43a7a008a30313269fc5956ac678c61b6609d4558c20a4307e2b420fb22e888d6631a0353d60f51e6092e33ffeb29e1c5526f93715ad1fad20918d68b05dc492cd1af0df75d81ceca40dd0b0566f4197711203401a4313a9cbf8ba0875b75719844260d28e6f8a0e4c1a5419f1440783d79fcacff23506fd46766a3a6aa77d72426e00872aea4ff611fe945f1be3a9d9264c12bd5640062fcff09ff437fa4f86db9190c084aa24575951ad45ac1f632a3551a95c220b57e68ecd89469314bee4c89a405251559aa47cc3cbc9719e514d2cc8f0cb3495215ffabaef93353b5e55fc4dd07d67e89dc185df51c24635929762da359e29a41204d70bef25effd127adda9607878c6329826ca6557953497f52596803a81f1d299068c9310612db21401e450b1ae5c9826370584e72569a29f26ff75e6f1c69078b650087f416b1fded37270a5fceb89bc84a17dd15ceff23825c97c48cddbe23e95619f9ad218ae37a5ad201f1e0f1aea3c0561b90a43debf848b76dea3093d9ba746f218d16c7aa7a37040e4e6e3e6a43ae54b2b939f00fa9a692d486341ca88f5864737499f394b431f50de73884629692f2ce962c095c935d010fd181304672aab80bdf423fe762a3f50a58603d98524f65241381ab612fc3cab3e5461b866474d0eebd22fbf3f7ceaa05741f55fcd81713d78ffd866783fce5866a55b794c60a0e5d19ff1b738c17a70655b94b75f5150398787379e6d5acc07ac8085d1d9f637ee1f9617e738bc8540d0e9959856d412127d00133066c188f12f617ddca67498378845157613d1ee60ca1e9e82d5302ba940084b44abe06843ba2f472637b6233b97ddadf79cc0dab0517f1197dcec84f7b169e32a743a1c40475005504ca5b658a74a37228bdb3f2026a241b4e740d1534dcddce4c2f73cb4d0f961241afe21de28e7a60ac905cce00b835da70e4898c90d5e6689326971bb8ade1a0109bab818662ee2bedfda9590f937a58a8ecccb933e144001644585f0d775f82fde69e3a8ca54abd21fa661f0bc5df74ad14f002d7f00b9d2fa65889d5e8e96d3d7b1704ddbe6492c66dc0370e0603ef2fae7dfb6ff80b47ac64bf001d49570dbd5de5e72d46286ee995745d0458d7dfae2c5066f695682706c44fb98ee2a1107ba66b6b9471947a9eefb1e6028f83c7f9329270510c9e1b36722d444e1926bfec7760de3c4896f30fa0bb1102f7cc5cc9920303539d3f308717624fa2334201e854cdb07514a593a1d1769e0e5a732094e6313b364ca12a0eb4bc871f27a8f528c5ec316368979d9a4149140363c3d5e3151469910c19a55b9c543a80dad4988b8809bea298aead8973d9660d0c5de3f620e7a8c7c7e6639429627d4bbdc9be3a1edeb6ba3060d85b8308aa01140b0bd3d0262aa0821c63f9f99050d7834283d3f5641ccdf631df8008fd7d0875dcc5de34b011bbe227429f3e0694e6046974eaed923ebd289bc6b96646f10841b93e1a0b6eb569ca0922516053473ccd1c9db8ce0b12829f459da8f705a608c3dfc4cb87db34b422f20696a9b68b5428231ddf19ca4256a7e12c648ea8f708c002c0a3402089f1eb9c54f7fe7524d8e2c92cfa2729d981cbfd3c94e1f827067189a3200c2184db8074738317e0553ee0a18086cf9f57ecac5c7df236564a0072555b76297e4fe6e3f3dc3b20187728297fb3eff2a7d2d90d56cf906ac4460b6500993c4dc6eb8d89391993b17a19f267a47bb4e3bb3cd60aba6385c40a7000da60b0fc4c5ecec1e357a7308c9c351a610c1d4baf0d287321749ca676cbf106591d858eb047a829e24265a4bdd91d7117c1cbcad84a703080a1b98e16fee007afeeb721e056a6c6b915dc6269f87ef8a4c43bb5fe648ca2d80c6280ebe6660d2363ba6681d8c8769fc95eceeaa0835da81cfbf488130133e9f65238f462af0bfacc61a645bb74fb5c455b169132a32417a4ae719ab5cd77303619a232dd5a0ed8f97f1d6fa1b89da37b869025817f9eee1bb3e698f45d52df64725643b2dc0f2184a5b13eb3c14efcbd12af38d5af228f66f2c68a40d373e06b8cfba9e3690e7e3573e06740fe02ea780b2a79f131991522f7aed41c05c54e50d2d7a39b57042277e31adb604edfe3b86ad78a4ae6e918d4edc8cee2c460e064668339b21a0a54bb94a9d550447fcbeffbb6bac3e32bb72adcdf57708819b62321e8ede5a4000d8f89bc34181d4e10cdb9279eb0d6731690099f794d6c449cdba7a3f84ee400bcc7500f256243cb7656ec25e22eb82bf6387b502737034d624d261450341202d5f8a6a4fb679023e0ac6edfd8ba6fb223ca0aefe16de051be25b3667885d60af573a2144f091fc51efac03de9031f54a6ddec4bca27cedc0a4b9614578b690f5cb7cd2c83cfa2cff2dc17423ad782d9ed68ec068ed2a3ebe7a3ec7fa84c6f09113ca3d47bd643a5f4dde16c7e47f7ad50fbfd55d331865a50819da741733e0c6b967b57bae10dc257610b9c9975bab2c3e2336a9698c0d5d9a7a2b50311740181094f6a8f0e4f1bb99f36c911de1628be8d0a24394808d78ccaa5d7e83ab1047f05c0f4b2e93ac267f08e4dabe904a9f62a0073fab59dc25c9b27a66e7f6f07e9c4e27a820367180757b7eac6a90c2ce30f593654cb1a37ad13964f24a80701f02050a8d067d94d55bc1af5516b6b3b40614006460be36a1895bf5e5a53610be95f17ec71513e40d5541b9585fc548d89697e5b17de79463cc34f8b60c0850e61f3410173706636db48eb65890a78ea1567576a8b15254566d297bdc6784800e6d28b9d92b5a74948b9f7ea88cf1d66f78a7cd017289c7eb5c1c75a5dc88500a209e38625dd7c48e0516658d6b9970584e19ed650ff5dc83aee264297a3e804fc1f9db536407e2889f1daf8218793da01f5c62ee481616d1bf8cab525950d09c1cfff9b56683e74082f3406581d4c6c193da38fac64a3acb23b17566221cc0fc0cde603e88f487f55ef526a7a0fb7e3ef154e0988f2dbc4327565e74d0d020b8acc8efe8f24770c908f3bb6c1977ad14e810b7f5094812b6c6ddc7eed4c2e03cdd676e0d9efedf55287bf40d9a5dac323d6e06309dfbfdda7a487c98cdd800d1805c9f9cc26da6891eba7cd6ff3989deedd3e7187128b3149b127bc3cf95b0eb111eb46648daf491b676ad92cbecfdeb9cad76abd56f37c2afc1fc3e6a12b02f559f7cb37d0d7e62b67ee4e25b411643a6178f21e8f327eed2040e013bdcc09111a7939c2a5f381b35f0ce0d96b935f84dc39c8a401cf7c0551dba2ce7acd0968f4e111d876972a27ce6896d67defb9a5c027e225c50cd1637c6e90b4898001d1c29034bf2bb4c428d65348847f60e06353575449e9b4697f38d0ac8d03330bb474ba1458fd22e8f4c966c25dbc58394eb68bae656cf26aff1d970030df230b268081ffb1ec8ba260b8f7496c3679bc601c8de608f1728c228f15f34501360616e4c871747de971585afb5920e08cd10a07f01502b026fcbce51e47864b110fb94341b248c59cb5a4de095e92f9fa96ae05c7ce56125850e5afae342be7b208d835834df82cde708a30ef760ea07f9b936adf9bdbe029ca00b58601b1c9de08a907bd208ba839bad5a6a0a7bfb2fbdb7ea762b2dbffcb5f3c612232e47a8107e45989d6a65aa8c357abc4e4df0a5b336099be6428befc8c7bf66f0f66a63203641f4c919b9646fd710c00ca449ddec471955790730ba748f5431aae5d28b5080d9ac17a3bcae5681dc46f9bf04e825a75094d55165c84abe24bfae42a9de60d4b71980d71f4810fa0920c21acd37369183967ec611e196bb04038c3477cbf00c503ce3a3f6bccd83bd7aa3d3f497ce455871169b48a8a3955e046d281ecd10d3a4e623927c06d7317148f36af9479a8cf5ef506752f9468c2e9fe5db71507075d78e84166866e5bbc6a6c27003f3e480accb63a4274dca790092f4e11837d08519f1e5c7b77b31f1c956eccfe3ba7b9cf8755639fc497f8395bbddd75614807b0fab88f7f14488a8bcb5133b37d3416ea9a3b998c4576506102d5c3d1515602578495f7681be9273f470ffc8b10c95ca78abae338140dc258572c04d17e8c0b5d89458db5f279e9bcbb31b0d9a294a030dda25890ead7f0e20f634a8c0fb90a6dc15a9b7e68b9f64a5637d78b302b9a0fed8afea88acffd854b6452872e37093b5a5708cfbf5a93ca42284510358f764d5077da5b924bb062433a405bffb009e2560781b90f309ccdabd4ffd6df44af462432e2749ce37856dc27e28b938d097e836c35cd54d74bef256912b2a0fba2a4390011d482a56050a133089d45f10f949dd49871df5ec056e8e08f31c59e86789ddf407951f0df4db3f22deb4b950bc163305c5a071b670fe8b19779207fe49fa9fe79e695b6b3c262b8dec1b1cc06a3b33cffb23b4995047590388ebd11f4fead7d6a81bd2465b2a7621e4b3e290858c509386d6104b3eade816464f7b665c8b0a2848c18c4b1110fbb09fe8f620b6ccf170bd373d7f295c9be831cf6938b3d552f09e2cfe7d2dd7289b9d584930c7dacba8fb3f686e340e16093db144118e951bc04fda6bad869dc670d7bed670ad1c3556015eb7a56a2eaf745ead766851544fe54200c067613f485db78fd5104d069785183759f9f68dc1e93adb3a83831bfa829758d0b51c1aaaaf421e6c800 +generate_ring_signature 71a47069c20971db2d8647f3d5ca8307957824c6b42f9061fb36d7eef65194a6 b1d9ac17a3f96e4d8cf71d7897454d5d7be81a951db7a0419eea94dc5f711d97 43 335327a0b707f8624998eb4b303a03170fa651c3a4577b33286b05a6746cc9ac 57810da9dd86265f297e8a160d99bb7dcd6b43fe16fb3b09f69ca1998cc6b6ae cb4bcf9dda4a69631d56c6607fdc0f84ac534cb62df34c88d5eff2118c625469 bdd4ef4abfada7e2252244f3c0bb9b694583d403350fd6116de391ef91995515 a6cf85efc3beac929936d9e71667c8c6938dc33996d2a03c34c686760938565a 5e039597db038f3d5db155152293e65c0bc47419b66172897a1880f376fab284 bf6c948e98a4b1894a5f4252bcebf16bf0ee3f7fd5f0adae7a3bf48e629304bc 5a30774edb0e5e3e1fbf1d6c36dbb09cf2c17a2fca53ae0c301dcf2500e71582 0ddc4b70dba024f0079d78759b4281b3d229e27f014ef2ca41fecd21c62d7696 cecade9fe7cc9fc544bd446eebbc3d04fb038271d7091c98b735dcc4f9a2522d 525c7eda152cc15dde08755e88d9d19c4581914e26a9180a74c81097fe92c57d b67055be665cfa59deccd7d2194f8dfcd8be3e7cbddb1df4bbd22387c9f7420b 118e165261d12f5288a3d080bb24b544fc9662be1ea50c2e79b8b15222248447 46e309a61363ba9fb69d7a22df88c15cdf7cd827d47ce39497226edab4b669be 9ddd9cd54928fd6027c15af64276b332177f488740d28ff2f4e86b2bbca453dd aa3354c6f9b6451adc387bb270a85f67c154f8b9b061befe7175c08c91266abe 22dea64f2164638bcf4ae4ee98fd728e5b4fcddf540e4141e7316ddc3bd6a148 bb702de52e0ac897113f911cc50475ff9f898e59d233a4f8401d08ea1e5df4aa 2d337eb466c72a096ab0ff741da8309d22ba59e8183e906f82e4c41d5570a9be 90fe8d9f6030c838f8c1c265c14b59160057c5b2bc66177c328a38f2d2529f67 c0b2112f5a88e20b4895482d0fd765268123217b4c539957b813bf3ee378c603 f9d05c493c5bf997c755d7887d0da2b8b78203ba1abb6543c5f3717806c4674e 2c1560ff2bce7f94bf039384b2531aa84dc70217cd1afadeb5a02a4fda99c288 02992f1992a329a73669f30badac429d155239cd8e069023a1a1988122fe1833 b51c6107d4c1e55abb9d8a25fcf40c7bc444ed5d1aca99744f6d21e74d073e66 805071807c1e6802f4b83c26a38492290dc351183c3890b5103850ab287ebcd7 4ac406f98da328cb0ebfa67a7c01ff8a6c50a96c9fae47b7d28d8ffd33ec0719 6831fd5d12620b442f871eccd084c939e835faea6cb572840c3f311fcb19b6ce 5a2da5f6bcce814b30d955f3c817e94a503c489701dbc27f646577b713f24ac3 425744e053d435c3962d4152594ebaf50311671e95172b682391fbabd0faba0f 350c29b84efdc243f42d2a39c6f01889596d1d3bfe5cd0ba397956670c84b9c7 45927e77546a754d89839e1c274096715d0dd70ed89f9f1d0162653aa39a476f 31bbc5c02e89feff86c2f0a7f8f3c7b6784dac632c199edd5c630e239595f7c8 b4e362ebe742e61f9a1bcfaa7743f403eee9f94bcc20359beb603b8a1a150b4f 62742fed2b661bd628dcc02dbfc927f8854fdb4cd2eaa51830ba7d0254576064 caab7cb70e2bbbc5a73de3214a46311186dd91cb51ff014ae4a5a77e3709f4bc 08be0befd88bf18275b0c5da494664cc3ac4fff24551caa77f0ecc3874a5adfc 381689782fc176c59589c191040082b4d30a4e02b044dee9a64c476a57ec031a e9c3e48e31b42c12a0e9b87eba2a36cf27e3fcae2f0e39befe6203411c29c179 4303999fdcef50f749cd7f92107e2d649ae196c4df0d22e9b4eb14bd9004b22d 6b8ecb74808f6fa187eaf82fbaabd1ef5ae37c8628703cd39dd68c3f9ed800e0 5dadbccd1db2512a03d532506edd99fbb34e95d62d84bd64f697b6115a5f48db 137288f02739b964dfe357f072ffddb16a56e26174cd7c8a69f90ec11d5342d3 1d88773d7f467936e71dab8afd9f0aa7222d20f7500375d9da697840627ca30c 22 8026591b1292ac1afaf32fd29d60f028652c456d4e30477440523e8fce5efa0ac30f79fbce798fea1dc540b6fe4b0a2dc3d38a157ef03dda79d28ec0a20b3c0f8ec9c9a5d4c7c4dbd83de23ad18d752b2bcd12f824efd92d54bce17e6390c20089879304fbb10861937e5c6731abfcab7cdbae714bb48a041162f2b73ab67b02c51f8028cfc25ebebf43abe03bde06a457dbe07ac61e581f1148287e1bef4b02ddf3fb60a6df8c58ee3bc0ac0d985a6bf2ef5f04ee0b1e489fd3cab3c1ed0a07f8a1a31286e0b30759c1867d727efa6f40c85f536d5323ae8b2b871376f1130b6d6d77fdc8c6fa7c7824058a7e02d1194df850b59754c1a36c3b55716b6a9b03130297fbd851477853b33c1c70b94e15a4b93384e7407d7db8e781885b1fe3059ad3e9ac891316a685b1e63f8c27142df8754c58bef4b21726d770b1dbf6e005b0fa8a034e4fe780c1fde4374d6ea8b3d24f2c3d0c1d7ebbcd0bed9f4079dd06090be40496e6d6c78f77cace756ecd533fdced5957da5c80155366eb1c08c201ac06a24a8910d6c21bea0cc8997056958e85a8d6d1b0b6b711cf1ca00a83440598d346828c19974907d627f84870997aaba379a3ca5befa7e3729699f2e1730ad2e3b9810e3a412eb0433e9a3ce27ad27f8cb103478e711edd4277d010efa802f7e79c0bc67464de0bd04c2ac3b9257b0674565b1ff7ae61ebc891438543880d53a5bd635b0b921a5ec28870137a2b8b903a152f9729486949816ffe91cc8b04161730411c071c07f168c170a99b311a67ce4ba4764e126b8594452036c9ff0c3cd12325e10d2cc6af1a6b143f1a383ec5c96c61c5b45498058af21e0195bc00d416a2998b57fd49daf20c0c8960938f8b578de34c9ea869309260d6ac356a03ef953057330502317a96bd6f68e763d71478501ee3cc5b2452ce513a1ab2cf00c96aa69bf8a6741496eefc544ae129281e76632ef2d811cbea506a57cf050d042407f995fd8e6bd38ff7fc3273ef542aafe98186d96706adaf7d086eefced30b96d413ebafcfef4759a736b761a0834a1a2faacf81d2e30c0c124c087fc22f0988a80e63baa58ce10ea08f51863145ae011aac911df16ac4ffcf440555fb5e032339abc3ea0f3bbea5e209ce8de582500006cce1c5cb252597dba11b83f3e90ca640fdd18b67c1922cdbd38e51702de91bcab2f5daf1d6fd0beccae4a179f00a5035e905ea176cdb717e311d974e7a9788352d6403fb052ed7da614c145f150675c6b22bdb5a272daa3732d65b74acfe444339750bd2f04e80a35fea994d840c6230473e03115a4a4a2bb36f1e5ea33f459d5db1ad14ff2aa0bf4d26c788c905dc48b14d14c10714081b1d66a57b3a597353b9bb8c66e1d43b65b60ab7be5201767dd16f12a018fc04ff1ddae3859aa9f25c0acb2e18b0a3dfbcee3c17fdf60e87b8382cef601bff5fb4589c1010d609c218a4331bb38896ca1795600b37db0a0f8a6d36edac3712b2f7370d0726cf6bab6164cc3be4ecdf56e99d4a46123e0b734c25d033681e89925651bfed2269b84e68c08efde540a8a01ce652f838d00c96394079feff44bccb18ad801cf78948bf0afe946c76d2ebdef3bd7251341d0ba07d6171db1ed62742cadbac95ea9eb126fc3a588d17d61b5b069d1ce04af205026ac43b610aeaee77f217a7bdd161bb5a1a48d01ad769d83b11370b4f06580c991d60d7688b955b06df0da3ea5cc612bfb0d088b235cc8c0686919cac6c5c069ce3105233e0a671ddc498c84ddd62190a0f8daec38c2dee0c617b86d8097b06554e746217f329b39de45109cf5c2e110db741de3a742e3b14f0ea9138217f0d86a7649773e3a27191a6c6a2e8a8f8c9c647789cd94c4ea3b9321a8e4b255c0a398da8c66acca7aca94819693d97eac740b8655fbc5414f85fbc579d67b8af0ab569234a9d4b58d2c912c78d51d131b9c0e09086511c498f510f6bf1a2bc8c095a53821895d511dd5b8791ba62ff5597d09ae34d340a3d6451dc8b337371cb047d7412aad887375e25bb5949b0ad298fe3e76b7707171bce03bdcae6835cd50b3138bd3cdca68eaa2bc4ac9f8dc1054196c751d1cd5ead2b22811103099b28032640e1cc9c20c96e12aaeadcaaac7555c435aee9bd5bf9cb29adbda470e1df0f9a929be0844c90218ec6ab537b16c7fd9b81f909a1ed338aac7fcaefbeba00046c2095ddcdc0fedbe1e387fcdd71bdb9e3bb742e4ce8e2328cf0465377a371036b2ec4be16bb2e1c7e8efd2059c36b5309d33c693a5ed543aeeb027db62f2b07c2d4dbc068f16af3bebe0fdfa2d730a13a91ec13d7d8041bfbce12ae21f6ed0d28de28c73c117eb5464f14acd14504a1d4474aa3392ecf3a18a57220c159e107efd2739361f2418069caf7b6ca49ab2b32dc12095b5e11d3a62542451989ef0de9d9e0903df6cfd0b3076692d480977a35fbf4df0c25bf9ab6371c7f2aa0630c095f07a156b3acc9f6bea01741305c67eb8114ac86a4f64e406150a9c5b3590f4fd90bfff3f4d96241cb1ea4768ac402c4a6d4b51f3875439e661407b2709b031ae77b48b6abf42ecab94e589ab1fbe88b7df09f6123585af32439b2c8480a0def1056d2148cf4ced9ca4b1e1b51d8691f356b5313aef85a3cd3aec7fcfefd02459363f1de4fb7eccec2d90681172531118bb3e08987b37b824bc209076ed502cc935f131c10a1da6a25994e9788033a63d5dcab0ebe74d8928bdb30e0518a02e9f3a9030e435d96b03cad0d5cafbc552bc15cace574841c97d3164123cb85026b4f0aab073722621dcf7aba0324c5c179429de6b62bfce8bac827868875030e6e9ac195b2d4e56653ba2e4f84d2544a0d715120541f6b273d62ae5964612301a005ae8a3f8fdcb27071543872424853ec23f82607a269a1bcc5f663994c2b028a0259738cd520368a7847c26643beba3da24f268867cf585008c35952256000476ec977ea3603ad0f85544333df1ead2dcc8631939e72df48ca0c447cf1dc0ad57897bef03034232eeea944a00a277629e89eb4a95221e21e4cf6b47a42c8031cfaea1c8680f0b423b35d725fba54b743ab2247d8e70ae2f10ca16e424eca076b7e30ec40596db135c3bcf5858f0e4989c154e5ca4beb5dcb3908688ad9e80463175f17f036cc30a6e31f2672ab4156816584041446ff0eaeaf88142028930e557c81f62adbb493f765fe78b887c6568dcab3ba63da440f28dd79808ea0a50b11a04d8e55dc87f7a4b9afbb0a7cb3ba8abf294b1ded2f52cf7854811cc68102e289fa6e4912bda41d5ec0a2e29dc9d99e297111a3854091c267f18e9f7bcb048357d12463579bea22fa534d024f4c514fbbc249db0f38d78083335bbdfcdd02976d1ebbcdf44ca2ecd5ea2210589d2a0939dab885f879252f58fea5017e1a0c4ba0259c35123c276c079ab36a7bc22af60706a5a269a9fbd7ef8fa9474f95011f746c8b0839edd645c067bae80ab05647612c0af5b37f9b5eecbee5ff0d5a077ea3e00474ee358e0ef7ce6c5b60bc895fbf341c549e34c9acac955510000606727da8cc7bfef228d87ffaedd2af1c97a2f787c595cd0d8e4f9626bf2ae5060252537d9e883a8430576b9ddf791ca41e435280ee90de896fb06aa1a30de3b30867485f431518b4b36afb0550590864ad6d2b4af6e74a4093c6e29076b5fb620f5e6e2ddc04c0382911a05158472f23fd39be251116a867c340ffc8f48044940c8aa7696454b255064bb18dcbd513bbbefa87d4dada25d85d2d6518ad3346ec00be979d397b6351352b3818a54b1f9803ba307b0047939b9ca126337a6f385903072a25e1d50ad4f90f23fd6d0174490784b1f02f272a94f195849107c16f9d08 +generate_ring_signature 101a2645bdc21ca8cc437243b1c54f5a19c564db39f80ea5ee2740b0f5fb54d1 2e54166e18e7d2192f8cbbb04bb80b44ad6398639aa862b607e9d6b39dccc25b 51 b942284e63e5f2b780280a87703e4f7036f6a270caffd098cad25ec62bb34b00 4a4356b0f038b6ec50c8ed2fa53b84d845986b8dbe80519d07f384720beff960 18c61d9d41c62a4ab9eb2bef5d37906d6388aea9a62001acbc10653c96cdf430 311b4b24888b360c36b5c1a1129db3f3a8aaa3a8d82b787407f866a125572c62 077fd8a9f448f934b8f26a8fc8334fc5ffb001fa56294a12caea1c202ced5279 40cc24c4a89dc8270f49ae2d18e842400b281efb9d0c090e3379660769b76d13 a2658855c70c928b53e650deba858c32016bd124d0120a2c8312c70c74ba07a7 aa1c27cb6a2a5ac4bce8226d7c2550d04296d1a6a7cdb9af99ffe6f79c714048 ae3a975081d89a1b59cb79dbddad4b7e7ec03cd250436e58697e98c3b35fca72 d16b085ba5b3dbec4bc0824a86fc891d60eaf80c082129f7b08ea018b1cd895a 6797e34abbee54ec390f786f9e829428bbbe9f931197ff4f05f68b25ae4a86de 3692558f334802719b53927648acddcc3351cbed989db9983b608bd23cf0f5db 0abadab9203a035c50846e4e6bf5faefc729f62c1cdf374911f9fdf7c30617c9 1264b7f7a4bd61c51f4036dc0852e7145b4175b50ce254809aae0d537d54c015 eeae6191259e348d0e05fc8439f06404ac5340ef0206e8abb47158988a82b39f da91d6f52d592e49ddf90181bf959be81da71c111ae4d3ab1113761a5204c5a4 8bd2a7ec382e259e4bda11603fcf9b6ad9b522593264565c7bae6ea2ffec7fcc 94e0f971cae0f30c4ff2fdb04f07de39c7c19ad9c97685104270bce3f2961706 f1430f38b9a063dd2fb58009815db376a196cf8bcef28ebb9f94792737ceb27f f98ae07e7238335700b2741c71ab764add89a0d47358f5ffe1d742a0eec679ec cb74583cb185b7a93476aa29deee5ff87e969cf118c576775697f38f65e16059 4de3b8318f2f652578478c8f9ab18f508b4a9f022677e170ee00cb689b13de99 4c7ae9f5bac789ee8c8ae1e4c5e7e179e8809064ec6c92d15bbed8c3ffa9c54d d4a71c3e940373033de467234d6ba7e863ce83052f0e4c7148c317f151581286 613b4b385b21a76543e78bc6533baad5de435abc68dd29ad654ed148f7afe51c e685ce390ab1491c85cb317533f10fa252fb5b71fdcbff0fd2e0d68d25f03b75 c53db2b6fee823cb5bedc26bcc9c3bb523723b403b21997c01407a6af4215fd3 c8e21b4badfba425c032623b182bb2e22ce7532c140ff9b81d0a217378aa2572 2d87ea417860f4d8e6a24c5a5c01b7fc10e62d15770f93f004a76c5e4ccc25df abedf39e57420ec152fdfeecd75eea79d139dcc9e17c9493f3233aa5e12c8aa0 5bff2365081e50f9d9542abfd6e3766d35c75b4152d86ed2d4b2af2f2ba2a84e 81b6a56e44678f3fd761bf99fc0a67847602a28b3303c44a7c7dc834e46b7ae6 a38e86da8503d55c55c86ef9e4db522dd48b5b5cc3a42527340b8025b6ddef81 33fe4c50a1646de0f5feb775cb9bd87b89884a0050a777bbb23adcca80a02e02 2891dc6c530afbbf284e7ef2ff03f375538c2447f06c94b364c2b7905c9a2c5e 348b871c8639552d05dc13f781c2ff9ef7b628c6d150f78d7b9092026ab4e035 5d078988d47acc2083d2ced7ac2fc9c25812f2ca5cbcedafc3f1fdc91942a948 7965f670fb3a6be938d0ab387cdce04d4fc33f28bd6f79e2c03d6dc13824404a 6aeb2c54ef75da858847c27f58fe40bce4491dd612a3c9ba46df0acbd6ecc6df ec9dac14cfa819524053bfc5b17da56ff64e9ce3e18ca4d48fa8a2a6d4593b46 2b3cebd0f5db2ac9f342ed4bb1086ce69391a75960ac43e564e00c1aed5eed06 14a27465473f995ffdb206efc4f392b644da353ced3db76ce3fdbd9f4a2a3955 02536bc27511d349fc2232053ddcd03a15d35d7a66a5a29cb902ae929ea3b193 93cdf1bbdf1e1fcba2b66ca061f8306a791eda3e5165f320ad5bcbd8dc2ff063 dcc620b1dbe01163fdd72cf44b037b76401e6f7f297797512f704d4c13b4f745 fa9da3dfde2e7d90438dbfe8ebd61008ec31b3d26de2845d07233c5dcb12d8ae 9c1f9002af7357a24c06b61183da6cd4865094c9953fedd412b0541fd5d63968 e7c78c6812f5f739785e6db800ea0885ee3030d38f1a0e4ef3a4820d2715d6a4 4ca2ee1c004bdc849257a7af453277ea51c621228bc9ffd01f6396817910b5df 54d4537ed25d10df2a4adb572626f029ee45dc5c589be6af7295b5ef46f2b45f 500b5f0dc84880c14262f83940755493407680c66acae8488b3beef9d8749a2b 09ee03520ad4e9e6ebe1492a00757e2fde4825264aed38a025a359370d41b705 7 7120765d6af44f740aa67461b2764b5a989e038b4b8fe99d409159035c6d1c06165d4cec602da1faf328004ff59ac8dfab4cb7004e83610018a6b4c5b9938109fc9e8823b21ff341b7f8664300bd524b337243d5891e323a4798d03b4e97c2013ad0ddb0e572cc1e49e844acfaa7d94dde230d26fb80aa3350a1dd41642ee80d647aac84680dcc4670de9f273982219b8024a492a468a62105d3c65064f5d608cd4c40c47178e89699e75261727832f37f4c7055a8a067e61ce24daa2f4da4093ecbcd28345805944e72eb4bcbf6922292e9d52c39b765f496284988f15a0202adf64125fa2ae5fd0787d291f4cbd2602972c9f3a5537811d654d21b708a36029ff4d90e972906c744a9913be4e4c7dcfff24ebb804629cb44a01c6da1c06602f51f85d4530165aeadd4d8832bc1e16a77b073ae2fbea7ed40c571b363e8b601399b7122f3c36d3458f56e6a1d34f91678dc0375b59075ed64088adf520f080ad94a58f9e37b970d873b98042d703569d8ab862e951738bee82299e0a6ff2b0e55e43b6ba05fd7a5685a999c269d53a083bf33a72ceb7bfcc3aadd2a315b8f094525778aa84ba792d23a02c17530d880bb79485219dfb93b14819f820f4f210cc63591fb9109a40cc9c3d87a71d8f31b622b60891ccbed9385359e921e880501aa4a1d45747fd5bef9b1d885114a62763d2473448d2c9bf061ee9c3e84c2c0092942495b3679b6cec3caf3f37fbde7b7e05f6e6eb32cd3aae6d49e84d500b200f4d9a25be645719d63800b2994f03228f5e4b6e76af3376bc621f02234de410d4bf8447c969424a025e2e7c98116d858e3e4e8a94d1ba1c22513dd0d5ac1b6088bbe3f482016a4b39ee8ef345c979d7ebdfec33400e2bc6480826ad0d7c11e0d2581fde37b6b16d429145726e19c18d5d80817ae08a48eb6ce0ad7de4c64c3062c7a12ec2951d89c4209d7d969c0c27abc16fe86597881ddc5eab2e89defcf02c791cb3c8910e78b7105faebf2565a55dfdcdfc81538e99defd20aadb21efc0e1980390a08ba30bdef37aa965be8e733284ea5edffb290547ae3498aadddea0a2ef3e22d58ffbd48ed6386436f15c6e533b73d7e92cba4972c9738aeb64bca01f739b840eb04ff4a5a8362ba5135181694ef611d2a1edf7285714d3c0352cf0ad848720362471cd18c0722e3c4cc8eceecef593dd837142722f0eb18391a5a0eeae082e22bf6904acc0e3ff256b8c76a2bcfe4665f4bf5014191af680bb43a085fea7bc5166f8da0543e43f8c9276ca25d31087027c37391ed39737bd52313071974d1dc26496fe65d0a99d0f1780e9563955ed44bca504bc3790da34b4dc50dc5e5b449463f7e20194b88fe8073ad5dddf9dbd8c59e3bb9fd26dbef4c498b0e1e958ac8b0dedd67534c4b5abfb60f60bec260646f222842b96579a654a3150003f2b987da589a47da296ef69f50ef5c6fa81a4daedff30e04aa9650d2cf3008dea525fa94d5e97edb71d788e6007377e5d392a4caf52576617303497120cc0dad55de73682c586f8c7324a63fb0d9261b7b69d794f947384f4d9936bad2a9011f525bb410350b443374cd2a8869cf8f18a5ae7fbb8b39ac260e2ca14c76280291fc096770d3b37fb1540a97f5e6d2ce5f6e291ca3ddb8bf37121f7f5983050f0a4ccd633146cd1fd1860a92f93f7b4a362ce90b028e1fdf0b6a33ee90c6fd0da9eb1d3458ec385e5a7ffcfd3000dbed2f216d1d57c74928a85fffe3e12be8028640d0dc26b47c12edb00ce534620b204ca3dba48687c1e2136007b0849228099f588d711be7d5d85834ce5b9e00da4a9fc0d7e69c58c41d459421a5e23211000fac8d58064ecbacef948e83f63783f6ff85f859df46cf3ee797d515d0d892000fb3e896069c069b55c0c85845adfba5fb50151f2b5423af76891711f0843209d47f2e8e3155881fa7861ac7e65f672c5e650d19c36d9db39d3d90be266f290c17e751f5613ddb20c251304679df44ee6d6735645f696a30eedce3bc4d750c09255468151748c3fc3d018e4f15f644f0bc36508bc092f5d90e8235b5dfab8e02b67c66ca632ef6da626a506d44a8be4b7e98c48c32c658e078e75c1f3ffd2508ade8faf3e7f5a44c9d78b4e764e91b0c6bb276bfb36eba819adf602eda32d90e56c51d4feac2d667c8ef54531309f1ad12b8778aad38f8bff9d9393cabcd73001c9daeaa52e8a034005bc256cfd10a75874817ce8f4e9e681f1e018326b6470d7d8cd6e679d88936bf287a6962f480c13b01a1d64907097a5796fc0294364900679be5616fa52df84d4b681b89693f12267445bda43c1a137b14b07115ffbe087548cf1b8da543657f7c5cddbcdc502ac034b02c7d902438f4e1b5102945d3096fd5bf10463d4b0f920a44c43855490b9a19c1d9b8534862f3556519e2a4cb027c9646b6ca7580ed361eb066e5d1042e626cef8440a71885342838bc7fb3e601394a865d0068bc334da16fe993744513b1341bd9f96d02791df43a59dce89109e76661ae678ae3362ba17d536e93f781830577ee664fb9c58c871e0c13c3260b965f63a9e8a14d1ebf50ccebafa4ed28d078949d0d03b57849c8f8237c9eca033f0b1f0b40fe3e800fb48cf2964037aec19704a5f63f490d5c191f84bdc81b0b177a9804c76dc1693a66271dded98480c352d87e5476a77641a0d82c3ce57704546d2fafed736708d7352a70581d7907d53fd0eb41da6f8f864325737fafa3062815e8bfd095d79737c66c7c1b710aeaebafee6415250a96e64be58f64aea104ddfc33418f15847ba685fafdfb002cfff772889490a9445ec715937f45dd910a53410f24a369f6c7747893bf4fc4787728a6345d3d12f5ecf296d275178fe50d1a0758473f78210c36427a44d8b18902bdc0a215f13be707d575fdd1a6226e0ee93b69e7dfcd31ae3657193c28a56ad6ecc6ac131294ce9be39645a5c5c5d10f3463a094664c5fda344a70388399ef736d223b058423e77cd58a535e39aee7020f636498200e4a8d5f3332b0f91f2e37c7e7ead728fa28ebb282344db07dfb0c416f4506fe5e81f3965b77acc0fbc01b30f5a0e96b22f26ffcdf2ddc7cc3830bb8997597dd2955ec04ae8991616e8bd61edcb47268800dbc5cd0091df682e200f3682baa7f226ddf5a294c5c9fc515c792a0ec51545ad1292e5933a115dc920d984d5d414fe8951d049a2dbd3e15b631eb5dd25203ae87d734e23fd6e02d4405b91148e04cecf42766318b9e05f8855f1f0a63bb6905fba6f0e0b02c3c0fe0065c570bd696f2a7fa71d3c3636172ef8a1dc7af872192c5d38ccc2e6aa5a85a02163b34dddbcd21eb8cfcd07c26880010cf8bf13d65583531351c5984fc809303885a8feb8db7a3ca5b8a0fc3346df6e4ce4e385d347bd2c7fa66c52fd5da790d9ab1b348b110110975b83048e05c79d5fd5e2a18b3857041f5174353292c5a0702b460abbf19e7b7e41931867d410f961fff64821d082330abb66596dd01b6035bfbb08bcae268caf1d1b77282d3cbcf1ca91f0d1fa7ecc73aecba1b4728fd0e16004994b587a72636d5a7937bbce4d1a716e7a78ba6e174bf13646b516aaa0f9905920057dce84779aa3c69e82f701d0a0eb48a8264388ec3da81a18279b90829a08a4a73557f3daefb6d0dd3f5d21540a87b237060983a893326ab3e042c064e01b442534a590b3a25b05664869f4010dd6a543625915f5cc8574fca314402773fe5c3a1c547c0d6b01a1e563f9d0b9b6a281a5b75c2abd8bae9ef2847a20612d5878efb2f351e68cdba8c417959a05974bbcb21f185b6cc9d5a8fd308f806b34e677089efcd76688dacf6aed9d269aeafcf235fea011cec2768532621e10d7b1dbe5e39937eb2bd6a1d0be9f8ccd9569539d39efcc0a6391ec4ccffda570ae723b2c924394f42fd1520cfa48b3b39390e51782ee16f91ac3d7c71345acd080e5b0830f548544f140822d1ea63405f883b9cbff2fdd272df6e139af254ba0482d7aa3787f6b390b031d37041c8cf54822b8ff80a592443a9c84848825a9e0dc3c0408444bcf0265faf5069125fef3b42ec90237780d385f8ae92f7d4bb7b0a2aeee2ae4f20be3a9427e23b21f73885c3a6d4cc7601dbf768ef339f3f6a9e05008ceee0e177937ec3fdca7250ba3a1b624ccd74fd7a77358e542a5881ec5707c44ea95fdc4fe392f0d085aa855b647a4b05a1feae6003120d86c0ae92effc0bf137fabb93cf1381c5b9a656a11d6614e1ed549c3ff645696b70d1ab36813c0be8a0a8c926fff0e0e004e8bfb6b31deae1ed097bbeca807c5cc8707650764406acb4fa685679407edcd6050c7cda54f219f88d109c858e529ff9b5e008c26009eeebc7260edb651fe03e0908bb8793b56405b305f539eed577f070a44aba9b04c89cd94ee1a82f5c164fbdb1ac2258423bb7c8deeb6c92235bf24d16fcb69a0fa0300aa9fc528bdeb32b775e6f3138cc6bf28b2719e42acfe7691a422a496d0db2a475533bce0ed1446bdc38bce6f559de90460674038d498b89abbe601be1093a78b74d12f28782e7560aa64137a0e2123130b9317eaef0054576915177cf0d +generate_ring_signature c36802dcc5072c350ab08a20cf1f275c9328d04a1925015cf1d5cee63424f4dd c66224ff4df1b47a497825a978493ef57d7228fb06e12cd84bc3ed20d1433891 51 9d2137d48f7f16f92afc9e3f064b7502ade72f5e440a24e5d863f8a3c15efb14 5d4b259a5b24948ce09e31957556816c6eb6841d099cfdfca14addb87e81982f 5abe3da5da322eb2410e58a4cf1b2a9d80d426eca61ae0380d5315753f7d3793 bce194392d767aec90447a30269a1c46ffc23f4908a99f8855e4bd5aab96c764 20f6a3fa5280357c190f49bdbaf0364d0fde67773154f1ef222f6f334f135bf9 e94b47a45ed442f65246546cc65e4f05c395daf7f9aee46f0146a92b68979e10 adaf84a6daef868b9115ea7707a02a488e7a475f790996895825656e51ca05f4 784b1768d310ada653adf4661e4d784576dbf018a69aab119a65f510785a9dd6 e8722ddd5e3ec70c448b99271cdd30c953dfecfd3af836824ba7a83b27a260ee c736336a80037d24cbb3822a4de85a0f86f77f7aef1325d2da132a5e21806a8a 5a19936132d6ba3bd02797841b3f6c9f1797ed8746ce8aad66f2167630546b8d 5319d153cf855678adcbe5cc7e07793c69762272fb3f3c046518f0a45cc991f4 1b6a674f9ed3e7f8bcefbcd0beb86f9bce4799dad12aef32cf7e45757f8571cb 1d5fa8cf9c55f2c75d473dac9fa23a6dad7fadfe883e1c3d2cb36cf111010b7c 185f024aed8aff6ad4dab5e66b2b7aed37c0563c48f47aa8870ad44f757c2eaf 9cd4ba8fd51c5640fd7b4bbdc09bd2671aaacbbb7ac103f5d017999909fa7396 24ca3484c1302badccacd05481191623284d9170ae4721b5988bf98f79e40248 b4c26a3c2c9d16e9156d244f07d149e75946d8571a8e8d30c1fd8b7941841ee4 413999fc91e9b48b23861183d679c27eb4c68e231113402f905c6afc2b680c3e b0363c1ea8f5b9af1448d9385b0bed1c5a176b641bcccfd86df4ef44e40c9470 26f223b3667136265e3ec4cdac7a8d7865e0cbf0b75c0bc5a2cf55203f22c22c efee856617743636bf00a12e558d2746592103793337875c8f4da9995bbb705a 962bb982361ad05cfec9331326edfbbc4c1038b47953b3b011f6a8d5bdcf41c8 076457eedb037ada1e643739e117fb2e913f5089fdb22bcf3601a4b33cc73d9e 282a56d7b03f50d5611dc3f3f1c69c84ea361c8668f7770176fb0680a7f4302e d007352541f9890a618aca43d2a9a7d3c4255bfbb85f828c710c67ac864fb353 76d3c9b619bcd30986034f578eb5602b8f41bcb36e0a63bfff0de12b2463c709 517911473b51d93729cdab8e17ec521da066f44f0201882762e1cdd3d0ed97c8 16541b306148dcc54dda8cbba105cea4eef48b7295c76593aa551a43d29dee34 0a8c96465df1d5033a179d947f6582816d7d5c6ef09ce6702e5421e02b907921 12f07b7a090e3578119246de68128e7b6bd8ee6a0bb9ba79b93a5db290b9a516 d89c0c4159b192eb55e0fb7f7e6ad781c6eea97ada34d4952f058e0f31bd22a0 d26cf4c3adb287bc5d80e138f6a1b9f61e9a6f7307a2b848ac56eedc210a545e edebe4cb0e4405d317695c3b46037b77550bc5ea66c407964b793ba6cb2132e1 b71caece5f526246e5ff1aeaed55db59ed3702a6861efa24c2a036c05a85d4f1 fd7368dd65a1fb6f9f0863b22712dbe4f97ff28f42a3c8c2bd4cbdd876675634 7366984df442813c46ca1011b462817cd425c98a5a62ff94c5e2a51d1214a6ac 75ccef8a14a90a15adea637b09b6f54e2a3b8588a4f49fa8097d5eb7ba4af455 e19b0df019d39d25ed3e43c7a8dcdaace275ae07e4ea529fd66066117147ca82 92a9375ad45d484e0e63ff4761e6e2101567d48ef89a46cfa75a5e5959da73ee 26758eef908ab2094150965fc5a14d4a23fc8914b29afe79589e8ef3dfab6b92 31ad6031545a07e4783d9f7671bfd7bee852fd457fcc4660a20d39db5f92b274 642954ef26b935b81006a9f60cf14d181983a51d268d93e352c1a08ecf88d9f6 2f1104983f83fc0f6db412cdd2d755f31c8906eac2cc671c3cec0fbd69614d41 a7920348c2b86fbb1176fb576bf3b3de8cb50e1743f2046e2d42c358acf4ba0a 41cae82b77cdddf18da392345e5f5c3d2e14063133be59d82d1a6818facca4a6 da4f44cd07a5c12174e75557dda44147154a13b4062b4c19320e773404e67cc9 6fa5fb1084ac7c14012bd4dabbf3326da2a8b66e107e482f39e4a568f3c29c03 184f4ba08e67db2547558deec608f47b0a9cc05ae15b5c4599dbfd81e18598c2 9e945a8de54dfea733644e266d6263c12fbb4359c0945614f5a6e03764c6d90d 2c642dc3ea5980dd2f0020651f75a15cb7faf294d034bf42606b2bc974c32b96 e3795a288d1c11104bde0c9b98ca85f25714278f9920ca28f837333a061e0d0b 30 4062a21f18ceba224d2072d4617eebf89d707eafe7621226db1fe8d4e9319b0aa73f8ab6df8e8453f06a4700991058da7dee970e3c8f25d4e16f746e89a66b0601a59002c0ab449f86fc24552828211c25ec091f6cef0e516099ad88eac24004623992cb59558dcec47652437e2b41fcaf8e3a8c3b04fafc114ebd8c1caecc085e3cb08d4fa394267ab8c51621636a6fe87ebe0aa3de851d3ac8d38608da3d0844aaefe53c7653db62bbf6653b4eeb5f83f4f16c8f5e4148c71442958b85e30fe7c48156eb14a5d25a68e10a369a7f1e07015b9c6150312b64c2085e46ccd40e3d21d8422d895dceaa16a583f74ef7c6b0e8d6461d0dcc9fe7fdd17319318b06dab88642aedc55e6b54c2389bf8b4560ed2e297ca3e4e19cfaacaa89f492e40f53b4eb96ed19d54ca826c534de2df03e55314097c4f8ca856729bc8d3706820646915a580689401f806f105d340cb87cf010baa1aa63ccd569acbb41666bb00d680bc6ef8b7b9a3fb1ba586126eb86d807df5fa3b920d44443cfd8d9f86f340aca8e0316ec1173b984a9280bf5f8780f05293fec0093d84d60109fdd82d6990b5a3864fe52d3b83718561f1a9b79347e2d2dfa8e7ea8fdb9bd8c730fe4d6910b22efa9fb7b527b98b09be10fe5d129212f9e672aa2bc2e43b58053eb1f826c0951a429ac1cf5b15dc4503be9de385a28841ef927ce132aaaca1630e30615ba0226aac3896d6259266b90114399774ad60d6e2dfd50c231ef3f6fc8434d0f2500b1074945ff58c1d331dd52d1b503f76e1718351617f7a043295c643262489b08658653cf709c279dc5bfd37c21b166d90dbb68fb1ca8112f2f5426ec2695d20bdd93fa9dd37161fe05cac19a10c649949c5be41c98a6b03fb07f3e84f035730b6b30720734e2185c7b49a5082ba6e76131997a100a5c57f8394457d60a594707ff835fb3305be29deb762383e8732525439e2590c5eb3681438eeb5b1fbb390203876f72805fabc9e4f7627106d2950cc2cfdcac7d13ae6cdf4c69fa8a43d107c3fe20d633e4fd35d3406f14a31cd24e717f782b7e4b1a2c42dc3129ca4ab7020dd02a222ba6917780e67728ad4da7ae38ff2340b705bceeab3f6ccea0f9700202eba70e47d16d7262e1c01fd8dbec17f9651165e36cb3a826646d3d722f050a0ea89c55335424763fbe8e5e53c9945473d4b23ae8248174b5ca1c4a23c2ac0f91abaae8d736d2fab66bba605492b6954cfd3d7bc3f24fdc59ad598c4dc5920b7a291b79d255472c77459b8be1b8297f33735e788b2e3adf4838ae35b54d200a64034a2d8248240ef917d1bc42a7ba26804bdbbdc7ee48621ad724bc4012eb03a3a48ddac10a5f046452aa7050776e75c3bde3ba90801204fdef6fa42ce4b90dc2fbff8dacf6729066741476c65db1495db28ac64c69c46dc042be50ef048c05d5508c04d22113f3f4318470ffe27518e4dc263b5b81c8c6c7e303fb577fe9050da8aeaaa4da8c641db5e93cca808c12e8e0e05a32c8db0ea5d06db4cdf0d102b9e9055a95c7cee35029e4f019de814f1601842b0c8e4d7f0a8c5b29e875690f6bf88591b93aabb65194685c384f0cd2a1894b08eaa93d6425ba8de06a90c701e1e6e0191c913d9605e051c9ad517fa109587442c89a45b3eb7bb3ed5bb727055df35e3395befe9435187f8e9c51ffb224ccd38dbce45f66163da6c5e01e7d0701bf211519a782da0206c7a87607dcbbff872479465e336afd6537950c774009aee5e4c0dc342c8ccdaccd9913237c1e857b4db28291321cfc4e79d14041e209875d8b2656035fc2f237b3e8860d87ed26c37922e3d678fa5b20e03bbd129908b14aeb8ba0625874f69f158353cd2c2218f10d1e388d8dccbbc541cdd8b4980da32111ddcfced7f3add704053ee5650cee5a602e573af4ef142ae15da5a96a00c31889c35106cca6d623236d41c606109e202ad2196d10ee083cd150496d2d0aa69a035b4d57b41bdb0f20b47444d94c98edf3f303829fbfec3208241428a80eaa8a07bca7892e316eb04a0bb7be534f8ca6c51e226f61643f070ebaff77dc0a3079fedca87e625d198d3b862d1503015867074edd11e6b84604b1a7c3400a0b9aded1562761792ae5068d40159a675bb9944f8c07753e48415d2d34d436860627e69592358c7cb3704c8fcad6c5b1861539f8f6f4ef28c6014de7c1ff7ae10919398fce8e5e21cce558e5a8635755b69cb2034b546d92ce0e6e480585866307ef7557edead8160bafdad20cd5a4f6a9321ec4641d4ea798b23574906ff7a2048118afe07c18fcfbbbebd3011fb44331227bc277fbc6f65a5e63af8d509a850ddb4cde013af73de775eea45bdc3d254347bd4fd5ff51b6ebe7d965360820b101fe358fdf25c26f74d337c28ea60ce4a9315439409775f9b01c8553743994250480e0d2e77b7e6953bbca8a34d8fd39a2a316c1d1609af2151ae8c956dc0d650f5aba4c5428b9c73628f98ff90a31c897e8b6893273dfdaf989502816fae87b047075ddcb986b010afa1f869a97a9e6a049466db88dc0ab3337307ac01800d00e11e3e7d01dd7f0253eb4637d9034f2053bc25f7c26b06a2c50f428516768a20ec03e0e11eec71f1a6dabb159eae876309c8dacbe828c99d58fc3591c6fc20906bc8477c2959c205dde6594b5d981b7ac0a82d181668bc4a539ae76858a55bb007c87f40d1ec814d8a28ea912c5a36d3f6efca9214a6f8cf7274f35691513da0856eb991d0c845da4a8a6e34775888d206ca5815eda29848b80b48b9c3185960f0dd6bb714f0e37c79e2039c20462141c690f536a385959a9fd945e691f73240c3b56d2d136684dcec3d84ac9e4c23a31378f6fc72c3eb1d2980c86428131080ba36dc7518b6f02b154927984dc3cc6199bddb040fdb6e9fc2edc4e62bd253c01ec2463bc23cdfd42e1d5d8e8188d3c7eca939c9bd90d98d79c9b5e7f04c9fa00c8efc07f884c6a3d206410b062e7a495e29effb3736ddfb5233142352dcbc60a0751d4630fa9e990a6d929da4a88f524a97b64f20e517f4a9be0b8790acf5c0b5b789199b72b3cc813002e3a371886e951341a2da298e6c7742ffbca9810bf0679e3abe693bfa3a83c2dbecbaba65ab6c5b95e33c99acbeb854bdd271a55210168c91ccde3e0f3f35843c7e4a807380e2eec169bd9c0557f478fcf19552ec3001fa06f9d69718e37dd70c8fe5ac81880adc894c5431f12be29f1b455f871de0a0e9f8e3fdc751b5f1404637c7604795b6abfc577e5e0a7fa1989f0b8f0f40b0b841a2a5be363e18395009e989a47c4a56e7fa7e2108bb6dba81c9a56ead22e09186ebcf03b634f85a0287ed6c10883803f2ab4319dedcae76f4c39df5882b709c2be9a80a628a3868d7040a59e608eaf7249f88ca72b4811d38febd510081a055b701c049d485dbc0f5e671e558a799f60388e3a0add6da97c6a1ef46b4828089ecc39cc1766b48b558ba49432424a117301d5981e89343bea8655f8dea3fe0b4e8bc3bef6159aa223934ff4789033c5c0e3555387bd2a134889e3f3d481f00d30e4dd97d163b5d1434e0e0b84ef6c81818feb68f3bcf40e6254eb8d0d6ba10f7462d4437ec6254a23e86495673f5d06cd36410cc07f48f2fba9460ead997203c2c7b6141a58eab80dd621471454d1798b6fd6d11bd039e2966b50237a39f1082628ec8d554e48c5c37ffae71b5f617e12d4b159f5ecb856c0ae648d0d75870b5a8b34cdb3dadc24cf4d52c02a5f9ef4e1445fff7fcb09900e2b885019af020cb840cd8a4ce0c8fcfd4104d6280e45a1d59b53734979471b58e2d4c3523604074dd09946c36bf7679fcb3ceb27e558cdb162a1b7f3cac00a5e0952cabaedb506487f5b388441dc0ca081c40ec153e3d0020248681023af75ae66173ff9e94e0531a1fa140a7f7a98f359859b9b336a014e13fab8db98ebf42d346e2a6efcbe04862400e4ce79b4877a61d9b8501efc1e281b79463f14b030beea07fd3dfa8f07b169aebca7837f6e14b9fa80f8945e577b6fa06af16afcbc5e1504c263204905ea7ce0455820f39ff356fd7d2ba9f58584d3767adee978515350ba4c6eb7950dfd14347879f76c09b73e88f50b586acd2cc0aa60a3ec9777b4e2b8685bcfba087bb7d5b5cec41a47ade5f9a0628570ace3ccf51ca0508561f37dd7207d4e16031be3e9de5639961f7935a093ce8022d04e68ddb994b662c21f0698c1540f6709901b28b2c6e7ff775b51dd5d33fc0008097287b28b21c4675f423e34d2779d0d4dc6ffc5924039257259bc7bbf596b028b05fe9826cb3f81ba364fc56687c50c716a74cf9e5dcb5ae24079c5fd6eb1082d17e9903ade9ac72eea1e39db699900b71609deeb84f042faed447f11a10b25a93d21c6926542d386bf3dda78df3402d73ea977ef78c888d9e96dbc3ad614ea13f5db00aa08222b2bba89a4e45f6a03dedff0c2fbe40157b4df5cbf681638cc998591e3ec4afefa84a7b2b098519205555b5ab8967260a7bf1df7467d34068c42f2a2602d0bbe618304faf8c19c7f0ea5844bed0fe02b86ee3929251e06b5075edfc0cfb536e7190feb90e1f77bdb01 +generate_ring_signature a9aff09f6d8345764da1feed7444820dd7b1b08485e43d7874116c673291eb38 a3cdb19a9a95978c6c2cffa214cb7deb1b61f1a93ca360b5e70febf8dcf9b43e 9 ddaeec1eafa2215041c98b30ab5d4b7efe509f4f96ace4700cf281d04bb4d190 cd13f6e476f4fca756f0e46086e158b6980cb4f71c80da0a6127d3554ccffbd2 6418ce4402f5a0f20dc6f828b3f7999139ed7b17ce90a117451cca78ecc8f954 609c3540a86a34bd7107beb6d893f1af32f1be98f71092cdb18c1055f9cd675f b1b346bf3c2ec5ff4a32396f7a35376dc5a4c231017ef6048648e479f903c583 ba0fb757df51dcd02a68f83900d30c19551185984d9b140f70232d271a4e03cd 691be9ebebdf5327232e24bdfabccf9e06a8477930beac23aa91186b3a2639f7 b851ad3537140b5a1e111a1acc0eb6c229930aa4da64fd80527b79bd8a173108 335c25ecdbfac6126e9ff44f293e7e155ae8aec330c6ae531aa88e4e22e659db 4f672739d453a4ec3ea79875a9d6def11e9039e714b4131672b1e48c0a63000e 2 2fa4f44b015bf8b7a33cf76f6dd593a50cd37295f40228cda58405a8d73dab0f73857fa8adfa2abaeb6ae8bf29bafd9f92170c249968c795fa08d0075d4f9207a8101c44ec1a807da50203d6df396a16be078798a910aaec65dc575638cc9101c4a7d8ae1f6f68b9f3e56afadbd146a82a4c61eb5ec335a37491cd7162de1c0b97bd5b253db446d16917f854d20654cfecd39ec0a1b7396749800d7350ec0a0b2a900b571b8a4d7912fba59457c6c2cc2595d7cf677cdc80d959af4d37abed079bb564f6bcb615c80fd8960a694fca9d11c2148b8fb629f43182625b19697b0b37b3ab77578cf0559967178f4fe2bc6d6b5c28993d7dac6d87320bbbe099130064cd0505def5baa591811fd209cf7eee18226146fa55d01b9557f3b2a4e84d0c40f0465c56841e1add1afb908e81fc75db52e47ffa91e87c239b786a191b6a08ca7bbfc312c2836cd50dd41e574031ac48c98a1ed9d9ac0eccd914484f4cd00fa511684847bcf0b354b11d89f1cf61eddb19c939a1021c652733a43bae6a9f0a5dc047fdd41dc086a69671eb0666766ab201023bd6da6dba70d3b44d0e5fbe01966a31997746eab742044610dbce2c41484e255324ee51ba020e3a7436aab207567d4b645e1b230c8402f2e9c5e63442dcfe8f0fcc9afad4b420a5510cf2200dd0ffaf0419b18e7a4198b1ceadcea1af34a4071a7b62867bc1bfc181e0bfc30919e9eb87a66832d90359e61ee5ec7ee9ef7479fe955ae14a892eed85812ff2032fd58bd59f2ccf783d634bc51f8d0a5fec1f85d4936aa1f9d0cc412f08a0500c +generate_ring_signature 882e66c90e62bd0cdb22e7d2c2401c4273c27350762172eb4786f249df5a045f 790e57827fe0b49457eb55f52d5fea5c619d3d3af7e6904735b1165fee775e6a 8 2fc505efb64ddff9a13507b676426678b612f416b4e2fe129a18910e777c4247 4a0a0412db98a0fd7d318de9307f7667dc16599d71cddc90cb7d00c03f54608f bf84a11ff3814f462127fbbe61aa6c870142f393a2d752e4a2b0e3314922e1ac 5bada1c0712ef70fe43a07a847d5d11dacd37df9c3b1dfb5ae5fccaaf9cbd5a4 9b3dda338e332fc4a2c140d6200dfe780a43a6c83fa63658cbf77a2e565b03cb f6b2dd61e8665228fa6f7a1ab4d1fbc7a975b490ea6b24a819721a346772ab87 bf2d04467c5c9d6450c3cb5642c6f84fecc072e85d50f91b916d5f43fb55e534 6d2208b08fb14c3a0f1c89264623eaa5d367c1eb03887691a2b38cbb6a2b17e6 53af7e5f21447264f174fdf85b41aa2d6a8841615ecd68c81782c2e55d0d4902 2 a8d3f68fb1c9368ee51992a780d9ac67fb14480a661d495fbe7a465ec32d0c04e8d6d2a0504f6584037235c8a5a18fe969fcac983df58227cacfa82c6740950c212ba7aa3ccfe6b06daedb896f26e275ac0ba091e80e93c74d7d639188a6d902d8232226e39ba99a8a1c2ac3ede8779fb748667414053ee15baf75f347aef004b4d6928688f4e3ffb27225e8b026b6c41eb07178d686bdee050c1ca268300e0b73668212d02cfa3f2d02878b25af62442c9babd22891455a351fb4dd63e79a01b8c0869f15e09080123f49e9017dc6da7d2b01ef473430d925931d9a39561d0e039754e50ef473c48b13ef8e2f2475bea7f038caaee16dfc85ecac79536fe60c5a719ddfa4caf74da69c4f72c1ca488888ec4e9633e95146c2128f4991fc7400c0fa5041620ee44ea7e36a5daf7aa0f8b074a5753146e35d5de5243915705e00c44efb32df27014c6f9df5ef5f9c14b4be0986b21eeaa3a92039b94bfe9eb9078ec7201355aad554c6ea2c765885ef9cb34c371d69b739e78493faf68e211a06c38a6504d831b4e26804621e6b3779729a1e9cfa604eca18a344892e8ce1db0967348aa1c07b4afa0f14f85c84f5e9fd663ec5e35240eafac851e9b9e557ee0e273d153637a8009812a00baa53af4ebdabeeb084642cf0acdbf143c32569c40f604cc3c3456d782b532a2c082760eb12d4e31e02606e6c705c8a4b5185ff560e +generate_ring_signature 46954695ce665669d10df0c8c16ea41ba6a9baa31a73a3c00148e8b016179da1 8df4b5de88e7530beb4cf421da4d4b76ebda5a240dd576251dab9436d46bc5d1 2 3b667631a1da9f5195ca225182aaaaf8296545355fbc7bbb0431323119faf6a6 e924aae64c5cf103137aad30f1104a448bb9ce68bf24b7a3f9867ec2c8f212a5 833e511334416ca03a66603dc732ad65337adc35cefbdda3113e9a0705fd6e0a 1 c183d56a55a199d1907a67d9e3094c28d8427e71410d1b2f95459603aa0f090c99b239cded44cde5cd4b51fc4433bdaa0e5e701027d8be8d764f4754e8a4290f7b2a0ceda8423bfd224a7db080479eec6fdba28f396de5134a9be88b71967b0ea7a4cc8aca6b1b3393d574b0529705bf06678b16810a21bb0a21d61d03e5bb04 +generate_ring_signature 046e4aae461121085ca6f594d6fb1cd06963b2aeac3f00452525006ef1589066 56a9111ed84768d7fa43551a6fecd64932f8734c3e02a2dbc72fe96f54ac062b 1 ef9f68e70720743274cae7c2428e6e986a4b9cad79e15e9aea20dcbd48331bdb ecbbbcc9b8aa24751154cea273afa3466fcee242497dc2515e0a27524fd2df06 0 c594b3fe8e69572bbaeb76c7ca3752b51f91974639dba8d70a7a353132474a0a740b6bad04bc57a9e423d2a71ffcb82074bd92c1960070eb38ee26e4251f6a0c +generate_ring_signature 64966f5b65c86abcd6cd3e0d712a324e76e73517207fabb3b1617de29321d78c e5dcfd89bb6ffd7f50e56bdb51237567de0fb9e27f9df0c89ac6eaec8c257619 2 fda66723936157260c98891600369efa7cd68727c532379a3668ca1c63aa22d5 f041a1d06c8bf33b5a92b7a50ba348151244c06f5e4c871d296667f623feb1bc 3d8ca18361f9d4200219eb9623cd1f014b40fd47880982452531c00184c8c50f 0 a0fdbc77e7a323d753c6e35519062fa762288010ec4c869c0465568504a74200d6ffc854c3c9197236917e545283062892761a58989a709128f96fb7271e2f0831c14c3ad2d5c6564a6466fe96413b6b5e002abcc59253d71b436266eea74000af5c53126a68d9ee1413f89a0a13ec98f63119a613151a76959755f2b5e5a80c +generate_ring_signature 6b55b5c732c9554b4b4c317718634b4a84816c455f1a2f26b22710cd732e6623 c1aa7e79e32558ecb76b7dc537c9c06d9b9d8c800da86bb70766c499c78951cb 26 9a413a543074613cb1cf79233e38943714f3d3db34f5c12f375e73e99e6947fa 661e86065c4041d15408eff8368740f57753036e94a13b5d871c809dcf141e55 d636bae92921a9586841d97f58944c29bf4b7af0312a47000366653f59c77209 f616f391983375b4bb7f7131062b2216e3e108896f294fb5318b64b89bdfa748 3224349369bce1e5a49e4869fe4fbdee077a24b3ac3fabed5c8708f4a5d9f844 66b2eba55ab7f6e8a8e4de8e242b451f824ac319d890214a53a0c84ac7b2555a 68c9bf8d5dd6979b46bfac09e6eb5c7a47abb368e99c16c54ba76b08194b49ba ef8d62051b6fd988349145c85247a3383b66dbecec8e74af6fb621f5ae3fd775 eed926b7e37421019548f46c91410d274f804c5ad21393f0f1758a43d0003d60 4e34fcc9ffd545a3d39b35b72dfd6315ca09dd064c834c474ae348477955ac5c 84d0d67ab845f1bb2aaa85417a30a42a4107d9bc0d9f3c24fe8874f48a853ddb da0d03f22a9736d931159e474b9a5274b885ee4fa32799f3411021b9a14a2e2e 55b9192e6e2bb97f2fe101199057bacde18dd4271192956ae16433cffdb67580 a4044ad386bcd12489cead560ceeda434b4a37d0d900decfdaa792ff479fa8dd c23ccc8a1c4b16e48f0505f7fbb041bbaa6d9924f4a337f5b7c85bf4b31b1c1e d2cbb9f9d3a6e2391064dfb257d352a9f0e0f15771d39ffc1364c9927ef83a05 33920b25ab1a09a27f2463a22096412af720af30473b8db41ee7c88ae13c2954 20017862735f4ee18b0760422086b5fff76bbc94f0df920c81d101e50708d68f 0b18f3b2214c84b137126a8d68b883a806d347d1ab2efb8d975c12725a68dbd1 62756d50454408fa12390cbce24c91209d120ffc09f8b977d39058922973b3d4 d1838932719be6b8c5a971a7d43053c3aa1b31b64c75293b62f08aa1e6c38df6 12e067bb23380a5662884e1301942e6943802b672e7f36ab1f7b05b8848fde6d fd6f827ed519cde3f4ef33dbcd19e4cdacab65a5edf8fc78758a65f23e8bda79 37810f70c284aed2c9c0e6dd3938c5574cd9d52d9309395a61fe0d4b029dcf4f 0cdbc8549a9f83d3e90bd33e42198aea84deeb67e64459c600b4565b4212bc79 9580ee6ee55376383a23e7cd2b3d134ec1ed522a69db2f3d66db26edebe6026a a36faa93f9ce7f2c091333a527c2883dfa376b33d9d333e9af2bcce4520a6809 22 dfffefce0b94835d0d4713af1eeb2c4f04af2e3bd8fd086c6cfc13471f8a990e0ccc98f12a908931b11cf5d74fbf92fadda07a30aa2d4177f553678ee39b810bf17744c2350864f9caa882ac1446ebba9f9d470669aecdf467758c5b702df201ff8dbb6bc8aa260a009dcdaa6697f30f314e472462dfede72f783ab1cb047c098eb8ad8265a0dad188ea696206f088d48a3412ec098d6b7543eb80b9bd094908513c3b301d7fe0d66bcdce58d94f93d5d022681106d395ffa0bb5a266cf2ed0d1c5ba14e770fd17fc31eee392069aeeff733a2e470aa501d77e7ef8182d5e101a00c6441bb70cf7a68de4891240136244a2afe8d728786e78a2071622eed0a0cbe93923931d352b8df2ef1731e470ba3cf329d3ee99b1b72fb5e45d4cfd21e0232fa5e8d585daf6496faa4afc4f720aa7ab12f076f615dd96c3b6596a88afd004cc98fe38f465126ea9673c73fa1c12c954773618031fe177799332803890100fc0e4bb029f66ad60433a4c5140663b8d1d596c6034364201ad801f5ac664e042de6a2f390d6fc6a8f53d7e5f44cedf0218a4a2874d4c9ed6d16e590924da30e0462d4ed35be9212310af067a7c188e8db2287153e3fd44adcf28342d028180423c190314582a74e8a7bc05ee2e1599e49ff6857a310d79bf3f7fe7ee480fa043bcca9bd917fae9baa3b2e2add44e78fe4c5bfa5132bf7db40ec86144af75a06497a15e6c975cb9693a57180f2680429f077aebad164cb404ee36a341503fb0cf623ed6a959f5d4d5b608ed8a32be6a8d52afa22cdb25f0f0637b64830bcd3037c6006d933db96a3d6265f38ee1fde20afc3d981ea0ac2e9de93066a86240c0b77b8ff28b27af1e48f23f3bc374610f9fda06e5a9d2f730664b00dd8ac828f07f602e5c8d898387fb6dd9b739a4297fde466d450843dced0fb1b2d46158700012503430fb402402e0667e5d2608c1521032afbf0ea957a13ee552fe1dd90d8070f3442854306d03ace55af2faec10305d5be2ecab26d27fa9f295243ee5a4b09bec8e6974c35f3bcb09d1b67d9d30e3745f5afa249055bea222672e26fe2d6060101058a673e934b1b559a8070a799ee43f530541fd2a0eadf0abd84bb21460bd04a795573abb964b21b754d91025aaac3e6f9a6ddbc35bd93cbef374e9d520846c038edcc3dde7b3f93737b60289b93ae067fa9e80fe7e032431ab6069f0607edb61f375a3be3d603e8de96cd6775417e906a9aa82759bf73badada9bdf740eaf3220aefd4f88d269136c5e51602a5c91b5079747dfefe442e5ddb379ff380ad6c120f389cdb33b495e77597507f90c96b30b93de1d12d70a8392bf8d18d20307a7ad2a79a16e490aff59ce98da535fc7fe683fe365a7bad6b5abac2cea120c9f966fc43b6d7e3c991317b85c0232c9d8c40ed629c0b7f64cb391edd5f971094776faaa068ba6c08b9f59b46b3aaa9a1a27525e55b3ba85a7d5d0711e4f510ecfcc9edf24a4efeaf7b5c5832e42173c1966c91993216759f8a4b62f94a6990cb6c21ab2f1965f0d4d3086e90cc6556fd4d3c8706af022bbc8714fdd4bf3460bc3cf0d27a00dbac0754eb360fab37c446dee38aa7c67c44325621f8b517dbc0e8a3997dc29ea7ae6c30bf925766c44e1bf96c0e5067a4cc159d16832b975f0087672e7c4adcb9d5f4f2777f9ece3159ef4048a6ded070041f783b13f07c7e5081658b2e4f4dfcfeea2d7490fb0b91a10298da1405ec9ce20d13f849bf5ed300b54f547884bb5b0ace22b7138505b1b0fffa5e5c2a680f044e52b10ff3fc6b40ba37ff85391d84b1ae545cfaa98a486c3a3b5910a56eb8e11bce0811594c71300b224cc3f78cf39977c4b1cb6146d51359705511a374782e77a68180e33f37902de00a29e87502acc0db8b58e0d9321861a8d9455cc4af25b8a72387e9779220fe966163343334096ee51a5abd6b396821d6622147fee1846da190ac0bab97803b0422a21581c106172d6bcca41c5ed45a873bfe09d2e85a9bb101c6619dc03070607cf6e31fbfd4a5ba0a2be9b71798b226103122359342e061583aa4e35800aa565c77501f312b26c8b2fabc31c02e859b0898db3a6dbd20ae5ff5ec770790756c1c737cfc0a1c9d902d6af504db7dc3ea039e64590907475059e2e01d99102b47956d47d2a0da3874ca33ab9a39574ce6b6eac156988a26dd6fe49a78f15051e9ecb979fa687512ef06f53234627c927cffea67d79016886ddf6a80b43940334d6ed4dc25927e013b849ac0d45414fe81a1cff7ced33be67c100be0165170243bd47f6862aaffd2abb5c26c4eb040704efaeca75453407cef2cc7fd7789a0d +generate_ring_signature f67da68cd55334c3e376831c8746e37fa6f0feb797c940f80fb17c813bb060bc dd3c44a72c1f033e66284dff68133c4462c65fc326b369d8b1b882d2bf3a1b7f 8 23883397aba9dd2e716500e2205c3c2df586eb88655f7475060b005e96402f33 684031841eb9448456ac32e78ed6597a8f61105fdc9f356c3ce82c04132252c4 80c4facccec873e387af547c6ba6d60cc3a16686175d4958aab7a7df2d19e298 cb6862a2ae12f1fa8444876417c6c267cbc68a1495f6cc58ff18415366dc820d 6be8ab0620ecd74213a08f0e5642ed4f9352070eb6edbd45f83d47c65428e842 7e2a9993e3e01d5d935e5defd29e09535394d579d00c22c7df09f41a4ce91ab9 3625301265ea851bdf964ac5d3782f33e5c5fac02c9bd102c957de642cdd5299 abbcbc17da5d813b117259a1901226403a0d67d5907ecf99c619347310fd6d00 48d4002b820878c89ddcf5f98609b066605c0f13fefe6ce8c3b4ae8d9e14380c 7 c31f09f83692ed1a4092a5067b5934c4fb4dcb960128bfb4d721a2bfbd8b1e0e89e14b79ba1b1932da801d4fc4b1eb8703a49e2edbc77ca58e4a858238eaba042c4fbca329745e8f9269a51063586312da6439fca33f8e6c9d2e1e376fd7e10aacd5382983970404352babe24cf40be697371f0039223126753bcd9c8a6ae005fb1c8e8ae5f241c07d2e51acb4138af494f24ef86b2f312bc87b1fb36d5f6902483ddfac009bac4e5c03c438528d88f11fc36120f1596c73436ba618ecbb6202585846fa4002005e9d66c66ea9db47817d3ef5bd771e31b3113f3d22625474024f8f2c2c82970b1a2c79dabe49d9129a323920a3d9634728554aafc165bb930ec17666099ffa57db3fae9f0c6ad620cac61a47e4389ce6dd1e092396259cc707f2f8e37aa370536e704058a0a29c7517e51980133aed029d53aa38920f622b04a0ff621552df7672a38a073e5205d543fd82e0b8e6d300d11da5de3cad3d8c0433c8053c5febc08ab070ef93dc062c0e1829d6ab740432b938876220060c860290e8527499969f4213fb6808c9634af5a86298129e5ab9fa4d7bf92af2b8410165612bffd7e619a596f55e3b63886e616690ea346b0b9b7ceec52cb4345d590607cf04077bd599e0e66484f36fac154e4e7f836fd8d57fe2ebc6597e695fad0933587e7d7501001361db6288685aed1dbad68fcc3cf668db91fa15d0bb3d6806 +generate_ring_signature 85cb442dd58385ee9a0bb52572d6e26c6360b387cb3b12576bac554e6433f37e d68705bbd01791c3159bdb67df7a49888ea8cce4965df78d32c76ea90dfad6df 3 dedbca49d6ad510dc1f00970aeb35b76df87c82edabb2bd8fc06634a74cdf6b6 e9305197a29709e5f4535891f9ddb268e05a4a9a7efd56246742c6f545e9e6d5 4e86761b38a2f6ebf48edb6b076b46f801f0aaf2508c4e8e493d3967942affd1 be8f8c8db98bdd4c3948863b175a320d39a99c386e9ae7bd1cf0e20c4aa89502 1 ffad4cf00ba7ee4d509907a1aa79aaaa6bba4758a13ad9a9841ef91e6a41be090effdf19b799d05f2f94161718be6bd3bad443ebe47344fafb2bd64e1665250f85786043b7066a712d9feaffd6fc85a8beac7827749a009f48be31939809140cc4fd19830f89ef63714fd1457276647617b002287e4eb29a2ec5ee6ae21ee20818f550661a072fc28243d05f6fdd386a16efd397b58240f2da77e9821313f600fd86ac2813853bec78fc9a8808c49ba144cbe0230cb4a57af6571ef09f0c450d +generate_ring_signature 0e74598bf98fc90c1024369cfa43952b9684be54ef9892815bc5218042cbc69f 661b8e03bddd69499dca640660f15d419747fcf8755c200e3e5baa539ec2aa7d 48 766bd04ba6368724572d9d1474881f96612f8a6779a5a3895cecc2b7bcfebfea 74cbf889ed2bd7390554eb94afcd667b0831b5d8ac56ea5c8fc4145954579db7 d17cb088f3f98200fe1c2f47907924a9e44104aa8918a562d28e330ef838ffce 755466ea153b940f81442740c1f9f20bbd8702009b01e3ba7aed88e2f4ecb48a 149fe0a93d4bdbc4d7cdf393d1c65c6ff864d9fbb37c8b021bc5b0e0d41c0eb2 029c0717731f236b843f1b07bd909dd2f422fa0cd38edfc21f0f8727d6423b1f 5d9d3e9507f92fd7d013049bce0d6cb3d617094896d6699c70643d4f97f4df8b d592c7db9eae28526090b3b462d06758e3e33f6c85e448cf05d6cec06ceb0423 df31ffd6fbc6040c290478587f8f1403715ca69c2843ab0b55198fbb0ce4b4a8 dcf0a8f1fb784fa91fa90debe3dd5c6844e037ef2b3b05dfc4a98f24d1cbd80d 2a60c80657d7ab7b533e831d9225e4a32ecf604bb92e088ee833ef768b8a03a2 71139ef940fcb72faba29dc2af0e1a3a729de0a5c5031c03cd60b72284e0f676 5d0cc2243ec729646f020ba16f2d223ea5ab0755e5546f965993a2d2a52f7ae5 ebc8d85f3b4fd7afdd6a287e2f8654c9fbabdc37bc5b3f69b7fc398e703dcf50 bbeab3a8138cd56005d64227c4fb7061b09abcfe1ff74b25b817a14b4d9b579e 6aee135626c5df83aa878700b99a272083b46b01d26299df7110623ef8b35c39 12538e6e9963e5c79328bf91aac426a87df10ec7aa28696841a779875902bbe4 7d56cc23bedae91c8b57adf3c59d6a4c5d1c7bf8dbc6ae3c533ac16f9d0b2dd8 30bd0ff87af4204a72feb03dd786f42c065a321e6a8e6fbb13aaf37fce53fb89 609ae9f1afaaad9d5f06ab8a33e6359b95efe9e317099a68461797ba3d621492 c273151c76ec6b46d31efaa2c8b9ab1ec7faf60fdd2f50e138764d85537eebe0 5a3db59b4106cd00ceece7d2eac38d81ee0727ee69d4f66145cff84853ca156a a9322735c11b554d682f597b516796ad95c0cbfe5b1509cfcbac7806e1c3545e f99a1f835a309d6ce7be49d09de0dbbf6b9f246be02cf80264c96ee6ccb316fc 590d8e3756f2a1e5670a15a1bd09adffc73ddccb3987e74bda3bc8c42847a194 67ea48bd1295e2f1f5ce74fc52cbe5f18838cae378223f43f1750e4d51b4c0c9 7b6a797c6bc818d41491e2598416b9ddd65b7d418551b6f0e98c35a1b0a9086d 02c06b12e0f4281fcced7ad28f726c72b2451e315f953af32a86838c7288658e 8a13d7a88e49e7f3e8e524a4ffc5f8dff9b2d1993f12c186db51c2b51756d0e2 a6eebd75e8ccacf813e5aa148772b27e442eeb60d65f9bf4a967d2ce51957ede ac04ae000b824af3f4b6748c082dc008a700fcb96863649de440d3dcd26c3fc8 1c4fceaf1642c13820829c6600cf9be9e9d473cbc4470b94c6d14185e8b7885d 51bf14cb57a401897feae6167e23401617f1e416d25e43a6c36449dfe698a221 e404cfa73f23148d9e756ce23ba9b99efbe609fc85c72760cb995c5eedc8259c 66b568d8b5bac0cabfba6ae9b1de7a95bed50784096ac28d3e93e8406d7412f3 18f91e282ed81d4320880d4a945557f3c754b95014b09fb70ad291fd201f54c8 1952bd57f5977642bf31b82c5c364423b3f7f74365a47869bf0151272480ff0a 42ec9eaef386e3c46aeaaa953f5b5183003fcb150ce4202945e711be7690a4e6 6a29a7cdcc37404db5aedc6d1bcdb912aea89080f4c296fc91478661f8237626 685e3e8dca737d59d4ef4bda06844104dbeab52dfb2570225158bbb3b902c593 1eaa5f964b21b01ad690189050407ad2916462ef6de9691412676434ddae9a90 506c4a60365e6b123b23d427901a1571c3cb0db5daabae8eab5d70d3e85fd78a c452fbada6e224446f0c35245520fe5b13ea1d457bb4d54452956803f333618d 00bee5d548dedbf1b22d091bc4c7a884d53dcb2e444061d63dda6437e4c47864 692959b292b03e798fd3443c2d3c4208d686c9cf6784e52ac5c5bd9a09a1be4f 39a5490a499db7f3bde7f28cb2ed029aa1a8b79abbe4f879b40288947bb7d2cc 64d446148f5a4c3e6fe7461f43da3711b5c6dcdbe95a4d89cc7550a4e8646d28 16d8c8686c6155c802687d8ffa7ff44f277706b2aae67daf6e5b33a00dbc7db1 53aa9a85a22d759d5053fbd6e8a23034f8e9f99fbeb16ebe0951b0a160469b0f 35 399b40cb3b785d0eeff6ed3a57d66676f8bd7773cea9806b0b365f781dcc400233816634f01c7bac0220735a7779e8a806bed47fbb3811084b31ec0ccbeaa70677ac11cf54a67e790e576e22408ef6debdccc62a160793664870054df2303c07fd5f1c805317323efbe178660b3c9a35c64d5307865df6b9a2f180622f7854086b3a60dee05a783be85cacae8df2f22d5ebd9738f2c2bf1bf6a02c1be929b30f6a46ea2d518baac63286fababa5da9e84d4fc40a9c8b989988cb1a5437f52302a022bd5f341b4b6ff369259bcc49e1ba4a5da776279eb8f49a6d3e6e7e305a0b4287574af6c2271c55b9fd45fc988177b18d9edf46ba1ac56e7aa4b20b1dbf0c1880bf3dd2578c72308069d26cffdd808900958b6fc0f8fa0ef608fe64fa47064252b303476da33d7c766af70e229c206744c9bcf511e1d0f2e247be266bca0f524273eb349cf02279fc5f28f120828e45c991d75154669d749ffded94c2420077182679302704a53a8e79a637ff41f46eb8ee000e1773acec0f9304730661043950794967feb9c99f2006f271a8d20a6b4ae4709263f698b91d91f00e386303afcb0c51e055463ce406b64cff3b10671a5b29d109e22f34b86347d06d0c1704e41a15709b153ea2e0d050e23db3e70575ad2f28c39456260cb00057d5915f0df383611944f7c9bc467d571a18875cc9594952caf58d8dd035394839c3e260058322b10bb6910c9827e314323339fd85d988ee88ec16de4357384a801c8213058ba942e1207f3c9ab034f1c02ef5c8fc59c9f8342e8615473d27182225274e0b665959cb04fafa61cb42d5e1bcc12a65186b3e30df13995871f550fb216d240a719294798be3de5d61876cf6338019575101b76f491cbb0a63a46cccf6b69909e0123b192e73ab6e6457e7f1015c7cafa0a43f36c7d8ead15ee597bcfa700a00d8cf084980e3cf93b192599eeb8f455666cbb441ab825656109e8e16ff129e0e72291746ce845557d1dea16b24f8a0407082fa2982f1396c93c7a76636bd5903b38d34154a1ba099c478193cf6455370bad57da7221d7fbab616d9b2676d2a0707501206d8a31f269400e184b765d49d8b0174fcea48e40a0ef187466425b603d1d55cd76abdc15f1414229f7c8f0c0c851f0046fb6d2b6e1fc551da532eb609d3946a3b71602b8bf89bcd6955e5b2646f34237b15641b4109853b8300b1410d73b389bb9fcf6b2df02633a110573f0556f78493e6ed5e7da83ef70d693fe50356a0ab6a2e5e0aa2117b39ff87b58a24103aa908c6885858c7428e4351a9fb06757c9703694fe2d93d4d2fd6bef95ffc4bc3fdde768d23fee5413834ff50130999b0e9335a027fd20d37e0618b9c46b8b59ecd2160b7dc616802109f7268ce03cd529ba52f107673e532ee30f3d57793382735152d6d21a9b2abb0995d09a80e3bc993cd077871b5afbed21937b1d786a7346ec531c7f2fec8b3fe889c4c190c069cabc47e9ae78cafa9b894e0436d0386342b15f2027a014885356f356971052f020985819766364140c72856271cbd437931d2e276662a718ad088ba323b0cd1d653fc7250306bed636376b677157aeb2c13127d68fe79a5f54c9dea05a7086d1b73b3524f72154b55225cea2192e04ec563594868815301494832aaeb1c072c0a142bc01ae74bec4b2de2615b5ecf466a714690a10acdee1cb8580697550e8bae3cf6a004f699497573ba8c5ef819da8745152cdf65ab997027e7135fa7061b6d4a364ca78297a4cfdd1e5f816dc74bc7f3c7529b546f10bad2b0c528ee0cccd96b6415fcd5f57cc6a9eea37162ebfe109b1fa54aebd3af2c0dee7590b60deb88c98c7eebc2bccc74ef6799d9b670cf2b6a62ce0e066021465799a4b2d302de2fe978a40e7e50c33bb2c03aa314022709b5732c1d8d284c7a6421eb3fd10bc841c90ded74e97cbf5aa08900bfc94d8c87b589d678b8dab86088b656992a0b7e15b91054e285c9dec8b10cdf24975b58ef7a657205c83db892c59f1f1fae0bc6d86a6688c35a05f4c8691f56bfcf3b3346f24a72e1254d3a5a8a6dd4fc3b0ded6962722284519cb7ef72bf2b417f230e8f586b30397b589b2c88e51bc1850946ec5e6ce6ad3d87df682069e3626f016b1aaf0a8c46e7e06d6c6d41bbe32c06f0f0ae852ab7c03529f711b8cce877d3c0be94a055c0e995fff011bb9364140f81894c5a771b2dedda261b863423e6b643552f35bf86db1a1e8615419416a803950e5fb07a2f09d9e4e628d19c837b5686c2f63b0014857939a783a18d2fb107711064b5af448d33014c345b1659bd59e80a664eb7c47cca7aa65464cf027609fa386a9ef78f688534e556eed1cdf437661362a45b3f256b6d97f856ea632b06cd1c067026b694dbedae42ada52a4ca2271ee9a13f64b036f62b311511e69902ceff96386bea3d33fe3e5f1b3eecc5f18d9f598911d7d0524a65bb9ad40a1101f7432051eab465c09fa993679468d89b4118cf2c7a3c2f395023abdea9c0db057191a452c0ba1e21871b524462b754350c702443272ff5b44270f765918d6c018a1e7d1e70452a98cca9abf669d4843fde9694c73228608f173ff19434eb71027576574daa92a6ef92161b201ee00d66a07183efbc393686534fd8d0321de708d5864233ecc6c7d4855c9f42b95f12f350e4c1059d6cc0039b179c2f70323b01fc6aeea90a37f97624e86ae8a83fcd1ccd2a86e04b7f9e1cd1804673ec293a0b8f26f126baf8ae299dd4cc9ff846ed9f608ab348f4561095857f455af045ea089151b3ca0677aa835f26d29f950687aa6dc61eb36d7cf06d0831bc0f0c0ca60bfce4139938c21f7f975828eed1cda866b488e8ec8fb2d81090e07e1882228c08b3967bcfa894340af51e4f93a71e9cc09e513e31b745aed619139b7dcec8a70a7891e7ae3c229da2f8f554d0cc7c1dcd7c62705f9a02e88c9588d3ae653f2e0c3d15d3925992d3fe956fc8fefb06823248b85a56ea5d080554867cfc3c8c84044bd6ac7b50abdd7148f2d4e794fa7f8b7641b92e6e57f971beb46c6cc0208908793934d5bbb8ef85d49fb36555e339d7608d7e97347d2fe2e34af46a9624960d351cc194b50d8158d5115db8e55940ee6057d8b775b99995a9c8b7c06a501d01364d882d192785fd6b3f48d928f49132e6016f22bd08a37d8d19de6a6a69c10fa8d5a2b5c5e9b311a3d49a1d85f4d55d4dd3d69ef624fcba4d089db23ce2bf0c8eb212c6f008f3d6163b576c0661597b9b351a330cbd6c8d325aa5a52531fb0394442f9dcd2c3ae5c2102648a03f01550548bfa03645edc40b05ab262dbb94057c3324a7e4ddd15bef4c89ac1454a3ec427dd058406606d0c38526554914840d096b2bfcf0f64ad0299a0f9f4fa17ae2793d1d8b33319619e8ecb375677c9e0044d8b7bf9a658c50b5da91788f051f218f2543027b6f951f492c6807b49ac20660d582097108b657be33e4ebc52a8ce2060df2ccd71b1a67b5a5bf918c9cfa07839fd668baabf0465ec8594b16a9a4723d8a598c26fc5ab60168a54a6cd7bb0c31a626d3c2c440c19fcf0c88539fbcaa51bdc8afac1fc57ecf3546d0eae9e909a30a5e82f48fbe4063ddf83b039f3c38a3ce914a1970fd0634b620636ed78b0236129418057697cf036dfa1d9ea053f8c568964faea33d2b6f204df00e222e063058eb1b0c6ab01020e30b06d712d5b2c40a92194c7abf8a0891dcc2bad4ff067a66b01c5ec1e4eb5db290e5965943d7ca0d46cb8e22bb0e9985c53501e98a08ff82cfde936dd3e8054566ae92562db999e239e04a40ca7b52bdbb37d37cbc0e6460a731a98303adc3511b4aea49f1080aa59a751ddebd42b4f858b30c758809093a74b640f8104eaa6e79eb541cb9f45492a2393f65740ed3eb6c2967016d0b626c0336f4016bd4857c915dfeec00871923127a1aa064fff264bf1b9abf270b0a2cf5e40b2fe110760f90399209b40d2c83766fd86fdabdac09a3fac038e601ff30e323eb035220b9bc5d66a99d2a345d813cd80d3f6a72107656160003b8032c20316ed4efa87138b6d9ce22d010d4d00d9a12119ccaf2cdbf0c936c08c8056ad7d87b28941d149ae2be68885691f191e5945c23936749448a97858e44eb0b46d518fc76622285fdd9cd789b66c63eedc2b0daa9642e6483e6fcc5ed491d005d2b75fa2c1a135c7c84588607160dded0ccacb91409377e5d05fcc796371407dc3e2154fce721cd3d2b51ea35a0b30de6cc50fb670fdf599e5cabc560ad2d0cd580ca20618090f7221de59d425de6620233e1e8a95a4b6962a05fc53d06510d +generate_ring_signature 0b9baf13a2cbaeb562d564850660c161c2f8c646b6f6cb6c9a14a2d568c4f715 f8e0623473bf5dc16af72bf6d5fddb7edfa69332a2ff24121b14e1fe7c31facd 1 760a9c4b06c83b8323bac2738e40dec79a039e60276d79d06c4784292aea468f f3d75a40dbeed48117fe9f87e9a6540a1cb18a140bfe8f8ad2e67dbd2fbab601 0 8489e2d5b4cd5e70442fe5bc609b1db748ae1c01a6631feacdfdec2d2650c403c18c850ded0cd4d1bd2256b77e04b737f99b5fc56319b89026d6302dc0b81706 +generate_ring_signature 2a857e016750ac3a139ebd2c2351c2d12d4d0a6ca961ba0a8aa415607da85422 7bcd9d4e470f81c7d3730f231e83450ddea2b4a28d2e8eaa286dbfea3a7bfcc0 29 db16cce584492173e15eea96e9c65d7759c4a0a9be30e0ad102f4dd0f47ab4dc 75af9a8fcac816528d5cc01db326db562a48369f862a6057bf152e12154b9b71 37844ad1e6dbd8bc29c8d82778d808b936a1ebae1260b4bc02533598229a3762 92dcd6126203518e47ce1faa0db398b3c9e13a188b00bb577e18d69583757e4e bb4dd666d52fc7de624be6f2f9c2565ed7a334c0ca91efe58820010aa1ec1241 e1b8827b23935f6895cbba133480c110cbfe31756495efe67708cc638e29dbdf 61a3821f19dff7121a2b701d6a758992113f7dc06a2d0a0247144774105e8b14 d5ac2f25144f7d01aa4529c07084cd259961a7bd91aa2cf664cdd4bb37f63ea1 c56aabc323a9ca0dd278dd526a2ff61e43829e0b68a1833129bf6dbab0a28ea6 90300c52b2fe24af97a98ee9c8c5b1d3f378199fc47099e415fcd802651e208a 1c8796674a0f6d2bde8da8221b47fef2a39916277965339cf55f89f9052f5838 ca3650afc84a907beef1508b5b43b6344dcd99d4c13df8d80f02055f094ff3e9 d7f6db3447e01bc441a48422734e5b82e6298100bfd35adff9871e648e6bca21 f65cd6010ce318912e049f9e759537390f904bc331ec7beb76d76df70e4673a8 7a096e38ff275657e32d1693c3cd7e03df5737e6f206435972fa8c100d68e5d5 4cbc46c71995803fd6a9be9c2ff2e3b3e788f89547a687a2d8990d51c1cc70be c029d6478adaf3b9d536191f6d5c64c8b8dbc77a7178482e281808d94441a27c 30b4230d34409b1701eec7ad279a02d9cc0555bb96784925aecc8ac86d56954d cd15e29a2134230053511093afb9a427766e237cbfedfee3b36e03c75ad11660 4b583246509f9138ff1b98a4bed71cc89baf62675cf7a8617177d6ce4f7c6201 c50801db362fde9b18dcaee1d95993a6d5ee79bf48ad80ae099e9534a645b302 225748da0d7f903035eac71c798ea3ec9ebf1f59b949e539a67aee11b8def522 f6ed695f433b71b159f1b298b7f06fe863b3ae360a12e6cca2d5ad1d105ae139 fc01325be21f78d1aa7057932261b773012ce60c5e26bd7a2a87fd66d47f0cc3 267b3fdb6ab546bc9f66ff35cb6db2c5ebdd155010503ff566ed6d1f5b1c9a36 626ade48d76254250d3edce0046a37851c5026c7a4ed303d870e9766d84a9e6d 4e5976dcdcd60f34d4bcd62deda08ad67b215db6e3e9c6b4338aaab9482d94da 39a5bf4afa43a45bcc766d37a87c1f685b27af20795e89487f790697b5c99b97 fe3beca4031647248d1c6321f2bc565302859bd961da8ed2f6a25664e5af38fc 8d48cd0e203a91c827d064f172eb7211f7ad8ec11ded8183616fe9eaa5b69e07 16 fea95d6659965981dfed023bc2a7dcd71ba1b432454871cc8a2da21a7640eb006531f8be34f81a2bc956ce67f7d33b5b684ae2c79b317f051eac8b388a767901f36ca1012b6071d1211fd905b3197cf6293112df0873f5fe185980b142b41204edece66653794841fcecbf29b7d79b864b21d6e537c6f0d6c370fd249d06d40c6e8366d807107d40f2ff17ff1f5bf7807cc1b6fe4578fc567e2a385d92cb5309fc5632ca4f0d25bdadb3621e5f94e3dd454fe34dbd964b53d7b149b9fa51660fffe6459eaedcc89c17b3d9943f7bd5b71271590b1115f8fd67b628d22e97fc0189c81b838819172bb543e84616382be9a68331ea844c8dedc13dd0165c8d1e0915e9f9912e2b6174a9d43be981686c785986a964a8a53aca702a6ac56573dc00a0785eebcaae85bc9cd9f125fdbe843d07f2359975e741e86b65daa260ea0d086910e8dde3426dfdbc161e3ddf6ed3471a1cd507e45b4e22bb922620693319054adb3cfc361257a9502b82be4af3e3db9f3b7d85a7f7456b49141f71c370a00f6d4eab54cf4120403a5d5c07f24d472b6f56993f70a3cddeb51020380dace7082916d70c3e16373bf3339f352e608509e0796c9ac9c76e43c0d603356abe1d0ae73b5bd99b231961d8a358e35d73538cfcde8dd266f4636be72bdf39766a200c5e78b1ca8d75b154fb928b30944c092a7fd87fdd6c3cc813e5aef78df177f00a4ba16a4887b50dca6b7074574749087939ab59a6299e79313f2dc217b3a8c30cefcef325d5422572debc291fd9b18a30cb43cd8bc50b929a669c7cfadec0a2073ad3049a2060e03347b838e6e125d19efc69628fec6072f0422e591788effa04f0caf66b1f9f65c6fc4c014c0badc5caebccb2f79af082c916673911c0e0d20a7855e06d52ad615e24c7cb66fe3c3e685051fc200da0919adefba55ac0916f00579c7002e2c1a580304e74113723d2f3364c8babf0e84a41ef1e890e10069205063de553dba0b9c20b47a6d6e74624a9daee518848b372261c6c014319fd4c03452fe4333b06c47dda6a4a571625d895f8aad091ecc99ea2056332a3786286048d931f5037ab1603b507fabe6d170d6d5a7249309744368911297278bb94ac077d489e8934600e46b96a352fff1b4f5fb81ea29bf7c2f594b406a8c7b59b4e0bc5e3f06d2c7cb9f82606c437a58471bfdb20ad52c773d3f2f1ea5f4eda175b0563018c9634b4d4f8ed40468cfa1026f3fc05cad4a86fe6554c0189ef0ca84b0fb476d074ed8a6ca26badc68db55f35f7be1843d6337113641baa2e841faf9105acc8b6edd38c99f547e43b1511aaebfdade6f43592d6cf70c08792c8e824b5063be003754f3d597a5edc750115e445d4c7ab4233ea1aeafd45c2915db13f9e0b0a329db869f0e8faebf35d32d1314d00711ccd356ffc19c26ad1f10d2e34260012ef0ef98283cebaf7511de0bf47f9586526ec89e878cee9dc0d84c6b2a20909e73ae924315b9c44e2da405eaf80f3fc1aa8a7430c81a3cf7d8dd4e1c322fc0a46127436f89b89e9bcda058877249e2adaade2f181e33755969abc87530b3900104e0a32530a48ebfceac329d0ce5e9a45fc83c087b959997c2aee80ee91560ef6fd6f5c02c78d7eb8564bd432310660131f4602fc0889499ea39a4dedca670a5520d71cde1432143e5ffc9c89154274f1c4f6ff1c787fed593bf03f05190c0db67f08f47ef1f959b52bc0f83baa8d354b7d0485a52e075ce0e6dedc7da7260a477562ecf640eb3bac919dded41c5032e0b25f5184c7233e3f8f9207387c020610d546052462bb052fa6cf93900d293578d6ac1f5ad8b6f9d8f213b7863eeb0ae4f22dd815984f0fbeed68d7b95a8141416b6259119ff4b22e9b0a77d983020d8a9e1f9d7caa97bbf9094655bd7d7b5851a1ead42a6392af94627e97dd119509465a66f93484f0c25b0c329a856d09363b86214d6623e1a4793c216f2752fb01d57d442478092ef0ffc7b2bdc9222d4ea9c3789c73f46799832ccaf8574c4d06e4b9b4e5328ef4f472098889dc5608135fec6a80a11178dcae22b6c0b763e9018b0f432ea4170fcaf0e6b5c7bd5b668865fa03481ede94a209213f7bdc56ef0a4a21e54ff2f2dfd4aadedc7cfbc1a53dacbdc14eb0001911eaee3938b04c680b48694787b094b014b2f2c3af6fd0259158b950b0893bccb449b3895cd6ed0500290b0ac2002c461ea95ed4d698ae459519737bae46f38798ffa68b8baf2cba0fa3055aca841af09d945c805eb2016b02a15a38818bad54a54706b1efc0347d0b9229d80e142275216ac496e8ef469945fafbc3e24a101e9e46fc2e68751a26036dbabad7824ed11daf560e076913991db7f707f50bcb3b0799d02591334332002e4dd7699ec0ac612e3a88108a52c6b0e08db7312ceafafe22a054c63be63a014dadabf570dc612ceb7e8ff6d2a6cf7ce4adcd7c24e917daf4d4265ea196530d3e76ae2ab232911ba22e1016140b7cc4ff8887890f5a1f6606276da4c777970afa95876ab2c312266c6ee3301d810cd627833966c77cf69acb4b5e9aba14a30baf069bde349953da537f921f41b239c41a99d7b54fa3943d15d2ec2f3fc97c05 +generate_ring_signature 43cc62acd8fd9467571b35b0c35f8b15d8e4a0443e2446f5fedd1b03eeb4eaea 987ef1bd5273a136934beb14313dee5dbad95fab81ea7a8dac90adb6ed09196e 4 88063b2cd6d9a033cbef82bd8015ba0d2a17b5abe17ced34ce64d12216a428cb 199c1e1192735f1beb3ad254e60191275559c946a87c5b3d7856001b353968d4 b8f8e9f84b022a747f2c0fcbc856f2e4b3c03ec54526bb0e565f75471613e3b4 30c9164814515a1a9a686ca4458e871bb0d49a523cfc3e01d463f3904132471c ea0194654c590bb86f350f2c5f99c920984b41c7bd188e91253e000a6c82490d 2 fcefa5bfc3d1e6134f1d5e847bf0f23fe5fc4b75e77f131e6ce4ada4a35b9c0d33449ca5f2416b8674a16ec47617c2b79fd3bb860d7d62258542aa34e511ea0bb8b5b02751db07eda23b6f6804e324d9e04fdee5ec9abed6078a2ac01e8e390c54d3f9ee5c2ccf3ffa452b1350536d5a1d199bc8fe7937e34948e21594dfe807884f2ae60e1672659496f168a8dfdc457c6195d7678a9912ce05103027af18090bc83bb1f98a0f0d739aa4984d1807c4b6773a099686b0e3a9dea3a012f3860fab88537a2fbaf1597e077d7f3a5e89c3270ce91c50e1f15acfcd8dbcf04fe80f390fe67019851a53c9285e67b99e84e6e37168d0cffd541a51d3f22d273f6f00 +generate_ring_signature 40bf115a6a4d02774bde1e03e84b3b53106101b6e0c83a0086a0b81997c35887 9a46f03c1a6f8451716de3cb64c324858f40aa06a3215ce817725dbdb7b6b1f3 6 96266012383705a1bbeb81584a1e6885e5735a6cc5db6da65df252df84957abd ffb7d446cd9f2b235298db0b8617b1efaa6dd5a5561aab73cd883bfc0466fd74 3e52250fe1ebd8d28749c979824761890eb63f89ae4b70f100f8f991716cd2f8 d550e9027d98b3a39315bafadfe5475b990e894d2eded529c99f29423818f71c 57033444643a8e7fedc07dc48692af4a7eb7dc11321d572f937b7e206b52542f feb827c800ffc51e756e65ddddd8c4971915c8703ebb78bd277aaa9a317c5362 7647530a136f95ada0a7b6406f90118b4a45b82e45216b8f0dc2689978441a01 4 710331c406f4578887d2cf5ae1ddb05adf5bbbfdc65fe3d42e04a44c1aab890808077a1606098572094fdba66f604e8c95248acfe9065ee087427536f888810250589c73f454905c1f061c9cd8758ec66e1a3da228d200e675eeec4d6badd504ef19d255c9cebf93e139a82d6d04b057da3b1488b6d2a0d15486b2048619750719046016cc0714126bf129ca1831634f5af54d1900939df6c45fffafdd441f0439d44dbb03eca880fd44424aebed1a2f6bded1865562dc9ccd2c329b49b96a01eb1d60e38a96379ae2b575f5cb9961d39e6918dc7ba27e583cf4166947a68300e414369292001666803dfc06724123c0b616521055e5652e4daea256ff9c1a02d88a7fe2dcf7a54ae69e034b0e7f58c8c9aaca88c5369c86df252f3581349b05f9dab5314450e76c791d33457b53bf35fac53631eb404a4ac912c50692cfcb010b099099c282cf70e1af22e2e48401e7c079317a36b0b9f1834c5016e99e8d0f165369a87da5e53efdec5f25694a93396ae3eb3fb39dd3d3710ac1d758041a09 +generate_ring_signature 86c2090b89b4920e5bdfbc00e49fd3bfbd4ad03a9ddb50e812459e60b689363f 324912b8ee3a9850d2fa404a773d63c478f85d99c445cebc37073ff44eded6c1 1 4b42e6e78b3d5c919b241058c491aa0b99fd05c5ed4e7e9e49f6e7d192433278 bcffc9493cb0e24053693bac4fb5a22847ab971afd1954ffb93ebd58003e8a04 0 dd4727ae6c0d6402862731f8b1f1de5f6e174d0050037724ab8f0638edc46602ae9ffcd15a92f33cdba1e8c0a0dce1baf91a66562f8cb226122a399a1e5e7107 +generate_ring_signature 551d3e6d15c82f14b12b9493d335d7d4d88966982bed728188b47fd7eb1a1d47 267a39afa87eeb3ad05e80e75eb31d308b867fa9965043fa582ce93c8ada5b80 13 346446260555bad7ce0f4c7cd3d5928831e12a4f5e26e351a43d560fac03a656 c8742df5970958fbcf8a8393502d00ccef6d85604aff1744e73876707ceccce5 2469a57ff16d49959a2dc7334577117d474a8725fc4b0b05286ce9da4962d386 039e6bb2cedbfad1333279dff33e2fbf338923f577752fa8dd7ebfbfa6aa2fc1 29bfd97460c0859d797d1a35d491828b186cde32992fdbdb0599c611caeba816 c330ddd3a052d22288c636a8edfc5659ab228545dd26fecbf70d44a1206f1165 3ae0d91f9829e05d35f3dc91221369faa66ef4083d21dfc78e525ebc7f7f6d14 1aefc6e2a839839bd2b8d00e1d70535f51c6e8cc55a11320fd7fa04c68f6ed73 295a1be83e521de6bde8d798110f1747ad6ac7d956d386b55af7070a6f17415e 25ea70de76354ff7ab5e9a0860081f9df8c84e52a73b2c2928ace87fc8c1bce4 3a4a24edd3377733c612f84793a7c00a16ba2e1c75742ddc3f4583a11d5e7cc4 d9f17a4a4cad132afb235d1552658ecfbd63ea840c85c75a5778518e02f9b914 c693e90a71a413f0557ed79a88060efd49888e672fe1315568eae59f21618c08 45896c5f1d561ebb8c66f77de60a7576673dfe356856b296ab07fc8a89f40004 5 9fd655486760486e1f04dfd68916d743aa3c8d53b6a9a63986fd5262ae387e0cf4268cc984759bc8552c3c6f98df1bb0dab45938c7cba08ee21df1d560b3920d24a8474804837ba94b3684b71e768fb58d489c5c0ef410584572590323c1dc077f4fa6185746770432374a0f08b7bad50ec7cafe269852a12adc51753945ab0f54c72296c48e728ff5f707991c9884b31528adcb8073e9046c86405942b025022a8244e0efc8171af7165ceb4e3d20e07f6e19158e48fe176c155371f8e5200266711ddb231ac55bcb009c0c22a2fcae9fe5c5c42a6054155f5ee61549a26d085d678f9e2f3932ed67b245e86912a3c43ef72ea33ae83b561c0bfa7c23febf02e0d331e8a4a800fa9cd663229d26b8101036fd173bc5eeafee092176c4fa15072579a538d7f423d005ebc63e41dbc78cc06e624c603621379f2d7d723619c90e4522a3b1dadacb421ae1546470122134ef2121db2890f6fbba7891f067cb4004e198d207c7f573ec89f3d81922f8b3d8502152e253c0f92860653f9aed0d1a028b78c9925e52578dc859beceb2992ea8ae839ef081d08aac63d5d81cc2fe7c06551346edc43bac05062d05d52c8f8537d36b05243a5aab03147fda99ef25040d99fee0c83c2c84ab3ae951c427b8bd9ce6319a462b8e0903a526a70b3d1e550e0d06a24ad4f89b490bf6c882391af0e88b6184c41240f81aee87adcdf8914b0783936088a8925db3fed6c41e7e257d37a82068691ae19448b28208816e5eac0d89ba84f7fa5e8fef12bf8b0c2b1b69ae072a00a056dfee71a1a80a3f7d536a05c1b2f435e4bb895d8e61f69eb665050291dd318cc5b40f0c7187797c0800d30f6ebeac3bcfaa0fb6481e882a26571878705eed8eb64c10a76b32cce32071590d0466ebe28dbec02ada2d642d6f8b95378fee0422ef8fd4c7b85e59ece0dd4e05652f2a71c334f5d22988b882b4d1e98e44d51968c2dbc332cfb059f156bfe9096b857e437fefd8163185a032797cd1ccb50549b76d956c355dd910b9056c8e060c8e958629b30b183ec4ec392eee1dee4c02ef767693f18616f8c3b61869030645b8d2059cbe52e1f310218c416ce77abfcc3e9edeb2103cccc9a0a7bbb4fa01f4a10360b9270b733eefa8a29e7a8fa7566f89c13b6d107415a433f6c181bb01 +generate_ring_signature e8c522b1f358f5475a86c9b9f7f499366c762514ef0758507a078f1407bc8deb a92a11aaeb449a323a7aae0a911c53bc075b91405c780c2c882007719dd5956c 109 25733bef50ed56f3d194b31f631066ef2e8d666523ae539fa43e4c1059df1ab3 b6ed870eaa4fa51e38120a8bef3d654131d4f87c7788509fffd8503daa48c23e ce5ed33efa80bd4d5d8a2fd01a85512ed39461d5bf91758128d722bca31a427e 82ba3d46159d07536ce009994713e0b60316d75b4f8d9faa56eef38493d4bcc0 bf5f1692ba31877b68d33828552fa6f961332e186b67eb9b4ee91c8bc70a63cb 3ae350c1e918e3d73984bd9a40f916d33aee8d77950297f8faebcbc0dacd87cd daebd159e07bfc44e243832f4dc8a1c26bcebfbdbdd2e51c0821d4b831731420 63ba3276a9159eb27a3fdf82e0479ce7842d10dda71955f322d5cd9a74995c03 8cb52014a9ed4dfa4e051cd36d4801bfa702230278cbd2b4a19006694bfde5b3 cc322c54abe96aa40e0fb433632f46d0772e24b1cd67882a93c5f1886726bac6 03b2891757bcaf370beacf202c931ecf3a158a8fdd25cd100418e51a90a4e978 3c81ce8268d24ee04421bab636e3559f01040ef78ef50936e5966eb7d9ed4904 870c1924849ecfd635277c72f4135fd06bab61b895d20af29ee5ed231991201e a6d7d22fb1b0bcd6dee06e24af6c5a08b8b066b2b19e8f1009ca49ebbeeaf242 dd27875a154d205b0c11603a46af31ebb8a413f2eceaa7de2aab87d164c77d01 b4f5ac30435648b5269d3d4acafb6b3105c8d61b329b7f1e92675aaad144bfbf c57425c3f3f725367df4f02e7181293028572c03a1b9a2a7a063246f65580c21 6c74095852636876bf8ddf77e56849a3a0a3ad1b59d2cbde2c389fc7097a73bb df9175f1b2ce026704112b9c37ed902168e06d96274227c9f0750c8643c28742 d1cfce79f1dc341d13c5d9f857aff1fa2abdbf7ef88d74671b74041b0b754d82 bc69c0ae99c27550bf2de303ef2dbbb480ac151e2a36c2280a0bbc5462462891 275aeafeca875792f48e2e29d9680aae53911c0ba50caf538249eb88b588890b df6556e435aade63165f732ea7e2073857327089aa09df186ad48b52763b783d 66e05f82173fd8044c99bc0d1d0ec526ae3f99af9ea38b39bcadb51b5f071115 fa6c77b0b4003e8de0709e2b5a10f70433c3e63305d263e834051ae9696a53c0 c7877e1a403a24f09100d8cfcf5fc4bd4ef8fcf73c8d71662932881b8ff6ae4f 677cb0f7b287a1aa5aa51b9a1e3bf3180f5aa39d7ea79d72e35ea6e5464524ed c71d34873fc9b1d454b28f40175e041fb870e895874b62389a1e8a0883c84a91 601c2355531645b240313b464a983d88a74d945761f855e5798c39834380787b 8daa0bf1bfc98cda08619c6f7ed6ffa8a488f2d9ae93f32bbe2644d36fe6d986 e4b5741e9c42df0578a40cbc90ed3eb52ed6d5b39f5908ac511b8ec98b30600f 5a67b9168cdbd494f07c4fd65b7894b1457b5cdb663fda081c4edcd2d4b6bb38 f948ca9dc551b6825e69502ce64b680154e2cf44d55acebf03fa3862421077d5 255cd9174f184aa6a0b21d4915abef31bc6af4f29bbbbacdd5ec9e9556bcd5a7 af36d526da4a412ba9e03413b31e5a5264e2a73a8f340ff5a348efa1a39be957 3e43f801eed03efa2e9260ffd22a23410bb25a565ea13fdbc086ee2b45a8fa5e 0a4bbd58b45924320cd418b54dc110068692457425ccd85e32264c544ce7c602 9a1956f7f3112844554e50c63e7f926e872f94fae90f4f3c27bead82a5ccd84c 6af0061a61beba0e8f1121d5df9be1443402df78d7168536301eb76cdf58e9f0 442f0d2af418a9dc7001ec0bbf560295772d23dd3dad5cac94c416cee6d1ab56 257d380d7a47d24d6700c5c0c587e393148434fac6ba1a6ddc5808a324dc403f 88771f2f969d8db6d370d279c53b5ffb7c7b7a8b1e0f2ce456cb3494117c47f4 8bbe1be44759ec017808dfb9d5a6cc33542bc7bedca0122cda407d6dcfac11d3 5b01f7ddf5872cc69d018425264fb0832f1786c766089f17e89b719be7db8867 405127f6d6bddb9117fec5d1747b6a0133b9bedba15941b840ae2cfa3d8b76e4 1472ac1243dac0cafc5bf22232c7d59b77b8ee0100da0cdaa522349ab7d37077 19c0f47c1b36a353d4d43e2d708545217345d5b38038252efc89f76c5b431509 a19a9b811a4bffd561031d0d84ccad7d61bc64eed825faabe79dcd5a895b08fc 455279b884dc75667484e4114f15a070a9a800e9491cae501b47341558520ca9 69f29c2dba5bf7576c5ae487c8c45f4484e894ff0afde473726277683e2ae07c 37036ddf014ace6b22d4eeebd804a932b71241bc54fcda283a53aa4c34d19726 3f4de64a6f7d610368d782580dee29d9f29647b6a1e16d63cddee1b9c7655f2c d33ae9c55f1d82f9643762469adcde19d1dbe9bae691b09b60db50af8e9b42ed e0923c24c648c6c6248d1d3ccd7d9f055b716fea064390f6291bde063c6678ce 20e296e40b50aa7aedec4c86649910e7d373097d16b867af718fd7c1defd0c37 9d847ccc27e9e67897154526ae80798f234b18e1a65d8ef1089e94d96f4f5bab df0e466cddb96fca6d9a5975d7c211a06bc6da08241c3f7a37b8b3ffe2ce4e22 8a34f301338b5b0e95e79f5d3fb62669aa6f73c23486089e06e520e4815cc081 e4bb6dbe2d848bb966d755ce40e87a77cbac56690c54243c391030d3425fb49c 1b6077f89e10713f208d068c68649fecf654a84f536f8c64879392b70e8474b6 727aa94f33a11f0e5c2a9b944320f4403a7f984a7d51da6dd60df76dffe4d2f0 79a253571a9d562c483115f546eccb14f0d2f07f765b2885673498a9253a040d f5c8db96ce67d2b5fff39b95e3090c67d9e62b2a5d3de6a3929057f441f27202 92b633156021a1558bdb63f89100e6b7bf4ebb9053d3ce4b12dff2cc32ad34f5 f549d1b2fbf813df3bdd927b78bfe8ca7f1dbdd0cef38583d62e08ff9e3a8e33 6880f8566d83ae19d5010644f2cb0272b1813bb7774fe5ed93b2645f68b8d2ce 60cd2be4c8efdb64280711014ffa4fda5054114fd28ba1bf2d6d128af4d4918d 543da984d211ef82892cdb90bdbc4e193f5d76e5aeb46038c19355ca22556da6 f072adc234d58f0a4ecbd8658b1e9c3e60758169654895263cfed9da8bdb1f05 eb6ea7fcab889a1a6227b791baf35295261f78600e9495fa8003c743d1516d0e 31eca8fe3d2c0eba186b80572024c167fdb2f7d3da90de373cdc771486193a81 3b6cfbb6518fc2949c97df050d6bd80f1c2fcbd63f8dba98c63067e3be96590a d88545f521d6ad8faa109056b6898637623fed2b69e9541a270f58e05d8a824f 0ed5963699fd6d38bb489c3b71744415ccfe5d15d47963f1e44e753bae5c4562 504eb6cc7ba6a5e1ffae5d15b16b759b455f8b9ce47d049123e25128a85795b9 d0518d218ee7cd3d2f2448a08c063e039da2bc59d9815bd54eebe2b73efa1217 813d01bd1eda9e012b024f6184d4de2c77f11ee1a078e5fb0bc92cba10e079ce bd2eabf4ffda586d1f380f77a19ce1d6dc00cf90f01ba58aa0f1175362eb7110 7687bd04957af8b5c6ffa1157cdc3b070928774d5b269f19c838e46d3d97b463 25ff3a7ed1c757660d6adc8f07f36c030640e4d720f8dc1a639c31cbf411c871 9346bc7374bdb5c71e49b0bdd465bb5a5887a4fccc118e582b55f4e904d0c1ff cdcd40cae40b81f80e3eb178cdd5870b30df2ca1b6217295f438ed173b509469 8e2f8274549272f176e629f698241acd34a875c7b1ad4168a9b211e8589111bd 2055646fce2352a643e06076cc85a9054ad0f4b7cf2d62043eaa13844756ccb2 c80facb07b26d042f9e1a8a4dd2a25d67ba6dddbd5c9fcf87eba8ced76a087a3 2170be420c1a36bbd22da43f3ee2cd92778c4dfb30a1270ad265f100de6079d3 1fc705a2025def18bbc4bf2eb745715ec57402a15d73bd2c9713e37f07f92c54 2d33ea2cb5903cf6e5b920efaa197ca8f7c543e9c507d661d868fb1f20e85316 083b2c1ed9991bca34f1e542c33aac79be1f29029602a6b489b6ff787760fd28 121f31dc7a7e69cebd4b50621698006bd59346165321346037d03f71b95ab3cc 5b148dbb5891b025a814c931b1622bcad745decf96614d3426e317458d0fcd97 e7e775750edbbbf070b135a3d55fde72eaff52546b2ed5e9550756012a70b39b ec1404b374de2ef62480b66c733c962e0d8cb72908cee79e928f25b497bd6da5 22cd61e17815f1d4b85b2fa3d55770abd44e7d7111f47c5571935d9e5abaee6b 092402ab5f46d3399fe1944fc161ed48c41e5199c0d1543e1983de4cfd5d2c41 0f45311b9f00b513a36c96519fc338e48724419785d5e0cf0ffab3a91114ee72 51862f302555dd638dac03e070da9c635e93ae6250be11a35cfe9bcfdbe7df3f c5041b11e63eb10722cd002fd5449cce396ea3cb09b3575ec4df734a32340ff4 2ce378839dc36a9590dba03b271da05c3fb9dd25cb566c2456742864f072e0f3 06a92df3c0de96ec9510acd8381c8e46e6fe5b3f56bdfad698cf02eaf2c849da 7bc839060280dd85beb9d51168206681cb9e2d07315dc15c19594df7e2119f07 3566d80d8d9731e92e147b4549910056a05d60cb44d95c258ec13b82e57d1348 265626c938d19385519528d7ef5190ad8908b5c5c66a61e7668e699db5fd20ce 20f269874887f6d071a1f002aa6b543a8f3c5749255d8c9e659e1ed46e480097 e48ee1591649372ce8f0c084f59732aa7312dd360760dd3019a073c8cae656df e2fe499004d301c7206287219eb32f90879dee2d05f3ea36557f828e4b91ba14 f329332c1b19226dc13531650b0de4c419171f4265fb88bf9b645396c39835a6 458cfcd0e57e377efd75d09c108bfd5c72e53a072d307c5f60e0404747b6b186 f44f40e2db0f59ab7c5277761232bb784b937b97290407dc2e22e0712270c185 828a47a64de40eee4687e5823746aba9f19c609197788245ab1c1ce20788b40d 1 2d295044d63b0ef0087c60f6f336cd916984ededa488a5b78addeb47db6bb609e7d6a95be81b4dd14d501be253e0eac658688ce3984d1378710d67155b41ec08474ac69a48de33ac08813953545489305089b92273f91fc4493540e3d5488409b131f095fb4b4131d47a521badcf089c4f1a32f6b84530162e1f072d32864c066cec5cfe7938dd7ba6c2f4130deeaf489fc2f8e5f49b17e09b305dbce5f0530f4228635f9d919d1f870248fc3f627b3178c16df5dd2c81b577b2367fab4feb098c1bf927976487ee7a1bc82c0a8f15aa0e9d95ee8b1d91711c4cdd3502eea2091367c7a531c80840bbce75ab44d4c54e2f06b65fade1c9967cb4482babee370c037c81f7a910c7dcf54a317dbd47e5c0baac251b89b3d243c332db4947de2f0cef43f64843428e3b80dd229ef5b8d632cdb4ea914011886442b8552fc33562012bce27fa8a4e1c1eb6ab6cd17682f673d2c59999747e209ddefb5aba7d2ee40e52044a6200a880c0db8a19659caf3e6bd4e38867e4dc0b9eebd26d477b626f0480e91e55d78235095cb7bdec423a96330e7e2c23bdbc3e37dc8ca17d9166610a46ab3741c1b84157320c07b9fdfe45004c622afc245b7aaccb17f5c0abe9d708e60db3c0c4eaa1c04f93088462af20e83ffdbe62c55f4764efa24b10baa3900151c41410397ef07fab9daa067212a2c5490488c99f60252313f363060eb5cb0d81201bc96bd19b9e6ae97fb559a076f5318b7c5cce758ac5bb9ec4ed346a740f9f04f35496cfe991915d75bc149a5752bdfa543c5c3c29a61975bba959855b0384b53fe53c1418df87d3ffbabe76d6836f84824f6a1c1f4636f8139e48dd10081e070d5c6924f05c5c39b82b73e3c1ec92850a2052998be524f21705933c8508d57b5060db5bb4cfb9b5b1f7b869984083cceba8ffb1b2c2221edc53a4458103236ce531e65fa351a7ed287f568d092d3e0313f283ecae83bd09a71b46af5d0265d0ff1f8155301cbb3fb989bb086e246ca5727539521ce60f639a75ebc52601e1adb871a61b856cbc96742167863c5e6cd37b157c0997d02170679a2989700d51407fc644ebfd5cc12d85259467cb38db26742035883d3c47f48e8dcc14b1081d0df7c8671f1887e6c483478242e598462032e2d3ad90a10debb832159de20d5e94721d54f30c57ec7d273f6078704ce458f63c5e002580bf9cacd06061590ee8198925d952aed86b2ae6381afb32b13f5f1f8569126309d79f8b178f9d620b85743beed1fd9b09c791915288289132043f4a441c8ac6c7aa19f66be1f9a900977754ad2d1420fa44a59a0c1c6f97653b4c18fce5477f7295663a22dc86b30943ec69f635d934d87517161c5089095896af99686c1e2ce79938d52fc1d8cd014d44f2f9f1f1aacf3077f505832dcb055f5d20f5dd97ccf3fbc2cc569892910dd01d035286f111ed30da3188eec887dd45c990514c51cf7e72066d936a7f4e09f1418fb9104102a3b85c26b55837133965735f2d68f4d9c13ad734c460eff20ece44c9f7e6f302952d68b73989bd8d12e355f34be6670f22f4e6362801110706629f024bc57f309ac518040c3ce57d95789b0da319aeb167872b56210275640be4e0abd44ea7fbd9da7c78f3e9017262a1151cd475443a1b55b27959c8cbf801ed4d789434f522223ccce824a0b618105f86007a01ab806f76d02690d6c54c02e7b7e3efd4a506f3cfda767e69d94f366d1f937ad24aeb6946f9cd31aef2710be61aac76d05fc86086c1ee8fc579275de365fcb0b822ca360fd5c1272f9ac200a1cba46a2267ecee31c2ac40282a171273f30ae5e87d1b2c7c8e85618ddb75063577b187067bccc457db3f56923f60ebf76f6a247bfa10d72a8876cc5a503b0d47daa23dd9f2efc2250944fe099d36a4d9b965ed735db0e09719605690260f0dfded190d9ba1cb42a9916379097d71968663583af28e18f61c70ab85cef8460006830f32e150027c64edff64d2331962c1704723b48c8be99cfc12d7fae88507061348f4ba7fb873bdb4980ba92ffe25f5b485910eff8f3afb3e4b38bc2fac0a668236bb1b223459cd428abd8fc449a25ddbcd4f836a0fa06d57aadcd1447e04db2bbca48ef83f5aef0027082a18aae1008a37ba1a66d1f145213610ba1d0c04839b5de943cfdd22c3a8170bae8b4ffa5a633ece9ff19fa0e37d053b1bf3b60a46dff49b2db28e1c098aacee299b63949cadd4fbc2e927f52e279dfee058160be0466335354a37415d38581cf7b96ef19f10b7a11a445998cff67421025b2c06ce79ebe09d5388ca909f0be3821d713ad65db8c1ac42fe560baa3af4d8e6520afc81bb45333a42af172bdc5c012dc7535be49ab5fe9970936dd1f7cb1af7a60ab6e1d84cfcea747cc21b3550219cc9b3c1b40ca2d91ee8e270c6db30f681ce025b3d36984e0407e501348c312f018d885e421ddeaa134677a9267b8252c50400ed100e574c2458411204421de73ca99fc395ee9c255238fd8f3316154bbfc606caa33919f67c5a4e0dce1264a50f41f435e1ef115bbda423903587a7d517b009f7a2945af9142efe486778f90b660ae4a2174555cee863a84a57208983df5500a914ad4fde328008626e6595695352324fe8f4d9d84e145511c2859a2bfe1f0f74f8db4ed11cb85572a4467b8de8aab016f2796f3b3a99841e36157a304b05030671c77920a4227bfa583cc152393809214d7dea429272bc8b6df58177e56b0e0997ea963783fa79ea7fa6de843ac513daa8320f93eb0ebbaada034dbd313d02e405021036375581f2469068373dcbbfb2b724194cc67e574b20f41f23b742004b190ea16898144477898a9b150d73f25660b8e1c47379ff3821c5c38057cf08d2fd35fd4b512e4ff7010b42a8b75e91131d9552ff90e97e2d9e2bed6ac9ff08e73f3e2b3aa173aedb0610c6a926b729e6586ae67325bd5fa0aa6902bf280a0bc0e9ba344ad04daa3fb41b8cab1ca2f0c7156f760577feeafc0946ddd91a830bf9e2a301077753d93407f0269ed59df668acd88750035e1570fd5966082b7305a0ebefe90554c331f7698d4132ce73bf42f56050fb9c84a9f422d839eb26000ee78714cb9d6ea13c19d1423da1b14ebc05d995b6acd430c411d31e9b3b6f60020138ad97948eea3f4bf6112ff271dc1de77b78b049a0ed65ccc5356f12473c029b4f2cd3f68e25317e0fe895f0fa7ba5c2a30f92a6b1cae75a7537dbb42a970a400b904348e9a6c0f445f475626121f98e18a65d008de9a26f2ba24f770167091781d538b2a1add63165aef0df93c90d3eb26f2313ef5290aecc0d522dc234023c447499814c5d9a0e9f8b00faf42fb7e0014c2ef0edeb2ab4824806d7a8050085cdbbaa6bcd5effeaf554dafde0e1f7a2d4e2f182020d7f7fcc2e803136930f8ed06714d324e3c74b62e34a7b779d2a87ede3d62ea9f41b06994d6a7c4a500fd6d3de7f6f59772fb14fd9d0ca5de377f03a2e7526e24de7389ae54bacd6cb0a35ee84830c6fbcdbd519548eed27a34ce41a0bccf9abf74778ce80c7746d2a02508a83988f9d5736d5cbf6e455e024baf50133f603fc02e64d2c38421bdf42067b777a74ff784dd77caa243c4f0e177a8343c1cc44e9e012d1e0e04989bf88098587a2c7bfd5e7c2cd10c8ef4390fa23efe9105434657d018e1bf4f311124b03bfa9e7ab9a733e65e3dcca633941b2b5edbf5d581c1c20e14ec44379141acf0a8c435f1928e0a9e7a7c0da5297002e3515391d2cb8fc239d44b8c256d715a605d866ff99d32cf80c0d2ff333ef4b9b8df4b271935874501625ddf3534b800f0e7ccb623f2776d0f017b428d1a0cea0e89fc0f4eafd13d0ad8cbb91cc54b1310f46efc15d32beeb5a07d2aed7d7ae1b55d070994c4a08478f93837b46c039de06704c64484bee0835663e9831f2491b2ad5a33d1ffb0554244181efbab40f170f535c61a948bae54f7c20d0e2e51cec09466b12b0f5a62e500de5cb53bb69370381fa1c414e8cf0d312c2eb8d2c730e7cf886570e25581f14da84d3818d451e04140e6b8cea05a91971828a45df48c7ea4bc2fd1b19e4c188e5fed4274538220511ffba6bbaad1b0ddf7c9bb069f1de025d790601264400477d1523d615f37308e36b144a0c3b2a8e5fc0b2b912ec350c81303e36c77d7764a6ec650493106003d5043cd8eafd61388d0def0da1466b1be37cbcd6df2020528ec62dfc7a20600f79ba6483ee1a79a4a14174470ed366034e2d4447704eef0e028c557401d409004b825e7f65f67e6e504be68d3aae5b4fba7ce5f0fd4a8d7700adebd5f33caf036b6e6551c2c811b9dea48d0052877c84bfcbf81acdc80dbdb577664111efe30c5180a4b30ef53c9f600d631f5779f5c31caf32a98769a8b98d780867e55abc06e3d29462816cae05891c6a2814e62105cae8e3df040e5381edc69412bf4b6603bb30e2bc53ea79fe2a61ce2e1cd78774c57934498f60c3dce28f3792d6e8cc0dea0ce47159f9631c8aa8492830882183d0a01973f53950cae85366f2de5d5906a8a9351637df0fcfc31c5fe4dcfa7f8cb2c1eadc1de05673041ece354f7ff50158799916705e25fcc5deb2f2e8000890d1eb38dfd034f50981ff3c8dbd1c000d6ee51d89ba327721f7ae71bc1f3f477eb00db44260f330c85e131696f7f680073b8c465b8768987e85afa69540431fb0ad0f929ca40bd0484d12033f85b48008a5911a19c2d85c3cb8ed2d77550e594a5c7f85b3ec95981aaae10c554210960cb3c9203e9f86384d3dd718f7b6d9977c45d623235b29d3d4646e8bf9abcb6a0bfc83a7eb38309766f959a872f7dbde6e5a6c14ef42fe769e5ebdd55f7ca3190cdc043e83872260c7afcf6575209f81dff10e8f62e84527ad92d37382dce0d602a4e20a916cd8cd18b483f4a24ea20c755390ecdbcc257292324bd2d6bfb8f90c1fb76e7f7f13abee1b61c9e4080ab89f7898e5bf0944ec7982188a2dbbfd2f058316f477a7a5a9954e605a879c243e6289b7f58fae8ce78f2ee9d3382f9c69071e0a1cd287ef72118014829ffd827aebfb60126b848f937dcebb193c8699320b5efb7f429c1953f7786ffddfbbbb33b191e587ee47c4dfc9b7e9c20671528c0a65c08adf777c11e202cdc81916c859f379c585e82fcd7b5c7867f2280fb7c3005efff98bcb27d7cf0b4defcb74c17d2e211f98f5b71b422ccb25d83b98cc1202ae6576da559a5ee1949cb5821ccdf8a604ae31948e840d9c8e7b9132bc965d0519d69e848a16a2a0ee441d1ba13a43b05b652a1d998dbc71dd6b9e14cee72002661f63f3032266e0b0f66fc5216146fb43497ffad889d66de327821c93da8109a7c936140b388341379964b467c02856b3fe6d1b7426c0f7da60cd66b427a6091901d258a948e9360f0714093c1ce4ea0debd1304d326d614fc880df3800060e16744051764f1eed306fec5d6f931c2dd6d467b1bd258809b026ee0aa93c5a048890331996006fd92da0fc6fd12b851a4e57aca0d403b73fec5aa4ca258adb0f73b296ee48f9c0ef6603eceb929dcaa4d19e202f3bb529abc53591e93873280d83ceec6c7a4d0a5ad1520bf80f8789fc78fc3cc8b8012445e93432ac861a590473325f96b6cc4059aac708961149740c802c6971e8aa0ea78e5acd2ef57f2e0ef7a3ad8c47d4b2b55f421cef57184e760f0c42f1fc9a41e7a6a6332ad881bc0cec790f60f5035366844f44a16d0e761e092c2110b5c9f8f758f0ff498b8bb601544de8ce5fb3aad15502d35b615ae5296fb67965eb23c855ed4886a4dda95806d822c013e83315bd3e505343e61e1af752c4e1e8994d08c732c0504105462e0ce6a93ba83e716e73b6c94fdf20359c0053ae2d16267ae20e2655e83416f556041d0de374fdaad4de679ddee6882121cc820f8026cb670d8b50a0935b3477480fa613a9b42e2a6e71b7e1d7577c812a0b317b33eb0ec354d18ce51637b077ae01c98bb8969300cfa9dca719d747fa1e3be21cbd9801ac7d04a81ee8413fc1f4019d5245451cc4aace041a69edc012235e6c0bb82641ee8235e0acb03ebb3b700a021acf48474c94747a5a13296b17efbba4df668fd2c7a9a31d94780c43c0ec067781c5c728361eaba84516101155fa115113bfa251d99f365bfb9005ecbced0d6089f2b49a53b03b153f98ad7e506d433b61e4b13086baf34f3b2775ecb0a00e30b6b6aeb606fc2a92547fb584cfab7fa6a4639e4de9f30397548b5d90f267071a32f1b3060e13926cd7f3edd3a1770cb6af37680475b6c1a435503519d90e038403844f83397fb841f634f618bc1051b8fe1366366251b10c2c2f3e82d91d0029e9cf2f0f294b8ff0d1bf72090f720f8b94334f479486f7e5ef859f62ae510314c0e9a428ff1b2cf9173382d330ba47529767cd3c772aeaa6087841b94fbb07bbe309fd14412bcc5b32edeab572f51e5a00311eee3f473734cc15fa7ba674038a27bf1f0df73cb5e2d4b743d73164a5b3cb8200d9a65b4efa4cdb26422b430957e93b87bc6254c1e209cc1a27c15cde689b80de640a74e094e55111d3fcc10a1bcffea31f1828e948ba49fa80e9d04be923b09d8e86e2a30ca405e7a506aa090242dd7e3318be3ff67f8d5647539583ac23a3cca1ce500e9027d1cbe01b930028905d74c15951524b5500cf4e66a829578d1a85258f41bd82c39b57656c590e62ca792dfd788975f1679b67a1289db4db1659e576bec566ade726986ff8e10a2a9db7425cb3b1861fe2716ce74ae0a4256812d6492a3b34e5f28f46715089044b627975b662eb165c0e996eeec6c58e72f530c3bd1a81362ec07899b815c506e42700ccec4019c3b27b85bf7a12a0cac41a26c47be162156937d21328fe1a0d18b93249a51fad9c6a26237286293e28aed7793e44a34a3b60feeab09346f104e31f5278689c4dac9f579da739cf97c5a2449df5b4404468da344d458ed21f075986b0ffa673bd9674264c343ff20716bb74638395917dd11fb87a6209e87203f28c2f55e4d613dac94ca265707b48130662d7de4a7d6ad66c928a445ed4fa0152284c2ae8e1622514b47e8ff23a883806640f0d3bb2c3ef3b006ae16d7b2106ad8e2d7e2a33594d13bedd026a049e9627d91adce15a92c05cd3cd1f2806550064a1c9c97179d4f402873ab46225ec4c2b1473437cf0fceb85578b8c8330c8040c923be0a01323ffbec6809a85bed478427d30162d1f4c6f9ce2f3b8b637aa0e41027a1a76a2882c08deb4eef8c400797970ffee96b00fca66b895569aa4f7073bb2bf422162fb756a0835186d1da68a1a5f2a63e8c992fa822c22dbee40d8083576fb60345cf2462ae2a09cd7feb55f922672dfdaa74bd708e63f1a3c26bf065027721a8eca31473aa7f8e5fd68550c1e6fedb0abbaa140def967b68c173004a3a78fbf67ed4c435513282355bea9d6854fac15dcf0741294a41307d0e5830f938d8838923ea212b145d48b6dc356d5bd11370fbb5d030fc6b6616df3fe3809962817ad20b43bbb871ca48de9aa90042e94fd9d7864d64dc01753af5b02190665f38f8e6c533116bdb9af4fd248b362bc17b77cec3c73f79c2ac5ee460d790e78875fe7bbf2213638309afeb49bbfa66acd8fadc4badbe601ba107c97a90b0f0700fc754f01855a2405d83970e83d78293d81e8573f228f411cd52002258b00d1eb28bec88c71c9213fdc7375ce9a8d7b198ede9ec4614c663199b536c40404c0e810b2a1af8957a8809f6c6551bd0d7dfaab933f5adcfacd44511b40acb50c41f8d30b1a7a0ccf90abf02b41edea1188367945c2138ab56db9a515228cf802316903d4624c8949864d8ed3d145ac3e5ffb526446345b606f37d7b001486d045ae4dd4db6904e02ab24dc4ad6a0deabed09ad74f36469510ce00bc351d928010e9a04c3f9f90d01a42599867f6a035ffe0d1c7962d889b1ead85fbb1df8b2051f582fab0a3e4a317a1050ba95a8f8d38144eac19a87f4278f2cbbd47eaee7010ddc2728ac62c7735bc5d1107d8ac99d287899cffab765011fc65fbe11b6f207535ea960ef2e21fcd98603cc7d0c432314d18128f9473472f3b11b69f62ec10067892d38494cb322f2a5be36b43745e4cd73cca1959eff3f589b4afe2a411f066babe89ae15641d6b4bfa51b0128c44d1615c21fc50e103a88281d3c2301a00fa0eaec90851485be76b35fe48ded2c740c7076e6baddc2531c446e6cbedb7506b0a916ae48370541f31a777d7186cf8cea08507648dd31e8e55c671e1bb9d100d274d568f001cd5915c8dc3f507f09e203ba88c878e6a4e5659a6b3932dabc05be46cc70dab045696eb0fc18a0e2390b929ebd3ed1bafa6c2976b1081ba7d208a616b53b407f659cc4a76db4ab4284307209598e7aa66c956db300d22db92909de155f8619d20ebc3ae3e58e42b81d06c990f212bf9fe091208ac73164ec6d00a6b8595a5cfe7e095afdd703d05f7a84223e925f921befcd74d82208c0031c06bbfc5d713991dad458f106eaafedb00007f7bcd660d3785ce9d17f4e0acbe9026eb5280a3be5e7be37395fd38fe92dce05a78fcd3ae73d3741a4b023fdbdd9035d8cb1ae47dca5c5bff08496d3b531d040d65eb2cd66b007ab5496759b33500b2f0e339036e5b9163d983aca2c91af9417ce1367892df24315bd530f2dcb180c60ae0b0c7b8b121ceb6a1bd66d004ba4d2d9eb00f017f41924fface09e4f7101430af8649cbbd7d0b4f9a180cd421e7616697b272b8246e1af07b7af85b30b0ad7b9abf1f82c5e3efd752cefc89f3a9d21e457c14cbbd35b7b90b15c94bffd0ec3b28b90cddfdbd4bafc5f073ba5a5235be9881d497855ff5946720a9011c004fde8683bf3100975e279d77c609558427a2eb48b3f39ec0ce8f705307126cf002161ff4114b2d7159f4790b936d2882c6198483cd523593a302cd1d0e712180f9a4a9151a2b051d8daa3002f5ddbd3c32076702ead26a28b2f07c6dd69e3aa022c059ab46801c963563226e34801c0d413c780b794441724524ae732d5a8d60e6b1fd14eccca696dc2442148225267aca26896b7721c9ecb9b3501019e12660d99709aa2a22bfa093305a02ce5a9037f02b13e2d19d05d7f20160dc634df13004e3b0a78c8b30ec0e46f4750d58e57ae4c44ed5ba1998d7ab25dc51569ee7105c15b9a2e326fd9a177729a02948db53aec68265fb7f831ab157d97dfaf953b074a285523d65eb8372c7dfc42c6089127c0ce3119b12d744eb180c95fdc9f1a0ddc3c41fca77587e0923d6d447dd5f6209c004d6a8a5ed0cd544df46d9adff702dfa407c30f508bc92e551d498de5d5525ea7c10c51ea17c39a7ce0aa8983850a329280991d81df6ad975087720522261f24a1558921d90ccbc4949194b5fe00101e331a803b03eddb5231b3fe6b453135674e997718af2d92df38d984a6d6d04805c538048d62a6898f9466fd1006be8aff74817d15c8cd15035577c745e2202ec4781581c4cdecee96ac40c48d6ed2bc156643b083b0a76aab5e93cd5000a0af136f620ef54ab23f473f7ca09a6e47e4b11f3fbf5e4f852f12d84366d4904054184d2a6add272670d33f4d869b2b56c347123756b38f0662c28f96a7875d903b138d677738c58c1f649ac13f2f09b50ac27c46f3cddfd8418ff695d81879009ba226621a2baf68fa5e58b168ea763e0c5ca82357a6b6062e75681288483520e41f8520f5b5279df04cbd8f94416640ca021f45492c64a0b946484026f3e780fed1d5880ab92193cc0fd6c9e6a18f5537afc37ecd6c5c079f732a9ffcb7c7809 +generate_ring_signature 61896a85212b5da1f62493bde6c3f398172047c7de1217bd463249a3279193df 5299414c59e58dd295cfb332156c4e8a17ec1fb1620af10f78831f8e84566aed 5 8b13feea0e6ca36f0097ea7c6194fc688580d03f7f0a598361826fc53174b121 75cf65e07593def98a4118a5cc71bdfe21de8af1f9dfcb282c04f51670e095a3 ea619ae3829c7c333e81233a036b09a155131eb468344645e858009d717040b8 c74e17ad7a90d60251d719ac913cd99c12d2ad73a49e19cdb4bb437fb2495d83 6d474a2e9c82d6b3d5435b9f1579006820c47f8e1e52a8d3edb640ce7fb94b42 3ed9a47536ef7badd75314470fe987114bb45f3c57276b92d4a39b02ee0df20f 0 fcb3ea5174dfcb522f33efd80f017c70f8a1bbb42bf95cd010fa923b5abe090019f14ab92275b607f75723d28d82ac87c955d630fb6d1b704692ce638660a80232094fe0ab36cb9b52a87aba4c089a6fc538f68bc0c4e9927eb7d2b37a6e6507c163c06804b7dae93e01b1f022d9d5d68c63538ad9863d6555da910fd1866f07d8e339a3fd727c89ff5e44f766e6f08f50f02b20763729ce3dfc64d312fac20e9a873e46c0e5ab4ea4608956845b136838bb6d6d5a694b89e9a24e35c0d96f06e3b87b3056e8d5ec464e2d1f3ad34e3b535fbd354470b3b6477d72f04d87d50bbaea78132b9fb9b9bbadb027457800641849156ab7f756f41df06ef0197f230b091653ad06addccd2ce3dbee76a9055f23f0d13f330af4e59e22ddae93709b0b724e5cc8fe82a6206a764d46f36068f2b59d73ba750b9dcb0874ff615813c702 +generate_ring_signature f1cf87ebab071856a7aa1e2bde40bf53cfae6bdc542dbcb3694fc847e87cc907 c67158809dbbbab1a1a4c6158e72457420ebe1a8df0094ea2e0106e29407e223 1 af704141088e5c5d8055e20dd7c20fbe4004b8822002cb3592d7295f511ded0a 056f3879c501655730a0b15de5f518a62343a1ea00a6b9055fdb03cedb91e70a 0 d7a23d14ca76adac32887419478b619b15b4ce8573b2ad99a80448c9e1028a09d4eeb5ba5b0af677f65d737c12a6010c00e447bf8410a11ee5243d5b45f91f0e +generate_ring_signature 1d7c174b7546b5c97109b162dd0d74e488146dae2d1c7a5b0148d21d75395d45 a87c4931c4abfe502dca5f05c438d30c8632cb2e7a33928e62c916569aa91e0c 4 26f59b98e4b467dc339b10fb006bca53fd1890e1ff544158e80502b72551bc45 c13bcc0b5c7abe2595688710213ef314c8e5d12a549493566ab5de00d85463e1 5f6f50ef8c8a9dfc6a401c6a78e3e5309f81bd1239e320fadeb858223dd2649d 632a9cc8e2cd76924ba74762f78aa415df44a435904064de4367de0463d63082 a2b4622fda5df170a49ddf777546dbf42bf1fa2947054cc6bbdf54fc1ae44006 0 e1a9919ac7f510a9ac193dbb3b4b73518993c3c4d77118e1f8973ceda36a420e003fd3923a86e1a9f84af5e99b65a437e7ab27609588ea96f88b7828fff0960572687a972c283b649e84c18e47c32fd830cd48e3933e8f08ef56a1f08e5fdf0338ebc30f7db86f4db86cfdda1b8cf304842582dd1449cfe9ec065a685d0ddf09a49dcc7ec3a311f0dea71a2c7428a9b54774ea16848eca0fb9e5037ce5f6660bd12dde4adbfb96cc167580965f35718cedeae022129bc4cdfe54dc5880052908037769137b6f870fec12fa3ed797440a62f1218072fbaf6c7edd9d9228146d0aa2c87995d7f629413902af2b5a9e333c55db3b902c377c4ba1d97a77205ac200 +generate_ring_signature 986b305d52926b5b71c2d83e5f5a38e666026e3f817656d6648ddba7b962f446 2423b386335814549d66c0f2047c70f9923b888c139226901f94bcb428e6183c 193 64e28fc878bad3fc0302c9af1de6249e7c73ddb6318fd526be4c5b411d15a481 cb55a8197fe4ba7d779b9b15d772ce7255b8bc7d335ee9efab8f8e701408eab3 f22b6ae39b90dacc8dcbbc5e10730f075a737e15e08b0481bce469c21e6fd52a 0a7fc1e6469a7c2c4e6d14b76698af6c00566d43b2ac680c618d0ca717ba5183 13b7593a540441f7e0e616a5fce13edbfaff8ea9545454ac16e46569e55eda55 57c4844e918c4d3e2d1cbfefc1ac53ea95dddbde3154a1551a5cb4fde2d44dd1 db8320872bafc9fc4d93add93749273b693e8fd256ab003032b728586f7be15b 500f01f43c2a8b449facab3daf9900a313fbbb9a2ff49eb86534afebbf882b3c affca8620416774b53d3cb628762c9911f3371b4edf45215470b8fd314cea5d9 3eb2d72c85c677b663ac4a8b224a004366e85ed3b71e84be900f83de97fb5684 24311e425993402ca1704e89451d40d37cfbf0f876433865ed713085276b3cf2 b687a261886346c2cde09fce4328fb824521238aac8da46ef05897bd489a4d3c 314ba3a0cfb0f96de7abbcbc0a0987e0429a0ad753339673da161646e5d3a4d4 e9ddb541a9f67a1f26fa54f82afeedc2ab53bcf66ce8019f3b700f7caf4f713c b5d539e44346cd981ef44013fc705fbf7f3438eca1a9ca9e4f86d4e613a46a67 fc077ec3e1362f6c8a3ffb88dbe01e145597c43f367e1b0b416ea10a113f6079 000254a12224da500cc2e321fb3edc1057457a8e03d8eadb9adf4c7b000ced48 284c2fcf80b47d00666eed994629da99cc716e005d555b74cace7be279fb8c94 3a1efcd10b992cbb83f9be34d5f9ee331b99473edb329831492484efc2c6e0a3 5805062c48a0d3c36580c33ca648fdfb36b902e399f2b8114aed5636bbd8bf27 4cd507c565447375d85bccad43ea542604703860ffe09a1afe345eae40c71b03 b8b0292f4c2cbe100f910ca4163d62f6ad484a20b42080ff33d533898b847265 97f8fb6bc8aec28fc68759c37a2d4350399ed6585ea2ba539cb8bf6bc6f3cadd 085e05a64b2530858d8b4dde2ca339b002615e6ab3d2eff31e8528732e04f8ce 6fe3b803d33f007e9177afe8c1880ddc81982cd7a9be6a14e0a2ca7d9a3dfd09 c7fac859fd3854ed256c9462fcbd4b96b1baf44090d9565198336044290bd527 2cbf6ab39e1536abcbedae4c65a0914c5c998fc4c7dafe17ce418d346641c3e1 e793ff7610e6f9dc08247c288263cf1c23391c1999c5a6812232e136b97d5585 685204f283cb26e1d57f94337a1dce7b3da673ef9e2126914acc8e1ed939774e 9754fd8cb985aa69aa9ca9f95c1018dc46e21c59539c01c83e2bff5d01e2e745 7ae285957b36f6008fb99ac253baeba750147a2858c68fc8907eb763e27b7cec d9f0205b971834377a4bf552438e4e7bb3cd50c0c49829ab2cbe3ad6fdcc6e37 848a8f27906eb3e3973037192f2b99d505131baf1df3d9f37865a457358d9f3f 396042ef40c52c4400be7507a5a54e5efe96d89685f0355c6273876f0e744037 85825d9136dba78f1f9404512493b3afe9291ef3c87994abd034fa2087e30b32 a0844c849795beff8c9d18ac01ed1935bd8cac72039bdaacf5f742c4da7b351f 35672d956ab3e16dad5b3ebcbd6bcec03f3d6310e26188511b837040321a44e7 c8e2352bc6773d82d9aab8336247d6bd925604b57674a3fb5b0e9563bb0f04f6 1bfdba6bcc80507609151bc313cbe199e7099dc98612dd17559e7ec28058e732 b5e84e4493759ac2028aad657eb6276d407c6ddaa411ceccf66cb7def8e7d424 b9cdf793bbeb434e9c42a5dcbfb7e2c7c8bd402c26160e9c11014052d3bbab3e 2af90d63dbd3b7dea9b27ec5078fc1f8e2286312e4c5f544e631a82d46d7f508 22a1c062d43a0d02e663d0d765f787b5396a1e22449cc5681de74fbd608f0d08 78752a9ea01297340cc692102f6fd3fb506e723f773d386ee090ca24cc6f613c 577c5f1978ae795b77e1861f5722f6d9ea1e6e9a5dd09cf7312794a8b1d78b6c 4704592c1b870b9b7d71fa33ee592e3f1fa103992babd67b502f96a3be255346 54b399319deba16fa1fb4d3bf56849743d5a36a4bdff2ff75492a57cabc1b6f3 442f0990e6ae7fda7340c60091d1fb42c6423d948e2883df820749ace9720aad e17b4b51958fbdc4339368c8d04f9799b333d12d1b4ded377c2cc8afbcfe9dd8 04741e62e3f36120f73ed323a51ccbdc6c1c80df17c020185681e94ecab74884 35524f9de8a3003c07d1c740ca47e96f2524bb46c4f8ae2a94130821250c0cd7 dfc06888ba564ef532a2dfaff9c15c3b6bb8dcc8ea4f45c4b5298f0216940f50 591016984aa045a73bef77d7430c467a1e3c24aa4e028e1c51ffc1a41fad202a e77b0bcd020d224f1832b0df84c33bc1104d94434b5af9c76cd6b6ffe3c8a90e d66280a46b332cabb68bae17c16a4c3712fdd3d1cf63b21e3e746db678339efe 420d01fc4aeda6273ec2c2e1f4770edff919d2eac4e75dd818edcfa00309541f 04696dca8c1c8a058bb60793beaf5b1327a8e55987a9b2a8dd7e7e11c6bf2b14 5fcb869db02467a4f6e6420bf87a58913bcae79db0b2a55e7a496f0e7e60d4da def638c612349f85018429095796035eb50ba3faf7222a482f13fe739ef6baf5 3d5c0738274a2b9d55cf1b5c79e0a5fc019f77097bc327df757761d15b043682 9863e8e7b8fb741c5827e656e882e68fdf836fc9585a4f8cefe11126d14e1695 8720326cf7d07e65c4a31c66a82f3f34e735eb9e91a2e61f79c494f4429588af ac0889322c47f74dd438cc2e4331d72fe08de50a072cce97fb750022d88637d5 dfeb8e94fad0bc6016b462476ecd7781135a7019de578d1209d73dfdee3e6bcc c832c8325f9fb02b6b9804b7fbae4b04c8ed43c35277d2bd6e41d6ad366306ad 89b080bf099dd2d1559edb24f4e10c2bfb16a5d263e6fbadcc0abe5ab2602f4a d14ea550caf91e1e30b47462cd2380c2a3ce21302a75746eaf90ad4016b13178 b7c50efd54a775d93f508480cb7b22a02b01557fcd41ee68783c1e857e0629fd fe815f924dde57bb42ca56328723e8a4f32d02f7264af4458e5440bd4b590bde ca78f4ddb7a6322cc968caed28afa20f4a63fff68fb3f02a7aa6c4440f69f980 a0b618d230cdcad36497ecd01d64ae07cc362059fa4d66b1a387367aa5f8f586 6a5c7b634717b23510ed56342d1b456a6cb34785ef3af4985666f157cebe635b b9e391fb8382cc6b9d790ce4c08ec2f4b6aa43afc5be27d06dc24907b96d6787 f9304fd7a93f3d28c8d6afdd0833e7f51bb68887d48c79b08f87e0a77f31db5e 205b81f9f25c4af9b8966b5b7ef6f8554edc65c2c6f7e96e2894a8dca39fe74c 66b1a9540d226728fad397b4a11b8844e76c36af67a070e4ff7c178264931755 dd12d3f20df22f420164ba1b555843afd24b5bfd6d329ba22779ebb48a6e0d16 d5009193f1157d241c12d0fae4fbc73b9d1253bd3551cca53bbd8d16bafa9366 d66697811740af1dcc9e0dbe112f5370c86e4887db4803c255ba818ae4d32cbd 7770ef2c20b2b629f2e122333fbf271ed4c51e1c058710eacf75204d9f63dcd7 54e4cc2723d809c570cbc20c8eb6405774d8b7a181fdd949f64397a20aa15366 9b84255864f51ec99c66d9b116a427dc27b8aac9b761e5ef783de98526801437 a6bfb8c09e36e45d2fdc8a0a7dfd88ad1d108a137c7693b9e20a4249440fdbb2 6539cfca92335382426fc5a284f24f82c2f702700664480a1d8205cd7864e64f a41085565173017f5a0f7243112eab7d32f22ab597c9da6d4c7b3863fb3c02fd ff79b8bf0b6ef4f27ae65844e68768a36bd03df8458c7e45e68d7996f78a79a3 be1946365221457e839e1783036433d126af7cee9209110337d2b04c679ba230 08f8cc04bbaab7763ca50a5d8df435d362f250b35117b1d5bc2e130d1bdadf40 752319a164c63636fab8a3c14c73ee179bbf82b1739f50d0c53999347b24bc70 f51f74be436df6f696b52dc993a26ca0e67b2e7a12e9dc7a6df1cf2865a16bfa 136461e6d846725df8c120afe6333e2dfce998464fc50699bd3c0df4e22ae673 d9176e70938e896065dbe696a2cb2c23879dedd43b1678c1dccb2d8e5fd347cd 344aa03b1747d9d6979a4c5f0af9194b2bbd7065238afea2a97ec8c620764586 28643d04dc5751ffb768aa981d9c97ab548782a87c82d430b439f1bb27020ecf ccd0b6b7a96d0903b7b687a9e075918ee8fa52cfdd9d8032ad3b7a6bf3ffc8fb eba5b6bc84af2d9823e7becf4c68da48c3ce605b8993d461899d1bda5ac28cbd a9ec227b6292243b386132818322aba227decdd192b784fc90fc229bd13be008 69ee69611e8439d1d672f87ad2a7a585b2f081dfa964bbfca0e121d33cd72403 2bca475d09d5b744bf85e5c31e7d277aeddd01e303a9e8b927e366ce86034417 dae99068482c9327917505154e7b4ca78dfb625f3221d9ff9312d09e5425b54a 8963f95aebcd677447aa8660d3718334cdd181dfa676a66724cb2fb58fc1cc78 4c17893f719a410992b0a93f14a81117ab92abeb28ada8d849508f701bb19a14 5543466a2cb8fbf2cf10de1c089b7b2f8c858e5c95a94e57d5de93ec7499afbe 1306c8f567f0b28fdb51059ad579207cbefc6abe59186f7512736bfb9fe24a1f 435f293112b5c60b1799803e772a61ee0e6522d14f521723dd042e43df06c194 2fcfb7b8b2e57df707f984990ab640c8768c2320f9fe0e43f2db716137efad6f e98b8582b9a7f416814c62fb38910dc99304f9b555b07d2573a0e2ce7ab281cb 53378be377fc5303c69466efe2f649d8cc34c7da90b50988bbc3aacb117ed4aa 3cb88922835283305f809a31d0127b8b5deefce5c65917e95f5585551dced831 88c03afc065bfc401c7664fbdd7e99eede515f159500ea72351715a6c5ce0eaf ea82eab3854678002f3f3c1c4ecd4612480b2557e4eb99b8e7b6267fef41fc62 cfc02a35b32e88a8906e30820bb671837b1fef0cfc6ca4a3ca0417e45da3bd16 c98f1b796a15bcc38d3b98ad9ba052ffbf7ac11afebbab26c060e522b12c80cc aa54da86075c9e11a1c0f5bf51ed7822f1c4a7a2cde734b3876bfc3d79ac110e 0d92bb97763aee40cfea5267ab4227a3a255f585d55f140021ba1f498ce36b22 bb55bbf881057c2ed62075f04957b50984be540df2667f899eaa360276616da4 fd8f5366296116056933ba2cd2514e85a061956ebbda20daa60cd3cda1ad8080 5f9cb009b2871ac122ba37ff3eeedff536d8796dcc000934012e9cc91f4a73d4 4c4fa07601c4885d4b21477c7184a9f7876f166844778c8807f4dd18c5cf0ac7 35345ff032c2acfdfd579b36f3088ef84088cc41d823c3ef1855dde3c0062c86 48c42191efbe04bad3f704be080b58435003d61ba23cd4dd59c60b92f5de7980 f643e8b1b22aa5e3b6715ad247b469e5da6a6fe536b43fd3f8b36a16cc3fdef1 c7898235e8d871bf11b805065d6129f87127efb0784a116e0348be7b355c1600 eb6f5b76a77294d0d9f35c12c0c83fcea4eedf7938e4488ec11694f4403d604e c0282efd7197e7e04e220101d8cb761d37b683e59f5a7504711b27f041773da4 ff09ae5badd85ca9fefca91b2854e3947dc17b40f24d39f7e0c0270ac2c99b89 22a7a33709cf636e34837d350f4d2f7b66dc4e318e6a4236b36852f129dfe5ce 6ce1ca3d8b5999aae7ca24e87fd805a15c051496e45ed09fe2acce3f25792982 845f0dd4c408a0d2a349a9f804fa14a0cd78fddb65c8b54734f494c85f5f28fc f75f1c59283c1d315e5581ecca8abd25193fef1dcd97f9828a8dbf2d1bf8186d 5305be3e55cfd9f798ba2ee390ad6098ac22b0ce1d3006c98d9a2c1702d71d85 11744ab074326c301aa9af30b8bb5dedd3f928495b2b03b53f0b129de7afa3d1 d8862235799145a5cea8178737618d3231fe5ac11c9d5093aeb5ceac644c3037 bc557520838873b12e8dbbf70e7ca14bc688b02f39bf3c29bbcdad1d268c7ed8 091897349eed69b84b4aa192a3d8725ca8a2ae4e760d016e281bb44fba0ff7bf ebefa40bfb8cc775af6ef41e7f0d2fc13ccafb196720305e3132865f372e2119 50dcb3dbe8e62ff76da60ff618012b792bf6cf7853c2c286ca6f2195b20e1db7 64a518ea8525aa077b08a639257f725754ec831d8292ba9511efe3b14270319f c6f21bab1f8eff164218491f9e1277680b10dea72c7e3d2106ab5ab0211d84b9 b1949624d08991e9443c454d28793633e89617ada815a06ede17f6102ac33e34 4052d987d54300512218ff4999780c1c3b5393d4feb35b2d8c2f11e9d46cc0a2 01b93ca78baab566a722a2213ec6fd5088263b8378e84959b4b4897528151e1a 5640febe0ed05ab00b61cabaab5e58898341319b41b572bc3be3aae4ce3004c3 f9beaae412b2e3105e70fdcd308a3ed99d5eaf7005fdef1239c0c12adfcc5f76 44268908e6eab168c28020cc4f4a6c5b5934c06a547d0ce4870c5f30b1fcd87c 2bbebfaa782b4005cf83af75562d24e61ca43b1e182614e6b1e7cf1d4093dbe8 53409a490922309f5a942abd914be97978f4ed0914cc2b7a3da99e9fd7edca12 973a5ad3f5e300bb6132d16ee323a1d9848c0de22fc35512d9a188a42056c1c9 37c54ea8856ac154fdd9ef7bb56c750c9c149bfe6533093b8516830ce4bfebac 18383da51f765878102a2ffdd09de39d832ebe1e84725076202cff88d83e80fc dbdf750721c305fbad03ca3e5271c347ce902410e39a0aece8cb345dfcaf4d21 291f1b30322c0225de46053339a192d13b8ca23b957b2f220852e6425e67bbaa 88d8f6460aef971047119d3d03ec215fb013874ed8c6267f1c9474a702a8b667 a96c88dce3a98d63127ae4c77243ee809697356341d6de5ecc9f7403ed13a8cd 3d3c6aa784cf6f00a702eba750d03b48c1d62b49be83b9f0b45f8cdb7939d356 0397c066f70e3390ce87de4c41608f5ea19bbde701bfc2b76229160e6212b1df 9a90977c569ead928187cb0676de2d8d3795da40c25d125ddb09ddfe63ab627f 299d3205372c8903d13a776797a6c881094892e47a5f28a9a7caa78c3815f68f d8387a3d00699df06fb63f06f32623d056bec5c548eefb0364636c69a1831a5d 5138bc0daa2d9bca05e680f0a9a8c1766fc7ddde39fdd13e82c63e096db2bfbe a6024e53c975d312cc054fecf253f3491d5c2a1c0e9a11a404e4e0d08b91b364 49ca80d97de007314c20306f036e66b5afb7c7248816e0e232334dc9b30bee3b 9fc243e5635d251fc0e02693a2fdbbfa0490359125c082ac16fa40de4c65f180 059356574bfce3ceb71813d4a752e4515cfc951ad4020ad6ea2b019137203d60 86395ed9470248613e979a41b0c44b92ba323b33c153e74ef99552eb8eb89012 57ebacd2b29767fd62b98c38ddb2ef1ad144a258945942474f78eed0c129da4f 496d64efd2b8d83e91b0b782b89272efb8eb0efad2b4d9d322008bf3330723a2 fe1fe90799a6871ccffd8bb5f21e25965d24cbdda29191f69b65c1a1456acede bdcdbc5607cd0ad3a4890b76ddca20aabba9c3b272947d539021597b245426d0 2b7dd4e60764a4207ebfe43db01213d832353a17a1f15713056ea3e0aea64085 8a07717d7f669302cef445c238f188e947d3f626a6ba08477182d6fef81a2506 660b86422db154d8551c6d8154a82eb2ae8e95543b31a942fae20efa8dc84bb1 18e2b9ba3648530ace278e0ee994a08fe1eac6c7c3d19452ada5c0b5fd453ab1 da5e7474ec602646ac16406ad761faf04fd1624a81f3ea78048ed7afa5e013c5 6ba04704dd03bd353eb460f105993807e221a75f0d33109fdb39f45fa3b5ee62 a3e4932b10a1e419d342ff690405d8d768d5988a8c06d5784970833d14fc3452 6480f8bb34dfcd585ef7715bacff2bd5acce57df9078704484fed323200a29f1 0d460e9b988916d2cd969201920691f13c8fef8477f8f7018e1555c8daf6fdfd 34fe4aa04a327335e35f0af1726b32a5f6b558da2cff30bff76407146bc12d57 b9627d113c6291cd204116c05954add2a121a500569bbc9492d033d382eadb08 d4f88642bc6ac4719aae6e3818456fa580d46c1c6dd981eeaa6a4b88a936f252 d9edc8b5b42bbe7bc7b157ddcf91b78e56be484fedc95004ec70b2547dd8164f ea126989974f7d05a32e527b73ad83cc2b6a0f19c98f686ff011276681a35e2a 0360d77921fad68248513c80bb2fa9b8f91197d6ca75f112cacb35da034dada6 03f7f66cdecac677729107b071b6468c995d94fef0bb06ba8dc5b9d5a2e5a244 d7ebd618d0a49854db73c78e3068c77066e6719e4357375e0e33b76a38c949db 80e1b0811da564db015bc8ef4760d3f122bdea4300d2480aacd88055124dc690 0c837ab425f84fe713f8e3047fb7de6fc61e36ac72f28a7bb9d8fc6bcab99888 98a9e83caca5108f10dcd7e6de1a90c4ba6c56f12868443d9909838bb6b38a18 dc0733bd50fa2658fc99def389ca8faf1e6d5bcc8595619cccc528fc0da37a40 574034f3f76014c6b53c7f86b9a493784ad658a5678c8974f356ec76e79ed716 4de27e24fa2d988fb5afd8c49b7c20a82b4be9fbea92cd6725f2f4446b4ac1d8 71385dbd117ffc251466920de20b230a36362efe877dd27b2b860ee6cf8b8c63 4e2aa49d76a800356b60980eb8b2f6e29b7613d941b9da7e535971432c397e00 145 dbed76ac8a8fae53453eec2bd13e440925a3744260baaf24bb5cfefda6567102bdf76195eab4991612e5d9932d95ed0fc064e4c0a100f2cf9ae4ee1ee071e80a3da9bb3a6df41005d841bfbf62d829f83d6d7d917e9456bf430288500c1bf505e8109cbdb3f937b6054e14b8658b3d3b227c21e57b4f521d44c1e6ab6ab6a60137345891d2ae84e620b9a3a0a9cc14006db20b2ef222c449894a41fb93ce4b09f1dd5ebf355897e35b56d87a131df647f7db76e3c36a71572d288d95134c4a03ba74e02107582da9e4c89ad539562f49e65c93e950ef5ad208f32e070c3c4f08c62d6bb907108b39081223416cf2d010e20a4db10237b6fe8c6768f986518a07129010a952671fbd5f99dcd5422d44cf947ec5e381c13ea1075ef79fd368680406941eef448775f34e0cabb1abc82737cafe18b7ad941043ecc4e4cb715e5d0d309169cc93dfc671b046ea9e4d283bc646870e1962219ec90429f3b919486400c3d4200722cf64a3dd17b6d1328a2b9f630f3218ee79064799726f9665d7b801ab7a7da18920372379d049e7d54c35db0cde634fd5d8bcffa8fc22c453b5d5082506ab1ed6fa16a9287316b731fdb80432e6bf86519275f40f37b202b7f9f20fe601b902a0aa8d910116d8e16ed0a74a3bfa426b2b901eeb9e8c372edb07640988fa05a67a83d70d15ed1dd34ee3231d1ee29c390394242b6033761b2ee7a1052ca5785475fc734bf8b2ce9d18e797f578e6affac7518be363e657fd4ca7290686e68267a79a07a867dca4e9759989f8367be88ad43b6f2f5b4e72ffc0f9950a139aebd0823ad0ada46548ea2c89a1fc0ce420cd74de6a5cd785b819836d0f09f12b71f67747c833fb81a41f2c273e0100f97730d200b99d6e5fe24c18e49c0f5c5fc8a27efdb301dc287e0ab0d7f896c77a859324ab47afffb2e34e75051a0cc2bd1393eb19a78ea83db16d298fd40242d7c639f8c650588c6afc640913ca0f6e27f9a09277d516b06cda2f1a0aab61f6704f626f171dfe08f926351292dd0515fbdb0f8d85ab0690294172bb170f0077cb792440da0d29d9de18b8bbde50021d86e13bb9488ea273fc4c0c687413317b63b2b9089114fd13bcf08e00aabb00f64befc2e158455f02c9d6f73265e3ced5ce4a64cb3640b159bc12dab0c55f0456c4aefdf7e0c2ffe6e47b12f0fb334c0e94249897e6517a9e788d78e1ea9003672229244cc5c6ed0462d9146ac424260286097725de9f5df9c13caa4e1662088399b9bb344117311fa6c080403d58126054a54daa6ebf62e82b0d39a1ba9a0ae3aad114feab804336b73da9eacf15970aeb378c5d05a658447a73732c1ce00c7a622f023d310a00a277ee9deefa7f388686fe6b51eb4dcdc74af0397fed60073a6bf2b9130342360e91d9bfecdc09e6714be01b5ff36dff8867afcb24d570092ec295789235eae689af35638a3d9efc618ecd88dcd0d44d341463049b0d5b0401a4b919ec8cd3f2d79fa4d4f1dc0ec8085532e19892f15c37120c83d176dc0e33e15eb63ff37516a694b9f5510e9ea9c8474fcea2a45b0f130d5eea2fe0b70a86d05f17d99185b41febf1d16df22aaa9e755f49a9f30d896b500d4eddcd900e784173bb369b08618e67ac50062e97a164dfeec8ac8210dc09e8e9a0ee3efe086575e9a9cc8a05056a65cd98f25bffa9e702e849fcc322ec903a99a5bbfdd40799dd2c864d7944cce27f6528a6e4cd541adae59d9aa107f6b1c9f6602cb80d0c420e6eda6b1d23a1839b6ba41f63d24db06b63d2b6fdf459ccbcd41f754c2804daef6328b6e24aeb1c3007dbd2440ec490dabb082a626dc6b0e48a6aa56bc1073160121c530035b696b3942a63262a09e6204ee85e91f19af6ecc52c68be9103f1b8c33a94dfe4fd332b61fcc274d465000a806d0e5f92f43e83c9d62d46de08db4ffa975087a990cdfebabc3167ca238a15fd693712553087ae3fe0d3a13b09998b28e993bd59960daa6934895c773dd62f3da637924965cf1f55fdd0f5920ad2435a942c1a52ff66af5495c33ae776c61ba1bbee26d8886629cdfa649f6c08e03a42d051346a1a8de61429293bfc619ca87c326b8600b59a845ab8cffb7c06da8418e324bb5922a96c2faabe64f40bb8c34c2f4650863b9cced75ea8ccc2041ac4c690cbc711e670b58235856883acce6f9dacc0ec3e5c70e5e5d5e84e5e0612aa06819d17b253de1d1e65254dfdc94429197ead2c7da584092744a8a689009582e94d5603c13f2b32beb79547d053a59f020de992a1bec3f0c44bf85a1c0e84cb6cf0f74542096600ab63d0ae0b9ded2ee08ca1858ed03264531e0bff8101fa328b54e088c3f974f407c5dbb2f6b17dd88d6c0ed20e0eaa98a02cbe7ad50353c7d241a51dc89aa62493bfe9e36cd4a1decaef486b00e3928f03cc47f0a80db8ed73c9fe552ce3f9448a8b195edeacb1f6165fd02e9fb7e92998d679a7a0025b2b45cd759d567db7c7dc7f271f9bbead1b76332b5350c9366e79103e020e05b3b3c536a22ff7c58ad8442183500296bc3d7a501a54df81d37ae89c53d53d00c3e79f19a7b68bc32c8599275e8e2fbeef776ad3bd1233f89448c14aef20ba03eb5260561ba2cc96e8554965d239dcad9e3bb7992bb21a3418c18e23f699c702f22e4dfb5a97df50455265b00e28190579e730b14757b32384e4e01a19f3480f1d025852a8fe42ea6d83aec259d25bc2d7b44d4c1f893de320827ed21b96a40b3d4f8ab663808f655caa935b7f51738d0c758a21bfa6175ddfce7e27dcfb4b0a1d02161e78ac17ec36b83926d83eec437a9a427bdfb31cce375a5e536994ff035c69ae446ffb68c5bfcc9fe773e829acd8e28c45e27e12da39134fbd89fc540dbae1b298dd5f3fea8a41cccbe770fa63d6037a945bc1aa040426e0f63fbcd60f4b59b7f0ed60424885781e78fabf5d714185e396b6cfd0c02322e8152bbef504624f3d98cd9d0436922b260a422b63a65f6a84159c6ec7194979b0f53a04d404cfcfa81716ef04b583319e56398fa3f6efaf23b3d75d81aa4f4cea6017c242063c18e8d2b5aac7feccd78ab025b29f2945b1dccf0e6ac2a2db1cdc8799a1bd0486737b7f30d4388c1adfae8e1b9aa17913cfef1d4ea96c629e95396caa1b8a0bcca1bbc540fb602918524cfca69a94c977d25220a66eda3da589d4137252e6026be6b816f6d5242f9b0313ab2a51e287f67c4ac68d1a81278085518ad1a17505d999cd4be65bd9eee63e3f7cf51b1bde9c68d64f147370a78a49ea5986ed340efea3e645ee510cd1b71d276427c22ea41d9a1da6a3ce24464886f3cee0a1550a675122c20a5c889ab3d0085097d6e1d67d0f6a6be6c84a4007fad968645a6a029c4f1c5047e55226605f005317014c6fae5605d91515b91a50c482306129b709fa5ea891297eca5254a1a3576c83557ebc6f6fcf0e8e6dd806a26078cf1884020c13c84893f37e659492657f7aff1337125401fac939f3887eafe995731cb70cb5e3de6b41c823eeb22c9815155fdca9fefa31b50321329171a34b72a716140b693d79a7c051112c631e1ababf9db4812c0c82f08cb684783bf34ff0c82ead007b335a583d8e945e458957a0c490984d3bb21b6e03e75933d4600083fd55bd046150a20f0e80d7d136771366425b3286fc7f303acb07ab4b272c2ddc4bceaa03a3695b175e3f875f1ca1db89ea78075bb5265246d3d3aa1f005d8fba724d1f03b49558d30556f62ce56b6ce430313c31e1a8a5aef22d5696b66fa192dc8742098603f9cbaf0836ac464844347720996aab3fce4cbb7304188bfd2183fb5b7d05960b2b86e037b178757397da3df2a2d22d326b3c06905eab5233f349c17a1507f05f0f2f538fee1a36d63c1cc898528c66240acde0af5d65b8f4f24c80dc2105def448c774d2ee1a929671c5f204d738bb1114bdb511d94780cdc109e5f62c0b88b414d47a9170f12768a8efee754e0a41de48020cc8204890b8c6fde8d6ec0596d36e26d2a504d3c7d0e62a3c25b19eb9c2b475311471e34d98402f2a1a89023ab42257f0f17724f65c555467f5b8200c02cbd577aae544488ad3c06171f00afbbbd35fd3758db69107288f0a8bf2b20d53597eb00491091910bd4b0711c90324881a947369bb7c32fbe0fd1c5f2e4e99261c167d535a1fbeea0523c7d8f9061fe4ca26567c8e513682be7f5421b67935a4de003d1cc73a2893abc6510f4c05cfd2ffcf9deb8d9a99af7a5762a6f654fe6c05cb78051061da13470a642f9a007bb79642f3161439ec74875e661e3cf3ca255874c53284f213a72f24c4848b08fde251d94d8ab069d25e1dc74c84cb60ba35131af0dc93559c41f5bd2027730f63d29b5282119011d2fa551df84de48ef6c9416aea59e63f77058a5ccdaac706ecc6396b098a599568f9db2dde6f082ed4de9e9108f3f6df59f589cc85a0900d6ea76d5c8e411de77a406504171ed43a0a8b431b0825df3ad3165fc2017eb207e954154a2fb2761493ad1305002de113483ca521101289747e9cf26e515b740d1a9f8128babce5e6d71dc371975fbe33c0cf80a392cbac04527cf1ab57f3410f1ed1efc87793a29c363eca914ce42cf9fed09dedb87abda96aea15c19165b30d564d639ea310b04e5d6025055297869a4b6ab30f6bdd05c94dad48b9fe5cdc01b49c27ac48724e929982d0f4ff2a4243df4caec5c200a7f34dbeed231db4ca0acfc33484f65ecd0c5a307605b75720874fd61861cfa6fd4378c09276e4e94d0ad6f29406bdd5bf2d35cdd8611b5c659b1288815c5890d6c8b9b0dbdb52a87f044565f8f556ebd59ffe09c5aeb28ef4e46dad07ead92c842788d76ca1b58642057a0002d33fb13590c7ba094f874430008e4b34a00194fb4a499b16f06da40206a25bd4327ceb8761703b54728b9323f52cfa6c268a2d20d2755373c2fa8dcc06fb5bae009f8e63a37568377e43fb2b2a7bd8b69e6bf7de12bf772ff1de8b6b031cd9d18a36b4f4d3ab4d983fe5ca9fdab035f843a90ac2d4f59cde8a6fdf70093efdb5dc922325dac958a9e9ec994567eff9375d956ca0177fbfd84e5887430e3e143fca78896a881f80cf2089644c8236fc5b14957952872e7a429651d8ff0240be7c0327435c02808b0141a7a9237f058ac52989c79d84901d201f0b7c830047ff270eac3ed4c42ced54d3bc9e00c86bbf3942e14e6f4570fda2c4bd97a804b35d131590839f91938e8e6e24da8c39875fc1d1fb4d1d7aabde2ea12884380ab497d734f7af5dc2f5ff32a694eb8a4eb7da50d699817c9e60032c3eacd9bc03c089deb294e43a29f3ff30139f694e08910d799999027366a12a4cfadf9a65067c3f295c725bf95705cce9c373437f76ed153894105e019d7f51823de2e87309f9ffa7551627fbd74fe9b44b990c6a4a0917255802397e9aef0d6babbfc6280940c305bbb954d218488bec0556f1dec9cb3db01920428b9f88c2d31a4833e60b7230778f064f5124c4d5f0bf239105476872f7ccc1cf4e1ae31b4c46b5e02f058cf7b52c6065d8a2f5bfe3897340a0b95929ce18ac437ec7f3ff406e7ae9430525bd08e34dc42bf2faef1069092d712cbb6a0b2867ca4489523c9a4454bc54084dcdb576bef2c203c0d69e7b4ea7733e11e66ec47ac98dbbb5e1f2a413c44109d66e4f8680670fb9f3826bdf58cebe94ec79b28c8c629fbfdc3eb95d32abb10203347fd3343319e873e74f65f10c83cba16a48e01f41c4e01103d0c0996090010be030d518a28ec6f13a8e143f8b0fe2c153ed526e3cd17d65554fc06333b00a1472c6f18c6457eeea8835361f4aace6e06550bb080c38009b30ccd351c925028b2abe5793a6fab12a81e72983c405a69eaafd87d6fd70a40b8957ee1b83370283fb95965ff3f7655f476ba2b1aea34f84e5a1c6a7a2217a89094b0afc42ed0c051793005a7780286ce1e8cf2656b717d97893f92f1c48be16e7dee4277784035d79261930be8d0953108f77c98c497e77ff4de0df3a4c1734fe206777a6350c9519ba9a2d2415d2c71d2792bdb32269869a2a6496a47eaa78fc3ac5ef0ee1072591aa253d1f6a94f65f0b6ab13321cd23b80139daa11533108f8d1e7e177204d644adf51db847f7bf1b133b3b70f5f00ceab6486c24aa0322ffb41cbbcc2209a77bd88c9bf9ca35b142d2c4ed277ea9e14f072b38d8ce5bf3c62b03b69b1c0ed8d5365182e12054bf419942f5e820f92a832f0f738da6accb5a3914eb98d00d839b8527cdb674cfaf568688283e653a6703e7e7c086963854bb422f88f93004ffdf6e3044c58438b20e3c581c8f2bc8200784889e8e45786a061c5802e66c090e7ee13f42ba367584395adfb963e3035968f30874b38ccd2130e44c4257190ab2b1e65e1a76d99ebf9e6a3d6d1ef428926754d594f86b0aae1fada3169d3b0547c8aa4fcfff9f4f9232e647ad1b841aed82bb7d6dae968dcaedaca4bf6c740e88c6f84996cd924ef183fd232ad867b264f4fc0d1fefb2f92c4e3097a58f510f415533a2fba44fe7e4a7690c0a4364ec8a29377fc9fccb12ab06a22619804505c310598cbef1c4160b7f8c12715d38dfee01e277cea0d19caebf46bb3ec89001c0e23e234a0b32e44f67cb8a9aa666895a24ef30deec9c37a691685c10f4c80cbf59475fa9fd8ea68fceb648d800904e9b2a130d6826c783db64b774c54935035d92772ad24a1d390cc9018cc3766dffd512f20fa7fd5da68876d9470a38d903c9f98ac8fdc96728d6a58bcdabdb55ae3fae5a444b644c697de7b65ffa7a8c0cdc5921f5a0e393991986da0c972d1036b90c7761f484d5c256a999b886b8d7008fbaa611000d94675a0f3632a194989ee2d96aa2c3a9fc87add0ad18a20db303197f81e4eabb1c30bbd60b452dd1e9470d6205e7ae87ba1655fa4d7b0b506f09abd71222a07223400c8891a817b97ee90c31b8b75817e844bd9b8dded408ce0b73f18e32a0aac86a70c28c1957017548a286ffff395dd990e83b51f5b06bfb064a2b6b5447c0ed4c7c9f78c821b34a816533113d8c50823c2ba7493d8b45a9028f5728952f838fc658c88cdb7f70f3c1711c08f784ff3a8d2f12457572e65c07012f6ab3fde43e05b232cb1ff3f07ea3aa4c729772b31a786a732895310d7b0ba4140a542a0e170bf1b66655f524485822d300010930f474973e4f72da06e80e6c0ef0940c63c64cdb28f7d718ac4f4d08c5e750bfc2093f18e455ba12283e0286dd152ddfceb509087b7c7cb363610d6245c346849dca061324914b82aee5037c8fb8987ff6a64239ea2bf4f4c7c20850f2d50a6a30b57a96ea0d948a5d7107b50ad73b0ec51b63ecb848917ba9c98e88a56eb62b4889841e8f26d44be08600d53b93593edf53b85130a0a2d3c4ab582953889a4dba291614b4fccadaad850f335043177f684c2b0f9343e8d189e618266a554ca48bf1dbeabfbf8f006a67005c35a0fa188b14ee0f29b96471de60246c67fc8c01c839834df41985946c17054278b8e5fa0b7d0ab81d75c8bf39966aaa9da353b82956a0d8ab4834b2542408201bc38880e05af2474b51e099c6148764d78bb5f48004490dc6221ae2a6d207e55c85952edcd0e6df4f24c39021bc91550995c7bb563c2b65018cd38de24f0718b71375d637e7edcb053514d3f887761d02e61e2c7d8bebb2117ee4abcdb402404951b502c9008fa389f95a23880dc64c1b5d64c58d7e4867fda8d0ed9d5d0fe93369da0e895566b33d595c7b9c03e6a811bdc58a7a9a7ccd3675c7b183ec099ae90ee710d015a5cabb137774d5b570656eb754840015cbe8bdb558b53f450e700d69a1817282ca1e6d479d58f3c92e545454b7cb0f29f22af59277793817018508b29b34bf398681d550099450fe97f61b3e1b36914757b2828a54f305ff083094411520e6cce06fc2253fb803970ed5837e602cd7398f6fe5d7ccb11089096a1c66e919c7c8b030d6ede6344fc1c4b686bb41bc3153b2bf652e925fc171078c37b939bbdfbb6ce9fb6a58d79585db6c8a4d14364f913faa6d68e969e2cb0338af4c102f6bb422c4fee4c9714f1f97c0c156ce1c60300917370ac0e1e677022705af53d2c651e2458e32934f6723151b223ebd39f0b3e518fed0af90057709f472ca0ac2c9f2ae294ead1981c8f9f2b9d4cfea5d029b1215506058c95b2501fa0f56914df186a298d34599c4d6396bc321fee4d97fa3fabf7b41202560e2015eb7caf7976411c08f9d4e37ef3f82bffea3d408fff2da799852dae71e2c0a05399d3c122b44b7fe3bf7a44379e9eaf98cdffb77b2b706365630c6f469aed20e620f4c3ce593bd4d65947e67e111a82e90b9818666b1a67a1697704e02a5c90e3033d37c159074777aeb2bfc8811f2d2125f2e42e537e4e5f743cf25379fb605bcd020c9eed3bf429ac0f199ee13bea6d7328c3c1a39973fe53927d4c437d50aaba207f2206627585773eac7a32282b72070f111e187fab30d790f4d6f0fc40ae74c690e9defc4be95556c78b16d0f9b548627657f727f4866dd5e8052efeb0b7ec3902cb93a3bbfd40f1d9e5d718e052164d04cfde9b252024fb0f55a655609c76fa80c94d667c2ad760d89b6be75d267d8f9287d0e48f5722315f162e5ff018a416546eebe3dc4f2f47e8f69eda9ad1d4e181aef2266130593d456af8a220b4d02f05191e1faaa1243df685f1a97f89a0101ec53378e21dd0c59584a886f037f58bb8c82228bed8ce31072ba88ca03f56b6151fe8ed95e876222032fdb5e024eb6bf117271df247607ac86c7167f8873f7731cdc0ed47140ce9afb9d895f025a70cd86d1f33a1e2646c6885158989aef4cc75de7ce1aa5a8226bae92b7cb074fa459c3bf7dc1938011cc17287bb4f61ebc9ec7f84be311a0114bb8f476440847c9a57acf1d5aaf3ba0f7fc4f294acb9e9e25631d3a36be9ef65193782db609d95a673d1619d30d6e6ba382d2807f1691de9cdffeec2fa931151a3bd52c170db25e56471db52abf6df7c1ae6d19078a2bd0c068891822a865bf6934579da60e301645e238786d2885cef96f6bd304ba75ac810b6e0d6923386b9ba7e3c1df0d5f69426153e3fd07e75d4991b1285e0108c739ad3eba73615eefaa8077db710216481919d402d0841509b5063463352b2d1f13139ea043aebf57d747845ba708147f29f3f30977449bc6a16e1a69d01f2e77d9bb5cbeb7a4dd0e0e53fb74600cac706707688cbeda01227bdf523e0b6f82a53acafee69d0ed319d1b8caeb070f32f75fc8bb28a312fe164d3db529ba3687e183316b71a5a287ad22a226bc4c0bee922ddd44e44f19d61e7b676b2309fcf08f16117de4e1725f7cb1ee6c86970905e56153f4646ae302f3d3a0cb573324f8779ce81e544ff21515374080b00d0c5ebfc41fbeb6886ae697cbc63481ee2dd15718ec8b9fa5739ae35a3695bdf303e713d34a237499b5a359318c2e636739145dbab1150881c142221adf3f445f03a3154d2c1c4dfd4835dde74bf6dfe6cd0cd98778d55ad829abdc89b11c40e2014bc60204e2a48cdfef521f444816ecd026da2028702728ea691e1ef5532d9301a70d40db26fcfcd1a91d632118fce7b2b997be61b1a1d28ab0466b7eaf30f60ea15f8c527876ebe48a26adf390881c42496c8d955e181c9335f1def53f48870c59a26589ffd758a59e6337d6bbb55c14ad4a4b3f4817e33bfc9114c0243af00b217a16a394eca1c0d88d6142077e924f4f61b88ed460544f020b9a2f74fe1a0f31fb15266bc32f0fff802f6fdfd8568550c55af2e6b4fd0b40e8a1f3c3d6bf0adaf4eeaec18d496534b2502379d92117681138d874be826c9fccf9c77deded0fefcf2ce2abd33446a32336081326fa0cb80ab0ea78996e3f1467384a79b50b0bcf6ca3b371ee663b123699d22f454640853b8726fe922b23b89346998a2e1109554afd193913792351b8d55d37ab399008f7d8453f749bed137152e43d66fb0e5d5c0f0366845edb99c8f4494a224e5fb01c896c6cc4cdb82763cba631904a01e13cad259be3af480d0c905f79e2c03bcaeb4e142e7fc631cc27ddceaa8f9f03e5278fa7bf7bf12ca4da0f8291c04f532b44fee200d428a17902a6451d85ea007886a4f37a31458180a45b07c8870f2a437099666975c8279b04efbabccd990b3ee4f34ec0fe84b4842cb561071122147c0c0d53e9948c86da63b44b39c3b909f0e8bbd5684895adb237d31cd405b6c613c1c31f92bc76498019c5acc91ebc0ef54365dc38a4acfe21a932a53c10e754b253dab5a80d10b6b411bc1de9ab4d0436469c105ade1292082e86ce2e807525684ee792f5f3429768ecc101e1f95e0259bd25c470ce1d6f76c401e40a5567abe34da52b150ba3b9127129df0cbd3408ce271e8235131ce07da80b33aaec9f4203f18201b2fa3cd95d8a60286b440302aedef5e7ca8af6eee8a3f1d62f06a4ea34fc251aaca3a37a202e9f24dd46c606ba7337837f234e22d1ca0cb2fb9623a4c60e573f6ca9bf00de0e4f7032589e0046c7d22e0c8aafa73a76165aa3d65f66939ed957c0bdc41ddc96963a8cd6be06c17568a2e068494638bdee02b7e70cf8e859c34829cbb4b89477a286d41f140276f5f7d261dffb7679f6965dcd3281ac6b60cbf5418c71a42d43ea674260f403c5f50849d245e34f363af0f2893ae92e08c31e664aa4ea45fec53c2fef2b1d014ab3e291404d1713fd813dbbc964d234d37512d6845dc63476b095a95b52120a0df384e9b8226ac400cf075fb5535e418771a14b9c0ef60b5ca3504dcb2fef066377cd51973aa814c51692a96a0e42ea8b7b40626e27e6c8132b5e9070765208a639f54f0c8feebe544944a1d7c896f4b85ea62d3cccd333d31407704c68f50c3634ad6a2dece210008e8b7285973b9035a5856059fbc01e9e43c4d39a2eb00ae3d846ed6ced000606df358230ef101522cafe33a7799efdecd0f04713a7d30aa7216052a7c11077c94a4735d667873d58a8d0d9aeb2783521e1ac396f1ffa0ccccd9fa2211e348341b422125e07ea581faa7c8b0bc902c77287339956cf2f0c5c35bc17a3a7ef46df1a6ba920a762a638196d7429c90d1c6c20c0843ec8380bc10dec115d6d41645841cc9384fad07e7bde4bdf1ea5fa5be13c5f72ce3ae003da98676622c697722fbc33d4fc148998b1eabab882ad83831c17d51dd3635f0fe3a279ba287d4dbfcd216dceef7cb04a7c9af16ca60de353cc0f78425559dd0a41aab5bc92126a38ebe42e5744c1250c416997acfa3a6429f82ad12d0004f90ed6ed0b8162b9dea972ad3fc6c2ca253dd2eee6c7571fefa52ad1f4a62b082909dde6de61256824a56f1919488cf8a21eae5d207ad19cb046ccdb8339ebb70104e394dbb5b49cf41f01c7f145b0d7a3e40420ccd78b11b43385d3a9525c48d103a391020034d60f1479e6b04ecfff58163a9703e6d430db2a5e05c2e8cc776803313e097215bb574e8d387f0e6e958dc1bf32c4d1527341b20a5406b34586b10f7a502b44309aec3aa8b4658384a2c29e6b1cf040744f40484006ba5517e0af08e6de13956c35012a2212d0abd5c38e6702b796ab77a586e30d5fa79b8701100a33c3778c77e90ed4d68152d328f1e007d70b656521ce77f50f9f8fa0fb79ad0ba3d80c1182a9f27ccec964123070b5c10ed1c4e844278887f5bcb1229d5a34087b815451eaa9afcd821fa59f3965a9e2d8ac2a91dc95691f94d8807f65f3c40aeae8f55ff61316b5ab3977d81f72fa162b4f1eb60d2bef4a115f282a61467c0d376b771a12b794b350ad727a78b86b4048e75a85fcc01e2cccd9ee05deb78f0fd707a4737c037eb8902318bc91e2df361f3be5c9f09f5844107b14a3f20afa094308313da5696203ef0b9883b0fe2fea23bd0b6d7a035ea4e7c6da8a96d0900c2211ebc2301cd886e0e8a98319eedf153b61ba0962814878314267c41c83a00f3bf85aa4d105e1e1bfc7a4f4b09a8cef0e17ad332bbeacb29a6af5c5d90e6c0cc41c87151774c5af0f1a0c55a81411ab5dad5310d3ae3a0797c705fed75eaa0741138e57ee09161cc1287f0eb79cafa65cef01777105cb73292b27bafa5e53012e6982fadb8d7345d6efcf4f95a4459397ae222ec8d5a8b36503105ea7248008ab8d2a156ed644a589300212b00a3ec09e97a420c149f059b44db932e62ea0043315c39c9f2768aa0ef4decc479fe1d18e1fcedc3e5279229fc4cafc8d0e31095d4544b4bce3a990be0e1ec74ca1a3c269a0328b766efb3e1f353d92acb27b0d9765a4ef3b2b5045454eaa0005b4f19b2de4ece01f84b59f373ae58789ac520b79926f3abc7a002ba534ea493fd10867c0b834d3f9f45f2d83d1e931f331bd0641aaf483e0de39c9ba8186fb1f07da63cc37e7f1d3af39626dd6424cbcff680c77a5ddaa4ccb6a6d00fd41d13a9e47276982dea3d1164fd721961bb928cb570c955b4cd05de3a4194bb531e6bde1172207625a6fca15829370d9f45929c15c072d3bf21fa99ef40e49a19e3ff692cf4afb38aff3d3abe1a66c84ed7d27973504b44aba87a4182ddce8f0dfaf349e3ccf5cfce254eeb27351fd215fe423825d02ce7cba29ad86cf2c400e1d1ebafce24f70aade8d7a98839bb111963b43947d0793281b2d4fa5f54be75f793a195d76fc9b0c7beb78bbabc3b89c84ff66c4cb0615f871535e6a7617dc389bfcbda1e7b9db77ea87d0c6233840f4010e2070f0042df8ba862c50a204e1eb1caecf86688cc66e5ba71e8f4f4b77312f55382b8c01c4854815af233dfeb17b27a2a081c1f170e0f95e7cef001f6f65a9b83b2a7e0a018f2a048c6d8bcb57a7c532ff58cfb6ce935b32db7e3e10a4acc20f971df90b323bf868d320bb75fdea0a8f970adf9b66ad842e714a3d6f5b90d27d449e710179fbcbc65592648d3ba8b67f10e3d6a9e71b0dd15d9a73ce119d4093468c900727596287bcc669ebf12d8c6d8fd0e8cd6d3570a68783adfa4abbfa859b777903bb283dd299e41d0ef3e5ff015a9699d2564d3e8843bf1de7d2c8c0253f15f20627ee25eba1fdc6973a154768060d7626fa3a22b748d2cae2c17b3ee13a2cea0dcb9020c37a08ba57629ed747ce88c7299aa62d54bff755bf29e06c17a74d100f27db839bf7c0f1d42f29caa3e8642996b6205ea68ca2fb30da2f40a4b46325055a32e041fdfc039e5b29b2644a3c7626d9cb6e6095da01db8d6e44d86b332d0135ab0771b69f404b834c2b723a76f510707fbf1221a3e18f16c5935234995f0fd2f3b62ef930b96acb8335a3aa40f14c8bdf20b8a8d1f234e2a032f285aeb0044f4891b806f34b1f02443aabc60dd05f1192602ac547b0155d12cf8fa905920018b26ff27253b45390775eddade641ef7c66a2756fa6e7c160306ecc4bba8505753f3f48c304202abcf70925fe9af9397007afc075123c4f4d42c369b1180f0ace7cbf781cc0f0372d23042262378fff0b1058ab98daba8b17e7cb5630ea2506f430b613cea48dfca2a9671a708b6e5214281fe5ac26965b3aeb0f461e46690e6ccd21782369d1cccd4834e8a184fc37b45b09884a0ccb1fcff9383bdaa5890dc5f2fffd8bd6f3aeefc07bbac6ab0d20f65b291653d17db1a1d779d09db52c0def5de303ae0d97b5090885f99546d31ae69bd47ee409f26097c8f4a5969bf009cfc94a95d18ddb01e39beaab0c94c7e995506818abebb6b1e091400a0ca34200c5c350e7550b9f71e38270b248a45cb84fefbc51de9de37404771ac40d1ec20967a662845594dd9e003d5dad9b701920a621abfe6b4b4fccb7707c54c691780b4cf51e7f5c9cd298ac981e6de09810f410901efe04c55a90b94c944cae18cd0bf8ce9f7c9d277c6c575e13d118a47b54c3b5b2b52152a2d3ab80c149f5aa6d06972525680731c577bb1549103ba6c0672b049e6e1b5d486edcbb6fe934ec260cfea700f7a61bab79d36dcabd18e2ffb5b8829637314dd05ff101542982e047080d822b6eadef2ee0bf4760d3e93b72a06dc186321db63223bbba93077828990f1facd2c3065c18db8d5469298a6646a71553065cce7912d06e2ec8af972fd504f7a0d74a3b7899554c602e22a052c96baaf09bcb6297687411f43276a6688d0c93d355256715e444f6df4100325d7969ceb714c30aa8a7a5246d56177552cc0e1112afb1f63b4301145372e15b2d9d027cb34c6c8b56333cb26bfcb12d70720c41dbe6e2fb0bd45d75edc16e1228d9636b7289fb66ae78a7fbdc27c9a4e89d0231a97afbfded3dc16be2d8970ba75d49630263c32d1eb890673fa507d534520dd88d53dbee52cad3e9ec03eb64bc07a59325fecb5b26d1b8614bd9aba638aa03ed6a26404afce8185e09c9bf8da55df9447c040e5260b1e2d07af339372c820a91f87aff238eb5e05ae3562d9b61c9758a03ed514f9d78c4ddba946e664f73087e65b73514237e934d639a3ec96fda115c2025837624c9f481e4303f56984d0b3cc918c7115872060c0a822046aa16e91e371fc049f20cbd58833d02e16df3069795c314a4086a1318a454f803f946758f17b3459e93d95950f208757c57bb0e560460344619f9dc7fc65a61b2c76b8cd3acb63514e2c27e52f89df48933fb010777557525ca36c6e09d5cff3a20d48a4f06312fe7428df3f65e1fed2f7aa40c0bca7fb56bd1592d5fc61130cd585a450188ccad780a1c4278234d1cff7b860c383203bd61c4975b143bc9491ed6f8cb9ac60b50559ae994127f77222c66db016f5a8f175bf183e064ba6d3d1e67786b80209cd13425923fdaabab418291ce004f12cb0392c804843784a9d7ce2ecc6dd98fbf8ca283c8a1dc6c68877d5d3b077ed2e764ba1065ea606d005d3f45f3f479b239044b4aaeb820dff7786b11a801615d25a8d4c3f8ed2d5b0beeb8e96a1afa8bb08ec76a323034e2f1eac54c170e652af71529a757c174fb8d908176478e26379048b366eab7d90369eaa0a4f7021784b43c708d2be8090d9891c465f4f14929c4e112bf1ea3ea498bc3ef8b060b837c14b2849e513363e10e3456b0bdf7c7d1207910583d5a46cabffc7796a20431a1348b8110982911ed3d76141d6393b38196547e292c3e1173f31e9053880f588f6c406392301f0559531e6cb3e2be8d612c9a7d85eadc117e84c9614a81098fd8c15d57171df31055927bc0678b8ebb963efe55a84dd6daae0208caa6780f5dbddd4cc1d8c53fa7d80185659e9285759aad1477cb6b31d4134368244ea301a85324f2b2e37f653177c00097f76202a8408ba3f7f71da128fb2aacac9a1e0dd34645ee57a5637c4f9d7bb4bff466e37941b61a29908f59ba8f933215cf5509a8abbb61dbd6a2059e0ae785ecab78e22be66f54503e7f92cb835cd49a24e70a506d3d569faf64be49b16cd3098928d8f15e9ff4e175f19809e2f69170be720189c0f563f897b51c8dc815ea1e618a548ccdef6ab13fd410a78c97bf19806d09bdde5c1baadb233325479760d2e9a2cc2b41349a1837c88183c698b97ab0e60708f38d5d482e121c674f328dd7bed325631cf182ac9b7daa21b892c605236107fb72ea761e3965616150c53820094ff47c9ce85063541223ccefc8436a7ef002edcd7ff8b9c3b94746b475ad2c9ec6ed06f1ea16a34388a33a1b8752229f6a0cd2200abd6bc0a1e0c66b120044cca723f050bb86a0113b8cb33d545bf2c76c0b49e7e59d7a5d96aca6430a2fc8d6f7313fb0803a8fe275f717d64bf47222e90488e63838f78ccf02e73cdbf6bf93b183e2612e2ef52d33f19901e700a901ae01ed4b6b7beecbb393232086b286a971d4f64141895a687978115557303526b800b0994e024ac2a5ef0a305edf4b8244885b6bf66c99bd5988534fac1a05c2bb03f255fb32636af263a3a950efdee0256d2dc80ab3fa01b7a5efbdac157f17980225776604b093c6a566bc84d6833e6e1c34108993a319f7d9f20654de1bb27f0312c1e10b5788fae5623bcb4a8a1f5f44a6d4bbdec3f89917dc7808286cb13706f1629bc98313d9078b5e7bbd8147cd3be8b25c19f8c881d3680c178cecb7710404e59da61333b4f67ce320d5349a3e34eaf1f76254683d02afc3c39fa8be0b0f9ae372d277529d0e834b226e44b8a73a122ba6780ca5aab1e19f92a07a3ce30e109f0858af0b240a8088fffd8df3e7c897a500479ea4766c892520a703f33d0ab7e489d8be94afc1d41002c06719615b301f337aa91d5bf305bf06d16ff6ec0aa693cf10dad21dc4a0074d35bdb3927acb41091202d912889c40d5e1371f03039a11b6bbf6332a2d25b00c3f9f05e19c576d4634ed637677c10e5c33c82d840ad1bd1c9724d5d7a78ab61ca91f9e9d60847869abd14282c31ebabd92e815100af699242b66c0cfc01e2450265adfcb720eb8c2dc60f3f176b360f2897f895e0d2fb14d4554507f975566507f306f6a7570dd70c8c4bb275aeb3028216a152006f586fd1b5b897c486c979592a8305defe8877f8c5bc2b05c90a31f58792ea8063704b4898b21b8d79f72eb6af274c2affccaa29ac151eb8620ac240b23c3380a46f4a73070fbae23562b5057cc725652ec25e39464b37074b8ad803d523ccd0180c2cca183ab2142dd0cff84a07d12214f0520b3922b21d97ffff6965db01a063b16bfb6ccc16997745a6bc8064db6268b52c7281b866252130e7f4b15ac2b0c8ae7f62dcc97dca951d830f0ef98c2db95673325bdb2b2251dd73e63e6ce8207cbb4b7f2ebf8a21a7b8e7712e35335a29332fcaac8a5bc92fb29fc0eae15690f02593c0e8d7c05a21628956d831a3f080308436fca23387c0577d92d5e1f3e01e9abc7f674c52b38418a36d0cbd2dfa891c7e729aa77a20c3b3532210e7d1204153241075a897d4b4514a5be57be16276f928ea30cac29d874dd4d25919f8f067d91270bf5dbfaa7b8cb623841dd0403ec8c6e15317dd508d017e6197bd96105aaa4035abe226d16ac93e7a69c5750cc0d0b279f514595844ddee04b80089408d22e2efc5097ab41cd8a26e867714eed25276ae358b2cf6e2b52556f6b20e80f245fcd1f04070fac7479b0cd202c12956232a359e9223eaa173abef8922f4e08fa4051b6dfab81ce2fa0a53fedf1e7aaa89a957b0b9d19ed946e2527b54e63090f969bbb87d8adc6d8b3fc6ad60badefd9f494b91a223d43b131a6c5bc98540e75ad7c1809e87859f69cd662a40fce9f01010f530f604a06cda6e613ce2b5e0e4c4063b547686fbf836434a5890a52817f503a3b1b38dc40e657215a98a1e20fe157c282b8876dc8f10fcfdab83368bb6dca7d355b24c9028bc3d0642dd6d50491f24b1ceea7d4626e081166f62a8e2bf309adf32913049b92bee57d8d85270e +generate_ring_signature e25af5f99e924a014d5fa921eaf4a260baf331c5071218db719aa70957d26817 6df77719481c359b2f1c5c3b0a900d69787c7af240af076e375d46baf44d5c31 4 00fbfcfc49320fc8441e8df9046c5b946905071c80582b6ab4af7e5a79edc0d5 48d5cd9eccac2c92920d03320c0547d0188cc9b4b1677403395767dedd4cf4d5 19d41738d4645859da788ca93a525fdbab27ae7a032f5ad43d64a17524c85211 efc3b8996b365f99ffae62114290fab6ced7716a4293e4d1241c2698fb321af3 5c9f762421cebfea2a8bfad37ae4eb80eb49c6ea1b14b9f5a41cef33f898360a 1 3d938e9aed155fc14a2756e9cde4a0dace6c58c81b0377bdba88b395eb4f900eea0d982732ebd16b46d8b61f2d0048330458ff239b3b76cfb241c7030c9181076b357327ad55e3838e51518b6eba6dcecfdf9483b59602dc83c8512348205b0e7b7ac82b20a536265f170d3084dd4a764bbb7214fc41e1d35236a8e8a8c6fc066c65fd53ade2467d9d52df3cd9a3890c52daad6b00be240bcdfc9842e83e6105c574764b1ac442c2fb02513a708e079d83445709a6256d6168e90e996117d305be654d5f438d6c2e2e8a3ba44d27c5fabb12ea5d381894bb1837ea1830454e07e2320c14d488bdd66545a65f9e49d674b240540ca879e9e0885cd376fed5590e +generate_ring_signature ca4abf11334faa3504ac8cde062a2ab3972afbef65e01cfba1963a458cffa954 d066ff3c75a37cac70871cc327307ea2b49ee49e05518165c53a226e6d25a3f4 4 29cb588295916a6c29f69a394ce9826f801a96179a4457898e14e7e435cad1fc cd9dff0491abbd8c927716ea692d254eb06e0b0e6ca6d0148e4b04ba6b298b12 f35c43964d423bba0a347d79a58ac353dd4bce7547e88caedfb541fc4d9408c5 acb9d3fd3782049219cb3faa410f1599a06c8f1d8fec65a4ed7ef2c2824b5ede 6ce03c967e7fe1a6a7a61cf1b5af3303d33877ab88c3d4c250f17bf34a4ca003 2 4736b5019667cb70d04c77f8cab2a5b5ad740cfb0fb149a558b57fa3f4232704900fea6fa000d9f1434c82e37048279171a645ef38a1d2602638694c1bd5770773693f0e29c5e9e4ec2b5a093dd81d73c81eee0a49f2cb66132e13fb1f993f0b8b3def3ba00077f32cb191edf55a7c36bf6eeeccfc398a7e7abb6734cf85e2056314b73ced7d5c8b49ba46c7400640dab8e065a87e067ac175e3e74f7397c60f519444725be2505ccbb0c5b089cda7158a16f724115469fa67b3b6b4dbbeb40a920ad50dc7c3ff306ddf1a22d88a86650c867e6f79be9d95bb9f0356a103870718f5e3b9a2c08b676929eb0192d66eadcc6a615070863f0b78a964bdd1e4e70a +generate_ring_signature 095e8ecd386c79e4335ffc97509ba3b8b068685d06b606eb47b1e8d581ed3cec aeca28ff966fd9e816f5355386936bd344f2f9ed13bbc1c1c4b8965ce777598b 208 5fbf1e7d642ba16000a65f42e52f4fffd6c5475a65ccdbbcf4f27aa22fdee961 d56a7e39e138b16e8c7c7d9cd9b8e7ec4538dc42673f06a1c213e5bec5bab4f0 8bf295aa5b24a31da5d0d717af8b421bd87fdb5d31c03e85284b85ea67fa8637 7db9b425939f019a7cbaab6e3d81f97c7df68178a4f6be6eca2d77072066f2e8 826455f217dccf76d91307b714f7d06f28a6c8d4d287995d9ed55a83a0162b4d a87aae754b8a516c6519abcb4059dfba2528384a96887c5d2f2cede20e34f9b0 096a403597170bc40392e812f6bffff3e0949779465be375c547adf8b8ebc0d5 c141b4cd3a854e623f1b2ac730515d7b76152943a8a4546fea83b4e8534fd407 644fa227dc6c98717d9cde9771d5ee279b79a87a4714e4afa070454b6747a561 14b758c4ef965caa54f7670e81c362a5a8a1a4674651e9b6dbbb08d61bf4d46c b669dfec94795e89dc17929e1e0601125d02dd99178b9a3b284912ef17a3f2f8 b20cf32a7d2d376b66c07755ec6d18f51419a6bee4c165ff079fc98dea5a2c23 5a58b6e6d9b306b893a4ced6ed85fc509349bc4236f60b00e7748f6653a2cb91 4cdb685d5f285464aa515f6336073eeee3cacd01ecd5cfbc31031f9ff3e69253 52fbe2a309697b182e148052c10415d651161a5905d03e054bc660dd22a48e76 c1fcf495a9d0cab85a1b35f1817beefc870c7d17eec3992b137e11ee046c0ff8 bd93de898495f902a7b02fdff94497c98613a25515e52a8e27b1c30ae24442ed 49d8d805b1d6074fe4a947e2cad8f63aebcad474e8d47538c000ec989687bb9b 3314ef1b62a1588158387db5f48c4cb809d10b0ec4ec28410f782854212aeae7 8fbd3967deb8022774ee9821309de664b12b5165d24fbb2ba97d2b995e8ae541 b45e89a232115c034ccf38be65cd0f5b6babbe7269e8d2cdf8f56c2ae50098a1 3ae4f37c574d18fa8a91e8f0aaf17a2aa5327b44ba31cd989b6e58a5a3266b36 dffacc3b202ffd7ca1127fc294652de08facf3bdf9cb0435ced36b75496c5d2c dc35c4f9251e1c347aaa63b32143279b36e13d2de4a2c6a7f348396ed2e5a427 d483bf6cc045a0a2de28e19b72b2b106a903b62f9f7cfe159a72aa94e853b2f3 63d5d6e0a7e5cfdc9ee632890331aa6c9333762d0418ceb7986d3a7360eb7c4d d52bc567c55c3e155c813a3dd0c1f48bf39e5b25a429bb251a1dda5a822e9052 5a6a6627d04acdeda2ad734479ef9aa92bce0988446c4fa2acee9c64893a3ac7 6692ee1f2a44280b0f40ac10a64af45831a75cc1ed1021507532685f3e1440dc 1c46d22ec9826558bdd987fd8258577e8e2a4bb00305812fbae61e1450f2df78 69eb10ae030b5d8907c14747a55e294b9be21dbcab8db5c2a2e6551f8694f925 915101bd775db1dfde9ac57c192738f1856d1dd30fc6d64a0febc33042cfa4bd 3a7cb200850fd7b6bcc72d447dec02a911a8ee8df343ae1d3c3b90d52e645d2e ebe6e398bcd3b00f57f97f76e704659a2c12754feb24dd771c1b2fe6354a3045 393a87c7ad8da0defc7c58f12cb18cf0f526535f9eb1aeddb2c35eab3bd5097a 5b001d93bdfa026ebf336a0bec6b5d2ff090207b355bda797d595dd8993fae5d 66df875ac1a355803a4c185c30f7835c6f1ebf5f9727c33dcb4f940225d522d5 49f3fe9667cae996e9a102db49aa18fb41189afa7416372862eff0141d3c1636 0f69b91da77467dfa4c013132a81f3f548d75f69a382720c72e85d985d45674a 95033acd0c456dc482c4bb0e47392352d0a2c72840d427368637e8db0f898fab fdec053bf96d547151f274e389aa5792d08a628913dd384745456daa51f375e5 aaf4df67e314e4ac6b974d5a8bc05dc83a26727e2701b7c565f8fd36d9ea0605 0fb4f2d5d4b990a6c3546370d86d929ba7ff9b1c76e97808eb1cb90ac22c965d eb752d59c90f7e9a6af6f383732c3b758860f4ac32dd9d1937f665e132ea5bd8 ca9861b546ce013ea6c7c1f0fc38438dd9114f08fae1dd5bc88936d16aabfb3b 98bfa486066063ec5b3f09e4891df1e28b5402d13e410c109e4fdb3302898b10 3f8137205d18caa34a282f2e71a2324c967211efd798719476db046277e24869 9692d540d6af84f75905cd913083459c762e7f285ea76bd7184f2b7289aaa7db e66e9603c880811f325c12909426366702e7900aca80ff541d1d91a14c510c6a 865b6763584643e595179e17297b7133a8a1760b795a00b6f470238fc9363d77 6883301dfc5a8d5e99ab2bb0e78ba144d28cb6f77ba9c36c812a667f66b50b03 bc7967e36d68f187123971ce609cfb5b80bf279da420da6ddcee15648ea14bd2 f6e6cc0a7e2891b0b3e019441e044f1413a24e49692420cc1c88d5b20c44069b 3dcadfda30a24f73587c4ca678476c0e949bd5d3caed59c647e111716de65499 dac9b0a44649ae9b5d4f867a92f1b4bb26918f7b93b686a5b48127e9a892eae2 99510275c8d89d55bd6855bfe6b6bb05c6487d155e107a8ade988a0a9a5ca72d 446b11e9e18a3d9fdbefc200861b861be6aea56d928742c57eba8d640f27ab7f e7a30a4c1d0b9db6ee90f550a19fb6f733ddbefa0ff418dbefa32150d7b9a82d 95617475ec4da88c1543b28d1f3f36eef86c205fc25a11fdabf0fa88d90a047e 0573508bd5e7e610b73fa20924d3d48152302457050015822ea9331050d1eb4d 28136fc5bd90095da1186d93ac663de4f393dd69b2d18ab1a7710047f782216a 1e44dead4ba0b7e339d973d55f971e9db660155a6d987f2cfc4334c0d474af93 43aad2dbcf6a292e3c568016b40fc8f52badd661f660e49067fd9d5b7671426e 5a92a918002e3e4199f0843f2eaa8748ccc38b06f40ab6112b8a6792386279cc 1793c78bddf395b099365803ef5d7a874da72825c1df0a360abe00ef8a5d2d0e bd543502a8f305a5af39b06e492a3409614851d1f7fa8033fcbfba2dbf954dd5 cbe78025b0cbaf42282596e5eb3b7715c459f3cf36599ee932e69d5c0a3152e8 fd5bd4ebb04a037f51208c684e204669d8117a0aa6645880f2f33782c2a8c81b b8bbaef363589df62cd906cf1c920d28425e2f8f032028f6c1afd9f29413f863 5346c33cc202c3db444653db0eebbd71d6bdced71580f612a0d2c709dcf0c3e1 a03a1cf3fe560f683c8a6540ef01b090ca7d2020e4d53a2aa9b4b58331b39a84 ee38978c5b616571ba2cb52e6857c7e6ee351359c987b7c35b5a8a954bec308d eddf667e5ebe41e00680fcd08620da3db59652b46cdba53eee578d2608d4bb11 6a3c8b5ca8eafbedd7e68f037a10a007e64c69ca334cc2b0d70fad83453c007f b4777272d3178d7851faf45951edc6db4ba052334f78c8d9656181f310edc51e 038954837e5ebefdb05978fee56d0174a7a4f604df9cc1f942830069b7f77445 d3bb6f1238e1c82152ea39113a270ff81a9f3f9d5fcd766322b8ce581d20a931 c1f948147004b1dd9576a4652b4f453204eecf6b8f14a2c88bac5bf02b9500de 29c7e3c306ea9b1e05a6202814d83e9aff787f905ccab2b0ef8a6854cff4c3c4 acc14efb4eac8c9c8aa493edc6c1b14905de30013fd21d1c65b78b53236a3e1a 7f20cddf545a67dfd6ff64fbcab6567995fbbdd05d2b94c57d43bba25ed72215 279d1c93a84340fc3df58ec623bc17a8fbf53b3050e9a077c362604d98187386 e1f9e8355671e3373dae69577fe04b73f90d23118a9bee7bee4dacc6712fc632 627867827861d17db6ce361ea7940d8969e15d4e810d0ade02e694c813889fdf b786b831eab1439501745d1a9b34fcaddb7a42e8d09aac0a4070e49c193b7eab 83c30bba0dca1ad948605a6b66f441ba05b65af9bf7e629ad28e0418a3cb1795 c1359d61cbb0aae0cef5bfe8d745761f058eba445e1c31b50eb7687c10a204b7 bc6d4f973f0ca409135a082b565422cd13a9fe84b27b13727a790a59ec3c7c2a b0b8eb98017172e2a12c6031ac87dc5e1593405f3113d8934bddfd183bb37d74 8b260a1436994ef6f939992d13b332dd6c8c7c8e4637edbae762db773fd84008 4da3c9845676e9175d2e90778f3a2a731805cdc0baba561ad4922207e0333ea7 4a211880bacd830b3bd831742dfeaa086a7d30cb9ac134f3fddde7e38fd73eff 5c66148001016a27b443cd48cf12c35fe10613299f2068dc3bbced9887322925 98c70e3f46187ccb8cb0627233f35802b33b05e4caf7e4ae1332d44b3275e9a0 eb8acf9359277991ede2eee9a0121ed85c2bba38b208f37a1edb1607543d77bd 8311820fad35884f45299dbcf85782f681c12b63d5059013a0556af256d63dd2 ef87a5f1475759d2f6f7c91433de260200951b836d378ff2e10365f44e20d9e5 2f2a7b9a1ac233993a236f9f058aa326381a34b7754752f96808a535fac4d982 4fadbfffa0004042b59ece6e0ed3c9a5ddf2d28087336303327958479109935c c1fa6b3d1b21f8e82dce2eefeafcf519a010b99910664704dada99172d765a0a de9f4c500da5f9460523414ccc09114e5d86d28f561b9152543d0cb76ded98b1 85802a04e725a28959d4c376e1eed1d989e3a0a0b5ec169271acaa28b25afe5b 471f3bfeb67ead16f0d0e5ddebe1232528a513a5199469b01b18645fc40209a3 9e43290dece41578797e1aefb10505a12c85add5e46448a5c8b50241ff7a12b9 2a046d2f94b33a0ff03be81a3c6a3acd2282064473a83c56b93231852651760b a0a744e3c24b3ae8c28fdb33151c07d246fccc1a3580b53b2a59325f581a95c8 fe67acabe5433196b82a18967bd791099143ae4ace5bff887a40975990d54391 27daf3dce6d263cb5f8135bdbd322d26cd1a6a578659337a3ddfe4256eac2d91 b03b52459b4e15390c8ad950fbc97c9caa90f64a82a640df0b1ceda720a7ff57 9f4b1e81065367537483c4fd1b52c2f5c8b25ddad8c061256f63f895a546d699 f63842866e7183ccebee9710f8f02e214c16c2f26ed4e0e65cc2907066c6192d 8f06af87e2877c68c98a9a45676b8b29b08e06d0a3981347ffd1ecc00e0c1dae 27a37d3a05bab25ef362e06fdaeeb92ebe75f5e7e2395a6d994e3eef036bd1fb 6f68cd9addaaa55034904fbc38d7d1812d8ba270c55551ee2fcfce9a4a63c9f9 dd923a1d2bd39cde65f30b798c303b8013f5c155330987f70d9306d8bcdd1563 11b59d0209dcc15b4518917ec9fc322f770c765901616824edc0d077e8ff3122 77a458af8737d1c64c5fe059b39a64ef963159713ef62e1ea805e5b3baf84c44 2f5d45932f0d7074a04841cf5136fab30e9c390f6c23c0da317b04f3fe6e812c 1a1fed7f65fc574d3dc93b5dc089027a23b73f2ac213dd111dad38089fa150a0 452de2a0382a937f846e591654ca8250230c4a26bc1cb76b2cb5dd1cbb3d1df3 c4ec5807fab173ef62393f4cb68977de710ccf332ca2b331b5207154808dd357 984869661e79ccad723ae6e61c59642a497f3766a543a39ddf9d1b50ab32c002 1f42345ade00e96a640f27d5c7d4f049004eba7a26ade3049408c12703f52e96 8cefaaff4b79a549e5d71435428cd215afd2937537c5667c2be5fd88c18d1687 9ca4972cda24d4d7f86a823cd895d43bacea4b8ecfd3fdedd604fa2c5573cee4 4f66230e58909ff31bb026d9e38e5225f7277d03151e7132fb7cb45d91f04aef 20ed31e77ba4877fdf78764552a2fa68b8912be6f98ef2be4e8414ba38e4030e d13e9f4f1898973550ee008df2e6666475c1bd441d017f55baaa373528408dd8 eacca789ffffe899b5f55403d84902f51fb28c6a88a72c0e9d9fe588f55fbf86 37db90a4932bfef762b14d162053e3e39b7a6ef4200d66ce13b73705abf303fc f23eb0d3838a2be9594ec31b04d7a8a9ebfe4d7c9d3f1a629d62bcb2dd16bc3f aa552f99a4c997a2717675eb1b2c42c881247435df509b540e8af4b715b863f6 96cf8f1d7def9492bce2742cb5aa8d01af3e26b5ae8c4f0e2adf8bbcc3aaf11f 5c293b637ae7abe70002e2b39af8c3dfdec8a6e04b1680c309f1766bdb35386e 00c969a2f02a108c26b473083c4a87cfd14a464417a8103f90e92b142c78694b cf4ae975d8a7b60e4af5444f71ead38d9155adf8150ceb2be1b9d59b3c11267c 82d0ba9bd9b436b88f287edf6cc3280b1a0e57115d79b5b762b3dd404ed1749a 9bfd6509f1f76b463e8d74a51680b6e05a4b28677d1e1b8edea120d4d9e4ec2a 484b78027b0fbf90e51a4197dee423f190acd42720cc120eacd6fdb3bf10b792 fbd925aa48726db38feee49bd2230d49af5772edeeb1e6c04cb4d7520123f00a 5a1aff0890e91ccfaab03fab0ade95d7dfbadbfabbece5bce9db61a50a77c06a 917eadd04dda05c5e8ce50696fb3972559f40e71e36a3f1106a7866ee3ac12c0 782692e78c10bca229640457e883af1eeabbbb707cfe9f543956f5bacf9bbc16 4b209819bb4a0feeab3deca49ae49f5ae55a66e5b3a4b78e1c05756c13c7f795 5805e984e8955bc79a7c86409e249867122b50ba26755d73c0255f4afe666a80 c980af5cd9ae7eb1065c02391c0fbea44d7ffe1a9f1145a760f6f42f7470e8e4 037ba9cf4da94228b91d538057eb35ad73b6a99178453a88a91be2ce87e886f3 8b50167b7d121906883bb4d7cbc46c2a1e4defe4a98eece6f03307dfc12f0200 ee7d23b9ea99a8a381015bf366f1ec74ede8c73463f695f8da2a2a075fe82c3c f7db787eb3a9f654965a0774bedf676e37a4e4dd53b92a8a469184ddac922854 62e3c11bbfede385cf30cae7e46ca855f80c8e707cffbda4f0cb6ddc8875548a a8d30e1699facb2b1912ef433c7e56b7bcbabd7bffb96518017eaee9a8ebf6e3 56d4a2056bc05857b5f1e529642dd2f586a91cbd0e8ab86c673f209a691c2a15 34073a2a96376c6030880a625aa0d75d5b30689ce10296eee682e3c546b264f0 7581afb7e90898b0525b67c3da6bc45ac87db526645bc731bf83062f662af18e 682c915aec114068d6db88f5a2fc48f326d75499225833ca53259133b943a6f8 6312577970acbdb7042e45be657c54f36475bfaf2bf889a9a6f2884a1577f446 5bf206f43267cd4d90bea4b8138b0b7687520bc45d8bc3e6d9bfa314dcd1d8ad 51c48c449afaaf7e7f056d71e3cf7715e05664a5cd498f79055b41a5bc60b4b8 bbbbabaa3778602d145523a94bfb5cd9dbfc3a1c41e59f9e1c00237a46819643 fb967b5b92a80e7e386430c504e91cdeebb6eee9643337c58c719e44d401eec9 b044969798f92fc2208d6ea4aafdb00d70c98901509b119c77fa4af5a7fd437a b43482d074c91e7eb4fb101df4be010794473e1db3049ecd4d84d05627dd33be 6858ada87513d89bebd93778785dc8450c16176b64a616d56cf7ac44f1cb8c07 f59ee35900e09985ea82189eb422e821b07ec1531698e2b89d50997475f78f8c 59ddcf7452fa7094f89944122e183662910363a1534230cdd7cf9d26e47cfc8a b08bd336b563370e12dad303224eedaf8137c22a360e140df0982ff0ebae98f9 c41e7c86c0fb3a06ed4bb06b969fdae87847d3650db0dc200334355f0e3fab79 aaccff69b12c52421da1d3798ff811aaa240794bd5e8cf5d21ead05fd7556786 9d01246b1f06fa2c2010b050cdbdc45b81cfcaa2bdde6d4319b0234ca2b8c464 652b65f9f0e3cfda98b6d138846eff34251f5c0076e4e2c854983c4044b6fdb8 36b1d6958084404c842541d0ac43f4b8e66b021d309663411692d44e37f61b24 58b71f60c71e9e7068533eff31b2b6d776b38f559c1e8f6fbd7701a1f9cf067b edc98b91d5992579a6fb20a6fac2263cc95dc58c0a389a4c4f61e83f0f8018b1 027a3d91e6950b3746c17d9a753c3a97d7a7ac59c3e32b3245b4c1e6a8c65fd7 109def802733de31abe06579288da7b095382a572bf2b269f3d2f0dd91468d45 a24f45e555107491f0bc0dff54aa22ce702b02ed4d25fd7ecde1ef7cde1e2c14 14247ce9bcac8345f32a397390f1b088ff7cef4436e5cad92502d21075b84748 c2b4758dd0c1fdf0396c45a9e1e9df368aacda8282da3cb81ea1758599413bff 67ee652289e0ed9d991be618b752035e3bcc057c6718663b21109bd7f58aa4b8 e364bcb6c1dbb28de7f1c81214fd789d3530800344d64a8b0173693bc41a2a90 9b18cfa88a5ca700acdc726a99d488cb57d38b4bcd608e60da683d569b96ef4e c95ff0682d40cc7bc2c142a4ee8a25c3fe426e2e9fa3bc4d2668a7bd2d71b518 c82129b879e44fb402ed5535a6b766d16ee81a8570f6e7e3fefa39954260442b 8126fc1f799ba42d0c677e1ef10371b1d529cec6b661598cef055c79385302f6 39c36f42bee20fd0789d8305adcf8ed0e7276e43eef068daad172dd6865b473d ec291a5dc8ab1407bc0d7e71793bfc7db631e1edbd61fbec38831f9b5e324d23 42ba6fb39e1f066e2d8a812ad869a67a0a327f783d72c395cc8b3319fc55edf4 fe85b7945afb630aec14a534817cef583a7671217f530265849d1676e4d030c5 2bf66e396291581a96ac660ecc7a1833b75ca30eb566ec98a16e444022cd0537 e9eeca574e5279c72cd402ba434a6aafbb5f7bb7bb2ec19365c652cbb997ef28 ce74fc7f4461b19ee0c95dd1a393560e00ef2187f5baf6338066076d72d170d8 b037b4b55eacde13f6c17643e4489d4c54cedfbce2efbb01f95bd7621d04e3dd 30eb4f84f5dcc9003e26f6d3a8c9d78c4aa289234a9dcdf1159fbd77fdee4bed 2bd6093a3fe2027e9b0576ff813bb54edd48e7fd5893439adecb9f5ab202f47a 820832874d8ff198a61a910ec8c4d9112d647e25eae5ceacceaa6b8d8ec36689 b8b82bc64b312bef48ff7c689975a9f38e903e3c9bc04e20b5c698d891004049 d01a45f915a2a87843843b4675b4ad5fb7b0c1ac909396920f93871945b728f3 8deb1c9a2a5c504c8fe800c8ab6751af9b77383ea8980a55d8fd977de14eff00 e59defcb8cd7039726651bf3d0274eb348fedd54fc320599745cd766afc7941f d097deaf7aee15a5cf5ac74509acf743cbe25d018b3cc6e19cab3d52099d1b33 cd6f785daea8a97d6585e9f92c77a8854531b2426072b576f3ce7a44419a2e2f 1128031d19a8db2fac382f690654775b4bc1d0ed0d04e5f91fe74b1fc16ff9e0 548303cebe62be90e0d3a6c7905022a65f7a2fe5f0b9652b0c6aaf0497682fdf 3e3273679073f922026fd496293a79498c86707ca746b38403d3a7589df11154 77e345803a16560d49cee8a095bc51a5df33a409a49227b92b9800aec9d45194 903b11184036b68baa6b8de2dbcdd5fd97326555402a1b47c565defaac5bf0bc f7b26920814e34e6d49b0bb3ef55d7b180132146007a99aba79c6b5ce6d7b09f 37412649f16ec1ac73e602fc11f9d1723374532dff095203a6e059d5d87ce20e 31 70f14929a53325ae7eff7108650c0af753b155f10dc48a6279afba189ee59b0907d980c848fcc103960fc3afa82a6c4091690fd98b1d4eb40282e06f0b18040dad556ddd1d918a529bbb2130060f1d5d864e0f11f716f9f008e11637f0957d08ee37214b6c3776b9dfa1b54f8b8ce80770db176e843cdcdf4ae97a5af517b20f31a6bfd2f7a36702f97db2a0ce279585aacdec463873eb3294f7a81fd0da8204c01a7b01f92694fe1174fd6d79cc5fee99161d4e6ebcfffe58b597693aed6c02d2e30fb07abc58b44ca5093fc3160ef11bdc4684fd16804b63043e0db8421c0c4b03997a735a1301b09a99c6d71b5b98bb5cecbeeae0896b899c38f88867200e502221433e9788f1bdf3f862434372e541118a5801edf86b1f8f065e281d99010b22b0da4af99b8190d366d640690060e18d56b1bd19c7b853cf5fb377aace084aa63d3c577e61ef2f0c5ad326e47c46b28c9582f448664ca9110b8e53d1b80ed011d85e10af09d84ba83b659685d6cf50d2639f4316d6d0b7fd0369d026ab0a0a6adc3eb66ca223b5d5b0fe1e39ea17939c7bd19b903ac1df30547c9977c604c4ef81a8deff5240fb358f49ea093aed9f7cf381547c1cf13cbb96027f9d8e0e4ef286ccc70ae72cf13a86ceaa56437072a1c3ae028a4dc0556633b8d4e21a047fed73118b27514f97f3b1dc869e8a481b79977c5f22506367b9bbb59cc8eb083467c179c7098ef7a6817a7bfb06f43b167283707a2f16a76479327f54cb4c0e170c73684fdad71bc3f95aa1d5543445023db3a29b2cba3736156cea48d09b0f1769e82bf9efa3a8b5cf8ed16aa98b99b4bfd53a32ffa27defdd4039ede9400235c4d44ad9a7a298f3ce0da249d78f6bf773855622c1f33a853565055d8f460b6728329445051cf71626a5f626cad6a9ce23a8ed072f42d778fffbeb99f8fd01e474f99c64667fbd8fc8fcc74920d667a70a45cac3e8550cd6100b90a5a5a40273bd4fb0d07df040cf7bdfd3701328be2e2a2ca77edc15581057b0d9ce322b0d6f642f1eda83843cc16c9cb9fcfbe77e53d0ebaeeb5c77eae6752452ec180c0917cea985254f27fa6893546834c24fb171afc7bbee642c7ff2dcf93eec39cb0b4ed064e969320f9f1b93051d9a503986d3151e3d8c365610008bac67a631760b9c103b2c3fb13d93fec57a3fc7ca46b0e4d11e3efdc58856a51e48d9cade9b0f368cd33c4684b58adf6fe3549b51e749aa0b28c5bfcd57f9bdf835bdf0a7d30a7bc5bdd643c76cb83bd8f48799e460d3e8cc99945ae5809c75003a0befae6702055c45ff5e657254135545e33f5406f27eb7d6f4de525c6da0496f34450d7b019f0dd26eec3f2e25f243623986d1d42685014e1844bfd3ee4a484bfa47887d0d2880c64269ea5443b817fe88db13241eba3b4e141acfc5df1f1c3e389906330cb1afba6763877441bc083667b8eda12faeb149ee6d17bf0dc832b03133b96004797f9d0894cad651c60548056b00a6f052ee00a46509b5698d72382c310a1b0295f0480fc5914c091daaf244397adc1f2153d3d23aa9480d067ea64de0a73a0d0b804c2e9469cbd2bd8d28604b127b5eb0f6997c7b3375616ba71e830ea03008297631a5fe7818bdade45478f139f31ddba0f5574de9f80953219529ffae3806a6ee1c5159a0a450a9c3e432dcb557225db6ca4d70bbc9219e6caf56e42db00cb93fa6d5a4ce94035978d2625cfcd2971643f7ed19a53ba5331bbe72ad150600d01c208ac07bb28da9ba526d482d283fd02622ec5fa62f55ae57f8973b13ab0add5c42c3d34090c2f3520784444fd2fcc3780394ef19caced459600fec88a90672e7396772aaa42df9dfb7f9c7f79fba772fe73a40ea316d03807a5955fabb08f01b6875c0953d49199597bb7fe243feb2fe45c3c8dfda9dea0c2ae09adf36003737a78928b24ab4f3cf348d8a1a964864c414fa0b7e51c274ed7e80b3a31e0b51e7abc6196e1ac9634b625dafcb6764a3c8f8f5342ae8e4efcfcbfadab14d0b585645a2349b2d87cee7136e897540739944011f54a5f45b6d2a65d8e4423c0acba0bb117b4273ffd66dcef50b94ca63cfa9cbd4153d6920b84d32888b3aff0d6de5b9a2cc0e188c3bacabe65095a4a1c20694cc8ca37834e91ed1d1acac090a2fd83e7b659f94bd5611069b3e69fa59eae9d0be700b69f908350d1aab8a2d0cd8821b88f7a28303d8d3003961a55cdbbcb489045186effcad057a35823c600bcc10e3f998ac95132e86fa908d024c0418cd38faccb099aaef5208a510be36026722c7ba1646dac90d406e344560faa6cbc1ebec435de647e26c054b44c438094200f119ce52eb00b08e38c09cf7f7b76a57ef77a0e682a3008b1b54999165065e4fd83abd97cf779d3844ccd32c5e729dd0353ab26425e99f9141d5d103ac0e725cf6f73bd6e70842be46531dfba48653f6b4fec7bf4eaf723ed1f86afb9a075fbaf6f2df4386f69cf3c4078e164e8bf911759bca58824b0cb5738742cf2e0d5403817f45d671cc9b12513f55243a095f28a063ebbc6f110b7cc69ff9f9980dfdf0a7cf1d26ae99d60db3ec4fd3973a2cf7cafe276bb7f86dbd5a0d8c98600724fd1e701bb6c05f53247e22593eec3f57dfd2a85b6afbd70597656e70923e0ebe23a14b05b7582c50177673856ff4e43b647aed4db5f7f9a7c4287ff516b201898ae83a7e9774ee35c30a74cb6d23ec15a01cab2a80af13bcef44def8d1180f061a53e59fe416031ac5456b837e2d8232d9534a592bc9ee26c6005f83c20a0a146a88367766db312ba655de986898237284dbb5b1608f5c3d48b1468f099e0a8aa9214a0d9fba6149a4ff7d257049bc462159d1048bf8964e9c304386df160725a38c22c39ceceadae5c883ed8b2d86cf8e34fc7f2de5db2b8f3b87f52c250c21de325c56e0faa60d9af69cbf29433d13b21d0f1e07feee7d9a02a27849cb00fd2cb46f808d3b96f02bfea5b23407f205d3cf18b47d95e50134f4f579940b044900d49e682db258d02195906f4e428baa1f545311a778d4635f9d60936a73010b7b95d5e9335bf32d016eac31790f512b5c152c2715b20eb923e5a04f62f1061880f0901c252c012d7440ea7c4a7394095f69096fdd5a16c37402e1bb187d0ad9b567d4a57f5a2c745033c8a96eb420a53d4214447d50798a846436ad13d00dc3eff33f270d8b350fb9d0f203835b2a5c07e411c1dd5b4251bd2e5b2cee360326251e5c106aaf91591cbfb9c9b0ce5151cc1a631e8497478d25c3afb98f0908b859bda6d23ead47de16b5fe35ba9462469bf819747ec2d71e0f34a62a8b0d0fb1115c6d76cd60ebc91fd44ac50636a12f65e1920449f571265bca0637bbbf00c0a8c4c6a94e1d277cee28d340484a1e73c28613c9f5369faf6bb9b869f4930bfce6152676c18b96f0737e210205c79fe63df78b6297749271e650bd1bb4b5009b2e2b569081626663261c459633df8bac5188e84c2e13383e1bd5b6eb01da00ce3e59c8a8a92891d3c2fd1653369f58be952e41e4965326a921c16ff429ef070d48696ebbc80c6463f334bf5629aa92d802a3d2921a7e0c89281def6be1d90301443c1036755e31b63a433958f24120aa320a861236c0e9889a945bc45593038c4bb1e679c5d09e18c90872d30c1a43195922be9d6a831bec7410cb0117630ec108d95919215ea3fb238b4f33a157d2c20e71b21e00b7dd9ce3ea86a5c50e0e485d557eb140902c40cbe3e3df236ae76aa095b283ddf29eae4e413be68c6306e05f282b36ed0b8c9a424cef88807ab5764be2377f640f25851c03b48ed8c70368ac0e57558a05ba4b6be225685c629d36c26061618c2d54672623e00ca0d600c95a9ffc2b317bc89d5bc61f1d38d74c12107b075a0cfaed1ea8f9c3ca578c05b512772195824f75e003101537b523e2cebff486fa6fa071fe5cafc02abdda03b013148caa42bc5d31a373884e5d278423b9018bea9b2dd9d183a9bb83c7f90f207970bfbb161ac1d5f3849c7a005127d83a067c4d4dc5fb4e838042bcded6023d8f80a555ffb01bf2f10ace24ecc2f08a9e2fc9cc7162f51c5473942cc98d0801c4ee092804804f86ccde546c11e74816e0659d7a4869c8940370f78efb2f0ae9795b69feb0326c67c2e190b7556dccd83c2e740e379693433f7e9d2a77ff0720a2bd5e8be9907f94f51f9ab1fa8ebace45e8c0b667405bc66f25fab4cbb10a4e58612552dff8e6c5aa77a4a0c033de6705e56dace8b4fddcc87e863a2be7089ffcc4e732451a2360feb674b550150d4cca5960d75d4d35faf0665d3ff0380e532798977c1920d9db81eee53e913a7e641adf42b70f7bc4c69f083c6ead55051ddb55e81f5d5ae49c9b143f68bfaf482ad755f9ed63261467ff0549aad0a70416055678ad26d00356e6ab225dffaf46a645e05b18115be9dde8d72a448e2209d6c33e3e0e511c128e66ad4023410c35819b96e8b2148eeb7cbcdae45acfd70d87607c1256aa13de66aa90a83e670928869cc4bd10adc1a3dee4d61dea088904a47e97756d0d3b67b8a0f45cf4b8bb869828b3c118692fe94febfc164a218e04972ad5fe433a89071d89707ec3b05c2199c7b73d71dc2b9394e8d8c41878ef03c5872d617332d3b46af0a2b858b1cd0772af9b37e9224838248798c7e11f680a1cc18367202d85a51bc3c0efba359bcdfc4fd011626bbe7ad32e501d07f7c200e6e5f4d92dc9be4f51dc6d29fddd84e47cf26fe27802fa5235b3a6e1e84b64065002bbf81562b999e34f1626b602cd33add58c51a52501cbb81dd49fc91989089471c3aff461e54b7e4485acc221a957a486ef60ddd35501659b67cca135c500d36ac2e4f3fbed84267822884515748e73bdb0f7c7c955381485b291a6b51506bd899c75c37237e24b5e4a2953bbb0e0525f2f1e6fc192596267e58a5a8e910555d29eaacbb107cd853379f475f5509f6682c383c1cd1777011f38517e89d304ec0d17d55f7ab98bbc92e302e5fa261b81903c50419239e4e97a59c5bb40db0c187a7aa04edc59edfcb87a383ba40b4481dc84701acc2a9e2805af25d24e120b3d2f97f93cd8834993088588dfcc116c113da5fb4d2962f7a0d0c2ad2d27af0fb14148b5fa5e102c562df2f607aaf96e6ed970b7e01b5143ecb7db5ff41fd30e0ca400665352606884732dacf6d91c9f803a9ccd537de804ee0d61d9a17fab023648aad39e0185a5b06373e9b69b98297d9f5fa9c5763d420e4398d3dafef903727e3d3d2090da5a91262b2552cf0ec62ae854492bdb2ff6de8dd1d179a2e10c1b0860ded73d78f06a594a756926a5b5305a4708a981f9f90e2372861b36aa0bdb97043f093d33df88ee38047621ae4a0b2f424302a88d718475e69152ab7e02f56bda3b6baba9b986cbe1964c5408c04f6db62c909e01bb33d9ca7f5fa173096f7f8fcce17eecb9bf1f969447e62a054e948d52830ad2ac2f2a0fad156dfe00e136307218f8e5dfdfc29bca922504862dbe567a6ea5b0de98543c81c0bc7f083d4cdbbd0bf41abf051b529b9ddc6c1350c7ca3618559e4370c2d87a2bfe150c83df9661e0c708216516a52fec17fb0134819cd9e3815ec590585b29858dc9070eed0d4f55997870ace39cea6c2350e96f3ff5263de7eca20bb735f3ee9498013305efad1390cc4e2b1d0bca0018c1eeb4c251497902c741fd36e3ae289f4103900b1483f5ddbd1b4aa899f64a90e1b237b4362927c6a7d7baba96e65d65290a80dd5b43dc7e84bb74b9a5683d2344077d96b8a382f4ed414f1a67cb1f087001e11ee74c9e80fd982075f406921e3c1372c2a25a447593d8d08693a2cb7e060f6fde18c09153e2a56a0120ecd160b7f844d51690c8b2d0c222b1fddc577a40003fe42c9aba8dd4cdf4372ca52e8481d8e983bb82b6562ef95aa2e94ec36bc40b608cd2bb05c92877fe8cfe8abe377394c606229333a0d07b7601dca5b44513036312bc1e8ce0891984f2c2bdf1f60dce763ac4d9c9406ef2541bf7244c062b05fc33b3de86eecb2ed14e81701567b539565551de5fec0fa00fbb20aa4b699b0af4db4bbd4859a3126bd6094a46237cbdbd16cfe2279e176fdf06a54dedb61a072d9fcbdcedaeee9d85b75b0d2e358b219b160678be8a8e0168d58e59fd9d2a04dd95adfb9bcdc27b4d629f439a19295cc6bf2601860d75331ca3462f9df6540b23b3904140b752efc44d01b35b967d56c78164489c6543c4e203db0d095181002791338cc722ea50f1263975fe949f0eeb7b6fa2444d19b95559e708d49ce00986d96c4fe7b48b5f26ca990bca2fcb1a77731a06ab8db0c1e9a68bf05cd1d408ce924f42143ce576b74f627be13faf05b8dacb97d49351aea9e194a39b13d50c568c2fe8ecd4656f49454bbc2748754be0fcd2cf01c7829586f87b24780bfe0b82f9dc6047f7899c80c3b2493f8c35f1b11ecb55e90f3aa75f0e767be98be60c1adbb961eb09d32324f6e9e16853b3739b5b39efad609c8c99a98bb78a4ffc03b6e5bfaf6473d1968337d34d7ff2630b73b5520222bec2c9cdd6872a5398360678291b523a983a574e2f1628c287ce7eb7e630e20f4667e844d9ffc6648cb500e8d043e46319c242a63506dc36c5270f5d1489752ed12824f9d1069f88ecf607cc9148e6d69748944803a5cd717626e9d8608bb48ae5240e86cc178b4afcc50e7536cc3fc86d7edb8e0acb3069b0503114f2b8e451bd151451605aa2bc1eb407349a64d89a71bd64c6bb8380b46b00cf926fbce6e4e5913c37907dab9b00210979e1d1f31a6b628c340677b6e0ff0087ca42b294793e1003c44cad5c48c553073913f0cdb63b6f67a323df3e514f747c489051e4cbed27210abc67f13d0aca060ae5253518d3bf481866dfdc6fa470b987580c28ddffa431336160e2a8ec6e03e6f166bcc119279de76a786ae58751cff2bfc5b759108a3797fc6056ae2eb50c72a593c355065c48f37ca4d7e97a9717c78bef8e5ab449f7763e317a1f29fb029c1c7d1d7e137fb8766fd52fc7fdde0eda2dd4cf8db2ca68b13a324bf2a2b1040ebc7e0f690a50bde81fa9c69efe4d9f1d538d23a7ebd512be7b19b858870e006767b28501e24bdf6717d6acfff42ccebc4aa34d1a20270f20775035db17c7090ab0a9f8f9b0d395ad9af51047063d5425a6841c24b5d0a4b3ba58437b8bc304d921ae33cb1bc6573ddc9195e1aab39e1164de51694045944a560109b2978f034160fb55334b0a081c544f2a5249ed53e1407b7f8903ecb9775ac18efb60e4060714a8fe098c6fced73f53dd56caa8dac3d1062f060b15340d75fd1cc981d206f9284520e8e309795c1ecc93772db86ae63ec786bedd591b4ddf43c836b27103c48e0e7e0b5c58ea3b2efb159bf6d9468001fb8a24a8d7058a35dcc68251a005df1eb06efb243c926c520a9d718ed5d2d3b732b1f4cd8c48e22729bc53c15b09f896f9dd918c2ef075e89601d2dd254cc7b8f92c4de4f722811b7af1c482bb05a82c7e979ded65639dbfe1b2546cb3764edfcef181824012bcfdbfc7c66cf00829ae5799ddae6f5c4e8376e71653e9426bad1ad8228957ce22488e15cce64f0c9d71a1c43b57c64f989883dec8f4c70cc2541fda1ea1b5f8497d34a1fe47300c98a6ad36e1e3b9cf2cff466d61211fe49510db74f8dd881341f25252e79be10898a3dc1747ba3c8c136c313af5b9fc49db7f9b49c28a7233626cf418935c0e0d8d2c2b445f68cbc87e8c42b672f997735949e937dc3d4a99bfc6f921f706340b10190ae8bfbe2c45c50aadd6664ed3376c158546cfc1c9b44db3ee14a6de4308bba9f098ab6cc5715848f3d5928a03f507881d737af6e46a3f9974144a3f7d013e6625e77c1d0e0e25d47602ae0740e2844ebb5f09491e32a20068236764f0010443cae26474f4d37c8a1d0b12aff5dfd8df218c5abc9c06f6f97a157737010435af54ed2928d542b402db88cb0889bdc148597fac8846fb88ac2e7e93f991061b357f04adb9ecb19451e663eb80db64f3bf09bfa0ab64639ff13f3ac7a00301ba411acbbfbadf0bf2b89329a5cab42d0d285c33f9060861110e56c76dd2960c701efd08d2e57b2bb9773cb6f830471d1622251bfba63ff9868f7f6e8e9e7c002c7887d63002a47af8f5370fb49da79ab4315741fdc025193152721dd906180c95bf86a900c023db7317becdc880bff892f9e573e996df6db284af629e9b620e9e40c44ec933bdc69a020e8d7dd85b71a5f612b0ad05e52b079c07ce7012fe088bb65ec402602ab2f6b7a2b0186a1dba4ed68f633d2ccce9e63b069d755c5900b38eea414ea71f4d18bdea5646d26a37214775bfd20815235d3ca4f9b451d408de564161effbd9510b641cefb3a8361a07bc7bc7fb084f0d7ebbfe39c6c1d202c9987fa1932de448df85b4b8c20a107f3420752298c43220c6d2b67582049b03658889c9e94de08bd75dd10ac579c999a5a702ffd2572f4931766e4c88132a03309703c19c20b2b5dcc33d106b27bcc60dcd945226a1d974ed7bffb449852900561c30ff4e4aa4d1670b7ec4e8b5a8a3aa81a1fd8a97d85483329189c25cb706799c1a07112dafc55c89ac403bb1c81e4b7d5ccd32828afc85d25cffaee153061a27077d7bda86eb2234b5ca2ad4dd5d0c247f58c4567dd65e042bde6489be06804ed0d64f9fb7e461d32427b7719940244858106b853292a158683d62c43f0bedb8ad87045af72871ff6ba3ea9f6f79e75539e1e815fa20df6089deed0b2804c5f1b5e2e6bf2948e65e7352a484bb27c1ea0c6d4979d2ba3e91ec2cfb63420ab5809e005dc97b709f2a1942b950ee5ba740a5778cc6900c05476e1c21ab8d0dbec2575112afbb29e3316bb92210f626ec6883a1e7d482e7b3a7392656236204ff299b844d9fca5b03da7a09fdaa0c3aca0493edf5c658dc7d5f45e6265db4098de33e08995c3834aefa3087bb4a5961715fa91c16ba61efee73aaedb0d2f70bb576bbafd0853110299be63e6f6136dd3a847e4d0292f994a78ef2faee9be60d50b0fe1803500ac7c0b3408587b67bd79b9381df88699b3318a35b4391b363001ac2aef58f276480ebd3bed1992419541a6d4633687c9efe4fa83fff72b8b1097efd28c03df0be6b5a6ae45109bff15e13b81f2e0eaa86009af17cdfdf90a60551d60d77daed5d131cd4c4dc9aa6d3bb4c7b80c277c8d99241e55b0a82b9890e44b27a9624e1b3a55c07d384920f5ca9ddb1d1733a9b3d97620d6129caa7df0d4a1bd24a8b292f7327d7cf25943168bb4270b99318e7a0798e22df47fdb7ed0c52f2c65d2fb87c0fc7ec0005d3889e5b21b4043c01afe4a8b9d0f2411fc859004b8e7c2037b618b5a7c7290f07bc85d5ca63f9541cc10ead0fbf3e500559230070341d2c3cb5203f600b7aed512d9a6cea639fdbfe4d70426dc7faf971b8940e80b18d009f31bfa34834b5f967b6a88483607d52c9b17c2362957c615c87aa02cfe675033a94b37421c493978bef3f8e20af97a9c8a653f7790a848b75f14507e19ac61b41b26acd6f40b62d2ee1b01bd176be91a6c951cf0b7aa9fca35a5c00dbc45812d49a7c98dc3f55ba86b419a07d2bf71e8abad3ac6d9177d95b44370160b13451c729c71523d5e78ebf140c80a83ef6b2f2dea100f741d6ca90eaba0373cdf1ebbe325ac0628d993ab25cfb17bacee0590577cfd00c807f3d90f5d10d531b87a94ce09b97b8ddfe5968ff5b90fa0321585cc62b6d6e7d915f09a7720e5574c5b197abeb04a687b8a202e89a7b331d78e6d610d9e70a3e83b4fac1de0253332e132c7ab7a71b8488acac51438bb544a62ebd7da69a6452bdc879453d038e85d774763f64c29bb7e7187a54cf06252f7a04ceaf0d1efddd2acd7332f406fd6458ed766d9f056a16e53a2af6b8320b812be9b8ad18dd695d08eae1a9df0206c1117def74372c68d01acec24709adb59f8e0696cf1bc46bd62fc23c4632026e1afd5df47047033eb71ed880c0ff2c894d0dab604fa6431a6098ac82d24c08c50df3920912eb814b91561d1a05671fd4e61458a2d38c0008f2d220b52af00196fbbd0f8e4f8d424ba042bb160f56b1ea670c0c001648ed495370e96b89e307439ca4b8d420855ca34c3a09e596e7d1620f650418f56d9b2fa3b2adc3955c054294f03644841ee31a97e86c67bd9c3406a2cf24789cdec77df50ad81c20b50cf6d117a2f1285d0613abd682a1661d3ede349a11fd994c0971f9f9f019811705df419650aff50b8be9d3d613c256aaa79757e4e9470623d0ff9d62ee19fe0701afe239cf4f18664a54f329383811c2bee93384add78ad8a223f7439b575e660f486c85d33c931a76d0dcf4ff9b83f170d1fbdb21733950191a6edc7a9261260f07e0754c542ec570652be18598c0383fb9aa6b04c0a0d90d280d2da8028a2809a99ac08f3cd71804229ef47fceecc8d189351e7651c106d123c1a6ecd4caa90e02f61afad58c709dfc987436c3212ecdefc368b8bc303d9e8a32efeb81b4920a46fdf1b546a7f7906f64ca2f8aab120861f7e1b1f0333a4470be299b31defe09d50b6546739b47acb9046f06ec64562829d02bcce0df8639cca70e0ee1fa04022b3ad6ae26bab32867036c6fe87c97e5e60db8f6b83e588e38af7b47d413520ba737c8b794fd297a7062e30f3829e2d892739ccd1352837da5afb417892f780b7d96985c6f12ab9b19f773a4dd0b0007bb238d0b95f481a8fddf20a241974e01a78684cf1cb9bf60b6c7cfb717b0b06f21c1ecde9b487ad64d13ae2009ab1b0ddacbecaf5ba187542220c9faa6a9bc4c1cef8e888fe9f7134075c8cceba3ff0e275383fdbc61de7f38bce44d20eeced1d730236ffc08cac7cde99a5f52e6eb00a136a8b40fd5f5e3d9619e97c1ac0c91ff76fea39cfad742c1bd780eb96cff01c4a79475f9515ce0e2aeafa3ae3db279d0cd5160b1c9a4032a447c87bf0ce70f471e5e9f8a2fb5ca895eccf820b9a7b254063b668f1467443670559f3166c60413890b62f6416ccb0ddf70f10a97441f97d554d97a300206135a2b1620b0550872014c7ccffea8b65d4d170799157ef4afeb25a57005507775b9f5a4278a20038ebcfe4666fb94944cd943d4a138ed8915af1b9adc369b0ecc106669c3b4cb01cc5f3c29c812949cea14d92cc6495813ca099c19e22aa92b8286e190cb214a04dd4aa5b1faf08ed842e377fda44fbb8e7533054496ff60f3e70c396084467305e4226e17e6e46c02f4c00568b7a521950b0d81b032a1be359c4e0507b9d64f0a07fab99065425eb552b19f7f873f52c9b6ef831c06d12ea464a70312ba135b0578f2e8fc388666f61b70c85bad867e16b20e297815772dc195153e058eee7906b0dd073775efc20543be33ff0e0d541cfb2c88850e58308f9a62e7af84f58c07ead743f1487f392661e036f89291bfe3a3fd5f08f0b97df4227fb3be491ae508e31cba3d76b6e0ce01c72321c899840260f5e94a2413b1542a8fa279d41be60c9fb020a00acbefe5bcdd757b1f8daad7d178601efea3484cbb18b8bfe65d2503d2a055ba1928da4c52a499cd25468477f4a890c3dc760ceaf0da8aea8c210d0f7b880cd61bee5dd005423a9f5e748b9ce0fac0e4ac5c0a4503339ed2f0a4ad08fc067f40e46ce980d0581d4eeb312a5cffb6c159ecd6ebd55dbf6ca6ab763b05a9f6afc3a5f7113a7b74205814464cbd7fa5f6755682ac9f706d9b730398e103a9339e2f5389930a6e6a95caab2fb72837b308790b2399319c1f2d7deb0ecf0e688b38a3b854e13e7e3987461b7478a8929c9075d3b03c26cfb3b808fa73c205613c7ab1896084675996affa04f040e147ce4f236a1f52ddb985c1f3d778a401da7a37f03e88f58913d5b2ba06175bc1e49ff091b2f612fb10409a10f710fe061aa92b45b0c6f296480722021989dcb54c09ca5ee65f2b3dcb5d233a3bb08e01f99abd247198edee9df7b8755abeba330da012ae513dd719f915ea96364eae019738c20de55dcace1365907103fb5667d49e61052eca3771a3298f8bd27a920135b76b6ef4b2ee7c644d2682435bf797e5b340cf4d69ec7cc09029c98b52570a559f4f737cde8a61ccfd44ece9895607e731e3e6a88900b8a802129e2a38a205627aad237b5b4c16586098112771a17441a945279d6939e2a725e9616f7b1609746ef446257e5459c31ee4433b92c9b361d111ec5c78c109865f5b0199f8ff07ef2fbbb220d731248ef4636cc561207ccc7fc796c5035046964cb46126398f0b3b63ce970d308894997012b94d82db709669ff788db32e5e71c000cc5bc4bc0da4e7a079dc3536080fa7cfccd40d209924b52dd11b7c708f835da98f9bb51403ce61b21c96d6842c74243aad66f7399477528e8c32d410fcc95cba21221e2c0f01af329432c91363eb86f64b3e90fb2a6f24f9430129c77b2e54ad4718cdf703c1b7cab1f6a311e6d9d7dc148373df9aca92077da33bab02bfec8a7084119f01f621dadeca19cd40246f2ddef9674ee9976a007ce0581759b5382c48da14f30513c1b5aaf4f6148d56f3b03ff66083810a878ff8f574084692748c022dcc8804fd9425a45e8a6923df4409cc25b8786259b1e72fc7687e434db1c3af1933640fae4e9c36118481ebd3d2398a95bfc641428124af20ec1531ef5be3285fd90a0da419e9f64ee33a07324b3f6bb05894d51b0168fb2e8b41ebb3e7bae6863f5d0331c31a4f3415194167dc3c7383cb3329dc3831da9c6e68348f3075b4ab24d3085df70474b4e554201a07e9fe75a897a587435c53212c15e0507e7901ca7edb0d1342ad31623ec77da723cba6a8dd568568cfbd5afd96ec75209a0db53c81d600e316957b8617875cee18b115c21d1c179611a370331a72d06a0fa57980943d0ea631369104dab7cac63810eb7ebf7fb98c7518236a045c35177e4103b85f6a0011a5399ab854493410af3194be049d801911411bd82cecdbad761e629211900fa387f22fddda49b4a9f0c5cb4b23796d63408b77830f4130063a5cc928ea680182e95adaa73ef0529b11f1fe089683bcb10cc09be80c205b8766836dd24907065f94f014e0cebb7a4dac5ef2bc191f83690920eea4bab4b5f2c486b144ff410d0f4e0d80259a0df28361947e8a20d2fbe34655403843d2ac2160235b5d6f240dce97ad24ba52a07d0fd5dcdf363a9f4515997fb5278a6ecbb46c260be7d1ce0d9d569dd720fbe8611ccb0a56a063b8a8c96ea98a115fb6f60a3bd0af64a69900a3d4d9b54bbf45b2810501799f5fe2e887d2c1e1f43760208f2e202f949e090d0d9453f452c63aca1149be49380ced2cd1847b8a1a83fb85cc9db16734273b0a55ee2a856ef7199adcca9178f92b6244dd39237d6496090168646462501f1f0a33615a051e0390b45ae81463216bf99a15a373876094318f51d4cc041f42dc05fb84510ac1a90bfc5e62cf36b7972e82006add623fabf079b8c267446f3a700725e487de3fb6e8e54d7da6b2c54ccccfcb9ed6fc3498b39498409af872697b00da8417a7f7c5cd84ec5e46f90c1a33b1ee577621cdee3e2e8f5680a738a10a03eb6a7f5e6b1dbd7eff8ca6a3ce85c59ad1d62ca89faad1bf96f50093397e880eb450c50173c93bfc2e7393206606901208afec94fac28fff4b1290a3b76dd609a40dc3e70b69729dd15f14aa677e1df13d602973b04eb6a7babb1394730ed502e3ba9c61f9df9e6f1d9d2eecc2b6a0e5a8c4f99dd10f1b5feba0bd3176cb7f0743a159ce9e2993a6b9b3f7abd7292d9f5710b387b06772e3ef5bed43c040f80caf7d804e1f5305fcc87de831fe446872d94a727e3dad6b740c41711f493d3f007fb2722174d5c9530078f7aecbe4633ec3dc86393e6b3b8843cfdd8e86e1d70238881522ef4867cd9600532166d73f22cd3a6ab7144da1abe0330e84494a480c91c86e563c9d7bf275c1b5b9e3bc5f983cdda8d4691ff7539d3604f2e39dae0db6e2d0dbc5778d90bb261543706924a0e7f0c2968749001996e8be3c3221640b7ca780d0f7fba28ccecc9071bbb557d8fc22dd5962feef64659d5627bce4ea006f28eb162c47b88b239f1fcc60d4bf5029dd6f4c520c1d91e51aab4fd4ffe4018b6447c9fe051451e13f7d3b2d0abd4645bbb661e7d9e6a2a740dc7b84fa210b1ca775854901d740802186bb099face49b713fd21016acbee94defadcd4fa30535d0e85719358d53478881bf98e9579eb9f518d0825aa50ab5f179dc621f4106179448c5188f930e1522a7d4d59dfaebda8fbaa82a246908758e27aeb44ec303dc5488c5006fe1121fe463a2b5b39458b0bc6dbdba899dd04cf7c7d931a1080e273388d2db163382f87f6f78f1db9adbd720617dd240c72e9167bcf7c9d1b90eb866992db8533888b4a049de1600f594dbdd649891bc0522062c7681d8f06c09dcaa7cbe59934fb60deaaa357f5f1a28f9799e8fb3cf4bc4a81c2c462fdcb5029467003af696e7ad73026b12df546e989b2ce29307361504a01134a8f26d5c0c686e08613aeb4b72b9b5eb4d5e7c2a9dc1c147ae4f3ce1b6e7bdadfeb7c13705daebe01c96c35d4bdae282bbfbc6c05016ba3e28675f4b75ab6a88f65f645a09880045b089c71503f39812a2822598c446156614e7d0c572f3ad16a1e4449f071d6fb4fc552ec45369e3d8386c4cfe2019ca621aa4602a9f9b8670fd7073fc065b3f3c1a956f028f8cb3407ef3e754891364a8d4c7ac140a4dface205dff97094852a1d1b7e408b538db5d0fa10c9448911e26e3ffdd56513fd01f64d8436a0eefbe17a8e575b9a1910e5a2eb0add1f974b5b19cab3859d507204208dc1c9500a86248d50523df3d3fd8d29fa0e2641a32f6abbbd0c81524eba71f3d263dc903e555b659f9747337b26c9616ea7792da0e5ca56808a249c0276692793bfc6a0118d6aabc46984fd1945715494e7b8d18099d77d7fb2111f27d1ff4ebe7df0c0f8d41de09e10b862dc1a4ab33db71b2323debde3a3e31ecae0edf8ae9ac179a0660bcbeddb2d215e54dc5257dffcc0ea3b4d115441547cee0485d89611f550a0e08785e6e39f88144792db66286980c748e8a1de61b937f33df8fbdd28558b50c70a39e70cd36213103c05f44949a8d4b7554036a0d03a4d012718ba569b6a409e791dfe323005fbd710fac0639b45dbb1203f4bc1d2965f3795797d8df5c5b000c571f608b780fc66770781603222f684235b916d6d58ed00614568fe9abfb0b714ac20fe3671ad569abe5424c1a0f16baee8cd8e332f6dd5f8f253705f7dc05b5f6b3fa1b467cfecf0b03061365764c6a2b295d0cf855570089418ef294d80ba670790d567edbb717198601ba752d5cb9e0e4e6ef5b408508fcbc4d1956c20c9c998cdc089052ae34b21f5d4d53fdf9a9ece1b2a108ab30f71277a25d821f0554d6c162a429df0e1c14c2881b75d760a8ad5b3a0421a18160879f7b50b6e20209316f430ec267121b3d40d75d63990469709bb85e7fa2ece42a2504b251710fc32cc0f6211725981c805b6e7c590f1c3f0e2ce16509f7938a1d3ef07ca51207741c46ec59a4940954b3fc759a796c66d5b999bcbbc46d024c283c60995255009e052ffdb2e28e72551642f1a026552143eff0d1751db575b0ca11dedb7394061a3e70dfe95a02970401dc029ed1709bee70d89139bc63dac0ff5d7ccb92d702ac09e9efc4a86cdd247cd5a2c65cc0272e954ade438a28757a7e8810b0378109c0b8b989374bfd6bc2ed6a96a051a2867f39eabd60b9363db993b573d07f7d0dd3ea75d1c5a854cb55a8c5c3eb65cb6a663e96171aa9bc118629ff9f19ad0b0c893f00a540b883f361448fdc820e98b0b6c85cda45284422d62fdba548ca4a00ba56c5694657961cb5dad8cda9ac46a3945960dcf97e840fc19eaa54dcc4e30fa8474a0f5ace08804c6e5264c2d35c18a715d3c3280da3a83e2fe85fec803b02f7e7f410aaceedd41a920b3d8b0993077be0991579990977e93a0c7539c6de09a65e764f1bf04747843adfea8c7f855e333db4fa5f4d5ce36b90bfca9c90480d591b37b8a7c7e281b7420b9280e47733ec22dcd4dae0f1f1a7d9c33e50df400df015d32207753d1d326d0a7bc9149a79a24329c952c0568264438cfa7e3fbb0c1545679505e64eece9d55cb319f9bdcc253d32f15bb410beca2d5eb128edaa06fab4e1e4427864c6cb8683ecbf4ed8021c191fb32f01e5fafd99d8269a3f280d59022974edef9c290c945e81ab811559b0f7a4fa3f6c1fe322293d8de152ef0335005a7c28dbfc82dd78a8ac81a52a3e7c059c02dcc6d3539ef28a578a1b080b819544d7f1ac63d11c3711cb6b060495fdf6370e3844020cf5671ea901863c0863680aff79f4bb44dd36d705ed93a45ec7864bf432be6fb9fdb4990d3bbbcd0befe4a34e41efaf993555d1a076d72a3edbdfccbd7b9e8337ba5a849baece670e810245573fea730f7d2ebf09a84bcfd8beb1772fe09791083b9ec83343b9940ab1b79dc9b51ba2121bec73cf54861c184cb46f839499eda21933da5c451fcf08cb56e9cf4b9896dd6b5333b712051a905e1b4183b882b92c4277ad7f670c76079c07374b0e27e357001cb9b1277b4e00749fcf45c23063801e24efcd707e6304470d103ad10c4a0ee9b6fd81f6fc58ee5e86bd0ee46056e8d0297f31bab4220958ecba3a108330d1202025aef0ac37e31716dd566af882b3bc016f8c61ef57086ddcc2c7951876a9793fd5ad32ff02e2d2976f7edcef72c3b65d8bc903b9f307870b65eeac2b6633f78b59000a7604887ef0b88402401841bfc8f60b677502079df9e0b603be3aadddcd889752e48d36c433a80ec177819fcfc5888b987dc50dda1b21d85815b9539b96062603dbc5f81fa03dbba29756548e52a4828965c602b8a8e5bf4c20003830157331ddb3804be9d390c84fd4ae268ca6210e2592b00fc109ef52b27c9d05210b0f86032b9a97ae7d791581ea7822b389a6939dd7fd00e2f7492f6648885c1c442f0ca49ccc6c5b4ce6e1608e7c178411fdb836df0d063e7f567762b6af26ac0625bd7105f298c99751e027fb97395f68acf3d1f70a00c6450b220cccf3f4ac8e9c31fcf99493d0768d8953d0df333e6125c08dbb6c0172a3440fbfc54679c4bba0541045b867505fbc4d60351233e761f1ce85a340087bc0347cdf994db9088a09d4a6f2d0244b8345b163d6d7398ff3f986aa3e490a3746cf0a1714ecc115abb67fa29edb4635dec1e25744edf1759712901cd48a00bbb5a25e6e9c6307aca4972655f4d208be23f5130578ac990616cff249ec7f03bd8fed17c5fc8bda8e4772752678b4996dfb8e088ccb4ff10e942c216d9b1105115b6b7af2173b792786ed9c9734ec303da94dac04561484a6db209a8d7b5d0b889d5d483b3132f76a40c0fac4870210fa8c290f34a86805cfda3530a8ba960bd486ab3832976f49ec0418ee9b8a07442bd7fdb8dc5b3949e9fe354fd80a930cb453bd8cb0cc8624a163b409c9609956dc8776cf5b5bac478d48e1cbf70f6f00f27df8ad58b507ff04b586b1fbe9f02b88dca622a0b8fffa5c088c4e5d9638077a6b9b8dd63dcc798778f0e6bc8d7ccb6f849e525ea176c4fdc29450a44da4030052b3dbb078f8f828cd249822b5cf37b38782622e652121f8b8730aa2aa050204558402774aad5b664697bfe4962c9734db84c60357b8096e8d784e6f0f7d0bc155246c6019b8ebd236a7b36fd77530fc370ae6c86c1d0d8b6299063d53d30aca18661ffd090bb6b10ea1908a83ae2a1ec0a859b8c7feb126291ca7faf5820a3ae46a5980c0214a18329632d5d6823e42a3b80f72bcb70aaac93d0c78b1d50957d1f7cd45481ac22ab4c7c3825877f0999f7c6f6285eadd3a5252fc652ebd0b08de7d58b4a8635d285c1fb699bc69a774b1ba5224b8d69ba6622024b15c840df55c1287f6b1a4949c73936ad846b3547e5eb1501acbae503364696c1626a201c23c7b84354c80a3b0da7a56c165dd5131ff2446fa16356d349343a25db63e0dc1d4869ac2b88a2ff8aa753dfc2f8db086e186b2003825c47ef85bbb1209f6019efe3c0d2548874b797a0b04528223ef6faf191288667e047231bdf8e920fc0d9f6212c61a619cf31b72d2d7c5cf1edd8c26de0847d821676658027522e4eb0e06e8b1647e7e1ef9666865f299c572ea0478a69f5928de4caed4c96c4fcf2d08059590a7c5bd542f226c4b55f9e8cf6784eeabbeaf72c90695fafa58fdd6c7003d43d11ec3e01988cd4049a94d174d3f585289675ae543660843810ed1f853046147195fe15ca7570a6c8c2b7226829e8efc85836f629e4f9eff45ad3d1a7807e4938b4439daf19f0abeb3e2571962a224271aa3d416ba35b62f9b937020180d9ded43ab8a25e5773940e7b794744bb350176411618db5ee630b1be0f3350d0efce3d9504507e49f840185770a866e1f30ef8a9e66bf49e3e688aa310c78c20890b863db94ec61be119785195dde5a63c530f26c1dd62af2b33dc2ecdd9e1c04790908d0b1eb062fa9d4ff1e62760f986ef60060c8133aa74aade8489f6fc608ec0aa4aaac401e2120742e0802f8b2d5f0f2f2a805680841c7c0464657a3d301969446c860b582e639a3867879b6e4b103657e02ba6171c3041ec4fc43493409a53b12c44f96b413cdba2980d908269d97b20c8143978188c2007fd742991700 +generate_ring_signature 9ca3190a0b506eacf4176a63d6e3199972697415d98450070dbb1e3b9aa07bd7 b9b3fe681ce1ae1806e5c6fbffc629627c9013670e2b7fdb691856b56c8b83ee 63 e0c0c0b37c53dd07aaa2f07d9f17a1cf167b437a46cd575478102a381151fbd4 5f7215bc18a184c4d3767309cdb195323c5862a950e2b9856ebf03797106edf0 612f63a2a2429af662abb639929f22e5b0cc92bcf5c8dc2a73d758ec33551441 98be7836d44895cb317ce32ee74551712453cf8698d35a541f55e5f72270f08e f0c3a085fb1aad991063009f07b314dfcf98ccb83009ce4b25b87b4cfbef9b5b 1bb16bf94d329e36507e3dd9a1d8acd8f40e0e88a7bf5125a91ef800a7ea295c fb5ad155c1b82f473ab36950d903a283694fe27d945dbbff3008c5723006568a a39bd4a20d7d0ab3a537c2a5bdf98562cd02f23d5153cf42d5ac79021322e337 eff08a965d574cf59fde02f3512a74973a5e73ccef6cfeba8bad72cba34f400b 15ad05b1453a7e525a3ed640444426d47b89b5716e614963e2d872faeecf144d 3e72343b270b61aba8586a6f9860978f8b607c4808453da0a230163273b7870a c9b4eb024603ddc5f51c3e988d4139903d7182f720559814b9b3dcb7b66c19ce 6aab772207cf522512f22cd8d9d7bf4c9fcc57ca6bfa4af90822a91998de55a7 df9e1ca9fbc2da3c2c9f2d64d2e500e6cbde279009e2400c8b33349d7cdc3893 bd7c982120aced76f83185123e038093c951593230c9bc2f916b773d285ee9af 1e389ce6094c3057ea16bd7ebb127f376b3590fee646ec8e0a603cffbec83234 c7ad2d3c6619718f02c9cba402e9ce900ec941361693061c11b5033c2adca39b b392dd8eb16dd495095a354acbb738aeba2138d5cbe9921d6680553e9e233ff5 dd4a0dd765c1db431dc5f37712411203b172ccd225516ce921e401c54707d867 3f9918d6e6a5c682db633b4b3f8b4356467982428a171251fa72b6e6ceb5a3ba 38640dacd0ce058072a865f364a3d6296b06ba9251e4eb2d3d5b9e98b8f1f826 9f0382cbaf6285fe7333a9cac2cf355933cedc1b88d19e011622144e5a615e30 e2c70e4d51355080887714393c0a0c22262751fdb7d364d39e5fe676f66cbf2d 6ead8bbe954a6ad345006cb6bc59b8ce145140d0e39b37bc39307a71846bb24d 97206766853c9a699d50d5096b03986c842d3e07fdde0fe037b9e0e139b1f074 98148748dc8ee4dfbd33dc64f139385765d7ef7eba2b74f0247f93a832123e70 ca72b712a8f71fd464a4f961f618a40ca9389220eb29c5ec6a0c754064774400 51fa139e601182e861ed0143b2d0e7a865ad53a422de97cb8113ba61b24f74a0 d7b0cf116ccee72719404c58c226b05f4fb8ca6beb40acec31a00107997dbeee 18d704408f308adde8a99517fdf2288a092f6be379b459a6ea50686ced117332 23e794cb235b3cbb84a0f8adfc17969c23fc227447d23161632eb790046ce066 1ae9daa3d619e04ef0136240cb7b73d84d4ae042eee135b833cfe74cfa0f05a5 ac5c4f56b9164b6c07e15e42a245f8220b815d563efc73d3810a1b9a9d369ac0 a18179793272803179f02b5ab74992c19d32a3c61eb7fd4e4c4efe141b2e9983 7aba9615b2d8bd35e31c6cd464aaa827f559b5347285a12c9c5e730542cd8a3c e7ed34ecb75cc135cfe934c69dbfe4919e57d9a314ef8d5da78286d920aaa688 b7c98b0383b2cd75d130dfccd7c5168ab9e2c71970cb73730ec555032e6db7f7 3ae97a97d4fb999514a64c49977459ad6c101338a938c509842d12473088b916 b8b9115ae28d919d4c0e2e497ef71536cf9918f5616aeca5896e0dce7dda17b4 9f727105248eecd16ced8293054a0f7efc6f068b67216d32b578b518fb3d428e 220e078deca9bc50b828a47095e636bb50ce745040fcbd0bcad42524aaaa2b31 f5cee4cc19d0da35191eb1e976065f4052bffc304221db0a463b927095a1d959 26a777529c96b4c5f119537cc8f8a1bdc81b9b88ee4641acac854ebb1cf7a5ce 330389b1ca2da182a7def9f11ad32ee40e17f3adffbc3bb055e828f949ffea09 4a981b32ef8b9de7cd0a922c7b9779d1865886de1635134bedb32d38d1bba3bf af8076ee9cda60a1a264dbf7b7b8582e48dc4cf86769871450515287ba0663de ae53c7dafebb6d517ad5a6933024549ff98b06c9ce0e43d23eedeb7716c857be d128bea7f7b48ed2c8a8f4ca16599e9ed09abf33abcf1a696a2a90ae593e884d a484076a912dac1ca5f21626c1bf64fe1b592a182a4cb938f218ec0de97f9b97 c3df0517de0d1392155a02a46e79b55ab2349a200f60714a0003d750255c1952 dfb19c9be4717cea6b63068af1b3c7052c736f86c69673e5487cc1c8ff49e821 2858b9e3b364408bfc7afd94e58a017d2c42751e3a143122fde5e861c321cd55 5d80fdcef0ebc820e5200c03c93404f30f3312ac552c182b4de4534b1bae3aab 3601ae2ce8b3ce9a85152bb927cd84cbb9c8bb10565d9ff829414d20522a245b e56f2b0b165ef1bb63aee0a197b3fe42f19e94b7b6b6cf4bace56ea3c9845d77 be33c4ef06e198de17d8b32b482fc2871dc20dec882d28c4c3aa6e0adf311ccf c8804ee1d7524f67fbf04552f8f7be9c2a36acc12a2999f5e88d658cc2d5bb8a 700886b0043a76d2492838b97eff5cdb0c29b691761d562b2db46b8b3e4a0471 50b38b5a0671b4359ac922d2d9f69a831560f39b046c08b108c2782347e9bc03 9d284268c3e37b2b52503d4abdce87e917277827b93e8b1a8ad243b96f807367 0537efa4968fd6b00144ca148f7bcbd3c6f132ae0a887686913b2769154c3a33 c00910c17c2f515fa321185c23c59c0870b4deafbf2fc4356c370a9a734d87dd 0f6094cad7a45addb4974b3bbab376e522d25eaa86cae214c1a4a86c69b3bd53 41d37781017ff779685d8430f9e58cdf39d8700d389c96d0f0c0e74a5bec0b0e 43 234346331e93b53ffedc65ed9222346ae01a61a7d27a3fa86ee722015d134808bbb4d43f34a363d0962655d719e35baeb6b84e0aee24679e8cb778975f7690076858cbfe682fb239ee0e4c6ceccc9e11f76b788cd19c9b75aa2ee7a124bfb0052877cf7703c29fc893bcb99e842344e4b51f24a0aa2e549006f852419dcd140730c48e4a204161056efcb8040d25b496ec29bf495b62e912f4e121457b744f06c35fee6f3784f91da6a25946acf26f0d2944d7bcc5421ce92b00a50ca4ddd502c6115939ea7d574b054659e8386c7775e8852073a8dba7615a1155aeca6947054f756dc48e3873486e77467fec7ed7b60c8c582c0bd5317b4c4ff13692d4b9098eabd8b3aa417d8612288185ef74a0859dd116b3c165a1b0cd8c9d0b2aabeb0d428ffa02ef977309f45a790d6744c4ab4010abc0e2a7ae0d75ae4481ea4a5a0d62fc8e813ec145433a8e2068fa747b5b8307fc0c085cb09a100b05884b01d60d543ea3d3fc33ca958acef7b9b4075037d425fbd36f649c413f38f88dee739f02e023f2f5d0cc18633ce81707207955c906c1855c56adcf275742bf8b1f947c0dabc587f95194f6930993103fefbfe948b005835be9fc0a3be4b03e98d0be360b480f53bc4ea2a319cee317cc85be7fa5170d295d367e66689bae68e6fe438700de5ca8d5e253fed069bdaec1d5afe5025f62371760508f8658d5ffcfdf4bae0ce0cec28204b870a4f36940d43971c4e56aa5ecbce58cd71890773643cd76ba07e340c319238969e2c350884abb350d8086ab5f843f3fd1e65c0e6ebac6629a0db1f373525ccc262d33e5a91b299b2bbb1744e75f9b709cebc02f06d8c045cb094c6e7b967bc3a25f40cc5ef904ace8d1775bf6171140fd38ca5de554917cde04fb3de3c965001bb906cad41f08d7bd84158df173cf56516fd4742698dddd810feb1182dd8d36b9717e4de51ac7d0e92293b623d58b58be8edbadc8a0e6033008a8f6b4fdfa4ea0b1c137a6dedb5ae26855f7b78b9ac2c0565139dc48cc68b30fe6032346e70566e581f42b974c6f305af5e32e21e15e905e3dea96e12adba70d4f6e7fa1c63f7670cb572075591ca70b29335dcbee9bb573c674c92b03088c02566ad644ead71ef56e3e43763ac130e40b381509a1ada96d0cfcae2a92b3ca0a888557a073b787098e8369dcdae262cee7456e7185281f8eec00b2196eaa3d06ac21a87b3431166cba06d60b8b9080355ebd1df12a6662edccc8df7c64c0ec08164086f2f52cb8ab65ca5b89d00d931fb3887d37f1d493fc38d1d563a7757b0dd3fed0392a1e7c55bbb5c71c931f68d7b756871578bbfa67a99604961b94bf0841479b423327be2068a524f3d8591b4363ab3a24377351e4fc8a1098086d3b0e7a80ea72b20f633296ea01ccaa076542a3b117520afe20d1d1627c402f3834032ebcbe1d6e028f09b32cc907e4be52fc9843a9b28ade127a215642a26c0c0806779718a58f7d5e90c4d003b4fe845e4e210247cc89ad63588718c9be6fdadf00c02c98366fc0a30fdac62648ce944fb85d6360050782969dfe0fbf712b79ca0ee5e535cdac4882e9548e5cf98f0558e9d56e135050ad8144cee5ea3f4433e1005baa29013618860d2393118e36fbc4e9486ccbda7c92dbad45e69cc7650f6e0e3aa29b6fe12a84536864db0ecc2b53019d1e65d82fde60824ebda153d4a3a80c15fb8555497046f60d19c1d41a2b3d469ab7364cd89a6b672acd10802b83c508a9ce395055a8c64d0ac61f5cdf114b4ed9d221bb2ac6e0ba096fa413f2c63103a15b3f989449ef732cab5a60cfa0ce82bd9da41d61a97b676d17146e1e9b3a01a0cb05124113ce8f11dafb5bbb2592ad3218d5ec5f115857a539165cfeb45707fb23caa64c82d1a0010f33352e905416814976413ba21963ad3feb4e051d1b0c25dbd86ceb9b03bd921172657ad2d7a559cd237402e5f0cacfa7797f70ac9f023badb2580324203750172019ee824a544a1a5236e03e3acb1c9342ec02b30d066d4a9d307b612c6dec409854546817dcf07a847752b0be8a109375ff0e239a0fe2d4fdea9ce51519a70f2a3dc8109ef6544f36721986e3833411f061984abb07fda15f33616a7e5e0b0114d4690d41830a32d8f2ba4864cf0dbc5e1481405e0f740a73df6d24b989d8b188fa1bfa174480e7651e2055434fd84d070e55dc9e0c0b06cd1ca118cdf21a561b876e67d2f87db7a90d547bf02cba5fa2c8239835042acb76323c11259873cdbfa5f4108a461bfe611364ac05014e7f41008b94e20c1610408ce05ad2b6582802689a2e82845539631be030a986f8ffee43fd65a6081e939a480b0e685a90f8d69ceb9486a812a5bd77558d974ce53a24e0f72b4d01a0d291e421de0dc2092cd61316593d0a137dd6c1c0ccdd86e6b93e367f3c0a0dd5d2d34f29f932f2ac3f8d4bcdcee405755e201a8b5c18b73946a17ec0b1010c37b77c3b2940b1160fa985bfe554846be7776843955fe474eccf13f1b13df50ca2dd2737a86af73bebe5f9f7bdcd488d1d8958c859fc64c62d378e87e6166d019337e606813c052cf20883a2e4ae1e1a3b8b37afb1227d7c6876b18106569c0b06069445a13fad842c772a0f397d8974e5b3f99bbb0f2dd8354ce8486275270a8a2a7ba12b82b5c026ef0904d1de28ac1b1288f29abaf6b5f6baedaca4c6060f2d729b0fb48d3bb9a25534e302c5c7b14c39f70b722f4dd22c053602bb273f08d01f70d7e746c0bc476d7039a2ac706d040ef31491d6e732007d6a08b9557d06db6cb81ff12868de57a2e50cb6362bd46f37773c01a05428d838253a38a3560cbbc4f04231c8f309f9fed63cfa5bc40c66e9b2d07d8d74059ff34e2ee834c500e3a566d38b49713e49278cde77eb200f7d5c79b0b22525a7405d0a0c677a520c9335a0a3882cba2eea157d082c1b28fa31e06dac49dfeda28578205847805005881acbe1c95988a2e8f81aa37b0a7d2f60a2b06a6617e2d7309eb11f09968f031e211c743259746552ae79019020b1bc4f5caddd2220f614ff6671dc58e02d0d355fbfc71bcf625fb992bc55d00434d3487cb9246fec876ec985caf18d30a8018f3c930f41f46a9fda494178195aaaf0102fafcd53506dffd628cfb130115b0ad9b85a807d3134b0b8f894624ed869607ac139309d27fbb9a12dc34d09037406bd70f7910e9529f175f4b4d346b61557986ffa732b3a3195b90588a738eb7c0bfc4d47b8788f1f62131755f323ad9ea83583d3347ee7f61f1c1aca4bccda4002318f84c2d9a910c5801ed23fb4a033c8f7d6ef340a3297706461db429ce39808196fa7e99a0a5b60586c2d02b29e2fbb359af37ba7257def7c8c111bae464005c4ed1c2a39c6338a55705405018042440303ed124e31d7b12a729719b2f0a3042aa329f94dd478b2f5d6d1dab630ab89f3d16d5cfe845c523306e5a08c3d880e7f452301376797ac3dcaa319293863dcb5cd395a62b4ef7555f01ff8e437db0370c0d322198a600eb28e5dceccfb75adebafff408f2bc93fdb1d85b9b5aeea02ffd8d4c3c1ec354c80929c1fcf9afae7fa4059a7f5d43265eed11d61afbd36010e8f01bf932a41092a6b8ff649e14b24918c43525be237da5bfc4f8d93b9940a8baf3f9492e704f252ada20c2c79a908c7395618a1f1aed4c04771c34608020c6da1f30c130b70bd49a752c1c3f7f26d6291858fc851e2eee1607cc004259e02db206173c8cadc16eb6dada5d537e08cdef1f328486278cf06a1b1d17eddf90873b238ee416380e30c0114a06f1580451a95baeeda3de31bcf5463d58211640595e15ff81653ecf6dfe4c78015d80092a29c6ebbe0e7475a592c751843187307e242876075327be3894dde3137130135cacf11547880350f8c340eb988c04401d18d5af28077603564651700472b3b15e46198eda9e026e5991172953a06b601a2cd3247322d67c818e3a28a7bc4ff0f0d199ed5335ad503ac7e1566a848f008b6d43edb13f9585519f310f2092f8d0ee914aad4298a7ef058bd389025d03d0dfa22f33201a48bf56cedf47c1b4591bd3095dde6facc0d5fe3a76443fbfb1e0a1f372488ac3a04d14119ead3f34b8c10a3a4958e60ec2d973bad29eca24e450a8cb0262493cf0e8a053e8acd1a422dbc0b71484e10c1ead3d7422e41bfa19604abbfde5f243c8438220f92caaf351d43bd1db0fd00dbf47eaf8f68051389ce02935855264e4c805fd9369a55d702af2b9f8c9426faa16418e93830bd0aaf760658d86101b208affd12ecb4e3335a93dbb907bb8577d3d09a51f4a51c2c385c06b9b3a131c98101e22ddb4491cc774ccf4c1ae3f939c42cc48636e2e87fdbe40ae8857021d1f49d11fb78b180143a62b22a946bf474cb7311f959b3ac60e9a0020ba298497103ea048efef539e74c2e121fff6347e7ec4e7daa8ec5a6e1e0380d1d9522fb014415928cb4fbf1ddd3a9928a3375c551d14572ca40be9bd09ea107a6e4f67a4a1661ef385c8e16476fc35b6565110b01823ac0dc1a94d18c549406730bb246b8da40dbd136a59b39d93dab22b49504fc35e52b778a10c4c63a120687877f0a99227153a66b23cf487be93e949686b1284e53cf0a80fa77a011f80ab83464d011fe72f7b23c8f52efe69ddd61a8149e9b2d8d983553fc68aeb82e04647e48a7f5286763388d7c86b5ce280564751012be90750a9d0644670550860051dffd84c1c3bb8271d6824c7967b9e4f5346dccf2ea0764c3eef9bdb34b7c04cb2e77de53f4786534c76581619f5db369364ddfcda1ddb426650539177b060495b6134281e7a7c5353fa3bdce166801f0a780f78fd13d3ffa867a6b0a016c0fe56411ccbe6a146e84d9b6547917bf14d7282733b66c31f26a23e27c28882e078798464af548ad7128ca4ca84b1a418dc22651aac4ff0945d1082f1bd9765204733b10d7afd451b60221cf276536d75644b68e60304df090db729b7ce80d30009b48e2ef95897e4166e49b7b0536367ff89021aaa350f7805675fa8cc214cf007ff4880f43c82f0e21f7dd57b73a413215919e223e3a2541f3386af196828e0bad64d8b4f9097a2739dda12fbeaeedc05bc0f69a6562657baebecdd11ec15106b312cbd389ac26137fc6dbce5d64f5eaf20ebcb51abfb4ee572444def17e91087ae57574888ab851720400b7157d740618ccc5257ba7ea3b1736d2f8dae5000bd699a331d435cbed5a14a318fa0c849a61deb278d702d7f3f559942187b8220f9939a72cddca41095efcf51e48f92c4da20a83c63cf4758878333f1cb089790c2ea7f398764fe1fa914f0fe7078145f6040009a861dcd27615f5c58f5a17320ca436a4fee5ba69c60b2af50fc7f7aca7ed9727fd40a208d966c791d3996c120aee118d6c3d00b064844d23e24ff16c79bb271b1aa6d92553e331dc58d6eeaa065ec3297213fdc8dc4abd692da3908e46c021503cefea501be674f3601f08a70316a49229c8499e9087d925cfe3e719f2a739761b4f7768f7efccda887f5e0c0f1dc170ac9beead502f758dd1a28be927271746dd2f665aba003097065fef530318e78bb0e6031e38544f00d5ff77421790ede53e9d33b4bf28848f0b1297150256f2d75d335bd07635f505b62e2b9fa4eacfbe26694c2f0aa3e3a6cc4315a90f +generate_ring_signature 54be1366fced41b6e6d5ec1d55d88a950fba508fe4b3c48d3f6583bf7277bf60 48ef9726ed51ddc97280f1e45b0536fcdeb8176d49ee8829235c9d5c1d647c4f 2 e57ae1697336c544064266e556e40d887473d1992dd298132d6b4ea563d57b4d a3e734da2f415fd5a29c9e8fa29b4097e7dba0cca57a53c205870069c1b5e49d bde2dad66259c5e6e83d1f8e592f4e8e97c4f929d2a1c584c9ab99c378500502 1 abdb2d22ceab0d531819a05507563b3e9664cfc3d1076d257fc4aeef9c8de703f25dd26086dc42ed93934a960ba47c956c35753f8ded7ae256cadb1175892808f01a2f84f29848208eed248bf716389575b472312aaca9d8b62890262e8d2301fb7dca63dc6c0f6e274ac570dcb72fd3e1017e21623460ce215e00c650fe1f0d +generate_ring_signature 9b7e917b3ba337d24633f9def5073570b217b796667cb0e0f2fdaa48cfdb027b 256a91a877d54695bebfa8cab70ce32c2dcd66f843a5d4ae0355de4b7e38156f 2 b374c00252a500dd3d84a23d08811b0cdeb68b4ee5ca7bc6803b657ceb5ed120 642b5402fc991cca3195b4042a3821fb445817f31e4fbf7901e85180df35b1c8 d4c7fdbf674d994b49be849eec2e83f2d2c128729407d39644ee5b8e3c2f6f0c 0 e172958d30067cd72ddf575c20660c7ae015c44ad5d56a8eea2d36fb096cdf0516a2f2c8bd094f3bb3bb6ce74415233e5f79c3b85399d001d228d3fc44817c0c4279494c319f26b2be006c69cd93c315e87a9e25216cbd13d61cd714c5644b08376de24e6c661de22dc2d1bdc6130acddcf94f99fac6e29ec95e9f14fd942d0d +generate_ring_signature df460b12ab3872bcd6b12bfb3ed7419d9570710d8f8ddb6814e2017c8895ced2 907b621a9d9522106d5b93f60ca9f8ede7bd003d50ae07ad8c499a334b006300 25 a2164ea6cab42425226e038c4607f72bc0fee4bfeb0c0252bb7dc7e6463b269b 94183f8fafeab2a11c294f3a5e91b40833696bf5835323409ed81fce2c11c0fa 6ded7fb7d68f664f474c2e8a6efac4235d8392a8211144ffe81c90057d565bd0 ff542723a56e03c502316fe17fe0a8e33752ba8ce2bd812dbe588aadf7f131f2 445af05d9b0fd2c6dc313f8177de095c2a322cc1acc256994de50d2df093ea45 0b861a0fa13eb87eada7f337a672b6b0c56799b7040f5350d7deba0ec40d34cf 12642a368c2b814433127ff12243c31f2b61f5de83ec1504d0ebdc4c88be4996 3ddc29baf23737f8bf71f13270d230c745947c5c0cd5b2b484f2a07bb780e2e4 92efa89c3ad4f31e5b366129a9d32346c6b94db19e63411534e0a4adbbb89ffe 3a4568e193f10be1a2efdd03298c00cff3ed8d0b685a08b574075574c76938a1 65cacdef6890b8a93d104991619469e4cc14fad5b8308b53686b035f3cd7b1f5 95599b6552c06dbcbc0fa81ffc67c88590ab7c89740b82810119b52f21c81d6d a6f20ec72896d5e39c80a951e4cf409b8a167c9a1756fc9e504fa92b44fadc14 d7df2098949062de05f73a4b846bf4d52ab66ddc778a722a78e520291f1170e4 40ab8084da56d321fa7a6ba7005609d0eae0f16d26207f6f87c486862db68688 d3e5bb60f1d37bec4c00d65fe14fd9e4fe1d1df3c88b34f4bc9b456cb78d9be2 7442e6accd2ff6c95f701927e047387cb8dca907e9c9a09461c01c51f5511dfe 247c8e43d82f6e28d49293444e464b221ebdd79ae67b5ecec1c818a0679348db d45c5cf473f742cfc6b14e09517e4651b53d867dc7e35579de7133fcdbe73366 ff083dc2d11a4d7e029832965e483458e2593b17cd94c72ffb988c814b955c3b fabfa5dd56e41d2223068dd33cd0a446cee08472d206f908a17fbcfd085bd9b4 f3541c31c6e3ff5a4035eab3659f418ba8d33d293f690a148f039dd5a9d80b25 92ec5be24b228f7dbd2631f670b0c73ce72534edd5efbc5c44acc1401fcf93f7 9c4afdd02824b3cda07ece980458bf112afde1b111a05ebef1df1a639729bdf8 35c67164db03739c53c3a3924c71d481bd79be6637074ce9d8e96eb577aaf08c d15fd972646e1f1e9d8b3b98a05e897ff3e614ad9876826bf6370b2fdffbfe0a 22 a6ab70463bc5de3f010830be54dc2f3d59a1ecc521e8c059fa00f23ec72b6602f9a82fa9f852f7f078262077fbf2149abc283d6bb7b2595c7f1569aacfd2d508a4558105c5d73450d578aab5efbd4321cc0911bb6a4d8c99f99cb690a675f506b7ec531cf8b5de08d0182a27effdee6fbd7d04c65b9c54e94d660ff1865a280401486fcd8f26ca7e91cc24fa6319583383810fc909fdf88cb5c1bbaeee11ef0e58aed9376a0e78912444da4fa20e1dfeae0e57b9434db6fe8c0ef0fc8a3451095560770362029225a3600982d295779b36d0ff05ff432b4c450c5e34dd97750bb4d98479a8566a025d32c4ddab00c28210212fdebe4bfbed92a7e46b29cbca064ad2d75f14b40f364407bc885e38cda0ac32729983246ef1657124e541e4a40c40f6e84bdb5c433db202b59db49e953bc7700a1c144050be8646a6e5dc0ff70992485ac3ee81a7e773d638f9ae7450ac61a682f805b18200fc0c76b53542ce00a7f64eaac91e7de6ff53a92a2efe04d8d325ed0cd150c148c22b603dbfbf8102e41c7431fdb9ff2ed8e3640d83eec7df5380914666a8583974810d43a1ce620c89085b6aaaf3ab53d5d8ecee15e6184800569531a697f0a8f3a827bca202af0828675190524d1eb14392ace2f2c801927546ce80f83892f3d24ddaa020ba23008bfba031ee6ae2e3d73c9caabe6c34a34e667e9e91bb7201feb0f5e13010c60d361f9ec50d0584f8b2a3152897a50c2dd84990567254a7fb5255b1a82d11850d566802b1181d090df0c4aa627a15b1c7aafc332cf0ae52af1f81ce93495cd906c376a96092372fb7ea24a489b7ef64b323a83969d48cb366e9d7730931ab35095d1de8db865389814acd1036c58bed52495c1594aa0c0efce69cf911c6508a06d6a187090721ab4552241a01c94ed576286b957ccea0f2f8f694e095dc485603a59b6ce46216ca52c8db74a8ec2e445bcb3205d7fb0322098ff9142b18819b046829b8d4da2981567026fa73a27e719923c42b885d1f2f88f0eebc17cc6b3e0060bec75f0cf99ea85d18400fc2f426814123a612011b71ca6fe129c6ba9a4c09e38c6f8ea7f33a6ef49778ff9a8c59014e7ed68e66b16a4bd838c9387ff7330e06771c4f8279d83d78d4dbd1a37fb376c8b8a72cb5c64b0d21de66b470903e0ed55bb67640e6e67979e62d8ae917f3bc1b71679374d04d7d62d56dc3f8244e03c686a5c24c6769a2eb1f39b73bcd1a16c6f68f7dcfcf5661635c0a5d3691e90f575c9a0792e7c700bdbb06aee5cc497887f38b5f3724143e8f64b74c9fd7a109b07fd4e2352f6a37cd3a41c892dc477699cabaddb6dea3e6a98dab4799e6d5010baf2dad4878c1017b4801ae7927b113e47f1ae3fcf555dadcfe53e4f1cdd10a4202d30e627ab79d4f4fdedf4004dd383cee46ba0d12ed424ac5e4e634931706b30554a84e13cdd72c3b3aa3cf04f50cec37afb5d738dbe29851f41a5decbd007e4e6a697db06f55e8daef9b1398c43b49ba2ebf2597cc2fa8ac28f7caf26504809719b2f88229df23e00581ecec6f0d9129929fd8feacb5ccae30a96cd74d00301f4ac99f669a5788c09ae25ad2343f2e52b5a8ab2b162b21af3c60810b1a0682b3f38d9305234522c267332e0fa54ae9e4a8bd830bc92268757c752a96f204be039914b4f32dcceaf81e8ac382fef26e01261117f662a52955b6877838c10e03bf6582223f9ba4acc7c9840a56de283c384da8c5124eaa7036de43465e090407bf627fd2da48aad6276a8dd6ee8dd1836ebaed402dfc3af99cd08f35208504dfffbe0e392c636e3d33b34a2680964549e90acdf36f53c4c9df1d2dd305d20767429558a6357ef8c88e65b1b29deac7459d2bac2bc581ba363ce366869bc70c4e8aad373ae0ce2052de4aab89581d58125e4f8585e4a58906c687ba9163860706c7324c7ff516ef59154147c83cb5f6e4f7841268ea1b8e26a7bed239a74f031842533b144b9cba8fd4df301411b353de85faf349ca49ba87fce5e8aff55d04740b144d9c1ebe671f5eb3c2fae2f52263524ab3485ebd2309dddddb37dc73065f81c5c7e1de42c85782c86fdcbd939b8087693cbf95bec1c76d9acd0cbab80687bec3096793de0cc7a02c1c0cd4dc39b4b4d0285d5d13303315ef29b06be4035b233496d8da3bb1774171d5fabbf0f97d7069475b068d5c25d39d63e77dd50edcc58221e77f3b2fd56cdbe65bfa02cbc8b338c08816763a09d03e334a4eb20d +generate_ring_signature 5381434b8e1774889fa452a9cc4498a7c7fec904eb382db71c7477f3569dd71a 5f7f404c3c23b4b1f46c6a214ab03179c7b3b85b765a5fffa6986624b88b3b9f 5 bedb76a91313a9a683ec1ac415dcf6d530e8340168147e750e7eec6124c3c34e a41656af775549538b8f529a11d676587ff356a18f22644701d272fcc3fe1273 e4e281f7b69203b8382e4d15019cf3de4450671a0b87880604922d6f03b232eb 3782bb0a759f29fea1c4b377e177561635a7ba924f1f288a0c082c535df91109 7bfa951cc4017c97447ec3957a7c6c7e95fd091b18ea289afed340cbacfe1b6c cee59076682db752cb2ef04b83ec7f2360e2f28343ca1ec5d61a14070938f40c 1 e766317b4e10c5a42ed8874160d6e7d079e0888de19dbc4a7b181e313398be099812c01a6c84e8dacc7bd6f7d3da937397a032157ad21baa95e89983dbf066099ba3a55b3164040a860af2448d9957f2746e9a112edb6c7e623920ffd6ab9508925613797e74eb68d620028a788c976f0cc53955313c7cda1a4571dc1b2d44044729989ee3352471dfe09d5944402ee21e5038c3f210ed5f0357966f1c569d0053c419bdfac45599bcec64084d3db6e040d47bbcd6e22dad5664e71c79b5010649b598e71edc39ea0d5bf64dffe06d1da396ce131753c59776c61a13cce984018c5694e566596ef915ebbd4ad5e95cf078096e88cd3c7771693b3476da4ca7031eeb24b85364ee5f1f127d4cfb175983026e1a9f05c7e5a433bde8605415b4094226a5bf5055d242665313739bc15bf75f27342434248e6d49e3c984bc93c300 +generate_ring_signature fe5adaf6940ff7400b7b904ca7fcc6cf0c57958e19bd716cb71e321c376257f8 74d2c637d8df2981d2823461762993018247f3067dbfbe6b19007db9ee222b43 38 4d05b0094c85dca2c7c26a5d9e2346c2fc9d7bc659cc5b2de6a30345d06c481e 74c42045c7f7c86bda4833c4156e6c5b3e68ed53f01fcf54ed5ed954bb1409c7 fc25a61264b0bbaf8f545c94cee0ed30da674ec5fdc7b60a23945becb32b5903 ef23df83948eaffdc552ed1b73547db017db3d06c33d17ebe5d7bf276fe51759 398a58fea1a9b6e579e51084b59476e909202216cd7ceae8793e27ae4284b5ae 3ae59da7855958f0e1ca3138459de077fb6682cfd3befdcf9effa41c2cc37761 bcc49087e44c272c4caec5000cb4ade9345826a73af95ea0ef40228b98c3a3a0 9b66e91129fce7e830e2c0d3336170c4282f43104f05bf12544841a5abd33d0f e99aac3d9230bdde039be1fa1c5501c8448c29f9376ed40ef290d237cd736020 7c8dc981d1a6e897f26daec52859e5f73959135c1860d1319c05d3438f09bdcc 1f57d093a8efc0262854a12b5a431719382811642f34ca0a8cdd7066693e107f 3c8859b7dbb63a0cb873cbfcd57b255409ce406d852f06eeb62b89a39b00c6cb c61f717aa8261100e369b98eb6a6c4930ce60f1ef0e4d9006fab16ecde055598 2ade1acc4e0c6960f1b4ab16ba6a544eae7d5e8f4527316f687cf02a605c6355 ff1e5fa429de7dcc6c1a027bfd95ad9cafee5af28ebddd8e4c75f2ac0c9abbbf 4c907bc7a78abe1765caa9ad959b176309c74cb402d09e53df08fc78e14170d2 ed40c9ea3c9670b9d356b4108ede5e07e1635a3bb1a3bca40062d45a6a6f099e b774c04f5a0311e8cb797b1ac6c61958f56974ec9cf13bb83be55783282717cc 3eaaf512039a2f405da81446cd41a1320992fdf3ddad5d8a20093defec849103 b19c4bc93533c5909611ec5dd565bb88afa4dccd73a7a035329b917b9d3d28d6 27d95094608e8d243b5ad33cd976cee21e369610da4a6be1b0bd55d144a686c2 bf02b14fa25cd8d6581b82a2016e8d27d5cd12f4bf12d51352ab5b61b0eb2bd9 3212c4fa044997776f3b40a3f143623daa61be52b91d6e3976ee117cf8f15043 0140f5118e5ca07113366989c110cf95f1766e481df47b559679fb5af04ae318 fb89d48e892f9c49dd4cc4c7c374392d76dd5798f57bda10beeed58b3244108d 9bbaae2a7df50e1a4833e9c28eccbf80ba008cf03ab9cb39bc12663de2d9da19 477c8192bffbb3b3fa5d064bf63b4c16062893fdcc2aa223b489363f38c1ada7 25fbde76bbd7b54f9f2a45fdf579d1fcd645fdefee92b69de0a73ec4378c37e6 5f42892fb03532b42413472084f53004e8a4043ba40e543424458e381fea7a17 90ce4394edeea99da92aff2ecfd2cb851acc14c348d27ca34530da05f758a1ec 25c719087b26b55df2732eb2172faaa209472a35eb621432a2c3f0496c628fa0 6581efa56866ba7c26666a8390aa017a60519a83c31dd5276b9c0297485ef0f2 8cc000aa233f11373245597b8628ad8fc9820676a9d73965fb807e74b3921ee0 36b26582630775e24b5353ae8ab0b30ec56bac6920ab734ec239b39256cd855b 3b651b8b2bc43ec33c45a425885fdb3c1bac9be1b95a39898759aba466d6dedb 8eb09a1df2edc7c21be6d25bd2b5f5f1dc6c8a9356e9905bb6b14735bccc847e edecc26037f479b04a50c94cb477cef8250a64b7cf85170c228325fd6538e0c1 2de9d1c34260d978cb1ef26bf3c32427d44c69196f8ebb393bc87604d0f1add9 afe57b5a32e6d50408e3e11f211cf1a82b60c0181659874dd94c028797ffac04 23 a6aef818cc5213954ee403ff3e8ff12b9f11e167b7dfd88c4274f58ad50f540dd8123618be71414b970316c99e022a1ba33b5f8b08ae927931920b1a1a866809ef11678dfd7bb985906d3eb829a8e4f87e21b922d0107cf184ba18db0e05cc017654f3851a29ffbe66eac0cce0c36856de90bc19580f6001f1a4080c455cf503160cb4a772deb29d37a58944ffe95f4d918582bdd638b611e8d1e5e0b02a5d031539579e8e8e13c42bec3b501c1ef2911312f287d34e51f0e7df570a9065310bbdbb87c1a99a2248c20bb70c7978c5d686b9843fcf66e5d3d070e416c0cf7f04dd070e79eef4c3b17a9f254bc8e48c17a9ac9091bb9139c5faa23f18bae1bc0d270d7b8cb7f8c870e457a1285dd73fe752136205cc26c2bc7c0d4c7b22adba08e8873b08d19c6b8af5bdab286b4174fab65f75e3585ecf7f8010390765d4660cf9400be07712e3bd7bf7bf917c499edfa156fc6a6b49202752dd6cea91d26a035b4d8bf2cbb3c436c920615e1d286a8048836c7e80f931c8573aee92c3bbfa023196a63bc5d27e926cd163498b871b755f25153e66aa0287a9b176ca80edc10ad0c51c37259ebcc49ab5bb554a412887646d892cef791f50b3fa9e90d426e108217f76d143becf9a5674d1917b45df7b8081f8863c192bc689b07cce4d83b20f8480e71c3e37448d247c9eae4d3391f26b95d84b49e64226eec0759cb4e5a507a445b687f4cbf4c52cd6d894eae69a698780f7355a9db22f14664215a226120b410a0c928f006951f15a288bd5fe0155671106ebad7fda2fa9702e66a8a7cf0cb4d3afd52eb2e07cac7b28e9608530aefba8ffe5f1c0b77df205e61e476b0505d47f5bcda43f1b088ef4c7fe7d570421f1a0655a91b384726de2d71c0c822e0ba255a71319118206742b66d482a648c6143241645eace3cc1595a4fff5a5160f6bb0fcc53b16ca5686be15f692bb87166ba741f3682a45430cadb54b1078b209015d2e3619cda55de54d89d538d72389e3eba4199df218966797299ecbec3c09d35c26024e9d66c0457893cb0e7e0827186ee638f3af2bd6c01ef585264219050c9d8f247d85f6afc4a18939dacd8f3207cf6b8ebf32094130df7146acdf0a08b48f02dba3419e726055028cc296f2b8c0b23f1938c59a357088c728bd249203f6bf65e77129ae8cc7a7a06fc35a90d4f0f4069cca0affa2faa1e7e547a201032d5358760a57ed17c7d7e6ce356c416f76036e45a67ba9e6cf3bd24ebd01ed01d755812efc8924f580aa3dc6f97c9c801b193df77ce9d50bee9dd5a8dc21eb050d7aa8fb42b91bbc8fa7a21adb428d6d2378578da3871a02151366d8bdee8b0d0b73d191439003a39559130d080fb15462a9cce312da4abcf3fae679de8ae90bce88fa4f7b835159c8f974cac4dd7d0b5be28739f85b67bde1fdc0a8c5bfad07eb34bdbfd7cc8a97be5047243cc66051f6032cfcf3570cf8f2a7a174dc157b000d567d67c8d0de75a8fa6691bd89e37ee4e1f3849eb336cb5cec711fc0c6d50efe3c6ad66b93307651370f342f3eb09a3013b2871e3bc76be372516969fbf30b71f4499ae26cfebea5fa8f75b6b1ab641f8be58d47b03a9c35a66db1bc1fde02e01e593c0de5f2050d1aa428b1a71e4213ae63c6edd96a75202a0476162c370cbccbda0b90fc12a7f9d7814e8fded3cda58f1abbc53df5f69b6628b07c556b034687cfa34737fe1ddf1fa5a44263c7bd57c808f8c8f5740442f2d1a0f60b0f00c966db1acc68bdd03763022c2d2cd90ea044c42ed3499740c8a47564f57f2d07f72f097abdb0693f8a4a42a86ab1628ab8cfee0af976e6df84cdbcec5b14b00f0ce5774c3d34146e21d51a8caae9fb9fcf7560e3ae74402ec9087fe07a94f70d2eb52081a2b0a1946e05cf90afe7e6488fd640a7616456300062c99164870009fae52c90d219c3e1d27871d1f161b800dbc988c18624c93c78a81fab62d4770abce8433a59687211d58effc6d3f0a7bd74d2f85fb93518093fa28d43beccc20c9aa24436c5f384a9368a7c239250fcd25d8342057dd25d4a47b3b37f1d54750e343244c02a7da8f5b211c05f3c33e1cfb51f9ebda44efd2a98f04bd61f8b88029d4879fca011a84be30cb12765b24ed6737b7b4d2df508ccc44c7d6cf1f2ad02836d39e88c24c6ef5504ec0819b5a7ea30ff833baab0756eaefb9097d419bf0bab5d9124d0d8037af1420b350e5acfb63411c861c983b766d6bd6bf16550b50e43919b39911351d31bf9aee35a4d1161504c75551406d941f34c785408ac5d06c6ad567043ae7fc414b377e6ac0b7c143a4eb183d3798647a949d52eb2e6f10012e03619125f41d6635ad65c86a866608ba0607035815bd718ee7d611a9a8e0796def74bcf92707dbe8c41820dcfeefb02cea57f7705af9bea1990eb3494bb0d0c8a629fdefa6c1120728dc7e6190ea79a8515916d8a34406eed8ce982974a0554195fa8de29acde9f54e7ac31fe4c9b0dab168864603b50726e005a5ed114024eb35426bf9eeac0226ab9bfd00acc103902ee534e7926c4ebac4f579862f6037d6915c8cd0f845514ccad6ada6c583e42488bbd4dbb2bd5ab8982e1daf76805a85ae1c2c087b367babdbecaf45b23a78399c9c96580a9020aa57fb4e66fa9016ecf15be775062ace07257036a8bfa0f389a48bed92c2d1cfa15731e69192c02e032222d39707290850888c4c43a60bc8e5e3839950bb2bd33f519611152da09418f9ee7e459f8c0fb3c310c13fa15db255b8bff94e12ca5f6a29527fe6dd700cadc5c1ac75dcecb760207920b9b86648fcdbeab2dd03353ba74fdfeed5af103be1defd50190a641ab546bc8ec32cdb73877d97c6f4e41e5f151657ee0ea2d0bc7a4249983f38a5833535c6d62684f08d5859aa2b0faf8e33aedfc13cb4a91013f549d6a3acb4108cfc84bd5a9453d5bc55bafcfb01153062ef3c52f94a7bd05fdce0373114844bdf2eea82cba82df7e82346ae4b8b951c2fced404896868c0f5456bae6df029abe4533cab647e6f85234ae8e197c41c51ce13942339b09c801fedc365180a90ef64aeee66f89a5873a3ef76b1456ed76dc6f7f041134756c01ad0ab380eeec6fdf38df6e4ba2e9bc6fd33ded3fb3cf047b8e0b25053db4da0e061763451c19ffcf7c0935ef7731d0503e346a9e9402bec37f933fde152d2c0821087e1ce84bb0360ee236a916be89a3c228190da2582a18f7fa033f8065b40867f17a4525eef932dec5668994da6f522c549700c0209e5464dd46eb33848a0e36963fb727f56613dcf2ec2597d9a682dfe82fa422495a32a6273308ffb768007f0a44e8d997db416f0d7fc77900d315a018315e33fa854cbc86c927682644044a988446dc779536ba198e254861cb7ee18dada358611917ae3e350044d1410c +generate_ring_signature f156984a35c2e23b8074e3f70018e133b0151ccfdca43cb561b4a206a625cb30 25de6f3e0efc5eacac730643a4cd5c9cd7edb1344ee053a9d567cc00054da225 10 80e37a18bba7d24ae33a1d7649ff3e4553fe2fe81e3f02e6077fedeb8196aa13 40291bc8b23b8c6b8204f635c5048e93aea00fc9765b9ca5048cccd2ea77ccae 1aea5e5f3bc7287be74b7cf926f5def5fe3e697a0f2cd4290641496f5686fc9a 42868fa112f01f2cb5a06d0d3cd229ddeaf05444812cdc84a737456460761ef2 489869e994639b63a100272b40004d51bfbd073006eb77ea31b9398e0c280e65 d59a0b398d78df6c1dd670996d3bb2bc2bfc905ecff2f409da4c3cfdb2935cce 3135b5835d28e9dbc7558ede355d985107251016fd9c68d6e4a6f58493e725a8 35685fa298688dbf8b6fcca1aafec517f1fbe7c784b32e5afb5fbf49a897c0a5 59d50ddfd99dfd82063db41c06ea6c133dba427b28955304abad315960a44601 0410b4db7776eeb2062d9d0de8c446e310450c315d1d6b4590f732cf253cf986 fd0a98d6e3382dc916f50425f74faa15a54f79472dedd67f79e783d7e9241606 0 66c6cbf1905bd3455e6ac70f88c0ce12a620f55015b9cd1dbfb8320d54039e0e2dd1e204e8d99e7b97da8f3b0fa610e86a24658ea0b54987edd75f25e2a37b07dd988a91c430407072c17af8d4350fc0c36cd451d57c613a60cde97c6c24e90476ced43c9b84ae6079651ea2ca62c4c83c0f9841157f8917c48ab5a36884040ecd27a89383947d9eedcbee66979397f997c4f9a3eb93127aca56919dd0e6f202d8d282fe1cfd2d30094415adeca541365e64909ab92ebcfd052a124572221f075c0450e01c465db5581630e1509abacc72b22f98339d4605124053cdfbb3ae00d3a42cf75adeb4f49829187b5b824c76ac6eeb3873284ad40e625d986f243806a0ad08507062d41ff646ac0165286bc2a7968d80f128d7098245febf6f2f17091bb8ce616d176454ea8c97d54f8d87a1b8ab22eff707adfb4ba5120953551e0dcdd253f56d2e60524c9baddc2ba19198a9a0c2372be17a1706084ef6c95a2f0a74a89fe9e866a985e673c915df4a9af2887b803ca14f175b984309e8989876044e83cce4e48a812baf11220507555d07337576155d82469131a004488b90140c23caaa6d33889c2b8e0d06f9fb1afd6e8d35a40119450cd2336503418879090637f5c7ea6907f04cc6ad11d1bafdf95a693e703815bc71ffeed805b2ef4609097318ea537e07c2376fc268ec5911243e451b569b03a757bd6459c42dabe46e057bf1a4a04b1262061718629c35486156df3365cf2284e7d6049723ca48cf0a000b164814223cfc8a52b7f0637b05e6d27f03140d3eb8bd124839309ac4918a0db704d85b989ee9d7fbf8aeaaca115df92cf4955428c25d5e13dba56bdacd3a05df07cbf0932d321a5835b9a250f7e94a64b88fa1a22a393ef14ea49823f01c04 +generate_ring_signature 2f30fae8d4ae2a3029f82d92c1b7d911c9252d7c8cfdacdc1a0d6898868afdfa 827df04e2f4b9411e690e4e10e99a48989f506d01f52f680ba8205198254ff83 3 d79caf6e2e52289346190bbd1cca9d6531f2ad0ed0787578dd39942e3fd1764c 02e555fd4d2d3e10476489a173da84b285bfc9a615b2db12fa369d5bb2b73bd1 2af49d86aad9ffc414962b0355ab5cbce968255b15ac93782b33ee2a7bdf60bb 6db453298824bf9fc323b193f3ddfa0632912001ab8cd2bd8bfed760cbbb7c05 2 381df894bb15ce00664d01564bc41867159e92b99c851684e62b387f861f4f03577c37f11900736fc2d85b421f61b9147000f5f57e7d67a0f060b38b561a74039e9520dbd41cc423af03e2e27efb71b1ea4ea583b2656f26ac07156456420d0c947dbceac0d99c8fa9bde00188e0e27281384192966fe69492ce7dfe0559df015e74238e528f899f65792feb94d4a02036f4cd180c86724c41a6483259343f0220660c441f5535e633ebe00e933be025009c7d4da2c94017a67b9b67552c200d +generate_ring_signature 04d981741f06db1014504e7a1a150c757629e5a51cf0038e5ca10b82f1ccdd01 5a43aa8714f98d5318c59103a41a90cc6c55d93757d5b4d4fa4c8cba678e2b97 2 dc9e4861c27c92438d69dd51eb030c87ee1b28c665a57304f539f8f2b5057a6a de95c92c127208def2432d017fae3c17932fa1f4a87f3e7cf8ba852e9fce90df 3df0ecaecb56d3bdd78cca07a7d831facbc728eaf140156b3c4c457b6e8b0307 1 d529e6876a6fb48ac20297437513ed006a068b2a902e1777798dfa38829ba50d3ddb85b3e199f4ba6baf2cd4b5314aa8538518a0de5dc4d8a40dd89758e81a022dfd85fe8e9f81b3958a4dfc538196ae51c3cfdeed72f41599d6263646661801b5600e715f33ed6a911b32a1e28a721ccb1366b5cb832e649f23073a2d236701 +generate_ring_signature c62daf2ae5678f7783534ef2b0e478bfc62c38e91e47e9c91f5c670a43a32c3e 8e56865b2acbdb0bcbd3ec30f425e9649f232fe0c030dfe4e198b65920581cc2 122 107e7b9532ccff77588f80a1420eff9fad7ff0b4517a6f8c23c3646067519f77 f14259a195e6bc9ec0d19c41e5868ae5a953c2a86322fa6685b6d6ee48659a2f d19ce004fb22eb065c612559c72d815d1e941cdf9e26014fd0e7a99737113b5e a869c76995a9c812923ee757a399a026f6a3b8e4b36061fee4a888b978dd14d7 6c28c34595eb75ae1877ed2cd54e6b307d033a1c28864a0a437dfff74dc7d244 c2845162d42a124ac41e1cb54010cf4ae39accabe1a51ea5142955beccbecf2f acb244f9605a72f090bf8ff2888c29f960f0950ceefbe5b0c7b379cdd6f6f66f 5318eb446213e0ab7f65ab7f7a2a13dc75f1527fd9c8b3ac415ba0293f763535 e8a4a6298dfdf62bf6690702788763341e25a44103ffc0d5e3d5c2dbf7218a6e f53ef3c8cfbf2b6b99846fdd20e2f8daefb474f395044254158a53c42a978f48 e9b8144fbf13ec261f71596c7e665be794917af526e2559e629f7a0cc254a728 8accab850189ad36818710b1f6b1a728ac5754240c091e60188bf5f9932c9c8d d6bb04660f8bb39ac7f69310410f432747a3943f012f769d778c23b2e618c549 e55204661051e1afd00b5041e5c865761c012c3f00df898e02e0541a4c3b85aa 95419b11553a9bd3a641c9ebcedc0ff170797a7eaf5bb6f2d4fcaadb68ff4a29 b6f754f2efad72d488dcdab80d58941b151ad923acd88ab617113e5f1ce79ac4 0f1397036bff8c5f9d5b5debdb9979f643ab3e7731a71d4ccaf5f1b3f0094cfb 05aaa5cba04e70d0cddd03459e5a9e9d1c91dcc90f8b5e1f936818821da3bf9a 136a20c444fbd7537d38ab8eab266aaa1027d9c6bcc04c28bcb3e0b5bbadd89c f4eef42b3060c8a9061f6f59cc383183dd83a7a8db498938d59bc4a5b481d244 f93f32235f2a623f1824c3c5ef8bc265b9912e948c74f6a21490cd7ffb8d6178 25c4239f47f00fdf6ce05023c26e658fc1df904a15cb7be6f84917bc5d3accd7 c67ae4e33f5f40b8d4059ae5349ab867787aad142b083578c09a0e05159882c6 aab0d62bcefddd7ec38f567efb008222715d6095fcdafecb2b8b3b46bef74baa 4450927923685d7f5904423f03be687348966549e83c88605939870da0eee8d4 a96c244c9502d035822bfcfe701637f6565b778e0a164c4f3024976133d903b8 3e058f9f2cdb7665eaa2f736176417b9759f03440f14118a75682626870cacc5 cca0f029f39d6e49c5fa9e32c3ad4d7c0c2e1b96ed3c76fa3a1d7a1e3f0d6c24 88527726b56561a90ba36542cb2255ec5275269c6a5062d2477acc15ddb32b73 24017293d7174ae405aa564d219bbb960d588545af245311fff937e147d8cf14 b8f5cd08a15dc781d95d48dc57995d10b29051ccd8ee7c61bfa55c8f7287c769 f286e89c1d981f33ec0131465d13c69c7f1bf89929c25915f7a58f0c40ceca62 01560a0015e9772d3f30d3948adfa2684da2764bb6febfd8c8e04b2cfa881095 a1311336854fd54fc4b6b1d3b50b30876a61090b7f57337f5c935cf4831c67c7 48b354865ec74c8c684fa0a55fc6eb4b03601a88b19d5bc27bab15b33d1ff472 d91e7a928a3ac537c709dab71bd56e0b34ec953e1d827af2e94b9935f7b8b979 d4182c4ad7d1a116ec1eedd7542caaa20651c09e2fb3608ef195d8338ab31f4a f2800a6e78656ad20f1b64265d65beadd18d584e85ebc1788a95a43f24242a33 cc9fd281a25029934850752ba44d1cc0a2696d71168201625a49011911d328a5 14d6ffc61600e807450349198bad3d8bb1c2f0574ff45ceeb330de965a3ccead 9738da629a941436b22f709939a536bf83b0556216dd0f2845fe88787ac6f314 92101b93946cd5594f2e1d0d7ef795895e16ab4a356ca7682ef9b89c2ff32ab2 647a651589321a8bf691e400b158b801723a3e2594f53e57e715151d18f87554 7727dabc44e8b044d55b8fc4967a3d3fa4b8a7093c7b2f11f4ef98f3e17f398c fcd6c6d791486816317d624ee90f31e4744845074f17c4d32bf7b9c0d4580e94 a1e86f2c5521c5f3a2c08a815a0978b931298b4531ac19393a103da8df5afdca 6312fdc9bd467bd488e4aafe0336563c9127fd630d5000a395971aa0d908cf98 a5c7711e0f01d460a2a7684f52d059daecae915500777293d1a23bdb2d70c17c 964b9a63ff8dd4ce6dbcf45772c3ad3f3aaed4fd4c12cd414a51aee770231700 1d4f67c65d08991f83c2a742b243b561b48d34c124988a550b37c4a1d823017b a38b39b790880379ab0d9cc0031f934c2de61fe61cbcaf6ea1f10cb8ba8f4fb8 df780cc00c0e517bcaf9284cef02bc46fb74809c39ce6bca5b7b8ca2fb4b3808 793002b3c9df3b7cf35d201d28121443040f04ae028b915b6d1c7d906be11eeb 5f4c12aa416c81bc340b85c3b8b6ed527f86d2be2d0b482dd24c15cdd0780e68 b92fc8cd48e5c20ea6a02dbc39bbca73435503b90a2d14f405096ebca6b42282 6086ca151a34931b0f14e5d0dca7a2fb4389e39ea013965fb34ea6a5d6160679 3e327b0fd7db1cd554360311bafa4a433501c4032435d512fdb41bb93bb0f245 10a86dca8a19fadc268d9936866f1497cd14e1c7c5679c6b46087c0a9afe9430 8f04077ee508f7a21478ce91daf5c2441c4cf1e059d045e8f543f1cfa4a7b183 1309a824c2a0e2b8293149832bd524472acd80251b3515fb625a9a18d2154b9a 3b1a19c26615d90530bb81a9f7bb86d308545aa4f120af844c5e96f09d8cffde 11ea86e772631cd2a17d2a98933da41ded488f41eab4fe62d9ca4b7b84264cc8 320704afcbaa174d0cce1799d577e7414294f3ccfe624c32b9b2274e0c12e55d aa840db023085c7e25ec32e00e889bc20f8c0b190eb31fd4afed7e449e611017 d35b747f697bb88a04147e419c5c2c7295f6978d381004fbfd0a13a0dd03ea2e 8e200e35ca665cdc126068a396bed5f17ab2237e5c7199df656e0bd937a89905 877a40b8d7ce35afbae4930e435245d4aab06cabb47b0111da68931631552e97 a08384e059212db3104197b5bb010654eb9207d102e70ef77349166326193c03 7511f13940a761e9caa6276c186b3eb2dcf0207e51d04355b3e9fda1d408acb8 b802a971b268527cd7fd6340d1a63205eda3adfbc53f0ee74a7ff646a19a1939 5c3700a2d0fd744d9aca7cd23046db3128f42851107ebccb82c95a2eddc18f03 833c47355b5fa4898cdf0f719014b1e06da6e6a3b0f319205cd7668303c2f9d2 6c94a290ca2adc732cc3dfa5826c00e605a00927913c3f015930f8ef9ef6e711 6e3cf0674201bed45e77f57555978f9e2c845272e16de9e6b1fefbfd3d07505b 45d6c6981f0322f52f330c808fb6cb3aa1e1d82c6492b0bc2eb958caa42949a7 a154c2900bc56ed651135dbf4b1b170fe9cd704515511be2cebf723622ccd368 3485b7643fc64514f97a882d9254f4dcba06aa962ce431a69fa34459bb65b3c9 ac0ac887135a3a8dd90d58462aba6f56b8d2771630b0cebff8941b4295fceb60 692aab5cc46f9080e27da45272545311f81f211dda45fd73c50d7fd41205025c a497a4138e5ecd49abb5a4551f0b11a20df0a065a6bf60e5242b2fed9b365e5e 30a7a400c1ed7a182291be8661340f03a63ee2652e866d9bb7a613abbc4c1ac0 efc098265ed75d88ff4f13a37551b0869d98b046687b1001641f63bc3e7106a1 4f0bd67e5ddd120548cb85bc3e51d199faf69b9b648fcacc2359163263d4c21c 53e4b54290ad12ad0c8a672611dd74809f166e855bdeb86000a7c98f5c7c3570 6703b809a4b2bf59fc806f68be975a1659a3d67cbe0d234f31dee1eafdb19d62 e3302365ec1b1634b24a3b48bb83f7c2e38a4c58288dcc100c8e134d0a2cdd10 e7d79f0738996cb966e0db4831d1e8b65cd8438803419db8e28fce002e81e702 0334007f0d993e663b394ba3e14540c5c4f9fd81f45054dadc26cdd1f80e3ac7 7ec84f10398f31326b1b227e7bb2add02be8d56e984f3feff70c861c80a3c546 1e6ef0a38de147cf0fa360c09d7b6ba92d85f14168e9268bf9af8e28e84ca368 536bed6c36c0cfe9368be0a937587792be18a73c42b6ab41fecf4dca2b1150f4 d8952ff6d3e26c124d1abe310be551f2d5f5275a35d078bf5c63a2c1cb935754 c25610f96a764af7c483b3984fd45ff02dab67a93622119839daf53fd79c3d53 2eda99dfc9e9b48f08b73b45211d907038f38aebaf48b6db65e01129b2d51e7d 5b02900aa7122f079f9a6247d37341be18c7493e49630627a6644da9ee51fadd 02c54dba4abd237db31d8e7f911f9b3669c71ac3cc1323b8356b0134cee1836e cb664e0f4e6e95e3814595da0304901b1fbe9c76b8fcc4f605b726eee9245e76 a30b4b8cf290857cae6708183e0b8bd51a8ff712a28c48c79a7694db14669c83 e2e358d579be0aa9bea73dfbe3dc6217b12706c02c6acf01174ba0b8595e5a46 d72f5166516c71f42534055b3a945447bb0a4fa2d85ca4e842ecbd8c256db5b5 19ddca2d4c64985a079ed5188aa0e797b0c57d510e338d303c59926ff532b127 f57f07f0aa8c1e1ffba1e8e96e87fb32c6d684daf780feedddf9735169cf111f 60f02577a21f9b5a3e2ee5e440c737ae31788c8506888fe284c0789eb6e33303 797f365dda3cc8f2a2f9ae7754ea1192ce14d435516e50c4ecda46bca99463f0 46fc2acbe00d939a9fa90b32dafb8d52e25c14c3a597e24bc6f4dba4354d97ee c4ae6eaa44428e3f35ca2d060b85bdc2785b2dd2861635da1e9955e43980a21e 4885bb8998c8d86a047f382f94f93da569985c3566f0dd261e45dedbe26d5e06 0b60cda4da5aee59cc0ae63387dbed7baff10649058142d0bb6aa7bcf6a9a644 95d5c9123d1e42b5a953d0bfb30df52a58d40567447477689524d8c7a55799d1 ff9a4d5f9fbb0a836ccf7161702eb816986b33edad46d49022f5f606229a3206 28a2ea580b8352329b6d457a37caad67ed0a1c421011c08840a7436f93660918 64d940e93e6ccdf7b04189907ae27e4d7e00d96a954b68b72e15fbb72d805b79 c8137d01f8a83546848f8e84d99d7e049dfda8c5160761e382b3dddeb20b285a 60335140f03f3d75acf9005a8274769d3d2b3b60107e79fa1345e672fed588ef 9b0766e7d663a791ea3252916a9aa838172cf4753fb8f5f3539927e5c4d8d254 80d958efd7e7012112a5859f635c812273c787065597e4539febf5cf5572df1a 1e819947c8b6acdea2fad1d5f2799ec3f52f5423e2f90fad68c4c2f5eb272e8e df3d11080eb4780ad610e1e8155b4b31458b93ccd9e4af584ecd34b989dda63c 374e27a478c1c84cf58afd30191892cebc3b9ecbbd7254abfae24aa591fcc630 b3fe13b2f6ecaffa09d1f220ec3d2cc166709cedc0ce7e8c5c83f0134dd397ed dac21da3df53a5cbbdd0b0155ca9fe3a920a0703c152751c37e5a7bf845447f4 517ce45fc7a60e84dd81e4ca06235693d8cfb44d60dae8a9d1e9060ccf416607 b15b34ed110272f46a203c6f6534585144c59048dea34ae1a8d4680aa9f5440e 81 a12b9dcbd0675a962b748b51199bf2c14ce2ac67303f2bc5891b6458360ed4047e3696d8d2e8e1863390481cc5df440f0a4fe6c420a108a40fcaf6142574ff046bfa6e689e746b1ced5eb1f329e4975d609507e999eb85c204b1cbb578ddd70f6e87278cdd3d237d7fcb1f9c027393b3b77e88ebb43861fc9e9181912c31690cf80ff6e13455b54bb6f47ba51d5c032c6b1a29fdb84186cd04b66c5d3184df074e75b9d860d7a19223f9c2653c0724d9566c4b2da7c29d3c9cab22ca92582c0e1c2c7a9605fb9e58a011bde170412dc082b264dba6d7690b4bde99f166ca810a6dde4b3e265b9ee3202998062f463469fe2f1737e8ef2e4cf3a8d0af59d72208d5d15a0dbac55eaff3ce0a1e63f43a3fb25d7714631fbf851c5d5bccc71d15043e2415f0bbe8bcdf3337df35826e0d55ca14b971c879cb28a4f9f2d18afdc00a7164870761cee1cbb2d6f38c4fdaf9f204e11cf316cda15556f4f69a4595fc0d6a587792b037ec5eb40f7c06620a790036851deb5a9e805fd09e05763c8ba50bf7cb7e5f8f1b7c83bd9b24903b2bdc5c48f8304d817fdebd048392e32c393b000b6f797ad8d1002529ebeeaf10d6dfe46240c26e77982fa6ee0dcafe8d4909039e9ca9e234f1fa5556b456c98e107f31810d413d07014d7510aff625fd747b0175bcd76c72b280679faa5c80858846b670a1af06bd20f2c51c7fd86d50543e00b3d93e041e89ba3eaeae7192deb9fd0295d3bac30d57ddecb7d00ae4b095300083ed90f366c993d8fe3b0bdc61a740bb283883e0781b2208cdc78aba225a800c63bf097307a95aa1a9cc3b57a3e569622047189e62c758ecefef1f0475f27b04cd04eeedcc0b54e3ba5769f8186f30fd7e137abb664d437058f23e1c87b8e6069f0584e7a25ddbfdf9a076de3bbd3f43a105ca101217aadb75fee6ae19ea0a01a932163c9e18bb255446ff24f6b3dc364ec07fcae69373938f723bc07eb017011fe6805c5c05178eba5f668d01ecaa795d8f622eb775135d7e97f63e0692220cb4e1160f4a4f115225ca20605df5b299cd1d746d583cc8fc5245936102c22d06861cdac802a10ee07f4464881c4900c327e64df6ab533a43b70ec96147bc9b0c671a4cab887f949b44b52af63b04cceff89deebe9f788178d8472715e346d503837298e7e4256ea3c5dfa3270f1c67260c4a0ffefaf802b220591511f06e430fbbcc3a9cdd9c714c9285085a57b4ba5fc59135cd274d89214b88639eb76d8f0827c1ac74947f3486f0414a70cf60cb6ca693bcd5b34a73486cbad0fe2ec636096c4c62fcada2289e13c34ffb514fa646806fc4131dd2d968e54f3bad2a0e060db26e60ca99d95b42b7c3c4a33db22bd2bcc1c111e08d7c46e1fe04ae3e8d1e0f228993cc158b447af4c60280493037db98c7a95f1ae566a215044d816aeff50a2101dc4cd13637d410e598d5452fb100090fa01efa04c01f288b25ad180d7609acc3f67b42a9481003c491207a1a889e014ed040d7fd77eea45974469e074b03064f2098fc7b7f7304470630086f8ec3ac94c1ab9bd9a82a7f72d65c37b01e0b1ecc10f76c3f348ad2d3143235f0a2174d14ad8241982e951e94e80c11ab530874c8cf78b87b38c1c9f6fb0f7527de24c9cf72c6a4fdde7bb6bb01caaefb8207f43e25001f414e5b4ac96f8ec15c69b83a2a4c145e3268c2103e80131c825b007c18738aa7c31290a345f80175f0570060f2ef9b8464cb32289dd42f0911d10a534cbefc9adc5d9b383b87130e7f0ff8a78899aaf2e52382ed4eaba6e132890cbecf0c76f04d6bb7b674a3953656b228e0ffe07c09f0ea3c3ba2c1790334f0051fc98897ca6ce47d3ea9334a12b7d4afe1e2e74c343bfc71321e141383cbe10acba357c5b3372a7482a80695b5987ff985017ecd343b33a546365a53e09577005bd1e4d0e29da17ccdd2cc87733344d8a89a897b104e63755c12448d8323e4071f6992936f61ff9a351b5f0fe4da593114477307232a685de382289ebcf18f0aba2563e826275831dbdb81e4448abc96e6efd56dc43f67bc38bce297a3cf4507dc75b8166c872ffbec1b73c70cee93d9e48d059c431a74aacbd41d224919270d496048e0c9cade705315910b0be09c0f06d2c406812c55ee1f013d5dbb659108ab4fd5086da22f12c87d1b216c087b7515dfda0ef0893150f501528c284fe60713659b4c238ee8798314f8cb46ce5a83c0682d0ad85b9de396f339f6888aa805e3c3101c1f6cc982c53854418aa03117e8feb6f8e62ce509bc57c561443a4109507c0b65ef425021c4983d26eb9b7c1df651dcbac09cc7b7222d38d4c3f38a04771ab7d41b2b0ea9ca2a25b0bd5f630c83709ec8f65169e0f4234bb5bcd9050a51e89d943b8a94facb0ef47e13b89e28213d09e1fc82ef8d634749f8ce60a5026b55db7d9ae29ba1e573be501e9c6ba113e6f777217502c4205e35fc4c36ed0b64cf2d0cde522f400510ee33730c362b893227a27307272348ff01f12059b2066c46adce7178a2b5b501e47f35405cb4f7b3352a73c90c29972dcb0eac82fd06cc3aa8ceb261186ebc8df619ccdf72d48f7fbd57e49faf6ca9b37d9b71efb305478e1bf1a2bc853a90be93a7ed66eb71583106cf0223d44e814d85f23178e3039b664c5b2077c1b2a92b09edc153b5159b632b895f177b3933f907e903c1810732db925c1356a7bb99287b305d273aba5bd9931d8b32c48b62d4cf482bdf3b0d1b8252e49154c4817938e73567ab5099f658cfc4c84e8e4bc42496ddd7c9490c1536b4938c3e4a84302301a1d79941466d4b800114f0464dfb4cb309f4705003ace3033b9bc247c92a062eab746e4d439e20f1b668ce568400fd06ddd6f38e07799bebb719cb7e277276c1e9d1ea63e5f1ecc4c2e245922fe8e8415e78c16f081c060e4b718ad35dd0778d03f2fc067a30c0cdc73cb250508897be83d7590d063ea36e32b366f2832579eec83803f0bcf2c59d63d872c5d295152d500f14390d5148197ebff9aaac3e6203516e5eef74e5b02ac41b7630042fac87a90b18ff0d64eebeec9cb38221d72acf81e23460c1da849d321e8d749b45cf242f8100780c89087d1a3f41ae6b3a7c5f43ff51d9a10f3be80906b868db51792822df44ca0e274602df1a74a93606df1094c6bb9664ccc637badafd420b2b4a6508fd1a55030f27fb5a5c3dde04f2714b7be28e33850ceaf6b556570f69ab52f119bd344e0832b6958f0c14e0308574c4e0f8d0bf18dceee90615b21e43cadf7169b04d1505eecca306fde36dc309247bfbe67c44702eecfddca83dae335f7cf6672437940a7126eb05f1408dbc23c80b8262ec38453970bc64160a859156ec73fd8ec0a004d2010370a2fbb3e87f54de0198f5b897f83d74124f419555d4385dbd07dab70f606d6d0f7b65e8afba2c943aed1b1eb7dc4664ee16b84191a07c0b1322cade064e9dd86172e21bcd89d72d8fcdafcd9a10708f0ab735eccd883c237918cd890c6d34741571987d61e01ad17438257927592607260ca991436cfde1d970025a05673db25e6d6dd96be7d5c05db86c3fb9ffc0dc4ef972d56dea72435a2f76a003cace49e1b66c67095d42b3e9aa174dc27ee474b0da9f343650c9130d84813f0bbbd1a6d33210df7b6d39e7e50daab08b6236adb7cf7afed9ae024a3453c21001c938b07e0b4f22c966ee9204b3363e8f59a19986317a411ab7608b825dfe6606f72d21269013c4500a92e15390df3c7a25b8c2b9c21c86aa9cb3c29b58563d0c16ee1c3e57be466d2b2cadecb41cab6825300dda2bc21a790a4a5a95e4d49b01cc64b01070ffa3d921e93ebf7e9d36693fe90a6919b0c97bc6ce158e06c2b9041b6c8f3b76dfa0782b9d3eb6c046dd8b7c98549e232dd0183ce69f328d614905f24bdb33ff33af3976b19cc38125a721f9d9891118dfcce04bb6ea50d10dc40898f045614d1378996448ead574df21d737f5ebbd6c4994e915383e96d4a106044cf57f5973b27203fb138c525d94b389d96e5ae041acd6b4a6126667c531ba06b2dfdaf8da9fc9060ae76b79936027f0bb533be0bd3a769c87d8f97fac70540327f99471ab01adc44331e3423bc58a3e7efb5fb831b32217dd81c8b5f1b3a70c3be7ac0779e38ab9e69f9735121a5488b510f36169ef57dbee611bac7c91c700b5eed4d6c9ddf4c86c86c78d9d11ee537b5e762f03f8674c6665e4e261016309684a9fb50f64704f530f4c7d1c56fffc8dad135812f931f8c8812a0da101e704cae2ae42add0fe3954ce15534cc4d4a4a41bf3ba678e562648be2042bc752a01c97fb68f76c311539a61277cab126f11393cffa9cdea89c86faabbe7f7a7330831879175a12eef2945ffeacdb33bd9e2cdb6b03acba942881e8e00fa9c46fe0bc6d37a82442d1938ad57a2c889d0f1b4cd8147ac9b4c22bdb49a0eb56ec1a40cd608726c9ba99f5e9c96c89c42f5e617ab5c95e0dbcbfa66d83bdcc23e3416029b0e4fbb70aeb54e8b0ff14221073d25c70b6c3b9ed6649cb60fd8cac4f3e50888e724fa38b852cc3fbbca03733e69dc170e0aaa349264024cbafd1b40766302a36a36521a5c21fcc69a729b11d066dd729b85362882a77ccb901cf5dfa3c501de7642b02e13c7ee75b593e3256ea079e41d441f1b980ec3ce44764cb737c001e63bf2e4d0565e5afa488abeb7514184659dd201297299088be6b98fcd4f840f82d91f82d4665ea26754d13ed1e1b9d68e324f1b1d5e9c7bb0557207fef72c06d9f2458c110fa90364d818730967cb47b7fce754137751b99705cb8be1de8a05b1dab0a1f5cd6e98c7e105ad9d184fff34b17abd1cc7211515f90248ad4add0831dfde2779b135d636c52de5ddc47d237a21a391659e56509a4af0ad97bd880dbc284fc3b5d6864d38d500b659f743644121cd311e02fcb7d1cab547d9acd40d5ea960e3fb1090f157ae5dd728b569e5c7a4791d929a612a91eea80b09b85f05a7a0ceb3a7f84ee6d613d12956f26b392ee8f5483dd63dafbd340d39b29bad09296ca46787d11735d6d55702adef45fc9f1849500c9c00375413c549b3401400dd527e7deaf95bf9723a79699ed949b5188cf13127fa5a7304c70d5645ac8d02192a12f7bc261a4b339d1aaa648332a6943ee5b7213b4f436a534fa1fbf5c109bee686e58ed508542b29753aaff6ffdd1f657dfcb06b2114d2c3ad890ca6010ac12417457a49d12f10f847401250f2a9ce10505e30b2cf4a69c996bc2399de0b3abc7273b9b30cfab4cafb867072dd9138c8a5eb739b4ef47c2f831db224ad0f55ff7e9df4ee6553bc6eae1b5a4b05a26dac75f8c93f4b02a8d5e5b50b6a5f069cf691a9b0f7a038238b9c65e883927d4c1f7e6d724285f64d2e2e6c6f0a730d10e4411a5d148dcae1347fef3954ca5cd574c47fe4feb88bd9c89ecf15e07903627cb8bdfb1232f660beb0855f4f2e24bfc21826a3dd437bd68e14414a1b5100e23eb9b1df67de709988d74455381827beca9fe704f7f4efc9b4f627eeb01902564d6de66dea14c8a153d992b63d3ac47b58f00502124326c978f23a95afc00f383fd7a5310f810de2574e0b504f800fba375f0272320d99e949ece3dba456006ad82049eeed08caa969f5f3bd9996f31d70a437cb3dc61b4b024afc88ca640b8c14c29cc2bd455d9593eb5bfafdfb8b64e0b958aacd420c64b07cc022d8d00057b9d7320c35f191154277370db342b1b2e64b0b183193f39ea534e5a355320e7cd21682474e5531956662e7c590cfb06fa83282e110ccd688d9f4fad12a920778d19dffd022318b1ad4685570ca7e7db4f10d0149e73c3b733b8443634c2e04622428288983b52d587d4edf85fd1ac22eabfd25c376375ca32d21e00c32bf059501eb8a063e12bd1a2639adbc68ef1ab98243d1c616ecf514751453f234de0117a1f0dd65d4c3cf00f37161729b3c14aea2f9b8db7bcdf39fece1db5ff27a098ac239f59f4ed6334b676dc9255e73708b5e0a61a54e01f6e5bcbda9df99d109f534942b6c6f43b1c859b9750f7018730da2d2281ead01f0047fe3a0f1df5802613bebf4a6a3de145361460a8611fc821ba897b30604a700fb1703bf8238b90600aeef55e6c397bf49d50457462583ab0b18aa25cc79dad37ab73bc564f94003869a4573c26aa22f79b2293189089a0a2f5ddcd4bf3def17aa9badfbe28de10f33e4fd1ab609cbbe27282ceb040dae507decf82f2f6743eb2542bbf3486f07036c3a8441c96c76b2a1226ed5763a9c84a1f0dc3dec3bf6183c619cbcf49c930b4b367787d1bd90f471c2616bb5fe17b82883b72981f86d8da65ce383e22cfc0a5f14189fda308eb8df2095c1da9375830bc8e7542ff299ac43a27ae251727e0ebf5e1e35301dd5c93648fb3b28e623fb78b093671dbbb227527bc0c03fc74904527d8abd50fdf88669cdedbe0a407ae36d5ca25f2763b1fcfd4c4202683d230226ff737379c9c4e8de09b57bcaa4e9e4a456d66a91b7ffb12fe251c73df52e02d581443c4a298b64d0e7e7d0956c874caaab628c7a06269e22c37ee23b21e904584bf78b5ab5596d9878bc164e1b820f61d524624b61ddc7eae9cc03509d37089331f5de4320169a14c366671ce4b3c8f8c60426b12f0d2eda6871f749cab50615ff8b71bed09e9168bb4faef4901da92314aaa71887ab1dc76229c26f8d5f0fdd6885d397f5abf88e778070596fefab1f264d2f5d9acc666c8f06e6aa54090bc01d6cb418427af7afbb792ca644ce9336ce7ac9c4dea4ad525fd431f6ac1a0e0113d2842ecd445c64732e197f34fe02cf5d0ac0cee9d6daf61afebf9e9f9507310e99cce3b30e0794615d60650e38327559e0f0139605638b763e3af85e1e05414bbed64c828745396aed05e5b93dd677f28533d3c1cc6d6c8be3e65fd1c3070a3fba978c2d59228db763f6fac16d48d3595f558240504d0388a45b9246260e8a8aba6a4f1947bc8182d80de663c963d8327eb994d2f7624ed3822d5be02b0a430e2ad697f48adfde90d8d5479bdabb5c90fa822ff795dbbb3ba622c6824c002c2204522c94f051c86c6cbb7da443e17e6d04e4337119536df847431c036d0ce7a2f9a1589fdea991f81aef7775593ca0add81c742b86459ec10eac3b745a05a23c6459c51f742c9da61b1d749fca0e1a9b470a9ce2b935afb1ef6eddf1ec062ae7fa3ef5ea5d8764e71351832cb2dff6b0309a0ff2891f02bc679b5db4d00d9188a02d131b2e254290668ad6675773b6d871664cbcb00d3384b3c62addc80c0bff80f7fc384272e72d19430637e7d545b4a8faf57a24d814792ea7ad56670e3d516889f5ce0b39e934a604886f5dc00384dd956c6263097ca75d5d5fb70f05fb762eb6f2f19a068b2671a2cc968a4b24e161057f587bf24ae8ab1f2a4f39072c615bed124c101791679d5612186fddd858086ad9fafda3debdd230910fdc06f89cf810d85c410b6324c8a629740204925451ee69cc81f786584d3c79bede03c0790bb97e8073aa201162b0de9cc455b95c5c0cca7172331008ae60020ea50c0349b999d930e78cbbcafd0657f57cef724b7de63e70459e4921ffe76678040d92ea7e0d1817eee31d2de0b67d3c204fe36bc9dd61cc0a1071fff0ce798d77077e464e4d035e8fa5882738514f6c87d70f6ff23da373c648b5bee37d773fd30cef270ad09ccaa28be0b59d26d582771cbb5f882b6984ea43694e326157642f09e7fdf0fadd20b1d61036b2f107fe9422cfc8eae53a3570c44f8b330671002903cf6865ce3a8f024dfe7f54a1899cda525102b1de333df7aa9b35884a922e0a01dd7954c0bd55ddafc88cff982caeddb497c4f2d3dd5e366a6dad8c0d5984ff0e701bb8eed7900344bc6171baa2060dc0ccf68d8ebe42368cb08b6531da964e0c722151ba3a01d87aa07965dc2001e4f295f157e789d419c4886b1edb4c5ce50ef3f945adfbe7f3fd79d8f74211e54ad81bd454234b22ea6897f42d8a71a40108b2374a6efc028e50205df6fff4da404325c405634db665c4929b9e0f494a1306f2f715e84b97930a122d9efddfeab90b1fc40bfce7a0780d275c518a05dc0902ef96462e4b42c077b4aaadcd9355046db9a6c167d498f83712eeefed6319520e0f58e4d294e0e5622f2976c0f6d5e16d2ec459aa79dd471ae50cb27cd0901f0880ece376a18904364a24e81434987de9bc6dc1fabf9d1036fc8a2bebf59aff02cfb6aa21723405672174a1f79b4d92f1b38d603ab693e9224e25aec51bf26803c2468916dfb2e2c1a0dd5f8a9aec9ac6d0aa489f75f5963fa4506f914fc8800b36028bcc74303c8c11b349dc226ec2d744c4463daaa4d584320e15cfae83210147cf23fcb2f60280cc39a925d6b6bd26c8fd35be3c67a74761e183fac274aa0a39390ffef2a28ba4f9c2f27fc8eff5a1eb54f190b5aac51e94f58a82673901036f0ab01d187e4ddbabd9f2c52aa5739cecc097441a31e554debfc76da0d68400e2b130db60720102893afedf7213399e3d7286819b35f0bf962dea2c68794100bb4ec91f5b8d97bfecb2e2069d97ef6e0298889f14f128ae4ea90df45c2c060ea136b8def05336f47b436833e8b7332dd265f8a28bbd3c668720e6776e61340fe992c3440dc79da73362ec6fff8ec929db6bac08aab2555d36a30b2143496506787ab770bbcafea940f1c5c983bfb2a1fddfdc6a145fd24af10bb15f0f99fa0342f35db2a1847f49c2312cb4281c6e053f437e17b542b5e9839267693a5975033fe38b7779f1d34ac6e963f85d4d168319c47873d317751ec3c597744104c907e762d5ebe2be694416e1c76272c4fc720eb6b5247ffc509f65ed8830cfcf840cfed01f47c32d789da505af45da3bb64d9961aec551073e2e1c9b796c4adc8f04ab77acc1b730352b082334dfe8056e3b8386d0ea417733b747fa43f1f20aa80b5d94b10c83c3d10a82c574f4501cd2d31b95bebac61f9ee98d7a16c5a0706f089210f001c9a830529a44d8af62d394aa7ead10b9674e0a0f9ea7eebd3591ca019b71e0af91a81b2077e4d32bdbf907c39b13aff0196632eca310a20c4dace403dd4a1ad80d97386a5ee8c2b5ce1976ea38e121b1581ff27d7b6a4ef2bff2da0e1416de8a1daa6641d3f7b940751c2b0bc93c3367a3794a5a118d74eec9ab560c466d45b3f5b51f024703d94982c4e425a12d0d811fa171ef5ed37438867fe600d247fc08def60ca1d4e0a096b2908dd99347b4e58c495ac64dbaaa02167c620ba677880772c02da8ee257bf79c683a7101049d5dd894952a98ea0550e34f9a0532972c484f7a9f0379e6461de3719fdddaba2e3892afebf1f755c2d0a077110be04be1cb0d6823d848a140455c142b7eb98649d35fcaa10a764175377b96b102717959814ac071cfe1549100651543e39f4c72855b21f41bcb37916521e8b50d9727cba4330223b16585fc0138db67a2277ef8c182cee0b4afda3fa4af67950ebcf5d7584d45d546c13d07455b9a16dc347504888312aa67d55218c9b8f8a30a18548390de92893fb7d89b3ef0ab95cbd0bedb5fc8a9da03b9785d1578b1d0048ed384c18e7d1e3163d3702ae21f81041ca25c83b000313d557e0e8f57c1bb09a5e9a499056638f46a87edcd2b97131fbad44e39406f83e4964e996a0cef630a94cce99b8f0bd7063712ab23f40d019303485bf63b4f6d8adc54fec4614de2029ecca1fef6dec10256e65d99b9c2d30c84bd16925ac65e8199182c135c757508a8a0025b03a81cf3cfd2575d55e5eb7de974f59d1b87d66434a27661663f3d0009f70b97ae71e9f0b41023e2da6ef1de1d38d7294fe004e3198d8f63057ea40f096d37395154c8494018163039a23b060cb25afc2c385dcd309a82382653a50a42f05add0c18743f412de3a50182dd97cab071deaa2db3cd2f1796c9ab9d1b0bc70c620c7d2f1bab4052c50086d75afd753969155c66ec867463fbb7c12a5106c9e9d36074434e8cff3748712963119648314b33bec178e0494ebfc8152bff0855d1c7f377b6f0f827b4249f56a775fe5ec1cfe898b39ca6e75eb0cba7b52002d97e937fed205e2311e2983c35a97250a5cb0fdff19f261d44b7ebdfabfe600b5fab6abc7f93b79940172048046f20d09a8362168cddc062ab1c57d700afb20dff95aae285c494fd3f7fbdd0787743c9802c59996ba27320d78cbb3517ecb00f8754b3668a456847fdec90f137fce970c76ea2df4cef926f9eed02ac70c6880ed8e67cadd561897d53a0058576d6564e9c8085be6a9172e1f911bfcca39238024ffa59fbaea72840c0784add45bf9c2a8eb4038612552a2447e8e5fecf0cd108b3b62705c5a8be80fa4444589be4faf2e878b1abdc996061990db1a89b485c0c4f73ee2a4ce3c91c1595e423f5c55d6233e0f0d6a1f88f7efc04076fdf97fe0929d649a893004739be54bf5617617eae6808ea1a5a8af0b136a3e8e8771b2200d567067307dbc28e0c71d5e96b7c82a1288ab7c91edbd96eb753dc60082e2d076da509bdc935ad5ba97c4e553fa6c2bfa1e3561663080d482a74a6039290350c098d2b874d1b5366c980627cbbe6a3dc07bcc630363336dec6dfd65f4a9a5c0d559bc99afa62d8522ea2aebda7863a98d6107287c7ea0cf87a50d5cee441f30a1a07aeabb6bfb197fd169cd93e0e8e147cd01a31c5a6eb74ce425e38dd6a2f063112d18d5b8fabcc3fb101230903a52e7ae40de2b3cf4d9eb0c17d3e3098680ce81cd33c356509b8b9e93cfde1c3aac7d24f6f6415ab56f822eb0da7e4bc9f0f1ef32149f5b664fb4a16aeff554d3a15e5f2084720abd5e0b562a1cba24c3a0ea36d8bdd0174f354970c2a53154b8ad404c770c9e8595a4cf6dc87f869c5eb0d56ce145dac923b980336ebfd808361eeec862cd119b85c4702345c4c78e3210ee67e58cb1eeb0a2a9bea6080687d72f2a546c576ee392682bd2d6c236876c30d +generate_ring_signature a14848ffba2714efeb80f49e9b00b00c9f05be868c6ef973374deb3c15fed472 ee6d173380f86aa8c0b415cff34beb6fcd08fe72446c42932987a2b0634c9c67 25 5561cd976dd6a9628cac374b73399dd75dc8472a0da0213469c7be31244edf1b f3fc600bd2dba0fad79595424540061c2c366626626185a40711122b9283d2c0 537bbd63586bfc6a66ea7a4451949ab35a213c896889ff221c528062339f7ff8 93b319e1b97587be40a2ebffa11bda9a1a9da570f8d2dc162b9fb56ef3f94dfc f0d238bc1f801f8449c189cf7737a88c688ec27627adfd603a3d1ba6efb25003 4b9ca42a2c7bdfb289be0d289458ac0b4d17d75446247ae5fb08965558e945d3 28bb9760fcfcfb8463737c02bfbc515bdd38cd73dc98e4cdde116abe4d61b95a 63296aeb63e7a1aff8259e3111aad3a9607cd69dad4503d92fedef9d65a2cd5b 09f4d2a0b9e995e7598761fea97f46d411b72dfa75e97aaf8220c046b06ac8bc c7ea6fffa05d8764a6dacfaaa97af796514c6e51a0addf6f4bfa0724dcbe9923 0ec0d237df80c882d6c0585f0b7776e50f012206535b235fde8ab79aa950d050 d266e5d7803707c6092eab1b6176f3bf06d8162423b6d75426078d541feebf51 6babcf57170e33c2c1c7b062f8cddce695aefabaad135fdd45fd966df6c3bed5 41c1cff807068f5827045455361758db2723422380365a344b1a07d5a1da9a26 e2df6790924a9d8ba7602ed3565ff20d3162b1b823a01482dc026dc4e48838b2 4a2dfb0787961052af7af576838c021e0a24931f4993fe7503730e520750a395 e98bb7dad66b018de171f40b16fb1567701f03d0531d83780b17081a08e338cd ebead3e20c4137174fa2db3e1e4619c38e47f0cdffd4a97efbe390c53421ab19 7ae66e9f79a689ce1c421e7d1ca9dcd33ef099acd5a885489f8bb79a1c227b54 3c554006fbd5be66be010900bc8e88a7a7ec3a016c9948a4ea99ac456cea1e4c 19f1ba503ee4e1af42c230700b545e9968179be4631e72ad282664ef0e7f7084 cd43d6622d00846ca286cc0fd12c6f15fb636d4722335c2df6adecca8e26e06f 187129e443b8ad295f0f8553bfcded4c975e6e144cfac470a8f264e1c8df0af1 fa5fd064bacc6f8f3ff6ac5bcf160d36e24a4de556d01c7656675c1e73d6e0bc bd049deb1117a6660f143caba50eaef65f5600f23605b5af648090e4131747ea 830352ec3fc76c93c8f33707c4ba8ed11f00d0a0647ddc338fd8ebd845ebcb0e 21 3645a160c7a8c7416bb3229aaff0b79cfae866e84b70660a42e38d6af85f47046a3a4268a5d7063d1cb14301596ee8d56b4fb73ab5a1174597f2c483dd1d81073ab0a4b26c681fa20886475454e769ad06fa8bae0751d1a45ece79268bac2b0a1cb54bfb1f38cadd93f55eb5e5abe7a4d49fe33f1989618376e81a70c54c3e026eb7948bf45b8e3faf7e086017413d024a8ddc448181579be75f4cc89a87aa05f14c93c79e9b3172f444b47d6020f0b13a68afce3245bf138e336a074a130509bbbcc64b51f0ad467f8e16c10b2966de1ea54442291f044ac68e2a8355f7440ddded1ba83b1816a571129b36d3faef75348c36fdebacddf9799502da63d8410f2dc5e4456f82fb5aab1b11f2756b4593002caba548be71ff41ec73ce9d754f0aefbc1a4753efa967876e854f38dee2c29c84e42ada384898bc13065c4ac98200daecd16e8cc1d2388fbfb5ae0143af42906d9edcda4df0e1e6cac95b29726902ed97d94694a47a56725c5fb188fe93fd8fe3e2884333822df5d34e7a8d040002bfbaa60c508de15b95f4cdde7ce7c26458daee388d5945558b86a7ca867c680a272ed6496af299cb4b93f6b9c82b3c7beca3795ae311cabea4df69dbc7dde40cb0c943ecdac212ea1fe06a57703965d284b8dbbcd6f3a26d19cbf2ca842c5b0d6ee9d0b249b5b94f7ba5386935680ad8e728239d5a9ca1613383aeaf3dd70b0e0dd3c689ca8e739389f34368c86b2c4ad67f3f7dc6ef53bbcb22a9896cff720ac933b923c5d470746620f00cbd9e6178e99e64336211d0595a0a38d9a3c04a0763237916002bd2963bd50ffb5fb46dd2fb04b4032026ea967f0edb11fa77760e67446d295bebf33421635f2188e6a7e367a628b260266af6906aa33bdfa93303bd31e371d356bf2eb8fa4d4afba8c975b4479e5e0ac06270aa364373eeafd0035fdea718228d75b5e66698253fefbe06a21cf77a8d999febe1ec272a39f50604cb5e8bb8b3dc1d0cacd3b0b078af9ba2c9fd9fac216595ba95e81724c751e2054bbe08dac2124d0d25861bbe1c65f4d100a3e8663fe9f208a0adbda3838c3b0dd2cdf5550ebb34d3a7806555b27ac2e0bed1c36811c57cd89b7a9d0553dcda08a2962631c8414787640ead8e5006b6bb83866a5e73246010821e88fc74619d0c901e2ae34feda9fcbbb260338262417a1d7b001c430150cdd8f6c015a7cfb2053fb70bdda784a0d7fd7040db53da9cd36bc96d9a94d863527c07fc7eeab47e01518b798c5f8bb78367db7c4d2908966927c3aecfaa1660d67af9327e1c41b1095251c81f68fa2fe0e61f08f467b2ed8344b9ae5d16859bd6e615ea5878fe7b0d77cf395da8e29e19ba66eb25c023d52a9dc9b78e61d514448bb3785b911e9a0c2e3ee7ac7b67213b5eff5a37d8e9b63dac61cab79f20e1389dd4718bd0401c0f0cec97c762aefaa2d49d9caaf5e4b7ef83a4aed937999a2ad03528fe09d7d40f269718c3a6694b09924c625d5b9e3d147a015354501cc2f45e6f06d7184f8e00e4acf989a487bb03dbbb16d42fe0136d83487dfc0d0c621b089065c4e90d9f0d4108d39a0bf1e47758f66794e082513659bf1900017a57b86bcde486aaef8908dffacde7e2ad32da5522d890d24af73891807384a86b4db998e88b9b5d6bc905360cc28925822ad4ef2334197906b7d193c5a2fb4b69d7346928549b9b2258042cbc55210aff59348f27085a662820b9b7980c5e8b3ed4d40e612365e10aee0fc4f9142ce3cd28539fa9e2335b51acd48e0ae3127d98c1cebba86904574e61014d5d532c5b3cf2bfc7ea62a0edf8acd8b0aec02b0b6e0f2db2970c1be8e6ad0f39b6e148eaff96b1856e07f21c34f0b24dd5d180d8db45ede2de379fc5f5f2018122cfd60b4a69039e62e255faa45bc433ab396dc060077ef1768c486c0f3c0318771f1c5fc4f9b6fb22a48d943ad65d0de261f18beb83e312a5ed95ac1175002921ac391ea3c4473d013c0ec1530f8b58cd8ca2319867eb8e60b735d597100c4096aff355ff4734e706a6c1622de70d8bccf2e7e6b7ebc73d7ae1af8d4ee403dbc1b9cee5aa61a34a0d170c7b633a008f8699c724388262712e7ad4a68b930e480d180cbd155692259381018692f44e9e3bb3edf9fcce8e459c062335f0af0edad13ae2094d5fa21b0aa5cc45726d7baf324516835f67e463da6c6fddb5e2082e142acbc0356b05acf8bb0975c29d892b5be24f565025dc1bf689ef370c950f +generate_ring_signature 848bcde10abd670233337901f01e689a912ab82a957d8fec7db116443bf9b340 e21333cba00ed9a2043b791c4eabec148e706ace68b1a98d0cee2450cd206c3d 1 e8dc590eea03045786f3348ab4183bcad5889db33ee6bf3bd886c1fe04a1560c c5797d4ea4603c13ab90b70fa22de0fe7c786bb21cc0a9f4676a24940e190b09 0 0533f9317d6432c9147a9d639a2830a008d061d0643078926e5f4eb2d4c01303b9936698f17b0857e1986e0146736008e19deb4885d5c3aa1bd8b014cfcfe908 +generate_ring_signature bf8f12d11ebe4fde6cf73aa0459bcf44659cf1153f24e8ae638e1bb94f62b897 00874ae86e71c37597d9d1e4071053dcc52ce3fc3c077d199d0545ce9bee1d70 1 bb33fc863fb46e04fa5de0304c394e8e6459350da187f0744d96161cbf6ed609 65a14407eb89bc79f4aa7add72a23d52d3f11ce3b2a578b554d320eebfcc010a 0 5ab53a695f49bf7750d2df60af09accb1798dd13aae40be4ab15b9e1b0b2480a9ed068fcfe012532e4d84e0e2f7d17151e3fbfa3af679dcfd49998b39ac04a09 +generate_ring_signature df0a85ee194141afef80ea0d53dda2371e9f2fafd07dbe67ce07bdc4b81cbe6b 2a272b97fce953a72efa326d074eb663d5799bf362181069a0f300aa9a5238b1 40 6bef09b99d31bf876c08685d37a143748c8197e48ae064696dce6f7550a56059 7d298a398c44d0fece107681114b6cd376f826db3db35a3d163cefc048198c21 d5fc5e5b86abe6db7f05ad8ed0637f3dc595c428a498277241694366f5c90443 0e26c85a6cb700b70d94b5019775ee1878fd51c55fe8a0039d24c0ece356427f f98487523ccc8a021500e827ab7d392bf41dffe3124cf07afa2ef42a023d054c b6f4f67ffd303d61471c91bcf8e8283967d81cffa9f69eb44dee67b9d4f2a9cd 70d90b8a01f079dc920e639d284651d2ec92c4b5354c33e73849d5497b269fa1 d67f8eee11b95e8ffbd2169d837f8967a098118d4e23c7f04bb4e6861bdd2642 c6d5ded1cf7da436ef31a2af99cb719f980d454a478827d2c6b22df55167cad3 8c44c40075e633a8f84932a283b81616f3f42e2108ad6e8ef01805b8ea9da825 e0c2a68cc9103c99a58742c5fa26acdb8a0b227fa5fcd683e4cd7489460ccbb7 69834c62220419b47b7e650786cf005b11669be34131191e5950e73fcdcf13a3 dd069ee8c9a3c8aa72b54508e795513a9fe9fa584e88e1f1efaa7f216fd0546b 60f134accf8e0470c6314c97bbb8d0d2dab05310dd750e5f83ada29cdc11727c a7b22af5fc52e37ed1fc416709ebb753d5eaa833d2c50a58c166240e89d85089 233f79312d6123612983b5e5421f3456868423acdb7691d57006f1dcbce17c76 bf2687f9fb2c39d15922809ba94e19ca9d542c9232488b3d64d71525f5f6c0b1 a5f1b07b48a9915638c56a46d06165183b466c4281c5b98aa07454216f4ad3d3 3ffbe62f028bdf06c3a1cf248e44b1d59443e5bd4bdfc8d4ae2fb656381b8027 ab42d0bcaed8fe144be32567a28f7498ff4e15148a8f67395369f03ab6d2a7c8 d039bc28b611f9c6d23453999f9bbf84e6d660c0e80a520b4551f404d55fa990 bea53b41a2bc458e983a3a1914a6f273371eece8eb438c95a72febe3f3d3318b 04ed366b60361e5a88a414795baa05a41b102fe4d71db3408950bc4a82580ecd a7ac9648c38b86479a7470f18a5fb75aaf462a9941452de453f987a6b01b3f4e 1b539e6b1601d64bfb781fce6fc36518752621ff18019b0a56c53add8897417d 607fb2ab2b95bdec14e3f3dbd4c49e1cf0f1325981d0bf95f169f7bd97eb861d 032b31e9246a336bd6a38f12dfe4d44bc79f8e747cdfeaa08e3af37af679f5c4 d3d57e353f25cd7dcf6706c443bb0e43414659f0febbc86fe9720ee7b6e9aee6 2069046d400b6f12bceda297140eec548d2be2c8004721fca704b27ed15ad3db 254f5a8ae27428319d41efcf45bfa81b4f1b76932d3727dc3619a908acbbaa0b 89b0d40ca2a970e1827bfebe59c63c4be9dbc795d1d2cf96ad591679983995e0 bf196b6e1e4fb34f2d76d0454140c8872be5046deab62419dd663d45c00be785 d199bc6a3e864c111b481d3988bad8222717ec9a20c8474f73a548fd38beae4b a45ec63fc0bab26df28de8d5f45f1a3baa2b417944f5af6e601c79ebc8dd33ab 5e32f5e4d1000b64b4ad9330ad5cecac56ea4af605a4a8a29a64a443b0527ea5 0120815e0bc99c51d5aad54b6e036b686b73ecd14f52f42c16b8ff838e8c553c d478f4c276ae25b664cf89c4c97d02e9c38816c13ca9041178bef25a9a24d659 ba1d8093adb56a6e688111633f7a9f706e33fcd39af580fca2197794d5e783af 075df929bb470bc44d8986f693d125beda1670598722ce96eb28e22c5570eb36 edf1b048012fd52244cdfe7f24e2e3355fc13c5365dead6930aeb882ca92cab4 2c87fc9c7413339035bcac401c41db9a3b5ffe4ac316801636688e6df2927308 6 4d874c81df9ec42a8aa35795be4f56143a48b1991e4e410897c75571cbd42b06e1d1d36d04c9b2b9f816b45aa15e5891b86317c91fd78241fdbe4e775fa30b03bc7515fef9ebb893d078856a40e49453a896e6b55eb9481ff4ed581f5add6306530a57e55b626e003943cf5db795d86d5b6bff74255c288bca82672a2ea67505069b9c0b0ff3d8a5e007085bb684e61108598cdfa0f40875d5d80fb07a3eff032a0becba0f4a0badc2c444482a32d2859d990a891042ec236f6167cc2ccef10c0b6f4712b7321f743466ade2e993743fbeb7ff583e375d1017525c1240fdb10ba4812a32303bf0b3a20e4ec1edf625425b781a06da60ece291dec74b207a5806b920e5d86501737f1857d127e315c212c41363250356ce500d7e1cc0480b83033a5831cbb07c30cfffb3b927944f1f2e394b802e9b11684a0dd0e285d1ad3b0c07c72da2c4b365099bde76280fa7e62d6b4d57f655494ea38ca23e0d827b950b85637f95675da658a0df5c61f34544294d111ce4f4c933a4eed851f8eedd2c031665fc4fcec86d093272e143adf8e120d84588e0ea8e255a383fa5b0758b8c0e4b4e59e4c6e559ff590bb7f6e79cb687076341b50caba1d5dbcadb2ecd309d0868ec01f28b49fa837482abeceee98583112f94fea14379ccd269534168717c07c6ae408248f70139aa9b9c987be8068e790a899590e7fa444f4b0f8295e436018ecc0ea0305b90169dac5d29a23207ea78e7ebeb590cb537245f304ac15d4f00d93261cf7b8d57334a5f12a44c18abfe1ce8467b6360393dcab8ec29444b940b417861d0198f5e5e05475d629057ffc0341527ca766c9e120e83cfe5a8468a025a82399c7f77c6d76471dda84c4ae53644c05e5f72ebc3290da10f0388ceae05e8dfe8292757ffd5bddb66f751489759c0ff150a629d4b8eda0aba4868e918034df2cb0969a882479c463e15b522eae4be19356236f870a91cfecd2c54109e09471635282024b84cb3feffad8d59b8781e63034e3c1928de4a75923640217f015fed4beff5303710480f50e905e1e371a99309b76b52dc581545c9744e54ce0daa63b476e52187faced15e357b711f1b5bac1dd6430c4304da3507506f926e00e8912d151a48e404191db06e10b112d43babd1585c57494f2a7e49087bd3400449ba829a3a443ec383241fdd9adcd6a83c78be44d3c55819bed2c5059be0460b2051a5eead3a5e0c7088d294c4f387dd57c42c20670bf2dbe4c47797dddbe2059a0139e7148a102558df786dd0a1ec7b83f58cd081d244173a2a039c1b7cb90b528ce4323c607b5aabdaf6c3ab2f51b4abbb6ce136863f278e219e93a760db0db49122c7d8c4d9ec7d26605c282f827c421f9324bcc94602e913eebc819cfa005c9c1e5b32ea2f638bead0dd5c64ff1dda7558a402abe2026408372d8eda730c1c81b20ef5dd06a943d37d9235ab291446cd2f6beeabb75d6fc2d817dceccc02488ca210e44e4b3c345a3988de2d053729d6723223aebe9f8fb2f48ff2589c04476f10a21fc6842535bb4f36a138b37f65424d9f3bb517180e4f16b2f60c85086590124dbb590c8f13f4af8b3a4e0f3391a9025c6b901423d32638f3f1cfce03f3bc0f0dc7cc65987c38cde66a6d9969cbe48f9a0e2c3559fcf2844656860f017eff136e9f00663aa190dd4dd055e093987a705850723f91c35c1d0d16505c0916934a8110a24505a385a84741e96c806fec4514b9d686f99160500196e56d04de28f8b4829e5db6d35865fe2b7bdb16fd2b83c5a3bc98690cc265d37b9d180041f745d6f9c0f13150076cc342d13f3539c86937c8f8bdc03cae0660a2f95b06a097351f7c25f54b1c6c646d81e82c0394ba822f8fea35174ad144f8227d0c042f222281c66d68e4098544036fd79a7bb404813ac4fa0bb29d7c13359d61e10bfdfb1fd9f8f579a16795d4be4ae314fd48c9b26c27c08c1256c28c17a775c703a35207704892fabfdb69ece4b6a3caa7776cfb31b49ef9959ac3a3545b9ec30be4839d7573160191b8db60584fcad0de9a3fd91156e545f5f55f0729b61eaa07b246fe0aeccedeec5dbbb94737bd68474a8ed1495a899fe4db7ff500782a69072d2c790d2e0bbbfcccf05615a396c8990bbfc33a7c66c131382d7a0cd62e2c039572a7767208d64f7e6e11d7226497f21d1919fb70966aa3e8802b9e1579b809d6ed96e0b122560812ba549bfc764429c43124ddf35d9a121ab1e0c83b00e50e2d764e1b669628c21a7328082dd01383edfd7bcb47f61484f45b22abea3c6203e2e74bf09450ee7f18ac8052d8665d0bf6237774b6a3242d90e78dc633d279043d61d8b59d1f11f31b3d0c26c7bbe0ff00b6c32580776f20dd529f477744a905ca0af9f161f00950c928ba41eba3bddc0eaeac9e9b43b664e8d639a5ef45e10dc0fc96fdeddd394592a02e839e6a58670ab56247af41fd05d0a41f6c758bb602cdfb9c42175fc564402420af47c3202c535cf10d6e301dc43dd126a380b0a20ff7b9687345b3725ddb46638bda04259703a93b06e293f7dfa3ac7ae791b2de0d8ac5e6c7bbe757db505157ca03c41812c79398d08b758bbe5537b93442c83508af4a68c03aa076f31d8639bf51a1d484e8c59c39ff2c51d222e29d9fd846810e7d808cfff412b420d9a37cccc5a90711bccbb63354be9770c4a79a0cdfd2960b7d0ec834aaa6a01d090772628e1b379066b4bd90b96d64a8f328e5a93dd7410a0950f841966a6dd17be8987302760d5dd4f7bb73fab3e336fcd48ee9ee1e2d0574ded55459c2e274e286e9fbb4380df504ecffdfbe824c27f1be258347c829099b29b5cfe857b76e254c1706ba06a7c6a3f3503abdfbd5f1c2c2640858e44109c0c92d81e3bf8d5801ec1ad0751d3b6f4d3c74f60fdec6219aef00282e604d021da94412c1f3be683943b415e3b4173493c0796b31240c71d1888a1532613e030e7710ac4a854d9da1378b844fcc03cbb6fa2f0b0737f772bfedfb78f3d5a009e5fada20ff489d0d0e1618158959e5da020d4942270dfc86ecaf8264fb878c0e76f7a7bd3363c804e1aa689453d4f2eb3049be20a4f828610bc4608660b1390bfa16cb4bb011fda6a992675f9ba2c62885a5cc06351e1e7743d026d8248c100cb0871a33e507a35bef755b4b890eca47301119f15aa58df1be80e2d0a18f6804603267ae831d1cb28a44b812431ab122a0f86d7870543ca33f4d07f7c6f2c60d570603b507ff81f8750fdcdde5db1b27a0bf21ee9bf229db879febbbe1694e01e9a4aee526f17d4a43a93d8ec3773dd102836de550dfdc74a090546d1e068e09ebdfcfab8ce3d770bf5a557c5901a2c3eba28caba30d3991deb30bfd67b765006353c29a5b5286d5fbccd8d53b028de175f753f7e60902d11560568ba34f7d0a074223a0fbfcb18ec94ae252d4ee0bb25801bb86e746fc04105910011dacd50a9877927ac91fb1bd9472662f62743c6879ddcf92253d8bd60a8dbcafb48833074a32f36c178b9607af3a988fe3bd1dd2d4d656d8a9292e999e3c347bb8483b03843dc776e2856098d9c8d8515fb1ec5ab86812da5554ffb93e76b0610d70dd02 +generate_ring_signature 6e347846fe39961777444e1e80af1c7d60a27b68610d1945c970a3576c8feae2 64fd00504e7049c67a7ed50d9df0409d46e36f3f406b270c75885ab5cc27b449 3 93fec1f43399e0a74813ea70a1fe386db98b0b81b22aad8692b97504561673e9 90ff1e044b91e8d09d7b1a00abea0eed8ea426ad249ccec3fd2d49de89ae2650 fabe55381e170b97ab5f5cf297ae65122fe2d5096d9c363b4fb31f1f67b99f7e b852495e23e3a58b4d0a38c6914c8783eaa82759149a4ef85223836c3c70d107 2 d76243cf532b14d66ce4b94410ac8e2a3a2c3704e89c37b68ef4025b1ebf6402e0a17d437733b8528a8d5078e0ac0b8b29b30ade19ca5a7a404919c140224008b403752879129c2caddca59e5b5e574df024915471c10d4399cf28a228bae2019a86e60e6c5bce5ed629ed9495ac7740ef2233fff8ca1cda9e73b8751fd21009a8dc401b4cef22008a430ac5b499fba5bab6bdfb86e12a3e9ec96ce21987df0037a5d7a8719000736d9c3f86ee89474e9eeca6fea1527c49469e9711b6e7ff05 +generate_ring_signature ed828b8c0380bfdfb9d0e22dd49e10ed6144c1d8536a56a5dbdac7e567413199 9f90ab106d367eb75aac732fa9b2cee0724e1674c71d0e498e401a1bd4d79123 7 b0e439eb55189dd705b6551389e8912c3d15562b0607180db0a79c8ac03664e7 5badfda132f641b60897880f04e86658d5493e4535e8997be8257656b1ad0456 d7dee84d3b1a707c352a398d158813fbaf472db2299f63c046fa94ca2896e4f5 a6504984bc8da79f8bc23292486717e49ea2f239e38fd345605e527ce22d5fbc de86ef2efcd7314e4655c8ed559891cccc6f874bc4818d43401e6df8825029f8 547869d3de9609f8b53d04dabe5e77b7d37a0572155796be490ca02481f5c779 462b265c21320b6fac046b1cc589d4ac2dcd64faeade79c87c65c9d052c196ba 249cbcd88d262d168e962e5ecc594389d9d5abbd0f8c047bd42d640e66a3a802 4 53cf4211559a1a1b953447ccd822c7acb64ddeae8e5c5eec630caed69499dc00542f53fb9e24553067024fdc13ae367ae337ffdcc6a6207d48b3999882442d0bc32462f0f7b02084a7e1493465f4718114d98182bc1074bb755686fceadf9f0fcce117bbbfb2c267e820535a29494bc0a49edffbb8b0a24f6d1f294bff900702e947e7764c499e0d329a6b813cf75b65d834ae24e2d9213bc38bb04951495604284f44c125bb329569b86672d62c98575eb2299cfba87144b77dab1d651d9a0aa612e66e8ea97d16736a8b7d0198ee01e0b44cfa21cdb7098790c4aac31afd07cd1829f403b85d4dfd7e25552524394eaa026fe75840d22e0f60b131edbdfd0994ccaaa7d31fd856340e91368c7468ff1ae3e38329e3acf738791ed440767004539dac682e2c473f48da1df1564508bbe4e7fac107097d0186db43a9f4ea600ee45470e8062e6020cde12c214a762980734ee01f2c4cb8992b98940727d42501528db9ac111a75e4f0053602b9367222166d0ed1298bc00d82a8a1d65f9e0a0dd12e60919f8f896b48d594fd737ceebf88183e3036498ee71156f6b454d79908000f303abda2bfd724c646526421f4d6beaf73b0907c5a97234b8594a5e7b203 +generate_ring_signature 1cf0e1e8aa1d2e4a998c2c711de4e4e77c29852aa0b11320c66641ccbea5d903 913acbd4efb05feba90a06f99c3ee09f06dcfdea1db58049b39e424b239e6d2a 3 81d451a937c385363fd835055805fe46621c340af7a9fc3033dc0345bfb62389 67e02d7f4d780549ccc8baf341366c5d47e219eddf0d3a317a1c9a45601d913a 0048c22a032e2e65aa0704b395af62114ea7f4d8ee77add5720ff203e3072d08 aa9e3223682f720d926fb1a9fcde9e01fea44f5691cdbc23ae0d9e1f45899f0e 0 73bce4f3080b85021e884b8db2e6c0d5a0d07949599a3f55172cca3f02f6290bf02caaaa1c8be1e1c8ea851f0a766d26ff3b5c8d01a94e7cea5f9649c4c54a0c0cc7f8ec6b41ae1ca776b7b8f0b005771750f6ba450167c41f0df0cacdaff502c3b5b0e62bae62609af2c6b23cd36f13d50b1e43336e9e9526c7783a1d7deb0c2bf2e67ff9bc78924185bd94593d2fa61872b49e1f5a3426d02bf41902a6ce0ae5bf26ff85c3abeb00da799aa11ace904aa9c9085eae5c8415676f8a0b65a307 +generate_ring_signature 55395a52c30ec36b0e9e536b89e2ab65de5d8f3679ad3f2919889f54c8f9f1d1 1003d07084e00785cbeaaf138473ad19b2c94f2e3a649306c42d2343e9dc955a 10 a208dd08837d95e7e2d34e1bc34f01a71197973665ae793c4b93be7ac7bd8c55 4c396c28a505cbdd2da61c6d6ba93ee661717a8f3d17fa47c2d08b6e9dbbaa4a d6cc9fa4c8ae3b1cfc65d779b7170ee8d273332459e7dc792b40c8137e45d429 455bc8591b78c8a38c731d3b5220e368c60ceed1ad0d514d5cdd41f9e169de27 27263c414bb2c41b0c33bb3ef981d1eb0bf6c606ecffa33583c4891f17cb6dfa 2ebfdceb30a04a2527a901222de635357ea9bd40b9b231c1aca324b9affd2588 8c6d0d08eb14793b13cc490b59ac158c51f21eba9f3ef7c66d8e95a46bbc985a a76771d55210fcb4016fdd42abe5981ffbfe77dcc62bec71345467d0e9231638 fbe5277bcb58bff61b3a9c0a494fa9c4efa33b74af33aa6f3c9c4fbfa4834341 a06350742d7fd51d142c6dd7066960812acd2666ea31158e3fb2c405dd94b4c8 65e0fd5aa138e313adb27ce1545ab1b91a265d265201d39cf46e86884bac0c00 8 72cf9a98f9a327243c9412772462a9573770c91c1cb11a5bb34d35926f2e780272efffe0e028562c4aa817a1314a7a2e3bbc46acc66267085f0ddc5d7ca2a40350b5be5c81d6aa1338b62a1fbed3ad984e863e93d5bc52c10eadbcc3c162df011bbf660181cbd6c0c5e595bfa5c2c8dc5159630d1be336d9a3bc4ea918a18a0264ac6f429a0206c8dfbb462debcab5c0efaa1ff17a704ffdaae865434a086e0f61e989c4e588a74b601ff0af770827a4505cd357e8fc376ed1db6384c5cb0e06b824161121462f534487ee0a83aff30a34c34c9dda9d9dfff87dbb4cbfb47b0f74ba77f14469e30d217e7212f7a343781dabcfe87db3009688185af3585e500287b96ea69baa667f788606d4be1c07ce70610fa0415d801765c5d611ba50f00935ad3bafb69e1d6c42f4d07d49b430496e7d2c0453396f2a605e294912b33d02e6cd306caf4661156bd454595ac71e8adad0a1ffb2c1519ec83e294ad3982a0acbc30528698b894ac31cc8a43f807dc5d47492a88d5a8887887e7dc22640e5059d4e0c016dbfc48bb4a852e000a213a22a17fc425ae4575a1f772206645c830a3dc7279187e551a4fce0978ccb78a4d776cf6b6211929db195c62c7c08ad66030e714ed7c30be6b0d6c3493e384d7db33a9460029e878a5a7e363f3fdcd580010617f61e28c03cc48984686f79096e56c542ffabcf069ec75da37a06367e8603fb36c0884904871185c8c593ea43047f9d9bafdbcddaecf03ad0f5536b178d04f56aeea27960ac20bcfa83949a114dfaad514cb9d04b5f4f99ca4388b25926090b08e607879326eea7a9ea5062de4ec198de95305e6434b4674429fb035a4d0593823ceee3cfec8d79acc7be18933bdb5c0a3f8312d81a02cec24a979d638304 +generate_ring_signature 1d548c32b8b7334669bc9c4cd2dcba88c49d8a0d60d1462109b7092df7652bd1 ce4329c8baebe6620693f387b49a1e6c81b6f21d34b8106d9a251130ff038515 34 3980e6d98afdebfb52ff211cc4e30d50ab3ddc3ee9c6384fc96309aa507c219c 2d51a92505ac5d6934f8361208da0cd4a6d391dca4e9a15b9e235aa736bafb48 be3fca2d2e08a73129d53db1bede2615bbb69edfeaaf47d171957bb579fd075d 10833d83c2d0a69aa9299c315f89cb827980c77d467e823b43833ae0d52953a1 12b4de7bc7ce81b358215dd31bcb4f9f4594d8349d34ec1a88d049450c991399 2c9b4d71e22b88413ec8013ff7c02ea8ef1182f8e279aee7715f7843d5618fd4 ebf47deebde5e0433d7e9219112ed0cd1eed759b884ddb62ad8557e9c41668ea 4a8506cd9de35b00737728f3f998dc92a27943ed80315bdf05d301e2487f758c 944539c4b36cc6556c8408ab2cdbdb41aa84eba489178228ddc3c39f1ecb7ecd 50a09dae848fa99ef42d77b4930e94e70c50668fd83988e01580a836c6de780d 85f9ec07b5f07d7cca4066a126a8b85612613a99f15d0b741df41500d4babeba e9087f80ca77109b278b46ae5eae201a20b7ba293d9facfbf3e628335b99b1bc 56e8d1377c0335443dedc7ac8ea523f78048a78d3d9f8d76196c752bf0184f2f f214a9a39e7c76150d5601ead22ff921b2825bf490ec301203d270d6d03d9f82 cebae7a9a81458fd2e1f7ef788ec73adf2733e589b1fb29347e3ca400ae344b6 94cf141b604e2e3f1f54f6468e4b6f400b5d201eabf3f56beecda66b6dad1660 0fb7aa41a196816fad5b70f382e48d19384e312d78108abfe9e822caf71c14a9 259fa04a31bbfe2318dd2f585d2f93fcdd6547125f4749bbc5c325d4256ca869 08e8009173306172c9a85ce585f784888fb5460c5c4b17bc4e20226a8c6d3b64 35453df5f7acd7b55179ce72d904e282326581da466a9c830afbf886431003a4 c1ac725234db3d9e7dce4a650955a27236a96e37dea66e07feaea9a75a630cf7 27afd4f90a99dde81a54bd20d29258cb143f8301037e97541d0dd1dc79f3f615 5913f2c303723cd8c577e1608ab7774efeb2d69461624f4135c843661d76aa45 30bf3f19121a507b93fd79f62425588ac2f374f2cf4bd143c21108386a579e35 a2287ba15d5634e3bdb41c7c085b7ed3b3c3a249937e86ccd6f24a5e492dfe0f 65e9ed396bd02ba59057b255100a98317db22a0b84722b794c0b1ca0bc51267d 8555eff910bfd9d57b50bdeb72f423efc8a4226a27f99d3d29c062903091a916 1c67a97f840db3e31db1cb43cd595b0ae2398451f3a20048a63d5b0f144b890d 9ecbd8961dd499621b2c8a9ad855bd495a3d6974f792e91ac4b9e3789f8371c7 d4e8fe74ac8e1ac5fc36a0791ecbe381d224b8e194ece5b83b3e33d28d1b83d8 b039870b47b69cff00e03b6c69472cc4f8d34b05bc4965963b4f98873aacac7f b14e13d060000f90d4e27c3f980d4441999c555c5a012930e5784cf1741e4f2b 26f4b663cfaa306f42f7fc6e53a6bc5dadf8d372be8ec789b79af32ba5b720c4 386d105e5d55cb122ebcc09107a200e4506dab64e8d0216575c0be5dd433f46c 699d851e6fa2f663b7c5de62cbaebe4461a72519956d9e71c61c9b412b7ed70b 6 713051ea078e821feec1c35da9ac2276b57a2da0380eb8f909890540083f550093e0e7a8529ecbc628a323c4f6130819ac1ee7b0ade97929fea0b39d9042ba0fe505ef08e8cc05001afd0b8ddd6a72e805735dadb6c875221766d82b789fa601d9e335761016f77eac57396f7050c0807b588d8b08334b8da1457a9bffd05708d113e4814c5a99dd251c4098b51ed303198f02815682494b380650f769b7a70b45c302e100a71bf83f7ada0cd8cf842f9eb1253ff63b4fd0f48c1caab2f7b30030d62217c8962bd4c859918c21ccab2747eec14b0f3940c84975c2c7eadf07033d8814e1b273c480910a0b6e4b42cd304073392638ee6d497bd58b7e3bc1e30706c274df61c35a421f7ce966381b7061aa658cfd1469a0c363d63296d062f102f647373279fd203bcba32048d5556224d0a63c1b42d430f38e5e572b19a7e40f6e766bc223d8d9b2b3fa3da538ec04793b065e0460a2bbd5ca5663eb254a940a98fb8449e717cacfd5a3cc58a10ebdea84d57a0dbe7406ef38f37ae6e03b5a004404b04d55d62891888d4884e61e00e6fb091a8a1c406edcf6ab42f10e14c60e58dad24c375792f8f09f65a73efeb25e355a0738b64c97151a45076a01718e0689d17df3f88390ab4cd0799317a9445f89564a6d298d3612789009564c870e094c4825e1f0ebf0deca6692e296ba4cf2a71b732b926c40c6ff19273699afa70c330102467be27decc1824a8e5b52b78a0c112eb8b1c9afcfac05de0ccab1300914205745eafb442b805256d12ef1558d8f504d1f63d2e19465c42e7246030708982db5a3d665f852fff2f52fbdf250b675ba38706f6b5b5052afe6cf3aa8910fb2f8a660fa04b1e938e6d794a3621295295557bf946e8991e2cf2dbf42c301006a6198e06156d079bb7ee2b34c58e3312a873ac99178464232a5cde168323e0a90d446566a8a28f56652526121d3bd7c8b65dfdb38528def61a964ecccd66d04af16b51481105e745ad3999f4d1019656aa7a84471e56656418936a96b5b0f07843bb795257ee62c50d7db84aa2a64b1a5d8870512e73c0412748f3709624401abc21d9cc851f6cfcdfc4ebd9a92078dcb7a825e50fb82b53d9efe66c309b60809342a79f92ed180608094155386526f92f408bde4691d8afd347c8b22897b08beae950df84aac57afbe98522c35dfb3c022fcd7629f5665afdee45d0180b60003650f34687a86caa06add5d3d5479537d2a1db672fc7c1d5b41384280d2b80e86393d7afd9991e7df8674ddb14000fba0eee44ea5b1295fcf60f943e289dc046c4b4b8b6de66125c068c32ba71d27d69a6df1eca96f6cdde2ef51c48b572304238efa7d43bdda3f49453e9e89780c849dbb7ea2dfa6ce0476133adef529c40d2aafdb0644e45133be798d96e777a5be88a4e12c7e0d19dadb854cfceaf84c0aa13544cc330e92c331ccbfce0710f94f1b353bdcf0e31bce049dc688d5473f0436083c1f01f5150ababc127d832f4e5d6ca6cc28b7a1ea9cc40f99f5bdaafe01b0c8619dc62d01f1ee11b17438694d5fd7060d701ca3f9ee596353c4bae05a0fa20a2d6a2b943d6b3629506f914b1b4f3c518185fb1cfeecf2d8e915995e670fca45c4dd442894520840dc701fd4749f40b1d045ccf86d2e0f6134cf9b0014025d8e45a672fc1b8fb92a47aa54eb60dc745e8b2365a36813c3b7b10723fa2a09b616a8a7dee986e5965b71bef0891833b8cac2db7ec6bcf331043def964a3002727c19e0ef91b75488b326338fcd0a3a84acb6437a7a5359bac95076e1bbbf04db5e01ade4a84a0de213fc061b49602247c9a757216e006996fe9d34665c4d060d41e49584a10e25c6285f2157290035ed8f2b0f5e6259509531616c7e013c0047ca85a84a03b27687b85f301e7509159d376e46876ce8304c33db5ce934b109c04cf9eac528e6550b82811ac472cafd4b9acaf0aadc61d660a52397e1718b01f330681b1a4e4ee43b7af1387ce1b90bbfb7fb7be53fa75efe15b2412f13070bc56be891946eade5595aae00375d6aa3998b517f38db9223333d4f0fb7d5860a8941c58240bfd8a920a0112a3276a740bb9d7334680faf2d01b60b6049fe660943789821109482838d0ae202a87c768190f61de98d9979863cbbe11339126a0a7f3af2c01d8c850c6a130ce1227fc0b9362caedd23afc47d2f5fda265d26fc06baa48dde88cefb588891b98013993260594fa6eaa2f267330d185ecfb2ae7f0a50797f8176b20deab8aa6fbd7350917a36749e7d866b847791370b6a695c920cc7f778190b997a4aa697a09e0f99d674194f9b7e1d748a485540363f747ce40136668f861b42d86283d41e90fddf83515df339fd186d36031d8339d4dd11d801226df23a16381527adee612245b3ce8145b1b001d4097417ddfc1335d344670a3344364026b202b25b42b67a5666eddf17a3ad0daba28426fed51688edf42009d7845447824566b6ee580851b3c51056490d3d788689e1928a366e1b1518f90bf9062d5cfcb2fc1e84d05021d6c06ea32bf5059df8a8963e5985eea1f91c900c0335377491752000dce817bec9f53a948d81aa07caa2450deba10dca4afa900a717c0c86f42ec95b4e6efcfb3034725c22267fc7f7157a082b606ffcee1f1506d584418bc9dfcd31868afb31c4bf1f00644b37d8b58c7c0f3bffda7a008ad10438a38a5781772a7bdc07fabca076fcd7ae93ca7feb3da46cd056139dd47e530443cec6a0c0304856dd0d519c262739943f87f9d52160140d1a762b239bf2f10c74962b0da0d0ac703bca600278e8fb27b7757df1374b505b21153900412f6c001328221a49f99e723060f61015840c26e857ee0d1b423d1e990eadd28892520cb1931e29dee406c7f95eff38f40d19a6492fa87c8db751979ceffd57cf442c027896bf79cf6e5b639b9b042bb0f280e1c25987b3c9999afade9705539dd78e03ee0e84b95d5034ec83d82175a3bd2d260ca39a4918a97a2814bd6dd27e9bd40ea44079f963e1f518eb29c311deb2e567500971633bfb97526f52915898a60d04 +generate_ring_signature ef4f2f75bc1938601bd314ad2ece967468b9eefc4cba19eac6629e095b950a4a 564f3416121dc98e2424ec9ff5b6c6394e9a4cb0efc786d399b3b1b62175722b 209 412fa61e4da51e9a34cb9d2f22e6b6a890ac1677269e5b2c71272fa0943a1c71 06d230669adfe9c9bf7396cb21834d95224ce6a8b90093bbb368cb28f57aaea9 70d621c9125a5128f8607289a8e56ab201e2ebe9d764a457e9d34b9e1c7bdbe4 5af63cec79f1287d8b65c40a293b383d7f0a56568fa51952db05619b8ada7d8c 2bd8d7472a61c4028996472fe256a8ff566adaf8d66c8ccfd51ab845aff3299c fd1b9dea7001e8093e56cbfd3b3ac4f118f107d5bba663851b06e306624e6c45 b879617bcf48ff148351993fcefcb0f397d257dbf91b17039052c222647d640a 64af323c3a1ceed0705b327260d7735fc113c19bfd93faf2031ded91fc33fe89 3a1cffa46dea70ec5ea0efdef01009e445069d8a9c50b8d729767c9cbd00c08a 15546142f2af695c023eb410084d35dc88f8f49a166af0d6a24a288d73873b49 3bd3ba5fbf950a3cf3983e9d4cd381812c1661f7d2d2e8807e82f11ad2ee14cd f2925a5148b3ca03d4a597d096a6cef8c90aaa82e562ad9ef51c2c7afdb19ace 9704686ba349062dbfb68ce8bb4d1e8906c30f4576eedb5943778686865783c7 ed0fe526a8cbd8398a478ab560d0ae6e62a80460811d5d5534b8da4806b00d3e 64d422c8cb8e3cd83201c56d50c200f07fba9e6fb1929dd9d5c86f507c5cabad cf364ae32bfe8a86c75d4482acae6c3b5e7696875f42af93e74bd2c11f88d503 7a85b2621995bac10e48c33e352a72418f443a43f412fb98462cd13d6401c049 3d14797562eac34bf9ddf6d607bfb8bdd1291080ddce9f856d4e67d1445bb530 e602b7e2186b8e3caf5badd863da8261657b8ed5b25fe32a2f4c5095fa758251 30d4845e317007b57876060b5d4a0f7e5e2ea8407696a5024f0be2924631dcca fefce6dedf77d12c99b253bb43c9d9e8b8fd1f596898ce301992cfff0f82d0ed 2bfbad519569b1ad39424ae21d83092ad6c96ad7ff026a863610f2a3fd3a75d8 f1b056513197238582c8b5f63fbcc088351d498483d4f0af84c8cbfd216b5393 dbe8020ef3f204e5533b350d6232e62308092afee62c4ec0f4fce45938a2bc17 ec3bcea3a6cabe50cd7c281aa0b9205c46a1668ac7a3c3418b868866b2645946 9ce01cb380030be7e561c24a2c9a75cf63dfe5bbb49addd7023362f7688d24ad 5f256ac56e653b90dc2fa3dfcf34bc06082eca5a4fe19bc2807b0d208c7cc076 81c6519a217e89515d6f80c3b484edc681deecb52a34b7071e87e9e69347b718 a2e12b42399b08b50c9a16066c922e6a88e9d18055af8c65630a5efe5e6727db 01b52210ebf2bd967be586a08e4851a3423062ae91cc8359b296b5293f702d95 6840cc8cf67cb4b0ecc5a3cc2e06657f9aea3476d499f13c1739751b582756df b2f84dacc716922e46a31afa165cb11980a03cd9a4db607b6459ed63fd21e23a 83cb7ea962cb79833f679f80781a404e9cf1f3cb16f488b31aa914278f7a109e 0835147abe33b66b1284d1a3858c8b7932de78cb05afbd1d97616929e31905ba 471e75a346f5c1bf5dc43edd7e2b9e809087f75b984680cf1bce2cf887ef752a b5355125ed485f0a8da3c05c1f52bf3ceb2cda98281bc06443279c48c59b3ac4 c6605e1d92a425af6fceaa1eb83e1aa59e4412c2b2ad1e42b8d73efadd86b28e 8e99494ce671961b74dfb4f8b5dcbe91c936a73b6ab75b5117459f21a9ea3338 3ef7726ac2cdb630b4cf9e68ae966d1f03cbec76cba73cb548d473bbcb8fad42 9c696aadafce5e646cd0cd51f4364c712aba9c8e7297e30a04d7a27848f0eed0 a66802983d88bbd2bdbbef12dca959677f9269e6595d414e1529bc0fe6ed9306 6ff7678d1498ef45aed221c1c6a9ed8b5a735721ea3b44c938e9b8ae318cab3c 1a13b86b557c7405d33f062be697a425d85bdabfbdbe28ad5d27f6450ea81ee0 0e523e2ac9c76dad926a2e4b2325afcde4b9089e79dd7c36e1969944889cf634 487376da7156d5cdc1ec26fb6b29a029c4e36ab132bab246e10aeed32569cdf8 661844428531f5efaf16a9042a6569af8544f45ed1a65d714fd4b2c13d907514 06f15cce3deeb9a10f9fc957f2761ce7b110c902924170fa93260270e4067308 6c71336fd4c01a5248a0912b937c430ed0e43626ee1e5c366aa12031c7730d6b ba5d3ec24201f3dd8de19ea0c2b60783a20fcc9cccfee877eee15cd3d357c59a 2706dc054f25be0b3eb831dbda7993aa59ab99b84e8a21b353bbff93b80035cd 78048a75c9a33c9fbb15353635c4fbb54f114212c2feb804b065fe07d108b17a 8fed7f7e9a0bb41444873cabfdda61af73b02c3639d5f98fb383dbb75407274b 39366829f639f12f7198da37cf36fa13c133242ea5fafd28390836d8da8f158e a2187a3c4969e05b9ec043d575e851c5aba9b23c462db7bab0228e43741968bb e38729daf7bf2b59898d664ae28cce44a98a3d833050814dcac8cce9614dadf8 b1e19501f3bf40323faa7445173964231367ad1ddd9e6182a947b8e5c6ba1d96 ab53c6bfd8afdfbd1f5dea152dde26ee16d2fff7a1ec980f2e3625a5d61ec1a8 321e4bb1e20e993110f9c88f31fe58205adb6f4efb784859c8dd9c5ad8f2487f 098e5405367598e003ef5dc17f26b46fb380873feee380caa786e2be9b508536 e3637fdd7b4d75f607d74da114abee441613188a87cd58a10a3829b4889f9dfc 6fd51262121a0218f9d0dcbc316a1202066a483a6e8f85155330388d0cd60e4d 2d6c3e335b6118997d9c401c24729393726fbe71393979d0d44a8c76c0411a50 36fb9ba0c4591255b185fe637fee509f4728a384a0c28237378cc6772f8f1415 023ddbec6bb7c5dd569a203cc6cbac8a8d3fc0df2bbd3c639006dadb25e90211 1c6743fa87d93dec2aca9df0d5c9886db0f75a0ecbc7b5792eec1ab8d28c2cb2 d8e9917d1c04a3692eb450b01e062c8f0cf901b87dc42b70d49289d277dc8d6c ea3e4374605a5ecc887c2170d171a8e38a61e681d757dcee4bd339749fc85d2f bd70324bcb8d60533dda80b2466d9395b978456365fdc2752a0acbd8ffa5ce7e c90fb457f2990c75fa29dba3d659d0bcbe75752c5d86b45c2b6b9f0cdd62853a b97ff8f3ae780ed8e84d715676bca7d80a4e8e33c24255982bc8039c06aff5be a92e3645d0ae9515981d2d8a5da9922ed7a8d04b591588b61df85affd87d3d09 f14360eab67c15d3c4260f93c158bbfeb20a388273cacde8788b47e1fb21eca9 a5ac0a2774ae3b52a1ba0606591698241769e593f2a0f15f24580a4e4710495d 4bddee4e309a0aca06c1a46d169c45966eca75f1b9ce33cf1a034dbc91556b93 2db7684432bcf73c3b347ef09099cc683b9a197448f65e0025de2db4f507faba c5e319e038d26552df91984a4c3e89d06969cdb8a7e960c1e0d5d9f48e52c5c9 94a6742821c623e9dc990fd60eae8e09fdc32bf0e7f8427c1f0af877f21cdcdf 541b53de25fae5965c8259bc550ad8bf31e1e0fde8fba3100fe5e06e588326df e40bc04e41b90cc06a2327dd9a9050a02cb92e984bf77a34ef6b5e1b17faa5a7 541fdf0f0f1fd874f21c1aada5b637868102ca641f2ea4bb61d0d2f279f972b2 698ee1c735efc811c7c1ad1c4a30bfa8735d0d142cd7ae98622762df8e02a5cf 64c95583b8c62507e3c890475ad1911d78b0922c1e891297f9b89a7b550d08e4 14931ec951c5afb87cef2ca0dbe819c347e402436c2b97421cc894882dd9ecca 4d8ea930ee87cecf35284af8a1a4125f5af7624d94786a6e1c7c8ea054849c05 db3a9527737324f81312c062e997f51056ccdeec67a5c94f4f045bc2557269e7 9b0f1ea1cb46775aded675f2538cdf795b057f9f982e06857428c0de455bfcec 5f254511b3760a96b12efbfb0c9390d47397483bcb78eba70c1c20b876b4d99c 8bc3c0282ddb6c3b01ba62eb84dc8233cf886dca985c032145f9df679eece732 74e073646fdd0d8c3fea4bb56d83b055e9a309e3e9070ca70a2818c9f68c7259 78b69b923c7cf085c802a6e5ff81288feeddacb0c4a8e43edc75fc2f814c657b cd2770da99eea712fe46aea5d48e056560cd0372f7475f1c44952839934b6efe a24e89411c86bb4a1cc556a41ea2bd5bfe14a40f00c0f9e7f5112d73cb7f81f2 7a12a3b2f7cf7ac0dab5b98590dda9a5cfbe05c39a93170e4a273ead4acc1fb8 5abce30e37df025434dd0652dcf461b7778a79c87e8485d4892600fc618bf497 55388c9cd5a6bc2b92e04dd9bb44082acc437a0fb92f815697c5166472846f2d 3d2b53b9ce109ce7b4c2d8853ff38fa3c8d1ed879b2b6bfb1dd3c9628a22daf4 3c8236a0f235cec94400a5d8c50a3cb52cf34a2e0db630c2480a4648f4d88389 4c0dda5f7bd8199432eb7e41f04263d025862361be53630c4c94419d713946cc 56c4721ec286c20666e9cf380c28477104dacf816a576e0f083690ffa55f87b2 f0468e93a25af07cc999a915524cb83c91249861af0b443f2d4b5426680bdbf5 862abd1cf0ccafe5abc1103d80284b4d707a39ecff4615bfae445edcea635907 b51859fbada65d16de058c28500a7a395da44e2fffe3a3dd2ea47a090d896ab7 d57c1ff21e06230e966212070982054ad6dc5952dd4ceca94dee58571430fff7 8b28ba66b695f05bba76d6e6361eec6bc528266e4305b383d5d5d41b15110f6f bf4e36c09e586f0cfde4041f467d886307e95ae4a1b38ee7a96a171dcdec0c6e a69e0a61f03549c646b37e0104c861ee7aef104c27b9db39df7f5a6f3cb51cec 3f506afb63257f9fec43bd9ced8a35e5b8448ada9181d39e3e68c0a36bc19296 4d631d3ebfa465367867627514730505d6c1b2296d60e52ed8e8b1bb04767306 6d4f2a58ce8233c2bbe55ba8840479052afe268d16e5f348d4554f85e8c32d43 d163a59bffa547b0a2a6938a4e8d2abd4845e040b74be7d89737793551be775f d98af45946034509d1222dd25fcbee7d1a453688a18172a31396e2f812a372ec 5443dde6fc76090c44d145dbc2e95d8c8d092d4888c168cb8207821d51a30e65 a896a01fc8a411502381356f4968949395b529cd9ac627b35c777c179545dac9 3f16704b96278185a25e5d08e41ef76982e7dcda56d071d1965bd094e2861210 5eef04a6ead657857bf8af6f7f892ad555cc65e83a44c38234de90f1fa83357f b35aa98ec01c93e548f6906c5d6377e45fa66aeae67e96c436cadd56dff634f1 660870fb780444f73748d8cedc95cc9088ace7bc1fda8714b57f9bb3c7392014 4d5cb1872f144d15f58fb5c52da5c8a94e9bf459e5bdbbb9362428957f0bd48a 288bcc56bd78f8209e63c8fa2264db3928336ab73a753cc60693543abd8398da 59e593eb8f36164968f9d1bd4ef5a178d05e0beb300109fabdb766fc801ee3c5 e32b1d84387047d1b6c440113c42fea6a6a4d5c52871ac2f9b3e3baeb2adcb3d 5e06842b2be122834483fd691fded8c0a1f3f604d880f89107cde8c8d28f39ab 158561aaf6e93df77158be25de6c7453be6d2867e951ef3f90654eb6bcf9182b dc897fdda776b95d6b87edd3450df89812700ccb889cab50c639e8d78c64dde0 0f51dfa81cdba085e02591e723d043831de9dbd370120648bc8c5b407afc88fa 805eeb3f23394936de6bab57eb0a5359a2593f35b8eb79fad045dcf87b519208 756b158dccff02cbb0b0f5b1ddf0f498748561cb9e8c94c8da76ba3ec935feec 4a311836f0b4bbddaba4734e19390e4bc0556cfd8c8285805d7484d68dba2915 ab7c439ed6aba0372da21f618cd907d2ffbafb0dd2017624ec85667c9208d72d eaaa7227a8d80351b768c2be0146e9badb2c85adb09660fa2ae4f518b667a957 9fe266d09fc1534fc3bc62ca0f137225b175a2f8370121a4db6da6991c7079fe 008552a1bd633437c2c2d91e59710013c4273f00d37a05b5730e2b05b488f528 c49d9bac87855edd97f74fc73cab83b897356794c7ea35281f19acebbbd2d149 560e147cc8ae1d33fce1e51bc678b8bc6ac5831e6cee89c1cbae0f58328d5288 7a2c98a37171b8b57913f50e19e50ee5aaf55c4c1e35a3b754eef1ac5d06d367 d24c024636f8d8fa553df412abe1120edf19921db9c791b22c2bcac8cec7ccff c6c491fdc3545367fd81744d9d52d001bdccd1506ff7e9ee71a05eb1204f8af0 74ff9582144102de33bca72b26d8d4c8cca7c5f13ef71698ee0d3b0728f7be7e 4811234d85548cf31a55a768d86373ea424cc14f9a429b650dc9e0c41e54399a 8836b6e40aa1365d1ef5170c7e4259fde17455188c0c4a3dd35425c654c629dc 9ddc86cef979ff90a5006818ea450a982b9fff21dd4d9a116038013ba82ab52f 23348fcb9ade06e68fae24021a69a9580397f55d4b4e2f7872311541966b5dae 2275f0a2b29819633dc5dec2011c102937ed5ad94108eb865dd65c079dd464c0 fee6eddddea47dba21bb039c2da1c8f759586adc8872f54382d01699e9332f7b 11d8c5d0967c2129a4a64967beae58a9bf2cdab6dea23e940dd6de709d48c7ca 131b6f16d4d6fb8d7ba3892bb908468532452dac5edde42504a9d15fed4ce396 d80db20b193c946e6607d58e2bbd5ba897f3de4660375a5b0242ebd318315bfb 51622bacd260423cdf3b4482c9b190335b7411d78769f9a555363770326d9e5e 2408550aade4248818cdff490676dc65e1532c55a735a0c8671e20a762859e3e cc14a46080391157cc8405c06c73d77ce2be11d10636430961b1e6d4ed6b15d2 9cfd5cdf95d5b74336c73070d302b081b6eb034d673c494bb4eb67b635d79cd6 623bdd46d566031dd8293142d570b9c960a6c61c027a18afa4f3689542265898 bf5f91864fc664ed097ee7022fb470665d38983f96759215408bc4b09df2b1c1 14ef5541a9a4b67fb05b046870381728200b75086d450583848d1bd353021d9b 8b51be74c2451f52c00a20195195e557c902360449937d5c6b8538f9039696f5 7f9d041d0c8ebca810dd9fd2bd7fb6588b8b79f2924c90cb53bedec8f6532af6 25123fe339ee3cca297337a065d4fdebfa6ccb4c1f6fb32e3299d70595819862 a97ac55682fa7409640f797036d7856edf70a42452acab13fc43323d40cf816b 18ae3dff58966bbf989e46eb8e2b817983431dbdca59551981ab234210dacb25 a0cda9805d34b0b7d4f766dc6d65b1a12b4643674967bb5e5700146060da385b 791f27e08abe8cb58f10b4b20921eb21a4316d5e8dfcc217b226475154edd4e5 28a2b8f629f4b14e76a6c321dcca72e1142df12581d741b6f752c18bcbcb0947 4c3152aa5b7b7d1043922e7576b32cbc2c533ca528a352876b1ee4dfb9a65da0 eb330c3248c152d339ee92027a70a756c260d9c1621dc86d3e4c7f99bba88a65 cb501fcb73ad4f11396455a6e3fabb667ac4230c512e152b2cae8c15ca4ad198 e4ccad16847766126ff5e219c283e22d10a098e3b7334272530dae54883e2cf3 0b2e7f413eda106c753cafa53dff2ad31b28c9f5138f514c97b12c7cdbc23f6e 903472d1e561fa07b1e0ba5d1a63ff2cfe40cc2dd6766701ad1eeaca0302d219 00d2c48244fc411d65def377b77257162d87cae4e682aa1796dc2a54344e793c 2cad4ac0e041ba495c552d6612883aa956572624f48fcc7091d9f6bdf413ab87 e6cc0f81c0c7f0893b782b5a46dd02ec32cff3b41085977f8ae90712b5cf4e59 e55edfb5af3de00124b952a1afb095ced95c9ee5a4ab5e056a619c555113994a 3f9e8587a41615472c827ab13ef56f8fcf5e2917f9c637932deddf2302eb78da 2feafcd67bbd2e13cd774fba01237ddd875dd8944cef7aad04ee06144719b087 272f3e8415cf368015e5c787b27c803bf0d9f5299a54f74d7769a963a976ea80 61763bcacfb198f5c2870fea8ed8c376b4054142c3d2e33a734cc5af221907b0 fe3121e1d1c8b7d7485599a24c847fd8278490a059e7aab95177185f20084b69 4a4e42cd457e116f20d3ff12ed79375f04d9835be23f833c02b078539c0c0d74 2f97084fed74463f1ca0907fa094077476817b4e1ccf9877126fc0e8b23b0459 684275db1e9b6a4e9113bce7008dab749dce76f287058a3e7b90e7cb9b57d6fa 5ee7247c0fbf5c79e156d4f330121006c9c35bf4da3a782d85c278f9074fb7ee 4fec56f898759e4bd527fb155c551c407965d7d7ba9279d167009036c45dcdca 02e0a13a6e4f3868e4bf0d37663db97d2466bdf2d8e33d96668e8236100e8ff0 d6cf2083fbc288392c089664f1b46e96b43338b7ea20f9a47de02eaff67e8ded ca77ba0bf0965d5804fa0b1db72d7111ed34f9ca5c8c7f0da9d94d7980376608 3871134c094f07656a0c42f66f57a011558f17d1487c1504d97df7f70b14543d f012a7af7ee3bf46e9a618d5bd0a12550d9c4c3b5aa5deaf2675ccc2f24d5d14 63164b6fbc13ad1269023f98874a66aae506f30e47b2c63ac17998ccaee8bdfd 132c07a1fa37573bead927a5d86875673d7beceead6783cfe19456e8630a8e6c 32dfd9a3c37d1e95d18ee128fe65a28ec80b151f9bcdf1eb921311ad7a15b5d5 f1a84c011240168df344b9f77566462d13f4eda955d7b342fadf8b5441c1261f 2e48de01364a444923419b2bd50f6965536d0694988790cf1bb4c460d78bfe5f 8dd3cc05d423063fd7759c4e52a49b476bb3670a593f870b830a8279be0b27a3 c78002f74957c0e538b028ffb5fc43f5ea70878b16ddf400ccc90f1da4a85e55 f8b44dbfb853ce6cbb4da7759d67af46668a897a296d18092b10fa1bac7071ee 078f270eaa6e0798b8eac7f0da1b17dd548656e6117e9802f39dec82a8848e26 cc8fc1eef162c8167025241fc31ad9b62e8a9b148afb1e622cbc226acc724332 8fb599625d6ccda97c775b3aba091809cdeae37ed4ab2afcf0a18daf37321948 7b4bbc62d745de9d0cb43450fa9a70e98f66f05a7dc063910f8a4eef6d753e03 133bc627037ab0df12c862eb9f2473eda4b495aa8542d9bad02d2094a75d4341 8f57b384508e0fffca3746f742016ed78bc9efad410d2b57473bf13d49b0bb12 a0eac3c9413f06bf6c73b6be3e5828dc0d93c5631b415d2fe3f4f01a8c135f35 30abd0f850c32b03ff5ebb69349428a425f891c163dfed82185d940559d6e02c e02fa52f7cf6eed67101b63add398e1bf07d5c63d3535efda28320be9a88c447 93a179c0197eadc4f829e6d4c656efcb809acdb97ebc6a6be58b93022d2ca4e4 e8c58523ad2a4060355e3603319e67697572361932987918d2b12c29be7c9269 06555ffc82bf56e029ab0953ffb09e89618b365e2fa4ae7b06bc747a34fe3a37 12bec8b379de90d9a459e75be62acae572b9df69d39c6dbb80bc83a4e22ac6c4 5b305027afbd2134537fc451c621bcd9b02394ea228411c48b2b84faa6674790 86bc4f65eb888cc2defc0743373c0099b9c03ba6c2d2580d5c520fdcaa8eef03 183 02da77715d3458f28d050688937fb33054b3716cd7802adc8a25d89e087ee40d6a48ef4c8534d7403c8d6ddc0400c1a0060883df6599e0b19d0d3fd5bd6895056f46f8b72a0dd5ea91b0f8ae30dad617e5c4a7ab3b8cf6a78e6115cccea974063c49dded25026d3b003183b21cf4a10a86655d2080a20a58d17f9c37493f560462f06530cff94191d0730811d701f6c8115ae902725bb3819eef596c333f910a7316f6f2980518e951c3ddbffb86e1712c0b4c13833dd3a7110ee625e6f1620a257f7b9214525a88922e876a2452a47b219bd53cd9111d197a79d16c43f92f0c48b88ad2d99729c1554fcbf242c26621601607464c9a5d29ec844e471bc11501f4ee12469f3a6545904aa0d44333acf94ad5ef0cd08c11ae2ea9c7c9ab2c1c0f696d4b0a612ad91dead27dad857a297d48ee78e27ce9678564d0212f24e8b9088433be9bf3ac522ab5e4ae243661e90e3f26014ee9df8d930ff6cde2f27a680c8eaa4bd24dc674103d40e87a029df25f616a955b2f398c545ef025f79129020a4cc2901b69c4813586e7a4f7c778481db86052e9f3bd2597207ccfa913c6c003e9c33139329b5eb70a0e4e9edbd1b42ad0c3cf7d190f89317a4d63c96782c5014ba2148165f1ff3f132317d92d9508a710c30ad41035a44f5afb1aceb599f102199dba4b4f47df06760649e0d561dde21488860eec810ff8a3a7a70f30f087007bf40017db4bf0b70c4554ae8aebea92436a7d35a1f866ffe54c3c21f926e70c868f074bf6fae7d4566ba0a24488f250ced49d91565980914e70916f0449ac03093cd48257af70d1749cf6dc0371081e62b65008a0a107bd83ffc5e032d347079c0c01c65946479c929fd318285fbd2c7c5701a98c14c9228e94d929644d2d0af11e91a1e74bf810ee2496db46796a9cea5d56e1ec84dc75353fbc0c010307022cafb33861adc944db70b837ab0b152603f5178cd29e87600fc37af3abb6640c7eebd1e7d941548cf161562aa74e31a638fa54a779dfd949659211cdca26240037dbdc98d7496b376261dc1d68f81588a1a133d2a35c598bec4d201e0696920fa6c6338ba271395a03a5aa4ad97a54a74c1e3488a4654ab0c1755550b95f090448c9692e6a22e12d322e4e4517b05a95149862eeb068a0ac394833dd390f7e05256ef72e80b9474164f615c5c6a20a6d2b6b750c1fc18d8e755ebbc6bb8ea8028b1d9bdf47e36a2fa9266af6a9bce367c0eeb74f46651c21cca31e5e61791607665b85c7ee9550c50856ffbf8e1e36cb2673feae3aeea1f8f47103d894a76f037879043d351318635418379ca5cc514886d53249a4e1fb7cbd24983668de5a071ce311188512730f8efcabb814b24c53a8c995d80773de9bc2f8fa4547a5240a5e461cc44a694884a049fb9b77044b2e062b606f75eaae944d138f31f3aaa50d5c151c86f3a0f0148178ad80ab7b7340b9d3009157fd355e20216bcfa7fb3c0ebfec81b3ba2cca9f395311f24e4041a0296a56b7d6e4a0e9fc30356678403b0b28fb124e913482572b5fa404674a597254d5c4d35158fb3be92c2bb71e3097038871a6bbca7ac73877fd3722afd3df63f11fa7fee793d68e963c4fa2c503c003bb5c93d541ec91a3f4174813fa19bcdf910595356bbe950a73b8be68166a18084faf963bd52c69cad8ad847acfaefd143ad79119dc49a1cd085757d88acaea0517f007697fc5b1460db409f307b8682f25e3bc95e71b6373e25cf8287ad15e0c6626a26c1c1bd6be0c983908363473afc7006e9fac3a61c7020e61ea0e7fe10a3b469988150079f588fa9b05fe4333844a47be436d937ff3e1b0d55ffb7213044622509582f7e92fe720348ac06225a6b05d4ea800e49cb3e18877a73f8e410c6d89ce8aa30a06b4caeb64fdf615575a791c825c9249bdf3b9fdfbbf204ed400c1c4b1109f440a994d97fb998c40deacb35547782c78cf5813d0754fa665eb0205c9dbfefb1e7e5ae69abea504eb7373ba4e6fd3e16b5c06c7aeb0190c58990a6c4bc3a9a8f95e57e042b4a301da7ed993040edf2e9c3ff55eae2cb6528fb70209bb44c7e0fc1f4876a9485009b290c1464684ecba388121c8dba54abbfde40400df037a099775f1446fa38340a851bbaf05bd5a57ec936c13e9db34af721304fc581f5348528f28b5ec197f65939a4c5faeb9db8aac88d41dba71fa8f698d010f3505edfaf1c4277148a8c2a5b586eb3450c954af36d60f723535b894908f07c9ddfa23548a4fd1543b209e302d36314e9db1efd8292e79029106c3f71ee00437338e2628e385ec813f48b1a6bbecdc2fd6afca0bfa8d3c1f18729204309b0e709d3fe89d155d9db6ab39366372b05530d73d8c0c1a0374e1badee71ac9430c076ca2b43fd9ddf23905be5ffd507a78458c072571a264c3d4eea9231d917d019636ac3f4343e268bd58916b5af69c7d611c6983f0e5a653fdb19f419f028d066731d827bd16e2cbdf4ea05d8de5ce22686bad1145c9a863b217d4d84afa0e0a37d7d0f5e34ecfb4e1b12b9af7c2c291c014e67912c2801db29e46936e033501ab2512888fccd3a6a8513c0b6f61f97c3d95695a1bbb1736ff17c85cca06590af0118ac97692f883042396fc92b2c500cf3b40fa91f54014f9ce026aa4736906c5b81cf8f5b3c7ed2e03f5d8f3d1f3121b56ed82dd3076f4ee330950ac42930e129ffed6e0460521f54d7b57a13662684b2d4541b155e5efd94927c1f333f80f78eae8e56f704f4ce3848ba782190f07c9aab223cdf22d73eb71fb507328290e71364c4759c5f21f60a4e5aa95834efce52081ea582880a3c6952b6ee31efb0c121b30e3045cf5f762b45a6b54da116f1ba327c891617940c8d7107bbd0388055decb5f240704a7e36ea19d1ebc1ccad2bd8a37e554e5a5b6158667d8d9dcd0c443ae07d6b1c09c7d943bce1117b082fcda2598e0796414ebd5a0126ab4a020b9236c3e5a9cadbc909164003718131b5641fee61de0590346d7f5d49adb6370cb78a07765bd59d89421465fe8ca6fd3c4d69df0525425232153f34e759d1f605d65447646143ce5b2863d1e9c5ca125ad72c1707e1213469d33958bae17fcd0b78264dc1d7e6d0e4e7bdf59a02d012c972ae2b5299cee370319f97949944a10add1bbf520315dc073a76956a96aa6022dcd36d0573e4e7659b0d271d52ebda024361cf502fe3e6915237a7e19686b2796f3de5967167e6922a4d59a2461b6a005fd1432a1df507d7a7399136923880eff6a1a3ff60a73a503b6fce7352042b034cec05c6a6ce01c13039a9b47fd37d1ac02b8fc325b245d4764bc8af0afbd705f68b4e95d739e6e6ce9bcdfe33c2e9a14c02c8aeb22d13d6797e90e084257903b287f41f52405e727d9eeebe9a0cd96cfa53ec2d5a9ac27c19890f26002a800268cf74576291341ce0fff7e3ccf6221b6db8a73a761a7e9317ace7bcce9da60ce59b656b2ac0207182180771634db22c1ef9137c8ca93ac0d6feff70c91beb0e562d4fe92e8dc148ad36085505597f87430aeabd75c7ddd5745136093993500f3f19560ad1e6e474b87319adb977aec73784d5774e986a4c673e39d12013e106c9e474c907c7c7125d6b8d48defa993a2c349b06cea3c57908e7f256eb6007086175f5912d9b59b644d91bc6fd41d4ac9042c294031a2cb3b14b07bc1c40880afb6c8dcb36446daa33d3345147d6e7924bef263cd949a34f3e24ef839ef21502ed639e086b907f3218fffbcbe05942563865e4f98032415d2a4bd4b0475d2e03709055b48494c1c4f271902148a6471aef0033aa6099a3a11aaadfe479fa6c0ce8c49c25a1d6f2148dbabb289dd9923f178d07cbe7106f09dd0f35079d925e004207cc5653b58a9b04bd568be9c1e37b0ac61da557082fa7743e312c1556e009e57b002fa611ccd017dee58ed9abec2b7785ebc73cd309fa9b22ef826e95360a8528a8e2897a786a51a4c101a3b5d589bd1e294563bafedaa2b1f975044eb20387fc3080f087406106d5b26ea33acc4510295bc6dd2c405f8992d3a52312950c5701e40929f14e69218f75e0cbdbed842d43180fef31bddc7c8e75bde5afec03c5b9ae77397b833242ff2321d2c83d78af41ca2277e687d0fb30d9147eb216005d2b8652222410a82841dec1a3cfe6a1a3075cc067b7f14c606f9094e7e385028b3691015c8864dbba35f8aa4b23a48d5f6acea0142cb4b0470bcab48a71190d19abf13cb7e67a9890e9caf33fc0d395cbee9cb9fc0bdd5800083c11f7df300dfb102633bdecbb6df7d70d24d556d195473b8f4adea46b2f2883cc43d89906097b609c37a793c01b7dbe7f419fadcb6e81153ce693ca4039a78145b0178d500a2c56aa83b3811edbb2b5fbad72ba1725cbb164c0c0e691a0f7d37fe129267400317ea0641496eebafe989c5fad8b74e796ab3e9460b5930be49425d1943fed00bc112656018c93937d7cbb910055e9756d4a980350e670baa7fa3c00340f610485143db298ec7fde33b57ffade27df98f4456be080c427c22752f3260b29b2071627796a59ad278731a4e8719f865a3386ae36e936c17ba94de84a34b300fb0b2add91439999f5e7161073ccdaa1aef2c2e96ebac1aea695c11f61ebc8e23205a901a8af7341a18bb37393f18c947a0e96728e17bb80500b1085d37b1e51a305cd0741f60fa8fd130e57195a393e2a43c577d837d95c592bcfa2b45b20fd6f0ab85099d6c3f43559284126ff578243bd8c338545d576e7a413aa2ec1349ef008df56d2f02fcef212ad0648896b89bb0537927f94d8a41699ab8a4f39246d920c3de10afe6b9fb8646157091bc98327091e9dbc2e6ae388994975b9668f76b60efc87fea94a89c1ec6fa0dc32bffbc84100b41af88758f2e8a2c2048f4f25f300f60e217c8e9ae063a227b62a43b99028a3b186264a6e20c9250ebd1f74bfcd022a4f51316da56fa9266143ebf224a121e062b5d699217189e98786057e7db702152b2948fa4d18dffc79d4cc8a04059a7396e8e62e5d1adfd2c65d8bb101a80416b251ba0683fd70c45179fef382891d49eb0b853fdd80b93d99fdefba53bd06056a746b10ee43a05dcaf0aa99bfd21717e40a4436bfbabb2de5e9f810ebe10dfc9d92eae6b6f11d4d9f1009cd2c8bdc9c2263e516881ceff9970fdc069d2609458bb52c0afb68aa5c0ff2fb79fb808e1d3f6bc5f892bb959dbd774bdc99a30a644e5496cc80b2947bd3a2eeb21e80d648b79a2e3d479006def344fd18e5830bc15142038d3d38128382a5e03b4d4cc3880ee39b380116a502ab15aa4aba16071c4db28b8008233886b4f4fbe1b81d8a875559b22b7822217ee51e8c4361470830c20085171e673e235da05e7fdabded185e32addcfcaf5f2530695cee3daa03bde3268c9ddbbb759670e43dc6c2b5abf2166010f7f56aa5b66be37828decd09f605e6a2b05f1a1d31901c541c1c24c52e892412fc7fe1d22446e871377c67006e6ce9d2fee9ca73907b94e4e9c6d0be94fc924b7fb8a38edaec08a797449508d0f3b0b10cfc867b679d76e22d41ed784dbcfbbef3105bd572befd6a5b38650ffa47adf42d5a94c16f15d51ca88623ba769d642a7ce153f8cee735b71094cb0d7bc7bcad3aa431a5efd90b2eabfd0442cb2b8d2913500a03bbf11792cd11c20e896070ac568d20117497ff8cfb4c3a0a7d6b0421010db7f383e8ab619486c3016cb7d610bc7627ff0636eaca6679769f850905d8a9c7b8ea2ebd79c188c85300c0d24f048d88037468a3ac88555466d713adbce5d85fdccdb90cd223f297f70e8abd8b8ac28ee9700ed200dd26e3201ff77e8f69a90b2c0a18089a68fb5f9e0eb0483cf689e139fc4906ca865a9f5b5554f2ea037b20f9db824f0eac5d8e1d02f018821dea3c227c9d8963ff9e2d1414dbee4f16f66e1185e0b11140ff81c1094680f8eb65629a3d7df9700fef33f1c2cd38130261d6b9a96ad2a2b29165fd01d11a8df1830ba1ec1ce410c2f51e204b1efb077174ed69f10c693b38971e140937c0f381ea0da97a0846c3c8c3b6360428f0f9235fbedd965031785c9ffa0b010176f95786e315da1b2465c73254bb949759f3fec43320eb38e52f57e63d720c3a45e2db1ba0a3f1f27ce82cb7904865a550b89e536a9ddb5ca310ce2fc31609ccb9d18a1cc2f5c991ce4a9f6286a31a184bfe482c5f315035c6a6ce5be54405853faa4938e5e12655ac4bd318fa29157a4ab6fe1ff896dae2f9e7cbd0a8d20430d3b6b00ffec329832c56816a57bc9107f6442ce0899c6bd6ef9c0ab958d00f33d12a6182c8be686c633cf53c52e28cbaeb2385795d6b6a5bb691e3b2824b08f00fae6faf7cef59b29b46bebdf69813d2d6c743e83868c29bb07219b3d7c60f2ba8caf2ae3d2929ee2ff6d56b45927fe933ddb28931f4d4f9379e6cd5d94505fae2f754a3aa6a241f04745c65f545b1a8f6fc82797be4c6a4b4cfe6a35fce018609033b35a5fbfaad3c29232ab71063f4a520762dd97cad978fe9d013e6ca0fe70f383470d3e2ae7366cbbeb5797f02369388d8f2575d59982ca00402301c0d1927522342f8828391b06fdc57140942f96957b2aa320bfcffcbb62f1841eb03c02d59669d08afa374c67b92ee4b3b248dc7699f245cc9971a461610c52d9604d4ae06eba5ca0790f06d7a06bd12a7eccc30fe10d1a04246c7d377267ccb440481e25cc60765dc1a8c3cb17ca49a1ccc1534a26fe0b95a1a56a19a5039a7c80a3689b58c9ad8020561d88cf13aa5063cd3a41efef28cecef934a54edee2b8a033988006748c53f3733c1223d39d177c0313a0914ded7709901979aa1030bcd046d8dbf377f707f26076fe313a7ddf251b5cd2c0593df247b97c9c7fa9814bc0fb8e4ec36f65680a48750e354b80bf257613eaa7c31c662563e5fb9960a454504ee6ae264186722aeb8bb4396d1e6aad3832e26887f4e06c295711af2efb4630b750369ba30dac687046ce95de409a3ef2eceafa8f26059d42371856c5e915a06c07fdc587f8a699f54d96577abaa420c5f25e172ec3f566792fed1d469ef170ab317e01673b31c350b8c3a4eae5f7d12ea3e231ab666f11c11f64ad2ce91da0d8fa95d502feaa2df26f778563eac77410d8b74df23974508fafbd1293672810b72ba8d92c54d238b03d601e51933c100a9f95605f62e97c6c5b5b994229958070d64998f5c38b2bd08d3e9e603912b6d1ff7a3814652e481a803e759767f5b0bf9fcc380b6cf58df50d350ea80877dddda5dc2dea919068f90d9c32915275e02cf78fde2a1ffd94cc2847f2664cb018de97aa2d30c00217e7f5ea409ddc93a03bb0be17850ec3ff4ad9a02fe869797222fa89f2d339100e32283035eabd4a3077183c2a472225a352a0e046899d89d42fae67b9df13a009332db8829e3f8e80cb8eccf1d977cce5e2e81d572af126541a3449012f2be71e7c5e8bf602cc1ef08b218a154c1744cd23f9715672c7359903216871606b78ef04ac3b3e717cf860d0bfdd810721c87b2e27be9988db741059972e4a0f90d3cca1e4b92e2d0d88f09069167d7a597df546559fc792c1b3060a3578958dc4bcde93cc6040bcb0ffc0a1a4eac1e26bd8bf091ded31b9b719cb5b958f0019b8eba702080e6def402230514fd0c128abdee3e1095b2f14576308a48bed1b0a6763d7771175043a7c3ec06812afa28bf443fd43bd3b4f984b385acdb2d726e0f6702b48e08b2abdf540f0889df6d5bacab9fb89307113cb0f931c912b5cec337f1baf6a684dc54b11f540e875d2a902282cad2c9388c78f88d2c711812e0bfdf0a013d82149b7c000d67086d68a7b79bb80d5c384883ed24041852f83de125c69fc0a15184700738c52903a786726f66841179e997c122e703b8af6a3fa8545f7a2a3c204f359a998907060bb02b957a4d1c2cc738d24bdedca11d12b90b79bdd8732c5d506bf5be01f40b009fcfca336e722d90e1effafafee636d80a5457810bc20e253ab0259c14e40f2fb79d82f7a0054653eb9d1ddea44f4f2c5751015f5e34ff48705a2e06caf7046268c412e1d8450f374aac2ddfcd730718fad50467ef341db2d4e1b92bea940ba480d937044eb6e403ff520b8c45967d5c09a622795c596410d729284258330791aabca0730c1e0ecaaa1d8f5f8827b88a7f4edc14f9fcc018d8b3c823c07403e80abf7815061701c1314dea4cf90834de80d572cb5dbc7aa904efad7581240bb0648a9496bd170fd81ef5c07634326987540f828085a3b0c8f7c379572caa0c77b6c1c0f7221c5a2179e53aa6a94c5cab5f05b26472cb8a1c45afa97991970458a18d6009f5321a3608fafce6dbd8c03ce3a74a24a425abe14fecec188ba3016e33d00c3fdca5e09ff2bc79cbb9a7cf4a4982edaca5172d428f08a061b06b0e11a10b8c971a4b55b83f50a8e905f2ca311506b6578b2f9593a63459312442034912b36fa7000365263c1ac6b1f520ca6d0a0d2ddadd7920befc898b80e42e01937526a65552cafba142d888cc9417e4b996429d623561b9c3b8ede4cf92b10e6a74f406bd634034c91051bd0dee18ab626489129762bc5be7767930df4b3e0adaa4f223d4442cad7838d58346370208f44e05103c723cffb857a86eb6cef40d290c38717c82bbd5dd2f5b1b110d6622ed3a85209123c61a624d6b6cadfc0e02a887148cd890c0987cff7991c86041542d149d30b073646b98da4a2625bed4001ea3a24ccad76bd949ca8aee98f1024c497df8567a9d8ed29129c8c6fe46af08c4941c6ab24fa82fb5a5fa123abcb7ce895b9e4d9c2af2e933438f7ecb79ca0ca783e0bf440c18abb5e6b5dba23afa1601faa74e46e8b638452351985996fa0207d184453f3f002ea71019de0b289318e90ac236d0a68bb25128b1b331b1a10c3717e5c88a344687a4015c366347b93d73446c80c0ce421bddf0bbdd7ed567067985abddf720f0a7871787e6e0d32ef3e3eb7acd0ffd23fcb66687890944f5014653ecfd67bc0b4042f8599796a0efd416a9dedd09f3ff678ee07460920fa900b9c48df26351c292358aebacd1889fe7682f507cbba7455635626d803c122f07240cfcdb2e7ed793e637a8e89c8416600736b14bb370e2d46c9076d04636ae07c5e5c2ab96ce1fb599803d07272482dfed6ac9d46180093586f7c4b20820b50ae6720ae6b3974ffd571876ee54ce46d7f8bf5229514adb020fd682b6b512cc0e2a14ea9a134dcdaf5da985dbfc11405f1876398d5b3178f96fc63c23e7cae40a988b3662afc9960bf2c59805006560abcfcaa6632d173e90cb4a9d89e579c808e0e63976ad7a65f41653cff56a95b9e2a47e2f82916e7ff202ea0abb7fe4950d8a962c0dcbf2c209289fa0d9bae16234a7a3468205bb0808b20ac27ab4b7da0c16db7d8f6c6736a21407c90d6fcd1af713b26bb163546d805509eaa83897c00aa443b974fc131870ec45845b5b2fbcea2ef2302f80d531fcdd7ac14e0b22600de97bad2e9ca349dc09eef5310e180aac10d3d738fba9d41929504fcb7125f906b578e86d9d245124d0d4875989ee02b19608d170116cbb6ae43121dd4ee847013f2729904631545736486d331c9c2488fb799549cd6a04484c79381bfa621706afe8cacdd602ca8bc4da2b3321aa1780c406e91718fb363e1b06a24f34898b0b8732d9daada6302c2b681ad80c8e1d5eddea76dc3ac594d6c61a6334c88eb207f2c2bbb15dbc8699faa4e1c9a841391469ad3fe049d2100fd630cd6add4bbc0b6955e09b45e5065b1d464e45bf8f39a9ee24d797e2df6bac813f8803242a07008dca4f56877f9e5a4ffbb7081523b05a6f72142201ad7d84f0f9d55c5052fe024f0b27939f9d39753e8a003c9ea5fc92c100fe6b6860431c1ae4116258ff020d21e2ecae70f01da130355ecd4b23b30efbc97e53af7cad22cd2cf45b73aaef038de8635b23b6313b1e6d45b57b0c00a541539e347958a7082a674b8ddcf8950f0b413817da446658078d62db1dfe91dacd1c13226797aec9bfc9cb7f9bb1a50d16372c18b18c55899b3e98a0cc9037499dbe0850040915eea2ec1c34bb673b0246779bb309eed60a3d4fa0c97e7c953b5c17d56b68fd640072e67a0ab2f70501f14c793290f7dc314ad6d7f7a515d400d6f2e6ca28267086e1cfd87b36c0ae08b207a9a0795bbba801eae04406953f67e1b1a8b112207be1f6a7577bf2bdce04f0ab8eec62ef50071cb931be6b5fcb18f12085940e6d82c012af5c9f72c7dc09d8a7fa0401ae53e0bcb11b997640d398fcf3232505b1c6acdd1b2de5c799f90427751ece69a45c76e2626e115c8b98b7c462db0ed970c3ef42b86e4de820890bcc0c87fd197ab9e51bdba46ec0cd6a17ee5ea31e3d03735e027babc7aaadfc0c41e0417690e9706ef46f29e9eb5970cbc97462fb8eb3c6d0120d826c08973c0ec4cfcaa8e3f1b2bafedfdb1f7ccfa211a417d7066e32f76d6eeef50b94c1f20ac06a841829975c9adfd349aca2aaf13b4a932d655074654b6d356c0dc95dae016154234d4bbcc655da1b70044a42fee89d33dcea06c61c92c372fde61515d60be05d1a95dd4b876d0dc0755268a22b3425a5691ca0b3cd94c9c49fe1fa856b0db1c90d0f4ba2ee0536823a3ee56e243512b33047baed42e45c53a30f1b6ceb072c7f2179d7b4569142e327925cf6a2671470ab930fd435a01d181b74731dc2005f14908b74638aaed591134d0181fefda1231282c6db8ede6d932ac4cfdeea0b5e138f4c0f465145e4f3decbe0efd9c01398ca9f0aa3c1af653934af90b7120d3539e9f388abeb832c8b5d9ce73b0dc8474aeb061542869611e59f201b2fcb07cbe224066962c34acbbde66629dbe9d89707d385aa238c9184775d06d70d790dfd8ef14d4ba94cf504c1b6a9b311ec8f4c4037ad4fea31f83d7e48045b4402085c71e988c19932a207cb9f39fa112d9173742901ac4cbd37a22df6d810209106063a5e17aae1e545a72747a23b0c44130839b4d26db59c56b7772f91bbe26105f33a401546be475052bccda69a8a0cd8b29ccef8ae56370c37c9b8932ff185018e5feb6355099c79065e480d99cc57e5b03d1ffe7a4c2da43001fd1cbea37704b09bc6ee66685112568b3674058677f59075c98ecbf6d157ee18c5b6732a6c0a9a9fc9ecc4040cfe69d4ba083a460d2db338bde4d5828e7fbb40824e18e21508674806c738bde7f927e3df9bdacf39f94338e4daf331874a5aba23365bbe6606d92d8b475575ac7a37e7c94fc6d7bb75945b4877d1ec12b3466f4a363f9df2033136dc21bda7877c2e5730afb30295919b0c52578e8cc5ac235c99d34a43d2075bdfcdfeb1746bb5941ad0080483225fe8597128467f5e7b9249e06dd32ecd09e7be1878d78dd04af525c2e21f69232913986184594d6fae597d77d07241b50f340213c984fc70779a00c94dcd5352b2b7e96c9ffbc625d6007dea365ab98a0727ea14bed3c17f2a1300aa5ef6ba9b84f691e83b8c944a2857535d40c9a318059e526631bfbefb8ea6a4d93d874c0fdc88e6a2fab1c2f79c19ac08f7e354dd0f92e65f54d73ad0adff902e9dd21c08c05e30da1f12a4be48835401860874670abaf3e8520b0262a136cdbc70cebef8bcb53c4216589540374f75135d5479740f2a3935cd4f78dd2215ebb6ab655ced75c26f2cd4eee77a6c36fb506ba5b9420a679c8d32388a577be890855b18b1712c66e8faf1484b73a9add5c0221a61430ef7ed2c7322647720f4a688cb917927e7ce62752fecddc18ad4170739ae06560da1ad8b3b6eab2b661e508031f801f40ce4904c39c9c025105d51f6c18d1b3d011b1689ddd2be039d187ceca5fed24d994b3f646a4955e7eff719fda67f571f062552046859a3730d25b8386776baecde30baa9db2b195c3cf07ccc70d97e300e867865af98ca97b08769e046aef397d9857a3db45850c31d2b9fcc3fd5fb950cf55f36965ce02b8cf366578b83554ff571caa61126e247b38a6e62e40e66c90ee9495dcff99ab1950e24ce34397947200cfb1eec0f8db976044c529b10b3620e0a6d0545cda31ca43f8e934a531ab805a03ab401b5e3e90d93c34f1ae9e55d04f5316574eef4b860691cd6ad733f1872e2c9d2a5992b925ab64cb1e03b1ac50853abe0acb8137002d03bdd0da920ac686f2196f707a4b27ee78a454d5a126e0fc8b3c168b284bc38ba02b4beb37e5c1b25e9783b716134cf0c5008769e878b07e0af3dbc47d6a4436a8bf94a404ea3cdb3e1d76436465c48c9542ea744f5d50c17d6be5b580fe1e751f6eb51993b4b77e276989d082b7fea4864d9a7324d7703272100e82b0231e7b00d00ebf2a3df540d714a6e99c09ceab28e52ea45514b0c99f3808cb0b6b8e2cb220b15ab4038b15cc9f3e5505fe6fc0f8dcf37c310440348c32c4deea855b208830653b88e2006ece357417cc769148ec683c0cb6f0e05b2d285980f87228ba801dd02b36e96ec4f6310c650deb2e9b8ef69d3166ca103d17f287373951d0dc70fe4ba015ae3c24584ace21b92e151746d6f0d5644ef0087b1b0017248267f03e80b316520823d15f2ba68807e0ddfd8392544f551dd0633943c4f7b169d7169e98c99ef9c4b11bd0e70790bd849ffeacb598000fac90dddb2c2bee5d919a5b857ad7d7ef41ce2d3b3af1902428767f1ee16a32882ca0a3def84ffafd0b7fd1905e9cdc5447b08063459eaa80f17f4849ca4fe59d23e0fc62b339d53de40d56c3942f0550d25f1e416e705566a40ec90c88b72e4d5f001c5c8b309633dde663307faf01e9ff99f56a1cf1ee7768c3007ae3c21aaedc60dc5659d2d3718482d494ef6c0216a9ac3befedcf4aa9fd99224199721d58c9e0c0145c47b7ff0f081004c89256b9c55f59270ae5b2f02ab5326c2adc6d67d0b047815d520bc20b60a60c16d5b14f70d94ef6e2687c97eaf6da305335837970d0e3eb0c01cd0a9cdb21d2d7bfbd51698f7d214e3c94d58c5f196415ad087dafb02adc93f16421dbf3468a3851e331affd385a92e934c3a9632547b2508bad4ae0c0810f69bd2833321cb7e598838567c1e17a03ddac83a4b96ad6bf32dd6b92a02f766ce2ba0cf3c5f66935b804875d3d928002aa59cfd24b7f7be13996db8bb011a8e575edfcd2276b27aa06ec206ecc79bfdd2ddc949502f308c6af2c8eb7d08bf8835ffa831b02e181e7ebab853c31d0ea22eeaf99cae8c6bb4ed88861d47020eaed6d3b338d822eb7197d6f9ef53b954a1dafad6dd9189a25323516aeda1090f0aeeb8505ebc49eeee56174512b557904a592f3b6aae0a1983c3f653e4cd06b7ee078b486f33e358fc9de4d81a1d4d85675c148bc13b7cdba697836191380d1b20f67e8e3e441e2259ee13b98497cc0dc5a9d478f3302ad2df2f24036112009dd353f37f3e9c1ce5bc236b2113803ab02216b3e527c49389ba73a4b83b4c09018f5882bb36ae3651de4b0caa70196e94f055f2e4aafc17d6112a884468bb0cf83f89e2f4d2c2971d249fbf658704ee0c6ffb4b02c62e790092bc3d72dfcd0f9a5e9709b689e1e3d2cd0939452c34c9c785082d546dabb566dbffb4e472150cdd9aca954ad265ad45f9d62e965022a90f01d86da325af3e8081d65058c6c8023e0966f8796eb2b47c1163130b934de10620235fa343e999b81e8e44dcb653033ab400b2ca451bcdb6f5f233c3c20f66016b7f7942e31337f439222e7675da06332f7fd2c2bc0b3b19d28778cb8ccd686c0fb7c56d322220de3d1d2f7898880d3962986c56a88517948a227d6a8dbcdea78297ee9cdb9e9f68c85b07403a2a0b7ece3582800ca0fa77b4b1525789ac45c89049ed833a3f7fea632b8c8e1d0206c2a4bcf67eb3602d4ae503cc43c65b246285ff453ecfa85eb0d9e0e2b369d70f71c4fbd2d642cc0d3fd9252761e95b304a06e76f6cc765f6f6d5a4f6aa79af0a43b368cb2b88770f8a7643efa09411b58119bf64d75058dab500f6ea3c0af709f570f03e4e67a16e5fdb4c8b8034a268e5a88f21de686bd2949195f7dd09300265f5f32c95960fc5f9eaab17b707a466a9b530f52944f4c8d99cc4c23253d80ed493a8926f2365d316c6b78e69663fed248d0e68aecc75d3db030a9eb5e9af0021634000a349391ae44400541f00fbb6a05725cdff629e5291c952f59fb6fa09a820494ae06fc83cb5864cf5a80faf3e67a2c87a822ca2e30400a76f476a840e3eee6d6841295f16e586c6866245146e4e72a3b92e3c1b745e553de9d2d27d0e80330c68937bf4d9543cff69e99299ffb5240c866262e96804f1cfa7343c0e0a730d605834802f345ec6fb24f5bed48933ccddd25ae0d42ef4d37c960e485a0e674daf8323bc1e3f6043a2c37694f7aaac3916880fd361ad17027818506e0d07764677de579a55ac4dabf9cf1c740147daf29fae84475c076761fe6a228fe20be2312971e39355427bec8f62c83d88ef9c174af30ca9db0c5c317c1c07cf46077f7517aa6d8966e19941065b1130a98b9c32c1537d4caa1cc9ba7ebafcade40de7881fa0a15756e90b93b29c8932b1762d080f298a42e983d154f7f84cdda80014ef9e22844ce726e6210a5ff77b1a6d7f2b400d52a3b103e041842c6893330981467d49b34546649f69365ffb6bf094cf756736c2dece6e53a10b86a6ea23079a66c1cf0cd196aff556cb429417458f9989c8b6ac9a83fed4d4fd2b30c8150ec579a8f9e1bde499bc7e00f67f294cbdecd0956393305409e9eb38f74c5e4b0b4030fde83679c0c498da914709541465edfa43dd2f181f5f50a2261e00ba9805fb4d3e77ce3bea71698f0230d30a0df3462dbb04ff7495c3a6527acd34163e03a0e39bc63d4c8df652321cb1908361aae17d7f539ed366af353225ab2fb3e104ff969baae5083c4f8ce8a5b995552231c96b8fc3338987120c7462ac3282b80f9738aac0b380f80e58905ed86e7048c9c8f7d571c210cee03892bf2ef603a808c15cac0c70ddc3a9da893b34dc2732a74a0616ce7cbb9395491c380e47b8f50e0d54de75bce6d1fe7117480c106ed73287e27967ae78d603f4b1d41031221606deefef29209e04ff2fc95e260778152f895cf7b46dc59b98c37de30009555a0abd169bcddf16f0157e4c75c2a05f94c75bbd27d665f55dc5332a60565d0c2d07cd09d8fd335c9962cffc1b0beb4c4bb0d56909861a9e48fb36be1cdb85df6c0d04609e1c875a9abba5e3a364fb84220d1b6167ce9301a4f74c81b335adcbce007034d7d9b9c4c40d4f06fa557e17bcf677848897c3974d9673cb6f6e07aae60521df94c51bb361f9ebadda58a80bf78cce3067250e7c298c8e89616f0dce180c66ca4f4977fc42dbf0ebf5d254598426f1d8878692adce098642a25f894b340b2016c0ee62d78dde7f73ffe497d6d618d57cd5a92bba7c7309c46a85865491088b705a69396b65423457db60533456269a7e1e6bee02faac417aef9b23f41e00e4aa8b4f3565585364910f17e15fece3c0bbec52f54a35753f5cae7d30742307c31fcb9f395b90aafcc85d949ea3ea53b9ddffc765c8e517997c84166ea69e08e0a22acc560d1ffe46abea60048ff30ac981369d4bdc4560c98de3fcb9ac63028107a946e39b9d8a715cb84ad90927b29cd3a7d8cdb5d4ecd183aa1f9200e10b57743f6a86bd6be6a2e659565acc1c89c59bb26271b61a0406906203993aa403f9a3c1606659af3b2139c93ddd1fc7493b2c8e440cc36f7335d9a080b24d440bcd6a1e4a551cde1feb1f88285740d8044bbda0ca89e151d78d44ad1fe0e656098115f2556479175643095785a5b1b6bc5a230a63009416ca0ae63abce5ad1e0e019ad1bb16781f51ece664dce3fc463630c7cd68464651d18fa3d1ef2cbbd40769c0040b0c2b996f408d43114a1892656afb03cc161e93fc94367c63ea53780a037b7aaaee692176a6b943edcd90ff8eae3da2be7f8f924a9ad73fe1ebd07b056bce3137094edc5a4bed78f79d9d733657fcfd83eba2002d77cdc4d4668c1306dd19c30266ffc0427822d32b9a93cb86ff8c01340f7ae43d139ad6d4aee6a50fe80729a7fbfea64b1db77779a96153532af38f2b6fdef06d91a069c86d12850163e8b90ecdb5475716421ec85c2c2899da8d858bede4202dd300d96bc912db0e24796ecf1373154ab1fa8c28420c118696b89f3543b419fbf2adbd31b2dcd7069486e8b9f28c0a2b6a602aace74f367cdf561591f6ccbc4245d4b0d677a4390b8d7782fea9ea4b07d75ec3bd0c3144f2a81fc41ee08dd237dabaac539cb57202a21831f51d1bb2fd231d4cf326c8350d04dcccd233999bf5854e3243b7e7510ca66b8050bbd8e8adf9d88645d06ecfa0ba7a99177a9d6432e283f3742be9c5098b67146f2b9e3e4b25362c560350cdb4a75f83a727a91480266ffedc40f5b20939f080a390963c0fed20c1b4ca4b0453ecc22d6e5c8f6be043d4d9368d48bb085e0f14a79d3e159ee44f36c2b71419cba7ec76873bf5e7803d8ea33c1262ae0e84bc7414df024b0df3e4a86f0c6430adb83ec698fec10d5998fab0f761a16202cba68e4b833566a6f7de3d95942fea5caec10eb43b3c302b41b8e3e11f1e380f831c3656f75eaa22ecada377c5c21bbed21de78f3590eb185db14f4d9b41a503c87dd1b81cd6176c1f48a1e294c8a1b2641a9338f1c9775a9e3491f9ed641f0e4cd179e87c93203f9eb00926540e566b84a372c2aa21eed66ae4bd15d147ca059da57b4f34d9f31f23f126ed3701ba1915aa422ccd83c378641d35aaefe2b6046b5b401dc6b2fbb5d48bcdc3056b7ddd02f6fc93980a02381f1157655263f80027e17ec95817eb06e88130d78ee96781ce4fb339cb175cf041f116cf985c3c011e837360c2677ee9fec439721d24ff595691ac853656f89d5919a993f838d30842219ec47c80190cd5ad6df4d843ff8c50068ad62a075a0c084abaf263631203be2d938847eea6f6e75e8977bd3f23e4731284b3e5adec876c9a2e255afeae023b6c837812811e0b3e2c525f48ef7d543b3c3b2defc01c3f541f2b830ce5bb0ca919831951de7710617dfd7961673d5c04e14351d4ead0d00978edb5b69f030a62fb4da56fdcba2450c4c78a47ff2dec63d7004a7c8fe0dd319d2d64208664059c1ccaac719b680c214f3078eed8a9d4ffd8a8603a3809c2d734e07db607680af14cea0d37d1d3489bea91fb84a173ecf26bf726643220593f2aace7696b8000e7e8a22f64d640f33c59446b6691eb70ccca458ae134652661b7ac7f9dca9b0d7cf687315baeb97ff9d1cacadf4653cbd69e4ea0862c52315989bc6c705c170220ac23896b41ea74b06e7cad0089de194f4ca13e1f78b5e88a0b2bd26519c40fe4248e0b3b8115ccccbfca658156635bb5cf79a1c0f52e820b5fe03869a4e10c161c3214c6a873fafbee4d592cc76411034b8ab57c2b8c2500e78eb8ce83120c86b721a52fe8d11e2e02edf87bf602491547500e027af2561c40337f4f0e0c0c84e4e130a6a2394084416cd765fea55bed250c3b59d70601a3ef8545802e6f00fc24aa01e07538ebe359f5fd7e4a58e2a80c3603c85ef7bedc4eb806401d490fd930eba08f5cd80730f2b8b38ed1b6a5027456fe428b3b249d4bf2c8e23cdc02aa4a4f79d18bf321f723007d3dab4447c3e893ade64110fb6bf1a1040c52b70e2117033f93aa6f9c14a268fbb10806b96d9dfd441652978914650f5cb6c11a0ce7347bfa9f95910526e4afe71f66b21ab9d8cec18c5a84a644d92ab0f444030a00817abc34ea33f2a73a2838bca8dd24fe8d7da1d1d761138fa43ea7c9754101cfe61d9483b664f1564827a14db13993fde0ab3177e809ff04434693fefa610a907237251310557deea1dfc721208198d09a94253a3a30776e20b66113406409fe6790d40e369d34eb1943b646a7ff21093d83f104bd02639b10c6e610377904ab0194078d927589f666ffd760f85b2731ed5938046c63b097abf669a46f0b0e5b12cb2ba21244972d9985f1963c855be9cabdc52ae9cb94e0de7456150e2f05d89a56fc92c92a6a429f7b6829c0d4b99477a286fe394803ef09e925ff941f0f615cf4f80cc3a184ec95bc1a9d98bb8d5a4028a323a328048ed91a15b77051056965f1af13038312d91b59adccbd95914fc4efa5a3f91b7d17703a3479ac500176115cb9953da72c304ef4ad7e2e373ba8e88311b7c8a3e9b56985455d3aba039ad79d8ec960016b738da33bc1c8cd94c4b48f7f1ae1b369ec567232cdbabb0b542baf32478c6163fbadfd533b9a621f097d500bdc9bbc8c4792b0013a94fa078f8f656aaeea3bda29922dfdf80e7edcc531ad18c65815313b9c4a0310d00c08f0dbc39a82bde74da859c8299c8cb92c3d768192e464c7f10a3179858a81a5063c66f30672f408f0123f4b1c68fa3cc28509f187cfc83aa9dd658ad1244b3e060fd096850166adff6ffaaf6e22d9e5561a2461eb08ae3409f7b73e18b864f20b560852f04fc801b3abc08b06b7a9913b65cdc169fb9c10f9cef1ecfbb94736093210765097d1e3fd10c4431178dce2177c2eafea60c9377032d54dfbef063e0dadfb1aac8b604f3c9a2776b86a20d531b34bccd2cb3c3126008d5b89e9299b03e841810ec7b0439cae25d07cc95dcb0c3ace1c8c14a0c5c9cfdaa1dc4f69b402674db19d605299cd1eb8c511e558be4e3ba86bdc8b4341e08743aec79f01b4087fe06b2115d3018f42cb82ae134d14a6f06ec740abe14b2bc42d050072e3900b919fe7f81efdae73cfcb7047f89d8574eb503058c92d848b466ee78412b8380a +generate_ring_signature 877d5c4074d52714c4d655d212f091ede42f756d0cbf9a50327a56c2bf9f1f1c 4ffb6136230d7f2dd39b2620c047884de517fd3d3deeafdd5941bdb5564b229c 87 3210f7b9073ee5be927c6ba2fe0656f63fcab9db846c6847d875e0cf468a8a1a c98cbf088115e7752353eec48b3fde9b0226c49b9fce5f087de68642f7c2a9fc dbef39f34e267021546030ec09b4cfa328b5700091b7a80d924867b0be19abae 01b69306ecbcfb127d627cad709c5151937db8002fefa4ae66a9ea18bec741be 4de8318d89162be90ffff5d2cda0725437b7144377bc816a9815b87200c3d9bc 1195405ccc2e7063ec839550944c0244c790892b7e98278ad0a0ca9ab23b25c3 40e73846ebb4ff1e86f85bcb6982e1080b54d604067180f2a28995931d6c67a6 ba617418f6ac698fb06b0e605d3a0735e4fa4050ea468e4485f077d0109ac388 5b33c6157a26f2efce71c806c7ed7d608736adc0b0fd1d5777479d672968fec5 8c2d634b2ff4f2459a23ed84bc2cd088ba5f69d1c8280304d7141a150d06dfe0 4e9d8073816134b3caa8be158bd531bcc4e3679801c74019ad09ef406940cb38 49115975dba4d526623718b55dee5f98295c80e2a59a272559cdd0a30616da8f 0289bcffe866629dc0f7871890aa6f44f783cd3f07f4731ea6fca98a905cd6c6 bae9f72c7c76f7baa325208282f7baca8f34ee68ba6cc8894d0d393a88f7fc73 fcc7ee862523ab850719ebb28876bff128c217d2909ccca2ec006de4d2d98470 9a6132e65554e80baa5724f296751b22b5c71450072b879a9c48862c2fc28a4d 804be5a2f0128f108f7ea22b537e7e1c83173cb64c8313f0c7104126d96b5576 91fbd9baf2fc6e19ec975c67bfc5b1ca29e1d44c908b93d05ba095b27b2f82c4 ccec622ef186fc58606521a8b5730dd5055e2743a5f206e435cf5d43229986c8 1a90127d0758fb06859e82211a100ea2325e25e998ceaa85682a95a43cf40a47 dd654805d0780ae6f2a4805bcf1b7d89d00b76be8607c9d70bdc7f76afdb97bb 446496caa2f9ea661dc323406a1f85d7bb80c702c4f45df8d65c3a541e61fd3b 7a165a104e123b31c1e77b1143231065859e892c9a9103b9bc5049198769141a 34a148d2c0f5f24c8342e92d4229e8500bbcdb12e18ccf7d6d348a7c17c819e1 e86b29a344a4f648cfa31466f863273cc05692756f5d99a835f8097999ff24ad c8c43442069c26957e9ad7cf0ee2819d5329574b231ec6b68615c27f29df99f9 902ef47bc744ec0b7592796ebbc508b1cf2cf20a6a640c09547fcf90b3fb4d53 c57657c796ef6c22a4bb2771216e6af83c8c5d382b0aac02acd29947f7606865 ee537884d9e94f7d07a2ed77e978903db9901d211e202e1005bdaf6b7c019f8f 56653db8a3cfe6f52a864ace41629fee52d4a01c81bba58fc504d938415fe2af b472444f1010b06f675737cef23901ba0c95f5c43ff363002880ef6b1c329d8c bf3d79d85524df03b9e0185a5a9141160fe11695d958c8495608153d515cd85e fb1295082e72141b47d58cfa3055aba9092268a563466f0cff30c18a8e18abce ecafe7e5b29e24004fe38a5ae14f3bba866f0419b5fa3d1ff883af1104934b0d df39c8718a81e8141aa2e9d5136c2c9e93055c90f5dadff1d4916d006a3717a9 4618e60335204e55d39f8cf9434eaab8a58fec9d0311b659ab7371cf8f74e049 d1171c99f9a4c5375de01fa857858e4fb84dd61238413a3c28e356652c45bacb fa429e6f48429d26f77e87dd300d66db659569114c3481f0e734adbbc236753b 50565220f3fbee7be3ccadf86cad83bd104b297f28b9e63907c8265b03ca3a7d 4c84e4e55423348c4c91717f0a4854a76409e4670845a4a6c902e8a1af374575 f272329f9fb53c3d1eb1672ce5824311d3a94b3c3e9e6daa8d89fbe82edabce2 25fbf3cfa06bd5f7223de607423ddc8805ff653d4b15f0e4e2015226538b69ff 8fb84ca124286a78874bcb7c76bc673949cd92c63204bf4234710445b67536dc 2e14c3c500eaa10e72fb6db95212a677297b76e2be77d2c3c89c1751188100ec 0cd9141cfff49fc44c9f277ffb6b61d415399db5875a1edbfbf89ce4cb391a38 9b44c27042bd8546d43d4622e869d5cbd11f7fac72eef1f218bf3fbbe7bc8311 db61874036e55711c8b8d69804a39efe81d9a9ff356189fa50c2b9f634c70867 db7ea3b4a8267b7d9dca6d0c8f6db9ad757a6fa4f16b851c5cb2a143bd7ec6cb 396ae35f9c3fc4a7675358e023f1fb886f2311da76733e15c8f85a2748f46605 4fc999610654175d5a36b4ecbf3fa065b3b33443ea41da9daf722e146c66be4c e2fc709d509b40fb6422c81ca3ebe00b1a148e148ce62c5cb940cd787f34045b 3d751157dedf4e579144f755280ccc3f042b2ad103c7e0f0432624164e538fbe 61ebd19c09e838bf7c75677b43dc24b7a8c55225c823630f0ec492a902fa9e6d 5ab7f04a17a930b9c7b7911ca86cbeb55da464f905d3aa5d59f72e6529862c33 b4ba0769c296e7a4e9064f689d474c7170a5c39e13592019a2371c81ecb490d1 9a1bd5483f36d606b8b503416b18191507390d0137fbff9c0ac7a10a55952496 818cb0aee011d8ba1dd0e79af709fbc96d1c3caff0b256f8662b353a01786a8c 76adb1a24c3b579f8124761b02f235edafc60bbdc259eaf9fc5a97e1069bbaf3 114fd9b8f43ab68dcee216068d8e1f91df7563ae2c92938ad0365acbe86883f6 ed5310871e25b11f26f7ad38237e5edfe602ebf31257f1f4ec5190a386487ecf 63211bbc3be619bb21b1831098b25b67cc97b1c3810f84e6a3be23dc35cd572d 9bf49584323bcd7b894bab4762c29b0cd6c2f61e6e2feb5437a81ad1ebf4243c c8a01ff154edbd60f44a93e2da4717b13cb1be2ffc82ffdb7e0f455d90f5fd37 b55204234ee2e8de68b52766155d269e34440885594e549b25ac1b4b562162f9 64d5dd7fc8f28cf54e403ca0d7563a91db570e3313e0706ed2c0f36b1773c100 765933edf4890eadc9a684f8608bd16dd4626ce955f5864065339a3ca93df468 a7472e1881fdbc6ffd445d639c0d5a4001c082c0dbcded78c9f94cef756eec62 fc20f610824ca0bc0976745ba12365fa6d67b1b0334a54eefa9ade851d1fe85e 3f328974dbfa54ee494dff8238f4d704dbb5512344d51356da190ac841708a3e 52aeae211868f2b4ce506d04c6482aaea4bef14d80a3d22dd5d4e4a00f7be3cb 1724d2c3e3c18891ce397d1a38652658762050eb84bd408dc061c82da5d352e6 b00c20fba1c7dbe735e2201bea069199a152b9508fec5e52a8b2fa6d5a619b3a 0676618dff041df7b57cff9730e808bd781532bebc34d1a3a6e9095aa460f427 a94caf2bbceb8fbe7bf823079bac474cb5b8e80121ba0273baea751b8dcfd94a 5e8e44b361c2f5649cf4eb59923bbcd6fb95f810eb0911737fa0a86cbb9dcf75 bf8e763f85d0eee34a7a6f59c29fa09d4c83d104f399dbc97e8d7290667835c5 fd668dec0b1eb4123073972e9c0fefb503b93a4cacc56e9a2606fe1451028b1c d2ed8a2e8d2c283d8652dd79a51dbd1aba529932603cb4bf301f4bde4464936d 163e5a331945316490d1576c52824aa74fe644a26d9b4eb3bb2852eb11f45008 7a25c491069a97e071df14a5f00d4ad509036a2c6617f530de83b4951192685c 0658100605e7180a1e5637d106f7fbf8a0026f2dc74f5e0592958e0ff615b952 87d7096bb3b352fcafe34e8eae4ae1c8c630b4bec0097fc125383eca9e208a3e 57bfc3f8da0aa12a3a767d52bff8b06d4b3ca39b60323cf1c1510178ebb35915 c02e88fcb149190bb291d9b1763d4a5f10abb1e51e41d619ac63e3fe5a4cbb21 06cd62e87ac9d1dec380e3e30c17fd31a76e94fe725350494c63422a5186f7c6 dc6fef8dd9f6f05a544710f0f356c7ac5add10bd7f303ddca32bf014d86281c4 4b854b80f75d09ec1f68c5d4ce6acebd6127ec976b5ee17d2a5ac629457737ff d0e58d6da074a8ecdceb4eec62181629bd28d644bad783171e9c53810a5aa80a 85 6b81b9831a81fbfaa765f7447af2edd31c75f5888b02afa39712af9e334f1002c5ee9505fa0f2e16e6fac68de00bba97df15d15dbbe6468d85843a617c9fc00d73564627094c6f6f4d10c4c5abae315850d723e10582cd0e3e3c9673c886a60be159f05b2f7a99786182228c47ca4505ba1fa65e1a61576629dcea95779cc708c0b95f430987b6c2277cf76ae38a0e88c56c3f1508ca8a73a37cb863c683490db5f002312845fc9a9fce24599cc27417b115cd30e339ec2ce649e827a7c76a01eafbd0e52c37e660a50cf9cf09228122965f1db7a426779ae0f2a771f5944f0521bab199d44662faec253a6aaa0a9a7cc610529bfffb62e63076920fc0050b031f30cc073f46f9f5bc7b40b6326fd520b1bbe1c75597d117330d9eb480375a02cda8792f7e37490b66b3633b8644700c5bffd2b361dc1897ea391478ba944e02c071ab66949dfed5a73496f1430a9e4925dd32d7dcde3f63b8a6dc22e975fe0f17978230c67138d0b8b86f48aed521b925401acf1230b18b07282157286815071b9e5280c71a7e3f359e6609df8022ee71835753ff0c031ca3ae98c12c482c0ab3de173b6f24a8c3653d1099f1463f49a69fae810314d2befd046371b6f39e0f2d662cfd2fce2adf703c1d49a5de9351f950eb44981e241667b09385b5f990026ffefcd2c86383ae666a5870cd05a286d030661acb8a599b1a761f83d549180841872c759465faccc97ffa7bdcfd4bc714ee42e369f9432fa5a1cddd505b8a0f15a90efed1f3672cb191f58bc93e577188a0369b8ffc3f1a712468806ef63b0cd2a8cab92c6e1656f6a4d5eaeffbd84d577f9e705c4080e88aa22d70d9a98f0bda448e326d6b3795ed5e0a75a6a6ffcc0aa17ed79066aaee77f9d3d42fbf64070bef78cf9f06a1a0d605500530bd18e035bdf23d3ee5a388378e2eeca72f350665a02aec8809e19d51e56f5bb17ea0723f544660c645338b358de338de9d8d001c746d241a7efa3c88d034330679731d9c5627ef6185b7fc93543a06d4816d04cc856e52e18d89f1ec7f77373eeb5cfd2648070565c885840b6b8e0f74799a04a82b2bc58c1885936139ba917e1ee0b62fd97b57c1063a25d057dc0620de910ae82c6d1a19298ab70055cea5961ee5157e735a0b031e5bbc92ae6b1a1eb857058e17833cc97ff3322dde1e85ad86acdd4f5f84ea76af07a8c9049a080c502b06af5c2b964e84fa5db70af00c4e0f8ea83cf24fb70ba5d351cc698e3589299a08debd5bd8a96de91973ca5a97a41f40f79647cb180dbc775d2f8e436674daef0bfe590a77978532974bb7ce54b21e165fffef1b846629a37de987834b342140053e0c8308c97006c3e7962f987d939c7cc023c4e460f7d11dfce3a0b9feeada0578fbb9c28a463ee07928e73c008f821e9977c053d037900eb2f423198fcc790715e698667bdb2816d70d6454f80c991cfb383733c4b31ba84500b4b59275b008e06c0b9b9f2b363440af3262c90e8b74194894ff0db159a5d84ec38b4e0b7c055d95d70cc1383ee778b7dfbd5e1b8d729a46aa08ad710cb48eee8a5a6a335c000bff7ba36cc08b838aa3d4573a8da3f29c1e32fafbdb1aedcd3696eb9389940c3786c24048e08779bd9e66f0563e576da8b67af39e473bc43807fe0ac9dc5900be765d8e6ba06bc53a5d0986aef5ddfe7ab1385e29d2f77ddafc09d2eb189207cb30cf37556412b160a41c556c70dfb471742e155938f850c90cc6c75b045a01810b00b85699ca0f4bec4086ac804734589e704e9818da08aa09eb03044f5b05427cb6ffbc9fb45b776250fd51ff7526cf65b148ac57c4c1b367706edd2fdc07dfe2a45d97fc05fb96128723d12b10710d548efa2e1585bf6b4852f76750d20cab29e0521a9ed776449bba11c3e3f5a6acb5c0de6e9a028ae70c7923e709370dd0f7aaa902ee6737c87bdd70edf4ac5a2ef1b1ad6d282ef5af3cb2fa07f45b06f13ca1fad4ee897b410f85a62bfdc029254ef1aa2383b7de13e078133e310a09a203e0ca75cdff2b40cfb7fdc8c2ebabb85889d28e8b1c89184a3e6ed984260d35a1e49b2ef6a13dc2759f3f79503ade9a8bffdd19ffe3d97e632d418661af0464341fd80c8577f9596e441b40d49e735e45240140fc6ae267552f79a8117d0ea33c1414c4193effd1102b2959952490ce9d70aa106061a040a31d0428fbdb04d0ab2309204a161e953e966c2e5e58de2a7e896280f51c9bc0c2701a68b98d00ba1e952d100074f39d1a36049eaa63e1337e738b20e2c9ea03545fa38998020e827eb4ffdd673f18279815d8e955a2d6020dac4f2e319bc216a497b58ff94206d874eeeaa2e1384e3985e41b06185b78f67b8a15bf6b8ed4ce8611533fee8d005b0d27910f5e85c0a0e82b091f3acc93f46d4c88b0c3da571ea1f896001b7504d5d97b73523c13371bff0656ea1cdcccf45d2bd4598cbe588b21e8c5bad72d067b02c8acd848decefca9f0cfb0b1261255f8ea98f8a1371f5e938c45c471ec0af07e9b1e6a6f06b173d3a36c7bd31de882c43e1d0d20303d00d78c2abe4baa0d7c8862b65abe5001accda0b355cf0712cd56daeb18ce96974b3f871ea063b20fe7edc0674594b65437c32b7e2b964c55f3e6e9bc4e5f9c3e3aad8c8a182e3c0afeabb28cdbc8242b8f58ab85d3ac29a9e92abaf27d416d129cf5c44a3d29ab0176da04cd81a64cd8b5bc945a004d1d72dc0d10ba979687df04659f3c11ee6c050e77745c0a45fca5784177a394a830ceb3734c7c9a426521065567a10d784c06309ca760419a9c434ab7d29232d13c2610897cedf531d5313579511935a7000597f769d651f44c0890976b7d77902c8f478957ceae47fcf95ddec185fbf0fe03a08fb5592916a3d25057c7d32a4a0ca5acaa0a6d70c3a3e07d8f916292c3c70638a4e6602e2e9c1a092d921b26624e0ab2e5ef50783299fd0a0a8aa59f216a0b2cf5135a7a5968225e5bdca80181d588b0879c6d04a92a13971309d8f3df5202af831344eacb85a767882c4f1406f1c495a63adaccca747aca775ef2d803fe0a90eb1224eb7ed7ff19a6454a54e7ca734b7c129b5e67334f7aa29b1712490c02b9e859492595bde11a6e77d85e6bdb1d80bc35153aa9bb86d48da3c8b35ee308cdb8227f51699bd718b1ce428b915f712ce7f028ec21b5dd2642ae9bb8b63c00dae84c7cd63f0138df36c0654ac964981120bc5193fee418c532249f9e2e90058fb4ba54ed729d10c6c646917cb752b8dcf4943d3e25ed1cd057d07a1aaf5b035e0c0dc7a602de2d8820bbca88e0e0abfe4bdec342683e3f553a6e80845a0803ee35e6b3e7b7b02b8e31695f54c63015a9815c66d6f2d1261168647db6712808522f73e1b60eba8ccd4167b7d1deb6380f30aaa6c3ff5610dc22aca17efdbc0e61acd6e9bee4c4827449f6b8c2b7f5cca2c006a3de07d65cd3291eebc44a06087a806c85d9b5c1847801abc370f6d76f3cf30d9356a422c34b015f6d07d47a0582d76bb4f1c3d4f2c366f4bc3ae87811109dc104c304d780cd936697fe8e310a0d0a40d51303959ae1d058c2fc11a1c2b61425d281ff7e60e44fd596ed32460d574508c5f2314a90656032691c2b386b2e0278ce1a68ce933019bb5098db8c07534670ec131a7d1fa55843372c4a06a606fd281016ea426ad816785f4171b60b3a4edcdf86aeabef59ac7eec706d610dfe0525a5b8fc634b0e9c75e832741f01f0badc5cae428f6cfe40cf6e1e9a6ffdbcfc7311fd6b0685c07ea34064e51a00f0e0665c7375e277499557b7da9b4295602d568eb55a0cf99d55aedcd14b8305bc0aca1fbd28129f9daf2cfa1e4dd5e328264502b47b3bf090c39850ddd292034b67f16ca07facdbe92b1d40889a942543e0770e6c901b7f838b9b7201de7c0a7cf9f8edde5427b56ae7a6cd6573417a6d4f598bfbe235a786713454eea2d501ba1d5fdb1c837a830d4b1beb34d7133e7bd12b9869320a998c5dc0392faf9109350dc80cf93b23018efd01572818c4bbf4968c17e4574c4391796c919179d50adcccef2742fb3f142c9278326a4dc53015182bf2e09546379e5f3e919271e60c0a48bea9b115feb1a72b67ac36404e9f5788ddf72d50e03ff53ec9b733e0f70b1c45b3714e94c45a02cc5b14f4d73579f94394c2da606750ba51dc6d0dbf140067b139a5203ee50890fbe07c9938fdf282224c92eca69d946bea5e67b017fb0c69cbb2d0b8221d7c7d11981b26b3881f7c84bae2e3280a234789341733fb0e01798ec93fb457eebba17da4cd0cfc5fe0ff321b381d9c94e9169516af93b6cb08ab5c3101dfbaeaec87a01a330a4f90ddafa9f52960aed1bb424936f10ec71a0c2ed983755b73e7e019f7000876ca9f13bf43de86d753ddb5bd23b5c2a356df0ea5fc8799c09dfca1c5b0ad6fa164be96886d72c95dbdf3a14be09ea667c1010b8ccd4984bc397cf569f54dd58764f38a1b912412af1e8e3a27b4675395347b0eb27d7634ee10e89d06d738cb7f2c30bdffed118cd1498f6b699a402b6dbaf70c66fabee52ec335d63976833850b0eb6a806bc6c78a87c436a0a828165bc250037230c62e1e841fba35d78ab6e45516a4f0f9b5d9bb67aaa098bcdae0ed47c301e75b5443b63f308ef0462dde97cef8cec8949f32372457665d2a477593d5890a3aa5c521865f23538975e30b2926b69eb9bb12f930dc15ddc30b796bf201130b82cd2538b2640dc589cffbdc1a6036fc3aeb1be370fc27b74b99278928589103e1d1451031d8eb641c0171f4de353701c6d9a7216ddf582220bf9d966d3a8101a3fd8b659263b7b9238ab2fff62231c33ac2099e6c5f968196dfe3721f69da0e0411a93fe645ce639f66d07eb24e8d06b4a746d26b0c6e6a99bbd8e7bbab31019235bfb60e774e57fb8d69aceda6ff5dddb779234dab34e37adfda08b5fd6a08b3510af235465e38a58e8a8457151bf35b515e157c5d9bd8bb75c30abba12c0e78ca34c7214667f4d809cc46bf2a466867281665c47b03cdfc8f122dc05f2605852200b52b7eaf99a693e683dfdfd53664973257effb1b3a108c0aa6ac15620ceb9aa4186c07a58d4563f0ef2875d5d52ab1891d92961bd8222fde90afffec02d48533f7d77fbb4d5519c2ef47328f55ca0bbbb070a6cdd5f156c082dfc8550687e2317a879494646c7f06e1f3be996d76e29631ec39c6733e9604a8fd41110e4616ec90c2b7555c926fbc483081503debaab59c9f1d33bccdd9b067c136930f62d8c59e83a4c0980b8299b5cb1105774d9ddb25291abf1f885c041ad881e204596e7bf8202f4e48a1144a80b0a25e800774392ca266725adc298d6acfb3260a917419b49963a357a95ebc2d1c870502dca605f38174b4fa16209bdaa4a12d0cd861f8c0129ab8d1a195d653b24fc40abcb231c7963f77c7bad7472a46ac9305583be7dcb0ab392185c83f057e8f4031b9602849f77c1f52c550ad4b3bad85005d3ded21d1c88f6a00c008343f241fe07d65cf711ecbe40323c19782e84a4b0e52e69a8cad6a8a8e535186b504a9e671141eb81094f52d2b81186e3d9c3e160f3d530f8de43b562f6db2c50937bc0c75a9e97ec6a80c337b9d00bb8a889287018d7a663cec5b36b4e429f6da978d40788c33334085988d30947c4b66ad8b97063b5bb0e1c2dc740ada29670d63125b1e377f8a123e9408030245bb83de34170c0da65119b9837a7885a5a0e88f7f676e48d962a7f1365b2914ec5f4df67bf5070619aac8eb7835a8e17cd66152b7f9252770be7134316ca6ef0c73e2a4e1230ac3c3050eff2afeb95667ec390fe6fffa6cc1ce356a13aeec853fed4a53b4b6022578dadff6fdfc9d52838a2234d9f1333017b13f834fd30e5c2ff6be368d9909e965613a29a4a816124f53182dacb7d66be9e03a08cf03bcc3df52579f71d70b623d4e99f45c67f0adbefe9e6de351a1bcd58ff33dd96c89ccc2650359df080ed58c305fb6b4a33ed692a897abae06346df4e31b92e6b1508c2ebe5c97f9ac0c36abf5c82b8f21410be60f182a887bfa5627a914c5d791e22c956f8276a1a2078f26ffc3321a1140c32e760b36fb958cc0230a1ac00aa3fdcefa02d53f06ed07492f0f4241181aea6c97d5ec3d89661a62210c0df5a91d3d73d772bb98b5e20a62fd3f21b67c52922ec750a2cc50ece8e9a06081cfa30eba8bb1dc06c8c7d10ea523dbe7b224b045f87de031add2a2faed25d1ee6ca35de70cc4e802e5bff10e21c1ea30fe8acd9abf8ee7ac8b575824037326cefba58c88dde0bfa3fbd7a3055ebd4ba99e885acfbed3bbff88d04c9b9979e5afe202fbceb2fa530d8c775a0e982fe8847f62053f9dfa8b7ee0c876025c6fcddce413fd1cc2cc2b31a22acb06352b41cef1ee63034f2fa10956e5d8e8108f8d3e0d844e6bf57cd654042f680fa4d08ff558b677c601457d9e5413331f7d31c9fb6bbb357dceb8bb9c1dbf0b0450aa00cc0c8f9fc78d8e27f4300a288dd70507f02455f971c20930cc5968210064f593a71fbdd164a4002457543f61aa10b88af5e6750583d5d0e2ee64ec8b01bd4d36f54535fd5edac983101c767cd06edc2de36fc8adc678bdb2da14af910539ef5b67bd3e32b62994ac015c42dc3caac1a1cb0585aadc5de8dfaa56ffc903def54b707b489181144095bcd87c952ec86ed41e125486239fce8b0c569ec4095e485a9d1e8a12373e6f5aa32f2a72ee95b36ee9fa54ac9ee47e25db39c30e000ecefd28d757a905d33a756e9f7b4f124f8de53d51bbeca940e339718ee96f0956b913ef3d7d9dc597e3bdb81b908f0af7e2045ed51bbbbb7f61af30bf9be20f6d8035201eb427c949159ca3f84109077c8e12222273edc35a6858fd39f22b09bd7077e3a602726b073f80ab7fb436d625aadb6487829171ec44926285e6b3093fafe0e1515cb044f36f466bb8759a579ba5bb481b1bacb5bfe1f122057f04053552f9dffea80819d246fdc69361f5b35342abb783f3857234cd0276aba7a4003c24cbcc4f6f5854ac17fe929eb297cdf27da0e5ed5b0167f9a45571a867e40834d553607c9508e5861d90a513c20f7bbae08799198280ba539e68f0caaef90a046e4c36701fd85ebdeb05d1aa86502d7daad12bfca255a3e5349409a4c19806e555e87f482b77006cf416812feff7e79e9a8c670979cc71d734487b94f1ac0528d975654aac31609e63bae4fb7cb353db4bb3287311032181453dbb35af1f0d6d4179c20a1842c9877dcf63312098d272973eadb41465c9066fb8de96f43b0d57d0859d3335cba3e685904ea6500a69ae7cf70d29279a9606079c3aeefad50ca8efca8698bd65b55e4b898b3828920b803de9946f1022f745e9af09adf5a4060d0378bf4e71981475ca1115395f2094960280aba3c91985c491656412d9e8032e0953b6a8bf161c7bd0b7e9635af20315922eeda9eed0bf84d724ef67eab804741f1a38988d705a3d8fff31f531caf063ec40c9102bb21f8a46fc316019480194f02a79afb88a96b80ec597fee0c8186c8f0171dd36c0cccd4c445bab4bdc07ada6d24b4b363a7aab3cb7b2ef13efa39f6d315688bcdad2044d05540b9979076ed035973fe4352dc5c6a43a98a2a1ff4d19751c1006a9080ea2ac958ebfd70ca86643f799455d4a37a41f543ef92eb2dec712609f2d687a357fff478a6a3f0c7391d1131e093a72babc0a6365560820edace96e368a9478aa586f3941e8fd033e047740fb1924e03c7c0cd4f25b4c668923bc3eac6de8cdec2b454ff05c680324089080b3a541449c18bfc9e83cb75be185a81b62fd1531c1ea3dd47e190102 +generate_ring_signature 23c9f5bc8a0fb5a0bd38658cc670c06e90d1abbc3747497418d5b5bdb82d7ca4 f0e47dd257fee9eb8f19d1291f6405f6a246edbb1e93784786bfc06eb0cb2029 64 2216d4d04f201aba671186fb915685264727818a92e2d6c20028e2d09f1be233 59536be0d65c9bc0477d518109ac1c3f9615c996db752e01c4b02870d9f2a71f fd572cd326727585c89d4db545c6f312a7f056aafc83ba66dbd4a466e56ab46d da85517cbab9dfdf36db6bce5f02e798f165c4c604f84ed2609e6277fb0f94e1 0eacebe95f019627b758023d050c6dbb47f924803bfa3ddf47e9a530d3d61d32 bd9dc1508517f44f4a85fb31812a7e12f56c5934515b292f06be03a1d5b86b11 aab3216375470a5523aa56fcba0e712d54a59d4304222612646a4dee637ddb98 ec07ccfc4a75e6908b3c708e6f6bd993b5c38e04e7503877a0c3d5ee5189a549 2e9ce4b6852e89326eb8678bae7147593f1c1d1a841a24ef0abc4aa9e2f4e930 ef4b7386828ff745a71b4d5f380cc0c6b082606559ebcd28318d3c05b16a4707 d21b8b19c5a282ea9bf1e6b9edfa55f055e8803e9a7d3b4753224a704404b3de 062eda78e40e94d8a86c628672e8822e16b412c867ff0b88bf86e4daf1d1338a 69cfc7dc2d3eb08c0e3fa1e0856c113725563be8b78723f1e0a2f383ee3fe221 6745adc16bfae92c51b677833f0101dcff312dd7c107847f8ec9d72aabc5d858 3a4734f9d2e460e8e6fab9c0a8fd1dd4715c4019556c46f070e2afb3e982a497 a1dfec461e4ac671020768fcb40bb962c02a32dd6ec5b896a91cab7c0fc19fc5 60511641e709f280113827e33504f6b6b7ed190b043049c4c3f684b607e82209 8d1d0a9638d6895494de736b7f7be8a0c58a2420b5728ff26fe59f5279932ae7 2d5d8784833ed513106bba6b6a7251e690068308c7ac14573866436a75676445 286da2c3c4abd940c96994438bb4e704b8d0c7fd3ecb191655e31998bdd181b0 434337fa5298897a0faf88b6d75803c1c9147bec259f3885cbfcb7ce72ce7e0d 2e9645069557743a1c1076664b913de47deaec35a8241643adfdb3867dcda183 58f24e323658e15a6a4e08eb1258e4db7d17a567b4ffdceba03f00f41ab1533a f629acd9a0b1d29e06467d2f14309cf9011e190ff113f09d3b4beab88c92d8c4 839afe6417a994b08101ed25a35ee4cde85508b0ddb65fe295133456c1e82ddb b349895bb3a87d78d1d3f7faadf60b906b60f00197c00e01cfb53cf401dedc15 68c557914d6cf8aaa214d787e263af55b1a5c220f0bd1abfcbae35ae9ccd4c29 8e695adff904476071771dc9a6f334466c4d16f6252eb04db8ce30886b2654ce 794f3c149f16c4ee523692aecf1d22d15367ed0685a45022ee410707aa13cab8 2333bb2d245a708a94441b23f73db20811c3b37d760d3e56a17657d3ac57075c 792e5fc5ce44caa6ffbff84ea03859a6003047b4ea277dc42c1de8b8b0dc1abd 342971f355d3c0b4a0483bb6f639cf9e7b871ee4d94312ae00ce6f71914bc65a 2ddb2108ef688ab137cef49f76aeed8f5a49f0feb23021b4e3c3792684b3b479 fb61d9cbf723bfb870ffc029682a75753347360da36b4497052606c5571542c9 0c0e1c493279d16aedbd319f5f4e7b0f4be94659149ae1ced807fb96c615057c a18574e1122feac290f3a6fd69d65638cd2bdab84886b2e5a378567957332099 45d86967e95496b3c0e77343a47dcaf3a562ba0553d530019c568894edde13d7 8345c09175eebc614b126de843a2b3eeb6788c23973db90edad3696368d3958b f52720dfc3b1b0faa4f8bc3fb731a60e1facab500f20f4be47f822ab5a9655ef abcc13e2d639c3361666e5fac12a44f10d7c7c845331a8addbab175b70ab0e86 90549a2b69c1c2a08584b19975d414de0ede6b3af6c159096b3cf13b3c03225a 41f1fcc8a14d02ff9a4269e64b216499443a7048205677b403c2103a0d0a4b55 9cc033f79aa843ca66c6677d0b0357a065fa0d6076c9fae40e9fe427aa65f4fb 3c9b72510365c6388d13dcb94b98cf0bf60f44a6a8a13bb442f08e9ae94986b5 396f7ebd76cc887c5b41da8d1dfd2aad5cd074d3cc510222c155c00f5472e481 8196278948edc831353a98c3cd15c3bed0b97ade1b6ba9d21dc9a3208e3bf5ea 8dd6e7dfce3ab7e9596800b797074912c4075b8652049b8ea96fbd028af96564 11d8f07ddff36280604f1e796aabfa6926d15fc91e7ffb8a8fa1459202e6cded 1484afca73c1fea6771cecdb1092504ef125ed20648e85a8fb884f4ef81c7e7a c3fd4df8f8f919a584da686d00ec2f1c411cd110a320d80ee10102fb0f638115 92b5b1872dca8eec6b0a09d5cfeb589827025405d9bfbc9224eaa870cf998884 b9624098af4a0bb3428de169b20667e805d28b9c4c1f359d97d7d51c1bd0921a c3b902b2920ec900cf1139180ac280bdcd1a77ec00e620c3392b8bbf435615e2 f5f7318f5cd07be4e61659aba0d3c8de5b5717a42a4baccd7cc98184474c46fa 07ae89b296aaf3669acff347108da91f8bb35bdeb033d4533a18a63f4d977315 79708c4af56e6ec198329d00f5ccb72aabb7704e9425cf96ad2e17a2373cb396 a05e1647e37ca359f4a5b20f98fcd42f0ac0db40e35fa5279beadf8efd622ef9 3b5ae79eeab598185ffec6bd7393e29056f6f51ab5c6459ffa74731ef511ce61 378f9d166357744e0438ae9f5ec180760077a4f8d8aadf262a41178ed2006ee2 5591602d05cbe8f3ea6b7687b5e1b1568a12c7da4f0f6c696559c9e7cee873dd f7a55d1f0a818d5190e2e270ce43d29270f6b295884c1ebc3ebcdd5aa2a6e567 173a7cf8e3f06fc423cbae82c31a1b17032210806052dbfa5d796e3e1ad09615 46795cb780ee4f9c4a6ab7cf6658d76295dbf75c50a3f0a738258fe1e98957f8 257260b5a8474ace8a796f02230c68fe301422ac26d10d1c16e66638541cac93 0ac14b331f791dbfc6ab25a0ef94a57ff280d4afcc26291b767af485ab2f730c 50 9a0cd54e99e28bfde72c6cae3f492a21eb9407c684cb198c5d289b7583b6360d5814063595c458175ba84551b2f59cfbfb60f4232dcfb3f7d73c390d5a157b0d7641997458f1d3d2e55c6eadcd498800e200e5a642f183de57fee2e21d1c4e0af700fe40cc85a5662f2031cb19b7d8cd5cb7561c3fa2b0da6b97992c0bfe8c02145c10907822b6b9f392b485166b9e6b3b82c490f6a077708bd13cfc4c4a9004daad8d1996df4706ea034a1219dc31db13ba443acd1c2f9ef1daa6d9298ece060511dca78d6e467cbce919d1ed40f4ffc0d9d5d4f71d1f052e77c3442297810cdfff0a0079913b3e7c157c741fab658694d12b3934b665bd214c51b7d80154072ffe38b6746220d544ed36ecb0775fd6fe1cca0e2992432bf035efd91672440174ac65ca8843267d4b4cda494955d1d2c2177b4b3785a8fccdd1c5cd5346c501eeb89663b9fb607c2709c9c813ec65e2287c583a1461dc9eeeca96de9d415e001d40cb3b6aec4fbebdd06afe5ede787bef6b842766e971407852bc2c754bd80e102ad20020d568a303fd4034e65abcaac8eb50757bc1bead9e95bba8b763f900aacccbd17fa7d687ccad926a250ad0483718a1bda827069dcb180c3e2e9b80046f6d525f4fac675572b9e48dc46858bcb826911cb74c936757a7dce2d5551d0ab49e6876e3f76569dd5de8d29cc0e0467524e2df86e113d60174bf81daaeee0fab0d1cf05e7a974a07abcbd16b02c90c909c5de1e87c13bd4c810856bdf6ce0cc1533be366964959e3a5f8b24d4aee9d5946373ae5d47c0ecde2a379071c9f04768a0d51e2b3d0823952ca8da0a63f3d0f3105664479518aa20bc93fae1d720cddec2243873ccffdf2d7db761396b0a21568e12745b47dc5ab3f5c6ccf65830d4cb5b7da7ca82898182016245d7e6a09bf2867ac1e24ece8e469bd89e0bf4304041c888569b61400c2193f507b2977f1fca7b3da447ac03d39b2ad45011df30160a4aa4af39653cd6712e1c0596dcc9240aaa2fa7ecb77190c753652b666e20b14e6f0d3dfed54c5d69ce4400c98b70456105f15bb8ce1d36ae8b51a3f97c50eba8ab41c672f4191b7c0bc1dfd2c121a29dfb48704ed2ab16af56242f34e3109118305d5ec50e6add996a54b63a5a1f04baa5f441f8295b9942e26ff2a9122071de999ad584754e118b978baf1eb9ffe4cf6e5b2d20cc1872621465544f47801af7dcd651b31718a047cd5809aaffd514280bb14de4fd9c991fbb4ee413534080ac64cf417e28b1291417c9fea124101b03bf0969db4a3cd87e45e10c089fa0ce97493f503c9b296d8a5f4c302e12e576d03b6ab21a825f8d9040471bc268704cf1cb8c21dee73da433d70b05d91d3cade550874a58f799ab591871de7dbfc06dfe70990eaeacbfc2cfef27dca6976fb6930e3b5272b7067f94b1ef785484702f11ca30e6f26fab9071ba021397c0dd301b293ccc520d8de320c2a9a3cb7c00ea031f47fb98895ab3389711b1cc6280d06541631244d271f6217d7877800d600ffbe6c4436ac3b03ce4b3be8809c4bc3c90daa2107fb6e93c48494c5aea3ff0545db2af10d9b398fb3d787434e65e3bf8b7dc6283212fd515e31045b70f9e7044ddf0d6b11a0bf4c5e5998d16bd3d4bc86a780af0cb017cc70f69cf76c94e20111f6763a397ef9c7db98e0fb66d3346b3cf92dace3e1216c66d022b506b0f606be28435b8ad9d2d0e52c4a785488dba87b49414eee64382940a5f58f5820f904171becfed9ae20d0242d4e38a19040352e3a0341906a00f6a12ce9f70077fb040ea7c9465f50983acc6872faf5bc6771cda9ad07cf122c9bcf429905abdc720c3ab2adf6f852f2cd4efe973097836436f65b5a36e84f9f21828a3da8f1c8050950befe70c0cebfce0c2fa32099ce023fcd95d33ffc1155d1ec937df114f45604c02ae7112f8795dd406153dde63f39526ca0d81981364407d4f28444ae0d7b05068f7c70dfd439af0e33db1431a055f1992bd9f84ea0d94a32d7c71528638e051ba773fc1512ae991a4a44a433d5451b65475ab4b9ce21b4b1ca22d75cc0110e9658959d8f289fbd0473cbf29b5f6ef8d7af1d5c784bfeef414d0e4a9a9c5d09f7b87efe9f07a6093455f192c8468583546ee64f435826f002877fc86ca7d10944460320f688e1a5377c757a68ec2d53c1c4975227bf893d0e6eedcbbfa40b087f34f086f5cd188f2411ad68884324c7f286adbbb1c4705abbf222a12970c507c9ab578c9529a8a9133ded27c9e3a1f5ec4c2d1a3b93e3df7835b8ec8121e70f37beec7d3b0c1d1e772cb732b9a59155d2227e4260ab6a40b18a1cc4e8a4bc0c24920d2f86f9a19f5b7f1de1ab1440c2ad87a0a29c4a7f7ad02f67dcf4571d05512e585da945d60c2b75487e0658167222782782b9a77f099a383f3518247f0d3e01324cc932f0760501d369a3cfab1cedbcd5986ce0f639d7de84c395692d002d01b72c641ad4553f98640f5824716db74ce1b2a89f9447fe4fc7e276990a0250e69e4a64fda3382f97841db0212fc5ed24ef8644544c2d9aa2808a9e08db08ce3ab87d7ef14a2c5d234c826fcc021cc6d829bae75755fffc8e838d41fd36076caa2ceb30020103279aa225594e2843ccdde2208f7062552977bc627715b7057629e5b87180822cfa1d97c362848b20579443f484b10bb355ce2095b4252c0741b53ba45b5b3970724aa300ab2dda919ec7e9ec96bc55ff947ba9d3e2a441013f70ddf20fe7fcc5b25b01187eacbcfae52c9d394c816f4851ff707779790e03b035c8fdaf2e3e3d7aa6950c4ee28e82e853d97ebf507a58bcbd7fe51e7ef1006575c26f76befac0a2d9acddaa35607b1b53bf85a9a000a0f63e62ae6f4a4303c9403394492ee1f8c56cb15aaa9f718a73bdec10511de6830ae474ef6152ea099c2cca21238ab104fb8e3d95d8dd33b0261c9e3444fc0a05552171f1b04d6d0c5c0e4e5851d1cb9fdd6fac114b944cc45bfc3d248ab01d61190222c162b31107f7a5d14c7a0fca9b02c52f9a95881188f29ad3e81a1c1c5ca37563f0df68850a9fdcec6c31572707ae8ff3ae77b75f52315602296a68f6db6b398ae84a238c07bee1fd13d8995cab208f14def989d838976b798e4e8b7e8f8ac437f5fb1d750646ea8913c2dc03e8c2349d91e192a7dda2c3f10430fe60a214170dbfd3bc240d30233f51d4a5f3157a8fbcd29bffca3430efbb9e4e6593310fcc5054b25e6b0a8f7f81bbb61dc8cc4a08d86eda0dda2f9d7ff1336196e65acb88a0d5c20bb1065e6bb798ea68b4c63acc6fc79a2e403abde969bfc6fd4c94f3a6daa2cdf5de04613b121ecb98d0d6214eec49a570d9072a7f15288dc87e47e00d78aee877270eafb7f4c95692467b3460eb80a161e58497950e56b46a6cb1aaace912f0c6120f3ab15e40aec285d362ad1d62a4929107603a6fbd0b2cd9c5b0eeed6bc0734e099dc44a567e7542700d194d20935b9f0062fafc9942a72fa9aa1ce9c12eaf1303572aa7591d5ab3ed2ba707fa691a79b9059bae9bd52fe57a86cf263e27cddc0c7fa0f1354ecaebbcc8aca629faa1eaff8ff623b190f331367e951ba541a0330b8add60ad9b705bc1ba7cd5cfa2c179e6eaac20a102dd2f8b31d19964c473370b2e06559cbe9200370cc1c04213ec87b003526cc925242b3e09252156a283b0036af67f34013e4b9e32af141117441d1ffac97df5149e8c13c32c1b31c82dc2009e73174909d29085d027d3dd241407f3725c26e0c066899e2f0481af23ec990ea193474d0ca20fb6244793c84e47b6f677be584cb030e4762dbc982c5d4f7a044d4b061f6d6e9930e05fbe7aed980e97776fc0ccd12ceefc5ececa294ab20c008a156418be199b641e598be1d29193f96c3f38804f20ef3845339e6ce24b050342e8a29b5c79b1f86ca1185e71fafbf6054237197ad90927cfb9b5f9e69f3c01b699a3c7f23dd70e5fa0a1f76bc0262adf2475d00996c8d5e2c22dad74446f023c1f478b74df2fecb5ec683141c0a96c4023d6c4c2b13abbc6d5f9efc6bf9c01e6e95cd6d2d00eb3b8fb678499e4893041ba5946577519a3affae81ee51dfc0533314da905ebfe4753c240567e0e69d1e5da65ba45a7c39cb2d8c8f42c5dc503cef42d6c719088c8de4a1d2af347896a208de11cc3d66c21d767f211314e090a7f4a04420c8e897f192ff7d7851f17ed14d08db1f0b1d368611331a61412040251736c1e34cf5f97d6c1d20ac174967e0ab68786aa972039c421eae75feab40280130c39862508466dd0753ca12a44f6b857510c51fc9128aaa96b611125150206a1ea400a46f2603744eb7f0fefb22077f19c6b9f693b6944d46c5f2a57320f0a87d7050fe5d1f35c8cfa285fa46ef13623e80a3b62d622f039842b5708890bd69debcc6c927859e7fa5eb1fd01ff1967cff1eb0d149357ea126f7c995d2400c5c5d53368d68ba62cf63581270ac3336b391371cb897ab732bdbb1ec998df08ef6df062d53f0969bf9572e9d52d8ad4e5a7b70e64bf0059157702b1ed8a010934bad0d219fa16a2732fb20146ef5940fe7e6f8219542e54f2703dee55781b05c07c26f8bdaa47243c3f100df5f95c27b3d18ba2747aafcb450f3cee3abfbc0da6065d25bf30b3e2e0be42a8052934fbe6ed9c6c7cad930c3b032d20d613f10358c07e6050e2f79d23517cd7f5df7469e347b513e5441fc493d1327374585808750b8a9300bf560f0bbcb9c98062c403f704f01634bc605ba2edf4cacd09af0454f0be4b04e36ffcdae8be9b92e1fbb1d8b2c90176428adfc4062739afdb0d0784613c9b9c50a59543eeb1f352b301101f6e5d9bf114f79a9bde344212cb22046d7957c337da0edf56959c79862cf5c981e0c1547b2bfdb7e08a919b85c8fb07c5447a5e1db9e177c2090201790e494edb3d73c815e1307dd8e0fcf59254670aa21428772c370ed9ba7cde280cce82d125c5cd26979417f69305d538967ebb0e810bd4eca100ac0155d843fe6b8a326a27e50cd98fd224deb77030fe5205d90e12af378539c11538240fe1c94cd46b5bca6d7bf9efa361503bd508f203b7250a69ce8253f21e177ef6e18856d02fa1108df11992c2775959618caddcf093e7062afe2bd1efaca9f288398245555f0f3744f388a98df45f1c391e75716fe0650c5b02e80255392b26faa46f4675b55854e85ed54593032d59923d3c169895d804fd448d4c658c6e47cd2feb3c47239273ec587dc61b20d02a5fa18392c66e560c99cffa58f5b204013524f1d26ad0253dd405898ab485dc11495e2ac6a19e540e3e25f8aa4d0d8ce6303b8d91deb8bbf7fee93fb3183f65495337d575c73ed70f9788643fb4ce2e68e4934e214ea8a10b5b0cb874b66bbfe3fe54259e2bfcb803780522b2709336e1943497b1aaf240f7e4b8904df09d8d46167013a22537db0105a71fec02ae0f118ce263e6530abb9561eb912b928064851a8db4883e73af0ae82d70a3d8d7bb06eb0547cb1ace67928a28add43eab4c4eb1cd95a94c7da90901738a2f5b935ddfe6bd172698048c94eab2ac87647467424e377408ae19fa0daa940b68d451a6e12a9d19f9af285b48b542a7e3f2690d8701e3f643a8ece1021a913562d80e5b9fcc7c872a3b30584c2870d1843bad3cfebf849cf4bb4600064e595e13be3f580585ab98a10fbe7f80d9e196c9d43747e8f3a4303ba4d0b80794dffc188e4b2ffe59da63607bad208f1466eb80b369faf3c1288f724713fe00 +generate_ring_signature c3bb6b6506cd1352f41f54feb12b0827cbcca01efa75ad126aefa9e94ea546ad 910aa8f071164cca51638cab88fbda7f7a467a6673c478074127da2bbd220eca 2 d29ee7a6a4ce7fe30e6a52e8c8faaca807f177665ecf830c19f850eb14af4f86 58a93e441f5c9dc96f612b5f51906fe82371f67da79fceefdfac2a229cdeab35 d73085426b1006df8ff9e7867dfdc31586bae6a6077177e5a6dc51427fb76d05 1 25c01ff28a0c61c0a5e6d7bf7c53c56510a042bec0f81ac38a1d0f3b827e13091f57738216759ea85356272b824deb0d15e0a9da9dd09a0859e2edab3681450ba93189e1017c004170829d6ef31ad696f7a0f946629a26b08e2e16b991f3bf097bc2489d82dd9a710b34dc623f84830010fe381ccb83dbcd7a6fe278fd19f401 +generate_ring_signature 268674690bd9d9b074cb8b4b41a41baa92199426d32d82dd41f7182e05d1b2aa 84169edcbfbb196422abce1a01824088aa40369be8a9c80e87437e53bb4716c7 42 b3d2368a0557cea8de40c67e6d5e20fed84089837766fbc36480688bc6a7eebf b06272cf373b7cebda7b639722dd8fc025fcbb78b7724a0ea17b630f6d3db311 874a2ae9613c0d65eda7e8d65d99e98346d02433c35296448decda99db81747d 372aab47f2d73de35f058b48ed88da486863b0c4c0739a24b5d870f9a73a1247 a10ca23215c2a20aa03412fdfac4c248066538502bc105caa2864ca4e73b7abe 4d47f233d29f9de5441644b8f9a8357b64a96ae101671de0dd668e0e41f191a0 615aadf49b455859d495d26c32e94929f5170d7a59dd47945b2c9b45117f034d cb91fba6e12784d7425dfd03b50cf325c89abc80e3e116430e38a680259c3871 60e71019be7e5106329c92859c33d195a8f4e5b20304f64451fba28f39d2e0df b82e13af9378217aa09a4377ce9ba33551cca15cdf6c49f3af5146434f191e33 f516d466a674c8da3905398422fa82cddecde437b773e96ea68be67dab8fa392 4de941b4951a156d21e69aaab77225088ddb787677881d893d564eb5b3cd0faa 9701bcd67ebe7c409c0a37d38277f40a5202bf26f004fad09cdc6001ba91133d 30e6a449498a1250449143096e79cdf80b967509c4477d8c234cd24b6dd1417b 48abb3149b4d5b970f1a0cf8ce07c4bc4547c0b7b4f9f9c0e5c9b91496bf2c14 20afa3d8ddb849e09f178aa2713da3ad42d9a42f9313564cd3a852e0a22c98b6 62f1ff65809357a478ad753e2035d5a0f975b9973f077d61d412df66456b5ad5 ff8985192f6c61a664c8294c6d4c6466e85e7b18a9c63aa97c87e1dc97b5b5ca ff559ce3a9c25488d5b485ab47dfed840d642a887682d26bcaa6734c89d76ef4 7f62c0248d41e558b69ed0e3931ce6d03d8300bc2a3961e19c7e193f91bfe824 dfe89e5156f918faf367e660b216b7104a0c2e98b0e6eb4161be985bf7acda5d eb3e53bd1874b95adff57573deedec696b288ebdf78254fb87023a56cd7bc568 7febda4bbde086515e56fe4a034549b8586d4fab5d5c963bbfb1234de0b96ac5 739777453118ba1b27ec9cd6f120e7f173afb2683d4a56c1181628f118fbe949 d76ad6d158bbcf756cc8a6a447962f8e059f55a82179675c0814020f57c73516 328ac5415416ebc984f28fca378e02d1c1c9bf55cabf83660a6b22c5e9a567fe dc60a313b9ef9a810f3e4c0442e6af913192c5717340b6fcb523c73b948faacf d527a74f644a3d63dccf4a9293204a856e889b53199f9ed3fda45c85ad7588a5 3425cd974c456235b7b978826962c0b2bae8867b66bc5ebb29cb5bbaa1e82421 a5f0679c31224707269534fe50f2ab76ddb241cf7ac4d3d2cdf63e001efeb2fb 470740b161e4c7128ee4ddcbb5176383b40ca968d00d8a0d76bc8a7e481bf1ea 61c307f4a3cfe34519172d20e7513e85824145f0144a0d387c2f10d73933f26c d8382691809ef7ed6b047943e79c2966eb0dae5204ec4a92f56f725a4ac6d26a 69019957bcdd86fc4ffb73186ba4df49e42f013d7fa804dfed57ea4138162710 ea7392fb7269be1f31a44130275c15a4fac10c0629260bb9df19b5c879542bc4 d07d94831ac413ea292a85a50e941fb795a6018b424b32a20f5d2ed915d1535f aa7c1af2d4e10a83e6b5f0b458451d1cdd19d2e7d48bae620e7eca7dcbb9616e 03a8b3021f95062437a980d0739954d90a01865700239dd0d3391ad8c46384f8 7c24c9b8bb03f8aed48e952059ed46f4c88d06500a460e4d041fcfa199e92ebd 66a11365d0f622edae17ba5977784111ae783fd5ff37f560b0227fb3890b8851 cd5bd60a7b39fb9bf19a7a7762242c8f76dcf312102035d2de3d6f7232a05bc4 1148da79c1dd872cfcc2b5c706ca5a11fcd1055ad36ed6378b468c1f5e24df07 f8295a11596a78388c06a258e250dbc2e90738e730094eb0b33e3ca06db3fb0e 17 6ba3f34118d6a64eef4427a39902d5d664fa9232fe343daf9c17d98076302700705eaf89964d9a8a7deb2591aa9d80152383bbfbe4aa6dc37a8920567cfc42089f131836e84682878fd272778b04adcf155df836fafed5859490e0a620c7b50d4b278b1ec5dee22e63fecd2405948764e08c72e9087b073a39d4066bd3082b0b78cad0b68e88044ee54ea99544f0f56db92892b8dd12b9ad392624568b01df0f73c9b8358a9b5f227ad68822497a1705fb50375412a7be6cc40d9cabcd230503b8dfa88bf05dfaac20a9c364543e787f9c1821b731bff05f908fbdaa444492007755c25a905091f60931701d242cc163966b1f12617d380c2d1bea3b9a6f6b03b7c6a09732bdd7477bec7fd2693e0cf327dec2548cece0a64c0f17f04251630b4514ad091fd15bb2c75694057080d60131ec3501ca2eb50019efa1aa3d976b0c4fdb431829fa9dd2b1c59da9d01d801cf94a4410735af1ff6a28d41dc53a9e0397bc7c0220d3fb6742da87705a57c57689f54c5882bab5bbcf0c00707fd1fe07917214e97555b9223d8fb78f4b736f223e145f9a2d7af5c0c81e226f2de29d01893ac55f933098884478ed2919527013b783b8b6286fa0d1aacbbd8b326c0f04df6976109fe611896dd6b21273eef3bba8c391c3f49550e72fc1b37cfa15c7089657b2996a908b1769665dd52c08648c9f119b46743f02c3f0831a5e2607f9047ae6d56a46c4d07541e66d1e5cee1998a83611dac5de0835da9c5f4d24946508f53c3248eb0969d9131755aae55324640f013a291151c83aeb93f80a149cf2022bf421ee87d498b4730a4b2432ec274471def33111bc7935e48e21078083900f6c0c48f495838cbf35236fa94946dde4d8ac0d38cab68c65fad19f34cd33cb03789af7fadbdb09f92fb0c0a6cd7fb4c0fd792a0a3eed232dc532e149fa346b0190c0cf7e98db81c88c0dca8828f545d0e9574ebbe6972d564f33460a5e42290765eb25b6081d92d189d1c6b46d1a156cbb4f97d5309f22e1ba7e3c589aebf909c6aefc590559eb09a2fc74571d093a06b0afd467dfe8575f5a52544656410d04b48d907060e51dbd46225d06a633706f2c9f33c819e48ba939a3875862273e0d47a3dcf4e876f06c7256e8857dba04d4be0522c949a0c24ba0ea88efad53cd0e7b4f955fed79c8a7b11bc654e124e2436be9dcc2f6eeed5b4f41810bc4ffe309628534246cd2dae5e51a9ca2860d79c4d8907a57b50bb170b5f73b40e512770b36b45e1eebef6380c91a92cf3451e08f6ffefce787aab6e6348530af47b3c102280ff2587b89d62ba2b06db19404e425eedb32d7681a7f77cf4baefa5cf5480226559f62eea7ea67b7550b7753a8ef0046a995dc3d0391a33856d11073785c0be45f8139deeab023d70966d0823eb360e1616888499a56fd0d6bcc017c9a660e6a16ad2293c1079d573008556e3705f536ee3415167e407c38ef14b5e5f1280679b718f202de973868fb92fce4d378faeae46b3e6d827f4f60e334378fc91a09ca104b7f310f91e6de5e554f0a0a874a3d705615bb3a02a8b29dde79d074af0a5713fd3d3941c0cb85c215431035f138ff4358e2d9742be502346ec829d9ef083887d09afa80ec6238cc20c7ec564f7252b333ac6607a9689db433e7bdb37b0544ae91a2d1ff4a2902c9d69a71c8421b9aef939be1d70265f469b71affc0310b8675407872fa0a3a406f343bdcac64e96eaa1f6df0618f8c0c4e84ec93d978072bfafacac7ca2813d8d3b6ea442e1cf18030dcf506d96012d3661e9d399ae30cb28e5d0aa081050c8682c5e39aefc56d26c569ac8ae505b926dd9b6ff12c080295f208414a0d718aeaa25a8edaf9d9c92e9dfbf3fbfa43b8ad7084643ed9190acc75c2301d6b81dfda870fcd2d4c9fb7e4556d2ee05d811455ffc96d7a2edc07ba00aef3565bb11cfff2c6d59806bddc2098314b3409a285d4e93b6b6557520cf6d3c07e445612a4b2533bddbe20be90eade852bb65f987f9f332902e5674300035dbb07b9abe345e2723fd3fe072cb68e1fbabf602f441e56d2e6e6f857b001fa8fb5d29ecb57cfed95e1e5ba90f4f48884b0931bc21a50fb11a0aa291d3b0c9c34c3caccf46cd4371f835755c515172a7c2f519b297fbcfd82caed6b8f6200c00fcf1fc4477cba0ff48892438e3ea798b45c31b09e237226646af87720cd02c61d878b0678d3bc32bd62095d0af62803b1812b84e884cddb5ae443443dd60a0b37997bea1a915830a1d51c6a7711e10c41d5ee45d8a6884d92c7dc318522070f964f342c5dbb3abf8f9afc6efebe4b3408db8b6ed354ef770aa2fb12229d035558e965fd2d4bcbd7d94285c48cde29ff84e1687f20640470bb47051c23f1056cad1e929fac1f61b59a5bc6a99a08a9306318dc7468f9daa1410c144d09e00379b7502dd294edf747040ff4d4ea830cda329b5b18fc863436eccec86e38270df0b712069792b08626ef240f1df7ce752393fe219812299d4da04726c2d3a604271990dc88af0d865883eb3e2308ac8bc3f26e342767dc550793196c8bca1309b8f855e238b59676be07ad693d88c5ace477439b1c103b99f0ae51b5c287d309036fe5dfd9de9416331e29e36989d8f9e6c2f3dd4a5d1cd7030e099fff54f109d12357fe8f874f51bfad1f7e07e0f6ac41b540eb7076b224e3cbff1cc4a45700afabac7aeb969389b0fd1482eb904c58230688be90669d503e05d9ab91345704c39ad48fd1537085385883d3dee96c3ceaa0643a5a634c447a35b8df1f0d5d0641aa64903d162245cd62f96f68d2745441f98634b00761e44442b2a8a9dea10e6f98d8ebe20c96fee91688f678eec379433a207604eefb885a0f12d75dc7f50c3444137c43598e117c24ea2d9439b87aeff9b8edcfb72ddbc5f52b7c174b1b0174bc44453f23ea99ff0b4101afecd650314d7189b4734e1a8334989459708b0dcbf1de257bb1600c946ce86ea4fb8ff7cf48e678ffa788bf956ed15e04b74908defbb20004d81f66e0473e3c5831f50bf2a98070ae6d64f6bd30a18e0083ee048b34e3ce5f89d6699855118c353cbfd4747d1ca59b57ffbfbd2286aac392a60d97b4f47000487086408dbc2e92c3c2c69c2e264864bc74e14ca61314bd7f9705b216b7b0cfcc7d7aee5475d9601590db7929925ccb21958a51d632b70b12d808eca1e4127f1dc33787a668bc0b7f5dc7a3ce885aa23cdbc81e3658fa76d9a900449cd134e627501fb8742d15f4e118c7426d47c187cb6fa7a8980c58350ab806b7cfbb3e24949f2fb59a3f832d0ea91d3c89a308e2f86621b09acacfc511d302908f11756f2b415b08f0b68cc23637c505711997e449df7f2be6af0d4f882e0e2acee835f1de614cf2fdfd69cc2dcf6186f15449f126295ab3e001f196ac9301c263bdd40dbeb010c16e49c89f1c4b1be38b6dc4541d8a8182c665181819a70736d55223420db207dccf53ba599f2b52e479b94eb4b5fc3d7b703979e2acb40b08bde31399b1cd4e9de4402df15cecb281ea4cfc17f26396662f0cf6405408031a08ca9a53bd5699067a3dc68fc6b47687f4839a67b85b42985f062c76c4fc0bbbb5aa3d95dfad752c4da2e60a2ee06ec1e28278945fa65d587facfe9598b8043695fa9950edab521a197c024f6fda766153d9e3b08176ae5ca88a4865b25602f704898bba829c3b85f911ea03cb5c9fee67a7bf067e739c154d46fed943080288f8330808a03b032ab28d44bd422cb18574235fc33242ef818e04051ec0df0e +generate_ring_signature 84d634484b61ffda4edc3b2f5ce759b88dfd7e2cd71a48a715322568408b0e0c dac5a00d84cb587acc580413d7852f722fa17fa50dc393f9f17ad146973aa511 13 4af517ae0f2ccde53fae5d3864debeb255ce614f9f38be6c26b5a08b65d31dd6 55401caa431b7c1efc84b5d66e3e3a0f75caa90acb42043a5e39a9b781b0d5ba 1afa2d3bb8bb1f317aa6471e2ea00d4f3b9c33453ca96b2e22e6971866a284b5 222c82af10e40bcf00dbba5337e16efd74ca678f6b62a161f116d120f8ab39bd f0d4695eada66463882904ac51d72eb03573f7e37beb7fb3f1f3185b9f9bf6da 89b1c26454a43149a62cdfac025f8fec9d9b6a45423c81d7952d25407a0eb06a e9fb153da97f04f2e913bf7a32a8805c63605dd8618a7c22747ae83779cafbca eefe186f229292b2ac9804f4e93436d494e5b1cade16873aae6453ca949e6cde 934616247b356d08abe5f77de5d257ec58c446d60a77a83e61e91fac95032219 0367412029475f67f8f4160430f0c9081b42a73d7974d01e1b1e516e0404f128 a99e43a277f24fbed6a217fec04717c189406d0cd47252759d3a5b352716edd9 d83fc51b451416259a85946dbf7c5cfc93503a333ccf401e25c383d72e8e2c32 a668939ed1a95c5220dca10ea37e8a3eb7e78fe95bb32e552a6783161916a0bf 1108aad5506e4bf46809e98a29a8e8a38737e7c6a3d76ba46d67f4ba29693c04 10 e1da417adf9e5a6b1d5b8e78f20b9dff18e98dc9a96d7fe07cf44903ffc55702e3f001b7a775b5d665a812b28cca861342a2ce01c7a0a953661d82616e20a9002ab7bd8b0a5c4e6590d04a215b1ca885e3e72bd7a8bdf97b48d810f5dd55550b154c13e5c53f3a68661c54893b80bb7f1402610d7a76e3031fffda15722094024f4bd13f4ad7285d9ea43bf55b5d08c1a974d504efedc9f23b696ed982fdaa0ef38ba8004357ff573d53f5502c8e5ef235d5fa666e0b98141d49a0ee8e55b803aa10c9c6d32931eb72d04b650d4382b993dc591e1da22f07e5eb867846b9dc038ddfdd7acf3fb58d1da5c6da876b2c15ca9af402f1c053660c1f8e86f1487c0c9585aa3df01dc611e27895803d8ea115e89117dcfcfeed4b812e144a95b8d709d6ffa03ffd6767fd8fcda2e65652ef4916827cc566f7602e47ab776cb6df2f06fb65a5305f27027f39caddfd1ede0f33b0d9d3d02788001b2442139665e3ce01a703163aa80c4e9c551b706e01db895c7cc05afaa2b572963ebe50aa36644c087ccaa7fa60f3ac9a32675fabd45882f161ca678d246de4f6ac58483d92d63700355eb11017db69dadf8b30190f1531e92927c1d7bc5cb66e6a822c7f3b0ed507adafd3b59c3d7106cb0c9603e872adf47bb31f648e223feff20bd6fc9f20700cb14ab92dca062ee89053db624ef284389b834976a06f592e4c2d57c01b2d0f05729489e40de569d9de66e17d39eebacc8fc23b5d532fe1a2761c059c3a650d0130116c922cbb5ee1fd5a080b36df5f78a4294e88905a52b7717e4ab91484790c01c2230596849563be1d4bf1159241c893715e097e4fcaca3497e4713450490425eb545ff7886459e2286836c3484cded23f55f3728d3902b994eec90aa8dd05790b73f49101e00bd6e17475d4638374411182051a5ab4e559bd3e4901836d01c2dd73608e4953fa0d7d11677784cedbdc5165e201d7da15a6745abeb49a0c02b4324072feee8e94f5df53213191eb14847a5806a734a5ecd2bb7a767911d605a4b37bc537a76bf4bffbaa8ce802cadcec691c4f9bab57deec90d94fc1bebc03aaa1d945fd983e46ce171efb77c411d76f5e4a77e254c946a3173a8a512f210bab86518f052f2dcd17baec0560ef4e3d17b458b2c7f695204f0fe34923559f0c +generate_ring_signature b6ab0649ee9830dd3b5029cf67cef6b2803d2462f118c2a47bfd766c206f3c5d 4ed7bb6eb073c9ec628e9097d39e298194085284760f91f761c5d2a88834b9ef 13 1b39aaee0d5895abea6dd8582318ec274715ba98bd4f7100e594a4ff9eb3ca3c bdefbe606b0ce9e2eb5768eef13f5037ac149f2a6c2417aec42f34bf373aba70 f6474943471d14bebaa0c3ba8494e82e7ec5f179f8c5f8e0877e5ad26bae0b05 2d464b1877377d5fb93af201bd84fa0cf015fb7c2aff1a046e5cbc8a4280d81e e10162d20f8e1541b490feec5e659edd5956e7889ac301375b41d4c1d74aa700 d8a98ec825c4c87fa88da798fa060e88935fba2540f10b18e5d06e2c6687388f 00ba12e1ed659df37486ad7958558aaabe306c4a836f0eef7b26ae0ea4fad6f1 0d09ed6821b47c0d6b6c099dbe189554a947e693f348e5573ca903cbd3b27b6f 174b4ac42392c08e46b5e695b8eeb073c25e299768952b955a620a979483a6f8 c664041f2a889e2dd1c874923583e1c64859ab75de66a20181b6b897ebccd168 0182aec0ece60541938a7a046a9f9f3e09951730aa95190084172ee9b5fe3584 fb50a980d09d089764eb877b34ba287722623c86d67683213a63d4b96f17ce40 f73e7bf0ba7c8be1876fbf0582419fd4f92b3ac59aa828ae90e4e71cf2e5c60c 32506eeadfc8f730d3b65f534dfdc57434bcfa5b0d28c7c8fb613634a78d9e0c 4 bd38740fff68761c03e9d9103bd54d47bfe1d80a664a37d20d5063ce22ab6e0f42c8760f3a9d4949b93b8e25e7b5c82300ff1a32aa23f7c722e72e1a20b25d03e114a4f5179f0c777f43dfc5e8d59c70f0435c023bf6bd561457f54efb261a08183d7c583f5b418d79cc741a2db14634810961e4f49956d0f122e560d6445704c93aca385222d9cb5eebda455be07041bde23c58ea39b67dcbcfc75938f06801de9dcbb6371d764891aaff70578fef0fdc11b91f6d6b67915389645e6ca0e40a7481c3ffeb17ee126a758760da4a8777a35360dcb782d922c7bdd9c7481ce8093b85d43e467ef0306b5244a22fe7c18e3c401927a4f50a5492e77611eede940925bc9b27b09fcf45a8d53fd895ab217696ce6fac63fac9d6407bbdbec8b48200eb6770608da26e0329a095abcaf73ebf9f794c705cbf5e564bb3e871643e4a025f2085373bcd4061bfc209735e676058aefc07b004e2b61ae1aa28379d31ed0649d67519e7b8b030a2b77d339c4728174ae4cf1ee9f0e9f570d052d365853f05a59ed44808ba467f1d22904e6532119ce1768ec46e2849201caf14d1fcb6220fbc906cec720055f9d3895c8acd4e325d4bc1be65ffe657a4cb300dfe6f0e5405fc0667797b62e73e4bde6231e92ceee7f6bcea4d998a85406d90ac579c3b340f4417de3c8b26c78759ac26e976ba5d18aef0ca354120ec9a6430fb4a6d8054070cd0b299b063a6e97b040ca58037c65ca900c8e8b0a25f7d6b5d8462c5307107d9adc0dd41856e7f2f8a85416659adae7178ab4eea41f331263aec54ff774b0694deba9ef4d89a877733ed84b689c50ae5860d85133a22e28be6da345f834504a7d383037d642de007cc85e6b87cdae0fb2fdedbb03dc963409f96e4df5a030fb227aa324ba28d8e0f55c51c51bb42382eadba557103cda388dcea9cf9700d0fd8fa9d84e8d012c91e83e5e47efb0c21141c8eb63412e7a71dcc3003422fec0f700a6b5ec7b028e355e83b919e1dfcb690d5171c5f2878ce9f166663cd06ed0a1ca34401d03102cb3a80ceadbb5ff4a65500b001d9ad3026c3f363419d627b0f5575a03a04d558ae222bc760584865de690e66e979e5a790bfab6783f47c5d0717318d79eec74ab56ba33940c9975d0dec5dca2612a0d0940193f40610b8f20d +generate_ring_signature 4f82ccbbe33ecb61ca46ebd5f3a882f0661ea5e3b4751f0484b7443b3a035359 e5dc46c10b6babf97ece1748ad7905705f086eb0aef9cd55ef7f22283c038405 214 e6c2c250fe0a899e313a74782aa07d1f506489a530feda6c51071e435ac87820 f0320c7d8f0b110e700f05740745a6c3c03ec54dd9d98a693be07abe4ed262b1 a19ee67df0d938efae182dec0872229bb7a406343e8550c89300425936e4e91b b7e0e975266b5a4327388100fbf572b11eb565dbd190cf63d253fb76c82bc7ce 484b7430725f1c2f02a28c95b781bb86b9a3fe6eb6939351a0e16b5b4991c99b be2b0912e82c4655496d1b59eeda3a24111907b0f69f72abbd4154f0b6ab15c6 f40012785ea71710a9b7efef95ec3ec4511bf100f12202afc10b0f5e41ee321f 539714f72d3f73d1e18963744e647bec0faba59796e37a1bcf007c6d9c652534 fe800fd27508999735b9142782e422da8ccc9612f5c67cfa3296d45b671570bb 49e60ccaf9fc9450b553d23efdc6f34aad3fa20eb41c96ea21a6e8e267f68010 782e8ef68eca66a8a34971863fa85ca59b4497be050e0bacfa1959a7959fd9ed c9978fed9225baed5c2365f16af92ee7f145c024684bde508038e7d77f8006d6 f1d4a9175c820343e68603ed03eb7bd298d87f2645a410354058bca3dd2d3012 1f01203750dd8f960dbc81e3452715975ef944be2ab6f4a1afa3a5902e32eb73 82327f97ae111bf7541597d8563360400baa93c7497c2e8c1a90eb51a45b612a 57af80a74ca2da1f6984be563844b4cea0213cbaf7de448e7b7c635a1e9c944e 9f118718e22e0c17b564dc140f5a8612c0a055ba9205c630bdb45509d3e37557 870f5f57c34e3717b74bfb06ba2a130b7999ca2481b5b2d4f2877a8ce399c274 97534d5639b3d57a67df9332f07820ab809647b1b70f56f2ea578d2ec07df69f 83510ac2354bb8fd770bfca0b3b01e09568b86ef9bcc0d7bdecb76930baa500c 4d32091ea48b9ebd1010a5300a7a26096036f5f21e07653d900d4f89e8e0bc5b e56a40833aa5055d8543f58f9a6db03d6f9f29909e967edc78c7e2d57d8c25d8 e8c13d9599d92bf24a32bd6ec0f62a750650b05e779344dc9f3c5a7452518719 9648ae63afd07a46a8e1f5fdd7f0c64c9b6f0cac4d64a4b30c29d1c904be2d71 6465b697123d7c706ab1d8ddb11a6edd6c7ab2bfe06c523c2d3681c0c7952b00 81202387cde5640d4a7da3429e142b015a96e578d009888d03820af8f81e5b8a 42848b70c21cc717c97dd9bc51a7b77f82a6095812165cb1cff11a85114bbe5e ac50991e6d28032cc949e85eb989b0ae2df4c8b2ec6c2ae96788350f21520241 3bd213b50bb724e2db2b6d261ff722ff6d5c70bd7cb9c0a409bd60b6b297ae3e 52c966b971d5bbd3446ac59ebe3da267269488e7cba201dc30521b5e52ee5c7d e3136ca5a01417d426c88698c87e84bcdb34c81708f15f168e94b8ca383d683a 088da3adcd2d895ba2d6341e1426475d1547f4932f7e77ec7f7902cc36990444 64eeb5f1a47e8494e0b996db5906c9de2f25efd64cf1df22d6c19fef671dd158 6b11c554d14ec654acc88704342bcac2028ac06bab7deaf462b4278f464a8592 9a8d16a9509b122c7a47b66f7d576639413d1ca2c436f69b65cba85870159968 6e043a31d8c7f4c5bd4988b74d36e8bf64429eea81ad8fd65f7c49e2f2621ce0 22229260bf1250e8dcb5a876910229cae79577e788b5240616d3ae5301373f2d e08d322aff53384bc00bf693609d2ea61e61d638539306e3776d5531bf96eb76 1830d07c0d7df8f564332ae37a4028cc7f1b4938708121d1f94634b9cb30f1fd e4c2df824c8ea749af408e624f77e566a1aa63b7abba4202478931cdd5637898 b764d088d8edb48bd0236cf9620e17114242f8863f5bd9d352d93fd8aef5d102 2458fa9ef36432d6c964080a77f800537a741352cedffdae5bb4d6e526ca7216 45d210972e0b22408fd0722d144609d592fbb3186e10261e920a9a0c6d2fe746 54fba8a29242ea95d2657564f73bb9c1ef99550e95dbc2444c99d9e49c07ea68 fcb748a076cf3ef2edd8d1234abe3aee88921e9b597be703ab815af8ab73fc6b 62668dd05f8ef8b481cf76ef58e9223b527ecd01b4fa506ad3b3e1f15ead54e7 db4b9ef91acbc301240e191ef58984d6dc4aaf5453bcf24d7453a61468551cf4 562fb219391c6d14fad9e10974ae3b5ed5b4d1771b99ef86d6e1ae0b597340e1 2d23b7d049f240ac6308a4d130456e35eeb3aca98e8fdd99b8bee5fa21c087a5 1d70a1277238d2b7c324127a89158bb82c8de6dac32259497d0cd1945d3c40ed 7550c427c6d766052bd64b42876e484b1d0438b96dfb8b9f6e2280f1c99e6a81 8aec1f49c1ad3e1e4cd4a4b45962f693b2589a2676495b78e4e60280ae84624f 73edb199a580749d03ced43ea6b82b55bd0aa66987dd58315967b63f0527c50a a982bf65c59048ebd004d47f872478aba9c35eae6093153ccce9c07e2387d094 24f7dd5ee7951453ccf48a088cdf6b9346469b90088bfa60356aec05055685bd 29635150f913cdd975aa6563b48bfb10346ed58b601f416b234b22081599ad6d 0f0a50d706ac1a64de4bcd19abb24a28563abe16115ac8ef0448470823a3e20b 5b3646fe99557348bd2d5b9775dacc1fb889d42b002cc36190238b1cd1fb598f 5af835ca263d92d0985b971b5888ebd4de33bb341e64f9b337d2e438ec3976e0 620be22294bc7cd6d528036937aebc3a98186119cd94ff620ef753c562dfa0d7 bbd4f717bd092feecfb9e3ce09e96af6c335bc2e6af458c1e2e37226179dc515 27c7fbc6c5422a6bf85d22099a634308ae4e33eadb8993a01b35d56dbd7e6063 72dc0b652d127ad4870b541b0fe42e3d5b8ff9ee09a5d560d27b7c7651b3f99c 25cb6816951ad8b4c26a52f3c218eea431752ebf9ac7c5615588f6bf58eb67a4 0ef100e0937147e5de121d1e28c7728a394405aef39ca2bac493ce909331f490 93a92656add9d2a8cfbfbf332c9fff9ccaa81cd895feb8fb7f4ce8c0a88c25ed 69a8fb92ef6a3ba56e142355981c5d7f0bdcd825099b71c9cc80bb83f3be0287 18aa112dfba54e909bc95346a2fdc10b4f8b727c558b0b3e393fac61c191bfee 39ef91b847b1fab8b04a5a1474269a00196d381ba150ea08347516e248b6d362 53d8309bc5c1d6d3bca9f77601d59fadfa0bdde647287a742d28096273161162 4ce7356724f3fed67a2b5db1ae6a6b605c5035e01832beb205191f3663f4a985 0ce39fcca84d40002b6074617cda22fda31231a2305614d7f708e6a681a4a600 17b0271cd0b804d0ad7864f9b097e987aae4f4f11a3fef72b153614bf89d785b 9bf243e2aa55a3014ca8feebd86408d8cbbe578741a700eb123077f82ecffb7b 555fc237cd845c34a86e53800dd232b7e839fbb1f50245c3fd526db8ac651c2b a22b69b6a684880ec38165567edadd8c69acf97400b5250958bbee9b1b551522 780eb58be0dee80df51b3dea770e72790712ee1b5d012a1b29e6921a390c236a 25b962d0f523328c715083dcd64f25323e827e9c235790b3453a2bab9c6fc6a6 be77cd7a98e43f7e7d4fc7ad50fe25fcb972fa5a7c91a54ec1276ea38e5dfb23 ab61302bd3f82ca0a2c495d8432c7004cddbcfaad5319a8b9ad00be09062106e e007a8e87a503295a8edf28b237a2480b1c22cd39c3814cf21422e49c3d6f645 cbea2ecc1751939b86f233e88f6365384e499330f25ff31f625416c34617fd34 50cc13ed35167ebecda1e458c3b8812477a9b6ca0d1001726ca460cecff7acfc d838253ced55b0cbcd52d89a882a47c7ee3d9e019b6c04b088e256640dd50133 9f6d60b4155e00f0fb94011dcaa34816fbbac62517c6aac436e9a2ae1e5d2b90 d9b1879d0fffcc21499fd5edfbca8a7a43db6bea60857652c75b6382bb3e7787 ed210a5238fdcda00191dbde1512187214e4f8931f661bfaafcbc4952a39de29 0b9bbad566ccbba9dc707b7591a397d1eda42b62a48b950e2cd5307b0245d7fd ba69e3233a9ec37d9a4775b8ce34d83a95379c923b718acec1594e2d7653f8a3 a689586ab13ed412ee157383fb296a9237deab1dca7099ac100e0a740f9de0d9 e3e72f4b64abd92d281b06bd61518a749ab9ed143d1f8908f84302830d64b45e f834bbd42e59f37eea2f4dfe03d8928a48b0c45564b1df72bd7b646cbc6fd66c 56cc237fb33634bb97ffce692a69af1b7df53db6e4ea8784c89fa8d3383d5ee5 ab32bcafe1837f1b3fef18f31c09fca1a5da129115ee3b8ee0294f21e6e7c4a9 76a8dbdd68297c3d4c4da724710b4e2959f476b99b44a2637a65c066ff76f289 84b635e35da48cc315fb0d3847a26f023a1559ddc806cda057790a38b787444a 2c6ace85172484e1de416222caf2b7c37af1e162bcc1771d6af34363c1536f37 eecfe7bc1ad184a938fc8410a9fa4f196f7a739fccce7bff5f19e9073a997dfa 045c069c5d4d3587c68c38b05c128f18c83039f3724de5cc849cccce09f9a3fa 622ba69948c896fb4c41c8782a97ab7d42c9461bd533e7ff90c807115ec61a2c d077889cc7ef69507ad7555053e91c75c5806ce6be573670884e1355c96518fa 05f55e2aef45ea12985a9a8d81c511d7b4d13254cb3c066ba550cf590cb25d0b 1c60c142c84ebb27ef9614ac8ca8efe24dce0e1595a6efb3c84edd5f45acb28d 97d213628ad2e66e822bc1c51a8ac38568b29965d2b7439ce0396a8fdacef632 56e3e3edd7fa6f7caecf5d1ff3125264234108ba101fa82fcea8ea1a45dd790a dc2e66aacaf8e4bbb48e3096d2a68fc9da5efc41c6ec24a71898240fd914c68f 4ed4a5c544d99628bc6828178e687f3e292de9dd27be4fba96b3cf3c847051ea 40d1afdc95d0d8be35d0680e960f9cae3513d2d7430cf755a66fa7355a74d0ae 336641cefce1e9225bf6bd0ba9b96bd5bb1aaa80d1e1b621e801fbf75ee0d430 3296636d5e6f69e37e8cb7223f1a2694a789d58f706645f044f462801c8568d2 5e75790976eba2bf85e7fff477d1ec61579b03ced3a8f66a1d42276d38ced7e9 3f2339fe72c8cde57e449b9099c3b48e8c17ba9fbbce52b7049da1ae1a2a1321 3e1c979537ddb3d469954d7f3892fe94d8c25018c5617f03f10d0c8d620d59ef 1009b8d201f7c9daab37c87dad4dfcb9b248d98a2da707cac3ed1b1d75269f0b 6050f2e7affea42c6b2f592c98e5998d2dfc9be2487527a59a8771e680f34a70 a8b8011e20433b07cd35acbd012ae3785a4dba09738901a3c029f68273f6711b afe59fe12d29b9cc09cb2c29a52bfc4e92125e429658036c64a64edd397ee09e b0f7153f367170016412550a6988aa36f5477f3e93e465b9507f113fc1d10896 42b307f032a5d98e6e946cb0ce0e9fa7296246dc37db572ca13a6e00f6721914 41074e96b01d4afa594d23d77014dd53cab1a67ffc0abeb69c38ef22f5435988 10df6b96c161a8ebc7b1da6b389ef4eed3bdaf71532b4ac1685215153f3d55a3 f17a858dee103f090a7a71440ff1888c22eb29d362e5cbc1b0b30c996204c8dd 9aaf933df12e953bf1cb1b58eb3f6adca1e4834bafac243cf6f3162087f6f580 2578a6230b2f9a26759e5c1d1101e7495ef04481d380a476a3d2b6006b235da8 4f7fae2c53a2f818f8ee5e617e91b83baf52bd63b4c08d58e65d48236d189230 da3c647245edd9d70b09eee9bf840bafa0ab9214f054233c402ebda955caefbb 83b7704e84601e8bbb3a271cdc10ea4b810a87dfbf21fd7d8444602e6dd94b9a 0abce7d04094099e8f485dc4ea8b5b207c5580bc2c5cc741484739ac6d1c309f a00a6fa8eb5c8acf076d046d52871fdcbcf52e35bb7144b008f9bccb69d5157f edeccf809c531d891638ce39dc62ccf0ae052e0eb6dfab76d79c9228df503316 8bd3e5d0a381e743e73e11f1540a5c96b39d32d940b9d71b302a0542d22db1de f3857eac99e19a043d781b0e95f63c41ff7b3c9439edde1605f35661a06dd5e5 b179b4db2946409469dfdfa99e1411f5929c684bd6e17030d463bbde81824c97 f4bb4d39932708de95d555217f8f2cfd3b1dd252717259e48765d5299fea5ee1 bb47303850a9f3cb4b3a8d5dcbad88ce19c934d9c4d6953187402951f4d519de 449b905a069eccdbaaf133f2469f742a32b5e14aa4debcf4f2b690b58ffe836b 34e385b74c10b6e3120134b02732435f83c72db058ecfb2f2046d089755ad4ad 14474ca779506b021d6116bd853f7e9b88e55d449edd0f5b5affe7deefa62161 da02f912c37ae88a8d741bea5ac1a457524515ea9fb7ee28014253f1d26de961 1001df6d8012c6e13b2dcf1816a7edb8bd9f49d93d13a7d005042156e0f9f80f ef8e6eefde253c0cb9b1c5ed6056763b700c55cb38d827c170f0b80fb8473292 2261e1264174c0e2ee8f84bb68036bda0055ba0355a0d419728f885b909fc468 bee9382bf9eba2fa7e85700ad6aca179824d516cac0c61ae31505e62928b9232 7400625c42b2f0395102dbb5943edc0c36af21fd0b15cefb9630060d1f2e8a53 f325b7bb548153662b5a97a23405d7482cd68e9ec9f4e47c48ab9b186e9021a9 9ee41842480b872278af32b68bb98de3265931bb17a209b9de25c4eea0789ebd 30ed057f019abfad44b74e59067a54f1cf218327597e870718c9636a19d17ef8 9c48310a8f965b7b1a78b5519b02ab99db7bc4b83dad3c9d5fcb4cd362570b4c b1b40ceebcecfca711e7bf946d68c4854d8d768438beeadfae9964c3954f2a50 925a7b8f0a2fdd972409495e06525e1f937947242410785a4e6501be52d1a569 8dbad6a27845e480a55aba798939266ca1709700484b20ec55f33796270c5645 f2e3819e05ebbce537d31d9b2f013fd7930b84d91af0e79e5e0b25fcaa5d0ece bba4bd3ad6e44d5738bed6ebea0086de752713d409756895fdef5add92fd8c7b 64884b9cd2cc05781f295a1935de579444607e9daef575fa6ddf330e53f01ca9 7a0d3cf38def845faca18e303a724f6678ded00c1c47be4387050b571d1e8554 2e87460bf8019ccefe6d712e0e3eb766770d35f19c31f8b51403736bf7211a4f e213b59301e4cd8ad30e35f3c193c76ef6a1274e4c985cfac813c20e4a3f4aad 9c07af591bb6d577a83fcf7833f5dbe94cb5307fa63720d352767c4b1eeae313 07e89dc2bab1b483eb07f77e4bda9cce2acb882914284aea4af53f795cbe2651 a538264698d50c23026662a7c0d7cedf45355d0d2981b5f47390c3e452becb05 2aac9236e38d25a8fb47f9eb41c810fb20bed74105bc9cff98bff7526462572c f709a8b85ae65462769d6055e0ae0f1a539622d44ef3ec8fa1565017e9f763c4 5f27eb5cbf6b926a414bfd9f4f68fa06efa2237946f5b43c1e2ddf424a4f7d3f 979e3f078b17033fd06e1af40bded037b2e010b6bd88a9ebcc08f845df34ad63 48e9b3e7319bc1a9ef395078e0172eb16bc072a3b80ca90d93b44df26ffbc7dc 56ccda530df51e1bd71d0bb2567e5e858d217b840f50e66af8547d3bfd23c762 90ee017b1c5ea07abe4ca35416ea54f9eeb5605bafea1a2d687cc69b714fd9e7 d28b8a141003899d0a49da86c24d4eea3e2e636a9a24934ab8174a05043ad561 2cef38bda772063f291d090e8f02773bdf759271c5aefdd87216928a6b957091 2d47446656b5df45b59068094d133c666676ee111a7a3665f57dbf118e32313c e82715601fb2c000389aa526b2b17f8eddfd010da161593db87e352a00d0f915 eda062ceb153e152cece4224726147561ceeaa7c598a822105df2df920e56892 ae14d7f70a7702288d260a5cead85406cd60d7b0c748145ae8a3f812fbc4f477 fdc7313fcd8856040059024957a887a3a4c0c59596a7b2c0e05827ac99986c38 4e11aae54e9bb677649d26616737d13193ec68407c15698b3ee4328c30cf9ef1 4bd224732c9942dbf562a766c210b6d293c921f9fbae334a9b4e3bd564fc2157 27ca7deda1ed497ea12943dde898bb99ca9b8306e31403127c535b9dc67b2843 4f2ec62814e9db190af06d2cca1ac54722a26888351f7bea00278f474ecb6e2a acb8c9280e3904db90b3edb54d79a98fc8c67fd07abfff0ca7b19ce2f75b9a0f ee4fb17464144a9533d99feef50c4837d4df5e92dc77d8d023256260febc3059 8dd3c85c2a9d0e1409c5bc08e241740cfb7b9d9955f37e4458aee3405f9d4934 c46df9ee8205f866e8f82b0ccecdaa727519739d5cc848c5a97934c2898a3620 dcc54f29eec550a826c058cb820b98cb67429ab6b96ef0dd3fe2aefdf0f17138 937d7c7d0ea0e095eb00ce22d64cbfdfc5806c30f8a22147d474ea597a1fe3ab 34f2d5c316f5a8d980722ac7e4e467fd7e257f1b6cc418d288234e33d19eaf98 d61fea3a3078f5627f4ae964d444a7d5cad6f7495d25690ef8ea344e9234462a 0e9e672483407de9d032015df3218a24b071eda183173d839896327d24dd594c 327bd31105ded3b3300a516917d55dae63a666958dfe4702d4bacc15658f99df 89f37867df66849378b97108fc2596185d438e0a86deff266931c89804758c1d 3d70e5f00b5fea3cf8589e2c61e33af8503962551baf6509b7dec49643c6e74e dddff48a2c4b8f1b3697cf5ff29b8460ea82e302d2fbd1d0ce113fbadad36113 54e5cd29b1b0840ddb253876225728cdaa820847423a4e1f0aa99b39ff1c615c 617a855952e8214191ff2a2e3d21b729ffb8836c59d4177fc2e688e978c0c77f 5b0ff6b34ea93829140c22470795668ace061a0376ed36ac098204e10d8a2f4c 90443e5435b2a534d55905221c9ad2d1d86057ad46e3fbb96714574a8d7e8f39 ee0db93ffec6752fdc7d3219a3d444b1bfc42c7717af0fd5f041eaefe5723ba8 e1ff355b46068eccac77bee3e88af3be5723be6ebaf74cfaf67a1f943428f228 9ca01793883570fc03b2c03f4fed46e8d3a67b16acfdb8c98471c56850fbd77a 1f44a82919212c6b78ddd5ba3b6fe044210cbf2a76b5dbeeef8c26a7f44cfed9 e9f1db9536cbd505adf8a3504aeb5749f28071801170fb684a8e6866a455dcc2 16224b78b20bf5cb341428770d44cb63a8c3b3169d4546952c23c5dec272a8d2 0bc49c0ac02d0df517172fd6a4f67a7aea41e8e3d196e77b0ad3073dcdfd4f55 48500ed28dc6aad13aa58a6d2496079a816c1c1a71c8b6a08cd80c86a5e7cdfa 77829c3bfb0b9796d4d67a2373e57777f1602b8fb959c0c0c4a3e3d8f858d30d cd4c3018b107ac31f5ee244e4c8551cb772874f3ceaf2b2accbbfe8595003ad4 6ef9ada8c6452429cea23a5a3041e3b466849166394cf032b2fe7b802ede474f 9074a4fc7c3b32ee77916da4d428bca446845ed5e3f6ee23e78c7beff1bb42ee 65ea735d1b066e25582a556a8ad6ca80544f980e99e9fdb0444f849e76dde3b4 de7335d4c4842e7a7fec5f842a4110922e746174c75b8cc75feb19f70cbf5098 fd15abdb2ef28649192023b9c5019b4380305ef6623c4764dc8753d5c00127b5 1a0d6ed40aab1d3a82cb89b2600b704831ee540a12c987687eb703a8e857a3d0 ce97678455ef4ec1684ab4f924ac8f367f2629fe25bb4916ae2af7eea5b11b85 20020b21b79980a304805e5186fad1f6deb9d35b2063248e5092fe51945ffc62 50e6b265ba5046f1dae57abe84a1bbd3d78748298ab3610e868141cd7198fefb 39dacb4e44ed71a22880e710d80a088f2a0ba3f38850bfeebb12cf8d966da701 125 57641f087fc32435d97b4c608084edceb4fef72710031819571c68ab20ed3e01a751d8846efca33cf77a257a9245b2dd217a627683898d1c527007c4c9f2250b8fae55e0d37e9cfd97efbcaa48f7407dbd52e2257a1b501d1ee8a9088d45650e10d4fbcbf95274ce7de048506465cc990f878ccc7112eefa90334195c859bf066b1167722628d30b0b0c467ed16d98cecf39bdf78984115a5ad25f5df4a1f50d384d0595b7a48f6c713ebc4e128aa3f9034e470793f1618831a96a1a3e06570beee84018358b03b66e56cb4fcee0306043683466cacb152516c01142d56cfa0c3ad7e30af1291d2e63ee864c3a0948a1f28613dce51acdce88f3ada73719ca0e80686c8e9a05a40aac9b7940da2eef5b4afc3c2e22ce481982cc6b2d60574d067ee0b18f526ef3de2282659b46f57ef2662b4fe766125b098cd3b7e764f78a035c277520e637f4da2dd677a29b2dd82899b5c5cbed6659457b62261435ff9f093570672c750ac293ac48bf3d248b14f52366b8f3632d8dee37b5b78111747e09331bb6b3a930338ceed708e21a8066bbda232d68eb53e1c6747f2a9335cb550fbb887f02bcd7c89cab0b2d66e13c8e5670d198a3ff0012b6262dbcb7fccd8c0cd6e927a489e73f67789c4eb4f950552d56af8d2446fdc1663e06ace879ef850cd16e42429741a4c55e7f587c86fb2b7bb777ccaa0f22d738b36817c33fa15c06306eacdafe469f4487f23615aa4777951f81a2cb14fa682c3aa802c6ed8bdd0e1c8bd5b14d8c5f90376633c91c3fda7a541b88ad935ef3d5feff4be731041b0dbd9b899813415bd610ecaaca4c845f448d5871a986db1373beeb94d82de04f0670b064332d84963e00d584fd2002cb890fc5968178c4d5a87b7dd25fb9446b0ddca05c6a2aeb6e1a62cd12c4671372aed24a6fa2641a677ee244b704f9626e075f2df67d86b9b4561dcdcdafb2a411e9b02b6eb873a30fe0604ea6d5bdd327072b88a087bd258546a250f7d8105fb7b5dc7e804cde9f04b6a1ccfb2c0255b9029372ef1efed13ba34dd85655543c8cbd39e89de58f17ab4ebf1dc961cd58040b832ba1341dd643c0a1e5d9b0271daea3c7ae6be78f46ae62a2d6321e76752b0c883542b282770e62155e01a1048671c8a5db1068e03639628140981b5d9b7205519b73495ffe6d094d056f5806d42459bc62427b4096ea20b7b43412a2c9c70d6a0388c3e2bd6cbc569428c1ec0707fe5cd85b71bcc2baa4167b6b0ad8bb700d48be4eaac96bc555219fb68bd279337fcc804f24ecf3c443dd7d221fe8c7bd0274ae470e4104a5d2f8c4cbbabe68dbc9b875c27e2f8fd777e23c9e2a9fb59302945746b5525677424db8bb39130892ead62d9ed97e3a5366c7f913428fdc3802a6866cb2c8109e5a271ff4bdc92cb8fc2f6b69f5c520193b4e543279bde37c034efef28a1c40baf1a0787434388d9084816d5543c20ec7fba6ddae229f98180069cd7f40078c452d895b1e53620bf0db99663c4060b6c7f0823c020b8a49ec0e9f4355876aa2505f9f263a9aa45d90e5d0fdb982fdf980cf2bd7573446f9630b1c97a86f62230d4012db97db4354e134026638a719368dcdee25110766e85a0999af6513114770d8230e6e4b1de0faf17a2ec38ca3a80e731f1b0dd67df9620e84b3b955ab692b53b53be4b936b658cc1ddb99b34e563ae15d6eb671c7b42a068745c9456e56362a5c66873b2a5ca56b35aed8abe5d1502ea2c02125e22afa0142d05230e4f032b240aa9e30bb4136022d5cb93a233d1eaca2ff091be30c4c088a2df2e4fe40934f27ab8cab159bc409bfec20762ef11fb0b72f6a5454a00202f6f9a31b8609204d8d6624b935864cd510d764ff226fd0528c9f019d6c806c0f7e7eaab344c9ff3a886e75efce0b6e17670520ebc3f36f079871e5a238bff5010c20b6a9c10e382e13ef57856c28cfe31c49000e10ff204d7055f61639cf1309d167beb78a3577575183ee9ec65effa40518ea01639faf45a0612c8db4944b016aa80cf52f87948ce4ff7a14530efaaecd555fc48a600c4f6896e5eb71fb87007f479e96d594efa26fea6968cdebdc4ead32b4e25dd1980cadc5f318756c5e02128c169773798e01dbe8dd00844da21bf1fc8a6a0312226232c44aa51feb8a0c940e425593f9f2e64aa5c985cc505625ac72405485e2b3c2a542e938a8c1b602ab21b31c0b8f5c8701615ae7d0d6e4f950c4aee097df360e740f69cb53b87c0c361f665985caac51095e28f9effaa89c17348c125d02ab9261ee852a1166bd079f613f72a71ec17d2b95f2f37ff4dddbdee7c9e1bc9f662378f7f7d66b1fdb0825e584636ec22b2689049033d1561658f625d492ae65099a662bc9e5e3eb8f0bb1d966bca19793a610c9c4b1025636e496e19fddf178b217fb3d4d71fa8839039874c9f91ccc92154cd08eb0f77b6479179768b223dfd1107bc95fcc16a59d0621d8041d574b3f74dc3e96954d25349cf4604c2c0dfaeec80c0280d04e4e01034bb55ed7f016c28650af56d854c714178acc1b82028b7e0479b6afb1c07e62063b2b8e4b87b899e05e1534e4fa3c96ce62c4d27ce76985731747b2b0e95015050b971967245d5a5e81ee76d7d446c424a33990c69255fec1b96fb1790336d10cb0e5110abf9eecbd6d0c86631c9a046c4cc54c2986ded357ac413b50886d4306e36c7e60227a90b6625afcc1935439db3de6eb93a469317ecd4ee6af318462074b3bdc950e7932a733acba6fad9bfe4c408e1d4be4c915793c993100d14735007961e41bc6f8b90e057b9e64a27ffa54e631d8e1675799c34d8c283206cb1906dc02084b8c13a9280966f2291c002d28ceea7120b35aa61e45981e88ac57480f3f5970c4ce4b13d01f8e403525b15d097b6ea35cdbaf407b674b190ea4b46d0d6b9633b6fc382fffc00a546a029708ccb43e119ba9160d7b151848c11349b50da8baabb3ba03a81bef87a8b8ccaf8993f3a6ad1eebd486b9e5f61bf614c98e0e08bd67342091485e691234cb91884dd0b135942fa1eaac0cc4170d83d819eb004dfea136dc62e8fa8b5c767e1197cf73c6f0f66ad8f770d6faab71464445900bf028aa6cc4423e133974f6760c9c5dbbc265b7ce0a0d6b1a1c6cf73914ad800b39f9c871ccdc3821dffef96de9709bd9e14d508286a4b8d854adbbae6a36380a76bcd3be5f3f8c19515ff42451f597e242ffbc8fc16ebe295e13e117f32ddf00968f05222384b3933c4a67dc9f8093b6bc6575150b8e708393ce2c055fd61a07490773e765a05c1c5d415faea79545f1f531343ed84dd421f12a66fd16c14e0e48ef5bfee5e0bfe83aaafce65bd168209da10601e0d2f45af64c1e0ae9309c0aa301d203c500458cf63ca5bd0a524828a68367e1727229ef462d5e876a2877065142b3a317763b29eed8563e3c8694775a2117efd39404bf506d2b77bfd9eb05e85e92292407e32fb5f0649dba979c923945134b2459571648d588a0c8f8650769bec01f0eb6405be28ddc493a68164e688839a0ace810423eea5bb9d3c9cd0317426830c099ce0e7e65823cddfdad0e9d3071d877be8fe2175088ab0bce1c0e93fa53ff7dfb3dbd441834b4fe5eaa122c61115d189d1f268b0cf97c38d25f04e1aa0c1c47835708597bd3c9a2ecc4963e58bd22f4df5ad9c477fc4620aff30d631d80c2fa0be5cb67426532a289f7c93f4b326f91a0f200523852c2c34dec02b720508bd5df28f4e8e01a8e6d82c7417ba1944b30e68adb10bc03e3e325c80a7d0f373ceec9b6b659f973ec70f092a3ec3b1e078d6bf5804edeb98d4de4ec05676368cddfe9ef8d2cec608465e8551ae3b6d8e23ff3262182d36d2f65a822076b49ff4db7af9f56d34052483638d87b027903fd287711a44d8fb87bcb26c30e3d1c3e82f385e4d716fb0e1a7d329bdd06475766fd320ca4a6382bd3c2928d0c817a0f0be38503f7c4c73b21081a19b6f1acab268bb658f01c4bf470d7478f0a7a1821f6102f2c96e299b0b83387758f776fca8283edfaa0292bd5c5d8919308cce402cf866116bcc080e9c18f05247344f0e0695981f1f2adbd6dd4ffb54303957d6474f3cb968272b4c8cdc65f3c356a9b97eacbd9ef2a0752e07454bdc90ae69279030c2a36813f15bc23595349d2de0d8e2956325e0a4b69f04c7d38ef01ed3afcea841373f72bff8f334cad9aa2ca2ccf0fb268d1d0495e4bfcd1e30106ab251f9cb9533779ed47637606ee9f62946492d9c25a1768daf683e4c304eb0691fe06b84b96d7b05831e83e2ea9c4449723ecae711d58d381f4987bf8e6d401427325e6827a366d0f8b50a68689f5108ea332c20f7128e67cd1192e1631a00b99038d5da9ccc0c6be18a00f99057a03c32db69c01f8771f7a8ece1e54675f07f3438a6dac4b4b033a024a4490fe16d9c37529d2f4d9e115731b5fdcbe2822051eb3f81836bb835eee82ac761f9b37820d6f4bc562d6491f3e078a6294b01c006133141f93f47fac1a91ea6be8942a96034675b9a0b7bee72a6462eb79a9540531f8a63261e1fc3907828a0418572f980b39c817df7a06f4db472154bb7035028ffb6e2c90c3f43482d30ec49cdfd1ddf0fcb3ba824cf43d6b9db2adbfd3db030ba6bf3c79d4e26e4c80d6b74ec623af820458344790d6966cc076b32cfa9c030f9b58f9b7037a792d87a82064147121e3fabe6ecd2f06f104db55c7d86d6a0f1ccca26a912adaa30e7d9ddeb6f88608683e6c110b84288c958f93451165bb01385588a48e343b6aa8054e2fc7ec8bd540f1da332515ddc092a948dcd6e6ae0ed97c3ea3a9b5c89499b780c4d49103561a57c4826e6c1a5ab4cbaea1c9cbdb0b85f9342f696886e3610eff3024ffe6b3b5984631aecf82b3ba6515a4cc8c7609db9470fb36688fce628423cfadf7c80e03e7928d3133726bd957309056907d0ed37f93fc6a8e71b02fbc920caf3426c72ba3044f7340e2de42edd6b0149d9d05bf31720fee26ddf8ecd626a24bd3f9fbc3680368f4827659505bb046c4d5230419f51c2eabfd2120aeba6d99efe4745276f48ad785a4d79d8c3f911757047102798aba9ca3ac98478537226bf817c24f451403301290f595b755e164be73bc0a9c74ffd79175082dc0b302c06841b804e2d7b40e6978426a4be0306e7ba1ab0199ddcff443d58d273f2cf5962de9e10918cf5177c82276c8efcf3c64bbef3e062aa10bce6495a8d5e00e3b6be909817e93c4bdea1ac6968b354425711a155802fa7a936fcf444398e23648222f88df83fde8426d10903515e0c5fad27ae31702bbafdb57963b607cfde7885394bd49b4e5f02936bb919deaa2a5ef506459e107af5244973c168e6c05b1380e0f996815c1cb69fe093cabaf1b724b2dab71120ba242df6a6fa35343168f2fd2cc720bd9f739b25a7a643b00ac4f3d20adb2be0a7d189f62bf643ca339daae59f3ff3097051252ed6fa83c2ebe177357ee6d8a01de3f2cfc2f74ae4f8d9529171dc4830f6da960e8422dd6e1dc381c1ce70a6408a3279e201e1aa3162301c3e00275a77cb5c9ea93b8eeecd126d33baa0e8621014615b1c5b6064a89dcc1a42246099942cc26ecdf932a267a0590117defb52b0536dcca2cae6f9000a7d5893d28b4214b064ef2a14a7427c839df3e78ea140e09bee5c61e6bf87401218dad6f09f2d422813307417774c2d336c96b92f30cbe0e5d326aa10e0cfadddd93cbae267c2e5230b88992fd2d3b0fb97a8ce5e1b68f06a9d61ac282344ee15806a221a4131a42f9e35f842e23500d149e890cf088170e5ee5b4eb451750e1ae8a842b0b7afcfd7ac7153f481adf1f4cd95324a55e0007d3743ba32240f0b5042d8cba0dc99ac63cd7831884b39886d6b8df89542ef103c096dc6189f5f1700b4b304cfa1978d8a63f9673d24fcd0533c35abb8138870a410af74486335435781b1f380927ff47b7fcc988c2346af3f66eec255b1c5606f6b4da52ad21278d374c958d006b088739815161fa68f3a83a9b6a11fdda3a06efa2fcee3501823e4fa0eb2982fa15aeeb05acab0c72d2b9a699ba7d7ef6c50c24f3ab570b841b00d0482927279e6e68034faf77b22643a30114eae93a1c6f09565a07856bfbd0599be8239c2516d70a2afcaf3c82686e56e0a141f5e78d290ec502cda77cd6c61f35d1f13cb53a189a2e4e430537d3217c912c8b34f18f930ecf04fb131cc60f0c8a88c31965a87bc338f63ad01fcdcc711c3da1f31c26d004262777a2261e1e36c8fb8252635731d5bd61ab4e2a3fb536dc5945ce2454050e933c1b781d5a2c47f631cdf04e60feb3d7f8d967d20b4e66156644f21b369b039da4d0b4793141a6ebf0b5d2751b3946ca75e69c08366f1ebce41f75fff91c0432b142f911801faa9d55e81b0b6db8b3282cbc3be3cafa026344181b6434420c2556a54d30ea0391bd43fa8703f778b5ef93f40902f32cdc7129346c17b50c0be82648ac81fffb01cf9e6da19208b3193a91954954ee158cbd002c70092d8e0ad6c93d747cad97d93f724caebad1033c711c33892b61c7e8af1c78f3b40d720bc566b282e9af16fe91a16ab9b21115ae81a33337a65ab92417aaada4f26e2902383395952c2355b49fb24a4412df68ec25ed43a340f60426aad3dedcb93a4707166ff52e60607e8235637ee58c69824255ccea20e63812eb3fa139a79ebb69097aa50758d7edccf565712438e85485af6f0ab65e5d49aba5e06aadea73c367002a9d71fe657a4c15fdb013ff457b41325e65156d1034ccd51261610b1e897a0debf1bef6aaf52b1d422ea6a3355754a5321958a624697c28ee098a27a5e9da07442724cdd25826dba4adda17c309075ff152a0a34ebfd47b56ea3c72a0484c06137713075542b62d34463245aa0f82bd9589fea69ec966442d097e48d865090e96bab84d33f96fdb23899d6711524adaadeb30f987050ff311c10d936bc1d30032708ce11ce8377644046990f1530a41d6627c35b5e09845771d200a51a4260cf63449d5c85dab260bd3676e8705bc46166508f25be0501a51eaa6feebd9d7039864f8ce539d94f6dcc7e4406b74ade564655a57cfaf44c9db7898efc5d377028f149c1c4473ea84f105fa0bbed3e2dae5042db7db30ce16814f913fdba58c0485c3cc1846582a39fda28ec0e8b489dc02e0ee0dfb55e2df4e81407ce13fc60ba7e4c749655675094143d113c442725fcae7bb99d7ae500edf1f45d0fd15770b4c5484438013777ccd1ec182480bc58d35e5367ec85d2a7e1b9824d815f817019ddf27eaec92bae174a330a9bb596066ae06a0c240f25b32831e7aeb24287a02563d2fc01804d63ca7f84654bc35801fdfa34b5f2a119ec07eedb1e057c0230af8f37fbbf91348e28f9857a3fdb586e506dcfa1f115c87488a77dc8066c6c60aa78e559d02674d05bca5cd255b9841019b301b5b9789a03d019144f2137ede0f61e17b65fbcd75f55ba237145f4ee4893a551b65268c34610e7b05b7e1407a0d6efe1963d9a3f28268e428d3d50b4f89c0c8998082e6ce5fdf9222f1ce74730d7fd49b07e6dda0cf60d2014e1ea92da1030869257f3021e23c3e8389eb27ca0d133b6cfcad729f3bfd3084164e4cd865430793f32ec6d1d1ef28e94bf069f40f3fbc65a071a846353f3d9d7ca065ff67db159bf8a91bc893d888f09aeb4d650c78a7300db7906c9112f9f6e56143bb6608f655dc94de1780714843548a1e1a0afba9588a40726f22ccca2194f6d73dd87b929672ff0c887e3fc8e9608d4bae036345480db48ff47122a4f0ed4863748cb0c3f339ae46cd83e6dd72c95907e1077c6d43b3ec163eb8c80bc9e9c6846e6af823d385e709a7e72514f6e98bbf53041e346abf05dedc57348577278f9df4965d915cfcc854eb547e0997c6fb20d101e09cfd860f7d1de5100a90f89fc50e7a5eecfdcac785c6a7ded1c4e93f70ea08661d2193e7e54c9a97c2115ed696f3ae0e1c6651545c47d5bb3fc0d9e3875d0cb239f4f21b1781180dcae58f31dcbd3fe81f66012b67ca379e3fb4784a712e00e25c0a99808928830217db56034fa814ba61029c5ee6fe86e5c4a862d687e709e519703b19c9274685477cee8f4acc53a6b03c38f8efddea59ad7c69354ff506053a39727b4be66c9fc262765395e94b0a8540f266f31dd884f3afcb33d0000cb1a48421399a4243c70a920d23ca1cd94d4b28a128ac9facd30ced3616a538072a4913e4c1ac1d8d5eb35a7d7a3b5c7421fc3e3c18d10967312fe89520d0f60a05a8e2917b6ebade72ba9b81cc5b53bd7dff63639c7f084f7b5590ab2eb5700739a7c716c7894e419e4bacc9e95fd25c5e52c35621c93cc3159276edc8c2880b6a42f43d2651d3f3999d231d387cab89aa3104afb1561d52bc4800f76bc5b90a4d75eda5e3d25f6691ec70842ea94a0d939c05f39f6a804b6a90838042db8d0f95ecf749044928903eec35528a94d9e62a5cf141b0ae85d7f4b9673439d99b0704a5c6943724154d7454f13e6b4edb77de82725c009fccac878f0e6bc22c8f0e0d0be117f78a726c360e94f4635da3991d5e6c0ec2fdd48bc691fd4c2006d1015c48fdd20f9aad88a100b227e9cac9e51962c7438cf105f7e93b732a6b244505ccb2301487e9d14fdd9e9100a7c10167e8a13098f7a8efcf07862b99fea1af007b1a6288a3e042cf515a169637da4c8bc46762c2135bd79a08c9a4a82d96870d946df5869fa72114a2b3513726d70006bfe7d877ae3f4623d445769ad49e450f7c7b84aef1f450f4eea232e4370a4af1ccd3fdb5143d046b31dddcc82177690ab4ba483f262e59b3312d1676bb4a1052cc9c708d873d0db5a0fe0008839be90a2bc1abaa6adee4f9578aad74ec5ce8ede57050629ee5035fd605013c2668c10d94858e5165b2eb1deca53f1f23d6b252eda9b3bf0a993bda229f684e2fe98e03fc92576518840888a3c078f7d78fb59b74e56203239634fe1c6e077c49e9f90d766e8e2dd693b45138c631123ce72de9d06ccc146aeec5b64c6ea08781964f0ec2a56ab7c5cd4523f23aa771c1ece525c132295a204263dd38614eed0c3d8d08675b92b17292315deb991e93b1b51ad10e87168b3baeb3291cab0959a3bdfa01180eeeaafc258459b94c0dc53db1b2cb39e92de190672d0d29c38a95ee6dfd06e73cd66121e26f63fb98ea08b7dcdbc539df98bdded17e0b4d0d02d669e7100d0a0006c94835cf3788e4f215329eaf390233fe45c0e03186d2dee0f9ffe45c0caa406144f54d848578c0977c94c3e4919eb97fee102122dfdd6eab55157c130ef8d6511ce29dc1bcea87b1dd040c9f52798d8a063847d1a6a958270b850f3905261f538b66088fa29dd5e5b6598425d77e03158e0a1721099683fdbd424cfe01977f8c97f8d1ef3b3911b40f842f01f9c5e17fb4adefd68b590a2d33ec694f0e703068159b1ffeaf52813fc3524a5271127c4387f0e5b25aa4ec04e4fc88f00d7cfe81367e2398642c8b32eb6cb3e816d62fa1915b48c6fe3ebb7070e5470f0ae47c58b810101bc366efb264b2db1e31a5735a7ad0b593ec615159332a48ae0968d69ebd226ababb07b60b5708906ac5476e393fc1ca6277556e1b7f55147401a8457ac995d80999b51b73a2ca97114da0f68645c3a22d7d30ef74186dec9f0471064ea0c7baaeb52f4c404825c609cec68c414fc8c51ccfbc8a01c5e95e4a0118d90f527ec4091450022d2a7637aff410afbcb52cd67fd9f221e60a779a6b0411662304535d1ead4dd919978119a07573cdd4cbde78de826062534c9f62c50b0ed6c7cb57eb9d3981a9d95790aab429a928be4ecf9cf17424036c7c1e3ed706549a9d89bb7463519122af6d479e51e2fd2c2a017351888149643c4c4a87e0019d75e66878288edcf96d6d261b0309f18f2229ac152ecb29121aa1c7b66b4e07a0bdfdf8c3a1b4a7b695d430b63e996e57a66f4160572e2552a51b6c316196045d27527b98b5c1a0f98da697b78763bbffab5e9aab27d856b664e4ae85f5f20d0eab8a08daa2022a60367cd5a10d649631bce699744734dcb071afbf9533b30c592c9cc7003dbba26a48cb9818a0a0b89fb29a7dac101eb1597d3bb013da090b14594bb1079ebef05b8e8bbc0b0174994ce36bfd2b6e5ba5f18cb773bf85e302965f681b158b14383eb2b2d4e1e1bb5470f901a97bf5529fd337141af6cce001f9bca908c567a5bf34036b90ede75f21e9fbaa459a6fb09e7a0a5e5594576105d401c488f6533dd068f4cbf73bb5ac45a1cacc5dca3347539c10ae483978180571eee0d580a6b7f5ff8207aaa9b4fe6415bd0ebfa64b68459e7c1e09d36e480da11aa31ecf968231db5c68cc697b5525a8209d950a48c44251c59c2369cf3606d9bd8a1de0acbe473a4219a634b9a5b673e931ee55430d85f3a6090d00d66101d8cc94ab4701e3258d01339ad1205e2266f5f03d2c7d68037800808059cd3c00846b44b2711b3666bf5d146f66a2e9d14b6b288e321979bd8465e92dfed32b0525a976b427693ff8cb9d1b305d44eea29105a7f57319521fac153283465e1f006025d8bd41ec8242b91148cca70c738223c7734cb55932b7b424862ed351b5015ded45da2cbf7106b6e8da062e758bd96963b40b109e4eff93bb7fe64598e7038ea8ef500e95622b3a899fec2e1913d67bd15a77bfbbe24b4dece0aeed9ed808221ceed7311ea139e358a0043d67ffbdd21635bd642577a2cda9d454c5048000f29b926b25597d12202db7d1731a6d0506b2d94c2125c4d192a1c2f2c7c41709a5a536676ddce3190abce8cbf7c6da0910fa108edac3684d43dc9207cdbbfd04b1466e0d126468a70deb53094c990ab470313178a66b21bcbd293ff7a231a80c197d7ad65f2411012e5fc021088a229e3f2ab695431593e92b9954b52fee1901af6579d6eef8fedac4f256ba801f56472972cf2aa0a3ca2f306786dc874d520ec9986f159ebcfb44cbb8bb27c9f6338fcbf39747d933bce287461bb2b7ee8603128f03c8a775ac66a7f5bc86adc659f928364fc3033523975d266a39d15f2c05b67ab089ec267cb11fb274c3649fb4ae39759e260483d61b7eee7f014a97c606f031374fe314b96480def9c2e01a9d0af5aea7acc35ae0425e62f7c7d54026072dd9dc5abc8eba0a0c8edc1783a2cf14d85736f428bcefc59dadd7f58970620461fc683ac5624225f282f6d4423dcfc9f4be8bdd9b31f0fa6fc061c99dbc460b3266a8bf71b74afdd0d591cc0467fb5ad96f2daea5b30ff5c51a3911d200270fd5904d2f7fe501bb677ab1711ef52bbcc837246a6138067f2086e795decac40e17e947d486a1a29353ded9a175a450802c8b59defadd18968551906c3bfb5006ef550edbb79c8e53a6c393e87aa6944f7acc398e2e781580b00e032c772df10dc0b68440c23ea342cb532ade264fdaa08772fe7fe81f05fbe09989863022bd0653bb126c284aa5fc1d20c5f633b584fe1f9b72d0e6eb8982523183759dd99e0a5c937cf560e507adb09f1db1d8f204b0d2faf36044f2b8a95c259a80d3c3db03a84351b1e57590ea14d3101cad0440328beea0b5e68cd40ce95dbe7d44aa180cb81e26b51bbcf369830a333341e48f92b28c2645cb0339a08767a5569d21670da6cd651ea41b810ad858f79ec46a0541d26a2e34c9362611fa8e79f4193bb70e4771bc8ce22b902ab7731b2890a316a66a05e7604fbb6d8597f8066b11f655063e85c4537675c4af73a744027588cfce73b6c9951dbb3570521d465594f9da020cc05cb9793dadf5d4ea654e1816a7c5cd785b01ace4285f6b796de830d8ef02168f2b1b4836122fd60e5480b6f24c9bac781ba446cee0bac253428b114f3e07edd214e29bc2f8c96b0b34d217762660c9fed8fcf973c6903a38cec2c3fe7d090c583cbe8efb679bad56f4298d54d77d8703e4455416489d12a6f6b93994240c407f8d70a7260ccd4de5dbbbde763e9508d32a5bacf0000551a991659c0a840c0d1ae8a62b9fa9c31df3638e9444fe6ae78c818bf8f7352bb874902ac18cb60007c08b4dcffc4c771047169d0445893a036a3db09e6d7791f73c7a5b2744230279d8269f239a75da7b3688f35c8d6b5c43d615a3269459e8e7c897ae276d6502a600ccaf29ed74f5e6947c9eb4998ecb9ea93b8a0aae57f008d5a126462593079e65512f65fdd7b3fda4b599c2a6f66573ff2d04e281988bc743f37f46b5b706c2eba0deca5b648670be3f3f06756c6ee23f8873bcde2fb3811747afc05f610640bc942b0a9061192ece179546c6975adae6c2d2e8efa74471c782cd6237540fe6b50494725e1870363724fd66016ab77a5fc16073b3dad7f1c419397215c90a7c0108b7f29e0f37085d11babff911f290ab1f052cdef8e418d8392983e2d1007a3027101c6861feacc824650e200605662a7689b09a180b9cb08591ef085c0977a015f1395590c384ce7f3084df907dd29e366896310043d0113b11de64700fb7cc7cea935df33ad442d1218d5035fde74c7cc488cd083e4b027181b365960c659af46dd510765f356c5328c762086a4ffbee65a0e4788b837e146accd9b80a3016dd062f5934d8bfedb1d60b726b5fa7c30622dfd82ebe8de9696e5278b5076fb232dd3c26b6f9cb7b14141a319bcf8096ea8fc69d5aba4a85b7bc09fdd1057f211f6ae2895f49d44f5472279e9f3465614eb1845cddd854fbb95d1a65df0d87e0cc023620abbcb8a80196edb8f1752367c7c2dfb532ad1ebe78533ca8c7044c8e4e533d21d6c741150de185ee448116d6d73d2f35efe0172a89442b11bb0017537a8e1c6efaefd37c9e84d41edc69a40794a111f6cfacdc38c89531707400961c127fb439876703df4b63627366d4a605fd7d6003fad0cb1ac6067bf68d04b9d6bfd964094ab01301cbbee6f8babafb059b65d46f9abd0e8f720caef5ae0f46cd98252dbbbf8b6e51682e685d02a4d5593d44116e7c289b0481f906b1ff00062d858bc183d865f878a8c33e2b51dca9e550719f13c78d3e4973313664620adee531fd75347e90adb0ce3d0a584a78f366e8b9b78b2f8137f6f95a8bd919082ab195613c4bf1414f9038c399cb45c36f69025cac91164a7fd066a87531230597860013619f96db483a716438781c19a4b94cc40bfed64313d083dc7302530f087e1d869410d2440b423dbd696dfe4fd53177977184c570c72f07fbd12a8d0d3f328a5fc12192ac90e37f739b6da58d60cd1bbffe0da47ab2d96451ca18970e1f0a5603ee8ea345e1ec7a6654281abb9561ff0fc6bc640aea7a2820f046b90fe976630a38808f6db196bd56654cfe9bc241399461dbd3b99a0d9833e517b500553cfb1c04009b09ff283d4b9fe007585d6dc602963936bd42509b961a229403e86d2cd09955ef0525113cfea90e7c9e71f1d4014db4122f94b0a02e8dc04a0b1825687267b2a6e6168729eff2ef17fc876f745dd208a99df5baa1caaa52e80e3605a2af7e93b16ace09e80338fc05b4aeeccdbc225aff659ad73c22948ecc00c13fb08e766b8bc482d67814b90690db672d64f6fb6cb4c3a21bb92db8309e0263fa14891f1b803e73599637b0ef024c373a0123f0ff78c4ecc6042889687404a45c0a81abf301bd58f9aca4f4b74dab2c99a538159dc966cd7a98d2b8eb460cd6fb77ab8d02437bee21cbe41b1d4676dfc607a592e6965ba4aa33e521f7c9009023b97806bd32030b6eab34bda2b6a2e4dca33eafe3adce6c79d72795d4410bc6851f809cfb28ed82723895a4de9f4c4b1a706308f19e322eab58cbd644450f091461461ea4cd5ce23a74364cc3a35c33d46d60c9637b92406ebc2827ae230312e7a467c3b77f38be8ca818218a4e0b12733e059f86c172b6931829e62e090415a65c90dbf8995b84d0f604465608c90187d9115f533717c70726b72740e7013e51757b71886b27572ce43e28f05729517d6e16147776450fed0214cf28a40593e868de242b788da77642316b324f5f016d4e48f92c4655eee3005eb9d5ec094ab076eae6f1f7ff86663e9521c4f6518f50b9194fa1926cf0d90435faca9f0812bebc883187b293eb3525be956e3eeed3d466ea5b3e79041adfb975bf2df4046ce17dbae4e1f891c1d0f313eb4f2ad00d99f46ecfc36b63ec848dca2076530396216caef45edacbada5f8e5985cc8c53c6bc53a0a40bb917db3aa5f360984039e880f5d6c9c4b324e2666573415538413fe93280532a255d62dd1629d993304e30a78f1eeedbb58efc05f827c97e0503b605b3c735513da4d4b594c6e707d0faf9cdcc8eb19b4fcfd82cb8a805478212044cd983363d357d34daf084c27350e4f0f965abf301f651f276a8b69552539fee69b79afd13224e070871fdb457f0a73c70b4a577423adf361b47d4e201f1fe974dfb3900a96d5768978db23c0b704b329e6e746065360d0264a3b8798cc6a72d0cbf11054e6d7aed7d94f4616820e0cceee35153f79f3ca95ddf6e48498e7add6011553c19fe1f1f8ec9d1cf09f051b706c631b9b4fd11f7d44fdc842e683ab1a5ed4bb4aba3c7aa40fd56ed73e0b4cdaeec6145524f31a1149dc7431e841b93350c84cf4d77c609709ebea5c5a08bfc13fef03547646c945fc3c612441c1e95413751f58c4d77d8b9ae262cac101e3916fe0a48cdfbb0de25abb9fc3fa059f481309b99f8014abb5084ad2c9c20aa8785cff3c2e410176fdb2c81ca19351615b0902c5380171c8049001f5599907736dd9edbeba043ac2a7eed88199dc821fa7bb74e47a76b0ab83a8aa606cc30c3695559e09ea6bfabee08b457419ea355a465462a8fc4c74505b2212307179046d9f4d6355e28a9cd105f1ede8140d824a655ea79c2982bee3ae6b32a8d5320015f663acdaa32d0dee7b995d90d70eb54a613a93c9f4dda9e262b17488772a0f780eb54e2efeb6d0a6fbc44bc1eb843cbe3c759572bfd5e9f6604d978b982d09a34c469e31d19c79d3f8d79609812648fc5379d73349d58dcdc62fc003b23303ec4403201a5cc1299d79f3136490d62583d68e3bbb414421795363d7780112097e670cc8841da92f27d927b2c6d693dfd2b3b62704a04802e8ef669aeb66f90d7b3b7b0c60b79ddbc64f261b7e85237bf113aa592aac2433a67ebe65fa17820d2aa09fb99f6e3c404cb172e0c90a5436608ebe5a38935d5f602098f1327f3607651bea874a4a8fbd813730b3786f922dbe5a091dd0886eb7c76074233f487d0585db0ecbd38b54b248383a3db1b536335e33d126abbe55ba81f2e0a9deff9502b2ef842bcd3dc1032db5ed2970e480ab0f6211f233552703cd69fd609aa94006a6a5faca8b581b570d5d13a011a634a18742cc09aa88d3c4d1bea79d9c790e0628a7e525879248d280f5e5f5d6825bb01ee727c2753f63e1e517b6088fdb8e05c9ca1c2b303627cb130233f76d9fe7ebbb7219934017a706c47b89efe9b49e06a82e52604777df96a66b59def478068faf35bf423552ce0b580282652ad0c807112b41c4aee56d4df30f22226c29c75b765df035708adefa2f453057f4d2c802a0e0905c3907a2966eb6025a4d9515f944597b1f26294e34a5a090c170a4770a45971223a5255e7055b9edb7c6c4af857013a5e0a1b46e59f352c174d9be380462bbcdc366cd24c63ef4eaa1464b7df8b1ef02eccf7a71883b61d34be9b9ae0c62b33bf80e76bb4fb95dca04201ee43b11008296f803f8540a4a0a438fbc510c94c5e867bab5a306077b14772722484f76eebb5d15115b7c6758df524353b202818b203e5147988d86372047cc0c30a6decd88fb9a06bf920ca30acc7002140a899812c1145d36ca897d5700e1368cae3f20c945bed38b6f2010d31b44c7670fdf17197242ec3b79047d46cf49fb8c58138d438a2f06be42c1bd761497beaa02f539e2d8e8b78d2d0e698a0c7de56e908e26054d8d6eba43b0a42fbc71e8250042dad9ff3fd12831afd3295c3a3ea20dcb60de564cd7889f242349c1dc10a00e23c93a9922e986e1669e953bd32dfb1ca63c29f7069b798c2e901ef5b8be780a3628b94ac3a5a4715bedba03979972421531a9fe1552a8d82595cea24878ee0c461e05db4662ba14e5c4356d626012efa93e87f584cf05de8771f3d688e0340ad220d1412256e9eb656c321fb26bdca3bf301593b54b47fdf495b3e6561d5609724e451d0de1c487173e79c19b03f952f77037953f0cbfd6d044ce70f3ffa40e29b999cf619c690c5022a1fe596f5a26464740cbc993bd939a6884ace11b850859c6877d6494163d832cf71d1145b4b4bf1eda7ec8ba0b0ceb0962ff1604a50d3bc053b4351722a3133bc00d56cee2189e08b09a66302ca1a19fc553382b52034050a2fad373ede9886b047c815685ea8656143043999fdf79ae4730033b1d088a5b1a6b09b58fb8562ac18ba9b8a937013e771a06626a7efe9b9fb012094805b4d8a82068c4a776ed0589bd9f282e2f6cb2f8176d31b0cd53a6280b78ec760d0176898ca7fd169f1e759f1540e5d28a935b6bbb0d5f30c54cfedaa9a1fc3e09c43a609dd96af79aa2a1b9c5a0685498273f8de7ea11403d0a1a7c018ee1340a384bb5abd15ef1d9afb1dbca69697d4fd1673fd0829dfa66b7707025bbe8a804924155478782cbea4371931d75100b3c58bf5de77fc189c258697cb93024450cb9cbfda4314972d7eb0c105f8baf86c17ad9a64126fd885d0e7dc4cd595e1d0d000b3ef6eb50b2de9f9e54dce4d24d6a0d79a539b25dfd99128f1c88dc5c2d0973cee877916d4976459187c1011aad2af1fbcb74f13ce40627d2065090169802649879e56a933f6eb7717013de8fbbd42bbf3de7e3abef8e7aba53d202a8580169df826bbb4229f22383dafa7e0e3bf8ecae4f73a2ce700e67c94ffe16c75e02804482d5e78414606b13a2c6818ee3d368fe9f421a720a408af019aaa7bbf30521f3348cc88cdded65cd280e7b8288a674139aa949a06ea6cb2f29bc9c9bae03e28ae4235ab9c1445fcc0966e5301555fc08fe6b9f1789f9130e1c1da507580fd230607d6aef59aff2f03bc6080fac0aee503dc1c292b83819066e0f23756e060cb747e371e2984a6261c7f6b41ab014b484b861d6fc371b48cb7ad9ffb452040f04b620de9a345181fbf7c22bf09387ec0e3abbdb40ce3842e6dd26e2e64a004f5703d0e64d49b0e7e481f876f577d316691259cd55b99d7fa12ee14239980b8369bf23ea8cf9486fd007d8734149c3842d07ad27a87f08f89c4b06e3f88c0a9eb0dcdf06c8f3ef67ac125025139802d173f8b0e0ba385a92a1091b6d823307b7fc9829a697a98fcb3e70bd23acc504910517afaf72d2e305733a77b2f08e031b736f925b61c43365e78fef20f4ea4f12cda9818964c622d3b38d08c6716f02efc773e64aef7ee0990bb0ce687f165b9f1dc000e8a004dcaccc55972fc08602d6bcbeea5544a5f6d62bbcf5c373e6e3f567fd6fd4c48c4fb19b57e76a718f01d48a7b51e2dd16a8a6be5b1e5e6874749cd8fddbda2b45afd567001343eebb0e7769521972c024ff8e0810bc0c4414123caa393cfa5a762b06cdfa72a2c5bd01487b73a1c57ee5bb1eea1487407c56362a73420fd2172c287f548f3215fafd03846fd8f7eeac6186008f1db36f148452f981f3d7bb675cbe3f21630dc86fce09d93cad1cfbdb2ff4ce94e5bd62ec79b81885503eb2f2fb479f54a61fe512410d56cf8d30dcfee1a95e1acd5eb7c1360c92c9cf652930000f2e945054d09d4c01507293b541bfbd3a06e619a0469c30e46a60b353f300b2b0f0109e480c970707487f469feb70add6fd36a8f6dfc5867b8e3d010896ad0d9810ec4dff4d6b0d0e22e2ec998fcc3bb10eb08a8ce31b884272d25680ffc16af1f0a7b421953c55091e02ac32de29409c7c02d5a3682d301347d3e4c42a830569590895714166400538620f2935fa129f02e4d90683e05de5bdf083dbbee6ab43a5a809b621db940195fecfcede53e347a9a0dc342e4f73502a5fc051a1d99bec6a156af90507fe00491d7dc38edffc560a065444b986be082d51fd943d91a1674fb3535af1fcd9084a9b184b1f12ca67d3946b7b06896ee9132fa10efff4204901da3ad277efb8061fe3c92a1992c922a41bb58e200d103f1718fce12e8ea83b867dfb691ad8f306cf450e0a886d6402d093d26b2dc33007c6873e9f2f1036cb0a1cfbc5845ec6095dd5a63fd6247ca1b56ce29423f3a1f949a3ff2c61c158967ea938ebbeb943043f96db2d4d40e15a3d0a088fa376f05a5c6d6440aa97db6fcfa364f5d4b2f703661c9854693602d6d100e31a753f8d54e7d42f8ffd65632299a6564ff9cb1406e5bf875cc315c354955491ec75b37987c6ea65d6489d45905dab0de185fdfd06a68e3647bf13cd1ac7058eba97c1777ba74636f33f40b00a58a9b15883c7f5067c94b05886e164a91b10a56d8f79637a74648c7308d1b813bab620aed183fa0d34ea96f5f240436b133d5dd6039c6346581604cf41b677989bf31d4e1c7f7f03af68ff0274536f5ffe635599c2cf7aa1947368162ea19de83c925ed66eb4860940e6f5c98974aa0bdb0d02d449914d65f7d6d7225fd170a0b261de9680bbed0013180b2ee6a969e4f6b1b7a56bf9d2942964f30fe815f8781b0911df6cb83f0f2c91da0ea30a3ed180521e8c486531dcd855a76850cb1a6c39850b6d5d2ae20ff600cf3df53a8db17e0dc79df107fdb4650a55c1b37be8aa1fc39ffe8579940278974ef472919d92bf60389917065ee6c901b02ac3c4e480b629d1f1d983db0ad3efea30c2a50bf7ae08d6b2d5b361505997ca71c549f065d0d627eede53070875cff5e9cc20b6a8f8cc516b4473199a67109905e2776fbcd94908afc82d880424fad9e8108434287670822bf00f41793b5de0cb70a76123929440579c5cf0051b69d74efac6eda99de0450f282e077dcfb5006f637ea7e640174b74816ba8009fa704fb1bc0c6fe710fcc31aa3de2905992ce162e7b3c24920eec7e20913e0ddf1ca6c6a03efb4ed328a15241485cd01a283ce415acb9b0ad3d16351f4f320c687d7eaf698d0d137d2c9328fe77b2cbcd798c164361895d7081dc1508795f09e763365d82ac4ca6c1e5ccc5e5a61ec12d74f71edc1474cd96f15982bd31400943b2491ec8780c19c83e77fa322ffe80bc5762672bcd1b9cfdaae373e27a080ad395c55c9de36af5b142b3628d6e99b2528478e52108585fc5af5658c88ca904 +generate_ring_signature e34b9385f92fadc3a04acffd360d3e67f7ac03c9ea43c089f328552f8d26f17b b0ebf631bc62dc69fe8844676c23f33a0638f1bc4d80c6561177d3633d064a30 37 64ad73add045a1f06d5d9a42da0a4ae49053e36f91767234259bff0b75665902 68c54d8672156c60a8b56117d68397461942756bedeef45de92908a48c60d87b 495df93e5a2944de1646c369ac30701ca03e98f75cc90848b3792ecb8f886a21 411d7de70564e6620bfc6ff198282d540da99ac004ea698a42f852e84393a968 df79efccc397d40359f381c79b3b48d41ab03e3e9cf368a4834e181320f7f1ab 1bcacc3c7451c4c4e35f68f9b9e44e65069e158f26105755cf892ef606fc71ad a3c9ca321fc9a1188c6f462110a42ada78c71e899bf785980d8cd94f027e3656 84eae7c4292cc7da633e00edcb90b84693047173cbc9166b0ad16a918d2dd5cd 5cf12e6854cb45a7899c24e08acae005fb435b764f5bfa48dd10a05ce3db3bee 21d47204d0354214f525f53a09177121f30b711b53877328bd13c935362c654b 97cebe3f886658ac3767d60843d9e555257d3f2fd95dc9555f8d89ef1dae813d 677a4254eed91ab753f738e75da3524d319231144c044486a75320bcaa63dd92 a282d92f0a00b21780f7f327fa4df4e6929b34d3833940d5d30d2a7b0ccaa425 360c42effb1940a10f49abb4b8d6960da78fa1fdebf415e0baf03b63eff38b81 d506cf35c0dbec7de0093cdb9d26ddf62a2129fd4c5724df848a2fed900d57ef 11ef81bc330128e7c7365728d36d72182c9dd9736c191efa97312318003e1550 b342ed11e826390a85d6fd13c3e877ccb0cb46f40123a379e014f46103cb2116 9b8f8dc376f34602a61ff3a164f600a30b75ee3056f5b45211b1dc8fe48b5eda 86c56091d04f6db76038ccc81035c4a730fb9e281d183e142c9c6256183bba9f 5871290071ead468d2b5479e2cf1def7aa66dd688039565fa35a223d34c83aee fe5a6e702889eaee554028efd3e40971b4f1f22807df39fcb3d185515eaa79d3 4fa1c81cbec70512323ab608eecdb0f4b97acc3bf7e319a8cae7a7070d15e2b4 813e646c474854241c7573beb1b6d880b21b59a8daf75d867562eccee09f70c2 192b97d9656a2fe6d234b364d977b8b9736de64c579b41537b60c891b6161ddf 707b3ae6dcbc69da2077bc1d6f61ad9bf2a571f586d8368024107021af31d5f5 28b249c919bd82d231c60e969123a3dd06ed3dd6760ddaf0260043b65097aee4 f12dd2428a81cfab8f91cdd44bb6c9c1eace8b017ac3ced5bfd754f06f354d43 5c46108773495810bd8848d13e3f2a556683a77c596ead6cccb99fea25ef9905 636d030fc1bd833552fcad07bc404788b7a8e7b695a8ce58a3d61a7a3bd37d2c 65cbcddf0f8402dfb8a5b199639422302b20fc5f87c2383fb0120ec29478236b eefdc9e757b9eacc7d3f72222a8202ae8186d25a835ea25bdd979a85e2ed9681 180b51fe5812ed67f431ae4c5f629591d0471ca533f9ed96be33d7652910446d 711bbd9006f60717eac303421110c299da7dc870541368de9824f7070175f985 da2fee4d50308b9e2f3eee227ebdf140bb04214d43c84272acd28a16c49e2b6a 2b161c47265124ebaf0ce5c4fec207a8b6d3871392e5619120a36a228faf6efa 12ee22954d7a84e9a4a51d573dedd6bcb8d1bd1a7e78eb0bbea8929e77c6fe26 7a80fed6c37ad9e324cb4e1e30f2853b8c312d0419a8b5b9d97600cef50c62b0 75b2ef83b44f340531859d0dcd82ceec9974ecb15583a42192d9d1450bc3e00f 25 1c10835031b92554e0e8c9fcf8a32a05c4b35fc8c2eeba82c9cd1787e02c540a9de875b255937e384dbb099d04d9c7d7d91da858c10a6dd1ccd8782b48bdd90a3fc11175c13e457f5361b8b6499064c0ea7b48b3ee84cd55926c9182c9e5d40b79911a79fb14bba9855c56c0a2ab21898cf71d82fc8e6654d3df2e7c6965ef0be5acf0563d53361639a5fd4f6d2716c7930c5216f835f0cdf4cbfbaf77980009913277fe38f4bccb1a0423e2bbfe7d60ce5e8f84ce7764e5bd1769184c80190dc1b91dce9b586e7e86a7445a109fef836636cb9f53deb78f943adc622f20190ae9da923312c46936e0eba3a1409e516f7e1885a315d5f1eda019937c578b0b04ddef02638a00cc263db78a755f2b776d384f80c68a50822ffd12d4c50493e30869e17cb360b8c5a9cc8942683791350cb75374c4f9d987729f754a2596e5c20e7d7a7334ed8d964947942e47a08b92ca94b55580286d404c1ec10b109dd80603e2bd4e18cfad5799cc819c0fdc84c366ad651813d8b7d2868f716a989cba030d8b2d5d7554c6bd53cb5faee71469e4a6161939e664ac91ab69c169331eeee703f19f9f34f0a627e67c0eca040baf0ee0115148b7af0626589e0e423120835e049b04c2fc686113c34b5e9fae5aa1a178d2e84832fdce8f0334907e13e4a3980a41183def03eaf8bd619d7229dd880133b91985d1e6843ab1be63e6ff47d3ad0eca16aa9926e99c53f24278a84c291b482cbf608f61fa34c5b2d593dcdbf6860c766bb673a021d23e7b3bf755c1063eeb70804680c1a9bcfe6e715af229fedf0a072b5bb2c1d55e5c906a7f9bc80e2b074067e1e8aa7edf3291ddb9b13340cc04300179c2ea6631cd6aefd2cd92cc379ec10322d683637c9377bcfdec1bb03906eed9cc9a717890773940a697a60385463eabfd73f85e7524de8081772a845d046cf46461a7211049135a54de00f1e58cd9b974bc6eb775f6b05028ea9388310940db5c8f09e0666452aaa73b5ade37e20d908dbc2300771e48629bcc82411e08f228960e15bb60c23b41955e814c020a6fa06cff386e93b4a292829c1d96f30421fd3f6207194596557c226329dbae1c9f376aa5822c7cbaa9e7eefb090b270db002081b2863e831c493104d389763229187599a3ad4afb2696eb4a7c7b872018f2494e4d8b3268a71ce41dba2155de9945add72b768b4f0941d5f81a1ee9204f00fd63c8d07335985ac2ffb6b8320e91ec4a43ce2d4988b5fa48e4ec87f340ff6993276f43c2e748ffb8e391065b312628ba8d2bfe55758c65f23bc23089d0d07b75fd379e489c0942ddbe63cddd9a3b762d7d78bab85561254edf5e5f106008391e736f2319f734666d81ed80ecb3ee90e126e6bb694d2be4e299f9249a40efc7b35954654a7bd7574ab2413a8dc5728dd8432ac4dec1bb3dffd3821226b01a017d43cc9663f1551913d8e4a15c85aa4597083b6257b9ada03750adedf6f056abd30613422c42b5a0067098dec03d9719eec06ae264d066880441a8c9af30d022e772b937970a0e5db1b1ffb20e66115980bd3c2f356d603f0ae50b09bf60b77b884982a556b94d94984e637bd26c6e8cced1cd064dadb8d72c950fbace00ed805d658d4bff0cc814a726298805fedfaa03d0b75573fc4a2f442c377e7d9077420a5e2a766b7e11de8ad617a1e61e83401dfa9df7c133f8abb3563cadabc07e9a885389f370065fb4d2cd7d611e48267ddbf206bf28700026b90d82268f701d17f183e6ed39b388daed5d29983f5a953ad77b7b981504c790ec20d66f19d0e0f58c77db5b73b08b251c7b5dfe06541cc487f3b9df0ec01d5e1d7db5032260644efff9b35a3c45cf294a31f084c119b8a6b1798b12c43c42a9dd1519c66100d3aa7ee972217c8bc8ba2264d7187b83042ccb89c0865ad05d2859052a450b104de8ebc02ba0c5753ca96384284e04586571fca1ed2476faf6e51d6e652c0d30ac79165dd8f3adae12d4ad8f8610b90133aee51012ceed9883bcebb09ede2980a53efed00f254d8b3a5796f76d87488bac81fc1a9fb345e15db9c215aa329b40583122d66f7f86db3a68daae471978f96cf7b213f13e68a9e10ee8ac9ac8210064adc8157a8964fbe324a0ce977a47f791af161b1024f8fc56e7124292420ea080d059d2362a0c1346348b6b48b8b37ec7cc73c27f6325b45a7119543278a8a07b758ec39fc8283bd04e642150211cb779754daa6913c0d19e2477aeb678d4608b093934d22175291fa1e9e4768c0f2edaa5cf910274ae897e87d5be2ab8a2509d13384c6e8e1f1b183b31d5b4288b8a2b8c001f2eab7f56dbe4049ff3715a80d1aaab3a090bc506aebb40f6569def33f028961bd4e2ac98f28e783e32795c60f2c81f87176d8d43cbcee4a0eda32dfc2f7ef09e202485c103898679c592db20131e40cddd05737c2f20dc289cf12c0a76c1e16bf58614a2579effd08ff719a0f8207912087c83b4e5b79df2e86477f7f6a4c350ccf765467aa928618f3ad4b02068eb50f0647a4c799d233f364748e1cc8f5153eed29cbcb7408b07df2286208d02742f20b68bea9957263ee871a1e1e4c910adfc51409bbf01b3a32c99d220a908544d4a50dc08e215207f736d0df70ee9cd5a6adf5f383df040b1e7c59e00402385a9848ccff005bff9ea43384b68ecfc8d6164ffc6b93541005a85de7670b6662e82a2f4a33a7b6c8fb7eae9aba49b6eec6471a6375492e38e62fb4fd0408adf23948e04b9d013b82249faa5801500ac29bec7c014e151b04231e6657060daef331c316355dea2d651d665b1978b7f550450575ef5ca450f259e03a693d06cae1ece2f7b2568e8dc0ce280c3778356a4638523592e3dc051cfa9397ab0c0b03d69c71fd6fa66b28c7a9ab8456ed6f5e7b780510f8ad806ba91a96eedcaa04e4bad829cc6d993a4f853f4dc34ca613aaf9851e851b446ccfc2837cdf4f7d02bc97f46218e4a89b0926e8b17836b8f438e1efb9c60b1b636b346f08d491940644611d74c33a9ef49c529c02c10d27f82cce383ca153739ae6e13dadd00ba7097fb465eedb7456c5600de2dcf2ba1fff116899824e81098a140e3d554006240f6a0624f7b41c9d18dc010a8a4bcf989ffeee324c0daf00f88413ac747df4260f526e7b29034e7af06faa58cb6b8d109a1255dd231d4a718dc8c4385211141f08df8a034e83cb4f171ea95936711949f67f9f3b2a93d340ad908a1c153170420d440dd910dc441fa71d89dbad8c9e1e977760f85f0de6a6b5323b5abdf089ec093df5528354fe4737ecdd94dcc6e080703649c2a9aa8a1ee77b41922408b82e0d +generate_ring_signature 5011bad54d6cfff2d0a9646ea61ee1c28c4e44817363335668ad1b2a38b2cf8b b9ff582e2c07cddfc2f4fdaba86c37b2f0cb134677708c0e5bf91e60149218b8 1 3ece24f9950bb08bdebdb4a09d7a5279094ce43a449c344cd6bb0aff26977da2 919a395f5ad6ce0f4dace9886ac58f593bdd1c65fad593a09c490b4f2efe0101 0 b9bf84318179dcb94c2e7bd653bdf24d291b091562fd5b11ad7d8b52d791bd035fd1f251a673327dc99704c71b38068404cbe3aca14026a9ab316970cc8f1109 +generate_ring_signature 5f0c4cb0156d22bfa8c93043911e25a0fbd001c209194a682066189c9fe829cc a24e1a0fac6586177216d8d18fe7b4aacd5ff9cdd97b300ee00e24c172f86f40 14 15e7760af0e9faab06e7953d4ffc18103e0610828c38fd34eb14890aaa486de0 a2c28269b804fb7b76ed273377ee4a0b3d9a4c873d44cf605f5ebf6b16b5034b d5b55fcbbeb11a81e7ffd414fc603826c5a738bbbec71966b666ca44c1542c70 3576161bede7e7eba380beba6ab55cdbd7c1ce4fdc75bd2a0c13b7dea01393d6 04832a4ef2c196870780d8c23db8571dd628591f2075039ac58bc4dce252f5cf 41b203b4377f88fafcdd19eab41ef90a6b7d6ce02bbd55c2479c10da42419ba1 5b47f10d6df231715f22085a17629e7e40db66ebf59a63dafd9e35bc4a35ec7b 9ecc8c017af3ecf4c5d76ad618a3b4b85ac0a3a855f34725aac89d5a639866c0 15bf0238fc64e8bb232b13c4256805233328f77476a46a34d3980fdd9c63972d d05ac603783992b2a4b60918f557288470f25c758a3b1a3eac425d6672baa006 3c005b7b70b7f5aacf4df632f0c0bf8afe2e1ed05b77a6e606521e98203dc49b b23bc07f5330d36aaed16268dbcda0d3889cbc1ab42e63940198730b1d696510 f3993c6fb1ae7fec40ce607a0572afc1071e3515b0cad9a50e06220c85ba06d4 7a4a496278c5ec8edab724885a00cd71a6a76250bd384157d535139997849be7 2f057bd8e8ccdaadbc6dcd2987448e1881597e5cf1c1ced6a95cda0675368100 13 4b9a052a6145bd416f171d48e04d09744302f5548b9324610109461e82aab303913899979ef0f30c03ce085f17612dada642045df0b830ddebfebc64b3153f0cbac6def5d5683ebf4bbe311c6a58d97b6b94c17a5f15ab7f456b0e3cbe5cd30566d6d4a32f7b9b9a1c47cfd99da5d0298ea81966511ea42057aa289e7df4f60a009e82b3bcc670020dfcb633a2d12c917c78b8af019faea9f2b43d225e86280afb1551bdb60b3cec9935e34e76edd02d870a89a157c8b6c9e275905cc9469b0c3403003cccb2ccecdab6741dd2734f55b82a93c3a7ba3b8faf2c0681c987d5061058820497685f99b8e6470aed1c650a98d6f6789e0c56318bb31e7d25933a08830a0911e194e9313619b872c3630020ca0e22ac41e06d56c98299fd78c8b1047bfaf589a6b608fdc5a26e38a0f822df9f3193c214cbc066d8e361df83e7f706ddc712ec8d2e1949a4910a363d10da5b351a10e75d00b4ec1ffbd97cd61ab3036a88cb4f5b353e05e773295c5d6bd4287eb00778b4527ea7183ad2230c7b880cdd8ea9f26aac325e3b79333411cdd68bf2d2ec202fc2bc2e84de76186e0bcb07a8bf7a5a3a2c599cfad00b024b59fcd4234e4b647b69615b14596c58d46df605f5da04cfbc5da714f6ca4ca4c51377a3c0b6d7cdf0ad6a7056f34d30acbb970eb45bd40880a600c90b6284dd6d32d7bdd4019dc6485559cbb2dca92c8bb6390a49276e227246c028e80690b08339f4309452d92c6d0f684d71a20875d2ccf405a9a21c4b697d970e444bd5942d4a62147b4876951e0e8875c4af860e922f4805d2428ec7ece89bc1ce5408b0822ff8fcda1726f4ac816d90352c27c8dbbe8e0111dbf042decdaaace9f30fb95b81d1eec43ceda21a8430864f5aaf39ba6433038cc50aa2c0e83b71d2b485c92c612e2ce0347cee908d1bf11802013d605f380d1d38b1e9231c11bef5da0f709a9e3bf14891c7aca60d01255a9bad6f64bcc70ae8820a2ae22882a76b6f74f93a4514d5e15a335c3c496a7a84ea53b2e50b0a00771569050c9aa1d563accf3ce61e11cc7dd724966961b403daf0396fa4480d0c558e6091a201ddda9fafc799d055687d8947e1e81c544788a3de06c7655f9f0ac78adaac6a5369d3e9d17b12c9608532179bee45ce895d6bdad751b2d1a87006ccc09f2a27bbcafb40218e5b9abe6ecc5c8429c838168ab7b992a481856a680ac48927bd5c6068deb4028b1705bb39d8740501d100af278ddd54de3ec065c607 +generate_ring_signature 72e18a288f065f04d37ff00c672011f977a6fb2bcade5230dac7ec12ada9c68d cb146920471680d2967b3530c5e3edcdb2f32d66b043e750d1c8bd77e8cbc6d2 9 60a39d32614e74394bcacb580a47ab5c4bdcca768ffe9180e943c6700c824f7a ab518aa7cd39e5e9c8b3aca7c90634a5f32c032bf903e5656371c72bfa7f43c2 13cef44d79abb5d8ea7ecbeaacabb16b2db8fbf1b7a5016fd7453bdfb7536569 1a0b2edd6869a67ab68bff43b12168673aeb8ea55bd30f5e745758b1ee8b35b1 4e34abb099d8e971d52fc96c3641abfce6fa979f5631dfb8b1b633caf8636bf9 5754ef91d7d4fd98e0518d252ef7427ad1ed60ec664e74bf727b4d635406f8fd a1076b722e4c35e8782994a01ba897ddf6c48e956c9c3daee51618c9a66ae1ce e024123e8ed8ed64ea3eab898cfc2625b91e7c0f4d61aa458f3e76cece357f43 f1a7d13167c6dbae8e9923fbbcbf93c86894504f1c6ebdacde0a37e2cdff3325 8d065ac61c4506c4be25346295375ce60284b016c9434d7f8939ca3735007b0f 2 b5aeb1d7bfbb87e3e6dc336f0b3235a1b267b54c627e4a806c5feddd58848d03963d2be4a295503b0573bcf8c4819128a3beee7571caa9414ab81bc130f8f4018bd62e6eccf8beeab00e8cdf141b0b55a8b7abd7931840dd80b8229bf76aad06b5fac330c9c9dc11b62d31df2337ae982104d0b70d2e0b2851c2716824878d048df73b177b7b0f58d9943bfd54ac33a6d8d1fbe9d8b8e149a750d0d4d9b5e4067edcff04ce536db138b4ca0cd27473b1809c0925f334b8ae220c9732d1b21003e8b126d225b350bec5c172ad42222f4b97bbd72284ee721a6bee05c776e1740022a28846a687b9ccc1513e390a1200c223bd625dfda4158d03db0df60288850bc29fa65872819b8e4ebc74a1f1652263a05abc449c13eb1f4447ff84ae23a10d60226b65c03caf20d30b493573335cb6b8a4c1d0a5d71b0ea77edb88be14a20b3537c5eccd1675f861551704b2fa83c09bca45f5dc1399d90c4a3ec281b0dc0c3c63b0833202e776450139049c9c33ed3f1fb145781a41d3567e76933b89540c3711e230da5d04e453241802edd99a55994f52bf7ea1ad0c943de722993d7b0ae4427bf523ed08a9984f00cb76b0b426bea7704c8626457d7fad204dc039cb00245958457e8f6cbfe824b6aa7528c1460c5aa343bc24e3cdae3804745f5b0b0b01e30ca670396206450d24266a70d83253ab97ad9d86f613761f5e04f4283f037ebab01cb553e19b255e8cd855067d88133845352cd585e55d3f37089b528d0b6f925d43e1b7bef00b484266221750d84cf394bfa13e02bd4c29040099242d0a +generate_ring_signature a799a7a864d7f441ace75e5d93ce482a529f3548ebab5343cc5bc5334b2f4ad1 21917b032f0f8498c33bc20b016111b635f3299401a79f1ac04c23f3937926f5 4 420c57d71fe672f56fd9a5f895a74a481e6360e2d69871cc6e5039e6229ae780 a8c1fe8c6cad2d42984aceab86862484bd4ea93d077af7a8fec8640e5a83b134 f4545642a9c37c0613d27e32e6313c0edd69bff484c9e82a3aef59a8c42c0ba2 ee886c7d5ed26ac2c955865898f3c2c7d0e9289808c90b8c926533dbc309938e b9e45d8a9a36833d91d615986acffb11b7ad5143f7093700bb5028114afa030b 3 72e0808454d7bbcbe3b6825009c2fcb7ed7ad79c3055f4ac251296273548ee0f1ff3ac4454dc7ffc3f461815a6712bfb1291c76c8275a5d28dd6a9141fa69b0144cb57cdcdb2a2b985a2170dca16dd17d7d0f2977dd8368aaed32cf57b37d2018d0a19739a1792cc41acbf668930301d5352578ab5a659f15308f604be1f95026f74f3f4c953470ce93e3e5f8edb49fccf266b2a69717319aab4ef1cc9246d0861c7c6ba663240f2cec1ed603fffca92a9d8c18d804508963c2f80d9bde6db0790339047f7123a575da3dec621aa35dded0d4bee932216baf07ff76ec1231407ce3fcabb1f37441cb959a9708241cabfc4be5027ebe9f20fa74a87a64365a007 +generate_ring_signature 41b3f147b2907f5c5499f7c8e2ae95a275bfae422cf466f8dfe5d297bba01d29 d5fe3d12813ba8b29f01c93c04d14b0a33fa1e412717780bb0233d0a7c16db2d 9 940b6ab831f2c2c18752e309b6782483cb18f4dbdbb49ec54a0d97a504694713 22359aa20c8bd300c9992fe7f8a739e1917d2e842db3936da8dba4588ebc73b5 06986e0e0d3743266b471645969e4a35e1c35c963c6e4d3fc517261106ad3266 6b1bec8819fb88b95ada60973487e43174f90ce5d5762c21038cf2f56b10855a e1e3e56a84c18a097d779e92001fb23b45ed04436c09b1c85d186664480fbb67 3adfc1c7b7b665e86998d28d2c1c7aeff2360f711909d7356226b2c579853343 609773d01ec79086f6ee90825633368364d7eb7d92c029c5f443ca31fce1b8c3 c6697ad39475e72cb937e5111378706eadb72cc23f7b7f6411d492c61a86153f 4a419c7e73cd086cc832ef3347d9effa64dfbeb582dda11a4d743bad303cc48a dca7a077beff393e7318489c0c0d1ab2248e0247c5cf1a35fb48cdcd198a2706 3 413a8a48bc9862fb9af31a90505e5ab52374533e9ce982d34ccf34e3aef5b00ff5f27176e3a60cb72fdf9f62e1b7abe2785a7fda1644c67cfba352503796f10f497e710255bbd6b2cb8f5144ad3d0c9296382ca4c70773356fb2c0331d721300d0fd29fbcba38abe854ee30d68bdb764e30b85fd4c2c9de40d52dfee2bdad8022b85071be84435fe2df907b903db5acd3591bb7c81bdc790170cf3ba8b90350fed279cf95284b8bf128bc2a75f55581331d662cb14513c00e4639831c8e2230babab2e4968b4d0862f9ec5e25dead85f2174020b9493d41fc73a5bde7b89490eb886911b32dd363f4eda2418491b8049dfe2ecd83c7379bcea735d93b6b09c0fcc9e9ec43e60a02696f7f7987b97f102574e66198faa1a971b1f478cc9dd82005e97078b4ae9ecd607c4c2de6bd8b9997cc044de7b1c51150d2cc4f489d911014f467f288adf5c315c53a08b4d337b991693d438793bb98796294c9558a5520c81eb1c44da54175b254911ddd686d79c12cba7411f2f42e0bfd544d5da57bf081833700ba73c10a7ebe919032e23a7e44af7fd25f3b6ba4cf28d56b394eb9b01fbc70eb7a8ea75a6865f78146a6e8030318a63dd4142e005490ceb9a0c6d5a0f9155e3b13fc833d443ead9d9795074086dfb89bd59b417a8d3a72c970037f705ec99e5dbcc4bc7c5c64af3b4d63cf1608ab133871075e297fd6599f8cf3aff048f779fa1db27fe3a4f858467aad7e11f1667608686a85795b75528b65f09660f59edf320a7e30fab0ce849ed23eaf7091d4966bac895044ceabc6bbe4d58930c +generate_ring_signature c5012ad8ba117d8434c5d28363a23120fa6a0b3be5cc5aedc4071a6b31895113 b497326c10d6d587de06d7687c962e630419e43e356e1857ad49dd732f61de04 41 ec9fde8bb6b5803b2b7c3bbd1ba5f3ea21aca4124f11168bdbbdf14dd3d99fb3 e284615aa5d2452efe258b1af0b9924873ee03889e0d3e3e79bd829e9cb7119e 95df7bc019bd7de7ac83fd4757b20383f6d6bd8f7418da936160c39343a9c5a2 cf14f1ababc0f54dabfad483ee2762fae52310e6f96d9e650659ed6c3c3c900b 7eb3dc12e00d8ca1aba3628171acbb29ab54fedf2933d393aa5724f6fc7e775c da67ac7d1d12e2f3b578bbb270e6f7416575f37474b97ebedf6cba7087e50e9a c169857935cf2d1423ebe4f683357a353948efeb243864f688720e04493c5052 ef655ebd53ca1dda1bd35eb6d0048129b7bb5284c41d53502626385af168e855 6e2ed7ca4984c07160902f7b81898149a372650ef14c9425fd2fb7633cbb10ea 12dd33f6af3b7270ec7f9ab51b435ef8efb97352f7c60a11903d193b85e1af2b 5a9f2f3b31b5f25e43fbe6d9cfb7e89b926ad1e5216ce092d40a70faf0f74735 c7bb9fae72c54f80ef982c5355dbcbd966ea11c92ae20b7594346a449128e982 cbfe6ad45b41d460ba7153efe75d6002fd4d46e5466bdd76b36f445885780cc9 d973217c70b68c9953e1c41f962c44088cf06b04813d5458dc4a655263a439f6 0333cda48b43d9a0b950a07581efbf9232ef9ec4b4d3d39c8cb380f1d7103f85 d8726a8bb1592954fa62bb8548f161d96500d5e678c2129835addbc324d4ec46 88bf5c149fdffda9a654a94699e49eced6b340daa43f8560afa41a90584a4d35 7aaf8d047f1cdc2237ddbcde2d34344d6f03c6f41bc66da374d5eea7acc82e31 c1eb9819971bf7a8dd248a1dd221c4d90e3df73badcc95ce641db441c3e7aac2 f18b20e9c5554b0eb20753808c28228cc5bc0ad6616ccab63865af190ea546b8 38182376ea314bfe86105b240b17f26c2fd745b65cd8311660d16b3d75627f66 018b43ff5d35f0b722322343e627daeb431251cdc4356acc08db2f771ac07b40 58058182e2218b8c027ed2e82fa1e58f7e03b3e6271aff03e1412708f22dcdee f4cfe44d3bc0ed9c77beffdeb9b1ca3bf208e8bf5247a554a1bc32f168ed16be 9b4575e006d066f37e9f5ca8fa4f7fb67dde0d7c8d16ec4bcf504bbf1dd51554 57161388fc8f4c667fdd6cd88041daf49c9eaad48fa6765b6b6171c6ab8a450d 452a8595c178389cc459b4159d6e1fe5ccfa9c795bd36351380cdcfef50df705 95979a6f3f4b79e618ff0cf6abcf27f88b882175f335383a2325926c7a8cd3a3 333fa3ee8e4020ee49316e4feedb8264bdbed7722fc6b6669a0abf1d3934beaa 99d7ad40f415d6abac9acb358fd145d37b89e3ee92f16a9e4e2457c74690d534 dddba018ab7253c2ed868c17238d2c513b05b242fbe746be1b3b53f446d60dd0 2482428e051ae53bbb246b5605227d7c7132cb8c5740496e598c7082b4ae9333 068c2d2b0f2d111db2edcb5977bc5ec2f72aa276c9e8d0cb3c48e1027257a502 b3f1508c05ffccc3a97c372b32ab2cb8399d6d09d60f50752ec72bf1a5d88f4d ea6327f4fcc25f323d9df4ce93bac98e7ea969013c3fbc5376c922f31c511542 84ab2e7181d762f7cc5db78aab226f94847584d2843304f7ad7c8924604c089d 11b05850fe9642662170a2fb66dc8c9065214a8e37d4c024a8e7ee0ec5ec9082 7aa8b408200c59110b610f75366e51c4c5756e63a25048178460c56afb4bb020 1caee556876e1009626ff7b99530e974bb57d8f541cf03cfe3ae82ed3e5e5c59 da4c812457ad078b2cf1e6728e59e89aa5a5581ba8711d10d4866d1d5ca27ce5 7b7cff9e27dea5b663d49feaa69d8469e842ccb36812de0856d10196d8594765 702a4319c73b2479525c3edf49507e48f0782a9af98c1acf7e243e2b0b6ab004 35 76e517db245f4dd329c4ea32c4f4f33e10cc15f2cdd898ecfac0ccef192b190dc007c290873a5561d996183218884c529f17bce90801f1848bc7bcd3b14b1303c1f10a8b1503b7103c2e8f4c72d7b4df21c3f8b18fbdfb7d8db2a47bbfbe2407c76469a0333c920a985e7c68c4f2487f4968ed88b74c3824a4f0072b6c6db90d60aac4863f4dd4fc36d05bb7c035aa9d436e0d5a005cc28166f96a3d73916e0dd2554a4dcc66b974668fa508ec7264ada08ceac5bafac59264757b7549bee20ed3b078eb05d40dd54e9c3ef28bf2553ead80bd59a8e97d4c0d799c4a60e91f091e20b258a036d9a986cfea9bc2f03e865df495bf267d6c43fe3e9c05a97576026f4158f9e12b37ae0c16045bc39f614b9a0bdd9cd1da39bec75417940753800e3736af83ed7776e297e66e40f60d1ae6f5574b48a28d2e01253ae37c84a1f306d1d73242b8f154106d2285fa54250d151a4234fc879ac724fb9c11a961e9540a40b2a2b2d482d41ecbeaf30a66b89351f9f678386bf31e6e6ab28c3021674004f736148e907e2429be17d51cb5c984550e883e41bfed6d418fa66b4fb924cc075d215a78cb3d5042551d1f0e1b1208a91ce4c700e75d6f9a2211d66f4b7a2c03de977606aa72f9ec33f93cf0d7c94788f9932978c2b9f62bffa4156163e229092c593ac38411a3234b8053e9ce90656faf7676feebacdf42ad84c52f67444e02b4294d8918e77a1fe5ee77d7aa351a7a573fc8b46dd0ed021d841d5768f0ff0cc6fef64707097767b966da327ce620231ef3d7c88c11e4c68585686645ee340076017bebc3be5444d91cb59ca265771e5e3dad7a98c64cbfde987bdefd3f0d076da2cf6878c4250208319d4b9b87439255272641b6fb2f5a63aed5a1ec41500cbb167a02e9c0ff5f517072d10aaf6d44060edd77a2af096938c864643b7c4101f39eb2adc2350a90443761ba64f01cbf80849b4705bad7916d1480b3d01bcb0ed65c4269d1f9d34167eb77389345f2f49f7f3b2d54e8921c1170fd0514b6d70f3dc0666c6753b1f268a240687341082dd63089a278d979c946b16488495ecf0586a54c0b9b5f85ec28419777f1d938579cd3ce97b17181d71f2ea30fd0e3890cd4c6737b6864c1edc2c00c46c9043a468287ac0d8e125641ae1d90e934ffb20e402cb4b2084c22847c6aefe9b148e1c70926b23b0c912bda7e2532b4965c5403c8bd4faf33e202be3f828f01bec54d1cdee9e8297b29eb8b275b3c6627bc1c0fa6516165d76f7a7e4196e0f0967788f35f67fd06741cf0f2efb42d4aa463e405fbd2dfe2c198e2bda12ad262c937267d511a6b4c871fcd47df7e9422d9a4040a5da8e30e6a45e1e0347e56b02557fe1cbd60a1c346c1335d74c1b188991a06007bcb8d618a473e25221584cc7d4bc00c0faf5affa4c681dbc9442fab1d3afa0937d8739e5c8f228f3845f9862c4d0b1a1bbacbf5ec41ad21276e693ea06d6b0505efc13fb3ac62fad0b3dfd9feef42f13ceac3c8f16080a6d1e085d43fee7b097f3e3eff9e2b36316f50cc736a5719bc375119526a0a8e484d218f7c2120cb0d70209ee86b22d3942f8e4001338369493907e99e3fdb700b2c6a8fdb451269007ea703f446fb28ef00e5ffc654aa6b5fc8090b22e1b0842ffe5f197592c83a028052faf10995419f3c7d726f44268759591fb1e65d7139fd69e40aa7dea8b20be489a21cde4a3548518ece1844b56e934f52c47680d1972e23b3dcd1ec98220976b2ac9d14d2c02da0ab0d1cf99c153411f46315b06e80dd29c107159ef9af024cfe9ec078b6ea84c6754f22af0807fbf004b9847d4852f77767da4a13c0dc0ca9f20e153707347a8e0a29b6ef217eb9c46d19fe7b59e227d8ecd449c8509c0fe39db25af1e0167815f08027514b7ee21d9ff2fad0367c30aeb1b626dc039d099f13d73b5b0950a5138abb7fefc7b569213c8c1067bcdee0ec4eff7e0d26e904279df6ed0e9ae086afccf3ea73b7e631db40dfbfa41671f4bd64aa398a26b507df5d2bfd94d36c5375899d42ba614bd52cba51daf99297766c2357e4829954075c5d7193348111f2e3af3af19030c16f1037ea7dde96005f53cf30fccd717101bacc1c2326fa94fc189e2421b1e730e65986deab07de1d95fe66ec0080291e0277c99b0f7267446f593a07921f1e398d27965be6a68c28ed48a28dfe7e680e07204ea029b13848b9617a6a8205d2b67823014e3b50789be6c09d1549ad5bd80df9182d6d2167ca63d8bd5537d1d859749ab2e46ae0fb5a623aa2ce49e17e8f0b4c62f8c0864709233d0248814b01125ec936312580269f843d64e81c5fa2b40530157b84f3859f8127cd93128246975688cfb81e9a62c67e1faf487141efaa017c73dd6797471d0b52668afab39c8d9842e72d6bedda13ccc7aa4a8b70ca8c0c6bd3f15a87b9e0f61f43291279221b239f7fff89269e99ed6c7c524e43bf3703bbc1ff7c77e9a3797a6eaf4c012f39c7c0419edfbeb0ea12d198715cf92ad60ceda407877898e6e9dd34eb3e53777b704be23a47a319041d99d23bf40bd110037ae4ee84d221d988477f029249bedfb3fc4d5fb036afe547c4d68f4ff95110013ec27485ce1b98b6e98f4c4eba2effe849c0de32e562351a4864a37a3054480de6bf08fd5872d6b06aa23e54bbb9b29c7bcedd709a07af929bf72a46504b9e0e83e9271d5c0bd2478ef4c083fbec56321fe6a4c6dcf92c6675ae340e93dc9304b93e64a70edb2523257c4ce49beb114eb16ba4d1967935f5a1d6de5a27075d0caf6c164b529564c2417d6632c3eaaf9bae07d65480c93f9b5ea612fb79d41c0e7277482e5e7741bcbc9ed6a1372cf51839f9f4358ef51be1d3170c70dbaea70549969544269f14bcbf73eff8d4d660def1bdccd9df0a7d255ff035bbfd22cc0dd91c8af9b8a88688cdc98e7357fdaa3c484a177f44dfc49aa90322a7bf205d0649067d896ef7695cc8b37143ae4be823d55388d1135f39e6b7573f828ea01207e218331d779272d36699e1566af1bed0ad576ec8acf1b40c5c176709307b800e94994a7ecdde98246c1196c0a48eb67f22586c7ca5d23ad5e15cf35f72b9230eb89749f076e4a5f46db1ab21feddf5293c6a171544a93c38c4bb0490ec040f092f7dc13c1cf0409544047b33cc98fe5ef2e113a1b9ff0618cb643e1897f4a806d9a70fafeac0961455ff7d63b94e0a79cff05cfc6bc7fa5013db04c46c64eb0e08afc9b1cf21b78ced1904c04c9df93ab17301c9ffb958fbc64c0840e89ec70b44b02b865c2c5cd9965d57c79b2f01ae8fd541a5c3de0b4ad38b5f6e06d6e90312393dfe632a47f978013767fe418a7bd5c0856b1535977f8cddb63514cba00de03a5bea9b0991e5bb3a37706a6e943e6a342cd09b6f787d693a4335d1e0aa046461e282e60549e1ec3252f018dea4322be995ce725455b5ee5d9b35f6fb4f09bdd8919349169ff069daaa5672db73b85c626a59de0cb39d1017c969f54087054d93dc437e57744441f5887a0585a3dfc34d424ffa6cddb45421f73ea95f0c0462038e45ff5030177683eaedb6d5f31743e07d15c354e5abb74c0774dde99903344eb8db09af07c474151f220272b7fd1d14237595a20c7d74fcdbc89ff75102d26c25b457ba767e5bd1ea4fac78b916a64e216822af7c5681eb621dbf830309 +generate_ring_signature 059db03940186c4787f0a33bb3324d99742a3e848635262415653041a410f9a6 8f2264dbd1d6568038a8a1180ca6b93c28c478b70a47113ccfef09283b32473f 60 c87c1c541f91eabd2773590ce3390440c7fe7b12c2c4d1de852aedd24a157dc4 3506178794de2d0b990e3f04bc7237e3b236e7feb36ba4d613e727bba92c4a7a 3868502bf016dfd0ce04136928f6a809952375a54c2e56060033c5764285b5bb ea68f7e58fe431e81b6fb8d5db68f69a7e4596c76227c0291f73f9754640085c 767544c6f727604dedb0866d8df1e05f180b064f88083e49f9cacc94844a0f89 9a45f1264b8534f932412b38c2b2782ca3add240004372eb8d6cd747a50a4ffc 9f153a143a89e143e860c93e71c71f42a04ab75dac68baf92da4fefa6c6b1735 23c076fe7c50556cde54a52c42ea690956cdb4bd4f3388b4c4ca383cfecc8853 deec7417ca2eef7e735a3ab12e4a0e3b3c2439c71dfad03014200b8192206acc 01c807bb411475bcef968858cad0a77fed7d106ccc991782eaed913780ae3590 3e4e3e0136f18d38405b67567e2ea5165d8782a1ced39df60f06980d3d2ad9d2 4f3b4a3e58971a4e4f94ce12c054249d1fdf9dc371bc7b5279f78197800c2a51 f42e269cfdf63c238721dba6bba1b839759bb0fe9e83d609a2901d141786090c 3f9453ae619cd54d0d4655469d7c8d0903fc9f3adfd19eb4f5c7b190478b8081 1f9f95eb48427cb6c4f471c2ea568ed9df6633862be02cb2ca025c73b2d624b0 56af421e453d565a46650ef8227bace09af7c068466da857357cd9b2873ad1ad 09d6319f4f2a78475b2ed156f94841862d35a790057d6b050949dc7fccd30dbb 05b4c40394856093b0f40c439a94508d1fc3cdd5d6f1747d7a876c3250deb242 61f86558bcabd5b3cdc9140f0b9826af2ec7bc838eeafe8dcd516f6bcb333495 7eb80082526a179e1f85f2c456b87355d540144ae21c53cadf9783c5a00cc145 fa7391265fa8f7e3ccf55f695c17a0004ac429cfe485463c2dca00a116b84883 641fa3aea8193f840fd72d17b18a89b937a4892fd267a117d45db566c3347632 75f521df42a8e0ebc06d0be965488fa6bc6fd56e9a8071e7d7066e9c0be8a187 15e90d5ae58e79ecb74350781341b56b24deb2f3259af83df571c3194bcaf664 002f53d95e59a7cec2ea6ba975536d48a76fb560f61e988a7d5e27b2564c4e42 94509840ea72bdb5e374a4b515fdbaa737f50be2d4115e4192d0973a652c10d5 8b958d6fc1aed10eedf6376e1b2f0d15a817513cf2aeca6ef74671705543b32a 14fdb9b84b4655239182a7c635dfda8444a090d56ad9f36b39e49c9cc04c9b90 3d5bf5c7569fa43ac9d2639d582f90467c15ff8edac68da3368307be710019cc 36f752b6e9e81e004a4d350d87b19d86dcd759429a09954105b4b884508d0fd2 0dc5194d7708a9ae555cb73ddcd4b5e41ae54e297c19b03525eadf28030b16c4 e7a241036dc9fc3280a4edef47ededd8036bf6d53291d2d316e30cdc4a3c3389 410894029e383d59c6fa39b42102a1942909ea7763867f20c687fad05400c54a 2cb16aecb71bb4a7ffcd6c4d15bccca66e1f2bc16d4a6c3d75990c3dbd30800d b8f95824367f4a299d2f99dba1bed610b421f6486ca776d3b1b3e8ac8e9c7567 61e924b81b2e2773c32d17b2b686948f6a71402a1bfa8c5d8d27de003088d3b2 c67899cbbdb23e2d576f225f542915e3b06c3d2d11975acfda95f8820628bb9b 61deaa612eaea855d93ac74841fe9761878484214805f1045daaf86788818224 9e3ad30f8e5b89b088b901c943e89f093b40838b706757139eff1b47c848edb2 3ed0931de2cdda585f0ff0f7f81e747c1c5b20ae49a930a943871c8581dbfa54 d24b6f7ec2a05580bea3790eda02264a371e19ea10a05818b2d1f9df34294527 f664f5a08ffbb5b9317ba9281c7c77f3f6b91b02a17b8bcd51b7e0b2a84bfa03 99de79cc567792c314493b3eff8444b24d3f02b21640a6a56eb98fe2b1d5f17c 4ccf9bf66102f87a054dbc06958ca501db6e99380294612f17150dd7b3a3274d 4aa5872c8913f1082325e29d11d0e74f0cbd2186a12bdd0c958431908b77f211 3a1f25fc69ffbf815c9542705c516c7055f7d6a12bf009a9bb18926801986427 f2e94737d5fea3b9e3567f1c74a6527c2be489002195ec96c75fa7a412fa020c 4b908693790f118dd039b89e51cfa8b7887c98bf8145dba4a981adabf3e354e4 a4a92dd40a4a4e85fea70ab5f7d86195f310e374bd53ff7d88d02eb85f62832c b8e04b1ebf45036321b68ec8b0543203d21bf9a639027fe58261a98b44c0af19 bfbe0f7f4db80578af367e3fe29d875643d23e09874193e1b60c79e54f34024b df94d0974999403a69373366698cc987be4850dacd5a8440615688c814d5a3cf c9c624b6b654c8f53fbe508e669b3804cb9cd55056540a9b7df24ce403888b39 99d324b4b3c77cd8f59c9c6c79e9748e7ed26d0a74877a16a47762c2ef9478dd 2704fd9930b13d52c5d70440304d0e1e5b54a4b6b1ca2a435677c5f118f07e2b 36ee717eaf9e1090b2f1894e4678602a3cb5c0f374858af6b0d4fc723b396353 ea1d15174dee7f9ca715e2a91d9829d13931e7b5074436fcaa77e50420faecd0 405bf9e77ff4e9dd11bd294e9cbb2b4ba3f9fbdaec2b0e33d423072430ec07d2 c636de9c034a546b2c6cd88154fc9eb766f30b986dac38c3cfba22b16ddcdfe9 139910a20cb4e6d750785bfeefe3cb2ec1f542bcbae42c02072c941123546552 caf9139e3746af58519a3672f4e0e0dcf626f46a00e26f5f7b84ae2f62cb3f06 26 d5197fc3a9fda6823aba90799181a11739204e35df65ec542961caaa983b110022fc86debb66f35dd2ee2cd44ec51353b382c56914373ca78330007e9de1350c8d6c2be176d258f3da949cefdf2238092a8cc922ac7fb61ea0d076592efcec0a14e461bd96140a5bdd0ce0d61f8096d31f50a6e56e314eca938425905d1df50bddc8db3aae65e3b4bfbb62a0dfac856f596e3a19ba3cdda272702853c03e8a088a687af20411b71bd97f647a64df87eba6c969027daa910f60ea88bbc6ed250bf3df8f4769c6df92d5ff3fe87584eaccf5e968d71f38afeb3b17570caf02b40c89fb133d8e2f694a23567e1ac2acf2688bc1aab0979d972062e4edf29a77da0d3d67fd339dea6a4e2760e702d6dd9f118d9b865009b4c275625cf8cbb3c1b304f68770f7f65b5ab4360bd12b10e63d38dd6e2acdb48d598f6c487ec8772ad80d3c1aaa5d16529c85af55ee17362c942899325bda378de2a1fb43a36d54a622044d8e063cd0bdbcd331146064d6b9ce21b5ab2df81d4edc7f37253ac34fd596052c61071316500fe5ea29e7edc1d0e1f163bc98589042c8dca49e91c086ead00baafb8755c9aa88739b7eee8336be965326073e6142a6845cf0362cd0f7bea402e3da8bbead4ce0aee60f995a84344279adb415401b70131693772bc7380eff0b95539f60bc92066f60d2a05cad4ab4b49e76c7b715a1f3fabc24e6230e44ae0847e9c613bbbc78e24ff8d0798fc8bbcebc1a07eeb35672202c3490b300c39108576fef9c614b2c9ecefe6ea2193c5540fe6289c9e650347f933dc7db1f60c8069339fec6e015973bbff1a4b056280ee794035388e81353b09d0c984ab4ea3c09f5c32e9a39a42a9273bb3216b93099f20db6bba5a1f44f5e6cc97b82d7cb0c0579cfae162e8c501e9589cd6e0a9f336ba1446a61ffbe0754a2d799f4dd443c052ddf88b6cfa4d8a315e791442177376ed96e06d67bf8cc0c3556034b5893d30df98926e0dea9f4034a7f97752392dcd2fdad3b6cf7646e370289c53849fb26075d71ec314405cd0e34feb88a3ccbf670bd0d61c5a3e4859a79ea3f24ab405d0140eb4c805b1bf3ae4291e56eb4e12f5886c384c2c0666510d308d3c481f66e07d5662735ed0983b5a708e7406bc4fa3ef7cf3e09c9d5ba59309c42457571cc07c8bf2dbde7af6ef827e3cb9482906ceab6306bd7fed5491dafc1bce202a9d1022029416f0e410ba91c76adbf18d321ecd95f64baf1c2ef099edd2b9fb87e4a0cc934b1ecf50ae9e2e2ba7433a9591c07443d38066ab756ccbe5b00ceb12647006ad25d5ff41205219fbce2f22871219a5c2a9ac3c6a90708f58efbf53b03fd0f5383853d24698f0f9e904105717c1956c4de0d54087f0af8119db3a990ab390467625232af073c35ee17a7f6398b8a462dfdb83be7e7ed86fc60b834bfa51e0768e52671612c3f94e1fcd58392532edfb6d9819b54cec98398f9fd27ac2fd004b3e770afda511a3ee8bc0ba8af1acbdfa14046f0e98b59a26b9e874f8a35870d56b0c1b32551b74ebd5c5fac33ae19442c47bce515d2983cb8e6855d18c62907de0abe6bceb5aedc0152ba80ee06f58f2204f71fd30984070a5f3ac0260d2602f1a84cdb83bb3059aab236048b68bce97b89aa47ee9cd5d61657b82cf2a81e072732fd7716b9b4d4c57103697eba92a4a79d986c92d7ac56891f663b8a3f7101f169c9f14c40aa1d57e1a9b358befe65767ac3732fe956e0b06b7eadf618c20929157c6105c9f805c11995e792e617a368961d95ea41eac1aa274b670272d60e8d43a902666cb869795052bc58f6f13d0ab510791a9c7e73c8fcac3f4e56830af9b423a6517384e038348eeb2c6017a763f48619a44e7224615e73ba32cb3e08ad961c51aaec61ad7d9a4059cb8ebdf014a58ff64f3d65394dd4a08bc9e5fd04bd527ccc313319122604186eb25dbbe364fb511eab3c2992ba8223cc19ee4c0297ae02b6631c642ecb798584ea280b3db079f09384105b4bdb895ab44941680a87262172b1185ace47a267c2c3efdb9fad87cb3800008ac289dd806abff0b5070a246196ee6be8219218e57b0bda8fcc192392a8eb1cfce3db86706109f9d10e97c6222eeddedcf00564eeb46b7bb229126a3e3c6ee85c3e903d6430a779d204a1c720cc31251388deda60007dec3cc3e3c86e54f3731e1ad0741403d726af0220d8fd337c74bddbdfbbde87ccb4e64af42885ca644b29fe6e21cbae73d54403e1c006e827708cc83aa429a8fa3bc2dea052bef647d270ff31ca8ac1cff1db0d592fa77420535357d298d1a4ea4ecb0394f43e114866a483c70aa4c2ec632007216a92015a23655155dba0232ea53d97974335f9668066a868e66ede94fb490ea40c244c2f759580f2700a0d08118d90be4b50bc915d6b44c23a4ff3f0e7e20c7d3e528dfb33441022f6bb3ea76b2283e47b984f1da48dbf8303fe09472e7e02c7f33246533fc9bd2355a22ce2fcb2adaa0342bf436553888708c4d8e12a720e0bf67287bb967f2f72590f56e6129057531d8e0edfe32df810d13cef9e64d1030060e31970a97eecbce3b7a8e7025d635cd7f05cd974a6b55fa1a68ef9ca7702057efb8358568257a02be15bfda2a35265c80396fb5355ba0bce6da00f215a0d248d66918b648e70f8af7e5e46421d58cb1820ab921c1a19ee4a837ffaec2f0bd2988994d9e0218642ee99bdb1cd29dd14a215c6a4a58c7cdaa2c48b8bbd7f05c142259980a437442f856a1095717a8a301420465c662ff899723d6c970cac0f54bbea322ac7cfd614f9a4c63f190b5328b2588f8bfeb091a44d5e49f60326088a0e097f3a5402c4224355d3a60a397cc27935cbae0fea065e2672953156660e1f63ccee81065ecf1b2fb733b6487a178875c30302dc2ce64758bcdd68b97d06857ee791a78f87396bc8bfba1329e44d6ff6c7fd963bb4f89f83164bd6fbd50fb73168cd495a54849311479e904e4223e7a92a65ff5e5a08694450cc4701bb047d775a7a45b72e84e4c209fca24cfda3a1cdb265022b1ab4cb8e6d7a0fd3350326007e74131c43f3cb668750218dce2c7f714c21a12ea1cefc20ac57ff08ad0bbccaab7222efcb42c65a12754ca6319c6f1a31e398da61004c38ed15cf704c0da75463a32f5eee2b89772dcae5b924053758d73aa289388333eb61bf793bed0e0bd453e13f13b5b96a9be5ba21a09d883d3d19c3a4b1b45776ac5f3168a1b80cffd699aedfd2ca92c2920c9b5ec7bcf90afc111cbc80f9e7e0fabcb5ac978a0b59216fdf13f9804fdbe2b399b8164d848898ff51a5e354734061a318fc30550a9e949b8bc385628dae3c9748b9f02eaa0a2f91f077b349c1cb26329b4469b90b1b9db4dc96fc8ef6de06a3f89d2b2a0159b5d38808bac1702706bbb7b1ea4d08f1eea768ffa76c400346083d702f5fa5f2543e9512c15130e2fe1252b5a3270da85a7ac1a09b2ace45a91a090ff3b900accaf64b9368f695f1034fbf4ca2da0add9ff7d7665c2f8b225baf9d8388af3a739f48d5cadf5bc0563230421da2810315f3a974ee84ca66d5f133a98e3a6bde708adefd21eeb085b6f401cf8ee5190ffb7b393b068bf2b5d63a2dca84ec8658b4d1b52cd661d6a2aceabfc10665ec011f71ffedf1623f8061904e8a150094a6b65741bd41a1f8a74606d199fb70fe0cac45ef401cc10aa78b42eb10b568392630bd617b7687d984b6f6c14df0b86a0116eaffed58e3c334de638bad21553db86f3c98c2dbf99fb1193aa714e429210110a26e29cd15eb6bbf391f3bd5abf0cc18eaee664571011fa79292fdb0d63f0242a6438d2e62dda6c6a92f709095e8ab1d43841467b9d2830db3a73727514907a5cd2bdd9687a81c3b37e7285b66135ba51653c2b73db8927553cf5f924941004c4d5daa9b19eb66d090ecd2c153345af50ebc2c9f95a85afd2cb4a8bb5b9d055657a7d9f8cb800d790ef0e404f43f7d7bd4272b9c906e9731a9cdcd1de051081aa651d44aba92a78aeb95a6e4d37e7ae80a4248145bd6c37c02617b79026f07440239f58695dbb1c0ce0ceef8a705793601363517e3e9dd196248bd96e5e00754e7139d993c4c8346bb71b2083d1b3b65def068bbd69c32e539e7f4c592a2031698f9cb78ac753fb6298a204a28df74dbb0576e5355be307963b3a0c386090e117230af0c442dbeb45cd084fc9b2ae8da79dacebddfdd36d9747d47377c3f05381a17243019f7b46f799407092aa96ec021f6879a7e66d97c34e55204207c08f4c9ae5fa68c230943e7028c04d176dbbbe6cca1758296f84e0401b4bfe1130886be23c117abae4cd5c2998a85863a5e8fe3fb39061f86ee18db6c688488d80b45a5b2c7529196f859ea1ebef627fd8350956cc53f9666e7c58637fbd402930018bed276950ad9ecd64e4c8beb4e045bec1a0f41a6aaad3608666eb3bc5215069b7a4729be14144788601d4d87d9030c799ab961c9f432247b5f090c0381c705325f0eb5b27e36c1ecec49cfcd1086d7de471b1c416fe7367b81a62b6c24b4036d9833835e96b552d74fd2e53daadf17c2d823e0292f4b1462e73c789e6b050bcfe16f8964178a9f424e393c1a9d072d798460289bc6596f3491671a5705ea09ba03246b4ed5c19520f2e523087ece792d7a8627c2e68c9a4620370cdd1d05056af1bd6a1ac4a55ee8f397eadf004acb538407375075216e09c61254f48e7f0b766f357cf08e97822109712623a27d406bf97b8c7f84b832a5c79c69c48b29024eeea39f7d2764d463bd8edea0b0e7d8d5dbb2e20f0f19210cdebc7c4b6e820cb74a95b13d393d035b1c1926281e0fa88911d4b6d6ffebfa6e383d8295a3c203fe006a68b3d5dc498705ab1cb112b160ab0668026c28890c23ced30c43612c0d637a52ba784cc1e7d6cda95d48c39ce6d86e0cee1394fcfd4e61439abb3cad0d6b6c80fcf7122f06ceb8e387a4d63160ff394c3ceff157b01773bc895fb7d707445077870eb22b20ecee55875078e5f52314b8d309f8aa9632e71cf6c8e2c00dc5d8189b7871cf57abc94d943ba62cbf28b9db97e8ab0a1248e248bdc72d8309d57adfc290f92e858ea5a6ac9375fc836c345d7a4a6023c6020e154d7ece760bf17cddb02f3de51d9897cbfd77d7e93df8a4a794e361e5e62f880e70cb7a000ac8f76de521ee037814cb0537499db66462cfe02fe803dbfb59db0097b5e416070d515ca3f666ca9f554663b2ce12edb9a0b1cb562dd904519956128a17d8fb0c1b42e4b7796e4bbef863071ba12466c5b27d3ed0fc4234635b9d254d3b11460f06c93557ca31883bf351136f4fe02f2eed393ac936162bc26d8393aea1ab9e09a9fd71be0b9527c2c816d2fe8e7bf17042804aa39c84c62fc1e064c8740e1d01 +generate_ring_signature 07d874b48a77dd142d1a367bc433aa20016c03133e0ba076d1a5300c0522878f fa09cf2ccb938bb4b7e3362a311e8dd453f78f4d178fe4acfd8da2d8fd13541e 2 338e7dd2b1914f6ac287ecc6f8a4a414e484484f6808b1b978a5ef29022083e7 a0ded47cfb0022ca6abc92f336351d19d34b637a39a9271264b71b276d72522a a600d276e3ce0da29692add55febf088ffbb8ba00ded8edb9e1223497eb4810c 0 c207660fa833e770b5d4a9f915b785b26547132d4a0fbd1e79852b08fb15f303dc15e01ace9e0ed85da2f2fbf1d355779b4f9e4f73be14065194db377e794d09522aedb578e0a698ac69c740acc94c17349411985fffbc31dbeb6b9cc360bb0c1340aa97a5eaf5ab90720dfef92a2dc161cd4f13269565920c4888e6d40b180d +generate_ring_signature 3a401e7593691fd8740f69dea36cb55f2c1e616b3774003de33b091bb082db44 77961243f8e4b2edd04cf82ea9645e806288eba6ddeb71c22adf27aeb202a3d0 2 2050b427b2a2ec9dfba6c86014777901852742c5783c8e89d114938ab728ee18 5f8bafaeaeb4a6570a742adb638cd16fc38daad495a4209e3b6823d7474f7275 0270a1baaf5466d7d2d0c67a9420fbd4e1a058438505209905d71f644331a00f 1 f7bbf36aceec2f395dffdeb7171f5a476ea31f3f536f3fa5065c5ad8fe1933084aaa60e5f17ca7c12433d54bbf9ed8a9311c0bffc6d5f3344f3c71ec6b92210238e5b6e2475deffa4d49fec04480055eda5f53425711db355fb30dce2173a300bdc629fee18bffd7534a13c7f76cbdce3ec338b5b4b372289d1c2fe9e6a2140c +generate_ring_signature 21b2ef14fbada0357bb86e7f0c8202396f401252d8ab9a55eb4eeec6f7f640aa 5fd5d918e2534a5e4b2e13f3b348df0e2fca60b7777680ec1af781f09064e32c 1 8842bb7ff9161afeab75bcfd1816859c39e807e8a8d395106fb098721c0fc441 87e09177fa119b89dfb378cfaaa0b5d7ca439c4d34144df7aac3cfdc8cc47f0a 0 709f6debdbdb7c72e7edd6ee91c20793133dd8c58b92e44a0ccd9621dd3e5f06c465ac1f127bd6c6ddc90d038ff8674595ccc119fd80b0273483079fd4405008 +generate_ring_signature fec67ad6b2988abb3a903b75d96f317bd1e5bc02e148122def50f5639aa99a84 a3dd34547b463eea2942a6a891115b4855e4cce42a2833c84625feafba66b696 8 dce537d464f68a845405b379d0a2425485a32c05aedc1159db2133e5a463e37e 87b4b3b9a67a1c4c09d234f98d790bb4721bfade5eaf893bc957780d4e8998b1 8a3b34583f6ccd089499bf3b4a6083ac9a554447cd398693841397abf92e3466 522cb4c282e48c6dee105c20a64649dd8b1b00f4edb7c4b7974c0eccb149a304 abbdf8931ea00ea873d7771b971c550ea798decfbca08b2c48b3f10fc23b0d2b f8dc28d3e91e608f6e8e4b2c63768247115a3d547b81d8a1c80587fee154dbc2 926604ba09b59b350e1475f5790ca2a41390d728fcfe434741a61887c2ba51b8 5635046468a6e7e2b196cf0af68303f619c609ead5cd576e6008d69d4c9fb31e 0227a26693094399ccd3742ce8124b110573d43a6b46c77d2121be0e55e39e05 5 8168df99d29b527c5eb7bfc0abdb0a5bbb93453f0e4b8e9aba50449ac7b6bf0d8fa8531e1592ebf78b1796afc53596fef3ea9b8a80208e69ada0420a9a947900a7f199afe66cdad4097a633d7aa706331961ec42e346de1de43019d5b4335e0fc3bb77133e4ad9ba87b29f79ab2b06a2761c2a77d768ec92d81c9f349edc4800b20f570793bcf03f39adc3546d7d6ecc20760bfa3d2f87f8d47515ae631c660cafec8ca2f8ecd392fb78f9d5bdb5de745e5de27d7feb1b01ae83bf9ac8ddb10293bcf776df833cbd5285c5cc929dd61fe601941831e1238e77353be38838210d0a3b91f958f61d7a3ed73ed9bf3fb90cdbb21345e806b81942906f09ed39a0082e7a3245a84f66f2a73afa08cffbf4ac6c3979b5c170f18f15a274f68452b70c012ffe2e133e7bd1d67b77510a4aeaf5888023765d2d431970f942991e69ba0e01b7d07dfcfe0ec67aaf2fecce93346a2f18f05b1534ea777736c2f9da3a9c0af27826714952af086c2902790402bc52a17c29ba5e263a880e25844908a4ff087c5debba9fbcf6bbb2af2ead58dc27f4c0e5d3079df2d184ea0fda3e1478e800c7dc991909905443976c1be996a791fe0bf1b4ede9565e3ba5d88e1c82f13f02bc223352458daf31680d825eb61ea86cd321303f1d746a93818d765f6b621c00ca3eab5d5cbabff6faeccad2870bf44d085ff17489f2c7b67e0fe66386fcfc08 +generate_ring_signature 960914f8774d13c7b25bdde16c029046ae604188ad4075c949480190b2cc32be 6d72715a57c11a2abdc9dde571abcb0b43f71880b8e2ce92ec9bd015a670cd39 16 c86de10882cc7da7e17745a487d9954b208529b966e828b796edceb5e6b7d04c ccff7066b7ea2e0c15d0c3190594eba7f6ddfbddd4324c9354e44e1be501644c 69acc2ddcc71dd789cd5fae6d7c6ff56263c03e3d09d5082643eb817f0bb7fa2 32698c7f0b8d5bb2abd744a588fda249dc94a8fa83f6981552fc31f6e16a326b 3ef3f03f844ed1fa7ca1ee047200c7a4b34722f66bbbc66834cd314dce920bc7 f576056c2688c1ea878175e3352aa0e57e6dce6f90c57ce1f0bf2d4f6adab687 d696645abd871be43d280044ec6604a21be7c1c8b99ed8cb6a36f38a36bca14e e1007fa86d7f177f26968daf50c112305fc2f6bd566ee23a77755c338f6badbe 52cac54fe0ec9ed851d8da338cc8c2d00ca4e97ffc12a47dedab48013922f977 6bd36976d3e4e54c29198e3610a0487c5a43681282dc6b6624370c05a061acd6 945ea1cb565eedfc52cc113e5049fc343f3c6722f07d6f2a6628748b253c281f 07f3cba85bb3b4c743ee663f40fc00e58e6acde14619815f05a308a9ec0f714d d858bfa7700bac90c579ef56124b876c711c968dde73b70cbf33bf56cd68b76c 53843fc6f0486597872b4dabd61cc7cc941fc8d503f0d02dc4e45a6847a86833 1b9514c790084517ff88b1606519263221efc3425218d700a5d486a9654fff7c 3cb69b787b5603c683a3bb518bc30b38d17550212fbed8b7405e5af034c93a90 c3ef5e119e65b95548eeb03f41de1ea9ec44a99733146458da4b9db318bef006 8 9a40c31b7b8e5ff17a07785529e48af0f6f2f6a6aface3609d9cce6aa335780d695a7edc8e11dc634dd51aac1f723fe00b99ff5bd86d4ddfe8c3512b5aa1260865294f71238bbc172e18d2716c05513901fd169a0b5d8c7f737670d8b5416c0487453eb66681d32ec48f6cf3839de0e9b30e12e834c55fc0955a6801862255048e5e858bb90d5f93a8bdb305ca978b7346b15c6e4a589d141f350c20f50ef60b8b6899041d4523ba8f4393c1a142724e4138121974418afdd62da98673830604c1cc06875109ead27f1b38d9aec4c211361e8fab8a3bd8c8d6fcd10af882df04f4fea10bacbe184e8749fb6d6875d484c12023619389dd17cf7a9280a669d20f7f15f9badc4bf7f934242683c1a920caa9dcb7471a6de7ce306a466196280601fe066fdffc96de9d446ed6dad9d846e413695a96a2e551e777190a2f6ef56e023ffab17d398627b794948bb3cd483213656a786c989884355eed526df1418607e1a92cfb168f87fe2c16db9adc095fc78b4c9fe3201f430a703bb2bd164c6d096e5e439f5718274d79f87892dea7f53fde28a865762208266eddf3799745db0444ebbd3bc42ba3f9715b1c6a45c89b8916042cc8da4ea373d5dcacfea0fed1059955c95a4d87572908fe1a7c1e21e428f9320d3b4caba66a9d19d75ab64dc7029d9eb7ac8408eb456bbd35fe1161230e8b9793bc220d63b9219ae13a24413a066c903bcbfe3b0a93ac1bd9641bded1c40a2ba405936ec90b90651fe9d6210b09dce8ebf40fef714b21df579f50cbaf8e1886aa53f129f7b58a7766667b21d201e2d87a8fb1c035efce5721616ccba52b1b0b08ed8b96ee6782ff0430d4ebf90e83a56d7a18e7f69d3722b152faecd48b0bb0f9789535575ad4d4f54c49d6cd0d8600812c3f518e51fe38b71220285dc88f7e4a1fb6b297198c465a901242750178e565d34b5785048b226069d9b2b727f56ec3538d1d2391561ac6603714760ec0a554b6c6ecd858093c656d9a39b3a60dac7d213c3bcdeda1460afffd8e640d69a3c15c2449070d5d09a2289c5a883075775128a04a69baa3057bad14ad7a073b0fb14197f706eb464b8e82a042d07dcc2155f3b467465912f8c3174bcdb3068d678821597baea752392cd2f0d8ca8bcf6c9e5d2caf8bd2cc4f979eebdeeb0b4d1946a1b9c9e37f7417c61969cc65f1624574a8972e210bf3ebb4d408fa390069139428af72007e145bc24c5e0f7ea0a37a764cbf10467aaaa4593a7dd89c0edbd91cc8f80af8f96be8885413731e745555d4d0e88f9ba4462be87d2e94c3068124b6024a753add50015700fe481aca7ccbf578ad188820c2825a0f9073010b76571fd3739cf8c9d22d9fb3fb22389cf67f3879ed02cd8ed2a4018da5fdb5063b36d1d02fe7dfa7df935bde4799dda213f1f7922397aabb4f98381da6ddd70f +generate_ring_signature 1b542e62941620ef435213a4c10ae81f8cb2ad8e38ddfd52d2069c3686d0e4ff fba9fc9c19997e4dcda0df0157180271d7458c80b96f32ef4850cbd80885a623 2 31bca90b2daf3825dc35ae3dbbcf83e8e3a5a9f779524e7cf054139cfe784091 f07998e42a4839bf1c9aaed6a2d799d9f1044f08261f84b09000cc18536da3d0 5e3fac8756689b17eead27e7d170d34ffbfaad1cb2bfb1000d9a42f3e4e7d40c 0 c5bebe5a8306e55c7de59419297ae3a590bb8c4207de351cc646a3080a52540fdde63bba005c7f2cecab518984bbcf079e8de7f827542f7dcc428ff4199fb50005fee1c794b85b650344c75e2d96dabb085df83f67f43fc5bd8bae80c1325d0db809af6ccf8e9ac30992a7fb0fcfa4c5b3d0e46caff6caabcabe9ad0876dbf09 +generate_ring_signature 1bade1053562780c9eab6bf48b619e6ac4fd045e927c502fab7294a3a4a28fa1 ba7fae07cdd3f8da4827b8c4c826fa084a9fff0517aeb0b6fb4611ba9c0c25d7 1 86576d05548391ba8949f41aa3a242cff7a50b6a26b7d0c2ba010fde9d3ff815 e14ecfae87682763e8f928de3a6aad018d639b4309dbb3fdf4af4adfe92b570c 0 5f3be504a06d7a36d6ea59024a5797ad17c740836afe71b51a5d4ea94a5bdd0ca0fdf51d599ac8e3c86fbb61c751d22be82f51803691e78858cc655fa2fd6c00 +generate_ring_signature aedcd17cf2236a03ba8f1b7e2e1bd709c1b95da9faa65a8008b695289ea5d5fb 9136db6bb696885ec8e41815bdee6619c6d4aaea1b0094b9bea78e31a4835020 191 915012e641fb3e0bba85f68ed576a07287de909b30159fa99916d9f63cab387c 0c6636bc573075b389d6cf439e3480da3e875c83b01af4a1745e3ea550814fd9 584004b43a67a3bdaa0b4a47978b78faa63f6214edfca130eb3f7b1dc4a5744b 52877d6977ac5173b8b699ddd3e1197831c81cc5ca0b7fd99eb6b56a76a24871 cf791c4c57858daf8095eefaffcdf8cf4adee5e65bc5fe3a8eb8ca8f68e3693f a7db16b125db0b53d01eec43d32de0fa2613b657a437dd60d572229510087cc2 b999c460a7d41c27c7ba90d8792890a240d7d21b03dc46ee25e4102f07ed1727 784596020ab319d1cb18a5ec06eb4aed4cfafbb26046116064844016ba4ebc4e 3c3bf831a8fa85a87f0fd8ef8344c2650d6d5c60a8e63641afa466a89ecbe9b9 22fe02fae9be699bd481ec608ff9c87a267e18ba18020c682ff133825b899f1b 639d35528f645ac3c0096b7bb65d5d546c8213f734b20b91327e9745ba921e51 8f69334d6333269f49a9f279876983076c86cbdf21882d58b15c5ff784994bcd 7151fc5b098c06bfc3de520298f50414a920f98fd25b9a49f47cedd6468cc6e8 f679f5c9fd6fba1823187fac9954951334d2e13ce9f3553537f5527bcb85f7a9 3e8412f053cdef90b65b865d21e13ab9839afcfb31a381ff8b5e8f954c8fe360 48d4fb5c56e56ebe153fe8187fceab4f1a9acc4026a6a644c4aad1faa557a1c6 5b785ee84a1850c5e58f69745836c87d0502e7bd385f59035d6d7e102107fa8f a31b1bd4478ae917fb20b77403d2dec8e1d2b9e414cd278e02a2b8f91135cece f529e0074334229dca8a2885d8f45c347adf1345cae1de8be05f03f4064bfebb e1277da7f89d06f39b82ac41112589606e6903d9c1e34bd5da73893ddec636d1 8d4fdc8f11f49d706eaabae6d11b77b88f5263a91369d453d0ff9c897808136e 5daf7924e978526f13664e1cef8dfbe3481480726a9c5bb2a622f4c7585d2e91 70512f31e04cfa1f1fb1f95e8497f03d97019ae985a2de6d41c87e535b4f5c8c 3e080ed2755c6c26b9ac96bbfa3e02ca1f1f6be7c9fab561b8dc0a6c50e1ab6b 6348c335088108d0aa2c2bb936db0c97fc6e5bab4d8522973b3258f7cce6c6f1 97671a425e981ed7721bcd6e4325b704557cf0447759c979e0440dcda06900b0 767abb87bdc9a650946fd70183a3ae4b83923d60c78b9b5bff5bc3df47ab6bf0 04b92f02758d61a98994d0ab165e737d10cc65f2f638197d2ee751a1a3f09f63 05b2909ff4e91346684653575ca2ddd7aab6f8e23e493e187be963af47fb9f9d a8d0de23be88ef5a071ea463be85da7f5e616f8e20f5c277a890a5bfa8558997 19d15eac113bca20c973c5c74b65668d012eb5f3077f9ce911a4001764687265 e11ffd9d09893e71e00fc4ae080310dc4cebe8f627500ae4043211c299431e43 624d9033652d441c1aaad6d36029cd63ab325f43566048b531685c25c01b8aea 6883f6f3fd1d69a40005724f6be3fcbf2ab65ad7e0a21b88c4817d6a35d970cf 6fdf50cfdf2f699b268e9eea3431780e37514d018b60cf6ebfd1ca764d3d4239 098e10ba0721277083ddefb7f82c23501172dcd7f65daef9c24813de2fac231f d74f0eda83405aafb55d13ab15e08fd48dc0f83723700cf9cca2a5abdc992b19 563ccdf2083e0c44d51f0348a874e0522e7f75f4a8a88e2ece012723afdea235 1caf5dec456804e87f4c816947035d638db8f957e860ebdbafaa0b66ef94c85d 28ec71a0bad681d41d5a7f5ca0a166a9055124a156b89b8ee6059f280c0a082f b534fc80058eb087e9abf9a8c6c68790191ff8afb5aac464ad06f1757249423b 0ab9e909911c94095cf93c3380f05e9dbf180efeeae8c658acef9fc19de9f554 91694b1c80c07a609ae530137f154db48c4fb22d99704014f195c5054697beeb 13a8ac804e2b5983008b7b9a8e32f346fbefea7af7c17db3ca3df1287274f106 6664fa65d1d692c8c2cbc6553723ab430d92e767a0d532ebda9de4e897902981 8421562ab768b362abd70641db8a263d6306d71b81ef4137074e6dc971a12682 b1218f481057c6adea95e7cae46c734b93910cf3a30156b768329b0a930d54aa 5092c81ac633da3b13fc6f0874a74f029000920a33f042480f834c40430901ec f6f760e777268bf375137ed0f075f083e6a7210a1930ef5a712403e070b176c5 02098612344a5796baf72177adc79cc84800bdda02e33fd0c1151a56f8c69c06 a75f5ba72d4f75f7a7d73f2ec210192346a9318f8cad9ffd880810dc1fb3825e e7376fbcb2003b00929c95cf5d55d1fb8c25ecf2c7921e622194d8fae97fef81 35fdc4ea90eaea556cbe507a8b17c7c8d0f511e4d3e28e7323033d07a58879c4 7f50f2afa613bc34caf3676e486e307b9859d829b08612aa1b330d518bdce174 4c63d2a0151ce65d8ac97f771f7c65ccd53045dff92b756f0cc1cf8ac93da358 c67ba59841937abb57639252ae288b0c3d5af1950c9635be250f9580714b34cf f61d96bf28af1f4e5734d87be0306be73dbeed93ecd54e64ec22068e3cd4cb6b 93de96532eacfab3ce1a4b2aa8fc707584fbafd466e829ed981579a17251a95b 0480ba9e369dfc97d12dce28c485ccedbe8a1a093a18bf50a8c8728ad5406d30 d5382503a4853cf751bea19057cfd35f9d877cbdd3b8aa866ba1e83c0918ea3d 0257fa79811de7a7317a644fdfa11958f822d5d95859f74e040a4a2f3e7ba8ca 4660e000602dcb3a2dec05fedccb397dd822acae6febaef2ed72cc72604e6bc6 eeaa69df624c3987f8f40644f4b7e8028f9f99557b106fef539a9a8a9ce69c11 88a2f67a1e8dc3054cef58c7048299d135868ad8c755409f7a7dd5007ce5ab2e 5ca1e76e3de6a992f957f3187fb9462ff5ce1659216fe3b3a6cbbb7ddb5f76df f6b6c80159e5b9e20131fcb94c3976c3900c12cfe954bd3e784deec88c263cb6 131c35baf1466ce9d407b2ead6e25a2873072cfa719138f1636f317600bbb2e0 772de8682a35faafa4eb6accabc163e30a6a9175affeff2aac4ad6821e2343c9 f37227a3ba24e6ac2a974cb1a08536adf13a55d53c6f1a5e3b91acd9b2385498 a83d9d5f27ab6511c5bf59f0e08d6fa4fb1ac9948c41308cbd7a277905350b68 4a47cb1af40c17404feac9b134188350de9f6100209f22989ad16d40b2c3f2be 94cc0a654ebdaf913cfb1aca7aafd5c3750df7864cc2d9bf4667af553baf9f09 6435bcb1e3e90a0a1293d1e5e33955ed9244113fc80f6acc8110de55eed6192f 32b6698bba6b80f47a51e1f500bf3b69bd148c95e9b63c825e58967da1261a64 487041dad9bab7d84e2de2917a102586db600a34c9e8dcc4af8c4e9c6a2fa13a 9f6d364662ab96022a009269687690bd99787df2fb96cccab45c2779466a59ca 8f6b42fc1b91031609d5286292264a9bd5f5bed8344686c1dcc49635dce6dba1 bfa47da0cca3b254c6d814269e944745d2c11f5614f81288834f1766d67e645d 5dd70b75a35636114fd013ab59188dde6efbc941a5085e6fed4fa41559c6d7fa 23a6ef9e7dc7f06cd414f96a403831516c825c27110aca63685ff4c91c46d487 3b8670df26d851ca47481cafb5ff3c7f178d3ccdb9f7749d7fdca3d01603efca cced791c8f294a2fe834d36e492d50912ffb62cd290d8f5f6494f043ca749600 9c2b4e0354f3c676bb5d2434ebee7bcb011913ea556d5a07888599896b6bf559 cc98ce51d2cb06eb530d0cc9b0a0d373e1d1fc43d4b1382f88110224784df8c6 6bea59bd2d6484abbb4c4baa8b9a26248f9eaa0b0e1cd1f1bc090ed8124b5921 c2154164da92c0ab62fa846c813196037a6a257773e621027fb27dab9511bd8c 81bd64c1eef3f01b60b4c60765795c12d9d1e67df40fee313bc7b0cd7d769bb6 695d191c57c6c5d8e9a134d162ce5b129be691505eea0b8ee337ae5e44e85dd1 aef92ba0af38360a140ca637098cf55509f8ddb257dc0ec8a1f92f18ac9d510e ff77eaee1e495089e7a483d3735f864bd824a55292ea056d92c7a0630f61bc31 d0ae20b8d284cc742818c38e284160f244b157b868fbb088f6125b8d039623d2 829fc2c03ff34ee5caced85a000ace633987c688e13ed8fa577ffde5355a3b96 7367676a9bcf5d35194deed9e14a41c23c81f5e96a82ec4aa28d9c54574c27ca 37494be2c5d57bab3e0e2df1715d4ab09912124a8eb65d44745b6ed4bd50718f 10d89243a7661e6cf3d7ef223ad3eac26916c71f9337e02d56db8bb710a41dca 55c18ae2c77b4f87e07ed8564e9eec44a7fa841b9deaf425ea1be3ea7c800cfb 94342c2527f9b1b1d38711183245ddc829db67ee2be096c9ea4c386549eb7af8 50d983f5d7e4980efdc335a6eef84da9d194aea20eadaa2b1cb2a2dcf6e510cb 363fe08f0aa6a0bd5ffacc337d8cfb3ffa5ed8b1ad2717d0d51b929d9a338067 f55f856b9f80a22fda561bf0d294c6693f75a2a8ec7555b6632712eb60fddd88 d6a94a9df6a60b266a7c250563d303fada3d899a1aa1af56cd6f414a8196e3b0 fb526b8e6185f40078ccdf886172921d276549dfbdd3a0cd27fc65f172d3bdc2 9dbac5c2e2c39c7cc1ca4f6d42582cf12a18475d7ef9c7760233466633ce15b9 c747e77c067de6a1fd54711632d9a79b72cb48b47c8be21a9732f416b4d0d85f 6cdc0915a34b7411206b4694438d629c17f84c2c07aa745284b1e84133765b88 2dcbcd9798d2408480f40d994667944f5289e2f67bbb6ad4e214c5b8b5c25db0 3f17f5506f9fb143ca4e0978e43a8a537b0cf1b23627a6ff03bf41877230b791 5c8cc4b03df9b06722f19ee747f5e4adb7b6a5ba3f9473bb33e98f06afca0155 ff7913b5b2a3371c554ee75295492f120e4fcc1761a2e90c163800e0bc856653 f6b72f8b3844f18651a0c3c6ace80a0c60357f21b83ba2c13a6b8415aa56b01d b598914633a96c0cc0a2535f74c434dec12809c453ac75dde76a106bf851af6d 8ad5e555016be782538646c00925362d280850ecf85d9c1975254025187739a0 b47e3f52638e0e10865380bb55c9b4617898dc39fb0d44e6238c7077e4a74644 3ab1785018039ed1f0e47624cb17cba05a29daa6af97847e985c77b5de9e2f90 b68956fe73e12b286e488351161ae5cca743cb5223c5c1f3e099b0d9f7b6a545 b64d1d4f2af470f9e5c21fdf9985312b217dc866cd3c99798322d4332a75ed3f 8343a5d94480c5c44ab7e9d9eb219629b23d14a7b96f7d0dd607963b58ed0584 84e401492fd7498a1281617bbb9750fa4dea2f7ac1c05d4601d7b4d6f93f05e0 5b5b580f46b28171240dab160346138e50c6ec1219f8f7be6badcbcceff3cfc9 d6959696fb90fdc1a7551bf4e65b82478b6b36912ecbb31c10634f664254b5e5 e34b50fa467dd7bf98cfdfa98b87a924aa5122b6b09fa19e2c6b1aa82e4f109d 71e4c8c681e9f7f7f0607ba801fa44816a802e81e8fc42faa343d0c0ebfe8e3e 9e8a0e62086feaa8a6259ce3a21c4063fd4cc807c1beb371372f45e7aef14931 234b807cd5aad555031b165b65ed5ff5b99fe7749b48b6860db9c0782a755cd7 19fa7a5f9e71d90eb5ba18bdb7a9337243ba0d8735c4721bb9b65be880ac4ec6 e7b803e970d0289686e7432e63875c87c4ecaf8dd9a7110c41cafa4384492811 7e8c89e1ad8f42ee19016d782eb7489ccb70b72be6ed9bc3861647bd742fba52 676c2ceb1dfd2d66f045bd54bfd7e5908a57bdd5216b7b17ea808e479c0e4d7d 22d07a9d01607074f3121efe97d2733a5874e3ba9d0acb02bfb65045bbff4900 37f35ac4e59c6060a230fd6290d641ab0b7dc903903c06638edfd88f9ed04279 36c2a9021386910cd5fe5e791ebcc4abfa5a035b0fb2198bc23e64c5f68ff0f2 8cf36e853ea751ae307fdbe0837663f823106aded803139620a9cbf8d65d85c1 caaece56d4bc522a656f2911fd361b3c53f4735b34a95994966e6a5502a4686d 7c1514c9820ad846074be5e94a1a460efdd832703bab36dad44b04f6723e6b98 9b90f095ac9e66ecd8c321797c8cac0b6ead2328db02968466cb2a813a14b97b 35e656d87021f86642a2f147b58869913e1721e8c8320ab77acdf32775ae4b81 f545847b41ac23724e860ede8d7fabed5982702ce646484b7c008a603b48b08a e47ebc13266c2d167736b1fe30b6447f51d915a09cedccaf68726bc2dfde6688 169d6b2ca742388e7476e61980b4f842dc224b8b88e56d5879bd3ff4f25664ac a2962df9e5693023d2427ba2d6cb39c174a456d47afdc51c0dfbef6219d2bf8d 5a47f6fdd8265641cce67a5f4cf82c0dde5f1487789e4ca947b62ba15d391a0c ae53337434326613e0a7e86c87effea7123084637fd9ec488d6a5cf79678418d ef63627d0e850e46d81bcc136ad2aa095ad1a451f2d40d0954b5b9a99009c9c7 23e30dadfcc7190509f48bd4d15c5574ebe16d7464ec03ac27722768c6a0fe77 0522d3231a1d7e59481188324c8a300004065010feb4408ae9bb8c5a60280990 70e3d8ae37a4c76f71493b5c01b5e42ecde86d9c702171df71477307b87f2342 dacd39573ffeb7bda0427535439ea7b5c518c4500254a1555b5ced2aabfe263a 0b8e4624bafddf2d0659dd9568425153ce7249d66dd796ae9d9a3958c207ff62 cebd8512058818d365f2a894bbae3fb48a8d8f683cb5b7ea86c20876fbcfacfd 4a70c3f4617a54f7e568c19a3f04b30330de4ea966f16817f67590168567ba2e b00eaf7556e1b89625b173c9dc6c1850f9b0f017c00003002041da914f398946 47208e753a3e2ad56bc88aaeda6673781d74c4adea302c24bb430bc9d2ad99ff ebbd60707aab510cfe9aea55674eab81065c4481d823653bfdf4ceed5aa4cbd2 fc04acf87b977a025d7aed687524a1612b4ec58f042f570dd537e2b7bf1f3748 19b67f9de08052c5fb3a0b62ccb6ba87ea75cd0ceaa251708b40844267f6b014 e21ba734db12fcc7ec682fd0ba5e50f8e201f88bacb180be02ca298bc501a043 348b8ebd6852aa5ccf10333f8e0620a3a6420e65b584810fcfa27640d9d509cc cc36d7d9519faf0b07448f93704963f2d5be500feaa60567408b62c2feee1abf 63f5c6300d1d712241d99ce88778eca50d973036a3dc280c5ce49a00f415986c 7dfb64b38df7b1c5c931251663722e1bbcf64aeb53da9d97989e62eef5b3d331 cfad09dd9a4f9f635119fe9afb75a1ba138aaf7d28ce9ebfba487e0eeadcc992 23cb6174b0170ef65b191639424a1d18c67ecb6366d5e789d006718ff192b74e 316b81df8b5555c37f7a6a357e47f73c191a3278e9f09b097e3aa9d4342ad072 72c270e6c89dd8910f87196412e16a2370d76502dfd08fc229f91b206cc65e07 5328243b51178b327b69128a46dc348c351e6be842e628c9a1423606b2344231 b721f2f094dd6f93598aa06ad3ecd1a019c6f281b599c17a3088d315eef50a4e e69466ad947fce451cf7fadbe9aaa5c7b6084f72b3421abe042121d694757954 3fd221ba32ba6279137c88c55d22a192535e263d14033e775ef45eea9c076d0d aff2979dd26982e33a7153b5cb68d0d0d4c55afd550ca7974ae28877b27013a1 3b8769dc3e2d1ad107d4f0d96cb5823602daf842cb159238809a9088752d340c a74083c2735f5c674f4896893952c90d275c6d28ec96961b36e75164b42d6346 9fe654dd30e98c246694813077744389d81def9c03886079b3c2ec7fd177a1aa 0f391e66ee3fae617389c9c84a17a7623c77c414c32a69115edaf3f6fe1c71fe e4f05c77a708ca44b049ad52ee565c9095dc07ed794f61b918d61c6a3bcb2e1f dd7e5fc3484d81d4f393f6fafe0dd4357ce73d08b6db85aa6d4a5dfdf61586f9 195c39e84b62a9dbcbe60d5625491f3cd44621d990ee50b74b7b3a7862057c7c 3f1cddf5fc7808b6466f75be06c1b19b90025a85e241a44512e65a94b2750581 7967021354e229204be77a10279395bb0db8e242363416d1207f9eca01b094c2 65fbe891275a03a53b74c77b32fc1f3c069aba9642c68c187da4a37c58c407f9 087c2ee2e454fac8cf3ac495d0d12b629512e1fc0dfaf9d6ee41d18e585a164e 101d1f78f69b6744335233e84495b0511e32d3c720e2373bf669c27a4990668e fb8a3768805b5a09ebc505e5c0d4242b617adb9e0f3865199bbd2a7be7579024 5bd7d80aa4dfa29c7f09a62636780456dd78e079fe1e80345856395c307cb956 a501ffe13cf7ca14791be6d59d529c182b7261c0bf91500e8d1ed1b90a6874d5 b81b32d833d664cf3338c60fa3205c119e1ddc8c9cbeb44702459a97512575c8 3212abc274c96f4ad4062309fa3060d05df560ec075cb78a80251cecacc9000a 8ebf7992dcdb01f560f7f545f65b6eeec6e6c56d737bd5508a6c8a33f2750bd2 2f37389496a5ed2dbc4a2a4e4751739b44c0c313d2743e09e03a81c0c392c11d 0d88cdae4b3f0d29c8dd05a4a09dc8e98a86615891fdcaedbb2643732b054aae b1a01aff1dc079be3335a3efec1cb3be3eaffb22080688a4fea7ef634f8b1e5b bcca6d0fc7788694c3f31ef1eb29d220984e958d264f51168fcaeaa06f658774 327c6fdfddf0dac8c72398bef170e16e2f7e84c68874e9f5d943a7216af14f03 130 0cc69130d5c717d2190639f68493c76692c6ac4444e32c7e3d11e52e1df1860a26b275751b3688fd25bc57024095b19b561fac23a02104e6839d87836e15890c18e2497970ed1baebcc31cf9d9678e2b6302c379c159d6261c527a2efa136d0fc2caa75d57a5cbc6e1d556e16228c56253e5faf5c345dff9d733e93beb4d0b0d6c3a6b6602c17c98b0e05b838963abc58ab7e686c66e24bc592d51da8463e5053d24074b27070b9da64437ab8cb734e5eb258f9d66ae565fbdc9e1e11e20820f8569e5d364ebd0fc6822c9605434ea75ad4d08b9483bc307065365a80323190294592ad089f7ecf38d1b75868861f020b2d4f59d74e8ec4a98812fe9736ad30ad41cd3332858bb84d1e981690c9358311ec6aa2fa65e7825293d0fff87b1c90c6ef28ceba11b3c205462272146e9b0550f4925457c83cc73f10c7e7f4d1af10c70c1691c1f89fb3b538ba42596903ad71e19dfdd0bef51ebe88c44d4aece39076705070ebe886cee3038f2fd573c202508574914c090cc85cefc57999e2e1802442c2262650cccec99036a7ad71df7316ae084edf52297667b99a9d29f1bc608c0e9528e9ec68fe28a31fc1feeba16693e2e0ea1bb06cf9b983a4aa346bc7c0c80278e29fa9c4e9073eeacd3d7c98a71101e5b18529e0329bf8c8030dfb37e0f1a7ab73eb95afa118987936bee26320d24099593b572cafecc9224a243c7ed0ca53271dad1a9c5220bd7e3e16404dd343379db10bd8617568b9cb96c5a748d07150f26090a792819b616890f73138eab2c558dbbafcc44c7910410ceef4249046740a554f3f361c957936b652f5b52cf9144a3df835c2877d5ff98a3af84020514897cea22fcd39b5b89932fe025b9753624d407bef8a0aee64e940fcee95c0b8ca1d2387349150ef2c994626853023e7da2046938fe69aab2a2cb14de98040fe144fc511ea90d8bff4eab01ed06d33fe428f0f6221f45b878b26b8a0376b20a50b9ccf2a8104a8fe62f3df5a69fc76002eeff565f85b65891cd792930286f07aa48e110e0ec1c843c0c390d10812cc0369ab3f5c36c3884ebaaafaf733b55037ddec280bb058cd49e1d9dac2496087817dd467d420914afdfc09b2b563d460b6b3977bdf691dd849099cea6f1b9360f0c05a932566a18d7294ad5eed534210917d215d2034cd949987eff97ecbf8c45d01b89c8c2f8cb60e23fba06dfe281078df96a76b1b3f1288c7b7fb19956d48ed8c8a45d737b3f0f46d19d29a3bf51050e0815c3bec51b5768080a6f3f405cc4a2309f0b7cc366296f4b6c2c214b2802130b3c839a71dc0ce94bee3a637352427b90fe6907699dc6171d51ef3bf6d20eae5fd379af9dc0d7d92c3874fcdeab72278b79e02be65bc46892fd18d68ef8093ea61a0a6b984371a913b3d076c277107a458e7f9e8f1433e03c95e283911d00e920b7c7d0c394c2562e211bbd89437a549dda5f24c76d169a453efe5b6563012aaf9e54a56bca4943d2be60bb806ae945ca8f41dcf51e2de44b5d931fc7140410f556c5849661284784cd827c8150913ecc317dc5c427b995110b4361e03f04c7957ff8bb3d26be5d0e3563630e24d9d2a67ada5a35d03acaf89dbb6409df0e209a6690904937aa63ccce745ec8a5d00470ab92af69681b20d1c01f38d3bd0a82a1c2d172d1380d75b102d694195a50634cd6f17275e73847ed98b807667d0611cf19f2765ab9553ec008b9aee6c2c199919d128fdff4aefe0a58c89a584206851505efc90b7385364964794a69fe613b158d47806ede5b6b21439e36b6e009c28286b378c392692d3f0623dd8d601a7e4b47f7019ee506f1836d4db680430c2ef74f3c5c526603144f881fdc30c101ef169030f66a0ddf4ad80487dd2efa0305cc409be4efef6e5ca6b5a69bacf92be245f2b009527ab07d1b9e5f17a2f403531a644bddfdc887086159161eecfb681cba168d40064255463c22b2c543e009230f348b000394dd5e947cddacc92cd48a48d0ad1445c62007ae2863f2d2e608b7a65913758db2a460dacfda59b536924e048995975a7a1772226dcf92c8850b850c988079b7eaed2bd02fb2718f2ec4692c5bd06ed1d15828c66de272005400ceeb56e0bac61e357caf228f6ca25aa9467f9de1b80385ba4eef7636766c7e0a5e4ee1f2c213bde5e064b69839ccd920020285ebfe8525edb03145546eb6a70486a58b324c4d04be8c5cbbd65957b0f59b0bd46b6be136ff9b326a48ee32f70043e4786d1ce8fbe45cd58c75bd1311267967f522d290b51b4975c953f8738001a60d9dcb10f79bc89ee14c86db1782dad2afb6fdecd005f7191e2a67af565e03ba3db1653af23de6f52fc111841514ea3674f8b0047c0cfd42c99863b44ebb0ffd6dbd91277a360812edde152f79dcb83730ef554d43aacb7dae10227a225603028e3175b20560cc2a8aa4ed6103ec908a930ba1a2e712d335f31578628df4075a06d34a279baf1b6bc1cb5ff803ac217dcfa962f3d8a138a687df561e8b2e0ce0f02baf5b1145a2a6583deabcd8e65e8ea8c796de579964389306ba6bce8607e4ee8d11a055c69cfcdcdf6804042ab7f43ee7f9cef3a1fd730f27fa49a7fa05f9c0b7f104de296c8026440ad05b99e65c28a8d1862456e80189746cfaeb6202160d8c6cc2d721893ca7ef402f56e97b77255d40990374de0ef62f6d1df8260669d119b1434c3a11f6172722376b526412e045436b6797b9c7a130d496bcd10de8a5f2658c1065b802d01ef7ea1d802a9920edfb6abb060fba469cc453b4df07e1c8a7aac9c2a282d376cb6e21bebc8d4e20603166c877bd09cda0c24db99e08332e76ec33f324ff765f756baa51b2daf4c06f5a6351f6c5ab40aef806ed0e05ddd5aac190504654020092bf65360fe692abbb2390404063045626ac86e9de0456d36b8824c9b09f25c91342c082543c068a14ac8c7814acd8662adb89b7df00f0fbe742e10869c44855f3d943c8e4e7fe7f0a7d12ed898606520f7a00daf90b5f44ae0dbc6a38425dd8a5e14a07b09f282ae4a26913eb1329aef71262ec0202c6ba103aa37e4daf2e5875ce63c34a4b52c263117c79ea551bd4e6de877aca0a5db8386cc50954ecbe48581312cf0b59bdfb53cee0745ebf613c7a52552a5504bc4aad1194f4302458a1870da63d8af69d24e208856772bfdb367b6f23c80a08f9d1848a26e669e58ed6ed6ecbf2ad25e17af94b4dada0f95c46e5f37eb6bd0f1baffd1de518e5a7b58e569defbffe76a863c620f227483cf0ed2f8a99834b0075894353ac74da43a5a45e6f10b2ec06ee6133fd3f3bf3a9ac9b7fc29d1cb50009d5038ed033c069382f9b5109554e2cf85f7cb1e68d139f04c41ad0394d3a0de1cfd224dffbb7d0a75bd3f26d521402b75ffd3c3cf5cadfc9e6304809600606c53e31600a60d90bd8829166457bc493a585b17938163ddb21b62e89e5294d034e82eb1c4424a0545bc5d205e3554ad5a14a57fc6a80ef7acdcc66be4f53660c57baa5c5c8f47ceb551b8a217a88da4af94155a40c022e0e86e84adab1976b08036680342c682e89d6df4686f84f885fb433df387a8ce087b01bac9aeb24480328ac48a6a06ad29d7ba6d2bc89ed943e8d7ac2fdf9ca7b3ec60bea50d00a190dba8198d8266f057c1010ffdd6d6a28122fec807b990ccc209c286699d9501006d691cfdb3db5af4be9bb0638fe9f80ae8487786c25aa8d51ab5837802aefb107207462b6797ddfb969d40463f3d9ec66986af0bddcca7f8985c2f707a04cb404b8edcae84eb9a2dc8a137bf6f86828bddff24e503110b506b065ef477edfea004ca9707be13702886e5809150b463a1c44d36bb7de440ce51c3fdb1a0cdd0e0698074481f7d608f44e2dc2f7f5d2dab967e17f73e39af5620f2960717e8f8a061653ffa3ade70de75a8ab8cdd7179cf5c19f1ce01f37782aa33f583cce37090fd966ae0474fef3bf353acc9cc1054019e0a4dfef017e31999b6307537b939b02b650c25470ce6f289ae5ad7207bff952b9ddd06c04f5e14d13369257584d4804aacaf5fcccfaa25cf317919f9e97a0c7009c161d508e7668e5ddaa4f0bf8c50f5156caabac38b4a0d2730d0a9777a77029b9e2d89949403e9480c46749b715040a73ed23e0b1497f4cd461c410e2b29a3c0f4cf06c302176548531566042af0834d0cfad5bea8f64f0be8f8788fe6dde9b99042586a2bee321f754a37e8b2c0c384215ba15d685380d1f5352f5ac074f50f026b17a52e22a4f95502d97b9510f8b69e944d11bf8f9280d9c059487359757c51dede20f9020347a99bb35540c0fb66949662142bf64e62624094dda39b3bcc3ad0b1955de28dd3cb10754316206af641099060f30688e5cef2ba0dd8e977cc6197fb3a251fe7b4551cac497fd002624d5ca8778ed45c25d62c9a908f96700b6484a16bd707ebc42f9a28d9b9702981f8d738b052dfc8a414c76b0fec67c0843a07a5c9d9433e183b7b805923c064c67bf1298e33f0adbb84b32470b7a9fcfba58289226ce2e1d08520afefa450f2e4a9d40e2ef02eeeb363425c276ec4ebcab4c248d8f1ac039553eab951375033c13381a4eb50abc4e3d407729421fb6b8b5ab9486a10c9f4acda62ff055860d1f5eb6916498a09c1ac9eb319d69be5f1427f5add38e6697c0edf48604d1ff09c595d267bde5d75aa0796e550be62c5204e32c3d86e5b049286de00364aa6b01ee31f6eb101585353d0fba4933a796a9515ec51d162eada60713025735238403a021cc81835496f337eb1252ed8a28e2c2db0d7432e20413f6886ba0be5e3a034befa87d1ed53b95075bb865360d95189bf6a041c1ec6d31ae1fa1f1fe8eba070180532a55abc38f61ee82438c31909cc0b04f8ea12353bf57024af27cf2030c15b22666c8d86fe21d612ceeddda5feea2f24e6b8487d55fc7a48998ffd3740f352dfcc3174534c2043612dda174cb4108dd2c89ca880e6dcb82714258024504bd1b021e4d930d1fe4cece2a5c5312fb4a3414fed7762a7a29a4172f9ef0620e705784a00d72a4cd3d00aba62e080dd8776b02b7ad668bfe72e53915f6c3bb0721dd3ad5a054ebd67a31d0f20a70290419dae635aad87a98d5a894dc769ed508348ac45cf08ec79fced7c4b4415afeb2820cdb84a4016c00d3d6696ddd06fb0710ee7ba44410f0fa4b1eff607f9d1f227adffafb8e2897b099503c7fd0fca40b0e44ebfd6b122add600ad95134eb759246d4738bcc42e6e3d97f2882c7311c061232a6f83489eb09ed3a312eb7b910f23767427a0cc3a172af93c71188217d0b40d12a6883576b3a60eee7bad3a5f3e0307c5dad6efa8c112e7f583a413a020aa979aacdc49060c9c5199090d1e0fbfc19fd70df6199c5083bb5297411f7310eda1571e958ad438280009c9acbeab675fab4aa3264ba360de0e19a9adbe14b0cce60355d48427e30a59f4932be418d894005a47f56b4e123d2576e6c759fba0dfa26e582c5148ae3dce1f15c75fa6df6e766302e0f51d5abb473d4ffe660330578329d2610cb8c2c2546b4031be82c3eda11c46fa9d6b11b38061bebe8bb33058a5ce7d8f02c28767a91d79218ea6765fcaf57a32a53661de2397bff9534ca056f586cecd54a979e120326f63ce2830f48a1d7bb8347b882d30a3f6ce043df04bf9bbb11642dd2f36e191f719a7922c96f9f2f0c0ef6b129d29b9465f54e4d0af16346b901bd7496340a7c869b2e6e8c07ed5134150fbc43f9bbb7d021399801b613dadf5c15e4e2979ac5eb9d16267fb9467ca7928d4497d369180eee3e8a0a6b8145b18c0b74e313794591749c745f1aebcf0de765f006ec55b06516ba9f01dbf501152a2b4f3b9173a2baa7c040e85a17b55193212fb0b58e95162539aa0ffb064ff4116146bf2f67161f4e328515fe4f324c6c80ec2d225c1c1144f74c05c531ebc4ac5f299b7bf5257994b2c71c1464d6de29c7c416e99f4b6de852da0ddf4096331d0aa322535d71067ed6f9d7613706310540de09592de5ca67680d0f530a7dbddf00b7aaef4a58076ef2dea1fa23f2ac7b5fc214e76a199e901f2403d3143317cce83ed356e7a39be0d3c6b17735a9c741a398f91a3a7872a1fe190a9ffe802aec237d69a2055fde4dff116d7a9cce4c6e92900840cc933ec76daf0ced3c8f0799dc37db742115b829fa32049dbe067ddbe082c1809e631135999e071bc4e4ac375479274623f7636deac31823745052cd69bc748573e72d40d1e70d03ef3c85ff68d076d7bd2665f3ff851e515d09d3d0e4fb5c465fc2a2f2c9af02169308e91c3ba97bce0418739118ef393eb846ed25df75eb12f52b29c0d66f06a7965db5945212fc6ac52f18259ab701e2d8a3c8fb8f3e927dbc637425893804083f9991dc691380cbe227a12e9f4571bb9aacb42cfe18e392c97b79960b8b043868322e9a43e19faa02d36e1c02dd3a72e5699e7167bfa41c411bc1860cde00a539cabd8523584efb2b78178d79c4e0927dc811e7610cb73830f5199e590400b0f9db5b011afcb5eea715ab0484a44ad2e3c4c1632eb185f9c6ae997eb72601673840615c76579886150f70320aceb4e9bd65b0d95073cd1bbb8fd00ffb50023fb322e2fbf2bc0adfbd4dc18c7dab46e56f09f7ba98d0cc939192fc739ecb0056e013d2c15e22bc7be8ec19ed4ddfb86d54b32f3daff4d01b745ee3410dda0f98a22fdbac698b0230c421cab3350cbd8516d377166a01bd6165116e19835a0cbc67301db35ac95a159333b632a14fcd82d1ae98dc93c364b5fb6631d1d3ff0b761ed093b32ba83606b34646ba596c1b1235334ffbf5571b236fe56c9bdb9909b674e523b4f05c0801f660f28b0455db1d69117028b798de13ef2dfc1e6d9c0d5ac417bfcd821f577c7e5cd2c60ad6d65b804e8ddedc0348a9f94433fc0d730eefe059bbbaee1397e02ebb0533a2210f430ff2e4d5b2076342f2b7cd62d814067d04c322ef715b642451f7c515af26efa5839853745e8eba3e130f9cc14f5a0ee2d2e8c85256343d47691c919fd72b01f12cd2a1d724bb40b059ad3f559238039b4657de44f14a2f4f84eddb786fc787a126b96e07ea6410063a47beba6ed709ede08662f900720f4dc009092dec863457a0980862de0561138ac866beaf1803b89044e5edc0df8bc618f22720921b56c3c15126f9d03fc188cb21d5801fa4035bf3e522e8156f5ebeaf3b4b629ef78074c342af18c3bcacfb77694655b6300f1f6252e2dcc7ca1beeb9e628c376a9b1a44ecac142b6c9a6da96c8b2e3ebbb092ad6e885de4ecb37f73be6c4ae1d86724259d65db9fbbd6cd5ec0cbf9e697a0f3b1642749426d3231bb9001962b7fc22346aa5aaac3777444fcffa6b38b94b0297b286066b6df06f73f48e402cf6e52d28f3d34a877bb61a95682e5e69056b04f8188b9a98b98fa5a18e43ff47653590db26b6d17573bdbbf8b58e325338a00cea26b4f6061142600c0e6cfb6a3a0d07b57db58efaf195757451f163aa6f710ee7fa3f576f663960fae7d7ff2c85d9a1c1e3fa22f672b2a31491babe661e2d0cde441e4e19f604a329a1945d611da4612852303caadd1afc8b45837a38bc430c5a92fe234b91b760f0953ccf984de4f151a0c695c39eaae773c3bbca5c773c0fdf00613a18efa4fa1675fbaf25788743c7e1b87fd8c58e5c94fb93fce3a1cd00171648a51c4f88e2edb09f1b8fe32a4db69792031b588777084f1939342d30011ac4ddc826a1648db66e2d4a2385152d6a60e3753811787130f984c52212ec04ff38d1d12b46d4489374dc2ddc397895cf0e227e44cd3e9cf061b3b35c774b00b187f8029b4de9c4ed1e5f174be4eba6f5132da82a1226c8e57317df21fae30f419cbb618ccd18ce9bae2f8dcb25e3aedc3d6aadf18add4ced787cb0bb45910dd5f8c65e191ca08c0965cd29b36ad98f171ad10ea360ac67f768fe8fec52ab09661775851ef96682fbbeb94550b28d061ce2c6b5b1e5566be19493464086260b2bedd7fc8b3ff07596ead3726fe1c91302f08b88166af5a30b29933464aacf0e827ac4c5e4c64e18fa44b9cfe1265da34f1ca61f10c587b1b4831040125fbf0ec3dc9c29aed0709d0d64e7624ed71f154a1ae624dbddba55185b8b6f830f14084e944f9892089ec195b46706fd3a45e13eb343a32435318c279c2f522030d8021363948509ce5b52e2ecca023f3c5447addb2de817642d8823b15543d6dbb90c6c492578b75ef7a6fb6b68610eb46cbc04d0aa49d1e46b489accdddf727b2c0745abf366ac4dbb05a10eefa4c8b89ad9fd2a2fb99a6af47dea77f6050e304b08b140f76f496339e1508e4614eb8a5fde19e76a68a21f4260025e87e3c499d30b045d514c6396fec580adb4ec65ca487095d69209ae8172ff8bbed3391dc6a80ec33876b46ebf8b544771fedfd91c779e9ea7ff59bbc1a8b85942e8c8ba421204cf2afd357329a31f941eeef45052566c1975e1882e8abaea891097bbc628320b16a6c3e0a3be19ef97cd4ae81e9479392daf18df08a7c58ba9b0aeef68781c04dcfa2cfdf4e3f5526645787dd309a6c62b7dd6c21b0b93c73edd31323cc16803bca2ead0d6963151fb878f69cec112bf20f959fc5e98009de1209f2556ec7e02d60d82ce55721cf2c7bd6081ff2ba89fe3e9dc6a179c56e5cec6ce8baa85bd092b147a66136a5a70423661c36d6701b1132bb4b77b6a8afb4bf2c8d8d3395300cfde725f2e28bfa43e6b9e6a01d4fe88f53936e73fc7a43d45b59f42e8d4230ccf228d43120be8a8afcee8421c18a542a3c887098ebfd0fd9b3c44b71d4e8108ca2b945a828159560fcee8547049b604c198e4bbf0258956e0e7e645b527f507bd5189caffeb6e5bec1a900485c5f8541da28ad83530fef6b31bdc597a1f090c7ca7159f11d61bd877dfd5e8fc249d9b2078cc21beef6ecda49ae2b7ba3f7804780c4ba8cb54c2a2b69743fffa01d5f3e13e31de772110074563099e086bc9071722a2415ac02915553c332ae790e66fd76e83db63d7316b7c2587b3729cc6016be22a7f2438193b48495171d55a2463a639d8a0d86383f6f7869eef2d6db10088c1a19b4bc14e95b1e60f8f241acea5617d24cff9bfb782ecc81b2811e97e0edcc0d177bd2bfe934fc21e882faf65b3eacfee902e853c925b1031447f877c0655f55a69961d84c463f1a9bc87f540ecd38de2acf1bf7ce2aab818503d79640b63a7b80f31478f0af37b48161ec6a29210bf68c86f30be110b3a372149e4b30fd2a6f7e2f9e65652b0f24f42db4433a1e30264ad1ab34b6bf34f2a690034650ee53505efa2b6b2aac541c5cd55e18a362f48b0ca8248a3f8cde2cd13b70a0406f440dc0cfee480d0b1428635d671c1abebaed3f59bf7a8c4e033d6582d05770be91504faf569cb029a0913e832b9edca34bdb5d9e3f6eb1e948df12c901dd906d22fd64388e4016ad3f56f54ce28b2f5bd4d651989bf5b213ba5ae2ea83156028857dfcdc758b6e05221afa2705ac5eb4f46b1d8aaac34f2ce280799dd2ec707236c20df1f71951f589feaaf822ed81807064671fedce0123b7520a07bd5210b13c81b6bbe5261c51c52c19c23a0c598da9a42498a059c454bf921996cd7860cd542ee0d69b934a6ef5d9bf07f8679109b27cb3ce4157434a02d9e50e094840cf7660e70b5426e5aded63e22e38887c029bb2238bcce3ff6d6653eade51b1401d9d377395fc9db6387bd2e0deffc0dbea9c0039dae2729fed1a5ab9c40bc0f0c998fafc48bf22b7b2d2ef2c5fa8782a0917e536c7574c2e4cc1e5b6ce0164402479427d64aadcd801bcac35d1daadf573d239d8eb8ac155ab71e62b373fe900901d6191f67e8feadcdcfe7297bcfbeefad34ea1586de8d086a7d17dbc6c56107bd9cff11490bf9c9d679a1477862619ac421b1ee38fb7b686f27b37112c9010209e003cb9c7fabf610a2b355a4e7770e49cb91cfbee549321cca8fc26f561b067d1e4d5a246d347f9533fc87953c822c60f629d700ea43fdf31a580ddc4fc906ba4f7d1396473b1dde7d2511bd5ac53f17896a06183eb9b9c9c320dd542e52062e30bd7f06190fe1288f8d7140756bdc7395ce3d7cc32fc582d5d59dbe31bf09ab9f3d3761d54d3d47159e0a4546ddea1b7eb378808a29b5a5f458df4348dc085ac6f9e26dbdd7d2159864dd671941c7e1865033e22f31e4de9de786a4df440b9263fb5d94e7365fe3e494fc4500ad768f81a9b3b3666bd15936850aaad6b30ed45a99ea1b31e1e0bcc35913c82d516973199d58034da218ea6fd1bc1484c1093d0b07ea5069e50240f44397c705d0f0e318756f9c060f3c88a1a61d7d57a2071e2be256ea12f47005b426e4b27dbd6e0a7b9ad6901c95aef1633344cb82150ae73f69f47d8fd39d14a23f6a87536fcd1780362b4141d0981826c29b3ba24b01d68a4d5f3bb987673bbbd8f7d0cd245d0e84be2fcb646576ec9da62619674c0cc86a5a02fd68aea5844f596ca3bd7171bb6c7ca1b9c858ecda2591fab0335f0b7dbe0bc5152dafb81af94de5c4df035b4b7dfbc4874b3454e8a32effd397010df5846a88d53656f5e2687f0c4e81ef68592a98b72f4691c78f2f43b4fdd2d30d45a9d807f713d047d76dbd423223eb20598052cdf6a49b8c9c43dbb9c2d1250b1ca180c32c14873665353c7e316855738eb0becae0e9771efc6d40b3ea4be9071525755640de265576899a314b1ef06c5f8fadc737fcf6c2301fbb8515349a0e988cf0d6d90363ad4ed0eb40e7e63e3b54367967455a16de7327d1bc08864f0050f7e31413f75f5f4c5707a2e0b246b703e44ccfc7562767cdf7b85e44249e0f72fd6b79a1c4f4e1e47df0bf479bd68028d65093a1ccae83e7b078d2c3594000979687dab48863baad8279c266b1e9ee63356774f90defda947cc31a11e20d06dc4b685dbc1ae7bdc4298d082f97dcc5891b265f912648529f97ea8a09580a0999f9620e1faee899bc4da7acab72b2c0f1b97fd7613c86c68b89bf254eadca015c68c79aa819a5b96b35b265c92e7c485dc7e733119f33fede7ef4bb9cebb603113e0b7b18315bfb90170316aa16461fedf75f5da5f072dbfe1fc74b60d0fe0710e8243ddb88900ffff4949bc87de7e1f03d84b0dabf6e1ac710e5d94b1b360e28cd117df72e1150e598c9875b5f8578186555645a22a887f400093f90623f0d3f02603e35beae29f56833451a6ae410405fc0c51cba8cd32936ba65b9199b06b7ba48299454ef41d90171ee26ca81bfac2cff6413afd2daf15e7b5c1b826c05c6b80c63253a3fcbf6c6204298450f1534d2601188b59915c6deeeb4fada850703e07a4d150456c7451daaff2f2625a0c2b51e973eaa4a0bed7875ba2dfd3c01fded17cb2ac3117773b764f7bf88bc594a91857a9958d8fe0b4f5363286c95092d4e945fdaf51bde0fdca132d3cef76918ff07949666bccca17a766f1efbc80cddf79e9cab89b307a49f8cc6b8ea54a5817e51a1c7c8c2322622b83e9da0f8049300c71fa454027c0c9385dac052442d7dc9bbc26b98a0e67b680540cdea5801465b90a73f5507e6c2231215c6c982683ff1f682d66de858370ce1eb08652f0a87083b4b062ba5cc97a03f79a7239bb581a259170e714dae4461db10cf26950a7aa391ed33ad28665a50803ed4bc8ddc0e3a0c5deef15dd6171aef30b0af4c076910fab848d6178646006096df73ab0b079fbc15ba901980531a94a243814a07c901df0b219152b37e864fcf4baff91978123db441ca889d4f854985f0af29019cc58322d29ff9e9e24a212bf7305775eefea0d3174535107afc79b25df10a0665f7b42550b344a8a11331e9aa89e0d1995f6da5d76f614318d341ce5a2e250b4d2c3b439d41593c4e6eb467470f0769079d9de17690f137f223806432f3d20b17b1f446d5dfb1e48fa5559a39fe5921934bae756d4084555349a8830ca9f80cd4e8e12288864e05b296eff84edacdcff1653d71d4aa39149bac191be0649706242f00f28d4a52b6a60f85e127acb608671d89b6e497f30aa17df04ea04e1707cc6d2eac91f4f47dea55529385edb1d777ddc8d1c940e5bdfa9ad7526ef6e8050da5576365e5023b4e1157f8844a443c3a9a7b487fc2a5bf3b36f6fad2335a085002c9ea34f3ec4a619b0f445b7e0c0c1e76bdb24b4358d9a75976463d026f01a33830bb5eeef8510fbbdbca0e8a944e127c356738f59ae00a4ec870d8da670eb815f7e7ad0080794522089ab161326e9c85063a837bc95b88a3bb6d993331070724a2f4e4e0a22e4dfd5c698568d5d8ed30987b1a6640525a6a9f248895d80713e75c0ba48132858f7aa1f19d770859f9394e8632caa6f1e10c3190bded0600040a56daf1772d5680a2d12d4c775b63523eaa21ccd67c6a910b0bd130d80d04ae2f34cb4e61f4396100628a98d4d35cd166fdbf3a3775b14f9385c93f8b6e00769a9fd420af5952b1d08494e1ced6c4193fa2e11f59f687abd98e5663a87803fb283a62c8d860cd6d2f7364a07cedc728a7c2bd1eb1c88c69bf48b1674d870a484c50c6a5473982c74c05bd0fbf3f41dbc4d1dcf543500885ae2791f214b20bc5c9a12a38d264e19346f11a4d3a7bf7fc4b6b4918f4857780dcb908a4301a0de95db179f9e2cc806c99cca0c276d0a509becf743e414063b54d5027d0edc70404f49b7b95a8d69f254e230f1f4db248b17a4dd99810afe3bcdea1b930ac3e0fb27c2f2ab943433b0dec6c97367c193a6e547de3ef510372d6e8ddb9b05e990e958d2892cc1d784c002ea1d6b9e350cb05406e4a2d94a5e7055138260f0ea60822e6e42e1e854fbd83450fd6228cb32c301264e606fc806f01b2e1feb549ea0b24c423012e27ba58724599305a16d71427ca4e9d2a0024920dc26eb708c2b206ff2c1770ee0f743c0140b227119f01bf51548b27ad4650be0ce076ed0a7b460ea6c57e13713dc13cb8d5885034f96c51639d4e7b3817d01991cc8fa55af8b5083a182efd696d00c2f3f60dafcb3cdd448e6e12ccf16a8ee3b67d1a2c1f64dc0f70a5a9acd0ec1560c4078444c3caf356afbe8ac020699ec65f29abc97ee634013773bf96c7b23a0f28c446a6ade3411648abe7d590e0565aef83aaf49a3f7000311a1f515055bac75db885cb809df0fd90c9f6898810a40a8eb52934e214ab091e9321fa9fa1b3e70eb42881b89f744a423d3b841114c89d6d28932627aed50bc5fc9a086b3d5ec9174c7e7b1016b1845372aab68a3a74a3082b288480351701b4c911743a6f2e401faa58504fedcea048aae67604bee3cf64012c7622d33f00b48bdbda977881a47f7d87d1faec022e724faf7ba64c9232ed07258cda325c098466b1b26d4ea65b1b1ccffa3fac2a679d592b1e6c8bbb2ed54748213c7c530cf6f060cda105bb2b47dfa1baa5d6952af807caa9ffa6e67b907e1ee706f02c006c35a0f1dfa7eef88ba3a38f7a27fbfa9c8181a4de8c52b7598b5ebc7bdeb00f62b9d3a16bce2730d632845471d53c1f919d88d95eb05a1db942f840a24bd000f7d3e5b3a0f78f6b7033559b09a7abe01158325714109f37c92b2a6d99da4a0b090bd1da7225c767783f672b2c0283eb61ddda1a1e1d4301b114e6640b73570c1b929bc0f52d16824cf70653a44b2f956a7ed42a09e4bf0caf7c733a314b6304b88d9bb35da68a3333fea412bbe375cc76ad8f5c0d17c917d656afe196de9a09e225e4936fa2455071adad386fb6d1929b23dfb1f6781c33b09824f8f78e750e5ff103caf65b4bee44bf315cc849ad776a9e0d61ffa7a87f2f45bfc608a0070e4da498fc65da7baab53519fba0501f68b2909c88ef714be6f4e4f79f854e8707aad4b49e8788d681faa6b7e71ebf95c9257317faf67c3c12a8d02094392d0702a67765488d36df139041c051739016f400657ef4c835835988410cd1d3287f0863db49490cb9e24318d20f8d53d32c30967cf0538b48e2f1592ced8975c4f702a33531d9cdcdf3443fd78b1df226218bbf7e24a79ed31300530f225740b6700f7442a3ce993f323cbd826bd308b887d90cadaabaf002bd77a5df68743d68bf0a6bead99a307a4139bf414ab99ea7f8410b7bf60bd191bd84fe7760c98d4fc50f7242abe82d13b754cf3b2c477127481720e02490de10a328e37f2ec8f6752906ef9cc5597763c1fa7ae1fe1972fccc2a38f764bb4cdf53eb94221cd6a5a12d022c561c1224b6043cb817241be0119b7a4b095f40347054be19291a997053e3041a13dd85461a770d2c274c72d9b2e52cade0c3c5d7e5a096639f742e5a115b0d69894976ca8bbcb3c4fed370149fe3869b07ed334fd760eea68aa00104301900310ddbd5bd113f538178b32f2f102e4aa3082688acd35be132dee4a593e5bf0608304d9e228b6dd71f1e8506d24072cf23010912e709d485880e2fbc319859088ee3a7409561c9acd797f12f1dc1608de96d348cf18948d8fcd863dac5dee70b545f9c9c126ec0c3aa56f4f207d5c9998afe5d92a7a5e2a9686956d8cb9c270b08fca47302f4c712c3242e0ad6a74daf70e00a41a545f0555d0fc4b25c251a0fd5cf4025f8585a3701beb018e896001c24c520545096a3e2731af209d70301060f960ad732f1492b6a6973f9b1c55314eabbd87cfaee19adac008de884ff1c080978f5f4b8b90502645c52bd9c828b7680a1b9b2b6f759c2a0a040eb75f1090c2aec7a07f9a113a6e71a2b488aae087b0a0c4571fb7f53d05715a5c19533fb055a6e929a4bcfac0b68a8c1d37179cfbc74053dbab1e283da9821e004f710c60893554359be8790b8bdd7e913d0489643291a2d0cf21d14cb1835837d7ed52e0b8067308083e970a2aa04919072bb0ee6188ee434c61cc79a6f42929ef81ba506f3bb63084fcb4dc3657cd1ce8e3dd8ec2690bb6e59bf800f9763b92e8ddd46046d04f710c01ca9cd8e6d8a98970161405ac874a6aaf4007a2c07b6451f950f04424704f15ba46f70526a0ce1a54a0454a255dd4dccf3a93374a13b1b3c507a0124dcbf2160298b35e2688c4e5b4e34d5c97135b464e51094f7d4aae00f31cc07f6918b4a2a09287ff24ba4a07a9dfa73a068231d1a2e0a25412ee9b843814c015ce5bf24852f005f0ca4d476262b37d8d0d99fdf04f4bef46dc51dd4aa90970ecf685723861b5aec4b9ac974a1ba68f3db11c064b5a1e8e2572f7c53e9aeb8091d18db6c2230410357a5b0964af1cf257eb20c5dea2951c7643c63621cddeb02fc07f0203d1966933963616ed88bbe6d1465682c60e9c595c174627245ae17005fcfa8704686cce92d22c5fdfee01784ace5c0b1317854421866da3815ae74059794972f1c2762c56f777aa1ead083ffd2c00c1efdf4d2767d8dd2057f0ee8090f85a93a86c39ef62deb890f3ce006d4c8115866a86d628427dad2862ef27001907034cbbf27f6d01c92ab75171bd0b103c776777024b2b84c2270c6e359d30d6aa565eadc33e4101591fda33cf2ecd7989211df4a1ca5ec2033494a699b130b3aae31d647f74c8baacb416125cf140e3a4ba99bebf3e7b3244feed98490c90da71b22820ef6148bf0917866c261a969b665cec2f637d78f9651bbf4baee2b0da24d96f299aea87b2a47b7f2f1161dc6e65dc65bf63d42c54eba79d65980c60d4c8829aea5bc47234126f97dbda1c1da59594ed3df1bce82eea5e5d290358e0eb2c3609da5d34d19d733c3266bd2b9a9072edbf36e5d5c2a9365ba77dc8741081f6c0109d12a38fe750d618e3002f91a4910cb1a405195346dbd74f663e48c0c71536a347434f6910076c4ce30225d0ea03b5d81d443e088d16f6d18cdb70608c9fb9c03c45e418cb149657b492d06ceb10af647debb04cde864b5c20c28430d475cd2604552a1fd4fef38a3c6ccbc30b98832b8b558674faf1c4080ad715206fb0934d13d159fdca4932d830f98b8b8bff590a7de5879da054f17f8db1127052a9e7fcc069c31db53a5f65497841c75f0802c0a81b9637798fd2a8e9ddf2902cf8fd6b9580c7f3493e454cf145c63698799594a65c29b3a31d1579e658e4b0357d2ee5b7da8cbbfea179a622302cf5c06104ce24fad40f9d41b6cbdf3264407fe833c33b6d1e7d75f5a3a7ff72ac5ac10587299c370ebca9de479a5a344f704047721e4592bbbdfb21e03fc694a2bff62deef1935863e4b7bc624582e854e08a89d66b54418e407a9155387bff7d89275b3762d909cbd7a8880f3c58295060d4fe1657b5591ae78f20d0cc8161b13c45ca8d7eec600e246d3dbacc4f2f7af0a62dcd41dbca4e253571420eac293c2c63b818294537561392e2f3b13ac33f20f5ff521a476dc8caef82c5cb51e39734f274c29a789100af7884daaeec02453096dc7352a3bbd6aff3fbc3356f801095d1d6cf7a5d813049841d94d471aa03108583ca7d9de1582e8e4dd45f9f88fe16713ccbe9f39a52ead09d9a395cac7930d6c611eeee5b6778451405e9ca09c4b9bba9f97bf237fb776427323d7d9b926045b2d5c95d3e433f6531fa01d34d7af45e6e2212fa712e2164db646795d83eb08e80d35ac30aa28ce8932ec0d73942302262d928782e8ace0486f986165a25c08bf88f10958e3e2a474618885b492a5d7eeffb7771e3e6b00bfe8da3efff57802e52aee6ffb4d7dbcf6dadd5acd4d0b2dc4a41982be90af45c4ab1bf9f62f030f8ff3cef2a36948d8316fc800a8743d2325dfd0e1e07e0b4dad3687529ab5ab0e384059fb6418ea8cf9e88379f8215a235187417d8a879cca2a685858e21c540bb359158de7e98740003b5a1b7f88a1cd2e9968dfd474795deb2f193e632b9202f596e5ebcf0795563be50ececd16af2cdd4e2eae8ec35b8d8c93c7a3fe6d7b0f1d8239a05b2b2cbb675cbcd268c933c03b0cad7447fb57670dacd54d32fbfe04b32e1da6315396f7a0183ec066097136a54577391ec4c549d06dd4097ffb490e78c4ce6dfd0d93c236c8f337d90e2a926dca75cc1819867ec70bcd44571f5402cc1250f2001782035b22519dc75e19104c8c0a83d6dba13904970c6a6908f4082f269fe6068e05aa6f42e1a7d48afe822619d2ac3c9aa4241d0165e9d53acb01c2ceb643e1f426379c847ec4f7af4479a1c4bc71c9d37df76055ef0ba8004806c6d7e9b07a43f58ffd53821697e50e28878f5b8fbb86fcb54ac221cec6cf6908 +generate_ring_signature 538177edd83f79988face73a00ead13d6c0ecb1db4f365b2e3f9568ad58cd8c6 ae14d63deff469f3b86d1fd13941882965b067bde10010b478ee30384e396528 1 1bac005e6622fd7fbe0342bd3d57b4725df41d352eb430e3b1f8c8cc2ad4b394 29795870451d36ff4a5a0b029bd4f6c848d752eb86cbcacb32ed47d575a58b07 0 4e86c7534eac9d89b3afccf8f39a1d5fc21c9ddae365874c64e355130b936b0c9bc9d2d8b0673fb3478ba79248890c58ebe8ea8eb8eeb8534c1e9e414d30fa0d +generate_ring_signature 76a18f589bcf131c7019665977a2a898f978ca45acec8ba029719540bb8cbaba aa04f0b99247c0332caa25eea4c9ee06ab9bb2b6ad615795ba3c83a9f69ef148 4 60277b45b9b94f4004260a2dd9b2682f13081a1490cb873207ff96874d296ad7 9d1caac63c124b43c41d3e4804bd146fd00ca6eb399a4327df9335e74e56266b 9995916945798f3a838a968e36a030e64a240737da517d6c29fcb56e2e033a3d 4e43805045a899631b5dcd110a01e6ca522ba59d3192f5eb89923f64d07242b0 c021f9ca95dd8b1ab7f71ed6c7ab474fbfe889019f6e9871c9282d71d204ba0b 3 b4d1eec2b4384d5261e4f1d4a954796a562cb2bc5517aecc2f775c08a77a230226ae18efcce4e136943aa37bc1c7221b1bb2b4310c3c08d16dc1e2f1cc789d0b39e043fcd39a011ebbe4a97099a9728a518c5959f1d3c1b245f012fde9dede0dbdb342707440cc6e9ce51a0f3e4785b657d86b89813f1a5ca6bef629ead3f30babc3b64ec83cc4e69ba1eb58944bf669d08aebd1e4cbb86db2384d48e5d9aa0ef7efce0d17d24ffc5401b4a2a42710ee66ec558524fae93413b552504cf48503c2b885d87d66bf9d47e9cf4223f21119d104ec58d32b1a99e4f932a5e7025804301e3e1d8c26e1df51379ce39b7040f5dbabf5d50b1c49b4ba2046e9e9cf5101 +generate_ring_signature 0762e3135c801ffc2df0714bc22bac52f23089d3698fa14c4f9dfd1eb093771b 74e24caacc3a043427829d355cd2bb7be48b67842468b2d4b4fa560dfc8acf39 2 5012d49a41246dc010d56d3fe660ed83508003018b4e8057b7593e0ec75e0760 182af5d4d1989656819fa427f8e31740eadf10e75011bae1f2048f7768368d18 6893027ae7019bdf3fcbb002a55ead85a2b0015f1c77aac5e121dd6b313c6102 0 b95934214758a003113d22ed1106bfab1736375ea2e8ad98050e607ba1686b0e0428c2eacedb1a3ab7167205bc5403d392278e0484b03c0634c723fb2bae0406dbfdcfb893a3de7fb3c38bca750bb0fd5941d7b2108633eb74018aa195f98600bcebe7e1204a85198c2fbddbfe10fda46b421c87f6f68a873720dc93953efe03 +generate_ring_signature 25ab435a8d7d8b961db420063e1ed23176e030b3ddc410458790712395394ed1 85a23cc0710d2b5fcd08559c65a6324547ffadaefb1339d42eed13047ebfa877 4 c57eadeae6b61eebe9f2948699da43e9aa512a404d3ee816871963598c98b3a6 fe72581e3c2936efd3a1b5e52b2e791d12f0308e77b66c8c105e16a08242dc44 cc718b73a4888f48b78e81073e97e01d5c6def07d2f7d64c6e5553768e42a392 42b5bddbef25293c5b286d2369073358861c8018918ad3cdf8386586da6a6aeb 96ecd44fbc3acf6b964d95595c7abd839acbbe3610da1dae02bc7dbf00fff202 1 70552f755d5b7a23fc3873d6df9c04d4712c6868cad526cc42f85f418c8daa053434841ada928bfeeaf99fad80357484a96facbd480e77a96addbfd46686c10f48c4087167c7515383aef4c2e9f9ec24f094618c092df640555029070253460ce0e3fcf15f258d540c276e4a20dbb04f7f6c1f2679951468704c180fb544c8083115b9deb5a512a7161ca8e858d5dc37ec96d3c8cffef899a9110b25dad12a0983b92c3bd43a85c463bb745cb724b10007f98bce4ed3e512dea1a29fa9bd0309ff7efda12c5791fbb40a9c1f3d401eee4e9dc551378086721248fa46ea4f440b2057091975a65d8544e94c410d1aa2399ddfcd1fd12fe77f11d314fad2c29008 +generate_ring_signature 36109a906300b9ff75f104ec19ec0b8d10de8250120fff5de9ae6fd8261cc0e4 be90add855e696770e8fcea395d6684748d34cb0e690143713256585c7112b8b 8 793ffb18544ac045f94c468c03ca2865d86130bbdca553823494e3d0db04eb8f 52c1387810718980db3d1f8d062c014b86f786d4e107c4990e8fa95bdc582e1d ae76ac7f09279ab1864c4b0011fa059be5d8e91bd248152fda52124914a225ff 6a20686c99c341c66d0f3715b4cad868223b2a3a76bc8d2ecf569c47567e7289 0a75e6260eb6d3185718aeaeb28a267bb38e48c969b225ac911085e3c891f353 cdf81271e68483d5635b8fdd95b6389348d362ab23a6534e0ee9431e07a862fc d40af8baec4b7c50751387bdf7b5c4901bbc786f5bdb382a5fab2a6c9978df0f 99fe62d4dcce3a753f64b5c5a9af001155d320560c8156d66a3e89eb65cfaeb4 40daed68bb92b41fe27245be6ac23531043d49e879867fdadea68208d9e01c0b 2 1efa17caee8d44417cb5f3b83f87d44427062d9bf3013e5dd569d16888d4800b6f374bb53b1b67968ed89a2043eff4aeb1671cf01dc50fd202d05da1e7810208c32b7362e276a24fd5ffa9fe8553b526cd3805ac929803f4feb2d8110accbe0699a4be33049281d6bcc6016c2956f350e7058a6a941b250166778e7c2171300ef6f89c7d2dc58d48d96d1123f38ea833729b9e1541e00238d2aa0cf1d100130229055aa24e965fc419f81290229410c19c1f668f9633f14ae77df7cdd0c803027a61308bcbd2b3010041b2ca33d039d3c9027da032b355bc4efe30933facfc01038f459739bb5a3703fbd7251e15b0f900572f2e31b121871be7fdd89c8fc30aeb3d18669ecf5e9a8470aa4fc768f10b59ae16763172c5b4428b9ddfa906fd00e022f69137efa5307b6ffeae57d21c0a1e8bccd6d618cef7e7ca823ac143040d96335e6c7684d708253686dcb13c23837c3a7410126cc6721274148b451bdb06ffe7c94adb867b157ece494f51abbe060a52803900517ae39524fa5b42f9b803d071828f7024257bfa66961e8ef9f0e68c7db1d1a9246775f02af5bd1c54d40fda731d7d80846739b163e1e231ac3fd8073b2dad678ec0f67035358aacd0370bb3feca62d2d2eaaeae524ed4f217e19bdb007499ad7248e3def590757b2a4005fdd16a81257f642f463b2a200d8bc6e7e8e6604cd9a0d59690cba2e631d63a0b +generate_ring_signature 5e69f24934fe54e4c9e57cd12a0dd0ded8ef9e48cdbaf836d2302de007f4aefe accefd8d0eb31bc0864a9228aef1366972386fa714d83c7edf67dc9b78abdd25 24 20933943395cca6f661bd9897726601770d54eed0596c5d89cda5473247eecd5 1418e9ed86c4b632ff7ed6d371870414e76426f241f861f74586425c68946242 e95765f36ccf82c45700ae4784927bbacd66562801e715a121b6e92f9a5a8734 45b76316dd87020465c5ec0d1dbf6d2d31d3fe2fe0bdcd2b025f6fc40eb3a5f8 df0e8dd3754133c64548b24693b89036b206793f76ba87c00a70a8b515c78db5 4212ef6d66c31d23b12705e09f60b4c7995be3a712ae99149bc81697def5a34b 9c1813bb268b6374589ac6e12d65074902668924b16d7d980417279f6dfce8b4 3c09af858cb7fbbe6c1ebdf065b172f968c5a0f7cecc96d73781264aef2db7a3 08c1bad10a73ef79de42a43f4ebd9bc8b71ed5ee4dde0d71996f70e494821347 d2f9f6cda9ff0e3b434f13974ff27755ea29faa936991a1b6dfd50cb2c8abdf5 218c022af244df65ff5d3085d391d07ec3ea53641d193a9345ccb55c7326a80b f273d1a4643e9b405008e9d48ba94d53f0c2a6e3f18025f874b064ebbd597c0d 8be9cd3323fdddd931c84585e6d74a43588f25e7c4ef2120810a675025a12e3a e91c4000fa4e7a48082ae5084630d5acfb260454d2ecfd6cb93a73612f252b18 3bc1a1de3068460d21e93e9cf7e2933612fe34383282b6422553c3a7a668782c c367a3388a2add9c406e3b037f10e33f70e14275cf92ae2c37c7214e5920cf48 8903a2da247b55c98496a5f34d5d48ca94ac027e8a3806492321bf2d4498ec38 4147517a40cb32772e0f497d1a9b3526b001c8d87113255f69975555de7cacfd 05706ab8f0437757bf583eace8848758b093c9e87bcd5b3e08113e437d1369c1 9b074e110c2ecc3910407e26bdc6843469f05dc7f8c4204bf1ab52755184042b c580f7d244b232d8c73c3ac6cb73409321ed843406101927c080bc894dc696af b41bce9cbd80b07ad4fb572f5eff6ee8d395958f7d53a216ee5cc1d2e0e7445a 30c9e2efaa8d3195ca1b059bf516b7bfbfa6c29e9c7fcb69c11c65ac472c299f 533612f3d942e99164a1327ee60a8f6e19ec4e4e5bda187dbce551e5ce27d11e 597be362ee860b3b50948c534355a34c5dc40ac16e5cb9d974fc8fae2c9a1e07 12 b3cddce90f809ecb866ad923a5b255e10298b4c8194fca56bc6afd3fdc6d2302f5fc45ef486bdf714fddccc81987932fa6f691c47f613527bde4fdc5112581033a87cb8af0987c60d886cb7997c79437ca93432d5db6baa552561431a8fdfe02ed1e0fce8a54bc7d81d948ef70f2b47b8ec4c36f01d364da0ab4ff87ec32cb0714081360a8050b544cf47cc499a06d523ff10007ba031a26e71b705aea77540ec52001b24694b4d1950012738377709d341eeff435003e04b00c284c3cf3410e7387ab80828b6f399a03bd4730a066d859c9391009d50eaf421a6fab2094230acf0207fd8f690b404af3b284318429ad39208b6cf12c3d6a93d7a0e710a314074ab21e1ea1ced2043517db3395a996d2199226900f7f38b475fdcd07ba6d5f08f1ecad206677275cb49d6b2ae1ff9d47da5df61804d39fc802a0050b1d7fb50d360936f132e4e781eb2c9a4c3330e428151ee0a6b4f84840f791727cbe19f803fc35ff98d2c85550d700d83f918c2271b1dfc28c6765b9830a0eb00b4edc4a07bfc83831eebaf1068d164642c2ef7bfcef45971912bbf73b46d7fc99e01c3107e364cd274737a320736dfbc13a5501c70a124189f79f3f7d4d46d3c44325430cabd25615ae4fc6845b07291cb07e4d97de80fdce7e1cb9aaf1ebc9de062fd108f0ef40cf79977bcb66fabe3411f71ded21d673e647331f6014c36634cb77ac06d7518ace6eccfaeccc357a4adc183437f05e3bce67dd1b848a61e9591ae50905b74e0096d330a7f0c06fd86f617addfccf01634b3b30e26f67f26b2677e5a60eabdab5634255426dfe682df38ac090bd42e5139baee04ed6c78f8fef5cea4607406adcb5eac3b7a8f63cbd6a8c04bf427c0cf5ed90c811af8a51fa9d396ca700a5add4a40985cd9f959607a6a0cb50fd7e25ff6d89953d7e85982a5b1ef80d03392dd99b5a6f15ad3c674e67b0b645c3f714a8a06813b5eb0a766367d9be62031a9a788343bc4aeff833e8ef4547f5393a6d3903ce5935644020f7f7ca5db40a9e0d44716a42af0fbd8c8f1ca2682cd72b248d5c971f6456d440e61c8df88c05bd330569a945415c0550e90cec68b771efd1e915a5191b9be18a57ba24b9700b350fff9af9d277a3c5cb3acf1b399140d02ac04d467742ad29d17222b93b5306119a1dd04248ec776105e7a3f945a3ce67ab9ae6871b5d466d70eee5f8bc8205d5924c9b0a57abb87acfba871c78002e5bb333a94bbda688a8f0c99cfa639a003ea94302ea59d2fbe9198ce230cb88ee6b55898c51f4d0c1ecc50380b1efe406662e0c7fb80688e0c9650c9e54b6437688b95de6860406a8ce2dfcfb6fdf0c0e3976cdfe3246bc26fc4fc82a97344a4f4ab4d8983636d3559471e609e81928034c19ca0b5407e0dd64713b698b3b8bde163299075adfafe2fcd99fd782893a0c82704eaf49797f7d3c20935131ad4fe1fa7e1ed94f3ca80b983eed640a89dd0c606e86ff30b1184aaf1de5e55d34479d930fa1ba2a5367abc03a8b92ee3c7f0ed9e4db76ff06c68acb2bb1b228064dd6fc9cb7df2333537856a77afa330f8f0b505b571f2a5395cb50ce6acb19ccedee7b1c5d2af0c33eaf9a408e8e487cd901b0424d3157ad145af80571cce3e06b2f2a02a37b7e4a22b582a84830337ec30e81b49c1cb0d6ff18db26d517e1c41a1c0acef119d7ff8bc0e9457613f5a9e30fad48551667ec55115674a1c5c71d04c20fdbc1a96d450bfe0c0a273824bd3f0f2b8994bf47a95e9d0ce255f3ccd4713ea93eed4d5eb840140c2558a24ca2210ec5d23b00da2816d2d3a3262742f59b1ca15a1f90ac944b826a31770dd8bce30aa106a8ad55c7370ccaf159f746baa62c41f0f56770240d8a41edc5803dc18102669f47f93dded14422b715b34262f84ba3947a793c3cc1ef1a84082f0d8b4b0b20966a3d38209d93d7003ac9c4c1eb1f3cbbfa766dbfc6e660abb1e87ce5dc0daa9b022d22e4eea6205569f7633e2276d84223f0c9beee40e70bc8438626600694d20006119debeb36c56f5a0702771902fa326ced613a16499395d5d80f310f8a81cdb84511b7f76b2f0e165a00bf501a230a5448b1e7d31bcda28ef7e4c004ed00ea976b7a8303fed1541e88c58e23d3b95d1ed48773b8616b139ae23f0607 +generate_ring_signature 853f13b3b779759a31f5912d07a4c5602944f1ed5c8d8c5a43a9035839057d88 92fc80532e074f492d8b6686d746563eeac9439f52573fd15a2eb7bca6182478 10 81ab38cc62a204a3989b2d2a21d4ce521002358f8987a0d446a85f378cf4416f 83638477c8ca388b113fcd16dd1e6f0248b2f8b8449e7759047b8f80c806ca6b ad9916eac0d92b326c84fc6cbf4a34de5306b2c8ebf26c20aa7e8379b3906c3f 8eaff90a022dfef082b02ed599335c1ba7fa22188ff4572aa6724e59d5861bbf dc92d33d72e991e10c31e592240ba0b9e6af6b1123ac173eb0585b5cbfe025bd 84120edf660b602d640b832174d8f56b91333028a24ad0df1c6ca8d10c79499b 9a079f481178454ebcfa25c8c88286b87fbc312960db3848973c1b90608ea6c6 8505323775eef2e3610a21060cbf05f3550b56ab276951f55d38d526738fc217 a191eddfd69a258cdc396d6ca1e17c188f3fe13f973eb304efea1455cfb0f136 9e096bc49df64f5ce985b39ab69613c7fd3b3387b5a5ce4f4d6582c95f75aa2e 33e24675d1c29014fecf9f4954ea8c1f135c2f75ba0a058df1f64db755635c06 2 94fd7cd566151e53740e7bac0cf765f60e6e798850f92f3f1473b9a704015f0dd4a87968de6f7b285875b160e18c3435be260fcf93025043f6962ae75300e50b4d478f941b36d1c9ca673e778ba26e930c6b890b8a8b41fa0e16d53fd4247a0c2832727e0bbedb29f84ea07d205b60c7eb71a161875271b3365d60f98456fd05997b9614b98cfdcd9a5e057a8c530e7c1291db2bfbf2b5bd4b0ecf50e7ac980a7f7886b262162a0ae91b0485ef94aecb4d945500ea183d6adf4786394780cb0d5bc808b6a877df5c49f803a15bfbc5f721895ae6f067d1dfb3875f1cc4af260c78c56e7316d996fb4fa08a367c70a23e60437f855c016293258fd1ca03e04c083b28316ad9f5c20178298af49eb36b004c2612335b502a0b05e50ca20ba0ce043e8f2187df5b62bbd51379cd7ffbe4eeb4ee480189963653cc967154b5dc060d6a881a727ac6b55b235170f7d28935ab74c376a8b5b4cce95544a99e7855480fdccf4e124eac6a5f72162cadf3ba258f159101cc9521e65231ab1d9c44e35f0001e493e7d42a9333b139b320d2074f9e024a8e2525d50726f334cf21625be4013d2d62669714ea3506b83cfbb818d7e9b5d779a68c47f5ba513c83539441cd04287d7b218d26907eb9a73b962c343dfc68e3375618e1109dbec9603b9dc2c2094b0a5f9c8086882e3cb80c2af5cf8fb528ddfb0cc85bdbf54fab39a002eb010cf3d925a51f34f695ee0f1cde9b3ffe47b382a76349ade45806eff53297473c0dbf8ea142e02c2046349d9e3cd23efd76929a7ada269246cd1c0b8aca21508c05f3d8556b65e67579c5cdb71330e050e653d97f86911c72ee7f24a53bdaa92a026e4ab01429e75e5ac0114022ef5933f3d485e6fee786c205481554587a586f0e +generate_ring_signature d121fd689618f41c0fc3bfaa7750d366822614af28885c58688c7b67ee67965f 5b98d3430504ad3cb48f3e97083d569140ee4078e46e95380cf73f9bcc3c6b88 1 79bc8613812f852b26daf7bf881af03ac0994463e7261c36d13ed7bd8d7e4c9f 372cd5e651e087119dba89f988ec61fb9e87a32489d834c73b55fcb02753bf07 0 69e740e19dc18b0498ab56d7955859a3c7193652da36bacaca6d978bffa06106453a891edcd584eb0ee7b5f2536aad0033cd1f534748e4e945bae08773cfd000 +generate_ring_signature fdd37e93439e6ac56a0d70cd655a84268dad20af32f0611ab1a628f55bf3bbd9 e025806f8702a79270bfb4ce0b4aa11762fa5372759bb94301dc091cb0602f61 29 2e117d928d39215afb5df0c27b47284b78b23f014c5d1911292ebc9a4119e92b 7d2bc737ec48adfe261589e473307cd0717bae097ff3b2e604aa14856c47747e 056f26d4c98d0f34990ccfb424b5abeb8d127062ed36e946ede0b57d7fdfcf05 8e1b0280069e98da73a69220659788f6bf7a41bddf46035d1ef1f0482e008ab7 87469d9741f48579e13ec640b8ce23f54f1e4a40de3a10b53c156467a781d4c0 24181ab8a4386f7782b24cf3b6e1bfff079088d961d9f0d29e765a0d4b8c916b 2e22d8e4426031c8fefacd99fe303d4f3ac9219f27f5fb66a578049cdeadb7f0 6bb7acb55eb90d7a00af145519e5dcf0801ff56077ed57dcd4a5d12cd8d79654 340940a628fb235c9b2791f78a09184280ff0122a05c85d4dfe0f1260db990da 33c6b21a3a6157fa6815715e8b8590538b1934a02aff7d918a0375bdea3fa281 18490dea8f99a2410b69d0a3004a4ecd08988b0733bdb2a34e871d46af022521 342af8dae5beb97535fcd28237bd0a3800ffca04abd43b80c50d1945c324501c 56825fb02b0430ec59dca42ebab78c094f6ac2991c34898dd58f1094d1aa2885 7f10511ac65393802fa258a123e7bce8b82352620386aef6bf8f4f65f4c86817 98f095c74fa2450f5d63a32cf8e8b8ba913da076a07d00759fe5bf939bbbb113 d95cdaaa51c8c34f8836c1aa321475285e94a6673f4dc945e5371ffb9cfd1c4b 3b242f5d2a39b66d3098461d5f8e726b1794cb403405139c24252110811ebddd d11bf9602c106bd73047bcd066b647a9181d8be3bb24c34c4140d3bee012ab49 d2c8e00aafebcba2e9c56e33ff8bf7a96d1a2d098672a70ed2041a09c41e4b7b c71f08f309fa9bc892c13ec04fcb0be0e2ca2b6c5d035f36bc7caa4f39af3b8b 2c1829664244f4b335660e2c02fa42b12520e037dc4c0a6cdfd5022e0f327341 417fa7440ec9dc1096d36f9c60140dbe915f02c4e35e1ce8c772bf80818b0d28 80ea7bebfba0f528e2826de6bdd12867eb8eb005af06bcbbfc876712fae71fe5 9722374a95fbe5dbe309554d13d29bc305d8effa5676f6a0bae9721b4831301c 1f4fd8a4152679727bdadabd7d36e40461939076780bb6f50f12a22373e2fd97 253ddf5ee993138df2089c9a0a120f0a379a005a3dfeb265197aa7654ef44604 5d64ce3d1b775617a9fd57574e88a5b2c28e861fdb443df1ed882ec1992f71ea cd1320602de554fe4e4257ace663bf0058488658cf6b308ffddfe26520c4a6a3 24ed6285e28c5b18831da18f05b7da03ed3cb0fa985e1c75c93769603e6f7ad2 dfe3d75fd80377418d2b0dbb1db4d419a7fbd3c20c8d32773e47dc22dfd9800b 6 efde2905f5558d94956588311d0217b7fc54b147e02892c6ec4b0ff2358eae00a1995f59edf3a325a4c65a221e9c1c7ac795e485a3ce2f178ace51fd2f61bf0500596181f632fe32ea242340a80a0791cd4159063f09f75f9e103fd89868a80177de9b4157ad515296277a8fba36f9e01899bb31eb61b10d6912eabeea9fc00cfb7ecd29a6da1110be6e3f3c832b4200231b5d599e63770253c49862b19e730bb7904b6d6f1ce84acca0e553a7d5826f46784b71c5601ec33766eef3600ff007239d61435757ceb1c36f111a5153bb2508d6cef09f0605fc8295a2fbf0ef120d637312573ec6fd3c128dd4090aee377378099a27e2da78577e25a4ea4c276d01d662752c32b1592420259cfdf65aa3b3d912fa7ed56c94dd957d2d27dc58fa0b6298a6359ee4dc492bfff33921156371a28537d3f87b1c2e6c10f5b7a460f90b3981a59c2276ef99ec8f372c971f10bdc250aad67d4fe6f0a887c50823229702ecf78f20c196392a6f42b00c924264f61d0031a0c17a37a30dc1e5f64fc32e08d44e456beb178365a76390598a63110572e8e06ef84240a3bf9c34315b661201f98d6897c47a935905dd5b8446c5410d2aed9cf2359ef0ef897198d16b585a0c7cdc142b5069bad91f4d9cf3237f18c2eb063df57c8cb05bb9f509b24a268506ba6a6c3ae2bcb4f97f50207c4a6768df3324f273f5084ae94d0c0aa8ce2b9c06a5fc82eea90896cedcc7daa59d96f5cee9723542266ed10bc72dc945be13f80d5474eaff22b6af3e2d7fab8ad84797f1474d01afd382ffb6e5ecfd1f94a1f4005e9b44e4b98316e4cb80e83d9cf8fabca10f6fce0b57df7faf68f249c5ba2e07fea131bc7ab27744230014964ff55b01acdef41d0f5858fc500a2ac1712ef10a269ea91e4741824cec9dbe27fa11b02160d46e9142425787e3d50de9e7d50306cf87ebd5f98fd7e1834ee229956e36f9e4c732d247e56dc66d65fc4be5ea400a31ba08e3c75f38c6faa5862664523ac5b855c40baba22d6345bb800facf75a026adda86144b550d15b2f3880c6fe5d862bb8acae22a1cc57c439dc05b91c7c0e11204f64529ba9d0c1467be24d221e151492b6bf90b54ec55ce067be80536b0154f7804f265de7b8ce9efcb0cfaab0ec148b64fea21e7e230d8dd511d78f7e0b53c9554e6475395b5883be2b15684e637dfcd33b1540ee1d42c434b335182106bc48800665e27254b338c19588b1620f768fea5458bf80f24ff667cc7be356059d6a80810bc2b499324806bb89d501a077520597eaefdebd14462b2912e215027c92cb33cff1dd1e995e6c2b6e7149d9957ecd483e7e35793947df313d01800a52bed35c16b50e20fbf799bbc956ff4da05fa1d9e9f16a629bf10e304f8434068169e04959cb2813a672f9c65ff0806ebc7d33428130e55d1ecb7d76b64e5c0b35df0c330ad2dfa2367a63c9cc8a40e33b07d03d26b59c8a1bb130c4090c1d005bf4159df29e8575a522c7d0c9d3ca6af712012f6a5cf0cd82eaeb4711d3c20cdaa848dff524ae9142fddcd78b1c50001e398162f7cfbd822c55ff5334905d0eb0b3f2f1cacd6756f906a1400f5564fb5764e75aa3ca57d827e16b48d3c03203eead6c3b9db23d9c9ba9555dfb3b7bf51417ac1afced0dcf291d83377cc13b002d7f39484c4ae398ad12d15fd85d73b05572522206b654891de330abb00da5056576aa43b1924989390883350716e8d33ca9fdc33ab250b3b60c5bface8a950e07b85593fd456311a728d73ac32a054482d960e7143c9680301a18464d310308f1af1d823b3c8db2b887567a7956cc795dba7a21b83362a012cfdcc762a86805ce7ee9ae88d32ae5d1b8e9b970633792788f3afca11eb64351e49b77160a250d4e18f05ab2a69037190b80808b8adbfbc603928c4a4be9fe236b0e608f873003399d35f117f277d7594946e2fcd06d6742da1c120e5794d1383e08e220550306c3c92e41f19aa2373255fe04f5798318da420fe88c9ef5e562def50563814a0bd461604574c6fd89ebc751cdba8e995652be02200dce75387277d70a5fedde07dc0c9e293a8167138f054657ffcd7bad8c67d4ce468365646f851ae30566010299b748b9d2d1978757f6104546c7facddd6e141a6342274788cb27258852fa054fe537bd5f70021f4ae1473df685e63bd239e7e671647d16c9b2e39326c2bd02d309ef9fc2845f9ad48dc05abac407a796a095c405c884bac4b4cc76e039420e1c89b4c6ce396093ddc13722068b4f93a719349407ad71bf7904bdc6da73d805e003750832e93e155cbda8f3877209c71cdd2a5d5660a4e5c38a41e287bbd30e592750005b36e3bac4e5658b5c60ee13dfb46a70f1fef1180c840e877bd8480409b48d9d5cfb789914a602b0fbff3a4cbf2be7fb8ad2503eba7745c939b2d908ab6b5e9dd813c58919be30c13c98edf94e28cb210eb1d1d6d913e19a92d8910e58e0d86212cccd5c6d845eabb3e5be459c1530e07116bd2c062325df095e370dd2ec8889544c4954b1712816241808139c1c39206c470ba153cffb74f03f8b0353f25f42d366b5c0a51e826271a4c2db6ee6ed77773dc797fcb869e1d69fc801 +generate_ring_signature ee3c08fefa5767f525c5658a20889bbd2247f43bf55b9fa841019cad45c8cf63 461af9dd26b2df247925c988b293dda0e246d0330912826d181d073bc8308dc7 6 06d1b7e38702f8751e366e8bb2aff5297eb1e9cac0462270de9a0dc119c0641c 9d3ce4a37916cafc0667d0be422d2f66b6e6ea5d6295102601b957889a649034 61c2fbbe9edbdd20d8c33d8f504924d50500bd7eb9e0007f4780a16ecfa47761 00325bac7d8533a965639af230bc3450d92d7ec5fda952b667c34cbe9e4b2590 94059c06ef76168ea6fd1485016d6d5728339f79b624881fc1ccf1cc531a2d77 931d72596bc65642598b52fcf9a914ff87403a82c7643d0166bf3841094631c2 f11f7f858c8f9915acc8e57fc1730822383b9a4f96bf1292139ac913a808c306 4 e6c1e568195aa22865a5cf6b1a6f3f5323c86fcc2fddb689d72e886618d8cc0821b657e9ab47ff9b62db88f7a841ffd794de51b8b0796407442b305ab5950201788c08789960c13455bb1960f79435aef19e2b3def2f3dedd41322ec0253ab0322d2971ed2a5c54299db591c6ae94680a5326ce3a241fd31844f028cd0182a0edc8f285c59c5cb9b1a6bc455463d756d895db5c0995ef520db111d937ed12b0dc8a40a84359a8f9339aaae621c37c529bb08ebed1466968b8c4eff3946ddda05146a907fef84bffdf36f16ec44a6fecce9111fb552789a809f91c771b765510e5d0a350d614063d3247758e5835a5084a3db14853507d5277e2bb148e614c307c3f0b830e9d7eb7a01e49f69ad692b0c7b576f313faba6da26f88aef42f81800217f2ef6ba7dba09ac64eaa8c8fc7de5c01c7cbcc3f1e37aff640ac836932607e03834f7e407b7ca2130096cfda3ba7009164382d13c9ce278799022465424099628e07758bc13a26c7eedc17efa4dc545087f2c1afc8d7945ed4f0565eb6008 +generate_ring_signature 6c7b5743441e70bc28cbed1acaae2f139cbb608bae0143abb8355c84e51cddf6 b180b644b648758e876dd53bb3d1c9e13511a0a9966ce4d5f07cb7f5fdf10608 1 385f8b220f357e41917ac54d7e8ea00987df6ca6e6bea6737e4b0f434c0def8a f834fd039dd1b107bbb513da568330124c99d85b6da61cbdd52d68564ad0f809 0 878ed8c50c010582de0784174137a3da388602d4c3cfd25298687559218ba60294694e2cf494188c0112a1ac34216b9070d9e590eb4b0f0c409f71ac25dd960c +generate_ring_signature 2b4ce8c325ba95e9656cc1eb11d214cfb42d8b0dbf016d6880278037ee75089d 7bf5cd37615cfeca9dbe1572295e647c7dbce6bed0bf7108affaeafa9a76d676 1 d469610fcab5d9c8b6021da38649a215d74fae4ff3a7d50eee6105c67627bf39 9c00e917a971607934f0fd42f633ee2dcc27e881a347824849d6fe8cf789bf01 0 0180f50aa9d3d51e8dd31f2e02c64e877e5ad56a0bf02ce8c5eb822e012cbe0eac083cffae2ce55722543be1f4865c7bad60064d511214f568d23e2d1814dd0d +generate_ring_signature 82e51d2f552f56ca34155135251b253bb6f973376f1028e6616799aec80fe1b1 0fc62aff880994a7580ed51bed71c2eddffed8315afe408bbe6a23c5772850e0 225 4776533844b1ab7332eb546fe3f3ae5e3f51d7b3977dc13bd2970e835f56e544 c1bdaa70827d5d91b6765bf5387104dadc4b8cab58dcebb5f40e2da82e9587ae cc6e5a1c43fb8ad115de28f5e96ad31ca0480e5e4c443c2897c63d267e358131 98dd2f11b67f9e514bae60d12e032b001444f07f237dd5b8affc5e3c986f5b46 c0fedbfcda8605cbb2655eb7750635700e624690d6e7446da9478152816fe850 c4c5019ab6e470d60148489061c9d9a9109b301397f77745c6e9e17c066bcda8 36dce3b26fe5bb5dfd5c278032c3566794748821df733ae4678b6e985eab58dd d4ac3e35034beb2af1d7a95d18a96e9d011e9ae455024f920a82182299814f79 c93f1c26bb0001e173409a7d9809a116b3064486bba6e5511b47c0c5e86ea390 40567328c5b44c3e78c59e22c648dc79ce89bd0d190eab66eea3a97a5ec75361 ff9318413265516d38a3efe7c534ab1a789d886a00bfb158d9aaae19898ee64c 7d5972d550e8d97e1121dd4427e002453303af756a6c5858083325b3280c9e67 4d920e913482cc83783851b1c51e9cc099fb4bb47f23354334738f87fc44a867 3444d9614e4df718102de912ea6136d3ef010cf9ad6bd6d821cb70997209a163 521e1955d183a2cdb2adc17eee7643f1bd00cbcb1fd28d02c0d67c33c32203ca 3fdfe7386cee499e72dcbbd476235629b4b6fe7c8b8b4ad05d0b6486f1e9075d 6e793c4e18e1e9f7b867e6ddf88d5cf905d8b4e8f7fc37d5636272d45ea9e037 d43acd6377b801d598c1a9580a4838aed6a7943e0c711760ca0199e42bbeb425 45e4cffb3ddec43173be51dd49d73a1ac4442e395f2db275cf8e69cd0b93115d 0ce9058528ea374de9f6bb59f09ca92d80d38a7927220cb50e117d83872d51c0 f34940d4b6eab860f43eb8acd557bbbf8577d8f9a7a3f071c1ddd00a6d78728c 6878130c56716b3e7cc06b935030e4295fb922556404e420dad7caf039a9d5d8 19d33dd444331d9017744fd935e0de03817509d43e8da91f3870cbbc4eaf63ce 06c025a5173d4003972939753ab7e35c6be1e5d4e5dce2c8a4f8fc7fbcf47d54 15e2b780f6dd2b66bde3c647544b719011711075ca64090b69e6b0e2eb91bcb0 81c683e692ceb7d0eed56080a4f292e6f4ea4e0946e92e333574fb237ad15654 41b5ba818e55ab48f4d0d45198ea40cc49d0aa7c2bf071d84a0fb6b8c72793ff 6b954e61b969d4f8076ec1a5a21e0f3c5b9612b3f3bf3eacf6e12576909a1c20 ba3ce4b08a528a7f78eaad33cbdf5dc9b9bc813f261a422a8a9e1e8ec6fb5704 525c58916d0a58429d1d05c5396984b52b8f6220facb3b92ba364bbca38cff7c 1d637af787390c0b48de88c48ac55d09a046ae447336471e4d2784aaa8a2fe02 4be98dcf24d3b1c84402ae467ce3fc3a1be334ebc35b1f9a8525d7499436cfc6 86c1e7390b789f62c8c7aa83199703c401df42435b9f8a1c1fa978d8d1817444 899aa7e159873cce18eb03a47a7fdb25e080c25dd476f54ff340fb9d37cbffeb 9e7566cacf547833c4946da2e51075c0bdb1e01a2f27ae9d2c7aa3dcc39ca85b 222277203cc08dc0db868a6b93af19a12b0af5112d897193474d3c4488b25259 4dbd52d3805aa446a991096a13f637101b64604ca0467427b14646635fdf2f6e 6f6b3b61539a7337e3cf517f75a9de236c0b3b8983f07edaee6937a2607756c3 aeb8e9172bdb16caf66d1b57b4f29a6da144731619c6483558d67aa2226c64b1 13f41db4772a8d3bc4b053fcbb5fea0eb258788783fd176d4b516304421d8234 8b2f340ebc0fdd680aa0f7a0714518785221517bfb1da774363ef6bbf9ca8dbf 53ddd304cbd7fc412298843d9cd6e14118cf2203dd4e6cbdfe6862a59f4db3d7 c0822a9a69279fa66360571ec17dd1a9a37f9f85a3bc47d8ffe2077732544b18 5655224f4fd3e9f53638956df90f9969402903db811fedc263ba44741e0c9003 c2b61b30e2610bb2c7b0947f9a2f758a1aec2dfe86123f0bf6bd1b13cff20e39 bd94642bf1b9ed35a201e929db5036d0418cfd1566bbdda78928b1d3fea20e9b 256a3349caaea49b2a9edc3183589f637a5c6e0781ea08aec65a1373309cf3b1 57e6c8b8f4b0eb655813c955049aeb795ab0cf8959f52e170b200f8c06ca1558 32289182322e4afc21d46ecb7163e77a90bf666803697e5f1df478df39dc280c 6247418fbfeb973845d7ccea030a325e403197744b700e90447b6ba7591e42c5 98c3fdc28e7fb12c4714ab3e5dd8023cd3a77b556a470a6f82eaaec28913d962 743747b315cd6c2de0c201beecc75d1a18207f6aa6fae9b6133fad1ef5686bc8 169bae66fb2d471376b2b64f9e633e731a33f7e052373289b511e5e99130f51e 8153570ee10d06521d6f1dd39bfc27053f3e98227c3758100511ffa0bc935b1f 887bbbd5749f3b17c03c071ab0ae7878324f782f0198767074d60317550631a1 8eb763351f6c7f1f33097d1bf31012e834173e12185d3ca8b43dd65848a411a6 fcb01d414c161fe7d3d78533b503b558db26e83651a95ee968c6423b5bebfb1b 23ecf585fcc98ff6e163c38d16e0a1de98af90a956939dba0c0954d046627dc5 0197566cb12677e76ec94666610dc00b8b285fe637efe2d3fb8ae590c6f765d3 1a490f6fb71978d9c753a74e006097baebcc5d9c5bc167cbb5419c5b46e61880 741fe64174ee376e031a2b5e4ad0d5113d6f543de1c71cf6ca67b48fa62cf039 d98882205eda31c6417c33a3f1eee50d9fac6d49ee1864d783c3d737f3493a0e 1038db187aafaf2366ef945e9f5fbd06ee09cb177c85439a6083aad34d80c66e dbba3aec05a58d73af285b1bf2c6ed073938331d70e58b6685eed216df8005fc 5605b8f585c302df23b1e2399219f566198976aee35ef28ebf6ad3730d697136 1d58023224dd8ebbdefcd67103cff02fbd93d25070f8a52b04202e3eb48051c0 e31d6b33b49835a66eee3c1179e27feb0f31fd366f732a657fce27e629f70d44 abd0d50f0468d67f45cfbe5af3dbeb92ba773b0c4f454f1d890222759042b079 eb3a78c6d0a33f2fb1224cd306cbf754d1b80f14e9c7cdf4b92d3913e3fd5e37 1e406de634feadf6fb03af207e0ef88c6b8c35e52c5369bad04393b8ab0ea66d 0ebaf83c6ff13988abbc8cfda01a0ada171e4907c8bd1af030dfb0ad24af82ff e61ec5427fa3c1e869c0a344e9c376f6dd68afaa717c0f2b12780f300c9b4f58 58b1fef63889497fd883ddb6210d5211a01d169e0dbeb3ce02e1f6b3a8796952 e2d017d4a623e6827af137a610048245d25650472744d1ff09b42f2d33bed7a0 091515ac5aa0ead19b3f73b562f4aed4d3dac6ff66d51a429e99556a6ea95076 61996cb147a5fdf04686dd8a24a83c30431f003ae221993f1429dd26db2ebb9f b086533ed9ce917b933945599fbc534a6ced7c9d35233571fecc30489d3ed3d1 7fe3c6e4917ddd0e02a12b928b0a9df027a3489f5f2dfa8ca3e4bd61b079b9c6 c747b9f32d5ff063fe115d5b7c87520f2ccfffdb4f2b4cf85b1c028ec5362f3a 54feedcca83917b74e985c111da38f6aee3ca9896b3513a624541c62a5fb099a c37fa8ed7db73a6e8ac7f4840519b55aa45e35a585e1336ff2571544b3f673d9 e6231246b0cf5e427bbda7217ec67b5b2af5e313de04ac63ac42499f5a821729 f14fe414cfde68269d023792af5b068fcf7775da0d7f6b73f89493e54c40b5b2 b2a83ac56cebc51cf95108ffb046e6242ee291776e67b8d8007d7dab80840d6d ed9e180dba96f671ab9d1fecd9cfe0c5c08e894a108a8215a98a76af794f7975 d44a2f54b19290669bb8788acbfc453215fe4a43163c47cbad5f15880b28b9e1 ab960fbf915e2959f570f43915f2301989a425ee078d51ece1f607b95887ef15 d85147fe12f74c52bfd8aa237c1357b8a40e4b723757ab330da8126f9ab8513f c68f58689650d12c81991c9ce81a9ad503747fd566382a95692a63e4132bbbc0 828b80df8a9a7de26e83bfe5ed04a8ed55a7fc5679a9deface08823fa51698d4 c68487129e12ec137a7013688840b798001dad7ebbb4fcbc0650311dcd255a5c 1eac69e2e68947224e2f1d672b78d03dfd16d2a0347dec756db790fb74d17e57 309da4a4b8e66310ff9fb13987f83ddebcc58a0fc89aa84bed647f18bd75941a 6911461e63c4b89b194acc91e25a062ca0ffff496823bf6b500cbcf957deff24 835956bea0479217098b51bf62bd260239c382e14f78cba348aa7172b72a8ee1 0f0e6e951974aa4d010021da87e7a0a53d76fd6e7cf15f3b7a9bef6b243b64e6 f0f34ad415a82c859670747b56340f2ab38e6e225461144b2189f17cd3ed85c7 04744f298bdc447962aec298adc8caff2f0bedc6262539432b68fb2c5fbaae76 6ca4de636ee426725791cc4e5ebc154d81ce5ab3e70ec8dabe67014da37a875b 67c353b313cbbfef67c4b9f888aa9ff96473fc9c841e615cc2a7c274ee46db7d 80a45e09bcb4701ca0b6c651a3f71698df8681f565da1bc6488575cee6f2fcae c6481a85ed19eabfc01cd30c8d7023a6d249238d569bdbe1ec3ba3b9a5c1f938 5f8bed305a71aacfb21419b3c28a74c776e29522d3c46cd26059d3c9f334e298 114b23f63ccae94e135d6a07d428ce2ae6e4d2cc7fa37e376f687a817012087a c04320874b9e169806b3a384ecbab35aa7caabe11af3af9d75927476297061c1 d5c1a9316546143df8f1e74dc6ee3ca6a1b71c425e1f64e6f0454df292cffbef c002e20637b78ae2c52993e2f1d7801fefe9894babaf97965c5c80ee12cdc2b0 cb6edbc68623ea6fb317c78b72de85947c57374e0399e7d6785843c0630ad1a1 97845321a6d2bad464646e96ef14f0e5be9dfa282434d05cf4164fd1d89da371 9033639359ac9acbe7b954a8bb6d7502703cca5da3db52ee57dd6ea9b9f0b83a 144f62f4dfeb4c8a545da632d3bc18a30d9cf37f2d06bef43d9857a3c0ced069 b5f897b0164de0d1d7f01c6f06cf4b2cc0fe2dbec6d34ebdce68ec53d717b3c9 d2667c06467904040283d8deaad4125b166497664528ac0a798aa51b4456d13a 6561f9dbe3b57d0cd32b022555619088a66a351d0de0a515a59c1dd5958f6b8b e8d3de950cf6acfe9626c396057a13e6ada0c2dd7bc5f370336e9683986f6bf2 0cf649a9a0277b949b380d4a6e9e33ed4b2e10689e2fbd56362383e575d808e4 d56012261206638de22114d111f068d3ac6f16244fa6074e8c6e51b88c5df9d2 f41a66e80a5b443be79333309de74e36d26867c5260f2984d85995ab7fba0a34 a50d10c6c9ba52922b7f5f1541e7740dfa7fceba592b3b47cc54030cfe094408 a0bec828acdacf8d76b84cf96a68599f4a227b432cd27b4694a07b6a82800b4a 34b80e368ee6a1867aa6f1844d0c7088bf27cd14caad2f04409b6c2ee649e883 6796a2d0413dae06f8f41cc352fb00bfb108a6f608ca32e1f0e570d0df7bca2b 6d686c544b628b9b91aa3cf5f61c0c835f71f412705f8a51b94b0df0d8fa18a5 a56c68d89d6957117a833f997f419f1d7360b4323c34739c207be7f2a862125b ba51bad1d80314c1dd5a8b1d95cc40f78932a03c7107ae39fce2d41d67bf1f18 2d1ccf23e140ceaba4322266b10ca3c950055b6be5b023fef4c2cf18221ce0ff 9810a1928d9168e1b88db9f42f668d74c2876286c848e3d1ebeccee551e9c25a 420d6e2e37cb9a72f19d1df5908fa1fedb306b16c3273e8172aa6cda51efb166 83412242fc3b90a3a37c9381e4109c1f00521546fced9d410361702bc7a75627 bb47e34650a475dc2187bab113a2e7920af0b6291d790f742cdb2d00baa84781 f672f65bfb59b9dcab25610f8e193891e786d61324d4ba3a0dc76bc9859770df 244c0f0a14caea1c5d77b13ceb6a2ebf2b600ca2d236e6a838726baf0d31bc99 aabc912b93779be25c1521dc85ac60c58e717beeefbb53719b3d1256ab1d9f28 6f9cb4bac235da7131be9dd47388b91c6eac952c53312a14a64d08f79f8b03c3 21fdc291fc633fbeac766125cd5bb1293028bb7e86e4cc1c24d7999d761a2fa6 d3f195a30c0b61d9b9637a82d17958025215dae84efc267b33d9688dc67043ee ca80158b1d8944d839d82f94e1c0c3ec84c71d00a6765961dcdbc6bdca6ad3d8 4385f12e32e06aaf259057053084520a860d89d72ba734aface084d21571606d 09fe9e74d65a2147fea372c4952149cf82b2819b3dc17e7c377465d285f2f6d3 8c1adc062f3a8af81d4937447ff8991f7c780bfd9d9631d33c03c444dad20c86 164d634bcc2689be5724091a95c006723756544a5f88b342c6f935724c8f7f3f 5c53645123165daf862cb05366a895eca0a7656cebe76c4950dc9ec99c46c3d6 05d97f96407e9de344b0bfb7b19f976c9c84a18b3d26abc813aa66c11bb66586 ad3af0cadcfd7f4678f9ce71478135b8cfaf3f7fccff8a257c884b758f835fde c175a11d816cc79653107a845b30bad510f1a79f7e379c84d8d6ef96186b7638 86d2cfd54d34f5cb9dd5e7f7e1d5303b88d1742741997593d5d44627874d913a fb8c80f9af8eb2c5cb46c69623982535a1b536d9cfc8956ab82244ad86a78352 600eecc853b779526294dff95453ecdb98dde81fd5c14d9f78600c3e8692c0f4 bd61b6682ce7c56b24cc9efe4a46268120dc4dc02e46772d2b4a25e1f4a864c3 759ed4cac37082552d3935c827293da30bbb5d3403613de555978969a470a509 9042a7f24f299bfdd44a72ac77d937a2b5bc754212bbffd8b1eb4fec34d37f40 009fe7a022322be2958d2b5cc271257a2d4ab2b62a726775f9ad68287d83c40b fa314190c273653926c342c819d74e9aa461300f238855d13f4ef47e39df72b4 c82adcc530d34ddcdee4ae31a3adf037887f0b700917451c1322db318ccf855d 84c7ed066543eb76623923557c610ec2f8de6f79f635b2c6cae4a5a7666c9794 8acb843e623b83d182f9ae069ee80356b771173ca0c1147588b14ea4f0e4a215 df9bf923ce011ef47e284c0bc462533245be82213b4b7a068b0b684dc6d9f951 b87fe25646e31c2d237664a32b274ac9ec879d5d0108e9d5e6a2f9f50e6e03b5 14ed898ac28f24fbec78eefbb3dbe0a6a00627f72aaf4a9d9e9540f388127ffb aad614164f542c178477fd33c0bfccbe64f5641c01fcdaebf00d242b0667a638 d295c10d4735fed3eb509835900755e8d178bc3b13e17ab63bfadeff995889f0 bdc44a514b895188a5c92e98b9111893df4e2ea88ed0df0ee543f8b280ba4381 bca59e1fcfff6dd425851c4119e251a7732c99b8d2f168d872db69074557c210 6862505807b4b78270c0a170b888a3bb9e3e60c01019088d3606dc0218adf7c8 09f52dc2c19c9aea02db66e95b21a4dffe48680bad98c7721174c2d937c62637 9dafdd293004221eae3d4a0c6d14bf3f1d2ee7639691d1f0b7d9a16a98942a88 e492c02253ee2a7defa7d4a3b6d5bd47bc6f0addf40ef619c058aafa0a4e93e8 acd715734903a6d6de2e8516006b9cffcd6946e5b53d8f97bc500b936ed73732 ae98089bec9c49bf7ed348c857e9a8ae6af9fc1a1f1f66afdc97548124eefa64 96e6d858ba29df69fd29eac6cfbefb0f9e38bbc5959f065290911f8661f9a415 535158f2effbe89a9178c4194c922b660645825e2345847ce93c985b40b7e94c 646934f6ee0734226dd836ee407bc83ca232539af2a10b99e45ae4c160a02429 34bca27cadefaad536c36bb082e4a4f204b3ec626c67151a689224e864e3777a f79dd88fb88f109f091a5716ec289f72adbcd9d18772213f8d7c20a19fc64d9c a3b1eff52f6c4c062d6ad85e9b242b3d16618cc5c359b7abbe4d5df7625cab6b 2bf9548947b6af97803a02577e538e9ad452fe51960335e457e7a04242381202 0fc386991643d770ce3309203f1b0c8f182ef797fcbd3a26b885b0b2ac44d972 41f9d72e89d7d619938c1af7d3d2420e6d476ee3e0679410fb4e6c7255c26a3c 2c4ba6b6dd7e46aa1f3f85d68f08629ec7a96b851188699259dde18db661837f a5ff6d3be34e804595053827df0d9ec671f90867c079159e4bbc220042cfba72 fcf30dd1d4ba8c99b9384bed66d70c3580e042a6f7833ce6c61646a287ad0a53 c028c872ce7d857b5e20441ff2c100ebd80165e159b0cb0448ed63ef565c5660 c439726d5108d06942c5137f54c77abe2be474e1f0e5eaa342e5263341d73590 e6ec245ade09bc6d37d57ded456d1f2f496b6286eda69c21e7442030bf8d818a 79e5674f1b5798a576fbb1ef84472add4689990f62e8a44f38e02f685bc513c1 de29d42b1c02749c4a7797105d3d7aac984c46c40abe33556a6debaf73e12430 10370048c36077e9b6cece4a52664c460f6b6301a575cf4a2412ff7bdfee9b84 771d73508e6260754d2d2d61cc4062eea4ba01ab2c3dc056bc88ececad7617c5 c04cfe615e53072563a714928059e4b4a9158d6a4ca4b97755f1fcb777b9182e ebd528bf3f60f6a2da148514e90695acf897196bd897ac8f50370b4a8db13017 7f370e16380182f13fd53474acd485cbe907c468e47e2af1727e559af25969f1 b41bef6c8fdd990c6e5408e0fdcc51fbe890b51d22a3022751c97f92d12c8f5e 35b9518fd0032ad4e817da14248d2cead7a753964275957f088133d344d5ca9f e264a5f29b08c957b5c2896974664b222e60b6e47e1b0d4e0a19f0b65791c9b0 b510e49c670cc06bbb324d67f657a4028ed9f03784d8d8340dd493a85b838e8d 59b74749485deed9016a234aef79ef71e31218c7722f77386291c7799ecd8cdc c5f03897030a603f7e3ce81524afe2d654183b1aae96412f4a5d75e0868c2b8e 523ae394049ed0ca390bc64f641ba203b9b4dbc8d41d2f841f17f2584641774e b003c751d0ccae29152dbbadbba8eb066a2ded5dabfe98d96e7157019eb21d9c a9ddad08a2c2cfdd752fa10cf6aaf11089c180f6dfb13e3bfcae45a24427096a 39548af056f8083f7ea4c12acd2dc724029edb17168f137360d3476419e86cb9 1c67bcc8b844793daea164b5e2a14baea66c3c7a3cc773475b9f75013823d2a4 0fc7957fa0d3eb649f19f010f8496b4c40b1b8cbd7694c0175daf8094ef58511 f09588f1b8757ea86dd0f7265155e188851365cedf5dfb8243bf2c961f0ba630 6e388374de59aaca3109b8a9424b7cc4e9844de93f3c3804bfda9f253526a91d 0a4eaae8e8449bdfa9d200346509ad9fdcc90c9bf72046baec1c08bd22e1d180 3758f86de1dab9d472cefa662c4db5da8bdc3efbc19b34e7937d0fb36a1c9b94 f6e464a2ef681a05d30501c71191f74883a7071b38ca741f0eaf33404e3d3acd 817f9f8a49200f6610b730b03dcad2c75829a92b67d8f23575c5c5419ced7bc5 8435fe094d99126849fa4d0382a50dfe0535e4deb065b78117030777c3b0138a e3df5bc3fea0455d068bfb4962a546b2f1a2c305c2705226035b94fc931c0ff9 2feda1df906d3f32077d610ecfd55bafc3b4a827a5961a10b2ab4153737474de 7bc454ad5f247fd398761bb29f665cba97dd2a446f7397e80e6ca216d8094012 1b068701541baeb6888baf3bd021d22ddc91a6971517466b7e8ef6c4575bac53 da36ba1fd17363a9d74d1cf7a9665385a9da1218a58a21d989d9c710f84bd677 653f596f20617e55b50dea88cbd1ec29c073078ea3c39f364ae34b806c6d6d9d ba3294f150cfa79de993e4b4fe240e0383926eb1b72a7e4e8eeda5e29fc48622 6e440ccfd050663c1567bbcf716c18738631be861bd928471d3b8b781fa7f283 42d890593645c72fdea1234ef0136f69542a5af2ee393359c35a48c89f22414d 21d532ec6621ecd7ae5fac32530d5a9f862720660c11a62d8e36d9c5d5bf1a12 c3b2672f0556fccc5203bb00637fbf6cd9df41544483fec2ba1b76ef0a6ffad1 f816d5a195b4290ea5bc5a195c21b99a2205a0fb0fe19f9264af1aff612cf6c5 423fba55d78357d35f1507961a1e5777cf9324496ce5e2ce48bc8a5d073ebbb8 1bf4e4a31d30b43869d47aead425642fd590c2c6b71ad9033f728bdadd12878c f53dfe9ad9af13fffa80e17d559e3ad8bcebec4e3244ae901edecc6fd05b1ae9 18d0ab4958f69fa6213482b21f73700b4ca622ea5394771a5c1e7b0f5726eb0b 165 7b21188f9be6d460dbf2e8b3854ea75772e1d86d6dce54d1c8741382fc42e60e69f278e290daf2e59548bcacd310b425153426ad9b6be2ef1590589a341d190631977493a747a2a10de0029634ad92063a0d8585175ee47617dbe238aeacb1059edc1e75517464d1034e8def8df4763fdc10780a3be6ccb107b53218cfa1580cadceb3a052f4774fe52c1dc638c818710cda72299da8c9ef319de2a1341c570b8593090a25d525d8170624e347ae0b2ffc4b225395aa0f50b6ac64fb3c8ff902e9f97b32a92ef8915443110c52f89f6a999ec77fd4b8e66f4198587cc0df15057956b1089ac5f4e62f7062a80de0bc86f4810e86be56e2150be0347138398f0db2e8a468de4f2e68ebc42a3d56e91505949e2f712de88e193eba0266ff64c30ac56f23b14ada083b1b7706103467c2770a2811725dfe3948b0921e1af1b1600ec3d29a9d93262df58092f441290f31056684afa93268c46248922befaf92f10864a1e59e53f6fea4eb0f0c79163bcd5cd6a7d89b72db0c74802776b47cd9d702d4ae4a08c81c0c45c854271c9bee9d3af2a51853f977610b8dda68d04d7a2109033ebd96bfe29ea1b8069beefa2c8d815683a84c776f9f16eb46c13cac89b5064cbd858a142bdf640afdfc0ace9af3a3c6fb4839a779d723261d7875d79d3a01d9becb60ff8c4fc5ac81946c6777ee3b1889058e20fdeb5fc386a22ea217c80c75a4cbcf11b915bd1adf528287a35b976aa56d0359560a3f52091ff515cd3807ec016b6ab98c1fb8af420473d063599105e0a1e054336f83a41930878b4a9208c9b800b629547275d8601e19ddf6398df907ff94e2d689b6cc4d7405125a8e06ccfd133043d9c1edc53e26287eed0752045d28f747fd2f799007708bfa2f27030551702664a2d3a9f4593a57b1201c9bf507b4a3d63d4e9eb1ab9779e05de8008d0e1647ccb9a916bd2b17ed7e5cdcf15ae19be966a229a9e4eef5e78d8ecb00ca077c7fe59a018a28a1b26856188659737d72eaf671792cac9a939caaeb58009ccdfa69ab08c2f99132bb2eb0da5cf89304d2af77ea7314ed86bafbdf6cc20d169adf66543aa350bba8eeb3a689a7fdd05096af8daecef63a3eecbafe517803980526d3629405733f30ec986330f70c234edec213422c6970e28150cbcde505ec446b86bf218dd4f0289f4627c2c29bb7cf80e0399ab7722b5ed8296503dd0f5857046b6abb20be7b65952ddedd3b2d82814208fa411ca6ca21bf4c85b70b078e14e292c3cc52fbc11283b7a61de388b400415633853ab8cb8cbcb0e4043301b976dce916c8e210aef3a80884f929075e1e65e71e93c004826c24761820840f7b0373159e0dfc73e0c37d6c56c06d3fae5860c0d741d98b233e05cf286ec002627aeadd015ab252e3f1fcac467251138696831390cdf85e6c9de1943d6b9d099d5a9af108a1dedbe471e36a255f2da864eb60ec6d768240ea1bd5bf95aedd0ba27cd49e9cb9863cd984f3f655d78a4dc9dee1613cc6d6c4ca2020d755ed8d017b2b846c061a85c3dffa3646ec0c68540b6da19a592830db3112d75ad39b11080d8f8a50c05e20a4613f816e2f8b7d8326b3d9d223c4ef1f949cd3c903c37603432d81c922444445339a85126166829e28dd8802b3e6d69ca1d2c71b7b4d8a0e39833caa57750c213adf54f53d2db2b80410bf8edd387a2e1e55148d843a260fa0e671dcef15d3a66e65012dcd5b987fcaee20981918da8b2c0bc3734faebe019ab54a84513b1df5f713d47eea719d1bd85067c8ad1767aac18032c57636a1007d2218ab7007237d5e87a3a78e2ab825e55e41ac9dce6380207fb635e34522029873055936a2ff46f1a67afb0ab845407363abbe7b5960266fdc4982c02f2506192a827c88470a2c1e07af2ecf807538ef95fdb3dbafe7ae571ca5d4524b040e887d9122fc6e06d77c6641f663146dfbbfbdec0261bf5116d1d51e3a39dfac09833f08d69d647376d2b326ebcb7952f70699054b80eff57be2497b1269fafb069bffc2c0f8233b16140585ee4d86c6e358b09cf98dae8962ef35c2e1f0bb5c06555d13460a74498574222574b7a3aaf47f12eabec8a75393bb6321595ed93b0848d11c88d486a15d7018c152bcb75c93ab91578b9aa04198377b7e7e32eaa8022de882cb8e02073abd880186368ee50a999faa9365346d4324d59a89e1561800610a3710ae036446c7608912307b19ba274bf00be8f6852343e9c2d581b1e201740a5c3a7843621f7e96cd0eb1b1e5f96c9003bb1593a68b9402873e43d2ec0c0cbe88114b9050b738685dcbe59432120a8d53c78e29a82215f95f7d147d5206ca9d67b3682dd9b840f9b23a860222b61e9bf1a05952d2d9827eddf6aaa4480e51f244e84532af9a52b40de3b2bf103e525434c6d13177247169c7aef54edb0de118e195d1b108bf86549c6eaf5c43492600ebdad3127ada26c80b5e6e8c450ff3553728b243f2af37b728461dc0cddee6b0103546ba11a85226598f30f5ee0c005d282258ae425dee435bedb0a7c868669b233b480bec36f17f85efe76c390ac275c38083d1c23cc0850f051000cbd07592438ecec9f6f8b34e07a0bca1bd097c154e208562eb43fda95d6390f07485b21b440cecd40df72250c9d4159717086e6dd46d51a70c0d9d1ece52359723f93a3372d1e23d2f0d1dc7c70a6cb1c102d297c961134c681672487ebfcffa6f7db1553cf6ef11323c76ea723e0405cd0a41c807ac271e44be9b7d4eed91f36317373ded2583562f2653ca0cbde53d300e7273530befd90d22b681eb60fb2885c1481afecdd9c6671890d2877473e3e00e5bd93ada7b79725ed2f374e15e613bde119a275b041922a4b9956c78ab7331014c537ca508574a9aaff59b8e7754d7dc08adbee1ff3b507d437f7f232533de0ed5d6439df2c4de22c2cf0ed2aba2b265ee6fc58b1bc26a73b0d4f8e72133100da92fbd6531f082f54bd706ee86a84a0c6e354c164064ef65fba5d3f3be052b02ee9bb1a3371c15d9a6c6e7bbc70728307b95ec17f08adcf7ce6c2a5d6e928c0eff6e2c60beea67ac7ec628da351e75c6a9f4c0df5c859a71c1c9fec978c8470ecd4499d29c4d007da67da1fb9fc856f95fe87de9bbd963e41bdc2f6bad211902c25f8b38631431f68338142da599d2d6e687ac0bf2a89c22378b3ac85c0ec107383324e8be63eea9fb70f111166ef97bdbd9e496f46e3b0976e3dc7a9438c60bbd6bf113dc3db276b08eefd93f9929c1ffce226996879cc85c0dc7ee5844a6067156e91fd5651782c99f13a981fd552132d4dc2f8e58f36631347e32f7c1cd0d058ad28eac50436c91d62ee2f1628b0e580df9bfb128fed2ca7d2b389c97020441e43eddca4e2d94f0199bc0ff239599b1862b4bfa079d4463087b414751500e4aff3109b0909b826aad6df455fc4f7d27d92d3e713e9a62b4aa095259382f09d29dc19a0810502879c54a47a463e4eed4461dfb9fea6fa3996f62ac9256fa098fdd5bd675ef2bf0a56662586bd2e8891af10a618873bb4dc392d71d105fd1089975f06004bc19c58a6ed5a3e770fb3debabb5617971a719a4120197b1219905489151a4bae3d3044c1a07481c5785ef9f639298face685a793555c3ec3e00089a06145960c1be57ddb8abaa99262ddff393b6b9e87c23a38a3538793d369609cbc0a317abbd422fc9398ebc43fa645b079674e918772fad0e28297540b5330e217914e23d8caff07c6ac19c62214e0e9a284bd5270853529a577b1d6d74230d8cb101cea1df3f85cf4b6b37423efae95def057633ebedb2dd15ff01164f400eec6f78482bc9b8d502bf987bd7e803e4bddcf56779473f3bafc87143d5c08504ebd79fbae8912a5d06dc8606b87dc834e7d008f65fd8517e4b2e29f9b62a25082200e986c917ef3bc6e51558afb0e1bfe30b317230f7db3f3683842dee6ccc06d0b6853075a9cf2809c1eba3dd4a7428fb9eb847e60e7403048daab2e40aaa02bb61a635e078ac13f2c35e6e0854a8d3a7006f87033047d6a701163fb5cd7d0f8670797c3e6745073ceac6dfd19f38c51c215e780a1fd24085d7632d4d658903f9aa319d9d119a1d98941abb5c756f88c3f4528825ad89254495c5ecc1f33e0f44b98fda383630f76175340e4275bc8616fefe81c2a1c23d1bbf28b2e47bf703c75b2432b46f03457d9e803213f9c74cbd08cd2deb445588457adc05e69d320af00feffd570f3362167e6b0eb43c3ae605349bd1b932e16f26db6d33cf8078051081daaeb75c5ce7de982c6d5088a6061ecb6febf156861c3d50d86c45e60203f256694d2efe896e03d32ed4dfc4051ae2faf0b6c2c3cbeed5213b5dfbfea509756628aa3fc5721696e3b3f12a41daed0c311a292547ad8d7d942b411e1fb5055e4119734a3a520964f0c40848a861817c602781d611aa1586a614e23c698405014d340acbc61734e334850b63b62a5b9b1d58fd9cb9001378708aec332a7e07ee7593d6b699b2e2442188371887d09146c6ff33c8888000e44c28ebf0fd890599a6c343d8c01521a930bfce4ddd3ee632179a0f82199ce0b13da80a30054008309c6dadd9aa1b53a161ef9648e01d3057b6e0f1ffc73ad33d283e0f649adf0877d836de77052cd9228d04829461723ce80fa4b7dc5127be0b1d54ceece7fa0e3aa80c24945a606a2cca53dfea3596318893404fb37c22de34352dec440de908ff22d4c4dc5f0b38be10203b19fc9917387f89de437d1dda8aca8954a1fb8206e6f96f7476c41d9cab00604831e78218cde1b60664ff4519c9eab6e3e3288700bbbc3bf7a3229558c81f43cfeea5f77961255ffd315c2611ac47054309ab56054e0e7aeda601b3883900fce06cd44a762c5f2d902fa344b0bb83cfb88ff95e03b3dd651794add1416723a6ab9e08bc516d887187a55b81c4ae730246cabe510cc8305b218a0b894353399eab8acbc27ab7b9b1d6bff456f9ba7d1b31734b05029529c6a4e2bacb205df7cb2470743e99ba38c80f81daa271715dfe3a16553b0805ec528c024fe30cb3a6c6c77503736bd6fd7289a3d4bf18b90cf63be49499046de776c6ec74d21cc38458822abd12eb2562837f80f44088f8de009ad6d6630b5a9ed56eefd1f0793b6fd7e74cf3d15498b112f024c07a49bcaafffd6386d60f26af5dc8755382aad3862f5d79fd91f907e0cbbfb7cdb964eb8f84d2794d3e07e978520ca1d50e5f48055aeffb745f89d270ae3c5cde1187524de7aa62260d06912978ab0a018ace84d0fd17a2f47133d1c8b17eca7e354d436f50bd93fc6b0e59d1ca1bcb0e6b575e8b70e28d6606aaaeb432ee6e32c8b98d122bb4c650d40cf6e63b51ee2dcdedc59f740127aa5c48e43262fc9bc640ae148d5dd0faca1c0163ec1f07a73ac7377914f980f275fe9d83c9ad2217bbb93d49281580a243d801c57f8ed0f471db63aee674ba687588399cf98fb5858d3c7a678eaf67cfa4f30f523e0ed0c8e1fd595278a5d06d50d6e8fca0e4abed1a712c542390ef4a4c6a02c5bd772acae7ba18d5377fcc52695059b980a30f22f2dbf91b20837ec24b2c0ec1f23393ef30e2d29a6dfec42a060c593b5114bdc4c6d8e06fc5893ab7fbcf087ffaff6ff89db8275716ea40847a17465044e896624e3236ab9143c64ae5d40c0ce0d86e7b91b458b8b3bb7bd5d6c84b8d071c47cb00455e25b9e2c81875ab00047990db02024e78bf8e0c90d06ae473235a39673df66e118a5e194cee56ff09672ef17ee81c54ab0148f6e5da0a19d35dc9dfeeb3b662ddfbd4a01af47065015ec144683f7cea209061c6dd4fb7273f845bdf75312b68af958d161a67afaa0579f053cbaac0286dafd05ef2a1beb3ca08d3915b53022f98f465eb327ee4f209f14a9a2ae9a6a59270be1b2a641ad30bcd81bb7e3e5249073ad4af4d2edc3b00cd7f0809705aaed39bd750c07a95a19d271904995ecdb5aabcea8da27065900efa58056b0bd1253cc47352261b3d8e81e891152be4476e6e15aa9e055d04fa0ad3fe56eab69f80b77d3398a1cac0295f7636214efb7208fa7cf7e797c422a50e9d775d4ee35110b3e18b7c445f211da2ff4bf60bb39f6504194b942e7aa268022b2af67ceb362876044101ff0cf3cdfe9c2749a5541f4c8ab4dd684ab5b843084f4ce3a3f1338be360b82c417e2e41872c9c3bda61e5e30770745f1892320605f12206fd71a77fa262c85c025a69e8ddbb35c66910166418be302f9c853563020d3aa9d87f9e7c6ea120db2efb88979671090bd760d320843fa79a9400e5ef097d66f47e24bd69e71bbfb70062e0ee8e8ceffcd83ecdc2fcd09f121259017603d0a9f3257bd54ac3acab08d3216a33ee84b9bf81ae6723aae4482d84def0980f5a5d4bc02dac35a2e40e14eb33512705e7aa6ccb1b37f2276ecfe138ff33f60c5cd39885ef4bfcd06515ef19fb1a299c00b954b91bbc101dd776233634523603d9cad2719bc6812242dffa91d36969bdb8b982eabd181c4be867b060dc5b1c03b9b308c4a7f90a185f697b22ed9acdae09fd6affb4d11181afa979c184029d0a045f3d42830775cd95cad1b47cbb6bd42b92b00879ee6b99d8aeaf8ed7b7be08ef324a1e2ec5ecdd2305e62ee99ca2b32eb272b68d0da5d639521c77b9fff60ba7e25be3ea7986491834431bcfa6d7d79c0c4be218f0b8c158991814434028055354cad9d1dd80827dc933c5da5f9a8035eb439cd8abeb4bb1d7eefcfeb47901fd613132d898ead285ccade9a4df1014d0d9a16de93deb56e06d07eb96af3d0c5e202757f37e20e67ac9b0a8fc9b79a1cf6020286587a07712e961b2baa7ed041ca848e66ac4f8c05207905cc66dcae725c64db8d5eac403c4fdf53ae8a81904b1fde3ec5d6a415c49ed86bb5642728daf96df2d587fc7ec72173a70806af5073accbfa2fa2763d10e7cda7e2f661f01f9cdcd95c1634ba59528255e7347660bf228dee268d59bca7a0e0034c5a4928f25103ceba362c16b624bbaeee4bb140894f84f9aaf259e956f77b819796bcf891029dfec5657a55d002e4d09215f37046eb168dd33d50bc48cfb5cd2302ba91a41a060f7d3adb3aa0a9bff2128a1690e62e28ebb95eeb0e94a7c9e34275851f126372e92b36da36028f839c92d389009ce59af264cf180a279f6164b2c4be3d304457fa7372c81a7f1ccccb5c258db0b5ca35685a924439b76a9a766df1ced549b731f70af4ed14e2d6903b7cffb7c0761db3c01dcdf7f72e05703ac62880002f91622609615b5150d85bf40d91bdf06e828a0caa5aaa3bb14c05db5411a3b45eff47f26be7a9da984c485b703755a0c5a1105e836b9adb9a8167aa356fe2d461862d40a352cde54fa810d6638c6790125c2f5349c67f0079a17088d221e20c68b82ce4791ad7b28e20678af05df2a05d8dfb0be4f7e84d8b4e37ab74b4ab4483b3ac7f54a95eebee92507f0a88167083aec084496dd7711a87147498f4a5f44dcb7d51e94c5e8cf6fabde4c5d6c9a0a6b3e82fe56a9a693440aa8e4fb90e418a69f6ba4f4607036398776de633a350f848385fa584069a0ca7d3371f4dcdc8347c2340c0b5d46c2ce41cf40a2723c0bbd31d4b62e05070bb100af9619a8f7a6d7b558221eff679fce79bf00c424310d2fbfdfc09943bcfd63a3d3ea9000bc75212e0ad989f52798e678d254a76c1407b7d9bf3063284211d0e1a85d0b0c416aa35aed76b8bf3e6f7cbd3c45bc98450d588ae58e391014b882e9edc7c17d8ea5b05e4aac46a01e38c1bf2f0d58fbc80e1b9542727f97a6ebef40c67df53a186c5073f08c4e6152584bc6ada4604a4d0e72f791e179f9857c8025c1611b16c3df59c6d130dcd796ce696cbf1e4b93b701eac52257bbe36fc0e42c38bf5faabbaf1a71e1c32612a1d9d6a248f65bf34c0f91d6683d299962e393f91924699612f0b1b40a27e0b6531f887b1ed2c38f28080819f2e02b61ffe71ba47c9dc5719a8346939cba4ca16185e7534a98e6042e0483bc8ade91360f4d4b09b334634ee37903bc4be5fba6276940025ae7c3dda30545329c05e0e7e498a65b60f28b116225920239b8bbd14e4acff95b9fb29c4702b828d747bf415a926f50c07baebd0ccfd1a0486c4bcb14a22e2e78af6f3b4c089e2f495ac9cb4d620b20b0db59f6dd857dc3e67766111f5dabe4cced97db05009e9e3f1c6c41e096da56e9e69a11f6baa8a9c074dfcb2bd74ca4278d2f30a90e39a7860251e28d6809a0356f731d471ec64d5fecfe654c0f292f576c0abdba0d0501a8051e56392ebd0516713bf7f582e01053c069919b3b6a0551bb023d77012880f2bc7f2cd1348eca9797c2eb73caf0cd00df1cf93af54d4140ddcb94e406b034283ad10f0894bc3dfd2bc442037d6ca2d4adf4f1e5e60998c4ed44dd650d81b700bfc79bac595313b2db372ca90dc23aba56785d6548b3519323a584e00b606b43d4d188d4e9601869dba4d1a52968f91651f7fb860885245e7936ca770d57c213c4a368b2233d84134d5501535303f5b443b1f337c179b9ff076d57df052a807e2a0e209075dbf9aaab930300b3a31b6da4b5afd03a0388d7fe915113000639f4cd3babc9e1a20ac86c0219ccc0e436a2c1eaa1293e8f9d5ac47fd1a10bf893ec4cec19f53ec55edb7c59094c84303a8ee0ac7abce4704e647e12b46a0878f76fe8be5de656a9aad5415c562462aa81123cd2f506a6a74aa6867f2d610458c7b0e0e5087b22df3c1dacf4518c1aab0081261e95270bb324e55830b04f018faeb5c3aa92bf8dabb2473da76fe917fb0abb2ccf478a870942aa420004740173b7b289866bc91676b6fdbf85734c715a030434a5e67bc5d9eeff05390af80d450a936a4be06560030d2864f578d2ea1aeb74dc1146680eff78da13a135840d791cf014df4dd135127afc4787dcd8a0532df1f61870a51dee0b02817229b00db3d0254b2d99ffce4183ef25e7df5b4cad6ef6868f1d6a1ca1c85498e66cce0e78e133e828283cafb3f85613aa2ba85a3589e5c5397ed02bbb071258a28e0c0032fda768cd5a24b412bbf0adfdb765f033fa02fe59401e1b8d89151edb92790ffe3525253f906d4388966fd65384abfae2f2507cc36f939a3ba7400b718cbb09cb3acbd0a68d01af8296ebf26693bb66d08d5914ac063f2c8edb2813eebf8b0b2917f811e0935ce1dd710adcea39b5eb9f6fa9104b0b22325346a2111156540e37380c8b0dcade1c1403f3d206875a80dc5d999196233be1817ad5ebcedc450e1ac250e639987f79fa2c5867686efe54c22cbaaf4c12c6de5601cfaf9a57370024c00f51a10b616df07ef3efc50bf7bfa2b7d35ef570b5dc27ab37fe6788e008bde4a7ef3ed14d8484e55e01d0586b1e5f54c5ac5b95931efa33dc705c031e0fe5880feeb850efe440c29cb0268cdc84f5562b6f7c53cae1733bd06b21fa7709b06bab60b3950f477b0bfad682b7dbab9e57ee7c7ca2db57913a99f795708703cbb00d30dabfcc760ef7be01d819f08d2fd4cc6a67c1de7c1735bb6c1429820cc368f19c3890aacdbaebb5110739cd2e041b4c27ef2325bc08fd50ff3a7976043ec9c242a355801beb75e1bdaa25c16f0080b8f89ac68289823f1418c07bb2089dae49816cc08d7e5343debc7aadfcc43b9336fa77c3f50164fb0095914f320502f97373ecd95413b640a6daf8f645f4047a6f14bf7d55f71f43b5a2a34041010a1d48469c4c2db26dac14653a8c0dbf62e8de1faf2ddfb8472bdd93a4ad9e0196aef6da8e7b79c4dcd2db4469552691757404754960659c8196a2828f81f30edc8152f941ffd0f56ffc18eef2908a6691cd5c42dd84a0f498e9aa1af533d3027793b5719f4acdb6d051a657db94854ea8f6b25706ca8594c752089ab27a45004319d587eaaed70b6bd0e64b19ba59276dfa75ded4bf37fb227f6d27473a950bcec3768315de6ec13cd8e22ead83c5dbd109e87acdb3968a58b196c60c39cd01d3fa802f993d5ccee0abbcf6eb28e7d2d26936278ed6b03df37c2c2f07a58a017c0482e27fe448fbfa935ae0b71437f4d9cd7184946717034ef6461b76f91d01ade47e7f72473b3e45a7d696836a40cfbf1a7dd0da9d8e892a8cb73d5777480410a9297e34f73966477d68a0feb7f4a81ffd26069d060283a741d2d781ef0b0f2327a8912cc3891ab699f425051afff5ad9ad3ba91ea8c1b87e36b12291ec40f01a0ea626c67a230d20eed54b1fe3abbb6f2bd2d35e6ff4c46d331b2f40852057a72f7b41ffbe695beb17e7a20f00e1dea0fe1a2afa07fccef970967196e3b04d464e4b783a2c0a25d19689ec5b5b139b2b6090e45c5edfec706889e92f1b50c3498ae07c85f1e1c7283814d883c46991e34c3151d59320e5914960795fcfa014260fcee1ff03afa2c602648c40dc789d28ef7af40400b45fcc16692a64e01097db094d04b8d15705ab874e797697919ba212126d7b9da7d53eddd7a527a760fa86492836e0d6c5e9bf48a08a130dfa5b46100ea9d8171b1596c99bc2593e90cef1eec5ecb13c08b306c14d266a1ac7ea7f47b905137a4f8843fb0ba3136770f132f3a700f6b06e5d85e4fd58547f237dbd53f54169334f50647c8a0fae411050c708972328e264468a684010e520954db6a862e0cd04c5bc39bb0f75963b7039576817fc3ffc5c778e17ae48a27df3912878eb226ae83ca043d8fa79c77c202419c7cff8c7deee4a8affd23fcadebca3cfb01200188508786ea7a02916cd507efc44bc6151b3a1a8cc54fee342d7fc418b8580a1a8d479d179165e7ccb88004bc9bb733cc80cdcdd4f663742f468f16f7a17c442f3f6f85f4e83771357dfa05894bd45befdbd9b655342b7702cb80d56335ca0f1bbfcef3322f2637409ceb09d707dbdd67b1a4c39876cd8f18dc863852631165407c0ae99c5a33f3471b5108cdc964c0ec40281c7bb1aee9e94378127ceb62f37c4dc6971443a0ef4d96a30e19d718b58850e6c804eb99a6da5c168f85d6c60dd4e26907a537608791fedd0091f74e8f2d1ec151fda049e9e5c577aae060b1474c2a381852921370d10de00c0f9e429382ee95a9ffe9d0420306e7beb98ed4656d4b1ad3d22fb5f843300203bc06a858e1aed773271e72f4a850bafcf21d363f2c9d24659387a0b51ea1de0a6ae1ae1e20e40a5cb071b62dba844ebdd1fc1416bbc56ba983b180e33165f20f70d73fe21b09df5716c2abdd0662e4a80f74034142e76c0dd3bb391a1b889d01f7fcb058e74a4718b032f3bcf7b4ee17c4c58655243d82a87e8363ecece3c107406cfdb365febaa4038aa1db6565cdacee7cbd0e3d880b6b70a5aeae20b81d09bb8905d9151ae4b90e19078591858d51c9632f892fbfd6b0d17617ab5530b600da6068939617397f6dbd6fa06ca4e874de661f9ec6f25a10cbb8f987a838dd0ca73feb4282f4cff2d2a570dbca198c22eaa965fcdff9a6e61e5474fb4071ab0258efe8bf867277e960f7d020ad1210d35e9a33460689f511ff720e537662ae0160cec6d0764aca454107c2726872716d1b1e2d126323801d9236de01f0051a04f34365920c3ed54390ea0f09e37824b908fc05bdf6d26b511297a9a226aec80fcca2a9487eefb1b5f2c380bd9ba211fb2fbdeda09a4368416dfdfbec0bc7090454b19ff3213000d60eab73cc7cabf7e1b3f9b0ae59c9b7f2989f0e25f264860064df366ed5ffcd3b965f8d15bea72ee3cebd276d2eabd69f8161de5af3405703718c00ecd17178b398602393b77c69eb9de038e0b331accb115912640460880606913eb9f0be2e016fcafb9d83d45ceed83426745813264b6cfb1ccc2b9f90066379ecd48713b7d027b855ec4b07fbe688ec324c9a4b4597e909a438a35d0e008494bdd1cebb90c6ff4e17b2f2e3abb0728e242b01465500db00fdd611ae3e0f4d6f7bd03b45d572a324f082f909c9b19b04558ef905bd8b7cbdbf6583b2a302afe8b946931f90b0c5ef27e2cfee844c5b61aebf4a34d845108bd3d44b7eb90ab7fffcdcb0e07b8514fd882fe60d078b5c0a55fc6408902c730308a344eb3c0caf85d7145a3156a83b8eead61b7684a6e23873a267fcc738250143a41bf91a0c59e0afdd2bc1bcfceb033f3e0bb4d8ced2ca9ada75b3f452ebb9a485d4d39104d8dda253e12c196aca2f14a4d75f324bcec775759760418fb621fdc2ac34a4070ba93e3fd9da8cd598e24e4dd508c016afdd9baa9be70da7f9cfe8534bea4b0d7b23b5e8f89fe15b2a1318e39e129538d8101f49f949ae868155150288835e0f96a901893bdf92f6b0844a57c1f174bf363bad42d805a7f675db48aafe41e4063c9f43f188e5910da3894c20df4966a8f3db7ab2eb97f416fbde38968eacb90848eeba9323ead91ffff98825d5afb48f06aeda7e3605a110cf7339870c40cb0dce84f142523dace04ae8e348ab6bc515368c75d98fac07b1e6a8453b8c095a0cffe2216fa5cb4094a6aa0a801685c2f8cfe72c76b5c66e58d0ee76f31bc6b9064406e50afbd47430d221ac8a29bfb4c14cf39b6c11910da53fab5b4323f85d0ff08680942e49fb440b9687a5fc263f062e5c621a9b32e4423dd1f4f36a06670cd00a19e153725eedc6907075eca47bd4517db1145cb83c8ed92997f660e93503522fb42024faafcbb31e6c0511b3dac93891ad279b5ba89f0a57a737605dc501879d7565254fe3597fc0656dff5d6324e096619cc115bf0b76c560f4d2d3f20770a7696433c5eedcd7005bab0c21cba403498fd292f1caaf7cf5c3de0b567803a9af176d2007a816d9813d7d9616139d1c09130c9beaa5e98fa848a12e29c80c0dad21d35a9598c222e69d10e0f393af7412172e51a9386de3295ee52e96fd0fc0226411098a45fde11bd0fc41b45a3cdd54dbd97c41d834ea6198631c6a210fb83676c3b4a95a9e83aa683ae5c25e218b4b5785be5f4d58122ec2816cd45e011d91f240222313522bb32254e551f7c625fe0df9db32c82aec85ba6305ba5607c4b77ff8a888cc2ddddb64b40ee79f1e1931e9f6426d94bae170c1f776173d067e265fb852862850d33c9d89b32a1682476b5bf162d9ee9c1ee7451dd9ed0805033cad7c8f3b1387f65584c39943836697f2301549c266ea89360644a63c0407c8f0ec0872e8c80987acf7f8eb9e78ecdefe8bbcd13ccd4b05f646c83e5f920775cb0ee4a89544d321152257593d7dbc7fb7097726515ee3f489311581e2fc03c5817e265f3c2cbc0a832e2fc28296774e05afcb6054e577f0c2b9252064f70050281dce0a292a7584b5ff7319c0a2302aed675e19d563e58f877701338daa0b25ce90fec48aac5209a7abc16863b2f5aa524765c0c533d8cf3130091c6ea80ad89c9a1853b6666761b47d3645ff484d7e6738a30a5aa447dbd9adcf133b3c049d692676cc4b31747a366384153b344923b86af65678d431466df7e99e05b30fc00bfbfae6c59384f6d66932c17e8dd08bcb4ccb40d1b5ef2bf15e0882a18f0358a500322cb5cc090285e04f7288e8c1ba7d1abb671844ee94473f0ac3cbdf04b591e857a0dde0838f644e5d484648950d9f0608209985e00c9cc54abad114061dc756f4cdcec5aa4cf6bbd2d5c61c5617576f7a150666ba72085d514452d904969f34b365f4530905230b56701451a0b59deb3b8dd6d7e729c7611bd0edd10bb79e2205e13ea452c628863e24a5379ab87290e38160b6e4de1c892ac90be60324ed366deb2c2daaf34acb06d559d2d7140d6a6fffeac747d515cbcdd80533085642485b6d5cf9699241d2508c0742d60417a72cb0df67073b9eb7c123b1cd05bbf19d2040cf53a1e3fee8a59bfece63cf4c785b878c91ba8d0631c3b208410fb00a0c2b98b17355124413ccdb9abf233fe59b500dbf33e0223993c09256650ea4c98001143a3ed673bddf6ff5d8c528b8d62308192feb6f245c621321b31d0a5514565a641b47eec3addc16dc0232d2303872dd32bf9b8ec816af485739b508f6b76554859d06c28cc3a59e1686200e35f6355a712267f50ed7cece68288a044a18900d5666e21c1a72da15de670d343840418816000d0d0eb4c12b669ea001ba6932e13a7d890958f2bea856a65a8459441bed717469877be524b491e6fc0f12b57113869c8549c916b42bcedcddbfc583303795a958314a5d907ba55ae1043b69a73bcf9cd2243fbda94f90ad481945c9de9a4bfebb97438d714442066903700377acfe8b29b4d7ce47e7f08f24694aacb7e01ec06baafcb56b316300700b1a0a1a4fcab979f90b5359fa7cbea4bf256f9f0e1dc489d71a506e54fd45120acdddf09521a9ca4bc1969800b6d158c0ef6644222ea2f540b86f2de1ace5bb0dd9c919a0a5faa9b8668af8ee14adf4d1152098309476e786e97404a0f7374308c228688555f902f5c64fbf8625219ae30ac67629abe6951bbcd1947a780f6b0a7b5cafa921804d37b6b1bcf354d6101cdf9f293252c304c038da48d58604fe00622498728d3310e61bd4e9141e39645f27f69bee1392c1b8ebb4469b4a5d890ebdb3cd201fe6d8be899c07d4a3a51d1adcb749ce32977f1eabec0554d9487d0914fbad773fac6da9764c46e3294df66a85d49a5caf7a55c54da0b61df4c1a207f319c4971b6a399f3c28bb6a17554c611eed476ad070c0f06d38f86a8079c903374a47b38b28bbda6152192b0eb1885c01b78ac3c36758573c25bf5e6786780cb473d16e2b2f10a5215bf12b67db1f1af8af9c6af25ccf62766dca726bf80706cdb829af6cddfe770d7220865a1c5d6c931ccc1ea698c577daaccfc36a46de02773565da4a3859b090cf797f6b9f55ec17c70d9e83a65139c8ffde9c6bbc9d038a1e0d68c910bab96a9bb258e1dd62ce81af602d9de1a2e794f3604c929e8109037e92099333320a52a611955a0a5796b6dba090f0d01b713623d25a221aac00114eafec2a04b10e3caf9396fcf3ea4a2f1d44873e722c06ca4a8ce81d141a0097f11d8d59dc5830649d88955c935e46adc91d708660a86bbbbe92336e7de00c31e87e2b3e1cc19bd4f2a33c3220aac13b2dff8d6a7edcf053e15b6406ca850885de73773418af682169ab5ce64e38b26f857e5bb40c78291b54c135a2109a0532f11fc7c2a2129adfc95ec29d51b3cbb467c07c2b305967dbc706fb5234c906064835c101b9dc8979781a726d5ae1ef4bac70baea83e4123607b62f211c02034100198be159e388106ff6422e4bf86d848372efe96edd2e908c04608a2b0e0c632541120e687a61e09a578e22a4d8b1fb0ac2580eab0abf9506f201664a660651fcbf78343a381e32542a9d3941d6b8031d54f199ce1e335a09270b64c49c0996fd57bb76125ed9485ea4329db44e14b6a40a681c8dfaec7f02eae7e8b1ef09dcbd3b989708262c9040da9028dfc155539ff4d2ffa04bf9607acd2b2fb90d0406a0428040b66200ccf24a592b336c826451112f38f02e38084c45e493d6ca0206c02b5b9717fd0978cb67d7d1c1552f77bb6d93856a34d9f73927c676d2e101b14f73c1545d9545c9b86df8319a43184f2c2e6a69816513daa48d5828d8c5075970eccbdcbe097eb283e8f8f9ac19b14858e0ac3b06fa93b527abdd112b710d3d02d2180c059eca017074f028a41b05fe524facf1be50f76086a3ada595080da234098bfe8dd2a12da4911640c6e60efe88c2220cd6492cfc171b0d2cad7308573787bb1202eb29147469327cacf7baebe2eceda04a04ca1a48720ff07baa0662fef238efbcff9a476bdc3a4ba1400372b0e810e5318a70742edd6e9f3c010377881f223361abac93f83fe81409bb34bad302a77e5795ff8e250b3dfffa930fe7efb5281a308311e451117da110122875baf57df125cc4cbb1fd3bb4a82ff0fb4d2f6dc29a6fe3bf1fa6605eca688ae2a84f6fa8890b7723538b3b0d7a9fb0dac982411ec36e0f4cf08029451e49a070c1011c2a389b62cb34d38dec6c8fa0ea42edc071ce287823f46715eba6593eeeae29a09187c06f4b05fb08b4243e30fceff503a64495657f4ee6e41e34cc709c23345ae15192d0c06d1da5d0796eb0f09e1d0af9748d228ad92dd40103196058379b4a74f0717f91b5f652efc27a10dbccb7f58c52db90ffd492c2167b313475ba77ee1843cac82a8444894caebf60fe50ecc08ff21469a59614c94e7969778838802118ee6ca04f41a0b705a36270427a144c90a6c2b940474cf36a9c5091d02f874896b4add80a30eb1b14b98d7021ec467175e4a46ec0eaf7e457f14e5b038d2dbf588889c6ff1b7f9c916b25907578eef80059061a7e76a963b8491e04a5efec82faeb6d1814718d6a4d0cee6054bcb9e38c56184d4f9e8706990c8bafdd629665f8ea07bb04d5c0da13b77ff09e6fcccca8302404c856ce8ff37c14538341417e2b01d37be4024b9391c028707780ab076e20724ca06a22736582e7af8732d664ca1956f13560e61d26f9b030f9525f63b1e5145e69d32f42cd319ba2f8321063c70e43382bec71ae32beeef061b49302745a6a760c06d7c56960731030176969d8a5afa4bf0e61305c7ec640b543b08eb2be0f4ec11e0cdc3cf5440919bd008f662491c23a800d5c7f975f8031a2c5d01965b8e9181711134acae0fc5a990b1442c12172b5cb6a8de5a7a2d045ed2b243ae5e29b2cbb7fee274908fe8eaa38f3ab7d1e13d6701a865a7b26c02ee00778c75133414cbcd145c94ccd18bee177db18e367797748ced49b714690ce5670c604f513523fb8c32226f0da3b1973ba10f65effe90c7cb7b32ad727c013e1f02fff9d1d78fdec655c54f90b9ac7c7d0e6db53c2520a7495a5e8fc2680f637db162b2ea027e30dfe9e7e1d81483c8a150d0cd45fb1d9d9d0d26a148df01913bff51b838a2bc16e8e0bf9bd2326bc46e04d7dadb735c569a0940e472800bc0cb5a5d38dc315a0c4ad9c700ac7c169e4d70f9c94ab042c7dacbb898802e0ba4099ffd38278d3b41c3db53861cb1c90761bd2f012673550458dcfc97718205237b5bade7dc123675f6050fc39f1e0bb4b160b28fdca716da422f6843ae57091588f20fe7e450be69ac4ed9058f7241b4af10db7993a09632c67e382b1d5c01634be89f7c0786fcdb7b9289f26653abd2b7e469e8e3a6f8062256e24364ba01d32210c0022bb698c80c1eb0ebd0c3de92b73a7a573cd7e80fd010267568c803154be7029146c88a8ec6b35f54e440ab070e23624d974a70bc1a5b2b9a644808393487a0be253ab66dcfd39465584459885831b861639fab641041a31d5f9c0a6a693e39c0297e0cc018121d2566b21c50c26b0a8b6f23abf0223c1e999bd008983a4b03d4d9102dc23f064d89c36036cf0cc088175578194dbdf6952f3eed0a6b815059c6bba320b6769995214db226e66a462ab6a79dc3b4db81358b39500df4de9abcedd5f87dd6f7abde0d76309332246c749fdb7197e6d6bf840ef80d08f8f328410b4ee3975a84afed09c870e005dd9dc1e94acb0831379cb499725902a9c122315e7d6b00196b41913062da087e348b2c26374884ba251e0e2ad60e0df94255ccef66b110a2aab17e3c98ff45f224c96907980d0d07471a6ec8f93a016031104c3baa75eab04541de26924526b0d25685c0d3f315f098c767781e8509d76a55a74e95cbf8bf29c8cb1d83ab4acad61d9bf1bc01b8b6bbe32498262f0f1dc16c88f572b141f6e102accb2e99de44cbcc209719efcbe4b158fefc144306254a2e2e64e35e46985ac344106d34443690308bb005a8cec5d53eab3319f005811b42759519c9eb05ba2ba925fda5af91453fc08be0cb357a09da2f54985608f6e0cde38d0d37a6792842310a171cc88593c80ea20351f94ea5a47cc5ea74038678e8c347f4489a21556072c065a2315948690a0dda014e7004445de46092024553aa6866e79a60cb62236387896e6ba7546ca4b3cf395d6bf5af57ada4fc06f18da3ed28e209d5206d41d598fb919f50487bf43df18d04bcc7a26cee111d0684ca68ddc9d6909b10a109f2aee4490e8f14ffe0ffd1cb0ca4f6352adf55300d6c3e859a0c8ea2ed9e02f80eba00470ceb2e06f755afd931e2aa85da7a667a0e65dac2a62be8d9e408bb93374984a7779a9a4fba74a50f61022e5777cdc75c0d7d92492cb03c3279a8298441f1b66349a932d6fb10988e1b45f368c6c8429800b05f226c00253fb1e89f98f91a5794f691b6be46ed61ef567cd5d46de6bdac069d6e4548a431d948c45e5f2ca7ec5892071155d863242a8665978e65d7dc450ff09907ce69d1fb35e69b95a79d65d547c5a10740410a435819e33ace8c446205434338b41ff680da6def62effba476569d6b1bea2c7c9eaab4c0f40cbfb5fe0aed99e352cb5e6e4ee528955e3bac1afbd289a248239b48e40e4a043d5023080a97a65caad2864c9f2668c4901b91490ec652f941e3d4e7301bb195bbabb37b0af0c9f1de155ae7027ee74d50f09e9b847986475118e7f38386d8f95485c319041571a96cdbd154635e07ee373d232622feee5c29505b6cf34ddc6ec0d07864093bc17c3c916d9bf3ece75c442dbc17f604163ad954855a2b7a52882e12bd660d8fb541c380f5701f2825b3b625a9267cb5d83fbbb7668cdb4f9a61bc38050c0b7bdb1d8af6eaf416d8e0e04b39166ed35725f6964e5cbc636b1a68b1f2292c0a3c28c9bf6def90ac0803ef9754ab51bb1626e9faea62bb45d744b3fd922a7c0dbe9919beb2af315f2a71257024b6e275106577a4f119fd992c8508277fd7ca063aa625771bd55eb3c0a0e372b66e9383b39d9aba38fb189760c67fbbcb5f040ef3b642040954aa9cc701b7eec0f4cd586761fa026cfe4eb932ce0ac36541bc09393141dc34494eac4a57133d1774c5562156e16ef72597844f29245e3bc7c000b1de547530400713a419b488a858df36d891811996adf2bec1bae4478a3fbb03f56124b242d8151bfa2d3c6a7fafa1d080085abe970dab12b7b0b46041e7fb0ac61d5347e800d7f60170d87c52d4725dede237ac9a727ffedcda440582ee2d0e8db028b41d2826a7ab92ed94b38676a0c4dd5c5e5fa90ae7af147d51472aef03619a831f45222afbc2f6923654da58761a1391f3ed4340a52fdd27d888f56a04dc2826e97816250980a323a8daacbe3588b659b72300d2b3055cf8abe4a58b025685ad2242edbd8873fa943ddcae7893a0053a65194051608af469990d7d200bff7dabe5b21778c2fbeadfcf69d1e72ca0d72d206d8e900473dc7b87af01b1065d78729023b27b337a40cc9f9362752bc26cc5da0adb1c459c0909416f59410680c58cc5f9fd0191ba2123ab3a8c4babdfc51f583b8fb87d2b005e894bfec1064ba0fb53ccef06ba46bfdea53e91e6d15e7ca803f5a970af16c8a3bf89585c0d5776e7c067d781f0736ae450b2d3e165abcfb618925b70fa58d3351e71131a0b14f480aa3c731cf4af63ccde43a52b3f680035858dc92771eabfc68c5c65e303651c58c6ec832e8941624f70b20a64be8ef7dd817cf73cda62ac1bd39b0c0a07994ff2744903e6b3e5f397044f9a28407f00f500ee2f14d3fc243c7bc12bd30cad1eab536edd21631d4696f583e93eb9e2262c135e8b1b4f446fe54e1ef4fb0b232dbc0c9135ef0e8b0ef03107455fd8409687ee092008021a0093ab82c6670db200bc273f53e6acfe365950904aa5499e746ac9b88a652f7e9f28470d6764004909c2d67918daa144b3126ae67ffc63b6fa5146aecdaf3a5c407ad109d0ee05ee1ccf76821d57ee5ae0eb4770a453e6d1235e2a895f5b74078d0decb9f71a07adf04fd1254c0c8fbba5dd570370409294363fa7c0ad69fb0140fd4f7f1fa30bceb64372bc7e373a982d315e6a06bbd0c85d7eb2f760986d934e83090b21f60f05a9bbc0d6290e02c4f97e149c7d764775faee252b6ea57a122bbf6c1d7cfb0c665707647ff2db2321ad92193519e2dc670495a4b1bc13ebc997fb5ce93aff0140aa59bbb390378beeeaf056e2fdb84d03e8b6e11b0d320a4a6316de60c4780faaa00447a77e2f40b1d0596f564101f81ecd6163e4efb70ab7b219885da5f40b3bd2bced5bf7e5f49ccdb5744bbb9bb56b96190fd7d6031cdb7756bd43b9af0fd29152deeabbec005120ae2d730c3be20446ee54172553bfba9146e9ac47450ff7ca651966e6a579bfd89f419d317046db76ccd1479925b08d1f90e2189816000fc3e06afb0abc7583d508faf7a2d7c69cf45feb0c578add97f7db40777aea0c +generate_ring_signature 95db1a6aae43481bbee75323b397d78538917ba1bb6d9b0bc8a1c0a2a66085e8 8cf8d890ce48116ef099f073e204fadb9af092a9fd0a6d84e3929ec70972b414 7 2a2601109e25b41290716c2dbd20cd71416560042de497022d82a79c271eda12 3b3f2cb12d7e050eec55c7ba3ca5c18f90456df49e5593d828a1c1ad5fffaff2 49e8df55455cf90d0a32e01a5bd47ce4bae2e2663ed9d7c8005034fe0718d3bc 42efe10793f0f6d6c16609ab69f74b9fc833854b0ad3210f6fb94b6157defd83 a0022e91523aba078a3d3a484f69b02fd84913b6c42b21a17310ff3da504aba5 75ec91cbeabfe7cc7ddba637140f2b7ea4a087e5d25c95555b68498a0fe3bbec d83eddcd8be1ce5d6d1e38f7ff966e8d36220f062cb9893570f58da6567a380b da104c878468f5e3f20abf0140b868deb77ecb09af221fde94ff0aede8ebca06 6 56654062567ba62e73d46b53c4edd269626f1d636ae9f23ab9d898719263a0084ee054551508c8c2de22f48c633b4a0e43f094c6a23660868ee36e47b7e98b04f0e887fdcf6ec56e91b48722b61cfc6fb59bb1eb84e6d13e78f4126c74bea10a3bb871031c90fd2cedeba1aef589aa615cbcef553e1a3cbd1c85b62e03d5f70dd263aed628a1b4886d105a0dc864af5a39fea392718db8656bedb924e40d10080fd7038d0c0f77682871fb1982509349fdb941ce9dcc9be3ce85b8ee42d6f206982b96875f2e0b3e14000b107767e45ca9eba1ab061eb2a4db61783cd8e9c00d939eb83a3ef1caaedcc26fa24fc0c7d4a02693781021b2bb8846295280cfb3036a3e2ad5698696b5d7d3fc6cf2d66941395b070e1c3de9d1f2e2c5b91495750be9be40078793283c3a3f4acdf6a3af636da5d84e0d0f9bbb2db59c18fdcac20fd6a3b5d333e1c60d29737fde0c60f1d9d441c7939bae48b46cd49762ad2f560581b1504365ae5f2ca3ce355c0238966a89913abb8dd8a924ddc9d56136d2e50cb90b8326425083c2bbe26bc7b5eec6b1874b8514f4be9928ab711968a2dba405f4814e3a4d12046ac5c33cc3d8047a7b462b3505584167fd5fd73f5742ed240e +generate_ring_signature ff6febf6e7efb7817a591221ab81ab0661ebb595f99c5743359406a6ff022850 f586b7d4b8cd064df16a8faa9a48acf661ddd3ccee8c7ad982e941bf880ce781 1 d6fd86f974b4569139ca42a1b3f8ff43455036ec7a5943502ab3e62e3137a11b 0c6636fe36e9a2c647ea83a364b15fb56311a7a7f70ce2faea3a9c83c91fd60c 0 30486a12f6a5ad4a66b8a1e699dacdf07cb6ab1b3c8f2635c30a64dc1f836e0d5996e61d781c75e1b1cd6ec420a0ff6d2c778b328b8b6bedfa46b2c885236a02 +generate_ring_signature 8d59d6ee24ee5dead6b5b91ef2937ee5c5deec5ebe36340230d49ec2bfae2041 03482033420d1bd0fa69cc6be4e5ed096be1b04f90cd21e04c355d4376d8f2f9 10 f8c23e3f0216a54ddce001a485ef23ff8437bcb2d7f1ab48ae12ac447d9006b1 6de3414f6b112c72222aa105dcf062ab3dfe2785a0f0b3516a7e765e8c2fc27a eb91adc0c14f2dafa266c56522e037bd543f6dc86ba6eb6846cf225842fdfcdb 5e6f0d5d54f850f8e4a4307c3fd2a5db6d0cb27e2f7563d41739dcc27a208e3c 4358912b1384c985e40b6d20f531231361f7d6ff01a3f0a6ac338679dae271b7 5409dc5e185443550599dee57628b152cbe1b783587af029a131a26a2fa2cd46 36b3945ee3e4915cd2bea5d0308f5dba5179b2a99f7970bdb999aea0345a09d6 5c356d84ac3f23ed32b06d2b52bba73b578e06cbf2f8915bad92aa93e100fca0 6ae6ab51d430756ca8a44c2aa0dc558623a339936e7c653c0f3b2233271a2050 efadd5d0434c256732af421ca95921cbee871748b5a61482181b63b42252c766 d07e7cbe44404cb290e2d0629662ba0031ee637b15c0be56350959e90b759207 1 5b9a7181bdaf5feca7c7d7c3354b5057513df8761c7d25fc3b50bf29cae5ac0479b28460ac771ca391b1742aa7c4db34eb1f0a4de3c6893367235551ffa6d300dd61880e846e15c90ea36e7e951ad05769adf6f24715a77f153505e10d5d2207f13f7ad634f66ae74566f55409caf942340e30022e5fd8042e4dd61a39304c06e3df8445189bc1593bf50e43a3ebc8e9ba74c4bd86ad8aba0ffb3b79d6691b04df5ed970a8c13987b132f40bfb19396b145606ac50bfa16ecb9a146b6ec6b903af07a93e893b90e4287f0354d0faa6b8dec3ffcdb9b61d028848da2177a0160075df7551d8da6b8dfe2e93ea718854a5fe116d9a554e260c90d76bddbab8e20f2d80264c5c24b685cf0c2e58d7af007dd49d6f52abd35167731303bfaab4600adccad9ecb2e6b74d45f4917c779443f0f62e75951a8b5813e57fb270a6910104978bf1ce768069d9419e7d96d363775709657ddb7d6bdf3e962d138c57be6900d78a84dbf41a6ce2b01343e20b13e6b14adfab0a8e462bdaa1d63b3f56fc24062d4ddb600e4eac8169ed97e7fa3b2d3735d648236bdcd7dfef4269791b1648093be81afcf60c8ed67512a80d1ecf2274575c6a7b4cec813223a61f1147950805dcc702e37ef783cbb74ce5ef3c759c2a30b636d20c26cc03338823ac9225bb09c97e2d7b5e26462e7677e88fe8a098161d7f28b5fc8651a081687f187ac94d0101af7a361f2f2f591c26f182517912988ddd47a8e78dad730fededfb3fe95e0ecc33a4a8842f76e87251b3d80a8b1ea52cfaf2c55c409a57f5c6e4577ee67e0f2d00387edef7312c4dd089cf8ffe8f4aa55afb79737bef19792674435d3cdd07019e8a51950091f349eb8348ebdaa81f5b2a31d7a6c208454ee38d9ba1794702 +generate_ring_signature d34e7e06f7fc7547af940a6f18f167c7121326b4ac82ebae7abcfbfd96780997 c22b8a0f30c6ea827dd40829d9eff83968d3800d30d14a1bada0584cc5b5028d 130 1b045b3781dc3d9563860caf2575c24584fcb132e5d422dee2e89bcd767b98cf df8588308c44c6785d412f22e6ba973309c171ccd892d173085fce4bda373b15 582bad5a55b36a79a82757f17434f101374559a32a349fe61b90653758c4476f c63f754f1584894b342ef5d9751146813be816c20ef0a74469bc3596e13c904e c752d3fe8ef63bd6c8bf129b434d7e3bec688cae51d79167fe9e63d0446198da d3b336720b4ef2e568a8aff5d1293968cfdf9f94c73e69b5fe6a346df83eb403 4922a7a9e39d4653d687cf85083a0345d111a9164d1bd5f1ee79153cd2dfa3d8 b7b8f31267e73c1df66c1683b51bc2933975aeaee826e039c747c5b1b2dc56d8 2695b2d7f52d78aa027898b1759e293e9d6f5404a644e9a0ab3ada0cd66ca03d aefb1ede784cc09b305d972b122d7f89ff263804fe59879874ebe50c437de38f af1cd6fc9a6fbc3fcaf901146d4b6ea8f4a8c6707de8e3e29586b03166a46002 f686a3f5e19e6901ce874145cd36989cb8ad30562f0bd2758e97d0146790aa67 0deecb3d34a2f6a9c67dff6cafe3eadde8aff98b09236596057589493945ddff a0ddf06d97a918f49c91c80221772e405070300671ceb2e2bd46259f3fef9710 8f81dcd3a6622845fa47864364d32fcfe6bfa23139408e27764cb5535266ddb3 f42bd85ce2574d0bb4443b1f4a8988940a2f84d09e6069ac269375efd68bfd4d 186d37fe94043ddeb4c039b7cfc7b8116bd52ddfca638a6d0e9c3b1132ecb98e 41103a9b6b7bbbe618e9479bbdf408fb80fa504ed9c19d0feb6b3b1592c145c4 25886e894abab07c0aea72ae5f9dfa6f04a31e55c6dee030898a36c60dc89eb3 73b064f0e882079edb8ba1883f06c98fca995c1ea9ba6140e0aa6bc0db278fa5 585993e51b6bb59c76ef3c9c1c35e04e838ddec9a559fa62628e4feaf67282bb 36075d3e9317c8cb27c54fc5580914dfc2f7c1aa7f4a06a1666a5a063a7792a8 36ff186b989343ff47bbef3d6095965c647bb9acb78323482ebdf8aa65541a75 66687f47bb9956721615d05ca6ebeac9ca8fce2617416b9e84e8f0c43ebf332f 52efe12e49e0338e20fb70a3aaf4a8eb15627e8ee145ad84027bb3d88ac64de5 1b23c65a4cdd5b3f42f6cb1ff557e399d2ff130e9c48041c0e5f7adf44bc499c 016c2b0059d5f19fd4e8c2b6184c3436bfa5afe2d2a354bed5b724a00e36b0d4 63fc846ca7a4a8a92995d3de193f0918ef243aedce227423bd8141b05b04e4dc 2db50b40ea76a65297e3e50477e2fc47d91952347fff79a514dc20e317b1bc0a cc801843f0b8dfe8af9fbd7dc03d18402f39e3983c58d2a26e140e368b3e5e23 c524235ea36bf5aad81c9cca7f0f4004f97a1d9fa08dd6b3445b4df2d4aaeb6a df74abd2db639e669a40dbf1987f7d02cd22eb9262f44a4eafcf482fad7aa68f 3bd48ff167f820e0805f0b99a1df2c6183dcdb47200b30eea4aee5b1b19a8be6 46cca4700fcf1d30455694e5ca33fbeecc2f8f22b6d3f135904e0f8e2d905b47 c65df4702cd0c833315a91eac1d0b2c8c58f7fcdc12ff9ccbce8bc3e15e6a13e ab7dc47277b143f1ea79c8503291514c2e76e602d9ca62a95e8aca5bd98e4cd7 62ce7dfa1596109407b6f4e30d2f55106c61dfa3211d0cfbca5517fc496abbbd 9c92cfa2743dbd1c9fbbd6a34a7f925319823a4852a3ee5f2559eb14c4bf42da e2d7c85ed0f43b75849b791a07ec70ffc2924bc3d137cf8f93bd8a0e0cf9c342 10d4514d9eef6d2e3f8eaa5f4526462b5a205a29f757c1259d8bbc54199667e0 4528fd28289dd79d09e15f7d8a361492fc9f1770b88bbb9a3070171069f5ea79 b01634dd37d55d252ffb6277356106bad6ff9105c7fe26988cc12df68821b1a3 f50114b6a94d3965e94b0cd9bdff3db121a0862e22c7408c8cb35b61f908cb84 6b0d2aeb80b0016c91c77adead0bc5a5f2d96ad1b913e2e71c4b087e0bd2e7d3 52d9183a3ab5712ec9c4fac3c17d7eba2515217c3f73e2770b3d831c164eac38 67e62fce8c9e1af416e398628706fe4581759cc8f31a9567c81f2b415b41b06b bf1deecf47c16347c544839a63b2aa5e09693106284288133fd311c8fdb93804 5658bffe4d257dc661f8a4978b1a1c8db5ee42d47c4536a0ccc86c4cc1533c83 55964eb9259db44b01e4bb73f53004846e97d47a8dc7cc81bbfc115092777e78 44e629175382cea99e2c2b9f9c40a06d7ff9bc87b12b08548ba871ba780eb9c0 a5fdc376273b174d0bb79bed1af76b67ef68a88b31021efa930594d64b092fe0 43204e6580319371026f7d0e7ce6eeb66df90278adde7498710dfa9851cb6090 a844e2f627e176367c6bc02b374eb310b6dcd294d62459a35e1a7ab7784e081c 7c683aa1d6acbd9ccbc83e54ce737b8e9630411f64ef6ce82dd7041edea025ba ff0b62e54844087a9227a2e8bcca1f98bc8b34837510ef5a24599a0ea89b4151 4dd866c24d3161f0747e061a234599c0ff2bc11bf4e805f1b8b9fa16d0b80e41 e3e21c255b1e8e09f7fad2c047c459a17d5ba5705c2390d96f6040fe7bc9fc6f e4fe7ce79c53a98682427097c2174a253be623da5daac6b67adb71a10b9dc2bf 230301b60c986b756831da62c0b013030e4b7d8c20680f5b32f9c793dea8f84a 1ae77580ec27a00019979e31b3f50ca51b16a428f78fd400db81ac2cc584427b 76e4e5926bbb8861b7f02ecf69d6ca62c4082eb3be34b1a4ac1a0210532df6b3 5bb6689373e4c6448f9e868178c88642911f91a413fe1a4ea6d9a561129786a7 320f2a563e97530b59e922ddfff00b72a47411bea90a72af12c18bfa85cde0eb 4de208d59a069a1384059e6fae4b56389cb0ea8c8b9fdf04eb6ead87b1ba4334 1859c66805cdb5f27e683380c65810355462eb0cd2d5ae36b1514c59cd25d87f 4c18b00a47976b540ac47d89b871c0c14e2ddc5a72a26976905c83bc034dc776 038f5c04bbe5bd0e021dbad712e315a035c1891b9b8bb8a8066a6a80cc942b1d f195faaf5ce5145a2ec1e5cdfec07f37aa6973312cb2dbfd962ee4220c8cc40e e526b37bc64d607e165a7b462776df7ad810c34910c1a1fed93fa8745528d6e9 e0bac6819157a4e5046e5f059ff30536156a526cdb2e6b07ce16e11acf471075 ec4be6cf12ceeec5c29383d21ccdc4ea2119b5847e56c3283bc50a50fc728198 4e1e79eea0fe39bf4bae1525a145a1ed0e67ef13c4e9819531c2d4a2c9fa63c1 f1f35ed89320a69fecce4318d91215be53a2d1d38b382407c2baa38a169dd18a 10450e96fe80e622dd4a0c950db223081ee6751ca55f3dd248a8a42797340622 e1fed70c8014ebca37e0089dbdb4217c0f2e635bd1911405434459cf045209ca 14fec244526b4dca98bc01bf63ed0f13cb94821d21bf00d1e4239d3b8eb752f1 38871a4f634eb7021f11c750d05e14552b7b27a42de22d3ce9ce14d1e97881ed 35a5a219e6e781be2d02005e67d05e4c07f90b71c42ce990bd0d9d2cda8804d2 0887d80f8664cc4a6a5e4d9f68f31d2e6fc031e7d98bb12c68404be7a3e9bce2 97982a582f57fa68588e309abedaf88ead57f8b91ca79c8a4b3fa8cba737833d e797512be60cff64f47a9e583b94138c98f761af3470162a348663902cb37947 46ea126afa55bdf7e00fad07250b6dd1da7e11d830c66f1322834e71f413ab8c ec11f5ea87493c4d2571f74534db92675983594e2869c9dd304bb5c6a575c326 4e8900b8f8ab056b1ef8bda099934b5be14f2e4f051adc939c9550c49db0060a 91600b4b66148e45b5732f4b0e215f1d9b3856658ab98043391e631a4b4ff41c b3e6bd7b14c7fbd76b9f819444e707ab8577ace7344d924a0eb568b2492884b7 ccd042a6c45e5211465a52b98bcc436d7c2a66880457dc77b3a2d12cb30e02d0 9d3dbe53960bd0d10dc44c39b2e5a4d1c546be8942d855b1945d7dbd9dadbd91 c08b42c0af31c84a80e4a7da8e4474f82a27b85d8b4377ffd51a3f3dd238b95d 1c377e68a7884ced573a2fccda5993f1f67a83ac1d332e504de375c43480644d 63c9d812db5ec9c974e9659f7f3fca55c8ffe0d1e6d6df458043d24bcb3abccc ef03bd91ee0ee790c06f658b06e3cdbd5e0cbc5e67d95ed194118ea6ae63e098 6734bf35f726cae3a909fe1eb3166453c92eef3bbdeb2761d20c96a0003fe889 eeb55cd2d6f2e6435c4a906290ff0f181040df496cdff570139df8c86ddf56a1 c20fefd773e4042c51449f247d2a2ed477be6ca652986eaac27db8d7ab3f3c96 cdd8d9b4a7c117f5aa2a98ce6449e32ca8093e8aa3eec624dac7946d9d65e671 d8ca999c5c49199b7ebdfc9bc50f599cfc46b06797817c301ebd5f7aff13b36b 65fd016b2dee8e4de3da851cf49c5976d40cc431658c6a1d3804b04bd329594a 4445b95c277b5bb30f1d2a64c58103aeb97de6403875a306fd45c05ada0a41f8 cb7dc6c4574f35fc7fe786a7ceffd5ec13970122fabc3d16ee961db16391d1b1 a81e7f611722c1b0faad6907151976327d3141d9e3d8ebf1dd824d5523cd023d 96b2e2ea0e7da769f12c0cf639984b62f8ec2969b866171e362c4c8af3d10c0f 6079849c5d59424a9b5c884008ebc4d55c7001bd3e60faa0d9f8e359ae5a7598 edbb030c042f7a95d699d1166ef394365eac9fe31f845b461433e83b4f7cd3ba 0d3c0adbd204d0c8644fcbfe1b1a44b536008d62b9efe7de66d9d1074f2a103d 850bccf1d6ed7b82e7c72e9713b2e81c78699528e92c78402140c90fab0c8fb7 57d9b80fe3bb99883890584116174d19bfcbb8120110cc7c7d4cf6efe5d4769d 457a4e3966ec08bee0a690ef5e916d2a6fed3d50b47ca2c4fbd417054911f100 69da5dd4d636d5205a6f96ab63e7cd9c9e649b2f9a5c8a3d945156bbed581fe5 5567f88f4b0b58cc2ddfc7b69924f5fa1c01cf34618af66070ebfb3f8c515f5b a4593d2a3debfc9479dddc324376181aebfcfe46682482f0bfb5dea761124602 3e4eceb113521cdd18ee7bdff444215c0da3087b84ff057c6dfbd9da38320bce 152465b2d6d3055508378ba2897f152f00514ef02d50e47d87cf7005c31328d3 5a8a2a885d612a065a7f184b69da8e31b89aacf3719de56b779e03bcfe2f6ac9 22e38f454d2a41b21bdc4890c46e5c2f708a9a7191e9847d64cc968bc7f0e927 f45ba9cec01fc0a0f20c7da93d91a0a8574037930ecd9af2d25ecd859d06b49c c9049820ba117bbcc61c14346a1216cf7044b3f9a24e358d8522a57f01ee7fd7 964c37dbf8fdf64eb3228a2b508a3b66dff89a6cd03f0834f0c888fd35ac56ca 2f8ebb8257f4ff68365f2a44dec7dca5c6296578d22857cf53a88609ba01f218 d8ab55f24eca07ff3fc45304804a30a15451f3e2f7ef5e4a2a249b138630a34e 7e7e4426e9637e10c330801eea87df3baf7606211d36ee23ea24809d9b93d46b f175e191d5d7c67089975d699c162732292a09e40c4bfc1a8138375d7acdc541 51e0898593a7f8fbec48f1af60f200884c893ba33772433d7d0bbe178af74b37 ec0bd6b6fde56d4873c3910bc605c200b09dbecc354f1279cb4502b38f7f577d d4f98fd898f06c5a3bf879d05f20770747b508d1d3ce026db7dbe96e1b961d75 371249fc9273d9acda43eb7234c94ea502571586ef4b09613340d0656f7d393b 4b20b942aa61a7cca2a77d1bcdf95aa2fb2c93866dba2ae9c565a30592bcd18e b531e203fc5bff9e945fbcc3088a7594b6ed8104261c329664e2278f264062a7 627306c61c458a88573a1ef49a401cc44bbf84f3318e0eaebb046b788be22776 9b189ad8a601752d38d8d9e34f6e003f5b96a9664cf40d82cbb41de4c2b047d5 dd730a9b64cfd04518fc3b3ae828dd1638b4aee4fb7ecdffc35636d21c9cdd0e 120 3c39ab7eecae26957aecd76dc1de07e448e42a561dfd3609e7cf410ab3903a062c410a22b08d0d23233b046ac20bd018cdb6dfbb6893b20517cac433396c660114932711304f43bd8f3b39fb85cacf7901a1712589d9b04def30865a066a6903691c30ee5850fa316792c879b35af3fa909fb3aa33d0cf8759a56a5c8a17e00ff39301d366eab6788c9eca765a7a49fe18bed8e3973b4a1197706e347e1d780053279373ef1c01e35a09b6afd9c3062d245cf66ce1c4f084e71edada6300ed0254040e8aeb31a330340215914adae1777fe7b3a235d727827329d582c8fafb0c95eddad1fcbfff275192837cd5c432af63513a67b663a8117a70f3baa2bbb003e8a9acb7b6f4d4b277fea49376ee806f929540b01fceab083d49d0288634c20ffe712c0de3e0d99ae4f718490c852840fb0ac1e4f69a7f8cd074fab6df477d017fee930b67cf08678bebc15292e0f81bbe701009b205ecaefca6058b87daf00eb584b7b2e6efc80a51968e741e4cc735fea3f47505831fc7012ada2f4e1b820d6603e1b38fb5d9081d0a55ac819f3ca82b83bb70cd2e4b8f9bd834806a7ce90e1e8f3a3d27e8f7d0c93cc14b2feb2a5c8087a6db88184b5fbd659ce968bf3c0b58acfa89bf851ffb7af16880706fe91ac64e2a016083ac55d673bdef0288e705980670519a65a55ec6b92dab94e0139c8daa5d257d067e91777c9801af1a160de8ddddd3b702be86b3ee74361806c0cd8cba509211d55fe157a4198abd2732013a158be5bd0f2c4b41cd1418f9724174f4c39d27a6b9f5390cc2eef40b7a7e012295d3a2921ce9102b62e4822b8b5e34df993d5eedf0bf3a011c001c7c7c9e09670206fd24b6a9eca9acda528f6f32befa273266a6ac5bb62b55274494aa7803ecb412d4c10455c8547207b4a915c3b853b736c0f9fc9caab0c3b53476a4780f6e7aa51435281a573eb7904ab08465c3ed4d25d306ec4c4ba014dd1a84d431079a2d10d5f6feecca812322f57c6e472ca32a48c7afdeaa0a488266bb8c21e7030c49dfcb7cc96be55be488173d3682093e5b3ddd69a303d848c75149a03f98001c211670d4e02244db7bfe827af43df889f21fb491329b744dcfdc1605971e0abb2f5762fb6c215d570569ae02b3fababe5bd9cb9c82eaa35159eac4d3fe5f0c20cc869f8cdb296b9cac5d696dd5a07143c03c2e358b093c026a7f48e2a20707bdfdfce0be251da4f0e8ce810d9130b99f030da65db78543f14ec53282f5330f9f884d8f841e30603f762629e76a11663cdde8adc06811c86d830969db6d03021325a91127493e67f04fd35b8d0b9af84faf1ccdff4eb839a1c40302ff0722090a597a26afcd478f054148eeffebc269f252a3deb1249aa6aad53451d5c17b0dfcc4abd30b7890ce2f5e967e6335180695f3ba4961ec95ec51f9e787351d4808792f4d98e8c542f66f41290cb0d4be6905271f6e7398eda1a3318e886d17440bfba5c340297330ddb62cc2e683cbbec53725b4e5b07bad47c6322f78a6d7860ecd635d3529458c2b1e402d0de02e070909d13d8ef46b7f2cfa8228479a5e7204af46fff145529ca4803c9eb835e99277cb835b5ca3c86d46d3a124de8b6ae103b2cc996e676fb9b1501ffdb6ed64ac16460ef82a5c916e1ecf815d94bd877c0aa7516a83ad1144dc068899bd66a865ac7e8e5255cfe4dd742d7dbada6900e4025f34c8b620948b4a9e3420c347f5640059e73701fdddfca962232b83a7bd56051a49f82ed7318d4b8fbc9960c2b723b06eebf0c7721c9a534d92d58c24120f008031b351413b7cf104562a830378e71fa420ae83db991d6ec9a8a2ec5f40140c7f3b1135880c159c3ff16f3ec956e325d43952baedf78aaddcfc5b5a842f0105cc1a79ee7db794693b887d643c5670363d6eb755a58272306a447bc9b534cb06e3ce26fd7a740c912c0433b5c5b4a7a8105ffe81880997cbd12df48fd230d800805fc2cb43b97b166925913ec5075a84f399bef4150203e7b068f272122b9e0b831d871232a7dcbd2c852ffecba003b231a849487e1c5eb1ff7ce3a038d66309dbb11264fd03cda170e5f17635b55df65c2b01b3572661b06121790ec67d470993333571772d7257ecd75d5e1f52228648a149fa03ece06c0caf0557be73bc07331912da8c1bb57baf0380f7bdbe0c88bcf992092c81190b33dcc49b5341c40fc08aa8032e74cabe15c2c89f847d26cd81f6314db7cdbb2f15242804f02e6806395224e02503420c5ab37fdee8000d2edef2da1fe49c6dbd533249f2e3323503e6a68fc10dacfcf01bca0d086aba7570ac5b7d1eaa8b6453dc4b3ea781bfc10a62cbf3123abae3447670a93a6b4e02841e0f472a00489678afd20094f3b6c3008c06ed6632f3cfbeaa4e4f8189df1c17bfefb13111976ef54adb5538cc5fe20e5c75cfad07808e785348ca786079b8a5f3c10bd0989c6f494476fd914ab2f10d247982ac1078184e97532f370e769da920c32bfa52376b02f5964737147efa047731af801208facbc9a0a561dbf9020c1e2a18c292143288edfa81d651ca4d065b65287050ccbef529f92674da14b2da0b925a094083585b89ff261dadfe720c32523c0778317389ff0ac0e973ccf20725f7301d1f185f39130b7744009bde0e0dd88ae0e1fb5bc63c71431a4845bb49fb46bf386c4810c66d2efc8d93e24607c668dacd6f124585fb7326d289524758f0b41a08a77af4c9736d86242d1ac70553742b43c08063449af8b59dc0c3a42d162a4a315bcc146c0071e9b3bb6708022d8f582ba11e62e68a5f5fbd1da6ad783e111477f3ab8e606fad29739d73ea00403baba0a64312faf8f5362101fde71c395c8894c203dc41b4aafee11b8fdf098198d911a67c6c5cd922f1e1418728dde6c73b09ca20f5473f5b15ffda96ea01e84c62df7d12f02363ee5397e219038e5114b717e6399e954efaa5a0a8f89802f9c2cb2597b941c19353ecdde0de28d4ac0e0333e1710138f45e0f69a2db3401ed793dfb3deeba8ca6017038280c6db571b440826929a485bd5096aa2e905c0bac7a007bdb4fe560b18a095d0ddc38f1bf4c4bc891cd22a9025439b45ca949089298fb240cd48b52539ed3fa710bfefc2a3feb2c2da211f8c8518e4297305d0876f214f0a9cab927509178dfacbba4b5c0869b3ae8e9000d71145d263d072f0cb80f41b77f3acf1132b809f6a18c3afcecb436f19c560e1c435d9fcac33542041d6d941750fcb886e399d4d94a11d3a71854ab38c87f42928ae496cda9d13e0058859a5fda8b4ab0f187b65ac4c4576e501283422d6de5e50f86a2c40ac6c807f18b83ce4297aab48ddf39ac521373491902582913dd636ed6c856372883040fbd5fedd42d00d56761bac266b7a3ea9716f3d761ecea5b7e8bcaa7477a51f30009af143d22c736e7e0dc422cf38867ce587406d55cf3f5c1f522df01b3ad7e037a6f109701c3755937c453f327de6bd2b20849b8de2277bc01499d8f5084ff04d4d766f63342be77e4342112aa0454d6a7b4ea1214d1c13ccc35a0d7afd31a0478f7229f236fe0426cb7546ec6963c34fac7e27710d904617aadb2ea17513b0dc026c8660d887bb75478abc7cbe6346d21bb4387bd92a9acc33e288884a7870c87d57e1447ff3f0ee664630101aa1a540fc8951577b2946b53112aaf2913850874a1d96d0ec2dc9c5f280886ea3d4ceded36142497a339610f03e48eaec8e905d169f7e9837cb86ee4f91c2290c843b055bd3d3605b40471c4148a8a37d5bf0cc4d12010023a6092b640343a0ed316d1c098c1dc3119a4f2f195519f048bcf052584163afec3d1f2ae77100b02a8dae3a8b77c45ccd5ca267dbc428c4b82d402a37c35c2593cc41e802c745db1c9e08faaeced24342af14be37a23e2460c01078df0802260305e5257e5fdc92fa343295f3d06cd5d674915c07e3a31f34380079ed7f34a297704579e4936995a897e9dabeed33f3af49b5b553510eeb231490339a988f9936612d67d120e428c222c8175b3688c8c4f068aea65f329292c94013741c6d3b90d97b172b283b4b43ea552e09159cfc860e30c0f377e785f7e580d4e7b17b87b2f55fc8111324741d46a243fff87b6f83c7a76171922ab657a9e053bfda200c5f92a62986bbf73c6b634a1e294641ddc24d0179d1c13e25439550ad9c8288b79b1a5cc94b36570654f7dc2380b0b393e82179df380b683a7eab106f6566727f93afeeb73af7cf0e0fe7175fd7d1b0b8827b65ed1d0696268c5a90baa93c0d2e970a8fe9c3cb782dd8abb1b2f7ed22918ba914981cec5b6812a5c078d6179ea4805bb94a92f8c8856519b710a2843a448c641a17a4decb0d7d4350ab7b24dca92410857dc514e0c9e3d9d33fbd4d2a6d97c485666f4dcc3131614058223bc35ebc1dec077a7784337c4d55da2c3fe6b8403acf365ce1baf38dd780b485fff94da07685a4c1deefcb42d76521775f4af953854915e3427d59cd7ed0eef61be7a3bbf9647a13c4999d2076af643cdf5136dd4002d67cdb9c40600af01a827ecb2bdff091163e4958c460f1f4194bf023b9d5862d74ed0483da92d020634cacfb2f04a843407d6efbf5924c512ea51c3adc263a6c458d6302a8ff6520f4bd945ed26c0fc088733e2b6a1febd4c7d1cba2003cd42c63379250a52189b0eb4c6e4d4a4926d6f2832e7261ebb07a8dff136322f065249006f474def5a680db5743dfa5d09ddaeaf873c3f532a1aa883bec2c0d578bfa3973cd9bea3594d0fff7d5334435b42fd7c47e47022ccadb997785c6a2c3e22129ac501d6dc2e28073b923a0fa9448633d28d402e4720cc430de502a9f778d71ec01620658fac9103a2e9aaa58fc5559abf9c3ba0ce4db069b501d07119e6ba97ec12fb1175359a0cbbec03cdb5445770e2599820e1ab3b1b4c4f5b75330067c5fcd3e60a48d2ed0a959b4775d8fb0e52f369dfc039bea3fe81c16917d6ef8889f7fe67ad0ec7e50044e15a0f24551c09a3f15b563053ec77dbf753df36f5e582a4d5872f2ccc1d0fb4647f37248bd5fe6b8f51a10eea8fa121296f8e87410c208c4e7faedacde1019e61e8205f6f3d6c4111213b1bfef5f175b869518ebc2b4d194b84a0e60c810f1f72043d74a1608d0c85260078db0a6445b6d784477ca95630b6093dafc5500c9a2611c54309c9101f421eeac9f0018428ce468176dd53c4dc5c8960f2169807f5d019e46a185e7077eb502e116982fcc5b962e5ba3233d6feff440d6bbaf2098a7b832fd1d59f48e9db83777abca177d1c5bd21f7be8beadd09b94fd981b702ae06b75643c31f08d45ee8106a6c1073f0da4348d4d9f1c5da9aeefd33dd0f0fae539d8466a846e38a65235f4ada9949362a49b9e8d5cdbaee714e0e9555970144a616bf9e67e09a57ace5512be6e734cc3dde62b6349b5281bc5e27fa8e5809fb5cbaa67629d6835ee11c0dc1aafada214ece29e36e7a5b84bd5aa7eb6e5a0e69c984f80cf7c5b44fcc5413ada2c00fdeba5b28bfc54ff05f676bd4e4ba3608a5f04be1e0f52f7846b24f411fe5fb16afa965cdb350ea3453cfeea210915d0c5d4cf2b6faf05f6b7f1cc5d948a053e355b513b4b05526f481be89a80d91e40a5156a76f4cc90f3aa19590d67492e0c6ac6283c4b82f766046079ef0cdc76809fbd2c1933de913a32ba576338a60adad157c18988dcac393eaee7045e5cb770e8773ea55d6756c410df59ff71088262d1ab62d92817563cf3d40b504636f4a0197b7c74be6cb9aa661f12f79473961a137ec95c52b23c28269e32ee53eac9206927cf0d65be5c0ec8dc984327e231a739432429ed3334c56172dbade5b3b0f09aecb4da0e696437724d1f66c2ac3c3b8fd786a4c2022d26ca3217a820e3a6b050ed7f08ff3e66adeb244b28020e15b23647671c5ce973fa2beec824f8a6c1e0cb0c953aebf97d1c6f4625e863d62825dc062ef212fb6ad964077ab25e4e80a0626091b64830c43fc5628b2e07da81c13595054209c93e813f99e45f2a6981104734973931b45a7edd7795b2d9e8e0384a04e995866d29f1cd7e0b77c3f8489088b7af35f2c92ef03cc21f5c727caa23166a1451d17fdfa8ad3a97f958e84910e8ccc8fac9f3e1430722789803ee8222371bb74ea5939a817dbc0f6ba74bb490849b8d446363a72436659e6b336b562ba486796448c2497d121e5f1de14ee4f059003b274f6421820c589dc8117145f166931097d865ae8339a79c9f7dc020a0029b3b25688e41c07009dfac52de261d17e3f9b464f6727dc8aec0936ee025301d702a9f4582bf3c1be14e697da2a58909f125f42b66be3dbc0cfce31c3e09d072898b2b1508ffacbb1821a3a917ad17823525e9142cc6bbd84236e6cb913910c74db8c51f66ee08c0daea8241fea7cbcb1ce6c18ca2bc26d415d39b3a877fe043a3885f3d653b487f841b54576fed5f697b48907dba46dbd29abc91f53de350d7698dd6a38ff93919be67d0f6c3b44e9b2e227ddd099f6c3d941cd8c69482f077926eb9a0fcb13548718655acb0d1e3eec65cb88c6d0ca5b69d251c50e1b3809e1fb4576edb72001766aa2c963f717d7d96de22f5ff7772f49053e216ef31b0d37fa905bfcdae88b0eb40f7a0e2724c212d6c328f765e5b8ca0c185c6bc799061c88463409ab22fd54ba8c65ae866395312fe0eabe28315140d1922a109255072e9aa99d4a1989e2a0d0e2e95853eb5df11378d98b4acc1a7b1e31bb9d5d640c4928cb4c6f464a8e34e9f0983037288503d51d0d7169ae66f661778574249b0cd76f9814b9a52fc328fad2b777facd18fea6d7501f5f44146fbb012193655a0a306564ec51763198398a602939fa9ca73a8d725e81d0b453d52673d489d2aa043c368fe7de2b61e0f23d024963cb4fbf15b4e16c30b6b7cabb7ca4259285800e8c20f5a215822af371422130945bee307eb00967c66179dbb5fccf4e0bf4bb00e6bacfcf74ba9cc4e849f346260f0b3ce81bf1126c715fc2633422851152a00b18830c8a9ae90180f87cde6e025bd4565768c77207dab6505f1bed03d8c28507bf77baa1fa806cb983ba83636366d5f1888071e755bdb954ab270a303c8dc007a6bbc64d54cbeab53a11151f926c1c10dc41022b0cc18c010b4bcd2af9bf2407d577bb40cef488994ad1659774a6f1f0588d0faf2e89ad5539d52172f4fb2f0e892c04cd142a74b2086d6dad64c8c11a522a84062a88301799147f0f5fc6df00017f18cbc07e9fa02e11d5e0ac27bade23e08883c656938cecb6da53f17f650a7ae8dc8020e749641f4e9632d614bc61cb88cbcb822e4f4bcaad8e82692ae60f64a357ea047d4cc3de9ff7f64ef19569e752db5f279f01aa313218f4f897df020c5f48c428454b4554a30e2caabef575053cfdc55e153924f6ee05e27ee2c6076a1caf806707bbf72e74853114ad36d71c2df1c94fd6180aac02e8d569005b07d0621e745ff61a80c60e643b3f002e69c712f1cf672310fa9f67cac36eb51701dd5122da5be81457269f077baef6fdc81b9b133423d1f8955c2f7abe8c60f10302d713dc10135b5a1bf0d0c9671492185770f71b6840e5076438b838d754d3000cbd02de354d2eb12b92f4766fb8bd157717f22ca165682fbcaa6dae301184056d4ead9f35c1555028fd437f880b0ed6de6152bee7777acc28e5a949c3ecab07f12c931e8dc5614456d877e67a2bcbbd0dc6db18f78b235565fd8b6158e8a502e8994e90f2755e10f56148b8fb70606220f847ba01d8297f369f378ca464e90a525ebd63cf795d625084cc7d9a119e6f30734f8655a3296b8b2f0b8d8806380a8aeba130394c6a271fffd4da4dd98e7a8377324c660239991163f6941b8192031bab8b0760da72bd00d6c4954543ddb76622f543657821075da91b85987d4a0036017ac76e852b8f7d469319dec551bb4c18bc79079ded43acd0c616633d44049369c5b3764b0a66fbb5d09adacea2090005aab6e7f1f328b3b7120655c73f07e510418e47cc63fa0d877ba453a9694d54976dced8b76a03538cedcc2de77d0d191c2edc16c653c8962a765d7197b205ac0f2a9241f23054d9fa58bc5b84e10b8e080b0dfd124481e16785e570969b6a9d8e46257a10a8c7d101b62c787ae40b4049c617b33d8db9650f4316c4800f572bd752f9e3b052831fb21a9129a3fa0ae80a7c40226de8b47b86726ea144d174175201bf4483d7570984f461177e160bb3c37e59865c9efd2596b76f2cc6f6da2f98ff4d888fb8271e474b7aff58d104b548de7f41bf13777bf5774e0d19e80ec5ce2ab1966ff61de2d1dd45ae1ddc019c8b7c5aaf58e96f7801d07d3d1eaeda30c86b3af2a2fe798b392cc7e7db5e064a7d0657819a2aa53aeb06c69e8c5b56bbae4d13f002d84e9f9f2d25cbd10900f34bf5b8e7da18ae420c327b7e88e4b63769c01c9e09f5bb2641ef599ed2b1019ba2559a71f20f65ab7e4bae52fa5446d2e40fbcb4508e297343ae609a227d0f0fb71c0dd4649892ff80762ee499a01a59287c51867b6715f331b361e0cd0b0cc9d20ede66de28a535a8b732ed0ed01aa66494299fc449a4e86182a1d899d10625461bc0dfef43437467844115a5e7797e7aaf052376ab779ced39735f66d007e86ec4434a1e996c338518266f882a92967f9a289423b5196c0e28bec4cabe03892ab37b49e81e8924294f38217cc132354ce40428dd7f7c0de1731b31fbec0cbd84f0e992ce6b9f1ed28bc102e42463a5b96a89286719f2c916d3991b5e5802bc52d501a2b0eb238546782ea3e6fe376a2c71b7308a03b48af53ecca039e00bdccf2512b31c96a9d0c79e4f549f32bf44526352f18db5b5f80c5371c5e6170d2971f970bf424c054058c97fc5f064cc3a06eec9ae656a3f1c1467e04ea4680544525f4bc515eb90b7bbda45389e545f4f18a0ec772465dfbbc6bfcc51a01d0e0597e74c36c5ff0005e42ad3ae9b2995385d10cd3753bba05692c8010659f10eb25025f108e06983dcb84e49b26f28c0d057da620adf628511882e142a129106016caa4f2d3d68e81010dec7db114ca085a75f91f1efc633dbe9ceca36e70a07e96bba3f625d6186ece692707dd42bddcd7b2541a2b1ac32287d95a86d430a07798a326c9cc8d5854a5f6081e2e52f9cd4531bc2ed19244b96ddefa69127da0d6661c253b9a30d95ccc9622f774c324bc18b26d47815fe0666bf8bfbb83899043196238f2df5dd42abe0476298f84b6ba0cf5982bfa255cf7f6d15df92a7a5086dc464bf2c0bfc35f064aaaead12af3242fbb99a2f45abc634a75759de25e404f6df74078dd2c23c166cb3b38d5249f88c48de544ee19ca2abeed53383388d04fab8031545d07e92de47a0b82ee714957ef13881021bcb36a83e3978a55cf50c201807b5dad13a07c69e3814d71458cf4b1eabd881b5de9a52bf21b61a37ff026c0246dc54a40bd309870486300e9c87aa6f70a5c94fb9fca9e4a6c139342602eaf940a50a1e9db9ec549aeade094aba23457d5a6cd2d733e6e52abff6672f0af206dc20708add56284964b0a25046e86f68fde58b2a446369bf6dba6255f809882f655b0c5f2774f5821816995934fcf5e6a967303eceee5f2f30b58814d6003a07fa598f8e7712be42cd59ccd5137b1bb47b1a8d4df04386fe514c456aad0a1cf4e7138a094b73f73e9b817babf80fca484b4f2aff85c46b6df14653cf390323a692562eb5afa478028b8bb19be2d75ccc15831c3be745f4ab5386db99ed045878b4cc5b739ea95f39c8223fb428ce77725d3de67e75846164b5e0e199030c230bf705bb098b74f430e9a328792468c8618feb20cc241e8ee1d40e8ab13b0bffcd83e135ca951aed021bda1ca109dfe720e9b3a23d353263500d1ba4551d0a949b272b3675e57b2af1d1fdacd5f3f11eaea07758161265d3a7be78794020076976daf471844ad08187e8dd34475e09d7d70a0dcc481b2a23be435ee8b4dc07eb09fc638bde9b6f58c38e1bdd760b128c37730d1bde9fe311e7976159dcdb0b7b549f81ec2bae7f71ea0268ab93a0da5e45f173db5ca2eeafae1f6439f48d017ee8f35c05b107fde4e65cbd59d5aa81a4a60393512dda2a81ecf7daa9926d0c5fb401f2b13156590258304ce62fd5cf4f5d81ff773223cc4a850da86673d7004c187b1f288109261c60462a1e6a6b01edb1b7491fb8cc45991790f5c14f8c0cd597591279ab00612f9393d6ad3d2266c2fb55485fad9f49925d0281b9167405ecab9f99fee7ae3ca2b97ea6f71edf8b01b42d73990fb74363d199b2f088a405f77078cade2794a9e7ae4b252325b2bc5058b08a444abb05afd9a7a51bbf5e0f2b0a2dbc26d78402d8287d99380f97f76a5e875332c45243ee8f11ffeb6a0a0090da5c1c979de1ce2c51d75ef60cb841feeedc806f6bd29ccee51ceaf857c104c62c6859aa2230bb54448c34bd0c064216e04f73915c2cb7f5cfdcfc183a2e02eec0aac6afcb5816838265663ab08d637f94f5107119fa8d944e8719e7c8020dbb2d0c2842e202ee39440816c217d064a1ab45864344575483fca3f18d9e6609fa5896af8aa49b7cbeef5fee5a41aa70c209d08c59c61c7301c3fd2a93b6730a970e1af1378370d5bcfe26134e98ca7965b52414639f83ee7c5bd493deeaaa04158ccf1e49aa314e0383d5a6e7dcc4bb976d42bc9c754d79fe334f0f5299530421fa5cedb1070bc1f837cd7803880f846349ce29f367c348b648e0ece22ff40f48471bebba700d9cd7039e7ae83715da52262bf24aa9c53208e4d16e2981200ada81815cc49cf0f6d2443aa402c0c6a4b867441c09138f3076078cd50afcf50193566149a31613a415e54ba9f0619d283917544c6d0fcbbc936456e7e88f2906a1e59b619730b9f8d45ec76efadf3099ce6da55e792b6229fbe6847f7127ed012f39dd585597c13a22a81133406c90f23371d0bfb3c39ff9fa3cdefbd7dee500d7b68bb025b89d132f1a1e854dc73bf1de2bf41f6232cd714dd36abe5d2757023959c970c0116630785f51bb82c253f7a217ec2038cf765074d6797d7abd63012566181c0c3b1fd34ab359908ef61ff71894f75994a7537b824392c29b2d0403a473dc6440ae375cad7dad09dc8563fa57b042382bafede170631e20224f1f0d586939c2623fc7d0f4b3eafc92865507333fe331860990a4bd9a29a1139f150487900124ff3e68db5959df764d34f028b29d7ed5706b364a0a11107f8d7ca90720b9ff202d70ff8ec9553bed6ac8a9c72cdcd9036babec7124288b237eb524044ad90c8b4e1c1614976ed993659fcff8f738f4049cbcabf3fe5e5cb851213f02d5172dedbbc0575d0ec76a0c3651a6850eaa423e2c3bf2d556f476c236d4eb03591d9df6fe4c2f462a24ab867708893e504cf89bdd1b4a29f6c1ce8d3ed0e80638ab46a8da84fb5456716119384a9c036bd3f208325debd0eafa2aceb817900f2a55e84a68ffa2694cc4ba948be0e830e8290680de889fc7f469c3b63eafca0b35e1976f6fab0ec560baca010d81a48ed0162ed47050ab86a7c628033c448f0b49d7f0a8cfef60e6e7c3605eb18679da2c791f37b57d1fb65ce54207abc1340fa21435c434222e1676a5b453007e05e086607fe6a9c63dee9772f1b2c6aa1609a95bdf5bf18f78a27901b637c8e22dd4516244fd58aad7f27f397b1de1d18c0c +generate_ring_signature 563d014ba2e3d73575bc4a754d2dc26a3d50b1b28a884182749920f050d1c284 6eed9562d29613a1cd99c436d0dbcecdf6238c13693ee3c946b87bf84b5105f0 175 a25b32530dae7e02d35dfa15753b13b714190810580d3b4cc13329e78b862508 9f50017212baa7f6d84fa964360bef7d9b51fcda247133d02f6dd892dd101083 21dcea9c5be2c4d2f94c225ecb9c0df207616924ba160e5b46e663ceba0e2284 27cb90941650d925d94dc43bbb337babe62a775f9ec20f3c20510dc3fa2a16a9 362c47bad0374a31ad9b90855ae07abc8f95e18958ba154d2eaa6b5920b6faf5 eadf17fa2186dbb7c4e53c9f6523f1d3edf6bb0a8317fa7757d4899d6c4ecee0 425599a381326c46c2020fb80f49675ffed921de1bf7c952dd18b4b606892051 42257e2b47c3e9660c785bea0a321f1f1093463f875952b58880965575dd0fbf 6c42c22f706f309ecdb7d51c5857ab3e8f3daf2ba953992054e150138c226d80 6627d7ebe1fd0453f3c6be707b1cc3059060117eaf1761b4f1d8071947a7633f ee9ba9cc6088866805e6c4a401edcbdce66a641794f2ad3e428e7dadd46db0fb f9ea638027ebfcde40cde13b6de08f3889563d9a6b8ddce45d8e59049a356419 51a8b43dc770817763112ca5c90591acc1e7cebde3a4de0e3b7a0e1b22e7dc75 299dd61c2db114ffc1834a5cca90765581d568abb80e57c8d32e741a02726697 6e6470b84a3aea87bb1abde1b49e596a597ad1b7872366beb842b2efe6b572f1 96db2d2f885b272727b0d83a482be29788f91df924011029848ebdae5215dee6 2ff61918fd8b31834a46bf1eb1c8fa1442010682213465eb6e878e25ab1ff869 f0867a7549321c7365906e449a2e18622763a853bbeaf27c46d29a03fab61614 02459f90d28c365dddb4cd50c827f92b0da385279be4b0fd80c8047af8385c1c 6da6d056f8f9e16e0cd0ddb319564fcaf86b6cb9f08b2201872358f4a7caf321 0a7e1919906b210be1c5ce09aa612d43b7bee071226b37b936dbdec69436f7ce 1f82f73ac3957a1c0722afe621d849138c7f6a87d9308f863d38527c97e1a73a 9304be7a48c0f67d9ed8536c628691b20bde7f63ac175a1dd1ce4e788e7e8218 ddf255be167de46960c16068be188abdd0d7bcce1123631dd3f0577cde6ab0db 992d57eae542d11c1a39179176bd56240a2f31172d18dbd111573b559a977901 c5757377b6bc1702367845878c17057afb6530b0939d73cc754790ca04d2a180 4c9b09fafe2d2bda29aea8afd2ad263ab5768e46b4cf92051273b6c0a9aa45d4 bc4c048d6472743994eaabcfb03602ff2e4f0f05712fa74b3f0636bc4f74e94d b6c6edef357fb4e2d56fee043c62f9ad46b4e81f17ac0e65d13a79299e9a8ff5 fc937db64973ae54d1c956b7c095209b31a3fb11e5214c10be8a18d559b1cb51 9e5932ff9afbfdf25cf8d6953bca4d1bb0bc43dce599e52ec09b04b0861072ab db3c0c80f8f9ddf45b43a469fd0757665cb750d381ff1acafc76557acfbe6f19 46a2d9252d03c03e5fbef1fc8c80ebc85a0271d65ea180a66519660f3bc8e741 ab732909fa9fc4e4c2886d185a82de24d05d6c2e0f13a11d1e36b4394dc1ddad 0c427da985bcd9b62a116f8daf6f8e0787de86434102df6417a30d8943b86516 a49852b2ee4d6d0839e9979f69567ac1c643719a68f096e4d1e6c0b35b7e9bdb 1596e45288ef60305c3fa61f04023728a6f74cb76ec68c004a1b58a5e68b1d33 383edcae5797cf6ffbb8e9c69448ce19832c9a58ede1bd8a038333efc1dfe5fd 439cb749edee1ec855f0900af70df1aa88310cb6455811bc363e72643f2819d9 4d10f514a74c7ef754aecc2ff83b3c675fb23388dbcac5b3d1646c7126f20550 b2b3b8dbaa7b5db6161765ca530697b57fc6c133d69cb284e60dee9d571a0edc b6d4e3d9beddc46dc89014388fc7a77f745640a9b62b0c8aa2c6531ed4b1e5b4 ee721795e8637d47622effb11a1bad9ace6d478e80e572ab4417d305d55f2aae 0c225e5654581e11ead0fa748bbdab246113c94dcd0c0f29aa4e91bd1bcccba1 cbdcbec093c88fb422ee6a169dc82efc386742df2ffec56f5e4ea1c9c328ef15 8650c902436d00fdb064ec7ec41c7fe26f3604ee1319f6ac563c998c45a51f88 e7890bd262ed27b50ade0fa38c0c40c35f2d88ae7c9d0fe155cf71548c0d2e34 5299662bb5cf5ffb77530d4b5fd731bb5dfa366b42b63b84b7c6bfe9858b1720 44092846f4d866d33c5dc8313db7e4ebaa56f6a68a7adb4246c63fdd246d44eb 76f5a55db792548b8806aa7c492fbe7a886b0013fdcdab296a903464effb8cd4 66dcae3186085c186fa4a65cb51b78faa19cb782a359da82c14067078550ca97 cc7575a0c3e1c4a67317c3779b69d7f0ba06b5058ba54d81d1c3bf4113dd1a66 741727e768690d7a69f40743e58e45b9569fb2f66455030938874ab9a863335f 10566d6c765e5d61d32271c3616294c4e0e34be64babb0d0b19bb54e0561a5de 202ac87ae0f2fb87436286ed7bf863b6945089c9427ce45b2c0b5d5e2a13434e 856f83a15b98a195b2550d1cf187ec317320f8d02034d530ddc4dc72628542a4 42477dd714c8d85001d34006ea70252100cb6e8f0a80b043e49768c7ff613542 23c846a2bedd1b4dd098a95134553e64ba0755e79cf6ed87fdd09d7104acda2d 8b88d1bbb7bc2cc62a82e6237a9f614615aef248894c067e34fe207898e4f73a a49733fd04a3e84b0f9992d85a2e3e8ec9f59c64b3cec1e1166bc4971584a273 5cc3689e8d71d87ecd364c41fb2236d605f34f169d11145caa35e24539fac46b b7ba0696e27858ac313e62724a5ed9a3e8e71ce1d2affb60bb41078a14404473 74f24a2bd8a11773cb87d90f213156d62b4ef97e5b824eff662e6547a5afe839 e8b2571cfb7c16a9795ad342845a94909389676ec170426931b412e6ecf8848a fe4a76c5ef8f886f65c9fafdab387a458743a5481c4e418d47e16954a88be1cd 326edf62dcda9b57a9bba64ce56a5777341e61fd32a88bbd412ae97af120fcdb 0fa25f1a523f9778defb5c5ca9a65a1028f00603c9aefcfa11b7347d15bcdb46 7bfcbdb7fc03c964651336f0705a4ca766779a6424d0d3cddf96221cdd5dbc4a 864c60c5fd3d8e884274b348407ad6daa0dad2f39f5cceb3cda12b6dcb4c85b6 edd89f1a2a4af586cf58a36712dae57559a4f2bcd303a81464c472f4ec269c34 2a8703922fd860f7333687cdb60c2ebf40b4eaf622f25c74322150f6e31197f0 fc39f9ac29441a290ae032686e943048212529fade0147536023d6aa27c21744 f44eb5a86d28a2250df530625ee6953020e78c1573ec6a06e233d25a101bc5d9 dbbfd8523c8ae7d93a63d528179cdf250af9973d04b135cabd46b00b5c2359ac e3aeaaf084469c8de753a206adf28ffc0e1756cb82c94a8e3cdc49d74667b2d9 96161a13c46713e0f947bbb89aba13a89958ea5891396e1dc70dff6b301e492c ede6e0f5f2ddb64ed20e13b59d40b711d9ffa19ec517dab4e7014ef6f4880c15 be1c0d1efede90575205b9381eeaad91f8bf18df7f1616409085532111bc8e61 d8068e5ce1d416850675f181954537df21d6fca5896f58a407326c79243a6ddd df4b1834a1cf4f8839e9e58484348c52e2826649da157154d1437a95ee2f2f8c e4ae43045e82841dad5bedcd90d311ffe7201a1e79e73c0f47428530b9319ca3 16c00f4923a62c779aea195a3a70e26234fdfc132513851fc02a25874db00a75 e9d9e3f1ce4ea8a0e6317c9ae2d7c2794cb1e384c0cfaf2d231e1ff2a0be137d 6280735c69f0d8253e79c7b2b4e168f2ecb3178a16d70a6494e05690e53709a9 65c14168018793a0ba4ccbbab3dddb801384ef3051240ebb802360532d0cc75d a5ffbec87074d8e6e16ab3a4dc01bed849646800d415ca73aec99b51850a98ff 1d6a3197d5dabd9250738f071bf4e40dd6fda8bac866cf6998cb10b8f5c9612b 12802109a9dfc7385d0d562865098f869f5d4f9719dcef355d614bc871799a6c 90d956da9e8db9fd329ac9ee1a69495215e1c8ffde38c86b6e9e1ad735c98565 65c561dd7dd09df78627e5fd8f1fa82c63f09805c8220fecffb7c8bf659306e9 e85ead64e768c732fe46d229aa8725728a26b38a7dc344cb85697d90f94bf87a 6cee4a19fd80f8ae378aa1dc911ecbbaf3627c98ae3bcf76fc4371c329e25480 3c15024edf3e8c9f03dbf441685c6bf0983ddd88290c4ca3287ce97d51a69fc9 c891c7483ddc144a8bc0b092f949184a319577c67555bdc64ee1215a479d5c69 9af31e31fa1e1dcdfc7c94ac78deac276e9e410b20bba176e670416e3a518cae f03004d802ade3db8e08288a78105da31b28841cf1354116c7e2f05ca71701ca 8f411100685a4e41725dfdb70d429bcc0e80cd7942f72fe28ad554798553bf1b d6d88c4061084c5dc2d7abf9e88702b671b05be44ca43db3ecbf3d1652fe2871 3f630f1da6c94d31683430da0c750c7480433bc707ce5f211885108d6372ea63 881a9f1038e00aaf35e4d222f948774f95ed1419a41051ea9ba254b2c256eec0 6524c7571ba335f379bfcd7b0e9c12b7fa380ff33d43ed94ba19bb8bba0f7373 a73a2eeeb16af820a684bbe35a8ca82a5d5416db55350d2f673addbe77b949fc b82a9faef32e159f540fec6de4ae96939b88b7cc49b96c2f31a5fdc9c707000d 8a41fde846c1cdd97d0e98bba13e882d2a4c3b585526cf542f0a4753bde20cbd 7b9c75eecdccbbfd43263c016055c282084fcb65b075c1bc62dcbb345a0c908d 16e7230228d87289cb54e78a5c44746fc09ca168fc6fe5392875b5e657452380 c03e5fe5b25f10a2813750421c82397f7d14244bcdc561668d30f464228836a2 15e2bc7c416562184ccdac03abb313ae4ef2a3daef2e9b6c5d6eafa53ce1d97e 3339211295a611a0456ebf4ec91d0b0dfa43307de4e9e8a5df75f1c8bacf8ea7 4f29a8eaaf0bd2a8629350cc14bbed7e6be24a886267ad6b06696cc0f6db7efd b70478e3467651e55750032224421268cd39c3b26a0eba60e6494d20ec20d5d0 121e0b3a56f5c0aea83f73ca6bea54a8c7b639fa3f4b83d26fbc4c163e71169e 48d8dbd1df1d6832373eaf9c37bbcc95916dbf1edb62d438296dadd9336a1faa 68d378214a7d3ed56c6181fdb42740851c56de915e245759ebd77647752e726e 93a7e92a2113feaf6436d7d9c28864ed1befacce3983fad58937dbad669f3b8b b58f1574b2832cebe2b51d07bbca0ab5c1595a7cf4a8c6776a8cd419faadcc8d 73274966355eac2e2fc06ce084d1dea667c1c75c7e5388754f5acacd7c44cd7b ddba36e3cd320790df8f046e4b97c39e58670107bdcf210d9f6563443eb88e43 045ada5560ae2562fae1ba2688c36f96ae00bc6335a39ecc2aa9287ec727b39d 210f1f2a67f10eea3801e68a6ee5589278733b930da8198efcf4e32f74cbd1a5 1dd8978bce801bb60023c5c0553d233d019d1df8e7603e32f5b12ba967aefdeb dc4aa791b06544d1f8365d0970d6018459dbcef0ef6f66f91493c6536c9f2356 f7c46bc36c440c7cbce87ae927edd03a3a8938867af3fea08003df31850da737 2902571f9976293f15e7cee98dd6bed6b240af3bb8b5fac36504cfa73fc3267e a1901179fa374a212757b38534a1615853f11b0a254b02b45218e57ced722746 cdacda13cf9bcf86dab9a48182676cb2467f71bd5cb1039795bb11d36aa6367d 42f0119c2f2da9ad6f031fca8273db3ada593d17ab788b6709e43c289dcd342e 1f16db1bdb59a1239b863f6dfa3ce2d5db1cebc7cc9657bdf0ad2bed9d7d984b 23c84577fe547e16883a3a58b41b05956be798d664f2079a94f61546063a3ccb c2b76798135f14340147ec03fd0f710c6ee48ad7a720863f86725e92a20ee450 ad9c4e3e6d05c93443e3f945f0218176b828c35fafe4dd87535ea7d971cbe3a5 8e76fb4899e1d8dc7e22d5c503b76c34993e462b56d7814cc1e26b493366d02c a9c3232923cbd91c9ddd6c071ecdab3a0195fb2ccf345fa377fa6396957bd1b9 fc7e3b1744478848bf32bd8e44e8866b30f27e1d0b67fdaa1c0c37ea8905de56 5a8df8afdfb44e87da2cc3f54a2265431cbf689907007a2da0cdafe662f1ed08 17f6ed513052e25139c86228257154b5a3f4c3969fb4176689994ac284fe64fe d0af4a164957e4a0edbbe5ca2eeac70841455d52ca23c7b6eddaa9db4b86561b 470273ff6741e29c1ce7bcedf7211b9ab869232f1bad8dd88e8962c437baba90 51fda21868e2ea133a30faba89a8af8e3eee639d8ed9dce01f5a642b53618771 5825d569f4585e8070dea3b09a031182b516cec00fc528505b1bc710944a29dd 4d065c00139072b9eaf3c6c769392999385b69e55d6499d98df381a65eb66ee4 8b708c79519686b1e4c24568630603bf2f713e84944f68c7c82eeb86e9cb371d ac73f5cabc08c95fdad5e44eac1c6c70a009b7015d8c9c477ae5edd63206d8f6 c4653adab64b8bd03ae0134922eebd5f6b934f21f9f494ee79c9fae1b65eea31 69bfe286a95032970c792825ce3a1bbde42531e8d88ba718a53667c002324ae8 8339ccf375fcc5de206676b91e7a826b7838e46bcdb0439a0e5ed291e023005d d950df6a441e4f4b50ebb966728bfdcf46234bba1bf87b8ce1e285cc0586de48 01814266c889cf27ea8adb8056aa3744490a6553e39f9d9c30e370077acb92e9 36e046821fa3b285c60b906722ea3236450ecbd5b7126aec326a107f12c48cbe 391fd20ccae7e77cd74886f8708d77e0d4266b13bced692a8aca3e7bff9f8f20 f87a8695a20980c8dd36c8b340f3432eaf0f84464407ef2f862d365288c9398d 44601fdcd5ab0842c3ead486ff88b9fe262a202643f18f0187fa39de8525f60b 11737be58d485734941b1f035bc57250e41dc19f37af601111c1406e6add4fbe 5d86c58eb7c44ce4e5c30bbb28d876ab743c8226abd40cd898f3c66298af00cd dbbca6172fc2e1a6f8f4ff2c15f4b708c5d15cc2d4eb551da1a0992552bddf1e 641f6b5a0dcbe3626a38d429320bfb56bdee6b2f6da3ad9791ca1bbf4b0f4228 c41ca151efd421423e216df060628aa6566921bd68180cad8c5079ad194a3511 deb70d1c5f95788bf90e654e72e3c03553ed9dec4e1e199388855dd65a120fc0 73aeb14e2e64ae19a1feae32b5ffd4e298001245d6d22f9dcd72d6777e77a18d f3c937570cdf0d2a083ff61be77e2ecbf91d7204bba9ac61545ea5c8599c7699 7ebec12519207dacec947de5dcd5663e265fc844dda3b14fe5f1eb51055fffc0 fb637866d05b2a45f3a33c8f8b0b034c9c99fa67d4995ee53a61debe51fc7501 8149f82686804e6405d537c0d25949df21e3e7308ac76aef645d145e2f7eaa02 1b8ee1dccce97495f49e00f5381651c30bd12f0b35558a5e9c18f952041ef679 0f0b962a78703436a2cf6ff94b2654acf66c06be4649a5f283cde1804c782666 2d00e214eaaf0783a4a9012280d76fa20a3a54c46b52d46fdd811f823bb3fcf0 20e78b334f50d65eb834b6513d24d566b406d25e7ed99b830ae5fe33ef71d4d4 ba409485b50037a385fb5ffa961951d04ec5d0c1bc1febbf9e882b3e0a760067 5707158fd0e75cf05482b4cc3a07971665ec469addf854f203e4c8cc6fdc281a 277b15c477e6667fbb2e0b0a59f08e3f592f629075ed9bce694be0ccded829d5 f2c893cdf39a521fcd308d5dd02e4f544e3a5d0a916a055f28a33ed0e6d48f29 d6196e84701ccbe1c033bfe336b834e41f2e86acd3951f38e9a77704a983890d 98cb2c89ce1b17fe3aac16c44c41fc479ca0161e6606f89c828bc433d9f88426 b0704044c50bd542e3721322a30c0e85fc5d41bd3289251e401f3b92301738c5 6d4fd0435c2efe41ef59fdaa12cc33c7545688c0fb9306c0118d0c1aeb117bec 4a2f0f254d4c4756e181f912ec8419497baf465b7b16de0ed024e7557e4d9409 34 e8f9e8b7e5e39ad5c3b2621c6b264b7b5b7bccafc28c102eb92e840c9def5f08e10a27d1a099c13e24892f2b886599eabd5786ff11f879bf36f019409c4af20dcf85593bc65ba368e1a97e28d2eefa2c0cc18da18f69eab981d163f7d56fe701a263e4ea95be8f362cc79d856ad915b3da62be1a9c4867dee558396884ff100c93eff27f93db80bf0c0128ec5e7794d657c2a49a5f62aedd1a07d4c8d34c3000bc90449ff1a0869de544a7453bd3ce8cecbe151e80bed86676292380b0b62a075a682ed4d1e5da6572e2824173639cce61ff1a1bc3bdb0275b910595a121640f12a1713a213da67f53b6ac973cb4ceaebc09da41fecfb171577ca500abb77e0bf07de2566f074f71a76036a6b9c11609280bc0f9fc45e66e28ffce19e9c1640abacb6dfe29730754f69b76aa0ed50549f561b19a42b931c648caed98887a010271ac4ca2a43261e7f1e1f9b19f0874bd7135d49342a8eebb638f04f3ada2ca0b9be1da9a008139cdfe45e93605cb03b1fd77a1149ace5a58526ef36589777308e2f72a05ce3759f812dfd83fba9a45588f0edcff9015fcfa7d1365e2d0629c0ba4819250ab77feea32d3553fc70445f00b5449fa3b457e984e7c19c60a5c3500d6b92c5a11930d13b6d84fc964b96fa20465a22dfa43ba41a9d2775b1d1ded0e53851d02d9eb319346336bbbe80d58c4bbf4282d9bd7b1e4d17d70e16335f8076e28603fd66d6cf8704e352fcaa5e339f82c5855e2ad4ac4cd29c3269930ad0db9ceee2d79cc1b6ed703a6426285bb6823be70d889d745827dc3bed4f5ddc80ce80340847633bc06db6cb4218189b0ac3edbd2bb1a8dbda2a7e74b71a3c5be05255aebac4fa7b075ea0d6f87563b8d9e5459bc5a9e5ece10aea346249cbd8e0666e3b41084af60ad61ed0c462e6ec3f051865e0bf2fef3c6c5796dd29e6ccf027dfe0d8c24f867d59a1a2614043154e2520dc6bb35f84bfc4b0536b775c5290d00e07fe6d367fc1bf6289cf98b75e5b3d14d6a12f4c291c65d62e69943fa270147a97954759fee1aab91abec68005fda859232870ab19f97e0091b995b3f100edef32955d0d677340616ca464d943e0996e0023308000a160254e3932db3c401b8eaa23491efb3330f4f5deab45aad76eff9a2eb98b04c3a3ce27fc9a90c52028c454cc0a3a2fcf558ce23b03d3dbb2afb5d6f94225b2920e99c5044fd8d6c0fe88edb0613e8cecf0c40bad256860c551af934b3fe612bc95f814cf17e4349077a34f9775b5b7a7735858bbebef5c1d6766a9dc3652f4aa92c7ffe5a923ecf0ee28024c4f408bd865d96a8e681bed01767d710bfd507af36f603a7d2699e1308b5264e81d8921781f7ced544dc05fe0c90d17b25a6f1fe7efce63389605b420f1b85f9c0347fb1eb98c6a9a883e44a6d055818aa16de1566f9517de4fc2fec03f6b7756e7b813fc87f3dcfb1c19abe028eb54cd5b1b23717a1818ccd82c86f0b0f0bcb9e5a3ad2c074e68f89fd92c2fea00c51c5e3e13fbb8e5d1a499ce51a01fe570e803a9a1da7344ede430fbb501dc63c0b23a926283779e059ba05817e0ae582f013bda193dd5fb57c8335a6f5a88d98377dd14964ca4e351787800a9606873e5b2ba66e57e21306e737e3a43727ecc292316ee6a4d94598589caddb43002a7ebe1556f3939de02a55f3860e7b1620822607c11c486c87405c4b751fa90d7089df5603bff3b513f3cf0d26c7bb29055b3a6c9c637b100173f37cdb9dc508c3685bc8ebb5357ee0fbe936e9c514f826944b8cdda55ad0c9eb56193f249604a9883feeb3c7759bdb92b648f8892cd5ef81e8b33ee542ab9610787cd082670bbffda37e4c1d78206ecc65e34d62f5ee02efe5e1f2bd7ae727916073892bfb0cc34b9ee91ba8977b4a117e115025dcd72e9562f1fb66b0d66a297102d1b5780b2735a1b5dc5a056f4efe0d9f7ac574f29bbb36c0931f3048460010796cd1d20d98aa61d87d5ff523134c0cc8738ae8a39bbf76e975e7e8f732e020c8ed6a6f0cdb2796ba7bee29ecac06cdc1448e9a05130ee5f64905724fafc67f167d68a7018568cdcc41ea23d40b5e47c51481dc0412765d67e8ee1f366314158f83ddf30ade63f8b965f1a4dfeab7a20a8c033951e0475383fb181cad85bbe318adcec806d82a7406bcf46ef1b8d378ed89c4441531a9db29b0cba6cf77abf5b0537c670af2edae68e2acde0315ade9e03fefd84b8dda69fa516a73ac3f7e5d2d366cba05fc0a5ab0ae8885b3a11680ad19fc6aef08535b31379e5006c4618f01d0686f0c68772f815274a5b28a192c5ec3f5854895780cdbc4a39368541451df2bde5509e08e5c89ef1108b52c709b40086b9618b89f47087ef71ff66e8126156badee0aa8df96cfdf6e3c22baf4be8739a297b479912dfc111ee0ac96f7472a8d0ab90c2a4506722e419f767af2b6dc21af9b880a594181a2212ce72c1115ff5e261103c018f17b4d9f71f147a7807895de86fa0db709e1a886bf709ab56f40d341a50c5ea66b5288df5938b98a852c58aa50f57fd00e5721f19438e8823b409c44560c4e44d2f2d6c918aba6e31e70f63020279be425ad9f9756336b4983ec8be200030185a7317a4a763a38aeba132f7118a7ea5a7ee7f1b47dd230d88b1afc11b5058bf9ec44cb661bb2efd16c88ae8dea66323d7e0357eb7db35afde44cfa82560db7154b3eeaebec8edf94db4ba0cf7bcb49c1ed551ad408bd76b9ea48c2dda901b17bed30edf0234f19bf489d81f6e8db6a8dfbcf8f7d2dc58babd8f7746a7607e51040deaf84a7764f9d886a2a008784d4115e5c3b82fa499bdd5eefd9696b08018fb608f5a962de97fedfd8f5a8228178ce43176f54fe30c635a3520a1b480b40ab115e0ce14a78fd126d401da8fc61a1d3d56ecee546778df63e6d8f2032018086f2e27a52ba8d4af390e5d229d84b6e5d70d40e3aa34629c8b768557e8105c251fea0ee7a1a7103c72cb7064c0277de737c9f56d8cc44488c6305ce2c9a024d033a4f0f4df8849fc346019ff739dae9da77d7fa6cbed6629776d58244bb0e26d134f71883e023eac521993719d8e6263bbc28bc3477b2e45f57f443860f0307f906b085258d0dfc2727d2012c4060e68dc528a608bc199ae9a060a3570c0674b6702a9b9d7867af37504376cc41dcfcbab70dd836208f2af27182018b8a074e9f0bb0a9d8d0ebf0fad11de4e283bbff52b9bc0ff006244dd857f079c5110fb80df2ad96a66fe907deb2493ebb690e12f1eed10bb63b8c31b3159c8910ec003e5b8eb8c68eb9eb79b2066ce28bcf12285c6d5d66c615769bfff99bd7298e03f88d75d86c03076c6fe927c6e965977c86acfe32ed4586d052d5c4cdbc2860049495b7b15a86c2717116b2b1a81de4decaabe5c7fb5f0273d023aee3c29fa20681932077d17b8661776a0e20ed6152a67612c77ef347e356e9ee6e60af37e90f2dbe217b74d58dd43c2a2e117d1f5cc3fcc7cb3cb73311055bde27cae1e17e0aefcebac8385232326c8dc8ce0c34fa8e2aad4646aba4503940190c47e575190514aa6002aea09e739351fcbedc0940e9c9ada306ceaf26791eca66531cfcc409bf0bcb53f9f48454503173730e1a76a70f01b38843e367dc99beba99cce80a00dd5cf8899201610c183c5f8c5f28e1a3f833988bfd21329a8e0ee3a1d453b40c8b17b3371c91466d6358bb452da8a661921941176e49d8daa293a67fc25df50f1a8fc712fbef5f00111b5a79b1f1cc8507c61a09b2f94ee38de4fdc7201d560fd12274a03d9f820ebbade217b66eba03ac1a01dae1600cdd406b8417cdd09c0448bee59878266716ab0cc09ea690377786286479fc4412c32601cfcb9ef73308e39d04189d723c46ae8c48347387d3a40283394089be5d29bfd7fc43846d0d06ba5ca58b3f7a051f106a5bb806ef0de363c09512c9f62043818aa4d158b74c074ea58126314e4fc8e5f53e3981bcdb2e5e1b95fa1e0d76873af19f77d58ca10a16233d02775fc775bb1ecb6538d90fa90011a156b60ec7cb2256a96faaa2a101949eb4a7543e6fc0d7b8577ce7dc009e10f817611c07ba4145dd7abcefa3280ec36850903f3ef1c4213321c4d05559df1c298f863ebaa68ed2e7107a44b83e0224bf71a205b41e82e16da7a9d808c8455c59c34ab45ab43afd1306be19870506df8ad0b23c62a481b5b750cfdf29b6df6f5b278c9e727abf8dc81c669706b705b77b2424fcb759bad4d730d423c76668a0ddc1ccc0f6e7ffdc218c7d10488408d4691c401e508b9116786bd6849f05ad5bcd29418d29861774f2f92a2c090d0ee2117abe4bc9785764049773fa67344406853e0914374fbaa538b2d5dc0e2600ef017225bd6c0398e9bd3e93d26c6e9dd01c7903c53155c07a8c069e14e3f10b89a1e23aa29a4d51421fe9009183782516bf3aa898937a10698ae076bbf7200281cf9b5974d14971f6197d38899078641dc846b996468546ba4eea21c5c23202cbe6be6b1bf39b7374dc210d21b4cb6fe43419ac175a9613bb17ac06c4fc8d0f9a8106b8f6246186cf0ab442f00a773d7ea7919f1318a1a09b674db74de12c07c5ef5fa53d1e57b24b4de90b074bc9de6bccc21182c59a689f395ca58bdd82014c7ab455518ebb04deaf5503fa4c099db04a926e0bb4871fac68cf4c46e6c60ab03923076463b77ce5c04d0f23b05447975b511a0bee07c20e9d8f5570cb36027b48f56c541863c0ffd6d92af6463b688b3ba05d018db8d257ca970e918a11056aa2d80e65616dfce7082db588909b33ef1c7a8baafe6a792f5925dda762fe04d2c635b6f3d242b7f85be4be8d694cea618305c6fdf40eb4a60bf36bd128a20ea963387912734d2c6bc8f3b849a5487d785327fd9ae42d331b77efbe78a1a90323e9bcef2465a427ba5f6057f99c042b3924cd21b18bbf797dd0dc72f5c22703bd9c6b793c37f52ea5340d87f783eafc072156bb1b958474f2e161bcd09d8b0603f371ac9a4c473691754cbee411826d1929508eccbdfd1f5ab0938786ee00072b12794ae1f09610e13ae244388107db12988a8d7b4b0c76d8653403c0af8f0843ac99c794fe42afaf463f7151126356256e74fe50e07d81806e6d3bcd66160c1b09fac1fa7432ed62c82bd0dc1d558ab52183ebdd19e004c8da11596a85130291c2d1388f21cfcb81384f8c3de43603c449b27ce788700ffdb0b80f956a6c0dae9d4417a6e4c5e12d2928cbda7c4e49f3a2b49a38d458d0c760b36d2e777a07eae695ab55ebd407709c23b7c4df7b207e8edcd828fe266ad14407925ae2c50e6fc9d57e59fae268dced5f887dccb71868a831ec5337e56686fd36d400d76a0d1a39b5f1f0c251f8201dc87ded81248c1f7f311ce992baf3080ceee1a90298011e45d66a5c123a2c75e8222abd69d85191ad26ea8022b28f8bce293eea5d0a02e7ede55ee7a104445429ade3c8a8dc1ffc38ba435777d4192f99f32a38c1e10539457e29a025f693e80b66200c3283342185adc000df699c07d6ac18b501b507e792359b3b4439ed360ba12779f90fea4548f2f748326203d7fa166498b0a90a86d73eb850e6279df24b2ea1030dbc42c5c53121478eeaaac9e603be6d3a2c06de4be622bd80db4cdc1d107afc6cea114d4fb2ace7b17fb79c642e700f6e1904c796c83866423156570c9d62c896ad331c984c2f435c1e41760d73f73a4abb06abc89fb6c36d92728211f4fc8dcaf7b27133b2379bcbb5b8b1f5d15df3c0cf05df7ca37f0a9f0443a0934c3cf3ac3273c574c00c710c2b8f042bf918f783300157e71737cec51e5cc5a6908fe4d1d79502b0d7d6e0c598603bd6c5800307bb0c1bae958619a8247d428e6ef028c395a45061583c4071e8f29f45f5d4a94e1c089e5695655789e32eb3d5d67206cc84399b26875b5d9afb78fe10c25724513d0b0ca29ec24e23eee1a49db1ad7f4111375ce4da6f9a38dfab0bc8d3572df0d40a5483ea1095e629a25f8685b500ee44e9170fbd7df92c1ec87457ec3e54279b08978b3b1c2c9cb2845cd67454b7c96e059ffe11307e22bdc1a50c22700a64e00b0063c3e37bf25dafe2acc40a327df0828cee3b0d18fc74bb98702e6d5dc7ec0f3f78e0520902cc2f64ac53bb214923ad5892578c3d4812cc115bd30310d62907bf60fcf32d3cebb9c5d49f332b4c120e4b5efe53884e600f1ab8369e226a260059bd58d14c525835e11a83a6b5afb8cef0335a7aa53034dd59e1c34f32065606939654b5d29b8f663a815e09048d7cbc728b11a90cea49fb29c80fa8e664bf0ce4df82c2b88ce9179d483d8a662ac4d24492b75f48a7592069f6926e0b053c0d86b78f4403bc5d2293661a6f1950ca8a90b2589c5087e93db72c79d1eed89b0ec1e33ab507653bef38b9d5cc26cee03210c2523b4ee58886deaecce98033b506d2d97538d8f6f33c0b1cf66e978f4f7f8ede24dabc43b4cd7b4f3c31cbcd770f8f79d11b392b2c9befc039e0fc2ea7a3d42283b07160d741e4cc0411f357fa0b6ed124a282f8829187f057f3be05abff550c0d7c84d1487f3e97174db5858509624c81024efa32bb2b7025c489bcd475ba429ac4a5e6245441bec08d3489780c19a17935880042aeaf5fe860f5b1e2beded4d5c96a59b3f932480b4d2254e8036d33532254aa78b4df817a0ecc86450fbd4c5b96c749e1cd3ccee52d75d689080ab4409b915be5b9962dc6f434d94b077d094d8ba750f9cfeabd9eab88b3d10ccb65294b0c906ef1eb6415b348bed7b291b6e7e194bb504a812af0eca26d340facd69bdbc17c5131c5dabf2c6974546a663c5f1a149a368ad7534e5954275304982dde0205b44a2feb3dfd9dc5a347f5c08c32801d53ca5dd4f687880c527e07e89749cdef37d5fbb2b47cb1f105b7de373d3003b9d236b82bb15fcb95030d0f31397ee4378d0644a90d3727f37be1ef44cb4546a74e5451a145bcd5cbb52b0f90025a25fa0b4d2a7cc68cc4e3dfb058eac044a5e67ad6e4d3a9704fa34a8503044c9e694e24c6529e268c1c81a901bd2df74c9468f4eb505e5e2c2bc837b60d3e8021d8910cbad9d4658da634a98957c98b871e45f9ce4aebd0933f0c7a9e00b94ab50631ca6608315d822e32a5232c2c9096c3f8d5093b480ef93f91a4ec0210c38e63e3e89d5f917581c81208662184298b3361cda328f478f889bf298403beb66763678c517c3d66080e33b58751990e3d4f4926c467c07ebaf569d7a90be310dbc146e7589c2305682a2bbc70bf5e43854281de6bbc2532a7df03fcee0014b3a95cff515016394638f4a41266a7b9087893c5d2eda94d002d7f146fb90347dfae04fd63d448062897b66c5a096fd1520a4224efe564b6c43633d9316f06abb4cbc3824c939be70c539d39d8a0d80a3a8a057946ecfb8acd194e963324066ca3716af7f23a131b1a10fb021542c734b66695e7454e1c680ddced280e83054b954ebfedf68d9082c5e735f2c1c368e9a446e70a2fe37a07d9d70a7db7360e787d3c0b9b304c87932dccedb67c88ed1335293ff600c1b5ab704c0fd3937a0980b39982163bc020c9204cccb766d18090effe31cf660c64f56ef00411c309061dbb75f2845d6d04aa31b42b5b44914b46ae9d2e0c4b968f4e240a09e6daea0983ec6f3c3847d3e5491aa0ebd2500a273eedda4061de617ba9011f000ff618003efb3af7dd3e8c24a0cf62e1bcc6f93a1c6804be8fc9062a5b2924ebfc6c930d286830053b8231e162f2cfc866c49184d663e23fda8c0343930252c1ea968702fb9cf263a3528c81f9a1bac0913d3bb0941cea9d2e4e180476be255e2d39360c6a051e2a1d05e7e59b72c8700dacdcbb589f60778b6149918a939b450e6523072d04ba1bb2e562eab8eae2b81ee7c9b06e00b3f109cb33b5e291f52f8c1f6b0dac68eabb71ff42ccd42fb73e0d2b534ca7261ed6742187ad6e52c5a6986e1805fc222a8f3c647253180bddb56b5728d92bbc899375940ad7b084b4097d0cf8071e3a10b2e3d169a1f6ca75fb5934e482c9594c630ea149cfd5b99ef98c42eb09bf56c488f2f1a6fc474226d543c2357ddfa2a53620cbe64a6475bea68fe6d20c8673de4104773a56e060950c153603dd7a5f17eab792e8edfa5068de5474df042ce843906e41b6731cd52ad6c0ab289746ecf8cb4250ee8f32746a9445d22c0d63cce0c57e5ff499d15731892529e47320c8c595a96386eae0251febec43d2044e7651db2786746140d8aa50602b03ef2eef3179db4746eb7cb617ec42e36607f600c6b58e475974de0f0408e3fc1cafa8582f6146cc19f3a3bba30ae42b8b075d5adbc03117ffc01c3fe92d7700f7d9b633331eed8841111fb98a8ea834b306c591da878a1f5fe45b559e5f16e571a736b2662bea27969ed8e93b5cb2116504fdc2acaf844d4f0765d9119b5cb5ae360065b3f41fcb07766f54faef0633470413e14b5a21a588fb495447ed65f39ea31a010180cd7e7f5a6285ff20beec410a3668e374e63c903b775b2fe68a3cc29ce1fd2f733024b08831c64a187e58d90397663f07b25338a0338c2a190f46fed77ef80595d03892a3bef57a1d4c59d4079708532be33f747c6ab9cc9eb8fecacfba783829ef04633be95cead665720409e3c003c108a0d6442ce78abe5cdfcf1bda948c3fb589b7014cdb53798372370951141845cd3a79f98532e654c0379c754ac497080b246cf361d6157b7ff4920a591cba28747affb0acb86cb7d8ef4204cec998ccebc399069b8d516592c382098dd2b376c5cc4e9305aadbc741933397b7525cc325e6af5bc4d57848a961c909fbef9f7cc00743135347bd8c3a5253bf16f2184293166913cf2f46bdc8982d0eff4d0564c6e6c21e8ea64293fc8747c4009924fe10a073c805a67dedb37e7109c731a037d3ad828eb95f7f95857dab2f48cde42ceb4b670badfb57833cc6a304234e760982963fa85744f76fed720ebfcbb35dde9a4f7eb9fe4388fe8a148b08a49becb888fbf72fed3c930616bc9d97d6186afd8f8c9e7ed36b58b1391c7a092408d3c55f174928576fe5668980ba7fe6daac071090ee1e2c60edaaa09266075a71b56bddf9dd6ad7ac02eb9d34f1151b27ac127c455c81e09c29a0b2c02409501c8bc40c971a5617294399c16b90c402dd4612c1ae43d106d4306dfdcbcd0515872bb1895dd03b2a4c5e0d57c8ed9916e80d3c79b2f12da0257a7df91b160f1af96ae243612c92f68873f19d71166d22ca2dbab0a5f24201e736326850c40cb1084c462840234ddd94fa16e838a00535116a7ee7201840f86844955f3ad90738ab21db16ab0dd562dac3d7073fce2e055d51385bc150f44a8dd2c7befc7d0164448f0396320c82b47aa27a08729524998b7f3c998c78f552e3d296a3244a032a55d613317aa5c8372bbed101289b5fb5b14dfd58bb74c0f0d9892df696cb0264cb8f55ef439b7af0e536b5903a39caf99a6cec4aff6b96c0dbf26f1a43080eaa73456a0c2ed64462e6dd5352c4f34fd2eb2c9116ca781031294c920afa010a9da0962fbe102e929134409c7dc615522ace308e99d0f29805d4ffd36b5e800d34e7d08f02c8466d51c5df1b3fd0c4a391aa5e71b7380c530cbbdca4fc668703397788c4f82a4d1703ffb5188708ea8d3643f178689553fa225e6d07f84ff10f647e12f6d2727429974b7888dcaa6c71919fae95d95cd763f85b2c543bd3e509d79aba261d8527ff6b3f78a9d75a8691ff3f651236362b32e4e4551161818d0a8c3bd248ab505c98a1ea9e20fa5cee5912003eac7f1c760a8c8e3d66688e5f0d64f71d6d2258eae6c80d6a8a3f298ee54aec7802fae27976fb324dd81a08840d82f267729290b44ee61340d17a14731f1608c5468a6dbd23d7adeb5ef57bf20ab7e8ef543c144c40d70b658dcabb6ff940e61a003fdbd7b8ff02cd6405dbbf0c99eb7f79b11f702bc2104f207053afbbce73830b58de2a220fa3c91a18e8ca0386836a2daab06a83bf996ed07141f7be49f27ab30a29db973c682387a76da704f0bf9a7c60b3839e69958f2a7c4b47dc13e3c098bfe8a86068f81e6efa629603f3d464b7c2e924946b577d54167e87e5987be464245cd00f9ebb938537b9080d84a9000e95d765264013845ef8af1410f0096bc86a1962db82d791e7a2000c04281324852ab0505356ccd01cce2b7104537aae503113f807d624b8a90c96860b1ef5e05b5460a0c857109cb8f10f7995047a5fc04178ff30b891341614569c0bc186075b41f90a4c6ec76d399355236e35bc8eb213041ece7f69433eb92f030ce5e05a6613fdd610c169a0e7f6ea236a46c673f87c1864a219ee964e75c1b90d7560d3cbf7ae08c6bb4615d572387f57cf9ca1d0e37cc6b1961637f533d99c09905b714543ae93a057605b3559e827cd4b643ad5ac10e966ce96cdc057068407876fef9acd567a43e614c6c7014ed57acc0c5b63e14fd35b7942c28d9df56402f7a1fae9ff70a72ecfcf88343bafce9af0ecf993f545e580f3895219b816150d3a4e7b0687723461f4a0b86b1e92521c1487ae43f11de2f2413fe1c79d4e1b089e45be81b899334469396b4e1dd8f4d992a2ecb4d8ec0ffb92620a108269940cc61b135c6afa449bfe75fbf28f20cdf9673082ea35d787d1f2ce39c3a34c7100214164aa36dbbcce1e4ee80a3ee40248895969321d87bd50f528d2e73e015f0aefd601f242b6139c3fefc0a4246e1d75f813c70ee453e8837d1f205a03fc9a0837277e13bebe2c0cd77a15f857059bdee35f3274fb2bc9ae6efd76d75ed84d0b12d8ed411db1db230d962d896e6ac08d1f70f437132f25ef07e8f116ed81dc08c6bf7653bae9a14aa891fea441bcce9ce169f12f4ac51b0d2c67d8db1264b70af5840aaf61d3765e60ca456f303d7f2631b8460ac1466524aef8fb6a0d97e803919ba94dcb5f9e8450a7952151aa2cb5377bfcd70a0086e244afb4fa9b8abf0dcde3490bbd65ccc555ac521f00be5b7c389b700e39f04c96bec76c0d655b260ab1cd5ad8aafe3b584013e012c711d553bbfebf83a8bc447259e8906d79bf670ddf67e79defc4341e7ec5da7213070208bbf75cf71b3b95d01d5159b862046f0fc157e74ee11ea64d77483cb24f67f46054df9d88381110a6c0c7783daf0630017c0f64f80af4840134ddc41bee48433f0eafe115f2da0fe4da58f22d82758e0fa43f28a2319ef6a65e20982bfb7208ffc9448262386ebe03b71b29142b39eb0c9e4f4cfdb2d824c73b9eb56f4bd5140bebfa2c0f7728b70c76c400e85d9e6308ea0ed7f7ae996ce6cdbc3ddc1ca922effc4cbd72140273bb0f3cc0e847a96509f13ad61fbfdb37e133d87b2e158640fddfac2912628718a247ce001edcd8bc02e75d366448cb5ab854f91cf228a531610334bc60fc079a7ba481f834b698f80b5b147ca191fb46efb13c1e62ae2da93b4eff4c9e7172b367f7558365281f94019cc366ea25933f1f5743e08bb7927ec1f84a2c3f54f183da406674f40dc357055ba015429a174189a6bc1e0a0045049e745c8fc96f10ad64651c7e0adffdb702e3d735f3431759f550594aa423a850316e690f8a338e1b4d211af40f0c11f70a629703d806aacf268edf5743816f7524aab685777ec3709835cd4875e62e610e75dccf22630231deeef029f449ab60fe912b40c3d9f0739336022b874baaa606af0410252907213e8a6ca4e445c26cca8c75f45e345f9140f098a8c6d15b6b098a412962d17c0f749ba406fecd2ab385183e76a4576f596b96fc6d0aa03b2b0720f63ee344668d30ddd0552b9343c210af3e24d736fae800e4fe41c0d27e1f0836c356a427dab83bfbc3823e77ed20f2b0a0cdecb7dbed59cd98171c59f8d008f9f389db43977255b4bd549d45555877b6a5cc0c8fa8974e5d83e9d807e7ab04390c0b3e0680a4403fc2950443bf969dbf09570a59bfc69119b0bd6b67852d0d05bc63b573160a371581aeaf39206222b218e58ef12a3d061b6e47214f40f6086b20269d985f2c45b541f893400445382b0f5b4ef97b6eacfbedba87b21ee60466d64dadacd2a9d726e0c46858b76ffdee33c43c2310d54fb9466f62ddae1a06b732e7022a1aa70b73e9971f2e5864063d286d22f4e5b211942f95e746c06202bcdaaaf35895f67483c7588193963cbedf3cd41bebef90b6de9dcdc10716c508be91b369dd43d7f722eadd4a7c83600ff60ffa30818d4c54cc51b62426a03d07c27d02cbc33f39c5c3c42ddf6a29143a1941dfad91807d8bff30803ddd281b0ef13b55bfcab6b2b04231207077b98d1380580100d6b51be896c81816ef5f9e0ce8412fcfd31ffe7a208755cbf595ec539e8c2743f220be7be026f11015a3ab05eb3e96e753a17176af427b1b60ff652233afb4baab4b70d0ef7f46705f882e0423acfa4141d4bfeba5e007598b9a51c091f4a076fc33de0747ac509646c7e90ec99a8df4160d01f797d8e6906e662a75d1603f7256a626468020701997cb6d04dff67956f5c93d1553af5f968ca5d08a73f13831fe8c52560b69b102c788680bce257d4e54ecdc0479f0137b18a5f3bf4b82218467902150a627ff46c6964a0487ecd11e6c5f687fd2c9a61252e5ded1e1be8730b2701faee8ec742c21bb610eef894feb22fcea8e8b28b70c8ecf43395a11c378b5913eab2c2e14dd68b4740df56d7107e128e1bed3ec0c0ecde53a45bc7f01db0b6a95137ac93432d743500315a7023b973f5dfbb06084d36e8d19dd444384ae2048ea07a4dd5072e1c4d904a4ff4e2a5a8accdfa1241966ba860125fd526876310eb136b5fec6b5b7925a0580c341b459f527058d03d136f597e4928057b16b431d0398ac7a6580db726d0c18045cf1275e42409ce8a6e5f0fea2c44bdd9780e11a55e003ad00e51204f40c45d986b13148b8902e07245b4d31870e2f0fbae75939ce69a062c1116475150fd4422561c5806ac96c7a21eb63ea0c816a94ef522ab6a9c00a14cb17439967048316d6c0a9ea316b889a836a7072dfc4c27a1887f3f3880c41e7a3e4cb3421094b71f4546cec634d1767a305467b70139341076f8a1064ec13b487a823ff2004fdf01f5008d31bf3ec400b11957ed46ba980fac40553b6f99ac0c919ba0e010f913e91db1490716bee13bffb91efa33e9fdca799cca265ea702b19cbe7891c0f3e2a2724cf962a195edac9564c8ecc5afbc7dbe6b9e7f1ded7f64d6283274f0f92fa23fcbd5a5c9d906778688cc70917574dbfbece91c3ae1c5422ffafce64053db69675b59569fff338b3ecda90f4da91a8891cfc45e27301bdac2c0da24304b188b1d24f6c41949c71fa7aeeee14dc472f29d4d69c57bf797fd1374e690a06555345f54f5f776580b0421baad18d7c247fa5c798c61d4ab08c5dde4165fe074bd1e502a38e7f5611e1592038a3677bb98f70a21a1128b788f02d799c75c409c2c48e116d5b443abc7f6f71268963ed7411af7b7cbcdc3ab709c07b5d8d3d0b31d6a6ebad909c8afa4092f033c0445d39a75d63199e4f5501cf019dd0053100b4d07e7e3c5b585a3793f4dafa0f1eb249187291629bbbe07b1120bb4c998c0e05672a0cfd6ae5ae5ab01327dddd050584355128ceaf3dbd8dc24250eb057f00ced13902eeb3923861a156b4c386c6bb9783b28d9544409969098035b5754c07ab0be326688b1ab498ea554c167cbc7c2a70b43d5ada6d9d6f82a77e9687c007691ed22cd2fa497d4d4d0aa9406cbfaad92c9ff9fa5d556ef3c8c9a6b86f9908a61cadb6d46ea11c13fc645d0895e836307bd3e0bfbc8605d8233609d10f65099f636e144fe7fad602726237f4483736295a2fcc7c2b1a391b0e43f9e8212d0aff611252392f60fc301883b2b79db01cb42e264a0a6aac5c55c4d2d86c9f200f558ed2ae46540eb898737ab0a9bd659a8935f59ea6e042af5af7645153feaf05cc4ddc4a4d87d56aee105a8e24baf2df9671d4558ffcf1108f50ca64734c9607e6b8b3f630e4f360ed255a8edd6ca3721c8dd5163ced4756f3879b02c465430cd2f4cef54cc8a87850f6369f663a872589fbda06f51f7b6e2c21ac5bc2647b08e117d25af1c81b3b0a5ce6e3926de6dbcfcf47e005f8ec51f4e70d17ffdefc05299e0170738f842ea1a2ecedd77bb906d34523775288f741dd7b550debd1150ae99009880bbcbe470dd540b2ae40bc229d1205132909b3ba92442a79f3e29f0a4167df7dbe522ed5b59fed69cd7a2b1283ad167023a08b4a0b43c8e2c4caea07b4ce8052dcfb3bd90629e56bd7dc1d7237bee1f929a45d465ed6060a76299d09a6d627703026447b75ed8ee9433eb9180b79dbfc5c8f2cd380889d551e73c0073992d58a166f775781692698e2f72fad4af930e4364ef521f8a6cfb62be18a07f06ea262ed54f5eb56ac08acaf5a1f56b7f30e6ac895e76f356654141dadc5009cbc5d1fdad54d8fdb42f999abd829a044dca9276cb617db9d40c32e36f6d40005982cdeaca835b5c0fa17dcaf071b5b9b16e861c87e722c6954b5cd07334709698327860298cdd2d24dcd6b14d4c8079247df13d85a7d8d3ba1f85e5bb61003bdf4b0c42173bfbb801b570af0b232c72ebb62ffdbac0459392630321cca4e0a72e7a850742aa0ce57293bd5f15b760733e689933d03c6b8c58ca5bde61872069e59aae1d0239aa733e32812b46d2ede15303b8105c0c7953af0fc968d8a850849c59070c18e261e6c519a1075e4ea54ae7f1de44f4c649134305918f526040e2c5b055f8f8658613ddbe6d2e772d60411939bee95c0880e0c14953e718e600d5f39d7d40e865891dc4fa8b96673db1e934b900721c81ee5613ccdeb8c73cd0282ebf8a3668f7f2682389118df891454988c6acb6eea620e57b1396d45a034093aa3daa979cf8b586d702b8903e48d206db35063e7f2eb5257f714fa26c71908e2925b96066ee9076a340d8e3d3bf236ff580a279d0a7a4b24bf12755e54c70b9ba85a969adcad23dacc76b52eb2049372eb64352bb2aba6bdad0f186a287a037b9438ad6472218ee5c87c083fb7f9591402b5c12b6cce6005b3639da11a4a06559bd97bca04a566a7ac37e9bb15a0e1513c54ca86889a34a253e67dd9c0f50e8b3fa9a19bb7b53e3510f39ab1195d38b4e3e8395d9db34b6220d23f8ebedf006cfed08e2f2d89f9e516ce9a37a5ab88598e5f210c4a0fcaf5291e1f0b6c0c07254e436607659332094de17d7c75ae10b0299e091f55000093e93f935886fc0df835c4292696e8abad848640923eaa650666b78236a49d444a781c169d766d0e51669d878da70b76881810f7d5d110bd174196981480824eeec772ac1c3e1d0882a827cfbdf5bfafa9d38436ba7ffa92fdee6fbc19516afa70099a7b3c062f03060ee322fcb5e816fd354aa890a010857104c73be7685d0a8264edf7cd76fd0b304e0ef7537acc33c0f6562e412a4baaa4d2a53e9816a2d44f5c0d80fa88f40cddb5e96f2e25e883be6b425e80448acb45b7eea48c8f55ffd7c7894e69201a0fc2953ab7786108e59cc8a7bc45c2d38b242f24dc812a5dbc66b68d6ec6447a046075b80527163d5280754260b5134eb766dceb50112efbdab83d5f13c23b80062adc2740b04492cdb81a5faa3993a943631900d4bc6470d71a8a0797f70fd70966669f5a71bf6fa3c7f38b76a3e2c5cadae94461c658ab4f6c8128fc78114b053497c3363ef9eb3739bc78934160d54eea90e7655e4ac2a75d0fb794e2d6ed09 +generate_ring_signature e9b4e53fbe3084c824f70c693b3a4ac2139359f9bff2b747ff60e9f0029dafcc 804904afe56087eae75c1a1dec52e28021755c38027f18f70307818fc6f06cb1 25 3002733b0b8065cd550bf1f096db411b48fea61aad486c691ec59720fc9feb79 ef040e117f0a6071a497fa42107fc2725dd21ea96aead91a800f363e643c1405 4e92792a3a581c8d3113dcec18f438000cdaa2da8ef571b243815d4f0516ba46 60e4c57275e38cd01692d52a1b08908a1d4b743273820e4dd42750a03ca29e14 096ff12778cadd973c0a00a4ae1dd78652f5567e4586df9bec05e5c3a207774c 34fb5b79751c1d2979428a2d21bfaeb7df4963afb79576ee482be4fedcd766a2 ab8a64748ae862f1196230357cd5d49023485b2da486f35ab0d81e80f586edc9 8a3aa0e39ab1a2f05e46b76cb49454d44aafcfd9ed59ce49a35f6895805a33dd 1b9e565da45e7623009f7a4bc6a5d878f4927bcd58247aca02f47d8fe3e1cbd1 5ad471daaa7e322d4193209908bb25b2fa613e40d0e3e6a510b4719cd7441c1d 712a2317a28cf99f50337a58ad79b2b8095752028d0e59d7c868994d00d587fa a3b958bef937539469676dee483eb589dee9e815aafc55bfd8e035979e45ab55 362003e2e2c6826122d7cb6fb3b594510f652250682a43aa6e9d67e60db84633 bb8c6576c6d22ea4780e92e31308445ed9e8061caf0b17ff6b0d80918dc78005 ec59cf609e51a115e92f5d6171fd625a9584a7199360e186d6d6a769d0bcca3c 1cb779863f4941da4fd1f2480c15f8f7671ac1f2ab70d7739a835a870bc02b2a af562510b1ff1c078833fd120f46a7bffca16a721896f41197cdbc13e901baed d03f6c65c7db1d4559d9d2f9d081dd8211bcfc91fdc38749f48cadc5f490584f 08dd6a425920c6b2d0b5341614a9f61586e655e0238b177d956246856a042a87 2b43f20d8d88bd7db5cadddf08bea0b2e8befa371413ef6ad18ac0d7ab56a4c2 7c26cdca264b654a8e18f98696b228cc698045c08ab24b52468a9a07cf67d0a3 2f5e1f1c67948eab5221c8dc5abff840389146e60a86db450290a9b6d92ad12a cfb47f22b60f59a11dc6b169a32f16cbc0954b2cbbf9e9d72319ad61fc4d0eaf 33289fa023991d56a92c76878f7dceb50aff02379acf810dc012c7229ce34e82 352cf1e4ed3d0db2d43eb401eb5a376a7dce6a30d75d2cd7f7a9c35ec284eaf5 f42d02f0ea573f3935b0cc5a1439d8fa36a3c9c2d42a1fbd92a01c73a373fe0b 21 0d56bd7cf49e4ef0cdea176761dad5eede354e493d5619bfe039735cf5d7b00f88c4a74cc44bd54760ff7ed463d212dc8982433cd29e3de3cc27c2502ca07c0a37be873713fa8c4af0fcf4522400f9c9a563c27c9d5c281345a757674c910b0c0e45e50bfd9892ad60090507dec28b60094536c70059d21c1165b69586d558053dfa071ba62e5217dfc016b8bf1b012b5efa7f7042f341d3ba65e897783f630eec83084967a0bf24f7f9510b0a2e052a880641ae7f425ad6584b43199181db0931b1b0d63066796a3542ca15440e87a9e4b08128af9e3ddb59f0a304ca2c800f8b7da72f0414dadb213e46d301ee73187a73da2352e976288209e71435484c0f1f0fb0a2e14f2e45597ed3202114ba2f8d24d4a57c14ebc68881fe43e19aab0eb5d6015818573a783e7eeecd388086b370915be847305dd6fc732f7dd7717b0823e5220f98b8aa424631d0cc208256861850ba9e3ee3ea03a1d075bbc6ccbf0d570c6a62f145a97747c46ae575c3a4a281beecdeec1fe05eb5dcb23148551809070dfbc10878afb2c8c6ca33292b67ebf8fa0af6c2e6b559373fe0ad26522f0657299145d492b3369a76b8f9633fad5284449d3f30b1b0c7588b2c5bfc139403aef3d35d33d5644ce645edd01545f07d11fb49ccb62c408fb8740e3659d4c90acdbae1c6b9d6a14878e241d9a8df447a16917db0b9b0a2fae08d487e9feab10b962d2e8db5bb524c298ecb7e085fa1b4e570600799565c68d33a11ebad0d0701eff07d0d4d036d8405a81c4baee1b1755987cf372dbf2731f798e749765f870926b9eee96ba35fcc0c2f443290469a48df004526f99c847dba49a1f1c673610ef5743a089704a1bd4bd8abfd8f8a5e539180adabccc3e8ee1e149f5ddda1d9001e9891038eb8a6325cf6df79877aa5d08935fa588f1618583579811628aaf30a3dd9b0d0e1dfc4b26c0fa890bb727d7631bae8103eca8c046515c6e41833960307cff5ee5f9b328d455a97b8ccf1c9f2c43f35369fa637982c9063930ec98504f3fbfafea3ee3092345437b64d747f8c96031a1ac2b35ffc7b10dd96162c570d617abea710b3178366c4eb06675fa7cb9d2d82f678b4c8d884942cf4ab09c50e6a672354e32c0f8dd76433299515927696dceb536e9ce1b5e8ea57c8038e280a2cf75626e3ac222a8f974c00ffa85e090d4ba8f077dd8151513332fea3b564082f0bae5f428ece1ebcc1c915b58e6a666b8ec090c683ea1181aca8a3e0088708173205a8faf4f2c12dbb6774345abc2c32387c44dfc6465190de6885920bb00e986b2042893c3d09440f944e0e60c33e2ad3d5ecfcbf52cf61b45e81bfe78f0b8ce980bfc044110f8c881b6f394a85de71588113a9c042d58901a90e1741b90ceaf46cfce352dc8c32b42b4494ca9b57024080ef556a91f8d996e50115046604ed6925c734c2a485beb1d538be64ce11158114b40b7efcf6e0d5af8d47fe0609eef6386f45e4ddf0a614fc30c0527bf05db82b3bb91db3e9798e92e456cba200489e94dc992f0eb270f945b884fca8c7d1d0c026a4b7da0484db231a1bb11a08b1b7b8630f9ac77b8e2f0d417969f1d0eb7061317bd7b5e970774d3fddce3000a356632977dedd25969d8531704c396c6d2397220abce9c075e62e73044dff0bb568a3663e097a54c50e458ba1fed1241dc26e107758007d18a320c961a4710f8ec342251269a6187deda0d23d4e2c26799e030228cfda437fe4d0edcf6c000ce96580de7d02a7bf779ac2ca8fb2ee9745f85f1d13200b31dde6acacdd2e1a0c50550275ff40d97f4f7a953ce335891467588a8c3c0ef2cb98fc5f07d3a11a0eea798e4892c1028b2d2b2ba9da22f59f1b3af0c6cb6e5cc281fdc146af2e440bf1313f3a1c7d6ce79d7d24b280517a41580747d0c4ddf469f4b271ff678131011d7cb0c5fa57cad348eedddd276869fd0affb9cc17ecd657512a43ab4bea2808383877e6a66bcd2242d986034e900017e067812c61347700f52089b4b4dac5022fe8f48dab038158497fc90e3d5561c9087164c7777698b025a1e55c88973f07b674f1a19d049df21cb4820e68d35363a168c3c8154cd6a124a8a6d9c28a300829c135b63ef4bec920e61945591767d81e2ff3e4b2cb7228ba70f8c4f3edc2095901103699ded0090d7ec52cce87bb79bafeeb7cee3dbea6fb27ebe6a7bbd504e3abfc5acd943cf68c1235dae8d03eff0d3ae2be13370411735e0fd619046e03 +generate_ring_signature eb9545b03f8a41557402ee5b83adee8b66557216164e8ed3110af09068e7df7b 3ad1fb51f9917d6359282770ca51428368a2364d1825d4f3df3b4775a6d11262 227 b135e6b1a5d2ec0d4bd54e83c3ba81fa4ec6b7dc3527651d2a0d22cc7042bfe1 70d4916a9a25b6a25eb436ef2e9a572c71c14dd207ef0c31fbcbd0afed67eb2b 20aadddc6fcf29254915a2464481608b75b9f8f56fd23f10c91b63a825eafc3f f878c2a6696c3320ddabdcf50ef9a2122aeae5c15bcd7d8c728bc2bb6971d5c8 990f047cae53411bb0e4377527a158159d8ecf8e22dd0fbc4deab9677119ca5f af2292781f195fd67267420695d765b088283b6c5b25329612db53d64d862a53 0c44521c5ccb9da1dd7246220453fae2b7940f65742f407b8825cc9ca898fa24 1a2a0fc45731dd123afabf36958bd4dfc17ae514d2e0c528e8917a1f31256cf3 e6bfbe487dcb7e5821609f51b785db9ed69496d225a3cddcd7af74aee2e01f30 0bcddfdc4729e16d10e5223c445955fb248f0dd7469878b32988de5cc07b0d06 68314e20506bdb0f801ee4f44f633b7f52f61f947e8870c29c9e4a94f51996e4 169f0f8520be828037ce28e0e5baeb1d4b5c3b07c532ec5cfdbced0123477919 5f8e556b88d31c4e5f286146068fd46f013837514f51265bd8804ba81bea8b3d 319be150916f6ea258be7dd6a0076c27fc0a83b18fd4f8a34653bd94d97cde62 e6e1215111015682073c3028a8b440d932fe29041e5481d7b01eccb6b95dae53 300dac621b203eea58d16180505bb7007fc522ede0036fafdc0c70c1aa13629d 404cda0229965cb66ed06d4172bfbcd08aef682c24fc1971a1dbc2c8ec5e2d6e 2956b0b56b44862fd1713b874ec52507483f247cfbbbf3092ef304e265b2cca9 090e335a4a359c4d159453d78a1f38efae03f979e1068792eab19f5c11fb6ace 2cae962cec49cb62626b6f68d43811ff7622779d45e8d597653328f7bb20ce2f 134ea6b2bf582d6d2200aded0feddc0003361714de85120a907bea448b71d59e 69824b9072b1fdc172f0f49a4f034db0d7d92c3233facb32ad2fea1f129c44fe 4bcccad30e8b02bf9f809fd252c36bb72ff0861f37ac9875def0407fbdc5ceee 9a4ea135dd5ca3f4a48f7c0cf5f0dd04e630b75d131011a65f911057467b3771 4cf0ffc0acd60ea96823f1c1f7deb4efca887d9b4ebcafbab1518f2de49ac511 1125a23f9723cc84899d1d25c334632b375a40adb5a18d9cb7b05ced686ad70d 24bb0fbd6b225b547bbdf2d0eb004e10abb61a0fc6c19f56aed53638536c5d2d 23b717bcb0649d2f470d7d5f6163730d5d991fd94657d63981a5d1d0c6bd41aa 28e4c1af3a9413893f7ec53ef718c3aa290760b03ea5b532a2a97f609adda521 58d85597db2a55133001f3918867ab43c641a3a62da2c9788d9253a58afe4703 1434c6ab99c890abb1d1821a992ad2ea6c536d12bdd206f80bf473b0b7a1242d 2c975f9e511692e1c7a459970329b1898c728d1ba6e9d15a0923f186a561e5c0 13b781ab987660253aa0f440842ef1fba7aaec87e0ab1d8694a1348702835557 8a4666ac8dd0115967e701fde497603bbca23d4445f4a415454f00d1d21d50db 8e38f23176f4df63b5fd7e835da5a9ad1f1b7ba3964a6446896b4c616f1c2ed5 9ac9459da38e30c9ccf0d5069da2d11010a646ae321a4c6c2ccdd756241b7e98 1d1f7a8ef5c47dfcfbb5ca0b484d6d6c89b3584011898271e2632e4653212ba7 7339c8535b3ae64dd0425584b97c9bc69df36e5952d83d2bc072e8bd46d946b9 00df66803a5d8e02242100e1e5f78dc74998fe0c4d9501758084169ed9c4b730 037e4c91a4215ca39248124add9eb1d942ca1a422d2f3a66b8ed70cd1afb1483 a59149d1875441b997d2e2c484b684e283801e292f367adbfdfc148228cf29cb 3684da94b917c35f22b3668a75f5330aa3fd4d5254a3803923664d14bf1b1cf4 9c8f9c7ff25e0f520fde6569c6dd496924eaab0c49063e62c3f58e9592ba4b08 725905bbf76c533eb746e5b55b92aa90dabf9b2aefa63920ac1b02361bf6ddba 7df04e39c892c37d5d012586c0770f3ade4a447e480225ff3f72c73653de3037 0ae2ea812eabdfb10d27503f4ec1eaa45f7acdf039aedb3b053b350cef6d538f 5cb5f76b48f0eff0b6b471a763b05dc209bc447e73c265c98745986fbaf3bd3b e43bb5fd40d4dcf72fd2fc1f2643dbd1a53fc8f43c30c4adcc07866541712de2 5c94b5a172d3a434651559ee066f57ea0b0525ee7dd31b5c40fac5b56b9691c8 eeac34f08772c6f40dc37156918827b8d830bf04efe204254a19e8dc393a319b 62f750b430728306ff5137d31fcb1fc2c4cc767d68fd19515c9ccc7f216df1ad 2db61216049226400bba1c660b6bc5864b2c5653a010c6c959c7ed5fee9dd236 5352162146e5d86add4fef9bfd844ea37fe6bb1690fb3a86ebf18d2e7ca97f39 0d688da489ecd3547e7cc3885a011749594139ce5798b0c030e3739bea0f5da3 3bc1da739609d22d0fd283d7ef5062c8787f1709fec67b30c8c929cf06ea432e 4b580b7a59fc022919dfdb833fc8e6a1f030249b143228cd41f926a4462baa0a 2722dad63b367e94326e133dafd14d1b741308bbe518123ac79979c789f4917c 99fedb47b6c06d8810f372004daa5abac0eb054419e9f881798f6899a51a2a7c 5b1eff78bc198da78335193fb76cdd236e711cfd8e5424de106a1dd978a7ffb4 2d16cefe7b8622504432ecc41f608854dacbed344bd45fd1ce5318fa9c148fc1 4d9d87a35381696d31f2c32f1383022678ea79ab3017d9f5e63675434507144f 737c726f470ab07b34a0d377718733a87a39e0c64650bfcfb5b1628c79167162 62abbb1c594ebf54cf86f41e953d860bf21acabb5de1589296ff64b9bbe24bc3 873f5758c805e429bbbeb3483a808f68df039df94b0b6b41133a624f5737dae1 0c456455f9c2538ee6845336723a8fcd8270d6976454d01893f3516693242b58 b79e6d6ab7f03c4a050163b82ad0825aff29d22adb0959e402094b50f4572ea8 b3216ef652e1a28974d0da6e8442112d621794b28ddcce6f49381ff542ea4425 550c70f6f6fac76b1fca01eb9f1cbebdd376cd34258f90771463f713763e5bbf 0493c98db5eed052f556ebbb9e44b83fc0ab153cb5df07c55eebbcfc1aa45e57 9749872dbaa95503b443b828cd5159a2b1a904fc8ffd96ff1ab1c305558b40b5 2b38388a48277079a8d707796e0ab5029f8de62dbe11554214cd5bdde99bdfec afb531c6c87e7a222b1098182a87fea5a8a6cf84add0cba0ee42d0dad5eb4603 7cd7aa12712580665c03e484784c381d5119280985e37701e7da4a6c38d033b8 0e9c63c6a7dde7cfd52d8923ef8dcb58cae9caf6294c3cf29c8233a835e13faf ad1d4203d76143a6c356b298879c3ad65ad481d271c32db31401688778edef01 3b36bf0c569fd263af60b18d887682da73436df593bf8aa111f342554c13a55c 2dfa2e3275fc4c4652feeafa45e4905d647e87f5d864e13187ff36790c6bcc84 f46723d1ed8145569a1d791946bf028ac146c07f7fa6a39a333dd53b5c0f4e6e 446b5accc1a7b98a8177f6ccf0945cd7bc1429cc3431948a35c6f7fea0f722c1 c5800b6d9f7d1b3fb7a73a4b395d89f4ce4f6f1d6f967e21858c664ef8cd9454 59db55bcb3324403f4c71853ca34d67a6567c0e638b5025a359d953d5fb78a65 a1e66cac3abbabff818195743facbfd84700271ef073e82cd420509ab25c95a5 986ef2242f122048591f0ffa2e921b0c742e9eecd32307b4755a0777192fae2e f50c86bf42243e11e877b207bb0653230363c7af99ab7a34dbbd7efdb1ec4680 456ff5dd319a4bb3cb7e5d0fbd7cf9a32343a71cf78d9240b456619d2d02e804 bb642e8f709fcb02a2170a70565a6d4a8b741765455c2133c1d0fb23f2b5b78c 43b89c59368d2bd28132a1e130a151dc6cbbd5e7e6a94ac2eee7a960086c9bde 819d9b407e6ca993e66fd487a5eeca4e05dc7c0f7758111fa9fe33622f1f0be9 ec98c8bcf6a42acf7d9c5713eab87392b9a4e6c62c9fee2fb0cf6b36cb46d23d 0a919352b11e755c3fd012692d3a35152e60f2ba90b5cae5ce09037687b3968e 917843ed33c9c2965f19510c9446d9d12876838495448b026281dcc5df4df7d3 00d7d7686f07deeb1921d36a9f3660bcd15f8c44cb744c07b4426ad82795a88e 0ee706e4e8749f20afa08bdda88661f3d893ff6c6921582ba08e17fbe72b7963 f0966f0849efc365bbf50a2f2481adfac83db02ccc7fdede7dcf7e1c7231463d e06695e6096fbaff9d4e1b312e6f46b6ba5be6408c3f387bf032067036780f8c a01e431dcc8cd29df56d09f28b0970c525cf66df28d022b8f1f903e582a19a06 cbd16ee05c043a47b1e31ea1ca775fdf160a5c2f2415603de5d6ddb7173da119 75f0b7d25a5769f02d362676271f2359d99175b31bd29e3aecdfc3ac78df18b9 3fe7d86bf0f4110cd744da22d207991b5f184d63e441711cc4b861caee253065 68c8753d4af2679036bd268c35d39550ee0afaaf9c02e5e5588ee9e8a3716160 457e6f97f1de46e350a3f03f33bff666d8634857a44e31ff57ffc92558a9d35f c5c53940eb876f716902a31d31817f22df5a9f7450ee5420286468af79eb450c 626dba1cd9ed1a1c8617dd22942f1e7cc3889ce7de5ec9c16d43799d328632b1 acb9a136a935ec4402b5b7b99debf19736c0b0fe6f388a799733b284659a5729 da390d778adf35d9cd8486ea6fe2b68503fb4c56bfcfefbb3113de8e3f578bcc 3803e744d832f2a77afbfc64774f72ae3bf55d4650827f8e7f601b1e5c51bc53 a4afffd0693b0ba7f520309744d8ee61c6c95f2e82f48bfb602953ec863d928c e1146dda462414a37bf27be6b623ad6eb3e1e9cc83b1fe7e50b93882203a7441 891138d8fb40409a6d5b7a6c1ed287d478dde6bd9ca126d2193c9e0cd00342af 6b75eb99faa7c61f4aea64cc940c6a48c97acf4b0cdb8a35b5eba01d191dede1 104b707596ead97edcb21ef0fc9e60550822416bccaa4155f11a6340a66e3550 1fb72d3de45d64715e43d715a1ee73b04344fd3ee4d970748066f8fabf66156e 54f95e4d6494d99fcd955a2ecdf377d4bfd205d415a35440282edfee1fe16e85 a9bbddebc8001f9491594bf96fe7e1e7606997a4aebe9ed51b978a0d4f015955 a07a3eee51d6b581af8c647c1bb05f7dc13e7602d78a4dd2910ba3632e05f29e 38e309537c9abf21f1a90940cc37d413e2d4b17cb6eba55052ffa86efeb8b58b 64e06ab8669e61a80a7383b7324ffe4f507e4be7cbef1e8d6a5b6753e4f88ce7 2d0b27161da027576a840e7ba8891213eea759b7ff7875b2479c0df1ffb968d4 c162fdf95366152345fd51d7a41cf67d491ff80ddf14f2f522a420a1e1cea009 4cba57f77d5cac6fd8a19dfa7e1a892a132983294917099f10cc6802bd66b7dd ffa84757d572ea0b215889fab35e7ac919dc947bdf89809c478846c7b0c192bf ebaec43561bb539b4c52f5e8de85f81d177c15a21a63b6957ca1f7c177545d7f c5d40ba48c29216affb27af97141bc899fc6ef9863e475d489d9bb0bc9077914 3e39091fe9481e72219594f573b9586bd28ac174943fc57423aeedfeb575c364 595ea58b07f24d939c433cff7daf5eb3a4e7d57ff3a79f1c0debaf2e71cdd72e 0f67dbf5edec7b206566bf5551d506cf981741ddc3ba4303f88b3f898dcc1758 28e098914123353f8a8de24e54ebdfbd5acdbf6a47fc104dbc2d56861369db6f 0f902d57de0de5a54b5df1a5d81f3f6ef3ad396b242a259b753afd28c60c576e 97cd1e62e3f5f4607dba67eec96134405a09a5489fe9a6e870ab2edab9a9d756 d77afa876d416275433b63c676a1ecc3bd26e3ecb918a98fd65030f6023e1231 6f7694fff9ff60c62648a70cf752ca65a37ab4c09680db8cedf0f4b32d6f909a 98b50e3b740c7d866e8c5c99b81d5349f8d7e9526f84cb6f6eeee2484377295e f5f4476919c84bf17e526774c84888750df6ed30202924fb5bbbfadba5643932 b342f1669e1ce3d06cc136f70ffc54f626dbcb0fc4e8d7ba0a7855fc10a1d502 c2871b547b20ac88598441c2f65291eacbaadb6c54ef460ae3a4c263333e0d45 47ee87232015aa72886eed3a9f01031d1645f36aeeaf373d8ffbf237ce7f80b6 a6052c5459a741ab8c7df89f0ce16313f20cabeab7a9056c2ad923409ba8e812 3a24a8a15b7d11924c3d5c407bdc7a8c70b4f9b2c3eb49b2fc602119b52ca054 a227ee65e105d508072a273d1bb7c22866e4a3277c92f0810daf0521f167e971 3f06dfe715484d866316864da512307acc929aa9afde3d0ebd70aa68578de0fc f2bec8e2d910abbe8b07a64dc5724009f8aa26ebc935f9d8ccfec8d34c14eedb d871478772f66b3acc7230a2ac92ad9b84954e3ff8ab7ffc54b8d6c4bbb6b6a0 c6fe839610736bc6b4748db900289f7054396f1bc830389f47e7f3c2278eb0fa a1ec0a787adf6416699310e63be5e0d3f54ce46c17366280b433c2f6a8963d79 dac9c58c64afee53e709aac1694c16615946c413080f8cd14d3c70eace317343 3208f8c01f38deb4eced71e5386465596a3cf9fc0cbef92db020ee4f664bdae7 30e40a476f4f574a5fa51a46037b8f34ec2a22f88ed53dede1262b928222f8a8 2d4aad0c9cd1d1fdb6019010241c2b3a7f3c0ba43e5c957c57e381fd4ab1a320 08427014b35a218a8d42633c2c904f637a58d7d399929cff2200691c28f28c95 75594a53c60160d036d14351026e40d8b74484e8940e2cd36560dab4cfb03867 84ce0f8f6701a94578f18a52373b6dd4bd3478aa5a43651ae4720c66bffc8f65 3c5a3a0f3f3bcc95834de91e4785e7633e7aa42d165a1020def55590c059bf95 91b7bd70cfdcf1499a5f53660295c4c1be514ede2a34f2f42a4a03974c299f57 7a3b48117bd45efa7aeca05ebeaf57cabc366aef6156c33ed189f21852940868 9ecd7a10f1308d29637b6f964ab042dcb441cc0733d6707aae74e6f3be46b0dd d952c8e5ffd76425aedd14ce597188b8f8ec9c2f991cff6cfa72d48520e02b9a e19f10eda5cb9bd6c0009be6e63d070121c4e9a98409339968a1996edb8b9cf1 f6676ecca63206a96726480ea6907c00a26f432ce45e909f48d8bccc5ba47425 b3f433715b6dad85834a78477de0a3ec947286f1bb47e00af607113886f57a94 f0284cae8f5f7e2f1cee2f65aca6739f435e5e1c17557f3227049250468d5d04 b1b13def871ecf23163ad53b82718ec3106f807edc85fee4e2e7da347782d66c 7a60c325a14099bc5733d3ec4136f5abd8ee3dbf845679100eea962ef0349dd9 0dd27c65e3743c6b1fbfc3a88fd6fcef519a6e93f3758d57dd668cd017bd90d8 4ded808209b6f1a51e10c48182d87ce4f55246657a306afa82c2ef0d8ac27732 c9ece550162ef0e262a25d8c560380fe03466f5c20f57f009066ac009adedbd2 391bbbe71b68162c41cba0c39dfc546a252fe340dd2a6519e64d225d2367d0cf 6da4b54c7101f0ff5b42a5464237bd02c17a6706beec5c38baec0e1d6cbf62ee 8e24002d40273206ab5ed04ca415087761a55c4d63116fb86800acf60d3e34c6 77515a2f7cdf957df6a8e6f8210fc5af94d34f8abffda70489b2ec5c3d884d36 66e0e208d1749d946b97f7a144556ca7050ba783bf2eecc9f13402113d313455 1f04e5d55e039fdd3e61599a86630202f6f20a4ae406cf1a786ecd5effff6890 9c8d92e1ac1f8f6cec0d86395daae3bb623d3b2e4e7e3e6edd6ca5caff9acc9b 7f14fd093fd7d2a91306106ad80c21d3009b2be563f4c483e1e2722aa589c5eb 8f65a369ddb8d53907af63f9f331b392464f839f7d857ebb7e28e137cb0944c0 7ddcdb83693718c52d2f1be790ddec54ee35510f8100e19a561256fda8d71716 1d863c9145d5a741df02f9c6f36d6ac62a1fb617003544e54e996b7e9f7dfe9e 90731a17179c2c8441cc41aa12522ae552c5e50cc94d8a9a3280524d2e1b6da8 bd79743685562c8f4c1e7ac3d057451ae0da60abcbd104037953976a97ba3c58 eee2fac40db754473fb6686900215c222124f8de5a6b50da7af3d86505f9f962 06af4b74603cebddfe9eebbfa9eaf1cda90c195be3a1c306969337dbcc93808e b0078da42b8f408ccc97a1e4b600d0a9a3817d07abf8ca3681a372261c496128 c8d3e785e9cba4f0e61b0578dfb8b20eed4ba9d7404f6683244578c7e7898a40 1b4d5feecf24667776c5fe4b86c08cd093f56e1d0ff819e0b27680466c165205 c6d0e4e1dc13beca98fd4aff8a36e73ee76414c3d7012071cdfff11ff40fe788 0d6fb5559c8eefea26e1c820c493b9f44f78728e16db734d67ca5546c082ef20 3539c398d5d3b7976fd4b738f45f4d3be8397b504579bdffc0beaca1a18cc0d7 7aacf47db8eb09f2848b6fb8038f25901f1ac0480d09ebf8fb2d085431dff30c 158989e9c5e4868710f10347353706bb4adc4dd61cee69ff4b06fddaf12f5321 b34da8cc8d713c34825b412b3d7a8afa970a5ae1ee68b4313bb8231377904b4f 00fe049544681188a069a7e412caa7a7f764e122d7cdf7b7c1330fccfbe59131 2f1f11f143f4d4fa47955db49edde0483617926ec6b4b36d006d4af8c95aa816 1e5d8d3b41bda8feca23e67ddb47ffab4b2282973c6995722aaf31e33cc55b10 843eba5cfb6f3c2a3a69118bd2d05b0c106cd43887d035e3c92ba1691f19f782 3d252a9bb68a8f861c549ef4b8677c4a46a04d7e5a835abf95ca4471e28a6bff db98df79792635f71d65a11578968b24cbc387370120ae86a7fdd249f6323199 bca4e022254e2ad688540e353ff5b316390b42dce1e7ac4a89836dfc8ead117b 31b53319585e2c855c8ea5ec98e89783632d2d8867f6f29cb9440d5a0f803dfb 972057efb30f7c0f990959e8877594eae81b06600d1fcf0bacb37a2db1fe4527 7d8d4b0eb20c680c2c6350f6036a08458531ec5627f0378476deac13d4c6c10f f9d6dfce29a744e39ae4e8770ef3a6f375f7b8adf4f80443ac622b3aee049758 86a77b81f0f61aff8c8b7eb190a92b685679cd0cca9adfdc784f92119f11b9f0 9466d20af48d4ebae40eb4d3ea9399aec9886cd90e8a50b73b5bba58b20f9f73 52e33a4d99872fb9152f37fd3730ebb6069db1891b8fa43e330af1fc45bf67dc 3dd812af9bad2d52268ac493bc111624a2af3d8a053bd1653e06e4d87c612475 81dc421fdcbcb71e85353890bfe927f1b9d3ee8c2165e095b8f82d84ad1460ba bd871eadf768e07b0ce4cc983da0ad63e5351a430547338c5ef6cc75bab73ac2 6ec0ebf6e7b674b64e45b510be7ee1f833ec7f2c36ac8ac8e6d6364981cac0f2 41995dc88a6ea356f9f236f923295ca18040a669de3697d06c32c255c582fab0 648f1e3446e00e1c9a3a6494c6dd3fd456f7b49d9b2d01f166f01d2f27ad284d d36bfae3e1aa4f01e3c9c65c8e6534e595936b84ead4f441ade8d6e6e2fc0e0d 10d45d6655e8c3abb2c24666bb53b22ec5b44f5bbc6cf0e0a27dada220912816 5fee53f465852ba816101cc32ef605f18d3699936806425ebc05ea14712ae693 5aa53dd87f84d2af3a7b7a44a86945d22f208be51344d74a6fd90f3135f3a8ff bc8ffd7ff3c8f47ea1dfcd7521f042b768a22cce4b7d225852adba263581f119 2431861c0bdc397681c029898d73ef18a3dd27838800a7133ff3e952acf0d52d b983b2d865454c7d2c6d16fda7a2b5141afd17991884a38f644b9ad7cb1209bf 01df6c253ac63d7886ae797c2f391f714b3616f6cff4cc997d4bf912b709084f 59ffb743f5ad86ec06d5d172a6f4f20dda09e674f4691af83ada8b97d9c5b87c 6631b88ea92ff831e392eac86a8a382203804bb3a6c7bcad7eeaec438ec9bace 4adbf4d2d8dfd90560cd7f35c5e729bef90f6e3cf5775dff8d8845b92ee1b117 ca7b20357029c4bac5c66a544df1f076fbce074d94534f331db1f365caab192c 3771b42d750bf5ab0276c75e2d1ebc730fed7dd1f5c254a2e5f5c336efc8248d 56199daabe38292b12ba9c05824c9faff3078a8b0d539ea35bfc9a302d552cab e9fc2fb37e013402e322838621d0a7f8b322a6a98d0f1cb74485b404561c365e 98e281ad9f554474c7e171df9add820cffa34f8ba349afa323074aac10a14f48 3e8bf8b41609d0ffa1d0a2a0d15c371ee6b5a49bf524aeaefceae5e0e88e2014 7dc9912fe5828144987ed46a19a44b4ab12c31468695a0346b2014365ae280e5 d5b21571efa022d3c1fcc23b6553bf2b0c1b0668891464ff0361e045d5a57d00 156 b2069b60be4da51b89e6350187de6d72761769c2831d2f6f7b145e0dc9abf405e31e5efa6c7515dbe6413cc2a5b28a848208f51b9bcf1985bb6e43dbee52a20456343572e7e12e709c70c587f71b1b3328090e626c4363b47815bf347ae95502b1b8555c7865d87939839dee7b41043c37b9f5aab92d011fe2a2cb80c84b560024538bc62dbea125f7a820e7c7167ced3f1a7e98ff1a55ed8ae946b4a3f0d200e74f21d8a47b575c7b6128750edf853c444eb192760d99b63060b11d976d160c72c443c20e6c88b2d4e812ace55bec7641d12a6bb63414c349547bc900d0ba0f8b9687f9af3dfbc6c956990f55b931b043977871da080e0ba164b42192fcdb0b5e970f2216686927e835b295d16a0636847b4df644b3531483c18b14ee1fd003c744a72e1b69f8214c1e6febddadb5d611bdbe764ff96c43ef2b1d4b731f640f118dd99c5ce3f72bb62237498f769e0ba4dd4eeeedb20f83d94b28d22f6e390e45631913308957cec12a0a0138564df776b22dc484e5fff20955e29fd2c43207e978b3a0f5aa83ea66f3cb32cf11a55ba43392310916152da66890d3a0f7a0032b038dfa13c29c09f19653bd49cacb07f7bdba54105e3763ec852dc0044d980e9294a163c19b9d61e2dbe7f56544e98b54f9b3c883809a70114676de1180140c8fee2fc4a9d41024b465e42e846bdb65dce6e552796b8c8007689c1bd8d7760060148c11556a82ce548ebe1bf14b14cae0941fc4521851086c28ddfc028fa701c452dd3eaeb488fef2007d050fff01eb65fc35c150302cca0bb75ca0b46ee101d174ce5ab5cfe0d7fee6b930e4906a2a318dbed0302581d9247cd7ef2a2ada08bc869013d74ab09b8ce0054a28ec7881af2e76ef8219a1154f854ce4564bae057c4d669367c47be266c1c4db887c44560627d951da448b699ce8e7bed7552307f3e2bd33edaac957319d27742b277f40cdeb71b7dfd36d48b462b851acd3400601ea3a65ad898933762c074bb90a5c58e32f762258f82acc9108ac47a29c5408e0fb9091328e3ccf0a5473f23abe1a888322a6ce903631db2a31b6bb71880a02e8a0eb5676569d220efbf43e03f56d11e5e898a8efce7ac3a2389ee2acff4a019ff753f3762b84ffbdd5f4c52275f13c4b6e47cbd5fdc8a5f34c75dd7e63f20de9d348de218a60b44892195c0eb7db4f79a3740edbc712f9a548a4532e18bd07eca1387ad7a27c2234c4a1f403e9f419cb126bf6aefb1e4b27c22f9ed19bd208031a0494b20cc3080aa7831c5a88e1954015911d86c845f2c376d76354cdd0039bcbfed762e2c94901d2ad58180ad864480503e77b070a922c169469abbf050bec24f6ee0e1eaf7e59604ed06dd99409dd90361bf09fb810bae92d88922e0e02d41652bdf69122c685b09f507c65fb16b2d0a47d9a95fb61a4a9f017db70510f9cb0243cf1a77e4064e6ece8a7aaf4d0a2f6b771d4561be4ae3a15330dfe6f098450544f2dfaf9435b9da5f5b67da2884165287429f9c0f990511ffbf86c050eb5579d52ec9f0887558a0ad0ede1829c039d220420606bef22f7231befac7e0ff03b3a92ac4bd0518e4125f15bc0d1e90b67245743218a0c11572aaf7a54b60efa20fc180ee3434ddc16254a8ab18fa9088696f2eba873e95cc7a22289299e04e1f634cfe30cc628d4e8e6ad859c8423c820840334bf3453c8a90d58939e280fa8511bb9257c34f7e03c749352ed0c2688da08160b919a4957bc9e1b9d1476061671997e5cb5db3f882c796f5122758f486f54face99cf15fb07418012a3120f61f9f8c266fba8e7189eda9406fb77ca4bfa6da85a95675ad6010f2ab3f7c8062792648a723e7b07749c33dc5209d2b24ad20465d427005337f1d4091e4a9308e869d9ec6eeefdc28bb21e1a7d4feb7ded5e35e51c0a160b5dee8bd7137824003b5b7ca82b2662874c7b788576adafffc79af0fb3db74ee56f3dacc85cf3f30eab2faaf1582f770197488bab88864b1296f8aadedb919db91b53269b6d0b3e0fd46f00600a081234bc08b09b15a1d7a6719feef5c2d070546674679811f120038a17418e3a964e45b0e28c503628734d6b54e0b2f1e36ea056936dc95d0e3306debbc92a043bda6c5cd93509aab9678be2cebaae3cc098c9a2f378e4ec85d8065917bacc357d87b2673d21a15a1831bfd2a0c834fd2b093b0cfc128f76ddff0f48c86e3032e53cef7ea90a9a23a522b23a9ba03a26df8e61fde1a69666d43f04f633e20fd09202dc2946f66227ebcb8e6c898d5cc6f36e8b092d936017c517067352506c0c6f82d59854d6be9c73f2bdd225f5456db8c4ae8de96ab95b38ff05ecf8688a88df3f5a8ac8df44e02c16cf4f25d85c7c5aaba0c33d057de0cb0106f9b108e08a145148dbe4c55e4dd20eea720c54e29473042d2a529812ee2d5602ba370a542f4bea7e79f480f3e110689422eb1f356ad3968e6047ba28b065510f0723c704a32bdc98cab7f831603f20d45b21e84c7b8073cfb43868b4d29c8609de5fe9ce2ae7a5b2af3fa3bcda9332e8f84df38c856c759b2dfb9f44ebac05018b9d5d8672c3baf2bf488473533e0e3265397150b872f16da4155d85bbb00901570bf1a24ea653a94c4878865f273f2dc8b5484cf49969e22b2d319613183c002b1075f8dfeccc7779adbab59f5cc2e7ebf7e032a0949d9c4bd034317188430cbe5974d327d81d06b37270d5547442dcbccc913424d87e8f01c1f423888ac60d6d13d00c68c5dd56a45e601631694b0f920b62fbce689ff5ac327e553036ba0b916afdc94fbec5b8bd4b2dc6f45f406eeb30d32ba02fd10d1400c6f4c8a2970684c230a5039cd0e8674d31c4768b2353b1190cf2b7ad98a7af4a5c218b7d080986f3d13b3bc6cbb8dc23d08024005aa47ba2b733041d105ed4005dc35f0e150c6a3e0a3e2dd6c67dc90e02001bcbe4b26ad29a5ab40179c738b9a368ce10cb0c665d0169625ee7b8cc46006d5e0d5cd7a6be68e83f10ee6ef60ec60baa587705fb91b3c7e9b95eefb719752e00ed16d4d2d03a81323b567d0c0ea4429e68e10019e0bb8305ec8b7e854c1a70974068e20078bbf3f531fd0b58c5da64e0b28d0f86fb2747f5148febbcff9e4f2a9ac93f1c87966d24f045cc079a0bc0410c4605cd4f03c388a9c684d90b99990de7e93ca885251ea0922e31fb51313c81e2680117ab6ed36ea919b09601b28379ac333a16cb814779000310c3ac2b536f055f0989426e2e76484f7f8cf781276cae6b4c1b35c9febb111fa9175831e35ec7fa0c438417c497602b4e608a3885fa4fba0ce1a8ec6c81376641a84e12d7c581a10e62eacbd2d4721ec10dde995b7ce070bbb69f9997e7ca9a0fe0f59efc6c0db004f57e5da7c5487f7c5a7821a513cc93ed82eafbdc8bb668ec91616b10b283200640da3a3ec57bfa24f02a79a9576946cba417404a2408993b782463d3d032cc01a10cb908354989247dd38c19b1d0c293922b43f283298922074e91e7cbad8a0b4c5e21f2d447ab4917ca2f89dfd04d33d9569d624b232355942c337069ada10af7ab4a1467d14fe0d2db812df5a1cc048d68fb7c1ef17cd5c6e42ae8ecfe4e06fa25a4fcd289fc21497198f431b0aa9ad794fad15e0ffcd065a112226a048909dd89d803ac20d334b41c548d965781edc9c38fd6a65d68517018f84580175d035624febe101e2c34c54fda1e9754a6b87838b17389032ba9285c9388d0af530500e2e7198c908a88b3ac4b310bd226a43d52c4f2b6f13e1fe4b70cc67a884e000a5f9efa30e25644dd4fc012040c5c898ee5dfc63d820fefcb9d3a34b854a10b7a36b5376efe6481ccf867c44885b99f6ec335742f8bce1c0dd98de9c2e45c070c12ace2497cc35b0454a3da21afd9c39e28c34a4ff749e23b6bdf646663ca0efd7898a89656c611f2f499529a0d6c387e9385a1700523ede2c96dcc2f5ceb014fb2da36f821cf3d71f029bdd45588914d52af087a4c27b2e17c18d91e409e0493e32bb78c8e694bf96c416bfb4ee49d39178e8b46f8bc26b206df884dbf0d06426bdad6d958b40a2c5878af339b157942dbb4975ce1b26f37296d0727b7660687da1dacec8fb48a19d455e0ee429621b93f4b909a14e4de0b6d61b0faaa96048433a3e4aef400d3f3edb516db797f3a9dd4b1bc461814553709c33bf607d309fe04c5894a0b507d62891cccad61d2710795ddeb9b38b898e74675c86f0309039173b5e28d6e0cd822e3f101798c3192a7e5a520b92ecf4841e764885ad39d0e64bb5bba7262046dbe0389d98aaf607285659a29517971b251ede5cd78cddf0ed16b58def3f16ec97646249801a8b9c5cb4bbd152a275fa2d7b36e57a6996a0b476e0c2edf0cea6d71af435fce68dd3cdd1ee54fd444d8edaca934d8385ba00c657478d39a6afacf9ab874b9cf47dcbf93a9746b6fb2660381d598f99ad4460101c6ab5b8499763bab936078dfe28aa4a11f46f6e4d64a9f5a6491c20db3ab0bd927f632992338e84a9841c1af2e44d1dac79d0a075cd678633bdd2a68200b00dbd88aabb0fef8ac287f99364ecb57d3d66b2daee02346f0f7450d45bbd5a10485c007bd9aee9238db5e35299cd02d6ca2348552f0eb6c7858239df105cb580e58689a12ea7c3cc78cadd417a88b084c26ed25dd4022a68f615030cabbd00f0e9e9da0d046bd221a1717e75ff6ee418b18a6551fb9f138fee63422e8ba7d2f01bd0b69adc8b7d792d3f6002fe9643a4434b5786daf91eac59bc18599dc19410517a9c337b0ebc4a5640b53fc130644d27380934a62d7a0d62f17c5b7bfb0bf003876e463af980223938df24bd52be82758b1608d8b96abc03023236c0428b005ef05de1c37342fa9d82ba6638aa5ff0cb7d494de5855ffacd126ee705abafd0a2d97cc5a854a1af31b2cbc06e6aa384ce929b34cb25637d02961b233da32e10870461fe0c0e946bb2a99c56570dcd460cef90805d8f7e6544be6b929283d1c0eb9564e6c9d1d953212e7d4101a84ee82809cb3f24cd293e9eb17d9e605c3fa0420de064113c9cda6bdfd199a0b54b9d0f00d79f90c13e64f144dd5d31b48550a5ecbd03cc4fb43c5de092323b558903de8db2c91241c430838b02c4f1e6d120f7d4ff05e6ecae87e2b00042b175b411dc05ee55e56a0546e58f9eb915f77f60781d03ab3ec2b581594cbd7d88c057a11c452e79bc79775788aa73f946ba26003de80bf2448ebec3ebca500dd82630e386ad04ca70f705995efcb18b11b99f902e25884e41d7501829e0e9254da5595fa4b975ea4cea5359f9a70c458234eba09cbf76f8143fb31846242a73953337229ab122a1bbba9317bb321044a380cae0c1cff2f5a0b8a1513d4d45d2b352a648eff41c652a732b6323408260e4417d103c65ce6fad0244dc10b46e59764ffca1fba1804d433cfcfb6c5bba7da600cab0d2fb777440afc3be6fff9f48e9e01b2adbe75fcbf3aecf894a85a40cf0eadd808525622c60ee53c2d03b7f947439a3354b612faeb86e9a3c7fb3da5f5391d670b2ffb49ae20397ac25370c1f38b275f70e0cfcb84df4a20808b7160d84869450790549f28cd754f739be88e9dbe00585597c9bad5a5f5a916dbdb992a58777c00b07ff1592c54e6074b8f5a944fe48cf2c1fc133b6f97dfd91ffbd3c24c37a90eae9739590fcba3880ddbd9303a0ae0dde6c2737fb2fa0d6952cb7f2e36e3c70d2ef32c91d59e5956650ce34095b5ec5d6697b469f5a6a632887322c57c143b0b0a3a39cb0cf91590a9052938a09637f4c9eba6f2f28a9223a3603a3d3f711e069a596eb39fc3f233185d54a211f519990ed254c07f5b10744e4c84c160117d0510c88c76250cfc83bd6b215fa7c3661aac474e1cb0d5048188d4485b874fc305f90623f80fe372f2e53c4d9a8ece5277f993117769ba5f361cd504c7298acc0659eb1d840a155074a4fefacb8c4ef1a7d44b83a1e3a54645cd3528d0ec5c5d03c8e09d7fc70db1dd29798d53100a218e3e32288ede08b094f9ec05173bb6890f63b71536f28fb711a6287187ff7ae93a196cb7dcf6032554bb948b6e6d91cc0e9edb29cb1775bb10c99d6db293c9d9f04df59eb0f10d31b445973b3dac394d0190f9a866301041a18980ab0578dc8a7effb1286e42684c2338eef2bc924ffc0339dd95a5f8b75ebeb0da4be6e993f534c6841e027ea84fb228554b11c063470cbd973c92f2df2115aa3cc681ffe1de5d9ce1afc511d281003375bdaded35e30c4f57d71bdca770e48453abf472c41f45ca4f2998d8583dcac75e7896b7282a069fe79c9e1e6028de3e378ab3ddb26c51d058c9bb68a9825a6d005842aca2ff00ea555d3762365541a215cae9ca5c131bbedd2e98a7d18685e3d4e5c627483b0818e61f7a373810a9831978204b97f2eeec5482bdcfb3007bec4eb4a58e727b0ec83caa9771e8f64f4a645eec22a9b5aee62ac5da753c095a899a1622707c7c0cba5979ed28e53f9164061c7815f6a14fd712ddc3da929ad714960f4a3863df020ea22cf6f707c7fe11df75f75486e9f24538dc016898f4a36849aed7e4d4490b3ed07e045bb6b22407e4878d93bc498c173356e791c2f02621ac7791b7462b030d9ae8e2f3404c4ff08f88c2975dc3f769119468032e03ecbe69cbfafc9c150b272172536a6eb404f87f9cfcc63b5d8311f74522b7f10ec5d40299b20909360281b081deeedac5c816b63352f2a5e0056e9849d0b7e3d99cc857262971aeea0fb64ea8be0fc004a658d69ad12ebba206f20035e49c1e7a9e8d6c2dcafbb2960bdcba8574872a24dcdd05dd7358c5dbf32ee1367a36080306622b314aa27edb0b049a704519db313ebd11e8d890709cc89d19c77d5bf594e17eded6e89e89bb04b5b324d008e36449d25b9659d8892275e053bd47fb01e1bcf46aab7404777b047fbfae64b97eecee9d81014e15e65d4b91ea8c3fb46372d77e2804966cdf600e52dc91613b979ceeee05941c0c35bf23eb70da79d3460cd9f009b177124bc505a75ffc5a69ac1aa624aa414c4cca137dc1603281aa890ebeca18e824ff9b2a0f72894fd8f5574e2a4c464eb5cc3747512ddb309cc13ce3d5f4a40fccfe3ce20fc6fb2a6bfe18e939fb055b85dcc086297f48671fba27f108cd1426bdbc5e2d06ae28b0aca2c430d9ba3c4ef7ceca58ebb31b97293326fde67e49aedf351f90077e0a3c39db84f3f2358acc89d4db2e98ac890a557a32286479e6509d10723f0fd58cb029dee79195f49dda650197be60f55bd2da9f7fd39fc43c116ba3328c06d6b3ddb02f0f372881eb0c8007ca8d6c5ce48091d41e2836299368479a2ece028eee1598acf198b6adb921aef6f6e3d5ab106b2639414131051cb9896ce0fb0f5a5eafdce9ad70d104e1493ee55b990c0f6d5420a1b8ffe0643d20f76d6f64098f94e5221de667850be66cd6b66556074562b3009e2a4ff3233e7a52d1e6e1025e93723e5f7dff6a3bb87107a948cbd249896262de7287db1c48702ce15e6b09a48efb1939216548edfb6f5fbe5f0b1a0f5068ef0d0f16826fa5894512ab870b65d028d8cd2464770ad76b0cb4b7039bd87173deaf8b28fa99c33507dad56b0db8f8de89343910556681a697517166748c206ee68cc4a9ae5c283a4c92408c01436359e359d336ffa0f609828a0e6259eaac0106e576ff0fed29f47225c7330f9a3da1573bca268d12b8cf45ea61396d97cea8e0e675216b9c6e1361647b380a90162df0f0daa053097e2c6110a658b7bd9a4b38aaab017d71fd9439d81ae90ce5e4a84841a7e65a845cd16df9897a092a26a27f5b167dbe8f1b3093cc5bcd0b1c25ef1e0084f8d1faf87786a88695786f898d66de1eb0b254f0eba82730780163ab3a69a66052656ed7b41f5cfff6e1849d1ebbd68ea706efd58425e5364e0455a5053236e9a93a4471689d3de613585288f8b9bcca3ccb9b47550b7f809d0f2b1e0de3676e7db0d25f425b037b8f4206b4bd8af92538e8a21e18699c924d0565f785d25e5bb79e54cf051527ea7f6e9bcedde091bd81f45f099b6d886a5f0ead94a9a83c7dffe90b901c13f4581352e999d6b12664a9db8ea0e1aa9e5efa00c1d67e7ffae3ecf8eb67dc743ba27dd6fb93dc45a197c50753a2a5d83357ae0e015f3689859f4f43e480eb235d46654a69451895f8413e0df35452190fd5de0af70723abdcac0035de81ea575c63bbc6f90b392f1946d8bab1dad68cfc893e0d7468e03d4f0d4e1012eb6105bc762e507f34e4e88ace67ef34f11bc7a307250986c57b2bef510d06493621b930529a8a58f1a519d04e888114c7e0c4bb24a407b88dd15a93cd143906014a9e17eef73cec2425bb6054dc379b9f1cdeab53f1059eea7458df43e34423f047c474ba0d3778493abb3b139d8c752381104189d70532996aff1e9ecd301bdd21df25db06d7a1a8406a0f02e6c58db1a597355ef7041e4636a07f8a5d56079a6d123a7219274de25ed44b7a5425705eea52d3aaa20f10c8a37089cdf4fb3876b6c4b3f37d895132c9f3e5e04b49b7a6b2eba6faf1029710bf2db7da62e6ce7929c954457977af1e85e66f252672e1eb93c4f028a40c998beae5df1368123d69c1228d289de1e53d17e42f54d9b5995e360fb0c8690712d34699b4b67e5a018baef2b8f98e38e8a2db821e4a46bb36fc3b4824d50907e31823f245851c4f564a3797315b1c3dfa4768d9f2eff9d752994c1d4799fd09347228798bb9171a47ca5b69d3d72d50adeb133a8874d62e925df90d3cb75f00fc687eaf1dce521473497f7970339f2e56e933356f27858de9c7dc4338e2d00c5a44cabcd21b0afef1be19b5835bfcbb893a89281af8e42877c7309b85f14e06c05c24f9727d3b2579535c1e573e3aa86a5e38ef90cde5f1aea7c39754c9120711c827cd5c0bccd24a220c9a80d097fdc77feef21ab2aedcc0b1339249626b034be3cb6df1596c7c37a81b7e576c69116e5687c8ad1e34f2d11c7fcd1894df075c325c520d7bebec386a37b90473e74d7fbdee3cb6064612a61025de017dc70b565635c1165e3a91a17821a59510a63a6c19833e7f6c39bd3759bb508a40c80f79e6d2f66c81bd39a79af4d0a8f08657d8d46d3e42517bd0a54d3735100a7f0725373d218aa01947d04ab45c568c858532f26eb72ebe556e1675335b416c6801d4e6a0a97d2b6b74e420bdf748ca0e70a5428bc8c8515e8a239a849b3701b80d01ddd0022dff5c5b20d0f3a3ab547fbcfc674bca871bdcafc32cad29f783cb054f4fe706c8b29ddcb568e22c73557930c842a83e004b254ef40c4128622eab022c5b27b40d8d71bdc945a690e8b62a63163f1360e75f6dcf188ec1b6715713069736d9a13660ccf207606a3ae8d3826f0fdb8f0efd547c53a0c3b0522ffdd505e39ee85299faa4c0c9ae4042b45d65a6ccd9b38707f0d697e74ee900bec8e30803786cff93190d14253f6a1e1db243d9e4b6152d77d52c4eee0c6c6f1876a30fe00d99c1114bb2289276f0fea78dff9fe49f901b487f68b936b3e93e3f9d6c076a9dad40151cd3889623d18fa11c9e03423eb43de66b8c2fd6d7c49617a3b70460e9bab20b94a7a38c1910ddf1dbd79f80174c5d692e2681d04f146783b90e04c6687ed9403d377a13270348721492ee280506d5c15f8242fe5180b8bbfc850daa08cd7a97a0f6a17bf6dd8e8d909f539513587d7637cb9f2311456976fec4026823c1bca22e9ff315c6412145f29bf906dd417c3d33055ee6265045db914002322c4c081c429067e2f500fbd8ab2c3b38d2ff4cd24e2ab340ebf8cc6453eb0d8e0d8c2672ee63d94413c99f057f661b556c62a4e71ce3dd7811a68272957c01a8736ce753f297d97854d225fe49d2680117d281a34c0447520e1fd5ea80830dd610b8a566f63f8c593662b7d5079e62467ee53272827047bac87fe6eb0ca009a3d7844d616b68eb8ca0dfe2caf3c9c62192488d05d0c9515c57c58fa122690248a5215615c74a226a95d7eed4c67620541f023f1ce6d4d6767fde4f5cec410b428b631678640cd43caa475d26b26858657a723b8b784f85153094369ab9de0f0d9e5d463c252c151c339c73e482a13a633785d1ff818696ad5785e2447e240415815c281d355a17bfd041b26b666b4d25d44eedb28846b9af31476cb484880f62d1125c360ee7925afd2cca2990f92ab22feabc3fbd7cfe803d675e747a6e00581ef398cb6b88f13ba318488f8b5b6dcb74564231049f992fdbaf32e0b36d0c3c9d4e1e4e232be77138b93ebc6b6d151928a844a90f76c761af365cca82a90023703772faa55e9406ca9b3e348afdd3aa26a2dbc171aacc2bbb088d7c18b9006871a2f0316c0a970201e3470b7a60142efb59804773bf75b5a71c7e08e9f00798f3305e530b04c25b150aacff157974094d19df3ad5bbc397234324dd31d705b5e9326d60d82cbdf3f628ddde9e54c711cadde9ed08d10bb0940d53463c5809b54f2711e0d1ada2d9e7efb02b11b9e78f6229a65eec37c5fc8ea3ec3af6b30eb446a6ecee39a12fb59053462e1bccb53a735262497cfea7bd2f0442c9b9fd06b746df185ec12c52208a00d35b55cf71b1d5916e8778e3839f55da591d97830d06051e370a79ac3c0517f77f4287b46ddf18b7b92133b264d08d22476bc58208c57f835512687714994487f4840049038e950a2985e75690d7a24b66f9396c076cafec23ba1fff46fd40f5dbbc8a574f21049683d65e69f5778a787b7e6e8503458df1e816443722993924d1a6b5de7147350f06f59dfef3eaeb23ab80dd0500ac5e06fc6f404f7ecccb1cfc2b974cc020669ba53f3eeac2ad351555e464640624863fab3789e3013c2e96b46abf7aec0bae0bb4dc9ce056d253edc892e98e00d519ed8e9ceca094481cda831a370f64b2323145a428b963d0d28bc741e3b30c0f1211a2a86bea654f8a1ed5189bcaf84fec1efb4a0fae54b37ec0cd9e5ee60f89162b44d7dd2c0ae21e64d9ed89568a743fbed4ff5ccec92ab43f61b45b3f0d1de9be5863908957d3d8f128b75064d872bf1c5cfe1d304aff43104f38fc3f07cf201cc9b350b451ed3b2fd9647d6c8dafa171f05e005aeb8e54ea3478641a03c373e708bdc99601f8085f765ca07381e6780e09dafc17c736b875c188eb26010875233f3fc5bb6f6668e503d42d1d5c27538b4393dadc278bd3e0bc540295091ddc1f1f671e64104586d47e7ff4bce6355dbdd876dd9c3e33d6ccf1fbcd2501394437193b038d6a22a50e9db230a83589da24ee47315f30ba95b080c31f8b0685f8ff3e044521b841687ac9e197dc0bd5f83c83d5aa7075ac87118166d9250171d70df48fbca8c6aa20edb8a5e45a21133c1c507bc00cf0f4e9d8b1b7000c0fcc2fe3f39f40abce768d4d178c1d0ada9b5a5c344d75d599433f890eaad89b02d79b8224059e07ce3ae632e0f34be6a7c06dc73618208664c6d5f70445cca20b6609d1aeafa23dc6cb7a7eff9484fd02afde3dbdd6fff9a2860e94e70af99f0bc63f1c405203de9ff59d6f5846f81baf5e09d68cc4e7bbfb4243499187a4af04355430e176ee833f59219bd0689d59b4ba0eabd95166792e9507a8dab0e8200f84e18d493cdadcd375f14e9c72e4b642836e4c0ae46002cac647604e21f46d039a704253ec73069f5315e749b0a91459d6cb298daa0132e69c1c433c82702d0facd4b73604f5c747f03e2a81f195ee6ef75500d649cde736c0643cb53d65210a0bc68fcd11124c5e5dd446a0b9c1bff9fa0a2b19645c85c9f4734c2f3e9a350892a1b7deb812d0a53daf3d606c4b2b211e0234cd56004b9b19a4d8ab39812202709bd67e6e348e172ae16d19801770ee40fd829477ecd0ddf574ac34e17ca40c78bfad23135f027f39fc14154c6332ff609291519ce31fbe1577aee8d8d5f00d7cd5ca025e6191feeacbcf974eb17824b75aa1d4c5b6197ed6419086eb8d430b8edbd555acb5b1e25f3159d6121103e7bf7304951e546f9e51a14e169b4e63035269e48a586abbe1d3a20a34827dbe8b33638e7b4d905ec54400930351e78b0ae5e3640bb5f92e54522d6ac6b57c0c2fc5bd0151378e3c5d954153c0cbc5350fae9b01dddf2f7c8260d498a4af5401d5e2b03da16c299180960196c247cf7406a6aa168af6a77a08f90131562cc39d6106655370dd05135fe676f2e7926c590ac883eaad671415cf051621aac530a7cfd7f6b427a6e6f5ca3276746a73ed200355355ec058ce192b4e1135a77adcddd860df7e3a48b0e978b3bb527e6b929a0a5a23063b7a68efab8369489be72758f870a307f2f64892b3844e88bb2846f709f6507f6dee2a70cf305148e02d85f8786dbf7a58b3bded1c5436b95a3b9a4d0dd9557b46a7865faad86c52122cf777447d6dbceb339c8f094ce41e2cf6eaff0a756c6177c76440314760d1b7e63ea9f59950362ab84aba87c89aeb88dd1a250b90da701840d3ddbda7c433d507daa2f175773753c168e37ec7c4451505f23e0341c438f9291841a4926446b6a0706f4dc6fe32598cda75a4652bfe982f1b030400b9871a82a7c96fab8955f52e7926b7ca2027e638f7ada82054f5e9efd98e0b707d660bf9b54101b5fd9d869bf8532d7c882b6d89b64955267db9e16da06b0462e4033b484f017396bc456174a5ef8b8d2bd279ae1e538d500b09dfa72e8f0f42bba70482093914fb5fc5681aeb5b762f296cd3725348938a95bdf77be7460ab7a15165d3eca688985d955b54bb167ad6bbe3ae1dc3e28b6d77de55a180ea0256cdb74c2ce5571b36abac0358ffee5d46b03f98979d1b5f5e4f7a2646ab3a02d8cd90f586161d5c99ab9cd73deb555053c33b280fe8c347d89f5577675bc004d03418695caf809aeb3c9290537b2bbdf72454ad3352a8f0fce901092d54fa0b44fd56cdb23500331ab63f3ca0a466444506479afb52dac889f801fcab293403adc34b13928a90f3f2ec099838b42d8b5651381be5fcd195c07e56534581be0209302757405b70dd9cb0aa62b674c4e9395dc173d5eddbb987b8cf2b7638f10c9f884ed936b9986bfff8cb2db80df12198a3dec47d702b7b49bcb85b570d6509cca307c382fd6473818cb4d53b2bbc5379597f28197ab5b1cd93aa15dc52e9045850dc7acf005b41143a65187082f668993ce1748a7b482a24f3e0763ed399074acf8baff145dc4f5686bc059ea0f1a06b4e73d8e689f5eed873120b1c7ef7049badec9061c26843dd23db9918a7573cf75032096d890c29d29606b760c11b0ee0a0534a983923a0ec5b7cb63d7539e593729c0d747d9374a78dddb62d739d020211d82c7f647ca4cbe4196ad1e2137c33b1b863af6efffab810c2ee048bad08bcf5053535b46df0d9058ca2e48e24d856312d09e55800fadd9712ec10b4200af8216f15466272c56ddf0f35595842510f72679f7f9d2208474e14acb29836005de94e99c00fd76c72b250fb83861fad9a299bebba2d2bd8dbd16e143ad7290475c05ba60c6399ef970ac6f2aec9d6d2b6bc0fa2cb79e8df46a51d6ac40d5109cbc9e9cf59514c909fb6944696cbc35603b3f9a971a644063762f5ac7dc3d606b1c1bdd79a66d02935988dcba349dd61288f0f41c4a113ee5186cc1e06ab3e0eb7a3920077bef06dc0a8c7c8c58a7c0953d432181bec10c84338d475d29510055c5b10a4bdb9b8b8c8a8e0cb50a6f31fc7557c9b706569dc29bb773172971404ac252392c950b3e6cd92902f671ab76731ceb165be1360e4048a280b9853cf0688bd3a962764ca80d827d5979a12c7aa13fc38451643a3c175bcfe9a476ed90557a4398414f761a281a99852567eab7ea2f83daba8831e7ce73b4d5232a19c0eecb96b08f3b0b4ac9bcefc0270da0fc215ce76d0396824c5b7f9ff125c50080890fbc848bd95007a2270ded301ee24fea8569c9dd67da5fca4b5944976efaf067f670c8ab56b751f17edb943fee8ed800db5e58911db660576ba930d9d47d50ad0fd0340b2213ced28ec59e1a86ae0d3cbb5a32e62690c33b5595d0d51707a07519adef05aa31b3674c6d5f202773f85936fbb19ac6b5afc2e0009b63cf6650449d266c207ffcd2a3ae281f8211c5a557b7f9a6e41a4979841d898d154e8ef061ecb70658e8653b39c23b40f7049dd9a8099528c15fe969061a29944d9a1d9019c322d640c12eec0a4f9de426eca6c7b1a4a282c12428a5a86c8ad374cce4b09ae1f051215744bde9fcadb311a424b91d9dda4e6b04eccf81caf1dcc8b9a8406ddd14e8f12445e91a3538425c626bf5d031fd2ac75a7b3bb2f9fd5d269fab6087bb894148ff269653ef9c5afd09c2e147450d9c881d452399d556ad5c3e59f09b70306515679c796d16e159b52d42bee83ccbee122fc02f2e154c8ab02297f0b147406e0d8231b6b5f3bd8e4e9795ea389856b2d673a9b2bb2f4693e8802910b9c69180d04af7b3422cad824495b2ab54383f3aee9c6148b6ec0a6f10dc656037d8a44b6e67fc14101136aa988b22471308e091784cd386399ba676f632db401434218dc2b6194719ccd8c526e5bb77d701d8c37f3bfbe4e038c0c49fd2c880dc674cbb2220d4e2398e5d5a18c7563309acefdd9f4cb5e481ae1b44b34f159015d477f2183596eaa9fc2e32f344458f3c546f160aeeeba6f882a3b31d58f8e004ce3cb16078b4a4880e6961f458fe3a8c31bea0f778e3c86ad33c226ef07930d2266f8f2741fe6a45f1479cd4d58586f4cfb784f4968cc3192ce5329cc201e0545f9f8d4b0a3eb3499268c5d7f1e96117bce7301325d2da552572486d73494092008d42d6499befeb3463659a16d4670b0996922cc2bea44fbf4eb9f33398b05f15d544c29ce2a7af6400f4d6a928d0646cbd515f5787f40bbac711328cca905cbd0cbcb9955c0e831cab63c812d1bb943e38cb63ba9e147bcdf94fdcb4034039af4eb0c2649df980e496ee114a8556c42f3bc0847c5ab40cb68a1fecd1f360014eb467ae4581f69df9535878190a4e87215f511817a8c9f5aa3df07281bc0021e38fa6db15769a65d3198c4bd2ffbcf593b1073a766146e5603591b7ed7e50bc565106ad26a5b728caa2336683d326ba5086c1e51bc83c6fa11f37a66edac0f8d6d4cb279257f3c7fdbb689a1d1d8ebbb4dcc4e1000701462f8504577786e04591f99eb24e2a03035e602e297c463954907b51ec1d73df9c29e2ce4e9dbcb096b0b0b840faa6958181ddd168d87e078195c20447a417094b08f794b0999550319f3ed5dfcaaf826183f283b3c63e85126cfb2efa1601bc771649e0f7b58f10f9e63b73687fefc9aee5c09d0facf9906a7856cf5b27987e10908f4af35edf0086761b587caf8f4e88e071d704f43ca774c131bf6e194de06b3d472ccb7031e0df7234cda2efbf06603473e73d31115e96f9b405e17d76f71f5057899ca3ee208ddcd69e46e10111015bdd7ed5cc9f080c3806b44efb04fc470ec0d170a06000885a5704b7edab4146a4460830ca747528afba5e4c0faf9d4bd90d89db1aecd041b3ca97fdccb3defd5e700e1649818f6c9422e4b9ed5beee3c9cdc2ce4dcf102af35b7cba1d6e6d4f33b30bb618041f0cff3ea6ca96cd4cacd867fb48b98b30f23f59e97569329fc09b48e3111db3e97a6e04470cd306ef3f7956e81f11fab0efe01cf8bb6e79a1b7e95dd98d9a647d27c164251610c1327b0acb6ce7145d00b030ac818aa8ac55fa78307ea7ac38499c8daab06b8ad54fbe9977d12dad97e057f99dbfdd678b50322c97bfc33b1cd99398339b746dcb5be12873e24056a9709157aa18e86598dc0bdb99a50ff33d65726a28d82eda385426255e90efada4506766a5e2299346d708fed6f1bcbdf7af21b5ff5c0448ff3fd3674b4e5a674d8002af1bea83c8f9e5f7ab069a73d3d9447f12f95acb745ff708a5cb8182de49c0b41296fb7b39642d45fab88fb9c59ab551d7b64d6c533a3e7c3777228a08e0a0798b7ec0ad92d0e880c34c54116220e8c5e36ce0b8137b134bd2a350f9510840733d9da822ecb3d2184bc4c9b4f1a640a8eb4ff8e3b22638a8c1a1abaa7088d07ebe2c2b05cc79f37f4a8f714c4107d55e3d4d66ac165d6d801d80a333e7d730489e41386588dc8429808a33986583450f13e4f7a9fd4bb0d1f3e7c2bebe2950d8cb54f13dac1ae3558537ca62b90d119d062652d5ddcd679f7700aa9b18a1b033db69d27ed1c4e772891a0ee68d439537eed11f7dec43cc227d65049328e3e0cfc8bb96b40d664db2d961e94cdce93042881f1185b946244fe521ddc6e082e0dd93c1d3091c27331dd0b1a1f21b5bd85e28bcc335abdc0ecc725c8980195a50605bd2df8b2c182788969c235d95686672ebc2c6a8106946ac667d421b5804b042b006a65695c1a8c659a374602691ce1d5338bcddcef24f898a1dee90a77620560dc55cf708a21bd7f2755fad85a39c8181bd9451b8571a96efae1901e28350578980c1e91b1179b21db39cecd7e5f009e5da5720072f5938f60982a77bc5708f3b5a28a1907d0593d1e07d2d81f6a62ca7996d3a99d309acb4e2aa7be719b010a955937c1db0e1cc700428361aecf5c5058cfaa400fab9c761715516ec1ca0f2c6bdb6a7260c4bea2acab7bd4e893e2345d53955d1b3a2a4a9be9c94196ab05f8894241a130b42c0dabd6b01396014003bed48105a8247c6c682f04ec308108606db89ec6767720d33c42eefc60cb16ca943eb2240e7ceea89a8211b7a1ea0e107213d8e53929f4470e72ff74f534adb36d1c47a97b5386b1a5607dcc6ad20235c350b273805e133075a5aa5be56afcdf06f0eba11495d047dd601a1589d30ec68abb43265e0174808f682cfa48c0eb18deca57f316c204e3cc829f35345c0d98ae766ef8abbe676aa9b9a4900f5b8e0ac1428507d3d1c96863843516e2810168dca38957ccadca1889b3a80a0df9f0d0413035bbe9a9b0d98c3d0a6666c20340f7dd6670bf683cebcadc0e0e0430a01ba8589cc83b08d48b143c8df9c3620305845528af6121d587a9ba0ae90009be31cdb7600c52c686f1f8fc3d76c9520c5a0a1bb9aa6e90e076f4b742aeb4f5b45d8bbc31f45b55e93ba6aa1ed9c12a08c415f445af51c3a39c1f79b7b4cd805236b93ee1ade25e6c7e26a3ead19fe005995c4e1874d964880c97e657631983a455b26a946e711806da19a82bfe6ec703ac5e3bda47eae7fc238c8a8f3fa830588e490f573d578f0c1225e4deea251b0e387b6fdc2f73d3e9835ad508367acceb0826b4037189818864efa973cec71f0d8dd37430a2f64cb49570222a9899004f0f8e4982aa6ec1f7a3826f2d9cb9b1014d415d2eb7ef6b2532c65756dd23a9b72a99b255d45538ed9274d8c3eada6e0d7e31a4117e70a83698c001c2a371731e535caf279cab1286b916a4eedabcb9041dcc48eadb7b8b12569dcdf631e635e202b6ed3b4b9a88e605d8094ed3510a09d93a50ed358b16a42cb7a7a1438113865db26ec6b6136c7841bcb74c9cfe390f868524502501cfd06eba5df4bae62334ead7d7ddf3dc795f968f438dbf4255003676793d15c69603bc7ae94d78dd134b478909d293a10230dc1146dca5fe650b978ceabcd59ddf383421a78120498afe38c1c7478b9eaa58a9f7cb62483cee06cdf24811056f8e7381c0b4d5c60287716ab89ea3707b9ede814a57a34ef1630ca5829b859c8681e34ea8e9ba242f2838c805ae52e03cf7f3907ccf7fc56c570fd099c8382d3fc03269e9ff18a08a3bf5b17a14cd8ce26721b4e59bfb63019c07848708dd31ad4b25a69f36d8755e6622adee57e6bd793e195d07d51420550c0c21f463a3905e8a08070a21d0b5ecb7078ffec7e67746d4257a40799671d49f08664153de762a6b8cd9b2642f5f9ba99b52e0020ec46c460bf3ea51cf7c73e003a404c979319b571c00ea425755a2477b7a79cfb17de52f37019ecc16a7afd30ef89d5d75e6a536f8f4fc9b3cea872bad928ab5a3d86eefb2401cf79dbd72a6017c4771f3b7b39d3672f9c7d5dfeddec6f0253f36b74244a54f276b795418820bf8ea9d9222c9463614f6e2ff359c633d1141f70cd48eb3dec41d1b5327f0f8040c9e08981304797b0682d319948f8a8464230f5b2b5e6463cb952801c739c809469c868d6b142e9ea77f22afbffc2cf54609f6a7507bac6fc4320592b0684d0a1da4060497ce8ffeec964fb7c33ed8a70f85170e9aa32a53e6d222de5ec6c50c1498741649e9355919af33529dbccf6ae611e8890000f863dce6ce8f5f5a2000a097abb669f7746d4b3d22ad120bf4be7be07cf4ff1ee094d13bd6cc6398c203772179736ed63adb10ffe41acf5b1817856cbef577ef321bcaa8eafe4819fd0b26624aa5c4e60588fa18f51629e95b42b973129d70bd80f35f93215a71e7dd04a4e58d3ecd485ad34df9d52a0f8b0c8ecdd24f7f908feca82ae0acfdac2ebc088452d9fbb7dc1677cd8fbafe84e3ceac6daba485bed92c9f06ac8499758af20588984d374928e8a6e398da409cffdb93e456153d82e4e8b3f40bb42ecf00810d810bd39c4179359a0999faa458eab3c779defa34c301be7285acf25ce694740d4749287f7ece511a33bbd999d788b7326c0f65c2297c70f7680b2840917a89050bbb88b67c5b90496495874a552a7adb06808c3aae3ae55a0b3d0a85b87f4503b7ad4b1e8ee558dbc40d44ea1c71df9941b819edcf0fa2087a06b1b08c7de408a0d4b760e781d3e4b073b7c25bcd7f188263fd4372e09d421029dfb08114a30f43e3d1f58aacbfac1afbbf00731590f651194c329a6b3a6af040f4f5b0c75909a42309e486fbb735a70351f1ca9699abbab6d5e9217d01d8243d623a97bbf10789c158d38d02f335d336007d435f98707dd58f4ee04050662c2c3429ce467c03f1e9dbefc460e27f2be88960355674dcc421419fd35158d350652d79d8772e054b983cdc2bd991b9b8e33324f3ca4e90931813e000e8054fb8ab459b7bc83c0439158d788cedc7bfb41f2a1ff6713273229ee9aa2ceb4e6fdf8970234cc05e0b35767c1f62b16509233ed477fa8381ccdce927b13548269058588131a610790911b07c73f3406c6940ef751d29ec243e93e1d311e40411d0c8516032a735730a16d6820138aa3fda2623c5c76318471356c6cbbdc77209528b185bb5b3cae00309a27f3061098a217608dc1ec234434be1d76704f4d2784a2783886cc727210066f960ea421d2eb3d2eb6cdfe55218868257a8f34ee7abf167bf807280c16203a7dcbdc9704d9e22a45667ba10f8532f85cbff63d0715736a55392688b1c4907cd4012e4c22a50e01d84de03dcf791a73c47e78becde219d4e4591227bfbc7072312a5d901422c482034d4707592a764104ae318702a666687f7de5c136d3e09c9be79575fdc545bcc965d5b0132817bd45ccacee41f36a5b3fe1fc9343e290e0bc922b09a0a33fbae81d79ccc484f2e19183a1329600d918b8076ef4ae1e30692c5fa660e85366ca7cd94b477c51452a47a75346589682e2105dd8ed22915064a05b781d25b6dea5b3baa051d6820c246a05dd68f212a1ebbd5cc2c0722d70bd966f766656f12c6de18d27340562d043636fe84fef490e7c3252f12b9307e0638cd431723d0347efe931a2b4b669fa31f1e62b0f3cc96cb63bb8908cb4b420d59ac287c45b0bfc937035159ba0c1bb45d572f5b7aad0ba76f56ba628b5a52071c43f7cde011a4358b7daa0ee574a51ae3337da45c6b9f97c4ddf1d105cb280034b8eb0ef1a53955f3e757088ef32e045d4d27a01f9525796869ba1c965e6e03f7ae1f95fbf4ae3265d249f39b09e12729fc2b9d759b1fccfe46d7311292fe0ea1e697bbc01bee50d4f2afc035e757bb8a448c2caf1e5a358d8dc980288467081dc4c8d71a413f993118f9f196106c03888e4415c4e75dca657a1f42507a3c071c88bcfe3d8fe50ec67d24c0054bf7a99e5c53034a2059a3ec20f2e93a165303a3c14d32759263e04e16ee728d2dc8af1e7fabe29258362c26f7783dc96715025a78561c5bf5a5778b3bd819f21da302a801f30978d0f37d26c32d7a8681690f3030caa8f27610c2ee32ddf65b239055b98adc858edd4f5432df06cb8e598d0d1db241b17d86f17624c7deda417735d63d5ee0a1d6bb14667223323be8737a05beac0fe81d736f1c36f053ed2dcbf6ce63695429498ed9d67113f8cc8459ff086af07b226e44e97fcf7ae18bfee54d6888d5ad3b298402d07967c3e822470d013f5950ea61ffbae8c8c4113d0e91972cec0894685d7cb7547845e9192ef31e08083066ba0124e3874d287688c888f6c17f8f8808d0e9a1fb893a3d3eb4abbe02a9abb1540f5dfc5678e50abd3e5211676d8738e0131d4890ffcea96c31298d028dfbda0f423e06c0db00b7605a37c495ed671309a3b244e96602a7a77a49510c +generate_ring_signature e5f0a29dd77184ea411e8aa0ab1acd3babd988ebaaed6d759522e6ee0b91318e b184ec5d2fd9285d700395404533be692c3695ceb0d6e4c3e375aa7ba899d3a1 8 2935cdf70832a0deabf5d87013a2e5ae96598aa235c3805f0c9d146bf2491a93 3421f69806fa21fa0f06c6e6e878c9c901eab8341bb12a7b3fd3717d3af9121d 31bc668f760939eb41ac0bc0883a7830be7303be7eaa84128aec5395f36a5058 34c3e972648229e539940d5115089ae294a110e58b855ae9212be053810b6ef6 03217d3640647c653eaf40b8c4da4ed78029b37ce32de3e51c2bbe5a30925d19 c69a1b9968186a512f290c0a3cc549c6f522a46f7319ef08b4cf8c754a1055c2 544290ab09681705ae3b25bd2727021b456425489b2399296dc4b93db69d49de b5a2934003d24e5a75d61d541ecb933cd4bf25527716f2c4804c20c1b083575c a73600d26bb82767a6e8bc127d7b61e9843c74585a8602471030db83805cf006 1 de945a3192e31217b091e2f2fb23c20c785f59a421fcd32b66a7acb04fea1e00b8244b649098861c7142229d2c9ea0b73df68f8483cea43ca75a7b9895932a0736a758764bba9e7b2b5d1a8743266b8bce4d11718fabb56786d4380c0a5cbe0319aae2dd9f545b8303318d16108e12bc9eb3d0fc50acba9290e651263a0b820c4f4007b7a93dd422c6dc2948376a9b7b51dd32e8724b8ab52154e49817974e0a4cca632f3a53ff9dad31c173afab24e9e22ec0263c185a2c0365576927534d00216440a89435c18a031cafdb2d640a562135b61ec86b2d9da8d5e5a306012c0c5c709d5adf44eb70dc86339551141557069ad0182fdcd70d4639dd7798e9220c3adef439c4b10d1e565ebc5afdca15b754554abd38c3963416aefb007a61430ebb2d66a21ff262280fa34a46511a222016a76c1b029a82ef62ba71bd66bef803b0c6eca0526bb60d8fec5f9fe70745de987a429a93d646fd67deaac2bac8e50b68ee48dcd5d3a0b0fdb03d183e60998a3b16f3734d1260919b4660c5958ed600d330db8c226fe703960124a1a02d21675ec8814e4fafb41841a97753d852f40fed38548cb71479d6c7066921a2137ecee2e5560448cd00fc7f6b9de47dbc350ba0663323aa4b0d86fe9ab61939f36cc607c9853a04b08cec502f98f3ca34980287099b12fae09d9ff0d06c22a9d11246401257eff629faf8c5f68f5c212b5c0b +generate_ring_signature 3bd2044b4f2d376c3ddaa3cdd7a432bbc539669245c3a1f685c2d56fde7899ff 9cc951e45b4e415481ab8018b9680a92cc8b635ba7048af09d51ab9122fda3af 26 14ec0b39cc07c91cd9646faaa3e6f0e145c0ae87cab81c7f5a964f317aea96f3 53d24156f88a571ad52453cc34b49e5f5030063373a898326ac5a4fbefed1a8e 86ad0a8a73f8ce314b6490099773f2e62d74017e0ae06bb8bfd1d3ba33d35659 8253490007ddf302cd3abb1183a5935977e2db6c0dc1082dfe61f310fa252415 e8ab257f1b72361d8f40d6c02f94d820fbc0feb94daf01e80280becd1c139f20 c8334bcb7aaaa57254d288e7d5fa9b75449c934c327f2a0f7a33e118f9dd6019 b06f1e714453fd93df5a16d2bc2465194f1d8f3b746236b2e92e9372a127810c 4d2a6887b8233a3c7cf2ad3c8f2b5d1e1edec6ec11dbd6c7e54ac51629fdf8a5 02866c17301812a6d3f69f3433c35d5fbe9888d4ac6939e7f32e73ef3b431229 2ccd9075aeb0f085d13bec716c49a357ac536419c07d6cc05db5c246a75e407f 070da4292f9429ff0c9974229516cc5f51aedea2ae15e7d62e5fb004967ac48c fc1658877f8b339dd3f7019cc11c19476fdd6d15aa60236102b0bb409310f5d9 ebecd500eab7100eb407e96a69e964c9569ccc9033fc704f3a63307bee3b72ed c260be5e362622b1891c16dcbd4f7de0665cf4aa5184ebdd5e4c74d8d1fdfd25 e1233414e0dc0d63af9eac4042ff0849c2c2a9cbd2e7a513e621df8acdbd8830 3f08e26249081c2893b12c4923e0b76504a6a3b1f697cb770008b87fd92ec6cc cc826a6e9bf8754383f5c7018e567fae30118a565d6194b997f1ac929a32284a 62693b6e8781cce3fca22ffdd52f3486e0d7d140fc449adcd466785f2a563170 965311372baadfdd1aefed2e85dd4e77f15b3f9383c0dc83f5a81328e61f7c35 9177ef1e9800ccac1b88afb0a8b8f15df9a0e65e915a7bb49f360a2d547ee3ab fb17e63221d91dbe74886ed23485f0b8f3274d401bc7f3ac1975e91fe73d716f 80aa8a4853173f66995c9893ffc526af0e7792b9a2ce156bde6ebab0df742ede 7c0572ca57ab919c91d1bf9453af71d23b76b5679196f9d0d326ee55a412aefc a3ba27e7db76d65de4424789cd1d019200eb55b9e487bfb9d4cc5685224678e2 b0def35abc12838be7f4ab5e7223ed5d26b6824c3acab42718615c117bb83bd9 a95c2d6a90469cef87a728fd14096dff5e9427b7f66b8cdcaf4737ecce9c07dc ccbb0171b24a9cc7c2bb3be518e35334d55aa4ddb1881c0a8585c82b55e44e07 0 f95998119bf2258e7b69e537573f9f0759aafccadd45dbc72ec8bb47604d6201a54511e0ed4b8ae3af8510bccd1827b8236ebdc93cb73ef23a3f198c283ff5098ee7a5e8403ecd8cd222311c5426d26f378cbfdc02b06f4751876e2cf4fb7d06e34c1a3c5ef7da1c219506e63f94010e96076c60fa776497dfe7a6eac18ade0bb6b7887bf7e5daca4e689f20b0b2235da7c0cf5ee32d8e23964e5bacce313009a894f857693364b597e1f0c0cfee06bb7fbf4ed852b497886368e6166ae0c403defc6616a82509e3dfbf3cfa8f4b3d18263f0f3fa92417ec8ecee101150da80063e3cdf8942e80b66fc2c3ddc722a3b79ba5ea1dbf44ba436879ba075cf8960066cb02863784d311362aabbc4a751d16d85ae3a252260c7061cd5707c71215022bd79b969468618bebf1ac07c9633de9596ba6031e15ab219ef59d61be19ca033cbdac4f5dd7310831c3d87cf5df2f54a375df1fb4bc0ca5dcd7576c565e8f0217e418e94c38adfa48819550791aedabb7058bc6b0ff1f236870c5328d788305f8504d987b16d5cb5fe69a67d1e63013f1ad47e2fb89788fd8f7f8d72dc6420b33739f548ac6f16e85a085e60d09f911c73270fc30b65b9567cb7a4b6102680edb184aca6c8ef878b0846e926ab9ae53a95d69db2da518a66c98815c6ae8f4058a91cce7d5aadada0cd2f2f1b4ef4c3ebcdfc5c3a7a725a82f24af401e1bd60a3ed089f100fbc1f7cd4012740b83ec98450253260d078db346514dd003abdb02a13b8f23419b3d490b531ea318a604a58e52687182ff723b9f6403f446edda0cda1563ba68bbe31df8f10a7e1fed7fd3ca0202d6fd95e3f7d48ce935d0b6bb0932e0bf2096b966e75e0749086ff37586473ba9767f552029a76ba4e9e6a4df03545ee2df9bffd1150e832d85a1efa01a8911ed23e154627864b7f134b2079a0a4a8d3c9fdc3bf3bfc51301a06ef414b1eb3498e894dda13490707668cfdc970af38c01f4b20315e8a4ec0b689e3f679961e16ff339be7d00c70240d0aa943508e2f3d738bc1c840e80d41f8b73a163106e8abd6e39f14e70bb0aca0963051c03ffed88ee4cac9bf77eef3ff635526db73a4870d132100234210886f38395ce0f7b053606a4cc06905db691c648329c0104684a8846ad9f1faf538d19967ac209d1daf6c9aab570a2ab12da019bf9f024e29251b06f2dd94c6238fbb05409a1045ff4d572bfeaec6fedc9596c01a6eb1b8c369871def5555913254c1112654300a031bec3f16f30a3f0be0e2f528f2bd7f04022468007ce3dd8a3d12615672a0cf8df620eb52b5b07d7c0b65f0488453e8da7cf0179b5ef413936a7017273df091d78df5508bd91980a1e9d0f3d0e253c9ac1f38527e9371d115401fd8577b70ca65869b03c210728852ba5b0d7ac77ee73abecd63b34fd3263e6a0589e71be093e40a9537c3ed77c9c65a0b24fc5a9efa18936eb6514e38f8bf8402c3a233001d44de5b3cb9a1098d1e4af1d684a9f1827b83a235b58a2c9cdc8393be5e57e08b6f2e27da48bdabc2d738cee40fe2797549fbc40ecf13b8f7a9ee38af615af05f036fc9f87e30886f2d3c4fd816802a595be412511bd85f9844069f02410d809a7fe7443609a732833048d40f8fbfbcf3849c6bb8b2a05993cd232f4f4edc80942e621457801931b4975fc4239262c812191beeefeea7c6371f3256ccbee6006b0712be4f7b32ba0e6a1e925ff952917cb38dab1c531c7052c0953a6e834080ba9750c0a777c4a041293b0bb0da4df691ea4813127b61c4af044ec6deed7c607f885d2a2065e46d601c46a5afa395396f9b49a0da4e3de190a09d8648591f30d13279768e12b8b8b3a469bf2c96670e4e58a09f33b4ff2dc9c7e22364014cc0f4f24d5e6648380befee988a9d1260a29d906d575d0f5812dc1432abdbe6df80d86a8c0cfeef6df7e4db54fd4a06841c201098ec4213bc415e6c3c16ade5889060c9252161d128dcf8c5a1b9a05a957815e211d14a0292b2745a3da673217ba05dc671a8479373520266e9266d2a8eac47e3bf104301f47586f98749abec9260e6822ea04bfcc36f17a4828fc9c4ba8e9186af7e17e0bfb2ca3bffa446504110fb5873772ad7e00a70ec68c4a1801092df6a4757142d22cb970741c2f40518e0129aecfb1708d7ae3c5099d2564448b61e0270e2065d9cffe58db6ae620284308b012da359afd9c5e31957bc51f6e5238fcfb57be3cacb439333cdad53845be0044433d562fd482183c069784fd599e463f45c0bf9ad4060b5247a9d343cbff067d8809800598b7858f2ce288688a4baaa432cebed8d82eef3a295b0136972808 +generate_ring_signature a38a56ce74c6a676013b374525b7b4a6479c463802e862f08a04d039c2b22ad6 822bd2a9e5c9874f9b3913936a6e412b4f2f7820456f32ef2299a3417e1c482a 1 e0eea6d37ddc4533841181dd0038ba2e598385a0b5ce7aecfee5c4f3d253b0dd 7dd64bda7e0ad3c65662680a287a5ed7372cad80bf25fcd8d1ac46d1b5f87b06 0 32a4b6d980d136d46ff49180fc579fd7ff927cdc1dbf73f69c77d9785c43b40333554199d7bc2970dcfea9cf836e93bbe93da7fef519ef5c6b028aa158b9d102 +generate_ring_signature 7cd255f7a67c1424395710b259d1a21b2a004a033c0c7feb709c56cffb761d5f d7221b7012edda97d677f98967689e2f73830f51b7ea1565d688ed7dcded20be 6 8ee35a76bb883365d45a82f6d0e30b47df1cb55791b3b3b195a67c9a0b59eb96 6868ff03ba585201b1b6b89b7152ec2ced7d6ceeffdf49bd8b9ac7f008eb9b1b f86d6a6c8e1ae32556a8cf71008f3b4db979606ce29e1f9596cce0a03a7aa2c7 6ef7f992f809d43f543cad7713e740d3e368f939f1d5b79ae3b2d94c256083a8 b218940efed2db15143524010a01870cab1e4cd26fe6dea4536554907404e5cc 57a61e8dfc9e63477e5673654056dbbc5adbdf3922224d59d412e04f08ed0e1e 46456c44fed7ab706c238d55c10f763844d1f5b1c8475268445e105599e97b07 4 f31038d727855ff1f80362c62ed4135f4d8ce3884da0a27c516f1ffb2243140b10c1293c386ab9db2fb9d2b606bb46745d7a0cf09242455b77a244bd6ccbb7013f539f936ba4a3e1fe226d3c2d9275e926680a0e6f21e9472fbe709698eb010a10a657ed5430953c67cfd5a44413cf7f904b284542fa8ec030cd318e3d4a630a97e8cf8de8cc695127dc091c26eedebd5c6b1b41d5c69b9403540d8252037501666b150a443d6e90857183126b07c991754a70850401eddf6a5ee2013a2b9d0a2a0390d0f020583f4b685b18704f79de1d8b136ef88b5cbea38f75224c45590d92380561f895c39aacbfb6e4e755613311bf17bd2a8f0fb53051949f9887bd079d1f64cdf0659f01ca98a87e0d8ce556cbeee04eb6d1ab0de8ded6decf889a08ecd7edad102514ee6fd8ab410936461e66905f26de7b2d58cec75ede93568a0349297715a686b1d691d617aea6f331ab13140b657a7e92bb9c4e6778d3a9c508ba3c7cc538a15a4d6178b19761d5beaaa29e3b2e0887ef96cc3d3d7ff0738e05 +generate_ring_signature 5707bfa24085f0ece0a5a81f38850fc2636548e19efeb0264c0774fd2176eb4a fda5619ba5790f97be831b568407e47ce4e7c7c884059a750f43ed335983f096 2 5b388d7e5f6e94e9a6c3ea839377aaa634cb4af85f0f56051ec43368b216b745 41763c3b4bd9976cdd10ad37258ffa7af589f773d9e51b7261cb890acc32ade6 107d415e5138868c841ab263c7e47e9e78f0642da4ddd8fb7cab32afbeb4c002 0 88501072578fc2c333456e1d24f89767ab60014f1577e3287aa9892a22de2001bdb8f5e10a3f9c5897c5ab8d3ee7398daafb530aa5c27dd6b0ff434e27d53f0cbe2166788560ad08856b14952b80539c57bff9776000c422f8fdff7a03e7dd014fc87582c6ab72998e83c3616ad3ed2e358aba5600f3a58fbfcfdda5d8b2800b +generate_ring_signature 931124a3a0aad7efad883aa981f8155658fddbf1c7d152412d1b6ea8e8e880bb 6452fea46456fe0149ec4f37294dc2061aefbf7e8842e653b353479fd3765a32 61 50120c3b2f003637dab92ee8b72009cf349f55099f96cd1f4524327271abaac4 f22c03d500721e53e0fe6f248d4b6c334530102858628db7c083b50b7bc8e625 20a3754ad0e3e1ff790b3ec39490eab47ba82c23dda5f16bfb72ebe4908efe00 bc9e7a813437737796ad27f5143e627282c941134d2c5101cca0daf4b308e31c a2a29664d574f9154c321995f47d2be37aef2f450bfe3eae66ca1dd1b8997ac6 154cfa54dd2ca51df86d58f5cc12a0577a125df5c584fd24dcc97af4fffb724d 54ca67d2c40d5671db327395a65e94fd47bd3e61d9a51ca8d2e447c6982af4c1 cc44c266417fb976343949a59fba4bbc1fd890bde3cbf7b4dc26cef4548c6605 ebdac714540e44e553fcbf7786fc70ca3e24b79bf854814fc7fa854f50d71c8d cf354797cd60864597c22390542ee513270e944ef623360147bf0220f6eea610 a1c6696c2965f0ba71f1dc26a84c4eb03cab915d88f604dd2a1c641c9fc6874e 9092ffb6e8dcb3c9fce70fbe446eca86a133e944c60ff63b5e335762c37c9322 589ba9cab2e75e23c49009fa6fd97c695b5250eaac9c51c8c56a7594a26daa88 40f918215ea5f357a3323c8286a59d8c3374d684c5bdba96aa8d7e50ee7e9c2f 605f3fa8b7acb661e7c732ee13052bc7621f3b249ec0835272f229fb3e10821c 105b7a37d527f6a3ab7475467f8782615b45986b9fb5facdfdbf00fd8abf063c 9b478a282f52db469a4c82f60c4e444b8b7c3ecca4eaa319a85cd05904fb14a7 d20249113b24dad5077ae99eed1101715c67679f23192e0283e5641a7193445b 645a9cb50884efa626302c9b4ed37c5378160ae816a38b3eb4029d489766e671 35bb786e97a8f0077e18c9eedd7f95943ccbea9fc7a7c7b9a3c935e840d31aed 0c6a9b9e2f5611fbb376b6a812b0b97db97d6f61e9f9eac9f56abc194f3304bb a18d4129230b193ba2277393fbf66b3574a2e4f9367151516996a90cc84cee65 be3f31cbbfa36ccf4f0e786a9b3772581edda3c59fd47d34ba34d6f7f2fb2f40 217a21703b92be83f88538efec6ae01e49a138e0cd3148215a9fe03e2b425593 2e391df6cc248bb12ee81f8df52a1c60ca5b144d029325c4bbf2730145349807 9448a717bcd3b977a30610f9146bbf9488fafa7b357369f1fb8e7bc13146f964 024a5fe462aa2436b1e39f8fd09bf025ed5b94c2acfaa54d31663b8e99771a0b 6940c0db8f651a16a346a413ac65c031a05c9c053b08f07cb781f6406cc8251c 89cf5ad3f1b3dedba11cb57b0be3f1693201c7a4312f8b3708224723336a4b03 3e9da22c9cd81a1f91ce15a00d38bf5421506fcd425dd2712e744f136854cccf 2bcce1639bf39ab42b5f4acfb63bce2274b9859dc5c6697c091865283280cf10 fdd8f7c5c0d09cac4daf13fddb220676d7d43e8df97039e422d7a029d50129c2 e07f188da7f4d0d81a875d2ab4e4751a0fa0dd01d9d7c860455bf960bb78c9dd ed7c3c3664a9d30e486908d8ce4acadd456a5d1ad570cb24256943569b062969 e3bc108a45fa338fbc276ed0c3cc480dfcf2f5797e5568373e36310a0aadf73d 04ced9b369c249ce8503ea1cf8f03db09addf3bda7447fb63bb151e91b26d8e5 e08ff76432e98f3e7e19f72b57d739ec5b61040fda80604d609fcf222cccaf0e c88864a259af88da0392de14645c91efee3ea3fc633ee690f2c18a39eb1a872a 4b3183de56fd789041f15422b89f989397d420d427c7ecc4466dba5ed433a275 259167389dcea6b53a1c60e2494ff99ee03d590a6dccbd64e0c74b7de558a697 9545f5b64a1b27aef58b4e184789a8cd94f75b01d578154d7b355dc7a39f3f8b 662a7ad2337222fe0d4318215a8cdd8f33c2a88e076d80a9a3e36db4650701a9 2fd4fc08a5516b4395a8e882f46450fb96cd48bf8c7ef80b213b6878dc7694ed cf9e3ba21e0d2608cafba7a39cce9b8d2964cb3c1449941262078214a37d863b 525461be91c9469c1ac5002b7117c103d20218d397aab08dae4ce8175b75886d 72496fb826bc2a82d70c29551685f386d59953b2e315ce0d5b780079affcdf4b e4e12c9396f8825ca28077eab656fd0eae53d416409033cecedbf02b8102f07c c63d3542389ded527e8bb53bf6b28a554b22aef442ca904cc8a51ade47dc575e a84f412127c07294791031d75def85def86615a4d2f35097b75d363d435b1871 845c2f10206a26287165c243e821e9c721aca8523f6e6e76e7f75b474970436f ea5bc471eaf6a9eb7ec56d14fc60b09776761119cb9b8170bfe4d749ce2e04d0 87e0eecd031e5b84b17243ce13956edf5c022f1e00aecee5d227ea495c28091d 0e8412bd8a7f564863bd4839fe50a5d220f06510e9e97bfeea2e8e49585e06e9 e370a78f168b50023752460232e791cf495913464ff08d20577fe50377b4b166 e8bf2872771bd294db8c4ba66bc9ded0ed5b046e98f4c15a3df8bcfca0c2534c b37bf7f956a9c3834890a15a485c2645dcee4ac8bb4482f91b61a1ae21d00806 4d0885b74d6179e8efe04bd75cacf621426ec90fa50932ca5ee3797a7bea6740 b0d24296dcb0b150b74306e82b677fd6430153ae6d4d2b7eb3e1fa94dbabcdd8 7f6bd6a4808082315a5fca50e21646e23e1dc5999beff683eb8e015ec3b6f6d3 4ca42f002f213c84f07969b0efa01a879550c9e1f9e35676ee1109a0d070bfb3 ba860b6aac686603ca017d767f149362c31a73d2cd5c3589cb7958635a69526a 3c7b8898cca48450b6d3e813d7875a24a292bf0f34adc6200e69e0c62972990a 11 3bfeef80cb53828fb6f87b9134ce1bd6cb93d6516dde5a0a20ff3c864b8dfa038f93e52e1c42dce46481c96e20964a3af859502a0aa11e8b4bae549d947d9c0adf0db0155ab1b5c39effe5b160e42fc972cba717d7da233c62f65e35582da30d334d34521e5bb0aab711dfebe4ba231f07ff30a9a8c9ec81e3beeff414df980e9dafc262bcaa3f5bdc6a076080d6f2424499939c4854fe3df848719facb44802499490807fb70f83280396d7797a9eda4c574b507e052b92121b0d2c938ce40e16e066b12ef86840ecc14e7c2501b617d682fd855241ab630df8441b7aae8b0935eb92991249446807df71b1f34fc23a004a799f4989604847c01fa59a74630eab214ef045c1952d49ae48b8a61cd48b498459aba83420003943f128ed32d70af12ee4d71a0b08dee149833871b4eaa734dd0c31968431dc5d219f46ed5e97076605c0271654b9f42a1b8bedf783fcc04418fc692708dbb88eb8e4be2865b808dd67e2355f88b72315d86165f7f09c76862e14761dfc1770d32908e8b3abbf042ac2c2aa0e2aeadf6633f49659f524bd2d001e71bf6c7f73e76a5e6f895ebc0bc258c05aa658cd42ac1951603ca336ddc5db7022ffdc3f1d11c4b48bf4a509070c52f62196ea01c29e8b8245a78ee0d5a4ae47abea155acb67e3d6934aae1b0c86cf3d06002a87fddab9a42640c441ee7a19348d8ffa4bea7b04d7dbc68be408ac8acc6ba3c675dfe30f6f0e448516670d2832d3cfdc9f7f019851ab8f920e0b151f9bdf28f716e38d3a73a5626a57c10ccef1396e74bda41cc545c3773a8108b5069c24d678becda68e501738106eab1e970d6fb68e1f5796dbd849cf9f950c2d0ac3e3dfc9732c74606b4cbec05f7070e0f91b15fd11029732f3e12eedd60e853b48557c86938d853a9fd2b8ab7f23c06dce0b451a36a71da8e2820a3d3102af6a81f228521763c8c41a9c3e59d7188858ac6f4715c7bb964ab89cdf376b088a50a0211c598b9769b89b308767fcd417c1f62de906ae1c3d7006aec9fd2705964637c40fc83c12269e6de76da42d87d5589b2b2479d0e1d8aca6d613b52b028aab1c4d5a56a2106864bac7ba1dd0ea32826cc06eb69969f4a7882eef59e00d0efa9595f6c89b7915b1d5ac743b943d23311da69363e030e11f271542ec360fe6031fc7de94e3ef738906602b2160126d8ddca00810e7f2fc964aa83fec190a39a78ea9999912e03926ee55c244e4f43e3e03f182fdf062e26e8788a06ed00f21cc3dbe8f1f5860012a2de12730bbedd99b670974a7b2ce0664ec5a5738c10e5e1492beef70357e34b3c5ab9997d945face86d5fbf5b4ac887ca772c3c0fb041e5f744787d53b18828ed4df1308c32db20a30739abb60ec321eca14d8b1fd072d60b560735826c80df1ca9cfce7f5ac03045851adcb907234c4f4f40bba8908352eaf9c5f714e2d5fc59aedddc4bd04224c15934f84c316fb07e31ca2fb8e0b69d776964a882a2e1ad8f6cdf75c20bc914c653f9b2677d99b875cc431d8370c03a8445ddea816a8c39f12639d10782ebfa96f2a0b966ddc25aee60f664a750b5ad7b7199d394239c6f57cf2c3bd6a087091ee0e21b28a5116d23d7368f4bb0d1036858942d93de4018b0b9a2fcccf39cd44e5fc512a08c2b194ed5349f9c40727c2f087fcd3e74a93145a1142a4949a0217b8216169e9c9e8e1d849681d8506f56db206547488a34f23cf53609b89a8a91494ad1158ab7c607e692c8dddb801c7b4bd192d512e311ed9460c351576d5f902761ac97944860fb377fd323f8a083ffdbcd7e72de3931ec8571f21e037620460a1521f245a2370570bc4ea777d0bafb0483fdbe797450f76314ffbf6196d2e563c7de4a48f1603eb56b7e68dd50fb4639285c8e048c5ab62831f176e4dd59c2a7b909915732ebd0230afa3b06e0d8fc3958bc372b17acf891f64af396eb6c82f834525f3646bee3a5ec68dd1750f9caabca69e6d9c1c55f0056ae3c4dabd0e8417a76d28a5d3eb5ac374b97106018616ef2e1e5b9279c5df57e5f6000cadedbc3fcb2acfeed493a7c4567d95120aa50342c081e94d35fd3718e535213e934553da750f605adeffbdbf977bf68e0bbb45537a3e627bf1f341cde6850c9e2bb287cca2b0241d290d35f88f89d2020dcf26e61ac8c1ff9e3140d29bc1d97600ca6cdba39cfd7b1d6eb5ec956736b106e8eb29c18761d65f9c110ed666804aa58b0a1475858168d176b4a1373a69880e1a0761d134d15eab55adbc1d0a5402ed5a34dbe4253b96ecb3cfc3e9b75cc90850d5e6e64243e1a15d1517ce935f12ec36b950297727ed0a63b81279bbc709033de812fc46fd2ef6b3baffbe863bf86d9b04b9576d4cb129c0bcd4fd5dd12d0564e492f5354104c8219607265b3b726497e56626fc718580bf2d48fef36bba062135e38247e8c7f878b1541e36e61b40f5471b230e7f4e0af7d193b31ded9207697dea38995b1cf24ad164ab40888ea22bb9eec3ec32dc422ebdb526553bec07045f6eed0632a34e3fabd7c30d6fbda90a0c01097d1a620cbd128b7c0aae8e07691a52c2593990225f1939140be7004b14c08ed7f17f9844ed3cba94bdde3d0ec2c421d6f0655d78397aafa286bdf99c68da90f8e32756d4eb88968d18ea8d0d669efaae5c128d53767c7d4ea85b72b4f9c65055059df07ec5f53f21349a1d09e60a7f676bd5ed42093e0587dc541b7f7c8787f33cbbba1ef1deb13082f5200daacbf4e44ba9704eed70f6400f47dd37d9f40707c0f32e2a29c9c9327c38e30026c2f74b5da6538aee15e755ab67c1c166245208f5cb727d570d119cc75c1506f93901c18828580c1c0221f55abf54777634a805740dca19de85fa1cd1a3950d076013d607860be5d4d976bb2cb8dd32636ee2b063c27bc6a78971548b3205005ae3403fc3d1fb2d2dc61c50eee70c761bbfec992dd3999b1977afa5a784960e29f019fadd4e0784c1504bf091d562f5b2c00311e314900afc3be0049c6d0c0daaf86d608472935792e5090bd1a0b9a4494c4a51b4e667e8cab00f1a80253a0485eded6e281f27f6ff99ddcca80e4e2b8fef453ad36bcc77a2cd4e708d6ac309e7da309dec6d73084edb412ea542cf765d4ba789de2d6041b6d78a1bfee4bc0a5a42a5ad72470bd572f2b46be3ffc1ec364744164733e23ca7b5573b4edbc402719b269dec1aa4869aff26901c7db49ad157d256ddd2dfae8b6ce33f57bdf600ac1fdd883fa43e88f04ae3e5e4b6a0e8722aa22216a00cb44d1102a131b0d30304e6768b1cabca571c4b9c7e44f2c2cbe37418b419ae0d52df30c05c51ca430e89747af44b213e4081635234f4eb05fb76a9b8b66ea817282d7220aedb6c4e0a8e92b97a10ae916965b856a11070034a596e434933713d226b3fa626781d0208193c2a3a0d1d5f7ff57b5d102a16463a0929369f56b999a5cc42236bee16710ad2713d5458ccf090f5d52c3e94b2ac228c63928b5afd0b80a1db75c5a31caf0944c7c57a5eca3e7a61e221352f31af52f21fe66a5925b759212437f129f81809d74c13519c40914a90e422a5f1f105e2ccf2d0bd6cb05425bcef93b99c11560a4a77b20886c182dd8179b14bd707ee43615dfd2da94075e043ffcd54560cb2057a4658e8da8861c49e8a3488f16e7e24898119e24e91af568ec0cc3ee572a10a6825549e2ac7042bf1d6c43eed8a7310f23da10a13307233d594311554050d0f1c902cc5bd06f5480b725e145924288aa8f229259e5e1ef5bd3ad8a4f320830a9bdf3066b7192f58fdbd3f4a7563f9c6dbf5cdd511c40ad0fec445df75021e009594c3a3be846128bc6804c389c0c6ae5d5605656f2b55d2e244fce9e7e3580afced95bc026b9c642dc3805abc67e08db4117b374335000419fd5d0474b0020bd80f96ca6cb03024ad4d831e11a3f2a42a5f24e3ec59d0572054960cbac16e0102b872467cd03ce84d3cbe2634cf3d4d756836f41f415e67b5be299adb0d8002564d180a05bba67ad5fc5acf720cc7d3f549f3ade9c4f4a4fcd27609bae2420c5ba9a94ab02666dc1ade644d4e7d0c98d8d6a5ff77d4a8e539cd95badc0c30027bae1043899df21cdfdb613f804a6d1eb02c9eb25fd01aa3bda83edae5c3350a751219e141a25f481cc74b9fee0a854c5700ea695e57a0ffa24e2adf97470b02560d5d4158e313e77b6e4f05f326f5fa6dea5b0db62843e6f5b312c460f53707c487476311edb9fe09ba78302b993b21840872a7b08554df0c6095b0f0635407c2c2a2c068a2c5131ce72db505e1cadbfac426e898a45930e15a51b72c87c606676dbbe9f0d9c7f048cf96a8adfc71d62931f799faf49557c25088b76ff989027610a9c20fea14a186cd25f7afda596dff08da3e84c19494f2988ac80af5e408f094d37d013aad639c6c577a5d5a029ac103680b389cceda16a6dc8467d3720447a00bbfadfb8ca085e35edf26e40aecad699de753852f63b94b6e0c5109640aa67c5723a9f8eacf67410a3e6607e154003921fe153b1bbdfb658f4776f6f8012d1dc06e73309d5e63b2b8bd0e67b130cf4c53cd30a55df6e48f6bd5053d09027fa41a8e2539ec40dcd42f719031953a733a4920a2048a60060cb0864864fa059b58bf9107fc09719c38715ef930622e0c203aba5abbfad4566657029493510e4894f0856d18c0fdda9b22b89087a2aef01d308c3efbd056ef1b058af94ac405d83ad643cccd58a0d56dd914acebcf9ff1336bb4da48b4a6d8113ae67b8e770be8397070b154856d0716d85a101518c128207f47a29f6743345a9534a15e6f07b9b55a090c7299d0688d98756ac1659dc5202bc724f734fafeadaa1edbb9e201e77b59d25c04e6bc16431d0596293e1c6ac6af4b79c4e4244c245cd68aefb105512712bd8a8411431fe0d6f517c88be497c166f4be56fdabb7fd5ab33999550d4cd01cafbd35138ec19f5f8545ba0aa80981794f3568d129c4508a098f3e9f034d1a79e86150ed4fcd7db1c56114abc8e28e6a9fc27ba5d74ea89bd3a1aacf0a72491cb8c643d1a9ccf101a696df0d09b3edfeb3a0fc33c9efdbc91ef328b800bf398303bb9c56c0e92d924e608cc831f84e9aa43f0cf06076f2866560c4ee02f1c3bf7a6a1b4155462af6670ad0a33c7cb4abce3a0e8498a7430e49886f2c09061ea995ebfb435707dc398d61dff7752d67b3263e7710ef8e75cc82f050720c51e3db5319f4362f0bdb83b21d7a707cad2ab2eb960857839dab616a1ef20c09b5cb474ee475a87bf799e60d1407b7ae97b674908407b0e360e1b9167929680bead41e0bbaf52e05f8b0f2752e22dfc45ca691f21ef63d7fe227f9cdc973bf0b54ce4a2477e93dc3cc6902fa033fd7e77436a6d37a39631a1a48f27b7e76030bde4e500b60eced5381ef1a8ead1971d0e55b1fa2db02b9100385e13f219eff0649c0600ef9dd891e61dc9a4f0b2dc9215c0fd1d3b960f1b9ba83f29b5c21eb00 +generate_ring_signature b3138e0867234c3558f51a772fcedc478ba43e822df36bee23108f9a69095dfa 061e567153185c51b8720a9a6eabbd7594d45cd6dcf85e697ad5166ebbb96240 1 857fef38975ea10961cc7dcb9bd6ae58ec4cc7648f022e76422356840883a7ce 2b9a136bfec8fe6c51d7bd5d8b6b9a19962c30008d63d341216bc3db7a0ae608 0 c9056805561dc906195b2421ccb4da08e7287c0b61de3aa35ff9d38ade53cf0d64f9988e2d9d2ae5762449f96bc95af76012ccb1adc3034ab44fa1f0d960150e +generate_ring_signature 74bb98674a3788150d3e4d5da53562d0c6744c9ae51add341c4022ce641d57e2 30274ba0dbbfd23730185f82ce12fc07fa0efa0ad291c656e80a53f8ffed148d 247 a403f6e67914326bfaa8bba21f34fbcdff502e38f41468ad8fedfa5d351e2682 654ddddbe5c7256a585a864c203358dc0a2c16f7727378da6e12954d21ee3c4e 3844b35fba3b52f2e28b738e181c6e68ea94722a6a2c0c225b12350ac3325268 62ae4fa6f7eb06636000be49b96476e10943fd700d8a2256cd84ddf8a38f2bfd 6e98f0c507cca35d3f44301571f7b2c27af89a37e76eaec9678e9e5c833d447a c4e2d9c202919f10604d96f29979d18adbe0f237c8af39c119a8838460fd3797 f3c26e70db554ec9701b499407ffacd44b84205ad080cefa1ee6f7eedc29bf15 672c9cd2587a839464a74f3f74bdb537ae0f6420a99e443c925c754944c23c37 39b60debc8e51bbf65519dd5e3eb402097ec15c615b0e5af8174b9a5b8268116 597c79069a799836842c189511016dcc23f4e50cc01cb18df97f8f7669257ab9 35ee4bbf832884ba2ecace7bd649c403344bb01c48ee5d5eda52920b7a3a8f12 6c8ea85a97f654508034f118c4004acb67867b4f39f79acd6bf3dfe60185db02 a3d9a816893dfaff1eda3cea858107d080d0735405104a74e14024f2b9820737 c3d64852844d331c3c701169ed282d678a4a5672260cf35a288d6e31f2633b34 469783b262d0ca327cecad0bc12ed4ee7c596d3d7aaed5cb3d5f4311bca72bc9 5099fbe24f2a0dfd68c08cea2563e6f8d58aa9a7484b0f1b7e710f3e1e81987d efe1c911784130fa43a9a5dda599fe4b1110468c88df1be612e354e8a1d9eda8 214f5ac3cf0be8e4e9ac07241c7145084276bf172faa3072a2c88173ce2aeece de942b0c9d428ce4dd38ac8813bd25e076008dab84802a14db381f715f77c1c0 5f494e710ebe0907bcb5ecf68ef92ffb1357bf267a64370b4371350c7d0e86cc b1f49f078346e6ec1f645dbcea34a7a7d8bb34ae87600a4759b11226ef611813 72df722b5f973d3db71a6c4175c9fc85862c156b950f70232e3623f5a735f935 172407763393a65b9ce29bacd3f56e7b0f800911e0575302f827dc49eb6f1367 cd858eb00ca36272048a6e06b1158387395ea2aa192ed26dcd31d4239cd8c83e 8e30909dfb51e13a057796036a24ce1f95591156cf4bd80b27fd90fd7877a1e5 dd902962aca46b8951f260d1553301e9229a9e931fa4b105127cdddd2a13ed7c 9dbd79fa494fc010088344afecb297b7d94d990fad6d60c775d7893a48744780 6f3804d666367575ccd9f268db307b92da304232ef5aaeccf210f5fd715c19d7 57a840d14359101352b093a5d0a8a593081b07f573dfd46e6b705c63a373b341 668b74e77bd56e05777364e683bd3401f7f1ba5632e1568a9ad93f29a01be881 5f6ad3407b8e4f0537ded3222190a7a1909bc318f2d992899652a21f150a8228 4ffecefa18ace74606be81d17ba1ba6c6fcbc26ee027b45fb733ee756d358739 9b782963bd3399d638ff6855892c5bf401899c6bbb4ef43cb02242098162aea6 9792cf572e50ef983fc8e0eae66188599193612124903940765a9137546d073b 56fe9a77768fd9eb84015ae63e338f300b44fdd575ef90c27f1d9fa05d18dafb 65bef043380098fefe47a6c2fa53befd409be2037c15fe1166ab4966c60df3a9 6ef81de3df9f05063fd86581e60fa997d628d54974a0c9875cd3f717083650b0 4eece009726c988cba999e43e4b51c39d46672666f45fb62a1d28a6eb25c8871 8cee112fa65abfa6fb47c257f27871905ba5635b064992071e07ae4cbd4275b3 e199ca84c3239da8e591e5c61d4883484ed82ae677cfa3f132c52af9d1a6f2b5 482c07bc110b76ce4fe5da99a0c35f2ea760157cac1611e5a876b4db4a6a8634 b57ebb719e2740d3d2f44e3f224409d70e2b23e1b5b26dee15435b76b040606c cc8ff8b008623cfe629bcb27a96f308ed6fee3a880635d54baf86f7924b5e863 8f24857f82e81b2936dafda18811a0a87601cd1d8a221999b2f823ae0c8b88c4 82877a20e099470ae59f041b697661358bda8658412496a2268d48fde9f0d15c d72b23513a406e23395a12619013254a213623e518fce1cb64fbf4e5ec030f1c 7c08e14836c5766657635c78e742dd5ce7e3812b000594353787a4296af2560d 98f2027eecae4668758af44599cfd9d8829eec4119e3261b6d5c0d0479735175 b901a74dfc94be80efa7affd7a3a87a323083e5cccf8cf63cba2c5e873a73bc8 73b178a6d1bb560c0aff370a33bf86be5283f29b72113c13dca86426f10b8637 134def8bddb038bd0b74253ded0996b8510d5332343f702b41b3fc002a679bec 68b676e2c52cd2867cbc26cf20948da146f6521580129b238fa8b2a902e9ba5a 2b6898dcfcf4ca56fda6eac7d77cbff688d6d23609e813d4033ae64ae7272254 b51f91e495bdb4d5e5d74f365dc86d4fbd749aaae64093219d95bf730a715e63 be5dba9f0a4f8192fb18625eed829aa8d39eb704a78ebd318c610ad6a5afe3d8 b359c1da6763f2a37cbc3034ab10748f406c8c9e722b5fdb3928f064a9a0c3b3 c88c1611506ecdb3dc532aa2f6847c52aa77bfaef0a24d1f6ef71dfd0599fef7 43ce1e104f09c1f160ea39feccb154fa6c6c2567c5e9527e9fe6b72f323b355c c5de2f6a948340bf7cc0a4991d84bb74804fd947680e191e106c1ab781ecac4a 358e32ff328975270534c97d394bc309c09bd983cb481673d9f07e3a6da547cc 165b4f8c6f8a2b380ab29e7794ae2a0dec22d85dca535c560cf8307fa6fed40c 00cbfca81c8db9332e567f20691c8e32e8ccefcd741a4b421d73a0a68aa05b98 d10dc519c4a136f84a394be6257ee9cb193f30cf949f2c8ca6a9ac01f80b6579 48539264cacfc3b2c1b143fc9c88289c3b657944bfe8794d6527760e078c19d1 55b10be77693872d780ee7ef9153fb61041d3cd2a0f2ae2b8d8d77f7119e8676 be2437e2d31cf795c9ba836d2ce1d254fa10438354de3531e53f9872af881e6e 2a9b3e804b01e4c0d96835d381af39a6bfaa11f79a4c2253f0ed7cf41b84cb8d b390fac586c24a80ac6eebdaeb4fd2346e0dd006e9591f86b58350e3d0e4500e c6822d552fe7e33de2c5773df077e7c7e0797556fd5018ddab3fdba5c3e1e7cd e0b14fa740a4d6cd82968fe83a8b13a76a9991bbc60994f7faa321b64ec658f3 c405f632fa441b0ccb76360ca7f38e8b488a019b2b1cf55960631a35e1652f71 9a776272863884e9089e93ccbf31288256c74984e4b719df8c3b3532dd469a4d acdd8dd10510555a42ed396aed2dad9e12a79fc3fe1276bedcd094e28acc3f4c 0e6a79ebe6a0e061920646c776e87a132a18aa8700ab4505c2adbc51096dc4e6 b527de6b9ab5ad453463de96a208ada49f7a046ce01e65e9920b714c8d1b3447 a8e0a97fe6c58bc66eb25d847e1df8212c011a093cd1ba349917c669eade71d7 ed6a69231d476b41ee00aac97f44ec75c814eab267a1729770ea43c9a47fa4d4 a0cdbd9a9bc3f2969731e8523137c7e893e428742f382edf96542605e225339a 940960857f98ad65e7e285f7d0a9ce418a01740607ad4f6ef9ce2e5ed6649519 7a13ff83121244b3e8e3d7f3726e38758a8c7b5018971d47784a536b618aa59b a67947c638c18de0f82472a8b7fd64443f85cea0b57d25c8aefa693062d4b811 5a95c5b870a0d1e299dd52d8fb02dc82feda7bf5a4f8a29b42c6f105adb4b7c2 b8c7e68af5ecde11edcbd9bfbe5b9b8353154994fe4d779ab7b6ffb6612af096 79fb247f26b61bc4788de3a1b929df284db7233ebc9a2612a3b872c8511575c2 baf07f5f0c96a64ed46b6fdf97b992a67943be0e2696ad04372b94be10bb5ced f03903d29eef2c57299863183501bd241cb7293f4f8bf25d85fbe7cb87df4180 b29d3152257bd49863ff65d73a780b6d02e04939f6d1d2066cefed1dfbcb4c41 603d23520673678588a96826af16818d93e9b3fcfa198fab96946c28cbfbc20e 77e8347e86db8d594add1ed63b772725bc45e39721cdb5ccf330355fea7084f6 c3aa47e0f853b1747e2cfbeeb43d3596f663bced0f4f37d10e8000b00a34a12b 324680e254e514becb2a2b28fad2607fdd7bc3d71988730d9980f0546dfb626d e0154406b889f451933e2de614414fe7103705126f8be5ca6a33b9f344eb8319 050768cc61cc1295cf1dab569995b5cf8606d21ff5f5a597197788cb0e29c6c3 b2b1eedd9eb60471ead57c563408f856a47659c68101ec9e01160bbac3e5b0d4 3f2d5fb2382d3ed0655c2f96886876fe9fdbaa6418a7aa2711f31dc99e22f7ca 59aebaae24d3ba8b1d16a942a73b716a3f2d078eefe6ae18fa5b43d29ea277bc 36e9f2bfff384363bce553077e55f3b76aba4040ec72199fa64de6277b81d5b8 9ce2fe210fce4473cc3a828e0878c6e8b009cbc2316ee5c824a2a25f0e990c64 2a6ce9e3f0423e54b5f19e5d6c072b1f9516a1f09ea035c7e0b20de1a5c08a18 c434226bd5861b2542f9e836dc3be2567b8ee03bc8498d787b177c5576cddfce 2c776cfbfd6c070affd9cbb3398d2dba436d5f65dc6f7e3cef0224175e095c49 58cefabc46e8624cddf09ba6457e1df254fb0c3932c679d223d4591c4b34bdf7 5d07894c204d56bf15a75ce4bb6fe288374cf8712f14c53251b4bbb5d8f7e31a 2dde1c6ccbff876dca2765c6a5bd0f4437180eb79fec903cfd1e61f141a77c3b 0889d022b3294a29b23ee2d16e514ec9934f5c1aee674f90c6fb645013fe8a49 521ee1be221b49242c3d6c410f7d437c71dfac857ca28b0cea2961130fc6cf4b 63cb54741d87537ea7a2aa06a31876ee0e157d87659ded2a274e8f87f1c4fa48 a47a24ae88308dba31f669398ec79bea7fb2d5dd3d4a4be45508e5f21de304e0 98de0d6d8a365c314aadb839f8712bb935edc79a562cfeedc24bfa37f6843528 f7b8204342f6146f95e4c83da46a60243b02e3b146b12c8eb744dc2cb5ff4067 e12337150f2cbd578fff6e7bb792f7d0e1a7b1e77bb7ad20d4b4638239b091e4 337a8fb61d5930c9359903f30b462e155b01e9a064d286a230069ad39fc74be3 24c63c7f72e71053f512a3daafc5b9cdd888992b8fa3aa39dc49908beffda245 35533b20806f302ed029ceb0498958c9177776dfdda304d2b3a8eb68404ce5d3 554181eb9a435d15c34f07c695cc6137f7c73a515db8995ca6c2a20c33971743 5f9dde78ae700be3e45deaacc7f10e276b89763f0f3ac7bee61dd23309b8ee64 fca50dd9762d12fb9570f499995388f26480036646699e0a78ed770d13c9d37c 80b55e55e11faa0446ad75c66d637d949a952f93613f78c176763dc21ef79ee9 0a438ed805bb96256e0875edb59baaa4b0532cdae9523701e8f84c05379f9be9 b9baec6b99b5deaf248085345f58b26de013383bb89789901f60dd4f481ffc14 39809db7ca482bd8457ec6b83052458c84789fff4dc323fe13bc17997f99ebde 35ed28f1883216211d0ce7f5db608bf7043f4eb541402664cf5486c42e631667 facc5d155cc590e396f886cf79078589d18aaf9bf828e44741b8c06e2b24d320 7bbe3b6b8924575bd24a3109d8764da6536b6b807d420ad4bddba1d8d7931958 477b59e7fd404e9e445d95f493742d3e5211172843c17dbf658d47de51ab744b 8686059e2add14f46034ae74bf40594393881234a9320e2f216fcd53072b4643 27d03a502dcd7d0ee13d95354dab7cafa16eb065c83c588cfa26c3cedc9e824f b367f7adf50705e47051f149d6ef88c1c1562edd112079634920c20a3be27356 ea6e1d37c2c1bc6cfc2cbe6bdf388e492a24a7311b179b4c0530a2ed1c898a90 d05f32f294ecc00411dc22f150d18f05cf1b1cc3edb1bd3e6cc3c5a34ddc0c55 15fa76d2417e7eb34f02147b77e655c4361b85f10398a8848a132cab3d0f1f67 aaa5752c00a761e7096f44a99b5b85cdbf0814d990e1bf464252d26377ebd3e0 57b46247f8ad6fe4e1b3d9bfcd7d399f6d2fcc23538b60e8ef68760c59ea0a46 2c01646d46e443b2b7cf5e198472b8cf4959f441894ab692dd06477ec04f9378 f10864e9ce50d265bce1e48914925dce0139df542ebbbae763b96da0ef164187 c89cf02fb9c510521a526a2513b0f8111a424fd59e3421dd5e38f90b1959833b 46e95633ffc0b37e35913ca37abcef40df5ddf594925641123e20add8795fcd5 f057673818129fa92911593bb3acb1fb167c4ab5958b9d1260784554f49c9c01 f92fa09778304db71d00476a72f6f5d378360e94dc0643d5d9e13c9a686f28b4 03afce2dd46f4a28fad13feeafa2d0d1f3493ec9b38ae3996b8679730922baa5 3a6b7a70c48c9d4cd48cbe35f9c9b0c51e818828fe92f0cf3e0b9a779f923eb9 89c223a5a2ae23e5229b483efc1a83c80b7c906ec967d93c842a1817e27c5812 c522c194a01b2e9da5627fb192d7bddaffa9b80a407b62661e555e5d7cea819a e42f251c8bb060d4d9643ab1b86da07c752b36d0feb4d30879ebb38dfdd3a5ff 95582f103ce1600ec70cd90c7b2adcc0827a726915624aedc60ea3841f571ff9 9f1379621804e9f1cc25fe052a7179323bf49ad564ed2d7aa9764b87e9e71f25 60efa3252a4b4351132735a4d6cb6179c5c6a299221ae12ecd048184e44d66df aceca5bfcc73f630fb0c9b508ee9160a633879067365a7fb89dbd903c6ac087c ed978794c5a12873734d5651f7b4c467bfd449dd430a85290687f18ae58eb67b 06a678eb271372376889956df606f75fa868a4e3095ae73639b86ab050e43641 5d4ec3d1f75add15e33d30436591fe767e3ebeaeffb9678a53031830330b13b9 3e79237edb42aa8711b9a879f3c00ecd311145940a0b696bcc81741ae9c978f8 d8af90538bd3a91f2468d9605e712968daca18422c5f3adbcc2699a59324190e 814472cd35697c360a680d47ac0f92c2aaba3ccee5bca0d818a06519f0fd66c9 19b8186ff714970a3720b0eb845c342f37a5aec5ccc0446f702dc1f049fa0e63 b4daf3fe8489a7d1fb0a58741aea698ecedd9d6bed0fd1cdd9f8fcfc9cd56d18 e690f2ce911068a288e248fb586821f524f17724740666af025fbf779a8bf95e 111483e38dd6374f2c55a7b3ac67611e2393b39db40bc594776dab3e5a494142 7b5e74ffd8cb8d4c234854d7aa9d61b27325d0ce51b842ed810d363f9b585317 f5a434e5dba8b02eaadd95e2f7a3767d0da7f6aa87a63486fccd8e6bcfc1e9a1 e8b7276f342a64671ddf552e78275bacd5209c60c19b3dbb67dc764c2b07201b a44b93a068802acaf2c1d554e739560317cba48ab0809be85394e602c0563be0 30794b6623d0fc6d27901889e482410bc8552fde425d206d5f2b05845fa6c7aa f288e352d9b94d1e4afe4aded68b4e09c2b1250277467c3b0f8a6330c841c088 a2af225ecfcfead0a664f7fcc9db2cafdbe1209332eba5f90da4fe5eba4176c9 b25026d24d69e9cb73c1a3e3c37b1217f8b6a5b24acb39d002adfa9ea3202ced 9785fb37611f1258424b27c3ee661204a511b5104e357232cf69666f2a21b80e dec4136651ea173493f718708a93e2645bca125853689aee3746805821f74408 47340432811eb72dd7d14e5207b3619cdbd35f61931386dcea6343069c980896 4a0a6acdef063137b37edc572637b3647087fbef74a769c9fac0e6cfe551f40a abbe616275ff26f5e7e5989603a6d4648a626bb0b05558cc6bbe66eab33ad207 75ac5828625eb5a870a1faa2c622a5fe3eff56e629e1a0e2137e940209fcf636 371ac95e0a0d8df317851754a77fea4ea194f4f920361fcc2556a06668837c40 ee0aa04b392769d9484d1c8fface6abc0e23bc8b408c02afb680a25725301b39 2fcce02bb272381d1868a2b40f4f0205a7c84289578cec1eeca92ac9745d8d89 95aa67d2e13594267e5644c1b4434e4e19d1ed861d2e8d9bb1224ef37b668cdd e2d40f588e85c9294d43d284edf2162ac795c97365fc62ab4daec973d0efd778 56265951976f949754169c4b8a511405f96572503af306c3dbd006616fe5a01f 81b32319dcf4169d490dcaa47be32a9048125db2c57b823192f744011d5480ac ea02c55cf968a2675094c2cd7ff204ca3319b93afcf4c63552042d2790eaff84 038660ad671dd65e8d770aeb7a15aa441befd5f0c538b99b01222e3141d7a3ee f2a894210a7366fe80aa9a965ae8f59a42bc2fc36b6cd73aafdb219f51ca81fe 56d71a1f6f1530b3fb0d98807d69f9c2582909bb9ad949d9d7a7f5a4e04d4a13 6b21c8dcd7c94295d40b8418568e1bb408563ba2500235a7612d0d097cdbff24 4be1594893c6202bfae4a526ac5cfa9e9c994f5d3447223ce33c975d025840f8 004421e203ace8f4abb4d00366b6d5fde03aff9df9c911ed23dda81d0a3a5057 e43d95b7cd3c8749e8d849bcec656be1cf2ec248ee507b495b904fe8daeb1615 8cce6f5a756f3635514f3a06a55ec803497425ac6c0009033d47688042a0daaf 8bd88ae791b60a6022f1511d4f230750b72dc03bd4566bebbc59e45950639f88 af05f26323e19be0f8880aef467ff73a53691c6af1eb2903589f57475a3e47a4 3610e7442b3279115c9071a77b5d95fbc1b3d85e55d2d032a64efed7fb76857d b45521aeaa79061c7f071fd11087f193d999f9fe6e539e7d011f15ef2e087cb2 a64eefe5c6cf876e61f9fb2f547f25e0d91c08fe8d7778cb4d0a9b7d0f86c709 aaf2a91b5d2173db0c6a9631eae2e266760993438f3f14fafc9c6b50b3c465af 3aa65906b0f41c27e2c2283a2d5bb851d1f2bb87aba98c426c65fe6331aa1747 7f80ddbe121db6dcf953942a0fe8b16ccef7dcad3ccad14efe8cd9f6ae730dc5 754e81ecf5de1900bb1ad84034521d35790628da15543008501fe242178637cb cc2080c1a95393f9306e2a2aac06c5d944b21cf224a2f44cc9eba8f47ef430af 1249868f4136367787608e85f529cada51c1dfcfed6b47b256221f8d87ff68b6 858d613e882c7903e9d284caa85c0ffc21aa4033546851ae49a839d49e4303c7 e14cb2bd293a87ecb0da367d39b2ee091ddc6ed881e0885335896d45b1113ad8 a36637a3ff0eed5bdadf5ba21b7e0e12d8f9725e09900a35eedf8619f4972615 6598ff731443df31b1ce9647fa9d7aec5c6ba4769df3ce70e9d26c0f895d1b76 c56b883901b12481c7e484988e80e10631d405d58a4d572685cf89f0e9f38e63 1d43a705d83007facae9d526646f8911ededde05ca1086e6fc5cfc7698030793 e6fb6ffb82e288ee772566cd0f49f78bd7d745cfedad9764fe1efdbbc8a2d128 8d05d07915f5bdca2efc543af9d5508212d76d263dd91a5018a9b515dbae3205 3a32c1043272ca775712a82759801f63a8b4ac8c957fdc19b4a75e189d932a41 e35fa5afb3e406e69dfd96b743c9dc6d7199fb561ac781eef1c3d6f357b57e50 910eae3308e3a2148e12bf6dbe9368b71749fabf235939dc30dd7bbdf2947021 a77bb6cb1c0886706ce63107b3bb3d79b996786b1ffeef53fb39f414e9d05a40 24f7fea5cd219e0b41cbb5e4d79fe987d88c39b398ac1900a5886d80339e9815 bcee0df70d5ffa2b72152a1318db31b4725fdf7173530c30fcf6c712da498f95 668b5ff9db612ba5c1457424864b97501041867df9ca0b4ebb3ce0a469e8c31b 8bcb6af39a9ae8123778a6559ed410429409247ba04a66c6ec71791cfaa24867 b3ab693e303fab39b84d0e32fa894d2538c5994393e8a9f0ed94448c307ce7b6 de867bedd70b78baf93f318d0d8f397a375c843f365748cf5bb7d05b0083c099 b1b91eb17a6cfe9f36dde19aaae4e221ff3c8521c0896ff2d0f524222439bab1 5e2a0301aaaa149d63dffee51929294c3332872e261f46e4534ec1f1049368ac 40bdef67187f90f2799e7341429e9c6c48c646db6a93aac1114b458d4f0d4d39 4d1e51a33d80139d894994137a9ddbe8551637bf6f8c3ea92320a774976851f4 a33b7f0a88941a7ee2d606a7ba04fa6d39777ddb5c464329e185f595af6e891c b45041025619bfa5815080372a4d4ecac279ca1a743d61cd79267493311a0403 3a595902e5b3bf97cb277bb7fd680d1cca9e1e2e56feb7fc92c0fb0d82511c3b d12e04b8b9ac08f579b08ebef03b3b2f499c0c481c7aa7e48c55e32377c4586f 2c17dddbdf1e3edc7981084ba2066773bf0e2d286ef641ba5a98b87f5b4a87a1 d7a40a08f886f4748be1d5a74f5e87419abc43fa5196014d29cf02a901db063e 6e103741cd4ecc3815d29f86823ecfb0c074e013629a724f990c738e7011e6a1 6b463971ba91309637ccbf1882b8995a8531ce7f2556c10f3e502349808525f6 330db88cb273e51f5962be425e0ead6bfafd8bee6f9ae55b9447c31ac3ba64fb 1835bb6703fa62f3aa26c9367400c90418939c472da0dad23064b04b39864f9f 3092db4a20be5ab24fb4bf6872df90853062ed00735acb2bd1e54a29d0c4e260 9603ab2f0b837a5eb5e9aa69a11b505956ea208131e89a473132764e28b81b6f 6b299ca100f618781234b8aca6e1df18ea561deb18e74c9201bc27153f1724a1 35c3f4b6759ad57a024861beb21af5c1f9478a0599af7960d42395c9400cf7f9 fe5ddb09debbe4fc8033233a9eceeb6fa54e94961fdceb19a2692d56fb8d0347 9e3361fe41ad4385ebc2c9d5da927613b030870548e5f89b1a7e88f0ac04b232 eb738a92c1c1b7896da3f614299d10916ddf145f3239afc5b8a2486fc255abb8 914944d4afe219ec23122f9ee19e0281bbc70ce899d55274d97c1ab3ae866a9f 081860bd64b3de9e06b644e7d84df65d2dd274285427355a8726b362183a1d25 ecc627f1183f70be9abd87ed541a373284b51b8ae055c3ca08221633785e7de1 ddafda7db7735101e0a03370fb117d8898f3de88deba4f0c10764150b80e2224 ded32c36ffde89e98d0d35cd36a4077e4f3c0db6be090c5076b62d8fe0c6e247 0bfd324805e63f3354dc287710a4d7cd02e027bd520e3d80ecce1793caab13e3 2836e4e55d54a8f82318bde886c09e7971b5640b039b81e8d51abc155703b177 aad34f9804be639c1408923f778f52aba076d4eda87800ac38652c6752aaeab7 1c146ca32e0bb7abf540688b6d4e25dfcf0267b51f5683e4a0dfaab357a55478 b4b0dbc49494a51fb353c7b59da3676abf267284625f5043484f1ffc3806b700 15 7d231f3c8385ba6f45d6df89187fcdb305de974f9e10af43b7fefb7051ec88073adc61031b5f4da569de4b01a33980b9a8f8ca918c29a5dc52e04fc2e633570647e0b6fa300a2e6b9b3c508154ee95cce07f05706c7e3fcd3090f05c29976608efd15881023abd9e7c949134be1b213ea0a2f84b3ed0089e3959e26dd81233063fc65d105b72d55659031d72a28b89cfd17eb047ce6e3b935644597e6314820e59f04ddf9a002b9caaefc439cf66a3afabfbf7588fdc7da1d9784a7c6375880131dbe82683e52cc1e95c111755a18af4b4eaed2c107cdff2fbeb022c12add70542060b04063b3db811aafb5958cdc3a11e42ca4a4f209bd07bc5d39d28962a03ded6941f9a528d318874fde75590889ca2c20d62dc1091821e2b12e227436104c1b87ae46b6a3d1c50bbcdeb5ef4b4be7454b34ee6375483dcc8c5545a666102b1268dc5b85be9019559f88cb1545ed58b8ff337f3c7d8dcd3da65438e4ebd01bcaf90ce90a8cb925516e32924a34a4e9f8e0cbf7a94a2021837b0073d3fd0062fcd65bdab92b694005e9da0d1b5dd765637e7aaa8bc9e198dc877a18f29d1054f3167141781235ee5092db5b49402284f466eda233afc86bf7f30f3848bc40042ffa2bfb11c71719104bab42a40a29c6d2749e8906299252277c431a710f101d1a63e60d39c08b532fd08a81902acce4f328608f016e745e4c0ee36188ad1031aa0f45fc1a151ce5454ea222ed2e618912610aa1e048d4347eec47712ae64053cd3cb28f8e958c2e8d1bb6c6001e71796b5fdc8f51dbe4dc73b4d48831adb0e609c2c1cd1447434aee3a37fb834ab2dec1ea0b2869408ac8377d0f10ab0560326f29f912bc3d8523726b289efe3e59a61d9fdf21af8429c7459655b9488ce0cdc23a4cc689eaf1797efac41e6bf5c82d946b1c2160a4e45968529f01e95690477aa17e46c94236a697a12d973b8d1c653f88b3f3c2afde6948d0f18e631d30c4d5f6e3e70bdb86003e95c689d1477c30406eea99eababf5a843466b1ae08b0a8d60b3c929eb2665cdd86f0187052c6cc879173db22b1f671d0db718473ca10266da6b7a8b4018f1cef417d910d0b7bad48d903346d908a6d4bba953b423ca00f621c7f740a09fd891aa534ae2e28f720effda033ccd39f80a91029de112b902c4bdf0a7382093d610aa4752ee1c95167db08fc0f0ee2469f1cf7d7de4d19b01920f4f8794f7d5f7dc91f8176232c257ae9fd934dbcd0f9f356eb9c678138c05c7f21e7142d1fedc3ac06bf6430f5c78c39233ce1b39ae8794ab80144258020e90f2d4962ec6d485c10f9522f1b1a337280375d58974afccc41d76ae174bb60ce43f1b41f9ae1e5f38ad4b10ae1dae743a35e04c72cc0e90ed73e98b5f44e8068186c76453c6a0d4eae16eb1ef348613e4eb30f04a2c93a669765744165ffe0ecd2a26149da18475703b83171c9a8e915e959587f0bd0069a406ebcb67d7d403a5c518e3044269ab6a4703bf69a3fa8712bd509c806896cd2ae431fbed3cc3085260f657a2e810d24235e9a03ec6f14f48d9a2dd04efb532c6e13dacfe03170d6165ab9fb3456b84284b850207c78b634fbc3f9e47af1f432199b35e2037cf04bd3f46a0ef52a7af417d72cccd106adc44b322ee1785ab095bcda3a4d2a45d0610aaa101202001066079bab1384adbeb79546d60c4246e74de2167eb87c50203fbfb2dcebf5199405bc24c77360efc7287a71ee234e263845b31ebefc7470f097afe33197e972a8436fe84f96b110657a5d2e4e13f30d090fac38fbd0ad45a05a5defbf6f761097a8674ed9777dd808618b7cc02847b6a090ad633dc5508e606c8460b3f1a562c6842cdd810b0bcc9577d2d5694fc3f0ff1392fe3009640540ed90d3b061a25fef05f379ed576d1aeeacda9a8842f0bfc61e24587e27daeda0663e0bffe0db747a1bbd0f931c0f31bb6807949ee05abe9b82d1cf03a7f3a260343dd2baf0bb47e6787b0231bbc037cbf2bcc5b3d1a8fafc70eb9d76aa9b84f0b3eb6b5e565487459c933f4f780851af650e773c5bc8a37bcea9a6ac1352b430f0db0b53e0158aea3f525abacf7344f2ca55710478e73f2349f9bd9be583a50006f67cda9877cf58245d7c288e644c0e5257b0b58612b5e5f1a2f367566f4980b6393af26ca96880078955e9adc7c1d4c77bb14c24661a04873442044415f1e0fca75f75848b4360dfeae55cb266c84260a85022549f4ffe84e93d9c91ed72808ae061c95e95758506aebe8f8f80700f1b135b77f055af43c56e29053bfc93c08516111a6a5450afad8d18ed7b9ee71f27b1c320ee06c6b4354724e95481f2502e43f14a92f01f444be9c9dc597146cfa4c2d2892917d9b68a9a3e1c25b02ec082420212faca1e8378ef8114ff8ebcd2b67e8906e28a6c72bb9f0d26a5d855103e9a3df5323370e8e8aab333087010bd196f2bc02a52c0db3d0eecb47b83b7106e96850d719d65c3c070a0d2b5e4e6e447667479b58f9b2e47315678cafd09f02092031a2cf8df93c36f4efcdc4cde48a6dc76266b42511a19a08e2ee9ab9bb064cf18f64ee5d14a3e8b96813489f0f44cfc0ce06b48b0bbb0c2098eb50c58e019f7c8d6afe54f2568c07eeddbe06eac1188a9a5a8778912bb4089902b902990c747383cd7a13f18fb1503e08cb8c54ac56bb8991ed8b96282513c0823343540d6f9fbe12f526c2c6aba727179174cb63a9c9db8ae1286efe620daff863a24a0bfbd4a26df14c858a56ce3c15e96406c2576875b8ecfff678b3883c9a57f50309b51ad9321f538728fd8abe5eaa9b6979449a8bd15a05e6ac3b3a54a2177e4805ef4e51a54d8ed714eb85ae9d36d207337be9502719f4a5109ee10efbd4696d00b02895302b739bdde38b7496e6532c7f946fda61fe0ec052d2dba7979f0a4c021e3f5429402c2f2d438d153658348b9db1fa92768b58025aaa656b5a0ceeb8071527a476eb8e8527d1b399f0084c7c064df912b0f5e51a0130b0c5ab943581053bf6e430ea696bc4fe3b235590c7c2e5cd72fc22ae0b765a7386d4c9b06db80289e24dc6519f251573a24f1639d99e2ddc4dadaf8bbb7b375d8a8723ecddaa08f3cefceb0338dde3b2e793c1824f21a89a0f33fae835c0617c37414b4edc6007bb46b66883e8d12fdf3fc5be8328d2b99515c57356165d7810c19b4b56283d06011363282230023dd26a67c78b73af6e6e90257aa409717da7cd70ed6ea6a8040452a5275aaf787ab6079bd4c33eb1fe62dea4afb601c0d99ba698e3174794095ef3b4d3bf633591d21f63dab0f995c9d90f94f6c77326828689ae61823b6502a74a7a717b422a905543ba6e20cb9bbe7746c3dccd2a99669ee6efb67f81d20f1ff5d63cddcf3a48e0731153b27fce5a4d1ca4c7a738f61f427cf22a4ce70e08f0ac3d04b27d80e701e234e518d59c840c3d38cac023a244510c60e44a8ad40aea70c0bcda31771bbb1bb303b8ff5b72eaacff0722d1701f197eb7bf490350025a516ac37a79e5eeb1a74b45ee4da771cb789dc3e1e4bae6a5b803a888bcd408c2a522763686609090456b207b9e32dd9b52dbadcc4c2f94a827d98b4360b202a40d2cec28dceee6b9a42afaae7d5a373b45da6844bd74cebb387f7ed11cfb0230549ec9c130148372b313cca0c0ff8551e59ab3d32928de7170a5e695a46101dd3beabbdb229f35ecf0b27f4427314525d5c9555cb4dee080c026df5a12be0af98358187fec0ba4675442a69e69d9e032947bc4e3c20078bcb9a382c6cfe700f1a3493b4e409b60f92802647b9ff73a7f28bba16ae6b6be6e405cc86a8bba06b8b748cbc4e53ab2e41fde6a0b5e22229ae183a9f685125f1aaec6b557758204072e6bd33d61314e0e8709f0c2ddada2080ca41b4662d238d4fecbd60539b80f088733f68d578963f3cafb584c45a02dd37516e2702c93939a067c78356d1c0616e6c2068c65d185e48cc4c156687421891bcd4314b43688dccaf83935591806458a1dd5b2b52b22b6f5acb0db3b9c30da69f11e31d5c0f1e6204bab63342c0a85f658bd93622e1c87d52b6454b1412dd49e35b4e43b0959cd4f1209f3695c0cd590487509bdc883e2042de7a74f4d05986089671998b5149c98c319d574af05c2db17713ad6f8a70e1a44646182b23ca9f1df2eb4ade81ba0735521e3dfdc09132a57d8a556d9e1428c9beee129d0d559698882968dba55702908ff4365000723c81206678b4c3db16307b0f969b269a574b800d4fe1676e4827ed979ff1b04c71d94f0730406810dbf3742b0965aaa8915cf544754194dae8067fae6a633039feb395d8cf5ed28de3d2d613b2ddce356f68960e989478d459dd3268a3d780386f95381f5fac375ac9204d1bf2a446372c209bb5ae8237430b8252439b6e40bdcf888f06d97e145c8c031dde8996f9dfe1c860fd896c2e2c4b109bf6a2f9a005659e9941bb25fb77d5467a5d257746c14ceb737deda0d5f949d2c197c881e07bb9ad1cad3e9257d41070515a1938639a1c0889d22e9635584e8b50dcbff300f7de6908bbce6471dd0fb1cd915099b968c6b9a53fbcb6d9153d7cafb4b11cd0c2e13219139a130061f6cd6229a28b47de01e39c83f9394f60374249e77e894098e7eaf51a1a7aba72210d9c85e118343089415ec5e64bad66a811d7080f5eb090c6afd4aa2e59b854cfafeb4546254804c1651505811f81e622255c023795f02584a30952fdd12cba21af1ae8de5c3e4bc94fae49fad3bd1b9e031ca20f5e9047b4ec29be0e08b96990abec80b5a04d641461a7e8e1b8438656dc080cbbce700016bdccd921325c2f17ade748e6465f4792918d463c8868525efa2dd472ec10c78d3636015ae04186e14457f9828cf8c9e36f91c74e9c53e1d2ee7140a071801df3f71c479c531cc0cfb50b605a637c4ba243853ec36a98f89c159269866f001675a6a6e0aeb30a6020387b443c74c0e6e9c9a7d46ea7a0bfa4904c3461b970e4909061ce86668277121457c00e3f67e7fbdedc86473e64517a8b26b2dc4fc05a8c854b178daff46132cbc0fe1e03e3f2779475c707144a8778f0fc900c814061c6c0354cf669897f4d106325074187a346dc4a1fb3b671e2f6fc8b895f93a09c62c42340a518fd411b47a3043cd805e1a6a0b8a9d11b82d3d20d1f4b9472801902606bff2b6d96146a51619a52a4baee56b0513e58be6375e2aa6320d7411044209942a211c663cf571298ba5772f0fa1e2c50f86364952993b339d9c48840332ad1f1e7cf38de7f4d9623d113cc31bc13c4e58af496a92b1c2fca8027fbc06677b311197643b92dbd5e0c10b91ad248656be62b142d70adaf52401c28b52017659dae4e1526642ec34c956571d11665e50fb7029bdeae41097502537ffeb05be17c9e5063c14615fee9b3e4f6633ea03c28f15a6082e433de67eca50ab020ca42a3ad96e388bb8df18dff80ba8d27a9d049bfd9ef6e4469c912ab6e2311500732de6a7d7a4b59dec06e7cfe0f5a388aecc9322d4463ceeeec1b666a0f0c502708027bfc58e35e1e0a75ada8e503209953df7518f893c801464a586481150026fd04894992821c6d0831906b12f43b8db94390efb9a5620c3433f4882e5ca0e5e6ef717ef301c4773b7fc4dcf016188175583d984e9bf9e7b35faa46f02b30aae72b67ae7a74bb7d6bbe8bc2e8346c4d53ca20e2fa459d05aa479c059cd2704591652ce5b8d4d49767ac68977efed6fa452c2e168c7531fb3028f622779a00620d4c335d922b9a7456233e56304c8dfb97955d6297882d76251fb9434bf06087bde6ec55f003283b7b0ea7cd6f0f617abfd1f0a8ecaa0b827585c1f91f3bf012d5d92fcc6f3c587a21e325a96b00d313f21637bfa05cbfc2c534fdfc4e97704dbdaa453b6f4110a6491525493b7e27a3ac2b4b98b2918b4ce27b0d041d3260a6472e85b4370aff80e046092807368186c3aa087c1191b31c3ab1f13742f0c0f11a3dc970160dffbb26395b506ec8c0836f84bdfc5bafc705a6fc37cf3d3ea01a7e3500fabbbe3526906895e49f774e65bf45ab068df7b9358d7439dfa450907140c92c341dcddb72562feb027550f3f0f4d612c550026511ac611c688781d09a1d689753aa00507b6fa69b34db5644b204e2097dd60248f5b5eddaec240f50491e6b0f7d7e61bbc68982f302c4b2a2865f852bad7a357c929fb429411495f099b704ba7f7e6fef0c46950fe82f4f16434dd0e4dff5f807592e03f78fe9f740234ecd21074bc63759b8d45ef770dd26913682213440b4ca926e656710fca7b0f0f6d238599c8617b3eb6d430e44c71809b1d0fc533b1b7b78cac853abb30cd0a95affd83137591b9e60e8cb3089f6552d38ffbbf0477e62bb7b6c32b4bbc2a0177b36397b549887bd7680c9942e9cfb196bf59302d21077661b409778fd08c0e355b8a2b44351b9616a4e913744c072c01a4f6f6cb8d5eb255a6a68e018868092dcb498f436051d42a19762121bb37e2eab36e284f2ee357b8a001c64997bc04d4fd2a32b699e4be8cb49fe7f01c506b6d9c03854606d25132876f202030cc041956fd56c712fbfdaaa0b98ddc3becb3e3a1f3c4adf685765072e17992145a0fb62982a4fdf3120c722bf115cba9f8cf6fc5a441b8b15d29b200f7573426110bd465bf869d17c3ece6f6f2456c434465cd38a2bec5df1d52f5be4e5c8d63d102383acc6699e61a2f5f739833d04b27be450d0c5a5d4cabb1b5ac02eb44e4d80d77c43cba76fe13d68645f87f3858b705e2dbcebbdbb3a7e2a2130874aa82690288fb86ec4438c151e94d04021b6019d483bf1afd4e304fc8ca6d89c274872b0420739a988d0c67cb5b4eb4bfb220fa2634150e8da0f0817d0677c2dcccf01f06d1b03ad9848cd43f09b0b7a5a035daff1018d5607f20f77e180c593911c47c0155509c5e27064165c619db5f14dd6e9a23dd8f4a9775464489e5834e64161700fb219edbc24f07a5ca211bfac3e2db95060bcb8acd38a9a9cb980b2f7bd34b0505733e973b59dfe11c5e9b39d186aebbd98c5fb26004b554e37d68f04262b202af90fa6684beec39273c51ccb6caebd0bce628e1c69db70d495044fe95b18c0115ca541ba4481e26f84a98b884079563bf1cfc292aa2b9aa3c9efda991349d08b680b183aa3d70b3ea5cf61e05b777139a5f630a6365ebb72381dd579712da091a30332e25a88f6ac0767046ae791d75a3760dcf3ff4d85815f6438d681d580d24e41d206c8861b2202f1be514a2cf0156f5ab874389d0a3c83fbab8596852077ba936ff0202215f7cd99a9f61a381198b2faff50548027c73202c0304085001b48b3a053cace43abc786c0476197a87264c35aed341e8d941187eadb8f46c0327b8c38a7f4f2ccb922d9f33c006729e5796d2b75b7e574a6d10a2fe2949050610b414103a767459e11cb446c67dc959ce6031dc6d0c2b1c4d921b07aabfec05f66dfd9102fd806a505009e6da02cb33eb6e0bc87a4eb40ea42e031bb8a8b90e25bf9eb70d0595c42d01aa130243ceba46b998a75a8c3755aff23bdf7c972800eca4c6c8b7ffebc579ddd9308a19d749d48badea97a97c7284dba4e3904af00f3782454b6eea805663af667c50dd1d0d200990a6270ad25b4edc60e7e747ea08d2979680cedfd63daec07142aa2a3473d7a8ef509a769b99b6248236b3c8670af47c8de7332c291028a342561970d68d3a1bb825107eb99ccb5513321488d00abac56bc882a989ac2317c951ebe5250490877073bb6831b52a7bf496ec6b8001eb25483a91b1c3055de525cd4c8f041f9290a70f3d8af37cd98567484069660747c5360fb25fbec7e4c7653506cbe972854cf3f05477c2b64dd9bc28f820e2062536dc9857b16d2bc285b45164fe713301145d2e9f45cf54e165aed055b4ab0c74b29f165fb5fcee76e6d573d43aebd713fbb6fff3c99dca561fbea4816bde005e59f408a1fc08fe5682e9c9e5c040eff78d2f870f50b6da46c745452d46d9007d679dbae101a95378636bdee964145be9461530929cb28c6a45b878a6956d07b2ff162a55ea9952381a7bed8466016cd01f17126c4a25e30a5033042c277f0f73a48441b293e399adef351a5bf941d33509e15e25d3df8e15ccac6be4fda501128c9f1b7425e8e0b499bb2db8912148e7cf815cc81849cfa63f0b3b54bfab08a7fbb6532970445b1133fb152e050362df2df7c7deb00b3a4301f484d878900d723a43f46b9a1b7b16223b2cdd477f087eb0d8e6b725b37c54dacfd41b8e9f0cf6743a9a748739289b8d766f5d3bd9cd0f081bb74301c11e82bd82c82136c408ee6ca8eb5d5384aca30e2fcdea7ea682dbb6b1dbb4a54dbc135140ccfb5154026d8dba373c460d3483e6914ccf0469383f9159a470f7ff956223ffb8e7485b080127904c66cf6b7c2dece896b8d1f9372b413de96729630d589f627dbf6a1f094fa02bb9fa74a224ed7a3688478ea24e5f88af1c284febe0494aebb9a4b69f0c44c8f06258ca3deb8b0e85c6395b8695db04f9adf5078ba2ea8d7dfc012474079d017c3ac2755c6de31a2d70c84908922b6da1cf57d0903d275815182345e80cadadd1cd492f28b81d05fa718fc09d7a7d89e5d61dec8c6d33875ff020e5b30a9d02fbc5192e51a09077766d11f4cef7759941e26ba1b575869172c906fe0b09537c882a4c53dcb80d192ce379fb8ea14460b0c5980edd6a08df2aec386f230d982bb36d2ec34f786af7cbd6d89d128996304c79df4fc2de1ab7877ab850e80d627228f8b0a9ce215d4d5ff7521a14e8c27af78b6851aab6b43be28dfd4b150d7f89268a0004dccded7f038c0ed3ea0c7c109e1fe394a51203512234f226560e74fc25beb9355fff41bbec70aa40f2c9320434ebafaca8d3d85226017e00da0db7fcca7055d9510ae2e01000f4a51b2a34e8cc8268eb83612ea6fd7ebe02fe08180679668a48f92fb56bddd581a5e4a6fdbd99267ba3c59c71a5b4c97dcd120e326583814f2ab331815834014c6fac57a76d5677362ee62df7a25bb3ec218d090fbb06220894460daf1a6051e5be5b2765059f6f4c9e72f77fb5477907966f06e020039e6a3d68f1e162b9d9f20d800234504b03d98e6e275e155cd0f1dba1055fc922da75bbb5223914d33d72d9f2e7f9ef73401de7793d0c938e290fd5770cc160c04ad4de194c8608ade7e11d95e9b8172ca3a5b6fa113167cdbfe029f101ea9bc164187d3e1e178aee36ce6c0475f6daec39d5cfaa67182e6cf8765a16070486e81781b178dfbb443ddde88a5fc9a1f17141c125abf148216a54f5e3fe045fd1e0117118e595a114a40d33908e7752b920e692067d1f454da8f479b90e0b3036761685b2ee96939efff172a4f689d0d92051ecb281157439850c99e8280a217f1eaa78a02670be26b294eb8a370960f251796725acbdb4ffdd1d0a4a2e096c851f6a397338767d4a8cd8e6029bd586ba4ed17166ee437749ad332913fe03f0f65913dda6f71da6f9cbaac6241eabc88b133f1b83805704dc7355396cb809ddb26d34f65536d4c17c91ca60c7e579ccc7c80a6aff0e3d6d90aa76756ad60d442dcc560ef8a18cc8bb536a0457e1a6251004877e0885b0fb4ae7d36ac02b09326f3102429e454b1fdb2c9950833cb0dafee697825ad9159c9ac6b7ed283d03ced45d9fb1b019e965cfb3d090eabd6bdd497bc18dcfecfec270329240be0400a490f8341fa1fafd917ea4cf518423dd4c9597f9672cceee6198a16fd85cd704c2c6bd796fa81a0795cf5cfdab340bea552f501fca791cc497ae3b89733c8704677139a493de3d0a12e927b34899580f88a5437b8472bdf0c48cbd634d7e8600ee0c66087d0223c371ad8abd8bd27a6ec907495a47e6dd211145f40695cd57046549741963c9908878dd4623722216a93f54415f5229b552fa2407d283d8cb0bd3ec53ecafececc414d623d921c398a0388c88eeedd5ecead01d5c007beecd0bbab02442657d58625af2b766c9bf91b256e58cff1789408006b87d22ee3bb003cc645ed0d6789e4c6a689e16bcd05d4be6bed0d748bd05b9f83758d60d216e00f25599362ae78fc7e2421d50d46e5da1ed76282f6df4917a0fe7f8d621e4ed06b2bb4ccaf61c050ac87c6b5e26dc43cef4e8d8a77c9f312d8012f7bdc8fbdf02fa53442032050f6d03f5799ee4383d031508e9aec28e080f5b467df5389d0406bce1dd589d0352586691a258d44e286c63113a750b1d114b4413863b00a0e60db0da3970c76e098368418be1d63f4a205afa22d411358a57d16147ad9e0795032c8320e751ec05e86a4e38884f59e873bf91d06fdef85b08c11a30e55454a0094993c2770cbc4ade979fffc67a1eb628f95a032aae85ff797c426261976b32040e3db3acfff273f1fbb88625189fb5b8c9af42957ac2001932d8e33c8d18df0e8fd33532b7b58934038db5c4eb432b94057931ef17ded6688f6eedc214c5ab0a05fd9560164eda7271de0012e84eecf48b72a0eae42a0972ea6eaf33a3d2f8051db1840f05ea24bb55a0a4349b340ae433abc745d025761e0a9acb80325fb1057d152d1098d51f976930495886f00d1ca8974d0aeb3e6f4a455f075bde85f6018f9d850e291c3fc41f4c09beaa7ac1d59460f531243b2f1f237c1069d0ebf7057942f1b2a2fbab12bb98120f6eb354ac2e704776fe5d7782ae8a03ffe532f50d57233c52cbcdc6fdf9a08b711aa57bc95f983be8065945b5b7225b74bbbff604f71d1aa2e3371d0d7a12f211542d8fc5b43f881a1dfa2fa289c507e57cef840d4b8f00eb6e5dd15ec8c574a1f6c2adac1b531fcee51dfc618478a6d068d0ee028508f93a3cce63f4e64d55cd6d769135c826518671acd846d2234bc0f0d67402c01a7720baa1103e2068f4c0efee199fe83c08a09c90ceedcba73d613a67500f58990e8d38d00aacab52e4c9b6a2e7464ffb8634e86ee4f15956429283656c02fb2df2e9596066170be459b66065e82551340de35652ba7a1934d6310034840b1306e47497728ab4b4a30fb5619e524fecd572a52b736d56648fce1aed690c08245cca2d2f824030cd852d801eff6c6f9e498579d73f55071293a1eb826c75066db17b1737434bc72b358d72e7902a7ea79c3cf7208794710cee58d15fba8a0ac63223eeba966da10e17d7954f1aa7aedff5f88877d416919110d49a338fe409ae80a03fcea94332379322c9b11cf7e341705f2f52460ddb0d197b9b7217640cae45433bc7747ac68dc5e30fdc698e7837a1b42a719096e3524d043c336c8f0cc5077593db45fafe1f99e83621a186dcc5b5931cc026df4bc733287c0ef7030149443a155ac10a8547cac538933ac8440b4e3b66a9a0cd7c6c57cd38f6e5d30630257b6c559884e108d83f4c2f56899d0e88e90830522e9907fb651cb50b9001858120463c448ce5f31b5f641f83f030d8a5c8c530bef8bf9e21c2ca999a630dfa33372732ebf34a070a07435c21e8ec8ef888b7ff1d23b6bcf37902aa321d046e5d7b8f22b00ca1fef141df473e4b26c0fad9d2e03be6b20a65ade1ee44fa06b817802e45fd12166aaba3becea9500b4216d7c0876e0d7259a2445aa16016011757a92c3d3c994fddddf9a1b8c435422d73ff422eaf58b742126f701f347f073783738209a1f2e29b690d09c4a374fecac677e16a14566bde484a5129613e04ed8629fa32f89fa3022e737ef598cf3b4096bb732aebc62e58d0e18bfd31e0071b2a37688cecd28012cd9116e9db95246820e65e7ecb528e00117421d53eb10e76b10a9f6e73629e654413e31a445ac6dbc1c2d7a812476860d09f05aa2e700236e331ac402922b7fd5f4141dce1121365b70f3aa57e9cfdbd00d27939e02b0de1e5c1fd5e43db13384d20dec60bed348a91fba1abce4613cd4b6a3915cd2d01e5ac703906dfd0b5e178336351b552b39fc3e4d0165edb64df25769dabfc580c031b2e7911e2bf6a7d8e4f85abc9d46475267da17c2eca45579f027f9411640f06453fa3cc97f4212cc85ce3139afd07d76e69334d3f11823bf7189564cae701b45c982ef88fefbef89df57dfbf5669e7cc897dd870e13af1ac7b42363e7c000e4be3cb367aba79db27fc47e9b8f43830c6b6a0403236c05088dc8e57e86fc08208176e632c719243baf6c46f3a3f50dd23bb74ef80dd1af2bd651c1c73a430409e466c5d7023e46b9a1724f7e472572c37575910a0514d44d128dbcc56d7e040c74cdeb49d9705629c6b27194f943d1817c4154ae1ef6756c5b9845c0845b0568929a74ff9f3ded10bfd84a6d6e920d27c9b6acdd14bc638b220673be1a060106d336422ca31a25c69391b1a249f9ea6d62bb5bb16c48dc6db887d1093ba805d6c813db53d80d9c2f1a127157a3f63648a5751b650863621ec8bfad932d03024166c41c0c5497cb30ad22e17405453b48d46a45eeb1a479ba1782d1f3e973098de1960bb71d7871a6d6d7bda769332139d4aabfdd99bf48dd34d89a2a50fe044663e445e9e02bfd8108588104599a4c02f2c2088cfa7473103979dcb332e902ea2e265a558b193d7c719564e5718c514167af17c37734f87f49af8436a615006553490e88571e7cd459b61e3e0854946107f97bf03f7ae4ba50d0522d5f430bd5e4e58bf101092d1b798cf00faf6cad7feb341780e713ba710ef38509c63f00f9eb0d57794db7071b23fe2867e4a35ef566aa8f1fb28d505e7ac7ad25ad4501f7ce24732b8c9659c713ab5fca2ef60b8b830e376dc55a88cb146d2163fc5f0cfd138300cd26ed5c3abae76ce35f864f32a174e27c1f9db5d12dbe81b61b160e59be1b1328b9179437b7b65d970d553039081b42514058ced7d9a3e9728b3005264d2e25102b68800fe2799e01da4d7aa7b372303d3e2a11369833844d2bb80dab2d569b43e5847c14296667aac04d0bb02375de06e6986904cdab7bfefb3003f04ba57db09a271caa3a0feb680e15e50476a0acfa4868822d11dbd880915e03293f437eddd841e4e8866cba1c518516c986b55d56768f0b5ab31c9d60cdaa08ffc7bb604e96f8ff1e41fddcc5df18845c8c2fe6c65f0271344bde53be25440a1b4c852408a0152b80c497bf9caff01942d861c1e1f9b6e611025c565e07690871f57dc0bf49cc86722fd8e4f7b8814f6cacd91b988ae53ddfd38bdb44f46b0c4e38008c32fcff87efbe328fdf9508667401d42f0c055aba88241ed7d1ba2f0278d3b7fecf3902ab3bb6be4f39a22051b3eac755841c4c5300cf1ce0c4f26a0a38fab6b7c557302c4bbcc7e873b322b9f3bebeeb9e823771078802b323d46700dc915509a5d1da6709fc9460f5e842d708605c6e54bee332f394458568089000fcf97b08edd311bcbd923b48b318f456be3791453b23c1113809a3885e52d70f395c73a37ac0c7815e9c5ab7301d36d1c89e0c2224ae330cd32f78025afe6b092115c5d35a5c13a20cda4acbbadac38a012757739cd0bd4d976b8169dedfce0fbd9309c0fee595adf8b8708c4ac2c7f1a20a8a82723f5301368e4acb72f68509fa05a52532c8a23fd9083075f91799b7c2a7f2bc7fd8ae578bde8ba87ac4ec0c034c459486ea5a30d6ae860106859d7fabc867cfc034d3f07d876572437da7046775cd1708020693dc14e25fb915da34d018e0d419550f6ab45322bce149d307b49a84a1471a0b90702a2d98b9d619686aa6a15ebcef3492aa4bb3fa9d60b20492ce014d5174ad99571a798e5418a7f4aec444c400deefac4d4c1ae35701a00aabbb456b46eae243fcdcfbb6a65b3a558ecb7a7a526454eb8cc29197f0173702c31e98b4b95893543f634a772dbf66a16a1d472744c914cf950b39ba36bd7a0c3da28f4fd5486c48698b9b499fd2c20972e23e2febddabd24fb7000d8c853e028a576b200a40e1b306f971ba15e3ba3381c077930be6177dd670761239f27f03447fb3ce26c8a3025a296fe3442da134189c3681346d7882c5952c6ee595fc07801c641b51013788bf828223a31d5360fd1c7e3c1b82d47491aa8702ab3c9d0c9642fcd651b7f1d0b1290af881838289689febac4416dad3fcc6e804625c8508b6711ef5e6eb74c43b64d6eaead74e9f0e9f0db81a25083b8053317f0984050ac01524af1ad01fd0748b7421575a3fd498c8b37d4f9f1f2f3a1edd41dbc4290dbf60b8adb9cc16ab64722e20a4dd6ccf170a83db5abe074321df6b960606690bcaab807df67077188a8372aefdb7f47f55302886cf1f1f12e9549f8de887270e975ccfb1107d88a63dfb7b2ad016ee7abf23f7e469d168e9082b231ba2b35f0ea9fb543917ca0be31a4a49d9c9110c60d2f243da195d4a4b80aebf5d4d4c950b58e540cb885d4a4fc1efb3fd9dc2abe3e2839f7022b4a66cec9ec6726ca81e0fa3e75257cc56593d3572bc9dfa9ba7077a225d723c43d4df405e3097c1383402dd4ec3c148eb105ebbd29275dedc02fe0a61c68f960c8f638d97b6f2279f4b08fde38b20135102b143bf1881ef4d96b342664fe5d2a4a17bb549682cd441910474d76823ec0da696b1673aa02fb4ee6ccc072f089e6ec714a7893e9d59595d0a6168a48dacf7c073ea4d534313605eb4c9e8f3bf88a59d23bb88f21b1ff1aa0281bf65844f296787b96a9756913dfaadf1b2c31d33808846a648e005d422c204d4122a5786529322dde572b7df683def9d0a230b99874840a2aaa232f2ee8f02407d6c693dddda783c430276e66a3236da2b7fffd0a465fbfa03167dfd40590ee1a98dd2575d201bb78b21d77dfd3ef5dfadf06b4df0e43ebe4df543cd07fd09b10b3b2f288ac9a192fc9a30e9133e207491fe808fe0e86466a7e0dae5821900ee9ec37ca52f280f8c39d64bcfcfb59962cf18495d1100ed409ddde86b082e0d4d7ecede9dc279e78731eaae413538294364f545063cae30452a4ebffdf0480dc1bb065d377612998a782d47ec870cfe950c4035144ee90f9e26fc57538e580c9dd19cea2bd42107c15a87dace7c23bf8bf793873bf5a614e94268dde512fe0656ea60cc6114ab9c88afa7fa312886fdcd5d1d8237f2e701f6f3c72b65dd57059bbc817fe9ec364718ff8b7c9ba27a4e01539a7cf8e0fab9d5ac4efd1e422c0c929c29f56df83e0ea39aefcfcac51250c4bf9da027cf9f8802d10552a28c7505e1957fddf2a00e96b0a7cefc683c557e4aefe1c260a088443715cfac93cfc50b3c4b2260f3113449adda944e135ba8b21ad7d5ff55b722769c2ce00e7b49fa026961cd070e5a154e2d71ea118e1f9e29040d9ba996beeedbf2b04ead7b5c2009a5d12e34bc4a7f6bcd93234d738943a4c868f7de939ff620cea49c9d521d640358c0a640d281f3d6722b2479ea6a102b02cf03a52ab2717519326c28a991120d7195a17777818cb7cea0a6def41aac132a72f34b6e558bbb158a7b0481dfa908f69b061ca602e5517f52b3f8658bb06ef39cd72a92138129c6482a33ca12850eb60661080952771d28dac539d17b81fa88c7fda2ff322ed4b76cd03b1e5fcc0b6ef917d9af5a8dd8077ad14645f00ee052f60ebd4406e0fa1aab1845b6bb050220325454464cd24cdc1460a06d4f857e23eba545cc7bb7e00e28307149e61b0732023baf0b5756f854de80bd8c6a57280da90abc0b7e950d000e85edfadd2503c78dd10a75a1d025c26eefcf8583a6b4c744ed946d949476b6f608bbca0d820e3f6c208ff188fa40ca40f0f6de837957aa67c0e74beccd2f083046e345b5170d8bfabf404a36c3670f23eb530c38716cb5ea478310ae0674cea17a7d01c51e04a06fabcdbc9d93076c49a9c60daa4084722c4e73f4386aaf61b854a11a0e0c0cac63536cac31bdf4942843c94b0aac801886a22cc61700a8b004487dedb6b002d920925be00e8400906645db89eb37f07f47692654f356d58334a048b70fc502be5aaf4828340084211fddd9143c95d1f1b0a03c6f0efa4a1383527eefa08b0357d0428ef53a529d78b6e2293c625dfa6d11c1c458ed229fc08594b7d6dfaa0b32bfec4f5f6b69e59b40b00d06ef8551cc7d2a3e25553a56499e53d0d9322f0a8508eb7a12d12d7040079d1e4a958beb0d5da5576f7cb41238f4e84579fa750226937f81d439f53b0460b108d3e4aa475c195e747ee7d317ea8720958bba150af87d37527d566651f514cecdc086e8b2c33664aca3556c6ea5f682967375550c85e239f28550055a8093d9bc34b97edbeb3f90e10d1d2a7925f52393e07b290c86a6d5c4e8095360a7c1e011730e33c1678af656910cfcdcf2c8cba40f222504be77535cbaf5fe2a3bafb678352f3b931df8085ced17939e62733abb71dede06ec9af96a524e874e2b0c9a54ae90a022c0fbffc569f724954fe29ce30bb9540a9001059de39f3b8dd6e312a2dc8675466351b5a18e87424fea2abda97d70160c227a8756f5335e7cfe8afa1eeeda5db94bbadb7eb953a6372b943d9939ab670df65d5e8d4bf0fbbec28140fc45e3cb7e9a66855a708599d130c577e58f05be00694b451b7b699225940f5f41acc9e4d74efdc873f8ec8437c8eb11b4fadd9f07845d5151764cd9a58432971794ee026d1ab488c8021fd1b1513843e2812703031065e19231d098c6515618494b57b0819e98523349ca53f9c7b6f23c2e38ad0428e22ce47421c4f0fcf1ee54f5dc2b9a6ca02703a47a02570b772ed3176194029e3ba65ff9262a1ddf89eb7040f5ab7126041f175a797ee39efe6282ab0aca00d87fd48fc23fd939089d611f2e693d715b2bb55432c1a6ea94a2da36c1562d0527850c65c97ee082d6d942d5dc589f834d7b619533b71016d114934104c5300c7b744a81b99a9b036cf9d7a5463171a0f61764ff2874bd83595be9fc13f2ce0e9b25c07ceb34b48dcdf0be92ec2fe75f011b3752062b5bb982f49556f9613e0b4aff7b277ee6733456e7367f32d41eedfd7cbcd58e0b25c5ec892fc9a412d40755e88c14959f10e0d53fb9cd9d5f5afb7b00c4cd9eab331ecbabbb5a7655e302cba68026222e802d1dd3d39b1217cf700d06c6e78a5a034d17a9f093408e910307824ca38e0b2578648e1c52ca96fd4a16bf5824a67a6d034a6a507fc654a70d62fee23813a18b57e97c70107d2137fb4384cedb0f9928df4639bd058cf41603dfdae3a7fa0ca01710d33fe4fe9d95c42aeb8b9bd915c4b71caceb4f5129cc0c93fe9a9078b9cb0b405f45afbcdae76286b4587898f9cc0d555b151cfff03e0298d55fd4aff34fb8d55ffbb7e7fa062c8de14743edbaa61e4202dabd1bca5f0cf5b93d107e268d198edf1c0831a6f832153e90506f10f24e866a3c88ea9bdb033b845964b5e5550b9bee32e035f613551f6b84c0b74d163c271d8555ad245507adb1e8a30735796bd5816e0f519a96e13d338cf84c21acbb8d371d07247f6d0b81f29e2471d057a03a74296108776fffc397a3d25b95a9da1e79b1b2cddd58081cf602c558b6d41410d665c2e10109bd123f51bdf39ad3529a82c140a4c3c9077a6f0cb343fafa9d58fe7ab482486eacc9ed4963f9d585507524c0d2735be408310e66b0f5d6e9a5ff031b8278c532930602281b1d9d3b7f15a3aab576e00a048b9191f3e574d25313d5f285223bfe844ca2598643c7c7f5fa53da0fde9d3f0c92f438842d28888f8cb0726a314edf81402edc95993e4c78a2ba8fb63d5b600fd4b38c758de5acd939c2ebce0fd9b4664f4651b1c4e167fc9c33626498c7910c431c2da4629ec6f7aa2e4708841e21faf5667e11687cb42028033f0eadf060017224903eea98c201aba323054dfd232e4146da96239b5e9883b180eb35d57703db850ce0ff034401c4dd7fae4a4e1f5e090f0d5e560de60163229fb79b29200a77710841c35f9c545ee15acf2c5b4087b9eb82b1e6c7d49662836f7af731820effdb63409e792fe14f2c75e782f39f69708499856dfebb22ce374d6ddfabb004dd19b90eb6b35605644c03686ab2bb8d66062942e781aec8370a70086c342f0484dd7e5b9806f38198658306a6c927f6338652387aa6ce880f6dd8e501eb4e0da626eda151997d4cc1eaa67e757d4cdd385b0a911dadfe9271a1bef2926d540c68d1a7d2df72413d0bfe2c95db86ff27a876686d74cd0b3f8310ee3e06b725079fd0345ff0b526b9f38824725f72d1a71384480f312128b4a5b70f843cd6f20146ef3b8c4d8572f163f63ca92c747d1a2c30b036367fd3c37299af229f99840fae53aa9531f60637d17f1fbb06f419333c212eead77aee78988356eef6542c0dae766f91cc980b46132431d27dda3e9fc7dc805d99ad0c67d883fa7f2f66c508b3fbb61fb53a3704600c4b9ba109dfd139e727d88b0d9e78f447908f776b6f076cdea350a69dd84b8ff1daa43e794806f3acba2905ff759726de457ba930bd03593f536a7a16266acb34289e9aaf1fefff916519361acd40b679d38590a5d000f45879c99525d5e4ce423f3103cfe651a76fc005773e0a8fb57da788a17b850e61070342f1d5f1552cb6800500df89a884ed2f29148ef541cd0dcd3b56c25c0b12be61762d6d2ad4504c4071c7fb6def8aceae19cb0dc3acc8ddbfa2e176010c4e9b0a24272df440254e398c4a4bb91759737d6d2234a9ac00e95f779ce87a0041d405e8bd3e54f72c0add38d67da6693e8e55e11147372eb9fd02df79f2af0206664bd2b9afb1105a2b74a7f52fa408b32dba5a9233e51108693ea23d793104f46bbc555ebce3da301e9131a07d2f94e865ecf7ba8b459ec8edba9b50a52e06e2d30bbdd9a646b2b45d44419ae24659352f52b11de6a90c0c8e388a8b5d4504532c7ba163f6b7684ce26ff420ceb6ebb7a2597012ecf948de57f8bfd549320485b6ee8ec1423b3e81af06474ddfb257ec110c9801803836269d4ef1437a240304fff5c6885fe438b5ee575806ff72e94dbb78a2c5abd6aef1db6cd059a41f0503696c50b883d5ee33453cd7936b5874bd23d54e7607eb05a22341f338a06601251985f7493c6ecaea9b57fbc258c681d137ff2331c22cd758ab4142fe3062094c506a0bcf3d909d5a725c10e70c2e2535d1ab1ef110d66f0b5048a8499aa40f0eeebbc032c19d7e3d15425a3faf8d8ff007e46b4852ff7ca7d24b5874161c0fba4b6ef1be68355e021eae199ac4d9c0f1f6a3853dde081f2e54b2c14b294c08bda82a45143468354190f0cda41c5fcab99519b3a5341153860a0258467cf10e11ef98b70e69b496f8fe61882db1ba1536c2d7bb2f95229647deaee7c4dbf205fa624ed5313e81da1543b2ac886bf2d98b73fd92fd90de4ff2a53a43ccae220923cef9577cb6ae29c65894401be97496c67bdc31cff5635fa9649410b9837f042ddaf849eeb591f19605436d28d0cf3afa9dd01538336721094c5aaba6d5f80925cc1946ce93f2e9bea9eb270ac9c8fcd392e855407106a48e07aeb595df7c0136f2cf586350e32bb37b84e48104f85812a377c9868269b484e3c783e6982d0f6907ed05d6a85c7348d9653fe6f4e61c742dfb11c3105829bf3a639eb39c0d0fed90c0d19ae84d7ac960df103cdb08e0e133b37d2a224bdc742103be791734084bfeb7d0ca23f6fdbcec1ad53ea7977876827210ee77271fc9ead3cc52c5e60d2ee56044a07e2c672e68670bd627c9c566eb31b649d0b56d553b6911e43fd30f4e30da8ad66cbd81dc73966e43197bbc8ecb97b5e582335e2004027006a4b109e7b2bac97d09c63ec2b3cdffc7aba4e198af8fd9c5a48dcfd6c36ab3f1914d0300910c197c6527a291b1acc1aea59a62fdf159b88b2bcd99850e3d95d1653a07f66df6f8e2555225580bd0606ce1b95932fbb990dfcbba2a1ccfddac0f30860d5436fb68fc32065e016d7f634debbf6297da1dfa7a9e71574d1217b3b31b04097f9883758527e55f1e64d9fcda61548aff8507b22937685260987daf4aab7106aec89df72251eb83611b75a2b74233df6a2d3ff3771acc620f01cfb75275f00748c5c25769115bcaa4a4c37d51ecb2c122eb11ac879b34742f281429d9566705c31455d63af2037d35efc5312116302d17a332f372ced3c8ee2b9ce27204e8032389ba73992421436d235427e995e9873a6920cad6657f8b4480d1792d395a0b033ca02a8eaa8685f119bfd8f658360985d6d88aef8031a92b05d36a437fd60bfd29648cc7126fc1f22b1fdf9a3e396bd3ddd09d474de17fbe62e34dca5e1b0648655603b0b651e27c52c1c495ddebdcbb280b8d3aafacf00dacf729411e2b0ce143491b8e11a7b94c93d63a61ee2a06a7091ee075b0a51222bdce5ff2a15407470550c8988c55fde828f5374f94b4df62493c9f64764a84afbbeb24a057550f79c271b93503f79f7b0904c1d12699a7c3107a725732eefdcd197cbfa7237f02d6cff74de6ce0a5ee582326fd6bef0f713e0c48c3c01d17991378bca0ea02b039a8de926abcce10c04fba6535dd97b5e8a280a9d591019d2230b1778f669de08b1c3bdc128446e7522facd8191a5c5b50c7e08f8a7ab08abfc9de39948b6150a23db738b9781d13d46be5d362753b2d95043f011b94175b0092ca2271dd597027de8bd3a669b635be879c96eb63c8cd77d295ce04ff804dfcf484d0216c9b60cb7c2b4d673c2a60bde9ecbee1a31e983ef3bd2553506605a70bf60517ecb6906c2866d9de38776fc471b1ede639219f3f2246931ef2f02764b9328c1e1bb3e07c2ac887086ef5de180f352073190153451a6eab0cb2fd4bdc00d7527592a9b03b23bc357b0eaa9f0dcac7e4172c368cf8877bade581abfb88280275a2e60700eb3b23a3e98eff22c6eaaf59228e7940426b14d2bcb37c94e629f87b6a5c1d609dd972eb63567956c45fc0061257cd6f89aa4b71a75b4fe85c7e955236889ae0e285c87f48bab3152866697df1acbccb96e3c2c4d50220ad468fb014aba78f50e7928a8b681c5a6c8a268ec7d2e2b156b57f5862e403dbe6c04a57d4ede42aa0a6763501d27b8c125a5c5e6bc26de9bdcc1172c64b7dd79e6f2d3f4cbd705d30e0b8b5befec0d4b10aaf9247c5390d748719a7d0dcf86a0e19f72f40e904e450c014cb2b2e6241f766c9ef4e4c8fd7f18074f9e215a3665b3e7eda4229ffc1509e8bbec656492ec500d887e9f09a3bb0bf2a36ca56aa788bdacd7f01135762605d334ea4ebb3ffb6158e27bd7fac65828b3eea1b871c4829287c7d59579dc7104933c7bd8ab9ee657cd1f35d31f1a2a9277f3f33bf28023fd9366c56a6a6fe8015719926870df72ade43aacde3a02ac47c87a39bd51f6d2e2b07afa4e5f7a480c8e7055491045d76f2064f435d9fa2b1be67f041cbf2232f2fa6371ccf2bc3a00a66e25ca3699f29fce2fd1d87c79b7791a90008e13ad36fa5324c5dcb78b5f0d19fe905a441bd47e4754a61241a0f837ad511966063e9f8d261d10aa7bbc6605bc0b87c028248c4e431499bf3bb3b0c84aadeeaaba5d4935f8d3da46270ad7055b2e2e47457c5d8c6bb8081bc789e25b6fd8fff65045fb17c342981672f22008660b76b5a42b05cb7bcc93790e99ae74e1cf4bc87d3a4b1961f3e8ba3423c8071dc55ceab91388bd256e567af11925f6eb814c7f938b7901133e16ac7677c90a21c6753e0b4073b2c9b4a03d4db088d4bb2e036939cc4d88bce67e126b35800e0749252e7b24d4338348f9d1d285b2906c7589626ce493b3776c1fd8ab333f0cdda389ea32e44cac36fa44155744f398f1bc7ff4d8615acc54ac1752a2114700a4242e412901f5bf3d81959537400a23c7137f5bfd18cd3ac47fa23e5eef7d0296d64c08518079d51067751accee0c33c7069757c76d9efa0949aeba96389309de5fe41274fe600929d774f1856ad10895d8e364f45e42105cd3a2123e67ed014fd2a728edcd8c7f44ac91cce1bb198388ab24e7201c1e0e1ab03862e3c4660eae6c1e6cb0cfe9acd512980cbfa9353d92730812093d04e53a5c8d3f3de7d9008441d1016191c674f472b0783a58465f5ad0e0502c63c600eb4fbee00bce8f086816351143c0f5f75eb178df26d59bef86af456c5481c2fed66452360ba94a033c6045fd4a063aef092990f9fa270df8d8b44505677c211df014e3d8b6ba850b04e66f20a863e8248db4c063ac3b8770cd5411f3707a7cf2470f87fc696dd103a15c7b89a464faacf5a064dafd08fcd8d41ab3149e63191a0a1db4e1ff558c03431a1d99dd25ce5ee208175d284fc3e339f296d2636484995d4e064a3c6f1907 +generate_ring_signature ac5f3eea820fa048beeb8a4b1dee423c197e4b3916c43fc69b56671cb5994d46 13ac70d49095f2cd0ae8f0a3752e74b1bfd24830d0c36eed0c8549828eed744a 12 95b440fd2832a2bdafa6fe9dd41f91bf9c190827ecd01481714987e5c29c0250 773d9fb30cc824d024ea812955dd9e2ed4cbe0ec4de6f183cbfb3c97c72e64b4 ca4495013141014394afb127ef75de6a18c424159c525ba00e43a3daffd0b284 cbeed34421f508249e5639e85b4bb0997ebf20b22396b44848b5d0799a853482 b9f595dfc5d42092b46ced57cf4a77a7732010e5435df18bcb35453430ebbfb1 f725e1937654bcf210f8a08bbcf4c174127a19bd013a4ed13c5acde492fc1ee8 ed14039aa614a62325ebad2c98822df3220c9b3ced3167099c12d4c1914f2078 09cfcd347eec14091864878040351f128a36f17f8eb1bc1bca93d59b36855cae 866c74c5cd6f1a50142e787a0dd2694c6e4d64d853dbaad5c0d3e409f13c5574 03a4926f65e40264e87ddf719e2308c4700d80cf66a87f959164e91f993a0446 8ea40d03647a8f966b008a24515a7b9f70ca6a0561a607338a4ed023d792c2ec f5e174d78de4f4eb1c972f05fdc31510ddf285ddac03edb6e9a82a7b3cbc5880 e9cc33cccaf3e7f182ab8dc40dceebc6ffae186ce607ae1a01bbc8b6b640cf0b 6 a71fe555bab843901ade5a9bcd34475d7d50116fff7bf26cf2179d2382fb7e06c774946098b77481457e4711e39c6ae234d7b1456283cdcc3708f75336047d067291aa3ebedc800ec8fae2cc402496415b0a9c6fc693a698159dac57fc49470936a132a8d79461c7ba4c52562f59585f5e27cec0d9c449608b33f460a632b902dd97282ce5deacc9e3d7dcc13e4c11ecaf60f672d14871bcb9796eec22c6d40919b8c51dc376db7fa3d5c2da5c09810e0e1480683b513758ce809188887726001b5b6180804c025ad00a3165dc63adc93025d1a7ef741ef7d528dab9ab1b3a0d5f7294cb1ddbb6adb42fdcaa7ead769e5bf8df7f477b189c02a436da75ce1a0ad398d03fb24cc94400359596d8d12927bdbbe07b9325feab70477270f0f1850cf2a682eddb32ee1149de0f113f115ced4833accbcf1e28526c90f4085957930fe66e6f619aa0d8236ccbfdb0d7760c06057b2329ad9a68ec08bc4895aacc4e0786e477f9dc48425ce4ba4d9e1272322e416a49cff47e7994d3b9e74ffcf5bf03eaa45cd35c271df310490afe1d1bdec6aabfedb9a16207b276ce9d28f6a61007441f120e5542a0d301cb5d5d51a28a024125f62ba2697c1551186c798c60d60372073c9446218ccda22dc91d92a5209bf9e24380d63fff1b654d7d40b59538058046fd7bc80228c6286f1adc9c4690cf0a523f7825573dc21f5949965cab34008719d220e4b66e34cd25db57aca713f0a98addaab9bbb2a732c93c3998ce0907caaeb747a79d8341dcfaa0d0f325325b2ee8f497a9d3c5392f490b160c8884071bd40d97804144f15d7ca0332451367bc911afddcb49c4093a42a814985d920303862d76cb55c2f9034241de8d2bd0b195a32e0104c3c34275852ec7ebafdb067f0bd8f1ee055ad4e82ffdad03603cc23705ff791073cce9ff49c1332b281e0d01d65aaaffd628db33e1c569f8aae0e9c535853c20023d7586a784e4b26f6c08997c7aceaf696bfd256c3a852913dd2a24267257f88f2f6e12cd96a2ea995c0ff27eb70b4e2d996dbf333baa5242f3d98ef31bd794d3e4f12c2918575a95d60f +generate_ring_signature a969dacf6ced17410d0e9ad8cfc607b215cdd70eb082691fac0bdcfc843714a0 4e8e28703fe63162d0402cd73727f20f917cd9d4e9ff4bda4675bd17eb21542b 8 f98af348320a87d8143f474d7291cb6b99afa0712e7fd365becfbfe9c4485cef ff92c396fc10dc5d8a158558803c2516e44b8ae7de85638a091778171c56df9b 65472baed472fd9c36c0f455964079680408534a70706615c984f52967dc740c a2234ded3bf8b4c85e7ae2a32ebe2c1cae0a0ddf119dd08cc7c39139b0f0fc65 cfbc7588fa7475b6b6fcb4b3442e4d13b5f227f7673e4239731ba54679c15e34 f057ac252ea59e8d7c73b85998489a6aa907b121b62491c687562b09891a3163 1e4cb0ec4660171f5d77a14381a274e52e9d47fe5e509260641975807814566d b54d80435cb7b5beb38d7e560567dc51fd1fe6f6aa35b0cfce2e5928e5a263af 18579794e0e0d78db94589573fb1bc9097c7d15cf0b12f1605b598e492e94f0e 0 13f71f97c4245b92d8d7c505e094530d98bcd525bd94c2565866603ac292350c19906cd85ce9075345ecd1383dd695e4392b9a273960d84ebc8a612989fb4003d1d78f490d9a54b058868826ad9febb472a30f23eb5370d8807d4875b1ff8701562ae08eabcfd119b2edf9f449ffaccaed72b2e7452d9141c91e17827ba3d303359dfdc4346df4762afb0e9dbed82f5440977d63465bc0544551ecdc27ee060ae3a477312bd1d29ea45f233b6b7a5be1f86d33c73349399f3ac5b12545e06e0f72f999a21f80a0a879bb272ef72ddf1db03b2a6a7743014e328be92bdb4f250a871befdbbd06389452aa46da0824150169616c3062990a836f8cbec91cd1ab046e7162a94d6ce8f00649058bcf7fc58952d7c5a62b0328df804576150a10d8097e9491a6c9a9aa18ed37df5fb7e5499b68e2f77a039f4e44485e9f77b907750e3b9fa0f037e20e71a0cb6a419951e1f502b3b1c2e3c1cb7322df3de37b71c604ee28d49d2808e2b91877cb7eddc3d99247180725f2a85cbaf53ded05cbab1d024d3c47bf7effb754cd2301dcc67958981bd419061fcc85513d123e89df815506a2b114a6f6bfc981a7b47a44b5a01dadb65df3f2fda1ec08739fa20c066ec00d4691188cdf23bad15b56fadbb8af22587a6150cdf32fe51e4f300492b1986c001f5ae7cec0264ae0eb7162ba9f109a692ff9c1250f3b4c043919914af079d104 +generate_ring_signature 615c9575ed50ba85d5bcce8d3ca6402e275d2d6bcc04ef74f422ed43697d6305 ccffbc2355d0bb6aa95ea588189f23272862c42747822c86da8f9e7400b5bdf2 86 45c4867df5611955368fbb77ba909fd5533264b0d0e19c5b4ba944192bf0f437 bbf31ccb42e6e5f5bef94c7ac1f3e3a75f6765349ac9896f8996af4c26e25bd9 ce23374198ae8f39080039cc60e40c5b4ff38680388d8245bc33a0c149fb0b9e 3111fdb0a826ce14b19d3318f8f7dc1f2309272a93da05c2f75db57247846eee 7814e26e95e7e5020add601b6185b754b37190ddb169d10c0ecf0d59bd9215da f729a29038891737a5bad1b20f73603413b269ee62ad0c9d1520a712769e4899 3175b4e203ae08b56190ecc9b55298d1cd8649ec3096f0a2d8dd55b4207d15c1 c70234bf03d5ad54a89c62142b7041e344a822adf973c5836d083a6cdc4fabc6 2fb490c89db6fc3f8f72767574f12f60e9c599aa9a6aafcdc1c7af532f88f8b7 7e5cb0e67c8830fee5b8ea2285dc7274b540a30a41c2075a1ea7f9c93ff24aa7 c620fae89744affb549b08e73eb814439594624e7c3d5e702736102a41c87f0d d6d09a50219a62dee24884c8c00b726b71f3ce86c8234c463fcb0e30dfb851d4 7f92e989e03215cfd627a1195b1dac84582cecf25e3ac3c8613045498fc52540 d6706fdb30bbe2fee7a2372d5e6af105c5608ca053728f88cc11b640fbf1ea0f db1410aa134823e1f7958e118e34f0bf3780398535915df1addcb23ffade956f 42c017713f3be566b7b2bbc20e67106967753f75c01238c4c1f850c03c483316 29b2295ad8908b632bc60914314ef3759822e6af5b8413e712f24d577f4f746c e7cd0d161a32365df56de9ac6cff81ef51a9e84b15f9683f1127bc0ac70b2881 6b37a0bce8f6f5eb54e8e63e6dac256abcda687fe556a03a30303665978c2629 55478720be2a0065a23990cfdea13f6838fbd83ffac966040623fc87e764edb1 739c038457463cc29b2eb38b917aed29aa5393d33e974c6752648ec9af2de855 05b68e0263c5d2f4ae49d868130f3027f9b05acf1a7ea243095fd1221c521890 d78fa51a108fa1cd924da2f99df0b7b1f36454daa447fc35fd22b6d9c3c0b271 18ccce908cd3ee621f29fdc5be2ba330d643bd390a680545c7eade334c75b3ea 5b8b589f2d948f469d24b89342c91272ac6901ef55060b86635f2df3143a8e17 b080efbce39bd81755764b96e356223cb49d6650f9f163d949c79f0ffd54495b a6bc8258ae90678cb73c1b1dfeac0d26709329b77ef606b16fe993194d1a9105 35066c4c3f84e5321971b4fa02eb29bac23d168869b4ebbdce7e152244288735 8685157b8730b950be2708f444018d020095690d77a6c59c08878b45474d711e 05c0412d86384c2608738ea30116de8657d59c286df1d37d1ccfd9a839083c1c d3cb9a0cf3a971b5cf74c6593e4e9469bffa0b553145aec0ff70eeb64d49488b bc656a3214e1b67680c8af2d95da4d676c358e755300d8b0a7704539896f031a bcbf2f6697ca478aac7f050713e658ce6047082970a39db7c93567302c8fe8d2 6a2e81f64986eb34d08ff924b2b9d26ae3d329328b533987e6d91425f01ba240 c10082bbcf0d1370fcaa34106e957d40f0580aab32a9799df05ab13ea4a2a623 9b3233e5ba6bd1b2c75a58c33a69b3a6deb3742ad4c15aaceab509a57fc6ce2f f29a4018f73c0b2f2c4d24ddcd3f49c4f6a2e51aa59b189cbbe7e185e84662ae 006fb0afb48ef40790d45b493abe745eb900e444301986a6a019b92aa4cae99a ccc203fc57f33cdba1860befc9de2594c1e5074198d80e4da440f893028838e8 a830e75cc81183313bbae383495288f02fcc2111ae0a23da9b6078afabe0a3d2 6c905cdefb9eed06b9610a231bb6dae1bc166a8812b573280ab21936b8fe6bc5 199658a28f278240c9c3b034eacbd4dcb1ebffe4ab0f38a06e64007feae6bb90 685c094ec507662eb2f34ed91e5f5375362af5b6f2d22d4e129ebdf197640614 5938dd9d19e4d95b197a08fa10e1a3cf812dee4662fe06078da12d39cfd61ec3 266fb3bfe82266ecffc3d9d6901dcca929c10e52d1222e5d582282adcc2738d6 0653baa0edbb94d97a9d0ff7a9fe830194160b4da561f09067060fea87f17f54 b1dfc020dd93896cecba2f5079e660b83e0a4180456c26b3f7c0a4d545936cbe f9c5d3e22993e1616a31afe1cfa02bbd1db797c2ec54616e0c72b8222348c00f cddb3d33cd975399aafe428428bfb0b0bcd84f986b5e856b32c5a3e69ebef5a7 689aabdb6f4660a0350a8f54aed5ec1d8e83799e4ffd212aa9cfadb2715e252c c39b37bda8a4ee3ec8a7e97b718c94a354abfdefbb8b7d4eda1a15726edea0ec 16341f92829fea39625f12d0836fe5deaae6607b9330ed4668f24a34fb8f7100 d928f0ec2abad78fa14a1ea2063d98362b4cc882ac37a1367713e49257138dc2 9dc2e650e31b187422ffe54fbb42c6ded09b9254f81e315b3badc8d386c44ee3 9b913fba5a3e06c9d3ac13e7df86a5f63745c4c02eaca482ddc8fd0970fc58e2 f3ad6852c78723df9ab5ec9fe6cc06bc5c76c76d7823c8194db9f4de013ad3b9 3a12f91d1bf521d044a4ef0a4b5b39fa359835b371e7823d6e1941a39b54e871 3d44d56386dae58aa08fe756c8d9def4e4e1e9ca46b1082f7be0b707281739da 0e0b0243e67d34ab2c1191c366427130a0b8cc50578c15a6949c8e8b8f74e8e7 9f5b856ed838a3c1521cee3aca1ca04496fd663185fb2002e89890b2edecd5de 9bc65378a808ecdd9ca9d17da20def7e455c8713df6fabea3e61de66d954ef2d effaa77ca3c7a23586255f734f1275f27e1ffac3c2a0506d281052eda9468739 f9c158f64be5dfc635e397971aa161af4c330311b97a0106754bf2baea34c3af f12d6493d89a5d9c40f8de489f0b43e32fc8b4fcdfa686771087938941302608 57997ab378a7d03c767ab87dded5f9353169295cadf9871af0b627242d3e0ec1 2f8f6e63662f791a4b3e6b70c0f29b17c6a44282cf2d6cb235387de3672853d6 eda4262adde95393e40cf40e78482374f99bf5cd2f41441526235b2c308716af 66ff21eaa6bf89fc67e99a9378e4bd5caa6040331f8c81015c06221cf94c25e4 82ad85e83a18c3976c8e2e2fea37558f9bddfeaca1de27aac555dae034289a42 5dfac5d97b5a504e12e9011e8dc36b41b8a95ba3cdb088a11ef64b1d1608c352 323274a435e4c6a649110f9a82cd0b10fbba181754af152ea1e1f25177277011 a426092a2be909291865f585e6a3eb3577526aa90c8cfbf334700f3216223007 0b3e328009ab9686416b174f5bcb5b074ed10bb92e685a356a4f3a4648547e4f c49c7bb87c276941f0c596810c6e89c3dd97595920ec26e8aa21808e15963432 382c164cd82084bceafad7ec9ebd5c066665b12e944de351d0edf23c5f10d4c8 ed4f2176550c72ae65e25569805539ac898932d2ea649f3bb6cd1a42d940dd0b 6cb4ada9af2f08ae0d7b00cf4f5a559de4c5688bd7ac7f3d25b1b15493a31406 bcd2ece716068ef5dcb8c2c5ed00a64b1c2ed2ec3aef99eebf6ff09b0e913f63 c7291fc7feffa63a1f0d81b305a4a00901c622c1f1e0e225d48779c36fa9d488 86cdde192535877c1573d38d8146b50458daf87d9cb08de6908573df156d6665 51840fc9c96ac48065b11c3df8f12f654fec260a3587b4bca415c605325d5c4f bb062dba9bb7990f7a2fad2bab304c1b608672c41584f160b2fd05b6b269419e 7fe2fe9f5ef87becb3003dc61610b8d7bfb43a1c9bd08519a3ea45f8d19e58b9 e68278edf900c7d04027d871f86f5548dd396a3ae228a75b5bb142569fc98274 add1dd3b18f64a1cd6a60f408029597c720cd3daf3f0e92d7205588e796795c6 a317c868cb4e46b00d7b52f8df22daa1292759e6fd814c47ef2e35ac4acbc864 68c6fc5d61d9452c494f4d79b8a3ff38b4ffdb77d47ffd2497278673dbd3c40b 61 5aa661a6edaaff218b732c12d42240bca01a02a6bd8f439354c15578efc307074ef3bc9debab57c29a5ada19c9c7daa0c14e2bdc5ee5db6f38b49f4ccb622a0d3884819f7cc807ca0c3d83b71d4cd44e098d808046a1b643b323d93f84d5b10191419dbd353af71b2abda63426eb00889d836c5da5a31bb00d59728feb0f7d0e31fd6d413ea41b8df781a1d4f08d5479d4f1bb9727f12752b3d19392b131d90137c9ecc126e9ba94b4e99e3f0aa48735181ae39540f9c05f2dac218b951bab00181291b84953751f009cfd89ef63ea398bd2800bbf1983b033820e477f9e40039f4c4e27a589c659fa58cf4a90a9a10e774a2caf5f7b5da8e092d176f6e915068eba8c09acbf03074b45c33fb7d5bbfb40db48bd2fa20aefc50c9f53ee715308a2a37a8b5dc12b674623a103be6e91b7663a1921a841005054e765e49c76270bf5a4293de33fd5eb078fd8f5f162a8ebc32226c5554f8345a73556992fac3907a484f93bf39efaf815c66279a33c3d52aaf53538d07404351023a48fa531030f284491f85c38022707698cd4c488ae42028353c2ba3e5c29b7986b83b7528c01d5e67af0f3cc617e571896f7a262f840696997b83698881541265eefd71cfb0c4dbff05e030f515f3c116ac1b986d153b9d0807eebca1ff958a53a5d2f20a20fb36f1eb9422da5825edbb6a832adc7c8cd844684607bb36c82507689fcc3df06346665c9f871eb0fcefcdd935506b813db1ed87a8a47f2b5415b33c3c0da240de25c5ba3f8cd79327b65850584005a96c24f877edc4920de0ae34c3a0b26bf0b078c90c36dc1c2f0057711f797b9a7d3d4768b1fa8f143cecb1df173dafd3a03b4fd9beb68a64c471101c87c2e157405afe89fa35ea215d431aa4d6e9dc9820e517d79f52333f2b4ea87745e9842c92bd0861f3635dfff2c99f2f5d85907ee08c8b953884406275d9233d1fb81a73acb924a8862f69d5e61f11d5b8bff39a2093dc0c47adefdba573d8aa2ae639ffcea058308b7e12eee07ab322d67c506c9084d300b66706006b91f4860c5789581edc447a43f2f5fae53a198c25f1f759a023210987c3392791cc7cb91b53f964af6ee7ae9c8600bd8741cc2aedd0fe36803dad6169af7ee3e29d1dce1ec31e4b8fc1c9b36929f6ab1bb3414df1be93e170154815680f467c1ec3577de685a2d04ca7b33b12fd88ada9b778949c72794e300e274a3ef15f3d9731dad714fe57eeaedee34157d836c7ce563d94993212dff097c8679b46f53ff1b47990f1aaf9d5b24db0ff3cb78a86cd807ec66e529fa050956dfc9a034b9e94417008cecf2abdf0c25882b718d944fea96a50982a9f43c0299ed76d1e1a4010b67bbd661556b0c8c168d92fb7414c9f6b4e179fd6bdb71070691438fffaa8f157569c774f5b589669e84fd6ab67f6698a2dff3970287a601241514f0239ffb5e2cca8377873e2a5c351d33757ede8aab3965919af6ac5407edbd7214d24e21b117cce97963a9a1d7b705dec257aa9872a6f45bbae635540c56e398742d0a9b33a67a8d50b6f1bffdce64f73c35ac2ab24a76120f335b370c1060f22d47bfd68c2f30617b6dffde54e0961ff6a4876f8f6f0ed84aa8f1c7026274cb0219af5240e92ef2f905489e544dbd1bb2e44102680fae0a760c9a4f0591a246a8183f6099244601bc0aeccd6baea0223b27ed582263f04ece76a1440a321e4b74446ccd7b5c4d53f17f3aed6759d948ec334f3f81bd711eceb7edd000aa9b6b8860a5469b273b9984bc1bcfe190beb0ff236d4d616901ee1a6bf445074a92e96652b40bd03f57fb6b1470c96a1fc0f1cce2d6a3502f13b154d1793709daf5a548db269f9fd6b17c929542c1df15fcef6707e45e1c30195695492d9604e6acbf2fbb66f5f5a4c55682bec702ce4ae1113ca2277682cc41baa600dcf90aae27e8785a88dcd630cb8c9467f8a0e7812e6d39bc5a69c2fa3533768d037e0df9492377bfb2702548884671d9194c361c2987a1029c7d9c4aa20162f4be530eba454794e411bbf30cbe109c9c1b7015009a7a65fa30b29deb285adc21b09a0ef1794c1792f2560572878dc180748763f2d6c277db8e0d7b43babc30bdfd0b003bd420395f8c40eb7dfc63e339c5e41e40c5a17ba0a727ef18c2cd3277adb903a96d535ea7ceea7588f02f63e6ac0a17cf05a16e87fa50ebf288fcb559751d06aff7c56105d4f1a7f8bc0b730d2128b496a745a4b65293039ed47f0286fe3b0c982f61b5aadd03a563908a39972901649792828ae0866dc8500fd75b718b1701508e5f3171e4281a9ec13dfeedde649b208aa760eeb1b55e592b17096751730766e9c4f3b2676926520c5276370df69a462b9e65ee57e3aeb23b77fe95ab9c034fd4d58385d0fc345d2b00a764a77cf69c93c2c5c29959306c85a430d9c3c6033c7753f94bc2ab018526682ed19969a072fde807bd914a947bfd6d91fe9acd0acc4e72641e286033ce10d0661011fa0c052e33bfc550bbeb71068fccd6ca0301d565b1507b2a376ee3e288dd98e7ab46f244ddcd08589b735784da3e8765fc087f768e40a4d4868e46ffb3f6c98ed61ddf138c107143e8ba9664b17e56f13e036a8aabb1e2e6470610d04e7ff8ce230d78c0bf776521503ba3fed8f0fbc5c1073abbe716f3338bfcadadba8f72394a87c6aa53bce687ff2b4690a9b7b6695603fe426c18175e5da7655d70ae9f8ba593d9a63ced5ee9678bfdc726ed4881d604638a04e3d9db727bced13cf385901989e797e2d32c98100ad3f2867e4f4a78045b8d53de98cf13fc1eb44fbb0635f0f181b9f2fe396c71d173b04e2dcc95190035ded94df53df077696b8c684364d48f8774b48cff2b979ad89b4a536fbde00a5c6c98ba3e69b1c448c76807e8dd5f8d76796b0ca353afd1d4e0513e7bfb400219bd2f8ac2ac501a2f2e5d7ad013f339ab158d28c6aedbe25d8213d0df608d0e247b9557ce81b98233c18150539907c06bc3f71adb2de3d75933344836a1e601ed0b385662fbd83b44c4eabeecbaaddcb436dcf48d53e2cada8ded0288ecbd0a99bff69439dab4fe8e4c3b38b07529ac5b313a4330aec2dfa20912545fad0608d6f8a7d2edfc621f9df7590bb8b27c407112ee3627798f305092ca4342bd3a029369e75da33a02ceef1d6235c9f845d5b60900b0f8a7efddf1deb489478cbe0a1e38cc31c6e4b8ea013f60dc2808b06f400be73c20f8f1331c9fbb33b474850d394615e40923d8aeb5963605a8027564762e4c23542be9fb24dbe264e5686a025124ac06fe5602fe4a4d862be4ca1af8795a562e38b4d5e4fd230e94b66e780284a937b8df6a8c0290adcf66e94682c593434139ccf606b524cb721e9575280eafa8f9a933ce28ba07b3159c9ab8d6a8d4f0ede447759ffa5afa0f3523c9ea0ef79405319c6dbcb869fbab78b23d4247574ba04cdc05e3f05b90978dde56790b0f9a522e7d2fabade5b8d75f5e746c4c466ed7d94b68c9a104c179913e3cae08cbf93eb35c5a67bf16ab795b748047f2ff180b41ef761d433c66163db22d9603764f77f0378a0cb6560496df234fa5caf94bf928bcbea7356c001d737607320372e98149de6623f869351337efdd832c45aca76edefbc839b3584ee84e01ea0383ef38660077281fef52bd34bf263678599923b1d9bb7824ff29075633636c0a94d2ac1bbd623684bc1ff7add1b1b8cd0c47870cd8abf9ce2c88bc85f1e59f0113e87bf65b833f116185893cd4b0ea0d4b5e7bcbb72a78c144d332771e19ab032e07414ff347aa7a58beca9ae02049960123b34cb7c1cedc736371d1f1bfbf0489cbe3e447ddf5c9f08be1a7a7bf3c43af58f683e2b119b9b376500e20eb040aca271898f75d652f66ef7a8b4c4534f17513c958707175c8b7718e8ac9b2100e55d71471b4cd538e3616b19ec6ecc624c74813e8be8909da0e77284866d6a00efdf8e020befcc61cf193d0c785dad39c2685e7b53c500dcc28712542ffb96c0bcfcc624a31e7982ad22c917308c2554badc1c92080e746867d191860c0d447034e20ed9c7e3a93857484a420e9814fb5793e7d56cbd47ba258adce32d4705f0052187b28c8c51c1d0e94b2dc525e6673739aa2074d4340aae0629bd96d01a9075d81a21cb53d351c0e494bdd89d437bc528c44b20828eeba444f2d253bb30e0a041f0c3cb8ae55c7ed13cb78c41018e83ef10017eaa4da505aa027cc58ac660c0426c2cc5e20f01616261026b70a19d082981107357aa01ea3288bfe7cc5880eef3536fe39cc05b6ceb12f1a9ffc1b1dab62d8c71756c785ec213972c42bbb06dae18e2616e498d5dba9b64612a171ce762fad186276c20d81975dbbfdfc110b763bc58e8a82b4153706f13f75270309c4829ef1621fe1c592fc230f80faa502f43376768995cbe24716e3c5e53a5d763b14731830332c0f0ffca4ed56a96c08a6ab93bd76db8574d0a71beab256c7f407a127741e78d540981ebdecaf991e05f4d1d5a8ea8439a156106c16eaa711d91173b6f74eaef160ac2d990ada00fd0a0e65d27d8bf002a6aece9177265a133071791484897848aba46e6dbeb9ef1b0b40da2ec380cb44f290b76f5ad1ba2c9cb67b2195426302cb07bd292dc0b33f0d7ff168803b1f492972637bd3ad8c17e5bdb43105b595410b11265ef231cec30537107d31ec4fe8fac99a71f86f7bc9c231992b175f81684ef81a6f378a4e760c16612eb880970bcea6e19ab049cf1635242fb4c7bab6a3fa543feaf00a3cf1091b6bd11528445b8458806c0406d05f8849dfeb3703ec3d46e9f904ff161ee8085d50b7e5938ff6310c7b435174d681e6616112dcff3824d5bda0d70cb4f31e001c147212817eb625287e9cabcd015514bfdcb85be0a31257a9b332045eba6204047f374d0fcc2308f0aed0d6d3d30a76e7710b0545bc1aeebfcdb94d36ee970622adb33eb42f638d45be81e864b52ccb038c84fde4767e7a1c3b17878418820ff2e17b5d4a2f9118058b5ac683f37bcdf58aed1056c7c13edd873bf652fb8909489a6950f95cf6705653d2722bafe56251dd6ceca6ab79654dd3c6fa2ba8240ac24cd15ff96e6bd5cb7292b70587f35f7e97faee84c2a4658f9f3056924f6508b48033d612b2b8207ad71d47a9cde166a5255ed4a574d6fc8b923b67c440c00dfbbdc86bd23bab7ed5485b34021d82ab031651565b771a5ae58790c4c1661e00658feaaee05f00052e1f4758137cae3814dd3e0cbe85d21f175bbaa84ed4540e9f674f6ed7e5c46097bc40ca56d6d098d5307be09ba75fd858485b0efc4076088a1734e1f5c1fe6a00090a11d727468023311bad98d8410c2fda21ef75815f0c31a2ff77e59381c397c4a2d50c1bf661bbe1a4d6a5b9cb1c26be5a4564532309f1bb597fab50c4a55ac822ef3ded3e5b3ea7ac90027bb062063739f63b95c20ba6479eb126fa4fc065d9883ad19af9d488ff9a1bb1d99ab615c543dac5c3930a4786eaa77517159f0b0846a863ff326da0a08fae66ba959bda3b9b4f5f095805ab47743de70f4cb241426fdbe6acba035dbdedee49f6ece32a80e8ca475c39005f29950d917defaae873b0b21219fa9f6eb253c0e1642e52f71156276a423b04c9429d225330c00494f3e7d89165a2eb432fdd8d51c220e9efcaeb2483cbcd0d552478e704f374c2a9f3b730ed90258f5f677e0ac2bf3777295486db9bdc370b8bc1862b17c25a0bad39a5733a70a99cb64f872682a7f517d225b7ef27b004092af7ede355db7cace33676325ce8d5090317bc112d2942199e4728e006472708f2289b96c15ecdba43a896b58e3ff28baada93cc328964a7f4efe0637fe4bd079d40f3746db9d744c71626ad02e876e4d18d7945f319eb283c7e7275785de00e763f1f77be8990c8caf54b7db6be978a1f33fcdec60a51c9ae1a832925012f03800eb368572ec114de8ad17b54966072b1eb7ce08bf87c52e6ff41025f235502c14059a49d130bc4ef3d94460ac16d216692c8b38d65075e0c648d20cac97700290d992e07a8957340c8cb7ce0a1165ae750b55afd110717dd909401db0cae0ab2658c9faa835a263e5bfaf4e05b464bb243fc7603f3bbf23d063950085605079be5d620b1eedc701ad79e846ae12c8b503e66ef4fc1a9bd334fa65d6e513e0b525420404ccadb1d30aef8115e993bb1ebe96d17f398e65dc28f6f2900a4b50bcae42530030d0d16a589346820fb5de32e3bdfcd54681f65b10b20d7517861030d6a0b801e9b70577597a75d2c9cf13225195ef9ebf6988c5747e8638a6d1b0c55f52f78f106c88c3c43a7a3e5578764ecae91a1f7af26f39979f0569f2944078abc6e0486b7937b73d54ccbd307eb3a6191ae61113ea9eb55655c7799478507fbfdd5926a4f8cd791846b7fe55e831ea4a1b5edb3f4dfb2c2eb0e26868d650618fff145c3218d7f9d552227063b2ca2c9d73b149c2249029218253b2f05db016f44d7b8204c022bd64c1d63b268e612d3a6fc3d145a76612a90439f89a62c0e9a2426e9f2ff760e706033dd1cb9b723df4f4c08fa3de57f9d9f9c9628bf23070412a05c47b6846765b9786e7e05eb973e2dfa58c82d8fa4f7e7d818e19d6a0d9a44143694c8672f19dbb687e36e934175c56892e96159622052619aaea0a20fce0496f64f0d772726c1b67fcbf0fb3bb04996cf21ddf60bc0adec508d56d207ce48e89522ed79afae285e543d20488e5d2eca2ab5ab67c6f0d3ce0468cdb4059eafeabbc7cde5e0322e40d7a6211672fbfe3c11408fe41b747179b6072d620c500240dbc40e80b6fd89cb38580c8cbd5ed42ca4baf1b7e4628227122d477b037f279600ef6d0a3fe699579d0fc5c709d66853b5827269a72e4cb9581ac23204203060c06f300aa50d47097dcbb4c029077fdefe18f7a8351e0619b32a31f0057417647f2e50765ba218cc4bc65f5a0a531d683f5f4840fcdb68d2c67c26aa08bc0bc6bad8b2337644123314cb1ddd4081a99e50ef7a1580ef514974a3c1900485647b241ce630e78252ad061ba143652bc367b832a9a524b6c10650523286065f97b3a62e93005f2005a87f22d3d3a8081844b4653726d555b690a66029e705c6ce661fb1f91cd00ea7b806f5bbd3d58cf0b635ae204bea1e937472f1927b029fdb35643e2b5c29fe2bfe3a71945828d8263bcdac6612700898ee1de0a5c6081bb93dba5f2cbfeec38ea01f89753392f561af75702c0bfbbf4a1b2f1796f70fe9f1146bd1895769b61f67cbcb219ec3383908406978b340e6520bc85e7ebe0371f91a9691e3571daf314b78d00d0569b0c6ef1f67a0a2fee8786cbf267aa802eeaeb20b77acef6a039350ca99170672c27091f7a2a639b22464b3837ef0b2039f87eff79ac8476274ebe2f97404b1f636fa85f4e75d6b9ab5ff477af9e916087bbe8aa73f7b216024859d6bdd9da9e104260c847770226d2322a7057f3f08017c7d06dd0b310bfda24a4cf6681d87f97da779b81d93c5724444ae35c96a4c027b495ebc8b5fdd2c03b48d265bea1575df38a880c25577583fdb310019d85f068376fd24622bebd505b75636ea4afbee05d327a09334deefd8d29dfdaf89480f81ea313277b5771ea9e5b270a515d44215c0efe1dd52910c45bd16c5d72e430e7178c31af86354c9fcdd905226ac8189441e3fe8fb1cd3849dd4405e7bc966003267ed9f8246b68db6c28171f041f48144394292c9b932dc2dbde46d9137eb02 +generate_ring_signature f8a0a8f2d0ed89850bab5235d713ad6f5490b1118781ff11b4761d6a00386b2e 097f291d2a85c9f687b4f3f104d816a0c55b237c2fd80dbc9cf50c00d711dbed 49 3adc8af93e6e58ad23c1f5770b61005602941a8b2cb0ae9faca5546dfb0a0420 2b28a39becaf5ed07883bd704abf556d75425a812cabeee9aefba88b7f083247 c91136837480427acc69194032ff014ff024ead060a700b9632c951019191c42 419bff436a2407c99dd54df1fd81f03a4a7242185d1d053cd9047e3b8c5c853b 2a61cb879a72de3a8691b0070713c98ed956686528dec69ba4e59b39be283997 c916902de419814e20c2b54988b59827d8e51cbcaf40f8754aae614c0763c3a8 da592711b634489fcafe152a2ef68c2b7aa382d0bb1c0938c8287b2158ba20fd 116f133964515c727cf9732fb85cc32bc8c06ee80e196bc58dd74e27333669d7 c11229e062ed17bdc5691683f00e7b93d17de985ee56504d2c973ff623af5ef6 d766fa97192e22bea05b245beb08244b26d6d1fdc53d23552c25718864279b45 23d568f972426e091d5bf729795f4df85c372edc8596318930c571406b142b02 4eeeb87027885df4297c54d4ce1b0b0fc9f5d3cd9fcbcd117705143730b254a7 5d25fe0e77bd41b65c4886db6944acb5a6bed7124d161ee0db948278489941ba b5a4de6b2fa406fd06f95dd55474b28a65d9a1b7899b0df2bff0d88d7f101520 a49017f7be6c46a2761685ccdba9fadd9a2445890bb29f4f09a5823ff2c868d9 e2bf3549bba0b5d384ebf70d3a63b79708237a787fe3f5ceacea18046f8b717e ff5b2067283b3e78a69b90e14aa7c3f98702911988a00a3f84a861035bcccbec 592216f04c2ff85ff72e7a64522c2a72999a5a0de96e355d24c86426d3a8c97e 7374d3feb985bf4d7936cabb69930df5505f9b8fbd53fed52b8cb0f2a3c48f58 7deb1e6bb57de2fcda1a98f7ca6477083f18643c8a5d1d5a3f63a0aa2f184b02 caa94aeac9086e57eb961ccc264bf31b4a4fec167b53b63429631b7407b75386 6c1d32018baccdaad69b514de41a791c2f18e65bc61e1b113da435cc8018d510 9d9edc104a1fca6cbdbf93b0e8dc44c7e23138a75147c158166147795a7dd0c8 6b0696998bdc48e44e14fbe152d8f08949ce6c07a761af7a9da99d6c21dafc8c 431f6588bae47c2b4faefa99b29a8518b157ebfe1986cb05ecca8d58632cd221 5575b35813692ae8e70642e8e21247e5d8b87f85aaa7a329f01c3c570aac3342 26782e5030d9ecc7d1a12dea519d23e2ffb13f781797f513ba6ad8102c33d74e 035652e8514cc2de28299e1403c68b7e71f583e5683ab7e45b4f8f9e72f0c024 5e5c2014e6e6ddabe3b7793648f24088303650fdf61cd5a1dc046358cf27f2bc bdcac0dc358181c5a85cc8080a342bf74c8f98feaf26106afd8b56dc7a1fdc0f b547966ddc17dbd0f86b851410b2fd750ee48893a7e83c6d720925b95a8f2385 f070568da8ad68b79b0c71df3157049460a044cd5e3108b5143f04fa19cd66f3 ffdffc81d2aef3a6d82d6117391a60e828398089e7c83e2377f2c867485decb2 df6fd04e2690d093d3fbb0eb6c452ccd2f47404b3a96614dfc7f9d3cca29898f 8c7f58d07d2c8bc6d517b9b0e18b65375c734eaed7438965fea2243a3313e0e3 2bf00245c8cd152d26c583dad85e0f05c993d2608e0550f8f48db469e4a921fa 67b8acb9b56ab29df60b3af0bc6e8e408ec15f8645e380a3275cfde86ba6e73c d1e342d34788129616a369d4cd642d39e0d5ecba9f80e37c4b6c7bf6d600bb1e 4eca9f83a27f8cd9a62de8abd56ec65b9fd287252cae844acbc500e8bf65b5ee 398aaf7942d2f171a708bae3cfe5ff251029c38043813434c91800f45819877f 7750cb21816e9e092cc3d29d30877b96ea721ae7456e6067a6e9df1e4d3c0ee7 86c3a7459fedc19c0000afa48418889480b4873b229883ee1899b6cc646aa90e 90c4a9c48ff536b1da078dc19f46dbcf61147e803ef58cb96ead9ac657c8afed 726453b55f056e08d689c3d7048e9d59135a279547749983b21c8ed0fea72cb8 bf4065391a44c483ebaa0e5b43a5e1adf1a8349a5b026e7b423fe25da7458bd0 f420a68c8ea38929417bfe82ac1f6bb9455bfb01eb8afae64e4a6826254e6e71 d64de0a38ec78c940d4cab238736c25a25d7ca047160df7a08cd4e4840519d09 fd0e51f2ec0b7603ea1a3f7b4b4b3d7f8185d498729ac0b3f04bdf50ecbb7ef3 3ea66c0aff0ec765b6fab07728e2bd403bde5e2e3a21ef495debcbfaad6836d1 3926d196289e2bcb00b97021af69ef75fe54600c9d319ee48e84219ceddaa10b 48 bbe09176bf69a9c454bcd147426addf446213ca4ce8e52541986c5e4fe6f2f0c69707a28091e37c860dd4bb69c1d777e25a61f31bddf36abc522bd924c0e8107864ca6f43ba015739856089d05113cc775f1e6fd7cc682d00751c1db8eaf4509f02a143fc477d9821bf83b248741a6bb87e374ce165a0b12b01722a6cb211b0fa85580f7a8d0b7f01d75d7cf6772ef0e1404e659e6122bdd4caf13454c8c3f0d4b8924b401a68ccd5690dc96c30a51a260552d2172c0e4e1ab63664f8bc947069efb56fc4f6369245135788762d33ff03537d5b7df986e78d822a5b757e4970a0dd2c650713f36fab8950478083701b353648203e8639679d449865dba9a5002e05d4b30ac2c3b08809c4ad7fb768429c42e804192930d3248423ff5b4c1380903fce8e78c60b4e1c8ba6589a3c18e76a0a7ce0cce400eaca7d10f25f7489f042a4e5ac55b9ffd3df13aee62263fe8918b50c3a6d05a98b80bb219e2eb8cfe0ea81cf20362e76f1ffcdbc5e2e37ad14874ce28a01dbe2ef617f6699bfc02870fd029b39586c1c5a19e9320d7e32850149b3d7a4a9fd5ab2ebc59b013c5ced40df1738a22b12a2af93d59dbf01457d7ea9c78e6b38403d807c67852a9b4a7550bf230e7a7165bc5571d5aefa8bf06de25089d4530e93b4b7e86bd53be87594a0b24586cb03a1001d8a8a139a39fc24ad346a8e3e7410072b7b3ed59961d6b200de8aa32bb948c0dd1a66ffd23453888d3fd98338708134492de8462e15d61290df754d56f72da79599513abb8051063788f269aa178e5033fe77a1038dab8de055a49026063a1718771e41ad73d80c8af3be19f73277169ae7ac8afdb31c32702fd19530c12fb986f39368677458fc674bd9d19d8d26c1c76de4cef2769cfe107d7c821a8ec1c61db2a2f5216460ba6e3c0a29a9929a54695f42226473319ee0ba334c9bed53bc43dba9f77cb0f88a453fbbb3dc0fc3772e9574eb6b0756c380081cc7301b83aa9e696730bdb3abc3941731ba367fc5d7f29b43993a9a5fb570a6058aadb732e61f6afc71811b7c01455b0fb1e0ebcaea0cbfb6d36d9b5545206ce91cc0d683a389abb5d115f909b4b09f3032edc36e2ece537c32bcd0a91250fa08fed464f1b79cb593e7b3b8bab0a3aa286d57874051c3aaf8632d1c5ea250ef3394146aef5248ed08ebc76b784f2ac306eab5a7e4f0b0cd485c3cd7963e606d99119f21a30ef1f2f43c6571af00b3956f8bff3ab1b58f0f376f7aa8d5d2400e1704931a93f53db395b36dcfe0a3777896fc716b3114f4f53f3c2599ca7df06e6ae2c6429836be65e2c2c88ed21d5d3f37af84fd79fe31749c78938c7736f038a9efdbb1a79b476f8ffca744a439f5adaa07f68b90a94cbf00ad34a153cfa00fcd305f7f3bbc7a0ead0efb0a10e2d6861c87e1ef0a8cd6d1d2563be45d5370c989177d536715c2d473bec54116bbffcc29e5976d0fa6374ba1a7a56ba114204180a97b58df17fdaa204b838f678db45019fcc5efe7dad45802358cadb0fce0bbd95078e70ed85e0c0bdf9bed9b6cac576f26340b22dd7e4f94d0685bf140d078a31a9e084c60c9b7777f932fd7efe4d908ac2592d21ea87d0fd8c07244ee60d3a218164ca47b947746792a736b10726740616c43bbec2fbe4be3e3ee4299909ab44de3c4b902ba787595171cfec6fb643fd228c40c1bf869bd73703cc3d92008ebd07b8288d6ef6da6c19441557c6d038d3ee4445ddf26a98100778ddbd43044f5cd548b2d4482a5349ee7b087f543e0c69071382ff16d1129fc38c5d8ec50f76e228de2a7f180c7d6fd5a6ddd503dd7f96b5ac4af53e65148381e8d6d8ee06d2a82ce0d8bfff8910c865eee9455535d7181c359ee051622f02bd875a13e408d1da4bf14710f8aa4a06950e67659741edf0f7cbbbabcb05deb0be4b1762f0029335d26c994be5eb3110130917622fc1d97e1228b5942f0161ac955f1555060261f64b149c87bdc723ac4d0e33839074c556da7a668d9586912d289e6d54f4010bd19c428c53d14047b25cd1851f766fe5c7aefbcb8e084fedf2def5edd16601fab8db877eaeb788270151c60484bd8cbd9575a16c51d20e95a4f29fb7f78300ec96e6821d721205631a22a25d4df049da1f643cfec91d603ab1e0b6478dcb061dc0b514714ee432b1b035777acd2d34d634a4c5a6cac8f1327dd3f3a5e4f90113371bbb94fd81929488ae5097af514301477f85f79f32af161492e5c7fed706933eca47a92ba3f29f496c7e565537039fc9acc56743795a3310ef44b080ef027265698130ff6c8f75e410455b0c133828817b774cb13d476452d6781ea10004d4c56f4d627c9b30a46ba2dd1b880738361b3f813ec8421470d1246cb3f772011a2588b974fb22d8a7921d2f4eca2b3c23c69c2941d658646aa2220b904c9a029fa1c566227ff9a5fd45b22e6fcfa7117cbec87787a23406967a61d9dfc0790c5ac1d76fcad50f9ab94181ef0c3c0f36de34973ce3b32f8358e4244c0a113607c080cf9ff97d0b68941de6e75475f269672fbb07bce69b510216bbf2fd030e056d3ff095943d3d2e66f2c3beee4306a1de1207300cb99747e64be7154f79fe0686c94a41eb88214312ccddbd5f86d013551a261b9575c861d2a4cf5c5a81760850c228320d08422cb28a47de29c98ff9304974b322e62109b3614a7b5871cf0044371ae0425baf7956ba9ecc727d467c3a1f24cb6e3f87c26e01c415fd5a8905a081106c9b846dabd5b61a36232659f5d2ee5a0f9c96ed1ae9164d5c69aad70552523f92065410f871501128fea84dcac204db1f1887dbb34d6773824fe590084e2b04a7c22ef1e02ec19dd23ced02daeaf1873af995491bbb4ea455d48039088fbf11e86aeb4642f1b4f2e6c43b8d2247bdb94708bb6794a27098890cdd230dfc78287cb35892018c017c0d6fcc1a2f45585e63c4550c976637c2a3739a2f0ad83770ba4a4042b5a2f2ad653d51ddc670f36a6e258de3306f78d467d5decd02b32314a410fdd37fc0bd1936c6a0757eec77999cb7e0468a3a3cff43557bda0359d8c409f88a365d26befefb3aa7fd86bc7259bd3f2add5342f70512d8a0a90c3135a9e61d4383daae8b94df546a12693290bf276f99808bd0a57be766c11006d94d4abe695665ff2f5ee8f3ccb46f9352b16b6946ec93f59c3ee45afed6630d0fe0bbde7156efedd327259f86f4824ec3501810b809fbe2586cc449591c5001af8022e22eec1d2237fac63dfc9c7a0881b59c580f42cf748be0298540cf8d0aa2775a4c3498dac83fa5e5580036f5442f4876d3e6646445c9b1ec35c4e312009d4471c646bb2376854647e9ab13639b311dfdb4f9dfe72b467a32f8e86cff0866d8567e7ea74a25ba0918646025c63df98874043e6749d08b3ef0939dfe040155738348b85015ef34064cf4c224538a7af23c3a6a300d38dd9f9611d3f9ba015739951e7602f1e18e3921570aaa58093c570223022f228dee5a464d4cfd3d0096e310746cae558c6140e966c41312b1d7d8380336669633f5ddf6454eb97a06598b0588bc3c5fdbdf15f1612d1dfaceca8d5a0632f098d11cf70f09986bdb0319ec0649c80b4f43901c95a1d107650f19db1161a101cea3860fcecf25d3e109bb20ef99e8cb8672b3bb895f41934e406a97b2491addc6d0be4c3ceef6e0180d0d8f7a17675ee4b1748b60be414e4e2aa3c307caf4a288f4799e9e1f98dcf90a16c4f76dce256cf2dae6303e2a4d88df9b4157cb018ce5336db118d425368106ef609f194fead7e73780a96337c223dd3c9751937108bd026c28d61675f5e70742d561f7e6ec62c11cbfa9674e6e9efc5efec1abad297cb37594a6938fcf9405ea95b08d1f86c6f080792295164c96164a603408f9e6dd542c02d9d88af06909fd335961c3ce13dd3e2735b2df55299d74275215901c19cbff99b196070ae504b72f4d04c48f277c9f953745a387804251e9aab855b68f097baae974eb4c710a283be9229daf4abf87ab8ef76aabc150dd2fb5d4eddc9a4738b120f29c5bb10009172601daf6e2ab0a9cb605574983fbe0d13e118e17f0d0fa0a6e7aa447f60488ace0f308a615bd293a350fa353caf8f49f22c66019da41c8ff01bde33ff000df0d19783f8dc840008d1859e31a09b696ec5f9667e857d84022f0ff907b4801d5b520e4f0aef40e1701f9b03f490b75515fb8607a16dac4aacae4de82ffcb026d3c806875c108ba2d5d636d8cdb702bc344882ebdc769f157871851a35d6e0335a5b4478c153cde155455114d1a4fe6d3b9623e994a42c07c7fe05e45d1670a350138fef1d1ac7c37edd663fe3e43d0552dea2d20e532e2f37b0f65ee8a96029f2fdb414916f37aeb5feeb42e8e1e8ac85e173ddc4649ea4275af89b5355f0a +generate_ring_signature 4a2f02d89b53e39106201616a9b7cefdff468a16e25d2da32d6252711d4b720b af571c8e094bcfaab7cf153058d1fe0734759f0d708d47eac8a861fafc6f1d1f 7 95beb14be21bd1299ff9010f720b9b19325debe854590150a1752ffb4986ea03 6758b03e05a97b10b0b36a90259419b82c9844117880aa9fd2a0aa48d9d60695 11e79f07d67791263e12d28d14402a35d8a23106a50231cebb7954005f4ad5f5 23e984bc55bda712b246442286a76920e00b07076f9fe9e43059f8515616a394 0797656f7a6834f5d3a6d81f4736c578e29933c80b0e6002586765a9206a1011 d448df9a487b26b22b3407a355ebeb0a1eb598bfa0b4f4181ed41e28f10b800d b5eaef27ba67ebe44eb71614c345b823b46eab30f9cad613373bd33bc4debfd9 b602261b8b7949e2168946c6340b5692fa3642590f8ab37c23da4d22bb67a504 0 ad384ad50f71ed2cf5a1e520402a3a39f0198d1205103c1ed1829336dc66df02fb4706ec20e6228c84ee0239188b04a037c449bde84f2be813d8b0b547c72e0ff1dfb4f51830eb4b07074010c43c4cc74d9e37106650798159650edd7bd3a9045e4d4e3b8a7c597be23ac012d3beb75417a787562d822305400454684fd699058960e394a4acd397332f53174b9c2c727c0456b546389cc8973d0a42265600081f032d549a09ffad9e3d2ca10621905aa42d4f84c757f68fe235fd41972e98015f1693073ecc81b02a294b786d4f3cbea8b6cbdfd801bf8ade73cf85a233270487a7e7b84ad3207c56bcdccbbc1123a4d860a47d3fd47d0c44e184b417b19f09976eb746b2e08ae6512d1ad76e56798e8bb3a3c7fe1ef14767befdfe4b8ac708fa3296cd78d4264a8af618ce5c33312ad1a353e7521e095b0bed87e6d9011e04605f19df82f93213d978df322e35c851cc5d8f3fad8b383b0859c484267b04016b1e7c59af034548a5c4c1ea3493db30b8be7e2502a9b56823efe50925580c0cb64aa97227b0be60f69a86db97062fd0c4fe237e2a4618c03b589393db3cf709f8c1385384c6aedefa3d7e31f676d2e0574b50da4482da723149234a207d0904 +generate_ring_signature 61d0c822334dca1dce7c18de620c378b4cdf3c6887c6356fb9f3e514b1d7d28b 0201e20bcfe1d5b8b1dc63ce09cb3d1d5883932592f1ffc02d5e9dde818bd567 2 74854563117e2a6caf335d0a93610d482adb470d5ef448fd07719f92d28834d6 8057854175fd84a9b67f60f7dfb176c1b5cb299b42ace904edb6f7624cafd6aa 3b9735ddce1da06a44d249590e669a206320590e4a64b2471bbac54a5d53010e 1 d614f183cecf48e5d60ff4ea092a549cb8176bb70b3e8a3bb8d962a6b7f65e02f8e6b80a68096fd1676840d0867f7015e5c4814e6c27b171e74ede5561dbb30f880bea448fb0d23880cfbbdf447d060226ae0bd4228aaba33996259c7df76908b243c3ef0357c24818040db0f3e4d89e97f8edfc2d0497c545566828cff1f100 +generate_ring_signature ba991fccd60954e43f95758637a5109f53e0016dacb3977384e3a5629ee30e7b 129fed96c0b093802f1b23e499f993a33633406827cfe472012296ed88b83b20 13 b9008901f4302cf6a549d05b4d94a1a6a35fc9169c05db04384cf9fd8bf4da7e 5001888418f85ea9a9c84d33129362ed5da819d12364cd87da355fc643b4f12d 908451d378012cea1456872f99ac29ade3583f93305783b81f511bb6cd4603fd bf1c0befe57a8b9c396e9d44c0d448d4acfa50a4e6339b16d38bd60d8b2f7eb3 de2f5fb9e022f5bb70485eed93fe9fbdf8f156da873d25ef98187240ff788494 36e1699e463d824bc29d3b265689410371c6805e9921b4d8fc2970a89dd977cc 59e684f2ead00200ff47eecc60548e0ebe5bf0f999d888f438096f8145288f55 cea7c5add8ab16406561fbf0afd738827c8850655584b6dfa5d1468379bb2875 000b6b76b4c2d636f7f894d6805f1e522d6d195dcd550601f80480da01d4d827 b03c699220c30b864095d2942ba9681baeea4358eae718651b3236a243bcd451 8869f5967b2a108072d99fdae101237396035ef1f71606bda8502b2622d9d4c9 48b6f52241fd0cd366cbb6ffcbfb65384ab8280381e9bf0ef2d8ef263ff892b8 52f46cb07371b11f042c4a4a10a0dea162ab6be5f50cc013d7102935814c9450 73264a80969bc591ef1c28147912c24bae46abd49496f2619d507086256c170c 4 e5eb76b950cf02497250a2599875e6f22640dc5a0c976f3f11e53fc2c88cff0149253c4b2efabb335a8579bff8422d37a50f535273c7c0acdbb7e6568459cf0500564f8914a07167083e89582383958786a4c17fd804606b1e03630271318c05e44525b21ca658110ee05bef7140b906026e273eef934a750c276e27b1edc7018c6aae78c6129cbd10888d137cd1230ec3b31c483b9809621dc3fb9b0a523a0ee37e047bc98d06c1e75f5cc7e7226fd16fbd55e1b76e956874f6720dddfce908816a9bb10c6e3a0538c9408dc110d5166dd61e14a5dd4e4f9d13d6e2f6e0ff056e1a7fe7c7dfc3bb74170d816379bf0deb539bc21e418b0ba1904a0316ec390642658a9e63b4a3ce5d1b070b8ab9be9f0505bcd6f40f5880ce79e7d80a7be50dd2ad6ed975040919e5ac5922ce6f0f7b7a23a473db180c2a5eb953d454b19004d1b508329483f1d5942574fb381a6dc16c12be9ba638090093d191c0f475d5013e49919e30d003efeff8e4d9e2c92d94ee018486338ac6009bbe15bce2b101049745b799a128b1cb0ce3c4a60cd2cf15eb120665cca12f63166b180c62eea50920785ff0fcc3e785d2191cc23356c2411c36cc265f7aef4b17a5858b26c81400311e2737f983a00cda3da11fd51460a730e2ced45330ad28831bfcf835c8cd00e0ff1bb15b1466083b8dc250c07c10c30760358969d17404050e267da5c7a00c70cc59e71bd8d12bebff157a960ebce2c0304606ecde99edc82831db3f08d405aa18a0ccec3bc4ae46433bbd82125eadece7ddbb1a74ca0140f16f9a0befb80d0e594cf3a2313e62f0a4df1f3ff771ea7a9f5b7d15586cf9135f3a969a8d060442e5e2ad7d0f9c8b2c224ab09f342faeea2813a5ea46b9ef543c21cdb173680537f3b70175eecf00709af0ebcb0aac0d42b2df3197b19cce5cbba83952cece08055bd4c973bc815c0e65287a2a2d8cfc8a6f5841d5528e7de19e3e7c0343d302f3a006372ec9c517e68033c8e9b6214db063bb4116e77197960b493a64021108fc3e9ffc062381f21f53b061a8898612c6fe61dd4c70cd303d236a4639579205c3a19d6e682e6ac19b8430954bf8e519284a297477a6aebfc3257272102b5701c8ac36b1928fc1705d4dbbd72133af7332c3cb1e3beaed129eba8ce1b5305f0e +generate_ring_signature df60ad01411f86ab5115b04f36a944fa5417b285acbc726671724332b2a42c72 1718ec920efb0136fc7bd862790e0be707af9c74ae2845e04a1cd8c5a0c7c2d5 45 2be1b73a749a573fbf03134f240d17392163e646927336d2a60949077a7a355c 78bd62b3b965b00b521868b4b16b51e2556ffa35c22fdb215fcc266346bed038 8466f3093f74529febbcf7e67c34a8fa89793fdc4499d013913a6a3bb6cc4734 59142c773f15c4754cd76925c1e36a5153e6bce2ad86e86988fa87096953bc91 f619696dc02e36ee18f2b0a0f072c8f280737617c9f01b02b70aa9c0125f3746 d1b9fbe2e71a53d72d72617391c36889107c46d493764404346230eae7d8fd29 6da4e77aa978fe44e450ab0e299d275d1fb2ee744fc6a00afca97a9f47d9b653 fec9985b4f72848dafd4a84036c0cd4e4733b9d3df462c54fa8f85cc58291476 d74b8f125da42f61eaddf08926ac42ed67c869261a4c63347b0c4258f96064a6 666706b036c5db6a9dcb5f3a9d40e671c987dfd37cca4acb76be8ee02544b1d1 c727e8fc5aff61e285a744e8149800cd7b8755881bf8d79b9b57dbe953bd7eef 4d79532fdf7f46763dc52c95fca0c664f14465350c21bad5bcac30961335407b 5b32cd6340a8b8349285d37b0421dc22e2290d08d763df18b60ee82c7880eea9 94565bf7fedee2ba501ebc492515b10c31843320a52091bf963c201db7e10901 54a2194a5ce4ffcd1c2e3d92f8a4343816c98f8609418b495379b1f5d5c8b2ba b41ae9c67867dd5851cc354a8e8598527519aaf42cb68ad6f182018a11acbd36 344d5e90889bd9e98759ab90e2c2c169060a7c3d90fb4bafe0e4b80a85c0e27f 6995c0965791b75298790cdaccbc594b9cc1208d775e3edd95170b5b368ce879 1a7b17e1c92b0f6a30f4df4607a4af8c38a8ca0ae76a565d01cf7654739244c2 c26ea672af690ce3588057a63fb353626d5740b3069b452f4f91a2b8b51307da 8fe0986d9ffc024fce84055309ae60621794605b01c06458e3354d248233239a 94c58f09986073d67f328efb792ccb10ff46342edcc974a030feb93116fe87cb 0901c0727335d4cacce1d0c9f407114b0064eaa227573f32f3f820be36a1fabf 2192b49eeeadc58bf2aa354ea9296f666784f85b20e1c6a5aeeea5db84c45b01 c73235e38a0387943297d2d01aa1707c2ed742ff32faef205994d8af19eecaa7 a1d7462a4bb6ac24f806f761958c5889e8647a6c60a3603e4e4d1e628c9620a1 598aa967908cd1afa59b96dc915e15b323ab9bf8b22452bee4348f20e513ffa8 0c1b0d8d72adab87749df3d3c2f4d4bde9b25580ebedea827e69a633c8a771f3 b2c2f2d69f76f598bc6f39195b638262a3f25e1ebba77efc78dc170f7c9cd4b5 81d8a737ad2c30c75ab5f740df6b83f8c8bf376ee0a6087efa54df55a9f504f1 48123827e43bcbec5286e251b9df9f4c68646ed5eef5d64571e89a5f3b8db296 b46043d69c854efe78f3f67474def70a083a354267919365f394651fe3adaac3 b17e84eaea1e674aafebefc57d732dd9e4dc57ae672f6691bb5adeef2f47e8eb 9c5fa37deb88303fcbdc67da748cf732eeb7eec907d09296a410cde7c2dc5c97 887ccdb80fe2cba2ce4223f34a2b11f3046cac6ef1edbe9b5bd23e0cc60c42d3 f4b91c1f163ec7cea4db20e45622aa762a5cde3189f3bcecbd4ab3a7f6282bf9 f6868d9169275243680eaa8e0a512e2325e7b29b31fed03959b0d7502cf92bf4 2f88677539825bdfd5c3907811d90e96652c2b02432aef5c306a674ab2df9a90 f46829b1ca728c6a61429aca9e26d219f02d676ba933ef36f9133d4d1798f489 fef6fc7115a1b6eda90bb1e7ecfb2150d6ffbaaa09fad08bde4e09256aee77c0 2b2f24e64b02a6799402a629ec7bbca184fa76d61ce99e0d9ffa2166fd383eb3 9e2282d8cd6c3959df478f6ec98cd3439bed7cb3fb89ece8c29e0bbcbf89fad3 49412f14773123b2444960ea4a8a28e08967793c965f481a034e7ce556e1121a f810b32590baca1728907b250256707afc9a501c8aa8446e548d88928e20e761 d9e008b024ff7a0a06f62940155f9efa97294ef063b9c579cc7e6d59dd54f73c 886b7ebeb8cd6dc9ba435d4aa426500957f0bfd2a1c138c9dcc322d49b41f70c 21 340451de1f72c3d88187c735010dedc50a4eb1f23882fb797087548ecfc9e901a7eadd884ac8e506abd6a316fec835fbe74c582ec341f5fa360ca4def1accf06b7fe2e52880d4c81d0ae98cd60ba91a3312943eaf9b90b516419446cf2e50c0a9aabf240bb13d2aa12380b5931364e037cf8c179afd6559d2bd857ca8d0dad00829fa12a8f88ef09de46a81809bb6a15877032567d0f39475f71b74103b11a0322d4c0782888915edbb03931c2111f702a1a48eab008bd64d715f73a3e366c00b07cfceca8a2cc9a6d60e6ae3086c62f5a52261ad389b5cd4be4945c193cf2041f102034e102cf5fd5529f85f4f8fc6eae2ebcebfbcf02ca48a6f7df1314ae0b95a6c8160b496bef5bf958eafa11c2946cfb778cb9d27da2f550e87ade2a8f06f73704ffc27bca79dd6a6b0b9774003cd5034a02ce25051fa683012ad227300758c433757b5b9dc44bd4ec639aace54b1d2c88dc34196670c198c830e54a5b035c0c1230f6b06ad2fab77079780a682fb48ad1ca6ea412376eac56d7987a340ca6575ae059ac7de1fde59af1934d3e03a6aa684c4f13670c7bdc966fd238b2033729d7411495bbdca215a81eacfe55498db0965adbf2c717d3520c43a784170293022fdafc2a267c3e6c6e1f7f139e0a82e08db9b23d216ddd8d9ecb81b7bb041d3082714ce3467959d05deb3e2eb8d9a69cce999ad186c5a1c5e960768a3a09699c155bb70fce383aa52fa1310cb8d1e92a4434810bf36248b535ceccda890e95dffd6ebc5902d0e2874566d84b1701d0abee21740181250b1d5673f099da0e68b2302656ca2ad866e302a5307d00796bb15d36b9b58b503c17a7c21a17d40f04ab0e25b51c10e9be2a7c860dd1713e4e4f18141ccfdb2f307ca1281af6e409b72c23c52725024860e7b8e3daaab89005f64954029d02ec80654f0e79368c02d2605dc9eba7c8e35c1a60582a1aee587fbd7b3747639772f740b4eb9ab522038b3603de31a183e8711fa19ceaf13a6b069abee09684e1b64a9600302700a809ff3837befed4c8d18c48f86382a82187ff0cd2c7b1abf704d7e61e1d219b56011f3f286896bf6aaeb16ed90dc51ec6f28cdbcfeeb6672b0a2f36e387211ee201b86441aa81abb09ca4be385718a7bd9f8284141a5385bc2207961451e31466052637980bc8ae1c42a274702638f173ba80a62c037037bdc46ec1636180cbcc05af4bf689d2d36349474e8748ea7d4cb143c1890335272074bbca0a7cf6b5bd0c823967cd12823dafcba95c7d7e9eea148cc466c018197fd70a9a17d2a5f3ba0db7fe803145a4dad1e8b39cea243ffc73149753773dba07a7aeaa344662bc670806cfafb536c9eeb054d3b6b339c3f1eacaa2859ed0f11faa26a3a8fee0ea330cf455f39054b9b712922835b4c4c52166d94d84355849aa7162962ac22ff678071c28a993adfbdacac80cf2901b30b9d3ac1c62bb68c37d7927d7d11bc0c662018373b34f3eefdf4a8018ca6bcf69ec1ed2b9626bfe33d6c10633a6be4770b60f963f37ec88de52b3a7b5809b8ee19a3a81e3c5c6e70cda5cee480ed4f59ae303f06600f585d542a8d4583d068225e726013f9eb4f27a416b02fe2de2fd4531018d14a389d4dc3189d3dcb7ab7b69cd85a3f6ff4e40ae2697e1b5a4a9d0ed10018d3eef23c367727e43029bd3c5c84d5864eb2626bdf1879499e178b110409b0cae786f5ba20444ec0f446a23a02dddaa99babaa38f3e56d56869a8b835d4830483f606ef8f3701cd00cefc634eab52140cb350386c9c50b063223eba954485031d537f552b595d941c7d0f2573ecc5829b0886f5602e03708ed509bcc6d6ee0bd3096b106f32819553aff3942f74b05856fc0cf581b13796051ed61ad9d15707701150b4c2036a9218e319592afca8f8bb6f4190ea52fabd0a13312a4857e301ee84480d1cc07112219d441b2a887816aaf4f9ff941607ebad2f97e954f80a04457998e96a1de693369fe1f00fb540633c54ab9f1072e8ae71e5839c73595504187d45f80ce7367134ae1720b9a174dc991c4b66230edb09f9ba845339a16a0322054eb65d77499dd443b66939ca3682ce1a0261ea91bea69ccd101b23f9f5035f4bbddadcb3b49dd25aa7285d20d9293ae5852ebc2822e12f1928b9d7ba8009758d419412817cd5fbb00843334042d6cbd80b73fce1e199da86cbc6277b1c0678379ff8271a3816655dc1562d44bbe8ad0c55518b73d017615d9c2dc0530400ede1452f6b7d685f8bcd99059c3c19463c56d55a6c71f9aafb42057b04b531038566f473d4e4d06d630d98d37ce4c310afea18099655df1a441646fe23b5d20501b37f1eb378de0199bd94503f83fbf5cb98a50c3dbf8b5d269f1120da3a0a03cdbfa8c7ec79e4e495ed892a13e66266967ab9884469b1da19014d29c6d5b10379d5bf52f9985bf9fb6e35157995f919c69c74bc7a11aaf33fcb61d05e79dc0dba3706efda44711cfe0cc6271b28b0dbc99abafb31f3387a48428f00022e99073990a3b0069432c4d48fae84544cd0ab2c2b8f5e9737f4fd77c2dab2bb807b003c0ecc2a2cf81c74a37f3bdf48b1b49ec51384b44f9d2a1ca3865dc53388520e6be38d23f525f1f1fb4582fe62577befe73424e151c1b34de48d758730981603a7e6c0d10af91fc4a0ff2469fac6a30f5cf08a6c991e8039f03a235cd4b1180ed80f07263646a45034bb3f49c79de6d1b23c37d0ffd03a1d04bee130720e720079a4f3ada0a9f850b46e031d17a5c2a31f6c9940e060cfb0f1e5bae2461ce105c65ebd00fa0332419160c38fddbd7ad775a80b56ccc9c05e0155af34ba6a840fb8b95d57e12cbafb5f1760245f1368ecc1f156d578e2441a09393b2c4abae30dc693fe4df79524076b91ecbecf5c921a5105042fbe72c4c7aa9c106b9d30740a458c215986e9fc6044d5db099b8da8239b1a6e9085014b4f635860e30b6e220bde8bedaa7466ab96c31b5d52f528b9e74a1134e9609cf0640abc1ad98d33f50db641be2fbaf0b48c2975c01af7380f292792eee601c00b7d90562f74f3c1780fee823dddceac2d1c71943df3c2bf6c978cef4110d17158edf76aa851ff44f606703a05af7801090fce02b21653a6ab04ab2a8b215b053389ac3922b282b2290fc03fbdb7240feacfa1b9db9cbab9c023cbbec3b571c758dc3aaeb79220b559082d37cce0cedfca3493347d691395ba1c0efef5740cfa01b3f175ab64cbb7ed0e87188f24ae7617b3de43af067d49b0e61447c5c076b0e9e5933635f8769ed5033a5d13b0e40e3293efe0211f12020992194f5a0c1e1ef15891c2518b2d20c70bd148156d0b6f063f5f6cc7c8790a76e8188d5e059224b877dbb01b987aa2a00d9cc6c74bb058ffc748be0835ef6359e37a671acaa309d0ee880d27e1bdc4bd09031bbdc49e2257857cb8902e5d3c3c4723d8121c40d1f606357a2a2b50ce0708fbda50324182da7988891ab0094d331eaaf1dac9ad77692c21edab579a0e980cac88a7c3cb40654b0449747ed90f330391802fa4d535605c3efdaeb609eafa0b8ce1065b8d69280a5c2cd47ac139802109dd07a9496cd3e358b43618cebea706e03177b5099cf379e6e7db8197fb7cf23c65b37eed77fee5c82933dc2153c8094028e102d337102d210a0332edb18eae1b6800907f7abe70a812cf574135f405401829684920f1d116d53807202f8b3ee4ce34b92150ce9edfe2be977d02910cfdc16f1bfea3828cf5c3aa483c691fab1b8eebe7aec1919017384fc2bec4c50182b4fcafd2c3101d125165244a3ba5b1e1ab56d99df9db66ac4a1cc93b39750d338d88ab3b7f9df6410ff6a1d15ca8c0592c4417401af9106658d2b074fdc906267b6cf6e361ba17669c4214be8f67d9e0130eef08a0fada6e32a9004fdb5c012d666e4d0768fb6058cc68d8b21555bfb6ab9a3fb25db3915706949508fe5e0db36ce4b75fa97bdc10df5e534dc84f6270fc7ee8eb5c887a4f1c77c984f3850d0e5da8f87404f970eaf5889d96207ce12f9b5ed962af4ae7c954b43be4020100 +generate_ring_signature b7947526256356ae38da9d34bc7c86a9b174b8813d425e864d7800ec79d88af8 aec1b4594dfd4008cbae93762a011150f3c7386270f6cf3e95d101d9df19526d 55 163bd4af479881e814be8c9f39765eaed771872143bc3102e9edf5df3f171ef4 0395dba59e987413b7f654bdd1954108efad8c1ce598e7cb63770860be385ca4 41ad1010c0fd6c2b45c59944ba75ca3a08f391de421ea4e1cac9520485778b4e 1e8f6bd435daac24e81e86215031e9e0e4f376ac3e7f89d5722df9a7c9c1bf51 1966218b316f4bb52fd8353b4ffe253a132b88e50df9ca310699da2d2581d021 b29a06dcc0fe3f5ba2aa49c9a63482242b424cb91808858f2b86c904d901ca64 848c3bf8c696e7533e04449fce7bf24bc6f4e08ed7f5e6f38467ae2347f4659a 433b8b0eea9aa5ee651fff2beea8e4b614c650d83a62c1938403629e93e5d66c c13d34fb3e4cdb32ee71bb54fd356fb1a18c9a1b90777085c5abb6349fe0ddff dd7a3af31a66a281066e1714931d5e6dc746914fd99bd2cc14a44b5e6ac076e3 f51f9815f624fd3a551c6f27d0d259e7224714a23bd2ef97eb59b94fe30a08a0 86f096f7afc188b56a4d8380595a67d3e82c22bd82b45db57d5082293d52ebed 0a3b56105fa562960d4630bc97cc9e85b6fa4cadf36122b262d92ca824c74019 8fe3babf30601bba0bc3fcbfe8b8d9e7ff8f01a15c306437b6f72cc3bc1f8fa5 1426632cf1b6fc7a089c8e574a9ed25a84bbd6c18159a1cec2a3fd1e8847d42c 1e958da0b1f66d59bf51641251bc5798b908799c555af6415e1b21ab753b5ab3 d1e51dd21b9f25166060c6f91e0e3ccb67182a41f1c980a76b013061b95271a6 1dd0a6397324b33934534dd840afe815f2addf0ea424439c35ab03bb437c3c75 ce5097b0742949a1a8d30505e4a14c692179bbf32dac5cc02737ad1d83baf8b7 93dcc28ea00494b9b6c9ef595089e42324c3c9a6bb7f1b181739fecb26d15685 7579aab49e4246f39c9fae3dfe38462841da99775b354179d887f006b5ef0848 9c0f7cb6e5c2f03f1ae87a09aef59edfe0e32b0799ad615d82bac656c680ff24 20b77148672e9010b8eb9c61dfce5459c8a507644a3dfacedfd619cf7e670904 67dc29b478b36ea0730d8cf755e065b5814452d080c67043ab772d0f7b9244c5 aac1d23d54b480daadb1fb7be43a7b66f9f23382c60c0918480d81c9bda39888 017f46be24cd6ffea9fd727ca533f848cd7acbdebdc7b663114503b6b11c1b5f ab326e5d6d6812d82e4f937cf3e3dcd3e88636b047f8fe21b2c4aefb883510f3 1c8672d21d1fc9ee66f6fe71302ef54e60731793c4ace673ebd572af725d981f 6531efa2633ca746693a680131aad78bcc068705f800d89a61bea760281aebeb f0fcd8d3ecabac3e53210cca019ee342479bed4c111fcdce20b5ae80122396ba 962d15c55d3c0d7a822b7abf6bab97472a5e9b866687e898aab298e2515ded7c b9adaea9b5371d33769f7804d2d908a892efbfa1f35592cf1fac824fb571865c 3648eafb623ba218a06b3e6e1cf2aba0279fff5904126df60aea32745d797f38 6eab30b6830743073bca92724563c92f46456be65fbc1a4e2a489746efa95aa3 dec7602d3b109a6adcc18fe5a6f38c890d686ad588d0b230a32b3d7664f6112f a3e253729f9b6b189e923f6a5cb3b36163b06e8e2e5c1c6d685cd272ca0ed4a7 6171faaf0ee551b7c2dad4b1a4edac2239be4e7fe76479cfaac59551deb88085 ad2252475eef5dbfd933d3240670d46bedda452b1b123a88a259fd581094aed4 ccb474000bb2c8c79215a7c3757200a9f21ee3a1b1300eb2e5719b7998cb6b43 abb067218b8dde421e9de47a0d22340f464cc244c9c4c13982a988654eb62e9e 58cd307a14f6482f0c00d9342fc0b08776deb879c567b53c9a5ecd58d963640c 99f9c2d925b883da8e5c365c32e79f6503587b5e45dc43db9a674ce49ea4f74b 1a28f5e448a55f1b17e964763401fc8ac296807a8079907e86c2d8e6ac9869aa eb56ebb9049870bbf2c68ed50c74c74f738c282da33a5897213c7e305826b1d0 e9a71c4f7b38d6e32f455107a814d6ccab122a3d027f320ca36f072f58caf025 f1e5f1017b7bbebc512f895666f18e3bd3964e430a60c8db576cf27b865e72a7 50d5bef941cea97a3ca5587654794b2cc95e4ace85043435632d6fc9e6c239d7 bcbef4d31f101535e835fdebab9a6bb7b869f4a01f718cdfc9536d2ead231708 4f1c9064fd2337d653c08109662a533ca1ba3adf29f71ad52fec1d581df0d6db b92bdeb2edf45a86b82e16ebef73a9969a4fcf16e7d598b809485b1f7e03c20f c1ecb1756ff4cfcc965c8759d57919fc345e2b02d46b3042d332389f97ac0e1f 38c8da9e1841686caccb994796b0d462db73db8bd5b023f47438da226212d5e6 a32ab16d92dbd427cbb0a9edad6469fe5586aa4c8f447cd49d92b4901f283be1 f7df3b4c2921de09b3dd5bbfa015fc5910f67a1807c627ade7e9237450b9a1d9 71e6906758e41dcc42a26fa4e8b4ee034bbeb574c4798114a6d8c9b3697eb43e f7426e48e2419cbd11062b5ec30a6353ee48e67ec3a185abb39ed2ae6c201b0b 37 6306223916f582ab76de2c92fe78bacd5c14b30236357da5a6bb84625f54bd045105ecc02f042cab9e6cb046b53d112a14f30124d5ed52fa01c0cee9ed631c0c7995eda8197d0d455426a53a8ed3306639d65de72d66987a3a4452765c47180c98c42d7515795c2ffad166d926e6701ee8b57c4be90485e0d6fe0a59ed2666076cecef45cae18ced4775a34f1b58ccabe2dbd4fa082d3b3eed15d6fc71c67f0f74ae2bba1aa26ec6cb3bdead6909dcda77685e0a3f8f63e551a2957b526452037153125a0280346e47a006b5ed111400250487eb0af477ec12a23e6385d61e052a13f6f0433ec4bfce14d3dfae30f40296b303c65ffbd3e5fdbefb2d54b58902298fa13bc4f971f513c9b47377e90fc4b7ba6e8df49be1a0d219f59c6de2f80f53dc3408a1ccc0711bbf72fcea1f8c7e90201a5278202419e3b67e144c2abf049c072625736a93ed4a6d4c6e6893dcfccad7bee84f5975044311d627418254079c6ac6758e1d62fccf3271e70e2130019cb74e9520c3e312b6263a6a6df49f0335838fcb286004c6277ab807af82dfe30a8e3fe7cd38fa9f1bd2925c01d3e7008be43a7a3a0c944bfc0a08a42f359692d4b4f8ba67b66c95cedc96ca4c10220ab5e4c97df182d3e4c8c43adaffee90c96cb577eccb6970d399f7a22382b53e059d575030fcc88e8fee28a8edf1bbcda125d6c5fcb5cca4d0e252eedb2fdf58067552e76d6acb53106546b0c0f5b16b8604711f2f94c850ef43d87a91d221d40002baee91bebc7142bb44d6eff871b685720da92e504fa7cfdff2b688e026a00cf3092cdfbd22aa77f442601939c822b5a211b80d9bec600b03627458f69dc80b5c932425d1ab76be1d60e2235876b211ed6a1bd19c84fd543f2887b3f31d3d09096377564ba17be5d7a1ff315528bc4a310ba5b790d61d70372f4b8ddd2d3208dd1228c63c1f9d4c217be9cb50147e507c15ad3ee29eee89bfab26fcd61ee106dba860a678f5352ebf1d9da6113766eb2432b2fbfb26022b659046c08dc1f3087b265d7f3566f0e2067f207522069c83ce7e16a3b746ed8f789ad9030a714701c5b0ea2575b6708a67351da7ba690dbf86c19a4194c025dc7cdf0164de1d430db29dd64913e13c0cdded960ab5e31d2255a83c6895682f0eb809a519484136080dc86e26d9c63d9ffe3a3bbf1d5e60749b58563db5c5c891505c449ef398ea0233dae625c67d0886198db0f456526fc42bc8194a511c3e18249ecbb12b789f09a8eee5222720dcb4b995905b0868b4cdf02a0debc7eca6602e8645f664594301706e7a237f9bb7202c4663a422bae1dc7e9efbd5b73ef2fd5500718b10da2f006db9003b59fa88736c2068df1401b6a35ae4ea315e9225811baee801c2994f09945cfb581cf43095258d0aa56f1b2c8e3587353a152f75fc587d8cb49aec0e0aa8f55fa127d759d7b10239add3673cd0f43bab0abf2b25a49646c73654c7d30b7f390f9e26f07e2bbdda3b4116ec064bd9ad2389230b81576757f630977c5c0566a6f903bc4ffaf88e4991a9aa38f004162421cb7ffe2c8bda2649eca2aa770c58c43c2001241000b2fa189589d721b64a1f8a77b655bb2263d73aff17412e0dc17f72962927b0482168f32fb4e982ffd7c4850e7d44b0c0ed0722300cc61d02b7b3daf5564d15e43c2c6264814a05aec13317eab65d64b91ff6982e5ebb7a076ffab8882e0417cf221c37a19410e671b260e214344e8bc54d75900d4720a40a57921bf11e12bee2a4147db8c2892d462cc7e73118f52c5e6bf3b69ece95b00836305f78e29e2b1b1c61a1508b16ccc38aa42cc013c7107e7aa0f8496bc0cc0cbd872a5ec4e43b7954468eada591635978bd28e312c9d5c2ced6d6a7de80160528d7642041b37068f15b770a53f61f9c0be8bddcd0cb7382df8903c9e98acf09000e6d3dc172fcb203fc77afcfb4ff5a4f2f7833e86c8c9e8246d4329b0dca0c8666df50301f726824ae476c894f607b986c16b9cc42da70014410a1ff99260d4f511252411e3b91b1f54192bf5da2ab56b96c5f08da3b6b46992b20b8f1ec04d60347a252012c3b1565a3209dddc9f77a2aa2c0bcebbcb0d4647ea57b739a00eb9bb291c7217c303780dd8051689e707424b238cfc24af046324f0756a5c90c539762ddf46a7f90eac285816cd76e0d67a1a3e000796bcfcc047e39f9e4860bf97772f51673599321bf5ae22669ce5f29d3d394e726b86ad3c737b238724d0fe7740688be5254f397b59650f8c1dffdff9a206cc76cb1ed1399277f5cbf9b0da2b634053937609c7f1a7a9ceb4696ed3095c041ed939a3cb2a13444dbfc200719113d3af2a2a7437a4619c5be19d3c5c99a8dd5dc687130032e342633606c05b32d8bdb214a5502e25ba0bb17b9d21222fe28608950c85e663bfa9f17db1907b6e1be14794d6fb102ff7e421097fa01450d76bdd25c7e70f25ae192e72e180a5097da2d7e3c02c8e0570c351c3d33c01fd95dc400558e22bbeaf8a042c8e40f65ef7fb2250400d9f6688e305ecd1b78ec56aaeed13be247e31dfed28b45ba0ff1fc93d00897c167299db889e18ffbfeda0a61a6e76204a4801cb4240bf62c0926f3d77e1120e739a1ad52541b7f7466bf1287bf0d01edd6d4553bec59790c01f42b4fc482537537450e967f83907f2660712e060c196845989da75343bb5d0e6a2aef1d0159ab2e29e0aef953f53e033f7e121a9e7f80386d94b06e76904a0727e011bcc959de3e46db77eb52b1e3de93eb9b7821e0a62e4d37c353554d440165562275e8c25018a4e34afad285de62dc60324b7b8c1de48cd3df64289cb202ba40759bdf60201caa2355372022a4a7798f64f21028854316ccb2e681162c0be9efc12c59a8e8400d32cd4f250ae72db272b0142b964d14d4ae5a7ff138f90008761cdbeb86651c4d23b23bc1b5cab9320880ec8ed205ecdc11f9b581dcaa07f14bdb4ecdaf28d1bc426143c6aa29ec9d767c20955059114774a6de204ab5000436c488490c79a5bd7adcb5e0c035ca124a0f0f4d3fcffabea17f5da59a6001c058f4d9cb7cfdd3b1eb932e3d513f24e3358e51e803aad15f291c4c1c39c70585f6afcedf53152b33cbdb852d9c0359828f5b5fd0df767cf2c5fef16ea761056e71046e46351c9f22cfbc9299433f2ecdb0ea7d860ba5422b3ee6dec9c72a0c97faf66e3d3ca4d4c55853c4b3e07f86ebcd9f94eea1b8f699282ce450871404132f8fa81f30f8f05a3940227e84bcbb35530f33e46b8ce7f01e91c227683c0a40ffeec7b6cce32e1c1d38a2df88ab66e33fed91a02b0389c2827821c9ac2e0f4dd02c010179406878998575887a61b28470496dcb8cb4a6e2998c95091d7c066b3ed994da9458e5c69f4eb4f952b287946391547d6db33863580f150feef90aed9e068aec35482d8dd2286dbe4287b705d76f973d97b921e19442a4240c94035c5429d5818ccb36efeeec7290d2ab229874558987b824e8efc469d1ce4fcb0eba3ec391bedfc1062a68404c8a22b00e3dd18f9b3e6577b5bc9715a0ff18410872615b1c3531b1565241973914a7ad18db92545863918a70f50869d9d44cc505467bb1f1fcc6dbd40e14531bb3e528d6efce29945ccad0aee92f72254cc92002f381851a110d6ad30df65a1b723a0664d8f2c2bb2882000d343dd9a0591c8204c8ef4382bce5826326b772a247d84c942453c02d76d3dfb4d0455cd6245a6c0aabb0238044cdfd28b64e4223e6c54f04baf11e7ef6d1a25871a5c5b1e878a503ecb8bb2529d4bba0cd29d3d73be4181294d5719ba1d6cd4de5ffd5cc8165560daa8f0f2e81a06d5bf1f69ef1c7271ee594edf59149671c5d7de84c6d5f4535022e84ac48aa63b309f0613c8eab660731ae7aa6f4a68ca97495c96caf7c296c0b9093b97fa4995c2b42fc65c786cd5c7a43fd0fc34af06aafba2919611d3ac00fe95586514b2435e50e401337ec635f547279246cc0780f0054cc8ec38f7a5f09ea47e4b9aead9fe593d2015ca72466ccfcf0eea9a82ec04ea004f93b8a945404af2b76b738c1c79323e3326b28b851b9ebad3aefefd1eba7844cf00494ef7c04fdd277b358f4b3f16e0280cb3831d07c61339b910118c1bb74915f6c190e68022516e3872cde090a2539efe0dade8f637dbc578887d4c0e020a1f80469c5a60606b26d4b2827d81a961be371afec14b7ebd62afa6cd16df685e29de8ed51ba07c9598168d601365ae316abe963a5616abba678e2311dc63824f9c332224e5e05ad5461d9210cb3a769c82223fc2ee0b277218bb04f22b4f2e415ee1df9f7f6026ef9a378018deffe81691cbaeb59896c1a78da3f9ad2d893d1b6abaa0cec7d05de7a35d89f3bbe526ed5f20fc5f3ab53eae2aee7aa0201dfaa563c8625cfb604b67940e550ae1b4ff72095d80ff178223717ce340bc0961843c64a0176cba90a62a5e90c98dd4c5f638e96f77cf17053b486230e33b283f36a9af21813db610d601bf70edfb36ce020fa1632b0de73428292de3a6d74163fde96dc3c426d4205a9481e1a3731b7b92464c11b08d8ac3cdba39323b1a5752ab13ada82ebf22301a7040c1b4a2748b9ed9d78110756a8bc2855e50ccb709da15d7c354298eb26096554c2947677980157bb3d5a420366cf7e7a889694b6a71e455bf7f29109b0012773a48b0da0874863cb0f3a29e32f5fcf72ff6465c9357386e402c653767d03f57a51d3c6bcbbb9952c31bc692b13696781d41b7acf21410dac693d7d96b60300fe086eb26765af0c3f3df66b1e12707b0455fd54b15774fbe852ac7676f707eb250042cfb3f0aeda9dda93b6303f9d71e0d7a003184108cc4ed55979fb7e0980f158acaeb565394c0d3b929a2eac26e9ca79fce8bced33b47994c74c572900ba11df2b119243413af75935ca007d5ec348dea55236ad7533c4e16b9d26a606 +generate_ring_signature a9874df81aae678880f135e2a926b7740336035d21d2bc39a30fc90ce5605614 548ef0e2b593e70aef41ecffebf0d22fd110ce86cda4a27099867fcbf893948d 16 5c7b29e462c3d2dc39f59b90130590ac7bdf05b38c9d57d23a669aa29743e2fa 5599bfce174648297c1e5476fa67abccb829bc33b741486f244ff2853fea2203 9250f321047a3f3146a8fb0957a9be8c38df273710e7f00f2a3520f3077af1fb c14f16bf74ee09787370f9fc1780efcad06fa10be62ed52602f737f5ae9c899a f2bb460c8710ca606db937623b32307093f338409dfeddd6ba74fb921fafaed7 bafa94a05eddf7614c827891508c5d24ac8b3672d2d99caef3ab16c3893e7fa0 9e95d1d6b2feb94de14d4a86fed3ea2c2043c1c161d807799efb33f62d284a39 099346dd69c4267afbac35623470943101e8244efe677113c56417c243067949 f584a89b6a62b0d389b6094b1be7db24ae4eeb3b11a7a5beeb7350f3262e9605 92476229a857a7f45154086f46bb541c7b4a43f3aa9490ac5da2ced811d40115 53e935437ff7a90fd07ab21cf7954c1506eecd3fbff18b2bafff4741d2157517 f5c5d0e403e0f51eb1432c0db43eb16e1a5b5bcc1d03ee136db5b3b58d8abef1 044ee6b81aecc620fbe1b38e7153fb4ae81fef6e1748100dd294dda9d45a72bf 704e8f3b1513450d07137b0738c3b3aeafc1f5f6982f341d58d4758c0a60824e b3455876a626d13b3972d37d2982223e73dc9d13b490f4dd65f7796694442810 39ec381942ccb65a321ed8f963746185d002c51230f1b4824ddce59e487e23c3 bccd13da8b7615ca745d96b27fb536377b81a097625dae6d8b26beb7414a040b 7 6760c15a397780eec95951ffaeb333556da1c8bd3f32a13ee1c4b91cd7c9bf0c825e2ae5f11b4a972efc8a91adfab5f3be1668962ab4453e1872d34694dfbe0517065569d8d66a976c765df244386746e09ba4c3d97bce8a13f64f228379cb0f35cea8c2ed5297d01e5ae04e56afa4a05204e47b23b40eddac16ebd7c61f2b06c0059bd19abb86788bef3b9e5fabc6ab93b28c660adf2ccddeb21837262a9b0047416cc747657af3e5acf1c2bd511027df1aa739abaf966b1604675f8fd943060f3ee7f27487048595116c057c8552d34224921cc4acf0fd61ea16196b8c150beac3a99e1a97e8d116387b2c22e52578e8f1890b53c3c4df9d3af727cb17d200eaf1962636f4141b1837f8892b808e77706906a63692f612a2534997c74d4d035a115ea96c7c777d499ba17bf82e4dc590743d6a2a1d706c5b3387d641d41a092449b7432028733eb864134d7534eb0ccbb8c736aaeaff3127c220c57e01040bfbf5e4ec60313bc25ad53484fb58d4f361f618661d00a0f8b44e1da391e4d90216cb5009e1692c65ad6c2d4c40b68bd70515363fe718e79ccde419ec66f2cb074dbc474f6d9362bca07a975509667d83bbee3e8967a547ea74f1c1b3f2c97f02b978900b31b096e87fe9331d0b8497c13dc0b2407cea7af8e2a77877e46aec035675dd6c6928849e18e012e7083555e7c25fa61e9e3b3df7b30b35328538f101e0f0c711cd750d9c70e7bbd3d27251a9e7e01e1c9a30ed4a55d1f7bc791e170eaeb63e22fa393ee6278d69707f5fb3018d51f43d8b4766a9f569a025f8e02c06ed2ce8eef86a025cde257199619ad743b7d461b8eb0b2e99e6feb3e4065e6106515fb301aa0a59b684af8051fdb83462bce21c37b5068373e963b20ba3a65102e12a99f394ae06f57b875969b91f71dcd3f17057322ed3ca6e9f3e901a84440c0326872fdb58fc649ae4b6fb685d3a8e37588be8cda1faa54b32efe3ace0cc06535cb8ca57cfd332df1307c0967e8c86aa59b184e12e6120b8034d499165a10bc6d9146d73f147d7c406433ce30ba4d7b47c4e6fef9412af75f6f06ba359660a5cd550e665cbccd650278ef66639ffdfb6f4a43238fe6ff9311db03aff4dd7084e2ab786cf2561521d24ee1bb8a08f274b8ecefd687d04f6a32dd5d1f8beb008ca6365c10fb45079d072f110a65fae76771b471c1d78431cdb6f3b398de4d60bd39e84792f39839f5f84f06bedac1dbad2b8279a2c43ba2abc2efa39dbe63d084eb08cc67e2502330d9ddb845e4b0411c0cb0a9e5dbf0278d01070e85f2f6300f0c396acce8f4f44dce7d079dab154972aa4a0a47548d1d2ecf1358c6fd9400fd1015cfc9d4d31e7e537093d0ea200547974fc5760eab7e10b4b8e5848eecf0cc8a48e27b1caf583240ea88ab0eb00cf01c4318839abff4f1806ef75ce353b03 +generate_ring_signature 57bed4f1fff9321409a1e88ab817f136510d35229653315d37cd14a347a13780 138430c2c63d72f910137f1546888bd5c015d7741e40e985afe98327d29246db 1 cdb21579ab69c066c04df820e1f6ececc593f94d036ea2576ca5e475d1c8346b b4bb74d1813a7b87358c69f8d8483ae6e62c6ea24b768af418c4c5bfee10b10d 0 48dd9c6296e0459f7fd6454ad2f9219cc546a0ee73da1cf45b3e9adc7b32f90db44fcd5831fb336d4a7dbe45c4cea7af646795c34eba43c3abc97da7b221a50f +generate_ring_signature 3a68075925e52f65481974ffdd3b18a33ee7565551769af8c3161886dedc9e25 d14dd1473e6a41a90e2a7d9e6e0bf4fe27a1342faeaa462bf5ec65e8e28a700a 1 08300bcb4cb6af72332876f3dd338d31850c760ed9be65845d6fe2c9ae2fd668 d998d18052ed52c59b572ff7d0cdec7f3783b347fc4e76509bfc550754b6f908 0 1280e8a7412b9d43182ca56c311199f619ec1130c70431e9ee148f81a26d220547b2555f0539a757068041a9ead34b91a8734b263524c18ada2448ea4a083906 +generate_ring_signature 69e451a800a50414caa03b24b4eda0645798474dd2c4eb15c36238d5a78e036a a99e79ef8e5e757d6fb8d28442a21a325982a3dcc0ebf75cdd267080f283b653 1 0ba9ddaa73682e775b96149473804559eb148ce9c6ddaa94f57783c62aaf1a73 7ec6bfdb07d4cf1108728f1d0112e6fcef9f14210eaacf3048c1b91b53b25d02 0 1aa90617212acf1b012b2a9963dd6fba0cfdd021e734fb78b93b61246ec57e055e600be992197be4804c14dfc6bd3f664209471182d8265ee5405084a784380b +generate_ring_signature 33caef1c65bebd09cfbec7b8b83717c3b460bc434460c56f518d576d5af30b7f 091dc3c268edb58c3b86fa2f9e380f5aad61a853baa2d43ff5a8ada4a278c06e 1 d1df4977efede94c057e1d817e176e9a3a9d975f35440a73b8a53865dc61e171 f22282e79f2d84ae14dabcf5b1eeec232c9aa77aa0a55003847a65410fd0c90c 0 dd67c7b50a1b260dcc555bb817924f9101e28a2c3f5101c6720056c21924b90319009e6c54cd97d641aee4fba2f88a2de088301dc94dc4a1f36810314a868d0d +generate_ring_signature faf714c0cf9e6ed4957886251074d5276fad3db5ad39525fa227a6fee6e4c0df d1f7d7559d61fd42d91333e59e3a45c2028b3bac50f3f63875a6978ea082694f 2 f4e4831716761adf99604d47c82071b2f1026d879aafb30d66f9fbc72485ec6a 209f86ea9c1c2ddc7485a3f89e1a2749a4a9342c7ba7b3b6e9ab432c564f1a11 99eee28695538301fc83b7febdd2c965589bd50f7658e61913d1eb727f9dca0e 0 a8e4756cc4a687f773318ebd9954651abf0d932693d9ded86103bbfc264685006b4339131aba5af42daf3f52502cbaaebc329f7e3527cbdbed9191a01d9306076f529ebcc60a9b0fca4bf1b954248ea8662d0ce23112b575c02472fa9c94390a68972f339f29a9b85d756ae943e22801d91cf2ba888392823cb1252176bfbc05 +generate_ring_signature 398cf34ea1dbe129ae8b5003c14361b1136478c37cede334ace33a35d8bc9f63 a7a4ca116139dc02d36435fb9f5683b2699d17663a5ce116e06d1a316f88e8d5 1 385e9d70f7775ea9f0ae77a201c5986240dae57ad0dad927c624fe9039e45f6b 8a57352dfb829f97fc0f97f6732c4d4e265545c58c2ead03d463bcbad212010e 0 6956eefb5c6f39c413b54182085be10855cb2a5d9e972f3325128ee77296f20ccaa35a66ab325d3d6367a8d49d3bdb7de022a1b465e0d2e695be6fa0d344e609 +generate_ring_signature 0bce90adf883d5bcae869275b98279cd1625ec10bf0d60c70b55ef7fc6de2a83 f4b016854e2f10612cb57da5899816bc67fb4232791ed63ecf4e482342ff1731 21 375db0ca54afcb5da33cdc3f00061569a8da4de0a7d84128f109ce493016a705 d719fdc9d0abc9df267fa815d2ecaa0f9972b5d72805ce9ec9e9c568407788a5 519bc5bfff40917726188233df60f82a75f167c640c0ddc7d624725835dffd16 ced7314ea5604fdf99702ff6fe1201c548da723fa34ee3fd94e5a51168f6b5d7 d56c6ec203dd32df492a5e8b5b926c879d708deb9a215cab33ebfca9578aa8fc 2c37d3634c74678f915d7c02402bc112d7d13d99a91f97917033ab225690b8e3 88e04b5ca235395e82408bc3749bfc3141ae005bad8cdda47391352739635f17 7f12128398365ee48de2340601bad74655065428f2e215373520f1dff66652c1 7fa94e6363c800ba8cb892a9a9a08b1557d4599f4fcd7e21f345f93779c7e2aa 8db67f0c8afe624ccdc34d1a07701f716db9956a69ec4df31d722d7d965e69fe 6f9f8fcdc2ce9fb1b93a2541f1e5ed8fdcaa23a0de569d331899d32ce9fe9d5f 3a55c3d77b100df29ab6576e166a94fe232a64c934c040a727b71a38ed2d49d3 bcad6e17c130f36d665c18461dc90948d7b15802cca10b22f7855ab6ee763880 5766be47f942c476b30d6a81cc7e96d39d0a154f12318b030954d86b13e9c6b5 61f44c1534616940e94aeaa645657c3395fdcb3c7da2adfd27d90897847ea554 8b431395252c6cca25c86ad2c82ee09abf91df022c7e0b69e4696844c68d9fda 5eec1b660556cad9fd67e13fd81109351e5098c6b74dd1161299162fd88eab4d ced6db782039316ba77aad6e2861fbc32830e17d322e46c5e436938248afbb26 52fd48cb7569455099451a9282ef9ac26ac67b0cc6008d30243774eaa50ffe5e 51126e8b8dc35a09b2c23532bcfe9a2803882d401c86751be970c7ac40f3d41b 78aac79a22b1059ff8a8afe25a11de676c5c50b42cc24a626967dddf7c668185 ee96da1406f10eb225910b93e0577a87a0a28cba8cc42d85027f2dbf22b54c0c 1 c4fd764fe5a4907e794fef30f570a27405a8dc284ab3533749940eb09f44d40a13d439b5d97612b9d3aa84833129274a1a5452b073936d74c30971279dd6460e60976b8a3d512112099f9ff82a0a350d5cacb4586a7fadd31436832a8db05904e6ce62941fac8d766b88edc11880a67d2a2af179dda0bb0f6b0e5a4dc9acf60acc6308f7a60662067f617c5ef217f88504557fc1a4286195e79f5e0e8b2524095524b429803ad387c24f71f48f140aa59b6d26532d0b862c45f1d0f02c75440180d043aa43a44dd16891ec8b42f4e59f9595c0e6e53eaaf1418acbd29f5ef0072a8696fffcf59864d3ff2f474229f7e0eb550462d9b09bef2ff62edfc75290013d3b1fb5c8da637dc2fab23f9b625c106c05697c9788da2cd8e01faa9db7c10d48df12f704fece8815dc16504cfd72dc18b0a6a3ab2ffc3ad4ea9ea5c8b74101b14b82cdf4e79615e3dd5bf88b898be65c5c99e9ee5473b439fe7435443438046b842505923c5897b356d80575c1094a479813628c9531ff0806fa9f9471bf01382ac6cdae7cd422dc3b159ab421579f2535c9f51c032fb3eaf0f81fac8b4c076063e756cb02ac907fbed336341b44c5d81295dad1ba89a8d8660c9b2e20a3005b215ba053aaf82e8350e014a87150b0bf62bb46c9a1386585a8dbe089252e025df605198e68ec65def26639052056a080f65358bf7b86d36547655ad9cb35045450bdce7088ff0c7fce1001f2a984cc4d295348fb823206e01fc44539a18e0c9d73c831f9beb0be6a57aede7d289cb4bc6bb0cd3edb5d1eb9e78c99fe895b0f6dbf7683a52fc62d02706db839518953984e006e478380797a6e02a0dc71130cc2cb376c947667bc27b526d7a770a7f6f6544681d344c8cc2d229e537b02bf05e55fc71f3d308d5ff356d967eb652bf4d2b6a7bbc2179d3ddd1b7b7b7a0e8c04908c3238ed1d771735e01938376c40dc912acea7e0875c70d2890443f899a20550d802f1a1521d1003e8e6979d22d641b0ec3108068fe60c850d454a9984e3071d0f3070c82d288e78cb48875fe85d2487c4e10e658525e0265a95e6c249150dc801a4a150d888b630c5efd4bf2d7d0268a7cd25119c281f724392dcd8ef100b2f02dbc2b00fb56e6119da21594e5bd350137e66cdcaee36abdb9a56a20fdc00f2a8bc36ea687afad50b41876ef9e635ae380d2f852356d6720d5d1e304e68092c932d2e7ded08fb225b494881166f47d27f98f50270fc73905a9e99a870b8013deb8140e9d51ac1be0426ac5bb37b1375ca590ef3e8652612a7415bee93e70b92a320e07edc37f2342d41f0ee7694532b229047a540b59c77484181baf53d06d265e57ab01f1f513c8806c335857e7c57f93ead6901ff384b78615d7ac6870f15e8234b094b405a65ed9ff865f0989c0f003c1dc6b3cb5f1f6db4fc5c73be027cc308e98f8818d19b27033705358d2d8577e49bbfdc2479709fc30698d5f70623afc3f059160a3423e46053bad8f299dae379c1cd1557db30baa8fe5b8f1909205c0f9a6b96bf2e95803de309da2aa772b296835dcaaa1d1105413e87ade0072cdbf38f578284acec09f8929307daa33485bde7be120285fabb4673fd616609a4a0cb596bbbad907f6d52a0e7e535c950d8678dd123b9b504a63349540749088646cb592dd694b817e6797a33e02d4692844be969c369dccda18ab537c1660c90ffc27543e59985a2652f271c535c24b134b15433cd024bdb0e4ba3267cb80646d84a3364aada1146c7d544bc1e63a7024dda6e528a44ee0eb9646c642bfb05ce3aa10e648a6a95e5deca0e94f79d9fdd0ec47b6d84cda43fe36aefa9c0240856ca2d6ba309459089e7603f449283a25879351d8fa34900163b97f36a7e420d +generate_ring_signature a3de48a0c3c384c9c6650219e0d6ca0a4d00e33401600269f504f1192eeff149 98fbdd7e5bb3bdf9b34eff799b3c4e607450edcd46227f357c8d93cab741f71e 55 1bede0ea8131442fb68e9749806648f27359b5e0b42a2d40094a1375f0620f35 036e1629af79f168572ec168e22741e6162d62797e4eb071d1205f0a3ffbab58 915d466cb0cd4a8db7b34882aa57ae3ba376bd6678b5efddd777eae1ead6e824 79eb9145471950578c3fd18c82a588d052cadb0aff1dfc9f72a02cf88ca894d8 0922b298d45d95bbed8694217ed152a4ab3bdd5f1f97a61a055c7b2d9d769d09 95de21d20b953d6be44dadc3a292d98a8faba3c649b2f8b1f8694ca05efb21d3 f732344ab92f1fa6ecf4d1b9e5d18c4f1c9e129613eb300230a572ff0ac40c3b 311ae1a63f19ffa679cae5fe64344620c2bd1515e6f0cf8f1369f8ee887c62e4 04853e69fd9eef39d9a4ebab0c2384389d6032139e324501cba941aebfd027d6 f74fd019978e466a40598f75be44cafe5452ed6625aa591aae416b5b12aca9ea 971c382d016e8c6da42c6720c3948eeef58484233fd9021eedda626f1b975482 34337f8fa056c7f807c09640f9357ace5a88ce79a3fb49912ee5ab44309d0a82 258b6d78c6601bfecdf4e7ded08ca6b9be2e4a611ba61e54eb09a5ac64911267 d247c2fae8403f5a2646b6ab9ab5187e2b7cca3e5a9ffb410b6282f45a979bb6 ba695d3e613387f781b428238c60251d2410075c0b03a3472184c83a468a9909 787a5c870abc63ed171a1bf60f449a4168efe0dd82083dc6041d80e1fc6e2e5e 93c5061a722599bf5d487bdd9d22a9be216f3f586e20ee7e577f73b7c59efa2c 21fd05ecd2b6502903350d53a2f4fbe1b5086b5107233a673452ae29a79809bc 87fb5fbfa109c73e6986cdaeca55a82eaf4750f20e737984cc747aedc1586e89 346180becc891a370752d14a032274b762a037103e634b45bfac6080922697d5 764cdab85f5c11a86546a36a96bae26bd080c451ef596daaaf5ea684086c3f10 de099eee553e1b45f8f68a760d6516e2690ab993b9bb75d57a09a0556f70070e b1a8fcd9732847154aea827954d8eac4034de82aa78cd391ae8c8f395e19e050 ddcd88b0b1d947e54addceeb331c84ebb861f88b812864aafee74e04ef9f307e 353574f3cc970e7a5a34997062c8c2f4c6d6085293c11ca55207f8721c4ef95f eac3b726a3646b184eaa88e09daf08ef6485bec1d06e69745e731e02dce53846 2e0de6277e5104d0c66d59d1f8aaaf600c0cd15d62127848996a2dcf039173e1 4d79d6f44b21be6ab6a3096753e966b3063efeee16acc14c1c0beab2c715dafa 3ce17c4cef27dcb87159da97e975d08cf15c1e3dae8a8561768262a2ab43b6f0 5852f5661de332455f4c992d9c718f22d2e549351e33e70c9db7c0665cea5a64 a8810fd9b51f1af53fb04043c1c3ff63c9ae9d60b537d8d84c1ca33aeec9fb32 2b9ee71c7cb5bf3fff31a3af8957846404b346ed8aa6358a553ce49bce401e6b 333a9e36421e12a537972af5eaa6dd71994746c2638aeed8cbe4f9248a046631 7b834c21fcd0c4b08e66dd95af852beb19ed250c3c8fd695e27ee0e8a4d0f436 00eaaf3687018a37c832a0cfdf9b4b92e00295d8ee6b0ddb05c5390205b43ec3 d7f2dcfbe69f0d891772e83abb266050f50826c2d7ca2e36ccca71825ce69696 2e420be243c039c04ff0508de4a68a4b675c299a125df901e5b4a7dfbf958341 c40d5d73fa009d2203bdc368f67e34912a7b69f5ad4fdf5fe98655558a870a7f b3b722ad871e7f64975f8f2ff542919d30d39af122b100f2ad1a7e04e8645997 995a8119bdc5d588e7e7972759def53dabd36dbef7f2e924fb0f7c410faa55ef 51e735ee4581235889d4342eac86a7687f9240c4c10631420bbea8cff6fa60a8 beec212a14f5d311f1996f6266e1c37a2600be2f4d054d4cf08bb04f7f28b073 50fdc813a5e6064544f5c71d6598cc7d7cffa87f7fd426b026fcd5eef9d7c64b 8e26525af9aafb193a246b5ae8f43774ec43b492b6aa27fa168b7081a7b9eb49 8e244d1ed59cd1c1a81e821380db16fede30b1aaf0ce6ce309d69cb6598362fd 4626c70df8b75e75cfa2c967f6d1664f68bb757ee760c183ccfec8b94c4fc8ab cf48e5461c813786800ea1c52396894bdc75a9428722ed142e4168c341f909c3 759aed38eae7876e4edaaa4e80e3914b3189d902ffe9ed87f5b0fd01a1c1028a 3b8f652a5236dd4695be8f2a4c066c188ec12224f185869e3fe3c92796a9617d 67cb9403fbada1a4e9e2d5298925494dbdc85a6f61f76dbd2b51f0546ca02218 f48ecfa803704121b50078c9d55c796daf69334d94274c89ae6af7bb0d22da37 203097f44e793582cb834ae09f0ff10552ad9de41d7be7b92323759c5e155f67 991117c4a2c6c4197e8da520824f64bc96742e5e0f1a36e824d8e44e7e6da1cb b9ac16e3210f9cd58f7776cdaa795860c5dc4f21f405b31eecb4d18aa2f450f1 2fa06e53c1d7cb9779388a135c49bcc6bf4eabacf5bf3995c6e0dbedc5e8c8ba 530c84f117193af54c3ddc42b2c09dcf7a2ae550fca253f7d36d3a1e21965406 37 88b422aa78d449617bca5853d4cf51c68f36d34dcce03247787316cc12f1860416c6234f60e4be6f7c689dd2da4e5d364e6cbd7496b539f5a0091fa08149910c46059269d67601348b7668188e111ac46185b0361b9c67f59ce865ac08319f09acdfb7ef092c52e5183a1c8396e23233b1fdba13622586f4ef29d5822b68dc00470b05c9b4e221c29cf326247e52ba26b7cff0bd7295ea7fee2bba5fb571360a196a2bfd6a2f99abe91e0590f397231a757ed026604fa387e73791b383e23d0b9d3967c67f56c42314a1ea1b8a267076fe21045348f88eb1515f5595c4140b0e63e2a232553bbfa80f44800e952a1beac79582d2f16a71fa409f5d21a2dbf4010d5bcff3e05f5b48205e987ce4647e9a4fc7fedcc7d468f5a8f1f3a37a0c2e0ca4c28a46dec05658a40857afd2706e9c16b490bf715bca05f32d70f78a0fba0c9f8324f35f47be1ed7f7f117d280a7cec3c63cfc416f9427fa19693730afdd0fd4a59418343dfcd96d2c1f80b60ccb45c29132d54dfe1d8d15680173ad3a46013f5dfe36312cc3a695ac9fe367950ccc92f627e1f47e447c50b10d48c293520758fc774cc69fb063597f65b3baa203c5ac6a415cbd59e471310ca7f7b43dae060e68b256ccfcda9a735e90379e7eb5bc0a11615ccc21997a58b851de69e2be0a72ff93e056df81ce2d67c7985f79926ee4b61e5fe479ab0dc2ee579c825175015ce881fd9279a1ea79183910d3d37441647fc9c8dac921e7144388a0d0185e042d1aa0b080d8e2021254229e156d9d9791e79e13cc0e51a71481acc10e19a90aed0f4548200fa44c8f2f6bcf874eecacbafb3727c3430bd00b732c6de3ab9803b2f6e9122781e2bfd30d5a7fc1b91cf2b5e0d4bd50fbd7e681cec70c3719240926e75f0cbc0eed2b57b671b5691ca559e969bb472ea471a6d8a0844fb4e75d02e5e4792386880f7f0bc685eecca7240960b03d875c1f574f1f3362e107ad5a0c4d6dba2b2ee22e75bc3a3ab533f9a9e6a02ce97bddf036db7ace2b084a08fe0b12c935bbe5f05d007ac4671d96b885a2439b9b4c022532c19a12796611a6b30c2524866595a7ae16ac7bc5a577d42fc2d7f7311966c8b954b6d52237576e79074f87a15f5f30b024c603da0ad08acca32c9f95f0d6546a72b97a007719ded20034e45c56af15f362f7802d481f9488dc5e20b75e48fc38f0e100a9ad55f21e098813cb8ac18144fab035983a593a462612b9babf8c63a76a2b99644ea80169058a30592f9fb56daa42117396777a711403139131aafe6ef56cb818a76207d6070af5492a506c9fc2328dc11f3b23717cb5dba37ebd1211dc4707a9e1fdc72a06de58c65f2f71ae3f3a6291b350419b4a631a7761afffd3e49c95f6bf1c4b5e029ff7d928d8aa61745bf76d147cbd427fc1d4cbbb5904a4654aa50e7727165c0c124d52fc5e7a56e75b8c881450507cfeef3df650e814632c50a8cdde0466b6040fb03268ca3c633cdbcdf9fb70494193e8bed32e9538889df53433870eb0460dd660df91372fe286fd16057d458333666ccec0d062e5c001a1d768cf3ce073030f34713128d87a01ac0cb81debc527414b56136b412ab32608b7dde35fc9ad0d37a64cb6f09a3e9f7dcb1f1376aefdc5f81111ff28362e4bc183d368c18ad905afa509d787cf4d066e844276156e2b1b452f05ed3c0d9a7f1ea34e4f51d057036bc6e9a327f13ffcc9accd73235dec813c675be6962215f1f0f655da5b942004151a5a659d712425c1208c80297ee82839e7576737baac4a564c2fdf4b4d160c76af01d85d6d125cb866d47cc0babd0d19fbe4f14284619e44858fb8be67230ce94d4c5431c4be290f2a45d47e604f06286bf290cbccac8bb6da24d0296c5c00730186ef2b113c3a094cfe76acd5a39efd222082604288994fa9b38dd527830f097d53d9b6c7b6860aafe4122b5f596a22eda5657043a17eec3d7af42ac6ab00b2fe4c7fc430d4125f1a244a60971e80582c50efd2eb8a7649bab44607f7fd0dbf48882133d739a4ca3b3d7085af57c541813108205131a13c02b3dda77f350081e2de16eb3b8311bb7e39135d8d37f0d4fdf84431150e10e1dff924758bfb0f44cbc252a6b55a79d9540f60290d28073f1bc903d262abdbf58dbb10ff5f5a089bda5f5fac101e486df95374e814ec543cc4f7f65c35b0c6d3071983a2c63e083f8eb271f933c445672a7b79ac550fdc9b853de6140b64c031b6b2d700c2950905326de7aef8da518f628593c7528c7915f359b6f1b580372f39976eabe88a00ce818c70c861b520f88887c688d16b5e081ccde690e9129fc979dc3bf242000117d679cde32d8453dd1a2bd87dcc90216c0c47b2c88340351d924d411976a105d278778e3a82d6a8cf44cb0cd8e2d15b6ab1156cfc76ebcdf76818d21fa8dd058afe133708a881f3e9bd7d8e3cbfdb46945395c47ec534a8ac92d19809166b0e7f13f91f455bc7736766748ffd8ccd4786cd7736a113b414d9ab01d01bb881013e9bf9bf24726f19c4030d08e4357d38fc4dc4b21b4461a855a10f725827ac082d994c00ac97265704adb6d65ff336d5af2335d97b9e3bc1b3e204e60a90d30119e32cf602f7b8cbbccfb0e0a0b05288834f29152e578dfc39b8a493c2c1e4035b1606bc297128dcbaa2383a55992588505c23c6f5af48ec9d34bbe1d06a7103af9f3b24940d628c640a367694c68a00a4c24fe52d0c6228d95cbaef8349c10d54d1fcf3e01ced5ebf925f663ace49f4b3dd965527a55e5ed017880e09b05403b79df389b05a45aae5fa8e9623d0dfd38f5d6d25c075b128e295f273b2f4f40562fcba778a63a7fa810bb900ce5ddabfca6313ba17472abfe7180e417989ae00b3fff335dc5fb914bd40c6477b2363c5a9177689767718925fd6e320a13b7c0e1035984125a968d697c1e58c2515d64f6f3810609834a38f888454828270140b724c20e147a22c52e0c0fbfe00e306dcfc191b878bca1e787baa7502b429fc0a207f735e1b3d94c8ad62e5614a143fe3e8d2c62e4ac610900005ad26e0e2ac088dc03936b547307cb91bab37311e1637cf58ee68ad472ec49b36dc504150ba0454f312c38509e9cb8290fb40056ad47ed6c13fbbc4889ee69195bc7fbcc4f100eabd3104fed90bab7624a4c86f8bbf6c1379efd411d7c71f3c84c1f03ba6540d341901123dfe240bdabb3fcaf7bf81ff6f817ec20beeea9bf8ad666cb4a35d09c062a0385782bdca31d25f55be2e11fade4eec29614a34094b5199597c48ca04e17d4da2b6e4c7d94b8a35143e32a927cd482b85fe83856ed71832b9b7d00e09f6eea6abc9e37dca844b7374334848b28b4db3a17a9ed909507349d6b22b420a56ad4da5ceb0a3b65833f27f6e06d8383b0f55cc59e58e3357d2f798f0a9380613f974195af5081c24dd2bafd1ae944d3525ab0d09435f5d59681092fe88f901123c7480f745cd1d3751a8223fe58e8c82c476ec31e875cdd83ac102b688bf0d977ad9ca765d082b3e380b6e2da45c4754cb260a71b595baa4de9131b53f06035b3f844d07dd8cdce1e70bca3fb68e4470846b5f66a531c6af8e67e406a6f20a11047d6e459124cc6140ee6d2ddfbad23886d01c73a3971d0058a7abbffe9f003d350543d289d9deaf2a5df7ee8d162b8c9703e377d9d93c6f5bfacecc5edd0e0847c66898d9f5ea236063a94b6e50ac3ba40919fa4fa5b9ef6d02f641894f0f7206ac08785140792b3560775d0bb24587d8603db1652005dd1a3d78fbe1900accb942dfa4ba61ba1d51d1d8349ab010cc44b54b23911d0c4837f68d07031a091f41e3d6ff5c5b3ae6d9e90b62583ebf1fb6d53cfebd317e5b8c0028c694610457a07b60143267c2c38a62bd68c494abbe80c12b47decec80ac40b07a7111a0d1057f415e5653d2a9e747edddaa0b3387aab9cebfb0d897a4bdd732bb312e804d5412b77538fc535978e9d4f25da771e45d02a590d8a9e0382021e8627dfb701e1a2d03cf47d10ae61a89cc652361bca3cddd6e950f9c0d2e40abd421c2c2d063bd33af5551748a7b3a4fcc1213386ee18915be962680b6d52ddb8b0fe2ee8036ed2750102c62ca04b81469f6853d11c1a189cb22acc2ae08ffa312f918eac068002d0764313851cb14321c34c18e26e4b69eba87f83bea9acaa53d6bd5e1a0bfff377a731b8b451eb36b6d01db3191b79533da5f24b0a9c6bf9b010a1e0fc04de595f822e0895611d0c826f5ef64cc4c63744673c48a50c24d55acde9b29f0e69ebe27ccac8aebf026f55e9114c3127a45621bb00e0326e3b0767d2d7d5140fc61dc5cb8bc549b43c3b2771ae0505f1bc5bb06c76d3ff91323d9c17a53c3a0b504577a09c394de780bbd80c6e6e08fc9b795007bcb129026fde69b5f572ac007cbb6908e75dc717877cc2342249e42144b1fe2451beed6fd06326563b8b0c09c1135ecbb971cd616a2cf9ee85ce5851c52efdfd4f2ce3685aedb72601ce94035efe5a51e1c941fa0a0bc498b1f4abb5d3daba9ba48c0d4531ad0767a9748008e62c8ad0da2f0e63e9bb75f0ff6da4fa938c4b09ed05ccca2f5edaa2501b6c01a36b4d34a0dd7f36ddf4d422ed42abb6f08b23de9cc0bb1956cfd0243042a80b6531e21377aefcb6befe0e6b18293920815c3527b602d6203209a85f800d9901500bfee92bbb60178179e716ddc2ca5827665d277a6fc08daa0987a311ab9b07420a95bc7ddaa45815fd02ce10a71ada7be19e9db428cab843e616fc6d6c65036e662dc88990e990d041d32a2273f7b12e2b22b6eb200cdb33c5443b24d39901601b4456d8c2e78069248c98056d87a31864f6573f22578f6e52bb33e051b7001ccdbaa9e92272cc890c678a15d4fe021a12046c600cf6573e3d959f9d2d4109a9d09d504df479aa0409dc249524983f2d1ccc1745b2a117615727f7b54a3a08 +generate_ring_signature ebbd5f500deff28d9be416384b7b333cbe4ea8b72cfa91ae1eabb91908890aad b6dcb8c86322d2f0766ef830fcef34c2db0f18ecbfa87ea97db5e99f275c6dae 1 5469459bcb2e11a76e55e8f5ede635969abfa2e6387e4bb70152f90cac48ad86 7f0e58c710c5670060c59d2a20936463d4d7a663c918656bf33d08d143afec02 0 5870153e2411a09177cebb525e08c4ef34bc1df970b59cc59c8cce552cfbbf0ae73075d07411a5becbdabf1a206ebb60d8a5aa192fc37204cf8ef4d1d170760d +generate_ring_signature 8ff06b957f1737b7fac52681449d9375afb3c03a0572fd3686f72ca27d16c10c c78d82fc1993940aa5f6a36640e9c84904411db43a93ba40488b36c266662250 9 e4977af38b5d8652adaba56de56ffa5768b47113348137cd5253ee7fd9d00cb5 f0ff45eb886cd36b4d4de293ea3c4b364e1ff909633a121ceac2c5a06abfa0e3 1d6ccc1afe36b63db70c8baf1add9c1e045959fc194d8810d908194b91493733 5566042fd74dbfc551fbe58b363a6c5bb1f434449540f7cd6419dc36bf2a262b 28fa874784b34b24951e923b42d36654c7dae903e59a69b3afed42a5096c1b5f c36fdaf6df3966ca8ceb51738191fb61c0c67978a8a76ebfded6d005a8105bde 23b514334892a1ec856f2950c4af7afbec711ef2d1937e72c33fad226255c116 b79dba6bf19f23e0c71b6ccb145bdaedfac0399d616245855f14f4a31a5e55cb 804cdfc469262d9c957bde704c3658888badfd12d00e383e00d8cf24c0882ba2 709d0154a85776756f3fefa9bde5d87bdf799052e052b626670bc85fc5be1602 7 0d4a8ddeda9f44ed77619f24c8eae3c629cff25c72444b38a930ccc22f6cdb01b30e88ae1a9434efd95a6f83f0811a658a511b7ffd516b56feadc495a5e8e9011a91017eaa8dde202b88c570c609f462a94fd8d0660c7901d81e397402f8f30e035b2d2bedb1bad1da5bc7883d612994f2f84041f6ce89cbbe861f637999c4042de22266a235ce7d1fbf207a7c5d109ad7f7f53b1ab85e6925ec00ccfa2c4107ddae90c516d607f879fe71bf5dc3fb676ae9e7c5ae329131f84b31c8fbd55b06caf450fd9411777212daf834176026a33a9ff8af1cb5ba66d389c3c58d29f90cdbbbd0b53cdc90f8f3eececa91b7a7979a291dedba23eb4da3218158a8e0ff03849803e89a3f6cfefc3e220469a79b505772a4fca5beafbe8f907b0c9ec15101b75de2304eca53c4ad01286b0850fd92985e12e220b44636337541c68804830676c2bf964e3472fdde4cc94bef9e46bb71cb14e260449def5f976fde3e62110cf76e9a287fdfa857e7b09deb5c56e4de2f691d8a37c9eec0bdb77e15e119850e0dad9de524ee8e70e051c4b2b3a979bb36d4146522a33a3f860f429fc92bc00d3055b9ed2abc1f1bfd9663f43458be6e6fa6867fb822f7885e1d7b1adf204504d7a233417d125275d4707af27185d073b800626a26e26cde7fdc14b5da43ac0fd122eeed22753158f7d3f3a8cda21165cf5c9bc8d7f8e6ad3367d0a324ddd404e00d84d21c96dac5099dd4c3f9ab102f483ef884051c841d7c8c19bb452806032c244f26458b540e9c4a598cab9e4e9948a03e2f608542810a7a0802ecd9650e +generate_ring_signature f8588711897c2193e8680b841cb5a276c7dc37366aae012eb8f7176770428ffe b773c0109ee7074692ae5a6569bc6e25c3ca3d64bdf212766dfe5b736f0fe6f0 1 657834b4f38e1972826f9b6df81ebd9b163771b8eb398543eec09add7f029fb6 fa02106813b83b3c755510795ffa19ebacdcd44f0999c6be4702d9c74404ba04 0 a8228bc000df5262192e81bffb81a9dd82ce035d0119bd7f917a2d7ca108fd0df09ca21b83884bb08601fb4720ec801cc885014e81653e1c90ce6eb663e92f0e +generate_ring_signature e633e2eb9242d4e96d2000214d9ed0b48f1652a700f680897f39ee442fc94fa6 e0d6bfb17f7ff27bf2cfc08fb49aa50c6a908546fc9ab454d4b3f79361625ee0 49 dcb1873f9f6ce1a1a6f7cac552997328543ca07a281cc5b50c0374dfd14ac6f4 2c2a4af7195de76a24331a9bc2bbba71fc824006858d8b67cfec927dc5620dfe c66c3466263ed4a7180a261a5ee27c68ee25df7aeb3bf35f11fe52d7ffcfe24f c7c91b454b9ab62d7d09fdfda20606468b86f401f0498452dad244c5c14ca555 050a5bb93cca134ad2893aae23126e088f46907bad617b262541699f7f0267eb 215d341c56587f0caa91947fc6e29615dda4553559fab560fc09c3423c961738 6435042d67b459b024fac470b984690b717b4476ee7d207c0613e01dcfea9bc9 b6650fb1f63a45a2cece02db4ec2a8a4c2c6337453bbf445f95c2ce5e267ec13 48fa13930c39f4114dd52b6d2f0eb3380396db036bd2145f8603c981097fde0e e829d559f26600c057bc1ad3228059d27ddd8c737c21f1fde80242b83819f01f ae32155b594acb4c7bb8ee8367c07820f18f9c183a5b132b720cf5cdde066225 8a18df9b04955964209abe1a45b3819bd739f186f3129a48c09905f1655edb66 a986412a508d8a0a73177109cfd1ea13e8f403fc59c22f847350a47c6434ecc6 4ace9f1184d7aafa2e01da9ad81b839178565ee18cc5e6b5eea6ce15ccedb2df 8a4c8d1c6737ca881ac1b8c2b21b54f835b67486548a5a22a771dd2a999b78ca fda677f772469cb3567d3192d6d033a1039879e40ae4ca3711c7605d57533160 8d063e7a19dfcc7f9e1d5a6264a155087acb4abcf3945db3a3dc6e97ca5102a6 cac995e0ff6314a7f995a195a3b5067722a0c846a4898dd2b8545050d6f79909 32be171c7b86830ed1f235f521d04ab5fca087508cfe7f8d1cb41c9521deaad7 dd428e3f9a5a23813b4449071413a56930dad4fdbbc69b825495d5fe1a32715e 896cf43ad415fee8a0c82c8541a4bb08becd1f058e3bb8d794b4f4e2ac02ad47 11b1b831f4998f674cfe24c7c3716714598e500b3f2e5b750bd925aad7c019d4 9fdb3a3236b4fa9f0c5c7f44a2398124495bc86e1df48db83c6bd19dc7c6862a e4b0b84537ac72fbdbf7bf4cc79c70b13f556eb65534e3ef1edf542a2380cb38 d5eed574e1b8cfe2d9dd4eb2443b22941af4df0c396b34d193bd24b4fc9318d2 3f1e5e5dc20aae04ad41d382bb32725cf8ebc5794bcde78938b6dec39959066d 2378a9cdb7fecdfe43a39065373c214ab7c4f910ea18856be0bef64fa707a3d2 03a3d93ef41ec837d9cd208eda0c9a678f0c009ebe01da311922fe78103a48c2 c87f6a99c0774a67e134b672aa1473d732142f27b28bdd6f6f3e8901d16c5638 60c76f7c3d1a182865a67ce57f77ff2a9734bce1af583ccdca7109399516a80b e10bd2f59297e573a3d3d435505c662a4e3be39655dab02d0e2c71c4fcf818e5 4550e12d34e63d86fdb61be7f6c0999f43234c407452eea18ed74d99896509c4 a3a36c109ed4b65632c5086cc0391a128c3cf443d5d7fdb07b368e7b631b4ed5 d494e96dde0820c03edfd0ae8794e2aa2950727467f43ed2f3aa8c94a7a1e4ad 0715ef02409a2dd33d46eb76ce5054a1908514d7b45baabd76559c357df7e5ea b91b6badc4ee76d7936d59896428cf8a6f265b64e6be498695025f09fc52148f de2c8d0a65a348e8661bd43f4a2512dc4a705c2a2bd08773cc2af93420c7c870 9dc460f0ec1af2af90cf38448fecfc9fb8e6896f4324acbfca2d889132f9a2b0 64ae34fac483812b9776e487cedfde128141e6132666fcd59c25c25bcf6bdfc5 513f383018089ea57e57cded6f5cf59438a449c9367dfa421d940954cfbc0932 8b7fd65e78d3e9bd615f1f02223ed0014a5993217ae8edc6f70d716dd6ac8a8c 4aa62593cca4bd3ac1b2efa948b0557850a0fa98f8917f8af9e54500a4ce3bd2 9abf4b9f5b21b3232b5987467fc3f0701531753667a2f4c3a3a69f86afe80a6d ffeed81fe31c6a1e388fffc826a62d7fe58950b3e8bba19ede6b96a9049ac4d9 2e112b2a53cbbd1898f11da21ad93e2034e800d5fc813a45401e2c4a3837f044 af9b338d0e12cf0bc473e38c9d344e2f2a7b5f8ed12b8ad151f2ddafa22b9600 696ba8913f1790a3acf52a140002c25d91d7431cd47a0c500f42d85eca57c0d6 a8c9909e4250cca34d2ed21de3a63e02e40da52a115d04aef8a906251da37198 2d32399e23536933fbb32057cd71a3e405831d4b5383a29cda9ea13f0a8f0bb3 f71f28faa22f25dbff4d62e04dd9a861dc948a101eee89972429ea56e78a5b0d 5 088e397c6bd2438a679ff711fd930052bc41dd2147823aad5929e8ce2b9951078d680d6d99f32bff4c0a20806d634242a18462d6879829fad65b927102fd5d0fd861d14240c8ac7c37e936b48da679bc8f4b057beeb417248266d952dfc0d90f36a895ca8f58d9eddad833a77de92dc3e6ff268a5090b81ff9a6c6d73235bf01ddcd6ae5f6b4f548d45a1235c6a638ea8ca955ef54aef9c44fc6443934252e0a41d407f1f0ab9efd20e0967684b7f44202c15663f9bada2f1885707daad20b02346f117dd4d6dc17f27357f5da34cced7cc24cb184b62248d6172d3279cc700c09d4377a60b46b2f173d67fe874f49539cbfeb29098b490d98edeac610a8c90e08b824d268f1127731d7570c9eff6480ce4b432a7b7e9865c234e9b715ef43048f54b78c9ca48d0b4f082375f0b4e550990db8dc471186f41c482de929506d0a815dd91072bbccafbf909507528a0bd6373555c8604c16a7a781903bf739650c050a3dedcc9b4464f6e9d35c6a678c6ca05e004d9a409ab94547fcad87f1ad01176deb487b65c4be70d1aaf8ae4a82bdb56bd4940700fef315e89a7db1c57804dc555103a5528c0fe5dc4352e25cff7a0afb1f489690c80e00f9dcfe4b0b440d812a662b9292ed76f944c456c0eed7de13f8f29b88428bfd549873c98a21e70b5c7611f8a33100e24fc68a73036ee6e958ce34cd040aab6aef49d38ed3ca150b6ce8c50bb91d67db121e1b475c9b5cd11fffc677890fed4c8e5be250daaa6c0613d5fbdf7e3694c8347492e97824787092829edf1adf123c46695395d9fce70d961dd5af042bd322b85b937034ca0026f975be38033ee3b8e6b37efd73d84e0a0cbbb79fe3e54c2fa7d695b76b5e10fa5c2d82651a9fb556125da1ffdbe4e90700248aa8a77e1d89bbe7a66d7a59d356494ecb8bbfa4329c99141b219640d40cb0306b5b6305f430e22be54fe8bd2bed3d0da269fbd8daeff76b27467e54490335be171e16c2553d1b8825d551f3ba5454a9553407b88d0bb539c75de2c52a03aa553d3516b41dd2372f0f046bef9d428b7fba0e34da0212238d315a81d70a08334abfa4ae77da4909ff6b9ee75ba87c6726f14158b9a09db14960091f8ad308751e2b1c4d6714aeac1a4712384e9cafed2e0f8c7d13e23413ff8c6bbb7cbd0f8297100c81f35eb6aed0b12354dc16f62ec9aa1a5579a9d87920d0e45468c206327635e4f377ad8e0e654f4cbd39e358ae62153862c9d64879f05c2f20560f0969aa2d035995b4f8c8462f3cd26d6f109ba21e61688ffbfbd1d17a6bd70b4008bdccd875fee97d9840d220efcf480cb14b4f0a3f745ac12bf14b78cd4b9cec044b3fa3fb035937b679170935c2f542afe98f5e823ff6dea2cf027a37fd6b9d0536b532c80506ed49c2da71cc0432d32d9ed841cc76fdde20a0fbb728b3e52d004ec8266fae951d419582fead9da63b4130311279a17d439f3bbba557bf2b820e8d726313ef6bebc791889201a0779a6e979bffb166e0356466542b89e7815a0d9769119f949cdfaf24fb773c795b7f56d29fd9f72f1cfd025a25485629a1a70a6dd4e81e5a796490a9604566e376930098f038b2b17cd5101480a567baa72502d88c6fcf3f4b728f8cf7dc50da26cfaac90d4b80846f1a1d1704c632dc2f6a0ac622f3fe4e3c1fcf0f337e79828915385e0d978c88c38b8ef7b4da8efeb67901572ec9a2dc8e76639d37b9c5be13dff8993accb5c7086f634d5f50cd47cdba0496a067bbde44e31619e0c8dfbe1071a7d06a06e51a064113f2352c32f7374305a12fa85e70638f02022cb9206e0ce8866f446f7431f81df1c32e694419a833051fb9373c3939de2a355a740d61e855ded0e2c52f22bb7896e48fcafe6b23ef0300984d5b4974fd0421431112de1776c30d03f074435329bcedc77135f84c3202caf1b0cdb7904704d07a1fb0ddcc22c496bde7df179cfe5989325dcd3437890d85537efd1adec486fd8016a5f4256dfde3ba6aeea8a250640e0692ebd85a520b843fb0e1725358e4470733fa2f845fb143ec227ed8f05c12ee3c926111b7bd03d8d16467eac562eef2e5adadc2311d4be59f058980699b86e2bf22f791b70606f5fbe910677966ff3b3adff10d74e47eaa55a43b1863216be494b81ac744670257ce42e9f69a1ce1f03be88d01b8197fd413265e1babcf435958006435323c0c5abef8224c9306413d5f3a8148a49a7fead11104b9ea836f9ddc81a3b61c1c0858452bf130d8a246042cfddb6e911fb64709169ed88c1ec7986ea5bc6d34130746fbd94ee13ca3fe41c5e54789278459cf3f56829a79822e95b434c57887e300e007dce4391a161c62989eb9b35bf2daa57ff1eb505a8cd3cb4bd328a6e2e30cd310eadc819b09401c5a6befcf1512fc82d3c3cb837fdc8501735d659862ba0c7b7ddbf8f438847758f4205a46dd5b24db770dbc1337f1d121b8c2ca404afa09aab6bee5a07dbba0ce316e61df7ab80534549fc3d930baa75355391530049f0df07d4b9d2053825b6f9118caa462f41a1b411e072a2c8da577e700291c133a0356818890af8702fed546525c4554b0b76c6238bf8eaa0014cc3ce3a94dc3c40ec25f3afa0195d40358a02ebc1bc4297106eba901fbe04206ef8af3a095ade70b1941f1997a5a1e8b4d9ffdd58df2503dc8b2d580607bbb59cc2e8f5dfe3d02011fb2e7675a698eaa1296f912b9380bb4836902a3f4e225f3241153ed19003807fd637feb7006f2d0fd58e0cbc273b33d1669a44d54ac23ddccf4d8d09b5b4309b22b26068deec69940edc20254a0752910c9728188d23c132a10a78545971405ae464e3b88f98188e9e4af212642aa78be1586b17effa7eeb7232387cc2329066171284d7b98d48123002e3fa0c96ff61ca56b0e10a1803749c522a48a60bb0114f320a5e144d097ada55e8052eebbc9a212cd62fb5434a4ec8644903bfeb102ccf92fbaba64152bca192585d0c9f3634d28755f565f51b815526408920ace081d9e3b8af53443de673d15d68451223f902886f01b855654c7ce3f900c0028037c8f3fd72c83648c448ba98fcdd166a8e1ffa700592b69d522775fe0a0cb1b04f30ba408c19054bcb54e554fffc0e15843795af5104dd6ad52f9ea2ad410530a556d4779d4ef20291be755db134e42a6c6d0ece5156b30cde1a15f5654954d0c8af95b0142af3708c3c0413049b8f81f689658f56cb29236be43889b3892510a4e62fe25540d72abcf6b72e0dbdb2bf236d1f6df9b8ee4d09f18f2ce7477180e1e350ba3880b82f41ef752ac3142337b963b45b43e81c946c9b8fccb42521806d3fca4a3aae3f9d39a9d322179dda320f1f0f93fab3daad351b98db21177c10ce4a1df3e77633a08958c216d2049363217abe4cb8e07c9e28c7095277c2fcd0846f346e0f98cf1a6895381538606ac02bbfe793326aaab637a55c4070be54e0e96c96eceb23392f5ea40b9a2cb84ff07fc3ed8ad9a3be6903c0d01f393e9530b096cb2bcfa82a1aab8f62b749617aa9916b8ff86be454ffb8e3d2cb3a45e8e07a14cc2ea96331e827345d41211e98c824f63bb122af6a3e37297ae007b4bf9021f9e7ae1dcdeb5c13e1190c6b32503fa09077b86d13c5c1e1d501455f0d73f025d6e9f043fe218ed91a71dd74c591d1d4eaccdf487984c6e5f246c91a1c6e50c9bb981699fcfaf50da865e6e45bf33ebb740b5a2eaeff82b225d46d0a87b380bd561b32f5e6cd2cb84ba75256d67b7a0dc933cb40c8fd8432b4d5fee4c61a106ffd768ccb897454decef6496c74c3ccaf969514d7134cfec8fa24e838bd9660e47105466ff2fc5895bc62c9d9de2e441e1b78dcfd8b88a802998eccafff6e70a722c494774cdbe5a5ba97918148432b1d7ad64163e018c6128aae7ee544af50a0b9ef82815540b4ded09b38ed1a33ab9f008ca0d6db75e4f986fc4c3a0fc080ae7679b04be85324b65e41f02fe5b578cd551d719c57f925e72596ad1863a200cd8bc1f82ff6b26cb04f44005c4a7158aca2e982c37ed0a1eaa017ae05167e408ecc7a63989ac2a51d3877dc14109fb35b2b862b83d3cb36d96ae2e65af1fda05b1597b97cb21fe0f333e11e41e04debe85c5a6a25ada1a3e97d82235d1734a031e385d1032b6cf6684ec63c80c6cef010c08f442d937ec21ce93c74bd0153b008ae01203a2c082435ad484c431416ef1be6eb518053c8ea11262334ce9eb400de54e572811b3a5410cb1bc652db14b06088e4dfc5759efdbc864786904f05b0249f8bd391c339b3bccc22dc00511c48480eecd480ff51219fe5f2f1fa59c3c0894c4eff1a987c02867cdba30ef31ea4b8030b564b342a6915c3a9a89ccc9f0072d6ecdf17e3c3d5b20241f84bf91989ac8fad026eef9bfc68e72fe8491c8400d +generate_ring_signature 10c39dc3ee0ba21a39a68b4cf1c3bf8727f0f7dbf41f7a3ca56696defce7c74a f211a4110110ab0872c3656f0229a3f9bf000bb6cd8f2a63c513dad5a193329e 119 38ab6e47a55a81ae84a821e244d005179357f4731d60ed552a3c76c094e9b087 8554ef038f1f1b14d005bba8bcb3b25f28f4fec0a0aeb09d003e4e8ef6178705 950776af9c128d398326ff3c51e26d99be64e3d88f6417a1424626bca357a355 ebef3a07e148a6b95f82e9fbfa1aab731cf3e904dbeff47125ff7b912e7c4bc7 46482bf905ee620e58dc23a0c4a69b41a9e7deec03c31bde7e1a10c7f6f55251 73e594f3ce829301f92b728983a547d4724a9e49860444a3f5d0d4e84268f7b6 5db59eba6ffdb3eea157343e05cf60a1ce32d61204ca97761e3d535baca79b1e 3dac9b77cd3066be5eced5c9ae995fcb868b537958ab79b3338af772a517a943 0ff246c61edbded1e94a0f22f03aa0a3494404c724c98f98a1843110d99b13e7 8368c1b10e1c865d46b6a014420c86bb2d71cfe70869107420705be973058eeb 4bd492d162ee06cb972e0a33cb26cea752c17c372b4703a3f46657bb3266b1df d3f125aa0c13c3c1be6ff17352cf6de9d7530a116aa3997e4a12d78574f7b5ef 36af53bf51d4c3dbf88f8091fd19bb7e713ab48758be69dd6c32adccd93dab9b 2678da11fcd4061bdddf699baed2404d5c6dabf0ab7f30b63b5f8c9c6ac9a8d5 ec14b4352e33a9598417d429c3ca2c0b889b89f1770974a44dc890e173704ef5 7ec86f39ca990bd8720e0fb604a027a0a46dceb14f20b3cb03e2d9eecb049bb7 01f1e19b3fcafc67bc411ee938ced749d3e32a0557a7967374bc3c5495e65f32 48e3219e2f558f3d9ebd4524a1b30ab5231ecc1927f373b1ca18beff6a8fd7f9 270304150ddf574f4c71d390de5b6861827cea6a4b83688b30f9dbf6ed745273 c5ae911e2d19de6223e7dc6fb210559fe68989b8718c41bca3cccb669a037f20 614501a41af387730bbfa724d3111ece25cfdb86894501c4cdb5e9ebe4e7c436 374048b1ec6928c2f425972663b96003a9fb898b8061f7783b8aa1d5d6030f18 7b2f9ab9ebd879b257887b98e2527dbdc6662c8da655384c51f07f695394fe5c 0bb222269ee01d38f03189694c101f93ea8162ab75371fb3b968f74e9849d203 a7ece680c31f993952be4dee0a692394bd1075f166aedca05e23439a63ef8f5b e3e09115e366d65570324f12cb3c3b60128f264b29b2a8252d5f91297b796ab0 a06412a82e8cc7237137cab966a6a60565b4b863da1429c025eb690ebe519a18 0bfc3d57e5a597d5a8141a31d32b602d4457d2e0c3f7d1eca794ecf530a05dcf b4b4c4ef5c5651195bad559c5fae619ab1197907443c2b77e881b6b8543db8b5 58657cc9a36e63a46f30c60075b19dc8892caa1c92ee9311a34a2c6d6ee78d7d 816db25089f4e53f09b338b25d0da0a832f01dd7b59e5109fe9d2d6bceef2223 b204fee912add3604d84837b3ccb6b3c536cdaf34b61d248a7b72d38fbf1afaf b3fd816e13dc84985322c4a76592f1e803ba2bec9b29c9ebd5310475c39d385a fd70b05eea99ce7d8cda9ad4337a8e40494f1b83a1929b2b9d4bef5fcaaec06a 1af613e4062d6acf330cb0951ea12320993cc97250eef9bab4c5ccdc23218e61 52842dc7fc960cd81d1a870ce4fc87f85f30559c1bd66d72a4ce1d73bb44691b 4792caa70c4f41cb28b3048a3b2345a85a7472d5379103d63266c4d94f126b6b 73440655f1875120a1def7fc4547d73e046d08e21ca48a4a3042fb9f9e03337b 0872e50abc4558affaadc91104467d0709dfa109893e7af9f23c501cb1a649e6 559d1bc338b961a525895bee14320c77d63341e5e2a1e532eca8f0be4f7c1b96 0f0a300a6755eb298d3c509a0d1e6325ee86c247c6f9641eb4bf3992ef74ffb6 8eb8b458f28ff0da3ad045493c04a38f08799d2b877a050bfa1f055814e7f439 0076a3999e8ecc459470ec784bb6dafd740f184653a3902b77fb79979348f2d5 d9b8263c1afbe0b4b40f1855924b6818ebe77f38294be913cac0622c71cb8e5f 2d21c212d1ff44b01404ff2583593afd23cc9f1b8fa089114bbb607e5d59d9df a70f5ecd8aaa1a80f7c89efcee4b730f845940f9df8cb09e04c5faec45fd0f48 cfab1ccf7e74c3973f2b6b4538db3b7092ab8493b9773083996dbded8a7c8722 af735567c319db464caaf7611e4ad0e548e97ae9f4de089ef46c36ba916d3372 d9f39bdb078ed90d17e740a8fa026a9e2825b0579a71f60a11ce95294ded1e65 60b85c80833c504ec3a0e88bd79e65219517e550d39f595c50b9d72451f5c114 670a68174fd8dadbdc80b7c886c36ae1280fb923f87e646c892de74af08e3749 cc5bacd1a11865418e27929343a7d45e2d17cfedc11ff3fc24c94cbebb7df936 e079400a324234504f8c43044a6c2195b64ae3ff9dcf5d040451a56b0b13d33b 2071eb3bbc02658a8826db8496bbb883fed8736bf1b2c99365d7562ea789088a de6f697353b34b101010a92e4785b1f10054fd0f661290987afd103b9cb376b0 b428e86b2bb7a0199207da5b00086be50df40c5228ca88aea6b401d3e334b0d5 56cf67a067ccd83592b5bf6884114a43be302cc79a118c0250f5f4045da6602e 3c72c96482f6eac7c6f2cb45340fc3b880510c320d6ed1bfb0a595f2700b7baf f36cdb2f5fedcd52086eb60187d8299ef43aa7c7368f1d776cd615bca2206469 ab1a135c3c32a63f8a156fcc66c364e8ead3e684e2b6e4d42f8d992985a54a38 9998c79fd49a5c2858ef2912f4dbb228ab718dfc0d0359cb7b74b024a0734a44 86446b2b5a3755e5e78fc1c290b157818c1c614e10e17a7e9bae1c7aa0b7fcf6 74360fc39f0a4e9400eccf298dae55cbb313e514bae8519bc1407aa514603b28 dddb041387115b6585380346e95a3ff4ceed11a3a5569fc4b5c03575a5de5829 4b28210dc4c9646d642fbd689bd05dadde6e9e1b94593bbf859efac13f28c532 96a5439d12e52cfe151fafab31ecb63bc4ffa399ccc14152dd10e15a862b72be 205213da55c228dc66d3c174ddf1b6e5f6291d4beff5b8ae3ce0f829146d3997 da2f4f3cc425294d6fba9407c04c24a6fc89f0c72db12842369844a14df4ff32 fcb90af9e5be20b54ace9526ef83a60c9e320c2c499f9079f4339f463f9b6409 17b797be1d2f5e7090502fc9b4e16c6439042912821190bf7935b2275e64c9d8 0a454ae195174b26a58f785fa25826045a704d360dfe35862772108537725b2a 3e72dbdef9bbdbc5d0286c98c1221434d799f02753e30cf086e0fa4750853f26 91f37f6e194e105a5b07b480d6e8cbb09b0bb080bd80b501cd8c5c8b0c2d4eb5 f8f5544696fdac2861cbecefc08a57343234a8c2bbaa37dd91677068ac3f9237 8e14d794cc0d28564ed57b18fa78856c6f08ebbe02b9ed4b5cd2f812bba98043 3301cdc699ffed900d99873821b80cbb6e2238098ab4a0a67ca3046c79efa49d e902f8038fe11787a82eecb6d3bf572e398462b2c3c68c4dcb50a403f508f319 400d7c8e3579f274f67436655cf9e5ccc93482ffe3eba4dcdfea3eec3d1231ff a7aea0f5b582230ac4b0cf1357b8919939ca4adcf62229ad7c16a19d46d86f08 6371762316519a13dcdd9d7c7e52e7d237c8e7eb2bca9b372450adc555885eb7 53e7a344ffc078257e1a25183e6ab1f6bcaa42506bc8dfd1f95c0c4bebd788e1 2aae64ceba6f18b8b12f8ffbca234a0920d908ae8de21f992603e14bf9cda10d 0a7b9f39092b6c553aa2eef6813a019b4e20d0f8cdb33313ab6cec5a34b68097 d6ef78dc4d1fbabc8c38dc88dcb8e364a122b813badca7bd969d7fffa2a50719 f13ae92568bb67cb177d3031453bac59de0ba4499a4b52e19635fcb0b5a68670 08af9a4b2aa3005aadd70ca3097992129322a0487699cd1184cec3c6732437f6 a4cd17aca6f3ae6da38aefc64af782c01c460a745c0359d00eca8fa3a5725cc6 9f6dd9b4fadc3659f2ce08ec1e88650b207975e5bcc5f47b32b0571614625cae 94e3d3388ffc5ab48534b559d750cbe5be2f104837903b068d648008ccb56fe5 7a441ddbd2e18283050c180992f0ff5f293dd90a86c29ee787f6f341c9aae4a8 32ece7b6647c7dca0e323ca45655f80969fc61d074694ca835803c128cda63e1 ad5c690941196bad5cc48b8ede590b678ee37a3956e4ad243d03540d5611a3d1 a90b743f0950de092f8c23acf7bfe1d912a624a8a45dd651124b31a18610808c 2d850a72db81056d1c27d30949430a403e518e784b0ef065a2f3e85eee17ff02 b7c8edc8900da116185e8f01a3c124c646c5177eea89786b7a581da10865b1c9 c1812b951ad2d42b3cdd201712ebd730ca238697cb30a222314779bff175adc2 6feba7d9ab494a9ed7c8b02b7b5564deed850c0f65c91cb2b49b56d8c6a6bf6f 665e38c21c1ea71bf2e026d0802881d65132d84b5de997f4d80b93c764587ece 3b21d3fbbbbbb257c5d572b2f2710ac94f3d92599968ba758ba74c708b56c0c9 83d7bdbb45faa0bec0f3e480f669027480559bcde0360d272f297bf2d95f9fa9 29ea5cfe83d6986c0321550a467fad5004b1b8d88bc652bad05bc9c489c5d432 6a731233f33a343b55a3ff00f2555ad22fbcd9f61470a34beadb2daf8faa4b0d af13d5a62eb54523c8a159a8e4eb9f2f6c22fac3f34ee9c1a0c10eba99ecebb9 fc6e8e0bb5d6880e867c10ecbab0f89cd07a5f81e4d5a919102755dc69c9f204 c5f6ba6e19e3c3050608a912697f031687127c0136461d524a894fa9c1c9ecf4 a3925a73e677360b8675f444eb38c189c072ef5aee2927835e2a0dbfee8ce070 47af9f9f47179b007ba8047465cb2ed4785a1394b143f728fc1f0318b1fbc023 57f739a81efe3c781e25e552d33c0898b8af4c03979713929937a943624ea133 97ff87e9ff4c1acd825ef90f84273e15100ce33cd73c29d27f79e826a3aa5d56 630e6b6f18ef2027901f32258d7ebd671ad9cac91ce4cd377ad3d272991e5f42 42cc9f267f3921b981a8859adbd2f1955bd1fba02393781f4819a956868cc2de abc54f00dea96edc357cd2c9c2f8c080d1173c819573f09290642115f4d1811e d8018369c3fb07bbfbd2148c2952a3bd3cd2b788aee42ba12fa8009dda02991f 09428eab5f47a48baeaa159840c19e86da45518e3a64ca32f69810aaea0f3f9d a28df65e7b94b78b06ea2a9eb36945f718e5ac3c32b5c34c1fc50b4c811d3358 26311f4f9cb014c109e54256673d2abcc3f9d56d9d671f721a599ef019051778 5ba5da8e7060dd538c1dfc5e0e95a1ac479a3fe032f6d4d0e38da8d6fdc222bb feec673134ebb237c33e099d7a5e245f42a931e5e0a47589bce63f7bfd06770d 5ebacf99428a269927c698c73004ad1778e79c9f36cbb4cb5a2840fb0e3ce613 5fb4eb90daab21ddefdabeedbcccab507c2fb45d02f7ec3395d3960addd5280e 27 b9100825e8dde068adf9db7cd33f52473691cde4dea14d3a7b67bc251894f20b6e6e0d4927d8f1ef7e9541270b827c342197ce78af8a76816222a2971dbc340d22bac2941b043072ebfdb0d3712555c34c5a82a11095908f8918fffa0ff981058e1b1d57137688de70e0c69415ff0c3195ed79e9eec2704f91b1a9e723194206c943eb079fba9dc05a2b7d2db0d3837718fd6cc7428ea6a5e8de17d9515d0c0d8eb3f9a66cde38f92cfa889ec6df2f09615556ab421efaf657b6ad5b27906508f1415dba9d2ae84214e96ae1dfcc02ca43c6a76ae58f583bc42b5e542b869906171256c70230eb1b5719df8bd55026e426a90ccc6a4342d50d871155c98c8d0cd072a24d7cd876b39670eeb6406d23ac481dfbb99da8bae52e2f50c59be0d20c8ab29f2382a9846c3581fcdf3e7dd14e8fcd53c4bdd615158c2dc71d045ccb0e8d524b71db66be503a2833f36fe941c59154a049c957ea4ff2955dd6c51d640d134a07862968eeaa19e71e37995954e1acb95eb527797c44a93a14416068410700615a7399c9c7db46a34eb0d06346051e5b2a2ff399fe3557794b749f7136025e7512e67c11c7714d10cd6c5a08a879921b8b76fef134d66dd09e9d714eab0f9d67ffd31e694b690a04412b16f9dfc72583a40712dbf840d60a9c80cd2d7b0ea413ac52165704748d376303135865718887d86ff6f0ff469b650c3a220b4b0afc7faa3039c0dafc0686d330eddcb3bd310141155916362f632441fa541dc4023d0aa5aa48a4331a35224dddff52ea8be0cbaddf3c244c950bd70f5a3764050b81e7322ce667166c5c3868eb5c2dbba54d1b6b982323b4ff789fa4a07c987d0d1135dcfe210a8f25c73504966c7f62ae7d84749da7fc23eb4698bdaa4d94870ec5e7c128d365a3c682c3205e509eea68fad4c64fcfd939642bf06b9f79fa6c0852c17f4d989b32c68bfc0fb3d3cd8ab590bd5a0a7a17efb8c58159e182ff92015e0e85cf90938e5ac8c0c64532f2ceb36e41bcd8aa517211390f0115d436e60a382ddbf438bcc94a3168be3a670d19ea024ffb293093c0abc7dd39163ab2ad0d8e3be19b1f72ed43028417dde792a25ff6d1122eadeed1c771a35bb64e87da0d9a0cab215c2ac2e27e080c298e5570871731d244bd1dc1b6f7aae0633d156a02d6ef8c5dabeb74d2c1d72bb62aa723c09f28c9defbc4f7829c1f1f2ef6f82001c6f273af1c1b8f0213a898489207dfcebb48684399f5425d487fafeb8f7cfa04c19d636e3b043293b7e16503230c36b307e18e6dce9376d32d7a0e170f3d8b0a935938faa07c0bcf0bc3226c6a90f64628c320b3fe3fe36d1ae4f5f9cca4300abc294cf8ce1d8488731269ac5e365adf64d9787a44ed58f4d0df24ca73e0f20a238fe51e09066a9c253f7ff229d8c93fbd176efa83896340867ead9de28c4f0882fe3361249b8fdaf16352be941c8d01587b4f52eba300ccf1213285d3bca603d2dbc7dcf3f2e9c7fb8c16da6da4ac8a310d33c3fa4c5f04bdd174ccf522f90b4571606da2c95a84899c43b56b0e605c0d3dc7bcc63158a0193ef12c52b3540ed0a98d255f03f876107bd668a726480c223ef10e50ec8909448a5298865ccd0895990fc37f9f6e6de9691b1c67cfad59fb97ad234df24ec69331525bac93420b625c50aab85bbf8dd401871231c0eae1376bb0b3c450f3f3e94fe3bc7ccf2307281fcd48209c51bfced2ef1298c7cfc23c63bcaf803330abed62fc0b96b11e09daf39525ba5fe74605dd323dae6286f2151e1a7f663ac47083d954bf46db490bc3180647109ff86d33d9c1134c5c900d2aa7f1879992c5573c607603d253140b9bc73677f63b6e0af854c48201812b1ce2dc0ff6376f2d0da8ebee69b985db0cc2ab43fc28f2f2999722bc853775e1320d0573d213abc3fb9b648945f8867f0e4ab08632e5c786e45df4d2b0275248ce8d72bd39052e9edbb685da350221d90a11bc2c754ac674220dd55b1ce608c00096473c065703f879b62198aa6ad901061a77f5365e8c28812ddc2c1fbe9c215f2647ed2624cbd0f9cad963f3f9357f05b061a506becab14789dae2641d56aced44862d7676d61f87ceb3db45d22c8d027a849e0f8424e6bb0c275ee051d70f252062ff9dd7cdcf107bfe839d95d49c06e50cfc25cb7e64b04c1ff5170014130d8bfd07ef4faf96600c956d1fc245280a7bddaa51ce33268ef57e7084f580c4d144df9d06f080db24b1c93af3880476064502739945fe48fb43a05db3bdbf9eb0076da4fe98df6e0f21cc6e25951d5a0af076b82ab260f0e6db120030e698334bf80877e298ff1035126e731d0bc5fc0a48468579c0ddd99ddcde3235db260cf3a7b0c65fb68eb1b8cae7646d5b9b9907f29c3534740dd6a617259e4b6382b0054cd1088f6ec2b8ffab72756dbb3a9305135849ba6981d71fe13183a763330e9965b301499dc20c8b1fae52f29331e00d6e86a28cd0d5874df1a704d4c42452750b3efc69942b865b57413bed0c038008ec62b299f8043c9ff6a84cd1924f80f3d474d342b5179df1a70bbc1b1408e60f40e9f6e92a3d6313c5914f0e00786d1b2ab46a2ca546fd8cdab7926cc21ebc0a251597aa93c949981cd4f944d29aa46ce20b1fb7fd1e3a05826c0aa9cf60920974b3287b9c995ae536e2045ddce2cd4d0cfbf7b534865af4411ec774b953da03c87c78b1c045aee12a71b0d9e37e2ada402829f0d611de44693ac2c9ff6f070845d2c1e8bea6d5d1af778ac5aaa022832f97aa9f2019a65f9bb728b8b3a4c30ab2db37bbf7f5e1aa602909cdbc6ed07746e4bb69825accf2fdadff05abf87f0a8907e4c2c8ec3beed33587d234085f333cd26b82a0ae1a4af05bde04b49cdb02b9c76c6377fde2365e398c7eb567b608a7619599da27fa65df7f7e11093b220c220d66804a3f146c2d77b210fc58590e6f1c9b2fff1e99b6d94ba765c1757409ca7f20aedbeac56d5856aaba165934a633ba69448e9070d22e6a1de4bfae150b75844f4763dcc1a9b41d095a542496c33f6d69143daaeccd5d3529d35df86807d213388d79e0a3c2daf99b7e0b7073f615088106e0025c5f8700bb7b38ab18017504dd7ad23716be61ed7d610fadac9728254a62941e98d80e5387a44c84210af0d80964e9bd2a5e5875fa4c22e9182bc4a1ce358e2fcf9aff58f99cbcd48e0a7d96394fa6d3e96642a2456d73484f2cd91b0ebe14285a3ef80b184fd9a1320143e6a9e2c47babb0798a4082422bbb8d351d6a124140710b63842e984f023d04a176d9bfb60fef802ca2f9a184ad5e64dffa1ed268b467a8fcab9bbf32246a0cfac79e86270c4000acd7373a9d3be681efff5127eace29b90ac31bbe045a8108239666e8ff89bdfbcb186740f6e8588e336cbc954868fd2c33b53b9a2cb570024315b2fd68bfc08fff01ad4f437f4c47f8b04725874e0f766c76bcd190840f011e5d452f4394d554bede29661103c117a5df8ca70d107f6cfa3a3b1993392b0375e37c805aec67470f4d7157c775e6ff3acde8e588ba8c8130df439e7427ed04b683e7906e83b92d54994905a8853758390b3b047065c9b4a440ac5377f0700b4b797492fb0e71478b721fd9e1219919a8cb583b1808dfa99e2ee7ee9d23b1094fe83c4056cb416f16281361ada2fe024f659b2ee5fdb581ba92b4dfcd20c000f263f4997ec8ea7ecc38b6ebe5c84651c6fa542f9105863f293e8febf109c500da1705984fc99044c2e6e91948331aa7b095c5b1fb067067dcf607cd1cf5a00065f757516c47a106dea45c4846c18284d77a581724497069fef949a07d05eb0e33d0945fe45b5be145186c07ae2625a33b9ad68a71d03ecb08446777bfe8080136ae0648d060c0fe8b0db26ff12ff2e15a9b4eb9e8ba13fba262fed1992c62060b9da94596a3876fe84794a6f9591a89379293780f449d625dbb33df5d76fd0af20887fe66bcefcb919dc60497264980ec2f3bf85cd983f89c4d2faf96ebe9024940aafec8df42b54344ac75c53156d27158830adb93be28545cb327ac4a4e0a2893ebbc3571b39fd59b663472910b2c9d2d8f483d5d8631bb889a9bafea910f6a5c5b4e09fbe9df395eaebeb1c3926208380bc18573f9f893e81b2201eba90a8cfa7b71a62d576d9e07ef551e89e0e55568223e19f85be9a381584fea8d6a09308d2f6e340ab7a5870188dc17f80080f63536cb685b7f6870f04d161adb7d09cf4303a18cdbb6bdc88eb57b9a3e80ae12530ef25c6f1e9b3e0746a6e11ce8010ec9594afde7a83189c94b9fde62d9bed9972db0e12f1212e4db2acc9ff64202979b580e17b312d0da79598901759bcb9be3651fd415a4dab3e57c843f73a80909f4621ea4f62e582d9efce3addfd6341342afb5775e0b627a2c5a26437fc20c4269b49ee6831e42096f5a5ee1757f8d28fa5d8f4252973157fd31b80796e80f0b592a9cdf263364cde5f5c0cc90b2739cdbc9786922db93e1dde08283e0b5031712bceb7b896a16e20e3e519caf6e05a46e9b067bc4011a090548e4191535044e468a929b60c38562a7da82494f868217bf7b0489715eae8d441edb6cfb950e5776da439d67f48b66f8fc94ee4b42abaaf0dad1b920ffc67a4507e624a60d0ffd465dab5fde1e00fae5f3f3c8aac90b8b3bcfe371c6f623b120555536e12a0acd3bf5a6b8cafdca7f28056f1f889a2f6dceaf78f33e9fbc41db2233205e8d09fcd18d0a7e21ee9277d9389dde0649183589d7c87c68bcbd5e56e4978af361052dcb3a8111e878fd4f718f32f6ebaf607246728aed5c376a83147fcee2136c024ecea5f9d6f019bfed7e2a769dca53f5fac81d3fd32210b6366ef102e2d4ba0f247516c78581b1e2231bcf866fdafc749b2872296a50c1eea69d67775d660e0d5df26453a11912129ce2a9ff89180364ee7059c7d0782944a9b4aaf25a79e60c939331916f69cbba564956993891f64e47bf5829325a051a0b201165fc1d4b044285172b0f12caf314ced876ed0380218ce20d5ed91c56e1c431b61e3922c206e759338c8531b99eba872f7bcc0424caa0255259d4799f046907692b2dc7e40bc51c5cd4345aaaf8c47f9854672f9f14557170564dc80a2c0c2f3e187fa52d074a26439765a40f02cd99fc1b17c6f221cfd941eb11a092a914df4fa02bb7c20c5544a89a12fc85ee9f67f710e11814160b528d2852d253d5c3bf87cdc912ff001df7df72bbda72dcffcb1cc21a057fe74031f69cd4f7e918d17ddd848e176602f19c6a8790dc472d72cca8749e1989044897a7dcaa7aa86334461f644063ee0dad9597acb97b99975cc15d8c1f67ef7ad33d7d358550fbb5354f1f7ae34c250fa393c7a297e1413bc1d675c95d9d08dc345e0fc15ba1c870e0d581f08357270b0f319795ab1b93983f4b7b3c44c45f7d7a7c329347ee032e35760effa95ada07c57e9d5dad18f069234580ae0afca8ee33e6795fe36f91b6036ac328f70c48057114369d440eb8b3afe526a3302eedcb9b924d597d46188ba72dc1d055f7f30cb9f25851c28a8b25d89d97bee2b80c6db6ee8054bf2a0069dc4bc62d8d3c010572f943a18de8ea9cdb1b2bde1820b12cdeb2d9cea7cdefaf356c219a26e0330d86e9bb191f4c2819121f57ff402b7b3c0a30e882484c8bfbe13b1ff5735b2900530701a54e43fbd9a70fc4d3421f329e25f12c886528e83d5f692706761678015a73b223c2b1c48888e781103ebbd03d824961b1dbf090b7011c47537b88ad0755e580bcd1829089b3944abac4b7e320f4382905e4137b9a65cced9516486307fdea39b119bd0fef51989cda5e6a390bfaafbb36760e75b8d26b5b658df22101251bd6c75c442154e9729790252af57c49073c381055a83f157d6422679bb1039393d7bd3581dac7db443a0b018f50c130e0603afe695589d173aa6e54d7400d1f681c65d4a1e8f81210f7739f42218ec6d0086eab19b1689b10fe46b73c9a056b415a47d05ce4d885d4f4e0ab89abad3884a842d380b67be4991e453238e302de37802cf3514ebf234143dce044ca63aca2d351d8996977b396b8e6c696bb011e554bed88c5ca0e5be767c28438fcafbf6f556ad87c18db8180a3bf0116e308883cfa9c19651c20d6ccdb8584102e908514b15e93efc61e627b7e970f567a0f564f023d64a02a339b9870fac5862dfa8b4c9c607b1b08df69082d1a5d627100c425b074647aa1e7c1f75cd5beb828a498971f82e21a902005158e0625249d07fa3541af82ccae70c7a518c6ec6b2bda4564bcc4996b6d5ad547137d7345370e44cb5904a70da55543ce94dc98891527e2c5f73cddc2dfe01f4a93ff47e4f809964ed60d15f413b602db2a5459066beec953e823054fb337e72f383cb41cf806d308688121811b3206c4f81cbc4959c6491611d91e246adc6f6d97fe07cd6f0c845c2ecd1617260761bd1717196a2c8669c677bf8f6e8c79cf19a6e2ee39b30bf716ed6167eb035a69ea3c152c538c9d49619bc1ac2bfd93ddf522288f80e00494158815deed830617a064151e191408e13444c58ac38b6b4081112958ef820ec24320093871db1dd02fe0dfcf8e9a7fdecde8d2200a1b2462fef3cf6ff5500e28c7ce5d92a5cc2831565dd32a871967c6bab1fc6dfb8482f003a841c54bdd076ac4aaa9ce3a99d2e05e1c1e56d89f3d420f901953ea4a7d0053c80f09ca6e07096a6c2589c16753409332c1c074df607a4873095aa8e767d0ea59ee7a73bb02de84ed5aeb5cd38a237890505e05db396ab1f7cdc94463602f3454b66958f308409f3509735458a43ab57e9f84b67fab661fb176ef966e0246935f09ede6cc04d3922426a197b95a99774d89b1ba2b317864e86c1a4ce8eb8afc07e8adcf3c0982f5ec7f6a9741e76a343fcbe223b485eff8c67563acadcb16f396c28b10ad0e8139eece87f2ee7f0a0705934929dea9d44c59ec11982417b3df1041d048c50b29dfd3cc7a51a7c429bf8e7ed990190baeacaea9d9a4f3459017bd5bb67eed079b668e6656e1816cd248002ff65eb5ef5e6aa529feeba3d0e2697072ae3b27062054afcc6c476e90e5c4ef3cc41760e0a28953cadc26fd4ff8731b3d73e60c028ecb5051fdde8c88d715f835becac741fecf40ae9f61855396a6d3dce87c0708f0a69977784b770c938baaa0b21302712c8186f49879caa24b0be0be947cf7001333448fba69748a8976e1d2138bf0b8c35febdda4b71bde89c251b6c362a90b2721613cbba18fc48536e21bdbc523a9395995e51bbb187c34daa5d81c8d21049f156611382a5b3bec2486b2ade41daaa40a6354b29254393c6abd81ae05b502597a9fc1f48f1ea027d9fb9d70a9eebb825ac5718f022baf5c4a89adf4054a0205049df98e5f98687315124d456d4f8356c38f50329bd31f8657c17176d556027a31a59a59378635a55da0a6026fd726bc8557d266c5e5074ec6b17ee2d826011c1bda6bf8bfeb943eb6b14826e5f671f84c297df49b2adcde7d95cfb28fe00ca77973a598a10c75b5eb101a3721f0aadd2603adfc4a0f0d765f93cd796d140a29774b64e78212347427444afb38e62dcfe2b1ee6e3937544f561de6e44d93003bd082bf8feef5c0e1b0301b279cf3e9f2723faa9adebc3ad6c4bdddc296450c7cfd00844b02110de3be7406194dab771efa6f1dfd0241396493f64f296fb6020a6e4a01f915f8b1ed37364d1ea88354465031b5e6bdf36f930e8691e4d9570bd2dfec97b5070328f74d8cd33023a6cbb7f929d9ed9806b30714db2c8ae79d060b614f42376aa5f690bea1258c2313a1900ca5c8bb56f2b386c1e07400bb350b8517475d89b6864a367426842fdec6121244c2eccbe9bccf4bbd5b753e523e023581a049fe5d01444e1d4478f3ee4aebb5d04daf870e4c0a8f22d22b9415c0006d3e8d22af77d16de11c1de7bb9dc6d5b5a3df99535c0be2baac77a5b1b7f0077df32c9e45cc017f7c95d8d9f5f7a0136cfd029cdb9c592e938a48281489c80d8e9f2b290d070b33bbe8eed37c6a61082c6ce94125b26266c006a75aadf4c8099514809a9d1cb686b9597a8b49dc46a39b47b5f116d8f03d044dbec4e6c7500f545d39176cfa3b926eb4d997f886baa9f3074653fee031f25669bd96554e350e0d2a1f75aff85615f2b29285768dbd4a5da9a2d97c2537c9eb3b7fbc7346500dc7e3c917c9ccbad97d91802ba1fc02c197a332e6307fdc6525bac4750923ca08bf63d4855deb5fc02fed01641a152daf55ff11c3e203cfa0a1a0e77adf92800ac6fa084097c8b71db55a51b719b9629aebe36a296a11a6590713795d80bd230e6cd4b38bc2217b7c9114cea413059d62a26a10f81f93f857053308b47e0c0405f6130b9a53edf31147702e5518b4982ce07f3bb88a6c28edaba4bc88af582507280622e6722560405db20eae4f25dbd2c05094b8580442c17c6f1fb3d18071085a7ec45e99ac92fc19be6b40b430532ed6d6f568fe948262a08de15c8e23b501f9ed111b0b532a111f3ff688d78e25920dd0309278a9dd4314833eebff963d03998c3a1d1d5e3b0d4e614029aa9431979f8fd47d2211219e19f638bfd02ad508d682978ca5be9bc67ecca0476e11b6e315f132b06ee88c8327b3c234cd42190e17e329ac83f2f37b59d3d3083d433f21ec75f9c5882fdf5be9b33dc7689aed0f85b72e0dd9a69d1cedeb8485e5407ce76b0e978c6c16b37b6be5778de7fff80c5c84f9e06cd95993c582e08778dbd19d7f0d1a647fde2856caf4b53838b30b01a0ec444c64e99de188946860cac2043c9d88a3e90ed01fcc3417d4f8888ead02accb8fe80eed47e600a9dcb2127623567425f2445da5ec4c4eac851eca5732010e0f3e1af6c9da57653f807ab2e62e834508bf5b66e8f3bfc7070903063bd900df315269cbf6947fc41b47945f1f327f4b0c02c58fa911cf945fc338f463c704f076c10aa74f027aba33788ee468d8e500e7f60e3a7bc2ce458551106fd22d0b159c25bcfbec288d346656b748a8e4663768301a4beb98446f8d5b1c2aba080f086aaaa7e2b372887655bab738ab160f0ab513016590ddf2cea2c444ef21160c7b95757054853e5894ad9260844ebe5e7a8324881c2d53c282e421549b76290a629055187a98c45a934196214157d4e8e9bc1b9b3c4c98e497c9b71ba7e86508abb25d2329c2e895dce258ed4960e0225cd9abada365831c94863195bfd9b600ff6d44e90299ab4fb79dd4eed887af7fbd91b8ab70902912e63c3f66e39310062207ab5af0457f95fe94aeed0efd7ad4ca4161db2e588e9eaf8376d112577a048472fc9a64e56c0d13d56b2f2ba631c74f21ca6103050ad2bebbf01d5333dc03f6b2bc2f5a0f811034dfc03d8ca5e84fc060bf665336457063f1794f455df80f4e342ba4dc4f07dd1e50c2c766e8477d9b136c5bd86b38039c7af4fda90c900779df34f50f1e2485b981bb235386f74125ba8203200b89f510211aafcc6f250f4f7c1cd7a3b2be94e616d4d11c7e1f40fd03d5b17c1511a4989cf94594494b0a9b0d59f389a06b629bf9182064b6c475f146f1d7420e604ccf30204725267a03a70c2ad5b95dd973dc5acb0173e0e783981c09b5d7ce6ae429736080c5831e003b24bbe747d0fb032cdd8ac143ddcbd96c3711f3bfb5658dfc40a1f17d62ea0c33a415410a27ef67de554d3739b8f031a50404d923ea6cc1a5fd652545dc8106dd3d8548705a2475898572b2c7adbe187362fe5ffcf8fa175bb5c8abe21f68000e30925eb5a619a906c0af37e917a40e9772c067319ab74cb2b8947562e3ee0bc9223288a0659dd7b92dcfdef5e9d116ed8f0a4a762e1f5bd00a0a0b0ea5d40ba941f78bff8c0d9b6fd9109a6a1a9555eadc12eeb7710b7e2aa3ec637068e50653ae645a102cb7c796322ba434c44955d7d267606fa82ff4873d2338154f5b01d48ea6ea4f15775e7ef443c78cfe50c23af7cc75a1f82c36f00cb44579e0260f6eea4c8bfa2541da77faa58fbb8263da8783c3a34b33616c5f42a414dd27330fd30177c260704cfb64f380cde109b41e7c18d62c308761c0d4ade58512156e02a21bbefa94f4a596f81fd03e7a727f1258a8a94f1dc3371bc4e545747ff6b408bd32185381978da89b2717c6672174d40ec10776c50d9ee194fb81c7a3cb09035f2fe071a62778757a8bad5e4fd547cf9fc8f89f3c63c8f8cdf573851113ae045c0fb2876e538bc2be575cabdf5d8b121406beee8d0ff1c713416fe8ebf73909cebdab8b921446232f1cabe3af68b2e58c358b34911c21fb0dc8fd7545b782092b26dbb00a6f16f501d4b81852040e8327c0ffaf77a2b1c6085c3ed45d392205a9934a94780bc7108ce4fae66e61a95653ac4637f5ca2956a4fbc5d99d2b8f05ec6269049a214215de4ec3b0e2d9992366e70f7ce8c8d410146fb8d1ad72d905d96f873f5f74b13392a71f443033d63560757da0135f51f7ed24ad08d28c4b01fabc6a685c85406b0c41776b3d28bd148412b4bbc61f0130bd0cc629b5841803fa9f3b48b5253850bf5892256a063e090a7ecfd98bb00b450090a00b23c69e03f9854746e7c8ca24ac08a8c984457a42c8eaa6f8cf253f5c872f84662a88020433371dac324e9898d6444cf68dbddb583acfb1e433a47b204e9242a9cc05df08ffb379835890138b7e1f430ec40e8d4f1cf37ae74bbe669bf71a09c4dc0ab705 +generate_ring_signature f9a706b8e2c8c11ad955fc433e22bbfe8f0514a6149dd03198dc250fae467c18 e11869e1cf451d2cf28a50085d49772e035632d2a0c7050e5fd268f33bdcfe47 51 1d8ca8a5f3565799e1784d4013d98468749e7bacfab2468ca435f10fd127fea2 524340b98df0328ce17e3de25acf3d30574dbecb9e5cbccb09f343423042871d 4fdbb8f668e0f56fa54a307bf0638c391985267e59cd6f656b10c406b450177b 867735d8c1a7de5e338f6118b74228d80d09c9d6cbf0cb02af31f63b2395c8bf 9988cfa13d5d52e2bed9424b90dbbcee2e1502cb1680368bfc5e8253d3ac1dd3 6be02afad0d99522a758962b1fe2252d8f186f32bd002efb52f46e6ef5b49c38 d76b337212fa5a4439d4cf462edd91b3d2620cebf91c19f494785c97d13080af ebc4533d115ef548a29d72b844a269eab72379d3777b1d0ac4b4102ece25b9fe ba74501e410779223f0dc2f00e0ca0a2103180358d883080af34ad6df16966ef 06ba5a0566e4dd9a6c63193c8c41a2b292535a0a5572b0a9dff367838ef2053c 2a9a81422c91f436367133b4a0d35f14a4aa3fda90b297c83bfe9249e19daf8c 4015773db77ca1923286d98b1c5ba9da4c45cf6ca4a15cd40a60186bc5b872a8 1976182416e39854ec3e2a972395627ccc0342b1d66dadad77f44825c98cf4b2 c20d1807fb677494e9eaa308d3cee6caf650e3977bc249d744938c827c9194b1 a19a738ed4643efc04a431d59e02714028e49c0b635b2ca3288d09bdeb3314c9 9642d74a00032e4b465d002b526334f0eff77fad0924401bd8a21a9efa4bec47 d57bb76fa16e524120cefcf3e8452bf250e791a6a90a7784aee4998014012048 a7985083f41c7a5d896b317c2cafa3f3d9056ecc571cd3b0849d9858ea599c12 a44796399ad0ed580cba7ef1f9491c9bb4fe06896931dc8e33ba067ff648ed3b a914ac356ada49590c838f393bb0ddb1596b892ca815a6255c6c0d657b3335ed 41b4e218d8eae0c527477b0d106c4a0dc6f443eb460ab648dfe99f864c68a48c 482c2110fb49df07fd199d3e3864f6100c98f0f0a24fc190747eca87d1a712f0 75d79ae313a4f8e43c318ebb20f06fc7561f83fc5fa7abf79936213d91cd78a5 6ab20047474f129546d26df477de5adf3bd5312c3eff73ace9991160f4e359fd 36a4866da22468e1a7606c933e85dc1698d68719f1f978311c8ce5e833ea4cbe c74fac19dbb913cffedcd683709f35f1b72663e8570ad2f0c10cca10a65e5e64 fa8dbb6659fbd2ad185a278ae8d2042981f783a61a0131b09c1fc405262c0013 53b1d4d7fae4870b272aaaf72151607abdab7d68c03b12a02b33847920b6a875 b902d117b2e21b88ec571618126ea8a58e3d9d6d3415253a7a9e2a4833ab4c68 869ccf369a1aa1ca28fcde65b212a1e67cdfdb9b795b06e62a166cbfb94bb638 73309f434bd5c9b6d6f020b95bd03169fa8c79d278f3bd39684ad5048580fa31 a2c2f0f97cd5600c046ad31cc28ae8674a8d7f714c15f10a6f6486ac05c819a6 1e0bd550f16014f076a6e134815b7a0d32a792a347b2f87e63aab51b90b3e63b 281f4832e2e18470e452610ce21aeb82aa3c043db9e5f566c9b099d201f0e4ac ff6e84091feee4a074087b5604ae9a247eff5c40efa5965808a9294586dfbd6d 0fb54e8abe6043d656ca09f0764a5c3658cfac41aaed1369561fc7d350ec22dd 806e039420e643b359503c358a038783a448cf3286470996f23f42d97e3ca2cc 4bf4fe62ae3a4f974466758313cfcc11506b73c95467cc474ed3f9458274dab4 372cce5d618d7f557c3e5ce0c0f74280351838242547aa953e6ef42f3830c976 4d906526f55f89decbe56db16ea86470b1e4bc1aba22ffd2f6a309b8c442cbc8 2c9ae1d7503ff44ba235e67c1eb693c10eb12dd1d2138ec3353876c1ff723ba5 a06c34921f2f478fc566be0247688b123debef97e52b3456599188fd4cc6ab9d dcf97feccdc2ce8fcb02b3e8bd56cacc7aca9c0a10d0805c96859c23d6f05e44 9ae77bb8536760b89edb2a7ea0d2ab7ecd8275ff6c47dca7ec0fa796fa4abad6 b7be8c547e161be70cb364e9ee272a0fc59ee43bc0fc8d93723a3a1cbd0be989 15bb52554971b6ac20eddf6572168b7f7bdb8d1d2c9792fd4ed0e57db390f642 837527985cd9755ee5eb444932d09130b6617092334d0bb6f952a80bece653cd 4e81ee5164dcb513ed81e642cfe8f27cd85b8f2028e1e0c8a0b2b4c681bb111f 125de8b8eed3e6a39e15636c9f41875eccb656f4c9495e463abedc7c0b1a5af8 4c4b6685a34b0a64a212e065a64af314013132daf153c9d7079292340724999e 9b9e0e2ac22c4be269cd44ae0fd0320230f57bbe081415831d4da5ff3a4c6fe4 58eb36354ac360f689b8c21f161c96a50875b9b9532c368ee07c3ae182d7140c 26 a70bbea659b59280f89f11706f2f30ba93078b0a6459588f277a930278c044040584d889975dae5cf2713fb2064a7c22d42ecf9a6172a4e8ab693b6f0248900586da3343de25a8ceb05f6e17428973602cb1968ba0473faeeb1aefa337d717062cccb2057e2383f26335e9f51075e936de57066339822bbf74f6668879603b09e1a9277811b709ec8c0ec972e1ee65ed0720b3f6bec68e3bfac2d2fdc5e4c809475780d60293408ada1efff85ae49dc1ec47146fe42c2715978129792b6c310ddf09a301388aa34378a19d1ede4cf0d4614ce6a7d36bf66123c1695f5b9a1f05bb81bd0775826725a084c47855c5e5c5940e12947e95ca15f6b0a0d3e212c505810a3c3e3550f08d7a27c46fb39927c191252986dbec39f9856fd4448d7df4077026cc7868b7c668a0171c7186b6d28b1e40b3ec040a6947f636082eb0c0c60850ab79d597a7d94f46d290e84621e9f722d05fa59dad7c927bb7bf8d18d5480d30eba39a3a84ab4ab4f95c681472bf8fdaeabbb0ea0ada3043e0d8275c23910e59dd7da4c1a7c421339c9cc69bf703332cfc4be3f2e1a368b70c985c8575b90c4301e64025eada285f6bf3c14c903467c63622c8ad936c9c5ba789fa37dfb9055d26c21b29646a25564fa9bbe6b2dfa0a226f7951a34207ba1fdcd70a4f2df06a2d2465482ac7454551f85a3d32538ea301d2f32cb3c4b428c0dd55103378a04e870cd03eccc01b3061debd9772cca6f1259e547a39603f18ebe5d98dd119e054b8ee1d13fa5c04b85d69df54573de19aed9a2a8afe87ca744b3a82718dc260b6b859c7b13d87572a7d53badc9b8871049cd657c1125b816b6b718b98a1b150c8e6931617affe4ddde85c99befce437291836ae6e38bdac4447a447cdb6deb0cd81ca227d84304a6a0aa84248ef11accafebdbc99ee3420daeb1dd9dec7a36093ce9753b2cb8f192d8800ea8f19853ceb025cc04c956f940b30ff4ebdc310406cb06bb5aff6ec948f0dd20c9cdf82e5bf971f9e21fafbb2db6c7aa6408780e085741b8fd096ae340dc3989d60c67ad137fd9f755b6a63c8c08704e14005eaf0580befde99202baf133273e0b98763b0e56d7de3a117e1bd5678eb27bf2dac205d67fcf6a84e2d7e0c815765c1ec03ae1eea84e6a12c0d0974c20985fd2cb17007e9b0b78b3916007c94d0eb54218b65066b9753f0df8ff27aa052c4a33dd850dcf0512c3e9e66a1295142b8c7dcf67f01717bf41db7a68a79c5c1257784e820c8944c65cd8814d97df7337843d93381b30b5d0729147b85dea31f4e2be17390e191ab652406fbd30ee126ac09100e25b396b9c62b4cee73e8eea1cdfab1a710bfc9ca7039605aed48d9ab1120c1566c96eb17821cc1eeae3d34848141948f507d1ac52fe0c8e40a2e62a503785b9c5ac1ca18d323fb699bf2342ba9e9e8b6b02d7502950fe8fe6927d4c121fb205e6a7dd82caf88eacf730baf7d650060eab0a999da80da576d7052c52143fe4e8034660d46893f323ff7423089234d589280528c373bab797eef45d6ae7387e76b235a7684d1e1bf88e87b99852a17c33ff04b013afb1dd6a9c6e58d3048950edcb46cf61fd81fdd0401188639d63337f2d03ee0d45a59c4c7c949dfed157d59fd908d434e7e9367911c3d4297c055f5d9c0d76221f92a5ae9ba5e531dceb738c338491efc38b4bbc85a617b39a181b176707737b6692cdadc507538d5c2b579f016feab76d1adc1ee5077d5ced6426952604c81d4828acb2d2091f04d3c0f8eb78eb32bd68c18604eedbdef6bd0d8970bc05b558253ee0c8402a23d18ebf781f340420197897cb112d812501dca57341a3033276deabb8888f91ee3f961332ec53006dae4c41acf50a1778e3d15460156e04f0b7fc05a4db498108ef0f33918dd8dbbe0e8fdbb12a7aea890d6139bb083c06aae8cb761dbe34f44a1d8e79e7801a47324555e3d323c64040853035b97fe1083c54acf2101af949adc7db6e100ff14a4d75df7e7dc59f0313b47b484030dc0c3f98b9b3cc0b7b57dea80f241a9f5b9bc00742bc3dc539e46d046fdbee6a6f08a0afba17ed0dbcdd5dd5eddbc743c648898386dacd3be4a256845b1432a7330851bbda5a2efa599dd72061f053386841693e0b6d9b0e306f36a66c8273114607ad0093ae9c67801ae19c0686cec69710a8a228e618508eed7858394ae53ab600588e21bab86c0e3bde801919df4808f87a8e58888470dae51e84988f22678d0e0cd9b3cbfeec9fcb7cf397c285092bdc7a4b9d37336cbdb0ce5b23277e84e50f2d21acf77e1c43043adf97b6f469d2eb7acc473483695654ae26f981fc11a1018e02612e1eba2f63b79e0d44cd3211bcbe7b69ce167bce44634b4bf0c0d4b305c3b58a7f55f97e1b088624b831e698ced99095b636a6cb185daf0fc5aae9ca09ce34c592801bbd5b6271c842fcc39725114a257474d6b749158968b96d9cf0020ae568273cbbabf2cb5d2a0a829a51dd8875e689479e5278ecfe01ce3ef6340e79f64f2369f9f08f6c387baeb9a02b0a46ccd3ccc1ef388b7c43cade99f92302df2aff7634221aa5b00d1ec6c730696cb15a95debcdfcec41b71c7688c33930010c183a6994a757bc2a2b92a2610f803d4fbcc5435d223d205460b569532a00476c310f2b178be1dcdaed154923cbc437fec882c49a5e78d8f5687880a22470404c66da44b25903ce2582079bf3e8ce1223886fd4ff46e3ea9cd2d8266b2c201957e90adbda51b70460387348ac700fb151d4b82952837f23bf1a641e9a55e0c5948ff2ac84d0f0836e9ae3a7a6f4891af27ea9e6ba117298f575d88c3f845015bbef52461e4593506344a5f9709ba59777bf0e84cfe7b1b09e60aa1297b34034055c9268589b9331a6d7d6ae1890cb4fb1804b02665b9a1ec9113d6d4e6240310c60f144ec333dab57414523688bf2b24aba24a644df56ad3404890194ec402f2283359fb524b26f20f351bca9ee0b8c17b2a9d1b5d80f6f986a6afa7225b022a4c9e161ab17148a6c6bae7977398999fdb184c5cc07d83a63375bc8e46a30aeb170b4c11c7bd7c29e43231422061875d28497bc2976721f0c0e9c07218990840f7f1f0bd02a730569450691766a1da0e81a6d87f504d2934812a818cbc0c03c98e53fd26cff6d37080463c8070368b3565d798eac0e495b65f48af612c9706a3fd5130aaafee562fc7adcc68ffd31012ed98bd073299396a6d51ca12e2d30a08dcece0858cc80b392213868de96ee757e3ce85173367571000a31da8e188021c4115b9958c1dd464307d84cf98d184e49dae66f0287355375b04021b9df50829837e9aa3896537f4fc6896fd2a2836dafcbac08f2aad4e8ba563ba5ac8400eaf54ba7e66595c5b9b47a9ad69737de48a95a26f18dd03048096d5b227185c0d35836e59ef54c28d706d36ee6c075cdac456e2eb6ecdf67742ae71576fc96d02b1ccb339d0f66eb52971bfaa98514be4480f0805a4860b82ed15c909210aef0238991474bf0af9902e6b19d3578af4a16a8f787c3f0f406fcd2db455d7bfc70843df753705f56eb6195fa39a4c75c47a842e32c052f1c87f84bbbfd5f82829066ad473225b7e3ace27666e293cffa7cce1d2889527388c8970bbc53fddf21f024cbbb2eb31337b21038e68051bbfa2e2dece03504caa1bc8e5de7bf383efda022aabfc4d03e799b122f1150962ab96878cc75a53e1f37b83398ce0e1d781990e4d5cf9f039e960ea5b4d0d7f28f2d9cc1368476ab33c20608c6ebbc9e32d1b0449ed2d46486f5a47aa99b4b27adccf192f8ff67d35f50580c9145a63adb9c10f1da65941a82de83c076778db6b7209c7d2b9e2199f63152e06ff4295162b14074d4c06f69ffc4457b33b7252461e5e1a1d1665e3e03d6521658ca66e1629980465d29fc4eaf31e067710fd3fd3380f3050e6e785ea8c2338bb16be5ac4f9490e9af2bedde4a947a2dbcdf14e09304755aa704aa618059abfd14db3a173dbcf0044b0002bcea993ce62c12e6626848c5c49ca5eb2cd6edf76386e8b0ad96dab084a387690eef03de9ae45a87d7704b7eace2d781bae18a99c99f2b8c306443a01cb00a9943f013dd1b1f00a21c3359894d1b146878b253ecdd25977ba7d2e510a3c78b70bc94c56b40f4f8bd8e54108b7b3793b7e01d4f0c401c29f75c8608c07915229b3f906108ed1eed4f885f9275a1932c6a0742e8c92d0e7c6fdc098fd02bc0140fa0c177d74551c29c5012c1b8f8b19bf9b0053b184d1f150de6c75be08fde07c0215ddc36c3b9f3f12971382c8cfc5baac60de44b5479d0b97be4a1c0cc290995fe16c011a5fa76492c88e7608ed82d17606e4d772394660a04a810e059ce4f1f81f0b5b2b39be07ee5fd2b57ff87853b8aff3d7394c0c7a333deddb0a1e3fb336f54c6f40d744d782cd0c7c5592c0fcc73ea5899d5f1502f09b4dee04cd402eca7f076d2525d8758828e30811fadb12f3d94bbbdf2affeb73a4d634094d7b461e507d71129dba6dec68c99f4033390cdbddc0b873db73bf036e5c0a03220f8509cf212ca8903e23c96712130123c9552dc05bc1de0469eee101ad760d +generate_ring_signature 1770122a086ed129b1368de80b124c0d3b35a341b902236d64b29223ec90b6a9 c91a5d96fa4d03e4dca770f77e72a7ad30bcddeff395e1c6f9a4bc10e1ed30c8 1 ff27c571dd9c52ff997782b1baead97ff203dad9d8e38b175e538584cecdc9f3 eddc8e44ec334afdaf33bf3bd970e020adb1910e993b6267231d73094808d700 0 8ff4114eaca4c7ff39b7fdacc3fbf3cba0ed24f3bd95a1e0986a0168feb2dc0b5448ef4ae1fdd3b61941423c90c3a35fcbc094b469ab4202a189aac8f12ef80a +generate_ring_signature cc59daa1c01d9e00170305fb202697d3556c39ea2700291ef71b8e4a8a2b0ca0 6daeb39a3d9422e5d12edf6a76760ad33251550900cd2b70ec32df4f8272b125 12 9c8cca603905b4599c7071d2f5ea71e647e04fe3bc07c8b8ce6d46c257ebdc89 95fb88ecb5012f302926834141c838358191d0b5b8f51768742f782feae253c8 7ab94894eff48d592cf9cf9e7ec5abbf1423734b4f10530bb04c6a6a09b356d0 793926a89857314b0da69d3e569674cadf1ddc8f37607a80b5f49d36020116ce c3cbe8928f78db289730834e7f552d504d57e9fe0965762bbf757302ace4676e 990d423fcb3d171604d926b32073fad3e26ae172520dc10987d4c496b9cb4165 b0cfd74e14ddccf3e1ae8139b71f9ade1217e1f2cc987a95a032104edc5c3c93 a433061b3825dd0cb10dc1a83e47abd2f4588b19ccc546975585e05d3e5add3f c0aa7283c28060741cc579d263cb6d58ebbb208ce55c1187e46faa89732312bb 559c4a080e6db091b104f062d59450d78c04eb6d1171a2d22c78b8b9c7352583 d5c6f2ff8e4c5833f6d4a89410e0eba5d3b32c9c4cb6d1a7ede06f20c798b4a0 61604ef0d082b3de5812e02abd63bbd524b8f5e1903eb8ffc59847dcbd9439f9 91f7e43cae2c8d8db904693aa8c99c93e313bc788b412710aa6e9ca46e825d0d 2 e419ba1969ec7436e29a8d712c554d1ed06a386080a8a89760ae71303280140526b9e46e3f9edafa0422d5a3f5593517bbaed51e239981a459b96614a4b7510b3c80f95477ae3a511044136bc7fe08fc8476fc4d3e0375ddd487f7d6af955b08187e880debacbdd12fb2c96bc3ca874a5c3966d9810d47440a57e84777a39e0133b29b7930ae443cfeea1dfa2e1c3e05016006c33a1d21cfbe426ce9d1f3d502c9f1dd27c736ce387d185126674e436e416f3025b7a15ee8346b0f221fe4da02e3f8e43da5a94796726b1b17aa31087a20371e01f89769135c9ade46f6d0b7094b722c9958f49515bd60952d6e1d04025401fc5459c3e2c7aa909b54c0756903a7249e32cb6b63e9756dfc33c7bb6696412751ea121adf3328ab24dfdc721f0d6d216bb0b4cf9c1db96727adc69e0d31bb06c0fd543eaa77161b920af8e7a00c6db6d30a4d8639de3baa092a85e44b0b1c6230b9536bc2c5b7721f4671e2ba032011a1a6d61a40993d4563461e5cece141406838d9639026dbbb6ef43d8cf10ae6b122a1771e7a26bc357179bf8742fe3ab78fe44d133a68534280f08b0f9f0560b3153fabba83b20baca22cfe907bf299f1db71997ddb949d85d66bb7acaf0c7d44ac6ec5cf0f5b8145e44b97e490ab2135b7db704ee8c446ad8f170850ad067ba77a9ac25cbd764ad2fc2bac4fab36149f8ef1d92f2d2fd8462daa2d143307a3d2e22989f2011af9ab524dda6cc51c366b4a63f1761bfa0799622a864ad403688369c0e9e136701911f86a679728d7c49f590e63e8ec118939a3edfaabb50b959690c9f8b7773b3e59212796740eef3efea01bafe0b541e9f2ed6d8c7227005ff6f638d276f2f3e75f49c3eb3705399bd54d6f0f5c5d3e61d18459f9535b07c22b79ef3c1f8e919ffd8112c0435c90c1e3368f7eabc7207f9df8f3e55c3f01cec17fa860c39441ad7339394e3be01a752a9401b7a09427146f6e2b7e58d90c026ead63ac907b72cc59726ef4c8949a2c0186f6d945f7ed49ecfd4418d55d0add42d1b45486da58cec20cc70117ecd613ecb0b31eb4da960b10cace94615708 +generate_ring_signature a738d31148a42df4fc161df83c16a50b1c3367c991873f0cb39267170e495b13 84a17edcbe2f35423e6f498a81a5c18db37dc9c5b59f661a9baaea66cdcc0a42 2 54e9ec97ec5b2733b7df3f497ad1f92a3756188260f1ff1a3751499f4601cc21 2a76287df15556a92965326816626b436dddb068e0c3187ac6676f5197af141a c3980bd877cfff32fa25a65984a4dbfe0985d1a975df33e595df693ae3f1a50d 1 eaddf9277aa7cb4423d9bd2dd6f571017899d6dfbc541541164c515bb92c7a035c151ae2cabb75a3cab34eda53fb192c0857e16e488cb950573ce7c8e0bf7607ac7cc087238127aecd84eaddefbd321da8a20b1512691d4070f0aaaaba8f59005ea8c73e12ccbf0c42be646caa5d0c0a4e9552dd14ea944d2b2813252914b700 +generate_ring_signature 4b190b5d3e33114c957f34f0811044e239949d02dcb5ddccf5596853651f81ad 3ba9c3c8f3f9d229df49d2eb5759f08d01754eefb51f726f105f464ced3638f6 16 5eaacbd40d350d03e05e2e5becfe33b3105ea2572b3cad0e0e18849912c67c9c 3fcaacb57427e83fe23eb9ac394825793498fccc38709dad45eda9a341e75962 fc20c79b6987f8fa7bf3924f865268790084a01372873a06a1bd6b48640b6f1a 11e5c5767030717e09ce64c98b69594ab0fc4146c0ee02a944bf69fe6697d3a1 e3fc88a384efcc992b2385c43a3577a506bc27afe14863544eabc0fc90a4cd64 eb86cd3fb7ca22b3ecb4a16f3d85e1c346321e87434786ff884f6fe690fc0afb b06da4e96e9d1734b980445a5e5dc1b49a4466afa08995e8f032d1a16a929264 2206f4ecae6fe77733c800a3df527d0d537cea3510a9feced950997962de70da 85ea7fa4dff97d58e152c40b9a6a3e44cdeb344191bcef82fafe922170b5a3fd fbfb89aa3566649295bfe7f6142598c49b47582126c255e5a2e5eba4c95d9c64 4e78e975a7356b3253dde7c83f873dcd467f846a16d5a7a9ba262a97cf60bd7d 541b340f6a73e6cdd9ce8855c28ed05b11dd1925a5434b2d9dfd35597098d057 c4905be0bdce6ac9a9f09c52843e42b49ef0c094b68711802eb407951bff7e55 6add7607917e7e6431f0ff259f1621f15e0a3eb9c45fa3bacc4fad54675abbc0 a32261f9b0d498cbd8e8395793e4fd2675819167869f55dcb0b61c46f063fbe0 7d0e1f41a306b05317b0009eadc247b00cd0bf9ffcf995830f47c91605e78b48 15a515d245d03ec021d6edd63934c3549fe6557f318392ddef4aaf9660843e02 10 8a6fa568f5a7de4f0a1297e855fb23b8726a2c45c8c7a600ef5343d7563a170f48b84cada81e9454beeaf01bb65c1fbdcba7a28c091053b8fd34c40e0fe8a6049670236fb509b808799ba505d9586e5b1a339d7364ede4f36e3cb3e12137440eef92be17298831458b79cdb3e7effe35a40c6e4e7268898ad729ced30f14100da60f2661497ba2cdd619a61dec11a9460b1529a9965de008da1fcf0e7c0aaa030b4f83466b208c16e27b0fecdb5f7247f50688846f79f08c488c5634cca69b0fe707fa74cc23631f5089690b31e04c5b3cf8e095ea686d3546b0459870286b008df432be097d28c798c30a2f8e711e8ca1b771f6df17305b9261d1d3c7c78f0ea71ebd450b921983dd9f5012c2be6fc8a84ff7f1c7d5774da045f913b12e400bf3a34a9c0177ebb9bb6e4f5f7985c4ffd8e9b571510999fd6bd9c3f74db30005f3d0c2f92d79621d2e796cf82809fc44291fb86ee5a297a191dee132f7e394000f90c61e8b7fbff096678e5765b20c42f22a3be1e053dc0e6ca4de15e9211b047d4228e495936105ef596ed0a7ee6e548808b7e7c2bf84b6f9663a2e277d30099c1be1bf081a5f31c981930cbcbcd2790fe8ec1cf00f5022ad2ac7fd51c1c20b520db3d9b5d6f30af2ff2baa2cdfd1984a4ba744922688aa8b313896b547bf0f96854547f981039048ea97a531eda340af60836ee79eaeb0c275720f4e8e8b0aa724f4a2ad75c3e305be841597ff93cdf7cd585a898b2e5c216e78306d520e04ecae7c883c256bbf7b0a451bbfbab35df144e963514d9aff4482259a6c5214026931a8f2c1cba9a949e63087d4133f02d7d0d877e4bf39eb79177a43096c2a08c8c845644300f1ad8b253fd63b874e4529083ad6661d9ff2902b5552d113b30e002b07b42287cae86e99cda584cd9d6e7306777c4df3f29410ae8ee26b14450576b93824942f65e29445251aa989ddd265070f9cde50d74ad4235ffcfad7470bf18a32c19da7e9735604d58edc1cc88c3f81b5a9d8f3122bef969a488c61a50a8bf6dd50314f600be74cf1889b9254ad958214bda3f849df97f096541e15600b68189a49b7969c0cc3c0b029e24fef03e9351582d0ef9fa709f0b5a17715dc08fce5772ad6f8842226362775033b553c2d065ba6a0d63ff340311684c1fb23046a5e2af21d467dcdd4a8dfd8d0414d2eed3c741c8f502c8e5b230f4057491401d64fe13123a8168a3df6f0c5fa729e0f19425cbd8f15843dcf7e2561b039180c37bfd49f021e97bb6aab6f07fae225171a98c19ac3892a3425d358d412144d09a681beb7957425460e151856c0448c55e27b9e8735e55bda89a20dde6cfbe8004f347ca14cbcbfc5097046d8d2d0ae0ef2183c4717673757740755fde4b48f0fc30422be2de9a90bb13949596614cd2d729423b92f2d69671b0b0dc4ad7d710d +generate_ring_signature 63ec665826ea3cb0250a4617d6f9e6218928fd065cbdffe0359ea728d9fe7371 ff740390e98836b0e8b3aea154211edcd72318c0bf0bac0bff5dcb39aacf15de 114 ad26ab3b39280c2c7796b53ae0532d532f82103823df266c6aee2b92199e453a b4672f0c0060f1ca07d7842cbb7129264668b6bfdcb29ca3b6b8162450fea05f 71a17bda5a2d5e2e94d8edc8b505224e7440e3d6f15b4812112067441356b32d 0ca818905a507c61f2c1f46c578d7c668239c401859aed41791ea174c83e50b8 5a3b50d52d14b0b9645b6aaac964513c06b2e90acc0835850aa127c025680f7a f3c8be36aa81838c2f8904d2a5270f43bd60eb6cd19ac3a3615668380067e6d7 541532b424715d98cd3b17214ace69415acc7b8e8eeb643f2ee9027872d3813a 45bc7688e47fd7fa7d4e2c980173d6058b07a57b5103d974da9cd61d014dda28 c71412b7b0d3901f125cdbec02da82631e20239bda146c81302b444901093cdd 151a305a6bee8271a730b63665451158aada171f3600ed16cd4bb86277190534 b3bbf1bc2a336e70fad7e916186544b2ebb5c11ac35bbd36813aa9aa5be31097 331b724d95da50c74474b42bc2590a041803a6997139edaf58868f641756da6c 3df3169a0b7d44ae23117617a77de601d010b7c4bc09dfe84f0eaf6906d6e4c1 a29a057552fe9564b5db9e577b92f292b59bc15d1016d3ddd72126a5981b74b3 9dccf9f5a625c7b0ea6e41201caba02cde48e5a4d26a36eb63e86673ba75dd0b 0407a04060f5d94960f1c0513d60210e69d89425770e90b18066d3054b52de3b fd4ae96063697c1892a198ccbe198ed603b75131313b033712b6660ac1c54ada 979027ea266107adc2ed0507e69710223b8f25ad15872db003a33231b30b13e6 24162028e2b1d43633fbece6aa3d4eb8decc029955b348f4c62aead877ae7c6e 8c2d418ca11402504a4f0e51ce2d16afc9e07588e1d441a1d898bd4862669994 e626ae5fb0f276f18f9af3a2f5d045725eb21bc481d08ed5daeeecbd66326f15 715dd30faf82fdaaa2bb32669ea451f32e83b7c9a0274677c3275c50e2b4296f ab3dc21bf7e7d26e659359c040540c8822db4cd4e52869d07a645661a82d637a cad824978e2f6d983cce913f5de5f123fbfa140da6982ff6a710d4c7a3ed0a25 f4a407fe9f86b7038d1c068384c1a16943f92a0d5a92984cb4ebf3be2278cb57 7250374596e41a1e7e43aa364d0bf7cbb65389a710967a1f4b514a3b1a3e7e80 0989f4fa53428c92bb2eaf7faf8a64ae34dd1d697d9cc1fa64c71503bbb3fcf8 48f0352489a12e9e8e1f1bab9c8c9b6463a27424c57d392da008b269bee1bbe6 c6428d87896ae0903a79f5368dbd9050920a307ca0f28fd56cf49d1ffd60a156 fb521c2778fb5aaae96fbee3cb98b88a2db038af1689a98502eda592b715182c 01c319fc5bde59020132c5425715c8c17095385558a18e6693c2d16e36c7c430 48c31868a0e599a62dd8888f575c6948adc69494661636dcdbabff69033a550d 682649ecf263432f4d9fdee2d505c72f6e816e2b3db42f13d0a4a83bbeb7ee1b 87492f9ff5f542d4cd329dc0136a6ab15965d9940ee0037368f33b0bf9c3f3d8 c256dfe7549e9fd6179c1cbc0fb21d5fbc10b233f4fd32dca16b97fbea6bbeb7 4febd6f38decebfa3458b8d99267d29df11927d1e4fb23dd71ee18d1dcd190f3 955d6b40e2197f23b06e3ef06c06b47dd4d5fa1c40f77bcbf30f8148636bc922 d66b5d74352de79d28a04d5d783e7e9abb7c4b78a014217b6f6578dbdb160673 2e1f6b1f8fbde1cefb2b4c1908e4654fa98158f60f18d2891fff76e9d216cb54 eefa51a0ea43f05f8979ca9daa6d214cadf5b0b49b3f288ea551da386e720719 d402e084a0051ac1b135b9d23ed9ceb0c1d06d241365624bb0d9f4aeb7d3bae6 b00f9eae6d5547d420daaf543fa7b501a93a471c31d019c4fd12a6daf171bf7b ab36ede9c802251878b063dbbd22928827389b8c5de67b5c8f86fb20cff3cd2c 22f4917ec73153198454bc0dbb7dd971380f1f2115f2bfc2b87944fea8405d2a 66ef2b907ce64dfe1af76d09ea2c22f41af7f869f3bbe7e142f52bf55a1e24d1 8e5dbab6cd825893e995025e473a53adbe31207f3c48962f6557843d66ba4ef0 d14158b6e6d248fb869b1258734c3315b929610f4c8215a30412459f294317ea e0c32055c225de5a5b7174990fc817461aa56686cfd5f453b41a69f8aa901fd4 96cf61ef0d903633991a9d001ba73d65cb0bd421adea7af23964f7a15c1792f5 0f64bda0bd743ab567ff8dec97e5c5778667e0ddd106fa037ed7c45eca6ae55c e50b1c99799b7d1991eb03b887fb6c85fbf07764e553120516155bf7754cae8c 967a587bed35d8b798cdb242bfb4b97aef9956421cf894e17fbad016caf68d66 6bdfae1f95d0535eb6e5a1c58243c6446bfc860b55045ec7607dbb23081b7226 c032a86ccae5c9dbf07841910bf9644f3f9fe19c5a219d3b7f96ea6168133c19 3e44d3ebc88b131e98e8e668da93a3ae47776684304eec3c4a7c43299c0cfed3 1b3d90b1aafd7680fa72f2553f4c0fd73109c497c05a0524430184d0995adaf8 7df4b35b48dd589db98e68ab4952d05df9c478805e8858a158c5cff9b83f05ea 8f0e03507864e3cde5638b8797218dc8dd9cf3861d0ea906ecbf44d7d748704d 04f3beedde286fbca385366ffcc3d9f2704507e10f7945ca35af510d4917ca6a c6b4e7208bafa20c52a56e8396f83467caf6c90a774f7a35dcba54461a71e2ee 2990df127a5271b8093c67c866a6ad962d9eb69e5bde53aaaf71f5c1c26ba03b 296cfc02e5a240e6b65ab1479cf42274121a55db4cb1dfa3d8e7e60b9647919c a4ec463a21d92e24a22d68612e48f82dc9b78fcb4b18e6531e3343c2cf0d2571 79d8281f753c2596fcefdb16c097fd2c630b63e2ef6f3114838d203fd0277fa1 784aea655e279a5f28140214ccd35e3432dba23b879df17a8efcc9e2e56826be 4d0ed4cad87bafd20c9e9e572b2ccc41e5595b3c74f6b1a571dad53ce4efdd29 f20e0419ea24324e6f054512063bb19daa1ba31eb0145d48f7dc9d06fd806ca8 be4869425e2eb5a91c275e593460d6bf4a94bde017f00c9269f29e06b88b59b1 1e4582244a277b2001b958a843e36cd18b4f113b8ecac1b324382794242e5dd8 11f12afe7c17aa6c4099fcedca69dc2ecbb42c5d35fe45acfe5a69f6fc26bb4a fdf53712745ad96d520b5cf679d8957f9a11d2edf5f8578aee1d99292e43c9c7 a8e68cdd103a0f074f521ca8b4b01c412826bc96e5ba5c25767698870810b9ec 69fc433fdf993fc5764922cfcd87dc803c3dbd4a4b57abc655d0246f88ffc785 854811e52fcbb2ad8d3e9f0f704a7ab7fcdd2e493b0341027a4b1f410fd848a1 44fca3cfe0530e23618ad369074413b68ae6e19267f23138dd27488d892229f3 306ac8ecf6b6d50eb3181cdc236174dd302674b2f4169a3e5e5deb1d452c0607 13286a208b24166ef3bc5272050844a5ffdf933ad9e5e818e8306e63640326f2 a3079d37986a00c6c447b93ffa67293883a7a8edfa756cd4360899dd148679b0 58e9ee775950c9288dd1cc3e85ea043f730c5d2e25faba01b0784e136f77fe33 57e5b96537acb11490cc01d645648f46fb4e18735e4505dc428b309ff51e38a4 e83794b9b6ce771015b7a004297c6ccae2c3264b840b32f8f9fcddc684686819 ed340987ecdfdcbcc02a0016d7af4f7460bf0e25d9c779a10c26eb1198126a49 d87d6b441e6c88ab581dd1c36c08062ca09c3b78290f53e75fdad7f5e51f2761 038acb5b37af1206b057c537d57ab82035f2536d454f7994f3807187641c492f e8905969833dd86b0ed923c4f2727dc474bd5006afd4017be6c67036b6abb9bb a7118fc9f8a855fbec234e81877acd19e4a9987ed353f24896a46b21655625cd 8fad3a9698860e861944c0dd4cb04a23633e496d96100a6e2bd1bcfd51fab8e4 542517bce9357016fdc98ee5cd0611ab593597387a26ae52ff445427dd17d76b 1862b22574933f477f27e3850d9821449b6c166c0df36f286185d0098f6f2efc a8fb8722a09cdabbc8dcdb7f9db858e8aecf6b16495c0a4b672865a1f5ec6959 5725381ea1d3676f7916eed271a1ea4473a093dc98ee00585842fb4e66906dc1 3103122502360e5bd1ef3326b96210633a12db323497c149089ab001bb8493a8 a8d9aa631c296517dab28f6cf13d783d5020d2833dcbcb51c1651051a377537d d186c3cecc5d211de9fb22eb67306c1a3278a64b0c7d0f9d2a277edfde5cd2c6 ff7b3b8f87999b970517bcece1c32b9d247b08b6ac15ceccf1bb897a8fff6268 404cac944be60c2a59a775982f4922474511e9c1de765c6a5a1dbe0455b4fafe c28151ce40c6e2b5a085a2a001ed6ae7b454ef567c1b7769038d8f7c9ccf52ad 5512dd5a07d0742f877327f86eab18eead419c94e3500e29a554f219d7cce13a 2f7f3971efd45598ef34cc4363fbfb317696126ad1938edc49fae30d471591e8 f8574d13aabacf2e8c198db985ee43bdffe8257c1dd1e9e7ca6a2f94bd510d27 279d48f9d6712cd248e48942839d7b9f8bb407fa6ef378a94a43f44432e3b6af 7aa38d302d31495ce7ebda66acbebcde69497794e4df33aea1fbcb6bb0cfbe2f 5fb75069bf142f23376316e9639cee3fbffee1372821e6a00a5f21074474f748 fc925c68012b8b002b03577e154dd77a0938378e5a17b70168a29bcddec6075b be2561f61b1af260c934b5b38f07fef82ae06affb52d3de6f8ecd429c0d713a0 fb90ea2e58b18a6d12832f59f5336a760beb026f1749b7427f9316bc86a41644 0cad57a55ac515da198cf58a6ffa9d729f0126b8db60fc7ed4dcea7da7697ae9 479ac5e83fe8c399cabb425a618da7eb810775844fc2033712d84079fdbd7cce 63a015b42ffdfaff1d4915aec672be15a098c3253593a03c7b66ec2b368e4b67 f709de4ec8f5749bda8ca4ffb34390ab7a40045a5d7a6827e74eb7c7398b2c3a 60cca629f695121687fb3c1fcbd0caf0b724c2efb39222c6ff17f52621733b2a 4bb930a23f1c7139c04cec68ae183e9f892c49a9a2b99431d9db9eebc8e81911 2a0e19514ddd9432b10cf80a05d60a75bb0f13db93ade8ad1213a1cf10216248 0163fa233d16e6dc203d5f386652c7bdc148c53d63a050ac8c0695eca53a4ffb 0c72b0aefe1cc361607283f56e111a4428b1f3eee993ced0c1168360bda7970e 78 52678ce0702d20c5a45ece317fa35d85ca71f7287fe33ba982c7f2ff1697f9095d186eb5898750a2b71b4f228192260dd28cdfa77824f948af2ac3babb1c490d97cda45c8bb5e47394eb4a70aa03af6a3191828f8a49cc2b2ed662c460702b0d331a78b28072e0bbfd19ee22783d96f32516ce00c3ad6d0140bbee1d4dab64086461a55ed55b2b5735d742f52c16d8c291fb77031b056a0fca44da3f766bea0bdf0bd43d59d18ad22769a111e02837c39c5eae0b295952d4bba02739e13ea00d6e72c1ad74a2ec80ed83e99d36baefb3842c059bdcc37788fa9dcd8931daa2019197d29984ff638994d8b81384e604ff3b854cdc59f716ef2ba27fd11ebac302e599caf3d9f9e976daf90fa0e7279fdac575eb1a89b0c5bf228136d558691c057257be9430377d158406c81c0416313de76944f5db158d17d3a71045f78f8703a8ee8b13e2b8a9e08b24aa03252523265ae9e2014a3136e322715f034af1ed0983b517a936ffe6d41483db4738e576dda86323cc8b3897683f5bdd5a7775500df4c9c4601a09c88539a1b67e78e434c73880284ca86b06286bdeb014c9d2510a3841625bd6b50bfe4f8ecf5db8bc9dabd641ef1d7c0e1587eb7b7a19404f4903eacb7b8ad5380e9933dd013dc0d085fb077858259fe4dd22f66aab82823cad080e811ad706a78f6c8445326d2dcd3912343e3b4b5120b0c5b747323761614a01ca19ff42e9fdfd777eff4b6e227dc0818bfab44a6f3dcb10563c7b94074a6a0b9304613436d3683a2084a702b869795ce3ffcaa8afc15267b0e981531f1bbf02716870f852cb9d78eb0fe8f8cf6c487138fa6e29aead0564d889ec546dbc260bfcd3ca4dace75d3f842f05888c8604049c375236114bf373aa8456c4a2bfde0c453ff593dfbcd45318d800f658d7c94a1c2a63e94b6a5b739762a3f0985b350f6e8a2c8520d5bb62f7e8b2d40e894162d79f72822a9cd6dda2a1a4828d12760852039be555ea51027b59cd2fc4a4ce0891a2bfe06f30be89fc7b984620b4ee08706d8b79bd7f85663e842980e6cc5313d5ff9b1db76b90771426ce9d244f0e0547aee34aa6881ff594d97114c2fc1c90ad93dc945c202e013eec21ec79b0a00b583155550bca79f90cb3d651843228257b6cde96f1800fd9f1c50be4ace4720fbdbcdf139f71d88eda2ef00052f059144489f523c30e7371595a98308a96a10f325a3309f06eee0a6f814429c680ce4c10398e1cf4cf2979c0727b233c82f20768ef1f1824dafc98758de8001fc077ba58e24f142070a01cd7694ace0aca830db62dbfd48ede97390df604671d4d043dff6ad899bbe159726c656128ef168400181967462722471595003903a7762ad5ff602e266662f7acdd107b72f4c4bd02a54e3383d2169b0215ccec97939ec2d75ab7848a9b914f8f702605066d1d6a0a0377dc8969b20ef4b33603536b8569a83ec3f7a712c143ca6ba486221aa8610bb051e81588312074f5d950e3f2c7e4e4c1f7055aa4cdbc8bcd4607791d40030c1dcd881d31f0bf01225fa288072aa702cd05942239441cc47077b1516ff4670866a04581d1f5a5744e001c81986b5ecfd5af3e58b0680f27beb6ccd822b79207da02cfa57cc0ca6f6392a4ef40fe97e1522f27e18bdac3c1eb4770947019940de76c1e74975c69f82ed6c9c359814b4961b51795804722ccbad3be0444e7dd0271367b75a2688c9a5742ebbaaed0e088df1f73f3ed9a4795486690a2b77b720908d03bb89f1a3576aa4a8bc7f903b22cfc1c5429bf9a9709251222acb4308b0f54a3d9426f9afbca76412d45bc6361c060f3d07467e1fad2bb211d352272d10483dca3720dedae9e3b12794fb0385b3aafcb1734890ad1aa05065ea8a843bc04710860043ccac5f3f681e8251cfc44a0ed62a0d79e895b308c604c6bdca2ee09967b3c5065f386dce418321d4c4ea65a8ec4d09e0ec247619b31592452ea4303eef30acce6b55ebb3f5fa93cb89e1b6504010d6985999abb4d5dcabc8fe7b50fb2a814436cb2aa3ce8846fd11f87c64921476a0952220f909aedd2c1e423300a7f47f043b5d1d7abb2e3e1b8e5c5efd2cee16096e5d3ed572d10d1b012e48b0e10274cddbfb4bfa8409eff3f9f1c675c655a9c97a63088a60eabb1da009f1a01126cb87b9ec081eb1b18ce6430ffacb6f7e07ea63922ed63ceecd399d5531a05048a731b8745d69d14caffc5d6e98fec5967fda5acef3b6692cc8778339ed50c10f8bb6712ad411d8375e4b2e82f2d559aef97836ae7c9d5a583ba01ba58890501098359700bd0d9509ebbe17dda326c6e0d9fd4b797dec3fa5535f811d63d0314ddb87724b3a03ac7baf99dd1efc06f50ae4556c4c5f588f1183204cecfa30e87310706324f4678a803ca6399ee2ca10a9c3bc83b914570d150d5062db7fa01ec7e2597d73c4b31b347a52818c03605354869cd1629972d17a4835d87cd4e02e501a1cb253b35bff1c78eec0d7f20bf77cdcc59a9833a7f86a0650749872a059d6554b43af5fbb4d2998d9ba2934412a37d9a894d743b6b853ad109e995de096c44d79a023c2f8fa4d6170f7e1a7a9a9e4afb38d3756a47c0f1fccfcc7ed00af51cb6849ff882ee433bbb8437bdf9559e4c261698061079d278d7c1f3adae0fcf20c83eb5d28f22927bb1e2561126cf744fbd5d71581037447a1205586ada053e5b2472aba637e6bebd1c5292961aa4744a4028dc9fc07c273a0b8ea409de084dd33b2be1c475a94b2cb5acc7780109cdc7fe36fb663613f02ebcd53b55ab078c15f935c6674c934d917d672c375d7b26399b6fd91a40577a5183da5a1e390750eabaa69c3df7ccb1190c497643ebaa67b1f4ddcd0d935e0d4565d1bd5031097c786233ed314573c743a517013e78d7ca6358059da8a4842866b935302c1d0f61267703eddcbb7a10796cc1024ad23f4c18d58360ead88cdc3cb266f9987a08ea802b36d7db9b539b4d10f4a1e181210fc621a4afce15fcd13c60190dc63f089c06c762cafe81efeace01d60b1e9b9cc2da0eeee38deefe1cb6b6247972560298a7400bc7fc44ef76e0098bc7db0664db554394b863f71fdf9248417a4f760fdbdbd8a75001f2784d0358548bdc2e489f4c494b7c490a8b2464d3be88c9d5035e99d8c13cd4a87b4524183589c79e1a1dd29277bf7ab10115cb67bc89ca89053f710de15612ad2405fd3ee8c59d4a9c1a85a0d3607219315581a2875bc9bb00977c5b5cb9cd0ab2b9361228854d7b65e352c3425e91157601b4f0529dd6f6059f7fafe2f25bc6bb89fc55dc2ed841ac53dfa2fd562714181e87ff46054c120c0f8a96e3794d076a46f3c95aacbc6f8f0a7782959d66d54ab958f4b556231e0d2de35753f07ab896daf40e1d9ce957618b3af62665b0a72a9d85be68e6f640029487cf256bc3f7d6629534e9c2eac72802650bdb24d3b81d1d78bfe96ae9cf026020012bac1c4cc943d22b130a13455eacbc37fa3b8e59a995fb97aa89146b0c23f19ff58a4d84abca01fc8802000b630237b7d93a6eed862a43c1d30d4e110f48ec74b1f9ea4bb013becc0bfcbf1db35bb3e31be5eab7832407ddb281083f078d8b2d07e6b2a49197f38a4f46b9d0d7035b36dbe1a913f0eb01d24fa74703000932771cb325058d8b9e22e44262986ade9f9417d41514d6cc892f3bcf8c2605e86b8dc8f2b9e7c2797d8e6fcc622129660d205e26bfa71923816db271b1bc0aced03183098d3250bdb63edc9530b46c43a03e61536a687bd1a5f226b9e74306c2b14782dc0eb4f5e3dfe5e4dbbf3c4dd2cbce186dcf1d88bc3de108a93c9902921cc5d82a61e7683c615db27ffd7d532172b5f97d98fef739ae22df5d70a3054a58771ed3cd7010f4767ef2f1684a459b7cfd876da612adda179e36fae26c0110e38aa8566e21fc35e7e6ddad0b30bdfec3001a4a9236fffa9a9a720438ce01880d696a1506710bc69566d54f374028348182328ce24fb65ae7f2b113b4f005e8a1ac323d8f391b17af9976c06cd20c9b1bbe47868f3d895b3cd43a78e25906c18d3c65c4b764004cd0d9c396083c793398535be96346543833ff3f69b30504848bcb7ddd0591d713c90e7b105fd71796f0b4de360fa346ade98a466457ee0e24a531b71b891d0fa8ea5e732127f3616b5432271664b334d2b71ce31264800a097bc794ea15f2f14994277d2989d49244c626f5843fb667bf8bfd4f7f8657085775753cb6a97727c4f1cf7321f240e0bcf0d9b404f9aeecf524d4225b34b1075481ffbd87ec20150963d7587de54094e3cd4a6c380fe76f7369ebc9da61e302a6dce52dd5839f150ca5e9749d8c2c60de249d525ec3ea9921847f8b5fa07600fa947c0408bbfcda5db4ab9f32298c31284d0ed43d161ea9a2f4198835b63609fa308d080712487207c5177026c1b04177a6f79d7da0ffc2a81863bbd669730fdb8f808db871df9e7cc34275e692528f60b01ff3c5164ab777ed6271691cd4063388b1a9e3628091250961c91d3067ce25a16a832f193e1318bacfcc6734c20c535494b00c4d205b6a0d0b11e643bde1b376e7c0218c49207649588fbe64870d8131b5c6ff7df8420074d319797977d47da9867cd4f06ddaffb06613df15c80912cbf65646e69abeaee2c5da3c7988d10a9624dbf21290bf810b26fa886a4b0ca451b12db182e2cf259e95937107c10bac6b631b4da80d62cfaef01076cf3702907eb478cfbe336eb433385ae9342f264a142be6cc16f7dfb32b5f8a5fdc29026e964b58dd1496f52244d657ea2b5791fbeafc94be98d4eddbba5420ee27f70b3e00d0a83a6ee0a9d8e6c1f3277b60d90efe76af0f86b10e222e01ba2e3c4502e1452c4c9bf630730c3f328a77c047c1923aa4529117079e99df869f3130660f39f633527c16af9b521068e4df354a6eaefbf5756f196df4f7b93f90d046ad03937be0e6d35bfe4b3fe9d6a625b257cfc83e4fa875771b39208b3cfd68b3c6095ff30918d149a3bf2bd4417c3dfb06b22e536b817e227345605116b153e0a3087c7cb67a94405025aef068322df11132ab228c46df0c34bc98942cf84bb02c01e5605072b4080d39e60c46f7493144a490511dd01ba081d39eea1dce58a46203a3094532097a3161f3b9106645e67f4debf15960202230c7cb78d6c5938cbb05fda3ba70ec96d4059ac84f61e7d152c1ad8ab60c5282441b699c9c2f727123084f12d83ff39c66b90eb41a404ba162886b78117bc834d4590632266b20adb303eb90b8cd7f0ab8c9e10eaed8b1f87abcdf0049415b3940471f2d12015d43ff00398f419a235cbb36523c113d211ca539e5aee6d623ac2b1b13731ee0c2be4001e065dc32623e96eebb01e32abb8695e5b7f0331498f6a0f725baf15587234e005a9f0a75fc1c418c26f0dbfc94fd80534b9575f1e29aa1eab32f85d86b28cd0e1cd72900234eee3a1d7999e80cf3e2ccac676d9a59ad71517f440ce90c0300008f37a6a1d366322d573d227c295ecfb2b2a902c7a04653a75a58064e70707e01c5fe94837e8c7a18aeb9ae9f2a957a528933f39e1c2d1c544fc4a5401df0a906345a575ce7aadedcd460d107149a86001af92d50e2fb911e2bc1c72e844c7502d29f3ff67e8aebd7b4f15015eab2647e976a4ab7ca44114fa7a20d998241c40065197569cbea57ff4035dd3962bc0441b2c04f620fbb2492b56eb8d1187dfa03621dc1ee23de16523682eb167ba66486c86637bb7de0f3dca69a205eeb48130fbaa592f446ea1cce0293ff92c2ab010c78bfa216e0f623e75549594974943904fb4781203c5db59535341f84f503a321022650fe86fa336007d86b3364206205227f08dd869e69cea8a82c55730fc389a7fdbb4616616702a9a7744f6c40f101fcf01fff8749d09327d01d9f43555c615a46425dbd8a9a8d84fbb5a6e1adc20b77a98d02443bc95fd1dd0b9fc6a993698db53dcaaeb2b36fde8b907a971ea40e7be2bb93a0f6b9db81b8749bd8ed63e3e913b51d57540e731e600aa41cb27b08324451405e27082707af1d9a94c2dd35a44cca16c7a03d2b0a74476aa675e20063c92b8e3f187af70e52f6dbb5be071a13b82ed7920167c6bbef74605869a70d3e3a681f05e065429f760e04e4edd0e2093dc402266d77280a4f0ac462c50d0e1198f316c354c0280737072c86e01dd92db04515a4730f2b82c3ad102a96ed0970d1e025ab1bbe80e622a10c8632d5c1a1cc48c05f65910de936b3588b83f30832cfd959a4468b9fb173c1da7ec13d418990c9e428b43bafa08361c86cf3e90a24ffc09aeedb2f37440f7f47b1f710631574d12038547b9777d4bbfe960a6006fec38de2c7a6a648c96dfe0a69535d2a645a584c93011ffa134efa5b07734f0bc55c960aa8677ac168a7e270c19d50cd395e42a3998c5df8a6af738b58f991005d1226172772099436eaeeaa155f823d9f0b7485a4e77db908fd19842bc4ef02ac356b916811843a761cfc9debb9d73eaaeffd5ee37818c6a0ed9deaf383cd0f6a07c301d3b2dbbb85aa97489f47f4eca5976fe6957528b61e9ca3abd56601029f94427e1c2eb3fdb42a76244a53ad4c0be74fcc3c839b1e499d2349fd4255069b446e437a649a8dcb1804fa0b31fb5cfee9c5a86c95ce5349aec6cd8202fb02212be0b7af075cf677260f60ab9c45b441ce4cdf711b7bcc6ae83dcc9462f10b902dfd7b028a209b6f86327e1c5e43aebede9ea38b4833356ba01170ed4ca202863b1118d8c7d11aa68cf3d9c11b7d0ef47f0afaea54fc2c3917f6f39fa52c0cf83831c2627252336c4c18f5f2b0b3f84d59f8098c88ead35561fb3b6a4e09032504f8f5437dacb1e5428e7538fa7d1c5263435801f84febebc77898c0d03100769817f683565704cf34012cfcfe55746ff0fbc93845cb38b8f8b51a75207c0f916449029afd11627d09116472548de7714b9577fc6f2f6641a0c3f67a34c902e5a6281622463b17519112b73721de2d52492e364f5e4b2ddbb2f4fce39c1e0cad4a35e57b12d10ba983cb6a055c38467cd9bf444b89350612eaf0545f3abb052b2dd095581c9a80154e239152c9033ca4f672dbce77d4b139ba5354e411c20122a1741565412f25cf246e25751c30fa3fa61dffe2882bea70e0ede5c1dede01e0962d9914cfbf5d6261ef5cbcb290a2fa490e1015083d90d2a2b1cf2be06500d080d540e02db9bf0f18fa9bc5dae3f5d101929942cab1790fb7ba8ee0b013071a3285c21b3446088f78b06fba3360ed82afdfd89ac5cc727f513a0a553b7b00af9c66f324e04a177290e2de1df7dd552cadfce3eb1ed4e36be5b601108ebd0bb1e43cbbb95a3ac721bbc00eda922bebe28f75d7f4c558784a220bee9b9f79097cd460bd658449da483c785f5176a5e69e880183ce9fee3fa58b466f967ada0315720971edfdd842801a82349015e329b580db963f74d3ddbfd817cd7437450fbb839dbf79d0f88a512b06d897d23b5585227f337a1e42b63017d780f02daa07e6a2c325aaf461014cc10c161815fc8549edbd8a06a9007901beb08c0db44a06c7f3752d60599c972503b17b105f6b4b86c82d31b9ad3a0fd10f8564d625a90079842b4fb8500098db0014007699cfa6fc8458a2eb9b13375bfa68911db9810eeebd8ab0ace35eea753429e900a8f8b7cdff42eb6432004ddb960e5cf01fb70b405271d7c403b793f6c49284316fbaeb37254091a37fd2572e0afaef786a5c0e5205e74f1ebbf5ff1e5aaff99831f2c81b18df5d0e114a284b4f5f17f1e6930e4cf048650bcf242fa42e7a3f5bf82b28d9c656510fef8aa9a05bfd81f0a79f0a39a8ccb72f01b81fd6ae3bf50a8eca45cd9628d569c02eaaf8151944c3851202f796db09bdabcf2024b1e98885fe1ccece1cbead453bff621dc7def4f22d1f06e715e3670da51919673af25774b1978e04d332aaaf98011ce15a187d39542000d8a656c98be845f2a70ec91e6e50890adbbca21a96a3743de48c288bcfbbb905b03df9bcf48c2f988f0988f139c28ad4fb595f79a79aad4a396269e260eb06077af01515485e4edcff7626873d3902277420e8676039c716ae37c6598f577309ed1add1f333d812aea1509603b1418c16cda7f49187a5fcbeb797a83d42ffd01df3254baf3f08299177458c361bed330dd784d3561b171b95a43a440abb98c019f87de223f26c4f3b95001b7a7db54ab8fdcdc30289f34c813a309941d875b0d4f4d8c4077d426ac259fd394e0d3989d132269d46d9fea269c96df066320b00be243fd507b7e3904d5a50a92717f373898439d10339547e6b89df26151a14d05d07a013f8150cb89181413df7fe00a6e27717d71b18d4cf4cb748186f9f78307e30f5e4cd36f3bae213e7c2ff522e3251386b505953e1db23a8e8636a6a51e053038bfe1980993a2411cbdaababa57982cfdc036e4de85f9fae77d0694305d076c9ed207853b4178d03d9139787c8c773c20cb8a8708f3b9fe22b81e2906e30c0e3fa1e0dbb941bc73caa9a0ff7b541e3358bf83c84c2442bbc4d2901554b607d5ec03e265713a3a93b8b7dd68c833f4cc31f3937df6640b7cd449ea6dde0f090cba7db641271d6fa61cfdd2c6e47126657487957502e3ac47bcb308822adc09bbba080625f8f270f6c19b0bb7b8a985af08d8eb8a03a5e4385052293008d7062bac29e8dd27069dbd2e6c38515180abb4967bd2ea611c7ece7141130a93750b50644d5065cb192bc372dd85a9004e98e045d2a0720b4fcf60ad58da5c7ce0096f42f8a3ff66d32c58727b6d4a85939e7e8be232fb679e643f3679b2fb1fd40bd19b07cc386ee67979d4c9dfc6b6404f11c8ec7ca49a111710a470bf6170c50a467efe4ae123fef126ed7eff007b604aaff09a22e15daad73eb33cf29bfb2c0569cd6f668b3dafcb0562a5c84cc7beb68c81abb9ceb2135fa75d717fd2ac450422439cfce2079600cf782b59e8b79bff31dfb5537be706c584ad4c4584884e0f7b546b7ff00f94c5db4762be96ae2eb8707287a3b874aea664ce0bf89cf94604af2f84256f266e490b54d9cd08ffbcbdd252a6951abab3947384c636ac6ab0064d5a9d348e5b4598e804f3f8e877cd85a0be978c991c70afa36057cbef696e05471d1b5d47dc62bd191b1d6aa8bf8b08725ff1dffc1957fdbaa1fea787da2a0c802511f9cc68e2fc833c87b43ae77b83670e09cdfa6dce6c7e8e881d0b2219057c7a1abba8ba5f0210bbbee1b8e7b6d988e34f958ea3737d6c77bcb5db250b00281fdcec8e8056897ae845d67826ea92677bbad3a0cc45efd8826f61902e990114c3635adf2f334ccc4dd3d6e81ecc6ab36ff7fc2808a41fb869516d055ab009f7e9e9dde9983a80408f9be9870403305da4da6a3e0f06a27c715dec99f5a30b76c3c3ec79c1858c859c6d3801a3af461b11cdf7f0842ad48812fe1294d96604faef69b3fa188c3fc5d127704474bde48978c8dc3b5f7b038868fa148aeb2d08ad37fec0a9248f06e54930fc60010d9616b546a6001551c50e82a866e223fa0871d8332224cf9230968c0c5cf4e7be97d86fdc99e037afb8b9002ff16aeac008367902130fa42e1bc0f548c203ac904b4e2dac62dc7bb60c1c1107ac395327097275e2b07ccafeb1d652146ea22ef9be3b1bddd9831daabb3ebadea9268b9404cce58fd26a965e16329559795b348458767e591849f79e2a62ac688635f2f604044a6431d8cd3d3a0ca8db6bd44092135c7bfdcc2d0eaf996213ab3e0983cf068343034a90fb05a182dad9949909d1c2322a1cfab980d37f459df1fa829b8e0a63ab2cb8734a87355c72d9d3a9c300ae7cf99c3847cbaf9ae322764f7c4bdf00bf708c0aeeb4685003976b7226bc3d55c532327b75bd3c5afb0f0e1ce1aa7603c94695d174d3411172ac68615b770bc6a1703f52b75a253f5f727af9bbb2ab073b32a31faaa025b8c24af2b3a8f08bffcf491063cab2d8b6f3de32a0e45a050c547e43415686a65e71bf718bc1b29c6b2eaea654f2648cf152d2ebff02683e0fe3c4eb59fca64ca4531103823d8762d1bbe49a4b63099fa43554fa19d1101e00c7010322d026d659867d1e9c9a2e9ea9c56292184e3279d3f98f3dca676e1a0ad7715c17a56858f7ba905f21f76ceac41c3d872a77ae4dd37d71bfabc3dfbc0a58bbcd75954e3582238f33df8efd4f129ff4562191e29602013cec52f52fc80a6de90f14ffa1d2d6f19d83de850e4b67f4aec10ce3f24abda91331025ffb0c00 +generate_ring_signature 12ca538466fb97e08191a3feb3b9e69a80a0a59d51799d5cb721d9fa0ff737bb c652cd1ced99d5353422143ee0f055c15c89f27a8ee4ab11f5c44e156fa67986 15 29a3aa8e1450b67e8bc7cba1ff2ca56d1f73386c1ca6c1d97a0eccfe296558ab c6d76b97d2d6d95aa7dfde95d075e2e80169408344e258f95c42f7a582b27a71 8feaf8b4182903d0c4f35574f3c8cfb4e6220ce0b915bf069a7e7c7e52faa4d9 48eb3443fa8112b49b2a9952cbddae2cd7e9c1f236f227bee6df4f6056535c95 02012dcb7bf50b08367926513df7260a8a2bd506c775b46d56adfa42aa55f449 adf5003fb719d34fbff8c26031458c138b2aabc7ca067a577779bf7dce2cb486 5c80919651ab795e4a873bd3b05866d3dafcb2bd52e446efab8effb7d5e8f02e 631036d46633ab568103f8332e85976f8f7324514fe6080b9456198ede8f8e7a 19a6b06d2ed240b997038adf49b27998a58e10d10853d325f1b3a81329747f3f 82366dee67b22324e673872de06bd88aae884de0c679bcd7a47475d5c233a1df bbc179778becea225383270956dbbd0beae640977f2f0d05918cce84fe548c31 ed8821eda94a4ee8f976b07454ef05a19c3ba299e3cb4ab34d42910df3a17e76 1c8d343e868cab20a198f14578e58165bc2517efd13e84baf4a4652542da88ca ebc1147459feb8ac209f75bbd33c3981518ba505ee392f9f35084b65fb890c8b 245f77cb42f865f672efe7d2696d8c13f10db4dfb639a4d9127e3a6c7ba9c479 882283c1744ba3091513bb9d5c01618696727bcb7d0a08f5be8923591d2a4e07 0 bc413ca142bdebf5d79a6873f1b29e319e47d5612d2c65447fac4025fba9d50916668d9da8aa69963dd190a5beec903e406f657317aede1fb0b3efe79695d30127a988f4bd64cbef9c1337b2dfd2b3a42264511f277f20a08d1bada1a3eccb0d718a57a85dfea5f5b03250f24a349399f8d1280156750b23af392873261e490aa489087d729bc8c24d05f15a642afdc94ceff6512064eaac036324b84c6b4a0a8373d109609eb7acbf41dea7fabb47f93d5b869105aecd030341a9e28695ac054c1fc4c72b962fd3b707d2d0d9274caf6bf5db3cd4310f7baf25d6f021f8830c56e3352203a3c2570367dc7535dd06066d6f2152385f178a0c92466747b15b0c4e84e8b4fbd7fd7269fb2abe45f57cdc6de38c303a3f6b9179ea47c53c61e400c327f0839d1fe5762d534553616a680ece81de316e062bb54b2d5fe969488508a53c02249a9dc5b4c6f56ca338d301e083bb84e31365f866b7f9e8035473a6023576eddf3b2c2ff123a4d9d1d893cab04ff7ff16351822840f7268474aadc30acf07763470819557d25ca433dec51d11bcdbf35a7e1ea5f708e2f98711326d08f84e93a10e8eb593de7131371f75a497e682a72fa4e8f71999f38fc0cda83908c419705f275ea3d83ad1dd6fc828fbcce4bacf40741d0f43c929da37a6a4640e3194e2a3ad532239c4522b7109bb2ca7842866895ee44782a9cda54a4d86aa07a4feeaed3708bbe413932c0a96ba2e5fd796dbfe1c21d5136951148936069809bbd4ad79e02d883a6305f7678f0dbf4e16d168c1ab6612fdf48348d8133a5201583f0b3387729e0994cf6d57145f1baccad0d092a03d3684f350b0aac2424b02818f8d677d2eb2f34ea739745fa372d46464e3aad2e9ce70e5a6480ba93b330ba966d5ce99376a59fd6499559679b6ae1e119993bfdb43dfdfaa1cfdde30930e33eefeebba82434e92ea351aa30f880aa4754193751cb71d65338b4afeeefa05baa6c4db37d442abb5d334a8c417aeed35959c0ef427ee204c8c527e568fee05af52aacf3d284be931734efd17e54394e10c343220685813676c4412250daf0501463f0466ae704c4a56024683b105ec3bd4905a1e0ac597813b31d992731906d48cbc66128b367f7862b7d77c5f6e1e652053c2daa96b6821e2511334ec6a05a670778c4a7b7387e953a82d18d3e93eb0b9a52948692c342337467419a2cc03a7a96fc1fb641c5f968951ee925e59750412aceb92cda3e4751e87c090a87e036106fce0f5962a8bc8776f97b9b2ba1164483ec1ef043a3c16987ca54b34ba0a486e0fd5024ef75a0b2047ecc5117ab5628a6981841fe2567afe6ab90a5c2308 +generate_ring_signature d63b7922029543d829c7911fede014df91535a8cee15c59dd5d0107eaaad5107 437f39a5cffcdfe8d2b632d5000cc272d77c0d7a3658656201bba4bd9b5ac908 10 396e7c3de8ea952bb399b4d1be54b474591ad0d58a89c6a696abdfa17005717e f4c02245bcc90c34f4f29bc5db01a4dfb62b753a5ce1109a566604b9eaeca130 64b13ac2fa24b4bbdb859236de3e15f15f67e61b3f05022c387ba7029432f417 88b163b5e050e9fd459d57c26492e05f278b35345dba991c795fd25bc543bd66 8b26d9f243c743f29d5be15d996821bd16ca86b256fed470da20457fac099615 200cdbfbaa322b28e55a083dea6f1542d5e30a924af40992e6ce74a81af6a2f3 bfc55b79951fff313b97bc24e0a2251911d1355bc2b771b8fd49e75c101b167d b36d77647f9aad5d07581844bad8c92f8a25e9d1ef745a92b7120a66648d14c3 7ff5d273ec42ce1f0ab160d9b6ed5f7bc4b5f3cc8c2b94cb825e5bc910dd4841 39fc43886cc2a89d8cc0a62e267b7d4f735b1348ce0f1d51caf71cb653b22a33 2900169000c30eebb4e21e4aa649528324b98a42269084faead4922daf318d00 3 15a427040150c6ff1febe912595d14f07833735edcd648698fcd22d1510b3b014ce17d7a2c13940e8804c4fa6323064206c926dfd9a6d12db6ebd0fc0726b2066e5e328764465a06e85383d4466b979fc2ce829f424fce15dbc94cf679432d01963968fc9e5f726a643d661a6c3d50756e59ff37b1db5de76337c4930264f20bb984617f4493e1cca8ab35b9dd4439853f554044dbf50e0726a48b6c47ea340a25f5a74bd66a2ae04339966058f43c936093ca2b83e75b0c7ca11e99a83bd4046d661e976ac65898a413c3e228d8b2d6e9299e4225744599b3ddbdefe9ac7c0f541892e0246746fa79c809c54977b1e54551454d034ec8c93a71ad456a924d0c8121f5e85952c030c59decbac85fcbf3d5bc2e08067756f53ff10ad327f51f0399344fd397a8560f3fa0754105e2449e71a28241b3e776a454e6b213fd4d9f075ba77a321b2d3e9c11decef1e3b484898aaf3d743cb5df6432f366cc5cc3410f1b0d80976f1e8d2698f89ec2cd8d16ce41db37ccf15faaf60fb9ac89d306550cc432d27aa411a0a6634d47abedbbe43e95743682b49beb8eb00e5c935c1def0df31504daf97b63e4b5bf788713ce247707e0dcca98b31dcdbaf9196396d843012bd5e1833f636ff66a629ebf95a7bd8935549b70bff44ae71fde5351fba802045849a3709c95e42505ee6a9b43a3d237fee6fb3b0013907a6101b61fadefe50935c7f1d323bc5a0da75cd8dceceeb1acb18f9a76c50434bcaaa0a4132c81df081db1ec129f9f1b3af6cc7fb0de53e63645494adb182e99152eb61a368da90c006a0e3e9b6e9a8578d7feedf2c2c8bc22c00d3b64163b5cb3cb0e57829a35190647422c65eb129f14e0645383929c38abbb198980a93d604eeba11bbd033ea405 +generate_ring_signature dfb67d8befe664eeed8a01072fbc32e231fb68cf4a94bdeb6bdfb59816143526 6d42ef8245afc85bf097f5551cb44eac0a441ac130f677317a81efc5430b278e 28 76a2d0c82cd6db76613feab70af61e04eb43eeaa7b9f9199968f5c00615cdf57 c0efb547868c00a26120cd9fc6afbfddb6b994d26688354322df22f1646dc704 ce3d16f37eaee22dafab61c258c62690127841ca96cd0fec34d8991a99a56cb8 a1d667f6575d93b053b8081d3eee1726f37d724cea2705287cba6b26c08a5c97 730f60e31815b4a478a6ea95d34b724a9033d7abf7c7e448e47877185dae9afb 38b35392f321a694c4d251d3f82d0a4a054da7d2d2328a4a599358a099315d36 95e632a14b4edec2c13efd383c295f247f51397c483f55a42b256e6ee553d5cb 3262284c53ea70451ad36c985c2cd27b0d0819ea3bdce394ded868ae2845ff91 2e6f721ed97baffd355b93013a9e34a28a81d65d7784e0665cbd5d9c296e4773 88d33121e197a4de1fd5cf82cfbea682984f5e93810c77b1f8e03f3d7c627746 f8cc1b45dfc972380fa3b9a1dfc5856b15e3cdf7ad852d6fca33fb767eb92319 0d48722ee228c1ea395dca5b0c1bb0111c6affcb62d105c5ce0220cd03cdd48b 9d1aafec649218e9f500e95e4853fbc1c00f6c0d87c7ed93ed932bb2c63872a0 dcf538885fd1dcb376c1d12825b1ffe45fcabbc227b9aa15482b10d73b7ae300 1928e24e6a6bad04eed53d6dd41d5008981a3c83217df192b1b20eff868f35bc 5e7389b0f5eccbf20c131abbec394c5978c91b966ef4fb546b81bc0eaa3f529b d63d332d98120afcb72abf2cc37e6a71141010ca373671b5a88e07d6d9793aaa a119488feda649b115772afc27523705cc27e27b4df470753f2fc2e99ba4a9ca 8e917c20698c09246033d76db49943bea1626d0a720ea14ddd8a546753d09150 80eae1a562798920281029606a0c285f17667cdde3a15483db65f4734b1236d1 f0600df1140e50d24d92402e4656804bcc31918e078644715269e8d61a80db9b cfb3a7654b84016ef52f4be2b3901c4dc5ff39cf333beec8525974590cbfbc54 bc4a0a11497fecc2fd82bc0730117e2a5c9626a992351e7f0f56af840f9a3a5a 9954185d00697aea0025a0eae1a9643cf98980dae619efa1ad70a39000cb3398 0d39b2496c2675af8a122722f82b464c4189f867f0e5cff15feb1d29517c48bf 90415e3c088f33280e7eded5c1166a683d66ede9c6cb3126ed68570dcc28bba5 ffb532de223026a84c4139c756316dc4e4b55206acc40fe18c57ae235ad5b253 109a333bd7c77df4caa225ec1a58680cf597e4b280dac0adf8f9ea4904ac73fa d8f9f9e1ea5af2502f43bc987f457ca602dc8c7a4063f8073394d2571db30e09 23 dd7928750ab4bae88d7b8bb3a55debf054a4f0a622ed515b2842bc332df10000e6a082ccf82d2990ed48b0933b4e1516ee751cdf2898f11e4f4ce5dea7112c0b5a6f19fe3489e55bbcc5b7ec76ddbb2d105650cdfae1a6cef380a069da72ac05ed17d0fd9d9b05f363a2fbd010d596fceecced611f918547e6e9761e65ffee0cdebf7d466687ee5a773b70daf3bf76637454b970f8adfcc57481f93813e2f80585fb05f138c89bec156ae14297061e0a31dbe0212461d5d2449cdb8d9fbaad03ca50e33e68a2ab00f54ba5446a86b346993c2db9b4ec34c5eaf0cb93ff21cb0992e941cc337eb70bfa567a831f83382df3010fe7313b7b3ccf9205b7b773050b12379751e392845593b86433982edc3cbbe7fe41641e4d29a617f7c7650ae6095ab02966c5ccfb5147968dbd884bcd7babc88d02a10110247d5ec6b8df4aa70fab874097193e593f127773bcf81338fdcc0544c1d5f77572575013bd4a4bde0cd8520aabaf93081e9a36c3a95b0f54f4fb0f9784c4aa3aa2544d2e2ec11c5103972607d2a5fb7598bde3745a82fddb5b50479a33f216eb922cc56fac27fae6060c46a661e619a708da82735f5624e5a84b5902cb06db9c2a57b51bc547eee904a1d4b8d3228fd49dce8e2bae065535ed2e2159ba8bec3077fca27f26d866fa0b088dc5f15f7474ffea8cefc56fe2722d7dd46eb072ac4ba2dd0d89c04ebeef02d0ce2d33da408729145338cf0d7eab0f2114c6419fd68ff450fbd80e039e220f304839935223d71eabe7d545e8679240cb96f76f5692fa259f0fe0b206f7cc046484c7b34fe32fac5bba3e5a1831acf45d72a09ed0ac89445100b5fccddc6d043b360c3aebdeb1afa6e6a469c45f92944f80bf660b5185f7f28d95ec5c71b20d8a379371414cdab6df57f17efa366c7383b4e3a86da98ba6df38ad47d47b2e0ab9fdfd6c026ec27cf4c466cbf2abef888936dbf0d76e9bb073750be55faaf70c6189f97be8290377a7507a9db59a0d7faf2ed7b2a5e9025fabd7907c702d04058b0aae32655642605b83f4942c53549258f91cb4a28d166e93bf956f4a4aba0975f70e7ac4ef8f24c490835f440f5917d9bcfd3992b8dce1a15a148fb69a07030a8235013c645c46f18f1a3f7c13264c399fad67f8d7c2106972c6cd8fed620863fdf99cf066db42fc13586437b69a78871cee40cf6fcdce41c66b3aa96ed200c70b2b19d97d21af237445963e4ac6e0ad05a795f0f7eeea3772c53d4589fb090897ecd59e00844a369c1f96600105955b17cbef8eaac048c956bddd54b8b706d764c905062c43f4388eee90ba4c3fb1f65b93167178d7605d2b85b2e9113102eca1e683c206e3e22423410873fa99f25e3cc07f127f48f9a00ad2643b4c7404e116605ed62517c9a429016a32108ad79d1ee7a5feaf041e8d082fe566689b099cb0c8eb82e6f21a2563cdfab50bac4d6d0f1c73d644c4a7374e514a9ccb310b58c366dde0d50000c3b3bf0064cb994767ef371a36d854edd1a3d4c71551040742f62edfd0e9fde69c6a082a10cb2a5d027c944752671be45be8bea2fb1146039ce0131004b33a9656c23e6122c03109590fc7eac9f682c4b3c54dc8d7590708af1cc0ebdbd9c0235e4f2317e4d0d25b88cb802cd146cff3293b2a5e747dcf02e8c27395fc3dd2aa7a53afb3a8d2253e979c3207ab20e95911701ff143bcf5081859a207a498abe4e8a6cdbe359549363304897ba7cf50d1411e98e3745e02086102b813a36e1711d64a915757157a2db6389d9728de67012b720b63a5f10f075d6894d1d2a3937157ddb139d8bc87bd83a93421f53edcc09a2afad1539a31023d16c72d15fe3de7fe63a13869b4e1da23965902deded6425d37a1b5e2267108b51c3c5c0b0084261cbcb91bba33ee44319cdb148d25fa0d99f5ab35a5791e06321f693d2bcc83f53fcb33e520dbc6b3e77fb1a0ef13d27fe36ea246c88ad70a6a11e3cd65b792cfda3de648b98377d99b17e3fe2979639d46ad3a347532e200b62f25a227472e7ba6462681c2701d969cc354dd9336eb2ad23919e5584e540616111be1c3991846ebe9a57a77eecf5c8b704ec33f6b87028518b534e65a1b05363f64f26ecdee0ae0fc6d77f16a62f80f9fb67b8bf2ef79c812b3a4ee4e890cd1a78d1caf0ae57c008ed9ed1fcfd5510c73d9e991dce493c122b0ea9ea35506f63cd4b1a43d24d1289fb1a89e7bb04d607b75b8e9b03e9de9362a139fd276021bae8218d46dd173bcfd832af21ecd26974ccb4e9f3e34fc42e21d80138a7b0ebf9f767ac7ce2c7efaa1d99b057223dbdd403361b1c2529d4ac984a1b1a6a8084af8aeeccf801325c75c29cfef455666a65400e021b0ac588ebecaa977150003d28fdb287310fe750c35e045afdd2f76ebcb473298ccf7da7ca48343ec18ef0425a51952ca3a06ba4c621687779a37aba8ebd8758d8c645ef1414cad1df5720652f54e7bbc860d001cfc5b46e0c9c4c5dd121797697d0bcea0277427b4558801 +generate_ring_signature f666c3aa1de0303437d91f45d92407466ae391bf1f8f2b2ad3747cd5a84694f9 35ecdd78ee7803a836b5bedcaf9b4317bf85fb38589d03674b7f5d329f10e984 63 f318df7060c24c5d1a584628e2726e87acc5963a04beb7f285a00a44c09f0c12 fd7fde5a79284161fa6930a97c99bce21e484984a29daac1bc29c1ae47e7966c 98313c8c06a2e8e3408f9e92a3aaf4cf281cae2675404385dbd85baa05a2ba76 a777d23731cc5741a30e4fd784a797f84893d73d9e87386cf8eab4f65bea5508 7cfa24ce1c3687f8d14269e92af71938ce69c5f9c7bb6972b9a50dfb0a9a793e f27aafbfb468e153996f67d76533a36e53e6ae07a3e1f518dbca36caa531b4bf 228a467340d4fd5c4e1c622434c4109bacc80d2a8f87026b3f5d399c1c8a11e7 7e9de897062c7f250a09010f4c4e186a868c2b870767355d87b344f691a821b5 aa671e3375e6b0b3a412c0fa6e625575aebe4d930c61e64a283b7b1debbd77f8 a7946f1f9defcdd58a14b70611f4f52041dd1a8e9bc9806a87e8f105b1945109 6e872c22b87df620b496acf85a436c0970080e5b4f61c16321d2da03970961cf 9ed56acc37423b1a49520d483af719fa0cc21a6f6437fe56ceb62fe02476a866 05e21bc2f997248e3453c1203e6affa496af52b8ebb4130606e475ddc7db929e acca3e6ee401d4eedda5c2c8655b7d90f58cef91264192eab53bcfecd1188e66 f1d692e8253797e7338d7eae5688d1d775033a0e8516af70ee4fba2cfa363cbf dcbd9493bb018a3755ff577bd1d337f93d0c2a350cdb5ddfd758f6f0139501f1 5a8ca03b7cf7f7ac59678647cc0baeace2bb9bd7b09db0a3a76d220fce410b68 922b495d8fb1ed937a4616c52915a41b8dc8c0da611052157fd78189b1eec935 7e2070907e8fd01f8a199808e8c64c9a9a25714fb31f0e4728dbf1b086c7502e 88f09775a6df586ba503d0acc2aae674bcb17aa7354f6ca97161513697d2b6c0 aeb8841c6ccf7fb88a9e2b4f79c3da809aa0f6215a77c8d2d7a733124e8af199 658ac3d6eaf05d0a0a000a3e6ca7e6770cee15aca6222668380287e0da7a7bcd faf28212a0ed9e5e327f1e60416dcf809dce31544b235a1efd1e5512775b07db ed2ceeaceae9007c8adba9b50ba2703e4e692ce4d31cb73eb8255e26b60958b6 776e554a73f3ee6d8059d778a4ddff13a41231fdbfc8e46a066a224e2ca65407 601bdcf4bb5f7f631c647161f666688f46f77162f5c77f64da8d67f044ed3f61 fbc4dd52d705561b95c38c61e7fd63043d20ce3dffd780b81ac8d545d4679007 87121ff1a209d6d66827d67155db82697328bee3aa3903efd4db699b663690c3 332472aa00a7031ac47bb8242f92577794da33834a0a41997fa563575b6894a1 d6dd9f9622464c0fef8c9cf3fcab52e566ce99598b69e35fbfbb4362969c56c5 9ff7acf5f0da5c50d486d5c64d0afbf3f2f33f075b3a0524d3e9d5a6f2bc7133 51a770c0e28dcc1601a2bff51512a962c5023f9e6d81776114ad1e36d85d1d38 94eca7d1b38c71c6a8498736641f1366f943a59eca6b1cd8fcc640da78657d78 18dee895fbc667fa3bef16f737adc525f7c7b62fac6d1406a9db15928008b4d4 2e9c0f2c2b053f326d1c8aa8f7c6bcf9a569b6aa800e4c1514e3f98812770e1a a0a8ec6f8515787b6e1df82754c337a72ac19b8b09683dc9dceda27d19f9bfd5 ed227fbcdb88bad74dcddd8231be56f076b1490819731a87621fe60443c1bbb6 33813d8bd142defe0bc19ecffcd57f679844c6892b00561439e674c187b3b839 00c01af98425a685f62e844af7f2ce97eab1e6c15fb32f94f913f947111bf0eb 3492d55be407c9bd1cafe3e6ef252b2a30a6a7515bcdc90182bf1c0acd4f7b17 541f45cc75bc30f2c507bdff760e07ba208bd3e09dfb31fb4aa8eb2d0378de85 214691f6e84c4188649d081e772dc95b2d068607b79d022104aeba8753efd384 8e8dcf046920d97d9da4cc6f0cf11225cb5dd68043739c5a180af0060d0d2ec5 e31ccda63e4b735e011053ba7491df48bfe2630c0d43a5958755344ec82c9da3 d86682ff1f9cc53333d5135b5fc2df4a1b9e181559b88b948016f4f040a762f7 4270400cf7cbc1c3ef27cd822eb1e4862d7d51a06cf753b70a931a77838cc751 2b65ed8a53902279be41cb0bfe8e1b106a78b0d441d306cb5e0d872f450afe84 a00d32171f95fc58822c04da31e5b57c82b356b83a1fe7516e07d7d9bc64ec2a a7639bf8faf0a7c6a5dab64ea560b678dd79871765a444db3f1eecabeb99a162 7203e0cedbb125925229a95099bcf66b597ed6554c0856323f5609e7458b4ca4 cfd198a33b3474fd109a8e27dbdfe6b2eded8305a4c90603d41b0357de6bdfb0 83d1fececfa25ad862bd6145dc99934d19d7954edcc671e07f1f3321c2a75ef1 d91eb110827403c553ab26635f30c9e8e2baf1c924d021d9a61e79d474da49c7 64f3654e9f0ea9a81a2bee570319abaa0033b0a1413af8a528c8e930db826985 96f801fe278d653f945b73f8af1edb9da073613c4dcc9cd6af539d5fbc6c7715 47275d29149fce9400ae2323a48f9d289a3d2375de281b390e8fefb4bf75ddf7 d6de8e0639fc7b49ef875578d47d0db92075c2e2f2831c55f5b3b06a1c50a1dd 7382464fbcf5b102452fd2be968513aba0129216b9e859731f516ddd91193b57 7de3cebf56511748ffc17cef41dc82ba8d66f2d10ba5afb29877d313d23dc24f 3a096393970652c0235b7d347898dd26707f27421f155cf1b3b8595fd6376d9a 707ce23fb46a01f6491957329c9781716935fff3e17bd4d7fc821c68d8aa905d 1a2c8b8f0272b938bf820492a0c379ccf498a05f7e11e5fb8e859fba0529864c cbac7e2831fbf68142a7339cd041b8ac94312bfb3918eb2f1fe3b4d338a09881 85b5bec4d95c0589e8ef33c800e0aa4a339e461845dcecaa98c602248b67db09 6 6b3e9aa776a411d891cb1706ced41b4ffdc2b4aea0a636b412edd9e8e060cf071ef3972ff6e0765d45cefe7f35676774c9c827b9d05ad373264eedc0e3d72405618b07a010e4db68a11971ce53fadfc10c106b8040e2564e4135317534c3cf0388ffe550f7a4abca8db6ac70733481d03b422c63fbecffad43f9d809c1029909b7d913f6fd1714a641b4e7811aa8afd0b05d2c98a334572d173bacdd24abfb096390f6dc465172390aecd7526cb320df4e6cd5548e58f5ad4dc27f28329c710c03da6cc63594a7e46793c5cc1bd8cfc06a872612ff7300344720a3e07917950fc1fe980f6f14b36060621fa11de5e2c0735f4e2de86b1188afefc9cdc4f0e20dc50495fdd8ef40ca047341de88ef05a8fdf71a148dbdb34c407ff5e9a704f40baa09af700314ca47a1d3f27f595688f3ec98fec880d94ca7240cf81542d33106c569eaa1ab35e8a99a45b57a83565eb0d84edd2a8ee64c63e527dbd3dfeafd0b78403ca102d3212bc22e36c95c7d6d73ee37ac707149974743b576d22b22a008daf1cb9b4077e1a4e858c688af184abb92df6e27ef88c485119a2e0222049a0b20e9acf0d8687b72202de26403d2440a7d5525d20d6e4d81a8f65890937735005bfb8d1978ea9a41a016bbe7ed9a2f67b516a25e6871ca9af72d002cbf683e01016d0b7c14e86ad8626d92e2db47b59b04bdb1f611a5b51399fc85efdde19c04707a1c02677c56d4fc2a36d2e87df570e89df88822cdcd3f413b33b390fc23050d3220215813fbe1868ed0efa3240560543ab329dc9683854f4ec1729b1a6e03f3249bfa4e18c63df92dd4a4d4c80489a3e9a6818b01d27bba4d493b84f5270b46366d510886b6f5c6e7979479cad2cbdf8f662974990e9e2ff126c5ca616b0620eb7dad4b356bed7b7796583aae01c5e4e39cb7694dc004e758741d1665b409f78043cca56e9e9febfb5163474472aa3544195bf0a2570e5d635940d89c9609febd859f419920e5c42eca3ed7f7aa9db308b32d78850d5815e16eb626d91100efa5b3696c8f191cffa8108fa559d498643032e78887532791004df346d86f0b52aa29fbdd2831d63e9c3bbf55bf1a4cddb86ad3c5b029ca287c498f39fc3e065efe1420bf6b9c385f883bdb63b467cdaa1d87d5c554a26c2cf67071b0d77d07e110207d20aa8adbaaa16478f46672f81b1dc83a983aa72b78ad722b030c350e10ca5d34a7f2c03515d9f511bfea0e22841c3007290d3ca649b5fab6fe2222018f0ee0fd7e5ecdbb297298510274d3515512ae34cd9c6c65e0df2fe85e5fba0930415afcbef24ea83216b039c89f507c8bc0ecfc8caa095fbd6880401eb82006c2b1389a2728e1f1bdb4234af18698e46d7f39a0698f19d7041eabc51c7c440eafa0011ea2cded2181546fc51d9b1e43c6abb820fa0c6183c96d29874961fb09cbc6e05eaa355ead01c0851e2f5e4a166d8b3e739c7e60df316fc78e8bc2720c93eb62805d618d910abeeb1fe31419e1cd05f327c30ac0436e6297c4b2bbb20df55823e846d54230e02f1ff85ff30b59249618ca3e465ba00c132afe1cb9470072eb377bb5c017d5747b37410177fc00c885f0f971394a7167e001736e6dfc0ec0b2cd92ce138037ff9957b19c795201077b845e56223c37f876a8311164f208b6aefbebe03f1182bf0c7f42e0d0dc4a25b0540c50b797b3bc1b0361dbd842080e0beaa20df00f2e8fae98f358c1d49a5c7976e9df56da8845e6c7bed398d900e49913745274fa22e1938d5c5a963f91e7150211bb7cd9383749a13fe6dc9606fbd417f540e85094cdc7b70cb05aa99cdfdcae2883b44022d0f419847f6eb6051eeff3312a0ca0d884cfbe38222c853e151da00a1d35f72782e48366bbc3de0a3768d79d6d77851995d0944511482feb2603208228ca43b7cc2542078b573d0c5b9922288965f8bef55f933e26019b600f47b28a9a38ffc715550ede7ff168048ef8d957f4e39dbb93601221c167a97d687c2a0c977709c3d43cc0247afb5f027e8ed662aeb4193a1e3717ed34b72790e2abc050ecd8e6bfc3ebd11bbe29170e81d97341f44a76a48703d291ed9b16172782fdf8f4e897cae15bec3d41b1bd084dede7d415350851dff7c54f753aa843cb6b76e84b8e82268eef197b56b8700e8f8b4437a256ddacc43b1fa5dd22f823e8f39890ead6c0be097c827ecbdca003ec6e1542579bc90d9a5bc31007479bdc45010e48512542c0ef0f5f358240e10631b53004c698e24c0a31d7603ac59fd56a2222833efe4af6a3a6d681bec0ef0e642e5d1d188976f6fdb440126b44487dffd71f9440ca12d1fe12e80f5beff300c6f8f1cf925f38736a0717db0f994edcdd47f28397ab0da6e71bdfe1274c720b60161247863006e288b1ee644e0fd3d9f0b64297ffc9f1bdf19959d19226f2010ad4c24b287d4361756c012daf077bb50e699f62caa7a45353d69121afaf86056db7501ebd4ebbb0ff46e3e5f15f210158f0d952eb80a9d3a6090b0c132ef70662d1310ebac870157e54248c30512e20de8356ea3632c76a8fe3a9c5d10cb90f080531279ae14dbdc8c650391a5c3f10ca6ecece5ac469811f4973a2aa898a0e7862dbb23c81a9d1750ff84aed2311b8d243e17ac22bc177b45c40fedfcabf06082684147d75f1d3cd12353539b84bf3504a8760ac9cf45698219691ef406a0d1672e8cef753181d6739e64a83159c1cff85e5afe7306dcaa4505bc999ca27079bf3c6728be957abf1034c51f2e0324dd6d53376bf0fa25ad632b1252afaef09906a7171a0566f7beb2ac5cef05e6c616a2afd59023cfa530e84f218b21e000062159e17f59547b879574db951a12f1ecec5b8e6c4a3bd0caf242c50157ab50e2ebf3480d3c1f779ad45dd840b577fe3ccecd96426fa60fc5dcd4b1a36d3210052297a61877ef11c4609699aad6e8afe0e065867dfeeac903f2bda9f63cac301e325998a9632b9f04cd80e0097c11805f74a123e8a898aaac4e0bdab86572b0c6eaf34810e5444d7407cec8e9856292e3b0a9053a63bd3b30368e236a5a37d08d27fddd354cf41355de098ca35d905d4edf7781b13afa97f0cef17225bf53e007723b3a54f4604215184c2e2445614661a675e8135e4d299845cca42515a1a0118a7652eabda44c6a956f1bba5343f598e647e915cdbacbd7ade277990f59a08ccd2a7c70087637e856478cb438dcf92b298fbf2d35161ba2fc540338f8605056b1df769095b03820d1a7d320a84d7a77015413287d93f439a98d993d8fd0b08784f15fe71a3d198931aee1361712fa549f4e4901d86df650933abf0885048036489f5436c6052cf9c1290656af4b0dd3af1249d96cf920dcfd3e0aefef28c0edad50a13c416d3f441b40c0147cab42901faac4abddd6274135a968958fde30638a577bfdbb374961534a71e945cf9d1a74747ee043590d8712bdfd6a3d68704ad6a276023d24b5f5188348585c35d153c5cab554e04265ae76843f166159c0de4bfa3b1ce2d1a70ae6234d29941ee5acfc92d5ebff07a0078fd403738cb8607ae2ed5c868a9c0c5b32b6548e8d7d5709298ff4d6b994bcd1e8962c7fd136e0d5e13f7279d201e6bf6309ee48dbb2605fb68471de5cb9844486f16af7d81a509c1f7346b5f450101dce7ce5ceacc3b2886d5a7369b33d9f63fa77b9b9038540e5235bec2e733164804aadea00bf2cb17a98655b313732d97ff8430653a518f0b4511babac57055214c7b89b5c72d1ef284813d6ba67d925bcd86d493bd4e3606124aec794d51912a37dcdf91eb5906acaaa831916f888e6ec5fbcdaace7f2f0be32110c078192b91056bf83cf9371b2187d7b2c60049add10b5a7e841d48e403f4918ce95df12dc28c50f26ef040781742c2882d132da1a6ab2ca30033938e02073696c119ebc33d9b5247530a99336e94d1dca046fb0c1af3dfa530d09a6e0b676452bc973355f8db4d6e052f3b1fd763bcf2d582f6a406428cd80b7912640b88b69194d9f94d57d0e03fc0252fe20621f9f79c5d8e6a0a1d7a04597d8a2d0f629bfbcfa1813a647d30d6094053f6769d65b3d099bd1f0b053e6a4000ae1d0dcf7ddbcafe83746a13028ca7387f09d039f2bd41b03923782fa11bf22cb58b0d1cf0d914cfcdd73ee6225ed2053355abd6177c5abe1d622a62c6fca1ee202f0b68d55c831c1e613e386ebd9a831815a102da2981be850a29fc25801f0cff1501bd99fc1fc35e6934306338c65bac442dfe2ef27877bb90ca684b8edb893e0d0c482499c5536eaa91641da509562ba6a1ab4277795441b084ec2400fba8809e0936083414a238d5470c35bd7896f65af439379576b3cd8224ca976ac1852d45022951f13b91d25b096977248701883ded49ad21de4f29b9e0297860567516c505e66ab6b392b7851b0f3965253b0d70eb916ff8c31cbb9ae98ddd7116de96920e343099596852825a1145ea009e44d603ea84132759cb9cd2576963379075f40d9c02c70a0f6f5693a3facd023a52bffd5132fbf7aa3c4b0fdecebed33208760db93803f5466334a45a1577b1e27da9b25eac4d849cdeada60f4b1d08405d570ac4c1bf1c01d6e0c9ee0816c13a2b248afbd5e6b1cc2e4da4b44e9a9ce5beea0a062377aaa23ebdc9b3d6b104e93d8f02df7973f7759cfc7bf9ae4818a0c4890ae6d38d05cb89b3611a3117af7f2e0a12031f78cf4bff2dc5172a877d7d704a00d513c70e065c392ca73183549a919fd0111ef3fd567f4144648b3546e56c73080f05ce85b272c3fec124f2f912859ec93fe0ff2abba2e1ea8d40965533bc000ab46926b46156142e05c4bfa6ad2234e2708ecb430bbe33239e90748763ff0d0e192c50c76fe037ae878795ac1ac5f16bdeff6cd2bf3f73f219cd05cb87b3c202e653b0edc52b8fc1cb1267e0427a4f7b557866ffcc8282218c5f467f55b4190b1e4aed5d016119e72e73b15c49f0bbca38c38558f785f83de2103d1e3a76cd09f6e1f47f82c58f221b7ca7440e046db6fa87da4d4f4ec830aa1f59022130e80786fa3b9a6bbfa8a03bf1297105a7b93cea4a282c8e94d87ec39008cd32f945010ba4701965a19998b0276b1552e9aef87be65ab0930abfcffc50d509b19a820d1f044571500a6aaf401cdfbfaa23dfad9f48c6a8ae37d19b823eb0da28eae203b1933ba8732988dab0825559ef58f16310c6f2f62e8ddbe80e37cfa69bec19088603c0a3accc7eb6685bfeb90007ece294df95c581783c8add03afbf2b16a50bee9cff5539dd57667cb674e3c6270448c5b8b0130fa7118d2e98d3a32747df0d0e8c3d94e6e03afe6e93d24891555ffa8cdbc5dffb4e63d026a9bc48b588b90236a35fb5a8f27334c0649c1595d116862109d8460bcab53f39dc0c46eb88ee0f8648e15173cf3df1d9860d08ea6a109598d20e8d82075ffd35b7a124298dde014fbb2321be2734efeba897ea1d0115e81872a4fda570f5742bc30c76669fa304250a51b0bfffa25e15480154311a6dc2c8c477a5efde1ab51a7841101a31e00eda10e498d6a8950f2250110b16d61061b751f7cad9f675d3c8fa0ba84572c600554f7f9a0f5bb66a5da8606f50e0d05f3f85cfe95f93998359884416bc067c0d9fc634d530e2f3bdc578e08d999a1a0756ecbb74c520fb5a671d088d58a2ce0a +generate_ring_signature 990c48fcccdc64eb83d43424ef72e4e3a144c366de08b064024a503996099921 c62c9d6851e36a6a0b8542718a423dfdf8e18485555c11a0ea435b21d3d8eb4b 2 a0153d8734fd1245a0a39e36a5ff95afa76d6b37f96cdb7f95f6bdfd44c0f5c6 17d72a33462986f79962eb1429af08805d5f37986692b1261338fe70300e7c2d 59ebb25e5608ad0827edf6df67b9b51b6a899fa06c37d71d918e134a808a370b 0 35c7ce76a338e4b586104ceac314a16aa7f3ea6cf2307ff2bb92e9399442f70117af68c98e2d545c19caf63f3c854d9b7950ae135dd821d021d72a3fb222e309baf9cee4f485be1d7348b3d45aecb3c657362df8890d3bafc9373b1cbd782d041c2d0b798e69292dd1b14f88580dc508a40516e1f2ee88f3d63b9e322b356c0c +generate_ring_signature c0f765c06f3b1d1c2398aebb89b082fa4757c42ac0064d0a0fc4b198620ac735 efe3d48a3ab1705a5de3c51edcd454772c5ab479568aaf79b041d42061287bcc 52 1d44e8011338f23c4a596d35ad4dac0d4070752d27332d64a19f9795e3e81685 deaecc1c78c3b412286967bc33b9751da2b33cb217691bbd5fdbc87db54e360c 99767c9cdf6bc9925720b279076b9092772984b180cfa0984e2ed6588a2c970d 4cef66e4e89dc9ff9a96ac32b1c4d83ef670005b00e79131cc3bea1253d5c082 c7f7d7f237d58cfe9a827ca48a9bb00510c9c89c629ab3e7fa5870fe2493bc6e 07b86390c7f893e91f7724d7adaba57802ed1509ec9030323f08e6408afb4cdf 4b42b0a9061cb5665436531c6921a6714ade0666547865088b530cdb32d83f9b d9029da6465c54703a62ed1053385cf71c7f25369a405d4b30b726588341ed82 2fab9949216c55269be188d797d2bf9c1dbcb62fc7281cd20212e9d7ead9b4db fc24909e29b035f7a644cd01f87a55c7fa770590241d3318c51923a2f97b5d69 f7e5447961cad69b480012dfb8eebda069b6b178448c062b01df2580dfa68356 ea9b18c466d7c21a1a2b87f48472bb3c7e4d2cce4ff0cb1ae568db0c8731e6c3 68252a791bf83836bd12f225ad1fe0b27a70306aa653f8d328064f8502d24035 e44f94b0e1a0d62c22fb4c4da5dc1f72661f0af5db7637f1b7940d849103c430 6ab4afd3a488437fa36b59dcfa6b73af631703807c00390cc0e5b695fee9f20d ee9f350cc3f0c9991978a67470664d2bbc9b352cb4b14ef9009d9e90bd4cbb00 26803d1968a3cd107833a432156a46b70caad64b435cccd6deba79b91375890a e2bafbc4d313b70b0689e4bd7046c2f12dd51effb3d2c800f3ab40ea4da49977 c1d552a12d7f8aa9591989bff2c64645d0364924db8586d990ae186ae9f313e9 1dfb4b3b70d05c8f932d66d4ad5f5d567c8f8f6e13bb71034d998421068143f0 64b57ecce97f9ac6266a21db1b3f3c4a688d426ef63958c76f33fb09580c0480 0ad54bd21909ca58ac62c8b0e89ee369f17704c4c36588a90f1cea5fdfcf1865 22812960adb26f3579898f4c3d84ee041cdfba888fa65e0ffd3c07963027c358 ffdeb0e3a7f685363848bdf19cc38b3cb65d09300b0e8fcc0195a89329e0e532 9b0a483c5e2fa2f89e24d3e4e0d3fa7ab9ac60fa35b352c7f85494125c80045f 96b77bf2a9b4b525c3b3665634d3b273446d99c8c2faef838ff11567a888ea8e 681560145cd3544e61c3baef2e6ec89ce475145b1fc207a2d028fba2bc3ff349 cb1a0be8c34ebad7e5acdf0e0bd22cce36830e9b2822a178c1890833d4898527 72cd37be5b7ebd02ed09f78a7968c74f519ec07af117b5937a93445fd1292b3f 8e328a82ccc9cd0ef54d072bdb1e147c44696d3ac42708497cd14bd5b2053c42 3d25cc40ae7e727bd7201d6fc7a1d66b80f66b2a7e7461ff48c2f7f494ce75cf 6a8fd07994315c7d3dfe6a9bd2ba897d20958035d03c7c89fbb87a37d6c78405 e0c6af94e7a866031aed0924ee5d673bb02f4169f8e48db0e39ff5f1120bb454 f5169f0b24760db323d63e6ac0a613211c47deebc65082d115db27ba47369e15 28ef00fbec9b115db44aacc4df48cb39506847e47fd1de8aecd36644ea9a0a37 92d84330dab9032c3b7c0b6f25bcbdfddef802981b87e2d2602e796ddfbf38c6 8df287be3d759211fd03090e921c92ffc6e8f940408ff9225ab5e6066abcb8e6 58aad878e2b7f180bab472faed5d81a2ad3b5abad7bc4254a17e227e6cf61dd8 865f4404bf2164f78aa777c375406fa5771e7014fd9c7f1195047cd7afafbfbc 4e90895f07b25bed88b916d5af465a626b766f32fb84e06097191bb0010ea5f3 4b6d771df10337b1a82c8a6d21d61e1f11e8c4384310f41a6d170302ed169376 f5d78ab632184f8c84a629e4931c6b0932c982fa5204e98907abe2f36060e857 d7ae49100ac3dec0dc5e334c7ded8a7fc203e7b4b7e03842582d3f87bc6d2e97 5091c5b1749dc3afbc89608d71b27b1e7ea0e4e17ed4887cad213264a602e70f 055926013af6932a309ede74c0b6637781bd4d617a1ce397c5c6d985f56ae0a3 a55292139e636e343e397a7f6cc7cdd493461ed52f39dad6f3250a8463472c95 1940a159b4ae7f96bc8462c023a48ee9dc0be6a06e92a73b07a181249113a94f 2b65a499785c4fb523c3cec521ca1bcf1a83804197a975cfc36811a50b4d9f03 e2489de30323151701248077bee126813ef1a6c7fa86d587b8acd681d4027336 d4b75af0c0131390c4f9311df5ee2a0e176ae4134405dfbcb62a89abebec7fab ba595a99fff3e197e31f6a8baa9f51140113107f1e5dc419e94941084f243023 0a4e8d02dbbc70bd66b5005d58ab18e3bc8be3424aa3a86063a445383729f643 66d7396e1d8c91b45b3b45d3f2e9f94405381b392f5fda7bfe9c16b0593a310f 25 170fd0a6bc9d84a9565349a9ce09a063ccf4710ffbc3d1e1c5a93a4369c1cf06dc40213c7ef54c2018e7ff0acb0583833890aa7b5c8a856723f6592ddee85b0d85e8621319a7f125bc240a7b6d71438db60c7e65ed644ab89ea459de23606301c376bf2c63be7385d7f75d5006e625619e8906a3129e161b0bd0b4f0d5ff4f07e971f4ab955f6a09b0daf4e8822fc911430ecdac716144039da48f019a186e00e6d9c15a6f415cacaee64fe84e1e7d79b5ccf57b4d32d60a75cde039601e1d0f39e7ec9927468464fdd35cdf11829de01d03f50b6c729a502b819f46d6adae0d5824528ce4e152b3efc8cd8a6f40b3b7a3352c043b422c658c9c73d1af0a560908ffdab8bfce11a411c09eb2957d57df4cfac5dd5ee5fd70c42889e331defa0a88222fd0597a2f0ab75fed9718deac3848c0e7b34de0fd350135f0bb43979b0375915fd90db19194ebc6417af80b739817e23caf21abd0b59a0b2e7b13034103fbd8aa85fbecd82d1fdf7987e3521238dfc53eb1980d89359a5aa866a9e4700a4d5e8393b109fd923b7403666a46c0bb1c3ba808a79064c446110e36f1555b0aba79f84d79b6a3fa75345f4759cb62eec95dfde80e2a9ed55e932e21280300099e27a0a253f1df9f706c59ce95daf205da4d86e248821d23b6b892e26a283e08238872430a5778c7fe39e8497d4e7bec7bc0f65098eeeda3f99528819c900608326f4d8f57cd4f39325d56cb076262a9a055eee6e073b1da8a4b57803917a90d80a9fde514b43be359595d018237b77e4d37c741794d59aac1049f03dca37805aad5f891ba90a16f08d23489a0513620b464c3cbaf594c321c04faf9fa06200c8d23224b2132ad049e87964b949ba61fe3a5b976efa9fc4864de4b555db087003070c0291693be3e40e1ec6bad86f574154b078eeeca5b6672df8f42e5e5950e7bff18dd4f859ddc56e6a5af45be81951d15e1d7fc75f96bf9730e62ee723c0d7c26515cf4f2f3a088587f6f81c6b1dce08686692f35dd6210c1171a3f298009135538dfa3923ff72fef47cb8993ac6c1398fd95ea754f1322ce64e102fe5b0ac2d147ed26493ed82244f5db5191d94f5ed691525815bf3a4fcc70c518a22f09bd4e2a97ec91a949060d528821afbae0ff557838a0d24c06e95e8818bdc3ea03fa7950554f760f4d03c90f4b928f4155c90e2a7e3d83b8337ab51f13c6e29400017cee1d5884f423c2dcae367ac8759877599650e31f52c77912272ebf4a2808e1f400a521e42d264e4920ae2620eadec5fdf2928ed23ba0a79ca82bbe37aa0e64e56c758bf89a0a02c7495792ffa2bfbca846e5a2046792247b715c27bab308e09a9efe14d0eff6246892158727546bddf75be3c5712c415267f8986f30c6058cb181ae97d4ef3eedda4017f6ad93720d9a52d7a3f325cefc20996adc1eae0b92edfe2cf7445bf36ae467a0a4ae8030976d4cc3f743e3d3fac76416fb8464054eb9f1265db0c1e30737320b2fa13145c073150302edeab77c3387e1e22b48066adeee949d1a3981029242e92266b375eec12972e0df2abafaebf99337cf580132d546f898929af43552fc8a060a6601444ec1daaa5c30a05dd5be6deb889f07607d2ce0732241afa61398964ea8a6bec52d19204dbfa37f6252f4e91877b8001182d00b1ce0c46a5c79fd8524a0f1dc9ba0b3aff95ee9e3f3f13502e54cfb067eb6afaf58ec0148ef72d6c7f78ce8f62ddcae5e44aa06fa3f9c87308827b50682f22b668f28104057d8700ecaa1c9b6d4f4902d2823807fe3046b593013b30f422d6fae1503261d04cfc14d656b843fbbb21684df20cf69066dd97b979c3e0af28e2fbc818c087b89d4f83bd1c4eca638228baafff20b07377b630ea7bf4901571a3836820c5a29eb9939c1794004ef55647d2132954365b8f283288ad2bd0996737841162b320784700291580546870cccd36c210017e323ef5023c015230627dae3c006633fd1fce5e60d8d0add6795eb9a8eeaf62dbe689ae1008d0a220bec480becacc7ffe00c59feb7b705526b62a84438fcf8e98e6f2162b34962e706e025cb357518ebc287db8b1b69b2c63ea45849fb406b293dacdc0759b1a57e0e8075444185e5f252e479bd57db50ba8616e92dd0b1b5fee0b7d9b0bf14bcb90d545a35ded0268d2f5c466cd00fec5f8b20e310b957d207774879a1766f7c92053c681ae84a6fe4847ffae9ff497e43426988e8e1a2ed3f40b56c47e7baf2a3023e9af1c80ead3a53ed23a13fe1c97d5d186b900000e348c2a441ee29357ea00afea40080b0240440715ff9e136a25df89a3944b99247d972e7ca0c3fcf24700671e2170e61249facdd1fb12d4f942e994d8ba12abdaffa20587ca16e3b3c7403e8a95e4ecc70c280721e29003e25db3be058c923c8964800b6a7c52734f03304a266a57394da375dbcfb5a784297ffa56a1be545e7867f59171d4ac0da95fa0845b966d7013f52474c4bdd62d41718a438ca51cdf40d71ecf4c5b62d2dd5120ee9832b400a096353ea204f83cc6beecff07b6cb8055aa54ddabab73f9dd87f018359bc5d1c4f6b6f91b66f07c2f95691865b4c00ecad2ac8d95665ac92095705013bdd781ebfa8aad1a05222e00fec8a52b75a36bf850f93d767840f7297a3090820a4bfa7a102e481b9b81029161ace9e905e9b5a181185065b4ed7f6510902ed3694af45c6becaad7e89bbca9e3ae431c0bacbc95f1aec484631be4073530f9acd723111d2568140bcd882ed56384a9e92c2ad81fe3a0b434f2d801159ce0861b6169421a9dbcbbce7057e00c6296d52c6d1ec568c475e9aa7a3c5f716000734bc687697c2420e6d2d71af58b87b0f294eec9c4679c8630b875c7d7af8c1017bb460081ba427309d39527bac59f4c2fc5a6e54b4bb9fdf7b138a811b02a6068f16dc6b30606f74b6b9322cfcf9dc86fa99d5523b88f48199ff7e9c166c800b7486d2264e4d1ca24d1ad9cdc3b7059e6a9e474e34d21eca41cd8f87727be105d5ceaf00a0034e3472784cf0c1d7949414b42c710f0c41bc0258feb9dbf491076075e5201e3c1c2bc6fedc4f391bc7105168d4f228b53b7f75faa41e24284a0b6b5baecf3bc6b959a59f0e737173335cd23d9363e7ba6954f0282c0eefbc1f025e78c0afbfd47a67e80e92b1e046f3d4dcb507dc0bf9f575b68781d61316950d6c1daf6c06821b891951ecd37c3c038b3f37130c6669dd3b7298e09496afbd0a13c3e7466ca0d2c453ba66f6aeaccc6cbad9e2d53dc4b44191d4d726c42b5b0d1bbaafac8b819439d07560c54242313c696902aa89092c7f987a16176f61dc005cd9622f6eb7d6da990965d7f75d8fe4bea327111c90a6c0b8a1b8d55d7f1f07376f8203f2746fad4855ea76067b459d8b33fd3f0a991e66f7aa3c04d9538703af7e9244b830f009ca7eccea38a2071be1cc0d0b6cb37fba820c9e796cab0308d8e4e860a379e321f7984f805dcf6cffa7320c445e60a551e66cc6689f488f01deabc193d101cc49fc2d4a08dfaa7f54a5a6bb33b8315b30136430697fe082022f658e7f27ad5acdd0a0d673297001054cd51a25969d06169f702c50273ea909b74750b454c6407624e5e2b13424ac2983857373fc7f632ff630e8b7fa1a290ce920cd62dc986a1ee61275270dbcd7e8438bc5c468ee2fa93bbbc075a1b95b0b435332c8c916ac90a6b477e6e1f7c05284fc351e4aeff3120fe22369aca5f7003c2ada11839ca61c7bd45f7bb635cfc7969915a9b83efff2fd19e3b63fabc2054182b9c23ef034d4661fd8ef834d46c7696b1066e81991e0d48c27dc792a870c10c47e0d364c0f221759fd2b3f701d5d475b9d00f2dab4118f0e33e5fa1ce80ee98916ae7a9172c547a94c037849147471f8c10bf52fcb9d08444ff4cb7eb903da956ea9218fb3913e413afee5a1b4c4183e5ce22b07cbf47b511bd7a4f1c60cf957d8ff8c449d5bc066145a2114b73da093c1cc149456e22fe9bd4e9a4e8a04e41c39732c1a01b133cf4224ac4d3a0b1b942365b5a2c3a27a6673e19582df0820d5deec1923d0aaa0d95d1306d356f57fb91cdc7359ab7cb680ebd3baa1b4094e4b88585ba132d9ee6ee4204fb503882720a12613d339203cafd6355926c404d076c2156c5b7a7197e8e67d04e534000f20237b2a36029076757eb50eec450900fe4b25d8e717b6a9caa63128921b160be7c0fbefb25a15494eec26838a070f52d7b22034a647358975e0130f8e7b90541d32e86ca85296eb750741c91b7609f928cf658460bee4a75b06554908e1f954330b32925f9015b172afb44764f20144f09b17b12a859e609decb278648627090f27b6c5568c7cdad573fc26deff08ce989d799515a7f1f026f21b8aec04f3a7402b0e387b9eced8397ff83d3ac10e426b54746e59ceeaac26c8ee74fac266f3cec6bdc00158248af11ef0de57530a05b38f7ef72b5728a323925561896c4a8d162a2fb4b095afb9e45f1d4b70bf095264c1526f63e791c67c8590b9df11ebae239f3093cbed62c55153e5dc514904ba815b748f6ae9828596c16b3b8221d652b5210ff49d0e10f811f3d41e540109c1a9b349096d6faf52cd6d25c80c27cd276592c0df2105c2413bd8f06e62f5017461252839ad3574c0c3e866707842fd00d4b86792f77df4a13eebf9b88aae03 +generate_ring_signature 4969fed8bbcd102bdb0ac2a1b9379b4eb1c63d742bf7789f3f9ebac237ca1890 1ba62dac78458179b07c45f42970196854fbb96e274abadba387341f3529d915 24 c46ee58f44c649717637b88c7c1ac71c542c44b573cf1f4b4740dd2988e34ae2 108a815161922e25e15b555ddf548dd07ed0e6e4ef2245cb7e3e5177fa9d4eff 27f331fc5ad5772ce60209bc1ba81293a698d43434ebae890f2f0333ed3c9de1 f02d1ba99092aef6f35c9ea20c91257713a28028e582b9192837f3a3b39b4381 947c0fca32bc497f6d4d8088ac98503f9989d31312d2e3c37e40f1d12248f38e af872e612422ada3687c2fe87ab96d5a28b3a5a277aadb73038bfb40fc2b85a7 c02f2a6c9ccaf91aad5c06e8849e2a768fec07214d5716900d18abb1097d687c 2cfe5f8213192aba38b3119844f51eea398bd1df8988b4b0cac0a4573a8df91f 504eeb18ad611d34d83f757ae16c4d5c57f95651019027b7a361ab4f99e63c68 49ac27e61aacf9f9fa6621c1ee2d22c5f8bee229f562472e964a32f9ad5e2846 025b4375b6689443e91b4d2914780cf911c9f6bacaf6df676449e9f735c23c8d 1636693ed3617a6cc9986f2a3b48d6992aa2ac7942505e57d209fd0c67656cd5 58ba402b76ee0302652e857bc85ede266a79410dfb99e874c6d4fac56b077ed7 ea893202977f4abd9cb0d7e39aebb06e8adc210ebe7f068dbf7ed5e80dac3faa 72221fbd057e7469e82bf1c117f8d5b4e7fe1f22242c88963952fe7d883c0b4c c00fd924556b2ef0bed2292bbf6915a6ed0281525e8d6cb43dcf094bfbe89134 36b9cfe70f4ec52a607ee6148a827fba92df533f81c96842c88827bbc10cad59 93976bfa67a46eefe2b2ebb43cbfe41698c13369f52c18f88b9b90fdfe58b817 528aff42327532e03b2b4a1af86aced49625576712fe167f675f2349f25352b6 be67980a273439288af72fab71f12b0a388db286813c93a597ab1efe3563e2c3 d00a352aca6fdfbcba8f242ec1abf28aaecdd8232aba67245427479843b61eb4 25c4feb0b207fe0fd052d34b733cb6c555c4313815cecb930b5fb045fe6dc925 717f58b9b1d3e2c2555a2d30b4824d795450875f578831fc65035141fc6d1d73 fa9ec23b47dee88a56e6f3d6f97b5be88b074551a2e286603965c5a8b2dc101f c481d5ad3fbe10424760203272d29de7a5cd9ced278d05e7ba89754fa499ad07 23 a8180d782ab7a1b6173ed161aa00ce6f414809e01c94d6b034c8afb90618f80d9ee009ebc782f756d96184c8751087919b60d762602d2d068aaca2a9ef765900cf6b9622d183c8a9f3dc5bba85dc560056ae457a311ae6496747adc9a3baf802b383d76285295c225daaacc15e3f9ece2bc9cdfa1c2092312156f412e6951f0ab34b26c1d4ff4a312494d2b33dd610815d352c7ad5ada36f2f8465dfb33d6e0825a3b1520716666963079c14cd59e9e2bd1195b026c902e1dd04b588823dc10c072cfebba06c3906a3856c45a898fd1d077eca00db76f2101cb37c4b375cf40f60e1ae72102977e5e2fc61ec3f9c0f5869a141b4b97a3eac0391ee76717dc109f55c7a740ae7463c5237ca529c4659294f3009ba3a91bf81f714040e222df60d6e535a7762d93eeb1c2fab666e999dc64a1135130c6ca272d0857db488a4cf0ab3849c704d8ba7fedf16fe382b8494ec7b5e6da78eb9d53ff3fd900fa256000624055ccc2cad479e84bed2a88714e96a33c8a092be47893624d8ca81a3616603d304cb527d9d9f86e27d06956bad42282f59d930fce5d25ee4b10264c6e13c0d290db4c8ac47cf899e0acd54e6450837866418e1c0e85c3a8e6cb4f2c02de40f2ef132ca45d59d70911f8caf136114bdc06349fed97cec57c3be3c1e791ae1008685926b4f66f73425de762c61cf70903d608b7c5411d00d31d6489389c4d403722f941eefd40b53cd958892ee0753e4a15c8f987c86450ea2a945dfbe83a30256bc9958c0fdd15e9cb8bdb74e2e0a6e56d78029e254f8351ddd8e9f4cced30314ddec258e8e6da2c3c81207a0505e9e52e4d4b3b7ad6ad28fae4ae233f6430452965d22531bde21fbb497f4f6f4085ce01cf1ab5252799c940f7da5f02cde0e909728da39b8ed899c52f37911241bfa22e551c73edb8e56d2661e87b77eab04c9367b784ab7c01c12d743b68a20b8a7f197182983c6ce10b7668274013224071b0242b8bf92a9a47902ca13687231ee27fde4a06c2877a67849aeaa0f513f0df31d4654abeecce6be06c8980f6036684d2f6aeebf064f74a2d5622e5d220d0d65d414ff4d751f3536cf8a7c91c6f55bf1c1590177f45de313c14a55e48abf08d2d325acf7295258b31d6879020528567848289965b228c2560e9bc3ebe0cd0c73d3febe54d145083be23df1a9339ea9acbf3430a8ffab4f83ab5778f3c7470ba07075c7ad7830ba0f6b92f7e98372f5ce0378580e96d23cc30135db098bb60dec4c3348467e15ceb49153c5743955875559f70e34ed5046eea6402df344d904af4c47c265ea902287a6b2736518f0c38afa439739b2981d2c890db4250bb302c74cb097f5013aa18cffc39bf12af198a1670d022f47934e302181925039a207fd6b630434f7153c3ab633c8ca2399baad006534baccb0ca4e50cc7998c41f087f7ed2a769c7ce41cbdca0cccd5d91c5a09a008902b39546d61e4e803b869606b5ab86ab9f0d3856315938829cf8f842b6eace71c45bac99369efa0adb1d930ab6b492bee164ac6d860480dabd543fdb8d5cf3b06f4fef7fecc9ae6502698f0b1614e9378d56dc2960e702b32b51aa510a212a72c07b8dd4918b99921dc26a0b1869d6316eb1713c99fecab8cd41207d0be7d7b3c10186009447ddd9fdefd9073e81edddd0297e7fccd62ffb8999f13ef386ec5c7008806220e7ea4b0b9fb80140948956781c7aa9a3bcd6d98f27d58d811d2b74af404b06a6d1f7cf7ca9000da8b7ea064e8c07d4d4bf3e36f55018e63bef5e289c7cf9b312007d12c3468007d7874acc622b1a1223b0bc8e1ef1e2cddd4b97ae590605306637b4c44466730314fcd7b6a58edbabc2d65a4331a1abd36ba086f48235d39591bdf6d1719168085aa4b696aa834a511f4be96a521595e55d0b2e05110f33a635fc0c7cfa91fc0104ec716c9060d4b7d1072770e204ace067dd67a7af97a7b7dc16ae795f41a50138b6cafd281d141de0a524acf1803d1e4b1cc4953d84df84cc67c8c561e0530458126cc4069875538b89174664a7eb039a5d98e5ea207cffd6d00f206914b2034f6e8e79e49fef24e812b921682edc7d6e55a5ffdce9e2f9fb25372abf2d600472cca714723b76932db7bc30c4158bd3eb5188f1f36569a4c9f4792f1302ba04 +generate_ring_signature 1fae66e548b667c03a1855efc3facdc79655198bad3b985e00176550c5189bbf 6604f992ee42535783eb5db6d9b694fb38f318d4e0c87c1317442cbf39ef174e 16 fbac3d2dfe038ed71c37d41691b5e1b8cdcfc95cf974bc08533cbdc019ab938a 0a1890ca54c733468ac9e667349485ccc05504f6670105f156f2418e65ccb4f9 eb8b0953711e35645a33ef5fc63d4736315d9d91b8a85d22cfe6878dae76d21e 26c3d07830628d8644195d1da54d1a3190d5c39c8e834149fa9153f748b339c1 fa838950a1246d434f92d6487c0da4459666d606b615feff6648853f8527d56f 99f7fd869b528c19aba2a30d0c3b68fa5ad8efa087c77134fbe94c2548f61096 945f37f4dc555e53b319d6d89f66248798feb776ea9e4c206d6fb1716128f659 4e12a93723a54e4a8ed3fada626aa773f7fab274eb71e24663973281a633fd31 211b86fde85668a7fdb04bcd263516857c90731297f1fbd82a7de7900c16d8d1 07fa3a83263858c236319a04076df5cb6de6f6572ae46c5867c21c6a7161e2a4 db433c1ee01d59fcc187cb1baaa6242ef872fe3efc50826ff08dd5be8900651e 2e0d7dbb8241b77d33e725348a57e32425eb7597e64bffa5ba87211a1cddfd45 003fd5cb8522c535013cb50859885bc6f6dbf2813b5a2cd62e251c3d61e8f8ba d79e8164f702bc74062fa3229dcb1bd3521840778594e418812c7efbf4088d08 6b21e617c01c2c88765d39c9b9b36304e29adf7fb43ca0badb7f84a5d4cfa8c5 18a661118f2bf47bdd0e887d9ea0270f956f37af3b5b22ce4a9c06d497568295 d85b281d5a05ef10cd5dc618de621462a1ea69119f911ff4c5b219e5da76150a 11 9a951ffdd212b0346fd2d3c50b6b38386407fe29ce94141c6122857cb86ef907bd20f0857ffbf6c5fc8cd196957c6c1e1a17b03826f9f96cf0560aa1b152b50b976610a52e19ea99a6d0815a8738a89adedc1bc2ac582dca4a5cabc64368d30157873aefeedd3594ab8c40b4d3f871d79b81be524eb3f02959cbe754d596f90f7a440f50c5f6d31c75ba9b8e95070e46d80ce1b9990267d90ac9358a7c60e40c21fd3354f7124b2b6ddd255fbee040f0c8957b3cc17e60a6e2f256823458dd01c819df683fd7f79560c53bec127314bc930b9ca3878d8900878e383449e864019a684c763c15a6f1d6b1ed9be75a7854755b3b8594d9006205e5ebe1c73676023325b1e533818557cf5bf0dc6fd117102bd65712daadfb8f5c0826c38b0096030348d4e79f4a20af8ab0542df4e88f38b851997d5394c2e7ff8257b1088d450a8ab6e5d3952d9fa523203276ce6c759b0655cc4d1dae366990b9340a6268a70c7041c969d202cd104d72deb6e223023122b76271b108d4dd4d1369d1b7dd320987cf6a6f1aee843a1420141f443e110573b7a4367cfaf2917e28dd844bdaf40000eb38a230b441e9c96780c67f93e02448dcf99eee13c50e7286eacf31c8de051d69f243166b3e1a78b81f32f22d128d066fffa0a5329c330f5787dc65e101029c6246f8e6f9e0c50c8b0db60e8435bfd0428f559fa06ed233525991d741f909b97c050a9c868cebd90409afe8ba41f46bef32386f1b8f8ea14c1ef05412870dae047c9f85c6d1d99e0be0e99f8cae6377918046c4fa17b95d3cb8907060b100015e9c1636938fa9df28f6779f219dfe8b1a849e09040d371546e75aec327a05eaee56576575dae15347ff92a7361767c3332f01badc2390df2eb80904c8be026b79de215a60c4681ac529534b8e7cb308f84f9aaef75250ac704e10de72a201763e3fb0f30b0cbe800d09112e56683101a46b96427cdb18cec920c0cbf43801fddf31abce85e2a82f45a85f5846e26744d467c93e36668c9cb0189a44e90007ed692c58bfdc85ab8fb5e96cceec86e1907ab12dd335326a7ee656d7dc7d64045fd9735954fe37eb3a7e1e26f417523ecc4828feff7e01890f74d01ce7e455013388c440fadba9f1436b0bb9e918bf191fbe513a56ae5cf193652b0f6393130b9b324bb5a4c272f74f870679cf8c07b739f804f2cbe3674247a47d1a4444a5044c65fc7444a864d48f1a88d2595e879d55e64474e8e13c28d61176d53dd52908b6a16e5f432b9ac79764beeebc7df8cb982e5e77179fe0ff91dcbda719d0f20dfeadbd80de1bfa04930bef661c69188bf8db0d552017c4a1e889b20d22b8a809b43221aada734cd615b32eae2533bba902eac73e8db2aecb7730cb9b373c430c5502b08088252b92fa87ee4ac96c50b0babca330b28e5ec75b7cfb7547489505 +generate_ring_signature ccd52a1823262f50769de292d53194ade83498d22d22723425aabb488ad34db0 60cd1c2c012845ee17a6a2e2d72601592cb52844c54675cfaeb56a0ec2d8af16 1 688495c7a565bcbce015a948cca122feb89fc82d1104c53e29f6d3ee5fa9aa72 2d1e0e58df23232dc00444a74f744f091e21254240cd01ebfb5c96c0d9cef60e 0 5e244356eb8c001a9faa7d16db39af7bb2d0cda0fd287fe671da8edce2cf0d0af1a89500621a45801b9c5a35e6206cdc863fbeccc520c543ccbceaebf943d50b +generate_ring_signature 319393837238312ec41b35b2fdffce98660d179dea1f832646206aab86476c8f 9983fa984485fc6ad31e895184e520959e3a8ddbd0d7630f09fa65c9da9c0efc 2 a8a545f22a342fd7fa178909b7a9cc448f4ec6ad127604143f30066a5725dbe2 91ee3f82128d7db8e2b8c1260fb7a9d22657f321c6e74ff58b82a9454848fd9a 1f9e01d21eff68d4d955c63c3effbe03d5840901eda859e2be7bef238247fd04 0 4a28496ad386291f8236dc0f6c66417acf85b53c4081f63662976ac4f898e608b16876fdc5ab5935c3caa1039ed0161cb756e6ffeee41a742bae6cdbcb886e09ebe1a4517a53bbba21c2e4436b74e91365f69f8a6b419bd81f364967d568b901969bf60869395c5ebc0b7f72f58d2a6caeb4ce9944c82ad97d6b2e018aa69e04 +generate_ring_signature 775f8cb9592fc9b32e118d6cd2422aea08a8b4473106b203c258bad1703b7e02 8e2d476bbd91bd2f745f7ae93b5cbfcf18928c5e31803cf504533a454e509981 3 26e3708bd26ba39946814cac1ae28aeaefa7fe66f7f6866915d07b31add3b503 91d3e2517ff1a178470e299e8bd202a705ad6b7329b8bbfcdb5acd74da7379c0 38e6144af4486bc8409bb70dbc8d7cf48bfff40fe662d470b6477800df3ef3ed 3f1f3f311f694125337743aa4e439f8e17e2873d4292426a6fc91ed519f5f80c 2 b52cf60a4e0af4b342074bf9031788b215847aa8a4d8d0112f0e508eb5580b08b01eac839e91bd5d86f969aefb1d909d764a22b1c826d75deba6b68311f6130960468816fcd11c9f67db2cca1ebbb7854c24bef4eec790b5294a1bb94de08f02fa871ded3635c9cf814a72bfe7cb0fdb22338c779ffb052e821d8f2b65ab0f0ca1584e3acfacb08a0681ce97ea47b97d35ca5459bc178a42bb82d48d493ed10fe566d6ba3d5309ac46619cdeb6dfff0e95f0e57c5cfb861317692dfe56c95f08 +generate_ring_signature dc01184a126faeaf6f5a815c95c47e9c04a7944eb0bf9a333ad1e170cc214e0c 5ac9130bfd571e2d78c8022b439519986fcf82b700397fbb1c2f7e5405b61b66 1 dcbc0a8cd731a5c2ff23b7743a605ff2f117a0ccb85bdfbe139e6dfc2a24f118 51402889a343dd3f20aa85a7ef7ef5be22a4f692f0597a1de00a5833cf788c00 0 d0cc35b689d57fed254e2404c22ebeecc84a6c81ae4e6dd1950f6a47c7b57205e627c7695663211fd3b52f2255466163580d4f3edb87c3b06387a1ff97a4e102 +generate_ring_signature 4f8021c13932265c99a296946267c9ed73b3c66150db889a572c153ea1592752 6339bea0a7d5f01a80fc6f0ce92c6ec1aad50fd8071ef31b8f1a831d15efccc7 3 8777723cdf3be281dd650f711a366ee9ff32b3c05cbaec3a749b201ec07e2535 c44c5d424aa5acc70c4900626665d6bc82323b5f8801a61f089775f68de60372 bad15dfd31f607644082f7655f9c3769f9da7ab32a5c4f45eff730b76bd16c87 512e84d058ac93fbee413ae8c9830248d3c7a70e4d5f894e0fcf3b5b6c3bfb0f 2 3b10dde939a9ba43d2c72f41259701251cc57ffeb7c5a015d1b1ea9a18c86f0802d4738ad1b99ad12e17b989ff7fc43e475238e57ea5ff3f77afa3f960ac2f0552f33d7825d5baff0f8233b776327ab4bd1c494a8fd1d85838d39b5e65f5cc096662770e6f4517cb568f0dd1783ebe46dce982f1c7b6995045de5353c30b4c0a3ceeb412bc70ef0bd9dbf0af9e721f2629137e4e5f62f38324c2de14639eb108748948a1ab081faba1eca1bfb73c57e8de11f089e4fd2caf0996d388753e8708 +generate_ring_signature ebb89df8b7f43d432f84682525a8de0c5b961bd9414632d54b0a793c5d1f2806 0be069016e8ee85fe0242cf448f2394a9ca03a27525611250fd7628659bff05c 5 dd994a333bb9d95df4c51b98f9209a0bacead1948c52b006ad61f52612fcb517 bc2abdda1242c24698c74398aa4f92da2661fca9337e252b2474dd309ba94dfa 352baa4210402864e68e3dab91a598416ca0de376f692ee00c5769c96b9d70da b982c90b36c59a664e8a39ede9816ef14714f421bbcd9d9b96634dc8c73ff85c ed1df4415ef3692d4f901012f659edab03507749cd84c343729b4534e7698af9 a5c86a1e93caab7b9dd20c1da3083428d6e086c4c20526fbf724fbd7ba296b03 0 d91471d8f9386de5b0496c822c680bfc3a5e52b0c31f5907875e4b27a2c09004c6e331bd72522a08b8fb5a0421be87985fb06a0988175902d0870e115dd0f40ce22efa17fb77612d1b3235e8bbf4c60079310f3689147e6ac3b7f049d69236040a6a5ca8406a6581108e611a7d6d8729cdb2a168657d85e683ed539e32072f0695361c3567b28fe7bf690c737d870467b645d74a01054208db3eac0a6ddddb05324ade8a016e28758b0dfa6f4f7856ea9969cda255dd93424d2487080a9fbb031740e026d90fb3c70397b3d154fe18e8d9c12ad96d1749e975ee39d36d1a090091a718d70d599c57bbe5da7220c675be9f89bc08de0f10363b5b8e665596e7039092f11dc333aad6977e7948c33c16c99d2d9cbedaf76142251743dbc381670da644d46fcb0bb488beb0596074738affebb02f9bdcb1e482256233c0356e930f +generate_ring_signature a24a13a502695c2f8a6065fa6f005d257be436a2c7ee8d2495ed92cd0cc8c339 dc9b7751e51eaa409c6d73a6958f0402f3015d327f39833f9651ace39c1fa9f3 1 85508b343579d29c48273a99767bc9750e07bdfc0f20a01e7876de99a739ef53 fda49391fe688813d76335fd3655c3975d4b9a8cb53b0ee346d4455ee003960c 0 ff39826a2a4307bc79f486ce90f60cc7c6ca4c15c4fa25a476e6aaa6eceabe0fcc9b3bb19897899690a357069391384f2a65df2c0427a5f742de6cffdbfd630f +generate_ring_signature 644fffc89210281ed0be8025d6f26bc199f6acef6c7d540db9074d22d480962d aec9718a64e0550974a11920635522df816cbce7266ee7c927ca7b85b868b247 1 ed397dc2444dfa96130d07b7ba2466b5547ab0b47cc15dd6b8bf5c129e2a3409 efc21d0119c0465a8699a6f4e38847f4eafafef6fe13042a76de0cebf2eaa605 0 945453046a6f1b52eed47c01e391634e6d522d2cb02e0c9f0ef4258f27a4250b329737ad11640e9e5116b3e5c69b3f6f65a7ef28fafea9d905b5bb7bfdd4dc0e +generate_ring_signature ec4f01a5d25d5c682bb0c2405c48d89526011d512f581d89f24fc51ed04f479e 37482374336d1b81139ad17ededbb47d0bae9b88fd6a340038c7cb483122b9f3 2 6468c6a8e7df132757c05757fe14a99dc7aced23d707daa7096c33b9671ecc2c 721c647e2b37c00c216bffe8b8dc54bd31a413d284d73bdf6086746a3b351167 f605c2ad33a1a44433eaefa43fa0d4ccc1deaf6e71035b2da883abe292e8c90b 0 489c4bdfddacb6191d699167ac355dd8b28a3025343a20a9241ed3bde090be0858d5621f367a5de16bc52acfe7c04dc152f6e4bf9ca2cae62dacf66977bae60272d243ac5c921a3709adc963f032a445cdbda3552002302b1a65549ce91dfa078328d9a6fae6bd75499af5dbc3861b0465810f1faa917c813517bb9ba8080c0b +generate_ring_signature ac11b3fd17e29a190b3ae50085675fc9deeb3c425b0e3a49b9a568db6816b324 a67fa5a096ddec73e04f95901b56f3b65d89f3ee9280afcae7db0bd62942629f 4 e1c72db1edde330d10441ca86845c658bf7825f94ef6ea28fe93ec95b69e32d4 a11dbdd572ef9bfb20264324290167a38fd93ab175f5c51998e2c93b47ef4ecb 5f51dd4e616a80c8bd717dadcf7462bc7bf5af7038fc7ee02aed5e50310ef922 26a3b45ca79b1ccaa3d07c6c1897da68511a233d97caa17df3d8b88514691c0e 86c5d31e7856dd3c491566fd65788c32c232260d9ed14c8a746f42eddd14f207 3 74ac88cc2d1fb879342c6fd5ede58a594771ccf1921bf7327f0f65cfa041b2074fbad0e74b283c49c7ceba5e3b968cb9bfd70aee408c7e0c21bae964c6476a0622319fe204b5b6220ceadb0cf6a1d06b7ef2b1c8e0a52bcdd22fb5860fa091058b275cf8aecb93d8d0d16a7496711f3e9c59ff66ee2c656d64064f7e4790a006d7fda8eeaefb74e325f37c9aab7f3793933a88ce7d8d6b1c165b879662b77e0b30beae70a43e88e2651a6f6f3bf9bf8e2eba6a3b0eb03d47a21d28ca102ac4025aba842bb3f08b8595550a1be3df4c7f932d79f915ddc7d9ad0834cb1944ec036ef78d643b045647cd2d1abdcc641bee69deede640c17d9ffaf27a287c05f90f +generate_ring_signature 2d5bffb439142fa06d805635a601af97dec5bdad7cbbde48ad930deb32523477 fbca4ef1fefeacf7f5a474a219f7f5f5d30ac9077fdc4cc65aeeff2156aa7392 22 3f2a6a45ff1a7e67d61c889445b22327433d1d9dc249100fe49f28c4487209c1 5a381decc7e0d367c2459c617286058bd35b88a5e68320201f2b97437f0f7524 ca9e6ec0f3a789de051340c7e7eded2e30bb6df19acb4d245478b0a8176517eb 4e6016bdca3620f8f750eafc2ffa02e746ffd6b444dc05995eda148c22ffb615 7b0fc9631e23f953497bb2ec9a4520a2aea664963de500fa0cc2476ad89520c2 13480a2b6de8668e5293b844256b955f53f913f717db121cd593f977e745a96d f593472de652671fd0c8133c0bf63aaf624399bfe4d59ea34fcb8df17a309305 1b8473fcc2f8a6cd119f0dbd3e13162b6af884180d95765075a669cf21d3bbea 88a771c420fb70ca822faa787627ec6b738a7fb3513456cc3008600ecaabe856 3e2a503c18d7a604d0682832fe8cfea70ba213e619f236c729ad8dc007978140 b80d1a75cb3c461c741ac01ed2954420b9d43529ea1e1c958b94d4c0ed3d151b 8f80d0189da11d3cf3c5c764dda9c9ac7a5c26670411dd91858f992f50485b65 0c9b83768d4c9e9655551b694da5e1812e0e00e286a91714f1da495c72d8d5e4 61e836a5a10afd13106324ee32570afd96204e50b2dd31f9157f867633da47b7 31c55d076645b4cb7999263bc06d8b7fda365f3b16983bcf0146c16ea7cb61a2 b46eacfac0d66574947bc0c4db1197c209dd2f742e1989cb90c312a297c1aef5 0e27d3199fff1c3346c254279862394c8bcc9adad7e7e136bd6d0066efb037e0 4250d557c33a8c15716d0728d4f55843a7ad0adf56439d5341d42efc71da006d 183f417f3585155db8cbd0b48d0a781d84ad06041dcfc2f7d651c56b35fe8eb4 45a8d5ded86c252e4366dacd0b6a4dfae9b79c6f5e33f5ca9ff19cc46530f596 863b621e3fb32495550406cda6d43875ea737e91bfd6e3c7889dda904636f79c 3c967979da7730e6d5c74578cc5dedee260ca6fa8d00d6ff3608570617e2ddf5 f6a573063c7f4dead683395e2ddfb9e9e12ecbd2eff25e514f9185052211630a 12 518ff7a4efba6da821593529d5032e1a8875bee2e2437f78498eae36bc0d410643cd91f491797b409cfcfd3e3a36afc606d6dac4a0f6c262b2d4b72fa5f8e60cd6625ec61141aead0f1c82e0ac502bcee41e6920bc812dfe2a1596d067a5420b2f942c19fe7aa5bc2b677fc45a7ae019909a3b2b20fe9ada73e98759604dbc041a8ba18d4bdab182c2d2d07a7b58b8c1d5102ece57177d5ac794097a7d31d50fc447e84e14ce6bc466bd9feea5293ea772eb45c9041b515354af619e3017de0d06ac23f54ca908f40fbd6e5603fd09b7a7ed6ed271ce90ca6187911dfb50720c857d033564c56a0d64f0066dbd7cee44aef0392496875d1e21d4aae4c645280d459f36990dbf5c3943f6313370eeaae5401d4e9a10fc3f12839bac17fd49b70854a45543b801057ac84d24218c4e0a0c490188bdd2a1ef8880260db5e6ae600ffdcff73d83f7067f84c566b50e5d10a424dd800f25aacfaa64a3907c37a191014eb39129dbc48ee48a346f97dbf007f1c1d599ec0e50bbd05f3c90d67a4c32057357403af89d6aa073c5e69b437061a646400435dcd4871278c0ddf8fdc7910a0ca61ab2926ea7013d1bdb6ed8d8f8fb0be4bb9cc424c00b7d4985f5169c07041af96beac30643101b7bf19d15c08977492070ce11ab4b5e048fb9911ea40a0043cd96c1c119ef2977a110b9c34b63c725659080b3971fcf2f3caa696a6fb2006d361edf59a644d3b79b1fff23e2ee1581f377a0d130a7aa2ebb75d132e46809fd55557378e60cbf75d30219b4842ab30ddbaca9933cafb7a91adca35086280e0562020278b369f4950ec0a494fe33f91c83e290b8562739977371d2e5dda60f416202d6c9b4bdd95e4d48494a8ebb88dd83d51787fe8f2f1434ab3901a8c0079dc640fa0e3f6c3cb687dd7e11b8df4b47873773d1e0eaf139aebb940f98be0c135c85fdb6d54d5055bf057644a7a2dd2227dc9c6646a336b828c18f4240fc0766c0b26a5838db51b9c262040434633979960e86c2d2748065a92bc11187c8044115823bc810f4241754ce1f8ca2e64a49d23011638b0906b8f9e32f54d12d0d56b07ef27ecc214d4402cebc5bd73d26cadaca704815087f7de5b4773dd68303bbe1820dde3aeca5e17aaeb603f0b0099196466858559c16860e95363b362a0d85b0a84901fe54f3c4a5ce893b3d6c8febfe11e7241e8175ef2d17c5dbed3c08c29e55da8ae0147947f3acd98eec093978145d17fcd55c64d2ee099e9d42e5087950ccc2b8cc0856e75968c193a02f2f3935eea6f2d7e267400fdc5b71675000f78fb94a05a5a349bffcdb6b845ef36463eb3b08941118239e243d9e92923e08674347dee5956f30addb45eead95b65c033168c0df7382d7fa2d57070d6da806f7be2e150e525499bd3b7ab02b9c516ba8f903290022a60ff1a415e75ab4450c24f3d5200b74945fda47261b513e80de02a21cc10c30126d812887e0518d100fa6ac743e1e96e2bbc2dd2dad4dda796f9a9b55261f8cb469436c13ee1bb63603737503b62c4e93d9e3da46f68e710bbb83a5845897774b8f2d7b51bf2169390a41cbacbbf152f8ec2545b985521cdbd1eb7dc19004e73da733a6ccc0d7e56100c81782e38e281f5a7683ad424d4c8e84c6e7d306cbe0f6a18584816266e8be00a848a5dfa85553fd61f888d41010545452c2a5d77886466628553741e186620b15708bbb14a9c77437f5532ce20c805f1d38db9b341a5ebb5659d629924f710cc0cdccbb6d4c24b2930f512ba08f206d421d7b4d74965f7a6e5bdce9b7a34603c2260a92ffbb628307871c1924f067c4a4a57283572bb8bee8b0de9c76d8b40b4ad6bf1638727de736337288d39bb22c6babf44e3359cd6c2e017b3c241c300c6be4411a488afb1917a8fa8fa257e02b8ec041b091e763533c40139ba5b1fe0b05a1a0f628e296e63ee6518cd884dc7913f6bffeac4329ed394e342a2fee6d08 +generate_ring_signature 024bfdb515b29b7778a8108bb4f6b81eb2abe37867e089dacc872b58466c886a a4362d3c4956eb41d3abcf5d613dc1a617469398e0b96e2dd1c9f857b349d0ba 1 5ee0f2a26f7dc30b5d5671a7a3d1fbe99bb9f5d8b06d0270b1ab47d3e356f5b5 c764d9d89c080504c3a7bb4e5b2c7bd053a0965252b1569361fcd2335d529b09 0 aec8bdbefb4cfad3d295bd3af3e3d6d5abf5d5acf278e4297ee3cee3643e670531fcbe95e03c272949b55bce1eff992b5e18c0090a44617f538d83b82f571c05 +generate_ring_signature 6c5b151959b50ed0242edce1f9edd975b4f8ddd0f7eb5901425d465c36935ef6 b2748726c51b6a37d04cf115c2188683d73546b23c2272583c46532b8925a516 5 94ca0ec4113fca43219c235ccaf985a48e4b0105bf864882a5395ecc12ac0bff 55daad335cc3fdaa27b2ba2cdf6e0a44434ac6126854284af1e67a1b51b8d716 ea63c294119c616cdcda89684444e9f3b7ce5785ee34d2613863954099b2081e 76459bb81f55b793ac840956d2dc7f7312c722fc317727b6808fc4bf17a98d4d 7e69f6d57a67c2fccfb5cb15460140f723da355cc95b09cf7947404dca1d7b49 cb86b1c9c8d11634c2f779cde332c045284c408433ba32cc7900c55dbfc83b0e 2 e8063eb54c024ce0daa8b9deb17160784f72ef9cf57cca2c8ad0a8b1e705bf04606317b03bf502a63efa8911d1974048e0c2bb74189a9a29ebfa69b6f04c20097343de5f75b418d14bc5d7869da33f8af0e73e3ccbe8ba13bcc4858746e9830ff99baf481e6c93591bc4e18f3d30ae2ae043378e9a702395b42dc67c0e5b5f00c7846567f81a0bacd21171ec3f1931dac1e15020bf0b9296a24bb69b3818390e5b394ffd05894cc5959c1e0cba118d55a8f978f131f15fe3c650b173feeaa10637ef86f1602094dd0c0ddaae32abedc6b504293fcd91ee5d7c787616962bf409f74f6a7b3b82583a2cd3e6bc4d75ff5d68f0c8ab5265d01d50ec5b5a805d5703f73a1748a1d4ec15fcbe9201102ef445e11a34ca2260be5241456f7359de52062bd5efc717d0201734a11a252a3486a1108703e56902294b18f2f805ec71050e +generate_ring_signature 580316ef822fe30d96d7ba1cfcfe89b757e41d707af3ef5539d8464dd903265f df6fe0537596511f2d70004bcecd1f82995154a37f67f400164230595df08658 132 b34b7e02bb93d2bfba4e332f150b87c4e562a5be5f378a16ce64d34aec50b248 0e711932511d8b99a38cce193e4805c673b050629d5f86516c90f49b86cdfd6a c8e92744a9188bc7d43faf597f4a7e0f21f736b459e21f05ed81aea70ac6c4c6 a40a92a790975d4f4df1cc90a37d4ebcb58c9132a0e1091fbd4e3bfcd79e8de1 a0807ef4d367aeaa73e389f9c68b3c2b2c1e5e3db741728eabcdbf0b9e858b66 a36659be808a22c101e0eb7885fa7f6ef4d0dc4be513fc03bd1d632f8ddb487a bce296dcded50c677db43f310e39bb4502d5939bf5a0ea79c7fc6bdb8f4cde4f 31d07aa07f114d850e82685a49a3d5a6baca97fc5bb0cc7681dbec3a7aa24dc7 f697a6992c57955b70dee9aab9df6ebac33ec1847c8721b5566410b0e5382300 6f6a5f10bc80662bef7a45ed728f7b4d032ba29653aa405769c0114085eeefde 0facd1f2d17e14206226d7f1308cfbe191c3eefcb48802150e8629952476c219 4e7d48558fec4da2b72f9d1e9bd222db18da03e10a5e7496b8d70279e1cdd1a0 c85a6dc62ba5112d92c4bd939181fd0ae3aab03a9b68abae4718a3cc729ed1d9 8da941cb85625d84075a1a29a6b7618c6650e7bceefe34f5282e2acd540acac8 9a6a065a7ab18579303f6010539cbba337ecc52c7f4562aeafb595a92a62fb70 6346772abc043249ca1b344cbdf3db4b1555876c2cb0b8aa0e6af91299c5e66e 8655614b54a18dbfc16fea98fbece5baad090c4adc9e00d0cf289da81cdfe09e a527f0cbacc894a16fca00de248637983bb46437100ebf36cc5f88a83ecd3b1c 8580c646f8d56984adf152fdfa705bdada01099bfb42fa7da335c478e588610a 9450ac1ac1b6804aa020f957c0dac0f4b90b0097f26e22fd72482c88baae0f95 3801f8a777705d40cd672d44f3198b774733b2c35738da1e28321151894797fb be210d652b7f9f3642f828c15ecb5caba64eab8b4c7b0f70e4757453d8e45fc4 954e02d7a23d189f3830d52fdba6adf797fd02fe2d6182a4ef0b268c2de90c5a c7ce5fbcb2833703c61356e58118567f2f73780b78cbe355e3bbd04f7f5b02ef 5f788984b631da7302c67bac02b29d25928661756f3ea07cf172f863fcb0b789 9844be9cf59c6226c88df3e6c52e96f7cf5cf1c07afaecfd2159fd0e5aa7fc31 be8601bd6d88bda6d7294f4a4e4cf6846389fb06886f4e3e6c3c2b361ab600ce 752a0afae1156ed2735c37e302ff1102481e00a88c3a8e20a1d52b42114c9f53 7ddb961f3f2a38cc9e22cd54ed0a90fbbda1b7943029974c64de97f3cf9b5006 68a0639cb1d89c61d3bf104cab8bbadbd05a4079d5693d838f212377d0f42168 da647c44b92d5c3f7c07b20816a406f63371c9a8686eca2dacf858662a736f44 277c3ae16d72628922827253010558b42c6e3610d974eb6939a37f8de5636654 2f3ae1a1f9ea7ae89cda0439ec65a58b48101f1d4bca4c35eb527f29e1407a34 298024b5e0af8ed698dd5c779116d55962329b7f587b0fa08c1f86212b2e213f 3407ac609b2dd85a67fd303815aee59870fdb4127741fcd1793d45118cadbb84 24a3d9e3062280ac09c56160ebbaf3b75e2132d96d92e3978a95f78f406211fe f90ad26d85cba3d294501ce6e369f0649512a18d7f12f4d187a473cbcd7ee511 e24de913a15d865c52366ef6a1a15c7090390cf3ddda1334888a0c0acb7c3115 516b615402a289332fd66181fc297d563e5f9e3950b0722ef6cefcae6c5276d2 62e3d1f25b3e329a0a9cad17873c48fb12a301bd50b4ea542db578671130dafb 942dc0885ae44bd0c442abf76342f06035f5988f067bdb259fde8eb2848a6e4c e3e2385d3b680f63aff02ea099ebceb64572668b54e9b970b31e07e1276c46b6 4f6a69849608670ff3bcf7da7db3902c26be3c252d43c7a702de62b18236ec0c d3c044c5d2912f8abba64cbdb7bee2dd94357ba44a9ec8b9d9e3cc1b8e83cb66 ee33613581eb64367a9505de4a65413fa0c0debaf22d1165bfae6fa2bbc2cb59 7a3d22911b734dd0fa856794db04f7e83fb2fca9457ca2faa591c8131a2157ea 4e7d6ad7c1a0ab021dcd214a5b02c5c0618c4c4fc6193012573f16d91cb5850e 1ac32449eb817ad3b99a66bf2402bcea30370903167dc4df785a45b2c4c84f96 ee668aedde320834b8a95bca725cf88ab2920564cb45bc0f92394b340a04569b 427d3988475c8fdc5bdda5490b12bea054dbdf7eccc2f0c246d5958affb67d62 f75bdd9e9e124cffa3d03c9201d068d76d16cc9eba2864c3c2720129bd19072e 123abf669a9305eaab1a5562439afa3428f1adca9d042eaf45b2091842436dc0 f7a692be3668f410a210cbedfbc64921cf14a7ea4b45acaf594d29d16638e660 311121435b6b5955dc30fc0023f2b449ae44f7a397f1c1860a5ec4ccc942863d 7727e4ba903537d9c66979e6c209af6fb9fbb4a9023213fba5fe7d56be8dcbe4 f409841079df8f98e06d357a404b8869580f2270aee60be424a031e9d6ded0dd de70c4c91cdfe6bcf413d42d326f30806ae6087f733d8d58cf0aa2590d09f721 70bdb1744d0d2ec2bc6795dd28c9bbde3836ddc1fa605cca6d3a455b5a3a004d e3ee206d324e427d7f0542bdf71e2f3fccb579dbabca8aca4bc6d0df509b4607 b4a506afb3d01b20099c3600ad93c5f604bdf1f13f952399d116f439a8da32d9 6618af72e85b97ede6382ebece262c2ebbc32b9d2a1d54ac0dbfe7c6836300cc 217075fa6629d5a14399e29f1d0fd54f354f61ec550db606dcc0ac589e529613 381c161aab1a4b1c19d41472e2b4b540195e70cd4761bb03fda05c0d7c9e053f 272361cdd8311206eac4f92d092fad18e36f5c130c8c0cbe83f82b8dcc509f84 d512d336c1d47c34d646e8f82212dcb8e7bd89f32f6e7835276fb30c8e8d4ec3 396b4e233493191855efd4c8d87cf956acd297dbd624d360ccefd7e138126ead 01d4ed988da7fa2fc5884efe4ca0a5d50e8ef9393cc8575b5fb289763878e76f 3ff0db064b34dc1b128975e4d036573b74c8f1309d9bcf93025356b8781637fa 1eec915073b85aedd46514bbe1befa94905b08e23dc492f5235b2e9d170f6516 c16739df0fee78b7a449cd7fca3c58cb93e4c1647fce8553313adee76d6bfbda c4c71ad3cd3388652c611ba5b75d5f6643ee0c5a114a6234aeac90b3b3974d91 85ae6e8c816a63694c2d32dd673c4ca1767555d86c5b700e0435904987666fd3 ae209ec2e91bf762afd630fb93bb4a2730b3107c6a6761eaf34f76dbcb419d98 279e86f8f39c732c9c990de490fd935f310961d3cceee328b0c919939f87de0d 18b23aed6885b84b26601bec25ce90f3576a7165ed273cb2e0447176d317628e 6bf656bb05fcf838f40ebdfa70f78cca4777c3aab0685c8744a0c63f4c70cd1b ad3643492d48817f5921696c39ee953cf8ee1932ff7474943addece1039e4c7d b4cc32d4017e585db9e60d718e20042ac4edf43b4cdacb66a28d9eeeedd40f98 36f39c372a4fd7ceb56af6af5afac4b9ffcc25217162dcd9e3a18ec871b1a99b c5904020ba1e53e5d57b5ce10f1106945e9fec50ae6a95be547cd55b80de878b 05d76dbe6d076dcedbc3df53401ca16767df5b015743f5e6d5e245e30d741d94 d60b0e6050aff7fb20e260a30de42b73f2440a64061a6d1a3a05630f61a70b91 fbfc18be3c293fe437988cf496443eac8ed328f6087717dedc9c31078a276391 64bec8424f35ac9eeb375422fc16e938b424d8cf1b51583b4d284f0d93ff1c0b f2fc77263a6e360a34ac1d70ba1824c5568c6f6fcea28f20de63a6f334a0b9c1 ad6c8a02b70c13613c9326211c73dbbfacaac0a71a3cb8721a1341fda1e87225 d334a9ee9d4ba8a3d54b5b6283c7070b1f83880605546aac60675c3b73ffbc4f 212ce511eca9f13a3c0117cc2117152860f89a7bf4dd552ca55ba255d5a98745 8d85f16d32eebb600846d81d37adf2ebb32af99039ec3b847aff7b9389c8400d 943848e764cb16c9a25f2bed09e27d0049f83c88e77d21eee0866c8ea8a314cd e380beda36255bf8dd1cee4b657b655248d0254cebd33c1bf1f2faa93bd11f05 149120e474adc94cf3ca1cd7382b4a6f2329321b33ef66a5e3737d537eba7d34 96ab4dfdb1205ca71a5a49685d292cbc70f25e9834867a5afa21268c4cd94a46 b5a396b9dc5f972e92296d50018ac3f77ef0bba1bd9a3f06c611b48096043aae bcfc970a9fe5b0a80edfbe3cad324dee72fbe110344b21f1ac5436c90bc2cc1c d669e0d1e1e0fbf85caa7225835d67b6666b45f27c3452709afd7453ede730ba 2233611a4d2ae4fe6e73904e12b4e7a1d0aa406a444406909d65ba618340de85 58f9eb6f8c1873dc73c28b1103dee444c07b02893b0f1f8429c42c2af5389f35 c0f7e228708fd0eaa663dc49644cfb21116ae33a03b1a9fd7a16b4f803e43a09 b71c0f3f4dc94b92fcd1a01a8ef3d9363017f91467551e214c35ca1a43ae7906 01bd37ae01b8216845b6f07d8a9ba2a8a80d9723350dc5b0b6926e477beb78e8 3da7d1bba6d342c4ee5e03b9d92d9f41606dd8f044def81ffd5c8cce54370ddb a7969688ddc41b91ff4dbeb2a0aca8bfcc75105882d46a305754e5c6733fde90 5b0fac02f253aa4809f1c040be34b621ac6db3035e0a10938180788687d88145 9bded6d26f5479edd42941538f1f1a50b9caff323fedc1f33f3ef53e12defb36 f296c97baeaa0696359bb5cd34941182781a3d311ed67d4675d7df87d2e5732c c41553bdbdedf4c20488ec8a7c7fedd1faca8f8aab596f350abcb85a83c8c48b 09ffec334be4bf15ee90e0a178d754e1a6bbc094c358eab56d2c1b9a81da251a 4bc93f32e3157652d3bbd595d2ec8c9d97498ad5f0fe9d41b0d345578892c615 25b5b69b77cd994099d5c035d94e03eacf210195f7c7e5cf7b1266e9ab1cdc70 2ea6efa4a09deecfec1f761066623c9f741b10e0385ad26f1d6c760c0da22d90 16bdd0e98f56a40a673332a001215f4118bbadb467e631f2a9b49d9d1a405ab3 1dd62effaa2fa12cc95e423a3a3624f23454e83ab9c1251e58fa30f016da7710 a2a2fb07f0f153d3e9e301cdb4a2af26a89ba47ebc7cc3a1b6622858b7e37815 b4479bdf7d7a2a767707d3f16491d45de341dffebeed8b65f36141144afcc668 ff09430fc170d2212225d96f660e778ed817b3eebec83d52549cc1254e67a2c7 918c9e59ecffbd7b636e5db439a1f8defd8764461f3f2465354f178d90e061f8 7d8e2baf6c0eb2840b522eb57025369ba42450e36e2017d07336ef2728039c60 6c0bb17ddce3c3e2a1ccd6202744f477510b05cf1179b81e28d175ba5a7517f3 ec91336052a310259a74117cde2c37df06342ddc42d3bd641d3ff30fe70f062c 4a1302dfe1eb027c045b3c120bbfd069208396f82fa0d897832a85891fc7af46 dfae61fd3f0a57f9cf1debb2df57c123128e7f9712442132342dad21042d6583 3c0ead6d37bca83d450cf7ca7f656e0516c00c879d2512f1f3eddef322a56a49 6fea46a6a331eb0f2d88d287edde376c077d41fd9683f874ba2cfd0ac76468cf 2993d0ead8f5982cb168ed06209361677e8eb770dd6a2854aa24903312996ca5 836193a5879e41c3b8185ae3210a21b124c6dfa477248a0cc7d728aef181c08c a54f669299536eb7560caa948d48c4ebcc984757d5fecbc5266e1cb50a27d1b9 0e6c60dc52cf17a0ebb33cd6c4ded0c57eaabbd83a0e9812f7b04a5f47d64a62 8d8a164c21faf495760a020348e883997988016c931e64bce9a6c7ceb500d377 d6015ac775006e5d54b74c92678e30f66edb4596e87bcf82555c48d6fe299aba 307a34452927790a3296ee6136b7ef18c2efa938648ba907052da1b5a6844e90 83f5520aa57166502be136e2616c98916e547ee952dfe448a353cb7b9e587a6e ccc4616eab2dafba25000d31f8ecda5e493a9829fcedbc400d8a81102638040f 31 3dd3e038981ad406f10244c35a9ff4a3717b65ec8b936c8c34ad6947767b7c070b748222de979f599ffc3ca54b8a332123cfb7a0695de6817f6a083dc62be402de5c9a8a234deca536f802a978a93996cf7ca22734e2c855eae62ebcca14a3041ddfca4b798f105f88f6498492358ce560ec5d412d0b073a2423bb451df43606fea10b2b42ddca157ef4073e3646af320f1c82dd3e548d6a03b6c65fd678510a2169f657d67c759b58b19374e837f4e775d531b37b4b24206320aafe1b854304da76d85948e65f4cd1802a373c8e0d53c2f47a9698b936db3ac35ad59a84ad04b5cd557acd35a4532ebee896acefad53bb91ec2e5bf72d29b616de4b01210e019965cd577c4dcd24066276304b325b5a46606db3b1a80a66297dea8a7bd99009e041b4d19584058eab3401e45fa62d952639d94964b822b9c47c400498481a07e5e67b56545b598b0a58a7da27323b807b580a0331046057a863c495aaaad40a5a1eb13384fead3107d5d2325511dca8c448e44a93ddf1df024962ca6bc02d0fe400342164b57c8bb06007df883f900247c9440a284d4ec66e31c98af4e3440182994dffaf7fc1d2cbe94e09d02f4a74f0a5d9ff2c9804a1d34e47fade341607c5ecd1f8f01918d10b411cffa3b29216f21177c8bbf339857cbed136b6cfd20d8db0043ffb551821b3cf168455c51939ea784004689d6ca17e6ff87f26ebc809ed74ff82329a98bd670b5261168a14b781847ee873acfb60da960b42f447d106a3835b7158a3e9fe4f11d8ce089067935923c971fba9f2dd1eec428ac4310d00d3f22d1203ea82213c16400b5fc7d5839144307b25cae31b9c154b3b2609d70adfd3d89648ad072967433852c44c07a507bd09bf351f42a21626452616942f05f42cc02cb8416d2719d808f369fb728ad59504433a4777217219ce100159d502755687093e1475ed224485609ab4e96e9b92ed0d2693487dd265a58e723dda01dae8e5a1ab9f802fd1566468da6c5019a4c942a13cc6f5002621e35fc90e50042d4064299f01c973f585c6554bb5f5f7911c86528943af1684364bdd53f8340fd130186f3233f72ef1fd45e5088b59739d230afa29b2f34a24b0c8b1a06dc10d43fcc3db91c117bce99f54247afc218e7cb7ffdb148090e613e0b0a7d9236900e3da11d242d4ce4171a1d3f33faf6b315f38868e58295f553f7f2f87fc65f007e8a2add5c0549d88b6ef6a93afaea8967691040065091f7b38a4edbd7aa0e904d4d6e02d3b51a9a9f7fef8a13bb7696b1a81439bab9a1b27da30da4c9219060a01a0710405814155cfb2a01f2caa8f12ac2748c1691edcf1a03bb4e45e7add0d1d0b44e4e06af38da1f998b55b495d4c4cf7c29e55669822cd16619c64fda60176af6919fea49781c8205210ca3d7adc2283fa8623971638b431a615a0478d0f486c8bd6c56ec9121343139d363c0218f4c2b8d74ac24465c612c13517095703fc3070e677443bcc3c0f4dd9b3ff2d04e62dc955254af9540f0156b023fa9e0f8e8338fd856c3c0fb8530c836e643fecaf501f4acf5c68f3e3035814be98a00dae0b01fe02fe0f2ea04142784444c7769a77299fc3d3b634cd130a257b31200bcef08af0c98cd22ff5c5b1cee30febf62e4bc72e02ce2a3c4a618ddd7c776302c18437e403ba08a4adeb43acf06e806568b06eba4bdbfc81571a1c905fb2db0b90d6c9a373cc9d211bc5075ffcd0f3e9630bb95fad48907bb992b2df641cac003e096fc2d74ff87f7e92bb33cfe45f62635a848830fa42ba62859163d4b1390982e981ae5b12c2bba168800b967b1aa7c09593b4b9daa2d1872c21c0ec8438038e86322d828c6ab0b8b32ae5ce7be0cfa64a97c421a16b6e2970579cc045620fc3c135eb0d8b613f54e03fef8f26cf1df308854a9152ad17c175a3e645c59e0fdef6c6319b6eaccbf5fa5625fe8813905c196a61e6295bf6368d7b7e3909f80b3c5b705c107627e2159cbf4e24105690ef7e4a76148ffe79d671566604b4ed0e75479e2a3407da9172ecf637c6f6e940cc2558246336250ed485fb859286120e18d8f3c7c42e531df6c223adf732b43277b3bc2d1d97f3e89b00f6466242020cfcbc9f3eb1448b9d064cb6a78bc783e585646de087d20a38c0f52933d21a6d0076be1bd3189af0870b85856759a0cec4d12e56cea4376e41e75293183ffe670eccffaef5f875c4c99255fbf530af2df360ea5125c049ee205ec0e3c199c59c0c1a35474e7086f022d2a2e7a652d8cf6a5c2fb53dedf77c15b4a43e11c0d6ca0ae54e46a92fdc0ce9729fb596e3e064ad38be5fef042920b492a502b8b63ece097923be00922ff0ac7b6019879ed247b19e63fae2abc441d647f25eae3891bc039310a4f1b7453bf4812cb86083db704fe970a5915c08805dbef1781c945f740072e7a4a2e03936b721d9deecf18e02da1a721d30d7c980b0975265244a37af0e4b0e884ffcfea083716bdc25c78aedfa0c7d41b5facdfd2a1de21af751c92a02d939b102a8d8df9fdc4bad935e95393d9b658cb9309dae4c91b09d0ce2a59b005f5f5fc060fbbffd6d1102d2c4d9fd695f217fa04c2a44f9e1fbed6ae36a6b0645b0ab7d7c22830af3747ee48e76fedeabe8b41b2471d1fa2e634f8388b59306baee512fa2a289bd4bc6dd2655c29abbc0139fa72f3ea4256a4acab2ab06c60c2c7883e7aa26204f87904de2d1c8337434f0083c129cf303ccc318d3b1dd7e000967d5cfea98f162f8f4018ecc190f84488d469603afbbb07271270561920c041927d4bc6ef88a3b0befa00c9a660281d63fd0ce3ad015bf0b86ff572ecb680a48be958cc2c1a4690e3cd74e42928e05413b9604dfbb452041cb08f4691ff10e981dbfdc5249dc0d2dfb40bb8685e63a27eac0a8c849852ba999295cc6369a0de181a79ad745636217251fad948c8f5521e85f1b7998e88f8167b4bfe060d502ff155ec5583fe7969df136c74e3d8f315f66e69eed51baf0ffce59977abcfc035b46109093024f5488bbcb7e13dcfc09592c6925bab32e87d596d1f3158f780b49672a7cf2c6bb54e7c8de272a6c3a56e312a002551975632d17117530174f09d1a60441a5ad9520a33ec24a7e4c3321fc2bb6102565371c4ee1a2e9e8d8810eb7743165db86c9960050f21b08753f63b68bc71507d18cd5360a6bbfa5b56f0ffd07a1d36cd066bfb0d9fbd500527c14127e36d29c2e99285b49d70d8682bd0892008db552114e0a3e51b31365d1774a5db044dea943610e2425b250abecb20b4f29bc93a0cada956eb5d9311d3e88dffb352d6b2bda4129140547699357a4020e97756a72288de85451fe4045bfb0666eb23b6d2a8710e566daf68aa814240f68b36c7480500784378e03cfadd3627470440bcb12aef642caa359c079647c04f76654d05c47812fc8ad7b9b14004d36190fc09c9cbcbdaa955834a58790b506b5c6ecae68f99f50bbb9b3a4d734bec7c09398c3a9d3264728e3503de0e3a50a6308df7055ac6b9a214b08506847608bde8f21e0b98bb8f664b849aa1fa206080d4251f66c9466997702564635da32b6a0a86e2fad189c56e687d17727d7ce0be264d43eb8cd05cb4c68433c832f40b160056409ba445b6b041824e98cdc7d069fb5f64695bae457f3c3adeec934e49a28fcac905ab7d8112a665ef6fe2e910f029db39f2ca48809bef56b8e0a52f290974999cbc88ea543e87c4bc2130d320aa84d72f0343a1ec7f592dfafd3dd0733aa209cb4446f6cc0e9124448d38fc40d5d877845b6cc24263d829dd7e01eed02078d81aa95611dd07c2c6e6b48aa5e0be1c2364eb6d2446de63b947f3f091bc48f2ec86b463dc7c9a331c79d0c23540e2c076ea46a739c7b8a3c2ec3484bfac82ee0df9c62f60d8996cb0dee83c0220ee54632928c7db75036b24c9285408d5ac065da69db304ed30bd29c68bb8a41030b7af12878b19b7e6990512e24dcafa11c1955539b6691b42380a87be03e26033fe95445e26a095f30e7ecaeb8744f89b5bdcad600ef2ce7e3029d9fa6c9e90d86ea9a4bdd61a7d3cdf7dbaa4552ea350c0d3fb071d0324d6b699e73163d37041e4b7a60c29f5744f422f433da5e38e084fc74b836a1c7c304519150105c9e0e002ec6491e671409a7a697abff5ad58b91c709a9f77cc49e378dec4e3a4dcb03087790726c8e24e930e1d9e48f122a055acb5ac5217f00d00cd8e1086aa04f0b38909fbacb50de71f8408632ac66d5e3f840143be0790a7d4d1ffff925234d041447802b03c8e15de3e4757254718e203b5526e13f19c23d84f9a8fe6ed107068d2ce2aca3f00193c127caf8fa38bd90235d292e303e8ad92068c8ffd302f50a70b30b91ab281fc548f2d1dc9c15cfd0d178ea865a3673af91d94949cd2a2c048adfde036778326da9b035975383666d5530c6b6db2558362add31dd6dbcc1004d74c7e1917724f95fdf53be1435aa337833cd54aff7a82af3fb1c50e832360c3f39bf1061acf1cfe1f1314a025ccd0ea69800dae6bbbc10ec1da2c0b7a6fb08b5189ccdd1eace9c140d068a7a002cdb72a0b437ae76025faf96b3178ce0760dcd04e890cbe63c6510b3f1338e0cd5278b80ac79623cb2bd9cd4d71c2df7120a2fa8065ce116de285567269bfaf2917906c5d29f1a9e04b0c22c8397beefe304fdc5fae64c8ec3e05687b0811f05a172493cdbac717c6c64aa70273ea4f4bc03bd2e22e8bb01c28815a99c126cd42b2d8bd39d432a0531f2ba59b6975d01370c091445e6a1870bbbc2cedec27416310389d96f89c89425338300c82e1080ce01ed29289148677fe314202d5d0d66d94d8fc10404f51f9adecccf42c15f30cf0e646434540aed01652cc9cb4052c161213e1c943a6ce9e5e817267c59952fd60c01407a593c64a2fdddc7360ce1d7b207f1372a3a1a092c43ff0f95269205510d27c41d2dc65dd32b8556bea3e4fb776ab030426aa827bddd7068d184ff89c20cf775c917f10e2aaa908dd0e31f6d21308ce0363ab41633dd3785d679a8b22b04047f8081a0b83b83a9c4e56b8b9dd5a204956ad312ccc2e0575d62ddcfb14704df75d4fc2c3ab020ece41eb995e33d0d911d8e3017fc0d0615b7bd3308385e0df6612e90936221e0600e1f5039e057cbbd4c86261566e23f4aac3a53fc8ecd080ffb80d2a86631bc31e00fb706e9a807d83a137f7a4bfda68784302b1017d6088095c3702d772e6e46cceaaeb87d1eeb2fedc4c7a3a20d3b68117cebafb94806b0269691b83c9648aec5564cb6aa1470690f3ba137a1407366b6bc70f660940ad91035d810de81c2ce73d8f676d4c63670fa44c4ed4d8a6a843b2de530fb4b0cd556e55126e3feb040dcc2ceec7fd792cab33d2693e95d1ce1078502f9bc7309583bde1b0a8bc4a7b9834e37fb07c491704638b5517295a141ee8786cfa32106b553381db86c18019c0fab929343e971ad9a0fa9090bec465a3b55423f87ba0a697cbe6ceee2dacbd20dc64b1dbebc8b0e4704b2b8ebf8ead250587c101be00f9a1924979ec6358a8fa2d9ed91c80a069caf2d34ffe9e4f75d697a2f337c9f037a05925109ee8b20c7a3ff85e2069383e0761d136002a8a1eeca7f8da4e0c102ff031789b87292ca24c2dbcc7f590f03db3c3e6da30b7527db6a37fc76821a06b633488a69151f46e70f4c517b52d0200079b109516cd35e65dade2e85fa690813827f67548be49fe583d871465258f76e329608c4c6f0a478be60ac8c1adb0167c90a324b04603fde4562af0b11c75512508477c91d8a3a524b09ac91c6f90d603f2df56d9befce761d05328972345d925069709da3e9df7043891831f1f80782ec30c8ff3d2b8b75d7961fee3c1c5a7c6ea71bc63192b7d556d672c1e9f309262f60ea9dcf5f3ef4dd991aa10cfe8781bae2cc99571d3f8705ddaeda9cad0b8060e8b77302ecf7335cb00c24092f4911e8c60a9003b87e0b90e09a3a53ae0dbc29e83a4cbf01b574a0204ae934797fb0ba08afbcd30a6d66a270efdb42f10a48cdf07ece1a42e3f8899fbb723fa13fefb826140775320a4736e51989b68f003697a6efeaaff2e0e2d417e52324dd8faf2ccd4699a5ed8789bd9851a094860de31e40931c86cd88e0af74d2b61ae5d9b52bf123c10b755b3bf072f80c674e0305ff920ccd7c86def905978b0819c16afd6574b58de32c547c0e56ed46b3a70c8938e5c75d98cf15629b9a5691cab6258d9fbac4328f99e038696fa606a3260c064a59da90298935c6aea63d8128996bdf8ebe361ae3847c0eb52d7b00934d0d4d84b3f632b056331e33ecf887595c7814b5d57a01d399854766e06cd3970f0e729468bdee213c278addd34c0a343ea4ba71874125d2969e0e687f7495ce210243f46cf481b80ad2d3740a37f01c4e9223647c3b97b1046b8285a4bff6d0ba043b2d0641279bc329114a34b4fc3e936f2bf7f8f32d9aa0f3580f813c92c5de07b1c472fa265fb2502b64ddb6dd3b9ca1d708a8760aa8319257465af4e66f9d02355c6566a7d88ff0a2945f73028446120e94eb68509d141b8e16af4c217d5301cbb735bc43d258b2868cb0ac0a9c8b6c23b7ba0844e6cb175266b2baac89790db53c1eee32531b00977f1b11af67d5a3d862873b14f1e5742ea62b96879a500d1af2b45e70aa864ca4d65faeee0dfe5c0bd17304e1024820801f5ec809f12d013023d897737ac5f20be46c5db8c556d9f6ab9e7d3081fc253c87d0a8c8439d0bf5b0606c784208014ba3e6f2b29e887bcb838dc623bd7b5da9e11160414a6e0fc1739bc3ffff85decd47246ac81bd58b29c80f8e5e13603e1b811775667fa40d2a8db82af5ecde2ccca245d3a1b9af89159100aa79a50d60ecaa09e449eb12019f05a2908098d53e84ac63417d4a612028c15ca212d7a1876db4f013b58206087ca805f797005b3f583af5265a51e62d454309187543d98ac57d36fc3ada7c0623e57c7be802820daa7ba1fd0d2145143a7fa3e44a794e8586fba082d2b2c70b6734a5531f4b290e7883dfa882b0b8c3c5b1accfdb069687be70c048a779da0ba76e632331d468ce5b1c345cb379f19bc5372cbe147c3d14960c8d4a7c3c43052a192582ba6c1c83cfc4e2df94ab0b2daa8df5a35d3f47f71d85cad94ea4620a8c58b6fde7300128898aca3eaa0c0fa6f2d2fbdeae7d3ca038aff52d5f153a0bfb3cb41c147d6e6d5a466c75f5ac8b8366f6ea7638f0e37cfbfe1350e9c6b70f215080042639f32f65556325b9b317afa4b7943839b2b3981377f389d343560823f40044968997a8092068ceb4508dcd3722cd35ae18d5a09dd0e832d4b2ec009ad9dbc5af31e83c4957d4b3d1d10fe717b5cba0f88a626fda800fb65f931b040c88e42052d9d78996c399e42bf73afe8146f20782c56ffa3463e2f5c0c417009e27615d275116468b845c6e757710fc2a00a06ad1b1e69ec1b6bc935f2a160ff67d0cda52d4743c4b9c53dea12c22d778ad303ab055e4cc829742c370e84f08f97b82c153986626156737ef4de57ec540003faab46c57a6c5931e88ec81b30bea775f5ba94e981eb45ff42da8b3d12c55616f9273906b7e40002df19d025901594a96efeb115c8ef194088777e233670a36159005ab680e613aeb7e9d2a8c061e87ace2e21ff03d559fc9269dfb0f4ec2237661eaeb4ad354ea7585cbe9a3099d11b9ace359e06bcb492da9ef68a38ec0cb02935a396f21c8f3a067d81c4a0eb0fd6c227917868bdbb5b1fd81da13ccb6b88e9f4039252f0f2e5e1bc5bd700379635d6b937b6017f237c1c3f7da8899fd6a53bb923d8aeb398aab3ffa9b6709445663a31939b5860e900b49a41cb697da78d22e2d6ce318ff2ae7fde51cad0688c944667ed9fda64bbc773a4de64a22e917e3da586158042589d972a164830f350194bcd3307833b2ea13655ca55fdc35b08cc9b00c1f31dd0d176aa43b2e0c192ff766bc837953a2518a92441c560f1e80d429f7050a2d181d6be28c58d904d675c34a1b9e0907d5ebb7e48e087a94e0ba994b4616bcf147237c09846f490bcc432aaaa184202b9492d6b87e3c5e0ce8e9a05c6a8d997b4202581ac2156a033d3dea34314ee05d96099c637f42902c250a2b47dcae7617d6e622c37dde40015882219fff51b55e65037cbeadd1152da01696c9eaf4172308770ac2aae8930c5a3796d2838d8cb9c834c08d3d0c64519fddcb1382ae0c5d94da65f9808e29092cba10cc5d6bd316e3924f1e2f0da066f3c5b9d93d24737600515a0f559f0a0355aa582e7eff3cb34c5856ec7d15ff34d9812613c4ce65f26bd81b38ba8a120f35523c5acc61ffce273b3e77021d516c49d0901a7ca71e1897784037d1f489022daeaa0564af08089a8372d6aa98e2a9f9fb21b7e8677df8556ce50e5fdb2f07aa5c0e8ea1ba77721f4e8dc25c226b68377ebe5158923aae2c0cb96de616f909b045f0887bb6b6da1b4b3e63cb3d8031b1549dbf445f8799a0c31a27b68fbb0f892c7b2127cdf531bee1e847ebba8274699fe4186aa40d9ae4a4df01b98a140e61eb1fe67643335ebf7d14473fdf371f640d084c4ff7beafd07f53b875b86e0c01a101ba5810e91a91c8caaed620e9e850bc7fe66393e526ec8b27f55c0fb303728a22a7ae843d876b33175a56b0538b5c52fa38792830ced62e80c48a2325018628eb3136846b78de158eafa6089737cf7ef58d8a0c8b3165d285372cd61c0f5808e8a375462a788bed713e58024f3225dc22f6f0424b52e7e00788f2b71d0cfbb591e79eed4f4af2735d781bba8ab1be691dbe29b921aefd57eeb645231c08b735efd3482f95512d79e2fbe7783db856eed2be68ee5ac20dc86a698863300e25e89c7b98228a5faeca342b36563e6b5a71396eec5d1d64b50cc8432be268085994d8bdf7d1888eb677b2e1699f293c7d8ccc60370167c9d44d966193966c0660f16a01e69683c7c69bcd39772790274dd51eddbc2aa664d76f88401545470e9ce83b21bf4df4d32bf210214e37c36c2b1ace13a33074aa6b817881c62c460167fc53e825644e12c7ea4ab21ab4e274361919032c106071694cbd8360e26403d029c88396e3948f55bd5f24713138c8d580a373e46b4fb48fc1e7916b6642099c15899e99256f6f37c04f67a5bc484d5587aec7421df75a1116b290d94eeb07213c876177c93055bbb9222cc60f3e81ed4f34f6913d6e9558a9dfd4367701091192135f4113068be412fd6b9dafb0c08b84ff5d42f59976dad440d519afdc02eaa12f01430e3d585ff5b8a0d7f2b40742a9448532e88666afb0384b2e80ba0894fdaaeadc61809a84956853f9475191c016b87f3269e76978a0a279a5f912096c588d599031257ff85e6c7c4b1db934c54c8b47f25d407f43968b318ffa4706e01bad4e35570badecfba3ed7d908ec70a36c4b90dc6645a1766dbeac37c5a0b6013593a54d7e9e3bc9fdc23c892051c37d7bb47bef8209bd0a0145cc30eda0152529e09c5dd125ad1839fe6a4ba1a4ffae9ea53bbaf6de0e55bd1212475fe08c7cfefdd3b27fa566bd87bbcbaae31d6fef58e9a6620ee69618951fe5dba630cc52aff88d01a7cd717ff769967b920b0d108fad52090237ddc9fbf78def1c80e1507fe6914b62fc72801777e2754a36dd0e8b1440b6f1363804ea224f8b814078416d9c921b2f8bdffad7970495162cc5aa55736c8ac9a285d80e28e73bea40276e15d59e61f89b1376aff4ea10041baa985ce0b693c8a6d57953210cf8571021f924b42f88a166b1b37f212b72e9f37d7226cc833aee2755fd7aab0755fb90bf2296858992859bd63cbf89123e8e9bfba1e2d1f6a0baca857c7149ab6e14e002c51cf03b4c7e45d42eff1951d0e76d5094a3b8b826b66c5df667e442f0d4b01721df2d3cbf162fc838bc5bd93ef4248b4ecad7b47b7c8eee10867f1ea9fcd02935cdd5bd44ae02bf2d8d9f1a47f65d58f6a6216df30adbc390397ce29efba08edc1802d3873ffefbe2e5dd844ce105219a4d420a18e3b577b7a08f1f4b4240334ac87d305158f81013dc8f08c36b8c8ae0bf083bc9ac69e4bb8063577b32e0d3c54e26ee3c99ec3f5937ce97bdb586d9398c45a1f068d1b366cdd5bf8670e0edfeb9862c6a223c8a92ab58b4f05b2960e8b40974e171042e6a2829f716c2c0dcbee4e9fec95aec62e6053b207face6adac77b09c06dfe0f9859519998a3a70964a56ec333cda783c1b554a9324ff3dae92ae300b3ca929a58351fd0d33c170b0b9ba232b06f8a48493fe4942226c3b9d87f073e4c9f2ceb8379a2028acbd60b12b1bb84e944dbe5ba588c107f48b28226883263cc3cbe9b1c26d88bed34c50db565710af85c128017f53b20bda89c5eee04e3047980f250e042908e33a0360d98b75208380e3c39d004d6ae4311c0f0ffb43e3df07f009124cfa5035e67480aaf693de8ddedcf0ebe180496a420313ac4ccd009d3d69d5491dbac480610b50af3fa717202f6df3d57a3de77cd7f30e602b947db479a2b105cfd11b6999d790b3275c94679367de07f669afa644f3efbd7e5ed82202dbe297bc36cdccc6bb50d1588fb08db36edac37b512aa418895e6d3b4bc4fd0ef1a68654eb3a3291bdf073f2cd7b1d405636d1f3b31f78e73f2b2c8af399bb83f9634a17d2f12fd76160ac844e3fc573568ab87083b4345b9c62f112ead764a73170032fe83ec0247240349d8d4d93ae69872c7903cee05e251c47d3c9eaefcdf7db29a1eef1a2c7b950f4d30c2b6d22acf156addf1419b8a650f118ab9812803cb838dc962bfe588c70c057f40612a5d06de2d483a46bace481f3fa1a3a1cddf5e5c47f79de671907a0d1765346cd23df12aed29c7cb15901df0e83e0126a765e3e91029fd5597764306f40e06b53fce21b5c330870bdff0f75435eb2e33739aa356ad2bdacba97a85052dacfda2639f137554f5cfead1f5b20531e947942e1eb186d09c6b4bf3575c0e267f76ee54ff8e268b32b45ee62a947c33fb564dc46d5e0a66f6a53f3dfc8c048e0eb4dec0081bafed110b382662b132ce34040ff0eb6e72d19fcfcc5f86e10f02d7759a9abb246df6f9393f29d759be51990e8d94204e27e5dada6eec9fd109d5c45737e0a926daa76b6e19d44f7683bd691478238863ebf524b9a6d664550e713130e5edee177c8e3fd52c2796c20c0565b8967e095dbc104874951508410777d70e1db8982e36f31a91413053b7c3c58e2ecd0916db363d22b22ab55c200323d6745cae7c5075d7e47ccf4f832e1c01775d2ee89d55438eded25a4c11730a9ae4cd7a512f0d55fdb5e2992d2fd4a26a6a5f015c9e89f9739337d320ff850a7777ff10a35cee1f5a2e6f47e30f366e0967a67e1da23207f2e80cc6052e74096216269b8f91674acf8216bc4287c6781972e66db33a727910c39fd302531b065dd6eab59d52cd58f33ae3ddcfeceb4fd04be55fd3c87652f2a04910c9e42600c1447cb1723f5452d8d266d723a199db81430cece9bc6728e7ba40180814f70bc144aebbfbf39aa6c43f0bf4243494cedf4e0af6dc166e6ce7f18408d034770ce1263b73041c45c6680b0b38b11da26467cdda23c8fde26f062d7a2c91186106a9685c3f856c69caea19c412102d6377a7ad9f0b636f533eb5796a670920290268ad1ede809164c95bd3d2d79dad5d9942aa82616e48160d92a6ff6c49eec10be19fd81252561504f92c5f9bea3a7b48cfa74f62ceaced7be30331b59f530b04952953732cfd2aaf2997804792a251b50eb51855c7edabba366a1049a0216303765acb89a204198dd6c760ac92e10f5afac3f3efb273227dea2d7e2d59380e0fc542bc142834ba2a44118f3ec00dde7911f66e0eac8a57af47a1cdf0e192ca07 +generate_ring_signature d1d43c4063186dd4885f71463e6d3fb46461b32f7433bfbc42efa4ac38cf01fb 3cb2887cc2d19803f9c3afd3b6b50ebc61e36eb9d9f3a46c7339db07cf9364b7 4 e17f982586463a7cf5e56a5af610d5be7d70c4a32ead60624d287856354df2b1 d05ab1209d2cc11c2cf85d494bccb624898d93c77e29db8d9e7f8f638adb1079 a9ec00025159f844792f648a711f783901daf7ca06578afc6943002a11c03954 3994d4f2de884a0fbac706671e3e2730091060d3c5f945046e0e9005f450e2af 8418c29c9fbe96e534ebb824d80ec688db0aee8ca814389a8352d0d1d483fd0e 3 7de41347ec741568afb9e8d2674d6807372e46df0ccc319cee05a1adc7075a06b82c6eda62a13eac9e9662be4759f797f04f4b915ec3202938fffe2a41aa0e0c07473ceef32c8d256ce7e6a9a2febf400c7cdf04ae4f97d78f9ca00ca64d3a03c8795cae3e212ba8fea7ee9bb409384d3c9cbb8102f1d2bcc5c51f5d0f90770dafa39748117251c53a8733fd3d7eab7702005ccd4b595757cd489d83e4fb45057eaab8503f28a219ffc4a65097c310145ff5c997a231085b966976cdd8f1520ae3f3de269c4ca3be8083566595e8de6047f8210f28c82e965b841b431ebfbf0e850167a4733ff6f4083b9d76d1ab7393f1ed502a0bc8b4606d75b6fe851a2601 +generate_ring_signature 5c28c998d9c607e4823acbdfe47f8aeecb61b7286e6de0be0616213130a346b9 3805d3eceacd8c334a9321637e8e36e28bc7155821c99a21c873d878e4010c50 33 288f3f945fe961994eceb149f6ea0b1a6b6e3377c399ccdf423d39700ca1cc96 548e24ba81456d5e03e68b130b4455f2b785d80a1b2cbb2d8734d0c58a27e995 1937beb88a54f63d85f61c2e87a581848e2dfac5bfabdc02acc0614785286332 57dd3d734f97f671a87653351ff17cb79b26b7cef26625396874d9e39869656c 08989751f352d187045855632d188425d096a9bd2d174a60d79437284c6e04e8 40d48221d362637fa9a87a03d94d716551f4a9430dba949e7557f27a7336db72 007c55fd3c4e4a2cc56479fca4656f539b5aca16cfcb846d614e4d5dc6cbc5bf f9b6794617e44a235fe86f34ce26c952e15aa91599460d89b9846c6b621a3a66 df4602835cf65181ce30cec60e78db2937d8db1c8526f4b07c9d179d5e4f8033 bedf6fb700b531f02c27d75e3b16f89f5c7b4831a691e0456b362abb9be683f6 44a2aabb740851c42ca5b11cc8ac048906ae067e0a4c7c5acdee9772b40de8c9 3a41cdf3f2ef11d9d9a03519fa77798785d52fb882c7d7df49c0003b1d74760f 699ea2850c3511973b4909fdd408f0c731fe196e8de15502f8f4afb385a2e313 a25986cc3488d043eed7f51d65193560fbcc9de3fb9800a2cc3bd1b82153f95b 1fbdab731056b7a5be73d73ac8e8b0f033ba2f85d1f6027fd33f0c1c8ff9f358 bcf02baa01536267e8abad75778762aad083f1d7bcee65e371bad564b6abdf00 9415f8899e41257439c4f171d6b681f4d6f96a1811e0f2a8af0b01737de6329b e6c9aecf8bcb68f5f631a7c72c46cb111473e1888bfa8f06e5962277e8c5c969 9ce6653980273f6a6b30e6a222c63e8762f4017d6827db3d1e81cfe04f5a6e93 48ea5e25095b10653f6f8c4ee7c65630aa6134737261f9b4ff4c42b60393b584 bf4c90ad8313e1bf373d1d4f3a71a7a5be8e320dfbf69712e687353efcced7a9 b066d84da0a685718e10609310006b7da1944685f1fbde046d537c3515fad93c 2b4eeab67e38b7e382e588d1dfd497be36982eedfa2d9d81f361e3030d1c9f09 601c1c8a74779ea848ea07bcf22d8d875365d3fff96ff33835108ffb025720e8 9aab5224775e7135ee375b867873a21a02512397e0a4b1eb827ff18ff88f049e 013eafab4412b55ea9d17f624a42f3b8a95b1416bb0f99984ea7da3f61f1fa0d 9a234eaef5d84f012fd20b60eb2b0c7bcc5b74cde5b1679aca8c885743329a80 db14f6323943eddf9a4ce8a524aa851c5a6fcd37e1cd54f25b7052d2750f02eb 509ee1708cb735b64ec30a91a4953b9c4d9a864fd4d2f98f03905cdf5f245e4d 47c1438e558ff846021d431c09387bcf5805335916e7c58b3c2aa65057376691 f43cc477d6d90bfc25fe0bb34dba57e35f54bceab28596c5a2b41d76a69bd843 159c3b282a1a5ea5f19841eb0c77d5e5f79c04270a65ee6c45732ea290f36887 8c2b1ac738b21c16d8f16a2909792febe66f6d94f1ff45e035d75633c6985d97 6948b535f7856e3eb2c1e2970281b43a3c71f83248836428bbe69f11cd121601 24 303cff70e434352daf505c1f3d4526118900a13d55e448c00b244473b779570f62a14b3cf237d91177ddff1099ef175cbdb2985d45d8ab17de6543eda3726607020f2784b76dddbf78ae4f1187e05161666cdb4a707453ac6d7b54ca9bc4b3089ab90653e17c75c6afe1b22f6e95462cc9848c862e10b461a774ee39e65906046d02cf783bcc0213899d1ce4489e5c850a1a6cde91261d8a87b509faabb2ad09457041b8fec38566f061bf3ee0e08b6f89e86c7b54f3204647d2cda042d53801528988d5ad6a99ff52972330a7b610b3984dee94bb074c544902c072ee6ab908c0aad0e965e3f42d27e48861588adf850ef6f84c4e3cf9a794fc0a06f99bcb0587bc68b475bbc4a5b4125d562aab51d2d4718e57e6dd48d6f2fd467f7f780e0975faad80f60b3d8a1e23f2376741f0f1a9628c5f691821eeb1890704c1a2e407bf0d3efc592eaa2b6ca2542ef01c31349bd59a7275e3685edd9e297683b7d503d04f6405fbabccecfd3a3e7c401342d70d7100801e9eb7a436f20388f2cf8300bf261778f4f88b4ad165ec0e9c3cd5b97b4dac7b34479e6fcd728398fec1e8051c479f93e91a762af4e8df5e514e84e32b60c81265fa961c69737895800fbe004a50edb8a95c60ad44af27b9b9e35e29880bda379aad86662cf45abbaeb77d0f842de263e88f4102ac641b996c55968d30b04cf42fd416cc2eba130e260b3308fbe67ff37403c07e19b37a8d48f46879ba2b817c34b01cd260a78e62c51e2d01a9de05ddf5d3ac7c9016c91a337a6d651654eed0eb17c0bc26600eb633dff9037f5ff12fd7a0b05de9e4927dda0faa4b5ab1147305ffb89196f805106ac37901b09b0db3cc65d5f5fe60fa4cdbd9aa04b309340de7b2a7d01c61d670faed3b0fd766cac68e8de45b4ffa9abe3bbc6f9b63017dd3c17f87e0ae10036c760b290b109afe82eb271139c107554ba53aae4920038559c75c36942187750d5151580ef58ecd5b8f6cb274d7644d317624d74fb3a88c1514c233ab77dd17dd371ea906384004d03ce6a2446b30be79096dfbff5af9e74af77ac0504e7121cb6cc7ad0624d703968f7c8374dd6878415c4e33869e62554727d2a97dd48e8def1be62f0cced90e00a0130fce051447162cb232c9e215e65d324162085929d6851b61c80997ec6666c4c3fa12f1e6cc0e62eeb9225ad22fe2094b000df900c8be2ce383084772b6e7a25452f1e4c60b52bafa5326557db7726e8e75880bfa0ec747ce0c0b91bb9ac8298009151e76475ed1c123599d0c8e3979fb878a16f365297a01060464784b7cf9fffe94608deccaf88035a068beb43da0108e698be8e8823a8c400d74e8d743106a93bab634d61183065cfc91213627b201537b47c614e02b3901067f616229db313065a50d41d8909d862ec764ddc0f263c4093868b5244edad805a2196791a436b87819ba8a5122a1f4fbb29b7b4c71cb493fd69198bce5a6960b9bb3d53f344a075861a7fbefb21542f497cba9128d892c25806d3ec6409ba40f69a1cb76d0529af901607bd23e9f2a35eb0811a05acd86da2bc7bbba99dd63067ee99a6b8f6e0d6a7f549aa945344b9c87a1e1b1901d1cd76842fafd3b6d4d0222f557e6a7658af587a940908ad9b775aa0c7056a4775743859a2f170c04540be2d5d4061b5eceb3495af4105d614785317e442a052226d014e18f56789a1408efeff2f9966ccb3a9e6f02936cf91195112acf5b5c1b4e7f591ad3d7b852b3032c733ee66da1175a81d6ad42c49e4b87bb8f4e000502fb0c709c1bb5c9ba210f61b3537b7215f795774cdcb2f207d3870b5cd59d937f5662ee3babd8a6341d0ae2a5f3b9e3f62814717b24735036827de36fecb78716d1bada8e75663c7af902e20d1f54f648c2b9414135091838282c63b5b2c431db0583ad3efd55c7d2b50d2cccdec4661ef812f936f2e01a45a2fb11f7661b6548daceb55f0531b283610bd665dd21720654d8f5d0f7e9f166f2f49a1e378168201649b46f397821d7ba058206962515f049689d2abe8ba8ce8cea763845684f112b15cadb7c330b498a087146ece683c8435750dbe01447bdc75e073ecc0a52d85576b7a067d794a747050fef3e57500c4f9893dc2e2366a0fa93a99deecc272ad764452174d4b15b870bd85547a39ac0025da5bd5888de0f5acb558866de49a688ab948abfe6b4a4770d6e21a1632126266de64f97483c047e112e323a9cf2f77e344519b0372228850b2c14ba8fa46d512bc33c26046b73e6ea67415e0030f968d610dfdb17b538f00ff108c5de40659e2df9febce5a60c9dc32406336d866f45578220fbe740f27e0fdf267b0215076d4e3fcf04248b7bbfe484fd70db162b0b765e9268ab405034033f36cf7209512d0c9dc3782c9aa94aafd84390d972df153ccc3eae40509a910577621b4fe840db9706fb6280cb74d43432dc82d9a8482a23643af827986c670c972d57cdac5d62c1f226de782bf61a3821cf6df8dde959fbe821dacfa2408805f199abc46df195712ea923902b6a0b8a6407b8a833e5f94afc488418364e9601730c31e5db1fc84bb02cec4aa80703ca6a539629dce827fb7045d576a5479a0935b76e86576642b5b897c3aefce58728cadba94b05fa413f2a844a50ddae8006be3a93ba6c7931a044aab8754732b49cc9a1b85c46e0fc13824f28bb1377750b8dcaf01f4a7f1f6fece5be63f9a3625db0ff4156236cc17340f0e4217e2bd4069fd350464a57ec6409d4d98033e76c300da874880dd812af2419a2d7a943580a11201a2136fa8686450b50724f8def3c0f7fcbd29c4ad84c60db268c1c6e4304fce50b96d735bea955a451a0ee0205b198d9e0dd43e72ee58f58d0d72b6c370033c03650f21703f518ada2d64e9153a81c46012b8afefc99e1b6d94e7e97bd08950f3c619c2f07c26b72ab474335f8aae980edf7dcb954bcef9bdf061891ee03 +generate_ring_signature b95d854ae4d429289f3870f0129a9b0a94b2a8b292e8128cb95b6fd79c1330ce c63ce30019a8dae813e32710c4f3f809d95d146d79118939264cf6a66df59e33 41 14dc4f794c8f6177a4d625cd58257dd7e8ae1539b91dd4bd09f7d52c6c419470 b933a31a74e3f6ff24100d9e315b76b0ecba512145949bb6eeee099a28d5667c 3fff7932e73016c1dcb6704c3772cc1df0afb1f72f421d967e416f7988364991 2d41a8c91c43588d146aae10459fe99a881d3591b14342f0b85e081e8b25c0df c4d28bd8120b8a0912640adac07350d78f65e30e7a3b93e26d94119bf40b49ce 312d93ed3ced5134a102a2d46e9bd7660c43356c7b4ef478cf49ef20dbda5645 e0b759d95492fca5a0e26804dd3687ff44ca23f84d27fee38c7e507efce9d240 7a7b9ba12a2f3c9cc1a7d1270e1132899870a4551b2afb77fe18cefff3e941b4 c9eec4bf1cc6d8e48179ad79324c910937844256bf0ad6dacabeac8ef3f47744 2b2d2e7bcb5fbbbc84bf84c87e8697e8047c6577edfbd75119c6f410ffa28597 cb75658a5bd42b75bfab046fba9dd8d1ea6d8f5f3d6a4e83f7b9dbad898ec38b 22a65902152dc6828ce0c71a3f07884bce1bce29f811acc7d289b93ec585759d 7266e8249404d64e334f46f8173840920941c081c202a741eca7b0dc036568e1 1e483fa305ceed15aaf0d5b78951b7a267756634d2bafac5f859a472f777f6f9 c26433d4fc037217563f90cd8f5dd27da327f378aa85c450a449cf87fec39ef6 e4800d3aeb44e99f93f241f09297ffcc8dc5a8e74d4c6653685e47f6e3ea1dad feab79a3eba8e982e5f25c3f362eff1c9ff85e0c444e339c9ee9cfb507da5d59 5c266b32ace93332a6f38c9d74e7bdf5c48d8e3231054ab6b58d8a446636ac33 44fab5a0ad46dda91bfaa3559dd8b0eec1bde0766453627d80f98a7e405a343d e2ace70071a33fe9a491ec157ba86c2412fd7d1b0594f72231b07a27ecac47f6 9a854199bcabfb53e6795d25e5f10de6d87f788ebb5aa2879682eb47ef66afd7 db992308e155612e5dd8974ee0fc7cfe14473665e16a8534ed178a2707fc48fa e1b2fcfa991cd416288a0bb4733cad6959f6e4f218c98954ce53e479a44202eb d6c557fea73ce55252b2f8025c7cb7a9dd5cfb1a8286a62f83198d674644a3e4 3249d9d10a884f651ba2639db5f711514c9b4096e498d4fd8fca3aea4ab8d51b 13a04cbfb14077432b18ac3575784740c183620a7fb34401e860382c482cef28 071f77e63d166fc87813f0e852a96373a6bae82e9b369404bbc35e559ffe529a b1294bd876dd934080686f13d1cb7c285e108e1af1a9a440c9d194d1b00ccb6c 48de523c6bbc25a81b987e9897b8ce1a5819780861e7c0b7cfb6d88a5b3989a7 447ddd1c0b506470e5704bfc602a2a0917d6dbd8cd1ce4d48ab58852a0f7c747 23022c5a6a597302917e229a85f62c0401b6ba709ca5d13b1d9d3afeab370021 8d5944402ee3c76a824cf05306fea7ee338e2b1c2400ad9bfe419dd4145c3982 84aa059e6a5d19ca6bd75ec269b7c536eb5c97813ec3c3abc7d6497b3349a139 09b818492f8114067ba5d705480af9e9610a848fb205b7b7fcf75e97bda55082 40c414e2f010155bba00c7e0385e6bccedc2115d27cabfb3e9c60f5fcb6e035d f534f1061fbb6be476ee3258261490e8bae10a2e59d40577613782a9e2ca434c 9bbf2f0775ca8913a30756856d236ef06973fd5ee53994fe698e4dce5882339a 40f9036a4ad4d22f5567ca363fcd723c19bd036926f5139eea051c0f1ec5ec77 ca96cb5b8bef5dc37898f8ee6d032ee1f364dc0c135b68b423b3dd4e9f24c292 707f7a25cfe65bf74d6fa47a32e769b753351dca2a5b50eda570c3cb0bf1839d 1821c1ba0f2843bf6c03794d9f969dd3fb23467a08b09a5096141350c8fc704d c341b880793fd0b4a955b399ef31d5ca8096e17f263f77e10b9871f31608620e 16 2f1adda9986f0b45bc57910f709b4125741e406b8c7fb314c6a6257e498c9703a031bfbb9f66b36d2a301c33076e23e35f9fe776d37d1a59757508281455630ab990735e2620fbbe70a83a1ddaf757c9fac200dac34a25e267a777bbc63b6102ca2996b88f433b70852087610b751630f0e4473b66cc057e9b503075219b3b0ebe0905b2d3387348d041e9273179bd33666f0f320232cbd15dfab6122d183700a085204fafa6a075ff69e2d1f654d2360da79c1feab5b197de1c4c08e6471c09a605e2b299ea4dc5286d8a45d7e6969ab61fc085a747577243752958370c470caa73a9359bdc0f37d8ba133417291701d806eef3c00e4f92e24850ef5962ec067ac16592a060e9c655612bbaf162dcc20459df154f3b66b5842f1cd87c914808ec4e9165e61e60be5c52ecc5ac8dc52dd08b4d7b590873b379f0b3cd3f7c760158369b54278887365a9daa596a95e06e275f5d9121d2aaaec89b0b73cffb95048c86b2b62a4b3dfb210bd87801efd7213fd953b53cd050c3dd483c6c6181ee07f638d52a8cd0930c459407a7ec32076d2afdb5dd96ff77d752c887293ef8f9069cf4eb7ef3535fc53b4b149cb56795bf10be39353f6a2b224c7b49034ff435039d2a089d479d0efb5c5675cd028206cfe556cac3d38e08d8dd5d27117f8c640af5e3e48edbe5ac5a86dc81df3ec74bb755a4aae39f6157ec33cd0928aaaa7d0f07d6d486afd43e48b75b569d64ad533e5a7752fd2abe28a8fe52bfc5244ea1034426795cdba772b9b264586878bb3c1f64eb399999b815ace45c949a25ba7709686089a3ec793cf9766d1453bf195c9ae6033526ef3f27b6a8c3b56d4922e8026b56f0d3d7ba125c55372269d5eefaf332499b20116aabdfe2338e1bb41e090c0b7764d7fe92e35aaa69f0a07aea6c762477440c285312a9bd0a8c4a6569560279d96389ea92fd0232bedf261ace36ccbc10dacb54fbd0e90d0d532b1249fa04787c19b964e9b68cf68c5d9adb78bc4d31efc000ea267b98743d95eb1fa33e0c0f99c8a9d2ee0a2b5dfd1fe54a6aeec26c3abf2547e99a3875da346dd5de8d0e12c782cb2b96d696b1af7f93446983b82f3dcb3ac04ffd6d06a65b714b3b48025bc2cfad831fcd95de8c2c30e8da980368003dc76b8ce61b96e2206def2cca054890ab161bbc036d9b20b23dadb678f7d916ae394757a9baabaa32b8110b150921254f9d13b5f6f00709e6582d8303871ae8e848355802b46a2c650956cc3301c42e464cbf81cdc5fdadf7fb86c88afeed047955d1297264e3d393358263df0739a662281f66a41cccc6044856f2239274812c2b572d099453bfaa888d8db707b9de586f90d4decee41d7afe16f2bdf21ca60b35a6ddfc7bffb01fb426bd41022669c35fcaee52385d9b830c9880ca9aa74d9b9f662a2cb0ce7a391bb2d6d408e26bdae5892aa82e630f00473107a3296a39093b44d0abee19d61a9fb94f550bbbcff5cbeb60603647908228b448b0838a1ed38890ab66a4803185788470780baa7b25246e9a59c4804b8d1ca2c5661c7d31c9e600fff4758478373144c8ee024287c2a9b47b6d1181482e11ec90330a6e633a14b093b250eabf5ac592c4300dc9ee54eff14bc9116b973bf545813f151b0c0bfc225cf7eb254cfc5f32a20a068a4a118e5d015de0fb38f034d857fa5915ef48f8e13277112818dde9703a580d786263aa3c18cd58fd3a7d768f689d1764b98d1feb6be17496249d0bd8e5910e037849a96ce1a183cf2f7e93e346629c5ff6f7b0554657c794a183fbfeda91056ceb690ce7966eabac2f2307bed49a48ee2a3c5634ddc2a3b6ec6a44e46a430917e023b205a95854b5d021f62d44a91dfa55cf94e8d8295f1d92d07228d3410a0282197fe1f24f59392b4e5a7e196ec50704a6d4f69390ba3e7d86b03629f007349e4f5e4db491f7230ba1b540025a84708a0d806772ac081be11c5dd3b2df0fdcac5d78724e349ffa5d14698a4f51a911e8e02e05d35b2cdc89c908be7cea0565ae8c51bb995b0dd34fdf682d1a9f2bf1f3ff55e1f4df5e199c9853772459051deaa42aee3546c2ab4d98b96a515e00f643f4efd25344eb063fda166a283805b62ebcf52eeb49066bbd656f34f803457029629c42fdcfb948bd7cdfa0cf2306a4ff3ec4a7da4b6c45bda8fcf525652b5bd70c3df91af9cff84e45d3eab8a9028721e6702d71fabbcc5c8b36cbb8602a10d31798d0343dc5bee33f3bb1201b02054eb1d614b4c0dc42adef21f990bf75ef26a0678236420ef6406bffb9117c025cbd615f1434688aba22c74f2da9c02441c87b8a2ed54c290fc480bec324c0061d510c6ccb4397257656856158690bdc4a723ac0f597848d38ba6bd481a4d105612abb1c48f7c368c11fffe234bde05a85619e4664ca40e6268e44ced064ee0e51447f11ed371e61cd3653c3e841ac331c12d225a164bc4ba69ff178b295cd0bbe865cc5a9f35e114f2ebd33915a8d1f4fc6a20ef444bb491127dc6caca29601d74fbf978ba18e1c1034b600fc003b4c3b8230ed4a23047e20a8405b5acac80b66d10839e1dc655d88b621b7b952845569d3dddcca56378d34f5f99d039edd03f7385fe2084e75541b2b38f1f815a680c98f67fe8ff97287fc32ab94f3adc3073ba16eab62488d543e61b518c4234f0040fe11b518a0cd0b19b96efe9ef7d80c44751cb9d0d4740464a7d56e4c7d06290c63ae1999bb7d4a79dd209f68132c015a8218d3520ce45e8930f33979b5a19c8a5b2f63b791477cafa01de229d7b105501d7fbd216516a79e2aac69a9c0d2721034c4274f526b7424a481d64af394094d7d2bde5ac33936f5f85904e25375e136b293c189572ec1521e681ceeb99a0d6d6b948674e66ac2a5c2722750afbc894a4c202598c00b24a4632fabfae0dc0c6c68cc651e4fbabf9de51b11b6a189098bad615896a2066d469fadd05ae60f0452729a08dabc59d501262ba74540b4f24b69f36ea35aac4e1c7a09d038146802714d4bdc89af10e22d6c41cc4f516c75351fcfe3d147d897c033f03be8a40b0bcd94b5a1ae74ac1c16a0afb91f12b396edbae1e6f8d1692a6d3386883fa08d037ae821196788f13eaebea734bcc736d76b7e7b45328d508521d75d3545333f036efebb20a6dc6a7917bb9205bdffb8abc8cd659cd51e1066eb553f50ab566904f0d1d50c1fd4d951b49f5e2faa6705351fe23ef31e2113bea7fcbb61f8bd760b5ed4560d6609885b3c1157441782932b1758546b92c1378a632207ad63f63a07d291d926328d8777d55fda0e5eaf848f75d89b3b9a3f542d065f7db66258560a88ff1aea9a4fa6abb6e51394692b0f7cbe30c965d4b16127692d2645b31a1e0ab544c58f9a6af2eb97fe1a394b2b93ce082023beb5cb937aeef650bd32244f0fbf76a8df888e43106cc06403251f8f1689f92446c02b3d4a9170c6089ae8790a54a30f93d8204be7b839082e49c8260d21a47fa7a9bd7bbaa2d84257acdd4b005748a947b44ab7fd9648739a788e925aeb7f98120a6a376b5fc17d05a93d940473d680c869dacd7d5a4be148c120cc9829728ce95477725e37fbad316d432600adfe9f3a095bf6bb422e4c2c51d3491f24503e94cd7a12db5061774184e6e80e8c27df5e6d3194b75e86189a829dc6673c9baf28e5d26b89bca1628e4bfbb208 +generate_ring_signature aee8052eadefa86bd38bacec33b1c6f15e63f6c85d34ab52c9a0cc7b1b9cd5ff cd24f0bab276f93219b382343728216654a69aea9400c686ea065853a120691b 5 ac61ddba86ff0811400c20d53d553bac6abecb41b16908e725de041e688b66ce ade4a4aaf7feb105709c49e45c2600de99bf75cfd6454e6de09694525379d209 db4b43ae97c85318508c43f8864b8b8153c51e8800182c561895dbf8be08adfe 625f9710ba704fe3c6238781bfd1e3f12ea69124b5f6a3fadd60b191323d9c8e eb3234c400650f9e75e0e29b6a199ac07a5b8746548d37072f8dd53af3a38e3d bc5e6e9b5b9b62920c0b257e870ba69eb059a73435811c34930740e4a0f8610c 3 894d62e57eb9fe91f1168adbc469cb6ea1689e6e78f2efae6c0ff3eaf068bb00ba82e5962cb799fa4077e7047a47bbf4e5de482ceb1886e804c957a9dfe79a09539d4c3ed77a5404da4ac2625c6fe4134b8fe00d7ea4d0bb26ad7edbb7567d0c0f11cfafeaa7a9876b35fcb085f75055eddc1812758b8d56e28003853b7afd069b0c09ea17d656445a4e5bcb0aab9d606d0e7b09dac44c35919f72be06578a025c89410dfe242f4e363e56af7fbc23177918168b8df210933bc7cb1dc6265202400c866a7721ed41bd062a2f5fb56233ba3c4d6a36f1da80df09c78c974d8705b8d7c86578d53437bce3e6058b33df2835d340cd065c3b0323736d472c00ba0fca4bb02c4c7234572901b3f62ea7bcd9c1c2fcf134c32d28d4169393ef538b00f6fe7b109ae61adbebaf1d2357f4ef1f6c4e48c63b0c90e87c11905df6da340a +generate_ring_signature a64cc44723724779afff68179682d105c365761822e3cddfc09c9eac071c8f7a 1b90e87bd8607b299d5f9283a32c8974fbc12ac3d6c69e6242fdde876b02c1c4 2 10faf0d2909029f80a46af4cbc27ba2e5e3063f10e49d95b2a349bf0170f2288 12edb042e41c275378e881e8cedb8ea2d18653ed655a623c3a6ca019ccd84fda 62e855fa6ccc77bb2c6352a782fed82769c2617880c45ee20421ed629dccc50e 1 e32d3b2936e3988c43708b2011812f569e69cc972c05f8a19a6cf46aae585701db358e4b87da2860518682bf9194007c4e15528b0751e75ac4a2d4f41036960c5df50a9cf0db69fc84e8770ad81054f238016c60c2daa1a09fe775ac85c5f3050925ba66a11c279405f1ff998cbeaedf4d2d0766906dd81a23abaf3b18ed1f04 +generate_ring_signature 39b8dfa0eb3c1136ff375c1423d44de3eb1643124579d1db3d7a56052d345265 33b74a9700184e0bdfc8fe12667177038ed2b7aed67d7ca8892b59692ee428e8 3 8f0a916d9df4d956a7434564a4a5d9fe87e70c0adfae7b772a2779d49298cdea 639ae0dea20555ce941e2e9caa0b39ff5616bb50820869c6f69348c1b64d3f9b c594dd2ec84623988ba0539b0e34a0216c0b76685c1b900c1ef0bead6375e901 99ab7eab9c1b97e2115a30f2d1654067e1424cdac2a672fc8b8937507c12000e 2 61efde30c73937c619b035217d7bf0c04c7709dd031e50bff32d25086ff222079a06db5a419a98e88f607e8ad7a4a5212bc54f10c92bacb9f1568c8b0f4f21054694ff308642f0ca4d9faac8568e0bef504552ae087b6f3776ec9561c6e39801ccb4626db5fd9f59f35731ec93a8e13bd37c8be8aa6dfeeded31a80b0be38d0526c7f7c6f52e390285420a14f6168c8a37d38477c37b7d6dd4e2b99504ff9f047c5713c3308f47b2533c2aa8f976a7c8669c200d85894fa5f26a6165157fc308 +generate_ring_signature 593b9ee2d60cf9c26589616162cd2bfef251561ce1d1309c115c30458dfb81bd 91291572620b1ae77cf60efc86f3ced09e20af6aac3ff5f78de467149ca90e02 1 d258cc4b3fc7df8de59ba0afa4dce0d22df33d925383ca17aca35ab35e907d33 ac0b55bb5c652a46d5978f780e4c0c35936e77e44d6a21c430f354c5f66d3b02 0 424424cbf645841603d5cb35f29e6b390902ebfa0e1ebc43915dfb76d0d3af05dd0daf1190ea6ce6f93969f8d78db5cac8e1295781728e2fb317a0177dce0b0f +generate_ring_signature ce1bd7f1429689124c929ce5ed8baeaebcea260b390810b8c6e37eab28b48941 892d2609007fb9c52f42796e8d273b9cffbb77e7bee5f780914018a5b51dd895 7 dcee6f45bd99582405ca54a0e60aa1d4758bd263cbeb3a5ac96e91f35ccbaf5e f09ff54dd8be4a8210e746b75a2a2ececf9b563f4c62f4b110c026ad34254e58 8e9206d466c5ec76008cd2bf3a9dcb93494f7a1b1bf9c572f2f2df5a7459e295 d2f359c396b83325418260714d6b0696d7ba4a21f49576681207c05dfc354c8e f6ebab66ca92f13fe5088a040c3b3e7a06c1ca3086c3235fa2e319d9bba64c6e 805bdaef10a039a930bc9bcc9cc7eab5c2a17c473d9d4f63004b9657174af3f5 6125e9b90e0bc7c94b15e73334b87e6681ff2f5bf05a13a95b1a913aee00ef88 bb305e5270d1904a32e052bb36a0e05f7f1b14e6dff936660c6cd6e3c4acde03 0 4de3fa4957e76f64dbce9287752150cbe91ba181e4317876c68c98607e2dd7008cea3c997c58d4647f1ac025557f37cf54f1dd669ab7f77ddd0353a5567abe015d90af93f8d49a262fc1b42b90757227a79d879a61a9a2893705cdd4c2e4eb0fd9a48071dd52726ebcbdbe678f887493b6a944247ec1972b93ecf6bedd07b2076ed7365e57d67dd58207a281f44ede22360e182c74e3f89594fe4a4f4409a2045ca7483dad929225f24321f0fb5f6bb35e204bd0c579253bfe79a09d59ccb701362af9ac67f6df96646871d20b73013d4c2a489086fff034f4ae65fe2e327607ed479c03c4e514a3f045b85061efa42a7d8c75a408ea0531592630cbadc86f0b3a2bc9acb1efe9359e99ad04d490387bdb75dd1d710aadad6a4db9a93431cf0399de3fcadb35fde5fb32908b756b4d6c4489c06e52ea1b74d36d66ccbe11c805a0e95557ce3e3780af613e10b0c45c86562dc1e115cd28e79d1d450349058004abafa87434ef9140ae9b7938e27080dcd3326814d6ee95b47cbab35ee9dbc808ce85f0c6944b74442eefa9648c7ea61a2d1aa68a4c6c577a70025f9a9859fa0026d3cf54ea0217fc0ccc6bf9d745946b9ed413e3e1fba186db55728e35af050c +generate_ring_signature ab808408a6cd49e6294a267bb3ab5d7b3da59e558b2eb6b4f99dc2dc7ff50e5c 1a1c1070166944cb1a2156c40952a1e6bcd510f35ddbf307ebe712c5870ca995 1 5da4515b20646be9a61128bc98abdbee471688a2822bac54a5b44223992591ae 0a155004d1d12e0c28d6983d93261b6c03bd240299b3a1dc4137908ae96d7404 0 eecb9ea4edcbc47c10d855ca42baa6cf1469100c3b9f94defb47abbe4adb0309f4d36569dbe51c814b114f18fa544b7d770b1530ca50e8a10e787648072c3f09 +generate_ring_signature a81ce60dd37f7b6230c99e9ab54ac7d141cc849e4026a04492fac91ef33527eb 04a97d88f05caee309d45e5318548874a90d01076d51d890ad246f380be18c3e 1 8205194e3033664c5951286331236649c98203444e2821bb57679dd87be72536 0abea0e642a36d42f97e8424d03dcb2a421984af93a119b70babaf59c7389602 0 b8c9385f19b6745fd445117c4772db6c6788faea13249da488f59973731ce40bd33fccf2ddc7bca172c468943af9de363a058eb4f187e9923812d4bc8087d407 +generate_ring_signature 61c9a8ca6401d6d7fb704f7c69fa59119e39912deb9b27db884364cc5c8bea8d 639ed6f0539bbb0fb38e16283ee1bf8bfe9a6bbbbfbd75746065de0324170384 3 5761523d07bcc816702c3c043ac6954d7d394a0086f1fa8c247e0e2d96d1e4cc de43f50b8a065e7ce4c0bbf7f362c0bc752db58f968cded07722e0ba9b26a0be 1c79af86d344001a4447d075ddc2f3fee4b5873f3627f43108bb80211c8fceb6 009e83fcc1a87d02168f081a96101994590d1cc12e4a6f1e9fec18211ceae30d 2 a0624e3fb3183508ea3bbb7c81f572e61b8cb9ffc02a335368c02a0acf365f0e14b7680f276ab42cb095bb815958b919bbe296e3cf9ff7278d5e2443140eb60df82d7ab06ca4095cfe0dc1f9118d90e243646d39771606f8b9a7dd0e1e978a0e7202ce46c90379f9f9e44893bc2918a52df0b866fed93164d9fa10a18ce1ef00c25246218e3b477f004289c1b4d8f028abe26d4a2b9c3e075ef5381f04b19b065250b6239165b0df908821515f86f8feaba9280b7bba14265d75fc07e5e30c08 +generate_ring_signature 9f68da5c35b0c66f737e632a1e8789e68dcf20f86cb06e37a7a714ac5dfec96d 1749672bd5a0fc679638ec245a008c559ba3678323e75550d28791e52e1305d2 9 e9fa42578f79c13d981f798740d02d5be2e1bd47dcde3954d911d6f399fe66a4 bc0826b1d7e7c92cbc72c42692fd2c78321a06f2ca11b5ab2f870dcba0ccd39f 06efd9610effaef17151ac37dde836bd02dae2a341abd94bd4df82c599f0a67f 7754e3e42e489f1c2825ed40d78fdafbcd343f60c25e1be51ad551871c252f6c 28fb5bfc0ad1e028da2f33d69fb9dcb8b8543b071a34e233715e7a9d05830822 26c388537874ab3bc51934e06947dd99d004c9cf87f9a3e9177171494820cef8 a2ea649b773164501ec825aee87d0f9ead98034540fcdda654f8fe99c26e307a b8029f5f134b10df48c5c63e02afbc64bdadb7646aa440fa5e3c878a225cc745 229d83a3057295c8e00606a683090682cc0be74094732bedbda4b5e15d66c466 41d68550b8d5f95e1d9b89b1959982dfbee1a4c46cd7632e2226df4f85216608 6 68f27cbf0ae26c853baab8d33c973ff4aa2fbf621c45075f53bb590a19835f07863383f619c57d4ce0c9326b7cb3e13d370c84b024ff2ddd1c179541a1757802d764200f6790573435a9933820b997228fb27045f687cb57740ae33e96f94d0d84480c742d574db0615246810eaaaa0395361f58f3d2d124f4615ad84591df0b6f204ec330b64b4913192c6281bfcabff6fdcfade29d6f2d9bf68f2cb12e020c36a01548a8cc989be83757e7ad225d720e18f06507c1affc30ffe985b36f2904f7c3e0090681f45bb2a2fd2f73b4e47a334af18ae618ca840e7494995c155107df75f07e89d54dacbcfa2da7375ca97abd0b5d244c40e731283475fb8a22160b8b2e6312dcfd4d42ebe4449f55eb2457a72b374f1b441bb676402e7ca3c5340e2aa8eddf58d1a98db83dc0db10b98bfa59c38bee383bb97fd5d7100db3521900674f2b4bab63cc9bc955107fa9695521026c766b4f6661cd073d2105be227c08f550a995e27f7c51c286ef74697344af960859c47dd59fd14facde66d0cb360e5072d1ff24e926085cf470664baddce47e11a06272a2579960ade92e9af4f70c207196996eef1b995e221fa8a78d4a55e58c8cef4801fa1f99b881263ce5030d8facb6cf247256ebb85f1bf2fab798f3e19596512bc8a0a48247f9a22e8d2c092bf08ab2a70bc7dd8bd9d3f2f474402ef44140c4ec9971feb8309298c40ab70b9d75eef1e6b2ba30b1830aa83614c934f88ae044e056848212a90a356466820a55df0f06102bf8d61c5d128ac753bb3544f101f6ff92b95da31bd1c789874a07 +generate_ring_signature 2a90c74b62f79d4520057476105591b7d0d9477d47a1807470f2fd7bfab2c65c 3a79084c200b84c1919514850d5116ec942315b878ad67170fa494d4e9be7221 1 7e4a3ca5d46043fa44423a39bff8d8838f0bdc0914f4309d108b1367be630e62 10fec3aab8e0da3993b0e7abb51adc165bc0b750b2d7d9a13cca1c14b42a9a05 0 6c17851a2deb69e63ce9f6e4d36ddae2959d5f2b911f7bdf171a54b59e59a00fdb585e59023f815086323f5f9612f41199f8ebda7a9a9928a3fd77a04785b605 +generate_ring_signature 6a030cb0c1940ef0e5324eb07b7dde793ff0d5c838297c645e9ab1bcebfb8375 00073eda05e4a9f16c15cb61baec8262827934a7a43cd2123d81d3d0991643a7 1 a447f79ae6e9999ac2741bff432855ea58e4780866a27a4033a88b469ffa64ff 550a72677312f05974eec777bbf92d809869fdc0d393daccdaf7347e56fa070f 0 f5645e2bfa9b7834c93ce52d250a35c0950b2898b02aca1144429e3b3152350b5674595e8bfdafd44fc109c9308cd041235827b34d59059a0f82723eb4493101 +generate_ring_signature 27ab38a82ccee16501b8c1e20b154fd2f718fd089d5adb251f3fb99404b5f408 a0b44769cca243b396976d2de157b16ad7f217568df100aefb48563b3aa66240 16 d2c60538e637212316442cef178b845cac7bd50f7fa9d5de7998af1cb525293e 553cb45c20baec4d068b950c4db6f93ca263afb1f9b8bcbdba6a75edf56a6cdb fd6c6ae98626951c6a0d851e0a02b07cde0e81f6f2214f5f5b2cfb50bc803078 8fc710776df1edd7caed3c21f9d073f4d1dc4d435904163b065f4cdfd2a2d837 124f402b98bcf32e8b07f43b149332c4b4a97f643fedec0bd1269f47bb43f983 a5ca56d162a8cb711a92a508ee33c2f9052416e3ae2152aba4b3c95c248f564a 95289be2ea1c9a9082a57be49a1fbaf60d1a005de873f9f4941c24daa93ee67a 4968dd3f49e82c33a559e6687ee6746e69ec66d3078e28e702e0e875b88611e3 c3bc37260ed62b211f9b673941b13d2c28f22a26aee37f01a6826328584a76e8 fbdb781cd7e0e8bc13e75abc71368afc1828de9282d15c85a42d24588e9eccc2 f32c23d98ba6e2bc925b5a97b4b09ae9efe101c693951a97b50a49f355978bf5 07287abc69d747fea40e1480b630c160bedb7fc78b507ec97a7e39259cd6ebf2 264a3a4df767da108bb4ad198f3ae936d08c257ee1330de0e02fecfde6772784 94d6a5a18c7d95bfba78927d44db0dde0229a14b32e4a435a7c0416a620128f0 6adac37638a8e3b67151d1b1cbcf8490f3ecf18026eeabe8bd190586edceb088 0ddd07c613d9e7b25c8f6419ac9c6ba1e6d6857047adc4a5878d367f91597893 796d2e73f85bc2a0a062c76d10e91e0704dcfb7b807fe99b4c36bd30d1db6107 11 c583910d7f294d53327bbd3fe291b34a935a11de825a81199413f1667679170dd272ea3cbf1629ccba5316b2564e22b3480b220de737312e0b6aef3d8db145080355da53be185ad49c8731b56fdac1cf2a17c81b974a51257aa736c43b3906098268ce6bdaf2c78b4b726d0a8b11a2bf29481523ca620d71c0d5400bbf5ab00021b10cd6203f7c6c93f023ba1a5b77f0a16565b5e96e81465edf040f93b98907d07fcbd84846295eb07d2e7b6064d2fd3681584ab0be34b508bff4c73d16420941c08035327bb90f83e62450390b7e5f605028c4cce084c0342f07352fff9706fb1b4126e8032909be108c96f2edd76d480fabbfa8b9477653bdfe5b19849a003c2388139949cc687ac9c5cadb5b53a3732004bbf92edfcfc7067b947b99fe0d51b94e09bae98ab59dcdb3401b0c51e9dcd5ff6c704634d7ad70ab2255676d007299e3207088fffcc956f130f30a2e6b9cdeea772ed9db147c9b14d2ed9c240bdc6820a84dd7748706329c70e7333159e7f2cdda385614228631e710395aec00e0630757852b14dc7c13242e45cce7442fefb33c6d0260cc763d17e917d26e09249fbf291458e94d48fbb39c89ea26cb9f1bbe213d482821cb42fb4b8c67f403375f4eb78802c594215ae9c122de51e0a04c1da7ed740c157c87765235a42f0ffe0c8a4b2d47bb9b7865edb71b02d1974cdb4eb4b3ed3e13ddd54cc638083e05a4c34d5ebfe8b209b1b69cfd4c4348895ad12bd5efc7f047ada2567033e0860cef0cfd24bee14034a7a6d0bbc3861946470b63fe6e90f08aeaa407b8fee2ea0bf3cdf862bd90a54ddcd518ffbad9e6f3bde84f4f61182fbddb3d316557af110ad8386d5ab5b510df1a09b17298caf8b56d099299c3cda93369a8a340dd34330876ff063077874fa48a3f6154c1d0c7923f1250ebac437972a55e5af169d4d70ff907077aef120ce7eec7bdb987aeec5cb53e4b9938ed47f463fac2b4f3c6e1041e08b3cc8e39e5e6863341bc88a9185eec8f269ecabad1433f1bd8f8c350b50eca8802a3ccf7dd4679667f1f2d23d6c09329a208311184826a0744deae574706501382830fbaf90d50ec765e1bb3d70ba14daad7a9d8e03e87a89e96488e2508258c5574b7d8b1762d6291da7dc43c16d4d61ea26a0556dd512f0bc18c117e09b90717ca155534376f1c5fd3ab658d1eb9f60f2d0643a37a13f8130942d64f01169987f27ba7d98ab48b6a5b58b3c0c507846a4a374ac78c2f064287b6229b079dc63c1a51a32f071659971836fca2e750cd6b09739dcc78247f7be92de8fd0d6aa63b59c92a17c31a0c6d0e362317534aa32a489866239ceb6b2db2e6181c02c0c691066e3c0ae2beb899de62fe25b3d0acaa4246b5032fcb9f3bca238d31003a2ac3bbf99d9a90e9a460082c2fa86053546a41ac91375a7059f5b721466e08 +generate_ring_signature c135bdc852a427f72b26c1d52ece37c8e6f0829580aea52a0cb3b1de8be9b99d fbb5698580c1c2c85fc37b44f29c78fc51767d9f66a6c4bae3957e1e2cb4e854 1 50d36f88064e4365024b03e3bd4ddb6860850788052aacacd03c125acbd647f0 86b86ecc7d9a2b01292b7cc3ad041ec233e7fc050e7c312fea3d6626773a2d07 0 4f9689c896bc9bf522f1b03612acf79e81345c1e699519bb02acc4fffc1070093633533bbefc07c34cf63c1a867a7dc99801f0b75560e7c59df1258580f2440a +generate_ring_signature d6bb43afa491d01d21029af462d2135377c68324c32c62fac98e2dc25b0dda2d 571b31481c0603599fa0dc325307204fac1377e2f333e0606a19b8ea2a02434f 88 2ac71f7e7837af8e5659b7234df3027ceedf7d83902010aee960b1127dc024e7 539f400b432b103f5d8133b16652748073b5542211682b1284d2cdf4f33d1b60 db9d0d01670adc280d4ddc271e7fd1e6e1888388b3931a65710d5a1e73a14087 030acfda0ed366270767f5f894eb9ab4b70baeadc2ec935aa94fd50f335d86d6 a616723fbf9e61448f74d8fe42618c5c87b229b6624ac14dc14116b76b689821 01d978aae404a04973e46292898b72a89a7ac7b046389375b07cc5157a26bfed 11d2dc42fa37e455f82deafd509fdc4b45049fa87ea08d4ab7340f823c0c47b2 451f577a4c19028c766480946496b9537d015eeecdcf5089ed53c2047e0323c8 62d52f27db8af59116b38c62f6ea8c80f7184ff52217286783e234b3c781eeae 2cb785fbb5d01af5cd8f719ebab31b9d359bbde50a9d5be7e62135d41b927276 203e4d1662f6b64a1709aa94b75c852b193f3789eb3e2a3d8d46c6903ec08f60 29a16a1705b9ea82175f3fca9b8eb8038f4939463f4ae481dc35937637db8de0 ca45dce8b021f848ce383733bd20fabec1147dc2a79a905ed7ef69c6d54a41a5 5b7c7da3d9268c9963bd29d4e1df49465d4e59aa142a8b29d87d63b26cd68f41 0c17a5987a211044abfb86d665a5848e60a5a15edf4479cc67585c1948aab726 f36b0d926564475c556e1e03358ecc422095b08eab983c748a1f10afca9a2ca1 cc1287371645573b3788f1a8872e072f612ee27833de18640a53b26f162cf39f 9464b4a02a0f820e6a993bbb3383c55094e78b145036232c06823e93d73e5dfb 38e08790e3e96ff09c73afcbb006008fa452b0a6837548a832906b6caf85db7d c18ef7c82964192d94641206b8fa06cbf45c3aa22d6645c257da069d642031dc af4c9d42e1e44852830987b6780fb6c7fc8df2f24b5747544ef6ba916c13c45a 7e1d532cb66a9fbf4c414086278f9efb8379c6c0471568d0c7461e9e1b3812bc b53229d701998950897263877a1a91e87b92513d4c7d9240d2d06511afe6cd90 415a1bbb4f112a48338a7deaaa6c51af93cf8b364814138a2f154f5383893434 4be183f5aca392cb86a234b8dcd19c117c2f50cbc27187ffb572a1c27c3d340b 0401c3b6153baba83dc74ffd52a292a687cc0931f8859f884111b3ce5c607f12 ef2920a435ec86672bf151a767574d8973fede3dae21a1178b9b326ece635308 85b707ca7b1d03a48aeb9891988d083cf5e0aac7ffe182834fc18426db0914bd 898a1a1ab3b94ae263b04c5b1638c2dd2376dd7f41034d3cfe6d1f4ede70607f a388c8a62edd0c2d727ee74e238484d264cdf4a32ba15fa57c3fb56a9d801b44 ad788edf99b1f4e04da6afd23eeb174c576f8b38cb9f199afc7dbfc49fb39036 c95a343bc0cfbe4be3d0a0f8023ebb02739553b4aa5510b168332f7d58c69c1a bcd0b5c959874dfef5d49e043fbd6f0d1189a379b1584711bcfc0c7813a3f7fc b83a66dce71c58a90697b828b2a0d11582159142192f84fd976da48d3bb10c32 31a3dcd30f22790dc9df9a677c4b0db780f983b1d51d38b06deca16ee20261cf e11a2cae873e38b5b85c9ef4f8e6c065c0d6e227b270565f09c2d1b00083c967 2298aaf0e3cdc84fb4742712485a29d3a2135473ed8f39239da84e632615fd4a a0f322b52cde3613f22f8a12e647b9ad8fa9b3f4221e08b8d267849e5acf0248 6da2395d85b0191a625f1c7b4439689a1f50a2d6387765205e1387ad63c87b19 bf68f39eafa5ebe6dda4da4b5e80850640bd12b7cae4639408b54649357913c3 5903bd4a42e77ec25235d94dee6e1b65de69bc435dce2d9a4bea51f7619d21c1 8f4225f7661c83b4604e01d8a8e719d8d4e2a4eeaf5034fbc6181c08553d6c37 c0cbe7aebd2e01f17d7b0204bda288cf37a01cc14e6628f9fc937e0408316a47 cb4b33ec9bae47e5e4835be87c95ed324f0250d4fda225bcf1b90af992488caf e1884785f96f3357a06ad370abbff7557d524de0592827fba5d8338af2de836d debfe141e8322a99f03f4eadf148b785f38135e312fc44001fe22f7f073449f3 fea9ccb7d947eea308c5624063ac560bc75c9b9dd53d742a8d18c5fcfec8419f c5e32a2e259870a34210cc4233977a69c9055e667057e719498a37c0b93f2c00 6c7887187d110cca61bad9ac3b8a1e1cfd9688c36180a60d4a2fc800aebd5c86 59ba5ce12709079868563cdf1a80fee20012792a37674bf01e7060b599ffb053 a0e57b75653583ae480a0eadcc9af5db0876a884728ca5f645d660a4d2b688ea 8e8c179ee2e150873529dc38d3c8780456da39c605176e0a8368a760dfc658c5 938bfaffef96baae2df4abe30ae2cef6652d041cad9e6daf642b7b934ca6d12e 29879c0bf459d97edc7891e9f03d094ef5365cee441f6ce6b1a09f53b4ebf36f c1886dad5e1b0ba7c22b95c0d319b149a7dbc094739068c5105b3733a561dc34 5ff1b8ae413e51aa401719793964d47aa2654aced6402ceb18058122c951ac5b bfb66f138e624bb2e8a579099aa78dbda2cebad0f6609973c6f56e8fd93a8239 6b69e59132043ab67ae33acc43ad94973614661472cb2ac42ed8e1c4e52cc4a9 20b935a21c9cca6a277038e535fbf94b34dc458fd987a0cf788723c2963650a5 a5444f21f805148adb54bc61f1a318a3d53636b75c346ea770aa0f9affb861a3 53ce5f0350627c802a7f18ec73e5a0e67f77a7b4aa2a9e4c72d4b9b79086bc5a f4dfa4b7f41aa2730bf956f784c7886364715ffae4e63dfeb2f141f7e16a308b 77c2d5dd75cbb99b2bf54ccd41fcd81f25018b8b6675c133db210427e60889ff cac53bcf9283752a48ccfdc9a0dd3c96e6ccb55f2d96386ec927bd0dc3eb0986 ebef0ff8813af718424e7bf5a872cbd0603eec47763c5af48087ca6b932d6276 d34f2032380b51d9a33739a326388f80839c78df287f919e2629d494be984c87 6afd61c86e0ba0368c8e42ae59d2e69dbb8841b606bad826d19e8afc87e66448 2c28a115a1d825e35615410a7592b2d5d781c13f3e0a414fa94aeb0d1dee2700 8e287a65a9fa40db810d867bfce69e96404af4ff7e514dddab625ac386106b4b ba4a7cf3f7a636558591a9b51975b83f2b620d335e3511150b0121c730326342 50ccf852bf54d35be86435298ef9a71aaf40399579fd4afd76c83d433d93d38a 6b9d3dbb59324e7a2eaa26f817b4a67ea157414c16f02b3473390895c8de1661 b6d9e0e327c0aff40be52c0aa4771467758911e52b0d457e5961ee3b6a5916c4 862e6bb65266de2119e0c46c09eda73400278d7592f36c43400bb179aecbc38f cbdda549c799600e94c6a82fefee8328d0399610d6b35f0480d983bb0c4bda97 5d0f63540f391d45db21f80a042697e5bc7c9cff1b81b8aadc5cb16a8fd3ae06 5060a00f55d8a00c67f4dcc6b09782cc1c5313168446a8a0f84779bba8697e0e 0ceb76ce2dc11acaddb282a276fc575667c6f9ee9340c40aed38ec4898dd5a02 4af8a665fe4ac4b64ca78986ebdaac192118d3a50b6d9c7b54dde5f06fad1832 3a1ac5f525f224695e75b57da8c59352fbd38966f0fd7ebd1436188f24ab9e01 5c191588b1058e59d67d2d885b2f625b21a221cb2cc2d6cde5bf003983739ef9 b1f202334337c8e96311cc26107d3e023c09108473eab78ebc963511b3a23ca1 4aa608bf57e6ee56bb0683871bf80b8020fc058c294b101557aff471d2fc2065 a739a03d8c49373b5eb913b0c7740d39c0f813ffdcd1a3cfeb27bf6d7c9064f6 85293323bb1fda4c5a682aff7928ce77cbbd5703a24744101f01e1cb22c8cef4 4f890f00d911a2b78583ca693cfd6c0e8bda02ec0e8d5e7b6cdfed2bca04c7ac 7879ab4946915b3a20ceef4fbe50ed07dee5911008bb338ade540b516b0fe61e d919ad1d70708c7716558f152e81762230b862677383b0633fe28e4f02a4e886 6e215c51c202998bff7bb71c1191f913d3967dcde33a9004b8518b7ed8817e03 60 2daf80b314484b3629db87e7d91c96e4b48cd0df1febe7bcbb5456ff0ddc7a0a19e2d0c6d0a73be134d25d7d56f56f6d948adabfb25eaf186653b2ecde193d0e7b4bda594154e30de8e95a8a44779911530d5b246981d5d6b48839205a450c09e0bae81cb0ee5719123b0cf156ce7a69b38177c60c7cc6c13a47667c68e36702b521a59700f7712e8a7ab119aaf06718f7be3f215f4d7fb84006e00013b8490ce80f4af47892e3f63898c5283843945e681da6b3d909baa2c3917e81e22d030a8eff4a9f6e83db190a78ff1dad78f93100ffef9c2ce8d1efa9aa24691d300603b2cb5a0a74fa5c49c8a43907343389d76c337235625eb69172a2ec04d193ab08a0aa2e86010f454c414d40a48e2a896735451f141453299f76e8b7eba7aa2d0c5a72e663fae4bda145b3fb26cd0823daaac71d95e6ffa9a2b5e07cd2f2cba4002634e3e84221a4325350b2402c3502c9ab4c25ebaa3929028d75dc92e31cc80b095a285351653f85c1745c2aab8059f492ee5e9744860dbf178bd06412b08f016b0b67589b9c0f0e78ffd56bb2149e85f62bcbf6b84d6030b2a30cd951e4c806d948ef430ebd3d085191eb41653ea445bd140ccdba7c5fa26c78e23ed7850e055c97b28450c6de86b24b415592429cb98fc31942607770e87d73c8173688110bf18c90010840cd515f6ffe1ea7ef5ae9e1d5ce887ac6b7c84ea46be647900304eebb026f00431a9642c68164713ffa9f3c8cffd6cc3e1428627813a6a902f30f4dd224f5f13d3ae5e9c56c75871f75670ac9ea693aaf0681e8bbcd64570bbc0f217d1f9ac531d56b3defc20be8f641ef72313bc8950787c7ad7388647e565e052436f53aeeaf690a5dec38713dbe7485a6911dbd6791bac20d54fa521bca3b016df4bb4c8df6810997edf83d17082ac596076ec836bc32e1d60214075b49020af1667906ef57b89ab49187838d60a7fa10d146e24c3d01f0e5877c4930942a0337447910ba6f3da89aa541b020421e2415e9550742af8037926f5ff99060060a27d6e293b6d9ce08919dbef4aad04aacc3d5fc05d6ea8810fa99a0f5ceda730d6e2902b2a000aa6f5eb22038a186b7f289898e86251483234855638f248c29081fa5d7c565fc02abfb2be22d1e0bc5e5dd8a9be4cd2ec795d977d80d0d8cf805d6a1f5033a87a865a4043068bf89b401e6e91e6022cbfccb6a7f8becfaecc7047eb21c02bf48d32a2386a8d96c76e852a269513d0c443c184ab113b79a521d08ee75c480f854f6bd8e3cec7ff162b11540155afe7c43d6959a7dad8c1469c3044e64796c5d66d45785848e8c165c6e2ed6685b2c15e66d9334b0e14fc47d80085a8f9dcfc711ab7577541feb706ffcd71d016343c0aa6362f812102a5d01270956f2596d197b9c2826a8b1102e71716cb135d56dccc6d956c89d180f76c4df0c33f25f0ce1cb4e4282ecc70352ff64e43de7e0dfed2e49d6bcabc3072ccb6800bacd740852f49b6648d0ccaf0781c0e9c76265e64ef4f3b169e6f7869d3cdb079e8fe7db6637135edc5f9c8663a818d6d5d343cdeb35a372084b80ece9f87e0ef35376a391dd0b0ac8b0c38546694fc6a89014a8b2fd95bee45755caae37b50d79341f1f86d949885bd2368537e344e7fb400566cb1a82460ca49dcca014ab0a2dd7dd6ef86bc6ed007f6b01e9154261e17a2ce29e74f51f7fd44e386129e201c6c5141203dfc5844b5fb9644ac1348a2ea75e1ae98aecc2a39cf28052897704566adb45fdf4782dbdd79a96ea5297dd5bf44071b028c1bb7c775f0f38fff20b2810815a4d4895e767d55dde7b7d2ee5b9a2179bd4af26bdc0f621e549f1c302d0b28f42329375e03055e3f927e178468b29dfba8f1ecf39b1933ec8dc7d420f4d4217c9eeb2d6db00e0569bf17285c4a44299108f0cc6ccc098028a3e41af0630b06dc7ff07392cfc9a42105a513a47b242ba86f3f7ef78830f5c78eea9b203e3b3223fba880b498b19f9cbf8f6a17b6e6c52051c70de937ba2b4554b68d20346400b2cc140bf7e522d44b929ea56cadd9167e8e16625ea2136c8ccffd1de0582cfdebf2d656ab5256d1c3ae397ad1847876510159ac2d14ff9a57fb3abd9067e37405b0d82ed9534c0febf9e7601647eea4197bca3260fe11b8665ca44a502b3bbd48ba8323560743076abc6588dfea40a152d5b91b8729208a38368b5b00e7bc113cc3c48b987192f7abeee72a6683336ec62695739b2e3443208bd65e705e5f551358c803ba9d34ce1abc801e00a31e5fcba19504507d4dfa84c4f3da808719dbe6fe10870d66b4e3b72103fd1f2ced71048b3ca492e4ccf6e887f796b04bd137485cb0c8916f6f1c5c2b4d8c04f5b916f0a4afc16cb2434dc2d55d5700417f871c33ed817ca9c450eb97dd29793a34cc3784d663e02b5ff7424ea4d180f99a78fef644e577883403dbdfc7e72ba9fb92324874d55248aad686b1deac503562ac51d1757c46e18921bc29b90be46b343c9b3d8e83c2e19160b2a60b5df0dfe2c09b5933874efad1bf44c70fbfc28fea919e7ae2dc595bfcae1d2f3b0cb07496a255c47e63052a7322d4ccda5b320ef9acdcc890faece8f6cd3dc0880cf0a6006fd5c6bada86c101677543dccbc0d122bed375633cc58dc6f283a04a153007ccd107091527c12f5241541bdc74a16070a56acddc967f10aaca882c384b60fb632230976841dfb91e2b17565b1ba20572508eebd52521c69de4a1417ebb8008a305332a640810ec089dc0def977df2e9dff8f78a38baef523a563400ee3d004466546595862e01824259f3dc1b4f772fa9697ba274fc32118b3125ca94d800961c34dddcb06ecb576cf89e9971eb8165b70a07cd7914e116af433f02b37b0d73690bea67b00ec269341bbbff9143372ed65ac6f269d937ac5b58fa53f0910f6df1baa177607a91e16877e54a50f6dce1949197341f7407c45734de5dadb406e88380dcd021d8ddbd561c26a5971285fbaa27d2ddb7ef558857b3af131503037e6c18a7cdf96a69a30b6f070b4602b0699f8ccca240a6acbcf898f15e44e4073382c4ccbacb0a378bc07d3cf15cbd45a6c0bcf867c79cf3dd08efc6ff88a60ab1028f0b82df4561e47879e57a2eb1561a920298340b41777332d5c449e16e0b9bcaa1632e21a0868d475715e78c10306cc03e1902283e684be2196e5b90190863ca28a42c88fe3d191ab5f370c251e5dc3a0664499fdbef7e358c0157fb6f0064f68398089508faba95fe657a8a7c3de066a8cba7d6e78f0fc14d5e0b13b3001f69c36e5bbfc05162a9c8042f207e11354a9421e4f307ddebd419166c3fd3063c067aa716729ec34c877ae1fc42204b602f55180d719105d65cd9b32876a303da953d4b99f318a156e51d6d6622bf2d9153787885922d3767bf6cfc3582550bf34905986a3c02323248230c3e81a724994166f32dfddb5d1d151aafb867ae0d33534eac63c348213a4f7656a7fb8c79e8079c14934ab1d9bbc250b7a9f3a10c3249971ff0faf6aec2252b510fc5dd28b24d8ef1f4f63a7a0fedce891d859d0a33eced6d9c50c3abad5e5fcdcfbfbc22dbc1efee5f84035eed0c73b4a236590719579be736b97deeb77c16a3063af9927c3ea2a2df795972797568f3da8d130784127f1eb52d7b6616e0c5f26e2658aefa12a597aa9f312b18811bf8e8d6020e00d81eb38c8e06293a5537d6c0940a6cad20d5f69ca83b5e22411176fe8f1b0aff13536038db93ed1d09b3855a5942879fc8cda6dd332a4bebf8c55638a0ab0d621d0cfebe87f1d58f2a07409b46ff434d6016ad03e3bfa62cedfbde60499907cf3f5e337bf8768dd9e46e8b3d8f9b7fbd34267c8a5d3c77e1bea41754fb8202277c3b952bb2a91a6ad58103350aa51c33dbf71bef7612ebbb1da2b4d8545a060ad9b20b0e2329229e8a06ad5c272208bdccd6eedea58d1e326f45861284820c91334e5681dcebd333139caf909b53b461637b97a0f7d73d32514abee649bf0230074a63f6a577ab532a4d48d46f54e48900b5c4cfd5e2371d67ee8ec5f451081963f016a27d898f4d01177df92749ed5dfbbc6876af85f559f736a03b6e5e0bf5da7662eeb861c631edd080c44ce5b97a36924199fcfc4c0c8bc3414bcd9904229c0b2154eb575e45101d8498563a555ebcb84c61c86699eb6a5c4ef3864f06507bebce9adee37d7c16cbe38ecc8aa135e126425ac55c58b0e42cbaeef02d0e1291fc68ff40963233ed92b6f6f3326b83b64e9bd99e222d13aed9226700b004fb841034aed4635e569a62fb4fb00c238319cb47452cc39aa4cce98d27c7e50e8df360d12d657c5e0b7ca82de5e456b26aabac36914b4dfa13f7873bfab38b094a2c21b9cf75dd7c543e9532c826db787d8fc625675a4c6d26e08595d2fddd006be0b7036d89ba3e9b7635a6359b4341c8838a5a019c2e1de614a39ab4aa510ab83d40d3588c4f1bc989371f0c26ba7aae41e15a70584afe22514e026f9b3a0cf939f18551446ba26d23f48d4c97abd86ebd818484388b2acb17744c9b834a0174f1f0dc253bb7e99d862a998d0db2208e0264bc84df0853ae256145addf2e001b578e761c0e7fd6b63bc6f7efcd1bff85df1629d7fc507fdfbfe43d2f9dd8051429dbe7f7c7f0cd3576ee31c67438bec03155ade773ac7625aba0722514f50b13ae021f0700c54db4cbb1276d02ff3b459bae220a04cef3f3c4d5d5c38c640294cbe08d2b15ce591df0d5545cecf16de899860969a59043adef65d6c656cd02a21fc373b429ff8fa6e667a5bce9f68ded3a31c272b21ed1fc6b525daebbb80c56e2109416c0cf4aa3d17039f08bce24a749c74ecefe429c8893e62e7f7ff40713579ac476cf00fc42814a4d205ee4b126529bb77bdac530a05c4e4ef82625003e7e12671a6117309b4c716d26d8c112adfcfcb89ff92266a7f89c9d292ebb0cbfe64022899c1943d7da369cefbda680494a84cee0372872fbc4b830d837010754ded6e916cb89273c0c72838ad9814fd6c6f66c8f294459356443c2bf3a1e0136da5d3d098a4382eee903694fb08d05832b925e511235066179632bfb45d30b842340fd4fd4dc0c778d903f6623beacbd7010e2530eebe8f8a0c24fad45310223d28062fafb417aad594cf963c71fd112e0b307566c82574360c3d5c31f3d0b285f1cd726260ad6ed516e56d73d8e68f8ac2dddb24293d6045008be235b9401287f4ec7b3c9241ffb2a3e8ed32db703799bd782be044659a0549e7856b8f803d5fa943d8955d67accea03a49fe674868bdaef609c27e76034b8b7d825de3007f87a86b8401830dde13063801c5df0c5618f3932591d1ce0d54462b21f0f9204535fad585c986f54917450db3f3be86066454225e2537d1241681830ea0be90adf393cc12c5c7ce53bf4d935c5ca897c170e6d3911590c70217b9cbfad811700a3c3d944b1de99ec516901243afe09beb8d75777072a719f7464f5acb4322e01ef02817c11b46d9f94a6db763fd55a45cbb9df94eb8b0fadda870c9376fa780b978912017721284c003ed0a48447f276a53ecf84c1cfc92d7eaa8403b8dc000623e8d5f7538b02a409f74b7738f88fea6a66848453e6d9653aeeb639a21d8701abd82f7b57f9707414274aa1c9435f5497edf0e0f9f4f427ff69e7070f2dae0b5c1bbd3fb86b33ea6f9765e9c2bc825a1c053036f0ddab5dc574f4eb52f3fa01fe360c488d5d4d1ec951293e22c8cc3be094321aaf167169ddb2780700cbaa09eced6d0b5860cea6ecae6cd2ca361c5e6da6dbe401f58f6b505a336d1543d8062a1599de6086c28a8ff6562101233695c1def591cd0e5b1323ba9a54fa83bd09d5dffd972385d7a263bc24f634aa55eee73625372cbc15be15339f85ad41f30fccb2146e52b46adcb64a1f37d2a3ba0a2554dce3c586d35666f4f0c8713e110c3fd0aad45bee7bcc6af9d91d68ae2161e87dd0d13308992f36fff68f2f5beb09191085f94df4c11464f71eb4dea7aca71cd67465352fc85275ebcfbf3f70cb0e8ed51ea2cc0b0b6ef6894a1f0039bd9c8667f6424bd0b4fd1b0272f311e7bb06f28db1607e2209dc40045a8256feb71157f72f6fafabea6aea557539a924ff0b83dfbec966374e6c6f301983af5829358d2d2645443c7ba00851bc7ca4cfb80d63e3bda1a45a3f0a45e783dda4b457c3658d528d62681033b153d808f365d007947bca6fe131dfcefcd5606c515680531fdef8f1013903045a624c16c480890638259ed6a4edafc8cddaa21beae0e5b4ff0535ebb1b3900ae8969c26728d1d01ac487e49929782e36ad1c073190a8e41efb18382ab791bd728df83b144ab7a07708b9c62bd3dc94de1758243cb8d257d5af10d0723bfd9781779655e4d661706b56731d58f9dd8c743b6018834edfb602cf52e7b46b0ba3ed38560a572bb5204c389c876816711bb74d7c7539839fba2c476e68a7c2170c0cbf5fb1ffd700e0606e70efa54651e4efcc7386ecaa79c77ce5e274ae852bf848f77c69dc19be90b970433e79b0cb21d9dd06c8e4913db7fd725a38166ddfa3b35370ea4f32a4701384a5d12327521a4de2fe2cd278d751f6b9c983c1dd3f1c8c9a22c7df98d6b0628438c427ce0a9ac6a1030282e8c5dbdfc99ed6560a7a904c52225c2237cd80d2ac1ce4fd47be52d4108f9767455a2a4f1f1e28405415a315457091b8782480130553c5ed11267130e078cd6c49498f081b4c120cc44c12e2d55d0b63e5cd90c586e5227249f23fe58c17dfab359b9c9f1b6deadebbaaef492725df3eac9d00ddb8a165e4f3539ed32041b293ba076f399c03699a463425cb4d7a60a492b20014e5f465244183965b86c5860bfb7022047f066a2218b87e52e926d0d43c4100533415b09b3f6fb603ba78fe9ec57780e5a0936fe778f9b336a5e26dc33e0ca0aa3b5d5ec3d5f14c232e1d90b8262bc40720ccf8e2006f86900ec58eec5fb95009ce267ba6f5cb84f5d0914026e24ba6023a55d42ba4e0a791cd13115905098024d5b5e79c9af0f78953c067b530b1d047e0d26f11a888ff810941c16a309040cb19e75e0b3258ac470cde46f47826a250c367d18ead01cee9d5042bd28d31207dd284b0d3122553f87c74c83541bacd3dc73f89e8b28bf415299e79d500c7e0e40b10ca0bfafd34a8541bde4e219b4faf89c2b21de84509a3fc5228fe4840805167baa09170c14e9c89cca5d2371695af8fac21b2b68f7eb9ed170a51eafbe0c80d393e0f481e05535208742c3cb537ec4f14cbe8e37ead0f53dd3cecb8c2d01c6f472a2977d6da182b8fd2c9462bf81d2dc5edabb8118d9c47448299cbea70d59d5628fda134112ea5b68333cdbd88f49b89e14025c10e035f0ba2c4a5c150ad3c6018876bce5512a72358e9f2700e30727a19f8f5589246f8ab92ce3b2e60fd46633fdd05e58d87321f65848abf42d267599a4511560faa9f1f8337f69ec0f0d142d3da81f673d2803475c04f6539c0ff87a4d61c569daee1af6b848784d0fbd4f737eb4e1cfebb2d214d780ad477487deb07c14cb9fe6bb8b43a809aee103348ff144861ddbb02f4f898abd85413b9b76d2ae23b053cb0b1c7ff14fefe604b9a1a717eeb81708d5515d78e6e9dd6a0694ab4665c953599f7512f8cbaf8c0dcd692f685379eaff3c9bce57b0eea62f67cee43f835a7175b0897dc193930e0781b91ee35ff20e21de0b86684536e5b5cac11a944ad10c3368e48d9d867ea906fec3195aa453432c3b4a11a7ec2af41a771bd726f8fc58c925bdd4b76b5ed00fc2f27f8f81d798455d2db1a9ece3afdd06974752c5fde9a7b7f7a29cdf6a2303efaf12bd9c505de07d2fed7abf586f6ebce41dd24e079e9c497ec10a84f8a5058240698987990c4110a3dbf9d475e17cb0364dab7102ef2ec904761841478306 +generate_ring_signature 987f7e40f6c2b601590f6b4c5a883c96556b8f4b9fc6469d19e7dadded5cd555 6a0aa7c8c03c5ac7045ecc1a4e893a4fdf7725ab11d25eada32a77398e9ac3b4 35 d3a13b012ee829bf5b786d1e1e10e33edd82b6f038086da4a841faab9a523e58 1c187d7bdbfc1e5a0181990035dd16525cc2697f0fce4e2d76c28eb397fb98a8 56670bb5098767cacfbfe288269d56d58d7a05cc923a0450e23e910f74809ea4 5451848bfe436b09fa8293d03b49b03681d694ec3003def615d5eb31b22671a1 dc25e9e654980d3eebdd0c89e0e15199da3beb67a447936a5e7b16a0fba8965c 085b4f87297ae295a42605e03973201c7adc7b320ac18d28bdd5924005bf21e7 e4577e6d769f19183aeed4f7d0523bb90735dae1c49e1c2ef0c399f9eb5ba1a7 1fe8919cf7b811ac8d9e9c3d35b89f58368d4800b3e15b7098c76b756901ffc2 765557b61310f2d3347f9205374a94c6a03934464345a4243fa42add2dace240 ac3a8acee643668e88ae599efd92f5e214028c3d484ce9338bdfb696f5664376 51dd02d86b98998c015ec90a316b528f7bf8dba0257a68fddc5f57ac39a51ca0 eb7b7ba74a77f39b124e00429621b47320cb26504f4427103e68bec4a67e2797 4e8297a779ce3d319cd68eaedd9625a951e76cfac741f545a0c3e1ae2d86ab11 cbe3f614bbd31aec922a01bbef67c746ad3814762b20cc4a4737c5783837ee8a 411dee8d19314401aae72e90ec6318cca64b0db49b3856cd92629f0eb90ebec7 263cd98848f23ebafdbcb953d7ffb440932b3a547d7671dcca1f456cf0372115 f1f3a8c6781078b199ee7bff6f2d2cb295490fad44c6015d8aa15c4ba9936c91 d6ef214efb7844dcd977dddfecac8836f0de2215441e004f63563eb02eb6d4a9 aec220f95884481836a9d6ded76d7cd6d2be0625e2da6db8854b152f113544e2 d2b2042528b175a5753155a857e374cf505c4de49939dcac8f790bd7c4791867 5f4ae8373e0ce6bce67fd105a4195f9777fbfa3913a073bbd43cd2a3be9ee9f4 01d0f9748453a69f1464ae91fb6d3f70706a994d23e91884093f3d940da05d25 18752afaf223f7b752d3678691d195f006bbec65f5ea0db19c607bf8a2158614 d35c6410ead698edc172d7477ff3e47189e50905e49ad00eb850817643bb68c3 bbf667b0f880fb24be9e3dc754e8bc2c3c232540e0c8650604ae670aa080e472 b8b75c7a18f79d948ec33ae4da698a9d067662bbb9556bc7efba41b1b72af2ec fca14b488315e37eede2f9838e7630d7b43d5d7bc7a2a9c2ce81e18123483d1a c3bd147999e9177179619c57866ed7db7a6f96c3872687d236d35f7563b9ad3f cc0967f9d3b34067273fdf9f748dd9eb3bf2ce29a163b43a9a42a42d60f867be 8d4b63d51f58ae37cc47b09fd348e7e862ef5b38d1974e56b39054a82fe2af9a 332322ae38f027950fe85adfd1917622bb8f03e71ebe202ceac385ef023092dc d517ae75acc67d77eaa8c454713a54db326ebc6e7bb44ba5abc4b4861c1288ef e0a6ce81f85d41be0f5ed4dfccc61e8480b7d51bb4070e46842837eafe664ef5 095ffdd0841a679bcd1a700a26bea342483b13abfe9062ac613853214df37cbe 8a1f6620d286dad2245eeae98fdea27419050e76026cc8594ee6b7be0a723cc4 2b3694ef8deb74559f0febb88908d6041f5017f0f8fed8e8f5fabe2fccced70a 12 c1bde6a5c64d495c7875a9f0a6fb2f23327de0dbfd53ebc98c3f85d2377ab708a6b0cc3a4f04faee9ff5b121bf4d13e1fe410a45a20c0e2869a17f92672faa0a1bd22ba380110fd42a72af74dda1f968f065156c31e709ced4b3aed8ddf2e7015d36b0aaa948190a72a6ba03417800119ed6646edcd35245b5652f8c239e9f050ba934a81092fcb2b93f00922b719164715f7f0bbd5a74c90f5059426914cb0fec3acd8f3a992df54e1f15b013057fcd8577cf4a45e512044b766e923e626d0fe876189d7e8ea2e7cac5f57128328cbe91ccf0d51f21fde01457a61cc3317b0070f2acd6510698da30170716e90e2935d1fa7907bd91f378b137799eb8351509cb05ccdf79590434194a7a108e88f6fafb3d75b2eb3df2f47cdd3569b4a2dc03c372dad81545926eed0178af3ffb6212fa3e61321cc5cb26e201ce1e8d925805261be94b1dae393400dcf00b1c44ee9f07851d93247fc9bf85e2cceb12fafd06714e20631b9798998ae6b52a6800d2200af799e958cb3038d39c18ee1a75cb03c20d806251aa4518fc8c7844d95b2cf483cf1434b9af2979a6f4ccb0eeed220957e810a3e7e552b60da7e3323c84c4fe30b7b2a999178d1849550a582951890be3710874d7e4bf89fbfebae724d2f918da8d97d2739355e6d63444025d54b702ae449b20fb89e8fe750f5b9438802f1e9bb1e0bb393965cb2cf2f9ea7458d20c938e967f333e4930c6342f435bb21e61bb5ff12abb7c9e9f22871499d59d730adfdb68d8a154397faef2dc323cc7e376fab89958f4b34b353004bbb56376680b37759f8ff0c82cce930a25c961b31843f023b0849b426ae7b23174496ef683038b63fd6e21e13218157b13262cba636afe50bf0eb765e4f16c75d9fe3b15a701fd09a4b0d1b440b3757a27b2734abb1d20672db669045977f5599048a173fc00f6b6f342adf44f58d3b4ee3e8e152823d6a437f0c76d7e1071f0d04f59d22d05341783edef13f7ff5e194959a82fc619abe77bbefdebc2051e6bcf36435d61008ae89d3db5445cddde15694f424b1854b1416a4feb0c3802009c43697d272d0feb9d989d46506e987304d4d1d5fe0564afbb249733b00fd70034d65819f1db0e43e164e23860dab10b4f8749a6cc58611803143def8d74742a78517c4d48090a2c083fce439c0f28104708919efccb764733badc226247ecb33d91104af688038011e16142ca18ebbf038a158947daf1b3d1fca47ffcc2b33332e6a9eddf3000798de654db9d35aaed0d1a3af4dd687068f751b148a04c2ffe8b22da05e3e706bdf9a59bedb2aca5fa3e9a59bef4dc878bd77be6f1ed56d74aa0a6b63bc9e50f3ce6e26b879da60228ee145a6042954d0d0faebca2d0f6b73cf1594bf64cfd05acbf24eebb31388f45046c2c5c47593b8b36b866a18461bcf483676016c30a0dc27029573c13b7d65cfa54c6f86a014828857f9394380a8a51ec4bbf8c93d90cccb685ddeb68ad1f261ebeeec9fa149c2670546c5753f733271faeff1754a601663bc6b869307c8c2963fc8a69c00ece65d368c4c35a7d0cda244283a6ca0309499f84f272d2a3d7f6b067384a546cd2ba5a55b149644a854a0ca90b3dc5bf0930e0854ec67d3529216386bb2e235ede6e3d74bf24cf68e9eea7cb9c24b0770d0ae3901cc94419f4f1fdcaa8d9010d6ccf1742057337f6cae385abfa4c948401ade2c9c750fd7b369fb8095b6c8f08062847c22916588a1d09d9a6e2f4626b0e6d01ea3e683b2a94aad4da76a3850070e674e8b5ecfdf0fa1cbfe08c147f6c00cd1c68541c61e2fa33d429c84ae4be8f78ab68751e86a8c52351e53ed0e948070aae7004f1f785723a04a2eb34e6c31929e0c4e5a979c402fca9c570681cb505508ccf22e425ea227709fbf2de571f82feddc5b4134a87d7dd322ec38b09800ad32122cbf18967891101eb2f81addf04db2ee7555b1fca520fa94be9c135970720a409a6de46cbe888f6a552e1fa6d851caa474c236c578fa9ca61eabc4f9a060925643a7eb22832a1bb7deb0224761fb34cf2523e445da46c6605080f87040ca4b0cc40969770f2aa7496eae59e60b966e197d70de71a292ae1db4564f6a2086b8e3e0007d40831aeabe66c24dca892c87c9533bdbf21d2d4b559c453774a0147234301ad9bfd0197ec6d473b76c0241fb91bf96bc2a05f361679677ae88e0faf0a61d9559c37f343f51e20e4bc062c4e503ced4a4629019053685ceacc86003e3933e94379a31fe53a1b9b6d460e53da17e78a8b2cb9b6c0f00586f7e2b007d41816a8a21510231f9e9d2588ada66647be1d2c213af7b61fba9d56b7ac8b06fb20e38d3b305ef172efc1e91d5c24a4b3eaea00c1b68cac7d7d60fae1eb4b0c14cfda3819992bc241a75cdf9a88379509c8a20236c23d5a5057b101d453ec0b3f2bdee27693af193efb1e50d9521324689c039a43ed78fbe9c8c4bc9560f901fe6f662e6de41e68cf14f560ee8e1b65eb50750516344c93171d3b482f6df2090435fd61bb07d39f67ce9d6c5b78fd9bfe793a1b0e4b4dd878f6027f2d9dd10b4c2ae3425be3cfd7ea30972a81613d1ee578830a64ed1e2ef7868cdaccfcda06eb23ff4b73a20e91f4c68f82a916993bb82a8cf2b721c3317c3cc31fea16800549b3f614bd5c3023e81a694ffe68ce33eea4e1fef3be64924e558585f4107008402f9da916d79031f7ddf7249db1933c67904fe6429e0406e035cfe9286cf2073b88da10247b65d7d727e4a1511e521512d48ee9b1906321892ccb749fa6b6079b6caa2245173c84b2d470c9fd373b8836ca02f556a3ea76e6948f0ce9c18108d598a7c5da3dce608376200b05ab670569ba252eb7c318a3a3e4459dd28eff0cf82968893cec0c6c0f12ba0e36d534946028e644770ec120e2d7384cc43cae0960ecfd80dffe21059554d729989ce08f098b751d40f1b429a586bc93a4496e0a3a5fec758104de9520476d99dcc262429a8733250f8b250f866a9424bed9110013d404e0db14d6527ac2079f9f92066d97d0e9720bc28dae682daadff430250116e500008dfb0d4a4cae569690feb71209b7990504659af1a42a13918286550c5a9b888aabe583cfadb5550bde04fe54ecfc8775924581a3c1fc62148791b304 +generate_ring_signature 132293f351732cfac5f4ba4ea4f281d49b007b301023d1cda4dfb0c000c1961e 885983ae66610d9be2eef3465c820167ab77fb03333917eabd92b41e16351bf8 18 e8deb175a2bb118d8a45fb8730b3bdfeedfdaf6c4d8e56e6e955050cc7b21e9f 066e0bb5852b00a1f17c3ab99615578b65980273dedfbe44767474777d2e262f 755dfe2ec38c20269ff50fca7ca0145d4045a549ff8512b0573e709d8f8d832d aca04fb5d390da8c420f9508048133607d79eaa4035086dccf22c1ff6028e237 412dbe67a052ca9fde2ded0df96a2d534a66357c5677374c35f5a50e7870b0be eb3d29aa1a7ae6943a3a5b4f3b9f562f551c7152ddf2253e8d1dd6796f3382cb e4435113473a91474d4b99f8196588d30b1af8c46ff4b10d9e22fb708678d5d3 299d5a2c5b409d2132513c4f2c15103c088e6e37fd8a4f85404c7d52e23ade87 edacc145bd62001b3b5418c2fe68b1e1a6e715bae1a444e0e292f167b0bd0e1a 38661e68f5867d73239ec06e6b8947d67fbb93dfafd41abde45383700c764f0f b09a34a38319359ed76e90e6d2ee6059a3743bf34bb36318391e47de495d07ea a9f60d4c4eca606fe81f07d594576a39d361c76a64e0d30246db0bc81979be97 c8d3e8c93225adc771c308b20ae4eb6930862df5d1e26780d97f459cfc3bbfe4 60c2ed7ff953d2ce7e83495efc721eee9c65770a97ba4ca8f20db1e5438fe0b1 8e33089c855d750c5dc32591d43df2e4f8a3dd53e1610567856905dbdc26d76c 5f14110b6f5e406040aa68f16cc425e17e9272e03ba5b54206f8fa781089ded6 daeff9bc44f8c78edf900e24f029d31b7e8e5215808fa557bc9e611d05c200ec 4c3f795c54a1f1e750ec62772c32469f92a9c682495ed62e963754384929f533 c496d82b09804a5d4a49464c6fe9c8b09dbcae34905ae10754d24a0b1bfa9708 10 1f9337f37b7198fff80b088ea9faeecc3acf53145f29149ab7e3ec95573a65055eb06cb6181cce9a18f002bce5fd64a984db9accc86d03a76cff9223a315cc05ab98607a8e35598248ac6c575db40a34c563a8a4d68e55304fe46d486d67d604e21a5338858a67848f1ef886c3243189b90c76a3b594c79fd5e7a7f272aea40ac7e8732a14b456856076d1945cacf6276cbb380b868e6467e4b26f2f1e930d06494483427f976c326eb254db8839f435b4085e873c27748c06232b476498d10defd2fd33b2608032c78dfc2b7d10754344072999626e8717cd2f69209d70460c5cfeaf9e21c96ae308c8c291e20f00b5feb26332004c4b048704a8b334ae2000bfbca362c17f5effd6ba9b654c054836ee23ebf5748b59bd171fd3c7606df60d28671d312911404d4e3738163c13849cc23c99e9a91c053260c9e55ef2a614003a3c1494c2fd54bafbb0d15c74ad13a819efc2b247b071e46a29ecc601ba870bef946ae2d4ac42230313c62f7a8030bf4807832f61a84c914dd1b3f0de6f4c04dc6180943dc5afaebe1db7bdb0c5d683118d6640f407221881df650b5857f307a675254180f353041ac63c64ee9cb6d840c6a16af1b4cdb0a1f32f3848416904c1cd19707d576f5934846bfc502e69c91b6eb9e83e24679349fad17fa2462e061ba8ef7001aa89b7e82289e50d89858c593b928dbc250af8ce6980489ee2af064084c304587739b1de0dd39d9745638a56e60f0cde420611290e68913d27440feeabb6456e2c86a1841a0a689086f184251267666fb8fab321780d2a44ca5403060ff5781471be18f37bba9fb955c53b40a9bb28a88c39b0d7f8069fcc4e890be9c33e68823d9e282c39dfb9480f9a6b8fdbea82baeb49d858c625786ea3ba0a1839fbb8afa84b8177e4244b228c535b91758fe1528fbc0e5873d94b2b5cfd0e7f6cbfc4d269f183aead71644ffb5f8b946dc6bb106c2f7cf3aff68828a44201584c2b9ccf4d050ea0ac3d734983125299350d427eb10afb87896c6e7dc8970d812b0397ce2069d93dd093e99571cd82f6586818cdc8efb5ec23933be8ebb709a780ae021d2c145e9aac227b1f4de2806f38fc4b75e624954fa2eb908fad13073c1caf957b60e713d976eaab4cea02ca45c2bd7201bb0c77fc4b89cb791eda045e33e2c30f2406de039d7c9cc71bba4cefdf23dd932d7b13cd1b1d674cb14e01708205cfa9d341f8b726170f2ee1913fbdf8a47bdd7c2bfcd8b73fe6f43caf0e6563ec16780c204c6b876880706ba529027086604fc20833e09780f764d03504aa01bb7041432206f16c82b4e0964c708d26c9a67381601510d70d8c71537d0b81c03a5782979b13ea1fc46073dd97d23f6afb7ed0bc64496e8cdef9a461620a150061c7d28d2c360140eb1ea4490a319355ec81061a3aa65dfeb9c60d58600873641c1b39c05bc035c323e4dace6dab5c4dd1b2999aff90d7f27442cee4e70ce73c51300f9883f98ac0343323c69e425a17246b4a40d247ce1b11aac1d50c031ec830b06ac9f19a8bdf4628e0658723215feea6fe730ae0c5bb9b2ed0faef0514dc53c1e91ef4b561f547385b7639de1a074c277406ffd7f2d7964dd2f9710f +generate_ring_signature d2fc6af6cb649b0e93072adc42ca7807f4ebd8fb82c96b54e4252f471119dd49 dac8763af95afec44193f450d8a3e5b69c4db73184899b81fe45d840a1786e51 2 133f9da870f566434f81a4abeca26f1d8928407033bb89759d40b6aef2b97929 000c075a844699d469a0f75a5622b65b41de5a88f505697649074222aafec6d9 b25779430f84cf14659a394717eddeea6b6e4412ad7ec04f461fca83269d0e00 0 c2c0b6442f2a1fd5bc23ea0767ecaba117206dc749df686b638207819233ca0bf6f4b46b52bf08affe1d8cf530bfc6fac563e9a73b3087a7bcad0b6a2f0706043d256208dd45c8d9c72d023181923203971ea519d233a759e14a07d0bc3ed60345a2c5be32f7027607dc83f03e644426b60858f7c3e23253309ef2699b07ea06 +generate_ring_signature fd838bf0fef0186c9744abf3d7936e090a8187b443b8546fd0b09a9979199e45 02b5f0a3c977d5abe9c8d5b0cf708ea8868d51a9c6ed6514c7657f68a335c8f2 14 ae4b405ccc0238ac29ecdb72b6438d50f4d4902ca7a8fc96a12ee29d65a1a01f 29bbec2904aceba3ddb32e79df089a608a22e98d204c38622db6e6728cde9150 e1f5e32e131fc31e41b53bac343bebdbd5f84a1f486b5cf06561f0e187a42136 51e36b3fe1f0f96bd21f9e09c0f8a6faccfb96b70a2a4a38049cdfcbdbd7e53f 2d87bfb9997ab11fcef44e51dfd6032f3933722bdac37058802a7b8c31b3a557 396549ad7ef76ffa79eb5b5164993a52a9e49e3d536bc445aa62cc160680e43b 1a6b171aff3cf113d5d370d0086b79034405cb3e60c37d0c56c45d91b469a776 6f7614d69124bb11488981d9b7df797f59cc744a5622ddc50d2945bf48a735eb b84bbcb11457864850eadc2dea6c1a29265cbc2e249cd612e2cf35c1390b70ac da7c60cf46781afebf61385cc66002ec4a3cd6868f0c06a0aeda38a7d0cf7377 7f8e83edd777e53536fee1f77b9151ebe41fa0f60d176e7506a2dfcf777aa9ad 0183b28af683d28273596d6dd0391c86db67b76735227da64d9f126c16b93740 79d91d9b31737ba50b29262c1d56670e5e67be4e71bdef12a122105492738f69 7003a97bd046dd5acff9c5b405e3261ded6d373ac73e51625033265ec9a5823f 1af85cd92c261b1fb48a537d532a2aba74dd193f0821b472f0e5efbeeb46fd01 9 99a8f46b58aa15c46e4e854093a62a8d474c33e17c142e6e167aeb15c830570b028279459a8ed629338291c222b67c4f2975fec98649adf9c6d343728bb2a7047142926764c46c0df4d8d89326b1a82cd6fb14dd2a173dff2d793ccbd115b3010f9e6a356be145484238346f4c3f4b8504eee276868dc92458b6b74f95868500a9180346216305356989e87984a0d59ee92c2c1528b0f1d6020282110847aa00d9c73ead3198e3328e98fa7bf027fbd4250e891bfefb0f42b3569265032bb403f4db71c234646e13d62c2bd35e8ce1f0cd1d0e1f043aeed0161017d16441900039d96148b4f8ec378ae163d433c0de64dad415650b8936d4363e9ee3cfc4f4087d4dac7d52562637c842cceab65afe7b81a07eecd56765f09375b8c2b3563e0e30804d149df5805a1093ec953f8bd0c8213e5aedb9e7ee572700280364ee380c2846a03fceb0b920d70067cf662d186436cc3128736661d2beb57f4ef573410f8adf13d6ab7d07ba2fcb24a38871bbb1d472fc2b1cbe93fb38547ac5e4780e0ba18aa0e499934b28fb89ec39c0abf1e041739cc5f568d2e45d86f1ec641cd107c6c5f5258b3c1952480e485e71c25401e5225473198a6a2e6270e3d4186c3f076d098e1b005890b5815eb8c56ecb33983033ba57f1f84f33f55bed9a9b37de004f3060e0cae079e3d7b8642a67411e898bb2e44db47c7ed4176bf5aaef77c703dc70a1d2b3d735f1e78f19ef867eb355f3419030798847a0afb5ecbb56d926037a65483fb22b8561221d1c9794b01abe3bf2d7fc15f90279ba4a0d66cd1f2303165ce25845be52ade5e4422d435c6893e24c7363312e08a50e793153953a8d0319c936546d8dfdfc68be4decf91054613f350ef3521f12b36db3db495245f7063c6212e2898dc1d4caa645aa0bbad911411d6f0c0240dcee59422ee5f493f10713a623d85845330de1da42cf4bcb84f3068d7e9a71e6390e540d87a0e4dd18071e78d0cd3b9ff36aedb82151607bf1903672165dd76bcd87192e6192f2e0a000e1c2a55f7c29e17537edb7ca5fac2ff543bc5ccba7612bb7560746be7db79e0eaca3e851e5e02e4cf8f6368f82d7148d90eb05591d4adf9607df293c188d49010a7a0d7c93ba87e0a23b1762fa0331e054433c0bfc3fdcedc749f5cee48dfa0d936647ef4698f457761c7dafaba142f17251ad6aeee9c493a060143aa8183405c8e89ac68b4c452a9ac1047a4a6e0a4db2cb4c0def0774a8d2499d03c3a30c05 +generate_ring_signature 252ac575896b5c38f96767d9c6843b60d9ea59d054dbea810c49095e06ec8699 4a1113f00e9844c370897ebb827b06719c984b78a1d906307e242648b3146515 30 4c8240ce12d550b68042762c3745586a7fc781af3fff64bde0072cc4a17c91e3 485bff2076d43a19148e9d4307ea1ab67695d22087c3877f2e66a8e65a9c71c5 7eb10ee0043dc7c3ac341bef6899a68679ce8a8a123aa86a9d805d9ff89a4858 ef614d7948da829dbea6be8a82990b6e1d9207a0a0559f5736cb7d404fc12359 8ad69c1ecee7df518ff5773bc42b1ea9a7dfab35c8eb624a4d53b21adb0026c0 5d7dde8e14a8d1a7e967260ae4e165d9a1805f9b4e632ce3c636e6b6e89d20aa a28152284f515410d158f41eb8450276918077b4e3f4e1007b5ce914e0f0ba11 e470fe682cd9b130bd5728bf2048555c22a2ba10dc932d3ed3d76942581738d7 92b455bb759122aa1cf6357c71aa9952c3ce0c6a390a5c238ce38224c13333d3 d880267c74d926850babbd861adb190d2c306a173815f70092d6898d9cea24c9 db6b2bf2e2430048163445da2faa18cd6650aca968715b0915f48767b8463a13 5feebb40dead652fb95ee60f6f4a1bd22972bb374b73efaf6c35c75131aa5709 e561db683ceee3a82ac54927c54f9070c69d760d0783a8a4af80a2a64e6ddd6d 33e53c676171c1335c73e39c7ef0e6c3b5d628a7986433c483a39b46e9914b26 14d533e0c6ad1819d7b64ad606c00104283ec306369efbceea43bb84a95c3d0f 6c6ed347b25b3125d1c98366d1765f6811baf257f08d53a0e47da4b950d204dc 6e613967216f01141c0260fa68cf864a7349be2c64526aaa5552b74e94193137 925736badd14ed14df25b4a08e8d7505419dee9323138cbc002f310e61c19a17 143cfd5623bab86aeeb728e9f3cd5fe9fb15ac23b7fa3bf38258e4b179425505 3276d9700788dbc1a7d5fd35bf82e99a26827f9ee58c8f0df65cd42d01647091 e56350680d8dd4cd279448fc660d8c56694cf671386f17b4b40395baa3237195 4bbaba9ca29f0f7c30616cb775777744ad5b811a13ddb279a899999ee8387266 99f9dd28972e814b0be099d452f00da2fe1eeaeb92f28e632f0a06f73e5119ee 7e29ea0c205c42092eac957e99c5e3f6ce714f20415d7e99e20a20356ab3d211 b57f0e8e9047388a3d509b305a6609225316af5fe036f1e42a9a8392ef2b7704 df97809fe7dac57bf6f8a97beca6074b41ef328ed0ccf426f5d4930b1506cdde d66b70e6517ba71de001909301354336a5545beba841604c85d4d4616166249d afe58ff25129c8034ac4b1d6928def7250963d8587ff3cfd6a6dd615ca585184 ebf342d4bdb18d626dd91e7c46971a3ef33a9f9aaa6c35268f92eb47c79ee424 3dedacb87788fd74f2c0666949ddceeaf3c4f8469c7d138266c9a452785bcd50 1fc3d4104b7291d6e7a99017e6686b531ef74d631365626177d419671d2dec04 29 635677628b68c666625809b6d8ea15dc78d2079a4cd1d757219633a0733511062d60bdcb35805d9ebbbfa174ea4d2885f69c90bd6cb280e5de0dc5ff77bef70607ccc403bb7863b0899ef3b53cb766e17859ccd2665165fe72ceed46de416d0864a35fba1ecc39ac4e49a04b161d9a873178b7631c34c681237e86ac3ab76b04b32e8e4ea522d845cebdd4068343cf04a02d0173bde7ad3b29ab21545ff8a20cfda2f6ce4491d217eeaf60135e3a0bea24162cc9fcb4a2ddb23786f5c13bbf09982d2a4ec54f7d60125dfaf6349d72e2c6ab2fe3a32f37a11e7de42c500a31041de0e1914c690b92c3f5324501e60b2e61324f8e7091fedc7c68039232950503ba0c235525f34ce5cdd16afda7a533241054d529477676dd268397731e0e6e0bdbb4455ed6fdb76f960e32fa3d6d0abd7b79f7df9b88a98f77369ad169b01e08f7ed9bf879fd8e702aa40e57ea3af82704cb34f94eeff9784c99ef87c1e5a3006338db21d032774a699b668dfea5f67bcab6ea497850bd47ea19ef824227130dc35b04ea4d62418bb0a2316b35e6f3f9fca6134770ba19c764cca012d12cb408d0f57b92f5901aff66b2740bc0d0ef372918e7d6d47ffbbdca1eff037468f0047ae9dcbd9639adc493fd731eb580a9bde73dfc219fa5261b99b645e395b50f0f67585df77667a2c916ea27e2f4b109b63b3bcca1ac00d6f0064ed498f0cb600e6e5cf3cad53f172be585f8510289e6ad8e2f38cc6236cdfd2c1a42018e23c80876554589cf41495096f14f1661f9bc9460a77a69b6bd2f34e38335751e4e3a0c5f23695cc0ef26c8b58a9db1b77b782c1c31b2fd7c81f7ae921581d7c8aba50153ecdccfd240fa27a4cd3b7f72256c2f9e7d8e6d72afe49931f2619753b9a9048c57923e5b3c260ef1b4f85f819f942e0c0be341c20218877a8e85f58c820a0329d04ae5dddb849bc19c54543a084fa74443e912c0a1b1bedebe126fd169be0a93ae3bdf69384571f9365024de9d77b5526a10f6f4f663c09a40f7690cb815033a7ea594356b07b6e7a9777852e55de23be68377f4c3259f01a14ce82da0a80f6a0e586ef649f319b2bc42bb1bdcd3e9747a3ace381e6d1cdf17994d9d828f0df2def8bc857d91e4a91a6a9054ca8d93b12692f07b956bcd6fa6ea1ca064aa0a6320a4489cf75070a3be9b85eaf9ffbadad2abf489ef5d3478c23f62d60b330dfbdd33a6987aeb7c657b90980dff567cbe00416dcff4e5647c953c5bd3abe60857e82aa2117905e81d306c5007973aa9d363f425235d2143f3f23acfb70e3303e9ad9888a580e146a897ab04bed8297752c19fdd65518c998ede4531f748e5096e34fce0e57f9efce35592ccaa2f16a19e6f545109a42a090ff91ef818d0f005f7d5f685817682a40c70c1608794d8cb52b33c7f35331baf5db7420da07536015cffd83a8eafd216467b7283e6f427078c7d04b7a229e96c348afeb1c62c2107c290d92e73f63576ea48d791bce9438dea578709efbd9052933021c812339d07f511d524766528dff8423ed4c39af5d29a6463d4976eb89b1270a593caa80f0934e088ca31b3f1aaa86897d6fcb25918b50b7dd7e4043711b447df4f1bd0ae07da6cc609ca5459bf38b35c7cf4bc7477ca70a7f7a8437e50d84bcf72e972a40bb51d78ea24ccc0b8810fc34e4447ad00e73ffb86d81af0f46740a848c9a0f604e3c81243d7554ab1d3fe21c1dddc799e93e73a962ce9e22d98f6c4532a09a307959a348a2f28610c16445c8ec96f8f469d6c98948d5a6a8a6dccc0829c06a007820d8103533d00db99cf403e46a583de1e46a3ed1a23214c6b5901c8a4e51d0f99178194cb1b244c92f2faf4511a8a69c07fdef5fdabe2b9ded48e501b639109e93af1a22b10bf5b3f5c036f4e49a5856dfa2f4295d3e81980c24c8223dcb703c96ca1651fc4ebe855c235d7eceade6932ea25d2cee230660d494518efbe060e605d1231261888e7c950cacf3ac3abf8f29aff0948b38ee0feb7d288d060cc0d1561e4510058b33e17f4a11913d0049683040689bd65168b093a20eb720298043b216475d6a17f195a39122b165da3a8fc675f648a74b90c66ee937b6afb5804023717a792c77bcdb0f633e7d0bb4b7f69377fb80bb923234c9bb066a969af050a6db4549eb19d9cb12995e93faae67205aaa77e9beb6d87807a93136bbd020fefb7b2c1ada259bad481255255651697d68fdc0ad6169deb022ee16f32c60607e593c202ccd0fd198a907a9f0666f7ec7a8f5006715e8d13a43a0d84149f21099cab1c80c9149112bd1c9c8abd842594c99351d9eb8bce0350bd356a689bac0e68863d218419b5e5ee434833c517db618249804adf8e45142fcc4ba283dc590c741f177ced8c857cf70136ff2006703c09fe9340bc4885c095ad7e1c11abcd04780cfbdfbdc48d893437a64c3b348911eb7505febe9d0b9096c40311518c0208b3f587c56aeb9f670114ded8bba027821230732c185af45fac658d9d47440108e69b72236e8407ff9fa01a47f25693a41564c0f6eb9ec9b1a90bcef1e9e6200597eeaef690577d82d2f026cc3aad22f55112a31d165fbfb2a78067124be7ea0f4eb8f372224c02b6d9288042a716cfdd9cda92995ca6b3f8bdf99b552c063c061731fdad13c576509897e9f44e9bea20b7d1962c61e89388ee8af723097b7300 +generate_ring_signature f1b4343f88ea1b6e547127c9e9c270d665b787ecc36163a8e5750e2a8b701d8b e6cca0926bbfdb2ea145c1bedccab485c8e7008c5d10e4f94e39840febb35c63 2 9710e17587a3bcd6c0e2e58e22a8fba827e543169dd0d2d2cecb1b4902047168 5c34eba1f00e35c63df100c935796b648f8eb455e7723e94113247861886e94c df2825cd527ff171578a193e1597ded108b4dfa440d8030eb29281d8bd186905 1 0d147d3569fd6dd091a5853855160a6240d9d455141bfa7201387fb4e19657002b7d218c154595757d94b33e883e62947033d197bd50524e796a97471e1feb06376244426198149f7ea324cfd136c7ad9f6d8dff5539f5dae706511ea4804d0fef6668b2ebad2b6b4beae426ddd29db84ef1473b3b9122c92825021a0f5e3809 +generate_ring_signature 23a332b9d9020655cf48889f70368221df8dbc140b66d871dae2d8aaa8fc6635 9cc36ea3b5bc7b4c0749d4fd570fad4413bbf84e344b05c2a7723980cea12e1c 1 96e0bbaa0db8d3c753ddd4bfb9b008d8d004ec0cf29f0153d6a66ddcb7b9f183 141e79e89b44ec52dd8270e8df78882029575ece0a386fa90d8f6a9429c94107 0 b1a52644de2492d6da7ba9147d4dc03665b22f857c19a22be96a6fe5c8590404d54cb4b524158cf4900a8cd8c1f662e7ee433a1707abf50c5a88918f72e1e900 +generate_ring_signature e5c2e75ba3f64056427af8ff25d05aba008e9eeb832ee5dbda6581c2e1ef4140 b54b57b46ea9e264ac78fe44272f0d823875ba39b11a8aa0ca4a3f96d40a81e4 1 7d6747f8928d81565fccf5f1f74703199618d07dc794a68cf28a63e6a943234f 496085b368ec9b7abd26270c4f531a66bbdd14e3a875bbfc0c9af583fe6f5700 0 8d236e3015fa9e9e08b2504a0e4a6d2efcbc61f3ed856f0b8cc8529f0343fa08fd3376b28fe632c6b6fc9330394e50c558b27968d3c139d6bc323d3e850f400a +generate_ring_signature c8c61355d22d90bede732e167d7cf79f125c1900f741766fe4725eece922c7ca 0e732e5d1af1723be03fc79d6ac2c5adcc829e66e4ecd98b17a048c6f4faad9d 11 2692adc832851f961763ac67bb23970f802cb9e3b3e84b476eaa179557d2b42a e10f25acec70306448a9f4cc2d3a96088942c81bc4484f73dc5de9acf502350a 10bb709081827b8e79155e74ababd3493c31aea3b21d87f33ad200640a0d5a4a 2e6c4ddd0d43bf7bab89000809f8ce0a6d9441774910d15818bcfe55588ee57a 7c2f82c6dd5132845046b46c7cfed474b2819e6e7cd50e983bc717b9312683e5 92cf98620d0f14f2194097414f06b8387f31768861862f51dd4b9fe77a7e25ef e7ab08114e894da0d031f5f511f59a02f31243bda0c7b774e5ad5b28454d7761 d8f657c3315bb843c1d1035cca324d4cd0008a4b8d4276fe158a0b9c24a40b28 3da6ead6d99660527c36eb5c441c0f8e498812af0e32c984a398aa010977627f e87aa7d26b836595300a9af42671fbbd3b2941d5a10bee397e496d882d7dd820 469d56b99e0adf28615453a798e19eae331012da7c09abfc5fe5d903a29da5c9 053e42af6cd9735e7718c39deeae22f88feb1a6573283c1f7687eac61e48d805 10 f6db193f5dbb10d02804521286dd04fbaa78931823b8c48798e9a30a52366a0da9ba85bd222769b5edc9f3ada37d7ca1051e08c736ff4a637c3ca81ebafd5b0be6014380d3bab1e72c2e82c846efd61e7da6db148821a25f236b489d726b6e0cd511c33e31175f68a6a5f4f690316d0ba3bbac2f0fc273d590c41f377aaeec0260e53411f80ba4a999c36a5f4c577db1bd1672e1d6f5c2a943cced72ecd15e0e12a4802a5f1480681da2a4fd2da7d6f6fc42683836142462a0c52daefdcca30da6d98991274b5524104bc1371cfa8bbca0a1d72bf6304d31eaaf9d78707fe900fdfa1a7f32da871a15d80b03c89d501fafb075d323ecfae02824f33ad056ce0349aca87ced498313faa97be33ad1ce3fa9b84ebdad0096d20b78ad6ce1ca7701ed6d215d18259593481f959ecd79c7ac4724b87a818606c97a4fa9cf306cd10f9329d140f63f3a30934f411a96e3e0106508fa3f169df705cd7599c14b91c902fb5e3a2947fc7497f86a3f6ee08675d0164c838c010d1c5b3fb93b5d45904e034960048aaf1bb9574539272be178bb0184d9c722a781614f56a674e5f95beb0d80c337327076105037c829e24650b92c128cdf6bf360c6f595d7fd7eb8028a083a2324b5529bfde8d966451da5e36ae52d1d49b6108536d25bfff0ceb81e1300c5d85b1528882e0d771cdfb70d1f57f588e8e6d8380a24dfda05876243efc100cbeeb3a4290cff8347ea345e8251aeb5c5ad854c007953d66cba6da20b0b6908c459ed24e6375ca2d7a714ffff0b19c2063b1920aebee69370cf02f44ce422033af8c8d7b5cd634493611bd908e9d52547e93bcd1f3e6e3be4d6912748bc6a01f7c93b0efd27f68de17951f8037c3d191ea6146ea52edfeedcf51c7d699a21046b2c4c696a160fc7cf87866c2f90887507f8e3c462ed449326b1b5de9a404c07515f6a418aa4357a9022b0339b5ff256ba49253b2a3f33d0af602075d24ee40b +generate_ring_signature 48e20730307834a62d7db0e98e04be4206a3ad08cc4a35e7f344a95b5ddc7db9 795466acc6ec73cb2d8e23264e21615bf73bd79c4f2bba9806a1eebeca62a3e4 1 1568d7dbfaaa9dbb1f525b25460317c2bad8e408d6cb09bd61694ad4eace5c76 c8aa624d4f70f889077ed7be97b99eab199856f627a52bff88d0add5bd929c08 0 7001c55eb3a466e71272bcc3b3d0c740ca19d3ad09dd16bb7b6060e92c09f20ba3ed0c0a381c238ab127c10c2ff3c32012e344cc03a658b9d1967e87613d2f06 +generate_ring_signature 8a503388fe6567c73abc08cc3db1c1a514c1a18fd1c7c326d0ecd227fa4e77a2 5a14d29f88482a9bb8b893b1979576a67a50adb5201c0986123f10b7d19665e9 28 7f6e15fbcb1c6e35febf4fe4967af0d5ab3d7e69c6e92af4cba8389c19421bc6 de95c58d0aacac8c10677a95a51d1bee6c7b64675fddd552ab4a6071c15ed69b 642d3622fd6fbcb1fa5da6b5f4c66261ac72e0e0d8a433674065b25ecb70b4d7 b04bcddb6551dfefad4d4b24be5979685650cfd8b851dbf173499f03574253c3 d72100cfa6b2120078e263eccd28595625e21998b668be2f1b5dd06c8bc9ebb6 f048c1fce37c23c50dcb21c6bdf4fa5e2fff95c79d4fcea6d5e793fca043e366 54e34257048fd9f1e29f90612a1e123579d0482467b9d77d9d4c0a8683e00cd7 10305975c56e7fb45a7d91e79bb32647f1fcd71a226bb987f3c17bcca58b4257 34db135369264404d3d37fa5137f567ad1d2ed445f9598f35f74e9f421d18564 d1a007a9067df8a0163de14c44edb995cb46216be986ff1f71553f2f00d82804 9e0d33eef94b86df2eb5b2f9b020504c13d7e9c122a73d5b72e809ea9011b7de 95829862bfe623c8924c49d42f317245e2266d79793d6b9497a4c0b42f61df1e 6fe3015c4b73fa1625751fe61b986538addcddc1624a2666230b0168a9656d61 59c0dacabecd19b83bc6ad9670e62b2c22405920bd9d693e1dcee419ea75d4f8 799f2c152dd30d409d04bfbaaaaf28922eed59b2127f0e3324cc5693cd0a9622 9dccd8200188716de3f4d865bdd299f1d4a6cc24b6894be1a2494e6bc795fcf3 fe83e0e7df91fad8521869e3628db7b3dc62d2cab077128d894eb71f303a2e7a aa2fff6f1b0c0e017af5502fc0a4d692ed0e8d6f2b7f42ed04329941d274bb75 124a459fd4674957fcc37f8b2ae8e794029cd8407b8523632f6ac1bd719af8a8 2ab89d3cc110387d8f0782d6e188be865d863ea5f7f47db23c8e501099fb4c57 c34bc2836839a6092a3b6810dfee62ac8d9fc25dc46ce52913afb9e5ae05c4cb 7e328e36efd51c7a3fb6372f94753fd148b2509adcb5cee03d0d92396fa8a774 fbfc880328863a67ebce47871df1efa5148bc9a6d173205fa5ea67b4612ef0a7 694cf8a3d245b00abfcc8cb80dd28f2e96b50947dee41efe37f1c2e7c1502a69 0f5177e8743e39f31e4369495c2e5555f0a2d99819b40eec7fbca14b8c896a25 a471e33303a8a2bc6da1759f05d56a3ad5511aa5214e770de7a77b58cc1fc324 ac0a9534ff021ffe7cdb8d91ae8f48d4892badbc017bec4606c50db9c6b8b3cb 8ab17971784546728f93e4d721050b6ff331c4a281ea7956352df439b8ce7953 2d98f06fb2b1ea6303e595eb1dfccdcec971c947cecdcd5eb92ea5b1f529c00a 19 4b9c99985bc23efb5ec0aba9bf8a502f77dd11d0892796f60eca9424dba9250caca45511f49851f4f9297a5ae9a64542aef477e3c94b868cd2dc8d5a3f337706d967ec3ef48c5188e203d3019047e4f1009561ad156f57202dc1196ad621c80db973095f48ff4e3364d81cb9d14f78115237fda1a72c7c311427af4f55aa0202ef78db049d20794a030568975f16cc745b2ae61a8371cc057b014829ac92ff0ca0690d251072d87cf612f60449e4500bab0dfc5295c3644a706b56555d398d07657fda07ccd87835227e31a95972e3b79d5800c8187b26438c911b84fd861004dd439e01f11d775ab9bec5feeb4a6d457db56dc8014f6b12d619b1742c164a0e53fe7df4c84fa64fa63b5e8fc7304a685eb325fdc000e1a5c00972d73f45300fb7e11e7ddc58fb21c4c9b480fd902d8b463a9257431eb15f45ccb7fb8fa01206de80462477c344df4cc6b36e06f11d5d63ec6f3972d031c6f98c0022ab68b301d0f08b0b7e20ba32dbf24ea0c5d85571c7694eae1694d62838f17e1215e7300e65e150282e959d505cd84f556dd931298339981d9c9ab3ef82f1279dc6072f0c2968dabc5785d972a3919bbe64a647a3450ae418aa8c7a645473b380ff1e1c0ba489f57d263e611a6eccbea135a4fc8d94c5bb0d4c4a046298b3d17989151b06b24eab1ee27f83ce529cf098763af8ac41dd95b170851e910adb45910da9f00662a73e0515c82b2123faf944a8f83eb8edad3738a85d4161f057559e477edb063b973fbcd8fb7e8245eda525dcc88dca15d5eaf892b02b03d5be2d33e8328c0c3f2827b7c711ba835424a4c530105dfd4c33152df4c78fe9213930e526a2e407fa445669c0f54a30180fe5f382751fdcd340959851ac18c5d50b0b1e24ee400a4e13fd437bf84a9191cbc5de505bf7a2dc086f109fc2d2d25f4a9ef8ee723309c68542cec0e1473c1204e78a099cc7937575bb3e725d14f9da148280bb9aa8063f528634925047f019c5df5d81e580682a2ef25aa68bd1bbd01e32b0594be70fb7e23789d1ea98b8f85a27163c8d15f9fdfb1ddbc28d638ac9c6b4909f70a70e49f73caf557cd0f1dc731c0baf6897239fbcc01e552c347580164581df6e9a09846c6d5a9e71e4ed3f15e3c22212026f70f69c7508116d129034d3f1fe96380b43acdb801b422e37c26ae83490111d54c4f2f10e1bf1293466c924f1a4af58009079d950b77cc482596b2789b07a3c84bd483bdf63d1a9c0f2d2c8d5aced4f060f5ab4497c21b1a4f0fff38b56029ec0db3c5e462b03dc07af38dd157080140112f929397b37022f79d4dee0435d64fe22f93f32824db658938bf80cc24a320f0d018d7064ae92fb3ca26c8025f226c6b2eaa95e4cff7ca81be04534214f750ddd3a4c258a59a85740897e5b7ac8596c10dfb97903b8bb7547d0c7ecb0d6dc0c6f91f90f6bb252d6ab38d2c4522c83a8a95b4d8e6a78ea12f8dc9b5503f8e6006b5f34481dfbdee6f1fceec18b16f405254b59a5091d9d0782cb379e4651d10dbc98ed0657aed24f4c64338f27f32bd445b59f2580bb56e7a97efe0840298b091b89169c4649153e72ae5a5952c3ad8c03761a894e097b079ca4f3e2c2f09d0485694646a211d8f9377d7d2f47a032c7a05253c9d684c27f0c59e7ab534d7a01e666c51e05cee7f462c9a4a560b599687869e245c5130b643295649e4aca98077d6d5d8996871bc466a1a259cc3ac7b7e81e287a24bc1e4bbc588128d61f870e73448349ffdb3782c9279c1bdfece3a1d919a5b5b71145efe37a5ad5353f42062802c541aad77b6f1a1fadedd7674ee895a6cde2d0950060f4cdf79cb500180d7a182f9378cdc2875760c5eb84b7f31c0597aeaaedf9432f6eb466cad3048706c1d076c0c5198f6a6b004ebfecc316668d0b70234ecda349e6bfcf8635e7df0d3540778079e807126e73958fb9b60645a2e6f3358484b38c5d9402bc45108a00e32a2242ce5db6404ddaeb79f7c8a7bcd51857cf2c3b89d6dc39a7be9e5b2c02a3d50025652e97398e3c7a4a522700dde46c8f2bcd94d8fd392149f1b1b15a05631a28ebf07bb9fd83c0be05fbade1eafea4aa468623f31175b6bb7f889e62058a3d3228143089bc8cdebd86b223fe7ffbae2dfe30443982216bc4b83a2fdd00abe86eb98639094b3dd8471f7527c61900b183be43aa4baf07f35ddc2cd8a20766f64de0e0bcb787403be1967766bde7a44dd6280c2cf9ae650afcd41ee68808d733893ef3b5513f0039a6d4ded0c51e0cd68c4a72b6101571c3774f7b1a690ab110b78b4a3145ffe94b6dafddfd73980b6fafcd9867c60c8b5d162d0f81a30c8515f6349649cc970723b2bebf31e511bf106d52eecf37a772bf360208ec3c07869327ceca72ddd4a56f16d4d3fc8d52a6041702f6b96cb40ce3cb71f226bf0c14a45847006a490bb78e869d3aaf6a419208994634ae68dac4da804e6b3684079f3de3a33fd029b8c22c1d403d2d3ecec994e21da33409cc10eb5a4ff9d71f09 +generate_ring_signature 6ac74ec78d1272baaeae23cc47283fbb7884205052ceab5ff1db3e719ba29c6b 5bc004015c33f613e0148bbfdf1bd63437175ddb0a00763dddfb4b47c788304c 5 76432ab8311b552f78778e21bae025508f4316b5be633ac2cb8817f71dda774a 848425333f96aa63c3f66f6e69bfd15912536e4ee877cb1ec7ad212ac28d69ab a5ad98899069a864134770cce5007238ddecefe790dfe13cc4d4088af13220cf 08ee789e594211def5a3c1424742b628f8455b10063b4d605826fc0ff6f9f0c0 f100326d6c5eb3e1991fd21f7d347411cf403b00ccff1f265c98666150c23f9c db0846d03e554a5dfd81724ae0e6aaa4f6d7c979babe9fbcb53c7d9d9caa5f01 2 492daddb0c96b50ce24dd3ca67acf3ee4eb1a06bba910405275b8261bab72605cdfdddc1b5f471116976804af5361c9cecfe2f111b697453c994038cda7ea507f3639b094f6a893d7d3dbd563b9ae5ed7730a8e2cc882025760d731babac63091a87a5b18d543a9db8eef93df338acacede5cfcf4055e20a27f893b970242f0ad62faa35c67cf74579339e7f12a9c2358401967768a11646bf1e4416cf11fb0de3f559ea0a22e50ee48d5632b4293cdbe9e16436f2b60a9e74400ae48e783307419ea52d41a5f2ebfb2d3db13e4e376680171a0831ed413e9b2fbdce5072350a3269a21fd22d987d283626bfc6f875854915c03348bb729ac89d02aba9241f09683e0225d80667e261c623617b37e0cd0755b8f6394617b9cf20eb50d323cc0f2395e1c150167768c2d39d52b6b2786a456d34218430374ebea428498a74300a +generate_ring_signature 7a716c7e4aa2b7d0fa1a90c525261ce4ebb259cecf2389381fd58be21d865a3f 1b4cf91c10d1d603876cf41169fe8483c6c349504b0223d1368e716976fad61f 1 c2695ad2d92c1baa8a784e9997903c98c44cd62eb376292f70902acc11277de9 a1f09e996cb15570132f12326cc7d0cd2a543dd3a73914d5fa29df6f099b580c 0 95fec40ebe897cc7d9831dbe983d5feeb9457eb60980904c75f0cc50c854f20bab4c4a6136e10f5dcf0422907bd67e4c459173c5439090a129afebb33ab0070c +generate_ring_signature 5cfffafdcb9413be4fd37ff5033ffdf0f76de67e33890ee77f13d32719e8bc6f e613d79ce5e560d2d1ebae6e9d4efcf3c932564d40b79f8ecca5be457b4fe67f 1 33cc83f4e5f671b97102dad9d22add061cbf33d8ebb9fa27d4bcd14a989c1bf3 4e67ffec413df43231448148c8008be094724cf80f7a1744184af13555da1e03 0 c48686991274468f9186e137b97b5105f5defe90f34da430c3813444347fd10739a3595b8856157a9048751d73235c7d2e64b734fa4a90f0272aa4bd621cac06 +generate_ring_signature b243bec50802e71c29415c0248459b262c010646c72703c7d9f2122e062c6ac2 6cd8bf899ccf55c20eb9bfc4851db1715aa17861feef8b4da173b538cdcc04ad 2 6c8204ccdede7d45e500631a231339be57748af07ffad380a92ce3313137bc74 d33e501016e38068cf7d22423bd8c0990d3cde4627b595b7675ca92cf61fdbea a09f8146e59e1054cdc68f895dd2c34a859fb60d1fb31d905c56349cf058520a 0 99480d4bd21503c0ed031931714be3ca5280f4c09465e08abb859acac9da7c0d14fd47dd6c33eb3faba0560c37f6639c87c8359f592cd902c0fd8116f4c09e0abb923f21afe50007f3426013fd78172176ff3131b79cbdc485c079838ac754089e7a0d3a78b035f10a16ad7f0ed86eb19495cf14d0bd3095f88e3ed17f299c07 +generate_ring_signature c982ec04b08403ee015c04670c528587bb08062a7b63b4f6bccd26e9e64eb3dd ab2ea869b47b0ba9068280eb49541d3d3da9a26eb1a80b02896c88dd8c42d4b2 2 2829d2f2f868258edb4cb265aec30d9dc5e5be166b958a972600e5567c98615f 59d384b5f3b8788db99ba01f0d050ffd6064e186151d2e31a553eb08b71dce26 dc9018c1fe984cdda01a91c564deaa7f2a6a9d14f29c9062b99e8c3f36a22e0b 1 c1e4e56bcb55d21b2c27c2a9f538dda9e0b03e9be260aed6916046e24da3db06add46621482b9f7a6017a6938ad53d79b5d06c406ba653fdcb1252d69607250eef1f49ea378b20112525c996f72762d797bc2702fb47f5269b10a38fd6991a0b5012709153e270e1ff268674f60e5fe11e6740fc0d653fe20313c8598010d30d +generate_ring_signature 3780b00ce790bb76551837f04a5d56327d212984e2e84026317ccd150142a955 59d69fd2a4cba1b41b50762104efa0352fb09692dc3a744d9bfd9f182d7989cd 23 d869f847586fc6ff4e4d26625505c39810cc10183513ab20ae2caab4448a0429 f50673b239286c2843b43a9aa8f60a2de08e5a77b7e56777d6ef84a5b6b546e6 934cfc3a635378c9af0b7e2f1b9986783276b0dd845c3b0c56ecda1b29b28bd7 a3ea155ae6fab4376012a4018055b62409d778043ba6d678e6a01ce99ffa0215 cb9c3ae454bd161c9d1391a6310b3b7b391bb34851c1720cea7925614177f278 19004aac067984cb6aed7e63176be709461d4642a63ba07cf29540e840059963 deb37b16d52197fe8aa45118be29c3df2a5f393df4b126bf9439cd911802fd63 d000b1039d64ea942a3a7a82df6dacd633b4ae90023a94ebd5e13310af683d04 5e368db66335d4844bbb0e25a46d2708854cc628964fe35c7eb2400692a6a0ea 61ad924cae30544288024f8ddfeb0811c2a5cd702345884a9a7fcc80d11aed92 f450a04d8220bb99730958a5ca4b7b5ae9dc755559da17de7325ad60d80b4f44 2bf508e304d2dfdfd9a3853a5e2a5d589b910884a9267458678333e8a3818a71 a45005a7aa3a9e088ebfcb88b3eebb3f88b3f7951e243d7d8db9003edca9f0d9 0effa19fe799ede54bff803777db9edc21464cb1d6a8001bbb2933214b5ed3a2 824935d63c05a55bc5b681953b5afb033005752c35a5e1fcc95d2341640f532d 11107b3a54c212b610845db2038ec392866d432fa11d21b4293ad1ebd421fa85 9ef7c5311eec1acf73da95f366c10dcc55b9080498241ae448cd36b515695ddc 59b031e8598db111c81ea0acf72d372298a9b62e508013d83cbf0d95ffd72fc8 fc6fb0d623db98a47e30640eb91c8c26c3c2ce203b85c0632c686e923f2bfb58 114935937d2522ca025aa7ed9494bd218eb793101ae26c861b54141fa7c1c157 1cb95426331709640b313499307d13621c703d7bbe44a196819e192422a9211e 81bb591d9b8d7617944a30828bb3e674609b98360bbb85e1f9b80b779453ae63 5c894adda35bd100e7c6b1879cb9fd8cbae04c271e2e59702508d371c15f1803 3e9fe0cffa51d7e27bbd0adbb173c5288edbfe7b6949e9b292a6d1e63dba100d 5 b9d37cb510125285811e5267b555d2bd92c2b9c6113ccaa40c48278320a3cc010ba1746accbbc29ab64c8acd7295ca0a85d5a1cf7cf2f7dee39f2a6f33a12902c852541ba51e1086a05877441065409a4f5d6ebce45ab4d04597dedbaa66ed0842278c9e1495a6737f548f0e60513ef87cbd5e83a999366a65034a837f23f70d9a460c66db3b581ba4f1cad1f12567abd79a611f3a8e8abbb3c6691afd9c1f0b12a5e895e84009895741904574b6462e2e4409b840195b1c9ee3e336d32f0e0a9b297dbd8e6fa1ce0c18d016f15ed56c89d4e4d232decd24d479db5310524a06c271289de8bb53e1f4036235d750eea842f8b7108868dfe7de4589d58af0030c69bf749c3ad8a6904205bfec68b70042421ac8969d0be2d3d365d094999b0b00d35f321899a5feb5d46354efd8299572324dc9d53dcf89009774fe8168fce4018b9efc92e56143ecb027cf02bf799515418ea72f329754e6777cf1a889247e09c88a7b9b28551c127d5942c81860898efb82b74f41b803001c67af6b5b69d307b51aa8af10474f834a1f30cd427db59f3d7b748776bc10e841878e8289336c0d477d356c50cecd0f07844f5d4d064bd9319b2e3f57c0b522820f03f8c0b29e027262bcd2a0177869ca9953e948725cee62886e8b8f66c6e17405529bcc85580ff46065b2718ebf67b6370d0e1d1ebf8a39169d16e9e0d511626c543f08c49000f240a171460f5efa88123eecb7d4492fb5e47b25f64d2bf116631358956f960b456a8af53c43b353c6a490846e003463cc85a848d3f40a8ce1bf48a9d914440e81d49e9c68a1f37fa45d30683d3ceba1cdb6b5d78e6d1bbb666d555d29e85c0e323f3d7fd990490ca7c3a4a76c4648f972e6c0f2f6def30c138f38133d45dd0928b5ba8b1245afdd7cbdae48f35a78f5ee530a8e78c79b41e72c283e277bb60f491214db93017e284a85497b6f6230889067a4d010b1a758afa9e4d7b4694a01cfbe9df0d79d403a0c2bb70c7f196697a6ea307e2ec8804618b080a28354c70fcdecef45ab423052d18ad53f608de76438073fe90bb424ede8e1fde0bafed105864d0dcd1f03e180a844ead462e67f3ed2cc8064fd66c872f2b4982827b82e0282a8f2969fdf79acce7715e5e4071f01f5e8450ab526ab2be63a69bdc7b4b00508d6f30532d0a41036893706d178eba5d351c0cb937ba0131e50d821e49e4d09076d479be6696130a4b267fd9737b088b174b1b5d81eb2927ac6500ed2960509ffcd43a3c96ba6fcfe8b412bca27a8ba5229887b5e7350a43b3312595f68bd0038cf35acdcf53bf246cc14ed52dd481ad2b7f6c9d6896496a9c228ea440e52024c5ad8c0cd0f338d084da20d33569c3cc819ab703c6247243190bbb24645100322953f07e891894f1106fd501be3f6ec8e61476df5d0a2e0e40f32b9b5d1150bb833da68bb63e46a98c13d7c01340fb2000ed25c98b53a23097651008e255009424063e382e0adc9351ed5530c99b2a074a7e7155cd43d781a3a22f502a2ba000e8527decb9cf254450449686a4ecbb0b574b37355cb53850c20f1111d48b10f3d5a8e284899b88e6b5f39f744c41e7fcf9c0f4815045624626015bd2873110d4d07b868209a3f8b5ba518f814a8ec889d9a6ff720f33ff4852237435bcdb2062f9bec558e611fbf81bd461dff64df2e3b349bbfe73af651c2f32a994d5a9d0d35cc65decb590d78fcf51843e3c16797e3f22ec4fed7d81299728259d3ee4602dd6246be985cf846d4e3b8b07e79912fb89d66c2ce5f3f4d97b33938944f2a01127376f9938eaf0a19bd138614d8e035004984a9e9e9763cfa1d53bc17e4d802481dc034231785c9b5df30b650972c204e8f2e51082046ba299fb601053b2a04ab93d0a66a1e160fe4083776902d99bbece6b915fb80a3d79911387eecfb5009a6a52affc0c61dd8cb1eb440c8f2755b1438d84f0d965e63bc858c098cf7bc0996305fd077e95326419eda04231c219ae7679f52424bdd58bacc315994a76f03d3c6f1c98eae24719acd88125a0124888873ad91ec2d77df04c8d94e5d80cf03 +generate_ring_signature 4b921f3b7760d14b0d0ef806bd36dab9d50a9451443207677ea8aadc68f98060 e55fd5fc12c933bb0491d185c92c3cd2468fa3367cec56f12793c110b8a200c6 1 b52dab33b6d947a4eb79ae7ecad089d9f95d36d393100941adc58de108561d14 4bfdc68e0801363ae8a10da362c33fb5fedd0cbbcb55ede88c4befead533010f 0 02e8d6d904997f14485faa493004e5f0bd4d9db5cd651bd43a6bdeb59cd96602c8b16aebb569c7d69aff37dc7e5ad50f825e023b94ba6de3f871d295d9501908 +generate_ring_signature d1bb6f2f089328c8a0d25d11b619dc7d22210fb7ee16386aedcbf2c762bed603 e0db8ab798873c85da06e2937d5a1b19d4edb2a6dc91a446f4d04768f7077024 7 3f1daa085f45801447bb58464b4f9e872a3a7f8907efaf3edfec817f3e0b81fd 70eea5bb1a328031ac68f239b7a0d5b41bf18b6276d579bfdbdd381bf47c71c8 4464dfca3374f6cd633001a9e66e89e6f8628373c794ac09497e9273df2dcead 2e47020c5e25493ebe80810917385b45bffc62fa005c89a2c3a2739e9d88a17d ef47e1105577edb7174cf0aec3a95d4d1ae3988f8d51b67b0b6e1a028ff4d591 cbf59fe210e7103ef5b0a5e1b69ac413cd4baa76389d2471421218b25dc100c1 9d587363aef07867ffd9d079291c279f7014eaecfa729bbbf0c9344da50519dc 745bc876d42f13eb7e796287728821f7014bb616432994e3fb1b7e2a98d90902 4 e1412b2673060a777a0c5eb901992a79e7f03dd61f1edd9097da5c4d2bd60503d5cda1ff74a55a66337a6bbacfa836bb8997fee7f3d48cb42b72655a61c90401fb4901513531bfa7f2bd96482de3995f94d1ecabd5de8dba046d187ad24f4904df43ce3c9175a44deee403fb5316905428ce549dcb552bde73fd607c06cbdd0cbd61b246b8a475ec17ed710919ff314cd824a20abf1b2da13f1ef6b001fe900e087b3081d1ca9cf22a67a456e4d7d91366751cd102c46ae34eb90ff8d44fbb0f9b11a00ee554650f0b6b682f1c3326d51a7cd84650e3080c3db0dd51175bed01f64dd99210cc3e8eab1f3ce6519b3e4d9c78ab2d8527166dd1ea8033338a820504acd2d2d5a504e641b33ef26385821cdc10b74280016645ce1d559ef48efa0c2c90badb39d8bd0305c9091193131fbf99952fd1ccaf7cb543a987752aeb2d0c7d46c05c52d7443a4f1c6163076d2e1ffbf16f9cc86031a809f90c70367de503636c06db19ccf042fa48adad7e9658a712086c2faf5f2bc9a07fa3d7cf0ecf0b43927d67245a748fbff6e222c3a8d5afe6d4ad16ac55aa1e6157c1bad9015e05e7baf567e577e098599397eb7e823075f8dce16edb7004be94406b0df04d420e +generate_ring_signature e02c19babf1b852adabb34319e5cbce57383c79faab9d5ed7bbd65a878fd90a2 d81e7e88991c1e15229d2df99a09c03a10e65933aadb5cbf3a111625e0e6e657 36 09ad534e9afa2f4c7f3a2b2618ffbd85f64484f63aa8e35060f48ee74cb9622c e6e0cbb3cbcfc057568bdc40ccd3b748c56461b7287d7413309820485a5c8eb8 71c96924ca31a37c55cdf59e1dd427c1843cae4f70805490eb5a471b0e15684c 4668e0dc303bc8f5d040ac0ebb17edb2b4a45960b9e8ab50365e4bf1bfb9a087 7638e69640b55b331940b90bec0c651b7f6c1622ea0974db707ba90e746545f9 aa89bd41cbdc50e08500696a47b7aa4ce9229a79249293f5a200a845460834ac e897f2947cc16cdd79999f238364c3021a146c3f2c065a2a93751c77f1af5c2b 1aab83fb0f3bf1573262c5f05974b5d894d55f41891fccbd3b1e6d45b18989ea c759e7ff18a45bdb6c3ba50e23ecca60af1670ecca1e251ace0867901dec7eac 9d4638db33f71a34f4d4f5e20cb42f4b51bb4960d75cff1f3171cdfbc7022537 6c920217f376662c567828d0ef6d0e448bca9a719d02e27382ab61cbdd1f7842 4c20656b7a6ab66e78918ed0927e7a576f1579db9e7e2555656aa589b0177561 a690ebe7a8637c998567889d5ecbb2b417cf5d44a103df2e2493060be68840c2 cfc1aa1ce28ef6ae9b2128a5573815f2d542525451b88b46ce6bc24c28cc7b2d 6e775dfc6d9819b9ea38c123c498135f5efc451e05542b69e417902f056cc165 5f159b75bcb15a9319397851d554c93fcc5dccae4b5763e648e5d13703d79bfe 2adea08b979b850ae3caadf814094b5ca6e378508d4e1c0a2d3a3ed60fc88b1c 20ae61ace0d3482a9efee161e53ce2b55e1993bbb6e4f6a3f0ff1f9d3b24c305 3983a77016fbe30e09bdb1db1a8668309f3bc5d5c0f9ab4f9085302afebaa9c7 e8d4e0f1bffe4bf1a9ebbfb9159ce5b1b9217610d3b654bead9ce461f4ff2542 52a2f1e910b2b09348e5dc61fab05c8f1018046ccb5dd3a05692cc1fcce86364 39313600d45be0006526b1e1eb60c6852f12062b5b79070e52f6d1853e44037b 35d29f2e7cd469415692f4fbbb5c6037606996c684c4623858cab0bb9882dcd0 50f00984cf21f298cb530322e37961655ff4564c95144a60cf404b95dee4a707 72c5aef3d82aca8e74514348acff468dbcad40d30145d7ea303e76219292073e 0732a814899f798987808f6dadb8395849a7819005c671e85fa952cc123afdf9 5888b94e01c472ed9d99305b6da34d2fd6ead234cfe32326bb260a13572ed5d8 c09992952e0cb68fa07534f938baf8ee161a17535fa1759b9afeeacb81082104 04136f50165cdb4fe6abf99e470df96ee0c6a6b11c31b6560ad42f10c1945311 f40dd049db5f5fe33b26de02e4ce067b110a1eac5a71007ae87e3325b4ffd041 2b89d9418f7d18483fa34b63f2a6a865cd47d650be39d90cedb176d530226975 9cc703c0de92c6e92ba475b31ffa2ae9972178dbe593fbeaf584216adb05b49b 6430c0100b2aa158ada6c6dc0db496b18757469aba4933065464eb10e38b06f7 12cedcf32a865d553f3633f475b3eb36fc8013a1c221417f88cb2351ff8b599f 5fd563b5a70cbd1f80255ad0f17cbe812b76d599a95868c04e0a0ca220089ee4 1500c653ed6b5ce74e0911b3c5603f685f86eafa40396c60ac679c34e935908d b00325f04679be1eb1ea6319b64185cdfebdfdd6c8dcdc89fa1391bf7c13a503 13 59315096ac2f28f674453e33c6aedf68515e0b6dcde5dfefc7fae7009ac8e90ece8020009c9b1078ec0697b2cc624db7165a7fd9a9ba0b59347abb956b6d9e0db41d7d0030d0a13bdca7a0fdb2e6a3d5737d2bb0bd1d4686893989dc18d3250a67fcc2040cd39cba9822f38fb540977ec6f8164dfd7f293cd49d9eec5e75100c07baaa86805fd5acf5ac3a9884c89acf89cf58768cd9ad5d4570706938ad5104ae32f67687b80a8961bb9ec7148e1eeca527ff61fb9838e97b6dc634f231660ceb1ea9d03b81846377a41553268fe3d2c048f5c333e5497505c0540e7afb62052a006de3ea7bfa7445873ba1733df1b9e85ff71b205698ec83ece56b1ddabb0737ed6796afee762302e714f3be2b311f10a0d72a4b358107c4951ed58c7a830f05c5719e7948dc35daa78dd72044aefc9a4973de00b37c92b984cf23b2372d0ff4baedfe217ed464c3232173f9e65e0593693d8446552d2e3ee1fc3c0e8e4205a7a55983e5e09835e9f5d90c6d7bb8ccf001af538febf9264ca7d6c6390351028ee241e4618896273a775f5d62994b21f274d6f605c2da4dea2abb477ee8e20fe44ff0a32dd39b8afa67a53c80eb9ed68e98db4f76c9be3d68e7bdb25924fd0fe6903775b4e7b4a3a33f5a2837b2d44e1975c0913a440680d091b578b8c4c50e04f352e2b8dbbdee4723599697ff9db3d2cb99f8a7d80769899af59e8f514f01d2fe66c2c60a0ec368de8d0992cf88aeb1a1874e065f81a70658be0e9267f80b2066bdec0602cdc1d8670c38fee63853e7419a1768bbd0c6b20583ce7541480633064a6ad1953b713d0b68938e565d14c671115d11b833565a96dafd54633c00958d21563e2e4b532769b2af3b959dc5cfa1b874af94017ecc8d859b6ded3d05aefac665e2ac21c489db4b1a4480c1ceb0aea17727d13f941537abe22d523f01358306e45fecd6e9f22ce22c289d67c585a2a3ac71660ce2c8088b6cad887c07017802b60d27214ac9bffec5436d0dcfeee97c066149d84f084bf42cbbdc8c0f83d90a478af2b41f27f74d9899b16986f8e1990784f80d75b181c8af7cce410d16be831ef8bc7a2e66edc5d9f53697168b00ef8cd2d38598529203e01ad9db047d13a6a85fe2d7e94c47ed92d7798f2c827ab8b89f77215122e185f39fe07f003fb6535d243f1d7b8e05d27da964f3263f169fca0fb2c3796a67ec5d979f3908ecb672cfe0d89c2d8cd4def5f0f787bc18a5cc75a931a0a9abbaba2aad53850322d997dfb00ae46e78d6b0da4e96dfe547b3bdc0263b1e3eb73424c779245409f3bc065bb2037c74837135f70a93b2838b246a348b43fe5c499c94a8d4a1790cbd41dda0ce5ca637bcd2fb90c71dc98029448ae0eebf5e689b5d8302af91cf08192945c240417ae12819f9e3759cd03beeb207b3d6b09b400cf9d541ea73f500499100793fbdf9d8b77d35d80eb995b39bc8da9dd91632dc5f0be60d5a93370ceff3781922ffa7be8c7dfb5494394afccee0fd18a9249e32856b177be8b6af0d367ad3730a491ae85cb3b1197199827c86ff778317023912b11ec33ce4f586080e183552ce9290ebbe03a6f3cfd8bd055517eec7b4ff2b94f587ed1edc281301ea020c3b642ed38bf1f299b6249a33239da5231c1f6c9f10602b4251afbc8a0195efddbef7a0261bbbc737499740c9e6d7861fe60a1ea6f972653b8f50982f0d1fdeb046c126fe27d4fe56acc91030369e855bdd6e983d81fa636fc29a6d5708226d63907c2237696f1aed31d7cba54504144311cba26611bfe75d4f8797a90d0a500f4eb38840679cf6ba83a3a61fb8f343dad2b3b0b823e1d911b0f395f502ca907e9e03e846d3e82c9b9cea205ece90420a07f0021b5886afa248be86740c947660e0e9157d34cabeab6f330b474e6b14ecedb70bafb25a9605a1b4d8a30e2998d44bab62d6fb75ccd74db84ac8b0548aaf1b667b6947dac2a55e92b63c0cc344cc2d39c5a34a6dd376c7b50ec78f1da1b39ca469e8ae47559877d5302f06542bc685e7eb10d9b029dd49f89cc2e357d32c794466e513b093c1e532802e0f8b6653a7bd809a71a92c8ea60f8f24238e0d17045336ce7c866714efe6fdab0e48dd7ebeb3d4522ef6b93d52392c885a45c8f6ca61dec973c21d3add421fae021f88679a29e541d1794d3a1691939d298f52462fe6129db19615d99230dede09b53919725baa2fa810c86929c2ddb6f5010a52a79ea98c7564538ce4dc6da805d8d61e44e3f54e93ef06beca9eed53c8ed78c2b44c007c6db175f6038c7e1403720587e0a73a7b2d0d35dc7456c7d0717e2ac03543f56326c83096f77200b40227e4da2f363e5a796f7aca69275d504ab8590ecce9256d234e8998d79d0e75067c0ae79084f9ffea60c87c1cd3e92e90565743f0506f372846905f5f0830dd0e107a5d479fc9e3177a5f55490e3045a57577c0b9afb97f24651824ef83903d0bf7683328e8b82b0ae50554710f11afde85e441cdc878ad81230cc42230e59f00b3e4e3a27b778941306b04b51182e0b30d05e0a131f261eada28bc9788409902f52774c90306e00be1299562df53979be1036b74a29df1c977549323d89d41051509ee86704b546e28846cf70426c45a24c02ee87ee24fe3d7ab1e25ab35130684c0a116205840a411863229f9759178b8ea7769646cd916f41cd9d79ced5b03aa03abf324b40eb370f59c95f0c28292ad97c7f7171bab1e911ae3a2858bb7071f2dda3246d85ea6df4660eec38404313fda6d02eee8f5457ba4ffb9afd8dd0eff9ba583223b1df7f9f7438a34d6282caa4338967e94e3bd10d74f00ce901a0249934e368567590f9cf4da4ba957d5e7066d883b28d66ba21183a427c83545005b53e5d72cd909bc3a503e725761a4082ff2cd959af2b85c0058f49fb14dfa0e1a0ad5bc88bf42c1433bd545afbf0e27db3b38015a05d159be76b28bf3174c0934ba868578219fdaacff832f693f6e7e7f47ebe130214b029a554a7bf49cec010feb9722228bb5b5403be38a10c69e250b69211cb30a40b5ec4ebf2f34cc010260ac6f66d615b83798511156d619351fb2731777a0a0dd83b09aef7753701705cdf0271995dbbf76e8dd3ede2ad03c2f106c27cdd8eab50e852356ffab507500f63df7e51bea2e50f6d01d6244528fb44060bfd4bbcde8f5f11183ddd639e308e9be56505eb1839e27e5d7cce5ba58cb02509727b74a3709dbea2406634c730e +generate_ring_signature 01ca5767c85e53284ec8996a17321a4af94a9230b60303ad2861c6dad24cf187 9abe98346aadebe9ac6b4c902132a37b0595203d019bd17286e06a4bf37f0aa9 26 c331412f0ca71b7a0b704eb524a60b07f7484e2917f77df63c85c3170aa2c62c 092c50c21561322315c2111dcf40bd2d348a8ae882393f001e390ab3409caeba ba498d7740f675720808a6019fce2cc2558b79fd2932302bb7643b41a7fead3e ef7da6c0c2e9604c28e197e5fddde5c9c97ef9e864c27721b5689f02ad12799b a716f96dacdea71177b7f4eaf34684400bfbcdd27be61c7f107addfb2344ea58 90bb196ac455c1043f247ef6ab4f471f78f3852eb67cd30a1195c5b57841b6e4 53beff235fe620d3bb6afbf1e69e349ee3cc42dd55ee5b6652deb99bab2a90b3 f4f928caa2a62e174bf70961f0078c75f5f562819e86b998ec6b27a717c82808 8d7598f0fc864bd4ee4668f36fafeda5ae36d7a742bd3df7dc1deb0679ee774a 28a5f07e64a2d012a8a3b6819508da10b4f9f13a60a1e7ae80b574b3891f1a97 7f67e93442487739b24343783769667d5131240a352c16b19d5c92ef94fd7b23 1d92e9cc5fc70983904c460afb63c9551d221da49a9177de8b43ce108686c9eb dd1bc78f955c1436557382fb40cb26fdfd992537b4ac601d83be5d91469e1e84 7b1ed56ed4861fdb1f1cd69a8420befe5f44eba3f934522a35b1da69f52be26f e0cfd8dc4e7dc71e1a1e34278b1fadb29b5fdefa11c74b106b459d61c3110953 d807fe5f0963fc24506d2e50c910dc9ed718891e201c97ff93d79691bfbf7c66 8f597ab79c24a822513f1f6a9f2fbb60d0e49ffa4bd8283c758759a4cd15a54d 15db9e9f1510057441a6bf4a66279bea663abba237667105c96f228571a52ff2 e47f68f0995d0471d3c9e24d59bf4197e32b46ee6107b25607f5b819bc93d380 e68cb6215e3aa4d151b60df4efbb93cecb4578424634769888876958ae357424 98c0ce503abe9c0beaa4d62dc7504c5404e121cac2c64ec2420f55a690801d5e d4a7f428d594c897844c186925f652c8ffb88c933ccf6c315ea909c5d4998369 ee84747c79513be12e67f01e106c57056bebbddcfb906e8f009affd9d81a052f 620b4c02683424fcc34763893d37d40f74f6f627910f2b05fd7739516a9caa9d b08ddaae09ab73cf2ee463ff15c4bbcdc786b81d014c06cda0d1d1066fec57fb 30a012d1581dc12bed7df9f6a929780cb67ee856b3df590ba4d237acd9a0f4f7 7162bdbde3f5a085e384fc96c3e599ae0c9999bdf2f2506a018938f1751ec10c 7 7fd513f71d326a79a03489d68779bc0af347205d59a77f47a91cad5f0b47bc0fdbab685698b4fce04e9f96f7dd8b0364ffa0ae3b5eaa86e2560e7db5932b2f06fa0c481624bc676d1240abecab7164305b91f2e30d66d1a4f78cd50a26760305ba1be3065e1ea8df05423e9d09fdae0385ce0faffc7695abe2fbd34b2772ff01cbb1db0663be477e95ec60c03c7d1afbb5af14f518d93e7ed973ba95fa8b550a91b2b3cc8c7bed5040892a522238ce80b5b9949238c61c6c95f974119371870cd716924105944c8af09a67ac1af35928833ea628c6f440afd610f23cbd265b0c13cef3204bb8e99c364acb98710377a3d455e522edc6f1cc639c63ae48d2e307d1d8a3a7fa740be61fe6b1a49a2874f40f1260142f46c2ab7505d08eab9dac0d4a2841c8dce8942c36dd4e676fece4051c0485e85479da6c63fcdcd076467106f24fa54039ef091bdf93a076bc8f7f3b08ca778e3d728a4f8e3ef0680f9c370193b6bf9d4e6a36eb82a8c1ddd456cacaed255a507b360bc61213b0a03988f804a94797f05a9eccd653cc89643eb18669c3875ea5872359ad62b4c110ae1ce108e39f3e07be04b9d41b783093ef509c400f31887c500ac4c3a6393a63ce5ae3098cb0cf0d6f45e3773fdfbedac80f18a23d22b85d0104329e17c617461937d701701db66969b435b8b90479daaa769f618762316bcf3df2bfc0a74a55abb42a0a3ae49b297201fcd51cdf967019b3d202ae2118732d09ee23160cb0a554e1b90bd52a2273907c566a628fb92ca2c42e327a24eadebb25bd877abfd6cce515060f62e39685303557957c2587f2ce910b202ee0eff0220868d7e08bd51f47aa39099ea2f56c38c63a5899745871781aee38b6af514fe4af49fe301f70858b45d70ef0a1039cc202561f0c7d929793cbfdba955423eae7397b84f09b9ebbde8cc702f77ecaa26839ad021ea3851d559d3f4828bceae05bff38c044e1cf28a53e930c181d0701f457c47715eb47f5cdb249fa2dd726553c309883cd9c070002709c0ec2b9cb7154036166561d9688b7693e3b1eac12d7cce74f007f044470fb65550517b8446da8e940f6e5e3518e331affe60ea832b86116b57990e647b0182a2b048b3a17907dcdaa7d8c30798bb2054152a7beabc2dc6d1852e9ca3291fecff60c88fc5a7e11febd9cb85b548ae18d7a03c0746dbd052e4b02dd22b2b8bb044e01265289fee1c8c4a2364dddd8f620e9c2195f597321229af45fb305c93924eb05de2cd44e563e75e0c0451ce488d9125c4c3f3cf27ad3cd1d9cf09861c2635709e741abbc4613b5a8a8d3dab8ca7fce63e9c95e1c7297d882589aafa1ac8d3409bbfc30fa99e044dc0a540327d8f090ca5a83dbcad83c53e2650c9872a2ba98029a23b7f4b5fac046bca25360e288b919e268cb91c55af64ba1914ef763289d0ac24e6120cf1009314a6c7d32dd3824e6f62951a0cbbb613d6f98dd2766cbdb0b1d9c037b86c6d9928be4d2098d68eb9115c8ec4663461a5d0edc0aaba46edd03c259e3cbd967eaae3f5a23f8a8b8d213cae697fc10488d84ecaab1ad5f2b4905e978915584d96e663452b875e8f8e42c0a09aa65324a6e37d7b9b4a33bdf0b077618d0037c3a781311386af29efc4b76f2476f48f4db3b073a1d124c646a4009c52d15a39fbd9f5cb2757eb5ca47f722802e36ed757d819335c4fc28e8901a05ff9246f8de47d12c508c5163084980896c6862b8f0bfa81db79429b47cf40200c36e4064cbf49186767b144f85370e8ce0ba4397dd44a9db80ec92d6348b4a0eb6dc7043c458f23f4f4c6914cb2217f8a223249a081b952879dd19bd201a150c1b767df97a61abd9462e311b87c64f0a7d868346a93466e79df7353ae8c1460a7ba16e810e8bc0459574c2e5d6df67f53a635677165e3beb001e78635cb105003d0f5321e22d1c805ccaf58898119b4990486e9725520f3526c5785082a4070eae6691306cefacea216dc45dbd5abccc1cdfd7e9bd3189cfe0ae644c0e03a90ab0557db11f464db95e53307ef46375dd3ebab482aa849d236742909f0c5a39084b0e66149d298be776bdcd4e8b5c0afb23be9b1f47a8395d03ea7c3a51186a00d6f44c0d113d47fed4878d655df44595e5580250e69e92a2730f9f4dbee6dd0d81699745b06c6f5454fa3df0d4ec1bf4438b6279e150104137f71dd9b592e90233208406159b523dc9da6238baea4147e286099846f49074f029ab95d8c27f0fa2725c61c85839f4fecad1bbe31e27baa533c3e98dfdf3eee826033f7bada10c3b2884e06b42b1c75ebc401b395854addca8644be13bde09655b9a8d1c746d01 +generate_ring_signature 6cfbb026bec0d6ef6fb0098612f2fe6e2541b36047222879240edb74b826082d 4cd750214701670d028179becb5d533617d5a75811a93a67fddf9a31515e2d68 39 7b21f860cc96a33e355d817bfc074940b0ca58cb66987cb9a7712b26b2da9043 cefa1b4c61929b0673745214f9e13885116b0d4a558e1272a0e6c4a64b0a6f3d 260f9eda171beec17df9326f9da975ce296f5ed8aa2db13cc0abe6d1fe6ecb41 b0208b0c48e8e9d5f6445471ad27c56dad8f821928a9e09e31d735c05f7fc9b9 029031995c9778ce4b6eb4f4911162e2e568fc9a42900007ffc9425387e4e7f7 81733548f183845ca0835c6c26d2ffdfe9857217844260221645be1e6636fa5e 4f6dc46dfb4f51c4d49a7131d0705f239d2cb20084d8f007db8e40e8e109e7d5 9e3fa0d688984f55bbacbb7ca55e676adf081ea8ee48c2c34dafa95fb8111a95 7e44bae8380beab3e4f8d5c79f0be8b816ba3d9a784dd2e720ad59e250519af7 18362767f173a7f660f49465aabfb67b9daea37cb14a00bbc407de4623b2769b 0e707d1e5fd5104d3d37ef080c68ca8d2947340275a189eac03bfa3a8ec21616 f1b8fa3fa4695dabd7667cbb1deea54ddf5427086f3d6bf12fd813f45cb88ad3 51830ba6afb85df9aa1e1981d888c22f05cfeed0d63f6ec146ac4ce0e22691bc a6686a92f95c7894625054d3ada01abdb4f882b22fe47bbc59c644bf46307225 a206ff5d95a9b2c2369b2468b986ede95ab491731c70de09def24703844a3c59 8b0a8122d4932399865e30f9a721c39fc54339711d19415742d700b3418e1480 114e7cb5520b54a7a5d16b0e443f73b54cb255a21df8bc0dd5defcf2d2fad26c b098214b23a8e11894339c9b5ca475a3171b30a75d5b34139862cb656f5e1f3f d41b7bb0930d598d762b8f4332bd46044130b3748cdd5826151e9b6b7a2fc70c a2b73e89e39452310ed0e3a125e32f145cb61ee3cc217f7fa9728bf090cf5d49 01010d5286910f44557446504d52860214fb315bc5ac7f7fe93739af8c1be519 55297e0e6a6697c5900f41cae17b80ea7013c1ae7e443d33e4fe1e6b20a0b71e 18ff115c038992cb065fd84c5df19642acc8cabf9a4857ec11132d6980844d44 ceb235f53d75f4da2dd2013111a89c83a4494eca9067dee01f6904749b1c6a55 d93bde212fce10a4b8311f8b3288d010d0e482383a662e64796435dc7687127e 1226162e54ade4ca3866a2c4f2ca700b6c6937c0846e3d709bf8a0193cce2623 a61f7840463997aa8e574bd23c358b121ab80e04cc7c965f53a4dbfe990b3d21 8a21e839cd1dd41db3350789a769cef58ed8b075486f1b0f8876472f3838814e f8d22ca0632ef28458c69671bbe62f50af65f60a54fe2edc61fad2718932b7df 4e57bdd6b9fca43e071d84daf31a92faccc90e4c45c769be5165c1a15e3803fd bc30b6a974ddd9ce939d46201cd789dfbfae2510e54d17eb9977ecae5e718fd6 520e0f0f5790d03b442a1dbb0c7e922b85f577f67b10e6174f191f2d18b7186b e7612acd10506b636f883d49da30791f8762d2f171eb3bf29f558af206cee9ba 16d1a0ddcd09dfb68d05a4e1d5316918a2353ea025edc739d6cd05af2c0d3a05 8ea5b31e149ade0f7a84d45ded3fdc1681677167d7fad58aa5ac07ce38a8fca4 8a4b12db8a8daabe85b5e4b85af18a6e25199590ab30277f01b06eee2b699084 a67b6866be6de0efacc893864ce3c53b5d4a348834aebbf713ad5520ef0eea5e 84d7d3451d4c601b0d27c54fd5c96c6744888cd4ac470ad77c7428854b11c0e0 f863f778a932bdce843fd06204679af23b565ea6d6ad84629f619951d5be2e6f 50eafeb6888a17f57949ce79df1504b009598a040b6678bdc31d641f9bb59c0f 17 b5e9c600423ef07108768a15d6cc28eac1e25874bbf5f16dc97aa1fb7a60950a6f97b8c0ec7263a8b30a7f0dff24a977059642708352504d0d14064315c0020c9c152a1c2d8d1219f1df7365fa652b5d3f3a05c026b8c616968a404aba0af7012a1d4036ef19ed80ad5cc7889762db53279f95468ef7e504bb3205c9753e2109086220ccb85bebc5adb4d32db114cb93bb48cc75550a6a24dc87386137558e033a65c6da5cd33520cf365d676a84d7372d726083b7f09105d6bff6bcfe64f205dc238edcc1a5117eb596333b86bf1d9c0fe53f6405f8aa4455160e30819a440a9d8f6fa0f8440826a838873eea5f5181897fcb2eacbc04534df5316eaf593604a3acfeb07a1b05ab9262899783cca8adc8d77fab6552b5704cc51b6916ccfa0ccc1c11910059157217df2370da381c899f32f705b71facac598fb5afba14070c7d91a30f8c061992859890d3c908080fd5b727070653d271a87d529f2a53ae01ee087890191a892e9f81c2542be8e82e6bb81f505f52b5d2c94f8c302209e1072aab841c0727c3ce09261b96054a845eff19ea83923a1fffb006f745cc931e093b55c892050b058c721e59721fa1a90f4a8f6da3e2702318853b800cde47720c5ba8f087a98ac04319a5ffb68960617ffb67c3c88d1f1d043d5e75b963ef9702b2b95d25b8896c47647af2ed40b8921d0caf7cede9f856d368b17e1d9943fc030272095f2842566dc4093c6ea48adfbad30563f9729aea02655d2d6945f2f90cd61f47c5e60d5ed773b3b77d3a33230e9840e727ed0f46f8c59ce4ff60da120736abb7649a385a444c2d325f3df5954c4d57cbd1eb2b4b6e807dee3a0196480e9d21f2807d73996b3d0671cef539929428c948e41ad9cce53cdc0430f0192b0fb7580e137ab371be7df275a80b41d220ba0adc40f9e47954b8796c8b47ed590fc3eba866e5af5667da379bef1f23cd826eff67a2542ac70a545434e8394c8e0892b391d7926627752a9f950db3f79131cecdba790d88d65b455fa7373d0e2e06bfb24924abca7cf61a65c27d813cecf61871fb6b16ebfee947700441342ea8050a5453f86452f05c9f6059cc22a924ab15ebc9324065072e0f7e0d737850db0ce003b7bff9494667cf834cfd64c15dce731ece412ec48488d52068dbb1f9070771ebe21be6ca72af75e7514db14b72ca14190184cbc9fe4593d08285a8f8e70f82523b4a5e9001ce5dfe141378afbd534d4a76e271543b06a65eb252514f000fdddcd0637dec749c5d6c00960fbe4d45a1aabd739ed4db02613385c0721f5909f6ff6faf0b7dbd924055237cc866ccf62d0d58913286733163116ef5cbaf9f02856ffc7e6fdd34b4dc2507cce01cd545e3981af3f3298a9789e1b2674eb82707c4152655b66421a889875ee80eb98ed41e20263bbeca6b7813268dd95386450799d8824d44688605cc218e2db672ed4d5651f5a2065650bd6124f465d17a870854a74d01bb1aec4c2a594d6c98eeee00d5408d8d16494d7ab044bd858fbfe602cd996e82060782c539add9f75b275e3e1bb7c348b704efc3a1bd6ace45882108d20e510cfc42ff8d27bdb0019b1697d68c24a957e13409512005e2c83cfcae03cd1fbbc862c6a482014bce3ce1f2655d9ca7ba534b1d40e7e116c5893be0460bfd0155f7a8aa83eae0332465afa1683924f7740e2cbb6736fce9bc9ed02ef90cba43700142f380c3d58dab43ac8e744a6ce4a5f2e8114a1cddf10ced4b638804e1d6bd71e88c08d24d3d1baa48eaf86e3f90d0e54245ef0847e48a70fc55bc07ad9016b4e408bd0408a7b78cee62864f0e027b98a3a1fa1c11ba8ad9ed7a6707f63ed4f90361da4dadae280e32cb9d609ce6601a0180f84de6399ced4851270110bb346f18d496817adf5a9e2b1899b1a9e03076b82cce307a56a06d2a083b0f8ad937edde70bc9f483de1f14f639bd0a655ebdc53bc3b35e310311f74f3590057b9ba0366575faee839319dd99ca3ab8a87d2a991df38511e7590d8d0900004b216ed1b4af759f60593eba516ba64febd75acb474760b598c34ef6a9a908b01ba10fd86f170ab564293418d7e1a203216db515f6502764303f155b8fe9262093f95f0b392682bff02d00f687bacd19189a82eec4802fcbcb069baa95cf396079e8d1e3b003c08be17694a2d027de63a80aa9b290439889a48bee8144e90dd0306871f39c2e36016ed2261ad9f10df97a81f46d18b4fa9f8600fe7184c87f2078c09dc6e2c1e79dbdf3fc6c8c1328a1717aaa92bd7ff335bdf600b6e20118302e4560f64fa10643ba181353712576f094557a870f6f1453d71f3e3cbde1ad203493c93ac2400ec4148ad974e1f7118ce527ea53c07c541c140801a81d7de560ba263971ef1bd70054c18b67594535a5d3b0b455d0945f3941a9dd989af0b0205908ffaff750b2bb2e32a2aa89f1743ea5620af6f6f6e6b7330456cf4794e1509b2c99f6ff81f16e98cf89bbf412429e87d74900877364f8b8fb8a2c6984d7e077a38f0a27fb6f5532bd23af9cfddaa3308f9208f22c0d1ff945da03b7c1b580d3b2f64347bd579febff8bad53ea2d3cba2fe83145fee52f0722195f56ad53b0af90a6bd2f27f28ae6ad88da35cae1f4e7e2b9d2e31fdd9ca4d569d97dc9f3303b72c62981df2311b832abb2d9bace43759b9c69b9413e7eb04271c88a2d198034f660ecf55a5d6220144166d18ba339f3c729ec38dd691726c49da928c626e05a8a6996a9d4a62b6b06c677a5abe9183e8384cfdc52b5d2f098e79741bae3a08dbb4cee731e8f0263a0a6e8a4f4fd0e4eb55973026605cc04b61b42a528176078c02459aa318e962806666f488924b06b1c216a9eb186b52931e336674acc10d56816dd8965bfef0c23811e3ed1cf352438610eee960156f6b32b8c6f24fd20c96e1be77e07e182186d2082f8b97ede9487333b496eefd1ec92cae1ce4595c091bfbb55e2806b8ff45cc03f16b66ddf674594e9bb97ec650fd29093c9c9b99004e0907b4d93ce474ae7ce2b93a9ec37f32d1e7b41709c6372a1d6a4a4b98f6037c59c1f9fc59df37738fe5248fd9a4346de8d89d7e8c40cb59402521dd62e309a82c0301b4ddadd87eb87fc9c002c7e0cd619610ce1cce5f1370b42037952c0c8389381851e7be7870ed6137882b52c0bd635868fb11bb734ad33a968f518204edbddf59a2caceaede2df61772ccde62474ce1ab1db354f4bb29398e27d3bc0aab8e3acb00c19c79780d685ad972c73520f98777b4737c21c8792bda44b4480a244bc096b6474c3870579da0863e0bf6de2d315c75f9247658e822bb22699d098e86c634211d378923e41d0c63dbe61f2d2d8bb61d5233107908f6da1d6bd3034f48797edead198766b6ab33c9b56069133916b7426555bfeb5dc7e7cd45c4087f7d2badbb46e79b3b6a641062029eadd7046c5dde17684b5a53090286a61608108cdc94611651fe2f5a6a9af00098c69546b688f17b940e053815584366b005 +generate_ring_signature 1a11f9428e6907303c4061dab544ca31d1a2a4da96fffb3d946cdf4eeb7aaeb1 3a323d23b9157984e6d71a95dd8b3286e068edc5dc3b078e2bfd8e457746ccc1 1 4d9b75c69197ffac3dc10a2a011d359fef0a1128b820a632f7f557f50e42e1ab 5809bc5d4a38c525ed6966ffcf8ffb84ce1c98b8f12e14dab6f87bab076b6003 0 4bebcaf83ccb3b86c744e7d7da2c0f52606b54239a12240e129dad6b64ec130c5a477fd99ed8eae7046fb4cf8af8b846377e43a59c2c0a5310edd2e4c0667e00 +generate_ring_signature 0faa0931d4cd62b537a1b6f3241577840553c3bc8299b0d80f3f6648baaee515 3ee1920b3b90a866093d76b83f782cb1b58abbe8c01a24dac469aff832f63646 233 d0c2389176d13533528ea31fe3b86534d229f852c2d3057d2a7034e16be2f739 f21abde811d0d664fbdb742f04412ca5b4ee5de66218c328dd8adc57c1481ecb 826d8dbf56d39e414c66896a619ffd625a0aa990953f5d2f71fdb8bd4bf38bc8 64fe064d0a5d57618d1f645a436d9b3ce4f28eae217001f2776738ac6c5022f2 7cd360ed53621361861fb116edf0d55822bbbf1eefe5ca8a0109c62a73ecf8ea 8989785c354e587c5201663686936e5fea6ec8d789d9332b4b711cf345e6386e 3da89c5417484faebbf45776c16d28b591de57c48f067b287e020359f6b48e16 658bf2728efbbc50ffaf3d5267c8dfd30d95178bbca6c05d29169d789b90c069 f8432d1fbce6e35a277567e4476ea50b3d80938fdfe064ad53acb126d0f21b6a badeadb255b7bdaa14340fbf5982e2badb874bf8d6b3d510755da0637fe602f0 a2d0cc80938838d012abf1530b7447042e1b84c89faaf690e630dfa9565c1eb8 41dfadf55495bc6ec33b069a111689828bce28395743a24d6003a516046e4d73 6c933b718ac1005e6a3d70af72de202e381966caaab6e37e5cdccf1a352a0e36 fcb34e5a8aef52053552adaa3862e18b8cfb962451409d1688925069c5fe1ff8 a2dc98eb0c9a61fd7536c1370b48049df61826fc1054cf5658b6665cf53d7e5f a4e468540f7ee7bfb3b6bff3eb33b9f03068081098fe2ceab6c022ded6d070ca 7bca39f395e39cbe59477844a610cb0a8597fe54c830089daef8def4341e2c89 986607d77f0830048bd9cb47758f5ed888b1de2038c60a1a1851c999a121de32 12bc90ef98fbd5ae81314f7ed6cba031c3ebe7c4dcaebcd5c8b9c3f0982085bc c55adb61e768b451df3977df5f31ad974892e93aae498a52c341c2074d79dac0 96745a4036ad08613b9483edc3353977091773948f867beda7da8f68ab787ee1 594fe053c3b76db1be04d4768dcda3b8db4681ed1f67d3dccf509973943b5f14 c83610b4622068ba762fc9eca7a14db57e511fd75a316c004b82c2b8e6b2f36c 755bfaf844bcba2e5be89c1a12fe10fb988a709adf8a80db7bacf9114b1b17d1 054688146e78b41bd4b4a1c97843906641227ae80cc2d5b531e3bd278f561ca2 e0feacad1898f2990545923a8a021fceade6d6c54fbde93c3b590b49582208f0 a0fd39224907e6909adfd61857ba71b58d429866d087390d6090760661b46cf1 08e1f7872dad598bbae6959fb84544971a3d0066f309fe97db5e560f53b21cf6 288cb4b25669e162c1bbb2134ab4816a74d5441d42058ad7c6ca0cb9a0b53c0b 3d8faefa14d73ebf9df33cb2385adee25e5f0534df15641d3a09af86a94ace7b 7a2af476225d5b817049e1536c3f5480a6370dff71d0cbf7a4807984daa21f9e a8d887fc797efcc1261c4c58455a715e3e704becf8313136d7909a1e7886b826 1a07dae36adc10ccd51b538afbb0650141a8b0f40a1801ef74ea7ee97ec7e372 f8fa038cfd134797820e6770f66f179af6d153a9b02bd11991f564979bed0c53 a1c0173cb6d0b0d4fe0d25a2b6c43f9007689a7a065274b719b833711526c478 55ea8443bacfd32d5dbf1c4975aec0ac740a64b88d6d14c0b8618f57837fea54 0d5be4dd6cedca7d1697cf04264ffe40a68eeccf842bab0101e3898b165b57ea 35728204310ff363bba83e4ab9c7a2737edce6bc649f7f0345d840b14eb9ae8e 52bbb4fe4ccd9bfa85f825e307f2bc1665db0a919a43ad0ebaa41e9d4e396f30 8c47d142f7d86b6957860abf343c9273b10ee0ee525d1757c20c0b9f5af62c65 30e75cd2dce461d8b04462fe3b91f233e8ebab37e33be6c579f52a2350abbed6 e4e4cf68d75fcc41ffb27b4fccb8eaf80994117726c15fb3381792f9673fc6df 832bec875b851a11d24abc02b77fd632251d42aeb9b1fc4852b7fa415657aec0 9aedd4786dbbef3f7917f6a21af6c2f7ea93fecafda8f657bf886a2b45def363 74f8e14e0e4d617818a0a58cc5fbcfcda04dccec46cb262e3db605e0321f5e52 98ff37ad71cea87ba7b339f378a972a17a66249c8f24e501b5e96ac459ee7e86 c02ac00685145b00796ff9081755017d24fb61935b16363d9756b619ad671b6b ff940b0417089ef68b705f7985598e271882528801473e460fd98de16161c9d4 4ba53d7cd6a3969b0b2944cc58b5c5b67c1bd1fd987f7362530ec11cb0a15262 f825ec8d47e4c583a1ee7bf6057599aead1c9fc0d8e49eea7ef6d845c4789ca3 a521d5783efd61d93a318f8bca80feffb2a84dc67cd39bbf7ba09c95253f1e35 c5edb267cbc06a0f1a27f108624a08ad5ae7c8f730e9389a10b7fc22f5c41a8a cbf687b70c68246134eed0d1448f50447166be3c5ccbcf9383e6fa3bb8567a71 510bb4297912488cd2ce376d5bd4b0d01d18be0d288b20b7665c1ee4c1497311 408d5b4a63d502134deaad16b72f6f0c24868722073770fdc1badf01667c00d3 3b0494f311efd251c69e978319cc5222594c528cc59c86a9a998c5b082151ff1 4e65dfe261e725d33ea95af49c0c32ca85ed589974da1c0ce85bf17dc9b4068d ce6a7d024966402e74ac792bc9f8a592f8138318e0722bc054182d8a5268584c 886b7057ac1b17f8c2bd0279733656c06aa97cb1cece552acd4203c4fd52c32b 0fffcdcbe3c4c7dfd00d92b4a186079c03f1a5985f575c36f78cc84c18009277 24714d3a822c7503ffdff1b210f6a240b5ca252aa830388c0f4953930c32ad1d 43efbc612eeb512a91ac550cb235239cd3df464e849cd54aa08d80e076321e53 fe42cc828048191966fbddca02ff3595f9d72b58ecf89792a73b28dd544a9b08 a7109ee20278776d21e6e8deb31d7a320b4afd43d2cbbee49a2ad2ac02ac62f0 6aa8cf108dac2cf07e3bad00644f0f6d673a9d467131f48e56b9ca8202ef82e0 9b23e0a9c949e5606e00c79324f02a341456c9e876a8611f6fd1b4b9d5cfde8a b3fb502376bd544ab030c0e8fa1606d3c46971397c7e9ce0bde4420865e9ac5e bf7528952785323974fc45022f185edeb5a5dde69f4138b406924bdc26482d1c 8ffb57b7e7709c501569d46a34f6567d86de115e2b7324bc3425c2c74da941b1 ed52973847fc40b3042090f21808e44da4587023d9416d17e36c296a18caaadc 891b513c38da1592fc65b2bab79068c45b4a0f2993b86257288a1f84e6a55a03 2c8acf212ea141a31bd353853c9aa20b3198a516a90bf2211b7518917901f1de b1653f913408784600ee86d715f403b2316bd6d9a8647e71ab68dbfdaf687b66 85d3a81a718891973dfe31793d66ebec645ad073c9f2268d2c03545fde59d6d2 9865aa24ddd13f1925325c51c6bda2be2ce822634353330ff92122c7afd91fab 003ba9809f48cd96a3098a31a2fc68d4e93b168131a4dfa511f690b144391ddc 7d283513ae4881d0b9ed8138e80653fdf0920590d661b7e6f545b2f477ecfa8e 7cae916e9afb928252e599ea6de423d80744d9ba0277a20e7cb8ae912c51185a e9ab61d01d843d73fe2a0297a2736883ba92f48f0ec9da7c5cb63fef6c36dc21 bed131aa2c2996b90951d0295ce531303a1011418cdd1c2567a1c1716eb3e316 88b30bcc7ad8045a160cb6b9dedda43c1a07e2bb17b6cd1329c2d0642d20263e 67fba263397ae1921f1800f91995d0ddfbfe63bb6353713f77f395a62a4a2c46 04ab7cb540e8bac6a93c3c4d36217a910de1fc689dd6ecee5a53eaf95ca60015 629242c1950c389a7108104d10416e610601fa42f2e13dabb3a8375db297cb5b 8231f713ea9fe6311b3921b38570ba93c32ee55ae2962f262d9c66a5fe95ba8a 96595d418b0ac42960f0ba36cc548fdab72f540e14af1e73ed603166214f8654 31ddf6c82c429549c6deaf4d2df7c4dcaaacbe7a91ed0b242be761dc42c95d31 891b9c7df62eec2520da2587b5576eee0105b857b5f00d34c3d7596bc235ff80 2fc9f5cd73c1607a6a48375794e6853a86db6c5f733ba3ee0fbe96de44ed7b36 7a2232ec221a0e3cb95db241fd0ecc42a6424d145d249f12ca1485ba69d7410d 2976294379ddb7a9d30d06a56db738534357277c9b65d044474cc3616fa833ad fa456d216fc4fcc62b0b8f80d5f9ff6ed81282dc3b765baa11b7c6e4e62481ad f9722ddb3e63762ae102b705a6b100bfc5eaa5700936b210dd1100c76a1becdf 240df492c6ff78bb1c09f8398ca515ef261ae80f4498ab894b61d41e59fb697a a714d580211bff44b6b0e431b42e6ac4e1037acc4af9b9e6e22a1ed3f7200a8e ca9c2b65497c53d0959cb925c306e7de59ca1d028b778f22fe8aa23f13387393 9bc099bcb972fb00f8b168f83a878ac5ff33dc49631e79d0dddbd1ddb202021b 0ad055ef114dc9247ab1acb3fa5d908f4e14b8bc6f3a604823300acb0b234323 55a83a1b3f1611e458488c02e09660bfca948b93514385b381c4e0427f24b89a d0a2dfb220f5bb31a9f0f180843291b4587de29cd0a0fa235478a4b73ad4cc51 15b5fe0e26086ed1b4e46b23b4f4629034057c00cfb72cb125ec74b1afe2d385 187a166da2125d976b74091c3cb00421be116e9576989365ffe52194637bff5a 5241ea70b959917e0b050a419c4f62aa1db2040d5aa3c61e302f696e979c4fad 29496eaedb17be1c85bd31af889dcebb733a5eb0795160668ee1d8d51c7c494f c2bc205b3375390a2102fee6b6ad7ea544ca33063a868ff237ee8d458a93c1d3 8fa2902b0a4f23202f2b7d8d0e652db8fcad21385b64568079794b85b198804f 74b7c338b0ca1ad8b7426e7f2762b2c34e6ba947fc9a2962920f35457dd5270d c692def1da5727fe4090b613c779f463ac51317de0e671038216e3e6006fe50b 301a36ccb64437eb74835225a51b6604d82ca8261925118870633bb96ac8c971 d1f2fd52ec5479b487856c564d39c1d7f07e8d3a2649a911f408790948ef36dc a531d0e5ad9410bc26c3ae46893851a2ce979928fa675763a2d0777eda72906e b7527f84adf398b45d29b5b94f80d8801a5dc89633aa750e03c74d389be69484 5ff6ae97ca428b8a378244a4316ad807755ffed141459e85ad357a09821754e1 e8cfd81bbeae6b10a54af26e23e3e57135a216a4b524cf244ea1192c800b7679 5f3cd3d65a4490b39bcf9b897aadcb31e9247409d36c071c8e21d2307483d075 d96648492b8e1b227f2c45a7db9245094c9adbb251ee23c22729bdd5aa47657f b72dc2c3852aa5ff5bc6b63eb3b305dff1f771236fb546754b2cac6918dfb8a1 888e7aa7dd4820da250f791b276f3c977d6810f9fbdc7e231f66590d9c31a900 b242113ac470fd283da0597cfded029326cb34bb26217f7516a7cd901141af79 6898de247366bacfaed3cced83af22a86bebbef8407deabd3f21936b531a1ff0 a8c05c0fba7b6fdeeaddcef93d9ca5a94549cef7a52a30aa6b14470a8cd00bf4 cb5554df0c76fd0ac6df4a7eb103dcae6193f001d226ec3841a864615c27e412 ba1da054bd4c3086b9f35e0931e718fd5816d2dc07b5558d636ca7bbdda24d0b e136ee25ad79e3eef3dac9193803fa4f0e7d4b0b5bd38895c8bad0e3bc44ff2b 71fb34b3d66e2d4ff0b48f66f67bd80b35ccfbd310b884e11bf816e5cbfe9212 8f582f7656cfa5e19e5457abb701e6718d43fee9d3090a167541541463091565 534f860b2c04d093359c1e4eecdae0621f1c1d05cec2e4c56773acba95757b3a dc90a971946124e0fb4be9521dab95c32fb9e34ae15d5f3a07a13992697924ab 4fdcd4ecc22147a4ca50b8f24b5a137edf74612e3a451c68eee03024172ac6f4 cd69bd1e9f43ea15afbfb4649eea6d8f32e4a0e9e08c60eb0746b646748df521 d61f09bfc6acfd2ab04e6b97f0c47cc71c764eb28efd5332abc07c1f282d7642 67a743f2cd2b3fe41a18a6469e60a4fb3a60fc9b0bcfcfbe7368aa0e89568574 99ddbeec6344410196af3c191aa9958bf76c27fc4d7331adcacce3742e18aa4f 9ef4ec7b45033a6d41f57b3db88eca109ee77674226aa419f0115782295f860b 7f6ba5df9057ad9d10d81c4dc302372a28e4c28c996ca71966b855b1f09ce1ee 4e6038102b95718f85ed6c8a631a0f7c724cd403c685e3da0b957cb64ae2bac0 a9af590502d62ae4015e3337b7c1e6f35b5ecd670b11207177e13a7df3d7c218 70e7dc6ce9b94daa6e15eb4a815b3a4cf5b91dfaf0a301489a559af5ebac734d b2bf4de4291847ceddba0f2919c600bb80ab0feae28dcec475c34f9cad175be8 e20093c17ba3bfa4086addca36309cb427b4ba06f5884c83e887b792b47d8507 d0d44acb82813c7b6e7dccf8f692b7422f8aaf4ef577ff64ad82c57591415b8b af21e34f2a96675e53e6c043aebfc1d468075d12ccfaea8a2cece728eb5198ec 6099a89ee386610f69598f76eb4bf6d3df04fb761ff19f8ffc82bd301ae3ef8d 37152e1fd94ab034deb390c4bde0949f43867055bb1288e1ef3bd44e571e2f59 14e2bc4fe08c86fd3ee1ee2166fa07bf864aa6d0cfbe39c3e957ecfd107fa2d5 5fdf9a23f9393f460fb39d846001aa33d8a6d79a34d19d3498e4bd23bb2b50d3 d31978453f1114284207a44825b841cea07b62c575aaf801c2931d7c5d7cbe99 5065bdcee71afc20c76514d4212c23eb48c302175cea72160f47e12410fdff64 29b5caa9f72a1c86f7ec4b2e49abf2287d8abf42eb742281f5733309bd4a4b77 253cb9f86b03d2adaa14dcac74122f0bafc67e3ac3dac7fb979fbee6621fb213 c36c38337ad1d138310a9749eac20102a1f9282f4b0625b9f3ffab05c6729f75 ecba92094f93ad1189326ade235aa023adf3697a0bbeb8a11c73ee016bb40508 427ec1ab97a24b3160b05b2055006936c09c4e4766ba7a49d442df127bc85bec d002b8753d323ebac0b0645c5af17e47340c847a330ab04144085eff532e85dc 962d4932744de7cc5d3a65ebc7a2fe8af61add288898ab7a8e9bcdcce401aad4 f32b38dd138b86f862683588cc01692d9e15bf20160e9b508233b8c3b43f4da8 19d64d02253d2cc07cdf4d2de1eb785759f355956403a09e92927789bc76fda0 0ea8cdb2eba0d06686975079f03bc1ca35033d323211254806516cae120de993 3c64e8b0c893f52adadd7cdd519c505ed570f09f0240195d271d062748185b61 565318af6367800757bc07dad27c2ca35313893b7d1484b1e1764de6fbc07fea fed6b6fb49ac99d1749f7815cd88e3f3a66f72348540ddaef8ef433425132183 38f3ba290c7e84cf53c37c3c2e66bd15fba08979b35d7ca45c8e3c31bf3375c6 eb4fa52d6cdf5c5a37a8964dd5e671cef98ca9d9a4c15683a27fabe9ea4f7969 3e7e21bd65199fea93864f08e840295a733b36972def56b04a82d5605b0bc9ce eb6b59e238ad26c9b433a58a82b0b46206bd505342dfdeaf9b49fd41dc8770c7 dc5f9327cd2039b7b15d7fdaa812d12e407ba921f079bf6837085237abbaad30 9f66786e6ea7c9cf9950037a3bede49d2c559b74e7493f45ac431be5a216cfda d764390b80a98379b49fcd6c4f502f07f2cee9d3e7d7bfd2395b017d2d292b10 43bd9e973137f330bcdda991bba7041b4742d7ce25b45be5aac5a4161d7c064d ab27a23fc840fbb2c3654f492227ea5c0af3ef044ecd2871c4a4d86a3d9aa449 2c00305051cdd7ee30df65ddcc0aff2e5edb1a54dd207c69c0da4fe6e5676bc5 0f4d980ad331ae4905da365f34701eebeed5aad51fa8f6087b04be29345b64a5 22a86b068d99fda7c3c617ed1bab37398bcdc9fa491e9631e9e3ab30cfe314ea 9fc74af1a73a53e6aa34e382af788e1bd995042067811126ffaec2dac121daf0 999f8887f6d59daf085699d8d5239b7cdf782d77034cc7cf3e1c0d6609654f83 5145eebc561647effe14acee80f7bb4ff6104a861733939c233912119734b2d7 8b901b0388cf901736c318fc801a09cf9b081d7dba1e769771387eb9c949e847 48acbc72f2a0ac813d22b475329dcdc76bdb139669e2707245021ddb962bc7ad 81da38f6d715cbdbb5ce0dea9312afd3b105ae57f76b803a2630b30403ad682e e10514585eedde44aeb5ab8b505de44e8e3734f8d3087d8578c74195797dfc14 a633c936c61ea06fb333859ccc89b82ba4afb6a1c54d0791ee92981f1a6ef41e b19b60516b20a61add905d8344c0088c305f43e5e278fd2f0f950fe8a1ec3944 fcb78e3ad068565f35631ed4b768b347571616ac4d19219313709f8cd988e23d 8fc05a4b7ef2759288306be4088725c6990f3a7673c0e83e7fe4575b6a5c80a7 573430132e92bafdef0d1b428f9b002636b8eff823b2a8e57fe7ac65d8bf8272 b1069feb51298a6e13a1010f157fcd7b1847da59e71a7e89ca3f8c9cd88d8b04 df3a9bb8a253ad5ecf51f1e9af52b966d5c49ce2b055d7e15e13a4cf9033e0ba 900e3093031852a1b07a2134170b12a40517b5e02d793777139fbf3d518e9da6 0e16091d638049aa45348e22b2a7db5e784f5ef1a56eae806bd4f1093d808f83 8c752a655055fdbafab7109dcfc6839deeb6f7df86154071e6bb7ca197d2bcae c7e08ad57191c4475613b45ea1f6cff90bbdf6c629898a7f2f7e5a8be3253f7c 85c297d29e528f5cc0444a9384c86112204b8d40915ee5375addea75f7d89884 6e0199c00dab635474e81ac665f00d6584fa4aeb1fbe5a30675464d04590a0f7 1568f016ad2a350ca71bc89abcb447f1fc1b379267543e5d239d22205939bb83 b4871c84ed41575cbb264f9e4479625fdce98c82375885c50feece02db44a149 b2a9237e53832994fecb7945e37e693cc20ffe36a2721d84fe0568e3a936d25b 0c658a1f75bb42bfd1b8181842e17ef7296c068d4cacb98c126f1950a1533064 122c4ca8c54fa712f559efbe89e7a717a3dc33db49edf13ad30abb7233dfc368 55c8e2f24b9da03f2a45ae2d62f7cdf83d61b103a27d9048f3ac00705fa10996 637af76c358f7f4d4a71f595534bc7e2d63a45c6d1419c18715621c9653025b2 d6d064b071bb6d4677bcdf21644cdbbb099e6097437023c3ffe25e8813ab4279 674dd1c4ec0e1486014f9d0ce190980ef27433eb5817ebbb505d53a4f1406796 46cce1f48ddfeb792774fec659be8a0bc8ba89f45db3daacb8712f46583ca070 be481cdb569f715a1a40aac04ea5b62052df7213ffff5f395540523a1a507c23 0f8d415484ec7b73fcf08eee491bbac337b120032bf152da0c68a17931c7f5d6 5ef67109e755cbce2237f57821f19fa7ce672792328768566710a5136617152e 3118a12f3ed941e3a001ce751377d5e7ad35bc65c83723cd1054e65b43685243 3585ca5bfa06bd7fa8295e77014ffdbc32bcfcbf4ebeaf2fda2ddfd190508861 0c0a6fdbbceb2b42ac104782f3094a1dd940b33998547b7af9aa755dcb643267 fc3bf5d92e45c3835663d1d233ab412766b38b86948c5a59ab45df319ca95dc0 4b3a29355d53e1c9f2a827b96bee3b0e6df93061926cf00acdbfc221c6283b54 cea4c29b7727f51e05adf091b92a70b2cfd6c5fc8ccdac78f4050772f150f115 47de9e7c2f3e4f238b2d33203df172c4ba7ee77fe4dda20d23149af7682e361d aaf4f9094f37f47efc62a66248aad44b3461acde49fe964acc7500ffa4f78d76 db25fa464f5ed4dc49590daef4e6d586e1596f3a1443ceec6f8e8372a315f35e 4f9124d0158da979fd15289dee677565fb218b49c96e60d1847b94a0e898e1a1 4df973c1da2fc7807101039a0d411a6f8aa35a2ca8b348b13208a27d70c5f6f3 026d9684ff074ecbf12e8d46ebc433db4632d5675d3c53013f355ff56ff3f519 b718f49e5fba86207fbb0e1a42069f663396639780756757f21b73f1c92a8b5d f87d76d43e96da1cc24fef1177c14fb2d57d9381d25dcbc8edf20ed94eefb352 497f406390bc4ba7b54767367b58e760d831afddb24cc929c43439a8cba28904 5e7f52ae72961dbc185754eee5ebd24baa39ba82b37dccf6abf69b1efb9c0786 f47b04e320a73fb3e20840ccf6f5b6330fdc64a3b1987b4d6c4882a93c71e636 72aeac91c0a975c2cfd9e19db333dfb859cf8ac16fc0d479178b756d0af04eec 5961af89de7b44c21c5da318d437f6ff9169bf8eea68c14bad3e31153c670f42 8ba7d1cfecede47976e774622a7a5e11bc5dcf05a5e837cb180fbb32a68674c5 e2cdeca9a0aa79faa42179cd783182adc12e8cfa7f69a8f389cf4360f2a2bd60 a99eaf1cb66dd5ae0b22316ca1fd5347cae5c2e89c2ed498cb47514e6d69a9c8 911482c1daeef30e31a7b305c953498cc0155d3ab3372abcb01e7a3036bc3bbe fab632e09104ed80fc07a04496b346d86c1ac5a677c76df45dc39131eeee8a35 cf77c1155ce4e1c97c897ea6d1bbd47257913ece532ac12831696feb9bb8c20c 5e5c940614a3a17359169cf574676f4f3812dcad05abb7bf2d75d7b6167187ad a851408e8ffd2003a73f2a747fc0cf32b1e0f9dad6a7d2a6ad6817cd1c8dfb90 dad941e18087d0042fcd2e5993db1267195ffc382fedcdbb7960fe8c6851f104 110 3c2d283254d6033bf1ec2ab2e5a1df12034a64738df6bcb200c032ceb6ec4f08a915eb11d507ddbbd3ff2d7cb95b3eddfccd9f9172749511abb2f49d39f4ab034071d9297a4abb66870148240cd94bd4227e823dcce5e566bb97f7c9d67ca3040b35df1e582c715072640dbe2b8476954f7d4669b69c121ae99bfd81445d590bd09a9b63270b7fd944a61483b507bc460425d7016b4fa4ee97f40d8ea0c37c0cb8246aca3d1c474747d9cbbfa57b96378d14d1ae8048d2b7df5e4a9df2e6c20cecc5e4fb78c924a4f402d77d730690eae236475663c11f96360249a3cf81a906b52eb6e9fb86a9ee20885e470f6a0f610f0ed8bbdfe9644c8462f93abf596906d2584adfa604b94dea83a169bcb355824fa4f7d37cd2838b0662837ae93f7f018e5ec218c9f54f1370affc985f7c78e888782773725b31b81c896a1d8c6d4708de957bb7377f1f73a415b3e155b74961196f28a51a1efb8b8fc1a2aaa6897808e48039520b41de3b2b90c9dc2ebb0e27521b3de1f92ac79b1b5a4761ec5fe00b1a378c7566838ce2f8be6ac3e6c435ee76a9fa49a0105ebc611cec620ccb7f0bd84db77033e4be24d1977b10e1b88ed9fa85b3e31e03231d5094773f1cb95706a037ce7554b1c6d65f8ae1f8690d1079e2040bcd2cf130f3b4db4e96deb59c0c823d2c0c30ea3f4807b6e7983fc12dba340fb1d58e6de48a237aed50527f4101582d37d188edbc7d33fd2d0b70b75e05bad00f3001f7dba0c7a72c9a30e95602450cb9fde0d839e2f030a101c72f7afaa6df8fa4863374723c4653c3dc8df605e0db4a38431169ab791b151610281f824da175635bec67ddf6837d928283ba0d04f46df63a34b9fcd4803e00b66c645e969e537b7ea103adab63b69c30a2ad0d3d57fd0f4d06aa66405b349933a7a415ced6aff4bdf3c88fa7ce86077d391103889d028cc8d6a40875c3d58b35581a812396439549f03ffa333bc6d8cd7d47053e698cbbc1f6cacc96463a2e5d1d81597f24eef2b571a2adae2851eb1d7f1109fae757094ae239ded05c3836c8926f9a263b465b97cacf3058a72cdf3a195206e13e7f5f88f58dbfc992dbbf97979bc918a700eaef427c8dca0fd104b010d501427ac9c8867fe152a3795aa2dcbd929bef6eed2d85ed95c8e5185791eac8560564f26ad7cea31e505803cedff79eea70f9449ed59100ae5684260375c189ff0af9ab7ffcd88fe1c95b7716fab01f9dfc5ec5661e374e1d29c900a834cbcd2d080f7160de1f2cdc312fc81d4c42508cba95ea422ae5d0fcebc325a140e3d5450442a89fc74a087727e79734bcabbc3727f4992639c60df05cd6bbb923628a77057d187fb207090c3ba682fa7ac9bfbfd076bc77e6710d99ab084a849fd3bd8508fca802886fe3f29b0a6a950e8662ccb7cb6ed661b9d6e06f22dcac2f13103e021b2b93886055380d92d261a9aa074f6a08b6a755b28290cea153c6064ce80d05ac4cb053a18e758de02bf33c33d0d8859c3c31c3629f22f78907eaf9394aaa0777c2517cf52ccb536d174704171082ea2d63719f20c38b653eb0164c7799d706a39b96e5429391d05b2176490324f6b0d35957eb2108cece40725a3bd56e860da584cdc4adb3756faaab80b6413683a67030cf61eed53a68624b60943886fd05a00ac482c0c962c539aee4d82262214d5e3d004236c77efa4063a964b4bee50549a4f6de2126433f363f39d0c1158bf937d05796e8591b48c7e96819cd47e30ecc46767578b49e6bd06f0f74de2d815e93b23c72528aa7a54aa88bb94b903a090928b1f3398742c941ebdd20afa4b9630827a5b902b6f07c40f6e4f7f8860904db879424878458bf4f10ee2acf3709cdccb65b876d7e9a1077c2837c338ed70d7ed41a888747735f4b94ccd011fb5043cf448a4a4e4fc4d48e3a5de5e8dc350f20a19880ce568d8536176e9089b0580c1195b83580d7e2146cc606634fae6f0193a8d2764a0c48344ac384b35c5faf51c5d306fb21008a445d49ec8e29ac9c0c800dc82771fcb96b35e14ab5e96e2f5e5d3ca9ec4ef1983086c1e37936eabc0bab64acd630f3461d49934608f701a406e1cdcd4f607a772b68e1bb1891d3220c079f4a07ef97ac86258f2a83c4b341371a66869b769a6a99a7ae8756e4d1fe0af4886e3e7607aa7e70448ca03673d9731371151f0d546f7bf925858d5550be0591d17d3e509e0252e1b202857fbdea87bc201c6af97aea342b0b08e76ea2320b68cd1ec00c339e874b74c2445b2d26031039887b7b937f61bcaec18a5ddb6d015f1faedbc9ea4396050f04d183c5367230d4e7213d52b4a783b35ea05d40060a5151a1ad1b8b963b167cc00a8bb84bbc261cfc5bbf600f526883d7e58c4d4404d41f23fc9c67ff00e08697d88ad6346d183213c36a801d482973a1bb53fb9f0f502f9d03830142be81dca661b302ae91b3a2fab58785b85e894f4c70d0abc308f78e3a80c7f164afbb53d742ce04b08ee429697342b49d34708264423d407d004a548f288daba222ef5aa0f2313a027fff180fa5d17a21d18409fac72367880f05f6df2c6c4ede88d55c497f584211b954658a259982f14c6fb005c9a95d070f7b9d02a38fd41aea237af78276b7a01f41ed558cf0000e26c87f16246dcc040370d3ea5903ad1e9f91f29b1fd66cacdfcd1b7fe2d77ff92d02a6f85ad496dc0fc21019596eeb3032b9b99c63f293603f5e72e945531d9134206770ba94757500bec3b444a4ccd2a704b4cbe2465b9069cbf394d893714bd4ebb2b068434dfc00ecbb702a1bb4144c0b48443f8892a2fdc09a7ddfb5aaef667ec4bcdec3a48c05d17bfeb09e03aa163a3c6f5b53349ea1e45dd5fa122a75a8adbc93ead39e0e0f921633c3ffa9ec1d619d8c8e722e28ee598d2cac2bee794c133a417ee6bbce07f53bc714b9f61d938eebc228459e3d2e413cb8760d7488a8e1c806b030eee60a2788d4fa662b2eaff83380bfe4606e6819e2e589c2065a1b94770a0993a85606e7d20326c6a0fadc07cdf40a1d789e1d581ce386ee17fee9f788dadde0c70907e7a9690f9a840d98de6ba53fde6353e3aa51a71b8cdbcc916e0e030853e1c90d250c7680a7a5129bbd99979a261c2b2e9566e9ece085ac1242211b94ab01220c243570fca70007ca15777c102fe88b639cf68c44cbf3ddb469c96b56066f9f024055f2f954dc496121157225c1e11b51203df567235f86dbd8d169e5d601770770584903802a9a5bae9fabce3407f60deee01478e2061a7436d8780dd6c4fa040187b60576231fb3b7074651c4181861914c9d6a951a88060d42a51351533e01844cac05cba7d31f158326fa9aa8cf8e421a20e3a65032516950b42b232daa085826125dd0c235c6712791dbffdb6c553ade38d96a12031889b8c78a0094ba0bb9e613496c663fb42ae86506d8fb0db7a534d2760007830d0a15d5a85532c507be44135721448005ec3a8b1afaab1e2b56df7a783373dfda787a6e78865f2b051a596c17ea520f09e8da181cff573d488a5dc110a3f2640a77b5fc2e7c722f0afacf730d55fdb70d51ce62c59fd23456f12a202e61c5675004f6cade581b0e0f694f88aeeedeb95c9fd84e1a9529e5fcd5c28d8b4cd58de37fd17a4aa209fc061491b4e4d1d077ccfae76d855890bb4e51bbf1a3e1aff3603ee575c383c43d0780d7a70398d62c8514823cb8dde292fa8dab6d902ce3b08c316c67fe12f38a0e21e2adde63bc6cb9939f3ed52c4822efe15ed1c61f7e0d2579f06b2c98505506eb1d553b7adf76ddf7b6852acd6b598dfc39c99b88a501dfec834c3e38c37a0527b27645d36374b500faeda21331ec1edb7162307405f85e063f7907cd139a0d5c707efb3686c57b222c01e6e1aa532d857dc81acc47cb60e3d3305f09b4660408ce84121986013b43698ee27a6085684071b0774eecab30968753f1b43dd801c7342b59fc0d21fba0f633010633a41f91175a026c64b45f2045c34ce8cc2d0f3e2dee6c3422b8e3e5ec0af9219b51fafa32805b6f7acf700a62e83e6f6be609e2977d633257551f29923d0df88f4690c187088983f302a3de267adf9dac57085a8dde3f3b9cd4648e2bb9526ee692bd0a1d7a8cb2aa7f6b1675105cba474f05335f1a47c489cb1ccdd9cab95be04b6edbd8392e6fbf541f2a84b0d4fd5d3d093ed9397785a478ceb4594692d72e646ed524b99f45aebb847ee755d455a6da0bb1f82928b8db3844a1672729c1acf994b1ac22b42c5e8c85390b0841374bea06b32650705e51fcce376ec536bc1081c698f7ebe675749096a41323f3c4baed0490dc8aa8f733ef284dc467d60b8d2dc72bbdc44987115663b778ddc62c6c9a02aff0dca6b1d21df3a79f7f509af64634ae944ed528ba05a1ac07d0107c20010b132707b34b998d1645bd07352f10730743763b6fce70e610f50a50d5adfc7e05c3fbc70fca726f184ff9fe1c2d1f6b7a5ee01a5cfd27afa973c04a6594f3c0049d391dde75c55e0462327278915136ab49b7d48376e26e90f26e151dc5088b0f08c8a7143dba1eec627cc06e11df23c2f0a6cfbb0d30e45e15c566c282785506531789e38acd9902010ede1ca42fbb8a6a5e65ecfa5dcfb887b21e9918fc140eea411deab51f1c457aefc68fd2b2d381426b056732d75ad79bcdaacc89b13d0a124a135c0ca6053f34bd388b55d96c818ed6ec9e594ed78d164ffb74c73c0e088be32f2b5f63d87395571fc4263e18572a79f79f6e505577e312384dc4858009270b8009dc9b5b6c7be8b55c40d92e0be5b14cd53d7c7458ae5717a0cadfa3041769134a34d912832d4ab1dc26c300c7723fc100832057e5dd22d099dcdcc50cb5e477af3251f6cba5b7a8328d1d4f1849b253b180855690beb29e520ef2c405d7eacfe29bef236661d8138663b4b3158ea39c6b5016c801fe284533e9f16b026714beb6dd1b7855cf15a9ef3cab140b465508d0080501dde4a8e3b792287f00b136f697b0ac2cbfe9a9e9968b1756639c09d6988989daedb18ef672b06b490717e5db5c2579e231aab4ede21c5c23e596036f6e21a5ea4b8f2f1dc9426bb1045f8763f88721f6e2a7141437702e8a38bba87fcdf6758ad7492272c7ea82250a479bb2d10e76bb8912955bcf13b4738c6466bd4713889be420008db8662ded0458eccfd5970f4fe4057fea5d65e7faa4aed9da8352b74729b19ca9d1c09a6909a4cef39e98ef9f3857b38cca34acdaf3a665771015bfed5a722d7ec934dce10e1dc901fa4d73377dca8a4fb392ce9f476c2f0e24867df06a3d2403f5a4adbc06fc6b61f902fe830b79a4913516392a387e8cb5fc76948e25860da1fc07d9090b38b986e9779df7a1cf9ac961443aa6bddf3021ca0069cea1db522460220a3607ffdd7e875bceaef63e1774e8ab1c7affc34cb0d5d0e5e02ab58c48a11c08f8038fc086dc0e1fa62a9f745506aeec610f8a43762e1fb554188e0ef15f4803650a7b14b82b2587679d1d0cf1ecaad6eab40f6f391e5dbe81447f74bf0626eddf03a5153bf90b04f6e4e806269fe5589465b737dfc017574da06c1e019fbf57ae079440ee19b4623fa6ed63866a5c2d0c568f461005d6470a3846ffbe851c88f1083c3b21dc8e769457e2f961ad898b94c45b05f32e4869add2e0fdf22cd513ec0bcdb1a07b16f0ef0cab3d70ac110adbe2ad2ec5dd066a39a4cce399dc5c86b300e31deacf4bd1b78044eb402d004bdacb088a7973d91f8044d0002700c0c13a0ce3b063cfe08b5da05d9e10c9afd761d77ebbfa8cdd58da89e31c0fc75bc7f600b122ea5b3db1725e45ca9fbfe4bee27ce6f5d76a3f7d9e7faac3a77203be8a0dd5449b9b6069cd6f04e59fe92fb406df90ac387971c16cc9c9cd914b4ae7630c8f4f79eccf806bf1cd7ff36adc80a872c50f802a828826e133ef3a7ca8c9be0ee6f3a2a1de5c6b7479c6fdfff9916d92e4961a859f7e3d4fd1eebcfe527ef805f6edd21471b71d915e0f43223e29faf55fe2e0da8d8a9788cd79ca4cc3047b0a388456a5e027ddc14abadf7c582ffea8e8e334418a11b95c3a02df88d2954506cc7b4ed0d1efec79c50fd6ec8438df19453d68b7c4da04d6bd64534d19639e0392abd504704d90f29c62923290908c63959ad93a767274d6e9ca1157554c7609d556050c43f0be15c4db7ea83c562fdb4c950f4b1585d6b3e7ccb2ded4db87049c94ac27c3e3c9dfbb860f9c6592699a379bc503e19a7ff8863fc77afc84da0cab5598823f33bbc215afff0b9dddd541b6246f09da911654b153e37a1b9c3b09723dbcf3454f47f5a9040feed603041aa6536fa389dbf8125e01ec7abca32306112744114b42583a4d188b71200be1d42ef5f48e366aaf859f4d9e1411bddf0ecd70c68dc18d2cb15f317a6e7c37b83b979a2684e1bf3eea032484f4dde8b607ee41838f083fba0858a186a1be2ef4845c3917fc9db19a873e90ad7bb3cff4099f6801f8a05777a65b5c4beda6094adc33aaa7d5ebdb2f0eb53405b77a158908c4f15388b21b5fe972b210f1cf99e482b04ab679f72dd0e69d9a3bb4b707c80601224b6201ab48acceef0486c3b4e4c928d34e23ecc163d9ff6d0bcf72acc9012d0d8b21bb98b18d6a3f979a325e8b8c67e025f48c7b8bb98c57af6bb8d83a0991429a7bb2f7ac13bf975ead5974e0687db035add0e02583014c1960cb6dfa0d24089eb3071c1ae33345913b4cd876902c7626529bdc922ba6964e67807d4f086390c700fca7e2672e02c36e80be27f750ae158fe426df4747716832b8d9820cfe6ecf8ec61c5cea8629b9661bf59271f2befa1e84bc08ae759b1e9e51fc9805e38d289a60f604a9b040e724a75231ead53bf712cb8717a82fcbc9f20f63b70f779f38800d96cc5faf80147d5ab7f60d23f9c1b74edd1e4c297a921932825a085cef4f8507d00a6f98878e01643bbc55563a3ab6e249c565e9aa5ff55cbd27076c4f11fbcf8675bfc88ffb36e20261d80a1b62119bfc032faa6a1788615e41011ac602893c5cf7e619fd175560efe7b8266dbc070ab16c332fbfef9af2904409c3e42a5b6d51c6cb5f3376c38529adcf45a14842bfa3fba569a7765e0d81c708eb0fa4268b2a9db5eb4216472524716997c0de4ca2a9f35569adf07ab4b4ba0fbb6f90d783ae4ffa53c9ef0eae190f6059b24d9165fabae62639c3b74583a0091b9a8e8d5d6b8b5ddf2acf5b5dc298f0be6038dcb2a4d29e54e5140d80a4000f39a66a09f627276eee4015ae1aa00ff9d24a4ebb9ec99acb110259a272d4f608121e664fbf624b4609059e2ae13083b87d80975316e07be7e4b4de6963ab6a0cb710d30fe4c7eab72aee4bb3a3b4f57d00df893d3e46f1e2ab659345161494033aa75b6878c18b6ae2bc364dd01be84d3e1dec2919d230a2cf941844e9e79402562152f67b6cb838b016874a838deb12beb5be486ea65e36ec804eef3ee4ff0fa9ac607e979484b252eadd34d0bdb430d13147e079e4a8768ec0595e7d4b460643c3139d259536752f2b04a6c470a14838402c797a72ddc6e51235c647b5640b7c60259ea3b2d78c1201cc7746f0b13201ec7c9595ed3a787194505a3edcc30490db343ab3780607f3eb390796ba094cca5d7f3f26dbf16cc01ac072bdb10f0affd43cf2a5ccae647f654c583ed548038c6ae0cc227af066a73c57450b665c03e3784f04b9a8cb27ba7308124593a2c4548061276a3d176add509f938b2f020c42ba5cf2613267e0fda6258e7da769546a41b75f7347ad0f806a055cddaaf80d37d34bc5e89595f0a0c86413819cdfceae32e442749bec9f71d164dd53434c0235cb94e061389b8a6d5da39c80abfd9a3e6939c559f9e06281c6283e01f35700d0b8502cb342a117dfe675bc8d58986f845b6f8c9f3f9107083b87fcd4c2ee0af413e60352f88377a2b30092589220726414e82bf764967f45b3306f59cd7105dceade154847140312d3050db6a5ed77afc203d9e0d7bd9ab7bad446bafeec01947d03253820e53681e8365d6e7ac398b262905619ced8b8fe9d2791b286dc0c8a9f28947d0d08f424b55e689703f35804118f6407b357dca957745a1fd4380b1f59ae56ab316621401cea1f3187aa8a7e2c0572a6008fc796211b96012a3c0f5e7b8815840a499931d4b2c13616b3cab4816287ab50490ce0e68234626f3c0e5c15d96f21e6f1f37c552e318bebb2eeb14522556c7bc7c79be93880cb90650aeb90ecf478afafe08ba8af6a74519f984510fed90f7cdccc1c02c8eb4232b50c93eb6bf4f8de91eb67dd6501b6cc547d97fdcf628513a23c86e4cd79e4a539040762907545a22bfa406e80f19175299a500e7de78285d3c5a1f7ca530d8a3502711847613b57892f8e79d6539f792e4a1e5d016ab60846ca7d36676009991d0b3b1941ca0668915c1d363691e57ac37143ef313e10c1350169700533b969180d27bd05f4b9d1bf07080f8a95df14bd7d746b99205d39c243d353bc5aebde1a0a84e54d010e82f06cfc01cfd61519ef4de5b870b1722edd0aca2f6a5d0aa55b0039e21804380ecbfb1109b449c0f6e6f1d9009a1b87745ffee997b05bb6f05b0c43a407c0aed0e59a59fc10a63ad2d97087638fc11f2616e844ca92ddec8f19075659c46b2fa93af7d0ae7c8c32749823ce25ed6aed50cf12f59a748c0a421f0bb6da8ec3953e854b64150fb3801df4a71e42e00a459ba9097d2d02366d79560817034899a5aecbe9d25e97fb25d46b2a5e36594dd8603ab4cb4e92ee927e30015b4227bd3187bd957d8031654a9491f27dc312e086e4d168493275376661cb08ba7e915754eca03311dd8a4525b6b793e3cc8eda934bbade7ac5aae4ed1d3300acca58c5952ae556f8b9df8fb459f97df4c101e6bbead70c662e7cdc29db7a0de800ef29cae2917b1ae0ee125107e3c7771324c530902440df5a67f92af0ed0c65d1a45d09f39a19712e30ce2d0d0e8fdbe9aee7ceb417517429c7140734710316b266253260da4cd301d0c471439207539657b9a2dd291597ebc2ce49cb0b066e79639f14c9a1931b3a4061f38700161b7c25991ebc9a7f479e4e7e845a0f0f91d482c362fa19d38249e02583e8b6ad2d6fc3470914f45f1182705716fc770541a0eef814252dbdf2aa06e3383a8c767a528b72b31e41283bd3e9881536660b4ec4d9cfd557d7ddb86ad60f12b32bbab393cfaef13b30704a1381745c34a20f81b5637acce21c3a7398295ed5691f2767caede8c1b3e31ab616964ea114c904c0894339d7adbf5a86261be663fb38986eb038664a0b73c1fed9ad10b264840ad1e2b9e8a8437f96d93a19d8e2ccdbb5a843467070746aeb09e564e1baf01b09be5bd2a0708dcb4ff9abbe792ce1a7ef89699d6cbb67f5569744ec4aafb1860812079435fc683fd0a497e5a6d0f6feaee14391918df417f0c4871aab7d41630a8f3061b1f7824cc767e5f3920ec957b72726f5b96f2f310e98809a8a9861bd073a00c57262d3c970606f72fddbe5c0f8a48d46be8e834992b8a71dd840f529006bc6037ccfa323ff1bdd3b9206b967a2f056cf8ca4d9c11a8e27a75fe1e4b908033bbee52cc0b33773874213ce079132b3ca8d08958d65262f3ad6a10ff5380574ae35f5809748adb8c1415ece2ec4b43dcfbec288efb7fcc519aa74f80a9b071d153b284701ea8f683bb23ab1d532349a82bc08ea517c7963733d372097790fddf0954e891a2acb8fdaf90bfc7813410e71ed8b95b2f8dc1db8c0d7ea4e4907ff3edbf711f02b7204ec56213a473a12e577ea4e4fe972283701f2fbaba1070ff8db2b296f3f0f5a62c52eccac6a0634d15162bc50867d73223125bad85a180d3723fac6b89de797476457a84be129db78a3c59dde723a92a19913808b0974043126360d2081d9f81492053b17aec100ebf0a9cb4fbf38a77c18a4f3b3f01903f3f66934884808975e66f9cad35fd2b38031a48078c481224a7ec9960134fb0022e8f8a267a1e38538bb1ae5392709e094fe8f234fc8cf6ff9fac571a85e480b824625b92c4f89ec013485e0c3baa827a16bea2a3c50ff74f3dbd1d95679f30a48c705299ef0da1d7e2d6b5b5bb79ba98f7615ee23c2e8fff2bb9a4fb2d0d502e9a7740949a4ea8027a254357e28b6bb22926809751794fb1cd60d5626b48d09607906ee235d2669f2f9eb0b75523577e082664abfaba3d5bb3b19d29ee09a0f8d24edd03bdad7376b9be8c727eb1cb88bc913d9f24d62adf671f94aa1b3ae0fd62516e4774fd62ab3bd031e3e34096568da1725cfe5b47bac6734f12f994c0168cf5af8ac6dd2dbc4243b4418970fa6365cf87ddb2ed92f2d9ea766fc14430a2b41172f53a5a9c676ba7fd9372294b379549fc476a2df4991878437fdf3030017fa4fc831ac7ea5e0b251f0abf12bc460ee8167bba7f5516c28a3bfb1063e00b8d9ef5f0bb0debc015f77fa0a5be2db59bc735eaf5ac3d40fc5ca93ba09e408db166493edd9b417ef502a8dbd767bebde023bac5f03e0c7d7288bafbd0a2f01b9b4c8c0f890bf04e5662d76380d5efe870aeeb16bd1f848b7a323920f51840daf53c3efae57ea72279c85e8cd8aff4fa2f10bf9d74263fb794e18b95b753801d2a9b241e9481c296ab19a246c1a5c357a9c031b5de0192a6a6b0d80735b5401e295b6adeeb5378c09d52909abb3a7683a8eff68860cbcb3fc43a6e6dd0c9b04b761c51ea626d8a2a58e9e525c061ae0fc19fb8daa0c252e65fb4bb874e55a056bf63e49405d6c248fa3c2ab492936afd7e06f6c8094dc06237f640b9e986a0fade4c1218dfa65025295daf83429cd5aa062027362d96f3d64784fafed1d3507cde328bc85c87d14b9cd8ad94a4b4a04a146c12598643cc13457d34bf2feab0d8de25858e3046ea19944cf57d0f8cafce406618efd7a1ec78cd947cc9914120b558b1ae6b72a3ea581f3a331f48ba5272af4f75cba497bc77a465a55981f080aa94243f1872a8b0765fb471d625ade22d903451f3deea9b39bd8488221eeda0c24dddae208b19f8b658c12f73fac6b7d807d105319a33397b91c8628276ab8008b1725e96d02a7a7ad8de326493d82798538c77d60f0eafc121b0f5f58535403d13c9e19aee1e559e10ada35ec1c51e534da99604e8b038f8b14db35b13afd03051c9804dd3c950bf536513b0688c03665623b24415ed0f9d1fe4b6a491e7f06e6c9f223662a5af048a2cf8f2195ca0e8593edf502cec90e95e51d02df853b01c754b47aab9005bdb80c60e414c61cf16583f7219a23a26f7c2c3893b1741e06349aa1e33c60379f69a3bd9b093a6494f4308b754e5d14722059ec0d34160b093282a1e610ee1a5fad788f5ea8623ab978e68c0f9bd9a5fabdb245f303c1df062db87ffda7c2f83e314b2f282629077a2ce130fb423f6f9d5af54889d84f710835124c3e75f0ba814019226f73cc62b3e06c0776d8086e5ff4ac60b2ef23240c9e18fead0637b884f6a58d0d04d3d367ba6a43f7ad9076532f3bcd83bf8c6e081788bd2e7a80201bf7aff008a7648ac603e531aa59c1d238c215e7a129c2f40a3760b6d329b8bdb4d655d8e93dc7fc148d55c3b746f7726f24acd32a79fbc108c1774c38c40ee3ad8bcc037acb10c45496a8ac35763a268534ddb93f6eb2410d12002030bbf1f9044683823622f0c4968bbe93a38af01ff265c7273f366a1402bb70523e6142f7938724aeb431038645c7528baee9aab280278fc9ef9ad3d20b2e00f104a067b18659534492e72b1f61b8a1c949a84814b23611397c0c356a0032e9f6afdcb932687f4dc93eaa4f076d17e22f2c79679e67697bb7229c8e4e072e6c1c9aec503ad2508cbb64db49fbb5afb74f6e595d3e1dc5249f37991aec0428d0b7ed97ffc673e5a5a42dc978645576b2eedcb2b6ab1e1d7a096d33d20905758e7be72e73203a49b579e422dfcc227999941b2aa77d3d3cd64a9e750d110e93b119c3be4c86131ed2d1a2e154823736b91140fb12e5a37c7a848e5a6ef20dd8efd490605bb9261bcde405803a687bb739bfbf0c545d1d3612928639f305050e05ae2c24327816971f969a53ef3789af798135d5b483fd40254ac67294ca0d81b937f3a3c10eee3d9f9d2eb036b3dab3e338668331f5abb9213552ff2e090fb16b97312042ca9d5e83ca5321607c8394fe20a62af832a81a5c3fa105768204870b20b05b23a862a20d17967dd33070754ea52e1de6eb8b269b920c18ea3000cabf7142d1d148faee8e503ed0209c469d52e5afbaf2a598ccc6d379d2ccc505d8129bed400cd07fd74dc33ed5a303005df5d9f087040188f96bb9d22295130f70853de475f0ff0f029aff1b62c9fb6e43e30fc2ecb41c8efbbb49c7bcc8f60b9ec892cc99de654b5346e9c1e70fe59be749bcbcd05bc617144208835ac0d30aaa30de2c570dbe49996faa6e35d5ff0bb8f9b1c94fa926d47cad5d03c716bf046a08a2711a6f760c5f06e0df4a49aea3f95245d28cec398eb1b7b28359286a09a9b640e5ed71b06f7f2bfb26bc6a9f75d8553a34b69e9ad4f7d089a20cddf90d5a3c9eb132e2ac1e91c1d17b86634a27a724d390c115f356d0d6a166fc7f9600f2f7d46a53ebfb77d132d39e4a8e2b10d4cc207b4407c54b58aea342bf45a6043f4b5b5bc1daf1985afa4e1232b842b0289b78e3de87a68645acc6a761ff38088fa5fe5fa8ff700c2c520d5a9864227febebea59534860859f91f82db99c3e00aa3a6571b972db87b51785497a86a24f11f360e542170f10c41ce6d8d9da4c07ca1c61453fc069f9c45c99735d5afdb27621b2a0f19b46b336853f3508b6610670b3c469b157db4b0b47b03070668b60c37dec2abf2e70c5f0f0a1e371c0cd09b64be9fc9a97003c5de6ead72e460773fb976037c9d7a7703ecaa8628fc45f0f9c288691bfdada723e5d2627faae032a011b13088c6c93daace563722eff8c08874b200667e76d14a021138f4b79ab04ec376bc106f244944d16856d92368a009d76c9cbb9c3090648b5ee49a102715e9c6f012092498cd76a7ed34cc5476d06260ca34e4fc3a3b1ee0e99ea9df001ccb0d427de45b77e97a9de70ceda41380aea458b236b42c459f13a1b13591c0c196ba97852024af4a610137f4a5cc9a90fff59c84faf0b5fcc601dd0f62c73a217a3fbe749f0bb8dbcd0f2b745331e990b4e15741a207adb65dd7b8256f2a1fb879c149dc4f25c509113d37e5cd93bee053c7f51fbda2e70f8f5f51b6cd0a8fcfcbdd18e45bbf9c7ceeb1e2babc9ef960059b616cbdbd8d33ef2ed6deac12b347317f3762b7078d0619abd81ef398d170788554d85406492545748604eca1b8477fa13031a79acfd51769ac0584253eb0e183486ef0589c82d7ea8cb716b579f4446c179adca1665b9aaaf58f5e3f186071253ab3e0eae916d99df72d3e4aab2c9499ad7e025ceaa3e864772ea0ed80f01a353ab3346f8b0ada6047cfd05e1c67720104ac39ea940ed82f677ca7709060ec69fe21690cc59933be5bbe83bef92522ea116d991ed88836f3b9c9e2400770bece92843682c62c958e206394407fb31a08ba876cf90ceda963c96537d361d0b91a40651b4045c512a9cbb72f2a240a4dad87f03c2fb410fca545145893dca059b2485ed45734da327a4efb69e50f912170c5204985a1efe73aa9e08c47ee508f055386df94d6a33d123e2f8659e286a8a7537d052a5b0d024592e5a8b5b0c05112e83cad8ed34d792592cbb5ed1cfee8f94d9892e14ab034b0b78c93055fb07dd504f192bb36f02c6f91dadc6007356b17c3f2754bbfa352a515c2b6100db002009e66d8ce5f2a574e8476eaf95e195d4e815e35aa734464dfdb81e7f505207b9d3805cc3e5ca9b672a5d0d715650f9be4437e98cf998de69db3f32ab8d6901a1c2a1ba18ad7a4a0d56ddfd8d9ff963691bac3c39f8a46d7f65d291cf8b1503cec622a744e389570db43d81c66313e5c68601b0b8a8a2bf4bec56589b19c500603c051f0c70016ea996efdea9a8d72cc1d5b5e7e725534324eed869b296ca021483a7a7aaf1141b9860df16003f28507223bb008af2142e2d3fef6b87b29208fbc665cd3871f69ec4cfc649e9a8d7f47f8de63181766b77362a33f872877407ceb01ff6a15bffcbbd813bf0f248dfaa453d6411db1db172d3d56cef41f4e003c497c089c18f400ccd6ed95ee5ad8d1e21d4f0489e63091b574a3b4b9bc7d20e95b79dfa09a0a359afce5322902f4f12058f1626079acf8b0eaa40ba685c100fafe4ad21b561daeb3a06aff79306c4bffe5fb5f4f98481e760331b5f30dea50f0e52c443743ef1c0f2baf7b4656dbc90418d99ca91895a7d4510a22c5035150493676ecd3d30c756b5b93187c4380323e80b60d7b3ecf1e0e6c0e5a5e12720076352f0ffe25003eaf33bca82d73d590a44078eac4edc5908a180e1c63f7db30ec6119164ee7563f1734f32313a61d509dbfd9f90ee46db20a5c5bcad907f95010ef94a963cfa44bda2bb22cae00e6870433879adfa64e99727f108134a1ca3073428356799c36a5dbd2aa694bbee1c8e964a75d869dd40a72cc535aa11d0620b2a2e134ed29551fb78cb54e6590ae1acbefdd7f7c2de560f929b10dea978e002808017b3e17a8a33c6d9ae1e2ae6df05583481d99f73b93611be245f6f7c580f15a715498be425fde43d258ba5f247ff935754581e535035fe05d24e6a1d9d0c1c0cf0332c9849e6051460e6d7648108049aea6dac7ef0d3d26652a51ed181082bc7c49a2ba3539a432839694e63a088a40f783739f52c1c16f59b58a0eab005f4e4a2d9b32826e5f4ac91a7bb029577290bff3bb28ea32ce6ece1684970a302c5c23c88488cd2eda93cb8d1e0d21c85760dce63e1fdc7bf6facbef037231302b802e151789a9a9f2dae420f2ad1b1ff4b7d7984bd281bfcb74adf200d881802865d6a565977f972b069808b625f75ddcb1ee8592e9adfa5a10f1ea09c7e9f0c4eb5205446c879f8269af3da94c849d1f906eafc8bfb6ab7cad110883b80f30c3a43906b94e1b90a2ea6f3b15dc7488161056fb2e05dd0df2c15d5cdddba360d6f3e76812f4067b63d1b3b4ba4eeccada581d382f85b988e19f895c6c5370e008b4dc3cce48c5c89589a959fa39580ae60bdf189bbb5e57a3452144c0d7de706850b14a67dba0e3bd72147ae9b3f67d1c27aa98c2ab75d6b321b5f5a8aceab01bba3aa0e4a805c6287c3c4e9f779929aebfbb700260c9d5b3b31ec23f492af0c20651a72990c4d91e4f5443aba9ad193177b815aa2e20552f21f7911387c110d1ec9b044221adf90ab9f91d01ce1be9e43c78a14a66b09d231cf3785a07f890affc91846327738680e3040829bc2d71ba789925e06f2b412984299468a145703eeba8ae9e7c160beb2560fa77d59eb239626cb886e87c0dcc994bb7c59cac705880014a663567e31ae9c571bc6f493bcd03a2c5c466cbbe9ede1278fb64ecc0748da9e9caabe6893e3674be5c7215dba3447c2867085923661332b6f6b132c0520ffac0f791731681481da3ba861816b8ff6e529dcfa61007e3af93d539d020e500df97ec69e0778cfa6e9379fde1de2e01aaa75333d34ce295a75f90bf1b901762059d42d76a8ea5cb395ec1a8e7b81941c4ca2b4e47d188f5b838465a0020f28b351b0a273e238ab1ae37e485bccca2ee9cab807c2e0ceb4430287dd5c42083307ce25ac70818865c48a8a8ff3cb915deb7cb63ec777fd229a4be5bab79804e2b5e3fe47a6f64aba5ece79b9ea0fb748a8c17f28cba9f0c02cc8bb6bf4fe090d1c3f1f759d358ca1547b416bbac263c5f90555ecdf8bbe2e764cad9ea71a0311089c85b6180f4a5c787e6b9ef978111a02a0f6b0be3ea97e3a8f41e640d809970242be1ef2f1133ce6d82de296221f1e3bdf58fddb81964c104f20dc35240037b289b84642714afdcee50c425d0877fafa15b3fc6e85088d076c3ee73ef90644e9120984187f18361ce161d96ee8bc242b805098f1063f7166057710225905bfd8d94776c70ab0ea64d750117349a18426de549948bc3724a5d36e4f307606fbf572cdabcc2ec1b02777870311354bc54980ca7098e1332a1494e4324d2806b51cb5e95ddd8d99259366b042af00c7b93b40e905ffc58dda55fa2a6707fa0537a8ca1dd45e455fd3a31efccc5861eb74ded58d230299006ead48ee1eeaac01965c461382eb114fd672bb6f9a714387b4845418143246a215205f078695c90f5c18826acb6d8a69235d3df0ae391c6d23818ad77224fbc1c134311f2ff1ad0d0e036ac73ccd57ab3b184769e78c1f492c1be692c82dead17d938e7b7f011108aa176590ec6204bffeee81116877e0c3e488437005cf23ed64f8467b1238e10043852436abe09a2d02a4b6c278cdfe4e2e4387c822b5a5d19e81558f4dcdfb062fe94cfa0d33b7bf46d0260114d2e19ab7fc22ed704ba2511b317aa5766b56086de0fb55d8c05b8a1bc86ee79248a5c48b81b528a2029a1be71da3566ef14c0dd3c73637e03348f2c4385cfb7e5ad0ac143bdeac2751ddadf45458c41d96c200e4c9d5a13ae2f1da2f8151c148767a23f07d8198b98ba95e55e9d49b0fb197034a7a4be181bff19bfcc86b225dd70b066758b424457306ce4ab59bba294b8d056e485f071fdf2326fe6520db7ed667ab11137af7a3fa6dd90fbce341af050d0a6a15ef696314331f034f0847e73508ff03cbf4fd21e87c790073baba59201f033db25052fad044b826a0ab0fbbc07af55db09f8b7bd8627fdd103c0bce796c0f8cb6a437c23d639c27512476eb9cad357ae989775ed12bf9c83a9d784939600fb7a9cfbd65a48a76e7f44a584de1f2065489891e2678f8b83bc04de20714eb0f73af368b71e462d5327e369dc8804c07bfd1dbc941eab85067a12e98201f060127004eb95b29c3b43c34ccb1215835eecb9db6e859d4b62a2648cbc710546d093262a08c71d8414e156a8fbbf88803060ddd2f886bbe8793553b3800e90d2e023a34a42898836629ba93c65d4d0d2d64e8da3aa58af3e75d4c471e43a70087014e0aeb86c5ea51389aae77aa1ce7990288d44c01187857bfb4fdc0689fe4f6073466ff2fcb67df1463045b1db7c7bbdd8adddc33b1261e815277e7319da8eb0fda836e2cc6a7e090e6bef7cfbe48d7ac707269c65200264f2f38bac6e5c101064cb224c7f6fff97f4cdd2b171256d85305bef58f975b677117edd26f58801c0a01c31f899418712eb2d547f582726a395feb419ed2017413f44e6803fb36ab0c77aa30f286fc3f62dfcc0fdcc6f10f551d7dde820aef340d718cb273fabb320716feda7cd4d38587a8c70310d4da2b072a04cb4e43d970807068ccd8ee790c0c7f04657c6b6980af69626924a02c3d74240edeb72171f632b7cde9e6215b1d06440ad3ad7088c071f980be01b0ef47586e1b7738fb7b6715def561801a217b0e28036bf9337ecf754f6a88fbd2200c6c36042998f9a027879a4f94768d163a0a0b82401b64aa5a50299e8bd2333677d8643bcd4266da56f4561fbe7c123d9f0fb539694a6a2243638f626b70541e9b0037ddb19dac7b2cd011b9bee2d3abca0cd1c96b31aa74233b80286617147af438cbacc6ac4e8cb030d77697ee8f04740abd31a5f1a084b6d741842864913a8f3f5dea88806a3c14fcdef007da9033ab00824f53f972008c1bac33becafa3014c41618fbf7af7a55a4b27ad4272bdef00f182f21d15d150ecbf5485fcfb4ce6425d594bb212692569742e28de4e75d530e946379e1d5481737ca3728cb7e738c59698f7e4ac33243c948951fa1950ce10abd549638e9e9c1b3d41ccbbf98ec0136b4a797bb0b237b32c03599c4f6876f0d7a522fb8f37b7da8d01e1924dc05888b1b83e0c613a480aa1a7925bfcdc33502722249b5d74cceb6cbeaa5024c4a5f5d8002420d4898b098ac4497610038980651b563c960ec0b667a0a3ad0d43e911d13c3af110992f19ab94838212af15d05be843bc5e063fd5da5fd8b6681f45562420f542e8df8b301553728a62cf2bd0fa88592c9ca4e259d42a2a1258cd5354872d07d275e2557ef9ed24949c519f70066a152fc86a0a86d1098f3a3f9a4ded3329d10a8b3c88447e3ff74886abd0d0d0e8c75af8ea10b0b1c0b350303b0a906c02b496b64b149a06b5f21aa74ed360cf162180dd9020f478ca79e6499eba164f91b6a52ec4e86b15d85c35d259a300633b8daa01c261ccb31789b36a0d28a71376ca2cad1da65f71845d00a5298d90cc2affda2df98b21cb048cd8a463c4723e4054a5a1968b330ba8235e06cc5ab021ac35eebfb83b6c15338be71ce0b4c4141b0fac70b778f26cc992faccecbbd06297e0fba25b1ebe992a0462dcc863ad0d020b3f880f4e8fede60725144570f03f18cbf56188ade99ab33373b4fe803015721dc17b13b3025782485151b2ab100dd4e857c8e6f8cd3f07a69c85f25a46ce61ad4b8677b61f8aa2080ed1ed14d04057652f29b91eb2d5efbaa632ec58dd01454650c8b6b403aeca871d398ec6500726809b9c4661708acdc0a7243a9c24326253543fb370120c0bc07eaf8cf050dbdec253d48fbd73dca3e382f7e2ccc392f9eff833cf00520a2e877b4ea331c0f8f63ff1ed94df96b32af36ec087d5197c1c9c512034d5dff65a00aa4f2a3460255a39b61c2e8be4879b142204807cb2558a6b08ae493df54962a25761c25e30e6755590e2b5734a3e5eabe52d4faf300b0cb99aa70203b9639243c8aed0a9e0daad5aa7e04f7ac7262f1366be8ffd5093a32c47e8b8b80df61a9b9a57c7e1b08970127619b6857c43c5f1a7f09b8bce221bfe3600830e3cef03765b58f082e06a6a1c320c93fe31ef1ad8e0737190a3c68f4a52f31cf918bd67e163a5d429a04f87e2314b5ff3ae03ddb35e9f4e484de22d4f24ebe925a0899cd4b4f8c1c61002a60212417c67a22897a51d41e4d14b117cdbdd403bbf8a06bdd502bccc11a088fe94d3ddaafa7cbc0d333cc68d0d19ded7da4750ac538592efbf34d29c8070e91028f47955eba64b7544d737803b7c712ac293b5a40c9796e83eb2d7b9d530a7db8c6e0085a45f0ecca5d5f37006f19acc3d2d9c9259c444506b02cb446c80ab1e0f055aea2ed79ee1919273a26dc40cef44c5d7e2ddd8f5de1baa5ed85af0bf0d4da3e11544457e91f30008427a3aff1c0fc77b4999687f070f2cc244d6e03bb7ad849739531db75c4a6f7873fe1f8811cc7358a234171d561e9d642cb7f0a72bfc3b2dc511314ec66bc4b85ebadae50675d9f14d09aac3cf9fdc06d55be0587295bbb514fe8b239971cf8a31b4d25fb2e03d62c54ea5ca07d48310b1e0e0086f5c838c22a70fc29f11115660842c33652dfaa19389ef62f5b14e9e77320080c1f3bfbcdf6085392457930f3094d89b97cb851d80ade96e276bca9472c98075421a0baf0ea4227c65506f815a5c033b4112005b48dea78fa06a8500da7ce00692242c2091a0baacfd10d41a2ed3022ee1b834c9a8c862a8ca0be9841fb9f0cf57edc0f4100d8bd6aaec9c0ff2a770fff704c107370e8cdf61d3b97e16d480d3f6ca32b574822cce57b8914920de3d178ac38e06d785e32fdb9ff0aaa525101ccb683e8b5331b771405ba4d561b9472d4eb87eeeece347835c0014bc2900d03e2a8cf9b3ea59b006a610d1115a0436fba8e61fa44d52e511bf5216e22929d05a88c64d13f7bd6c92dbe1e2c1db6fa0e0f934be43d9fad72b5c5cac8b4273b06ace8587241095326f296815cea3401e3c4838336e810709711e6472898fd3109431134f550e25385f6794aaaa721ae7bcd1170a6e81f62e52e054d747a57df08bb34a8b5ad62a8552ab612a0ed88ecf55ad04a0ddf20178576597138a88c0202380ba5b1676708ba7f2b2bde97ba77c0e5ea430ee343c2eb0236cede26c85f087c3f18cfbe230160e5d60a8e0e93af45eeb8ea6c6adc19702f2ca20c22aeb008d1a4c2e433209b5eba285f7bb9a094522364ba7c50eebf674c85a05946afb30f888a95a67859f57805c70869596b4662a8fccae4d719cac72179abef45537f0ca62af829d1a498a55c3f0afe737e618b401bc7636562d3f1adae0cf7bba7dd067387835bcd1c7693fc428a4621c3bd5ceb0923885b7029b652aafba88ef5830e82c2bf44388c69da50c4dbc3a782c1440c0499a38ed841f0f8109d77feda8a0c3b0b15f71a5102eda43768dbed26659387c0f7952fac983ad57ef00853e14e0e14eb5aaa052ac529f29fe07062cd6f7ab26d7f86570e1e020cda2e97065d7a037b5e42a8eb349aa860414c411bc5da995447584195cecfe7c70fb101dcf54a02a0d3bee2811c6c92c3a42391f4d0be322d44956aff37731c4d9d02d0bf6a340166ee0fe711283a17a50a09075ad37b6d383d12bacc5808ede0c34e7b24f0e002f2524913dc6084dbf648dec8da45e876116dc0106b304c8e3c372992d26e6d009525d07d082a601fee63c8ff3967f4d87384527856153eaeb6dd9f4a3f01cb0d2714f19acf65905c6c931d952351a56873d14061ca1c16ee25af4907ac1c8207e93feac6f69b47068d99686cf6853eebac580cd19356a8d434b17a20516bc904302b29145f5ff62cb79ab6e605ad4cf4ad309de912996daab83cfd73b3697e07ab0ae7c4befbaf9639acd37f65de0ecb87b1eae47ef4a239a326493f02a8be0b83c3d25f64d84e39bc7faabc214e83ff8723f1b8005090118ebee84758eb500d659293c0afacc0250ed7eb53389b11579d0e7c78593751cd787048273d52460067f448dc2bf80b34906d79f06a70a6a342ed1ca03ea4ef9e8fc5c8e15fecd70ade80f277d1040b97f24bdc0504a7bc637b492ee7ceb5d3c317be2cec4327d005b59fbdf862d05576e909970340ebdce06cb0f36adbc2e4eb90f2ff8c9bbf5406d47e6766880d6b30a9a2b258e2298174d57aa15567556b1318337f52dab8af09 +generate_ring_signature 7ddc522257f485c1585479e1c6d5c9e54a23567da8fe225dfba7c81b98616161 ef106fd554ef6d3e26ee4d2570ad85a7e149acda34e72a0df1a51c364c47b2ee 128 0317ce4bd0cc9ef9f912af285724e036be53b83febae675b01c5b29af2f7b1bf 641a763c110710813e784150d772b24b5d9f96aa30c2551ea7d46efe1686b0b7 96810aea9effb6af029a05652ec6c6a283fb206272dc530a88f18e5177721559 b493c1d4ab9a880e4de18331e561b7b2e9e8d4a57b8d66c55d562027e869876a 0a2ca57d790cfa75466220306eb4d9ebce7fc8b9586fe1b3ea50b35cd0b57e58 1a9beb4e1404d584edfb3d0b6b2d02c65e55d799bccaaedbdc7b090a50d3763c 8230d03000d332286076b9e853e9940195d232001f22b21e5799ed3c3ec5f4c4 9bbc268c598212991f12c2c554bb2b4f4aa6ee46f11bfac9bbea653a6e877e54 57f4795a4c25f6128033e133ed805581110d02599d96578ee7168b8145d12e9f b953e45f89694b1c2f065bc03206e77c186f20b0f857fd476c54f09a25edb61d d4d817451b92b60845645bd79d73f39f3ebca01c06cfd8148693d788fa75c9b8 6f5232cb3df6755ff77f74275e03d4e656174c7cb3dcbc21310a79fcf000b0c7 5722d3ec45c3b40e72b70a785082aa69ab80e5b61c6c5dcd4e46934cad23b694 c4865c17a41fe6a211c4e0481e20ecc9530fa29390111ae7824ae32719d1eb7a 3dfbf0cb83fadd3ccfa44629fbe8411ae5b7ad33af32b209b56a30ac0136a9be a6dfb0d9c3c49a916b12beef05a4ed252dba3677fff295a1eb81b8d80d263565 f0a308864738b5bccdf3ddd3d59e27b51fffc24223ce373f9ef9cd4bfcb08cdb cc5a1cd84122fde1b883c9aa316aeb3034baa8893fba4b3facd14ada22a669e8 f6ad0512436aaf1aee279dc12b26d555488313d059557a25f34194561013c35b 65056db6a4789c2b9d15faf0750a7ecd3614a749a9acc264d2354f7529e74bc2 6523be25d4fd36e28de2356e1997daf24082b7b95af0723e259561254a717351 2c9feb80e13618b261f393da5e37a6226121c2281acdce436b70464c84be95c5 5d5d076610c09a7852b07ae4e10cbf6e32035cd2eb34cd33abece2c4f59a860a b815538a729f1d590c206ada6c4af45c9618cb72df31ccb03bd204ea2ac0da24 fe71023f4dfc528d2d8bf1868e03d771ee663c04df6b57604365373584cb7500 33b54fb1f79551980d55f21ab38d904f4beda8f16b3ca9d086badf0f31232220 e0c0107ebac03bf9919362889c96fe8302b9ebf3990d9633de2b33e78ddebc3a 761efaea744a0d6692d4791b393e399c8143991686f1a5548f1eb8958f75c7b1 0215330ecc78326289b26f6fe962a65a4bdab2808019c91d19c71718478430b2 3a6d1f2e86346a835126e54664c20b433dd879fe7c176cca57e54b44b1a045cd 711f4f0165a3be325ad7bd39fd49d8a3483fc81b9ceb16d1f5075023fa0d1448 c1dd5fb7e42eedef3c597bd2994533a236a7742e2d813d88a16360a577e38bcd c590692fe47e8d2f8923b2f50f1b57bd49606508b6d9ea46afed33ea92c361b7 a2de7534a91ea14653b272e68bc18a5e00c7cd8a4f267e1f9537daa0589f10f3 25afde955a55c746cff3294aabddea9d394debb494d0c1e41cddbcbb7b44a959 e22a3240a3ae18326ee642977f85665bee895c53a5dc09405c964eabef35757a 6bde88b37705d48162654c13011df785a6c376f512f5017e9c962071d9da36d8 aa37789087f8e0acda73bc16aa62ae62e08abba3febf1ce47c8aebdec147f70b dcd67a81750d4b772f104d3f530f0a4398a6eca521f9e2f31eb3e36066903270 e7957bbae91f437c13c7d145286b383740741ae0592ac22cdcaf7ee77d6ae9f9 bb37f49b36dc3ba5508d5023bee88abcd7583310d5cf8624a4a68f6d8d226972 4cc8fa0fa9be3577e74e1699b642c36b2da81411342711734149cd6f505e5192 ad949d4a0b9a1575d6390b5ff9604dceea8f5c353839f2e95ec88d63952f5fe3 f2a6c29b1e54a4d58a369c063dc6b1a1f475f9707b889c3cc19207d61e2bbe26 dab4a07e99560662ccf67f504430ba9d7e24a51075ff3cb5696391292b349668 39227de786b95f0b2377fdc9f052162a02b4fe864dd0b54d731d2fd3e267efca cda0d776b3cabd2fffe6bad1ee04b3a7e316d6450ffdf66fb6deab3f841cc551 a763f2d41ae25027742f2f211927b76450316dba4d26be72ae8bb8625fba01c4 a978f6481c3cee445b2dcc7f419b1c53d94c49279dac493bd76d74009b03482f fa267b9f2334894d6db3d1c2d4369ed11511c86920a68f462f4d8af4ac4c38b7 c016309c6fbcc9eaecd452ae07bbf9da09f10e0a2cad6aa44f3f3b8ad4ce07ab 0414a75ff4b281d84273cf56cb27a4e27466ba0559243e561617d5533f64cd62 82be28df3ccfb3e4e726054f785d8cef2110523783fa041612169ad8723c07e9 cacc954444ecda475afad7475b8f297490280602b2b13ab0e98e0a4a5a726dd4 6ae6e7d4d538407a2c201fe45ee1df35cfb020454bb511d1a2137d7ccd39c4b2 4fc6a172a71b72889e6c596c25f5b7251e42ab6161f2bdd3bdc7ee424f600cb9 d3fe5f5b5780f02fc6789c07eb0e44cccba95ca580ce7ad86a13d0a1aa5a65e5 403689e51e45f3ccb5755d56c9d046ac079265e4c0a299134fa8e05b36b1517c a6c0a452f9c4c29901a1d22806c6cb6f0787e966086aaa31b4014560a5a49fed f7d597348016f20c9c383e9df38f2094eabd5cdd5375749a01811cfd9e0aecee c5d7624c136c0f557e347052314450d2e9e5fd60a3322f4eb8aaa1d86d6c9343 ad8496ab3347777ebdc7f634973103c78dd230d5852dbf191cac9ed46fd783d7 484c01a6604b1bf9554326d22b5e39d9d5c3abf8a945692138f664269bc6d052 c63250cc1aab7572424e7687b482b82d7ff97da71e027e6b657cc0edc36f9091 8cd1b33d72eb6e4b86fcf22aeabc57d785aeaf01a0924f1360d724f96bd9f7e7 a20fb90943dede1e58e6283661196e0896599d8e7fe02a0dd4bfc4357da651b2 6bf2b942a8a96e676de8c73049943b168bf7a333e63352c0961927b159669b31 ff3276030598cf3e27d9861b6e40cf46d5856d7942adef5f7a9fe994dfd63c9e d8ea04b2dbb58616ef41a6575defe8693ffa4bf7ccd5bf23ddc2869e0a49a3cc 98c7d44d0f74936f59eaefaf731d363d9536765f3fd27ecaf0e009d8878ecc3d 1e1de9a281b0239737fc838bc66f20c82704be6dae969bafec74e68d4766dd6e 6cfc59b35f6be2e31e2ac644e55e8d842f08a5240e6a773ac367d2c9b764186b 2f1ab6da1b7108a8776ffe7127915601136ab0109f6d2b678d1f812006fd72ad 27280fde4b2fbe5c553b6912c388964168e99d0db8fa9cfe81a1b074a961f1a7 0b0685e9986dca444c9dbcf94c104599923e548984a518d694a1f2f9db804e8d 2854273a3eb23c3dc75ba309ca9bdd5d40b74ddba8af0da8b52aa5a9ac8583c7 9f666674d6e58a01ec9da7f70825b34f8f0e505c14706943bfc8a190c37c490e 95dc6c38d3ca3f6f9f23a7def6071f9ff98ad057ee9ed7c7582d281f004074aa 2a85745fad6668212f60e79783563ec8df5b62a5d1a0e872970d16d214f14dd2 cd0ae63d68c4c63dc29ace65061cdbc3e3a724d29e898b860e2dcc9fd8291801 e8eba92de9e2dd6898d97331fe0add5555f2c9313ee0409875eee032eb920743 9b89e89b74c3e118c7d8f0ea9e52b191cb5a6b7bf45e4da8614dba5454f0b658 c8fe08aded58f3a4cca834f88894bd9f65517310c62c8764f367e2ff68fb8103 6e9b6ef829e090d3c4c3afd2f3e782fdbf69d2535a3406f9e0898c4d32ce0d99 c5c297418f40e3e36e4151593c7f92e3b70d9b1722c4d9b97a97ae33957f09df c1aa7d2d88f3fbfcc02cdc2ee16f1b45feb223ecbabf1ae1f8f41259208d4bb7 1e6bab3c8694ef4a6d18d5bea0b59ae6fd5b5b44ae90aa2b9161bdeb2bb5ef30 54b9ce0c58dd90676f399297752dddaf3b62aed1c1bcb8a870e79ffb47f443aa d703ebdc21ad5c168b87b731a5ad22bfedf7034c82d39d4d96836efb3c2edd8e 63d959bc776a704890322be07269ee8b38ce1238602e4d57b25d7fb5002f6716 7b7ad1421c82f0659c7de7d5242cfb773fa1243ac09f92aed438aea604921947 c6b925089182bfe1e2a946f7f48ef242b64fdee25c9987bab8aec9b8c7e27797 090e1016084f62a92b356ce0d7ab777731fe7788df247d800784ac5ddbde43b0 8eed975c96c6adb82a5684c61ba343192ca08bee83a4b0615e60141596daa982 90ce98237623db7aecb4ea8645d3b594c376819816eb20bc1e2f83b3704b91b8 ff8b2ec954e5de4fc7f2eb77f38e14ae540616d2733bca372c48487c59cf6bae 081e987ccf1e337704aff064222c9b68597fdbbff6308b17602abd94c467795c 111daa7182b98eed8bbb529e707c5e13a7b4a2141d6867d9a25ccb54e10b9657 f358b1bf78b03be644100337fc67b6346d31d74bb8db6549ac5db7450a76fe26 a48a3b21e1bcaedde3abde4b987a70fd716edc13027c23e5aa514806af88e6b8 dcda0a5c2fa7ea31b35dfae9a3372042a34730c1e351d04d53376738401069ca e03b0ce450af6626426751f1b48ff080630bb9e3b61d76ace958f068fd5f664b 4f3ad5728df87caf8229a5f8ab6d047cd97ead40a53bd49461ec0ed4d550845d 051868f6bc842f3139fddd9474d9432483516de6cf19fae2bd873515b9b20f12 4c0bfdc24a5e162394f2e4f5c82e3c6fbffb7c488ff54402feda3f8104c189d9 fa36323c054b770fce031b0c105e23ace54a99fb6ea8655fce615b4e2163c278 fc22a7e563db65d5bf0a6de515b888c6b30f63416add843afbedcbd07423d78a 91503017cc05f4b9d9de7b71313974afc153fe438afc11ed98124e7aa774bebe e4d827f705f912be34a1f967f46ea54ac78f978bab315e21a69b89c73f35e7aa 02da39060aba789f94747f704fcc8f2a01186176648c1ab62c3c70ce3d6ddd15 c999e293fd815160a60ba7cef541220e553a0591274886f4d85823c63d568db0 30376cd4d091852bf4c3a4d6671a69265f43e8fce52ba14ec672615d2f51b7d3 9a919e57bbd2e66bac26ab96ea0b68949cc398023e6111eed82017c62ca8dc2f 6a09fd3b8cd053da5d06ce44d96ba2221d0f7bdd4900db52d883205d5861ed9f 909b2a10b5f1ffc521d71461dc3a2ded7ec65f7200debf06363fcdb322fc8284 e7fae91913d60314ff2e379b7f69fade801ff9d230a507236cfc140db93cba14 6b4a229007f706d6996c6717c202db25fc6dd3f824a7690e01654dc9939eee33 52135b686621d0e7e39662ef234d63da8ffc6130ed40dc3c5d1076ae561a9f33 39011a232b2f4f6be17bfa22d350c4a067506d328fcbeddbd78d138a74a52ab2 435d074732e9057b1dfa4cb0b76cc870fab9bfc9c4f599e37fd45ad673bc4c1e 3c11e0e9fa5ec758def256c28c8a6f3c6c97ff64f567421066cf085565e4eaf4 44e9fd975f0315b5981e65c78a429048b9355af745d5121e20b687d7b867e310 25d576f2513c23aeb545d6be90054eee2e3bbf541660547b5423dcb5b1bacd8a b8e19c3d8f076327a011fc0a7dc93c1a7cbf6870fa0a5b1f308a2d5580e24b20 98d0e2f547925b8c4708ff51966c465b905be43a6e36f5660d534f336230d29a c57118463dbf68c4697d0b0dd9841c71fdbe7dfc04508381033e0eb9f8c29bd5 b33b939dc5be02d6ed342609a712007a0442eb6ba4dc9fc2153772b81c7fe3ed ae993be9c234687fbb222f51a3f05871262da7db24571796d5b7e71dc3b12521 6db703695e6c26eeeae2d0b6a2fc2eba6eb5b9995e3e4765b9a5b2fb2be4770d 111 0531180bd45be210301285cf1fa71d6c225b5142a60a06c23b9a270648656c055d62d54daaca6e6ffa41d238958f5015ef40b00662a29082a7c6cae9e7474309e2af2f32a5bcebebe307d5c55458cdacd9f949d5e71ab9e2894a2edccb425107d4e80c6b72e2e9b1b959070b69cd2b2f52caa86bd2e6507e8caa2d2bac13df0907236bc76b69f55b13525a2c0c4f5d41b2361acf92dcaa61af2b870944f7e50ac9f7145ef40014fce509d62da14d4a57ee1b83205396cf966db427c30f4c5a03236880861b04c81ea6adcaca4c3128506b9305bca0ebead5590554f668011105bddaf30cbda3343cbb9d00e8c708135fbab90e3ca9637ecc7cf5c909500fdb0937bcd1c3a3aa128db15dc80ed6b46ef6b509a5b2ccede30385bfb05409e48a0dece5f842ff46945d6852cac916a33b0ae093a1a1aeb190df6fc905062555070ec0254ca95bc9e1bc196ee1c14f96bf84a9c096b9d15febcc2e5b08ee3622e30d6736c593c8b06cdecf98b2844db0471b69311779301f517004e751092e033f041db67cd1974607f8aa154c869f751fd5c705c204c05a333f6d5773e659437e014e24b70a74235826332044ca10e447f8d0161e63f940362441e486991bbc2a033c76bc62dd9b4ac09d5b5c73e684ca2b982764c4394ceecf292b1fbcfbb396094ce4a5ca9a593f75ee3b239de7558f25ad0849d14468654cd08c30aebfd3e70fe97417a8c5c3ecfeef0004be23b504a94378ac04dfb8e9b59b33fc4e49ff7b0cd3cc7904edf441f4b6d1172d7f0b3bf00f45719d2aebac54ee1bf261f746930e54b660eb767e51896b7543e1a44a222da92d437d6f2ed36649911f0ea994c70761103ea3576ae3d914ab6f16e987594780d6dfa3b0b5bf679a5bc00946cffa03ffaddb1f7e521a3fd67ffc1e60ed0991add3812c501cf66058cafe2ec1557d0f89d3db17ad24323bf156b7ef0bcc80d41f4f3b992f7d9da27158d3f2852a2b099bb5658fa113387ab3ec2a015205718ee211ccc93eaae9d4412af094e4fbb403d5f71e6178b50ee7ec5d057da953f8f4fe962cfa4ce969b33c6a48dd9caaa402a1360fbce70f8c87036d4c305aee42447466ea3d7c1cad7a7f4d18192cea490ecbca99ebb21021a6493b46d55966126b9121cd3cfefe2e3651fa0d0601275608a703c91273dc91984ced641ab012719b4e2d302f6c0327a45f59797934fe670e8dabf373dbbafe33896d7f88b3d599c4bc2af4d02afcea47eb65496da3b5d902a7e6517390f9ddc797ce1af98d7724ffc5f0a5a5199de408419a8e63c757f90ba656cbbedefb8120e808ca144e54f6d74806e3e4456d547dd65181655cbcd709541d1485b2248091fbf2d9e0e8e565de692d432b7c2f9ce2593be286f2e5880286f0e4d37164bd100e2d9d89e68e4417c5c3bb01348d37d60ad863265d2502065d58321976af8c2b79a5391154d27932236c7d0b14f9cb7e807484af2f422802d8ac2f2580aab1b564a240652ce32e1885a82000d5fe3e00f0d80d2ecb737d0c3feba1a37615df5b0e2e8c3fab1fd7954dce0084f49e85a4c88b4ea63583090723806762b945a0576665631231d629755e725effdbff70cc9c2432ff0a00d701a99890a7fe3ae1f467584380089a71c8d829b9ee332514ac8743c83839929305bab9f355d5d0dc9d7f8574c1d4f89930bf0de4ac06b04f51f3958a365310400692ec1b7d3d45815744e3a9831968d4d3201a167e54e8b4481fc6ab89379de7088bd203695954769c3e38561605440e37a82034c15fb41504f78eeecf047c2a000204bb58c67bb647fa66f7e107fe653dfb900876bbe5b588e3b8aceadb5fab036707011b880f2aacd3509c0c7a8de2aaeea819c1bf86e25d150137b993d167076ab05257ea59d2239cace3b6663b7ccf510c504037b03e0664775de0b67e2206c5fe07b7d2991a57334ad4e170dca92d5088cb41e0b32cab9b7464c606532b0ce8f67532fe05c4caccd94d61b4c245ba971c8ae2b39f469600fd07a8786d5f0d2f01e56492021dfe3b09b436c4c9fe23e8141f6ef8baaa354e4934a15ed5b204141211b4ed897e18ff593460f605f2727432000d8bb45f393816f6c9bbba8405d823b783282796bfa1626b02da3bed2d77243a6ae6f3e5db15eb717e9932300fa9aef47dd1148ed006c89c029f606de4af5b39471c788597dcd21899e2cc6c0fe67350c8536a6bda5cc3749af1bfe21540261fa4834ca0a37f29932d3108990c96318e956d821bf027013716c9e334a5fec20816434fe7669055524effb2c4084c0db6f33909ad2c648aafc508646d342d87b97486dabe1d93f49556db3f20026aef9f59608402bd9c857c9f86a838a81ccc3a7cbad2fed2049afe5ec1679a035d34224ec82b4bcedf4882a7367ae74edeada6bbebb22a8b46a59b4bd07f480dc142be32758dce59c6baa95769a07401336d472d022979d987ad94c948e69502d62dbfc4fad8795fe76d534cc4c2d0fb60759bf1368f9f6a7dd9c9c9c23835044ae114bf0d2891535a14ab7a8d8d9d0506d1e5d3c4e66a8f285d55da639d5d08bb3872388b6c3f1d115329f2b161144d308e43dc648dfe296612533d911ee908e139aec7e2e6f41bed56e4479a6dbfe28ce5b0920898a06e8e4beac88fd2250a266876dc57c87f9e0bd0f97cc7e5a09c32355bfbc3e1e55047da87c00ebf620084671e1f08914050420fd3d2e1df959c9fdb4e2f3d819880e2aaab6e1231380d006a7fce0472d2c6bd1f26306ab4769705eee06ab8edb6fd49612f6e0fbc0f0012ef3ded37c45d0ea1c9acf80c0d300ccdc9a362d7501ee3dc5262dadccac20b0cfc6237e5734e942b2c0ed66a6cc7f07596da1b3fdd74006e86ad001da4b50df73ecc63223def64b7b6307ae1f65c957ea3dcf901310cdb63c67c57c0768e09a06472b2a71ea72532c768c60d65caa9820928ef313289fb2d3a74e98fcb3f03f9317a02a23970dfde6dae796207d4aa1c2bf30e6d57d80b9f016700d77a7007de5b557177bb0e28483c1862e41e0ff7fab6cc39d3e7d0aaa64a1c03e3aabc04569eba1fea0691ca967a87b8afe017358882b57fd1e359feac0a8351d0a7d90c290bdbcdc50c9b2f9ed58e3bc51fd7230c44d748b1ff47457a6ccafb6677ba059ad9a044cfeb2dd82de6f8946f346405f2ef1d1e9ff22f4973170e3f32432102f11ccb6407f0a3ae7d3809807bb27efa4f535190355cf3b5a8eaf0040bcacd0f47efccf4a2d65ae41e12a7e6b35580487e2a8fb1305dbe41ee03e6bc27de8b0759319d7155230e78666f97ef91afd0493c0665128ec83300f94a68d0172e8c0e75aabdd5880923a3a574d6e5303728681fe598e9cbfbbbc9c6e7d8a2d9856d0e1e2e5aca44ed482bb354f19fa933ee486620b5449c2e7a32694470adcc8d4501a2495e3db4b9d642cd57f435b58604a202249d5656f1ed5dcc0b70b84d6c70059e4cd9052c72243752e02016080737e35f536f110d28def415c28f29c054550782d0102746c82732940262efb636865cd727eb98a039208060d18ac4f9783b0ea6947466cfe810d43abda5777a8602be2988e9661e975d4896d1f7d684425c0a5968b59919968feb6a9c7801fcf9dc7311b597dd7f18fc72c4e773e62198b40694e8bfc6274dfccd8924e0b3dcde8f73734df2d5ea921b0ad39ef42fc80b080d8c57207939f0d270eb49944f44c5a234bec2d62b94db07e4806096dbd44c0f080ab744047b44917ae81878980659c332e987ea6a527dcc21063c149d8103de0214a413c707eaa2bfc55e3b464a968a3a7d0792abdbca52de8a12b93eb1c92701ea08a6755a68f6d4a15a5319f80d55273fa97ee4adc2c05eb7021259cb8e96095a735452d060b6566c40cf3e736b128268ea020b52a0262b9556a4aa24646904fd26561e9bfe84c82b70e350790163f3f0b6c8f8c8aee90c7a0801c48817510b920a5fb4b53e163c7ac179c5a3b201908d5c669ad1a7ec79b131f96562dfdf03641166c16784fbfc9dffa08449146c92fb17b7dd53aea21d4498a8640228aa0cdc7f0a9e220d045594ca8eae17cdbee0bf3475f4ddaa41a0a52a5e3ae78a700b04b7c3704727bd899eb04e722d1163759f391aaf885d2c40a62978aaf8d87a04a3f517ad80faa6db7b79cdef68ba2b977f392122ee6086f731a277ffe495f307d5a4c4a7fde9bc387b18503f76964144d0a00f06be90a4a683f00df35f0a090472f0145000f599fa99b4e09fef5197b3efd7d209ebd381a5fb96bb7cabd60c0a21585f5d1b2d073f50255f7c3c15241a031b25228dc0fa826ee039f120ad3a0e6a813455f88dffe3ef9ec54e33e64b896530f4b4a74963ff218d51ac33e9a50b99a59fdcb6d0498f1043fb3ef54635558270ed2e7583ded3e73d4b5d3ddc5003fb1e4f1929e4a48fc6bfa9e0201f24c29ce2e4c8c7c5d5e053be41a66faab90341c42cf1723b8d3fd9c625c5df7508f18e804c063c8d57d48f4e59f8f7450f0277e471f810e97630ec49fcee21445ff33c37bda200df709914eb7007a1732e040e3fa07b54b636146cd0c5ba6dacdcaaa76ef54253bdee0d614ae1ccf6d0a80c9e47b596c97c929671e314edf06c947d5eec32ce4d0add47283730be7c28a7006fe9d1a67ae74f3537d663187492ad2c7d97a7c2c797e9deaf8a57105688c40dd0207822e1ac0dd0cb339931fa6f7414fdcd510811261e5e6b19d14f5f910008b55643353d36247db8ac9ffa4902bbb2b2fa0644dd7f922a1538284677092803aedd8a4d4a37376e3284c1294377714b1dc369757871c64b52dfc6fa89450d085119f8e1afa30c0213aed32846b99a14ad4a39bbd0400ecdd072470a3f08c70ef4ed8f918cf0017fbdd036e2b5a04a93a527d2ed1f52fcbf6851c62fd9fd8b0fdf349b4c1df00e38634f778385cdeed2b41befc6efaec2fbd75b461dab05770ee7674707d54b52d4d390bf198a6f8ea8d4c69319e3657721713f57607de2bb04e6dd18b418ca85d0bc20fc7961baa67475ae46d1e0e7465ebd79e2a9d3f11200ec826bcd729031afe33a1b80be15522de8b5b0731197f3c4322e796082386b06ff8b6fd51fcbd1377e2a6ec16cdd5d855b701d0b181849dd261029273c14d306810fa86cc91d94e349cdf4b89f9c17bd53319cc39b6d95f1e0fc1d092c210f095b9fbb8475a85cea3d9bcde488fb92a93a2339b8cda0164f7ef305765034d60d3615b0c65cbf4c3230ab2a79e3eeffd756c373eb374ccb4ca14f968a8a05320306d20b08d467b6c0fa527f2322d7de6f48aa04717cb91793640bb574283e700ae24b11491a8e30a251c72f109dcd537b0a3da98cbb612a009cab6c87a5a33506a83bff8f60def49fb3b66ed9ad489de2795e67ef8cf82dfb12c28156c656d00ff5106b880383557e114b87835de63a551daa9d64cd012d4d850979ae3d58c404a4f0b9b7bef8c1025b34f99b99415a8574b6fc519c5350af3ccf4c5e29d6e7003f2e50ca021281d762838a0959eeb1bbd45a66c5e4d5ea227f3fcdafd3a4fb0ec15f1d4c67df1f224c95c0a00a8dd6301b506a6552a44c02e6b5b2bd35d7f0003e8044b4c80e9d7135e2d02c2a34b49bdad2233e114984188dc66d693d1d9c0f2979877264a6b46e36185a6f23e5532403d4942a4d219a108a46d9d90b3bc200ca7e7d5425d03112f10113539af2eb0e47ab2ca67a39f7be25af07ee9f75b503c5cd6696769e6f89c942bbd5a9dc9e665a13fe3ae31da7f62ee597760a42d1081259e5a18fb0ae15dd4de829984842049dcdb819aa4956b368bcb8403677c504a58753d476db9c6dffdebaf57364c2c8ded19b99f4c7cb7699802761cd226703651b1c45d5382562708b27c34a09daea4417654875dddb2fd56ce6b007a47100c66681c4bfbb9a4835f04b7e7fcaf9889d972699ba6deeafe35acd166ac023010c35033aef0db1b1dc3b6426b3d4e2db8ec1ac05172dc037cc254575f8449705facd5de1605855f7830210af20046bbddfdb8da14139004925149b18fe180902ffeb748aa6511692d23d4747b4ea75c605d29a1da1eaae1f885974a7fb43310337948bcb4f3d09fabc783c1cd246a70503d61026317c3575e98dfd2f9739fb0a4ad3cd219b10627173166c49053b57d9ba9ea400fd88b2d34d28f4d9a49d5300a7254c4a8ff2a2495950e3734b1ad0f21df6bd45e17bbdabc54f430ef9924e057cc1ca8e13affad05f4c69c3b38f6aaff2f40f6f661547b6e76139a4f5ff7201fdbcafc3236ec431e09049685b17c8e822f34d30c1f6c8e0890f53712d948c0baf40dd1a0ebb1295443283d712ec13f474aefa36950f525ddaf44f34c9a9bb0c750412459f68de26f6d4a997d9e7b1e01e5fbb298931844d6f3bd98f62b2920fd730f8e9f8979277a24d57ce731ec08c4f69efb9ae282601870544e180dc9901a19390cf0baa86af1789d883ac02077505fc28840d5dbe4ce6c8cb1feb874e065b204388b2d5c8742f606d52f3b3fbb7721ec123b5f48646e90bf5bbc9759e0526a2324d249313d915171197a9481c1d54c3c715b14d9881db1793f0395b030d89a160d4dddba6b400f0a020ffee859ee6c606b80921f09f676b7a099500b4008ff3bfaeb261f1fbb3190a261f922c3f1b53c66762bddf1cb4b71bd26113a50908b26d822170188b1e13dc5d75ef8997a76d9d269d35bd6b33bcb70af3a70e0fba3cecfb0d0d90dda1e2de5b13eaa5c69f2b82192ec3e6b58351a606c564a40ee6e0e660f03db1df99ac66229782518b3518dabd6f5619e5455666625f8875028e6db124fd31ff7242bb3d8451e1c4d0340ac19ae5e462be89572e25b25925063b26d934d54910c412d18f0b8b7972fc1b8ed769f388258959037bbd5903b304db9ed81e70efae8701e2b2bb8e37be732dbaed903664f762989642e3f44b610bb43a3ae136c3091ef574fbdddc988f66f37f6389454bee2d7b15668d207b31068a0472a3a091c72eef865197de98bcaff1846908007defa5024296aaa565d806e9a891f4d867d07c457e6efb404a52c96ce72570de1f60ad9147d691e8cd1605c011516bf63202aff93e9c044c7dd5de30cae0d1289990c46a20fdac4d7f930ac931195a4102ad141c9584743117ebbea379518e8c95a4c895a15c9f5936a20660217e5315cdaa2c182d2087856a3894393c959a8d454dedc1bb3819c2d984092e635c92c35ddb7921fb2dfd16793f6f357c72b05a17629ded211a03f7dea301735b22465abde1e4ef76e4fc697e505f5f0f6dfe3249ba189dc90857fb7ac405a1d4f00815622a2e7cf2bf9c3afb1aeb0f32d208646ebc4985cbbf70d35e9a01e6637999ab39e948404ed7e3a56f55c81189c73a8c416c6dae8e5d295937cc0819d16b97a6a0ef7d27d4547693a13a73294a081086abcb34cbbb9021a1abe6079624cc90b7de93852023941c6f1834bd5ee1898c3d2f2115bd86ff6cb6c74007435e6f1f217becda795b5935e3332de6474b6214200ee1a00e649259f2f2ba067b7244cff6ac395ac242bd9fe648879e62f0cf7b87326478dc9d2d750648120a1c3498f23a55a0ce1f78cc9ff107084b61c5181bf35dc70dd733e57c34895b00d46928c8f59627cef947649fd3d5a13891298df3e63a49e68cd7e100ab27dd09690a33101a282426eade0fcf8f763d3c4ed42fd2904d162e548f55d438161f061ff0e829c70495a60927743fc21b2e8e468b5101e909494f23dbbbf2e7cce90d140198d721da401eb5f0defafdef4df22c58afef3d2d326f7b3b430fde21e30784e3b6e43d12e47d3644ac9a067ae6d58b5019b1ef2778b92a040ef139784d0fe9b3400ed8749177f0cc786710e01e1aef7eb52760682bac8381696197ca4f05c8e93696f26f518842c9ed577799ef13df5c414e71737667cb110d2d79c09204bc0ae19aafd91e3dada68edcf786ba56a2a5943209404fff24125402d524fd021077c49413f69a2e1e5e0c999a800b033a789165babde947f17cced6d01b8300e21e79d2b6bc302870dd61df0fd62c8996ca5cf0c85d5002e7f2c1853af67d0b8ae190ffdb815eb9079edec1de9c9c548dbde45a4cbfde8ff6d818e2ef5c8b009fe26dc2f128302daeb1c58c1838a07c58e1bac8b02747cb03485b4be029e104457039e8670466174afe4453b6035777b073b4255bc55bfcd08ff60946ae7501545473443cc3e1df7e61ef8d6f6622ed9f8532ef6cbc831cc9f4140f94906705e006c57491ff6ed0da7e8d1d9a7c1d5050283ef4cc1ec9f11e375caecc4eab06a3319f8aed1111db7b775068ef087632f05adbdb11568e01d4a14453596881027db6f479d25c02c3c1ef1253bda91a95419bbe0fe1efd84b7558fb54831ce706107453fc44184e00c4904a66adbbc0c48a2da320e013bc768fa1d4fc3e256802b9b84837a318a53648312115824f697891b09b4bbada5e6bfc04fbe11a2e3e06d97a8cf3944966bf8e85a7b9dc474277e18bbb988d09279eb5d05aba1554cf0d395ad3947e6e958e6ccdc8570ce5b28f2945b73d9f537d2d12ff177667ea1c0aeb155b7d2b62ae8507623ea66c9d8dc55696cc18ebea523248430df177028407fa81d373146d0208e7505bb51297647c659b684fcfe317ce64271cdedd8ec00dd69ff292138021afd8cb35bed152ce79d23e04f91398ddacded9346e65b9fc0f0e161b60f213622ce96632f8716319fbb8e641ed40919cfcb1448db04d85f309711e412df8c85da4505fd24bc07e84a176d561b106bbfe2ed523e1f5e4348801e14c822fe678f012d8a59f106ffaa72851dabadafc5555ad0aea3a08395ba4068795430d98728bca20bbdcaea9e767b8052bd835121618b7498172c7c8272e0c21797cfcb89b07c4332d71e81cc6cac8251aeaff6fd7e2457a2a20542ab291024a58e133f60d3c765412d29e7e93535d24fc315af6b6a02a10e41506f1e6f9032c47f7bb43a0ab1a6603d0933d1d494dfac3fc96e01f53fddefb220d8c62e00a39c83027e28431a1ba01fc773745dd6be2c3fbd6ddd4b36d59a269e6f31fb501a0ef36dc6c8fe1780d9990b7433ef4de8c9527c2412a349eccac7866c6f1c004bb342d14e0d6999721edb1013a50f9a6a02550c54f8bfb325bb7fce00d1e8e064bea4f456bf84f0505dfe6621da9f8deaea78eefc72ce69cf9bb7b7ee7035d0c19e6ca5340a793b6595057eda22607ce1d4ad1a95d598f3832d5a86af7768401c5bba1e466f496704fffcd38b97a952baccd3f06babc69c27392e4f84ca43b01e06f84ea6bd6288d5ae8c1fac8b63370e21cfa7eaa4e0eed92cf29bbe424a408980ab9a83a9f2be8d108202eebb7efe5e61dbd22016294adf3896e14f42387064ed547c9f05194d74e82a07613c970f7b3f16a662dd5cd89aaa255ba631842045c829d5cad85c48f78baa111e6f14fa88d2a709bcee77819d35a7feaa4f60d0a50fff3edd8145c4b9ae2d94f57c5a674e9a12739e90efb00314b0532b32b410277263b16a9a3719ae1935251f27a367f1a52c8d6594dcfdbb69ff9d8f1b0e307a17c3935d870a219fc854041380b847ac24a53e162ce786f8fd700556747d7016e23acf5dc438ca0eca914eb825cf9f385a871b827ca42691ecdaaceca3f9d037e1f9a35276885fa4e3ea22302efa173211ace3707d268360f39dfa493a5a20f71cb66995e6adf9d6fb6cf815e85d6f50c0cbfdaa0e04cea49deb8bd1d701e043842d08accaa4b506e5cd66590582b40d7ba53be5086beb5c37aaa0470cd3c08ec2d551980e80b094767524b87b539981c723dc510f761aec2b8d7ad5b28070019007d1638a3f2e341b3711ccfc944e1601e04c6ba5a89e3f23891caa645fd03a3b27960692b511eed9fda3771dce3665a1acfb58cb6611a9c6832f1c98af208fe8667d71e718f9823126e3794926fe77ac3af6f720a563f26e393d5ff33010dd1aafb3045b3584c630274e74f705d072e6b8e3e8da9f4ce05fdc175bedf2f056339bc4142770dc3c04014fe8561d6b847c1ac3eff7357ac9c9b31cc874a0c0b0c0fbc4ab5c84a8bf6c01f9ade400e014857cd1f2c5bba2f84a84300652485016a6170075b526e5f09d98314c790417ef5c76e88c852ad32d57a2d3b625aa802feaf4bddbcb5118deee2829d2c4131449306479de3eddf0892dc495c5943f8049b5949f6736aaa0ede5a0b602d150c55ff213728b97a21ee5d06bc0b9fefe7047a19b41be006482d3876b20b4987a4cc9190c447c8bdbe735aff6395b6a7d6008ec131c96f9ac77eee2e6459e6419ec69bb6e1eb422fbaef2144607fad4ab90d3581da588bc8ee8858f396bb3d4b4029e58c5dc42480ded003c65b60a587c2073680af2429cf25ddab5bf999f06bc3c146d8707a37150fc0e1825c05f981eb061609c778f90a2413fb3dee5670f7fd562a6d7f8452219a144cfba58091218509b8ce1781f0f66f0f46b3eaa62592e7710f65ab93853519e4283c43ecb789a30c1d92a903206a7fb3062aa0986b5279df61cb76676751da02375512fb6034ea004d392efcc7f116e1f19f3f4751af24a3bc7bd4f3ec37ccdb3f372c0cdbb1280d57e61a6568210b88149d08aafd0b3284a384b48e69774785b51edf281df7490768f0bb50465ec4b1dad497ea0501aa510e9f8284de62659dc28fe41a88a6240f8afcd0deb8f1caf5b7df334ae331325fcd12e37adf6030f8393515cc77ba0c08ec4e736dead991cf2daa3012a56b7da0379283a0a07c1331560a7ddcb787330e3bba86f326c0147962c50d139f506593885d63829be09d7ccb54b4395fca2e015e417de56ee53cdc841e7b7fd2c0d6fe947977c6d27301562a0010ec3bcc4f0199217460bcbac9fdfb8a91e9c0719ea27963e7967cdd9ea45e8657e87394500d21653d6c63e03bd4fb736326e8cce1f04bf191d3d5d52963bdc997cb5fef9d030b75ea92fd647567c38ba7ceaebf3be44d417e10b56f514b63dc401d5c2f9b03e682e30a8c1c0b5107912ab3e1170abeb06e96306ff2adb1845cbbcaf32e4f03c386f8b34e041ff3a46eaec03719f0ace9ae50062b89568047e4b6fc1b30a9058dc757cfbb6505ae57e05d58fe0ec95da36175f0dd24f0568ad34f593a8c1d0909ab3e7263e5ba2d5356978fc0151937e93c6a52a2f2ae25c9acfba2713edf09888a15267a19c59e4e01cc43e42c92926bb2ab18bd7d5727597c42e8652506013009b6cd749f880f3faa1ff9fbaaa2aaeeb22e9d708fc291342310ae8051d2000f6cb740a43b7d110691837f4bf06d691631c91831476df36f23b0982979840582794311d31e492b9f0ae2e0bff6620e59342669e6d91171cd0a7b2a41b315041ca0aa007977f384f30d579a22958620c8459929d47766e3c5d0baa085f3070a6d0d3c8858d51b4847cf7e246064320d88a0c2655c1387a1cf43da498add8f0cc509a03f3f41a14c4dc40b54c8c3b839c1a754960f676b0c08d56a8e1618e90641e4eadd9d8c400763d9ebbcaee7d7bfdaa555d5a62bccdaf7933e0ff9b70106 +generate_ring_signature ff2ddddacec67db5eaed00d45731b51a30f3ed5a29fe2e980bfc67d70622057e f546e9948f51e5f89e2b8df2402842df7331cdc9f86e45cc119d9b3f800b3210 8 4e4b13ea2c389f967a2194e8bfc2baec6169fdb1352ef2ef93537ddbc2759c79 1c6fa3f72de811e367a4183478c789106ad5e7d12a0941663d7647888babd582 b1fbc3ba8b540ed38c05e35f0506b5c26075b6e26c1ab22e42c3436563f9ff50 220b03944988bd89e093e33960a3aedd2bdd8ff5dfca491ef8b619a7686c551d cde7a98bf7838805317fd7a847578d27b73101479aa65782ea2a829519a86f39 bdbc91722a452b6829e1ad00a1d0b4f5be477ea39aabfba1d0e2b2d1d8f4888d a5ef7e3d28da78fb0b0fd26b06319cd9a562a296460d0275c22a92f1867961dd 96aa47a5d7a219e48ad522f229cd6e3406d68d8811f48674863a39b489c5d091 2153137cdf6572228eca8b17bc5eec3a8ffc35bdd2e9cccdd919e0bf34c22409 5 87f5f547d6fbc88a95679a7285429a968846ce33db1981e0ac634de0214d830525c1b808a8e76b7fab7eaeb7120ec2e0d9a1d0699a0f4295635b30681a917e0999c9ebf9304dd54cb8cf570956cb2d619861ea8a27a482b98b1e4aec2a41ec0c7da4ee1ca916f27ebae1b66f65bd96c887eaa8b98a2df1080d1fab0cb2fa92000366b5d943b75d74505d0ece59c9c3e57556a939703257d50c91c31e569c7309554cb44545bc8c5bc01ce7131063ab9c3d30d68dbb3d56f3829e945b41607608ed2ebe13cf3124186498e69efad85290d0ecd460d15b0f56420309c7517492062b4218b0739533ab8176ceb7a8ee672093bfccd4ee6987a1f22b1c6010b750059531bf0877f730583360ee914fd78ea2d9311b641bebc1e555180250e73b4b03b4d4195cc1bc00b69f6de83931e550beb7ef7536756dc7b8f33752804b56b6021bc77f39d14be1ea2c5fa785b40abe74ffc24c5f9d7826e107986dacaf40aa08647399ba0e50fd9b2714840b67c8cf0750d8dce047ff897efef569f304d62304a7c0b7b4f70c371b9eaf96948110cb5131bfe9f1caefac26a078dc1b4e2d3e02a4e8c02d040d6fbb59c1147954e67a4fe2275e9bcf5354d8da8cac82520d1505162fa0fdc19612e99939d2ab5f1f9ba70669be6c8f60fb711276a963abdbe901787308c3397c6750c0ee337353c6cf24b35447fae398bdf029f8f82163551f03 +generate_ring_signature 30c3628900bd6926585d4aa56eed9f513162bf1fd69b64d30ca188dd2055b52b 3b4789e798f0f15595ae1acba6e078513fd14efb72657d24e87d1026c0579998 1 eb93e7ccfab0a53602d1e70abdf9ecec65c00747fe193ee80e1e4cc52e35f42b 46b24672be3987e9cd61b28f94e9d0d7106002c12dd827402033a52b72028707 0 1bd7f85d02b02cded9de00f7a9ee3b5f3b4580f8670300dcd8fa3418c0b3e901fb7d3cbee3920306ae6abcdea4fd34d1ecac6e280b648881d5de2a20a5629f05 +generate_ring_signature 58417c842707348632c3c2e86b71987848c878ef35b5d666f1fad7edd17ad655 e8f4f68cf96091b431b94d84f3f50b9cadb209c6b8cd867c5697bdabd5ceef37 17 f62fd3a49478f4ad0b7cf487cf11ae58f39f50d49c748f261a71b8cf9952be95 9874de7ededaceb9e38ea0c9ab5c05e07d295ece1214c03f93decfacb9b84cf5 547706641c806658675aaf73b3b8d767b8314b71bd60e3b0c8dae4176ed8eb17 1a4b3196b366f130f8fb29f94c2ea978934c8b2ce50a461304a3ca38527af921 d242cedbe3ef28be3833c26e2dde2bb1dd48ab0d8aa8d9af2f98c92985a9a955 55a17d55affa0f314bd6dd52c6899277aff18e60402a0b261bb50f3a735efdf6 b453cbaefdfbb6b1cc84aabaad39791a5c9800459364039ddc9e3737166b0647 5e2f0b8c7d9720ffbe86ce041f727941baaa1b9f44be33e6597301711accf294 9f4f5e0982b2bf03a46e9b484f09795a4ab1375cf7be675e115aa7f02e479f08 4d2c5a9c516918d9e73b5a8bdd98ae7627d68c75455cb12fcadced3b395f4b46 ee243ce42821afff284ddd182b48946d8d244dd26ca97111cbaeb356e3492fe3 a955fe3814ca0854fc0e60e4bff2bf7702d9d170dc9c730bc44d477e9a2e2ade 9f060088e0bdd095ec833d41484ba245ddbfab3b8d1bd2131d1e76bd660aad57 fdc57c18d9265f5b30a041ae675c3802178178e44b3700d6c2f065b000de5b35 3703eec89bd4e9fbedd44f8567ea04baa0f3d445a95ed493eaff640e6c18d05e b82ee7e12a3183b139368774d3f3a7f987f2ed2cc81c438c867db9fef1f2a633 9993e9a4e45f23715c262fab91ce76c0b1c62812415bcda5e77ca0622676824e ade4532bd6011a411ca494fec71f87aa8c42d42b4b2fe798390dc5728a3b7005 7 dd130ee520f4f53a7ac261852c445684353d3f5998be2cc4351bf1ad090a9309fe82180028c0d21cb6a9a886451aca894c2b06fdf4f76e5794bbb2d073543c0c76f81eaf0a62ecba541115f23defbcdb22bac2dd602254ce857fb117029d3d00d752418acd386f9f71e9e3fe92cf4e00a26a17a5366c93100a05ef71387e2d01f819d05ea14d266c8311c5b0fbb4f2b2fe8196c2136ef5914d1d5c3b0ba53a08a5f9744a4f2d059349443a5444231d098e754df9fbb90cab0fd18d0dbfee050f1b7058d6ce6c184cad2bf2dfc61d39496245e7db943e31998d45e3865921190b07308d332a529f16aad07a5ab8328bb373db83812c16054fae0ead79c503240bae7a8c3827f4c2790e30ff2d5199e1fef109b6572499a7d6255f7a71d492c20044f6cc03b75352749096fd934b5207a6cab2c28184ddfa0a0cd9c08667f6fa057248f8e5d346e8e4ffed1aebf6d7e081be41553aab60e9255118ca7f76c45201c276e955252552b357b3c2852a2118df5524c4a28b417e810daed99c0a639f02e5adceac3e4a3e1e4eb5af7ccc8a551786b3f530163f0bd395d2a2f56419f005cddcdbceac18a1f497a0ec9222d894d0771e39d97fba3b627747c8750e1f85079d970a4c21c6a968270ad95ba639eb35278fb4e2ebfe35e1c3c8bbd4b2c23b0794ab05cb45e86bb9434cac0835a0d6dea8d4bb77b4db76b1ec9779253e123f0a98c1e0c73df16b6655a9023b41be0b293accddbb67d30e73f5c28df9e481fc0dc9d4216e06ad06490ce0a6797d5b246a0d8851e1c8aecdfa439b1dd036d0e50bb3cc668fdfad104024615db4a4af97ca443d5a50aa9c415b4eb99a04579dc304428c4315f7bcdded55971e872aefdec6b5ddd2539a48ea1793fb2ddb4a2cda0af9681dd8f44a904f754d42a6cde72a15790e997f2909a7401a3df9c97f33230ed94dfa7645f1824541442a67ba0d4b28e5de4feb55142dcb96a7136be15580077cf964154e6ef20549775d822bd8d502464cc7f6381e7df63fab9669d9faa30cfa226b0850395fa57d04de02b171f758b4942fa9f69278c67b3ad6a53d8ff009034f14f76e1a01db7c71a9056f5621806bf0d295a9c8147ec7152ce931c6e601e9a7a05cc91a3f09a59d364d49db691f563ec46819666b4dc98af7d52e30e3097e810f9ad3c7750c7ed87234d90c59fac670fbad64d4fb8ca9c267c001c06908043868c23405e762b078955ddf6206fc0b863b64d657bd8c006aca305a57c405635207ae047fd2817578793ec87ddafd7980310f7b140f1d2b016ec4eb8ba20e485d7a757022bcc90b8c739d947d1e24f98e746f8e5477bb57e440f3d46ee3039ced366b9484ff0b22b07c04b8a4f5467bf2009eadbb6532fd0d19a0890aa90977b9c8706f8f78867bc75883a35a27cda70705f5e4d9cf87854a69e2e3867907ea158f19d7fb6603c0a7451b4abc6e89ff06695a43d80b2170cfa7d23bb81d01d24e1f6682f57c844781fa3c93ca93a92880e0316a1bdbfd8885de25f20a8200 +generate_ring_signature 542377439ef6fb702e67744527b46738c8e66465c37c27818c112d43e18310a7 5ca2d81ec83a80a38c7954707b030c02a523feb8d000293080b188c3cc5e3800 1 7a05153fa83ba046fe1c8bf532b668e81f8b1350fdf462d7768cea3a0bedc92e 9c14ab5f43948a2c657fb50d3ff09f90161c5ba944d033ef363393108d42be0e 0 19d4ae3bdfdbb4c51390562f955b13151508504908643a5dd6bfbc3de162500648c61c0e91580512745e207152a95045960d5ea0295f6234289cf2712cc8e10f +generate_ring_signature 1a646025f26ffcfb1db6e76f8cac79c807484c7ca698bcb47323ada353ca63a1 9af3f8d35f2638d69052671d1fe6b602462b5e56f546b60d572dae14750c45a4 39 22727dfc14db603c1f6fd1da370a83a01c429966f30265cbf1eeac8c492b04fa a65ad12f051297e97d83edea615fd9314a20d2d009edfb3db6cb1c22034913e3 8bf63ffa4ed2635359be30e8af74c512ac71480943149e9f8824208b415a43f0 82cc33b2582ed6e09f5d535d7489ee49484796abdba3a5fefad7f1c43cf05a61 69b4c4bd80250a03a4dbc209370ec7ab539218b2a130a117b13ea3e09187426c fa1f5d95ca883961947b069b0372ebba41294f50481fbc003396bc61870f1d3e c4c374490fefd34f46090555a8d10fc2b0b618d0db5c2a674036326f103c4fba 02cc2448a791cd06b4a890ad9e33f918e5a4f64f5440a59e05028f8e30705457 f8d8be6bc52216bd0dde5079d2dfd07b8d41d8b8c014385592a40de0601c0379 147d0f03aaf2453b5f94058a2eb1cb7017888cd68209e6a1ee241388053c1c95 bee33cb10166970a8edf1f72b3a9f506b303185f6e0543323e64e7b230e594a1 f8dff7d2db4e9cf5ec75a48c063cec5f3663733a1989943c7df2371b4ed9ca9b cbb4a15d4c8f6dfeee70b815201a78ebfc0eea100ad473a1812ce618e391fd8f fd98115b7c318fa2ad5da4c9b87abaff2b870e833e3399050b4cb3a2454d9262 e6cf985ffe91813455b71d990c8a6e2186d4e8e83c1bab62da0f33bea54c0527 3b7b03c7e1c6b3a8e2e7cb951fe7e13134ed15445fc8497ee0671d84d59821b5 126c171159558e39ec3ff5bc2d2bf5b7cb8ad7b6db41870e660940973ab1ec42 b4f6a5da41594b8119d0ca41511219a59be6316d77e384900ca69ae00557d368 cb478701c6734e1344bcd62a6afa02329a523ebb587590bb95ac9b05b0007186 b351f999e645984f7dcd0e2518799f01337c6a4588b788efa16b4292d2b57a7c bc81c2a82ee8d486c54d4c6bffb57ea2ea0b7c90e95e541aa3aa34d3566685bf f4dc872bd3678ed8ae3e78b5815c2a2cf9d563da1375ca4a3ebeac198467af38 fd6fd83cb23729130376e19bce3446b5923eed958c67c219db935e0028db9c76 aab17036096cf9ca3b3999170cfcead9b01aec1e03e0df917fe526c911a06d3a c7545fb22485ab2834eba49085d0eae6ab0e2b4c9289ddd4b4405dd818dc5218 19e5d31fa73ad3ac1bb7b930d733840b93b8336a615752fa49304f36f91ba8fb a47f065da31b00c78822c5d7ffad6408bc2d65d3b2b32ee60e31b83f4b0c50a5 34b049b4a5e9e4d85d22325ce2436f28220c32f0f4b8ecea6bd81186f8703095 d71f5ec82f1f53326e65205a4a0e01bf47d3d0faa57878921280f89fc8f62661 73dcb69770724f616e8d0dc82757d81a40722a2337c118791ea04daa54ca627c 743a13158f2fd44b47f55eae3c92ea55ebc19e885f59367c744f74019454b85d 715c6c74c701ac012245f0d2c290dd21695f6173eaffc4a7d906ecd472ddc42a 2c6f9faa236fc13d6eefc9cb0e2016de702c02daa83b6a342d7c9030c7bc5f1d a12dbd5d73efc718b35e993b81f66bc8fca1fac28bb236f60acba9070f596e39 b8550f51e8f1bce714c5b5f59c9aa2609be21c962f56160014b3446a2eae43ed 4ab0544d9e333ee4c01a68b072a68709a5eaad2dacd0fc1a12afa742e705cd0d 9eb19d338a9ab32eaf345cc99c3033ca616b57d669f9869b279d1c7eceba552a 08e86802e52891f937be3237539cd0238d87c45a68a6de9e5cf0d21c7629bc98 29a677891317ef1fbf021efde4a32a7d853d909949ca86efe934596d599e2d77 0e67d76c2cfa61046adc817257eeb782252b0ed685872155a8ed053f3a35c706 12 e1e5796063fbd2aee7227f6c09ad1af1ba9b2caf9fbfe8b312db45c34783760b75737de3e3f4a80ac366dd9d4d1fbaf83fa0791bb2c3f7ceefebc6bf2190bf0801f56dd0ff021d359d097470935259598347ab9312c65e56c10887ef517a9609ce9887a78476895c9ee185f3920b86cb9c939fe1f818cfa5ae9540d1dde0c80092ea9498351160a59c40e4f2911b784f38c27a83043154e7e9922a7cc24916036b67d8fa3d5e8272155af6d8400041b62fea9a346a509c5fffb8ffdbeaf5310e3a10bcd91647e8182d46a17ec4c9e63cdc2731411dd0362195f721c21e256a0b2a58f09c19923c5d70d3793a51f4b1f85a64c6153e968a1876280872d6006d07edbba6532a39dc1de6961dfa50a7e3a913a7774b760fe3c243ce78b57543990e212087a411c8cac6a31c4d8f82d2e1ad2ced0528af3bee7ab8d1bfe6d94d1805968427b42ed583a20732f5ffaabf115581c04bbb597b9d25f13d8cba91227f0677969afd3cf2e6cc3e3691682342ac721a2a53decb31cd854ee447042fc2f600b467a3efd7048b1ae96431e89f078dc5719a534d15e204962b3fa1de8a2aea077500ad9e253c2f565809f118c4caaf9b2766daf04d04e08e80fd7b305d53e403ed49676b6cbe2596bea63b954cf22e65337e56371ca11a92efb5ea5f3317cc075ba9a10dba2d8d90e7fc143bbdb2df27e3c992aa39ea4fdf328b7f8fa630550e805c17c8108429e05eb9fe2fcd6c5306c4dd212c22e7cf55a12bc56b555d910d53bcdaab386cd4254da44e9da27c74b13474fe7009b09681e67990d28170f60229dd5dfc185f3a6100ff3cd17efa91d806a5c6db780ddf655b9b4b66b4058005372cdd4214dc1b7a36d1a08de84839f1388ef999c489496704554e00b2e2c50195aa0c70801620a2c83073db517f6b0132652d38dfa8524cc5cf3983964b0808f7cfab21425c757e948c599bb1d550fa6141630326628134307e09832dcb1d08329b278975072ab7c9e48c4a19566c41377c1b01bf5c66073366a8e153b57c0956e981739430a5c398146433235fab473e7ce8b00b4f878a4df93068cd425803987372e727307ede1e8ce88cf2d4a4e6b764c9e60d4836614f404ec08d80700011723d8a6085a9ef439b90b0b2a3eb7e83634157abf5716c200a552801623b0ea61387f22bb18a992aa3bce09b293d638dd7a45442046746a5cd8cb42c9bc70f7163169ce52c6b0406c7f393deb18e912598ad2f07b6b444a5b87ad4e23d4c07bce66fe560a076f952f7f46cbf7cb560c80b5335568de69dfef4801d88a0620be2dd922d8ff24245d491429f3d113e043d744d55075459b85db7ebf56debe30d003adc441fde86aaf9d860f6c0f0c6a8ddd4443b552589162ff12bbee12bfa09d7b2f2037657b79e667755982e81b5c8ad18949b00a599192038aa1486ef400bc9c55abaf762769a9464a26fdc5f53b739ed1f138976f5d93409b71ceeff0b0c598a684a9986fcca93fc78961d44d94de4e3d0b8811f9a4ef99f0538a3f10d0fab64e32e058a65072f6daf499f6a32276c9782cbf277a183faedccdacb09a305d3fb2ba370e279c6576286591d8f7aa7f309c524e3967bb68e57e1958cba890f556af432d10e7b6b53d9fe0d88fd8570e0430f5fd69b051788333cbded024e02e7e1d1c62133be48eb17e7d026129b6854071d34d3e1f7b3ab4f8926dd84d30ed5d46b9c0a580cdf06ffe59108b5195f5d318ceeb46f3f4bdb6ad2adabf3ca0afc56b9a5101206c04ebe44d9a0dd67e1af5ae45fde5290124fb95525f08c7806fa02000f32e34f31db4e255fbb519f52a821e7ea2a145dae4dc96de7204c600e01a08d112ab3167c6867c15a3c99f26fb3362e50050ebe1859319cf9bf3888083939b76ed64cdea9755a2a4a4f58a032c49444a13b5af21d33d3d170c412b10a26732d05ed6217c8843fb803c578290ac3c8b65da2e10e71a435dc0ef8325801e18dcb97f7b162de4cae1068822f4339f668c8f2300eefa4a116642dedc2ce06ddf411e4ccc7cbb95d7d06f73ab99b999161e360e4c8c1654534036979a60604739ab337e8b6b6ceb98b046add1eefaaf2e6b8f8e874fb197a9b84f3ac0106008619b6a2e797f1d8a3758b28cc23f2d3d3870a73cf163f042a978c45bbab9107c5ec30099598194e08ad507fa4fd130f28637f81601f82d763a2c6a8ef4901018cec14c87b29f4f670025c16723499b25d0bfcb97b319631784085d7f6b8fb0b40448f5738b9bf5e5aa70b7f7246c8cb76bb0c2c49214018ea9bed4cdd07fc078b58c6d0b6dc6a375587974b7d4a45c0e609f428c32b3ab95efac50b0ea9590b8dc8f47708c6af3ccb2e92b5d013c562efd630991fecb676f8aef568fb1281023cdc5a34a2395bb9fab7e0b92d51be92b079cb345de7ad39bb9b13e1d3a28d0b354c8e438f75e80df994033ca467c7c596cbf0ccac339a292c564fa14f09af0130d5d21dbbc807ba9242acf8eab9bed296c7e0b4bc1e1a4935f37bbec962730c3017ef97e920ea5f34cf873197198230cf83bed8a4f744c61f6ea8241373b0038e7df71ac43e8085f176c7f5199b49ee1c552b5413723dfb870bc6a026f3300205fc4408e20558ebfd1bfb8c286f9bb5cfdea8121d9d0ea83c997adb1297070fe6ec95ed87c4417e24b541b4002bb18e8b363527bb97cf27113968265a398b0fa88969f551fd9a95156fb175e6791c49366cadff7860480a2ef44995f46f4608abfbce9d5426ceefbda7365c307f646bb5baa5e1ee85a4e2251d3eda5161c205187b51d4f8a15254d85e6afb203200bc4a2170d437bf1b71e216d77dc877fb030a6be8289e5d2439584436d82717af348e6a4a405b60c79c5d204908648fa60c0af5efe2eeefc30995d7ca5ffcfb7f0aefc09b97ef6543090e8206f0689d240ce5a43f24bab1016919f7724095fd722a8417c98f862455471ee3bfd18be988087013d0079f0dee4bb41697712cfafb47615a324a7523449654daf6b43c800f03989ee5b32168f79709c43bad1b6c52151dc4ec309cce174d2bff5b26d3762404fe8f7b90d53107ac294cf7b4a74c193894c5f844423736aa4ab36b8cb5da0f042bd363f8ed374845854e111761c95fc8a875f2054b144183d35129fe35b9ad008c56d26a3592bf230c0a4f46aa8bb6db6fda50ab273add085a3061c22c537a0f4153673281790af8f7bff7912ca3399b00d9c2ab9a59fd11c13cdbd07637330db9d6d0583a19b24425330a2edf329b1961330b59d852005602f16768d2e8240262166ea37417d5d0f7624dcd4187d773203f5fc6aac18049b5397ad0ed458b0b0eae05aec54eb523b28565f0277986e6a7f36b1ec20b06ccb669db34f27c05010a16f88b460d8913d001a25de85f19d59f7ceeb6aff4e4978ec08b22682f1e03bfca92975096de8b513277783ce7f4d20471d582a7159067bec137903f008e067577aec86f5828ea5cdc06537c6266b76e842158b3f368b69cd5e91296a3410a +generate_ring_signature 2bde8b86a2ec88d4c9cec3293b97c31065f71ae63bf9317cd11f95c686f190ad 615dcf6ee337b3ddc906632ca293fa7d873344b406fa6d7b3aca6d17248aeda0 122 91ad2b34eca03e6ba67e7055173d9e5cba6fbcd876b2fa824432fa6e5653fc71 e8c28c674c1e7363e7789a5efa1b62f6775f6172bdc517aefaa3986cbf464d60 efb7183e54a0535acf3c80d99c09928b08ef132b27967d149ec1f42b1bd7849e b5ee8a7ac8f353cd49c1e501ad6007a2cba4e37535a8ec25baedeb0ddb8f0547 c26f93de8c2d00baebf11cc59726b3d6554d8342918220cdda0e949e85486439 0e24c43284cbab66b64b7b12769982b8f3105b94693f4eade67758b25d6b3c8d 8b5c83b36c1889661e1964d5895a7a8b8674b054f0823336386eeac522fe8c65 478f4ad76a9c596d414227a1e5842a9dbbde99202aaee479d7dd2a936842182d 9a605627cda0fab3aa51fe3df8d483869946d2a638885d7d74717e6091b0c106 83c735ec2257e9f9e9eefd82bfc9daf4fdaf712b19df601218d5109678c845fd d62e78ce883503eac9c85aa476c2c18f8a82a575adea396ce8fb265fda807db5 ccefc37948471c0f902d54da8d3b6ec27bfe3aa808a40bf6a2b39a0e1976c101 77d9dc5a4f51ca0d7f72cffb65a62300004836754ac93efa75cafcd0942bfd4d 3e2d071d5f477f2d60121df0cc3507a99ba3e5e052a560992e63e8dc4c1fc6e9 7a2fd5d2c64a2e1e4429fbe4e2e5db573b043b85a78052d7320e3a822084bf06 3111014cc6a4d90f36d34b03d94ddac2ffd4af90b233ef166be0dacc71deaa49 ef68fc4cf42f01c217fb7dd49647bf9a141188e535cbf4d2689feb3a07af25cc 5afc39ae780f395802ae4a3fc339943ab752e2ac59d7cd9769edf9f6ffcfa84a f0f467a7114cce2ddcbd88ecdd86e0731d5563757b8588a40227fa0b6cbfb063 5d8329f6decf2d7008a6e1c115ddf963e4d90738df91bd9b93ee0993b2147996 7caa44ce39504b33f7c31185a5f2f31e21709b584aa210dbe19692835a9732a8 f1f481e083fff888932f8a8719d7bd80b63ef44ff1336997cc692e6872b84475 56abdb117de32b679226f65d496f3d091899b6e44d2fceb0acb62e9607182d91 0c35f9a8fdd6b1db7d43a68c8d17c37b5dbe0c57ed0a0f80b8643898721452b6 d240e7d0b517df22c68b00347aa7310c8c4e8487a22d58e6e65a316db2e03e5d 70de31cc2e530e0828ab325913d3ce11ce66013d4be2bece50d0d17dbff69eba b845308a1eba58e99b06a05f723cdd7065cd413e984bbaa071d86b4058d51ffa 405c6b58356b59bc4c89bf51c829709f2c3cf79587ed79fedcc406d774bc596c ce13cffa5d5851f0441c155c9567fcac8297edbf64223dca7441c909a279b542 4473663693617cefc18097591e207a15769efe1064afc14e49cc4358a86cdceb bebac4ece81628c90ed537fcacb356241b6f12d3358b2a83cf82adf7f3908d06 c52618c0e6c862545a8707bf10e8fe18ed3489ded707ea9f919eeb508f6e7b81 60acd00188b73ebd7e389b655405af9f3c2a0a3d243495a4964483648099dfcd 5bba451aeaf6f3c8bd6fbf2c30f47643177b6d0edf15a5087f9703ee60218d7f a327a9a82e99f4db94769d13954e650b4a10828912dccbc71c40f5211bec9072 6f30a3990dafa3c76b810f9c9ff7adc4ccb7ee3334a2c26533cc261cc9f3607a 156649d9d244496048bca439c423f438b17e6c0db8f9469df3481b6995f6e25f 709b0282f46def58380ddb289d03f13876ece7a251e37895b37521efa5074cc2 5515db1527f99ebc2de544c39bafbd0a21ed83951c784cc1ddbcfbaa1e221242 12e212043344a0b5dc066326f7a9adbfb29db794793d64aa4cec8dd9cae00abd bdf97852a88c46a933ff004a4344a6906b5655181b28066dcb97d02e631aeb40 c042fa012d82a07e2b1a4038dd2d093541b973cc5494c2921d9d77e9f9060d17 8bd08abd039233a5c4de21d38bc7d795c916a7c9d2edd43c972fa4e7f17c73bf a7ec5e0d60d6537fdcb5d8e25754b12e14768567927c8706a3356724a109c578 3943b456e40c2abdb3e3e5cf5c76140c7a248cf5871e609203d30164c7bbd3ba e971fbe70f81d6268ae9472be6a040349307a40e7ec8e2f45687a1cf2209d513 83195b9ab4b1a3d5e687c9dc1cbf9b922a7a164035833abfabb81f7ee7cddbfe ba443b04f952d47e6613ab36f3ccb149450e1f8830367a2b49875b8e4a1d4df6 7ddd6ca087514f71448b0c4385a1a5e9336609d6769502fad99dd3f09dd9fd42 f582a83b9bf543591a9cea077dfc2ca00d38f93cef9c586437d83d614798d2de 7351da1d1ed0f2c022a598d23764052074f2feed5f673fdd595dec8471590308 51a27e00eb54dcb130b553dffc7c00bf05e0e6a104bbdad98dd76f5f05da2134 764a880a35fa35503a4b12e99335eda828ec288bc0a18b1a4cf1f46bf3eae14e 3adccca6294d6259f6431d61840bfbbe7c6636998808f912395942025e14853f 8e737db15a8b6be006605b5df8bb2b2376e44e52250b2baee36400c22a63ecd4 cff3696dfad97cc7b31a98904a58f18ce4d3224d01e03f1815d07025fa753a72 5ac0eab46e5667383eb6666debbf03f24220aa104b4f81a99b0f91e998811d3c 43d68db702f633ae0cccdbfbdda2932774db16e53b3a44d76b185e5db1123c34 9949e052a66882b2953fb31292f332bea29ad0ae1187ad1c64497a5a4cd35427 dd0e85820c53f9c63603835428affeca1257f34cc3e0779ddb34236e6261643f 237bfe842e3f9cc256a1aae34331c50053485184a7b598e46e2fd8537e2407eb 47b9a68e6c1e1ef6efea733161920ce47a308fd582328b2cd43bbd2b8ed2e150 189d5101e6134e3dce213e341e2f64f26d324c84bed5ff64a95706a193893d13 0882c2f68fe1bfb75f668a370ac0cb062f20fec5c871b8d6879db4f456e28b96 6b46f419fb640abb95bc38f709063bc40df4de28f6c74b69c89e58faf2da7cdb 2f83e82aa9ba725d401196f3d92eb0928978f956f24c2155d4838c56aec730c9 975a106e4496a43c286172266d3b5ddddd91bf640d8c6a105f6c94bbe70b8eb4 75df23054b362b1cefa776eecf85ddc17b5c366f87a9e6ba0e92ea1c36b8aba9 55494bc0daa6ae1dbc2ea51bfcfe5999e46fdfe5d4395ec02ec370174db67c40 a4c8b201b0e1cbb82be7289f36534afc55d841007d14cf341142e1e921ee9733 95c105cd2a4654438411decfe263c94ad255b7bb11019dde68d14a9152763dab 9afad7a49a8667751d04ff2100e4f4c16a7a08dd1fd9f8d6626e76d8007049b7 51ed76ff756b1cba92cc36e35c97bada08fa512c38bc08e07e1c48d41d77da53 f2b98ec4ff3a9c410a90445dc6d4ff5bf92fec23b3b6e0fb926332ab4abcf4c2 733f145e6606c83a4f8c185b69f401031e0b8f3a90abeecbb72ead1a5868c623 3fbb280eb6dc2071a4ea378fb67fcfd7b245d14e10a5927b0121b91fecd9c3ec 3c9b63b70971b4e5302dc86887ab5f2c36f5f801b160ca13610228a21fb4b750 079e8c397cac5701cc3bc6f3fde088aec23522f9b268687a8ed559f54f1f19ce 5010c4d4d360f876667213e787b2ae88143586cb3fd05a83e7d5b23e9eb4836f 9eea3a012b526a2bb4d18b5bef9ccb20dc74cb219316d873653c866dab4f7544 32297ac17f3f090bb4dfe0b7cad5c7461652df6a8c9e6d76afe9b0f06ed14d6f ab6bf409e53ef48ec3b91f9810891fcdb929fb53d706eff3734e09f579468dcc 75f961d5d836601264d90b1471f2bcf9fbfc15a2a70f519663db3922fca7f301 4751358209f05b96d64ddcb08a8a5a0d7a60c04ac905fd7f06cc3d3e416a153c 08e90bc52e208dd25bf89e75234c25ff200d0affd1220ce7bcd74f976f5ce804 1a5ad0bd6b59bb8c6bad38a0424a6fba0722982d848c3889a796d56f46643906 a877b3348a371447f706f302fba291634286529cbe5d2b252de2460432077521 40d738326335c5ea67e704a0249e1df8026748f087024208a5dc646aef145be1 649bfcc7fc2e5f8fc34b208232217bc1e939a9570d2d77d55d5d73ea0d207b41 3090d63ee8c534ac61f623141e883976aeb3a05b1a7df0d75c6f690900504ffd 1ecb54302a3681c0926e5dcccfe2f227320839a71df267eafa1fce5ea0561ca8 cc716638c4607f607d8c7fbfa51cd2d354063fae75a26cadbacfb71063a028ae 7f943ea518de364b579d870e0162d2656f69aaba935c9ef00e3ea8234b308f02 8f68ca2476a24450d935405a90ec6d8bd3986ead5958a4ac74890f706879d97d 89394b8f849963dd9f84dc658cb1b8f4c747fd8e27a559230aeecd2ef00e7e1e 11b7914ef6b6a05f19809a43f662253e21ab73508fd99ce5f143ac63546ac074 910760e08d8a7e7815cacf0a936a1f961db5406d60d19a7279e699a171be986b 3a35d022b15f6d03a3286cec1ed24e8a39784735afc06253e7562b456306835b 7021d29f647abc2cef105e4be6db9c1d623163917e156b2dfee567a68f864343 4ce31536d2382e660562df378be54611947aa1bbd7a38202c09c5eced106390e d1da7543a98ac0cc39c107c26f2db97d7eee150029a82e6cdb801331a2415412 3d3b041aadf8d49656bc92bbd0ffc2db9a85f802be7dc3d7815bbfb8bb56cf1c ee9565a94665ccdebbc8d68070987391cf05f76ceb8e10cac98dd0b8557da549 98109a96776dfdf3f21555d834f468d6fb5347a646e41164665b5c36fd814749 89a7fe0209bdb4a1d80828eb77bd0907aba70e6f6ed060189b7af3bbd206e8a6 2cad63eef21b170ed60a100d0fca30a8adcf2450fd1d92da6e6f582caeefa36e eed1fc677b3ed31fb38f96a6109a2f72b051e1d8d2cfc9e4c4485eed5e96721f 7a9cf28463f39559878f7aa1c042492beaee2de892934009bdeb975c31aed1f8 e6fcde4e80f5a848fb88e59f62b2d907b190e923caab19d7480429b2ee5b139e 7a7bc43c4397581d6bd61a31a680c33583aed68078633f43aa18f921be14ea89 b0f50f4eae39f571c7e9142c66d03475814db732f3b5bb3d4199f2339e234bbe 23aeeb37fc76370dbd1966dc9334e1bd283f2dd9a7334f04dd72edcfc8ab0989 ea9cd15dc4db9875dc426cf78522025eabc9bd779984ebcf38dc24610c1d49ba 3a6107bfe26cf5f9935070c7a641af04665f2b100cd1aee105dfc4f14ebb5c66 b4c3e2857ceab14e1ffec2aae10136eed9f1233ced514003e9622a02c792ce6e d4f2768b2799a0f0ade592e775e0a04486a39867f92f2cc6ee65aab8dc4df28c 9856e7b96a0fbdca5c25b8caab5cea1728f8ff15259cf82cd0efde11dfa669f2 13aa203f703c584b1e35dcfe314aefa8a0861c21a2bde8f4a6ef2910d9489508 0ea6aee84408a4a6a9294f1adc15ef999e5abdd9c14d5ef68f7ca611c33fb366 8889d8f5f0f1703fdf48be33dc2f62b61571bd8d502973a7d31c972a07fd929d e82a590c41213e209da140aab33b5bfab8d27517e8f5b2752e8a67d00e4e885f ff0e9b79d9904546b8ede8d607ed54d458eb6ec1c23c7b34995c1a79cc2f739b 4522035ab8025b9099c62ac5f5954cffe20e3ac4fb7849f616aef5d1df9b7c02 109 718fc612957014c52097e67edcb6d60e0698156d869f31d8675f5532f774f40ca56a23cb1ab9638450152ffb8adeee402ed6b19d87935c1ca4d7c25d2f616d0d4300683c2d088216654da8c715211ad0d3ddce340e9ffaae34c55e06d627180bef9dcb050852895b8f1edd356de53be21e7af8fd07bf18770e5fbbe5065a8b055d54a2437db50fb3ddbeb21bf381f99da4802c7b31abf01f2c7bae85be95860140a1c9a39038a511d6eed966b30c4c49ff95b1f91f44136ebef08444815318063b070ed4d7bb9c3494e724cf1deac54e424e2616b9d5ea340930f2ee1b8e34043a3955ee0a7deee391f008306b426a82a8201a34c606295c49fa2cc553f72300657f8a3e601eaf8b30b2ca93eb05856198148259460608a46b83477e6a8c6a00b75becddcc6d0d3dbf7817249ff75a2989dcf8ee5e9baccc17e48a21ff114e0f0bc3e8fa226806803d495d3ccae7112c731e1f5721db92acc6ff905ba4f1d904786ed9fe359cf0b1c193c61124bcab078bada60ad9aa7a479e534480f30be70ca919f585b99fb5b8eff57d905c229aadbbfbfdaf75ca387190bb2d8bbe23bd00d5a2c53a418c78b43ddce9f762989d6a7de6c76f6b53adce681665743175ca0e171bdfa037244dfdce15bf69a5d11111cd515ae86c9abc85910570a9a52c8f03ff4561e213cfd106249f91f174c65dfa983748112f043b833698f9c03f3d850327871119b75593a393b2537a4eae19d194aefe2fbc99d00b46b7068e296e1908a2162b01b17fcf4109eb5881eebae4dc62d05061d2324267243d4bc5ff1f740ab1550f007bd5a65ca265037f95e60a29b43c81cc150fbf45dc28c8e758f99a03308ce8ed92a1c538ee62b178cddccb4db5bed88bbd6aed6b08a8068c2f427e0f12b73d9b880c7a2bee47bc3a54bb6aea3792cbfb2fd3dd0cc076a0502b0a0107443b552c8318473588878ea5c9b6f8a0f56d08bc6ebf07d660ff649ab808da01e7791129b542bca8716958c1263340c543fb15ebd69ef62d19e7df2e50da3209503f39cb4c3bf19886ca5ff7319d17b17e6fc1f05c9c8297c1352b195e1e9b037ce5f86f7ef1a776019a92ae13e3d3cd01923d58d37aad6b8e266e470449af03a75162db2fa06bdba544c9f3a4de465c41d20d854e5ea4b4e5dc477c2decc60a272dadd8cad14cac3af3848c79730b299aa2d9c29431c2c6d60412325a0b020427176e4677da5159e6d9e4bae95295d938575cf5ba71ab4f11d2ec6362e36e03d102e2cd038c1eece1360445e222bd3aecf0e87352c4709d3496ffab403c430bc7414a46b36316ea0d60d5679ec5e99f66d9fa84dcf7cb4e5f831ae50b416e057408087d471a859468ceacca8078e787703a2e580fd22d21befc8c4c5cdbbd009579c91a032a602b621c81b749bd9990bd0da43134140f70ba8c4db4092c5f0ee2269ca42d80411cd4bc20dbca36a5c973ad46b98e57f97bec1c8599eb65d107f5a6838b9e8b6fd4d3271de5c46a180d7de863fc93a8411dbbe5b2b84388b300896f18183874902cbbca7ff444bb12b699898b69ca80f59829a195378837550ef76d2a17486dbcb6a75135ec1fb933202c00afae798ed967b521917a3d0ea10bfb99ec952c4ce244dbcbeffdc132b7a32931a1dd4afd0dd0f786fc5b83b4a50637791eee9c61e8c836f5235b31ef87b3336e8a2c660a4463ae93b23078b1d808752a02c965509851de48374f4d815d315054f6076e61adea1f3556d58debf509e635c872d5b9fb45d556aade31586f07b85367aae629381f1782b73cf1cd440ae44b78b444519911ddacaf8d9adbb4326b7437204363a5dc7c80d710fbd459067f206205b8574a8a65f6583a90e3a2a12e439431423161a61e9487514ce83d0c7b478ac0d9419551b5e80ac32dfcdb9b6dfa68f826705f763035ad30874c4103c071149831bb872b97683f182b0b007365e1586fddc4c6eb64efca247f685404a3b85ccd70ece4bd82094789d1062ed1df48b58ed7291b3a39ad08ef92962b0f9813ad3b90b1ff8ed941a04ea7b22937f6071040feb853b067185d456b96e309ac7063c15ffaa4118614691c65ec26c503ba12f9afdaf83ef8b7de515fb3cd04f6bd93ccebeaaf6bf530c3382b80ce6fdc7fae3b4a0876adab9e71fa529b750a58eb3fe7591981a2067bbb2bbf9b12467898300503a9797b5e07bb5faa1120096eeaca0c5726d3097c2de3d5786c952cd15b8be17a9e5c2c57dc1abbd4ad3707b8ad0a93d11c27224a19d6b59ebec1bf9a64a36429548dd02dec123e6661f307abca70834f5ca1c332304446bf11e08cf27a63597b3d73e94364edd907af7708adba1e91f21c5d910146decbb1db0ce8707de1e57e18a6204e0b34c687e4ad0b4abc43516cbe5daacaee9e5122b7013da1ecb78883acbaee056f6dbfd10a57072dae7e4cd4ad5d51cdd1bdf6b5cb978766750adf2c89d6fb3117143352b4970338f4459d1264a29769bf6ea2ebfaa742da62e61a2bcba5fe47eec0ed3236b00a9449f94a8118ef187d7bf115c588547c71207e99c578c9ff1050302b32616b0a49a76c034a6879110a01b470bc11890c3f1b33b073e8f59d584a26eedfedff002f2e4c709706751c99110d3ea168c36b07e81a42ac5c0e0d37bdcfc560119501262ff5cc5ee6262c1f2e0c641f429ad0ecf7aa28562bca2b4f05156e05a55f0c6cdffd2ffdd8705edd3ba8e31e55eda4a211ae72bff79a66064cc80f51b4b1071b92609e9d1eb0705f88e17613099773490ef85e1c90e0a7662c8fc402194109815ed64d65df9590c4ecf6a5c1154ac5be2cd23700059f9841f626a261044305ec8c6a0d76f63b6f81cf73fe19648aefaa30ddeef50b19b564a5dd4f695f9004f867d90ad8cb1da98e3178d28ed6fd7cd7ea5e71785a4ab57bf289200d34d40cc28767791943cf309338bffb3aa642b625fb6edfcac29e680b49af7b807c5a0cc7a0469ee647e59b0394a0554eabd1ecaa68cd98c04d093adc43738f4a966a0baf25a7d31fffe2072428f809880b2a2aa244f53a62cff0331b6b0cfd1608b0021941a28938ab0957039bd706021a004b8b52f2c70654bb941a0b5893a4e743092a31d5c5f2a782b35a20d42fad3102947d1325bf208fc28747e11e0ee5c23f09b89e15ca984a30a68e6d59fa3558e60d73cdd2ca8c0e24804b63f3166995bc0f2b2456eba828d234c0630408531cd329a60c98eafe3ed1bd01e3fe279513040fe091ca74360cc117eeff6555780174bb79b8e7e32dae26d4d9d1a26a3307ff0eb2a079799314bda073ba9dfadac4c79d0532f8f99d87790b4de4c91150d5740487acf50fbf65ebfc4f182f445f5ee81de7eef35387a0b30a153e8e2f5a50ce031e9dbc755569a40f1730acefa6e9644ec62273cfa7a2c8efc68607cf630eda0a7ff36ca054a19e797723ac9c0a5bda6a02a8c1933dc6777969b0268d12be0307c654b024f4a340f5143d3ca93e2903d65c20824087ec57723fa4f8e4693fa7022cbab5a8b9f21047c949764fbeaa81ca9e2c916f8b7053d19c3b50edccb99c05fdecf7adaa1205e76199e2ee1c94f57a8480ebde89e7391b3fe947e3e711c80f6b32048dc2ba0e738af9da7b4b242c755a288708598c8ba45643366ca1041b01db823e1dff8d508166ff4d303832bedf2a3f7323d7c1d0cad6d46c8922b019047664cd4f4d8fa9b3f8cfdc564469715856b8beae1214d1c7004c62852678a2072fca0392e58db7a95c2a76a0d6ee6a5409171218ad1139b191ded1f5d48ce80d5b650df38ff2c0f71922474fdfc206d57c6689ea2cfd6d43c10afdef5ae8b00f019e9e8e4b38a7c9168822c4736be3f38b7446179ce2487fef7394a1ae7418064b45afed5bb6f0fe81b8b700d46037111dcf5dff51feac19c0a84a1aead24f035091e4481ae9e58bd749d3f35c16520f9824949c4f25b60d4d726758fe6f4400134d891aaa24c7b2c73c529215bc93818828295c87d743e547fe696fe9738804436884360e2ab60c472abbd067d3033469f1ad8eb0d1a65e9bb9a520c3e8ed026b6f8f9b55e75934539b732fdf389f808cd81751ea72135f7f9df0098b73250ef180f543ad85493ad298c6d7315de4174596058fe674d54a8e3180982cc78c02d3baa6a92a9b43ecffd25c6fbda9b8de184f8ec0d17dd4b6bd606211145b260b20691084dcf11ef566b9c46ce60fdefe492314589f1d235a4cf1f1c1cf471a0ecca9b639875ee3f65bf16de025cec2cd851679810a40583018f3bcbee74c0805c786e4bf7fb6db95eb5642964e9713c338f72f9e716f9e6bfdd75df9e43d31061946064056b6c2ab31f98d1589a4ff7d318a9601e4baf7c670fbc29246604f02461ba3acfbf273365fecef7081c6ae4b243244e7ee95b8eb140e8679770ec70fb3f4c2d5af94206c5d2febfef2ff99ab62da3e263c235a70ecc957774020410d75d5e3d50e8cb32d6e5948c9247a7b85885acccb49432cd488a91e7beb88280c9410349069908d82f8bb74c9a7b289bd3ed8c191868319cb4ccdfed82116da077592eff8c9783215023d063090e9e21ffd339501be9703a391647b9f3edb7d0b9e282a0c806f5dafa74d110a4f38e4829c138ffd3459f5f22fc3cf43b92001099bd43f856fbf68dfaf0821fcc39a5791b9cff8522462bccc14bb14d327c7a80b6f9609589678c3140eca34fac9187bbd051f41629e34134f7be00fead8e4ee01f8e848825350bf6fde8e5ffe62a415a5f0744a6c30023c5800fa12a9babef0007975134c5172fba56b53066a70a39385b70caede3d91d808f02a2d496952b20d8b08f80d6bd57d7f549a13f1554bd922d4545fa7f07accc74ecc011a63f725041d76ce9b3b93304ed4dafd0a5ecac5d43c504b3ea472fc816d2244994caaa900a7235b61b4851c163d06d4440460059951dbb52988a58980264fb76f3520cb009962cabe66b6e9148935c7dc5209c426e7cf7ebdde0c1ee5cb19fb7ebfe3bd02a1caf7d58ef4db232af3de5b9f9137032683379623966a334c86d813f1644207236f2cad0954b39a6119ef4f857663efa43313f6a32683fa1da48e016057cd0b31c31ab4a2ff9c4961a8840877e982f7b4a30b7beee790c96522162c3ac28d04a80b28b3308faefabf53fa75c7597ad2a4a735ed29a423227e676414c739bf06f2f9c5bea5d7286f22ecbc139c5aa39a080da0f98aceaa95c2905ecdb3469a01747759734ea2e9add1bb9cea046c6481eaa90ede82e44547e2e9645b29790e0d17005c6a8366380c41fc78311cddf8d2f904b88d31d02032ccac066c0423740769977df6bc8500f80c89c07a46d337cc29184663a15c566da2eb07289d09180f81bcda3dbe544d2c4c2cb66135bacb3652af9ad0ec012ba6c5b1ec57ab253601a493479ba4026c439b56739a1570cae86139f08518f7f955594fdf2b75f02b03a8e7a35353e2b19d4c184518ac8a61aaa0cc01fda828f05965ec8141c280730026bed4a25fe5930dd63f088b525e93ba11a466b3b201b6f1bc14398fd7978c03f86a65c64fbae28414dfda972dabe8ec7a2b09ac374d4be5c7ba8c7c74d80906d996856655c740ccafd2510ee3677ee97d4ada4e618e948477a034817d186200fc0eef63510bb89fa2b9964107e229aae6399ecc2d4305048cae09a95d173b08cd4e5157b76a9c6fe0e3a829163c2463b972938854f043e2a0168d8c5984e10d994097538123d6b9af395a99aef473a8bd71ade5d7bbccad7f675b47fbc2630e0c7716b94fa007bb5e10c6390b9d241dbc1b2cf3b757b85f23acb2723cf750097c9474ba272dfe5e05765895f3ccb0e128b79b70192838cf7718d5818d8ec106619bf3c2dc362678132011738fd03b364a035fa82fe5e0863a2c04e2ce877e03189bbb18254632be96973a3f27b7de7974c0c136b635781b83bdf85b53942a0037579192073a96e431ef6ce6d0b57c5f678fb69ef66b5117b184cea9cc23de0fed228fe2a52bc02f86daeded1060160c4dd91509c3ed4a489eb844cd11f1400667e677ecbe0afeefb2b037ed1e7f9cb37a7dead68509ab6bb6d7bbf05eb94e00698105607ee56441f6c2f933e64d4e0f3d4febbec9ba4ccb6aa363115b8e18020ba78520260c26a9171093287dfd13c36daf57c6146cd2d208f7135fe5e07a051d7bfd81d8e6aad3944fc381b391a3d95b354041b9aada02c1b4706969c8fb006af3e60789696abe9315c12fe53784fd9721ad66cacf56b037fbe0e5df693500d7d99fb55563fdaad29fe99a6390103b8b5e001e248faee667419184a51510047038bc55e6963405dfb1f6368d039da6f49615cc06501eaa63e912ad83041a04855d0e9183a0b1771c56dbb22e6d0c8170825231885bc9a87d974bd3198b750d3e7f0416a895c53f20bebc1b9ff5cac5cdcf200bbede78131c5de2455f8f4a01b77528130130711da19562e361c2d56420cf46ca403f09f6f6f7f57fe25d4f004ce85c85a41e42ef01611ab5d08018e00477112ab65a7edf99d8eed673c17d035039986bdb9e12d494ad62768fc356664a9d7a5dda9fce6929ef7b05070ffe024eec908c066d24f1fa0028e6e0cfcc7f39ef7201a9a22aa702b9242c2afe5a001ae7b75c862ae2317d13c3a55fcc88a2bacdcc89920c576545c102a952e36605237fda5bb99597d3619637c4b8dca3c18f4ae7c04c86d923a8c8071dedb73b0230c510d6e1e5a60d1fcea4ceadd1d17a675ed0fdfe17685f8b65011f70b53b0b4ebda4f39cb6868611109829f4002df51ba55e731517091382e49ca9e1b4e8068865885a09d4fd9e98c3f36176dd118e2abeea70a171f7d188c6677b838d7c0af44df80b10f72be267d3a923ff110279ff330b6ed4e5c0622309851a7686d808023db28fce5c3b7bcf5f15b11898c037145388935d914dcbeeda298281efc80fd2ce749d6837f301124b7838988dc53cd3cd049f03d92918a939ef36f8f2a809af96a6ea45cde070691da9971d09cd0ac57767486570b234353c99f45494f00a2edb966ea65288bf8e4749e7422b7b5aeb4bdcd86c37c6965663fc2aadeb000db06b0d00e66d5c00c4a4908c9c831899d84df3af02af7b5f65b42445e1615701b67ec697a27b47ffe0d432ce260a7ea4e3b98592e4faab32b0b47149796c7c0432fe1abc64245a50d0ad35484676fd0cab9b5582702c9636f8ecedd4854e320861b5bbf6728bf0428e20533128eeb02ee042372fde49a6a0b2b324fdf1561409074c28803e653bb17ae34593c81ae9af2ef3af305e4a8b6ca2088adb0b9e5a026b8aed42fba683e921078b1ac78692784c886c465380c62fdc6fbf993d55ee0b925a5e87e0ce42b9c7bf53a6880f2f04d93a0e732d43e85765360fbd3c164d0fb40ab193613bf83a36dcbd6fe4f673a53b51a92c7067097712e51f0c9b6a460862c147c45dccee275b5677fe7d96f70288c488f3717238a7407dd568d74cf50263abfd820b131c2ef4f7d7164d6b736b54b5e10c2a78e51c17a000552257ac02363425b13e8005f67f267ea0e044541362f410baf260f53c46a8dfceeb4aaa0456f20763978a97294f8a7206c309640fed39110838bb3ee655af0b7de5cbf604971e25ce909fb29666beefdb4e22214508593bda0085e14e7152cb18bcede30cffa6abd16a7c999b5d06078e52c9c8e692d58869cfbff0bf35d8405e77658b0b264bc8ea021130fc3b936c489196c76f693396d2d372096e8ac97fde8f139b0ec8b87ca727dfb4e2a21d7fa2d3b6f8f0f9706344b2e050dc4e0f96022dcc750e4df19a75b0ca4a49b555b492571732e3305001df6b62afe48fc2fd1319064c04a0ddbd7a377bfd50d69fb7947b789fb4ad0a1f810f097dd151c5d4e04f938a0531fb5eb1275a1a0437a1c70deee2ddf7b6744316edd5d94025114d9434e19609f603f6986aa7ac6ff76774d2b5e319711a070bcedd0c164c48efc7d1c64d1c0b5942331e44c686d1c0d93c9eede3c63f073f06d60e9042443076aebb9188c70c65f17b1093e43ade726af09c7134343b54ec947a977aff8962a9936998fd5a06009c3d645a74de49015de95daa088b041be458982f6f6dc35d765c56e781540df8cbcb1c495a4609b762ab8bb8396c9a482420d4d1654d020413e9d637515d06e1e3337d085ccb36b9213ce07ef79aff6d2b4e88ecf91b2e23d486333caeb50f6c4afbb6747cefb82ff106c4f268b25c8c9e9db4ce1140aebc2dc9a777c53b0dc1415b66acfac754656b123fec38fd1cf5dd319a5de0052b31c465bdd1fb760d51737695b6f0d7582fe29bddb173b7bb453f1cae8b5fc8b459667db00b3d94099397ba5a84a282c2b6799ba382d1606ddb4aad7b3346bef070afd7e0f9adbe0314052e56868364cad9dc73f5661a607c29ada3f6d4ec22ef9148ddc3631eae0d4766c6f36d3bdb13f07b4d5f0649b4460fcbabfeb46dde7924fefcd0ad0b360e2fe56c3f38ec4cffc9509e7bd9f5e8a4b1d757d94e1be45d66217ce2a3b036042651ae82d9243e06953494f34cb231fac0f26772207fef194915fb4bad346c0b5e3ae4843cd6b8b25d1a711d50ac0c50fab2f553b645b96cd5d3acd7b052890a4f7dda5cabe169d70c0619970b8bcd44d8232953e984cba0b2f9f7e099c61403e2200a17aedc3e990a98b9aa732684d27993b691a64a9bae83dee6fd506feb0d1e8f2b895db1457e0e3ace39ae5b3ce0263f0cc90cfc7984cc78ef6423e21c005a5917749d361f0d9b235bc6a179b1da48a6002a45c7016b9bf8ce2ddaabdb02f723e1d532fbc4a2bc2f6b6dd7f6738ed25fee29382f2b18a4df2c6bfaf82f0c8c92c2dfa298946106ec8721aa00715a64324781b77b4a36cabdd38575e813043b99ad824d1844980a891a8151e93c315028b38f7af0a80e042516163321270b991249aa01e803aee99fb5de185ae50c00f45c8e64c58919d64c297f413be60579518ef231d0c4908254b0f1d207101aab7fc745165f66e73af16228e801e304c0bd1f275ec2c06507875d85c8fc484b45062808ec1b5262055aa04d24e23b0babb3c23dae4a3b8fa69600af35cbc809b402b529142ba85a13f0458c5e9513079154d486ae1c806fccb70b8aaef7b9191350faace065f79095658fb0385c47080f1110e672be86bfa4664e967bc1ca83263f8a9fb1de342ab0a0f6a51c333a0c80427ae9eb5c342bf2fba7d3c0b23d4fe7735ffd1d2728acce10333f1e71e0056ad32e64e01f90b24816dcb42b0c3bc276bb854840a2195e53df4102b8fe5902476445a80342279423d545ba1e4b370d63a17f40a839edfeae8cba75d2ad7a08d1d33ef7c0943d5aa729d432dee7020aa976fcd4e16392f9f6cbfc6d83e3d10c693701d0363f02eeeafb2132eb1e2dfc8a887ca09de08761efbe2014bc2b5d0bbfa77c10cbf65b3445284ca29fcdbc49d27cc3f75d481e1fcd4127ee5739f10c38cc2373153035546e7590009703f320bd0806c99759d9da18651e461d40960d9ca451c5cc16648ebc92c1bd0b1e039fe8c8e74259c12422dfa89d5165156600875d36add6f22dff89bd97b9348805573221e9661aaf2307f9e87b8452654009d55d9e6faeb10ee52e44ec0f63ace8a0d6cbb86ab13bb71c7f3da4d270115d05be81057acb56ae00cc24711b9633f7244560bb00221a8311073bd4dd51188607f4c76011cbccf9cdc72daea22a93a6005aafb592b7c9c30ff3df495dcc867808198a7d0632f3b34c07bd67ab066fa04493446bd95f55a61761d07e58aabf7c0424a775137d2e119d1a5ce4db60f5902b72810b3d37820acd2d89d530113dcd0bca6f6f470237becf35723df086c215ca051a3760539d84d24fce781991059a0a5f0fa1962d497150d0f6124a3e5b1f6038e9608625be5cead40cd227c66cf405d21354e5595fc1e53d1dcec7ee1988b99dca82316857299358f7fa3a8bff340cfa6c1575e9bc508ef25f3e9f62393a324cb40d2bfb4e575de0589169d2891f02d334dbe05927eff3c3f79e4c190ef60bb1a42f672aeffb4811bd07a66e17aa09f1d06d6eb5f5c647be3a6392386c8cf5d9adcec58a9e21966def0a9128dfe60889e04ad58fe527d1f10bc8538fbca40057ae024bd15dd7f860ad7ae838d0060169ca0ef57b8fbbf2221b0b659abe886fddab8bae1fc3ea0513850d2406df500b72dba3f2044dfe68d80b4580914d3ee51309d4f7efec131e5ca463bd54b36f04436d1841f0cb1b2d7ac15867cce3b90fc4ee26c82fa6301596436374a8110c03d0510141e8b76a0f69a6bbfffdcedc7466f90b111d71228c6409a2b5f551a7075bd5de54f1db2b8f215409f920761d69970682108e8155a2c48c6ea6cded4b0437459859bf34bbe5bc0eb096946ed6598e54d7e3d5c5baca93971a45a9d7fa001365780b8521dbc72883e32037ff5f100f9f326919bfc1ff4e7e804a579bff0e38622fa679584692ba6993d6175362ff81c7930833cb748ed6c97b0aa5b4480ea0f54f0b26097ec9e68b40a12ab7b76b32def17c8a4ff3d1c4552036b2f7a006782a9cec308e9bfa215d8cb7f4f15e2c6af78b9b99c5cac0195784fce6ad820b2afdd8ea052fa6bada77afbc2ce6ab69cb46c62f45f06a4aa0561457658fb00e6d6305cbfa1ee4d3a51716109b3b19da48ca741dfd6e6e765924ad4ff49a63018e3b8bd7a1b8a63883d713e8b2f109ea2ee6656121c1eb962ebd18c9140f040268e22acaecdcfb03acc7f4256bf412605441a6869a5ba4c2635a6ed24983cf028a68fd2903e8ab6af9d99330d800bd9179c5f7dd3ecaa642decd29924e25490766c3f983956dad44c054e4063507fc4172b188bea8c5649ca0405507c169470f9140b521a44039c53d1a4be2b4b889207c31b8108ad376d04d64e8434ecf670c89808afd75a05df25642246c8f9138196947496275d91f00c52abe6aa8d72d0b4e506d60c5b4478491fc410352d9c64538effee08942546cc9cc54c77db76e02 +generate_ring_signature da06804c896d88595efb06c78164cafdc78bf78145927dc153ea81bbb0f4373a c09f1c9478593f8cad7a2e0f6c5b0cf6f2ce39815026fd6c48ff60677e9cf428 1 a8cdf8769defc422763b2a44ccdc0c25c80550ec2cc8c1fbef72132af5d2d2a0 b7088257972b0094db41cf809d2a66a55cf98aade9cfed3e40906bc7d5c4ef06 0 12cde15f444ae90ff5353a21b14e80f38fe7c145840a6abf2f7af5c7c8aac9078e7f1234502bec094bde4d05fc2383b02e801476f123b3b8ba9bd0e89ee88d07 +generate_ring_signature e425d77f6f135a6590b4ca7fc37642b82bdfd2d03804fe2b4eae0fdbcffc44ae 1d7f619fcc8a2a5e0b1695624d5862729dad277ad9c3dae226647a9d9de8e8af 142 2535103e447d0ff3942e2e7dc3567bb8b60de53b26900d225cb8e93ba2f7733f 265db31ca32afb5a8c88fce6af4208bb92b94edaf543c78bd5404a279865fac6 b84f66a5ff78f62a8ce7141e777bbd25ed21aabbeb77ec6d546d4798c1e11d3f ec133ef83d8ed965608e87341f4bd4ba31c9c1e09336a7c5ea1bb1c8f9c8e048 86378beb5a6a494f95f59c2acd9887a7dbd9148836d4d46c2e5f0f16b927d377 36fed8dda0698a137e4b5030eafbdc721b8bc78e34f4a086e52be5cca9e4a32c 5d0ce38d812dedd703f770e611214d918419fc5924132ec78d1afe22ef36f36a 257a2d7a3edfc1e0ac7719cff060283b33c55cb32b62421c266e296301a306d8 c31fd34ef8baae1190ac6783178bbedcea1b293978649e9f80557ed8a4ffc3d5 f8a0b42cd6b41a66f494433d0bd9849aecf2bf9b8dfd4e11934ffba989c673c3 d591463f01f435239c67402ff83939cda8309bbb52d2bfa4a73e620136af4028 277fa3b11e48ba5620d132c625e8f0600d6139e0798d66813054e2121518d651 725e32e668ee489d89a681f410f111b247eab487051f0dc2e9f5f614a8b342ca 82ee09ad394cd04656d733d79b47ca0b267d449a79e4511b125fd4f33e954c78 1e3f90ec6409e9c1e845e7ad62834f00cb4a3834c64d6719200a49a314d3b6ac dc314737c10018ad271825ebca92984b889e38d594ce350a4ea784dfbb8f6378 157ad50c587b06eabe4e57de369982d437e6371e08cd0450700d206fd7472476 445eb4b11e2500621e119cd01d67222ad4fef9e7d4f889837a4d8c7ee5a0e1ca 89b832dccd44a3f6999229af3ec9f0945f680ea4365de3762106672376a3e747 873f32999b905dbbcb39557343b12d37f9cc2064fde66a5171fe9cce3edd1001 2081725fae69c8aac3e2cdeee5140db6196c2148331e5f6ca49f3c58691df6ca 1f02d3d6937449305546ea305df7c98ab70e3bf5f5abd02788c3ae4c3c7507e0 dce47863b929c77e41f427d0999a19c7609d5f36f35f180aadd7821011eac045 021066ec2772bff59c44d268a4b65067caed22ed6b9a27d346eaba9bf9ad24aa 04133cd321b5a8e2c73e7c5ee518a54ed71a0de26413eaf7d17528eedaa50de6 969d270a755060fa28d72a6ac3df0d15f6619e5e73de71151a426d9f20116cd9 ba0291ed69595590b695b2ad565265295a55bee246b63a7e20de3929af6a8f46 6a45ea9b297d85a2653393e4728f0ed85d345d8118a9ff647afd80718ec84e8c 483af3c23321330716c8dd594bc90d4822642491faa69b4d48dd30b5edcbfd1b 49cd3a14411be4659795ecc29753c6a0c8825ccb995910c4217b6170412ff2ca 275b7b9dae4476a7386f98dbe7bf9bf5d7b982427168d4a23a09d2a3a2c370e8 afa279f936524261cb26f3e5707586819d3a9bd94b05572ed75cfc527e385429 5163294dd93edcb5c2bf097b42d1d90d2afabfba508b8a5b828e4f12ecd08a0c 0f733f94b0686acaf3bbc7edd7a71dd5707356012e89d277c1434f9e8a3c4fb3 3be5818b3cb0207223f14260f1282d49a5904cf39aca33200a1d7ea6431257b8 ebe4b86d9759c07ae233669b3f18268757947d51984b037b4c485b1f0ede590b 6114c5fdf7f70a57a05b1cb70124fd08633d441f4c8afcdd8235b3b6eb586b0c 7697095b8dc946f26cc1e8a01f305050f52cbaf3ba8b2051f271bf8ccb0b4595 148980ea88a57f522f6b45878583fe2015bca2487f86efc141c4254f520a3560 0faf8dd74cd6b11613f56594ad0920e640d61efa8f48db3280d7d9e3afb572f7 ddf7b9b7229d6e9df7b4abf580b984ce190c851cf4ac3457362f8af8288cb872 edb8e35795e6b267b959c1d6bef1150dc671f06194e8490c5cd765ece6d85b32 d273454e4f9e530355251b4d51cc2cfc2483b59d2008d27bdb2d84b9af5cfa5d 0ec88cad75c9ce36b47695af674a42057eaba2a36315ec6216d69a2d4bb3af34 099dfbe05ab12346daa3c929ad0dc1c146fa747412b575a71ec8faf00762938c 6ce76504ad5a73f581fa71de624125cb776603a0959b48c1753dd4fde98f7221 cde44489f06773900a088aa1781b81e3994ecd8450a2f67660f3850bd3c3d87e 91f007d102a6e0cacc59e17703264ec0f3c973e85df85fbadd017c99c1f52b64 47865e94b512013014ea0d42b9360533521711660a9ad8cda87208843c8c4172 7d7ae4632947409cd8bebcc53caef51de7dce7ee2e701f600e9aca10d8978f72 9ed1e84ae499d79219439e4089cd889fb5803ed14ec561be2177f0ba47bcc390 e09bcc484b519a2bde473cc20d6123bf716851c36f83c44b3f68838e2e69cae9 7069e5743338b7cc5acfcd06eba926bd72d6f9247f4923459fbdbcf293dd3a16 43a239b097121243931a4b74f7de2e8e9619ce0bfe94d9bc0cad23088ed66a98 5a21b3a954b6da647f8b29a8242e7f985267c8de1c7b737d57e1acc85d0a37c9 2f732c86d58b389fc5cb647a00353339137c18cf3917b21754df9e1b139e5ff1 2d1ddef0169225427895638a9f75f34366b8a05670b7644f62ed5f5d4a87e587 4e4e2ef7deaf767d529a7031c719141c90bed7f9ed69292b13b5554bc278897e 7f76a29bbfe17e57fcad135f5fe47ed4574f340591792f2221c6b1e4727edfcb 212d47f84456292f87d33db417c75a8fe7b9ba2f2f4ea405a244d9bccb4c4ae7 16d73f49b7357b44a07543e4f69977e306062ddc824abe4dc2104ee5a388c10a 8ea23cfdac5aa4833672ed9c4f3d52d30a4e4dc76754284b0f2a186313baffe5 17e2d3086bc1b218e17146f5b7ed7096a245d16c9c02a285d9e4f0b2d58fff89 c97d76c51212bea614c75bcad8cd01566220c306e7d117607e5e210eb1350c4a a94c1f71700fb19582a68087e0a66249bc5d56094fcf0f18dc61d06ecbabd43e c4f0c28f6a4c1bb9907cb9c54613feaa9f465407ce20f1ca4a52182c23482036 2fbffbdee41182f7957ee3f805d45609cc9f4eca57300a438c3fe43017193e5b 6c6caa323129ce23e8b5dd5495be171b8369fc38f8f1958d47075e20ee8dc8f0 2384d8eeadcc115d28347b2b0240610591899cacdae03481c096ed5afe44ada7 09a52be112b8ad2bf02c08a576a734bb97fd405ebe9d89af22f2fcd894d1fbbf 07ea2182d297a4dbdd63c0818d5a52d2bfcbbc81ecaf118df981132f3d962faf f435c273f7abd19ec55a220b1ffef29c558c9e2322b5a04a34f71c058a0ba04d 30d28afe5b3cf2163608978a101157dda1dfa2693ddf3b29b65d7c818848e35c 12634047c7fe732ef96953d09a8619d30d0aa55824b0e78a791e319d63fb54d2 a0a5e99fc6f93d0451bc3068031a0bb72157b6dad36bb9d63c652535a0c1bf9b 2c7b9b7df49eebb9cedad72ca67b88f9afc774271f4a88b39acf53a0ea8e65f2 d2876c12de39523f2697f43cced3f6bdd2468c45e108cf3321394ec1f07be26e 80c2d2c2953a14d86ef12165c8ff27b546c84fc1cfd0d87402eb6ec80512a992 5ce34f5d8631d043096294a57de36fed7cae59834dc4f595250d236f65eced2a 91c11b801af70b82c62d91d88ab4c39a72ec102aa9bc252e8dec0b24efcbd9a3 d32dbd9e88d6ddbd8651cbf5efd57ac1b26915cb423b17d0665e5b3df697a246 b9de75bd50e7d3b1fe40a2db8ec241826dd087609c32f3e4bd800a334b54a377 51f777547f612227dd483c7c8122cc3ef40a29cbf8d5625b908c987605b319a4 8f60c75dc2c1a8441f63ab76c599025f3d5f45a3b28586956c930c264af1a1ef 7eca94f38f77ee72bb5909028a6194c63eb33719ba36cb7d3dd3c80c62db96ef 1950e16245d2a3e4c37ca3864f3ddcaf42abc2de05b4dbccc5dd505b9631d68c 8e21bb30cd66c551debd43c2742eda31704a256d6adeece6913f0a42f5af6b3d dea618208c69bdfa87a4610215917b48329af2028c3f802d6ed9433b5fb25e68 8328a4ff79fb1e2c60cb4f1c01f03b108b4621c2efeff4c6543ba158a6af7000 fe4dce8d92ae84a8fad3798e26cfdb88e59675698297deb15a11f9e1a4c6cdc7 6635924bd21e8f1a1a331a0a3db643bd7afb58948f008769d8fd7d2bf98d0d7e ac976a58bcc291134417b72c5668d381dd0ac2b7557e2a6e88f833982a09fe6d fe35f257ed09ec22aa118a57b191f6ce0f170ee9e61f0ce3891bf06ac014b0a5 472046fcb1bc0647645d02edba9db1160f3c9612544aa09e548c93cc6ead15f6 0f08e079f5b7b5ca91fe88c7ac267240b97544ba8659660b51d83e6f12325c09 c13fa5b45fa51d70e2e2a455aa110dedd2484d5d8a837e5b2ead7bfc48db408a 0ed433d8593a9767f5ca85602b883dfab40af01a337d0717485a337e0c0819bc 06a3847ffc6e289ef27eb9e382f4f5cb87aae3b8d8baa9f66584b8291c7f5fc2 ea9691c0eb61484bb952bfd37c0a2e9087bf58fc63a77eb83de60a6fbd87dd0c ddf909eea6ecc72a0ff7bf4cfb160e10c6ef82e668fccc5314275d48727be419 1eee8e306192032fabc7a7c20252858b55808f4494047c48d0521569644c2e3a 49dff180028a0ee241944c40b7f5248a7fb8eadf02fbbf568ff14c37caeb1595 e170968c8a372130747ddf12f0b1114869e79f651cb862be8d9cbd196b90b2ac 685fa6509285b619c17cf339e4487173f6ec2e3682907b2f34db7dda878c5bcf f250776cef7f404dfd3382db13b714f18109b722b995a31a6aeb60cf36e4770a 6622366a7ed5c8afcf8b33e967cbc0f8ff5967c40fa935a45039402300bbfe2e 47616d4f01e40682b173e50e24b3c2b9525caeb30f123d77c23c59b6f6329c53 322856910876f52049aa11e36fdc866c1b64c2cf05bc73aa88c462eb71d0c08c 1e99eff91c5401e3f818f108e5c4fd8e9e52ccae61a49c0f4474f2b28d5b776f d10d46c2fe89455e2a3313691df0502a89a2f867dec8ec69f48931b6f6022aad ba10faa9de68b08474732cd3f97e852253f75a4fa8eeb0eec22efb3ae5d71df9 9f1c219eca08172c88aab290c2e5014a20fef6d526a75222b8d0bfb512e39fe0 e812a8a4ec301db6bdafaf4562d6ac2f749b5be96901baf6c10ec04e08db08cf edd25128c2bb5c6a015938eaa4c589f530b927f369da91279d36c75fc8b64d57 3822d6b98d7bbd4e58f0e60f5a19e26b7de196cbcc5140535dbe3c17949a5704 cc6e5fe59d265d9d77067fb50a57b3fccb450ac19db2cfe06bcba47457585d41 ffb09d269f23adbf9000ba12468fdb38dbb3c575bfe3e26664777363dfa551db 1e14c3fbd6105dd728838af4c8e59a0a5c8c4afb11e6a2595475827e224364c8 0e1e0db70dca6cd0aba3726caa2bc70b3b08247c3a81a8318248983da95c82cd 3541ec7066f5905d36cdf01813d4c18549e38167410bef4c4a87e1d37b2258b2 c3eca6a4b6fb2e411043ef677951c88cc4e1cfb5b1fafc39626842ab01bc66e1 e420b2fd3ef2a76d68fc92180c1da506a3944103c4307ff1c25e0852cfcf672e faa35bd865867d60231a086b6f5956b97ab8daab616ef3534fb6bdb85ec83beb 6f82c96f12fdfae7cfb996313d430a891a8a28d3f00f118a5e4a972ab34ca7f7 a540ec694de4a8858546ee5e772363e43fc05fbf37bdfb571b70a4f3bb91c449 0ae62aa251d97f279cb44d4aedb8baf8e507d842bd383fc638908b8a1ca76647 7fd23e2acc44198d43fef6741af62834b180ba211427bbd26f756ca6144224ec 4d756f0b2caecb413acbb932f8d7f8a11922bd8c4cc4953bfcc333f6a698083b 5572b37ba3a956911dc0c4f2f7f8257512c7909ddf5f768c2d1689a89d7deb80 e540dae588581636db8d1d215d1e33cb5b60cd686ce9015a00e1a86356ba1f1d b8f65fc6c529bf8e1883bcaf41773d03ad79b6b4e2fb2f80e34bc0a6a4b17d4c 8e31cd33fd304fbc0046dfe042c0792919bb3bed3f5fb012f2c813a3ce7587bc 5904b13003fd45345e2d717b92d1a830a5a9b18224dfbc6e5d936c8e428e15fb 2c249604031e1efa184b31a07761352a1b79859cff6353d42c782e19e93862a9 1835623d4eeacef4488c3ab3222defcdecfdda678919c2c308dfd4d54d2f6dc5 5f9280a1f27786224854bd7fccfd4a4f31aeba08a04880fd83d9cabcb719ee2b 49af0862a0b323342860392456603cb9dc457f75fad22bc24ec3bf9881edac49 c6d7ab2bb1fc8c9be1f1e17c4ac46f5f4f5ba45ba6220bb061bbe8c85d095575 f35e0cfaa0207bade508b8c370c94443a31e244f67e7052d33bc4368f18076d6 288423406dcc4f7c2402d2dbe821bba45a3d3d091dd08c1db32cf01e94f8c55a 6feb8578f134bbcb501c74c21f0a85857a44126b079cb0aee212d618297ea905 e334c1146ec88d4c11e9b98f3b14a90889cab2957b12e44d87b96b95ec9dcf42 c52cf7b7670000f7bcea706cb8cb8b6faef9541e5fcb6f9e7da7a4251fd33d04 7 ebf070a1f0d0a6dad302f0bb9a7d92ae27d1c878be1b400b105ba0dcc62b8d009a809ca4d6ea4069de5dda4a06ee1ac73edc7c4c68325fea66a12ecb59970a074b87315d2870caa2a00f7702a740bb695033ffcadf0eb0b9bed05bd4c26b3708118eec5b3b1833aa3592dd33dfeed3ecf4cfdaa2bbce839d182f95576c51e602849d010312bf86f03264688b6ec2d6e093d67003a17372d08e559e8b8cb97902e14da30dceb5c0c524795fcf24001c693212ad2901353d806800e0e72d1b1509bcad27b520890b21889998544dd916a5d4553c7bf1f21c1f67ec4db8b7786d04129cb68042dda8ecd1b7614ec9d9ad9a9be244479a23b2a4273e8066ceb379006616777d9322af6020e352425f5f7c4f40eeae2e6bc1a7dae12169fd707ab4067577382ffa712f7596677c6d247a887d55ac89b38bf55ed4123355b6a743da0acef455e95c5e9aa9448d8b347d0c1f10e6c8bae75eb5ecc02c0256ef74ae290a5cc74f751c5c6e5189907e6ecd01977294f6fea6a71790051f330aa93b55ba06151b48293bf2470457098e63c1e4fda1499694e0eefe92860b49f76cd21c1201886bbe02c3bd63486aa57eb2b45366c1a1047c994c841e37ee5b38dc69029a03ac1a2119004408b2d6a8ea32bc8bda9b93a03dea57d586f40b4c639085135c0461f425b0666c654462a6cdd965f39bdea225311fa333cff224d4f5a3597c7f08fa9812e0642b4cd00812853157bce16b8a136b4b6ec30b69d94135b3595f3201bb27946f167d1fec95ac66ba8df779445c7e51cc6bef85b79380549c99c93205afc4424c728a2f569ab6351456e8f3092cb2d81dd987b7cda5f0f50a990e9202c8e762fed5ca2e15b0b7e4ee18cfb1446a60fa4cd4afa566f7058ba6675f220d0ca6f7873a0990e0865005a68712ff7139be832c5c1dcbcfe227ada00d01e908fb9b6068b9f4f64dc307417e43e65748df0d1e710a718e5cc6136de31fbc320e8549b5882c7c838871be66bb7de6194c3cf6992e9e2ac031d3734fddb410230148b3298abcd027157b1d4397954c7c8634a851d1ed4ed10ff4ac4275e978da0e47a6e4c77595a9375528d7df160abd044c8a444ae878255d7b394c4b4bf8dc081059542a6e88a502c3dc4ca14457dc1d40f1f9813ac9368cfc8d7bb0b7881a05a97f2ff8f88515eb5759129c75123db55be8fc3b6781a728d55a1eb4160a240ef9e782e529aa50d9da5fbc137f5a6d2fba559f127ffb8b3db3958de79d70b10799d272a244b1749d27228823d29ece25810004461039596b575462dd0bbcc40be001a3af90a0f0a4517a25d88b0bb74c4cd5d9e041a5d8d0d2848fef9965e109c46e0c15c539c7aa90e1c59d3130b7153904a68fa2e3555c99947c52c5b5460d87c573fd730901bcc7003328ebd1dd99f626fc3d763ce0fdb3b8fb64fe680a037d73a2b17d50c4c86d241b3c3be2600300551b41c06308d376ddf234bac2b201907eeaaca44f2eddb456b04f8cf115749ccdbc178494788ac0500f685b5fa80b7d834021c69f0f860a91c41e3af02e71f7c1f3624cf902c183636b5dbc6b4e0e496106260ffedb0cdc42846bea410b507203737369c7f8f5252d797174a1230b83acd3c9aea9cd8eb349bf643ad1f6bce64bab4bc8557a1a2bf975fe340c39075e1b68e63021978e50503fb2262e453b9b4bfc13b49dc55be91d9c282b6f700355f4abdc5add7363840a8dedc5ca07dc3e3984540e7f516a0628458ff35fae0256b2a033dff37280d8f7c79290cc0f4036a865dd15a0d57b4f51ff447fb569097b7788b4f841cc3dd33843a1c3d29a75cadf559089a59fa6b114d13452d27902ab76b8a290a0ac985249571391e9264ee9a93fd2e9e04bbf9f406b776f6fc90c928137a00ee002f861cd16b0fce5ca28e3b1a05c62c88fd61801d6f9010ffa06315789a1c1cf8a5d3f3a2d96599805a3f8e3f5d0d0256ec2287da6e8b2fb9d024d5a8079860b7b9b69239aea335f53d0e85076876879a07622f230fe788e980f8206e08591eb3e5e51ce784da0f0445790a729adff5e80ebe5469c6377d7090595c29ba748cab8d44f82c115511e62c4543a090c3b2e49173280287a187d1d096b728d081b80bdfb0d952886e2344ba79f0202cd708c875082165f91d12fbf0845e4bce67a9e204b1d9eee9847b625e1d85b4759d3cf0c69a79b3fe366cf46073cd787690b291ae2946ffaf4ae40fd2738eab9c963debd4025958916e01d6708fec123e2b83baf5474f1831b5e4311e44761e43281187559849acdfed2610f0459e6fb7d0db45de319b99410a2de5f5b35d596c7c439e41bcef77009c3b45b09f290e41502d4d8c692b2c11cae2e7342b4fdb50b8a64853945653ed9a70e4b0b6c611bbf09a372cf942e0c722118d229b4319b149c8a403d37ec15960bee220d56db43611aacae8559ccfc9eb26d33988eb61ab5710b1854f17406e544c6740e28df31248fc0674bf86819a72a29d80a862f178aef42b08c0c59900da1987708d0ce49104d9329eb7f7c5e3a50a1260ec9e807ce7af4bf4891764f90ed1bc50a788088a3bc5f6324b080049ceffae19cd774ed27d7c0cf20694db2392b738e0573b14616ea3bec93c082fd139d98f88d49c3a0c0624bf852b4692fada7093e046e923214055c532cce199e77b2e5ba6f4cff4d091de72fbf2043ebc04abcff09718d00661b5f2dc97703e083273fabeca60dd661b9c7bcaae6bc9bddca1ade0f02c02dd6e7a5090c136e6abc45c04c5e8c196c4c0e15788c96ed8f2c57ce6d028c51c163a4c1511efe31a9a93a95121fbe1729eccc7a1f6607bc7ee4217e080f41f118b2d450f39a8a6004d534bb13c788cc27c520a2aff2dadcf64ed876be02f6a2449225021abc07912d1cb5f5835389db3eded58867236d7a97a4f81f9d07baa02a79ed20935da35436ea2b0b5ec51e7285fefe309615c18224942592e50d934835195062810d520190d688508ab7831bb4b123781f04e1e98cc6ef4f7c0d68930d8cd6fae1af813622200cfd322db53b0b9b700350f7bbb32f1540433e0050ffe8e04ab4b7829d5d873006787d9c326d70fdc883d5cd1cfc21216ceac40f8e45e8479b6fe05ff8880a1af0529631b55abbb15919d99b3dfc850b1718c7049db40b9b33ccf2e73c24f88786b872b94f65f850ad0fd021314a510e0dac270a5166a489703f9c08926ddfc6b6fc71ef0b7286f1e54e15a1c9e592a3ccdca20f1ce24d45af9ac56c9c68954bf75bb6f8eb1f996b3137f62fe6aef5608b973301e7932a97ff004d379ce3167f5a95757619238588ee54d9d22b588687adaa050eb0b9c516bfb3cf0a746e601befa334b9877a60aefcf0ffa88e1fb64497ba2b03e6d802d4b3c1b46c8355507f204f226355ddeec43eed592f3c252450bd5f9e0aee21d46a5b03fdfd27760369f47cbfae94bae5fcc12c0bc916821ac7e832800f4b0d307252ca916594bf35aa27a122f66fdc3362ad5443424dc49404a2fdac03804fd818d8c8ce144bd22e381be9e15cc74c18fcfadf34c8e5dcdefa0228e80e6ff7a5225b4954517cfd41b5b9cdf2243853e259b7c2e052fc5b91f4edfbee0fdd77df83fa68115ca64f111c4d4f49b9b003a650d15b1fff9f218d314f919e0739e103ace1aed88fb387f6b9a058493f3fdb2dcc503f1db50de25433ab6cb9051934f27b99ace264902c99d832fdc5167fd0d2e7921b9e4439516849307bc505b9cef592931f85822d66662f3d70ad2d08f8844291223e45590bd179fef8d30e5f71ca2b9891da9e57e0882c98747a5b6b7256716f94d7f23f13fb9f7d412c0d26f318bfc12394ab77298f481aa72c6a6f551b1417e63d1252e00eb305a06e0fbd78b1791b2b1dd4fd9d9ded99f5cd05fa431b21fa709aa31be21cfa3ba7130e055b957a7eb2eaf5c3f4683b4f253af134926e33359ff035a424d1c3ce531001fe35a1b42ffe357759aeddbcd50575fde0d46ab2c166b8fe883787eb19f3580b3a2f6c6ff2e1e205a98a44dcbb3ca0080957fc55a809c0ad7d22091c3681c70f50dd69b5c85e6b1018c5ae0e339088ff2e2a6964eaa03d6b2cbd6d5b9314360c1c90d1efc99244948cbd79bdb7f4b25443ce85eeb3ebba822285ae844ff1700cd04582c64fd1b4146e3bc50ade085f398ce6368d830e52bf77158c467fa8ee06889710ef1e772f9265736166afd2efedf820c37d85d5063671d5c338056a6d002fb9969579abd204e0a06875839d1b81200f7137876f3e9e85dda5ae57b34d00c3cd38e3c61f499b08bea939402d7a8f29ca521b3d23f171901b5547d2ec2208cd9751c4caaab50b31f40a5e8182f7f31c12928aa3efa9a033171038e8981407f82edb951bbb8836aac7b255a1b3836508df8ead8a3f5bed2c5af6a855c8f00c0c91af886d9f184d4f5745e648221326c70f4005a55ee6911ed41afe1ccf920f5f0d9ddfeb9967061af423f701a58d6953e0a915929dd0c4f699728e5428bb0c104ea0fe5e355a0d5baddaf6b6ca615948f1e7a65dd0c4527b4c06b38a768606a4a7fa856c1b68443f0dc35936f28c11c7eeccc33d626b507a0b0f90126c530973f729acdfdfce2345932af6132171a47079f52b46158c8692e20a859171d70a7591148c70a20ac62ac2a09337b37aafdb2c7a6ab13d25c96850f267cb725f07c270a63649e41160fb53da30209c2df64b2285303bb155e1e4e0124ca72ccd08f81800b87d0ca0b265b41346ba24a20d9d3766b56561ef436e98b272ed6a9a0b7e8390470ee4a9bb87b82180a5b263349ad154f1e188ad8db22d28a252a13a0f6d9e5b75069bea03f566326d14a6e295fa1e3a3c1d80bcf32139f02ede749e08a955fa17af25a23f4b379bf43758da575a9ab85426a2f0437371313b35f89d0e40d625298a9395a8822989e860fa8d667fed4a386e0c3477cd709fa60bef30016816b89f31cbd1bd30817720ec37fc93b045d512b44593458502564346a9290ccfc1fa2b1a552b4bb20a1c3346f5b0bc332a717e4a0a401599aff1858121ca028f10b11657f47f5f05b8ea2d3a69bf36ad2abc30f72c672a3986c10f902c7c0a60d9f2b294b0b9b7fc9e1a28586f022670f0a78e1a68d24337c58a5eeacf5605e8932dbbd836bbcc335f6fd70a825176c7dfbdb3fb53deb83a86ff1dafd51b08ebc4866e385cc0241f19b59237016aab919e94536c76b5392e174c8d77f8110b45b7e83a1837ef3f002c2f31b1d85f0d0aa216240badea2df04f2dfe9731e10bb2941f22738d00062c94fa236aa854de8b87b0abf63321a2e61ad75383e937029bf97f5bafac7ab43cdebab3defde290232a10b5d0556917f4cc1db4788b8404faaf9724f093beab93df0184cb1d020a5f34c453b6e6969dd1ae3063cfcf1509ea366fdc204006354d6005438f99a963b59686662e64bb4db6e46fc51e5d120478eae4bff59c89c5e6670e91a7a98de3b0beef544219394049b8737c5c2c8406060c6cbcc45b5ca80254d2ea2dd1e60d255edc7d6bbbabe45f49108032b94f0ee8747cebb230eb41f17145fa006688974c8ac5bf42ead07af907fd6566e3dd0cadc6c33ba9f1f92e113b92ac52fe3012698b68d2e40fe84239bbb7b45de08d036dd4289fb8af23c3283b09c4ee05f0fd8e8ef07c94d3a23688c7840746c2c00c2f46e20e3520b11d58ed92ab8be2d9653917dedcf841457f93a5526d6462d406b0f27788150bb2cb23c4b96d9820131e317448763982c5dece2d55df3c591c03d74b5a8909f2d599e9c456ec5816fc958b9a30409aac5895eda623a4540a0c0e83c485b1e552f6058bc72fd1b783646532d9ce329a4b227f55f815b812e207073c34f7e64ec0c84b9503a3e6445a0a3c5371359342545307be77f0519a5ed70f643f2d71c47753c46cca87bcf1540f37bc3731725d45563aa49cee4e31b2850373ce47dbeefcb1c3ab98c32f91922671b93969d4d973f123e021effa70da200d54a635e11d1c17386ff630dc3d5087ce38b76ec70c0eade272f15636be59d300b1e648d6081c1b0554f9574a1708b2d5995b3db52e38c3d250525d62b412b60759f070163a6a34fd3ccfd37c70d559030122543de7f28f85ddc87b457f9bcb0248777f8b3c785e312c1f09ec71018ff04f29f067396e390194290e9e71803301b3d533337189a57014ca08864f5856bba469db2722df785e37949ae03b30f901f0bc0fb0e5d6377b6b187c6492bc1109619c72b40f9afc7e788946bc241fb304f375259a32f33dfeb3137535fc00412abf1d4eae2db27d4b4b1224f637eb8f03c67d0e33fe6ab3d3140d491ed9e497a8c6b8ca6c5a908e0976d5cdf4fc49a50b84cdc51091d9c60ab19dd1548937b7f431dbf22c56e1ee37b1685fe19ec1b5044382f3c3c0444c6a76bf5d16a1ef3eae58d3a3a6a6b4a69bf0be07e2c8647d0d118f1f7fafa7a641e1f7ec9f44a6c0a92a9215f1e7b56339581dcfa73a5c1b08c2178d45aafd18c77bdb3560d3710143174f50bf70c957b16acb732f299afa02e3054df3a880c9a8e6fbf7f89cdcd30b250358ae535714bc928883e4626fcb0f1399263057fa66db28becf0ef76fa2b1444b8c0001d18ff7e487a752b4426e03bc04034f2b65004927356b3d07a3b118c85f7c7cdcf81bcbf3dfeee9d5d09003e57cd4a4e1bf9e66252b199e5e542b8d7fcc859bddef3932693d99f9a0d70a08f6d0f1d4cd983eabef305db6c9a9fc3a6887463909d6dfb1be662766c1c8ba04525d5e85a64ab5c6bcbab786f14b4cb7977f4636cfe3b2fd97f882fcf565e1068e6482b8144f888996f15c2a9dba95f6adf3c4bc954ddab35936a35a143eba008ed398e4682da2a06d729849675ee44fe8959925f56a73d8a1f6d079d403cb090b79783b1a6d012b295dfc3b17f360d972e97f7bda6fc1635e71c215c840a40767f98ba7df41636256c1e4a3e6fcd794a92bbf0c201e5c3f1c7abde3df7c1209b6f32c5bfacf62a4239e43214c0a1ffa622752c47b98ac5f373c690cd3d38903647c56cdb6d6f63044b49a4e8e4370ef40849e7c689d03c557866544c743ed003de7eab9159b1e474f4cd0218aa17094dd6676dae597342514591a8289c3060c7b77e445aacfbfe75d059b8a9224b4655096a6419c80cac0169ec73880c4280df4c81a1d4924b7275063a668348e4857dba6fe8cfc6b85354c30fffa30bff00a7bc33581a5a11748d1509caeb28667401132e1e71559a00ea0e4cd9070728e0669aedf5e331e97e816f8562ac3b8fce6da2a93e137733149951347c8ee04e70dcdf3ea24af0a1132de66f4c77df4615c8b7d99abd04e9fecde3ecd88f7c86505c140201fa7b26f425543d72ccf887d132b81cd5bcab15cc26785620433f27e05f4ec8fe763b36835483237998c449c8ff48e3f495260017c45b0cb95c3623d02283974a96dfea296a2351b582168affcd1ec9f55b23644b6f285d8e101847d0ada36d4583c687360eaed51bc8ff5078e721b6b98c10d91229b91f27b1d15d8031de34ded1ec4091f6174c479e7a9a30c3d5d1a94882207fb2832973f4066fc07a93ad83dd4dfa5a9d1d5af5d04ab881b962db58354681dff54f32d19624fe50203bfdd67362cd386ff78ad3eac81e103b05932f568e932392d2c9b3911a09207521a773a5fee6f970eb513b27f3db0abdb5c3ec78ea9f5a3f076b05711ad6f0581736395e78c7e4b503c315c7223733c2e2bcf4d4751e2e22d2025fe7001d001e30150fe387ec821afbc242f4e21c155de60c435f44db1858e854d48d8f2690e1f866cbc4ef541c7ec0719520b046dd64d80c7e541e3883a571442c92d262904b125484138a168f7dc9072a979da19222aeb76c17ab23cd78d5aec4c4a40c40bf270f3c6acd0baca42bf81f34f92f94c3ffed2ab0e0b2f1a2c750009654fff09c1e021a57b940bebbc94298a954aa519effc08bfc595cf2fd415ee8f04d08d06a46e19c07bb87d0177e6b29cebac4e48faa00e351a8d4d1b133c25ce86975d039fe429a81ebf60623bd5cea692d7869a6ce0791c358cf1f864a70262273a5f0942f3a54917d1be05c6455b8fd377881d6eb079e49cd8c59a7b8450aca38105087b55de0b254444dd35ae168c103cf3e6d5b378884d18e353ba11ff96dfbcc3022d708534c794cb22f3ac8a4a5fef83f4815cdabf13e87549d6b43374cb8819007ccfd08f9aeb082069882351ce9459a2a315dddd21f986504bfe294f5105ba0e30a0fbed973df2197c52cac34747796a8566aca45b79917b339de3426757bd005ddc16c42abb5c66a834abf72308ca47a2931c11460e0ba303968257b4487402bf74a21e553592a456f31f0fdb1a657856a510621facf406203f2d58d593d00a75ef7d7c7558f3fd528c0614f6e777cee99cbe05765e4a55a8ca902778317709164333ef5e442220ba682e548822577e49f0b37af631ff2782a64817f767f50513db8b66e6a65a3a5dca2c81fce369ac66af66ca4acfac849cc4260364dff7084ed822d6a8589dda3059a7e6fefeee6f7d0e34f69010da732d0466482e7e2e0e6cb9f80cca8811b4b7bae8b01fcdbcf589e542e40fba2d8ccaea4d529b662602ad08e4f717dbc0d2e141bbf427f6ca74508e8658ad017bd70dec6d674e3d5d04561fac982fdb4d161fc0cb7a6fcbd93f78f93e678177ddf61e5061d5efd8b9023656c4f6406f49681b5afa017dd4016c7a20fd43f14e23c55553f3ada01bb70e4a93c01cd815dc268e811197b9730c255c2fbab5bae324b67058bb65abb65f07d0d2a123f93f00b3c6ed8b02260479a10b910e26cb453c9ab8b1004176a2a30e0812e6434f94d3b46e9c3c3e837de4f8fc07e079faaf7eb559d025d7667ccd0a177e2110275fa0bf25bfc104bfd3985640f82884efbe9c304bd5caa7b2563805bc81bd2e0595c908007c25b704aee611ab13dc5265298dbd9aafff6897c583040faad721556fcaafe3b0bc42b7c2bf9de1aa6ea61810bbec720dedece556c603ff9436cce70221dc3b53ab5247d0eb0f297bd0cf990bacdacc73d98a62fe980ccc517df1dfe029af7e99498a427ce374df7596dbc011babb318c1adce558f90aa61e1ccfb5172acbbd2985a827cfb66fd59e4560e826625d618a8a42012e6c04b559fc5d7b31f194fdac2f0ebfdc36a13bee1f8e1b8ddd78af2cc2527f31d4060834d93df7ddda14a7c25da575bc0c1ffcb5e96cbbe62a8f99f6cf4a91e24b0c981af6fe0d36a878c769204b395d5076941c805cd1b73f8e6987a97008e140002ec85c218489f67a9ad6cb641d0bf1517219c2e4cdde6703053ef87c22f111099caa568285a39908c14c963380480bbe3e4533589d0e93cef4d65e5083b78101e4c6ca31740f1316a08e9e07d6946f5dfd62c2ef1926ca600bcbbb9772f8240e50708aa36d0804d90862a0b7e79da416d54805e0bae72b77feb3234b27c853068156a0acf88317a1b9550584689e466e90094b4e47325a85e15e3ff082468c04fdd9804ac3f38adac249797af5e7d9b17ba749d4484ea17f942d3d567e3f19031e3afff6187c4fbee71f1d638639a0d383aa4b70ce75b6eb1e94afbf9ecb2a0501656a0656e4e3e60d1d367a9ed7ab755f0ea2de8b5e8a18c947573b6e8bee068dea9f384c053291dc8f5f7137ef9da1294c8b164728252b4d8ca63054f60b00f3efa5e410d244ef2194bf98f97774e93203014bef4e11838a6fe285746a9e0c9ff14855c8a5ace856ceb4f7dc54383f8684b6b61fc7bcb27066775668adff0cbd8b1f8eab8f856975ae28e50e1c341256526f0d16928d6a1b40cf346e39e40a5b1e4888d319e4f8899f6171af18ec7b847b45aeca3aa6498291ae3afc92840f989269e0df79757717c4bdda0815ff6b2e2daefd87dba9b1f408922e8a3186037b5ff96deaf5cff70629bf9d1328090b03b8bdebfbfa046e19b88637cdb6db06f20310fab830ac92fd2e13136b063df1822f0bf5cb4c8c33fab24caeb612110fe3c1918e04e8cf05619d2d0e0f0cfde285892b2224eb9b035ca92997504908036ad5f8e6199f22de4f06152201d036dcb50b902ee958ef367c8da7ee22a4d801d76de5c352036c4842016aeff55ab6e5f971bd5e2b38767853018a91248e25024409cc6e2d6eadc8afe4d657e1cd6b44706b85a55a74f4d4854818cf1726db0f9db1d66a6d4ac031671f647e583e505534cd7fab9c30699f566c32c4e72c9b0316588e1f5a1c99fe093a868927c1153e342737dadc3d2f8216a0e752369a200058e770830c2cc45aef6f9b768619c081c1e2421b1f7cd1aa8d0c87696776ee03f2618d7d98451f8071e5a1914ecc72e056949a7495b3b0546643e818adfc9101caad9d2b0ff5353535ad7a7a832cb640967e6abd09fceefc732c50a462f67806d6a9c413f248ccba6962d1f604f5e12ff23b80b72a35c311644ffe9f897dda0bc12e605eb5f17bcec15d7b0178a3a803dc4cbbfa73216be3b54a96303792380d5c0851ee72f01c4bfe192a320a1ce949f0b6f5e22778860a5cb93de56a0f160cd3d426b481777e71097728d30356667dbe1ba4549f1c972abedf0c2ee66b2a08b46ef94248aa0422823456be97140709ea0c52d46751838341f1a6f64e1f7907c87e566c9239caa0cbf6d962831f52d9ad966e959b82c7932a4067f0f3e8e70b8174397f2225b9424bbc89d38af4ee8f3a8de503dc7812bf1d9bc51d61232d02753ec4ebed9c39dabfb3430726146ca9301e12816fe5eda2b7e50ddc94fd580047ee26e1437375ba7b91c0caa91888cf7fb41155153f8a286937e921d5c13b08bf8cfa8ab56b0f3e06a5f7866f0f8f40ae95dfb6c84cf548cbef79d1d65f1603f5e37297b5f2fa6ebc2443315960d6206b20bc324b8f52fe0f53180059034a093147aacb85484062130a220b4443844cea8e65d2c1d257d37358101a2c026a0a5525540237c46e04efece6255443fb4888ae4de7197da4cfe8f89724f9fd64093980f2949b61c6c4a91bb3383ead5bd5aa487e07f47138ffa97c9022170b190cf61fdc74ae0c411717f93bdd42f39e52872879e72f9e7b3cd07178e106c1290b74d64521a9565f69b68128ef97e0df2e32915d1c2402db66aec7b777af9cbc0e48e8935ced3bb62b90bc4dda54c286bbed2cb239e9c01537124de2912516f40f20a93d44a5f8ab8968dc082ee0a579c312e48f94c82a853bbbe08087525f2a003eff91e83a1c55f523793f48f779d66886ea6c3f362f9286901eb9937df3f90d6bab7fb89348b098dac49153de6f15bb1592b20d52cf701b242dadcef30b800e950895b441dcaac722ee2ea4f7b91f1062064fa694021c19545a80a746ba94087b344d02cdf1fb4e647784e21e03a28132953959b06ef8c035322d8179c65a0225434ffe9bc1688cc45b153da0070095ef9180755db8612c9c7355ec9e3f4c01103f91f89644a31caf6501e0d4c20bd767677a5fef60422556e1668750267200f360e5f5882523ece45fcbca6d72a6f0893b172cf3468fddc836cc2ff838500d97f6d8ed9ecf19b5b24d24a154d712828bb63c6c47b2214e069edf5c2f64790a2383ebd87eab6e35bcca747612a4cab943fc5ae18a9c040616d47b97dc2064030c10d9577c2bc62102cbef4fdda6d2983bca3585213cd333c243b64054c5db0174d4ee0133fdee7c0820bc49fef926fddf96caecdd71da8a79c3edc56144630b7ff4fcfde5b987bef0129e3aee0ccd94de8bf66d73f037cab7dec8345f20a604d8e9a0a7e58d095a9062e600c78d7e821e9347da3ad2358df2e584ed026006067556f2ca898f28e5e39352938e2b909333fb610386ad6cbae9153a0b33b6590ebec39f31ea57c8a69bf09ae4c39c8f243994a4c7c396f967ea4d37d43734fb04ace9ebda5806fa58712ef7dc849312a3ce2bbd7a67dde37d3bd76386c08f37095fdc95880e4317ae02b37b9d863ee31579a71c52db6601f05ed6bd8252aa1a063ada5b920887b4167c514a6cf1292d65d3481ec5733b5dfcad6c9da14f75510b48b077436736add37cd9091f07b3d537fe7378b7ee245c49023b3a690904f20c061606fb2da49b03d7f613fe9071539e25d2a4fae930c7215e6745cf05b03c0ff988b3a8a448f66618b363ea6b584784dccaec6852870686235581eb722c6109d9fd9344007e295e26c016a574759b8bd279b826aef57eea2f3787b7d93410091fb71dfd3a14441f36978815e86ebce7f93e99a6541b2f6f95d49b2bb5720901463fdb377816d69e633ba8ae96f72c7c28e378f954c7bff8ea05245a121f540918f19b8422abb1faf32a7eeae2180b8e9bbbd0e9fa4ccad97b49a528b45cbe08cc408f3d481739c1f704f3bd4d3bf1d4c976fe674fbad75e7105cd5bfcdbb70004b1357c524485696f5aa8d7e283a9660eb9670e499bb922b072f14bed53d90218e03edeec21ea7dd228c8c78dd5a8f9c7b7f5910826e373dc575d065a6e5d0f7483ee99dceab5503aaac8d038a1f0ca3ebba40b521008044921b5632cff910b2652656e6cbc3fcf0702b3f4a44e185acf305b4f8438ee0181822ba5b3c7e20635c0d2f6871510e69afb53efc5ac1783051b922aafaf8d92676f4dfcbf4b430fd6eed8cd92870d1ff5e258a5856ddde5f373e9f6198258b2387e154ea032f40c3f81228f10dc42d54e25a5203874e45fff48063e9da116be56220a6b4d7434014d204f5c8037ba554be07be63f0ead01c39c27852881aaed539092a50fb50d05fe83a01f69fd4680fc8d16c6f8ef218402bd6f8022da2d55ed1ee24b40c4db09 +generate_ring_signature ea47a9ecdba0ebc3380b43c3366b0374592d940a852404b9e2624aaee5d65ed1 8b08dec58f211f6a4f859a349150fd6c656d15dc12b337798053576580d67eb4 119 86fc9e3db3ac9860def554cb95332dbd4a9f123bf394b4384ef71852efd270cf 90d746008968bc2975a3818d614c852f00a2484d458d53d139b1e22540ac85f9 685bf27e737a46c9a21f7ea961b13f7bc3ae8b1cf6c019d97f57e865d55e7c5e 864b8c6a74b5ef7f3e7c5e715091c8010f2774bbe8fec67410ba379c1a74f47a 8240c39e8acdcd9701a924196d61aab8c7ecc3d691c07a4d229c9b58891a0e34 a6393c9387d5164fbecd5d30e7f324d57893d42c18fb02dd94495c6a118f9b19 931c62204b3e135bc1760a9f6fcf297cc2dee2ae973a51eeaa6e940388bbb750 37f6372267a275c4e101695cbc67dad405316a18ff8863ff2bc32fbe25e08f8c 291e790733e39e83c429476a1cb1447d750cc7a5bd978c89fc589ad856642ac7 3a61152c70336fbaae07153cfd622fd739f3eb972eaac07aebf78208c6558bfa d030afe4b6b7af38ac55813f033ede18047f54992f4f1dc5183c1212135fcb71 f8c882a93a89ffa6fbfd916b150d9777b02535c5c4d17512874187e496a5fd36 70ec928645fc0ede59b5a88d88dbaf681f7f4c7c5802fb5cbde7e01e5ec3131b 4121fd4e70ff7ca2257bcaaaf3c52ed69792553ce48eb77242d350a8cd10ca9c fef151c4634a284338c15cabfb16ed19fe8aba99218c2734a87c62966e7450f6 6f69a839c8784a22dc1cdea4a71e5b7225ca67a6ca194f5f650cee3ab4aad608 2b79ce183c6cdd13791bcb397f57af3d7105156e93c47c7e75205ce4b199d3d6 1715445f06b3e6202f42faaa89fc94e7ab1e9c47e2be820e4b852e2841333375 960df3f13af0e3545856da20f96f68a2b122b56d5e6b455915d4fa39fe6ffef1 bdea53ec30b07b24f09463f72d700961cde8dd195b2bd34b8b33c27cd38ead4f f52cd65f32597d9f845c25a7d897451770ce93272c9285dd23e9cbae0b7c45a1 755c6cad77aa408d306740dc50f2aeb860353b9c32c9163d2f371cc9b1966ea4 dc4323500d28e525c22fdee46577f6631c36676b6cc24d154902c03d8cdc427b 8eaa9a16bdf1600dbacc5c6522c19ad13ce8b9da339cb86568b073eef5df4d7d 3d9fbc428d0fe820aacdf12e168f3eee21f7b3ef8163e3f71bda362f4339e8e2 e655432684d336cf106bfff61fb173a89280c2c853240f345f1f4acba37c73fa b295c6153d5b0c84ff5f2978d202b46b95461b1f0850005668ddc15e38757853 e48bbe6f531deeffb3c5b749a2ed5f2fccdcab70bf630218b608ff2f95adceeb 25d0d819050b0936d2b01eb4fbd260b5c4d963f1f1467eaeebdf072c22141d53 a586431bb36faec87220dd84014c7d6a4ed542bcfa0fe874e0b37fb747ab2f57 698b3efc3f1f585ff4e57c5ac968233f0006c4849db8c0134ff928fc34212a69 b048a160c5ae874631c3684a11534530560a8d50eaeb542b0fdd5fc555ba8cca 7abc324ea3761eedfbefb079a16809f9518a37141fb3c1e4cba246ea8e33fd4f d2b45a59bfb5f1d18cfaab4d29eb966788a97031479b226bbc1fa8b8124ecda8 e87eebe01a44322e356cba634e670cd777a9df99da4be3ab467801b4d87a42d9 3e057fa798e08fb239c91e8fca0cdca169779d6aaa3fa7f1e4c39e85b58d0749 5ffd665be0df48773453cdf0c20daf7e7d96d1c1282bfc919af7cee54795c544 31b4ea771f1170cb7b63dcb7429f8a8bda10eb7e6993550f7e967b69a4ae04bc f1be65bdb252f0bde3f097ed293f21c6a1269a56ca8335e5c492d520fbde3f70 ab312f7485b35701b8d033ede2afc010140fb9b7e89924c1a3696d1883d3255d 93b150feaf64637ab6dc2e9398df9e62ad755ce37bf7d03b555316e7a3a54714 ab87de4dc50fe8c79fbf79c2dd89ed63e3fe3a54c74b90515d514ad3a424e132 1fac432d28a035b023cab0678358781b39ff78d276740e2c8c2cc482c5bad694 edaa12b194bbe9dba7f78df965175ebd6af2efde584f6bec3180fe5da0168260 82ec42b75552cac36253d1793abfdb3fffc1c2669b18d68e3239c0662766c574 3fb1d33a5e6c6a0cdb585eb9ab0b10afa5860d279a36ca738754958805e5bd32 90604096442b8a4dcf9730404787c82c54db8321d96c7df31e8c6185906feb0c 1a37762eb5808dcc2f5632f0fb8956492668acc7bfac690a22b28f5bb4302a38 25a66c8ed980ddda47156479424d5a02be9c1eb935b78ef3d4fb2e362e3dcbf5 982be83bc0ef2f320fab69875a1f63cbc33213bbe824ef0a5d33eb1f7da06c96 b7d60e705199cba12ca82ff8849c8a63d888ca6cd462dd3244506bb25165e559 1b9bdebdb98e5b0e142aac45201b1fddb2f364ac758a8f9b9ac170aa6ce8a721 bdac2ecd85fdc2f7c3bdb617eed1ef31822b7c347152d646226b1d3a0bbaac81 6e5fd7b095e17493c8efec3fb97c0bc316c440667a36bb4d856ba4df0d30d445 5e1becbc15edb49e1b1833cc215fb409bf2c78694634251ecffc3a1930661819 adf29adf2c90da5ab6ecb76f63a8936b7092ffc06f305f88a20a56a6c9e586d4 3a7226c8728afe806043baf150f2125cf1533abe6c20cf46a124de66e3249a0b a09ecb689c2302000cd640514fcda7fbd350f6dd7b656aef7bb68af5cb0aab82 87c4989bb5226cfaf4bd0b4b782f3d2a4485a9d52b6858515b03cfeb2be5d64f 4cd8856b5b3169c01909da1eae147747b7258c82c9b3e0adfadc33a81ca09cd3 602d7687e43b983ec3ff6ab88b4765f4f9852f310c63f6167e827d23baa6f9cc 9f84ae7634f439b01c494e1bf8973ba082ba42b110f61d4b0893186f8aa030f3 b752bead4cde0a1a1a12487c63c5ba1a3667cc67c375b63683f35353ce67b71c c755b74a8074590bb6410ed403f411deacb9c2c966da9415c9b09bdce3492e79 e2603bf08aa8a3769f47ef6ef27d2707eef3add23266c51cfcff992ff6482217 1557275ca3d01530c34048ade9c42f25c2d7dfd2f498249427aa0eea3b192f48 02a3d212f7fda58c6961e231ec0ddad880485329a77c954fce8e732d14fc016f 864c7922b872c1a209f477e923944379f4bd7bbda8c150d2f5220e8149c0dbb4 d419e041a29def5c8b666835c16e32e5661b06229f2b5ac8dbf7d29844f0b7d7 344a261d0510562165c6891ddca4df2dbb23e3cba803bb4750739171050a55f3 2e6770846606e34e80dedc461491c5d734e53783dc5a5e4fd9bd25760fb0ec9b fe964f1bb06fb791d5b315d38cea1deb567ad444dedac5fcb6603a5fbf6a1478 359fedc74f8315bf3ab74854242edfc1b17e2bf370adf8553cadee346e12b532 7baa77226043c11f70ecef1cb15107c8154e5bcfcf2fc61d5872c97a8adf398e c52ee5dada2eebab99ab8a30262067180edae6331033f401c42e320be461975a 31ac880e175959f6e267fcfcecd22955c339909c1d1657d4a153d4f09ccc53ea 6a46733da20a3f0fe9979df94c31d5bc25daaf9f93a6284f702e02395babcc75 5933b0e2d3da6c2bc9c74846b794cdc42f1c7e264b1262b445f4178156119b3f ecc1f458f7044cc4de63304e14c97587611f055fa76d0c6fd2d8f16abf5ac5fe 4d2f565a548b8e432a22e6c871dd5055f2107afc21eac8ed0d9cb5fc6089f003 caff7db7105225cc6fe2c9215d295f26f222a85417740af8ea34ac66322a4f49 92454bc558e3edcaaac3f8aa7eead59d7eb29c81b53669fb67d9b383e512c9ef c120a2298ca81f2782f2459aee04544e6e27cdb1bc8a2da00d0a428e1f8e988f bde4fb225483f1bbf7c2f82c7e59dfdee4eba351ef605f6f6123f6bfeb6c185b 9289bb88b03105eaf17a266bcbbf2e9a9a3b1cf51901567f6e504c0c8b08a503 b8cd633d6f542c9020cc4d238e6c00f53fc2d5cd18e5788a96f016cbff765547 65afc0428cb260219b81dcead3edc978fc6cafda167ab4b32254b1b80fa97516 44efaec9a7fedc18667bed47a1e97af7b0342dc39ce8e3f1b5053747f6fd3a45 0319d7568779dbad042e76b31dc816f38b3365279c15fbb8c7d2758fd0de1750 c7533a07cabe9de76a9760097abf18504566ca51354a7da4bcaf7f457bf803c6 e32ee109a939a69c342ad61534831c44da8e0ff478e7c6b7fe83467d5a5cf90f c769db0d8230eeab2e79424c262229a32cea0bf3ddc9e0ad18757bd6efc37a6e 5923dbbb76d22a06463f25177223f3cdebdecc636abbbd1fa2b166bbfd742ade a51c8459a53d790d1ec93ba154af9fd8ceace060efe03e85bbf94af67c9aab0a 8ae5d9ed8c2a98d3d90cb40d9423633cf011bdb5fd352b887b15341314c8509f 9db76137cc1e684d5d201d39caac542f5293d8afca91f70430a90bb0d05bcc4e 7363680b6c1b6c21d05c3df5638f9c35568710fe10bb3e7984a29f423776e3a3 06683504c66554df895e8d9378d270f0d44366265cc5e6e8a13f3d2f55c79baa 3f046e8c13665f3c2f5f6840ab3b0e96ede650c08f09950efa5a20a5efbc7966 a71e9ede71b6f9645df885f7979aa5cee070551ababef74f99377fd5d68b2ad3 a2bfe14ec6b4afee4c39686508184f87cc8e25d84441eef268e987eb34f24749 b4a13044e341e6399a5cd6b155a87e6ff0b6c3850ee99faa03c06b6574ec52d2 18f51aa9999e7ba572a5757cf6b65124809cf8c670b95fdf50d68749f5558673 1510e0318fb908181d765b578776b034d6359ceb6e8f8e1e1d9e1c926078979f 37c10e3b6d5bbb1733e3e8d617f6d2fa82e86a4887a438d3d769aef03c070d38 53f17c4e470a4a7ec5e4a118a8f00bb0966490e30ff0242f7714946d5b872f9f 858c014ced12708662761fb36083aa29710d1120ee3a7a7312da1af159a3940f 0b14c40925571d6c2c6c32ddbaa8493c9615c138f0e9903ffd01a3872dae199d 3fada795524329c3dfee565ef3012fb56deeef2194b62bad7ce972fc7ee236c9 09cf19203dcd46fa48552d86f85a2dd6379e4e6f8c0ba0aa93f97f5eee3ffbf1 635b762898e2811f49c7c2c2e2ad356cd487fed5af00070c55a10d7ea2ddd295 3e593fd5cfb29eab6c6b676472d029152120fd95e839d97bfcee72acce077bbf 55d7ee53023cdb360111c1c3cb23812dd9edbb011599a0c326631c8ee11bb6db 20856232fc8e31d27fd71da70a6f0600f85dc9a6f35bd0d64ca12db2c9548255 b62437740d717b75cc7375b6e6f0569a11a6b18225714d5e663e5755e7b3b807 d0de16dce9a031761dc476b0a3f4733bfa76eb756bc5897d154e23c56cb12165 cffaaf38a4af5bdf7c5730480482457c3365312da2848f3b36302284378d0f59 d685b74ff37503ff7d02f327dbd17c4f8ac91b53c7d74ba128cd6796421135a5 fe7b3690e67a027610ade20dc02c1e67d23afa664c30902cea0794e6ad4facb1 1b78757da78c61128d33a5c4d925baaab9f3f3590dc8e252d028f9ceb3025200 112 11665e324f9c26708d88d6f9aa7562b1ce54cd144c488f04456446167b852a0b2163834bedec9f32eb449dca8b6489723a1918b40d87b22e9ae4dce10eecc40c1208d2f60494945df52ac15ec9760b7f180dfef9a49dbeefdc92693e8e374f0c1336957ac536600212f79a0b270a298f869587883e37551381fd7b0f436a150d67a3c8084806d836997b64ded9dc7d73972c4be7c7955898dca86aa175a6e50eb4c3fc3c5a1b41baea1fb248229298bf276b8ffb9defbe590db76f597769150d9c5ce73c45d1224c428e64dde9da2f8640759856b4161a5786a9fab3c65fe6046a461b19e7db170b714ec09a91f1656cfc731c2933e4b8f2c369245c97e8090c00928cb6e3ee95ba7ab1b44c494ff74a87ff10fe0f375e62990b5f42549a440323a65c466076e1e95a0381ea2ba6d8611af7034f52042666dd29764055f0e501d649e84623173d42a4b5a7ee3c3794ae6dda54d47a6afe2425565a4ce255c00fb6f44251bce0d5d083ff6cfea3e6122dbc504335c9ee484f238b910bbc1c0e0dd2117c142caf33fc0bbc08bd3f1bf3c1e3a1d87005b6fe8705e25d0fd484bb09e8cfd796ea5caf5705dbe3572b15505d46b33d2cac8be84131d36abef689ff0865acedebe7024e59cc058902bf8aac64fe2624c37c82101469ea9f32f85f2a0d327fc91664ba9c327fac963c6d438ad381f07c3ce2afdf1e8ccefded9b06e3079f990dc7d648559740cd2e295eed8e7a093a0a4d91c8f0680e6ab1764f752a056bc4b08aec5a586ee852f6bb509cf966939450034c909cbf8ea301f1671ab20615c88fa87036f1b74724a8d5970aa99ee1758965760413a41157d14bc3b25702ced86941277241da0d5a43317b40452a384c8416ccd3081ba3a4bce937880c00895c50ce6ab097705bf767aa5dfaec938014c76a8bc364caf08fff1c1b7b570e3549d741afe0a4e257d522f29e15eb08f529b36bf1d9ae11dd5878d14c8ce70a51553dc072ed3d13475935a260306dc261ddaa8a053815a1a9e0879fb986ae022928f46416f8ce2a36d96efdadf0b37bb7b146309c6ad95203d092f08cbce401d0fefb0a56c7299dd831e2ceec14315f67dc8f364949bada3cd5767016d7d30cbe7d2d29f8522c7adb5e455a2a892a8de82428d7415463e602b9443c8fedbc0d431ee3ba18d5c934285a770cce53335765ad8fb9e628f5def6504098d4b28b00c30174831f8bbedf2ca3f407e9418d79c3c4f3224524ba6667b03ae52de89e05cc60f057bcc5231d205dec3e13d827ae9f740aabb33b7b9a5da6ba539567020eeb1d79754f16a9bcea076337223b1e441f2598090e48495856d9aabc0341a40cbaadee1a40088b6a12d2d42c4ec0cef90b2ca2199671de5da0d632574f418005a886ce63952934a241b79867eb02f3bcbe1404d5da5308a5de2a30043d32b3055f8026cf0c9482445d78587add0baef4f4df801b8a655a368c39059d328cb3039aea951e7d28ba14fa535f9512b5602691d09e49b287d05cf4d4b7b43501b10a8fee0bcc04474fe2a79f9250919caf1e5b9b2eda5d06c528c4bb1672690a700d7a185378640c2dbd25806cbe1b7672e43be1e0c967837049acfff981476e900ac85c12ebb8808628165176d7a8bb5f51ab29c64e3336c5449f4a594830142e070ab91dc2262dee20c94bd7014d5805b2f62764433f10bcf304742273ae3a9c020a1f3fcbe3e72a0ad34e0ca4a0b9ee224c773a3f44dfa0489440dd23b5dc54070b3b047f304160be47a6bd8ce0aa20fc2653e73bd6c17a84a2c179e90107180e37fa385728b6bde59220ed07ed1ec543d21a6ee654dd5ac661e10730e18bfc00fcf5bb6f6d19624e32c54aa9f0ddf97513a3721f7cfe7fce302a05054c83c40e4dd2e4e75250b761caf0c2295b008aba28f2d684ada2d7005c520e5005643207816558d93858568eaba6430da13867ff23a1130ed7e76d13050579bee6d9d40e240fd9cd8022324d09f7a5edb819914f4bc34ea8de1940339c8e98508c91ba0643cb6f675572bf277caf485469d8281383aee0c60f71b394ebd587995cbdd90a8f709083fad29207556984c852aa6e72070ba197e4a827fb6ded5f54e27b390057e3818b3e357c261df9555f731343fce276663ed527b5de71a69432db4d1305c41eee9109e505fa6fbc1de8b8403489b9b1e00c11660ec5149cde26a0ae660c8fa749daadfe710998ed65741dc686000a72b8db7c4850b668932cb88035b40bc5ea15f115c03a194496e1f0226c3770b73b6df9958e961a565675cb35e6e70420bae26e18081c645a7e66df13268bbce1f163e83db647c3ffa1afc1bb6ce70c46d478a8abc76f3df8eaf4ca636f2890b6880f1149d0a9eddb5ca5fc40455309b07ebfebe6415b782ef1a7534fb271701be52e1713a174d0997a4288afe58e0f2b23cb4ee0097f92c6f2bf93891f1622a94f992425175b8347b9c394f418bb06ac67ec268be6623e322364e1a4dd479ea4d5a42288764e15b30e40ef7f9008050ec5fa7fb3c77d1932e2f89fc41ec2ac4d1a18a14563cf73e3f3a04820f9b20d6d63478ad152a1163f68a0a6bf63a4629c2afc24aa626495a9ff94303b41d70b62193f784b87409c957fc76dc66b6b45e861da42757c4b7488d603fdc3bf1b0f9bf42aff70b6cd377594f8e0603af186aeee6be8d1fce001fb65790776dadc0c6307f67756723c5a13cde4247e28af99d81e9dac67a5c699ddd1fa8393b509006127d039fd9e4995568f5828150165f99892aecfdb016981b00eb6200b3d7d08d6ac34238ecc5b0fc1e3b71b00d7327f55016d266c576e4c31eaf1b927e54e070e3105988a79723966073ce3361cd27c593af3c0eb4d7285a7a1efec53611e0e2626676818dc5e01a1102ed6d28571316a309e9fb1686e2f13f855e47242ae00fe7a68d6f68eff7277c961d80870c505281fc2e20073144f1eecdc571be72f0895ec0a6b89e3504dda22e2914a019161d784e4324974141ea7f6aed287d34c0522406e9a4d595e9b8ca9a38e3fb3a11ba6a1c8720859f8eb7e927ecd3961bf08556ae96bfac1fc3d70b682838268b4ac9212c544f6881076c6b399f1806ff90604986a2d3d34b119491f5e21bfe0c9ff8a1a4b369bddcdb5e5fac85f41fdb102e21ce8497664140ee1bf6bce9bd3c5f3ac62e767120718607ec3cb2022db8d0a508728e62d9ad577609a4ae88b020373d936bc2e22506493e713894bb7419f0a4bc8ca460979416ac94039f0237cfc43b44223b3f43ab76d2134a6e648dc15018f6810d1e96459ac7fb5fe285b3c33cdf14c3f724b3071070ce4310e63249300e944f891bcb72d992f5a049502018b2fc3537150be2b8bb65985e05641baf40122f13292588fd1b7ba1fe8ee890f8e756a0624985bdefe23c71d19c2cb63d60ad65868733e92b5b2f45cce9f56d570dea4c8c8d1da566ed7f32f44b53b01f60a9fe494f419bb480751bca9d0fc05a311e33821804ec95b693c77885acde213017bdce9e9c125ecc8c613628907b27722ec14f903242bccf55cb2ec9578a43808a418d50fafccbc128bd954fb3f0e0e37aac29d17ceaa201faddb997ecda8020a836c7bdac650ca09d16ecb26c39250123ed40d859424c238e26b81de62041f0f94767bdff0a99865c2f79d24ff4d6e77de004457e2d97075422a2e90f58a8a0cb553f8c044bcbfc7251faf2ee14226ceebb86e4d6933429e7bc6a3d6ea41ff00dc381d78863771c7df00128e92b30da9bf40fa8cae8c2ee1555ded0d2a858100ef9bf5aa23dd34cac63eef2d7a0e44dbf7787ae843ab609aaee021a4821719049477c1f446d5956dbd5c8c3b30eb3aa76e2969207beab56e57200dc086337f0f69273e32db56e94673f136870c5b16a672035cd03821e5ffc5e59491e702b702d52482810d41398e3a7704d28112298dddca08f0c372456ffd8d6111e6bd3d0d6850f58277049bd037f47646c6b2331dddd284f003e0aef6933a5c2a75258f0cc16c971f1564d97d3a39fa5d0b802bf1e71b9fc7c91e05073c3c8083d907380102b18ad94fefb4804b9a768cf5153765a1004fd10e479f2ec14dd8c461656b05183a510a111357cb586da1f14529bdbbda94ee337c2d061c15a9b47598614001f8c49a8cf38e0e8f52ebe3c46ffe7cc6727ea838d0ea45089f4701086d836400c3942694454c8fb70474980e79509f3a62ee0baa46d7f3208aa9a88d1717d20af0201b5c1a0e58433345427a6d75c2ec72137d6a53dbafeb7c1b18501a9cc805534c520e47f31fcb027a683534b6a7f62165f2b35d80cefec010f0d7c406130a66da55d731b14011b7d27fb41a885dd404ec75e3514f8c901a61e1fd02389f08c4b0a733b088ae5fa29c50df7705926d6cf594cf3df42e33ba58ef31babb230be0742ecabb3a7b239126bfe5416e7e6b2fd15ffbece1cf978edbd7497150e60c643272ee14b5d27a5917f5cb003ecc4eb9e92012bb354bf5ebb068ba43ba920628e8adf401129b2ce564c474054863ec3a412113dacd1c4354aa15087a5529065e6122290050597a22a2a7ab909532e1118d32a4407d843a0b22dbf43d9fc0059c0798dec28a1fb6f363eccc1814b8cc262cc4f2c7d4f822cb70b29616653c00e378042b409c6e3441a13093cd2aef7b6eabc34b9bd3016f6cd4e66895b1080af8a743a14f47c0e2069895de0d59149743b0edfb4b25b747b686fc241e5d110c98553b0cd757f52ce15ef51d4daea28559a59596cb2be1c2345854c63e19cc0bc0b4187c3c0042a8f7cd481d4540a06a5dd4e1797dde802f3392c4bedad908029b9528ceb7e5d93527a52cda61dbaa0290a155cfa739cad158b0151a6ded1a0572f7d44027cc4fda669d0245235350e5beff34460c3450b79e9b256c403b6c0269b431acf0e29e9ca1826329e2200b917723fe0eab18d9c842b172f02ec7bf03e07857813a0a1642c5798d45dfccbc68e6d5e43413f4c1a3fef2dec73e3c7107215134d463cce134f8277936a661f388609a9904b4a42d0b6c03f1ae3b52d309883759ef632157a6e417755d4dfa3f40676fd9bcd96da4d8ea64016944f6e101483d7b113934e15e9ed2323879b852bda2d333247059fe8103c2ef64ef17120654f0bee564930795a143800294bfe4934ae44b2d6954337ed7c5a2bb35048f0dcbdfdf7de4c0c1f68d02ff4dafa7932cf64bc127572c5020996462063f89fe0c775725f21dcd25cbefe0bc24ba9a152abcfb579ca939f424592c73cf96945e04fcc50b7a35cb0e3dc32ae26c7e7101b35ecba95e2575c54740f6fdeef900f70e917eca779c716639cbf17dbd8ce4dfc7b7c644163bb6ea6e20d0a9c8d9497e06acad00492ca4cf0aa327070b6058139770b0fc46d3347ecf65b2e14d2ef364069075f263142ca4d610b62400d28ba3f12abe09ea5836b4e6a04dd8d89c2f8a03ce740525372e95861f0ff347fc6dc539a1398c0d007c8c9614817369281e0b0d80268ed97d9d0a62eddb80c4ef886a7ab9d177125c58efd6d39bec3bc96ada0ce1d6403b92f1638e0ce885cd030ed92bd9e5d5bbaf6a8547838f8df0ed2dd90a82178c2917ed441327cd131da740ea6f9f82681eb4c3ae247a0c6c6615c4aa04ed77bdd11a8188a1b665b7a6d18350d604e1b63a3eb3581fb0d273085c72640e8ca93a3c242ff777626e61c98c669943ba607b5cdc65508bd00141f01ab75b01048cb7aeca89907dcef6abe9fa3ffdeb3243a378c2453453b15554bfbcff470d37b327b8d4d1deef20f821855bead227093d71a436d5f449b91393e05659a6029d64f41ec2686dbc1d5e4647563d40f6e4657cb4bb1bfbf7eaf74cd04820bf0f6d8899d6765edfeafadac809a803ebeb6d5df9dbf65ae81e73131aa9b77ad109fcc535965c735f9d419ee788d086a69a3483c8553ea17003c78d76b2bf4d2801d33c2be06b519c6c9f9e711bf03a5290bb45fc9b6d4ee55e9690e3448a8e880db76d214543cba97a1bea661789e6edcb05697d31cc775dc896ad8b0acd1681026b2a55bb66c3a6f5e8e31cd8fe073f0e09f77b138107fd0d94e449b2f210590dcd78b35eae0b6a0bbeaebf90486bd81325bafe6ef28bc734a513bf49c7bbbb0219d292fd586d4e07bd04b07ee7d46e87ec2ead21eb87ce366ee2a3de29a5ff0a84e7ff2d4286a4c492e6ef0ee087e78ada8cd5a2b073abe9abc6880b8b066204192c17d9f321e9541561d14035db559dab605adfa50186ad1507ef607ea3500cb693eaf87835d01fdba9f36f3a3b3947ef4bd1121ed289876e4d0b070d8ba906b81e7b753b7113389083ccb577b7b2feab1057ab9b806f7638f74bc87db9ba08fc4070c4807414abb0b2aaea6513e8cf44ac851de531cec4d85eb78e2e03ed02bbfce91595ac4b083e608cbba0b2786a9f188a2f67407e6c9bcd2e8233250f0e930ad337dceb494a8d89cb40c462d25c510029c5c2268d6d8d9ce744a0e30a014a09210f349e3be25bf1c8a832016515a89cfb549b4a31c4fd5866e2a1b1d000be2082387bf0d8c86c25148086bce0fa155076e1ee9480f34d59c50d71484e0a2d161620fac3b9875f82b1c38fecf6374a027f58a9219575d5329d730194fb020b1e597f9c04087705e6ec0acf89c855a42f138710a93e74110561e4567f940be71a2ac9ae3e170686599d2770fc96545ce0370908bf18c14ce53223a2c19809fca24d7ffacf22426df5bab411b669d6d5406b3eef10ba8293b1b6a6c9c2c005c928a5b37d40dd0a72e6b678d2d1ff9e826d439dff4171aa4ca834663fcc4f0f857eaa329751eef364df7a1ad9a6742fbad433d41196531b4bcc0257e54e6e02860dfa816518f0b91c8249656110c2cf7b525c6497663a97d6e658e71117d109285f4b0881a9f9b27cbf305052befd4a1699fb67bd017251cd7518c88cb7d60e128d58b391dea125fbafd81244b68a8f5593c222c329ce6ab83bdbf12404bb027f9bd5e1ad3894d5091ab55531e27a1da663958f7cc2300c7c473bfd9d633108a60b406baba2fc3d862b4a4de32c0f3439e5af7009d0564451c2abbbbbacb40a2725677eae5fa83bf066cab88503ba40c185a5649ed514c67b293e6ec12b720460e421c5a5213b388945a4ddb211febfc377d489163913d31e2fc96e1680d50621f276c9b0827ee64afc11514b6ee5f01df2ed0a5fb0d625099100876c3c480b1b89d7aa0426bc727de57262b217ccf46c3756f60f78f9bdcc4461e2f93a1405be19c73081525bd1d2ce06653bfd7b3d11c6bee650373deae82987627ca96804c522dacf9a8fb57cbc179f33899143d47896761bb76ab0b9596bba671fdda50c90ae67144f02731903f1b6724a6f82dacc88fb9fe5eb5cc6e97c183906282f0b8389980790ed7b3f406e96398379834a36fe95be044f91296dec79209e476609c5ce5d4a127c6b979fedd80db2fbbba38651afea7b85ba328c9605ce71cbf208e9c0d5026a554aae85d94850ce56069fb446bc1416fe02157109f4cc24bc660c950d96d2a31dc58b08a92e369b98dccbc3eaafa775fda54d9f2e57f7641dbf0874be3ddd2a9bd16c65aa2b5cf5e2831e5790069610c41a52a02e9fe1d60ac9061d0c94fb6dcedc8bc83c1b2a0a07345eb41f3b92d9beae78bc3e1eb0b5a90802fbce25b5b225382043bdae5008ff640656d4eac0ed64ba87faa51cd9f36f5001c1782eb8bd37f8edb072a65ad7bb86f94a90fc42ae7f45591fb06658d84730045bd9771a8cdd8079fb8a51062542b3a26e9156acc2fc6d60ae83b2b92b46870104c4468332c5d3de4e02c64665cd91a858f57e181867bf644e54c70c1badb7008c1ce71cb6f796fa7ed072f5c7ae4b2dc0b50e65bc557d11c093273ca401b0030144974a9439cac709f659d7622b1c87001fa5f4ff6c0b2352e8337051c5a60c04b478057fee16e5d813a79809f256a76c14fea95b1c4fa0c615c377f138e200724a7351a2650c46a4512c092edd21979ce41cc51a75c284c2d30867fa836008f7730698bfb3caed6a704602689da076e8366897282bbc49d7fd81922a406d0236abc9bde0bb2ffc182295aa2d419f57404f520cfbc86439c19f01ba82dc670a105160fcf5cc31206f301b5c6d35246904158a58c516a80ff620750268808b0c8f0082bcfacd63fc00fbffc97c5134ab89c2409dc05c915a3696e25558d583071a01701561b34a2e64fbe91ad080114f3cfdb72d7ec67f24dcdcc439c719f00761e0e262766e067cb0a749e9bf78017891d48c11e33364d979f698d178dccc0643f5326c1f143bba9cea98e35365cda3dfdf216251612278873ff1271ad5db058da0a6fe743099d3d0bca90d90d2c5d10d7aa952aeb2aa641ce4e1e9cdf9be04cac6014f1ae683ebf4143f9d1b420a8413b6f7a42a0e015dd77cfcbb9a60220ac8ad1f5af352c404af6e7e10315d8d9a23d5e2d235bf5e5e7d723b83f4de5501b38b37d79e1df7a5c027df76523eefa6e9fbcb1bd6150d496e52472eb452e304181d23031b1669c97eefff377ca974d249f0193600bda88ddd60d1f9db409b0cf64c667652f78594474f0388a388fe2afcb191cfc9387b714c0c20fd5a24100f934abbc02a62afbb1ba125fa292fbb03713b8dcdc21eb0d6216802f62178910559813f4e01e9938f01cdb227cd88c2e21abc53949254a192a4c797738c151c007914f8c89ddfae692fdbebdca2ff12e731cdc0876edb44b229714eba31c2e50331c08994a6b041acee5f42444874df6432e3cc52f3bc9842bbb64cee1b2c960a9c5a491d5f73a66582ad5f5cdd115695289ced7a1e296f1e3a1f2708358848098529375fecd7df8ff7f3d70a5bda7b85751e786ed4a03b674d012449305f2d07e1a94527446f57bf5c9f112365211d7dcb2d2d58314e2a54a822c8b499ae6d061a1b0295fc7f714428405f4ada366e57b224219de8b4859b7d8b28460aba8a02df9dbf19740ec316777fbd2179aa0a3e57c1f8aaec4ab77cddd49c813653420e3e7f25047ee9aba610b3e8e2e958e5b8f50658b88d2b3a17bc9144ddf12c340b9a4ef4585dd4612f08440b360f3956825eae5eef26cd370999337c4706b7990d30abfea11280a9439b9ae960dfdba433c18839e83862805c92c07305f78fce0d509efdb3a77ce55e90ba00935edbe2b3390327c31d3d34b28efe84372dde570cf0541c7973c38a7ca6d44d20e2bf6991e8d2a71f279abfe9a9ed55a218a0f80847c4b04f7a2e950e70f1d4c85458afd4d9c8b94021df1e2b16e2faecf5b29100ea6b02f25121c0893f3ac5525313cf08a78d46625adb46da7369e26641c3ad0575ec0ee8045dcbdaeef06a03243c181dc9d921ef3f8e0f155e8f7674cf99ed0b2486cc63a75a5ba5e3ae6baee70f30dacee1a7c7e40ba18a96fe7ceebdd59d022f3dbe342c8dcd48518fc32525f235578794666f3ff6afa12a0972ce61a03a0cfe72fefb9c405b785c0a916ce0847b86da93b28d53ae395e4d911a2fe6772c00440cebc658d48825b7d8abba760df9a0ec74dc787ae5b1e24d02fecaa345860568cef79d9bc018425db1c4e8bc46e04d573953e294c9c579d9329b5c480574026a48a3dbccbd85f70e5e44073423f4aa4eb60fbbd47e124896e7b2efd048ad05ccbca10dc26fd590c93f677dc1a04d40c12f63cb8356e452f2045b4bdd65fc030fdef0fdb71ac0a9c1a24c27153e313977ced9138451f8c8178e19c9a6493301a0c52025885cf0df16499c0a69f8c6b1fc58238c8061b3d6802314126e6c7a07f0b091415e5c97a1d1331d1ea4a7095a3cb2232b3182681befa43c04878290031ccae26cea240faaae2ea3e830439d76f026c75e175a9e2507749c6753c7de061233582993d2468796b0e9b533cf1b355a135fab5186f277207f3e01e7d0fb0582d63e4f4cb073eca43e3cfeb8e86b278a86a7a441cc5d7cb94a4c868f5f7c0efb7e59dde1899d712784774392c297abb2bea8632cb9da6228df187bec6a8e050bb57130a1c2f6cafb2f2afdd12af1c217f44477b10c481e81cea11af5ff4f07f107439f86f3708c547dc181bd76db7efe1dbb6e69936478c3be256ad0355805d9c30ff938b90c0e769a685e497ee38c801b56079235efd913604db16c71ab0c87ace55579188577bcd7d149c137cdac997e3c600903410ab0b65d74a46afb0ccb27cf575ab6e1db0e7fe9faae406cbc8e3155dbaa4e48f0b2f26685ceb6fc061e3b395b2c9e82e79f7770759d0f75b5952ba7c8a47446e44c6758921440d4075d3d0c3aecf73cf8d6f178c8d279689b7d4da88d2bf12e9a3d16a607014c84097699c6528017d7f14ae1e8aea8f5157d914c7fec26faefe85aae6dc389ee6d0153b43f913be274f77d3c30fac8047cf75b7ade70ca8d039dff0997f5be241a03f8539fcbbaf8295a110d166c523749073c2d54a792736568b4077a26176e910d070bd14fe9baddbf97fd9ba40c19c0dbcf0e55f3081a7eee75d53c28cbaf7a035acf29a6b7dca0fc2cec688fe826596da01cd24700296721814e3bf0c1884f089f7baa4f55551362498ffc5989c42e28f5a313dfbc34bf21529022ae7473080edeb9fd88690db5b3ba222ce77189fe5bffd3086cbcb4a5e16bee5c1e007fb70f0d8221983a9216f404f5a27a234ccb9842a5c3afaca8497e23c0ff11d0c1fb089d1f832802cdc2c5f58223597f5b95871238b530009d470783b0bc37fa5d1e0c +generate_ring_signature c85b39c033155e35b83945abdc77305a56443c963377f4e110cbd4eb446e48f3 bf51355ba12c7765ec8e01f605a3166e7f0c96828634d456acb09eedd905ea2c 47 6d5d5b9a788290546a7b84581fd753f117c4444fbbc2b967dd4e6740412aa85a 9b18ec9dc8d79d685eeea53041150a242d57c8a395e913e7ed49ad9c7fe0abac e153cbfbf2768eeae2e50711b79596fa62edc5207d53bab3f83186a81ef16997 be12ee92ffde1bc7e5f4a641db19ca03732606435a0c941303d462de7e5a2717 92499f2eef1b630c59a71ed09c0bde92b52efada516c8139be81fa7d931cdecd bae3e8e6ee6b706b706f72b25f209221a1c5a845d3e47250a7194bf3a95150af 4e4dfddb947c81275709b4d387e00474dddbf440a791a3d63524d03e1a29996e 62d4e89f07d18def1a68d0b2f295474bcd548dfd8e6d49a05298867bb2cdfb00 404b949fb2e9c014c3ca350d9adb43b48e0e46d5d135532a31a4b225e03bb8b3 a93af32fd34f70796a5949cc62b94794d02624af3715f6f9b77c3075be62f2ff 9acca5e85680bc79c81f470c4c11f2c0be6ddc3a2bc179675a92b2813f563e9c 193eb4f66077d365cc023d084452ab22d8afc1256f53d77b600c3e4f5c6b3a6f 25449517e1f9601a062137ebc60f9080f5f89c1b2427d17a219bb59e354c8097 5df70c67089acee7581d852c5be14736903942dac5264488167b0b1b19351688 9b51ca75ae40a3b7e1f04c0c4b0ddc7911a4ec83ee7d97d50e4a721d8964fe67 3f98d987a72f0a0909c9eaed2d8ef44b3d5263bf32f57bdd5793456ead67531a 3852134375b9c10f655de583610b0fce34640f0498fdda173b40b5796dccfd5e 771666fe743db4e78b436269f3aa8d2d1b7bc29571ad669ec0fb2e8ad5a2e779 48886a7e53fb92fa8915d6c2a70f3fcdae30232819ac4024d7e2bb6ef2058a91 37aa57be89e582d3d328340b8cb71d5a3c542bd1132bfb06c6ce6bb23bf5349c 23a8577cd1d07c7202b3f62e5b01052fa582ba8c1e61b36db586095b1e387473 6f489feb997d8ea6ac4dc935c1b9568a1ecfe02bf532a9c2e5a4373275e1bf93 b1cef056fdea00c2df4ecac1c258e20436d42d88f5b6e26abbdfb8a88b837b8e 0e234ecf472720073b03bbb049cba933cfca787a23709f08936fb7c95ba20979 2d88dea22019672f03ac3d7cafcec554c1e433cfa4cd51231190861c5c2fa410 dad85f24fc48eb04ab2b9be02052954363387265a3fef589bb7b9ce74b6a6ac0 f04bbccae86597b6ac05f9094f46123d9bf8630192db1ca5f6e19436cdab7978 7131dbc645a446f115cb7f608cd429b69d9299a4374c3813632284e983fac71a 2ee9654dfe60d881b8ed9ea2865ceed14c735eadd0cee720976d14ff99e17ceb 8fae6337e73cc1101fcd965501b4fc505bcf8684dfd4d76650c44f3868da6d03 f92f68359037918ad422c198811119caf42e6b55a124ae441e5a6ab4cbfa01bc 1c0bce320e2fef9df215e30819784fa55482621a7a483ab59a47c3ad01387702 44b907bf68bb4b801b5bc7098289f584a9d96bd2884939c4aea0d55f91a30065 1fcddeea41decdd31dd231566bbd9c2f67c737adabeb67d5405438021098c335 73b36d93190ba0e3f463a2aaff103816149e003249f21eeee03481f1ab6ed0aa 66b0c2c46db4dab89181e95180fd645987587b51a8e703bdfdb1e68eec1c0494 d6bebb73eb2910a4d968c9f1552bc22f9e48e6acbe5f960103026977fdd120fe b1e997c86d53d8302843e63bd8608bc7304d3a943d1f5c1357e25379524cd5e3 80ae849979b6ad7a300a128122b6557124927fb82f9faa354e06a14920dd1077 c99b515760dae4c8fa26ec48d64353ea7759fe4775ea9d1fa10f09c0d9be4c34 36c4d582e670ac995494584f0cce855a05d1164a8bba9e180f6b555c39ace0d5 6696e26e505e564a6381e774ce211c593f14424098f29059dcc97aba4142410c c6c88013782cef112bc361c0e70b08f4b489b6e2218f54edf59df681f85ee1fb a9bebfe567a2c56fe43fcb021dddba1705bd2bfb0471b8e2ac9990cc1872c05d 52f06e567bf5d5acd4a568856a9bb963e90c7f0ea777d83de24447afc84a9ac1 1b151c8350e0376d69fb760f656d8035bd8a4f519b8fd0ccf27a0e8d2a4a81ff f18acdddcfdf01ca9128b92c1c4b226fa361f72f4bea8fe34c22386dae7e2217 00dbc0a21948c9c84d2b21636122c884bc9fd35eb2a572170ca2162a796e3e05 1 c8c4bf597f44e79028c58969031850549840e8568430bc802fe4bd164315b7091b018bf918e7098380b6f50d99e0c3df3e2c1bd132cbebd6dfecf083c12653064cb27c39ad04e691c2d2a8e6e8acee41a6770066d5db3bf5acf2d506b633910b71f4416ba7bfaee3b0a53e5107683bfd3581a3093461c3e0918881bd56d85000ba38fa49ee58473a4e79a9726646b14a6e301dca59f70c8029e03b679a404d0d50f34f2d0a5815aacce5fda52dd9db8bc21c63072d2286433adc512d30cbc406d0ba1e3402eb4c35d6e4b80e60025eb13f72e5aa91f3657a41668927a836350ccd94f09c4059204eb56b5ce602edb210bb52f340a642385c463e6317e0e6840dd16775fbf5cecb5916b25a0b8fe06a4c27765e905843a8dd89f73afef2bdd10fae4144202a11e0666241e65aa7d701252ecedd4f42682fdf381990833cd62c0bb2bcf2b477bca6030f6454ff6b1faf1ff1fdcc4b27ed4105706c4182b743d70507e8ba2a747db11e6bf9dd8faad0ee27d0e664031fc99e2db89846bc409b4b009e0f38fc3a6a5bc2beccacf0729967997331d37e64f3442b58e1e23490449306d49c52ccb97acf8084ae683a8402df891431c5b6d3c48524086c08b909998a0d86d3d3ca23ac8e89bd616fe594602cd871a94e7e032b79ec5533215c8e79f50700de8f3a43f160e22dfa6d5aed49544cad2a2c771cf7e6cefe9f60615b8adf03143a4a4b16d9680cff746fce52fd6040fd01e87e9245a9da351e8394b3e85f0ff869e7df19d69ba079ba673c735d1a4854787e2e8f87bfff5789b33c0c8dc30baf3f8ef10534aea0dc8d0ed4325f40201f6ab1769e6539aa8acf14c7a6bccc073bbc036e0a255ab2edf5da2d3d446d23afcb0984d508bb188693ec5fa9485e00b60c483b56aa802f892b2e720b96b9f5621d7e8bc8add2199b6dc884bb7ffe0e5124feaba49b42e789eeb462d23fe1c49dba5f205cd474bcea999c19b5c2470485b1703ea3290efea59b413a881cb3c6e80bb6300bb1d4e670e2251efa883600b8312d7a23908faa9a3aec7d23fb73f7eaa0c3ce460a6ba814a1048a5e069f09cabd917d73fe656ec78922b6f2ec37e40f0cc9f9ddeaa85555ecfcd1e317c70a831a721c000a2a64358aedbab2a352f7a432a8ff084fcdd8c9ead8ac667b570ade21aae1b60acf9c428888530f5a14464395ae29817e309c6d40d2660e7ceb07622042925743203bfc54464c2593426850de5f288ce79c8e139777c2c38e5700fb7f0d53ca7290221eb162b4db7789b2994c49ef3e7320725393cde6f15a470a672aba575332216c9525263da28c4a24a83dd4f8a2c884ab63db6bd26d75d60aad37fd9906b3098441bae98ad4232ae9fd45e3c90ef594f6881473f2ffb3640b9d145eb010b9ebf55198b1a72264dc951f3d97f40e99e7619ee212126f7ffc05fffc70be8be858a865293efa697d4a681df3b80091f1ffa4771fae63e1fcf30c9dba5c5986610f357f3bbb7cfdccb67b3c54e5689cb5793230c53cf6106af8017830b21a661708bed697fd8913d465dbae4d3e62e460c4743720c23dcb66e50d3deaed26e9c305a03eaf8d743d50270ac6ee7967c7933b0e061afd1b7b99d50608c06ac83dffcb3ebc06bfa17f9e8f4e80517fd24bfc706b916c601f1c6c9d07aae6194f7097474905b36365c5a72ae8456e7800a4cd2ddd267edaf65d9c240523922f9ebd8b4fe636e6a74584fe0c0a12c7fe793423bccbeae818b83586f60fd16185066c63a98d9e4556c1381d1f9933af4f1036f965cb6c088e50d23ddc083b4f7f6139ff4f662f51e74762aca266343692d04b31d61eef50949a86d7840a1999df3e4d8d68541f4d17be2742ce7b93f4171a129fd9b80104aa353b0fe40d4814a5e6af813e7bd32788912df28295347c569088985f7c48b267a60b4f350696f03c1f11ed8167f1124a5d3d4729a5317927fdd179b0ec7a4e394aff609a0aaa14d45033867673712bdd0127fab2fad68d53a7d6f25af1290aa8e3d74ba7097f8df6f973df06ac48094db52115fcda333374b47963d24a371cc82ae2329801ffca4aad29326c6294762d4b6a459e85e55e7dacd14b983c974bb321cf3ec50241504a04c322fad289f81bc78c51022d3254743f7311ae1b8c68a74e2e96d50cc7cc6da886bf2405dd5b01349a5c76a4779580f1e7fc3624116053b7ea08a30b48294e8950961a3e0cbf676e85a4561977a0c04a3d4bd2a19152e7651723cd0f5cd57d6d8b7049936b2184274bf07b54d72fed5a4fb76e5ae1ea2a3ba82853097af67e91d87d7c7355e322f6c71b6af791b124218b12e07c7f4669704646b5044eb069717c8ac7ac859d57e99f6c15ac2ccf7c91ce7e5585cc277e58b015d906ff0795b160d1852fe71977a62edeb7354cbd85b5d51921b90b751721db4520094403cf4252e20b3872f08d68efafe97f5a9f33209cee753e0521b2c0ce8e3300fdc37f01d0ffc222ef5528426d3efa05bdd8baa174b254a13eaad1fb9af7520f8caf932e65687a25546bd5404725079244ed78af2bd15f24721b9fc1668cf2091fdb0ed0464e426472ffd1d5ca3ecb7beb01142a399d77f9a87d6c7b5c1e080f30510fad457d3c5bcaed5ed6d95f3d59eefa9459bfd5688efbe444b490ba25062d5bd4b8becbbc9816d6517cac7f6fc00e1558e2d8cfcc55695293a38849af0686335147fcfe9d4e3373ddb6a3ff5f205cd9d691d77ff1310a76f649be25550fb6e1a9d335fd3296e560bc70f2494c70adf006db442726677f4ec0cff17bb90f05809b21e81e578d945075ec36169ddcdacdf10d7b4bc22bcf1973f67db082095fec19bbb76ff2d3cb8d7ba050653ef0c2318df4078e6fea29c3ec25737fa20578984841db0b7edbe4c258865ffa6dbf8b29bd121b096bd22683c9f16785c00508b615f04902b95e7b0a75d9d09d2e6c5a1b11c45a60de6ad7b821fb7ea3c7018301fb374300212b96fb8f5be959a1ce48ea94f15e23a22d6475e3a2b3fa980ba5456a6b9407f727506ea0c863960b80b6a8e0d3888e4829b16f8e70766a680a4cf92a8bb73f22b8f87d0b269c254717f43b51966542350f02858fd73ef94e01d4b4cffd3e8258ebecabccae18879b51f491284f9593db20b1e62051b649e808320625a0f926867a894f05a484a05c35334e6fd074381474943b8651b5ae4e03652d6851a93222b5c166d66e17546f92bccbf0f7c3b1ac10431dcaaff52772074fed8278dfcfc38983485b959228eb3a7d11a035f2da1e6d52b0451e4b945002b22bda436e1c7e4f43c5bd36218051640a9f6e31a7e370d907d82a57d2442607fa70638549a7ef7c0f279e9154747ec13cd563edb8254d7608f4764b2642f50711494fe1bcd6ddeeae5f03b6374614681cca6995a61d601c5ef724d1cb4d2600e6ba1b20f5ec8de0744b77db63a9bd7189c97ae4c1f3f2e88d7786e23af80703b753ca1864d08a92e1efb30390a40d0a0c64e4601c449667dbbfe2af0ca59d00d858641ea5c43e3bb8e23d9284d8c9b30ee6baa5559f301c9e9a6c855abd140a3ad7352beb7cb266748eca643e2ae206230438e64214e7d8b26d71f435f7ee0b375bde33af12b63695522dd142849c3cb58066d7a44194bacbc8fe63d2ca7902676c0700a872e116564b2a74d24b5280ddb5bcf456834b8d4a1a0e2d829e170d338862681584de17d08b53768a2856ca3d3c4f3f429f92da951a69147761cd0f5b7f0b7dc1d68c473bcd8d3839ae58866f7ebec4d09412721738a6a05bd2c4086948c6dcfd890a0e3535f571f320550b03437d81d4d5e2628dbc01194ac2320d4ea9ec3dfa6075dcb0d86656574984f16fd61b345248b5b44e441880388e040f4c0db69192a29842090564bbbb7d514723567d9cccd2018b8fccc3be527c3f0c41ae65466cc906a3279a1b208a9d1da24822facb10ebab58cbdb4c8d4e9bd20b1bea1a1d99a62bb70a844f15df35d88a5babbd0c677b25e25b952344883fd60cc7da9cb793447c174db513f6adabe3a9f41adcf497917d6fa4e53ab23bc4090e32aa1348d35728f27982ddc332c47daa7e54e0deacb734a1de2cfb21a54be905d95222ed5c95077153383e65dc47b354af017b3096514223f0383c96185c4e009a7929549a96fa979cff0c4ffa4774a698aef7d00c382b695591c3afb8cc4e081ab3bb3fb3f97a4532e94dfc3c0de63da24b75e453e99199723cf2ddd8e70602 +generate_ring_signature 3d73fd6b98be51434e67ad8a5b93aa257e52e6aa0dce3ab63ea624d8638684ba 1165b0d22b4a9e64330c374867b6b3341d85570e4d1fde1bb01c7bf3d35e0822 118 b9c8444d765a73e3e40091c396d36f5af240fcb1f7ac374582bf645d6f459fb3 9bffcbff4b78c16d0368037a5c69591b4176e680aea48b7bad8e95b9a032407d 0b1db6568c4d63697ebe1e5bf141538ebbf96c4e901df405511bf34fd4929ffa 0a07f39adf9fe9d8b144aa7b1cef7f42f77fc83166f028393b757078b40cd92a 2f111102f7ebd83513764ba78679ceb87420069167a1fea02f6f2a3d8f26453c 039ea80defa53510b9f7c8b8c6e4d67641fab11fefa91e18453a460effd05cf6 969db322facadea4aeeede2198a01115f081136ebd67865f9039365c9fc5c5a7 3c715de126999d9cc57c51bf3bbd23b79e949131caa2ba862e98ed2ebf8954d5 45971c7ad3a87ca90bd83635f274173a770ad3f614ce79a929d659da7b502ce0 4b98d9f0df771e9b84a53612a0bf08a1a3307c310fc82f866c3a741ff256ee1a d1bc0249ba5825d36fd7f07d0fcb68ec3ea0f5eb3695dc1372a160293bb16d1c e8adfc3d50fdea4e1ac00957c95309dfb1ba109c9efb9dcba55ea2562ec171fa 9f4991f7738143614a0faa51b0e5f9b086a1d272d5ea0286fab11918b5be72fa 86de737fdd64a29a24c323caa970559fe0b6a488025c1426851ace81af4ba2f7 e234e99b5686c31437adb2113b09baf1c54814b52eb621191eebce4003f3fe05 59d88e2f265aa23a26813e6b9c758e7203794377bbc826ec6cd81e14d7ea74c6 b7a2c67438436e204b5c038ca6e3e88b39a66f9d21a1535d3b08a46ed8089903 f4cb240ae6b183c71fa448f481ebf2f7c1b6b353bf69351c64582bc4cd79a6ea 9373939dd5b543017603ef203bd295f75dcfaa59189405bcf476705aa958f61d 1d6581eff3f8a3c5fa89cbdb28d7f0f316284e7992820888a8d1f2b24a34c11c 5fefa9cee2e5fdd15c8ff3d041d237d16c21ec3f08deb19d2676725d175c132e de7a334db0a7ea43f3f26dd78562f40ecd485a80dfded5c2cf90fc065cd7d219 90d0cda73ffb017e875f3cb66b25a9db7426dcfc2cf87a5c7d1f20dc12665267 b1055d0672a81ea091e7dafbd379bf28cd57e98abcff587efbc2ae9c8f6c370c c133717d4ca98193ccdbd3253f9f94ffe4ce5ab826c203249d7c4046000752fd 17e4d82f3200ab8ca87ad037500a2d31c85fd128110923ff397e73d03890adb3 af084e65276d4f89f8aec250c266191aed45964f80902ad6dd3689f0f48a6a06 01c554c234b4919e215bdbf0426eea36d8e0fefc1b9f09c8523c7dbf31efa453 d5a03dabe36bf69253d883326a45f12c9c02909c6755815347fcbe4675d2dd69 004e63e24c2024f7c84f2c35169a486169522e2c0ced6833a45a801aa9983976 8a44e17e10c8bfb70a1f6655e20150ae10ac9d8cce5c107306884b4aeb2078f1 aae053d01b98710de5e383938d2ce50aa820784f9dc26b2c46cc49604ac5e0f0 0aa874bd8f6968dbd1174f9c7a4c619e09959082ffaa29f34836888583860c9d fe279c867c70a0e0773ce9d35094a7a3fad86071599226d9c7b1c0798c6c4889 09882bfae1e1610373e6c50fc81fd3c41ab07a6def53c6abc2b4f22009a5f264 93521270e0872bea75ace5424790aef4ed9255711f1add6c32e41d6ed40ef737 371dc4979d91bb82ecfb099f534adf15ea2f6bd19f5f76b398f16de34bcf8a66 8cf2b4a23bf63b97f31b223a9e22aa397ee8a49f5a5b56be7a13b160694fe6ec c03461668869010b6d492042d5da78b220d24bd5fe3a677b0915847ef8935ebf 9a135b997ff2e2a4bc5abc462df0bac8be7f5ba62030bd38d9446b59ead4bb96 a0bac28936b401a5c47ec6f0b0bb3206068c4d106f060ba9b448db7c987253d5 b3caa1722b6d498fc289e50dd98c663124106593a30d44442f77ee07942c21f8 334273dfce160f58177266711f895b55083071d13e1e0c145f8b9da856c7e196 87fc0e12a5025bc570569fce4b9cdfaea11b1f3d75a64466bd1f7c6b905ab1b7 4c5f46646b5d038c968da5f320cf33ec7262104ba031de1877e511dd9602464d 0017de544da77b72bf56b4531512b30b89c40866a357a7b9fdd6097387cb404c 8e52e31c15fbc133b39cac8924d84b47830a1d5c3595b42d6401a3b3c2a9f470 19f55b78ce88ae49ca731b35f644fe0aa233257892180a25a4f75a5156a4a018 75880606c54b186ce1da4ac2d6056084e874241fc034fedc4b1d66ec79f504e2 f0a7d47dc30498df6bd2045b0e2cceffad4350731c1d90f231607196ebe2e296 8299eb8d16918e81de4125d3f7f7138d3738a8e248e07b9f4f2c3204f39ff771 d2a99e4631b27a3ab9242243318bce8e1fa590bae1daa1582d13650bbf161c86 903e5f2dbbac43eb44f5b98bf49117402b9ebca4e90087dd8dca5ead8d72b1e2 3d5f843628e37e563ee432b2b0bb2bbf527029a6bb19036a2c9db5577a25af7a aa9764cb2e5cc7365bd3bd1b1fad5a1d7f46accf7aa0200a22d4d381cab37253 e87041ddaf57ffc5699a6a72f3426819408f79de89e5bd7758b4b98b4d45d65c 46ef4416bcf0ee3748da985dd091f7873f1655ffe23feecc7568345238d6850b 3a202a14f60c81fbbc311dd949e8fb049c3f051dddc88891513027848864a795 31d5fbd25b0083bcabd9100a9a9993d5f6af025b32ed238a168da83e2d91fe6b 32c99e1885a121ac0bad1374832f34bc4294d50126ef60449e3765826847f131 5f2989966076b65fc99eb75196658654f0d244d3354e6dab38a2564fca0b94bb d855a8c00ad60d57fc065e4981937106d62780ae19311578b602eb279208cdf4 ecc7090e2f611e1ac9bd2e789805cd24986f74514ef5ebfd8d1516b2218056fe 14768c35b978e22205a4f235b5d28690e321bab0bf20f9617a654f4f543d28bf 1215434ff45e9870d42ce8d9344981950a1e3e771d28cf13dbbe6d6dfbfc87da 96c087ddff45afecab6750729d99ea721fe8c0ec7671c14cd36fc8c032098897 02b92958a9a42447940c24dbb53dcdada32f437b40ab93a8d1d46c1009e0bd71 083b083d3fd4dbe01f55b780d33d1cea8604d6a040ad9c73b19ccddba36e7d2b 23285f34f37840b99ab65f853fcbfa80f5bd71056c6b99b9a7b84e4b48db505d c832b106e59b86834b8d43acb050790fa197995286ce40ac506efae7bedfae18 537ce92c6ed3a0ecaa218ea7e4deda826f9806885f41af207937ccbb5239c1c6 a99d54f85cfbe40aa4eba309fc716832c976f0daef586750d23a9eb7cf153a2f 890c57bc81bc342c95403eac94b02ad1a92f550c603d419ecb0d1125c2ef71bc 11f028a2ba028df1f3de7850814d87ddc1afbd2e7ba5bd54cf884e14a072f5cb e983fa22e844687d59be7e979579c2666680621692c00a24800eefb73f0dd16f 62a6986ac01c52e41871d79b0e87c00f367c62f39be32e9ede2d9b04d2a1afd7 ec81b15a1d1e99e25dfa31f9d1554894c2be6aaea73be518cd72c632aa776a3f 9b33fa419cdb09b32084ddcd64a640afa1ec673602623da59ef1e3682c1b879d 21180c602be48bbf4b5cb2e109a39ec976e630c911b287b3633451fd6e3bc150 0ed76d71d72c31ef8b984124600c70a4aa9d2769e1c6a07866a3ac051f6dfe84 8b528bf5e9a23be4f60b37790a5485f803ad0679351c77624ac48dacfbe2efe2 69c48ea66331d0e7bb6d1a38091e5e3ea9d191f8e42a20d20d2e38142d15aeee 147b8ef0530fd8a72cc129fc8dc8d5ac55c9474133eca1812b90df6103261168 5e5a33437f5ab85c241eefe404e25b8631540a26b09ef0e218a26883254ba885 4f547735e27c2949fb6c173eb6d1a52cba280e1c8483b94d4855209e4ad35275 343367ab48990050d59b6a604f1f8600285c791fb54738bab8a3dacf336ed3a2 e41b5494013ad3d43929810dbae864a3d499589223c92ce714530bbd24edba43 7a5250af29a78ca73fd842c6450c3fb4088249cb8833419023d029854adb72a4 76c91e6dd0464676ca5dcdfea1530b3096b9fbd231242a93eb3c95b8f426effc 6f629806a387d223e818460250fa9a89f447f691c4fe8150c2f7272f1ea8cf40 0875b99cda98a18e542187fcc577440090f78ab2482d4d080d196fdd4c00e91e ab8d738719b2c2d25195d3ab669cf434c64aa913a05ab267b852d73c1c3dbadc b4880296efa9823703b51807a50d033dce25eac94a5089e39b8eaa4a59d2cdfb 163c923888b3719247921470439c9e3b6f909a52ed088e12b4dfc27565f1e389 cb2e657e490056e54cea6b20f82d65bb363fdc9148bb3fb3c1bed69eaa797dfe dd828b68f431d544c44ed1e3b5146184f456c4af7348ff05d80bbcc314a7c38b e6e47bbeab9e3e371857d7dc088cf65c739800a037e5fa79573b0c0dda265050 920e7b71fb0a47abad3eef8d0beba36d57bdda21d16455460b45462a11531db7 852dd1a0871c84fcce43d305fe53c9af8fe79bc382c6ab882e85be91f01f19bb 977273b55c5fcdd3199bfce1c23b9134823099d08b4ae556d994fb321e2b9868 106fec23d0d66fcde1ef74b6998af342c9bebac1a7b9dad24c2acc54f50d5343 286d58e693d606a5841cf90c4018cfb1e81328b7566ee342d76a3885bb20ff11 e35356f020f755a1072152a72c3cb6e5c1cf5d03d4b8e50472796d2bcf7ff3cc 63c4c94dacdb138f5adb99239c96bd4f5328f060b6d7be5e3159567d92876e14 6b8605450eed11d05e88d931f9a98b3953498070504ac6ee36295244384d54ac 729461b5e6692e56b6e350706b09f52663c5a1c9f7042d2b6d35e7574f43eb24 e3b98ac12b00e56622a7c2d00dcfb8d75ae520fd40eec1b96433a306cbb0028b aa9be37fa9fffa516f9138b3c616684e9413fdff788622a1a8fa32a85c35a268 2c2729f7a8fb2cfa060f650b0cbfea3ec2f624d561dab83803e01b37160c5dd4 d36653de81dd57a3b1d6668589c0c423e129802776484ae9d0b3bf7787e5bd69 7cb6c45e7fc9ea5e2d88d0a21815a36c486cbabe4d2a673978cc224b5970eb54 8181f1b9018def024b77e945ad21a66f05dd5361110ead4968e3bc906d4373d1 e3e30ae37637a42bb6e598124c2d4d33ab6653e2d1764379f4c37e7069e19128 d36e1ad3ff86fd2e24479ef5eb4f41b0d382bd159044e199b8d1e6e871784206 f0655ec8a477e4bf9b341c6253ff7e50721b23c20bb735d61b67db2d34e16f8b 8eda6b7368b6df33b979656261743c61edcb068b1b8bd97a1930858d04b5b4a1 a7eadfc755ea673aa74d7d66290e58eabd36ce23c3cfe6f3b6d5e5a98127513b ffa64042b6de3683f41d527b0804cf836c918010db860b70fe4345b071c92cc4 d5093118e570a10549ba28d1811ff1b2b7859cefb6e97d5f8f8740e68c7c6601 60 55096774f5d8ee9e7c2499bdbea9ab2d74722f13823a9684945174d113273900a36bad1b7ec0e75b203b255193e2239e81ab20d8eb666314368593c96b50b0021271790e6b4cc2a43bfdc7391bbfc7f6129d81512a7a7bdd3fc8592ddd07bc039834c0550c9fa8c6148fd553840f0c8559bcef5f193a3ae8e9b85675b030d20ab9656fccfc40865884f00dee4d39c7e8fd1cc0fc264928dc3e1be0ce2e78650eaf211f9bcc054d42f99f59b51fa695bf102643fcfe881e38435c6eebba4bcf0a600a951578d608053ad8321450f7987fb154d2fad542cbc8012218f6344f4a0907285e338e99fd44339e19180a1d9db4b7797edea57d854cf37c47999a037f0935798d1b2b80b9e0bd7ea6c0697a6b40bf6e7d9867d470257f8558840e7a94050018238f4d77643e10ad254e2181146c70cc571548bb9063f194482d3675e00806358916a4c7163425b1436ecb1088862e0781a85ec2d0c90168b248398ff9016ae7548d8bcea2dca8846d8025e12602c394c16e854717b726b089b958523d0c8953e690c3e0aeb2183d173ccbc25da8ca9e03cd1a1ca48538c48ed5a96d8909a74138723e39daa7421d6ca6560f50774c48109050116506ff81b3000b3d6f0ee7efcbf1afe6a93258915cf7f3cdf0549962ea253978df104266bb07a1ab0600b8c9899ddf8d03b0b4a0af7b4701e2e1aedaa4a0f88ab1bb1f028fef867a4702304c6d2a8fac07c723aff04a69249be5a01626fd441ef30c306d500688896a0e5550ba5f3886259018ead4a18b9925b5f5bb174a5ec13aed90949a22dc98e0091e7df44e3165376a151a8c5e5dd4fa44ce38dc8ca8e00e0668c8aa95d8f67107290e9c08ed4cc37d5f46decfb4fcdb165eb966decf54b75cbce926ec115dd80f603eb1a2705bad8341292a5895a4ab2e4db58fa6125cdd97f73fa925e099b002067a2f450536c1a091863c95152f32806607195d7b2fa1e4f88c2aed31eeda0b4d518e0eac11a19e3ec6645f316897459125ac0464121f774519f153bf5c99068f01f8f970e6b09b43362f2ed2d876930ed96ff55b040d897866c1d8bd079a036a9b935bd13f0798cc8b9c67295416b83c387b360cfdafb40946cb17c93d370b01e048304ed94707b7f17997d94f95a0a869dfde069a4bd0b32ff941e59b490d818ab4dcf6d15c181303c3056feea6b50b796d61e3562a3adc4d06ca4931d10a12606573d938ad9f1f2c7d64769a7f99aff721f49d2e2d80ad95d10e35461608fb1713ce5efafa2be0eb39b36328b3434315b9ea96cc44a5cf79c27493247a02cea97f7d7438f3cb27c23baa1b2ad9f60769e2cb7dd7415a51bb4ae93e55c508f1d053d8922508fa513f9a4330671245397b07f8b30d8d36eccb8e2d982754046b28addefb45694b2af0927fcc7b47e0fa22c79528d1405aa0b72a51ae2ec3017e2a5ddbd4b680a36783ff52ebcd7124090e0397f2be19a2a69027dd82f418092fecda7070090e71e23d7d9978a673f222479bb831a9ff1b60a5c66210927706255ae01690e6e816a790c21e7389c7952f218da1d8522ad81abf5abe8eae6104e708b5543c4de71ed0dcbb2596ac7087bf2b1840156e7ee0a81707d2779d5b0a7a92176036050443e017a96197ba03104d81d7b8b96e5ec54dc5c36c434515006a96dda5ae6adf4bc9f517b569b1807356d2e9bd8afcb6c5a263f3ef625e7b0377fbebe2513cf6230f01b7ab35baef12c1a55909660d76a002ea0200e8d74302c1182c4073a665ca8d1fa26d667b77a51bba38ca65caa49ee4835218f12ba905111779150255a4273ceafd57630c1afbc908d451a1f636e05b0ec4079ab841085d103bb2c9ea8467b4bc0c85333ee0cd494590dc8fe554147ffac2fb38bbfc061d260ca6f2141954dc1c19cf8fe5f0710d521fdc709d0e5d33cf5721e1cfee0d3628f063b0cb37ab769d3b4ceeb0d9678a4546009bbb69fcd248c34f4b2c070148a73590c7b19a7b9537e5282215d101963522c5cd3f0c3b62a456911ffd6601cfac13c314121e01f2d07705b2da624c0c4cf1d5302e8ebe0d65e68ac7bc420114ed87c868aa3ada432a4a85aa1d0ab786f2a2727e95e310b88f88702c00d004e27770283ec92fdc9d62ffb68148f385da1db6a49294c8a62efd6699ed25d40ed7df6cc2a68d08982c5034d2bc5fe1f0da7331f553e67e142b3d152ea78bb00914ec22b64712086b9767d77690fab730212e5d2e107cf2a3721968099bd2220a4e70ee6f5fed0b08703446822d683b04a628a660d34fbea270c8d1954e55db0cc431a3d72cddbf609cf2e43f84764615d0ec978932436ab2da1a8fc47d86a90c94d8e33b1d8399cb9aa9b6bae14cf8219a1b1ff2c79efd1cdd9de0e16968b0094dfcd9acc9b7935df3452aa3bfb38ba24739ab40b85e14b707e26e4333f5710c772f287a64292e3cd8f6471dd677da62541df778035b0c96ef2525bd57807d0908bf02a1911112047d56e0201bb3636e38a6266dbf7b5edf6a7942dbf6c8d30918dc1828db748584ef398e37659798a015927fbd8db47c1f1a2faa754e48d209c810f1898a4075439c69e77013f97293c497f7bcc50398e48823b3bb2dd6ff04b293560d6db6892450e8841c1d05c26515dc8e439de525b93e7755cb43adfd00d20cb824779ac0b0906b6ba6e423c91416b2d3d3b30b66ddab453ac469cc96034f5109b3c6467042956781615639be124b388720e81fcdb8dd6fed959e806900f8b305e7d33f5b993417b2b61e4d30e7edf51e7e417c137b2e3e8bed90d41e0328c0fc75d12da08d3e6c51d17f6754d2a5207ca42c72696b6dd6dcf6d2d4ee0b72c6dadc43f3f6d0cb59d6f63e4333a19ee74ef388d85c48dfe158affc0c7a053023002d24683b77bb7b7afa675ae1794d71af57eeaedc7c6dc88f158159460c7699d24c6f6420c5df2917d138952ac4cfc86e5daab59c167e65ad49e1d79b0ec470aab2c86bb148de5068d41dbfadcd222ce662c90ade048a182465362b3f0d06c9d7934ac191cbc5765ef058b84920fbc009101c302f3ee8ff140f2d064602427748487d5390d6e95918f5e1c0ca3d3cfd17642df84b9d99063115109fe5019c944486ee93fb7fc0d3f9e343d4c233c0421f2384f4c7480050e35682f70a0511be5216882d3f7958f9d5fbd3994685b0709d06bdaae9406945cf266331250055113346233892159be485adda0e880459780aff0357eb43e18656d0db5fab0d1162395d2acfa54a757b83c0ff4402c2fb7c6704bfbe7d0a55569b656448ac0bdd35305124d3ff2bd06eab01b463d8966dc9371b2eff77f597b7317be48a0402e1e7c8c4db309580e612ca2bff954ee211a84aa65c01ef001b2dc5fa38cf880fe49155954165ef330202e20c026fdf2e4dddc20cf3ff85d1efb3d68b39b5680e7696e07095750fdf6a97d9f1575b9ee93533c769797c89c28792183b0d59f2000560a4631b402ad8a0eaf167ac066d3822d520e372d4838b62d7a7666f76a307102a72fa9f931382b391ca34f47aed483b29b473156ec7c4ba6da8dcabcd700351ad6a4e95714f84f12c09c157bada710dc974874b7d257eb40b8d7a0b69d30acc10eeb94fc10a66e38fff6822a1e215dba3560189a482e3ad011f502c5b5803a2373716e226bb5b57ebd91cbd569d0abe416dcaa6fb23b659e0df4e4628f801af0b8e0b275070027f19eec7fb4cd6f1e1627a9c10f5b8af0af5609d5f9cee0937f06610ca0b4ba147be8c8d5f5dbd74e76976e5ae4e1ed6f585e6bb5c28b708da69db14542af1da1e54335c3219c9e4a018846293515a0f86849e36992f8401300526d83d862c4963b2b424a4e71efdef00422e07a7cce4c6c51cb1c0f2ff00ff1eea8e5a44f5a843b27a0c21134ae42a45c055c00fba1326a4284da441e50036eba0b624f5957e52e7ba5464839919287445503eaba04159fce85fc9b6cd0f1b846c46ac11eaee5bfa636afb4950aeb638049ab6916babdbea77531b3ddc01c54c47e3902247218c9352385436da2e55ccc5e543e3d50b7b3870cc4d379000f8a55a6e33661e84d531a9e3240589b7b2119b54b524e6912226ab0efbdd42008ca2373ed94c102da21c68db29cf823aab0a9c9bc580555c4944ba9f4eeac70b49e38c35ed41d17decb190d8668ae4ff37139945031f8b9c7d117fcdec558a09329058c511d59a8a4d0f930f4c1e8e620d728849129e5109bf0d794bdc8ef40b057000184fa50e6367bb4e19985a1059dc5125eaff21832bb9573cf4ead642018c297eee14c6b79ac638fc5a31f9f74441e5521d50dc50b89bc487cdf7400d021303ad48c36bac9057d1fe9037eb2e3d8f9959433154023c7635c7d2321a63059edcd62b9d8234fa0c4e527d58ebe63d8831064e27a346fec2a6b85d309f9800ab61efe39e7358bd3b138846d77ce5a84a5ebdbc339200321175121e2a462100514ce800b4d751e122eca2f9067dd1d828098fe436cab727c3f01b1a61fe04046d86db9b138d0960fab3abfa72dde4c2ffcc92457c8c2e80f284e9a6269aaa0e0d51b93ec6c613e73e8070a66602266f947bff7daeea05841225bbcefb81cf02b5f98ff9dcf8f1e46d1d061baa055379907605e2f7ed9a77f55f6c364fc5150d888bbe7cae8bb4cdc1ba5d550df6b20d7f74a2d4d8d96c0626b4a15953ac93054a9fec2720c6a0256d7bf1bad5796131717766b6365ed918e3c0c800f16fef015ce5fcb7560c2b5628eac294610bc6429815df0437276997c8aa9f5a424c85066848e26d82eb1a60342d4aa40660575edcccaaacb29b1a8718b79be62ca4fb06508a88c08a7fbfe5013f290e2a4fc2680bfb1acedb5de60ee00f29924510ab05222d317fdabdac4bd91fa106baf82838e5648ab0ec92dc78e366fca3ff2fc50cb8c2352a13868fffe66d4f378c90fc7fec8cb55b6300f03630da2e822b2db10c9fedb49baaf593fbc2fb2cbfe25c79514bf3ca240d950dd6997dd8accf4c5e0148e400d9efe8704e07e94da4e8ccd111ecd6b3d258cfd1af9b202482a2bfcb060aeb911c9d65c0b4ff2fba761f840852c1135bcdeee158fbd11205ba60b7070c3d10443f9b31582aba2d992451cbf048924df6c11b39ff64c8221f5292f9d4009a3aa20b8d8e971bff4e87911891c169ad53db595aa08fb7a0a013b9622ec7066800b87fc33db6f63708704b2660124c5097d34995d118030bbed7fcd8878303898834ee478b00a3317419e65018c272560141cec71a66b29fc8472cd063c20097515b2e95a4eb1daeaa1c0c549c6a52d3d94cfdb906eb1d94f5a4d9b8790802f8dc443837a8d61839118e6fd960b75ba8477df05e2750a9ff8ac45454a2a002ebecdb6eec926e8206a60f8efa5dfaa174cbe8cd6793575b7e11ab3e4031850626faf69327ee73b06777b70b7d0ce425d7ea7fad47e8fbf89427ea52514bb4055e7dece4d19badb6efb3722c02efa97717f8cdf077212fc1f8e756c121a9f60734b780d5f5d85c980b817496d23507e319d1834e2c9e7991d99b86a1ea627a03c02ff188ecdd04262ca8029691c4a4d76c1a79312d92177a5dc0936f8fc9550de278d3554ee8485835ae54878fefde958503c90a75d3e43e250df1b1ec2056036944cbc8d595c54c9f90de0733430a576b1f59542b2f1b94b859618b8357a7061173885e6bdc2194ac34aae58fcafeb7337cc74432f8bce0c928ff9f73c94b035edd976564866feabc98598e42bd8d16a579933218c4c3bcb205ca4c771f570154a65b8a7e8ba4ca6a63ee3cc6ddb6ca532b11f545763d827a1ae6297126a8068a2fb9457a460cd57c5046ed4529ee6370b51e2e5588dc745ee02a5ef5395a0bb983cbfa08d3ca004401088b1e36d45b971b56ba8c6a39ff7936337b9d249c02138ef9035da16bb3f518f22bda67fa6b4d14e1a6f30835e781824486de788606f031efff2d6529a35f08ab6e883bb85bd0395b18ec23d716aea66b5cb30b4a01c709cc11a874353aad6954aa0c0aed85cc245f834e1c9cbcb3af1ac1d243790baa2d77a66695cced0a9644c192d0396b622a8ebe947d85233d14938028c52f01adcab749d0c9a0d7b4a52f93c5f9b37ae7328a7dbb821c9c8dbb51c5ae96ad06dbdbacef6d338e2f9a141e66b1bb2e517b5c57818564ddfd071b6d92b678240b81eab3b7604ebfb9738440d88a098680abcbe35d15a827cd55a4e52d217b1004fb488a82131cc94f800ca57bcc14a0b89d0494644be7fdd482096bf783bff6032284c8cb2ac8681350f9afeec168e203e7f5b92eee77c959f2fdeb4bbb0d4207de93e3e2cbed4094e58022f66e7a6e1109d9effec5bbf41793acbdf4aadb8f0fdf689d3cdd3d85839ecada43beead0e350d7460f6d8cc132c0dc2add668419069c2bdb398851f5ad52409e5b2c519ff30c018c11623a43f1589e07047a9214019b3e207b8f5f0ae006e4e429dff9f9adb8b807e76149769ddd4e70e920569f08af207e9376ab3445fe481df90da0ab67f5c7b05dd6fe8d96f4e402bfa686a70aacbb51c9f26aad0a1dda18af8c15ce5e2f69b931ed171c8480d89322b249110b5c8eecaaaceb505bb410e9f9ac48fb07dc1eee3b76489145507d804b1e821f06ee4a01e052052b83f03774817a3c10f7975bacd5af567a777b7cc168f2773c0c05a77869f36242eb765b58df3117108d666ccce7c4153e7cc5206b3357bda40289ddbabbf6d6841182f41c01796e9d5774ac019aa5e91e4248598f263086170588c719ff7b78c2165096cdfb451b1c95c18f42beef251c813a762a645d0b7307475fc3259e6d8c6f7f82f3e6b057f723ed86f4057630cb697d92edd674829a08c87d63def2e333a08ea94057459915f038da463d920bcc184a49154b04d39e032114fe6f1d2686512eda90014c345100ccf18de069fadb93bf352b8cb4bc2e0bcf9c52b067527a84c90f7056092abc97d208755934d1836a95270f5df568350428b184db3bd72fa2b7b06fffe86511e846ea3aadf2269f13abaab1f40966c3058cfb5923bf4e000ca8ace802794a3411e7fce9b8431234a38cdfc48304a3780d3e632f3246788fd1e4e6418b0a1da48b4e81387b8777f7ddf3d60852eabd790ef5c3e8c321fb48c9985d91745273db3a6d2906fd0e3c07a0f1c3f662c23bd4002d7591ee11877035b230dd251d18d314adcfe95a714b46c8693f0378439f40016b865157f741ce674aba028578b92ca8bc53e41b6dd21829a3b86f2a6a78e20466fdcf39a86b22e2450422f44aacfb1eae1edc863c2da65374c7f71c9cd83a065f6edf6f8b398d0b53d995b711bf97b48caf3056a318f929cfc2ccfa8044ef0f51c252e8bef8547ea759eb8e025b524be4a374e0fff013123ec1b00bf70fe0015a043bf41163ca7b3ee5dcaf0f1bd7bc54ab601710b8caa3b8e3022fe2c7ac058e3966b3dda5894d7de335153e9e63ed0e105d71d533cc58a4df6c95ee974d0fa9dbe4da3e9968c1bdee8b79a2346a7a5b90818c52dc773dfdec62eb1c3ea50b00909a2ab1468df10ced905122174c0411a6bfc3717bc90196ed2f83ca122f06fcc7b9abdcaab9cdcfa67b09842646cb465c2dba57077face6d9e0f299e7c6029a771e3e1706d1f4ece5e97ef8a37cfd63b06fb7fb04e0edf6086f6842d8ee0f782293490310d820407473d85a7bb59ffc9823c157363b589b74b44fca67b80b8ef636e99f4b52f6173b28602e30c930ba9fe82aa6ef07461ef726df2fa09103b6e3c3332cbc8a9ad4dacce44f489be1fe51ade8553dd89470e8dad0f985d603d76fea87d43641807507f783ae1d0b9611fb741968ad0912359c14c54cf7440aae2e51260460141f3838a417c47de785cad8b7206b65a939fd1d8afb289d380eb43c5ee7c2172e1d0136c3120f243a8b029da9a9181c3b81e2b225fdeb9c4e018625797b0175cbe1afa45e99121e22fc0b69cf625677403073998a9c8c53a50cfb6f57761c86fbe78d133141aff6926a2baca4e93062f747fa6738f7060b81083b9e81b93dcbed6ad2317f9a65d1c46051cc0389b98b60f9c7c2c92b5149d20be1a51df72849b1596076881dccb4a477c22567707f753017b6ddfc3072471f020255b02929427ad7156dd5e8cd5d056aea55ee66926b638cd25ef6248ef5f80769f3cf2db715711e0a2dea5d95bfd243f1311bb9a82d52aa77c1271b02ea5f09f07e181a5d138062d8630f0278d33c5f7d40e8c2bc2957e40aa7e0ee312689055d4ccfd7378966e72ba8b8578d9b810e953463b656a0c829b3c3581141c36205d545637ca2ce46ad258ce528cc14335028da338fdd5c7d6a83da364dd6021b007505ebe88de5cd2127234593d8f9e76900d8be2514cdd0f07768fe9ae460e300528237c51eaf6034177ab6e4a2efe9b7b95f7da4c8744aa83c7b25054e0751009db2e4c488c6aa2e1558d3124d9a05609d97ca33617b1489b7d6bcfd09e945027411d62abc79f04afe2656838ce64dffe8f20f348939b3d58a0b69c49b3f5801323ecbc6f046d0c49edb117fa5505bc7585533b62a97372aaa7c66cbdc63d005fb70899c534154f2f5d7ea909d2cdab252eb4311788989463c921196391d4306850aa7f488dc759b6ed069fcb4e38aed7feb201c6fe4828f260e6693d398390e7168122f1c47bf5311260f3384dc8b13e7b15f737c565b860766baa33e2e09035fb69dff1cf303fbc201b0e426b5bb679abbfe92e55e82ec6644ddb656eea60149b9a985f533a1fffb19fc515d64f90769db8476a6bca6088842df5fee482900890636a0c46866938171b90ab4f372025f9b1e91fb2bc69011e378d8c347df0a18c675f973e148fa87433e59725df1d16a38ce50b33c7555219592bef871d408c1deefaa325d8eb5f52ef31c90a5bf3ff759ae034ef005c03c52fec511f9640452ab71511ada4967ca2a77c2521cf97c8650e7fd390a4d59522863ced29f9e040af27e7d263dd3ff84ed9df454a9af4ebf9d41b50c4718c401a179510ad64b0b2445de81096d9933555ef996ba565affe911895be98fa7acbf2a11cc30314e081de4b26403f010dcee95e4a78acdd51366c0e693bc73c33528d0a3bd7002850a4ddd77de924ad5f916308d8c4c8167346921bb3571a0f5edb08531e35c23220d175d0cf9b323b4e9e49be5a8b61ee99e26e5d43bc48744ce9202904cedc5aa0869cd7281bf84478b46b252591c02035c239f5ee2bdb93c73cfcc4f9f75dc7d003f3dff8feabfdb05db7901ab0afaa57b792f9fdb0f1e2fafb2ab8a19b2bbf8026d1aa8db3b7b1476c006d067397214948dd45ec887a2a82efc18efa9c51f3000ede00fa7442a064a8a544c9641be8406f95b7fd683b42b8c43c0f0290e2bd10bd97a12bdaef77c8b6c01a4e66c476cb8a2bf105a9f9a5f130cffd26fe3281d04cbdc94f7702dbb7dfb6973bd7d5e22ca03b06fe8d4cf87a52c40a7ece57aeb0b80f222d4d9659f93fa5686d5b0e444eda43e691027cba84a5da54723e5fb3e0da046546085d1dd5c1e9f32a6db0aa7b61d31b4f89ea8c8ce50f45aec2b3f0001ccec53c5f80a78402d0b24bd1219e421ee27eac73094b5657eae1b41faa0f50b5fdb315d87510144c3cc8f6d8aadc8b14af4cccac7410eb1ce29d8d2578e610a594e3351c3cba215279b4994517699d854954552338c9ff977458d67265c5308fb5e1e43c1d3c4a32ada3f61ec32fd42d786d639fbb2ce6d52e5ac0fb1bfb3091755e9ed99a7c5ce6aa5c0390746338c04b321675ef32a359a2e688af21e61002a68b4c6e35000fc2bb97cf37d3c2f5930488aaa552e48ebef2b110ad43e9404c1d0ee92e19e29a49c93645c7592bb803f8e79478437a7875cf4a1449dcfeb0550eb490fa0d8562e39f1168b5d30070a7266258e1d5bbf05a6d2bb98ce241705aa237aa2c91df4871b3c174311562c9389c1e70c5af7b5ab84856f5b8f75370d121bd23a26311954d57e2c822167a1342b00a430037347ead4f888c13551fb05750ad803b111b29fcaeb8d6d38338cecb2c59724507079084d5c68943167fa0241e729b2f533f4b17e6fa9907772f2df5e6c9f43e0736adf7d8e7aeb53ca780301f900c5a592536ab8fa72c402302e251491ef67bbbea82402d8451fecb6510b752fe9d365332a7607e20c420dcdad42b149204a434ed4dedbdf332410b47e04cb90929186263683a2aff116780fbd5eba6188b1f90998d254e69ffc51ade10760061687b3626c9c2c96a36801b7add857ab1638079778e44302e4c49c677807199af7ef095a5fce44abdda140a8e5eb245a4fd338327a21b6675b13b8e51a0b815ca45effccb4d4c13ee40cf40c507dd35a9f9eb77989c3703a16308c04f50a28d549d895201b5a8c774895b481740009af31b800893d3d02611335461149043255da90418f4c913cf115c3157e1c5e9cddf9a9616f57ce8a622926beaaa306207f217050e36b8f2039743ea7af85816c90fed6320c86f687472899761c460ef74d7777574fca6d707a0690125299c5d6df427adf9905c15d876bf1061a0004cd823efd1425b1bd311a334992271b8bcdcad7f37cb49aabf10208705f7f6209383ceed51b03d4a4f7dd6cec6224cc3dc7db2ac5004185d34c00d8a0ae3b4903 +generate_ring_signature 750d1844ca4422b66d9f1b5f68743448b7f40e53f72670554034ea3ee69758ae c116f43eee5cddd13b1553e47679993b2783b336d0cd403540a879d002614c38 6 1b3095a86d7bed1caf858dcbc8626a24ed5f6b527c838f1bb752fd2debf477f2 2d907088473006765d7aa5b6e1f228a0fe453aadbcb521c43db0bb61031ccd98 c10e979a8c7162feb092d3fe1afd742c058b8bfbf4b9389846f758d2483fa9a1 e78c1fd21444600e41c38eec21410b1e624f9ec30fe47a4db93557c121bb4696 68acfb5fc2bd5c1c7c04cc2841dae8ad77d48aa6184ffa33a82911f588b0ea0e 45b67d52a035f2320a4e387060798eb07a3da38e52d6c40356962c7d9eb0787d ec050250f9c3ede9803b1a1ca98aa2028c36f33ee0226f6fb1523f6455c90b0f 4 fdd7f0d1161505af90dcffab5796591a39df54820b8f954201be911386273d0730067b16e1d17e97b3a8a6116308bfa07e01069a4240ea8142ca0c47cc857403ebe73a3732532533f437522056fcf23332b0a311a3aacd2c8be3d6091cb09a08f05bcee24da7eb3cfb4f972c87ac9079fcd083567bc4c3ba41c03107ba2eb2032028ef37d1013717634d3026bcd4e7798543438b632fcca3e9fe3533fa9fa4016a3fb1af759632292a4850f486a8ebab6952b3a8f7b7dfcdcbd0f9e3db376d0d26648d5a71ebe6b2dc059a295a44dbb16ba05b9b9396d4347b8ba6e2b110130aaf76c60477afbab330e1e973dac478cb0735a6f8815c686ba147d5a7ecbb450eba5e177d4c5673405db21f827ea1255fc05c811efd5cd5bca2487ac42c13c10cec2eabc015610c10ba16159a16c72dc887d8cd5703e45f2a1b8bd9da75363c06f1afd7442b29d5bd2546657b99790e2c887383724d9eaa4abe62723f5a20c70efae6c02c0d8c06cdd633e146052bbef5594c3f0de3158c905f12074956e04d0f +generate_ring_signature f27f7d913b60f02ebd80374824c8a90d6f415a8572073316f7544bbade8b03da d6d878db928594a808431952891d0bf9a50596759c2c1a7b3eb08ae72be739f0 233 55cf36cd7347b2d7e084564b90b43ccd85844c2a1a18598c5aa5f21640b0823b f23aef783fa7d2cf9be599a2ed8e5dcd9dae31c4a9bd0bb55d545981d066fc86 e6adf8a9677a11926e915aa006f07cdfffa443772c08e321288560868903ef72 3191483c1d1992d90482f91588fa989dcd4cf482230a36af86ac6b2f698655ee f619cbd553e2e87a5db5877bc6c066b7c8d092d8c9db543d1a591449788aa3b2 1b5414ede587f8d46a1fc3c7b95bb4f30f078104c44aed0fb5ff53a1a00a8e65 e11e8cead91025268b8636b5bdf66ee7ad9c2390ebb1b666afeb7d8e9aea9c5b bb3c7a258c0faf7ec5631234b4970ff712c699e6a7f7b020aa959d03fd6ecf88 147446ae1f6c526ddfc442d0a5ef65a28bc11fde917b0dd03ea13e20765f9abc 78ba472b95daaa5a31865b91be52159d0663c7f42b82364046961bc8ea5ca3d3 9c1e19a66ee03f04bab1081f55685b75e87958a58587767807f56e71dc919d87 4d6508014be22db2e87358a1311be58299122742401310f01bf15b4d54804076 4e5328f379233bf358f1e709f9b4ce450af0d953ced5b46120f3ac17572346d8 db09b94ea47c3936f63e6b3411162a2a7749ca88d900b3f13b097ae5850bac2b 87cfdb47da05322ea1add30760a5c5908c49d35d2f47bb0ed389993d1c970ccc 103ea313f1b6d3acfda1fccdbb25a560fc310945e85e3f845d7ee30de9b072d1 06ee162402f8ea3de2299497a00e59c46a8173708135d4df50dceb3fdb53ff8a 6313d9d1dabbf6724454ef264dd889d8e1495e466fd5454bdd08474748620c65 259538e877f5c671ab9a62746177e576b49ecd13686d6c7dbe1f564bf8ce2ac2 2b535c86ccf042fb71ecfa94dc2893e6a949de2490c70e2450712e8899f33a45 0c2d2a71ff7df05aa7f7020d46ffad3df03d66e0d4b61ade126a5b13af9b15c5 9d164785898918700b194906ff27c606e8222b7faa23c3a38519f2dc40dbb674 78e1aef8215b83d5a04d71072313094b75465ca53efc61a5657204ddaa037f1a ecd681279594ae430cf3c975f02a8b9dbc068ab7647a2d152ebcc13ea4484dba fa57cc242921d76511b78197bdecf530ce58e4a26f954d65194bda052201c3bb ecedfbc99ddd5b1a430f907ac1f8abfe2d627ea2a2ca95dde53ef5ae1eaa446f dadb2ad431c33978f0044b42a5849cdd886c8d7e52b573d6b4f70a13ca182bef fdd51551b461404f2cd004533d052af576ec54d0f3b8a29fff8a29c200611c04 1ad1e83f87a678cf182ba57462b41aa9c76dd9a70694222eb26cd114024beb19 3b9885f97ab6c63af87755958da2f114606f5a7e6a11f8908a78355a1b3e2905 09cbf9f9a6c15332fc29c3174e92e7ecdb41fc75580cee12a39b05f588c47164 feab34050875dd107b5f59f89f0904a16705558c7a7c9087e3f2f60e9e50b667 00b0eb1d9589159fc500133a83383ff0f8a279656f8e426f44daf5bac23e86ad a4dad93f716aad729f191fdbb10cc81e8f1a89cb68a2ce6f56c8942511edef8f cd9a6f360e4acc5110e3e1274cfdb2bd5f9dbfeef39312bd77d7fd59e6ae01ee a6c0680f8e9ec19e9add8ea2c24b9c5db0ef255562f6d6c505bda83d33ed6d93 099d0c64b0eb4c8917711e440b6fb3a5211ecb6682773b9a9f8570a9651c3e90 8b11f45b9de403bc521d3fa82c649adee5826cc0615cc9ec06c6787403802451 b4bbdcb3c26d5a00bbd45c707f340e1b93d15d3c3ab5addac01aea8cbcbc6d83 5d5d70a0bde32a2ca5dbb25d348b4ab81df2b5bc6c7db3df14e4ad0c9dceef32 1fc44bf338ca4742622fbeeac8a8a2e8205cbab2f2c5cbd67bf367ecf2762a9a 2400f867fb6d590e6ddd7dc2f0c5ca04111e0539f34c6eb65e9984cd1fb7a5e5 ce4d791a2a89c9963053b3b8a7bde9ada3a1e62130fdbc5797cef775788fa728 d40bbaedbf0ee5aa8e0adc8a253a43d2c881772d1ffe95cf39c54707b338d916 de7ef85b82ca9de65fc140b309b99620d68d67514db65e00f9fcab1fb9db80a6 e07c64f73ed8428290a795ca789e0b546e4e644ec55cb5ab1b6f5bbdec89107a 89ae904d135882723d7ce252249bce9e07136ee67806d813d13dcf696af6101d 25ce6018cfffadee334710779d3ac96744ff57129610f4e688279992e6e35e58 1d5061bc42d7b6d69ef0642b0abacdd76254ba12463add6dad0dea6058afadb5 e0180a0ae5f0fa213b4b3bb6e249d47a419e91391c25012eb97af2944c65ebe8 536fe482951410d918ff99245113e545d7c3f67b44e830933ed6636540edf213 d2852da11210f14bad624244e9e93e87fc5a7f56aee87b35545b8a3a2144588d 00b6b02cd7d5d76b9867018aa7e266337a33ca264bdd4fd9764572ca8d19c8cd 62b475d1a392f56274bf6d2e8518f6c4663d50fb5dc28ea2ff942eaae8a2c5e2 e2ae86416d4bdcd9996905e188d75c146e0eee93ec34d048c36eddb1d4699a42 28d4f4872bf65913080ec20249bdc553714934de9403034f370b02256a80f14f b316380b18dfb1d97cfb14477cfa90c57b4b4ae2ba71842cc02800aae9522ea9 dca3226b59e1dff0c52a6d87604b0a72570c1bda10890a250baaad090df909a3 e0523d35014813ba9aaa0620dbf903b498faf09c7efdc454babe98805465417a 13e0ac92dfdc247b8f8f7d0d172b672835a1a262b9bc7f420b0c7494a6e06925 444ca7dfe8ba14f3e20e13b66430f9e5fdca4c2151f2d48f4c17879365e8d965 d7f8aa1d00e0b242a059617cdc59f43bb620499e44d8a53b13699deed25bfc4b ad7ba1909617fd1c948bc82e9e6aebf3586bd364bbcc9e7e5a8716901801c647 9c8a277625b0c791f6d0fc6454295abbc33e4aa6b86f3684d46413dfc9777462 bceee68b302cf5a231c29ac61349088cb37b6b21ebfb68aa0b3e5bb875b0d950 bc616b7102cca1c2d62f875248c7017cf10cbb08cca437c1708ffce48dada302 9971d66a820b9ae68a79b0335369561787cdaf1c25f347287115c462b801c246 8ef16e4d713d98df55ac7bfbbea9a9c14348bbcd83aa0a63e96b634781af9d19 d9eaa40c87b17368d53175595676e7a64e30c751579784e2eba2c2e3bffffb4f d7827478dc4496ace43a7eaf9a5f4e794cbe615606d5e527ece72e8bf90c968e 42052d3f2ccbbf8bca2bc3c2461fc223fced0a4263dd3da971efa0be9105b42d 22733204ca8ad7e175b4334f8acbd5f7cd2c75ee5670915c328eade14d2daee0 46394842723acf4f7c00011dd9d3168a1cea9c19d48453c8ece16acc206e5c6a 349160a674557aa90f7ab876bea6349205005564f3ae1a2ef90eb819f24fb8c4 1aba4701160c123b34d8d3524652a2944aa46278d64b176992b5aab733b3c779 ff3e0d7051603447d82c80f7f315a90b8ac3f6f90048e6ceb98abd827c6d8441 80bf794f7039e9819c607ec305410f1ec9f828c6be9f324cd5facf7dda649059 7a02f316605ee4003be432d815dbcc70aece9cf05203c77a9174a5fd6bd96b57 152c74f4d732a9976abd622c5d7e616f5390b0f4825e910909df250564d58247 5ba6f8f22b921dd4dc6edb8712a42eebdf3f19670b46db94ca7fb19c99a999cd dd5c5a32ac006bc44b45dfc8b479263ee9680e20fff90231a6efdd978c8a4443 75fa2676c05a1653ce79ec8d4cdb36950feb5b7b34c0b7bd356d6dec540eefcd c41f11ac9af6de268abeb1b5ec139d3f96384df5762abd93f7511821f2861025 f678e7a03519036b0db42900da0c47abc8c2453188fc0f69d50bf3a118a91f17 c03ffcec68e821df8b871937311c265ac2503c5d8e581374dd68a6d8d0181ab5 d0246ec46cd547dbaa0a14947fa7306f394b059d02c91980a448eb56e761accd 8c8a616eb12f379011dff36c91f2a2ab3a092ed3288ea463717b28939c98bd47 3af01ec40e51f811e063689cae3916f96d43730d3ead3185e4f45295fcf87d6b ebe7fafdd5dc28f295758bcc98440080b8804afa2d1fd6ec312e25b1e7a636a5 afdaa5ae5367682f140851adbb48025657d5575d52bfd97e96e965cd7575f293 1f09e3ef21218a682cd1ab988351a036e2b999eb50acb5b7a845e619ccfbba87 bc01aeab6439bd6fd4e38f573a8903fa8c7f2bea9c4b5256173764f76958b5b4 402e1a1a3a5ed5458270edea61df5f795a25d45272810a383cc8c273956adfa5 092699f01910b9ef8023ff0ec3d6bbb4c7f962ba5d8ee0dbd7038aae95550416 8f6f54d9681dd5c3412849d44fb370da3c4b8edec4a322bc0a91f7b5c9bb3910 873b334452525b49b70d12110f5847784652751d6c3114b6ae6e14f07469dae1 3cc09155d918c08869658e541e3a882b313ecb45121e026730cdfc8c3734158f f086ca2fa67a55996402be93b45897c42bf43dfc1cb7353ee152789302834e4c fc7533abeb2e084ee3aa9223f85dd9c51fe3eec06506a69616a50b1da10ebc3d 5ea6cc55fb3b0e5f5d80ca25cedd189ff92a6c302679fc0222361937babe1629 7f1f428831f0c4f2ab7ba9b4a3ad285327ee9de742f9db218e8eceb303e8c695 6721467920ac275187a767f8837923481a21b676d186b865a771cd8674ab1512 40b547380c9e2d64de2cb0e50b3e92f0b2c7cccf070e504fdecdc5f667c310a0 bbadad82761207f7bf381e5dfb61e85e18bc1f941cefa50ac7e0076e01a15614 21231c2b13aa15557592181950817eae45f0d255c8cdaf340724faa433bf08b8 6c741289c5b7a1dae67461cdae78dcc7fa0c154fa95657d25ceb9a67def70454 774abffd27688020eb3d1751407a95f56ab4884e3f61355610dc527aa22f59f6 e503e30960c241c79f563eb190572e1392187683b1b441f5a0f19e0fc0d055db 67994d8e1579f199b7fd9762397fc3b6dee933c1614fad54a8614c2f3b197500 1106e968cf69d1055b0259b1b4c16f158954788efdda7e38125fd02de04a7c28 f2bdda49035fc5491ea4a0562a10c0d649937cbfd3680db98782e37ea3b27295 94040a0d3a0555f5a0509669852608588ce0276a22dcf9edfdded632af780cc6 35219cf23613605bfdebe64053237e82de4eb8fcb5e4e378d30acfbbd0517fb1 e31bc949cdf7c653bafa272adfc324bbc06d68fd1c85ccd73e2167ab0d398a62 97b23f51b37e3e8804f0486abf940ede4db6ce40ae273c9652ba6e5651f1a491 a1422d58f860862aa285cec422a689d0a6077dc39a4ee292e03adedb0fb9f174 e3b7c1e90fea749f88e1b3a71e87ba2af9b8d8c4dde2a8d6c52df891eacaaed1 e498365d5367d094c1d61ada9235d6f0e4fb749643f37f0fa74bf5ca9e986ee2 c91a276ebcbae296d67d5bb67f9489ca67dfb03095f8565a1c2d16e07b5b835b 2cec418537af261b32804a2968e034f17a332cee342ed2269045dd8f82908538 b30f4139b77e670b9eb081ba60beb1f747211a333ee0b98a94c266beb930f3d8 2fc837453346044edb3981529a136a9aaa8542ccb62c108c91bd0f3f094f0cca 36842df00a10b0993f1bd0d86e35db25ba43e8149ca1efd83bcc8effb4cb6f70 f0c5f931257c271942c0ace6b0727ab0546c648e1fcb8932fcde5bf933bda6fd 5a5b6b2dcbf759f76c4f5c03efb2c04170b9959af18788e8c6b684e6a9827a5f a0e0f8a3a68bb354a5a7ff663b50f00727bced33bc6afd1802b0c0be7359394a 47005a6901d8d7f6c92306e6788302e5c7e2c325597c4cde2c90866b37248ff7 fc3808ba77b319b5a1ad2b368a16f9e35df36f236e92dbd0b34c9ff110d565a4 dba9a7d63872ceaa829c4ce47452d73424cf2359a8ebf030e2395d76ca943296 78e55e3537dfb8be372a2155152533098241c527f9a52e16a28511d58c5656c2 ee0751c0b6c975f997dc1a4867c5d3984b9ccf86769ac784d1fec7730b6538ec db298b2747b3498dfdbf8a4de2742e5aaf72c0da651df9b71fd68d5f337cabf3 a053b0d064cf031a1c8255ba7eaa0ccab66902f65a622618b384f02844653723 09e838d2d21166c9ab6e3b1eaf726034bb0534f88a83f499cc2adc364e8ff5ab 8b090eda076cc9cd677829214ee92dc733ed9318c2cb6dc4645fb6608faf45b2 e21b3b60dfcf9b5630143f9d0b0262d39a063ad68a8ed1b0d3919ed986dc1294 e31b9ae021bfe63611fc259de37af10d6eeabbcb177ff57a83ac95f9eae895f1 9c39c0c77b09c0924589399d9a9637171d1c4831c894de9e705b1c8455e2040a 6f181317001fdef878ba509fb3fa29c8b215a1b75e4b57b1f2cf3b236a7b4303 4df0d7de26f694272dfcb6c61cdccf9e065a11e41c1e5c105ee23557dba83b70 cf137999dfefb481c3fb36afdb6efff6a02a2c0e3ad4ee3e74542e7633a49017 1a1f3c8c2c16994d54bb8b98e7138722b355068476080c19c0faaf411be4c2ca 821cebf968f8ae03f7a1952ee55c882079255639b5eed0845655c5f16ada6e72 73d6ce7c6143f8535e058bf00c7e74af22e8c8c67e3d0b1f83e0419bd514b032 16373455235ea4ffbb572f481c4e85d469eb4ade893b896a14dc0896ab6ec0dc 513811f68eca144ec1d32c06e8b2ae3cb52b0abdf92009871ca9551171186587 63e341454a2bb3bd985671fdcba3d013ec2c2f72182065e1bb5a79620afe3215 a0b89c60fd765e0e49111fdc61ff7c1638665daf277e3889226bece6d744a088 07c0db0f5d2785e131817776997125975004b51e2b39d3c3d04674a13d1a1abc ff0c27e6ca1a5ec1c47451c41571c31c9ac3e350a9b63be33e2e5f3dbc1873e3 56eeea4a62b43be115acb519425c1a6bc92e6b1c19856ed521815de56ff4fd50 e2f6e4b817421988c51326643019b598ad4d42f5baae4c224d782de73deb9ce9 9eadae67eddf7422824c16ab9ee259c81a500649782ca718230e7758919693ae ff64903ebf555e076eb53a1afab2aebcc87f6114c82bb677b2bd4925b9e3c3a1 5a5cc769eb7a0348b36dac953e0c07d24c1dffd947eb6e2afe138a0c07841f25 01166103724eb17c602824f6491d0d21e327d954d3e611fe6129524236590cf7 4b6759c4f6547701032388e54c1d4d8fd791e04f2a1cf1037f158fce296d4947 1d49d61f1287d6144274ef0e8f00d5b4546ae9ed23c68c5bec082b6f1ff00342 7962841e17d2d21621d146d456dd60a6c1cb4ab66002dc5a32eaf0fef24359ce 2d2e7797f0ca4880d0a2a7e68b1aece161e18ed9135a91ba6761b67e0c570ba1 e53745e1665e40d60a60c3a96b532e7eb2c914771b0384e3a293832ca682839f 4f3949f515686f46d34a60f170c16a287270659a4753d37653c99f5421609fb5 a297943cfbdea7047ab64829893c1ef3771e0415fce59ad5ca45bf0beea65a78 0e692c42ad8cefeecd38404b19b9570566bed5509b5ae56676742642db2664a2 36e6abdafb1afad30a53516068f278b8081ec04e2288ea177df32e2f93a044df 08b11bcc947152c3b6a2b263ed99f34eecf0a58b0929759fb75ce1d94938b27b a7ff0b95923813cddeb37715e162e799843bef03ea50995f1f5509a19626c8fb b933eb714c9c50b3d521a0e548804144fe988a152272137bc6f1745807c61adb c5a7a9744b9b082aca700f7abb006673f97ed785614d427e901efce9550ff6d1 3bc55cd88dc4dabebd14c1b3784071ca23dc6a2af534f451839c66f9f35226f3 cddf33fc6d77f2d6d39214cf08440e53e9afc605bac0ab77aec1b38cb9a8bfd4 58cbc859f51d72828ad9f05deb0d3b8b6cc2311e9a6139dcd2f66ec99d984d95 a9bf89d92bd3ab8df4d97b728ac1f009701bd126184481bfd3f3cae8b110e840 ada30d1224e653372f3323905f948a57da07a3a71341bdc9fac9b68c8ee3a81a 9c44ce63127dba9bfc2bde5b5d4c26a7dbeae91fcc88c05d48d52bdc0cfd861c eb790d6743796030eca936b4fe10e7a4acb71c556aa8e9061f43c11cdf6e5769 aed08705b21c49303f5b24034a360aa51eaa8252eb7af81295dc78e96d17cf58 bde2bf8bf9301233afacac5e34d853df4e0a452779a847bb1b727532c55e5114 f2e03899ff057b8f872650168bf01fa054387fdf8569313b89789253f2f9c221 decf7840e17596d5216390e2ebcc76ab7cb6d4c8800777f68c12c5beb39af336 acca39c401fdcb7b295e0d140065a98e5f162d404fe99ef9b554e8379f0e2217 39c163ae4ecbb7e56abf9510514b64bb93b8a4638b7476a025c1ed66cdb23ff7 d080550db3120f1c436ccaf2cbc25df14ad60e5bad1a864cd095c86152dda424 c5dec76097cd6e964aecec21e98c01dfd3fcfb89319411329ac06294919a7536 aa938198f986a2e2ee42fdf75ebba89d7562423691e863bf3aea7674d124a285 9f751930fed10db3c97f95339bd9d589e49f9e34f6ae5a7ff1f73d0ac8d74565 47bd552f33d22fee695a468f5f2cf2a4fd9e486446eae192274070e9686c571f 70c6936cbcaaed289674ec043eba8ad7ff309942e219879df96983f3102ed008 53dde111f8f480588f43ac4e09d44d7d855383e167738ac4501b804d20fa1e24 9357bfef3cc68f96c2711a2769fe47cf05e20fd1ef1f6f54ac7ef83235941bb0 4a255b14f94aa9cbde6eaf343657f62ac681324994a00a3a80cae7918a472270 d3a075cb9b68dc8f6373da96e9fd70d59b1e617e3efac5aaf74f338552389e98 5e64d9576a2a0ef0822baeb966dfc7f91b087f3a6b6dc65daf8215fdf2204360 7a1ce008d23f1bf6c1ea400738245a152f2e8b887b88cd6099441818633691ce c1bfdb4fdd3266982b630307af8a45724663e1bbb50e960f749ee466b6e5a452 0ea25037a71de28ce920f12504e42890bb79bf6744b4df841f632475e35fba1e cdf141db8fb7679f92dc136c5169f762223f13eeb8fd4034108090076f123357 47d860ccca39c159247bf28addeebd3ae3389da31a32c2db1a40ef6eba20a776 c9df8f134ba5f4d03b1aaa53680b1e9eec6e93633cf1404e2f86adc84112b339 8b430b4865a929a5f57c86ac17aa34e963b5a6d17a8fc71a7a5a7e8891c45416 9a4bc61db88aa29a16be1d6ec3181884a88002918670ac438b4abf7f06006107 3ed536a819c2f0e48be9afe623a22c74a82f24e23692f2c1b4325a028077d05a c100e5a347bb6d5fe9dac69ad42719631afdceb346e42fba22a34305106d4094 a3fe218a9d29510c2bdd98e825ab735c9adf76fe563529d65061ea47a894bb2f 749e4d176a30ce19b706de10307a5dec02a559b23fa668cb2a4896d418003c76 e4868220d72f5290292065b3eb158dabf945892b639623b57ddb50eb1a56f1e9 9a15c73559d607b0bcbc0bb334832de6941b76910437f1d980e10be2c259e96b f4b7b5308d667823307149cbc137c12b486b41b1d025523607548cd18890de8d ad8fc53750aa9215651db821d61722810b4fdacc33071fc226814651e92fd8bd c443a6edd599a2375afd477020d9aa7206d6c266fa3d212c8d93bf4cf154364d 9209f6b4cb1a085cca6cbff02fa2ac9ff56c5190d350ed98a9ad73a9f6de7e63 46515ffea15d7a750310e11a6bbed3ade4d54e552dec2642e758523ea895a975 a236bebc555fb6d3775355fdd498701e1f786bcf7ab8fa52b48f516c9efcda5e 84c0eda22b8553f1635550cf95e17577c284830f950574dbc0768aeee9fbb9c8 7220188a81eaa7bdb5efefd106f586b64d00762ea395b604cf7bf9a8266233d2 c74d4bbd427cb3f262b3061bffd852467f93c7269dd372d0f39cc263dabbbafe 3b8e921d6c2a0978f3fe7c6fb4e9dd44627aec5b20a2dae5c2214cab9ea47e35 83d8021c9049794c198d0376798107ff4b778250478d1766fdf920fa1c89fd97 eadf70afd7c1cdfbb0d5c9454648c2b18aa52adf738685decf1c65ae09353794 273e4ae6e6c90da64167af9eeb9842a6a000cc93bfdbb6a5533955f647c4a6a4 785d3b5ee6ed39f61649cc1a0c26eb3bd9f9063b24964b6add42d6e278d6360b 915d4c7b0e934d93884bd8d19039dbe1bb31911dd59e1c82f35d2cfb69b278fc 97c269be956df213a880063b7bead50540f89e79d76053c4fd1522925f1d44ef f340ec41b4b6de29dc26dc785919c54a168cb191c561f47fb49b35a64e2d2eac 3fb45387e549d35a3a7bb0f8ca7df2f84dd4ad6d4aa5b2b1f6d954a967671d9a b0e2e19806e0fec67cd64dcf88223dae97b5fcb05840ec93ec9ad56e5f9fb8a5 c540d6c46cbc6065d50c70bfbd3d7075aa6d051519063e2215bd78e119957be3 20e1bd02010dad186b960872b223ec2fdb9fa3d66a830cb9762bad2081ffc913 3c38e2a5057d25edcabd08b02bef69e4a872e53a94db835408af699d8aebf02c 44120a0a432c953c4256826726b17485aae6c6cd4977b03222c818acce7596f1 9e5536162073005223561178c87878d0b90e03b0b1f4c2671bb7025349c601f7 f3547b7331a81f64b3041c5a1c166bfb9e2f4d0ffa6381ca7970196049ec5107 8003cc4f441219f63b2c51532e61f8f32500041619bbc097ac586c9856386ae1 f7085de1f81dafc5e687d3414db9f34cea8d73dd079ab248d3d2e3127466640c 60 343747525a5f19dc05824400104860f01748542dd1cfb6163e03b00fbe58040c6738d23aa00cc6a6fac93952f2c84d89ff4b470f37dfeb97efeaffac8b57ec063af999b982ac5fa421addd9967b8ee660c99fd528fc6d89a13a428dba6c5ec0dad2f08b11df66ce6f1f4b5996941f9558501c0fddceb7e284991370186350e0d3a26eec4945208f67f62ae8996802c7d0c841c45c4d3e2eb48a7c3aa2eba0804159a6753dca74653c99e256c1353a921979a3d58a0c15bf442ba8bf763b40f085314a5986620a0324414b891d80674b480caf6b08b21e9266e1109509057b508692f00d0fc38bbf9abd3205047250d809f14bb1b04e354078e60ac1d5a190d006d7dcb364558010103a215c826c05aac97c48c6a943d70eb98739f7e16ad5303e3a5c5bf12eeb0cfb474f3f73a666509a49c008f764ffa0362bf3019bede600408ddf831861f3731777c1a0698093a95576f7ca3e9ff0c8a1f314bbd23192606dd97f17bcbb26db889f83238c5a7eee581f3a6f3041dbd4c9bd656b58d8a0e03cc13b5059612b4a828457c10ed73c7e4a8bbd86e9671f2a123ab44eaf3900f078f68d24652651b2eee2aa975a90af754fd6889a6e9665fa35248589e83a4d0061a92bbe1ab646bfc157cf26fdb7dd59196ad20b2c651ac6b0b395d8a9630310bfb9c780b8cd0a6eca6bef5466180f6979405afe46ab1fdb54ed0c99adc9c270ea08f77aafe34d062d15411c99a8a64d01e5cdfc2d2a2f552350193440104e00b07867d7b9770d1d2b43c7878a67edc965734e7ea7b9917d8d4555e9fd1bcd305032b0c714293d006bb1f3a91a462af261e390a6231499da64a7da74029ccb40e2429f67c8cb2035bfd38ef42a54db3a3155d9acfc120bea2c180ea9c365d0507ee643a763dd1a42599c528964b732babd0adfc4a6c0c4f8d51eee98f57375605b8c92b363b34bea0949f8bbc534f351b8e67fd0945e26ae92858dd6982eb190e8afc3798635f5404eadb091c0fbf7a876fd45e476b0f1afa7b8eb2078fce4f0e194300cda2162d58ca6d6cf7ef2280915bb7e38aeecc38d11b9309c3c741e20d9c4246ed434d8439a1580b0d0ff714dc11288831ff3ef498b15efaeb3de6c408c7e81504ace512e24a09519fe0b9b0f0db9d38eca94a286835072368f4a93f0f47db975bf98846312d58aaac5046346c79f8ad703820d2723f76824b160bdb006f1a3965d99667b17a659af4c11b4f0aa6df6f6c9893bc3f8fcc8e2a947f5100339defec60a38d638d5c1957b026936610b864962edce960fed8ea1c161a3101a2641db3d45396e0a4cdb77a90e1fd7e50eabe7acb24dfd0d1b88fd32bba7b042015c9e32358cd8d309cea14133bb6335633c076d15903d4d522630d24e788028f11d19201465b8495310c027de2fb53acf6780f65d507dbc2262039d78f780da6fbd0350d447b1e046aecda2226a02a2557a487a19f1cc0c267e3d5ea51d7033da7cbca012d98b8b9893dc2333a2c695322fb802837aa1a478ef5d5670cb3093296ae9ecc041ef0b2ff08d0b20a543aea00e66ae92c827bbc0240a59591750751fdc3405d462bac5f2e52a423bed1af6736346a8fb54ecfc047b510e08a70057138b6b0d6e5bbdc51b6a7c0c6b2389bedda4edf0fcb403a347b3c7c412aba0744fad9ac2f8c233e31884bd5746bcb00af04c9d54d54bce1bf2e1a003f6e1b0843a46b2c48b86cffeace5f92e7a363c9beb2d26f6bffc4f1a5340ddcfa3acb0c4a746b3e0e65c0eda373022eeb59b7eb444591cdfb65f8e34927588c8f4c2d0ca30f8a380d0dcab2467b6494862eb03aecdec5e1cb89740b28e6fcf85b7bda0f0ff923f3d399f64b7a1b08b17fd73d9b163f7370acb5f707ce4dfa33d02a9602de10cd7c5c75a85971b51aa3bca35353e335a62da4bbd1da107860ac60c68d0a9ff7aa166d68ae2c40abf916eb1016675ae292cf9b13c4e4804b904b5853ef0749bad3bee2845ef151010f2a751459534c094bc79b57c9f3571a51bf2140ca05bb721cc5b46edf826be10c3177f11aa4f93c6509da64a281a5cf4371629b100fcf8387fd520a510ab59921f9e6832e8fad969f0b64e5b5bfb0cae5c8f966190fdc5d4b9c12564a55d929f2da596d8f39ea4477d6dd14c74ab7265c7e4dbe9b09f381c73325325ed8f0beb6c7be95b9056b7c6d92b4ad6afa4781a454e031680cce9cd6acc635e4effc31d4ea9f297fb77bb7c6b9cfae906b1046a17b8cac1e08f3288eb1771b29653e07901ec3d7f16bb6ea3f3ccedc3fa1c7b07ce1a686a8062ea29b435700eefc01c22048700d460b2423c7a2897d9e3802f371f557007306e7e09451a831c7cff801b1d8ef9b2ef7042859f71533534946c32e0bcf57f00bbfefeb62d4b0ab3b1f534ccb7c3d632c823838025a3822b8ce299adeed4f0f0b4e05fe38a0d39e5a69fa2b3fe9341e468a4fcdfb83bcc752949eac010777a2061ba65fdc8a08be1e26b9ca14c1406a00c7e23170645f37893c8c8b287024b60aa665c676099056ab7562d43ccdde0c3e4fbdbf8046ca65f366d51781a37a5c0d9ac4faf147b751b2d7be538f453f6818118b65c69a56ef32038658fb935c4d060370517062894a5dd372787767a097b79e05e3bc8735aa6788db1e93c605010616b76298c6037f1fadffa004f28430f6f91b29529cfa9c14435860abb19d2804745a877e1cd673fadaeb67f5301a96f772e13782b20a6dac8257592b92813708787552dcbbb00493996367e3fb9f81b43613b781f95ebd4fb9a43fa914778f02557f566b436023a0e064aaabd80ef64eed06176628e5cea5f113e536dd1b11078225641da701eca671b73fe882161f9499624e345ad1c9b4e4fd03d0c9c8040bd786f89c2318d4ca25bc7882e06ef13ca5f3aeb1be04913914e3f48b99032f07bf2b15903bbe325fa2b9738ed2d52e366a6615a9ab9508c4db59a8bbc2cc1f0a2a149480b39bda7a60836120ed2c623ef22ea9b52aacad8a63f21706e93a9a0fbe0b28307583a987821b7ed8a5aa8d648b37e0e92644aed2a029e4b03137c2081a31519296d26ff70fe820639fdab592d1dc77d37c0037e5856c44bd34e15c00654f98122033703d81cd726fb9c613f96589b907472a4855635a69f3b04b210e7b4bfc09b90de3b3440a33e05a0ab2f1b2008f296bab7b82d9bbe67b353ff007aaa2ac4508ac0e3b07d1f222af52842ca0a2327821d2cdc31b6fa96bb7df78026f0ec670f10136442a4505d6b3e51f51881b8e22e2665b1da17b6b111dcc900a10b1347fab42abc014332bc103b437f866d017af80a77dabf72b817ec5e0b30c67ff03541f81d366408d5e8591ca6385ad2f1392b01afbb254aae8e6c5c3c4074ed0cd2360688b8dcb868d7af2bf24d54324177d302c1b8041713e8161f4520fc952abb2ee09e70d9d98e71368fab047dd10357170af433a482eefd8b0e08500db02504f197ce42573cbc892e05c86bdf193162154aed8d9e752b57a4483290ce55f2f13a3a3ad922c61947215429e49508dd3893b3d43f2be23674e0ac01101b8e045b1d372a5485131abcf8154a77a47796a34aed37b33f164099fdf296d033270d766dcdcecf38ffffe82045bc0a05862e7a95ff50a0d60d873cdedb92905382f27a44c9f1f0ce1b2ead533ef1fd399405ba125101b12c3f32674ccaa9c0c794a3d03de77e71f49d706d3835d02f863966e6cf1ee553afd2617dfea07dc0b57830a51934fcb42cc35728b88bea0acc97a0d69771d810d82e944f164280a0a3aa43f3518a37faa2cd3376a75e545a96f891720fd34590ceb73fa8a1d6d070f5b926b12ff5178bd165796946aa4f18a101d0ecb96cf70073641db94e6debc03419a75b85e09872931d383a4607313a5e1ec2965e3f4cd4f537862919a16e2011f06582b5cc17d02f3ad744a1ecc05cbd9f71eb764f01c3b82947ce424b8a80fa1d4846858a42c2a197559d1976c0cfdd4858127d7c3c9594553a80b3fffa207824adf4383ec38f21846c1fd8cf302b383afe1c5ca135de453e9ef3221458b01bf50d7dad6aafdd263027da59e0d13154777e3fee83846c43f2ac5f99622300f706aac8a15270104a1b33873364c706334762d1095bf1b0335e527ad3d2e550a7465fe93e6bd75fccdfd39ffbe5262953a5fb985f0200714917e823972e64e0e8de1ca5c9f2a23ac77dc6e91482637bf5e350fc5a65375359c3f07327dfca505180ae46983ad8dfcef9ee5175cedad5e9db59560a13e21ff6153ba7ab0222c0e11c3346cce137272a435f15c05ad214a1196fc7bc419a3396d09fd42fcf63e016f365122540a77cc4d45221d7b2e13a0c9ffd8c27216cc0074786f03e9f42805adab651674066ffe25a0c8c7164e56184034fd2da93fe76f80429d2bf05e1e05aa8584dbbdf6b6af2b26782e586972a7d3636c1b4a02eace5368aa91bbeb120421ed19d182db71aeb26709e098fc1690eef495f841f5bfc4ad0a50602e24d407dead7e53ab81074af13e10e06a2cd4a5b0ebfcd98101e5900fff2a0d454c1a0e0c1fa930eb04a8e51ad96e00d9cc8f84a592e4cba4ec9ab3abcdf01aca33240e03170b72456142da8cbbe0129a72ac562fcfda9edf2552602ef2eb5009fff20c3e29dbb78d96d2d13cf0f5ee1906cca006644943c05870e58524a82c0c2b660bf5ec3a2c4f5020d15f41e001194e09e713a17d221ac7b4328b25de1a1c56c00427939918c41c2c904e34960dffb9f86edbc40175145ce055702b7650c98c800ba8f097b11947dc4f1f28196ddace5d3bd32b5e1e6f806ad24b82a1fe66ccfc0d98dcc042b94af7bbe0cb0290299c73bf0b1c03aa15e8e78227d7943ba2337c0ab03ef8d5c0a21ea4c462575df4b1ed64ddb898caec1958a482cf0018b8e8060792687cf12ae43350556dc4f0e4bb5ec359c8196ee43c55f4fe87299556736d0ff19728a6f7e768e6c04c0c72acc0d3074d607446213265724d0ce5b0bc23d3058be5655542b2a657e3bc7daa99dcb76754fd3771ee3131ec122e1f716eab5b0707d3ef6d9d33cf084b9178f5759bd344df803569818791b34f87e878832d270b7c65476244ede557b824ed54304ebf4e855b0d98d9826f3aec948e221ac99008707b785344931c583e7ac70a3bac2ff44f779fe18a9aa184e9f2fb2d2321030e185aaa7432bc23cd32e1f64e68ee4c96082dac27685bc568b9c7deb41fa0050f79db2e284dd2390ebe75be42e710432a837c02cf5f59647614ea27fcdca00805f9a4b6c5faf267ad943275f385f71f2a7995cf35d26c5ec33ef7c51f9f51b4043290b43604813da826ca2137398f338522af64bcffd8a026b5807705be6a2607d1516aa150a3299bdddfec5109923543724ede193c9382b3a002d35f39a8ab0d5bb6a486eb967c070b74c6947371b2d5f96641b7a04d1368c79aecb16c717101fda1c8f8d8c0ead8daa1103ce94df32c2aae83438b57feb5563fbe9bcfb1220ef0c492b84810d9fb513929f94b2e01a803043548549b2dc5669f3ff19f7daa0e43a0f317664ae4c1828680fef4ed11037fd7700a58e830a219f5646a4b17f704b9b4acc35e661ace1a7d50be841209f5883008646841fd49745ca37400a8cc0cff3a59819f751a94a049008d8cc8beb19ba1f96cdeb4c6ead02d1f514ccb230cbeb821b64e5e13caabba6a0864afba25f474d50ed6e63c35f192061bea6ea8082f1456fa0df87e9875aa68824d033860e835de5cebd61c840d5c15ed403bff0d7be3b294fc8c72bd0fe21c7f868bba88deb53d88f8315a252a01a88f3a0f200c0392400c625abdafa68fd086b34b0e92798e022e446b46c3e1f505fe75ec530694cca6e9f486d9e20068dcc391cc8cbcd69dd9e2d473b72cc37281a04a85c3066458db4979c3a4e05472f673a08e75294f596238a78785ae64b2cd954d72dd029f4dd04b1a7d88bd9595a1d92eecd018fbb2a01d5db8e5a037ed5b7ec76bfa013bd26461526ba777eee6167183ee427a5be90e3de67db43e456ce4a1e3b0e105ba33e2e0ff225735ebef94789fced1d3e1060949a799332f052d3734228e4d022619651f336cc6af20e8dd554b9de89a46c699811ea0b274895621225eea4100940e581b7e4edb88e56ce2b5cb28d93c9aa1a06bf8204733bb818cd051f8310e6a83e0ce5c6d1ba8cf4aea17fff414cc635cb675cb7f9c8a0b6639c503f5d20292f7d94158f71019839a9d8ecd6b021d342d848fca332cb610e1afa53d2aac0973cca51a044fe4a36d369339a3df575132b9303a92c5ec7ecd0f7d05a3c8c301c78af2721481311cbe15221414d19ac3774ee36cdeaa553503f6d89b3cc3190fa5621ca46b291a408f4912aef614d8cf9af4f4594a9a6c717c2633677dcfdb032c11f70f333ee56e5f642e24a409e7fd0d0f75ec307a43de6cad21696a93a50df6ce7e4f9039e1aa4eb4f827134dc2ab91da0815cdd54acfee41543590c2c10423a4a0841c600f227c78b45d356e7d7126f1b7ea4bee29ac29506824204ab6084790f8362d624434596de5ac4b436b4bfc92827e0ac249e5f3f087088ef0ec03cb44b5f6c3c2afc078ba33644fe7dc1b232ad799f3dad382e7005465614c540c3a8279ca3592ca89a0c3c69ed5f48fe08907e7e4ff394107fcaf150b1b1eb8040d689f30a96d44d068d20dbe29cfcd3a57b154817c9589d213312fde0e09de028af6ec7d317ee49f5a9d7fb7d91e5a62bc8f9d37291e59aaf9fb92884d7c8b0104231b694c565c0aadb7a7aa995284611b066e0729da1151ecc1b73e2b4374011e9bc628f7280add669a4c6ce4a743a0ca5522c923bf24e2f5770e2c3aca3a0093a39a161dfce4ebe8a12bde70385f6fcdf8bd8a087f485ed2df87fe7fcc7c0c2c0ea1d94702aa7816f784c30b50590fff94d0601eae57f83d20b6bfda8bf80011bfa2c68ac1be8ea8f87c8a0bab41bab193aa5643cc154e088507966c3a44063bcb3d2054df471f943cc8bd4d00ad5a836e8351d236a343e6120444127fd002a7ce6ee2c027844e4305f92c35293aaea0b45b2107e628079b1c15848e9f3c053c26f2b1b5761ac24af738c439e7672c2a48ca366a381fd901713ebd23f6db05db3a308d610bf5433020951632b9e166ea5d0bddfb65a36d667baccded91fe0a0449104b79691eeeafe974110edb61df726edc00ad052b104533acd4b3aaa90dedd69c8d0163e3e51b60cf3a5a5a4710b2ecba0c68a3e290fb8b41c8acd83703ae9afe182ea05ead074a631ba8b53ad493d0c4c9567ae21fe1fcfdd53fc1c70c72e2e5fba80fbd300bc91864f1e5c3b4ebf2756fabd99eea55883b5392a7150f5080f3fe5f0baec76c3e192ec0ef67f7467c9c5bdc422b15ce5e0472c4d7890e454ca019b6ae1895cc9c6a328c3e3293b18319617046a7f9277d517ac349520680b8864dd3b2fe5484a85cb7330fcf93f219eee052c3468fd546a335c8d0a3019bdedba729f23104d093bee3b6e080273db3ce1d3397c35b213f62b9629d000f1d1aba7093be3a57de67f5472fae2f3a16a9b0eceb3d62aaaae7fd14651ad708e05b763912f0a483c377860bbff18ff483dc3864e4123abb4275b2a3d9587b05b8a20421ba24d56a60abfd2ba3031c7eb49e3b93954da692f3c8a165218acb058dbaa4dc38538eb7bf199856cfb2ce7c06ce4d690dfddb4520253f098f958d09cf78843c24c9f85064d29ddbb37f8658e14ff93887e32162b5dc17e818ef520fb73538dd1ce299be35c21128cb957c327a34e272cde7504e585c09b51c18f40255e3eb37b1c4d246e70379f89462f8063f992d473fcf49ba5ac28a31cee77e0cb3dc29967ff50afc8fd7f6c73a488690bcd6efe65bb7a83e5d62fa14ff3a030b77a7cd0a08465a85c8a083fc9e8b40d68b88aff128837d2e64dfd861b8fd9a075ecb6a577335072889a2544a5a5be86b5ff0a9633fab31a679463cbd7842670244352bf2e863f02bb4d63549646d9b2842391ea6aab5d264fd98af08a171f70aef3b974714e4bce98858708a7d67b120a28c0abb910a1d6e30e2aa4573db4f0b939bf4dc5fd9c8b90ccf97456d0105181b6c72a15506a2475d917f8ed3588006c9479535b5500205e658c503345630d2a91d062dffc8de3a32cfb022ef53160f9bed5201d8cf6b99469cffb507b57b251bca6916a2aa5b2bbc2d794008792402d49067f9f1c0f7c6564a540dd06d6d0676451366faf51ef85378a987200bc1044b2a7658ebd2a24c5c2de634f13fa6f41a9b6eaf37fedf80402987b8d732660b42f058e7318f1eca158d5ebe23be761a6f9f43d0ecb71602642bdae2d05cac04e4811899ddbc598efc0045b5a345bd078354b95d8ef212fb80e412c90d6921048f588949f77f6d3be11f90d6a3ce60fae39e7eaea1adc0520b7440369925250c2f9102b4120009e59ab2cd89af6399684bfbd841d6b1d05e0fb270abe3e64909e779cb35b4f300e83e2010264ef8c38006e7050539030a424e8ad71769223b0c350c208d201ac4c8d5c20a076bc821bddb2a2b3734ff979abca03e7e40601208acf627fded1a55bf8c26eaed63d9e6179a42f7b580705af95704ae157c7b6f09d53100aab71e6050a049daf445b5a251a233bdd048fca4c3f8fac71d4857bf072278bc34179f2f6f18d07490cce5d63418e8baf00297d764ba7d81b9f0357b0f4f18c3331552f18560a7fcd8097301f5d578d249c1196558f339f636cdcf2f08829291a2a00f243e64927c587024c05b37c565a21e1adee135cf6c053176800134082058eaee9b4f4e51d49e0e9bc77d207e8568604cabc3c76a46345778c10ea6248c83c074508de0297484f2a3a41d0da1d1ed7dd7ae9d8facad3b28592406f579ca09c258c4e80cd5baeea3800234fb04e5f83bc3861120fe2caf48cb780e83a0c4e07ec56df9b783cd741ed691c99475f7be8c92b06edb7f146b9d6c0a07b8c7e6d6c117c14f489e950f46f4ce44b42d01b4b8971e28fb9c4f2fa049c00391e8bee5f5a891ed167780eae4d95120f1852c7b6ef77a5d07637e5a0b67ff0545295a6880ab328dab17c8af6f94d5f5d5dd3abf29d2cb331cf5bccfea160a0039e13e8fc50d048bcd170b110fadfcfa2421214693e45d7b64f532adf7b354014f74abed3e26b3c067f2e014f00b27d58c6fab621562dec25d2c0d5b2998aa02096770fa39fb641a942696113cb9e35b7c65dc2a94437487c8d780fed65d6108143d7e1ecb67e4afdf79ea1134252742d8799db488a708a19b02fb6c9fcf9b090b33dde86fa0371db4ae9ccf1dffa75bf30d2413b61ae4fa3d0175b421ec810f068bf6a2050c3b4955fb16e8535601f51e5193151aa7e1481f1043f7c4470207567a7fd5d15c1f7a9451cbc9c294a94dea19323b0d62beadb56da2fa289e0902f6b71d658a2063feadf2f338e158727f4085c6c78499ae0b05a78527cb1db803bd5abcaeb862ee964479bcebd7776dd47f4a301db227d8492abe3c862b9a210936b2c756bf5ed4bd264b944122717673008d178839995c56f597291be8cf5200be7bf44973054c4daa8eeebd1e9da42f40cb6170828715add7e37acbf45e3e06a6ada9856f2475f10d85d00e571ca62cf9b4c869f5899ad95215ffc20640860e2a80e593046c088a57e10328e9d36873d54ef8b9e71907e36ea5e84db7b9850d6e8a856a433fef243c5638c025b61b03939552ed7562de97acac7f318435f808aeb4d6867f7a54c954cb3d0773580e8e2cc7c0baea3ed9a67f08954e76b4be007c51214f9c00a8075db744dc8ba6f7d03c8f4829e88716954ef7cb0ab2938e09ad4c00bf4eed127d142c6dbb2b05455b1bc3e0cc56ce7bee3c4765d5979ef9054050cf7c2b6b69d11dc7f3a55ff8eac2cc1579b6e511f7835bc33291875ee9044b93453dcf5242cc6190f061e261aae81d159f17c7bf546ddf2ac4137a726d02dbae985d733cab26450173c3554de10ce4f4ea42ac5a44aad959f74da67ade081fc0d307fe246b71840025e2bc95bcf6193b07d74d15253774a008f7100c220ebbeac577aff66bf8fa3825701368af352c13715b09aa829d742686a8a499350a28b64c58f77aa730ff080bc2b248438128c731598ba3bf446abe0ddfeee8b10d60d6d3423ded388d89e6e3d5500d2a09c977b54f25ab3b0487634f52c013bc0bf73e25e50328b8daeccfebf841c5170a558af7dcff85250dbe3bf6d7cb4e0d07d698aed4d21b4368e3b49a770827e8183a9d5c3703fde651a777fec712ac15042a5abb5f10076a5cc4926a423e6b20d61bdc9dc3300dd036c859754e03c57a0ad6b3fadc3a04ad875416f811c208172f781e54cb939ad56498050b49a2d4030051b794136155d7ca49de22b3f052cdcb2a230ae8c94c03652ce6f69b76827b06c8bf5aa4826cbcae9100d95cbd3839e06587e34972e0a46d5e04cffbeab8f600449c3c9b892c0a89607f06ea43eb55afa951c820fb343ec6d53172f6986b9c059fd5be4289bb92422017073695b44a0f03248d42090b0657759e6043cbf9870bef7c1317c1e8c5c0ff308a6ac428eea20120632a6846113bb7f9d101d7690b05dc332177a12a6260363939a171c2faa7c3d9e95112914c86a72cadfd8f381f0ddd5fdab86b9e86af6b31f42120d176e0cee2d093b0c9a852fcffccbdf357fe047180cf0534ed8056fbeffca782148cf926e74fd7e049fba091310d1cae446304d7f6d82a2bb188b7636ee4dbea5d6a7ea94d174dd1c1577073b04370ac7ef307da0473effca5812ff77709491525c99b454e7c1dbb80dbaaf0a07b476d0af306ef46ee7820bbc27c3d128157b67c096df6dc7d6d45e63e406758ff9b68588f07bd8b677cdda18236a5a80ad764b2be55aa0cb17da7aa1ec4a40bdd478d3d3b02e96255ea1815ad045cd4a9221e563f6c47b85ec6e8ffabdb5cd01e1cb637c909264aaf0c25716a0171437cca8211582b478b950e9a8af9e1b51ace0bec107608856bcfc67c0d7858559791169255f9ca39d6216499b4a04663f1071f41490d039600ab3a1170805c8e5e5deeb4ac03061aabd5e9a5f85b2ce1c12d6669ca870fa1667420c5f9a9b903e584d3aecf47ad6c65453b198e766be5ffc7d30aaa9a005bd07a014e542f6a57b236680405992568c5bc0cae038439a7344f9fcab2400c00f63d74e81dee5ccd0276ae8887200c28319a098d386a40d7c5f91569081403806847408576ddbda8a03247424886b8709f615948e5dc4d74f4224297de3d0274537b092a75395a3fcc19e868fcc552a68637fa258b62d96dce065d14fd3403b153afbcb5e0857ef46d5dd8147d576c65617f2c0653639d6c7cb45789c29c068d08144512ccb53015019a4306b045775d3cf97de6a2873a154bab3c5161110cd8e1b84e33535feb94ca7fdd2059aec71b3eba70134d7af9a64a3b8821fc8d051aff57926a27d8bbf9848c93d82c9cfb5fbee9f2f6cb208d06c7c032399fa70d621abd0b2e40b3e11e804ff8eabc790393fe8f1188ed70307b6c80dd8cc3f100db21a5dc91b577facdc2242bade8cd9f704ba3c55382481960ec64d23a9810040ae070f1e5bab7178e7159fbe0c09bfce04e0058fe5fd6bd47c4219cd5a2cb0ab4c9626d1215601c3e316cd83ad10ff872e2db971e07b51f4e6e08f6751ede0ced93a88eb4acf2849edd1d075fbfeea2190359df731b0468d0a12d3b78846008182cde0e6fea4ab150f750c1e9086737cd8689e5aeaf8db3c6eae20afab9080bf70b30d715170d59f22de27ba6c8b7ad72dd624e875544dc9b96e153c2544d0746f65752d884bc3ccad758ed80566b3fdda2681830eaa05e239d619c5e4e5a0e3c6b10dc6148d0b27a1e7a13464d051546c35d38f1fb8e3ba939b430c41dd90b5d7410ff587c4fe2d469a95e87f14d368a5b12d6e425f0272e51dafd27737e0fb00ff7bd86721e464daea9e877558637b5cbf75b38cee2cf0df04d12a1e80302ad97ba46ae1be834fa64f42dc8d34787c6eac1ec32f5bb1a2798e58607b2580b3392c8a6ad3e5a31f4e24149293275e678ef04bcbc5f002b98de4d5df6339e0f72642cd1df51a196e16cfe57c7fe40644d4515eb747c890b60dcff9b94b64e0e6d397a592e56ee0d418b907ec8d220d2e767175dfe645c5c502f5d84c401f80dd2c7b057e75b8650634abcb272360b326cecd7b1294439c3fd3732c4a9c64102c1d012894eff6127c79efca84f7a8aa44749b50d831c67b312eed7cb8bb47d0c03b0d497935a54df1ac30c8b72e5894f9b5a2d1cce78042638506984e7d91603a63aed1952ec174b0159d9dc4437ff784a38392e9a6440664784f28212d29802e4f82b75f8b82abd144738a5564eb2d10c5d04ec075199fd0d0527ae13d30b04266a1ab5f7fa15f79e336267774b36aa2aa5e1ddb9457de14dd7662041a7880c46eea1ee062d38a36438a5404a62f1484adedc139844a0a144b071c52246280379f76dfe89bd41edf52cbe2f60de137b0a589e95b1adda88f08aab62af49e80d37b637e2564af47e8ddbe9fc7319f314edb8dfd1579c24eeeae120548f03140ed76a27a0ba70c6c7b6b878887fff804c0b890d16971a9f95c4a92acf479e74085c0629f1cffc11470ddc1ac9301a6dfebb45249eda4cd74e38ce2c01491bda0d00466403a645b91fc1162a642e6b6a8706c8433939e7ae97cdf5701880388f059b906f5420c8a5f992911cf13fff27d124b634685a5e30379796233e914d3705c4f87b48b5308a73faf53dc46b4dc0d18f6fd545b629b821383e0fa3a315690b78482f392cbefdeccca76866d3e495954d6712a6ae83541d64ec1390a7b27307c472e1bfb061d3a0411e200b16044a70cd3abea8ee13c062f77243c1bde10409a2c79bf48cc349c8ff784fb692acea26afafe352a7f5f05863a61c4845366a03e361bef2e29dc00f0c63ed7787771f3b93460a6cf4f4b84dfd4bdf65f414f10d5454387dbbbdf79f35595624a93e35ccb7ae47d41a787785befb69b0605d4901752f5a4a920e9bc3dc14d391eab7f14e67d3161fd25c046bf85533a201bf5a071df13f82479177b110e6c6021e7774ad41913992cc8502cb7ffbf2dce79dae0a176059a07a5b279218fb92e701856b8ef53124e973921cf71656561bc2ea7009be9d80b166b3147c00b15a9d1fc180367dc70ae8e8a06966cd378eea10e244011d14ec1949d20f7eda9cf711941215b049c70c4ae7f66661176ae2cd9868f50d28e48f7aa52038f4f348996fcdaf43f0a17f573d1962d82fac79d2ba0d1d7c056bc82dcb19f5fa4e24be0b3050828364a99f0b5057b79b1d3bf16fc3606dc80e5a86f006af398c90d4a5858a0a933e43f8fe23dcf88db22abda73a9728b5a90c72ef5c5402ce70cc8c00b0683939ee30e45c6f3ab6b0db732ea058d19c0b24051f19bbbbef7219d8ab8bde713341d95e81c6838039a06813cdd11af60130740345339f2deefdbd162bf8045e20fb8f273b1a220e80e7d82293bac6960854f307a4ed6212c75b04a84680a016180f209efa242e8fb8976a22d53a48f2035a380197a517035ef6090440d6fb32836631081c1f993b400c825533c8919adf989205513cf737f8ea15bf098ab7b91505ce2c7eadd9d598bbba62b40ee28c5d1f4a07cd3395862c504c65fe74784a38a83fa64a6281386581e1e2dc230aeb740ccd01cfcce4b84f6e316f756de9666a5d0e49594f4b3a93cc4bffc8883ffda49b440bfd410cd3b05593e944860a15494d17fb5ac444f5f1752db73ab695f2f8673d0c7b2974c93cead899b31a020398545f27c2753efe9b224f841a1026c439bd060ef6e9c537dcbdf24890945a3386d3cc7fadf7f0202c7058f23cc0d938f7107d00cceb62b97be2b6c66b183887ee0a6850b9e124d44dd61f3447c9c3e5929a830639da4cfe878d471cfb3e29a0e0d31c0997c8464d04f5a37c5e707e1bb8269e03d703718560a1d4426e581e8ee6a0c2d7e26667c6cb7bcc91106e8946a01d3b0426c2e4ddf6e907ab108ed984e3d23a9fba15ac0ff0394627685bd3477b1b6302767cd20acd9bb903d9a6cc13578d48208fe7a5a32333592494b154a3e01252055068d358e844be916d8cd289130ae41bada5d5034a0481ea6ef68eff7b6bf602a21e5988a17cc638d00e86a87215557a7d66e7e664ad734624afaa3dc3b03108b2f93c11c0af4f3c983af5a908aae3dea6b29313b91c97f8d127201ddf64fa0b9dc2ba210cc292fe46f9f2183f396f8221354d2221984dba3730d2557995bf0530167347b58c4ffdb54a0f2f8341dfba68fee0b46b69ab95cfc71677bcccc2010963d6f16570538e2113ed1f67f23d1459624f0741cbf0b413074cb7801c2c040972361f72673a9266fe2f8559e046207732f16a6fa519188638aafcf8ce0d0683b55f3824a470981e7cf03a560de9a645337135b693c36ede73629f9c1dcd09300719edcea85d1b80204c72920cc087774c9d28947a7c46ba139da97980d90f53d4ebf42f63bac03f37f62144fbff25424e5bdc7f3af7e96694bdc970e99505c370e5451c583cb530414e46bad097f596c203246d77c377cd0829159dc33c09d2b95884bcf1633774df16136596985a6c9173a9941f98bfe7536c4a8207390602ce864d8c7be698dbf0657012961f389ba56c03dd0b0cce4adf88c3454a55015b411fe0f3e6ce93484882eb0d16f9c2e303ab08f70bd06c352a6af4d125ea0fc03723415224734e9b1275a6f5b4388e257033a8739e6f27fe5a9e373603fd02f03f1fa873304a2f7eb46bfffce3201720288a8364f7ff5789c44a5bc2ccad07414f8686c55dd3d538c16cfba102e872d1e202cd1fd81cd1f0d8351cb16dde04739e225f1c8848e2739e8d744cab69ddcbc01b4e18f5e1af2d52899c09fe0a0c48bd25157d1de6f3972bac8357d123c32a7867bc4d83565dfae3f3369261c60b6b2100dec16327da2e080fa723381cac50f4ff22f829430fa1706d56edd49f0f9ae6b2bade01726a5451c052b9c23864e85ca289f709b9c7067010544604a00b0ff579ba10483552ebff9fd83b284ac88284401f8c03fd88def6e6cd33a68d0144c752ed51b606d46fe00ac43251d2ebace8fb11a4b5ebc87391c955fcde5f0d59bd109f0df2d3f9291a01ef65f8fa99e70d51946960e2588a35ff11a7220804e6b789e42688eec25b1fc0cd54eca7f540ef289d5c10136df9887beab485000ef37cadc59deeea6a44950d3f923aa09415296b5cab604e279c3628022579580df59e95a6562c36b6599f304f0f66da24e111a48bcb73f9aa2dae6f17097db606b4604e8eaa39ad6e8bd151a3e806afe7103959d73efa3e74b13f4950231fda008772e5f79f702661566902b2e3073a74f6346c44fe30bcd122a644b391632f0a585e142707cec8a4afb9306b22862c7234c5263e65e608f2094ec4ba33f8a105a352656c805ddff9f20d31dc44c4f954f23624078868fe5fac12d446a3cbae08df7842054c1171c09a8568752b91aaf8e751e87faf14482464a1aaede5d2a106e0acd9f9e8f5e00d3495b0b588809dfec7dab5d0b8d63fc486383f9dfa9cc606297c0c013b99fadda30d7bfd62c88bf4357e687ef34da3f256521121b719490001cdc41989cdffc1c759c3b64a54d1a6c6271fcc1577a4bbabb93ba168f0930a76a1925c82f197ef4dcfa0e2de8ed5dba792983193336f60edef172af1477303415206e2bd90ac3b33c2398f3130fb1212599a87d56ab0da9476f1da7efae60392b5cda5fbd642ba7b11de1a9fe6bc09e2daa8c2519c97e9431adb8f883eaa0f4261e89818a67dfe5ccb9f7d13e08e06073c5caa1cc786a3ff6cc3961569cd0a44bb18c0ffed756c41fcc05a6150c6f08215016e176052034b4516afb32b1a048f2b9d91ab0d7f5e07de05ffe814cfd2b2ada678c2becab2c4bf1a76020e8007a2eeef6f54d927f62737fc3791cb741c05ff2b3243f1f17e521712066f8952017c9d4618c37eda2ab63f460293cac516cb377ee8b17c854df8da2d384f036e0d9ffdaab17d602f7b34b3e5f3e2df7b6d7362b5d8dce5f5ba837544edfaffe506c95d332b39ef24d71818390e7015cd09efe045d8463908c9584dad3c1736d3032ac13f6b82f6cdbbc5157c570c4aaa8ed29514ce6414729c96b767ef0e7e310c7f509599241f99864ea7edeec12a485db429c7a5ac37a275deeee27b82106a0cc194c7a9bcf2a6e1ba9a9e6af28b55bec610adc27471b1eee84d1a7a09d1d10d0791cc764567302c74dbb37d78b6c1fec4b572da8aa0eaf632d5a6246a16390627368f425f889a4b810020f5e7f56ac9c3e9e13309e9e2ffa6daa346e80e45074aafdeed13aa1a07da3a9135e17b5521692b5f15bb3ebd206869012fdb0cda06c41a358f74501fce2f2ed649ce2236468cfcd0eee3ca4ff4bba1681d43607c0ea57841bf0af3c9917263a3e06f7f1b6b66137ee5fee0626a399337ea0a371900ab5458176ea748a58a629fea6bd33d4dcf013eed25add96e614ab370ada2fa08c112057ae37513e820793c24a146c2f09d84cc703554c90709f0bd16be877e035410fd2abd17210e46a7db15de0411530e9a2042acf4ff86e6ff579a7e9da707add966ac0ba1720c49b7a4193c3c1fc82d8a3f22153639a911b43fb06088ee02b2b6e894ee189fb70b3dafb3c16f9fce983fa4b8f2fcbebd9cd040eab3a12c070b2756dc600bdc44707de949773d37e855a2e191927493dd79d0f82cf8bf890e0dbaf04e27e358936dd5e8b4121958fac4ae10b5d9a27552f236b94f3342be06f8d64b48d527881c7abb89d1b183e2767ba3fa311d8c4345d44ee52bcbc5fd037bbd3e3e6cb34a014e7b91683357b2860af691e135455ad0789f4f1bb572f60a1d749af29df925d9c86e594291182fd30124d139b2c11e1604f3dcaba65596079e5f701b9bbf99a4740b21ea476e4a3fa7fd8072bcb0eff26aca80a77ae64601d072dc34b3737fda37c604823640f60febb753073ca744be5be36106d5e42104f9d529f1ca168a74fda5ff366ea04198a1e3a735fd4bb42cae36cb2db9a65d00620269d8f9c9e0f5e453125cb1ca4747249faac403c3c10eb529e7e6a355c802c292b6e7982f5e536fdae3b650eede28d42df3feef7a4897d275ae642eb942058937741ff0213b09f18e3ce3bd35dc9d6454d347e76f9e48f1b86306d3115604b6c308a992ff8b85768e61030c9fabc5c3df23607db471ff83a97ce0b1898a05ff0e45b923fc18e52029ddb18c7550e8a136440940757dd8cf7bf0ea2582c60d269364edc87fbed8b591f7fb8ac8b507b7b9f8c6a2d5b16a22e79eff1f32860736a72aec2150a0e04f52b42495a2660702888afd4302326c4332f3808b281a04a91c7f9f895d6a83e2d2acf7a3f0e2a1cb12a5fa13f56302b0c316d195404501b629e708e4926c04976282322ca6812adc7beec3f7a262df05345bbbbf301d0b094dec47746c6ab5d5e13b4d3c6afa725474d019dab48bbd5c8f8fc187b50d0219d4be461a5f057801c0018af8f1f562761cffb5b36f3ce452eafab75b69e50165867eb78dae11d781e2185f6fefc5a359212a102aba93d4c9ebf5eb9257af04c2e4136ee9ab9407ccb953b8b18d61727715dd75f51129683adcf440cd196f0a226d42e79e94579d818eb36fa39d43c573f5c9bffb7e0af6c6f75028ae65cd0d3fc7d2c2b22c54817d51661c578dee5342cbd73158b3ccdedb09cd1bdc55c30e7e286af48f174af1f52657a042e07104094db8316f4115b7831825d1006c7a04249aa0723488783447c73da79a35d6417018c17254bfedcef44ec1b8cda53d0af04d73cdf9a7647f7bb658cd517a1f79b68428fd56deb00806c27a4baff38e0e9aed082fafd11753210896e84299ddb8f30c00d3cbb9910040695793582e86034c339209eaeab7d1cc4ffa6b310cae66a706a137d88595e45bcaa1938b604e04917368d12bf56cece99c7818de5707a8bfb6638c62035cbcd411b75169727008924f4c802ba99ca2c3c80396fb9daa3e9df79e24b0570f3bf3819895beb65b0abe5d5d93d8a5d071a63bcfaf54d19d341860de2e850a7e3082e4772fad9cf70d3a03ef0815fe52e5bdba9537c5eddbc04638723f1f468d9d1da9f1d732d2c706b10163f76d03304953cb8af34eae51e2193bfc80d9c5db027ab4048299a54200007f24b5a8663c440f3cb7a878d3a3d8aa896552653a94a0f7fa5a7b643cca0e015ae4afdcf39f00d20d4b523943b766e812096fad9a0a08d92255bce8f4b70b65662e273c39d047248a6ee895d219dbd69dd9bc013dc1491d4b0bb8084cf10774a16c26d1010155e196ee68f70af460b300f9542bc3a61f222c95d406f6450166f8b614c475dcbe8d897c28bd5df538d66578d8eb6c13629dd116450debdd0e24c4bd3588ab6cf79bf2e58bc983bf1552e967c0e62f19e494e6cfbc67e3410d9f8c63b47af3469e4b13e7b5429dcf78a401ef3530589e0c4694c71f37cd010133e9f168e9966974a6eb3be88d0a0349c82286fbe2eb838d62adfda4b744f00a54943df1559c60370fdb789b6b5483a59dd67e67173c7814deb93be2a0c18e0e74e171e5cc6e47d89ffd79f62288e0a88c737c3ca55930048cf7e24e85797b072731ee9e01f2a75cabca7cde5e1c07ca6bbacf47e8200ea1a42d74febf13b00fbfc4a75d0bc7ce7c6cff9d98723998efa5b7dd467bc9593f8d3079581b011c073207be3f020b62ae3042dc16164cfb0db0baf3a8130efedafb63cb8d6a954501603dd3b6c8639a08e0558ced9d19eb3bcbaba0b5bfcf9b7a0494645bfea2a1070831ceabb102505d420460ed138db3c899d7967ccc5275dc18d3ffca4bf93b077b23a2307da8f5a349d9cad4c606de3f98987376dd412ffbaab7ee231d2d450485b11ad2112b7e00ccdac5ff4d0528edc403dbf98fc80fa222a9a2cdedf26a071115361a13b89d6f1636629d54aaeac8dd14fe0804d596c714778ddb8d3fe10b88209309bf63164d8aa4d8f1f660ec3b20125107f64be1b6fc28c2f37b594209a657ba42c6c6d6af7a7c6b86ade02565537a61cb673c49924570db043115850e3b533ab427337418e45f71253465ac2ead43dac05a6ddcccee7c35df2b9bed03f032125ac29b919d0ba3f8d66ed069a5cd50c0a385b7837156457e38279e9f048aaf5619dfe24e6b6d5c6c09996966fa1058342b2cde0e9222e9d3589aa1d40d2d03048e6532fbf0626592be6f688451db989edb072a42ac07c4b373ed6073095760959b284b7303defc77d0a76434a9844a8cd33be69935bceeeea8e6a90c0c412a3523f8dbe38ed8d6516bc8461859940c72aaa34a929e714c49c6ddef1d0c593abc75fcbb4760570d757cf706c766aecd911fa1f39eb363ac0faaa51c8a0656033c23a6d61b7022a324ff65b023a3c36550f3a60e28c678267aa0e2491e05c745e04fabd9ea474f80372fd714e7ef7196f038c52571244174093dd3393a02b1570496ea8073f2de704698533c5fad5133b820b26571a0adc90233adf7a60c76f97b6c97fbd5e45c86f64bd56de1d9e6fcb14a247189e260665a7566c02501cc9cdb7d0cdaef929a9e985f7a1ee0397ddf6d28a48618d8816d154f963c720119435363fed37bbfe3c0ba90af5e9a48711045fb7dc74c48822ab9ffa4fd450799bc71c8cb8b1e707e5ac73caf719a79964563dab3a765a34e4249518f05120f8dab7510475aa30e26cfdfcd6a86a2b38bf2c5d53204e254caad7c1dc956230fca7d125dcf347f56b0d3a153440b510697ac5f8751195ffd1f98a917d609c10ab6d08a62d3687c0de185f14bf9507a7599a6bbe0086d5e908aed79ceb5afc308117cff4e47dde85c24740a887f2a3d2997c1d74b23f7250b984c2d5a5363a20070c8d95d471189b9986cb23c85f2baa5e97aac29e17e946e50d7b8faef01fa03ac2a65f7daab5049d6b776d1e6b341f99fac90b4a1ffaf2425c437cedf7ae80a21326e32aa74b612349d709db8df7926982d54fff0b3aef125f4c7106473f20573b55148efc1a62126a33c97640ecf4b049b3fda3e2cc9deb6abe35836a5a50edd40bdb651f0cd7c1ac894b803b8060ca4a10ca15810aecede6c986d112a7f0e5815fb38549123c5e6f8d8ab9a3d7519b92224106f69620483247540831f3e05bae6270769f83ff1fdfbad940f2a0cd42fccadf887b7192ed9951d4f394f240d6a0ac5aeec3d86c7f4c8a188ddc8a8dcb2e16e29cebaa6dce9230784588ba40374343ba5a672f37556f170914241dd2fd80a81ec1ff714bd2c27ca02e7b3c2039b7f467f72457064e7e0f39824e732fb9e45f6377a52972171b9dfdd298bb20e331c49243ce32986a0f8e4c3790ef46f5ea8624dcfa581f87a1268df8ebf3a05e8d2d779110cb9de7178730947d42393a9d6725a5e9553f06f292ed0f0229408628d43f7be7320197e4cdbebdff97000ba392a4ec801baf68eb2bea910e9870955763218a9feb91974663fea637048ebc7b2625d9a483a3a5220c2aeb4ec9402b3cc5e939ecaf0cda3c1138310a67523f98b809ec69e16c54bdf2ff5ba53a80e8408377384c963ee06c88e48e322fa31e5c3879df6618d4ad5842412f2a72a01c443c860a5fbe0d348f0a302a1184eecb2e1a1e38baa22f1b31a7a91ed1bca029e5f0a3e587e8b42765d8a82dc5ea40ad4fc48047053186204da7433cae9110d34a73c453fb712c111c25854c43a44dd694ec9fca15ebe8b3288d6158ab15505cbb12d5e4cffb57ca963bcf4ada9b9bf6c48047aa4714357eb21dc2b48fc050bc017dc9825eda51af429ea1eff540613a6408924c3b71259a0069e2b8a915200bfe8ceabb1f2b2dd302523fe467792f6ccc6b47e672676280f039742e196d70432c66d999fc777f18f53ebd227fa831c08ef29680a5e7f2e677bdd294073da0e67b145e0a8fe769add8693ec366dbd7e3a54264a4d1390c3a749aecac30e1902 +generate_ring_signature 725314957f1ae5b76076099e682196b0201325d81608ed5d68edb5e6eb319036 d6d0683cc049e6ed893f2a793d9c6dcd4217fe7e9dad8e652a4fa953ca41fe98 44 b38290b3af4ae97e97a5933d34781a121a18ebf9f40baff7903aeb430e7adcdb 504c71486434a3c8ce1dc3ccfc6e4ff67e5524c14af78ca8e9bb480c5ed9aa42 57151b84f1c39a7a9b0402b8f80526473ccea76fdc904a36e0cf56337027c5e7 c49a1a22756112ef113e059d3a3fd31c2ab2b253e7e210aa2243b84cee64a058 a142835ee2ed2a72e5a03244489047e9eac036380e4d1e6704231c2ac39aa3e9 fedeb2afcf36a2d22f5223c49e54f60b228f8e78178266ad4ea79c555aee2d79 f50c861b551a5d0e832f646ec9865533705253d64e4180f9b721726b06bba04d 0dcc1250d5fa551a3a318dcadd29b49fa27f17690f5a281607e4370aa53a8e00 f355e123ca75583ca964f35124830928cd4a9a0ec2f9cb9f1734b05f399190a0 3835db635901bf073a4f9bf5857be31951ee2a089d72cd00dc516786590178ee 847d8d918823ea7ef9d6174cf30c6bc545492de8432e21098b347eac8d53c85f 5fa6f3841a9284f0ca108239eb402256081189cee4562307d79d7bd715ec6d15 fafbda755aa5f3cd71333cd07cf3d367ab6f2870fc2c303014d2f89d64a30d6e 582b2ff60b2a75bccfa704d577669b4bd6693f27ab659a35239baae67ba15756 913f2b1ea33797f9d1a33b1378bf69d2b6dec3665583880282abf65d4158883d b30ef876230bda8a9c08f42ba82d61f79ec0ae87044153803b40cbffe6045fd5 afd3f5603dbc9d24439ced99d51075aad413fa7d6d01324ccba6a109bc0eb9f6 3ea39bfc753884e809173a3390d3b57d2da7c1fff9cddaf5db57cf9e9277cd63 59af7a5d6cf8647478a8fe4ccb9e38bfd5fdf4aff15143fdb24eb8aaf83c96f4 cdc9ab13cd7cc66cad4279c07d0c2fce46f4250f09daa4627a1516bde511e6c2 d11d06d16e145673211ae485f67055b8a6634421df3d9b8c17ccb7c30f40685f dcfe3b70bad0ccc3a45d32244ac05c703f57f41bf5e53feeff30bd9cab12fbfd 65919f7a76d63a4661defa4e05370951168ebbb6067a0738d69d0e7bf103fd1b c6bdf2f762fae08ce6b1ea018dc07f4794d4f1b6de8a46eec9eb7c1535736e1d 23d450bce1cdff80744a50caa8ff6b46f93a06d150f4d74ed6937b5837aae7f4 a488edfa68d7038f4274b3f7e168fee61d74f4ab3adfd65a81bf3d6660b8bf9b afac8c1ec76e38d45ff2cb8e56e6e987d3cce886de0f47bdba5de1613091aae8 fb41b5919c427695316bc68743a8db571fa14d14948771d5ee9c14fc323f15c3 4f253e1a524af5aea0224a0236ce0b1a2bfc4e38a6e0d9645885be39301678a2 dbcfe42df64ed8cb83371bd52d973c777b42fbf8e4ecfd6ed9c80dec4c448c77 29db43f97101a8f77865b473033d853a5539c51c96e1195b30deb53fb3cc5eef 68c6a3084018204b738f029cd5b44d385d565a1aa537769fc8ac2f3de2ba5ead 7f2b13333ed8ff43f01090eb0aa79b1bae611c36f4594786211be04fe85364fc b6e2d5aa6104a6cebba9a7eaede76a15ec6c6322e8dd5bf2c69d183c04fa8caa df959e292e8c4c9149676a5f15317a7fa8d135f4d6440e64939bff64dda06327 ab7ccc02cf497c6c89c713f8adf3e5a8d32d30d4915d9d93255ad9db987f08b7 fdd1854e72fb6110845c9f330bdff7334f5fd372f98283a96e26ac70f6a528b8 cb59376a871ffa3b48148e4779a07413d8f3ece4b93e2d5ab80316e6148e1e25 27a6a020bfdd2194d68beae510217e91bd402be4e9fef84dbf44991fb3e758a9 af2c75af01a263baf3ac14d26c2fa654f6f9a3789a7d1ba1425609f11a97f470 c7323d74ed4546cb59ccb39620be87e7f4819279ed7de4470c4de985274cf2ad a2bda9ff7bd32b0a36a8aaf588b1c546bdc658aff2151cf395d9a8dc9cee08ac 33c09bb1286d017fb5ae20e7a1fb7591ef41b84c2353102eaccde52b81e783d1 493762520ca862a27a69b9cf5696d96ef521a79c0326d315cd9cb6cd9a7f005e 1166f7384e3ae9a3b456f6bc46639d34485fc3f88b63fae8d8458dfc30fad60a 26 f01f64adb050fb4650f9be43f7b572ce46a5f6158a1732f2e2e29cfa6d0a38079f062dd91540c1632a23ad4b575baa18184f330b89ba63dab1ad5de8455d580b1faad52bff18321dd1c48288af3d90624422b1981739c7f39afd6c7e3feac90edc4e529798e72931c4df1bc7cad406a36a3014054c17da27f81a3fea7a93a201e66b522fc8b53c0bfd6a646fc436f65b787886e3813548dc8b77c633b731330d6e73548eb141ba958c8a0dd58012805fb24814c2b89111e685cfba98ef32c7042dd0dd8904aac6876a9f8f57d5a605c74bbed8bffed8630ca5d2fbde6b39930733c4597d0f74a4906cf848513d123ddfc80b9a9f2a7df0e07c291936a03dbf031a9be8407c38dda941ed36260f6d20084b28247617d6a9751727e549e3dfe60074fdc10c6a6639b125462190b78aab331e9629fc280702f85cddd94b49414c04362c1867408b32eb7ddeac44318d7a3815aa98e9695ce83dd44f18b89a2a8301b006b1987d02c4e22478dcc98f6018a3cb96b35382bd624fa1192a56112fed0e463acc273dcee086daf909e5be59576078267ad831ce366c10dbbbf7f414f106b6f063fe5aa36607d437f200fed767f2cdd7b4188b0cba257e9b854730a8700ff4f07499e3b7a042be35de7dff42b927e1b441374ecfa125ac06ead1df19c806f1d9b3192c8e86608b3e430e97c26d06d96419a5ed15064804fe6f0588bf8f0178f0c55524a3b80e8f61acc72f7dd6050df2f70721641d70b831f21e760d520422cc485ab602b6f63b6bea68933611c2eac5b2d99ba9b9542c8492f7f98a1b07a32eeead9e21d2fccbe1377e9607ded7e551885144923169a102196c6aee5903a75a78a610f140c6bf859c2e635598eb4a561a1269e0d8fde694db98ac9f410b4bd87cd5587b523d25d988730007eac4b115eb5fb4f69669aab8cd1f923dfd0ffbe43e28521f39e8864226fc7e2ebd8a1fedac100a0d58765169e2c40e86a90733f6cdbb5226303a62a2e6ee4d4008cdd998352f8770c4f043daf54dd19aeb00d35b420545d5af4cc086eac3915f3912a18c3c36b134991215e33cb7d876130af09ad824d791c5e6b0c787b38234b2af51f96a380ca8ee47e78cb795bf7f150e77b060d98179d4b61dfc47401eff8e607f492590fb27a485940f83a1e358e20b0eb755de0041fe9ac8b26048c7e8e2aafcb07e9b1d436fafbf76c1a993369c0e177f67dd60774d68c46a66b6f909e05d7bf00c7dd12abeb1f496f87d551eda037864727aa2565fae4b9f6575b1feb846c26704ccfc99e3c60ec1970056f0d70c73f3eb98788d7857c4c2d2f5b101385fee387118e447675c141186a8448e4002ca6b32e572191a452a53ff5f55455eb32f24efda517d9ff34c95d66a86a6750b0cc4f7c0226c1e53cd69277dd6a194edfb3e36d15ab88dbc77f5a5403e23900a3966884fcb8577fe1b14a0654c0d46abd7dd87bda7f5316e378ac49be4c37d00cfb6f533ce332f26eac3c29cd00b8a89b9194cb03ffbea9ebb9d32cd5c5b4d024b649120ffe7bf1bf7aa97be7eebec57bc8f5fee4dd7df8cd481075c17d8d90489d4137ec08b9e6d5e00aec5168c3c9ca8639b3ec089fc35e4c83f9758808906b5003bbdd212ae7925e47df75de84a8902f4f70144499c40337e454398c9170eb1d8568d2e10a511a92392d87389870cab1db1575c4cb9ecfb0cbb8e479e3a087e6ce076ecc5b406c6ffa58a6df3e42aa4f2c01eaf05951d38dcec5e05aaa90ea918b4357481e3f00d14220d8d2c7687c760548ef024de88c4b0884e555d970b0911bda3e20019ffab35d2a3786d093768163d88930eab6b8f0bdc1402de93046230b839c12f2f4181d0dd27e35a4d512064315e8ebd3881468c5fef44da9407de84d4e999d12daf51e3eaedef852235c2db889faae093dac8a33bbdc5e07c03825e131af304af9202ac44675059e4620de0e1bd8acb9eeaf59291d85ed7830c3637445d433a639e8b4ccc56910c92588e1a75cfc266badf62467aea94bf7004f1ddf9ada69a062ed0d34bbdeb6a53fed66e32d761157386758d7077273167017366d4fcd00d9758e9b2d143af4097502b90bd61919de651a54436170531eb0039ede8b91a8b36c05852e7cef7ff20f96a56f9abf7c4fcee2072d71a9fc27c071447943e1f8f9cfe2c65b8efe617af338bb3cb6ea9049c4e0159d5d1c6df8e00e7a13cfd6e9143dfef1d5214f399fd566aeff4bd87ae741d1a39f245bef16d0e62bf2b1f760ad3f1948b8d7955467c4d4e70727bfb5572fbd984b5a098479401d86f4a0b13c4cf54a923ef78816db6091139843786119989dacc04304268bf0522cde404dbbcbc479db3ede70f3c84803fed124a70b45dfd6ab3f7a9d770e50d52dc17e0d1593ed72a6ac8b534d00f1d7c0cae53da7f0f1907d7dcd42a8465035d6429b664db2b308cbebe1e11cd1b71026dc16f0d7a2c77185767279f5661064361aa63bc00ee9f82ec11682eb89d2ebbbad5a40a4edb46975fe2acb62972089d159877afbccad3afd8b6be179044abf8cc810729bf32871b03fc8ce210c50eb7d4035fd61b1e54f15313ea7033fe16b5a5d4689ad9588a8574dca40621f1044c46468a11bdbf8797c5389a38c4e61ec3ad5a72fb057a2c4b011cf79817eb0412f2fe5eee084f66b5b3d028657f3579b5427109416d8a3f5b2a622e5cf2bb03399020eb1e2dc14f82dc12c3366fa633a2e8110a84ea8b5024bdb27bf00c100d7563681f9cff360b591b6f7319f63e63c884626f33fc84ddbd18ef304256230d50e3dfcc5e8cd0cd3a70f5daba24746df00f0bfffab0b1c9be7532b65f0a590b831cba17eb2ba938708b6275dd4f32944df4afcb0b8642ddaa5a9624eb4cdd0ad49b5e3808fd15259a9f9947882f5f354f3bd57e315cbbc8aab22e180f752401ab7af69f4965ebd53f3d16e5eb7de2f2d1a7d28c1c107121f0b70a613ea2b107dc918908d88a70eccb4d3a073059fea44f58e13e10844b62ce04fdf1b19771043b972a358a06b18b87b6a0530c6c2a5aa34476d99326e507b565e35dd3849d0601d5d2151aa89b6fa46f5ce7a2f069577e0f8a858ce47f7c03b99d94f20d1f036e6c3322e6d78104e9fa5dd631a008e1a105319eed375c0d44a9bc23f0a792094cab183fdae7198c5dc3e308a725e6e77779a3e74a30b5fad9cb1b74791154098c6c388de36b5456f5f4ac89b2042323c2355188aecca9118cd9c37575e7e606f42e16867f3fd94de72657dcf5ce054b546c41020ccb360b1bcaa4d4c75a3201cbfb90415e0b2260d7a13b70c9ab7ef350e57657a8c0a50ad7efffcd913ad403e68ab5931587e539ace64a6e9d8caa006b1587d9f4431b6db73115ecc96d270d33ccbf30b89bbe45785e3438840adf1a940ec86d4cbab1352e023ef351859508e9467d72d0baa6173a0453c2a75357f97606e48076491cb0d014239cff25e50d501fea05fd8a520a11713c2586adcbfb3ac929ddad43b0093f2fbd5d3a1f160d5e9633ee2b519752602e51ce146c9dc4f943a9dd5a0bea027efef575c5a18904501a4237ff7a1ab2b107af58a26798a971193c13e3ce5afa7d93ffc1ea67ba09e9d2051b982087bf52a23d898a8e4d1bf412e6a2f1ba5ed7e4c83dd3e466ec074bc3f63932fc711b0defc517ad4ca4f8d38410964f15265d946047d854d190072eb73a4de876a781f6f9263242e93afbd1e01b8c5476e377a2e25ae736b4d30aa3c7ad95ab5cfc774d066d4a5d54b37a50c88c2c5b50c69e073d602db84efe089726edc17f4d20ce413103b6f81e8e072f2d8b32098007e72e344d388323c309cea34448c08ab6dea51e3004a3252091713dbee920fd4b2bd41890e5c0bf590cc13450809d7f54f02e880a9e2ea1614c1dc9e354395c01e9a16a709e6308e4074f85935c344701c7a1a4576633b468e2213e766f2e552359728dc87b22a2f709 +generate_ring_signature ddbea3bcea4bd61647ea8bdda5fbb18a06b540af697179afc31dfad45b309046 5bebe27aa6b2d5b2d0e9ea993d20728b662e4e1e883f3c245413c41bedd32181 4 1a4b212e88f7a656a67e05201cc1c8baf0bbe4a82a89dfc5a08ad0b92ee61513 957491a8d8f6aef195556230cfe44e02a33c83287267a9155b8635641ea6b32c e01aed5b6c9c72e249c2f255fd298959a79eedca06d418b92e278dfe47aef058 43301e8cb8f4895f84c2762db9c6f7854c1e03b303884ab2ffc4ed44b8da852f ce5192b1459f587230e4c44dc58990be5a0472df00d85f24605f219997bfe802 1 497b140d2627b38584056d35533a6c2b4d131efd00fac3b0cb49b9e868d80b09b49038e8c2b5c9d776e847c5cbaf31c4b48c37d222dbb1f483831367b4bf2103a661e7e58d99e74ef717848cf6eb77679ed2b3b4c0c44fbe1b894e14cab63a0c794390278ad59bbfa0f91d849602d8c8fb1c778462f3790286bfc54530c61d0554dc88ca78fcf3b8a79ece99d0ab959e24117a79ddd6df8d7122d68262166400710d37f0f40fed5ca60d12a88e5335ce5b316e3b0432fe3579f852d17390b00c92ded4fe90d6bdd6f2793817810ad82fd6316c3d5dae08a6a3c21c554872c90f407eba1fe8f74a9fe685cf9a950d5953967e6721446e9de528b7a31204dcba05 +generate_ring_signature 30bddfb6f2f84113c8fb27f210143e2dd1083e1f533620511f5827d357513b5e 7a452f5bfcedc40a2457a941ff8dcf487a0b16606d4a123d35971f3c094aa7b3 1 40c7cc1f1afe0724642fcfd4898e9f89d28b4cc043a91b03694906ade52c7371 41214a7cb4785e94fbdb22dc7a5b60c7c7db938b2a96531da78e4cae1400730e 0 fd16f9fe63f43db64dc324e3c3c111575871b91f1fdae93c6fbe57336c4474057397dcd3892af8ed94619c782f1040af9fef28643f079a67b7c87148c663e402 +generate_ring_signature c14ae3fffe949bc42d0af524f67f8daa47d7760eee258b46ad40eeae065d2e59 25d5054fb4692e3f22ee434b6fa8d7f1e107ec347a468446eb657a55dc1abb14 14 029f64ce0ef3c4e9bbaba5ee0b025030a5066ac353b88172250f1ac4c872925d 7d4756fff6d107b31393f9e018c93dd7f08e486955f0ae454345bc1dfda0b540 6baad0a1c0d39192432a1c840fdc66ecf742917cb19fa338ae8f7587ffa98d37 8c7f4be74e16cd450f05a65a0a6bd9e6d95961f4a68e9e2a327f6b9c1972ec25 ab6c4df024e7c741ff35dcb968bce6804b42c92c7fe59ed4d19cc2071a081043 97b113e73e09c18bbc2593bc305c8ea271ff52f2bcbdb41d48d62b0e0eadde95 3e56dd4d892e365b955bb4ecc0fc13ed3de2c32fa18c921a98156b956a64d525 de5bb60fb5a423018b3e7287988b61315e596f66e7d259286e97b5f229b46e94 2b23db42bef9d9ae329127fe97ff3d71580c7784d21b683a11e8dc480a9de110 c3a06b3ff66441110e126b29de92294a378bde72732422439d18888552cd708e 38bcb224b4ec871d5b078bcdf25676372bbc5af6ebc0e02bbc01afd0a630d502 3538386f092538afc003b91eaf1dba5053a913345136e2d3667b3a24ed8147db ce6d4587eee0b1f6f6d1b756a3d46d900aa43cf68cdc5f029169239779af89fd 5efb7a2d75d401cef30b220eb7e3612e5989cfad45ab8f286fbc6be2eb0b5d65 d96640eb0510f69eddb9568986d71c775bb5dd901a83cdf41ebcae0b87ba610c 13 6c79e9e795369aace191c02f5c6830aaf00eeb2d0ec2bbf6c97afd2842c72b0eaf3a257d6747b5750b2c8e80e01214d75412879129e65eea19e9ca51b3a56d0a70bbe2c0786ff70042c5af0be5e70532c4bf9f556ae6f6f6a701ac2490dfc909d723ba7bffdc16e7baaa45d6cda014add3837cefa9e07c2d2a94d047e5862d04d1d5c570070ec989388ea4d44364ea27aa53ec4c11eedf83758f400ca58acb0a0d4eb20141613145a633851b8ea16201d5b22110a2266a2c741c215387e9c80d8e0f9ed256e3c9b4eeb05587f955e55d75dd1aded253f06d6e0d04d2a3244f00b25e1003621b78852f0322b4d6b01b6d575f53c1dd15afd2da777db269b630060693d4cdabdca455e04ec1848d8aa7284ff21cc76f28ca23d802746d7d78d205453269094b5e447d25b542b82971b530e9d29a0062f41b89bd7e2fa65659420e2052231ae20c626305ee1cd70a0936e6eefefe45c6054ae1456adc7bd072b40c43f1bd106771a3c60a089de39917b40a1bca7ea2db73179bd83053819379a00e4df0660c3cbf2d1c4cdb782d9ce7b8c621a39274bebf683bcf2e173c11fac20ab12da734954ede4898f69ce027ce93ea727c44a4826663e515591ab7291f510b85ee8206b08eb23e248a4cf1946889ba4dd044c5f5aca34ea97a88ea99a54609246f3af9370417b950a0a4854c08a9e750d5b887418cd7bbc7ad8cbc0339880d9df3d2619df12ec19813d790f8827d3ead780ea4131a3908538ac673d348c8063c6442ea39ddf970a32ec99981cc22379353da6e9d1cab8e8e50884607a0c602e9061c65e5b9e672ca686424789d1c9dbcffe22fdf71803e1fbb74128599fe0f040bb7f29bc4e77d5a9ab59861bd404447bfbb67754f590caea92dd1cf4fc9013990b695f6e568e7bbeff770bf91470c512990400231d5b3d599b4280e93ec04af6488c19b4a3ca06496deb75caa301c11ffa55dac24b65410880469819ebc020038ca92b7d9e95593db5b18f3b21df81cc09c8a2573553d0f9db6663aaf4a0dff23beab7cdea3d884c5120e3655f250c73bd45deaca62f215d7bdc8fbd5c4029710911d11bbc1a761605af978652d0e5a5cdf10f72d345383a71aed96afa4096a5e30329f42a47ad0720eb90c71819289be796af926c1cbc47ab7ce55e68b0b47f88dd3f9fde92d51b90c17afc56603c6cb476b1a06621bd470d7b15834af0312879aeb61a51ed8d2a9811265eb7e8b0f3193960ec6c11ffd548156fdbd810b +generate_ring_signature 8f0b307ab3844c514790d6b082a73d3f793e73c03a12692f8485a17bbecd8a39 6e6ea400e0044de0c8330a49f45eecb4313752c045dd5e1100e7306128f9485a 27 3e4089b5d7f9a5ce8766a3010cccf1586b0e1ff49d7bc575911e9fd0f56f2fe3 87835d32ce44680624fe9893a5718d9c8c5f75d0e473ad68a724514e0c55a183 7fe011de6f3bfb7927aeb3a3ed5bdb6db124bfe8ff31066e6a6022b30598c50b e2e5abd0f33bfca1075fc47b274b97306ba429f80afd899199b55ea1855e25ca d695b86be80acad9427c8fdc010bd781bc80b301636d4a4e9c2b48e8117a662c 6127876b00694705708c06c13b6726b1e2da547663fbb90945242d0414128cfd 8fdb4aabe3b48443568c78bcc99abfa4fd0e08faef362c0eb80831083b2fa63f fbe710450e296ae18195970c6cc15571b1b65389ba3734ec8da06aedae31aa25 a1a9c4b56fd950920c5d946c9b4976955f74217494d993fa13685e210d9eddf8 17b5e204d6233973bf1f562eb108d8f82814c5ed96161bb558fc6a91cb6b938f 30e61c752b266615540e090b40d9a3eff8127a9806d571dadb3f37881270ff10 47c8d7b4afe493a8e1eb7e029fdd184df6c740497916c4e274afa508dde2be33 4479f06664d5ad8d62de9eb5b28bbbc40cbb7bf7f4f4cad82b166ababe29c63d 3960234df8cc17f955c05ffe419cb6f94d6d99b8ff76f6e6cb91a59285281548 7e5a805242d738ce6ffebae2259f9dfd2defc04914d9b789ebb35eec67972ef8 5b7b040a347b891b627f5a4ad1f4d74fadef77fd04dafb47ec6b376f608618fd b47c8cc9c7d61f55d18e4ea354bde4c129fe986e67755f3ac0a2117615da10cb 6356831d2f7313ec6ce735cac4bf5715435c2b910da8e7971b890761e843e0bb 2bb4543b473745f2557b96a94da73742056203079fade9369fd5a05a207361a3 4760db4eaf1c8f5ea191358e7934c938a0d6ba4dbe68b0fcc2530354f2a7e02b bd8e94774c41841d41839df5fcd1ca93eec9aecac932975c5df26363d44f4881 e89027ef593e949dfd548377b048ce8a3caca16cd12952819da0c6fe66a1b3ba f372fa408b0a6f79645d37cc35666b2b8defdf57d7f86311b649fdbc1ef10a95 5f4e5b46e29751f96e4e6f435ed4e60e4108471fd549191e93449c57d6358535 20534637b89407cb018e97f30880118a703a791e72fd83582384bec7915fcd80 5f434349514c399036434ac1d99365ff8316169eb6a248c458d5afb427be4055 4afd4a0e113ce4bd6b8eae0edc9b1f7ee8b3c4d56fe5f1f2693348df0e153e0d 238dee6077256226b8044633e9c66eadf3780ec601e07fa7fc358da9bf689b01 26 5f9ff00fe4a05ac94194b76a0bd100082ecb99d4527f6695e4343d82de893d0019bd24a0fdb9f93d32f57f561f8fadbf8378c23552ce198866545e3d8c5e58010f70fbcc6a681f45a59efc1c35e0b78cf21f215af8dda66491e4182727ee5409e9f9ed297a93ac77f407e7dff99ac87e293b53ef61d47ef8ac33e7cf3c2f3b06bed5e8592ace0a8cdd0c8b34b9c4ebf1f25df937d9719f28ffb70be58969af0b6de07fdab3b3cfd96bf71cc40856fcbbd83ebcf9ffcd11e04c23e90a5606870cd04207d9c39edef0551b02f7bd5b8ea5113402c79e44902e2f6e951d326d840259ba877a852645c95db0c43de422f2cce641aab7707f6ee91124e480851ff2086884962d87055c6af3f0cbc423f13a2a207724b78b891d009cff24a39baf920c964bfddbc79500db37ddd20a86c264ce5b02848f1880487fcd8b27bcf02d9a05d720ebbf15fb81199e7401cbaf70a4216208dba20e80731c459376abf8669b06b04e3a83e818b1472709f3c2394794545a306a4b3b3767c48dc3a6faa649c5056bdfb113f882c384369e2f6adb727bc9a7946cef0f32c8b48faf148178da5104ff53d76b8ecd5f4007a6c35ee49abf8f39f8703ddaf4f0c2747df46816b45b0e8ad2b3597f25c3cc12b55b518f32f6a5d3ab7f1e799b99c15b2323c62cac9d0f36108838b0aa3808a24b4a8ba8096679e04df0a245e66a07687b23025be5eb00172fbea7a4dc32c5146b3ad68f4320a6024a6162ed3fee48074a4ad94e17280c35c1c4a80a0da6ec450f98844d06a00b6ecc2997e915f9e9229717228476c6023385a43568e0193d74a195cd5cacb70655f2981e5b0eba0820c7d2c54f030d0b3471a08bbe8f995e877553e929b563a3c588dc26a2fb9e0d56a072bebd9e5c0bf1973715cf7f4979c7df0f60fad6a09b2e5671f45fb0e1c927c981fe11ec5c03ac5f336a284e55129225b189e72d5535afa014fab4135a450c90b83250e3e7040972372e9088f3e027aafa45b1f2a07c72dd34d684da4e0d0f3c3122274f0f0fc6952ba6d5206612f88b34ecae146a278f9b1b040fdacd66c865460a6d61b0061c747388bd4b77fee87af4985b4a209187bb4ff003770ffb5b97a371dc29cf01070525f9047161bf19a0caaa5365415600acbbbb412a58544963e98354b4830063e828d46d0af3192c4f90512497bd7d4426c24393585e33c8e40268c4d0440aaa728514e34efb27a4ff6139c55bfac7a016ca9637d54c68b598fce6c272730c8de30a61c4d4f1a20b70aac96d9631932b6ea54d40dbf3525a2fb01a8e533504560e95c2875ae26d127c83ac59bc851d8f9a6e2e8dd7767b1de96cc1388fce078159ad92b67e98859c7435c7f1283ba62fd2f31ab2c7f2ff964a2aada5d5de0f6cfe623499f34d44f76225277b85133ca3d9ef345362c809f55bd7da5b780103d1295a273c2dd679845a61f45ab5bf4f7bcfa3cafb6247d2596fd0ccd73c4207c265a7b0702168aef5c160b2b4a4181f28be1506ebc0608d213d317bba66930f7bcfadc80899814d8d1cea557e6bfba6b9835faa4bceaeab6fb9aa36a6be3c0971579be8fc5a276c9729a6d2eb7accda508d454089babfcc6635323ff9e05b023e10d8ec1b81eb32f957e80fb590c3bac818c4346887b7964361a7389e3d6a0270cfe65ecb137b012498f98238aa4aa3ce21e5ac5b633f91c0eb3a8d06f60f05706294084291588533456ce3fae00f2e8a7c2e401246dfbdf792742ec5cdf30eaaaf69a2ae7f2de0fd88d22060331ec8d4d6b07fdcd3b76d7d60cdfa4aa1f10c44d500920d0978f4b2b48b7417d71600c8bde88a2a27a7a5afd3435a66e1390645ec39246dd6604764a6746500c1b51b38fada0dc2d5ddae6bd7d988837e6506ba5a85f804389cb95409f611db128eda9ac732dc6da557986d790eb55d93ea00a3239ce81767d9b711a0885cc6ee6ff7c317a4c013a5dafd487c2a2dc02c9409fb65f4409b697727055ce3034ae493c9416360b672ef9b865e451eb2c093b30bdb8495d3124aecf44588df1986cc838784b483500e089aaed53a041f908b4409ab193bcb2b84772e6d329ed67001def3c9ffdb6f37a78eb6e7d80b3d8cc9880e9d7dbeb38b7dd7ffef3983d70a6313f289be6133d84bc275e20c3a5819245a0a6bd353267a96ee1ff9bf8d749d8da8e3dcb565059a7ec5dad37d74e8d183bd0a09f1ff6e9851fc934486185133f9a140698a6b93ddefeda94559106178a9dc03fa636fa66c2e8acff18adc1b4f907194839c4a0d7ec68b1e48dccf789aed390710d13405fae2fee90417d0531457d761e9f0dbe93b85adc8257e79bc5d2e960ecf5a7fee6a6c5f52c50a1c50ffd4332922629b990f0db2d07fe8be159987590d0622628806b4cde5fbd24cfa2c6841bb206a0546c145df8b942cee0af26cf109 +generate_ring_signature 65a5a286ab65e20eaaa8f9479e79edf4861685f69411e516ec8e40c927f83d36 5fe6ffe97ddce4ea5674c66cb851aba3869b2a6f2d32d5d5a40b0347fadf63c7 129 c86723da9025ff2e27deab84e30684f6bf1da71f25839dee6640d0de06db1be8 07db42128eda1ead245733853c45fe5b0458e031989aebdadbaf865246ffaf17 0460694735202f44fe198dce7ac4f117a0350261ccc21a861338c7a56ab3cb56 0a4bd28675ac8e217488290ceca94feaa4164eec6b97262e0a61daee88570ad1 02e53c5c07fd918a30991ebc7710d74b74a470cfeee26342dbcf1f3de4ff0125 85356ae893bcd924f5d5aa46cdc6ef91ec09b353cb4d61c704c29a64e44f9b1b c8025e3c58129d9d4dca062916670642dbd30bf8ac8560a407751bcb161feb89 5db7e1b4de79eb06aad5f4878d35288267f8e8583110fc995be3f1896fa7420e 1bf6c7f1b73e394f24271873c3a9935c8bc965d0701a93636a89fa744a79f46e 22dd9753f438935f8dc7092cdc4fa0a1285eca97cb7453ec2f94ed2e835dbd0f d7101b662f17d94430abed81ffcfbf3d741127f8061c7c892ef8b562011ffebf abf79e5d0bf84d4a27ec9f3dff3975375d24ce55aa27269f82e92a3a2e7958c3 2baeced77460a3215f45d02284fa13dace98944a4f758c6a5fa9432cead58f37 d04fb924c0f84c71f8113a30f89a3e6e00866a56235d30300c900136dc276eae c9124982f0a65dd40fe790ecff565b5b9cf29f73eb4ec8cc40769fbffdb9677c 0f4c3971bb68636556e63cd0113e132befbdeee44d2e52c950da99cd6dc03461 16164430e3ad604db3951559c3f54a24e3fad086e6bdb728b9ef9e85fa4e1366 b04237c1a6dd901b51cda86c1051ffc7d67154af2551ba491fb74e955285503f 4565fed1c20471f3e378cac468d2823fe1d0b82929d5f90904215812d64d1f43 df43891bf56af218fc825f2e75af54e44dd89858359d81364dbbbf28b358d26b 4fe79966f119c697ad0cc055a02c0d4ae368696024ee8426843bdbc49192da4b 4fe2f399a1dee88fc243db5c21432734923a358052c874d7911ae815c4c1232a 6fbbe5930d6b0ed029afcf6240db8103a2ecdd82014f40346308a6d036017f72 d0f58c12fabcf7a4ef54d1b8288aadf5bf8d306b52797bc549461459edddb7d2 8297c95e59d88133f0960ab3d560202271d27ec5f180f49859e347079dfef712 44a2e84e6f9d86f7a3ba1cdf5a883731d1b1c3aa579dd4514b689e1dba2e1e53 5edbe5072cceb1c1917357277ea8975f3d8092cddbd1b11974b0c52258ce0916 7d170ef3586cc51141c88e0d1e6c0deb41a507749d33eb5791cae3d83f4c1e70 0faacb4e6126448192c4bfe21b32bbdf38cdb6f5cbc44408907a8a31b7928e53 f98220a17f8a69fcb34a2c34f4b1e2db43aabbd853d302af5bdc05d288a1e9b3 8af0d5ce71765c4af456a02c8215fbfd0ad3c252c36765b2fdf2167fb4627e9b 79acfdb7ca1f636f335db31730f56cfbddef22dac9fb9a57bac1d0b8e2191ec4 405b8942e7b43ff7d85bf330e8c04719fdf42c4909197b7abafece390d0cb5c6 4b11dca40037484de37ea6ecbe667835a37a1d6e5c2b03d64a6caa479e026001 bffb017576bbf77919fca744992ecf33e86c98f2605a07ffaa83d8ebad16846c 4c9ccf4afa4ba2547a2da72e7015102f41be193e1da94102e9dcea9613726ee7 e1ce494a93a9ae242cae60895cf628e5bde7bf0e065d0d606d63e6787adbfc29 6e5b45072dc0925f6dbc9d27cc0476c4da2446dd75ea2b6d1af07a0c9a380404 2a25426c191eb249e2b26fb9011986d040da1647a84ea8fb43e4f365658e3f1e 101d195c6361479b1a6bcadcf72c13a4091601bd1420a88531fd67a241c6d9c9 4dc9dcde6aab549f31f8bcdb6032633bd597aeb753a705f88e0bf45d6a7061b6 2afb4f83fe66945a1e449404058f2482acf7f9d39ed02e3a4ee50e20aaa55dcf 3c8a67b98dfb39b1bc6ae6e50bd0991e0641370168abfb300ad323e0252bc308 1bc109ea0017dd736395812b9d4ad97752760b09f4b50d1d3c44e8a4ae95e550 49146e6e8f9461d9cfcc3b35e3803c5421fbe3127607aa345878d52bdb6be9e7 22e6542550bd58c0df24e47140c5f5eabb21d0b4eb4d7f7f42df45e3f8d4d404 1559ccdc2a2935c6e0925a69884a0782fe8a354ce3389e271cdd0d2f1a826bda 0be4161cd2648d05e91eb58519c662e0fd112b599b7aac29361a9021a632c62f d9414268657fcec329c2996a4cc091e883bba34ee54cc373a73beffca48681c8 daefa2f9f3639b6a671ade09abe48732d977da4babed965c57d6efdd8eeefa2d 96060f803ed69c9f5ff915aaae3c154de8c6f4f858dca1e274765469dd307c38 fbde6a5a03161613d0861aae0c0e2663d627a06074b44f750363e128a4b4e5b8 e9ca75bd2dbd54a2b3f7825dbdd72aaf52a96b8b5678a3b033e806116f8f7361 ebd05de12fdbc528be92f56ed8fa54152f52f82a23c26a7a81edb0356ed893ff ee99a361ca3289c498dbdbc135e3a21013d16974c8313ad49ddfdd2e661f5a77 8d2173adc20828091cddfe5f72b0468e84f9b64fa1e5c7fab5946c59c6eb34d6 a6738f52db22a81f3e3a7274454ee8c612c3e017c7b746c79215eab83cfa424b 99e6dd99f0f508c33a31a63b2df875d1d024f795b6db1895b7874f7edfce3186 384d851051dcbef1cc02cbec3c623ef78e4d130cf753c2f5f83696a2877e96ae 84c3e76cb6776939a1f9eeff9197a9d7102099892ab149a946f8f935c5b7fdbd 69d8b30a82696b76c8e543c226efa8ec1fe242158c65a87b6361beb225538240 1d198e2d776faef9128927a299c5ef761622330eaf16094e6a38554369a2bb37 c950581c2b5d3de571f5524e85e2b6014f651e3a97d3577b93d1b18a28d34900 dab3a3f80f5d050bf7aec0e347beed9ddad7ba901470745c21ea27c1a163c9e6 b744cac9da027a7d91d4ab9869bf201cf3cb3d44a0db3723a363b3025a04a083 6f94100b1b287ec79af1fe98e93bf9c331ac46e85a54a29aaa766e6bed5bd22f f5edbce02376cc648dd6cfa77ad2506749fa191d78fbcb4b847809fb525fb7ea 8ab946f77dda69e839799ae40491cc0e768bfa0928311aab86eda71331c17150 fbca6cb5df29c9c5182777f118f598d7150d10a053234a92a61a50d8a0b59238 7136511e438692128d2d46113c5a08f8ec12fcc2c72f04c6c0acb805517cbf66 37049db9f28133006de3588c4fff0d3dfaaef8499fae586366d431e57cec7fe3 6fc107ffb6c2350b528d8dae5212c362d778c1a6d1c8a772b9a8b056d3251959 d32f1b8f8a88643446b58f56ce86ee7921c066da40e069a07386fda59642a828 0a68451da5164bb0f202e42f361afeedb93e0c45df9c736d7c4024fa25f1f749 702a864a8dbbd5fee12b6483537d9ddcd5781825e624ae916bd8a3fa68ae8576 17f527da85f545472a25408df583d054e6165ab8d430107229493f2ffa3852cd b8334a59f8c59f26976e5fc994250430ecde9f9249c520dbf345b70d163e8729 82a51ab8a54b8ea39800592a774dbb63dccb3609534dbf668740a96bd993726c fcfb394ae58ba7c63ff136a4e8a19e666c93b1d184c0678507e6334e0ae2931a 472f577a07180e4be0c36170f04966a6941e01e6633ae6db0c68ed3e02000900 f0447a262f75890e181cc8978cdb6b325cf1e40d2ef860d9ab9433525a9c1c84 d816b2c4d180245d47ef5e8c3201fa867d8f58241267e4e0c8bdcd52d5b18f82 096ab645e61ee637692e5be6268c94bf69d5e8ccb33d09dd7cae97651a33457f a67758abb913e86e4dc48c2e374655c16a15ebc4185e7d87351edb07f208a892 de6cd246e32c7496631ae81744db677aa85d01cdbf4cc46babe3efd316023848 1f696d6adf729a8eb076eb07af6f2175f61824f1cb8865191614a812d42b0e41 f11701cb9dbeb8b3555773a48c1d44ba79cc0075c9792b9e1683b1885260c512 e1d6dbfd516a68991e19d0b8205d7190ac9f7cd6c09f804b0336d8f19069bd9d 12784acf432f170fd42d7f79cce0ffb0058e2133b7805fad9886b2ecd5c08865 1fca0a4397d7215396753e6ee86cf3a23e339de63f3bbd0454efa7f043ba0c51 f60719f4683d0b172521d3a796273172ab5bd1b1bf0639e463814116903a100a 729fc5a7789a3d94d2124e2f92bb2ae1fa29eab626a00918d0af769f835b1ced a4c73bc4bab47ede85903206a3b891cb994618b9718066eb9c3644ea74c99bc7 b634429e7b38432c326deaed45e3c06a69868dccd91e782ffd7d5582cec9005d f8aab072840d3a56cede78de71b0839d50f2b0923d41e3b149e33fc07477ab69 40a23d7baac6f26cd9eafeadfaf4e49e25edbd46f366368b50dd167410f2399f 107b3ad9d1566baa464bf89b9fa4ff5fca459924716ede663e117b6abc38bb8b 6050053af88cec5c2d2ab4a8689cfdc04a25b99c452d59cb20c80ea7a6691d62 61d67961f48992995991a843ca555cf849c893829c2b7c1e4fb4a15ef9c6041c 36e60b98067844734a71616e9ca4c28a77a34719d431fcc896ca6471c8ff504a a733088697467efddb040801766032328eb06ac41bcfe8f608d2bc8b67f666bb d6e263c16e1b58043e35f044c1ccb91944107b9b425757d3e63044acc902d0db 865d45363263f65a0dfa7a8a39466b1e1c7a99526368208897cdf6258d503d47 6d60973baefb02fc8d5459e7d8bb2bf25e0d89ad7e50e787a688c3141a93cc16 8582964fa2af6537136aa49561f0f4f69c34c2863c79256055fb3fe88d9ff8ed 1e3c6c7fe056d2e5363f5bf129c90eb44ffa7b582125b1704b2ba9ed3ca2c439 5397e032e5bce26583ac63832705b25e65adba3a927293d4e698aa70b745c1ce 480f00bbe806d3b84eb19dbb0dead2f43b3fabbc8063c055d04c017233a11887 3779c27dad04885d1e135cd3b51a0689d5cfc5fd04ae61007f13a878a9f577ee b297f0df31083528d508dd42fbb538c9aed23a8b7746327a02082ac5fffe6f37 f7861178e88632575e5ebc90d8744b0610bbcb1e9a0d2edead07e452c878829f d358a0aff3ff9cbf39b9ab0890fc0be9061819eeeec99530ccf6d9b2220277e1 30bce230c5e4ab08b00e7bd50688dbb0bf54571ffbf788938cecf443de39b1ab c2e21d4b3316aae2858f45e7b2930a327c80132fdf9970bea2c5b55d70769f2c 625ea9b92271c5c8d1d7da9fc4937c701273b406903ed8a2a23c3869daee5a8d 7a9e9fec7abfedd8187d201830ba2a9eb23334a2561b820c9bd762d53b0d2068 34e0c4de83a129ca3f5726fc31ce690f89ac423b0ee484ad4894f801ec3e209f 8823e333257c9fb76e2122b3845eba0fa046ef58faeee5031c80147a04fb5026 0447897545d652ac3e03475f0ec0c25ced9e31b5b6dce1faf95eb39229666a8a 07cfe65f3986c64afecf82df678b39aeda6a6ee2ce54880c1dbe9fbd18a39cfb a561b04e6406ef6c7bba531e7b9d39e86568ff0fa33978793a3718955bdfac01 a716de30b6a3b4d27fa1d605af3473ba330e96660c92a1c80baff2651338b120 d5cdba6d59eaa16074424d9705a924dc42260a121addfbeb6f51ef0a8d08e28f b5e28ceabf39cd0f89ac8fdf5cc98397f8c7658f704beb5e77ff1825763ec4d1 f4243bf0219ca187486d59b89453a30d3f3897c0c31c3e6f4e69483ecb5563a9 1217d475841d1f4c843b393bfe7372ef7ebff4041967ca9cc7be84f11efa44ae 260b8317ab9ecaec764425f9ea1a8db89c36c3f12ddaa80098de3dc7c8978c85 3c6a25569809e09918a7d8d1916626df085a82e9b4d3b71bd3103e9e5904d686 58531a7253c24c0f6d77ea7188b931962087c20b3104e5c6e1429c123f53d58a b8c675ef3cabc289d4df0a826f0118d54aa4240d396dcec48520b0a770cff406 92 265cc33773e8f8f675885b7c902dbc569ad392c1c801612382f3bb342eea04065fc7c50ddd2c9312745df5d88da3c9614272290694cf0bc022676d2eb23b00067f1cb0994d6f6b05d2674f3186228d2c24b45591f86a083f5aaf283826386b0039fca8b83633a78c54c66d9463295319f7bd20e314e85fc20b1f11c2ced7b40f65b0285093fc2a96920545a6f4db8c3aace0b53b348f8f4cd26a56463d210c0b103f12c239b59c0455b490fc4b14a8eb0c0bdc5e30340859f965272a15306e077636636e3c8110a6ac320d546479659d2771ac6c7567ad3e2a4583d86181e70d4e1bdc7fdd3fabd855a9aed2e476225755750312fb8d36a6fcd9c21aaec6150e2fb10e28fee90d52fa914bfa462168db1b4cca4fbc9cc5b4815036a831ba0208240f974d43175681afc3c46f6691679c669c077d8bbdf7f38f9301743d9d9b0a03935168a8be34465c124d8c69a5b293f3734f327132ba9b8890f253cbd74f068d75fac8adfa2cddc3ecfca6cc8309e8f4554728e2e7ddea307fdd4baf42e8002cc89691beef439c652969dda1b77f6188df0cca3272c6f93259243d40477208eb5e19d8523edb2046c34b8edb4286185a31a1ee477636241e1fdf84acfb050136da75250acad28ae9200adf517438c9ccdc1502f4fcbd29cb93cc7463696f0ec6f6ab06d509c80947e5fc1d0088f1f4b6cc93ee31e386038cceff17ea9fd009bedf172e946fbb070a1105800013e27c478bcfb246702443a232d3b9e012a10ee8ebeb51b4998669ae0a2ba516689c137dc383a582fe783cc894eebc45c0d2025e8b79ec1c37d359a710ec5815cd9e1eef3207fcbc371f28182c69fbf638d709629b98d657c09331d3dd3e109504850392f47b911f5d5c8c56cb9deda20f1301e7c73f4d643ac20a0afa6d03f51f60874931bb1c19e22dfaaf5f9ba29336c90a659e634f0ce5fe4735e3555b8d000b05a59ede105d693ed1a42bd4df3af3c80034ac956f34a72e7c8a7c441c36c713b62ebb5050ba8b96504de23857aaef2b087f3c0bd490ebdf3e3998640d5c25992774de93f4c87092ca31a8e99f2e5aeb0aaf667312ea9aefb8dc3e06b6caab6a8a83599c40e7be8fbcf233d263b701ed00f5be08c919445e4f1934bd947d84730547ac9c3356385f6b42fe636d227d2706206f0394024e7d5f87dadc3fcd90f2ef2b4c58bc76197e137bc94c4be71e9e0ded4cf93394ffc3b271ff672cc26818d1989b3cfe2dd032e38218fe175b280205333efc73762546bb99a2cec78e04edf65f7313bc2ce4fee9dce900da5b8d850b6bbe1740f86f996cfbc8f910298fc42869337526612d36606dc50be86c770408bc0153344528b91eec184877deeb66a30244781f816f157fc75630c55eed85057b5bf05f5a533bf4ebe7f316fa71161eeeedf5971f86669416ab28a448a1f40a8e2cc577e283f69194a54678f10f8bff39251fe1c9906c4fae2626f020fdea0e685cc657735b3e7175672b86d12319d6f2e3b9e560ceebd141f98b28727db90ef586867f0ab71c2a797e104d58c85584b755b6db0bc595ea3dad470fde59b901c3c7d2f7f3550ad8dba68a76eb8c75378728a5957abd83e76e8cb414b51f450179bd1e71143b8f3bbe827459924f4755ec1470748997c521e85d6a54dd8ce003af2f1031c72dd8b3a0b762659c4a8ad89f8a015fe1fa3601fdc7cb817cc762094345c4517a582bede2c49dc2ca0efd3d918cc72a38a87dfb94a40d13fb8f870595db230f821b38128a950f27cd6cbeefe44b6ae5d69eb855c41ec53ecda4df02dab64c2ac62961c4ae1abfd5adade0df63e81a5b06b0db7d7da51207bda7cc0d646338b724258c01e527010869a3cb7a2b2083c684ca0e138bf6ebf5457e800d57edd6593ad59c3ab29a332e406aa412052400f7a376ebd2e84af31ee5f83103c80519a27e4122474f8da153d5a8a1964215554b1d2e6ed155cd41f748f221008d80a0386fbf49f2cf4fba206a9eab19d126d8c76364d4d5c96a3a809d88690fdef26a611de7e07f6232596614c30778ac53ff6e1f972f4a22b2ba726590430f7112b5477870fef08280d2594c7b1a3c838c2487493f3831874b04ae8e262c02e541b0895a5ca4fe7ed4bc57b39c74e172fee1c2d850fc7a93a66cda29642006e79f170262aef3a0c8b071769a03ec93e1ac5cf093fa1fb86a780d32e946b1026078880722f3cfc4484556059c773e73451cbd0c8e33536a3760520873d7b709b9811bb2275c20af98f619fd506a713bba9123002f0d273949c3decc9b7b0301d7e4ea82518404082d8be00dd14b95d809313449882fb4fe57de3b2b059bba0d50d339cc8fec4d3f86f321db085e4de1958922ebc5ba5ab0aa412794611bbb0e4bd66b2491421698baeac7a5d31460a2dd5f9424fa877b2fcc883a122fe25d0458efa2fdc5bab93689937b6125959611f2a5e23907383b90fe4cd6b3bb0e13078baa20757530607e8504e791baa28756119d23e7e297003427ba634fba7a550cb520a55b6002321f2c373231e903b0043ea67b0753eb3b532af1531d4eda1000e42e54b35c794162fe4168a9fb725e38a204c89175e10dc9c5331c2acadec40608e976794565cda35e8baeb99144ab822d19ecec11683509aa99ce1c01def70139a14ff103a069931f3fd3c6af739fa09f566afaa47bf8f594ada93b20acf9024b1f77fb0eabbb6f2e2851ec10adf7b553c2510c2b114e0a3eccf85a3432270f7cabc434f0c2ad10e825d753e1ecf4fdcfc63e7036803641eb3b27b10cf8940c73d01aad67d2e4d5a118d6e9a20b92d74ffad3d8ea49eee90e4ebbaafc2aff04471a239c04ed6d84365bcc6470ac3787670dc3f1c827de0a88f9e0057876ab05d07ef687d077c4aa8280c0b824f00e0358a7be94b322b3c72cbbed9dc4ab280dc79c9c9df7f0eb21cb3de02d3adc6565425c1e05fdeb28978360ee7e58da9a07ada373599348acbc308d2823dd9c9ef5fcd3f12877ac7a64051060a276c4da0a77c4a983a8b66cefd5d3e1f8ea704a8d25b745a80aa7d745408093cac592c507aa02c93c5469e07fce33041bb2a08b9f3067759890daf487a60dfc042aef860cf486525b1d623cc6c6848eb2ee95bd69ee4d6bad6015b88c027d54063cc69f0da7612c79a5e2d053f5d2784fca726892e7e7fd42e6e1653c494660f2a0c31f0042211aee84d7eddec65f608e29f2ed27b69c98e662d115878b58ec1d1833c406116cfa0f87da7e73133490e9d64a9e48adc2527a4a8c96fb95ea8fb42fa7b6099db94082d6deda23bf7052436cc1ec94fc403a7ac7e0288bcf27d5f8a8999a0852cb23c90412060fd83c8df6d3ca7472273e3c540825829aa4658d854a93b60d68b4d48fa2072af03ed134448c3f84a5a0c9d6651c0643660842ca801b16420a46fb44ce678b383ee44ab633e89352e85980f707e35e2f53f43a9691e1f08b021f5e50f79ce241a3a800412ed2bb01db69acbfce6718c45acefdcf0f1b54f60e6c89d7b2b1639b1003d6e3857f28deb70f9434ef50782d36c935c4c7d8c7d60b4ace2816e0f4a3c242c88aa1a51a07f3bc344039fa520179546db1187fe33a0875d55266f6e3d216c54278c4cf9d81463b9769833b56cb51091d2525b1a041084bdcb794389eacb349768b9ebbc2cb29536dd193e95c867154ce7b9970eff409972ae2bfe6c42d8fe7ea251a59a1fa317fd6f090bf48ce5072fa5cafa0feb10a3ccd7ee2f7a3de166b39fdc7d7154759680a68fad5848de49360c62233d52508355b1c6a0575beb011a82eb093e1101dbb66815c1f81b4684557ccc3902298033d8b70115936842c0a5a15cbd3329a7bf608087ee52c1da871ffcc20beea580e9870539c824e5c4c14f96db84d9543aca4cb7faf21984b7adb46bd557362080806e6dd5f324683890c97b726e42a153ab6a7d6f56fd590ea6626fa4809538905a18c2111cd82ae8fe253de387f163e2f231448b46979e2ff22ad80c44d1f940e2454ad5f17603b879096edbe4cb1066d86d2e2086d04d884ff62d93b0e537c018a0e42d36b3e3095d60c177c0441611849bc23f9193b7abe1289c23fd8d3a906f79aa3250569e1e1655c9254960a015cc23c0139d9d8a85e70ce366fab470e08a595fdbbb0730a347f5d92e22faf16f5d98c4be26f5dd2abd8d88e99e7d77d0d43b179205190529c7893bee2fa185c2e8d9bffdad1691b730a3b8efc76a62f0baccee97a21a9eca2e003d7844723f2d0a041a535ff89dc336c008d9323c72b0b65e16c5e51251dbeca5c15c7ec8e57af3f5c28be09f12dfc4bfc81442dd107025fca9c28ffd1b6423df5df10b2b7e8f9944f32ea7da93c73aafa52aee761fd01386f9bb6d8a8ca2351a21b41aa5de3b7cd8d99100e6a122447df92ba02fdcd0af46686ac3dc1e3d280331a3586d86b55a856d6d4900ccb1f94942af43d3ac60416d80514223ed19391be78aa403715594f530a1ef2855d86dcc8fd875e7a4e0b18d7c6af6ea79aa5c53a33875650e2b3e2b3d2b2137bc022ef78ad955d128f0a2a81b3a33d1458c14374d7ccab6923743f3042a31bb85cbde1e2bf83ab614a03eb9f89562aec0202dc34e7b5c5b77fc02cfadd8d344e3f5a44983da73c8d140f6813063368a695d03e3be200b3dfa483813be4e38f35f3096e0acff9e6cf2d0016c040d0140792dd9a5a2a95824432d5f239146023e7589947285a96783f0c0e1d3d640e75a3be9098175e41abbab9eb554e432cfb16d5ed38aa2739f346e809537bb7d5a1f0a84d536f30f1f40fe4f2142d2a314b08172cb04998d0acde850f14cf76783e0e09e0413ab6140a8f1a79a0a32a7a90a02d8db075a4221182a9074020de16942242404cea4260555f63a3170798cdb713ea8058b639f7899e130d3da5dab6ac958b82d219dc1f5f5e9e9e39c98cdd8d53f941f3b67bb35a8f210aad905eb849610228bda9b6da5a76ac99a8dc0d982c64e0572593ce4cf8a7c10407d9e43ee5c1f8f779a4732f1446dc100de1a3fbe35f532df95f9a7e30f8a008457a029e78f6ceeb322de8f24481a5afdeb383336fbb6d90b71fb55179326307ff7a43fd3dd7a0ef095ff1e1d84a23d33192b5fcd6580cc3526be642bfb0dc0ea1e634b43d29561f9116fb2c9bf563fc4544b37df57413d386c823d282cdef0aafcc826f14db11618e94cd8a23cc432ecb2a9ae4e1e59e6d68230a79b96f200af2adf73bb59c7ff09c2af19693beff22d97e756dc6150c3c3a9ddb585c49e104dd82b64c8007246941523fb94f54e1295edeea6ca71c3517ce522d7a01680807c4af8eec74cb0705657962aa3d0aed3e66a29dab6069e483e68fe6c019035f0767a1f069dc359dca1b9c3c0daa2be78e40abe4338d22c5a04bc207b9d796420cf5a2696214a8071bbb27476554f22bd1a0321407c9aecd8d8d71037f582bc4099f432d5aef11b85f4f1b1ab2bff407f5638bf4710a176dbda2e6afec42f48f09bed0fc100a4035de7b031bdd765d1accfbb95369f35fd5111499e8b11c131104205840cc13728a65da4660b519ae22fe68e21de947028571cc42f82f1d199c0c7f1bfd14627d5198df82e27c75b3464f0ef42bae23822a9e96d390f955b89d0e5517767458b524495cfdf336c068cf0cf2b3434d41255693192862e28727400a4a474caddcbc7bd0d6118fb84fedeacf78d675f65497c97d4403901c163b980e5053582d1e7c029b6aee11498677d81c8c3c099d78f775942a766be6fd5d4a0b5d548a870d4b80525cacd99a1b582a1697949cf6583092bb72fc68e48710260b73480c0cc9a4c8ca3d31212d9f90e92e8464410feb191595a034aaef949fab0f1af08ae18d5f0774416baf24ffc7c19981281fd1255c0285a7fb5f8290f3080a98c3880f12881bf917f184551e3b71cdf50ef20371d79b3d86644ff7ed3a4f08e65700bf8e1338db34bd788f01637db50597543369bdd6d3b6cc47a1f5063f01e9792588c9e7b1f1916a2f175c4d8f5ed5297853969772178bf9889d1dfef70c4c1e160b4fb981c428aa69c9dd00c4613d7a595086ed755bb58c4a807e435d0341d6f15e8531e9763103033e682d5abfe99ea8c1595a367aa697dfa97eb3940ad339d4702ed5bf60d6f4bf6651688f490939a04e867600ac2a4b46037a4e9a05dc1c7e8caf119dcad60984c317c083c375d8e1cbdba823d18210bc92c4f4550b51cdfffb0c2bfbb24335e7bbe37fcad11329e56ba3ba233863a6862e081f950107f90723cac25f14ac2e13d1f868d2dd0f1f949fb5cb9ab7aca89d0922497d07e2c5784e0578e4480c04f1208b67ea924117f911a4d7a974ad4037b838c2710013902389e241a20fcfc20b7b9f487b4b717671f0f966131ef0170d328a2c4f007b668398608a608bc7a97860963ba31be502f449159abce2047d5d60f632c40fb75aea128bd6dfce6f37e4749eb13b37592406dc4cb281b52af6139428bbbe05f00be8135cc17fad1c1495baad496fcac34fceefc5d0c0f4fb5fb80864c2910004d24e5ccbafe9f1e42495358a6e63ad61de372825c965168b42c0c9f0429a03b58544e9b7acdad2c70023ae3777d6b0ea4999b71b03a4f773c38d65c2b40f03a5a13c587dd1a89c3a75c1e262b18efd365466fc0dba48817f10a8054e7b0f007f612725acc9eb8cc9595308ae0c10d4c0a460d9038a12e469b6ae3d110a240152d167d0454e9401560070a91797ee4791547679171ac64acf09dde5a783960f07470fef8bfc9f06da07cba248ec24c4bd2fc1eec410796f0ae895b769730a072032b952452008087f84146d890f9f3af18ab51fdf90a17c85900679fa1c89052ec146a18224d834e821102416d4178e5e921063225ce7a366a3b5626effd90794a6736fb4911d02530a4774d349831205e4b9030ace7e369ec20cb712e77300428058d56dee1222fb0d0b54c4f484cd74e0819d4625e07e326f585665580f08ca0289b2ce2e9eaad9c91753b4fc0a7559d808ebecaf506f434b20c748a2ab0d244159b3b9e2e36ec99478d1cc91b1c4f66ccc2c309daa5194c3fc849ed38a071aeee0aa3b8dae575edd6fad0f703e48bbde0c9e80653ec0045e424905bcbf019961405ff7fac7a987fa0ff813bc340cf35e29bac54f7cb22c42a05d6cee9d08786bf259b27dd2670850a58113d31495d089f805449f801166fa03a01d657f088a50363ba29662c18a329f37c8b2ec2c975ae4a53bae86039ea0d91586b27a0c78634bc53e9aaf4022229ca4a1fadbeb828f83b44c241c63b7181549eda14c07104a355e1aa1679ea59ade94c73f04ef4a106168da2c41cf213785b023fbfc091ec961abe67c9ee0837af5ed5380dacdd64501189b6ebb461cc7b530835d1b01563ddf2a6de78cbbcd2852406d53c73870b5a96bd245e5eff7c2a49344683c04f7b993a31ac8df4984a986124fbc87b3c93abb155742b85b1815cb8b2406190f02f9a2b1556e8fc0c2939f4f494eaa7ea8c5c85286ec955aae44029c9ceff0086832d9c13283d1ba135cdced3a85d289177483bef407150e0ecbabdda7c9770b0757e1228a22c2988863089195f709f76107c66aafec6ccde5313754e0a7750bf17380d21d75d882035afb80e4e27a53ef81878ca62d7c382c728f2ac1d98f03b58f1f58ba119c77c745f803a82829ec064b0e266d45523b4970d40e4cc2610e91a3c701bbc8aef8b2c70e9f302774401f89ccdeb87206e5e7e48f5dbd815702f6d1dfebaaa5d4e0158167161c58232d3d7bebbf3c05d3c215e062a4148f37031056fd65266779fbc654621eda2be996145763d511aeda0960ee44d0e43255031f341f90d4b09d476fdb18d7433893553b8ec5e7358f9db899b4ec155cfd6b0750801db208e8f0e41a4db18ef90a5440933cbe07752e0039cd793f93d63e0d0b2a2fb79f27a26a732e4e508a445b05dbdd60df873e804dbaa4026dd573fd1001aaa8646b07b4bfa39243be9d94b1bcb9bd544b0d748ac2ac2311325564d45e00b12054d0b150ae0e56a07910fab1ae5763a662a928b515bbcae8adb91174e801451ef43adbae9575938a3d80071c5d0efcbf533b603c6915c4a2b2e80e4f0a044b09c8124d61805d8a641464dbad3b3959b8c16a6425275f8bf7ed693401780dbbba9b3c8895a887639d6073ca1370a567b09d94b347c8ce729c7d7ba79d6f0f83397b624ea4e63d4064cd6d129980fe471f450ca28dc84388e25607931b470870c9546ec75220f408ada0a8fcf1a28e52bf649533077d031256d5f7253803075e298782b9826ab292d3c85bf36934d39772258ab7039795b93e0b276d07f9059168680f9b7779f8865d62c7e1a98195f5229304cd401c9ee9362a963f22e40fc8bd4312ad2aa9e7b6025faea230d10877442d31b103b16a2806b968d4b8f201f22a9b62d1d1045efe0278e77d23b0078b22b56004b92e5311461e7db9cdc30d1a3a596cf944c5e153c5febfa59250d66b30d72533f3e9417e69c31757a86802c4fe4dedf0d7291591e9bdd61a3b66aded0fab37b21309ae394e52c5ae83960a4ec1b1a2ea7ace221cf73b557eb755d870060c11461e1903f9e08eda59d0f10021bac5b11d6c3a07e8fad520f8e3fc9a5a572d9421285f294c9ae839831d7b03830a0e1214742c2c5690bcef83a8a378cdf21689b750bf0cddcf3c78cfd15d0727ed195cbcb0d9e3e883ee5d2d28a848763e87a727d2056bc62d579614d9360232e8256724d4f53a9a771daac6d77102d87b38ca6b73dec80b29a9ce74be6105f737de75bd2913be3f3092e2035abaea0c8056971f7df4da01e239faf5f2690e5245cc03d363d34a38fb2a40cf590edb3a651e00c813cf01af3bb0ac5ebd85020096bb98cbbe01b05951cceea3366d99ee675c10759c7c72d4c931c2a180e70e476045a3107ac79e910ba2aa30b18cd942412ecd73820017e08c3b1f61140b0a7be7b5305ee1eb4b67d5afa6599e62f29e64c03825ebbf9f865cff1c77d5350c28f1386742a6a4510473b46f971f58c52c7c6bb692bc742b853f26d3b135200ec37aec1af2de51dfbb9c60cfcf6ecd146e8c93eb45b9bdc9aa3b500850c0ed0e4221bfd02e39bdff4671081a11b61c959d8451bd7b19eb3d3d4aa3b9fbe5d80d460c16fb2de6dc51620cee36a4d75dd141756b8704fc2edacee36dc5fae7660f41e1ca5ced77f03d85d93858efdc7539ddc5bd5b80142b8f7544a2cba9c57c0ff923b49fe4e7696a8415117cdc3fd66c6fa50550d3caef62d6063b12c90fa30f38159f7f2891c25d9b91782e8b8abb7d1232cf02ae74d3e9ebe5242479830b040264d89bb6a82c7c76a372bb2ff7623fdc399b289cd71d997a63498df79361051c455a4ae9f14b5d93f64e4a256c78b38200d3260fd311b103d2ed512c2f7d06ed669504a316a256fb3c760b232c8df121eed55682624cf3847853d72e974e0534ca0b443ae5df327c9c987a923cec6b2934c6052f2e26af52d5506aa2afe409457177e28706872e84971837654b32c4f042497c0f5aaa25da09510f824106002aacc9f1df9b95bf23ef44d7691a1267ca263c30b0d5077381007055333f2106bb3a5b6400bd3c6eae1929043bde8f1c40a61908e1d6c83e89aecda62efe1e0c21979ff65e62de1020ef855f9b487e9648348e1e3a57512ef7c2df99cdf5b609c715f8d6cd8ae41f70420fdf48c125fe662c9d1f16d6a4b97b9f00e54e01d4051b4085202db3b9416a2c26007cc527be8f617e42d67c30c1aee35123beefd40655c6a9c24110c8f9c551ead3be22cd6b9e0c2624387b0f6eccb6fef0c4f85a0c72b6cb1e8a33db206c1ad4ffc2e2224f8f0ea52e54bd119d2a4a393498a0c501b99ad5c5734a3bef8fa30ae14cf64df2376e77fc51885588416f593dc481d40da219cbdf549cac9d748cd464f70c38fb5ce4a89d78b9c9aa7012ce418209800555a1d9db093ec2bc2bfbffadcc17cd61621bef958f5a10d47bf7f2347d0e89037bccb6e8ac833bc52e33de2867d06a113a014962d6ed943c71bafc99399be20c3cccc5233059714aedc2cef52739e5cd285ce48d7d8758d6f455afc7c5d3fd01335f7bc97841bb4b45d35ed27dba2af5993148099ac6ea0c7f5d1f76c91c390c04903fde8544891e5062ba94603f0523b62d0d4269517b3558bb73e7a8bd8d0c23850fb55267c5314a464399c1e17cd8fa13aa5bd2fce7e074387b92734cd702ec5b6feafc27efca2aacf2c5c72704638c2f7c3911101c1a0f65ce85352cde0601ca21cbcbfa5a8f8470d371a8f3b1386056350e14f61c0115aa70c6e8c2480d091c56c4f6779c6da0883850d495398d2c5192ac60a200c7738daae355a33b09c8e1f3a6253980b06ad73276833ff65d1c56b1e468908544d7323c14c85b67048cbf38475b5518ecdef7f7d1dd47a069f224ef5a5e3c8ef84b7326f64939f6023f3dc70557b36b6cec3de86be7f13c11492351d1a3d37c56e754e48cb47f9c0d4208f79607e47415f5b45f5ebf26a2645efc36a0529679b27a6b75d14c31f20091377bad8816b534120923f9184b8419a9d74d7dd77acbae474c2716936e920451730bf3d22838a84da2eeea0d4178dceb7242f80656a6b3b98c7701bf5e290055c7f98315f90f4f149d2e1edea84c26636af297e4779780e9132d71b4413a0e6456e0606e23c5621d2120351b92a6eac1027e7e46e10dcace9476a6b807f6068b143b1397a42ec32867da9f181e1834138afef61ba8984efd972a2d24cd160726618ec55d1892dd4345dd8270cdaf8f948dc5056411d8c60f3d80069ca2ec0ce545c0d05cdeb5d0c5ee5002eb3db168f15d7a3fa468fbe542ddaca69fa1050f99e1c793f6411dafbf19968a092596f096099522054bb7a0f53d7eae8a69350346f62ae77bb9fcf765ad7fa42c98b659e19df6df6d069bc3ee150a3ed2780a016819a107735a3619944178ec75da2ca504be45e0a788889aae71e25aa650080f4b4787f6e70704a5005a875d9c1bcaf7434fc2d1ce147c86ddea5d8fc643ae0c1b32e55e27ba9153a23c547b5af162c22fa85f8667cd20f7d9b2955202538d0099b7b43773f098f69909adef394aff99da0c6be61f4017116eb38c9a9a6aac0f12a299dfebcdb5a45c4358351c1a6defb6e29ce6183646dccdd5f5425e215d0bf83ba135a93fc7fd2f1ff7b7ecaeb88b303de520c07acca7d33ca28d8be1fe0bd413ae09c1fc7bc05cb6b38e334cc95c26c01784c588f81e47aff640b88eab099637a1521d8d5ce28952666da4690d2cad378b9319f721e2a5f3bb62f8b1ca0bf23f56529e77d21da1a308db9307749b31c1260d45b8212a358af7ed19be1f024028edc41d28112d9e2531e5fbeb31b46235cd35762b7621b1c77c51ee66040f92fece52522178fa03220b0743b41430ef01ab0ca6fad1ac4638123f04570e0bd5ed1611fbf5c09ae34e6b7742e4c5f8b61ba386e78bc74208268d63e623ce0afead8ebc80ea55fba7c6127be1ea79a220360fea3d3c8ccf8defedf3f78c02013a2ee53436452344785a990f2ec426edbb2fd3323989ff298067df92946af60963ac149a302b72940d50f9ce6cc75beea374498472d624561969245135d9750f +generate_ring_signature ed719c87c0b79fc16cd32a2efd4696df0b0a7185fe21ffa18ba4e2deae143c52 4464845fbd5bff673c0c426ea18d619c348b7c50a08dfc85e7df61626af49eb2 2 9c5b461ddaf73c5983ed093e27473cbc522a409ca174cc66e5c6940aa0101b7e f2c2905c2e1e13d54a03eb8471b88ab6fc8ed2100d8f986dd6036c9d0f4ebe26 382f93206236645776b9a4aeaf7157b8d6a1b134fd55048429fdf7edaebb1f0f 1 40b1ba856b25ca4e45f2255740132a5cb72b64ca321d0eae5efa9c211c1f0401bc40f1e4f444643f40951b1db510499b3607edafd9b97fee8b60b0dce673aa086b1982a4e046e3fc719fc422592f1d95d04e66a433f1ebddb7df28ff7a96be00a1ab98c54f424752ee4f07e5c0848c194c02e0a28900d0669e42f5a10b7caf0c +generate_ring_signature ea99bcf7027efa694e66c10b396f59ba243010287e4cf9d29e5ecdb6a862131e 1b0421662442e6aff47570da88a9bcc854c03cab75a7319f7999ba77318e1b5f 7 7870a850471a5deb3c18474021bb613792a6dc34c274ea8aa6b8035c8afc8e13 4038a617bf850b3b74153f8b92470545269f5e7d649291f1fb15bff2b62608fc d141633532e2313d160e43e4fc933f5fe00c4061b1447bc1f306ac80ac414da9 2652950a114d8e10d5b9a604f2530e077181fa99a554b9624f00d8c38f69e88c 1cd4b570c9e3ba70a57c50ac56fa8b09d763d4aa9b9b9bb2a4d317c30af43ef7 956e00dc7a0757cf0c366c5794acef1bf59d7632a4941c7b9f315af6c42333d8 c21a729a34dbbe0dd348c6eb7bdf4a380bbffbdbaf4f81d1604518b5ed188265 53326a570923524fa75ed94f50f7325b816dd98fab167710f7d7915367a9cc01 4 c89baadcd76a34c875ba42cc2b0d8aae6b6272b37b38cd6a4529ae8bcbcaa50aa758f0b4e8b9c46d454084eb9ef8ef089d85905928fca190721897fcf007cd01bf6c1264b6ec0c76381e230ca0de61f735c302bcc7e1b692e5a93e0c691b0609ed30893c589bbb7d099b283d1ea7f1e43ec8fbbee89515019ef4e3729a7c2200be5fc75045c2b7c276972baf41ca3143f2cfb72e3489c663f14a9b04dcc01507b7a6ca30cdf8197bb299316668bad141b5648cf15ea5391e519bdd950f14fb0c9785a22f504bacbdd471d84f6d32f36326a2b7fa0b7dd4da55991823f0fc3e039b9307f21a21f9fe6f5b65aa9a735f4a2d7b1d2e22b303bd93b78fbacb0a620fde2736a8f2fa62301d42a507cbb1e503246c36a4b04cced83c42e7d8247668002d013741196e08ce333ad68048142d2334a0ffb7da9c2d4591d7147b8253740163ce23efbc4780a208722a35d386a7984ff1f06a3fdebad7ea3c1a7897e9df09dd10356019e7e87b9b38e000f3e75c489dda83b327fa637124fb64ad6445f10d773df80aa06c57b8cc00c9b60939bf4cabb0ee53b16f30ce07ea4ef72907ee040069c8fbcbfa3c0e58ca70feef9fc4df7ea21359c7aeeb407843327e5f920c07 +generate_ring_signature e47768091310bccecd336c275d090cfa0678f73a7e37412bb74174c45cc87714 519803b3116306d37dd1a7b49f1b42d2aa513a46e1ef3a32181a711e798bbbba 12 c7793bff7166c65a08f8808d3f5d3e819442bbbbda64d39d26aabfee089c0280 f00a929be9e5dde90887dc8d056de66941f6dee36c3fd998073d1b8f443b04ec f703d0f8d66cd65fe85714c6b5f44dcc796950deddce66e659ede2b54198a444 cbd3f9dd832352d840d4aec26f24b721b7f5aa733ecfbc62672e6aa254913fb4 d463513327ab6aa87f941caf180fe90c34f7b71a9a334933f1bab736b1b84148 de5eaa883027c1e1b1b253ae601a7b30fdffc5f92ab160b73b39ac2d8b1ca196 621859fedec8d6d74738b9526b1453d8aa554989bef02af79d4b5116c7ba05a7 6bf92b72d07bb9981203eadfea5048741d2b7ecd5779cb9c4d44a668f20e668e 5cf66d857110f353fbbf31929b33f566ee3fd87b0a589365fda32a8f946166c5 051c470f9839fe3e7db24355bf371de7647af35ac96dcee755cc06aa32c1e142 f2c605bcb783f71f980729e9e78b2f31c4213d4c53848439395907748644fd12 305e05e5f9e81a935d77821f484de89d5193c366c128688b3a901cd32fbe4337 3c249009e060ec8e282da50fbfe12e91d6b224f6a14289ee30f4813967597d05 11 c3182f45838fa1b929598af7385b8ec6e13514bc2d1df90f7aee458bd0bc2b0db43ada77179f4769309ed6690c1b0ac9acf959d597e0544d1d49306ff3e13a0ba0a63664c8b00c811117544e7253a3e20e162164bcaddd27621e8d80289b5c0042747f29ddc4a9681a1429577e56e85308c54682fa4079b455c5f436e12b4f0137fa1bc35c4c6393f75da95b2b2ab8f54b43bb164b3d7407b222896b654a070a9bd570ef8f48fb26e7ef9e95f7ca0743cdc0eb718da19bebdad68feac510760079dd6e0522a288e721d8ee2fd540d836f9a55aab2d8d5ce25da3aab85c60c30c9a36a0a31536fa80b121ce42d87b58b607b6e9a4c95b1a8644cdc6961cc72602f3b4306eafd214c57d26f0a1f9d49e53b32633232d0ed8d69781fb53b6e376068b6c58a6d05ee85e4e1c10dab918bcb68077839e547e567c04052369bc55a30e173229f2ae2920ee517fcfb427fac67f9d23e2a565b689050070fd1bde12850a99749f1e615e5328c14b22e5422af62f6a1242a85c5fba0befd65e0ac6e0220f9a3cf0de01e4ec2d981c24dd904085a7299051d4a90357ad34eec221837a28027e4295d2c71c79c6258bb1a56cfd5c7260ae5b5ed28d3ea342a45d72fa2f2d01c484997bcd16be7b5446bd79ff41f899aa653218ed4f53cc011d27b0e9bf0409321450e02488da9f1c057064821518b26965641a553e7b6c330ed332c9a6040ca448e033892c30b46ddd8517ea8c5bc5ebd832d6784743929fc55b5607867707fa86c7e24a443a14c03ecda653a78462cb6aa7848627fc604661e22a3bdd4503b25fb806e2e551cc917a7f3f20d32b57de4d9aaca06a99bde6bc66df089154020add5c1dea3f193670d9b640185f8192be710c66dbd6b3c2226ad463dd1aa008f8ac4dd32db34e8af7a193d6f4bb3e97383324e2e968f3d544359f2a87bf850acabd0b35f1548fb5cb2ad8823a98e7d317e0671a1f24fb263c317c11f589de0dd12e50c3f951f51ee29fc7eff30b422cc5a6cc8650dc839fb146e0fee1bab00c0887e750b3a5cfa0b9a9ddd09f3dbbaf50f9b362cf6c5eb9af6a8ec222a0ca00 +generate_ring_signature e9ae44f23d6ad3658931a663d4ab71e239b36c9381826c98e604f444d1e406ab 3559bddaa43831e3cf5941cca69759aa619c1d634bf93435b7947e78556a3460 7 ae324659dd6ae94a87c9f4752e893e1bbada0e399505e6f6604c9244a5c98155 bf3a7f1c046e157a02cb47dafd620b6dd01fc87e1b4cb3579b85361660af108b f283ef8bd1746dce27cf52f421ffd8341831324538db5535460d20a65531d3d2 f289abc45a59ff9fe89e7324295d7bd4ac8c5de526bea2d421db59d3ecc9e02d 6d51d198be029416319f3c72d7859bfdba575066fde4bd628eceff9f122fd475 f9fdcea90d5ec05451bc2a2642bcfdb4daa735a1c731d8701c29764d1e235853 3cc6ae7f4cd2a7c2418dbb0793001cb5455b4ec34c00ac34beecb1a36808d6e5 31e0bbaf98cc1cad72c8e054f37aa14f1ebfa0f5a14ee7a72415cf766d017b00 5 52582f08247d36feac1482775e486f284cedcb14d9770fb59eca893daf03da0badfea951d1db4290dbc48abda26b848a5118e7bd59c0df157c88be3984c2290559ed808aadde2918f73fb9f269bbd2a84da638e83051060d63b5a24ae962fe0c5b7049f0b2a47b13d7e38d1b5cf393081923884aeec522951fb9d49e2b205404511f6c5c7cdac54d2a28f54489fae1d9b01de3b58c6f349fa73ea454ce31960b09679315cd1019855d13e6ce898884d8a7e1d703fae1703e1b4a28a4b588390aa9e787dfc21d16c883df3da9e0e28a434548bbdd8e7b9c216b243269cc08a40c5315d70f30a355fef8da895d0ad589c5c4bacab28ce2b23ea27146fd76d2900cd97191bb42d36e21ad65408bc9f2ca1e040199f05e4786df1c7d514c05b3470d357d903b1078e6e87408e898c5d7520071079c8871964385dbf334646223510872a0d326891430b1cad6c926c637c30527d7f5946421a9e7a3b678ae81d06808a088157ca9f64e58f6fe5870a181be505c8e9949126f6995b56f367a14fe9e0837610fbfd389f192f3ee55328a36ce92516d52d5ec5910afcb43ced36c34fd0998b59e431691f7ac71e1a161b5af8efed0df58c6dc6c834dd8cde657806bbe06 +generate_ring_signature 25c1e927c5eb29939219f79f5d2c6f5d22238ad51671169446003c1c7835c765 f91ca89e0c29175b95c6b2edd7b9876040e69b0ea9b4e0b658893a5c47ffcb28 33 abdf0f524c8e8d5aeb7130984a00f07c9ce7e1eea80ec066595b8d61b840a565 9888c9bc06ab291df02f4cf5199157e0a5b509c382198067ba8de73273c92a10 a8489b0caccf285534815128975c5e76bb676b291fb1815e6332c04bed5d884f e61c032b162ee27bcf87ddf059f23155329700d52a1d74ebd4637a19b79b3fa8 b07b5b99af0482139ce871e305a2463b6284f669863ead78c348eb0c65094a61 71d4dff504972a9bfba02748d69a21b1384db8b7d3b0d602606698a46874a150 8326503769cf9df0660dbb53434a5f39152433da4d3d1a9c7613666bb3e1897d 29c0bd4fd74dee4c49dc206b09d6275365cf3fc05c5f305cb1308b60a84a9941 dd55462ede7f9a54be15fbbb3cc28d7d3479252d0aff9563c9517163ac2088e0 64a6dd6e33f2ed29b933be5c311b5aac5cd5d65c96244e3bd2637a19e215ea05 f8dfdafc7b06b3b62f1829d4510ae5c41d1e637141b45f8044a63864b9473c34 0bf5508b6bf513409cbfb4ebcb16852a38fcb493810ae8981f00c160d9f23dc7 1c4eb3d277be0a34891423b22e1f3391fdae809f9e75729b352ed777b157ce75 063f2498e01af041850069fcfed16208011cb59072b5cd6a084f365baea6868e 5ed654635858ad9540e77404b0bed4afc20f8b7feee0f68712122063caa42018 f8b6b682850bdec7e95051c45ee6307e8809e8b862fdd51a9c9d8b4a6c408a5b 4e459edf982a6a4767b70cf8495dcb5f3286f1c573db0321bfb50061c92ceda8 1088d5698f56c4ec4c554354060a38711460332d38e21a33d77091a9d6d90d9e 8d68d1070ae328ce59f7a6a6599068e7520af35fee82fc94434589121d546d05 3a249b93f2decb928c3ab1916c38cca9b56c38e93ef4d987bd1feb8de1340e0e e34fe339cfd31c413a4fdb201bd7cfe8773875ec5f08bb7910ef02726da9fa56 75237cc3a5fddc1c68bd42d1b22df8d984dde3d0697f11757c7d75d14f70a342 708c3395a3e4e95e4e58aee0cf39670a69d167cb308daa8ac370cbe686f0ae02 77a4b1f6224c72201f47c34aedd1d96788bdff0cd01f87b517a8dc951c11526d c54a5b877c78de0267a21841a3fb137f0037666f996795edb526223663136cef f1fdfae6bf98a3a2825eb4898c4495765ed24c0fed3f3d6c28d84bbb485761c0 fb5125e443b79c3bfcdac655671d33187b750bb3a27cdd59b7e545642adf2ca5 f6bea66b804e41c461d05f14c96bf90cd8990505338add4d1ac0982e2ea75d57 971b7a0b2a9cf604ce99c32157818ff36e37d65132bdc5be98393707ff0ee314 d5805e53f04ed21bf57524caaa5cabfcfa19d48061f5f6123af07495a2debc3f 541209e215c4259f8b51086fedf41227f5a086a5c52a9bffb89270edc915387d f517efe1ef3d13561001ac485733775779affb438e71d8cd051a33c231cdddc0 5ee22d707ee5e8eff9d1ea12dcee617f7dad29210e08f6c4ccd5d8bc77d6eda3 fd74267ecbfcccb2d4ede79bb199a04044567ae5c9fad48732ebf51a9d5fc106 9 dd42d389102cf68e262bcff5b89579c61dfe72bcf76a676046daa7537434ea037c1b4cf31dcf6a31953601afed600693f1f3824c1a74ce6350842dba3033520997c7c22a4c448034abacd0c0f5a2b8f48351206d5b2b32689d12e8fd46d7000a848fd4e7d2a86a37ee3d712227e1d0ed04544381e2b0990d8e14266129388207fb785fb7c137828851a81e66120785d85120c010a4de8ac8d7d317a635115308a2b8ce68ddf07cf158db92ec28576855659d94d030e125c460fd68f54ae7b008fbbe63d34f1b00481f2ccf729fa3950bc4560e551e6e0f992ff6a81e8f3ef70e6b4413711a59b763a989283b433dbdf39c45e788862b4ce398c21faf3b44e60100c65598c1cf9cb89dc12a9d7ec6d16150ff6c898f3a857529f4127a0b606d019087cf25761f555e5d89c1723741a67396b22625ad03c1a1e57b0bd8c782b105dcaa439605a5e89a9f8a20c378d7395186108cdf6646566d51467ff6a549a40c17cd0ed428d4d6f4d51eaa9bf89e25977ef8a34401067f3852df2eeb9932af0babbdbae7ecfdab600e72f3ad1fb1d6be7b2a26707cd519e3ed1a87cc9268d00ee6745df7b3e678350bf629b5685ed50400b56d2bbf37b49f381b8d9ca26ced00ba6dea8f095e9726a06fd8fca84eb436abdb67cdd4b9a3a49c1af079c7cfe909654c694db7094d22217888b1614bd876459c668445194de1aae720fb742c2d0dae55618d43791aba2376e67141648f4c5ce53f09a7c2a61da783b533deff01049d8dc6c22fd9e6bcd8217aaa5dff7af40b22e2ec2eeb7003015c196fd99ee40920326acecd6a5ed528dc9e1be9f97474cbcc7c3f5cb129833ab84884eea38b0e24d2391ca2000220d0740bcffaea2249679c722dc9f8aca54288cc6e09afc000a346f5b06991ce8d333635d42e17f4f7b40e69a2969ed05c780ec1c25788b2051362c18393cac95cb1becd88b374490ee67f7a2dd67d7354906d111dbcd98d0d27e3a17beada0a83569038e26e466d4a1ed3f178e976cf0d3b735a4e5a47df0d92b9ed4ce2e2061a58fe222f82b410f8b05c6ba110cb980dae1112b20bc72b09dda21a37e95bbf69164cecca0153263e42e7bf712ac91188f9e4ac68fb560702761d7c59d937e145ffd7944750cc6c10f48bcce0d3e1bafa2329e3953ea64807c0e425be0e1834e3caa8410cbdaf7e0a002e2e8b0eb3b8cf25854320c4995303edf3680e49286942cc683ca3b560018f2c604a04921665a8f21ac9daa82f110d90c28b9ec6c54f43d5c0eb329df49f661b79bd3820e5702b2618151dcc8a5c07a68ca6fc7122d989f12ff0212ae240effe37e6114bf15c32aefa4a7228875f0d26c026b7da686e77fb5032ce4d9c9edd88617131a3a688c55b55827cb991a30c3656ea417a49e4719ac454f351c989621e61df24b16400125a74788294313205f24a973d7b6e0513e1a02d7c2c56f924dc8cdb0d9d7a9967f58f4e51f4de0307714a4f3758078f5e88c3a5a11a91adc138927c51325dc2203206dbdc7db2800d845ee9fa832991be73ddbcdc524da46e9b4e6ce0cc450c70a3180f8fa060a9053e3de5ad375342ac3e00dbbfa6c693116347d91c105edff8d99352e987022309b568435dbfc33dc35854f580b123f8d8350d325e4464a91c0994df7cc8a22e0b8c75704b72c5de5ab2ab7eaa0514b1e2d6f65f9a6d0605b3c1ca22d146ce6503babd5dc58b4d5b12fbacbe416c841aacba6117cc250872affe84735d7cc6b40df2d25aaef27fe3334ffb2ef904dc6186a6d35acc5c93f5f42dfe585d81b88107a8268933e8e48264cb61c0937c5612bba0196e9fd578455cd682b1674a26310abad7a75852c22677e35a0528efedf45edc40efb60c75ecf6ee6f02b14a85760da4b27bfa1dcd8ccc35f56e6ec9dff9e235a0ecda2d9529a60c2e1229681f0f0ef5e6b50a2653e5e7ea376802a446f280d757d626609ada1cbdf5a181178e5407050c4b146abe760d88d26660cf956db0e8a05e0c1a72a45a33b901b19764cb010d5b17f345062c5cf35c242fc8331bf4d1961bb0c52edd8ec4be508194d2f609778810927d1ce895024e9a918774a94e0da29e8955f62c0840b6ab6d665d7b016d9572211f5b0cd270e2a84db69a580ccc69951b5fe7d260f3a908a193fc3f09ee9b0682740f16a4c1439819270b7cfee359390e8a7e01b2aed3070acb382b0ee850eae68f81b006b88aa3cbec661b8b64cf95c8e6a0f52d1aa6a8d8959dc003daa69aee8da4338b48748277fd07cd1ca17ee6af345737ba4ca71f9fe5a98e0ba637a1b827750023c7fd15962fb5caf1bbec3fa6d6de7c1c632871b49ff34d0c163a07a7b223e94fd68a3f64815ba77d83436c921c639ff1c46bc505febc820b363de545a914ce3827c193def01f88f62d038baf7d5839b2b4151f7bd47d4b04ba9dcbabc7c0139c89ac3c21664a05a6b246edb351439a09f3bfacb1b4557d03c43647671ada6e45f9eaf58178035e6f384682189a84934cda1021f384b5bd07b94252bcb879ac412e5678b9d885fd1d1b5fd20632a0025be3bf4355e5136306e2ea5712a1a2beb2d7bb3336ea62230b205c30058404c3ea87ccd47c2614b6022041762d703e672ea0c5ce283cdb506f0173bd8c6814eb69ce9366ee924df10f9641a3a43660e4b0ce07cfcd2f861708c39322404bd4b5716c4a2634f6898e0abc1741c87e1f81c55b800c52b10f3d73a37c25836eaf0e95a72337842512d104ab760ba9c293762b69be42ed73afbe0f7042b99a0ec098d729140ad7f8b00c0f57aea8cd26dc6c7c75d0af39bb5cb494d617fc822e7b52c1b03521f5eea9cf0798131f16522c4432a6b684fe9c21142e605314725b4ac7d73c97994f5005a205d10764cd6f3688c41bfacd4dfdbb268aaad24ef447c8196f20966fc4d99d35076e25ebbe269b317d48aa6b3f8041a7d74f2557b5cd46d0af02eb2ce25395f609 +generate_ring_signature 28129b7d607f44620b28cfb21e8e5ebce02b626a8d4027ca9119b076330d06ca 4d1a533a9141cde8a5fda54410a37ada085d39dbf423eb2ec87c148c43824967 8 171d5bf9114ee1d94bd0fc17036a8241b09d02b92c4677b23906d634070022dc 685f0d45ef6e92a2014fc074c7c16d7d9949da816915945a3639c8bca4045b44 78f0c0c4b397cd716a25456bdced125b636a2537b97f5ff389821243fdd5f212 509e825e6907c3775faa831a2be0cff0a42ddf6e53ae76a9fb21d681c2052aa2 6c1115c3b313b2818b872274ff048b7e759820ca0996a7568c252bdd45663fcb fe3aae2bcb898c10d02f38bf52362287504ab6942a24bf492940ed275eb1550b 93b9d3c87dfecf185c7296717460e930c4397fb9d55de555147b46e0a4263408 fa5494a0d2a6bf220d4ae21dd2296fba2917a6c8fe9ea007d944ae04b34d5475 35ffbcb62ee6f85745b145868d8f16dedf0c382e2b8c877654fb82d4ea3fe706 6 658800b5074193521bcb6c393c299002d03471e66352ac84d5fc851011201007f7549b188b77fe807bac6db328a0392bc3f70b76aee1362eab1fdd64734bd60a2e0720cd19e9ec5d7611bdcb41246d7494ac7592078693dbcdf08f94a6ff6206d7b3553b7f319e252c01d5f600aad61d069358498aa07e00af6c1612c6fc6e0e70d40d620e5e01e723fd74dabd2286c4a72159a0f2a9965cfac981b63a5377061a30af210afb97043349740f4df27599d6f573b55975758bcf69cb6e979c7909b1784503f9a26e9f3d4e315cdb5bdf44f8d490f03376afb2d1365e7f9ff0fb09cbd5b99992343ba9832cdacd925298d243dd0a18a791baa2e673932a4896c705cb48af075a3e0a629faf3c75768ef950edc68cab2b7f3eefeeb95995560ec80e0cc9c8e679d0c15b8259fc94b6ffb90c87dd5ae34c4c1ae4adc95abe7b8bf309fa1c5f431f22a3c5cde7574569daf9b1c87c0a629ea01cc43fa8a2a7de2b1d0593ad6aeff73a911e46f69e32e1ac84c8184429e8dcbac48e0bcfadc8919134078dab992f04ef2d26224e2423274b703f3eaefa7031b9fbadeea778112975b70fdfebfd2cabf5e1422eaea20939e620705774438e7c1560ac234aac27cd10770c10c9c1edf887fd576be0dad506fa566704c30b86bb5a709d5a9fe9ae558aca06a1c59ddb7223af3f90f54ea0ae41a7f4bbbd6436e254080eed7b223865d4480e +generate_ring_signature dac2f838a2b12f77bb477ff71aa182ccaa8a1e1040524913329b0eff7030fed6 881c4e3b50720fe341811c24e3997699d0d40f7182151befa3c66340249a50db 22 f991f2b8bee15e15be7240a8349752c97567c20965fee0ce8ff4efe8ae0e3d23 8c658d94cb3b6c42c22b2bbfa0851459265ac94cb9f5fd53436fd05b56419e70 4295678932a64cdad51a883cabe6578beecad32f813707e6f56568d8a90dd2c9 7cbfa0a8fa6456ab47506728f1ea4f291d18c9cec3101c3aed6fc0532aee7d62 b9923d812aee2aa0c1d18d7fa6a2ec6344bc2ccaee5356a70e28b76e5461abd8 47ee0e99f1cf117f89cb1f4924d206c10d60fef134f31cc601c88261b3519dcb 80ad02cf751b3779dc6b9d99f1f46fc54514b6e55f416ec87bd3d3153489eeb5 75e18ba6115f2bc4a946882196e20e3be7436ca04314d3fb389011e5ff76a9c6 6983f591a9657b604de39f364ee99d1f35733161f2c143e40979b28ef07163bc 859922e6e20e39cc6d3c820f40c76769b33014b949f617a1e0f37f4d0f787370 eb793dde8018e74d1f56aab2fbdbce72ca37458b4a2d078754ec009559eabf06 d3f97c6f6c7d9722b89ac244f33ff502948472e2f6c96313fcd1593cba352cb4 e111506be28ddd39108c7e08123351d153f935dc61af295ddd6b1224e5b87ed5 7f7e55d3b508f9adcff652762de41655a4608cf8ada8ac6e132e9d2e020f410a 05892b309cb75fd1317bcf13b070014e6a98d70f5d90689e14a6ab7cb97fce36 f694939d00e563571dfeedf00c60eafcd4d1a2e188e17b6c45b68d090cdc4c48 d48e42b806cc8b684dc8f3df481c79d6eb72cefd79a2e7e147b36af89cc29db9 c58785a3273bd0ddacf7d4dca2062d64121dc5cd074639670c134dbc41a80a68 91935d3edc19635c336d6bafc26ff3e15100478866b094b7f7413f7df925c29b be3e73997d6b5f008eaa6215adfbdc3238444ac720596051c11b033a37087145 73da0c36ef7f13820e987b6553c2507e1cd73cbaebc7dbf6f73ffa3577bd9fce f05c456a8237b569a385a40c922c3b6de45eb9c27180b9dd4cf7037ceed6b9e5 1632d3b77dae3781704059f0095f5591b23306a5d8cf9fe12304297016b43805 17 5247f83ebf27f42967c69ef5c65c167e530c99f10cbdb41cc062d2378d26e90f5190b5ffc89d2150c357d435889802a3f64e3d2c8708dcacda1571a02ae25c00d44a40b6639f8c7f6b00bc0ae77e135c104f21e6f44c1b918a445c61164d3809d091c228433ecbd4b6eae066e349864489049bab539ffdc2dbc6a4d937027e0eef6231356b75495a7b10d0b8d84cc6a593e1ee3dcf93885252905c3348d31f04da3a6b79917313a87da325c1fcdba0f6d9eec63ac18c3db794d80f2f6266c7070be82c1f5134c551c3f4eccd3cacd009c281cc24b53354a063640f864a6ac00888ae8674cf4b6fa3657874668e2dcca4043a821f476ba39fcb01a453e648d800ade970fba4b4ce306797fe1b9f2cb70e4c77e4c08f3e2deac53209b92390aa053c3f1f3ba7a5c62a76ed9267d0f952e2fc7c7dc87111e97e6e249a8baa10520492e560085ab8344f58ba4150e66581c65ff8b479ee69172e31b820e44cbe62001be1d3e8589f353620232b10a6e3e966216b01916df88f354d5eb1c75e8e4002707dbf5766dc7fad8198d88784dbb47310b8e66b42037cc0fe4f282805e41303c48aa801a54d58e7caf6cc3110a99607e5491b348674207fbca1ea006f4a990d6f8b8f5fa4249734067ada90dec5eb295ea3e7a2299a47ecf65784e0d0f4f309a15c904843ed423e74e87c9b894e2e985f4e8cf152e14addf7b02f94911c24031fb01e775c7f2b2f1f5eec9169346187b5424b18e62592ab7350ba2f89cd68062dc018318cda179b5f0e418c9dfcf1a82cc75d293161afc6023a1cfa0bd1380712003c49527c40e26229802f0ce92bd3530f562beee1bfab0fefbf416c7dfd0beba1a6ade86c65c642b259ffce6c5e9d5ebebe2410060c57b26f9e456ba7d204cc3572e2cd2f2777b8dc59b7e6602b4cb401e06d9a72c740600e0f53af02ec067bd6ea4d51f6e6a0d79a15e9488f13aaac0a2b8f6ae7d7ba87dcd674dd50f50756dda37609ee5560f16351d77392228795cf35b7c250ca7ee01411774cba9502956ebe24254dbaed5d09187655318d92bf96671355fb9d67fc03ae0c8d2d540f17653418e23605ad68301af596a2e24620481d18224815e09a52b06542e68203dc8524822f2e93a41ac1247707ba530ad9cb424dc38ef7d2182dd245459fb208b0a06b4426c129d36b5d4d95508d555d11d09e5ab1b9fd5ada8009b2ba554802f768e7112afe6be17f1319c002e8a6eb98c1ab3b1a79cf3e82e8c1def2c69100b8ee3437cc6e0ef01ae0652409c9c87aa7c4c0f28dac05bc8c966cfe42de5b0528e08a8b3eb274309ad3bf56393af21a8d6f849e836c27b08065db986d5ec10e426a38767adf1f5c976b436ecc87ea554ae1e77fd959470bbcc761e53af4780dd1bfa14282e032a217d3ca2710fb0907b8743fed8d10cc30a9cd150cdb6110029c813376dde0a15633fae29ea842f6ab44f90146f3417e0a7c3e63a971fd2d0da1a746a365e63caca1bcfe9e4bd2e708602caa7107ca0238597a51b2f98f9200e10919be80359ca6a2e0d387e81da5f6393621e57095df744d66891ba9b3f8024c61ac1c992c338571422022e4b8e35e78d15b8b3eab03b100cc75d005e5d60dc20e7254ea5c6c6f7fc7eda39b36e113a91373967872e888783d9a264a82ce020e09d6e7e9e47e6b226184b059a47c9477349e02b4c70a757a73f9101510d008c8cbc8d6f26b693eb5e987f8318df5c0d0e808bec3015baf71a744a5b23a770025cc44019ddcead5d326d1fcf9883f706d76f7708cc7e6c89e6dc9d81b124d07c11c3bf8acc185b4616011479eac2925b9f4a40587f8719b9f3206c50fc8aa0761488b856095e02f49e907552b932762c446e3c84e96061a7db61fc21d815e004c65459e3098c2d6f7a16ee8091bcb180dc45d53c29ff50c1aa6b53b7b64230c3a56e0c4e8cfa18881a18b08237dcd942998cf826d5cf0ab92b0af51bcd3c30d +generate_ring_signature 6cdda0b25feb87d88847ae395b60370dca6fbf997bcb8c01beb00bdcf6d3be37 143e729ccbb1bce5304842857d6f48390eec3bd85b2c4e3277b1b104d1b6b32d 3 1bf56424b62c35d3d168c1a12109840768b68e81da844c8a360100eab65da677 a3b9debf3e8bbee7fb21e5cce99d1819b8fc1858f7a2ac229a50f179f9e9f579 bca9a4114d82f010cc38cf8d700facb73e220635df8433afbb4a62508554a95f 03f315fa24fefa8a1f8bd5cefedaa8243526ca0072a48afa068b89f77ff43d0e 1 ddf731a33ceb76a8888891744b09332c23c5e750faa4524fa62bd7d66d3ada0d72aa3f9e6a53c4939c8613f189f60ff86591eb3253fde987ff61c3b115e1740e3a5bff64d87dbddf58fd1afd29764cdd7eea3a42ffe00ea237f58952a6994601dee9a1945da1a26fd4db29e6cfa74e840edd011aa817f6406442763bdad4940d9e787622ad8c16e8467ff2900665d158c7a29abb3a6d86262d6465ef5951c702138c8341c46d705809d735766cff7bfb1aba827d015060ef6d613d08e7ba570e +generate_ring_signature b399dd0f85fd7aeb58dd748a86afb279dd9b27ffc68ef7a8a6bd9bf7bc84d01c 75c55d854691d90e7c85387a57e433e6e0a391fa53f2067338d933988a62d133 1 5736b683d9a502ac895e583026893e5c2563548472144e9f2cfcc4640c9a71df 4f434826966470dc5fa4cb162c8e63746c02e682d2fe329c97ab91d9b475ec09 0 c909a0522790cf2f29cc71f828c75802716dc62dc785525ec64aa150b705850e25d024a3962c75368e9e6e9eff49d762b75e7c78d08da64c69bd0fe306813602 +generate_ring_signature 677ce51e64fcfbe01521851a1a713f3a1f7223df84e64566e99c7546fdc6214c 5ac7acee7962b6c20b44ccbf2f46e52bc1d62a36ab3af2cb0f2cc7815fac78f0 26 93917d825b8661ade705f72743442850863f999e1fcdbd803c95f0b4a72dfcb4 6a6db4b0065cb686118a6827177c435f566372c1ed027b5c0948bb4b9669cb90 780e4282922b0677021052354fc1bfb9e1b5852c90649317d8d29a33df4d4e2c a04048dcb32cceb0263ce81c49e7efaea240c01cec3508af63e9f46ce26e18bb 34d700ff7645dd6e61a81b607f632f742f9f4527b0f0f92b887010cc0e425893 6c8793b8d7fdf0c0eb80e741ae9739b7b2b79a6ff9d5398ac6f75f9625c56015 08c1d4056fc9a897eebf9c5716472d53813b578f5970eb444ff06f72cbc5e121 a374655ff39a99939ec8289e8c75581948bbc34f741248026180d64fc0d054aa dac99c11365bcec1c0217477059e80438db5a429771e366d878af3e5e9aae7c1 2475cbf9aa0fb70bf9435bbd5f23f1f067dd2eebffae0d407f1b632c141e4623 c2e82f71874e3654020f542299834eec73950ae0e2114762edeb0ccb7ff3fe8d 2b9eb04812f538b4810f1c5bbd5e3e05c422b0953977cfa6b9c7868bcd9b63e9 4e5826d1f8a70fd20a40cdfb6213a58e3879ee75811184f821dfee243b20c7f7 96d5d4db6ec1f95669823bf978b0f3a98694efb8256114a9ac7ca477bb726dab 099b1ed9fed606930e319f74898a6c529e3f5c2e80843c1873cfa735a78f6ff4 fd5b6729d3782d69a6e99eda32a361e89ef0e9ac3b818b411a71803d370a6c2a 7434b1011ab9ccdc062776b7294a16e47ec383e380aef04ccb78ad749e689ae5 f0b4271a276aeed6e1c22281eb6e5c60466f0dcfb9745e420c64801018fb5ddc 08b5c665d1db5f9df3fb2cd0daa50e45c7085ab37045bf92c7a1515ebf2def00 b3732ea24ed99f2913f40ee342a9cd057b01b8923ae4e563a907b910f4015890 3cc608f60d0f2450cc5135828447b8166cdb8d41d6c5359308bfd9530baf66ad 8fc21829ea2b69683fe09598d2e488cb73d2484296b3637d3d1e3d6846cbf347 76ea38dd85977814b60379de2baca56db771a83ea4e070e3ef5810bcf768eff7 74ab8996a49e3ea611c4efcc2c6a88117468049daa3e18121cf6999f490f6d9c ba66180f24f56f6a86f56ecc3af23db7d30d35f5c6ae2d0fee8e6cd276d4d073 47353461789a47872013f0de3fec125b633c236ee2cb1359bacc09c7f40637cc 161234e5a7efbf4bf90737dfc132ef8163952db37898d7c6ba07bbb5a162d007 0 b6e70697a7b8df6c2f48d8dbc23365c88fe2104373f62a12bee342f2767ccb0a61fe09c725899afaf69214666680ffa381eea817e7c7e4c4fc59ccf2a238bb0cdf784245975906814855a5fe724f10abe2a806555ce2ae5a9ff6069a7814ed01253b3440b5e545aa4e4c4bc27790e35225d1ae865b23af75cdca9fba24a36504283bb799a59c892594b6022084a4385a321d02821e015763b182c61d2bb66e0ba188fafe0bf4c1352babc63503b54d8b5ddd58ba51d32623a4eaad3fc2a66f086ab86b58a4f9ed38bf52e79cebe3c4759c241ed50a0aeda291af2a92829c210bc56d2ae58919e74ebbff6950651edea1c064a09c8966fac0e918c9c1fc25c70eda5b820bde38eb31bfc3d0b2b1e39b25090655e686f2d6052d1b79927e0f2306d4b7d48a5e530a48610ea9973b0de934ff419503410ecc9c91acd20c41f83601e68738903420cc9a8da0511e6e70cb9bbef434cce8ca854771b2d7a472201f0340783cdb8f906721a7e359d32aba722fab5e2a7f5a08522f0b1e994483b74509d4373ccd743edd347582919269c7f2be6933841d6e32bb670d57c223701d0b0b1e279233831500ed654c259d5318c1d9639ae8ad35f41d9cf59c07e2b4590c0af1f42c2d90428a0bbb2d4d54f3f6b3f0507379fa4b16cddaa04fa92719fa9a085ab1ef5187f34bf397502649870064d9eca0db720216957b98c00e1e3b021e03da570c4876b2d614716d2dcaefd73a7c63dc8e09444af712ad4fc6bc38178a0ee076451c941600d530fa24c9ab9d522c30d4c5deef7b80614a2419e990fd9406454a87048ee194487a09d382fef40ea0c2cf5ba4e576634300af49229634780bead71965b58778ec45f5080ac8f9cbcc7f272811d07e17b37940908e9e1b870f8123b17cc1a1669afa816289889fa1bf961d1dabf0f226e0f3f8df48c3c3bf002277aa6f88cfcf760b1b81481bebf6ea1996c9eb6e0e0cd9d0672548d33429095e02fe13f3ce8b894331aa632e99e4f628283fb05afee6c8dea2c383a61c220f9923fc61786ba32b2dd9b16924e268271fe154425ebddd94dd20baa7f2e2a50ef5a6e27d58d295b747cadf4ac44e7f38a9c9c0d50aefe6ff10a72a8e1c4ae100047c11d65b281e5da92cd8d1fede7887a0a05a2dace30da2c09dd36459c17606d35020d7bfd33e0d8b836b349129ab5e2a6615d6dcf68d7d6bf44627ce83d30185828be29959ded7b81d326d6dc949b3b12587309fd1bd38f805d4f3cc821a0000151e089938da02e0dc44f6ba3674ddc4aae4997b4906c828266619d6dd7a05dbc44a59f86082a2e0430b32bc91041e5d9c489d03f08584a29d3d6419e2ac0800686816564f654378abc3af93a4ff3d1a4d42664d7a0cc960dcc31199a8b10f514f07e9535b41eaeed6f772f8479630ec77e356b751daec86fccc48c61179089d8557ca281bcf72a1b6d21d205cb8855b7b65d99829034ee6e5ca30ec22f50e75650bce7d504e5e0f4a32c8cfd7c4307d2fabb23713d7e009cbc71a582c0b0702d575a8575bca09ce25e2ffb0a6ab02548adc86f4f2846445e46d79ea414307d56aabbe47d32e1cdfe959442776354eb0f59298ceb547d6246319a6d6ac2f0f7f40f4588e56576ee386bda7e6b10d779aa2e445a7e47ca7f5ef209456dbec09170f0ac0a956ccd439294acbee90d765a9f41a130b224943794a73cf7f29f802733140958309f48e4b60545f9981d27471e1a492bf78b5e5aef6dcd3af663c0c19d20b3f7abfc5c5fe71b4f8334e02838d8445975736e7c6e373fcb81727bf077f3c16e3c4e53869eaa13e0f03e4a26c12a87c27431aaa4096fe84eca7936c0068bdd1be6d8befbc5b25e4434f3e63bad16bb6782410421d51d9d7eb5790150140a944a24b35c3eb888665414debd0ce8730018d2447f13afdfc1fb5da0fd20905cb98cd5479ee862a024c87ecebda7320fbae371d2cec529d9d816c1ca9cd023e304ad718764fe4d461d631a48214c09239e2d406f443929c6af39551799e0ae8b15211e45a5d3c3af2c7d0e7881e344fdddd35349b2da4dbf446ff1ba05e07f15ce41b82694e2c1677ee90275863d67cbfd2d4942b1e2e547bdcdf783a1607713c204f66e59d04bd0e07dbf3a40e0fa0de421df54824fc3d80bb21b71e9d024622e152674e82995ab3c9948ab2fa1220de8a22abb91b1c3d1a618da809a508f2b926d18fd34341de00319980c4aca1b97fac74dac4f2f15617a64a24ffb30d8ff3eb619af13f199d377b8462c9a18d0cb7d02d3040d2368731efebec8fb30803daf3ae902d6c85a63ccd6e5a8d477420106e3a63de537495e4d1e7ab5bea06 +generate_ring_signature d3f0b5f9d461e49f4cc087561cbdaed5e3286721c8545eb69aeb61138506eb71 de638f92f69d2832c8495c16ab927c998ecae42ba3baff9336218dd7064d7317 53 030e3a4468222d71f5002d66a80cedc349e3a41a6ba567fe61fe8ae7bb3ed44a 574de180d030f8368a1cb7052f51a4a3a23fbf3dcb279ad9e8f7a7c0cd34eeb5 75e47494d6490566a613aa469b04f6b90b500d806fff831483066dd97d0f4c11 8a54be236e0d78b0f94dd9cd8cbd86f0b738337d75db6fd8eb8e6a0abddfc1d4 a775d1438556fa7576682fc19c099703a357fde6623c6fac60970b407e561055 4d06a12264245b4ac8c011c8e3343348b9cbdae7cb5be7314f2e7ec15c5ec39f 9e2d84f29d6295739ba66471c99a5ebd727a847990226767624573ee54be6429 66988d8085797f2479ce908cbb4a87f0ce10c8ca8df03095a86fd2997b5a07e8 aca20f39f4dcc5d933f6c64e83196e0f1b56618bd6fb117ee0f446d65fd9b995 acc4e85a475b00d4cb3b6272a8cd098a26290db960370daaa9a55f9c52e41bc3 afe7026764c8d28864403699b58f5f7b6f6c89421b7fb6d6f5c1ad61a9927265 449d89435637a88ed0788e2f1e94f4674cbb912ce1514900b0de26712f0109fe eba266af4e311c87796a6e68eb5390664f874104bf8d02f7ad801d18658ee308 1d7f997790f8ec58f3c627f6f909c4a74c7783e78df223926c877413b4b779e6 04f6568e0c7898d53fb9544030e0488120c995cee7b24fb17b273109d22b15a4 0b88454f650e98fe4ebeac143f1c67708676a0a2e6adab0acf7938a2899915fd bcbab9687bbf9b0d25f4787bb942104af5a2ef7dcf4c808ccd86071307ee6a60 373832fc082b361c1d5c802b8687dec4ace4556ce3d7a0f068190da04dcb7f4f 56bb3cfa048d79dce3c9a730760139b34e00006867b03f2255181f1e1ebadbf6 8a05329ebffde2c58b3f1afc2d9f8a58d90376cdc70249e8d79bb588b2082085 5d97d6bce380f8203ceb193438487c6fedea6da3a2ec1e7a9402ccf1c68867fc d8c963c913b4e03313f540c7a7c1e3ac182ba84343c13adb4f270b43b8b9afe6 712cc1213d47bf8f66b687824c9572218f45380dd621ba34830be4cf69b727cc 83b12d30462fa9b60a423b659084817e521682c6a324753abb0949d16b0ec7c2 4c54a081fc81f0494e374e756d12a124002d3a7db2d09d3e691205ec3af67c83 e36f9087d06d44f6d4215273e922b0e79e7c49023d010ff874c10d220b0756c7 fe80876fd6f488926c282a003a0c247b5d8dc772cceb6e13f4850052443b1c0b 8b5f206dde9a0e29d793b3ad30d57c3d12ad50d215642d78fbeda5758f5cbc72 7ba4af2f70e25c26689e33413c638e18e81bee4ad09b6c91ea5cdc53c38c1226 8b60ee3086bae2bfaf3feb83d9a1a1b5448135cd5ae8599a1cf1f2aeab994d75 a51617fbe6ca87c8a23f135561ffb2908033378c00ddd6ce3a29a52c71034c39 463c3acf4fc93d273cce0b078988384c1634047563392baf5c7d6e933fd899f9 73e31485d5265185408cf50feb0e5a4267e12a16a813581a4646ea6340afddc3 913c874620e434d215664dfc01798e748e92456436631e0240170f1153eedca0 32b504cb66867096f8169076a0bf58501f4a28751cf714c2e7ebfa68916fec9b 8c52009e5e3b839c6562ead8bf30a259c5e5d161ce1100972b02658d3814dc79 1a49a0ac194064cd68fd36ceab793381e869523ddc003906a800ddd0ef3aa3e1 8c5cb8175ecfe313e33d85e0e3081b10ff63f9e761f64c2de5d144c81ad52f69 bda4cc954bef5e6c41c4fef4f55a58baef8607cc02d1e5a021631d8609702ddc 8f35174d927c51f752308841bd63261ba5126ac61715853680ddb4a7554327f3 1446af516e948c2e597cc4dad579f0b8d23168ecfb224f64ac71ce391631390c 63da46a69d6b10c5f4cb6e80dc5bdfc7b58d41247b9f68c21dacaa5cd2d94baf c36a0a43a149df1bc3a8a7c6bdfcbbc8b8db8b9b44830f04a00f263a28f3f61d 644987e09b64e9efdebfc00122e2b9d69d49c51f5177ef4f781de164bd0e0780 86c0794b3a9dc06f84913a2c90dee08e8b358cea3c4f76ea2836a9cd4026be86 f86a2a00193995f89f91b2da05fee55198044fb1bf42e4f75535389679dbef5c a7de1f95ce601faecd2628cd95ea2a41cbea4bae46f9c9e61d4d974a748725a8 f9ecde5ef7ec25e1615ecb692d97d1ae7aae7eb02d04e506e801af1053ab896d 1c768d7319cc7e90963db8a3697b47bcb2bf79e1f790b062d4d07bc292c9ee33 d677529486b04714adddc22b70b54bff55338eb531d5262d0c24c46fbd298bfd 876e435c0574b74efd5907ce815176414d1e9625545610d1a0e6fbba0762c688 305927e29b9be527f6f3623f1fa45d9e0d152d4f72b81913b75eb8d81beb3262 dfde7d5a0acdba90e3d5bf1e685a458f8b1eb8c98da58333b2f94ce1ffc41ddb 283a36b5cafb39acf63e72b64339b412798a8f1ec223068c7b6e58bd8ca8df09 15 59a576f1412cd21fd06c109f284855bd79b7be0b3d0a6298ebc956d3bd683f03b7e16c7555fa5cd55c7164729433b3bba3e5f3aa41cd9951d865f43e2154e50da193cca48861ed589dfe1c89ff621121a2ec85b4825f34e9fec84fe72f412e08446b9ad71591643cb0d1d0da58493bfad3d4a57e4b1b6ba3f02cb9b4b0439f036cf0bf4a44793b0828401ba51a206d65934b3fe56200053f3a78ecb9e3837701f4a1d0594ff0575fafba7a9f430b534a3338ea44513630bd3b9b940e126dce0ef87a931510662522ccae5e4348f3905935d45e8f6380050e0271bdabcd6e280e86a43d855f8006445450edb5995aa58397550206d5a348705777d542eab2c801d2cd14626cb34cdcb9ade4ca82d73097a4956b50e6db42d7e8bc249b30bbdb09b349fdd836ff59058da8723770607f5fb1178978fac7421f675ab846d98d72086e8849bc938c64faa28d15cf9c2c06f0f9d4e19e313d0bc2f1457856ff3e9b0212074fadd6d246c2a90d5e98688eb887d7503aec3cce39c0050a2a44d84a70007896af0dda0cc034183b3cafe8402fd9e191be67851c2af9cf6c80418fa67709070b9b385d42aa5e05adc6158fbdfd85f96d0b8cb55cd43ef8b4b65b119e9f0c14758747a72920c3daf4b27bcc9324b2d18da35841bccdbf3818990e394b650174fbab5911500d92ffd2ae69ae8a6049f2ce30dd716a848fc10dfd1efe2507082c16f9de83c7c9393e1a895a7bb3b2f705f45f69550d58895d9fc5b058fc2e03085a2637e2c8c71e97b5843aafc886ca0a9d712d28731fd1e7bdccf624ffb20acabcddf8296429d1460d9b56f64d9d7103868801fdb67680c1086219255bda056d531b4d882e1505f62b54487cee9c95899fbab8c099a8b0889cf7c23d5611071359c3d737e7b79379a006e8c495279329d452a82cb2985a9e793203a3d99f0a3b144e156fe4140392d7c9ba84d3cd0ae20ec6641ba76b4c758f8d4e78e2da0518a79d1949b7e11da1a6177afe9c89f14905d8595bafcedf07a92f7a9a195a07394e557f55786ba65d9a5223d247c02cd70ac3be5d0eb6da0b40caf1d5b5790f513b08e09011df1e13f4a691d360676d0ff6dac0f1230c66eb7f53e80662ef0e9786e76e220208021f750e8f4ac8baaf34d1b9dfe2224520a59ad0946f5cdc00c99aa7797db1b317e50f65741d9aad5a766a53d2075feeb729f73ac564562a00c9912bb6f97f034cd26103940df43caedd962c634bc62f850560a1ac2ef4850b2c4fe0b14cc32fa04e79f223d18f17645f495cf4d0b6133d4daa33c3710c440442d5cbb9cd09b1bf4cf1059055d6b193875bc9a4f17b5c0dd7a2ff3eb42fc906d50a817c1a8198be74940a3fbeffcce96f9de468995cadd152b2d3973f682a06fdcc9b78547e46fbc6ac949b3ed29373f56fa656e620b8427e40e58914c4b801a7945d6de34b94cd94a7c28e61c4f7664ad07b19a7e8b7a69bc7e29911e3b40815595b03d32198129ecb1cb1d4c610fe6835dbf90fde4190e80493f900ee4d02aee8085126d4408a5d2158ccf2d936cd7add54d044590cf274253ed5b7e808078637adfa49b1d083e4b6fa86c05645f7e960e1eea146d75cfb56cb44e4794e0fb759ca264d121b014dd874b50d28a5d1cfcd391e9ab6cf7491c3edbea747390c8d9cebf596660f37f4527c1cefa8e51126b9148725603eef510b4426bbb44603c68f169e3384df4cbba9dc0544d7f70dbfbce3502758a79f11a5965492d3470dfeb6c4ddfaee29f9fc6b7cbcc39da7241ce2e127770057c11e87654c6224060fe2eddd61397278d2766dc94fbb5c99f35efc4fa4133bfd1950a065a914d8290817be0ebfd84850a75fa0bb73a567f00e14d23aae2fb4db5d20f5dbb7b7fd2402de2f5e4c57c460844edd43bedf7808c03dd8126b4f959cc14b5e5d408fe93207c2d25c87984b3369febced1d76d6c7c33230aa27d916e846cb3c007fcd81480bb707098cb0563361d09a857771c999eb111b7755f0f557315bf5ceb25c57a0005ae95eb0928e756a4640aef3c8e26d64d4c35c7e02660f41e5fcb6d9e324490a60f488bb343be2a8c8886a73816df50fd05570edb97ae3b62b976db81eda4104abf191999959add0321ee9c82f4f26861fd5512c8b3bf007570a7ef2613d190ad67646371b47849a51a8d83177200a63ffe4cb06bbce5dab4f472c72f6fea70507682fc3d2537e47e5c954fcba184d28da5c65d97f619c849dd0bca084a1a500c0d2e06312ad8f462d4c21b2731b47f7ef0c6ee43727847c3198c6af48a928045ec8d489598e5d21faf5d78fd8d71f03d85dc1d693e5938eb6631be2a50ecf059f06010fca778ea847726258269f9e179186240d60d986a1a49aa8d90e8d310c57311ed22f493cece94ebbcd9a4f691f59cda97152006a5213d09d95179bff0bc7880a8e928463d7faa7e880e65dbd26f199c8366cd424b70ab1bde53c077f09595ccb1ef0c05301e3abf562db74eea16fef684f5433f06408eea82f24480c083349dc83c9d592a1d42c5b6b34cb9bc56b21eddf9e83837ebfb91477e93bc3073d890ef0983e9ad71d7eb5609e06373c9978b99074697f1417ed053c8fccdb036013c91b14a4e31027a81768d0286f2786f447c42f07f01e436b7d03a30fa80b338bc4243decf693a9d1a8f34979f57f609cd9208c9744b3be72c8bf8b66620f66aa8db31e3fe208fd8485642642f800185fef41f3b5f41465ea0d279293fa0023b670e4f9c49dae6a37be67a191de4fdea257acee99236368bf1a153fb011068e74c617cc83be3ddf1fa5fb40eb1f3f2d20a22cfcd9f21c6a771383b446110bd3a44c62fafdace018e2f140e73da46d99d610f36e80f903dc1d8787dafafb03c7efea6e7c4b9c95aa82d28ca08ada518015e737be743047c24d587faf571f0a1d102df0ea165329f529b0717e639abcadecb65aed32759cfaec33d82c9d840cac94d10b0505fa58305fdb4e68e70c177e01be7dcb77b8a28a525dc34b27b003bb0f1bebdb995a056c63eabb79a1dc13b54da389a1191ebeaa2307325c4bfe0200d673bd9ad8213ee07b5cc2adbf4f7371cc43d71c74f3874760e5ebb253fc04ddf9ee31ad868490e3e26d7c3ace2ade3036b119afd02465763822f05d2263059de00d20145e1cff9e4db12646e14f85a9a89a7fe27d5e8492267f4b16b6030c0045ffb0c63c639644dc0e0001a2e98f06a3b6a683d21cd4c8849479d45d8001da2897f02c40fc1c1db8af1b0d676e123d22c3b50050fe8732884e2ed4142c0b126ce88016c2c49d5302cdfcb63175a0694b5b1e2092f8f1fd6925c3f42b140692550e3a177a6938015bc839e18874b75ad972123d0302f05d16a0ec98ad0e0f9c4417992db1bfb2fd4dd4b769c332092e960d604309f2c1b0f5e9281d92ee0edc7f2e8624bcac48070aebd1d021f73405d5c186174dcf71f141e3f1a5e74201b042dc0aed2fa2be9130b5ad9721cfc84a5f6d657ed42558c9b3dcc747f8450356ddd8541529a83158d149e7606238075a3fe68a44b04cb3c8546278ddccaa0c3caeef1d79d329fc151e4fae90e91679d147399f633d06a163d4f09580f62a0c4d1c1a8292a3e2ad44a89c40ec5383b1f7e244e492577eb7c5040cde12b20a0c634b2418e31b9c18d64f7e64dde24d093f29da4508a2d78ac77a0b9dd61e9d038e8cc4383f3a826ccf482e4af76aaf0955e9711e341f300025d0ebe2ad4fad0c525eb6bc83adb10bc700a1038d80931943b895c01b3c779d0e65132ede960906f1b38f26d9db93ac4f2f9f5a83e5e9427d0dbf3a32f4d516a7cace88db5f63080817f61a2e8b7769668d2cfa1d7b3fae50ca261f68ce687a88a8ae4e0178490dbd087e3ba40c2b92afceb63a105ff97d7197cb26434b56d2e6f9a03f3b1030060f2a96ea7ffb24a76ce2fd42c6682789827369ddf7b867849103ea49b36b220067d9fd303d265c021dc7be071d6d61907a35631f122d02b2a0254dbc22c2ff06f75a6310489dcb63cf97650cd931a7f4f0eef3f5e7befb4174a7cfd4fe245202e10e05a24c7ddfe33a8fa02544f8d039e5f121fb443ad08c60f3c482f15397052f86b7d00f9a78209921d1367e4e63dc538050df0ede09e56c98f7e5c1cf9c0fd53f5336363198971797b527e6b00e2c6bc5e88a139aa14c5f8fc4e0ab8bc30801c4c6e1a2927b8558fe756aa57aefb347cd8eb7f148c31ef5e6f4db067c1a09d8555ef6a893ebf61cc6fa3576660996eb749279a5211dea00acc66da7b3020c14bbd033a0d9a90dec0a5b55054826d080759805b922f0df881cd9de9656d204a6fddffbf214f3fb74a4be0fb64ed0fe9786f912b86ff9065a8040ac8cad370b81ba955589c3472bd0c9023d49723663b4ceec8820f23e102c09150e71ee3103ecce7559586bd7f9f9b03120cac314894db5bd0b024989a616e1152ae763c5053ea63d5b02b64df0b2cb5319cbb21d4db15c76bc9d48ea153165f10a57d4410755c13d609d7274544b6508c6d766e9fd90db2aa5d2edb31be7566af0106f4101bb1733a5bcef3c22d9e350d6f74d1757b4b3e96918b583201e81a9e204412b0e54351f9004c68bb23058c2e0752e4e83bb0b458cceaaea9ca4fc6379b98fb30495c8a4a022c0c1f8205f03c2dec0be3c10f876845f96572abd35b1f4dfdd160a574ea04aaceb434eb6a185d620a2b3910e4b11f7e71908bceb2cbf7ea7613b0e7c20225161ee5a9b6f8d092086ac633db6aa54f9cb05c6244355f212088d610b +generate_ring_signature 08f46dc05403426f297685eb99f42fcabaf60427a37299a4d03f5fc0198c44d8 7d3f44d9faab16294c0f4b25db6035001e634e5468e2bc86e5697d63d7a5cf31 7 820a3e42c60db6158fffce2c15b434d8f6d64eb86bb0f59cbcb8e16e1d4ed1a5 e7cffa6b06d0c2cfd7efba6ace8acf9477a68d1e6f7af48802a8b86248750c6c d6b73b9a3c96b5f954ed30433a6e2e235663f649479177e009fb4a65d0eca60e d66d5b4bd423ded55b191a756970f2686dc40ede26bba7ac9f79b32ab471de07 2aa75e9254d33e1c55276bfff754fa9210e8b3393dccf66af6519cf601134283 9e1c712206808e9f1a3c2fc49cbe9ef40fd83755807f1effd0b9e054d28e038c 90b8e1966eb6b2eaf0dd3310eb6e4753061790903033858bdf49f3908a7ac8c3 c42ca66d2b30df139310c5c52880356f2cf0958758eec7227b1d5ce6669cc90c 1 8c364c13628bfd2a1f0ac63c9b88f7cf030202e01f54f49345f89394e9cf8407feffe229102acc4384a8ce76f6a0af466243288d22da4d1df237b8f0dd9f540ce67554ed8dde17b03b449a8a6f2263633bf6b7e90c2299875ce48c1d370f2e0595b3ea50a48164af1fe91eb674bc6ad52a651644f5477f495c3f142a7843b902e5eadcc3503f6fa1766e0167eb9d043b3621bc29fe36238525f75223d526810aefcca66110ddc08084d28431b115a983ec4ac1ff83f270cd56f3df6ac6b3370f06748781a2595b2591ceec08969624c7f78c5dbb2cd3430c12f2dde9dc88a0048238063f83d195648c1355cf1ccbbdf3f286bff4683c4ae9f992150d2b212b0312fdb2c6cab121d597400e41c4de7bcca069ee63afac912979d3bcfe960fab08c3fe91c5e18cfb1a761459fc37bf01507eb20d0c0b4931244b9786757f04d90797f2265fa4c3934f2bcb7ee12b5a1e9f58a0c2e4dbe8e9850e373632264b5f00b46094c15b96288e1958573aeaa8a24a43175e06deb300fb30667e7cbb9cdc016d8ac35f9b3aecda560af04ed4d9bb7062c98b19062a21668a5954dc8466fc03bf19e330c32196b92dcc0a4be28a3d64c00bb07633a9a175414b1ce548cf5303 +generate_ring_signature f2d2c41f0d1ccef9b95f2102685e09dac45cee91f3ab135439e2d4e14b3ce988 37f90d8108c95bdc5282428e8b4481eeb86a74e184d39233e778340248bd56b2 2 31b5d68675670b1b7df314bdf83eb24abaa1c172628cf962a9cb3cbc6347192c f5e3958565b3566cdca791caf194baa34d3dc194ff29ff0ef97bd6264fc5ddf4 7e9f029b07cc1ffba02c9cc54b1ba837a58377ac69409212976ed8697eb58308 0 d789202458cd901620f3f1b671293e22bcc23bbceeea292c1831dc61761e480b7fb62a42215e0121221a43031485863bdd2b46ba6437624b937c2df8a6bdb7093b7ea57b35213573a7e6a82a06c36aa5d61f843fa6ac77b54628fea1cf3f950066a2d9e088046f65511993e27659fb35640dd330fdde50f357f9ec4a01a9820f +check_ring_signature ef54ed4a4277fe4a877c95be3bbf81ba0bdffeb4ee69dee067a4a96befe84256 36934e92869e7847ec3f7a36d2fd05eeb6f62aeaf0c8466360c007a2801c58fb 116 593b2317e28a87de8e8d8e4be14006f96a907e701fad61239a42ce90fed29327 04a4c476ecc910f07cae4c932ac4bcc7e61a0b648a7570db398782c4e8dba23d 89886f4967e15e2209e4a69ac7c46b46e7efd94788980d90b506e94dfbbc5995 a0353f47cfddcc71414f286ce90fd32c6520bc9907bf16d4d6406d7c3d709d0f b080f91bf49053036fa08b916c930fbb2251ea3db7d298e132d84c0e22bc1714 2d2a216f392473787115f6d98a9678f4f5a39ba047bbb93a9a55da6feeb55316 a0795a837ce7e1d20f5da25a31427eb1ed821ca73c7273ea5b0d474b9d123518 07a76693ae56feb7fd8f5b6235b45cdd6f8c7172685a67c26e026e36e6a54277 e928ba5f814c93e7d9a4c5f5832ac364579ee0827391b3a41595989880956f34 d8155456b5980c8eee00b84e17f3593e0ed773a6eade42cc2465e9dd4b71ed1c 84ee45c82bc2aaaccb82b327e080377ab69a24ffd3dd100305d7c6c281eec902 8b6a620271223a844e532ec8ed4215c13d7afd3198a151340ae1e317b5931f56 3209d6c6914959d3af0cab405dd20d582f172cfb444c0f6ba26d33920c3143b8 4a9ab27571d69ccb755aec941512e9ee7fb2fe6643899e681c70fb975630554b f5e74f17921df7619e9a38720fdb9b4f8655339374b73030ca0ceea92f60bb67 8170be56ef10131d6ddf855a7ef4740e378229ccd294e9c084efac19d0146e45 681ff8b837e96feebe9f03cdab27665c0b631342c9944b37b573922a0c58abac 2a6933def733f269489457d06fc49e7eda59b6f4bccb3f42f274d9fe2c803287 4ceb204ff3595af364e2fdb50f40ee6bd6796ce7b767d995e0d9938e5881bec3 3715f3137f57bb471f7eb25e99401c61354a06da2684f5da8c477be5fcacc189 8485735ed53c46b2a2669ce575bf5d881f666817ce57d37bb2074c8f1e2769dc 2dba0a8830d7e8cffa1c0685d93c0ee660510fef2c58545169bfaf088c7032d1 325cb5f1ec41a7ea5f8026a30abe450995d77c6b182ae4a52b543acc2001beed d7b220bfbdf353dddc1c215117fddb5ad350f7b10c6a8cb51f455ae27d492b8b ffe7821a27774777046bc0601433903ac3695604db9dec6bdcaf63d7be3df674 40668b3a7b83afdf64b480944d6f40736558b45e7e8e6dec2b09424882717203 68f901090d85889fd8676b8940f31cd6d0d02bcc53aac5723ef515fff4fa7d7f 4dfdaa6b79bcedee898104e3b3bb50b6d743c681da59a27d59670c33750c645a c787007d8711eec24b0c39fc5bb5742547d063db36136dba5a72ac92d957ed5b 86ac30d1094bcd8adffa1eda479f00d74d8ecbe6579ecfe1c8b44a2fb5b003cc ffd5bd5d4eb1c1a5000bd2c54c95c2e075f5358540c7de3a67d068fbab6a83d9 e5ed42ad4ed98f436cf0e57721c2c941f8c95b7550cf92a641418599fe84b8d1 e3777165b261ad9e2e28561cc2ddefbeb7bbebeeafef299a5fdecebc481d9aa7 2810ce508e6007223fd01466f65d085ca20d67741d69d34ddcf3490322b63516 e5ce1767c60f100e85001f339afe732b7b011e9b53131727fe3b187d4c088a68 8d2732de1a78d8744b00b5fb1e7a0d0680f3b82c6b45a0e902f0a44c63339ce7 d834d9d448e04cae40a56cbcf54f8fca8d59232e2007bd13e7342b6bbbd1349f e45e3ed289cd988fbf453675b2345527c52439dda8a528afe0da505a595bfff2 f2018547adfb91b938cd475761340680d7ba7d70778761bcb21c4c08309ff519 8244c1fe41592fcf8f65f5540e07e8376662be1f402f54ccfa13d2c57bd2feef 43f9b689b1ba2bdb9026d2f50d072782ca888f4f9b609a8a499ca5b0cf114727 d7c4b929460c0d2bdbd9c9847b6978b3673e44d582586df4d1c9df4acee71954 3672410afa12022c330cc9fec5e5e576bbc7bc1224cc3aacaec3ff6eccc36dcd 44533de8fc3faa8bfc97a1866d5b306a88d4c8ecf812d1b2ba73db31dec441cb 01fb3e0f812778f8266dad84d0d5f123aeffcd27bc7736aa9b1abf8518e3d1ee e1232671237a4d334e703566801115a65838f05b567d37616cb737095004d1a0 af355beb96c722985b7713a7b11aa10d23a019dbf83b013ecdbda06a8fe4c20e ed2bf260df7927a0031037a20513b83acad967c4e389bf1df5122869983ec539 9e6be66063fc876cdaf983dbaac10c1e41b402acc6a048dca98c5dd0b5c90811 c45b080f0ed8be80c1029313f2a0c8d532d2680f4c58cb009b0b5ce92d840439 e55f1b1ca87ca58e486d9d7947276b2837b84cf48a907e37e1d0b032f508734b 0a1e6871e425d59c92005a51f45804449d1d16aa226a07e4ec2066a4dbf67b83 2b27b0eb43c85d0e5cc30559d12323978020721cb269c1d9616415d1204b5fec b93a68404510e39c1a4ec91d836d7430ca7c5cdb33002034da9b2f16d73f1207 3ac2edfeef05ef2a7c91d7a74a6a22b66c98bb83b1bf686da95c302f01949015 9c5307120a32efdc6d258057dd9a196727814ebda2cde031684a6afc38aae79a c9e0f6d9acd481ef73a84a92ec19ab1151ce32b331f56309c20f33cabb332b7b e5f1adddd62eee286d0ba884de0655886dcb95544b5873a7224c9b20c857609c 73ec7ecf7652ea9bb0f71a2cf6bf19435f6cac67158d4410f804830fc400ce8a d72b8f55d11c54b5daf1f7b4d96dac024c07380f1615cef7755d03d88de19265 793f656563e634277ad84e40b814d7b72887fe66505ac8e065240e1d70c5397b 1e7e0577a87f47a0620e74657453934092a4e9d2a62675b3389dcc7e95031d6d e4321005245c8a012e5aa3feed28df8922659b32869a20806d13e27e1634369a d17f119cb74c658c1ce2814d024ec658747c139e1947a2a674d68d20e1b6ac8d 6b6ecbe39f06d74099339fc27e1dd62f568dc9210f2faea5811c4949ba9983cb 525de9722b686305656e25d9e2dcab3241b3fd7d2cdfcfb2e406843844387fbc 9944282f0652012c2e2c1e4da36c038fa945ab8f242b8b34f9a09da5667941bd dfbcab5e49ae4740761d5afcc08b739acb4b29084ea2cff1496ff82140e75366 a61e58c67578a49797fab893ff66cc3ec961fdb031f62e74fbf99bfc452ef0a2 c59cf9416a2296d48bbea762f9de062defb722580dbd469e26b7d8a8b7177142 72cbaba51f8c4da80ad2447ea872a5cfa026e68d857f44b998b4803ce4478240 959498fd93b8db87dd49a7ac79a4cc190839f137f559c83bdbe39960efaedbff 98f3896ee2a265dfd5b500ea72b4eff95e8b4f5f950670068593c9f71a10f6a6 0168ae5321fc6e876aa63fa0f8cf64858f8248f88b3bc06afd2dc3f114885e28 d276f7e652f394495c80dd10a4d8b2945f3685be09fd711ac1c67fa382dcb771 94abb7893d76fa919f7150da0011d4e16f0d826b3f395d82705911df726d93b5 7e01cfc3db7a23fac7f67015149ea420e01f00fed4b615d0d4df584111e1f657 69984f57909b7382a63186b9aa6eec395e72e976191d00306198d78d1518fda3 7605796eba70470ebc5110b630088d2f8a97a83b983cb4c01260f35b172b753c b9a0b39ab8a4a37ac3b12d7532898422fd1be62c72d1f48cd0cb0afe96ac46a0 cfc8849cab1c939b78b0685b59616f45055b42b221e61ee4f87f7687afa2941d 64a71218f81b749f394adef68536348d639894f23141fd094fd30b47a72961ab fd2375a100c1074e18e7fb72b7ca74001cb6f33df5113b1f0d911b16f502b94d 5e38dfd10e5cb9f61dd289fb1d1450baf2480eb315683671ce86c9710983564a 4189c943112bdf9fb90f208d4e8be3eadb5bb25ab7518a3b173a34d78e4e6d12 8d904ef1f3ad2aaa812e2e27fa7022f1c4c57b0a7f5abfae77ec0bc5146d7015 8d190b43ea454011d6fc2a29fd5fb5b7f17349736eaa5bdf095bb2d7dfaaca3b 6822d005f9a071cfce366b05972a7f983985d19dabb3329403f2760b708a3a3d 8b2a8b971207c71ef0853f780306fba32e92a2c136ea9ab2f35fe7d112368ebb 50e210ef57f422d166914b0a4a84bc69ff3937ad93cda785da83011402bd4970 ad6292d167a3b224c600c94dcc42231a58d0e88961ef1b03ee5646a2e3aa5e2b 1d7b646af75f442d075c9ef71454c8ab0568123bc391ce7b2e531baee66ac779 cde8c4da3ccbb3fcc0d194f1a178e42ac780047739eda02b6cfc16152aba26d7 6ab2e4c3644b098188d906a417605fe808f4fca76c390db3adcd304ae83b08c6 19fed23f99a4425186233366d1b2349e3becbea8a1e47f5ec0aa2f4b5fd32ac0 a8f25ce0658fb71f439dbfec63c628df67bc8c6427ecabca4f0ddf4407e8083c 2238689998fb1021f2202bd35e8489ecfcb2ddf8ad281ae91faa04a25dc07984 8e42dbc6a6114838a0f7ce66114103919909d429d9f5292d851c7789444dec8d 19d1f5de4d8272f9090ca2e6cac35e24c4b757c04ed634281188ba018ba39329 2b82acc552067f96c6922a9a2c24a90eca428e5746a48c630ddcf096b09e790e 9f21818bc657ee10d7195d79779dcf150b8d566ba0625cd188d7a664aed2eedb a0de8800a53a2f00a387d1f29032801d447695a1c265f3eb308a4edcf41189c1 eb69f1bdb5d80383c013724d58f51845723da9eca0ebc13cd609527270adc7fc e7a20e11fee0b68bdf25037bb62a7ced4242d8c7d52211ead395f6377db11660 b975415e96803513a87916a5fdb98fbf1f6e6ded7f2639f97a3b5c61ce92c772 f1f11d913f6e00d455bcf1a88515f819117983c150434306ae219ff6cee806e8 4faa5253abc920f150664ee54f0e952a5877b6072c5e6e230ed33a097e07739e 8fbb3b1742c0b287394e389596d3da0c16ad602aa186fd24761a287e9cd802e4 80322321839b0bbfecc30f32273c217f38d44cbf248f1307fe42522a50541a9a 1482f9256dbbd11e7b2b0e98eac86106624958fe20156148f396bb00f245358f 6425c4e9c58e4c359fd502b57d404545d4e4ceb42c95b5b0267201af46a55433 ab896784ddacb266d66f2d23d518662be7aa538421b6556aa8ade8d4c7fafbe0 e3ae3e2799c6b40a087a519d5b20cce15a2504d6817b04022fb28abb250b5fa1 311b4bee2910a70abec7af36a69a2b9140ce209d45e240c549ecf298e6229ab4 46b93c0e5123f17cd327fff8d374a8ec093778b607276bc0d6b8944f07f42131 0bc6399818e51ab6a474d26613de379d99164fe10cf8e4c7288aa137565c94cd e46ae694d81778404eac6f01f939326d109192370348ebe1dd1021f8cbd920016cf97fba5756a666ea640f10ae67999da2c913fcffc42e42cafa19f9292ffc0fc33509ccda85e7e5197a1e5e0859b0c3dd1b0ef9a360c18f6401b4e204605c04258ec8d81432ba54357eab8415639d098f125f7fd7c9b942e6396312ce61a708e2239a2125475d5c8c4a9d3f2a2c81bcca92eb0beaafd4b281f28e079d9d82e4d0b91e03cc1a91f08e245e5b9d7dd62cddcdcc5e6e3672e28bac2d607e874c01485be800d15d9c87a66357a8bfe03f5a1047350148d864a6074d67b412e0d306fc28f6ad51eeb711a23693ed5bdb87fbb11cef06ff10267086bf2863c40bfc0ef73cb5f69ef9e49395b961eecc30b715fc1768be314e82195a55e32244c94403bf2bd29d881f5bb618aff343ad9ae94f2e1c58db2bcc02dfc844aed9fb36800b7215520fa46b10b99eb8f61ebccb301ea9eb34db1fd1f0c70ba4817d253686063560aab9e06fe848271f5309ce49dc2361c7e6061d57a78799a661fc5af13c0b34407c7cffa73cb7aa7aedd1403e210539da9c60bb014842605bb4564bcabc0874c701905a17b93d5a2f6fc40408428468f15efde841801232d5aa7d490b960b68495bad5208146ce9964e8e3a536458a40d4deff430c54600e082b29b466603147db10fd8ee500ab21430d9e3fc8a311b93311b70c42a15891fa266b196c200847a0f95f47a47e5e234eb4bd9d04b651c433fd51115b5b228ede43528c85102eab16b16caec2c77bee8e21a9b4934b76dd995ddaccde1e7940cf6c3cbd27d03b90d698562edd4607e95d5810b763bfa2f87d5342ea80730ab43c5f35bc45402ac5171989c799db108671a4b8c500597b9652feeaf7d30b10969170217415b0034e0353293913e7ba2794ad317778bbb554b11f1e8638ff5578603ab2e713005e59779f59fab541bb68ca4e565cd97ebd737da54de24dad4681c49914c195c009d544985905372ef8c4817ee40850f29ba134e04f24619294844a3779e175e02e8bedb662cbef18d345f082129de2f810c90169cdc8083de7f600ea3e1fbab093dbf50f20af2dff8878da8fa71c64b9c3359f838846f92b161ab3e805daf5004cca1625ca95786bf287ed2b5cb25b09cac1e994cdddcfb7dc1fa34d7022bb10b1835a2d6a43a2022b09d80039da3414b65d746fdea7f9b460b89a2b12df4700b2892061898798258e1d395345aeb87f2663af5b5cd8c3241cec7a4c6b991d102a66e76e86ea236fabe3b8e7a7cd8a7f98d6e67a3c9b587f9cfaa6befcf189307cef210e24338b3ab7bf0d25dec20af0d7c21888be065ae587c6e6f20b7ecc90725adbce54edaca97180f4f1b71b80b543c4cdde5cbc9716f232090b2324cac09411a65abc303014a7ee47c963094d6f1468615e6d1042c9d714e475d1c78480b4bdb4eebc0a94e22272d699364a56e6a06c0c6147fa59f9f2f87e4c04490c502c8aff77594785d1c70d109bef7982516413ca2e455db48cd4c2883d6671bd50aa4950fba435ec5484f1368ff612a23e921ad6292e561a0a3bf72eeec321d8909c24a9aaef3bfa8297d1c1ed6e94fd1e1175a1e800af2afec796d5e158a70be0f80fcff350a03020b122983d7168d74ce36f93e1f9d8559439c57b202c536d80976c557366cf97dc7e3e34508cf5cd523fe8a973f9017d74d110ff2f294ee5203dec2c80f40177af495403b592023960f0baed275c6107e14dc1bf039ba7737084325487c74095296d6f904673722f76cea54a79e4e577741d59971fed6c2bf0df01f7c4be7290fc2f34ec251fe021d2ecdfc14d55897a1bd6c0afe328f1fc50d7f33ffdd3ae8af1c09a9b4ef7ffb26139a4366f6261bff4562c6fdd754970a08b88b97d7aeeffccc5339f7c2675ace73e32a4f2f09b49b7f965d8cc8fdc8a3041507c6d2ec256507c56bf98d6a9e9e91229d5e226a9ad94282babbf4bf67df0431099e3f13b814972bdec7871c3e6f12b20a28e06659f6ace5012ca988e9780155629f2902384a41145343bca1b779598197c85a051b0edef34148d1a0f7780f1e143634c400e2d1f7e7f7bbc0c9e9c8b5b694c0cbbacd0e3ba5966f65c1f909a3dca634907afc2afe842e49d523efa6ee46bd792154ee4aa2be58c219fc3b061f8cdd1946d28e401033c63ab2d26a519900c92c6e3477110450070aed357a012bf86c3c9ead89e6cbdde6334ba83ae0612d2a5b8af4e4a2afb60591e7a7cb0372d038c50aeb51a7fc285b57b12324e0e3f9e1b02270325d01ab7b402e83600866068a1c38272aebcbddad7ee3606abe91f368a214c5a3e969d693665c8ade023d8db2969bf1a54dabbea57e144e41e9094118d055f250cf54bc0629d818da05f91e0c4aa4369230cf54ec24da7c84a7f2a1089b8cd05dd65a67079642eeb301e5bf2f2508c999d9febada481aac1b89ef74164689f81c80cd97b0b78de8b80d6e982df17dabf141275689d6d6b0512c224e981b7df59fff8dc072c11edd0404c0e1d22fa024dbd256676eb4e953a0b986756a0e2feb0c3441a5ed948b74f10ef64fbc81c7eadaa58d0c8ac10356922c8b75028ac1a2c574047cca45d8341f0bd545d93194acb3f7efdb9d58c4879e02b931b9738e84d65f057c8a932f86500afe30467434c14a4399f13dd78f8e469db9cad4a6e8eb1b4806446f959d602308a7877b5d8edbb6167bac0d84627b8a5fa90ef6856279a66cc75996badd15f20f3b0a7194e34b378a9cb5a96025dee8fbd7c22bd3cd5ee0070d1e28e3821af904aa4b926d288a6584287716a5c760814b10c68f274224a793a7eedc58ea5abb064f764e6c53b1207336efba38770fe2d724b03a86c7941fcc8c5ff967b31d230224e8158225dcdd4f50e7d20c261148711d34e81c1b60f733aaa57157ccda040cda4fa4383bf33242736d31c92240f4c0b93fdf0b78bfdcd6e63b868c94f8a40ee134bac8147e990452aebe6770e4418233c71f6f36636e77dcf55a69235ee70b603e8126874345d67ed6a52f5c2e7bc6066080bd67b73b3f9025d67aad8eb900cec702d060a6b8645a5bd5cdc92ceed2f9209ed500356b692aa642e4ccb0c70d1a5143b8ba3663ae55558e01183e888b6042aaa1d3e8544dea52b85f8ccd070c74c6883076daea88360134db5254c26bb4a58dbdddb8d041d80e1f169c1a470573e8075604feaf25c70ddbf28d99c9a979c5f8ac7e4532eb8c05f5247c7d9c074130822af3afb663e221cb3b4d2e3a1fe378f99e22f2735c2564e89670652600c14733bc9424907ebd3fbb47a5acdddc3a0768f7c906b87af665f0449f819e0f0fc6855f1bc08e342c71c28d00a115026408ca424784af13d7a2a9315ed32d04d02d2a6ac88786808eac4458d654af1f5351fd6b6533f94314449d11cb5c6a0ef077db8481e79c7061ac0d22ec09f4aea866872abbab53c2115f29441c0f0b068a418bba55eaaf299addb0db7d4c6b13b540c3e790e6e7de58065968725c1a0e4cf8ca4cfe1db9272ef01f61335b7d0327fae373c67f2ef2c1b4172f9a00610f1640c33d12fd661d62e294a5fc091ac2cb4aa2b8149e99d6a12905f17d11d506109ab1cba13f13d596ab0984f7e6913d1dce0058d88778c4889b828625e60003e6bff14c360573d7239c00110ed8d000f6bc5858916d51ae05322b42438abc0bb70f0202ddad23d4647d6e11683b2a8b8362a312e9b1b1fecb8e5f4636bc0801f6c1c63026fce8d31cf1efdaa9f7558d2a9120ae366cd7aab41623f9eb0deb079d41ef2ac5a3bcc31be5e4b3313d37bc3ca03cc28e09c05d90cb02d00868c70a5bbc873c773a16d4706eb04f7aac459528adfb861fed9e704381997706c6f50275fc9190f00cd7e4ebf30373f062db5d3338e6aae2bbc2caba2d7c7948ca7b08f87a4a58a138bcadc00532b198081b4ab5b22b56dbf38f0fa373abd3022a430cc88eded2fb7f2f82405745acea0daa69e355a613c6f6f60e517e9ba4df421408fbe9ecbd3b8e138ef11783e118dc7b5d13ffe473a193f5bf8cb0f037a195ac036fbe2d36e0e1a87c0cfafd872ae4cb0d2589ed78a6efee23dfd7cdcac504850f4551afc015682da31b111612fd8842683c0c267091965e6a83ba5031c193cb0fd6f90a7cc1712b119f421f7b424060e3849b68d748fe98367205ac416b6cc5020a1ee31e3ec8acb1b10a5d856e2d8255aecaa012e8439ed8b6734b19575b5a09a3497d83e6f1cf2f26469a734ec78bc282acd9041a03c1cad3743dff8f230d0811807c06b908dbf931c881c6e84f68a06e05efd605ca9019856c5fd416d6d20dac423cd95239ce0c350958607a67d2bdb86b004eb9b6c6465935ca48c7893a000329c96d8690bc7b0144406d9a145e38b61f2faab469faf9eff0f75b8026130e88cc25472a711d5f10bcae663d943719f5f2616bd28efed6d118249b90218d054f2299de9b809c1e13d473bf11c9c13fe37dfe71b0aaa3a5fde03cc5b00285069ff2258643d151a92d5c707fb73bab92f29aea53b4da3237fc4b21d1d97ebd03b057abf9fcb43f890970b7ca598e1e5d7b4015671164b8582b84a34b5ec75d04fb6aedf32f79972843b2f4ea5507f1eb0c32fea44282474cb8911a4f7e0ab20beb10af594336e1f192b804416abf320a8881068475dee5fa45fceb82290b610761a18bbad57f81f658c8d0009ab8b534852782cd2af2bc428de91a0069a40d0b67be602d46832b00fc958218a772fa2616369a92a614ff242030950e5434af054809aa8023e41634e59f3da5460f06e2f5353801b7021c55592627197ad2ed08d99163d7af4254c8587084db69e291d32aee4f09d8b40f0985674fd8820c9600a44a9455351f7b2d96e71e9b60966f818948671710144270a6c3a7f850069c0ee095194120b8afc657c4f42883009544fc3ba9f0069eec6cdff613cee604ad046f33b014460e38680f16f51dd297181bd48bd0b89f184af7e5304b26d3213403ed6207471d3c4b8d82135f33cae71ae7af6b2ace5a1025775e05781b18dccd0d6bd98ff24bb32145c58c0fc12432e9554895cf2fc6bec8c4a4552edcc93bf201795231a47994a478bb15ec0419cad84500220995e6674537763f404913f7c100f5c5d115495f7b49f627e94ad4cd810e5b9da015b33e8a5acf502e69b6a977077cd962a13d945586c1302f0009256bce88f388e3b8fcec92bd31982817671e0c7f0cc5cff236e33329481027ddb6713f063bcf02700c3ddec4182670d56f87050fc96b69e91db880997f29980565f0c27cb68b763861ea42da26d99589873807eef120b9cd6bb52e2cbc1652fbc089aa860627bff72fccb3305017c0957c15067b328a034ef5a6290e145b19a64ae01859984e7d4f9ee6eb5becab5990a06f0cde20b36237544058537c3008c554395001a9600bffd5efc04e3caca939e2d20d04f72e0c6929e48155e44da659157cd1a8957e624e121227b2a447934bff200cf1b6f18b4a6c8b60c07fabaad1f55390658bada057d678a6f042fb1842acbe018e63111a4632bdbf830b13542d6d1da730f93b68a586eb7ada82b149fb937603f55063bcc66169ecb4610aeb5cd72930a4164d680c64adb566e449cad0222e08906a2b47bf381c5ef906e79985bc4c2a21282952e30797670adf9df57db0eb005b65a7a936249940d7b33c3cf312409b1786f67d5976e32acc6cd816749ce70fceb4162a70ae0307c280da94084be49e3abf7482c78094380d533dbec9efc804c16fad893d32575da28219521f39ce83a2a15ae016a571a66dd9797c921a700102bec68e06ba46d56265c46802412fb3d26ff9483d53441d8471a5784632f30a03f8146d511629ae9d5d8097c51da11353482e1ea7f15d7578a93e1cf1f25802fc8fc3f3ab22f45884b7be78346d00af7a79c31bd033a518508d03dab36ad607de337a2da703383fff4409d9ca623c8078d57d86a2a0310c5260899a71025906287ddcd84ad9fd7e893ae8680d49a6b61cd211c953dc63c91a67934de72a9506a01ee67d1eac7807af725272ecd8d546b6407eae432256f4fae7480ea3afcf078af9e9b9aa81097479b1ce96be4f4835ead266d32477b690774a763e777ab20a50f5c5347dc6f3a8d37c79567409f37de42670390638d3d3cc2bd5827440c50b810f1d78ce286ad3cabfe87623fdd9547610e4a0511d60c77e2c3482bf9a880962ccbc187b26c7d12a8ba1df5879b7f138bc46a95b10976ad84d2cb8fd4aef0038545c2bbb6865479d6bb586e51bf4481a9bdac20f8085a5f53724148fd5ce0ab5fa2cc59d51e7769330b12fdfa45bc880d8657b921b71b3b37983bbcb8e0f08cf5ed04d18bcbb8cb0d850adc9a306cab8065f9bd6aa04ed95138ae1e8ecf904b61c54456bb7aab05aae746e5424bc80eb8792694bf54ef17eb5db10bd7c860b5542263c564cbacf30ee7534d164b03524dd52e1451984d37183d67334ea460b76793435339826b0d81f0cb13d98b2c4f5a50cda6e193fff9f1584cce6bd290fd8ac1a14fb767be7cbe41077160de018dcbe9f892540317499d2b44f11e2510c9563959d8ac12fbf01604e536509f42c8352c9be696a79fe21333353e2cea90e377e3a96f6af51ec862c808bba76d18fc9ce49d2ea13b550fcb6e69b58d62f0a207cb7fe035bd815398efb7df2a08cd5ec619dbc1b0cb41c1183d574b4c5490b30845f2024acbe3d2fa11c8440abe51cf9dcd4bbf15f7c32f39d304f822cca0aecdbdd8976fa830da6fa20f88772bd9fd388c14b4adc3c5bea74025967f9f70b7291d342b9dd0094c033729215fe6647de63aebf17eea04b74584ac2ed9e4601fb03112b7e4eccbd5e988bc24dc69aaff1b5a04f04d9cef8245f37b0879bdc0457bb51af18ddbd430beb6223ce91bb12fa81bdb6ad2e899b8414fe4a9affc10ed55e8b0eaf6dafc789f5cfbb0f2ee22a68e17a460f7b3f0498d49446b5ff19051708329cc3c71052fc7d944e47bd6afc4bedf8710b54a8bae3711f26cc24480cda4d8c4c556f7238adf89e66db71ce5ada40e56693e7366569855e03e23f860bdc55f1c3c3b5b3e5c383c68275c805d2fd51728ba40ae3aafaaaf884700cbf0873b0367365491a96021b3ae51452ff3e20266b6040b336de2263f9ab93d72f01e217cc61dc5cd74078a839bbc17694263871c73a66188bcbd9747c48d3adf30160cbdf64bf235a4bd1c72796f206a0a67e9aa8a1eb65b8e13c71584e92aabd09d542fd3dac06d105ec23d61733ad9bb36418bd8754de06abb95cf82d33a8ff0ceda6fd06945f6e6467829bad8e57da9c3aebd3cfdeac1b714d0a4b97bd378c038cb199cccea6f35e07acd9686a04fc344b19702d8a97567344163f0ee0ac040c9da74125b1173271ee927ccd5b683e9c5124ad87e3e8f4f13592b5b2ef2ae70119929840353cf653c320720d8e286f8ba1c5b7413fb566c0aa6d773a768c1a0ead205600a2b3fc6162ad6f4555e4473d87784e3c293e0aaad7c92f9ef8bcea084383dcbccc89ee40f86b58f093467b915574e610fc25ed7f5d7adfb9c71c5705ffc27e8a3d499f6380323bb71053aff6e24194ab122402b506e36ea83163e20a556f91146fef3ce798d7b847b7fb0e619d0d23384beedc80fb6dc74d0b32b00e02e8248250a8b933fa278ac49fcd3cf7b17a8020856d79559df92715232467036faa1fbc59f5f3c30d216ca7282e949a6a592660301075fca6b86dbe8dda44061fc9ddfe58f959e6c1969b14f282e84297a3e9e6136eaa6d16c9a2dcd7e22201d30d17c55623d0f26b2caebb8f212feead4a48ffacadf2541b4636a56fddf70520fbcead3a0503033cac270460d3fcca89fded0b7114ea6b558755d5953ba604ff965331775ace57f67a53f1f9572e3f523b392f553cdff9e10c80277f1f480c38ab5ac6884931360f25f8ed3f546e091e8d030bc0c7703fcb98cc3a31513e0a5a0ddfc6a497e0ad1180eb6ae7a31fb2402ee15c8b8a86c02e134fd9980b700e5cc28799f94ddea05aaf6cb7d3b747f7d929a85aae3e0e6720afe63299aef30ed562eebc4a8ff62739f9d791769d91602e9436c62f443c03c297351fa462680d78ea9a968e3eb01a321218d767218b6fb76367f2ed112b797fd0b59fa5d7fd034df98a6b579275b690dd042904728c3768f333fca36f71c2a726b3f1078ef80bb75f7b203e530aca0c10caf47563ceb3b3b52d73414bf1672f54255b0cc3fd086fe844c57dc80fcdbcc1c76e8e0ccb188664617f35a2ee1644818173d02d600f1d4cbe9034d685f562cbade2ca0905059a28760e048ab51916983cdc0ed0be0a90bb0a3be27f7f1e8ea0fa902e499f0685cd9792c4569fd41bf5f709a3a13f0a4b91939150dc2951e9dd456bee8584e79dca1d6f5c8c1b9f2ee65b87d06cbb0017877765dbbe07b9c90ce3a0bc9a74b7d0f921d470574d91489c7d460f360407519fb6a06c0852c206b099829af137fb5ff4ad4cac0913ba6c1c08425f31490400eb3e021b86ca5251a57ac3d3a701f6d83564991acce3138db5f426ab311d0da95fc19a9ee9f3a5569d302e91316831a71ce036552d384a882556362f5d5105dd18b8281c785226d7a730099ab69939bfeb811c7c766798ce4881c1e41ca30e5d8af49c0431dd2183c3e2a2088192eec165453e431a5a0f515c162da07ddd0c51a9d8cd9afe58ec5bab328dc8915a3e15335d749973dc5d8bef208457fa9a0a753f2b2b86472984f9782e66f744c0b4529267bd6a54f4ea2d83c5ed56266004c7fd9c5fe1b092fc21017a0003f15de0aa84e3465f4a340ba0d6f24022ab8e056e805ef0049b0cc7b3bd71f8250e4debfb30d0e023c5ce74d9e04d784bb23904aefcd2f4d7a7fd9c53e97f4e0e63328a3ce25c969749fd7ea293cee3137087088dd768ae7061ff65bbd2d98c734ca44c0b36fce9174950abb00ecf2e8a20c3057dffbea13222b5bef3218beaee7d719cd670f0b8bae73a26e8b2ec5f7d5dc40669aab8bd65b2013ea27cbb365c11ac44de9ff68fb173cfaed6b479ae17df1d04144515ebe96bd476236558bcf344fc7e1938509a1b02b1cbddd8fa78ee9c080aa2e63c24ed05e975eafffacb819e37fdd1f0f4fa6fe48dd4afddac0815cb4d006c92fa972f175cfe5a70e2bb1f52b7a6318e22924403ba7f161b55cb938fbf09aff8c0003fb297ade0b9fe2c1e9a5f374d92b0b97ef3cd6c3624d87224a6480295433b252f977b306c163637df78c9c314459582d9b8006b2dd00f7c74c03309e2a25e2916ff220141795604060c626eb5adc275cfe8b06b5cebc9da2a93aa089676fd9e460d75650fbfa74ce396663b4993d988f129d9c1d449b893570c7c0bd483221a29a6a40b1a42ca198349e22646f343aa43f91558cf36b6a3e62b9606f254aa238044a457d42ca0832d0bad1be75d9c75d61533d026b326c8a1737002a3c24a432c553a4323b23a1b052b4058a1822a1c0aa7dbcdfb5308ce8bac5f086c166b1313bca9b147185ac6586baf0d16fb8e2834f534fc03f274a7b8b7dc0e547d140e1fdf652923f1c2d29028435369a2b1434da657ce52529a8385204f068aad478461a7c597f2db6acf74d953ecb49e70aa0dc6584f971d67a54ada8a08c42ff688d8277855a4c629a9dc9f3be495d20056469131c781af45df1788ae04716ed81088e73106dde633e5bc0daf76e5a3f4a415fb96245cfcf628bd0439003da7b1073fdff497030584b1e9863c55e67beef22596da680f600e11f97f08094edf179fcce022650c09666b6a5dc845969f3920b82ad9fe7ef7474c4cb9d50c98451e326ea9831d25c3d7702baa7526986ec56919f960c96fb5e7c32064c102be2bd7dfd09316e586260f4e3148d18d18d5a8b48ce583d9c89008302a82ba0612a24eb7bdfd1cea9c00a93b4d45656a46bcd689eb8331a39582dc7cced0bf0c3ca05738c8fad0387580f8dac65b1e89e69c56ed79bea6eb12aedc94dbe2bd08f55ac5f978aab2fe82c5ab4f0ba09b4c43890bad8cc50d76070ebb6153c1770f1fd34c2b7f2fe16070fab500540e475ee3b981afcca97298022aa5027927560f382b39bd4582881343202dc29d6b0617ebeae847380220aefc7ac3ac136658017472e17584313364c19508e2d504fc49c7908cbf5d14722ef2dcce373ce33d087050b923df574c6ca146af7a9882a39abae944aba8c95b01bbd6857084f61b0fd76d43184b26e9d5741da4797a9ac337dcf9e405c46dc8d239e4ebe1a08c910333e5decef8cca544f56eb1ff773ec32014009cde917f7b3de102c098f1e8590a279f054dd95c3efe96fbd556066a26cca11c09fb045c9f0392cc9ac5f163be0404136940babeded54217a165a2d4853a8dd0a939a734fc72997b7979390fe106a7e76ed882eb85d6f34286e5f1523878bceb12bd6bd270e1bad653a92a81f102 false +check_ring_signature 296e3ccc245939758f69fdbf9713514aab60863099ebb9846ac7833505808184 4bcf16c2a11afdbf10091d8755a72964b9c091a6b526d4bfc64f700fd4a079bb 4 d53dbc3f72605ed69b838d08525a50e5143807874c5d9d279ed944de8bef1936 2f2480e3d33ae7524a04c0f9bdee5d0d50776873c88004df84b6e1d91f2d430e 326c1b5a6831f37f6ef67a223a30b1b6cbfe284e9b15dc5067f3698692c0752b 43f24f1fb270cab2acac9750fe3d4c0dfc0216313c96fc36e2e642e852dee2e6 6e19e09c108cfc6adcd4c64632817bf83c015b38a57e5608089eeaaf05e81207a68f7e7dabee152a0cde9744d8c718868e5c5cec7cc641b99a72dc7b6680f40801405df4454093e7d433e1d3c7281e020523e179e0cb3dc7ce23b92f71dd08d9765d7bd915ebf8c432e2b34aee595f9ba409f1bda836fc22ab4a6e291b02c3010f75e7c447b981811b63033307dfc0c289b58134a7e6e9a49729f373345ca25b9ccacb11ac9e5093aead5de11fe98eca48155aa5dbde69445c7c8beb6428c50a50c16f43bd5af17c65ca192a05b4a27f60d4704d38ae9e31c4debcbd6862630e54b4dd4003a3c3f1353cdbb5eece18f56d75ec55110ce5e747f607de61c4f30f false +check_ring_signature 2ac767100fbcdc03e66cb1128447cc6d5afc6cad6a2d944e62e3964570eeb4c7 23213acefedd6dcb30693b7db81015b8f1771ee02d62d31a69d05a7b77a8875b 17 298fe21d24cda414ce09ab970fe07a16247afc1931831858f682913fbf212d78 2fea6b57b40f7c199386f392488077429cb491f0380d649a87324411bb1df2ac 0aa9f5b6ec5c8edf7bb658fd11d5216eeacafe2ba58fae6bf6b9907746c1cd36 4e9ef8e890f510fbaf3391f0adf30dcd3f439637978314721238055609ea83b1 9533fcbbf456c9b22d519a25f1f5bc3099c71aabefee801277d0c19d909b328c 4e7bbee6985e1d5604dba2374630bbdb9651e91dcdad5ab53409630d21d4b5ec f465be28c17f9e5ca05a8539b748d1cf27543f077cb723eb1dcc398dd6052da0 d12fd1f0d68fd76d68e44ac2de9f0d82fa476f50f0dacafcbc38dbf6f07f3e41 f5e20b918db78299f63ab9ff993ffd9b9c3e50d32b89d8d68f1cb6ead3fedefe 434d9a34d16c709ffe515aadf1550ea4ec16811de01b5b45b7deebe33a03e15f 0eddf05d9c06f9e3b3569dac31b14169695d82348fbcbe0a05802cb88fa0c077 56897ccbf2106c26c33002bf5a27e3c842108a1fafadb3081042388f3f4367b3 867b2edec0d46d9d0fd14071c4c904025fd0c5ceaa5b13391a7303c9755f3120 86e70719cf72de5564ccdb28288ac1fea0f41611f5437754cbbb360c4f865ab9 a4d54e5893ebdfbe424e263d448a22c5cc15c7f83f3219cafc4eadf3eacabe37 b5335377e3d509a1e2232004a36ee7402e3fd621662cc81b0e4c3ecb5b9f2bd8 2936f28985b08db8fb4c2f8c9b4a48c261d698e77ecae3c3232c69ba4bfe155c 58711ff1d751b34c3dac540bb1d31939ecff7e1eaee0b838086457b6d0777f09113e5de7e3934008b2f6dcdb2a2d37c5b0edc17c344ea8881da9dd034cc1480c3ca191be72eb19c71febbc07a5c928a3122665e8f20a44c180d91c49973ce902ec32005037e485ac0fe75eb8ea954a50df3961db3dbcf308545032a97112850835bda2af70a90bc14b697e2216f5a576034882abcd777c3ea4a81663eb30220407955c4c5ddc9399be5f1ca12bebe66e27d4a4a7c2da94b550a7d9b691ebac01cf7e867c3757ade0f00a99b67fe8ec61726b89f69e9fb7d8b2db6c008a2ad401a724892f386e52f4dbbecbbcac0cf960dc07c6c436acc55920a265111e1da60cc4823a273898606b1e369d65c291122d6282c434c870f571bff76bfe475293081852f4e6adf179f21fdda6c2422850da980d33e8cb5eaae02dac28948932020e85f04679c3e3ead1ba125c6cebcb378ff2ae943bf05bc381dd0ecb905a8f7307b338bf12b77090f671d15f3e471d7e1e0064fc6300196f6e084e15b040efb901dff0c47f15410cc86356c03e6c12b5ef2b92e722f48ba70858ff4d5a57a3f70d4390fa3a84a1f6d40d5ebcb5f3c12bf062741b1b8bb60185ebe67670b2bc1d01dbe0e2d109f439b0ad7c3b2ef7809ac1b9c527b6266aa72e40df9d9aeead140072de6e0f8e07c08d86321c259a0859bc3d1ecb9f8e97aab57baf7bb48db9aa08e4462bc3c85815438990ff1d3f43ba2a5a578d965f2e56922a37b01ba2a923003e498068cc52f1bb3774e7707ab7ff4a7709e6b2ecc35c3dbdf7c31c793c4104a00ebaac84248b2f715da5b19954002f3ddc4f15a1927fbe79ccd6685134bf0d3052ef09e516fbfa6ba386ce3a68aaca9759999ea33c7e61db675044ee14110c4165284f6fe99ef5423a9bc6989dcce324add07220348d2184364832c29237042bcae805c49901ccaad6d6e2f60ae7c28f38a5be5436a8e0332beb658c589e0ec184148bffed9ad48c2ea745bcc8c29c83180770feff65203370a6cbbf7bf502138ef27fcf1b3462599909e9872acb5f9b070b38641b252494b8a2a0a2140b01f306cf740e4a15460c789fba8ae13a6a01cdd4716d49b61e44a3e00c76cb38097eda138f650ca88ce658c9aae9ec0157c22aacbbee3b3c5780cb4d205eabf409f0db3f0f455cb1865a315b32959beeca262b03344c6d8db8c0f4bdb70e27ed0a79e9ee8f70995e86ef5e1c7570067fc89669b2201b9b9c09e10c23489cbb0509c824159ade2d050662676142429aa1a633f041796243615d8a0587bd208f86043e4a35fa57dbb45cf2e89063d005314ae80211d9d524ed90f768706c83e7280340a95f9ee22b917f2036b70a4f50cbe461ed0957b9ccb84713f3b30a6a4a080f2e43acf6d02a21df74e6524babe5da8de22c9b4276883614115a42e19067c80963a51ed52b0549fd7ea087a57264951ae3bd0a854802d56b36227e89cadca3091649d81f37942ed84524eb62271a8631cedf385b4c912e873f952d82e36b9603 false +check_ring_signature cce82c95137ccc04244947a45fdaa24a5ae4cde569996fa7f937d269d95abc40 e0b01b311b07a0c72a2bea7c6b5b530bd510aca4fc734e76877ecfff79f969a2 1 2282c037103e31d3eb42e01fa9906536ffaa117746b6b502b45174e4e6e7ed3e 2644c7fd9928ad2d578b9d3c83c59790116aa306d0b25aa84fe4461fa36654695a166b0539922f418ccc1f1e404ff9596c4fed7eaa46f2f4acf6e4cdb63ce30d false +check_ring_signature a710c566aafef35f9c32372872de97a481994a56ad0745d8cbba107a452e4df9 cc1349741219d5ebf573b357b8c811d6eec425feb676e8cae97da51ad0bbae37 8 c8a388b269de84651ebccec22a13c3c1f9935616d31f3fb9e33ad971068a1951 e6d44d672976170efd8b23b00ab4c61be46acfe961d9f5e2f529015425f83cf5 6505385b4e8491db668f235b01e6684d977f301549451a9505894ef598bf7c7c 02b20bab084e526300190cd04cb5f25b808239bf51bd3b27834f928531e153c1 51842552162d12dc3827a3ab007dc6806794bae49d49d93e82ae23260b8625b7 112968c3641b2c6cdb5f244f0bcd92490529388a6f6b60431c430d63e0e9c188 0bf99edd285cb717476faa6d7fd62a4608b429f65cb431af9e7339d49fe97339 0d7bd1c3946ffc3c53e0d3d431a001238f3be5d2eb777e19ebcea0024d079c55 67a684abd479fbfe9fc3f3e3c83506f416f6552ae736e49959e0d7f63228ea72eed267a074105a2ba7f78fab26560421db6678d6f5ff45fd8c3fdc2f6a57c407147094826089ee5d0cbf120a15a34bf777b3cb31b3b3f1872e3f9a63e95a9e0e0356899fda7c74784e23b832556b9cac84bd2dad1122c86e3870e2752dec83c1399c76d6ffcdd2ca592e32b08831ba02e133f6515592574360e755fa06965b005ef4b322f4769d024a012d701b47e0711639cb5c2847f1e2d0e5279bb23ab30684782107ac0bf6071b0ad9561b24b96b005fb1290fa5885b20621bd3f66d4d0a6ace0c97f6bbc093a17bdfbcef68e3ee29762ec480e89ffba325b2e616ccec00958e8fd4697d69d893a5f7a1da1cbcdafb297b958790ceba88c9196f8e3370004b90e38226bbfb069e4e5b2d8b7b41b85f66cc71f06f794166eafa41b2e80501f00c2b885d32ffa4d84d4d7b7c258ba213164221537dece13cd750997592e20b6d81f8797020226957e0e9a2f1dda8849fbf4c18dafa6d8139bf078eb4ce970d18642816581e568419ffbe63662dd5853df525606fbee719e5dba9b2a58f5304deab10c30663209f29c4a888b3e3bd8e42fcc38dfb6048982e116f163537e9093d05a969c09039d6b8ea5058f7526c8ed7cb7a1237a5dcb61ccfad732abe0801d1b52e3b7b384cee749097862bb1e4d7a2b0144462de630d5738ffcc5b264c0d false +check_ring_signature 6fdcab19e69bbafd126746a902e59dcd2314ed0e9239be5134b70a3026133504 1d80a225914de00255decbceac48ec9b1ae7e761d96bff7d8fc309ae609adb01 10 96f1f6ef02edc06401965c6661a8c312943e5dc549a7f4527dcb207d4cc678bd 0e1bdc5147385182adf839e3ca10b1945610775968c53cd0a8558d3ed43d83a1 be9f2f51b8e85b3ad7646edf32546d8f138fabaeeed502ba96bbd04d6536f830 e00636f9bd23df3340d1155395423158735825fa69268dd35d6eae22d105652f cd820e52e503122757080990cac6efbd1b145647530a22a74282665b437d667a a6e55d1980e2c0a5fce83be56d46eb144045b2a22eb812528896a4cbd10f7395 9963aefa760301384a4d215e55709b1ebe25450fc492ea1f5230263a11be8dda 00d5360c82f6a60986fe0f0df5862c592f01b13f2b9a6699244a6b1690d5a7c8 9c184f62cc635156459f0f562f29c9fa442718b9701807a962edcdae932ff23c 89ccf4beaaa0e8398bc9c46248a525dce94fa81d1b151e3ec54230422770c509 986271286e0ca8988f5c62cf6ca0ea125b9bd66eaaa7d286ac2218ce975a8a0dabf10c04df18528f3bd02d759898ba8a4e597231622210b320a9cb3436a08508b0d1dff623e5989ed0b627e004240460c4eb75ddf662b2c0f161288818885803d32c15878282af3ba966b68735275f787c770f07b5c7a64a47ece51e2a7ec1055ded852b79d0fe17ec0d5dd81bba384a2f43a5833db777cafdf2e13855394b08d893b73d461ec9cbf137189ad02f26a70250df642f2408b1fbf4ca60c378e40237b981617b033d083fd53b4702164a80ca74f11b00c93dcb353cc18336e791023c1b3e8c1f01a17c31cec0e4e18fc0078bc7d6b71f585432c7ec6f64b27d980721b350c980ba120ab55ec23c4d0ae312cadd02073dddd56b6c142ceeb2b1e605ac0938d02a40ceb6689601e144bc0ac15c552ca21f7115d953ef3f41c230a605c3191733cfb14081e514bd7b3a1520f7d7613c6933e2fcf91cf745ac2c70d40746019416166da17772d48524a75972f3ea466138906b2f816ffef7776749a00e66e9e2afc0cdb1a7123809cac44cc857d78e36ba8dbcd71df2eacc7be4dc1f0ad9323e42bb91fad3c0aa928915d955816a8c433db3328c1eefb9198d60db920d600e742a61a3c1fb4da41a9f1dd852cf26e082df3cbe2fd0980976391b93e209c6b4d0b838b9b5a58930e142f7c51f5d54a4323a9250f79de3593313ec463303e02f72df35d37c529100b2ff39666b0a90bc9ab8755ebce35abf657c0d8a0d0ad42bfcc25022f043ce6e8665feead621257c6c5c88b663f8b2dcf926a4b2830ac6e42a437e2adb6a78a644b5b9ff11948d557ff948a7f48090b46d15415e6b07dccfa6392c62a2d80f0b44ccf3ce5107f62bb2980cf86f0d0c71de5598ae6904 true +check_ring_signature a497f2364048c4b94b3170ce69e336fb728160054c16d9ce6bc977398a69c6fd 1f95353e628915909dcbccd1cb7ad6f6f60df01c6acceed57a3595e110e4eac5 2 07d34409d5ada796e936f7bf641e9116337a6d9a5dc0003b50410a8adc5277bc 6595e851b34d4c779ab24c0b6ab5df9aa89c88680db4735ea8aa647094cf451b 0dcddc72c7113f69a1aa133ca99f9d57125730e0eb0af17c73f6b7b014514c048253de932a6a237f7a3f94cb3729b7c00bae241afa6c1dcc5960688ca12475763a7fcc1c7cecaa6e7708d4fde01946e8007bc9e48edd162d20640f0037edd70441a796dc48f3847bc663881a0d30ad167c5c21412bae93edd94cce31caaf6c0a false +check_ring_signature eeaebb1cd3dc34aa86e2163735dfea7e32d340db21f75e02009a1d69844a095a 1a40462b55754bfa00240616565ad2c3c847378644e13b237bc64fd04f81a51a 7 3529d5a681d0d0ac6373b1e9aa254e92e14519b509b02f5cf85cf3c0d7b05849 de60a7ad1f1fef9316022f0e39961433cf32927d8265801889163e15e6a6d9cc 5066377df77997da15de6995d0ad97b62dde2e5ac94482032c0b00753520d76f 813a433b7355d9a2e5190bff9108dae5b0577c90a5ed2ad32a01168719d11cb5 9009d7ddf81e02cb518fb7d86ffc8e35c05578fc58b359a4fe340a2b82ee00b4 21a47c5452d2fe4123c06c44f60f416095a503edf8832927c9e83af9a9ea2e09 cdc621ea7b5c2a27f8d026779030bd61d3c6ded1e32066498d8cd426620dc1ba 94148e18fe4cf51d74f8f4aa90a10b3faa76b3ec451247f1227bfd7a77dad004a6b688746a503e8e224623a682a1a434c5952809a67535860f585625f52af8d94ee9490ac1adbfda12bb607803253315c10ae5531d7345b5e6bc009d47ede90039d2dfbd7d871d17cdc6ae71513c83d85468a2afd73910c14e6445ecdba59301bf3e4ba9325ea13f2cb91b546e9c83e8561225f61e68a83757c0a7139d3fc300384375ad8943ab3c4df211a4235a60fcc37a79ac5442766a6ea32a019e66ea0b7ef181e29ba6b8cb8b467f8d876ad27d8a9ba2fdd4262002f8b9586dae70d005eed84c4f17d5dede83fa8b18dd0b3c1c2aa56699efeb558df6fd9259fc7b2409b8eef70adc798608c9562c0b9d732f869f4b390bc9247b093e4433b9c1dad20b47b245fd5ff2b214651f3e0351c970097092c3dbdb3e6656b74e79924c7f8a0c23903adc933367b5efd4154262c18b7651fa0593dfb55db4b3a59b3bc51baf05c32dfb578dcc4ba7d7d50707f3cb049dbecccbbd6f9d67cd9b152fbe317d2a0231c0919056c976f181f7b5d209953b4b941b712d6bc37c3eed90e119aa09dc09c43b94c0eea13c464eff0878512dbcc9533c17bea2e76f973083bc415d869807 false +check_ring_signature 1a3d2aba51f2d6411d9c32a0bfd58900a0cf5313e390ed910547b94de9a674f9 c7f338f69750fe54a975583d1627da94a7c3f9163755f19b995b309bc774bcb1 3 c721b146ebff7f3d033b00b260741d5eeabf81dd8f15ab8badf2f688edddcb89 07f1ec14e8104c05933b34ea9cbf57c006160bf99d9cb2cf1e5852bac57bde90 720e7b9ee98c171f6d77e165b862df633a961996174e9ca77119cf03a42973f4 615b2dde7e909ce88fa89059c4ec9beed44d7d8b35647cb91561874e77479abcf1c76af98f4ff2d20d05b077613b0cb63df199e36cdd42a8351648cd60c6df0be103c8e0d1c5bc7c463565579f588e0aafba2b969c6145b3c08a61c9a8897d0ac80d611a279b09a0b879ddc9ef072245443125409012af88f25db5a48aeb730e9fa68eb2080f983cc20f5feeb78ffef01ddcb799da5f047d788e2791dd46807d6b8a7494f93e5314fcb27941474b565b0fea2dbe4c89301938120581a2a1804d false +check_ring_signature 1c909782c70567e9968ded1c05a4226a3e04a07ae9db48e0153a56b2a4684779 199c5e78a71b320b704e01850a67c371fed3aca11a04077a36e10c808fdc5f57 17 5b88cc7df0447be04ce1dcf6dd8a7a7ca85b10f8568fcbbe707946c92fd964d4 0ef6633efff70769ad31c7e79527677e915a5e89c64c7df1d5fdb03b53941b87 01f0336eccf6a061500a88d07a3d5c153299f5bc9b3888dd81dfd52a0a9497c2 034c82d3a3285260507c2b6ce7f1c844d4b5640e11a4fccbfb3249b09fad63b4 b1100f59dd616cd6e8791e1e0efc8074d748151158defbfd02a6e2fc09aae7c4 72acb61ba447888273b8e3e0b0bbed53d89eab1885759f47080a550b282a518e 46d503d94a2faf805ad32546834dfd8e5f0501075d32eeaa3b1359433c6a53b3 c75d34f6832923b3bf22de92f16ab4c3ced2731899a9e46a8747fe77732ae29e 159b650ac9824a62f9bf89bea1db67fae28a2bde63e5489c756d2b8f06601125 5c458a7a79a5fefa84c2f8225576ed124f0c781e493a99bb94e43aa1db30581d 64963159ecb39690ab261495a3faee331f5c48ca04f916cf60eb43624963e381 e6062080fcb588f1327fe6856d3d73d2542779e2ba8c40b52ab1f9d0505f0caf 7b3837fc2e2c0bb4ca1a4032d21f7c1959552899188787c65a5643829e698744 32c3960095f87c4e47b330b0528ca43c1d9dc2aae59e81ab04f4b496fbd419af 8078bf92b788ece5b07a1596f8a03ea3d60c082285a95e0ff8338b15c5906fbb dd490e6d97bbaff5c7b04db17a1353b7506b1685c6fc3b41f9e112c2a0cf4925 c618c6b0e328886be15e2c92052dcf74cf4745a4e5e53f6d2417a9c9d139c167 f5b1a5079f6122d2370da403efc2e5504b07d67ade7ca57ad3720c7e00a676096ad418262cd9f8c9afee8e58be894cb2bf6387a5018f67d629bf8845bdc9b307aee86f85f8612f74ed870abc81d54cb38f2f9108c20dbf81eb7c1160935bc801cdca5d3f09f1d4bac810cb03ec32753f65a2071a97ba787c2d550ea5ff4d890ac398a1576d108ee67138e7596af54199247c6413e06124af8db76c0ad67c160f351e53029cac4a073c8b188eb8b5151ebf1f34a6693ddbf467ac8c20eec51204c3ca29fd5814f0848b966d2bca9543fc953a0d04bb4782950123d87bd378f60d3cdc5e44160ed4265b7cd84a8200244fa24bac86c50c35b885a15f1bc1de9909fabab87d504fc0cb0b263564e606505477ded1f89543bd4d6ed87ce61918f705359c525c0e0a4cc1d77043c71dfdf9c6834e2623197af1629e34962d940eb4080d06dddaa6e12953232595906289e4712f6aa8c1189f336b823a6a38ac95390b63c508a3409256f0c4df687d4b49990d9878fc5c97af32aa99bc9ffa1215860f69fc141c763fe1f4ed3ccc72e6817e926164752686d55238e74032c47c8c580f7042a62bd74858768815a05b58887f9828bf0f184eceba1416ac294abe453105a0de353316374b1c0560acc0f98181e3229084721a0d2a6ed891d57b805984044372cbb566c91696f5d63e7e5d8cc906c5fb029e71e87a339fcbaee9502f3d01dfd203dbd04797c33370dd0626baabfb9577ca1c294d1d8c6b82ac9624496f04893ad6b2222799cfd1d4ec525b011ecf72b01f6f3b8a6a165f0169fa7cc0960025787d4253e1064e71190d36965605adb850e72e17685e35772cdd3fdfb3d505e96e5eb0ecf8c11bcef62c79e0d49e3ebc76e56d18753403a880b7d7ed399c0e143df235ac8b0748d99e1b0cdcc511abc079c1bdbfead04265d9ad44e5dd7c0e97e4afbacba478539071db02315cc9326c8edb3d8530c7fc78af9ca12b241c04f87bffab95d85f8486ff01aec75e7e38a75ff023e4b2b0f59999c4ac067b500863a63de803da2c89de74878879a39aec6cf800b117ef89ea068a71db5ef70200163d1b4beaeea8c642430e15a64baefd350cb16ca5dae6563c13162904908301485d2c4e5bac6d55dd947a169a38df2848d5e227482760f6d818c45f66a6380507d691ef87e3550626254da3ec65411e8e5aa2ab4846222e208c9ba18cd5ee09617398af31b9eac38b59180bfaec538e5542d698da20016720ced6edb7ba270059c1e1e7107d71524e4a9cf562b5da263b52368f477c5205c768ac36975a250310c37d1f40f5a6eac857ed6082c12f5cf20a0403afcde3f1ea2e4743cb1ba10fd5d92038b1fc65d09467aeac9fe7cd24f78b9376d47d2ef1d3677ede62b2640544dd1b301292b8d082a97bd910ca24fbb5eea878f2cfa4e983bb25a8a66a25065988477eec0c1815a3fa2ff421621707954e9eacdf823eea02c2e3742797c6097e570479b36ca4580c5be9bab187de6a1fa6609768b3fa6448b5bc56d6de820d true +check_ring_signature 2992bdf70c71f748d520d09265b5fcc9605d782f8012e1ba9779cdf4e670378a 0dfbe3d05d9aed7b9eca859eb1d5eb167b8b239a57da82f6e3da05c8edb15513 2 ae981fd2aa7c03abfbe36c3d8ffa8120bdcbd3a79c2ee70292ebc4ea082ffcb3 0bf785c82d06b2b5e534befde72fb7edd06b51282c6f1746abe0b9cc137c8fcf 7aa70ff6f897eb222f362ace2e15454e58849d4177d17cd90afd14c46cee39043902816b4e60872312f99b84f5ff76daafae09c6a41703cc50294abcf22dfa015f32ee1b1c15f9e548f3f331c690096f694c5a5b71c35370a7f2cdc28259e65b88214566f29d7a4b637f09bb3b689f8b0a4d20a28b93f071b71510596837a571 false +check_ring_signature ff68d004a81de5d389c86f549f2de8e8e1e3a341609046750a646d767590c703 a092b32743b6b9c8bf834c4d53cceef96e8c6204aeccc30fc2c3a0a36d71dad4 78 2893392d09f6dde6bbbb4e2cc8560852d9e75540182c1ff243d25d77a5481d4d 276e3f00fea9cb5e57d36252c301b628b8cc664e9a879f402199cccb1eb092ac 9c9958211f6491936d813fbdece7eac12ab9b2acbed7eacebf0a2d531ad1b438 6208359260b951065df2457bf58d0e39dbed507ddebc33e0007a804c478cfde2 1ed385d0c350a485390d79f04bb1cb4fbe21b1eb27728ab79eb923af5cf9c488 ecdd94bbb69bc30cfe3e187c5e467fa9a761ff30efba3719191e3860aa923c11 4d435c1184de1cb1dcf3a24539f2f6fc0f318631b5625c97c38c0fe714c140ea 02639f8e709bad35aebc939efb30df4aa24f70e6ddb6d60a4d23e4b4e0fc6355 e5bc2008e27b1e2d5f2a87e17dbe67c5ce551dbafbea96678075d9c08198e893 8e545802a5c93101f8a27926fa99362e994d44bff64440ee693dc3ebfbe87310 aa47dbf6c5ef196d19ecf11f348d49fbfa22cc41d979a4092adc453675669da6 52ac25eca795f4fff944e6caa0e2bf68c9af1272052b38166a852debbd31485e 0640001e5f6f220d63fe49e189630f415556cd40ea58970283be4b6c33533692 b45ce0b1935e353f35261944834a30c4d9556e7d2badf77215c2767fc2ac4100 9ad33d6a911ee717a64dd9e7047f3b0f382d63ccd8fea2f1b5623f0e4339fa83 febd8cad01f9572c6fc3bde213c3abd19fb806038705d088613211c0bf89a274 bda5311f80cb623a6358b40df81de5318d042785f1698fcd71c8600b2619228d 607effdfb26d11e1e95bd94fa702244a6c2ad90b23d451f0e5d33bbb21de5ed5 9c0afde5c2eb31bcad304c29ee80bea23046fd8d694bfbd2925ce5419eeafd1b ee7dc3f4570b208d08e0680aad0b475557f484a7987bfb8b2dc0978f6b04f97d 158a250c2cf950a74132506ebe1786a6fad37516c048a4699b6bfc07380dc072 a153fb882c85d995ebb8821479cabea37c465839866365b9fb649af219257814 7fc8252fb38feb4a5a68d741f7888e5d653cd40c082ab7fca09c47308594eb75 70fe89c09e39636449bf9b0276e631c3357ef4992dfa4f943709c964435f68fb 3a114bab32d2b06144293cb4225da3f63221b4d04b6c2f8bc6acc368fe3c58f4 4355bb08de94c0a13cafcd6123d91adf68f847a3d5927816b83159a49a8ae2dc d7d3ef0bdd88dec448614edd9328d3e4d63a6d650f337cb5bda9a6f44c4e8857 60daa30c6bdf31410c8d88f7d4503a0561a3a06f9b6c4681ff3877892f0648b0 c04db633dbbcea8e56bf6da3d1a4d4ae392656fb880b021d8344e5de0cb0e877 873be75aa0f781bff040566805fd40f1cd33578a40ab903f3a27bc37fc9600ca d766b7c0c53c5fe134ab835e336d1a382fb8cfdc2edbc0cc06080620cbb17d7b c32ede89e4d0e2417addf3a953adc748bda861e9dc08bc9f577ef5828ca8afd7 2c1ce574cddee682c15044f0ba2b7f953242911924302980a16d703812523be2 edee74b457a15194034fa90a3f2b5259db64aef9bd20b5dd1580406210adb0a7 6c0d32ca0cfa0960d4e0a55a91b1d244f8539d7bb4b794ae8054261cda77345d f8c4a5c5d459a55283410f03b99da0302098c099666d54cd7edcfbc71d79bfeb c7e6404b4c1b678485cad626c8fbeca49b0747e395ec33aeac01165fb318642d d1b4fb82d29dc91e44c94229a26e8b72444dd73de2f9ab47d77bd2ff13cf7985 b5d6cf8c74a33584b8e40b989cab9906c88665e3551910191e21fde471820b00 b7a56eb12fc6e24e1f9cf2ab238ff7f19f4a492558381fcd80ab380420990207 24ab5c1ce87179a229c606cd005bc942018d6b37c99ef3215a8c4fde4e771000 33f816ba2016faff8fa5b89a2240a0ec1a239518a6cf0d66f527fdffd3edb43e 91e9fa7a75083928a154524840020c2d8b7388bb85e6c09a4baa9a1acb73f3b9 b46e804015303e246bf2edb02075c2d305cf4e98c941a70e1e915a9e41aa9921 1c66531a36f1e516f86f4a68b4625d839167be3201ce55e9bb600f3f5bd96305 91b425f64f7a425ebbd954af35cc09fc3138e17014e9f4e36c2a4ffe8e06b8c3 d39dd047b60efa0269479b305dc3051818d41a52304c825d6b4f2f38f6f26e2e 60ff0923bd1d16ae29471a655c45d89ba2298ee438227c7a71d6990b4776f6bf 945a7b27daf968a1469b0250b7e67d3f7bb6a1f702ceb3100a05f0a06bb45b1c a3444c8d897cccfa70ce25de81ef4a7c6a7d4336cfcca9a1db422d8c6c8bd448 7a2c2e9856285592ad51833d91c8cb6537910edd00cbcb76f54bd6915d57d8b3 56ab7ed929e6efa2eed00bac0c1dd163962f1a8d5dfb5574d8c758cb66a93b9e f2416f809c093b94382f240c3852910a2817928d4cb9e4f28bed086bf50472c6 3e581117326dafbef97bc675d7aa9e27c5868278191c67a60b447f020d09e570 439a60a82bed7cd509aeec5ae48d354c1c5bc8702915e68dfdf6725a9b2dc527 0ff2d643892656587ad2cd80c46658977fab2a834b4d43891592ed7b3c3c1b22 cff3ef125d4ed2cec791989347d7bf53cfb403f4a4e2225dcb9e541322723280 da99cf21f33b62d2b6ec614c0d0dcb65d5128c4adcfc966d301af5b571843fe4 2df9ab93f9aee2c448e33cd0c482c3a42ddd2c3da946b4951bc16f4898075e66 9d091c01c048f08a14e4140cfb1395b2cea57cfdbeeaac1b5d570c8c8f358ab3 ec04e6e9505766190007bd344d3692bb8e7eb66335390204f92725ae057dccb2 771c28ed454ef70f1de009c4a231c299e9e3dc72f40d26539ba19007eedba4fa 96c9068f323b53226d48eebe2a10a0d981b327db2767144edc63dc178c9d54b4 c1a0320207f63c329aaa8ef11be8ad2fa25f873f01bee29942db680b0b009b54 f6eed9a1a512b1776aad5e8491c14d38a84409d15eb7c4b801ac0ac9b79b7667 395c7335ec42f672dc2ad80cf395b945aba04998313703352c816ca825c37881 15adc5c9a3240c5f1c51cb9674b3861a47004af2e2842827fcaefa6e2d33fb8c e4b15a3424bce81407e14bb70b014b9f5cbe3ca792a54e395a7261ec5b9f999f 8386eee05f872c321c6794a0fb17c0c800e86b9248d81daf50f3397111c6ae85 6283bb6be33c94c25c3ab55261bfabbc635c36766526c468293a4f5e258dbf3c 92cc4500c9e363cf83bbd8ff213769725ddf025bb0109a5d1d30904fc9faefbc cb994c7bf85448774475c8d8f1b880b05fffb34ba1075131af635f08abfab452 4716db09b20933aa40641e080aff5016cec75a410c7584e730e17bdd65d2d793 af6291c8a18e1fb37493e453587da8dfbd91a7c3c483a58de1c7cd9d4dcb06bb b49902315ace9d1405f533af239b9fd1c8c5227fdc54bb335a19d8d57911cea2 8a047f0b42a33001a8fb6c25d4e41a4c883bf534e6d30f6d9ed7800401b305ee dfb6e0af5704d8259aadfd36228a79f08c535ef7a2bae8364aeffe54d9fa0a78 62dbb47ead484ac1b0fc488cec6d6f9fa5019f32dbb52ae3f6fafbc24aa0e7a2 5ba98ae792845229d9d98823372f20cc4b0d8862457d5fbd2ac10fa088531f0353526632bfe47b95868753c12df98b89bc5f05b37908ab5241ce1ca68b9767031017688c89902ebf15e56fac62f6357c94daf42cb0c713deb3cbd4e07c82f30b98b21a279dd51334dec7c20ca8746e7edaa3918c808974779c565ec938f85a0e19d572b5f80ef5999bb5022686755899d82a80af175d97178258e25a16011404ca43c56e3a3a9b0742e29848e0249d8908f74c601c519a50d1648251c1399707b6d6c9ff62455dc6bafdf066bca408daf787c1480f4f35dfd0533a9b3854180adf8158b7d5a7e9835669cb2d9122831c87f355df709f814d136871b74d7057069edc437a646ae52bdca1c414dc9d6b79630b1a2c999c2f784f92c2b91ebeb108402f7906e68a4a761b625feea61fb4e4809c6a41772f419690c97e5bc6ebed0e5286ad10fc3c072575552298ea756e97f16e9ff6ccdda242d7676dd7c4afe903e6f3570ad8704391fc4aee1c8cf3adf291ad5a45e8189d40b572c073e6a52a0974f8eee575cd2a1e937a5669ca6c1f9445d5e5ed6ab632c879be2651c6cbdf022fe11d3be6c1d1699568239ddddc30443c44f7132306bd4bce442b68aeaeec08b6ba491a452428e549924872af772d119a949a0f60117423d56efaf709d5a80b601ff679f8f2ede8ac37548d8c25c4c93df654fb04adc43050d5fbe8d8742c0d7bbe1b02675fe251c92bdbfa075a72b9768d352142a173e7421bc58674676508cca1ef0d518c790341bebbca3ef134f891da32cd9b3dcaa5ceb02b48fc39ba0cd51c28a85931b534d9192db39141449f6709c6499cdb920fd4c71ee4c532630be47ca0de22a50969703bf7d85b0edda374baaeef5141bd6f41d595e9574f7308dd11ea45cd9bd196bbe7263bff56130d002322d1202fbf75f3f95705bc5754021fee55c8e9239a09f74122bb2e77030c605b0c6b4cb6de3efa9105d7cf6ed00b19dc4592ef9feb9ef29bc67dc52181c4c9ca1c1c516befd88e46ee5eca9a960c03ed785d95c3871653e7ebbd8b69b33b57543934a58ad09fed07239507f4c90b092f3fe6054ed3d84568c35fd75b150019db8d620b735afb9127c274eb468a0efd5dead17f9a40b6747f90e1c999a78042b14f4ad04ce0d3c6bfcc0cd896a0045086852e5f2c05ac5f0d8b1e574c7f64f00168efe2ba0a6a38ce4b5bacf66a0d2f26d79be7179647a57788cfafcc4af5cbd9a932f812c4cdb795383c0c55fa0abac87cba14838cd41f0a4ff03ab942ff285fa820271a9fb44d2d644451bc050cac9adab6fbd11f6874e523af6170766b3b1b2e6775f29ba65079c6d18c8b4e048e2114bab58fcd4d1a0c3810b84a2136b81ec5ac6889898948095aeab8a36c0150b5da06cf9d84d8c804e959c9c1fa34415d7237bdd07a17d6a7b2f6de570e045b5c1d440f800c088c06cdce5531277d4647a77aa1a3f5ac80392f55b2354f08253238118609150f44bf2c7d5d71f5fdadb208270350bbb61b9e7d22d5021e016f987df306aa30756799f522168c7a0fb050bca42288fce7731821c16fb45604918a91b26df83e84e031a888b45d143866cd7c545ada94ddf3ba1eb1f3c4d30a9caf54992b0651897939cfb38e5902aa53d9f8e234c2634883c88115a919ab01624a6e98e16e77af6e5338a60537fe4a102c2effd80991b9423c69d002bb7a08c9952d4d194af8e5c153e63ccbde1471d6f7135a3a8c4f099e13cd22763821028ff0fd05ce2da2712f9c02442f0b0808391ba0b5f435b1fbe1fb58817266ca00c056b9e2f7fe9fb362281e31141b088087ff3ca878a3bfb6e8ae5d4bc0f4b80ebe7bbab559867615e0753059c1a66499d0751d4c879ac0f83cae5373ed30740769644cb67b9efa89be905c271bf42c5752588b495254cf3082f80e1dd5420a0ffe59ee182b5603b8b0e75b12ce7c17987a3f467ee2ecbbb591eeccb5ad725f062903b58e370183b9ea3e84d54be1d3cbc675eb85fa8c667a479b51e4258517088dce2bdb14d1900bda415350128ce727e201743c5708984019f8d971db3aed083780c348641495767aa0e1f99e0e8ce49196348fecab643b561433c687462c0074e79915d96a511a90cba862547f084e04dcf48b77dbd5324ab0f77024f31c02c1b0d554b14b3dd82d9f5ca68bd75303d2559a70e4cec21619533a8b47aba1008f1ecfbc20ba261f40e370a7fb5728c7939eac2d90271decaaa9276c6d453e0c7e08aae0a3c20b1a8f9c343ec72629d6e4acb80199458b7f47cf280adaa67f00ca99a50d27ff5c42de623f8f0215a27fcf63b2d227882213263934706891700af8e3b05fce0cf739b961e3ead42bef233abbb03a110516ba407b03a2bd728c01999fd54a156de64368198f5841f3bbcec9fe244ead2e10377590486ca62140068000240b2171a6493252fe775b4e771079eef5bde6f6567de47c1fe3b8f83f0b8e17e1ca6b2f9ae9653eed1461d918a447708fd877a5bcbecbf23b6aa3ee7c074bf74bc10ec600838deb68744f16029e8505ad172001a6895039d50b32e6fc0fb2321fdd369a12d236815b617f7906903001a3dae919b524c0a7f97582ff8704a5608addd2fa3631eddc0dc6075e701dd1854adfa2b7bab4d5b8754c2df54c0944825ccdcaf96a829c875deda61c94bab62973c6bb24fff6ddcbefd0a036000d49ec49fd5118abd62019a74077f95312fa4edcc4ca12d422a68347153eebc90c8f97c9ebeb3cc9a6b9619326fde5f8d15be4c04b9e8109b375d185ed16991c06e5c995b8ce1d06fa45f65c1a3534fb78f597bde435cb131801aa16d8cf556c0bc40dae897c4719d36fb2d698cae761cb156b43ebb53c47ff83969254e25b9f012c8365ceeabafc241f6e8124483383edfdccef8c54d33d3b1a2216720c914a0f2998e3c86e333c17a1df48e70890f35d9e8bebaf7b6ff0f29a57a6252e89370144737566061bd863c9184fd547144bfdc2633a40df36980e8b130fb1fda425001ed34ce58d6990d48a0b907b7b2c06a92cc1862f447e00dba13b34343f34460dc93e5128c919ae6196887b981a788a606c1b534b35b7b770f8ec488b9416600492648f75eb9cb10bfdc7b3dc94f1dbb46b0009d6cb024bfb9f2896d46b87920ca449312ab542dbd955097e47939573a62b1c7d006d0feeed75ec67d21c48790ee191685f7beb01eb6cb1f9007330796c1cf2db825d583fb3ca417bf35265290442bc2a68230548ca2324e47e347052577dc43552169a7ff036dd8f4d46c43d04d018c1309bad386d26c9cc2e07b27d7a0cc05afc92cb390a81b0cf2d17ca2e02fa70fe373e09d8aec0aded0afed02eff66b791adfbae4925a5ce88baeebdb803a2706fdfe2d3a75bb0fcdf59393a4f95c10fdfae8203b1cdead49790c37b900dc7ac2a868d051ade697fa18c1891c9d4807b4cc5fcbaa35f5aa27284798f2a0e4d11e2cf4ea779389d25d98869464b806c645aea397bec797949fb10ab4e730b2f86f9ba9d34686fc8b371c770cf46cc1b1eb2ef586d250e6ba698a7d1de950658cf78b329854ac80f4f103c2ebbb9e625cb0aa2a6327535efb79158bf376008ab7accee446ac47b4c2ba5ccce63701607de053338673702f5bf3163ab784e0ba06cff103d57d1c48357067bde2b0c167e7592697873397bca0a811f7c04900189a517943834f07f531bcc10a18c987b9ac1f926617e273b40efcd55d0c4d90cca6bd31751c80b03a850ba17150ea03689714a9903a74ca32e06481ed243ce06eeafd90142d75721fc5cda51246b4baea108f609f2cc682a4203093976edfe0a70442855363efb4cb3bbf9545be6f449c078d44490cbb5839eb903fc25b3bc0877279b6112510bede4a9d748a4581b7420045373d80d72482364e30679e6b306f415ea723ff5d97525ca12fad6e36408fcec67afae6924d3096e564cca234c03cee371fe272694ee8addd15a84def77df69ffd1435c351357c9dc9c084ad3e0dfd0d0b8d7fa71c959d8fd7196bea2b850c2351cd84779c4c8007ef8962f7fe08c578fea8a5fff9ffaf09adb3f90fc45969540cc1a1e635a3069d81603930ce04ba8feda6b9cf91d6f5d524e2add411c55dd56614287af206559766c0c8b3600bfb826e12b86468df5dbd88585723c7e853d4a842b62ff32523c1115f05a134076b3b4ce00aed541797093d4b29e0cef94d70d3d343f369fc80ca25bf5a34b4021c088b5b7dc861fe2b00460c32d91f282361e8c39a5ce82ee4ecdd499b28800d4582d03e68faa59c12bd1ad9e86d9f28179172940911618fe2f32f5ed8e28e08ef9de00e88c0ce408e1b74de207d3e0c16970118563b74a4ee1946c46e4e7c03208e93d51a0a0fecf24df12bc54171851da604519a302c37c0f14fff00de7d05c4bbfa979ad971f272d349e046b0c5cfcc713c08702a9dce058425f15d65360af4451b1d5f5dff596535f4a0195b70aaca73008ecd75bfcc8ddaccd4fe9e200b7f2a54dac225d9b6f0ab5988579400b36231bc3c4624f7c29613467b89d92c094e7dfaeee1e4b46b2048fe6f1f6cc2adf9dc851133f90402c711e5c5d5bdc4057704605eb41a8e67d353f1074f67df27b451a08fbaea15fb13b873462bb1fb064b1457fb0b2698948f32dd5ac54118f2d043b4c196bde9f39029fded911d87044112a519465caf54106864a4a3d49454d919744864c119524dd87d621606540972e988361a0c90ae1cab2784a3fbf11fced7c83eb917bae6cd8ee064e446e1064b1e4c7668a466f28334b36c001f52bf382feb94baf7902caff0b0727c3a4a07e02abf7338afd0c5f066eed03abb0ce2ef11c8f39e96b0fb6f1b51927f82ca0c63b8ba8f418b4dec14498de9b5f47f3d687b02c10a333180a7b7979eb3a93908f07aa3d478274cc976a3debf8c4401794ec06a33d0304685306beb97790d57054d0310cea32da129c3d3dabe8331b195d956a3622442e4a07ad971699c67e30f963540a82c45949c7d8e599a36f3096151bb855c51c41140e4ac5e4a5c95630e4802ce1fdfe97b0a82ad719972941cc24b01edc34b847ed85804be64a9a7c90d7fcdc425d3e69001ef8f0c85d9a549a42c5c53666b4642102cfe4d86e1cbfa04916e01440362d98947482efb30cc81c4fd9ebb6ca349792029f9fd9c63e53c00b8f7026f6727c40a18286b9aa700568b7ed57270481b0749fa1ad3c99eb550043808eeb5dcdc8d567a8910ffc81d13547b9c0bcd55d7eeefe15b51d66d6f720f5cc2acd772883d7d6a9996322b1645df2bbee4528fb8a2495d43e0a2a64ace70d0c74ea01799748a00edbe1919b43c51fbb9f2ef7263ef234975d7a8064e280833de4b36a88c78a9e01a655290ca1da53930f33ede8347cd0f6496b3e5b7280013f0897e52de5ee1299fd90f1abf564d80e015dd62fbf87aa12e91f01db0670f7f5fab3b17f2913e9a1dd24a6c920c0d297864d0571227497aa162721a477200725844764020d8b832718b4232f93af232503dffca439440b95359356e04b00cb909880356d6355df1321f1f02c023e9a462198a687d9f9955b7e874662c5d04e4a81e622f8221b8b36d8cf41587bf20d57ccf3131d801516fb286de2e03ec011fddba3bc6c614af50de3a754188e361316cb45d42423e4e9627449f3f71dc08f57e7c5251dd37f4b8676babc6b1b7237357d34db696d1e555c3297823a6f70100cfeb1404fb82fccc6c9fd034c4ff3b9ec806874bc3a401f643b30f81129f0f222d70a28aea8a96121f70a38c32f6694eb27bb58f62100bc0c0a61ee91e620402f3a57e77ea1b3c0bef6779d561d4eccf90d60cdff521855e738352ea436906e4356f40014db5a908cb1ba471fdcd38bcf29540b034cb5be9895c1a859d2905e36a41eb1ec5b9fead3fb4eff7f9dc067e99d2c76fd229c7460dbc4334c0ee0dab222081054b775c4f3e1ddd9c891edb7ff19f82dcdb9bb4fddce108bb7cce0676bab589f087e23d3bde5a0aad479fcbd939bb3cc990f474e85e0cdc111f2600815d415b6614d6aabd07268a58297bccf91cd609f08a28f89ea04a9aba5fa80d82750f5f07804ae9e1735fa363692ecf1fbf9ab2f5205f5d1706217166db65003f4b12bc995f18796710db36fbff7690160478320aefd7775cbfe5d329ed2202059caaa024d9d35b21ee6f8d8e1e34925d74e294f01dba39f975742410d79c05c22ff60c476042e134e7e89421661148ad19415cc2aa57aa4889410ff22910042fc93040dc7f8adb0e16c76e1bbb6b3c43c53691c5b912802d2f77098e8f2d05e344639d56612b188d1d87991696913ba19359a3b0724dafc437db463f45b10283d64a7aea4587ab9061b4006e76044614c42aa289f81461720a774b38dd490c59c54b907d6a77bc3bae0ef466ca12e443b38673f8292bb674c254589d46a20c9ea5cdabdbd755cb3fa27372b6954ac5fdb1f02644ce798286381926a6a010049d103ea98e9d10f6c5b7257273056cec8648030f1f17ccb878b983362c5e9603cc7e92f591432453bd5d373284da598c7001f6fbc09ca28d268fdf48a1f3ca005f03e2e845d399b5be35386a5eb6410717884b4794f820b20ce85e91737cce0b5f42315ec0d210c9624f13998bec82fa38c9055f6ea7f97f6dda7bdbe8eaa803c1ea27e98941d0ddbce2b4889d4bfce7e4cb9a2251e6e70f8ec4c75d3c356000b694d6666039492a63419d1cceba26bcf292ea0af3051de0f6798aeed83ca60b807ac7c57a354e035cdcb18ea46484be9c98e7d2b51857b184c9e56dde5935003a7f11a7d4c323f1c5c6b4f474b267e5e6f183502767aef1e58b4dba2c19890332797da32fe7046c445b9d5f14ca21f0358d9092082b2d8cdad1f3fb0648a40587c86b85cb04db98c5886480c5fefb86ae45091715de65f26c691632cd0f0a06c825f714d4c77db182ca024c2844e5a1972e4fa84b3c6c4d3c965bef62506e0da30d2a106fcc1f684ef81953ae1f9aa8f6c3f81cbd594ae1ab374632b6cef608 false +check_ring_signature 1811b898a97e5a3deb36b985cb6cae735e76494ce85efe5884ffcaad9ce0c7a3 a6e5551a82ebac4ce131fe3e534a44ea3df7713cc3e40b61fb99baebdab28721 8 3f05f6a9cb69f115895db2933cc3d7ac383b26862b2d0d3fb6828de7c9afb3f2 d58e2a7a96d494aa44134f0767a8b1b616b468ced37519fbb5ce7cc62aca622f 4213d60dcf34a63785e9750f3ac1b96864ab7c1f8a13d734fa6738ae1e45e93a 1b62aa0c6c12752c88fe1c94f887afb0deb19afc97d9b58b23e96d278ad4d954 99ed251824720dadfc70cadecf3b0ab99b3e7169a3f61d0220ab189880064a4e cafbc1a8c5c0aceb7f598e4bf5870b3eed51ec12ba1348b1db3cae7e330dfed2 46b2b24c0e555f539b6988ff97d51721d5aeda7e14f1716050bd4b340d773383 853fc0e8866d6f4373923c616ceed2f1d000ab38e4408ab8e1c3f74fd18057ac f278f2d334ce915c12fd8fe181e10c44f80ca7056f83c9c77de84594346b08cb1c995fc6f55ecae4e973368891b3ce4890fc11add2da9446954a593fcbb4f20404274e20ce152c9f5cd7001105a17d9ebd8c6b7ed8acd7991f127dd0697426073862230ea7ba2579f349553ab88b74e85109ac4b5d6f15aba91246460f512d08f047c6a955851ea17f4e3eee810ba9b900142bb81da952897d6db6690df43d0cc1a1f4eaf830d5ef52416b83204eeb03e7274a8b52ef23081df8e0ed85e6eb0870d3e2ee83fee8ab73454e6c59324e1ae9640acc3210a50caf4f925d9d58e70656995f5e07637267cb6e93ffe86a13aa2e9c1dc38dc6ae77981576d9556c150e8201e953c3948246836b1b68dc67d74e4238afc21e2548938e1f26453ceebd058ea1b37c3eb3614d2c5870005c320ddce362d2f5529235fbd7b5660f3a2d2802c804b0165385512ab04de475fa7637f5337a3e868a808053f7a79f1a30ad4c0b943aaa8c15027be76b21c7858457b7cf8cd7dd43b8825b36e20a6d59a7781107d570611cf54d3d5a8043fc9f2bf36d56c2ffc6affa9bb2d48bcc4616c1427901442c70f30bbf71a02f0b5a9a81e9fe8a1e885e70e1e7354d3c40148e78c8e00f62f93239cec56ef88f39407e696ef871134feb5b3b42da499e5069e2930c200322a6f0d2d06d38cbc7724473d2e496e1c0aa25c8adef3247a94942888137c200 false +check_ring_signature 487343535d0df9b4819d5dda28ac48d38ab9aa2d572aa88709abe60310f15102 6e50ca646cef94dd5720622b31fe897dfdd037b06038be64f41f68ab7707336d 1 0faeeb600a07d6a2055619998d43459516bfbffafd7469e0ea6c36f651d41875 173855d107095d5936ed6ea0383f3c32103cf1349d248766e098ce8a62b6ef00d5128caffdf31143174602c8601a5f72d5b84b2ddc7bc0295c1eb329d455bb04 false +check_ring_signature 0303126db541d84fc59c47693272d5ee66d11674e6924accaa9f0207b6eba630 adb45410835aaf47794442ef2683e4d338412633ecc7049bdf1b0e162a6ca1f9 1 6344d355e8f86a079df6d99da61da68ea0aaf4203f44ce069abf572241e000b7 fc220dbff712befe6bb6824453ccecad66a1823cbb9b7da1e44c94c7ce65e80ef63aded4aefb72590c24ee72107d3fad598689bfbf10f36178b495d5c3a8060b true +check_ring_signature 549af0f4dee7f897ad6e6d53c624d3f87286bc0e684697ac11dbaad0b32cd7e4 26d7f89bd0c07b26b36ac91e652eaaf9c1f6cd64e4b7c74ccea2bfd7623bd1e4 1 21d5adcf9668e4fdb3cd5b80ad066d19b5539e4b7d2d822e969845e0af311875 e591b1ff31a53abbe5c6af16859d852e91a55c1b7df1930aba64b1059b78d00b1068f404ac206ba4cf75a1377f203eff436389024214def3eb5942429d605b05 false +check_ring_signature 9e7ff8bde0e318543dcedbe34c51c6b25a850578adae2e7930bbda5224c77ef5 04e593e5e4028ce1c1194eb473efc21359b114737e5a64f14420b3cf5b22204b 3 6bfc9654082a7da3055121aa69ddb46852577be71d6c9a204aae3492f0db7e41 194f27c9fe4d81cc8421bf8256374edf660806d78b4ed7914a3b74359c8ac0bd 65ff1bca674607f7948ea0ae8e83b6d9c5092942b52d2847b6cf44c9c609264d 30041e9694c3184980c3bb87f817eab3f973cd969810ec9df4d2feeee907970693eba4bc5436dc7cf49ce476e091bf74d20003f0f73f6d0412909ed8c1a10701c9c4ec11623dd3c50980ead83865a03dfa27614e5e9fb875d75667c11ced390d438f5dd04a137c73a0ec9ca36dfab62c948ce596722067de0315b570db1f720bab7fb7ea1b124f55c9633548f06d1bb403d7e2e15a1fed70ab2865e324ef340327f6d0ad0a7129b272ce12a5a63836a4e96e95897ee44cc22a7048023f438006 true +check_ring_signature 7eadc590e5e4596a9e4a50a674670158e541f54637b9a63247168146f9ddd839 690074d708785fbe3dabda9c4a3032da86e6f010c9e0eadc052b60e23e67e98e 14 97a6a325d9fce89490b6dc1a3e2161b76eccf49c35d4d4fd3b4a50acd80aa3e8 977b0ae555048e72f72d3be6c9fed722127c23ffac870995687306f48c1ab968 6c6551a33f3471d4c16fb7b5c52ee94b0afc6a923fc7bdd50e4ea18c30e8341a 23a84f7392c18fd7b582e5c762af14aca108da31524f56b36f8b14e43c39bad4 79c75892eecd3916b528710d797514077c4c0aad5eb7da6d3154b8e95544bbfc 582980d0825dbaa62caed4b7417fa6a848d4505e78c6d0da783efd7f957df292 f137aa5f4575ab893be0661d043433525266eb67f85daf3e05fcd65ae6dc199b a7b7b32b91abd88f84c2b896f314a592f343802c25ec5f3e0eb72cc8212b9bf9 4787d2d863646139ff045f0a9666369fbfb9961dd4ccda79418851b4bee1762e c8a785b4ddd4e06a0cb8b15ffd8557e913fc16c4cdc07365b2ffe897edc0a8d7 0e60041f78722e4ba0ccdc1208c1f4d5503e7c9dc15f0c34264757e49f93ea56 06df6d16d2ccb2fa64a856ee52ff92f2d01c0132f3f2f3b922db41f63e3b763c a83f5cb0971de378286eeea0b19f103180b6d649760c89fd538bd195fd22b32a c3033e43933e766a284e13d1f759a5f63f945fdd9b85c86953125cfebf415592 15c77b80e50dd6285049ac436496e08eb8c483f6526d1c68ae77e89f1928500e1f8eb4e4ea0f2e8ef14cf25e12fc1d409aa193ac4cb9dae67a1003091887f3093d3b7a6879a84faa63fc52786ea20ffe465f40c18be4840b0e74e14b09dc4d0f55af45fade464a6fa2cb532dee88e218f45a9e0c4d9b115cfe04e429660327018a47afb98ba0b678e478f8bc2e539c3e1b22de002253f1d4b1d9931110745c039c76ceedc5fe9ae33e878edf3379f6207be4d5615024f213a845ee521b650609b09cc0164f43e54c6fd258889bf91f8772d344c8df091a89dc9bb9f844e6a90e061858becb8f66cd17cb005040f59a74dacd2170be506d970b0bb69ae20f3d010500d52cbd56848dcd51a473c0d9383b6d5b8e229736e001da513472d246f1028c601f56f9cc811b5d6e4fc0abf6f6ae2868b517d54a5a20978033fc342b3e00278c8f5d7102bdbc3abcf344c243ad2adad459de98faba2fc379d4bfac0fa2069641c63ccd7294c472c00e86835ee612fa56d5adec11991941ad64a9204379d673f492debdbe0384ab05f3aa349d25f417840137e12fea72a1533a5a06a9ff02aa03946312138590ee919a5d4446f378a457104cd2607b088323513817fd5300968db12e1646440d9f54c2d12d67269a5444592dac63e795afba5fba7874870d40bd99628c90528e7c93e33919780d3dec61c13682c522952018b3d98d9ca608bc5dd93c4d8481ec20327b666f1c5a392019173458eb2406acadf0cc1747580e910d06117361d3e93e0c9cbf0a5cc221c0de0a23e247c1e4f027a8db0bc41e07a27e164ff7e574d94f0f70526b9f949f4c3690281d26b6636ad14ef675a6ed06f93bb3f80f9b73eaaf82ba268ac80176efc1d616d6b8a1f9bcc26c5057b14402bf71deea66f1b97722c57ab037e474daaefd90fd1a546fccca199e1aedb803001ce6bef0600ae93dc17130ed920a9759e4f5c2dafb5e4ee1f82a122d1c044c0aa82e1589c538c35db64ac5c074a2be40f2592d74843b46a9b20236560c4a3e0600c967c20d1195bbfed51cc36f1d5be98f267f06220ea12b9dba0ebca6fb660faebc1645c0963f9d582d41cc54fc92027311bfb3ffdf4f8abb7d2088373d8606b606e09e25f9a2ac42e1becb2cc9309e457bcb1f3ad136c3a24625f42dd2850f3f54220b6cce33dabb1631dd6d85378f0d9c0f51eff3ef3f049e819e5c31b70ce347161aa21e89c339ff15a4c7c1631a1883ff9de5159d7cad9e46d48f42a507 false +check_ring_signature 4474db072541144868bb0cc46fc578549a0c83591aa26bb990910bda2494b890 57c3b9302cdb92be01252663d9db79e8f59430ca7d8f92800bb1360916329f15 7 29a4b20e851b81d5ae13077ee36f1975a27c3540ef92517544e2e52416b54843 8890f1593001472aee99f190db011e5a0616e1eb470f8717fd511ce6510d0eab aa64d3f3daf2c2d0270054d401ea5632a837644fd6b8b88fec1eca5f8f5d26f1 5725d2b502c1b1716b87e6ea818cacdbb58de88b61ab0e877ff9b9acf1d43c7a 614173d62d1550489de2fa954fde5eccce61597ac57f479c36e4092fe18e4d80 8c577f74901e55677b1015edce37d8ec29a7a27b312f730785d79c578f02895e c71339a1d6a8e12112b251d7dc1e3342a5242baace453f07408506bbd94bea50 ab5ca2620b4014b9369c1f603663d9b6819f4502d99770532e82dcaac8bd2d05e098569aa84e8e3bc5d94959d5d4fbf73b0594d35c03298021bd2c65d660fe0e5e1d35004694bfded98ed9cdd5a17691f5540010ee45f6d33ea469b951f1f50de0904a65b724258b2beebbb364158b9aff4bf1124fb476c8433b4560ecf86e065a891a2ace1e926a3f94d517a2b1eacbb602d2930367585373b1ce0d64989b0abeb4d0950c2c47d83d74aa60f4599cd0e211845978ad1226f4bcf816fd475a0e1fc0bff10918bfc05fc8640078088da828c264e5eca36da37f8e7db1afe0da01ca17463596a9826940ba64dec538d40b038472c88928eb69bd79913d2e85b2069c98c20b576dd16fd8ffc044a2d180bfb7d7bd88d6dd10b958e3de345602d20643bd2e832a1d131be9dc9f3fe700310243bc68e14a63aef57ef3f13d39120d04447f010087a1a68f2fe1c94f5d2011d841411a81d9d251132fddbe8a35e3790e7ac0a8eb5c0d44100c376a40ce9c9d875c5d08d442f661cfce84e1a13c96c20ec1b2b06545973abd499d04fa29397a2eb6b840efacbaa4039b19f1c4fbbac10cc9ea46584576dde86c9193febf68940fc700fec4a8e41b95b7a453648985ed09 true +check_ring_signature b53b9c4dba53c2b3a57798bef04b5d72cb1a9c34d52b167635312de3c3a6bc3a 3220ca4456eedd04679becf0382246b9e6fe8d41519473bf27fe97046aaa3b91 25 ebd7479a64570f02f20570fbb95458a302db9c4258c36d433ed6fbde8fc419a8 e86721542149efbcf75d3d5e7d70971f490c50f215c84e6cb7a64b94e4e9b9a6 3d033e99c60e683d26791b11d8a4a5903257dab6d13309535d81eb526d05c398 4cfb940c47e099cf73a4456950355ef6707812bb25f5fe91a6dcae8ff1a6d51b 20c97b3168c4018cde265bad0e485f01caadf1ec9a0b30beebf64dc304ef2363 76d21a98334fd87da56bcc0eac3c11c3b522b27ffb52d9a541acb2cba4f14765 e4a2e410878b2e35d6de41171ab0cdc3e536928bde6ac378a2084a6f834bd941 755f27d29df233fa300a35c8942726999d6fc10249827b0691a1eb60c176662d 850f10d33a0bd0720273d21308e9a1da5923d209627b3f277f5bf9a434124409 ab9aea32a9fde891552e8927fae5ddf26afca1636a8ce0921da9dab48cda6f91 fb96f2e7a5b0690a94da81e47414787cd467af594e917279e4363fda7741a8e5 03d68b6359e77e1cbf44375dea9e20b9c13922b265877b64cc48982cac7a8b10 749867f1b75339e392664b6329511b69ed51911fa34eb1ef3541bb023a22f2d3 07d9f24711ac50599a4046019cefcd266cf4cf9bd97adda2ed8dd16a95ef368b 9a75f8b10124e4f7749b7860f37a416ddb8945de09f2804b376005f0913fdcce a36bb8c950e7d73234cc57333919f24e68aef197e31a21d63f075684891c14cf efbdc86ea91c89ba251d80021d7a696f213d68b981631358c529e74370feb5aa 46b0236b4254957ffd5fe0bde5b2d50387e6c62cecf84cdc8cc0cca67cdf502c 314341f6b99dd77dfad4ebd906f0a0390c7830f9554bbbe0aa3ada0281401123 0a8fa5adf149c1d89f0bb2a1988bcc00611749c3d7aef13a0594462b702d7f35 b5871fe31c4070e4dec96d4313dfadc18134bee43cf22779ac37627b6ee641ba e87a948a9e6b52802552010bccda655fbbfb7b60d9df39709f1378e9804c8466 8aabb2b43aeead2e887d2e66a6f7b8693fba86290b4401c8c9d4a0f350b6d976 4d9f039ab9206c8c7401fb3cb9134b626b27fab5f5fd869360f0c9b63a3a4527 7f79cf8b77226b5a49180b236e62b0874afa8a2b84e8a1e07b6291035b89e4cd e2caf8442b95ffe2ba5703440d9fd9a84e67c80dd115a11f851457737c46de0af35308d5dc3b139075fcafca8ef46118a11eefcb0a7c541b2cc710b6e094690689073b72368ecfb81d2739468dc4b7dde135892a37495072c19360b0ed93420a09029a67f698145a6fcf5b624a931f7dd9147c72f6bca2e9da781eab5e997d0ddbe3cb1d5cf325dbb89fa4d2e14692d5e81d82da1d84a7afd00a0a98cccc61045233e8664881660247877c17273e11a411d077b660b50c41a08c7d930f994f0094dee22836d70d1dc6a6805285de2cb1bf0841d3f6a50b0959db0a6523bbc90dbd9cd8ae2447b4a1146e40dc03d0974858b4f3c74f464f49b7c9f3cf847e37077950378c766ada52348d2df4d7ac03d1e61ef13d1488ebec5208f5c9a3f1f30e5cd9dec85666e6bee72fd358841450f4bf49e46373720a0c7187ca7c7e1df2079dc3a8251ac277df96da3fd2ffcba886879f60cfbdfe84cf5e5fefc48194180e2e914488c098cf20bd6d5e3d10161cb8a3e67b293a35f846d902031a0d42cf049a6da2886c53ada04f9fecab2dbbb183bebaec03dccad0d8d7e0aac0f047a10d7301d3400eadcaea19a55faf7c7adc840e96e80d63be78b4155e972bb3f6680fcbd1cbcf59810e1a61e566e56cecb8a4954aa96d747b5c1b5efcc3b96055250998d4aa904eff2625293b79712967875ee9b90cdc9ac363e9e42a301007df4c074cbbe86a78232ada78ebd5293bda3bdce4cd09e32c741075b9933cc920a8d20e1c65ac0792ab39a89dd99f4b676eb44a812e405ea836d840008408913a309d082e8d840fa25351c799590fa428879b1085360a09013046a60c485937dec5a401ea2485b445f03d9b31db1c15e42f7a13dbe1490e3a2eb6a1c2ead4f4d8e35f0c4dfd3756270b00b53d87c4093d54d391ff70f935928b6ca654b658644aacde069453bb3e52c6afa9e63aa9e3dfd7e58a8a578f1e578421a8c34c4894e92dbf0e51316c0377be90fae1eded054a3e424e82886bd5324db6002dff0378269f8e095a3f83e534b3cbde9cb908a1193a5b4556f4b86f041b26618ead79faec18b40998e4957d61406a0d16c515ea637c89c11e10e8857f1c312d26e5b9eb7317ae040fa337a24917af3858dfdfc30e3fa317db9807fb2f3c487a5d8b6dc3f1c2c10bb8cdd7c033cdc8617397a5833d196622291af934b8d2fd46afb8f435099e590ba313332965f3559566a7f773e3e14d7405f1a0bc9650f97fe521639bc1fcb40ffb6ee3507b5491de168fecbee4240950bad3f10b4e2ef7799f5d4bd39147090485cc58846478cd5fd2526d029bd2b2180833241bc41c4494ffed6d87e9f77802ec3a4137921e1a6e89ba77591ba74dce44d0edc744b78db2b6ae6a54654db20335be0722bd4a6f2444d6237e0a88abc64c69668b224e71afb913b2ea34354b02864de055122697f40b9e94a56dbcdcfe039dd037283d1ddf8e8e192eb3a3800d5b5f810f586f52d289842191257bbab5b2bee0a258db260c0530c5f3d967944298602ae33dad79c8930f2cb40e766a6aedb9943cc345313a2dff2e9f360afe0d3fd17a7f591fea1e9a924869023195f6564984d5ff444893f7f4614b8e09a503b3ed7ce22a77f2751f002373e09692e400a12cb83cd4a55fec8bb81cdef33908a51dcfca18f66a7edaad79dd1071b902013496bb7c57ca265fbef0b413a07301fd5d0030c5e16897eed286aecacf1c48e6aecb93c5448a0db8d67339008ea90ec9600c8192ce9493ee4af7b3f693d60efeeaecb8897375a7d822dcd5e55b110210db65b881d00377a6098be571adea1fc4313d8d540517d927937f5a89848d084ff2ec95a3c4d1e4d1cef5a4bbee69c2c5b0c58c74b8c8afc3a0103762c67501386f8bfb71a09626917c2faf79d086e7a2a5ee6f67c69d4473b552a7a913240db98e868bd78f933cb968605c088dd4f2cc3ec845330a7adaf2174a5571405603dd5740aa68a1b2206804b157e48a4a18f85a1d326e1fe221866490748228fb04d2667df561ccbd9e245850bf18399c8f2b5ff49d2f1ef63467c7e5be25429f06a6094136138e5aa6d803645f9c6e8ebc2dc3f2cf489807e1c006c1c5f3cc0609ec755a0f7aa63285a54ab1d96e41cf33b63ce726928a06362df82e494076ec04d65ba27f8b2a688919533f258d643d1067eb968b6024db10ccfd95ca7b7c0a06476e64b8403d7f326c77913b0dd967155b3e178d89b7ff7d6395c9e834e26302 false +check_ring_signature e26179e93b917cf5fbc1f0fa992e6e388e50c2e6c35f046dfe0ec61f7398635f b7318d5810f0642085ea4eac236a519dc6796d86143e2bf9d703bde58c40c15c 102 dafa592940a118b8f8a281db5fa067b3f21de99fafcb0bcee4df00328bb50a25 aaf22abaa35eeeb89c78038fedd04420a5d7cd3d275b5c33be4d93d9ac9468a0 2211abd1ea939970e94e126aa29e0d37612e6b8e756f9bbd8ed4a92f8c1e27bd fe1c6a0becd07a13e9f784a1e655e8ae8ac6a1a4db128dd688b94f6ece05b789 b7447a50d8599d4583b38647164eb5a14f4ce047416e9068434141d109d00f70 43e5f4276a4c399a3ed5384ef193e6cfb7686d31cdb669b15ad2e5510f558d38 17059861b94df52c153bb434928b445ce4f6a24d2a28568cb23e1dcd668ba5c0 d74295b124a4281a1a768dc9de2b554d51a1e62c5f7f49919f911c99b1d52eb8 ed4fb17232afa0c4005d61a87bbc36408bf2c70e4b1d34711f249b91c2078fb1 41bd4c70671bb48310281fb7ab45823960e66881f8982ba1662b4538e7540fb9 4f93c5b490a9c9556873c874ac61ddfadbae2a868959e75b9e5d331bd7406e9e 4de7722dca2fbaff88d3d4647b41a38a3cdaa754ee87941e9ca899c35d4263a5 71d92e9b743a4b3a031458f593c2506e632a062d836884091696a33218f8f9ae 6e341968545fd5fad78efd6419e2ba7e14eee612b36479746b1d58788f5abfca 578c23f8a0da62bae43f5a5c34a4bcdf7a060aa38353e5e2db0bf2ad5288a283 522f0e1fa1142703142a76f94d8eb8e6dc92decf182d86af86741a63d4944231 66a49c16ee321c1c71048c7a6f2f88cd7e89d24779e4911bafe375a9765cb660 ba0e791f48aa277896bf5bd7c2520aa85718357c3ff27d41d40f32d59003062e f917fa094c01db8d8295bac411f98e361c643bc83ea03e000d5aecd85add8274 a302bdd6e4ef07ca2fbf32a70c354690fc13065044b04a3e3ff651155f0d1718 a0be9f9d202e102b76a5449ecb79dcbf7c9a560bfb57140615f3b69ed2b91fb8 f26fddbba955fe1f6cb1c86969b9bdfffb737b0936e1b2d745391c48a03dff2a 158c3176beaa9e3ad469d5f47201580fc0154e76044852d543be3c06247355d9 bd312ad46e2c8a0a97de42bc817a71a1ec6b088055e6296007eb21d1d77aec05 bebd7ed3546f0e28484399d05b501d1e824209ec7787a16b618093c04ee86693 fb47d1d03521cd37cd70ddd15bb33be73a4c29c8b64621a7df2ba8ec976ecb5e 71de54b8f394dfa37b3b82b84d09d90791546b97f880f94ac622691626359202 72c6f3c45a4fba62918b7f48b53550f3ae1a7da22889ec88c3cba9e10e49935d 19fcda24a991accee047c8bb4d8a769e54c95784f3a5c750ec1e81ae170c26f7 22724298b3c580ff0c4cdf3a2a80963f60808f83442ca86707ee55784ddb08a2 2d31ab7f4c66f392b3833c2a0a1343abd3a66c274fdff5c87b2a11f67f746182 30da2a1aea1ed7368b53e2728b19771ecc2683a144889b5ffc2de9c1c07b3668 88530fd4618b7f4201518c75d81eefdc8e2364bcbfd2dffd1365665a1142e98d 09c6f5190c38ceb0228a70d9ddb66ef5ecdd5c8f02927f5e543161671c40feb1 a3253836750243f0edc658cfdfaec50709d9d571b559f48de42e92f2ca5286bd 8e8eda0a0af7b8571f90c9b45e23bd117abb64099e607a0b723fc5953a45099a cdcdc588834ba03accc334140a4ef0bf9ec4dbab79c715d2f282530b159ae359 3379204b870f9d356837a30a1434a1ecf1bd34cab49121e31f303913a5283b0d 0ef4ac9b6493d5b59389417ed87db72e6580dd9cb4d6b8b2d86dfc45c7cf160c 4349b66b31dcbcf56783a27dfdefb9c5d9cb78ee33ee95fa584f6a341a7d8377 6bddf5a7ae8b3c4c595f106a3e4c2e14f21396d9efdb7672709f51a386f3671f b57c852665edcf362c2e239186cac60c2be4e1eba3d246c5c5696817857b31db ee77dac86608eaf45d593b587bd48d0528d75216ae993b57182d7ff661f871cc 6f2a7b2685d7c72e227f303daf7b59a7ca877ac74616be96409f2860d966df42 d3da93b0f83d2ce205ac34d59613559be2cb92f3d42fe67424fe59db9e9bfa59 23c32de66d778198b6e901c2e515f77462e95acadf24fbf63f3aeafbe36af4b0 980237e9dde06e9fda3bfc3d1c4c7151ae2ef4ebec167ee3e27b345f22a46e49 a325452b6b8c2bf789bac36521dc82832a1fce103692120c2ab4961dfa1ba076 3128eec3fa16574531f6791c87186cd98bb2e6ab3d35f94b2d0d2cffb09c2241 565dee29d687f846739bd4e01ab67975fcaabd208228c31c33afa8fc4116d63b 8fee93407d78dd6c2e32b7e90a6a039281e195aa8c78bcb7c4860418d30e7406 2c9697615f5cf5c073fcedb88de008d5b9e3fd600ffe8427bd1014f188d45c90 3853dac3dc82bd3382c48a24cf593385338191cb1830cf3abc5b1ad3b2709248 72229db81a9f1d597bc33bfe04b67681539516571d65af783dd802524343f9de a3470ee9cbb108598c53a9ee56d49fa722d90a36dc45329e8a3f3b49da7b7e03 396f2e782bd9a1dacc38c8a6f78d0c9965a9ca17f731e94d87442ee3c1251520 a163806aa82bd9beedd8287cc5113232abb56d3923889172c298836e6b55a6c1 743aa45c4a1e0d8f8ce8a7b263e879645768f966adaf67801236ed652f7a2396 09aaca403be04c36f78ff975b2a4b729a938ae8bc5c35c4f2adbc533bc625e46 1490743cbdaceff45cda902ba5af3109b280ea4b112f697677bf069acb07d752 d3d2587caf3f90e8e3293b0864144792d99a10be73fe0ccc46c768cf185ce459 34a9c92c360e078517497cbfcefa9b23cd0102a86fc8b278bcfd677094034a78 c3511d4f2605eaa9d3848edb8b86928a577e6f287c6f4520df0d8568cfc49c72 7449d179c17b046b35695ab2a33d3fe075dab16abc728ccdb909bb78376f8079 26ae840d843d50210607ad98f8a83e9a1189c12b4f711519e227a9b18286b697 b34ae0f8158502b5b648846e2fed48636d2d51ea484f6273d952271e193ca0a9 99e8e7fd3825eaea9ccf05b9814842c90d7d1b83263ad72a4944e0966b27792e 6d860e8d15163eac4267b2ccdf93827e96cee73988c624e4564e8055df701a87 9cbdb75f1697014c16210320083ba708cd97c7f7e4104a7b3da5f37923f72fdf d817c4dac97bff1d57fbe3eab5da14e6f142cd9c515604297d141cc92b468f2f 9c62b6df834c4df711d89ba8ff11a867007f4ef048a6f0f3db670b793cf75c33 89e0fc8245517fd7e03f61a60dd009e43a47791c8f7023d0cbd110167551268a 595fe638bd06fcd76d5340c83b9999be54adff34204f83f652ed29a9ef7e56c4 b2b4c463fd8a3c9d81550327cc4e62e98301c2430bbea1d45e78be056be4ea5f 90d50bb5ce9fc568afe9b54834048821d244d8ee6614a363f7826f45249e7c60 f4ac036b3e3cf1fbfd4b650232a38557016a33099905006dda3902f54c221dda dedc897d50d88df57e63791cef156da8737f6810303e57ca48a69be87a0593f9 be3b8d0549a985a88dd6327a71338db3a3839590b638bdd0ea1e128900f90d30 0cd821ec6b321887dacecec48410d3c05437f59b6579b4fadf2d3ec061208d44 466b6e23e16889c14f689313c31794b6542165a8626181bd1155e5f1337fd798 810a295f8a53873793dab8787dac74b4013fcd5b7eb9c56ede3835d2e880dd06 916046989453095c49f1a9d9263675e1c5bc000a78db29a5bc8098b5c3ae6684 89ff2e5bdacf1f096e09460163bf197b9d7500c0533ae6d1a4e206a923a8969e bf4cb927b7be1539bd6f7fa9b73834c9863b8d0f5d1e3963870b09638969d0e6 ff447b9a69773a9f85f444608ad51895fa00244e79eebdab2d678895a4805899 7a2eab6e65e0826e5628a5bbf442df4bf2b2771f90f331815e39903fac64d702 6205f4f6998fdd12c1fbd81afa7c371b29c445eba39a836c9b60d04cae3866f9 4d510cad041f0902ea77e20a18b192fd26e109ca26c08e1220a7b3b022b4d58e 3c342ddcca8e26fb2d297145513fb30b8e3cc0a52696002f0c825d3bd02b1f93 362ca21cb7403578b70484f422f07003240fa862a151a893b436e38eed6e92d9 74e85e3cc01cc253ce4e7ab62d6bab247084670b389d395e1c6884b6cb521ede f3d7f6cc2f7df613850e7d8ff19937d9650505025289fadd6ea60fabd27824e9 3b672fe1f151e4a73770093df05ba197f8d01140d0e471b94568d7eb06de0a13 a854fa39d1a3cad2c6af34ce8a170d6edaf2273a41b33a73dedddc8cf50b456c ba53352a5079864b8049f031db579ecc6d955d872906143aa81e803397e790c2 1eccbcb9bd898a31d6f8147d18e2846bd9b938b81742274e48da85174e8db9cb 0ab60caffe7e95b26abde206d31142f87758eeabcd9f0eeb24fb10a6c191cde9 083a094b1a80bed0fa93442b50532fe767d7ccaa3a7db3efb6157992b9c5d04e e460d9e74d71c0b21544cca375aab188f56e1ab2d04853702e5d943c460c3fa4 b681cdad3c866ccff1e270c66582831b0cb1ec06fb385b2f50871bd46c11fb4e 7b28503060a43adaf91dd8b70868a55343bfbf452738a4e5a85793a3512475dd 2e46b84195683b5709d8e68aade229752a60f72fdbf2c87bd7a12d093373416b 93c9d1d26d123cac0bbc6a48c86131c1e5552290332b5f2e4ab1b03099e50d0dfaef04c362f760a46982681c49a0f5d741acd2866fb8fafa69d1dd735ef35d001d6978ffef95764baec30ac650e9238b2633f1ecff7e92e9424569767b5cf50adf09fb95317fcb0959e8472121d35d860a8021222c02f9acb0403402f651a40c30d88df5b8025b2c11b7a7bed6d2cfc5389ae655a361d3835dca70c6cfd5fc04206c37ece06cb6d85516660eead0086ee6915a372e0f1768c00519b3f8df600557f50138aa9c17f881edf4d83439acec1860b131990b1e55a97e72686e13ee0f8af045a3feef62554cdca81769dc54641d3668a948a7b557cbab37ff1f221c0081cc1d50de8a7ea66bdf7a099b3eb02e30b9d7e93a6e8c8987da7e415e43e004896a50e2e84cc2ffaa32262ce08dd94c50c50b2757d19adbae5ad5a3bd4d3f0129f2e710c22b650963dd0a535573ed9327873769bed00fe356b9d3ea5387ec05de168f879ef186e06d13481912a93b10b55ddf0ca7b573eed7d64d8a922924039eb35ec82a2ce07db145f42b8a48eba8f352dfa8a6e8a94f028a45d977f27505546b75f734427439dd3842f8c067d233887a0f800054288bd05cdc22ac693d07535fd1a9f5871b005be62a0d8cc2e9ba718451f3a784285764db757644da3500c73521e020b0adf72eb655b14b9a13b7c029de10a4e190d7e623acc89f41710d6eab42ee1162150a6afe93b3905804ad21f5de81b2d35441632a1bc187cfa3071055b4b76878ac03c0d9cb1607eb3ca291c3b21d6f02ab7c2c234995175fbf0cb7878f77611c25e83bede79d3137c22a62d323ebf709bd406ba1f405de685a088863e184875d3a33940c8bdbee02370fcb4e79a060f04be2f0da37878c2dbe0ffc68e605ff173742e490bc11a14a8bcfd5564d3dad7a7d25e3fda9047e8470044f2c62499f8ede5f22c896c0015581e0ad875a199fed8e9e410f4a93f886090c63f4537799dae6fc17711a6b17122a9178742e4bd87c71498826e320fca450037d8b4b45855907c5030964b3ac2a3f2e37d73631317ee7f1b988b802ce1f570d38da2a4392caf02c63296e7d620c8e9d11cad1d72cf776f17f51222fa345cf049992166cb909d0efa7e81cf198a475209c563f646b4c3e5de8aaa330b0233203e6e9e580e3257b87101cfd54f0ba91361fde5d962b70522fe4961691247b300830aeb2784aa8db3d17acfd3a6de04f8146dc27cedbd75caa4229e456a1eec207a3b44be612292742cdec9879b1cef7b651d067dc485066d5f70643a3ac369e081914422a8f7e3c23c3904d643748581efbaf69f13d835bbe6d499b149b18400578352c24cc851ee65c21fede1748133c3de81a2e5b106fa497025b113bbfbc0fa54ab3d282fdbaebab93dfabb2a17d4701d014bafb5074d9a2490bba890d1201f561b931eab101a6bb134c1c77431c0505d384cf0edc20b341ae90d4d500ee06fead5cf347bc01fdf7ac880969c7ee3f417f84ce37099de7047a31b2fd3e190473cba06605756784e998790d10b1b8b0e11a080ed0c14c5da44ff894adb1680127aba7a3e07b879e718c47f73ebcf0dfa444231e876b20b49550a5fc397ac407ebcef87ab5e98f666d0f20ae8842fd2607fd1550fa11939df86a3728bcd943057595d0b99c1d5444c49a40bdb0f739d7fb8d4e8eb8df9dce65b4ae182a3c970be1b18d6e423b7b28c21b1e3f39e93b122eda8e8f83d2d645ba50df391b32890730d1c269905edf1a6b9acb3a24c621a38e9b2ec4250c6b675a6311a796957a07ab9027e80b899d072fb72f51eb93f2c109c332d3e85ce66a788c76510aad02098fe8f6b108287219234389f56111c359588f3d069a78b8dfd7da9cfa8abc360f654318e8cbd044bee8ff22ebeb2c402ff47985be67d3288f2c6a8246307b12063843ee7670ce95cbd65286c4990d03ee3bcf41af6e90b3e420f36e50fa9f4a0333ea88766f9ddf0a87c48fb1f5cb812b3b25b7de8e46876370535aa0e152e407ef628fe0008ebe35cd697c4f4a70ac5c0ff01b2559fb4b889ba833a4d5b82e05d721c073e6e664e647c59c6685e336116c1b542247d2cbeccf235e554e5a5003554cfaca0b7efc8dcd618c2771c71b103c8f1eee9e16f29a7282d3594746730a13405c21edc4d6af74344ac24fb33164c96ba34fd595602830a936a475c3a705ee645f7b2c04a3406acb4220d6126cbcef0634dee5d6a59f04ffc1958487890968364895babcd44d1ed586ebf28c1b2f22ce99a0daf8297c3df4f899b4d527022a91bf90909e5ac9975322b4cb1280fd47b6362141237548d21668826a0ac80da471e2c86fee5f7d2c0880783e77367269e82719d41ae6c7fd1e79c160a423080a491be0bc002bdcfc4ad3b030ed1ac5619a457d0011536f55942becfe68750c5ba55480599ddeb3be3c00ca0122a56467f9a482e09e4a1eef5fff9cace2f105ad2fddf3450a15bc4e66ee8e847d1f306faf9052cae718eb93c2324d1db49f0bbd1755254f0f5c3bd8805011c652afa6b1d55c9c45c1c3017ef178aa74146a02453f497119980c60cdc375ca24ed93eff79eb3f013072a788c95bfc214a77d085ae7fb2cb1927f0fe8fbde08a47dc26a19c976ef53787e30a1e4eb1dba92340432387d60a7d7951ad8ca84c12b49eca3dfe0526cf61ba3072b547fb818818401a9b336b88982a134b2ae1c9607d842638fac753e9bde84c522254b242d62bb0cee78427f57d333d47ad28815015d4e904f6bc940f6a3a2c8aa315d992041ca0dd95aab8331a29160134d4260c0d03b595b936aec325d7c645b6c7e552cb98d0c43d9418421c727eeec25bf17be37975c8b140e37ae3a735d8b5498b8abcb2d0d6b9fc9e93bc8a4971e9a60099d9a01aa80cf9f454de452d4845234f9dd5b1907730e19df4a8fad4e40e1bc1dabd91cc713b216c97e61e2799f88af2c514bce030572effbe00a4ce28af24e025d722df4c651e1b4df5910838029c4202774ca099743ddb8d340d653c98db660d9bc56b5ae493eef7b1e666455afb8a0e3da0c085c687cd7b05bc8281a53e14252d97c180c1c053b0d96498c53cbf0e1fb442802bf90f701028e976c840df3aea4b782b97f411b76fdbb72320d2d46646f25000a69f9e3942b1d760f426b1877cbf14eea13e6a35714fe64563429606fda9f310b7ac251322582fb60390084a969e0d46e08bd583d8d7af5049f4335c5d481cb01713149ee3f4b8c262919295e22c949ba755affe13efe79925f3236ebf275c10203e82d876313aec04e3a062f9aa626db91c8f7ec63e7ae866f7d39d7491d250dccf60d9a32b869586b9b4c74eca0b69aa72773a675e2e671fb3878bd3e81ac070ac13cf927da9cbd116e6b52a9e9767586d3a23e15c7a1503e7d0ce471b1aa063ae13c623507a6aeb4112542771d607c11bcd42e41f3f67d5b0d99a23d07540f56b76e54965bc22dc78db52208443a39bf213b676a6cb1013d4143c86bc1030dbe1ba0ecf55579eafc03ff94124780f618a5a581c2b4ce431aed9b84c15f8d09c013ffd852aec3ed888dd2efab87c5101df3752fdbad17cee12995112b3be70b254c9044dd15e9b130912e5af5763c9e3769ed8bb9b2d32bf42861c54054410d637b2469978a790f449de8259968099243acc07b7b8959ea7c61b8e56d8968024f5c047b96ce20b2f5f4b3317130633f31e4ce0d39555c007be464dfc35e8608941400e92ba00581b2c8aefbe360f6a8923097b364022b01519b78e7835c77090237c91ea20e670941f19975486d796ef0379b0b03ac66f290e50f2d9e67d20f762f1c539b6d40fed54b1f943b1df82740970c6525de25fdc1e4514e4068320a2df24f72a96082f6900890273269bbdc897b1d3262b09caf670e67e88123d0094cbc2d703d57bc452d2768c8a22ad27fdf4daa4ea608bcd940d2ca9842d64e0474ffd7d5b62ff9dd6af3461140a0ec0d6607430e82f39acd6f29b852abe49f094a9b75b78324e2642d9ac829ebe2dcde8df984516dfb1596e52495b0d418c70c449cad56328e508fd06fb6201f9f3a81ba77ba9020222221876f3b31c963c60cb7dfcf501aed029967720db0a26593931869634fe25f1c21103bcc3da85bd208e67b5b089fe928e1f9691b7ca4f0e81fcfd487d57499f1ba801d4626d23a310acb2acb683bc1ce0524186b6906855439b59a500a82156af3c6ed255c956f8005256ca0b59e078e91db0de45e7d0c9cb74d25a11fd9e012d67f3bbfcff29d3900cf0d1af0f160fe8750074848c87c32e0c073bbdaf1b948ce3f045cd72f3e89001e39d2faa1cc4278bd2f6fe7d75c7b57b9ce22a45175db9f450abab4a0fb5004fa6a3a323e685804fe94672581a74cb72b57afa13f9a6d21ace05ec1d7d8d0017c3a5f541242b4ea9a69c31e48c405c0b87c13d36bc816c2ef77816b695fd10b786922da6e13828bc0f019f98932828301d9bba73b4e7324a736f1d3f087b20bce35a82aed914db6f23a0879be2ce968defdcc36608a58546b0f69788698bc0d5e498a5efd79b3d5a43997eac269f0fdae5ba0b0da6155979d7d151f534fd203384abb6bc5344c4dbb6aa449511471a197731fdc7d6868af7225379cee938304e1578faf5fe2a08f26209f629253a6ec7600bbccaf1c86f8688458f2e49ae304a9703323e6f4586af8235b035b9128c4ce4552b0b9c79d20987224bb8939c00ced8947d58917ac9beed1e3c9f84b38eeb92c1cc5d7c2e789833acc32e3ade9024187949dc9c6259610a221c83fc10730e04fb83a873f97a5b56118e9ee08260a5d69dff7a94677347770bef32080bd05113cc0242eb9eea128554b0fa6c10700acf7427139fc327ad60cb5a7a13829ac5f0a90ad1811b1333abbf03fdbf3660240e63b181df41b7e67ee4ebda2c74c48f1d0d7af2e8ad5a30e429d1bc9bb710dd3496ac556b7c4981b9255fc5c8e66fde733d273d5d4e105ceb5574116d7320ae6e01cb76be40d88e20ae9956e910e306cf09fe80d8a505dd9d612241d0f3c04244919ce295a3481d6b355af17dc2c05fb97d7e42cf700e43ca75392d2f0450cae0b20602705cf7cc58b6553211e946edd410cfb476bcb88ade721eeda3d40099b51fa29e3554008d47cba97d1a9677d2999cb739165896b781c9cfc2c0d9b01f3b5e97dd23f43bfbb0f0e02d21940c5ed1addc340b91f3dbbdca5a187922804df20fb946f33a34696b813bdf5a78fc9f60e3fd9030ed07a03bbd137db7cb3042046036ed010673e04a8f49ae15b2df2a7485457300422e198097eaecb785b04560431359ca3956894efbce709e8bc015c85e8a58a5ce5c94a6158f69019a3043ed28ceb8d602bb583f36cc60fdbb1347f37599f83efc1129d73a214f940b50fe6d912f1636feb4685e23ec6927b0cebd1037a5f713abd5a1eec33f19d26940edff2055b0022a2086e95ff4cffd6e40d6b9c73769ee2ba353804e9526220990bf8366013bc74292c21014be61f59fb6a7101767020d582cca7c54bc9d3c97d08be3448dc00ce7b29a97d87cc086e5fbadfe65693fe1efe3590687350b869d30d8ddf5f30c17c6b63a7805c48bbd7a2b25de1b4291f0060dacf761622e038f3043fadb2e6f41bd2c99e659e6b271978f51d16be33cb680dcddb2f1c3d5501df0c40498cee8c001d1c004b36bf9e5cd5f1fcfd252cf8424961e44ab7b9670ebd011155bfa36f126bf90230f7c80062773ff564cdab7ce0583277c5d74f75d1d30ed6ab8123ae9750a647e152b3b74e924c011653d99b217c4b7a1554e2c6c9c5033302dadc1ed201f6826fcc606e6fa7ac5425a2b0f7196cfec19d26f0ea6ab304174b8cc37aab1c54f515219c9ec9f51a4a5280e525ede100c6405dee59e82f088baaba1c5c5742510d6c204b1a8c9e2129e43660a6c8f486bfe0d3501d6a8f0d49a0494796058cf947c808f3e2a43625e2917601b831b3448c01aabd6c90be03c0c95c3576ec1b9a464fb125850bff5390af459f02e26ab74e9c7fb8746aec0c6c54dfc9f1af21692bc77d5d8d4aa29754637a1afd2543f1fcf0e2551f1d850e0455546d7da5d9ef0001548d0c0024c40d59b2556d3eab105842cb2bd9713e0ad054675a657d5e1e0b143fc4f3cc3a0774a60e08e2415802eba961b00acc320c75726f8ccd7440d2935577336bcfaf4494d0d5f0baadf133e6e16f011e3f1200058f2c32b7f9813d8fe69492fffe5f2f437bbded773821fe0bd0616291c2ce053c9d44ff17df7a17f3f75d8ecae0ec9d584ef276d6a2930763288cebd141d8020b8d3356c3029769ebcfc3c7080e79462943e3cc08c41470536bc03b046c06025fa100ff85988b2adc02203968fccdff41325565456e754c038a8d845dbc210405c8797338993abac4cd18b1aa53fa725f1cd5a505ce0121b5fbfe918e32600789453afc474019038137c9a154d05adfd11502e49375d5e21347d1dd4c203d056ab944a5049e811bfa5bdd82522fb26828350b05b50178db6ae608d9000b2f0a603fa70b02b7301dd72b18ac43f6242908037c1f1bcded333de82562e298500c6d655b6504411d5ae151c82edf62a862e99ddae888bbba281880000317cde4017b4e4b816e06dbb1ffcbccea5f5acd72f31c0a3da73e49c5435dedef03cf7a04346cb5205e1a5975ed5a4b412dd485a21b77637ec45cc782446630ded9e689089624c7a17585567d5ea13dd4249219939c1597961f44f4b06fd81aee08b55402e19a226066af76360035af7d70119022f6b36f940d5ae4c48100d0bd2349a70c5d2f1ecfdd280f1315f1c10388eb533d8f8c548dab74a8e7c506b8cd9f36510b172c0ac5cd247b760b36d519f4d3dc5e5b8bd77ae0a0d3cfbeff01b6d390490e36ee0db69f96f013e08b2e3e82afa275c977676bc2426877a1a880dcc922b20e43843a86bdbd0f677c3cacac84f8893eb486b005c7170f06eb3ae6202a06470e8438fbc71a659d2ccab3e810365ed62cccaa20ee743a55be5f669b8a74caa50d0879ab398956daa30f99733e9de626bc0b5f1546d8ffef358e9779f7646af80b083a345e96eb7bae3130cf76f9d314fd8d38c8110410a117153b67be49994906fd1777f8b31aaa36403b95d7bc595b2b1c54fd139634ac7e60ee3144f634900a817c130a54c2d49ae841d45f299084608d3bea80f26bc46d1c0e01bf214e0e08e1554ef89a873262b728440f5e02df87a547d0057b3f25ebdb940f212b974f0e299be942562c74a2efd9e5f2db748a3cd1582b1ced780f88fc547f123e4a510ba136d22a66a75b476f65f8736e40485973e7908cefc205ce9b8ecfffcf79860c4470f294f6eeed340f1338c2aec5e76fa64cee1c68497f373c6b5418f915d8012762876794774a016cc8d2260f37d87ebc496624b825649cc4ea3dcfb0bb25085529c81be95a23e7a15734ed1fd5c3ff7645928507aaea03c7c8b9c9d0501b0720e3d7ae2be0a9ee1fb400f27afe373d3281a186706632ed4d3fe63f4f200d0216de2aa7f995e8667aa857765dde88be692d4a304a994cc2d619be2f8244d60b4131c77ba8e1018e5658eb04a85a8abffeec8ff63452f75d08a8abdd17a66d06c0c95195d68e0c5c2815dc66c930df7e3d64505507b55eef0383f012d0f5e70ac0d9680b1932e4015aa12967da11132341f020b46f5f2998e5874593b18d0f015c6bbdba88b3ac94e2736ff8a5cf1af858a688b732302952dfa6f0522aef6c022b59d9abcf7fc320d1958ea1d129e391eb8a915157ea51f94e1daf81f1e01902690f8aad643efcbdd8786694478e1934e4338036e55e843c281541d9f0e7a7003d204642d1a9bb7ed112983a142b9a63f20dea58e0ecbb200dd2d994ff287801e40fb268103516d53ca020a4be22f57f91bdeafd141abf28d288ee88d372220ac75593a556fc0c4e689741f1d6f87ade896be62cb6e85fcd6dc5e55bb64e0e0dcbec4870f45185c19527baada38260fc016f94fff1883e7794b1ac5585cfaa0f19efc39f64bc2bc393ddc0316b8157a43791a503ca5a254e6dacdf229617b800de5a3e69fa1b8f22e0681268f851daeb66ba707281a847f2ce25beb8d2b92705b9c4b5f82a0462085f4906abaad9d102f3ffdd27ad979ea96c62fc414319770d94d50c5539939c46cf8692ff1f5d9e2e92b4c2e0df4058c887f339e0ab7e7506308294b7a30cb50ca7759b62f9ce409af9c4eb1f7cb873c3040ede6a65b8dc02edda80cce971850884add0a53c5fcf8f8503299ae1532d18cfb6a9b08fd7750b9d924b77303e6e26cdd887de27fa59e306e8a6f5cf9fb75958d008d5cba0e2004312a1764d89b8c739f780c306eb19c93004690ad4f5dd369afc781064dbeb01bfd280a380029c416ad5f0411a2c210c2d181b4d41a678ea9f62e031c8bb7808912729da179a535218a20483f83cf5b7e364e8da5a3b7f3bd4e250bb7222c400fcda6dfd7ffe17cb09ae7e4a4c4958bda4ed26d04cb1ffc7fccf5c55bef28f02763ae4bdd69893b980dab104ebd974addd48057a0b22be84cd4e1e39796bd003af44880320a78fd179f78cb53fbce5c22e65696b74ccc61b4db529de99c2f40e01dd152d2721944f19769de6120fa015d0d4b93364bd87110c4ac0f148092e0b08d8aeace60117021971455abcd2f9fb745cd3ec443238ae6ab7755436625d03390b91dbb1c6a5eb9de256a2b0878cfadc428022efddfa17460fbd0634a3850e7460fea0d325d50ce4d03817354310d04ec5f6dbc7326de3ea344ab47fff720c478b969b0409b0b561792f809946e14fbcb98b20b4de7730f0526cc0ae00f60c7b651a72a4edeb9272f0a2cb2cd785e8aac9d85a89b8e026caa260e6db3076090acd067c58b85afd03fbfd2248032ad1f604b66daa383b5878cb63f974cfea00abcabed82900f288752dc0ad6bebceabd0e82dce6a7d1fabcc91ff07f2ff410959376aeeb6c0490955a959ba153885ce48169a6a219e549dd6129aea3a43b70633bed621646e425100955fcfe6ae94e5a13cd679e9609002488e7be208c79a0ee7b26789da8857361154ba5f5b3f171f8c404134a6c3afa0f8cad996feae300700d89effd89fb7bdd3eddb7172bfbdfe97d917001ca3e343b0b8c51accb9f300ef9ee3a39f923c8f08cca5750da16ffab10c44625ec097f0485cca4bcae1400c false +check_ring_signature 5a6dea18a1362485b26275f3f03d36ffcc86f015ae2265ee7357a71f289ca521 66f8ad1da8f2ba896f34243bfe5498cb162d3b4dcf993506ee2a07b676a4f65e 67 b38be683d8e1ea2ae78f3584a2722e17b055310186770517b93a997dabe1c18a d8f55fb51e43a53255f76e2f99c66ab5aa6a6f3de7f4a0081038c9da8b9f8a67 c806828819577492e1c3f0094091a2f48dd3a122fb460ba187f5d408e98b0d3f 615141d43dcbd85ee3eb753e6d599608778a26617f12656d207e00a5561e01a9 9b0f164f3ee9ae9fab2f61b6d756d1a6515de04a11fcb61d525769b5a158e38a dc3b0c78c89eb2f9e81d39aa54e8a766a1b224817cc78c670559787cb8b6ac5b 6631eeaec5ecd42581ac1e1f61259183f6c5f374e6c0cbac95389e17f6bcaa00 c836ec8915c41b1ab95b64900379fd89c3ef049882c0dfb9f56a5bb7f78e12d3 157118a0550fb7a8d94c17b7247f486fa910e9f8991f27a88ae602e8b95394d7 d6d25b11b7331a9d67bc4e0cd1ae424c7cedcb38aafd5b35ae4f4fa744cdc305 9ad07487e5d084cde6c2bd8cf41db6f8b2119a20ecb791ac9d3cfcce78c46d81 631be95c849a1de32f89fe0b92fce76e14069344a405f5d310ed271b0083656b d512beafa41775b15abc487f5522ca7282a1b9cba202d649b09ece9cc0c88ef4 720258da0fa8ab7be7f129ab6c1912c698c384c221d2ae7f1a507fbb86690fc8 a317bca64bd2532db1396f3b1994b9a00b10d96af87490d9a3deec31bd1879bf 354181158b7feba39ab80d6d4f73a9f4271f1d8c3ed76902484fc6568b1f5477 73909ccf96b4f51c7bbb8b95c3d781685c91309a8cc340987a7e4b6f2393e0db 2de9ec38db3ff5d4f43ae6b46c888cce2e7b7fa7d56ad9f17752bccc164a7254 0d02f84b640bd010920d9e8a4566f25c232817358d14a3d39c3a529dd31c9a49 24fce42383c8c6afd08a3ec17af73f50cf40c50f879b802bb9c3c661619fb5df d741e915b6038ac9c2f865f9fa844aad2a9da476b2cea8437d04a63acbea3cd3 de6c554e54baa120ffa1e789fa25d750031c176f9ebacbb0d14b42724c31ef35 56ce9c3a149f411c8a63e8348204c100f130c97c935af26409fe6d6bed2d506b 3ada6d6992a22cd1c6badd741a99a59ce09dcb196711e8a2cac5858272d4dbcb ed8f7470a17e9a5b1aaaf48b29e69ac4891156aadbe49751af1a6ec56ec7326a d5a53b9196e34cf462dde6b556d16f0cd23501b36b8eba740478410eac3f5826 a804058eae28e4620bfbe991b4bac7e621ee2292ac3a4ab1a6fc8cfd1bbca749 5012e2104080ad8607099371d2a545583e5608ae0da3705ed72f8887ce544ad6 e19cbbe11e27ff6ca2c0c831d5ef207cc94eb88a100b9eb16d68b8cabe823ff2 cd56456de9440daa06675b4781257145e6602ffa712891b78582b88912492e0c d9e8c6dae30de37e92fdfc2816706b273eda1777ac712911bddf73ab5f4013f4 536596a5166cd5074568d9f44ffdbede2348c6aa1964fa705d7f5f5612c8d382 bea45c6e58e0a883455d5d30fcca5d1bb0a55e96c98755ec3ffb0e5f30192738 fd773c3a17028aec7523ae7514fa9d9bed2b984f13c384ad34f479c0814a3607 171d79ce1b2a586fa879f37f3ddfb022512ba701fb3b25e02f57ac35cc2429e7 c86efebe97ff6b6a1f766df54697bacaf6074ccd6bee80ee59a278816e2a0c2f 09221fb994e7d069583eec7d2c2f983b67892b12f8a620f6e2c04aebcbac2ddd 54ab47b69db5497f1e9efd2d9d11a0ac6c617db1a24a61d3086894706e45b4b2 c9cf7c3bd43ee45597a629ab92e7e5e6048c1cc13c62fe44f4a78e95dcd9adf7 6cfbe977df27ac6120959a7e5019499ff88eb26f6f03e0ba8598229dd6919e64 554160c853f840ffebaf6840bcebf9ad750a91d7503ee863b8dee27a99682a81 794e6e58d134c64c131b9d72769c85dc039c163dc43832cf57a3e8101159b3f8 e729808ded6143cd1cac96c8cc00e58b146dbf337e9726fa63eb4f833747b2c2 9b6254f1ec76faea09081c024e04081e66cd53e51c40f44cbf89218c954cb81d b0bc66d3914523561ca8ab2840c317f852e1abf399708dc7960fc52df8a6b523 69cec899cf0a5d9ecb1dafdbc168c48c76ba72f970ee9a0ba9a380039b91e472 819c9f7b2473c9d66f063d7e1cabf995b38204525d3e9aef3180aaf5b52a70de 3e4d0ec12104251e75df0845a651f9b3c956e96c30c5ce5b97a2a752418a563d bc332b7a088e0b65bfb6e4409097a492374dda679878eaf341640eef6341005f d3d1c71bdd6cfa81d782d55f59d34afb5ec395279171b82c5bdb64e2574d88a9 7e17142c22f359bb6c14632a05ddd106083b97f260296acb6854e17cf7029b4f afbfca86540f26f7a83f620d3cee0cca7dfa929a824f44c7c17a7a33210107b3 1b6186220a8dd65fe762e13a36b5e4924521b488fdd0e0b6f609ae44331f700d 36a9c9e0ba5667aaa4fb9e43a9c148eca6dbd3d039d9cd102f30f76a643a3782 32473480f602681ecb30d4717496ff1fbadb90f50af06724f56c1f33b4eb4d12 0d75567ee367303486455a1efa0565cfb7796dea918b236938d91cc26f47cc3f 8aea919184ecd9941a6867e54e7184656cdbefef9dc630c92b8cc4b2f1f1a238 101efdf014594073faf9a0fc9cd2f1f05a5beab09cbdc27c379c9ec6652ebe23 83f608da078c9c8e055e800813e7c6a4dd69be8e15f301e9e3456e595faa0e63 829771f32cb2a0b62ffefd6584b99eed5d8955f91c604f3209ff4b1618bfa711 80cb45bfef80a7dde8bce6ec40f4b661a4f2022a8e79ddb043820ab5fbfc97bf eaf7c43223dae386584f809a09d6235a203cad4dd7fc8f28109d48155f1ad553 4e70e43238ba7260f739c542946d7903c666b89e6d6630e8fcf045a16cce26e1 47d76037627acb8420fd0c5bff313866851bb40eb3a2f53e146506b890bf5112 b3f5efd043fcaa5aaa40b934f2b73fe6e06fd13e708175a8a365572aa17bd442 6969aa5975ac1499cda05d08b091170442d79c94eecd9f25d5ea7b023a24a3e9 0d2a6cc4801afce5b777729c8f8594939122a2ec8c14287df32d91a6e9d9e7be ab3979e6141e7f30c95b3a4160425278eddc05bd052bf57615b8f2379219cf067118f31cb7172c3c5c2bdde8777166f3637ed3c1a81709811dc159c35d6c9b06bc647a2211d47e170d9c5b990ec5bb1cca0cdb844af7ba6ee51edb9e7477a0014291fe4cf5f4061afe1e5ad47b601b373d7f83bd9d1a65e1a9b6bfbd8a1572099e0019748e7dbb4e630ef491c7b7d3ee9215724899269a9e219033108716bd0a7a81ce7a0e3b1c0d5cbfdaecbcaf74555eb03ee80fc7f1647fb41b36c4e04c009ecf7019071fcb57dfed1090cad720f344ad252e1bd787cf804ed36bc3f17a0dbda4ff670947f84594e83285477eb5ec16113ded7bfa922b25f401df9137a20d251b4d7edb38b84408f11625410d21597e2c3e42f8949983500dc1cb5c876001d8e82a970abc17cef55c59fc87f1f4edd9c83a7580503c53cf93c92013ed640bdf8ef2d38473f2fb193b6bd258b61e5254c6f12dc7ee378ce35546124ebe490a795b557fb779a6e957414873d0cba612a6698346adf0c0c0f548c8774c047d0dca80585efe03418b530862e1245aaf2ad593d1d8d426324197aabe0dbfe67102af5c6a5c67460fb371beabdc4f56ef81f5ccc375eb8b91fc2215ba9d8275fb0f7c963178bb852730efd8adb54e7262ab447d7b75f261cac47063406cb0401c08c8168eac2dc211013c849bef7b3ad6fac8facca0bc4429ffe12749e9dbe9a10a6b5d720b4605a08d65cb246d56928d090105cbd97a1ac6bfd96dfd7142a76d02beb4634dd9519dbeab4160bcadf6f8c1251405d27808276bdd62275b091180020eb6e7b9a34ebe941a57999dab431dd17e9721dccc45e9998739ac6bbe69dc0bfa5dfb085207a346e304625f3049dc3249b1aabc6b86f2ddd6c1d3d55610b00eb16daf0a33a8b4896dc11157bb63f9bb9f7dc5c4014bf53ff0e14a3f2177050d84bf826f2bcd9d4d6a05ade388e00a399be3fc67e948af0250118ca54fb47c06a4b8827c81cb99f46f1d6e919909e437a479903fd14ff9280d53f6f08f3bb306accf79ff693ae265a09a94c53e02492be0276210ccf72f406d49933e08ba2408e11e03e454176242161943e1fc6176f40f13808e0a9ac19aaa137cc98b6f7d00236a4e5cce4f19384bd1792a4750403886f18f783a23e96b286698a5528dae063604a38de225521c77fed5420756ce6f359468713adf615b942fa831d044df09fe1756bcc69f504eb2f84716a53523221f5742948bcf3e4bcbfa4591fbfd2b0bc1716f5f3263aeb37e9971cb7ec13a7430b75d14f2269762617dc3c5cfe5c9069f63e356f1bca125b6f29a331e369ba0e4f95553f8e6d9f6fc34dc85263082038da737062f5d2ae6d80cb285e64f07bcf01204bd4cc788c5a3152e6a8c36010bbdbdbb25ee9abcf9bdec106cf902926bc50dce9d60a24e1d733a40b12f8adc0665dc2453803b96e448f0f7285809050bf59f51af01ef336132e6e4a2a29076010eae5ccd079b5751734db9065d1cca35b7efcf24ed6353d13bc5010530de3409fce5580c2b8e36cd70c61c23c1f953f8729c27f7b6d9dc4b68c3200cc2a0f103c5c8da9bd7af2c792e2551cc687731aa26522e9652f0d2c5efccdf9bf2fdf504ef9eb6226e2f5fd3b4887fa5334bfcb4f80eaa58ad22e2f4956676444fc611086a3ad1ed5f49191af8bdf1aa9d8dbd24539ff95a73f8d622ef50737ddc473d085fbb9d28dc9796fbf570ddcfc6af3927b46d53eb5e896a06767e37c0de0c60023d3193f54262ae208ce5292cf686879aeb5d25961cc43abc51bd355ddc988300e271ebc1eccae6eeda2790dc5b9c91bbfc2c067e03fc882ea145be5c5aa7ec0c7224ad609e38f1d0ca575e1863e8aec72a6b7fdfb75716327be93804d1f9950625b34a63ec33ac1f87fd1401c2bd3ddeeaf3ea5a2d4602c193a8a7b2877b680396f95ea95922acadb6147463c67d2269b9e935dd197402c218f28bfd17aafb0e219ba3f230f33f8dbea6cf1470c0c9cbae911a3369ca7f1b52939015916a4c033378006c23e50e8c1718a178b928e32d05356671371eddd22f075c805ecfce0e24f0325831e86f4cfe79de1b1519286a65d28210369893661057fc98c615640d805ef4898e8a14af3f8a8796db803851ed3a673b8e5551971dbfa95bb11c8d0f7a185b0f17917ff7db20d6e990a027e0f179f3f0b8be7b5820a2f6df16cd9c0ca9e345be9e3f1bbc228f6987003ab5e2edabbb9511596d732d07ebb206ff1a07f6491b1ad63137807255d5988ab7f2c6bccdf3004281e6174df78308da539208d26128d1fb9963c43526c9a95bc339776b9543dcbbf33431867b05760dabed0db07a63bc6306aa39aec17fd0ec5f416e5c2ca77373e4fece6e3fdcb389247e07a4fa08e6293f70d56a293b3acc48ff778f2e74df774c9377852eceb140dcfe08d7c4931bd42831ea6e435bfadbcb44f56d88cc3b995ee2a532bc1f7639ba6b0eed1b35efbcf9c73a3ac8990fb7af3cf170927f0198e811747b706a2ebe60df070c8a4470d9644022d1aee8b1d17ab4eaa372c98b19232c05aafd6a88b404c9069f48924f92be7b7913e0ac888bed8eb9a1bb51100aa4eb7a4e9656bdc30d2b07fc27e7cf7c10cec61149c14e71ec4298bc6902da8b6afc472ba93e1dd61d680bc80688f8fd0a62d9197d56e8a4a3f1287db639af138377d3ac5e98c96b62e801ed323b55d86d6b98f3fbda16a431a302c080a55e6022c864cc1de012d4b0ac0f0e72df7d5b3431ae13cd5da4ecdcd2f663fe0efc5182af2df0ea1f540a51fe00fac953e91b6208ffeaf5181b19ca826217ac27f2c9a6ea9192b135434a124a0295638dff52ed21062651b205c21879151c2662e7497404032c513e579f612b0d45871c2dd498994d634ce9e2d4d5e7756056e04b04cfcb9675bc7de8767d8d06596d0572a1ebf42cffcc37049b63f0fc0d642df3737a4fe7573ac268039ef608d345f3d56e3102283d81f17a3c3e274f4eae0b7545bb37d31c54b791a830820e120a4a77f70d54bafaf33c6b6bc05fa733a4a668721a4af971470450ba1739058e2bb48686c2e68794bc7aef9fd8b93d3bcfbd6e77aad93a7c0a4d694a02ee08369e6f942274309f3d738b1a01123bc7335070d5d290592944c8cd48ac05b80f5ca16a7c873445b43a67116a4b52440712776f4b28c09315d2aaafde4f7b8c07001c063c51b5dd1840c3002c55a192e712bcbbe1b4d19ccb81a46d93f18dc709c2c8f588b4f062297bd5df8bc0f67947fca3525f5578c21125238665d167360a91715002b8791e93f64f934dac6ba3d7bdb91a5f63a45f8b5810f1e3a6238b03755c062e09f30590a4c410e629c3f425d6964068160d17a19b495b20bc21810f536c7743ace3eafca263639ba5bbfc96fddda9a84fffc219e7f8404056a1fa0bdfd673f2e6b12d03770f5fb2ac86349193588c35e8cbb515f1911c531b6e6205b612d8ee7a4893a64b4661caaa38cc00d685a3b981be9dab646a15dbefb46507690cd79eac3cb283c79a852c37f44b7e52ed44fb177cab86c230599f290b7407fd8e134ca6bb561324ee42cc3b4e003b1f4b0b1f07c67d1ce9bc96f0dd2b8309270ac6bbebc816e4cfbdc44ade4358cb70926d4d0ac2b333c08157937c62b201bc809bca633210ff5a1509e0253376d0e3e2fb654df1cfc2345700b2b7a4b00864828df081078a5931468bcccc3550d8679659e3ddc357754b232d294e6cf20c5831ddd8e6b90ee01649f4737adbef20ed0d03973fb2f2d85d31b385f02e3409a74ab987a31547b48ad6810d0130a21b888940376a7030c06f6907e5a5b2c40c1e0dd17249009b269dc6607703bf9463eec41b203cfd6db821722047af889a057b20ebb682799d24076c2acf718e392f81342a4342ab09419ba72e38f99a020d1519c34349f65c2c695a8adab85fd2ce5d98da4b3ba830f6e8ae267f0c5366072e207f32bac1ec6764ce120b87c565546305433d5ebe1c060c7ad6f60e119d05ba5e8274b1a40ca17c29b6c038480e1f2b8ca579c3b584cc5890fb51e278570211db3fddbfd909e0290b501eb850c6e09a25e292b3b6d0b2c1f08fc3b925a00a555d1160d638b12a067fd372751c98d18caff058dc0cc8830d4e2f52609a5b095239b63545ace30c48b73548d45c9eb1b9337531b08e93e7d1f6352eab23f707570e29c976589bd6143a7cdaf72a9c143d83d7d846157df824ec79dac24bb4011cb5f22fba3a578ddefadb1540b1598f8bac8d57bd05baa6cbf902435e08f20968d820c7ca11054e662c0ac3f1b8f61cfa291e2d1ede43c32db637a9c7f8be0d3d7dddda91bbd79e446b3329ccf8b80384f02c58a4865f6ae1b84de985edf40920eebc0cc7c1e76707d0e010fa5bbfac522fb99e3cde323b6fad44e54502140cb5e973eea372d80d2724fb49a397ad5ac396e3bba0337c5d3c4e00ef9136b80dc0e558e0d5e237990588bdb7f6057c06e8dfe378831a6683ccb1ac077289af0b6381e3b867dc0d57e91837d7cf265495c226fd77817b48dffb64fbbc347a48081c9f8b537f7aba24eee47866cc4f98c8838b6828c64fcd1a98293ee55136b10a8965d200905be145578283816223abd7131d007b0f9ba28f9e2e6af85d85a70aa4078bca5f9aa367c10add3dc742ecf65221bafa5e24622e8fc49aea0f30c8033d598d7cff7d79b883c3e2259e3f4274f562163d2268bf44495b106a11a59709692ce660c31f811b7a01c3ecb3060f80732a916bb4d80f3e10ee15c4f78f540b19bb37b4c9ca3d7388a5fa5fa7aa0c5536bae76e0da7f23298590e817c82c003af18cd5344b3998dc8a5c57599a94a96a7b2d81ee030a30e32c7407bf96627095a51d2613d4c5cf310761f554104d0b3c7106a83fd0befafb7d2c11cf7fa7d0e5777f5ad6939597d3ff6cc090b131af87e87ac462b056076e1478db703ea3b041d43db4148fb20106e2c36e592ace745de95b9b21b62f09d41e1e5a92638c90a0242518cb88b99ae4b08a7beb0f87c7d4c3caf85d96282d88db448b62b71ee0688ac892e1c5ced4c7df0e47a132d9f93686632402a6b513773973fec174c96078354eefb25e1087d439e7815a685d411cceba3bb90ef59d2cc0403914fcb890f677778a12d586bb56b2212f4e42f5e9dbe754bbbee2b8a53bc9e53a009bb4609e025c00fa331a86930deff15394dac95145f1727805d69309d5e9de327c7dc0e2d55a77b7b7c87c4715cd5f968d94979448a9e744962cb419cfc42caae821901a7e0b4da9e390024c99238d47b9b5a1aa682868ef3e4f66696a4dc876d89760bdea500ef767737321f3abe041a2e39b90cef2ed7969048abe616cbda9fb1fb002a2e81476b05c59dcc25252074b91b56a8114464c38574ffa449f233c6540a01b58fdeef82f54eeee67a79a1a86fc1af4dd8097fb096b59f069ea27e50f10305759edd325e707ce502712b4285fca67c4b1848145ef5c8fb66d687f7a801810748c5d1adb39b4c3702a12a72bc33a508b9112b45cd65f4a69706c7078ac73d020d5c2e7eaf6a176b03a4e7f0e11fbb1b5dc95ee7894b82c2c2d2ffd6f97ae00738ff05158bb427d504ec9a297c72b8cdb7801d93c26adf13ea2f317d17bc08055ca43a556ea3ae0c16acde4ecab263fe74dc11fa5bf6febe4ec4f782f842eb012b5fdca5e9abff4b87062c63ed7a9ce2efdd99ed0514c4b2e6566c7ed25a5a0ff56b63c7a4136a5b3d2290ed9900924929695dbcbcd1a41785295c38dae8bf02ffb5b7e2d402ede6417d9ca45cc30fc95602693998efff1a4a254c4fa103640c5ded3dd007a28796ca1fb47d4ae3cefaa748f8b0dd938def42bc95873456e40e2d026d37ad183f48b270e467739c2dae7a6480d4787863bdd2016b3bc449040b59fa16d7b126c1174b95d8eae8e3be915fae0154bced4e0d60dd480d5a61f404e6c91759b919ee1d8730de32fa132ce18a84da544350901ddfa41e922921740a04c0f44452d5558055c88f14313ee0b0ff4900dd285c19f1331301450a2dfb07 true +check_ring_signature d19ce8ba6268c8a7f6c3366f77538204d331f97e1e65fe3d27a790850ff1bbc2 a0e00a8f73260cc188066e62779628a404c3f29381460dcc56a14985e0fe4090 1 3c366452ae11feb1f23aa7ebe30cb0a9e7aaf34312fbf914687b7d09b74196cb ba07196fe8b6953458f2ee1994e04720fbd9dbecdc5d62c7e086b49973e8c3013b4c28281287bd3c24bb5cdd011de8454cdc3d9e93b828eb922cf9de954a2406 true +check_ring_signature ac15f27fb19b149efbf32385b121bf30399b6e52064c30dd91ec69ae4d5850c9 7deaa72550ac632693e153edb402c4248c7fed6688728b17008e104e02f85082 112 cdbf93aa0ffba9f13c86f2ecdeb6b047395e3ff15dbe23e1d254574b68eb9c46 c3871a83e56e3c195c1047978c663de5a18004a5138d2f5b42a9d7a80afcfab5 76cff1938387fa6d9d3ebceda490ea9193ee2f59ba189180bd4229e7e817d3e7 735328ac66c7d1a7e88a50aa9ae38dd3bbd78fcee3330613be9c13d207cca6ba a81ea5de6a2fda8dfa7e067b33cf832e5d140890d28c5dd33419b6e9a31ef61f c0fcd7369505e19cfce144b5c9713db95d61568047b49d2dbb61f4022c76cb09 8f6c59225c20a745aa11631436128a7b7904264e73097842d18d9970842ab1b6 0d9009c71e90c3bca9020b6df76360cb9f58c02b0d93264e1f9e5ef85ba922f7 8fb55bcfc13f358badc8bf797c7acf4575871ea98deb763cf44a2282c0cf9b4c aa35f923c2f610ceddfad1921c4f431dd5c2a2dd422345f1f61e9792716758a7 271766d2b244ada93e59d93c9c605c4089be7fae0f398e0dbc5beb55299112ee 6f0e6dd66689bc5e23d95f1629aeb4c3225ac5e429bbfcd2a9349be9f1d0a7e1 b9b2ac85c3423b51fc75a5df0685afcbac065784443619a562d1ca2cfe3b7949 f4400f5b70027a99a7709009f8d397a1a8394be12e4ad0d6115753f6bc931bd6 e85e5615fa2707505313381714f9010bfee016603db7038c4bbf34499431e90e e04231e92967ab3ab266cd4a202778000b276a783cd0c6e5e3a475be02a57993 98724adf520ae09647e95424ba0196e022fc266c6c35e426ddefc4b2c60febb9 01ef22af7cc003636b343dfda1b90360bcce87d43520a05388afe2beef3c77e6 fa6d30385f087f397892cb2841cc0f655b634551510d0eaed26cfcfa53ea9597 661ce7be4ca4f6be68a27650ce6aec4823e9d0e62988799315f5a771d8a9441f fb5d081785e4efbc241c35d6c17d17cb4bfd352fd480db0800d515379d55289f 3d4f9d9934d86d5a2b2d1316fb996daec91caaf78e65a38d21a89d8853dfdc99 9e7e6a7f62816c329ad46b5caed6de82af043ed3c826c203163fd5410e62636b 481e9d516cd307acabc7089914a78ee4c4a5678e6542aa7185be852df03071d2 6952b4d9928e7d3e6456c3a15ef1feeae97d69060f7afad3aed93997b784dd09 7bf9fb1d83aa8759ef0b08e6b8edc0d127b6618007b608fc520b690ab2355fff 438b16e3464707664c10da9d96661a5f620ce3139422430de955c2e0dec89365 c224619af4c7615061840052bf98c62c9428892e6acff4aa92465b4a5ad398c4 e152167b08912235b55c802cb6774346e9a5e384f22ace4800e1eece5216b3fd 84ee9ebe5573f131b456a724f43b0b478220f58afc794888e792c9fe96994a47 c57630544b495cfc84482300f2404f962d0130dea3f258bea8ae27292d3afd4a 8b8a1ee4875cb41d3f0f0ef2240e5b06337627b45f539e475dd973ee05cd2357 adb7192536962f3202669840d0301f19fed9ba57c0314205a41dbcf9991f0062 9c5685a846d657b9052fd1b59bbb689066ba337663dd2aca326081f68256220a ce8ea6936294d537a49699da74743ef300d1b9c66bb96081d5a1266f2783a536 8c241551428249292b81f511a140741f2d6cc676a5c180b83c18bfd9f66abbc8 0dc95b43b65a785ca1f0ca0fa466004891cb8336bfd8136cf7ab824926e9afb2 758c48b622ff2685466dd52ace7c3a34f0979f9066056c5e1c7078c8c90db1e7 9f4364c1c5e1ea03e388a647a1c9f167eac316ed5b8e744ee7b85a2e8ed8d376 449fa4468270203bddac2ab8334c0aa4f32f6754cfbb9a6235e18ba582f63895 f33cf05d505b5cbde4098d217e686e51b7c79daedf018455d08eebf4689c339a de43dd36d37c0c6198108352b36293e62e42ee1ceca3b805b8deddf435c60ed4 a0021c84a7fd4b66e5c47794d67cd24a29c6c28c13ae71d9bd9e05b6dcfe66b4 f01ca48adfcde18f098d830689af2da08686343473e60328ddf82a2b88545152 10c313bb3af3ec837b5bc4e0b02d1423848ead68c3b97af5f150a6dcfbd7037f 686f078cd809be9fad88be67e24d8201fa48fde1f6eb370938e87596abc9a609 1f21188e8a0a9c46b20d206c487d958c7b6c2fd2783352790e8c2c86b56d774c cb161104ce6513e898bf4c0ac5d6a17bec2a361ad938a68f3b62046193e9618e ef538f7043a76ffda8b4b6f716a8c1affe4f9432bb7804b6edb17ca03bba0cac c2f99245690fe905d0c23612f5667a261c2436736f9ae8bbc0507e380028e353 c7873ecc99b4b1ced621af124ba89a44c9beba9ab924bfb5ae76aa76febca9d9 303f6ddb5c14946968ebc1cddd53ff35f28f40a35f28e9b7a4db2160eb1d14b5 e166012149087e6d6ce66e1d441ccd7ff5b84477f425dc8e667c7dbaa0a4413b 523d733875fee057d6f9982d9ff00101fd655f8ee21021aad40792afe05b31a1 394098a834f2b7903d53dd67704fec06073fd4f845ce8ca9ec7fede34b47dd02 b15780bb0615120e67e1509d991ee1e1c6e7d06d0685ff8529ee47db2e675b1e 92f47cd0580a75c475853069d00d5ec55b9b40d12a083f24a9f92a76e985c408 c6497291b61ae08dc147846d49ef3425454ae0f248c8193fb159d0d7791179fe 8017f96a5201f979e2604d5e53fb7fbb119a27efe08f72feb70118e7f285b063 bab3686c5d01cfa70650c9373e99da1b1d08d3d1afb3b99dc3fe6f520a2c6a3b d3510b055483cc2fa4fc45b8ac8e3d1a4bcfe13f09f43b92a9b23c8b9fe39f8a f94b022c8a4d7b51e158eb1f43b1aca7f7c1fc92509e0843f3619f728998d288 f9a258b1ba827bb37954de046391049c5b7353449741649a4eaa9f4cc87e429d 41e283f38992bef2e656947198a75571d8bcfd3ad05c5f0e17a36f49fb4d2d09 bfaf842cf948cb56a3f7eba910319ecbe8f3c1e7403882fd74e0bf72035a06c0 f0aae8b9df5b48f2f46c3fb1cf6cfb304db39dbd870606d048bde34642c5ce2a 8a30d2d9c233b33d55fbeab2aea96487eb8d4412a30a629c98b45f4b68caafe5 629015c9d70e3bb4fd0d0d13b4aab11be635779c8302d5a3b11346bf677d2ec7 8ce7774bbb8724d9750c54f2ec4bbac15c7cbcd30d897a7270fc0542554f02fa beae1b791a23d1757cac9684fe4c989bff6ee77f23d2c9fd083f2a4abc17fac8 1161d6e5b18cac8aa8dbf34dd224a7091573d3d2a8efd97e2deae7c46f1084e3 0df2def37b255e6e4f9c0b7645e84df3dd3bb77af0d3463b0d6c2060db6b12ba 0a6a99d241eeda76c30f7ccc7e097d5421ba4468ff3a9bf7b792f5931e039444 2fe6ca9f8159c1ed79e35bcad90bbbcd3055a566664546a7d72d74d347953a04 515dba3b69a4562c975b84560a8445ee945d52e73d9b736a5d4926e1010de3f4 18c92b2e4a5556a10170f1c9b288ec5edfd55df6113f6374591beac3b92dcfe5 8a716739dd5b669569d7f0e306dac867b267a76a5a49432304d7968843172723 9bbe1fad1b8c93119bcd60fef3aee466dce97f2058f25e247d2c7810d27b4797 74e77c46f4dbe0560b9ae3a83db82661045343a0a7b7f76b2b3ec7cacad6203e 071b07551d129d10be99444ca304e0efd427f013979eae970ff9fb92702e86f3 93047b0ce6c6aa2908afe342ae5af6e5616a311bd67c1fd2aab7354142bb72c4 4cabe5d01562c4da85ddf2b90e15755a25f54792d18e3828750054317db9753e a8de2993d3b253f1eed87ba722e5e0a14beb19a97d27142a86cadf3b8a06c4ad 58d5becc5f0a3cfe527342cfe9f7f135fff01fd20f8f1f5bda49ae52e5a0e3c6 d09f9a88ce5f8013ee686eec2952bec354a0a312d37907bb90c07e125e0aad19 732259c14ddefcc84dac2f7b62d4058d727ffa1d6c5254c84a3337bed8f3fec9 01ef840ca9553834e4735d9893906986ba8baa68b55f62c0aab4ec5d269bc06c 348a0d2f029b11981ddfb28b889c68c23c6bc6c4df3838ee904bd5289997e42a eaf77cee7f2ec57572f5e06828d56d28705c22274aa66121d35b3046af330427 df881b79ca26fcdcca21ed18065bec49fd76f5f7bac8c0378d23a86331283dd1 f7977dd71998b3e23c9a0ed34e236c13c038ef8e4337f7ffb48b3131780745fe 9b48184403c12b5965b4173146f2ac5352ac8a20ec1d621f7af6c8ee40ed6cd5 a91149403a3bbedbbaaa9ef3e7dcb721e9ad5116f6674af930f3e54a8ab27e45 ba853be8cb4573f489e5eb5ed9d0a46cd978c0b003f6817d949c8dd90de6246f e66ce6976c103dc73e15c5742bdff55f7529e35f6fc1faaf0962cddd373f2612 4abe1e8e6175dc0e6473773988923478079850f4a0f013c96294c954ec03c123 09f2e4604a9aa2c9a824964fd81638089a04a917037d6be591855597563fa763 4471241a5a68d68f142efeb16d6627413727f6d238aef85505d76e858ecd8f04 12ab22473848012ca769ebbf7e4133fcc15d7b759855ebd4ad2b1a40a437bd81 758ff0dd6f42c30dfc2bee7d95b9ebb2a2458850b1e8c800da615c860907eeb3 be287227e17ec44561e62a73624551f539e2be36f0857de0cc40529837d8abab 38555b2056570c493ca06b02fda86689b0e3154e0e394322fc78635880e222ec bb9a5ff60ef4719a6606f5b2d24875ffd77c74677d81d98f69968aa23da9bb90 ae6d58920374132b7ca832bdf7d0a063abf2cc94dba72f97b3389d623b9a5221 eff21e19c3b9fbaf05e0247148166c1ccee74aa80eb8b49a2b678f9accc63b26 87c9968bd1283a85199780d6cb093b31eef9a55d057608fd25a37823d9a97c8e ec11a151bced4ddcc6fdedb7416d6d71d50ed4d85549ddb9d06038cfdb704e62 87b0941ca7fdd82bbdcd319826c4e9c5f42d2295905ed6f2bffab53987a9887b 10a954d8a5eb7f5a46a2c36926d6732cbb58d63b1bf4c2983cb1a276327a8c2e 31878a867ed815a201109bda096fc3bdc984d51e789f3b94f6bbd6edfe200ea9 f5cb21e1f6b9a54642553bd14ab628843974a398944062461e21280534ab9b9e 25425c7d9ca6c8d30a38f70cf57a6a255582c873f064a5232c985fa16eae8584 9ec31401f1e24efdaddf70ad2abbb9515a8465e2677ae0f5045760acd7fa1f04c7d963139ef96bdc90a8e2c52d477a1d535ae035c9710682ba16060fdc4b680a8e570f6f2d74915eb6e1aafe27fdb0cb894aa3601b5968f22c6071e01abd0002439848fd2c56acef74b7f7f2f4091cbdfc26327a5448ff7319e8385b333d57017d64ed302d42d3a0cc9be5d7f19ea7cdcb16826f0dbf21bb5af3608f22e0ed021b8334e8c30447db8e4101eda1a38c295b1430f23e2970a7d3cf6b70879d7008bb0f5f599d60c9d83a7faf942325da714bd462400c3aa2c440d1c045bb901f05350b75ea1930c492ec7ae57ef896de47c669f2ac3b2ebc931ee6f5ef78d34e0020d9e62ced9a5eb127868e6eacc83ee15684c9f9036f20f3cbf08ec8f0bc7e02d80085707fe5695297864badc3f39d6fd3be8567ec9b60849f925c4bb390770f7b50a4152648ea49d601414959700868cc7b4995ae9cc0d4ce57c5f0ec33c00d2457748e061610cb9e501ccfde931dd1b1fc69162331887a2e85e30b02ed53035ac868a62bf3774e1b902b57beb783608cc9023d8506f3e6e2f6b1cb0148cd0932e4ff5636fb16408224c5cb41bb1b439f8d689ce666b9a9cf93a21c4989c20fc7d3c9545907c3eb0fdc2fbb49cf440c7b237b68c0fbe1113b7138943bfa9c0892317d8bccc35193821fa29b146266e9c89a6a32994e9c4bc1721ec92be1470b64bc4fcdcabecec5bb2701e13220f9c74e284102933e31c46e8e21eb4c53f504c1d1bda38233e69a219f09dac045e6eb42e28f829cf9693b69a09332c409210c021daefbd961c7cd0a3911501b3e22abb18286784157c6efeec2f8b1011eab09244c05fe5df2b8894c7c351d4844079f43564d0078f72dbe9319b049ad405302f9be6caca879bd421de24c09e922e9bb5ea7228754b8a09282c61e6050916a02a76f90053c837bf6098f28c99edd0f1bc0587f57f22e01ab85ebd0dbca82b30b086df7343143453db67e096908c71c357eb9e635b17b69eeb9c1b47c082e200c1a259a376086387f198d36b92ccc81768693313babf34eaa12ad46f9965db20de1061ee50b84da245346b7b5392a01dcccdd675c640a0c9dfbd410bb34b7a9029ea5aa2903f95e29e2b2c9821ed034bfc1d3c1b8819c402e784d7eac9090b10bcf45984ef71154c68a7064478fa3e9e8cc83a4a81c9c3158bba51a1b9a48500a0a46c3685326d6144e338f4b67f83e517d9e534609c0e4fdb6f5bc480796f500ed14b84128dab41857f8668b9abe5d238912f9261231efc575b9f0d2accdb802767921e0518cd665bb0392e2633b8fd858c3632c44e41f3d21f6b7db5a1a290173204c05cf92011e0d04f59f409ee92996c6b0ca7aea8ea2c4dd83b2806bf402bb36264aa832d487f8cb4ad1f0be5dbb5055abac6c1c22b5127b12d8cc9d660e5c4be95da26a879acfd707cfabe7b41c4f9f48781102022eb40992bb893bdd050a0ee670f8456a71ea45f310d73a1554b3c04d54eefd782d6856d201600b3f0866997445cbeb8ff3905ec1f5c0dca666fbe1d1fa929e272292c27461b0b2a60ba0c1237df790b0de6bfa13e2859a7648a337f5d04eb3f1cd919b3f35cfec5901ad555950ae94650a81baaf7e88e85a979149e1a894d4e0702df5544f8c3db70e66eeef9cc21ef95f447bd75376a521b5862bf2e07097a2da10235e94f2dcbc038de413755ca6ee87b15b3c4d0a3626b2febd32e5c708ca619a4b1731654ddc01d2e43978c7944a849f307de2447882273835cb2bb6e63c2181ac6554105e690981b3c8560af77080305051e79c41d6d2efdfa1679ed15f1b2bb22307c7ef840f6be3f39fc5f84d01832e003e393a6edec7dad8bf8de04a68b7dc693ff3d46d01d46f45ce1f5f998291fc71e0fd08932af2f1a409c78f18473d772ec7873d2e0229023bbc4d570e136db715e300b7c9cee285d201aa467a138becbb68126eb60bfadc4600383395513e5238d06ac58f2653415daa4fc3f03c7c28a3f86fb2e80ef0315486375cb7820840d10a9ff0136c0732d1972acf24d08af14d05e9859001003c1229894eea803c2f89cf69dda80936910e9a4106f274a0792202cbb99509db53926341118f21c253afdb854c689e0a07062cfb9cb2e39d3ba754a15d04001f8071454ef59f5edf4d65d72c783b6e9ee0d0d6eeb187405f4862a0049e1508884b31c3af27a98e41c971c8208f8a0849e1bffe27cb9d0ba971852e585e8c092b6443aec42f558c0778750cdc292b1b8b51ca5d0d9079a052c2df91168b6d062efe230628ba527021d776c27b4f95e23877056ef52e61435c98750b93e26b0cfa30f3155f82d52afaa1b92dd4945545df9ec6128eaa4296a15109b3d1fd5c0c1c114656ae11f6a18bf25e5a7c98ce9e2ba930defef1bc5e37fd77b231f9840ef97d0974267caa5e61d9e9d353fc3e9f9756c3c46e9cbe25837c5e6993ed000bfe0b8120760bc562c5e8b8f0011e96871a21e1c2ef196083359dc91e558a200ec7a995242182721c6637c875276154c217811ce084f01ad4792be453dc1bfd0b6f1b22d82ae0dc8364dfa5eda2adf5bf0193e9282864e083094bfda3235f020a961c3f0bc82a47bd10369150b47d7e2326eb82a2f0607e1f00746899cf8d160724ad9ca5ad76734d9d1f08e592439b8f1a937f57439af0cc6ab65b257cc3d00f72142d2f77c700d0c3817ee526a522f1beeb39b8f7e9fcdbb7e6b4538748870e6824447a930bcde7085b64ef3bd99f962214f42022dfb30bc276aceaef41c3001310a43db2818cfddfca4ca4862243ce44fbe2082d1b4a40c9ce9ee4d0b3f902a759adf1ef32e520e1972435be99cde496a9352d62c4db82408744bb763c4007ef48ceda479e5d3220f9d1276d1114f3ad0b7b2015a283d53c3bd9c52e515d05e4cd128f0e0331bd82bc0c942e2ec8b93ee46c00f61f5d82d9ce5f75f02db40089fd9af40094b2ab85c3869f43cca750b8afcf15fbfa3c35bb7c297e5a8a8c014e8da7a443c92f3a719cb1f8d19beb8e729afc79b505882fd1c2b95936b4800080e184a05259dd3720c1771862bafbc1af6084e5cfda148614dabf4d947902042dbdc9f59106a0a5c2119fa8475468729a8921294fbe3032ac69861c692363037e02bede0fc9ed155ff46a91c7eb3446620772e53afb943dd0a40d5ac96a8107274b0dbb7b170b0379f768f211736b18c2f80caae447ec0421976482df8dc80b8e18fe702138634ef454252f66c1b99866eeb0cb5e954d0e7c4da030221e560850d3582b95495b2ee0a49244432ee820e10448876e8cb78bfb783d3ef27cb1097f5eda3c7b0c81064a75b1d3281bd1ec0e3e7d89747c5d4b14c385f0379bfe049ab4a31858bb537fcdb5d17c383a4841b448267eb0dc50a4f7ff80ae1f6af605b923410f6aa8259f914d34d3afbeda0a97fd34cd8cc2021b56e41ac35396aa0c86837169dfbf6f6653d65099c2d4a1b91de0b1f90a822b91d9dd880aa27b88019904bf1ecd874764866a461506ac11bbc70fdc5f70a8d448b58287dfb606500391d6b3a05e5405e93332feafe2c30962e23348518a17fdee72da6305f59db205af91599a3f6d3a3a7936df6491b1b7892a38d9258bcb84c65cb3e5c641d1e4079867d804849de0ddc08372d6d51ff21609d157993b0d71d21958a548ca4a81098dd9d70463e7b81a4cea22f5e089f66119276a79c7e061908aa02a23b03a750a0e5553602b0557a5f4b8ee1cb3934df0cd5ea621c50df0fc52a7d8adf6dee30c796a2dd09a223f80d288e2bee3864ccd7fdea4f651825497aca8af2533835500a934f58b5e296f33633851ab8cd3515f2f5727fb12c6b071b503a32d7d0ac10173b12aafded05a77bf921558a27b150a7a7ef2aa443f936fe6474a94e4e3840fb7d29b2077bec4969b4d2d91a0ef276314e3afceb3a44d8d3d55a8bbb3beed0c57866ca554285f1c703fdf8d8d248e64e43aaaf8aa527727607fa50f8b1d9c00fa7dfedbae0e351b376d960a7f8c36d25806db4f8379a9e4eea9def85006d30398cea2f01d36049eebf28e2f5b1bafceddd228343a937f86501c00aa2f2c730531f006d20d405e3377ab1bae27ca918643d584c99b7063983433a098f3da810e3b9e02d574e3ae461842c0322ac16d0f618584c2eb790c58c46e03f1564a1f0df4eb2771b30b5daf2fdff2d99212327c1f2b3e8308d3abf19a4bafabf4a4e004110d019007fa5bd31a295f5e2be661bdd8efbc3c8b9f522a45f07c84bb67410be25a60990526b182b3e0b0d0bf0e4794916838b250d670141adbe22a777d6b0bd3ef1a45280063be63eaacd0f0ca44efcfc9431ac3d870d2b327d4cbe701c20506988deb35a5b59c154536bb4499e161cf416d95ee6c881dff42b47105426d03e5a3cfb9063bdbbdef4fc16ecfe9f553914007eb55e0fa2f26b216ce49c73c0d3eafb972a85ee25fc3a29c4746baff6766aac80affa12c7d53f573a2505dd80d544e0a74a99721ab171302f6f7e87bcf36a4d582b404c3d72750c2d64876fa0011ba0d01826e8561d002b7ce52ed3f316bd933593a921f31d35e7fa2f719d40760e11a3b3e2fe52fce6ca1a3cc632d446871e44793d63d59f1048d553e48810af0c2a4acbd1978847f965d74c95c343c9f0cc5a95ba5088bf817171cd868110376ac97352a1081382a4a7d5031ae7d17b7c94e3eb6730ca93e19440b9bdbcc07803424f8ad8c8b770218b1713105dbed7b0c73aa126ef8cc717feb4aee55ec0e5d15432b7fa973b1b5f8887a5e2a20f2faa6a577eefaa53852f0040f6ab45d037ef55ca82e456db44f334f31f44c42e83dbe007e44e275d11789895450b1ac0fa747439b807de178f140cbfd76c4e0c98887024e774c746b5a173beb152a96064add62ffb90bbf6aec27330d17088e7ef362b8fa57ca93697fa02adf02fec90998230763b8e7b2d728cba2aae1303ed5b841c4e0c6faadf43809d5bffc28e107df0722442eb60953d57fc78be8f74f3e88f732b9ac6c7193cff9f30630bf880730368d76f76434f209978a4ebefc67bd49e79d2aacf774724d3491f8ef5b5004ef5e2e8fca2403affd406a44ad07104edd55e3b979d67949dac2538f641312072dee0878b0323e1850b85550f96944226bd28e280d7537f4d2e1969f1d6df105f831b658879426ab2fbc6345086bcd5aab83fa792aa65f12b779a1d33e4b73099f14dea34aa5a89c2d83c5ad6057ad29373b93a7ebc3df18b21c0db0dcd9bf0e224d68cfeb15211ae8147e0d16bab6203d4174922820fc22ab23a49eba7d050b86b32a96cd77feadae4404cf11675d5c65cebd5d6a24c5a86063c127578b1c00794cfcc33ae3ca012dcaa30f930f05b890fc48166bba61162ba1663a29a0c30237d0c55ac0b5d85597d729e77236a95771480f584d4fcb05139fcc12c225ab0324763238fa02668a0831d6139e42fe8ec88b227a3714b62b14f874c94ce691093c7d34f718d28f7d21bb2e5da8fcfc7f351947751a7396784d51b7a30fcc01078686392124870dd4681d578e3794294592b04a00691c7027dc09d6d9c56ad403988b65d5b5afbc991c4c5d47d26a23f7d6a43262b6c6303e2c267b000e2d2506efe89b85f04564b0816f8e1904b03f4eb97e3c1bbb90ef6883ff4e296b847704616e213305c84cd2da8353ab817bda4f21594ee60ccf9ac9610f5729bcd6bf032e33084a50c30cd4d5043497c2af8c43e4f9ea82e837bf453f73946da25df80c39522793e325b56e0acd4d1f62fe875a8e2a1f28105f7739c2cde78e87a4aa0d4c9fc3fde477aa279597803811996f08d3f03556fa99c3c03814c66ecf60b30793e0a01e09cd38872adad18024ca6db492d3c11bb5cffb6af169afb59109c10577673e9b4f9f388f07fdc8fb82b6ef6c9a950a66549be9363710ade80c608908889eafbb6521dc66dc1550e568d1a57e8fd06f72a9d226ea947cee4952507f0bb9dcfa3acb2ed4913fb622478fae7a783a440b717c6760b23719234f1c400f0d95c6d44e8fb7ca87606021199d8b63ca91c1d7c86d5f701c384bce3d8645ff0fa58bc27bc72268ad16e11b7bb1cf4c03716fee096609e505baf592cdc19b020330f1f2354a7dc5b283c21d825c6ea5a4de8385e21c42818aaae572ab18d44f00acf7d95e33a02e51592b03f922174bc9d729611bb39629a67981fe3d28084a0e6e89a1b3d56f2e51b3733e147b12aafa5c933758dd50aee555269bdbb9ec370d757b257863e3f55307ed603ba1d9660cc30539185508fcbe35055d05cf19c70e40a5bec227b7015e65053fcbfdfc1200121cc912b900bdcddaa0ac57f919a30bf0540e1c38919d706a0d6c3dee76b4c279618445e68b6d3c7ddeffdd9f83c601ec22b2e79f07bab26f099a0461885877f6c8bde762e52ba3bc1c6004eab8450e66ad410bbaead1fbc1dc5a94a67ac32676a59d4c8059a75c97a5dec303bc4d0707ce36f04e29f87a9b535e252e714eab5b5723696f338fe37d040597a37fa30ed7c9471da6d879fa6957c4f18a49d0aa399679aa3b4d2452e5e8aa5b4dff320e8f8521a78fc31ef386f8beb5c29781366a14649dce68c19d87d2c2e134f8dd02cfe4dcec8a40a0cd18570442a3f34657d3045618383da4a0ea5c7bd18e2f950167e7740d41592fc42035882219341a8e3a3684717e50adc8a988c44045f381038deb266818a4670fbbd1522d78446f778f90d91eb1a12fc964712e4d3cff8b0a89584d5a1d890fd4610b0c06a70aa76f6a987fb19cd4fae63f3e0d00d020a70dbdb9250cdfb664d8dbe89c997a9c7e169df74455914424e4749bb1698a5ee10b8545ac73b0bb671c6148c34b2b099ac9f5f7e047cfeef0da7a1d058ab6ddfc0e175e3baf2f7c648db38a091d4362c2f6d7d420d0e507a3eae1d9dd25e22d58071933d4d07486a06590216411e7a341789855090d714bd8c5d7f03fdccb00560c79ca6aed5756ce23c6b38f85aafb4ce37c20e04bf6b4a7a04f05ecc2a3b3680640b491a6192d92c16c47dcdcd13e8ecc4412b4b09a381df08ce4b1041e0ada0eaf2d14a9f8ee1bd22baeca9179570a430c978f5761b7a8f77cfc6550b62b4305f052172b2dda7442f1f3c4cdf29e3a15d8e7d7ae48404d519416d7c32ef185036d8bb08057def58536a581702a3e9fc45ebaf9681a0629d99d295d2346a1a2085431549f29b0c22f74d5f95dd1aea34c8c04d12db82a18b5bf0424b71174b20e368195a66cac99a8ad596aebbc4bfae90e5284f0e403876598966e5e5608eb0a9be48cf0ef3434fc94fb45e9d61861ac11db5342b70a74c63abdb677e928a007f5bdd361935b80c49c7b11ee3129678c723747bda978d9f26e0e74c84b97680181a3e17492109614c1b390fc321982b6ac83d6b80bc4c582c8e1a3af4eea4d0ee609f1714bf2bb55831bef3faf12c0bfc53b93d870591cedce32ce6d8b709201d6f0c3667b7dc04cfa94a1d8c582831837faa974d931eed419984e155ce509021e1a7bbf3cf2f2f16f822e71fc5eb4032f41ae54ce9c167bac39a5db9f82390f9e5c405a2e82889ff7158d89055028961ea9683fe1dadfd1652cff277cefbf0b88dfae47c872e32a92c39206be4b3f8a50c43286a758219e3fa5a0ab0826e80a295a7614e413497b681bf8c317aa15d3e8baee42899fa926bc18630754bf7c091fef8108795b71b84dda8065dec4be1e50aa94278df6b632ff1dd33ede40480e0a74783412cb5254a8000ce5d551561f50cd1c9e4390e771388f916ae5944e03086b39ecedd94308a636abb99b4c8ae3ffd49620686d016803f8bfeeba75800dc915bca2d3fdb297e3ecdb91455985a36653472f85a6e93fe75d66b52e3869029b9f57b274ee477eb873bc59c2a48cb2350fd6f2a2671f4b9b14662d734da40aa29b7ec2866c565667ba87324766d47a88dff3f4eeb990eb98b5eec6b4e6340b5d23513b624dfde2b5520f1907ee0d7a11308aa06e25a44eb5edbfa6133b220fb015e0c22b32ecc29730430304390ad554f015a71efb9e59807d04625bf3100124d551cb012aaf5111772d3fad24ce5648b410faa91e8e1f14646922464e400061d8fc799813576c907b4053d1f1f4c7061ee37c38ca71aaf39dd976a57f5306e521614147827627ff865b5d892c26ffee3ad67466ea0f27b5f57c4286b74d0374d955df5ce1ceb8d6419819fcb2cb0880058eea738b9bb01734198b907e100500eae4bb70d96f234f496b798fcf412af2862412764e2044d3754f43819fa90064ede8a9aa6def215e5fed9f8fa800310cb77a98fcf3cd1f5cf3d2c091cac708a95500dd21eb7f72585048ae370122ebc6e5f92e215969e48e0007d069be9e002e05b31a5486b236260c76ef21aa3cd2c1606a378712c4e68f1403671fba0a0c567634a12687fe7c6aaa913db7460fcbb508acd91fe1e887ff72a4c357d5af0e052fb362cc827b0fb5037ab83c2a2e98cf1c2629f86359b4e22c7e2b67c94807e1925885327f96da46e179c8ee588a6d8a519584e8f923c54b3b4908fca7960a786bd2c3cc21618ae0fb4802070f50565225a43b1ed2f1b8dbb69ae82e5b8a0bab4144165c9ac430a44a42213e0eb492da92ad2230fdad84f710af244029b308c05f6f9443181e13e1e1c1016b353719d05dc556f32ac30c096f0e0366f8d70c68027ab51ff4aa955e5e8a7a0c8195355d8ba5677c72874c86dd2750758221061da9c53b5e07c5933b1ae6f284b6ee54843adc3c3f4301b84d864178cf1daf00328e316748ced894da086e411b7f4bfd48da447658baee56ca7dc19988748b0eac8b597ffb7a8123e9460755622d083d1e32be2026bed774e93151ef8f4b2c023df00426554e3ebcc7beacd6ce0c791d3ce8e73a644f4cb9aed75f36fbb0d502bc4acf9e126d59d6b5a652ebe443ced9be277fc07967ee36dfe9dd6674cf6208af9a23df3920ff465943ec1b7281944e258129253b8b0e39de2685d23801510b7dcf32b0a3ecf3a61ee68775f4b6888a403fd9a0b0a557d8cddc39bcd8ec46091c815b34976ee1d5d249e4ad78d930eeebb9d7c65341fc832075cc03f2bb1f0db392e63e9e4eeb95aef75456ead78440e1df5b1a6c55ad47386ad9ecb5b1a10dd20b20737f62e8a0baa6f37bca95ea6457375d6f818e639e749b4c20e987b701a9dc96eb806b5d81a669effdced9f68ba81c1347dc6db52dafb1c763ccf9bf037c9482951d308b49d0105357948e8901d0a15ca3fd84306f5daa416e28c4420227078e010ee5f77b58e498087d6c9b177cb54be0ee68fbb6b93853c32ec6350c5a52a097c596bf5f2bd7144c3c06b0dc5b778fdc3178ea020d2a062858e38c0379495dce5fdcbfc02518a5aa461d6a4168cd953f6785f17b05bb0ff83f7e1000ab30ba95129c33f1e41e920e1bb48d0b590ab2cf762023466ca4f08ded9db10ec579abded98dad6fe5aa1cc18264fb637e5e248e63e3c8438ba32ec7c6366a0a09839593fb746b4eacab641143743201794ea9c3008c944fa6231f29572f7104ba9243c59506cd159ea1457bcf5218c8d4424723feebf5cd7b680c36a8b26008d425fc3616006cba9b08b45ef99e3bd22d3c5dd9a254048461ad5e268353e802c0a85760343b846e8ea5c4c5680032d6a95712ac609ea493eb5ff750f8c1d302c0988079a34cec5e4b375eb2bc97c16a5dcd2dea7080e7a90ae5f00ae7df0308ea0b583d9af8a46a9989c22d9b13468fe5561e50e936d1b562821a56d41b9d059b71e5ae96cfbabea58de00eb2f863c900c4d4b2d3a34953046f00ff052c260e166b5d97baa534d59a3ae7269b4cd761227a59360c7a64def35d8782a94ece0623af5920f4acc173ac9b2c91019c1baf879e1ac6492d329298286d5e95183a0f7670a3dd1f4c2d6986d733e60fc4eb6eccd2a4cdd9e67595f4ebb9d04ba11a07c3b57435340e1e313d12dcd21f09fb867f095e7764c2d87ee526a8da5e8f1503e230d5493c973d5c39f0156596573b867d7419e3378fccbba247dff63c196204f6b51c150aff5872feb67671d3fae74f7296ce80afd0e51b0adab5ef9a52050b false +check_ring_signature 5bea122c711e080c3089625e527ff89f9f1f7987af25dd684e89a0b611d3b4d9 6ca30aac68fcb9f1b307ba1fa594903f41f19f1a804fc53cbbe6f352f471cb75 3 7f990cef3d850a3194bd161f6b14a49fe2c2a396241977e88d4883ee737659c1 bdfc7f1e98ce916e23fee214db23ab48866580b1c6cdf896e22a8b7f0cff0673 57f528be00b599bc1f63b1004e5049c12e75b314e79f3abd4d19652a89a1342c ea9cf6e664709c1104af555439a4dcff40b5f11299b0bf2e23ad4a5bdaa7b1007cdb3c274a9c8253511bcde89b3de6d6af1d5338611bf20b2b5330e84f9bc00466016450e377ba13940dae8c36b2074518d90314eb46c51ee9fea666751c5b00ef829dd48a089d7348ec5ab85bc0a9db1d4b62bb05f7b355c92956f9b02d9d0f19e99e4bfc000beca8b50d6f19ebb17d0e7b70da62724c3df2e0d74646fe150b6cc3d6ba2ad3425b1b82ab8aef65b9ede9bfa932515f0536ac6e84f16c089300 true +check_ring_signature 653593b631993f201e2edf2b83fb9d08bf5d309a54e9a0d80575d03c02279f9c 5a4d7249261cd55992213e00b5abfd8dc68ca60ad9786a9ab3478f8ea4c45afb 2 6945273372b284a9b2850618bda24c73d10745db6bdf7f167cfcedc7401f7a1f d7c0b508b471e940eb8b2df13904e7d99f73ea5b4c54e8232c7eedcb9713bc08 93a104ff663a67b81035a74a649ac587956a151348c58be59f7e03daa5f61f03b2f72bc7b648bc3b6493a9026ecc27dfc93dca2383bd1799df082c050aa7f9036cbe7b8de2f7cf1583d9ebe091168a03977a0ee8dc89e80c0306c5c76a3ca405b03eee9edf07497e35d84ed1aadabb2a8304eef86105251ac39d84f9e8260e05 true +check_ring_signature a0dc0ee24a0a3e6ea3815b52b669e0bac6a9177960ddc86aa709bdf59538edd6 8d964940635e87dbaba6daf58d54dd70637fa4e51f1d120bfc47353dbca7ee3d 3 448fedb7fade3c261bf804b2666f79bb117823c5990da7aa2090d63e441c99fa ba33f83cf144d59e3353c149b03d968740318e3bce69ddcd76bad7f21056f2be f34d0070b08aadf82e544d56fdd830520e336cce8d471e6094fff7618a80d4ea 1520c89e5a4e8c3ac0cfb3c30a3889b77a484303177c66ea3a011cf144c4430379008230d5d46fc1c23e93396f13e225c82ea88263c6ad91cd771406b733bb055c4782fc2429af6ca6abc03dec7e754da63d84496793d024f70c6b93da10cd0d966647561e5b872ffa0c9f9ec1cdfcfc6a64423c04ce590268a7d83681b27003f3fe96a8dd8e856b416f5dac2ed7e31e49f51db6dd00f87d2a9ed47adb24a406c7681419da43354a57467258711c093fe91cb01e5d48a3270f00f801a0199e0e true +check_ring_signature 68ca3473529495471de92b8488c20126be4781431ab03083645dfe6317e32e4e 4d24e8bd8a35bcab82830c00132b4fc4e15fd840fdb9e51019bfee0ccf770788 15 d3419825bee379ef19cfb2bc92d466c59e08c634fe573a3a892c2508c123bc2b e7385d8a19ae36e807383959a15230828dd2032b149e404c22726d1631fc3924 cfcd99ffe07df462d29a6636a11942b0b479e0823941911b328faa4434e20c22 1c7628aa1ba18db9333e986830781d901429f0972d38cd8f646aa8ec45e6b4fa adfca30b7403eb6f231df2d9074eb68ff481ba9b422ae0d81e2cd041ec617956 499fd33eb4b3e5e94beaf20f7b98ff53a00b131d891d01647b49f91ac967a9c7 5c28074abe3c1dddc8393683acf4715f6dc011aa30cd82ea59faf35872fdae48 797918e6d942ec5d11593882ecd44043eec3e442983ad3d7b8822849d24b75a1 81eab810a02a3d07fb0802b0b2a61171f57ced1da485c8f90c04c118ad76f711 e46dd998a7974be80be1ae7f633154e5ddb0202ad49a1d50422626b3469e55da 27e23f29e629c6ea71fd32c0ce5da7fd2cd8969d1c59e205ed2d9a46eb6b625d 226ad9a94484efdb4d49a3d150724eeba714dbd2d8d938a16179c9f1cd820a0b 2775f47bbd4a82443d40085412958e46d4fe15797956b1411a55d7b12e296def 2851b3f39b400c60db3f09ea237f28131147613df0a98e4f84626e044fe2e88c 2608f6a63eb6d7e8a581d5ea8746809f2a32371e6bc0c11e6ed36ecc2dcca8cb 7103419e7c5fd7111e6dc36cb8d07fc6171e4f1ca0bcc7c03bce534f437eb1044a7976c555c0d1e3759b4a6d58dac8aad5b5e4753f594b6c42faaf6a01da2605c872b30d87f94847fe28cdaaf2f772fad301cb30101483417c82d39aad7e7b0ede83241c23689cd060c25889a26bb938c45869bde1118f90a36e59184393680010da881e80a6deeb097756fa08d7149fe12a1ef90b13a65269aefd22e30a46039c6abb3539560e79baced53e17b612328ff9b6b49b73f975f68ea998ba86940447ce9a3a6177df0c49a5754dffc47c3f6664b13e58a77f73a5c657b8481849061c89223af75b99af05679ffd51f73550b46d9c87ffa610857e23f93a71a8840e1e519080371e422c9b0bdab9617898069a0151652d146f74386463743f5f0c00cbdffec13704446aa1b3cfbeeced789ea8a17ef59aae84ed9892b0bc63737005f5b2b4a1120908cdb55eaa6b3ea21563de4e34f4b529734a915779b01d3ac203705a0c8ae04aa9014aceea47b685ee91538161ca17eb8fc728c08a9f35580e0ebf9332a7bba9d2eecaee550d035e514b5731288a4780fd54be9004e8a176d500ebf5ee6f6c79abc622cb007e90f75b3fb7b13ed12e9aeeeb4eefbc3b6122690ebfc39fcd1587fce0e9ad6f2564cc8ab196fd05484c2e2d38b263f1484f2c6a0e72c9eefa42fd94968697c097f28f67238330b55075fb973cee14804995e7a3074d20433bbb12df6321c1f438ce368136de77b4f305f6db6f4c757e4d2aa07a0a198ce21d351de1d21d8f6b514620a32ce0499818fd37fe490071c1c35ce9b302c89bd82d6b3af01247a0adf3960bcbea19195950d4c92a696adc73b1eb3a040accc29a9936eb84531d173b567b0eb4e46e617e5bcd394bd739742f54d6ba6303265827315dcb458fe03a689e61cc97b2eff21b40df86a43e52527deec384b7082c11c3e8a550d1e0487efd0042ec1a379cbdfe604d989a2c20b859c4f45be3009b1e457ebd843d2e1e51e66d3ebcf8ace53aaf79abbf859b504255dff75389086aa209205a4d4b40b4b1b9f10c057ecaf93714ed7439ae0063fcd3c16d2c430fb2e4476d81b743d3a74d1398139556b74463b63d10e3bf18ec63bfcd2b4b3f0956432604ef79422b4bcbff3d546b8148899b1fdfa1a802687bce402c30674e01c9593e84b6e1985bad7b01f215b77cd6af6f088a01384b4d536bfba618e992009d9fb6f12e7b238991e5d8cf429a04ae4725e9c8840908324dc4cb47f2c44e07a7a5c283c606f46fb7c35daf90d4a31ccf627a32be1acb0a6fd3b6a80186e00d4dbb699acf2ad2cea5e3204523aff3ebdfae092c39c97415469e60efad21e802 true +check_ring_signature a2f6d8be0a96769b4244d83454211f7e4cbc8ae6eef71434ac0d40828b571cdc 2990dfd8264e280eada68a88f5273cd73e91a4a59faa9bb4cc4f18054fcf54fe 3 6c9762770e58bca83c0cd0f4fadf65a1672f4fa901c5ab0aa367b6abb89bf148 d219c911ca6bf02dc8fa7815fbdca36ff440647e484554fe1edaf4c456c96d75 aabb44084fc7f80c4f107248d5824c73576831f3268f37f5f06cca0495681a00 34ed8c09e611a3a5cbef872b2205cad56371b5cfd2e4a61bf98f2091c9e32d0077e6f75124954d82c65d4ab4b49e42400796304d27e10e1f5755ac8b551fd902c3037c706b76b207eb300e1ed4fc5c2d4b8126bdaaa750dcd6a242718892b700ee1985e5f07f129ad3ab4163b46d3eb8508a9d6bc92dde5095ff6fce95d4e50917707923f388b7bd429fbfb338fc59d9132a820e7a75aba284eb550eb502540bf7d5a69c0075a58a0b6b687cd5c8b9be81efd3ee13b03bc85ac6a1c164e05b0a true +check_ring_signature 6d462c32d6c91f498ea53c5d0ed012bce4b76a322ec485fd2fe01b39dd4e0acb 2163d0d923dd707809aa77f4501ecfbfaa97552e5e688b56404d18fcc895d8d6 14 feb3b7a1e2d6b7420b1b74401f1ce127120ece6953aa1b7dc9d365298540707a 1891d4ff04064bdb5cff5f612916a39469df7fc2457d98f119650777edd19e19 de795e8eb1cfd62a021f98c48e0a6dc4fc1e3120c2f4dbfbc0c9c0e5fe2a7d5c 91379ee6ed8740472c546badda206bad0f5740b1a29ff8c3090eeba4a022501f d9a57fc6a8a76c89aa6033f239ff02b39857991a8c4be84252c441d91eac1384 49b688753c4cccb61696a01d5a18c8a2bc57604c25a35fcf0fc87cb09483256d 6688b5422a53c06921cc1382e3b7091a6e761a18071cfa4ed8765af8b9e8cd6a 240c95822e145a7417ada52aa440e7302887c21378c0bb09d6fea302ac24dec5 9b9c9ad12e31f03e47db2cfe788d3c4df5481ccd1115ca08d8bea7f824bab08e d2eb5c6696b77eec8a001b0fb83ae5ed894c21b3fd6053ddc9fac5e6bcb7eda0 772b34c082af5b750c3d41141e27969c8f7a1818f4f17f9659a8e45936093bd3 c27e0e83663988be2c4fcb7500017a9e85ef58833b95ed5881c6a0b450bc33cd e2e75bc88c336d8131044031cc076dd8be3d678999a4a740738a2db3ed42f7d3 f2b6bccf2584d28aee8c38f33428e4f5466eb5f4ecb7a46ad418a86c68a9f8a5 85763389fc2d11befc74348d4d3d7e1df7a27a12713e59db7d8272f27572160f9af51036f9b880420000ddc34d11407950df0ee670d88a7108a9aff1ccdad30a39a6a9ceafca56f59277ab9e99eb1d9465f0db8beb669eb0e72fe05a812845087c6d3d5f7c5cf5dddb5f35466f583be136096071393672e33f9fff4279d755097c64d19e56e276fc999a60d78d7a19197ac10c9f9d3236096bf0de060713d7075260aa6f17aa9d80c8212ae09ae0cd4a514c10b08e42201a1eca3bff03a1380b06450a1e838faa26b7d52b842775926f4fc71f98c5c6629159b5e018fb04120f631d0c67aa04d69e1da1229d08dea845a03c34e31d2c73a18ac4fdd7c0ef2e054c9a171aa545f164bb289cd10a7b6107e74b461d9ad581b1640aafbf4fac68044dfe37f80e013656fa4d7fdd0b85e5183ab222700e560f1fa097b5ea1417e707ac3e8095d12565741fb8e6ab9f4dd4280cb48e8c00b98023417b38f368fb8e0be854a08251d446d1e516e9d781b15566fd2b8572424df49aeca627a1f590c2036bf280288a20516cc609dbf89ffe2d5abd89cc188fa3958d022a382758f49202d33cad05b253d16ae283e525a8c741212b729704f04af3fb18859bdb7e97590fdc6c6377b85e866b06867b6a8727694dd24bdad08e5ef90c77fec5520bcc11060b5abbc9655aafbaae08f4d1aaefb2d9b3f2ebdec4baff7fcc777b12b569de07b06675ee60e3634eb3812d076b6a8c38133433bd4e3d6a6c0de1b7328d76b40fcf8999e1bd767de796f1fa5b265ec6633fdafd62a2532083f42fa96649a9130e5de492a2bd8d087dc87135627bad8c8eda5bb1449c8984b6493146f7afdfec030140396061891d4003885c6cabaaad3954509d8010825ccc7640103c5286650049473e6eb1be44c378d8e08715b143f5245a0173f6c879367d5a65d670472c00caa4519bb37dd0d63803d9befd59c13b96546611b2ed6cfe2309c199dc18ec0529f7f4c12b81a94e3f00538f8575d422d4fd4adf97e15f2dfcda33016b1a37027132b5e4f0c810f24324699b6bf2ec947979cc62d292fbdd1421200984ea930165c6a9c42dbfcbc9f0109420a6c668795b5d629d6274273ff20ff3c263ac3c08e526482ade85dcdfb8aa3139d3683a79a7762fe110935d0f28d7d3d649f85b04870ccb4608702d0a6f4e90d7023ebe681d80c1b73586ca4afb7573b1f906180ad282e3e970e668e0dea85413bb836a52b5e4f11f914acbfdd8b491e056287b0c false +check_ring_signature fa8b12fed5e7868f50bda46bfbfac91cb416db813cb508085cf27d5e56a226fb 3f6f622abc13f6267e2d70eb19051368d2be39e3c0e91493db85d334e22ca9de 1 705721b751f7b70d6e18474bd121dd974bddc704596674e05361eadcc041076a 333ef42256b3643f23a173056bcbdd4e1db7cf24daa7ba0ce05cd52a8aac1d0631b130856f65e64306aa9d692404258383e66d5eb715ecffb107c4b85f02400c false +check_ring_signature b822d22ff11070fb2d7966c83cd5aa6f0fbbbb597422ca5c0d7f7fee9f3347b0 b233edef24e3f0d733cd724cf60942b16059be1092b463a6c6899fe7ee701b2d 45 8dfb839b9d1fbb7523bbdf66a178281e047d47b7369d097a61f8b179d6da419c a50994febcda5158ba73121b94a7794f5c5357c28a42102e7121a769ba4b9d6b a2632088458156be9bafa31cffc60de0c667e680ee9c2392987408705e80b834 78b175d320ce67df3b64a837bcd00ce0e7e49f94d5614a0e4dfc50df7462a6fa bb3a44a30685cd3bdd31111a43f2aeda902e63844148ecde653e85e269dc2ef0 f8a1ee346873e14f0880523a9e34bb1c3cf8effc2e018f0ad453a18cbff7f01a b0d51f986a2f311fa53019d14023242ba68398eb4a14fa2f82ba7a8fa7d07a2e 80bdd66328f269dc61b2d557c202b5478b285549eeed206a8cfa02d29198206b 70aa7f89b05c619f773815fbc75f7bf8db24fdd27574dd258406015a31f3f6a8 a8434670c2922c49b418e72fd8609ed712f97523fdf66ca528a04f279e36a69d cdb082a940aa0d08734f55ea7773cf8f2a4ab07c9d22ba8cb8124cd330d2e9fb 27495fa93b3e36db39e7d4af30652b5c413e070bd449646aa447e77e14b08a38 580f3ea4132a089d58b660a3b72cc5bff9de5dabe025ec6283b3d0fdbd442a85 3c013c9a4173a070533a09e20f85f651945346ddb0748b74b7309ad22b2f24f8 905e19aa3a6ca82e44944037ec18229faf8651e5b38fd1cebf76d3cda4624d54 f1341f65f521cd02bca493599b8381f258b82df0aced376533abe996748f91fa 08e42a8b0df0ec257df9ef2c48b2bddbd3287716a5dc42da5530d8f886f19be8 bbe0193a29a228572c00195f1aa4bd72b73aa8b2735aba8fa1203b21fa81663f 5015edd03c1f329fdca5cf924677784e00cf1501329c848c1f93854edab15214 04e6b98892cc6a155b53367326206473a10fb6b6f2edf6ee706cc5f49a96c6a2 3f93b1b5c1d8627ce650b6a85f5c8cd20a88a90b33f9f9abe108003753f193b3 fd5391689bf1d96eb8337a21b2d027a0bce13c650eed442bb2693031243f586f 1ec6dde27a28ca5fb4cbe86bcacbdf365c2a9089d8a0b932a344d9d92b6c6d5a 14cc9f5a2d7a5a993563a6ffc1535723a2fbd16eeec6be64ce506e3e6cf36855 4e1f38e5e5dff71c6f70c6bcf40f5459f52e3d222de2e0bf3685e38983735988 107c979f5decb82b3d307237b683c781c4e1127b495d0ecccf87d92a9531113e 4a23c239247ed5f859009a25f9f9829695b9699bfd406ee85600a6e1355cf75b 0e4e8789fee665160f06353a797116ef7e72d64d83c923b9001b13f3d7133db7 03f19758f1df48308e3b7d088f4d87b677e8b33df72e0228d1c541b89340c029 7f2bce187091a1952113cd2ce903686c421ca89f7149a25f80d1cb835c489769 697eb1d5cceaf48822cc5894d38bfd71e72483fafc9a719f7526b60137e2ffa8 9ed15d2240ea98ac072be3673d882ad47a4f05167f5d0b5153812f64e0d61c1e a9baf44a2a5bb8fc48cb874e08b00f1716be71c18bce3c38eb8aeb21228199b7 cc5850a33b5602cd777b620db2b93dab9f48d18e17b561c0b1d7a5771a514d22 f6bd68187a039d4d9fdd5fd178c35ff49feb0f38fa13d8da3d2eb1eb1ad15fbb 0c2c33e63b2db4b7b5a881aa2fc1450b16bb7ca8303819a05e762c1489bb7c35 ea4b9fc7eb3c67eaf60fc35076c6ae2283c7b36884b4a420ca3dfe150ec66195 b9e31a451670840bc6834b439391e256aae3e0e79fa0c4439179f835acf7cf3a 144924afb655e0e9cfe96280a90fd175a21bd9fc29b5ca3e43512aed5309750d 6cda1a0eb777c8897a94bfc88581a98a74bec5b34f3e015b0faf400b34b0d207 1f1ae6f3f98a87eb8413faf8e653e42cccb7732a2db63d0072f9520797747011 d601d1f311252a73a18f902796fa20742dac1fa48b8123b5161fa7edec321abb 8fe1390ead04f479867eb23767ec69e73ad7d7e1dc71fcd551f5e88b4020448f 8637e010e6ba9dcb8f57770e551e30111b3e73d999c2e94c556084556c62056d 19f278c9f556da8126ee0bdd718eacdfa306752fb0bb02e09b442c62e80fa0d8 0ee7f0ea73431dfb451bf45adab89f57f292e4fe5b1a130f6b599fbe2808fd03a74eff39fbf76be727b439c57d5d800b529e7f6dfea092875b5b86b96e33ae065f463d5a8210f8940dbe173f3a31b1cef13d258f2e0895ad6407f47d7e011506c27137da2e735863993375b9cf82de13407cb1bc95bc24cb08a913468c5631d9461eaf8244c914beaa3609abad7ed7d63b05542968035fc544e0496fd41aed01089738149b5e6782036932398361b3724e196e168188d2ee4f929a2f7b5efb0a52bc1ee260a5259592d9625dd196fdd24d08201a5bcf76411c332e6761110702b4f7bf2b12389bb906c9de632ca693496c3bf934d410142ce4e48440bb73510fc7619fc438a19633da4e85c346a37efaae7470a8f98ecef3ccb42f9e5703bf0fba2a4d10661317ed35cb350732578e164a143107fd238c6aecac3f799e4fad04e3bc10ebe1aa99005fc23a57d44c70ef0957c38a6f7e50ec1365834114f0c70ab8894fccdd385855e2f3135acf07813f37bdf718ada407c34fec196974b6ac0f51b9997bf6edadf968354ef5615f667f3239db4edb2b20b832c27f75266f8b0df24e74235c0de39687297eced01e117ef34c52d70c4c28d61af5f749c0035c03ee7fb8031bfc15eb6b40559df5c70242ccd0a6006e7b8b3ec7aa48391450760f6c11d0e07831cbf123cfe4fb875b5ab529e977952be4fb54903a1b869d105e05cfc886f08c2e8adcee565c6634a8a6141b5eb40fe21dbffd20792915fb9842036ca818357fd02edf564d573a3f2ca2833aba9be19fc5c4527454842c64b8bf0845575d3b24fc4e7c95355724b446275468cdc699482aefd784be5968bc7c850c004e42021235e21cfa1ca0e1c981d1ec7bb7c3fe0784c91729f975315ffac60dc9577894b608eaba8dd1a7f55d4948bb2d78f5c1046283611ff89ee746797f0ce524870028c3095f67f12ae8ea6d1453449ac28f79e8c164984fba2ad8ad470348ba8d0725f4807ad82b4834029a2d704699850e2b7dbb7e9f7ff7fcea23c60978c525f4fedf16c583119b162d2f2d32050e47d94928de82d38d28424583090a0c06426afb0ede79130dcb91c5b3d7f072400a31bad1d3afe5a048515372a602d97df6aa496587aaf291c60d335c6dd3a96f98a22b355e3aaf4984cd866dff0bd9a3b2cb069521633dcc09a7903d73827c999902f2a18adfc0a766c272412d05c01e696f55a318b37e62ba680170d6635c03c94ae3140842608d9fe65487a00a5dde1741f866bd8d5c4b1bd6e1f440d811427c3ecfc975f664243c06fb2dd00abb0f2a55d53e492478f4d2bc84ebcde49a554f867e5ba72a45bf8eb328d84803c3376d8ac461195f39a86859572f91ad19ea6724d14ec0f99d5aac2d2447b7029b169b63b199d3e8b283af73c6f5ecc981459f771dd7985d5da52350a4041e0a050da48f40df9b941d6251748d604a89b2bb5c326ac176bdcabd70b09209fa045601a2890bed3c03188210da474b9d45c7dddc7c4bfb039428afb065aed2460aa2b335afcb2c0e0815a78d3ef0d311b9c0f786c5b1a58c53a2837aad516b4803f4b988f83f598eb6f934d3008029677e34aa517fde7513732a3c968509dbe504b1abd7835a09d9f8f7d03e0a23b8d646ba02d3031ca6805b112d1ff6ccbdb10c48d3bac14f21e1579be9612488c34e52343259b2de7d52e205d5b28edeb16a03f23565171d6f57bcccfc25ab4f5dadc1bffcfd47e19e281b1b99deb102007f007dc96d79658abcb5fd0acdacea9f4008b95ec8feac4cc2b31d6fc2617ab3800663ce9e5231769612c107a9d2291d9f37893be8fbdaff2fffb3105c13bfa03701bb0825ba1fa98b10b665e808fc598b40a6d1a7221010cec72dda7167b17eae05e297147d4f407581807340277613222cbff8af550d267393920e64ed13f1480f88049476b7d7d747e58c27da2349cf7738893b5b1b27eef818771444f4323c070cffa03fc9322d149aaceabc55c2f9e48ff0b87e412c8d8ab5adec7d26369d0cc4757b303f596643b5d7f04c193d25991ac5f54edaef4bc5e90c6670b731700ece60dbc4963c49cdfe994a3b380563b2a792f783ac40c4b0320e72d0fd1e460c2176f808c8e69d7e9fb79ef880f398a29f853db6baa6db750d8c0c8e3aa20b0f3610eabffd540360d3e1cfcc2216d79f3296c67c9244efb9d752b9cff6cff7059b8587417b66792848dbc5f2dba5372a50a4fdaf7ed1d86bf16cacb956bc1d069926e8f8f9960d2cafe0541188aa45aec873c291fc78a8345fd2e7d922a4270ce44e7467cf66d9dc8ad57e7f72bf29bb1e70d2ec5f371118dc6fd42e4d989901ed85db2189106369865f5179ed76e1c77554f7889195e64986a4ed7c978dde0ec938cede8e5993c264d9af8b9d7809573501c3f420926e421277c0b78b53a108bbae36b8e1884a354c538ca31e748b67079227276546ed89ae5e1db7fdb72500f85949ebe060023f81048de29e4df0a9b3bdb7575e23865cdc1284548bdc3b065ca425e807ee797cb1bf707e8d0bece8ac781f13d4fab96eb2ae41e25eb334076e67f0aea8c7a17f50c1375ecd7811db6835a153a431a5ab1a3138439ed420052ccc01c4cca89c878e7828ec3ac8014ddac2a5e3cddb5a052629cf550402470b111e612b6ef515730fad8da1fbc7c7c600778ea98bf5fe401e1f3e8bd7640500d890e181871145e42f65aa03c63149a67108367161f326fb53c47fd47c96e10fc0bcd1b7f2a29239e987abfbddfaac66724747a0954bfdf1597f46c2127a320ebaaa34ecda23edcdd1bff941a8cb2a1b86e825a49b877f2ec7f66b991224da0059d00712adeecdb3d0ae5a2c4b0ec4710ffd9813af126b3b4ba69b455cf792048b4f9f2e1a451c3abadaf99d961e55560a4b1dfe72d87e26974ec6bcc6d5f305c3e031c762cc65288b00d0f669ff022dceeb274f4e4c97719a87b83188a6ff0f6027993afcf6413e3f783ac574b10cb047775cf7649ad8719cd9c80312f0ca057b2872435fe452d92ee6bd6466899fe41c3fefa9067d48926772a9a245556a01eb18a888ad764e3042414b7ad08504d533d11ecaf4050c54db8bb2b9cfcdf2093975be980bfab39b1a6781308a1bbdc805fddac8e4233512a81e21152ea27302852ff10dd7b0f7cd41f9b47e9ab043148b9499394567406c26bfab2895d8a8072087f73f5ec4ecc0b196900975178a2f756c99846fdc286ac11bf901e1d9bc06212d8c2bbd8beecc146bbc1d1759550774d70da99edc1b5a388419753464940349c05186156194dcd4da5470e3d5b0f1a6ec481f5fc748e02a3408268ed3e30377f84ae74008727371420e1e6abee24e219225b25290ce828f52e580727d0f0a1ffd1469cc353a9908993d381cb3be4fe8e42feb88cf69cd6dc65dab0e7c130ad27f01a49d54f53ec0c3317a92a015232ab2f27532f99c5160a0b9abeb6a4a00a1046fea4fa7ea125ae373f8b585f99ddaed6ab19193b53c4786b6a1c0165508c6ff94b0625923188f4281d7309e98f613c1ee79391a35f4caacdbf39e9e790e1edb32f19ade6e17a001958e2699544de760040dd36f1c5332961229e16b8b0f88e3ddc789a33b6968b8a5cd1081f3f1fba20ce31f6c6c34b0a2c7ab71e9a206027cc8b5655dc1ca345eace17a9a4c9feb142080bc78d42c19966fb643790603c188bd77b56e8b4acc5bef05da0f230bea0617ea6aabff5f8f5b13b6b131990e5491fb14d83eca4770ee4bd803f7f7e554fdde0f7926a1b3d9240f6f4eeac50310f8fa17d299b880e42fce42af6f1c31a9ca0ef724426fdc97a3ec2afb72b4089438ad39aac681c808adc86ae0d968a1038c58c7ea589f2c5645d12f2b1efe00964bbf6c3ab266e0af81e97708dd65823eed90ec6c369c00beec2cb0d29d9505501ebf0a6dc16f7350ba7faf89c423a60ac5410f8caa99ff5f23e2dba66ae40ec06d1bf98a30f6da34cb676f4f8cc404ae1cb93428bb66617a2d9c2347898609e4fad50dc20fe774e1f3fd0ac3f52ac4d2ae794e07911f40a5629a54d29e6c02 false +check_ring_signature 78ec09cb0fdabf900ddf19c9f92caabcbd7f6d487b48b92a0c73b211218cfb2a e8cb3a54fadad5c7e3c4963f5714608a2312be84a9bb8bb36f642706447840a8 54 70260f45eb56c1eed21869ab21ea6dab0a8abe5bbd63b258f5907b9d1174fbf1 186a6322b02828e7f04e70404c5838f5466eed89d11a0b84cc5ed68d379713b0 6d79abc8243832d3872180239b996fd291ef516bbdfca15d9c3c05f6300d338d 963eb0ab32ffd0f6e9e3a876bf6cc6aa846e3aeaa1e569c5ddc0c3da767a5715 9150f7d3945d9f3d1d732c0c9549566456518135825802165c4e7b1e6903cd71 b9006dacc6be664fe2f6525a142ad4ac58d6a2ed25ddd96e05e389ac4d83a368 4916801f97ff9c030d3cd4a1c84c04e4c22913174558044c610a1982f5871f31 022723ddddb3d5f1efa7548b809ad8d660ac6f1f93a8f866ce1f71b4e12d1a6e b3d956436aa8f602703b9377d98d0b21195cb2bb85ec37a16ab01c5bb57dc03b f2bf9d821c51e0e8ecab83de5a5134d7bdf4287a1784f1d292e2b7473884341f a3cff537ec2729d43f7b8d12e6409a29c428f756b1dd1f5dba30f96ee1948d56 207b5a179faba0fe6c0eac0b82881463f1023c4385985e443de78d30f40cd07c 4d0000e196f2ceddab76c15c133cace7d03f1fb807289f7d644f663ac0fa5d63 9379121109507cd3e007fe2674acc601aa6852a788e627c91c246dcb73f517ce a4563dedd9b3d437a2faad29459a402a5cac88491d732ca3ab624985d6bd3fc2 9bfb0a9b5a7a468a9ba7b63c962091bbcea4f7b89464a25c192e3c757c3b2a15 0df4bceaeb5e3be0beb540267ba198ad7847e141e69b12bb8d21db11e3289317 c7d262867aab291520e1cb9acc81e12a9a9c7d751a3de568030511b17bbe5970 cf9eb5662f2c60bca4cba276098009a730b6cef77985a8712f7043a055f3aa2d 1e503b838ca4b0a7f0be11fa11f15c97d3736705b8f9daeff23f198d51585154 71ccd72eb9eb6617ef69dda0c97473821c58152fc93405530b0244c214b82316 ebd0fb349223a67551d7943b14b711e914d57b55ae942e99ba6d078b6d7f714d 27587375ab9873399087d9255db534337f2fe2a551fe2f0d43608d93c071e03c 36daf227bfe6b3452a8b270807dd2efe9de850bedd521926d82388e5150e3860 36927054c3d6a040e42f0af6dcb710ecbd8ab1b40c2a7eed0248e22987808c5b 7090e8724881b7fac995129ffa66d5f3472da4f710e7c03d328a033f434794f9 4baedff70ec009a4cdd485994f5190b20e704c5c36d4c0d81c45a2f41de42cd0 c139b8b536ce3fbaf071962c893ddb26812ecb9c9d9c623799a2c603aa7f4c7d 6e3a796567a0ba3a8a59f6528d1dbed471617b75fecf4b330d42c528157b3f72 1e4c6aac8be99bdce077f1d6fa7fb3ebdbc74eef074213f9bf4b6f4ca10cd019 c5c01969e9d0816c3981aea7a594b96ebc2ca09483eb52723ae3018023f45727 653abc8a07ded8106aa96f2fd78c630da922ca4bc5e7848ea8bfe9ae73c21466 b454dc3fe1ebe630826381c30d5ad5db9a336dbb680e2e999ca17facbf0a2e64 e0faf79d8f51362007446bcad4b7f5f982cba9ce065415ff88fbc896249506d0 92d2aac56f667a2975925005eb6f803e02f2cddd3b57a1d75e649a9951cd2282 ba29fd12bd6e77b53f08620d199104ee940883a78dc0510bfc1c2536090753b7 cd6404a0532427b9e3fb1967a7813415c18e2b9bf9cd215e45690645aa0237c2 a3e04ce1afd76e79a2ec30b23695d00b4cf45c1066020dcd663fdb66c327fbe8 7f1a7327323233b400bf3366269064c132aaa6b24c2e035cf0fd7d9860903edd 0281e47225893f81e3f307b6e5bad2617fc59733aceea868b9a10935652e3aef 3a85491211872e86cfc62847da518b01682143178778fecbb82a0a9c970f27e7 2a2ec03d78bdcc17aa0979d415fcd721475479c606acdb847332606759b9e802 da1656abb4b5719d5bf73747e9bf4b3b852fef6b3f44b9308e9fa57bf67ca061 faeead07be87b8e497f32ed4c9cc58ef3d148db2698e468c4116718cd0fccc41 a789f750dcc52c6acb6dc30962609411edf90b59d61e3898a0554ebca1bc1b6d 7ffd7d78b7301384482fb129a017ad1bfdd6072998ac3c772bde7a15c465ddb5 ef7084f805f9264fd09dc215ef9baa7fef2af8aa53eeb5e61837a14e0999b47b 3379b73cb5d2b6c763aeed33fee36a0ff630924568e3e14a3d36456edb189d75 8f247ce37b8ed00705f34b0d3be07b7745e6bd82941724023e7dfb34f5ca7ee8 eb2eec94292918aa3a461159e728f17db58a41ffb0b533ab1b5f8dc9a8afcab0 9d822c7202f3d6d2fd8b2dfc0f4ddee4c8a49f8538f3c29200fe8582390baecf c1b984ffc659e9bf922daa7cab6b24d5057739caf35be5d0c3e1ea8d3662ddd8 693936516e3db6786373353dd185d426698749ed73e2e26c70f88bd06494be86 65e18b9a21508d7ca846a2b3405d84c5e1b62f75da104d95fdc7415b975d2614 bbef2301699a338a7e1dcb2ad902b0a8d05615826af74acb4107ccda9de13d0bcf12711a8e43c48e7469e1b0e13251e2354ad32e26bbcac8aa323c1d7302fb0b17e0b44b3b42a8c710370b23fa704308f583b3ee8f09ad3c4f8d8108edf59c0e9c33c8381965a7da25d95f4f0c28f0cd724d7434704a0965cd1e69071b61810ce7d6df567dbef665318bf6c03e17f6f78e0192680c8dee1b46b0d505f73b890e6b77f6d14aa428f041d28ea2503599ec3b0ca724bfdaa85466855e148d2f4c0d66cb98144907438da4f59746495abc7c54dcbfb7a5bf9b51531d6c7ddc82d60cae1983ba678a164cf2fdf25c2d4ea78324f4ba669326f077ee86fe0be1a7670d682e83d559db1d8aba6b12b5b0c78db2e200ebd29623333c07631d9ef5d3630e2385013274b11d9f8042097ef7776243f43d27d8bb4742166e57a3bbc00db203b2b551bb00bacf7e9f8a530fb81c3a11d51e6fecc64fee0888668627b2f9f5009f3ba1dc6f149655de8f70cd9ccd75d1329c3b8eb65a7c995cbc329d3ec7100b93b08f743c4d29b71b6a68093f09621078af95701ace7ce2b31c0885460fe803bd5aefcfc87a825a881a23b8c899a87d5e903a5ee68483c51198060ba352d901733780aba181baf780680db35fdb91c2b97f68435dcbf6589752663db4751c08b206328cb5b9e84dee0fb2c0103d118e4275288a9082c54d5c2714509834fd047b1662cf01b937ef3bd62b6fa5e07d81f4c35955d82f1be7c608d69f9fa1470911d24a7a111ebe3a99ff3e5a2bc943958e09e5850ae74ab53fbf8f182d78d90cb8a3d1bda22e5d363205b5855c65402dadbc06f3e0d00489d6c8edd2eece27031bc888fe2079ee108ffea988cf363be267417ee9bfb65478276a3b671eb1eb069ea9f18a041a1217ce45dac61d9cbd8d5a2db98797943edc1693f1c5ca9d22066a908aa3f87214280f7b6a47ceab3e3c730794f92d755205ca74d0e7d8532d05c7eca836a3b90f3384a06c94eb371fa3b282e41d5623dc92dbc947ccf9a9cb03b8d6ffcfa936a8f052aa5435410f4e8598323c6bca8adc2e4863c9a7fcb6b20f37ff49563dca155ba5c06a849cfa8d19e6f93a2182464e5a04df721cd203db0f27fa640d5383ec5c67d4a96ba2ab87695ed1e689033b78ff1ba6c44f9c905b0ad9faaaa94aae938697e5e112bedce07a1f464f09c6fb8bdd024eaa2266327801beb4b758d7082663b18a8877d57b103a6a7885a294d7720572959429fef94c0de3fe33b9a1a3a94fd0fc49b217c4657f6d68f12507b9e4f083fd40c8b4ac970987c709dc0652cbda66c6a3cd826b899c3fab125e258efc1ef1fa314cc18ca303f8b863c310c565cfdfaa8547485ebb609e7ef01c09362f173474e77854e9e300f9086c8ce15c9ae625798b63627023982943c55ff53bdd57b9878364ea6aee00a55ead697cb076d53930b3987f204efd6fd418b276f58ab96489316e40427307ae91b7082fc12b701696ff56a4e89963433b08be679999d73b337b45ca241e0370ae7f3ae71fe3913dfaff02fa4d1eb0a0f1e95862c2cb44e62d601c380bc806c05a478acf2b3d3d8a7e272f49656834411c8c2dec6dc0871c3dc1866c8b5904aef322dc634e154766043d6e300a29703034840fb38406ddace64e4a9472ce0d007c871fca8ca564d150a881b725be31c679b62d5db8f901352f70993878f20ce063c891fe311fccc0aaf471612463458361d3036a9c580f9db8993f47a64107d56512c3e4936d3e0eb91dd3cd2a7d8a1415d296e473d09a096734c3e03dfb06c65a52157bea03957b102df79e4865b71a75daeceee797654e8462f8cea1d204f62acd0c3ad4ee6643895ce609a53aa012bff0e6978cda4eb4e1483be363b60e0cece7c35c1d093fa2d3c3525e71236269183e942422d383f8ffed0b6c2f020d952d0393af63b17653667c46c0db291c866a137a3b1dfa1d2891259f0cb1c60c9a337c2f7f83f595d4fb67fc3b503485977cede6d7de3b79ae7e1fac21926b047fc8a5d7a5bf698b0fd96eaee9445d1461007c08b5aabe7416ed33b98022340adc7bf276cb4503cd4630d76bff77a8b72a15fd5d27a54dca1c781d3843600506ef5ad744e471b287425e57b8685bec644b5f45e8e0d8c0a215101c5912bfc30cecefff30ee7a37d129a901946f8af674a4342f5a4217ed3c334fb49bb2765d0bcd2df85f3d9656bf14416547e165d33736541014ac84c6617be72e75e1362e0cdabff6d67910b719f4746f9085df3815be49c2c0dcae732bf7773c1f6dd33b0625237204af3f5c22a48ba8ff4176039aa207a3d5c5a10201cf607b443384db0593993794b87510e75d2a9591a2101788bd95a63f5d44d244c2b67beccfee320cd509ad1b68bf7bf2b0484c36ede4046c8eddfa0e64730055a9611752e5b55b0c6f1fa38802e0f02812d9c9fe572573e1f5a46a14f43197be40c75fa2f01a230af8e3371135d20e90d3398ffe94261a01b1b3c07c3a3fe9d10d68bcb5975e1f04b9a8a282f1b2325f876d1a8379f58dd2068d416bdca78cbf3d991629973df206171c3217fd74712d70875e45f6d8776ff4949d7c3977c9d663c4adef4cdc740c6b942e0a02a29f0cd47b025656ea604abc7630d36854f96bc4845a9ae419a30aa1b09fea9b7fdd17c49c19f802cb5fc394fbd128a766e110416a5ec598497f02d21adca8a29214da36a305647a1165a2427df7f02039e309302dbf5e46d10b057eee829c3c43983d0b20e289b90e5cef1887424a71ec0bc0bba7ca0807e40d07dc7af16af2b6d34886e2fae35400c21f164728337f3fdd32bd3285116c228a00ad5ea5c5afa6418d0385eb9c7e8518e90c3eb078ea8e6fe40d0f18405127750da81300489415e227f2766f4c478895085e7ef8814578004ab5ec7b9bbef5300fd0cc03e59d2f15696b3a410cb14e217a48b34055bf640e5aa95da4af1cf7e65a4ff96ee1fb50a155a3b5ea358de449e1666e856c3d1ccb28af522f9c6f26360ee36141c64206da45e4db1e891496a2c530dd0de89540a5682f62dd0fec152e054a20d4f3a1f5782c6b02f5b2ce11f4a8485ba9d090db6dc1149ae8152eb4ab09b18ce29a95228d1a5e2792fbe12ae70187cf1f944148cf9314f95f7135bc6a09941cee8a9e6c4bf7a52714e05faa69aa53a8d21ed6b652896818b47e5942f502c6c33340b63c8323e6acb5bd6ff247deffdd8102b9eeca1712c488ea701d690c20824f0f55bab37092e531dea6fb8470a08ebc9097555c51b3837e2966db0a085b960b6db02b597a54dcdf3964648ca74538b0b1a114945b8f382e57aa55d60cdb12038c9bfdeb4729f33aa3ffb9fb55923bf204178c025ad133da6bfc4d0301ea1efa821fb15a3b4136694dd5a6bb7217e17e39232d5bb07c7872b36ebd600223bf92d6490cab4b0d6bea54315ca699fdf2ba1adecd47151b804f525af1d10f7ff1524392637a451a4eb614a57a753b61d8d9b7e715674d6ca7ba4ca14db60d92eaf33a2897cbfd496d2835baee326027abf4e1f66700a848766da48d525b03d6b785ccd3ee494f86237dcc015d5a505c02ee0b6163ae30ae6c4deed6981600b0b0be8722ff97d05ba4b9ebad07f86f14dea34617d5b7531ac0bec98da93e0ac54b6d2db8a5fd3324205bca63c517ed564aa0f2c13a66cd5b599db9b337cf04d6ddf7b0c2094c91279d7042c9058b95697dceb9780d7ece218fde909d14e50a399ac64598f648750b93d0984d22e8d6826d3783202128563bfb296d54998f021ef02ec27e243fd81090e38ead73e93bc92c6d4bdfd88e59e0e754d23ed7b20d7704e4e1f9d09a9a13044307e3b6f713ecbcad330926da608ba2f9f5374e550a21ba58414c48c159f7e26e44f34f9c2d4001a69804b8cd2c1c0b8de3b7b2a102787c291141aa9968b78eda9e81f012ba354fc525e35c01e9d403a40f2273850c0c9ceb7b50c7ee546de33083f8600d14363d34c35b70359f4790330784b9b8022f5cb312c25c7a5fdaeba8c998035173ce2ddb1347b67f0be8797ef1242284025bbccec7be63a127d54afc6c4efbf23e0ac1247274044b3ee3e06cc77e788b0f353218601e102297d9250aac0686d042ac715530ee957492426a01fa23db0e02f0821b9fa2157d0bc914f428c746075f5bbfd9352e85e418351e3aa3eadf2d0a10dde52f7593ed253a9bd36469bea7a075a27be304b49e05e841c0e91571960e8ea05121f5627dc41fe2b6174ffb1151a96c1f12a13b612f750e6cd5cf2224009d31ec3bcd4ca46c40ea84a1c2cac1560dd39fc8e3bdfb494bf480d816dd150c458052b67c13fb951d8e55dbcdbebf876a4e5c22bb028ec8006cbb5628057100c36de758dc5c0fa3b8b8e40daa5a82e26395b7ccc79bdfe3dacc0a701a027d003f3b2f206051cc0f6d97e99266ab1b6af9613f29086ab975a0e62b5bb65f610d554fec384b785ff6708dfd2dc703678b90318754791b4889c4cd67f34d02dd0c5948e366ce862432c6a452e049029dca32f302655578cd391aecafc89439b107a0e4eeb559b401cf3be4529f3b5ddbf18b589f31f9cf37004fce5d019d8423075ac97c3c7018161a651ad5039c12b982e9cea9122c18a70d0af26c0d11eaca047e2c1b3215a30f7a805d6e592c242788661494f03fa4277ce5304dae864d2e0699f7ee65a2ed81f02da25585e9167a15d7648fb3abc4ace4278a541244bcbd01a75cc224f9dd03464cf81a8a0c032551abbd8e7f1c477137618e81a445382408452a4a3d018f77154d128884c26a57623959914bc6820bceef93786ca21a020fdc85c5e75144b24b80205a17967085f04d8c5c9e04942fb3483e8fc726644504 false +check_ring_signature d7bbfb5c7548275d37e464ba7c88dc1482ee7d0e24907817fe207e2ade39441c 143ebe18269688d6ab30ceeda3bd3c3f3149216ac625653401d0d0061e7eee6a 26 aaed64ac929221c504831e93cd68f6a1944854259ff583d906badaa24a1ae354 e203e61814a878f5b5068dde3136699ac06f0146d84d256dd7e64f6197932b02 9a542ca99ca2f8c4ca6de591ede2427343002e18853fccc8cbaeb5f897924e87 5baa9449e0831815eb3442075fbc426deb7df929a2e172b8fe1190972985a2a5 9efaa1628b8c8648400a88309b0bb01ae40b365b597784fad8354096ba77cd5a 5ca5b59d716d37384f56268828a53745f33248874663c401f08962be8858de14 77c08104b727f22f4d570218c8938e5816c8006661640a896a9bb77b3c87babf dea486da53749f548e0f4039bff16b218adf385f59fe781cddb88835cc5e2724 9b8277257b5a3d299b79ce1913fbf3b4c8a3b632dab36390c784c9f9ef64d682 c78846fad7b5e39593c81e2fcd514371342ac8e52e8a8b511fe9fab0280ceceb 9cd6d390aa760053763dadecdf9cd9e9355f1d57196d374f4affd2743c00ab29 6ed7503a8eccdd3a0e49bf7eddfdc2b380299c2aecadd4d8161f2dcc0d2de7ef 59689118e6089e0ade1ceb09e776761de8c4460a4c452dc4b6ef18aafcfe8b1e 0fb4ec5f5e604f995fc8937d3f38dd81474fce107a205915a0e264cc81084a8c 137330079534a0005a794ea9878cd7833bb82acdbd1418dbf9d15890cde04a37 c5d2de34dcdec3db56661a3a741c53da25caa958e1000a3ca5d87a9557b818cd 4c746c83593beb73a5bb5f5cf35e2df7e63fac74007d3ddea9d1e0f50919b389 7945ee85040ff6c3fd5bd841d7ce62c057d6cea84a327a4aec7321fc67d503eb 23727704f181e9dd0df95984f410ec42c9308a79daaf9b7398892c8dcdc5a50d e1cadd9b83f585a1310285f20c9795abc01e42b7f3dae456151c39112cfccce3 3addbd8dda61541ecfa3d45f02d4ca8410dd3195b3534147359fac91a53230f1 1625f17306356e4449b7bfa17a0e1299446c053f27caee708646dc5e1370e40e c935cb111b042956ac204ff35dcca215f58245d5af19f179c43ffe46cb5d57d4 c763930b5df5131af5a820d7ec74b2672b5e255f3892fb352107e96a3846aeca 74a2a16d3ec156477d6140b6e9252d122ed1b00ff2a15e8d51793c315d2894a1 65164c54fc6d657111a059b7d668e74f495072d9652e8f26586c829885187175 a81c8b163b25bbbbf33831e38057a2f39cdaf32adbe1010d1788955ca15dbc0343ac97b7b59aedc9d26f2600ce97e5b961cfea0676aa54bb247a9024e773ab077ce09f18363f3751ff2b9976d3030b4c1ca1be4c281fbbeecbe2b8a056bdc6014fb7c2589c1676b6f05d1d5d63c3290b9be92ea4a4ea0bb99837ff507d3d4909480c2c6c485f09d57ef91736eeb7bd024b443ca6eb39cef6f18f1c76f89df705bae0fe8b3c2bfdcca9ae9d9d769623a5c666de18ae07a25333461b8430c1dd010ce70507813dda225333381c4e36718adcb0b10b4d692e44b1d9d61e1c59c50890fa885ad0fa4a2eb33aeebab95e808737e2c36ebe84c95f27a31864bace210ce4fa2fdb692058d916adb8f69e4fc021ba9fb20a5f9dd1f0bd30f80ef575b301c77a2d68cba68824b3e5d2df1e547f6cb195a680a84f4c694e2563b6718b7f0ddac902de06fe151e239412e79d35d8179697c63785f5d8e264c1ee4a01ca0309eab3d588cdf39603a3b9c40049584e693d02fe09feeb047b2f9152c111412c01c014d09afbbd5ba4e7324c79421f0e5f1c8455a1b40823b0987c2d2b7b4b4804eba7da954bfb0f77e92b5bde790ecf06a86b96431f218af717a92d927cb7560f41e0b032365ceed8ebae36ba53ab2d01a4fa59f8a14a3c7554695637a7eb9900bf61743ea7e187de4ae799dd43c9d998edcdcc3be429f1ee33295486d5dfa704c3b5632f5919717deb9fd08ce4f074e3968c7cb0f78c74701ac5c2367ebfc10e26e333cff00173852d0a0b090cd34ff71f22cbd4256c7263a1802cdaaf656106d948190c18c9f8cbd700a7aa70e4800c08e53cbc892f37d91eac7a57db0b190ee5b63775c0ae8a90bf3aa28b688b703f9777077fee770708ada6d951d7b9ee0adb5ec9570016a8146f554d342ef98bc6708d31be305e3aa71eff633c8ebcfb02696e1a65dedfc71f4f241aff1b77877f537b0aa55897e3af1cc78f6a0bd2e80e73e6bd5ca28474126bc77c2de2304ac2a815767b6fe66f5e768567bb2d360602de96f3e3219041ffed6b918077333b9265176a3167326344ade94fd0d0a8ce07a0c8eae59a0567bdf13b72ac2ba45106021bb8481fc089a7d9759965dcb3c40f3833dd9c849b46f800459b6df9e7d9412cfc21ab4f6b3ab732f7f0cd681c330ec078282c4f5404cf3c26bd693a99bf3bd893779548195d1462a2126fcbfd8f0842af859de1f2719c4d75b1a5e7d497eb6d82fe71568254eace1f5757fe5fea0502c9f4fc665f6e3f465a210479425f4f23c64b6ba8a1a50d55a5ed2b7ac55f00dc454948bddd048b9502e8606fcc0cb9ff1fedd45a772adb6a3ecdfda86a2100fb58c94553f69481920f2e79e45dc92402519511f06bc3f157eb11b3629e410e79fd3960011fafbfc3e44343b7019d349c569098329dbc670370742ff908200c72498fbbc9771a2081829a0252f422ec09a9294827ce026f5cef8b11effb00029257448be728aa5095ad9191b2759cde29d693c23a8645799ec07af20ce5760617a93bf8439439c63f39a5944a5ce638112c1491a9125c586127e8e163fb710b183f7b047f9e6f73c95268387f26805db35fc0de8883c0b32a767ad05ca22809a0298a567fe12b49b4b29636abd69c0b79179bf3682ab2c3a20d75fd8545be047584b86b39ab7c750e498a0157dbe59825609b97f9c055c5ecbb9b2c103d630a4b1d0a7fb7f71531ebee1fa0ef061e0e99156d82208e1176437b9f035a9e8e01488e9b5c60d04cfe2d78045f8b89fd5edef655ca16c85a1e3818d313b08b99f65ed397b6e2d7e5ca056aed73948b69ef510f2f72f23725118cb14dcc22eea007edad4a707b7036f7109c545dd7bea886b3b5830eee7ed03d7b064db22123da0898f8210adc9dc9d9fa0114f800225f721eff14352a253306db327b8671c5660d5e37a3405717194b5aba4bfb7fcb6d76456c1ecdedf7bd71e0577441c4c02259c2aefaf7e9c80619bfc6bbb38a61e149fc4331e0d00cc3be88db21a1f4b3dc032a323115300761070685de2bba6718467e685b619a3927c71336a193443a2d0379dfd50fd6a080ddc6bd34eb9867e643e679dfaf6d3b2d4116f3f6256477d0089dbd583f730d2ad0f27e94019b7dc0c6ce49bdb8b1e584687da032a44323a602b168b144ea993201831490d982391ae20c0a642d1a5e1a93aaeec9112940cc099408862a8062195d5529bebef50b6a7a162416e3b2beb3342992b49bde3b3909b88f0a3632257ea635e573fb25e4491f024ff03fb3760d2daf74a50713ad7e09e1b97c6d77f1e174858ab4271fd6fefcdf50bf5268de7a81e35ffb0b06af6031 false +check_ring_signature 3c0dd0c72b3fe721e70eeaa042d941e135fe1ab443113561d4f3d8ae081e1443 34051fd44e21522ed7919b573e65e5bec628564b436ba426f63e8dd7ce028923 7 d4732fb9f88be9315e21e8bb1ea28b536e78d4fd4c09e1db321defcaa9001b4e 20ef12c6f0022c3cd9c592e61c36846a7887ba658e7c164087b08649cef288a0 3d708657ced4d4a1d7a781da08f809cacbc96ce60369fb1fa1b2d32ade812cf8 39f2baed932b78beb3a53be562b78e14252b53e4138448690f9b43daf2a3620c 5a01a9945a8a6a7e03791a5db351c5d2d6099c25d988a1025606eceff5774eec 895106c304a9c47ca567c84ff4bf6a7bf266cf404d077c6e8ea0cbebc445795f 60b406afe9e30fc85c22e5aad604c4f82003bc662eb45bd69fa4424231f2d753 af8b21d8e19273e3b54599f1afc9d4abc10ca7b08a778792431485bff57a940988e851ab05455f118544b60c7c6b18a8f34e86e727d9393b180bb6c7de18250654166bba2928e3e1b8d1a35fc1fabea29e914b94eb789ccf6d99eac3412cdd0d51a833f257f1ef93ec46575d4902623165709f6ebeac782dbbf8c4daff1a040bd42238cdcb2207c347c442c33469904b3e8979a532f3184097837cfc283939057f8be1b528fe957610f7a132b7a2ed6b3efcb07713be2b75275b1f10c8a3c109742dd367325ae933d997f0013b93a3fe5a5360926df87fd6e8d39102afaee9072964a8a49f0a71a02bee5aaf7bf5bd82b2c8164cb43523478b9412fc8737a30ff64f5c870bc3e5c4f78765b5330a14773fccb1edaf0c37ec91d4739913e2510b4fc52f5556ed1c91b824a6df8e63c5ea7c44816b28fd1af60cefe8800aa79a0014f7aa44a665b67c96d92bf6bb63a690c7777d8847d2480d80179108e883400a6cb7c4934ae1ebed591e605dd2363e9de8405d4e1e29f4ed696ba88d1de76b0b07b8ed352bf24c2a31bfe2834a5b8606f9df025bd1c3c7c367a7c83d085cb40c40d1f01cd57b6150aea417d8e28733ff390cbd583428c577a0d87cb6f81cb709 false +check_ring_signature 48fc793846ff5780d00a4bb749d0bcf5326931c063cf5469dd687239253bdf51 98451fa9afd9e9171e03522cba1a3f0f26dcd60decbd2e8477ea021f06688010 2 6efb446be49a3f37d7c250b10e3a76bcebe051609b01d036264c50b84fc96efd 248511d20b3c207e087a9b63f68362db5484b2c54a3382490df6f1844182ff47 8452fe30ff848d05a7bf885eb296d2c34f56ccd2a41ea6907b7bbe7feb03fe006e7f0b80b7c857fbc59c5dc74cef9cb3f62473664bee3248b05ba44388edb70b6ee11f9bbc8e56c34b32437ad7ed4fef82bbf338001b4d2522f5ec0d4a885a7ce7436b47bf932c3f81d75e6a5d20aa89cdd9c7b3a6c9b8c58e60d5d9b3953708 false +check_ring_signature fa998839c2b2128208fe67a060ecdda9e8670866cb620dd12e6da22a7b162970 fa63e58d68ce58c211d56c6e796d59c9693186f7b0d9075874061bd4784bc891 2 62932cb4bb79f3d003414d94d90083e4114bc1e077ec21d29a69a8189f30a175 7bb2480b90403a83909e33b14e6a8caa590fb10fcda802d1ca17ec680a3cfd16 c19bf3858aabf7cf358ee03557466cd7cdce34c2088a28eba6ce69e70c0e6b0d263a9363a0746a91fc0871458874b89da07df74a5e9a231362be8800f4771602d6543b7818a49ea3dabda2e7b1651c031e48927f23e47729d5195bcb529f2d0b94b77ac796de4796cfdd09b2ae70f0fc6c946deef05f76e75cf8610317dff502 false +check_ring_signature 8d8f8140a619def57d6dcc1b3d47830999745903ffc449601b1f8443f56e281c aa005dc58c4319886000116c019b5e29710297707e8bf3464f55035a4afc3006 2 3cdacd93757b16f38059e67e9979da924e31d10552a1d5f713e8c5af744866ca ad560d6a8bec46f0767e74e10d855b5b0304293577d4a8ba5de67f0d77f24264 19249a2ddd8071de7978f60f292842a01a813c76cf668d8674068fcf6cd26c08e7a672bab97765ba8a25dc462f52f2deba9b2729a947a61cd39930517dba9b12d077abf90866b2d8b650c940755c432bfd376be57da722fd5dab54fa7005cebcf7be7123a82c03a215ea5bd1eba6c32e2549ee1973e6608f4e806bebd099600d false +check_ring_signature 2da44e71ce27f97c3f64ebf3539da8ae02b71f5f40b1f48084543ac91796eff9 72ed90d68ae067433442a8bc963c9278b1f0c269b009cfe182b662285428a6de 2 f75bf9ddc21671fbd510b1a295014dd1a4f258b1aa620c62d4eface10f4bbd86 5ef87feb63bb6948902b563520f47c94ca3a6c3c2a4738f98c411a3d3d42e9b1 be28257e5347a55ddf059a9784e7b1013db785dc42f49c26d13e9b31c7ea2b0dacf40cd25dee1c357416f8be6d585123cda6543d671851eb1586945d1026820c21d78175d74df1839f6179f6578eab6519fe8252a8386ccf6575b7824ab9e005b0b6732c9410940528f63fe4a7197a38d938edd3a2dcaab9d15f95cf4e78c100 false +check_ring_signature c76e28a8e88d092bff52be2365209170831f2918798236c83cfad440a2ec6ad1 01b812c2e00caffb1ef6722019b4ceed9d80db78bd69cbe067a2621396a2593d 45 e759b2430635a26ec64d78fe51fa73ba52a8bb6166df4dae0e1d5a365c078379 4f7bb334851e514a7c5ad1823d6086d0edb1ab6e2c7612bd36957832f6a43a0e 39f6437a66d1f9c69c8c8e8d095053a1ad0563931e469b0a381c62a4931c7694 edad4e68252736b954bf5f60d20b7ad73cf273d6fc142e632be662e853dca035 8198c97dd4129ae0e6531d707a0c5fcd2cb6c543bf7ea9436da4b97d88b7eeb2 e89438ed33a8f8381fe9bc1755e16e7c864157494e7b37a755c2fca51c4bd7b1 8795a6a426945339e0d2e0f8582bad049e6fd7105c223aaccf1a84096c285dac 603a725746e009397652db64d007b269d4adc5a2faf1d4a7da737f5cde2108a2 d61b27f8667fbfc02ac31e419f5bfbffe15d96220b9d8b8558e791661b5de912 207993d540e8202acf79f7e85bc6a2fa8da28c01f374b813201a284c8c86ca9c c42affc693ab05c38c76b4a8b7d8ad69e3e7dfea72ce8b196327853feaa8698b 33bafca46c95c6ce15d714899b76e562d0fc6b6579730186fb0afcbe84b42f89 4ebf2112e9c91740615e1a1f5efaa6dd901c81eb617a80e9aec975db958b1b5d d0b1981d5c5ebf08c63e47f7704eb0f9c18697c7198cfd48eb6524d54847d12a ef4307d86518e083db2e6c195828bdb7ca5112582b13356931e71ebb18d81cb2 4f08a6b2d49a177123b98bcf341ad3df6fac98a14cd79214361395501e737ea4 dffe0ac0d71a5aecccef1c364538c41f4801a77294836f35c67aef3f48aa79e7 78f834dc2f49727a87b5a217151e02609fd60adbb869c8a10173b8b41b14d268 d6724599097d5580970881841245ea4dffeecf3a49bc752574262ffa51de1df9 efe5d3ac5fd7dc0e1079589b5c4326f8aa0c0ea15ce64bb661c4d8bec86f5a3f 2e81db6509f03dc34b9bb1ee32b944f39d91b357189d8978be1f23b4d529e438 72aa69c19ecb9c0a9f525f8f6a3a862f6b92d6b462ca2f9287fb6bf86218cc67 a3f22c18c6aa2d7b931a0fe53066b275c7da0e9be635cfdd776c97a311aa0b3a ca969f18bd1776d4a91a105966cfbc2656d7f23ff3b6d261d3879460c35cc24e 4ca9c67ae86d83cbb497519c35e9112081754a1f1a8243fa4c9e547c1aee47bb 4a62331daed757437f8ce1199882ecb86fb9bc7dbb303682122136f4f498a122 256e2932672f7508c463a763223e6a9b01eee42215cb02c5979446fe7a955db2 3d7b0d60cef9b45d7359a485b86562de3bc0836a4829771d13082e35835db150 a6358d6ae02c4aa3a12fe6bb4eaa0651813c15d025b8e61fdd54066ac403aa63 a05c8431f2dcc957970f5d22c6409612b8dc5294d4b197114d6ef89d621f9e76 a11cf352bede4126122117142a56627543b48c5acebbf1e89dadd51c6c39a78b 1b949d544aafdbca5b2f14c75d1effe4502ec12f96150a193f01009c30e4f9ee 0466371bd40b0657a9ade4fba4174f1707f3cf63565b11a30b9ae0e5fb2ddb30 28c339880354953c20c940502ea34eab45e28a8dd07988d11eed2dff8f0ecedd 7d1d4e9e0eedfab2dfd7b24f758ae9d81202322354f9a1724856bfd6b9dddbe0 2a7731208b549e24d4111157e592e31f99dbf5d150c9e7960d06b715b5201ea0 0ec344fc0168f72608a7b56d3be75ff029c00da4fa3276fbbbfcb467018520df a535bd3ffc4b6937a457baac51155699c41d68de250a93aafcca4c45279f5184 ba5cada7442efd486f2b0597699fdb040f170d775dd94bdb9a295a9e48eb1098 1677a656f40f33bcda1983b65019104d76001d98d86aa5df3d8268254c5c065e 37ccd7d6f560506d0ee21153bb414e492544a46a1806c47c0025003e10c88373 824bafbc56aa5090dc944b992456cfc22387fd27c3da174fd49feebe73097d87 e9aa1d0c821be34b5664d463c51e727f1800517210b1ef19b6aa6df2584cd761 38ca596607187e204cc65827225111d9750b20c30845d4ecf91d919cb7793b7e 12f54d3d4becfccd61051e24d6fea8974b8df83558d493978caeff9398cfde0e 634f7d29a04feb8aae6a269bb1da16da560f8e2ce9991de088c121d15c083102015cccb8aef15a2671660edd2a169c2860d57af0a770a3b83d56f19307b8f702abac1002dec29c5b5a48936959717b3a5ad132a7dd253e6b8db7a2ad9ab2830daef3478e35c0faeab3c07f2259d0ada099e76c333e8e39c03b6d58d58668e205937fa6b39953a0890681598ddd9d3f66cd5d827e7fb1b0f6f608972138897e09ed86a4329aa483399f3443217cccd029fb724d66e74fcaaa02662c3fb16f700504970b78eaa11a0e4071364750cb448e86b4f404669a4cb742f946c1b247930f46f0dac90e5c3e7175512f2900f9d607ed10a7cff0fb0453081ea56db104b10729612ffbf5c236da7009c715a315a8b674707dcd03df83b68638ac29588f550ff18ed494c8df9ca534eafb5aa36a886798cc2b8c4e3e22d0bb82b9d04697ef0e12f73188807bbe5846c0f440b08fceeb194213d752205fec08517d1fb6a3ff0e543b13e14566780532c7989596e8fd3841bfd9a0f62033d483a1f840f7a7af013c134967a8da15cd6bf104a6460799b8ad73e85f74148a6a0e4bddde9d343702d66b2f4a9090e624949950f52098dd5c349c896bfe2e5ae4fffec6872cce7b0da138318fa9f4ef33bb69fce9c33374f942d96809c3719155a5508b0f3449fc074c4fa79301c7e2fd89a990ceabf573b43022180447b901549fbe0cf1dc29c4002ed870866788b65c8d964edf25273d25ed61d08d26768cd65fe41fd4090271047fc7bbf5d21392315c3b38be42c773fda886ec934ca57e7ee3056abcd5922409488e3d1aeaa319cb81fad9750c6121c0c81484565c502ea9e51c7dcb50be17022779b1c1e5280baa0ac30032811b04f6299e4b23ab243a9daf10c1c3b27f4f0687daca3e0e285b4dea48d865aa1a9b8e7193cd3244c05fe76ebbc77aeed5270eebec2b8a091b6e6e676f68183178341a57dbe58120ef5f51e2440448bfe9ef09c69b41a2c7596203d3e77a8a13282560cec66517f3ef4fc6ac7ca3afde2e360d191bcef6f841f4e00189e93a8812fcd5c947bb2f1cb3591a063477222c67620250c8f39ecd6a400ceffdc8f44754668dcf40e053b23fe815f838c12c0574bb010ead6c5f08eb337e05a8b2d853cbb25e04f21b25f59554ce9947138a89f05203d8a545db448bb1d26303ce43d8e6033f171f82f55f52d92b2e9e1d572ddb9107bc985ed31450e51c5c2b0b9c83f61a441df635d2390f71c7b6008ec359a94b0b35108dbe65b1ba538fe92f811be40f2d3fd1b5e95d7270a03e2245e4fd45530924914c3ee51b1f445894e64e371d1e9f93c780a53d77b20c5b2c49f239b6460f9420e4364af7ae4e9376b8f9165c063e1020836e4be5aedc31db93e204684c0ea6ccd0fd41e2bd3b54ebfabb370769883aa58dd501c1f42bfb9150fc88c5020c813423b72872ee331620ef7c0eff477e78c3890c416a65f1b836e0de02bd7a093c4fb9e181c54a054e3c5b2f57261144f9049266b4fddf444f127b05faade1035268d0153b5e03d9d8f91c85b96b84d351aa1ba9e21871edc7d580afeb156d00119028b46a3515f65e3b6f897d94ea949b6d85c4b5907fa8253e5d8554616d05bb48998261631324b250f2fd58d121adcefb951afb2962e1b4b3089f35df9e08ba4ecfaef35c3fc674ecf6f1df431f24fc05b45b11455c310b98d9e1dbc9d10ce02fa3c814bf27c38ae8226f272235887921d1da2c4c360a4e11ba1a9b27af0bc7c63564e164a27495d9eb7ebde4d69516fb58ff381c605b9c93ffce68780b0e380af62f6fd41641f83da57b584a53565688ad12931867d562e44854a807580676bcddbaedb4dee3b2bef15eb36193d669dabde34a6efa910cdaf69c3efbe10d284a1370284992cc4b7a4ebf8ca256143dfd16dadb5543ed12afcf52a245930e8f7ab8664110cbe91b93a28f7a5f1e477a18ab00ecb756f4c65427d95aa28e023b239027d11aec8c867081afb6d41d06282142f2bdc4510a46d8a1ade923970d6f588bfab275417cc7b7cd1bf5995fa156acc4a83dfc906c0bb0bae251d7010b8f4d9de7402badd77dde17803b440bc6ac4757d58830af6aa56d8e19208c4b004aee7ecc07ebd08e6614e5212942c7a11a0c146d2e6a18ecbfe60dd469f5fa0018c1a20de0e02b25482ff2de3b9d0e4bc504818603baebf9d62d53c5d7c4c90296a79edc1c0c0284c06e1f1b08d5ad0f69f6e938f3cc55acfe5b751ff485ea0447789253e36b88b77114844357147fb29fbb8f5bef51d1da3066e38841a2cd0bd31f607a184040010a091e186209bc3746832e93aa161c4a8462a2270a5561084b9f3866b2d79e762571ff553b8fc41e1538d1fd27518a7391e1351e7cf2170e8962b4e304a11094aad883ec6dd923b6c724e5901e7f788f69dadea592996b04c6f5b792b3880eab97b785d28dd3833d500ef07942b6798cf2c76797653d52005f7692d63f44ca1d651cd7945107eb0eeab3b423a24c9afa7e8679854e4c7501a3374a1dc953126db0f67dae1fcfaaf0d0a7453faa3a3b80840a113ba833340d6d2f7c10846c6779ca5a1b124723d45bc6a5fac5d47f17de4547455e422b55014b5da428271f3b29c5b3eeeb6bafbc4d8958842ea71ea38060d1f8e4245fbb02238b660b543e45dc7083d870a0f8a127ee6de669c44a35bf9f82dc50470f4703f2cdac1e969794341b2cdcdebdb29b82ee64219e6e4ce939b2e65d07d1e32d01c10de1793dc09cd1ad9f582d86ab6a9623c634ebd7ce60a4c6cb1b57e7e28a07b72a0c8b45b9796621c0b00073e2158c13001e65f47078a1baf7c672e652be016a12a27520f8aeecfd59bc582ad1a6b7024582fee5fed1a892c01d6c87234e0d2b50b88a178126930db4987500fb4a7815eaf07521ea81138961d6b85808f2069960e9ea28fa7079dcf9f8cb24b73d8aefc4e82e53d39b189efcae47fddeb6028f518ead9f5dc6bd64c0a7003e6831f4486e41c7c46d7a10fc6c6d6935505f0001880e0efd24d0b7f48fdec7e974d94cf137f06e435f492c26fde5b0e68d400338ad7cb2199d577f29045d066afa5babc0304223144a85f8833a759edbcef40e6dab3d60c8c3fc8315e776607433093525d93a4313077e96f958002d9026890d960380caa3dd72eab7ae1a9c72306bb5140a21cefe1a057f9f6ef3b869c22e0c074725338007307d574d070b144354e994a10435b6d8afde8e7d4f4d1fa7d6081f06db7213f4d8f6ad504340058ac2919a6641a83fc6b812a0a0457d4ea60b01b3b51be6653de93aeee2c458ddbd9fa4c2a1339601cf86ec2c049be6fd0ef80b8badeead0cc049d6337e5a1b442ba89266d12bbf159d67171c3dc30475a3cf0464f5566efa52920de599550563f188cca6f360544d2cc1c5a1518283604c9605bc0043ac3fd860f2a511167061d39c5bdfbb88edc73e635d9829191119c8ac0bdcdc2d24b725696fb1cbf03441c1c4774e4d56606a37e2e7d8ef8af9e8e70909689d25b9aef8d2b4768c82ec0650da59b4061c6d48f8950cd908e1bc68fbdd0d3388af42fbc5ccb6912a75f525b3bca8900f61dbcfae1ffd36e8e30aff7901086957824348f172017ade21191652d5d6eb5291effaa9301441ea2bded27ea905c2725ce3566d3e023360060ddc7d945d42732a9d9ae1593bfec673c537f29d0e53971b5f004f3d7283bbea71625df4d9e69c835cd50edad09aa1b09433e5a601f8eb785e895dd3ebee489b16057d144d1737a0677084602aa3cd4ed30ef4b40d3261aed5882bb9322852cea9589ca2896fa014086395d9d9ed155c32815a070880f0a2f6cf1c6980c479705f5752512fedf26ca5a6ac46b5ef79356a87519e0eb48441eb2d41f25a611f0eb5cc9d74da42065350197bb1931ec36d29a4799203a570478e426ed8cf30bf982f5fe27fe58c780e3f20c0d696aa39b3d87a2a01054c2dac23074eb0bb453731aad01753adb7f76e9539492a68db53d90c1559b20e4deb8e791417c069afa65cc92009b7806d2c60f0ee287fb4eb79b0993c960e0c true +check_ring_signature 87beb92d2ee62693bf4a7fafdc9c450cb900624046832cf1c0a7998d0435e753 7fb47946a5ac62b8c272a6fdf6b37b33111bca7e372f306d999d48663a9b35ca 77 430e32aa8391f3d0471898f70e7f47045a6c144a532eb4a62235be8cab7bcb7e 6eb836562775049879bcd613b58abdfeeb204e4d785266da35e5e876e3c026d7 43db2f2c4483ff18ba99b9946164d9f9c7f56ec02b5232c8785e3ff46eb6cad1 c565ce88b54a7efe700c2424c450e376a34626d082abf726df6a2efc42e4fa3f 53137e0123137a914bd04d7dabe7238dbf5ba032a1ccbeecb7cd066cb6e2b03b cb7d91a16fc8f2ea6dea1385201677d425e3ff3656a9c67b4ed3d0005888fa2f 210c7dd3f5f2272d385295ce548dc127ea6313859831a8c8d6d85f2b43f5ae07 2cb756adf99924e110b8b3532bb59f24bd688f72b6bf9e21f56dbca1011a3170 60950d38c1c7b001b2e3bc6d90bf18ff05281f017ec9c9b2a30c0efec6cfa925 a9bac025943d10de967ff29c8259a2809f084ff9895053ab2bcdd8026692ad84 8b68737b3dcfdb549a80fe92c12c6d4d97cbf29fba09b269c50e3ada4c097f23 7bbbca623da3d9dce2e35b8c02a58e7b181e78093e8b7747f9dbb18d3224e1f8 d516496b6ff0f9b125364b273972fd295df461ac2dfcbec97643f0f4be077883 c139664a2ae09a678b1cd9ae75a003f7fe9ea1fbb134ea64dc2a51893f8951ed 3df3a345c27f3d10629f4fe46a584c62e93dbaa502da4d6fd20e5ae7437a9e43 05968de1ca67bd3ea3e6531429ae3973792f82ea347bb21475ab3166f74c93ca 3430bcb15c820e756436d242ee329e5bf227139edb2084c48527c7e4273f8169 c55940199356acd45b2078b87ac62920368a0046cd47bd329b14a842a29dfa5a d920ede8cca1c93ea7e5b7d9f9dc22643d824b2cb2eeccf65022cf7ae492aeec ab0c8dfd2d75054997bb7e814495a14d03b416ac2809094f9c1f53f82b81c579 9d1004ee566d489f3cc16c16eef9b17e1e76a8b541dba55fe4057b9aa62ac8c9 b0dd0999877c43f4a2c38e964013bfeb46c1b1fdb65a165ecf21018419d6b7fd 68678f182000994b2d52fe511b77b51262553483fae8cbfd09e168d4871f38ea 38577610e4eddee3cbeeeaa96bde1453058736b5168ef99d590f20afeebfd922 089dffd6cb14dd07a867a74a61c9e73fbf4a94b98bda14163e3791dd8cddf78b d7e6923e65f5831c93ebf8a7ed8e2ab934a0b577c7c3ef9abb85dddc2c95894c 43d3124247ad5dd2897cea30066e622819ae77b85fef484c5b28fc0fe13741ab 66d4d61913cc317665d046151ffd27091827a6c81ef6115423346a5013466983 b940240e93c1127d0a7c9ceef26eac2a01da756c6439c4d8fc504417da2bf2d1 d3b274b724e69dedcaef5dda7b750b59aa0f047f0eb70da6bbff1d55ed96132c da3cac03622f6a20d7dbb70691821e7d376d21293a981061248f3bbdbcff5013 41f97783bb32199717967ec0ad7bfb5bbcc341b4c64fb4a2925f80d1c5a4a24e 3b60678078c625a9a4c2d177471d3eb7cab401a80137e3599e6e3b534a41e733 05555ce1e2bf12d69069d57f34f005070d85daed135b83be11b5c628e438b5ef 4f3e704d330e3d558c281cc67696e19e5d9b1bd976b9b9844b0b68c3c109e029 a38c1cd2cf59d207258da30bb90017edf54e3cd3a893b2187652df755025f6a9 ba5ec4e0206a02fc00528617079d518c77e9c9c000069f92be541337100b79f9 a6b948c8e8b0257c3476954a1843a4a5687dd83bb2afff2b311bdb37d4214b4d f0fbc863f956c22c0e21b18443947a61f0903f77946cfb38d8ffc0abc8efb1bd 22b112c39bdad00bdd54af656e394906c27bdc0bb92fbdb37dc0a181c4efbaab 20fe07880feaf45af600d13eb5c1ab34475d57a4202d67e7106ddf39646547c2 5486b98e3ca3f2351c0f13186c3628e79065fab7c429c61ea3a3961ae881ba9a ef5c8892f7723b95bdcc505142efde4ce67891acb381baea7a375baa9eebf15b 1050fa96e17924a603c6732763acf72df5e17c7a4495fd6881f7925626b6dcce 35104a08b1bd467dd27d7e3296d1caec053792c7e92441271d0f1829bf654409 dd051e65dbf13d1953daab755e0d4416e6f27cc3060169fe3b28f149f0444a8a 45155de695cffc4a46069d0bd78142dc977f064de2e945c29e17f0e7aea08bf5 fb6e36dae72ae60e58bd05bc9eff9d5b486a5e3a4737805474ef19d2fb41bfe1 0d6385211c161b35901d358d710b04009f77f79a5ebbd5b416492f2be47302ff 33dbc361e849c646ebd81e3785b1e6532a968b8857c7fc4e1271ac25003ac8ae 0faebdf8100a50ad53416b6ac03509fb618dbe935c56f9415b585f2395205bb5 78cf78822802f7eab2de26a1d16bcaa05fffd501db6e21be1eb22710d9b9f096 974236137bd8369b2722ca8a79c995791ee7e6ec544ed2fea8d6a4a47b70043c 0b60dc9b59ba9449d1a28f1483636301a6188ea44ee860a9f53eee5d3e662009 6aa720e2ef9eb38a555b198af27707e9f84c6c38e898c2353958b910279589c0 0c5d9908a26d997428be8cd4cd80fdbbdd3dab59b8a9bc3edd64d58c0b3de2f2 c844685f56871d899f740be135c9d56880ca3a4e9a83e979ada3aa34e9989ad4 03b35f61daed0a8f981cb7b954bba3731c39deefa3e02eb50210b261a541933f c05dc116f6ed98740766b2a7f2fb735189a0dd6f33c7e01b4e397c95088b7522 df5cb1a62c24a88e2f89d65a629af73910bc9a94606ac71cd78b3c1496d3464c 4932162c2965bd58cd0f0ac32e938959a16a9f28973db2b459e8fdc4ed95c8a1 46bcc3bb1507ecfe403b97380b9088f90417e6f36d292feea0deab63762ff46c ed5891baca795dffc6281214bccd8887f73cfe00d45a70fbf363ea886735c4cc 72be485c7e7fba67450ce6bae4fc53fbb5320f87eca4c329046633606604767d 77ce1a4956cb811b6cf4a7cfd07531d428c04fcd4eca80c28f1c6b75d1dee4f4 ae8892ce4ca8f723d80dbf7b7a4c5d5a04fecf84577c4b50851a213ceda8f0db bd20df6cbe7a2eea754169348c69a61bcf8fddad2f59e86fcb40d3451abbeb62 a9fa86805fb916ef71b4863a84949523168a702406840c8053e92ce36ef6ff6d ec9661ad5a3f39a4219e1aedf813504cdf8353a8fdff4f5dcdbd12c6bcd7f01a 4083f6eead9f716f886de2bda4cbc462dca099c0f70a4fef37094573e08c62e6 c600e2309ec91e83bd4bb9a4dd6eca2bebc22121d63d802e92a9c0d3d1a7cea4 4ba4d4fdf2f9fe0c4146a385827955bc9fbf679c0887cb36d87603612e29397c 9c54a86d927f58c292aa236c7cf1a977243261bbab329bf18362079544416fac efe1ca8c4763e51f162a257c5e53412dde6af2be8afc47abec1be52efe089230 f244be67803d8f44cb0b8ad11a96efa1fbb529ab90857eb829e82adb6ae29b7a accb0ff0e034c4990fd34ac474e415b6662d78add951a3bd06659067d492318a 78f387c5325c8b648e4458de16aec3d50c7e9db13d950f4b1e8d82e2ed540083 9d29be05d9951518efdf7d9dba06217ed7e9496329a60b3fbaf6cf1f58dc28062c262c988e17a589f798276374274e16107084bf8a4b0dd015b46581063bbd050cf0d16805c02a3958863c88672c3fbca00fb4bc52ffa6313e0ef2c08366dd052477c8c3b1eb2c3924f4c12a39dfe7a4f491a5f4e56964627fc49a9b3f6142038ab7d3a6cd2978eef7e57b292e75a690564c038c3f7f6d4379ad22419d69710eafa64cf0850e934005b51f3943188fa1406dd890002c18788234ebd0c9b46407f303112ab409ff98220074ec9a73adbeb6098f563b3d00e2fad41c0bbe994c0406d5804acd3953815ea4135c8976cb6b19308b82286446650e62d171de675c08b9623126ccebac94dccddc64ffd9c72629872c4dc5afbccb84e57fec80ed840fe3d779608ad8f9c7edd1fa8073b50cbdc0a1eb04e062191224ac113601a89f0c4364e3b8621fd29cb2ee3390a6d272a104e805488651cd725c197531517fe004ca7176cb217371e148270e2bdbd92ab5d4d325f6c67c1f9303b3f3ea2f0ffe0a5012baff1036c3676637e7eabd592684c41235bd9452115724ac4c038581ca031270b97607d4ab36e375bf7cddf2ee6b3b9853dfb839d31b2df448731a601400b40c01b62a293f266b1710b2f057b7a3064f4e27bc850ac23fc405b7d5ceb104af1b9d570f2bfc9171f4c8da3c9b89992a8250eab57291b06b15feabea43840273002eec83fa0d8a4b9de622fc00151bfa296ea2c573b815cae2c19736e0960af3e6f1d15414fc7e0bc1687cec4d436ef5c50da171fda69f69cfd6592654b6019a0a44be940648c663d0409388540d503a9ae0ff758d78468ba6678cecfb28011e8737c293cdd4804a822b7df0a127ddc094812b4de4330a7cb708353aeedf07c30b9e49c37c9b78beb0fe92baee3c65d8245fc3207f4697fb8b2bad32a2d8034bc446ae1ba271e6ada46cb9258f0b2aea09b8aaf5d68af32a653782771f3a05e36f137617a2811773f03342e9d599a05a93379b8a2ced49d95a147a8df7a50be8ba6d4ca54ca51570335be375d29e90de79afda5994c2f762e434f161e2f10ba62638dbae14699e2943b182158c193b16271c8d8adb8b6dce03cdd403f8b70b06e70f4b4590bdf5101dc8719168e488e9817267c5955c99b5f0b00ab140f405231f3781260028e569a9016f89b1268cbcbcd92cf337de3cc3448d6c9e5cc60b4e8830ebe5cba6c470d34f382fb153241331f6bb29c7d55eabc1a60b25dcaf0bcf68a17a0319c2c21604c1313b2b0df04b8179391299b65ec1bf001dd5a27c0be17986c9d5a5ca69f36bf858bc8624af48f79ae4395c1969ab39a1312d543005bed3a816a22ab2fe2cf32b475269d2971da5f55ed8cb4b87527b3f1696943305aeb3d20746eb21124891e423fbc2eaf4d51df0f70d80c3f7ec48963a81ddbd002b87c204ce459cc63ada6ab9e9bdfe003c411199dc4604eebefdf8cb6cb2480ecc1af31626316dc0568c9e7e96a6314a531ab27ba910917b58b5bd8f9124c2043558d51bd03603aba544ecd7d7823685df3371cb5a8222becae1c60dd7e0f007e001ede49aa40d38f8d0a45844e5181c2e9267795dd3e89d7ff4dc4ec58ad5095c8a06882b1c0e37a3bfd19e76b5c110ee86511778f83bd2055b4ac1ff4ce1088765b45740dda06992f2dca0735fca10b80078b67ebce7cfc63f973270082400bf024ad7f5ea95778d5a799720a74a43b13a02146293faba338f70474052d20adc51792cb27a0c9575dd765d57342e784f7ced753f42295dc00d8c526ee16107b7b6d1f4bdca362222a9d1174a8d5a176971c9e26662a79f171777e239c6f005adccd071270065cc792945bf062b7a9c846e1468f3698d065ed15d85be420a0e463b0569980e5005f3091799bf780735350e6746aa5a5eacefac3d0e78939b08a7b45c828b20b72d32096b0b41a6020123639b51ef140a1243cce7668afd77066b602fcbd0e65e01bfd5a939c123f182037431db44be352434ec5cbf2afb7d098959c750c366f0921376b38be2360831b8838566a679b0af0b4cb81c22532c020cd290bfae01f03e056070c907719da62fd4583af99ee4bb47501b53141b4b0dfd0b83f1bc4bff95a3f2df036ac088bac09e6569ec7ad539e0c34ab7ceb0090e2454de1578a90c04f02ddc0b05c17cdec3ab8ddd78341720f19a3982e65c1b09b276b86c34f123c758ace2c52398b925185618aa078615b702b272047f719d0a0e1e2d8dddb987387460d3d4273e6155a32bb827214242fce516ba4a7753b006537feec60d92c884bc97999791629ab4573f54fc5e75276286ee6b7208fef3056081eaafbba6e674279291706491bccaabb2fe73b3987d01545bbcc344c974016be84b0b910fe6c4905aa0333efdc0627c0b40744c232c5f0f3656ef4fb6190991ce1177f122078eb2833e585f3abb93d1a14d59dec35213482b7b66d4a4fe077bab6d1e7d933473bb55f836388f1b316d0d3c79a4509062ef72c63f69abf704cbc28511faa273bf23d7bd01d62bf2cfd0dd33d498093edb9e538958ddc38b075db5638a8d93ee3f55b5ef8190cf07f4b19523e5379880ed06ff18c2f009eb019cc78c1767da3302a66212a0b8baa8510430b1c6c7e3673d692eda14006a1907ecae4cf0389c3e4a92a875dd842b8decc40050f6096da79f1c5931473e044409da1f4ed1c0055bc97fbfc93b335b9663f0060db92187048eabeb7aae8b3f3501be0707b6fe643066431f2bf641f4a77f302f9be5ad718342d100987670d9eb09d1e84003a940a901a6ef3af924fc16ba7ba0853a00f695d46d537a6a931e320c9ddccba0c6ca115415df2058aca41c4f3e6d4287c9da01683d42fa6cd7cec60d3082e1785b51b39a7b1b070f9a7d076cab16755a78d95235818b08d7503e110c188e70a94bc5d69c7499cebe891638721425a5f6c74919e9870935c6df74300708c3f98b4a70a00a4c6fdfd5009b460a5161c0d4054cc1247fa17ec46934b409622c9a23107efde6354f403cf63bcecaf63c2b4c3fe8e6df68ba969c84ecec0858e5f9041d4c26483dedc6061d6f431e4a5291cb27fc0d884fa41613345a150b019f9bf82180cfe755517b8d910d41710b581721c27064cfc023b9f7865b9305394310e0d4ef22b3fbe95337fed7357180d274fdb6dac3a9933c6c829c921f09d1b8c0813d322c1214fa50e727b94a0ff052437aff051f79aec8d7bc56f7680f6b613b265f4d9218f192d496602a68797b82da6e210762af6595bab3aa2f3e0a1e1efa2999c26d3be343fb310cdb60b3b89ceec12070886b7b1c2d848204830d5734d486bb299d2fb5a7a395da0ce7bd37356f0bcb8173354178de1d8aa7a8012e22b21d01651a5e43458b4799246856f1bf0bb195a3e49c6c89d529a26f1d04eff3e021298c7c6878dc999a035fbebfd625a266fb091a33c6092de4b60f55081cac116031cd09ce0809fddafc1db2bd548e151830fc28bbf25449f89e193f073ea034661987ec094ac11a03d926b1fb0071e770d1c32f232461c1807495d30cb87d2fefc16f932929d7816971fda370be3b25661579a3872b74353f69315709b1c10b681d69afbc28809b70283f9a782a8a1dc130bd8840170514ed17e17c0c2ed5dbce7903c1c083ac74ffef9be4b275ac1cfd9e1cecb7381160fda194ff07b35c08d4d93f0bbbfe29e4c8995a59e2ec7f2285ef2791a25308f2fe8d429506b483d4ccd025f1d583b925342f08a39c7b698abc4a983c5814c499028feee60984b4d1354220439b52e63ba5161d264f3c0c2e5e1423097fa5fcaf17eedad10345ecfc9f6a0af876c6fde33e49f904a13f0cfeb95a2d7b309bf3fcf035c4be0b336e2bb7eda1a7428353a2edd94e7dbd62ef0edbdebd1e67cd832392aa2f3901928f83f1b9e9d3fda356e97e4b477b83a1df2f9081f8f7ba204a5dc86f604505b771606ff08f5859aabd024d8146530ad89320471698962ae117edb84182a20055bef26876fd59a57b3c8592e00f940e4fcca2ccef054220a19031f4bdbd5b084dcc9d097d1e1a97318541052525d739d54ae831a6901e83c30dc3574ea29e0c654e708ff1ce3e2d5777c01c7d5ed2bedfbd84acb8b32f0bb0d30c540222140e5608bb9f8c7498a29c9251cba99f6610d0b795935d80f54e69d482690b980e0e799ccf966055e4ff42564dd86d851e85717459c28d714ea3809739899667e20d5caa1539800cb476b37ebacd7da33758ac04911e06a6ef1c6177dd814586f00f1c781b3e00675d70a6d8e99ee5f3ba3807df5efe08e59c4db765e089b6b57c0b784782f6c4e07602d306c649aca98427975370226a67c24cf8e871255a189d08f5bd3328a15f2420032afcebc7ed70b5df7a2ff6311cba3c54a30f35a33ed00e35aad6b91bb594a7557ce2c265363ee3f358273851cb121173108e04fb2c0b0d29344b18b64e7bad087b0cd65bc8a0f48d6427bf2330d5d3152c4432a4f7d209153b482cafb759fe479a44838da58323a3b818e9c417a2492682ae3c1ddf900401eea164b378e89fbdbfdee3964635fa36015dd55d599536dc5e4d48492a9d08e8ac06437effc8c4a31519edc03ce54262baf10d3c95d5fd7e420223a005fb0e2e90514ddb169c664efbf4c70b9c9c451e4cac4394686cd71f750355c319800ea1dc32e7edc0e50e954cf26aae6e5f363f31b8fcb1ddf387ea4e4b7357642f052a6bc1385ba1a47b464fa1694434586f0d4f448473a53eb042f2ccbee6a2ac05c605a2c7bb7e1b5a02bdc7a5a725bf2c7e1dfd7c073f29be7d82117af69f7d0e2ac3b89106ddc19343806e6de5ac73a765d57108a029c9dfabc2fc5f9f7648052f35de4d8aa05f1e24b6939dfceb1ef10d179c8958b90b47058dfaa47c1aa203600b2886b872b22762ceadc378fcab6ed01e348c4ea9e04aac9d335976ebc3079a0d4fc2a33121b3ef39fb51ac78694acf4686f074a1ddf61a838c15d15f390976e4293fc71b836685c186830590aba8d6982e8c39d9dfdb6d2e4f2693fced0a2bb32c7d3334d79aca2e78d39e7e920e5f408fa5f96410e83916cdd5a81f240dc5c74352e2f21e6ff85009c294f0c685eed10ed1be531187748310830f5f01062ee557441d98f4cca994dfc099282f1a84c653e87fa1d6f219a32d345a09960aa22c549f05b36153ed6ae9a40dee1fbc8a3d31be0bb923d5120e8f6f5c58d80a865acd7ec7793357ec19840cc9adda6080ea50b876c703ae3aa8b8ac088f310b838188449dba2b527b24cdd6a9d6ea9e4fd8c3719b0fce7f38b553fc7c775501082ac72020527a400def1370ef31f601d4457098df94c4e248a25582ccb54e0a2bf80ee9b7f0280456987b2273ea964d5256871e734ac4c8c17978ca9b4d1c0737ee05883e65dc2ed7ebc746f6112eb70360f31ba13ba8dff9d12bfda2150105f804aef5b955d2c54e89a9bcf38e456c0fe860782e8fa0380b249166430dd90a888b5d8f150d1a4ac386f8844d731e8c71cf960a8f693060dd747dec18561d0e45a27edecd67832e3ae1c1cf5527c6e079cb11960a0f00d28ef453d4ec2114012b52961904aca7aba86e8ff93b621540d89aed03d0635b6e36a100b2f45a5b0ff7faeecc71c6b601d2c833de8582d7b126a58eb8068494a994bc36145607a00bc49e269c8ea3450944c30168273e416c702b7ee2d40624a051a90b26bba0b40e1330c489b8bc959e2e7d87f83a6785194ebde4b1ae0a4b4090c223cfeea2d409af9a9b6bdb0c90720fcbe36938e13991db05c2f3e7f8851df672c831e805330ebef235f5c583f47cb369eee9db937222e8d78b60a4f7b2176004941ff303cc0bd6622905aa6fcecd3591b36af04b7657f0fb111fd62aefe695002b37c8d5cd03719f5819ed20ec22c4fd0e93985fdd0afc9d359713c41a8cc09b8ed4a1aae509000c96cb90a120238e17eec891a9b1726e9a88e25670a9b6f7365251575abe0515112ce8443470fbee1e8f123a6489942589a3fbd8382234e934ae8689d3910c21f4b200610ef5d057c72386925f7815ac753dcfdb0827b3fe26929766027c01151f9bf011c6ce032869d849232bab4cfb3c1f76027d3b6f356bd1d0ffd70801d02f655747b21a4856ffb4b2c64bc5a3e2b6452114d0e0ee79467c08a76d430d32491a37b37dc4749980456e13cb989f4473d05ecb12047305d4e7f358896b0ca282688ef3071db3e374b54c44b64fb3f0c820c2e7d349e9c1d20b09a9dc260c4d82921e2206a4ef3dac8ec4f4379e94ee11764a2bac8d942a547bd6f3d81000524f548da8e974878ea5b41239eda997bd5e5f08fa944a50ce85671c448acd0a8d4c6d7814b425099c46caf514788d616c0a34c1197fe77b4c20673eb27fea0d5274d5d8b2c4e77555be5a94d44aaf0fe4528cb55db5f25d7638be3b5ac3be040e2c680765d6f6601a8ef9a1adc0ee4395ce8e4c573869d102c6c238d174190977619b4cd33e37cc76a0018fa3e558c9ad25147d7c1d7f10c99b9818589f9d07becfc546e5a40c4d555fa2b4a052164c5fb7406ff33a629748c216c6dbcf4e0a103828f2cb082b468cf77207bed160295b1e701fc37f37349cc7cf767a12400ecb75a5ace0359008945d7f862466890e024ffccb656bb4630121cb8435fbec04a61796380ae9e6e7ceef5b0a7923b0d8d0a513cc29e3cc215ef1548fedd6e900cafa3de8fe5f28e6ed2ea6efbdc444d91a77ec99b64c7a446fb9e1efdced7a0fe62b40790857dc8f37db3a48e3a61bd92dd14ac467dfaae4e1b5ffad41362001a048355e77d1595f73f17e2f6675ca8a32cc6b0b1d8629e2ce6ce8bedf418a00edafbdbaad685d90ed65520188d813e583c34e95debdfb63f16011f6c921f40ae3537f3ddf802c3c6fadf407d9314dfc247632f00b6723de14af22825c793905 false +check_ring_signature 89aea83c794b629260bfc24f0ef0f13814f60a4fc1b50f814cd8b394c3fc7b25 9b7e30e9f91a084b45f029adf4443da864c4cea4b1f49d847680ffda51fe460a 168 f66ed2cd193bf0d63e6938336dc2336a7d8c15697e2c873a3c4d2883cd02cf27 9b153232a5a4bfcc325f58382bf6bb04c57b17cad1376f06544ac76567bfd100 82bd3bf8aa8a0598bdb2e9e9924b7744d122b5e18bd7b09c71286f486c82be08 7f694fa5f9a7ab8d72d2b2d5dbb4404bcf9a769f6f233ffda5589adc29f8b516 04f4717dc37955182c58ee245a72e25144419301937a0c042361e0d294b9c91f 0b8973b5c74c64a5964bdb4e532f38063c13c72966169989717ec4161cdbb3a7 a2d96e2d4167f97152fc35b2b02f4b0eb9a94da6c4b9185f0b5276868ea0b87b 9c6b39f4a783ef0beb71d0a88d04547b720ffeb5d8dbdf0224aa3223e641823f 1b9a767e92ecb052867296477c75340feb52ec53a5050ec2c0baee03d3f51a5b 41c02d0894802862dcfde4e9de99df9025ff5af5c2cd664d619027b7959f87d3 d61a5ceedd52af4a53e210de1ecb3e2384ab4fa66f0994e1f0c735b061060b29 cd9ca3ffd8c42cfefd7a324247bbd9fe688c02349c38312c2763fc1d210ee165 449ffdf9af49c1613cc93b1fe2c5bd7c492d17aba66e8fe12c0561dd45a6883f 67ceb0f4cceed9aee8470f62b77242be6629e2f2793ef078b879f560935ffd75 38277ad2f9fed564357ebf52960197cc803e5a22250f16de8eb420dd2641eccb c50639948aae9392e646c356bdf12580d2d4b76db7ddeb14f8a24fd70a42c641 c7db1f69022614af9a3188313cf9cd75f05d9abd52d7a732fc8bb56999213d1d 0f7c57eb4a5d5b034bb81a7f8547ada4b57f75e372940e12ea70183092213be2 1e8675c4ba812cf9b55abc383eafc3212943a22fba1dfbf4b15c60eec26b2619 74f9fb162bbbe939c1f212f65b9880f8c433c20b92658e07f126f2e507356977 4da80682e982ebd708b64551829b2ec30f50516b55fa661963440c108596ac0d 3f722306bb84f03b335588367e3f40fec01156e14dd45a90d51b39454c38343a 33562d9ad308850ff55eef3d6a21b4424860614d8a8eea48aec6fa43edf54755 cbfe9360d98ca6b4673bb34d401529325e5cdde34e38042d413b456ee354a3f6 a2ae92b328e20636fd263fcde167262925db5141e0c2259a2457bcda3fdf24bb cb2e71a049d8b6ef094d0a77990cb837aeba19e42403ed87ee8d28abe0bc7f9f 07807e48d5e51c41d2a811bb901d7f9ad105d7d72f86b573e1c90b5c6f3eb36f 2773b339607cc87547df5049635ebe8855f1adbfeb48044ce878b9e3e0447175 54a969ce74ba70bb3aaf2a06afb824b959e929c51a692409b2b099e25a1dbf67 633db12561e5c784ad7b2ffd1144aa50816564f655c5c24d6e3f22365b248cf2 3756fa0241f818afb68f526dae07ac68facff782c40a276177c45eb3c8ebb344 1f430b36df363b3f8ce69ba02083d6b702393a90c6735723c3e086780fac2f75 3a069ce86d98eddd61063347bd86920c366c2a9c8e79b2280a9afc08e905cd68 d74918204ed2a1d6c75adc6a12359d022e478607777f04772ebb102bd2f591fc be17b363fe2c02297800df8077e564895a046fe81dbaf615592da7ce10f7671f e7e6dd5f09d5c8ebd9f2d4ab14c2d5a32a0ee7e77db06c06abf109cf24e6ab89 2e586ab26fbd16ad6cd76068bcf3b0f3658f49aa1ec3b99a63e56d055f0e2f40 dd0f6964ac0630d54cb2012b142982b05dd317a8d4fa92ca30a93d6651f7f3b8 cc1c6eff56b1f5156ca332a0c6afe8f5d7108dd9fd5287166e65644a77916346 4162e6c44c3fc70f3e43542386bf3f84ba4ece21594249f02835a9fd056be206 8ace05ac4dcb0786bf87852ccb34cb9e986e1450b2ac0fe5ae0de3eafa0c90b1 12e2b6f8a81506980b7a5192b2c5c23fc96b1867ed7b2a9b2ccdebc1635d9c60 55c6817d773113359be920f6e9507fdee10fddcf4f2fd49cf0c804f1d7c8e459 af5da3ba99a312ebabd5ec4a0b00ae898c08575688d024310646ca81d3357e18 5738307911477f47fac499a4167be95a3d0b1e61c18e62450b4013d438d7671d 655756933a501d341028e78be6d32776b64bb5f8e5bc435cf414d315d3440444 d4390d14234b7121d52fa08e6943d1ea34e34f0f62163eec013dfb8d94b31216 8f365054828fa78b1fe42996e6df717e98196c77d96cb8354d3d602a5ad712ba a6ae06f2845aa462ef66fd71fe73863bb7e097605a0711f5d07388fad6d3b5ab 98cab22fa3be046fc75be7f6dee45c01c5f4efb2d45ad3f662e189ee59fea457 964b876f361324d4cf492f526b9508f0e5fee380251c12c07f9af48fdedd5f7d f7b7392476f8e0ce0a907929cd9aa747feb528adbd8806fd6fe9d55d7f5826e4 746c76c472d29701b7e66356ba01459d78dd0b898183dfea1803e4c433652507 b4e408d72329f92b1ca0c872fd888392720e6eeb992f3f69a64953dbc241f6ac 0a7341521dbe5e0e7af4a2278cd7c61a6db17d1441e6a19b571bd622ab9c53ee f73668ecb3a5544bd898c5d147ee1ddcd2691f6b3239cf4872c8cb2d138d9ea9 3445ce746b52ca7f08cc1a9f4b31afff3eb06656f7e5f8914ad2cfb2f7556ab7 a9cb034f5e7711801e27d43dd37a7b8d7fd0cfca41fd17fa7cfa87474418c994 2a784c93108f3bc403cc996ae26479784eb55f2a70a64be810726474a62f894d 8234927b3e9b33a1fd24db10d176c4cdcc9ba17e1b19ced745185867e6b2fe5a 3edb393b28acd43ac1f21c3758a63d84d566856c0a154cc6eda1edaac96c4032 f666f95dea7b8552ce849d8c3a718f002c0adb2cc806cfb56fdf0bc207ae74ec 142d802f08504c76adf8f95717cfb1c3e01d173a6d75c8031abddaf4656a33f3 3091c2e2add79db4f47146d7b499a5e554f642b7e293d5f6aba863bf1833a9ae 246c9b684087f83482304a47b4e9d77deae976557f0c534ee6cfaa5bc56e0fac b8cdcff2fc471278769291523ca8bb5970a6d4d88a4288a0fe595bc031a67b7e 1b83ff8a21cc2587edabfd42b94b0b8635ca64b5630616a1b39cc6fdf1f24ba8 e9fb597ebc6b5f56b978a0081c93865f48869bd2da11e67adaf481b03d0fd084 604f7e0a11777612dd604204adc99dea21994e7fa03a19357942dff65524b70d 0c814c911b4305f87c9c93d8fb975d4ed81d2418d0b28dce82cd6f17813c661a 92b9ea0d4623e5d97efcdc2f2252d5364aabaf30d406e51cba79f0dbb5e94361 f435cbc62d862e09c83eb038d18c9e39f084e13629007a6c780beb74faf41298 260db9298e750eeca380bad043f92c01289b20942abfe6872f0320be36463d28 c309625d150d28450473fcbdc06495599bb207cef99b3b6268f6058a8a7d8d8d ee2e8ec9810ac3d95d9f7eaf78c70b9135ca73747df7e304a5951b86abb099ab 64fbd682697557584f2aa9355f776ef67c71cf3bdfd36965fa79caff8e269825 8cd736907e886e4d832cbef4fb9e09748ebedeb472af6e3081cd468b0f086b00 bec05fd8b48652411c179af99283c382a962b0c0f5b134c17fa7e82f21ae8d90 3639bd2d705aa701f5b9c4e28637a79c9e26bc43fce023e45e01fa125a2cd03a 79064009cedd80ccaf6bff9749ce6e5e0dfe589f5c93c6a41cb98be76f5ab5a2 c7e39c8d9053e1f1572661cd46b1468e67a762023b4d557f70d4a7f7447753e4 f975eb13a98fcc9aad99d2c195987cc46df74fea5f04406e7b43f650ad9822f1 4c24a2c76f766f7a2ffdac3645febd47475209ad47c5a860b10185e3d0a15f35 3f02f54a69255607e4028cd366bcad1fdc68a97e7038a59a9d77b0cd1f53bfed 9d780ff58f7bbfcb97a1d83fe266a6973a5c55b1bbd409b495879309080d58f3 aea0a7dcfd5a982d715f98e8d5f36d1ee43b683b4061cf6120382656c8f53658 876f2ced6364fa040fd43dbff3e8019d60bb21e3b307e1103fa6bbf0b92a4b4d 95e3523c74b7ac0193eb997e92c07e215da9489953bb7c1c32f9777fffc5ff6e e1f8278a75b54dc0faf274f6012b489b9d7367b32b9a7d0ac4f01907b1a498e4 aa762f91f0fcd02b3ca0d50cb236f83d42a543ed666e9ae5326c91ce98a36c14 0fb8eb7ccc8e643ea457755068335808ca833249ea04791aa4c69ed8d26789dd ade7babc691637f35b4c616ae7d87c4e2e1668e89da783f364ca2e9530a254d9 cf8e642e090380a43d50232d07b7523c7792516589f17a70994bdafad2ba5dfe 076d13407e39624407bcdaaa086265e0f2cd057c7f62ef9eba433f0f10385956 14054e850052b51351a801c6a28cea566cfca42965c70559687d0dca96b596f4 79e6e195cae97c8ded6dabf6fe157c17018c0f4976dfb08f8ac44d2a7c143402 146fb1cc25bf40c9e28d3a262068a6c3727f83d20f45a4abe452a23d8e80fac7 fba6a57d2c48d21ff98521cded0f8ac37c68059036ce93088417414de1b0d16a 0016bb13cdeb88ce7a4a46cea2e54d7455cc0b2c3f42166444e35dbe2baa86e2 8c1f36853983cdb662b99da56d3cdff9817d015153184621e94940b6e2cee1bd e5f7f81aedf5c619be25c7792ccbac321a6133d8bb214fcd020185f00e40f3b0 8da44858e4ece46f52818b7eed2f14f37130aad93b58330a42a66a8498719ac2 1d74b51809db9a456f8f12f2cbf8c6c444678e283ddc27b007da98cbf53e3707 72bce9d65db3233ac456dce0a409bfe72a08a07a4f1bc486b0334ccfd02d6260 a083c57f68780c418121bf41c5bf09007db5082030bd7720de0543da7964b67a 1bc347c4e92d3441aa33e0e4e430c55560d4060b598c83d0e238c8fecb4118c0 01b3fd4530edc891b2ded272d4ace8c47e5718759e064a6679e72f2b3a41738a cd464a80ea9da00175284c7f8ed7ab002fc1a797b69751332ce9fb466a7123f4 f119dfb01d28c2add710fcecf9e2ff2694933776ec4f027e3970003e25b04cbc b21b8b7a62688f3a07d2f13e1ca1871ddb3291f3602f8bbf9b582ec57aeb590d 477bb0a9fe5ef49585264a24b4ea3af14a296eff0890b42632d4e79b8379fe30 ee05030cac12f6daa4e65b32cd716427d4c4468e29345aa2851089e31c650469 12bda1258238df8439ef4c5d0ae58074becbfa8bd8c59d1723d3f835a1cae9a3 96e145fe633a92b28b6857c4fc8086e2d46ab2fc21bc573ee07ae22488f9444a 392176c451a670e572cf7d23e9bb07fb56bf5b1a2e7087101430fc16e67bc4be 2ed561d1534bfc5882d9cdd6f2e6fb09323eb5b871924f88ecc48e3acadb7d0b 3bcfce3b9f39f0632329ce2d8716477157665a05f318524601f28e9c4fc0e562 b592a09fba5b332787969349d08d654aab22e0fefbcd7a150ae23e4d7b4fa8de 771f12090da66f94d58c6fd49c1c4581c2c906094fe15f19924dfcc2572a114a 7dc0ae17352cd7e58b71da7cf32f12c4a263b2215e899574102eb13583624bf3 9902f3876150361c9cff711dab8d96195dd6ae4b0392a4e22d67ddfeb5d43966 b923b4842589d1802adfd45b4ada0ef8146456e9d5dde46a265ab8330d73a71d 40920e657bfbdd85d5b48a8ab7757867cbce782c8df4817207d4c86855a4c446 54e601d83ac6b8445a9dbb91e178cb4684da0e30addba3b42afc02f337bf5565 2392f31a0d256e10a92dc9cadf30232b74f765383eceb32b683f1d7c4fe50f6f 03f3bd100f2f7637b1b72fe3907d96b3539db9d28f5f3714c4ff932cefd142d5 f3666aa7c340821ea709a735653d82d430bb152c797429a31f2ddd39999230c0 688ee2b08e4b14800338bd17a532096ec8c82e31c3e6b2ea1a0b4fa018bab6ba c3f9b022773827eb1564dc2bdd97447a7b206fe6376e6623317edbe728eee758 9fd6b303dbabdddf412a5093287b518c153ce29aa6fadd566b66c57f922219ba f37313543a6edd22c57e296d80039be7fbd004465ed8224254853c0d035f9961 09db9ad0615634d3f528b32d55cdeb36d2a6843c985e7ad95a53aa6b2f84f1d6 d6304698bbb352603bc55e520b881c9504db5a1b59a47a3023eba70e157e7825 2efb912299a343cbf00a243ec4ece53221def38c75016e3e4cb887f9087938d5 56cefef8be44517ede4bd273eaab0562825e6046742bb36ee83fd514780d5e5d 8fbcb80842b0db1b7a0a2203ba47db079af5d419f6b2ef008000988cf943d254 ed65e47865ad3c35f3130ae470dbd9ef56c7cd33a516266f414eccea5def97c5 2ba44d146b6269bbcf1ad0f0824a1af147b4415d992772c4a60037147a1077cd 1d7d4f5522a3b4ceca63197dc9d0370a76cfa2d13299858eb1e0bd86b067084a 2056406071e93d8119815edc91e7c913bfab1c3a009f1a80a9efd80cea3be005 bd4245bf0cb712bbb4e006aaf82e3df5a5275ddcb0d90eae598186b0136efdce d36fd5f052235e9914d5ec0669111711cc4c0644902b169ad2689f1c3dc78457 7c059201c0060d80f2ed58056298af40593bad7bc02205189ace2d8ca1a6b501 a4d26e3cad324ba4b9cee2cb635b81b48e813f6c390cea1aa9a8ab277413edf6 d0f5e338b282eb6f0c8fd1d906a98e0ab436ff0c4aa66dd5e0746c6596aaef83 162115fe553d15076a60d654b9053a466973492792e6658dbb4c6184a82a6e9f 21dd3776cb83317e7bc1a61fd758a11055bffda05129aa9337da6f1639c3dd7d 364098e5bceb5cc076b6c778a26db781f3efa37f492db696ebfcc997aa3749c7 6289a62d926f37f485b6c678020dfa1e0348a8362dc73e0a9977c4fd712271e9 ade3db2fb1ce0d26e5bdfdcf16b1d6d7e9d28855ed3029b3ffe6be6d056c5b7f 37afdf68157209b7ad39f9713cc9aed307859d74f6bb7b80b3d1938aa42c1dea 0b6d0497ec8f8e7db3fb83b15d58c479e2b7ca0076b7c7a2f313539971a1b51b a6fea8f58351f75cdd3499c0323b1cfae66b06f0fb63857537af3ddf55979d8e d698a94597465a514ee84a652f4d79fe0968f215a6edde6aea23200b595f2fd8 7bb7239d854140c3cbdd1b8e16116296bf01d53625d98786753b06762d4da585 c073b0da11161875158737daabc33842b57dcc325c8a121013b202b92248e504 2c2b3676827884ddf1da3dcf3285d488f0f9e909179c052737a8493c873ae3cd a9ac3f46b2b82870df953179a44d33c2396a7ce6bb4bd61f50de051fe7fdca9a 11baa44d864fbba0f92de8582cf93acadd30de313d11a75c5e870f9826dc1fed 1d90a04e4f15b75e6f810ac0a889023dd469750596433980f9e91a32b961282d ce748ebad483683fcb6539d21f63161e189a57f367e8d3374b7a45d053fe866b f2476b448eafd30089e6d78a6d0f28947b29d4d4c40ca37b953789bf79c41406 012ace75a308a425821459b3e58da12a68a40a6d4c62d8456771f194b24fd784 4e6b778ec867fefa20f4a2e0acff4961b998a41c00e499433f73cbe352363244 605a7c5c5275ad44e2484b581979e86841f3846a88546101366b06b25dccef76 ca2289f3cfa9a68c87ecf3c9141769f7e34f1ee704417678fa1f4b13c0a0edfa 24cc17c76ab9237410658b4b0756f7a9d77306b489f7021564d6c103032b343f e5e4e4e8fc063dc5b5a7bf1c6e53bc82bcc038fda734c1e726d5ed15da091dfb cf179dd465ab83e21ce80c9f425ab6e97c18c703cfbaa16fe5457c979c29fc0055511c00751097470605933d8368dec36ed9b5c3b45d70270cc5bb70c266f800a7e87a7122ff47ec60a5733240ee9a2a8e879f14b63410bdae52d61211240008257b0acebb5d70db0ae20778221d6be9f9d65c67f4d0eb498b5092b7fe795209bd18a8aa352a9b98c6cd0fec8af2dd5bc03b86bd93db806c1e18d2633739a10a940a3ad56d599ad234347113686f7f7859d533e34d6c4f7dca8bf5987097ce0d89be07c997a2a5a1f782d883f7d16da97b3584836207c67daa7e3657691125091b224735962970b57d901c5bc72833e7ce4b666109e19ec9a8ecde1496c19f09a650137826f255cb7e81e93d72b4f46d3a31e7e2d7e0d39aaa0fdff4e77ea0026a04cda0017aeb72cf4ac0d8c9484a4889d9dcfacc82058a68376aab5a3c67022b89bb5f15dbe1ed85f026c84413a315fd5ab70828e01cb75e87debdda4960089e5701966bcbdc76068976ea687474c1d881049fd2582d68dc4c0b48e6d73f0e11a5158b0ddf9aa67b7f3b31c89338dc4b44f0f70485930d8e3a4bc3aa39bc039807551f40dbffd7620798ef00e19f07ce2dc596bd4f1029a5857eb13d46f703a98b32f19ef16a8280dff9ca2026f695975fc0fa5df9ef59a95b460ba4564708d5b24b677bfec6561ce9e38d85ca01bfb0043f11ae0ac73d5ec49360690bde0d46edccf84efe71276a8d48f5a37fb65849c43e5fb44a27c64309b1781f19970d6d8c73880bedd917ef4836555930878c0296c1f23218cf5dd8ca225b2093d701c7357e0bcf9b7853eb89a915f04659da849c98cf4bab9edbfe2c42b0cf6cda08547e87919c2273930b233feac991593ef9147d30230c4e5d49a3ef3797e2540f581e568a99fb644b5fe7783dade988d3c88335600b1515852d0f44d2f3266303cbbb07417c772176659efd951fc911e89ff9ca6b9c11f53c6bd2b61a1ef9500a7026b65898e263f7b68381a0fbdc6d7e0c612f7d1ba14a351f04364871b51109070f5b6a4e172e36967e1015d525aa89425edc15cd3fa6862cdfecea86eeda06e0fb58a4c02dd9ec81066227d9072c1f6b82557dd7f739884d5d40b15bc25909744bb1b569019b146fbefd0c659e0fc2730ebb2a77e2bf5d1c313fa427d4a80e51a1598cf317915f23026f9c8ef2428e662eac925b306d5dd95621c841f79e0bc31b949ea3cea50e2c57696cc2f13bc22b88dfa9ac866fdb988a376dcd172e07860d2e0e2f6e824e8086674a92153cccbf4932b33fbdf6459c2f984534552c0d1f94f0ac0e463d0ae9270179f1ad2a5566ba5f939b8751a94c6a92663c335507a96ce1b35069d98909848b89f739f4ae2f1cd2f9b7fd29fa4a25639f3c428200a9780ee00261dd00c413c0c677df4f138812dc400a935dc1987079503565f803bab5e03d4e909806aef295391b0851efba1c4a7c33eb7dad89b92412a575bd022fc8163bc5aa4658aac9da91e073b7be803e9e292f538d2769d5ce55c19607006f9f5357c50e917ec202f818c75c6df764b53821868ab9d84d4206ec3ec0c6002604a3b621b3771118976a3cbf877d7c55a2cda894e64364728fe50d9b316e0000f3436bc716774ed8384325d06746db383815c9eef9af856863458a1c78d7037edaa337df6f26fe5107762afddcb94afe72a3834a02fd82c5c6c8ce0984ca00d81b0fc235f1285f2443baddd71ea82cfb4276ab30343f39ccd06c53b37d8c0736756506a48696632237463395d81d34f3c67e52cfff589b410654a4a2aed60785b7e378fb075304b66ccf2c260b0f65ac1c64ad11116dbe1c6719b96c96380633e035db15750b3820b7eb3f6329da4f51d1c752d0e70056e9b69b998ffbd3006171f01e33bacaa8c2f1693c65d83d79430fd76519fdc7b91ad83b4c2ba57e03b6288116092cb1e0be89a5b6da79cd30cae1cd06146b73610ab2154e4b5243039d3c167add18065836e111ed1b94a037e0db89d385022e14274644b2ad9084079e17d5c5e6f6363d7b1dffa7dbdf7b3b1ebc69f3e284b0e7d8c4e9c950b6300f320b4cbf387d962c1a8ed1a34dad013421aa6ab65cf36444ded00395495cbc0ba4cb3c5dbd62d3bb33469e9317b9b2862d7690a0cd2af5b3876c306eaa55780c571a0c8627a2cd27fae4229a9bb54da41e801d1b40373ffa5764918aa421eb024a408e1f011c73cc59da8cdc9194f8e9a517e3107e853438ba12288ba78ff603099b809dd31f836892833b104a1dbb9b97e46dd55862961b132062d077fd490f882c0aa5680ba459deaca26b0311c2b214a10f9c30c75346c6b51f634f745e04600610daf6a12de02ce7b20f09aee6694795c9285d80f47f89577a8d54ec950f4dc57b476d9ff9d3bb734c3e509602e4045d7c7e35fe715c524fb69e360af20376a7c1ce5381328ecb2166aab18518e72d4671d398ecad985d4f62125d0701004621ae086c08885da43b6db8dff51b42d28c6039ca0d191e4ddafc584f71f103e2dd53eceedfa2995ab839930ec05a519aca5d194e103c102949abc80464260e710d978926fcc23578676ece3ecc344d02c713656a296e551ea4ec2141ee250257c58037e9381a8e39693757bf0507954c829c94d7cbd2382a84899a804ab209b5768297d9c8b54a13d244a60317daf5f0fddbeaadccce98ecebfe41abd0a10970b3ef85390b058107e44f82ed214946036c475f1ff8f27e15e77ef93175660f1d56fbce7d87f23978459cbc99da75465991a35dc52e270a30f776333663ca0bcc3d46bc59a8ace4a8d0b59d6c789aae9ef9918ba4f48661c5750f4365b9bb0b6c0bbbf55605e9a86c5fb9cb8cf5da88943fa554aa43952d7b8cdd38a14a190f1fd47e046b0af9ebae08af30a4993aaaa5c0177cef4a52c13f5f3022a4f0400c8fa533902675fae30e5e656c63871593f0557852038e9e75f49ea86908938d022285bd3358cf147bd06eaad9f4464d23141c12a6cb6cc7159e0f0ef912cab407fdf0f32f0ab30ff63dfa7bc3c704eca71a6858927ad99c06ea3b97c0dce8f10f3a4fa62523cc4c50ad5cc3309194e2cdfcef705c1aa80496ed48076e528526057bb14e4a55b34db7bb720d275a32f918a0e8cfbb793c0a962aa26b35802e5103ad524d66509b0c48a8deeb52577ff9a4ff5b8cdf211e7fa9ad56ab9728c853006483d5b518569c0199ac36236230c29144ea8138b0e39318f7114cdd83c84901ddcae974ee29014cce923a4b6c823d57a7e0bb280662ae77ad2507fa638bdc0db7f91620c77f28a578e52074401782cbfc40234e54b82ca6815686b8afa78603c7f2bc3ccc235918a41d73c68323991a354aa8789f1011fd9221111b661fda0fdd14a5763bb7a4578d111ceec9493fc740989d32a73841f6c45b355956dc8a0f4396ff1e468402e9500ac95ce377cc5b6d92495f14b6d8a44b5537ba854af40baf9989bf9d0720180f7d460567de35fee5232e5b3f09a014ba2d22b362b3270550e2badb08eae6d3caa1b00ebd92965fbea8c34bd4228a315b048ff29d346a0ae6ec42a07a686d720a15aca61519cbe91ed6ecce72e0ccced6cab0eb5634d100ca48ad0eab992301a3007af2f6ec500addea163d4b921fe14b941b530436900716c08550d9893067128125ab5304e35434e0bcb9d9180b3902a75f0836b1eb0be2a75ea1a1a67cb946dc676986782217b5a5c0de3aa76ff2b7743b229862e5092ca324abd10fa0a0ddd383583817e16eff10a42c58608807691241025d78510b2b40130a4281cce2bbd9eb2b2ceeca69802550dbbeb7c1bf7bb369e292b97a0bae7719e6d3d1d2b78b9947f895e96eddf0532b76e4ecdff266cb6f06863a700c0b9ce72b49c527c340af25d60dec99fda3c42b1c9739ece9f5ff7e803cc9650436939cb568bc0327a703830d10742cd4a3ffd15c9f083587522978b9d2536203c13cf7bb1856df748ff9f226b4c5ca86403ac3bef2b26a945fb98af2d82b140e20c9c56b72664524080e61d70291f9c82e7592eb6cd24aa3b456d17f95d85909ca9f7ea6eedd0a1783b08c2d7daa9dd7215477fd304d404832d887f70cc5170d484d75629f49c88f8d9b3d4db6160635ba295aee5bb5ac5169d5d9856266f905077fcd9deb1fd6938a80bfc7783d1b03f8aada4e712d61e3e024874b0592900cb70555f4422821a5b3024bc7487bc7e0ca2c09017444674cfe119d2c8a05490fd8bf254b6eb88ee442cab1c3620e786b9545134ed1c6d789f0b8b19579338804800576f257f6d21c8948b4ae4e1a29d230a3d257a51bbd0c9b3849085d07b80cf0fd3a681b67f3a623c179000921db0b432033cd7bf6bccad74bcddb9a1c950831c2a01370781b4f829831ea30dca683c35248537a74455b4a678f1d7bc9290d670d49bf25463beb236a6b2631344db6250bfa5680a601559aea8e1fb9bf4d004fa49eec4f691c727aba407d4f4a6c2bf5587d12568087d5b2ed8e87cf51a60ea61ab61bd90b2abf02a77eabf6c6a4fea7a214488e4d7b88967546543828f00e3030560fbdb9b1d252c07bcc5c4ef04b5f7a0ec7853b96b6b35241c0e8ab1b02e3da0c9c4f8d7453488ba5f406381b64b0777cacd58635f7dc632c4bece5fb09d199fecbe9b06746493f246d9b0305cc6d596786725f333f22eccb0af3336c03243876ac9bf5c550415bfd9b79c882b8fc1de654db6568a167e7f9809c7ca70cca367de7e414625fabe6b156d568f05786c73cbfd11afafe51262519ad1ba202e3069c9a185cfe90e60f50a6dab0d06ec5f33ae4b31bec0310f483c9cdea600f383172413cd010874d9afb807f01420db16a57e9aad7f4d5a3992f2c7b551208bef8e5e573316c33547fb4c1f376ea81518eb830495dca297e5ff7c89519f603a1d6bba032b700d113197a52e9a0b2665d12031f9161672cbc3c3e76bb7a630aae0a9bcf2deef77807dd6e3a4b6b792e8c157cfc571a94a0198160e1a1ae680f35fe30d963e901b1109fb5a1420e961190eb7e68607f72b4ad7674bc670c0107700a280c1030d6bdab28a5ea3ac0b27e417060f4f1b354e7a6db59195165320ca1dddbe998651a02b1519afce9cf5b148644effb39ce5db13d0b10ebbeac5e00441e59279a7bffb95cf03f10dd4c4f7f816b8ca541551f17426f9d334f46f40964648ea545b86ae468729886230a7781d83552eaadd872463ae99c22dc40520b464166d4d59945107009ff51454d3c2abbbd71157f916fc202832f12a48ed8068d846418950a726329b4a574c515057a6c7e614c5ed160bf431d360c0b45570c84cd7c6caf2fb78e8ae0ef9027a3bf1233cf97f2ee3858c2fd527f66bc36e201ec735c49659414d50d3848ef92f94c86436452ff54ea80b99a99c7b0a829ea0c17791f772294866e897f5a4c9f7d17e223786b9936c8d07312857a73fb023e068b13d20c40864204481a01203e42239240a2d773c2578e64ba8e09fe3f62a0036ccdf751ce0befd55eb0df358d0a87ea7b5e9d024f89e3502071d72eb9311508b09fc5984a69785535553711bb77dbac79ef6f77a3b1e1a1f6ecdf4da56b4a0d2b2be9aa16140ce7241ae420457425d7fcfec6efedce525cc6f49ce41754f60b7d66421e8c1a29e4d7661f28079a2fb5d63ae294bbe83b7bd2cdb8662db7870bf1c2ae13e29dfb70989991254fcd281716f7ea2bf9af08c21217512f1be34f001a4335fee28d678cba9fe96a1e8855956c5f2b46924228240673952ab81aab0f47484f9e36d76a7d4242caf65db6e890cb75f6e6d0b471918d90064b38ec3203b409b561593d5916173dc89842e419192d69b915fcac8d28e47f355a58572205570ae5df68cbd90dee57b9dc42acc04acbc656cebcd3685c452e41ea9823800aaa3e4a133235ff5fed80b2aa687718cbe30fbc2f827c7c1c1036f2d097707d0a12910df60b18f6a8e70fcd31d16683e51c723a5e2644b830b293adafeda5e602ff819e6c36f34c81c852208d5d4e5211f454e52f028c9080d6bea9318fc4170fa430f12aa1b657660ebb45be1173f225225e5997fe547b5a59737e7e41e4fe08cc5849af43c5273fccde2f831e78c3d6c61308669110f7c8f72dc7759e3dbe0c136f65937e8b03f770332c837bd10a241ab5eaa96dd252ecabe8371bf7d3c203fc22b075c59d9ec8fe23a6fca62a3249348fc389666aa12b1bc94d80e7d0390f4a97c8d06f68dbccbd61369d198fe31c71d71cae6d34078547f4b9dfcc7ab506f0dfd56da41dff246bde4d6864a39e4898cc35ff51e54bc5c1c3e9226362d20c4a5e42db794ad2ab593312e0aeb7bf3f6cbdef3b5716203b99925676782790071283fbbdcb8fade4f620b56fdf756207abdb4ac345584a50268a7c79f84f6a059b23cf34cbc235d4221fea08b4fa40b4a20bc8e90405933fcaf18cd61aeaff032f93ff6da3b63a39b36061463320e3e6ecbfa7931b07f39fd3f94bae7fcd590294eefd55881c8bc91083c5c10e139fe3aa69ae3d1367a6dae9bb7c4c062aba0c953682590cabb400e198e05013fead7bd8ea131b0a08d23e321c7a9280b76005a78d49b04fbb96499783b9c539bdc73dc7b3f4d4406fc011801db793c0632503c98a240f489ae534db62e63e6357728cce61798f23112719110e5dda1ca25b077986867d41a97bdeeaa536829d1274f8ee2dfd3fdf8c73ed4948ad5262f370089a31097974830edea0b5a0b9c9b5dc49dcd1dcfafbf3a3fac10302a8a9234106dffb8c69c01d576cbce4d315f64cb8252b14eb4ce2ade316b84dd04365ea320ba2793feb0152409801ac7c3cee2b5cee1640e19df2e2a43e55f46d5b3f776106729de36d4450445a638c85b76660b9624969befad29eb4fd83c29c66e95f0d05e1174c6e631abca15f3d5d65d44f4b4d64991d53a7c08463ef6db2292614800f0c0c594bfa1015b58959724c8c416eca43bbe09795bb28b60c7582ceac27e708cc6c363dc929d1fea9ad0a0de473758ab591ba6b4def6d52a8b1e761a4c08d027a1829b65d5dd17ea90604664f163d57b7afe6bb2fb18c1d087cde63c7729d0221f3a252edc07e86b6dcd60405e07455411c77c664c423aa39232bcc6c87d40a5e7d7b903ff7edeb2349c5b75fc47ace7b7a56959454c8fbb1f94863524c7f053affa9bf61920223798c9bae1a3c28876e1a915728bb8fc520f3625549d2540fa9ba8e37f1573a6581169e55dbdc8bf4ed792bdf135cfc20dc8c064ac9db150be58f743f3da96ea33a9eb5ab8a929a3544f6708ed8e0ed0247f584cf34510a07617910f4df89d7efe8f54632aaea5357bc073bae59b89e18ec49c6ce002ce205c695e5912f224a3f99249ec2f8faf8dc9cc94a3fcfd3f65c29be6a9fe60d4b09a82adb007cd0f5639c72c8524826b383eb2dd13932c08ee78b6ae064e1dfb50a2263f52e50cf6f6745e6737552fc57b8b255896164c3bbdb8e4a9964543f6c0b62a9289383201a12e421b3b2400943e67a26432fc34fb9948150fcb263bd580daf56b2a34f497ab394129d26f1b8639ef8d2655117ea062feccfe2e903734e06a1a9b4629cfc6a767bf6efa73c8c02478ed3166cfda4ba34ce7eddfe6c242908fc9d5754f2e69ec8ece3e530d970ac696dda0c1bc1981beac601f76c238fdb0022e388d38851eb3af719fdb6cbdbdbfda7f2a347d49557898bbac4b20b82a3023fbada8da9fe5d2899de07650384b310c823643f533e437be6bad8231fc97a0c99962fd3ed05b013d7f72201a019c5ea7c882830ec064d2bc6a89ddf5fc415025c93f31b8ca1d49215fc13a3bc5cc6f97bdb76de76d3a2d176b027d9b563e30b71dc59ff9ed1420cc37a28204f69cdb12ce24f9438f082566d8f8a8467edd10e0b41eb6a1001136eb1898d81c1acd5db7abd382b971998229b41ca3751fc1c07a24e7ca0aa4aa99462791019716bec1f4d72c4d1e034bc9bc67061098c774005279340064b45a032bd9c892ed24aae496bf3ca38f0d3517bdbc1f370d99f980d78ba763d3c36eba3a856966c8b98a68a267a6416a7779bec028e761c4435a609043dbbd24421da83548067784a8cea43741596961129eabbf83b3d4d0140540d25cb4fabab0fbfc7ca3faf451f340e913db5b27d7decdecaca292886fb64f906bbfaf8ee174dea23ea8d6f7df8597e8bcd46ca61ee7c9bbc47c59d90d862a109bf3a7f919aa46316591de8754ab97b0899485d7f67a1210787497a93ebbb070097e23c6434c503744c0e5a22e4eebd65a0bd493a788e6ea86a63354f845b170529ce3d1b49aa94571a8dc40641e8d9b91e627de9a92a6572ff59470b50e53a0cea559efc4f764627945260725c2b62a4aef56386f90aa920778265ae060eb5059d2cbcc01e7dc91501c286d8a095fb449bf9ce98e6d345ebab60e7ed13377e08f9eab91c47a7bf06e746be7b3340e43843ad3856ace441b1dde4ad5efe53610d43ca3de5f907f0e0758b952f5ea31c6ed77358ba54660718be171f174a8f9601b6570d355b313668c11402b528a1e716ad581c8608520babfd26597af2cb9a09b7efa0c0232a54fa66fba708b770b691472221ab67dbc413b21017203ba2f509063468df17455b32c0a170a576b9f69c3672bd4461b2d104d307f14be0b8c2077afd7e716fdd17595f9b28011d864dd680e6f14f7328ee7b864b3aca360f190532729a84ca1e996082f6aa2d40b8a01ce96ab495ea444f60530747bc7034ed0a5945b15fca0315a0396fdb0071d4fcabbf6584c0020d23a8035820b889828d07747aceebec268e0d334b57b71a1d4850cad6ddee53f1b04d410910d129f0f90efc7707614c9e898ba028b49f74b17399619174aadd9449e14dbe56eda46d1e0a62e205bd6f6ffb0bfa5b43110378bbf5ba3cbb60b942ce2e47eb26597ae42a007aaed6bf464d53779920fa3aa0d9d87d947c3db2f0f397ac584392f5732115012fa57b12e8dabbe1d026fbb1e5a7a66ebb615f2f5bb989b01a5f9c932407e80ff78fe5667e441620fad0608529bf24b59fd970d93ac8f654c49aa9284d296a07ae5ef8d378df949e36c38ebb863d775be4e440edd30087b5b27502c9e8cf9e06c31aa6831c373474d23169d2365d8384536ca6cea2ffdffe7749438f023fa40e80ab60d7061b4a85ea6c0b74471816f7d7b8da78593495707d340fee892b210ba12969bd9273db77c56e9b2d04f6c148fc462bd18add9b128bd5e97f34aae50e360f5336d6fa4d186da21c7a5a87562e9414181714f0453375e5f9e66c8deb02a8b02d2f44bc8ca80e9afc88ac11e65f5ab121fae89f710cf76a25638364970375f39d5819197fecba82875019796463ddef1242fc2b993bfb072f1ffade0b092180162004e529cbf07d4aebf99eb115dec633cd5303d33185a027c55d834402318b23c975330655146a349d9c2ea779b508925d23b8f02993bb30e741f5060d1b663f97b6521638f6643bb935a95f6bbad30c3a4f8679a811d6799b2fa1e703186efce1b4f352b380a57349f9cc928746b77850f7a291a924929164fccb3d0078902763fcefebf901a159bdd2a85cfc6691e8841ec9f6519139443b8c8d7b07a799318b3ba9be28ddaba358af0e031f61b2264c217e6ecfd5f0a50399629d07138940051476c5b21cd8bf0ee0c0acceb76568d77ff0886e501d5096d3149000b1330825fe6cdfd4e6641202815b2bdc82656777f9ff96be2e0f6ef6a5a0f50ffe22bcf566229dc0fc1a2f68974f972d2082fc1a9563f71ee179262b2cebf306a3d867c9543bbaad9787b4497648822a1b7826122479e4386e69b73a0367170a5b9eb596275f240101e691660523a666c01b3c04fed6d3b67ee37bea4d4a2d057c835f037a97a0d0b05b4855c8d4b9376625ffb6a79f38d988e8fe9b78eeed0e94c139a4a0dc2d96ca05ed1b8563566d3e6a2a49d96b7745682a8d6d1bafd8035c9ad3eeed3f38a07ceb217d825a32f774589da1dbd38b578328f70f4489fd0620ad9b8c2ab6309e8d602c41873b9e1b990fa59fc76873bf4a84aa86ddb7d60c217896f2f50fc6e0aa083f0b2af4017eeea8f443d608e1c622f61c99767d260dddf3eab4225be22bf32e55c53d7df83b2218bbfc9e53a020e11ed79e06ca4108ec7e8ed3a6b64f1b8df14fd40fa396eb56a95fb99bcec91416691176d9c9d30176ca7558ff1973b9e69663c0ae586eac6ec3bf6e79c2b8a35a9eb8f2f9ddb8082a8977de5dc0eca119a834d9e3c56348460362076c028d21e1fc0be8b963f104538a05ca951d30db73c9ac1bb6ffb37f6335a21668bb6c385194bec29ea99401c1d66d9a7e61411c12e016e9495279daaa57572ce5b4db4beb20c18287663909388ae681ce0224018b315ce4cce5c92ff5e900b5969226f9afa9f23bfc5e000f2d5e9a26bede452e4d7bcacb2f84bf98e6cf9243e03972e5840c5e0661bf780580a16c34b94111817244fb55455d2b28dd915eedc229f5aa1b1fc4e9d2d3130737611361202434877bad39b1abda5eb9d7ca842a98cf38d8c41512ab5c34a30e26dfd6ed313184f4ee684ce8ddf86561d6f8fb1d2e439e11993e2fbf365ff30f8e6ba9dbc28b890cad99b3e28b9d1b70e0f779c5e9ddddeb5a2a0936e84d5f0cd36528f9af28972dd2d4346616b1a950e09fa645d3afa0b3f6a75d0831b6ac0fa109b9c197dadefd3646820b5597d05a5a614a5c8b97fef0b56bb049853d1209659378ca65ece00f77278df14d90093fe8918b47750a96838aef4372be17a407313a3b97ef0265b6d618e58032bb6386748d3673db14996868b9afb6a6cbc00c6d8ff1f757496f8024c8db2a09c3e9b765297833ca11904e8e3c07875532990b0d7d967378fd8ea8acb08e2c0ade606f728ffa31dba8e32a05a56f186c68090a92c3a94e52a315ef47ca2429194f95504161150184abc700d8a2357f4c9ca503f28ac3cdd1c558c4aec411cb398e90c8b00fae9b5292011f3ac0c8f4cdcf2f02c572545cf33ecd07a8cba6707c97872206e3d39d587cfd8390ec4f9ad293d60e68428b4d18a253f2227bf6ba435128c0a4ecac5056fb25377373638cdc1dee032c02d23aaf9994a0c12ea86f5548275bae9f731c641b6e8530ca324194dc920e441185f7ebaf702d6b716b7ed4330809165f84cbb96113a583b61f3e6578560e620290cab412c87099f67d7ba8777e2d22bc8d431a0b8c46ab6b83d8378f5b024491c866e5816e596396d3363b5505c14f8dde81bea5441a43ee826cc1102e0114a1a58f04a8c0e6fc140b5eb24672bd9989183d3f0a33c0acf008f067298f05f056582e6dabe617f553c413c2808221f7c266da73048855a2ac36fdd9d04d09987fb0b07a28b56b96f7b49d03c90f3ed704b816274625ff1cc62c3e0314b20f2b18442ea73ff0a339cc45895740b6896c6f982c58a33e087c73909993670f09f7d82e87a1104a3bffc581f4862b22bb7e231373878349b9f5f82014ddfb6b0c6b0a299c03125cdd9f6a4015f8380ef6bd762e7b1251f47bd7743e6351c2970998c57bdb86971cba66f5044c78e8a7ce39f323b7a7f9c4e0f41b458480a5f20028e24c37ce73a40a3fcf944f1187d7a7256276b29e039974d3c461258a9bdd0993f1e17adcbbdda7a3e94d6b14133804c811345dfb11716b6e91b2f2b477fa038bb68941352fc8f18ebc22937240b1d649644ef3f8df6b1b28b50c5517bb38075fb75ecfe00fc8828410835382201693c8c0454c18109c374234645506431303577e101b8a27cf251ab9c69435d57ad20c0e68e4c544f8bae69359e275d81507fae59bab0be53b0ad6534d5885352b0bdd252f9ccf2a4c7f8e49c9edc169cf0b70c3a432bd6a8c85f091568af553b7681cd5cd8ff5ce07ee26073ff2eb42150ffc8521d5287342ad48d1f53b30fc1150c72aaf2078de85c1658959b9ee96420fa8ececf5c31c569e8ae2f141c880c58fe879a2cffdc1704ed3e9c118923cb209b6c3ef7217066825b6f1bb29e3aff63859e2f7e0bb4a48890a2acfac29f4d106d1dbdb130e36f769fefe816d81c4214e6e5526ccad5d5bf75d290d756643390919b05c76b1cf841d07fbe9daf3863441d76666c624d04bbea9db56092e2842094a3ad929db0a31ed18ffc32ce3125c9c7b342f57d2ec81ce78c55e9251265e0aad6ef6196123309242a1aa7cab17ec5adb0af55c1f40c556cb62e1e49fe5b207efe17a6058252864c5946fa8176b4ae3e4effa42d427d5b31566a95401ef5c0c08ef4cd987498f9a73d1520a995a60e2e7cce6d6159b41b8e972f68636cec50978fe81805325cfdf9431ea964a1e1141b6ad4544ca5f96deb79ec6987cc52d09e1f3567f638437914043dd949e39b08dca19c9896b207c5d823af7ee0e65ef02d5ce4c3ae77cc31c5a6352ede35db622f8c49c77fab6f9725a72c8f2b131c408ee0d55cf9f6365887e756011f3adcf48ae23dd8ee99058e03b811cebc9ced90e529f8c95f6e7eb70884297cedb9437b2f0dfe130bbfdf4355bd2687403885c0d3cddee6c52ca85952dbea2a1873dc6c64abe1950182cdcbd47a4935b111d1908d7270add44eabc518f65913150d6ba51c5a39bd9bfbe5987d0d72f89380c200b4ff8baf05de16b9d70fb5e108653390518165f343824616976469050c9418809240984e90bf43a013ef858a24056f354fbe08abff64c6585cc48f4447a776405333724d949e005059a74c1657404e413c82c764ee684aa7c52b02209f31be80cef577ea56b2be04dd954165c2454675e42a97377489b8a0943ee691a27152a0d23738ba0f9f7753ddb040970f2da03b7b28de02d2d7531512ce0f2568ffb65077e38db8fa9af12aecd3ec0f92d1cbbb00dfefaa7ee7a32b5ed18593ae1606d0918c15693c190e8e3678a2dbd1b046ab96be37b0b854736236ab503080b14510889394e2128195d460b3e93feeb13bb9ce080fca6c437eaf1b2b1b0141d8dcd004304a3d56cdf1f8d9c9171e0cc9210abeb28b77fc033feb4f65df9079aefe8041a517d8c14fa0c9a61e2f048ab7da6fd1bdbdebfd6d9d7aa8139b5278bbb4c0d390fecabf6f83475eb0a0614d0bc324257bc867d959adf96585a77e678dc91018fbf84b98f964abb8573ced08fd503093eac4e4f849f19650b0197a25c6d200b9f089412cf5cceee2c6d9e8878f7f1ee2ffc8798855ba739fcc3409c64772505f650d99757e084825290b894a37ad6f6a6142134e9bbbba8c2b2234711f6a20d4d48714396846b8c08fa171e62b397a3aad9f32adee8b7fdb2887ce5f593fa0d9113f6a6c50deead85d6b6992f7dfe66bb66ebb0dc994e46473668ce6e2ad20891a08b2df0f78bffe7187cd3e70fd961e06964b1ae533e479e9c979f42ea4c0706e3072fac215ced907bb2da8389179dea1d71e11cb29e162e93f79c4e311a010a64b4b8968d7b04e5001ca478c066a96b7f151da217a5750127bebdbc1d1807495bc1ec81fa78db3aec3526b76259e790758a4cc9f33b1b7793b2011901f2044193cabde012f3571344e76f8bbbadc23d1ab246a00bd9c90ecc7c99310f28099b0e654af022024afe2f42c1d156d2f636589fb7278ab5b8546eca7f5b43fe054a788003326129465d879ea5a26d5285c43c0df68ce807b2573e40b543ff450d68fe5d60d4b5aff867c0468e80434a8ff6a36ec0432d0c1610b965443494490ec50f2f277c63e7b1ca402f3addddb1a4ef2534955a7af270236ae5aeb764ab0b980eae4947ce133d668a894ac485587ffd4bb0249e5f63e8d452e365c83e7a0ce542307eb9867460c84bb1d86b8cdae1fc5e18c4ffcd4480311dcb303a89c30cb61ddd05b245b2303f0147f9655936aafd41d044be7de8f109c211aeb91e87028f3e1a90122a0ae906aa8192d01808d1d8c0d37d596a5f5d399b61746165e90005e1737d4c75b0e0a761b9b4d818c52df2a667473ba83d7eb0f190ab90ddc602d1b702c6ba88173020f52ec49728dc07c6e6134ad6500dacc345873d5a62050a887a64e13160888fa1d29a930e791ea34ab373a237bc15e8f7eede5797a44305738d9e8c38725704e09e3cc420ce8e8190e266beb77bb8c4a6b1a3e36d4d7b0a142fced350bbb64fc3c1edb07e0acd1dc9c924a360f3e50cef9905e66aa8f4086d7bbb24313071d0825fe4f803f1441524e2dcad7098ea0b4a118477b6896a01d2c26bdd4355533e8651b99f3ab6784dd3a51ee4a8c6adba1b15c469e45d6404c71eaec967c7ca8dec8c7d9099117154f89768f4f23588b9757b74d942de4d07003dc3605108896bd955b7e2f940f68b0c80af9bcc702dbf3a0c8bd954a6300b268e626f5689a29ad7602bdc8afd015ea41ad2456c8890fea933dffe474a9f06da1dc82260f3e69914474cdc981799a4a763c77aed43f06f6ff1c633eee7900533c33de636dd01702c3d4206b10c559a5c7df96cb74801448edd7575999f850b953eaa577be26fbde70b7e5e591a5e9af347d171fd7adee367a81558c504350f8fd648e97c8f464fca26f2118ad90629b39d5510fc31e567c8d35901b7782e0a6d45f699e8eac5b661f0c74688594da66e4a2583672d1b7c953e68b6e7318009d22595e9fc62f843380c2d581aa1c6dc9f8e7d5c000098f3eeec4dd67593530eb44e8f50fb6257c2ce9295ef3c960beff427999730a98ed83f7b8315b4d4bd0fb2bf95f5c7c723e3010bdcad50ef083ef3b90b397b94e535ba48ff7f4e94db05db9d961cbac3a7ceba9d1c93cba2128236358a320409be41c743fd2d41a9fc005f38d70bcf599920ff990930d72d5662fa8ad17bc18db68f71d63c8a375fda0f58680c22a9511bda4a115902364f759d39e6fbe9c3ed65e0012a2d476db35c0fd06d50ea48bc511d6d7572777eb361444626c56b137893609213ed2e568dd60a1f267b68af5664366283d2545a77f384143eaedaa3ac6cc1e1695914230eb208617f171a53bc40f4964ca7f892731b0c687ad407675ab05a94e432766b86ea042df52cddcfcbe31b2d4b625f23d3c1be38310619de63d90852f2b46734d094082b5376cfa8ee866b72318448cf53e148520467db72edc5f8d28205f17405ab028d282f3ce02d70f1bdb070b46b4771b9ee3bb5b9eb307c15621a4b9d13d5bc03 false +check_ring_signature 3ef8a035dd83c4028a7e20792433b697e4d0645014f4b55c1befd3a3d1d28e49 f35ffa3152ded8fa214b9f96f2bf1831ef057fab36653441c323bcf15ae646d3 16 d67a7b3afc7a30641b47117ea0fd1595590270d6cce9d3c632c02af3fe0991bb 04458b1d4974deafbf41a9bca053911a0c28757682852052061b2f622378e369 90788e1582e4e552662c1ab6f383ae2511d8c2aa351f634d0797ea2e47b53a06 46e70f61e4e5e4643727cbc1498361f581b44913b376d2be9912123226102c32 bc96683e06756f9dc669c5d9ca442d2f0477a4772442ccf82667616be1c31a63 0df30577f65aab05a24c5c1b001fd843299c3f2043ad45cd9b28af302be8b599 7e7b6b2633f6a34352986a2dc9b874b7d6b57fe70624853049714c992bb6590f e3c8f8ba2d74c5b17bc2824bcfe2f65bfbce22ecfc9d922a14ba2a26cc9b0ffe 3898eb3a074cf9ccb38ad4f8e275344df39d116bd5f0e99e656fb0c16a281cf0 e48f263e2385de591f64619dae5d047466ba875d6b59a95032826611ea813d9e dfe336bf452dca79b328b98d3f7d51db6de6cc6f2aa9315937c7ab5504dd66ae d8ce242912884417320746a4225cf6091a94e5f5944a64c39fe30e5caf216860 c5890496fdbf63d6aa7551fb3e267b190f305338dbfb81b838676972c2ce8677 e1dbb9c4c051c99814a68aed6c67a26cbb3ce5a6ac43733530716642878ed5cd 1fb9aecb17f20436124d00c1fd69e1cd4f8f2f5fe02e40dd938e6b7554f266a0 65041ee2b0205ce900dace88daa512b2c54c691071be84fb5f943b8a10d8c3b7 9db8666d58edd9fc583ee99d44621744f7726fb18d4f83990987e5a21008b10a097d94495ba188b10e53017f1b724ad596c55420bcbf32a652850017a6af5a0a3a89c55f9187374d13a08f617ae07709f3c1e0dcc26fc099de3c10e2385fbf00b9f64d287b901789f6abb05019e84339b3aeea6a4ec3e05b7e1ffda6e13c8908a4ca8789108a3824d1af3271536fff62872bddeed43d8df882902d8a7c02850b690325e6bb7579584fa12ed32494e76872322169d65bc5e00a81737a55c97507de5688a787b033f70e8c8184795bd3afda1c4ce59b74afc483730aef227c170b032293ca99129da653694dce48897dc2781d744d9fec0c6a89395c479d43420359fcb51430fec7ffc885207cd4fb71eac59c74aa62523c6993914430dcdf530ff003bdd67feb777be5909b72059333ed0d65d32466977defb33c01e675a77d06eaea753a89b7b686e8f2d764976a4c8910c5622fb8765fc2e7aa25a705a6dd07f6aeb0579e378d9171e7744664d932ffd9addbe456532b65118f84a6c4f949072427ecfd6e1136fdf7b71ef9e69f28e01a4fe1b8b2fc787e4ab2933e2277650f4be93d93596de1c500c715310179122811742041a129edb6d31639337fb6b00eeaa03b20ccc27d235bb8c0f72ba4ca95d65a82f288b82f64e458d899a739580fc6b9828920ea23dfd38e2cad54511e9d5acf078591849590e81c9fdd1d5091052b7263aae197f2641b53d6dbe1b908db2989f499fb139a27991264ceb4515c09c6e2ee0152b68cc5765946835046313e5078da40740c1bdde804caec677fcf0f71e29b51142dfa5d0f69fbb2b74db3c9e84afd78359c8953252784a232561c95e72a1ba9a2045df3912ab0b494e7118b745560ddea416b3cd261a29db43ef60c30b29b12711b9333ecad2fce8044713b280afd9c9a2db751a3840cbb29c4dc0d5a86a0e646fc577fadf065cf7d79361d8cb33e098d338b8420d00504ba1ae30cd818fd7eac5e98fce48b12e86d045f09a1b8324261ad63af39f5cebb213b9a0498e782cd254847716cd8acd9896a566dff4836a1dc54659fce57d0be4a351c06ac7d47f08ad16c0527c41985be6e4a0e8ee00b810de285d22e7e969ac07feb055f1cc950c33118624a1e80a4a408540e9c7c16f13f50c55b540e638162272e09acc8b0bf02f78610cf6fb76de8848cd9c3a2be9f14e13f9ef82bd0f4b9ae080283d2b0073f404375ab37b67b86f8befe677075aacb2d7d796ad8ae314e18ae022dcdcd65cb7606b12102fe4fabfb439c4740cbff98d2577866cb90360ef62e03d98ae8191af29dd0531798fc73a537075c70721fa3bf3e94ce701d1f9d647f03734be6338fd4e9bcadd80bb39af52c51a532db4f0879d871c16106d590773f0e33100b62c72a3379f1693e585047c23f541d5a08800182aaf3045f3f9ec4620c false +check_ring_signature 0768bda593dc9dc36994376319908448ebe4a8287536cc6c2cca3c64751e38f0 8adda24098e9d6a41fe00f5445e3e5a007478f8a4d36be02e47828aee962b02f 63 ff95a1d1ed9ca9c65f60505098dca23f2e65e8acd109f6c39a4354f1b5b4cef0 1b24b16c8cfb76ab7e12daa4f1f2c7e39131da0a0b7de87bd381675e4472f797 926879b9fe9a5ecb99b8ca1a502c93b09d56b9e86ff2ae8ebf20b4039f814932 2450d3a37bdb11be6b2c1b80bbb7fe2ea867ad3ec213ee55d69fc1f4f7aa146c 0e6cf0e5997e9a62ae2071f36a470911b61366de3e5cd2dd4c8fe350246be9ee ca0a09fe84b2c873b54abf44bc98f72a1c1f6cfb8d097d71b92d564766d58efa a8056c45a0ccd7975ba230e949058f6e10298b5bc79e6b80c4b2d2155fc4897e 796ad1030a175a45260e837c1df0d1ff68bd9c414e68d433e373e0a89e0d0c57 161ab4b11abdb6d1944ad210591b5f70c2a354ee671ad36cf8019e415300c13a a1fea0c9a897138cb2dc30a254edd8847d90e2e7ed79acc18e5295ae8328351a f4b2b55fffd1c1cdaa6ad426f34a016b3f78d4c520f3f766970bca2317e960b3 9b9c19e92d142885197e9abbeb01a5d41a73ef7fca109291b2ea011b5d0e1179 37baab38e3daf5f3c1eb52cee61ecf7afd24f3f2ebfeab88b7beb36ddc431c59 78c31cc5c0e7cf52fb4862f88b2b5af54b03570c08170a74a1e8c97f93dadc3c 8ef8dba9167a812dbafeced91c7dad195ebadeed772d4b095aa208d006889c9d 45976185e6d6b72414b3763257e01a696b25a58d2baf90d29042af5fd83da46f 652d72406bc8a03748f1bf9630d167d55f0a4a614a51651f654d3b4ad4d132c1 402f173cae78f9eb864f718a1a1a43a6ac1f638ab1ba6e004c135cfeff35bfe8 c84b409c05bfb19398469c3487485aef818d825b9d4cc7366f81d1e663c51955 a3713945bc62cc0e966851d6be5e9e20b8391a1eadbe139ef08c434f19727e5d d23370c002c8f172f7fe1f6e974e725e6a449617fedc591806063717feb9f347 f69bbe88489c8042aae6c3442729d934e922225a833a700a17b6066181465270 264c7593001fb14c58219ba0c636d8a70b04f3057dbf8b63d334881176139f77 caa3057293b2c6922aa2414b101a141266f0e4cc185f340234e6460cd2112616 f6693de6ae72b9859c2ec878e1be8f2b78ed2eb0942bd8a6c33df20ca26e2c33 e3dc45f741fd2683663a080323675cc92ac3058301112a4b9c6634d83be55fe6 cac4bfbd903e5a7ebb31f3eeb29840ab8cff1d69bcdfe9b44862f47e500614db a40746c5078aa3e4573885b5e39703fc92c7b08bc9127f9544c13ea9e0417798 3e9936ecb4bc06eaee37e6238cfc4d26a69e07814f96eb03b8f68285e4d29889 b4df2c837d71a81f7bf9457ecddc37f87ae1f8a6872c1687442c4b6373dbd35a 2a8b6b4e0719e99787b6cf66fe26e7e89df89fa593b61cac672143ba60bbcd4d 9d2d9820f9d7c1acb1e86dbc7ad7f128c27a81f9f15aec3cf5610e6261a2a803 f491e79c4c5c9d7e1cd165db7230ac1931ec5fbd8263dac60bbc6a811c1a0826 d9af423e67f0b7da22bfbdcb56b2e72890db9de62976cca6795269580978fb35 e8cada1ffc87d5c9a53fcaccf01f668f6921495067bd2aee6f21661d94c0d28f 0885ebeff840f83841cc882441440ef707ac66caa852c1d4803d0f390bea73de 1ec487e581b8ed211219ab1641c4b36f3475cdc86e6915d00a154d7aafc02100 24a3eec4ed1fca3e5e7e4f6739444b358d355ed5ce63009b69ba0c57fa94eb21 799536269b23ab6b58dd51e75412922558260ba3f71694e89f85201a362a5324 ed7d99a085b1b5ffa0e238d2aa2ed3abfad89ef3a88d5ca0df22053fc6f2289b 56f6b047979ae2bf054ac02a84a68c760b84ee8bb6e3600814f136d0c18eb5b6 412ac2782fa25d7ee4f46244cf05eb2bf11e48401f842520abb4a174660fd786 8756571d71b0049dcf3e90e9ac0138df533e0d18ebbc9492f159731fd9883dc2 f6caa685453a3c5e2c92aa91eaffc17d0039f6176c5b4625346a5716a0f1d141 cbd009a8bfb7a73e8852ac80623ade86e7b488b393c48628b5aeea457efcdc6f 986323af91057f5155c0f84ea9bcbc9fd65f441089c8438d85fa3f5daa3f30df 09a950e895c59c8377e7e96dedb628d9d942ae90d0303645f75ee469fa9eb9dc 72c3c760b6d2be9d11ab3c23dfe1b6f2f39a838f26c0e4c3ff5d92ce3342410c 6a97ae4aedaf0c23ebd1f86ddb033156ddd4a653d45a18a5b572ce273504f4d9 bd8c493ab63d26fa58a7466dea7a3fa3bb5fc9c4a4b0d6eb75dfe10e21ac35fe c7f41cb75e04e56fc768510d1fdf1edbab5fdb980741d79364453e7050db5f07 e4d8d4834247c86ac03de1fcc25e924f6101004ee08c89a28636e54e80db607b 41bc66ac06cd589f27edf6e412fe71d029e3fcce3bd482839c547123bcb1e2e3 4d54b794eef5a69cdad22650c6e8c0d0501f8de838643075664d150a664bce31 31a81dbc39595696a8bb6523c85d3152b4808e629b1de2b44d328829e4067f08 a00b6c31aa7a8ce0bb1c3d1682dc88fd2c7227ce56e8fb46edd8cdb4d121f0d9 f2171e9555bc80a4b7e654371797f887036c108b75633f2d0479cd8f9462f413 d07c4bcd89cfc3daa84af35844312d6a4b8e14ddc811c53056b6741755e1dd3d 49b765fe01847eba693db2baa82522cf8c66cf58ab842ee9c754a475b1229aed 2bf5ff3482f690c321423f21b9104f97e60b0eb0a430430f7aa4c92609210baf 62b9a911eef096719f04bf337e0c8690f8ef5b32878f187eb5f3cb0328f6e369 5e2b74ed175a5d9f74a170a7ddb867bf84686bd03af3766197d455b2e8013561 cf93111a6d74af52daad282551dd2a7e148642f6a6e46107ec601caa7cd6e54a 973e3ce611f585812556a28d7d2b39093c3643663afbbad10f86f5d3c4d4590f9025243c84b2bc162a689165218c17e33b07062e32d50d07b0a23e52c2b43f0ff4bc3c32298280034a3c2aa0cadefc68989d558d597fc1bc4f549b4bc8a5a103a61cb8dc13703dc179cf5d4c518c2ff6b8454a2e0d62954fe8589263f3ddb60df9a4f11eacd4cacb273ce0e6a772cd070d32bd91d97430fdc22a3126ba2a9a011b36b6ea910336f44813c8c2b7d55bd55774695dc2d4b0a007924d6870e3b50cb705652cd639ec0e644119fc21002ca3f2195c273aa8043c2697c1f36f0da00f1dd28ddf6513146b7f2de6cded8cabe7c14d5f4c49b074c1c9a92b5ca1a9140b63f41b35babd48d99eab3e82e4847b915fbcf3f06aff0ff1f674b40418e7ae0131c602a38cf67bf0e1295ee56e91639dc80afddf11f4f6b4c19ba12d2ef6960873ec1431a9645abed3d6af6aa32e06a9bccd9910b3bc7b753a8343f43317c800772a56869b7e057dde8ea3ebeea314e8626b1061e55587d8efdad07183eca903037706bb572b3d0fd5eebb3c120969ba4407bdfb909d93158de18b53f9aa450075f072c52aa78879157ea8926a56f89e28688cdb4d092d63e22d6d9bfb4b8001f54c167e0199db621e08fa0fcb53fabe354571b44c4565f6c56dbf909b484113c577db1a59c754282e57a2d18412fb5cf3c47c3ea44233498e5ea0309c291f034e5eb584e9bf455be01e471c4af5be1a90433ac73c0048621e0d319a72ad870308eba8c7686296fab11f1d8e4a33345d7431d1b7e1dd83dbb6e53c85211b220d4e3e26e2c1ca75583f532aa1aaddf7c467b20d55e8a32e9d11c5354cd4649e0e9f29a45b23a815fa40d1da90130a0529e0e43c995b0c79272bc838c89a1cac00014d1dc340867d7ea0635980648b72cda28064df4ddc1fc22733292e5c7f6f0d17abe9676645c47d7ef3f8c8fbafa879426cf8ba85f60e236184c3b8db38540b28b48944ecaebc22d68b25c7654072e0afbc50f23c3679b5abeb066132ef7607d3f0547419e2de2df287c9f83d83555486421433382432655ed469fcaad6ed043bf99e15f439f38b02e780b9317d6daadb0b1e427f5f022e63bb9eb27b532503ea7bd5b1d009ec4f2f49aad024370d909e67496700bcd8a7786ccdf42cff570ecc85a263cbefb3dc224471cc882caffeb9b70f78859fc792a291b759732aa50147a1b22ff0890a90be4624c657558f248b0106089065a25200e7357de5b1a607fbaa8b947f5883bb13e689b956726b24850f86e0ed3d9f044b0d0a73331bce0a5439c455132d9fa2a80d58a623615a03ca4807e395492695eecf42eba339ea072e0ba08a64c6fc54772c819b6b89d154910cf150d7db4eb78404ddb81ba4f8084933e36da8c4363fef4693449806f897bdbd84048c25ece7409d253d74632a05ae4ba9b191f0d10054a14aaf1fd819100e54f75ba5b09f557a5eb74e97c2070c89af03380b03c9f2979dd298a0834e4b51d3c3a9705ff0845bb7db69abb4a006f9207da82d748780d69f3315bd8e5c6156bcacfe90b7a294ef885f579982d90a3f0f02c58ecb5870928906ba51c4d60f8e5f9b94a641077eba3a829aee08ed0952f70abea7e54f3d9246d6d81030002a4433d52f3fc65624d57c5c039597eb016eabf411df5dc1ec241a71b6b7338aff4240a85da04683e330876e96a783fb0512f59ec7acb5e6c84f9a4c9dafa2ce1a94f72796db99ee03c7a9809174661c0f2fc17eb21fa3decdec3bc5bcfe2e57fab30eb1105d760388a760ed1500c77d043e9b5eeb9dd0d922968672301ab1c0043e17abb90b4eb0eaca17ef2386d9c8077ad97e8898fc74ae74817a267312b16f7572e7de560f962c176c13e1f2040f0801567de285470e05d697fef3d1b28c562ab44a2761334abcb7af7d6d2148a60182daab4be4e96f8914e911ccb40cae4f12b1ae8ae9d17e374981c9f00bf5c500c5a6ba93ee0af04215417ff5bd219d23d9fffb4a620b35dd9d675e3284b59c0fdc10d28c0c02a99d484d769666e7491b4c3fe4111b0eceef1c87ac643cb50902a9ee79e1ed8683c8deebeac0baf0ee17c635adb3131ea5345c577be2322bab028594bbcb32f4a3070edb055ffa1a4fbd9b9880338e59b18fe8755514ee5d5a08cd5330b0f08db6ac6d31fa16b9bbc34649f33c7edff731d58e5fdebc4efdb10be5541026529a7eded73385f48ff801d949aa794caeb81476d745c7897d4155080938c7ce0e094291e2cafd346b389eb6d19683e548b674371ab66f6c9519450ba98809d5115f7bfd2c52861d77e5221e13d75eb27f3e6c4ed1fc638dcead790ae66c518669d966f6999079165cdb7476e940dd7492cf6698f3740effc637a50870cb1057763709cd18f4f6efd108fc227da4725be87d095f279800d29c5b140682b82b12c2f6e3f52219388141f5c5578c8e50578cdbdea66ad1923697df790fae639ae475f70c838f3cfc2115ae619eb7f0a7413495158c8063b1abef8921070e094d0ef48e9361ce753736ca4de7a80084b345994ef13077e8d9ac6b513a01159b6cdbbb6cf46f3fe924912aa0a48c031dcf442ec78e1663382a39b5dab70e1cba06630714b78fb84286ccfa0bc126e0a1f4709c2f0378ec4b4f355d950809acedd13b25398d136b8d949319648bb5a3104a9627b105712c2b207c52aad3067be2431552137dc56fe883370bf4e9bd851f98e4d3822ff9f3cc0aecfa908f041483caa48a12f26f5ee61ce3dedc63034f1347cdd8286b7dc0589eb451301d00321fe99abbfdcd2a290db43ae019d753ec42b01557326ab150a2bf386f2261097f9c4e5833943808737f61177e71f1b7bf52aa917ec16f91633e5c6511bb730336b0a08ee3dbcb0b753dbde3e2b0a767de8d274c7b68df79cfbedccfa8d6d004540a8fdfa227ae8b02f9c597736bc12960dd6ea72b245f7629f6a6a566f117034fc365ab2a01ee551a70cac733ea9c07348112d99666f9094c30ba0f116cfa03fb5fe9e8e2a518430f08d9ceddcc961847773c694e430afb32b72eeadf86e408b61c879843f4cfb856c8961f0fc8e4cbc456a894b6fab321c6258e3af6e4350a75cfa4d6be76003e48bf5acb947ee056005e9f98c496885e472b20262c4d1f0f8a32723e7e5a431da006761f044f8ba3e14653d2c6acac023072ad5a0850ee09e9ffd313cbe363da3983b01d3e92f85a8e022517eb0981d23e1742e50df6840b72b60d87f341802617cf9eceeca21918e837ce19c80b3013b2cacbb3a68e3e0f71332fd1fd5ae46004a16fc9ebee16bdc6cb740b5cb40684ff9ff69098e3dc0beb3d936a9c73b8930dd6e1fa874fad71a4ca2bcb3d1a6f1dd80a99dec0b1b50f1d1c7c8a15a995709de1b222ffee1810e7642f7ac1018acbac74a54919f3c506790447139a1a0d2710f4977fb8cb031ddf23a36ffeeb1f4ad0716814c5593704bf5793d47014c5c0c79bac05c01b5b09338328134b0aaaf7db6515c5ff37bb0ef637790021199fd4795db629ea42c0452dade447c056d5017fdd46c62f579f01e367bd28cfba18a412fb648839b258b0f86ebd0c1a306134da79017e2c14e2097fb5adfe6baf293f6f2bd9ba86ff098d2e20859965bf9b536e922fd4ca1c530b0231f8c8ff0ed67be3bbbbd5876697d57d5ca90cc2df1dd5149e0319254da00a41050916bddc955d35f3d5cbcc48e6aab807d99aaea0fb0d65ee1956157e7402ecc2b99bfec5b59c0488dd8429f2d0f1c73695c0e08821d15064f5f2ca368709ca6b67ef767aaa7ebf56c91231f61eec79b1c6452dee9c0b333b0ea75cec8a052d880980153fd00685c3e5494daf2a81cadb651ed87a1304ae17b483f400cf05507e4ffe44594e193b3b7e64c6869763a736f5a1e483448622dd925999b4610314ab17481276df2b94c0d8afc38a0232989bab3cedc3f7c54b8a6ff9b4b68f064093ec512c0566d6786d6dff9803ebba371a56ab1f861b706802d2e01847560680182835d3cb80a8afe7de04fc2c08f82f9dc183a2ee1a0fb92c9b9ccb4b3a09eacc5c536002ad743bef8edf12745adf5567583224d283e813e225ee9e587a02abd5273c8f8924998832b8772bb459b191b2556501a53b71dba3325db2a37e0916deeb99f4315cc0fdd141067bd119039c4b7531742452b703313e6fa876d905075072f4ccf500f514b482fb749d2312fca1a96e0b8c5c7c0616aa08cad77008f3d8671488cb5d8ef59330e4a446cbc2bca322ba15ff0984599f6be801c8aa0e817953a16351b23a58e987410691a494d00f8b4cc0fdfe84b0f5557cb36de8074de671983316914881f54a0f1b4057043444c15511c24fff8e430ab046c73a092ccedf6c027e852668ba71d37859bb9c13a86810b9ebf83d13156fe9e75a410c3917d69afa3ccdae4f1e9f4f72085a407fa554dee1aefd982e1637cbcb41af0c7ee3d048fb5b5478e4bdb67ee33d852983b0e4ac1664718e70657f25a6bb700a6bd44e943efc5faac885fc7d07b60d11becdbccfb0090f6d1aacee79756326096b8a3d901dba84e303d16634ba6f489b63c217d265fc6d2670b89798224ad107ec5a6cb3e76829589cda4e6fbda30bb4d840e75209813067c39b7a5fc0442400ca23e6983e4716999e467b13cdef349f8caa6e4fb4c3f316251f8a1392184506e1d2ece01d90d82732b1257ec98fb2f8a4690fa83caa1fb79026041cbdfa180388da2a063254d5570daeabd45aac396e43227b71a98f54474a70269d61ecc40b1128fb6e1ee392c5b039e932ffffd1b11f0d12a2cbd5acad626ee8005c23dc0ae00d7a6f0cbdea7ca51887783c9c373bcd62b387d998e5743fe413465a4fd901e50fb7a4153a0303ccf720693a723b451ceb9cb41a2f68269b4992eb0b6ec00dbe96d4f24f4a33aecea16e595a31186c02f3cd27a918696bc3aaad128c33f1093255dfcab07e36cd65b10c19bd306dfb80d760d1d3ae5e1832148ce8f696c8058113716e442fe578bf52fee41723a271cf15bef951646b13b9b60fe083a8cd01e729aa63c75f33479c98cc8199fbcae8fca44bf1b7fbe2f47fbce4beb445e80ce338acb7fd7d956717782cf66d3a89e05c8d23a2ef1bec7a0da913d56730f2094ab107a46cfff3157a14175d519a82df2abde8cdd9c446de931941ac2a7349064484c75c02b973f448ede761d84a78f8a802196eef8643bba709be76ce8a950933fdbaa87dc016a2c91828f7372a4442aca92655e3bfe748da92a1c08aed7405d2a0dd6c2ba83ff316015fc748b4a1102f1ff8c4c363bc90b2666d2f96427a0601bc8812764d316380cfac8965818a6d992c2ce6aed1362b01327529ce0dbc05eeba0fd910ffb19e276da6654f4f60f2cab3497551ff52a85dfa6c11bbf091002e95723d935d7687e1dbf4deac08a63ef0f2bb5638787da4d755d2f92b14790d6742ed61af66e738133214e958c6d5aaee745eac80f8a226610ed8ac2927f80439ec2bcdbe4499394f59c2d0ac3f3177054f11e9872cd42160fc853b8295d7050f54219dc0b514258d634cff75d345cbbc5b5e62666c32afbd49da865591f20b88e8cb734f50b28163b8a085fdbbb5087d8413be8e0267aabfbe3d27d035f908a0505c7e0fbde8e63840bbdd0c125aaecb3ac6f5e42e94809f9d06411bf6010a false +check_ring_signature 91818a7eb70ebed2641b5552a0ea6f35f462f996d1bc78b3679ba0e82d48aa30 77b51d6b32236b8d402901f09c1e0f828eedfb8321f1c156f48f8924681ed42f 7 302be8c238a6290831834bd74bd4e6ba0bd8b39e8459ca2cd0ee3c1e5c904de9 77c46b36d213d9eff8508f456da95bf0a1b98953bc6e30cf91da0f10fac44a05 96f9e00c2b1dd14bafdc2d114efce0b925da42a1bfa943e0792e3933ffa0f2eb 17e513f44a3e295a510c1ddb388104e3e88b15388a2286a83ee9343556870a16 1bc768091d1561473f5d23edfc55e2bc2f06ae57014cbe8302b8746bf210545e 904f8c0e11016a7ab7e58fec0c99c6497795fa24e0e95f4ae574b28aef127dae 56ee2b5587ccff8f591396ef72003f9c71dedf9c84d8a199eebc2d25bfab48a7 3fd9742512765f441e4f0ad64ad61dec45d86f20216c6c0d6fb4a8a448fbb101e21979b5c4616e8cc5cb3635284cbdd3e229b66ebcdf08d1c3b9376e2f171d00fa037a3ff5f089084de3955e1421193221cc1f5a03761f20c8f2da59f062b60e466ea0dbda50667fb83a6222932752e21257b555aad48efa360895cf5828c30f2c6080807bf669f557a41b5f5ac0251cb207734b7f16a931488dc6a6b4f8ea0fbb1adf405eeededeb42c5231ed1dfddfb7b06fe0f05c4b6a728e1a739202020dbb974cc1e5ca2f45428cf6911837d85f63c0d76296e47ab0dd9a8e55e644ee01db75f13e2d1d86d6c063872fdc3a11004caed751f4d3f6c3080ba1361b15de05741643dcdf3385f487fdaff2b1eecff3248c526520851038e10920c5897262057622628921bb263f8f946436c1016965b3d336dcfc13361e25840393d04e4e02eab17c0095dbe5f1a90b20aa58d9321e1e64afbcb109594730bf19a2d9b15501f8afe5bec6220fee917a9eb83bf291397fa13505c2c8f73a21258cfe10131a09f0468091c89ef785f4462e87723d6089a79aeb720213da25bfa6d93c79836f038a8fbfb85bb25140c1f2435388bdec18f37ec3430ca7a1562be4571ce731170c false +check_ring_signature 2398bc0cea2d9049210c7401df4a1bc65e60f98eb20de4d76e4a19cd778e6b32 1151bf25757bedd93caa242acdefb0e274fb0908f8a3d967c926052949c3e9f3 78 1a4082b7a638a7bdff10a0443c5334dbbf1ab6cf7f29ec2cc36073a0e356ea61 6c07d25e7141374e4245efdf9a73f8595900209ad8669b21fc0bbaf6b6faf6f5 c80d82c22a6fb7d43aea80c870bc1aaff2fbd11da76d3ca66aaa04c77f73bd86 8efceed2499dd971d63ac037ef537b3a455ab9749389ac9174fc03515c4e928e 0814161f177e09bb22a7e81f6da8bc8f9b2b76800a19bccc2fbc7b6cfc3a7c93 30bf781aefce4a5826aadd6f79bc6b412d9e492ecdcbb188aeeec0fdf37f2a8c 9c647eebb41872c6e81ff226a9331ff6c33881d79a337e348190003903eacbbf 6b80c17b102f960c158b8bb66d963824ac0941904a1d50e8a18afb0c7c36e0f4 4027ff7a5c8c11681cc9c261d4240a617c3669c29a1ec41ea92b75c5ed761c69 acd3603d56b8d88081a565490b2ab888908ed661f2bb4c3526c2caa65315d13b c23de80fd58e602f035bc12209cce7b9f7d7e6ce57d0e36062198bca279d0f18 3dbaebf936d1d14f0f67828b042babc5a0219ba6a1271140d416b8f05667bcf1 5ff80427f02a32a0150c7f08f9cca776189f585b5feec29e8d366eeb74b63f4a 593b9a184a9f939e6a6df12abc685b2d555846901f15a4715a1ccba1cb1e9274 e90c4b0b7be6d56b1123bc24b4f9f00f35b0681c51f186bc12c3a3dfb5fba179 e63de967a2ffc261f954e1cc920924d4d23dbf740f6606cb70744b441e22864a d79cbbb76740124a851ee370d63663604670b6d405faf3f3bdcaec3f53631580 9e472a2206d65e9a7c86e07308d5df7402c12cb7f3f60a01967b664a281d8c6c 29f5ca020aaa402ee3d6f78110e44bd7d3d8730783917999619bfbb64f04ad08 eecb4e8f027cb973afecff23c8e0bf54f78a4a69260b5b5246f21487ee4a5f26 639e4b84277033c6a29ef636034599d7ce6d3521a2a79aa0ffeca9581902f160 16190d2d5ddea9f13f1082a308ac4e64fcd5f1905291104e35fade6eee315877 1c501fbebc4638a63d0d7c735976e69e5e17c73095a4e285ad60bb077a38521f df4e9314755f2d02f256923e0017faaff6b35443fd34938960288886d89f73b2 44183e8e13ebb4d84ebed7800d53567a89a1a017d7df683be9740165dcf2a4ff 998a6b47b9d732643fb8d66100d5ee7a1f1079e438991757677ac0f2222324cf 210192224db94faf344ac2d24e49c4622aa630d43a31c43d38824423a055c1e4 0e71dfae312ea1f5d5a13e20a48201e8374ef7384ba59784d7bbacb9272ea895 00e282bd9c356ce3999304364a102166881480f7ca691055ef0ee471c8f9385f 69246afdfc4a9f77538011a9035863f96673058fd967ba5261fb2ae37994bc43 ed9e5621b2f4aaa2500ccf71001129ce886c747977623ebee0495551a00e83c6 dc4b5ec60d3fdb9280829f31964b4dc9623b01412fb43b9e6a85d77c6e3cf7ca 03e8e38924715c2e0e7645e9d757e002142ebb20b326a2fc30ed85a46fea8f1e 106f0f131d311b19844fa6f0bfc4f944f6713179845b3502cf692cb18115709e 58a020982e032d1c12d90c99fe4443eca0cf6fa4f8f914561f735c2e14682325 c57b336106c4901bd046742b2d49b236e51c44d852e1598910048c55b5bd9bb2 04a4ec5ba704bbab56e1ab3ef0d6fca3a34cb15d278c95eb058533908dc0535b 27be916a48da76e7f11407794ae47d78bbbf6d157743a870ef4e5528ed8bdd0b 3191694ac555cec55276f3e91f886ee2985633bc9f3ebdf34f81a47ca770be99 9cdd479b1e01fd52da41d85cb618fd8ed3aef22c90d2ad2eb95dcda661298c5e 0aca5be2504e7ed87d84fba10750468737bd5ae10383b1d29552746d180d7224 f188c2af29320c05e7c69d1e0b73b94cce93da1c89ae9e7458e0bbad485180ec ed37b1cee02aba2269c386af33ea82a2c51e6552e3668ca9eec3752a2ff2e69b 6194c5bc8fcb1245b9edc6e4205f9741ff75f969712948616a069f22284fe2ca 165bbe969c34df0770b4c1898b8afe12b5d7a89c181c62518cf5498cd4b4da0f 36e59d9f9cb7ffa5b5629698baf97c7892d5e7983179e1246fa79dc7168cc0d5 11db7ce55e85bb12779c4b9cd97e2d5cae2215de3a44bbcc356281b242afa27e 4a1924f69e6c408d7b7da66a8fd75558951a4d942605e1936a6d1c5d09e5034c 28fb18817cfaf1fde2efbe8ea3a4f5b6c3122dbe992ad5d134a8a860665cd7dc 4b5c8dca34c9045489eea2ce71bf74bb35c6d484c851f6f6207da2c3f2bb85ff 1f8cf34d12da2f918d309eeef1f3daff34683061dcdd05541117cf996ca75f5a c68bbb18a774d02e35a956f4b8034cfd2fd5588dd134f97484ac7b014a9c2861 b22799b46bc21f152144c3dc33d92d5d23e685f711b5b176aac8a99b89ea1f12 385cd270aa7e968b989e226f99e62fb59f437b7354ccbeadb9953c4f4627e220 f5ac273b9fcd8bad6bddb0b9a420573a7ecfaa4d52b17136b93e7a000113a8de 74d50b1d2b7b6ec2ec7198dc921b35ac6dd02d4305b32c693738b63a56e8375d 5d67c02c36b11e3ea5fff8c05ad3d358f197142d8c40a93405c073b5f4e05beb 428be6d81c278eb7c12802a7db8fef8d24a0df62c11d2f639ded588d1effb156 597e2e9109b544edbe6dd29acad190635076d3357e9c23705d42a3083f63e9bb 0e4c682b5eca4935c3cc15e161cd0a75bdcdb23225a28429cef816c4185bd57d fb57b7652cb09d2fac2648911dbeefc06b03b9c9a949688795c27e1590419c07 b552af3e3c02f4df9fe9ecc5f1cac27e3a231c2dc104ddd3028a2cd24e6ef54e 3d8fd083eb0188f87eb566021a1ac198b2fe9bd8fdad1244768c58634f7cb40b e7fcc0f4a9b71debcc6c775195573bdba4ff9e76e8c2017a29a599a5cb8f5605 8755a4bbe651ac8143b795f759d058aa3da94626a05f92f6efdfd8e289710008 729a8449f3a30feadf5e8922aaf158823a28ac392c35bc665b10630ad255207e 831b6f76103e9eb40aee333b035fb43f47afa9eaeda3df8924ff9cbea23bbdb2 ccc8fc78b84ffcee4920a7a862344f81752a793e91ea0f2c3cf44bf3ca0d9f99 1c57e7fbd7c699310f09852d453d09a38b2c291e0694fa27772d76f51d789457 79ebcb9c0fb2ff865edd0e3d480a8eaa14be886aaffb7460d5b9e5ea02cbf778 a4d16805d0b2891512e45081e449742b074bfea0343c78ac060abed849897aca 4bdefbc0bc51bacfaa1fa889df7e8421d60b56a181b3efb7d744e7659cd921f0 49a94e3885bb7fbabd60123c00f7b11bb40fc1cfc35a7d58eb89f5b6f1520137 9cb67ec2b02bc7e590026f1298dabe3749d941e5165858696e9927e37da06ce7 203b0f06d635e6dc0f91613ab30852cb3a157b62a151296507a2bc242e0c038f 0f568ddab67af26282bdb401ad9c6f5f95538fcd2f69c99765ef88181af537e8 7faf00da825158d413eb67514a9c13e02544c868c5a3f2097f0288a1ed2800f4 e780c1720443f4224059f4a899a74146fc9084c6158ca1d2a53c5452f5a948e1 670e44de2a16e4febb3a5721bdb06fe1f8769f81aab3d0f9f7911d1a8e9b5d0e84e0e841b8cbf16e09e47007a89e52a7a31acf9bbefa298b6d5ba01bd28788019ee0bf1f2f520c48dc47ba4e0695fa3259eb15b24bcb114a44b4b677d307550ce72baabdf70a2636e55ad35515077ebc369e6856427f4236983a7b95cc0aef05a454b2fa117b65ed9a0b11232c91124430cf1798fd6e47464b45502d3c768f09135be590fc4cf1d61cf73ac4e2c065f898e7ae4f162cd67abd28d26f110f17055ad1a61b6b39cb6d0140a5e0ea60775f2c332880e0a41c762769a48ff112c9048abe0d71ef99df7a00b138ace70f65bfb5458244d14aab3bfc67eaeda7e7690a434848a2cd73b008892c601dcde9af64adbc501ffcc11edbea1d0b67f0b29b02fd337658f8875e440f08609ba82083a1d81e9d3c3fdcf3b49876629f6140130b4ba3580a7eed8bbe8f4e966b0b29572f788e95e35daa6782529cdaf11ff1c10de72bc828f9a628106490a570210ae6895fcaca85cc672f68b3bbe9a6383b57082c6ca9c21448d899958c5dc11a9a56e163e5da2f282854ecac0da279d88abe088de1bb9b7ce6c0cffca4da741e0af631aa3c78a2c467c28376b13b2e965bb707c011c6895508297dcfbabe6b6f2dbeb0c95aeb279150e90458334db99dec0c09f1849430bcb1987307fa164d91c79003f6387a653dc14f725691f9fbbb3e330cd6684aa99b04058531f673c2770e453ce017b8755da47cc4942954e8f9852f0ae7e5d61732d3efa562c8a274197568e5b275e048647ba9a055b95cbafa81530dd423af7a3cbeb3f83db364bf0edcf88a36ad3057e08f61e3b4cd7ff482a813aa9f05e7063164b4c0474f04aafa7928a699497161fd53d68be5a06b89944dc80d6624e8354f4bc310bd3e78eac75a8a35bcc36413057779dbcb683c1979fed503f23a1a5789c60de5fe79f40e31feee7ea5b609f2609dd2ef28b4cb0be4189d09f4438bea93466b45f5fea5ddde7762c6843a35be46d513af133cb734dda5c50f21dd3db027a37a5afc274d73c6790e17f56da8ce72b33d4f6acfe147f321460638f9dbb320b0248d71a44481246c58a82728acfbc0ccc341252d9dffdb6986086cd2fede8e0b36e8f49ac1f21938917bd55cf3dd841622c6e502857a59b66e07cf7298fbb17b360fefb745421c051faddda3474c392c905e25bb90dbfaf1b4072de31cc6913a2cc240e30feba384b87240ef44376bbff0fe84310cab24d87a03e2e66a96b970ff4ee6279bbd9d60f23dd92856029a940151e6df65d528e8ac077ea9dfbb39000fdbc72a25c44941ca6d338647679ac7208bffefe34f326dfc069fc5a83e461034cd080371b276999071e4b4745200d8de2cfd2c81d8c1243c0fb8f57ee31022c2579ef2f35cb02e3f4622215b555927b88d402f90fac2606be1f0ca00cb77a5f72eb2d6b0921d3a20156b76d483fc4305e27e2f46f6a0cc0e0dbab70500a7369d9007e2bc2dd5623bbeed654c83ce6907ad0de35e281502fe07a6f09b60fb11ffccb3467f6ade6d058c9a39de21803b555f1cbcf23b90210109c126ba26fc918bf02179ea658d00bef0ce69edc93008950b38759b299e398802ab3f8f1bea32259bfcd1862ee17896abe0c16eeebe8d9d6b41104d16036ca8068a268d18c45a828a81ffa5665d18a0b6b0ba8bc2e3d99e9ff8d21f224dc9d405e44187c27bed2d76060aa707f30040c4d9983bd9f036dbc775942e17f5e3b10c6b3913c981c90d45868da775c61495e069bf46d7c0eb1287a1fb24a852dadb0aaf2900b95fc6d8ac4553a7815957581cdc447938196db9c88ee59785a91c000d343dcb4bdbf894c04c2dad2383469bbb37c77f30de0b038f0d12d134f167810a9a4b0d96e884216bd4299a08ddacf17f2cda75e29c5514a0e5bcf9f8da152e030eb7c0d148daff9ff652c34a0d340cb74e8d7d27b0e1a734c347f49a435641059079a2a6e892d6e171bb468fec85d93b5a24119948b9d6e83b123e6bb752d30c4b14b3b5fe31a37cb441d354e5e2d87a0d5436c644ba88efbbb62cc48c06370578a52b8580390fca2aa7f407e65ab1dee4a0c7f0b8ffbb418eaf667606af130c04bcc3a3abd288db965cb9489cb213b5c09ffe5cb917f6b85da8ad27c682ab30bbfdbb52b6ab90f087fa780d634702e92afe08bf576aab645693bc6f22c8390b5b93ef05151d055c77e6094583ef5172457981dbe902519005c5435eebee7508b2211b5fbfb1aedaba785e079b40cdf1e8d219cc09750f415f5399c24310de09f8e3dd76a6272748cd0b1d91f229665daca5164110e1653b07866ccba5ccf10f1b51af53b6b6d674740456ec8b823101e2749798547ffd73af35b2f730488e0d7e0865f6d4be64262fb2a04224096e3cdde3c1bbaed649484bc8d68e8b7d4f06407e9277a82389fff35c2032a10a7afc639609724c2092339c0801d04e9ccf0d46c9023d1dbad68d9aa06e4b4fa06730abf01c4d43bd6962971e06af11a180091973aa1ef2a4cb0c243a3f7b2238ab4f831756abb86a516e12b031fa9430010d849770d0f4bce42118a50d8df183f7921c76a27b31dedcb138fe1da7bfb5650b1efa8c6a5cb2ed58f687ea7ad7901ee6860a293e53d9654f98640bbad1eaee03315376173b80d41f6e00178555cd7fa0c05865fe34eee564399e08edc960fb0e4ee4e10c7678b49632fbc808ddd709b8c29aca521e6cbfc68b3587e0d6936701efa1fb4baada44c3d08369657e01bf8b135829eaa3646c5596e15cc0062b230217507a8482b7cca25f6cb248c2c9809e5b3ef71153d7618ed7649a65cc054709fde6b622b5036f4d024ab1a1b7b7144161998d183ec62d5a69573b5dcd66ac03f792d30716fd6e20dd8f4f32c26016b9cc05210f46b5874f3e8b5b2e4abaa20bd9bbd18201f395580a8dc9f8281191352713a0f4dbef05a44bba38c1c6e250072eab9dc57217044862dca215233a65899b4aef58f75e002ea943979675b3540093d47140c5166a660aac6f19cbfd4b3e7990e6569e80f33592142d7523a8100c6fbc96a4175dd8078403350968383cbad4043d48d0cefab00790d6c669059607f4351708900d0c9fd08a43bad4474b8f890b7b6a88e66106316fcab4188f9103ce7c9c5fb915e05f158235836d5a22971977975818bb5179adb4336042971c07116ccd4bc572af3640ab069af3b29edd0ca526a7dc550122e2fdad89f7c4930aa8b9724a1a8f42ed503eeac6d0fdbd42553f0aa87f50e35fdf27dd7e5bf3420ff31cb817f956ab314933aabefffc8e38d645de1db62801e1f037ceaa30d043018b5d0e4a268040ed53177e50a4bf64b213cc3e3379cbc7f77aa01b813d42e301a872619925382d4c7ac7a03206250997472608ad891cea9186132b49c0417109d9ad785703fd55f6a63824282a3dc790c82164f3fbea61cc9484324d5467890f12eba41df156e168f8b910160768b5a1a9b96fc5115b116e5430109a2c7b8c0c676e19ccee266ecaf74b152ee36d44b88666d4fd295e089cbf0dc8b33319620437663761361389e369140a96e23f97bd233d0485b5cce62b272c3015dd9bc305e4a42ae45d2b6ffb7916a3761ac731130e718fca80c7f9ed793c25f3d9a0a8080f8553f023ee047b168a481762d94cdc1c5f835da05ab8d661b20727245e7c0bdbdbe1378ef2936085b67dc950aa4abf921031bc6247c30757192b4e71f39903af17dbad28f10391ca1574c4fb5dabce0b1c86ffbccb79224cd4f33d7d14ec0bb7e8eb98dcb8ac78ce8b2c1be3bf1200c03b07dc0e16ebfc3695e7a5d90701595eede9f6d79f8faccf4b7d13396440d5ecd5def8ec1e3e350f0d60838ab68508b8fc102d9c49d6f549ca3a82c6daeddb4ef67bd64f525915ae088f2920a9d4023e8b9b211177c6fe77ceae973d3c42475cdbc8441ff6589598ec84c18c6ea409aedec3bf6baca92ad812c14d14c1dcc322ef65677e6aa9cdb1bbb821da40560d75d768ee0572d7a34a8c5beeb8763a9152db1165736e3314ed0faf5186723a0a5b924109da9c7d8fc587233cf416bde8d279450f12f732095f6f65b507a7180596b31d1b27c8752f0e5da68f827031f134e18022ca83ac4555a07ba023b62642423770f1a46c54c54341b01bc9fd8edefa87d5ca9f6ab32f3bd1335619cae8001bbd65995c23bced2afe71957ca506fa89309d5a5c7d902e488e78f2dcca570629a9b8bbb48eaa8d037a31c660249d36beb94783dce9c24cbc03f050ffbea007c620c3b5d7a9074e66c133af89d3fdce793c6a7ce69822c173ada8f307c5230d76147cc1ef83e0b0b9a12b1561fc896dbdc474f4c3164e3e2d73dbb14a719b07432a14c4b564b9ae17812fdc98732bc69bfa67a8becc29c94c611acd630f0a0c1982c2488dd1ca8ac456e239a7daa89b98857169c2f1d90c7451780e6c01e70dda4e5006ca26904098b5a82e805c94ccda07cd77e70ab22feddfa1658d2fc60045a448ac2e8694025b8d67ca4be18727a769b4a8474842a725d189c6633fdc04355745a60a90cc00747c436f6be9fcc2afc4e7968dc7a89e720f02cb848e6b0897edd44dc3f2a47edec52ce36daf75d01e95867d7bd5ea01d82f316e8323c50c7d183bcd2788ba752e9e0e6b27be1312451cc642608436f8ed9320ebc29b880eac9aa4817f037c2c4f06e1e39f0c0498768d2830a0bcb5e3c24b4a7969fea30bf4cf76e649bb594d7944c2022c22a41fc8cc74c8ed9d4d01c98e129239e6ea023a4c7c2d61b21c8ee20d8ca69ef1750bb9d503f5d6f795cee354694780000f00e4663dcd553e414f1098e9d5065f10afadbbea3dee46949ceeeb87f340aaa20cd02b2d8178a693fca60a7ce73be85cb8d89417d40430049a27bd73923d11b2005f5e00d4ce253eee652dbf01bd86e1da523a9ce7182b862e418a1ac9e15e39043483bb76dc0f87478ffacb5df2315995489b2e426725ab224169009bfbefa90beb1757cc8e11ad90f910f476f9c5417301471e3399254157aa9c64f578cf6e0a40889c2bdae0b8c68ea87897ff4420f404549074c37951773a98353ca5a2830a26a4245e216b09b1b643038cbfd503d39105f9d561911c941560d741f665640890488e39d354f0dc6b50684650cced932d699e8461928a4a822f89de25c46c0daaeb6c72a6b336af31057738204499a9323fa499240662271200ff7a2ff7390681321e2fff66da83e904c8555cd68f2cdef8ae5d6c8d365c8cddd5b542d7d30d1690d0c23878ecef9e5698ce5879b11d6cd5c02be3882e1a465bcdec6aa4dc00681f3ec3a48f8514b179447e04c7bae1f5029ae749fd9c3c424f73e24dadc8089bddab2fb814d68e6bb81461d2ad9227a5b5b15ca7f1671ccb4d7a8d030b3a0fca694603e69be4a8fc66f8d9a1584be6ac85fb5775f08092cf307d6296c8bd0f6219bcbded5420d07f1dc11e51b1ff14208a7e3e84a402689b51c69aac968100712efaa6bd4defbb9af852c2e2192b90c35e6e06fd9831d07e78d45423bad206e54be96b5c2da7ed3083acbfc1f275abf4d244316405406e3cf3d0a615818f099818e843887a76b02952ad88fe17e7a11304f58539020160a223921cf12bd70a35f794ca336e81baff73298398d410d5af5a0d0ea2a47773e97129bd980ab00f19c982a947e2e7a520d85ff378ac4abd8bd95ab4278d7a6828f2ef9bcc0bc609f3167b2dd58416a05e9e437fc635a070cfd97411b51afdbe4117899deacd8702ddd5a8395818a408f00d90903ed899a591170228bbe41cc0ca12948f0e8a2008435a9478c2f89c15018b85aced344b795ec6a4e8a8b2cebac9647af2fad8a501624585f818c5bc30cd4d002947dcffc968c364483e90d8d65f718802fd5aa909f67d53a587c46477a6793eabe5157896899e3c0ee84ca2369c53316d90e98305b47a64c807a1e61e5e6975cfc7eb36a33a9be44749050c625a0cdde4581e95277f3c7f376723e872b0f32c0edf716e087a9a56b7edd37bb559aa1c7f4617d601cbf8fb0d94ed0c6dcebc6464f283dd1145a9147aead5e6ab17768b84d94ec702792d4c75c3fcd8474e843ccc9dc763ea401f9a76d984cb77054f2406f007d30043b2370371b69f83003bb09b91c141a948606c967e06c5774071c6104c53ea08ff9f3b1b74a2d92b698262b548704d8ae9c24dc3a2ce9c41eafae3d5bdaa3000c944943ce70e7c7b6192a41c26bc73ee2c313d4c3754e90e09378f1bfd14a70f51d019fd81aa820e011616293ba6f9c8f91d8b3f83ccb72e35c7fa89882cc90bf061ed0e6005fcf748749e4f1fa62dfcba4af0be9a9fe724b7716ff37c06110426e9a522e1a0e62d15f78616d178a908c3d44c9e5becaba278a9031e637e570e44e5700212f5ed7e171d2f6f87625f0a7e393d7604e9a5eeaea37a341a906f084eec632ff5424512b2d46bbf381b2d81650a84c403ba2a610308f8fc47111806ab8f1a25d21aa6c474ed9ff3638beb76f478dac6978e79ae957c3861632cbe06407648cbfe0e904356547bc55042d09ac03e2b18d11ff3f42c980aaee276020e9b47051d397c3e845210d2c473b747a91fb28c12568ec1ccc46bb99c83051000e6bf934514b43fb45fbf99c0de6379b6eda14618e86e90e7aeef544efe9cd702fa0e2c4cd9f3c30a916dbb1f8c896a03fe7aac141adf712d236e202e07e31f0db36636f4859528a70a89a94e594231b06abe163b8460b7091449cdaf73a9eb06bc99ba5a7ade92daf4e0ae995317253a92b859b6532edb2739953aca44ad7206d9df41e353120135567ee004eab4aa1426eeaf5d7537c9f48e1d8a04366e4d0940b4ca4a8280bf9c8c0b27ac8b2770287891677a8a5aef3501492def50b72403adb76024c7ed79d4b8fa9173864a5df278c7b182ab2b6207596068d2788a9d03e1615860ec99773504301409866c1c6efae371f77f2c387c699b8db5bd739008a03c1c6f07878c6c8a556c07474b4b91389ba53083a6febb3b4c05ea5b6ef408 false +check_ring_signature e6f02dd94d4415818264893ca21e60557d80c8959405c422f5bd96402820cc7d d1c8235118110734cb8805b1ca1398d64b6c360836915c4d81ca235b3ef668fc 2 2add92858e28b31745f2fcb01f2b45e199b5732e1c9146df34c6996211c1cfee 24fb8ef6b5bd607b120dbe4c0f7d811545cbbab08c9cc2b8396564d8541e4848 c1e0c7b0fab5c2e29ebbf710ea22e8f03bd7ba4185b6ef92d4bab2dfd59f600be232e3f0ab790a8ba66dea43b6a09c1a5493d51d872d920d3c7fda0acf82d30d30d6af5ce6b206539325fe0e498b847341469714c8791228520b50effd04f7076dd0cd837cfe7e91abf9634aae77169a508560c37fd6407dec6f298c76cb8a57 false +check_ring_signature 4893df1b8ca66b60c75ea8253bdfcf8a6797359e775889a0362fba91b0cc0c9c 338a0d9bef4b261474596a6043d68b1ea75b7308e8f1637b5404cb5d09037e36 1 1829cca0b397628d936bc3c26964344d35e554d4cf3177115ecf975b0835e808 e8711a23a2196737b70c52c98c894502248b952b899bc6b693231d6c7e85480d71b7538bead5ee505480ae1e0c05f34ee4e26708559affa5e118e1d6087968e2 false +check_ring_signature bdb74b20f287023f0b11343ec0552552e18d111b6275e5340681ca1c5310741d 468b80f27bed0db473503ba12d8f32513dacaa00450b948c3e81576ef46d4c85 7 66e555bfcebdb69b2e72d465b4e0ad395b06f5299893998738091b62960b2a2c 66a99947c3bb2af2479e9cc78db46957fe681528cf72d11853e91739372ae20e f0f86134a96ac307418bd75c572a6ffba126bfb78a77fe74f93be012754fc051 791f174819254a3293c998007df3c3f902a5d380638c7b1ea59c6ce7a9747620 a36de2feda4a2767869458ea557bd2dbb0a890e00a432136854de9ee834ef877 c3e0a59113a76a2b0b241a89716a75f762c88a5fdf44ffaf9bd360b5d4270974 aed36d3871ee5e9940967b3aed6dbf3f3f3bd0da102d06f622703809fe3a7527 b2bb511791a305776f632fc413fee11e3a1f66ea0eb07d66c9c6d614c15960058616028e9294fff59aae2a25f264fa863ef4cd338eeb24e091afa2b7a54ce60351df396d06f897c7aa1c669420d74cd62caf21da210a3825058c0aa4bd333f0a0eb2fd46a69caf0af48952a214887cbc2ff8f99d2b32e2d2a4fd7abceaef3c030207d146cc66a02ce3f7b758ba8d6ce7bbea14b9b87dd4390af6de35b39bd4038bac476b3b207516ba8d04865480334c8f5e66454d4fd56d46f9d73342a51c0dc65b2cc227fdf38efc467ecc278de117f0a3e491cdc5e2536e152c30a6fe5b0eff1f8569686fedc9753cdddc6f63759446c5c6ec3b9fb000d485303de410ef4be6c39b5bef88f136289260d5b637e528400d15210aea66fa64f4b5d326819c00a45e7e79d01f880bdfa8af16268315ac5df109b90f2997e2791477c75e35a803b7dfdc3440b19bd240cad5a5978247bfbebe7f68bb51248af8085d90947b180d81652e27f14ca9f3e3812b069c1d6203d9f170cb54c6a0a5e9d669ed77dc5c0e7b0f67332b2644cb319739e264e4d5669f2ad9df4eab3c54b61aabccbe593e0b3bc9ec68e73caaf0601af01fdc6b42f87bbebf59532d46b9baccdf35eb025c03 false +check_ring_signature 1ab01ade292a650dea384cee51142b209d40f7e5fa6bacd8e512514f5c3684dc 6b483d8255cdb3646d6699bf3d0136a271dd44c815a771fdc8a675b9aff8b722 14 86ce79faeb22c3dcfd17774b362db4dc23ecec5d3dc041415e0cf14ebf4b0433 833e14f9ec7527078b5f9119f32cf39ab8138f5dfcdec11b89863a8110425048 4bd1f7dd9b31545583a237e9ff6c9b52b0c0681a9ee91dc33107cbc77390a93b c9437fc900bba415e24f691b7d4ca1f007e350fba45d86ef1f9ab4c216d723ac 456a0d1173a9cce145a200889b208524a36c4814a6e84e0944f6e6cdf08938c5 2147feef624a1641d7e89c7991051408aac3ed64544288eb5d1ad03bd800abe4 5c5ffd1448314c881eb53780ee1e25db25792f6d3304bbd2dfe4f30ee7f78bd1 843742dda2f909dac1c12044d1e7d34d047181dc5745982cebddc99372a98e6b 83daf163754762dab0ab429f0b88a7a3b721109c2d257318eb349d2f9334d645 6235097661a7947c93b1aa59294cc0a64ad5e4a4a857341c7cc1e308854565e2 2b8ed7953f45371e109c01b1f372b0070e55a0c921e2bbb6865b3e8c2f7426e0 48cbeba1bc5da2bf6f1ce04b5d89b63c8596911ebd5ed2684720aab4dadf4e3e 2d87638e5bbe2cbdac5bbbdaa1843a719b7fcc9a522b2082e515e773096bb1cc d014c6192ea4d1bc5ffe4622667e86278e73282fdf90eb2f8e9fac4029bd3213 9c44c826b7fa4311e773b6972361af5931944d8ea5c99d347f2ad3f2a265c400d5c8d4e8730752d75490e95f5b6f15f8586ff852d716e34e2d6cfdd1d168250261935cf3d2499c374a42531f1f62015b2167f1a703898a053c466f0cbcc978075ad2cb28428cb2c6ce7aa38e5ab92f5042901ef7bd2ab4e69c081661c833f70b350bed8faa7c4d532c6e1cf324056256767cc6465f9bcb44c28326e5c782e80c2cdc6eb941c741a6d3275b9a1d8c879a199e0ca4a373b3b37c6119921c5e4301e1054a36010baf5aa4bd69302c38f4250d7cf7f3237fe1ced362aa5f4ecdab0b0979145cc9de8c24e826cc36fdf18b4658746cf2edffd925940e3ba5e94c8109e32ba87ae6f1c33250bdb747ae0f3470e3adc67e5bd3e29956224981de1faf04c4a9de920be5060f9e27df390bbe18542863aa37232513d23e6be31b41a01c026fce8ab29a1def467a53d706e51ad75556fb9713c397c7d13704a66d720c2100b3b5c25d46dd86f4ebd10bc1ec3da6d2d8ba17e44cb89596ce21883fbb49bc0400cd27bd7ae97eca6e52ce7298e18dba5f8a0b9dbbbfa06df36c5e7722a3d50583509b85bbe99529d62cc246ad8d0a9a575e17108a260a7bacf0085b08c57a06179f60d95653075057ed6dd1f85aa141541f6f85a24917f15d9f263b3b690506502aed1ad68f740b707870ca7dcbe6537fadab6a7c2672148caaac0575f55e04019adfb0b6f3f0295eb6738c7b60ef159fe4fb3022646cd8f2e8a21b3a7232032288add6ea09a744c05287f484379095ffd4817339ff771defe711a23f02bd0e49e9b8631eb01532535fe90d73e7f67a89ebca724ff48cdd70ee9513c88ae106d0e2606b27df4a6011be6c6799972c26499a49e8b13391ec7e77f6b5965b920b48d81476b697d69f79d1d63f56771c3bf92ad6ae172c31bd7e05d82be768140a7d520b9c37b18e1ac90b5c777c4f127f59f414c1ca67eea38d1ff1bba092780555c2a5a635e9bf3a24881250d9feee3b4b9b66beb87d920666de9aec917e5907b52b0e1c6effaa55132f0c73a93e21e10472592a6c5d17b090ee5c9db4cd190ab41828b63f492b3708fd16812363bae1e37b7dc3ca768464c194f4672630b805179be512d6636d2e6ad7feaa88ee286473156b8a0b9f6966f4a1b2300981110e76f64e1cdd49172ab91a524ce29e2fbba7ec2e719fd24fce6f2d5850c5967708cae5d636df9f8bc2ae359417beb7f3fa83baef31384e287078ab636b86eebd0e true +check_ring_signature 1df1b1038b97ac5442fb4c9637a2498812ca0957d3322cb48294535f8638835e ccdd468c96c68bc76a854c5052690805d4da15005dac8af03a7f140a4081775c 1 8c303cfae893e9bdf32f3073c8a11f622a18ff45bce26b0d93ddd91b7d11ae64 3672964f00af76be8f5cd77a62b12d4f055285be37c71818fe10f791c38c9d06626d143d9083a8f2c50ad4c2ac07ccf4d9bf926aa72585148f6bfed29b96d200 true +check_ring_signature d0f40bbb7085208446d926df71c49a1db41491f02e843ba8d88d0db1499b0c71 8d9c4d458827d157445eaf98e66a2774108dee5fa0566aaf842e3fba8817772a 12 7c8808a4570bab977c75d3b4a5228b32d598a32599f2fe8f181d759033cf9dee 476d8b893ec57c5b245c1e34a60c9ce40c28318505e4bb8bdb2c518d6d2f0898 68c1370686b414106bee537347e399e38f1fffd7d010306b3b2b519ab99fcd6c 4a81b7ce39331ad3ed314c8e488ae3fbbac19d455d2dbfcef41caedb2019febc cf4c07bd48df75f5161d1fb588b243794ce78c2399dd870a36cb28aca55dd8da 232d15df74b24a7150d88db395133cf2465f9e83063f891d52cbeffa608e6355 fb186254ca0e3178589ec1c708803cf4ac1921e3662fbca697f0b7236461e83e b76400f6468108e5017df023619f90ac24fabce4099bc9fcd6ae3047e726582c 9b98cb70568763c2e6569aa38521f770e7f4b7566d7e0a15d0e285dc3fe76106 a495516a4c523d123262621a759da4aba84d8688d0fe6c47c0838f4317e104d1 c136c2d75178d9ac2e389ffdde0a2a2354aa3dcbecbf384aa7bc462b4a62efab d7ef0c3bf52aea48b5c53063781da971122f7f432334d8fe46a5a7058f53f558 f53fd9e7a9a9e66cbdb7fba95d551e310f5cbecd2bf4caeb21dcf92877939d03e4fa6bcee7c55a7ae8b973b6d03c78de4b86a47e0d4f23f061c2c0920d14e40bd32b3bf053b5d1a30655386cd20a33208a51f3d38a17552bf2924c08c870240c021d5f55904dd4845a9c5cb3168fcdeb28d05680781e3289e21be08483ae080f6dec1dc4077cb2836847a3db0071d927a10e84f1e75194be38c66acd7c35840597078903676143ab648591d352815a0e0583c6986544f511d73522df8cd2590813d4ed81b6bea2abf78340f2ec786215791efd433a5e6a1a46a4f1b99e3a2d0dd0cdf104381b9f30546b488c90960b732d94912f03d50c14000876c9d3ce6008e04a71d14ea938be840c9a8778c9cbf7ffeefc27bb5d3651518f25150c57c6016926533e6dbf5e7ea34383a9d8aad80c70a91dfc6b5c6e273f3e6b635cd8b70b669da1164ff2bcd7e2a7a89be690df8bc53aace37487e4c3afd002ea2e18a20cd164cd0911a528df2357f68b776fe4dced1c707f15db1aa35bb48ce80458cf0317fd8095be98726b95f399b8a0c93af7655d8af721e4ede047c124af4bf9b20981a7f75d29b9aa78735e8556cdfd0f011ff5b11b5c8a4f47f672e15139306607d61163b0a3d19c034e000bfdcf76ab514b6cbbe4af12fa4e231b0864b0a8980b6483fa14f122f0602c87c74f3f02a9fd3245eff7529f8a6aaa8a588842bd3a014843fd3ebc3d7d632fc630280a401a001dcea61dc2e58397a1eb22c93be85a08ac184b521625b44be12edf43b4b11f876c67e15132d999f20621da59ec3aba0c150ff1236e6ada47f938e2e7940f1c9e2e0ba6530ad5f9937b745fc880b92f0be534ff70c1f466282c4ca8c775059e4c57765e728e8f0e56708bf4f71eedac0885028b2dd7708bf6ebac8fd2e6ac53a67cd34cb3ea9abfd5f0170455ddf0ad0d1e2898438a377871c81970e5432d331e1b7e30b691eb2cfba6e4435e9bb4ce03f299a76064ba391781f2518612fcf3486a9418b26126b8f8170f274203ed5d0117118af2fa8f55ab722c35ac13a0466be23a12971afd6dab8acc2a709ad00809 true +check_ring_signature f45a3a402556edb67fb42f8fd33b1ec52621b6ba7cada5ef0ee9ce96771c17bf aabea9df3cc06e0e56683096f8122730c6a5a4e0206dd5a00398e913b6e79612 9 4fe839bd3467c1f06dd7a7cf8727856fa48507ca215b1e53598e1ccf11a14138 63fc5947711e20b5feb9e74b11f35a6bb1f78f1c696abc6a950ea9db8739f8aa 1f01485ccf7965c5967c88de0cee6c15f242170e8f23cbc96cb638239e388708 7cb2958c89a850fb57a9a01d6993799451b0ddbd6170916cef5758c5e0f8162b 5cb2a2a0a22234ca071c95ba6e8a402046ecfc834c50abd58627dcf88618a8d6 a0c48a3fba2ff1c29283448d296ad13b86a5bf8babdb7fe04de317eabc3b37da 62d1035a7943c7fb493df12a55501b3d9efa014c903f876e3d48cb1188bf5220 102dc1240a4e7bba02086a8a9e3fae24b393c4561d752774d49181b26c73aad2 5e8889d5dea71fb3055e082cee03cc2ddb9d0e9d2532e2eb9d9fd2b0c192196b 34e20817c9c51bb8f6dee7544cd4b8d1ae3bf10f90cff1df4504ad8f21b3410de4b1700d1268a96a6609fef7a47b433487e2e8dbfcac2040cb47e7b4e24684094b4b7909efacf7a0d872411dc9d6ce3e44c5f13c7356343eca66e7487b55fd0ae705d9eb2edbed306b00a868004b9f8c3cb4aae5aaa60c4512ca67c649212c066e1a44c8afa846464f6a77ec833043f5ba449f3b2570e6a69d342963c128f10ab9010d8f940050cf04073c3e5b4dcd92b11a3d5fcd093e0954b98d8022f3e400834581f196b5565a101bbcca382b38c7b156240a75f61875a391fc3f52cbee03fe8e321fd2a3c992ad41ccda1f61e2e6f6eda00ab4390990f544e6f2ceaa1006330cf3103a088059f3eff485223e554e89d2b9fbccaf80b67b500c86a0a21f096de7aa9260061ea8e7806c1dec158da7114fa21b10f023138ae397a363241f05f36182008278248d28efb911e9b532b1817a1052e1a88c40dfd95a1b6a4d140d164dfca4de99608ae552e39d966dd992f016a5703dbbbaec677e4f1ee5551308b36919da26bcd0939ac23a315c3352f84e303bdfc3c7a090ccbb9886c04a9103dd683d87f5bc23c0caf36796d9c60b406928b44a532f98774c37425c737d870ae518b56935c6c1e5131e6a2f3363b2ec1cb49a163cdb2d8daf0305441b07720bf3199997b3a6e15de545e414681fb32478880560bcd67e20c17840cef27254021107e7fa8cca1d304f83fec7ce77f78d8393fa3df7e02b8672370ebc655e58062344be5a74f84f1c8887f57b7a34318e25c782497abc0a6d25e4dfbb56e76d0c false +check_ring_signature 04259f190719eb425bf69f29d715771a956841928e1c8908de7e7b99434021b2 f015b0f80a9c84227108b9030007949583ae0b51b886203eda8df6fb77b706bc 92 f0cb5f1c8d66b5c7bb7ee97f520fb78da28f35fd0217107590bf8a1f2ee943a8 0431c96fbeef0792014c8fa9d43365512be953e7837fb7eded8ab3ae9a24684b 5149d7b85f65d3c44703a64d60193729aefe15d46cfa7a9ab4dcc3dd88d12cfe b5c1ed98049381570112882756fdaba9580fa66a76a94c995e9db8fab22c704a 168aa09683ee715eba9adf00df740ee9463198666368659dfe367103859d7901 560bef02e4d5a0b5de374cc7df1a8f62656e7e2ebf6c36a9ab4cc840cda8ada8 c4975755b6785243c00b385dc1382486bab7b6fc126e64c0874485213707ed04 198526d277e8904f6d6349cf04943f53b885655cb9e45d7ce3912fe7311e2e73 9c1699c890fee4a5e0245e342fe983cb0162e7562490e72e697d982041ef18d4 f88e320eb4b63288d2df17cc10150f4294c54780432901e38a9a0ccb3dc100b6 17387122c7aa0b07e95757f101b661a7b47b0685a87daae2dbea61ccfc88a74e 90b3fb1634a67fe614e436a581c0f06f40b22da456fc146733b84a9853b49872 44cb53bcfd45391beda89a03f2df0532c3182a2f71046999cb908a96096fead9 9614e5a162dcee9fc6e2a25d464331773e27dc1d2100a497ce57340d9539a2a1 636d527931e118561902b4fd0026272f8a5d7b1aba5833ec4303b7ad53eef1e9 e6be2cbb1b9a0ade1654e25bfd5532049f505273603075a087539aa99a12019c 2852ba814825cb9ee568e352dad76cbe8cc8845e9990197114f352ff9350b55b 929649aee704841ae355be1c69d8b7a22403a92684daa62eff77f667bdcc7d29 d07cd294005e8ac1eb0075affce08c29695f2825958f52e761d8a645acd57dea 65a19434b05652ed0e47cfed1b388095fea235899833f0705d7ce281a1ac879b 9a28e69320b4d7e79ba17df19d4fd792cceaed5c661c28fa70454e21591d34d7 79a046fcc99a7d844dccadd63de7a54c2079192fd20eae41a5dac03c33a575b6 16986dbad68c29e539b6a8c9934a02ff539dcba14a53ebe621bcf307323c285d fcea0f8046dc91d87b206c0fe90b08c0b502647d06625f5c6414329278de903e 0579fd8178dae5c84854f2e12c459b2534e1a62885f0993a89621140854a5733 c1b495c227770430bd40822279a1ad026f5c6f39e0dfcc0419698a2d06aa09b0 2bba02481b9028fe34d46a81095fd75e102034479b3d13260d2774e4ba8d0a54 38fbe3b1ca521b4728c1ad934be430209b68f071633400d6b118f0109cf9e46e 2efdb3df7f355610926984bef4636f07905db152a46f7d93c7f10fde9a77bba4 8f6388541f5a49ff9c1c8d5e7f05eb857d1d31408aa33da2c044c88a51d28342 810ee2eb47db14e019e0e20bfc252174f6ff82f4e3b910649da98b5fcc6ee11a babbd1cc5a7800305741aab4491c336404d660a87a3d1525fc69d83ce4037928 063066479dfe5fbcbbc5b226c101218416b927ad204496d92f4f7c86e453cb34 f554a2e0f0e5385cd8ba0e2238ed03a53d9fe1cc97fde39da46dc5d0f351a26f b1f40892d53e34b2210252b641fc9c7b69bed179c04ccab274c998e1ba363bcd e568de3dde4f7d2a16ac33dced6be2aa202e03811df9cd07c0cfe0028cd1e431 59d2d90cf46e9bdca70d5858fd0b27317558be30af7dc15134996e93a2306669 4b08a3cee59c4a75316566a3ce92d89464b2a0bd95614817d176455aa32ec46e f036fdc3906d6d871ceb152a5bef6d0c9f0735f7c276137fe341920e451e6af1 9e5439c706a193a7f6fa4ebfcc4d17639b086516d091d700e46dafeb5bdf4523 f337fd324b6e4e2cd7b9dd5ed0507c6e3ea3ac4c22f0cf70572aa5f5d360c47e 9621d09c7381fe12a106bed1637fec4bea3de11ae8ca16eadb7f37f820bb160d 16468c71925608293027489f549791991598c9608c9a53ca2513b8d2bb6c657d 0927c29ed21ee6428647a87d23960687b8372c563fed4486dddc852a07465478 c0918710e94991b39c35cc3f1bfe1ce12855e9fb2633a6b4f30083d54f06281e ef14f38f604c3de84c2cca9cdb25c27d3f937060c8954cac989ec42cf219a244 b846d2f580d065cab11af480af65a190a78a3765172d2709cd77bed469bbdeeb e8d1dd3f014344a92467ff27d6e66b6e803c422b739ff0fc7edb059b3364beae f175738708279dcc2703c578922d1dc480252354e114d2946d4cdaeaf0f3e2e7 e50d509ec23e4c18a4c383707a721a605f50c375e5881117e05d3fadfc3e08d2 a00739b68cb08b84c021abe4a9a9ca7c6edd2292722e7786c4273e39162b685b 61b39c3319cf4c1fb182c9fb4e59818bd302677d315a0cf6f77c51854d3d5216 aea7f4d0d620bfcb5f6f32d94a11205947d86c6aeeb1522467e51593b02c8081 dea0aa2ca2788d75f186fc5e8dfd9b09bee938aeacd421cc002c59fe3e471bfd 994cc2bcf1513321646ed241c77ec291af22d8ea7494c42a4afad708297046fb 22005c4d9f626016c777bc1e0790d921523e63ae98f87726d286b39efd302a0a 161483f4348af29e8f38666666c3da5a279bba1e53a885da82be2fb8b1745c7e d6bddd173dcb023cf1d8f808a0b03b8f646a2d511f4ec644a96c0f33ff34795e 7b523b31096c6bfc8e4540550d863419ce50857920f5cc792592e21ea8424ea8 fa0fb0d0b0bb419eae4fcd25e18fc93b27f0c86ec490ccbb8e3f54788268defa d6bb1c081e3ce76efdcb9d3a6b2c61fbc77179c55ffe921781f714e35cdb028e 3b81d8d371ef2f7c750522000d3305ddb9debf2169b083284b409517755e4b74 d377ac7c71d4f8b2b1d41f401c5788094e10fa65af9e86f86e3475944036d0c5 1f2c8c82b27298f602b3525571e721acb058a11d215ffc4b57ccee0b980e0f0d ab946fe7156151e0e3bdd8cf899888c77935a94824cf38b08f190897231f4b78 3d022eaaf57fc575d5f22308bc3996b4ec50c827a34cb008871ca022fb157c2d 0dae8ff97f689133624abcf7eb2828dfbf0dd328fcf7a32eae7c1f601b4c54af 42aa85f1d66305e5ecefd9ee235ecb8fa0b27c3cd34e88afb4b20f95f72ed66d ca5de247147fd0074692048eaa41ab46dd12e189fca99ee09d25891fada58c72 296902bd1aadc086ff80f17c23851ef08c680f8cb30ce2e584709cf93a448f2a e8eb889026a56bf89acdf6198b70aaa7771d8a5aed523c9ba71a56fce9e9ef7a e73c9526d67c6df11a7b967d80385f3d9d1f5aa1370c757ec041b493aa57d6c9 8d12ec9b74cad09448a3a3cfeaff11d775c8538e83e066c1548146905ed969e8 58a0717b8b00c2565e5170201371a766841cff567da9ca65282fbf2f41697637 a8c800278de01041dbb3cb4df0fa5f3312036845e6f17fcc31d0ddd4378525fb 9d86f37e724dbcecfd4913ea36e24a03d71016df94ce2fc784bc9fdba2d3d329 9d688e6a5cde60ce43c29a16cac0828c159ed07935c1ccc9102babe3765c6b98 789c8d68919a5d9982e79f988a533db6361ae2b1cde04d7b3a09037750f7fc1a 3796a81947144e7b83f658b667504e4f83207a2731957d8b2c01d2964e786b71 a5186f9539521c47a1a7e1362066a64b39dd2169f543d8657eeb6456f8750673 1e7d0461d406941f6f4c50b6db8411c81c6c03cba2ba9cceb9dd92466591e79d 8aa801a5c4bcf5d9749075e820dd5b1158d87e63fbbfa2907008cd413e03d65d 1503ad1d50e1a2e2c3ea28913c0b7c68de801697c39fdb40206e67dc269fb577 db6bed4e76a9e5300f808dbd123a9ced384e3b21a5d6afe6b80739477d76dd66 ab75854543f909c8e44568f635b467e26c7961f6d4f76073cde0b6989c854fc0 ae885e7c2245e9ff2becdead4ed24318ea9e94b6d25c09434874eb9f5eaad6d9 6d1f5b816d64f233f1dd60b737ad71a546bf6cf5d0cc549e8e2b3daeab1cd83a 6968e18423634f78ce7b75e7f25e6a2e2e4c1f48fd23926c71a6334fc4d87a37 278888ae1ba3dc2ade4343167aebdb57353c0801689dd5315490fa906e2f7299 c9c2d392559ece10dd8068afcbf55b9c4c3fd209034c3a3cd12285907a8f8c6f 0620ab59c5bef3a2137972be69954163f1b852b9aa80851f6844d07ed81027e5 c23ac731275906d3cc7de4858198e6c66e0957481036cc5508bde2edab00c129 c352e2ae41761312d7afcfa5f5a7bb9db98b5f605a16a229056d0d7cac49770acec98c0313387bede85eb56d8c16190c5834f28def6375b8d08828e30f14e503ffa1ad2a8047e59cbb4355a4eac962236ad1f037a96f143aa56b426438151c020b768958782c28370ed9b999623c2b18633d7c3618a22f8bad0096850f03280ef5ff3b883a2c894e3dc853847c43f5655f85b311cfb139c940ee356abfebe3022034f139db35688769a2702d60eef05c58861b09154182316623a15957cc8709c31536dac5a6e7cbf32c4d1ac3d86d5156588cf8571aeb0d45cec5825b08df01dc80ad80fe91e93e303a712d014231737fa7bb0b671b61c228cc757c876fd5093b10f4b10201bd1bbe046bd24adc6df24e9dd1e3c6fcbf06860236ca11ac530134997595f543cacf049ece138471329263a5d9e23b549ecf8389c37986ff47039b51e25da4c5165db4d1244372adb6ffae493677a2776eba3bbb593252698e02eb12ce1baf5f48e6794e1baeb16c88b39d9a2533767619ac0f2ee433e90f9f05dd14bf5e19287c161f753491cad3ee36fe3e706c11ec7dfa7cf1101b9eb2900c20bb50177891bcb92841a0acb273affffce3dbfa71677360ac95ea0818703d0eaa7c572d321580b57f74cde686866828687c843b256fb7905059ef2ca4bedb081cb73ccefab271836bf6a3c65450118771e7019d6aed1e3b0c2f2b64b48d780b17ff7c2bdb48ac1bbe80a8b6ea77b725e9ad36ef4a19c72c2d3659786a93a1006300487ab6f4ec5ffab1334b48bb2b905d820b868656254f0769ee8098db090be3c2da98fd4e441f834e8178152d042746331f736cbb56f00992eb06798a040dcfbaf7ec5e6929c51a65e2dcea0d6070a644a18382a898c44e99e7a24f75260500a81b50b0e69c8e32253ac90ba46459c1e14297ccc0c34663521c16ecdcb004d129a31d0b74a758852a6c743e7a54d165e8883b772a68da0c75e21d4d5eee06d5232232f7f40883bc43cc11b2942ea0968abcf505a2532c539f4a4f90e40c096829b856e044dadc152e8f8ef93097b7150f2f0d48d6362d7937005e3289a007645b73d939e1f35a76af92bee04f171d527b1c287e5e97600d47e828da5ca700807b75f81d080f71aa22cc6ed3de8c51909cef89c5239907db76b2f81138c3096df82a7ed80bfb062974478d49e860ca90c587a156171f687c1f82b04899f3001452eec63d6d390019670ff327b8ba6b12261102d4cf0043f4babf84e48cb60415d393c89fd8ab6aefa7f9ed9e88ef563666814498365c9f77e7cae5881e8902affc54fef5dbf281da66aae08b0346990d6ee85feb26476794926af04573030a50c13471e130939e6fcb31d04b0ae88c8a0631ce34457e4428f141c43e40ce0ea8f483d97aaf08a0704ef321b18cce5ad6436e11d479aa84bfb6516930d37f0a91641b6c046d15358762b18057d459a30367dcc6c0235f94a1d84ef9ddea170adf88699b31ea0bdc847c82cd7a32215af41d226b7e08d5728ad8f1dbc267250e45ba94c8a94487657bfea157241eadbe2143d38575c0565632a4113181e2a105710a241054511a62ba74f5c5cc57a5567997d202b38572149708ac2ec60ad90aa63fadc4b3079fe4bc47422911fcc9c7f32477d8ff3f6397b02353b113fdcd0b0c0ab3bedd20bd09bda868ea1233871d3275f7bd077def13d38f8c6cb80b5e042f963f4d052db712b1a44ab169a4e256f3eb8ab295fa9a2e61b8c319db15c90e4e9144af54379f7b526b16204ac7913cd041c397020274dc17817b7db144d70c641a23edc06f2999f9ad283765ad2b8e20564ce3c511cf4bd55ed7291b57740235d9ac38c19ac4bc7545159fd5cbfd956ea452403510f7dc7cb3faaeaa89870689a23b96240010df9d1712f76114b3b965f133692aebc12faaeaf814b4112e087a21e7936c38082e1fe7e8575a8fb65627510bf6ab5cc8f2f96a817e35fa870282567bc2b230940eb82c70817c439ab564d2aa8da1a8b3be04bc6fe73e2ffa0c4ddd66783d91f9b8ad0c568d6ff841694613b8ed55f29ff522cb32a189769b06cbc216373f0819ee9a4a431961f0727c77d06f3bc67593a96f18759c0fa81004c9f3699750f653f239111dab7d3d92cb4ae668b602e9489bb31c4bb6430e9b024d36a789c789f3fbab6dc860c6af0c40acece40719c3fcad31a24d0fd31d430d02be722024a149661ec62dbd8d6115eb057e5ea28677b2cf895802c2594cde0cd64d90a341d12ba12264cf3f0063e4526ee561ba7995fd3258ea5b3819b2ab09700a46407466562c34c3d65b0730574fc0889139fa6ce680025d985401e5cd0d211e52a599d7dbb53fab2ea915890834e333bff8610dd04e82059840fa1f8503eff0eb87c3cc97faa3351e16ad17ac685272a9ddbda56620aeafbfd0510e3f0e3ffe11333aa5f9521e5404beae3d7772cba233d6cd95199a1e5620477436530699c31fcdd919e85b208840654a2f05aecaabea55db9d95b7b072814df18c39093f5a8cbd5aa698da4d06e94955827a2fd8a3a3d9b93c65cb40c6a20c821b8a0c7bf81f02eb0d05c65f3b83eb2cda5bcea30eadd648deba03643181b12d0a50005c383d106b0315f99e141aa97cc2fd13a60b3263e8951518e34a12cac10f5506e449071f20babd389e088ff1d524089f556a27ba4ba6c55c030d0be27c206107bcf4cad3700710afdfc851fb40dd04f8afe7fa3a89ba231bfd90ce3b4618fe02610852b3d3c9639cf462bd09a001007ed27d6c8489bd24044fb7467d1b30db0bd42e96897ef9ca7979457bec378f0ff769ed97aaf14018731f34316641bd6f0f0952be9b0a3f85fc0f134ba722946747b7954f4c45804a0ca900c9fa503f2809100c868809a30d5b71f1d543cca97eee9de456117d517b986561bc0c47f4e20225dc8854112bbf8ad520e34d9c316f9aec8aeb5cbb90ecd057cf3bca3ec71405c7bcec14cb7c8a229026d540155025e2c0bb1919994cd53f00982097a73a7e0133de5801b12936252c6a61c3e114e8a13f94b0ff32606e0faca898fa107a950adb77b1aeb03e3e20d68978752f3900396c8a276dc7168f96436caaa3a734fc01f57053841faca40193c608debdb0a77656e236a397cfd97cdb35ffa713452e0647625ac20de5c38c7e381e2f61c62b5f72d47035dcb63765c686412b8bd3290d2f078dbf8c0b9ea8d7503efcf7636e7746daa51fc910e40d7b2e3dc83e272e0c11db5b2723a03ef9af8921dded8378fcc349cc6cad753ceaf54949cf79716f088340c9f36374e77aa0dc966c7ecc12d0e63ccf51f6c0a2065bdfa47dcf4f9301287aec138d9ed2ec25dfd65cc9be27e5c2b79a0663a5d675dd02dd015065550b91c69c4c6d13ffb0d926fd745e8043404e631dd5a7d224cb9900ac2f16039701f5e867c84c21a73e80ce53be98ee94a7e4ee2a1fc1d9be396abb1818bc5fdc0b04f6e7a4f664bc10aadff143c35bb48bd05499460945e3b3719c675c6e731f02cb3dcee01b9df747c01dc5abda40c3932248f3810890fdccb8aa480618b21e0d62402157737f1d2d9352a224dd12bc5536988ed27c59de5abf25c469495f5c063bf3e645ad58cb7688dc75e9bc2ee46059d583896e8d6b53c987cb7ee6d90e0856ebeb98baf9c1ec4c847c2ff09614cc41b0a8b67168978ee9c996c2f952fc035d6f2f3903cece519f91d1e20e36d04711bde3c873e46da8c7a8bcffe676830589032b01b92c593d8eec683ceffe0825a63b638ecad4cdcb4c58a0a35057bb0b4f1a8e7edd279c93b76f79ac3b0a0c2650cbbf50439aaa31082f35fb30948b09f4606f4867cce7b872b27dfd74ceb378ef9c520946f8eb1e4e7f120b557c4b012a95b20be4d5fb0cf0a71c1435fbe349c73d8f0a32dcccbc9b359382868c1c0f02be03a8bec6f2cfb78c05d8beee5d414655115de741905fb80e2adb28ed84098507b0549fdfef7ff5ebb3604d84751b86aab9b51aca161ae5685384aa32390f7fb4022ab9efcdfc1555d6110a1350940ceb28634f2ec6b6858758b377bd0b0a16b727ff05d01c8a440fad0fa444f4df66350be8a7d973297f90b3dcc6461f0f1470f1a19a87a7c32e18a46d578f64d61bbf98b31d096ab78958487693c080086c30411275bd7fbb9e954782928728deff3aa8d851e4a8ca8c2ba0faa8db6302f3b85aa66e61f5f75c63a0f994105e29b38698a2d3c3b5839dd89f4a325c5b0a5a1de74d1ac94fd5f84c08d5034fb042b00db2af2b66b715e3345375eaffa70abce00523a555fac76243d7440489e26cd93337ba2a767670c18ebd1305e4b401db9771fafb1fb9938649d5ecff0284a53c37e5ac9a8b4f2e085276c674b17305e17aa049fe827a6907af3ca0cf8d3aec95755055488755b1964529ddf0a19007f527d28721ab683d77451cfc0f9692c34b600d5a593e954db204d412d0a1ea06997243b2ba2a4b9aebdbb8303b11ef6b3312518a3875a0b6d49e0621e099780179d7753d230c6d96785bbe3f7cb10ddd4daf2cf28851bddfb0cb471d7f43630fe95d9d06957ca1870a5711f0c3da269f9dde375b6c5160a68f29e628ed3fa20a0066ef992d164eda32699cab004f9ad724170b3bf5592f090fd38c5dff54020c76c44aaebb46df44ff5474b3dfd9c8b233b95ddc78200c623319f8b2f0609f0149bc0661ea38754d38f69bafc43a001ed72ebd025bb109869c70768ea9c6130d937e83151c7fa40180bcb0a143d57806633b092b6ef1561d102fab5a98fd59015a97023342febc2dab3219ef671d2bd16e79f3f19f3f758dca4536df016cbe04a9422e5e55f618c2abd704e8570508d0a5acc7b7e385a0f35b51cf7c84c5cd02e17422e97eaa4180215e8b7278ed763984207886ff664b36526a15da51625c0e52f7b3a4dbb3a9a93eacf65deafdd8595516cf3d77e151570139518214220b0d27c3f6137dfcb889de4c4f40756b8bf6e3a6b4e7a9d9b5ccf489b03a067aa10aaa69ff4cb7e331ad3d5c3b13bcd3e0b2c6588e866f694e88c8bf7097b1b69c0f8a9c06c76c3e98e13435470b56b7643a64b68c84e773f879ab2e206fe497280fe67cecdff5b722a507450057cefa32ddfbb0518981180f3924386b6eec559604a12eb1df88b6c3f8ac6e342816c964651956c36e1b8a5d030e98a556a8089f09adf8e5e9d9b2c76824102e61dc71cd131dfbf9b3b07e9ab5911e6583efb99d0d4fecf51a9c5c4162414a7f3974992f4614c0e636d67a92dcdf3206a882eb750c70f5f5b9fc20d2d31b08b985777014750a063b05594335bfbf9f349d8f119b0e3a28df73dfd0cfd4fcc4cea1b92d56947bd01b3daa03fe8cfbec66b505f182029b66dbce0c0cd427f5f28266f50d817dcae6da2e617d1ccd0baf8ec392b68b0562dfd2eb054d6a4864a899321683987f9e97fc6b7f1ad96aac15058af8b53a0cc41648ed8f10102b34731d50b5e7768e1c3c64787c8ed62865f0d5b62e1ef400a995b69c2b57db6e0b4f579035bca76ce61b1e98975da12d410c253143833001468f2b8f410f2eff69eb087d5b4b439694cbb625d43aff79d4759de28971ed0adc3a9323e054dc1ff96ca69e8a2e0105e95d5e9290552b31ec9398a8520ff30ee9c96cbac6e00f8ea1504871745a3ebaab58d4759c7204403b276a571c9750079df867062e53c4c0c8037489c9eae5ae256b3ca0b57a6e631296d00f6bad720404fa0b98e8c71e7ee493fbdd912d4148de1046724051e327440ebc4815841c07d7d4d221de050b3b9c22bf7dc64b694df796f3f409d52c872c0394b927d8010a8557943b20f822ee1c306da2710241f713c2732d780788baa2e66ba05b95e903032ba4b95d0bbbf513959e33462dc9f4ec4e852541a670d65deffc72642d8b0ebf244fdaf6fdbe2933be5ba8f156cefe15fa9ae7c5f42aa5236758fc0769c90e6ded709a06253080d8f670afa29c516b08a3f0876307b96f7ae6604edefb590237b3faa11f9ad99c293d932c8ab686fcc6d27d849146ba876cccfa8d8bf24807c5f2e98b1255b73bce71b1850084d24bddfa7a1c954861ea76ed233955a8d40e7f974eb65cce8669070e7a6678a893f18e3f0836f3f195d389948b0797618a0dbfc2b26939bc6ec95cd847b0f8923f7b9b1cf1ba7540e6909c17ff11369341081b2542b38c8aa3db00ed9b00fb71a0110175fcf7cb98d36b0209b2120b529e083bb062d0926db74eff87eb197fa1b6da154b5ccb15f5998e9e0599f10733da07cbfdd985139130c126f41c785bd347c41024ec36fd14c9a49d11706b589b4004e0264e8cb975bf5baa571edb489fb3836b6faca9d5858d6856e346cc31240809212c1ab2912bbbef392888e3856cb6175be3a234878903aee4768eec5e03ec057f7c5d1e54a53ef5f790f83fd34cc32c83495887ddaeef4397eecf1567ad7b0fd050a0d311741930ff075f62f9086771c64cced71ebcfeae8a7f05224f7b070c54d9161d2d53647e11eb04f8884985cd34f71d90022b5b7a5bd0cd146d42370956c4431b3beb18dc24f5541ff92649ddf7af6300359dad78e190dc681886510e0360afdc2023ef906ce0854e79b9a590279be8848f9dc63cf4296c19b864ef0caccdf3c96d5d67e75e014cbd23ae8ea6b06020b8a45efd87f464d95e57c28e06d99843d53e9b25408c4e5987c591234f5187a0561537066736bc6cfb6f43e600f271c9c2e5ca7bec8da7ad7e20692bdfff62aa9234f4628553854ecbdd3ad40961d6ef5e4fde42827db618b411ca3edbce6ff4cc1e50acf0bc199a1f3a37a80e353481a37711eeaff2fffdbb0b05cf388355ec0d11df72da0b98a9306d3b5b0811033aa002c27331e87aad74f7162e401a018d9a6b23b01134a7ace0d0625d0cc2f742a93a0d9875e48892d7e42b827ba6233d90d1886b85553043e586ff1d0f41e28d3a361eaec383434b24e949249163e5492086f24d82f4a1363c96636802586b41d3cf9ef06899085c96bf96f74a8ce91ef781be94f01acbe76f399b6a007ee345c0e0c93f6d3fb6cd7d7b7c90ac459cfa2e206f0268b77e408cac2fa50bdecb90cca9e6073aa97b60442e72414e727d4a307c1b34e529ad06282459360d1c14730da7f289c9c501d0a635aabf0e6210481840d8103cb1bc4b2ec3ab990c235622ffe829eec55e1d8b0568d2a69433ee3e50ef1d62be56f63650a3599703afb7442f58e3c4ccb47cd3090578d93ff97460fe37248544689d7b270889b20ba6e0e06918cf8ee109cd202cc5cb7141bb09799a429c1128893d6ff4ab812c028eea8db1f3e0701bdd4c9d4010981273b2bc9d3620e01ec7a2482ee37c8602024a9f13c33dc7931651f8ee9f13cc779260b0f7ea04ecf275df0db77e6be89801c6e8a3dc5a2a98e1fea42bac184be4d548b0011df3067245af216491983b350a134b4c22a7e1a12a2f9924d70bdd40acb7250cb0e75c56d60b1a66ee21b71a00728eea416a0791adb5b24c978b756ddc320961a56aad072a8526b54034c4ed0f7c17e27dd9a6063b017c51ad30a9b9cb837f8740e7983d15009a1b154740390143a5ee31098aada30b5c286869ba5e824b295ed148b6a7aadd6d116c57f18a07496ef4f38084cb4ace54dfb0fded86e2785057065f675eff0b755339ce78890330d33972d1901cdf871524abb80f3c57179fda12ab8e9a9c45e0f91c84df510b55d507e35f6787dc3fe8a3352c3756d1accd06f1fd6d53779ce8c7c52615bd00c9d82941d37fca492a4ee3e4bc1b99b2380527b537d283478fc8efd150d5e2037122f5b612a84c3536667b4ee45bcbb0c8002c62bcdbabc4f93fb0435ace930bf852896916b7611a85681062d4ba8de5076181cc42a9db7587d5bb89494056067b3a6e89dfdcc29f64df0bafdba0d563b91a19b11e79018d05156834461a2d0cb428708d316aebef56bb2f5f4b45e29f5359aeb5de00668bd987affad1eea807443315ac85b83bd23bd2588058c9ddee8417af2ae206a0791291a96bf3bf060b81d37b56357f733c3f8ec9905fd5cb8a1fa006adade65cd58febbee6a2ab5f0eb31997fde3ed0ebc3a86f02482b10cbf18753ca2740aef33aa7f923c5b1a8400493587a0d3d650869f136052557f12eab513bf95f85d70c66a11106680144702938c99fbedc4b8e542acfe82372f2033016e4f9e26a77d898d3bd6a1af8e8c08b7ce01161612543d5bfe2a3c72ef7388fa0ffefd89341c4257cf2b74e448680250c4572b2d4349fa899fa107db2f0c4e262d6db49eec246e3babcbb438030701 true +check_ring_signature 7e5c0b76c5e6add9f032eeaa63c350ade9ca31715b4e73b04be91e4f2ef08757 72883dbdc8987146b94ee497585d7dea142b5dfc4d4a6e17341ef6812ac5894c 1 8b17b73cf381361c2982e4f9c124bc60f7e5a47567b319660e36397e0c55469f 35edfb08ece5f2bb4b7edc7fc3fef01d56b93fccde31401aa666363730ce800db471466c433526ee7658404ed988d58ed44249f511bb6ad3d4087d9aa9807d02 true +check_ring_signature f0c6e8ccd36010e8757bf6e20d7846570b40497bbdc91f7cfbe60b6d9a841d9e cd52c02b6fc5e0c6162db37207462e86016d66829bb61f83eb118adf5ee724e4 4 ae8e4b2e2d3711bea63ead807e9113fdbb54151ae1ed0360c91503f680431b02 dca20ffe62635c0fc407c703905942c86a4304562a8c698ced6ce32ace6f74fc 84b63f81c0b8cd30fdf3d7b72196a7312f648b2508425c97f3ee17f314845d58 43d24e638ea91abdd92c64d68f023d9cfd737ac272dd01ac9714d0dd36180b3a ab66d149f9b1d47587ffcc6a85958218d7586a84d0d1d2ab67b78ddd6d491605f1dbaf6ee0d522a7e0e79fecba9abae2509eeda9e61f23c7db69983e8710570633b5684fbbb9b1c86e1f5fd46fd0c481f9aef3ac8e2727b8122b43bfde962d0654fd50ccd528f51e10ebdcda564991bdfe4ad476bd73d5176d4a6c22f8816e0fb2ac0fa6d38e69a65beb46085e01ae6bc943b15563dd1d28a54ad31afb0552031d0e89a0cf2b79904f9c08019aefcc6b449bcf9123c0b506118fb205fb9a6e074dc3888a8931a58e41d8f34f4fb344559d83d7b1be4cbe62a81863eee864e80921920f7649f0f0613747cb46e111569796bec23bd710f217aae00adecb405305 false +check_ring_signature e474ee142133f4a52423caa120fd7a9262d7b44f9f387003c75fec4a63b7d075 15217b9bf13dc5d3f110d6b761b2150a47e52b482376994e59624e889eb38687 29 fb67396a3513c6d1d42f6a5e248ffa09d1638ca7fa098fadc958d62e8e1918fd 8c6309f50c7d0e1c7c5cb79ec32a5e124912719aebf03a42768fe69aa6c25981 5a1d87c9155ad99df5d1e972384d1aa51ac3e579e835ef843ebaeef4e8117e15 473cad237d1484b96abcae03e2902bbff935fd6a465c7cba0c2c6a9d7021d2cd 34b3ea4fe5ca87bf9ad3634f303b0ba71afd5b02c2e7ebd1dc265b6df456b7f3 6ff803e1099f84e12bc7464e587d7b3102a61f26f3febb2325fa208f24d013e4 b488a0c49ad6c1b612effc4eef442f8317f90c7a298c320034b8905af763713a 0d61dea36ce9ed82eb96deb42fc139bb582584e0a479fffa54176a1f86d43cda 97c3a587beebe3c5aa9183b5c24ff8ff3bd6018236e59acb9873124eb0f59216 ca65fcdf662288e30e5548dd3ac174bda8819408650df2c4911d9b56f9967977 0ea7b55fb19d8d71d885016b4ab0bc8701844e6d469bada2d774a14948c1b271 90efae501b6dd4840f47bfbdfb9583be37bce51aa2a0dffed3f847b6807b5b33 581d8664ccf74e47c841672d69705b7a14a7df4f4113b3e80d29b9d1050ae6ec 886fbed06fb2ebc622bdf211c49fa0c62895489ad73a029d0e745012a50816a4 7d1858dcf61d33ad50f8e17b6f925963fe83d5533a28f9eea11c938d78fb7965 e7005362f91b4451b66c55828fae8167f75ec186e2aec09c7e4520bf921972ae 3221d3f43b04ce718cf08d1a79c2f7fbe87bc295202234192bf3f4c7a234dd20 d9bab0ff8bcf00b70b352bd12392e47afe7686b5ff248cce86723c13e6337e3d 201a45b4824790597ed55cd8a915115a2dd21d3fd77483e1db4be436ec2a1c9a df7cf348a050e73d2c7bac568aaf56211739ba53cc15e953a4d6fb50875c0f20 99488544c0aa19ed7c789db9413845d6f7c665868521f4cd761f2801467fd092 bf4bd91517db024b1a2f40fc2def88268fb9fd5eab715cd111b8b5449f42fc94 f4524421b2c5e3ede349b0e358959bfefc53c1aa8f167ffa836b04fba3c96638 7cfeed91fc113c25ecc97f75cb814c29c33a2abd5eda05249e5500d6dde54ea0 012b419c7cc64caf0c8dd5b245520526680e60cf7d357f121b1b0b983481807e 57696e2e7f69f54080689494a715ab3caa035761b40bef63ca6b43921447c7a4 1679ad2f232d12ca181cc868498bc3ec73d3b1816044d76a19c420fbdcb898e8 097d79c246d55a23df6e93099e0d1fb92132e67e2e7d940ddae0a5e5bb612d8a fe7aff34e11de3480d3af71b3e3be968d03b439cb0f2c6c1a937fcc994e218d4 04bb5280ffb7ca2e9b20a51e9101334bad86a36999f3451e4d1abb10d279090133ec87d38e97cc5d775604519c0cf9647343eaa329a69a74c4f56c6ffe8a7906cadd6bf4d47daa9a56274915759394b35d2fb70ea3e17104331b537f9e71580cdf6bd4f119125422a0475da1e99f7dd2b0cafdb24e7bb7dcb900cf951e51b2027dfd6131c982eae14e525be10506dcd655e1f3e40700bfbd3823bfb8904a850fe41c2182d41ee856fb6cc27fdb611a102fef14f8971a4ecd9f1ed6d443a1dba7bb9b488da418be996a116e39af520a32e0e6a9a3b34e6bb2ff86e1637dc6fe0d03258bdf589cf6c32bc24b7273c17a971cb77be846f707f4ee956d966b323b05f4620abf574529aa95a11e6558290df000123fec7abfb70aa1cc2e7dc41e700e112ccceb3c632678da6e69f01c645be2dd9c37b218d0dcd6cc4f5426e59791012ba585f695265a64e88bb302eb529001f83074cc6528386cfa6231afe47f6e3ff5c6a0af868405f3447a6494d89b151ccb2fdcec90a781ca439a1c00e4a6c80148653dccdcff2975287ef61a0461e2d27a4c7e12a8bf690b210df332b5a01807c104d0c9ee2e96ec90296cf5d4d5fe425f70bf94132f89b594b0d788a293b5038842fda24caf1dae59141f88086f18f840ea7a6f756f4aa324d9c94ed2d2c50af872febd33494a6c18f3352d31437a72cdeff3dfc3f9026c8ad74289419f040ddc94898b280ea896632e518817d0ecdfa7021efa02a310f258ee0eb6c0c33902860cbd37e3654ed54cb0ddb15692d7d215ac8b2aa811472cd0e2da5e1ec899006130ba48482a8406a4025f8ab17df1b220da0c21ec8448d719138297944d54074c7a4277d638a6bfa0e85c16cbad948127a0ea3edafc2d000b8f580e95226d085cff05724ad2c59621c925c8b7e774eebab39a085e7799e19a20594591ac960bb95a0189a1aef7336d226e23a44156999b1f27981d955a2898a3d6acce69c50e5b5302d310ab73b053053a75722daa82d5b4a2c3499ddaf04a28b561a5090007c779eb52402c8959fb6f488ac3c9335550095dd80c2673758797ef3c4bebbd0301cd69dfab9ac4880a2e5e3c3d8b0d1b024607947048a0170e60ada3551a6508349d39e45bd450a0a80734bb59e075343c27a0266839677a742a569a541d6f0268311f44a36770a803ad4c6ed1af57bdd237d90c1a505cc7fa0fc586733a5c0a2db3e19046d316b25f7b0e16c84bfaf1ca9fd0a2704177153e857fab45295a08d689cefccb7e094835579d0188dd95b612ead4821bc26dd2812fac19e0b8a807fbe9b0cf8cc451a5657475872232b0e8079a1c8c752fefc605a9ea04c9c11202e3e3da10e0752baf6e4bdd7c56c11808b9797d32b56d99e7661fc269e9484b0ed21801d71a608ebe6a2a787d13b5fc9a7ad4fdb0c94e28ca393546ff0e396f0799a3b7dd16ba7c87acad6110e36be451c8d56b8836af59ca1d97032bd218b3ea7a2a7341545f5cdda3646dafad9265f84d2e6a2bd04aa6668c5a3cb733bff20e5090a87b70ec6bbe89761e72ecba58a87ae6d0537efef5b953f51b355334b809599a3f54719ddb4bf60a38dc03b98fc6785d68bcad05022602efff5baeeef1067df55b5de58cd694e56c2b50bf7eb74b5564cd7dd59ca6d017bf0cb6a90d1408e74685bd6da1bfd974061b75746701c371b99fdd620b5b891e78ccb42766310c333f08e0f03e8fe2317fc58667ff419e48665ee22f2797a820974473533a31050032d637f37c2cb4a539eb7eb43610631a5d427e77ea358ea290030385b2ce0aca13ef508b7966b1c35e59dd343878d6761b767ccf470232ff2c21ecc76d9b06a2113152496ab3c5bf70366cc5ad34116ec045f04912c00ad97fce91576801022437075d2af0c0eb77a3670a5b5ead3925c60b6fce38bc67c6d92b28268a110130f83dea84592d7811f4d5862202732fc4105f043385cb141c790a269adc890ab4fd48ba2832512ba903ad23050fa375d5f374dcd4a704dc2d35dbd01f6eee001986915a6c14a91b21a6409003ee9103954595039c52d7309b4fd90cf5de5c0069c4e19a0eaf1d68d2bdca1c6551abc487440a41b27cef2206fcac09ad65b80d7e0e895ab8329da8ae9b9231a49287a5845f44c208404c9b9aeb43b1afcc5a0c90311b48ae504a0cda8fc42d29822f18903d59ec7f8cc3e0b7cfe0e38f68f50354a8d61171ec5af2877b4c8ff739cfa4ffcb53aa6a74aa06ec3b66669040e500c0c300c2955de3ea599728f521041bfb42bf75b71b4d915a44d49dc561e6d70bdc963e9ab612674039d53dfde5e3c91b5b019cdd876354587867d5fac1423a00c26a6bd23716bcab6b9452895e18714922b5c7bc55fbe84072716b0d181ab80d51f70494a69e4f15d50b8e250957ee2a3a94dd1f9220861b92b8ed3c1523b20fc2c7321571a4ab6e00a88f8e88ec0d66875045e00595bbc6612c1dc81fc5a800eeceb4f5030e829f680c170ee54f224c9e29c414c28bb0f8025a11e438da24a551b21e0476ab34b90a367346568859c832c23e00170ba4ed6cabf600022ddd03a3f742536d0d07dad5adb7068ccf66ef12f5ac8b5e32ce2c62d23b80a5ffdb02 false +check_ring_signature 8008706f41d4b1c6832d0b05d2b89349f8a916bbc6ead87e839e75f5638fd37d a55690a4bb56a2190d3fbaf5ed44072980f4c6ae923ce4d46fd9e221e58822b5 9 6616dbcc45b75fcf49c33d558aded6f0d85549028fde47a84daa143e24b25166 aea3dc4f49c31ec3d38388c472c55e77ab18dfc060a51bb808f2494be23dddf3 f50ab346861747bdb09c7d9f031c8917569ee1ecfadfc7c0d78f4c788df2e487 93da0d4965400e756d0830d8d78fe33dbf801229ed294df7711aba95d3363133 14f87f12a5385a254e15b1a8a0eea8e272621a195ebbd0759c4a85119b33ddb7 07998db855bb3ea4e2b78057c6b00e910d182a8fbcbbd24acfa22f3f4ad9cd14 a4ba015001c470caf1f1df819a81fd22ba04fb7b2f4b401c30a8911489a5f07f 704cca2a345b6452905197b115b359d74843ec8a329aa6b715d08e68ec6a6f52 2b386127628fb0ab869b0e29696a1ed29566aa5066c43f3a71608e0801c3bfb1 cf0a9b244b06ddf031e49682812ca9fa7fe962eaa7ac3037cece9b7fce846f0e824bb96be99d52e897df6271bd033d4b18d7940bc36eda5332599c9b777bab03f848467fafc0421c94e720b94a9d424a7d833e6a1206d40eeec86aafa27d9a0eb4be274f2a4e1399e23c54923eac80bfb26292e8e6d71c6ad6984205996d40054d97fb120621e7a900bc7fcd78e9d82e1d1ce9321306a89c4c4d76ac621395045d22d32a09b7db375ddfeca1dd5eba3388fa978faaca5d42016e96441ad283020ee15d62f0f976e603811618134d9459428268f986d59275777308b7ac07070c552877ea3a7be84f9639caaec5b7345ac3a8db82655cd28c67530b5180e347914705a0ad78e47e7c63ffb1a8ca420dddc6caf013d741cc8b4b708df637789a0d2587f6f8d054d3267101bff44521cea01710d8b8afbc1511f5245565d325130c79ebbe8d8325170d0968c253b6a3e30ca5aa10e075240a23876b5603dc6e260346bd001823941c7ddfe4f11e7742ea29b4adebf0c80f0be8fa0d4a3386f71209de44f7b3d9473760248da776f8922580581aa2270e3f483f0457fbd46389cf00d77d74535da4fe78fc48da4108bdb7cefe1627b5f4fe638504e39a7dd540e3043296db5d5e370ce41213923bcc2cf450668658411698db1f01bc5e997b39480786e2be7ec236c72c849004a8b86bf5ea15d4e336424379935ed4cddd3e234a0b2269f76ad21bdbcd626d884846b1e9b639f873d5e56f98538e35fc14356fcf0932ad262d6f2765c67ae42d821b605dd53f9dd8c3e99c90bb5456e6e9f72da400 false +check_ring_signature 35cffc38f681a50a0c93b4fc2bd9f9a2c40f24b28b422050e271fc39be7061b3 6f323f9f7c8b45197fd5aa76c2077043aa587cb142a968234add232c5d75363d 1 648bd48048cf9922c63b084f88761d1a030b5d9b2793de7246da07ce29cc7162 bd8bc22fa375de22c795fe365edfdefedece1ae1d3dccf224abf56ec83877007e224f41be01d9c98537ef18795980e9fbe225271e9e98ce2dcb02d504431b50e true +check_ring_signature 8e6feae030a19a02efbba193b2f1cfa4ab1d1a9615f765045b81fe49455057d3 998abea9d84fcb20c2d2f50b23d3c1445e7c273bf2bf4cf1ab075729d29e9ce7 4 9fea40c93edfdf92e284ddc1403125fb19896fc58d5e3157db5236b972250681 f00c7692a04117ddf268aebb0d4906916f6e6ebe5a8e9977e46fcfa76d9182bc 9b17282afb0b8de9892eea0d4c977b8eadaca5f851f2f932cf2a641fa6a8cca9 50576080752b77ac9796f6fda78f3d1da8fee509ef69546609d22e49430a525d de2f8e9b634c900dbd0318ee8f106c6eb8aa3ce57c0fe96fe12a13048d2eb60fa13e25f9421681ca5c8b47bc19c719bdadfec5ccf77e7c7feb16852dbf95d9fec959dffaaf950ac54c9e37c15b09f00bff92613881efaba40c5db69880045c04b74955c704485a256f013775b14ce638faaed8479fece868da0b74f2cad014986ce8af3a2e6481bfb2c7c6942d39b5040c8537d30d64c569339f7593767cb606520af211694043b7fefab389163cf30b76599890dd1864990fabfb3eb94fca0258d8876b1ba4469f6891d1975d4bc86aa38a71258c92007b67a2794a80de4700cc02169f4b0d986395a3b9f5e8a2afda9e94af10f0cfe1645d073c3617c3ed09 false +check_ring_signature 8ccd4635134994ccc215fa5e1efe5ea773dc7faefe4d75fccc6633f2ce5b9429 bec534dac0296850cd86ad45de575c81d6dc6f99757d0e7877e96c79a2fc6050 1 d5a8a40666544434840cd8620dc7b5e7e43e7e1a4c045e034af0cafffc2f5a4b 3dd67b42565960e58189337a8a03f5b4d5a685cc0fa0773d048ee03226d9d08e1ccf7819f3230b94e131803694d629e428e99894352a789860a460e612725603 false +check_ring_signature e92c2b16417ff3b4f45f70e9e5dbf485d3146bb5151f7b42c93221f2914a05f7 86e8ef1e3fdb9da464fa0b843a542d487fd68e83dc5802f235d25067d43e87c8 1 b338f6148db3c4deaae6ca46823fe0166c5c3e4873053f598f94575205513745 79672b5d8b59a7906b8beabeb8120d7b55554b4cb1788225538676cabe6732a0342aab112ee79d0123d2a7454cb6ec7ba4b0cdadedd302e7ac7b5a5086dda00c false +check_ring_signature 3e6c9c3c013a5bae7e062757e71a0c0ae376177b938a092e0a55721ac021ac6f 34c5d0e9e5bbe4eb1c41d7eece1f3ec1b269361652f7ef8873fbc6747b836aa6 36 9ac9f616af2f547da51bd98538f75bcd1a84499d92f24527387c2e5a1427f4fd 7cc4ea5e4351f885442032d423a47abb6c59592b71b59fb758302e900b09185a 14798c98bf1a770818774aa5fe059a33bc4d6255a4be1886869bc5427d5d2f91 de36895757a1781e2be08361473f9d65cff8cc089f589d6d511aaab551def697 170e17d04974a3d301cbcd95e4233ec07882142169e9e4d66da6def23079366d 7c787cb84ae504a9e375e65b0d2d866e41cab96ca7579a164b0f68892d0fa308 9f742ecbff0122050b54466328d04167fe828b7e4490e9e5f85a695a4c36bd68 a0ab65e1d9215e536afad38528f2cbd30b2e53f34bf163ea22858b610c47fc07 88f31bb821d9a48904e50d401e334ae8cd5d502fb6e7126e531bc567c3cb8fa0 fdabac0c61b5adcc715f2081c2e1365d1ee37ece89d5e1ce4ca371bc216bb624 bbe196a906a5047146e3f7ed968fbd49071375864dff2d761f923c59d8d924eb 3eb19d32ca8a90df01578a643a8c19da53fca1b40fa11ecb8b7178b207d322b5 4828d4085ec11e108e19bf5bc0e7c7271ecdf2d326dc27cbdcbb8ac42cd09947 2bcd9c64ef204c1e6915a1dab2325323d1d53c6686dac8c6c1ad3cb55b70a37d 4dab79848ca2741325fec1b728a397a54aa27a367c6469bbd338c769eeb1bdad bf6ced3de7edb688709285d8e8243d58a8eb728f6ccd69f2cf99f06cda2b4918 6a8ff2e832fd6300062af7d0153733b46725027d41755187ee6e64f8405f4522 42a3e9c593f9b538d02cc9bd068a93b66f690b39afbcb6961e88cd3b52c5ee11 b6af49f88b0375de2da30bd613e1c69fb834d9ca9a06e8dc8dd17f51296f1c8e c7a89add889ef435bc0788d30cb05728f035c18a76946c94fbab9a831266e4c7 f61dc6e6b641cf203370dd8b17369b54d6824099f3e10015a8f0a0748835b643 2c3512379961801d8013b632c046da69df9f0c4e590200046d86a6211bd4dbed 0d9b6d479c8a8094b2496c5e59bfea3eb02be163dcef2f5316d479a17e7df305 356c10b88d3c7ac1630069c899a85d71050787fba924d11f0144215c1dc32dfc 2a975b0348de8e47de8916ee8e5542be46beaa1bf37262bb5b279d34222a04af caf62b80b037e1658ad8ad5fcd4a26f14a60f795cbdcbb91a44f2b9e6c9f3fd8 d773c6d20bbdbef7276c8ed519471c921abf3fcd830115dd356e9b1c4921f7bf 4d072491fb222205f9be6f016c39433203630efeb2cd2da7ea00f68dc2ced231 a6b2a505928682063e41ed4627efd6ebbafaa246b1bdb3e3aa5c83abf36206d7 670fb164b59c07d2bfedccb51d01745b86dece4cc6a7b72b636847d0c15a966b dacfd16b7b0f27184787ebc242381492b6a16c55194f3ddd93c650ea14915ea4 5eaee6a84af640a426b1b88fa3e6292f92df28b7f2e3942a2f912aa37b34a424 a7abbd5e6782986ae30d6e814977bfee58604c40a3f3591ecdca7d10235ab6e4 470a115e0f62a555ce7ada64da314418c4a4dc9a3f0b5efe2c86e2bc8b574a64 f0f75302911a20ad49c101a64ade72cbfe1d7b07f68df8ae3c089cc80b50bbdf dddb5c978817ddadf0f4ca0242e20dcc9929e01fac0f41df0c9b45b61b951f0b a7c9758959c211a3cd11b8989292b1f63c3c988329e99b238bc0f97959d240057afe97f17b997dce0e607a9448f42c275b3b492d1b3116442af12c03711356014e916ba29b3fce01563b6254f4a744e7bf4b4d3a14b666c0806757c073fab5080cd75d14ea047a96bd1633c2b0a1b19d39c4aa2baf41b2923671f876b4d8050f2d499dbf5a540646ace7c9a7c0a605726bf4a7a5c0881a048bb9dd31cabaec03876cedaf1104923e7da9e87c7c5e52ba01409c96b1fb19d4f43c83392e55360e2888cfd245b73d2e7b6eb9fba18f10ae31f0f4719f33bbc9c1d8ae2d6174a202915f3a5e30fc3e664292fb176aabb78db05939eb4088fd0996349b1547d3ea04429ab0362111e1e5968ef90319054862c03d2165c47fb9adc09f852d2f868907c62facdb4cf2ceb020e8e375a0e6673305914fe8e5518f3237362c77b887e104929cd69caa05a5f97750663b8e8386ebebbd736b017508b45095ef062face608f8d178e5fd17ecbe25cfdcf0f400e167f8871ac0bfa262e570a78a2792082e08e22c9a73f19f2d19db460e628091d07b0b1b75c161eac29e63b13ecdf4794309a8f47e35e42038a8141008c42f3954f19500a5b86f8623c63e44d48f9d3ab10cbb591f0484b9cca8f9c8aff16f22876ee2465756a564586c583c1036b06d580df0d011c18bec43ac50290eebf70b311675a73d4adfccfe039e890a3d66f0850c37f84cfa557a73bd4d1d561e215b146431a887e3b91ef31dd3de59837f1bdd0441d425cac9542cc24002c5d9b722460d273fbf1b609d6a10114e443659674c032d5747cfcbbf98cd9be7101c36e64cc1e9a01a01e4b332caf1645ce68f9ea7019879363760d8acd5ce77ddfed855dd25aae938ff38f14313a1f68696c0d1330c17aa214f8c22f963d7fe34e5ed8444d8e7e61e31a9094a412d608c1608184206d5be3d28d7fc51e8eefc0dbe573bcb8d45cd50a9539eee1a02b4a7eacebd140410ac929827263561d87544e0e74cfe44538cb471cb2334e1389d28645cbf77019d8457222c45affa9237a74634b8e63caba9f211981c9ab890b2ee7edfbb0d0693cd982e8af6bbfa9005bcb4bcd272f106c8529dce93408046b1880adc5a3404600d7d543d9496820a87d9417135e4119f2719bcda5c517eb5c1a8048ae87c0a13807dfc4a21b03e4edbd569928463ce801bf233f8350eb4dd289c98c071ab0e4f0e92e516101820670d4c2e0399d05ea488c1fcc4a96bc0d30703dbb081be015be4c290cf4859fa27fd5cd377dd5a9db7370b2f1c4c997c0ab330a43d797c082a754303db2bfb68782a2240520f8e86c96d707d50c319e36c1fd53dbcfb0d0bcc0d744e794371bfa88935e4c43550054f94a511f48913c3331691830e920d025ac4971be23570fd12e8c7d752881c0cf1dc6dd9239450a78e63649f108e5b08925ba9141c8fd7fffa3305e2e35477e52fdaef8d080cb6be397a10aa0a669a0572c41651b0d69326f3ea71bfa6bc4f2a0fa3a5a0658fee1e07a822060bb3d3024fb559be01110eb1736201a0085be7d4742787976c831f826a8a377c1dfbe102d6f50ffbbdd1dee90bb52d10477be651de6c145fb05ec9033cfccb50a5811302d9ca54732dc2e73ff575a2ab964138c0c320562f3d290c257ecc8604f1ff1d0c8ba2117c9357be2fb8574a3ba21520aa0b4838185c45def7e2da89e9ac95750d4fbd07d368832b3ec89f62a5954092f8959e7343a675e71fbe2a9dac5ab01e0ea0decd9648a9a5f54b6c310a9d841d35aa71ae0497acb4ecf83f17a0bf2b3f034cdabaaeebfc32b11558c8a9ae49f2c7d16a7dfeff764dd10a7dd9da4cceac0ced61fb400b04d1bd68d0c26473697fe30cdf872dab8ab90875719e5fd7130004b46e2133cbf54261c18fc5f5b76d51527f121cee0cd1317222e527e1dc78ec06043827a04aa70b094c2da5c8598494b75bdd1e479dc24e61c1de4aedb1f82d05216f9f47eec37c68b2084310f5ad2c3392171b11e2f5b7c55b11e3fa4081f80f6d46999bab461af1a1beed41ff662000a0287efba92bf9a0f2ef68f36e2d0603d4a1fadd57e9750a52c4995217f88c2b07fa6da8af6678c79cbde3294669e30f5ccc4a33161f568889fbc2596139f4971e8dce354cafcca3e37f191c448a1b02b045332f0f476f2f9da3aa3184ddf6344fc07bcdc7eb8af99296caae9d31ec09b4845246cd158f9845c086560762a65514130def3b2cc38efa9953fedcc6320c2f554b657994ba8a10eea22ac27efc5a8aea5f45c4bbd1bedfa01ad9249cfd01b28092f79e80b19fff1b6a7bdc270cb6ec17130a72e057ac656be7e3fb5b2e04da576382738325c8cdb1b715ebc9bae5593a3dd569d4c9f83002d9a4ba643d04308f321ba93a0c8d13075574b80a4762710e7207e04bff05ccacacdffbc1b7056e505b4dd140f5d30f852e335bb83fe1108d2b5be2ec2c0df641a74d3226b104887fcd21f2f43946633c3935da231c30f1c4e2c259cbe05539a3924bb9eb390094b0b5022405be5451c47c4f49d40f14e93277c5c36cc9ea8a004c7656090f05910e5d8c6f6bb0ff82f966e2e8338ec104f809366c3a31a70585f51e7043070ac5679ecbbc95a605865be4f7806b1740b7fbef0e504c5c87d58404683433e7011f894cdf7d08e1c203423d8cd925a543b23b77bcae5bc8078392f38b880b35066823eedd8651f2aad11fe9fe6396f7b989f03a5ee57fa99ead865f2b2482d40da3a27511a529430712733f9b2db10fa64fff80dc851353e17c290b0e9e39ad04dfa4c6f54448df2a416f5c3808079fe7709f2c0127fb99cfa26e5dfdab3eb6062214e145f7098f07f5de948f4657ef9b2dc1b397202e8a16cb219f2805d6400b06a02ad295e3ecc602a742db04db780da0e0b34d30b031e8249e93fa14460409dc92af2e9cb213fdc1a9c5b6750e91880ec2acd97cccbb0afb855d79c0f59f0433f6fd377ca81aa9fccb2266b651d4acc7e3bb3950776e4a48f719f3a2617704649bb7005a27239ac5db75ec5dd0a9eea1f0b89f6fcb6d3dd1d10e05244bac02c9372237b7d2d0c21ac8aa2f1e652c7f38ad77635a03f607febabeff0fa3940f205b556d2a296f0aa8fd23a4a992e63d977d92c4fe53b27372dcf5ff2849060da108193feab90da72cd2aab8255c6d2a730732801c3958666369f1b52859e405249f3df77b17f6d1f481f2c8185f197c566822f4af733009ed4c894a5bfb6908 true +check_ring_signature 75425d551f248bc784f4735715690aa6c72c7fb63e7d58d63d1ac0b22fb7c8a7 9ee7bca8ba2f3abf36bbf8ec7d546d07bdeac8a149bf1acf166ee64e3f14d74b 127 15835e824def8aa2d95004ea48649fbd99abe3b30103e5c61928ec279dac0fbd 0fd42a5b4ab76547da9c78e4e0110d38660bc9ad53254f0e0f00dd21019000d7 e74a80da4f2964c08c57ecce349c8b8d36b0fc27e01ae30faab029a619789faa 78c6ba9e05a940ff3accb1435ff32214feec54f4513b14566b81352fead52ef4 1ad5b0e62088e68fb92a59c86607766d5a9421a578d7da2a03806d9993762833 590c934a4c49db05f2382c9e528ccd18dffc27f928426d8766a97bd6d909333c b65e6a4e9d04c7eb4e808e327a39eedb45f69c2d7ed07df53278fcb3298f4fb3 12cc8e5831e2398a40edf46f210b8435dcd8cf2d842ea21039d735f0d65bd626 2ed277e9171f432c3111579df5d69710144a0dd9e33ac678c539dacda6b6cbb1 a9654d14dd88d175a5e28917be53d0ed3b4ee6b249256f18530643fab55852f0 de237340d984bcb4236046fba89f637c0017854b52b92e0303c6340b9c8f3416 29537ab57c2a1ccfbef71758f2674c40b743dab9205d5db4e57a85c434ff6bc0 e2a630ca8443ec489c1d2e619ccd8528c110e759d5ced420bad2bdb3c49cbd56 521361f055706ac2a08f1dc578abcac9a6d72e78cd705c7095d185d528abb5d1 6bb0d69978164cd67f160174dd7b00b29015a9c09ef4f69fb17daf6f36bc173f b334b838d17d240d9c36ed87312a6526769be729a539170771abc36940f01afd 3d06065c36a35ecc4667410ce5defe29e0ba1120b3d47bf4f8c505f98e2fe294 aaa55db2a79ee58238dde42ddae583ebfa555dfe6fcd25a1d5e0254c9e9d0edb d199525b6bba291660fab9fac042f9dffe8e5724b381b3ae37f73f18baea2a56 0509c1117a0a585da9a433477fab0f010d087b4f0d823aa248d32b8deb46de9f 19cc8b0c273da9955ad0e98ae210da78db6bfb75b49b0705305f1446fde6ea9d 41aa75267ad0d4b80b332bf68455624785d7f06403e89439a32547f3e47f6adb 6055e9187d75c6f6aa27432b7ebb75a2764b53f884ad3627acd44d2adb88c252 cd071121f4b23bf42dff2bedfe01893844ea66e779e4bc25cf01ed2db0b2a53d 26e9e2f30c38afe5deaaead1812ab93611e0180a8ff42e606c84f17a900b8fde 626c47834989d1ce808962c49896f47dfaed5b2fa973982779c27337d263107c da223c90cde7fca2c6ae8454ba543aaab238de4c6607be3c628df4a103dcd99b d5cc2597f91055d9ef47f49ab17246b6cdbbfa6e99658ed12451c36dbe8363ff 59ea8257ccdd55c9c8867fffdf9002cdebba585279b2e36abaec893368b9d757 5e4dd574e89dca3bd5ca253d297013a0e7019a9079472700ab5afd77e8449fb7 c8778572330670d2c5c02d93908e155c16dd952e2db71403a037bf2880a44619 ccf202e53f1aa06d13752922e5e3f2591002cee888c4034a6b60f4e9b4ada467 aee4f0a1ad18a64aaed80f0de54ea7192bab47af334528536b044719c26c0cfe d6232a2c598abaed09a36a50731ade6bc21255c8496d8a43ecc96242a042d15d cab61fc6303c677663359df1ecfc6cf201908b2582e5c4b454089c3d73031bfb 2c3208b1b8c55f1ef1455f00d81656ee8e693c62d8e70417badc601ade44e65b b930594c1aef5a1aede259da44d10e1a64b383adb0fdd2e0ffe53535297f25ea 81756d20c6f3fd762172aaac4b435ee4018e3c2e8bd9546e2a6134af8720c5f3 d49e235046f0b08489811d079134d793137999037302219060c9d843fa704989 bca6cbfc11e274a4d2a66d924a56b326a5b2a3cfc2487a37a78678bfc4224200 1d1524978289f3a3af1efdec84c45f72ae9d90e432d977ec5b147505baba8fc5 c1f4ee8b52001229eff8bbcf176c44090428b61bd3a67592a505bcb57a30cd07 57c2d862a927552ed14de66a291beb802ad05a7fe216f9dde48991368dc5e14f 4eaefadc86809b0e2360e588b9371ff493721cfac1f8c5b32d9538d74fc6550a c28ac3e514b918e51b994dffa9b8d18139d81a92040c80d013658db89467651b 9ab40d9c22efe96c2df96574463a84f53f746f9ea6a04cbcdc9c66718e1b8e71 7d9445c79a89eb5e6ddb775c7365286abac1913ff2aabd33c255460f4cf64c54 cd03e008c494f01472396df2667d538eb433b8c08caf043092d538d575d5470c 68206b51b97fb4d231940aacdac5a38cb0bb9dbdca7994d102ebf649f2bca669 ea01a03b99cc2bf743d5afb30618fb274dca2d073a6a268b902fa01399a5f737 b888fce5850f1bdfa79e3c067b3faaa87a29dac2cd60f8bacdc79eeb19855238 5f64386a7f8dff96d19d6fdae2e3efb030a60d33892cd1da8315691949de3a4c aa11b159414603554a1d6bf8eb6228f0d1849112825574dd5b38e14cca6288e1 083d25f21eeff0c0d068f711d13d261ed2c1f9ba96c48d74a496a5fcce9d8bc6 bc60e39ccc9b1237bff37e8c51dfdec57471b5c73981c59a4b4565ebb6c54df8 3dd2bba7939358daae93f81bb5a40c4bcb6cd835633b7913b93332928b4bb8bf b773d1e6f25abad22caabcf0286a2dcdbd308fc9ad816f36c20b69fa48c263ea 7c3ef08c225380f7a6d3a4c05067da72e468d8116da40d21d0f8dce57c5c0f4e e5a6de60d94a1e71bc37e59f986de5ff450897e8fdcf76b2ce36bd0325d93688 307cadc4db727787172d122f456796bcbd00c832163618e123e57ec7a680807a f1eacb7efc676cc0471be8ead06238f0c4ef4fab60e16d821e6a9860e4853800 0a9a5db753974a947be064b19611735bac1f6387af9cde1222fc0655873fff60 86d1b27e0f1212b2360b370c95d0badd7390e5c551c26dd3a3ff4a4128b004be a37bcfd4b34abdf8f35eaf71c5a878d958199887f7322c5010d2e4ef51b7ade1 ada4411a2134f7790cf061d19808f0c7ba515cb16de9d51dba643f5b7f220d5d 3180cef9145d724293c6cf503d1d7b8e62143710c4bed45c5b36fa42c235d250 36a0db81405c6e23faaebe89e28d3ced2682429b2a9bd5d929592edfe71434c3 74b44f5e72745a4cef07d57b80f7b37e3d0198cadd727e7d5f33de41a68f3660 060ce78084779bedc5ef67e56d5eff1ab1e00f078ffb60ede648530aa1ea088e 64b21ee8c535991d097aed542da27ce3cc228ee37b94d22c6bbea33ab0f78ec7 f9f9672ca99b01ae2f44aee494ec256bb8e95fd9e5fdf14136abafc33fcf7bbd d94e2d3207a86a5c3fe4ed8580bc509efe7523ef35863d542c8da53914b74ee1 b6133d47e15684f02da6c6a9584cfe833f769cd98333b41156d65558c2743185 3e888471f5b83b467e10fe3cbecb243fbdf4c2235a033f40fece839487eeca1d 49a1db25c572feccd3c4f67b7fe254ab9a65072d35de1e491a64eafa6a902171 7ced43999e2c22342a71491513d49768d4c6d02314e5a4141f0cf0883e848ca0 97c7d6888a26ac73a14e500b05f12984482dae2c1cdd5e61592581e47f2c0075 f54376e9ddb35409f14e09d15636753e69faa9ca73da910318780d5da02b329b 19091071eedc8f6efff5dc4f07ff14028809318870f2662e65ed32557b0588bd 883a3dcbb166261592e171fe060e7194267177a2e9470c7587a6748aded2dbfd 54b4f68ee1170066f7d5b756100819a0f9ccf61566bd589f20fbc983d3982e9b 9e061eff2ae7b9700869ddc4395045daae30e7e883dbca0bdf18ef29b089c669 323cbbb3850a83fe7f28c15c48d6c44501035e2baae6d992bb79d66d8e9e7e36 852d0a4c3579c958ef69c64a7c079e698495601e0fe7fbad720abe09757d9bf2 e3e14b83d631c30bdb6687a6e717e46263bd9d6fab31856feafa4c6fc9f51d7b ddb83a5de0b83e53745e0e8d2e4434a577f9ccf54b8e8c413b8553a319b0dba4 cc6cd2fdcb59d9e6a4003e8edf385c124e374246dcc838e12f990f8d964e3284 8cc4d0f388a152574badd7c5bb7082c73e27097267f34650f424f68d1c9401b7 5dda8451f29abb37d1db7bbd12c3585bed94d4145b49e3ac1e805274d73c3ecf d4b44e74b8b96e40aaf89430ef75a8b525bf8a467b240d1588e327742b6e507f c57d009b8c8d4f74b3a8ab90d336b8c94491eb46727f8ecd1ecaafba3536ed03 51519108de3f810af6ca09ed8f56577f2ba9265395d9f46e71b34a6019b07b70 a52fc2d1df120b5e4b2b221b1faf0ff24d23a4fec231dc04bd915bb10987488c 2801b244234231cccfcaacc0b8a8ffc31f0f3ec63e14b20c10ef0e260cf36159 b12fa404675ed13777b642a02ed26d4bd7a6e9ba4ae753777e6132e67ab2fd17 b23d731b11beccfff6c82d2403fa558805701e7291f17f0bb3afd923db0746e0 6b75d54ef5ad89bf257d5f7add039b08407a6f4dce96a11c6f5ec28d402d1865 84554c5e16003d474be12d9a23048c4a98d984e64cd0d5f51709ef24b2c81d89 74b2357e367f85515a86cae4e0891e9794aef9614dc450c61f40e9881c4fcbf3 ac8cf44b48d274ab2a0bc51503b4e358b0198b4bd9dcf362504f53442a0b56ab 915304fa03b17763da7f022749977e8846d57acd9f961f03d020c1e2c4b155f2 0f8d0bb389434f35a7ba0dc3b831274dccdf720d8ac7dfa5fd99a31e712c3653 523ed54aa12ee144cd6ad02d9fb64cf91c45ed17c73c98272c1fc3b9286cded3 e69ee8c342eedf2856e37d9277db6154e435ff4b5b5965dd1850dd27aee89bdc 0b904c7702f485484e4038ebe8fdbe5af3f2cbc91c6151c1e14bad061b55bb74 080c3840bd86dc85fccd4e9948e8ee35e2c68ef7f49ac79c1deccef8935cdc6e 6c015197f5430e8330243f0a95a3e6bcfc80b838e67ef446a4d2b6bf010e7672 ed9c82c31f15d208b627a9c93e7746e5dc33bdb65164a18caa1baae36b1a988a 4ef94a0d3a85d055948c1bdbc2e40b992396644461e75f9d9cac6eaf895ddd91 3a78bc05a661f4f65eaa65b9cfa687ed1bec2e36ff4644e9aae323218b1f1c8b c92b6c06a6f788f3b4345ca1f194d59bc9cd85ecc8e148a70106bf72f4f88611 fb61f8541f1e0f4f2256bc3b0dd1ec955853bc2a912d53e3d6164533d83106ce 16a892a9cb546ec60c357243479c1f55a2a65052920211fe8934644485542395 d82e3838b43174e3057afef224d61c2d2df139ce1cff56ba673419bb3c8a0f53 37aa306b8a19905fb277d1459f8104f3005ba591f3af6aa06de65d849da9afce 1e4afb616b1f5a28a4fc80b995665feb2c8be3fb79980b7f6b609a166f6f5717 4d38ef7dd88d54951c987955b4f0dca43a4ca5948d6f8ec27f51d6f9281e538a 7fec419531b8f4e7410f43dfaceda795fd8cbb7f07ae921e25173cf1516e51c3 e41447976bda567c1a768384b6f5da2045121a0fb462930b7252df44a4cbb5dc c59dcf0b5a29beb2713c729ee60e2f7ce3e21c658b012c2efd8ccb610138eb1c a59de5ca6c192ff528cbbc9f878d699788758f0dc1e1dd4736f131704d929321 67ac195203ef1dc96792a8d0b0f43fe4373ccbf62b35bdc9a3488cda793b1764 cc0d3cae326c7c34d10f1bc4de64485fc40012459b1a025c872072e7e30986d9 343138b510fb68e258d6707a59beac0e6ae37a2f3d26d57fadbdaab7f9c20987 5403da81b08b5149200c6c327674e7d636fa728cfba63acde5bb3d376c3bcc28 c366987e4b781d04ddecd782c109f6a4406fd69ec796cb399aeb26fe42d92cb0 2ff59d43848bd4805c935e6ed6c68bc958e5849600fc38c4ece27bea0df84286 25facf38958d660eb673afe81799cd0ddcad337df69a091a3e8e0d5277420c082ddc4edc9ef3f574ea8cc1be7a75a081a0c4ae8c3fc6bde4fb569ba6c4cd4a0c2ae020802e7f6fea093f1e761c22eea13e23367b17d66dd6b367e58250994b061edef2bcaa9d6d954c62b58a915237f178976cb0ab2c27e596c781372ad3d5011ae5b7c16c5ba27412a0b7710d56a0d5f0c64c5dd5b60934b0239cd040dece054b58a3b3f132f89f38d4bcc4fdfc2439cb3dd4f2d83eda2f5985f2b590550a043b4857b400ca72b5d2b6e3b43dc27ea5421655fcf723eceba08b78f633dc4b0d8c525eee2b94dfc0c6ce70cbf05b869547cdef15bd5380229944e1317fd8cb0e001a4506e99abe89378bea7158a238f48f4d3a286cee56cee6a88afe4a9f2d06b6dbf50e8bdcb52d57875942bfd9d0913afe066cc51077f6e83ca5b163d5f30b546f9fe358f90bac2aa566fe154c675253ede1da0492cb9f19ee275968a1240bc33502a6fec654eaf7d8ecc8d5fa62611a9661afa0c4ac67e6a66023aa9de40fb9e64d0692f14a1a637db73a90f2dd955f5abd3f633dc72d038e7a2678707201cf0b3af68935306a3b750d0516f5e6048f686955c701a16b5866f3b92dd4c300d1c0d15b251ba7ef22c8287be85f9ac94f05989a07db1cac668ce1b3a5bf840f5117cd336ab68f093e736061def3ea2bdaa331c8bbfcafefbcadb8dd3f5efd07bc7eb6aa4905fc1addf76f0ebd03fee3ade1e2714efc0b13a5c464f8cd870903dbac0fdd59429bc16b1ed95fd828bd6d56fa489323669f417b9d1c9d3cf43e04f4bdfba096594fbd1df207a2759d1964a6f3564b1ee33ae150a154600377e802446bd0ccf94aad4b6ba3ba3cccf4829100b1d26350d24bc8fa731085323f2b03bfb48cf5cf2fcdabd9f16795fafecd92718e8876d6906b076f30de4ede6a3e055de174fab8e1c1f59f67274bab422871e187ca803ec6f4cb758382cf1734d50c9cfc6f931a26cd1002b88264f0727b42c68f7cd6829144f15d9be554452318032558b3ddcc460e0fae1f323f3c2b916c63ae952554c5aa53bfe53bd1c56354041c26b416741b07735829f48afe726884833358b2d2770e71a2f31f81f7031b099f0268f81a469a313c8d2282bae1bea441a70ea80b31b67ed8c04e3f9a2c100fdb9aab7365740c4d166e669379334d3c9098ef7fd931c55f042ae3b8af61230c67435d8fefa83f0b33a9c9f56a2495f980f28eccfda0f341bc64d0b186d1180aeb6e6edd3810438a535aebbe2fa3b1d4ba4a8e22c0fef9319c3cd7454d9c0c0cf5bde0058b576273c2d2474c0aec7d4d84d5ddfcefc6cc1ff3a027fa8b61700c1c71656461ff9cb234e2fcec4ae16381a4444444272d2a13476d4c03617a9203a9c63a18e183ab91ea805f68e79ad869a68d2a8bd941f4e03a30d92ec2b0080a59c2d383c810e697a72c1d703ab0a31829ea84403c7a57775c85d3da4cd0c401a52f63f963efb7e370f9672d81e822c1c45b0725344986715b34900513c400038046018d6c9252753f4d2500863da647713d308e4ee4e3170a70d1892643940a7163e98d87ed61021565e3cbff3d56628253fa2923ce33e91240f64094b0850c566bbee68fc55c3b5e2a65ef7b9518b1c9989781a9340b1681883fb29b11640fe6c7d9ae2ec62a05f065ecd82c375203b8896163c678046eec891150ab58580b88df9f0938bacf9992cbc7d410041b900d75cb40639e564f7c89059ff4c24408f96e9503c1ad2118085107992f720147843008913213d8656eafeab56a053a0794b66cead13083b13ea17d13db05b1f7863582658a1bfcd2133a8558403a7d0b9be6161e629ae3e6edc1f7f1ebdaff0d8aafbcf4b133deffcca47703228b73013d9c72216caab246965caa508a7b8c3a7bfed3bad974f981af2328176e46e1019f850af327e2e9870b8bea96e671e483d5aabb8317702cb2ee6bed69f5f1410231d1abd76ec634bcbabd8c7e93a623d9b298a44a7486aa62ad4f970e97f2400cf849d300a8f6eaed47e67f3dae0e912814824ac1eeb6cb31957431245e8c640f8181d8aa7d73fa139e7188fce7cae7dd4a2b9ce6789fd39fbab5734a12103f033cb5674f5562c39cbaf749dcc91e3a565ecfaaa3ac2b30500ac66dd5e8012a000aff2c9e6cc1fcc66a90bcff25e005d9eb87ace51df5c8db2da1e0fac2e5e408a4337485241acfefd7f9b9d4a578a017c08f90f0fea6373e70df10511e43a308d82b7f700e8ab077cf13513e654003ee441a9df53da587c7588408a40906d3062f12940706ba95ddd42dfc5d065b9595981668ba7fc3985c82287f4930228406b0e675f846d7c18a8e7097b718d30cc06ae5c19423a8c8a76dec3864d2dc4401eb8df6a196e859cda0c8e2d56bfc06a59739b66fc765ea20770632a700bca90de6870551f0561347978c0e75e0553f7269af078e1b76cd3515af19431dd9940d9d0a292a6910413caf30689c2b20c19cca7896e4bf1d93574947c1ec5f82f009f5ad06da80a14694848a7c64c4d9110cef8aee440f0a75d0afd907732279ae0e7dc814a1109ad17e808cde86ff1c39a71d2bc5984c368b4d89171097c9c89e00a85b7baf2663a75ed40fb956e8684709f05ef2b380196438a13cacf40c417906ab4a4d6f4d3a96f16aad8bfd2e0fdc0dda3a1e8e48821384774341dcda7cca0e098f27782e01446febc388e54a0ad20a78a922bfda9cba0e21a18089a20623042d1f1b5ee5647637fa5859c08238d6dc1e346968ba0032deeb6507658e61ab0a73bdf432e8835fb40bac94a251e807d1db8c3ecfaf32df5810fe52573c4f05083cf32a9594245c4aa9601ec8530b86e1f51c9ad2942da6e1a59ab4aa1b87050c378d4f12a05aa2b9e9d2e584399a9717a8847afb54cc53863e9effef3cb85306aea7a3152459e79e7f718729748c26780a95d0e2193de7b87355e2fa52b7ba0e802c5162c485e2cd74b33389661e56469bd642b431ef0a20cd5bf5423b2daf0dd56546b05c4740af8ba76eab7de86d23b07e72f8215e29b7ba7d43e4fc2c5b0ef0648f6ef79cbae6cfaae2ba742109ec8cf02a55df6a95e7f189b3a82ec38207859a260cb5ec0554393b35f09cf22835dd298e696181fb5577cba95f5c3d5c0e71bdf1932fa0cfee54a2051e9ac8e6352f30c3fee338ab71dadacbbe7bdb2b0ddc03c4a395f38d6091c64a9750cdf160a83c0fe817a2ef8a7fcd8f102261530e8d6fe386a45b90b488b8f426c3abbafdc6705d58baa5b2f5a38aa817aa97b708ab06bc960293cf4b1bdfd477cc04318c96b2d1fc7cb051fc73d623ead50b4c00646c610f715c7e02ebe30a7f44b191352e86319843763cc5198b2d230801100cd75901d787517e4720ad235e0c9e76362a11aba736a319f5648d5221f3236b0e77c3bdd9bd2f59c881621efe06c490c7a888926efb64429f08267ce2a94a250177ceacdff3d91a032813604282361c033a7a935f6b874d13ac7715404de6e20c62a58fb768c011c132af5a51e9ea42b31c6e9168d98165b36f2f9d562a43fb00b92c5c59c2da93ff5a8d52f85c880756dee494036a90d9fcafca0bac7ae86e0b034ca799167a8f6be3c69a38fba4ee0359d33b00a7fe487956caab2628a102034ef7d8a352efc74de9a309e41ac86d94c7b8c9492f3751358a4a46ffcf680c025b5961bf1ff5ca9014eb53b461b55c00b5cf8f56b6595a046a35d053fbf16307cce2c3ed3f37e3c63987fa3c71293bea32196e335c656d4ca91d4ee7eb9a0e054f99e707fa998f7347d36b66f8d3a00b5c116aaa0ecc4d0aaf487acc49dbb4026b3af499f0039fc8255bfcf9ce0398c59441d3540107dcfb0a943118e13996041420064c5cf97c9afc431f86fbe924b82ee9ea088d40e88155982ac4885a170ac60de54df754273ec1a3cbc24815958c7772527433cb83bc7cfd8cf799447205bb4208d9259a95963780ee878483346288de61e4e40f1e2f978ed2396a2d900a1001d8d9e67d1f13eb14ff0ba5ff415522597c08b9b640bcd5a9dd3f39fd4f0583c9f827f51fc5bdfe19497b7e98298ee081d4605efa9cd814c377cf18ff6209b59874840b2ae50c35b3659e2581be16cac60a2ae1ca3be1c5dda0c1f213ea03ab311c1d21123d81cb20f22bacd002c4603141e38f16407c24d923c27b330f0836e92dd2624f46e9cf444f39a6f6135e01a5fdf44636ed9ab1f05a6d7e8fe30fab99bc1806952afe6242181ff665c6cc122a8e32533e95115ab5f17c176cf406441722b216b2e04d3d6e6b19622eee52b0e4ba6821cffcd750b808be65a10f0b0a51d6e42cd600d13b051da16e827b8307944db5e024905d38c6693deb532b0882b58be2aca5e4e665147387fdc3ff6a30bdefee8822b18a6270854981851a04aba9a41cd81eefcf92fee1102f47fa1b4558ea77e352b899fe40d0e24fdfa004b547737c21761c17a1324fc1c73cae98f272e3eebbd27055f8b41f3e7e15f904a433e9f78c392fd7e1f559c079a0299cc46e7fc5059a5036ae527715acdd6a0ca0ca0feacf8d2d8f824981449a450c3e55d70c1b3bd416cf70dfa583a8c9570de0098b2a7eaff933aed4ff6553c8665d03189a8b1b2850222eb581666fb03404e080c1e33e673ec91f647325097f52252803dde4a55977c19e7558c778449b01ed9ff52619b15d41893308457777a062ea60850045f6f89feee24e269fde2404ed1b1e0c81332008909c24b894cc2b016cc9f0a56856db58d674766bb243640773f1cf28f40a57cabd85214a1c2eb38d511f63ca443e681e58b0a3370bca530ffab5fd56610077d47514fbdc96ccb77abecc6c024f2fac2ad53f113a636e800532ec5afb2480243afc57d16ea240fe6066ebadefc458d7d11ae65b7a7e695c0193ecae04c7d9131c10a52c294faa5de73689967b17b6fc857e94512bfd627d00f25429950db5430504f52e8ebcd2020e2c4178a6fefb3a787eef21972ae3fd0d02dc38ab4ea09be6e07f6f766d3f18840f062169f49d2b99cbd69332d1d4690304c2192b08ee07888ac65aadebf539fae5111f7b7cab532195957158483bd70ffd0e21a857d347875c9aeaf70b276c82ab4ba558ba69ef62f4b065fad6f2240d8464cec10834e160db053e1670577075290cf7ac7adbfc209682d67d7a68f7019050496006ffbc7549de248fffd654e6212ec84387ec40b3e6ba30d991727c006ca9eb1c4513989d06b9663ffe1aec7cf081facd23c5a1fbfc704b2647d59605466be8dda9c92635d152dbd5a97e29e7e3df074991e06c5fe2cf4625f22b620c84a0e86944e009de44e24bf1250a8d929b0c63c8a42c2ee154d048f22612730d9468d513e14f4b9fbf5a38538c043365690ec449462f4876e8a8b1cdb04e3d09aa0532e396b9458999ef23c8b663f46cdc9bccd9aad7a67e5109d821f42b6706202514338ef1ca90d7a20f988887a8c92858aaeb3cbcafa3f7d286ad63efe60b44d8e3a9563b56374414ed3b7fbd42a26ea29aa2f0792109aaa45e2cc066690ceac179e55faf01c1a1b897cd5954fc5db7533eb24a6b815b607024531d8a710082426b47b2fce6f6639108b214ad45be80bf842c23237f1bc944da6eea712d074f33a2928a6d7531129fa6380e779c7cf94b4a11375b363c926303d37e8c3204f3674b6f4feed731684950e64b9f0ffd43e202c2b85f60f9bdf5abf7cc263c000a8f09ab0931ec3bea7809464ae39b17706952eb703e968c706c881a8d16170fed54a0c65b627a6cbf45d1990f84f62427b9c70074cebe0c172fe385fc900b004cede82bf202161f6a71c02b68754f8fa24c36a0c59cc577212218160689cb0a6c059e15cc3ebb31ee0c4ece8d909c4b72927d0943ac6f155661b759b948e30f2f37a5c835fd146c87aac82ff01e091db5512f1137e0f3ff22fe941eedc3460e6785394597d9b3710b197548cd917a35397557fd4dd82e06eeb1726ce69616038e7c149e18d2ef2d4ccbc7db5deffbfd4d37dcf4051555d581d42019af0d930c327bbfe95eafe23c2ae8eff7313ca71d24004dcfba03326f06ca2514be55eb04fcc969d5180bca12dd08d0ee14b285ac5ee1aea7e6ebcb691a012a6b92c47f0ef0c68a6cde6c6d161641e0ddfc8a478d8eabd99e01d8893108312d90ffa2550be06f18050b9de178627958e4f3422c2d52c631c33f210f84bd0cad0326fe87076f83ac5b90e0e2c02b0949c84d7a2e8deb541819b634e53c5cddd74aacbc9b00b79d8efcf5120f7ff660a021ec3fe94121780066bbcef896268b4084f546140e72e2a7d25733dd82499c469b721ec3a9c4bca5448eb1aba72eec484d76ed93042eb44b418240ecbc26e801d43afda57332397a91ee22219fda2817429f1694036be98dff0def1ffaa2101c9024fbec79a5e00119e7a625d7ec1648eb1adc5a0031795d02f20eb57a8a9c0c687a6189e3830b055ee1e64e7313d40fe41041180380a1e31089bc876bde75f16c2dd37ddbf7ee582dd4deb615ecab6a5f31c12e0050de76cf54c131b1d2bcf821ccc5de9b8ecc7c0ba06885f66e18542075ece600fed812111e4163877bbb56e026966582daba09a2d38828cf599d445e2a512002912d794fce9f6ae2d1e9b371c08ade7aa833779599735ad73251353fede404062aa3d3d9b91f3439f225828e44f6865b5f88786f989eb64115fc630198cdcd08af2f72ced1a933f0a270452974baf64e7d58606d33016c1295ee350d5da0e50c5f1a68440db3d5d9ff972d5e0f76d6f3bf81b22d804189565b4293284e7ab60c52dbd1f50a341be63823d1eba4daec4a5d948af53c133a8a93c5382455d46a0b56846d1a9305513c484d9f13fd61301ddbac8c46c2a9e4df1e69f9702192ae0ebb4725a24795c000416e4611272721042c662910e6700ecb1b5ba3ef386f6600e21f3e2db9f252b952ff1ef264d342323f3f673c9f9222b2c01f665149370b079fcb9ffc80f80b6eaa6d823058b085955f5315630c9723ed7a69c1ccfbd417094f634dbbbc7a818f916da4d8e9c6930255187a97fe6c581527c00b60005b860f89adfab209f52a74229dbb65ad0c7022c24e42b980e84a39679a6ebd37de5d0ab26f1935f4122d442c75f6e52bc05b303509458ec71a7967e76d76c87565460f1eb9679921908699fe2d6d62f275b18849a0ba1901a69b78a09ea4a20807da0412eb2f2f3bb66fc4003ff654dc708a4f56e0c9924f806cb837c834dbc49e5706fb265a34e46d721287a214366fce94d1bcfb89891726cc30d9f8eb2fa0591d0a787f15907ce9d92fe8f9f838e3d1e9a0b4e5fcac01d4f9d27ae519c64f59260081a72e5abe58abda94a4a637bbcdf92491311748b5969c69a35489406974ba0d420d4d5540f3c08a258a2c686128f48615565dba55d16a457a3ecf06203a030ade640037346767d601d18af1c0a2c2049640d5f89b1edc695b59a642d899270911e7a9db7083c3038c18fcd3e27fd01567f6b4904cb77048af64a718a86cef0a3f3f6d83de303522985d22f025f48ab48ac1dde5bda4dadc9ddb9eec7e67590f73835208561ebbc320543793dfc1e4992f87c5a178b19d1df49fdf4e89e05c0b8c26cea66265cb91f3ab367af0a39c79055dc11ce740c3bf99610c1530f561079f1bcd51bfcdde89026acd8063a48d81de1771a31ca63b099b98ff46d48a6908bb7677fcfdd8303528510472527755f4ffd1ea9722c0ff6f742c591b31246a0fd101d13f3e2941693bf60b9d42d2eb1318ebac4993835102b031fb227837730a90042cb3769c163f2a25ac143cdf47e470935932cabcbe712484b8d9b6787e065c45880ef0d22c27da8fcb39ceab2188826ba08805ba15d3708e6b18f06a11034b8beb5e86c20cc59d820b899f4ec263fd18f95eef19c83a79b2ecaa4361950c28b40bf30ba990d3f8e6c68116c9263152fbf3d169885deb850f8a12e429310d8bdbea5559280e794dbfebde519761d50c0a1d1c3e4c2b65b673d00a0286b60804f60182f52d744e577be29fc453ae1f10c32c785c11bd15538c9a6c14e1a2034fd834eee6a9576b302f2237212ed275f89ff0a3c658bb09eb64995073fb4505ab6231855e9ffb63bdf73b62c1608aa486a2a054cc462726aae6130d749cc9025ca867fce3f4f14093809bd6cc0dafe57078330c9d5cbf7870fc47f7bd384d0dacf15293e083aded4f391e5b3056bc3a3a7a45b851647d98c2d56e97c9cc2202e6b8f666e293f588242a5b5ac8f7aae56bc6eddd0785f7f93fe9f6317d0761056160b354e9a1944416edd1291022f747dc9c58fd71dc4a8fefe29e7a0a4ec107ce4b99ff86935f8a1faa5b1c6ef7c47161e8e4ebb6a7570f92a75a00e150f3004c4f11b763a5cffdf75efae1df093619da3fefa0c5bc09094bec805a15381907044a64ca36fba3a25b8374eee7194754ebecc9c5427ae84ce045b88011f37a033e3e6e1a67771dda6156c6499798b29be5fbea5b29603bd99e9418fea1969306549d9aa6bc2004611d16d62b81a5805f5f62cb7a077a55b2eb3647eedfa0970dbbcaa331eade128612aa73580270e3ba9431b3614d5fe3c345d029147024510c758e4d1088e45cfd65818d4104367711d0707c98a069231987527e40a74f97019c811ea199dbf12bfc3eddf45a4ab28a14afd02ec3e3780898a28a5ac2d9ca0868c8d852cbff34e757776b243662cccff108da629ddb6a6eaaf6309ed9d59c0da6f5d2af1ed5803fea9c3963787f9452b6a8a28057d837c33b15918d56b9a1060fec4c481aa9e9b680c5ae267203ec481031167db2e3dfa09d7038f47b1bc0031ae7ed0926a01e09cd811fe11ca715329abc11516eb8307aae73eca5bf45bd07b4b2f74f9ae26f21aaec215185e8890e401cc325c45058723f9243d02125fb0a2c11bde61efc2f0803fac6eb93194b6a5e12684afac8762dec0dcb04c4259203b9f37cd683f2642b58f43bc06a9cdf293e9156b1744ab2a6f5ae9e2d4156cb056a5e01a1140985973dcb2d089e88f4dc68b25488734fb47db405e3e99d8fa00b0d00f580d0754a8b162471673646412bf77588e854b9629b598d4005ac51240d9a0c5d459122617159998e5d90732faf421f751069e96eac64c47f97be2981045f464f68abb0747557be8e29a540281cb06c6fa9fd0d1f5fa1a2b2e70ed6b400f29dd3892f5c49173074d2591c274221b988f4db8b0a3dadde8f1e12d6ab50072afc18ace7e82a0b4fe67b0a9d887c9e30a20a0d68970fbc3bc8cfefc212a00d51704f6e3ecdb7b7e741de761b00df7bc9f4a24959f7df69e26db44f9891460d8859973c8d01146f2f39a1ea1137bafc3a88e6cd7c0d901782e06051712bad0ba87a96f8130992ce452ba7a69321867a05c42fb7d7ae39e5adc749725c7d8b079905343fadb37b44e94644e3a3482793cad011dca35f3e522d6641a19b665f00964cc2a17b232699c9df31d3ef86cf3de33f64576897c4ce8b99d8b2c32e790864bdd0c29ad7930c22f82d03f6adc46052740a117d4a186311dfea3b73b917057f23c7f862e56ef7f3a3e51381271e576b917de3f69e51445af7a2083d6f78019c1e9f909a890bf5f7021252b1e837c8283fc65721433e454319abfb3f181708779f541e087e35c14b52cc312bb40f4e1b0114d08be5d588b3333daf84e1c50f69f3e4190b9410ff81e33acae851f205065675e3febcf13d0c9461958680b60d03f5a14e6cc3b13ef1c12cee7257f5a1fa6072d0bc320f035ec356149625c5014d8efc3df19d319cb8cc27c1406ef02cadfe33da3d01f34455971f8cccd4f7032b1203f4b03cfe5228d46645494be89f1f89ee650dd4a65ed65bf4266ba5a00ba21f45356a915cf29593511a6cdde2a2c31390821365753422a1d9bfed53f8002c540e9b6efa880c5cc11b9a967126cb78af227c3a21beebaa87eaba1c58f00d380510fdf447283688f47b77cc2ffa18014d88c8a01e309a9c6340093f9b1b08dc740b6b96876f7e9646e216c01499bb7e7ed7866fcbdd6c3ddee701e9e49503a53f4fc00293224fec62c2ae807b26b5346ee17bcedae605b07464e7c09366055942eebc5eab2dbec5a06bc0ac8a84a1437c2d91091de352298b0e1782d4dc0dfa55883e9eb297ade591f9b6a027144b7c59bd0be5e95f1f2fa104c2ee2d350f6e9f51c9b022ab0065abf182740e7cb7fef47d520e33d0fcd230f5e76ed4fa0f96ec74ec95d9f8b6aee2f4d3dbfe2add1c01745f200d39cc8b9bb3d65eab30039ccb6ba3c64d162d44dc3e456cdf95194b6a6a55b63cd6bad500097e6d63af03228f18ee7d5928718b37d12c7707d21d5ea4294e4d253ed7a8cc503a5c18a00bcd3774ffe2dcb6e4ed5e8565bf41285ff74c1e7c4c4f873fbf0d30b480f6cf0555e21685993b3284ab937cb4783c706f6ded427dd74edfc41a857571c6f0870a7dd412f7f65f82156233a14f620f2101dd7f93d31b78e7ef43fc71e86dbea60401b39d1ba53fa35406f26b46643c06fb3d037bfdadfbbbe10411840d62525503fd18e6a0628a7565db0b952be27067708d0fde80f755b95afb05b4c0acb41d0b140c31306fb5e087a40cc96964212bd2c3b5c61f32d5b899b5444cb19048ef0770d1287fe34d28c55703f047ffe99c2ff6586d6fa5fa4a36ed82287bfb1e2201b1ba61f1c5517a3a391ff5c23c63224f5d3b3d77a5034faf1979606304bdba00a54636b664c2f5ebb6bc3ae337b34d4dc941bd29550f2f4216dda171c6646003a75cd770c4a37211e790a601c6b5e329ab9b488b207b93606786f21b9c419409ca184c252e2106eeffe582d6df5bb8eeb40b75f4e21b10f959d68a1a88431f091939d5845a1665f216df80f0ce29114611a21409b3d20dff74b9649d88d6e408d93aa79df1d22f93712e56d9f7fc313cae8fa15795b1e25a0867698c5ed84a041fd44385202e81c614284b041d386294687543b544b4df245fbcab8d9c52ad04ea047316bb2faf2b016b42651510ee109d4eb717abc35698941a6b12feda5b06ae907a4a9e893149177b6928c0173dc5ca09fc335897afb556510b58fd12a502be7b5a6cc728e80f1d63812d79e7588e704092443b856dbcafa77df902180903d54faad48c0196baa7070df40e209a8912d19c6818a91d4f2ba68d7c984e7207418fab276ad6a89c01855985e507748a7951f19b68aabc46a10440d5e228c7071c74614ed864fd7a9dae31b0b10c9c14f890bf62b3e96243c83fcec82886480d776a0f62e0e36b9ae708dff9de292a7dd898345da67c9b87ec0fee8aac7be80f062f1eeac29550ef7c08c8bae53f8fee38bf07b60f7a1f8a93402eae9543db0dd78282a30afff1dc6f65e2143092ed7f32ed5ef7ae6a1e4fb0e78d0fc714aa0a518fe7737b8b07243c9fb266f09846d083d2251143c01b55c7952fe7cb059d0a true +check_ring_signature e7b5b38bac3cb69e11ab1d87ce0881c9ed80f5bfd8361abd19ebe7454b1003a2 f9a65966a5ebf58c47efb4989800640965e36a8b6e9cbffc0662f5df8fba989a 28 b35d9026fe7f9fc4a5b80a785ac3f2215e77da34368b8a5200577dbb7b39382e 0788c6b2dffc1c5cfcd33d9151184495ec9642e059d21eae883269b866c7dbf2 6444971e06dd7eefd976095f09aa682231bbcf21270b615cf5da8e70eb559638 287c7a3e3a455c1b80ae5a339366402e0687a009e6e212d752465ce6805b4897 9eebac2dd16031defc10ca9b086e6344c28d8852417af74622477358fbb0262e 47971938102fc0250679927e1e79f784e6f0f7f932f753db44e24827ff4fe924 74dedf5b1dc82517d19ca56873c0cd1ebb81b4e8d8dd0f0a2251c4c6fba9db44 ee1fed4328096af13c8a99a9fd2e1f461688a93daad62f2635f9b1898fc2b49b 9b40033f9681aea0181776e36fbd501571b5f6ea61026d738d2f8b984de59493 179b7ae385aa10d06355bb19913fbe02d57340001088db99a5d003bcc71c41bf c2a4e554f8a568fa770628255affc274d1b87edb4d6f8519fa98177849cd3eb6 aadeb652255e9f44860dd84ed315afdb5dd10c221f8cf4e601858e248d95cced 17c900e814f277ff87d3f17e054d1b899b9101ebdf79f8bb86973f28094ade50 5e59b2159de868ebcaf278560403181ed92994b7445f5330c66c183cfe17b9a6 43f044fe06309808d4a71ded6dfb4c5037f0b7bb6c0cd8d2b00df3a4925c629b 5e36b9c69284cab0d2e1d94c047fd5dbfa6659c07b5a0d67158bc8fa927746b0 f43cb6ed17642ad271ff6171a5de0e99af431e525bb9fa1976e248dfa9a30c5a f78b84efd445e5ad979377cb0f428e3fd8a73a2fb814facec89e5a9ce0353843 019811fe80bec5e3ebb184d9de08658bcd25c5efc8efec5b0893fd5a349eeea4 c3cb3cd8dce303e4510803d61619bf250375a1fd7be7615d7339272138d05cf7 d78e6d820e27270298eb875e8b61d80926bfdd79b5b71f287902866792841921 42aa0cd30ee154b9506771b586bcfb3756cb86358aa2e83ec6ea3b3e3e5f06f9 547d938964759bbdeb45b9ee6966c94dff872e6e30fe3810a04f5ad1ccd5112d a6199ff136066896cc4ebefcd67f5a26d8f3296d86f462555dc9b71df3699521 32237818c80932794237d022d3c53d79d4311ca96eddd7ae90c7db00c46d031a 2e3e6cbe117c8fac65d5c3d09afec62a106d509e5037c3014f94903d27144699 c41cdc54db494638517b58ab6247bbacffe24543e06c97ab28c5cc1ab949a97d 8db616696e371da659e697884c47615dd4ffa48cf2623d468ef4b06ba523af41 dd284e9198d5ce20ae4b9786dab8fc21d48cea507ebd449be1c240c0476bb00fc619efa2fb6c576341c170a53b0499a49bb12630b0335ca6bd106eb20437ba0f88dc212d4740af7d2e2f980a7a77aa733fc54c5e2eb2c4a7dd23e15eb4d1220d153fe533c29546a5364a14f88c7ac471c4f7ba37b9a868e3c65140001d35bf003fd46d259dfd6e43db3f9cd8b5b84a5d27ef27e3927c5373b3600fb7bbbecd05a39fd28f56c588ecd91d5d2ba982bb7b36356d19139ab1f179a05a85e2e90e081c1d44638b701a627b599d88633a59dcc24fbd99cbfe125bcd6941f1350c220e9dfbb437fddbe393bbc07a1e21220a1d7b5d17dd307f68a2b2e76fb56ed55d008250f60c7e86bc8926013f96af42308b442564cf2fc8a763fbcbc0e09cb5df0ab329aee3da6b33eb846b9b5cb9895a07abe1a7ac5ff81258d0f1e0920f56a60f2f4f69e7d8d5b5744d2c78160c11694a593e5b57df7bd6c3912d0a85d0814eb9357ea1dd009e929d5f4ffb1a4690ff5ddf4003ae42578d7c5c1aaa79a83d6e0ad7507b529d9b0a24e3b0a8200d8605a887e22078f511003e77198a235e8a330e11ed37361bd03262583f3e1070c9bd2d47ad6515d60573a19d367a43b2b2e50750e17d82ed0e9b67ab464283da71929df9a3e31ea04331799039137530ccf00c1907081d846cccb9f6213d84dda7bf9d4daa7c6409fe54128e76edc2c7224801f1ed1e4f24bc8c11288569669fa8f717e810aacc2b441153ea104f78aae04108344e0f1f558326ca29d2ff8f1686b254e096d43c1522b98235e7644fe2128001a00efdc98a043af42a39711b62b7927ce85c6d3c37a18edd654544406f395d0f47b6dbddc54c646c63de0272979cd56764a26f44cf94be5e2a90994b56f3b4056b6c6c0bbd57a5ff5f834547741458a62774c5ff8f53393c0d7b7f78e6a7880d94526b034eaa39fae4f7c00ff608cd68c21ef080029916c0b29d42cbf0e12c08c788a93bd359cb21e6c8fd8caacf9d178123827b5305e076ed7506ea6e139b0e86fc9fb35d122ee939513be609e062cf54e77f44af2f6939b1ff1b6cde6d93d350da2fdbf0d145f22c23356ae9102cbc228e2691a41938349e1e55195c0fc40ba1569fb02277738c6d70b644472ac218f18148891cba910052eae5629ada2a0b577e15c8fbf4034ba13c96afe836a378bed9e60be48c88d007b6df6ec05b1b0c7aab98357ee5bb5a513fe9118d0b4871dc4e0994474906ab0a6976cd86917b005d5842d6f66e0e518fd9b443a5661207d90c6517399dfa49ffeb61c72db5820b6f66e3951fde7f7dfb57ad4dae531c1d055061d5fdf873095b04912e7bb7fe0ad5765489e1c2a687ae06d84fca8ac5e6f96b15c1d65eadc690e979363f7812e9a9b5c14c01a988c0dc24687e04b463c5ad55f8468ad5a447f9fd1bdc7d1ff90b3affca2f2de27a1d47e493747872bba417c7d19eeb2d4100d399061e6c8b2d0c36d3c1e5541a94f380a18042c86a9efc8ceabaa83e8fd0ca3596c2033cef0506fcc5835d6b6d2637233acb124f4fbefcdc3512ddd115451756e394b6410a640a94966fe79017d01bb07eeb64c9d7b6e70b59bbc105fb55b97a4f61f906420b04078635dedccb2d135921e9e2eca79f9853585434f8e9f93acd89781d177e4c0a8fdc0a7e4432999755156a490fcd1d0ba57d109cf344905c9d194e218d336e095e95f003d34346dbceadd41bde5d811ee5caa9ae41a9de09340ff92ff882c8e4c26d55762ee5fc0b689d581d13d63cafbeb5f97e6e780a8c104653ad46c16b0aec5f7bfd30ea7383379fa95973b25d8cdd892caf9ab772f0ba6acafdd2c1dd03ded67205508b79ee3ae95fa3000455a494f64f7285d6d37064ffcfdc295a860ae92899d28abf7573e4eca426987f596d7ebd37002bac01cc27c211532fcfdd05aab1c7d3036cdd40bfcb28a6367d4e04fc426466e508dd932092ae2aabd4e904efca79c6f94f2974d7c4c12c1a21ead744abe0e1a06ad09d35a536d0ae6d7d082276bce0ae5cf55d8ef8d68567660a550d991761a4dc25b293e21f184685f403298d04bc8d6ac69a26a6473cf75a0f79daa033f6b807b9df1ca598607246e70ab101ab86c3abec415e436591309e8c63e94811776adf1f2204c33689e1d3ab0302e785e2a33d5b5940d82cb9c30d4767fedb154eb9219789a8392fc09bac4f01d98036b01d25e2c2ebf142f6ce22bb12d5c9295e30917f88eca5c03c4ddcd2055959d11dac2fca22f705db4f73643cb0c709f886229e9ac9141d933f2e05530a4e1978639309f6748c5ad52fbbfac0a00a9118ab9fe4d98fae2a5831de186f077977eaafc997453285585163b7d83d399e014ada524335bd4212ae7cb795c3049507df372e3c07b94b6916224e743164ee61d88ec0c2ef16cdaaffd7d8d65f0bbe9bda9ad15cf7da951a83af77afef112c3a9c3c5a461f12b422504b13202a0f040ffe93c7ad4c4c1607848feebb08e3d11a7807e939ac8fb0096190072ff403 false +check_ring_signature c994b9aa566f6387fd60b934c3c1ea70975dd37c2437c3fba46710e14f26a2ae bf3a1ab51e71a720eeff257890272e7bf14583eee609701c3dad7f06f3bdadfa 15 54e53e66d543deb01ddd0093983204ec727ae942ac63f6c8ab426d022196050a e465d95432b3c5e66d3847b148a2eee224e1d5a7e0350b9b9ba20792f6cc496f 7f0c39173a63008bc30215c687d8fe3bcb84e824665b14762f82760de9e410fc b09d2a9fae647f7c2ed2925a33399dc181b41f9e51308265dd2c7ecc653a1c99 bedb987c8c1fa6f74112596a16e77e3aa539541d8ef43766fbf8978014302db3 068fce885cc280332ae93d69498c6eb5b1f43cd59cee2a264196aa104db2c7a6 661cd566b40f2bb4463f59d9d9e710a4efd9d9b0dcfb6f4b8d37f775792efaf0 7c675d214919475e5c9d96499dd6b705a98a077f6e1b54afd707f344ae228905 eec04bae535a25c7f25d52a3cb82c95f7f1e305535c9b0436b3ffe691285e084 bd1e7119ea4366bc7d888b512b85c3b2fc2197b8ecdc988844815a0a62b64514 53231721a895f297e64a05dde5ea307499fd1824f5a00372adace07909da87a9 8821871128afdb03d3eb649afd789295dbc77b5dac502194addfb85f7d9386fa a7f34c8bd9473297a957f0af9c89716b324f7ef2fd74700934979988aa3ab5df 4c32b3c2b5d730f6dad6e2933e0a33dc0e6bbdc6a0bc477d2ff2d1c6f820793f 857a0600fb2912548ea83091ef5a80ea0ff40f8b058d5a450c6b06fd41177ee0 efaadbfe4e3769dd77ae2d531ea80ff2bc79356b104d102338958162941722074a27e93247e1a29b8b9c117a95a7ba8d388fd6cdec0292badbfee62c2121e80c121a75e88942303f3e7ef424610435b043247d9e13c2f285bc8ed4054934430a487ba11e1ce9e2eb5870eac5b9246396c45f5db48511aee9260bd01e72c30c0ac80bce70c1ee31fc6fc2c7713c430664d4de34dc1b72a83f83de3de17fa2350d16272f4b7ab3732127bdb02509dc04aad645266822cf861596d058e9d14f7c0197d6ba28c19e121cec4aa52f7a96e356b13a3a12d7f00d736d6d9a26016b9303105ee14962cf9475b8bcf45f3bd8a90ee3756a3deb59403e3a145c7cad093a0415645416e6939c2342ae7d64d7628a1037c30a84efb0637687bbdc872953f90c4adc776b890831f387cf6e56c26c62315bef1a75c2100cf7c3261848f47ed10c67d59f85f3c7203f0ee76fe9645f9aa44f1d0e556093be193db5dfb7a27bb20b6b462a7477e0251fb480e534b24fb549f2affd14112df998614b79e7a75b5e0eb16b7c55c43b583f8923dd50bd3eb35fc50b6cd6fb07befce69192980b747a037864ea24186a96ddab0e6effc2c27b1b3334d9a29e197f741202386fbd827b0125528a0813824ca6c6640f457b8fb0fd59b905d2c37e5f88746a404b298db3085ca4bdf7f175bbc987f7edd9337be928241e8af1b993dbae4564682f1fb7020945f0c7db9c2d410d28d8c936a851d2dcb61dbd60ee162ea548210a625655ef0e4637f71f9020506b39d5e7b69411a53cb3a896009b64cc947fbc8e1daacbcd04d2cac0bd66323f301f144accbd4654e0fbfe5fc6fad364fd4df6e96a117c3e0c57240bfba70429f761fdabdbffd7c9417b71f79c9bd41375f359b80b182070083325ebf132ce59a8a5c746d5789ae1ffa06881c36640b3ee59afa5e0beb5500e022355add44c48a91c3994438fb4ccac8b5c2da7de66149739c0269682a23601b2e50d0b4396490dbb2f419aebc0c7cb8d5acdcf192bd340f9927463e9c87501ab6fee97d8c1e604cd3491385eb7e0f1bf275c9c75449b422718a2aaf124bc0b23e34d55fc701e8f9e0749af9afa5b9591df4836addfa1e6b0735ccb12f7f10c470c919426d7a23c40c55cd9751870998d537250016c57573976936baae9070009d2ce8a9b0262fa5e310e9c30d9a1eb6459fa04acfbc5832567492db776f701b28c00dff5be38e03819d8a7567b18299b57ea17f69c92c9405c309938da91042f33a79a0a782c41e69129b03e659dcf07d8c742b6da4f4049a10cef75db94046478a691e45b122bd1b9485282613d520eca5a3ec8ad7c8cef825fe4e0cbe40c false +check_ring_signature cefa6b14fb36a8db386a69c33a8fa36b13bf67aef4562d4e2514f577cb1c9aea 9a5fd7db8e4cff49519a73f86308d4e92cea0944480596e16f57b8e865ed44ae 7 7d7712235e1518f5971497a915ee2b0299abf553dbd0b56dee22f9f4c38d53f9 979b847a5765ada3962c9d6406d46df3ef85f291bc170d57df8042e4d91836f4 1202cba658fa17195493b2b6fae9e0fffd1a6ff94252d27998307676fd5582b5 14595554b2688f406f9a891d2d2dbebbb5fb44906f8f246977279547e97562be 7d5258f8a5450e2d12e36b273131a97b7f73f540e66bc00b593f640b6f05dc8e 38582246f70e2a740bc29e9222fb430a8cb840ce35c78b855416eccbf54705c1 9d8a683980ff515f7fb86fb812a47390071cf540b30440087014ffbc6d50095b 9f333daaf95ed1441ea29fbb0c35c628b42d07132d4e9eeea07a711f43a11001c8a62b3c10627d3a5de58c3942974e3167ea51aecacd5a1504542159a5ef169b3071ab0fe67a00625040e92dd09bf2fadc7e1a1df9324b6761139c8436ea93073c7d0e3c4505f6592bc8b4a45eecf004ac351692b78136c070906cdee4586b012fe1d84f7adc2fde16f9f97201edb8cba5a0092e9a3d3a19728dc848c3fff8053d8eba5acd2a27561a0627e77577ca116fa9ec64e242aaddbce2d57ca7b3e40d46f61023848d7f5e538e7f4dc893ec25e65b58f59a45f25b42ac4f4d3c6b2b06f8f679ca7a5167f91fcd4bd1410b5128c08c4a8f1c438c72f6aa5232473460062c9443f2e69cb35797c648eb84ab67d9eef22a00d7d4c1f7b963c6c87128abf4a35939dc087d407d092a8832f4d81806bd3d645066530ea9cd0491df0defbd083e12545a4a13f8d04faa01abde103e3639674d311a1ed6db8e9291ae415b790c23374a5dbe9241b41b28fb226e33dcebccf5d8029314104848d8ae458f5453a3f2a765eeffa0e2105cff77d2197175131486d460865f5d9815344017d09c620df87d75ee3c5679109b954fe6757f32823eaa9cf496b9403aa52b28c3d3943005 false +check_ring_signature 29f02a51a17b764b87d8322195c6e51d758520d5d8efe6b53b758b03df771cd9 7b8242ec50a133ac29c8a62b3497fadba702d69fdfc50d105cf8cde4b4c92342 1 b526c5af223439d379200c91d8cb2ed53d61adb7fff8f2fe762cd7fb9130ee58 e755ef4b11fd0c6842b8fb984e11dc73bc4d14213cb564a127b2e3d62d346708e906c19f568a1b4e39e27322f206c453f45f44a462c0ac84a6e7f7e2d14cc501 false +check_ring_signature 93864c100ed2f895ea425f2576e86c7348f35be541666da08c71e2674bb5216e d9fcadfe9eda9e2604458f82ebc4127b5c055aa398debfb01612f03a952e84b5 18 10f3c017950cfce40e4c5377e65f79340d611c03f82efbb7e94a0b3dbc58e6dc 4521e795ae884119597129007eed2a9df799b9e34e7513e43839ccfa5360ba63 c1833b308df3adfd223c6af7913dc554a873ca924bad825b1838425432883584 62549355921666d79ee990573f2c761fd2e86ce229987a94ea7f29004cbdf211 ff3caa5f00fcd1faf8685dcebb37392d10102f2ffd6c00fe865be4232adb2928 46bdfd66253217df11d7cad88627abaf7a2d2b45bf19e9331cfa7305b81b21d7 c56f07c712fe375c0ddab236c80ccc8203b446141df9887292f747cd981646c0 4a1f1d75b704ccd0e17ee14f7afa9230ee4de02daeb2d6b34b266638ade21a5f 7c490dcae6accaed984ad3a47a3989ac31c9fd39db3e036c7f83dc270affd4f3 9a31829ff016168bbd012c3d60bb49066cd9130d83760126d9db9b7c44fc309c bc1db53385ad5c1876fac0c95675ec53a7869137ac2ca87c093285bd0c438d59 cdef0af34e232d011564e0b2fddf3a763f5d85514a5047155126fc949ef9c222 69ab3cd5ce1946566ae7b4cf8ec8b64a43be3883b18df1c2128c89913da40a59 e34ea71c4f3783cba3e3b39b3a93306b5a3c306dcb994829591251830f463693 ab26ba57fc9a937c6b417ee25c213fdd58d17287ea2a06384247b15499d9ff5b 8efec230606da10fe4c77ef88e5b2159e6ce435279ddf7f23813c2f32e1c1cdd f269df4d47e42fee204209bac1cfe2996d44eca4e27f57f8ea03b4fbe0ef15f3 d06d31a04dbfab8459264c507935a0e5885f81e2b252a29557afc09c4de50477 fa454de0ec1682f36a1fe76e517631eed136565201846c78e076b2b66d6e840abf281e3ca5eba84b971d409261e7ecfe21fcedcf6ab81b9f5d15387c849f840423eb6b60705ff3171d17c5e7c0e38383bbb87e22feb69bc0cc4cbdf7cb9e0a0fc553230dfe2b73f3b84ed5b9d8a6df7b30adbdb1bf2b918e452f2f30753ada02c071ec1c855274e5d296f1e6cbd8fffcecd31379cc68bc7b327290e6e8e17106d77b569401c3856c78f0b5654ebcf6b4728e12ddde4956a3d61c43fa5b90220d736cf59884a1ca635d8eb490f8b80ac314c039990fed9c3517f2b0ab621e5f042a0786f25441dffa9b7568ec936c36cb8b6fcd64f82d18837f5f35216610df09063a8700d9e41c9d2c1b5d98fc404004226cf86ad57f429a720c1362396ae00e69dca462ba1e2e3a2562b18a50c70feebc16dc69281eb3f00a60fadd47e70006aae6a45685b9dfafa15f3d6ea7b397724db52224f35eadc376d5dcecd21c5e03e4379d502e84a0ec9c958e6580c768378f26d34820b97472fe4ee3d1dfc49b0a502b22a784139173c141ea9f27f031d98633b9e1c5dd2c7fcc6c916960a2d40be4ec00d56ce2a7bca7eea0cfd95c462641141ff5c08d28f3fb6f744ac7cd0d0520eeb3b536ab8725b404c1ea3c0557eed579dc71a5996e8339243c1e7744580c30802d147b1fbd1aae74a1f4f461f9d6aa814d23cb0efe97116e7610d2916105dc543577db95ead8b0d8afeac5eb6bc2922d7641d44cf69828a041c765142600bad68019b877828639c975c2d475b253d801ffd1554653c0774256252fe20a0dd7d7b5fc542d2e5d1d6acde78396e53728a61d26cc596d0adf7985aea31cd601eba76ec96dffc36a5f8198180abf74e1ed85eb73a7afff253a02701036401a022ec411ba7c0a81a919cfec2f0a23ad0a26686a9c1c66dd869f8828c04a5850088fc00c12c2505fbbbb0168b3b19cf776d314bf0213698c58372fa86c0415e8057a896b794a4caa826301ae8f070595d6d3ea9127a8ae5db40d0528b440363800b142b8b3a24deb464ce30addf46b1779144a831260837bdd45911e21536bb1078bcd27f474844e41130ba90dbb43971f6c308324f1aa7c74bf4c6125fa746b0e6eba10ba9dc621a0de20e8d958ae598f182dc2b4232b405c1405bc6f9a93eb0c79576778b7efa3169f4513207aa54a9112d3b30fc77b341555f9541dfbe948004e7e0aa0dd8df1cf50d916285923f57a30009605e771f5a1444eebf3834fed030f7839c767067372fe87b1fa77276d4c164c05be156e3d9cca6a35e6920bbd0013d7616dce8dbeb54d85b3cda96b97cc0829ff9429aeff61e4bee5213d3e510b0a6ac0e7dcb90b02fa8b0e7090b3cc9556e6de44020f965d4dbc6cea67579b0778322219afef17d73eec6b502a01cd329c79443402371f817e6ee071079f5f0124c97c854ff8ae2875d7467cea42f26fa7700b3de115d2c35b2279aa70320e0764f66a1f44d4f6ff975abecac06817f1ef4dcf81fd4d9a98fd530af82705910a2aab11ff5a80f98ea538024a820dd8e7ab5c871bd51e2563bd847793dc8bf70e0e2a358124090ff471f242c48fc2bb79faae16c5b23121c8c7dd9db33de21c0c false +check_ring_signature e8b7c7de6b84926a19b17aa9c0e6955d354e9bcd732b0f03c1c4e4f45a97b994 43cc4e4cfea12ced5d65f321765f4cbe6d4d0c1fa3f804e9289cfdca10ec633c 4 5475c24c36dfb800cf4727cc4851c4371e604f9618724ee1598f6f83cb2c389a b3209f2ef61a0ee31b9d9373ce4ae4c35c005602d32c7a0dea0d9c71f70005d1 6fb2239abe8a8e718282019fc5e15ce3ecca78ed2846cb14fadc165ddddc3d6e 6a985bd23bd3458406dac92f0432c4d53ddc5513f59fca5607216887138c7b69 f83d8a24ad4182d608942c3fbf0e5032fd0bbb1bf424137514e69a667b1e380c78f0f4e6384636943b679740b57086c0490c84beb2aa2b03d0b3d65ae197d80ede4828c03d6c5282ea50c9a8340497d15668331f02e309064b76da4e628d6508a6d84522a6329d14ae3a2fe08b396f0e25e9e7bac7f59f9aa9dc8988e1dfc70b3411ea5fe5a18f618f65abeede5fdd96f5fcf2cb8a06713d500b53e643e0e0050f45eb3771341e1aa6195860169a12ec94fe68c9dc199dc31807e32c77ccd809dd4870f80899a03d6201a6c0168a59007458ec17309b65b6bfca33336137bc0dcddb6ac760862e348a1ba0600e9d0dae586100ebc2276fcb25a75731d1fa190d true +check_ring_signature 2b094548812b72e77b30b011ab40b5e443e3737fe55880eb8d3e12b683e859ac 5ad112e88658efadbde89586d0b253fd43be38f6444772d2a7e93b2dbd82f0a5 42 496a89199f7757b68142ed4b41dfbf368d08944ea4e9972776040ba7214a5fb2 5b2807f639e4369998e2af2149d1c1ab246ff43a4a8978d5c0eda9c2be63aaf2 67bcf832b67cd06b8f9eb09ad75cfc089b05d568cd38edc05c9b06d96711b2d6 a929c2f39e45f900e11001f049ee5e5e0667bcc17d1d1e1207751a222fb6181f 4bdb76c60be324f262486332ca24f06ed6253481c1fab5db05de584cbe103f14 0c09796710d332fac267eb400ec17622a60042285c42fb6943b84c2bc9730f75 a0f16e2ba76117a325d789a5fd2d34051c19caf91754e7538a7b798e7cccb38c 83919cc82339324f642281f6c9966651c4093b851636b09b94163b17649e6b88 2d34e21a2cbab8fee4562de20e29df9f03883ccdef78379f8bfc612b6f2c76ec 13994d60b2f0e9a995dda11d09a20275bd3ca00a55199e481c42b7498c4068f0 d61a057554f54c625dfc5b87564a078c0f2c5bb2e60bba5a67d3a5aef4c47028 0a811a1612cbfff019fd17a30ba7424ad91df9aece3bbb7df69d3bead3e44b50 a69dfb32ddde4069f9dba16b680deb572b4e81b65502aef43faf7a186eb473be 68619a38b50f152c08cd70e9af0b4e2ace94506536e27ac753ff867bc86c2b8b 703370c0323a23305cf3fe9c3c6384da586ade4db365e2c20eb62c6de676fe53 829c01da493492c8898a11a8df4b0939580b271149005e0b9d46bac9451c5f14 2e9be6709c75b04dc89b44f7bb9ad01ae615bf521811c2a7364de3806d0c41fe 357b9381369286c3d7c873ae346c7c9e2880089041e81f437ee2925b4e81c257 53f2e6c785157a90be54bf0750a2d3b10fdba0dcc3a19d8dc922f57f78ee5bb4 eb26790cbf1a07197f158bc49787eed9c1356090933ba8315bb5f3871b652a39 ac8146489444aef2d53f9a383f5de2b13dbfe6a4f7c8699562e008cd9b68ab6d e91c3df4cc022736876f5ab7871a254022c3a1983bb40bbe7caac125bece929c 0c24a637d763c6d9009a02bdaf986355585577a0fa843a5ab08bdad4728661a3 3010244cbca969587eb70c116a35e47103ca0616b5fdfa611e4194fae13c4700 7a0c0d4ecd4f7b5a908d9bbe0c83a7b917c44e6f2b52c12541fa076f6a7b4627 13cbb77c39d049c30221bd8e309dd68a22d52bd60ff9247d1643694da5ab6ad2 e7c5954ef9c6fc6be1d925db340270edd03a935b161074c3d0f9e5fe6ed946fa e23ab859b0c3bfdd61ccf6aaf1fd68271f6cfb845cc0d3a9c0afdc3362271532 997056e00abbbd31949d8154f1f99ad5c720813d318902031b3066e82529f260 f08961c5e350015c87281070a7947d7a8455c8c6b454fe68d21047ec6e358ba1 c5eeeba0cd5f6d497cab5e857c24bb8cd9179ddbcd17b6abcce0c8b30dbdb064 a94348fc52de56d38e77f2a13994a8d779697b7cb45a7b925ccb92ad4bff8b69 61fad01c4d29bf06e1b8ea84b720864c54fa358b14aafd070d5522714e62635f 35249d013125c03c4d152d93fae4e8ae387185c9c3cd22ac96f57dfc64b7b269 7dc56e0bb278f0d91e4fd6fd0dbcaf0f28fc0238d36a0e60d0c341ee76eb980a 360c94e08e1fc647bbad56f07d3d3bb30a1aced59cc19635c3dd203be24f0a0a 5ead7a08227a20f7dd97bb03a2f4f5f82db2ff2dcb104431768848a2b319cc05 1293acdf737e94cb7dd696918388b3e282dd4202bed8d229f1ca7e84adce1d79 5c3ed08c9c11599181634ff986bce25f667bc15f5bbf3b2444aab8f699b02070 7b19a292a9f47f46c17997a1629832df9a6bbbcf2dd2061cb38d71e40a2553e4 b5a2749b1c5bea3674654ad9019bb7c3003e801f2ebf25d6af340b841f05ffef 9fa1d1b5ef98b4023d30e40a1dc28e277cd90678c3b6f9652a09857f108196a8 d4b569a6a4f6e650f2bb433542ea549352c9c95c92de1579e350cda42696d00f9b6d7e1c6c20d7ba727d8b52abeea5a4975167bd25ca9cef94266964a89043029a07eb51e589fd6a2de33441b905a16c3d3e44d0d844b72a8583f3e1f7fc0d0e846c969dca9de31a8abe3fab47c75afcb0408229541fb60c63be0b59daccda0d36971436f4e75c338db8042280ea8bf9619c46afd35df23771303449c3a8600fa8a720fa10c588b48b3a3b7543aeff556a167c13fda4bdbd028bab42d64a0705fb24228c4a8e3efc383b81919222fd2f23421b8626f83cfd1579bbb6f2258a022f472d9049a0f53f5d13e02c32da4c0021cb8bd37194abad5b4587b998dc9a0133b8b206a01096f536588b3ab50b75f797c6fc7f219099301c1424fa768301086af112420dd03c06db601fbb13513b0886fe4427d762e36807da94be7714e40bbb5931fd52c33143fb88288c214cda09586c52174f27dc023790713634802d0672b6fdc2c22f36fae85a358e3121262e56ae5976013c14f601edcdfefa2df108320ed18440a76f806f92f190fe0669954f5d21bd55c10cf66a08cea8b14b3c0b2f90ba56e5ac2e4bd52660404c473a280d9a942a47adc37e3d51baabc5a5290aab3484eaed6d1e615529fd2fbe15f196e6290b31bc71d25b81dcabe02187c30e15a1acbd5ab0e627c0a6deff11d69209a90f4e215d6ad95376d73a6766b24f0a8c24c7cc5ea163078dd3a742067a60c691a430322af90a5a70756ebb6ee9f500c6d9ec49aad723a8c44445bfee6e1eda64a374e7a9a82690314ea705f1188b0d5e9d5e8384fe2bd030567ffc189f619a49ff449dc6a1b7c531a3e2c2b5e60f0e176e391c9dcecdfa8e049f8dfc1dc591113fce94c0d2afca8c1c6ca24aa31708142cb00a5bc7074eff7020042870706f53fea9eadc392537eace2b29031e2d0965dc7a640112ebc275636339cafe2e56d3797a109c9c448f7b670b448449a70f3edf7dcc3e8143d80e72416763b12baaf5cb15d1a6ec0afdab9d0a3ce23e64051de64326744881e932f66d260caf7a112efb3f2e01e270789beead23aa71b20e0a4e7190e89547feb7fff94b9c8391405c9458b08fb516c9a97e8ec360aa3e0cdecd64774777f7a09c606ff095426f19cede8e39b477590ac296a0d4c6a41f0bbaab6dd05aa7cd9eb7d682c99871e1e9270e09b539002b4fc921e73f65d22b05d94697512a5ebc3bca82e6c01afbca3987f3da0ff87e694ea270bbd5c41cf809596007cfa20c114d4c8208f00aa3e0d85ddc057031666e0653515d98bef23a0bbf457fce4ca43c51faa1cb5279fcd6eeed9fc5c9e9c409bde4670c35f21a2e0a6198ce682e4487287e8979c91d8399ec581dc644ea5f24c25d52a6b462339a0b3287d918dc196bebc70e5142dc4b23f939ab31e05a88e68da914711460b9d30e7d3136636d7ddc7bd3656166a55b9c198c35540715a9a622aaea2565c0c8a405a313bd0fdd3351400eab74eff5839848f90a648e01f967356e89e7757e249e0fa10c47e0919ebcf16a9deba48ab6b275b1e8ef7f1ab93c02cebb7488ea3db807e7fb3a78c10298f42209a64ffc6ef3250dd30acb4e7ef2fcc3551f678515470a6df6b29829a8494db7dbc93d7b43486bd361e3280c6b8b88d26642abdc80d700944b4a3b5ecf5d117b29a574e4e026d86efa2e240f1f7ddcf2d314d0013d9401a16fd8b37dcea468ccc4cea5f7b937653cf95ec652f8295f57e32805077cba035c3d1cbe3dd8e01a3de6698fa72a4b8984e648434e64f46424892fb3f445ab0c557a5e0df2c2fd8e3e3e54d6afa86e191e686cd7ae8b832a6fbe66b68ee9a90c7d1c5a78e6953fffc5d4fd7e702e9288a9a5cc77c08c96e39a69533bf88e300716f64e7aa6d365dfa092b249b5117ad0fd9994570ffbb23c1f59f55d47ab190563d0487e0af94f68579473a698bb17ac676ab74364a08535973528b09626a20ec8fa9a9c31929172abfe308cf6cea7c49d48af3d2e2775066ca20412bd819f08f45fba43854391c59cc2a14bf11187707ba3b0f83842ba631e51faf8fb6f8c08711b84e06bffce37bd3a450807852b9e363c5dc32719ff326b9996e203ef760edf6bb5ca777b5830587bd8b7ee79fca261c632bcc3fed764eec292f2490d4f0549ec74a580dd5e8b6e64d5586ed59f1451acaa96fef00b794b95db2a593a5c0d4738479c3aec8e75d4ff1059aa3a9f9acbdfabc8e1bf95d15675724dbb2324012f99aac66ef1f4219451c143a676f0f6ec23b1e52d72555d3dc711ac65546f09c7298742566031708a9207facaaa9d72924d6d1e3b82df97c9b31266fb58db0f876f8f48155ad84dc268ebc1875f73bd3b53940da217520013712b6eb0b8a700fe7bf64470edbd976705f4c2c322c215ee1699b05586af89ac740fdfc60eab024c6b50be5bb09b07492f51a99b3ceda06fc54ffc48e3b0ca73fe6581b7b7ab029b79825e2ec60e8b568cc24bc04922cf454c94866108e8c783e0ca7e176ea0070c679bcfa20a354d99de2ee84de042ef22e165dd40602751c769f6534e521d09ee7bd256d9b41ec62118ebc2d9664fd3d230ace06df9538335cd98ac0e5ff20d4ffe77a3c423b6a094fc837fc36d791f0dccad363d2bde06586f8d1bfe5b530456850801b10bdb3b40f67a40443eb9af44f573424b1d9da97ca5c28a61839d07ea75507f98bdc982c24161989692f2af82c329eb2432a86f383f53b2173d0402db1d26997a1f8de3b4c4f3416bb0dd70ee792676e5d4e606d667dd9011f96609de15bd7266f4b1c51e3657bd39c46ec847aa5fa2dd4ffbffcd9a649b930091005da490c98da6cdc21b538cdfbd54a892c6137002746aad6fb8b139b624ce8f025b2fd415e39b375e50c2fc29e3585216910427f4ddb827135c61955ee59702045d364af5cdeb657ff6ec5bdf8fd4d2ad3c857b1a12dd5aa44995549f8842c20d38e757193055f1b1f51d6e4e0582cfe38066e10653c4931839a7f89f6857270cf864893dc690c81690decd0d20edc1647ee3ce82aa57dce0dd10471b50be1100a2b30081a9afc4d7f1ef9c0ae97dcb85ad698959e778ed99f2892f3dbe7b330aa819a23e3c088d1c775ca392f9e1f3b6bd056220070c6ab969857f6b08e24501cd3ad2ba49f13ebf7d8edae1315f3ae7c687746fbab6e334df33cf75cff6850353d9475f7945db4de07e6c33661f110149be21403edd1135754c5b744b32bb0b94f3d4787abaaeb699f9e62c55ae13cfb1e8b31a7083d5ff70f7fdcc5648190653f38a04d93bd8b42497478d11cf9272d745d955ca60b4f44d081db111e99c06f310a27f36a591800aab5457deaff0bf465d81d4455513bda0409bbc0430090136ba4169046aedd153d597ef6d22f8813e7ed6e3ea46ab94bab8eaac597e100138db72401e9adf917f03ac22f42bfd578718e446c20db6aa836cdbfa420b330b75a92f8f09227c76ffeb0aa069bbf7aaffa11ebd40e958df654b0d51b061cb023787234eff110ba51f22696cecad1047d6b8e928c4982944e8ba94f2fd33b0042ca9ac3e41b2c5300c1f74b5e988432803ee731eb80c9f328cb9ad56ea6f940046f292d905c378d52df83f602e3b1033f17fe86c9b2c534055699dbde7acfe00e3ec5a9b894020353b1a9f90d0424b77499ab7ae79dd2856da380452ed1f650a0d258a0dde275591ff359059a376675023df23f43698a7d0ced2feeb89fbc409251195a21720489d6b9874a9641c62c471b94f71e952463d77e901619f20f505 true +check_ring_signature d96e12bca2d93ff1c84892e2f348594473384751e39bbcce80c85c0bcb2b53a3 18d8897ed893bc599ae27941d28fdf8154e8b7ee49a8830b95b84a23dd8bd3ce 4 256c644e8115535b79c79c134f7be05d6bfcfb782544d9dbe30b41987816e3a1 0de8032f557c66d09e4d2beea444bc4f5c39051c3fdf8cd28e63230b51c582fd d75e54aae09d051bbfdd8a3e13d18396f5aaff01a402a977358b920cdd38031b 776b77bd219027ebd176077155fc668635af7154c77f62b8823987142a121cbe 2cc1ef52ffc7979fb73ba6547970a4556d803df0b6745df09c76cd81829e670acd3d9f4549abff5b6696f5b5b4f1411d54506001f06bb691c672ebd23874b50733b7a6958113fff8c590ed59dab3e5711efb15c71ece9ce1f6d1859d006ab50f5ee2339d920796adb9261bbac23dae5e3fb0a8c2c3d402ae80962305cad43cb7eb48eb2917a9868046ddfb711ddbf2749008e0b0f1dbeef61541e06bf23cb00ff4566e792d393499cde55a1480097fe21b20fd858e30365a5ab72d4730f64401ca431587c4412d3a1571950d7fcc7a3a8092c0234079f4352e8b7669dc11de0fb35b1bcf1cb3fd0dce4c15b30ca60a57af6561759baf36705546f2d24498f408 false +check_ring_signature bbd5cf8face4fdb487d64b204878688095c68caa1cdd5c90a92524e8a507f615 ee16c4d8afcb73a17c1d8ab3607962e1a52413f96e3c3b39ca9a3e0c81e8ac6c 75 8af77d6c18a61a194e291ee429888b9b5862ae08d44571af77bef00b346cb1a4 1545ffc11cc1b867a3412e6435a15bdb8959907346fe1ea821465f4ddd595052 174fc7b313648ce3141c4ef984b463101040aaf11b9b36430443a3f756ebbbc1 e6ca77ce94dd618cd1b5db00fa61e9d7994ba1bce2374353e65751c1fb05e67a 523084d9410816d97991f7fe627a71468cb9ff7bfaebe19171ad2aa4f9c71bbb a2c461cd6d963c6f1605d20cb0dc9cd229415c710c693870c3ef56a6df3b6847 1203990cc3eb3896a06ef535d8bfab50fbf453393deb45df7e2aaf962d5357e3 b1a5b5c02d3102872da0a32f7b1e0a07247e267e93ac30f21d8429ea4b7087a8 8a4de41f76af644d3ed970fb136b18c2b00f89d95803a662804fe80ac2dae48d 364d60317b825dc3184b3a81d03d1f60c32963534bee69cb4271d8fe273c98d0 a32210094b166ddb0a90c601412a1a58f26c82d079ca6bd2cfb28b3bfac75354 a36df5c1c3615a1e45c759af4c0383d0838da082c152eaabeb66f5991b14d959 34e85d761d33324c670e94eea02a7d4de6069a0fddbea91b0fc8921c0c4df69b 8fd52904550d872d77741a44044f106408c618e7d55690a8af66a40ad77b367d 6edb8f4e035f7789e866324ca097d03bc916efaf6b8e76864f440e71ec43c22f 4277c21fdceedafe17fb8bb3b1462e96593885a1c4b7f0b020005d8985e528f9 d49c3bd397824c64acb80aabd4178cec38136ee07cb2b88364fd521c04c79b33 82df157c163f022c0b2c770779ed4d329d13d87aae4affa1207f387c7c4863f8 d29c5c12b84d351fedee030c37476ffda581ab3a3d28c15b7a098d126276799e 3b7de941eacf54428618cac077f0b2ff6e4c65856b7819e1c9d0fa080c38e5f1 600a23e953922c328a85148cf269fd83e04ca8e71086529ff07f3ec7e8b1bb30 38fd0be51f37cf8c4feb6c36beb819f1dee3f9acc9aef919c64a677f7ca8020c 86f8fdf2a0182f00bf3fb74d6301efdea01ec18d3133211635a6d6797a00eaf1 753179d1d69cb863bc64ef659eaf532345df9f4f224b408c75859484326aa841 04b5ac343b1a407a3860e39ead961e461ee299e27ee557110fc578099f381acc 1526fdfc887c97eebbc89c017e055cc5ea9d1b6db202e4958632f125d61362b4 ebb453e3b08d11f2c51b9812b1c8878f711982a9256c9a2ea97a0eba443dd356 8705f7c1204508bb00213cc8b204cb4a55f11121d376f6c53da415a0d1fbc3cd ccf1321eb52742857aca3e0c1af97c092c6f3858dd5ef0be4045256f92c7c504 c1eca67e954273c50139cf6eeb5fe72fa4091faaf901db9163a7a87a5df9bc0e 74352a333dd4ff8d839b18bbed154e786911e6cbc3b4d0c07c4dbe9ebf924566 5e7edd2b435beb0beee9235d11017859f09cbd63fff2072b80602f90cc41b848 9581bace59c9cc0868740e880b1fb1366fb0fcbf69f435df97fe9fb61ebc8333 ecab7c895d0ded787cc39f908d7694e8f7ca78137219a263affccdebc8b0c53f e639f75d5bad6ee144e72309cc1d253b897bba65cbcbad8256aa9cf81baa87f2 5de7ad566ca13d3047b7c59145897f385431fba07c6803ca5bd1ca8adb65a81b 304157cbef9d6dd9da0c6d223ee234e4e34cb9e15e0d92378937857a02c9721a 3f57f82b8ef26d5acc992ed74178ccaee42136392f9b09351a138c8215d0ef84 77b12af09754c043b8178d25cafc0caf5190b9a5e93baad7a5d1915f81db380a b3a1f085714fc784a41c863bb9b8f6f0285137413d8b098b865db037351ac07f 65bce6e71720a5090f86d5f9932bb77fd617c5e0def57a0eea3599ea02ec5c34 757b39d74b80437197771c93b079009652b222fc7a958971792d8c2e5e13b67e c1fcb8187f8909b0b6d28417518c7b2f510dceaf79a6d65605cd2bf0dc9abc4d 801a620b4d8152d82deef0b785bd049618c0142762808dbcd1b208cd4efea766 0295037cc3dbe93cb7a98c7b9a55c80b779b835ff8ce85b910548ae71d34cfd5 ab2ae407811a4f1456fd2f487007835120aba6c1a14286ab20a7e46b35c57988 68d104c4bc1aac657b0c8a634d662a003fcbf84fbac8ed145b57b92f0fbcafe9 8e8144e864ff77857930b2bfb19349ba0d951fd54a3b91863aced58eac1689ca 611692116eddef760328cf8c5e649c51446f25c185c07fbc3430889023c79497 ee187242f674ad89268feba57cfab3561bfe253c622be34bf810a41fbcbffa6f 61d3a4081bbc1d602e6ed06de6b9b921c97d8daa48b8eaed3714d24bdbc82afb b14683f9d93bfcfcab3222f5a6602f1c0f7555b291229c5d5d81e17ba4a46c75 240b4b955c876ba157bc38e673587da307bec3afae6acb517163f3fdb7af1417 05c689fbcf9348156a49253d20c1666638db7da4aaea3a8f335115b08b8024d7 d368731fedede32a949a0a96b57ecb494d49b6d231221a482a80cadcd147083b 14d7557a6bf52a8a726f0d161bd089cc910a8d57c173bf9b68892182e43ac605 3aeb52c08810c2bbbcb15266221179178eeba869f61a5bc4f2e5982c4ba192fc 6b11f809ca2e3806a5248d4fb746a62814c2c17f237c7e38450cd987c3f9aec5 d9ec3267d4dca761c1d9ebe481ade429dee7503529ea481f9499d5e23ddf5fa6 ead799b2d0ab95cf33df58570cc6c9125d9d5699ce8bcc4fc0d3a0da499baf60 b52743426c238fd7ee27aa9f27db95683396b6dc61a484e390aa57babfb16c76 a501b2959ab916506c720040c78c1cd93419fb315550141c28b37fb368092e81 d973b460a3c2e1bd9d5de79bd2461b8d942125f87feb4381eef216d41554ae36 206990bd3f7709d078f4fd91914070017ff28e5adef3d18ddd8d62ce18f6c0d1 6bdc5c1217adee106487d2449a9aa554ffa5d01c161501cf5f5d7401e675b3f2 e71e2b935066e9fcf69abb58683954ac61a320bd0caf35d24d7ca78c0f256eb4 11fba35562639484272cb3988297afc8d23ddcbc8273586a52ebbb5286a8020e 8ec8d77a0f103f7c5a34ef0d61be8ed4af8c1f818db58a3821e6a765edeb1f92 f6c43976ec51c3d55b636595f81ff2530073448a0224a7699ce2d531c484d53e c53dfafacdeb042afb9e8190dfa113685d715179a3bdf2244fbeaf877d265a09 acdab58cb8946f3ac6961f67e0f7e159682a6f427ba50778b353a2b4bb6eb231 d20bec7ccf49e76b0b5068981e98965d165534681416083700d70ee513e818a7 23b629500491cceca895273a6d1026372df0d00e664ef2f7cb2f99e018a7246f 856999f76e12efd2181c70c9557f5e62f178ff87636d0444c37d056252ffab8e 557f6e7d608bab5a2db7517525cd618c2c46289887069a9684ec6655481cd53f 9d21624e100a086e965dc8c121b198630f443e447462def44bde82e6c5475104bfc8a928c8c07a39701b0b636d1e0c2b36a1975ff0065ecf45fcde088abff00ca87854d44b7c201df5daddfa12902a629a1719ea4bb3f96feaa18d560806bd04c263ec6cb3932601b019ac36c726788b984c2b2e26e51e26f720f9a04ad1ae0f68efc90b1f6dc5f67e8b7b4c2d4054ae898f0eaed2c9005573792ec94d25e901c3135c95d6fba761969ef7d75853ddb9fc3c10c05dbc74a1e21b18838f6da80efb4cff8a3a37b6bec2cb317413c686b0acb6a23d4ab4decca57ac79f22bd1f00c13a46e657969e402d52a42290c307c64d2a8308c40687e4505d8e8b95b9d903451afad8cb602748fd0206dab2a37c954b095223f5e1e1a0cda2692749440404d813476d70522f04e24f27811c197819eadf70e5d3ae5a67adaee3561b6fd90eba84f3184c22ee249b05630dddb52e3d764da8d00a596f2b9c7ebedbb6beb90ccf163ea86d021410aea7a9ae5cf77e9b3a53354f04f5d789c3109d0d8f1eab06e8926d9c235beb1521dc5b942d0ab7fde9ee8d07b553ef2e557f5fbe9dd3dd0c1402e758d250be99a9471f5e7458937017cc0dd80a14c2bf84e2e69780e75006805fe27e0cd1dc0249143ef38144a6a066896ee4e62c93c23e20acd752dad80330c20f3f852212a148b080ea9bb234342890fdfbe41e76e9d8a5dfa95ef93a0646ebf0497e36bb3bffb66d38581f998035865e9f54519f18a4d744019510950f59c7c9994689f43cf43fe2fc66bb05613775d92c0005c31f0e45dd450b5a75089c67ba55e761c3567fc1bffc8278a0247de511670a7bd55f362ed6961029de07f4a0873a1b6a0a8ae02df4efee8b1976ae3d36ee5ddd391a54dfb47107a9dc09a42fb946b22b79d42fe4e2c408e056dce6281643b67cd4fab44c10cd6822060e55715993f568c618670f52d4dabe5719cd7a3037cfb63afe354ce950c6d1a405c2ae802d5a01f97186abd04ae5573af4a2661b2029bce95893a58228da7e7705e680651ab81e7dd1e1cd42bf47a60bf8c23beb8c8c40bf6cbe025421ba23710d32af24ca8f3ef27fc0d28a27e96796da832d3e62ee542b11e629683264156108556ebd941601ed2800d0a3122f9046a9f7ac684b3930241cd69450fbee38380eee8964ae127df894c1c7798af1a462e69bdd739b9e7c47f3a6668a584409be0cf27bc62089153d7b48c57075023aa74e57f79498e0ee8b2d899cefae0f3cc608b6b3c22a3845c6c2abad7e835be5b3c392e15e398eedc6e47fadb56291cfbb0cf4a4a7e1f24275f313d2e7de1a4086fbd5c330e8a3c20ae92c7a211779cacb01633f162d973d6c7f425aed922cef5eb1a821280d814d9f5ce53eb8d929d4ca0932d888ec7b4b655f5a15af1902a631bf40989fe73473b4fed5cbbb899fce16057848d8cca84e4c28d9cd019c292ca451fade4c0093aa73c9a0d65cdef09f8d0f6e45fbd74bef2ec1c8ae7973df761e4fd6cbf21bada1f9d290948f147f0a540c81fb7e6f2b28f0363664e44686070af57597e5816f55fcddc364f55cff8b95035929a533a91e8cb4ed91f5c58fc626eaf310eb1224cbf536149f309662ce6a075e9f2510144e5c59a482b6ec1e45183226ef2c4f8fef3f1a00e52a9950b1bb09fdb62c2c57ae95907546371b48bff1aa60c5f11ee47e1f637385ec97658ab40f5c4995515c3a25d09c1ab39b200466698e4a491bd7f4314dc6df3cc0c662b6008524968a55d1727b1aafd9844b57d34bfee1f948cd59149fae6d2478b3138a0c3a2b9f8a0f46b455c4b74e19e500cbcd026e32ee6bd37b990a7e5bafb62749007e908107466f9f8653b6d19acfe38d48b91b205c6fda8711eb3586e2d572cb049b1b65fc540223fa3d45f7777b1668aa6d22475f1279dab9b52daeb8895a920776023590078a50775f2ffebdbb7e9de77e0917489561b36ddeb88780e8e034006e9b6bad562a309b02d14ac8ed6f10e013c26cdf3416a94534a55f6ac94d3500a07e6573cf1467f48993c82f3126c8cdced2c16199382a1e89939f825263d90e73be9d41bafb4662b5220726b3c86691cff64f87ac9867feaf5d6979e85add0034e9bd0a7a58d4eb8401ee8becaf5ae91c750986144ebe5a32a66c94df356d0ee703343da55dd4a1b1a1e79a057ebd1795df407d9f3ddda1a2cce9120a9f060cdab114d8069d46d10b8c00a05a24e9509782808a909b83fb75edc744865910094b74def8b044a8a6be5f1eb0ca851dd7c62101a187c0819134a9b2536eeaf604e6c71bdcc0d0c1415e843efeba3ad518392abd16f6b74d35651ecb5bfe9bd40cbccf58ad52176638468658786477038b4fe79c30355b7a8212141165f847bc06fc4988ccd34385b79a2d188de422e21d208792d1f11b786c26b9da467f52760b2629dfefab9e61400f90942e95901f7385af183af5611a95e155b4a24c3d020b1f8b482a2d1eb5577b7ed94acfd9e88ad8f4234b0b96592b825f6afae175b5073859b2dbd627195139a1f5e6379fdf10dc6368152e714146f83e1ea4a37f9f0a16e7fcf48f1e130d327f1577bc655a797c289b1daea845730c502f6f6fbc9a078cc374531980dc514a82016183a413ddfa5c86745a11342a66053fe02d662300633870c68461fd91395ab9118169250418389dd68c531e76499031440403fd09bfd0c77cab3d0b9be707e0d9989255c4d848c0bc7904792c4251c3f6e2e4d20d72d4e4ddd6de2cf82869a9daaca38077c2cb4b21daf98f78042c7762606871061f082094545f5eb17e3b5106119710fdbdd14167a1e66f2bd967099e1f9d5800ecdaf92da6fa0e7e269b395d80da47ae38449dace5210e7e948442c5280018025889b6a27ba31536f7a290b3501a22f687576798a891138cca95973beefdd606472177a848a406bbc562bddb314d66e4a9cc40271fa9710b4468ae8e4f8c7f090956a001c3f8adeb657ed4f05dd80de93ed99a8396ae3f286132f8a570626c04f412f320ab76ce66812634e80098b10eeff4f0807f1437ac0e7b04d9599c0904f4cef24d23903d25468390051f14bd4138e3f7556e13cdd182f31559d46da402adc58ab160b8241d336197bf987d8cf299afac3101b7a7edcdf069fa4616dd0056a01401e2280d57fe76b8eada767869c453d3e8c6bb06508cb71d660b273806269f37211639afa0c8c1c0f60c4c8ea39634ea676b85bcf0f7b3084b2c0fbe08e74942979ca2cab82970a48dc85c015cfc003e689e0367e4d0f2ce5316fe180debc93e279ca608973489ffb90bd2671e7b47ee18697e3b1e99dd64dc6ee4000058ed0bf05c59e9e1096c931af10c2a4fb37d4c78d4e3c3abf247b16d16774103d9663605a11b160d4ce53454a6343e504812b679eafbaef7d0c39042c73245005546e73b85b71657362a1680ad8405e935a5acd257809ae1861a30e38d064f02d80827e281b3ca1d79d1971f12604f42b19a4b4b34c7e026d0aa2c1788ed51094805bccbbd1b63aa06553f74ac370adf809da75ef8ea6a6b9a053f71fcacb805151a1211c4f0a573099839a3f1617fdf54602029806d7ded4e52fe260f8d8403732451a37b01f9f6137cbc50396a50432f19c4da880173dd9e7a7a0c6475280c1c6f4574ffd592ba413bf418ad8a3ed1fef34aa1f949e433d4e8c6f3d139d20901da011c9a8ce1de1e7d135dde72227289a6d3dd237a756d6ece9a296599da07ce3cacce5d6081b3ddd5a788829772520f163a19b1612e14cada8b2e63e4ea0c711f330fc75454ca380d57cf31c7d318a8474c09bc9f202d2ad974ee089bdf0cecb2176dc8336bb7ff7bab5dc60aba19ce56c09b9dcea054616f0f548126520b143effcf79b591e8c02a2647b4df9b4e32e8383d5a39005a72f6b5077ab5970fedee63eb1d7d62bda30d0147232daaa04057fda5c83c47c771300e06378fa909a571230ddbf68289b7fc21115c74cd9bff2b137504a56f572ef32d7cee496d00c5b123aa5f943269d27b3f68c450e81a67bc60c10261c2c73b9477033b25c70f0a0d1dfeb1dbd720bd6571899f659383fa3edef4e072f6718e759fa5eb4f120518fe804c3ca39cc7ab878ee14fb8f4b564ff3a360a1b05f5dbf2d93b30e7d90f0525ba438e5e82176d431fbfd9fdca3984717d3b9b2dd4f8a6f8fe875578750fe2ebbca4dc4aeaa6e31f7af323ea37b0093bcd388b60ba4f421841c3f8613407d60c62852ff42417db96f7dfcca37064a5095818680f597f4bd1a1b018de430efde984340d02dda916cf0ae4a47adcfbb0a61ccdc21e4ce92742a6524d2ccb03d9c1d806b247e471f7bbd472e72e2025456f89e5155b373cfd6f1c8477fd1e08810d74bcb2f288e42440d449b6556a63ab52005aa4ba8982283f627fa706a000b92bed335204412a762634a0baeef337b7371fcfd3626bb99a5ebb07e838bd0a3af2461936412804d1295ec3e69a97e022417703448563ab2d0739a8c798e5077d38a487dbc4690f1b6ea8b21f8c75b2e79081e475dfd390336c543771d73902a710667b806dbee7d17c111118c48f41a7ea152ce951cf6518ef0423629fe80947d83d0a7c65689bf4b96706fa05efc33ec14cb4a804ea579c47db0795fa00097c24cbbe71f223dbb21a4f7be4de6fff731164b3a8ab7cd124cfada6a058eb0db6559c62e51815224a5145e18a6f9535477bf33dbb1ab6813724b494c5e4700ee6567b435154e08ec8533ef890fdf9f01d451b43145ece4eeb4df1633b18ef0c82ff3d74f305209377a20ee7530d75da8fc22d2b63f5a851da88abe08b35100655428a5b6d884b373f27c407cb7ffe8ab09eedf2a891c86208ed1525ac337b06a7d0dfffa40b2343a5b1567859783b215101f81bf53d5c45b546bb776207d4066e46c7ac104dc37e4cd4cdae31d9e252cba608871ab8797831896f8dbf515a0058585ee72fa6886fca4b29b0f91f32db1ed40c5b90a2928a90ff0d7bdd69750df37b69182ffd7fdc1805931625269a239f0f605ad33d8c2ca89bd04cb5372e080c3a0ed4b4e179baed0109a866fec224154608752f239e7bb743886086e4080e05e4ef75e57f14fa30b674f25af60913616cd1fbe61cdf56c785b8270af3080220ea6d2d682add952aa865a635384dd1798e82a6c52a4d00d8926274eb23ce0b8307afec346ec6c1240fbbdf7e0cff5a1f66790f11669d370db21b04e8ca0907ff8a06c33472fb55f9b8fcb2e0322bff5fec835f4632f046e22dd77ab5012a0b5116ea5c44c2cd344561bc2b49ebcafa23d42ed70b266621049e36e11c620e026cea87c95c27b7fb94d3033b63479d04837190de9697cdb6a020e14489bd70089f60ee2dae1816c21d28f2d296d6ccb5b6d03c9fa0d2605bcd8c4d89eee8430823612e4aa8db999469577b6c9dc5deb505639e7a1ab78d6a16f26ab0c268970e718f64efb8e317cc72af35b52e46e8543b5151f5f85580e819a7b1870daf5506b3b17df6286ea031a1eb9efa78cd44d58e5c61ec6793882694bf2a9397348e02d0004a2fb871497c20f7e01389003f5dfd5ac9fdcec99e4ae26a630a7e5b5207be8569dc6f7667d5445910ec4caa17463da78437232437a3ec6f6e0ab6c1bd09f03f5f5203c5d03cea423f5fc973f5f3df817c77d3c666ca1af2db1719026204cd5c269f281b6b30833ed5b4d3b787f11a3c2ecb71a3fe765cbb3694b679cc02bcbe733435d89cb27a6d80108b618c244d118bfa1d544ef63ce4d1d5faf261011e99b00850e27a402ad3597ff5b998edf6e99c79f6f5a4c8234ef1756402410fc010c403f0fb5dd130dd84330bf6f475c141935a5345bda6f18967a77bad4d0e0d82f72324d5893de742c84b51e75348fded1c98239f5072f63caa73c8cab200fa2955a37667f355ec2851f299a75c3694232e231c57fd97648bfda36105870f13881ebaec1a4dee60e00c9deb156d971afd307b3c946e6b0df5df0b49dea508871c94d71dd426c8f53d56377173af960b025d071c2d4e88f8917ae5dbf83f091ecacb0b6463deffebe5259640cf070f10a629039f885a5c9155f10017eab50f5b631e6fe39655a2d98b6f5d273b8b3a4293400741f9e5911be530bc25cb030cccf3b7de4c86747e657967570c3f8992d22f954b9e6c32277feb9130546abf0247e83708bb8da66d0a6ced37176319c3a4ed75fe47b333586fd0f09e21916a09b2a21c59c2faeb6a8f34661e2f211be68fcd10bcb7c635064190f894de88a4019d364a21571389520cffd1e8bb6ed43c64f8e4af388cd853fe8b53f17f9b0307acc49454711788037642490ec1094d7988834658695fca95469168dc0c784409f15e5cbe410b27884e771ced3fe6395d9b4d53a4d45164c6f4240cf9400adb0b9bc52371c5fa8ed865935a85ce22dd2f675b2f612a49412d06bb47ef25616f0d734ac02a493f2cc51fb92771f7744b6a34f1ad337190e2aae5483ebb2fcda801b0ee3e895ecd213ea22148a8d7a36cb830a3d9567884cd89bee7feb4b6d1b90ec70cc3301a1da03a6c034e268f735ee85bc99d92791f04f76cbd79de3ef7830e83422edd3ee6e2f44da654877f149ac9f59b2653ada6aa3ffb7da04f4a7e2601995582d8f3384b3aadedfba6d62ed21f9e87b629ff6fd37bd4358feef4dcc40f7af8f54b15d65aefcc72528746bd8cfecf50faa75ef85080388854de275dab0a95cc7dd18fc552dc16b950296b74fc1c0384f73242276fd49a814d223f3f010d true +check_ring_signature a8174330d8f9b95147681e0768168031ea8f219fe3143fa79c71c46ca23a8b44 c6da10afff81c0e93cb897f9e5534603f071c7815105f246787532c14f80f6b9 206 429a9a64622fcb058135c91c4c9c45a88a8a91d93128036d8ed7712b45e07548 5a5259163e0d34746c1261ed423c3d6b208429064557137b5400b18d092c6e02 4e3c5ef367705bbd890d02af879b2d850da9ce7af41fbd87c39b8980d628ba6f 65b924604c9d10e7f16c2cd4b7a776d41dea287551f983d4f37b672014766cee 212dc4137e4bce75849da65e706ef683fd1ba6482753f143abea323f78090b33 88103ff2695a7364acb7f2194cccfb9127ed1df9526f883ef885648375b081b4 016096a781a13786f28246e18ad8f4dea74669c2bf2ac8843d3e9bc472cc3d07 75fba3b03588ef24bd4c987eda009550d8e0053487468a2b5b3bfdcf25a2c0a9 54c49c7528a004a031aee70e23777b9f3291b4cfadbb65d86da9363559c9bb4e 679d43f715b209ba3959b5af7c6875f6928bdfed1719f4f20f398211902e599d 081650b4d9786da90a6a448c94de9467b677f8cedda0775e45d344129ca5a47d 0f04f4b501b1ef9174e7be5c2ae457f751243d04abe621a9e8c49fb9b04c3db4 b56c44d932a743f7d5dd058f541c9b49154c9d7b2d920b0fbe5c9e2a774b1f64 20e203d90ebd3a20e272d08d8775d2f53cc34512f7f9dd9373030f592c77a35d 024e301407a8b17d1467b2be561ce5cc448df9faf88db15371fa73a024d10843 0c3edc7b9c9ac1259cdcbef767606974867e0d54ee5dfed535a2d690f9eeb0a1 304543174d8141f94d2e96706f2680a387781f9cb3d80c586e3b7961f5480fac de327e616c742d15386b5cfc6ed78fddca68f700b947b2f52dd5fdea53e4df2b 43eed4a0d0b581e7f0b151f64a285601c3c43818aeea7d9f7eff11857edd09b7 26e0211cbea594a4bb2233adf91d137af03a9f6430adf1f563d1fca9d8fd01a6 ddf55d361feec7a44195fcc688687ed55320800dbbcb7da850e6c56678b8dec6 69d96a4c4b833fd9527be98da1a969ad595e3a64d54ca0fd8e8d1c0d914bcdf9 6b84430e44fbb3976ec51d61f231dc23032936a893518d671b5465b3d072c9a6 3ee5bdeb85861bfdeb142d2b1a4a351870744a9aec00a6f13cb03b6ac180c0b1 d27ce4827205e003cee65353a5c8455c86678890749013cac05422ff72579b24 84abaaecfd5ca24910b02fa5163b359b3fed204475fb6d5cec0cac9ebd72fb40 fe8500f270516d3ec141749a019e6c308e78620d9320a8120e60c900446b82e7 38fe8a8f6f1a56e9d792ad29aa58f746be67b3414472a390ed2baa4e8edc4bd6 48022b4ecaffde6b8435764872e7007eae0f7c876b022e34ea4b0d9132cacefc 9e4845e700569702192479cc3e6b9ea990233fec63c18bcd736f75f2c7e60125 52197b86dd17d9f9d1e31e5d0d348cb6cecd2e2988942fff189873e358d3d7f8 eb820d8de7d93802ba93329d8f0b830ad6abf82ad668cdea9e50b2cda4de8427 01dd56eb2e574fcd0ce165f804366c8317a0cae130fdb5b924813d448de6ff4e 4cd5c18669fd12335a620e746f926ec895ef8f9ce75eeb1633f4dfb2df4bfef4 16eabdbb8fbc939dc1edb1abbc0596053004c06e222e6d29c48bb5dce75ff612 27369d6dcf19f5a914b60f4217e62d44177960ba50ae3c6a36a98f30df069804 2098874c491b33b4ca023062e6c2dc0f96ded9fe4c8f60e8e78024208d937860 0680668a57566a8ba1584332ad97bf4c968119eadc12796bfaffdc3099275d1a 0b67767001871272c5aef7662525004ae7424955ca398b2d40902fc985f8bb75 8bf7f8a6900212e0c35351a911ca38b4df4a82e2cc7d515f5fe643db532d31a3 8f9eaa20dd38080a95a0a2c7493cd72ef6bad750a15263a18a24a90ccd08abb5 4ab0efb72e94de79382d801664be5c5118119c5221828f9ce3725ef1ca073dc0 acc519cbeec32425c831d07fa93d48e6cec1e40b13dd3c318b48e7aec269e1a0 55ea149c1efc436e2840da9da8bab77582ee6fdd36609456a1af400af8c3592c 6d9a716b1399dda73183f3db38bb702067b43eafe383ff1c87c39fb5d13d618c fd7a49fe27cf7daaac3745298c1c5884abeb3e22444c3bfd26ea49f6968de9a0 41b946e1daaa862bb76f207368d82239885c5d11bf102b0f9220d0fb43e5f765 e72648fecdae0145195877047a15eab781f84a095e0139cb5350fe4c90c9a912 fe2cffc4e108afbb95507e6dcef877469dd937c4269d37cd261482316e53e498 225d0c180b6bf052e50c54c3873ea8d34918bc961c2eb75945fb087d2fbcb5c3 3a89dbe7f2343da740afd816dd9138f4fc268933f5a66ed5b2551edab6b953bb bccded9ab136454ff11079fac78fd808bdf3978792a6e5a1695a82eda9a35cb6 13a3dd90681f2ab74bea423185919dc5307d93a51f805ec075afbbda69f9f6e5 a87788d59b4190d88e0b97644819bf796fae2353f8182a8962f7f607da5e9780 51c5ba9e3b40aeb5b5d8dc4d1f2946000321103261363f34e540054581486728 f2ba867d9768f9e8506cc4a84a9cbf51cf9f3bb91063e59ced03213bb2754769 c3697e8720de1dd4385fafb07eee8d00ae1999b2d811a2762fc0aa3013e806c2 03cad74e96842799c05e174fade623b57138f00487cff9e3b6ec5039bcf058f8 de80a3e47095110c2e06aedff29575b4c88446bb3a7609ca118d4be8d7f88c16 097391fc0c3512f0c0d91a0a8c8e24f648e4339f1aaba5f4809c91023ae61a7f 13a5a3d73b247d9716d509f3c4169e007eb8fc0246a2fcba23b951276845949d 336fb4ad82a62189f6eedd64a71966a3d6cb04d914ba6abf24c58897fdb7733a 6284000a9a8b3960edd6aeb0ba5fa31c1795ca1846c77bd9c15d70682860a412 dc1d22f48c34085af4df911711fa91728fecbb8f537d232347e8cad07d80afbb 970568a89967f948ba9aead9a5ddbc0a2bc74ce6853db67874b610fa5e5b4575 2fcc3b9c0b1d4d924aada9c792fd13fc903e95670202158ba23e76679b64a3c8 415f50bfc6a6aa93acd6334d6eec824d5f0cc287097cb46886df36f920f9d651 377a6b692b894d48c96e455b528e24de5ca4db9851d2ab56dc28a28dd1e09c70 ccda3dbe571631a8a19c3729aa6898933319776be6b912812050f26cdb332ad9 46eed6a46641fbd7c88c8217645ffd74d471b3c635d994691d68f58654f8fb8e e582862469a8770c3959a331e6db58a3c7a0ae1df8d438256c881124a35355f2 dd8408f9696636982bb62ecb1a7164f31c2de95ba97b2c54ebe71f26376af6bd 076b87a9c473b681f4222e499a0a20049b5fc2baff05302e83579b10e0117fe3 6070d9513a05f398183955bb227be20178adbd874a0e3c39fa7600ab8432924d 54f194ab8ca5012572e18cfdc8b066a766eb8006d434c610015d28b9177a709b ab86f289c7635b951f76a57167b4a18a6f7a8a5a9ace5a2155c49d6b02a4f372 ab4860f7e214ac0fae965d1f2382dac1504c9105ec2d0aacb2dbac53ac788002 cd21e0e810717003ab9842061d2ccaf22a5cb23e10e22a1714597cafba15dbfe 8e247b2ee3a9ab737ba15b26e90902588a8ff061bd4111170ffe193a6c221fad 9b87e1bd1af5fa152003360dba68c87e1f998d8f1728e1f9a284f0a080cb6a17 a071d20886101882b61ef61b72665309739270b7be8670ff97131eab676038ad c99c33fd2922abedfc932f3fe158d36e93bb23ff73e77f44cff7fbf6b6466af7 2456b2868d1d54da5041ffb2bb44c6cd804870393de742aa0bc83b1bf9c0a217 299971f54150eff5f156dbdc4cff9714d4a0b9b0048291d3dc1837334637a73c dd46ac76274344ac9f4d709d21f17cfa252d6d701964c45430884dd10590dbcd 883271f15cca8d22c681e68e8dc9ebe4a8f914c429d5910389cbb775d8e366d6 cbaf016deba4b04178c2e7e7ad306e5e746e322ff1652ed6599bb05afb490444 1654627636ef723017d3511b6e111363fd9e79748c67c4c24f4cbedffc64c3de e4e4657f828094b0f3aaf889837edf994df19a10d2a666c1822147d5f30f5e37 2149bcabaeaa24d87035b61126471856bbd70f17c5a24c6016f4413aa2a3ad38 94674eb4e2c3dce801d9f4645d4228e591833831561eb09d6280110655a11246 447116afe713acd64340d7541324e6a411a0d72338ebe8b14edcc18e777d8d34 f351ce36c86582f445215bc0ee4e95cf380fbde6f35f3f824f56fa14b9c83954 b1c1c424313e40451986bd106a499b17896c4241cddb6591c5e2b95a09f641ec c3efda16bcc437358794be42f0a6f15887e9115134c43047fc741a28ee29df8b d75e1453bc959809b4ce591e5ba9d3b772c08899d41bf5d1defbcb5f9b62e846 0f225596356d3c67284f7e299cb1f7b550b4dc0b9271fc514f7fc4b3aab743f2 0b42f9057c6381467090d1a6d0fab6ad07341a30dd661ec59f6d36f834726225 4ec7d6669ef4d025ff51eb4588d0bb9c8ac8740313c62da3e0b3922f382d7a6d c448f309674777e6085922c6e6861cae46a5f9c79d528d94664eb96fbd93aee5 84766ed54d7bfc335053034970992ec3071cda2a29bdfae0d40814cd551d4ad9 515c58d3d908bb13727d20a4be0ef0531746612bb389d830f9ef6d623735a817 3d9f93bd4216760ae788b6ecff293591867dd7f4bf84cd5921b460a134f21757 430ad432b84e795d2429aa40be6cadf3ddac9c75717221515e9bb9c8e537486a fb94d40abf69ff674e9c38c631b10bdb75ee5eb06fdc2c6d55b7182ddf5435bc e4044d3a23af9c7e55bb756dfb8428e66d78c94a971a3d1f09c6d443a19016da 04a1f25a96e4ed7376393cd7309f861d6311477446d1340e9dac9f344efece71 423c14d5f68a6d29c86e394144f78cb49da818a1ce0aa14850cafafadf311c44 6c3886bede30c71ab1ecce7ff30d77549902cf478df4379d89451585b2407250 d6fe655c8196418ccaefe7795a7d1e5f41f2a865c4af61f6aa00cde5db4a2af2 fbeebfd8814c686a8aae0f66849125e290e9b2648bb4aa546285e42440482587 33a65dafb1947b3b276d884d6cd69e816a9c51bff580e8d76bd9261c6778dd6f 55cfbf82463ce0045f4ead3c5e161b2c9fdd58070ba684feb36e9ca3176be014 a29e7a7143c8a4e24c8f033605c66ca65e56498f25ac299041ce70a067ff0d15 2128cff34da04bf00bd2c49fa0f84e27f50abedc896c5855c832d9a3422ad29a 4143fe0d88b72d978dea7cded140da98aa2782ee143a594764bba542272b3b02 bea2c6e65c2e4914f4a471fca4bd8c713f8c4457e0e191511bd885c7eb0f5ca6 1d3385db139a7eda50cff6510d3bb2860dac0c8fea5e31f4634a3552723b50fa 06e735b20be114645d1290ab8b9de67ff2111fa4109b4bbdbcc93e3f11356792 5de3a2a6fe23b2db1f6331a24821ea38be676e471a4c28d9db04eb1566ede221 dfda22deacb57753a584a5f8849c1dce7a704baa9c0107cbf7643d420e257975 4657b7285d0b3e754dd251a8ff42e892e2ffe15a9726a6a5608e114786a3450f 3a4e6e433128be20cd8a974ea355d1a0a9a142b2a069829ccc83ff5dc07f6751 f3e8813ecf97cb3e44bff70164af0ce276b5d2fb55f5b66da74e1847e5da7b7c 70f67c9d54d8b595dd8a6634ee2739595eaad18e21e1b11d7fa33f089918a78b d55cb0c7a6990d1a843efc86262d39a92dbf537546c6ac5c6fcc58b08e5f4b65 06e1c9fdbc8b0f0e6992d667c70fc5347ff4957b6ed7f0f7d64222ec76af7bb4 2dc53ae8b101cd611d108a83957487d22467e405ceee496e3635f3850ab14ec3 15b7cc8457c16790bbf0702370473aadbc02096b48307ca3b35dfcbcce130f1e 8637c6919db6d548939c9b67d5de7210abcffcc7f210a16b7d200f75bdc8d96d 933c1aca04ec5f2c2ada7008a51aee922b4087e21541c5d4cf5a5118b1e922c5 bdffadd9b6c3de9519be1b55686d93de84a08dad6081ef046ecad6bf8576e3c1 81737c98c4231b166ecc354b62886af2dd12b0d16c3973436d48048102fcfeb2 2f6c9258dd2e0dd75e437e943c1214ae358778cc44670dc38b4b4c520c0b2973 0689187488ec636cd152f16aef711c8bea1f465c626278dcccb9c60d90d45666 1f189b24dfa16fc2ea36917de8fc22cb2a8c9ab3bf7848eec2cb8026ea17ebf1 fdb3e9d56cb2885962bd481c8e58ba3aeeadf2db216e49a788d1d7163947d9f2 ff6b01ed8587e8f29cdc6bc2c74f1aa8943fa863860c445aa8f6579eca6b88c6 5fb6e6797b9c5f63befec00d7ed27dc75715118b9364238622a7b40713c49d98 b354e44758cae4691d105fc04d90660fe25cb8c2ced6b3d76fd4eb0051156ce4 554fbae1318cf708eaeaad8edec81ee22bcb34ba43b39c5b4ac058b6fd6cc493 0f235e4e45e5ee064da19a56b034bd9c1a1343b52ad8871b0fe9b94f33eb7b7d 57f5e7ceaa7ab39b83879639ac3bbbd6ee7027ecb4c0f738fe793d63087a455f 2bff5851724dc10ed23d209789246e840b48ae0d929ee7e0eff43511491fcc64 2263221ab98c78cd58063a8172c8352fd327a135ef8b9e62b445aac33dd78a2b deae142cffd98ebd2ebde2b99b435bccedbd16585889b9e75652f8a2191e188c 05282dddde3e8bfad2edcf4317218203aa3dc91eddb3f0573c98387728c624e8 ad229a7cb2f6e2cfa29025a11d103c6c30b5a8f224747b32b3190323140c84f7 e6b36ae651d6e22d81b59f470ca46d1ec7bba32d70da36ab72a32f4a275f1b11 991c9820fd7b236858fa0c3101a63bd1327122e3bd2e127bcf89dbb5eb936163 d57247f75cf25179f746a8baa51c6e5347e3c9cef335c81bee2247d4b4b7b8cb b36ad8141a3a175bd5a0f9c6250c930b1768d3cf9207f643af5ef823526922e0 1e4602c85bb43daa8cef2faa5f68732a615198139125c511d22b2012f256e9a6 d19d0e50a83714b34afab6508e062c6fdee72e9e376204eecf65491dbe55834e f530ad8aed431b2a9f4cad2fb9d569085fcfe7ccbf7c8babb600adffac605856 1a96c5f1db578882599c659b57077b85405cff17fe4a7cf50de8298bf047f12f 33167630369c25a2d2d568c38557187e2830eba072f04db85d6249cec3bd1df2 bc9d6e0f0596c99ad029b6e6e5e885cba6b792882adaaa20f1faaf3e68277b65 bf525ecf8e9d47dafe85152bdfccbcaece59f4ea82589c2ce1eccf6453b8128b 4441287007e76a1de625d85e33d06011ce4143eda558c4a169608b13b3e91128 a6d4eb992725a68c4ea50b39930df210b167f4dc4ce8e1e7d2f4c6791730b88a 54a046d6433a86bd45b76f97976f7d7f2fcdbe0b38b4204785f899711aa422f4 b05abf7b8affd992bfc2f6434388a64829e0c4d6524a231cdd24a44168989454 bb74e877f5d5b623766a4e708b9b0063f998a5197a1750a710131fa5ca17296f e9b4a62e37bc64dfa1eebcb0052d4d2148c88f73dbfef39e4d18c5ef398e80ec b38dfdb7f6ddc3842205170c6add96d6709c7ce817bd2b0bc4a85ff7f4e0d5b3 d95a97b7c21bb2766d2542df00c7cf720c51a310bfe1fdf0717879334df6fc27 064e888387eb728cb8da97bf5386746b46c14f4f4a3103bb3cd7735ae5b20377 1490910ac913a2d94e88cf08464c1479a4b8d15804e7ec26fd92d30caa78d717 c70ca4d218862f568a40ccf6ccdba243ef24189ca2ddd888bb92b4137e4b1e1d e62449c1edc62b17cd20d914426fbe4b91b6c8dc8d143d703ecac1550088a6d5 50189566d60a04e34050f59720a3b86acd99471b5cf63fa7847fa6dee95d4d87 a95d1626994518044bfecd33abf6ad5b03e3c43986055d833ef87286325cb4d0 173bcc05d86d48ccdbcc0d189111f99d8046f141a9c6251a25fe12ee4150549d 8106dd0603e485d18b300ccc820c35b8a9d1645b5f3b4c7cb0db0687f6405bd5 52890f159b49c981e8f34ef8d56fde30dccf7c05545f69e9e06b8d08a5979e9d 643bc3b2c51eb83f9bf711db53ef8147e84b2a920965d15388ae39334a637a85 38b83cc933904e5a7c942bc8ee2e6841a1c3d05184bd3fcdb5960ee932506b93 7a6115e9c9387d67f503a38eae94ef61ca9b964217556fa1c0e79e3da62f4f1a fc4dfc35d74b572b95f6ad7dada11043b559223a788befc4d4b162afa462d0da f4115a2ab73b3af4035c09e926247933a208c495044bcbc2e00b3345d0c85680 96c419b36c9f3a83a723094b750895ec15c2bbcf2c7770b2d9ba19553e03b443 7759094f77dc5cdf83f5ba1a570b61823ac457b014ad7387454c788798802662 11f8f6a0f7e73e015701b7c54826af47f78eb90fe47a4adc3dda00eebbbed9e3 813fda52d4c5c16779d8767c81d38a88c6f661afe14f450b3e0e08fd0513d1df cbbbf4282791766ccef72f28f3b726d7ab8bb9555456d45db0b21a7bc372d9ce 70c28f18d7d38f9553a03e76b654bc1a47cf133777a1352430b510a2efdd99a6 1b44a9c70fbcf307dda46b0e8843c017a6b0d3f08f7b9399db8c73995ae84e71 44b8154cc1dfd89fb8f9fd79d833ca64ca4abd779c2c427a5e5e2c26df3e3b31 56582834d79b0e976b7940cafe8ffc6f90ca704b8ff115f951e083f2154f04fc 9c4c661b4eb709c5632495461c2adc5e62adf3065151b13df3885c1028149454 97e1a1b92e58a49f7691b0f25691a26b91d3f4b04e0192ccfcb890dd91061b68 b4f1ead538206e29789439145e77f7b0a19979bf0054b3decc0b717f6ba5810a 23ad69f9a9244edf503eaff10353ee0fcd84fda51e2bb63eca1aaf1a8c997bbb 2c621b0f189bc029c5b70ce1f66a102a6c685969841ff885ebf8742f33eaf3dd c4fc4f47132f4c509551d843bcae70abc9b7a18a62efd05ffacb0a87b15dc18f 5499243bd6f5e517a0a8527f091e88dffd82110099b5ab9d6154947c25aebead 538ce8007e2071969b46b61a140dfe584d25d08465bbc8edc52f5e947ae3152a 014ca24509b0890eb849165dc6c3c4cf35fc5f7fb127b2caf33474cb835376b8 08e8d0dbf3825bf796e4960d345f27a682818bf532af06a38651a612fc98782d a34f0212ba601bbff5809d0edfddbde238aea5564b73042ac2d6127df79042c4 4f9dd607453f54f5df4203bcd6b7485afaf2a404a531cc7b937a8364655bb796 dce10790f5643026bb397d3dfe96fcc8c7d8be1ac7654a72d9bc806010c3fc79 a001c5568585064647c3909eef837b6e9f3e8c57f1611fb7a4eb407023ccfbed d5bde42837d100fb07cb3febfd0f54f690aab1de67fcd17b5fe960b95ce5ebdd f50891793b4c3f94208cfa25066a7bbe6feaeca5908cc3b64777cd0a832cd9ed fdca5806ee1cee776e98311e697b3fb6443572ca82c98469756707229921810b14232270fa4fb38b7b85ceff58428f84b80bdcc87774df1142d2b79c17653e0d6730e7bd5d85ffdc6bfe93225e54e16a4d6b2bb2fb174559ce3a14b17cb0600bc4e461e9558f3013c8395ee1107f888aafb2cd43b06fbbef88f8e34f2dca140bd9dcacd7e642a5564b354516a37a900291990842d4aa732e71c7e42c01dc180bd05d2928b0cb219849d2376b19901827cb964c63810edd3378f0b77ad876ba0a8a784c9805c16d7abfff09d4a17410157f2a7e4e8513ba6ddc37399a038ce90b5cae4133a9c73c10a2cf6934ca77e49c6006acf83592e14193da5b1fb875a30f0ea15dc512efd7b960300f55c9e1b018c112ea6732ce69638a1534c19d082309738d31faf579cbfafee96d72b63845be1b4558757c7d2d8afb9728a508b2ac0667d879c3624c51dae25b4632bca5abd4b801bd270551b22785d73876372e8b07808699621703dcdd20f8bb42a8eb1a5c13530a4fb6b5651149b37267a7c6b6040241fb0879833a5f05c43310c43e5dd1ce91c3ba09f811761ecf937fb60b05035f352a4030c843b64df74ec6573426e14f01ab892d6cdda4a0ac754171a44608ec915a59ec24b15265c6f68d9869438b3e7d5716cf03d8f180c0a9dd505e490389b357dc1fd9008e83ff9868d3fbc96737182b2ffaf07665ab1a4deafeddee0959c49aa3f77f1e3a1a6ad903ddfdcdb4bdfe0c7d632edbba5e74b8370480df0b381ea596dc06468dbd282c0de8ad8b92f4d633d622f0a98ce991d8b2da823f01911a3e1ddcfbc3a062792f159fec0f5461b661444a91042ca4f7750d5771a7000db614b1dd238f85bf1e68c24002b5b9285d6bb9141b37500eb2beee005db00695cda979b110e26de789c49177bce57665fbf6327a37dc3c7648d146cb567b0104cc5a5705ceaf21bcf302ebc57d563b1c7ab177edd2d6eef58de4ac64eafe048c1f2372c00914c46728a843ef1bd59832de9ab847b0e95bf06acebdd28afd09ce4955167a17ee507c1e37eabc1ba8e9ac72cb361fc28c96ec97219836bc240f69f58f65d1138822973d3fccb876fb86f1692017bd04f461e7e2aca672cae403814395496f2f52ab5d73f58f13c1e6f1896c7467c79e0ca1ed8cb1d1a1c64f0a29f965c93a99283be525d127bea1d6a1e5b7a8ca9f5deef6d5ebd6cde54d360036b0c070a4abb03d76aa5f213c38625001930fb584bdbdda2e0a7a5bb61436048a0e20920c8fa56fa74cf8d9be6bf7655bec3536f08e24c56d4c0c1545607508c52eb3bf67cdcaefea2d06cbf280adfaa19659c1704eba68c55c16c2d78f6009ca25cb7fa02a533df1f2ad63716c206627846c1071933410eae4556831018c065ff7057eace1870490f5dc21fcd46a3abcc8edf72a53c5fb5608d51583a93e0da35e7e6ca3729024c0714348ddc6288de97cbde3750f8edf733cd945ec06240422e95086690ab3ae75f2a2b32be600436fff5316e5cd05eadeb85f5b05942800204f0360c0ff5a4eb5af9b36bf5bfa86a21aaf9709f4bde1f8956d80d3c51602ff4e9c8ac552d65d1387f8c2d95770225d7c5b17495516cf698affe5ccae1e0689e8219d974ba7d5a0d2bad96fa5ce448f49bb389721386c5f957a50310d9205a8b7505ba63da81b283d68404cdeb82441af88aaabac817495efa060253b6e05f1b17f29cd0271bd0e7609be42c0feb8e5c847c5a8552a6e8c652d32ce880e0f9261fce81ab50a53ada3b4623104ad637b2e4792f8cb25cbd2d8e6d4385e150293babca539ce06a436576cb0b2b21ec9cebaef09303f58e93a710d37ff7a100beb84a28aa2171a7a01a012ce6442f2cf7b513954b2b0640e85c336e6f3f06602b1e310bd5285419e8ae8be61181508581b1003c04d0c980e7b79a7e4758c110926fe4925f7d18eab90bcc25d04469532ca4ffa04efeff78e4f410edca68728043c1eb50fb6f8a214205f9373d69250a0d79ee452349b975401d4661422c3c00547cc9594e7a2fc82f726b2efab7462ae518239d30dba7ee6b655bf6f69a3ba0c63d6b592637a03e282ac00664a1ccbeecc8e61292d597b580a685913ef3fb201efa1bd68d3befbd915d073b71e508d5ed6ecf4492589b463844bdfc3cc4b940c911247da12158c8a78f973c186c93245301204b6d7429636efe71966d6c19500f1308b7cc505d2a494942a5963d48e86eed54bf3815cc1ef86c821acccab650f28e2368dd0de1bc4df75aecf6758020c484c0fcb1e0a2f37b6e001da645176011c174316a668a305df8df5ee3470a7220db971a8bb5df9b66dd70948aae2c40aecde158b8446b8423a0ea6d22214f8ccf3846bd0a07ff0fc7e72d2c315a3ca0718bbee26117fd4b7c883149892aaae739158107a59b6a055491bf633543c900cf942150083ee16d72024a12d7d98878a60a75dca3ea57fa7e257a83e1a0c4e0c76815fa48b07ee350c1a84cbf47cb745f19af8e6fe9d5fa9ad291ed6dd738d0e4af98c44b73e56c23a6f9c46672ff0b18eed8c222ebc9424e1619e2a8d805b048b5976069f1e2d7cadce31e591ec446187327703c02d00a30f3da2d8c4cc54062c405c87b1ca7cb077ee95f16fc1e8a6a98da8d47b708a88b5c60a3488591c0e068d916fe6e7ffc37d3c280ea20b0f9a32fd5189b90b68e592b3dbdc5f00dd0997bb53dde85a28ee8bc45a0436d844f9534b31121463c17774a91169b84e33061639e4bb7258f8384fc06f677bb1a56bf4a8fe86889ce34adf3b441207ec7c0ca60ed547a0636bc11cd8a7ae8393348ad1728bf49359caa4aa209546296a6705dc159b4ee03b5970da001fef538b5199e4655f9b8aa017c09d3283add632370c2f4ad824efea03b5143d6061eab22fcef82faca8fca1028f49aba4e4ae4b7902080ad097c8ab41a66f1f831fecd2a63ace5b565b3ba30b150710941c3cb7650049f6d0310eedd2e8c99926b9dc7b065bedbd711ca55d7bba7daa1a1e6b2314040c442256741607e5132911fc427f3e58bac5d1649fb5a4bfbda3ede5a26cc1009ab2c60532262dacd560e6042cdf4c80865878b5d8971ce1bda6fa25a7ed250e559f20aa870d7dcf658c3973a980de5a62886916e37ad70742eea65f49ca3f0a83c14e38bb9136c2de260f5bd2cf82e153725c07da7c0e85a145e85c131cc9036f668717e0c902bc959f91daab3d96701318e584c6ae7ac6d76b9f6f9dc31a0cb0ef5a484da83905c17bb57439dcb0510f7303f41496e2337ef69f969e68f6093611a1ec650619bed990ae48d83964efe557fd030462c40b647146fdb3f23c0444395a95d190c91b1c21ef599d65ba232d4ff4daf62a58155303f1563927720ee459a752491b3805c2d10350c0cbd048dddcba24eeb0e235da01a0387e8d1b093f8737ca46e6897a8e5ced60e8b127aa83d5175760d689a76f1726c745793701aafb714e59dde879b94df91e40b48bef923669f9efff7f3b085671fd94c58c08a23f453c57287ed30fd18bcbd586e6a2abae1fc280e371ba21d05450fd083a0f173122848aa134d44ec225336a239f6db9cef40e84381bd7bd8ac506e12ce80e07ded894eb6abbc2941fa3f5753a792eb0aee100d1f602fbe3a44fd36b636d099d1c2dd9a6ff931caef181c554af1e068f713b20e2eba7466a435d220577430f9252ea6471dfd7693c515c91b369a244602b84304342208ed1315299085ef40b8ba825e7b37f597cb450861afc3b886035a425245dc76b89ec9c4cf58f66ec0eb779821f476a0fb60148b2b47b3219a3849ecced3f7d9dfe15d923307c05a10f40432b588fc0992a6aceddb6b196497b907f079e73e230f8b2cbe4e2275cc60ad68533a9e4a8bcbbb407853789fc1668f2f700d80162205fe748e2b9aa07ce00e1a4ac4d03e32a75f07974688ef59cc8adcd4466db770617fae2e4a48bb4100960cc80ab87d111279a70c7739a2c855e7dd0fd9cee24ac26ef2f14ed615b7b0a5b1dd79850bb5daa4a53d0189cac5f15ad64413c23948aac33e5d6f3583ab907f6ceeba8735b3dd9607c9f4f8f78a0684e2655317d101a005a8a6911388a4d0b1156ee16e98ba913396ae942b8c76ad455368bccb1618ece5bd99c53836eac03e77b644a9747e7646d8e1a709465b037e51f805eb4cbfebfca97bdcaeac47f0a745738f043888fe13da679e4accb93621d5ecd59683f5a2d7f55f7657a517e07bb12955ec96226a4f0a268f573fc667ad70d1274cce2678578097a01b042240edbe3e5e61f0ac6887ba5de30881393c22ab0a7ced153c0d0dcbcdebeac57b80a1c583defd1fa6b0b61ac5d4b14ce5ff5e33b198da51115762b50cbb31ddfad0ba48c2a2b63f6e96c2fadeeca1b36aa9ce1e3b2993f58c8ad29069faf4daed30f39eaa68108781f2593f29795bd21c2a06250a3e1797013f1e9db3db9e4a37205d4aaafd6e54644023cc6a5bbbfd667cb49bd7c3414c5f5fe4f4b3444fe32b8062d6247afced17bba022c5510855c8c1c29aee9e5bc7dc9d8d841b6a56586330c50bb033ec3894c7601d33c346d5c62eeb709652f0e794f8b8343988631e48e052f7bec03f09c8fcd5384c76101b2ecd3e1fd279ce139892860ba98368b91d50c9cb4cbbf6d16a7e236eadfbc912c7b7b70b2290e57bc6d3c5b1acb9583bcb30cb7ace1125139eedb5b5ac442c55c08bd8e8b1ac1f1406e15df1693e3e6eae2045dce5c2e94447c9f425ef55c55bd9f97f66ca6e2b1032ff0499fc9595bbf1007a49d820aa4b3974efb1455a16b3718fb4b9096d4aff48242f99f06d818ce63048f0f1ce3ec78dfbf9f31cbe1af9d27b1ced0e810a7e748030095f48619d035069ddad5cbadd28f0b4a81cc3661e3188b246cef6e9999f40de2f2b8f625814a029f2516d2977ad30968915db619bda26e1a20d6cb49205b6693a978ec040e810ca595468b4bff12d68e5d62b1e51297efb37aef73739be851d059c38d396fb00eaae5865fccbd2b60cb5705baecfdb1d549cc79e947186ce28c2a4414d1acdb0b3acc0d690717c9007facff4cfe5338b369394b7223e9801898d6477a27b77206b921ecce3d8fe7f40dbce65afc45dfdb126207222175d377d816efc7a12e04023ea80870e65ed66bc7cd1b0fd15203ce5ac52b46a5dbb7e0b8df10e7c104bc09cb97b3a59aa5d56fc55339b8a6c63f3621e758930b4e8cf7d1ed0e9081ad37048aa49af7f495300f83e0817e25079bff435f5439acb23ae41e88c9a3e3cd3b039266467a5c3bb58fea0d7714f91ec73f46c114fbd922a58dc3c34ae6832c440e521525e949765371cb232196fefa2d506bdea3d2f9dbd7de109ac78dfd42240cfdbd05c0ca041f2ba71a4f1476c1fa6d2ade9ad65036001a3fa03ccc6022000eeba2756b364c7fd3973586e58501651f316c94c3fb3578bb91225dc21ad8760249b3488f16ff9ce8a9ea1a90157d6b4c9c151e93cd9ab0f4eca0f420348fce0663697bc2574a77fe770845e9f68b4804cc678cf9c88742be9bc13593b5ece8018e2bb606318ffd72b3a75062486b90dc96952ae38b08a1565343cfcfa08e2606135600fd279911a24841a7528d9071f2fa2fe0c844eddd85a5b6355c05d0910c8295e99d3c3ed45457464ca44197d995739480059ed4296fcb5bbe542f4155004aebe915f2bd8f4eeb58df34fb9222bed31040cd6bca2d2cd486424cbdbe010e1fa6fcd8b4e3836d5ae7614ad71f8c3a7ff06d9ad2261568c54a9c2f2c1bca04827ac20cf352afd30902bc11858a81fbba8476e857c79d0365d794dc5930f20443003c43c0364057703695153f10c5a810377d4e49438371e149b0fe5691fe0a5fccb167f4638ed73656ce1c23320a302bd9805b5362ac3f67302abe5d32f804a42e46a0eeaabbf7e081d988806283c6c22c4862383ce3712f8166e7a643900ab4cc621680107e80dc8e76a5bd70cffa90c08c854400ba4b6105e55e334869076d7650a5b9940f8b21d73a3cb46d0dbd32034c185eb40250588b1357dbf2bc0738211569ecbff96f7340cc604acb397759e00a65e2bb544ebbc9ef3481d9b20dabbb64394e8071c7f9fed89a8dd20887aab437311c87fbb204ff92570ea32d01187fb7dfa271061ef7454b619f0e3201d42d01a97a2db879f9f908341089210f74ac17b6da5bd4a50e6bc270079a28ec9419674cd8dee33be5f7caf4598ec2010add4d9c8f5a41ba62d5c31e82e36f7112820129b5832b907a4a8dcba8dd1401497c6b2d585b978225039a12914369d6a42385df23b37599b247984899228c0f636ac59c2bc3fe81ce4c0f7be9268844db93f98379ecd53e7ca6a98b159af80994b46bf15f4e40ac63067326f7f15668229bd796313a409ea45ddee255b219041b482dfbe6c4267bd75af9ae20ec4af30d842accb735dd9ef1d421ac57f5da09a42b5c34a317f026a6763d292c4bf5f6bfadec6a702672c156dd55de77489203247eadeea9aca95ccc1bb4d06681824ae335ec182ce950ee1b6231494081b601bf94a600fc985388ea9d0d9d80fdf44116b0832fd3e7e0057477b79da24f460ede99763d30cf8cf867f8cf43c04ab5ef01584cf29870f203fb2d7d147e854c04f6eacfbf5acaa74813ab5a44fd635e9ec3365139caee24e94c6c5996ee18c90ae0fce9946d3375a502bbf365110750a1272dc7464912c0b72e35dfc2dbe97902446d5fb93f4665f7208c5bb22f10f961590dc9f0f4b18d792d55ebf65485ca0c71ddf0858ab500520872ef94dbf56514b86ee9b60615949cb2b5ab4b928edb0c095486af8e7dc78cc8ac0a4271a24338d8d0f9c2add24257560aeff32c0b3c092b7399c8dc3bed8e197af7686cb2e83c7db7e52b94ae28984f0bda2c827b650d95beb734c2ea7674dba6507fcd6a4a762f15462a1732edd5996f941ddf05c9059f3422b6cc8be92e06ebdb5f3e052ae48cbb38a6597be10e34557c10971ed107512b3d8451b6308cd6fd086f603243cfa00cc543a9cfc80193020a2b16e00d0b5077447dbc8b408775597e912e7db4a0f09404df9751ee6bafab04c40018f90f6f8d58401693b87199f50bd9b64731d868127b29a157779d0c917232712e1d0edf82a01b564106e97c15ad67280ecd24e861142f17aa52c66f86324c20631b09fcf8f392308e523f3d3a5ee26c595406e63ad31ca3ff3e2ffce9d7cc5b1c070ed5f75cfd9776583c014833a217c09f8439b25df35383f8e0e86e2ae397b86e001cdf0438d0c397602be99c831f7e50c503a9d6389e912d6456e3bf8ac22f6605ce22d93bafb5645fa31d735464f1b52d5e8bf52348d4d98d3581b7dbec340405f1336a3cf9889f8ccb44420439c2472e8299098e2f7326a81473cef47791860bd6526e4023ede941e2c4eaf85112948f119791e3bad91f4641a53264a924660e0e1da148c3925ca99f6a4e50c8973b17374b24425bc6737d0165e623540051018ccc20bf2d99bd4d46242c3c5e6a0bebdd4f4774e7095e8c2e4946060b3df206a434c33768b493323fc0e7d3dc0680b750779eff21dd2d9436f3fb18087ae70ba84acb0956006c8b8d216fa15b7cb436b9476efa7cd0ffdfa2340f4e4418ed0610881cc0155df8744b8fbb9caa9098e08c93896b61ed461028198abc43040800dcde563ba4d8d67bc0eb374a366f0c262b05d53c5fc49fd5f761f426b0be6c0ae878e1226f9ceac9d0f7f58bc53336eae6e3ca30d9423e958af5ce5fa349fd003e41ac17dd98bcfd951ba4f6c7141fbe161efe743d950b6ca12dd16d52b26906a4b59c544a4a91e2342db1b79ef574e24da9c94002adef8e3220467f5cc99a084c9937b087269f4b8ba728194436808c005c1c52c3044f9f764ccb04a91fad08198d5d4c8e964c630ce4cfad2cb20aeee020ef27e9284db2f9f8cca84560610ada58f8072dace4815379e4459efa0fca48e8af7c425e98ba6b6dc867c0fd3a0466383dbbf9e59ce84a7a25e381680324d775fcaa91a944b158729d7fe61bb50f2d9b65109b385ee0239f881dba27c22d0d6ad99ee0314c104f7ba9fa4a01e30505df810f9772d76d404b0887315111de29c828d68f413d1775f05e440e73cc01eb5d3cfaca7aaa50ee875b71896402c5a4f3603d9870fae5a547f164b77e9401028b7b3066b9ca595dc59e1b09db19588738d706d492f2ff726160654109a70be382c9bebe3430f71168100ce12bd790fb0f4b073eb491327072e5a8de5af6070e1a8dbc680c7eaa9e91146a02126fd24cf8420919ebbb79107e6c5aa842e4011a49fa14a37a0d329de59f47335157bf4f2960557c434ee1669977eb4d3391052e3fc74140bc0ba9a442b072650c6b859241ff4e13b006b6cedbc78672a83700309106e0130bed863fe271394a6295524cf989e8eee94f851273b2264161720ab741f05c8d2f7995b284bca64adb73f932a2032346fd3506346871aa79fae60dadf16a1446baf27b3a128ff64f05e0d603e92d5821263f0de123f24425d58006c681e6fe7dc5f930b013579a992fe9b3569f1c2c5e65b12ada288d141f3875055efaab89beb3cbe1fa7e078610a4ac81499784a45adc257f2167c40d7679800fba9b7e2942569254586756e0db618eece6439a2b2b7eb25d7111591e3bc8bf02474f73df7a45151fccc9771254b0bc27f073310c5a97a33a77b7a413c8e9da0ed7bbdcb62d4a95b18fc6ff2ab70bfbc7a3ae027cb9efd9ff9133e16cf03b470812a3c5fc6fb338d7a1934a6b32fdaa3f1c949b6e9f18f869e6395e5cdc248705fd61019e76213491e01da297f597cb92171d13966fd2af891e759f1ff4f8a30a31b22f6d1406581e9af79e95f140cdc8bfdc6acdb297c8d1e3849d2065b8590d12fbbcdb16368ffb7ceb0fd3d76365c603fedc81d1069c1f0b64e7d8b474e10b1a7a1fada932f503b9914ce63dbf9eb4a9ba8140f7a9bd166e8828fcd4510b0aeae9e526f0fcb35da216efbe9497f4ef88c1e4a50bd82dfcbe163ff6475db203a1c0cc877553ed8ea2a4569cb26aac42de0ac9890c3075202196b1936879b807db277c9f3642627c878c2e4121d27c6edf21bf3f8d9426987d76e0b65db1fe02a42bafbdadfdaf0a64cc457e6083ace822ebedc6497fb05adefaee08e043fd08d7f366cbffa37412825c9193e599a541bf54c205eed03be943ca061e33f9ed0df083c963ffee512f521be282d6bfa7d9db0168357a945800f092f051dc8e8e090aa33428fb80fa9a6dafaf733d614b38d1a8d611ab468afd1b974466b2af2409c2f23d7e8ceada7582f80835c1d2d3f9e3f3f67f86b6fde5bed0b85452c0f8063de7960a058dce820c84c5254112d35c6500494def6c854dac63233af4b07b04523df8d66a58d59ed3ec8020f31ed5f4fa1838e03ca99f6d298c929bfb0971013fc011d4b632f27af86b5f4adf4c87a5eb3c3cd52cd56ec59b357f0ff140520596b42e96e651d5f7f4387ae84cb04fb79eb6da14c52539641fb704933f91d004712e2264b059d0f7af69f7c0ebbcc92ae2a845f9021e8a9b85b1e15b28b9ac098e01aa10cfd10e89ac9f8abf8acc5f346bf406880aed3e08eaa99e294b7437027cc829ea4633204cb6afe10abb3d97c37df0dbd1ecff6519a866a04ccb3856011f09374bcc02d0f146b30e127d0676e73bbc386ae08a69d95f60ee05ce945a08828a72c9f3d9cd9ab431cc8ad1350936bf19935941f5bbbea5f0c9e755c32e0c6661182567e926ad7202e14cb0d915a097ad4c1bd77b3f6099ff8d5a5d1df7030df1f95838613a804fa51571ff701a2a57562fc53f30796efb624336e49d9e0963d428e5d5c42ce64d1fc27937253cec3b50a2cd59863c78c94bfef59b6cf701baf771516a9717a3bb0697e4e1e10a1b488dec8a44eb2fbaa7e74e1994d798017bc65fba54691d6e8f8f80b7a34d0d4ce0f16fc79cdb9c530dc38c54386e67083ab6856d5231755f759f4ab9f959eb559f174d52ebb145e9355aace1b7109204a0cf433199e2ab616e4f4d08e1e570930f073e0f40fa73237cf2defbb41789010ef562d8c22980b5cac6474777fbc76c42147d93b8264e9390297d98dc16fe0343a8f5d57d41c5a94894b35c0292a9bc49e8e3b0b50265bd522d497a52db5904e97606054ac3c4be9f6543bccce6a6d6013000ce39f48e204d279fb2950d950fe82d8d47e60cd70b5374d7cdf9e3084557368a9ec266ba491177d95db686c9079d8abc0170b78fe19e7832566c2e6a09e4d7b70a84023a4141377b309b6c4f0210ba46051a3a9303a6eb9f693c8b411e771ecb032091738b43bce8c8034d5007de1808aebf504de98be2f132af6b9885b2c0613834dcf09a76497fa9d9379b027316b76f304de777112c352cfbbf188382d3af29f5c335e230741cde7f6e490bda6f4f24e7fe5890edec6c5e19d782792cc828825c5e5100f7ef8b1fc5e71204bd3d8cd25e5dc85ce4dc0cb47a85d68acbf8a3a0cad71a59c1c860c2a919210ac1e314659b9f9d18b91d6dc4746579beff6c90db5dfd179918f118361a4862059ef3b88b70229c621d882b0cea8c1a820e1182dc2a039077cca4b070ed20c40276450445579679b1a52bf191414a37fab57f11f2ad5cc4ffa5a7630bf8a0fc0be21c22f0a4a9d07ff744819c52b011902a09596e65549cf86247048bf45816069a2b146c64260c952349071f0e32d05014e8d711e08fc12073809992cf7c27091caf2e48a7e6359de41861e9338d509b17b0b6b029f6740f32dcdf1a535e4108b47e35596c2c29b6af84e0785a94e3e77a1d7256b4fcbfefd0ea7f9ce0420602703226f63114131e89deee0cbee0df394ad23c8c697f416d5e2ee4850ea47c0ee66e7f34dcff806dc9247d2c398df09e383ac8adcf305f77e1ef784abd8f1206af17e04736fdf21689f6420b23dde5e77697880f5f1ea28bb7b34e9a31f0470fbedebff65262de5459efbfc27b9f86b8d4f373b09fbb8f4793d1498414ce620c8fc7dbc04c13b24adea3d4e539fad2ed32130b14ca68a6eb0f403136ca663802fb74f891bfec224cbb6591770f9fdcc2522e529ad36c38c9874705330d5d3a0fc408a76d9678a477113530780215904cd432898363fad34eabd087bf40f11a066ba7d36dcb9985f0443221f31103fa823907c3c26f1a93cb3bbf450a2477ee0d90df998d3ebc1083f9565dbae4e55d2295be22a01364a37b1f537feda88a650a1d74ca64f93c797e9b994fe58af8e123c3daff254c0f7aaf6d6320428ce0dc0cd53b5738a02709b6b19b79f48a25f0ea83ae80e1325cef4cb2b012b92dbbe12240d1f6fdead1a65803801041febf02fc89fbc8237e4282da4d18df2cfdc5d904a6be8c4301b65d2dff699a89d9173a02e023ccb8c7cc9d1a7839a8ce932c5c00dbb744b90dd972eae15815dd05f099ad3b45a8e9a97be45e7359aed6f1e49a00183de7d79a1a872a423fae4e68a0a89b7c34638e4672a927daa4f6463918cc04ec003e3b126c5e01a84d02bb17ec92c63e438909698a84a25f6a3015962123079fa2ec804834e258d2181b43c2a21b533c013e8826c76939f1a58f40a4b98c0a6725505a161bea90cec175f4008571f8d89c29dbbbf4bd7d1936f27ea9922005e0e03db05726298fd894b52a7e2bf7ab9440b6ae7a54d470e21aea2a90aeca09e8de24a75488005e87ed060747686aadfc40a0d79bc01d39fba9ce4313b7460eb7a43e5a6a69125b9975ddcc4c40b77cd651e2fa02a918b20843990e264b0c099137d0711f04dcd641642845468d7ba21663fa098ba1131553587cb3665ab302a4070769ba1ff3efd83e860b7d92a8f5a11056f1c751279a9fce8cdb0b55c00e7267085b00932919005f2e4c120056e2b53858230255b0ae64ab9fe864ecfd029e36404cdaa3d6597705f1b34e6c16dea1f03ed5283033e74b725eb3c720cd01398867259fa9c8272f4d4a9275b1b146c62e2167eca670e9fb0dd1b779bb3f04ae4355bb99207614a624c93d885eadc8d103c94371eeaf952933ef7e3de1d00920399a1fb123f38e7e355c1e946dc18f590fe841d20cba5aa8681a0e69ff3c0a3b0e44a6c7855763eadabcac21336b6dc18767005124920f47ee4e2a2212c700a7ce3914e4000152e28b507dcaafcb53fcdda40e022e597a1595589c45079a0258359940c4c477ed1b5bec1195a776e9e46221c4b2c982005cdaf76fbc968701f725e7498d64ca8c2dc3d877d08ced4e12546f3e0e2aaa6b1901255900f6fc07322c253161883d7d90fec4bf399e8073c3f5551f838dc1f35909d6081007910e5fc30529d3ba4e921700b3727ba0a4e187cfb8ca9880aba1efca288bb53efc019058e6e77033ebe28683429b5b465e5a9995b38453cb3fb92941dea9774b3b0adda0e44cca665c2db4ae8ce45786d001c0906f3472b3dff211780304b13b870a66b0c6a59c0c8b95f5022d463c7298eef74bedfb97cc15c2237aeedc36c0a300f0d54779aed354e8773aafa1336a3bd6439f0c0e836f764a2b2a3038eb16860b89a26b262678372fbb8c5146c47175cb384875e6a0a11953aa4207a4dffaf302d394cda13ae2df4fe6666023d47d04f347d0be2f4078eb44d7a9d2bb616bb80e6bcc7edcf64bc921209c4af21b61a08b1b337484e0a1b483f01c1039674ef4026575d3fcf7c1629fcaf7c001fff9e4a4eb3f3eb3e693b24cc20657f909084b05e05ee0506cdbfe846e2ffbe8843e8f858677f75b735dab06963f29b90466e20f51db198488566fc773cbec56d820013d94514eb6c028289ebb91b61e5fef310127bd8fee9e17fc8f0f769a83d9ae99c0977a4b7cf68cedb675fffce994cc6a0cd7ad90cb704df1ba6dcb19163c97f5b20ee95ab219afbca2d63b2e5637751d026c8d6f5557381eecd578de8e0a31bd04f23cac3852d359a263f7e3a8282ad10c7fcd81bfe7e198449b108f9a6e8aae836af919b8df2aeb990ba72a687b70f0055cbd17c3e8d18f4bc3781f283038aef98ea6909c11ced6a92009fca2c42bea05460be37a346efadc3c935a03eac7360e4c9e055cb948d3916ed070c85365940c5ca0caf1aad6de804e6e9d05a60a1b0ebfabf1768a74df1bef031ef7f64417025dc8dcdd6d1de4d7e4e3661434b0d03808ad652b65337b9a78e7b10204181f0b6052d908152d1718c6b13ac83cb8e9a8f0f2b7212bc3dbc107b2e03899eb2d030da1af5ccffbf2e3089243ec9aaa6fa5142f12bf96340b7b87f418a985e8010321721c0a1e190c96e828ac2855cb2f3a5472a70b772c743d2d51432e30836901496bd17b29dead2cf3c98d31b9e1aa5cd76ec488be2b94f500739b7d1051e101f603af598f1542dc699d4bfbdd552d482798c34789101b520f9e9220f5b3a80f075865a6c7d0d927fc18aef7f929c5e43b70652bf10aa25a0b1ff91ec8fc4f0e9cdc6933c98bc169cac35f1cb3f20df8d1402ad70228e6cb3e4b0eaa618d1c0be77f931cfd0cb2648870570666bdd222557bb473af1ad4b72177fca10a42330e03dd404b8ee7c4853fd9258a7ddbb2f632d64624f6fd55f465c0941c46717104412874cf00c6dd65dcecbb6a5afa5313a1d7335d75f90abdac80b92ac452320a9693d9d06cd1921c4f6b1518da1b9da1dae09070e4b524b36ecbab95fa4d860e23f1c8a72aeab7c127a1add3741bbf2cc64285a9d509728c24cfe43b8d12450977bc0e1fe842be390d69707e384b87d89c617b996407103161575547edf5c604b6e95de7f1268d1d8b7adf57dc3c947fa532d33273b709f19869028cb5b28403192eed50cbe88d00d5cf457a0b79643ac45eb3fb1243e3b8fe0a8a5f2d33b9067dd70a9a892b9436159096a26d0175aa6ba7b47a9374d088736bb4fc101afc02a1dc5ba299eaec1145923a8590f0334a87d1b954cea6d3b03c11e4ce0b665f00c11a5da4eb2fd2b6bd1d7c3a03938c5b985ce7c3fd01653820c39a98c3e01509f8b53527466b7bb85ead814344e3ae6391ad8ddcc9f46d8c9b1bfa4006c0010940129dd7c4ad32eab78aea45df74183ab318cbffb5b0b267852bd3d4b59b8c01623b2246e5a741aed54a8831624edcc230346c833ae0ffc45d948465c130b60b04eca285d8fe2a5fca1ea7de8b6faaa3dfda8873120e210463ce53b64fa20c051afebbfbb876dad03df2f0ce8c857f999feeeb18e48059695a161d963fa0600ecd2ea57653a6737703d4fb60aca6c47f74d111c0c5f9b7ca81a8fd531ef7800392c0fe0ca4c96a8e1369970df11e883eb7335699dc1cab714270c6bd3e84b009eed2c98c3eb582d2968388e0010c7c92ee2611a037d01457caa068018109f102c407059537b43afa340a62580589330606e04f1f0f4612c931342c5da92d92094bcd178cfa4c967cb2e51bf51a4aa58de49afe1da78989a9741417c2645ab70345b1b92193d46e4eaf81aa8de13d1915f214de9123019a70fadd5ccf64ee4e021fe02f9baeb6d36fd6b6126f9754054a12ab3caebf1160e6c764ace3c29656057f310f069e73e67b4817256ab326c16b214370a5539f303f6a207a2c6b07c704d2bb831fd25246deec357264ab17c5d1647188bb9401439e5b4cb384588bb6044a9c3965f8b3ef20476c519f8310fb1934e1f4b7fbd20e68ddd6ccc74ac5050bce8c9e95fa4d1465ed37e8a35b9657c2e281bf4d1431d3de26b2bb64a9bd62041187617c1813adc2ceef6ff247e59ee17dd68bba738a23c9b26937fd6b08550911932b05bd2c08877adde5601aca3c101c1e5df8e214e97e38319daf92addd0cba9f74012f4c48ffafe584e7c42a67f7a142804f979995ac27b0bda3431f400462eb53ee0c18ebe4dae13e91590bb6e0cc1d11c665f78712d91b3c744c1fbd0e7b081c894edba26e9666e0d4bf41763a9fbe741a39e421d6b4f9ace9b1c03b07016e5ca094e666df5331b72b692b81858b534c72397241634b1cf0fb1495650236a2fb8e84c57ff1b8921d2c9ac489640231536b0743a5a74bc4640242223a0ea54a0b5410ee27be6f9b53824df8c4afed5ce75aacb6c63857e6d2e6a64dcb03603247716d26222982f0bf53d165d51aae64d250556962539bbe8dea9cc0d8010162a20972488a0fa1052b5fef374de84c2542aec29675e724d036da6eb9a801b6f5363ca95f898558c12e695eb34093b88571ca5b375d00e0880a65dc317401485577a7e51c67533ec7f1f5ac71b9173f9b999fd09dde7a0f45d7d9957866022a903d1e5f1b8b0a86934c533308099f07d8cfa941ab0384ca6d3e2504f67d07fadbe6f26aadb00618284361441d91b7b7ff57f6572ff111a3388b96232a020779c9ed113707947398404ab8270411798fcdcf72b88ab0fbbf243f0caf3efc053dae9404af991bfd8281c54819a32043a00bf8a404cbb98899fc12446231f807ae9fe16f1a145bf39fa1963eb226f5b571e09ba335298f413624ada7977fee0795697a07182139460dd22bdc89cbfedda675bff60736e629001d761146bd730d13622669a02e4b532e0cca5901f445b44b8f06794a1263f37272dd37a2737b0d8fcd14110010611ba0d920846c8e63cf2afe796947b559238fc769f21abbd00228c4939a8f7e1b29e3d823bfadb6810878f1948d4acefc4a4d69ded0b3ce540ec58910c15c97391d7518a48546eb4a712105eede65f62057d5dda4ff9d08b2068ae4f45ee1effe21116eea387cbc3239a5283450addff4a860ad5e0230292e00ec5d73bf1b947ed41047cafd5fc2c1213136716b55f9d89990e4ef28b773ca009430eb2f9855c682f1d8d313e51351364a3bdc0f714fa1e325c95cf1b939f30a4b7e6f1aefc151b6428ddd7331e4f42bb839ae197f9ce5d18503438c886f3d0d05d15303955ac39940b27f2cbf1fe2b85d24fb59583238a43ee88bea356b3b03f65b99c040d7511676bc6734715cd02b6cd785603aa416eba2c4e3853b0ee406932a57a652dc17248a3d425a431dfd68cbb23ae4a3dbc3d1ed065b25c84cf90a3d581d044b1d857d7bb5052e2543b5ee57430927cdf485a717ba8fe18405a5034a4e3345279fcb3cdcc5369d3ab8f7e93444e19acc156d254948be4c20d1c30620ae36dbf9680651002480087f071972843a7c3700cc7b97133050055ee6c90df23aa23ee976654a607e04dbc503bc1e3ea016f17297103a657ea0cd09769e0adf4a8e613010032feaa806f0f0426c18d1c37d08fbf9deaf1de5b5c757ab700a59d96b230ea20b6b9f945d8773c0837ba4bba9420b2a9844721dcd6360b5360ae5bbd645a761c5da5e03016e4dcb68987b2ed4b3267cb8fcf0f55e20e961e80a5e0e6df7c2995d2d8410a30b74dc820ba1aff6cbd45a95517fc35dea96a5bb045dae575d4dcc33af55497eeab5200a6391acd8cdeeaa066b6d74dde1d0390203b4486ead7b4abc0d1b00114e93d31f151017f22bdc065946d01e88d2dc1d7506c0ce28305450c62424ce4bda404ab1d98ce6b3cff5b5f7484363489feb615506968bee961ff3d15b1c957382963856534347303a6adcb7bbbb202d8998dd550e518ff32f46ae821f1aa2b534b9d9b9a1a540ffd5ad13781cadfd6e4f0ac5d104c9be0a8b434aa51144ddfd0cca45e265396fd72934a786f13865360ddc61bf01041bce6521b6e6f85877a8a22dd6621e5ab422c116c14058c2b8e5e571ce110b47cecd9ac119e657c6e1afa6ac4268509dbbe2f7ac860be44ba1013d1c3ea1010ffd6d69b9b8babb730dcd8278661562085b5e1735cbcbb391bc29794a021a06856b2aa9d70a28be9bf4c870f381785c0ac6b8ff4707350ba0611ebabe00b40b46098b8049426b49636d7f6d120c2cb4972c12280b38cfba6d55b00cda0c0508bad37af774ab99e8ae8c391c0ff87b2c4bcce9b981fe2d28b56d75acaead030a4acdb0ced52e8216e962f538004d990d8bb600fe3584afb6649802993496d703b23f7166848add9332214c1cbdc16aa28240cfc260cb17f96d43015798081903aa7ca7d298bb3bd04608d5d89815da378769a4e7e9e1a232de654f715358890f3f410f1dbf7043054205b0ca730a8583c2f15330713b3f12922d7a45a5739602522babf592a14cd2648aa10873acce0ce30f264cd82f45bae50f550641a9ee01d294a6580be5c08f797fd6e735d85f76d8863f47937d83908d113fb51510c800e4ecafd1c22a9a040cf0b81d8c7c944b6bc6ee7a4bec69fd6f34096c6515e809af825ee757e25e3b32d20a4b0f21a4271bf9363d37db4a69c7f86865fd5a2f0eb38b8badff30edcc18cee510d51b18dd20f4e57d823feb61b45b91878fc7480a2f9ac5f4863bef2429d659d6c47f65c1f770f589c73165c529dd16369852b00ba3da7e934ac978e7a1c9467c5069eec187b5f3eb6b6d408e1f2c51e42d0897080fc92dce4fdc9ee1e98b23f9fdbd8d0c16d7891f836d76e82c865f9e018a67027c270da4d3d50d2f94b73b3388f25ae72a8f410a33b4529cf41683dd68a85908f26774cea510f755c3e924338d90c0d7c6c4e103241a4bbf8d51bff678b4b1072a1c8da0df9cd906da5038560fb4f79d77c726b6d2b5863ce912c5f3640c560e679ba050d6d683c089d9d0b56053563295132c00fa8b6649f991a9f3fdcba9005a1531827070a8a909b89f14b5c1340fce5904a842fcf9f8e2eb6316bcbb2d0ae4df1e45c3c8c3b924120c55cb3d2ac6a2b9a93d42e75dbaa4ea41300ece0a00e0d498ed629c24cd05a99f276e03163f35201d829a42fcf7b0fec0fac92ed70d75929c92cc8a5b65594c34bc95c24beff52a14b5e7600034e781d132d3cd0d039ee8c031672afcc6d013122f96c70ebca8e021542a841428b0df1c927a31dd085bfdddb8916a6033a920a03e1411509cd7603958f60eb8b4943290c0e2fceb0f84281f11cd3df48d0ffec21c4e4427be369c8b924068e2360d5c43344d9f660986e5ecc862611c007032ecc2f4252b29aa2ea4c095c662bfdda9a43a64d4e00458e8af844506774d89334fc43f3975148ec4b16acc77f68003b9ffee943e530b2e2d2944dc879896f082fcae52d09e444d86fd73d3ad9e9c1f29db3e757bac0761e4f070944ce9514fbfa079a0fff6bb3b837d9a311ab6382dbdbb83f3e1d003f417408f9fccad01159830b99dad154e5a0db18debaedfb94e855a5538d44d0504f929f5c49b51cfc91b134160eacc235186b3dd8fa851e0ec25dcf04112e605139682f6ae2f2b6c3cb6e07644591ecd880f14a8b467b8beaa8174b37d8a7c0b0e235f4e30e66c74ef29bd672ae3116dc7e98677c7f0c7f4cb3c2501aa301a0d1ee15f6c1c41fd026f36cf02ab1772a195031a220d17916dd541dee2bd8afa009d5471d2ac36ad18ce1f0b25431ed1333afef6bdaa3f93e19c32b34f9d938a049a872cec76ab21f15efd5451e84dc499a05c104d7510f02455ef1535f187190e18efabffa5640c7904a0df3de3b14c585016cd2c3c12df579977ae0118a76e060025bc81a95dfbbe704a543c10bd95e96cb3fba0eeae041a44f10b3f47982400a7f5b172abef8717dda6378ddbdbe1d116d3038684fb0a1f7015f2c888b73e0c false +check_ring_signature 712e2a529b2f909083c7cbed15d7f3a554ca60f4aa5763f2f8d1cbf0036d6767 a49dd98fe7caf79fe610aff8d7075329167bacf11a8a5249783e51cabb395030 2 b9116437199e2af3be9140bafc1c054dd264230c12efd5418ff8ff74873fdb7a bb6853dd70077ed92aa93284341022b3e2a0bd42715f2fc9f5e5c9067ee7c497 2bc696bead0e768833e09e31d5af49d55da80f9bab2fb52681fdb3d410883a0bfe8a07e9b17fe9d13b8fce8e12c1e943be392627dcc59b8d0f6d932b4c2f4b95c74cac8f51d733460fde7246105e69ed10fc23b15e815297116649fe60eea80343edd019ec9d7cd025f73533fb633eb70d2be5b98ccad1d0662333aec6d9550e false +check_ring_signature 70a3a21c5b6274072fc35004e3d25a300772a26122771aef9cda0c2b913d42cc 613df707ee3d79d194c3586714d9d5899c7e4daadf8eb48a31423aad57da603f 33 27afb87e8fb01edeb54c9d78a2ecac572422a962b108b6c27c561040fde33b6c 67745ab41667be07e4208c86b1133908ac705fdd370521ed824cc3a62fae3ad9 22c7e086ebde96833664837916f7e64d33a6786cb5badb976205876d07557241 664768f74999cefec2d9e629938d1d1b1408dd68982c3a18a80566c36a79476f 82bf2e9f769f1c402c4df3c852bc2748d0840431ed9df16f448d2ad828337144 c08a65b11b076fe292b9ac277a5722ef941e575d5ad34201dd2a96d0c7992f0e c41ff7e3d751f7c2792b9166d1782d1bd08a90c5485c39cfa6c852c0f1f0ede1 c04ade071cbce6cb1dad3b0d359d15ef93cdec9fb92c85ed9a44d44d3cce5943 08a74e16972d65949e742aa18a2f00607e56bdbb4bb83ae85404107210ed77a0 14bb12cbd833566eeea8495c759fbdc630d777d695e7c2961a32c5ba2f8e2f27 b703f3ec7e5b9202f7b9d98b2416c1255be92aaec11340e61da4a217d6ac96d1 1f1d1e143f4a891d01eb97116d7a6feb6df446ca793d468a9ca8f8347953b8f7 23fa78946658842cecf0e236c946f07908330cb1ca90a2ab4ed074b1601e5e66 6ff0a66a9715c242cd207c9b684c24062c87cc6c8955bafd49b2da9d8f2fbbc9 ec99dc5fe76ead93ee3bbe0319eb63e91f8314ffc148a061bfaef2a16a16fc2e 2a6b52cccd59eacd76653dc5cc24adfbcbea99e3b434a9b3712ee850f3fbc4c3 72d153cbf4767deca5a9521ed1d9812bcc94ca2a6eb7aaee7465df66a3f1b9b2 96d8984ea4561f8b8b00e62ed31c93673090a41be65a93a865544f5d7b2f7742 24971bb1e3a71242ba3f8842a64e2f040e25d03f8fa5002b2e73f09e10dae1d3 3bc239051a8d7de732c30399bfc52e3387ffceea19e12ced505af7bde875a98b 8338d1db0ec44b975a9b3e10df4b350ee36cc758fb64221c32c7f3d4edb31063 1841b1956e3cee41f73681d01be95e8104a75f8b13c0774a485096ac2fe74185 b864b03c3ac2e38e7b79025245793bc1507dd48c98f77b07d365a10a3ef93b47 0d57e5d68aa35b3f32a68167a5f17f9f62cab3775d7d93509be9cf60f7472d68 a97633b31dcc11fc8dc763add46a79a1e548565ed53b1e060bfe5727936c86a7 4714bdf80dd91a3588d91c83bcc31b0b253f69ad5ebf8a9bfd644ec4c1e2da8e dd905fd1acb5fc808f0ba503ad6e8a6cfa31f24d7daccf1ff96940a2ab736c0d 3de2dd76639b4568d5f524272bc36acf2ed7050757886378e72394e759dc322e 42aaada61c3648a32d1df4bd0006e2361cfe3e9bc4c7f5ffe70ae879ffb252f4 16b9ad9c47772b94675d8ec6b1991f34df1d1c1a723d5f26bec46453dbf10035 2a6bb798bd9f203c67596f25ed4da92cf95566b60c69f5fd0ca7d68e07ff4e2b 4fbfef707d62cd484f2921811896350f5c8b941e58399e15a8119f2371533488 f6f7d933913287d27f0cfd34b5a8b3ec0a19b621e95bf98b0432d0b3f186e02f 0ca4d415379859170fb68f401fc7be84214954f0b2c1eb57b1d07a63fdb52e04c41201b815b86d8594a59d4d215e7ebe00fbdd27d44906aaf73dd139202b31049a0cf994de90afdb6d2085539725ee8683e20a64d374bbd21e27f1a6ecd8a60817e156c5a662fbc762dd2ca978bd18830339d03fedaf9020b18d404bd423ac0f1243708f794d5c1d30bcac2e18b8b9c308492c9768376a233d3b37bceb96350c4f5740a2825954cbaa643d23849b429a8f9b199d9fdd71904b2f099223fdd601d9590b24aa2c1d5c1f1eea29142286b03d2720f5ff0e618b2a7cc9d6323d7c01cbf5a493feee05ce67d948b9efa1ef5b13f6246af9372a97142824c9e1e898044f3e1d05d09ab439b51720f488d37b820fef5e03ad1b138a0a6d2cf188166602624165e943b27af6183f8922e5e98a02460be3bd8fa0f0118eba1f13301bd2058510341af70c9916648bed09d0924e61080d8db8e640dad68d6ff4825f81a20cb8d920f73ee3b667143e11551202cfa05d9c65dad6583e2b30b2e9b1fa6d560264920029917d20f30cb2cf394089f6f7a18bc2d69d7f3323e07161126344670322a5f6a29704ddc8696e94c8556b55cbe7003542368b7820cc39468ee26da0017bbbd3924449ea6143453e0b796b3d33afa5342a06df5b3cd7face7d7fbc2e035d684aa995de2727b90916f94877b0c8436bbf89a0adeb1a2697476a4eebde0fc3f3f65be5d4a2a1f0c1abe2bf55fb3ff2cccb4d8a53c291e82e537c2da8f1054835e4e84d9d1ecaf2c565147330a6fa9cc3bd66ad740e0794040e1e7fd04408f90287e07212a683c2d71e6089f06d344c9ec60272e3b6f65b6e9243256f340013478d608fd7cfc7ff265e124076796dffe30b41475392cc4c714ba33bffe408428376a6720ed29430f0eaaa5e84502a018d62d5f5234b592f3c90a4030b2009314b11480b1eef7d18c5a83f1911347db1c517327b9f24bbaafb45134692f50bd08f8368c5c37a248d0eb46fc693a9b7d9cdc587fb895064fa639f02e32ceb0c8463d948a08a95a2b28faeec59d256b28c0ec374c485078c9a51b76ad811e10818cbabe45c989deaf097d957a79682a74f97008096ef9926b8636bdbb1e0c40a991c835d28b790accfbe9c184dafe76841185dd3f1e788807104ff051121750b5c86a49d13e0f7446443e3695bb2d17c82395d96b52e331ba10f2859b4792801cc00fb1a032c17ee0b8d2a7e3ef8338ab05ba15851ad789a0677e480e8e3aa0a636202409da85404d0bb6272ca66c50e29a7b5da0e91a9a9991c25ed4d13430b9ff6a0fb9ed0fd6c0209625b0dc5308ddfa52112b600dc8300eaff6d67a8eb06e2d65ebd48a1ada598729154f5f6488e95594b8149931cd855c41cc7da609f0757ab0cc8d5335cf0186055103be4289c6abbbb15b12b1b951c553597f1663b0f30bb2fda78613b5e2164679e13856ecae2f615d21c8025d8d51bb4163905aa07e0991a04ef49fc4a21f3cb45033e9ec3b101f115f6b0889314024a29382b0d083b8a0148ee16676c98b2e64bdb7fa7ee982a4624bf7d7bcca65675dc2b1702081a5624e2b1e947741339807afa81b221b9bea33a8206d9075eebeffbae69550bf3a6994f1c204181374a7ba3ffe927f159335df40b48311aaf0ec8fb3a54570036f8980b725f123fdafa0d4bf792884c8661ed552ae60a208256ef81e320f30f01abc8a6a4d1f52d3bf71989043f1af336a69c012e470d7ccf31199605d37f02f431adbd4affacbb850c91cfc5c4247313a485b442855d1a261382285cf7bd01b2174cb85b3a2c51c0e79b19d6d6e780c6ccea89c12bbed8985519d748d4b901da626ffc87ebdf9926778916c2376384947d17d716102e9f3511553d5535660c321c856e1af0921a63600ceb305269323e1253c8f4d85e5154f108ed5eb17107e16dceab6076124c147cb086889dae76c32de22fc64076479e351dd41127960bd9f4dc6a8d90b535b62b9e19848d8fa0271ed135cdddd92c61b95b6cfc606c07345a741c81a4c624d733a16bbd7755db897f6b0077d37134e79677b7f070b10fee29dccc419428b111466606dc2531a8b44ab2ba764c8596083831898a8e0a011f05467b2b1270fee2e3d41aedb2e64bec341cf0412f3dae01bbc9f6b42f9b07cad4656909435a4e169ad0359fda84608b892e2cf4ebc1319a40ee1e86cea4078d46bd04bc2c49f911d7cabd6a350b4ace09b94d8e765288804b28d11bdaad0d33beec4911ef14723647b5d2dc2b4267f503e8a3afc48114b6cf598d3df5f00d6bc7846d595d7e91af3c64b952b2156e7e145b1c41e3853d710532662b90430cd5ae5d9a4d223c89722d039d9bb38488b384d53c8720d320192f816cc902140044dc353e14ac6b47f6d291389110e1524ffb039fbbc380b603aa160c5cfb3106c9802f0a6843f0e8a89334a3272888220b8ba11bd256eb43b4427a813845740f3c5d3daf5b2a7acc177c5b581ae3ce16a22931d20d2ace1b83352b5b7492c604e4b8e0fc59c0e9adb902bceee5b80d5e62be18920d3b3aba79eff5208c506a0ee42ebe81e1b96bea75108347257ac00236bc1335e704135a5e80320a7eeccf0c172e179984ad88f21061368bb3ea1fe990e7e5a800c947e6597f537adb998c0776de6ac0125f26b8d734103c3378bc6ae700f722367d7491f14149977eec6b01adc345992a90373ae29c01f04d8ba65c417c02f589f5b7768622a8a2204200047be06133bd5e3a60479ee54d4d418b326d25c3e593f842530bfd99fee03ee30866294b7a4ad097df4ef1cdc7224d8eb71eb87d98d451a62f96448e58a8d7b8099edec5b090717d814f8a8b7d11456f338cf053fef139a28237675c0336c979099bbb0dfbaa561e054f184d0d51a45a50a7558e8134e3b78ba8a1083f7328de0dcc9039278f318528af3675737efda4248a1a30cc55ae1bb08d6d2e29a1a2a703 true +check_ring_signature b24f36ed044c869b4fec65fa63a1dac8866165e99e4ccad7bf5664a72aeeeadd f3142cb8ea1508edf91052da80a8c09d530a8bd8c9dfe8606505cd95cd0179b0 4 aff78ff38c12f8e537018496c7cce098cefaf3c4e0bd87ea0d0e3f11477836d8 1a107c7296fe1a267b824ce9d084849a81bc591f6076a69f0a46e6ce2fd20428 8ba221ebf736904c55dc61cc329e14abdab6a2af959ef4daa31dd27cb2499193 6c20450bbc4266a0e94f9daba3dd664a753f441745ecf05e9f154d7b1af1c58e f17f352bac691289b949c4f1b1e34d9154cea2ca64cd93534d490aa2a26e8d0ccd1b88610f4378ac9d9d548e7ce562ec7ee2baca77cd24f1b72ec3349c10ad0308c7c221a88eb2a84d19ce69fddd1c4f5f90bfc012519b83a10b85f403609289b9c25a9225fd0e80724d2d6e4a5125b90878b049362d03af56942eb08af1650ce864e550029ad3764b225d57f8bea7b760aa9052b8a0187ec0086630d649ed07ff528bfba76ab7e849e5b2b9e07ccb1fa9a04e9b09ce18189b65efee609120a1fc482e4048e206d5369556a388b77ccf1d7acbb5af23a567a2716e9befd5e00c40904c1de71b3fa351f83dde3c4820daaa735700370361df8c8f86def2ba320b false +check_ring_signature a7dc8aca110119ba99526d7812f8dfde07bd8065b6a5f25aca966be5919e2ea7 9ff9309490989a541070301b1de31ae8c4fa898bc3a2a0f74e7022dd3bb198a1 159 54f69537ca185ca19ff705972d5f3994e6c1a3f4bb0eb433ceba73c382bf71f2 01c6afe36c0d53ca44e7e45d706ed6144e37b769d5db05441bbd7ac1aaebc3d0 c24bd0ce00c228debe3fa3f0d588127a7dbabb6c3ceffd685cb214950cdda9e2 255ef4de2f6cf85ee2b9d25d046aa85a5c9229dd1cb3be95774aaeeddaad487c ae5a9095ff476f3551621548082d3b43b781cf601d03d3e1101928702106c533 c61ec7b432cb6d4a7f02a8de4f6894fc1653569d1ae7ab3b23695cf5f99d6ce4 6b135e7fa3a0638d0bb3ef34d9334d0b3799074a87afcc586518973be2bdd122 891df5e67f71cbd7bff42e8795a1c034d4d2ea5dd79c86bd37b912ac2e92a00d dcb77b6fba3c30717930956351d721bfa6af792f49cd19fc23b333d9e51b89fd 3f26933d62dbd3419d24e10417a77b49f77602aa11ee977be3fc8ae5065f2ac6 384e02ce44b7b8420ee6c2cb2e630ae99170f4b6d9a1c59b18e5b281f788206c 6c2eb6bbf22aef7846fa16eccb9513e5e7da671d371aacc53722f762e986e328 bcc1e5ec5ddc00512b1c36cb92c951ffca5b843ed928cdf11f1065e1ea28d641 72f1dce13f50b37abfcf46daa736e3a13630f058da89b9a4b27c75ac7f6180af 462a13d8e1e4fe952e6ed3e634aaba3c5db0f4d2a99fa5b35daa2ea874dbfdcf e27a6344cede9f8d72579f5e6d09fdbb1fdc638d8b8bda33f15e89aeaad9ab8c 22ba6e732f7e4b0ca9f7efecc156e9c8ebd077f833ac8bfee15705578a8e2c82 ef4442f2f4cd42ba0b8aebf4eb681dc1b57552e876894ecc21d52e9a08e05629 93b1767fccc7454afb06e35816315c4826b9e49f9490eae370e6a3c5fc7fa44a fbe016813692db1fadba27a45b9ea094106599512d07a02c809865399d4d1fba 7df2895cbfc8373c38a10ad859700f1de576f09e5bcf186833e5957a791f22ea 5f724a4abd972523d26174e2a4e4392dcf2308339403d8c387a3b484eea76b5c 6ff7e9b461caf0a73814b26445f8416b1229c24e69aaa5ee5b4189c4500a73ee 3e241be2b76d1758832a6abe5c87543291585bc8e04ae4648ce0e33780f5eebe 399d9cb6188b2b02c965d9a073aa7e9ac7d1cfc5ab04f621a5f7e3b42e88faab 4458aefb3539bf626a7d4070638719df777f26961e5604c9d469b92fcb215858 fd5a4d4f10fdf8d501c7778f6b8b91c4b64f355e6af2c45f0f21611a400b3c56 b89681d87827e1f6535d56bd4e324068d75d90b76b7f952362c0b3e441f6837b 786fc707c02e5f934b2467e73e2f58f913dde0049e10400b741620e0d23f8094 5088c86d995051e0c2879b9d506181b2554bb8f8383a46165c8c650e781b1ee2 48facb43ec97dc23fb158b77b1b4075211f9b999e8ec61365b50c7ca40e2d608 6d5d0b9553614dbf5ac903c30d18f507a3fd5aa4a07134152f1a80ad9aceb232 3e0010b1211fbc71dc56457d45fb7ea1db3bea1bc16633190da6f2d175c80768 03e8481526c801ffbe2f019849145127bd0dcc4952361f753740cef5da87e7c8 9c865f69bbeb593fd72a0a9aaa6dae6be56dbf95b41494cae967920a97e50e8d e0cd277ec4d3ba9463116876e5736275604ac806b5898b3481c536e59813c94e 0188090260d2bb77bed7bc1c8665e962e10e00bbc112247214538232b4286b48 90cc24826318ffb9c5b8ba039739bf6e952b0e945a1f7ecc34c73a08b6a2db41 e1688ba3fe5b566ca74eed0e769469124a1c701c3ee3950b04811a268ebea201 552c19a1c835511ce1b39060f1268cd07e732275786eed68886e88b560caf5e8 922fe51d06a797a69187693f91fdd4cc349854bd52ab310863dbe551c2ebeb16 1655436b321b61c0e63d8e7834791a69f401000346a1471dfd114e231c4a47ea 10041d6265cf3872b5e3af601e87f9bbf8760d77ee5f48a5dd30d8036d83ed4a 228e1cc0feb4dc89e61c3899ca9a075229bb9922e8843f2baf42960f701ecf79 c28de8c946bc0e1dd38caf4239c5ece763044a4485c2317ba9c7398a4999b6e5 40b5fa5bb23aec5255092dddc221ce078ff5b2fdea6cf1430aa2bc7b0a79f7f8 b8937311c375a90711f3098e18200dba441b667fc05b8cb95f817e5915baa792 6ba2a0dc62bddba2c860c5bfd62dbfdcaf9f6465bf2f57c2e2b0615882e0c5fe 1fb6b82d2e212aa59409442e1c6a0805def80f2b29dc3e985fd44ae4c9c60ac6 4a13def871989049297536d821240b7a2da390ddeba61c433f7a184202ff101d fd0111b3935b0a6f59ec4d8d475f5b45c6c2962a9d8574d0b151544ca8975023 961a04611ba55b511f46a134a8da3d07ae005fa8777056e7ff54656408f002dc 6ceb10714aa6efef465c6bfcfb54e9356fef84dcaf8d32da075c1c5f8c34f0c6 3856497f7e825e960a0300a87ea7f3ddad0dfe3833a1276c9f0bb6827dcd45c8 ae8c95b7e03762c0a353ce9ad8b8ee010f668cf487e2e34f7db2da2b4461be88 a8fafa5f4c2e1e01d10fc2d5ec779afb119d4bb7886ac6500bc4a135e7bbb0ee 47629949913f145eaf5002d408187d22373247167dfefd25ecb9c6174df52999 b889b88a2afbba3076aab038e66c869b0d4f3010e2d1033131ef38f1885ec5a0 477eaf6eed670a3251b178253f59edf8a39f55f50f6ebd15306c0cc2fb226025 858f86e1ba0dcd95c895add1313ffc86e0d2749469af83c7214007d7ddb88426 71912a7eaff673d3a168b7ea273049796c73b6fe92924114609517c2e63d1d56 21f404bf42f711fa2f35f950a155d0117f05f32de57af2d1d2b7a36fa96cb7ab 4b3e046d455e080ce66b172f4d44966b66fa339abdebd1e3f2b1fdeb5c737184 c44425e0c010950b8195ba0b22b3264013c8bbe9fba625cb302c76794668e82f 1a1e570e388402cb3aa4446d03c23c40849fcfce821a77f3152d03b4f88f655f b3163bcb7b374fd4ec6f512b2fa0f91a01b89764c6e7dcd77a8b097d1889ee09 1b144bb1311c97ce23fbec05c4623aaa62e37f37aa501342efae70a49a521834 a72a3fc0e4c5429ecbae64cb6576605599bc4b0bfdb7c6b30f2ecac3b2bcbd48 7e0411ca6eb6bb85e09d0577a23fd514b7a74c56a06150f5266775c1ca77ceed c667225ff16ec5a220b491641f05916eda01313ea7a96475ef26426b994f5dbb c6a1532b9b28da7cb6ad76270f7eec4df584bdd0c64bbb02055ccb75807a7f28 97d62b2efdb0a48cea8eca5aae13212e54953c6dd3dd110cc0f9086191de8c45 6254cde22570ab64bf76acc2d373cb7fc85d009bf65829d49e508bde7dee6ead c05a757e88e3cbb74ce56196314885ce08479501f8153f0045f42eba67f0ba52 e297eb5b3801e9c5a815cd1b8f78a3551f120d63b71a632d3184787b15e27f64 f078b1f70acfa3e31f4e63a3ff739984885598c675ed56f0a951490a787096f8 06dac553550a446830359fb2c6a59d3660c374057f86fd19fccdecda2ff29a29 d334b4b34e7f4afdbcbaa88ff49b23e2c47bccf1eb826680eb3b1e7dece28bf0 c8d3a52d9a7d281b1be7c7365ff16be31fc7c02948879a6adc716e04b3bdc2c2 9ca6096c2f5ca1d8a353e9e48640bc56884600df94ca8cf132a77605b3d2f5dd e36471b6659f01c4a082890a156c6a4cb088ad9ba2dfc36ce092af42d43135b0 7bcde7e83c908fbf3e2674d44101c59232d17a7e26993fcd7868a52f168cd7dc f2e898b7d4caa6717648170116da23360f1e02c5549af279fa719f3a6c223782 5c60c987c39a4de8d6a13575550be5956509d71a3062562f49a1160cb890547e 83117f9397827abea4b9bd0baa67ab10007bb07aef024c6e8d5faed5eb68cebb 6c19743dd47f9fa59192e0753945aab32e9389e02e92a790d927446acdaf993f 889870184f8eb4d7f2c432756fc1e44ddee436f29e2451f622be2bb4e4d3c20a 8214f3b764c40b2f7b4fb302fe8780b2433de94ef02e8b4c8a70846931d99d67 9e6784152f2563cedb3aa0886a3dd45cba3b95d969b3c2c84aea638abd6f0c22 2ee6cb79e5edbd12d7666e97f15529e45056027f7281559ecd56c53ee5ae350b 15b3230610acea1843e3c80014bba4aa8913827c8f5b351cc16f4682bd470614 bfdfea4883cb5a4c5cc5ef9617d1b748ec044036282fe7f8c002a4988b12b5fe db5f3c65f0f7e0fdaa6418e32534da77082bf2804d1a6e9079c55e06e80336a5 a254dddbbf21468d71e36aff180be1d520f236d44aa65b7b28fc27fb005a9a35 710bf0815f97020d7c15c5f73b889c46d089b3a534e893d79632bf05f99c4bc7 26f458bf4ac25a4ad8b73021645faa04c89ff9f553963d202cc4fe1ce5c9235e 28b7dbecd3e1090a0b22600dd31e10353cdae923caa8b137bd179dd10ac7b7f5 7eb87d1afaa1af3fcf82d210a677765eea5c2650908d58a807192f2549f0b104 6e3e0eb232b2b9e45463626dc19f934966011d6de81573fda5ed89021204fad0 4b52a7dd78cb42b168259baf4d6a9203cb7f7eeb3e14519e16b36b49efbd48f8 51d4a7f64cb023ef8c7ddb0544622b3beea652fae93ad3ec356f362a266a6b12 def1ee482bf201892341f92b50a5767164eb2372c587cb3b06c05e69cfea9501 b1a1d78a721983ba2c9a799e470c541b8120d95b8b5d9cacb4f9ccc354634426 b69a83b1f3c172534a09dc2710b41b8d9ef830953791406bb8e44f604cad6b5c 6e252ec56582063b48e224505d1bd36431a3b76910e0b761cbe51306744f0a69 13001ce2987406d39b2d46110c80c702186a8c2d661dd7a3764e2a408a641a36 ffd48b4415f1ebdc3a5baf700c2b0cae77d601e32bc447648580df0a1287e0d2 8b1e2d0ce6137bf23b770f9b53221eb1d5f32db65f5031dd2e6963f2ef79c451 ef4c5002a883b6d709525cc41f3187b45dc3b5e12f749f99818a4a8fe3724381 17e6522217ed3e406af5059d5ea6bd68082e9f274c6dfb7e39283470527ef9b9 cc3454563b9db19b53e54aa6cb8981659939d3b4b81ff7da25b2d1d4d47596b3 d648886ac4c8e1032930e4e132e1744dff71972f7357b10bb1ff05bcebafc577 ccd15cbbb58c71e4f5dd7d390a0eae266586f57247da3e055f7c9c2dafb74520 a2369ff3d680cb507579cbb9c4f052c5a91cc69fe205e8ad95f76d85568c9dd0 cbe847bd9cacd89bf117b8418eac5d0b83a769eb837534d303f0275c6e6dfbe3 73f3e16e9ae422a6853a35091ad760072b611b7ea9e24156938c7b025b3871d2 7bf956170b16672192f1fac689a9fc7e4318f21306248ea3b225261f8fb4f353 76934fac30e9f730589b5e7770b0e0401ffa807ba9b4e1b7785be24bd2959ba0 e2c78c7ad30b80e44874b957f2a5bc588869519626bc913b7770d21bb1b43492 7de19f13dd6fa74ab29cd47c43f469d5d7ee8c945708c3fb5db59f66019d182e 871868b54076eceb5a7227c247a88202e218840011ce2908dadcc6f3b2da27dd 4fc6ad6ea03d83d66b3d76f323d719937a6d3e02b8de861930f77427e86a5e24 a76973d41bc8c715a266efec97bacbb2bc5d207183c2690bc560ba0987590e20 628ebdc3221469d08d1091cfac6ef8d0cc8cb5997b62edde693000cf9b4d10a2 78a5a57d5988d154dd58249219a3ba895628be30b0f264a5858248ac23dd3090 9994bd09ccb14436a8f145ddadd2dfc3979e84826b15264be89ce2271996df5a e8191fba662a382164aaeecbce56dbb77036d5b69731504524ef488c0ad55958 100a0b11402eeff2765ab234c070b1b229465c943d98748dc4066adf65b06a8a 425f6e515719c65041c53228bf4741863a7e77809b86f0dacede5b12596c8efb 0ce5b7a7b10da321f4ba7e2ec05f1db88e8e60ce1b8fc558fbe1e9fa9f95bdf6 6b1e410226d40ccaa0fe515207be4cb9949b0015de95cf570fd82b3176f48166 26d0313aef43628c296ed6bd16869799989c7bf6678a1d09de78a2e8e9a6fde5 dc24df9a6ef1ded46d76b8f78281d028a32aa0a1d9b51f80ba3218d191e79f6e 24667fab3545d3c1cb4bf203b4bdac372ced25a7202b53ba96d60c02c4dfcb04 812790db49a9d2639afd993f54f5707d750ec3788af78eebe89b25b70cc41b31 c5c2eb77def024287f56751910bf919327c4e0a1f75cfde171137c64cb1b90f2 f3904cf37b6b6136087411db795e9ba115db6e26bc055c8f3e0c753a5d4885c6 078b72c38a7ef2ab68c2c26e39472481d5e46fab7cecb056c88c16b352db4626 16cd4419860fa53d8d9183de471fb57830e780d1825245a7a4c2c4d2386b604e da80e4330a75f58930019828cfc848bbf769eae065d2b1caede41a355f7bcb8a db3946493acfd997eab95f4f407d21b740cce0b14903d8e1a3e151dc5d7abcb3 43451b52a9dcea4d4a3abba2edc59adc9697f0ee0c5229ef7ab88f1187848a1e ebaf215e90a68d312476780b7258d9b660271982384dfd82582d7a40b5a1b28e eac6f041a11aabadf27831f40e5e2b8c9519d62821fe34218ebf7bf36f1308ff 1fbbf5bcac4ee66d514162f1ddee64369f17a4d88a4ccde72eb232e1c6301400 1be664f1f0e9a174c2111bbe8ed69cbb32ddfe45140d9f428da1b34c3c09fd57 dd9640763c8b836c6490c74c0ee86d1fb6da798c49eb9d76c074d934b5c652c3 4e4b9335db17f4998278ab973fc454030473abaec56936f7326517eb995a51af 1dfbbefc9a7b37e101f47b8db031b0b4096f707df75a9af509afd5918304839b f13a61162b533a2dbc03d3281355fc9b2d42409c973ca35041a8dbbedf0f445c 7e729711b730c4e59ed2f3227970c8dbaaf883195d538cdcb56534d1079be057 5e97884c29f0c4817c793ff09e0afff14acbc630feb9e0e0997660bb610d0595 f8a5fae45ade92184f49b82f92b4b8d7d3d9ccfdd96c1fa84ab210d81351fc42 6dc776f3eb5ac0191f74011e58b13a57d505335b3ff42e99f02a0ed70fefc7a7 628f23f706d5b918ac6f3e6a25702ea24b8f43d4944515e7a3c1311e4dd0b394 6b7bad100071427f683cf0f64344ee959b9642716921d64befee122e65262bf9 4e3993c24602888b92bd6e97337d4db5ad05f8de5e1c67341954edbeed9dd068 9e1c5c6078ef06d92bb4e4c066335457fc09bb8fd87dcfdc39928c1b6b632326 c96bc8039394483212bcb3ecf67ead7456bab5ec9ef4341192a73e834891f12b 5c8d7df7907045905854d7b470eccb7a264150dcb8217a8f79bb592786f7640c2b2c47908e8c45ddc88d533336cbc966fc20538bc62fd1209165d3f7cbf83006229cafe83309b3f22f81208516894b80a32bc20f64e90700a237331a601a42080b6d82e25f885ad8dd329021b0d2857997dc83ec05ea7687df0d5f95ec6eb30112658ebb372e05c5137f07ccc34011918396475f7a74b073f16803c77ead6d04500a13439931069d60c7ad333bf9482d3b687eb4442a3127b2713ef802d57f05d35a75583def084a4000ec1868fe9ece2187258308b5a836810a429f7a2220048b38fd34d135d72e5b3df8c186f28892ddc691857988081f38770669f32e5a0e3a3e71a40748bf6ae66e5231b95aec3d3b7a76e20add460e1fe530a369555f0af76f08d1ee4550d4d4a34cbaecc46dcc6e978c8acb82620166c78bd90b655d0b78cbf28c426eff5e2f6167c9ece67595128461e7fbd23e8338aa8693a7950a0fd6f88e50eaea40da9c726c025d546539d460c2101322cada7504c4390edba101f81ff47668047d0f6f60f8d2e7b77d78e9e4eb6c971a178f5e23855350eed2062be020c1725d838cf4af2261ed8b1077da6e7deb375c2ed8da8fb9863de98b029c770f697771c52d8b618cfedad3be4fc2da38552fe61c68fb716824f4588702dff67dac85569c72d07cf45ecc0675801fc195e0ff22cacd68779e3580c405066e1f1b4fba39c5043f520c34b41b1bc2008c835afcdbddbd26e3dc18a7fccc07f611137be0ad85a19e1c4adf09ae54130027c2c0590c3fb86ab06ef74b27160c7955eb6f6425e136b6033e395ff29b749303735fb32604779a0b167a704fd70b8f192def723a963e147a0a8dab0b8c0ed5c246dd9eb7ba12e51a4bdd69eab609c3be253a7d0328eba9580c6fb07fd4cb5dc975848994df050b282f729e029400349883726e3dce909357d3e4f5d848fc9d4f97942c7c6b0ac8e679c37a87620487de28f8c63284052bf3201031c3347d4307b1a4e3e3397e1ccf4c863d737b0bbafe9792b75f4b15d0c2b633b13be53934f48577d477b78ca8cfa9f4fa1c3e08b06952ed637fc295dcf5858dae7662434e2eb9477004fe4885837bfe78a6b30af275ebf84ef26e89e9129f0b8f57e53061528ee638d1cbfe7927dcb132bffe0a2e32e5056f805f85753275ce8f1031d9838616f18d09a6ed56ea8a676445b40d6b7fdbd50bad9e58b0267085f431cd0634f0749c1a7f19d25c5a5b64f0659d0d2be905f226150f588c5fd686d0fe28dba1e202c7be6fea3ab4d16fbb4f67e30a8c3469b8dbe34fb1b7f988ca2c7f73e9a3476cb2c80653979bfbf682abb14a018154ffdb598ca6546011004d4daeb25446fe48999aee7c8384614cf74fd47c07addb172659745001a6a3b5f4649d14b8f82da3e38b117bf3757ed3a0ffaf6a04e67460c67fd16275185101dffb4cdf7f789729850f34b46641c7c72ea5657109fe9fddc540c9b11459200003a4502ee9b0e49e870102e3774d9e87999154570071ee80233c5a44d16ed6e121cf880f3e9b3c48bd7ad6dc1268a4815d2744b7031c5eea0d443a2c89168e6e7f641e9c01e4c9efccc00b327f40fb22435aa22d05d430f051e0a97359ac057967569fa687485b31887bf8a5b22ae45a34e981eb08c7aaabd17b1b0f2c8183e8345a7aa1c088f7a8b21939cc3433e91534aebc490940102ca813938ff112fd7b35e966b9426f8662b555ef4a407b8e0c5072b06a0ae9119063d4ad7cf6ba07687bcae42f06026e3c8102accac87c04bf33fc2e4d0a75fda46deb26e76a22770dbb8a6981ef5586289b96c84009ec75db6be8b1f80fee4e9cecc95f65ae80b1d1f1381d4c2f74ef553312054d4dbfeff9101b92850ad9c5ba8b6165afd3ef9e5a0fbce0a06f94e2e14dffd2e55dc9584cbfef0db10846f21c3cd87984f9706acff88782bf21a1f0c7f7296b8870a20af6d80b38f40d4b7288d14c1ccf329bf4d566a8d29858df10182d37797f284e567cdeb18a850b27c575319d64149aee317b4c6ebfd5a55445449d0f39a63df5174a42aac72806caa4edd4bb3932d5233e4a93b1c54d2fbbbb7e45016c993e4261d2d4e142f404229d5d1cb5a8a997e95e06ab0ba8dd996edf4b97d5ade8b06d99f6d4f4fd8302cbc9f1d482858b1c0e280ad667eb0cf27bf067cafc0ce7262c0d9e79de100b055059d79a8f387f8508fcca994e6200da745e26fa725ef9c3b41db19385ae1c01fc3acd1c5033c3d86b1a7662968be4caad6fcbd008bc13f4dbf22d8c03222e0449a6b24c8b37948a92e2b6afdaf2a9ca19754c0910d73073da29f775aa9e6f05729609403c5fcfafc378528ca7387e7bc0430b8efd8688f978e787468b90910bee946a491a1df04d582685fff94fd766e9fb91811b2fc8521d604edb4d7bae02f69944546a0a0de2a96f0ddcdc2f43ff36a51e71850976baf5c54209d480d60964fee32948730f8ae6a1befdc4ed4f0547ea29f0e09593356b4d3b176a56e004174b67d63ab2e75a67289d3db8040cebb9419d02fe656c607b77f0fba15f1c0e4eda2cfdf426f987c3984820a5e8ab3daf111f78706a58d1d0c62a0cd5502b03005cffab4515df6c1f75ec9cec16880988da7ac7b9bba70a61fa121ecd96840c9da169db111d34f47ceb06d179c2fb1e1b5d048b4772d49cd2a42793c9409a0f7693d3126a641345e1d376f9208d794d3dd40251a62ee256f446240a7ac0f30e5befcea814b8e8e39a129893252adf23ca25728d9191acca872f9808a4f47b0e29ec95ff69423393c2cfa8f22138c3803f09e6443fb4c5e28597007f9f9df504be3404f9b801ee3a0ddec00fe1b9bb61f2c4c5c0e54d2c6d182901d86182bf040bf95473dd1da33de1b17553a274400d80386145530edf6f426105337040770193a22df53657b0166d33b2c430386532a626bb8dde49f8804d3d57093d03aa0cd2c04b6f1c76b8ffd7801c7f51b1f07cbbb66efd2b2a7cd9ebd78080dd982408d963646ea4396702aea3fe7aa9639026a6d2e3e81e53b76f7b986641ace3e50b30dd4f1c08a2fde26c14d92a0c1bbd557456569f7a7c7a33b2c201e8c379b1076acfb80f8c28444fa2e2b2ac1ccff2824e1a4202d835395f55e215cee3517f03371eeab16cf54c16e6d26ff07074f8e11b71173c1f9a644e4f94aa0b5a99160788f73fe751951b62f6dd64030b81c501d59760e819ca99b978721cb0dceea001460d6a8b139def47669a8995cb9a3122ee634c564c641399e07615335268db022e8ecef32be9ffdf8618d573d0d0d6e69b35ac687fe8fe90cfa7444672fb6b0a26a3b2b9d3b56137a57012f36516d3dea0a7a9f302e6571b09bec01b9ed37e0243613c007c25cace4137a369b50876266b8c21c5ab8a2962741001a75cd48e05e6efe57ff72fb7db7a98d93a53b293d6e391dbfc51a45bc4a35de5edc6d7bf0a3a4b1fdc29e858771881f7dcbc35adffc68960a4e153fbbcf80f21403cbb7e0a2772372070b1a83a3776d1d257cc4ca51d1a092141567bb28056faf424583e0f23278964719d1ba9d0ad38ae0f6464e87436f99d2ab6ad7b37148e48f816590339243d34c6e555f4f8ea0b35b5e8e84998a92d692409aae34f8ea504e263f60e7d494bae180fc038cf7b2bd5f8bacdc27ddfa6cc057bcdf5017be27fd712220568805625fdedd19778683ae4be5e9860b4039963a88877d873e39dcd9aceb90aeeac84b5dc345c232559ebd493208a4ace62d4e2ee8095418e082ea01cd4e108451b8e6dfd9057d69aaff7caa98e90e9f73bde447437d7fae7aaeda5cfdc030ce2971687ab7b36e5dec0d552983ef77ae2b87bfbff30a86ad3ca95a1a761b405d515a9594e5bbb39677cd185cb45b627572cfac28e4533516c27063e77e322026fc7815dc3f5c714a7426a25f2738f23d947790ce54d8f24aefc52308c76b908741998f28b1bd331b1a8dc8ed63e9c3d89407d9fd1d84df0d1c0b20d344dd409dc1e53a5b03f64047fe1be4b1ecd40035aac9b0a3a6263c32568d15458bfeb0ebc794106862c71d38567e3812b66594359a3efa8a90f14b33738fe92e14a920e945220c326796ec83ef9887c5dd3da675e4ee631c12db6635f8839d2ce1d5304f0687180f9d15814dd3736d38a3f5439a10282ce9ec3a5a3a5876da9be2ab20f276858a079b3a4ba9e97998a730f99185f55987828d822f64bfc13ff37cadb052b177ba6ac43898726dd7760ef24d0a69819a9f3303680f23c199264648f70036ed0c8d7f446b76518a8bbe3fe16e8f404b3064640ada848ee17ebf3e678db0602be42053869265f9d694904b66943cb93a859e412ddbccd4d030c6302b67307a967d0f0b55d2aebc066a099ae15e6fdc587a3d7a8fa8e4d505a5967b3dcf906076866f587251ad427c073ef815242b022d5113ac48a9a31517963ba66a8270314da1f7355e4e03a6aa8765ed2984dca5b77d6c9e9d30da68109f06e25c8fa0dccc5d7e84a6ee58a1a497e5af43bf7ffd123853a92017177a1389e27b36cad0716a20896acee411bca74b8b4d78dd2bac8d52de723ccb5dec891c216af900001cc4025a0f623b5bd749c2b923e7d569b795d8f18476efd38c3b768a03985500a2427a130ce4769037fe3d919f795ee054f3153430c2fcf86ac9d24600f323f02c56780e0b2eaeeadf88adc2d01d81d0ca06a9098c72758b0591025ceb7f9c2044c5b315ad9b6f63fcb5f9647cd81040cad013d923d89d63488d7719d7f7a9505bb1dc19e7ed290607a4a13c18b2f3aecae263c1443c6bcb2350d0154a31d6d0790a07fd0f821e4e5522841badf8bb458713293638b0d915205b0d62cefc7190e18eb9abb4fd5b131a93f42f9bf35dc5459edd8857f7d1b1480245cd1908ea600cee0817d72b071a8f3547bc23068967554b6b3c560aca652b49e7348156709074255426ecd28a6be92867efb788b8463dde1fc0b70287c88fed4f6a79f83610f9f116b7c8edf7dce60fbf1ab5aec2f47d002f4bfda7a4ebebdc69b37583efa0e9e0e87bb434b6e61e74e5de7bcf615d026f5d2a9f75acb072c62aaeee5123b0a6b7c13783544fb9622836edb4091fbda52ea7e5d18fbc159c9904b8cd0e5b90dba48450a60547ff3dc855cac637a0d123f6b037cec9280f03c3fc2b14c8dfe092696a5d557aad41d1fcd15e1cdcfcf2cbd0b37a99cea943dab2a3c4dd84b2303cceab3e0f29d4763274db976895012a3b6cbec0c97c6a6b0c20d10749a149f038b3387ad5353e7cc685542c673296005b57808ce89232176283707e3836af509a27d6a14d82c055f59d3233974a835ad3573baddfb92629b5e29339e80ea5c0d3ce4abc31597ab0f43f4af42d8fb60ab31441fb3ea80ab1858024698ea4017064651afcdeff5e987fc7dbe95f67c1f451342b9c4f62703643a442a5466173304151c2e27278136f1d835595afc6fbcdc5a94992861a7ace8e94704a80a2395073fc359b20d1fe20b88dfa8af655c16de50482483955924bb289abd0f0fae92061efa46a39176357e999ba811473be1ca5bd7a5134b854e5edbeecd6620259003e400f4decdabf22dd665813bc579a196991179d580251f0f43f1060fc735eb02a023b972033d5ab5da575a68e1219a09c3c2590df98b9a6085bf4f37b86ebb01ac24306356e7521ac2cdf31d985f95163284a071c107b730c74a8d24f551d50265ea8e1f6f12589a0603a03144349444d81868c97be9d8cff089179784414f0c9a50260f8beb9a6dbb5fef0d0ce8b536db26152823a5cf9ca102c37ea977170e77c979f9dc30229b25217fc52e6beff4581b358d81b15999b9d413fc29cb2200096ad7b99131cbd8f86c6f6178c260b9275e05d777710a3a22eab30a14a4de0156dbf043b585244c61e98a09b6f45ecfdec089589c2d75ef902c084e56809c0e53fac977690ab879c6335682c89fa96684ee105123561744bdd80dfa3c136d055775c259867f7ab374b93f4a3dda79a144839d8a8da803ec2b144c606f21910f7a61046809dcdd455691ceb25862255d1ffdbd0a21fe28df576efa297a2fb006aa4f4d51aee27a40d3b7c13e13d921622aebce7e54153a152732958bd84e0a0e9f8f64e561c58c6edfef0aafbdc2b44142f244bba2dbb2fde51e0e8b1f068907fa6a95906fdd6d696fe3b29adbd80ba8d886b5f5881b871507c0df0e94f5ec076a7958c479cffac54d493ba2709b3a0465ec38a7b39deed5611044e5690ce60b959ab4262c7a90589a14cf71a113a7a0a669a79d55ceb28ea5e75f5fe7dbbe0b4a7a33f8d386860c25712480b1fd4f7e6b192c0ff90d76eeb41f9da1ab141a0bb51d4d19deb57cbd7c70084a084c48ffadf5632c130fa03bad3004d4a0fa89093e92e7c68f42e223b7d70db47f23b11d550776536c654968c4caf9377d9fb40f517082064415a5dbffc4bbfdd9fbdd744d175384728fba27251cb3d667b7c100c09e220c305792808688bf2a68c46222437600e9f734237f525c5cb9b51df308d69c6cf8f65c86e24f6ef105d8a573c4eff646d930ef084625ae8dc42159ae0fc10bbf4c70d448329300e2f3d46d6a15e65dc893ce385ca815875045ff47db0231e93bf71bd47bd2764fdf6c23861d9e3d685fface7637835b74cbf85cff1709c17f6ce05b46cb428f92601e6dd691fdbb1cd61c50fc5d04da8e30b039055c03f5cb646b5471754e6771fea2a7ee228ddd0963c61ca11e4e1a9c5aef4c27bb03f5e5e2c1ed0255f729f9d7500a9778c5993c5f6e17133da94c2b38d78985870482f9608672ebe6457fabb1019ab6779eb314e701cd0795942084c2ca3a35fa01ba0042c50e109f835a28e549d453924209344859b39af5cfea469f3894563905d6862dfe760ec2ba77149de92589ec9552b11281751117ba9a70b6cb24369d0a9315e910945f20c85edaa849266c81edf868f86955fbe6bc05b378e3b2fef40e2f0270fc2c585eb070a95d56f902312cee02b4539b33976eb0d1d3d8712eab03488d02c2faf0d0bf48d649115466526e5c8fe8adf211ed6e930276572f79dc02bb5d2047426db9ac4bd43619f194fa59bd0b9879af45e9382cb4e36de3993505738cd48b23c5a2bef4146f1c50e2a9f1c433aef15e9c069e122896478a02640a9554fd42e16aa657123d8e3b5bc58f646dcb828dbc38b706361764ba0a8c750a005dd61e4293a95e12e3f74618e50a32486d62901150a6730509295a2d0c97071b9d3156ef19985d2955e7ff09afe485a32433b4f77523552f2cabe1d35caa00f0898128dce1d27ad28989b5d9151fcc701eca9b84e09d7844cecd6369eef603531d7f15baabd5ff42f30b3b1a7860d77f4b7876a9b9f49cfc1dbfce4a22b902e8a87d916643a804f828aea8d7e061c43edb1c2235cb0ae0c3d317f1eaaabd0970e9ba47533a3d06c03cab45fd47a46a848af34d643dac879cbf791a74d91c0f38e69508409934cc9c3b30953714ec524970745154763645a7c1a1cf44836700898f96a8660a8eeb883d33d081a2165364aec829e99e037f0107ac1fd3bc6a0f6103da879015cd037df7ff6645065964cff1173a9eabffb71146b366ca3e3905c42f9958f0353e88be866feccf5b07a5ae30d8e8c014fd5469a1937a2e87f40179242a341308603e2cac28969d11562f4dce7e6dba593d43d192248980ea1807e4bbec8f1072a5bc7354cfb8e88e9e3ea34b1e9cededf8314d72c0973c91fe018fe0fbe408007e1e70f693f0019d16641b36450ba7bc325352517e74f7ae0606b14f5033921a1ea558a688d68587ece0bcc29a2227bfcdfdd70542836fb40205075baeb8d671cfc538888f6d70417919698674cc800c86bb75f6de11dd1a25014e407e88d9d590cfd11df5ced0b37b7fe105e481d5ddfcbcf619cf9048884508a77f6665bfb000c3bc1df920e0b9716705ff078b4624fefbd9bbf5d6ab025e07425492ae2a006230eb1d4eac8340a4c7a79ac564984d223b26b853cb48491b0ef8baeba9175e97339a8d2d33f9db8651d847386851f1788393aa77d7a56f430c1a3b36e308e92c7b5e32a42492476d248ccef48589156ee74c8b448127d01d0607d6b9660f5a27a7ccbc57228d8ad3c297b54444f56d1611b40f70afcafdc40736321be574b45996f88fcd823beeabe812cd979410a714943ff0c52f9d769f00e4637acb41a48722f690fa09ded8de746213995f10aaa21c5a9e4adaf2b1d40ac70ed0c8d1477c48ffbd1f87641ca6e2e5771f157351b1bb468be07e524f640a6f7267f68bc69a27e7c57484746afd0afebaf8e31a71eca85063c414d1f5ff0956c4291c42c5769b8884cf74a0f221fca192ec9c2d695922278c8c026fa75f017af65fd205acd737b2fb7d0fefea00f45ff0586f4049b0aadc9906780218720b78ba1c2ba81039a6a98c8a21876e45f48f7c1a839ec529172a6753b6aaf43c0e2011fdfbd520fbe8913d541fcead350e1dae78c8872607a4a67b2664a737660ea5ca5dd2cbf6465734d19c716c99ebf461e7a0c74642e10d83310f947098210d172c416ef8761036f1a13088643026368eda9eeae01a8a663ead6429148bc0045f659ad66d39aecbbec3b35e2dcb3f1becc884fd0d5e026c6d886f91cc778a007140e8bc1c7bb4ba5d22e2c7662e462d2871339643b21005f04bb1a5ec9a0106daa9e6a6a5845f333778e4b3ddf56a78e160cf600c7e2a849dfebe18e5618800f7bfe23e3cc6e2f3b39fae522df31ec5cb09e53573bc6d52b8820171ca8add05d815b62276b0a9448feacfac131c99a7593185e7baf90e9d0916cf51395f610a6cbebecf9768ae6cc0042fb9950f08b4afad7966cd2d185dc042c0c1a84a840a636c1c86cafa1e3b4fee6dab99f02a0416609a46dee8678d71762f05603c7d0f8d6b6a37ce530fd182d0bbbbb8ec13d8361f7509add3e417f457656c2b7adb0f36464a97344883c6ac39a509edb2e2cf52d0d5121be77cdaea4872abf7fd000e0dcf01e19d39fe7a5970c635d151ffbad0c103ff750ba24c89305b641d178e098859e07575c43919ed4c2fe0f11e4947c39a3fde80e8751dc9cb5d843909960583fb346273c2f71248250c3ece40fe14ef13bca0d6df16846e007b0449c62b0c246324159b5471d6b1644af4ac6842dc6ca3ec7630a89dbbf2b7e53f40fc3b01d0c0cbf34bf6e4fcff20f57c50fab0c8f5ff498ac89b43f8a117ea1e49b256023ee4a46d40d7ae4339405d98c9c56db557a7a3a2a14578157eb4a9e41af92c074a7eb3cfc97830ddecd911c84b82650d8e8d3807502fdadebcf74813b2ce3e0413ad1bb42ebb79e425bd48ab3843d3f6bbf0aa5c27437e1568de0d63dbc8f0055c7e799d09498c61d4136e80bb991c57708a505315960aa7fe4da2f4c8833101bd63d87e63e9e12cda2aa2857fd3242ddc78a62665c1e17cb6637466bc31a304b886b4164018926814900bd558a404327450521849e21250d682bb9f9d49ff0f38d5026e8e9d32216829720193134d119c8584f5b778b8695f8c619669abb103cd83ae0ea29847b1dad73a2d018712f0623c08654906dc34dcab2fe61596c90b78e6adf22e26a4391bb24f04224862ac7ad03f1c43c8217307437beda1c8030203db31ff50b0411b6c25aa176e1b0536452261bfe65b14f51908ad09e83dcd03a9587ca72e988249936fc0f761d2f02d0eede7f2c1d5c3849288554a66f1e900063aacb6fdd8aabdb6043aafcd4a22d7e7f84ce12eb194bc686c4bf7071287089f8a102c1a221e6ddb1c124ccac63280c4b1500ae50f61727e26e88ef3e8f608a308644376075b389ba9bc79b6f9aa608d30205a555c3040bd2fa461c13644023aec307876984c0cd31b5e78134e7222194c14c8fa178f7acd7462a783a48b0e1b04907ddeb70902e955eeca1407a3325471f98d4e8f9cf41c13c57f6083d000d1ffc522c411c7ff1f9b317ca7a9fea12029afdc85e4c8eb35422aa8dbce340b25503f74cec837aae77a76c77c3ecd112daef20795ffc759fe83258f6c14c20b5a4b648d0c7e927987f64c87a76073bd82bb0439e7cfae0426956ab9c4dc5c0f7cc2c88b55f9ace971162dec808d68e56181c58308880b51ce3ab78d597a24088ad4f79e092f308d2637ac4363a3eb9b616de012c0a103782885ea82088ff80d841b1dda68c2055792b7fea598e37d5aec2c787fc61863dae1ef3be80edd7d05dc4712a3a7a36a7fc0ea13b9d43770b5d58bcb0c3fe8940ec1df8cfd6f05ff0fb8eb881f8dad3af24893b095a0b0d228ff9c422d918970a27ab5189de945170477acdf8bcf4f7f81701eee74f4c554d2b76f391080850be835baa71dd38c19095980ea3a77cd99b97b01dc57b05778161aa6fe35cb248840133c5e68fc49d10bc2bccf47395875b51f17ebca80da17fe7de36628027e9def47ec7539df8b300f25505f152feca82e6cc1698c797279e9383b43e3778838c2b16a40328718520f52796d9485051af4e593c32ab1ce7be07c839558af945138562d8d4ea399720abbecb174c0c34f74fea0595789b8b1cc35720f1dd5776fcbcf6a983acb18fd06c1c6bfea2b80a3cb2d988d2f258bec71cd084543c7b2927373108860e1ff0d075a5df2cfd0d7c92ee63264d07e61b90acab1b09917d5ae1e025ff73ce3cee205191689dda1c7d095e91db279c8380e3789a330cfa1db30ea815394549c2bb708c26edae0643f723c13f801914d675cb7c57d5819bc181d80cf9358c8d24f960c57bbd90986f7dfe1fb68cd73f8991d43f54ffaa5a9a3e84cc1c45c094c1eeb0e5c205942e1e42e0de4e527863a625781cc548675b33a45b9aed7ae47ebb5760236bd265b7eb810d7aacae4d2d921b6253a64b831abf6290f55eb6956ec166107df8a6a83390270b71e0d11f39a3933770d4925a57df1c953e7a29cd6f5ffc70032b8038cb0b1a4198b84a82a6e164b504212e4811f292ab0471ef450d13cc602fc2a14b0382d163227945470caf1d84236212b2bbce92acd6371fd0b0f704a00e44e3f73985fc8e3a880ff7126d16b7f7a8e3a2855caa0aee011d3d0a7a82503e344ebaf45d814f166024d6e98d52c83fe33c6113708c4dafb9993c6cd1c9a0d444234639feb10f765045b63e7995c2f9bea483b45a9b818627a602035c5e4011b6eed09737258c413abef1d91139342aced14a2db13fbaa2cfadeab5db7f50e4a1284bd82d8e397f5992972bc0e70fbe35ba8ed9f18b19a4cf2dbaf829c4f073a7487cb683931ed969b5c0d605d77b272a24fd8ac677a4c209a8f04c3ec9202075c35a48541ff95fc609d5a3b2aa28fd45cf4beaf95d688f2061dfc5401f30c554a70dd98ccca52b997c7541320727cb95ff05d5fe3b997142e831538b2540397bbce0cad208997cb6af461af5ddca4be3ad7db006e5be695916919081d5709f73b0fadae7999f8b1eae2bfa4d5a5898da157bcc1bc88ba16d3adb506321e02b9f5649233163d5c4abd5224cfd651aa9a24363c858299c3b8a41c6f6783c6084f8ec10f983d48d330ad5adef5a231092c002f14483e5b9ee7c081dbc694cf0d374d885a5b8703780c50e721fb7b2734fd3ff3d414b68bf868b92f01a22d7e0a7b9ed68e307ed391deb6aa38b4178bcd416e5bce5f425984fecea979341814083c71f8d26977adb5df19fb80144de4c4b9c019e18150415e1998004e8a25440d0de814651ff609d3d5e8f5a5637405b887d8cb63d70acae1aa4f92fe10f9a00ffcea45c6cd4184b3d3c65c3180fa8b6b2f6d68f65003cb0a148723307ccc98052b8db06db04d40f2e366850e1e6c96ee6af5ef0b8bfc1983963f18d74ee63b0502c28c3e735f5de8e8ecbff0bfa91f060dcd877ce54565ea14e3f5247082c90633c54eb86e20324258b9a2d66de89ea9a89661afd418981afffe0462a7f987026c7a77c2c0a6ca00b68179d9ffd8cd46b9293efadff654832062ebc3d40ab9025811c92da6b05844a97bc37d046b8c5712f31d6c132e794fb64cd0ab67c65e05f2af7e378c05303e15999e1bb71ade65a583f5f18a911c9a4138410de92c6e0f84114ded8f3e9904dcb9c310c72e77481590106b9932db9c44b59d01be14010e957e72ab760fab764040d19598baef6ae6e7fc0b8674a2433166e2e322e0b10783a456b52bf847dae7ceae31d2e6c266fa5c48d33a88b3e583cb85ece27a26097eebd5f16585a0ddb38aeef959f2afcb963d35af673a291a9c5afb044720cb043d9e6e67164efa37eb73c24e6ea9219fb14517280756b72b82ee3b53f8081d020ebeb47cbe9cc7430cdcff3f1c72b742584a559c81c8870823b960e10be4c20712822bc2804893374f509bbce2c5adb1eac3ca315911bb44c61770e075b2770a089253fb53131660e54053d83de5d3a6274c71790731a816aae59098aaad1b0790cafaffc6615242670487d7588ffae5e47d43f774775ead2bb9c63b72c49d0a4756d778bb4eb8483eb3f700ed0ad3728c450e62b79aa3fe50b0404790612f05467b481a2ca6c8c75464a30b28aa6a0f02f88e44b51b02709def589fe1001a0cc4968bf8408a5463ce55f1541578823a519f3c3e694ae9deb423fe25ec0dc20f02756afd0e299b6b47b128ce8cffdbd8a5457df0952aaaa9cea68cc5f6ebfa08033528f7ea5f8969d6d53bd3f6f2364dbacf0b8beee22f7a963a85822d66930caa57011f8596d15c982066f98e9ca686947c20efa451cf8355b2407012b8a404954430942e6a3c33e47ad30e28cc2b0c803489c402be6cbde82b9c79ffb4b304ec2393661161eebbc1d3d059a3a09f42b52747db7648724bd50529805fa1b406dfb911b98cc2b4fe19c22e37c03321600003b83efa6683eda47f509a8c91c20321ec90447be57e49391bfbfcdbd0688782e74c180dc3c149a948a0d16b409702ed7c3693e5ef1b840aff37069d964b9b115fdfd425b6d1e3ada3c020f07d8c05780738a5105a56804d3d19f8a61ab27a79802d6b3802e31bf2407b8481dca10c7108dfa78ea71441fd89e9be2c395a47e42ce33e68314140132b54a5b1cb2c059f339ba5956c061708fe389e880d2b45cd705f2d17f633c0f1c8ed69f2c0c10122be5dcce5a4a369308fc2474c92036c54a9f5cb49fb8c976af245d5d9ff4f0d7bf4f6d80df0eac47737b7d82d8505e114a772cd43697c012599c27416326e07a0e392c089043ef6fb9c44a84bf9de78af1365dead4d194fa2ef4ff30a193c03d77a253262ef531b26eefe898c4004806160bdc63177bf3298fd3718f1e7210b42ac4ea0ee2f0a75e1ba86cac75cd14633b5a2385f4e3629ded54af49add4d0c27389d682cb049427280c1793e9873fc6c8cd3eb3e9e25195bea561ccd6d1f0264bbec4e7d8ddc14589ef5c96b1dc88225825f557a106b6eefeeaddff960eb0f5656f46f7560125b01fe23ba92149c9ebf5f1df65e5a544d8a8fa2ec5980b70e4f6c90acde42e6a35dcc9654c7790f76714c85ee33d85b797a722462e353d30a7ab6420f1a65eaf5c390395649ab0e10c95c5fcbff3825dade1b031396e05603c0684d5d0dabce5587aa6633077a0547524ff64c0d79f0a79d3cd3c03c9760098f17937f6e0ceaff8a44dd4188281552abcf69fa40399b0920b32654bdb69403955d96c4cc5aa77fe06ff5dbe5200e0c21afacf6cdac9263c3208ba84a31010fb385b1ab6b0a31129c22cedfe1ca6ad08f1dd4d4b4bd53123a503ae552ec9c05d4b002c6df8208130cf5d388105ab5a595a0890dfca3f1521c64266c84fcc20ce43e46d5d94c6ed5d3bcf28ef8e5f382bac66d84fb64d9f2ea0ca400b578990c97efbd74c8d29b21014c74fb8d5a4ca38b06583cc7ac21ad56db6a37a93b7d0a0568b2376a4faf764913052ab0d894fd983f2b48f07d070419683c3493fe7709c2d9bad1703275ec5ca6babc5757db5bb79d854b73762c2ecab24c6956befd07121489904917277bb27f6330a5131b5e0ed41222ee83ddfc5626e58ce66a79038106fd6554c0f1b06361e4b527b009628733b51fa91e5a62965a2b1b883100094917355fb4bde04fe8dcdeb726c18147638169310ea656075da1035c9682e303755a74119dee854b211b62ca005e3864fed914545efdc394d1349549ddad620400c413cc86bca0a2c7bd3ff2114a959a84caa7d5309cb04cfb937018ef7a6509cd6928b3ac3a3316276f0895c16764eb689a11dc44dcccf288c82cd0b7b9e90d1786eee9bfb015c3e4a8a0cec92df6f5a4a0a0f07a105f64f8aa500055b91a0e4dd521041389b2d20fe77a1c8d0f5b2cc060a6f61cc70bd2bc76ef2cfe148c00 true +check_ring_signature 5fb06cfa8e01f224ab75f400c7f667624bf6008523cb046ce553066bf1b03848 4c6a83c10e8b48ca904c5cab18d3e98be1b334bce3e6c18781dba5b47a163d4a 93 e08bc18ddb01c32f61a5a9ab7b04c042ab98c8ee227c502cf78131d914fd4665 734276c54c81b4d42d6926a9e00d37ae5687f3b1d4f47a267d1b1bba03bc0872 4e9404eebf60c882e56b032ec41ce7f33368e1e69754413e4d16d109fd82e405 d51b07cfc88ab3a4d639de996a7b27a8b012582d68189669b020c58b444a2b57 91db9835c301fcfbdfbdd5a02c83df4dd66e11579ecfd2d7ac03e76448d0cd6d f57d7d12ddab18e3826c4221bec63305f09ba044dc6755461a3e1c68c87fe80f ec944fcd815814f2367659d85b9026c53f1c6100abf7b97739814d8056615593 beb991f7e842416d3bdf3d044ec0685a3609093ba95eeb1b4730d915db544148 c7a9b6fc02aae5d432dd89c30936394f4313bd48300fe22ed3ca69cc2addd1d5 4bc7afdd6557061b3a6665b936826a3479f74cbf63662874392ead739079d3c2 19663946e7227bb7a35081115f3156da6853d22687dacf30f19e331c0c893f93 8bde8f622c33834dedafdab08c984619d2060f291e8db45806e48aadbba35a14 5a3da05a04576324f0b10d4ad8a7ecf8baf612c9e4e99b431de667a0f9dc4f31 b5a8bb6875df497a605f3388f4146f2282e38505d3372b7d4106577ef5f4b603 4b7819d025986f24608bd04499f2083c81b4552ab1ff5ab4a7679ce2f17a702a 98db56d48bfadc60a40f2c914d2ab2b116f08a4aba08dd04be193a647e152202 634c5ea381ade8f04386ac6faeaae951f067035e8ace86b48425bb2611ca1ead 63086f53ff1e1ede656882d902b090dc928dd495d64ecd75c31f76099f28eff6 f017331072b97f382e00a421b90625f6ff8fca54430828269b5a30ca3c24de08 2de233f531fa4b0f9d8f46d01a14ad40d807211d2c95d85e1d5bc273690d71f1 5ee6b458ae016d16b1c28c74af01d955f929ab10b68d6eed047c33351c456abb 202ed82e81cf9d806794f3ae65353ac344ee470bad761dc7c686cfe8a945c3c3 18995a388fb2557f4d1649904ad1eedbb604dce8dc9a3f9a5a96bd80cdbc078b eb13043ec5d6f4caee078d67ac05f02786e8cf7c80dde9843384d905dc024b81 aa46f286d7e3c099acb5d40675abdbe4ec01c58be87d05b3e56a191300c1e9f2 7425d5e364a65c1f94d37644d42bfc85abd2fdf91cdc5dddc7386e495f186b8f 325b519efdf1828761ec66da0cfcf39df8306336793d17cb45e7d6f2e0b87540 e62ad8ddef0f0467aa6718625045abc3816f6638d992fa793741b93f3ed9808c cf605511034892fa5b90eb7e1005651e8b7b2a0e7c100bdce2b11578838fe7f0 b025a93eb7daa1ba02f43dd2f320efbe85516bdd7eb50608bab768556bddc87d b9ebc801cee931cdeec47d59e718c66a619f43ddca8ba4508ad3c8feb6c8461e 54b04810a729beec7fd888de3a51a38b499de881fe4033ca96c8830c4777693c 5eff66909e438e6e249b2c232ee2354dce198127536d88dbc6bf6e372dae2970 ff3b3e4016f155e5b73ed437dba3006498aaedc1bf8d7d16dc8c560d89de1917 30b5ec65793173ae11e82c4307662eb19c892b2a78097295b637f591c34feab8 a109c3f8d5435b06ea0753324114cae8d12f988088b1ab33f443b6f99e19f9dd 7067a5ed051c21f2b17c71daf277cd4344b9ed95eb6826cd852c4f9070dd6682 72c3499cc1fd63772ba6ada7ba2dbf5365892b9a9e1300dcd21c1fe2f3662d12 871c67cc04eae3484f45b54179c877277045452f5bcdeaab961937e052b5a2f8 39fe7a9c3ceb6a8111c3d8f1adc39d58c9b0bf8297b83c2b1ab29e62275ffdc5 a86fd42e0265675d9ce853a99fa37cf8cd8d1a799fe561eeb0ce0efff7a95180 a05fe38adab69accf7c1ad40247a465ddd1936c3480e3652d128dd1102ce0843 ddc73fd94967c421d6b74a1a02061d93b69f66bd655a0bc1a3ea12611b093d8e 6f617f60d96808b768b4853fac89ada07b236cf12f064613dc5992a23438537a 50f7a7198e672230dc2ed08798302d129ea933ef0db672aeb648d250f135c87d e1ad487b4c1c96c690f82c74851dc7357ee4b0ee9ec829284e1f07a5fd228195 2c7f6364071c68d9abc058b4b804207192ca48b31afae24e39263f0f426192ff 3a6918edbb19d3e948a744ef9b221e88991ae8086aa8d7215ad261b36298893d 65a964223dd70c9c7ceedaa89df1265af54ba462e2007ea3137449ceaafee0c2 ccb32d73177153d5d740ac5bb6d768f14bfc95a54700e4c060b5cacad0307a07 361f6924aafb280b5613ab2c49a06174b1f1b68a46d1fdbb1013bd6a894b307a d7e1afc1603e5d9dff34f43d0df095705f7e45c7a17fafb28b00cfc873678ba5 74656e3f1eaad9f4a566a4b634fbe52607be3a226d253693c5e7a5068d00eb27 c4a5b94e65c5f003a6809ee733a135f26767fc87e0f5cf4272ecfe784ac34fc6 ce552a955bc574b0e8ee998e619683d60c0080aabb0c5ab823e534beab6bb6b3 3a293aa3580daabebeb10f5a0b0fc4b9e89fb8159aec88f52c52adb9a38e223b f6e060382817e3e302b044b46b673ebd5c5f3d186cdb2afc032e8fcd7f626700 08731596cd25ecd03a6bd0b6ff39bc6207bc8ebff92e0a133fd474bbd92bc4f6 e398987034f1e3a8a9b3fe0e86be56452080a2b1fb26fe4046e2c9e588253d46 84baa2250af8e81b000c25a98316097434c3d1af184ebe56a23e2466c42f9ffa 22dc11c4436fbe35a6c370b80aca6871fcd0fdb8264bd04a057500b358d3cb2e 05b539841fd00254b641cafd5ae59a3d64d6684317f63ef8309b64506b77bdb6 5435db5c838b8a06b1dbb8be59a1d1b93ee883d589465a3a150df707e56efc92 bdeb13dfb1725df3a60d9a6db3d259199de1d64fda7b3fdb4f2d8810eb9201c4 c0fa94ca850d9fc172487c47f80b6ab9751015cf10173ab64017ba9d4553bb4f 7ac2a8e11ba167d128000ef1e3056f1eff108a293b7c9b5418aabb53cf42a867 211b556a067ad5562711af4d197b0298d0fe822cbf326f150cfc585f18109638 3079772c4ff45e5f34c7dee29fb11c6aabb34f5543ad36cfd8ed0b19e0eff001 5a24bd6772fe93d89196fa2c215540bcbf3eecca988f168a854f642e685c6844 5cd52feceb83088bcf6c33c1be94543372c6d86890c9f9312fc5cb57e12a3e67 80a366490cf78636ca0aaa079df53f3aebc7d632c56f96ce2d5e8e67dea67398 344818d06064c656913809c0a74e61604178dd9eaf64ae267df2d6fe0819a859 2151d97eef185fcefa548bb63350e249d067aa85f2f455e23b1ce3ade8cf0175 00e4aad40a866aff1fd2829b3a16f8f2391d1aa0b2b1ad3efad617e4509ed69e 3a8c7b3b7751012a6679afcdb7d7ff0a45416671d5554a83fbac2fd5b5ba544f dbee42a420f07044d2d4d91264411fdbf756b7e164ef1933c7cfb1a823df9738 7ba66bf80678c751c419d275f6cc34806e6f767557574fe21bdeb0dc08c34fae 8f55152485c21f004b70f7d12c6455a15cb735a50388b1509decf8be471a7d83 f011833b5e89dbfcca9669b81e83e4b14e5bf7d937261c0143958f613dd24cdd 623dc7c145f90e3707102d98496f9a772b66deefc8ddeaf4dcfe5532f2711ff8 49640526d666c8d71753bda30205198e6fd1c9726094e954c760ad0da483326d 4713a3d26a46e40ffbb10b544a2ca93081fd0ba1ba031716e3486425772b05d1 e764b5ad827457384e673f8b98e29845f6c7f154aa7a050e641d39a90b8e80ac 0f97dda6773f12eca2b0881f6aa853311065ce388b990a8d430eb521bf35d034 2fe1498cebe61b3cee470740790c28c704dcc918265b12d9510855fd12b53040 970da42195f03b8b9311fa99846d72f35f39673aad327f967ca17348bd7df5e1 58704905439cda562ae9705c26cb8984bec7e39d18760358924cad63a4d37975 1f5c2a92331a58737bae5fec207e7d59fe358fa1ff232dd1803e1c89f722eef8 bc03da0d2588f296692bd6682ee7edca387f444e6e9a153bb09571a2962cc8c6 dd77d90cffff43231b8e35a77d79963e25de62cae3df03053e8190487250fd15 089f6c698b6ecb3d225cf7646cf9e6612fdb0c97086fdb5d4d7b3eb18ac6efca 27ba9d893aa69779cc08306cd8771401ec7b34a6a6face9c475e36abf1502d7e c20d6321bc90fc03406c006065ccbe72a46b43f1803a0193e5257a13361998f2 c0f148baa0ffeae4c5f0b01d162e07a4f9e97a3bb6c46e461f3e69364401c1065b53d2cfda4a616676e1e4fb6f2d4d164d4874c317d5d09531b8b69e5ff5f3077c935de731b22189925d03f7eae8fc5482c23e07fbbac8635c81507c5bd0e506752e0e600be3f3f7c24cb6d94d6a6f77e94c9c88d4ec2ee77c13a29ff1759807bad8e13680e50ace76bdee3ead28f4afb9db9fb6589659e4e4d436f8d18253052e0ce24a00100dc6815b5b410622926fc628375a5946aa97ed5bf5d0c40848000ae070ed81be88801114e1d7809517c2ad35cd00e323019f434b99e3256dac04826ced5a2e6f15ec7054261888d083da0365326177102fe6fa00caa651c2e70b8adec35ff0e7e6f0917d7191358359b189b05281d91d707701d15af22d044b09d1bef64d28fa57a32a835938186448f6ae84a05ffcbc8a372911e726efce7202b798fb42eaf5356cba82e8595914b2d89f923986704569f4f7ebc1339a4db6019bb50534fc89a0382a3179978c7f6861a80700572ddf81bda148f9a8fbfad007069ab012ce9e654b004eebe9a39c0e725bb8e9c5f84a50a0247fb4d54bab2d08140e07b11a3a4091631b729a73fb76ee2399bc0aaf73cea41323584a3caf620bb7e310cd02b40ca79fec9c033131d690db1a4de9b403cf927cd2a18d31dd96020c81ad0dca7a7f586290629cc660511f2b71e5a83b305c5ae75d71f467ae7103246e51989152b7a09445f65d8793f354fd028c2274db218b5ca7bf9193a3c006f6b47ca61119eb8ecbe3d5ea520717a44e3bee87ba8a4ec7bffa26ec9902670a1f2b2cacb16e809e6f4484fef04972af99c601eef0470c214c1db49826360400ad48c1068071b382ac9427115e0f4b14e7ba532151d925333d9f802ffe02db0ce091b2204d2e91f98844cb224c0549d0ce7bb27ba9a92554fc697cc7c17e8a00e3c416b45866d862515e435380fb5b416beb08f18db34d1654f2edd361ffab0eb9da4d59e76885ee18d5d7e2cb7e088b1862058f6408d5f8fcd282c056fcdc00a48943363f3a1008dc35293d6b2d2cdf169292d0d26b2205696328251e435909fbcaf936cd0d024225640f8f9dc072a0f091502451431538dac5f418d00bc9074ec2a03c0ad61cbb5ef400a40fff08208f6a66129cd5495053062be89ed3bd0a063dc1a7d2809869904e10d2924047c3b281da956a0cecc9704b94ff794dce0342f00f4a1e795424720efce495752e08bbdbc63491b2e7a5586e3a5122acdb06535824dbe4efa62ad92a8d259d9967b311c49aac5566817df1aa9acc9d8ee7007881d550fbb1926127545c33332346ac76042f3270fce439c9a8413f8f41ad0acba7ab068cb12791b0aae5777bdf68bf20b5233acf73052623f9389749607703a817cf507f797410426de0a999c9183baba94ac96d69b13dcbc3a4d15d3eba0a6f0c2cf02303b816e6147664be4689cb4ac6992b23a87de73c86010610f7410bfcaacf44ed1438f119ceeb6b201c12433c5d528243fda0957baa555b1d2c470d4b6ab6999c64b28be8cd13433c61befff8ae28d452f4aa5452d428d8519e4a0a9c41ec5f7183a2d6a0d1250053819ad9a9f7257077dd20c0ea2b80f5bc49ce015ad147a296c03a0d06447fe695726681aabc48f3ac953079ae479ed5aa37730d613f82be33a8a444e70ae08e9bac37737a4ad7d5bef0a509565fced1d9962402977440f01e9c329d8625769619a6e2e8f3fce06733e2bc3c9b90672c3f3dd50e807df4ebfa46445f9365e430dda420d428185c0ee15819f1766cd00e3adf9b0bfe90fcc2e47086395c70318bc8cf12f39baabcd4dfaacb7c1bd670e6ac365d07205f360d7800f93191ca0a3242cb2ff86b8904f24fcfdc05bb0385271f72d1062bbca87cd1e659386f26e5dabbb560d00caa9ce944dc6c5b39fb7f491f9eee09deb8c9da7f978fc290c215bf957bb49e3148e8a52bafd1cb86f634856f937305aa0cff9b7187c081b428c2d0252afa45219f4ca716ab4428272ad600019db30082bd3b7eb5318ea20e0bb47c1c01b21942fb081e807bc74f6b8114668a273c0c70339697a141a45b1bad1ba625380219b94a1e05e6e62950e739a3838cc88903c3c28ddde6cface49be4f148924d2f086febd968c8ca761714bd7c544a521d09f7c6df312e5f8c7c973f4680bd61444553af34832c69e4fa104af8bedcfc7004f235f6c9333d00499acb6aa42df4d04be31b718f61cf2fb395f75dac1ceb2d0a2ab7371d06dc1fca5836d69662557c3fb50500f636b28d12fc13888025ccee09b08852ce1e3d11322184b6c4325f4760cc0e67b9aaa140c238e2d451f1369d09febcc6803db1b8012ae8f7b25fe331285dd4eb1a4556f74316cc4bc6d7b4da0f09dda925dc549e6e5b6bc8c09a4180196b5d69a31a3c3cf6b02c4307a721060a30f576ec960d3d6c6ccee82aafb6bb8f2779621a533116f8f778dba9c539a200eea1346f88ff2eae837c8ffd38c8f14ad3d9002a7baa696290f268d1811c090880fbbac73aebb40cd61072a51460700f1e3a8f9755a01d054e6266a8e98def027bb6e0184e6d0c498173d72e179bcbba0d07f4c8491e38d7ab7708702cbd9d0690f0789024deb3e21b963af49f7be4274a70344fad5485c73380e8f9651b8401108a2a8daa749c0716b526dd1f66d52bddcd5c40725528da0d9e8dc9d5fbcf0cb580561aad8a49fef8803c488717bf9d733abb1ce4df36e75e8710387a485a060fc279b0e8cd209f96a685fb98a055f54d437c8abd76427c681fe8488341ee0dab1d24bce89bc8cf24124dbe80a78a8f60ebbe67b67f9ee830b1f5b63de1cf049116189b27801de9d41f7c3e7a9b0f8ad74870aa7b7ec7639873a3e7802b4303b41ea1f0ae2b3f95ea6ce73e02138ffcbc9615721bd2e394d04d57e9ab478300a9b16f5f21ed0dff4f20cc9267492b900ca3023a79b3079588cd661f01b67406e8ae219a408685b01e78b92e3d360ba8d66707456bed07639ee20553e9c50804de2558dcd27e368531a5fd1d51415bac4541000d79962a59d95455a9bef2ce0875d2d6d9f3541c95d6feb83ca0d36fa3ac6ddefce42f3646c54274c4fc627f0765fca73c057041dfc959f0f393fcaa3148adcdca7636ef774102e1aaf752b20b296a77a52f971886722e94bc14671c5c7bf2828cee075c0c4d2c6f55299da80c21aaf2904803714e511cb48480488d73b0b77eb0d012e3591bf89a601c9cdc0f2f98f67d394d23e8fd77eda88515805fb6dfe48680b7b7a76a1fd4dbce96b402fdc8dbf8b613bf21ff99c04ba117a247397027e9a715dfb14ea2264f160f6b0dd39e57441e717c1a9569c6567ba447d6d10dc8a4405293a446195fadd7650305628d8f70006606c813943e68d064cb5d21f1110bb887c7bbbba7f29c4c7d990f858dba8c2e815a43cb3a29690811897635efc93e3d6a1d4f44b7a80a9b00970c5cb7d86be52120e7021abdf6b06a2485dcf30f2febdbb6b9dedbeb9a17821b05f61c6855c3900e057c5206d3fe9458637d05f8f9611190eb8bba398235ca770cd3f9736987570e364bdd8a3a27b942ca99182492f93d010e7ac4747659f20603110a10a1483ecda29b13f1fe6129ea4519a4933a68fe9da84342006092b8340193078645f48306890c1224c5800be810bd3b1768590c6bff7dce82f6926b290cadc29207f0fc11de4b5383c1850a21e47207184486c9f9b84fe58b0cf7022d0a3a361ed63ead371cf86002a0132fc6ef7e4eda3982bc36e8ac105dd574f8220a9990efdef149acf1c9ed1930afb683c43984d8f98653050ebbfc0ea3e002e90a45457ce85409c8c1bf154b7e4f29bdddd7148262350ec40b41542f3c13e01b0c1204a2f75f4c3376b87f3f1530c8692b63b8dba832f49fc28ac8b91dc1ba410af8befc07f3cf75b59186977fb15bf81d85dfb37463d15d752f356310ba8db2048b1e0c2990c33ec48f727f2261a79945f20322512e8419fd0dfd309801891c0c65b814e138e27e92aaea6e55758b612b89cf54a6e919f56e299bc192d4327c0c3ca9707ad3247ebc89d0f3a4994f9686857e85d943ce318793d002146af0880b7dba99b0bbe3e7837fa8553d3b95503a08cf37efcc2e29782c49008086f93806eaa417054c806ead5239d16b4dbdf18853f54cd72d57baaa1c094093cd71bd09364bca5d39661d6a68e937be3ec4ca1fa8eb0f469a2c0d0c176b84ea83fd2306cce5c02db9b6af5886e88d5ec429f15f21f704223e8028de9306fbb39c21c805ab13cd55e92e99368f3bf3e88f450bbed3d735d5973105b6266e13f14e33b105665831a2bc7830033f4a947f6023e1e575c946fce82b5fd64783a5e2c7cb9a096b711c6034081372ad9847169ae49d12e3b183ef36b2e2532f5828bc9cf43202cc4dcc9cf1b657722ef406eb3ba0981233ac649163eee4e9846dea6f3887c20d37c8f9056758ac198e58bde7ebaa08cf1dc3f9a73c5567ab61173b59beea1c0e5775e74644287c602b6f6509ad6b30b8c2adea15fd4d7e214f1b81c6a4eed20ee1c4e0b6a1e025b2cc2f5d786ba08cb2c5a2a1ba1aec8414cf18fdb0fbd80d0cdadbec2a0bc9309d029102c00a8948564bc3f5d05c03c3531121b254fb589c09f7e49f69bf5d940fe22ba15128b0962e35bfc7aae609c892ff0a9d414573940dc80cbb2ebd621e967a8c8bc55b249b71ca55cd9ff09452d918d906ad2a3e3109ee5e8d178feab71a5a6a5a3132d0f599e7841a60d591177387591feb78ff7e0f070c373b7d5aaff6778d128aa3e405ac10cabf57aea41cd08ff2e01644b95c00795d0628758a4835a3528f1a90ba69569b8051beeac3f6b3ee26238119837c0e08299c327cff05cf68381744214037380c3f6e347c9e22a569b7a73cd9a8f007f541cc819982902aacc322c814b4d1044c5c9f459a62ab13821edc1f6c79b50274806603dfe0c21fa86f9507ef41bd8e4f89cbe0b025b3e55d1219c37905930dbb009194f2b9ac7f95ca0ecb3d62f1a77490706d9d5c6b5b16852f5b4ca02c0b7d2ac3c30af8e587b97d5690019f673e6ced3298afe2a04b79a2f44c38253404cdd45be89c5a40d737e480c6062053c23d2509c3ffd5a4baacfc4551e4d05d0fa519f4a87322226ecc3d01a00b91421c482be86e83e5ae579a2aa15f936b37049bf64adcc5df1ac1ceb81e4cdc17e9c84ed11843c8bc3d8007a6fbd19ab9180ef44b57417c6d5a32c82f1aba10e52e5f2b7bd5bb17bd6a3dc60b72430305930d16ff3480cad899e4dc612c3e4467ee020f32aefbc5f64614f5766dfaebc896071c5b1a651684592d05ae10982d436a2c8f08471953d6e77b3a839ee56b1cb20bc5f5978482b99cce1ca0063ede68292349a54ebc7a3e50d6a25e0d27a2cc180c1182b4cd6b3d244e48f05025beee87a2d03d1fa711c0c929f77f2c231000540e5471ae4ed61e52fb9bbf1c706e8a16617e79bf89e3654513aeb0ecb3e98d13037fe795990fe2584aa52d70d34fa723cd06dcc7e2672c38bac138d62372b3bc07ff6f396e299be19fd028cbedcae540444ad81e28f223b6c58c11949d6a51580b91af1edb2d434a6cf8e34c3496032113ecf49f26976412359bc752ee163a7f075a65c0a5524469d21afab53f8072d613fa23a39b103a0b3c95cc5731fdae7e09f52bd03ebdee49f75b117a1077b2b3b0a115a9676518285d71ab144273fc98055d2c3694eb6ac2c1704052750faf506e118262658fa5acda100bed0b18a4c309cc68c8f75afcfaa902bb47674b0015a48be40fb4503d75e54aff7ffaed083302862dd8181037bde7905e14be40f65f50c6cebb137f368c9b39ac38ef9714960c8475455aedc8ee1243e8521b203aefc740a89bbf03accc2c36999c6a05181d0b2c367dffaa369f77d09e1e2867600b53d521c831a9f584d01163452ef2cf7d04c5ad45f7b4b6dd63277bcad77f024a7bc2e1a3dcbcb8af55585922525d369404d6f7966dead41e07e4c988a738b62165db624038d3cd4a1be6913d97c3872500fbd574ba7880aa6d00b9fab6ca90ff08add991b19b7e0b8e4f341d2ec4d2e70b1ca1658e264a3f6f1b66e134e87276db4d1c094b09f044716f6395359c44bc007c80f76c88ab4739bc11b1a84b348501a9127f6bf8182c41106ac02f5ed75f07836608608ccff34539d1b49d71aa867727be7d373ef3de843b9c0b159d86a700cbdb72564b15ccc2d9fd713fd2c2b056c904587a71ed06218dccd252e5897c00217e764804bc0161eb00307c4b85f0768cacc39c75a1d147758d84019ea9a7074398bf53cdb4206824b65f1e5ac58e4125778098eeead27b412d5b3e0b22d50551bac797f01fcc7bdf8cf4f3b73115651cddb920d863def1b9437bd6fe5b2e048b67b4dd3127e949b160f4b1b1b96b7a9ac455618429f56319fa66a549c70e0834a6bc4901eabe39c5eb4bc562adeb9c1e89c2c1ce8ac297db7a61e33a65240046b0e3ae07fb8095d5fe97bf714f27f69f355e462317b3156bd0d829a1c0850e5306e92bbaf97b21882c9055259ac00892d029dedcaa515fa3771e9ff76cf00aeb5d346731833fcb5d6e8a582ad44e9bba1f5821d8827b82732ba646d614e60a15e7c22f8b7184f201778764444f0cb1ee852a8746fece717981278df075f5004f05f1f69723f013afc77dd21e84734d8b0acd42396b69c479e823b852541706c82401f70b869c9a12e59622861df1225ad4c1513cbfcdbe870b625390658a0f4ea9f7691c1135c4594b903bbc87adbd7f9875e9bc07323b7ac8c16193bf700dbc3cc6de6cfd0659dca4fb92f0539cfe4678b899b4327b5b2eccc0981042d900765d6b670956e3747342eca5cf8a0ac69a7e1805f9c4ea29ca45ec955b61bd0f6cf1cf3a7577f1fd0c19620929bd5465266c8b8d2a73118678a6ca1618dda805b2e639f3b76266c274990991cf8f767537e7115c3e69f513bf3c4a08b343d1073572a6b4fb8de22a34ab7357e327e36edd8b1bca82c9699962517d526f9ba105904caadb23851df495815c8db33b3f7c1148eb8f2d49562f4b55160f44b96e08c07fbda900649b2f5542d84ba5dad4a0b3960f6ce9c45b64e269b3f2e3684004ebffd8c6b9f0c80f5b13fec1e7969ee4421f407e8e424e2e01eb2f8fc439f103a884f41c0679494b707833e18b518983574c7a87bd3cac89f76ed83faf7afa06d83a7e0a169c1404512688450ccf193702fbad5fd73ecd2650d07139508f850f0d63c2724ac572a41a406b21ceb8e1f077ed61586579677a8d84ec16f396010c5931722b26a7ecb096fa1992f75b53551fd2813e3d768a7f9808194c94d82f0ae614da889dfd2014d906ae072cc78bf392b6ae1ba923b05202f9368f29f9520f8388e7aff536e2c01e1c76eb0b3f8f53eb451ee8a00ef527422f1826b37d8c0d6ddad01a37dcadc2d6949c37086e29b27d9d3caa01b7bf376275c14852179d0be7820ffd72bad6c4f3d46982556b758299cc0b42e3862129da17f19f155f380ea2070e8da576d405a6328d18e8fa45b608d999a0523803f889174b8cf4bda700ff83407a35e74f387c55f82b04cdc41572618a7961d5151ad0291f59b07e3409abacdcbba26e17674002e1672280845963259f29f476f958257db06b08c6480dc7bd2a733ce7f7dae70092e2bb25b3b09c64905e755000c03a06a84c9313ab06b8c59d3def4a7ff72d47bd6eb4f03b3b117a47986d720c9d3025029b8feaa402628509f4ea561663ec4562aa7aeeda8cdb1bcea278c658a675bf5b77ec36760b8661de383c38ab910546174c5536c752a469268fdd54b46a641d82a95d04370159485fae2636403dae5e83373bdfd6d49c4f9e294a34e3177ac205d637b7bd068afbb37026497810a79fee5f0238332973853ed450365fe80a51e2e691f21e0a449c055067ba3f6005cbfb97cd1359523c0671db67fee505fd5dc09e45c0c202be428c27fb7115009721b88eddba4faea28fce7f4ecd72882850986d5e136c023c08e43be658435007d7b1afd22ea8b39d1213ebcdc7e83498771e77f86a790c823c9de1e8fcd74bc2909328d98c20600c4f467724a809cd0cbc01c8b473b704b731210eea575fc2e221c6f716566ddb0e00868b40d487641477e3691e814a0100386b650e3be9337e92a8a33dc8c3b8c78683f1b0a1c9b22920a3ea005a5203524f2ea45a5a58b6b4fc2ac6faa3c9aadd8c11c61662b126c4170214d029fe0ee5a471131f38c504dfbc7d334bc805dfec95dcda887c90eb78af4feb0037d2060205687a2289886d1d9639f8955a2cd88225a032436a957d13b80397cbb2c00c3ee8aef1a6c4c62176640c70503a36aea54b37f82838211fc91fbdd0c35d9504 true +check_ring_signature 27939cbc1d71691067184558529038f22148d456ba47052fffdb2eff23eb475f 4d6db567c817d22993924b0350b7dd888a58b13c463445abb01d2d9795eed02a 57 744d8240e08f079cd1dcbd6eef90b631dc39e2fecf6f35b69cebf94bcf7b3479 32af2f2a2492a6038fdc1819498434c04fdd8d8ec0d236168f553cf61cd33ca3 166ce04ec47ad52d7759787daff93aa2612d2dc94d457b40186e16cd845d786a b22818e07adaf73d5a8ec4235e71c126209c8902a7053f01848e19bd251ea295 0758a9661f3946ad94ebb792444ef040bc506c2d1de08b861f678778b8b05d55 7f5fa133e4938704491ef90ded33551f1f04616c2eb2c77fe88901d1cb11e658 1f3356514438d011b995fb92a9399faf5f1a8bf47a3247419806e3b06c06fb4e bb9d32115a8210bae295e85fd97ab86c09115fbc8e9d95c32998fb3cadcdd258 31dda5c39c4e261c51b71eedabe49452b021680187fad807d615b0da419bfa4a ff463ae749ce9f00fb04d11d839a1ac5fdad7568bac8d707555dfb5981b6d957 9d3afa786ccff655faa029872a128ed663fa2bd6be162e2f3bc07899a921934b f64a648b03002fe142d75eef60b692be7a6a28f04f3985032b88875d593d594f 221a144c899acd7587710dfac78b17eafa295ed231c460554403d19f989c8567 b9d51d2444ddaa100366b62019e8a752c61bc2e18ad8b61d8b6c8cad7fbc1f68 df74c5718f0154bea387c6b769f6b269b703d9ba906618482e30fbcdd0dc02da 07a8cc9b02e8c8c5ad9ac3b535cf73054fa3b8d675512553aa73f2d813b02b96 b8cab80877ba8cf07bee5edf0fb00a2f4ce1a4db44de650da37f9456b4d4c459 dd906de0abd1c67a2dc6a5167b10a1d73e3c1f2821224936fb6904cec0c0391d 6193c42746311e5d875847e8711331a694ec033d79c1fc22c379fba2694225d0 8ec49294ff21ae11ca890b12136df5382af303d01064b5f7ec14eaccabf6ab71 b2a381c4d9183d485ead3a1bc7ad01042ce354f733500494dc60e0d63e895df1 2c9a0c81bcb3b66fb4b0c1a7679cef2ccdb7cf08427b271157aaafab23cb157f dbcd7588905afbad0073e93e65b9b02f42d6c98f3d5fc68896c4e5ea63e95b4d 2e9aa5ae656e3bc5a9a94ae83a2b246fc16f0936d53e7e5d18b4a4a6f730e39f 9f2a67eed29ad783ec43f0492c8e0078b0b1902db23944723e43a24d355f7fb2 da2c5d04529f8b5e6b18392360536097eb13739eb2d8d58d0d9d189532fe655c 29560aa3ff1fce246ffb2320c124d603f6062f3bfd923fd163ef56d9d09a4f00 d55d3fbc59330e12ed86ee9ab506e9108933a814c1f3610d1987c8b23f6ff025 4878e090ebf510b012dad9c463a081204f7d077124eebd6a0618e70fc8fabd2b a052a4c73d24b662a7a305eed2b5ee6888195aa591ffbcc8deb794ccd739b4db 353cedf7a5ea464ee205bb4a89b753c017e4493ef6baa88d3c3ac4ebe3a20aa6 f77d6a1a09673efd25b056231236ceb70e15ec2badb9845e43d72e411fc31327 ca03ad9de90e1f7b53e106f9633c6b7404a508964475d7e37a96912d3d7226f3 4a929032f57e360172cd10e3b904ab4e46fab25b1d39e10e313a26eae2c30942 c99a7d5d31285181fa0a2005b3f28b292821d090ab3397dee857e99fb4a5d5b0 cc8ca39bf34c14cf246b56731da16efb99cbf13073ceab6e71ccda425c7d4333 64f48d8bc617d01c0e71b0047220dd667eb4175628df8cf7c72a3bb79d937ecd 306b82735eaf66cc2ae80a1fb2d57440f6b3a98f26c764be4ca9a161e29a89f4 307187ff043694ce048e91ccd610bfb1320f362bc267a7c43fe12dae3e4d53c4 513c7f225b4b95bbb6fcf5149058974bee81da275c3f15e3dbeaec2009f9de1e 1402e95f7fd269ebeb7093f1a1c9a72865711877c027fd4149c866d15f7c47c8 981c03a657fc36766622474cca3a46a16c88abac947b09407cc9b79d29f1f5fb 491310b0189fe16cfac680b4d50400a314c472e1b19d1d62fb395d32335ebf33 3ff8af3795933a0919ba4eb6a4b8e04ee5375a10c50fb85c7cc7ffa5858549e7 35fee0c49f6e8af57334a2301b1aa3b5042e815b59c89c88fedffccb3016c08e 1b19cc9acf338acbeb805e43eef91be82e832bd4ea76590f066b26646afaeee3 0477f3586a5969612a5cc647711919e2c670bdaaebb1fbdf4d6b8a29cf1aec73 c16bdef96333ca16d8ce4dddd3f1549b5dac59d4e3ed6da219d58d2c65c89f47 077360c15edcac67c025eb0c23341b11625878eba56180fd956ee55e1c7bdb02 15f4e7f4ae0747c0fd03c210e961961afcecc3b5049640bb4ecec431a6fb5a26 8ab55fc6d8b4860b48267afbe48d7faf56119965912b363eb72debc7b3144a1e 07dc192a1da1a83c76860a53e3df72136689fb51a1d57fc0c521e091ca247095 9f34660781f9635bbcbb8b92fbbc1ea5a9618cd2a639996e9382894239d0de6e ce10991ccaa4e575c1abca10f08a9b5b7d0197611daf2b44d04fd95c7ca4d5f7 420b16ba7b3a0db890da2c628800c77794a5fabcc175c5e3d536e2097c525db7 d9a3865f370096405352f7ff57cb739d49769d0c8df0be967cf16f838d2afec9 37c501865169b6a3e5b94ab83c57101e4f7dc4fb116225320241b42bcfdeeec7 ba2a333ff9113fd9411df4d5b731660ac0f135aed46bf6c30c8555be54cd4a087a6b4c7574875aaa5770469ce0b1802d4da9f2bbe6c66ca2cdcae66cc5358e0f9cfd5693459cdfabffd4909da2f8ca61e985405be99d8af9ef6099636c696945f92ff798eb5276752ebbeca7f02eeadc6860518f6a12826d9b4bdcf825415f0319f42c552e3c83b36448a8e813cf2beae2556281c121d548898b2442fe5db409d645584916c2c1d73cd3f85d3d3e114d74b291bc240cfa93cf4d4ea1595b770a52af42518c6762b0f4f90c106b9cce53a76909c830246d1aa708474aaba917072ce70ca0dc40ab19b9f4a68bd9f061aa113246c4d7cc052f89f8b5a7c2ceda03ffccd605a28e79f0cdb3189ce24b84dcad6c9782abb7e40b6d1ae3cf35c91b0ded9b018c48ff5441300f5f5a0bc663a59e2d2c03c248f00ebaef3cbd6c106a0f4b4987d77d74f55fcf7195ee7a24340b198aad1ccca620486b8685e4199b02084a73d27fa50f5ca0fb1280b9ff2449bd05267a56c34c44fb0e4b71ef7e2ab400d9126c31315f1834ff20aaede69b4d267bcbfb9ff5c7bc75198933dbfcce6602fadc27597bc6b7970d992ed3d9c0cfe34853d33a2b97e2c3ed1eb19274fc270924ed44ee6551428887e1410c1bad39503179f3260cbc9ce66acf08ea00771600b4647ca43a6e43865e7a98deecb68819a7500115a5799385c4fc1127e9ee3602a992146da9e5ee2faaa0184cf3a7002ec14b15df20f9daaa07506d4f9a2ff20ccc09ed27a6d33ef9ec60228983f0cd3d0cee784112335ec6ef55dbced36e5c0237a624f4353dbe6451718f259e0563882f56ff7cd0dfc9179714c5405832750cb970e3fe0b2706635e914fa06a9507b37ca8fc2eb4cd78918b50744f6788f407fa6ba385e8f1a0be7a0106366f37b86c63c8b840c2e29fcd34afa546a22a250ef5512e052b657547329d34add461eed324170dde06a662c78281b286648bd1000f7d7665fb5fca5b1e881e73c090a2da8ca93e94f52f5f897f51cb843121df0322229020f179f3b92e9557639ec6c239e9b0a2c7ede06368f9d0a9d3d69c720039f3030c805c97379b6b5967fe720ab41f3f4fc6fc84173c7164e0deb3a8df0083a9bd7bc6fb6f4f8ba99340a302e72da2c2ffc2eea0b2b14cd5024215049207509b95ee3539cb315addd70265f7af8ba0c1961aefbf343f0e27c25bb6025c08c1cb476834ab077e0e6aa199f562ffd3fda13a86f7d5f54a3060bd62bebbfd083b7b034ebd914b31edf3830856073a9543507989466c6c61b42544992fb62a0d1ba95fe1b7d87b844656ddbc699ffc92c8b019d6789abdb40e0e8139871b4d02f49460ed023347dcc6ba80589eb400ef77fde0e3380e6212e6b5c7c7646161080a8d4e083b46ba2b84a06476e113022506fa94fc4d24a031fe80fc203117280ea54cfeceab8d0305dee6503dacac432e24419e4dd236aea2eac186dd909b85030eaa0ec26c15abe0e170d28277899536e2aa3e28953675736bd84ce769ac6208f6020985206af84ac4b9cc16fc98792caf2ebdd0f39384a7a7702616ff51880d29f6bd92a624675d6a3b0dc04dded88b34d78c5957d045de397f193ffc367f037f73b2561a85ece489dcebfe792432a6880949bb8f49f8ba66430677a233a90c4fcb5b4f83ae6117f4755a47730bdd75d963062eea427c76cffbaa061503bf0a0964979713528923ba3163fb4ed73f8f83f1644760b5fc8e1742f415e3a025048ad98d2ba819700069c44a7ae063c8b84f93d6d92313c98a2d5228927100e30b84143dcc7fc8fed45d765a7725603bc3504a8dff781842ceafd46fc97ef96106090d1914e5a15db46f9c8414d81aead80a65b56d29d571634ffc89612b392503c8ff532f224c145fc43079955cddbf32a9c5be11347f47bfdf902111c9ba6909780eeb8e52b18dc1dd78df61147ee4442d4887079afa3fe2d7ab7e99ba0d6a03453cff3c6ac864b022aa66c85617233f46774798b074f6c64c5db138ffb67e0aa8905fb855a7e6289da64164f09f84199ab2d77af9bd5341c22720fc7a9b37073b35ceac1662c906891f552da909bce611d9dbdabbb467fc7a4d0d441ced720944aefcd6be1ff976fda6643bd5299549a43a21d09e7978e0872b74ea328be50cad5e0aa6c472ff3fda6d3faeefa2a9ded483b07c2b3e44dc81e514c43548da0b714f5008949892fe8b3121ffc19c41ad15d515f58d896fd63881c5a496f411047ce8e0ddef6aee1a9e5e77097a703f19db21f0269fa9d907810ecf068e9e1e04725da0bfc95c7146c4f43580f610069569ef205a3861cbee8f36a5c53ea2240acf46a300cfb6e5569e175721beb9a221417cb9c279b960b64d5a68c9f39eb605e9900e5a8a215283b16e6bbc2adcdc3a30247814b8c6071747166930bcde8f02cfddf55d906eb8ea1de0f7c6e9fadf8b2f5c09812a70c9a34e3c5aab4b25d90707c2763d37ae41ce6ffe99bf8d06f691fd9f0b8f3b7f2a01375461dd200325047ca1052d0418d1a03d47211cb431619d1c4d8a71ce927156b2ee0bdf3a5a5c0091bf909c7204efbe677d6896f30436d3488f0d5d13c803552983bd81070bb80019a8a2ddafea7b11e274c8910b22d04e87ef71ef31e6b564c2d407713aac890cbb114f26ca51ca28be18975d5e3cad72eeb04a2f4008c1e7c54fea0b3273320e8fe579f779570ab713eaa49e809a205d47b877c03f15ef949cfaf1d038a21a025857586f8b2689c28e431b595ee08d95640b746e724ff4ee2261f774335ee306e6442a22b61217a8b7bd22dc7dff53e8bd71f6b820c3100e607f6071a5c6d208199d8055c58b3b32846e738f45529cdcedf36e33d8202780fddcbfac6c1ac901a1fb7ad0c2c56e5c6076fe99cc7bf798c439b676236962f3dde249e934e8c80fcd5f4dd777e0b1b3758b9e6d92bc32de75709fbc17af01816b9bccaafff54a0065dc79b68bf20d2ef662683ea64790b1850ea7ae7c0a81a6fff078d3564cbf0b796c25eef21fb8ef017365d9bbce935ae78d1f12b55c8fdf8eb85f3a0625a90f6635f771e5f829cd955245ce3c3014c2d9d2f970404a6f9246d969fe41802907537416f09dfeb71fadeb85f45f18f3623cd161bdf265161c15876eeee6a81e05f67538c453256e1ae57016c2e1e37d6be20448b95d839090d30222b2f64b770ca1087a0a1b1f39786adade74fd3482c95d9b5bfed6f43e786d7f1770662b7c0517209f756321c25001f59eacb8b702b685a5809475252c234987be13143d4e0cb980c27b99f03a164cf9d9d7dbc7e446bcbf3592028519489c2dccf22ff8370fa2f15d02a3b9829e8bbf099d01002bacf73f73639536e2d6c5132a1233a371035fe0bd2f0a18c8e92247b84e4477c541c0d2ce0eaf933abaabb339a9bac727022e8689122db170dedbf08ddecb5330c40ffb227bb9836862d5b463926f8fd402a9c25e2154a4f9f83b01f7353e244375ffdcc75b2af907a5d2e74e1461618906f083adda2b9090219cf01cc368f1ea6d0638044fb09c8c5e24f918520449310de8258c77401a7912e281fa2e8014d363cd6759e00e2890c162d3a18ace4981025b0745fae2481b2cbf34ed05ef88aa4901984d3a1af4866ba0ae4e6455bb5506e9fe5fd5e57ad9c4c51ec879fc75b8558f3a3d4faf1e3462bbc8173c5056c208a7649dc72e862e3ecc1683d8c615ca59b91ebc98c47f9bd7b97eb6bd449d1e09d619994c28aea2426906ea936befec3bcd4035119ee9614705b99bbb6a5887003d0fc42f3a11d45976cef3850637a705cefc1570f13488f90aa7241e895dc103370781e67e922a2244a729c47f139c1b29c14aaab4823ce17550fe7f98d72d0e00f9650825d928bad8d187c8a980d6124e131aa4bd6ca8eb53f516920ddebd00be2ce2953fe4177d010ee6c523120c1d71538b11bc071433a015672a2a26a405c7d350e1bd2e22ba377e85a9c3cce39a55cb190aaf1a5bd8cedfe25293f1eb06ea2023cc4e2d087af9a4f8e89c85f2fe54cc2e666e37dee5b64aaf99483fc308b2bed13891037a20925cf441fdb20c8477bdf3b9c92515c6c27d847ef6e0dc03b575676b724dc54e9a37f51acb8a2f6af9bede9799149cd427d1b39ddee4df059aa7cecd9ddc82038b2ebc9690b1d311195786b69466586b0260bb33fb50910ee63ba9f85c3b1935dfa7a35cd8548f0670ff5523f0d7b0f60c60149240ab630c8c1960091693ce9a1d484b9368306646982ed7f4b0d869174444ed3e2a20d20e37e022ee30f89df4a8c9a586d591421e549c6d91a24aac7ee1976422619ec6de7aee4e4f07e427b3c47b08c2b6cb979e31e87f0fc9891febad478e95f5e5470086617adc2ae0b8719a5704cea31c3f393c71c78ea4cec9714ed79294d6b74e09e9c303adff45ce67e0c1ad908e5be8072f9d9e3009e6fcad449097ea08bfc20d79a3a34532500083988445540ea7a15f33539f14f3bc43c3aed8f8ff2642e400226467bfd9f58842a1cf383061b86b4e970a989643e65418cbddebfdfe6d5c030e940497b91e3602e1ae94677682e5d40895d06cacd18ad08f6d1a6a6a516e0a09ccb0717bed668a66af2c9877c4b09d5d6a411fa3c24842b1f6030baae3710db736bf1b0d7b2dc86483d4c00bd43915a7d5a6a2c4b002cd71f0e518fd1835069a61b53beeef37a9bbf5694ba419d122204aaf214d6277f04fba5d61e28597099796ed44aa96a2c1241280f9525d1b1329ae800eed8d46f5e5d21fb23442320e59a5e7f3e18b2f4687365899fd350de8a3287a7aa2511b8eaa435fd197d3f609fd7bd3fe8497340491f5556d1ae33824530544bbe6f9fa13255e54b941ecc802da6df3b14fa763e71c44f46fb766f7f50e30b87a2ede94b5d01560861500850e76c18ec3195982f7495ffc820a95b1994a4f5f4033755d15ba001761604ff200d6d762e4308a935f29dc40c2b8db8932dda6601993380135739ae30d3711ff0dbe3886bc8bae5daaf64fa3c5c64c332d306da85dc8bb8454dc3b206a52ba7308fb508542ab363d7caa86a02e8bc20b33e29f2babea9bd881fb32418e5a762d03c79eb0a36371e6168aa5ff65727c7eff8ab6d2279b2809bee2685cd32153cf0d false +check_ring_signature 941c2a4d988f56ea26ab2ca3ea97c181261fec71d20b92783465b8d9cec9b71e cd7ea288c2e8a9e5cbdd166df308f30f40d71adf899c5e0146e64f9c067c14b5 1 72af596dfbf42c362dd7a3db5031f0b5db9c7c1e26db0ed721baab5887d26a31 29ded7a461fab2a6641f9af8167f07bd42ecc4e0f94bea96688e38fec684453543101375673ab9ea89d9ceb21ccb2161c8dcaed04e2c271ec8d73138d828021a false +check_ring_signature 20588d56e73ed72ca2287353688035bae6681f153eeafeac0e4c52e2e2f13918 a0615da7c1a942ab0029b4623029046df7e58211377e8bcf257b88654ae65127 3 97ebe9e0fac6c8131e28e7ac8a9496199b600eff1e14fb876b75d53ee4141c4f 9e2c2a7afca5746d475a8dd0bfce1fa8fa0fc829ccb51af555c03ab6dbea4a8f 24d059b9484e59ade043d7ac4bdc54b35cef8997552cb44166483469abc258db b7c0c9baea92c45a5b76ece2033ef79e1846c37234d5a2d8c13acd83cfd6d202de644505aadc0514928b2aa4b8a2597a77e1b976e13c164fe1c519dc2a27030f8d0bab01bbb678cf6ab25f0b846d02ae51cf174146ee4ef05b766b1664f5ee0b641bbefd75529def601cb8ee95d1488abaeeda932edd8a9fdffff8cafb93b90267365e95db83e83f45d8aad14284568ec68afc7a90dbe250d642047b79cf070d768c4b873ba8595530a78715ab99bc56c8a0e6455ff047d89ecd4c7728078902 true +check_ring_signature b5f2a21c22bbdb103186a6f2c52e69b2ce5c1a066f2771b30fb25240010145a4 87999171127f92e925670a0e670f8e31a31860971168a94c95ae9c33f728549c 3 10439c34e7bf8e958af6680e3e381bbd49c338bdad6191a943f3b0bf5b4d8e7d 98f4421188e2cb59723048e1117792cf40f43e1c89b7287b79101b10155380c0 a2baa508f965f13b7ea1f8ea62582cbb21e0e1abe6f6f1406dcc8650a13133b4 0d72bfbb94f6d771c437b42dd99e4305d9d99d8e0af211307e4fbae4802cbd1afaca60ee12ba4a4b49a8a979aa834d14f3184bef732553ef58a0c3cce864760ad6013659598b561d06af02b03c6b58ed80a4e3b45bf4b548c8b106c338081b02d724251be7b25dca39a8643af19c2b0ef54fcc6e7e2f0eb8ad78f745d844b506a8da6c0916cda7ecf8ca68cfc434b2705ff3bf09efec958a358235d3de48c60fd38e963d0d59d535722a8f891b4f5c4e89264280e55063459ac736d354632c0b false +check_ring_signature 9df4be13688b8bb7e7f9046787b20f50427aac06f9d8b8735b1ced12ea960502 57a0877570cc7335313a5b8a585108185778e8b8194d0626499e0c1338504017 9 632a33c35591c3748894170c8ccc49fa13ffccbd854ef61a8779f1dcb0707836 651e0efcd2e35152776980943f0816f015df729f7fa6eaefa89e395f016fb8a9 b59217da4173ff6e73ce0c9f5d214d8e47995dd27ff0d6e59fba19e3e5270eda 5fbcfe7f9961d07e4af5fa0ad7669af8385ba699cc4ac38190e01673bbe079fb b81170923f235b30e740f8ed236beadb817d4ffd44cf37f062937fe160a5fb98 cc361a6213d7c9b1f9c28fb745370f4a1fb2eaf3a011e45b7899ee145b3742f6 cf594da1510ec62d69ae35f9a14c3f8449088a670432a1c5fb1c4daa25db909c 96bdb402ad76481bff5830e2773827edfe41de31eaa8426a673fddf3957b15a8 744badaf115eb47b4ef0b83696122af7f501fba3d3efc237d99d246644a43f9c 9063be5421d8db0fc08f8d2e2e227c9231604dc83f3cd06cb3bcd68ecfac4700c87b32e7dccfb0c71ef19412286887373b216c00e34bef726d99937a6baa430a82e50d09dc1d3582118d57900e407e66796a5df4c0b2c99815aa327269ae7b017c8324418f9017b3196066ab46ac707e19983b0eeac3d37f48a4e1ba33a52c0a34f9d3075515867062fdf1ebd77d67b4b0103f0f4c06ec9ef114c1578173e4038c949b7582a468e77a287648311ef30ab31cb4b4bd32b6e474d68fbc5959cf046edbe3f6d5ae1e698da9f7c167cc5150756561f848ab84e7c08265202f295b06d76c6de2da4b30c7ffc2efdfbb1f0cdb065d514fbbc1c768b8400afad725c2066f9814689011923ab0c9f2d853fbed5a213a049609d30fce216aad832137bd0e1ab421715e981a1891ac7d7a14b194d2efd4c4b794021742b51a59c9283a0106e73a2f927b2711939c25342f3698481be739c12c28c8fd86b1dfeea9be28c20e71502f216b22a8d5ed2f0e0c7ef2c720e6a39e22b15ef527b707b71cf533bc0cf5c298346ce53e21a867ca2ca5b5b3a325f3de7e717ec611b390ccea91003e02e97025239f9b85eb9b6192090a62d395231d31da90f71e3c320b87250184150abb2197740c4e6c1abad2072410d5ca909b716298f8dc468020b66a6fd592d402dabdfa1d39031dab48caf25d09251a389641dd58f286a5fb00bae38e24a8c106d22a5899003e3994afbee584e9b33f65caa212a7878ee2fa22834cffdd33e707bacdfce1ddf49b4442bfb737e3c1673d8631a7cce0257349ba926d9b18f9cf03 true +check_ring_signature fce9e3eb1f6d1e28d00f2642670c3919688e5cf9d1afb236fd35f6360d28ffa3 1e68239eb9c9ac90cc33d879ca3f6bdcc0af01e0f6c54fe7dd2d64a85fc524ff 168 18e8540ec55c6bf414233219b5c0e97ca9b5a980f0e488a95a3c887cdfa1c9dc b1f898840ba68e758e4f5dc571e2673b3c65b108fc377f84ec19bb97989de253 6f6e8d9f820dab62acbba9d4c35072136aa253a6017ab2d48939751a07f9686f c2abfcfc0358fe617e2bdcc175312e68ee6f415324e93d0968d13451b89b6a78 7bec894bee910d96bac35fb52a897744af438e96c6c054fa19cd6dc1e9fb0ef8 925611befcc27c8694fe503441b51031f9959403ac7d232a1e45961778b908b8 d51d444c9bfe2440acd9b9989da50ecfc0111a435f7e870f9f1a4ab5b07c5638 ffbe61cb1e3d0a30b19a26a33e24745b79a26c83e4e3d2c9c7d34501af4a7bc0 d1d515f85d0a2388b935525be55a60f85d0592200a46311500de7f46a9d07ba3 0b16c353fc495632cacc7e9f406c311bc5770cea606064ff8a66d7a8434ec12f 4002460c6b33796c16afc51b48c911f065b752f4c517a307230b3cfb9132784f 38aa8ba8e340c2c45d95b2257ffbf2f016a76cb7dc946e50630b086a8a3a5270 545ede55b6f682366a3e0c79b952ba0a3c9c8e39369231ea341ed1db2d660de1 ef6f5b9b8056a0490491564f4911ce6da307e465211f0c5875a4d3728ce7c6ff e2837834d8e19a25966da774f2610d07573a929ea4059f640cd113d90f9b919f 3eda97f03c775f4035241ec932182df42f1cd7d86bd28c677992d10bb4566dc1 179591f7a4ca38f6acfec717134052c8c1e263909f47f3924855d4086c3eeecf 4bab1ab4a9a93fee727da5aa9296aa5ed952a4aef37eea1608226ce55ab12d4b 9cb2b86cb7e5c91b008041d1c3d6cbd81b24158bd2f90c59271664580d9f8b43 cdd59098670f47eaa03ae85ed89fb8064ff4635c40508e263a697dc34f19e77b 36351d9138a32f9a0c727c5b0ea203e6e9e9831888211c7adf92ca546b085b1a 8e8611787222a8cdfbb8edaf789dbdd033c1ccbeb9317e4bf9f0f4624dcb0b9c 4b865aaf96309f2139cfc7ed8e2fa677f3e6196de3c7c9c8f96df0a656b12935 e6c904d97c58993b245855eeb07f1bf2c278e5082c4b7e13a45c18881f06aba5 aeb601d769169ba599acd0aa2801ad4f89dae2bf559bd83843a569ff580f8973 7f49d092cefa1b392bb498dbcdc5e03b98595709d31e3baf88a492edac4cb70a 71cd92ebcd22d526e462b9ad94daa958e37f8bdb0642dd60161d068a3f8ad2ed fe228b6d5501cea5e752a168fbbeaf0fd330f710518d9e4562120cda015134cd 36b132484682408134015c0f9c736d444caa069a241e2614c955a88ce08b6019 a2ce5c50e203c96608d6f5d1772d1ceb14a157111008a9b6c7a9b1fffd5da9ad 658b7b37147f4be69fde9c734b3759dbe5153e4daf88fe6439fbf634df285485 2b841a80f07e6fb0719a3142e41ecbb2e61b3f4a6a71203b7bb33bf1d53989e3 e52458d7fc61388b3e4786fdbc6f06eb5f73033a9d23537eeec023c1733cc970 ba53b322a861795e5bec0946eaa0701f9d7e1b03030f3c5bcaaa4fd98ee4ba02 1aea1b155ce0cf4ffda4fff883ace7dfaf8adaf7475bcd74933dce80876ed7aa a57f94995ab19f4eec5cbf1e68dfad83db7de43a94e23b506e2611b3c6fdbfc8 bce910ee6c15eb0f8c86ba5e3a846278f063c900a520c1542ecd95c601d6f7ee b8d7062eb1842ce2121962e14574a5135b61eba41de715b68edf78f10e185867 7804d11c0a60e0769801730a8c392e719c7443f5348dfeb41d4e3679d7657f96 ed2bd3bb940d7b38cef4bad5e4824ec44f4eaa756ed9d9adcc3c6b918ab73991 34f040a8d408cea7673861bdadf9cc602d2b195c9d30b5d2baab7f57603dfc16 a8fbc818acca8d44a30b223b371282de1d142a8c2f914bbe2334a2f3de20cba7 b3e37ae1664e798e0eb672371673bac6149da65887d7ac94891de47e4ad420dc f5b21a50f2df4d5cfa3d60d924fc94e4de5dd0d511dc4533d76311f91f9ca7c7 b0493a101c2bca2dc44b09cacf7892da477638ac754c391d75d03ccd96c9c768 fbd0610510a458823b3eac4e24c4423ef3c7cb2cd71d10ae898b0db6f369db8d b18da5d1c34e53b4a4e1db5571f02dd8539ef2f96217d29b1d7ae1b0600c70dc bffde023085136325d40c1b1a7ec822a78ddeb5422766d985c37a69a5b1b90f6 686735abd27e3f2fe1a7aab5e10022a6816f53f347ce3bdbc3d87a904def9cc5 758f8d91891deed1c3f6d8e1ea8620110b13fb31aff376f7e1eb458298b57981 51bc8279af330217e9d38863c6375a8f010541a0e812c3a72910987ab34c4d61 88b2d1f0e8093cf15bb3098cb248a0ffc47c4fc28e019b22b058682ac467d06a e2057cef16872ac4ea93f9e3b587be095ebe4e7a3d64000effd0367e2b938c25 557b9984e4feb17267ef643ea2550ad734a8826bca7d9b4974a2b414e5a9bdcb f958424fb9532582828297255150559b8046bf00cd9981deb11e68a492fcaaaf d1fd8473e258aab04cd9e008e2bb1ab828b4b4d3d7e5033710a16d33da27593d 008b5c90e288be04b38b3e649b345228b18729ea799d7344fdae15b0446f2c5c f67d4b3f888a10f7912243e6f4b4a2c995669ae917337815488f2b17a9983a21 a1650d87e746595f2cea5b660549dabef4ecd24a5ad4b092723152f4594a67f8 6e25b001bae240e01ba919d47702ff3c0fdae24ddf5c1d4fcbcc97280dc60108 6885cdeb579e98514abb72c9bd517be2ba060939631b92b2e2477196b04dcf9b 231e5f6fb436d9d203a2d952d8c03d936180939a1b0b209ffe2211547da622ea e3e981a2b7781c0c2ab5450cdf61175c3c2fe703c55490141fede2553eddc756 256de27c96c5e5a7de178dd0460b0dcf804c0ed840bab89672c98ba448ea7570 947fbea3b4c4ed3afb9e940f8523b8ecc24ec128ed91b41af73dfd24d572c6a8 a8032165201922d2f77622ac0fa3a0ec5d1ea8cb7511fa5611404eba9481c833 1b7b272098c6ab1a219928e6c3cab670005a4c8e92f49c6f9af87c76c5ac3156 15b4b72072cb21b76c447e2fcd1bf6a95a3477e041f6f976234c16c2585243e2 3194424af6d3b0b651aa89e44d77fcd04b270fad8f7de1c0750b2da1cd40f35a 9cae4482361ce0244c330c388134bb0afa82770e221cf81d5a1b2b6e96093840 12fc8ce9d722a079aa66561a3dadabc7927e4f123696c9296216e8ad37fad999 47a8f9f43370208e931802e6f218f43d780369dfba7a6c952de56ffbaea30d5e 19c7502edab9bfedcda692dfa5d70192cf50e0697401beb8ed98eab204f256ea 79528a03dd6e4729ba691d1d6a62e03046ba52feeb76e642e49e69cb469aee99 c2fd3342ab9fc30553809b0c8d87d6217e451ae2c372983944a767e108d0e90d bb50449566c9018d94c4437d0fe12d43b469c826549ec88c7df1ade508eb0b8e d3696ef77c649d53312591a6d262f2da1c869a835b833c6ef61051799c48520f dcb4a2c840b8b728daa4266353dad5f97ff94c9e68eb0d5946334cea45ff107d 1c099b7531641ca7b6fa0081b49fb002de6360c37b0a937d6e98cbf6a78ba7bc 13471358f158c74f69cd690d4b26e7bff0a81e9d166bc134346f71240bd4285f 610edce6ddbc58bd0cbe013e4278a7bf99f5560e8e211def36bd192e53a95746 2d4e24af37f953760092c509d98f80b8b05537fdf0f91db33ebb9bdcc6f7ed9f 3d9e89d242f7129704657b2997cc15290357d96f27a61cd2c50f0a8f54899f22 477f25a7d8be76260eb88fb019be09297b0c890bbf657cb574dbde9e3a4844a0 a2f84baa85e29c9dcab0f63f865d3309301de20c6c792789d7f05bb28d61c230 e54c3e6c86715c822604bd204c6f562d9752d2f562fc9adcbda36395278c9066 a2038b16fb5174a0d3cd6ffd127d0fc2399e1aed629e3e4938a54b37f42c0e61 58e562c9ef06e8345e68bc68c5a8732c3aaa050f991c79226235f7d7d216b785 5fb5f5011a6ce460ffe2f15f3677b65d9576f85c03bb9c95e4127592d1122a90 211d0f0ea0a4d84f268a867b2d3ca9880ed2948261c4d90c0c3cb42e925dfec7 002b12833c5a236c5e38cd4fcbf3b2b3162e2cfd74a8f5ebb9c3d66a76ac2393 6613a04e88954d0ef844ce259f7fa626fe7e3b69d51bc12b21752cefa68c64bb 17a46f5373524075d35985dbb071d79422f09e41dbee5e16b600aafc04894c44 ed757d44eb5a1b77dd0d94a324265dce6651b129f8ba760eb4a64b3893914bf9 e5c5a4b4426a34165ef84347af8fbfa67b783bf4246bd8fde7f094fc2b55b000 e7f90bf6c52429931b23baf1fd65ab51afc149813f5b7e10346d1b3c5807c8fb c91ee36e9fcf9474f1c36ab8da8329d5d578361680e0b6109a5f96d7a1c0dd43 d8dfdc059f2e374dec5d0d002fd1dd099a8fd6e4208afefc217df8850f777f63 fbb886510a28ffec2bec1169d7db781187c8b22f614a306b2204c5fce7bb2239 40002286495c6ecc51ccc3e60b631a9eee0d1cde4ae22b25fb15ea4a53ee9ba0 bf385e13e6d44b88081a3de342a7f3c67abcab58aa81f8e3d7df1a7018d1357b 95c292791574d1c0381ce7e58fbc1b87b88e0c0117c3d062894ecef8abbbd27f bece38115d551bd31987f3402498f347dc70c4e7446b83c387d03be3df8a3f54 7aa98dad008837b797abd478f949441923baa9586e824669bbdab748477672f0 fdbc24fc20347ef94435935d81ac2763257a89b5c2dbaa8ca89a5f2747f2c3eb 8b97509f8902104d317b6c3fdc5c19cfb9c73eceadb28546e20c11a5be7af4c5 dbefbf83ac2fe7c950828feec2d0a832a0bf90f17c22f5003ff511dabace9f27 6610fc296bf987aa50d1a39182b56bc07487072fe7d1f6efa1dfd1e93d6f871d b249b719ae541cd83238534db1d93ce73b63687c6e9e5c90b2c9fdf02f5d831a 370fb3885eea1f680fa537d844a8df7c5532fbcf75235555916e0f0ce53aa19d ed352afaf414e3b3b06e0b63dc29f435367b6393e966c3d0a5b9064feffec0b8 87de6cd2f577767c5580c4687182c6dd2f122bb655afc580622409aae8feef47 cc66d369b3d95c359cd388e057d703675098fe0024e631c509fa84788f73ae25 987f6f46f895dd311e5b2f79f5a7db507223b93cb2e9bb01225b2e9fd57d2310 6d6cb886b4c414c63e725a49cd3c583fc9b5e01fbebb4890f2d6010d44734198 80f77fd24cf32ab33900f15f1f52d4ad446d78ec039c99942495669f2851e117 000893b7a66e25d4767f91e95fd01b9a5c0de944e2d393e3b19a2fe36ebc28e2 84d0ab0a51a04537c2c9b98ac63cf6275f473036e49c0dc1a2a907bb9f527fa5 30fd4e03f678dabf50dd8ff9021275944520c777d71cce3be4414589012e7151 5f9d12787aa81c93f04f21fbff1e885d59775968b5229232e66d4e46b5671392 73a0df0a891cb2454a737e98e51666474232750538e710adec39460c578dca05 11008e72a7fa8ac299ba739cc5204c7c0245f00b64459e8e1103c47d0361ef34 6cd8580bc40943968797b9e064292288e56cb73bb3400aa6e46a37a3a9a186cc 3522802472f1e37c574301a24f020cd5efcceff6ff16ab52f383ca64119568c2 3e4130c0aa72f84b0a706f8eacc16aee3c68dbbe9ad9beab153c2942656867c4 58974e9951a9def409c7c98191abe5a4bb97d801e76c9a4a9d45dd911a192bbc cb63e050199d5a0dabb9e0b5c95a27642d6710caff61a1917c50b0993fd4d393 61312126af0e984c02e4d35327cb52ea9ccc32c8692b24b6835f205e058bdb3b dda7b33428d89a53eef039760fb0d834c3a2634e07ae51efb123640503fda179 d3fa08ff4a7df64097972cfb1a583d0c1e574b3afbc631fc53aaca78f54cdb2a 0aea5517ef6c3c36923b1fb5194e0a5f600cfd101e8b3a92233127510eac58fc 063814b6fb5a357bee60c3bd487b0d4fcd8f7af27759fb4d549f4e12c29a9be3 ae9b05b4ce240d747a6f19624a6fc7adf3267146eb655f93546eac0a026164a4 8a73b648c77cc7face74a2e0865a9ee2365e2781aa838c0302c10cffc11b2de6 08d5f390c202ccbdc08a0d73736477b26c8c50e0d09a5c7cc62fb93939480f89 d5f4f7afdf8a709c869aaaf3b046cc1c97fea1bd90a39d7b158cd57c65756154 f64b2cfa5d8d1acfbe930ca2230f3437300c6612164ce4fbf5794fee29e71d2e 43f3930733029410b5adce1f37b009c74ab4d287b11c32749c606110c725aad3 702b38a92541d7fabc06c444145d26cc14335106a4ed53f2564263db4c9c743f 540c950d1071bb86d8b1f2566f7df5e314c58dcee574dffea5f8425799ce732f 6b634ee77916ea81e885421767a4a688bd54e5ff22df92497bc09770581fa3e9 77c53368c3d1a19a1995294c9a986e048b954e169d78ae246d3bc5c0ad3584ef c6c3bd11f6e5b0379b88f715f036a1314379c5d67386f6cc2e8e2969f3b2b341 6a25a8c7691a24f2ce332adbad8b4f9ec85e986224c9a37825b1470ce669ad57 c3db2034ecaebe7324f50496609cb9790afa86cd3ddf49b0f4c6cad9e6f16266 91f1f9fc72a29f61f89909e3c58a3d7afa3ec4f40cd6f59002d3e5cf9eab21e3 fa805629348048f6d03ed2916265aa4583d74e8af5ad8e3a74c575d7120b2ac9 5ed47c7b1ec9e82b12c08d45e6c05a93f9d7686f12acd277ee4c2fb62ff5513d 0876bcc053c7d5e8bb917fec822b442bd9ab0658ddf6c8e777a6e9d9653dfad0 1b2fc4fd6f9245d97ba2e1a4998bae8f45775f45e19d3754027929fcf049f0a3 550832931d559fa1660c9ceae0bcae74afa9b45bca9da63a8ec1517b84388cb9 ea7a0dfc57d1169744ee6c2dac5c73b416ac00c10f394fb71db2198dd51d773d b7df9d7becb931a6eb1c6a9385204c77eec050c13865da03a0cfde16776429a8 f86834131144afe7ed74bf01395bef900d4176d9e25f59dd4e8c4135b6caabcc 3f8a169278c3f969d28ad76c6832063a761c834da9b40a7c55e9636a662664c6 78ca97ddd843eef0eb6bd4925bb73b495a20d39270d2bcad95320ed3cf6c5071 12dd173a874316539d017c0e0f1de1cd26101779eb7d0faa8759ec690ee02156 0d482c220475d4d85585d3664460c82fb387e1f38a9cf8ffd1a60ac73d268738 fee2733dcb08f0670d01155ce475a3042dc227f8f67adc953a2b8675a3726200 e61fdb4a4f97af97d42ee4a166f1a4573fe1467e3cb555efc8ed1a65b01d32cc 045b2e4a926ad3ccd56cf0a57a2af576bbec22b0984181b26a305388370f9537 97ce93004110d5133bfb043653ce9425a19fab6ea59b536e33b8b78568a28234 641fe2d6d61a6029d6173c7a9a8c06258f9b4ad340db6441ff6d90a9dd6e411e c0091945d003c1a3977ee620ba20d9c87dd52cdd783c8d8245d360a6cbc85d4b 3f5b7b23612efa0f6c5a2387a17a54a40cdc27ab63ce1bb44458128d2f962125 c5de782aa2326719d5d435648eeec42c72c143651e09ffdf7cfaaece73b99628 128bae50044124f23608d1d9e3d575addb93ff2a513f3a8bc90b9984c9d41478 9332dc7838a61d5bead9ee40c03df49a8cb86a24f1c6e4d64152b5a2649bf020 75e3b22a72d9bcb31e7708dc1177f32ac22d60ef48ea3fd61ad125b8cb64be0ba322d21a3f448f36ffde9fe9e90caade3714ee8c692fa2cd1e27363e79411203bf7337f709bfff1301797ffdea9f56850b60796b21247033a14c15d087f35406f365f19b68d11336fa7709a3d2983aff794f8fa51890a456f033e2bfe73eb70cf3849506ff6d4a04ef19b0fd54b22aab7c3a459d7212ad7e6d07d010eb3359094aa2543dbce6d46ca225758e05faa45b98c51f6b98b2a41445667a4f31b70f0df0be3c036ba1377885261553c62e250226113bff03eb6bfa808ddb13d56a510424181a5f301516bfb2ffcce58011adb3d01cfd928566a1b2c9a209b4990905008d68de2cc363d98a05f5bcfd67d89efbe1c5b88e23c904c3a8eb4331d401990a589e509da8a9105a7f0d5a43bda3cd910be5316eac027a9c7a9fd8641fdf6808e06963666b92f3a2cc9eaad7bc63f1dc82482c54499843f45651b64be548fb0b8658d2eb1877a9916c025908a738a51cfdf9dc092bd7df126a0632f6ea82f806617e03fb2d92d70d6bf9f23bc47ad1de7f8d60e31cdcf06b4da4dd088dd26e055478eac48818fc1d08c01e98dd8e8ddd7a820f3185fec2f6fae7def485985800ffe944b05973b61b34f8398e5f876cbfd844d185a33dfae1909940b2c0aeac0b1f3e33a862577f1640767c4f7da4e3cc740e330edf3545795e75c294cdbafb067653f9db8fe27501e129cf03f15d4131c4392e8f3c34696aacc817e87863750d264003505dae15dbf6d56a9132ae9cc96b1b8e978d67f76534d9d0cadd0cac04121d35efaf753820cd93555623fe5460047c96aab4da725044a525157894ae02d9b635416e800581e7b1abf8e0324b63d4c282869814d5ccc2cf4ab65a105d0c269565c6e7816b18ab9ce654af6c6757503cc21f897ecf1c76055f7d18167a04caea97971553776749f9ab77a5f45d1822d29b2dc2e53c74d35d05b616fb1007c1cb1b13bce32b59c04af6d9be6c38a82d4d25ac2aaccdf94f30b896f79cc203653748ccf4f55aee73719b942b64e5defc6e854aaa6d25328d013db27369b504ebbc1f5d5060d90bbc479caecd41c8a69cedbf101b22e597972e2cef43a6f60b9b98d04c9b68f951fbbbc980452b79d811ed2fe5caf6d3f56841ae7cb226cb0f22234573c8bd9ad6f13b3dd346a59571278c0aeb27a761e7eb23c7808dffcc01ba433c36d71228193a3577f02646c219ee8c6cf662e2f52449b979658510430888c75075d79627b962ecc245e6c4ba10e863a3c673bf1dd7eb3f49ebf688fe0772de12af261e000efe2bc92017fe6426d2dc165e0f226948b02a7ebbd5a23601d307089bca406f9763b2e06d0ceafaec8fc2cc00942779a5905212987784d8087cc7a73d94e8e676321d12c2a6bb21feaba4bf6ad811717316e6ef3cf9a0400091b60d4cf178bd959ad2513d8625ed974df139d814d5b6f1d7b6fd3fce575b0248bacedd9c48d4794f227339c41f117343704317c529940a082cd8ce7e162106317235776076823eb09c22745604780ba476e2701ca3c8b0dbd40ca0310b050e1184070532cf29b861ebd52212ee155760d9c83053dba6b28ca7fd81ede3b30299f8e2a9f86e87f859661d3b4eba0d5f0e5a1741240fdc57cdd5fc620382660338ee05e0461ea5e85385e67557d270e2f68e22b1e1e1dad8b5506a07a9cec809267813095dfb5579f36c02fa6797352e76a41f971af687686006e7d275f6e009ba9fd1c77177b1653eaaf61ab58d1147631711bac10e6c80f4f4dde625020b0d323c02a8e8e67a4863a010e30e8b13b0507ad8655463d3d3251ed22875e06f0fd5dd936dc2631953155b26b065ac997a3daf8d33ea5a16dc6060b0452905b00fc3ad31a00edfb8ef3a77e22e0880e2c98b8e4d18f70ced97b6a636529dbf4d0efd9b1fa90a60dfc854e5d4f0524f80cbdaab2d0b7b0c797a06d3871cece5ab0ba0223887a369d268ee298b0b76f073c1f4e2965114191b2190c80b99f47b4506f937dfd754bbc5821c5b1c858dd50895bd43a3d7957456219c05de548dbcac04aafd34f43710e4822831686928a465f8cedd7351fd62b47f381bbabc99e2ba0b92d086342f844beff7bd468d7a171f9996434f675bf735cce84478dfa8809a0519224709c071c78a3dd3beff4ac8b517156a76e2ba9d6d899287f0d39f061c08dcb769b10cdd32afba3cb3da53dffb52e68f42121b9093f1c7c22f922c02d30f67ee28f746c41f51ed7b771bc6bf31679ef4b2392d6d46c012a8ff09b5015c08a4d04e262963e5d68212ff68b35352e2fe0a6312ff13b5985b250675aed9240288444f7eb8e1a03001f90b8e1a4bfbd3c403602439a0fc2678605080654e680f8a7c9adacc253733d22de5202ede74688c16d3c39b5d7d0c0688a3fd6260ae04eac06c06dc31eac5179c86cb7b9c1aa44c68f3e984a68a81a61c369c30bd580a10897c73515c74a0b6999ef5fdf83785516a8818208fc75e1658110c1e4c3c0cd98ed0ee594f0abe0297b3b58e0c26ca78c37ea022b3c95b857460911a1d260e548f763a984935bc030b4066ec498d78cc7bf1aaed6e9cd041dd0797a7a67f0aedcd6bc48fb7586ab22111d291401705243509e7c08dec0567d3cefd2dca110750733db00c42ebceb9ca9e39a112697da951f7e5cb30ebb4909d113cb172220b713333c3615611766a224ac39751f0554bde73e98aac92e262dfaef018b3cb0ee3c47b1470bb02a3687ee7573b958b1c0981f4ee3b7d9c5aa8db213a5d8bdb013f9cd4807a35d5563484aaa52b53a02c7c64dc56d24d315846c8530bd9547605008fbdf0c81d9e614a982f9ef7a9782456aef52e51e2c8be2345a0932ae93e0b327f3c06ad26bea25a2ad69d1f17bea4e8ac173abde5d50d67b6d05d01a75109e0f2e9c78f11649ac5102f3b4777698f92290b9669c36c66af6ec8e7965589041b1f852b0fa102445f74329e6475c003e7b6da77275b14fad5e7ffb7f8722a0f7ec9c2d3af13541c43791ab7a8ec3b5aff1ba714a1dc5b3d0f8394b7ca1d3407c02872d9b58976307b192991fdeb80b692371f4a038828765733752b3300c80b74cd65224eb966b18c4ffb86c5b053ad064e1a3a201fb23255bc4e20f988ed0893f3780ca5a388a1266c614efa151ebf7607e09fc6c1b315eb0541e1c7de2f0aba20df9cfda3cec0c2ea6191620c67807f181643e16bfe3a0f0115e92f4ccb073404c649a5c95f2d3507ba53e71b393f57930940aad4ac11a50bf92adee32b0574abfd243281dd346703ba7d5d685b32b2ef77fb097ae90cc1236085c322940ea0fd7c5ad0c2b03e7a14a94c13158341c70f15bb3244aa5ff63ec5394a86db0e436a7f699cdf21760f3dc415ee3db1f4ac2b2eacd148b475d6baf4b08f9b77061cb98874452aa42c907aadb57d3b5ea734c955d6f99a5ccbf359481d4b06f6075497105e49b32f378c2d557c1a6d5de27fa6156e0cc77689804ccffe4b29a70a7147c91923c79fe4eba9bfe77c14cb70dcd777db3b59341c76bc8320876b8705d7ef3b4a5f37f0f5d68a31d24b56ff187d5f19f4009b8be125e4fecdd243b60c5bfe5e460d1a5001c6129ab1fc9c9823300ab11b56f9f11aff808d1ab24c450d0d9036919cdbb0de3f91c57ac1f075249590d4408cc30899d871fbc0129fd30af8356506d0730d3fa8fe893f62e3dedf0be9130255049bb20beae3fd19f85a0380989b92d095bae527451baaf7e3e94477e169892d369834fd6f67e0bdd7d805933a3cb59c35093e20ff25db8e6b9b90b6fd35ec64f5069b4742bc194dd6ae033514b377f430ac664b2cf072bdd3bb113c2a69a7358c731292c0f3c563bd8208810c618c75877018120dd99127b5c7144f8948af289aca43188f000fcd077c09427370eeaaaf9914ab265bf852d2633d9754abc7fb52ff5f184587a4ec48f7054616b0b3cd88778f49c704c4f9f663f73d2c046a7c10b9c0370eea720da1c207084f554710ed5d1311d329eba2582a6e482081774a9bae291b49a536822a4d06272eb136e09f2c3a1ea16305077235f217d7d14497e7d3d2e5f50c7685778b0b691168077867ad9f8eb04b399b9eb3e9989ab0bdced242091f7a2a797fe2a8090da9925dccb161ed4d4f5f27e20b9626c89e48fc22ebca4d65e893bb585cf70189000cecb264f84b270a2097693d224ac8a30a8d4e2728d3784ad078beb9e6043255037645d4b4569b57888a803d4ea30c904848d455b54c53489886c9dcf10afc4f5d3af07558d2c3e5bf1791e4fa557d2d550e98445567f2181af48f36550cb9db22cfe23951d771e0748c205c25ba4641f35fd7e087293bd4a32ec7a03b0895e70c2d3bff5093b3748ac7a2b67c124a6d7c163930691a04a38dd145d69f0b152da1e81da6f241f99f7f31e2088e616dff76d514e05d536ee4431ab7165903e5103c4190e9a1f1920885b3da4533a7f00b235e06cac067f804c818641eee0b3e2ec607a4a08a09c6e26e649f7563c5ef88a10a26e0630adc60d7d3a697010060ef15321e27915352b20759e121d248e9322e02d068ae02c472ad39b00a3007161a6751a8dee4c8fa7828d8a6e171b762743a67988874d748db07dab3d2310a3bec53681dbbdee53e56837c27955e20537b796ddc6328a0c117118804dfae03684e78eb3a802f2a3385014901f06b05fb15abb4c6967203d4430be20a84f300024f59968261b8bf3ba73a94fca40a120d6e5d6f553047df3d1e1749617f2b0ce0cc3f3e19ec0ba0d1a378b6d4176c4c3a0369c31cd3609df5860d44916c2f0d69a6da5ac138e7be954162439ce4361e42bde4225a13adc214c30ca33478c30d97296a857ddf1ff799a74a4e63c571f95b97e19351c626f170cae468cd94b7030a97ec9c676a0ea5aa559faac7f19ceec18e77678700cb6c020b78a26339560e8d48037a1ffd91bd694b96e35905f50778509174c1dc611051d54283d6a31a008395a068845fc834ef77e35315fff6ce0026ea623d8308e71cf402f8fb1a4f0ac7476a021f3b2f83726734d7d6819b8204c8aa6cc26af0f6e6b01ebb988ce50b2f393249cac41b75e3d7436d3291c27b9aadc1df76d6a5aac9c8f9e33191d50ca5d4160c98b16b5afd6dfa8c0c594bac7c96800e7c11e0764e085facb243500c04ae6536f29b2c29906ea1f55e50560be604cb61b8dff9ac61803374da4bc109d8c7f7c961eaf12dfe749ab996a4fa4ce71552bc41a05e3099a3786ece89200e18a86dc18c38d254cf15ac6a0c5d09291c6e1444213975e5a6b1ec6184b6550df43fa8e230514e488a331d2ec23155c9d8f59e25c746bdf4bf8d0ce44ee8120621a7661b437fd61ced1d2f2c21eb8031e8a007b606118943166e5e102a9503096c541b26e2f11a9d863b09caef14950af6c803707a652d71bd11ca12d9da5100e74348c113bad745d6afa1e87400a4c69686334138642278a6a863ab9ffe9c0f7db38b48f14ac22c8291e0a417ed8fe7f4c390e5cdf51a4002d68b679672e306559e0895abd94d4cca202a5fd98d36a930c74a867d63009661d2ed3844388900f434af939cb0ba4f4362857ef0d33ab0118ece38c07f1a051424e6c9ba5dc0099715bf1b3c9124d3a861fe5cc0cea3ee59bf4a8a2045591c160a952cc9f74208e0d498c42bbb825c7e333c117fa59b6d8421ac7fec9a27ae4efef83e0ccce50c58400e75ccfbb394738f7da899c4ef0cc828aab75e48be44a3ff5bdc045033072196a6c50732475e093452710a7b79ec1c0d61cd60e3a6ac72a266607f5c7300d698834fc4e6359048c72a068618a199370a6952bda6fc62fa57e8110959730fb47aeb0350696adf00ebd5ea2c5e04ee9dcf46ec2ebd47b7b9a947e8de3e500ee7cd78c49c158d2ca2e1a21118c8ce3cb430137f20ca92d62f5a8af048a3250abdfb810d89a483dd736b0517f8fc3c7c7fa4a7a787aeb98bbc518e7440c9de022e648a46e56cf66f2889fe2d8532c5bea4db87b7f3f5fa777f0a524a746c020c6766a504ef1cdb47f5df0671b7987cf26048f493872e5494bb3d4f5e74b2690dab1965d10cdfe02f56b3ecc3dac4ae4890fcd0bba61002b079d2d3908b43cd05c26e79b4dbcf5d678586915c50e19ef083d9f9b0f6994cfd500f4df2c51fc300a3591a22d46f23c0d0f20ff78479696fa7f29f33c10ca842a26efceb7e239d00a745b6502679ba00a726b02dc17027aa754da13d3284ed4f001f574190f4d40677c1c68031c0e3514480d4b832a80f9fbf3188a89d7c7e7ada4af46c65b35a051e528fbdf2a10d926e849987cd2052ade88bcbe488e6f37669fbe9e46cf1780380763e2e6f5b36af5a0c73bf1ef18c63955ec5d036935ce17bde76713073df02a9ff7fb9a9a18fa514a24aa75f8a4965747136ea4052ae4d56b3c13b1a5e4207f44b619253d682d5aa2e77923065415ac5330d4a680d9f3055afe741d3728f0beaf55899073f4c869dfa30a6ddfdb3196e47d5b71db108b355e95c7e34c0aa098bb13577403c58931ab29447d5bfddf118831eb7aed55635d67d278f4dd5f30aa4d08bf567514d08c0c121370970aaaac62ee58ccb57c5cf26e392c10ac66c0f118d0c493fde8d1cb5548593810d177597b5d66097715a0fcaa2391b9eaa980d1ffb33f60893b4a28d6897334ca7e3660ff285b6ea945ff7327a84ab445e80028efb668a00f77f83c436e1161a08674fe7588e65a892f0ca2be747844e7afe0edf7b2e659100e9ca89dff10d2d6249d8ca32bbb026ff646029e756f1a52d34036e6838f2c396455d7676eff0595cb9f70024c149bc4653b0d2a7cc5df91dff0170b6ae4ec5815d673bba35640fb83e45c7c1fe714f0fd611f59e564c0bb80b081e6152eccc0ede563a63d9d1d480a36bf1d2f29bb75a48b6e3f8ee3bade006027466ea0576e1a4a0419c09efef0ae76960d28f16d46548df9d95221ae1d38703f942452f6681200294c9192996c1053e035ce26998f6724526ee34f3105131059899cb97658d475cfc7fc7a88d9e01c4ce66717db694a469994be8d60b36db0572800a596179e40f5723827468c49b43a184f82b1c40d126cb951c9d22f5f4095d9dab677374a630064bc8ceccc7c1075d94404dfae9ee0672f9a99f79f6280ebbdfee603544756db1b263bf387a409bc90b231a4b600ea756b8376dda153a0e03f42d0f4b473359b1fa435327562d30cb710381896a0fd76852f06eb58efc0534f9519e17f7537bcea2a0ab568aaefcb54058f717ed8afb6e05295d3a81b307ce71d364caebf194204efc2b0425456d88705ce9f8627e36f9fff38c8ddb2d0ac847b9b29e3b22ae90075d59c1ca365e813507d7640f401f2f9d2d929181bc0d284eea70da4b35e5aa32609934abea301004def00e26466910a77e9a5cd5ff00bba41f1fa929fa68d9f24a3a223b4dda66f928630cef9b8746d20f0941c4990844f2bdb3fb912af2936297e1ad692b92c038036a06efe53ed36338ca370a0f011ede1f259a9177fb4ab59a90a1104d240d828d8d755793427b36387c9e1aa9087d34aa7582b1ec99c5514a8c4b177125d66d5727695ad4462351491b2c87ed047f41567097e172b6db7a3c00894f8b3a56612486e9d848015d01bb5d2b8a480a88955a625e7035de125fbe838ea15625e21a4bf1c6c2c4b5b460237c87b4d707b477e11acaa0cfc363ed9dd1b56e4e0ccf33233ae4d1b83d7b11f58227975a08861965abf16ce0390f28e2933400c8229805b67ab15cb81b0616081f3a8579002ffa79206ebea1f40fdb1d52cef228586d17ab3753df713fc4841d10303ee30a7b994a6cd22c225648df41e04d18f51f295a12cfc1419b1ac9d6c563b4ec7600d30c4c62caa7b6770dd5879e50a5041c47a4c73042e55b74f0597a555b893c017de46dc91a155b321db2edb37106332bf925dfa522f468c67ddcbc7eeff4da0ff1977f9171e13eba8a242ab6daf615c5ba3128d3cc83b909beff2fa0341e5f0632281f434fbcbba0652bd442c743aefa07c79483238c2d21253fc7872a70fa092da890aea0f4c9f1f16028c6ef7d6134379ba38032e9e8f2b8a2696d1a76e50d2334efeda34a12066b391103acead7d25ee103fc468d10361fe10da4724ee606521a3f9ff2602a55703c4d4e57bff27fc07476124dc436ebc1908961a0c8840dc92060de5f087e753840672c033b9bd120611d6686f098e1c3751740aa9d030f8f22bcd576bf11045973e50f3a7e63ddfbe3eeb9983385af32d22ccbb2a8b603ebc3d143b53b45f6d0a033d3d678ba0e6a0858bebd9410cf8d228dbf5776240d1ef457bee45252d0946257fb9113c9a6dbc766a8fff0cc3514cdf73235f0570771dd42d9b271d93c72ed142d1f3a993ca61360601115850ad0c765f50a566508a4403298eb8385fc38a2f27de423d9d4d1830a85bd58c9c68cc7211bfb0a3e09aba7b1a3fc814bba78527daca4f392251da332a5df729f24127ae7e5c9f6cc085606149873ebc4edc31d4f58e59a391678a4a30e5548e0f6b86cd41c5eff3103ce95b32aed7f74eb6426d12fea8a22dac3ec7df2e1bd030af30c8c42f29034094b0793ba3d3d2b113152b312e2bb8210b44e0941c5fc6a0c2c534f9d7979af05ebc8f1e2461da21264adc665bea4666619f39b3aaf343f093260bbd38dace20346a9f8bea71ff2fbdf85d7e1a7aad698b75c3083e52c98eb4e94151d4f7b4505365e7c4a10fa9f778161199658d59419216e9a597f32db9d9df7620c56103108d0de51e363cf9f07abe3f99ec01a3309728213e3e9c984d5161a2deab4aa8408cc4f91fc6a978fd182ec8dc7c7bf9275e3435b59dc672d9eeb604eabce70eb02426ee18cd847804839b9a9ab3ef0609cf982f9986106d4b254f2eb4a1d093005f71acfe779db78aa411788a26c8b3799661b6aa9909b3be29462d9650f9b2c0f8ffd0706a25606da5d9cbd081a2102fcb95548de0331a4b332c29254a87f4f04e796155169da19cca1412542d6e54ab4b0842ca574b981345a21759457652c092b24f80a560b672e99aebc419a3c4822b433c8a534dd66d5abfe03accf7fd10e05a6e14a445e8f9b96f50cb3fb37cfda0f9a09f226b5c0dffde2add647cd8f00d3224c390b8f1a6c95c3d0b4d4007a50976a0ffaff92dcf19e418e02a183be0897ad62cd8ede2703858c9b5408c7d7f2a5c0210762769d11d292f3bf746a30013319b1422dd65ed19261314446e68a312abb0447edeb28997ef93eb60cf434035e1f87e5a16051842f7f12c65955a05f76cdc58d57bc2ae540768e08f18d6501e648032a497dcc51d50fb56796c97dc841d0a3877e8764dccf2955b3c1c22d087736d69ae86398125621843b922c66e2bbe355cb112b4b75c8d7990d50d5460bb3aa4b592a011ceb09a7cfbc05cb6f0a5ba172218daa2e1db1e417c7fffd13052bb866e5ed815b30fdbefe30e7b2b672c60c301db0c0c9d4b96feadd472df70f9483e64f23c5ee6f32166c433ab24099b2cf7152ea5be434eb6c79710af1680ba44b79e3b1563863c7ed2fb5add9b14998fb4c6ab7db65e9fccaac607efda30c34c9968accf6633ad4877c8fd703bb8385d9685d0f9718c022fea5898b7c6e01da5aca443a019ff883c08e1f5d14d5988d419764020a3c7268d868f8b5c5210741380cc82b2a390b8365fcb2a9decfea5a6dab8c27164936341b6a8b602d570d38d19c3ee73541db6e005b2ada7e3cbf8e3d2aa70667a5066bfcdf0f40605b01d06b05ff31b318a997a1ffb223c61a1a6aaf342282b3927fc0f9ea518ddddc01723af7506bf007b361b6633dca91d4198b227d8d87d21c5d638d8105e265dd060659a773f660169c69a59e100286f37a19a6d5ffa3f579dc0e599fbe38766803f56d7a142b22d98f72bb22e2e44b7e528f871acb3c0ee5932afdfa315024e601f3e2caacd657f29e3227db7bd5827d18bdce30f65883412f1171b629a034c3050cac2846f16b252c2c30cd0a191648b02d2d2b0fe7594767bca400e3065c140280efb8b093c61b0aac9edb78c4321c4c9a496506edc86c4c8d54fd204393f1040c38632799d974dfb28e2630bf9c43ff29d4651f6e33ca22c548fd607ef31103af2bcfc88aeb4583e7240f3726f5d961396a95c72df04778204131b084b9130c4e4ff96a7697dd55a788f24c5a03bae0252405a763e35e3ae6fd152acf07cf0e5b69140c980eaf59afe5eda6ac9ca9f4e0623465e810e9a029037daf0f564b0ec9de538051e0d71875bc6c24f75e94fd246ceda14f94c4713e88edb1c106b407111f8f657a46126af786464d6a48053b1d119387d428c88ec063f6f48133f2025dd66be267d83e8b57446b355268448e6721d3f57c5b5836ac2f52f1f7ba3b0db1a0e42931e99560b965a8eea893b2fe3262ec8997f9093a3d7bbbf096df6503daa41ae050c20ee4da03ea72fba0c4e796b7164c3e32abba4fdd8c23864b2e08f511a9c4c6f59b6eaba9a7204cc218d27ed082dbb8e72df576df25f233a58b052e353944e7c0ce5c625734a6ee3869d7c185fe6498775d1ede1cbe968d7bf1031d78c42e572f2ffdc2993429072b0ba9c4c588bbba87285b63309bca3d04f60a304da0e92f5c44f9880b01f07de6526bea7567272d1bf1a796657344532d570324c8e3da3b44f7a22ef5708b319f8e36cb32e4ee02f4ec0c39b1b8c7d4ad2008f9b0b6bce5699e1dc3bedeb2b4578b6992118cd5de3c042f1284211a1245d20a4abe23be9863be479223d8b91e9ed87f66399d69810600839f1ea4ada00537073b48d90e30bd374d7cd3219e9aa31705b01df94bfc40fc58033c232713d9dc09915a5a5d6b80221e47a3b4a8ad74c78047d10cf91e3d25ce24f2ff3989970f0d157ef71ec74782fd0976a26f8c90a4b99ed54a196af8c0cd0b9f62ca45ea510c2354531be978056f125fe030d5d5f31a1a9e27167ae5d8a13f90d60715b4e202a412d6cc6740071f1a65135bae488928a15940e5e7fffebc8d44f57a98f5d20705c24c6540d6d0119fc9918be99a3ced2546970a99952041693f8cc190c1f3092a30bdf9635c26636642d3cdb7622db497899db48bafcc4cbfbd002ba4821503b89b8da9b37781d2da0e77e9201be6d53caaf5feb7a30e5e3253b0d1d6a2f4073e0ca1cced1b92bfe9b499bee2bb8b0a6e5b7a2269858fe1743a8bfbadd6ee02bcad16292b2c3ba888e260f89a1a3b4d692b29102726af96f3317b4541d45200f367e06244a0cf8a0172a242e078c926d80bf98190039b9c228d175aa93dd808a587d95f90b7709ad7450f3153e93e2e6f03fccfe41f462875919231a3acce02687ee7da5fdf8ccd7e42350908184f94290c67b12ba61148b0ab9cc7d807cf014dcbe82cc0ad1ed56443a2be830a76ab559e8e5cc206cb349c759651f6bb9b09ec059f7c1aa4b82294fb8986d7b3abad53610daea873fb96590c20a1a471fe0f4a39994ad84cdb781fc5b8dd4a37263407beda1840014afed09f4f018684fa055eda2f8829ced21fc9de6ff35ce9e091392b21bce6d43fa4d4a0c5f97b950b0d0a45875350ad0316c33a04d68cc66f24bee6eb5acb99714efa56d5c79c247c0f99f028085d474e3f96f83a8b9b2039029767f12b91dfb07eb474f85e1b87b6030a9791f70ba64efe48c672840dc8fb4dbc0b5d50d27c111fc06f5f71f1b1330594a51c84e651c9dd525d17732a788bf679c08bcb8e20e33df5e2a45426c40c00936723b3920a2a9b020c0609088474c61dce1fd541cf42f2f92deb5ed76c68023270f397ff4bbd8505d483d18a4eceb73e7a2c4f65dd65b67796768517b65008f6b57b8f1ed9dc8bcc2c9dcde6bc10b33670faa538c30e7424d37f7ec54a6106d15e2da5fe1a90d2ad86019c595a0dd3b10a89d857ff266dbd6867abc55a18033d161b27b26f63feb59e859de7fe5ff9972e6dcc4701d365cd3440ab74d84c0f9609f158c755cf9ffdf85a25598aa2a97eab2dfbc059c39b59d7fc11c27d2305c67478be0a6ef37d57d6a95686f6a90bf8b7da6fd9a9ff8d2549928387a43f0d38f8765b98aedd7660d9a269b5cfe345d0559e0ee18c0706cfd8538da3140502c8c76765a03d66c41724aad0bab796e7eeeaac1093c0aca365c0d1803216140ac9c5e67dbf5925eda1745d6379b759171d12adc8488dae1c6d6d6d0dfc9bf40268feb06c6f26f0897b86b329ab8562ad81fadad774b5ca8ec3317854acd5c50681834834212e02961a84607425072e5a82387abe334fc7d3ed038701c20c5f0a1ace1258d294ea039e51ab4a6ec758b9bdda62df135a1d86d72f94dd696ce405e5a52d586875aed06f2cd3958efb43109e4b6ee132fd239a92453f1aac3cee0c0a426c0cba43fa45cfc632e94efc4e0bea2ffdf8aefa709d124c456405f27b01a14716a49da31c6713b7d1edfb088490b26e7ebd0cd3181610d24c99a050bd04145a293fa13a094006e9a53112a7837d0534382a24d40c0ea14e8fc937c88f0384905dc2dd5086d51f8ffaa94b98344ef1d1197fbe0ea7606baeebdfebdd6a0857847e53fefabd265c29f7c422224bd3df6db8c11fdff823c39128b96163e80a3d09da7af00f334b469a133fcccb114e596381f8f481abc1bd051b841f701204931e52e62c9ce20adbe3ef975af6a3b14876107975865d89ec009335e1caef0f83ee1c2fb5e0e8353b1ad18efeefce7cd459c9515a277017fe2667b6b8ffef0796b42203eaab59ce8e92f4a7cd6032375fd0576d0a7352cb670225e39d61960428a1ab1685faa2e0bf0199153c2fd5daf56f54cecb16c201a500b648f64f0e0c2d2283422589b9ee1a756b05d095539b4cd6caf614a83b8c0477fb66b2da7f0f5ef40960112bfa4014424c082c1266aa8d1e57c0457ad80fd41cc311e09ca2027f66f86416151e3e2559f74d319ab2e32c4b420ef765841f2c7b2eea9ab8b90f6c556ba8a17623b1ee3f92a8959486478f5497452b41e1f02d46ed3ea3d93e01840c3efe5624100700cb93cb72d2af4a69dc265d5b775cf60bb0cb44c68871049dd831639041eae752b538270597ac08c65eb8651ac77c4c553d8c3fe49bfb0797dc36e3b7b14352ee476d6b008c5f1f1174b6036022faceac72aed416646a0956523b2cba09b4f9cdc22b56dcda3545b80d6a2b491e58adf7dd1eb83d57ec0d51d394180d52be93838d5c7f9e081d780fc14ba19e881eadab7047d7dac429094f23b4232ea58ba274f5f7c9132a9cb3b0fc67f25c8ca8e2d16cf1295c0ecc0af6af78871d26a55f7c695f6dfee7fc6a97721216c38ff1f0a2625f1af3d42704e8b4d23a016628494897eeba3295b7e70747f293070f168e07eecc25a954f40a205da61d3baabc91ae673703ef5737d4f2ff6dd25aefc1ff263547ff1e9ca702be7a4238d50e270856f147ecd778829030678a1190348467144be568d2c5030fa85099a6448182f9add7f29f1408f2b1a078f3da18a80609562b22d096ddd501f084655d4f5c5ed6396d5bd37a1ec04564d92353f387547d4a8566df412c5c0830c681bc5d3d4885693d8f9eec94ee021a16f97334a146e773f2761bf2df6f0019ae434c2816e083af0934771460995232f87e065e56a4726cab39f5d9e6a20289b4836f1cc64d92de72b427c058f9d83fab47af14eaac95c39d7d292556ec0f618f74fedfbacd32e255347fc1e3d5bd24d5c9e7bf0bb4ea6ac5977c3940f60b7437540e9a93aba8ae7e1d816d0dfc87ad010b1f68faf3f1f59c2ecf8864220e8b4fff267edd58ca5b1f8f7fc2f955f92a28cf13db5ee6d258788492fb26780a2c943462dcb3089a188df75f57c1cce853377771ebd3aea4e9e52b924a729f0a8533599346877efb7119fa15e6083c5c29f2d672b59f98b784374ac3b4122103c5c6cfcb2f8f23805a0a2948c845c564d90e690a5c2eb811f4fabf443e33e6098ae7b18dae8c92b7577eb4ec7a89b275bdc540e29d5363aa059f2aee2c97b5045cfe41b6b70b4a609518684c1e708a2d2986dacffdecd10f5a65007a02a06e04039ffe19be4e763e7a72b5789b6c898a98d390dfe551ab4d99705cddbac3bc03c21cedf8f0f2fe4d43da3c3aefc22851a1e305c2cf1741bc39075c3d24bf0f005866bf54e774a4538435d9fb6410449d14942641e3fef934499ef5660efc6e0c28e318bf3bf4f1493947cbfb1e4d890882747e39c676fdde93b749d3e1a3280550221d91b1ad33a8c009b95143765ccd74c011a7f90aa14ef220a640b6fcfe010412820600537798bebc33be5ad42e0781fac98b4a9f29e15f6717bbe0e46c55120e9bd568e69cdfb9bad4086ea644225971f545b3c1a93b3a6d5f00441aae0ba73b8a762eae72292a200e312210bab807a688f03edf78b05664aeb08697330ba8d993e2012f95d4e9e834058f3b9dcaedc50ec19256b533e2b1a82d78f27f0de4231776dab498b45f17de7ae8a974494241a31b3fd00ae62f6a85ac24faa704e5935f6712140d79dcbca501f8140a0f4e9905ba5e04ed7f828277ad5069cb0f4515cb3ef9dd47a5b96c9fb2a9b412f124269f9b12ac5eadcf845eb786c3db03b337c2ecd2f025c7dfc9ebb55e5131c6394208978fc98c3190eed5280f35de06866202dd60fec1281e59dd15a4ae1838a6c7ff506306f9227b52a6526737730b3fbb313d6b8e10192c237e3003bc7035b850e0e4557d89d1a21480381c0b9b0eeac0a6e51a749d0579b8ddd96e0e2af1c21f4b545f9d2a6964e1e4f34f06e10932b167f75da41330b9ef331a904ba9929ce6f70f201eb984decb416a0dd2bc0f1d27130562e5965b89f17071d54c6078839aeaa4bbbec78a9366fea9bc1a4f02b0a059477fb27d4fc7419c01fff468f1abca50fa655151e100dc71c06778b80241d4863574d167d6213beef8d0ab3d60a4170059bb84fe489a8c4c4c4b849805147456906ab899a5e3e6ebc13783d0ff27095cb8b8ce7583b1708f6370e4080dc3a7339b740b4dbfd3ea1fb0623aa664f75e9ae3951a88a8e7c7ec9bbb91b109fd487d3fb432e693da0cd9529787d0a825b68e9ad45c631473fb127eb6622d0b88711404c8c5058eafccb53d2ba6514e3b997caf84b3e1c6dddd322ac08c0106 false +check_ring_signature 6466208db4163a6d109020886553ba459465382a48cb5b047eb9bc9e3443a88f 44abf4d57465dc09c984258daa98eec2e4bf56d41680d5ab69493437b034dc59 4 a34d4a8a625f84ec782ed01f5be8e430c2c777cc74110dbec18f3e536ea97494 1291fe5eeb03facd2d11bcb1b0e6f602baa9caa16185bf7163cb3a489bac464e eac4ad438f073c177a4510e6dcc067d416dd1962c5547bf7af2a35f05439277d a59484f618cf3e11daea86ce27030077ffa975c92b477f6588ed66770fa13b08 66849651af1231633df18758ee7ccbb4e6d031b658a6095c1cb87b9f8001a10aaffac3349d96a29d14364afa87650ddf831e9e4dad389d86c8c5db5351aed006c13252ac88d0ee8894a9d3f9f81d9befa29667e45256f27724dea2253f33b70a562adc910c26c7b7ec999f4826175540f2d94338beb70ad0fd0c75d0bed9db0856dfc424ca8c0253bbbb4805c62398d63442ebf2464b4afd2b296ed329e0d20a222b1bb869ba932ba8ba20db0f3393063ef9cd803e15e69ccf78bdde90e7d8064c216fb5b3af11c8421ebf7ce2cf8d17a44009391d9ad03090120be00328070bc87c1f3189d043de603e754c59dd877150be1d7fd382938328e34314cd679f09 false +check_ring_signature b00f42b805925a1b551f94b5f36ffe883d569164bd491605c753e2daf970df1f cbc2c2a5b21fd0958a19e3ab8e88713f3ba6fe0ebfa41bb777f45f294103b662 60 4f72f8fa9fb12ee6df6e2e7aad41e332621d41856f513a70625c8b1785f677b1 0c5ce22b7d7ce4addaeca241d40cdfecc1a0acd9f4eb003cea8e6a84ff5adf88 5d043e7c50e5db46e66e12fc16390c736d54daa1e60b728a0fe092208dd9f430 6a64cfa5584dac7d8149aa38222a21692e172ac7237c8d257e274c5ed9eda808 b92936dc743cf2a2be4060f12beadb29a75ba7b9fd6eb0991789eb21ba3ef7ba 4256702506d2680bfc79d246ba032f059837880d26af13c7fa801ed658f4e813 decbad631d0af7056ae764a7b7d884551d655613748bd5629f54709dd2c9eaf0 34e1ff3db38fb78d018aa68c147222a5e3b3cb99f1bb7c47fb696a13d90dc7c7 7d84260a4fcc70ecf5d6e0652d647489cc407a7255509155052783e8e45f6139 5ce9fbaa7f0aedc98a5c1757e7a11959efd1f366fff5dc1359f0743c56d8b8cb 5dcee98895ccb8a1a5c442940331f5a0124ae5e623cf47b9253355cb43fb2617 3b6d0d6076293d901c4b8f8337aab1481701b8efd5bdfb914a2d9593fc866b96 66859de7b2db139603087755f81dd3babe267f083355ba1f8008dd6fa1b20138 e067557e351db2f3dd648962a5398d14135d918808de13d1e89ffbd119ba4475 a78ba7978c709123613038831d6640c0d78b9123d6c941b6e5d5d71ec70c98a5 26c7dc193a518a64340966c1c68c41bf469a9f47b8fe0b6cb18df109f40ca252 2cbf3d5f4d1dd9c7d82dbe0e19b99de75cd79156ff36b37b12c018abbf058c50 b895c83fb4e12d43cb57015ed1ff9918a5639cc1283a66a1cd8649ff45804170 d882df487f6a5964a68fffc7a91561ff1ac6cf527d43b86236e746aa5c23fb33 dc0d0b8bb774ec33ba23f0a03fe616a15e973ef0755bf9a4db8b44a15310bc23 a39949c41c2166f12dc7c86ff98db6ed596e53a9f7710f56004f038ca4287cb1 55a59e7d462a1353f6ef1abc8f7d8aabcaefb14b9c8a9770af7faac7121f5a04 b9aaff899a52299e88f3c4e5ed92292045d241093850fe5074c51f911f7ba7a5 40f6c6e6d5c4674be0a78b4083643713bfe74f53429cb10e30535ecc1f2008e6 6e2c414d65bcdc45020f99314b6ebadfa2293fab1029032abb346f022a6e61c6 e600bf73e25d9e29da8d6397535948057eb34b98c56ef326724a3bbbb939756f ab7198d27568511c0ff5a827c0f0b1f310cf6eb1f21ece90f4f4cdee0f4f32ce 5caa742142628eccfe692d689ee829525bfe2c73de8263e8ec8aa22f1c75a95c c8fb572fa9886632cb749741b86c736325060718cc39260bdc3efd040906ec4b a49dd11643332cdc407d8e9cb3a4e9e3b4f659885e7d83a7b511ad60d5defc24 7afe44735ef1c835a119dda9d5b5e24ec5d133a45bdc0a9925203ed8dcb621d4 2594ac46b66caee5e2412737cac2d11651ac1d6057f04671feb737acbc25e41d 52ced957a31f3bffcf7f683aeb0676a43c3fa50f43585ae2adc11681f6c906ca a775d34c76d3fe7a1e9964e73afc1fc5166044541fb52bb2839040b87db6bf6c b979537cb4c0c32657f364602236025317e4908d7accf33769f799a5e59c9160 e99ab365138f1facfcc3ea2f1c8860592725195fbb2c3d3fc625c434ff5b8533 c762bbe39334ebe3876cd8303358893e14a1bc4532c39ea5fc11c43aedae1a0e 04a52138ef28980ec60c1aa3c3c20ac7af0dfd704d5cd2e40609771f7fe4b159 e130a97dc4e8f3edc8750bed8d060a1a44416494892d6937c3f090fdf2cdc68a 224f2e23af908f769b454f1cc46858e26dcce7936ddd637463deaabff0ee3097 2f7bbf8c27334264e51529aaba80db240d688822aebde439a58bceb5f278a90a f30e1d4af45d8c074f72ed96c7e9004af4e0132f37f48598b2b152b2256e04dc 0d23b2590caaa850786fa63c4682779d9b45cf50177e490e892ea562fd6747f8 385cb715c210abe97963a848c1e5d34aaec62809d78084048e8d5261ed0efe10 bad543cfe3fa0be6df277bd652af70a2585e20b0002534fda1462823668ebd4f 4ec19dfbd4f2238d0b60a7a20d7b86ec1ac1cf1574309777124d9069333f3c9f 63fe8f0073a85f86355daa80bfd1786270b3c15aa4f435a741a0d2b4fa8978b5 fd62847e1ad2f557d6d2d1cd63f132c18ac4f3949e938b7f06a8b1a2455f3c34 70db2db2ee8971f466f59b8a1d545bf5d5dab2dc2704aeb9cb36fc3acbe86304 42114c2febc28b6d4b6123e131bcc426835cffe6da5d8ea10dea00d05bab42e5 8b21b7446fd64a2e1422439f0d19a2f9803e531eff4c6846b4b9027fa9085c4e fc1fe396c946677261c75622db69b1efd74db5b3d1cde7a974c79aaa2f833955 bf2c92d342eb05fbfae595fa758aa1a9b5338873dd7928dc6f8b11c0bcde4fba a31e754a5b8fe914c1ced493023a6579e2c9e6447354d4ebfd990bf1a2238bd2 f356b9ee18bf0b017587411c56e32a0c1f58f07ef2180172976b752bb8143de5 787ecca0c94a399e44a7b5732c17fb1b447fa057ee9268592a9ad37ee4c5f3fc 5c9eac6a1874a4ae801e02fecc9535e7b1b2678f94f07f893f02c1a6c580c0fd 9213b4945d4ea710df1c4ab1baf95c993ab48b4497e12864f39dde8cf977e388 12071a832e5615e9f6ed28ab0346dec27c720e3df66cdba2105f02a04793748a a596ad4b2fdb54f155f399c290469dc5605290cff7fb8187347edd53c544949b 7d45e276c2b25d795924e175674bbc392d5478d4246a1c89db3248dcaa22e90b80bcba9402cab0e58f7656b80261b98ef2e06265d7f4fde07b643016473a550364205045dcb3eab93e09af2bb1ad6f188c8b8474aa0ed8d981d41b139910310fc2c71f44233db6771a47c4bb7b712ad293378c906c20221cf98fb2e9381326009a20741e41121e427b5b856e6dfd8cae68a9484cf4746e1013fb59395b5f160f0c855fc04d727136c50c0d012c5702734ef0553c55560eb087db2d5cbf584004d1c2b9bb6e69baba6b27adbdd4c8119880c6e0efe2a507ea82b5d611431c7708035f0fc6f98eb840961717c7bbfe06deda6832059e2b8756f0326285da35a201380a3619779341d196f714ca062bf1cbacdfb343f7a817be2ee362f093b586011a75c195bf5c6f10abd47e93c01878d41840a3bc1f19a136cbd80b4b9aa48f082934f316c960bc24ba3e6256c08487ae2362313f105e2d84e1edfee854d0d10c181259d31557854c9db3e24d3ce9dd2ede7b03ba74c7308ea804c3a3cda2be0d835536588a32904c329e0aa4b20cb6ad48b6e3b8fac52ac807d1a703d47dd10f2e0164a7ddcd86af899061f3f4dd8c3d6e8a92121dbfa10d313dfe681f34a60f1152e8156408db01f03f0f10cd12348518cbbdf2f22626c8d630f83da532db0fd060768e3e4a63100351d7ce612ee757bd9f50e79cb7ee2955a7d1fbdb10aa0650027da09f07ac4120db2462c15b1b2f14eb206a55842aadbf6ad49ccccfb00609784dc4c32c74002bc24da75c52d51504062c00ac94ecc1035789e3aebbea099e9798ba971eaef72ed833eb04825b7b2ab35c41d365802b4912ba1b9c3cd50106bc697d44d25622cd0c3693ce2f31c21feb8c735d63a500685e3ecd52c1170869f2ed5e23913d547a759065d5d210b994e07afdd49f75d1971b3d59aef44604f1a7931f664c2b98e85f755c56c83914df66e9984223b7badb5460df5de5a30c8818f2e675f2a26dd8d99e9ff30a086fd0d9c7aded81a2c7617439a16c69110499e954e8e44625529a168fd393c2b21be6a0aeaecdf6a22cdcc920f334b19c030b5180d08302007633bd075b6b9a09f2833c01a2840bff9e20912238bdf378018e4ac085dc6a89c9bb23488029659a3b65098f748095afe932fa1dd751dc5609b1d113f2f485398200f2d39dc68b8c277c904b653ebb868fe1b80654615a3a0706e75fc6994dabc4219c5fecfbff25a28b3f9d16f7ae2c24183350f63f02e1009658674bda1dcb67636139d3270b87a873d71d16a8758639fdab08c71fc5350a2ce1cd860a45f91ed64666206ef0cb2e1d65ee3901968bfe4a64b7f9982dc90980b3d3564755bb2a917fc4a51de8d8a60f11a7f34515a6f061e1713bbefe6f0bd9e9ca034378b851b9e6d05b564c038f1fa20518fd2aabb11091c98e01c5ca0eba2c8fc1a905cf3d94f3383b12d62e8105423c35b936d359792e891b9ef47b05a3f633db19b767bed53d459325ed60351e6f739c821ff5952bbd9e05e81c1c0c7f9e83855c7ad69652eff6fe6773cfafd7ff03be638e315befd717ab2952e408039b0f138830b9374d968bd221d7b64e5376121a675d21bf0152c0e649189006c6de5d36828d075c7749757f34f80bafa2b94d268ef7cb4fe2f6d9ee46f27b090b6046ed2b20c1d61a6a17fc36c4de5c05f139d69d69463b423233bda1b0760d92323f4a121a8534fdfee18aa93644f65212e32f0615825e66ff67d5442c2947e4446163fba2e4f707aa09797d995bb4674d98efc9cd3f85bc0307f91501df0405822ff14f724c29d2ada926161857d1c5b05d558a3811b1b2ab638ace985108d85b8c52ff82a1c41a8e47bb7be987d9c21c165e7b8bc635bdb573a04b693202f3d595095a0bac61b36d2599b5e16e247bee89155477bdae8d739e7e3fb92b0ec984a388f3aca91202832cab6a8c16dc2f6700ea1c1e95f9ffd9e21d7a58a40f03da1443a7fc0ced87f71e294d2aad2b44e016e2f594118aa84f7ff26dd3d40458efde7b08395e61ba09623f17bd98c19a5c81ee650d4bf8c058f06f24702c0936c8fc4518403197aaa3ec2cc7a2ccd73553cc27a2f2267a3a441535febbe6093ee0c8bd2d2d3b631ae43fbd03f854606f5864d009959cf4c50a0ee265a4750d90f760c71b9d64efe2832b1965e9c0ed5013aadff414fee31c71ec0b85fbf7092306df30fb793c7f2da4b6c57e843b1be739e383ceccf95da71f674ddab0c2046253ebee4018df02a3892903af219c09e78de17ac96ebb7adfc65acda5488c02bdfa732762ec7af8125aac2679d194afe265b6ea9a56ea0716de1c1713c71d0af5fd935243577969883d7e79910418ac665e15cff99e7fdaf84b46db4100f504122e4105b7b844063bb946991bd00530751cb29f4c8518e0dacf21bf94d5dd06796057997880111fc90206dc90cd46bb3a82aad850b5b1b480572d6fbc1f050724dcc24a34c90339164d087204274807dd6042129663d13273d44134a699f10d13eb9c7133278f5611d6efbc48f9a48d9a545651d0513f2fde0b0369f7ba1b00c094f751cab2cec390b8b8a5ab50ccdc13abf04b589975f2abc4e8dfff048301b6f903e8c4b139f3b10ac82ca311a61c6046bb7d9df56dd40b5917a383808b0502901a3febbd2911c6bb2d9ce47f2e30dd1ef703bc06c75613c6381f6f730e05e8a79e2acf02aff7f5ad8cb51a55e1cded68f3b3b795920158b82520a85ce60ecadbb97fab3891305b26489611008a084c58c14e47f69f3e14780642035c0f0e283e3a96edac5a68538007474a5ae947e6737478eecfde6113c4f526d9a2d00048e9fa3417ca06b7c0c56b1a848c766c77f2b181c231506fa52817e0b2ed5501d75350670db08ed7a6b2e28e5d8e54eb77dd88d7cc00f0405c450ad411d653088039359ff6946b68b4e5afed58356b20d80a382d08d2e0694c483704d22a6d06c2402590d4bdf8f1f2616292a24db69a1dc79ed629299ca59f1411a35f72ce0ed10ef5b0fc4be0d5cecf1932427fcb12df17453a03ffb3a8d496a5099de83700114e3bb1fefdbfb81c63af508688126d5d71e80e0d01b7151dd8482831ee9f08411cb4a3dcea0a80bc991c0c81666c2bbf4af33f102c2bb36caf0c53d1159d06210ff4e066445e85076af441f0f991d523c3002a00fa980b85c02a8cf20c4f06a697dc7f2634ab441ae8ee835ef5f68e6bd838e284376605dbb001387b911308be8268b24be1b7ad9c3e7252b02510e78f80d579831a14b21b17c8572130970a442d2c00369298a9eb51220a2332b96f2f4f9ee65af8a4ad1f7ea6fcaefae40b01b03c62be686cf60dfcfc1f5e6592f33d58973782de5fc1528a498a2a1ede0278c03fcbcaa958c0e380058846225765d61a99872d2a2b46f2285301743dca034246d7815919729961f84ec21f98ab290977a56011cdd60a591a356f7b56770c7a411e25c32f64122ef760397fc7f6ae6b16cf2606390dfb2e1dfccd16f3cf046ab69cba7c84fa398504b5f0f99d7351a05995fda4e06c6b8cbff0684f856908149b5b77fef38846ba2e30a685fd89e8f162c87d9ddfb6e097a82a0859c5e007c1e740bf72afeb36a88debec6f2a47d8983057b681209b5eed35997294711e04d7fad683e88524a54bc3bef14bf3377f07ee1b47105ad8e5d82efa3f3b6f9d0de9cfabbe204e8248c30854141e22aef0b490289353b0f84cf6d4fb08e4bf9e08b99cf48f4dfe6fe875b3c7af19e7fb71facf37163e6416a30a164b47bf47a2093067032f10b2065dfb1abcf37002e00f8c4137d69f364d4343139e0ae247c309bff298375a86f146eac0b32330e23ef7c945277f77d8aacf661dde8a6eaa8802431cddb524c3f6aa46594cbb0f4df126680c8fc1eca7be5c01c422d77fe73e00169491e7fb4bb58a6650c956a5b1a3f3e1971f79f8ce527f613e56177669e001699e8131a155d86399184a2b7fb4f0a8ce5b015dc9ec343527e1ff525698af59f89ee04e3aaa31c740ad86dec5f55e7816585439c7ea203e23f1a0bb6344d30d054e1f43d4f5c7c472f1bb5c505401e7348cc9babbca718b8310a0d98149f0001158d21d975a3c23f7cba87d57af6e913d4402e918d0fddcdbd757a7eaeb5c092bc857b84e6cb8eb10d5a85f673df5a8b68e76656e64e2ead629f5f0fb5d5504fa8c064371d0f2a7da32d8be7ffe70df16ae95bc15e096b31327ef4237fab70babbefb60168cff64f298d7d4f49bb87a9b005f990c49ed046f1c45a67bfdf7014291af7a27f32f9b0cba3d12c9ff48c0608883f7398e9ba44a58b796a43aa30d5f7b347be6f26378cbcfdce54722848d7f2d028ec2b6f206b70b972feac5130882f7225cba9211eff9c17ac9d4048aaf8012780c6c0dc47ed4dbcb7e14ecd50057841b14a55966fb55da05ad04c4338b0a768f70b51ba6373e982b2dc49582070ada66828d9a6a26d5519f4a560dc03e92c31b3267fe99ded0f3fcf41b14050eb45fd55e4510275b69ebc8be4046546183fcc076e8eb0f0d198b2407bd9b910d295efe83b2083d2ae065dc7100e4d03eeb7128b955c66f8be721d0d160e3dc0fec3e0e7afd4ae6ac33d037bad1bc7b0ed639a3ed781b654921ff26defb1e9f07823a1037a4bd939e1b145c6ebc8794550974bbed21cb475940706a1a66b23c036d498ebc6f8f5262e5ff0f8a9c2016691a101f6e6600b0a17c777ba53ac50200452c50ee86e49cb636b0182106258be00e43d23415d0387d899949baeb396c0a07d6d540a44c59e51917d3f8df19c3eb6c7a45703858cae7ac242707ce6b19f394a8df5edf1f4cec30bfb597de160b7eca1ccfb4aa34d8ff0ae73c76891de20a18af3cdb2157052ca8b67cfe73f5776b81595af5e9cffbcca1572f3e8112a4080733c255e6358471769b5991331d9abd2b358a83b3f629e95aa06d953b9c4004e9b25fb6443251536603b0f8e16cbec4662d856e7551e90efd218c88e1e5f0070eb2178743f6c9e3942f03c1e80783205388739fcdb6bd257e008e0605d5240296439e5abf3102284954bcabba3a939b2b19a2db4e5c9fed55451ec367ec36080b0585f8025ed2aaa77df7b14d9af5053fa784a73c52b4abf5de225ff60585076ddc7f023a0936266cf023a5a5f8aac9f1307fc9c6af46963e436ec9364f960385ab315f515562793a5db1abfecca35dbdd5e17ab43c97ddafa81d2c90ecc701a5f02a1460def93fa026e37d7d264568c1ef78c06344922af2088ea966ef3409c98b2d2032fc47a3f99abab44367bb663da3c470e55444b516952bc44e4f960756bb5131ee4939ebf0fd1bce8f7c1a90b9fa028a31c725306faa3d778df18e022fe4a0e07a0ce07a5b97f3c0486a8593b702b8fa85d6dccd43ec710d541f0504 false +check_ring_signature b97943fbc93f9fb80b6db939efc6761846a4adddeb78f993a91bab032a80f168 cd63317367650ae1e282ffc09b3834f09902f12bba069974737c86e04710e7c1 1 474394f6ee5ed3522c1d540af8aa5ffa6c4b4b4b64b92a6f29452ab22a7918b0 eac3a9ccd063b2039fff48bf98f4c3b0c81e4b553902622989f7cd2b88c89a8500d616b3dbba879d3dc5bc79c53b01ae7a9f510558a2e460496fc0e63366facb false +check_ring_signature 75929bfb61ac377fb5e16124479a6e6833cf81c2b6ec3b805e16499e2c33edfa cf6662e311319b0f6040fbfa2a8b779e6538a626445aa211589ae1e9d4e42858 1 9304570701a62ab1df74fc4ddae0553c57afa063778c9c91cd55f6a5958ec68b 86a4b319adbe00c1f708cac96e20dbe18ebf5c42ca592fba7a8704ec538b9c002a00ced962270787a7cf1a9c4fe19f4c3f2e1955a13b625bdfb72387bfbd000e true +check_ring_signature c5ba18345f2610da12d8845994f3e13546c391770e50c3e8ee0588554a15f95c 8dbdad15de7bc01b040af5c33b317536bc04b641fbbf45b9917d88fc25fa067a 4 a69f0e35e18308fe47ae635fb28ddc7bb6bfad2322a304b631afe0840795693c b70a306252c3b3e9f736903c91afe9d62fa6d11dd6b2c85acfc85fef275ba53a 484adb0b9e5fe3abe51c40e5205c974c1993e819bb0089f099036b82fbdc844b e65c31790bddc3d916ed65141359801a2c943ddeb59770553f866042848ff410 0e8a9bf00bc8765edde7ba24bcbcea655b7610b150c22f4474ab036b2811000aee4cfa9431d3906abae178f644293961753a773dd8ddf45b7ac1cd61d9c0e50fa04accc6ad4ae812d40def660aa770d6634cc3dd4aec9f6ca9c69f7dabfa8406d94e7c8789794b5273d08059c83c3fa5d0b78a4625f392bcc5a73ec8cc1dbe03cbe7508002472bbbdba58de5500ab92fd38ab1dca56e70ede7eac66d2be90b0312baa4552d1c0189993d1c782f37a77178917fbf70b981965e3c91546fd95a0e2fde8a4d359679116921137653da0f8cb8fec03c071b591d7140155269e6a2040808d4273b46ff3cc42952175705fd04a293bcf3947371cf8b89b6f9b971d30b false +check_ring_signature a037e7046f5deac1827a4caf96542ea46add5f92584b37cf2750f4fe31007c46 60d41b08ecebb924124aa29236234a7f1b849aba11df7a01f9638e5c41d54baf 31 8d02afe78bc0b2b646bdce00d560d15a555490a7764186a906f4d69de27758e4 d14cfba79c7c7cba0ab97ea4ef4cff3a703f2377796668ecd14980c857aaf5c7 adbf00aeff191611213888e1ee5394195a99e50e65d9dafae334221798b3f844 e88861ebb56cd0cc9f5ab632b2b6feb3d9dd005872e3ff547aacc9cf4e50d927 3d158c3b79480f4c993ac7037b5a70af381c536c493b2835a62aefed87307a4b ce2b5aeeb29aae2c9e78a5e0c1da38228994d04644a73dd2c63b511f40ec0d71 55730743ab8a1f6c097eca783e37801bfe7f97fe415481fd008912c81d072ae8 5da1ccbc6052cdb3f298af71a0fbf0cd5d018661b28bcca0d87fd1528d612f60 76783b79f352892186fb4c6a63b6533ebfa37cfc545b68ab7291ac0d25fba7ac 2d8e9fcb5ee64203966b18f69c27b41a458039dc64dd937a03ab61fd34d7ed2c ed293d2da513bb653f1eefcb4f025146dc6733b874312cb638c3637daa77600f c03e7007672744052b8fd2534067c4c5b4b04f9a7113406fd252304144d17938 14a5c7375f39e79e7078a607445c305d518181cd19b58c3b241d918d70280047 1eaa6fc2149c0791224e9bf8a1cb1f7238c78d40cc699f28b2d839617071bd74 50fa00a4f40b93b46c6b230d508e6fcfde3f251c912273b624b443426b2fc958 18368e89a088305e2929cfcd0aa11719ed6875479117146184cfc6b234ae2350 8d4506ad8a1fa428442bb8f69f1b3bdf5fa23b75d815b77999a8cc639b986c1b cc1f49d7295bf881a592a23ac234c1fc4ad5e2864a5a2b8fb8d02323e5bd2c87 64f559396edce4faeb174960eb08237c71bb8effb63f64c9b98152a6e038af00 4950834bfa3c444898d8ceaf647461aaff37a6872dc1ab5a3cccc2fc8b38c265 920d572a64395805416037bb1f6f38b5c34e59d9d493d23d5eb7b6bd63c88e15 9c93544772b2c5bfb01855103d83e47beeeeac8e7c5161b2fb408304571b7722 21e4b12c8b5952a9539e289ce71cb562b57e4101d3ee4442df1ee16e41dea015 6b1e610ff3c590e00b8efdd4b808b6357cb501c65f62061e59fecf8753d83a16 72defd809ce97e1e195e5bacb099434a3e782c2a990cf3595ebfd73742c369f7 aa57c6d2aa38a642a83a347e8ac8c6c5d24b151ea774527bdc6685eeead0e663 4ef0c7e3790f6bcabc40e8487741174d7455d90e021afa63dea03b6ae75d1c6e 3d8071fa4f775fc1e48e2d3fd13fcea041595b50e289c01d33c699ab9ba0ea08 f6c161a512460b8d4defc3d3ea18792493224bf81f41b14e38639048ca08098b dd95cbb70505a12938376067d665e7e5884d4bc6a0c00450803936f4c8817e7f ea2bd8ce975ee014ca50b173f17ec12f6c33c41c4eec61106554c0161e7dc25b ac23f1b2dceaf11bf635b16cdd7b773e893daf8fdb1f0428dbf901be7be7ec090e43904ba1fbfc722dafee92f0c6d7ea1151809d24406fb80a54a24e8f44df04f2c273534712e8b18f5ecb9622d1e412ef7977664f4da909ece9e5528d036206be17d288dc49c808c1b6989c9b5669bec01c6cb117daef93dd77d80eeb819203e38b22fcb47665fe4605b7131143876445c16c33954363ceb1de6726a9d8840d5552fbf3cf7c777838d30a753ee0485d140e85fb652b7d49a83a3fbdcc56310b2bc9e75f31331bebf84b87389b97e1d306344419d15ad2901b74d0c7cb8c870f14c547f2441f491c5f388f75e7a01cdf5655214ecbe9b99d5facebb951f0b3088a8a0121487be5ea67c41bc0dae83184d16d2c0fe4157745a26138f908fb060015b67305f9cb0a225626e7985856f7625659ae3451a652aca593b4f99f127405ef1bd8c608f54753c7041062786d813dc5b0267ae670db694dd5ab5612dfed0cb0469506b672feed8e8e33c801122ebf544ff1398eb5f77e5f77621a2a08070996cda87db9ff4fd922256b38da64794b7fe4baa4b33cb542baaa57484188320b6dbf3a8ea34e233365c4d045736fb2daa285417e6f25ac3e70ff213bf7598001cb1b65e14a10ce54817726f65d76e8dea25b2421c9e427604086f094b9e3ab0e70e000190126fc5b52ad690081dfea655277236dda3dda9ae911192befcde3072c9a204a4c457361faaf527faa0425de1b0ce359f04f3ca9618d240f11994d00692124f5ee823a617145ec6c3d8a2c5712e0448fbde150e3daf18b0918b7700f9800392c073f304a3f9b2e829cee84aeccf1dfeb9e9f7d33cf946065ac80f10ef79408675f2d7bc72d41a32807b1d0204915d81b63caaa733705cf3feeec3201b574d8fa199650f64a49723d6d26fac4153a8eb4fbaad399eaefa413cd58500f485e22a9a7ac705c0e716459112071c28209bc3150047e74e577b752b7e3cb0eb6357b27ebc523667f9416599f6f01b8054984f3cc07d900c8cbba269723ae0bf14255db44b0f355ca990b3153c0b4ba67fc934a5dfb985d4ae36306ec79e90aeb90d194c71d868ad8108051d4e93d508ed6f6ead49219c5fa65021ac84c1c0e552cf28f8aaa244a52a12a513ecc574d703fc7701811705a84634e9031f61e04aa2a7346b35f919df9dfe708769b5d140549c12426364c2d46035896954bd301e38db5be6d907fd54a672dcca09be1d389216ddd2b5a8f9fbbba2acbab0c290a9d6df23c7b56a0fd68b014efbd5a0c173e3309a1be383e34c8c1dab58486e8005340a4f406d5af5c7ec9d1722b78681deb2d4ebc9322dc8b44525f17679a320663016813962a0603cab8d92c4a0e87ac9a52a2019754d4a6df83788588e2280ccfb7ffb5a3c2baf70801bca9b3636d2782c0e646d886bc1a9e894b540ff37c0c130dbffee6bc4c41364b78a1c6fe133f36bfcedee86c13822ca6475dcd911b099a124946a14abbd53c12770647209d070ea3e3eb0e716a88490f88577753eb096c51e851077cbaccc47c881a550de426d6ac9f16a27667d30f1479e5970a6a0f06dc6a678458ba6d83380a2d98dbbd9e3bc26ea1d21a4b037064f81829f460076fe1c81e8f32550a1250b614dfb2e2a4c64e76b552bd0a7b1b483154259e52017449c68708b4ee1173492ca2b282b43d5ce6e0168fe892da72ea88aad317650ea4b2f13bb728c3ae97ed725f7c135142e3a2b53d420e9462f5956bf15e08820f57e9e854ee4c93be64eba3bd7837e241998174832e63f77cca4059d22fb65501e7cc44fbdc3c35d2d997cc6156e91a37d7c7f55add41f6d786f27e30fb3bf9016e96f6216f651e75ff0dd13a1ce3200750383cd67384547d6570b2fe51cf6f0b87a067cab58262ea16206b32a347cb4e75f00558404edcb511047da3fd1d5905f586d13320971e9f2a4b954b16a007c63946801d9d2604aac98745331d48d7022b95c242eefb42d800220edc98b4b0b12b3628d92ae7c3d1fd7ca2d0bed0500330215046be4fd64aae92a723f9fd4c66af8454ca44e7afb01c77caa19c6f9b0ed125b5480049bc90d43f394cb2f875e516b2e45ffcd50797b1a763c9d1b87b0eb282a3d478c64124718d64d0b2e22eb150ef19b5ce9448ca73f1e770c23c890ab55fcd6342402c5e692677d8003ee00d62910b29e43952636e013c4b5ac13208c6027f70201e684b4f420f728298738703c755ef241456dc1a4277682bbf8a081382fc00ac55a52ab7077d804ec506e7fe741218a6235bac3b5cb8ab576ddd03509df56a48c014708ab7a0a43fc716db566792d992a71aa271595b82ace8bc09d8e781e1a6d73645c3ec1e7bdcd74a4057597a4f2aa286ac936eac475f48210848bf9f73db3b31f9d0eb7413774d00fc7d1e1393927378ef28840ce3ba977405b029e57a5b96ee3624057b2f3986c9f902c8a1d9ed7d4b96be60c4e3c655720455bb61aa5b3109e5a9d488dd0569ab6fb42c6b2e3e9140ceb7804595e4e7c10c6bf0e0c32b9188a50942afb5cbd2efb06cadd19008d41ca6d6c156a1ccbf0902027bd364bad36ca387829e9f654ce4068f8a45bb6b504bd7551bc5cd8febd501c881eed78192df2b075c32b1b75eb7ccdcfee908ff096a29c39314123ad51700a04ab631abb591087bbb0a327d1c8b9af1cae46c1d61d658de4b0cbdf7f4eb0d87802e4c102643e84b6c634095e610f70b39536d3db33e93c492e276be0f8b07bd469bc489b28595467162003d6877f613a30f5c0913d2a0548519c266f6b609 false +check_ring_signature 6d9387af1e06a1838b7727a96974c38906e252ddd537487e10315d35153e9b7d 68b9f94e87a35e025546d9310958a8fbb9397689bc9493d92de450515950ae81 4 f0522abeb8f61827b230372299ed8f874311325f6f3849c7fe3095e7dd0bc8df f041de34b498469442a6aadc03745d829b511936b21b27ed2a034459dee85cfb 6af0ed7e946e5f10ed0af9dc2786c9bd41ce45129cd44451dd216639a95f6efd 326ab59c185a3fb6c3e76777c9b576d2bf648f68a86945fc3a6e4a0c6e84877b 99276745caa3549bb62ce3b69cdccd95cb9b37c9555ec14875587a6be9faee0214d025a1a780957d875d8d9160ef2482094e1f892306f590f002608f324ec6019c10e418f6ff0c76b500dc4ebda6ceafafdf502c121a1123cc7101054c4ad70548d04699cc6613f2cc0b89723d450d539d5a4d14be5bbcec2c95613bb9c376092a1c510408352e53c256186ecdc5d47da1f7b4ed8a412ddf224393d3c5cc240ae303e29127b9ca4facd5c1b0b7491fa62cdc1d9e5235d8133200e1e2c2bc4c09cf79b481f39aecd057ceb56c07abef664d66e563ac0e35aff039b9f289ccd30897acbba442b3127416b2f7ef875d7ce1ea47b353781f770a26415772a8a36e04 false +check_ring_signature 2a3fd645a7b8f7cf85c39321e0e2e819cbbc0cdce05af74e16c3d399d262bd0d fbab6f41096563696f5287ef8e649d131533ea02703265e2f9571c7d7db7387c 1 69dbd968db97ba54c55cb75cc60639d8f952b6ea45085b634475f71f80102cc7 03407beded7f5d6b17e7fb64eb81b2dd8552f404326af64a8d3a5542e261a50e174639a664a2acb3a30f0ed5782cfc01a127407057b5e565fe3dd96ff34aca09 true +check_ring_signature 88133cb08a03b1b28b6b5ac7981bd349fceff22a5325f1748a62c895afe4dd48 14b41ba937019d4b70644e1e7c5b06a6983ee20602cc61127eb7a2ccf6d3d47f 31 b664b93ad063e023ce59ba8351b867b303b4e67bf9ede56bf3b0120445d20f6e 36910240a67cafb048e5802ded216a5ca4347cf6fbecfcd67f1cd8ded0b74566 3d71a55449ee1c748e0267abe85e051edffb4e614ae4740b278c1655a9d6cc41 16c8c541d995a3d14eee10d0cdc766f80da7f8751008d835c8f452434e62beae cce89d23e5ae8325068fc192e1b1637e6f2db2b736e9014d816ea4f09216f2d4 0d1130d921cf38527eb90819ce99811282a1079e2215b9a5b67df71d0994653e 2190372bbb1be81b66df19942d0d2da2dc65556641c10474fa13f7e520db485d ba0ce28e6ef289058f8554b6ddde4e23203bf21af8779216ba24dd79f2e9e2b4 2a739ffabe393ebc7e1e0eb9d25d80f94e67abf6599d28aa602ff8fb65f0ffa1 d8d6a23ec8147fc9491189b948d52940d26ee1991540c8451b25d267b4ceecb5 e287a265e97d1262ae2ac8377b30f18ae06cce643074421cb1132026c5c2fce2 3a5a66491aa96038f2834be63015ae86a2dd8cbf37ab092e36f35d59488f0941 756fb9aee708f11950edac4562143e349b9f782a0398b1d737b75633a7e5a269 e1840708a136e9a33fe10200e1c9637856c74f2159224eb3d30f2983b41425a9 7d77d765383b446e20782819ec7f7f90e3dbdfd51a426aa94810814add00d357 b57e3d52a790b479646966e73ba83cc7530166f253a4bd4750c17429bce0108d 6e2cf225d2e1aab8859438830733b00d6ed1bba3a2a2bad612879761881483f7 d60c631e7265266b74bb0e2191482ee811140b6e7acf98432eea2fa3d1e3bd61 8623a25c4d70b06cbf928be3bf9a796d8ebd1983063141cb204329f67494cc2e c3083dc47bee21b1314bbd23b9653f6e8dc506bc3d56c803414132c1bb634b0a 751cae614247ed1e0bbcc1a2bd82150bef14b9e554cc7696f32c9210c33d963e a4cd9621dc4da4c0f168d780c8ccc84b20d1cf43e8508fdf5cab358b3426d50f b497a6b98fd1beeb28d78288997bcc0efcef4939dd9b96198f82517ded252f20 8ac8422bd249cf0df61ec251e9af252422b99c2ecda828eb43746723bdb16198 218dc53501194e74d6c4c3729222cdba74b4a7b826c8fae39f069c351e69c5b4 799ad259b69493f27dce0b2efe0c220d8273f83b18da7561bb4274503c90fe99 9539aeee5b13f0d25a1040f3ccb680e24cdc0d2ebf8eec3d08c206ebcabf88e0 d6c9ceb0f5b043a1006491323c8cdff76ced70cb00f439126ba0db6cf5ba1fac 7c35159dd551dc5729b3f1a8027898f5ed09903ba9756592af01a6aad34ffa21 50814827372540d2fb25903fba41f7f4c2a699b0d544ba8842dcba944d17b04c 4986cb7ce0b8945e0bcf882358fdd0306080dff4651b5419676af7c96e1a7757 02646dbc90a6caaaaff2747f04e5c78f908771fd722863fb104cdf69c065cb0251cf0c93b8c5c4c335299cacc8a51e829a034c5ba2979350ad13025792f2e006d18b0d5725c1296471e621c24dcb0028b7d753c1479b5aaf760826b1af9eca07f4e4cb78b1cf4a3216a2580f269b0b23c2a8ca373d185424d108ea598387ba0bc48deb37f200ccf57beb0d6e846f3916c8023f6cd00a7bc8cb296ae17eb23702e7cf250ff18e158ea899acaba9942ae5700eaee521c00838aa413b167f425d22baf46cdf13b023e5efc2be5a7f3713f95e64ba215efe3e12ad8c8ce34988eb0b5febadeac7067ba87f3fce540a2af3ef6c98b115378461dae289241b01f5e00f8c17046eb4889809d481eb701b8f9548374b09974e05e99464821d7907cd400b24f1c77c8870b95c2702570dfde5a4f65e6022a670c3d59fe744a6ba54d9c200578d2e25fcb1344816efa2f4b31c3c5b280a1c323305aabed56de518f77d8b0aa5d165046670fb8eddcb450129d05e4d93067377dbacba9fa8be035606e95f07b4d8ef7da21e54eb166e1b2e1cfc55a4fc5571d92dfc56b84c387aaf72e6500891e2f32b2c870a950f62ce97cfbaff820c8f68e2014a1587c54ecd823687f907cf98585b1dfb57949fd82564e7a32c9bacb959f7e00aa5c3b53dd9b0010ce40eb349c76ad26cff2fdec810583b41865c434e08dcfe4cf6498108e975320623571e700bf9ebc987d1c11c007ec99d1e908b73c3a6b7b8ccfbab8e55b279f0510698fe1165150b5cfdfd4a8c1b136f668e5678df927528c0e3eec73ec0266b350f1bab87db63714371fc4df06326cff85bffd39d8db93c1b7643ac703d6b20ab036f3721fd69bd9ce7d16d88929dec0c9bd398fa5690eea51e4cb274e38f950b0176b33b400b267bac3f90d25107b027ad1e58aca4ef3d9c15947b896e6c18de022651d11e7c8255d6eae0476f96f4f65fd10f1a55b8c544cb8b749f694a3c9d0a59433ba8e6478a4353aab62dbf1d14944d4a362a42474acbf7aeaf8205027001c4ddf184f4e70fff02d85df32ff0cb564a33e3b333d801ad814c10b9bac74900403020d1dd713938bf551e7f2153d162fdbbfd3a7526ab6c296e657f39144e07448e8e38177a032597c03c8a723faf483ea6e99755278a258b9fa92567052008b05e78df70446ca408ca47d9395cad202fea77aeb7fe8ef7c0437eb118cb4a0221036bade133e98d1e5e3d447d044f44cc1bb754cc9870df1542ebfe069811036a2cfce5961316feb70eb3c6601c24cd78ab6eb94a5b926e3a8e6e2301fe0706ebe4683fdb0a5273aef5262a29b5e24401dd3891f3f17e9e4d8897067d5f24063f72cc5eb18af430962957a0d1917adbcb5a44bc02a7061e0e7c36280636f70ffcb08d7e99a233acdaed261244af4b00f2d83a132a3635a866e8ebd5e01f2107be66b15889389109aacb2e33ed2f2db1092fca6a91c23428b607593ef2b678094b330e88474a13aeba4b454eadc92cf3d242a25485513d87164ec875d7c27b0539027c679a8db0b7145a5f862cbc0ce24a5915e8901c9ced0b4489b75547a2081e375311a5e9e2086176304fddde322491dea118b20f8483268c796623df41023b065557207e361a61cae1adfbcd5c7057eaa63b657cde53e9cfad45adeafb08aebd34abfb416484c728e15bc674f8128f7a14c6a4d13fe8dcb1c7914de0db88303e8406ae8721cdcf30e5830265bda63ccab7768efb8c1305e5f29f9e28050bd1b40d5116beb05c819da401b4476ec6717d2c39245fbe3319ea6985055bf900b140b060776ee17daf5969e6694420b0c4ca853c8c71b35d51ca96800e9e5c0278e12185526dbb444ba0a4ba07e5d8e5f709f5ee752bf8eda9c640a2ea15a409ed6a0efeff6da7871e7476e62bc346ba2c9ffcd7caf7814154ee8ecb4b1a860e707f14b17073cddeb3499e60887c81eca4c51ae0c178314ea9dd032e73eb6700809c95a574e76365d5b87158812a95850934033691354f5b9c2744123131a60a49d463d88792b08186c333aabfa3d460e93c82d8dc8dd90de93d4471ad61dc09dfafa6c5cdf23c5b109db8b7744ea5f40d74290d6d034b51f5d4c2a9da3d0007c94ac7db550437a251d4c62cf390b3c962faa9660ecb05b49494377dd310fd041826e709997716107c868ad221fabc78feab2baee900cbb1b64654ed6822f4036b0cfb23a3b273ac8780d69d1ac56c67a5e77128d4667482812bf6613b292708157c2bdfed43a570d1732dcbaa6711148afaaad31337ae7c84ccd7bf68bfb0078b051435fca875f365ad67cd8afbb08d13b9f70f69ad5a755ef3d7a1d832b2094af79f614a5599542120921dc5d4aa2c9ec9b20c82229daa9640542ddba02a0e2448be6db128ffa7e173341dcd2911c53c182b22ac88b8c68181208a0de0d403909370b050ad02caea9acb182b855cd401a0ec14a58425399079922e968ab40c6793da4f14ca688f43c4e85af4b3a7c697474cd0dca4f3ddaa3fd79e8995020a35276f7e72b880d650a7452e1699d031aa4080f538bafb713d079cfc5703180ee1525a0464855c80c29f898b6564ba560f1a15b7c1bc1b82069c5dd763ff490d551ddd9ced3924b26519131c929d3d8623bb8b432e8a1b5f9431ac63e9902708c70badcae2e24c8df29eafb1bf78c275199d375a51365beebeda32e88f77e70ed993a53ad3f1b62feb59a261780d57b24f4886d32708ffdfd7dfee86e251a00b273357f633532e373f347467bd3c3504b42b7da98184aede978e637468d2fe03 false +check_ring_signature a2b9c67eda06e0c8375e5822ac70fcff5b3ccea260d93f070d21400482c50f57 ab1ad541246c884c7f958b293687ab23205fc05527a9366801ccccc49788f4a9 42 3ace5bde9003592ae9c26167cd3f2262bcd2d665d2e62f7bd0bf9f58dfd7571c 902396bf6c38932628c84f3d332e89ee3fee3c6222af1dbf9a21475c8531ea4e 4db65da3666e81b7897edd4dee80f66b6eccc40ff17a28daaad1ff40ad852122 3a014665910ca10b0e8a4062a23e3acdc43c051cae6edd36c53ac81907a230ec 3b3ade84360a80199ea4be99d6f5fc0e0bc53eea94f2622f9211527576d5c61d 40d1dbaacfe297a9a4c10e85ed20dbbcb3cce8815290307ee641c54aff8ef3c6 bb827c8e383846e6a85690a2e87e9f9cfcc523eb9cc9ce594232e9eb152c28c0 e2586c6c495b4f8591691bf305f13daf5f4ed22b783f1b9aa7051edcd911e8fd 9c4c8215ddcd11ad298bd757ba937f6e7b8f3a6fa01165b081bd1604ad8e660b def2cc49a2e46e67374fc273ea27963ebef9bc619a66958a63eec9c1a4f8808b cf95c84018c074d0928e99bd98f9e65c672b4dafa6d7ccacb5f1260758bccfcc 6a93e00b6b5a1809203b678f8e3899183f6be5d8bc1a313ad2ce33cb0e28d9c9 5f97b85b2d642557e6df6ce6d4326a102e6e880d4d8f7f14cd64f0148b1218fa 6ef7935ddbf5cb5d4a85291aec13a9f37ad50aaf1bce94e9cfaa96507dc382b9 53dc1ca1706e597f3cb8dfd2cc915e2f8e363c8a7e906eceb0bc3a8f4251e093 3a140468d63fa7ebf54d82b13cf609e33fbce247cf679f270976cf2041c24ade dd7b78a2025ed16cb05b3b32298283f942b62fc5440e4f9af08e278b1cf67ce2 85a7ee550d7652fe89271a77804d0eb1d642ed756ca0891ee5dcf8a82ccdd25b d8951ab2f664c504e42c0876e8a5cf74acf16e777ddeba41cdbb4cfd98f66c39 3bb8b11c77a92491805ec47ff52ada3158fd4225c7a1074b15eafe28ff7fcaa5 dc0be3a9414166010473e563e855912fad595ef164cc8b978f899d418696b880 8391d913716761393c41373406cf2548786454cb02466df943b4754106e7d7a9 aa341d8d0ef9913f4b8ec70975b1ce0b08ef3714fd6378f1e04de9f6cded5da4 aa40aa49116f7f2a095f6b039720f559534a9f36bddc3909aa6c5b7437185b82 7b4339ca460c6317fb7691ff2e7601640562d3d2c75fd7061942771f2b06ee53 3edc3e68f6d9fd960b1dc71e0c3519c4bb9528281380cbf6d420bb91dc680c71 290124541161e3c091d33f4aa875e4f4086076d211865a5de79aa48b4e2e534c e5bcfddbba8f6b0fee8b87536fdf3a7e05ce4970db209390448241e34fd8344a d23d310dd6a7094fce5ac74f6d223683c6a6d4d793ac3e9f42adebd649782076 78abdfc85a8192e19110f214ade5028f994c639ea58ec774dd30470b3d7c4082 e3a0f3786cf39ec42f904adef48420cd21b3a4bf25fb140c6a46630d2aa28c8f 2362b498a9aa8020ee04ebc8745de6ce82e097907a15835d9667d95abec12fd8 bbf5975165266add0b37539f66044392d49954a0a0369734a7ce2011571517dc 454f40061f2e23aec8c11004b4c6a3bc5cc7723eee99e930de12a229db90d916 3faf7e685e5da05cd55df427d95f8f42aeb38fb94adcb10eccd33c0bc62a136c 708958771d1b3a09fb91e2c020be5627deb1885cb759d69a72d3be65f0897bea ebd9cb9ad44a9abb6b1ea6c64ed33cb9c2fe1d3ba8775e3bb61858a472595de4 88da1dfd5354aa744ef296c7b4ec426cd1ee7bd8db2556b9c9eb719eb0804437 27a5f79c420e49a63820cb53811b10bd44ef2756fce3407fba9b84a643d047af da2f4133c0cacbc073f3e959c41a095c5b3d2bc8bb2b190ab00fc18929fc3ed1 e279981546a42f6c1d0ec8c438980b4617665bd3bd99630e9e7e35c4ae7d2877 9ce729bcd3eb222ea6a8d0b45c7692a374c88b3f371411e091d2fce3aa122630 7711fc3f02bc31af8cba851aede2b0fe944a1bbd4beb27b1ab4a84415b04fe074ba88658d4f2a0cf919ad07bd20e0a3333e8e7c57029cf96f633558bd91f9902b1704bf169e4ee011bed48ca27cb5754ac4f463ce6eebcd8cfd4dca23529270d4867bab172a005fb616199f5ae59105860f1278cf13ccacf3890b10d921e5a0e607d431ae568a91c144f01af4a59ba8183a3c04cc15614ec5b44c90b78d300081874a4ae27000aebcaabef6b72ed4c6b75989a51372cc973d61e92a59249e90bf23e732ffc86835d443e5592288af52d6daf08720b7a890a2444a8775478d60343996935987e0e9db327b45d31619c857d62c05b8379376d7292783b03bc8706c60e319c9b4a599f5255a8cc4956dbf07b7855fb9dbf24c8e89092cb4ce8360688791b75a04ded81f0f22977d4821ed87b96694b39c84e90d8b9b9b66bed61009d6821633e84793cab3b34dd90ce387f0ba6047923ab3c83633bb943237cec0bebde796489de333d9d7bbe771f0289f125e567fb7041a095b45b92ca2fa2d20982b27b9f5a8f745ed0a580fdcda77c4ecd6f3afff817e35533ba6db839e0dc0edbd2162220af6b257a47241f7436c70f95be3fe097f51c4214f07e7d75fe4c02cf6627048ae212a8dc2db79d0cad4444b0fac05527a1d521d818bdc9a5f07e069663c76b61ce9f2178a353462e0e21412cc087fd7eca4a1a722763479e758503c6e7183ab9b6474772bd32b2b624649044d04acf365195969b12fbdde3247602f67accf6127c050154fa21f4624d69fe5ff0cd095d28d9536c40dcae96108408fb767b0143c4e0405d3d537afefda810ee030f5c384220af0dffc358ca666e00590453e383b89c0201875967cec56e89d7c8efbf648a81c0253d8d1e874d2409542290b900be2a32a169aeafecd3931a8079419746665d21ef59c77a8bc4d101cc4307c53aee0697e276ad1279cea9376bb5c816ed93d6091d49f6313382db0a3fc9f1e3d43399a40d9b364aca53948ea8f200cc8c41e84f1d6cc45fb88b4b01518380181059a27c52d00ddf4237e8b44fdc6b9127eca74533e9e8cd66404d0fb8323582596dba7cbb68bb3b42fc98c24c8c5df1caf4c3f95d288a051b7be90efd2ec63e63c216245462ff86295593d58e37e0bc0e4424c279036446d028c807a8c2a8708f9f3aee9f77426060b776d6ceb3092f17b7100afd9d70a79eb19a0d58ae460571eb4ac804533b465377d2617f6932b9a28824448547c1d7b9ec7f0d24525ed35576f454ac009258f58fd4885d54c77c3c4c6f0f78088913c7efc506ed51cb4a2cc6427b7d17f5e9eb9499d27686398429282b53a9451de351426d0fbb9aa8120cc836b19fd4045e76c841c0243a9ca18e0f1d3b888708c4428c4908362482c0d980e3930caeb6d0cc3b1deaf2db4ad73014824c0b4b55ba2f889f097bc16d22455a0a0693f624add8d89fa188bfd950c64eed1f7b2f50932295740f43af9ef56b732dd9ab971bd6551cc9f23aec16fba0a1c0059fdaf5202808e208d0e992b66afe57c59ac3d5f6ea54fb38ab7ac35d9c548daddb58fbb2dc145e0263f6532215dcd999ecabac6172d242bda6a37c2504b48716311d16c0182a0705853d16bbba28236acac282d3bb60c34e881c0c0dfebdbf90c4fc246984e5820c7522a02f91ab058225993ce7476f5672365408c3f6e3ff455f737f00c5c73d077b31cff7d840c1ccd9d72e57c402372b1d0f04b078770ae6f7dbff9f54bc320dbfc09af9949eab382a5ef70a3079c9fa6d7de0dc42a7274f2be01d033c78b80f8ad08df3b7cf266c173be3fed7b1b95188a956f7214fdf0a8a4f04c00a4e1107c6838220b7ba662eaf35441a2dfc98c87bc89d2f5170920c1e034395fd77c70a66c8ff08d6c175ec9bd467137160f00a437f53ae952f858d50a138adbfdfbf01a45f047db0189daa945b87ad58998950a6b53ba21a4c3f13b7e1bed2058a990a1ba4f38147a683de4690a21e502b85dc9adf72dc5ad947e2a254b4f4226bc70d84996873f87b3d1ad37dfc4cc9e02fe1ca1aff6e1c1ccd3649ac577674658c052d5cabeed5fb8c0887b8cdfa557609d8632682a89de498bdcb223e99ba244b087471cf5bf75671d2ff29c40dff104fa8d9511be5f7356e769f3d6e4657de0a0f6e817406f96cc97f558f73e9546c9e4af59244c5cc31d7f89b883ba193919004340a14b56e02b63e9f2b8fb240399755f62c9791c0302f7d831e5d4469ee950472437677371fd8c82cba24f0f3c4d4704581a48772167a53105e604bf74aa3032f05976839e82a31145f83ec811c49f67271cd4fc7733ed42f508d496458c3056a21b5b6bce8a698bfc8f3b8a13cdfe1125f7dc79e4ab7187e2935aba7087c0dce17d154d5a051eadf3d507629291e55051a08d931abf6d5db33e0343b2fa70d6aac340b6b1744d4eac9f013ad29a8f92e42812b726e1b532ca755ff3a79550eb319fa9671b7f33ed1cb0031b64f8f23ef6d4b7e23d9dde7156c3f5b3de9d10ffa7711a4d345ce2ac408b8390af09e4b8371ffba9d1f8e09b92bcccdb65f780500f7c2a976bc1e4ffaeb7ee51808e0e92dd4c08b59a2a54b41d86a41c493510e1341bf0c7695405c557b4c3bae92878fe4e09fd9e8c03b438e4b47b83d5b4a04c602cbed6cd4b4c1eaaf35e630f9cc6598dc180aa626402c5e6b30cc9f54cf01d2d3881d28a372a7ad0b690570aab5cc697990379b1907f2fa3326d0e3c94401c48aea22203ab39ee64d441f663fa8434e7b6802dcff5e35d9575dac4a00fb0ebe40b3631910d1cc20bbed607821f682a8aa39f23a67c263aad31fd0ad479907584350bde51990bd31c6c9f2ea9691077346095ccf99818671f33ea806189e0aefbb20f204b5d0727f309f2b60f6abb7075c5c7f0a30ebe41d1103c844b7bd0005a749283b2632bc4464a2528d0672e9d68fcac368d2022c317d575ed95ab50450c58a6879e12832323c6a6557988914409554c3967919da78f3483d3ffd7a0cd18221edeccad300f704b22bbde97a8731d621549cf80826cad8d2f790415104e0cb5b485d58088113f3b2351f66784cd9564c08c4d080400fde682d5616a40564ced3f299a235af7e7096eef36f3c035a9a67935a50160134a17e4963ed5502dff4a0c4759cea00d679a5053320cd43518655748a7e9e59a6eb012ac71241048a6625c836950ee3daa2a5e2460dc18670974168c1719672de49a45e9ee8230ad4f8cf9c2413e037623fcc5b85d09b3db4f2d93feb23e5a507f4fbf8ff44dc054b6010536d61be1d75558d3f5a9a2176dec06bf79ce89d36e5b483d13fa20109c221cf0444d39503e77b792572619869ffe89524f0673cf87a76f48c60ac6c0bf090a4a5c061888503b37e92807b6613af7c032aadea84f2151d204573ac8a0d3b73192705d91de0509d48c8e3371578c8b1d4f9f4cfa0d426beadd1bc57570cd8d251157642444ca677fcd69f5b8348fff5c8954a87c6640cefe348386a0b08e9dacb5945fef537982450a96b905915b9c2b4ae494b1667389f6e33dedbe305837fc59ad71f4983998c478337ee6d994f97e2528b4d95d69a3c05302543850a63020708a0859a8d52f552b7f4b9b46b0d9d2a1c2ceb8c65b24a710ad87f2606c681ca9b06e0f86223e9d90c1fa72ae952a4b8b2253a6aea888db337bf01e50391b00b2a23136bdd4d2e58f6c2eb6901ca4f98067ac898ed136a9662325ff70b9df8d587dd2237352af3f81a47ba10ba2ca4409e8d4809696114894e2e916302 true +check_ring_signature e4089734c07984c9c0ecfbd3e334385995dc94f6ef51abcf532f572469bfd5fe 9f34a2410d82278ddd3fe6f5b8b31207dab985b2d5f096461d35013f9793b9ff 1 d5aa85b0ea41e3ccf98b2b4a546e64b322c034a85f879c8e21f04546342d2f23 e9f5badb7d6f7e7c8d574cc11767436f284e88d55649fb871927edb786114b3858462ada8bfabde746f690940d471d893eb7005f58432641376820542fbc9d09 false +check_ring_signature 78ec3596978ed3694ced3dd4456086f81f06d358949e9dd72c5c3a58a5bd1e4c 49bc229f0a87de47631daa01b91bcfc09669c86769ede12655f8e14c2a445677 235 b68891855dd3931dabf146f18cc07dada3556c04cbc0c252237e006c75c54f44 1cbbb44f9d665e3f42bd885a13be33d20bb0045d059e8517668282ee68b5028f 269dcc49e1a57aec2c4abc43d40f0858289b92de03902f195884c4b328d4918f 3f3f68e2d2f7206ecb6c8535a03f2ca98bcd506c6ff958adb899ff345a45d3d0 84988babd5bf1cd62ae22cb3b9d71c9e885d8e4dd83382b7e77412bc5127507f 64e44bea5ee0bd4b58bb2ec106018e343c1d9c7f798f160e738d93fe027df07d 9f271be0eca53d960e884ed668a33cca223bbae104230da62d3e79397436d206 de55463014414062d6667ce4a3b85e479a6b9fc360ca1fb7a64dbab87c64467b cd7014a4ce91d3d2126e1b95658f8b8f41a473b58b22c7846534f5dbec1ffeea a03ee4c0c2f5fc2f3c7b4e40e58427b74e0003ffc730aed6f7a44c2d01d7c198 f9de1ec2e8214497248db074838e77e1e6ece1aa494861781a53a7752b60f33a 69fdec53d753058cdb0f51fe459ea21b27ad77ffbad7345c0684aee9c56e3096 0c99c042545b405548cc75d0de90eaf31d6a16cb0370965d45e73e9e99e0f3f2 7931c8467278926a741abface4a9b8fc6f41fca704dea4fb7f28a30cf9d20144 a1fb69c92d52222d862810ae0cd2ac3c2c8b4f20302304f84e6928f74646a611 975a93cede97b1feba8472129f2473247bf4de6f4e56555a0ce2710911280d53 c2ce8a0264dc75d825949e1f31856ddd23a771711dfd854ef2132058747c30d9 f2a7ffbff34c791841e5ab4db49318b4aa0fd31cbbab42f9eb0eddc39a5b5f0b 9200804c4a37a4aae1a3cc67fc79e5d666388f50b5634cc806dbe0a9f6e16c78 a6529ac723af5e775e8b016f851907f4a93e4dd17bd8ec60eebb056881248146 1256b5c1610b9e21a406bf517918e5aa7ec44ae8c37d7134ec1196a0b2765d23 75a9d2872c7528065be1e9c83f904d6a1872a41957a5bcd18341794f7832086c 78fad558a9751c578f50e55f13a03c5cd96b74eb3f9e843d0cd35cf054c16828 d985c4679cbfabe09e574d9f094af125e1f3d20a4fcbc92606e247a9beb72984 13b4e45b6e2974e8bde4b0d1a3cbc773a15ef497813664b81c72cc54f6263227 0d9807920ca0ca12bf4091158ec3d4593b322989fac1d96a341c834593f7d17a 1e59b45a37b0864503b0f748c4a4964046c46e12f6a32bf69cc2c19566380a07 294de9087d30c845436172da9b0206b50f93da8f97d68ca66036b43c24764628 29a9ffa2ff5f34f1a39e043162c1f7ac7640d048010df0db01742273fea2e00c ea83ad5ed761982c26b0e4484bc2b5a9a3bd9a2ab87d1878d21e2dadd7858c6a b05e8ef194c65e4644e053ef55d7eaaf7f6f0fdd271afd10b8dcce43030f01d5 2c67d80fefdcc9f8d7670ba9d4c3aa8b44b340ef08b08f06a3303320824371fa 5f5d0298dafb5f939520aa5b0638708a1b3d6f5633a42b0717d9140ceae9e245 a4a4108ad8d6aea48136faca354bd6f14e99dd5da5e170b11b987e6c67ecbbc2 6d8d17e74d0f2b4663bc5587e590b8e75d18e804f4fb48a98ae1d7f740ce2166 3ca8a97fd3e8adb2e461ebb04b22b95567a3db82030220b1dac1bc062580194a 4d0ab42e7f3536acc4a6fcf23fdef991dce24a05e9a4f9b2c22214bf506e9506 39f2b3c95b55f3c36d3f2337ff9a97db36f3c1e21d3f076dbcc8bdadd5d31e8c a52e0bfbcfceef3978f37cec1acd9689e7f7e0bb4cdc54b808325ac60e687b38 c859035ad81057f6fc867cd53e204f8786e94ab906eb4770c36fe6909265e448 dd5645db86d37e7ecab33be54ff305c244cadbb470ad2de75cbffbb89af242e6 8c240227a4827589c0775f1be4bfb187dff071df0c96148c0931960b6d9a2417 fba5db55afdca366a6392b47de9479935dad5c9f29d0583465e1eca64ab116be 2aa7d469bbebd6607843c4a672ee6d73a7e16ee7696b1b1d2af0cfe478b24083 0e9b2653b43d7d86a02fbb2e9ecdeecb7a01663e4761fb0d2522727df58c8ca4 2343bb47dc7ac6c49c8cc7bd7419ada031129ec70574495a7e0a90c4e99f3ccb f95b7a594ff0fca774d466d4b545e0c8e900a17d201f453ba2227ba72caa986c 59bacee8724848cb4f93fab0530cae9be2c468b979a29419a346a3d53bfde3f0 58b7ce9d2acac58b15cf707afd3cdb108f59820bb83be70d58b7383592ba97b5 f6eb59d72d3addb2ecc2cd08a6f7a238d025c462b04bdc390eef7204024a41e8 225d6e7c92528cfde0d75a332c3997b94626306036100aeed8a929895fa2a9f1 915c98d356f8a6bdad42fbd7cd241510ee1fc85ad7cf4560c1a96dddaf498322 6893124a927781790f46c4ba4519df5f5a2bd17008385f4ae61bca71adaeb13d 5aff0ad6de26dd4bdfc0a855934d5c7f97b4c82bd69b828a24af476142bdef95 1a1ae712aa05bd352e212e55a71873b208f40fcab790c2082373471cf889996a 4e913185c00c3385b0b842f1d63e478fc0e703e1a04590d448a859b2c448f5e1 9681ffef43e82121430e2f4ac0232e082d987a00ee6a2ef2d61db4e8e5a49353 52ee931803336dffa70fd6451b84668c798e9dfedb4bfa428dfd57dfa70c1069 8b84f9ad919cfa9975a4087468f2c2c6c922bc3b98dea1b03e8ba0601e63c6b7 964a2ebb9af4beeebd723c56372fec78f82d9da80f96df6cabdbf90ca706862c d1df2a08ed2f5b2a547d1ae8e2dd2b9d11fe9352200e7e666a7fd2cf012c2fd7 b32e8175715e4a2fbd3ebe40bfdea3033cf91e7fa75050b13225d33172ee3c44 58abd822b38259a24d7d79e7f957ed14d402cfb31621e487265db7e1ad1a9554 979c2542ffd2bcd1ac0c519ae75310776a201e75dbf481e25a8652743de36b6b b88d703db96477008289641b8e26ac49ce01ae31e39018f45bb27d9cf054f2bd f7a16cd118c7bcba99cdd542ff1aad8b78c5705778fd73b842adf6d5aeb6f799 e4720cfad3b2408a6bc02aff7c630c83e24801d7be78640ba5bcdbde3027fcc5 d8580126e766d8635b63a1f4b1bcc15b1212a3cadf3b5ba8397e96bc4b2e6179 dd342d25ec2444fa4427bed4a041d8abc2e88317b5926cadf243f9f84dc7716e c116f89578e620aec6a4030e7de86b5b12f4f4e0f4a80893d4bf238546a0c5d7 e4c060909b79c4200ced0c17e1015ed80d98588ecb34bc4010dcbcca8ed94b05 b33c2b13804c6dbf86dfabda49096433ee16545207c4276fb4224c2cf6a82a97 6b2bc1fcaa3105b8a2ef7025813c2becfc52946dc6243529cea70a8699e0f502 4a344ea06b4dba3a598ca87e69da66f2278e0072910cf85b68c9f1d29f0a7d53 5198fceb3e19e505c7dcd7b0dc5ac13eac29ee22c510c6b02ce065fd53a55d1c 55f67a3f8f930b42b2f77ba26d6bbd6fcc6fd61fa45e6b74116358ddb46d7cf5 2c73f8e7ba3f3d15598dce0978a3a8444a04a66ed7b1c2c90cd9a20ab465d965 407acbb5b8bde981503ff5267a927b8bfabc46ad2101df55647b6b035ff859c6 ec545e151f5c81eca04addd4b30b3c4c6fc371bbd3ce6ef00d2cbe4f3854e984 28cbe89a41eb888b7dc4e10a9ea32328ae3a787eb85207fb5e5314ac54ce40cf 6c340c8725b61d059335e87a76457d26d8939ea74c0e41515707fd279d370812 38f210b22aff6848595959cd5cf80049968a9638fb30d22865224260ebfc4283 a2b493b09d670552ed7b151e78bf86b0153a4b18abadc327127e2d41f0a34554 51686c8d6e8c91c6b591155dcb1dc025ab3713781b3aee3e857e2c5f35d4879d 822b89dc9efb5341c349ec68fa612b3eea4afd811198475852e0a1bc68211ebc 83ae039e27b3430391cb72c3927381538255013f080f2f74b1e33103063d7342 227e18e691a9b7417132257d23976dc53bcc76e3c9c69d0405aa31710c6240e4 52fd0f3a8f34b58eccd83ba41776ee7ace8f8e34b6d45c4568600f265ef04f58 0bff82fd74a89e6bb8f6ab687671dc44214e03a6fb3ca901cf0d790d5a8ffcc0 ea9c2d955fb98c58fdde525a66178afc416c5c1ece03b345d84b1b7357f43af5 15face2be98b97e32714c96bdcb0dabd1109cd230f60cacb40d7285f158cfcfc 4ffdcd70eb5f852dd976fd161b884a13884f12ceec44285c20925770cdb32cdf 6d4fc022db9afad4c059e7028ffa02e704fa4e1312432313cd749c540266cbe0 04bdc094bdb3345c19118f16a40fa95e6062c957bb1d97e28a21988c78370e88 1e33e78710b59d0e43ab05e4cd74c25d4c6c269d65c71a3c41c46cf1876644f8 231e4fe6549472a37f5232119f48c8e0996e9e41b89414b213e730666dce75ba 405f3ee8a41c0ba05b039f7290ec927af183778d2d449c361721a90de4332b71 fa3fc514387270855d0a75b1619f9c61a8ccc202bcde77aa65319604b9ef4a62 3493a4b44348f5bca627dbc641bdd04969611986366699168faa3488f19c838d 8c90fa05f73342176f0859e9477015729fbfbb70e2c454b648f8c148174e9b58 917cd0231e47f89d5feb02b43e28c6f17c734354b892beb9f5cf7360f5d377f8 752594eb3030f3136d31b68b2b9702990b7a8159a4c213e6ea653195f2421cc5 7d339bf338a74399982cddd36c73bf4102f2283646c397cc0d995afdb47e8a43 12fbd842bb71ce2002b9dc7091d75fe4220790ec4ce14087142b2374ac832509 d74a150b14ca7d7491f35af0fb22d09aeeaa4bf5a9fcdbdd9f45bda7f938edef bfaa5066f276f83e4ae97216efa0be936ed94e5fa2421c0cf3cc4241bd4b3915 c20d05bfa4db675c9b3084957c7b87fc2857ca7025feace0922e455d1d496adc 1423abd56053ea1f07bdc9cdd6c25df6d0fabaa0de603f1fbad92487b7a47ac5 115961b88330038ef53f3760d970af36ade71f750d5f3bf2160ab56d90b37f00 af184d0d6efdeadeca7bfce9911380d8c714fe3534d6fefeb61492a6623e1ff4 a36fca3c740fa81d8a6f5202a5e04467d55aed45fa149e219e0dbd52bb777e80 4e4d5aad340c1f8530da6c0ae623285c2cd83dc9d81f97872858448e0bb84618 9d48c43a66439f2ead64dfacfacaa73c750cf5a3029ca56026915ae710c97017 21f380cd39a6fed509a42c61acf871f760541ba9b6efc5a95644b8b52bc22602 2dd6875f75bde44e0d208a2a0e6a45677a8f7342cea4c7e122e3e0ec729fabdf df4fa7e91e6fffc0f43e969256073f2a42eb14173917c1155859296bafe9db89 edee654a8a9282dc238541d9e198bdf0b6a2edd24787e5a57dbf5ab6252818be d76a99006e89f3e36894d2830e250349095248fc0be5b2b9ddd450d4256d4010 968b23cbc500be459943ae9a05e325d251065ebc55b7c1c4333d316cc6f67b4f b218c696f60817e4264a03bc092870e62d00ad617a0d733faeccc390bd6f114c 9ddb7e2f909f67c55a38e292065e3af03e04543dc1df0db51adbe33157c0dd56 080d6dd331ef4197e90c515a35ed9c5b15328172c62cff0569ab44e4032e83af d35ced4531637b753127a352a827c60d5e295216bb51f67ffad88a470ba43b88 46ff2432893a36b2e26495231f76698058b16a1faacff82cb8721184bc93f4ce fe0561e986b120ef035158d8baed32f9e157ba36a51b8dd87d521abbab6b7e6b cd4083097ad30bd24919896373b9545ecb2025a3174f109adfc4556fef37d193 8729741ea2560dd30996fc11a79a1bd8ccf7c412b5820f5482fc39af57436c92 c7eb687f2a22e630ec4c7d67495aa16da95df7a40778df4eaa2920667e31e5c1 32eea632bb308641a59192e3622dbb15d535ff3ce89672b04400b1e610d5e12d 8c605a22a806e26342ffbda76ae25b3caf2578f58e1c97d3a261599da9ddef40 62961e7fd892b718da90a051316156eccdff3ac89a1cdd2b5277a70d4e230d64 8b2bec490fe29b195e10f8caf4e9dbc29ec5c87165b03c507894e01bc221858c ab3b5abc31fba887a62132fb593c79663a2443162b2dac6d18f3bcb7bc925cdb 50180dc46a5d00129b91d8bef6b60ce163edf44534e9fa88e11e70a9c7dac042 87293293cb229121356a7d47213d692d8119c333001814e2fc2ac78ddde45013 ab677100f281445aedd66f309e743aefa7a766b3bcc2d2f116cdf3df2f52dbd7 00777d019f20c0d17496723813ed47b46a3ccad6366c21d9294091b1e53d87ac 6ac31d1c1a92d339928b4d280a9567c20b8111f61e624e014241a371b1340fc0 aa43f36f1a8e2aeb229456dd69125d21be4d2e448cb9c7783d2e8720d8564543 c32e257152824f2267f31e82a84d3c0a8ee2efd5a335b0b07d059fe5174f4972 5431c8448ac8b64d88ed89d066b451754d5b56517d583d5e277045af8bdcdf09 20ac03309f3a3feb843d37414594c9f4d67fb5e6bb161b59ad6817c15f36efe4 7a60c8612b12bcc6985c6042f83299a671f2f340b70341205a3c03ccf7aeb630 b7665e8aa16c0edde9d740626b6eea359c5144ff4d234c9a97ccc49d0c411ed6 593d05908d83301a58397ee34db528677b3878a906fbccc338d9c5834c00da10 bedfd708d8700ad45bcceee941a304c9000c9d2c9c05014868c2c007bf5d3b88 12fde9062c1b7f5c2a0f5d342edbb1995e66f9c19db53e601edb0743cbc067b4 a680759b5c1afeb9db90270e91bbe671795fc59b423f61ac05551d784753e90a 92c5de338e8a43d51b9f058474d2ca6bc880eaf1b9523ca1821b72fe5c8dadc7 c5433ce8b13966ab82729e5c271c3893bfd8649688581ccf23896e8b5facf898 d0d2dee9e7973f27b636760938b8127ee8283f9738aef15ad6b07e55f662170d 9278f69b2eb9a8d052f02c3e603a8ce3a6e6a3c2906450e24eb1314ecbfd34fe 6766e477df16b5b9c9c834b2b51173b1df0eb45e100c528be7760581aab9e0b3 05e609c74305c5a217d3120e65c990444f4bceb4619916daf4f93c1121ccf9c6 dd064c0e911798a94e2b9e49724900e566dbe7033a032f1676617b02bf930739 52de47bc21d82bfcab6d8516771cbbc91183c3c20f70b1d7922ca7fe413b05a4 eb687d71065ec793e8bfda0b903d40b371728efdd7bfa1f152b6f1fe808f65c0 2d4551313bede6a9619c4e8a535246531baee66e148ec4c664a01a92687963a3 982411d358f7b79728a0917992d433a236441709d5573671d779517387892f5b d9a966b5ac65450206a25b00f563e32585522e24ce641e39419eb750ecb503f1 2d1b97cc692b6dbe45e6a84032d6d2a384565ce31ce0c932f3461623ca694dba 394dedd49aa48dcbd9d623ccebdeb25dac0c1357fab1aeb5928984a754463012 862c1c02c974be3b7dcc431bc19efbafd779f237368817dbe9ff78f1bfd8d7ef 77155cf8df5e1eac6516a00e89abc3b52c54eea43bac9ed3937f2c589c82671b 76d820653f2fa86ac4a344a2abfccaf30c5d079c9d9e30643bbb6a21f69fbfff db36ede3d05226a5eac3e37c571c205469fa81d58ade052091f9152f8d64b310 91689b7fc7bc46eb98f70f34c1954b48347f626267ead2e04ab38af5c781d3cc 7c214c9bb9178fb62a832d5e0a43448a75660302d6243be279a8a89d44d5df43 28e05379dc7ca0d8f8543475282b66d2b40efb617f145b2e133340e7d337f9b1 c6c77e9492d4ae2d9c2d6200bf928c96dcb7ae7aa5c774cf30d0770c5c24cb32 f513b3ce53993538fc3ccd13c4e913d82785748a303891adf64965c686b031a5 d49671108aeb31893c76d2091f712df374875af3ad00bce5c39da4806085e66d 641c247432e99cdb5e73685dd7ec0852d5b008400f1d90d92f848956b1fabf2d b05c2e5b1373230c2596c56c2c1b064d6790cf649db1131e7323c7dc65d8cf37 f6a99eb3f130733644ecae913f1d03bde2f178e6289f260151db6f4b014ab425 0b7e5cb096742d22e856e990eb80d6fc248a685f4e1a6ae34d4be1ecad22b83e 9771a5be2a9fff7c4c821399f003ba974a1727556b74758a3a181082bc39ed0b 164933a88757c0ff28467bc9a734db87701d7a46f9809473b51a74caa14c48f3 b3a17a39239e7dfde3b6fff3f042f3190b9b240fd2505f0cf3157c1f677145a1 08fdbb4af13984b2a35ff647c068576dce375efd4ce60fcd2d7ad0f5414e08bc 1c4eecb44748b05ba665a9d737772dabb569c7097cf40645158552388b00ae14 40462ac2843561b39868dd667b9c67bc4fb79977a5dfd900c8c77745e13ffc04 ae0bd95fddd0e46481f3aa2daf2c2c5b9af5c695ecbd2c26186d74d29e99c736 f21a4b5711fa126af39a167598c0d1dd420df608f1629c22d5fe17a4dc54f7a1 a85730b80e6be8c67c7c0438082d8fb518dba7349d879069d15708dba92ca858 f9704a4c678dbfdee48732b3cb5c05b6d27c2b930ad9b415f49d6db20bf682b1 bc077cf74f4452c04a49d4f237f7d1b7248ba594cedbee03bb53b7bf4304a685 9114cf9bb72318cd62309102c8e649b58a18b62b3f56c6cb17699c1fe6d07eda 28a66c83bed48a6939d877bf34dff023b30e08968de6f3b06e4a6c7bb75b5861 b579449587f339b5ed6e6064373df1d131c2dee853da5efcfe010a30d84a9cc9 42609f743c5b38847e4781336446e381b6ed5f022b0cea6c3a9cb1ee58eb4fc0 4ffe9e98deea7fcfaf86d2c934daf8348d5c52c686b3af0af8d3ead46c6d369d a985b1f96e492e6fa26bf114dbc2778cc1a02441041414fadba2001751429353 7d6cb2f96bd2f1352ce1a7d8cbe4decca3c455b498b3b60d77ab97a493521940 b74788622e63f69cbd1cbbd76173dc93e86d20d4af95572cc10a9294eb5cfbe6 57c472d36e480754260876dccea751fd46d0a01f2e3691021d4bd4735cafae97 3c62b89b8ebe6fdfb5cd977609107bc51f3598f6a72e7215a7f0514bc35ab7fa c17df9c468b77e22d533a761754a3b455a429884b562551aca889f914b0ed587 5e688033e54fabf3f93a3454f8dcf4e993164d2b80447ea0b8650e4ddfee7a20 338d8e73e40ca45964eeb6e84b39d2d571fef76aa7d44a5653a11b533fdb81c8 9e186ca59b0a2a7365fbd6024a87b40fbda1b6262204a7f13050190c60fc8d37 c37fd3d981fd16c89098509c171cf36e00e4fba17b0db45c7753907df34b3992 3bbcfab8501cd33558d7d86a9cc81daacd43d93b08b8bec89fa2f44860be3b32 006406df653e1459362e86c190ba776ca49486dfbb5e79b280738a4df9357dd9 3e00f8e500c059a8dccd726237a930715c11a05539f57b34cc4d171b5b32dd62 e85c723849596ce4099a1cbb82587d692692ef0c367fdcd291a90e1a3c17b59f df2930fd3171809bc2e4d39796361a9cdd50a496569392ef424fee37b4ada23e 552dd9eeda47b596d136a63fbada237337e93c900542c12185523e66483a8705 2f35f4dd235bdd02d927c948b59ab703df1c501dad556738eaec99328358598a 27eb56eab1b89d3e7a787af8c927e37719f95b2b8e66bdee1bd283d4ef8fa586 2ccf8266532974b9256e7cbdad8e7868c1c2441ae57a67f02c784c94a564165c 600a3559abd1e5f50ad1fe3076b8b44066784dbb0d2545540d05934a13b1aaa8 dec15e69ddb3162b1f904f18f126ada318a92148721623628fa82a3bece7e267 eaf8a1d161f51b6091e536514164f36aff3eda2862edc394f2c8f983c97fec7b 6f9c267c28187586e36bf607f12541420b240152bba2994c69b3298dbf76b85c 763541d10b3ee125662fd061f7fb1378a844add51c2defdda3648844fae63e42 a7a2c3da87fd1eb7468b4c2a4c76c8f08ac49b9e6215cec6ced2817266165869 c5a05db4c0ddcfc557df138ee4999c78958a92851613c244111bc1cc01d8719b f84f4a74bfcf3326df67fa6efda807bc126d07156fa0de6e499ea830d68bf8cc 64e3f8f57169215d3158151d6f9bc31100100b5304512de062451b012d4e2702 59034b98dba7ce3e3d3db2b8df4f7e17b4fa4b71eca32240a17954190e568626 cd9539d537007bf7723ec12b67c9f289005423916d0d981d38fb714bf7a72f46 2d21c6e1e59f2396d62bdbc73ba944a51700d35cd76ac42121cc7afa053facec 1b674484ee82025a0606477530b0a56e1448e2265f84f5f3ca35a0586ac2cf4e f26c03789b9cad09936f495c87b58c805efa686c929654923babf926132864f5 fdba728511aca840ab627ba9f4e136cc8355b249fdd031fb1553aa0ddbbe7f9f b7e48ab6c38d62ceaa68d2ba9cc8a7fd9f83907eeb0aaaa0c17282b6f973854d a447221af17f623dc536ceb3bbf038ab44c68d0f1f3b8f574ef8e20c128f29f8 6136e3fce3213d4e04d5cc064214609a4deb21885b056b22cd7e7900260625f1 22519b84f4f82c7a3adc7b8ad3c332c00b80ade0b83a1461b51ab31ed7ec2068 1757374d1e5a19a43ca4c66ed94f3b3df4eb4c10416aff7f5cad132c52673ab0 a0244a1b86d2941b38db509ac7467bf6753be41cc6b7502b1b385a3fb29b68cc cebb2700e80493154cd0cfb307f4e9c71eb24f985a0e6349be5c029f7156535e be52b2f9254c4c20fb78de366e3adc1068d8a52e2dcdd07cc2dc20ac6cd159cf 20a7516f409db13250c1513f574c14e546bbce225ae64f2ab2a2963e413e5917 c16eb443127416197398288745992fbe6657af65b4b971c7b9072bc22e343007863a0a00e287c19387af66a99deb1043b438646726f2f368a2913db63898740d55a53aebfc629e4d8d44a52e8fce95239dcaab490db4564924c6042796cce401f38e2ccf140978db866a5a150d65802149b2dc76be020ab3c75c5386fb4e290af7421795781d4217f36c7562eeede4f17c5b9fe7bf5a1ab1df545d3bdda5170ddeb87f0f51bc627ca9353a5d61929f2da80a6f13af5045e35a1c0d542d804f017eef7aee0f3f631a91c837591d64f7e24d3fb16a0f65ecad8767db30091403078523ac207f9f2a0a66bacc67dad7aca7a6d2681a15f352751886e9daefd7a80922edb667e0b9783a07f6d1d7f8be11ec1e90eaa474bfbdfce4b460ec4de94400a9f655d9dd635447151378826a4f5cf0ae29b6b503b907d5737232ad8c3d94009b1be894d9d8f24b6eedaa7abc2c31b258375fb4d1fa54019bcf812b89265909779c4602725f35a19a67383b5ae1d7f4ab512c9abbd0abc0842e3766a26d640a008177fa6404a1cb805da89cf7f43a68c97facb0141fd668917e15c3ba26590b00bb79a513815105c7123b88493609f47a4c7283b986d98824c08751b419f70ef76285ce6a4bc4800cff137f373540cb38691a60d9e47a874c1fc0e52da7f8006864b826f50706a6bb52bcc2176d7ac162a56847674ac8e0877b3ebabbe0d105a84b1310284bda31fe33b882e1f413a120d013f1bae9f88613978d8deb76cf090a267c13535f2e338de71f31e295978cfacabf29a0064c947a806882cc18ce081764c5824b2eb3eb3597feeaf99d170cef3427e8f823fa7c103594a3956117035706db1f1c20a236e75d711729f62d05ed4c0f808ae5e636f2dc3b5bd5d92c0fcdf81dde5153af0247518e1dc5822d5545275d02b5cbf728963867e36fddc709915e78ff60a9d371298e9812105880cf411e60da224559a99e561d0cdbfe95092e945eec173f920eeafe9a28795ae5805a277e2c6e0a425f393034edc52ffa0d7402e698378942f06c576edad046ae8946c7adab52bf7285265d3c4cc65a8a0b97f33b3d91df470cb9dd92e56f46bd1e7b7e62203be7b185ee8239515dd799061ba7176ca4f7d7d15008c2bcc667b863099b1814eb3ca3cf3cca3b9a378147052bbf0f1dc8ebaa7a6490a177640594ed04cfd47388f1f80a8c8c6104ae54d208cda8716748d0319a02634ea70d5f70be09241d5c2fbf5da83903df2cd13ac7071259cf54688613b640f5062389f2561165a7273fd462d6d908666e0ed79fa00137bcf8b8415b427997bd808076b8e8201b026a0896960e855ec244f2f3342b0c1462e43532751e64c95c006a086b5d09477fce0dfcb960d15267c53b2b280e02590ab1c0826a12fd3c1d22ff0db64d86826937b22cad42ba9dfdd84497ed8f00ce893f8cdc22766071954f9030153836fa4c7cf45fe66d23670b80285a414d0a4bc04c4bf1f509d2de6ad8c40a8f8deb6718bd7d75ce7eab93032719cfe7b00ca830203b256ccbcf0b0a529ffb25885e22956c68e836ada26edfd00ba1f057015430d966c4a31f1914be86ca67a365cc270cf935c4ef7fd764b70495145b2a01771a82b75268b397bbae94bc007bb0e03967e7be1ac2ced407e68f4349b19301b7a6fdc04ddead3faffdf07d16dc72cbae11f468a175c1477db2792022583e0ce85da51a5f8e3c8b24583a3c6552e93707938fb5530b131312d857ad0fb0bc077a426e8dbf79f60a135c05a416fe625d6a7fc600edace5419bfe66736426f1093cef20eec61cb019c7ef1c8deebc3749e346c3e76a8e509bcc230b73778a0b0f03dfcb32b47d806f25d6c8e1ad2fcd93f2f22939d215889be59ac3cdfc9c480ce8235c586d4c0adc1ed41d09eef692ab21f1f65469684b05bc0f22370100c20fbb126ef2ae74570bf844d732e0df1b2f1d96dcdb0dfc41eeed55270c7e68b00907941086df2b877bfe31a7ec2a713b81389ad8f7239e60844dc4e2e1f86bf004aad1fdc4a0fa87e0741b3a45e9a417090eab7839a836fb6f7ccdf66341666e0420636aa1879cfea83df55eaf70e2670f433b952f16b57a27ac92d780de3d28028c24ccc310b010d4066e1232816d75252033d6c14df38b667045ec174dbddf0337049048dc9dc00cfac9a9304c434dacd40f0090e778616a107061f5f8d48f0448cbc0550382c11194ed2e93724e50b28ebacbb6182e3f9fa349725700da7304dd6e595dd2ff7b66914ef33a8141b65c21722c53b4d70398f24f0456054b2402abd90787243e5bc14e2e4d80f34eb18892fe7ff9ae02cf5fff0da2c78a567201ce898354828fbd8f5e5f3aff55208fb9f31daa8711c92844c750636c17b04002ca610874e992a76fddef865dbcac5361166bff51d481d17fd6d4455322ae130699d549c0488cab2849d99580503fe6a3f274d596c9406f3a51f6b58762a4b1048d77980f48a2c4dc8b4ddc4a18dfe619e82299e388d2c0a461b300bb45043801b3781bf276da3ed635d31a8b61065eede97d8adcc31da5dcf299539da13db8015a7c2cab437b61af1d7adb137fbc0198a5b6d2b20341099daa24708e727f6107d900e6012a1c5488542c959e2d53ca31cfdb490ff2aae9060217ee83faf54e034968280122e28d92182d810d72ea0b04ead12f05ddef1972fdbce8b35fa4dc0fd336e3a65d11dccb5ef595b59461f25d2b90d59289b88794f019834c7cc0750b2c4452b143251d436748fe8cb4f00a8f6035e0cde59b966b0345e5abc0cc4704f3e24b2c2c2fc3904c034252a838672bd6c65f2673e9e20e2ef66bdf1f68dc086bd037d9b77687f06df9bced48459d92c3406326e7f45e3b4c13ab0d19e5eb017bc271f8880794650030d70ae402c438838c323aeeecf110ec5bf997fe6126060a7f461ead4f89e113fda478297366a31276e4c28d60ffed6aaa8b38c3536a04bdd9f8b6caf57c38edab3d43779efe1c6768e6363870749a8f0d2d5966fa7202961959ec8601ba60fee47875b25ac3ac7286d324e3171016d52781548eac8307909a8aeb0a505751a5d2ec6c73e36efd32489cb5c5a25dae8912e2bf783af8069b90a49478f15fe16341080953ebd6ed74a0f381cfeb6144030a49cade4a01011c68b502ba02707beaf9b57d64f4ee4e709e63e362bbc59e0e916dbd86d23b0da170b9bf1a9d0030d2ba9e6278732da7bc35d90318456723a72b49d4568ab40abfdf03c6f76b27b5a9e45b1215747445df64f28db8683a8ee422aecf27622500e1100b219ceb9a127a33b63c31e54d38210dc0ddbdaf8e9fdf05d0dc5c25010b82de09024b3a9186f2c565cb2da3eace077f5f814c6fa62189e53c6fc3cdd50ca4dd681217dc8ed922a638a34730f02acbf2b3096aeff17834b10fe10a123603b8817cf52a774ce9e6f3e99a843f243c3c10da340e47189c6961c52dc9ac92039b640dda85787583e983cd4b88a8b07873bac3af63cbbb9089adbcf8cf1b9c03f2bfb22abfad04c2171c76f465a769e153a7eda7551f855e991c4cfa843d390594ebe66abf180ffd2b2cd4a322d703a52ee502253e412994d142ea8d47ba2b03c80392e5f06a928ab735e59f57d43359719fcb20c0a0092140e0ef14760f7708aa2c1b6f6330d845a52ec9e6e24f2d6f832cd0061e5561d64abfda35d15e7b0355e2c376879243df58cbaf3f32f1d0c444671ec2203d3c312af855dead556f0f48c9f3ffc4d4fbd10af33579961a86bff8143fce5620d1a04aea5de8f5c65a09cabe8d9cc9bcf298f67f55f5fda1d3a938ae73cdfec56e84dad914781da2280d12690ee54930dee8efb4e61560aed6aa54823e9677824a19d820e52196c5bd012482471f230e1b98f5edfcc4f14924285ce8a20d34a75cd7479d34d99aa5610d298838ffeebc20a8aa3d81a884f43d1920844bc3bdf874a73a3159cba8941a011cf0636e22477030d34b033aa661bfa7a74d1f29df475778a3602161c2990f056aa262f5bea74f13e847b1394e06b22512791ee54a5d3213ab37a54e7476750d6aafb87b2f9ddfc4759d0b98df95f1d8d5d8cf5b33477b95ccd3efebd7e6be0b7a8e2751e09cfdd24b4593eb62538770409c5aef96dd45b44b96c143a5fe5e0cb61f64cc48d26469531e71bdfcb56ed6f182c3648b00993500f562edb85ea50010a751e102317517b28ad80ee969be773e3b88fb3385322576063519fd6fef04227bfe9c0f335140ff731bfacb2b301d8d694002e690e158b6c978de29c8be07199831180aa0de320b62e02cae0d3bd6746145ac017f79887724e131abef6e097027c930821684d3c4d4764a72b07422c1de03d708986647a2b43561ada11d04f2bbbc58525f11a2d6849ec0879d540c2c6e6554667c37220fc4899d0fc19109128a90f600216dc7cb0eb017f751433e9c27c77fcce991f4d8b96119497c0001af6dbcad8762211afbfa1e6b6d7f85b56a65694022b1fa5d684a27bcf5dfee0862b00e68d6a0eadfb4a85ef813a68c925009983421d8f2e820a9b3c428219e00f9c2a958e11619c2a7c3cc2d46d97a074370b1afef5c8c7a3cc598d57fb54e06ea2faf4b68a76a053f1b84fffd1be337ea5a9ca5447e8425af05657851e5e60ed29640ff05eeab643523e66da44857fc57a7499c23010591c99dfd06df462a03d3f61abb23c64fbd9a3ca9bacf76c0e7b9ea0c3a4d5bb380574014aa62917608c826673edb89845ff856579a7b9c572d535ca4999540ab959af48d36433de0044f814f6f16f7fb05f0a80e4c6ce0b2d7047c9bed94a3f80ffc28de45b0485605c9d890c0089aef7b781d25f5b3ac9bfbb2a2243c00768435793870160fabe200ec3ec2359964e4d1c8aa012dc468dd1aedc2f2685acf975ee490ac5af1725302782fb8d1b87d7cd105b1a162b0364d1cc2381639af739361c207b87de769ce0d45123156faecfe134acec96a7040b272696dbb7dbdf815963d4881438f1cfd0f6934aa4428c829cf2858d690efd7580b6e7209b3ff4bcf4f916c0679a8e6c3000ba02186f5189786405baa22ecd19317dd5bf2cc8568db4cdb4fb92f57ebd502c3ecc0ff8f413b07d06025d7a843695a9476d3b0bd85c3735702f6e3e17fee01b7c68150e3f24f948a15aa5e1c40f531ea22a53e516c3a146db4e5693b73360db21fd0cdb4bacb00b83c731e756e2586d8b63145c8eea0ff7748b7633e3fd90745ff810617c11eb68061a1979243691acbf62f8c1dbe61e152066756e1f54c0bc47498ae2fafce4da02c5b77f69626074ead62fc271f8ef519d01378ca99d10a22eb67d8dc840a8c41b90938de059b29fe30a9239af2fe32f4bb2d3795099d07a11d1937e1e35dbd033568514c7a73ee4c8ba9bd6ef24165a46779c7aef4b70bbcfc1d0aee3fb896981afd3e66f1f8efe3f76295141273f9898338479ac4c60aa1dd5c711f7c48f0d4b73b57c64ac7e4772906113f47023b67b77ff11360e40ed27b1ae266ff6f25f7f94bb86041ee2e0f6f30cbd53ef1c9d711ae42b7f1310fb235d92c8642a9317af9adce25554111acd7a9b4937e09ed8173f443ce11f40063176eaab036910e35d5d2bb8c518724028e13e19163749b5cee92df1ef7cd0b2d168ba8cd87c40b9ef5196ca83cfe819f71d5d9a341c967798b22e95e104d0e18ffad97b61459b2dad7d51e78965fa45a4792e3b877159513a0463651009b07762e22cd14b70e0b3d65bacde8650ca493751b4c31d3dfaedf385e035b45a00452fa7b09c804a22e775e06b3862541591cce33e4b287cad5a42a367cc6ec39059faed6e079f047bfa092baff5346f61fdea5bf166748afa8ec4f8d478234880a42f0dd82948e078280f4e7ab82544fc066c8c4982b12b79f052956cece061c0b4720d4b19ed378454d2a8a853f3c791dcd037bf816104cf6df438d8cc740ef000e9208037120aee5039de60e4d1bb55086200b890a313231dae66577ee73370c1f66edac0276f78034a37958461e4b4a6c942284179f981819bf009a17370708f7bc905203a4456a6819e1d19d01190a4e6c0f54d37d7ec95a5349b3578c1e012bc7ba229bd74d9d9e7604b9714b2a54032fae2446fea51a7732229dd36f050830b71dce3a35390602979900b2af8bf06e6d394f19cef69a0ee04970d534150a25c2b0bcb30b19c4f43d9f937253e2574a0dd826b542ab7c1d1acee919a1b704130827f97b36f22ef24e4e68d06ed1071f4b3fea73f5307c819ad15329fe9306e080dc683a565183a16eb830fd728ce774c3cb6fdfebc88f6afeea0205683e05474ba51182383ffa7ac8fde33e849cccefe29b764e95f008d8224fa8f46314003ff2aa1df998ebd6121f9ff1eda1f0dff6db9d4b9f1e73c9ea3fd1344a84e501843a11130c2dcb65bcd0661302b357f871617503e544b556e777876638e7710cf4c37faf135a0cb275378d3348c82ba2fa700656bf87cefa0fcf739b6ecce1006dbb242e21bfc668cef69e23b5c65e393c950176639dc9394f4dfed7d0078003008350ebd204462a8263503fb2ea082c18205aa94fac43db27ceae4931148f006f95f64d6f81276c9d24a3e34b78d56b74a23a04819c120e43f38c4dca11b509b85f243a7dae10d78a0508a92b981d36419632a2714bde47e6271c18131daf0439f81799816729dd038657629b9be37c1042b6b039f0109eac432f7f59d7e706c3fc0bf1fab2416b832bae96c89adc38d0db74a1faeb53a577317de8e9ec5d098629d9838b0b71d3b4a0247b0528dde19a8edff58acc0569673edcbdf7f1df0fc5424d50ead60833bb424e085a0d6a569ca5b7b2aadaf765732ada625fbc1907e97657539ed8f7180c1f675289483343113a14830f84925c512d87536c78b20efe39ef0608ddcbbe4c3715c0eed90304a39c64e4a9e757209af7ed5abcbb120f061ec2761fdc6d2288dd823d7ade70da8f27ce0354e1a21a930509edc26fce0a5b69122786da9eefc7f22a9475037173f6336d1411245e653c48e44b8f8d190c3bb5260ce1ad2896681af2ae87c00e2835f8e18bcb0d1039881e9b1137983008f635ca1b740926c4a8593b14fa9ddfce567fc54f91412eb9c13084360c76dd064e3ae76ae4b47e8e772c3c24207d37e382a60f326eaad3a7338c6c0df2431b013454b5a82532995687a4f56d2ffc1ec8778c798d4329c1908d2cca12e8ce5d017a74e1e128832de33532906768120c8ded42eba4ec10deb89898cb0e077e2a0d8538216bd04321dfa8dfb289247f69dae2f091a77ff1d98f9b4efe4d514e3f00a91c2da7b45c3732ee856f3c12b5563a807a100fdc6124f3225341ccd3237c0d4dcaa5b3b0f3adf87b8ab737c3df47cad980449ca3fcaec0dfa3c89898a78704b7cd5088e47a3566841592f5acad2e7942348955faf2ea724c539ce2a4d28f0d91aba997a4973f7cf5e57a8a40234650930222f424c15f3af75a94676036550a90e2a4e6fd5a4fdd1de5d9b62c6a92ed7011be094418a8be2116645835642201094aa695c3dcd1dd04c199b458bed30ac9d5a51da1caadd468b0f9fbd5b7590b166e80e7495c2ffeb567a9a934c9e8b5bea7e4e9197cf5d7a1e0ecc73737c90fe5d0c2de4c75fa9d80af439e80ce237acca63a9a0cead63123e94e06b4fe1b08b81282801b48e1608d4dd717ce850eb61c0f6f1f5eedda80f7bcdfde2f16890e0f7d83db7fe00fe8119f5eb08688ad2913d7855b407526fb48217db625f960055e806875dac016a8ca31aba8ffdb555a67c3adac569d2b712ddd669cbd119c0f9ff52244946ae09c6be9f5164eacca69458457dd6ff55b4dda6f6fa11d13240153bf9fa8470de3c3031cb21d7f9465bd45e25cc5338b5eda1e16075de654760c6fa25f917a012ddb3f770a8782038e57431bca28702bd58300c3d31bd4df05033761d96d316879d0fec0003b79edd9a73ae9215b4259f86bb459259e666c5203c19a18f590b70f1f2d93b111ef7e952fdbc32a889216d45d42a8198e377dba0ebee9db072198e77fac78b0f336669e3b6e4497235eeb6dd5baec8e0cddf69203f5e5b7b9060856bc04b4aafaaf6d09f68522cc4907c197e9bb4c3e3c5e44c50baaa5e69222ee2f91d14914ae58855da03a9769719dc5b62a39ffa4a8ba48630726296e596602f329945a0ff8c42b6a303265f43bfb89f1dca4e7078f654ba70dc934b58a15d2ee9e184c3b087d4b47737ca8209029ef8e35d288515f41231b0dddd23ed65554130bd8964d0ffd3d95faa6d46ceeb0d196541fcf79a19702a70cbca289d51a2aefe5b2a87799a55d41e8daedf91f49fb22ea269379f1d7c8e60acb8396f67552e0256f92989344abb3b74155ae7a9bbf893cbbf26b32a0c94a055c4812e0548ac00bbf27328ec9d8f54133dcabb23e36bd9b7f4c1f5a53ac590846931169885349d1879593301cdd58a8850b5256aecd2fa7e6c1f13dc5f97f0d4ca80e256ad156021474c954b0449659fc6f842584f951de8fccc36774616f08dafe4fa04e68712679ad655d8d19c66e015d7be40b962e3e839d62eb3a492103f4a4959dc420b3ceb1181d7139433e2933a6f9fbeebe386b3aa0c9d08edfd70549a5cdfc4c8a55f094b07e614f7552cecd64b2c58eece6db3e1cabf6c7e9d80ccb6994812c9819f7a5f0abe13c91d3f4b7c097a235f6df42374a301d058ebc0ada7064b75b73b557d5eeefc10c3d686b9a5a05de1e56f765bb66f4dfcb4ea00a3044f1594a4255cf6f729075cb6f44ce3426d963eaf25eb681fd4a82ec342a0718569b148a8fa9c5ca94e584ae11ce9fbd780ef65d7f1692185ff082642bb10804d93fca8dc67e39257595dc2a14e83abb9ae5744e31368b389ebf9b04465f0bb966bd3f1d3bc24313ca9c0cd4625a4e4a0271e53c185181a184a805f91ae108cdb61ebf379c2f26f1d3b81925ae660f730a0adb578ccf31d70e70b7b02dd30cc72ac97b277795689854d30f217721e7b0bba0302e1dac9b45bb22e03a8dbd051ba2eafc19421cccd4e7ca72f0425151e60bf7f226fa3b5c42883f343225f00ca8ba897610fb6ddc777697b83af849806aa1fa98d300abbfefbfaedee72bae0eedfe44baf34d867545dd2226b3fb0a2ad4be73dfc5953a55dc3c38647c7659020eec8650839aeee7dbf6ac9a1c0a6a504ae59678b1868b846f47248e6c5cdd0b27dca22e9648b2a639ba21dd3eb4952e4f5feb9339ade1de35001a9ba68ce803c188a3410a2c58c7a6243b1738ab4e278ad262f1b986594608b8ae540339f002a981fff28b484a43145d8ed858cf8b77e3a5a74cb243673073942c7b5458490ee824475b230ce00825620b2145a4350a4721c072a55254a8e9414f48199e4605385578b85bb47c607fe56ef969a68add699c45bc567b07bc66fed98da92a6b0a3bb4022aff4658cf22c79930805cb34b5cd40d7f93fe2ab01450945cb1280805c2c5516ca99d0e3b64d0d6ed0f596468d7fbe70f4ac5f2dbd822baf6b3389f0257a3f6e2f63c71b090292d865d1d3d8033a773b556e3b7729adc30213d149b011f09e3b3df194a7201aedcfaf4b529ce1e92c3675d87e3c93548bb2f56c148033760254b5bef8049d39e248167ffa02e8aa63a75430757dbb4db0331ae46060adbff6e861c7933861de135df29099122bd839750174302c92851ee591e275b0d47da7c291d625d7af22b84061d7dde46018ac84024fbb7bf05c3e3786055c509486ca93d2cd769d6b9b9409262cffb74747514135725d5952bcbcf1f8ad26d0cdb54d310a50a6c0e2b6391ed9720d1c74f92bd2d4a140c13f5ec4f4f17cff80310a4d6eca931c910135020963b1cecaee165334d7cf3aa7834d8553d776a6108311a9c99c5ef3b8f2e5492571d435347adc4cb080a4fdcbf5fddfac1f1b03703cf16a7dd49410b8ae2a6e0905bc024415488083c73325fc839c6729b4c70a102b694c325561c5d43614c6f583a8b0ec43e206fe1ac32262a09078673c9fc0609b0dd61a9ff59555bd44b73c03ea2a400a83019540b62c04498cc3c27da30c70a558ffd5ef4f19cda2725fa3ad23df6ab7a0f3815b37237463cc34a2e8d352c05fa9c1707598b892d5449b2d17a323342553e3de12aa53797635a6be03e1f1d07fa187d0b9996f09e39de3f9aa71df8bc954c29bd42af71860d7012f0e6d9c50aca7cab9d79288d282d34397c929b22172407b6c726301e823efeea1d1356270126f9be2156f886e20300ebc16db12c928c64fed289471240b79f4c2de45b4c0adb143c2b5867cd4451c92660ce66ff593ab33986dae5c0e00e78b56b8f6ae305c47f3291f7b2f5680bd197470eb2504973e5f2cd06b4602b744f0000eca37d041cd7b666a1db04f0888f84f2d9836999342b4a6265ac2b9d26fca8ba693bd1038e4f18269eb8d60b4715d8527de773d38a79c71bc3fb00a26e45fbe3b9842c06c59c015a38745c04aab7b68f1c99f655ed9644ff4b5c29c2c40504446114050e8e4e8daa6caa2c8cb5de6c2fcb0d941b0966858d947742257d6c5129094b0c0fc34e9041b552819727fc7b0b712adc6da39a06a945daba1d1304f6505836540570e7fad2276ebf9d8760d9987ad22fe31cd90b0ec3980b95c22f86d4eff967083aa0794af6de7b9d512398da43374f29ac5e5ccccebafb9f6782268cce68ba04fba29a96d94974eca7c5f0f2ba239f06d9c16b125b4ff31a17636f973a850d0ecb50e7dcb75bfd7aa38e9177c9f231b82b1ae28a23282f9a7e906df1d147680cd694959a5fbbd8be66716fbf21185571a7fb81acf9d1233a0837f3e085fdde0abccf2843a3e0e946e509dcec3ca89be5fde0f168ee51aa334b3c886d3b7ec403119b8ad290efd974fbf4bb82c24efda3a988533a852327b67933f8fe22a551004dbd5df22118bca69eef19c9d94188681fab949e4fb254bd24825f2d71ebb206ec1920097c44710353720d567e736147551853939f53ae0f0f7039c92396f6086d87f5acdc24fb3e5aea96f7bd3eeabd2ea36429d68290ae849a8a1f17b65f05dab87bec5ba4870c65229d9b19dabf85064fe82a39d1f40778247ecb2fa8c409249b35dc277202b642f76889cf1b1a9e19093049801989c6077ba3975ef68202ea1595455d70088322df50c8ce933498d6e86d549e9cbecd234cb8e1d564f40fa7a3f0ab16bb81e3a546bd1d4976f19b81eafd2ca887ef7a6c51cea78223590eadb6cd8430e61b5ad033eb37dfc2c087911afdf3a53b775f664606f1590531039ab42a4b7eea7dcdfaa9de1ec66682e993feafad33e5de5075304779650d8b0eaf6893b143f2eea17c54def121d3cad5dbcbb656c9cefb4941765aba655a99018a88528f42e2c32cd5753cc9d60dd293d1e05a7092bfbeb2a27631ba0449430fe09071f95c8564fa197cd888cb5fc52452c87431c7605bdb04a926653b2b3401669b279506cb170bf9017232ee2ab096085691c4a051d0fc2a96e7037a9bc108eb863eb6f32e9894cdc3eb1c072d0fb24ce4414236c3ae25c1d975f25645f5077243c7306cdabeb98962ee6cba4b9aa8f0026f8a89b9587b95b63501355c3f00da2dcdb90158999576c225ab0c6148723c04d382b9f0bca12dc69d92bea08e0616def16604b170076d21ce804ad1c232a16a30d89535c17dcad4fdfa7976a40e815abb48a4f53d91e6d0dd37dc557c98f7b3d502297e4f558f6304e5c9a3af02970d0b9d20a1d998daf697bb4d2ae366731640790dbac3b37f88a99ab9e7e205f91b78e11af0bdbbba22bfc62e15874cb52e6e7909554daead5614e8579f650bc76da4f263fcfc8356beb11ec5cc17ad9f487aa089f8f78ba464c178a4005809c693dc54ccfd926595cc0fc7f6ce47a5c51694a842871b6938be8173677fcb024d7b2f17831a7904a7588c8a222d66c3e0a7232ede05c6845a2a5dba21fa3b0eff0aa23830ee53985191445008cc85ab0b386421d0f87a7457af1908166b43052ec6ec95e34cbd2d447b2b9abbaebc71c8bfb043cb6557c38c3aebb10b4bbe080e1667a7a9316715f2aedc3bb670bc6e474f7b4246fe48d878c292c305d20a018ba2349df12ca0f2f5edfc43ffda8562225dd52c46f9527dd4520337e10c840b3ed86f230a0e56e263a8b0c6aeb4b8924864b587f4f1a18804be3e34647a280b1d63fd4d5e8ee98a8e99ed632ad74b0da18d3c8a9f8de49764bcc8c214d4790d0a1491007f954ceaeb369af1cde3667e15aca0bb55c4510b924da49e57aaeb0db2f0d1ab79db1589bd0156a993f65fa8546bc95f2b4dc907704c9136f6d8c20df1ff2384ddd4f53b269f158fad66a0fa972afc2baf31d3badb3cca83dc18ab07a58355a4105bfc4f9cec2c81adbf672bd3152989e3794335e2d6b630eeff730a437c29c88416149447c7802750c1af1c9c336f374c24d4c0a819771dd1c9180f56f831d21878e312cb2dd114b958e67a95b0e197ac4b768c38c009a597710d052c28c3d6b41d062401e3962aaa3a4ff789708c25c6f77644c25c15f3eef75202a4c67aa53d2a4b8b019ecad5594733de80efbe2f03c0d96b0a110e24f1abe601e57ece40c16bc7ad819fe5028c0bb7f6ebc2b73feb0c7f40f9add73165808f0ecef409c876fbd8f2a0a69b21c3c684fe419a3da71a5d7313df49d18124d97f0e2431e1992ad5e732de4440bdaa5f42f4893ded1ba8e1548e4cbedb85dd577c0161701369c18fa7ca56214de0d5703a79ff560d6593ba238301e4a6374eb0ed07cfbe6719b04d89b2cf1ca123b039831a284c255529b7a45a4b26b7e6cadf1e06437c4aa44f2f46bbd45daf5464ffb6926f48a414e66a5f19069df546a1fde7025267f16d49ae471aaa1ddc47d59e4285ef48193d29206cd8f33fe3bcd1414b0e0ac7ef52f23e83a5430d5603a73af5fbccb998166c150f1f4d840b6ad27f7e08d79581436eca5cb347c7c669b8da2ff549aa7d35e5e765cb5aef56fce5367e0ea1dc9d00726f2f85b6caba56ec1e48b4b97fda6302f452c59941e34f3eee5b0c494e15ac112f97c782536fee85d2b921d42e257f314e92a03f0b25805bfd8800bf771d5e4a103bb683cd909e5b7bde79cd5518b1c9d364498bab36c5e5a0570c5fab4991b3c96a4bcc63506acb5501e94d697482c58c61fcdf7b74043eb308064f601ca8051fd0e2bf1d7d87e1ced73733da6c504f7bc69ab7b9b50bef433a05cc97e6e7c79d290fb08ee27502b00efd7c2d112d97669b053c3e213f45a51b09dae8109c2d082af102509fc9c1dd98e63b1eb8c6e13843406bb57e06e9b781044e93f46ddcf19c2229ca879fa91b42f01a34ca1c2a33f0e46e07f013f4ff3300e508269bee7ea451dcab87a89b970d3b9231b3229779a0404b620b5c14a54a023c69c9824a4af02ad44293ac995c68e62a74508744dc0d1a32d9aa5ea543840a8c483f34151e476ec7738e3320232ca3b49a6a29a417d3908debb0798f3ab409acd5c3fbbc46cbf551b4fd99441a0d09f0aaeee4ac46af065049a8a122b9990b13b724c5dcf2b9f2fc9efb56074e047190f7ea3712ec78fc6df0e62055d57f05273c69c99589ebe6d4ff51388b1a942795979ddecda69f1d102be43c19e4e4070ed7375641cdda84321f450804b1e534f11a4c5b1d0cee49863684be869d20067f8247585aa94470ae7f20b3b5939434554808c0f287405ef323cc60721eff067971b20e50858d57f38d074facbb4a2ea5ca84318828441b10ab64d71fbe80094c1a16475b028fb1207ccc9cc55161f18140e746f26c1f588c9edf5439ba390ddadd8bb252f42b4607f666af3469282dc7fe2c1901a87f1b0495aa7bcc2ce700f2a0172876e511b191e16547c6f6255a46071b1bc1395dbd4a9f06178373c9098be6eb423e6864189861dea36ce24a5c15b7e585b4639f7d4a48c0b642825a07800a4e34a9f2f04a5201579434e9aac3aea5d8433b399534b4c56b779b7841016a2a95ee8491bbcb623f8f7724c747f0aa86f104e970a9aa0f0f0cbef55a1b0fa552a1f6cc4b3fe60d215587e8c45825d22aae14b08544c5b4d388a3e42d600a2baa58af5fa9d8818157555992c2a7b1960634654dd4a3184e50b2ae4798de0b075eb44fa2a4b98c8d498e97dfd958cd2392121358b6ce6886eacd8c98410c08cc972d230d1627f437c92c0a861b7d67e3780e4dca014e6e145ab7d0c3ae0a00743920df534d319ae546383cc5b5fff5b165ea7450308745366a3d782e61f30b5f8cad1298c4b92ff348b8133c05366ec354e3a3097275ab365805d27d32ff07b30395455e2646111b509d8f209a24853b49f9f5a2255b50180db8f9c983dd0ba28c2a9b59f8bfd6437bc3bc7dfc9ec8078e5b9a35d6ceea66d0b9b930341d0f219b2bcb65acf267ca38c49a05c760d16e7fa9ddedd05e38d2ce729fdafaa802c74d954415bff5ecb689b4f30fbb3530212435e7fa404f9ce0a8593d21cbbd07fc91543b10652a71d84590f365456d19ddd83f419414dc00dab69606cc040b0bf5a3f5b43ec47a19c8d3cb97276dba2fcdf703c156c35d8940cfadc16c295600ab8b168548719b6883f6c0eac5322083c614ff6446af24cb39790ef3e0aa1f009514a49eea48e34ad812c19583b2295dfe64a7645de8c93f57d410f877837601fbe945e81e1ad3cf319a7f7e941e762b300b6e16b39000bdebcd843c93c60505660a86c3d7da9872154989bfac177e84b4f43f3aa020475e25caa2ed72f35506e1d8c9a1c596048d0d9dbdef12548cc661917e62b6472064a48b7c370cf7190d744642b7666a46fe4d408aeaf4778b113ecdff0fbaa5de8413a2e8197c37bb022657d1c23b09a787fb4be9ca33e1e172625afa5a028c3f07653da3f7413ab1070bab71bf84678edd91fbdf5e072792c8e7636ef8c471f3de32f94ad72101bb0f6fd56b2f27134c183b2a157627e78d76ff8fdfde4cc6e0c0f37c414ef2d16b07a75318a701df37126cebadfcc6b637b90637e45823af50ed34fc60610e87c50785589f503b53fbc72672bdb1d4751b2480a0c6bd7568f9412bb3c55230a04704e03dd7e50c0bed79e74f864d145b29d9dd329b6b568404538ce58f4146b33505898848ed8ee12aa293b7a66eb96eaaa8e80ecdddec3db917d09f1abc9f26900e56e8e180f6cac5aa7b649c2d7451f99497669ec3a7fd8a791110607592d36d091f1be8615f8f515ee1bc665fbd3f3ac93e83686faee6c03039180eae90867c0098ae3b49164aa96cea27d00f3f269b583d21d6ea00b673e0916b627ccd65ac0e2e595b978390138db839cfe6790174eae9a78bd853878865447e35cc1491be0a760cdf2d6a388e9dba396a58d7f0917a2812848786fbb0447920fe4426b0650416b2d2f5c1a2235dbea166f5c48435122ee0d62514baacee052d910474920f01b22122133c0611083b4af1d76a92fbf1ed4684eccae84c4cbd18b42fac5fff07c49bf25c9a739fd583c8799c75e0e97e5db63db5d30a7612f556ec9073269d0ca6117b112a0488413ea22a69eafcc4bc99fb175a977e7a62f91dff02703a330d93a92baccd740a42a2ab5c2523e09a799cf16d5dd601f2d18724116eee3004030ee25027f3fdad86ac4690d1320b5c1d617a3c654b6a4b9648ff9a312ca3850c237803b885e2c0f1e004ae958a3e346a16931f7305804857b05a1057823b74060837eaef4c3b307f020db0aaecee43eaae328fc4bdb4a9b66bf61ad96aa0620eb26c356b499e991589751ea9abc109730761cc8bd1c891aa0f661e6456cf1a014f0fe9a9b6de62e470ad3398d89a2ef6ad62138dbc406d852e752d2e37b6ea04021db6f3e74b1906435bef75559811b61c7506e8ecab57263a512222c5b7fa07857ec024e781bf8e330c62fe6ac6f240137bb6d45bad257abd65c65fe0a3b70cd30264edbbef92bea4cbff04b58ec75ff5330338f8d91118bc491b2babf360078d50aff0e4ed8fd628b5bc130606ad14edc7fe0f14033b885dd4855f4d18c9073ac0900955f841ac021cb55df557a379a0d3dbfa7c442a3415eb778d9783070805f3c5ada57cd780bf5fb35e596cb3501841951c44f5d2cbf0b0f9e3b662940786875ebed9bf1b4eef11a65d84c2c25119c0a2abbc60e7f0b0847f28d8a95002e95943c6b410e4965e21f090e31a00207310dc92c848a5aa26672de849c6eb03409fdd12342fde7bc11c4b5d693ab422d147e7f89f74ddb58b8b91847b573401d7d8f455645e8cdc2aeaf1da810a2a7b6b78873e81e2b7226d09ed340ee51d091f8ce0fbf8020d59118257e20296c114ebb15426a67b845a0fe8bef0224f98074fd9a404fece48f7af0bbb879f088a62786859d3889587271e3f156d42bcab005cba85017d9e99a4898739e8a10e4f43977ce6fe796046386ae200eee564820a6829e2a885cc1c301db6ce4b7cc1ac78ecf96b9f2660228c6d7811ffd123ec0d102a6943cd7eb8eb04ecddc7bd786f850894ec5ea6b6e742195f4817114e9c032f4df8b6a4efeb0c7212bd7f72d058d3c4e5ec059798a4ba08e739100d51b10223fa0b6dd8df1423c47097e620b4a765bd788d8691504906028c259f1174d205812667d752bc8980de795db2721492dee933b60882aab1fd9d5670272b2bf0022fa72aa70fffd1404f5b21df9102a1c995903174f809b6e7eac922b67fd5c30732c0be7e9f27392e93e2b968562b5c26f2d8fcedefef5353aa395cdd7743d103523fe3622dd34b3ae730075221a46fae5b7a1029b4b47cbdd796d01fba4bcc03bd0a9bfa9466e5c25a692b96229dab63ed86dee4041f74c6c1c875b678af7400288af4db99e575acf7834ae84e409fa99d3e4057ac3e58abe4fab36692857c0a578a9c9af5d68691805b4c55b66c083e73da216f621860775867d3a0ecf7ef0e892253307968bc1f248903feeae576e6f328573387799d311ac514216e491309ea747b3a42e7f195330f784a295b808d6b3cc84ce265ade1ccf463878a8c4c011051c16d427dbe3ddc3a9642b62e219fe674039a15cc2a0e88b0718a449dd50a16ffdbe00e63d0e924ba4f53378d4c60e17f5276151cfba95b5ae4fac5a0b70f6cc3da87c2e818409017ca866514c7b47961b8a5714fc7aedde4bb6beac87e0b2b5062d90f36d6b5021c0a8fcbc3804b2857cbfe41eb7c2ab7ab5898ab479c0261e1288d50b7f2d35b893a1d2f998643b5890c508c045a6546a7f0ab28432b0190e63393fddc4a4496ea4c273ec244fed0f58f2882e59f813db16950d1183003d368334221364f98af0b10fdafed0dc85780a36e54f46ff936520a62d5d980005ac9ffaedfe6c2d70a5b8e636730973980a7622b5059b3529c21cae39828a20f9482e1b78e862f52b2240a43b5bcaaa34484a6ad228854bbd0cfd37f6b53b60a9f925058427b16ac54b6919d66ff3173ddf219ecd2a3d901d7bc95700620570d8d2d86c15a0d83d56870fe270493e927066b1575b4dcf3b9889e2d69350cbc0c615bb65d0b2b1201e22bbf8c547a4bf9d9850a33798549922e64ef5ec6935d0242d4a9efb9a20276bfe85d2949d2e837d55fe2f7f0a723157aa84c7d08d94707fce3cfe8df99588fc1b4224cf0bcb5773a66f2ae613e60864aa633e2fffc110e23d93df57241727a6c814f1c257d489f26e4cf882b97f6c387b9fab955236707c402b7fc49be5c527c98b2721ade8671ae115ecc35b9cef2796fe8aeb0b5d5054a717208a715ee54958aeffb68c00518e8ff7367b99bdc9e201c2bf03efc5702b61fb4e20822d8d193c6840c06c13e8bbbe19c258dc7e42c495c6e62cac01500a7219800bcefbfefb92d6a07f5e12250fefaab8317ccafba33a77fca6815e701431432fea1311d5ab1e15085b9892d6d16eea23b9f30fd7ba01e4ea41908100fcf1c15280b5a94eb97661116cb3a713049d3f52fa34f726a883ad75e692a1f02c2decf45adb935ccb8e34d4cb78907849e23f724cca67efc9beb7fa413c52a0d145d0d53e671ed7213df6855b37fa44067a302bf0072287f460e1c8b8edbbd0e1387e870bd0d0fd8c4e85e1aed9d720446547777996c95b2cf0e73c17abb0a0bee84fe17bd44e751196c8e05237e06253e8bf6431639618a65e3441cf351cb0570b915fa1eace5d1cab7527a921b59dae50fd003bbe7d40660ed66fcf922ae0ac8b91cdf50cb480775c272154d72db09ca478fbb52886db19b9fcc2ef2368a05a82aec5ceec79363694ceb8e8633d1c716972dd0cef8585658c3e73c826f41072202c0e5fca2eebe2ba6eb6b7938087509f8bbc374f76940b1644c9cbd20e108bb651ccdd4b66521a9bbb7db26b9d4cc0d86e5a35c9abe2729950cd54b494e01ec0283def652df23699b948fa0f2929676c4f5b654813660c9f62637b7225b060651e84fd91f57e6527dcc5896e9ac4da315051871647f509ecf074ed4697e0fa0e11187d060d0a2e04b183c45b3ab1fdc18a4713861e061e05c60834278780bc553e6fb671427898e88e6a2f0f347a54bd7391e7e7171fe58e154764e36f20d624ea0757b3380428ebd69bbbc159f8de31f5f4fc21c3a30012b1899a069220b12730247946ceb18e6da30ac9e8a26f3044ddea22f676865caf6eb79b3eb4d070d80d9d3ba63d40391ba69b5a6f30bfd3f692fee117c2e88c9f78cf4212fda096c75b9cf640fe77829f2c08713ec5cc63921cb69a96deda868cecbdf93c3450d399a772e8bf37c7a2d3f6d50a33238137fe1ecbdbe80b19bdcadd5a2350cd30fce2846ae53d01f291d15da2446b974202628f0a304366463b249b0f1a24bff0d5e3086b70d652d2f93bc7c6d7381f2d6982d70cba621a1a71faafcb0ef950000c19a152b393073e82936e4c5ca1bfa0666de06c8f98a34d698f43fe7f0bae40641a24079bbd0063a5519658b2dba667bb7b19254fa2fdb4bbbb1aadec71d240dcbac6afc798d25ac3c50c8a97a89284e09706b7ad7b9968e63055a861484090ab56993e39e30ded995d8c413e4e3ee5ffa831df24c78d5350f14ff470189000806c33d868d57082d71faa4a8d7346f7b6b000c88ea7da486ab12bceea62ce0085966d631c540b262f1fe4705162963eb34727e0c675ec2ffe1b1be6818623705a9da64575504804a803d2a2793d9675cd3a32969dd28b98ee8107f57f8632d078f6e5d53af96f51cd9d12e56dbe3e7abd55ca4d13bab110816a9c6469357190b7c7ef1ebcc3da9c07ec99c4ea3b98c001ebc099a578c542bf1888caca3d1cf0fb919ad766b816ac967dd36792688bc8760fbf077352519031558c85e6aa8270b6ff3766b68a6fa52a05f77720242754ba062080e900116a732fb9a30a102780e3b1aa306723400b178a339c44ead88a5c1394d87169c182cf916eb72a01ca30b400c609570ed594c33f144566d74fe0bb1d878a9c5ee1894122a5101f3e9a90790aede41e8d5f0afa3f20d263b050b7e5a5f3b135dc05cc62d536dc46db0b6090abb83581986623d0474b97faa7439a26b65f28914895e50fbd966f05ac82d09b1b50e26b3bbdd13d7641f67bdb811977b9bd026cccf1bfa29602e7f4523de0f69c56dc040f6146d18b06c33658e245ba13a19f54fe0bc80f9784c2c50fa9907d66f2e763a0560d469c6ba083d21111b5866982ba4ed18d148084e4e89a2d8027925fe77180c935f8a789fffc3290959f25a2d0acc0cd7248abb7c93aed29c08dae32e45ea56be04d83784475fdf3b5b203119fcd0400687664ba68a20340b07cdb36b5e03d5b07eea8d799ede5286f0fd7a9dfd7686e5a6d520135049b29208cb3dfa802f7f3b83936628f11b6eee90c7946724f7cd6cc0579eaa615acbdc0e72157a8d3ea1c0df7bd3d7268223d99f5d70810410b9cb7e6bf25f9edf40ff0edd128a9e8ab720301a2eae622f2e3361dbd7c6c5579477c9f74fc9740f91fa008ff22a082f982fd28909c12ed7a64dd1072594a0a46ea95f8e08014f1b2e85082b4409bb540638672b90810cf24daed412eceb9de2f27484e8daf601dc51cb091fef88bfe7e0e3486c76df534d7e4221a275656ccf7b790c0297e76be601160956825071f6d02eb9b706c078bb670219efc5e6095f94f6faa0101ecffae8c60a0ca163cd31e164244fef08386edd3549e2d8816754e5fe649068b0518572320936ec78da8a92e07fe9ba87431b59f07cc9bb4db302b4ef831964503fa8f0bc0c9a64abec41c1f4a37879682c384e2547fbc8f03d9a37197bb6fd1739aa4e07033e949d5c1b237f708b72bf43797236de7a5e42251de3be5b0a51ca57f975a600945886a45505190796042eb36e36dbf818d9b09e4fab2c3f8837496469adb4015119175c96784f610df98e7c76095d3d6145514e35ab74cd9765b6fa0b265207ccbfb5d0ba339fb17fc0e94f225d8f0e5b9f2fc9b077f709a9ab43537659930283517db8565ea514f3d49e9e41f8fce7a73bb8dfec1b7c6b034a3c5da97ab00838ce3b547d4deb3b86e849498b8c8a60a7873759b6e19f2858877c617ad9500e3c0c2e019fe479a7132d8b8bab044224c7549744677c643ca0966f6dcb3c5903633be61364d428b02514b08d3c9566960d1db03e7e8cf8e233a4f7690e4c59088d244c5a47baf7cd555fa03b87eed93abc66919e57fedacf24b5ae860ec5d2080d9d28af755519869b8f465a568200ad8b894848b3cc6a3694b3dd44b11d680aca99fefc4e25cd2ff26c4b13f2911a57c08efe1c6991e3edba3ab68ece0ade0855f9fb010583ca1c670e2c60dd1433c50f29fa77d654ca783c20d787cf0d7703ef63e680cb3522f695e6ec43a0e6e53a0aaa7e0f0afec5e1547589dda4b55a0f55a8ed9e18653649ee7f73a9f67c6b26c59aea9a2b7f06235e876a242421c8025b3bbf64fd2fe04bf4cf9907c8cad52b4e39dd9f8e82da056ce44eb69e18d001ae88deaa885de57a9b7216f8be4493067538bf8ca3c843c449ddc30c09cd43059bacd753955b468e900c7e745dc6bad42d0f7f60243324dfcb1139be1f58a401fd04e6d203c47a0083b3257baf232eab879058eb077dbc2afffc4bb57574e907fd736c702815ffff9ee72493d6bb0c76619bb83f9ad0dfd97663f551b4f7d90ff7bed44c44643c3113c617df3e6c48923b775502f4273f96a4c73ccd957ce10eefa0b18c942237c629cae36049ddda809dc760d4621a20ec442ac92d979afb01 false +check_ring_signature 455b3f529a663815033dd534558b46cea40b9c918a9b9f2d4d87c777c72d4152 0ad7e5e173a7ea167b53dc2c00bc7b5879a5ad3c50bb188231ab86e701f952cc 1 a3a57c9bd2fc79be4c638af63a01af331d98df903fcddb227e8dfe78230ef9c0 b64714b08b90588576ea2cc3ec0ed611d3d70b092561bad714b2b9f7bc51dd7b95b81e6c72d80b61f250952a9d6d59421e6f37567c09fc0a89d8481647445d0b false +check_ring_signature e1f6307fe2dc52116702d15526480a676ff134cbb7c0b4a3d972174adf6644f5 4f0aecd66e3226701e103ee0a1281c63d749e7b1456cbc9c38efc95bbfc6cd09 45 f9dc1969d4dca9d482527a5703cad7df94876f4edcb870a0a37fe313f0be50dc 51aec5b03a1d602d2ac2deadf5a94f25276ea0d9d36aff912476877fe740a2cf 341c32c2b392eabaa82ef197f03ff5fcd92f14f9ebae28ca49614f59efb54be6 84726587e1138377b7bcfecb5a5782f68f736f98ed7c51d70b0c48ae9b68b34f de5df7b5affce7720ca0e2da5a831cd1c8723724185be6d457ee13bc0f6c00f8 2d1d0d89d507fee5fa510076ccdcce5826b2c6704b43ca89e0cca360c853524a 8c7ca7add288a75f7f2487e12ebb16a7c1a9bc108c41e02240966af461a05867 2326297c1186f672de5acab2c2bd7ac829303abf651e390d9511370001b6ef58 3adddf71becefe06371c7fe92dbd067dfc95dcb659f36df6e48843030b00cdb8 eb494fdc64ff2c51c9f71c4227f781eb62a10bdae5539c15146e5b5e17314287 3cf26a14bf5dc92e438890759943ce695b4d5e722260e003b4380cad75f9caf2 e6964703e8ad7720b871e62dd2801f4a53efb531eadc9b052a2f5420ae8f9914 d284e07c5e7ac67ea3563b5b5a2f165ce3918b8258c358b0cc8eb84a987da75f 3ff083be0028e6d783e04c5230ca940c216b5b3a0445acd3f87b6a6957d5806b ccc3d96e1d9080d95af2d72ffcaa3532760a0f32650d3d9eecfe0abebfed64dc 7dc0ec63947d619f02cf7415da653117cd26a374c646260d7fac047b22454a53 7e8227d72ed2c7541dff504c339aa81acc816ca22e9c92da305c9b8cf60d498f be8b63e9419c2148e59dfa2e8cce1a406e59fb7933dc3fe9afa3b18208e3a8b1 46a03b523918fec2ae221a5bed08ca59955c90408cf875a01bcac6a3554fb6ec 390894d3aca74eec36f4a698e328e292eb337e4fdcf5b23f57d80671b00c6e21 b0558539b205177edc5f5422780b3a346117ea01a93ba412978c7cf7a36f6922 0a154d0fa0f3d65d60f8bbde20d427b3137114a31ee41e2e55b46a6315e0116b 48194cea73fccd0de36234852b6807679ec0b09378216555a0bc6d1b7405e092 7785a42e712d5e44fa353758b71b7f473cfd53e08a5c419971c53efe89afc2da fd1d13faaa29a62e5b0c05f12d6e7865618a80dd9f4ae4c239096981001302ad 03cdd75fe0b9748cb0b2dedadc40950f3a811d95577523ef839d4c36310cc666 e4897adae199c523f4f762897f107f5074fc85ad04144432e442636cb113046c b27c3732e35c6f8f0befedefb6909876195bc9d1bfab0ff5d165b1fae1f3e02c fdcb7e03e653a52c4cf0852ce0e04a6df5a8d345e68e49953be6e9145b260686 255de64e8c715ede476d767555565ddb461b024715eccc2171276590801a2714 6f48027af9b7f6655431a3501dc7c04ae6c6585dfb7ee67bc4d88ae0f43b963e 0f39371862e2d54ca16de43d536a5ae3d33e9b2f78ff817b6065677cc3b00b08 4b29aa8afd1e0dda10b754b2f21805304c80043193cfd24f92f50c6756d286b5 b63a3eaa3cf28d112ce9c440eb35c580c06bd27dfea53b91d0f3e496ee635a84 292983ceed3db263ddf076cae6193ba89eebd8857daffe3650d20c2d3b909351 01d5e267b4a12a98a03f658cb789916f9c732991d55273cddd7a74dc2706c646 77f6d9b41fe3fdc03011d9b262cda330c08b5ae36452c76b18fac251a7f49698 bbb8f782317ef18966ad72fbb808c7b8c03d09b8b482d8782474eba822326a81 0e8f6928c6741e11634df07193ce803d9505b876b5e03c2ce39eb7663f603ef2 b26c1f7a2798d7d39fc667f79c8cfa3e886eeba4243fc4e714e28e9fdf309e8d 125559f56b9d0e43728179ab6cc2c77f54c642a5e1e47c911a4e0c699255d589 96fdbe85e4092f031def3a6d59f02e3dcbebd14aaab64072e37e0f450ec40c96 8dad8af155161be5784a27570e7b9aa09f371cd238e6ee866f1dff1ca55899ef 04f9823f94b12340e9be824b3f032d99670db856af3b6e3d6aa1ea72a2fc2411 924c844b481403ae36f2459ed440e91706e7c96436990f003230458d10ffa05c 785e9f2fba88fb7c6d7f4149fab4bfaeeca1818e630e820a83515f1e49923805b43ce399e2cf21e90c9ad5ee519ed918430b37c7a6a686b861fa27aeb710f80ed798bfe7457a1d41b38dec62ac718250a1a45d232bc34762026e22f6c5a1c77e84921941d985d0a491ed788fa537d73d7d3a3f9cced6827bc60304e71f5cbc02b7179ee5f4caa487b2f75fe709e33ba877c7c6da0fe1e5cbdf7d11de0d67da0303338c4a8553e1e7664ed09ee345d4d58e406bcbcafd46e489a72ca8444d2a04e21f4a3689ddfcb7c826798ca4f41c4a8d868aa49d5b27820b8ead9f2bd4560a18926a469d8c9a1b757e1d43c1933fcb97124a35de37b741f3c47f3c5c0b7701bb886238e99dd3e77331fb66323d630332183fb9cf4174e7b980fbf339388302b8b19063b620c90ffe143831e769f3b2e2fdd7736075b76aa7b289c35df17b089df23c28568d2d1908518271779f3cad1b5d7259b545841270a472d085867c04e4afd8072521a52313517d31f86ca24bb98667db663c3443e65663ff455f0e08632825bc2ae9104b45acaef493013c9aec29aff04f41f5860727764fefc4ab06b08da94b490aac9488a0e10ecd53647161df84908a85851927a5865a10d66703a38ccc22bfdda2f05e2a058bec0459bf320451baa08588d05ffa8e0220bfac0dd4eaa1bb11338a856073c27416545348067bc5f4df93594692682dabf25f220447cbbdd2dcb4726e73a3ffcee9b5bf60c8745119ce86ced9212f5d515b85f50b05bf1c8c79abee16d53202a6d12644760a5793f08f4101faf1713d53aaf4770cdac43560719e700d6e5afd30f79b03ff7f250fa480715a3c14396900f0de930f2332e5cbb51716c777ab3967ad7e382b09976fc3ff341d0ac6bca2b8c315530aa5f6d415d8fb3efec7e2023ded6402558bb2715ad6cfc505b002ad9078098b05dcb4123546d42ff3e31eef0b0e81970946581399dd5ccbc3dca2c21fafaa5f084c56b1dc4d85779cc01ef4bbdec74c9f741cc819a38332dea5c017a1e2d85b07d2011754373265188f7303982ea9d4f8d2b4e11a0d84bfe33a7bb3baf442a20052b1bff13a7b9eb7b8bcdc261ae2f1dd30b16ce28c821bb0c853f4b0944aa10224cdb271911ffc16e07dad43c2ca3d8e1bb6a3c9a03948ee83bd74309ee85f02c05a63ce9ed6d9fabd4733ea444e2af5bd494084ce62c754dc04416f3fa9d801c4342ad04fde0cc88ed0c4d2c445a8b7023083a7fe8dbe20854b9e270fcd0c08a1e7091e5da317c622ff6c859d94ea3b1e0862904d016446754c7cea5e9f890f48ac37061f608cd4990a3af52066ee08fe0db6bd99788a5e125089203030a509511baff4bbacc81bbf2e9d9c4e4a8740b26aff17b168cb54d59a291fb3f916018cfab3d729ef81bdbde7102e67b3825f7c26a1e3faa4e0eb7d54756ef5efb80a6d231950b9026ef19bd5c28d686199c69f2bf0db186998c03766705e9e181c09739d4ce816aee489c382bf73951e1de7f37224825bd9e0be847c4b70b20bee0c80e8caa874daec1738c63796ed84cc4f89959a7668b4b70ae1a1cbfac77aea01ef6a5bd68f1c07b00dd495790d26c2106483602121562b1d1abd93a785e915010d08b7503130a6a71449f273ff90ec4213640298d084dc5c9ef233dacdcbbe0df9a4f766be326644d2644b3a442052f55ab5f2d259533b937cab8b4b5c3a9e0a645fbff847043e79b3503ab5ed92e75080c6d8892f143d01d5b48f2d67c1a3027be2b1cf41da22ee2d26cca92de60a6045041dda2485af40fe89e479d9081a095ce07f7353e3e2b70a017ac21bdc1fd1c1847a335951a84b13497da677611a0da4b07866723cbe1367b67c112124d752e9b19adb15d96b6a1e33bb00cd22e800990508e1221caffcf376be1046ea0897ac43a35abb61d276628f0766e50f9507ee06461a8ed37cbb0505b4ebc55e3bd58500a613386f8e7d44b9ae3fd476240712d7f15aa2f0dcb5f2bc54ce3a5da0d04afd76d242230a154932013597ba5708d6c585083f6851d2cc6fce42026d1426cbeb3e90b433bb262169012f75796606901bd6021a5b3fd46cf625b1fe6fbffe659a11273f6d072e5e97366feb589a0b7e96957fe6f432c8f37090296245695becddccaa4f5bad1dacbec3e84f1dca01e4ae6bbf0c556e83d3afd7b6eecc396cb263e9497feb2ef30150ccdc962b400ff3c8fb36e5d7bfe4d2293d0b620195e0a490362aeb6eb82cbfb53cafc1bf4a0e8e0a84fffecb716eb2a3e47c42686d78cf5f04a260d384d911634035d6aecd0950245b68590c15c2ad7eb359a7dde48f776aac48a4b30f1f02be0b07b3eb3c0a786d7904305917d38ebc6e6dac50265df06a00d78ed0f5b403d4d7cff2a4d0073ea91ac33c6f162589d3a1d56fe4e38700d058d8636e7fa11292c12531b3dd01102f78b4bfec31ca50a522db392042bb54e848d71485bbe516e769cfeb94000a9f44fbdc9a504cc8cd839820e8007beca6e992c5e2f54e346e0a69027d4bc70b901efd8e71defcd410712db08f0dc5ff462414a3fa179679c9ab2271b9efc700124b6b8e73fe27f4e3b3d4c4653042afb1ad788c4f64a66e5f9356c23acd030a1014ab68f0c1ac45a40be5ad2eddeecd0adfaddd5e2f5e9c7d79eb1311f4e50aa0f37a19f570bdeb907bc665769dfdac904e55a2cf019a40d706c40e56021f05cdcf7f64ad9b80b9dae925738b49a839a18fd550019958710d3ca265cb55e105f3791ca10d1d09643f8d11ec9f8e55d790fddfef721229ab420c71705339730b32ddf91748ef2ee3010f9d93abb26efedd53c4b52e4eac0c73e6315ec025b10dd51680fb44a368404dd42d7c566097dab05f30d70fe1fcda28ae6976fd9c5a02c9774757354a0fb5d1db595e599800de89fa959e0ecaa53a77892ac9a7fef30c6dbff95217901e450f10c7e663d8bcff420f1a3d2823af5b6afafc7f6a39f6003d6ab16e00de811f57f280c71183ca3bc052c99851b35ab46be9ec70e304cc024d795b644e33f9968828cc7903b392686c634e3ea38aaf138241f14ec2c5360fc21f222e7d49f185225743575918c61c6c774fbad7bb8bef72076476cc467f0aba2dd5b6563e462375dd1862863312ed5882449c90185ed372ea62d039a4480aac629fc8fc8bbeb7142dc7d1394b41d906e028de05585c370a86de776422d90c00a1b734dc374201e165c595a42f9b99ead7eec28b690b33df50b1f535003d01b5c53e4d9e0bc98863b285f42393fdf1a790d547c0233c879511f01df7f0030a332b3fefe66316da6fdbd3869269af8a51374011a467bf5f8fb119343624d4081b26ab61f08ebc1a748a8359978e5fbf8761132d5e50e0771fba0fc66922ff08989f3b7dfb1d7ff6fdc4348de5d40e425a0fec960a4cad8ce1ad9d3eb8483b0ca7ec50c7c069e14b7a88b59c8212341f1cb56a735e99e9e1a73595f3e6217f028d98b7f9d4f090fe1423eb26b42f4086c2a78fd5ae96c81caae1a0a3e233b8077035821e7ae7d5f4e8623c72972f39b99ff444b3c3f6785320f38a12d633bf028c2df36b66d31a806186d73b5dc230d89b801ab880c281b4deb0b85d7eb2b80fcd8f9f8edc0cd37d4359b85ebb1afa29a81e795f5ec998cfd0e108293c5a6608bdd5ee30951a9fe3fd136485a6ace35b121686db47b3af5ff3b718e98f511b0de4234f0b05850533f9c3d02a85cdca8abaa237280d7ce8e2e23720433da75f0a9e1190d2245682292db554f72381cd4c6696ac12596b2a737f94329d3c02400869bfeaec2448178d6120a667b3723ef1785304e4ad2a1f2fb75fbb5c56156d0b70dd7fb83ce8be5b6c9bc8b9f9d2f398eecebd82853abfd9fae7f90ec0c1ca02528e5a6d3cfa126a75ae611bd7c2f416083d2a2756fd17f33dabba8d276e830108d8b6391eac05830a8ea82a278191a5682573d0f088b0cc504a139b5e0f3304c78cbc65d32ff8d6f5dadfb4f77603e1e3b1407c610859c5fcd661a786038f010a0ad66b3f4bd157927eab06cd8e7934f0b3a9ad571b27ed2eadedcc8e67d207 false +check_ring_signature c9c8547e71feb90cbb85c09e7df15c9778e513166777ffe6aac6410ef94a983a c11be6bc25f1d19ff74190d0968e54ce3d986b38a124c0c46e7151817fbd849c 4 9320d343773fa3ce82c886a0580b02941d589fb9d60a934048af9b3a87c31e48 6f874819b3c66cdb5a526541394de12df8d329e30ea6bd6de3ee7fbd4bbff6ac 2ccc2395e143868dc2922c1d3b4d42d0f719b8df5c3c57b24a831dec02d8d3cc b956f847c82f149fcf5f666fe625f54711b13c7ffe57c8579350178a28e71914 23f1e14b5d8fc2df920f5a39d463ac7bf567287f6ffb07f9058c7251733a2901ad33a9b57630b75155b81246605787b98ccd0237f902d8a866376967de879909ac63485f9066f2584b8d28146b9dd97e00b2ed9ac1573854ed75f6c1cef36a0ac06e6d929a30368f0b89d52e8a54a62baab3ced8d1cf2fca71868316caacd50b800eba2cccc97012a5f170af1bcdb0c4f4b83ac5a99d1ea61fa9bcb963f23d017b8373a8513adc40ec7ba20c311abe847d493c0bc66062d8310ea16a60774a0c26f40915ca747bb98db1e49a34730a837fb7cffc5094d2d38d43fc80f7918c0726b612e52a7eb0a5e21b69f62f1ebd2a22eb9cb623be65db3fafe1d59b616405 false +check_ring_signature 4e522235cd33301c8911c54da5d3629adac10a21f8554f1514f721ab1590e6d3 903e064202b5de66853e1b1cafbb4084cf94621808e940e08c3eca0cd30792ce 2 c2b8a1729b83a72fe07a4a916a163a1ab5a86ec5af9fb51b6bb89adc6bc23a9f badee9bdcfbab842d3ad628677e9f5a2726a9863f49fe26092bc8a0da91f17fa ba10bdb1e77b71e0da197d282bec694c0cf510b4880a2644a1fe1d36467684057266619a4b1b77d2a8a529ae256ff39bbf7e2a35638de1ae489160e67520f00c35a52bde67fdca7b1b36680aa032d1c1207f3872a5e645fca59d850ceab35406eb920521a9f0f07b4d650f129ecaa79ad40d07d297b32b6fd7874e5dc9403f0a true +check_ring_signature a6d95eca004e28d7008909948f9d2346442c3fa332114ce31c085dc266959fcc 74b240631e450232e7b15f9dba7f5eb1d3bf30624200d6c2b96806943ebbee83 2 0670f2f71d647f3cc108f1cbd195640f6ab86c004be961bb2e61d1b414c162b9 66200e98c5868222e1da04a4c63b65edb62b68d7ea925a5e3fa5991b00bbc205 45a7dcedae8776be85bf09eb87cff6dc7e214e97bd874e4b6a43cad1a51e280615cbb4b7706eeb0d6715c09c695ca21f1321b2c69594644547153330f8eeec0648ddd582160556bb2e0b16532dd0c63eed4ce3c1a48cbcaf661cdfd287eb6a05134d0e706db1c1d041657b4dbbd95a43b71293888d066837080722f32b8b0605 false +check_ring_signature ad3c1cb94cad612b57014e0a825959d6831235403ec039532f9f62bbb8668004 e224a5cf2878473e898d2bbb3c05fd2f60d80a45d12fc1e479388047f0fb5390 1 524edf30abb0b4092bcf1ffdf22ac224127d08fe21d43b012d963d87b4c0ff7c 552f59407f7ed49719279f84586b8f15b41a4b29d2fc4648e502deda1ccf5d094b9bb1e6b1e8b23101d498afdf989387ffd660ce12c1d4f1df79bb57363b902f false +check_ring_signature deb3d05f1e1dd21bd8322de175ff3071fe9d1e28c91ae8aa74b4016f2cf5d876 d69ad732dfd1e432fd6f32f3e08832827aea7e987344527b33e1cb237d493608 1 6bc0dca19c8b839d4fbf662d64805222889fa673c9a63f40936e27479349dc5b 2db023b469de55a6bb07674651dd1c8f5b49ac0c1967c4bdd20ad2984c51e305f3fb157a778a64990364f639c60a5f30385f4279e6f04f6357dfd223e4596e07 false +check_ring_signature ef18f320a3c4ef117a1921d1516e7895642e917b8c993623a6ab14c7c77d337b 06735b76c8fa4f7ea1254bf1dc2aaead825bb31653f5fa1b8ce257e6e5e20ddb 35 9df18226daea789dfe738ad26a5233b451f78ea01210091fb70c75e511d0bca0 c56f3ef30f07f3cad0a065e1348b471eb712d037befa46655cbca105472e40c8 2b026324fa2bdb23cc17c5a69bb066fb501928792a09307149cded92f0f0f33e 5b068f3d42f132fd823bcc233584198152f6897c221c8648c902d34c36d1b14b 19cf94f0c3b4609a94202edbd7337fcedff890c43f1172ede41a3fa5f8423cbe 3cfe2c0a1b04e94769150f8006150557060e0f03f133d6d03cf9c1d083c017d7 085ff78943658f01d3dff85070e685450035900b3cf6378f6765150dab18271a b979cc0a56c5c33dbe59b487efbf7392d1e641b48a074a0efc53a691d7571fa2 72bbb708a7121179becf5b6af53ebf393c2a1a98afadeb310789c1a48a31d144 86cb39aa6df96899116b633734e4acfb624217fd14cc66c6da514bc1709f2d72 4fa52561cf5a350962651eec93b76493de9ab47bc43e162dd3fded94ff7e191a e39d51b994de0561b2b69b5cf452bf5d07354e1a5b567fb063bc94d9893cffbf 1a86f95bd9c23fbb443b7edb279faac555b331d5da7a7a4ddcce94b9e23cafdc 25d824cd565be14dc9a8dae600d6e8ac061d772138007956d2fcd4b8c9564573 3a8596aae5ad9df1b3e7dcb7c18937c2424429811f397b81c3646d47238f09a0 ed285c4d23156e7ae8cb9e1293832673c2ceb8dbd89ab6688a9c572f2103906e c3eb8a5868bf15ab22d5b35fd2fb33566b76b9aef6017ad6655862154b062ad8 f9b58f6026cb67f37dc44b52dda883838f3c9a715beeaee90c7a01eea20788cc ee940b8c652a135ab7e5fa1c810e556b4e749c8467ec23086009ac40c94644db dcfb600ba0d8fbfe544dddd912dca5a146607077f45dfca6bcfcdf0bf1a6b7bf 08ff6e4a96bfdf395003fbcda4dc423a14fcdff92578025cacb64e70f8b76efb 6da1fca9b5dfbc63b7fe3168d1cfcffab29515d26f9ab83a9acb73db044a09b8 e4cb3e128ee458553d3f48752be175a129838e97e01290568ce64e44a99d7126 886741b4d9fe6e87dbd84d7f8bcc6f9c525296a33a87a61fac54355628a4876a a3479274287c5e997cb2a79b29a706414399e845dcd0638626a22808cfe85279 1af6ae84bd9fde369fd8a600358e018bb91f86eb4d1a7f7faf397d42fe54271d 4ebc3550c1c454bfe157a0f3f3a1dd290b48fb420930caa1add2909221ee6a6c e03fc3ea3ab12506413dc0034fb8cb58d52832fa02366098022763cece1b6c8e 00b2b3091a6164e247445af0f8325e5386c528a50b13567492d31e8027651df3 1f0118b01f817e5b0262a7904b8499f743252a8f00ce0b91facd648fd048e32c 1276eba45409acd3455abb9dd684a3e1c51990f2fff2741b1f0d6f3eff0f48bc 122c6015de39dfcb081988b83b16f6dbed28904f9b6d96a8d2dd8881bf971091 e6df91dc7238786f68d4d4f6dc5c7fe5059ece2b6a80ec76460ae5b4cbb1eb46 5d8e7b83c0c16255a5bb90c69ad41f89a8c7c0d7318eaa0d9e20b7fe00e730aa 95ac4509fe87ad31010ea577cc662e3f2dcb5360eea18cee985888818d54c72b d23908f765e9db46da25dc75b967cc17b305ffc21534ba7e6a8c3767ff46030cb0e632de26352c5ed4d0c4fad4387dc84fe10720471731c474bcb745c5efba02a7f33c1fc6d44496e7c7e0f570433a4645bc83b49d8faebf469b1b7824877601f65830e0202202ab5e96d1c014eeb261cc21293956310892c6d5ed41e5a75d02980645da3ad5ec478358e424e897db5b620bb40d0c8f3e7fda911883900250031f403ea72790f238233442f02cb943876a5981934113027f23ed61c6aea84409e2a62185b0a1da1bf150ab9c995aedbb3302cfc5e148e6ee140a7da1f7bc3607762e2f6ab1aea5526b9ccb697980af759e3cf2c30663fd4beaf39426fff1e40b858cb100552b71c0e5ef220cadcd046fef83d89e97eb6a45e36a0603a0f3610480f80355e63067c879ee057a10ca019c7912ddcccadbecaf999e1f22b8c3cd0b33001f6ad358e53e61b29237d9348d74cc26d158d14182afb19e224a7107a005284c4c2c5441470eba482524098b000cf2a9507b0e894dccbed05f85f1b16b0affe65ec78381b396bd72d8a552b03630c3172193fc39d4271a056c13d41304059b62190819eefe82a98c7dad52830a4f26e6db1d4de7ae3ddfac52c9ebc58a08542fadf05314e7e3810969c33876655d4249a0d042c4bfdb30cce00f27d9bc0c6fae701e9ff0e84a85ed535735915e5aec9f0d2523b36fc7cdb5806683dec6024a3839eabf509bd27dc87e1695caef243e271e65b1d8ba8a67070f6f75360a063fafc25ee592e462c8033a87e511e442654086ddbe0d5ccf130316f19ed2380e5bf391b8ed36d34e546e1408258f8ca84181496411b8198589526a8550a71d0826d8fbc82bfdfea810dbfd18c954352176c9fe7251d30cf01e3fee1ac39eb205617fcdd35c8258c5aee1346cc1ff4a985e62fcf99f878d5ac510c0209327d40f98c72f64973a08da8790d88d649c98e97c812a17417c48fe987bd04571f87f0cf6471f883ed109b9fd9cebf5b322b1a45f4cb5d45bdb5d335260748f81261a073f163192146de468d1752521039792d68930536100049c5097454b42a809c0032bb840a489cbeb10c7b8808655f08d593a47a660bd3302d40408690fab8b400dc3da333ebacfa98c0c6dfd203ee8b5e19305bd2a901410957e02f9d49df803001074b9daa8a1b070dfd1e1e0f54f9155e0f66bb2cb554d5e640f1e8c715be90b2c832a6821d281cac1e12ba37ff743e0c7a5484b437348472897cb76b562850fb8b4f2651edd205fc68a03cf8cb0afd47de71941fa96c29c601c1a694cc2310ccaf647114329e5bb9f9e505349e72e26e79b3010f83017c25b0a35a74f38ce0cb8298804f5f027296e840ca672904a3ae10460f8291c846b6c4824b9deb71a026d721ef475f724fb601a96c6637585155c583e1768c2098af60fe8117f973d0e568b2616d41180aaabee18624705192c48291b9799b53aee201f4079f41b2202b12483ef5768335879f5a2590f743aaacbefa5386139698fc88d0f3a9545290851591956315b085035df722c8f8abce941343ca18b8946d9d2274ba9589bac0b284ce12ca8cb367a0413a75020f7cf1106b3e94a830f4e17406c7c68b288d50b0808abf54669f4a472b021906bbe396fda018f1f1fdcdae82458df6a86f9c208d7cbef1bec04599f238163a0a60841fc3d2e8e49235c9233f3c02d314d543a04c6efa57badf061770bf69d1a89539e74b2df1e0ff585f13b55b4535d7e57da0a85eaf5cf1011412813576bee746114b1c04bc762e67ae455bc8dda5c63cf58017b05b5d81faff84161102797f9eff827e6036b7fc70c1e06f2605cbf978eed09f47a8b83eac9eee956316ef71fc66f5ea65a3ef4a90c54ae95e9cf556502b805205ffb5d1476826006a12140a5afba140c2389dc7a08053af57612c8477c3b068cad8166fe388a44fa95fc138be79c7258f398e4675d4e9925ae0be58761e90b06b63336f8a1e09640bac01cb0bcb3691dccb498ef0e7a633f2978a238709f0c0fa2adedc48567eced13d39257b4698e86f18e5d64babc59fe01d62ad5b02b0105271994cd6fae15b210cc01fb516ca8165ca44d7e26139290611ca92d1da30ccc626dd4972e479251c04db26ee51806f2ff47a0052b6714d5ab983b763ee50457ccbfeedbdb1c8d5a74b96efd4dd78555d43b3708aea243e063fc12734a470bab4c6d54f2708f205112b4557f6798ff0a5700b72caec19b59aa15fc7c070f0fc46290f92e94bb8d0cab4444fb87a38befde1588a86024e7f3e9a22b6c77cc08b8bc310ea24565eaed0c465f611742ed5682e24cac8f9db1d8e57e907b5d210325738b3b64076b9783ed22c7a266234dece93b0bf95ad47aa31235f8522653025c762a49a69df21b6cfc0eff44069d957abfc371a29c9b6052586561ff4c07051d5355db9cf4c9646e601d82d50bf2c4642774049ec1d944ef0e8dd9fb9e5d08f02e00f591b4c63def7e3f00befc1ac5fceab5427f892e435f6c089231d5f10babe46b19a4ce9904136195057095236f64ddd8692fc76f0fd6d883b810aea5052a4677a2e8a39f3d76d7bc1390bc542372df589d179dcc6eb09e09812eb09007105174129d5e2acc00cfd15cec58b3f77c5fcb3ec55d7fc86204f2301ef8ef06563a633d9f0bf570b7985ca964734c925e32fb47fe1e44856f75a7509f44b909c5f89c8217288c6ea0134a9791deabd69cbb9a72aff18b99fa61e9ac808e780aa9a35c612a747ecd7512da3a215b2fb11e4026e8830b4add18e1793695ec2f02e183ea36cac0bb66ad04336e761ea30b062652d65c979cc69cad3517970b1203b8f0f596943f1e6f01eb7d93c6a7c64580558f12d4609692eddb3dad620c7b0416c096aed27ae66cd94bbf98f71d5e561cc3c26097211d560ad998959f633106d09eefb9263406e05ffcf5a5184ff60be00afd3f9ae85184ba0821000e0d0a05acaada677e0563fe2cfbda0b745bee3ba66658d7e2fb699719844557ab0db4030467536602b160de980c72918e0a9c508cd571fe9eaa72b64178894fb594f6037c539acb8efa1da7f364bdeaef9cc2fedeafddb4d9579d649b7db70ceec39200d20e626b34a9e51946288c5c41ea7fef1b3aa1489894f492a97df9e4c1007009 false +check_ring_signature 5ebfe397d3324bee40482827a133bebef8e096892ca54e1ba60997f533738bc1 02b1c87ef3f6e224e67186014acbca175019ab57f20350bc7d4799b50f5453fb 6 7ea5a9a3b085dd651943788afa46201532574a8d2e0e460b3551ac5f63ccaba0 b6b5ba65f0b57d591aa350e1ce6cfea2b0b3834aa075f476de4b326aa9cd6c48 dcbb0b3968bb26c4861e9e919f5a37b5c7ff466c3991abb8faab7940e3d7b263 0ba27e892e93535e6de6e99ffb3f1720de316db4f61df42aa90882bdde5a13e0 67f5ede5fb846eea992e7cfe04eaf06ccd0522e8a2fc4192d471045ed83516b0 38ef36431ff071cccbf4a2a369c26e244931e9a3d87e9f9d7fde68bedda12713 932a767f010a18dc9f13b35e08b477198e149d4bfa1bd3c64bfec93960e2601819549970803df787a4e15236e20b26388d8268df6a0579a12970b51ee8188702b7afcd650c7a649f7d340d76f3b4840e1abab499653a7d8f66579e84e6cc4d0ca05eb9ea29c9f73acfa319469232ede135629b41ba043bbf73b237c607ac7b0d37c44439d96c48ab4b17b64bc1aac5d756be8b09ff3bfff21c1af109c484030bbb2aba5b1a722832efedf0f0087654db339818485be0da0240db5ef55a6b190a6ac18ef18ad6a53b09bfd61c945f447a8058fd91bbcb3ca12692d0f5e633bb0fc56692f173e5cf158f6f2282bd81eee764132c98f8f450a49a6f053d228c3702a40d53ecb7b88a17aa595d9e40ec315bf2f7db9f7d4553bf0f9bc8531d4c61048748a5cdb119288f289b308af68686c6a1471876b9e8ebae39e02db939513a061a1e7360074fbfb0ffb046b1e1137e851fb9ba6293931532b6a7764ffcaade01f0bda9e1d88a24b8aa21bae0d6d2212242a57f10e7536ad17b32a972d6581e0d false +check_ring_signature 594d53bd94ab96715a4b826be8cee1409e2595be952e0355d172574a1f0a9906 5aaebb17d931bb8c7c4c1bcbd158ea0c6d0b2be5d4de771fbc7b151b5b8cbc0a 44 13d829e04f7098ed646d479d7467f67cdadcb89b7d85e82560973f687093a193 4c083974fa12e988049487baeae0d5eff74cc0f79722e636256df1bbfe7f8b85 1a320ba7e88ed6e327c198a439d68ab5b8dc3d493d92b64a625e94dbc4910efc 1a0c2bc10d35aba1b790f84d570086b3cefdaee8feae3ce1f18007f880a82b2e 2416ced01f1cf1c4ac4d26e2d782e8f3002fe442c1635f0deded2bb0d42e012d 558f24cb4e58fa3408299aba358e383f53799e047e25dd36bff8d3e3254c7a10 0dcde9e7448b83dedbab851b49ddee10cd8e1706f9a2a903119b8bd8d4464f18 31c78d9b2c3214b8a36206fd55d93cb95b0167079c13839db11fdeb4eb4b09c2 8ac8703c19a273f5c3204cacd47b8ab4cffc29733090cccdd5ad48da256e5817 1f362af3af00fbbe39b42302782dddff6b5ff69bb858b6939ef97c579ca3b135 77588e4580fa77473a99c6bddc6c04a12763985db5b1a19c8447eb1552b35c8f 32aa22d13ed89e082d1d16ab0e766c2a7a7a63ea3b91470c7b82785e82bb5939 40fe53d3a3faf003e97744ca3a71cbb30b42eb9b2e29f3a9ebf300ead692a139 36f072f0b964feb9d067d0a534f0320decbb42cbb41b9ac0f838c9c3c57a94ac 2a5dcfd6eae6c08c01e5d847e42f285be9de6a0adfbef5b04d8586acf38f7745 c08be64798ab8135998186fdbbe3d66eceb49a40c5fd58962e0ded3f29c744c0 80072bf1f495ba35aa2781ab586148233123b237f99b01ad608d6a3b0ec6e635 147e9d204e95e3fadd32560954ca50bc2b77073ccc4280d9077242c0f61eb423 f001175686b70f6d47cb3f73d9854beb5c01f48cf1805c482bfc71b7420bc46b d7e686912b8d0a8e3e8431508061da6b7cf9aadfed8adca912d6deb3e039a993 27ecd64d240fb01a352fcab3513d100f7cb48cf198264af60469051d89dcaf57 ba1535d0b3750440a32bcfb91e87d2706a2dbaa0b93279d0da540cc1d82eb6d3 27510f03415c67384382644b4974035060d7c3b8f239302d2b75a1639e73606f 203836aedeb7e98df88efaf235fa96afe0ff08a122255573c4809d3d6ab4a19f ef223a60428b83c699232ebe87045a3405b95fd6b6038efae996234eed4864cd f8525938265daa4b4e7f0e35b777d86e7bbf9c56da5e90d88927348f217cf262 9a5e5a8659bd62834848b485ea9e8b3a5dcab97d5f7904fd71bc3c98e103dccc 749938ca7a644c5e03f06b29384b71a28516e0b19fc9e4371ead9734c883649a 6673ad1c033cd8f9e43b1ec68e124bba1a35644a932c8848a6868159aa057b82 71969a01a84996b667f21c04c69126d81aff6a1b8e9bf294a029213afcff317c e01768be2b6302d6630dffa52abfadf2ea12c76b247b65a9b3c74711f41c47d7 193d47fdd65c894f41a36c045b3ef9d3984047292c024385759ca899cdf7102b d61bd1ee399cbf79e57a3a95434f0cfc6bee5a26c47f1f71fc469ae2f03a2f8f 31fdb7146ef9b4a6afdeb94743156cb0740cc55d82c65d16eea5ed79850621d2 587532ca426f67b4b59658dcb03d09d03d7600e4b8dfbf89ee5f3f0c65f4e2f6 d69b8e0288e8cfaedce99ad2e4911bf794e8713f78b90c25a481484e19441700 6af3a260f9c316a013a28714b85c877343014ae86e476adeb240f1b4d912e902 7553cea6b967f8c7c649a551e59f66c8967f5b3525a69f5f798e50b786cbae2b fa871d0d0696f4142ebb45cb6278578ccd13c8e340be3298cb30bba08af4694d 6db1761c841f9c248be266ee2f2b3f02ffb8683ff64a878e9addf7cf95a63c1f 660288d69c0d0533948bdbdd1b217ae04aeab1480a7286e45798d92429de709c 3c424fff8887d6b7912910a67549d2082aaaea6bd2fa4b5d26d9da32d32a1672 0eac5272ce53cc3b77d0c140f1a9897e998aea13858442bb8e9743ebffb72856 dbaa3acb16adadddcef326ffd13bfa79a0c3d1023de20bd3fc2b244d20ea2c0f c9f70c525c4d4120a816c1bd103afec640aa2dfb0b1b435b3a137f89e2ea440072b5e0128da5d789c21cfa773f7bf4613451f3f9a95a779b1d30b6609efdc106ed7f73321b5161f568f89f7ef9a4ae99c87853e3dceaba240ecfea73904c54027c1c6bf8c6fbd91b0c9815fe5cd7b0a723f404a576ad354526206ecbef3ff20a9956796f0952b7641e2e510478979bb8939d7ea65797858eeaa2dd592fcc6904e9dc4bf898f721d57aa14b900db562d565dc721aa44cdbbf88547e8c9363270f38dab6ddc30453ec78f5ec1b86f351d49368d565de08b64da3cd4b53ae2f5c0eb39007a83973454ba5446ffe58341101c6ca1d4024e97ad40b6c2d894746b601b1a0789bf3496f7781b0c53aca2128e60cb670d5e69513c5fb9369181db5230b22dfb84f977c101e913e2d861dda7f54d23984b4f737ca24fe8d21a4180c050f168f3f428c934b2c82a8ff11708d7b0bcab4d30a7f2f51c0a4637d043eddfd0946b6f0fbb7cffc6158640e703d81bc8b0ddc951043d378133167b27bfdbdb20b6065a97e1df73fac20642cd9d8548d828526d069e34c8fdc95196c56ea545703f612d84ee1a525487ae32cc89891c8e8f10786ea24a51d6043277c05d11dea069bfb16d6e14f7af912a6c664a4fa4c16847d3a951d18396b8896f72956b72803388e9069c02987062e520880396e59fd043d00c432c7ce124240b5d6eece1b01038e0f186c991f2b9fcfde5d9fa076623620670d94c691d31a26b11bd626e30b79f4acb5be0d960946f311e97e12e99544a90f32c57eeeff51015fa6d7897c05bda98b83a2ede62cef0bfcdc1c9b09a4c965053b0fc186eeac55c9737aefc20442ab12fe39d6874c6f0c4dd9451ad25ec55314bd825ffa74f1c81bfc96f2da0917f7ec2c34cebf10b7cfe668d06cdfd88de1a2ce10e6ca899f9c3f087be39d09c13b4b557c3985bfca4d6b2203007813f0e3fced365d6b3dcd7974dddaa5a654a3905dfe4d69205592386cb3172be30d71b5b1ffd3c42216c03f190e2e5c3e07ff8ad232f3f783c76bc48d8dc3aefc6f8d8fccbb3f78e2d20dadd4c51685e50fedc698ba6e1faac4dbeda1a5b1d30ddd5573ea8e3d9d7992c7c78996b555f90fd4ff216937a9304bc89385d6481db26d563dd89e69d0e25959a8967f33fc6806050d685d008bdd1a26e8b7b3591ebb1aff4ab8f976b1ab7abbf01486dc9c470fac22dd15d7e5933f7495c0603feeec5f543d5087f62c8458e5f9661fb1b21b5625b71f15f3ad2e36cb17e1cb6dd4d32eaa716c919adf5d797d299a9dc71366020610bc99615480642e11c1b536f5a368e987012dad793d9cf0ebe04c20e405005c8fe8261eddc867de7b392b156e8158cc492d729ce7759be22c245437b25e0a94f0b01dab2811f3b12b6401b2fa849bbccd653d40452fc19412eec787cb090cd692bb303a2d0cb1b70f89f747cb16c8efe39653c6629ea28aebfaa9d31da80cc4bde3747709de2dcac8bea8c69a2645cdcf4c86dd2c324bf23b656218f68906bc202449483060c92d7994612383b364c5b906925c1089d7e2fb587c42478b05bb1ef48c1a842f65ecec8a33bbce0299ed046289f1d6dd05335baed045c7450052be0b474c2d831d98c8864dba2951cbf291a2c92756df93c06aa01e1ef26a0ebe962a5a325a41d432510d57546538d078934d18d5e1b0851d7b5c3b168f6e04ce481a0e9b91d4f1427a378f99a632dff0b5d6e60cd212de955baf882560fa049b3e5d7421329fb84d1df2d7a397014d39a3e31a01e0866398580f86fe8a4c010bfa89c8b850c6b7fec548e7c909ad8a445f55116b8ee6314078453a786c5f05486e18dfe97d83cf509c1ccfe0ac7ea78c4f2c0e8f91387b1a48c75ad3e5b90152748688bead6fdcc127e1efa03fe50000db67bb8de524cdaa723ca9fbb10701ee2f03f56516b9ec2b7a6ab02479f4967a50e96ad6377422e0d5d646eb746703f7862a57d204f5eca84601deb1dd7ab0d00c76491cf2d9ab1f6f8e93d1bab4042773b14621cd970f23f9c85861ad1e46101c8a15650f910bed655fd1e12ed909ef717106f479ec6870b4c360d910983c08b320d7a81dd2eefa507d00c71ece0db3536ea5c3bc1e8cd44df743e2ab214d1cde499b29ea79f6c4869bf8e033010888df34517b81466932b0e31f788bdc176d3344028dbeef432443810ac10ea10fb4044f9fd8f5cbee22727f46a5f25aabcbe43db3bb511829a18ab93ce1861a07c30065a5196babfa4f01724b320645ad9de30d0a68d2e286665c24191e32060e38eebd27ced02c002da55dcd9c7989a3ead4bcb0cedab07f471e3c6acfb6f40ec288efe206cda685b9f582c7c795c2bd9d61db5e6440191139eeabf83d0ea3070e4615fb412047e11d42218b4aa83a8aff880809ad979532f7f83be0dd6ed0007a17375a49acd35c0fc02f28df8d4fa6d2bddda3c64c3e0c58123e47a6d2e90e40fb9f52e1a24483e4608e23d76414bf4b1f2cebeba5cc95a94c1ce7d48a2e075e3796a99753af50ac052fae2e0a8f5706129cdfa181eec79886261f1f47430650a8874ab6921dc7776a868f69b096ba6cb10bcd83e119fe3d44b52a8582190a7c227e7695b6ce92d670bbd1d73ddd8a6e7586918b0bf7b9bd6374a4c41740086877b7a7435df44d551da2a131a201cf441462717377f677b2993ffa176cab04a5450e1fc51d608b454499c894e51a9a2749c4302d13973528077ea589d5920e24450f74a64d1409cdce1731b2bdf8e9aaa85261ead62f36862f4a499448d004f850c5bd49f202138b8816da1397ffa81e9d708cb2edc2c4f148949c3f5fab0f08c03971099373e7b7e27583582b8b9e9791dbaf5bab295b91f24702713a8301ff0be79e5812cfae24b2fa51b8b84216c46106026da7e2d57db1786a106c88173c4c17309324a25713dd9242a1e03e8cf46d369384dd2571bb7122779775a20e255058168c71bb9cec57b0c10784fdf3a7b432881a3bf6d721fb38c45c98b5083992ad6e4f58199e18ca2f076cf115ba7f516740d28fe7c85448d53c6357580dc0911675fdd64f520361f0b34be376fbbf0cdbfd645b286bf9b502bd7f38910c8de7d414c0d5f1c4aa9a6d2bf7f1df886ca8b956f0c477512391932479ca440e55e203bb5493867b3d96e99b9ab3385dae08ae1b423987652b170c91da5d16021cd845c13530e2d1f3021a02cc4b32b34c951e81f77babe4eddbf88394499c03c1b61f6e98bc5dcf7e4456953832098010108298021c4478c12d1be20a56c50242257ccc5a438ca20407b572d270c0186d42a3d13406fbda47dfa233f8282100b6acae7c6889f0139300a4be5cb27891db226b7667a395b37db52409845c5206088e052fc253996fe12bed276b1b42510653305349cc921fe5399973cbbc6607d8e307ea032807867ac3abdc9c1fc5bd5e10c26a15010d614033fb0ff488ef03275c17094f9c46548d284fda918f67f5b660537ef8f730c634e15080d14b1c03aaa52107c3a53efe2b26a512d110c870bc1c20871fd1e58f5259b37a5d671302e0895cd0afa8bc2dc74cd6cac6716aada9d56836642fbfc4138108548f28df0061a8567c065fc84a95e219b685a3bfaf93a1e5d8f7a919dd1de8adac1137ab035965e457025473da873c8741621f2f7eb9a343d78b341ab116000d3f87dfc2022c4b5d6fa720a5c7fd28777e168a6906ecb277d8ea429d9020cf9b5af87c5a0fad3774a42a9aecdab3c654e77889c92e85bd1b59aedc4e7bbcd236e9d5fd810209f734a8390354c686fdec7cf0425c2132461a2daa98782769fd7f3b098552026c8f9c9c64de30c7dd0a0f4eace97b0f9def4c55631dfd3364a4f1f6976c4b0213303a3e7fbf366e502a8bb6c0d25f448201795cd74a57e4288206736afd1c09122afeac91217353d93a24f927cef8073c74c28998aff565f3e6006250bf9a0c false +check_ring_signature fd7fdd809fc07018aaacf11d29fea2f2cd10b537b62ed0944f6b42672122f882 2ec3c59c8b3231853fe369de0b85fff76b2f37f64f83b44089f2aaf147151062 4 9ecbc7263ab0daabec3a156f86e2e4ab17043e390b97895e4fd3a3a5ff0baa9a 7a2692cf2179aedbc46d8b9a15cab0ca0417caa6602bf1867a65fadd93091654 512c2c01749b4b87ac61bfc3aec42714ded0902bf667b97d077085900e5fe220 ffe0af95c1a8d210c0555a14d4b61bca87dedfd7944c46e030b0e52cf79224fc 54df105a935e13614de7b2ca3f2f825409c1952df0aadc11e7a1ed3554af6a062a66f5522613276020dda153701fd12b91af4632e50588cc929a45cb5153d8058bb6fddefba70cc8f40bdaf3ab7d47c3f4e0583b21a9bec1f4800368ce9e2d7b2fffb60ee744ba822d0fe4a2283506066f487cf3705d6ce5db56fcbbadff510921b5e98fa370d9cfcf3bb3a3c4ff71050597abaf3257be201f2168753d6dbf0273498261b4d53cd7e17f6507d97496e778f6dbac8fe66e16e9709fd6c2e47909c6405c91678ccf831851a3bbcf246e5095a519148f3d8c2087184d2a92e4330f20ee458740cd8c882ac1090194c5d08cb84b8c36ada8a2c0b2a4f0dcb1c2c706 false +check_ring_signature b1d578c105f22a09ae20651a713598ad1ccfeb7e6e6188c826ffeae087fda7f8 5072c7b65880fa7c09242c1448fec19c1a84b0b862dc46599c5894aa008f420e 20 7a8efac4bc19d49a3ae50ff7a0fc8024a633d7a3ae5269a7480d97b69d0ae0a8 b7f31a070a34282bf20a0c869fed02e8224dea70398163a9f3f0977d71044292 65d4935dca61c02d532633994aa700487d4eb06a755771d41b4e7103c73dc277 c8d3dc3a42d6b94b4403a38af8c0d4993fe2a74618854fb45dd6b17d047b871e 093427611d5f453b43291e42399f74777d21e3b44569ce50fb10b1094d630deb 462de703544829481f786add9860f7152fc45a3dabf8f8a699297f12ef200558 2f7b9c784e494601b08e8eaf1352ceb65484060470f7724b0781d465a5c63c2f 8022d23024d865d7520a74d0a2d5983ce7db96fd36f811937549dd5d298bb228 58a0a25d3fc21b31b0ec44d6ba5451a312338fd0b9f2457682066f25682555fd 042ad0e1feaf4cd9800381ab9d961ced533da53b45486a50384f86d6f8744747 5020ef907b3f097925ea7c4f82d079b5e3245d49880203d33542f45d954f7d80 583b3d3e2b6adc6d6a6baf192c59cc78912a1501ec40142c9217028b3659bc0b 513c6a7b657294e21e38ab1d7029231f5c4b9756341e39bd242156d5fe4353ee 72ade53204afda61a3ed1c4573103b52e3ee3a8515411e9ad84ddd988502b1d2 3e90a9c7f4ae206bdf910bda21ca74da01ae667795e889c7bc095bf74bae231f b148bcec0ba0bc5d1f989663bc3645d10b0758d18a887b36fe364a7dcdfdf65b b618a9e9bb5a407795c9c134d819d2596a26991b9564e78e15eae0eb9b6f754d 4e22072d30084a95c827b0c21def08d0b79f06c81c23fd2f8dd40e75819c782d 3d33e977fd90f6072bc5f1ef871a463c213feffd15a41b35c46d53f7cd450412 863a952f2508f1e3cb4ab35c749c1cceca1685816305fe2fcf8673aab983abdf ba12c73d295be3ebd0bb5764bacae9fc0fffe7160ee049c203d92faba3f6b70894ccc74a02779b440bef3fd0280be88e38ab6c3c204a302858158d1ceffff6064c1e2065ec37ce9457b256683cb8d9dee1c7def6f37cffe682c60006878ea101c8d85aa78db2b4071a156c073446f35e11c7048ec6e49715704580b1c54fd807cc7040171dce29e9e519d12a230407d8c8a2a9209fac97402c1abffac9bf92075ff043c466413a59a1f6e3f35f1b520711f19943519d289e1769bc03a5814e0cf626b170abf1e9f36b3dee700d9b6fac6557dd4ee059f039a00a505747dfca0046b4e85f5ffb5a0ad3fffcab36fb2d04673c58d79eb0ed673db6e12ff74cb40d47cb7e787f1afc386e70756234df59e9c63edbf7f749cbe54bb3459be52fc205f2ddcf4f9acf87c10380c60e070582933109855de29bf99d2f9382fb9df1b20a698d96d5fd96f86e9494602f04f733f88530a89691c6b2c2830181969d21870ac08e4cb15b01c2c9bbf89a516a26475d55fcb06668667b6d67d2ca8154008e006bc27a740a1bf31064990584d0e0ef9689da47807b6fb74f8a11a37935be0d0dba2b550d1c35930fd9a2a1bc3c0ba9730fb14aa75286276a504673804ddb4a006b3ac9faee23c7cacd7cbbe1623ee17cc2244eb4ddb80a8c7558c09444b58006fa850708fbb3410f64e6da411aad6ca157bb6534ed065789d15c9435cb352b0c3219e18779a4055c9fb30a950f799db6339eb8d1d446f3c8e08c5b0a12b8670a66f99f6bd09bf2ff40a8e58dc0504b542cb4be952f040355410d69ee74b3df0c180fafbc6d862a3bf8f436ddf4a4b5b54d0c5b181a7820e39e0ae0364c94fc0c82a189e8b784d37b8449725ec05277fa4e3279df12a81d78e2ab00a905a18300946fe09ba6517c836fd4d4b04361a8a6f770454251d3135351d44d0e7559780515a86c480dd7570b5ebf25069b53a1c1b5dbf7087884d0244756cbdc343cd8013ac22c728ee7e17fc6a7e68bccc92a955a6142afec16cebabccd50088001b6047ecba136301e14e9e37a6cbb5468b29c02fee62eacdb385435ac73ee29c9530f404dba954c1ac4120d86fa1d4a9d748355f4df23983e744c25f073e2aaadab0cb5b01b5699d7f1a6af2d06fccb808f7706e7009f0314037016e016db48e29c083f3abe729f8bfab5a90fe230a911364579f30903f60b176623358d7fe50aec0a6846ae002ba0743a1406179bea198000de330613d7853623db7098652a8b0501348a023c6be225740267aa4bba3c34d698ec91e08743a26b4e0e804df0e1a90add809fefaf549bc7207274ad71172f146986ad9686b7bbe802def1318f8db6006e3b3a6925f919bd919febe55dd1cfbb831dae2435f5c175f096df20f67def0d716e9c61d4358c4651fe90b73597d910873bb7cd615f0ccd1c81806b0b11ae085c14d6cb0330ba6345f6c1969598bcfdee752d53c208cb2072aa80f1e72be4043801cb2b3bfd8814c6f7f14904afbcfd9db1b49557aee023d606b5bd710f3200817c82b903f8e6fae7cbb9d79e6d0c6577a0c40c0cd37241cbf612b0ba67f400b0218b0653dfc0fc8bf4fb0b7e5f36d88fbe5cd7b4675a493f9280220edde80380bb1e146c42003aabd3efa8f500f5b2fe2a32e191c6d0f7f177a139cd1ec900ea4eed8adb7b459077364f080d8559c5640682e5c2c73a52ee01341a79a00a090c3b691a22bbc9b0dd0f0c104d1804281b0b44db1a0563efc3cd20a219d2fc090345f5fac972507b122abae5e4e4b81122b6cc9783f4627f0c2279c85ba7f309 true +check_ring_signature 304e2426b0c22083d0c78b37ea8c05696b0938ed905721449bcdc1e939a17fae 4c66b83a571925ef54576b923da807c5f1b562d02453991aad26ea01d3cbc20c 1 9be8d1c942743d82a548555f35c66b53c5f6c4599436aa20747e77f71257797d 7f7f9735ea67209949f83898ff8763c3b09e2dce0dc5da4cffee60578ea1290a01c4c593489616d51b03a479d9eb84845b9c25005783d912513dfe4ead354f09 false +check_ring_signature 77dfe198048616addc1285b0067ec4d8de8dcb4d0ae0db0d80be530b8bba38a3 9b4f71b77350624dbd5d8b630e6ce96fc3176f6ace8e0e1667cc797a20d3dcab 226 9d93e1e725fc233451a6ec513446aca7d2a07d307883ea62049319bd2a03ff92 a6abc97847e1436aa58b2bef6f245fb48d40cd0e3570062067c24638f4a37e97 0d4326868246b893dd0571e06b92422ba1989dd6ded6bf87f0e8d60f59e3635c 34bfad1f1789bb080d26a098e060090889352b0ef5f73a88a232f3333ef53fb6 44fe442fb20d0d3f9e9770ff1ecafed06b2e0a2c45ae54275c23d04ca8e21379 fb792b2aaa6827afa9785812c132957197541092b02eb221c52427b4ca5cde06 779deff64e51d98a41811532c1056a0b464cc57daaf0e5ceac4c7aa646218f68 6b942aec634af8ca98c4cbb8b13f45c3515f2dcb5566f2e95c5f3182db70fe84 153592bc364e600942bfd2522e4ba37cd5cc19f0209605f23b66a11c5a69c040 c2e3a44463cb6eef595e1e071ecd0be7e852d426c0afa0b5e1721f6138b267a9 ef047983782906e176d741201782ce8beebd124830c525791e2d737cf009e316 84bb64d3ac37df2fa76623d760b4339ad64ef122c45d4f0aede765fbd870f753 f05e532f43e25689234c31cf9777b527cab8d18a3add21e4f29eac25264490b8 9c3af9922ce0e53d5ca948b3fc4823bac2fdd50b43f96bac405994372e7691b3 fcce65dfde2399810cc547df7b0f574c04832a5b760deb0c39e8af8b0365e125 74ad85f8143b32cef5cef739fb79ae0c1836ef58ae9e9dbe5c0c5762c2f7c9d1 b077a00210823de15b23e182a8e287d270dc25b0344432b1a9749cceeef44b0a 163f956c49a77393c8658774df003b512f228b10c9875a888d494f48d2c92d8d 5b8e7dbdb28733c993df94bb2cab0f7bc2886102d89aa948983b8d960933eab8 639ddf313421a4a9e47a335c33b40d51e87083479eb3e2dbc20353eace516e25 07e4f3cb825f01388ae98ffbd941db72bb6cf1f21d9b90a75ed84398cdb2167c ac3ef26e0a2b1e71dba909bbd6cb902c725b9f7d41598f8e71aa8b1b7932837b 7c528d3fe962e32f6dbbfb5159ee1156df8e2fe334d8170a51885f3fbca7d933 479e85fad3782af7f51d421ec1dd9ec7beb1f31dc1733b72e7a310cce8337a63 724eefdccb158d16b7945f62c847731b2c562de66be07ddbb2e9ee7c1625f21a 9541d22a18cfd3021c5eb0ea964515ee6b971dca72b123431a021cbf315e0e63 84c7507449d7e2b453005218904bf4ffdaecfa29d710a6b12d811352dab396aa dd843beec972b0346371e003f62c81a7288b0efcd9f5c38ffc8e94283d35c6c6 ce9e3783ef9e7016813c157f931bac0a249516ea3382e38a654c79971f17acae 3992f420fa2c489e3ed7edde58dc642ad1a8b4222306625f6772b8d1dc9707aa 71708ac43ade81785060585bef9cfb9b8739e356f41777b8ce0c148fcfafc043 340939fdb8083e7a0fbd0f26ebba1485c7d668e4b22a07edd30e9feac73cb320 5d03b93275247b6809cd2198edce82e445d046d64f84e7528646f64acd7d8810 4f8d00a0bf95ed022a10a15a69fffc84414f0d2fb9ee1b35b20be1d190b5f4ea 71b00adb1c43effff9e41f296a7dcdec3ec9dc0047365e3f587deaafab67219b 34a0aac4fb1d6ac43a5c8ac1207a552b07be76f0a20407d6bfe34820e29dc387 6cf89bd29ed2728c8e7d0f63f48f72650ce5ec42481199265d7fea470c739d54 fc5195dca167e3ea44f7c2d90ced0b25e12f1476b9315cf166b79c5089913e3a aef4d8bd1b0fb9d9d70393b03be3af21e02bcb0546d7ae3ce05dc64dc543452b c76ce142fd52bc53404c7a34d7390ff3346dc6c805fcf3f3f176592a7e26724e 97fd3f64fdbb452958a6ac451e675de3878255c8be2a63b78ec9fb83268075ee fa39672b4e3dc021092447616b966258314532e1892807b1d8aa3e0c0079a4b8 e3f8cdb2681047f37a4a5e1e738b1d35ade7984443ee9d71ab958f0cd641a33c 796d7170ff99f3d4c2f7ad7d69d49b32b4c6c05512d8b5b6a6d4951866e87991 a7cf0388df98d94314e938a34556d4ecce9e3ee891b4bc89ae4d3f9ef47d26c6 f96f72270f3174d0087704d3a00a33a23698f7d7541a343037f1a0c210924ad5 c5059e5846c520e7db09919e68f5e325e579841bbd8a900b9bba7345e9627fe4 fd1e668c316cdbac2605ff1bea77f63770a7b2e8c4fe4a8554e8c72b879bf2b0 c5d9286691af243d214dc8d7cccfdbf0c771ae84e14560cae31fad2334699156 83a28b52c7c306740bb7766dbb97105a52723ef7d45016ebd4ffefe9b232138f 7e8e6915088ff9f01c97c3c32e4203d0a249914e6f74be5e6146468b72debb34 6896fa82d2ce8e97adf6adc489d1323c4bdcf4f7de6cb3c96b2c6bb4c0e2da77 88c1a75f0bfabcbb7057ce12e0a52cd8d4863feaaad230b824d955b0060cdf70 1701cba1950c169e19ad3c93b3f2f5c4d852e0f505196669be3cb23de96bdafe 210d00a7d60a5a49cdaf55bb38a24a05ff3881a2e5bafbb9d4bd15c253db05d9 70da851151697637b21cd30b6527f99669a9265cff10f4dbf271ac62362aa70a 981834153da6f247b2b47d15c706568819b790584fecb7b5b644d1d4ef2a0cb2 eae47288fe60beb468dd6fb074924900041aa718022df206a778655745cbd404 886bafe3a13099e29519623476b7e4d9a51d9e0312061942bb16daa738c04a90 7d5e063f2251cea6d0a9ee9799335a486fb410c3806d9ba10911279731d3ebf9 f9c5819ee03d1227f643735ef7e8535190ad449f70d255e99c8455b525e89618 bb9236d68efdf1ce7fb20c456a437d098f527c7fbfdc7f9c3a677ba86e2053d2 c27b41fa739e6e5315eddd16ccc34ff4feca06d021feaa983071fd88cf4e3b7f 9dc9022957e25b9f295957160beeaa7ddef04653244fc12c1775dffc4135c7a4 40577cb02e4f558cec7ffc1e0f927f24f9fea84349da1c7222ff2485424aee2c eb13da26f023a6656544b1297ae6c949b2089fea386a5c10ad5e26bf06b6db14 e63b7d45da5d00308864d8cd6a4ec57bda67b18be37de627b29572bf3c92bd0f 0c5b968def0afebda14d6ca85e069acd5ebc7c54fce59d0cb085986255a14f68 edd7d3824ccde0ca99a330dc6e437684d64023349aa871ea24c6d90813a9387c c6f89d6e28d7b6a9cb7cb55ff61664b20534a126b037707a7480965127af66eb 996fc77f5b880946e4c691ef8d3c9970cd9b6a89124d2fca1a966cd1556fb6a0 3947855030319bf5a8c406f2ee276f0d5a7f283eeabf401244219660442a152c d4828a4e7c30813c786757a03380f13fcd1ed920815bd477c74ac96fec7b6e0f 914409d99c8f1cb553d842af36577a6314149ba2ff48d0910c08ba3bc5f6e09d 017332faabacbb04543aa44e231a5aad110e51b3f52296e756b153b8c845562f 96618c75e6f2e2ef1d279f8f2310c8c6df86e51b217efaed3572730521be5728 cf6d2c9af545682680f98028974f89ab8e7c9c14f3d9e26077b5977a2bfa9ac0 3207a0f7335263d96ebe7641b40c998a346ca7b4d41e45cdfd42a9b820288d1e f394dfa7a0129c0ee27ac2d5ab585377fc3ff943c9f8cfd81e2f7a558d9693fb 1504937c368e173067ca47918eb219b1fbdd5e55889f85c710468d9ed769870a 456cac47143b6bb9f7b8be9c28b7c8d3d3b11eab36518ec8a2d031a06d84e04b 53cb60ad898dd2e5acb231cfed1dc5bef854e2a5162c77bd6078038ee04977b0 fb5487ecf8d2afc73d57db8a8880597b167d079b882f219677d5ae794d67c2fe 52b9a11852b74e51b49cc257e6a907e7f4f9654caff91a88c416b76bc328241b 8f43c3c04ae5c8c77f10ff5d8f09f8932647fcb2a52451dad92c7f157e9ab7f8 d4b0cc604dbe3fe530f726c34d0e7d4df63b51bd41cdc2f3869d284c30ef5f68 3f4cb99344eb27b571767d897998a20b4c670dee878241a12542d1afa98d1a7c 4c2039609ba93586ec50208cff966ada311279cff064f96693801389520f1d92 9eb8aba616e14e0e50dff1e884d51a0847285d23e95dcb0c82f8f5b15d43115f 1433b9b88870aa8fa5141c2906896ce1a06e823ca2991abfeb82565591cc10d8 e013eac7226a1971a281a22d8563848012549ea277eaeb64c99973a5f2e155b3 5aabdbf0fabc064f9a48f171ceb0ef855f79ad787192a90bc7108bb9a6d6d483 00c9b44758a6500b4b53ea0157b4329eaac40d786a01d49aa3b88c4a42b1fd87 3b37828a16ae6b2b92d177ddb2d4b92610512c9e8724fb010b4d49e6c45b2037 c9cf203442be7be85a577e43395bd96c2c787af58dd5e72659b142ccf05389b1 a56328fa53fcef38055679803b10f5046f05dc6d30a53936a6432d6eaf7adadf 3ea569b8f6e3a76731074e632212fe0dd0d61c3ddc7cb460f69b721bf14c4871 43a3d4db39641184c89b09ab60904b9cafab312d42845c508d0f487d413d4e09 1565aedc1676c2c611b8bfad9fbeb08e2f9ee458bf7f0b673ab6152f858968ea a651dc82820ae25275b455a7adfc8cc409263e161234ad849ee7167947db55e3 5cc8fc44e2f592189a50e97e7b0984bc6e40e3285acfe9da33b0ec9209b0548e 0010b01bcba7cfcce719221204e02ebb9c52f377c54588b4bfe8e96fb8cb862d 9f734bcd5978b7f78b055ac5c18fd09ad23d16472cf88df2c3318436367932a2 558bf154420d1b10e94a43ff775eb68a0ff3bc09a8330ca38cb1db6f1cd06b33 284e6ab9d25a4517b3c370b23ec9e85f575283d41b841bfe6c03c1919556b4fa 7661e838316a445615cf5ba14a831669e712af23d228cdc87d9bd85071f44084 fa00311ab1909c8db077cfb54d3c8f54c05ff911cd7c970fe5206d26faa8fca5 d02ca888f76162b42ca7d9cb4321e82549a8bb396b96820ecac1daa112945763 19a49550b30c8c37d58584b87c2664d93dd688bbedb408d9b0ad95c2e6d89429 a701e0da41b6ae01a91ac26401922cf07d95a2d86c5e07284ebc82b1fc28b653 bf3b69473069b1a22c23faed903243b927153439439b1c29e762a0607a4d2fd6 a3950ee6a233bd91cda431e75d4031f12f0774ec6de1127ddafcfef1239f8a1f af6d81c1c85b51390f6f5e2883f5e417de3736cbfc467d9ebc7db970394bc7f4 d7d7e8a1d1dd0d460389065d823518d145ce39134c7da875c5428bb71822b324 ef78038b094b7fe1e1916b05d932d3a0bffbe9afffbb9b6d39d883c5a834d523 5d225d2b3d01567baea3af61cb6dd43894bd1a007fd0c4431f06d0224bac2518 9f218e16f6ba00af681badfa80be08c4233c241660eccd763e17788aa7d2b97e 03027cc269be11ad15acf4da718e57cedec30f6140a02e9da4b45013d22b50c9 a1c1e77bc25aa3f50d4da6bda0eee84a29aa43a633165853e119c8c8135447b8 ddace1329387b482fb07a703072655f368f954ff5263b554a5dde3b2eed10222 ffbfaddb0653c364b2c3fd68bbb3dbaab29efbd819ca85e4554540397dd53434 9080996f8b4491159b3d7782d950e87efcfb4c0c1384aceb4d05a0dbbb65f3de e3a21189d9f16cd8dbf8100b09b559777865d2fb4b81bdfc1849bcdadf2dcb18 e1eba56174999ab2602649bb4fee9ce928f27faf795f3d1c59fddf4caa5ca720 170d17c3691a8d81eab20e19ec1e3be8039061fdb1e5a8a8ec4d772c801efb16 4ce006d669fe41a7e1d6c1ab925c169f499d4e7c87c7463cd5183bc13cc9e526 81e550a9d7dcea996b781d4e22921066e9d890988dd28df709158914f268d193 b897b42605987fc084ae2b09b54d8f071fbb155b3702f3af0adf39270d7cbbd4 a5600fb412a683055f7fbd39eb6e8a4c8f4984446c6f0f1c85612b6cb218ecd3 d4fbe0354b2a9910f46b489f333b7126b8c3ce3234b72ed7c68406ef2475529b 29930cca928f9a2a62e9c0c5a2b4b8f4bf3e8f4c1f70ab32417d62cd3b140f13 47a91894246056064da90c7c6174880635b62d609b0469cf301c7c94edc6bde6 f6ae1c8ef471188d4cf00188aed9e81bed9041f47777ea55055e08fbc191322b 493a78782d34ee10909dcb722fa09236fca78126350e56c02b8eeef14c562f23 ce092847f679095ac8dbaf3fbefcbe2c32c6b19c8cfa47c22231a40d09177df1 742a98d72f3dd1506a9a0335b4cf464e23659a77af679a1cf88279732fe6acb9 ea711a962f1edde119bb1bdce8f1e704948ba0c854441c217cbd4e4485ae469c de5ee08b48023869a190646e00c17f7980f80a9802171c753e1b74c15c9938b4 d25f953946f59d83ce72849a5715be93327bf9131eae9fc074056eb68d9d3312 59322cb631625ce75b0b86c305336fc0b545c236554a6eb93bc41d811d4d0784 16394dc8b98f0a12f8d9e60e6a824349f631a0949c1d45f0c85f6960ecf29a3a 2d575b38cc28155e10b803287361638b949f99ff322a1b8448480dc06b34288e bd3c514b24079a2c3e7142646a7c8d1812d8086b6c703b2cf9b046677da169e2 4eb090ab601ab394e9ca3aa028c396390028c09f3caff3631996703a5672de12 b6939673af77d0e7d2eb0cf9bbbe5ad0ba1bdf7343994822ad349a4df2c7320a a454eac5437161e91326e1e41de2dd57e00520decd1ec80bb270e280f343a4a3 2648ca3a7bb6cea186cbc472f1820aaded35ac9b54631d36e9095b0f70d4b073 88a8bf0f6459f08fa3969e6659c07e935bced94597bcd48a2b684099f71b0050 7427ba7b069eb0927123979754e5aa50628e7d9423d7ea9f87cb534e419834d5 830b1dde922d8b04721048bd32d2bdfd7eb77dec9e9f48cd06bc693901c1e71e 10d744112150d82fcdab7bca1ea2ede3965e40591353bc720657751718bbb1b7 6616bda09102b018eabce80c995b9f4795611a93cb4a943d889d55eeb20b90fe 227814013a3f0e2f3349c1842465fbbd276aa8028bc9b5cf9d3ecc52e84a959b 24bd195ab1c4a36948b9d2568eb145a8c747d26e737bbf24cafd2a1f755e9c5b 136bbb80adae604b996c00af9a5dba2f625c257148892ea48ea076a5086af632 1f74f230033c2cdc80f9dcd9e56efebae6de474dc9fce4ce7587785edb1eff06 756a7fc518ca0aeb8d9864924a89d841167de5fdd5b209483635af25bb5d80aa a4c693b6b6f5d96d623b20efe25b80f44c47cb681bc5af3690fc8748f0f1a523 30e10cdca483e730d0274fa8860c4b035a65a6bf360387f16cb357adb05ecd8e 95a75280fdd89363230c24c65ba77d1e856ff9818c1019623a052493b3c970f0 36454a47b7e65913af7efef3d3dd3c186c95a79bcdc129fc8514893e765b60a3 4f84af2879d94e485832deb5379526551ad7ef9f289f5292c63540adb1ded194 5c281606b7f37ef4afbc543bad9da95416b809635745c72dba55cc5750e98f09 3a16b88234467ea7ba878560371adb2949de9935a28e2d00ce2760112b50a2b8 8eb639cbbc0e72031e5bddc30c1982f4c4310aee381319dcd66b2421f3e908b3 024b43ca154900b7c721528d8d7ccaee255e0a1c98810bc22d0b93be555c4b28 7a489f9b1faf3731b951087e838664d69bcc2a32c98ed3337832b957d635da60 ef55b5cb67e18011a15f8694e8bb8994a2ccaa89d3a83f3f8ae76f3a89328e3d 852ec9b7594876aa9fb779f4f3074fd21cffb96ca297b1a9b03b968116d5dd77 1c9900d4e4ed598575919084f0bb96ee9cd36577db6ac731043a685edda69aff eb8d2b1d2066532d76e0794ecd91150a36d2aa4e71b73b8d6c72cd3a48830c79 09062f9e5918603d692887f46d870090432f0013d45577fef0646b9a4276137c 8ad8188ef7884a9e8354f6ee5d4f0b7ab67a771ba7b11b76bab96258e2cbe2c0 d02a5e9381613f058e3fa64f59b8fc8c5009a9eff3293c1189f19e31bba36edb 878a3191e6fff7a92e1032fa458036199b51ef4c0bbd3c33fece383ce5bfa6df 729f5f978c70f5305bc0f034fc34e88ac08a5c4022a828f4bf1d6f098834021b 04f43c5b0cb461fd0c58265354f9f07d15dcb9e713bf0d4fcc65be9bdbc370f4 1d1e4d538634441e61a0985e2f9432bda3327a8b891e56b74614df3d42ba7cee 30fcca18321e1bb0490f39ff9eb667e3d448654e2550e90095c0b3d59be3075d feb969a71e3d2fb502f25d7727b2b6eb62c100ce5bd56fee7dd62122a2ce4dbf 4dc527faaec3868269b8e3451c642ca501d0debad3d2f30b7cce550000d21f8e 5557e359da38d0392213f06e59c735a6700c6aa1b9e684f13f6b777fea30d04b 9447104b3e45ed8e115a9ae1d746d387a553edaf37af8b2b96c1c60030f7fc41 7ee44b3765c69b063baf68406478088f648082aca8ac41490ec7666f8ebb02d4 505c4e26624ce6f94394f0e3632c55cfd89f3da494c15a1de51154cd88cfeaf8 89cc4578cee6e692aafef7917e6112086fa78c50d2577fa3eae7aff8f2e90d5e d7c5bd3964abe4814c4b6e80b24a8041b3f0d693b089080e19d4612d8c206773 9e88d33c4cb8cb768a085ff462d5d5a1911b6d5c39ea70a267c34074683ffbf9 3d69c10a8a4f58e42b6577d191b3eebebaf1312ef8085851afc1ac028e02d625 c5ae4e747698a11a7e185002dbceccf325a307d356c28a98e991b1aeb52f4bc9 3fa431e19f50565355ea494d1d386bafe6d1e897081604ce0bc21396d303c5a7 23de7c40a9e6ec552a8d3ec3c3401fe80ffb4f4395625b575074cd5a4c333f07 499b5c9881dc6e1c4200f473efc40254addec580265a9694de4c5e1271ef447f dc5edc2fe3982aae7054de76f69a931368fea2ae1ef3811f237f0d7dbe55facd 975f9aa7b2389b791c5f1bb1fc339aaaecf084937cd8e5cb4a52b24cba58aafe 98213dc1615cb495556b597f1305d702e46ef09a5b139578c3acdd49a6f86229 5f9a1eb734aa0b777677f8d35228ac9f7eb7084e2d4e4d78386c5ccf212c5d4e 0a2a795c2500320c8ccdcd92c86ebaa073405a7d2a885d475499e7cfa39356eb a372fb418bf76e33d7722d0399de292d8f8fcb341430096ef6a889066429e151 cb450e625424ea40b30f3364fb2dfa3645767df7696c04d60869b08163ca971a 0713abb7fd5a3eff40c30dabd6cbaf1520eb720828eefbb0abfc3368295510ae 08931881e087d5e1bed448cbb12536e1a4460c1bc4e654ed7a026bbf617f21c9 2a650acc0991470dd5f344a4e51c4c64891c6216b845d434bff64fc0dc8a5b1b 744adc32dbff60573ef7cd2570a293f35a5e7efe7366fe184816aebd3751fa3b a0866359a4f7b6fcf65cd737d35b4ec13a94ddb5bd921b7ed3fdf8c334c5a558 27f12da0f8de3bde479d6e697399a165de8600c069691a7216cfb969cf8c8280 c38c2ef3785f35e681dc69f8770a00bdb7a43a248af63a822735dd1757941452 760d4a27a2a9816f121240191345c4c8f9480fcdc0f6a36da0180266277982e7 b158e01e6267bf2cf0828e1f64a65392e52a82248e1d0168c925e50012a292fe 34e9e0082240039bdb49a6abb9fc6062de19fda7cc78e9bcba432c8e539d3470 9ea704025eef38e989aab85966d3a776b3e7e223ec0c004d518dceb2c32f94ce fafe911567e22aa535e55bd697dd0097ac6b5678773c96b22455260442f5e13c b0eb98a24812c622671ec8f8a1adc7bb62896b0269a94411ea700ad424b6f569 bc8972c4d76294ae6003413784479ac17c84bdf7b688b76fad86ce1969e24e09 bc5d89a8491a2e16bb8a054df72165dc2f0b11bf08afa51aa5b9f17b8aaf94f3 b557489b63152444469d3897b638e971dcec4e68358ebf873443531d9d2dfafa 1f62e286a2e7717fa9ad37b87917c6a61736e22e58cadfd41483e90acac277c9 64528906c7aa5474982ea29e0a1a9a4600b508845457b36033f7ff8e396d04e1 6c4a22feb9cf50f9ecd1e49386e9f367746006651d0e3bac624fb8cde8f1da70 9e6595dd7d8e2e50cfe61c956c4a8d347564cb0d07f0f9c779b14f999e5b8d0c 00f38f58a528b1b1c05cc629fb0f8583768e0b19f6149ebc384404752251bd76 2cf15fcc6bd03ea0e391ac3070a20013140979ad9f8b632dc2f01b4465bd5386 420f7ebe5e742f626307dae4225d4ba26dfac6e3dbe65e8a5f4d17a095c585b7 2c26afd46bb297b7633cba449ffa0acdca1d4317e8e84362273fdf9fac7818b6 8a29d8d28bb090f38d5f4b6fd672ff61da8e147cc1f214c4c1947043b3841b4b 8eed9ac0dc09ce465033923db92b9022a97288b5812d496273797bfd914f6aee ab83da599ea123239e3ce850d056e91d83686ca3d9c955ade6640ad345116103dd9460816d56868ce52eb4cee1b25a262baa680ec9b5189b69a49a4970b7e001040baae41bf737a1b5eb061d5ba7394691cbdb6632b651faa4ed431e6d06a30bcf0585986faf8a6053538b35964e4685e2d572814735516fcad3b73b291d56045dc1c88303b6c51437bd7fe444f3082f3854cb381654ed64908958a6aaf4d00b49c69923665ab20006e2183bdd0a3c98f440aeef22cb2459c8697f97bcc4710c63f90b36a494d728fd4f467f2b5557a4c692ecf521b7f9ede1336591e263c7086154ca02928a84e0254da2d8ae1ca0381808ddf22f668bd69380b3b72ac05702bc65406a7f6dbd045993023beae1bd22a00d540b05a81698c067ec471c0cda035f2db79af6519f2b342e2a090dd333607fa07cd65717991e13c1be6853e86b01caa49fa2bd2b33ad670a646953c3b670d5dcdc141b70e25d3d9604b0048231037486e5fb6f8de8aa1ea3bbc374450894ce4599ffe436419c5645a17fed8f4c094202d63ef6ae9cf0bbde5e14df4c478d8ffc8ad5dedd2b77b4ce24e379abec0cdf0d833df5c78cecf17c21e1bbc888adfc209b9a5d8236df8ef7df7d3b03d602f88809953e67454882c5d4918ad326f01ca9f6cc3aff4dffdab393c356aa4f061388d0b124c2e05f53fd14256f58f98cedaa457976f13e59cc92321c27d4a7053e060e8aa7a5c080129e6c42c6a96cb21adb3ed2418cf576da3a7c56783326047cfab845125ce07a1371f41d5538abe400abe8caeae4d88c9ac03312cb970c064731321b8de81e47215cd079f5d54165c6d66bae3508644838eba99085dad30c6e9e1559d4e1c40186ce4579e68724e7f5fda4b886af802faa57d5c677c5950365483a1930425b74112a6914279b86d9b294c2bb2048b960c15cc74363a64c06fb84648b7191b4a3b6ba4d251f96c7337510faf74f68afd73323ff838844050b8717130b9d009a58242b20cfb9e2dcdea57374fe605ea01efb2c4f81e3e4d303b1f2d2a4096639a793ce676de371e1fa992ace7661fc47e968e224019ac0eb0e1d59aecf4aa1b653988e30e28d46dbfe36b826f9aee05319813ca79a0c0e8e0153dc0a3bc7960a6f0c7a85d3147d7e703359c7cd0327f2d9afae75abadce7b042f2d53ae3158efd87ac6090a8a43e6dd693c9298daf681a6225dff3d99b9eb022459e433c219f33df0f97a750a32eb33a4fa743322fe48678bf556b4ab32e90861c333c2771f9b1e58db6a5572dd7170438a9e1d52440d13aefe940e45ee9b0cfe0ba50e5a55c48b03dd1fc0da984316125d4ade6ed68e72e3f56ad91228fc0d36edb715d027daf9a1be00129e3be206665a0a8355afbcae43e5c10166c63f042a0458a14d16647c3e61d43c709498ae42fee8a1ca0ce95bfb4b26e7f7ac07051239a5cfe51e387a3afe92dd56fdfd565c48132d3b1ce4d814e4fc84a45d760a47b2fe7ef74c8c94cf3e39262d009bdf9052172d402cd4f39b1e0d05d4a12b090bdc6f7adc23f4f3afafd9bce9ff48b1a8cf50f2102db8b2fb0fdf082b7a0304887c97747e93277b12b267f437992948cf54acc59ed0078361d9aed238683902f3977155e9852601f995e8f90a8a048f4f42e15505500498b886036e803e760fe13aa9cf27e3eeb650a54b97e9d09942380f87e28b5b98b5ae085b63e854450a8d205eb792f6b2e1e740c4ce9e1554375b0c61e7ad3e1df633a150a9185ab50e6b9bd131db0920a23fd635944ec1ded2769f31040a9d0b3755568dd89fd8350bd27aaa08d968f5e66002d56181875d342606ca07f6ef9c2b7dfa066b8e80d508e21a71d939f1f9f7b2d520cb148035cb5001d700d5f6e302c491ddaba45f420a64392550e60eaa463c5154e98bfaeea33803b7bd997674e89339e8b9fcb7100f83d8c910657ea03e04ff8f9c7ff2c78e8e3ff4d0d7487d1ba202b6afb6f34f041ddba1ea971100625d2fb7d6c7dc0f59a0849264eb122f757868e03debb2890c41b0d7b951ddaec79c44c0cec87f35b06955d0ee2a5108d04597593c3de880028302e48410a9425b93d3c05da3cec29360d152799203c94733e20e602a46110e3108904ae99de7906e903feafe1ba21da895bb47abd23c362766e3dd793de708421d8b8708f507ba8af711e58cb32e21020348c04441d88a940b33a6c2f96a092a35555c535ab2b8f44068139e03adf23126b38b0aae14ca948fc1781f95ad0ea113c465e66453bef3d69ffc4ed0c3d6ab235c04054372c0a38469b72f5f4406772ed9673172f64ce98501252cf4ac0104514f923683ee6ccd273da7feb27e0dff9751289c2cdbdf528a13a4493edfbaefe1f4767b9686662747fbfc866dbf017c95b9be3e7ed52dc3a16ae3a22772083599b25a0ffe7aaaf4b3263b8dd722081e7877c200a54914fc8d2376d174627da84ce7b02e94e79c6d75d5acbcabf601120e7b7050d9773afc1afb68a7faf9607851793eabd68386ca0b2061afb23a011c70fe111902f1b6b01054e6aa82bc9fca7f36e228b88de3591f18943e8a130b3ce046bb9fcea3ba3313cfa54429db743ca307cbe5d8a2100f1b37b14e04020093cc364d0bb5a2aaf79b44dd8ec1b0e569693fc4e64382aa013a87080f3dbf00e1e9a58150475e35b783b68c2d7e64199b25697c8d0bf97b50c9c61cca34f20b4be0b6c706dc3b1a3ff27a527282e883ff058a9fd2baea2b293cb27c04376e08a733aeab4dcfb2665e3dde042b44a50c2447e533cb825d2be254d58f01fd2a0dea5ee6ca83d3400de788c2f775b81d3607cf8a58271e3ead5fa57c5602f8c90ec48f729f9fd70be4a8f8e869051037ff993dc27bb77fa6ccae7228284d63a305ce9b7bd11d1c500fb465038f6c4f518921d8432279d41f93ab05f3e913dfb90b216010e9effa747985436f5d6380990834dda299eb1d3561dbfd2b3a4516b2041f7283c9d3d090ef86dcf54489eda4cf94ec5d4799cb8721c03870d8a9a6d50e4efaa89dca02e6726d2e66619280cfdaf9ebfe40f97be8676af6564c6dee550477700f841785685fa800f5098ffdbd76e6556f364df5909a2de9e8208c83e503ad2d034829b3c9e5c84ded3bf5ecd0b7c8c68147fc0f79778829b13bd8db400a114853ea5dfe60bb7fcf130259c31960b1e00ac8d001545b915632c5a2528302f08bfc17a777981de693683e7b1b5dd67b132cc9fe5a8f47f7fe357b1e01fc0fdbf8a79d4c0f51bd60c0bada1427bfb445ed4845b6d290ab724aee85e7d2cc06a83883a50279a6b9c33defe81878938e563aad592dad98a62e66a8922e50f0031ac234bd6c99387be7254cbde336c54b3a61c45f8457701c3bb315e520eeff03f3d59da5b94d31b0c70e21a38ee7c0cf03b5c9ab3f01f3db4be8393d078cb40809bb5111ec3b11b3e51a7dc5a147dbf67e4fd533d7479763875208948d6c2504709470ae69a4d1d0ff6e4f0e318735594a8cc0aea6ea34d547a71c162a46070237506b7aed34f57e1436d11ec4d6ddb06fc3c0de082913b00f7120a32c5b6a0a92af8e4c5ac1d59d6e6922d15e953b0e1d8b5cb172eaa30ea96bd8d548f48f07d32884d2a8c222ea546901c0b3c766d0254ccbaf7d29013f6e9fb58a2314ed02178cd480012a09da6b7d8eef2135349a8e7c018d993bc93584d861585fe4330ab6f37247678d1bee3558f950b53dfded6a8a925dc9c7b8daa1378823b5a5d80648980dfae94cc88a277cf0f2ccfa2132a7564679993c0534e2addcee0793d70edf707304d5c9bc5986f8562ec07756ab6775a93ab8eff119bf2eb678e6138b0921fd41fa9a06f4b365751c88ebbc3d2df6f47cd6c7b04c1b67f3f20c67986b01e46a2ea57269596e48a270ce364b0357b67357321baa72384788f91d35622d0b764781529b474e1c012a1b80687064a6097f56c35d946e6862e1a5e96b37ac02c93832cf67c3439cea79b6dd01347c13a9360d98211b86716c8c62b7a98cbe096a78f6d54490236f46a23f50af0ff8323c15b76de39f3e1bdd3e19c55d195c0eaa60d207437fc20d2b0b86457592c34f80baee086ba71430c0fe1cc2a122040259d4abaf875fd837d9ef5eddca2b423759cf34fe76aa36854055c444efc9b400d6ee1ee114d69fad0304e889a9fddf8660059a06ff64e95a3ca55e4b8633970acaffb2a9ea512db9060a991468ebf6e388abb0c36bf035722473e4103e8e5109ab69886e0709a1388c2ddf117e52125da274d0200da2fcac00437953ecac190274046636d6b4d21043d0ee86a561eccbb8821ded8909b993802315d5aed775060f77453ec9cdc3486c32f4051187bf3a8c6e5cf57235ac0bcb4fbe90c209e904669aeedff25d831a0c6ee7be66f980af00faf9a8a2728e83ad1f432cb2740c01c0f74c42d228e5d4e8b62ab41da740393d6d11470189f928d44ba601e2f27f014d038c69f3a74a48c8dd72e2c3f92d7be8b50e73c10d012e97ab50a5761cc608d34d0b7ccf906d563aac14a3c095926618e33c0a2bf7000d3133423c21702401e43b1fb30f9ae1e796db4ec270ad8150372b3de23a6df3fd48f77d7ec16a5e0e94a12984c0314bec0abb78ac4d0d819a8c8560d44dfa14ed24b10689b2841d0baa918503c95c59a5bd25bc03b381d14f817540e872b6ffbfbd562354b75c0b030140695142ddbf3f9717ae0bf7ae69e4db54a7ff37e6d14764076771693d3b067a8f3ae345d5930270057a28c6fec9d6eb2e2c809b8e01f3dc25ec0ba185750e40aa7a2bb84676c835583d438c6a9af84d767a0cf7ed1acbed253f7dfae3b60ff84fb67fa1764456e1fc3c3bffd9b2bb861fd9cd4720a15e9326b0aeeeb6d20d7b5c31f2e34c173d7cab2f26da37e7698ca76a70d14916e29c60031a1bc0520fd4cf9e4737bea7b7ac1d545301961ded160b1336d541aaed2801aa735928fd038b7ab281c7aa534e7b986585a0dfc6c8c82eff45ef7107e8f790ca47cdf1700652f5453f53a2abc3a6ed582508fe5bdccd3de57aa3fcead227710ecaee6ca702e86f51a0bda0ffb2b96e0e890dea81d6800a852775765e9b8442058bf2a9130da6f0eef6a520f7c8d40b5a1ee4f50b5f8413a121ad5927983420a4dd42c72e007b7971c6a0697435c99845748de9dd5ebb757da60aaeb1feca57a14948ac170bc1d983fd5f25220e4a3c213c4bede34cd1f0a31abab72539c5cab9f8c625f10b00fd5a82ce4f41abaf5195cc8e50f31927201e66faef8cd1dacf1b746e2e7b04741a3c87145e4486706012fe29b86b589b2d86bc1de599bac40963bb53b3f307d67134096aca8b5916b3852eac05015f1d84659d148c48ca2ae5d37e9d9d6a075ccff663bb8ce20a11be85778eacaeb09d355ebfae5849508a8e527c51ff0808160696af8233a2a08d44b22cea406c92e798fa682f79aeb4dc60b044cf6bea05b4a70c2dd33aee9b3ac944f87998629dc0ba019b703cd2b17b772e0f0e9ad40b89661f7e8c713c126949fd9e6c7fb5eaff7b82adcbb1ebe5c1db48e39a294a0b0801bef44ebbc0bf1daec54e0e55e91c6fd9f5fa8dbde4d9e7ca5292702b800f01ca8d7625005c2960904458be303a7be3f7c914169658c67eaf4d6f37c31f02b15ade1bef6da437b811a2c7fbe68b10e0c2325d8795b3f1595b3fa70566a90579af7ac54f714d5acacb5eba24fd4440fe66ab7074d34fd8ecb875e2c3dc51042da5330347c5ec97b022dfb528d40e21c4d25daaa2acf1134ebb4f9bb61be00f09428f9d48dcffb42f79b5e5082ac572bab6ad48fd872e34f90830ce489094031adac40c51b3d0368a8f980170687efb4c7441fd7ed77bebf0e969aa7249e5097a841d2f856b73f4e462eed1b32abbaf13fe99a61687ff40d0f0d83921a7090791f757056034c3355f9858fe04f096720b169024ee6a660ee8b2323973a61804b6c592663cd9da0a2cf760dafce93c0e82e04370a3eda3401c32cda20d0e710e95a91cdf2598c13d1fa496141dfe0b29248693451bd21ade69f485c15b890e0788de287171c16aad15dc7790e95ad0fc7ec6d2ef7ec04d43de4b66027f3efa0879946aeaab13912535a8cb1b64da40f88cd4bf05aca006c847819396afc5e30e6cd3efffd1d54b08564cc3418725365558b725b45fb769f1ee37898a93962a0852707e37962a3181ee50d68fe26092f9d62c85c1cb77ff82db1f073c39b358063288d2c9157851296f7bf94896826c5cc291a8aa4d5e2dac6d7c559c23f9c90061d3bba7ae708e8f7817762fa4166bb7b36de4b35e3134585a224ef9ff90760513c436b5673858547fdf8dcea2c9d236be29bac5983fed0d9feb1534683f9d07a2b3db9dacdf94862f0f34403f00dbe86a6407fa00cd87af42def98b95a58003b8da78dcc5926ded31a99700239b6e58ad5f34c4a060c977f25f9d4db2dc6b05c8df1095740f9c795fe1e6fb36113d2a7e46aa00be752e97eac3042cb0943909394f817374ef20c5791158ca47eff6a04a8ba5bedd61b02f0de0f34648fc0603696a6fcd56b2c33537ad50fe0ee1ad688be41b4484ad20f976735607a9c3a506e22976658cfc908d19dd66c96f7f22f034bb4dd0189585b13aacfa3c103c4f0e198fa444293b6c86c4e545c31e0e500135f2c981b8d39f97b38b10f0b53231029181fd9c540600dd02b89a8d0fc1d4ff603092608e9bd578cb9a57705db1620622da9132118c5b86e8027f6aac2e7945403b56a241dab1bb2c50bdcf54fb910561eb382df6f5312808d0abf9a6fe766ebe14eaeb05b69faaaf9a16bc1f98060891918b2e6f8da57c1b3897dee8f26720cae74ce9a7d07ab84b3f5440d3232608e73548c55d4651988ccc9b38d3f5c1947dd79918671dec61d076d3feca7c9504e1239c3a8051068829fadd7e43a480e77030d325baac9677c048dbea71ac91034108b205e733493d1b95fd1804eb3ef82d470f40f7a569e9fc61bdef4a2f6f02dd897e00529ba5906970ed555802de642954eac463139e01c92246e0fdeaab0b76d0a9ea9957d2f0c05ff2c8e0edc313caab29101b9b3ef1ab0852a39711e6060f6bda1e1a920630784f6bd75d4a2a35023159426e90386154d86671cb7b5802fd90bd5e3e24c727ca5ba0eba3a5d6c80843f5a0f18ecb28885a044fad31f208a106e7f9c7bdbe3dba897e00223ecc3366a4c302a577f1eb27e0b0ffd655a60facdc582921ecda28bf20697efdf970dddd88a2d67afeb2897abedf09e1f2080b86e6fe41df1b269b97513cb42e475c0f6fcf892c1294b770b8c8f743fe12580c7bc33388a6dc120285e2bd647d935d2fe14edd6767240523c6a6a6ce8dc4f703fe9e6cae64d27147f15c45dbc60a59b81c00fc45d75b3e84a7efd8b4204c9f0c8ce4b53258fbb2013199e7669848d1f0c41ed4f63b09c9f2821215034d5e57057de41b4a91555a3738ac00f14271679c71be036c377952ae8baeaa8a1d870609fa4120ac7d8141c6860cb9de41aa0d011651d6d3297e9453f4f5bd7447bb7e0d35fd056ae6e3881f6c2ab8348d8b72a7cffee5cc207ae28f70555859cbdfef0ee6b8eaee4e0a79110a422c3037e313f491f41dbaf186ad46b011cee4b451e40b212ee5d77374db40affca06957d19db085a223fd1b11b393a7adac6c9bca7001186b9a71b722dde0441a79bcf62bcd4856f96a508c86d08ec2017d32ddbdd40c7e4697f5604de91f0dba36ad94b009e66a2a26504b4af021195a32590e055d0505be5804362b5ca5b0130f114ccb19fa428f4a1a15cad2e84b12d1034bfe070db63d2850285105ff5e08a693959939402df3c200f3a816d1d969f50bab79bd08d041363dff4ff07ec6040493d5beb02cd56ce3f8c31de128866feb814ec07e0a4ced725e91e37dedc8dab2dd48c897f111b37754cb90edcde47e75630a2f6201ff2337472dd8b2061869bd4d661e69fd95c172944118d35bca599a47c71aad0cef543cba5bec9447951a5dafeeb6cd0da4a7b90b24f2d2da6e2976ed33fb600ed830caff9edd40e685787401803bb7ebea625b575e31c74e6f413a341dc786088cdef6d4a39a87eeb06f4d950192f190222d53d404f9f7c83d13c72d1962700eee6d15c79e475fd2b40e7e3bf481c47d21c904c893469c0d24dac88bccf4ff0d28584fc1dffac9b632666f8e901eed6ba6b82552e4e5c98c9300a1bcb1a8530be063d72e28a26623a8170747aca6304f8189dde4a2c8609aebb0bc44f72aa50ccdc836abaa8c42add0df4307fc3cf25a9f2349fb9fef41733f4025c203a10f010a82507391ae669a3eafb59daf01b9044476b5aefa3cb0f4c87eb20ff625a00fdd587d4e7c7634342baa2585b051573605e3c823866eae93ed5ce756b9593d0bcadd3df462318d4fae8f1fbd6f988b85ad23ae6f2782fc90079a3f6e9a03a00344d3216f5e2b180592fbe5315c048a0d98573f13bc6f7e0a3ef1053c5ee59a0c727d85b1ad1eb491cc4e64f01cce151daf6beafacadd1c1c42133c5066a166094d2136989600730b1d3bdcb6082da258eae625b93f909ec945acb019af46e4096035850b927402db1afc50442b7aa495b961abf0d40acabfb9a199f5cc70590b691933f84bce445ebe54c4f7548aa3655b66a9f100b8eb5e9411a7afd2c00d02011ff3270005c4e87b7cdfb133b33902f2faa23dac868905cee634ed7561c103d6a794dfa837f45106976d8fb4326438393f877cb488a2a7c24a4006046e1402b505ad70b091cba197d15d4476bdf618ecedf585deca22c53542b8937f1234025189d55eb51ddc4b89a8091af414369bcef89c1e0995d6e013c85ab3071a270d66d7e16d3e6c8b1285726c9ec25731f01306351f4eb04a14b747a47c54b32903caad6e4c2a4d18aac8e69833babf9a87246b125c53abc94ba24611fe60dc880e19a39386e46efcc7f25ac23ca4dcb8acc4e85ccae937ac5aaa3ac4fb3317d105b9a9c88e5a4b8210293b078949a321599a8c9b982fd7e7f43004c2e158ae330f6f64dd94fa298a7c88f4f2c3bc66c39ef280bf6c2eb843812790cfae93d96804a188d889d6cc87e8dcbeef21e80ef969d24f5e3aaaf419f7b58bf235d9a7a10347a0d7dc982753d386d7d4d03e16a5f2220bedffb2fb468376e32c836930f40fadbcce8dca39ab2e9b8cb373ad5fe78de6c8d06703995ac3e892028b69cc140c6859464f42fc44dc04e3baf363d8dc2ecb7d40e927998cb1a69e5204031c1b03adab9187a621dcc06f258e090c38e44f847d15eb4953a03c36d9b806e4a5200b810dc20e00198dcef79181f3efd80184b4f029bc47559499ef342c83f6c80e06614e96d0d60fffef8abc4adc57b96ca00bad7ea94f1462189ad453ae317956009f55a6ac1f7a444bc24a0e80d18810ef6e55a285a6dad7d81437d26c568d600bb4b9dafc9b0441bf89ea46abc095a45e01dd7db6da171d9bceae5eba5f766509ea81b1da50928e8e75e0f502b2452fa2a19e7a0c76618492675e2f56841ffe0f25bffe0ca4433a4d783ee22972788c6471a800f96f992e952a1f2659b0737d065c9f0f2791ecd66611cdf39bcf8e97c8165a217ed5cc8ac544f519c3b751ae02402e461e16b090966670ccfac56f445efc3109599e6277a3cf90b847560fc204751df1ae4803bad432d79b0b8369e772709c0409be5048a1b31329b0b312220ed18b9add8b236cdaecfae8f751e06c0219d8fb2523f1f95ff0b117ab6369be053636d817ddccedc661900ef190013d4c1f41968203de1d8865ef535145e3a20309421fc074fa01dd9a9d39b9b42445275fb23a1252824b43c52668d962d633009b8371e0470e4060f998e83b58d1f3ef702891a27a626d98e3e8a4bf988c8508c2993484e6919aa479b007eab4817a8fcd9d1494c792a4c925dcd82139455b0f256d92d0f75051ec330d1f1de06d8d0dd1c60adfe384d4a44ea2fbba260adc0dbe655d9e644e9f53d1cacb1f7a91328e6416db056d162c661a6b6cc4390b07029684e5c758124114f03bd06f89caf52f91f1c9b7308d1f861c5f8561c57e420c9b4904703da2f9251ab8cdee745322993ca0fcb7b3c7e75154c04bd2e3414309a1aa2c1d7f0ea69f248b3d3e3812cfc54e09862746f8cb4f52dc51ee02e34b0cd4aa42092387f8e4fba74c4b95ce2b43e0ee5505a6fa8f26b83c3f3e7cf5f70426907e98ae8f0929ab441e70968a6d48f5a253f3ea4c08d749f69cfda864cd0d626fa9159382afffff36e87e9e41b0d11b926b25bd1d61d088105588552db70511cab5a9ca972940d381b7a70d6863bbb9892bd1e24cfb7ecc48eda647abb50d922adc93021ba3fabdf972eaae99d2a9a680fcb0b8a7dde3d45872c5a7776e0479d5f4d1b5725c402735dbb3f3c9e6eab496738cf1e87e472de407a9c987410e5915c67ac72a215350ba40766d94330918a5e9c96966a09d620572764fa665033a2857bc2930b5dc21059748635fc6f83232a6d0a5790d08b7321e1fafb544077209f679b47ab6be16a9ec4f0d7bca1ae333d8d1d0eeddcc8888ff137c112b0b2daa7c744d2ab6e1d612c60e381cd5652bfc1d7424cfe881188f67d127a1d30d60d5ffa60b9e7d45f18273893af97f2731f1e6ecd2439f13924a75067183e70621ccf25bb9f9455d1187f40c6fe03ad451384e0b3e03f04b1936dc90ffa5f40049503c686e058bb08339f5b026f98233d3c74bf4814dad7ddbe89f4b8d0bc001c368f2b42f497330ea0f1e2797a76ae75ade3a6c7c238a1b549c8508c00a610b87eb3db356349b8c59a9e2e8810f5becc7016052334d5b8d551ab7a3b5ff6004c328936cca8b7ddd2443c79c11fcd1559885c73221ec8f1dd62b2a1a387a740450831cbb1df407ea517aac799bb43299766078a13e89bce0ac2cbfa5754df40c1800d680d1b3b7d7434ab5c0559f646f441580791021733860991acfc02c1f0cda7791cb5c3aecf8fb8c3e68180bee719ad708289fd1a157a20762c1526e7004e9e69e5cae472acd7e3ce19ecd81e353be13bd575b06b1e482141d478e84fc091bf25c20ecf6a21d3ba2929cfd7430ca14a59b3c29c93d29141e10017624670046e45b1af99d7113d4c1bae5abac5e9cf4565ed3facaeb9f6c04555c714de00cb6d704108adafbe70d7ca2ea13ef74b8aeb9d100e3cb1f3de26245662118ca059927aaa0c965ef88e2c937c977b34505e0220a7345a963f485ab3e845a3f9c0bfafd69414bff70bdf8d0529bedbbc566a976fe5dd4551cfd4d2c78352f260607bb7751f91fc1acbe1cd501ed68dbbdae1780c3c0d5b148a69992ed431178a30a745e28e6218b53b17f40e92e8df3c39c4456606b8bb1789b1f926fbcce71dc0c3bd424c279baa90c96c20a117e7e4ca5e3be50f7078ec772c0b1952c23efcd0c0b59f4325305bc2e1439ee8522a1cd40303d7d34570440d0e832751b26c432001ef35d4de80878e07899f7286989020a1a1c550a1e37e5fdbbadce58f0f28005a595b276b619d2d8f266b78850f2a55ea4e9ce268d201c920fbe114fe6f22c0de04856a1deb59aca4208ff91661e0aca955f37fa8d6c2078281c34aa15b878089ccafbfb333d51e88e8022b5ea64e36b19b77df06a7fc97d843b9661b0853407f2f781b11f6263ce378a3d4fe3ce4f82de7b2168eda136eb9ff1c3c5d4cf760620619fb4ce7a12754a9b28a875b7874f2ee3f4a70a667ce3b8c459190ade720672ace6500ce446d56fae9b173e8c099fa4cab4c02a60e9e449d5ab9b7212bd05aa9af13f11ae312033de3694c85efc04305d6d8759162d37f88f1d6d6eafd20e2518da21f5e20dc75e98289125388b452d4f50f7c07ca2c32797bd591c836a089b35140c01c4bc060e55e77f709106a3a885aa13edd41f5ece46f3379f9b7209cfd67947b5a540791706d51954cb4d57942b55df9d999ce42b3f7cde9692c5060a6e349574927854a89cdf3abf4b3b6984330e35306bebe5043a3f35ccc20c095e8261ae48d9ffff3946701c95027e0d9bd2eb602eb2fed198407fda8453ec06102a8d9c56a13eb9e8251b93ed26d13b1488ad3bca54d945ad29206d19087502b00ef7a70db86c833de437930626762893ac9fee129d7b97c3e2e66e7d970b0740b0d4636a023561e2b542f07b3a481196af06653974e862d54a8e03903c6600fafc96687a2593539ecd573f77e48e2630054a5048995b6b85b8d31ce70b4509a4fef2c0c9b9b718b44c04d4d83b02e65075e2ee89f710ead74c11d6eff41e098ef536474a254e972d29ca4ed3a6c5d2fbd5d8ac985aae95250ad02c3e33a503d44269d84a1b02f5b48083192464ae509fe9bd0ded926bc3e08f784e993eef095b564c81e9b3f29b31d1b0234ef19bab38aebc0f5f0481c14d0ba6192f6fe20bf299a6f6391d447d1c66bd058c2e08120c0993472b010f3da5b2fcc32bd8ce0b7cde14af440848892f55c18c3efe763508701df85fc5d16ba3ea0b7a03794f0aeeb1162dfa1cdaec27acaf76c1353393f487ef2c38e64b8c42e229c487c53800c7126f6db3e1661069aa2278e83af853f9994af4dcf23b0a90f57d547efd4605baf0714c2a31134b8340a45262241f78f1bf3b152fbfa7887160fed500938a0e5a507849d97fb79ff413ea0e78d6007d2e8e66dcd9ecadcce464fcf0b9587b076f2ac97abdeccb1fe9d5f5e829a2f003a3cedcd2e9b4baefe559fd61836ac203b674ba1eee642c400fad70f04a26461f50ac8664c0d8236f580bfccf491bd207c037bfe12c3937306298b0254233656c29f61b5539219336863bdafa5048bc08646bd4811cca8052015a4baaef7d2798483da6c09987ae246579ab732707cc068d61122562115aa45a7c2ea58462bb1de9657b9a568fa06ddffbfdbc910f000e172fd259c7513dace74ff53fa087fd65a0cb70d4851c8b58560ba31de388450a9e4df3ff635220dffbe186990c831893bfd5a8d94abf9d92ce9835bb1340980c2fcb6ceceea0c8597cad1e6d8327203fa3baaa1b1f53e505f163d80c3f489a0efb9709afedb2c87896b4bdc4c2ba0bd572c2ca02e33665b6a0de664f4761c30f9e664d1cef290ea0a67498657966c56c005a09e5ea4db9d187fb235e903fe8064ca1f77b35f64f15c0ba1ad5a2069ab6cf731aefd065a0ef7d072590435cdf0c41c0ae01abb08bd8386d77e87b607d362793f8b92610f034f5da6c78ff74660b0b3f1074f5d46db0558874113f6d9cbb0d5274017b6cff039aac74d1d1510b00671d86b8998324cabdc180b1bedf12c830a86b5d808fdd82b6e3662181344a018e1a6b7e935df9c02eb149a3d79c6add6536c5602800d183a8336d7dacd74c0146e4fbca34202fc775a61c1aca96b66c1ac9077250a59f41f3079a730cf904091b2914bd86f9f2c2b90ab13b59b087124875e38aa977b3a4d52aa5db6b1de309b79496f95894dd8b4ec91ed4c5585f33c89c7e7e44c0c5f20c35626d14365b00ce2f13a492496704f33d1f6d65756cc61b8dbda2b4545c5259278e2e165b3405b1964227e3210ecb2255d4f6ed48e45659afdfe8eb30162391075add4d68bd048bec518df570763d6a542b6fc6601d1c2918a82e20a08d6eecadf686f46fbd00ef6e71bb83efb0f7ae1639b79319088ebf1da1e1f7d139836ea51d1c30cf4d07778a3aad1b811927dbe7721cc523637c19dec276f712f656e0aa27b4eb952a0e507007d7e238d8130c4016475b7bea7ca0b4edf3acbba52a001a77d42c118a0ed3b960848d6e8c17b227888f4c79d06f67c5d2ca2e727f542b35821d01455a06deee7e7ef50964bd1158b29ea9dda547dd59ffc7ef49d040a4b8e75bf0f7d50024c587f9d81674d5b66f4739dd7b4c2e9527746934d947cace282b0f35736208bfc17c3fe60ba52ea0bfd164dac7c948eb54306b3f5b5409c946306e36dec607dcbf2e8fdc4083c43091d3a16872feaa1d173b1cbd3c7b9ca0f52e274e70550ac7528f6821ddeea19c7c6e9063d1997f745822359aa8df37e5e2e62e1d8d05010ac3c3b0f4f78653d8b5254abe07896af40e524f7dc21e471a4a273b176f61057c0fb4b117376ef2733d321a58168654ad02f2f759f4a9836574862ccd6cfd0f4543504477f42be432e7d84c64e5ea20870b6baaaf026286df734762c06ab30960d8409893a7e07c1a7a0853510692ded4ebe487a2894eff991f66411de1cd0fa7b97b0be5eaba615f2960ee93d029b0071d52e461ac90a8949361d65e59a109c883ea4ef1bc3f42ea7f5741a4c28c0da76e190c3cdd8396cf61c5b15382b7085b9ffd8380b85d6607f013efdc02682165139cbfce6baf90b76514743e936101e23087221677b9e7d0dc193d5bce255d227dd13b650dc1563f8d59df5ac6480d69eed9d88a7dc836c45128cc7d4333186832560f5457d787e80f36a46d388801c2c9ddb21304c97b93e9a1f92903367bcfb38c59c2b1e8cc980ce18e381a4008bd228dd152e66447e3ce3f86ac348aada01a994befc8db4026b4a847df3fdc0f8097fed3684922c69009181da184994cbbd807b12daa9297085e8aa7ab58980cf826f51421bd14f27023d68780658c8d4d03525e4c06176b3f50dbecd14d870c7b0fdca91508422d028807faf8a313cac8dbd550f3a2a0d73d90d653f29dba09fc98e4dfcef89ad8da0e8bf68ad8caa6ad30a1e076640142f93904dd7e5a79084b4379f6428fe491d8955b7ab3301a02f62ae5147e649565afee26df7cbecd06d57cac7a4426c22f389574fb3450fe7430ecf990f6fac6d0f3749701ee972b0d30e7e4986bbd86b5e10f1747f800e702e667d9885eacc95dce5903f142e5810a108388154468d4e9e365c2a08fc37bf8746085070bb55c1dadf92551286e2d0e05bf09290f3b4c86df7a9089d22da35264cf58979d2d8f3804f6b993419f9b02c696cce559b20c1d9cc4abbcc42f80cbd09e92e4c10be1f5862ee0b40373e60d2ea243e1391ba6b43a06a9e6e7432067f438d9f1f0739380862685359ad351063fa2ea1b36d91a05ffaa66e73fcd002508c19581674db5ecc684fa31d5190e028448d16918818a1f95473ab05e335ea030119a7bae4190664a0dc7724ac64d06901b62971ad6e2d20842d845ca346e644c04877e2beafff06bf02d765c82e505c7e3f61de91929205be778fa67b0c44aef918f56d45068c80f4ea6bb8dd0180194420bde09d15b760155690c9057fc9a3bcafab8d7bd4888a9b444e6b201900b8b9e5b8901ea85bee6c2583edffc82c07bbe6e3a1f847bfb21e32463747db40fe613f6d469e87e5ed7c23ee5ca23b8d8a7b7e2e1c42c531ff771a643c5461b0ed0707449ad3658dff39887e24c307bbf1ae9c017d8c85a1601401d337fcd20007fcee16575c3b24d1857fdfb10c76723058e549483362185f375a26a14abe10c02f7acf40eeb1766aa304c3c0ebec03cbf3e922c993d5d0e94307693e6c50e0ed4959f032ac7a2464b0fa822717b37c0a8bf47650f56c158c5a6bed599cd98093d8a76750ad47bd88f5533f31ce1e444cef482d3a188db802cb6b8187c97bb0c4736679f16828545ff7e7ade8fe8c36348e362402dab2799798dc595a762ea03285c464eec601fef86f3026d479a8e88be924651648caada883766fa943e9b02fc921ae573bb4bb1be4484c4d2bc9aace9cb0bfb4ef4449ae54e529ea408ce0d83684c8efca5045e21037ca8984b7599f4ea054990bbc0aa937a1892e287640ff0eb8005e807d12d7438beae472eb75c43175f2eaf99840607d0a80c041dce01f3377c640f88fe54c2f9a72d1831330ac2a489fd884b8d0739ea09b1990e430ccf463ecc7506080a84cca5d3e7743300af5f5a7c905d9fc1745a4812973a310acbc8f5dd0349227a44372ae64f70f6dfb9eeeba3d6e80c5ab5ca3705cc40350dda4cb69b8383ad35a1722b7d9bd6137633fb68ca182b20d2b8c7e633c7bef702ff54030aee57240102a070a85a567ff8efb4dbc5d13933ac1959793a758a890f9a9e211e97d386f7e43df18b40db3ca3a03b705a54a2f7698e325f932b10570b4c284ad4fdf776dc72e4e20c6ddb1135aa566d5e2a30c7a92c61f94eb4d9ee065d656ea54c85730455f0ad78c66b579d879f36f9ad5c00f1b51c1a7f62e2bf0240402c7829d440ce35fdf73930905be611c08deec06c0e16bb1216b19f9489075a2fc975c94b0b286c9e1e7bd33987dbc7fad31f52408289579af7a4407b6e08a7117e48ea257715bda6c70606aef765903235689212e6670478e53e04784800038a39ecd18dcb31a86ad2ac1c57656570cfa1379650779a9abb2760d76fa5043bec0a1f79c47f8bea7d02c501fe70970a2a8155f27551023d850a94439bc9025885d18602313464c57d9ba8135947606612dbcbbe897a4fc84b446cf3adf30c78a3316a787f9340c5493ab2f7f8293109d2ed4a4181ae5a50cc032aef515f0c8ef43b7de8db05aea1beef96ddcd5010098e59a6a6b4fb3f2fe3768838b2ea0d89317b3564f9b00b8bc3ea9ce91bc9e51c0acaf40e247bc467c23aa699645d0b8d29d4dc5ecd76c8dea7112dcbb33edb28bf99173bb78a5367377c6e45555f0745582bd92d0d1f0e8aa7825cac86318f9036c4c0cff7cf1e7ade694f19c4fe0650595a36d245db79d3c896cbdb8bdbf7e9f224546d0725b3a1bcf15f91b6690a6d662bf1c6aec3d19f49901d75f8507d683a1550fff2e79ac387de23b740e703518670ff30cdf13a353c12bfcedac0c5964cca8f13b29452a6cdfe99e215ab03d0fdd648a6b0fc236f380cda385f321575ce5a774f9972dc7edfd9f201c1f70d953226d712c1eaed603ceaa086ab2d0cce13b0fe3e757e6db1b32ebb152e9b0516660396f0ce6255ab4cd4a5042251776905a3dd9052f70c1342c56dd6f15e0ad1900dccd98c4bdc99091b8e209b3ed40fc4830ec028c7c7b09a550cd3120001e658f047f5ad9210177eb80836618dc008ba903f2231456829fc1c75897157018552d3637db809464c6864125664186598833b183ddb3a8f63dddcca4830cd01d6adc74b26806186e6049b4fb28b5b50b46c9ae8a5890b9f8468b1b081f41f082e2480c952c54c77086094b8583b15ba479765598d90330d7d7c3a7ad15d840ab289577c4cef26f2721fc10d78c8b175a42f11e07f9721e88e315ca991f3a302719e980fdfeea1fffbde0f43f14a4a61b3f50880e102aa78a8745a36b6ee7109bf306a31b86db50f351ce637e0ff6398317210f2909803eede618eab6b88760403de0787729739daedd68d077fe93d38bf0a636d442c127c6a8533e3c7e7430c537aaa1ea0bf36bbd51791892f0713afb45a9ff8cd054e19490da52c02022f0c6eeb8688681df639bc282da1e7bd2458cb810fd61a05b845269b2cae368eee0ae27370a68606099fb415d8535f772aa2621f75756bc782c33bf0f63509acfe00127554027ee33249161a224fee6a3ca11a25316423248cc375729a77270db80a27b65b308025c2094586be660224f9cf7d6b95d78c71fb047ec3a0488ed6ef0e01c1f406d70f7458b121905275e710806afb8f141aa904ba66389236c2017401fb16860d9599532592a8ef1a68895193d3666e6e99e5a076aad7c0a8cfbbbb06931a27f8501330038502121bce15ce59a89e04872e8a9697b37f9a5aa2bdf406199b3281a2c053bee5466466f5cb0866e3213572328ee223866181d6cafae601f7720d4ca7d0fdc7a4d4c3e26b595baa509fb1681b3362634f2b4fbd8a308e08fd2e4d6ffd6cc6fb1b85d1d64280ef165991162c0f82fee9c9860ecee583ba04f40cff55527a5618157323d7f8b224fccbdd7b0dd9616521bca01d1a10a20708a56abcab604bac5421090a92477bd7cdc425e3d79f2ac946afd30c4ce189e10db7173a1199d1b5641abb843492af9e7a95e048e311f5615ba7a29cff154b8f07cd42836293df587a81d0a83d20fa6a218ec5c683ae29fcb9c24da0ca1cc65100cb7f28534bdbf81bce154b38e35ab72dba3eb1119860972716c21e3f51ad830af6b3a370c29a6efad98097ca337b3288ead03003524eeed9677b8f00ecd0fb0f6883eab28999f637af6c70b8fd50870e80748ed48570ea6e1ddfc5ac22318409ef6ae02b7a6f1634693728d6bf3feff492831176f75daba3690643f504519e04a8f1e2d4c591f871d73ce80ce5baf2e5b3f2f5327c6d8ffd6d652f9509a8a80dbb700585435a5f6628b445bfb9e7c1a2710cb4b604e04bb8cae5fe07faa37308dc8e0850a415532644e8a0e4eb30aa3ffd713f5b29b7789b13d14b8d5ecd0c0789b362a1fd8586fe3012222d18ad66db6143432536be9f1e9e604705b94fb40ee08fea90119d596c28e3c1d0b6d00f345d68dcbc7112ad962d0c1aaa30ed720d234d0370e7d904a367454e943ab64b606c7fd23dead14d96ed08198922bf4b07b1fb92f2db068469d2d62bbde3095be07e6dde1f881f066e95f16c19900a2e07a1558583fa7de1d4ff61dc74bcd402897c33536cb8c505a2f57c644093d6590e621ce7bb157141f56d8b2d5211532459dd6704116dc8fedb1de79e4d4032560739aa95594746f5d41223cfba8b582b952bbb67b81dc48dc57cc044cd02dc140b526bdac159f7e02157f598fcc0c79a3331caad2379efb29631b10178b624e00d3544345d63b825078835fee63710a93612e0b5a90c6df9f89e5f6102f483fb06a17acff0090f691dcc829f019ee4d171aa038c658f29da19d219b74ba786820e0d54cb4e9f7823ffcb5bc382e96838571279eba02758c4f1e8a7e70fd328fe0029bd2b885fe93f6c790573715755e9b390eadb5e562f54672321015e605c5b075b6c2b42e683d15050775a1ffe55074196fee10aa53c69af13e3603cc596cb00f965f0b6222f97e759f1def26790b35036647674a3a2c6e2a97b3a557842390c8fbda79236230166472e3b948784641d59f0d04c63ddd022bd21fcd4fbbadd084630842d3f1216835813afabea03c8422699fe84588a82dc0f05e6f31f302a0b1c0a41362e47f5f33496b2efbd8001fdc55d75246aff8dfd41074a479dbb770d60e970130147bfc25d8ba6f74dec20ab0755592fa42c4d49c02ec1bae94e88078e19f437944c1fb3828ae86d2bb18d6f84979d32e181c31007733f9a72ebf0011e243bf80f9369e8ea5cae772db3e30adba89644fa711ecaea4b134abe7941057487f420383194e83504fe4307c55ef08fba74a916e2499bce5e9f486a2f850576d2cfe73f5dec650d712bda7c3fd536c142d4fe1cc7fa164a022a7254045c0ce1d20bf12b957b6c77a1f8577b18567886052015bb793dfdcf6ca7fe3c8a1f02eb0feb9968143ba1997df835e0e6f94167a551818d8f4f67d2c89e5d71b11a0f842627755e24b896512487eeef37e9df6e874ffe81c9b204384ecdb6c8d1d00244459ac90aaf3b4cb8a4e4c76357e7fa5ef72e3358f7dbcc0f01ce52e8bac6093b4d13a0f8d52168e309f2a85f49360e0110f127cced196d52d2c90971485c041c259826e3a8a891f6851cef8356b52a93f14f718f11d5be51328d810e805a066d4731278e5e8b49c529826902f0f9a036a35b302e81d5c3ef738ba53cb53d04786be5ba35a7aab02514cdc3a72cc9caa0190e41fa713284cbe41bf7807b540916f9ef13f1cfde2926ce13e565fb85a03101bf66721831a21b6bc512cd08a100ffbf8c995963a0d5a39ffc09f628f73e8148e133c5c33693ce6b26f5350f23073b2621251569c0f69244a33b6d4d3105faf870bcf8dcd7cd13204cfb46b46f0264a113e030a5cb28ceb624fe5b29101e2ebe3025c1add08645000222a035750935cd59c639e8cb63d1cf49b672cf9a126605d51d5ccec1d051880b746b640a013e7bedca9942e75dad4aa51a7a3c9cbe067e5f23807f79255cf00c6544370f020bc04d80edd25ebe0092a833dc249ae9aa8ca810f053f4b5ed5ab3938dcedf048358aab72be322dced3a819e69f17aa056f40141ace78dadb59620fca360cf09a51123b9a701325c19b6dd8531d9728b67777b4b9342402d81929884e1b5390b04ab9eb24053e5211df53008a98e0f39187f11b5928ed44bbba1ee5d834c4708728eda8318abbc026fe7bcb46e8640fe95681339d57eb74ca2ad850fc8878d013186853a4d498207141dfb8eebc9cf699b163810a4bd6ddd238baa3df8cf3404d0df52e654a3f391fb4b80d830c27458878e02c68bca180f7b25d5ccb35b140387519103af96f09fc7b37d49f8b9e71d8e2d2b67a1187929559c12daa4ef460d53aa1fd0cc1ba9a903bbf103e6914fa6ba691bc8106b36a8cced2baa4b7f60332d5c2621547dc801153f2c741ea7a48c26aeb9d22747e429b051dde38d2dcf02 false +check_ring_signature 94ad9271edeff9d6f0a2d97964db9d9872238a0eb0fcd8e9442765354f2ac7db 7fdedd8eb3a8a8285dd9e69320d32c7a4bb9345e04dbcd2e78f187239fdea182 1 536ae93d682eb66cd15cdded788bcd119ef22a36da695848aec65767442172f9 413d348b9668898514e3d909f7626ae17aa9fcfe3bb3127d688268f0f722e78613e1e1e735b19c2e7478026de3195358eed8f950bd849cdd0a5955f93ac086e8 false +check_ring_signature ad17dcf8b8021e2bd2174678419c0bec946dfe5cf8ea64950685797ee24afa34 c1c185703db130902e272515893b4d33cfae7f4e0d1ffa6f7784d19c3d030deb 59 baff37828e25f9c22f3a0b82ef28e303bf3f9db5d496c904fb65e4ad0a436bfe 8a28af4dadb3c4acc8bc7cdbeb4067c2ddcde16baa6ac8f95093fd3a5ed55425 3dd4049a16468255de9737af74ac83ce41f799a33398b1a93001933daaa26839 5c52f1e096b01c8cffacdab5755ca46f01f9f4385b21ad13cbc2cf0562c9e45a 7d0e521ce18ef4ae618eb6ec58595831407d7979dec9feb8ff3e6c98b03af6d6 f2c0658c1855fec9a3d03a5120450a7916bd2e8c4b010256312cb6e9d32f7516 1465c80b55481af9c246e5ba34fa5a1561d02e46852ffe788e972cea97b808f8 6f0d12de734f94af35c0adce5419a4731df1fe688389af5383b09fbcf957cfa3 3f87ff8b46fc7c29f46fef4ab38f4c94df267f648a761eb6807457b261f6b313 49040ddf2a7c1aa4a7ad7cf11e3aea09e266dfbf2d7b47bc709d1d2f6af76ad5 85c6c141ee98bf436d84cd82416269279cbe99afd5474ce99d9ff3592aff822d 435b2c39e293ceab776a3f3bb0114eb5555dcbdef2fe648aa36a6ae81015980b e3e7deb1d3be754bb40ef6d47ec4fc2e8658ad71b0dfdf642570f80d75535fff cf8292c3deb48bba36ade12059aa1ba3075abe71bd9a78b376c832ee6d2c3173 7e138e81c5975355af1b29a8af1a84062fcff80e28460a2400245378d6cf7f3c 005833fd1f6e96c4f3e5b38c1ec4f70bb1df118fcde8c6b0aeba95d141e5ab2d e9e102c1b380ef76da440d3e08634d67556dcc4e0eb0151cae9c766a0b50260d 9d54562c7bf920d027913f698ce4dcfac47eadf2156b73884e5dd438d3c63a2a 72d146baf8df9b6294bfd7357102e57944b39a6ed2081fa322ff7a616ef2076e ea34c68f56b7ee74c8fa5f5715726bec19a8f68351b60c70ff2fe614aef0b76f 626ec579feb0c8ca4068f03e591a7b47c6d6d076ae3735fc7f38181f55cd6740 a6167af7807114da8f392c924bea16146f56c760b6623b69ef69fa6b1c563f44 3b7092b2a40f52fd2971326a38ac85ce974908892363082a6135ff83c1b7372c e557442c1d9cf30d960dcc9b1abb69ecc1576b918884c21d443b2eceb3ea2d6b 3ee2142ef579a906c804cb0ef4da9501598f839d45f814e87b8b2c18b31d8964 e7abb01c87ba75a7b19e44696b9cb4bf6ece3d7af5c1b05cab9068c12b606752 e072efe51db4ecee9089cc963000efae699282edef252723cb540100692ede3f 39dcdbc0c400895fc5b8166aaeb13ca8b7bfd8d92c8cf2b12bede476ce8072d6 458acea080a5b3d9d632dce43e76d3f1311adf2f9711a9712efab48587140a51 82cb2049834eaf1d9fc1c3381c5eb39d202013af624cdfa98393c9e36665edf2 b4cf207779ab0036bd385d60dd19c5015caee514f7e941b51fec5f9f8f102561 6d0f0a90a15bace8a2fd30a1c48670b21edcd5b6d49120962f70b832d119a11c a88158b1205c689418cdb318df7809fce9c891f0754be37d30ce810e8728ca85 1a31ffda3c6360b1c7ff9844600a51e2a0edec6edbddbca4f8a7322a29c918c1 eb63144dd8b423b4f5931da3ac9348820cadf0bb4e058436de4f6306771d7a25 7d6c139855c4324201dd01aa4749bd32c7f1296e0317b78aa93d0b903f1dbb15 dd60db73d2f314556ece93d6ae587774253d0aeeff2109fea0fdb78c1d028793 d136d3ebba1f9081bbbd64e7eba1c87bc821fec4b5d46e4918259272341629a6 3759b54af7832edfcb10419e7220edb1e02ea930b7fd114fa84faa5e57c87dc7 c604b7093ab4ca537470cbbad2a5df44b3727ac3a5547e88bb57eda53f6f0fa5 d0a934bd5bd2109f2d143091d0d1871d59f39ec84284742fe9cb7099f8bec74a dec36a9627894c73d123427fd8918e077f060b1e97390dd6d583c817ab241183 57bd16511dcbaaa410565528908101eb8ce1dc1e00d7d93ee9d5c04024f6a748 8374a957f57bc60caabf8f64ee3e86c9c4bd532a800f6eb742c2143faf47db32 5c880e223a537553482b74b003c9eb7dc828fea0bfa11ee58a64b620e223ad64 d470c131720dd730c992ea268d0a3b1eef8f319b7eb83a6dea9eecdfd3ed951d 46f3664055f67ca4a8849bbb8d4af5fb9de401e66f6c255f2c112d91b38d3c08 cd381b11239ac8532a5e346178d8058bce04e78a728a2bb217329adef1824339 1b5a7f377f9e0a0dd6420078b08b9a90421f219ebdcd7103ea7a701cf5e132cd a03a581b2b65e2b824880e63e127b1a289e228439e570cbac581a6d25c6073a9 1d630acedb719528015da7dd7449cd90a4e7439b9bb4058f1391995fc6b9cd89 772faf1c1a71b570154de689e8acefa0a96c7c545ceb4cbb0b934f3a82856e5b 12aab5e49d1ff95328ad963b746cf52876f87347d17a9528f74d5440f83c5c5a 8796c07fde289f1bcd9245ab67aeafeaf647c31d6d6323584e2cbff999dd955b 7f5a3f35408b491ee565de7cf10327a3399d49058bb85296e837472a6582f45e 3ebcede7d5a853dc21b40bb68500a1fda03febded1af2d6a050ae3875352dbd8 9ae97b326cd41f5d06b7916e0af2884e22c7cae7add1d695db12fe1cf73b752f 33bf05860ca4e4c2b94cd8672bf413a4530410bbfe23d589e17d873467f93aeb f236fd1dc407a10e5c50cad676e42b0dd96f95ed33a18cee40b6d361fd6558ae 56660b52e533aa6c587481f62af15bfa8609cbd5eea01931afb2a7b9336ec00a7d42dc829a7aaa990f6bf614ac52d1e49c50d0dac1ed4e40b82033e8362949056d75cb212b23ecbbfeb6b6695715b5196bef3fb28e9ab38df5c80b707313990fdce3894e2159bfe5a6fd3a9f3040e08bdab630b3842bb27ccea0d9de47815d09a9d30c6dd88cd40461c1a7ffb03fd067f7eff1f48e4af4cb27ceedba5e315c0443325a416456699a7660fc17d837cfa11873626504818f023da37b20d7725f027a7b466e2e6968ebc1f2cc3b20e46e684d4659c8a82965ec534a1a193aaeac0c8894aa3f185819dac18c748e40ccefe6720e055f650220f12fd546411e69df05ebaff9302509c92f71a09a29c2f64404c136c3cb93782bfb457326c23ef19409a2cadcafa8402332ffb896f46f34e53b89eba71728b1d9f8cee158f3b3378b00c7ee7c9a3488b1f310435ef91bf15b4adf7b0010442510dbe34de9cbe68c900ec46dde8c2ac052b27d85d399b7fdb74e63d822462b6e4e3d18153fef0a515f04d63ccfcb1c37a2cded19a445e326e97e70d86adeae46de655daba7efae9ebb053c25e461ab35eafc002b7a9bc6892cd35b638c264b60fc0f5523186a26c1a709025299d2404e3d0c1c37d5b8315aa1b21948c1248a2306836e7ea2ce250a2204c40e343aaa68715f6bd42e0341628086fed7ef7b09bee7bfedc2337463ae9701f78113c4868e43d26f466a3ad1de63b0de18292691f6ecee820e938760af0604227277a3d702cf5748e627c15a8e35405924c227d29b325de7caa3fedc43ea0668990c5402916b641512923bce143bd774471ce3c87171deed1a5b2c4c7d260d9f48063627dcfe9b78f3392d26d5e014f2ada3e2c5a68b02565fca864ef3d70e48d64dc0475c671756100bee87e4848e601ca6e50e857b2a896cb789e7a43b0c24904b21aa9abb30c7715b3853aa39681c0056f5d6258001569460180f13c7032666ddeb3a8ccc503d45542e3b2f9728a3cd0374178d48bad051e4ba126f4b0d60b7fcf461a8542888c48a32be1858439f35674371941b6f790883895f105b0f0803249e15a35fea4a40036e1ed88bc0bfa4988bad0d0eeee63354d253bbc80e0da074dcd6ab924b701e82916093ea3aa84380c36f2bbc1c570401b0d392420617781e734e09b4ae2fba0496f7ad70fdbe96be8faee66ccdade53a7397f15d0fbe73a3a5e9e04245fbdbf88b031ebb4edc832a2b9e4b1c2e43333257d2d7d90a801b5e85e19bc5f8824e3ba80d168745825d130e3ceeb6d01d456220e1f3c4088e35026da2153dee8ef1ffcddc37c5dbcc431bc41490b20176279e958489cd0e30c721f3f3425133a93908ab1914bfeb1145fbe87453ef223292f56ab4ddf20bb54b0c6e5191d06ddc1cb418c49ebc1118fbd53f1d12403f72fe002056d6b402844cc5b6be367bd5774234aa28601c13c6dfbe012197e625c83e61fda76f5b0892231ed3c8ccf4dbe2336b94260a8a3153b85eee94eba128dc0aedcc5d7ba308674240fe888b2382dcaf07998bf667971b13afd7b5e0f21b09af5353a17a8e0819691ba971bccab5f06afe1469faf253d57caefb910cf31f5e7e914d5cbc3f09fcb560d90e87f1846c09ad923031c72f67780833650c60c968355e6d0be8960d49a9e9e307d98371718c741696cb1ed7924db89bdb059b45b48e14bd9c438e039ac14ec87b1ce66602588419a6e88fc4ce823523a4f2235374bfa3015e6bd00356c90dedcd8ec8f7e4c39bef50ad4e3e695b5b2c9ea9f9bcedd2b763bf7d0b0bc08f930fd756ebcc101b966af8ad487b60308e0355b313b986ab351de13bd8065a90fae4cc3a75848552266abba7d25e84c02d58a6ea2ebdb61393f57f268106e66a8f23c9072afac5108e33913b2fa1e29901f1b476d97df854d7adea89f00b1d643dc498f8adb8e8bbd2fd1ebbe15181584004255832007f52fb43af6e840858db3f918b8fa287dcc09a8c60a6417ca9f318df5ceed67aad158cd1d771350e338ff5a20625dcc17bdc988a11fc241523ad8df0dccf57153d0ec8f951764a09923d0a3693b6426411af7bb4b96e61a4c024a3e105d59d9478b9d6bfbb29000fbd43fcae923c7d3f09002b7d4a20bf453cc9f3adb8681f6c399f5710e93aa20aa73f370e3165da4c853b487ef6b87d059eb24752c9e038df8196352600d4360139a4fd01cf806576df52b0064f5d997d335bf140ba1779c227d64b3e97687601d440454cf284e2a43a1799b5227d7207b5a6fac97820f1d445b921a9ee2793066a65397f19a66444c1423e979a7f2e8420565e07005dde00a059307b212399089228ab2b030a9db08aef7789664b90f905b4125a8d0fbc49d3c340f89fc7770db05f5378f448808722bf0e4c6c6e01b69d6b2a244f50210699172eea1afa2b08aee30dbaebd85f23f5d044eb4b2069db1a717588dc6aa9602b79e91d995dcf01be67e5fc433488da769335126f338c4fa7f346979fa702dbd4bb3103c1e1aa0a68296f0a8972d7ea5ed4483dca4d5714b8327463e3c27f22709ecb625775a80df3d720500823ba12443f74f01e1d5d45e81d63f1342299e9322a8067ea5d4103b695707414e69ae82b0bf17c523bf375d2f871593d22b24491c684fd4649010fac6cc931f7107213857e5251484d18a396af651946940a084d823953137f2d0f46b533fd935275067209e7521e6d8e70668138f618d57654396cdaf1d9d69000eef0fe966bc138fa35944a0f29b5c96e804a9bd221b2b5dd83ec611259fc400f22488a9bb7e7b73b23af351aff9851d371ecc83d48d3b8785714fd3c0ed8af0523771a00a624c7c85a4e81f157e6f6ea4d1067d7509ca44a731f88ed5f31ac043c039e7b1af9e0a9aaa90faf37e88ac18f532592ea1c610e73b39e7cc187580e70e367ff06707bc92410343173683d89e291df7f1f6e65178121ccae563c210e13f0cf37ca4f1899e054280cd53558769464a020b1c5902b37642818fb484d0850ba26fd6e2eae0366d80ae9fcec8dede66ea819d34ac26f50d52d8d61b4c40e028a038f4c5c4da7f8891566ae129d61754266528e6fe03a700e0e5f859d2006b51022f7a4d0547b24c6a5343071fd4735c195f964dbda70b9c0885c9773340b67ecbf1b78bf54b78b6c8eae8f7fb53bfe02a688b613c6bf5398339cdfbb740b02cd7d4e1d103959c708bb2e4af0909244149e950aad3e980f4e5c09ba6cb107523fee65e845d8f373de26f40e29e2fd3fcc280865bbd730f18ce0c9fbc2690f75d3c1d3aa6a7076f1132c21d7eeac4ef221fdec21502aa4f0d3e1ec45c3310ab78193f23345bc6a2625760adaa5ea1bcfdceb58de826ec1df60caf40944e89c4afb12fc262f1b89b355988719aed44bcd804416ea3821336bc7d5f043c5ee0a995a7efc3974536f92224ebebf1aa2e880c68326ea41aa39b57ee2dd37fc67029542ea504652ac2f586efc99a1559fb2f61864973008a81879ccbd09b1296100d9f2e6c17793207d4ecdc22c18485aff5fdaee6319f96bf4f8cfccdd6d88440f8f20d4e0d13413bf6121fac716cb846212d2013cbe50d335280ec24cf985d605ac3a575aaef2946422399c5a5e127a25cb3c0caf602346e9ba1c679520fb0306f04e66bf91ffedfb8cb71fdf8165dafc3f4b6d8287ca2cf9f9ccde6919cf050ecb13b35f7a04a72b69452f8d3ce006a57e29a25d692d19957ac87101d766680dc5b6182ef5eb3bcfc545e4425e420aadb67bba8b5c22eb91243c74cffc99f90b04f3f2c4658de758a55ff3d2a578e58782e6bd8d9a5762def4e8738e39066805c72f158a1868ca2f76ebc3e92e6ff123934a8391446ccfea0c5526da9d3c430b106f0bd1d2970b9e21f3859880bc7f042e8437049ce0ad7528dafc31815ffe0a771be8d483306a42dd81e63188a994674b7c97e1470128045d8233383e5f690e5ff653cb140dc909c5fd0da34d4fbca65526eb4d404e61d5fdb51cca301af9014d55e6798162e5e1bf25333b132a04fb1159a8535760ef25800e7fea2ec6af0da07c5c7f926dedb8e61b9e778efbf72f3670ca92ed2968ce12c94ccbd1c733042171e1fe0440d8aa8580adf05fca532150a0d9eb43130ae38baa1ce12c2d3802f05aea9220fb2e6a196015bd7b5f48ada3e964464b51967afc4024a4d577160926c81471bc23c16fbfda0651a333edd384c29583fa9446b8c210d3f63d1f1206d54bb643e58cf27b606424200763ce8ddbe14fcc238607f2afb505fd2b0065067ec69101006bde28938febb5682e0d6b309f4f2af9f63553258f3decfc0a3b0d48c9667942a64fd75da2039f5e1ed1e2ab4069ef18ae5cf044930cd37571a80991c965a806a7cec807441e8083a4a6a8cf87730317300b85d88d238dc2decd0271e05ad00b90713afcea33ca497d65337e1a6e3a41d2764d0659c847a8120a0ca6546a32c9fae03ec15e05db211db400475264d47f2c43f09b9bcc94d67da30e524bd522563c7f87fbd7fd6a76f0ada70e4d40054d32ed598c17b236324a6b01b9103a13acc94de33224f3fe6231d65bd35d32fd8de119d08c65f19dbc079d099fae733e4baa326ce58344b1681da82b8d301c6e55056c9d44566880e4ea0509c6623e3d3d58d0339b3aa809a6dcbddae92dde839aa736a8db7e828b6bd21d04da14a1610838b41704f1c5a50b23768dcfe856f79d303a2c1886e7c7ea14a50f9933f30447a1d599cc0f15f725d178063cf7d27bdfd4acc484868ff70af0c273718948bc48838026dcdf86bf8a22afee55099a76953b2cacbfd4ade506201d05b794e13d50bcc2d8bc986d5baca9df02891f8f6fddd2f0b4ba675e340a688d07bb3382ab97e415353f445db980e7967441531b5cf2c76539b72c3b84b5d58c00ba51c39afcd0fff7d2ad49844230caf8ca41f20d54f62132fd0bf2691b7af500634211d50855a44395e670188ecd198fab3f1473cd88d872ba9066aaf7773509bc0ced95006058b94e423d97069c8d694af66af794bd29f2918b011fc5c9aa001569736bed033a1ce4bee002a972388146f4e41fece082e82cda7244c70d2904a4da532e59e296c34a834a64d372570eb2be9c9273f2b82dee912c73a0e78209033acb7b3ff5427876ea26947e5377f70288d5cd05b19ec4740723f273c3850473c4481cf17cc07da30b5e199a60edd7ca6937761f6dcfd780b7d15c563b500ae7fc4082d59585f2c3672400164b00257e13dd877c4290ebfdbb27f3759dc40dc16b648c8e29c2bd15529ffe978b11878f80971cd33a846d19bbf821b1301d0f false +check_ring_signature f55deab364e09828a7a90a8886bfca0cc62c903171c86b4bd55f4d981141e147 d0ec59c67c7329d660f97f726e562858f70611f72bc276cb3b4f0906de6e7fb7 29 331e8d6a5140affbbbc7261ea684de0c18474f0b09c5786bd50204f61397c67c ffee4c25ce789d2f8d9b60d31410876f79884d2b1aa1fee9fd2e5ada2f9e45ee 04a628bcddc3767eddebb97054ed35ad6a092fe8fd8dd99f071cd60bc50b24e0 efde5cf431f217c0134096575d133ee69d04e6369b81c6fa38da40a443535738 4b7f599ef3b0c282809c649c174862884518eecda69c59772cc41dd1a000818d 9d249db4836d6ab7625a823358078bf54af92e740c15f5b855d6755d94cb0d4f b3d6e12f6c3628cbcabb69ed57d05367ffc5217af393340aa1de37ee738c283c aaf5b7ebe349d7305952f1df3060e0365e7f99c5d8b272cc62ed134df82365b5 0432febe3a3143f2ac959f9aae21e290224e2b84bf042a96f381b1471df1b3a6 7cfe5a0d3fa78d2ce4c89c9051ee79bbc80e5ca548f37127e97088554c4e21d0 664d2fbabdcca74f3a0e1311a6752c45c878b023eb6694492a83acfdd6f1e0d3 3ed9310d322d2ea64701f5707c59e40d56fe819c99a8deb038197b1368c7b827 d616254acab2cfcbf66ab6b607a4744f34c97f44b8c772591eb1cc1deff55a13 e60a20ca491e47fd9cf1a27bf9f550e2d8d53c9862886516387c7046795de81b 7b40d0384b8ec4e9e3a6b4d27fab289870786c52fea9d91f1b5694ac60b49aa8 89b8d6b5b729418fe253cd92efd0235068a841bd184778e829e885db5bb63039 d4d21d69c2a6df8e2259d8f6a28b9a32ed5888be97a23733fa10eaec281588d9 57c649f38053ffcccb219f7ccd4c506768f2d76e1cdfa03f3d50afb238b55713 3c8dfc2f7e79a854cf54d4e8febfb40886c990802f6a0049f35c4157b7b82319 2fa0ed2d1b1161e1224c540d16b56f320f85d4caec9de67e81da88e6da78d11e e20358231495305c314ec1d98da476c4d5184a919d77c5fb8188414ee7022333 8684667928a93e8880c1e7910a0758e6c79295da708442d7e783daa49a51013d e1b520bae3caad591d3f5d0d65bd09a1a4126bd6012533a2a9bf34200698bdf8 ee00503ba19afbe43dd411b62ae8ab7dbd5a55280d1633de956c0456a716a462 04494e1745ba008e5cbfc82388edcfd084c315d2485b2218b5f8ffe3a473cda4 a0e32fd86fed35ccba466d1dcaa6e5df7f2f4506cf725999817a4bb7712deb42 2930222396f67e5979ba3357f30d518605f8dd4a16ea4b0c5ad1d4e39b86122e 7abc891ed50d72c9f2cac0050a75cf77d71706769a7252a679e840434b327e9a 420736af1ed2d85fc0941dfcecef6a31220b908e61b4f3dbaacb6d28c4fc2eda b503d19987a91e4bdb3b84e1a5fb7e34863c0846a0ef4d33cc096261b92c930f12cc12fc19964e0f497a58f8b73968627a18242eb537b39413140b841b2f340edc68ea29e32dd5069829517fb939c1d45b1691ac25123e3fb07cdbb541d6520e21ae79b2de3bb4acb4f3b09f77a235b6b78d3e1f8bc38e023418f93c0755bf03b88721c4631ff2c6a131f246942c7967fb7f5f7033d86315c2dba394cf798c017df86fc6518741d739dcf830e453d442cc1a5dbcb69d34ffa39cfbac6b8f1a00e632c5e27700b8207fb45d25d0a2369a3ae5fe97c921e730c8c6fde98d246d02fc75426bb0482c50e36acbe52565a7463d176a31abd3d0064e3afb7ebc59b6006edd9e23eb295819b5e4adff620ed41b270cdc138a0dd120bbe7d75206228306261182b0e2755690a9f813b6d9802072497fc81d13c868317630b40ab123d8027cf24f0c08132a68be9ee297777cfac939f6b894258927877295bbdf687c1f0553f3be758aeb842c7f02ce1d22440552f42fd527bf2528e1a386077c3661f90a35620a16b9dcea5fde044469a9d6f8b15d32c98b95e4121742cd7ccd1987d106249097622392b4a07cfdfcb80dedb5abbd6aa4d0e13d4f653e322fb8a8e23400aec18222222c6fd79be41edb0ace0e4d086feb822d4f9223f0162794f0a9df079c724301c9f0f0830d597d9023c7719fff3aed2a9dc690a2914c70a41321fd0e23e634a48e6ef479cc029833a074a95f554efe142a997d88c75c0ad699ab8801840083b5255b90a669105c909c3906fac2e381af3571c2fb0889e71b9f096501315eabbdc7f946fd9009f6efc440bebbc446df3f7b7f7f89501bad1b418af60473433436ef87630a6a238da3f59d3f7d2d5120b4d177649531385498dc2c0606b0235498e9def736f15d8eb28edacdab00a1b3275f486f65857b0faf49ca9d0b78f5b07958332868b812d4e26ae01d5dccb738568b00168713c261bfcd08460812c6b161e091a9472b40e821470840ecbf5a7bf70647522c22d740488d216601f71bd5ecc8d67117cc2870b3f60f841455c23a6706c37bbbca99ff9803be910e83aa109e708ef59ec51ee7949034bdc80b3858c438c2375c44596d9cd1d9c106eaf5839bf24e2e57e6bacdd91b24bdb385c4afe6b3efc657eccdb8d5edc56a065c90ba1d49d49eefcd846bb9c2d1d394b380402388de5d4e1928c350e17fb40b83e66d8836730639e470ff9f4684add73cbcd3d3df88b3f03818360c578849060e7b37b1f7ff053d0259c2ab084c9ba7b013d672d3f23e9116f43424429a760daea56250ec0f569a238b81867377c2d6b0deb79a37292b9a407904fb2aed14023d3564dd7327aa09a8de430ab6904d4ff82593ef523820ffc3ba4de36994a90bc42c64339247f8ea0de07cf46bad55f2a89508a42b99583f1012a136818a6d00cfb28f2c3cc4130ae1af87ddd22ecd37e9aa7d4dffe3b44981f3be2cd8ad08061c96c79588fc9da97feee655d6abb6c97d0c5b6635a3e8cea28322f6ba29e203f7ee9e6694ef555a20b1c9f353159bec6e837e418c8e52550e87731c9427330382565e5bf8d2dfe1497764db818c1245c5f506f965f40616535d6c44a3f2b4035d75c202c2585ac18df2b2922013ba9f4a8f4dc6fd817e8652e064e445ce2f0cc762725ab2164d8818f77ae6184dce8976a8a138dc0ec4a4a1e34fcb664050078b596a0b24bd49109e679ffb29d25a166ed7f311b84f5b4b802f907380c48604441410f1045e9e4a95ce6b7d8b2deb41585ec678b1ddfb575e3aa692ca1e850d567561a5c01e9d2dc5f280bd861e5b074b0b6bf5dd58c04ab317ab7fd1f24e08ea7f554746b081a0f90f8ce90d31c02036351f1df4cadd31fb6e0959e797940e5ecba93c80d2543fbaf228cb8ab1921a3bde4d71d6f4f7e48b6cf477472d170487ddc025c371d22bfe8704976b18a35f74167cc6ac0c0d67a7cb92f00358b0070b6f9f2e00d8d5baf4123cad4a18bb1960f20c2941ef6833963d92d5718f8b0813092b58f3246823cb75f485eb3bd28e705ba9cabe855c6f599109b238fe1a0228814f7ec5cd3b5bc90dec97c8db461f0fefca9756271d054d8f5ad705ce3a01972680ae877aff1a152127cb70600fcb0307b541ee5cc4606223e6587c4e760424f954ec83706d7b7bb1d5ad5c1c939b26ade7a4016848af72261d2c979053044744ad1c3ca9814dbc9f0602afcb09a0c280122d2e8bc1d894d0fe527b006a093582fe30e83a715dc70a68f58e49431ba62e9bc6a0651f27c669110143586c0d2cd43b170eff4271b025ed5ee1cecf52e199fa665916e30b2b886e2605a0bd0c8b6fdb48d51e59ca0ddcb2152e194add751f06e5ef81b070d6655c0b91ffbb0d75d0a18cb3b691b9d252e5badb14d5ba76f3eba1851367b3f098d64b848b7d02a91bc1cd5e48854a2f756dbd2df6cffa441911d8a9b10fe1283ce4480bb2a607aed4af6a7be332a8fb52d726193d8ef871d84a467b1d544cf62ca8f789d3bd073883596c591fe40b722b060074f3ef41ea501f0db63d90a9e97302a348baaa0d50f2098f08897608661cc82967cfc2e4f80f1654e1a8284870a7729de74b930d false +check_ring_signature f4266abac7c6a7ca68fff60f54a4633687c3d535e3d4d2bd02cc14d5e531670b 717d80d9159fe2fff34c7d069b40dc47ea915a2df30145e8a3edd8741a14c76e 4 f5c48d70edf1e51d74f3a4d9dccb2f3d7d0b3a7ab116e7376cb207e1bc25b400 1b660c28568f8236707808ae69930b2816634392eea323dbf3cb4883b5a675b7 7e3bbd85ec86535409c4170c2503adb6e848583697ef2d1deffa358e4dae90f1 863d1c4f1af2cb1f9a0ce7f8a0996193b474442a780a715e511f08ab610da1a2 85079022f4a973dd5628bd7894a995fdf0c87768a8c5c11ff5ad8062ea935f097a61fbc18c4cfa602a7510c904b87d545855527e70153c787720863a86d4ff00c8052038bda62036f369296ac3bf58dfadb36104d66bc0ec8a90f09956f97a0b7d3501a587c0674204313236d121d425e1448c22e25fa78367e3476e54e9f309d56824be78aa77541075eb129f9ab636bec98062635493fd669e2a17b020b70c35b774e76cc57a8ecd97aa2a563dfc7dba1f1ded0c3637655cf7c247fc19530567482495529a2239ff8fa763da299bcdf0d31ba3c262794c78d6167e7c695608aa3d113299e1d0547cc27fbc419babcd5296962120478c98569e21f8cccdd602 false +check_ring_signature b666a7a7699756eaebc81c51d7ea28668c05dfb806a42f78acb9ac396e8f0a2b 884c13c22f06851c31d520cb3eea91605f180ecb697bd9db1e4060c3c2d6a156 4 6c680249ef1356dd17a0bba52fb43b2b54c87e34876c54b82ad2648e5c8ff807 beb205d1844eb0c75629ab03427375901793608b9483997af5e02493dbffd801 5ebf6f697e865c6c6bc096a1cb6af5baaf03b0b3382ab1837e9cd15d6449f908 4a477eb368cad57f7cda2677e79ac882b7a40ab0accd250de6175fc0d78dc347 2a2bec358117de906a563542b2276e489565b688b76519366ec8048d65c3e005fa4a2dd461bf9789208c3b0c82e6387d8b8853a61061bdc6647bc4c670d64d02f5a2d8d58801b5f9c87fc3c3967f2f9a1de8d6f03a7b4cdbb1bc3878b0c9a501eccb76aa58ddd1a3e228d64682174824a2287bca68665673e40bbf4078c66c040f34a79e8b8544975777da329fd548b6ec54140ae1e67d4c1a5cd0e31b9456013f6c7b2def08e96808166025a92ac721e2da9c67efdaeebd8c8719fad91a410e8ea55a242456986dc1caa38cde012aa9939b29dcb21b03331b19d1434d6a200a34566e15186885d226f7f85da99c4abca86114adace9ff6b307f95c62941f502 true +check_ring_signature 9d10a1e8c35c71fe6ea997cc4cf233b7fe1725d367542b3bc8e955caee54515a aa46af9c5b8d5c917536b4d9c6f8108ad0982dd9eebc738fb24e1919e119e856 159 9fdebf85659ba213cd66c9c339d39c5a94d84a33886ca7abfefa213a9fcf38f1 414c8e70e27a3de220140450abe002c5ec22ee19aa40a2719a0c9fc232e2c567 8a58dc66f7e20bc848026665e91b14003faa7e303db1f0dfaab757e21d8b00bb 1b949d7cecd370b3ab77c2cf23826058ed7f84215bf94c70ae075aad82d6e422 eed7d5ad6936cd0a34634647e83a7644cff3074d8c0e5ce8fd9238446bb74422 2191141e98fe49e9c1fdafbec4bf2509c9f87fd6b3ddd0c2c08e12122968c88e ae988886098bac5af68c198cf65f46ce87aeb8005e4d5ee29d23044a3cf0411c a628f416f3b695b693290cbd2e941be4d61d4812e1cc5b3d8c825946698d217e 134ac2099c7ceca29412454a743e7d880fb75dadc2df5c8b7c6106c20fcd0c0e 082925e03a8d481eafacf50371b03ec0f3450e278303a7902f581a8ed8c6a2d1 bfcb7bad5e5fac553c6d799de1d0e69eaf01aff3300d243fb778da54d8a34a01 f09ea8124fd1de25752dc5ae4d5b0c6b12614afb14807c41810b8fc661dc563c 00da540e8e3d87cabf704d2bc8d38763e8cd2e8fc3927dd5743ceaeeb0978968 63cf90f1142d2d1c2c2c1e9c0826b6f178da6f69619976b21656fdfb8a6c2bb7 5cbcf60d76ade668ac58d9f3cac1426d5f55b76006892de2f8bbdf494ca80ba9 79aa9fe369b7b078bc7401ad562145db84fbfec46d0e59a21e4eefef67d3e578 0e0ae388f0f623560eb4a0c5774fa3b7c711731fce2d94349aa577372943951d d6125755ad3336468239335f789bcd0182864d0b8f868959ddc10c333d979769 2f1deff326ad233b87da76618206bd01c44f51ea372255d44825692899d16128 bb40b4ca9b844a135aa8c332be51ab624af849efe850bb59ebef1dfdb2d997a4 f184870f54eabfeafab4a5f2ff8848dc3beec0c02b7c63d4965560e797e8710e 71f19bf482d5dcb05072326ae8e87352770fcc06f1053bd3b58b9313f81292b5 e1fea7e32445611d22061100e89d0e4bffb2652d5af04bc6da4fb3f127fcc49d 14e2dd72d605b80d136865b08e8fc8cc4260aa8b624265d1a15cb95c758103da 7f7a24d0ed614f7bdb1ba306160ff8c8801b631b057aea2f54a4b89dd1e93756 2b9c451067dc5eb61599a0ef32966f857dde073aa5c92342fef8dd7fd29bb7fa 1c02f62d81d6f5ac8bcf07ff2ed5e34b736819b5fc51eb085a13211878c93b91 387f79525c68d3118a3f74936ea2c2684d9a004cdf45060f10c55e40bb80a1c9 e9c6bb81dc16daf2280b6c84ffb3043cceccbdd126011f0316511346774594b4 b785b013ce5e4c614e3340628c0644dd7869a43549e0b721b8653b0d9e7cd495 ca23eeaa52b1b1c389c1772bdf9971b00d9302d93ebe94d980e702b50df9d901 4fb97236c48a662c91249d5336975d5286a4c9febf2c53fb099e283ce0c59200 0f34327a7a13f611aa30c3fa61b399c4d81e4c238f3af2bc2cb7a82fea7c14d5 0306cd3d35c4b4ff55b893c141dce94766de132044ec53e60615af2104620ff5 71f778e0dfcd5498d64c60d45f8673778a434f61c8c2ae2daf57c6c476919cdf 22d7dc0777fdc4ae7762606150a1d960c484f080a0fbb44ae8bec0f78854069c f50fa6591836c09fd71cfe1070934eee3d8cd7c22dc1b05246990bbd355cd957 457ba92e746aabcf1d8165aa85ae475af79d979e028251bdf8f8dbf98e60ae50 17751d0d820529f4a2530d0037b398de6beef9b511acc64dd2b619e63b3a5905 d1c0ae83251fffddfe3a9fccdadba9991c70e6342d618b304a6b7632dd1771ad 8acee7e6be21aaf0a56bf92ae2135efec9c3bab99909aa31dfe2166a8ad5282c f7bab68348027bbf774f9f73520a40773e07606ccda0e55f939e41f85ffb2455 99edb9ee657571db4743787bd1ff54e9acda9879b0b04c5982964c3fb4b9e02b 66dec8eb65635ae636492f8592212688e4a6fcd1a6526cd3b25021be63494ee2 51189f5928a4e86e4dbe1b0e0fba72e5a2fc93568d3fc587b7147e007ebd4640 39e8b20b84c546f7182a54e03e429148c537a507ef5f0045fba6e55d9bc00e78 1eb12af3bb74c2b0ce2905b2b082cc96d1820faa0fee2995f3548001f9193b23 90fdbfba5ea15ffbe0b3420a8ffff535de251fc679fa13e40e00a13a97fadcb3 fc1d0449b3d288b0cfa7e6e697a0938d2f8dfe8e56b1a90701a7e27ae0a8c3c2 5645477f4d75b0985e3bf02d5ad57f0b6da4779a54a8259bae488793d405e68b 61a78d1c6f5cace4726ebdf3ea35af2e9791b0d3f387ff563c2d106e4c61c08b 253f1c491dbde3204a46f28aafd336b3471fc0aaaea51a5bc7dffb0ddc17d167 c51eb11481c24be5180c7eb43f89c28bcc8bbf8e019610e3204462fa8d8b3c94 56f470c2939e6b3cff6c1f18297d790bfe82143d22fd5fe8bb918e503e59bf6c e6a5caa6d240af08b4f0c6bdbb46f386165bd67d6e3b71147210dbb9479604fd 845436683a2098f908dff243fb751879acf5392a325d6efd7be0959893781ce8 87c29ec785f682eea2a3fd35661499fe2c75741fc7408b773438aac0b2e74c28 32bc5206bad3fccacb52c73826fed1e55c0e8936d03a60330f0c4099381f2572 08cef127d888dcf8d2fd31178fa234ee8d74e2324cbd05de7915b4c8759d8368 e3281ab7a222944e72e1bf0b1e08e5a4cac217b0eec32ec7277a940cc1f5ffca e23a649ead1a83cf2a209e70934c966343ca11b6bec99fe0b36709c87bb7f2e1 f7a55f9720a695c25fdccfb133eb44b6bd0ba81f1a377c5bd348783aa883ec89 2ad42a2ee2955b1a31a1847572be44ef4990fb3e911de39498e2654877af6dfc 63f22a22eb4349b33b881e03c3ab0aa5b22e6e8cefa38d0f38a55b0195bcc46c d55e60ef0c4bdc3bb25b8d85936b119b5fb39ffa36ab8104f18fb879727d8862 a282f7800663cd04496cdf2c19becdef2f235a6a43d860937efc2c2461a47393 1d74df543271d44aa0c6af0035e9fb3806379029fc2e3b3787000f2fe6582aae 9e25b219a6023cc6cf777b300650009dd6265f31f0a4bbb440dcc0583b9a7cf6 2b8691167ed7112f724c741e5370d9feddffb97b870e1618aae7cf0a414a69f6 e16a03dce3d7a9ece4bafe7c866bf7ce5188e2477e66460b4411bf840d597a51 a12e7652c43b109735c65d2a9961e0c0732d72d7ba702221a45c5d368afdc501 8d400d4c15519cc9b9ba09667bb444fd3df95ffef83094a619d2d82c4b38ffb9 0fc5881975c16a2525ec2864bfe453680c215ded5ce8bf2f17fe4098a2e301c7 19d3c885fa9eb32ae2c88c7807c30c18f3052f94ee277b6b4de8e5dc56c2883e 66f9c1c9024c2c2c4b6b549f685b60146253b9b4ae53026bdaa0887cc1db6a96 cb7cd615a5e0e53e8ecb335f4222d11a72ae992345f4d27abf14d1e54f2dc78f 86ca90dbefe5662fee62143cc983f39782528a84bbb7a651d7cce0a6ae6eeaab 7648dcecef70dc77c86b750b605742028b4c7698cf44eb5845e8c0e943c3961e a05e97f6d0267f661f2541ade2ab2f613e51e61ed21b12781f13700ef79bdb41 15823b4eb669891dc227298d3258f454566fd9f6efcf7247187d094675e237c4 6c4b9eed31e64ee45fa8f4add251d5e69a748470338c6223f64a59cfdfa546d8 ad304a823081e6d3a85aff3246e3720899c2eea87ca46414bdff0d08d37b1a6a c14dbaa3c52f6171fe28d5dd476fb785cae0199637092ff856eedcbdbeb13f44 fc96b1d9781100e66974c68a131659cc7b2a3ff6eec7b20d1d245a2c041619dc c22799a2ae96c933215b2cab97ef38ba4c8402945e3f17e6b3876a396567c3c5 e9cabc88f014936a557875465b102d0696ada6120b093cbd2c725903001f407a 57b95aafd41cadad896692a925bfd50d81f90c619419bfc9b6dd1158e9b0f95b 22a88711465775ce9ad83415ea6e1becfef48bc53cf959b4078e1c4cb2ee58ca aa98518f517c7c46a9876c2bb1e6fd9ae22390eee8d47ddfab364ac8788b83aa 0e1081cc2d5d9da8cd0269b0726a2dc48076c137ab33736398fde34d78a48057 60f1efdbc8b2cc039c463ecde98aa38bc30ad6fa8df63fd0528a79e1beb33edc 1d6dbbe2d0409ed234c96ef9f9ae00141fb3e2f2a6affe798a7aac2a8f675432 47e8b67ddd97ad00a14b2ef344145706d2b49cf940d339f87481dd12c59b4bf9 15d4e3c1fd66e183bbe1c43d42eb307c49df6d7136c2935fc84b3405a36a30eb cd470f0a58f89ff717b69fe89c15ccf0d96a4e77c5a53a194378aaf0d88d6552 c9168dc83e3e0a8abb11505a1038c1e0575c1ccf0852bf197ec6509ce8680a16 f5c2017546d2fbd847827a0f60a02c27eeed02da2d6234eed5493da9cf4e7e51 463283b5cde60af83d4d2eb9d09e3a283a511817f4ae564a5fa01eb2d92b800a 994fef34583afcc201667511b0b45f298eb33081b82b8d17a2d45a18273d4a0e b14b8bbc6b964c15652cb15752ae25f04772c070178e36140bd7f3f5b488bdb8 51ae514e0c597b231f3cf45182579815214ab55209c1c741f8abf1bd50aa2d3b cb39e3da365ec120d541e4e631ee85c0c02589a9b725aec69bf408ab00f047f7 cbc0ea69d1f6d48911f39151e70dd312e12a1fe4ecbf024d0652cf610f0e1cf9 e05873570f1b560397859835e52c4e1004d8c3ae3d3c0104fd23a702a4437edf 74cd9dac1b3b08c82d355a2e68361b03a417cce9e5ef9de2023230196f9b371c 88bfc1b32f5463abdc7e109ddddfbe3f58674f69d4d6caf8e63764628e5482fc 0a80d99dff2fa010f038adfc6d2429d8f7caa7f6fdb537b2adcc820ae22bd1f4 642c90324abc851467dd035856ccbef541c944baf2297442d35a255d097e2274 33ea5e140710ae65aaeb32b37411a3ab8c366206765dfcf361a4ab5864990741 051278fa2725a78a3f9adea7549d8a8b787dbbfd0624a0483402719de1b0fb72 be6a7d6ad76bc98671f7901aaa391c3924ae16f1080e955912fe8042f1997c45 cce113ada2656314f6e1088bf6360d0ec0da8e60c1dbcb61cc293ee471d7bc22 f886f60fa87844b1fba970ec4c249abdf92fe6fc16f1e074ac7faefa84c94a49 1f6d94844800029da93a0a8da2d065b4da77357c7924b38c4ff7fbde4a8a4a15 e3dd8019351fef36b3f905d7086f53089a1e9e44985122bf4f1b193596225fe7 f3153d2378575edc7bd5222ae941b51ef72e4a006c4812cda1a6d53d18f49c43 e0d5e26a982e74ed14ab010fe59467ff4ab6573f47d9c72e1a0bebeead9be5d2 f60894ee8069df794518a236f3eefb9c98344f6447c050788513f505842d4df2 88450b33290e9f0c2e64db03db9d74d4b52b6f9ad901d2ece052231516570dbd b596c0b2917dfc0ade6e586dbd5197c19ff106892a4c24c0a699b557716995d6 122f7685918b8f4ab008a464d3ec7aeafb4f7ddcfcbcbb5e63d2c1a29d740671 cc6a06e29fbff828e1a57d70bb2fb6d6c6ecee07214521a7d26527237b373827 6d1993d7ad928cb04840ad77f204de626553f72779b9e34ab040bf3b8b0656f8 18ed3e5a12cf5a61e4fc1f8d888a419e637c797f330797c1aafac3f7db3ddb71 65d70c2cc25cb0d1c8a3ea9bd7d9aa55a1b4df4d2257f381c69eb190732fb679 28cd711f9f5f87749342ff75aa757e19d7c385b9dd63632fd55f01bf144d23f8 a53e872b2704bc82926cdf06c81db805ae029659ff38f1a8dacbf39e729159d2 4e76d80883103bd4651ca2c4ef9666aeb32703edc5496e0506b3d244244c553d 9d916d0f114e130f377955608742ef6619bade2b6fefe467cbc918eefcb86109 40f8da6335ab301724cdfeb0960cbb4c52daad0aec88fbf84bf3d09ad3b72269 3cd4b526509c63d74d9f81892be8d86635edf93286ce6d83ee1d918ff7d47710 6a99d3f7bdff9eaebc69135b2697bafb3f448ba5d9d7e967859b3fb0dce823a0 36f5566a74b6998314b2dbac38c5bc8f0af189ec5f359d04e61083582ac52809 d966ee4ac4574625315c8e1ec9c20c50a10651c8621b036344fa41967e210abc 5fe892cc16c8f6e5754f0f13d0be2a8bc1ce51b0363d1e377644c11714771c82 389ee07743f90007f11381da0c7ea0c325d8900e81021cd0040cb6211e5f0eea f9217d0fd777c0269783b6a29358cd4a7db10625488f3b489996b49790120bea 73c83426150a6b68d7a4ec51ca29b616ac04954299319557319697c74d214d75 8305e2385292c6369da24d2e083652ae2fa45d5bdcafa05e38d263b155cc359f 707f10a67512faa39bdece9ac9c8d7c7ddda1b23630ae41207054f6205dfc020 8df035497842446ec23b9617828985493f7bb55c9fbb56681f95ea8d2e5adb04 c37c3549b507ef2ecc408d08bfe3114ece9b84e82500ab153d6209238421fe91 0fa614294c00729f17c2b98842c0bd4038035db503ef884d756574d34b55d975 bbfa70e56a65c74ab7336db6866975d175d08f38cff2203beafb2ab5fe17cc91 aa2c213c0dbee6edd5d07f3fe3ac221a29d4f02fe5c7fd3be8e5c0a15bc13575 b5a8fde792ab5571bdc776034f90755e362268e044aa9971891b331eacffa1bc 9532ab936b09f8f9134d740fd988fe0a1419cae50c1eb6bf03a89852b5e0cc9f 86377faa0b3f2218519fcd6592446473ecd8451e61dade71beca48f219febda2 355554e122dfc1b7bea2a15181826761ef7a452a7aa6a82bf648acb2fdd0c59d b309cb496b4b6362c724ecfc222c0aa517bd6025d71e090d1efb5374c679f4c7 abb12015ba4b73af06e83107584219b5624d65bdcb2d800f8930a2b2b49e7ff0 4ad6df84c47e005ea46b2b5b9299210f2f8988b269eed47e344c3755d345b2cf a575dda3dd0455bc5e236a4e3a7e4da26751154a1dbbd81a59a0e126a207d6ba b99b045f341386d69eb0008953c81a30dcda4b447483943348366ddaad1aa69a c727f19c33460b78b87bcb719cb91d6cf180ff53ab56502aff4709e0e88a1131 000b4d2aa05944681c4987960f3aac30b4ee9998db95e7d5e7229f8c7732365b 94c8b88b77e95c613e7802be31b0934a7d6b0372d3a8fa4c9fd6d1d7d907362a 204da677eef0088ec403001bc7674546566d4df0ccde40d950de1d93142af632 70f7b8f9db741042e90d0e51ee2a71b24440b3ed67ea67b8654f78df688fe6cb f4f2b7a91b787eb6d31480271add240b6c309cace45a5aef34eab83fc04a6004dfe2ce7152ba35ab589968c65ed84dc986fd6ad6ec7bbb3d71129f882189640f1d98bc512482486b9d3ec796541a58167b0ff831c307eff230ab742a9293fb0bbeebabc556ea72a27546c8ea9d997887ddd5c20769b655d1417f07cb49dbe009cd06b5a8196234ed998966ae2edd3e672aeebd21b2dee412bca86d37fa93b10e36157b3dfb463ce0e880dd52db839549d6a7096ec150639b0f2670f900494102f0e854e18792254312d87a88e451fd7fd496c6ed84bb723362809a7f2a6ffd0681823bc9224d214256e982a6d4e9c204cabdb927c57ca524c28ee3ddd3ed7206d313ada0f46325a0d15e379083ee1f57ca14c45cc075ac4534a3a648a684700c72adc7252b1991bde4ce652184f98b4ef060917762b35aa738b7cc6e0b2de70dcaff635da4cb8ffa51e5b14a9f0c9dc2b4467225163a90fe968045af46d0860266ae41f4433b6b42e51d965c3c6a05b748c1751b3c2a1294da59ad8eea431206714f9cc0fba6e81fff5ca1945ad54e62b31c0c4ed8dda041935f17f226b07e0342a05863f0c948bd00504f5e494d49f57a4873d5f71539aad717b82fba62d903924f9fa4f4a01bdeec4bae10bb412dc396c6d489e0e9cab601efaa55f1d0490860baa99a9cc99df0d537f42fb2d447facefba44e92844f07c8192f64e1c9c501808dbfb0b96cad3a2b6a293a83616a47f69ee412544c475f7ef9f6c927f4580ceb9271f79a751448dfca1c94d93b917534181a587dc45ec22f65b58d3df1670b9062ca151bdadb27a8773868ee23b3ddf2b6f07491f678e00a9a6626b7c26b0d227b46273ef2ba04b906237992fd0833ced88c14c8dcc2d06bf4e5ddf2721a0dd0dd959a0ae5c4dd357e0ec71f4d0f2fe344459c24c36bec81fc185ad216a30a0bff10380001775b16f43725375426ed48b4315d2e72598ab623ce8c5f0bd70cc59738251419cb64cae46ecccea836a47829302a74a6246b8f76f8ac5c44c70ceb04fd28a43c81a1de94eb73af7462abe4bbf4b7cab68b3793a9a4ac39a8b7062ecf217c446ce0430006473a2de0193249a0d28cd1fedd459e28aa0af8c35d00b000314b3459481811d034d8e21053630da9a14dcad89f0e700962408b63bd024708b71fd1392307a9328f81eed46ead2a8cb65364e6e309719d6865a736bf081f2b2d436580c5e7d629fd27f24158305fa98756c39388a3eac51c0d9c28ec0abf00f30a26fad06eccbd71922ba36cba9044823b802242d46fb1726db71a10082b921d7396d0c5814af49aef7c7edd3d8d908156431dcdd44be649d0d518e40b5834a2e010276fca70f6cb8ec0b7f51d06dcf5b57e0f87a09c922333f3b28603cd7e5a01a3255cc7374af7d115485bbf8492583992f0acfb29fa7b3fb1063b04e85d0c7a342697cba5c4eb88acdb63e848645ca134d0d25b948a79e7d0b3fa0b0093cf85999680206a6fc7f1629368c797dd208c7c9ef8fd6ccc72fd506b5f0366e008428508df8f31e96462981752c65f8ede16fedfd51ccbf37bdb5eb2d20be790fddd924db6f21b260c4ee39a5807931262c375779740b6bbf830282117035838b6db411a87b078dec684421baa7329209bbbfd4b64547ff100b2a608180057e2078bee32a041e3729cb4b6eb04f0d39bb0697292633cf9bb68a338ebea051f63c847e944fa107344c0b4eabc901a64157fb0eeff58d0e2301688dfd87108219a13c30b0e5dd813bd3ff73a1fb819d67cf2e4ae87eae94aa0af5e161fad0263136850243eb7948dbf4008c9f56bd044541bb3220999be6d6b84a893f9120258151553db8c576a6839235134a01fbd1b04d29ac56f3d0d0dbbb5923295210ac601ebdb368682f193d76fc527910a3a8e5a6594c7554dd0888c184eb3ceba0ced06c041bc38097cec1f6412b58ecc7ebf8954e7dcbac252926be85b73b1c60d11903bd803eb79fc9a687271992479a9faaa0b9f4ccb62c9d3843c21b7a01302719acbd2515a170d879773a6bb10408fe8d171c0ca06291e037106c485c5a40fe3341b5d93f9017615f65acf7cacc5345eb486e24ce51ded68ad211f31f26a0123c92683d8d7608c68d9d8758a519c0035210880c468d3a74054e73c56ccf50cb0b0d5802ca0aac445f1a7e2f21c417748f60b92426023e7d7668a5409ba3a094c52898819c84a9fb134910c3f0ca8315a3e6e333982a8270d0e87b9ab59010d067e0136a6085b935bb5166750e61a4d56bdc0d8902f2cec3b04df862204f208355c7e6206ef0bff838f7595d5e1905cb5eaea336df488df5d323421452cde0884c68fd4816fc462925f60595f55ada8a210d04718051865a01350fc532f130c7e84a387174e01a4f96558b80d83d068555cf77cf70e41bfb005d8083964ff06643609143e11b6ac7a72b22f98ddfec14d8724b4fe667b5c104f9ab8deb383020888237e440099ca343b64604f59c63aea1c81b13737ad398a2e98ec36b755041bb0c8d95a70acff7dd1304beaa29500d225953874da7ee706d22c0b8592f20760750ed84a543425733b2e6bd7b337a35cd446cf4ad693ec6140817cfc26a200adc24c542ead48e580db7fd2c5623676e73b42bdc713ed1536968e0730e7140b5788e4c5c60dc2f9d85249777e378fb1c8f6605bf19306113281f2738246c407a0c971bc614d0d0ee8844b28ad0d82f67d284fcf3181d0ada6ee17fe67be2f030de1fb514ab1d21a100e2646de806a66bea96c3a74e7218970f0f20c62d8cb0fb67d8384a8a654cb965b70467c203cec179868da943ca52f3898a16774565b00c94065c47d69beea4daaf7b78ca2cbffaa5e35d9c4660cb798c370b1236cb20b174b239ebe4fb619d6d90a81687f48dedb558ccfdda25bd1effd40b8e753880b5e63474e11d7edac7f9edf3d55f9aa9d47b6fd53f7d31f3c94d05dba85892f001cf76a514cacba52015e23e120cc29f8f9d34f49766f5362714ccf9c552d4601560d0bae7888edeb7d672f595b0a5928b70aa6a82c7d119ff2f431b3665e000588fd294d882636b19f5d6979bad6cf186d0acb8a708a1783ad4ee0f60dd25809cb3538e34fd8dd84b5b7a9f86c8e46661990d017b1641a0e730d390794a36f0e32c68de8fb13130cf9ef3ffac9b66991c4158d9f98efc0dad6741e3021d3ee0fd9996fae7085aec3e8c83c8b60690825756ac120da8b6ebb5fa750e06750280aae70fd6bf6acda81c3049a9634c80d6fbf1878ae4556d4e64053e6e925dd170f793ea07e46c8c82e2dbd14b0ec13038151985f71b58415d06b6697588e71c1072c1194f1f5243d26549f4980efe60d8ad0f5a08844efde9cab1a1fe69ec3f30c302a84398237696ac313bd16ae14449ea389e214cb940eb8b272598b22459b0dfa30d517d06c7c010ac114c358a025b61d8f56b7a349074877a54fb8069192088218c26a7d3b2cce676c8c019f21e84c8920ded2de2178d96ec148207b604202fad53f320fcc16d03ac64d5e78bc213e1c3fedc25b34f58a13e1a873736b160b4cb793f7b0f7fb8d174b52a8ab651ff3cbb0dfe02c36a2669bb54e23a71e2500866004473fa4fd7dbc733204c796d595574e71fad9fbb8d953d5e9654ee1210bab3cfd69374b810541880d2bb687d4bb8bf3652788bd62662a37fa2392c0830007ecc97543a3774124662e8615a7bf9315efb1965fa15e98393cb907b79aee0b8f1824f30b7cad47dde17fff26a9b85830f7c181dbb256c6148b4f44259aae0c6238f2f99ed1a41effa64434a15df8031cccf57bcc1609ed4ba8b8cc11ba020ede3fd338d1f78b2936ebedb12ba293c37f7df8fd189ce8c74adc4b096611f10f6b74130f44b2745e68489895a0fb8a272f3f3369d4a6e02ac7662805301eaa093b7107a7805363b39f2c70d2478e094a0a681e1e72ae06e84b6bba57b6d55e0db7d11d2ed687f605388ba9a84ffa41a02a42aedd315712599ef76c3c71a98709a07079ba4632add2dcc9878b56eadba6ce843d4cc28365c5d6aaaf7369749d076c7af2c4bb7259343e74263ab341dca654e831dc7096e425ff724f4376a0b4015e73120d47cf8f58b3c77e4621078bbb1f70b581641e7f41adfff82b59b3310dcae92b46481038260be47135844eb1a83f7c7eb28c0693a809fe938813ac980e02477b89f92edefdd693e520bb522e486d63116f27ca2a11a627749b17328808fd433c31ce9fa63f6d4fb0408402d43a893fda904701a45391d7e3ba953dc30c07a60d78d3d74dafec4664410b91ae47c40e9130f7b1d4f70301d2170786550dbda1c05406f75cca85eec399d9f0e477901597aa46a489114e43a6904e3c4b0d847968683fa1e1605c18933ae6126374c6d57c2260598e454f6eb1d16b4f6c0f3f9bfc4c927ff66875601d5c036dfbc135a5fd1845d74608e0e67b990497b8050f7318b24eb6da58e87b84b90ad53e141c5830c4f0a16f576f7691a237379303e1e88d884ff74631d8c1cc60b1724e2809789ad13c8d3e0cf033f4460fd4d40c5c909eabd9cca98794633705e2918c027509633533d71749710f0ef7a53aa00203233bf996fd813a85d0c8291d350fc8afc951702412b79db896acb2e423820b6fd1a1581b0aeb16ec33df9e2c3994cf57950298bd6b2f9fca7daeadfd3d2f0dce40bd73c75bbab32f0a644ab915045fc9d8eabf9390ac74fd1543c0f2b69b08ecf59cd5e5baeb5f63e10fe6b86f81169aa9c0f48584ae5730b7ed3b110aaf094f60bb517c4799141ea46a8dbece70bb20a6d79b7155b91309e962d2e98f9f0d81f710c456e5c768c25d47b4ca161211e4192ce2e17e7853d02b26b748049508e8ef96ed011081b65995757b2f0821b687c7c4a51ed2fb8ce26159ff2cbec6091f8a019c6dc55ab0d52d7d8cddc2c9a64fac0ab7e06001e2d03177d49f6254085a7d89f823d3e4a40f6859ce7866fcdf20c472f5211fb492eed6ef8f17f57f01de63ac6c251bc888d900b71cf21c14378312830441ab68a61b26e07dd025250b50f77fcd43a5e971296272bcf18000420082e70e8173bde035269bba58c18f0a7e86704f2e83137f57d75a99d9fb1867f4947b007ad258430f999e2f4805ab09fd1237bf6f245ef853b60f8abc45e7ab757f6085c494130031d113419c8bce056b8f2cefc23d83db718fbc58b76c8b37db45e6e5dfadd3cc0bb6fbc693f3880842fa09bb4cf3ad061ecc9721dc276b2aa8c1831c85c78284b526344ffb4bd900d782e8200b8e12ec953f234066c32b6d884c57cf1cca5210b8d569c2fb7a600c4100a3efbeccae298c1cbe2c53e0dda4ad5227abd6ca7ebf59b63f1457d9980b88fe61d048e3b99941c2effd22bce63ad1ff1de847f5bcbf2f54f82abf953800506bb1726b3904a14fc0b47b4833e7197f06c212cbcca7e4483530ef3c8b310aa45fcc8cb8bafe49dd590ae27953d06d3e4f4de47bcf719aa53e28fa5628e2046365bc54b0eaaea1c29127b7bd16b477fe334e0d9a228c77bc98f31c20279900902d9635a7ced9dc38d8626a66f459e4b0a5ebe26d204420feaeb0a977eeef00ff56645f2f36582d5e332c0bac2e7b2746388c21c5039d41583cc6b7f5af440b963260a16135cdcb3ec266d6e105cf4d81cf29a34c337fa3a828b2511f116a0a97f8be4dea49dc60e56900ba552465c6789f523fcddc8aa07208deb842bf920d5899b15368729970239363385389cd792af8dfe508514fb24430bcf81f5759040e113f6f1e9e42158a645afb3aefae0c2e43f224321634e1c5b5c1073612320bfbe81439dd33d0c507fbd0046cd62bd0093744b6d35a5fa98904866e6a5d880b2087d7682cf23e4e5972e29f4cff3fc1a037fe8da23497a434c41e8ce42f8908cc3f92c7bc2669ca7859f1f9e3dde25dd667578fa0ddb0745bbd615af190e805ad9c3a344de53dfa5f552ee015030a99225e0084e54574dc7092dc66f147d206a6739b56111a0b60807696ca430a0793d8d85d1c26ec714cd7feddd74393870ea80ff7a80e945c3274356ccd37a509e50939e2ee1c06ff1d9c7bd65d37295d0ed3c7a36fe61f4e8e30b353bca2de298f1c8388a8c3a6f24edc255a345e97320d886ef30c7782bf6bf2fcc9d5eba4b6938573aca50f911e14c63706134aa2770c0ce56255b25f7f6af1292c02d7b19a303e0b391459c35d99cb17ec7e586c8f0993d22eb01a3468dc0c6bd2edf6d75df46a652b9440a4a17775e7d1726576ed0a00a37dad71b775e3310ff15eb5793dab13f7daa18adfab2d8e65ed301b7e8e0dc534bf134774542da20722d49f26ac4eab2c83200e867792e24b22d98b4ee006327c4891c3a24897acd1061a8a903d25162b00b83018297f9c18c212c4e7d90a3ec04ac04b51fb08bf8d1677012483e50140008b1c27e417fae4f2310578be089ba678a5f8d3741e577f8c46ff055b69bfd0b20d978e6ae872863a4c8757490f0ce4286afbfa5f6a71fcd1790ab3955882c790f82713dda395976c885e96a2037f826410c9c9cb27cb7d496b427d9545e5ce3a4e1acab23f01509269f2a1770c6e576bc23c6d8e4f570bc733d29c7d41c8afcf82c0d7427759ec2eaa5600b00e61f847d5d475ed4305a353528760b7c495aaf473c7d99d406a979a28c0a73f0e461d6ff8e715d20f64d9f91e5869a621a784151746de4f2118134b8448e6c80b99b9bd70daa7c347f19bef93a365d2d83a22917930b3dab435e9c5438d68be0f666a6c5eb99e8aa2c428506ebb949766ac24145a1a6fa0c2f7659a7106f73b018502da9c262188eac58b9f407a61b3f5f2485e85ec46c69e401fd2b65385720615713c0ba94f8045ab5c1432f2638a2e1e51a6f90b43a772bd4f927b02876300f7581e86fae8e54a692cafa9a532d60cad138336b89679b5af12139ee09f75018e1042c658706c6a77774be0609d7f45f4d78f358c1976e09d068b7849569b0b691301d5554f7e0541c76f1b2c947ec4309a2fa824fdf7a7256fcb24dc1a1005fa3b73cdb9dcc83578221840d4bd88555479961763065f6dd044040b3104e801d4b94270e6bbd5895eaa1a1780f19e31d9c19d0e217fac73e2e664cbb24c1a00e4abce90120eec333b4eeaeac19372520220d2c01d27a887fa5725d38d04f1039556d5ad4e2bf0badd4b02a1197fba39b700099e4dbb28a3eeca2ecce89f310484928eff80d2ed3ad698c36bae8233493d04349d9d53299920045a7833165300b161eb39f707ef398a3bd80dd65ef8f5b5a2c70d8477f2582452f0a937ba190ce928ac9595e6fd792dc14885334e0f7f2a3e4896b51fad674aab3a1fb18f8b0470cdb57b87e412dca79ed93c7837eeb49a6d874c5109cb81b742a81784a54e06abe8a2a6387fab456b817c13cdb91b0e15ec72c858d3b8537084219df9be1e0a56deef9fcfa806fe2a915933befa0e64b27de7ebf42035b5b6d371e1e3fde10b667fd5135ac09971f080d5c84e5906beadaf1ed8f621cff496c3cad758c8c50b5e84728d4208db3b8537433da6b5c2238cfa3bed2710158219a42a28fef0c102aebeb57447cccbbdaa25fdf5660c138ad4b272b1254f0721624d212a778b900bec6b45d5dc5fe0ed805b8325dfb7949a1b550c83563eced89500347a1384bc02d26158eb3c878c159245904cbe835d698fd55c1e41b38520522fc211cc509c0e645002517f5a00ed94216a54b3b36712464e9460c8f2b131e3389047d11b0d0ae47d157f812914de7a8523ad87fe49780fb61c8f34286f2cc5f99c33b4466c02a30f730c8dca8d10fda0a9189ff23f9f90d5744717c524cb550e83faf6fff70604d437c04cfa1f314fdf0bdbaf87eb11230cab3515b4accd811a808e9cf40b0de058e78fa8ceda17f6d8ce1155916277da48aa513d5972ad72b1e40d8b9bda0dada6c3dc221f974e03ea7231587bc00528a703067c20b493c8b91f0e50ced50e958d57b04813a2fd39252394ba0b3361a395eeb933294502a513c3109fec0c0a038a8aecf0fe04d85a3f6b82ea9a6e5f47da0701f5a951d525de4208dba5da04d15f6bc9d23455f665f1cb91afe0087d9e754d4f8b1e6619e409dcc2c9e26609949bb29ea93be4ea191c170521f72eb6be04e14afbc2a58334a5abce9e99150c8577da43ce1e605bf76538d8a5095b66d123116a35942101b060d8bd1aebe000eefa59a110fc602ffaf2151de7d38c3e4d83d2f39b3379c956ff3d8caf7413065fc563a59f85850b1d551cfe341eea0daffceaa58b8eb1d33e8fd078c35763019c0bcf61434966f4b50a761e160fa941b073305d1ea0e7bf01dcf4d4eab67a00e499f033113abda6631ac0d57f35ec0a6747401278f0f70e95f5b9118b604d0bc7f87fdea5d3571f559a9841b55dcf07091a62a740f2f4275053abdbda86110e803431ef1c23b9f0c2a9c0b73dd49b5df247f645c45c2d719fe47bbac5336f09c697087bb30d1a6f43375769409f53858ad29d697e1df0ba4761ef38b0b2bc0bf904350e82210cc90c13d03200c04e226856b2e167f9693fd7d3cedb81af37021ca1a2ed2d30fb6df6a4b03edc0152b567a3b387bf0e681f95fe6edb6beb420318d887547cc86ae4480671fc8c1668b1f95dd44076ce530e0501d43875b82808f5acb75e1be546842a90739960bddbd4ba754d4e7b0dccaa2c1a15335dbb240330f7ba61b8f527e44df153a2d5d988f5e43d5c920c48a6218e67a6584af444016e5ad6b2870919a09546cbef38323a4357dd0b3ccb0abe1b111e5a7bb559f00c2007660666963beed5151de5bdd24dbb5248fd9b373a21dc6702b1edb8558004789712fc83bd22d99e61812902efec6407749057e31c7efe80a80d5d8e7d75048b755e8d38bd5871384f1165592de8fc992047249fc12e82769252aa064bd10ee09058b61e00d01f9970b61dec3e541af5f00fa1895cf3938988759d0924a6040954eb3a8dabcc362ea983c97da57b00a6f72d5f7a6a8129d1b06b694abc410579188288b176188cb882cc6885296be588c86008cfac08a77ad681695c33b005b6e46c533571bb57a8877ba5957af96e02ebc24f715daaa1e650543b0d02c30df83e4ad3dd8c5b05bd913d121ae2030cf9e18bfc278e2d99267ef5e74cc31403ef2ed4f35e420522240b058131891bc47ee97e08e5f4e4e7a5fe5dadff6637046091b6a0ac10ee8f30d64dfc01ab39654175f3e2d1970c16d5c16118e9d0090d5af985faa5e3585daacf401f78128419224a58e20473893ee9a980b150c4f5002ca18604160eaa82e96db78c856108a7852f0cd46f2d5a091eac3b91049ae20f5cf8d8c05f6fd19146b25fb45aa3f1bab03feeae233869833a8f8fa110616609b3aa04527adc920f0d3bc29e24e737805cf8e684e8bcbd87ef1e53dcc1055b0e375fe43cb9b1b2581de9860b983dc19398a4f08caa350465a70a432422fadf0a35dd341a79d2c1314174dc8fafe6d0bc5535b376372bcf5d19921e561c67d0080594bc5db46798f3773f2564efb8e3d9f013b0623dbbb1ca2f8fc8efb3dd280a108b0245e3dfe8db7059e8ae683e315d25e5eed3ea60efa8c4c18a75eed34c01bd3b5662e1bf54bb1681be64fae6f26fd6bf428e109b2f813293d1b32ce9ef0e4497f40abc112c427ed1bbf319038cbebf42fb02a9b3286a04c36b1302caf70e1e684349db3c463c0ebb27406c26ec1d148702e06cd56833f5a5eba7d45f070ec243b7de7b492b4a0591f3b1aacb8836a1bde9ff949f9eec939b760ab5e53b0ba26450150d45bd6c03550a83cf8c4f818e578e36fb33adb40e56abfbd07ee1095aef25c3f145f937a24cbace449020a4df533256aef8a330cbf4cc46577d8102d044ceae9b4f172d27bd99bba8ed42d959025ad17678435d2b2355cc0d250b0e347e9c46791642ba9957a16f02b1ac75783a1a4791bcaae4f15741b361991006c087a59c59e073216cea6e53e092aa902bc230618d95b3c4756b66302e84160debbcd8e0133090e34958d50cac7c843cb27faf443b354c61bb03e56a3085bf0298c216c4b9ef73549503b8dde5d01b400caa0b480cce1cb3b6a85cafdf1b6e01869d57b0aaf66e939fac8ebdb2aca763bbd82bbe7946283137a5069847f6e20ee320ad08be4b67ebff14f26654ca6768989145582a71901063f1c055c32bdb0804e6bce1abbd89225ba2638154e690483d5148600383f143fa1abefbf0881c090788fc5dde89f5a6ec2cdf38f7d52cd048b680771919f30a9c35c04c7a1c0101af95945b8db0a5612cb5bf355327cad35b6b3b14e68c86043e59a3325851a60a0aa802717c4894e2a34a6a0529d5d8803ba7f52065f4d06ddb467d860f55b1035745a8172d65e22e0e4157e30f94080ac28eb3e80f49fddf859120e83b95a4037188edb52978d8863a6c1320928d4a948f4433ff03174750ca963d10c487ed029c2ff7c7f75d1d80e840a4291884e063ca32d1079fb65b8dadd8aeda9ba96d06380ac70bc9f234a0c91ded8bed37169c0e69a84a4bb14eaae2e6cc08ec522208987ad69d4ec3f4599057d344011bd220c49e970b6764bb3ef83b36a014d46d0205969cff02543a85c7227ee78b1d8b5395c4015b37b826a95eadd33204205b0f9f57813f419edcabafd4823eb063be710b9254a09006a430a54b48d023f9b80eaa6219f8aa343b770d609abdb12eb0b9d1c06212e7fa773828569b6c71747f0523453272eda84836b88881bd10402d0f9eb2004451c6fa55f1dbc42d6131ad062ac5763db6528ea6d152c45d07b930b7bb5a81148486df6bcc7c2d94d1b24301a92d4475daed082480bbfe12fea1527593c1f298a9e7383eeb1f0da4d6ef050b7c241ddc4ceccc7f8341873ed032cc2a430205456c931a432a31157d36096f0d5f47896eaa98c097acc1e8907ee432333383baafb9704ae858729d8626f35b0806cc629b10e56958ec06a9393b272ef60cc60e1df5f2644290833b62e850650d31f867072f067b4178924fb3d7e9f5dee66b9fe09b66cc43dbdc3be8b790f70a1e698ea52ad4bd50e6f643e2d8ae8e77c402eca19138fb825f5685799fef2709aaf6319a7793717bbeb1499df55c26fb0e7b0246d8fbe9fe7cd3a857d7a32e0e6a64c0e9174c371e1e7e891999e509baa6eead905d586280eff52080aa72d80f383d72f331d1961926e29c23fe867a1b8698a7f8dfc3eaf0cd5c992cb95086050c50dffc7b195e7eedeef948fa7598899f2ddb8b1f38b4e110e052877ee7bb0bdd2d806d5dca78c3a57826e3ad5ec796cbd1ba038c6130702fb1e250ffd7e700a27061d0a5d73b178714760ffae6bf6b987870b8ee01e25bc7911312cd0154055a652678f8c6107ff498203bc1598ba3966a7993a622b06fe39cfabab337ff077aeec9dfda1a915366b718b6cfd74bc29a29b58b5e644686ea5dda47fb75d702846fd47e2a0f144da4a61bc255b3ab5cbbf0b687f59a7495da5ff30019513d089cc02807361bb74a54f127072eab34c171a0c828d1945d9ae52e06d50a5cad094c63c72357937404b5adad0127739eef125bf6ebd53679d8cdb4d5ba05ac300e8a82390ae131086f918dedaecd291cd9df62beb664d3155ed0a41f10ba5b120ddc6ce9b24e4e579c6c8a11ea870511bdc14799974d16ba7b8d6a8b3687d8c90b9f1f3c7842d88daa89b4b472ba356b4b1344cc10b60b6a014d322fd8db1fb206f036e600ffcd8f24b690de6b3fb904b9857ddc796b52e4bcb70b8df165f44e0ec8f2b7f0f4ca519135d8c7d7becd63c2703f80137c49a6863084f15cc76e89080baa8162fd8b02f5d40f010e670d6bde17c90548ecf1b2b8ababcf66f1fbb706d36480c4d30be75b49bfe4a30f3646da801cecdd6a5396b99e71cb6987455d0cb0999507e8c6c0581f6a518737a60a18c4d7cc1004d421c97b0ea6fdae1eb6071f4b499c2125e29b61e7efc89563932a62a6c85acf9490bac32c01ad52bd880c79ae179d1087bb6e0163a145e18b7896af4de2c80b55647610ab92ee9a905d00ee9682802ab1b882b95639feff92c3cfc48aa4ab411f349333251cce66f7860731657ed956002309b894b956ce98060773e4046d96d0cc75de6e535a27cd8f0f718875cfff2e23d2960919eade9cf40679f9b34c470f62e6bef3338c7e16c90788874e713b700729ebe73a131294065650ca61069461dfd0c38ccb857628750da75e682f7bcc04f4e6272d61eec9cdf2fa6da297e77aaac2435faf8817e3dd081ddd3c8d4893dc7e147ee81194bdcafd23084bdde3d057c731c7667778d5d6024039a8af89955b68edbdd70a9719422fd4f1b1ef82d05c865466270e5d822d03a8af8e2177fdda78610a473626408999fe8494c66ce41428f6d32dd5aaa89707f55404cf86daddfd701b95c533ec8d40c2761c1ba46c239644124bea0b167a0a6ba170a56b7871847495c7498461f45113fd6900b2a196f15ca15c6ac2eb750de9a25678d781ff4d22c92abee0ed6ba71adbdd70c51d1816c6167447666ea006587d0ee6e0b615f2b3a16f07dbddd3a791d072271ccea780027bad68953ccd02477cf37dc86f23d339b3bd69a3fbc3fbd9a89ea9de9df73b81d68627e705e709c95941e1e2acd02cc2d28c316c3121be0cc86c52be895077987e53386c5c0908a78ae278530462d68737b2fee3e89e167cb69616ff7a11f5cfc3994a81f83c099c90bd75268434ff792766758e43c189e81775f8fd8c847f60693fa0828a57081d0063701cca6b8d848612d372db92283f6027c483a61dbce447e3fec8dfbf02650623e56195d657e815c3457d31b8c3cdab83fb1949595e33baa9b366d1ab02a58a91d27448972972546f28579d203c5fed461e6f550a456887c9f879e3bd0cbd63274ccc9df82b1910f124c77a0b6f3899b9fe738277aa85334db43201980d5af8ae9384f4a013814a4a6c66900cb6fbb3445ea075a5f27c8630f457c01806fc2b4cb0c180213b0b7090951e272fb5214d5b3c6574e72eb1c73b72c9294a069e0185ddcf089f50fef57e233e44bb78a0db4fba5d1d536947b42ba5aa5db809df380c1145a4cb7e0c610caf0db4e9165d40569795d175d6382786d233a7240d13047307ccdbbf47aebf28aa283967aa05815981341e5ce86622b6b331d7fe0057ec403f0c99de4267335f657a91a65dc439f8b6924ceb3b840af5734441240e3531649c96608b3c22387f8fbf2ef606a1879406d6b5c59b8ddbe76499365b0dd56a0bba7a99e05e2f786b036c0d1523cf3d02a8145654e987c79564d46bd002cb08e3d3a89477e8f0c5fdc11d6b3389d58f4898ce7fe486bcf61e817928a00d410fb95604fdfa1d979cd3397bf53b8956df6aac26eb78e9dbff9845293e920f47f0e56174e623040669447f4c2070dd04c70a4043a705fb6c768531b60ae201c969f5b5967801a65b06e25221283647c75bb34875b3a765719bf47b5de8ba0644991eaa195311020f2692c6589527650d0cdb569509a22b6521f3bc3f1bc3090b5afd7b6d2484374baa3eaf75e21d9e804bb74bd50266e286c53e16a80ab2061c2d88dffcbe448a9d04d73b57ebb228f47d11d1ec5773d55533944784e24d01be64e79f8dc51ad034907797a28cae59754b849dd2c675473a37ef57d4345705d04ad48e68b269d96382beae678343c5974196d7792ed46f5d9ad06263cb0d08d604d817a84d0f1da4b516e3637e06ba6924f65d6c781466c809e61016f4f70a1947b495dd803f59688d72d783b0b91e4a3b7e30e5160dee189bcb4453b86e0f148ee97be82efb16020ff9dd820d6ca6dfcfcc72b03b6bfa6a16104e9f124406239d4c1b9b31c3469cb66e5c9e372a7379c55021c320107a5fc3378bde55220f6ffd106f74db9a396a143dbb57e4ce5e88e7ddd496d871e4702f4e3e3ca23f028302276663adc3f3f285a1c91cd8760eb93158344321258bc396c855eec28b01193d873c191db1ca948699aa710019069c333ac923554ad6451cd0c29d9aa507f6152ea62a709ff233a4f052bae0facc8cb1fcc240eb1cd834f3ee70a2bf0401f6f8e0dfd92ea87bbc7c877b514fe83a5778a7728d9b099bf65f18dac940ae003421f3ac8e5cbec29a79b083a6250c5a28717c3dc42c7b538befb9335dcf9d0d1c72eed7189be62f31d6457d781f3c72e340f00c6a89f1168b9a9910d56b9d069ef4ff918222e6ef912179808ed3121a947e9b82c7117ce78095ffb14a6eb90c5e15e06eb49b0fb58935569bfb486d7bc6c58ec0bf4369000620b60f5b7b0907ab3a833d1cdf2e8ff33680aaa2823aec3b64f8d5ea2c1d8c6a711d90bbd04407 true +check_ring_signature 3619ca561f247f8addcb55407f71964a8743d7ea7a6514a35730ee7d4a676156 e850de0543d914fc4cd505d8018c7df90cd7d61ddf32927e27bec3a96acd6390 5 09d7889aa2cedce981d867be576feeb4a612c0f87f342b07e64d5087f0809ac1 6263cd0ddb2ccc213348d9e898baf75cd45c0acdd63a40487990e5ec1b454594 9f90459b69183e678357490e7a4c4032862c4cc48bed215bbaac57d9369cc122 92b83baccf935965d94148d91a67eaf407d7ef76ffb50b29e4530b5fc5974a14 99a1d75f7073464dba70cc8595084ceac909c2aeca2950e9348c3d21fa349d90 1e4d9305aba4aa8977d5055c15409e373df1ab5a6c8ed9cc2dd554bf3899af0670e9f6540d96a2b8967e1004a764a91fc4b6adce8caa2aa8df3b7f023a95cc046362f1fa385258b00967440cdb1e51809eaa3bbeb91f2965a20656f85bf6a0094e2b6920be92dce08d6e1ff359957a3f1d23c1b98355eff3c4902f2e7183f005566dbc0175a4fb862e381c63bcfdb9c5c29ae36a79366aac905e22d2c08c5f0a9ed4802a341eda8166e1f58cbef5c40a6df5c5c9072d127b56967f19da19770e3990c471456589dd466b75ce3fc3119a3cf3aeaf62fbeeb534ae0af1de769b0a8514ff7f6852ef6767f48bb654a5a549b0820c0af2fa12e1b7ce1ee6912d3104b301a54825ec9d7e8712ce045bd0e7a78986ee1a70f825138711e5c2ba7d750e3f69256e68da427b3512805d8980c332e50f310692b989d90a10b3e084710802 true +check_ring_signature 50b4079367a0b1ea501622ee1e37dabb1e8bbb3bbf2cb2bb3661bc75a9f553a7 21d886015221ddc7e9c10bc4ad16db60fb2b70c9a84679416bdcb5b797723354 117 b5f6f2a372a2cb8b143a3c38491e13bf618379aa0e7e3a6a16eefe26b0bcc979 bac7e84eeaea43229094a9b8418f4c3c26aa044b9de1e6852fa3a914952eabe2 3ec9e212fda3e1847fa2de3692b5f09c4701f6240ce9fe098363342f3f829b93 ef24faa791e006db54e56fd025f200e72b91f748de5b34dcb1bbdcbd31706a4c a98e498b5592c94e303b530db5e9b56cdeb6126415ce00354aaa3b16e72133f1 d8d283b01990737c55017be610aeab10305570e1bce16d68369260531f1079e0 3ce864ec01fca451b789e78116b9359ee470bc9e89f440ab6a93b16c45a5f54b 6a0f44c5afd90bef74a967b88d57c0a0af3e2d0c2214fab269212ff3541a09e5 fcd4a1be7eece6d0984137494f40e6e5893b1b622a456eaed4d6d9dbe96a8395 04fbf7907431fe0d31ae30c7c1d4c84ac718f66eefa829638d076d352bfeac74 a57bb7a4374895ce898811c5fe8d381d2ba3c048c177d8ebff7af5879640450b ce61a3cef005b15d58fbe45f4484ba99cd5515e453bccc83e1aa53df26ab4ce8 9d94c36c84986144f4147c7d732e6f818495e7d1c8e89455425db03e632b69c1 7678780710359e82b15b157ccef34b135d95f73cb89da68bbfc3f08b0a10f7aa 9e763b8c6e888e32e334cf069e5b0bf5d61694fa3845d5d24b50ba500e8b4233 f6a6fe8d37becc7bbf6e161ba08a6e028717fcbaab2d8b35eecd1de85b907c49 9c8cbee7bb808294f27a41b00c63b865de048b3472b40b6dee2d0d5117de958e 2c0090390a563785fdc456f837db6542b3f173d43451b17bd63cd130e3bc8eed e6c745360d6bcdf6e40dd1b46e9261d628e1c0508a757196aa9f3fdb9e7bcee0 12d33ce4e95c55c7e0acc35122f486aeeb65b67386f58bada22babe1c96f6ee9 d7d6c0ea30df362be8d69ce263a1990206503fe23c10db2ec8c203de6d6a67c9 314a8992635270bc0f70d34505b7196184977cd173c9ff615601939fd48919e0 460f4f5ad236b5cde17b0f8a5a7493dab7809baa2d19a2a7aa84a70289388c85 4dfb4d3d6b07c95a597ced93bfade0168b83ce3490a50cf2d8a795f49a47f8e9 588a6d7686ce519f82765c5433fe73f906f9c90e42a8953496d42b913fe67690 6f543e97f57e63259dc69d008898f6966f863b8899200ba4d176cdf27d538e26 9a7d6c2def25ed7e409dc5fa087f3bced4f91e336281a2767858cdb6a1f33a47 2d8b14a3cdd86cbdc122ac5eac4bf7e8719688201ddcfef4afd0c22b99e23e2d fd424543cd459c4639ccb723f7b15a82848567dd4653c34b05aa7b18727c7259 311882bbd8158fda8a0ea3e3bc4d061a3af9d320b13f8025b26561de4e1f35a2 1f0f31339b6ba4f843e0c89ad9da5a99b7b022340e42bf39d654f11878f5766d 6b207006af315abd9bf74e241a830a0a94c543e0603b353d2e61a40b1c386ad0 236105bf593594c8eec89483fc0d5facdf5b2405b73f9fc968f0fa91cd28c9c5 02d14976a81abb92b2fed45eec57ce4cd1627e19f734d49492ace2f8e298bbad 2855e99475919225fa3ffd6dab9797347c87b49c0e2ff0cd455ccf50218a4397 abd72cf61bbf8fc9926bbc32666779c4fd57896a8346743c4aeacc769a5506a3 48e9008418e161294a4b99f1e4266855a332ab78792c75058d4b7954df18f4cd 354c94ce726666d1f4d72588fabd65817590485548cfca45eba7ba1d0b5de2eb 667511af8b07cf248ecc68d9e26f0cf77b70573a2f8af8e891a90cce9ab0b153 fe2c31715e1fc8ba8240b497062e222f5c208e2d55fa5356ff35615002f713bf cb70febabfd225ae26c7133664ede85a0969814a287bccd3895517681846d27c dece157a92efcb368e8c9f7563056182e7e5e988934282942e99029865fe4ef3 6b9199a1829fe2f306d3eba3c723fb567a17838d5cf70091bfd67a299fbf60e0 e0b8ac1b56e803850c7a415240d2238fd69ef6459475e8d76e6d9d62a88c8faa 8780c81bf22719daf31b30a2b6e1c0d740b992d9ccae778bb06c2ca6dcb736e5 b75fb48bc66e075fd4fec30893788c26ae1e972678197cb7da46cbc6b4c5e517 24c57e179e6698dd65c97c212f43810c50a62ce8048f53fa85ac2dd6d125de65 8c9e30bb1823d0b73d8b192108afe293ab9eaf94a24725bcf5e2c5690b3fe4a3 5ab5d9c77bd37dc9d975ea9c46941457e22493682977780de75ab0187834a580 1c77fd595ba7ca3aa53f1d12be6fa86de6c4e326dbf46dc0c4ad8101f75212fd 148abbf66e1d914dc1a80488f8786ddfd738d914f0311da3d2c86f5cff9caddb 9c82ade58f5b57db108632fd07f9a1786136cb25274abd4e752e9014c7082f2f 4eee296c20514f4eada08a914bbe2578ee01c3b411d4a0f94816047cb3c12e5f 25d6b2b6edd3f6c1e755addf1e3b52c6579f70582a29f5ce49fd72f65c4f452a 07f90551cb9c2bb5dbbcf3d0c9de583a6494ea13aee984c752f753ad7d0325c7 ddbe38ca7ba7390bd948f34fc6e8298a73b88a7f1fa3ce5e63e6908633eff617 a1ca7d8c869c97a65d7dc32698a96be6f1449817cc7a05442cb7a3a0443b192e 704741c90e4a92aa25c1e290975577f3f6b1484af2aa6fa16b38a950577bc563 71a506f331523d197a6d79dc4ca34a6cb909a4a5f6b471f23f08dd1cfb2777c2 b8ad4a3ce9b9578fbe0a38279748058b485ad4ec8e43edf29c0c56b5c9695d46 05bcc14fedd35c2063f2bbbdd6e200cb194fce921ba2081cabca958b17a10c12 cc0717a952ebcd3ce0242b457e87099cbef297d18bdcf24b1cee9e3d3fabd4e0 a6bbd2a55143555b069a8d2f06e843dd51a423a3a911a84f1179d6a331553e42 62554e86bad88191cd47fa1ff7b91425e2bd32c0dbb2e1c912eb2c0e3c33208d b1c79bbb901b849a9d7a22e7ade03e792e07db1f6817129123144f1098237da1 002942117ea43e979f6a682cc2f9b7390b7b2673fcfb8b061e11a2c78921b3cd 35e9cf8eb3e50dff1f9a8f389a178085ab9f4c4425e4800547a670183a81ebe5 7d6e7025dec696cd99c635c9547972032d42f93e29f35fdf1e4103c4ab50e1c5 f103f3f4749a58f294a560a705c6481ff2febe9f79902c0e780da888e6e2ad11 8ba7e3b17613fd4710a74fbfb2c739b05aee807ddb08f14382288c3261f9f6c9 20bd8597cd9608de9030a84261d39e4406c1560e98021602e3b89f7d87eb861a 5c3e29042813a70c52e5643c6dd90506a0dd1148340aca35f8ba1f78dc5e7f5b e51ebffe147e8f37cd7445ba80f9be55f9ce045ddf8dae842efcb054c938bfb2 e76b9cbeda23e5fbe985ed7c0c886e1c5800afc10d08632a65e334ba19c3adcd 150cb86510255a0e2c0e95379723937ea2595d82348b0b82dd6f1cd30c73e9b0 ab9cd016702d251be57ee0c67d7a5c29a890ed472a00d35cc8f02ff7e0e363e1 0a1f54b7c7fed1242289f8580e7669801733db4d1343cd8d099b819053a19511 fd7d1faad630aeebd15a45e8a63badac199a9a77cead2bb8a79261079996bfca 536f2cee0e6dae40f0745caa1ecb3417eb88d264baa41cde7287f1c0f9f253d5 cbcf39d23e4db3be49bbbfac2385bfb1c3a20095300434079545ed131a789a2d d49a37fbf4ffb846c14c94f59b3f5e56c21c7b20bc855edea0149a3eb23c021a 80f23309a46e17dc902b914c9f362afc00d48a38459b5ec4e84f734e37fc11be a445f51d5accf4b5c014daac24d29bd888a546b7af011954b2323fa444584df8 07432eea1ccc3ca41a83d9e034dc8c2b29ba37b9878b89d5cde019d96eb153e4 680fdb1cf56d757983f0efb46d121d7ede74fd1769f24dde69ffcf238ef12e1e a130420f40d62f00c493850a64b25b1f947b771453ff79611733e560cb44d4ef a237674c1e579973359b2921aef89371be1a9538bb96b0e6390275ac07a9b082 0225714c8538f3628667da3e2f811eaa742871897d4168882d0412df16979199 c4a4aaf5840c94464ea7d2886044de938ea3e3ae92a26a5ae708a5fb848e404d 7c3e9e826720373502ca16e2a3ff0181f3bdf50cf7e7acc50534b8e08fafbc60 3066c9cceb8f8e052953226665785719bc656cd69e9045a7a75b3cb6e61a4de7 9e8e2e9d8f519be454a67ffbd47ea0a210b87bf7aa088015d7257bfe611eba21 3d4f0b9bdf95046ab4cb0d48afda302c9ed10a6bcde280dc8ff4c79d8cd397c1 d89802874aedf3f205fbb93614600e35baa7353861bfc6c2349d254c1967957e 79ec9ebff2f5080197d0d01f715645dd270343f0b9ca2f2060e88331db144a73 c3e77edb683b6b830b75741083e68169c3c9b118b7e8e2f68873a71c46690cd2 d81c7502a69e6c5dcd0919327b71fd246fc909977f2021d6173b7fde34575b25 383e02257337c9d786e6ba3d4650d7a123e10e604d4d3dfe45561c1ae04e7008 032c1e4614735fa50f2a940c70068a3a2f745786c0b5fcfed9a898ec4f2f9a07 e0dbd09f6eb9d20d3b735d07c0123a8f3d39a5d2ebaa372981825eb61724f49f c6b951238b3ae2a3143da61f808782873d735562a743c9c57f5fb308704c9b41 26955e947abb3785c3ddbc9391eef11c55d523bfa1e45d7e803e49993a8b5823 e65a19c83e7f8ac7caf049d95354a5a72973f73ff243f29e1bd7e4cd477780fc 2265e92cf4a82df6722a2de466430e75413220889dbfb450ecb7165fde430370 533c46b90293a0b564cc9eaac73145cd31c3f2c6bfea9c2109f9bba7be82a785 b704a16ff20688d452231aec2c97aada7ee0897b203aee9a7f580269d0a82412 8f7a032a9c5e11d77b4e5288238fadff2adbfd3bd02b44422793ce98a07dda0e a6da5734ac166cf947b3ad39e89d70f1e130c8089fcdad9659f8c9a71da3cae5 44cf29a7c548337dee3febb6a61d0913420400fc7ac8ad24f0e11051b4a1acb7 61673b130688d218329f0e5ce21e658224cfc923823945af67b1fbc7fc17fc6d 8ea12ac8e7950187ef78a7fb6d6bb35ff80f2ef4176187937576855a14f49c57 54546a5d9015bb9eec6392659464d2826971e7f6d07ddad7645f31e82d09773a 6ced7cd31a4e528b180ba491b27b33f1cfcc26eed8a5dd87b12aba3b5d7748ad 9bd352c18c9d6a6b8a391fe667ca89fe09400c0e657e57c0d40865950c36739a 1492a54d9068ae8e33984a63ab38c075d3a47738bfdd89a259b01fb7c59e3807 911a46acf1addb1f58191340f01858fc59c66b2148695f936856210fe2bd95df 776ee3fad6a51d6413ab9e65c1dc78f170fade57429b7b4106e8ecf3f3f975ff fc583047e7eccf8af70ad946dd63d12ed797e88deb98d2a871a3c966052a8006b4f79ce23e37b0886f4ab3712e75db2689a951bb9277ec612b7b91da6cf59d02ba3d31b4f879db222788551dbffbc31a9b284d8d6df9477a8ae25bc48103d507bd6845fd307166d7dfbd502bf26a393341305372f4b0a1f5d4ec2de283ff5001925bdfef2216f5330bbe59cd8ca5fcc6e03d41b6330fc6eaa2c3114e539a4700251685b11769e36f199cea18f4b406236849b0ff072dac6b208b8151eb011e0893938a4a3bbb0b7b8e8a43df59946dbe393ea4f7ac926493262194a4f4e5ae04cdfcf6218cf2f8c459262ebbe420d496dbac7fcfc78edcaf9e39c1f8a699190bd654c4c81d06b9eda73acf312c3c13b73921d2e4dffdcd92dfc433c2e583a005e43a5a912aeb01a5a4a0ac72ecbb109f37450207572240cf5448405bdc0a360ab62cb287efaac89008dff975e048224558b504a00a3492d2c85b5983ee448b034938bc6acf4deab256b022204d59cdbd918597a21476eff3e451924b8ce65d006d87037c797f8c3f0b3024ad9a9da2dfeffbcf009f203910525b75cee67fbb01d118b5bfb4cc8ae3c829fe557d9ab4fcbe131d042edfaa046464b7c776443b0ee7c3502ed42f7bfc7c32178d754717703d20305da94e0e70eb3119f19ed33501b7469fa18b18ccb3d42e633ac8cab15c7c86ceffc0539eb1164bbe171635dc03edad96f44f5d0c7787c1faa1bbb053acf2e9e5feee868228950096bef59c610d14959cc14aa51d43ff2e0734500005e824dce9818af3883d8f2528b8ed3162026f1175652dfa1bcaae1e0946b3a722aef632b91a7f62c4aeeef44d0c13560d08d008301336f4e528bcda76f1382c56df4e4ec0adb3928918b224622f936356033503275f8228d632fc1c084236dde7d16241f4b7bfd994ce3fda66c0a838fd098f9fc4a50025371ed8192aea24550f6688ef701c3292de800327a8a4da6ec806f53004e5f9bcc9b4264121bc79faa154413d26531104d8c90de16444149aac0678e89776f4e42b2f289e888daf0638c05adf1d2d382672ae7dc2a847d1de6600083f102bbf7db5d17eeb9781d49d43a84bb8ee8d32318ffdedf64ab542f8c70f213ec9068b7f94639ec79efdffa76406b73d32267d0ce9d7b4a83ce819e99705acc361cbc33670dc5bcb482601cc21e5df3a38a3d8a0c6c3e42d57defb549b0643139db14da842ec3619d7749501fba5280befb2f72bddf63caa6c5665e7350a050eab73003ecaa76d9222e138fc42fbec1e6031a3b3f4d2e623c97fe2ee9803de527b8b9f32b16c8b7bc79bb2402f144e03e6e5e2c22f9fbc40d5e78dcb0d06135ec6a0da4c40c35711519357aa1ef0592b665bf02e3326a311307fc0272a0a4e2d6bee39bf061a5d436942b5e4fb5da84a388f2d74fb5a10a4f48548e8b40f21de9680622b5fd3891f50d8dd4e0b3161d8b46d9df6d7850ae55af113fd610e5d5d9e84596bda46c0dabb65e40eaecae1bc2cc73977a8baae80632cb1cab9062b1aa6150388219d72b7585d2b2d2fcadab94a5d355db91532dc5d1000dbf3043d1a5401afc929895695002e231e608a9bdd2940e44f5a7f1b2a31f29ca19b068b13e823048cf5e9fbe57e6818a48f34be4c50df9ea34cff6beb29d50701a80c28d0914f7fe09b17eebed9120e89b9a03e7765ec4f0881638d334c354e0a0b07f38cf4d64f8e2a9027acf48fef792022664fb63003125f4187e28de6a04abd0365ae6d69cb5c784e52399cb5aeb4119e9506b40e203924c582de620ea90230006e41c1a54d203c685e23db38b1e816e3302e417155e3d235ca902482c7b77802b1d007452baa11cd0b37333ee2d0907e98deeb748f1ec7200e229235b8fdd200074ed963ba8eb2333d5a8f567d7ec74d7abd845c770e157cc28c0fc3b33a4c0c8a95cf425e660cefb86e5a47d6e2fe614e43e08b798d7c0f90dca659c7479a0036e7d9f494ebae6f17dc516d619cfc96d89c0be8dee8b9a6e81df4c2877d0e0731d3f0aa454e0b6f7b3798f0a0ab97a4cea92187aaf0d21bacb78a012b90ae087114a260f18e1e47a009f8b1b8b698705c5c435d533a8c46d07317457c42950ce9389f5081dca238ba0234bc55ae3cdb2d86b559da3b30e27af837709884aa022b8b2ad27caa4254f129fc08cacfafd7ea76860a2bc5d44f0e62b541e83095070444d69b4750c62711d0aee715cd70cb0cf7fd4dfe756474a5d81a8a2a6678001ec3a9c81c256d11e3e5d8b099e8215a2850ef43d4e8727686e9ccdee86bdf0bb8ff02d012d4def638818ab0c33efaa89e0b94c3e6fc488641a1fcabd9efde082260677a749de14ff15aeceebff565120ce518d220c437a163a3af2564c00408b1f9296a56324793fed54276a99b8672a3ddc893f11d5ca21c615844684aca07188811320ffefac88d76d14bc04e2c4afa7365b9e76db4927ff48d44fe25700f82cc1a8baf94832ab62b0a41736cebc5d081863a766bfda6478a28621f968b0b771d95bfc2bb31cb13e970283b6187bc35ca14b243f23d91b39ed6f2ac2df70cb51e9b066255216c1b0578873071272bd9786a46eee538e50808ba7f6e5e920949cbea0fb861065e7d5dac8fe9f0db08a31d19c9cc54a04d015a318bea793b0fa49c7074ddd30a340d119404ac84b01f6257dee9fda64b6ea26a6edc284f3000db0679c89a4eca95a5a829923b0faf4d41c4fb05ac5288ad95898c98c67beb0e5f3e26d40baa0ca06b7cc9ec9ef0433ca17b41551292c5c4c3113606ce22eb0b6f7b3b690d1f88f3a0e88bb64f91f8e7aad74a60104373f50e5c2406235e11078ea52b10258138da76a8aac43b13247eb1f79e15ea1b4d22aa838182a7a96d07193a26d6332a1a65b4fc0b9a5b7e7f3709bfd0b116b9795d92801c84569f7d0bec7b49c29a9ffd6b07e3acf1ade60331c3eede32746f0c914e546f203d119d07f65004d27627346ad718c73fe8cbced60cb039cef041c91775467003ca3cd907dc377a49fde1981cefb70b8dad1690dd10bc3a8cd740a52eee51875fb8ab6f0b820bd040aca05a462a53bdd4cbd1bf5e8e88d77af9b909422cfb79120106f9089c81940075f3fb81e3084f23f99740d7e7e33c2121c5f74d63f99efb6f34770077642155e9d76b14e694aeb1995ceb7dd6e7c413acb6f82cf67f79f912def108c037a32c1790eee971dce130872d24e1c30b8e54767d283040229e411f7a1084b2fb256501cee82da9020d4a49a247f36d59f64f2034235002709b92a350d70ffa68b0a134025563c19b5b0342080c9ab3833e9519b581c1de737af0807a7507cc0b99f50939f0990968df113d9ef7702556ec717fdfdb8c3b0b08bb7ea01000a58896db5793a725fe0bbdeb1e9d8f696cb2d768311a2d14acc0b830cfccbc0420e9d995c385ae8aeb14d264a3e7d011718ac690c4fbbc5b7dfc12bef4fb5604416bbad2da9241c9a08de6c7e1ef7d66ce25b2118b616dd4b5c4ee3c5b185c03b0c353214dd891fdb3d754854f4dc04fce6d142a62c44039042285daae3d82018c2c3fadf370547b08235f4d56f8e9f46291b35bcc33086cc906d4c1bfde880b52b8afacaf3817b726d59d32ed767e7ea9017fa6708399824766c37c4bc43f0cf3bc90e35a98cb15e0fc78e18848f8176d2d6d1d836da7a83064d59d10a2250da3ecfcca9e435afbcf8d3e6b024177b7c5b4c4fad7d7f1262629f3041e57750746240d03b089606417bcfbcd3cc0e3dc47ce494df756bae06afb0ed34a865f06dabc80cac1493a168c9742142e713acf35dcdac639af92423d166dbc64b4400f047f95d39e2dea6dfbe872bb7ecd58b999df3a42070f5df50c26d9b87ed0e50b9b0050a56eb568f1e2dc7ce8c09efea814b5f214ac3d4aabd9943df97b0b110396648c25dc5707b05914b9c9d8e26fb863b714c6e387e36ad7bf1ac7a5ca3a0e99ab53cbfda06ca6d70cb6b5a79e6f36028748d7e042fe2010610f2c9d26180a5728601be66608da547a1391f20a7d76dee11db2236e780b84aab4ed1636890e2cd675af58d07173d9b639ed51afd118cb5bd07f374a5c255bb36afaf74ce90eff03d192e40103ce1defd367cda5a8a7fb7e85bc901950c86f2d2b7446c67a09008a705c7e937f04b04eadb48f407a40b5d3507165268e3bf5eae82f61fa9b0e107ac15ff64cbaa67fa4a285e1177d4b06c36c0e448620a31dc6b71eab7d9a03f6cb29ae39bc22835c971ed413ceca5d135dd858f3c1fcc02b6f1d2834b1e40ad32754e2f3bd98eacefb09352d3359c6bb4e36e5bd3c09a582cf0b6184103a0e97c084edaeb787fb174418a5ba30afdc8b0ba32d629996c570f9796e65238e09df4c6e73ca8425fc48fccc3d0c3ffeca4bcbc6ad8e70476d3968dc1a0442880e8b78d04cbe5f82349449ae94f3a07e3cfcc4be68c64a47c249b7c7076a146d00c7d30cbc8dc6c78c3a8901414232c989abb9f0261aacc4e957b0fb32ddab960e88c0de24e6c8dc01833dff286ed14007b9f89d2f5921d76e3c604ccfc0286501ecd4de56ba822de4e45d540392685f961367f3358102b4ad25f4433c4be35a07f47381f6cad9a6fa6be706e08ce37d5d571e2f49411e14baabc75323a7c1bf09a9a029420e8aa11ece0672ee4ed6b1e65d5caf09c9dba169d783c99f704ebd03ca0affcfa9714a18ec1717a90e86c92fa6e12f3807ca435614fbf8467212080eb3ebeceea0f189ca90e8f5abc5008d1a13bc6a9134996070b9fcb46b908b490de5dbcfa6eef0ab9edb0944c9d38a47d441a9379009a250466e9a2dcd5107250681f4fa1b3b1bade83f82facd0e3f897e914f09b5683cee5b34d5c39a0677770d9290876ca59b0909a6f31e05d427050bb0488c04f7430053a72d51f107910a080c1299d72e195bb21d735bc1dec3a534b386c539fca76ce4e34befb0e5f04b0d3b67791a2611e31fd6a971b3084260c9d257160cd14150c49abdd900d0ca7e0c73386adf0420b8f27deaab10d07ca2aaca53f89c3cd07805e5565dd3538306067e01601d9858b9b47aff0b809ee3f5b56cead2c6c2bd20de8c81de94d28d2b0ab50c20c87155cd073331b7d38f8fb3076b3bcbf7fc6cc00ae4fd777969e8ec07b32ff2800782ed2df8f291a7de52d7506ad17b4f87cec6dc06a5e13bc6411d0b6abe9a20113a2e58d42160d52a9f7c4dc1ac32b58164c0ff408801d5c68fc50a6f10ab788819f8a64932cb9420cbc1dd6f216b0be9297e91d3ac1e8fc3022109ab87568638a7e5f1ce2bffe3273e346c375aae108841946685388700680a660ee66a60d7c7abf312b89b9e0c2f00f8044cbc83632925130dc2818be127b1a8003858023841ad04c6c07ad716e0ac0af915d44a4e7ac5d4b28356e95877fd40035d2d73ad45e41a73d4cb7f7304b88a37ccb6c5f0ae7887569ab71ddd7e4ac50c570805f68061e35fa6dced0364ec62185a77a27b7a4d20622a488fe4326a19094fdf9811b1d11bf72e3c88a3c3e81e730ef5bf6df1d5046f1095b4f6e5e2360747b96e420eb5ab87d9970949aa0577a5d8200d372ab78e174e9d33cf242e58054f99bade67630eb95810b162a6e8236d124511987ffba561f228d45ad7c95b0fceb42915f72f28b56cc4727f23a4a1fd7c5bac7f2bebfb3808cc5bf7ba61f90247dab5828ea85b66077cf7a7afb238abb2c9ac265f650187cca38a0bc0de6c0427a4e2e80cd49cf6386aa742b5e9e13fd6101a8a17b76c535f3447aa69fef10b00ee4fd3114492457828110239a6d87ab5bd07b58851ab1c9fedbdb67842f30676f7b6d590b1e689af8a0977b7e12262d5ae36c7d8e5866dfa7b37f729e87b059040941474bb75a42b7a02b0b79a0117c0d958588cccef4850a373b9d0955d04d1e31ea1cec038ec3d3ec818ad31d574538cd9f29cf9955e5970172196201e0822c038a5a79fc4550e16875711b2444f5a57f994f4eadf4c41bb815fc645f20721d448aab02a3490fbdc608c72f017ba7cc66c8abf8e23c7842eed76cfefc30314f6b85508296d94e81112d5457951ade2dbb2b097a12c6a44ba80e5f35ddc02097985b94ef5b959f945313e51a607dfe537c632e07e285a378b60fd4bc41a0b4c70784790d6e8b21825d7214976c7c18587cff6a6ac10a2ad6f2a723dc2a00a3a22c99e227cd0322ece6874395265869d84ce3fff1fde69cb5daa8cbf23ee0de35bce61f693e87121602f12bc8d01a2dfa678b0c25df674cb8d38b5a9c5f60ee2ed64a90a0fb31d39407ffcf70285a4abfbc78701e3285b5c251e2e206a6c0f19d6bf79f2700bc6a44b53d8343b2cf96c7c7daab5d2eab94297c77b42789a0348bcf891a1c3c76d6e4715a88161c7f2e23be95d4f64c772070a69844177a0022ea0179a297eafbaeefb6fe609e815c290750c061874667bfbf85ecdb6c3a40041da13568075b384b8d7583016c323000be16223a3b8fa9949af9c60e8349d05a35f0de6b9934b23bbdeb634a825bbce943bcc54d3e37bc63ca80f62f74d290551e9849f436d74dc8e67b0bad42e9b1994913f085e261868f584962b075fec0000f77f19709f677753aca5535d4279b497818e5774f410ffeb7b120183a8cc00d0a8dc6215ab164d283371fb0e23675c08e93d3415690bd9149b3822d38fc30a1876663afd9bcce733769b33889e0d35873deec67ded4f0d52992c0ff739a703e270e829bdff86d6ede2acb675fe84827496d28b790b0f2fd14e7ff7fa19ad0841ae8419274d6d7ede63b4d8e6652cd938d958e5f8448a8b4fcce58c2b1f4706d3855664614465fc646b989f992dd04eda1ab958887b848a5eac99a1dc665409f8ac3008b916186bea3a158d448fbb0a4d3598dd07031fd7ebf8a8dbe33d8704067b291e8b2b4dc1b9bb30a67cad2504163b08fa32f38f8514ed69d7488cdb0b17f84e593a4c6ab382cdd28bb8a4cf74cfa95e66cab10971ad41412904a10000b1c1ea89f62ff491b07d1394001cbf0688a7b174656f4cc54b4dc8ed4f855d0245d4e1ff6946be5494fe560556dc122758ec459af2d7956b3a11346fac00120d1d4124064583d334e915fae1cd18815488c7396aa8f2c6673f9a48e97e576708fd334beecb3c487dd9e0c8d07ea5fefbac695019258276bd9c2c405918f23f031aacc1f6bf02ff6b995eaf883ef6c04b43745791de557883560b0b1dde4fbd051b8a2a4401aaf9650ed62628ea571b082b33065fc71fab11e451673f71aba10a19a378911ee8c7b9670390bfa4cec5ad7b8dd5e6025b1c4bc4663e0961879e01dfdef4bb79a40ad188643a621479b6d30bf512a2164a1a9e67e9c4c8c93baa0c71f3fc8df2556d6da76efd8463ebb24652c903ad0e906effcd73b31a95e9a10d225504414ef15283deecc7d806bf3aeb6693a06a4f460be88ffab5d25569e60754c9e445005ff8b3fe26f520395602b9aabc31f70cd0f3ef1ec4af5462cb710083067dd83efc75885ddc44eac308cd428cffa9bbb8f009a1d9749d56b07797033521ae6e796926efa850a9058246fb2b6785510a546e4012e0c54217db339e0a3a5140f364007fbca33a742110cb5dc77928a77f4bb9cbfdddb6b0e4c9e8c90c4755ef7f2591f91d2017ceeb09b104d5ecb7ebfa8cf21f8fcfdf15d4ee826f0d5eac3ef28fd4a0e8300d5ac291c33d76908f0e53e5805d1d5e8c97dc715fdd032a70f35cbaaeb3a8a871f479a4ffdf8950d9b8cdff1e3d08b76ebbf769363b05e30c5d30f01b2ef83c1be0d15b1444a08c4f86a4e33ecc508f53208bac4062086b305daf45aca7aeaf8b7cd26c06b404db4af8cffefcb9be0faff7ef16d89100fb69ba221a14925583839645f4d9a7dbf50ca59e02422a04c651ecca3ea0c604d8ca74d9defc5cb84ac0182cdf785fcb296b30cb6d88f867ff58aec580da6b09bfb1f740dbd6f484e219ad491404bac32b4b90d8e36ce86a8bff0d098b5dee038279a416adcd258c1d2b6a177d854caefc3727723136a9c2328f301e72e6830bce8147dcf701177aef7f3a4addb43e35bca0cc653112c9c2ad33d2713059230c9815228e45493d64f1212b7228f17a3310119bc137567262f2b152b89c4ee20465e2b4df2069eaf6ec450879d01c55c305136165ff82c23bbba0b6ddc0242e019cbd799ea4f6db70f643245f1f906b3fdcec86d4d9aa034dd19f8d8268b40103ee9b3d6a07658c5b1fab0b9b1c096315bed50e9d58f271e9503708d3e3335f024757ed0450cf00e943a909da887d1df58f162bba50570951e9da0c217e3cb3059063155a19205d16bd2ebf2642a31b14b47b99f74f9d96181b58816902c6e309d4ddcf627e3038809a6d873ac29bc72f4d726ecad61c7a8e7ee42c23a815c40022d21b4719f58a4f78da394f428947e91fd9e3723826c16e360a90162a7db407ba4595bab82b931bcd5f9c807ff085b279b92cf45de2b0d5287b4a70214e25014d9b3d8a053f66b020b1bc519eb7768f0755769f16c715fef8fb25445fbec9056de7d240f1f993f0ae85ed4c57d4a5b243668e750abc282f79b04e0739983b0dd6063e4a835917d9cef3bc29de6965ddc5720d1cccfdd15376bcf0f7a215eb06107e96cf9c4a488d0aa0157ad1d8fe8bb4e465720e1c8fb59883aa4047f3c8013dbc76cc8650b74a50b97f2ac4e4074f836642d256b3e90f002229e4a2284a0f6754b3d9f935840d20a2943e41ef6448c50da1b6e61b7d1d51890c0cacbf090b2dd5435b0b78f3271c51f2a9a321b0470656bda299bed607599a2146f1d49b0751fae8eba8ed9156350b4fec4d80ac6244e97862709b91a1b45201a3bf470b0ba0c9f2d11e80c16ec8a5df35c7c4c25028e95329b09e7b4ed5253fca3d045e02262d2ac5e476b29b2f3fea6c7a2e8312770dc287272cb7ed93ef58b812f5e70ceccf15bd7720052541541936d1a8ceda97cf48b7967d6ac8d9bd233b6ebfa40182ca1bb0c327cac78d133ae0a91d00ee1b44f790c0a151c10dd7aa5e8b7d42045a0c157b663e6fed55a692b86f6adb72b436dbec48cfdbb729f66cacaa18b30067b6b97bfdbc4229b8af6fda52a609e4c3381d661fb3db89497074b76b4e4c0f9cae8f3210e9963bb8d39488754e22516286e3c564192fbf52803d1909f1070ab226f9ef9f19c0f95abcdb3486c10df7cd342f3dc418bf097e52af891643930588700d853e6ce1def69abf4af9b06a465d0d3e71ea9cfcd3ef87cc55ce135802b27b1791cdcb2c4a16b5fc5c9e9e35f40a69e71c3c8e081709b38a853bd68f0ca051b3a5eb12cce06cbad3eb9d2ee80ad63d72f1c9af21fdbeca5d836516960fdc249ce7f59519ffd1dceb13e3dc073f5d8f6766da8046d8d10bdf4bd3c8a70f27a0dfde8000141174c1a3f0370c755e4d1f703a99972f0e4f705450352dc805b4fd9a2498ecfc772cb783237584a7ff2adff8dc10ae4bd47cff69693da66f0ddb8e403d3e447bd645dc7efa85979957725522fd28da33f127ea0b88998d3f0e867ab85f09e21a5a0fb8946788005da8c7814ba62466f79875d691ac1725f50470d7785f85a7d303ad4796e613499b1f825ee452b5a3472e93c925ac327aa6072beb91f424f0d260aac40257b29dd43f2a10e11e90ed6446981880c185de350bb2e7e25a302a26c66cb4f3e65e6727a17cf5c22aea5d44477385883c629b220d67a396878dde110ae1c14e31cfcb19263b0fa609183429bdf63f0fce7d8ae50e8ab4a5066596214ae310588c201fc44990e0b0968ee1920a8e81c32d717de400729c95f5fbef3a8ebed01559a9c5f27f85d66123be896df1ac9e2c3554354b0d20168fe13e28bb49e73fc2f807ec3dabd334ff0c6df5cc467252ebe4d2a84604f5bc2f2638c8a31651c7bd259a78550cbed46e1d27e993c8a0ce8d2542c20d0cfb19e11f205007c57e5cf2a7d50a0058eee9866e2292827503c374900eb5d6083f57f333bfac086a50e89159751b995af84639a4da8afd931d6c93eb9b1e440eb48c8e2b8cc1f04df55b9d8fd6b39c21c98489f8b455af823b009b35cca0320bf404533f690b5285f53c4bc28f6e221205745cecdde620fb79a31c66cbe5ba09a171daf50fc7f1db52a3a080029d9fc7ff028a18ac5d0842798080ef7490a90f5abfce8ee5384fc46dc879db2ad6759495e280bb40dcfee0a6dadc46591cf606040a4a73bc55451a59f133b595500bdfab4aaf95f064c4290be2446f95e6940c376b5eb477d488ad65339022c7687b029bee96f37f3e2c872bc932e624a79a0d77812aec12716b0ad94142c952a54029b3e49b7a9b1e493df0efc73ef947f40a90a1e181965007a63ac27b8625ab9350effd8471ab117718f91022eda2ba8d0984c794d84b78c576728e8bca9e131c791b57cd28c30670ae29837fc3acd3ce068546d448c9fb8307b88537117f23fb425b68e57692a107bec10aa108be558a0f0229242d8a6a2bb85dd4201e459709840f557d5c6814d607ba2b39e4f8a2920b626e50501efd344546f1b659c79ce7826973a949b0b3c7ed748d47b1cc2ff30a false +check_ring_signature 82ed98e12d1b88e62562330676570d4795a50583f9e7845cf41b1110d80a0933 520717715bd3926ccf9a8ac2c966ca1def514fd2f95543db96e376f113093d69 2 b8601d866b44d2f317fcd1f7ad20063b93bd97afe09fa604abc669cf4a7e89bc 7f33f1b9dfd8577f21a593476a55f5da8681c270cb138660e9949c3c25dccdd7 41c960f25f6806f5876dbee0ff08914fbaf8bdfdfb432a14a45c6abd9ed7da08454d34cdbdf22976981939f8cfc5330b240e5b49b54cdec046794ad2d439ba3c5a2e687dce3c3f6d46c6f3a4dab13036066b08af73f1314bbbe43c96af674c00963f9808b98b56c5bd585e448cbf436e2220497f7ba5b6fabec5a807fd7bf505 false +check_ring_signature 516f57d71e6fe5dd657ddca89fcce5a480ea2838648941829f817e12323e92d9 1b6ce2c655a08d2cd4b8de111e0ae52814a5f9f4e9c730b38477b731484caca5 1 e8df79d6d13016f0129a2770f16712cf656433c0467d70bc073c2ee3b2f1ad29 541e86b31b20b15f36b20d3a674926e6a6096e21afc1ea08aa42acbc8905cd04662e4e23f739556414ded2a7149ca2d316c10f8e567d6e6b63659ef3624b4703 false +check_ring_signature 0b6e68252ef5282e9ba433273719f01547630e1aa4696a9f726895b334eaa642 8136c0d7ebca8e367b8468fcaecec2869862fa80572c4fd65bfd1e5bb0192d5b 55 b6e77fc7481176608224d64aec30806e6f5aaba7e087a8c0d06cad4a84a2661d 101eae6ee2accdfeabea78055e67953d6d15930e1173c9a9ada948da7a0aa650 09fdf57f908b932609985fca6e719e6410fce776a3c37fc8dbcb91b119d18024 448793d1ac65843c3dbe6ef36a8d3714d62f0b9ed17a705e5ddbbc212758448f a21025045770b06a08727c6f09c5f7272f18076c7d206433a0fc54ee032765d0 d6e7094940518c7e88b044562dedcf58f1621f9212434063273cb910086eff04 b0157cc91540101ed884b647bfa7c0c7362124914ac046acb2db361250009789 dc29e705f0b49e6731a3d4a3804695a84c5255bece581cd82e83ad02866e861f 859b4035feffffb6db3c956bacca78ab146e56f9d871dabf702e7771d015e5ee bedf36a2434d262fee12f11d2f989967f70016c5947615df856f12202c45d34b 735ea7c561af704ef20ed37bfbea3418ee2f53286f621db9a82ed793961035f4 5fe80e99aa54c6bcbdb760e01ea63cdfeb18803306df7e4db738df6603c09154 092bcefc92751d976752326ff44a95c2eabb31b3c698040cc0b7a1bad51f4824 10d17ddce8255ac608560acf30bd2246b9d5e1160f0db03a540f4e9a379d4b2b 532766facaeb78645cf8d2dab3fbafb981e675dded8c6a4d9d7f1da2181c2aa0 c6cba6fb1dd656d0b70ec3f3e962c9e6f585b2fea43dd30c7f290e73bb36bb0c 18004837b46ca20737948c4488957841a9a73e76ba7d3e9d7a7a53d748973008 479d74a0168ab96bc07c9095597822c751b72fabf57188dbad63a4a19ac3cbdc 8c6b2cc31e47de6b3bfa845f83b086e932e0acf71b41ae387506e9c495dd4704 d05dec4ec707dab9731f54ecf2babe23d3ffa4f3f9e4ea5c02121a3b01831a77 dbe5c41acc4058b5d5b8d5cb77babb7b806370bc45c7e73eecff0b84e70877cb 589756121e7da828c39abc8a324dd6d4b8bef50b1d1b8e13a4192889304a17f0 2b8964e837952d29441f57021bce6e156f6fa0380c6018dc7c350aa4dba152b3 95fa3078497999742473051c3abb1759106f7413a3017123b0bfe7b6fd284251 cb1a003147b302e19f8f7a24246779d2df4eeea403687eaa7d940573703c7302 b2fca58bd6e6a0571e92184ef66eb878a0879c57be9ad4bdfe6aacaef91ab647 3445be1e494e4437d86b910fd90b78e640e1f4f8ff9577356b6076ebcc4ecf5d de473f4b80dec6c7052cfd4d4aae2b13d46d80ebf0543ba1c8ef55d871c206e8 ef46226fd827cb0fc2d1e9d79012cfbb4d9e805c8283a532235c47c48d5e3746 d8704adf21ebbd814f52ea37152e4fb9afb4b4a83647d1726c85a50931cc75a7 bf100a1b0b409759adef278b7e04e84c12001f790ae9fba8ec8ec8c4b3d3a83b 3fd019184907d35a8f6f4822ee80657387b3fd76b44e80b3c47bdcbe1387dbd4 7a6ff4c0ee4fa21dc2e177d2278fd49e9206da9a4315deec25d8c7139c7d7304 645ce3bd1b54130a26c94bf8acc4a794c0fda46d8a5111fc211e98fa8d794c15 ed84ea5f3cbd79fabf728c7c32e752242c62393688f28c5b89c979d17bf48cfb 36eca3d4b97b52481147dcae577730b7aa8ab4d8fa1f11a8e02a4da25a0cd587 99fdd7e50ba50249c40f74d0f8685deb134b80e67b8e56e7decc5f1f3934e530 3ddc89ee072e22ed2ce906c4231fca2352dd7bd44891082e0c8429b3ffab32e5 a00c15fa600c5778496a157e016ae154efa31550083b076be7309ad1de60b592 ba0f8f13df7d5b5e81d4ba6c68b78fe33be140a4c762b3327d73ef7cbc51fbd7 930e04007e63afe7c276ab5084fd65e3d1ee66468f876be7b72aa27f536f46c8 c54acf5ebacc71346a4863e2231e124c0400037ef45a1fe37aaa36f8a0d39e9c a7b5d11bc24d6e16c76fa3466458f7f00723e27800025998887fc1252ef33423 92d82ab02cf2fada9642dbea6a8d237405e3624152c0b664d36ea9f404cae8bb 9916ac6af413d6875e2ad1ad726e7ef6c070962a697d747456d2f2f84f4551b6 b1b553186ac80333d10d36914f9d823dad664b3da9f39b877685c8dbd0435c23 1be28a04144f54b9f52a1342359ef9f32a20e78a2bd05e5b2b27292fa9753460 9a6035c530e3f52f60fb4633c03029abf34e5c39a5795f03f8a3f2b6b7165167 6b13d24b9b1679ea03901477c79c71ae0327daabe547ca3fbe7413db3dc83703 2f9fc10e3d32b43f949c26caf6ce79b840687ca70f62b96e8a192c81d001986b 55d6c5a9e38cbe106ee747a82ed5fdb47dee83e7ef4165b27f0f422ecdd95632 faacbe421cc00f2a11c8904675dc068a3d071cd4e57cb2e0b11f4e93f84a363a 0a01b94dc105b743e49275e593ac754e66f49d52b92317b72fcea5ed7a0a021e 0b1a807ca6431d61af768b1020af1837988b39099e0405a699bb90873eb649d6 6164f96839e9680793006af494a1646d425dc26d3c0862f4317d94c2f802d265 e916116f89e8484320185196d192e0b109703d79b4baddea2ba8937ebaa47202b303ddcc5b74df8c47637128aebf54986b87ef2a44b5ba203aa8321e7a824403e62f17462b0679bb9abe27fdda0d2d8315c50fc6cdd3a1f58752d7a1b22dc602eaa5f3c61b00518867ea4e3db4dd6f62829157386f6ae248d6409b858bb4590df48969d783ee3105f4cf8a5db638600c2de6012224b57999f61cd1da578510d4fe04049c0c7f09d91a01a94d13219bae9371522861dd1089dab3f34cb040450b78c5a7c66ec808a4b6cf73c8689ca28815eaab372400ce76ba44fbdb658eda0395765b1b21a7b8ffd8c041adf77036a3020a8b77751f2a2bcca7231af165c10182222ddbd92cea5c57b2220303cd67a9c15cd78206c9d7b4fffd8bc29879530d23e993e3570e37f31713610ffef311212af7ff2a763de7ba581ef7ff872ac9057a2e8bf0ec72bc21f0fa65f777a62b4c520bc84c640d484fc82c5d0553192c086f38ffee8e87ba15fcdff494a5050129224d61ebc2f5b459a93600100422d902ed4272d2975bc91bd95d2cb07081afacd02cbb353c4242bd2f97a4520c821d0c0fc233826c2469e4f97b689c98d2e416f9ff4f0a66daefa4b5fe3bf7bae7f30378af88afef6b5ebcd0d502881b8cf3541a26b1e05f522fce35727e6e1ba9100a4400bfeb008ed5c2b20b21d6c650bfed06beb8045d8c7c1367d2c3a03fe5a20d2a2cd244911961dc84ea83bd61a8b0b8778387101417cf9d5086a0067a629d07d463464ce60e7ef70f286887e1440aa89b1feefa225a275cdd8d0f1868ccc90a44bc8f2c96e7ed123639abb5f4568ef8a9377efceb42bdb2dd414f25dbca7508efc4d87a265c95d2b79e82d4fb545b6359e12dc1980ce38d66d1bbc2fe3b9c0640c1ad44147bb73b784e5c3ebb2c16ac87c3131fef25417724e46a21151bb00072de44e686e59c9a29a33648460463f357b2e451baa1fc5e1f2ccfd5ba9ea808f5d7f6c9e0a364915044e1be9ad52a4fe42179e3f679a58c3856c1be4f96d906ebbd74008ad7970ffa588d549b01ba9eb9907de21ab7183c358fb45fb6cd24015478d55861578e4c18882cbba4b91f08c54efdb90a193f8d377f12ee5b4b72025b8669d1b4dd9a257198c84d2fb9800796dffbcb5a1889d01585ef6bf45bce074144c0088ae1de8eeaeef83cf75830f1f86d3d11b239513a452ee7e731b23108b5fbd55e9a510fdaf8db52da8b6f01ad816041cc89ceea942221021e1f943b00d0674e0c1cd4af8fb94647d049327261d9ad1f68a107a16ef668be2fe391220073070243a42bd88e0366520a687ab5ecf3c3a3aee3a5d5ff2fc122820974c8050166011d64b9024b3d3fdf1d278dd8930fe0654e04ae3629c6de8298a46b190f1f18cd418a23a65d2b5e14bbc69d48067e02762149654a00ca5d9a20935bfd081fae065e206961493f0fc8304ab55c364d61e438cf9ab97fceb240e43cb61208bb20ceaf75a40d085aeb86169b831bd4402091ef09935d038f920e3b93ab42057707b007a34047cbdf366d9707bce0bfae5207907f735a9ac1a77214b196d102da384623125e193b1c15596b76e42cd7b4305d7bc5dd5f998e1a8b35c3a85b0c3a94b9930d4c5071f7919f3cfc6a0762d088362fad33b7bdcd9145754dcd6a08ed17e85188524eee6efec51164e8a5b3385229fa9838a47b626215ed419402075f5fa277666ce62b800c0a8c9e381c979567344b1e40c1718b298bdcb88e20011ad393868fe736f4587eeb289b1a2ca17a5b39443fa097c37ee5ae6dd945f00c1e50950fa8a77025a7f5f8451b67c9500d186fa84152213cb47cc6928f2f1b05276b25757ec0ad85c6f8941ac248c7c075ab0509354f8690c0291ab5042f82048584d0ad05e598f5a7cb73ff537fc0376443f14f04b5e387d9961157fbf7e808b232fd044491b459d951f043deedd14cf58b3e0b07e05097be15279d06433f06c5af13f02dd532637fb57259851b27264b8de0a21d13fb8c765a5031661bca01ea7067cfde288c3f5f1d0cf422496dc388f426198a7a5695d81355d64beada0217705d00ced8c4ef0daa61481c35dc4d5efb74d7d1d5293107b37ab3ba2d060bdda0e8f3a20d4a341d0ce20b4a876c9c8e0eea8d9ebd34009a595ff1ccb6010033605f2c10ab50232000037b48ad32269216d1ba5d7d329a7a9c7522be80b00b8338e61639f5aa58c159618433bc1b90ea899a00d9896aeed7e70c18e0f9800183d7150c83591e689dc06e06e0b256e22dd62619f9eb623a51156a4693da6f0d33cbcc3a0c3152d9b7dcada0a2e1392765dd9db5a0a0c86572f6fee52354d105c866b78b08798ecc9686a09d9845114a3c3a9961186bb6ac3e9143d33376eb0694db6cb526586874a73037076ac66d79f0870e9505d9da81eb86dc345961c602225c9891199f7a335f2d03c3d02453065746d69f9fdfc8767614d4d75b6e490c3bb74a860408b7d27b85d9f36da6594c3412dfd1479245fa9c461eadb64c1403725b46c45826725d0fc0776ce7573d2e15ba2cb34917a5458800dc43668a390ba784c8deb3f165412e66407a98cc58c2409284be82d2027a36d36dcc295d67050bdf64ab9d03ebfc2f260881194acef6dae5b005ca778a3e3d733fb7ede51b0c5af09f30e5115fc621c4920a7c0d268ded631e867223e60d742fc35203e00b06282df03d49076c7ec98118b21073552466ec4ec8187f937de987017d534b0e096a369b4d32acbdb27936d1336ab9b7b68d9541582f47bc28a3d5fa1d783fa60f0bddbba74e59be0ad28be22bbc8a634828376557a8c7aca8b369935e5d4baa00226d4b20d6793c4ddd7a803a3639dd537b661e8424e73d8f3cb8dc987aec000d6766716559585c34d273895f34c22d37303d66069d191d84107194299ff3ab0e7911f8f7894626b022f204dd309a6f964c8d2fca5d4ca4fe3d69198579e8230c47ed552455e931617f9aea3ccb5752db1c70268469798071a54487407e12720fece9d37ae2d25003e954d6d3db559f065ca4a729d1c96865ea6041e3844231079ada36684614aee56f11737c391b14e54ed3377c9d9c0a574b834bbd10352007e4312cb112cf7c133c0f122af439c3abf6a6cc4df80af16c66c45a5b5b66b509f21db4ff0bcd7f655aa0f566cd66738f391665e636b1df5cdbb05fd420443c0cdf3f2ecbddd242ff3b32349aa51d5b59b87530750a1f3e042f721913585f9404fac5b66a31dddaa42d5c15bf48dce7652c5908659d9ae308a6847677bca3f402b9da30120a94871f4c45eaa06197be52703b3f8f3d2225b0363917da14d1fe031b76bbbc98fc6aada4f50325653f16b928309d423894a016fd977f5a76b4c5001024a848a492497f248c7bd8ef0360c701bc73288904678653169e1e758cba0453b8c4c8c612bb5b5f7f89e9469e5f409b45fef1244e1689c6830cd0dca80c0104bd7c7a6d3d2d884fba2b31c9bcd86f7f005f7e4ad9abd29e2bcf44662df10bb87e93ae4d8529d7d04af1b00c4da20711f96ce678e9b2d4f83c1ecbda5e910d621e4cea177c1df2757261182d54fbe3587299ce8ec8d2947056cf08a294d00e1dd6ee77822230f908ddf32f1a0adcf8be33710437f29ff92a052c7dee7b130be5e84330cc0b8ed6faa627cfd40d830767622dd55286b08a2cb6041cbf980a03d4e137bda0db24d2d0fa6d755935726cd4e74f983f25675366f721eb4e77170572b184fb75105d1dead2ea73b27586844db9f538aa963b89c24e4dbaac94fe0efdb23067e74b13f7ab19dc14b69f3fed526cab3f4b400d1114c61f0dc40bd10545a1cdf45abf2f1c56022e5ea017c85822c03dd5db7985c7180de3ccde12c80f56d91996ced9732bf9101966d77627e7fed02fca48c87f76ca0575e0b89416093012c204afb04855fa1807c8791c9f752915e5b8bd34504ca93465abae9f350a9e60f8a9d2fc546bad5ce9df7d41d67e7a317f6e9cb431bf88034cafabbf8805234be733c71ad6977ed4d6a3d8c54a54bcfd1f7d0be01d8019d6817306a2740745418b282993458e779ab7d1b6d1e36f8b22e18a06a6037f87c902a02c00b80c58e88c7c2fdfe674e993d3504aa161f3b637343bb831fc72632d26fba39e95066536a5f80858da88863c78af151dc64c52ac6b9f90a54cbc0e1dbe028c721f04470925f6c932c18e5533e85fd40c763de86d4b601cda04609ddd1b212fc83c06215a4c107667efb6614b0f09f346f02646d9a26d3ecc430f55e9f4222a5ce303e4e90639a2f307fe897707d2775ec8c75468b869ac1211742ad8193ea3a5810641a4593bce9bc2641d97b4b652498ccdf92bc310298a284197dffe8335a8f6083cd93f75cfdcabccf08dd4a44bcd66fad57d2ce5499e92c984be868a68f84404d2fcdb2f822647bc8f7416263a1c6b7ac2268ef385a67dd2761ac252d7fd9d046bd703b148ed75801880eb19f478fddfe16fcdc6f308c44b0e5558a3994774082331cf3f2f377d6501fb4b854cd2eab8fcb16952fa12a69d77d54db9795f4e03d0c706966af2de62d423ba3fb36d886406f0885ea2145e7560b41e7bedda0a0f63e38214a7c9696a73b7828940fb9a69989720d4e76194a6b7bc5508f3363f01b022e103953f028c9fbbf60cfb636199f87a244911db7def54c3d6a125a52908dc5bda44f3aa8a02fd61851b295ce4917635169f752aab02614bf01a1a615c0bf34e2df584229cd9ed33a78772af50c6a1d62426fd54e7aa2120d6d1a17da20ce5b11c52cf08a2a68f8fa44727a150610637c403ecddec882a2c47eb7137cf0c4ae82d2acebba55952eb24fcbe0613b016d271500c98cec2590e485cfbde140f68b39a84b1c119ae0397ac58a766bb47b7ca8204a61b2e550d2220ae1bf1f309163bba2b22ee8bb8f385530b84abc634f35420e57bc86c38254c855a3cc8430b false +check_ring_signature 9eef24b770f4722644e820f6ef8cd275b4d17bf3e96b28e38ee24f12848dd79d bc1cb4516a2c1cd9a18048e2cb19557232dc7ce95b59e185a65d5c6b8f83af7f 38 b486a1ca566b3479fb13e928b98d0e2c0c49c28ac7b4f40d7b2e4753a46df970 a53b5fb38467a4d3de05c8821ab5048195eb98f0c4fffecf7cb70808eb008c3d e6458582696062ca5b557f05a764ecadc19d496bbd96e17eefa981da725daa28 5749e66c4ec70930962f9784605d912ae9a3ef182ae2387d1d36fc9c934834d2 1ccd60612cfb4aefffa3272664443aa061f8750f367ac730711ec2e5eac01894 18995575d628e0d1b8b28089d8968f8892f7521c327cecbedde7a98d6fdaaf1e 2c7debabfb6e72f1305e99d5380f0d3203abcbd06d4cbd5ffc09c7a3463e56b5 d8f87d18e4bc1bd6533e6e4ec6cef9420999656a9eed9a7a27388dd97115d24d d0660a14b474465cc734c4b37105d6c71a2a25be5bca02342566e423ed34bd3d 750546f145d015a4099155a3a61b2c1e90a952098a8eed9013f3fa9161102f96 11cd7975b9bb810fe861e1746a60806c44341963e4a11a212013ebbfcc6af552 bf9d730150b80c0d42f9154d42fd26ba5be31fda364fe7d96978d87e8a59d4f3 0faa703a6cab3082c414713208f559c5ba64459c50a796d9ccbddc85f6171cce 47657e510a6bcdce21c845137b3ec3d7fcd73eb3067c35e653dd148521cfffc0 cfc7c18d03a051bed0393c3432619675f02f574871fdccf2cead290f037372cb 5a0832bfe03a27713532adb7cf5e9c182c5ad2a1e6169856c55eab3b852a1811 0d641b7c155a21e9d578a222f0ad92c6d0b5509e33c8f09d7046db5af9014275 98dbc2cd54601b57f950756f081e825276ee2a4a5c0d5f03a5f7b5868cef8f9b d50e342217c9acf5212f4334deeb22b6335ef735469ae5f7767ccc5719c609a3 4ec31182d323ef0d4de09358483e40d596979d82eb46c019e8106409f5d500a7 d9c377a3aa23bc04682c1fd5541596d49ac1b0c2de6b622ddbeb10428c3080fb a165ceca1ec347a5d813f85c0fd506c6e198580bce06d05e6ccd566681773cab b56c387e7d0fae384bb29b04356f11708b920712e4f7ac0711898f7f007d1b74 15017d8ce3140851ac72505cdcebf1e2ae413cb8fa5fabaf10d2113f3922f882 29a5712d7ed48084f71544ab5159f834f8df386fd5007251fc114eff6c9da472 fad2aed4caedaf68a70dd4cb7040be2dca74cf17d709b45385c6d4ad19a83581 9b59c737af37ebe5c7c926a920ebcb5a2e40e79f3240caf864d25ad55dc3e72f 31da9aeec7d10fbcaa0a97cf8844a7b476fa6bd83565e71fea51b793dbe22e00 e595335b7470501cf24c243e6a6d54c657c2380b661255b81d0d1f98f3bfbf9e 457758dc4858b631ebff1823a781241cefe16ebd37ea3e5e9d0fcab8cbc7cff2 28626d201bfbd136f30313a8a28854e0aa2ddac309dc5861173863eec6e69798 fb05dadb19ce23d68625b33fa3496329aaafadf1865c6046ffaae31851a449a6 f9c0d4b8c7e72015e869abbf8c0d2df3fc0e5b0dce4ae592631b4b95ceb57d17 cd08c601151ee6006ee3f03220781d12a67b1e258d411ad3e79a3e4e316fcafc 000ae2cee5364e9bcbc95fd451cbf9b261751c7464f5f05a690b132140a4cc5f 97080f41e92ecce271bcc7f2bdd4dd3bff561e98f72fdd2c46a11df1bd3d1abc 25ebf05dec5a632d4f2e6afcc5343bd8da86d6bf46a05276bb02fd785c312802 0da9dffbcf82874a08ee3c226d054c1b0e7133239c7a164884e91e2cc41dc395 45cfc3fc64b67be0f5cce23a3ab58c9a1919185535c2401ca56eb67e51229e0b61c33b67471320e800d27c8ee5610a41c1eeffd4594660ab77a9defe8b0f87006db8475c3e98256e5f178b486a9b4af26e016601fe3fbb9a74c94ade497b645462a6da04c041b1b908661fb44a5f215c7147b3105b9052cf37ad1196a4166b07d609ed2ecb6056cb73ffa35851b237830850ff7b723cf2eb1884f69a10b58606398c27623bd73c931454cc07bf6cc6ec822b8fecb0094e2a4def4b1593a75f08f26021b4f9df3d5c42c5c38de835fd985efed907cbe0ce53997c4f64b9c6350dfc485455eba00d84445f3cc42f7798e4a38bf87693205e15f15cdcf0aa63a903446ed7cfa2196c48615205b8a8eb9e2c6df13af2306e50aaa4ae36ae9af21805c27871b8ee893e117cf6cb9a35347fab61daf63a741e2dcc88fb85c3a8710707474978538fd85f8c3ddf35ef581f2e2d1dbf8f1292cf4f920a00b4d1d5c25f074757359d74148f121bd64504f364650942e975af78906d17229245030fe3bf04019cdc4c1a9480a553b9ba65d78eb9a54e0c82c2edf59683e999c34f251486833367eb08e4d264015d75f0eed962e8ce241501af1a7d13fd02ca87df89761f0a79e572a59c86ac4b0327f520ecd94597c0bd0b9d576d731e75b7e4e018ce930837b72c87bf7571bfdbe9f0f798a3e510cca9a62ff811891dd5f5bba01b6e0d0fb7d9ec783c9cb0cc52b0b1de4618546d7d04ea1a0d2c11f05620a4eb3c25660f845f7ca175eb44d2661c367007f87ce60198c02f6d68308f2155944e8a5ae40ef7a938fbafb64e34c935addbceb5d71644cf8b70b371c966fc19248011a6d40dcc177ff57eb843fdcc44313f4b17c307a908a3b61aafd2c8982dcd7c6dfc6d0783e89cfc379875403e8e1f4c9576b17b37c5276f11474b546826ea41685c6801b7c4cc6bd2ca03a1c8f5fd58add70d0b2b924760e6c38b0537dcc0f5967c220f61af4dbcd42d4539cebb8b3f62e98b002bbea58d9e84dcecfa60284eae91e00870440ad02abb504a21592cd9f26c1ee8d1525e6faa88c0c8e3b89dd1a3f38205731e2818843a2440a04ba7f5c7fda64fb528edaf13ee637dec78848ac29d1e0fb3b9dc3aa0bbe1e4ceb434acc0eabebebcc33868a648d5fabcab95394ca7f20ef94f303e9605691c9ce9ad943bbbd5870411f34ef33aefc69f22ec46afc6fb05805d4c989d08e1748e91fb397576cacd5da1d49fd0e96dbdaf821345949fde03da3ff33443353e82781b8b19d972a7f5793caedbe8909eaccecb2b0897b27206a65c15aa25b949436b7aa38c0d49726483382faec425b0d29fbc59a4005bd00fd5ee4bb1cf1875914e3e57d1f05926f28e723cf84a2218208da327ab7f89d105c1e198a0cc52e91894c9d2967389e1e0a59877527a16529bb89aa7e180577a0ce8e2e5efefd333c67c3fad3d2acbf1928c5b9d2bb4bcee039cc2dd0dc6b8170f532f53990a261a18521f2aad10106e5af0c583152327c16db1017cf2f88c8100a364519c01aa8ba258058a65dc49dfc2c099db9f50df9d8f642e987103096f0e47c97593c4bb04964465673e833d50b994463292975d57506bb3a02dc758b50ec9dfb9058b48656fd617dd1a7f33d94e2c5c1ca961ae013870ebfd3e4379d203a93735ef85c1ea358a94fa17847ce904e2ce8f174487d5d82c4058a6a5c49e0c9fa6e8c6ffc2837cb6ad09ad6f6e743d2e6b46202d21db4fce25f1a820176e068db4e8d84f2cc77dba701bd6e51648ac9cdc699974c94b2037ec4412ac11330ea0c63cc367e40394eee7904c74d904b9c0cb4cdeaf96ff55f1f954f920bd190a45979239ab5ee5aebb67fc773e2caec45f629907e54ebff54298f234c5373900b02f3a2ab6602935a771e671de4a6a5a0f0596600921b1d675fad2408688660d10ce1c5d9bbc5b3a853d98b2b08672d5fbcb70017b917d50391cc59ca0f5ef0cc616640767097df59ba26b51567c1c0ddd6b9f431133f6bc9b6b1e686c3f0c038ec7274ca029ffa820aa2fbc90300f57a9d895dcd8625ac50698d86977c4e908ab532266be54011173eea093c27cef07b848f84a4d846d8dde827760648db70fce751614eafb5806ec4dba93677860f4ced0a1bfcb9f24b9de08436f069b4e0df3b806b6e186205655dce4a72754ebba70a12eb6121fbd0d8c41542cd554900474fef96a7669221e8212e82b8bad17538df158db0e232d0e6142a3db6441ee00b1d115905ef8f44a22c36bf9404e129e5513b95b0939a3983e797509fea187025b3f2e2ccff3f71ec19b2739e1634030e6e47f502667e2dc2fc81d1a0dd7fb07a9b3cf7632dbc931cf859c54b4d1b35813dab5164fb3d367fcfbed9ea11aaa061e29436f0c978c8f9ba2895e560b733b2a5a806258b995ca19520349b44f150ee72f7dde6169f8172e64a5d4423fbe2b0408321fb7afb1c735bf50fc0e556004c64e98e394d915dc10431e50295bfb3fdeeeb4324e506dcb5af2dbb66daae90c2e9f97710337b2f6b9c88bff96e486ca2196b272373177ace81bc6fade4c1b0bfbb21e0f8eed750dfdd66de6faa058fd0df910e7b03baef788e64b0a7023050d45e53fdb8e5a18914b25590cb2417c8537ef9092a80ddbd72c8758618d028d01d5fc83f9f7eaad8db846739dd2dbc871b0e3fd196aa157ad4a7698a5ead22f0fa0ee6c9f2b50ec3e8b1f758326db4a93e904ab1b5407bc31305a2a04526b7a0b014f0dc53489fa1bf025c4618cd02288aeaf396fe1390900233a6ae180fecc0316524279efeb0a775972fbda53abb217cb7a868f5628adb763967c1da4956b0cf012fc85e609f6ff0e80718bef0b6830eb8913701f2208351cd64746f25bca0eb34185fdfff991adcb2207e5e06513c31f529da3ffdaf2fa43f13aa5c8702a035d8ae65617cab17afff4f9e48e709e6c99b403e01533159078edcac2d91acf018c3200dcf9e485b57d3c7d0a65dc376d7462d422abdf64273cd59759dd300506ead5d800efc131c63b81388c5e331f1e18eae8dbff82c66c0dce2a5e93e9f8051d53f6dfce0c097d875210f3ebde25a9d3aebe310a896667aa03dc3d54d33d0ed272d408187a9b75f3201d5392e644c4e22b2092ae57929c5f2be32e6f0e52003461a629fb5eb76dbe67646bb68ee97dcdaa997874dfa88f38258f74f93a5006ca22ffb1852b40352e6022058e66e4a76a1e454edf4e451cd365e0dec5fc5d02fbf554f8f29613f2c4c4d9067ff88c478ff8b372f243972317dff1d1856b5e0bf880b0f7dc1692f2538ac3da1518c1b0689e2b627ad8ffe0a045cd796c63c20ef5a155913b28cbffd52dc29bcbd8161a68d0031cefe6def502f0aa607c0faa0b86d059a4a4ca21f12d95f3cad80f11ba0d1d9092ad3980c7066e46c7a7178f06 false +check_ring_signature 57e6a70dcd9a25c4c158f4b7fe4e3fd2ee2593b32718e69ffc6cc7a3b27b4024 918bf7d0ddc36673bf6c9cc1ac28fba046b15dd6a6bc143f57f4206fa43d4750 3 369001758371eeabeb30b574c1f462a4f238e8c04290f282365fbd431e4e438c f376444d448468ec1af713646ed414402696dd70d256bc6ddc9a05b8f002469e 4d49dc01fbdacff4cc116e0f77e9e2e561e1e72e357fa3ecfef08918e0bb399e 8dce1775de8ee5e993cbacb68c3979ae4d7653b242360c83eeec46d9a1df99cd35699800226527e921276b15c8ed5a6c8946fade945e2ca50441af6272c84e03eb42d9032fde26cec9e5d6a290c4a8158ffbfadd5207df4926e83fa324551d0d49042aa78dcd38dfa7140d6af08cd8c1dcfb0e2386313a5ae7955b42230b5506408a8f2652e2ebe7efea587fdc2ae8c42d0d0cef0646be2561555ce94e2e6c0308e44a4db7b2deb1fa66ac7a1de78a05a4fccffc6c0f0e4b5b8ba832fde1e206 false +check_ring_signature efc02c94df642aafeaa705c2f4b44bc71a0f0ab3bea0398039ec787471177747 89eaad58cd285a67ebc84a8bf52e3dac2b566ad3f1095b3d4cc337f93d656ae8 75 47295a85ced2ab5643f84b17b1c4247ea7cafa46abd74b038e1959cbd17ab572 ebb9a3df64d9f7e3b9242f4b0c830a2ceca8d919e321b8967db3ec4183ab3d44 d01706c34a216493991ed9f88dc4b34d75d83a34d54363195344177436ed0f0a 7430f2f3b24b8144583433315ce6190fe6bf02695c5a2e6dada05bd52c62d1d2 fd37ed10a9ea3bcef6a4aba4d87eb9d2459532acc97520659eea328c9ab45f46 723b4673a3ce5eeb9176f2d84b0e7e80b21fd470bed51c826a909eff94f0d16b 5319926b8d658531d581bf2e51dd728954c80b1649226683df0808a7c0280873 1e02648d5625a7ce22344cf66ea6aa5af5b007546c311f19ea96847ad360f56f e462f304ae2c64d8fc98529465ff141d86837d73ba966637c7a2bfb9bf053cf2 8e64725b540f4f7614e53200e29853bedc49c14080f84c0c16c6991dea866784 f37af77c480d9dfef8407020ce14875e9bc9de972396fba02ff50754ade77ade 0fafa040e0caa867b0f0d26fbe2b45181de2541e3b798f30f12b1af4c8cc4840 f81f930b15a2c1c54e3eb11a649a0691fc4090c1e94a01357e81cc2b9bd95cff 17b6b46e44204e649c76a2382cbe49fbb071da6a65b91fcd7000e20b04aa8748 70b08818334f6a3ebe095fe7b1395299c38e85591d7620ba707495dac6f89678 06c00422705369ef4cc131257fb273c8ed01acd1f3bdb5cca3912d8ca0509c54 f282c41d76c4bf80c06c25d941e027958c6cb9c6d0e05cbd01875d5d90e67c8b 9baceb91039cbacd2acbeb33404a753b2f5de891975c28f3efa61bf37026b9b7 0f9b2b69166665a5cba4dcd717885131688cd9928d9cc07bb2300def470c3bfc b341b8b9094d9b88a9ea0f83e4ac5008877637e24088b2bdfd3b01183aee7805 3193e8fc357bc6a80f66e8c0b1a72970a46587184f16dde8a642b7c8085a6bd6 1ad60949c91c10e4a29c079794834d337c77b6d7eba75e7c6fa5f65cc0300699 9237d1d366cdec0482c898e373f2b964adc1db8ac322f48a4b6270e93d526612 fbe491ad120a2d56985c2fa807120600c4be020f64b3c39c4511a4e93847a5cb 3a9e2add8c78be84eda5636f8ccf8f2826cbec181b3a4db915011db59891e1f4 2a4607b134f5ee5cdd273f0b658da6df07306df56a45c80707d67350f164a727 471351ac1d2677523ed602dca6e51457ce649fa4e2587b347a995016ea0dfce3 aa865bb51a028a4c9fcf3995ac4ab145fa9e95544c898c9c6014b0f503b0763d cab41d91281f8ad560530714fc5fd25685082a15eeb0607f389da13006434ceb 183fd8decd2c931eb009abb00ac135057c014f7fb6e623bbb835d4ca67f91716 4d5abd1b4cf0bf8a25cb1cd23bbc9de0e3e9e97b547b46591ffa7446ea44bbc4 58dc7293a0ec202468640847b957a37d11e74e511d86ae428c0e047877f1ca87 72dd31b668a7126e58fad7ca616829f29f434613e4637c3a6bc0111c06f860dd c67ccaa48800fdc1ad2cee4d70c8e3b3e952306e470cec62bea292d7146268d5 7cd1ba7bacb688e5bf3625ec69bb96a35991a1fb558c353507762c77c0d834cd fefc4a800b5215d0146c96c8eb338c12232198512eaaafdc58d9e78c943afb07 c715c1f6a55a1deb1c91869e9f857ad2ca84ffedec8a9c91261c46629e916c74 bf2f85e5acff9fba778f782bf640b538b145f8c9a32b464eb204dfe5f174cd5c 97f7da220f625f133299b9683bfa00fbfcb7ca919ca797a39bba95be66d35c64 5628049932372e928b9db86797fe24f44ae768f2a78d8063487e361cac3bbf82 db5272c89fec1621c976577a85e6d72bfca7944886b000e56fbb1ccdff476665 a6d5a672c98b1d57a7e78e6acabe6a20e1f091816d5655b1f9cb4062f7d6583a d506d68001d80a0ba5724fa1b1477c58574344a5ffae08ae1fc9fdf4af465707 51d72f59ea56588555362bc141bc907122fa15b97a0ef761f62ca5be854ff111 c76ca9215ce25fe7c1097ff45bc6682fc9f3932b89f35e831c1c37ade8a0fd94 52a3d261231b3a8f05a2ad1ddd85468a190dc44308093c6194ec4124d738a571 3c5af56e62c123145ba281f27c19a9243f2dca80175aa20f2aaae5e1a2bdd8e1 82061697ba160fb1b94e448af1c6855abcb967345f424de25c6980e367e7d238 d7e9859a1cde8f123f0bb3f35b2c6abd3fbc8202d96975990216b5c3bcca6d0a 9d9af0030b45e49b5777b1b5104033b4c05dd33555feaee916c4830ef0b0de93 2a51bfa69d8f1d867bb37d4d03f8525a0ce227a837f70ed737b6b462a9388b96 4eaf9cc0de4b6e839e49f3d8fcfbcad2e17881456d57cee442e0d783e98e077c 35c939c2a61d7892dfac56b91ce600075fad18078c332a17859dc4b6b705a409 177a679167f1992f14947cef499c1fca2009093b65de7939d3074d37cf3e5e63 ca0e7fe0eb249d5495456aa08a04e2fc50d86cfc8ca8ae9421ff474fd8ca3695 259d28a29e3027dfdf002b19c9a18682916d2eae325701fb5d0a10725fd72c97 55a96cdf9b1a7521f6fd87b3e3f203a4b1ef4d2e9ccedd5451e32212c0b5554d 2fc9f1a55737f9c363b5d805f929c5ae5e8faffd9fd2c87a92f7fdecd2226a7d 796dc141a9bb3fec0356cf20e159d0b7b4c84d6e2a30c70c77e825cdf2cb3c48 483b9b261400d0272601ce66eb3f5f4afca0bc6e1376e7be125fd9e875c2245f 7c6a2c507d85a42462a07bc2c8ed3bee8f2e8bd9573cbe307da8a21d4c08b515 8fd40e39ac941717554d495030d5101f5b3613871dded63f58414514668adcdb fd0089417689cd194991c4d34f964da75fa2abef43720211dfe93a295c1801d3 06ae4d4ff45947fec3105be3833a71da63b1a785698cb84347e1a909ee0e329a 9f661458e01daa0fe0e776cfb276d300618fbc13d7cd4a599417489702954a3a 77fed2afa728370f536c88b1fc4e8931340c6155f6b71edccb391b1b3ad5f2fc fabe81f29a569f14dbf2a843987a9eac86a17817e5e35379fefe65df5aa799b3 12a900eb0b2847c4a6697b380b7d3b583b5ab521dfdcbf57eb28a4a98c3d2221 2bbd41f5db52fedc7bac225d4f1a80d1265d847c69dd3c4034ca3783a889df4a 5ebd273eaa91ee9577335db8792eaabd41ab4ce3628b557e7a7de3ddac60eacc f539f97bc6b5d14ac6808a7f76985a2534d3e961e0b75b157b215bf03ed6107f 811f3c8d61d13dbf94aa85d67ee54388a4b700904f777505425ef33dd4481f4a b36eb1b9d420e87076b694a729121f20408918bda89c6b29be88a7c7d1a2bec4 f510a538e77edca386e6dbcb05f16f1cd29ce1ee65112410080f6242af4e2915 4e0d9567411fb1832d979528ce3bf0e618345f59d04ba84c11ba77bd158f907b cb30c346a6a2026bf8f5413f27a5ad4c2d1e7681629a3f52b262908c145d660990d2b2d45e3fc209a2c60c2c7797641130cc177922aafdbaac069d9812b1c50cd058f504654a2094b0678febe4b8efc816fba76ff85bd4bd3ef42a5752b77c020fa3e9c062f962701c6bd5f192dd403296c2b88a46880fa57215d0d5d9c4c007395a5c29c0d7c3ec2177dc6f4ab3fedd00cd15d88acbb9ffd44e8f338a0a3106fd1bb0f63cf8ca8844a2acc8565e3c7edfb5ddad7a22dd5013f5134af8055e055a0c640cfa5db3f572b367b6128344ef4986520b1253ffd5fff69d7b9e13cc0ac1ce497f5dcdf997dab83a6013c4a8d484997e03b2847d808eb0e752c3be9c0f54f0fc7f7622d2e8e0d69d3f4e2e6d4844a1440a6cae0553420daae73127f10f3b52957ae57f27ff9d1736aea0761b63d40bfcc2c3f2810a7c0965ac7eb9570c4c2a7afa33058b6a63457346c5fea95888e79ded129452727558fcd5c2ba21046726c8a38d7936f0edcded5e4c30e79217ca10c8b969f93ebaa5273a8e727709a90d947515a2ba347595c2c7562ec405e72bed3e0177b20c7873657abde1e10634aee2b8bff17d01a76fda4a675536be9dafa6338d161fd973d48d7c686cb400cb5f2e50738a21c33ed63f5ab2f1835f2dcb409efe3d066049b63b068ce8180fde27a7ed6035c8557dd6cbec4d235ee0b80846b1a6c9f549371907c9114e8d0fc667141adb5f9d61d19e749b31e5065de07f3540875ed9281679ed04672c2009af6a6271e1a76dac50127421a683eb33d8ba29ad43696bfc0614e4482eac2e0cdcd4b779d6265207849e168e402cd4a797cd0e02309a0c8139bbc51e35721d033372e1efba8b068288b0811dad895fc697910c0fb51a36c54bb615880741470709c1d62787f27e15b35cc1d1c0a0699a21f8a0ff1a171a2c01739af7afddf20a25d77dff95b07a2bb16a4f619ef85c2d05dab27b0da7b4bc3dddcc86da1b97035feb8cf79038e4deb48b1e14c048297f67fb1d6c2bac89286c4c52bff06f5a0ec6e480d99126b4585b94c60dcd2f049a91ce2dac43f8b5bf2d5f7d70883ff10723bb031e74069af0510f32a597a0632588907b00f2194c86079a2e9e991fb10089ed52036488c4f18b69e38ecf39e91d873788dec309f7792a0d27982c27e608a5acf1288c0fa42a0d7bb187aa04be067de713cacb503ee48a99abb15779e608299982cfbeb4927b72e5e13df8d886918f7d6f764817398af996c96a405c81062909632d803af865aa2aab90ea64d17bb97357b3dfe24fe4292a1b2a088cde019db4d09d8ee6124c0d13a1a4958752b68c8971ea637ec5afdf53aad8a8c1ed0201eb8b43f12aa420c8e2f07f44374e4fc09cf81f2628c8d34a5ab53ab6beb70242d1e4bbe273f1a9969ed701ce024fdc419d9465ba1caab67545f5a80b57c909645cee9720630e645fe75c2aa38d00c86a2789190165159b62c6d61a687da701d866bd4a38985292528f5dad139ab42053fe6760e2aa06e6b74bc9435a3f0e0a91569e8ad350f5bacbf182c5bf466120d88691b44bc0a067270ab1aa04cd360a4bca49a2fee01aa0fb4fdf1a8d353c9f435b4754ae98205fbd608268a40f7700177b992ee574ad38838a0da782560895eea15903f61a9f419484dc148e2f2a0e9f735fc0b9ee3fca89dc7e19829629be2bd7b8389d4a5cc051355b0875859507388758a364ce698a68b692a2a62fdd62b6198a8b6b826cc57bb0193206e5a309915a66a44b5ed3379e2784dd4f57e7f588859efa1fb7d7a4fd154463c7318909e534694395f31dafdb1bc7c962a450b06a529a264f6bc8687ca1810e2bac8b03db4c31f094ef08447b750bfb085e074eece8c039886a4e242f1802781adfe60ae3639c28f216059ba9f5989b43eb2541d8ffc643ff95d3453ac44e83c8d646080b3a6a3bca85a109a4bf9b92a70470c1864110f2d395e399b62d9d9b9c89b003cd863d306de04a1090e1123fc83d92d7c23616a379f20216256b9b3ba71c820a61e548b81f8e20cb529ab16ec3c2788db389b57fe153d59e110a5830060bf405f7df88fcf1d9363efa795357efe2ca276f5e63fe3a60dee6a56d05651335c30e06ad212f53cbc145f56054b06bcc17749f3203fa3c751326ce8055c0e3c29d0ebb63819f87b344581a615db617bd6bbcf65e889e0df46218a91a84273d58f00fd5abc807fbc610d2a8ca63e60665c03055a6ef0995e58d60593b3de88a5b590de50d2021ee5b194d79c4aba61083e16edd39c0328a864d1d9cb17830521f9e0b9fa97df4032eb85fadfa6ff5579e119f7560ef56ffcdb14e66c5abd310c49c0fee3909115369de4bc39a74d12bc38f43db044f9667dd7761af3250111554b90eba08033ee1e44f41a451be1749d67b2e82953c02d560ef695e320429734a00001cda9d8bdb398547b94bf99fcd0356c7564a12df8cbe0a4a16dd7f3db04b100c1cd9df0604c326ae88c5ebca73573eedb34415373fd94343f1da89bb3835a5026e3ead984cf16ad7798ac2881f5f94918770e3ec38ee72ba1ddbc33cd74271069e2ab03cf42048e92663f382b24f4984244f770494af6171977031388223d20ce5627309bd692189e79803cbb4b895f9091972c3f590b6a1acff7d346ecbf80b16e0896c8774c191b642559b76cd28be5d07ce1c6d4f175535a3877e6fa7610082218d9ba870cae0cca11874c327730c4bfd4bace0a9584f25b900d1cecac4028248ede91dd04ba1db00ca45a5ba75a4e3c7283847d4118a8216c1135842e404f09afa8fe3e97bb1726de3cb1d1717a73d14d9f60298d2671f8900e05347f37f6cef22d6a40283a4d544cea3bb3f4544d87cfa8fd4c4432c13101b6d77fcec0af1ba60a15653153941003902a5e2b837b4ba061c97625c94db89b9d8bc72b00b404be9757de20dbf2071daa6dc4df804daca3e167d823cbb1e531aa48a4ec7006af13e54ed10a59aa32c76be2b0d35f19d3a9d9aecfe684b798d768bc093af0aeff7e59a72021b4559942ae71e5e5b338355f81345af369e426d218948c23801d420e0aba8facabc4e7a252e0afd2b45485fffddb1ae93813231027057984f000c3e5e8a28079a9dad0757f14fbf206a979525083b84a10ef15251cab44867088cbfb84b81a160c6493a521bd6c2882fd218654870ed9f5ac46b7bdbe2053506df2a869142016eb907f70a48dfac1deef2e8bc6b201ea64a076ef547f9295503ab0f3b8b6fc0eaa7d3829a8cd7a7f53a51da6dfec21178354c6da6b6afba5c0466ee5bdca70a4eef43fd43f74bde303fd29e7822dcb21b22a9f8bbfe133257098689573231a92dbe57fe7f8e9109cebd3784297da2bd85cf088249176709a50b88e6060226079afe545435271d6688eb82bc109e2d8acdb63a674648cb84740d7e4dfead7ed31f77cf35323658376ea6a997493d6145f42a441b976035d59806ac19b63ed223b352b306d2703d34cdaa53177e6345f54d9b5166353fda67ac0cbfa2e97b962ae0732ca7c215d89fd8bfd0cc3e1012e399c4d99bf52df455d50866b796e92768514b58d20d27938357e1750944007cdda4d15cc5c6be96e4db044b45531173b4a58368ae1aebc943e5661cc70e042e55b6564e0fc3962ba5a40addf1b8f1c598a866e9e674eaa5449202219245cdb0b6aca1bcb1b97d655947003d2bbac5466d9ed22a542cb20d0094752474ed2500b4c212e47a49561f47c10f727ae16af323e76b50f54fb657fea47b4e4aa93cbce46537fdac4f47073dec0c097a4c07106a8dff8238700097fb904c2c8d1dfc93eceafc1998e4f16bd1cd09bb24b53090c18c343fa5b079302a74bbfc2ff41c05ef234c06df33ddcf889c0ea1dcf84671f72a717a17b7a8975e0ef64068d2522bde2a822b1507e4081cda0400cc6b1e7fa07e39de0006e903e4e26abf7543bdd87edab31638ce274d6a830fdd5f24e60be68fe5f2aa0fd2fd2f4c2ca78a3cb210cae872dc5bbc37ed593c06700313cc867f7a3f752dad01dd255514d3437627b81164534ff352e32a08350408c1183e777d74f6bef878e8a0162a16045a307ac581d1961381e6282399a50d80238d20cd462fdc75a1b0d8eba6adcda6adafc3c2e3ae95e7296e7891d9750e45d64990dd39f37bb257f3241ff4d8cf314fad93d52a8734447a67124dfea8028b8d5c24195352a3d6fd625e68d0e68279261e2bc398206610edee3661dce0086fba56c223a5bc9a50d29b48c87301b65fe0655ff02cffdbbc700b697bdbc205060a2455fadaf503310e88e74fa3fdd343ba2671aa077047d6ef23dad19db0038177aa855049fa3424fc16f267826d230ba30b805e843763faf4e553bcd5c6066d00ba891678fc57417fd0f00d49196a57353b2fc2547baf70535d1bc560560b558c9dfd0cd79d320d20523d618339e2fb57edbb7b7a686b18bc93b454b9a10a74341b4abf864ab61d87016e5c33b61427028286a7c5424b506b1dd5b60c9f0b2fecaf6aef1425a756a3df7bbfbb0b256a0759a1136560b70389381db53508081476039e8fa87d635c0b004369130f4f3b89a06949e249328f59156798534b0db474f705ad99054de2c0a21b43564d633ef837a3a139a8ffd1474b16734cbe04f711b2cc5706e62213ad9182cfb1b9cad37330f64519e1bd4bb690a8fc33db029adc43ad6940294d003e30e41b430bd100f9f205ef3f31f4731a1138dbf01e03074af499edcc23d0e5984fa1cde93504ad03d24060c9957839fc0ac16e50a506888914df5216cf6953295e85208f137e16ed98650a4263f0287146a68e2a0f4239332d19750fff7772493d0b1ef87ff514a19711ae73f72a1dde7826165ef50f6e79d9371fe6c1d0e259fdcee9199415d3bd3a31c51a7a6bb0a0a3f4d9cca60dd3d4f681e31e15ddc3e9e8804f670c1f834da1355c5c4f5028392192f7164b51a6364eb048064b5dc0b8a4f6744031aef8c6edf2190606d18726083be969c2050f987837b96b9e7d493ecf8f602e5872ff78047a934fc9dac9ef0e0eaf8052029298fa962675bdb91778152c2ffeebf9145cc7540a43543d309183c263bdfd0b2a8e0b364fab6169078f25aff7135009a2543001e1e694836aa7943a68d0c405560d28191f3c8fff6fd395c5f7eda96af399eced6deec10167965db426db2a08ff70e1695683993749916867c97b3ad56f576af848849165068f79e38777af04cd0774200784afe48bf073fb74d862c50323d412a7e1bc674c32b60776f3fb098a71317b5eacd8adb32ee5148240e042d0700a1f428c50f7b3e609ecf2a4db0a657fb145fa1f1adfe736b7d80d4524af7d2950b7cec2b79ee64475ead708b0c9f4315232cd377baa207f2fa355bd979123319c02928b83e19f9d70d71127f0056a5e4ca45333938f272099ce59dcef02c7e79165256fdc361b632084ac90310a3bb2dd427eb86792f7679b5bc10e760356c97bbd55b1a0506ad8e7d4e33c780650e36f7038a4e8dc6db375e6259ef1d5276a30e6ecb54cfb9d9f017e645f9c03951ba66fba683f73ba667f207fc29fc9f00cf25b1fcc3afcfa4d5328731b1c0f4ac93f31c0408f636aefe67cfe7de219c81161f8e8fe2c9b90e943b52f6fe20403b65ed73dee10192ad4c2d435e0ad98f5bf334d70ef3bb2d2930aeae53239024583708f0e18f3bd776345386958debc874c1c14bd3aebcccb97f41d03d521092fbdd7443930d0e4a3ecd16269f99606eac76a7dd0e2b53ebe1b2fa074c28403d92e0d665ea5ddddd9f17a038d24400968e1117faedf21d19bbb069353858005496f34fa4e2556270d7b36a771e12ef23de2b99f6143e539c7bcddbb6087280319f07d7596f28dbd92e2e088c4d4c7e097b1df644a4fe8b5ed13b2005efd51041a0b516d727bd5b34b8522116cd9888e2b9af087e3cd90f306963adad4c6ff0f570a5be0559deca2fc2a0c3889f8b282bd0c6c9ac4338cc11336352e751ae3050d5b096a1e1f3fa26b274fd975537272c4835c200979007a25e8b0606309c700486a023c18397212d9a85ddccaf489028b23d5dc4fccf71d81985316b7b0d50b66ad05dc1dbb04438d3dc6cd3216d432c2b7c55df3f3478f907d98649b8f5907690d8d8dcfdf7dcae3c89e064453e2bb3e890bf0c0aaf14a23d73fc0e4f0c8081d854ada235fb3674352b26db3aa643dcc67968a7f44d2edd57d602850ea3d07eaab43411ae79f0d837d707e0f35b5899bd98ec5132ec52b88ce6f7d85c1e80a951800c0503178f2c987a605d5dce05cb76a32d2d446ca1666fd0500273b7f07f84384dcd9258f9dc6eda9b766c756bd5f057a9d739f4f44c478a4378385f60d125f326dafa40a1bd6839e6747e374f7e2c98d3a6d0131bb07ce591d7484ba025a01c6678e52c6b6c32cb66a46ba0d0672072071c5b703b303060620afa1b5040e03bdbe245c16e599473dbbddba16bdf9ee177efb35ec4866d509abbeadea0362c7c312112434f31d122192d52a4fa631f2650f5bea9fc6cce85b0ab97396073428d413055539bdca8a24b21dfb01be3f1f524e74389f03029b0ba04926230ba4002a7302cbb19ea502dfa663cbc0fc4e58b9a351a7e9f5ef3428af70a61c74d82dc342ad203d7d73a180937fad942d1e39484b7182759850c652108d6a2b0ebf192409aa1b8fd6767a2e05c9feb8d999013b72f9caffd1bad007cfec52c10c70e3e311ae9a49c81537dcbe68bae9aa05b08ad163c70e0135e1285f17e0e402 false +check_ring_signature 4256d8f3847ff3c6262cce6bab962a71857731b60602215fd1674638fe779b12 2ec7072dc255987d4ea9eca949d6ca7bf3eb05faebd542f6c05c4a139c8f13bd 1 686014b7da104e3620a0f37e37ea936482ed4d61c650732ef8e48c936ba088d8 ab113c1acddab0a102a5966c6f20d5acd0d45589ceb48a8985ae7f867d072d0e421c57f98c73fee959ef405e6af38474f31bedf6c2c7f43e25db9f7194abea0e false +check_ring_signature 7eff45b3c4aa535f6074660b8e1b7da4ed39925f34039591d7ccad842d9a935d 6108166d1eb05ca146bb4c8cd53d6f034bddebd22c683e0f34a799f38128844d 40 bf9d9aa4481db8e5411ea574e4e072c8fa1774e090a9ba79e78369a1219bdea3 a3c20c4431069b536520a84f5453c29ffe7353f3d99160da4079de7583f0b6b9 0431d62b3212b0e7e34b6e5b56c9765d923b32f0aba8b12c929f566610537bb5 f3cb114f717a62252d99eb4f3f7d11fb038eb7b55b3447345cc833dd9f891947 ccc56cadc9f4237463f0c31f2b431f49fa18a95e096e85b6a314a21d0faf08c7 2ff8fb48fa7387cefb7c21606906fa29437d09602d18a059b8395fc7ab660787 bcfcf53630e921e1c768297821a23f9510dc01cee339a5570e67cdd9c7a2c170 e0967ae5a50657918a4cf99bba08e58401139f2cd4add4b04e5b79d86618c980 e0d84458b4b74e6c97c6a0f762ddea824f59c55f1a8a7d23829aa03cd3bc9fc6 dbbfdf2c9e0fddca7a736858289485a42c4fee93cf51b39c803afe49932d066e e398f40da50ddb10faa768b0bea31da5ec1c887992ec1a92c26556797c4287a8 c09e81a77879409b1f1850419510396941c443354bb2ebbcd94626f601f4ea24 230511000fc4d91b9fcbea6f302763237a4829d9bc085521ba43de01e7d774ba f163780c09bda72c49a72834a3df21f4b88c3ecf24d2b7b6d3850aec487e4f12 2f27ec9733048dff644c586bce0d31cf0b2ca855249aaf426268c8fef536ee94 8631f0108ac7684f64cce6f0079d4172b7b3dcce9a6debf9dca9a6e4e54913fd 8b65b129bab47cee7cd1dd74297a54afc024cf266039f00c64c4407a57144393 d0f1da08dcaa279d6ca8c4cb76ed0ffa718c3105bb7d5540c956931b18dde35a 433d652dada59b463e6634b43b03cb43e03059294737fb111034909c14f03ccc 92158ac11120465f80f870642612fea5456606c36c14f289f0f4b78ab9512b1f c7b58461b34b9fd93d95621bbf5d6c32efc345b909d98754c138a41fbf0360f0 30521fad7bf9107384c3037c7d9acdc2bb3555f86faee07fb01ec186d6e1b737 c10134205f36117f4e6fdddcac725615b3e72f0d39bf5bfb040e745c9c962395 04b7ca3260597fc5705f5eea677e3de3ab19bbc803fb6c07add335cafc294a55 0c04251cafcfe1fe52f96220b574bd5b7ec970398024dbef3026da65553fe7d2 a317eb259ef3c0f47ba00acc885ae3679317dc22b421edd1be7dd023c7691471 a91afc41deb1a01ea8077e30b5e7d5bbc917bad32453f23222060f505018486d 581eada8a0c5360bb03e81733a5228647eb5a41e1daef66655b21980402ba5be b332cff43d75ae1a1c56e7028f0d0e9c22e383dc0ae725bd51c107f5ac2ee8ec adba18cd96cda3faa988761bad06e26a99d4799de9e44933eee61204f62db294 e174def30f6bf0e90c49e3a6e63bcc0de99d77348c8a50e1ab854b952fa1d863 9de631c24a5a121c5dbc2e9eb65a640231cbd6552dc16e0b3339ee7d3b6d2ae0 17936089a5ba20faca53de8dd122f1ae08a56127d8799223325ae289430cf9de e174659ef2077f6478896605366cf9cdf3a67aeefdcc651ae2c3b1630a5c9757 c9330c4ea5bdb5e8c8f9fb0b5f3f527ac8759f5888f13e92f5d35ad9e689464f 125ea1e957ecc4fd617d26c0753f9abb5f0038b03fa2f1b511bcf97743d3df5f cb401df9795cd862c26d645fdfa1e61ba0f5ed18c747f914cbb65d9a3c5e908f 63f40f7f9380a7f3b1b7d780b730842c84a4ae7be16c56dd5d05e894a04cf003 c71e94c23006addb192307ea0e1549eef4c4c9e16169d97daa297b1c75b0241d 4eeed2feefd1f454ea7ea57fc0e0cee558a4c20894020e2a1e0d3d7380ba3c33 cecbb86ba9fbb74a4b7f70d312f288c7dfb9c16d109d3f837613591a8a76eb0f131ff295bcbac6ce825dfc15c74c9469bb358a2cff18db34727484c31c09fb0bcbfe3d5324927ea8e6d3bda094729f8472f6819cce2e638321ee22fb2a22d30c76a2639f6b62f3df859edd93a6b474ca201b11315baadaf9408b0f683c8ae704898218a5de80cf9708cf1cb263a807a84e5d50ac560e2782cdfc56008f8aac05f776efc0ea06d555734b928545ce646f6c3abbb098efb0728f4ec9f3a6a0e90061b1442a875f3e64686dd55dbdaa077ee6cb62c04858c3333ed12e23a844bc01ec42cd36d6e850377091fa69a767d51604edbeffe858c25ce35372e9ea0e0b02547414a40f27f29da711f2c036724424de3ebe450d480c766b964b727d187d0304ddeb2fa81da12afd8a9514044d838acd8f6fa3de1fe3b1876a03ef95ccdb0bfd1c27249ebe07b9a60a1b3f7811fe5ed61b736d77fd85c328fa3a2a7b429202e3ba29345c23a610867699170ea4f0387b02e9deb165880f138e88a731b93409e060155115afb041f68196963c4d5110841082909b9245df9ab1acb9ead8fd0f69d3171be318788fd216cae4933acf883a0b0cb9c87bced6a0a8454a7aa4e30bbee43d074d9ab339bb63ce514aead847f22ea200604b264bd4cfd299d8aef90350033c82e5c40439ee4832415cbc6c91ab0a2957ab2f38569a76e203d454260b271448f4c1173057bb6a4b1c715d7d1ee40cedf4e026b86b24002e3aa3decd0aa5191953d3b26f3b634772b043ff416f112f2f7fee05c2bdd9cfe7323d7eb2023d714174cb2d47de0fc6d65563a36d61bf5279481df9cb5c550261b7d7f71a06c017d32c38084e871288b33ca541b7264cb52666bb62832f12592a6427e39a096c31801d702003ae4df00fc2b7c39fe5d31989ea116f7324a8ce88862ecda807771033d00f84095daac6c990fde0130a13a4beda2f8eb3bd01e479e9074189070af5bcc105c4709ef93f63e04ab73dea8d2b1375859c03ecd6b2c0671fefbb0d9a5e85a0d929c7b25192c4bb17544395d370624106e2595e2ceea3b33edec50e60208281886788aba1dc6eb48b1d83c6c6bedd17b027aa0ae6ae30a3956d57018a76a5788d88ab9ee781a3fa5b9ec21ab2a8873418c914ca3ab31444c7aaa6067bedb976593d2678b0286112d325b47ef33890cede3849bb342cc88b1c4ec00064efcd4c2b9e365b36090ccd2b0e78518b57377692ac3ce2b2759fa0957d2704cb0011619517ec6a85c4c38791a66bdbbb6f5559156db5ba7d55e380ff92530a74b05fd15ec157cb68f7f5b31e9643fd2c495c9bda363f46fc9574ed1d1ce903db62a0f12f0713f7d35a844aef872f9f341e9f236d9002dc1bf86a76a8132106bda766600b2cedb961ad7498a8ee19cae57fd08df2fd48506a38fc8659826a0a355935a4c2e2e706c53cdc934320e28745a3417edfcec0a827068f3d0df28506f573989ea041d70fc42318222191f7095239b871eff44e00cd16be6236b2d50dc0ab5d1e640d424f3321d46d2d4134b65a6b9dd2d00510e7db70f37d4372fa0237ff87019e0d133164fe423413a22f8e5185e3c3c358fa983d4a6ff911fe7d0fbd044045386846c2f60933f0004fc515107b776d601db858e1da68d598338b0906a9caf9b2aa2b5fb1d79ae0a90e9801663c41ee035efd9e3186084db4609d035885af2f4af426f02e6521f720bf676e36acce8584b694f2a0d04664172b860f06f6806d012a91254f891ed1e92dda420a7ae98a7e60d3f4dd1239396a3934076de0f32c4818863b42612d8f24a821f4976e6c8600466d2d90c83f07c5b9d0016b1959de75258f99721816b47d9f8a2ab1191c8707d463304a2a46dbebcde00dfea68c906a6fe2406983d9ac11980bdfe06eea7058658db84535f6bb5f2a6d04a9dc9954f97ee3959903746b874b55ee570cadef6f83c62f4e67f725333bc009a2f5fb91de47e71b1a43eb66f485c4851c16a8f0e4433c3a859c0e299ac32a020033e7eaa248c705c31fbe07d087bca147ab82cb49c20a116ec09a0572cceb0049f6b82a97bcd09c7f0d534759f6b6fc0dcad1c74679c1a757aec0f9c71d4f0089e37ab6156e37f7defc9629109edfffeca764e3a5788c26eb31d736338e2301e70d5c16fb1dbc422d8aff67483beedb33af55f10e6997422e757c2b47f2910f4570956cee998d0e08eda5bfcefb9d3753733ec3a6393951ba4319e14306db0972a9a6a9b14a7e91293f01f545827129b6bb8c3d4d6f7b773b6b64d283c8de0b5495bd32942342fce17b7676757f787be18db005950d3eb38a3c2179b896150182393cd363e26ec9fcbb1517dca665fd92c215db27f4d6ddebb24b7897cfc20ded18ee75336667af908ac7a69b06c66f570304928b1d4504857b99ee78d1040971913de5d5bdddc8cd157d5c44b0299a290a42126e0f3dae515a327e31f8c4059a8c8cdcaad5bc6fe834f2f736f707459f3ad16dbb57673997a82ee27e55cf0aff94c10994b28d3c66152f600439b1cb8cd261037bed59b23ccaf270d807390ee2c04a16cc441aceb2eb9b728cec46cda61af41a5149237ce1353c26b06a81049a7f596944668adecb50df36991c600f4c3e1e60893fe5d45c889d416fc40f077112594118fc699c9f976204aad3144b289c25137e6f98418fd9827da6c6c90b1414b7dd4f01db176d5cf1e3cf9b9fba2c5d0dcbb6b3c7aba2ba22b3822c6508ebd0d26d448c30b3e38eaf62dcad1f845c4ec2bc763142e61c6381b04153620f2d91d4021534117ff0810390aa7c510210b87d21d9e3a82a6844cc3940d25b0dbc0b271ba56a1822aedf2df7bfc395084fb508a0ab3944ea42dfec32c7e3d201a8bc19c1f17b01b2c9a1f1ef5d815eebc549db864054bcbcf534be1dd721390926b8caaf4587550ff660b70f8bb7d877b12170671d5163d41a8f6678c9bff50f4828858085a0aa2eb15530c9d78fab968b9e2a03c73668917bd80ffb341f5303f598dae5915d72ce0cc0c6946cb86e650a74d0fcf1dd0680d16b6108fb284207a3ce42305f5bf1454e6d08f38c33a52a0f1b25e99bb075ce39e458695ffec70f76d946bf8e69b37812b707941297f6e42e68b8a61541cfdee25b8f8ec72a2400612980ef775d6ae13f1d58c67e583ffb61cfded270dae3605bbdba53d75ecc0b8cf8db2a39cabff68774d71cbc045dda5025e19235b88b4f9bbdae0ebcfbf20d9b12689a667aaf60d68704ee2385c52727daf7ba382b4ab984db33768cb3920a4fe136f3727aa3aec54fcd7a26a12d8b12a304f76b070013d25e9ee946913a0e06815ecc653bc36a3f23947b915e6000a85c35c64e69e72f996304c940cb4a09a10387bcf448fed69f319ac22600c570a2d42cf466dc62c3fdb9ad32982fb40ef6f3cb8f0260bdc2ec2ca254c2b989e57d8f0e5ef34bef59ffbbeb279b3fd408a2eae34f098b953eb212c48de4179a582f5b37d6a1a4427b1be326e9d6988c09ddcc21f2f7b67981be134e3d91495e71e72677af311344c456d47e1db71ceb0e934f3fd8bec2fcbfca5f2977472a8cfa83c236421a0f21fce883eb9fab7d4e05 true +check_ring_signature 37339555df39686fe7a07c5a25718a0be1741cbd4a615806e69282e09ad7fab2 6a5a6f0953c66207f9e8cd1755b7127bbf14fd1261794084f2ec3bcb5b0b7c6d 1 8831178f7aeb095698d81376041bc0eed15293cfca6787c24351cbac41a022ee 85f899d8bca1d662d128080e68abd0707c68c570d439004ff68092f4ca8a0c9eb34b7436e984c7987139190482ff4115170d0b587cf5955f43bf90138876ea08 false +check_ring_signature 8bfc0b011b179cbc779e17b59a40714cfe6021a14a72a1d968dc6ec07bcdbde5 ec3a9a6be9e7a1f59121229611929c57f1e3a44c6578042506355e6f78005eab 3 0788dfd62a39c4a2ac3b5d45c66584677824501707e585e2ca8eb164e2e84b3a f08527c11e3b88ff3aa6c28ae6292afdfb5743bd88f921e7e098a87a82c1f46f 1bc34d755c720aa9b2871e1ec06cd9a2f6e1f241632d17c34d24023e91ae7689 7cacb09537037314edc358950accff851c9d4b065ae913ddca87628141e81c05ba7e06090b49e142b049a08a90feaa864496d156a2e8167bbcc661b8a8507c05af7deed132e374f0f7274bb066d5b15a996f0543d06c5f88744e2a2e408bca0ded7e79c8a87ed7267f21fc6a994c94702497977f8f3b301f32fa4a0c26956a04ec5a50289a05e67574d0db2b35a0e646cd091f9df319e7c1823eead526c1f60801e395ee73f96d35ccba197eeee6094edd65583c372b15dccccb697d51e0750d true +check_ring_signature a6802ff4a48007669934af74e90e8c0c183abf6a73c9a0567585988b82e9c513 ee95c3a65f57c750778b9fd0b2b97e2e0cdab90991b66e2067cf9db48b6a5d62 15 6af7186f66fd3b3417a3cbec6bacadb127ebeef6720a7e18f70d61c7c2bd88f8 d61570fadd1e8573bea02528ab9ed17c2d15150109f5439c3e96d56d63b78476 a76fac236ac443a069bef151e9e031f6e86f5d1f00b442082fe704aa44da7840 d22f96b27aceb557d93a97b0fb5b5aaff95cc828e6e0efaf27c3f16695230679 2dcf049c1d7d018d54bc9a6cb9dc96ad4cbb7f92045291387f025313282f6b7c 060c2af0a8bbf1267fa3916a5ed66d0d90c1b35f08ad87f489a1d43303ba9103 a9dc2a535eb0fedf8ff18798dc48b02c29be940497eb9b5ac664ea0f5532c15b 0a1d571373889e1d1925e1a1abdee9768d66c630029d3e649a11b7ac20b0f6d7 9883429f6eea3e9d4fd812f7e98b0e95d22387f58da74436e8dbde60bd7305ab 6b926be5ada95ed7a666960814962eec460ec43c9f93f1d35776d0ccd9cb0f15 838b9f5dbd597ce3bc4cf0b27ad8846aa454aa29068d4d74ffab70b9c9cabe58 8bd59a32fd112477be69fecc4713d79668b6c0881745cb22349d10dee8ec0775 7c0a79739601f7784d5ffacece15ab0793012c2fcf1021f12a91735bf6c64d90 77a789dc0c3c8bb165de49358ffbf79f29be0eb0a18582806e3c2a18e9a2ca66 996af27cad8df588c9bfab40b9eac95943517f491849981e85973cd6f8d84279 9b58d573b4144bc7e8f9c2f5c107d7a62b420290f7ac62d443cd3639c4b8d000e5af39ff429446608afad6955d58242a4bb60366ea0e98f518e777275fe09c0b290051f50a6c1cf0b7833ccd283850093b6b649c62af56d174fb0d2b9821a503deff703c236a0b8329885e58334d41d570aef47005fa226a30834858188db10b3a9f036cf8491deed4897071daaba524f068a402e32f1d82fe357677c3da430e95306ea566cf7f40ca2f3d739df72ff33e8e0864ed61c2da49842b46488ac505da158a3ca40a89d85056bb2a2d9fb1497eb99d57e840d2683d28006a142cf809f13d3473ff2b38b1f7d0d858a28afb02bbcd4c0f30e762777f04b22dfa706309de64b32cdb0285702fd340bc2cc401b449ef42fdb4283f3db738ca924cb1ee0dda2518db13e73c5fcae41b229d6cfdf5b7f19bfb1424568cbfbb4c1601db620c483ae957ac7c6d0c01ab053ad88fbed8436c5d198628d90a900eb463f5629c0020290776133d5cc4c7b653963fbf06b26a40d8b1555ea154cc1a6d08ee36940cf18026582bd9da64208c724fd39d247bcfa21ef7875696052f98d37ce92a4b00cef31b62b8c7fa92eb6a13d2070f0599e701a273a45e205bac3f211f7485380cde1ce869b93ca1354946e0e0bbb7d749a7b9377565e2135af5132b7e1aed9209cd3fd94f7e939464c4141d1477754495b7a10504a9ebb6fff2dd6e4eed68990d9935e417f01e8c6a4a03114edf5ecbe81a90100761fb0eadf7f3d150690a09037c076f69f83f6f232099e3acde077ff407daa7fa98617c687cc39609c8cf3c0cc4fb43b0681051a65aef840dbe786259cea0aca29c3e991ec1382b269c82120e4eebf4fdd3c3acffa000966b0007f85e9f70cfca15f7a1f8ed250a9a3c97130d8ed84366aaa90167c0e55e27b758b3c5f1065c0b93c6c36705c99dc245b2a30374b791ee4ad4fd0bb02e2b4044b23ce9386fa33fc6700319b959d565d68b800c4abe25b06af47ab7465441e9982f4ec61e295fb9d55b44933d6dcb0c4206420620b14c358e52287462193908ba7d1b78272eac92bee52a771d610248b912980a75c49cc1871bec878fb76b0f929bca60a27247ebbbe17d426542ff0d6555a9030efb636129b63e0333e12776960559b0b8c400c727730531be668fc01e43bd09f4d3ec6f953d08e535e628ffa1443f9e46d81fc79e471a588e7b5a52723638008d06f6284478009fabdf66679b05ded3f261f584fb87ba8679cb8084f8befd0ae3ec6c9ace6771488ff2e05b1da278edb0b0be02107946e8a36ce23749b5c401fe2f68aa37d1d4e3658f46e840a9c7ddc7b072b7a4a0db39d583555e0acd8405 true +check_ring_signature 0ffa1e39637ad4db64da24e44e3412be8ca486fffaf669b0ec81df3f00b81e5a e0bf33b63c2fa40354ace0bef1fea49031e855bd6335959e1e46efd06923a8dd 4 08b8d533a24c2c96a63504c871f9b664c6bfec94fa363d0a8aa3f0307b9c3804 3d42dda7e0314ab461d1a2d538ac1a4e8eddb4491b13118c5445859d5903e251 b9893634ab603ab62eeeb78c0e2a08aeebf936523e39a3ec0ca96a2bbfa684df cc9a5fd02a85e09cf57deb9745cb54068fe358f048ccaecedb0189f6e4da97ba 45f9c4ffe39cb5a16a733e5f339a5e6149a0e8632f78a425b06164f73a0def0136b72f00f10d7c1c86253f8a83cf64f0c55f1f9a8589f4a4953697c770c65509365a91b3f8fb1587c227e54acabafca5c03210b8d2782264fe9c36fee45706040a9446575129725534a6f948c6a51f63bb3fff36698209e1a7dd5ebcef01210920cb2b0424a2c8b83dd28ebb1b017162b7ed0eb4c562522c639143106602a20de73c917cad2e407ee00e7bcb8f75607e4df840060a26fbcc59709e3b2fa1040737bca7c8f267801f9a8456b3d055d56d2438174f3628055c3a2374503819720c83b47c3fde8726a57b363c3c47b0948d32135768d834e506719d93106231b606 true +check_ring_signature 954fd6e3cc913c5e0fbf8fac1559447a4e2bec51e03b85acb730a38ea753b9a4 0009d94428942ea76a845e0912487c1b2e9e86cc205e78cf8a1c664830655ad6 1 5f6dc8cb7af69b2b9a55e44f650aa5c5d10ddf8d48021b7b2e3d9866fde0e2d9 f018d7ab1d64d9209bb5095868c00ee503c362d137eeacceda80b12d619f0f0833cf37393d5cad3e027325798aa0d14f3c7ec10609bb307268aab26fb425450a true +check_ring_signature bdc144a780325bb7ee9979abad611946b97f5a95216b0a9d8dfa46c7bf1caa71 3d91759fa6cad9d236fea01923222f14b28012d9947402f38b5c660ccfc604ab 94 34764856c24ba0ab333d17b0dc34fd3fb2d5f08e3da0f389d3731f7efbb9fc47 cdc260dd65da9f1bbf6c16eb89f740f682be1316fa7e57c3b251afb2f3816d59 01819d43c6f8855957d290824f5906a8d075d9824ea0d201a69934c31931638b 774388845a9b9993685908fdb1e552eb9e3ee6e73afe871d1b812c8eebcc68ac a5b339243985be255aed460c0a59f82b919ec1f22509782b57f2c49f6156ffef 30d14c4654ac7c2cf7ee588cf44c93e094399409fe2e382cd97a859dd82252f6 09f031f48c91fdceb14676c60ae3cca8014c32f7eea6183c7167b5a5ac1fa7d1 0f1133ea7181909ae0a88280f722eca4d4b24fa07b407e5c95c49279ab1cd412 dbc496fe3cd313794e6087e8fbe976c874a9cf35293b9b8258ab39be5a5e79d1 337eac97466cdbb02a142c592f12d5214fe6d956265591eb38adb2bca29f7ed0 93f4fd7fde60e7c896e6870785236458b6fc0034f9ff6f87de51aeb04b47137f f51a9811e087316caf9083d07a57d17c845464849b624cb8cc0f8f6e7bd75aea 1de4ae4a57f43c73d45b11613008051c68fbd0bfc0be686c9380ae5ada45b918 0594a057b2cb214f8b3d96fc7ecf1dbef2d17958a389eb56670148d5aec0971e a4c946288b65d2aa3b9a207435a98e918f21f7199311724f3260876bb36e53cd bda98e9fcdad8673483debd3bf7dc78061e3a3c9908cf16362c2ade814fd2c3a 37a79f4472940caed5d8cbb25e26830c68ec0f71044dd601def0f9cc46d87f10 7380f83130a1c7b5a15f2f89b6b257b02f00a43fb699954ba86593a938e4f73f d9dcb2897c1f59c10672a40041cc8cfd74e7cd7fa5dda62df8b53fdaa1e6f425 da3abd8dff00f07bbce35bfc4d8d7a643b3cd26ce5cf3913fefc347e402ba968 1d052d809ebf22cc3d88ec40686ffc2c6af4d809ecc3ce0c35c2033739d9b5fe 3dddde01d64c285a1add2423e8aba8844997117bb137748dd692301cc398948e dce1ae2ae0e5a70e51a66a603bde762d16730bc4f60d68523656e5d55375b912 da176c794a0bafb71b36771d6c1ea9d2595e52164a461aa1a1ddb944f11df271 7fa6df26a41acf45f66ff87ca69be9f6f7200d8dc289d4f9682ea9fe422cd3ea d8470e2bdecd21551ca1c85fe7584a697a8556bc87a0d14f8562da655900acc9 7ce06cd4477f20b4ab0ccdc469b9ee27a9bbcf6d7861cd2a787256c043cc4ab3 68c798a73f7f2118302bf1a29bd0d6e9387195f5ca858bac7bf768bcd0190d96 fc79074b17ca3600a4af5d96deeb91208ae341222c67fd8d96dd9edf20f045c6 2ae28aaa0f0a5c6718dcfd19c0f9f8918db534c20d61f23b00fff0b71dc5d523 148521a42ea1aebb02bddd889cbc1a0aace032a865e034aa23bfeca8dc3f0809 2b325c6afcf174486a8a39bcae745367700897413943ec303bf4b9604cfddc82 2435c2247d47ff112a9a32a7515467c96f235e231308bacef54bd4b94279b1a7 4c697709a04a6f69078f2f8fa18a68cafe772e0de92b9969d1e3500df0754142 ba993907daf894be0cd0810da5da1d6f937c54406b1c80bd3ee08e2d7cb0aef1 968322ca99495eaa62ed6eef2f8ac0fe47af53ba7eee44938775c42b26c34ce8 c87b3b11cb99350f429ce5ea3f9805cce873122f92be205bc4b179a9dc8571e6 f298fd7d7b068b6f6194606f9c019b0d80170b6023c5e213f215a8e36040274e 89f4d67eb23961fa01c650b42266c694f6f629eedc13f3fce7df4a1e0d02ee19 154485ddbf731a59ffc699962b11d74a95cd903bcdb234de44906f2d394b5e76 4dd83c36cf594c25867c20308624068f90d83a30622859aa0b44ace199cbe00f 71322b736fe4cdbeb1dc3a744419db40200e979bef61fc2f75905f20304ea5a0 a70bcc5ca9564446f28ed9bd6988870b191dfad893f28a12535c2ddeecf94ca0 b46a52d019f3a5ff6228649b84742138d24bd487833db91cacce65652d0bb738 342e3623f210b90048ae8e32134589eb557ac5368e89bd87cc60977d06be62a8 3bfeaa23cbdcc41c69c4f552e4c28cf2dd729a69015a40f3e85aa7181bfd4bcb d0b1e95b4c44136f2b4306fd0a2d5a4b45ecfc49e50a5d1121944fd270663d83 6b6069e223536d8bf326bdedcfe717cf46139c71b27c2eecec1b55748962488c 5ac7bb59a5223053646503c714cb49ca80ce5b4a9c222efcd9d24c5c4286eb70 edbd8ad6edb38622d6a4f3cebafdd858a566f04718374e35d68d6f45a388c439 8f468af31158100f85543114e1ea27f37c14bff2df10132f997251b3f0f164f5 be866cf3df99c7534fc6887f09d8775a6dfa997a0c97b6a905a7c7ef4bd883fd 7965cd16dc530b53270c59d49f1801611e52d437e8d47a5f060b9e66b2793e21 e4aeb759f8b49fad39e7c425e14f2028c475e69b91c1c448a1f19fd1125969ee cba8784ea10ad498e77d131e33143e387d698ff67a61780181fdfdfd4ed438db ce7e40965c63d5a250ccac698105ecdfbbad82669b3cd7d5cd5bf3bd094cffd0 05de49dc4f0aa14e1a35481871e88a1247003302ddc0cce2a7bc62d740f00b5d 2beec2f1834c0db1150c5ba3487fdc9182db0c950c0debed4811939a47968c06 d5883230efb4e21ea8a63bba3736a3693cd0771a1f0caf5a5e53cbb4f5bdb6e4 7b7fcbcacaf415b665e2ef2458d49bf9891ee7a3f2c41440b8d8b46c33b16938 cc39e8d6238e04a348802dfef55b514a79af5bd4bfd4b07f30603a079294fa57 cde12ddf8280b4a548d287a80e3525fba3347f18c38ab04a8e15856f5a1a7d40 cc7ab7a1e61877390cbc0aecc59f2b95a02d8bb191000409bbccbe034be4c3a2 8f6d1618ff961d87af7d3537abb37fc8a90d9c9e5aae8a5a4d29d013a27b6b5c 48e03edc6e1fc7bc8ee4fcc6df84d1ccf2c3263f94f0c9d55970e1244587ca94 85410af549482a09a1d59a56f416a887f07cc1ec55dcd9155e219dc2989b9c45 d602d26bba7b10f016b7ca5a1036ddb6e5140b9ca0e3f9843478690c482ef884 a5f19841a9253d9c14e85f8f3926089bee6f9072e14f897bdd021c85ddf5f9c6 c39e24f859b06a8a050fdca2af45e574b1bd17e690354f5a7e1527c7af3b94ff 5ffe61f3d78d2292028dd471ea1ffc7bcbe28a5f9584a51a65201ca69e6bc5a7 33c568ec8e023565e2a439b003a02d0dad143aa1a715821c8df43d41dfbbed0a 993bd527d0580b1b3735160478ec099783491a765c6120c53450ead3b1db73fb adaea6ca399adec176824d789997f01491682e9f443388cbc7bd7b96be0f15ce 297dfa9935080a9404292f35c12a9c0e8171f04441d3f44a7a36e1ea382e7432 75283b8c733c49e091f2f926a54536ebc333e601c1e1c117dad9e0e5061cafd4 fd3a3ab0bf9b6b32b4618f55ceea06c91c476b117492be80cfcc999b64dfbdfb fd88b11a7361faf4596ff73110cc4548ce199e6a0e1b5a952e3731dff80bf211 f2fb70b8c5da8ce630f2706e2657ab061b85ed398d42142b55a19b5eac4c5114 f8cfae87c3284030e10624d666e4fd95e5143bdad411ea378b6a9ccb14435117 a1ed2a5eb228172a8da123c8764db66d3a702932f50d8f5b8d4ea48ce4b95f24 c6cb7fda30e0054d6e58263a78654a218be91c10eed66d50f6fd0a5de47e2515 162c8c7a8ce1918cdb4365b79eb0ca042fa8c78fb591d26a3a5159c5c45ad380 382e200d5189942f2049497fa5330b619f6b5fb6adf0e18690e22618f60056ee 79f4b0704b6ffdb8f6b2d1433a96b82aacadfe97eeb749900a34d5ee07a4bea6 d785a629c3e584530ca50baa3ef22a9c4691d176d25234f3f543df0b7280bfda fd71c387324d172650122b49f88510ed9202dfd45168058ea3408f415a408bed 7288be8577227c03ffa722611e47cbf4287d0070f58db125c1f270b3f9f58177 66018736675f0cefe16d8394c6413e604ec94a7392be335a5306490010445752 9e8716a7d62b39645771b40b1aaf6a162b9409e9c066fffa3977a04cf8379a19 fc554c6fae443c40e412ed2302d9f9820a7e9296be98dc9a74510f69bf3b356a 7c7fb09bb83b97d10f6fb98af0536bbf716aca3ba2e4ebd2348be2e870dbf1a6 cb929f036fe3db2c21083f736f001df811a0f2d7a51b47f2635a09ea29327d35 501ee58b8f379a97c9346e9bed9275d393211f93ff742d7035aea98974e03e4f 98af1f9d26dc79cd84390850588f3c1f7c9e89217bdbefb6ecb09274a5de1ff1 ca4cf2e9ba9875c7ac4e6f5e17586973536001dc1db0b38da7d56bd928117b0a9b655a45405e3ff37b23247c13ea949ece7f3f057efc845610809e0fd4bab10f77575263c27ee989c682a2d78aef86a5f539832e88831818fb6d41365ce536078235bbda468fa6c9a6259a6e2957177c7d06ee1890b049e3ef99506253729000e12e37dc4047a68c954f2b0cf1b4e1f5420ab61fc4feb9c1164861abd89f5c080ffa40ce5c127790e8ec117212f96147f924fcd7c04923469619448327e61704f5bd0c403ccac5f1541a5d42cfc627816b9e157ce647860c4277358ad1a65704407029c7ab21d95163f0d3fa53102a2ee85a734b0ffca641d504bc816ebda507dc1175b8c8b878c543fbce81d19293e8e0994bdaff5562f467b2d73ee23213028f61394b83b7c0cdc0f3790e525ab272ef56c3810ff3d83b7547a1bdf29c400d403279480c6f592ce9f6ddba74acd943542114677b107cb8f370b08011cf7b080a168d722f0abbfa01bb16d08d653e5b39f4c2b333a9305988d1ec7b1760830695ef09999c10e6f2836b008bea9be479de536d7e1a7300a4d2b96f4e1889e207841ea7507a5c5fd8c538df68378576667315e9cd759e4aa7a8772826df44a20ed4f54923f24442a9675539897110a5ee5b64a1fdd13a1af946f3408e6ea3fe0d1917bc7246571b8df24b24a87d55faec47f6e4d8f5c3c13ebcbc4fcddeed0409cbcf622cb16d05aab5a11e31423f9c63d1d65196515ae5316ad8c2e7b4381901a0d4efed59c26ec4e8770735ba4df839635ecf4260a86fee17d5fe6965631a0253d678e9b08940fafa03abcadc23a59c129d386fef3119daf500593de242d7064f635f3e2dd65836b5339f6f91b75e0a25a7a92197e99174e0ea190b6df71e05604a228b016c4a5e7a1c0d17d1ff9dcb8113a51b5988ad37c89e1099b8cad103fe59a4e0ef2fd7203aa6a8c09c8e674b3c3dd88a9ac4339b499e32de0dc8d505cb7d01a103d42d128e39aca9de5ae57fadc7acf1829a88ecf984f9d0aa539e07b2dfe713d5d3efef2a38b84121b0d45aef452eecefa4d5bdef259d3a3cc61a0cefdf40d52b22c8314ff3e688b9751f859d9d8e84583c3f859d0bf3b8a5449b0ef3f724032694e297aa32a8e7803bbf7d5fd2e85d62fbc15658ae0e0b49ce560a799586765ed7cffe54f341c04abb087a75d1695d6efbd72ca4504bd70c11350fb698dba6acb6ec72228318309a377b41c6d32471a9634d1350e11f89d58eae03e77c287bf225ed6f5932df172f7519525470a6b96d6ebce4fffd97d00334e7016a6f9ce24bb3f657113cddb3d6dff630590a5137ada0153a6c9f73d50ee61f08511d2d7719365be23901ddf9979068eec3d4c7cb4c79093df19c10606d2f9508853c66c51d6660ed3e0f2d25e0a177582087fa0bc42ac399cdc099fd06d8ef0770cf9f300142e65fd23a358b43eb3284cafadc695ff2b126d1e9da5cf068b00048d8732f6c7bd34cb80b713513d1c9fdc7518b449ae0269167b4fbcba5226103a43882327da5585e4bcbfd9c29d9f5c3998de9b8cb6e43166e0e95a17def0f08043ac3ba48fbf93a5131d6acfa2e5577625d185a3e1c821bb44d738da3ef740d6db6d3fa4416c23ad3c267705798f63ae953b6b5e9f3fe1a1fd0c23413b85805517a2b371ffd9089a24ab4ca5ec6897ea0628f89c2f0ed1ce12e455d52b9090b841f9ab28a95e2876c13aff0125a41dbca532abb0b33f95ca3100b1c142b5e01ff47b102645affebcb7d111362b1964e5620d575d051516b94ebaf9212a9590888a2ff4199ae059e4986237e1adbcd0d18f898c4ccfb657e07458ef8afb51302f4f21898e69b56729f263b2fb8ca07d187d2659f72b020f39dff6b1839641f04931c7b35747238b6fb1d64ef8a0699f37a1ab88aecefe6abb26c4b45150e7c0ae126f4bf622e03efffee687068a2176e3a3f6f9f73a32d64ce3129c521a02c0e84710fae750240a06e93f89eda76174544d82cfd4bfb12f851d68e72fafb980038b58d145d748d02877ec9f722319d05b53d025fa83f29bf4984187ec2d64701dc3668ff261034e5acbb300bf279bfa7891a409829b89614d7b647629c1211079aa9f06b37df810fcee8d50bc25726aba202d0ef4268eb181adcb6dff2a43401d44f8cbd15217868e3f8e85d7afa3d5b2808ba5ad081618574b0f2dc5366da0899b37962466b5409a3c7b831403fe1c2a9ce374d2744dd083752bde57108a006eceba6d49c365b237e2903879a01b5cd6e64eeb78255ef7cafc3d62f7a621708ab0d5a1c2d06de260558f89edd0cda4082efb8fe1f87fb3d83e2b70fda49a70ae64a64711ac6de225cbd1042e3f331fd3a76649f779ffba5d87190baaa81bb0e8ea72b7593c6777ffbdaa640ac85a030b0a6f119543426cc88afa8299d3a9f0a16b58e37c7224325ee88ace77eac03ca5950a5e0946fc88ff831a89e1d45e50706d59236eeba131c0a576294d51eccb81c4f0a40c491db7133dd2bad8ebd9d072d5d384e51a5600437753eadb7af6f9deda9ca0a090b880b42ea41014177e20c9ac10a01afda0ea9ff8fc8b5f87a9c1d1c448959f77828760d5392800f0655057def51b8401017982b971bd715100310b96303e1294189d7e41fd9d10313b208c92170c00adada04bf09ebf0944bfee959cc55a74dd3d37ccfadab532eb73406573ceea7b050ac26bb58e2f810c44bc7a8d177a9619f59519f7f32a01562010af5d8f82b55733919ee99cb20a23146ec0948fe0838751de35b36da938861320950945dfe338f79da1e77368d6e01099fafc3726415edb8bd539fd81ddadafb0143be4506cdb50c7ae5f38955a8194368d50f9b5249ca22e723e0b8a3f2a3f60ac9ee9297978c1015c61aa51ac4ae6f65c89023fa61f6bcf9d5f0150761b3f507c200d71cd2bc483d109252699e99d2764295543ed77a2e5d69a8b1a32c719201a010cd1c30020328175776a0a43d2bfe61586f3f644fd9b2ffeeed73ec0c42056f1bfb73d6acc83ad19aca4baaac3ebdc53d3577be4108a5a10eaabe64527e0da9c6483afe430bac3e71fe29e4178adda57d2d14d72d2d34a7e802a31c0e050aebb784ec2d0997358f30ae92b7bac717ec9d7d1ab03fc3b55970325251e6d20da35e04566ff5174073b062c5531a45d2bfcc430ae8cecb8fca310c4fffb925018416310f9f188fb6c69f4ab25fab8243ba2ae6be65f064a56cbf0b8b8dd57505993176cdf1a64b12faaab72f52450a4b9708f0b63acd2a34202e5bff6831470c06b635e9fea1e01799de2b986afeb611f869d61f565c9465e44a58b4afcd810a91b62c7dc72e23b7b88e3b5023b888694f76e5c97221e0044d019082b3daa80709901fef433fd85c30d85cd8d35707556ddc4543e7dc80c7eeb11034cfc29c0609c3b0b125488b860b4b8b81e6df93cf3daac84f39ed1a31a4d27dc3dd0f120c1d63c26bf11a84288e633f54113dc2418504ae6fbd5269fd7094801237d4dc05aae00ff41387870ae1180637a97ccf22aad793bf9b28bb5bb93f7d99b0ff0a0d1abe71590411f48457478bde1563294461b5843f50b5bb03a3c1d09760ead8084c81ba00725decdc22a1e12c9b68b5b8792c9c3b40b5617576270233d21baa004c86e7da1b2115d53ac540279d2ac6293e573a292442c8e740a4a14dfb90980ee5aab8b1812c2a853c8fa540dc39a34f18fcd8693e9da3ae8ac30237194e160524762b450e00b44dbb3ad206402739e480a9b26c491223b1ce587596473f6e06f9bd598870e83fb7ac22888cfc6a005e7303c67643a5f68f8043ecac6ee83106c2caac2ba53aced2030f3a4dd5940a36ddd886835a5a16e4c328dfa8570f8007caf3769e27515ce245535c77cc88373fdb93837b27eae3be9dc287601c86e20f0d5a7d7ce4934a35249d316898f20330ce8ea4a468963657acc5ff5b96c5e4041ec2c45a97814bfc05224f0b667291cb826ca538cfe9106c19315b54ed48960988a81ff5093364603590b9fcf83fee4a26b83bd2c1413114173a40a8a495850d0f038ca70ade327b7f07b400d49548f3ef37cceb1f672ec2beab0fc00261ad017263e8ae1665612c482a8ca752b0c20da9ac74deaece88c18ebe3d4ba943db0731f9a12df296e1cc48a380818c1f922020a22e483d852722b68846a22941bb06fe938b8952835945ad79c473fba415ca480a54e15fe9f3d3690c7e9bf6ddaf05086fb8a2f311195e169f7ac47ed28d1512f21667922bbfd77c69f987cd3c050df43a44a328d8eb2f16db36bac1dbc941a5bbed06aef10e8ff7ad5bfb7ade2102567ea4ff28a0b50adcf1d6f9b9f04c4b69ebe34178b718993be8bcef38f76407f3c549f47baeef70d2a26e85d1455a42defefb2a21123269627ea54de6462907deb9e84f0ac975d181b9daba73b383f887ca73232869f0aa817d5d9b84c6fa0d111d07a0664c13aac8fe76119d03b1ce75eaf2503408e29f640dbb8eb61e0804884098b914a017d939a04a1d60960df1667fdb44ffefd822748b8cb0b1acb00263a18e9dd3487a53cb0524779ec98b13c4f6e35109e1771300b59ce6ee30b7002bd76b134aa9a39759ab2a9ad24c0a3ee9a4e4d9b6bc99ba79b6d9793856c603642668a3919951931dd76df97dee9aba48b311e06dba529435fe63c5f0de6902047bdde41fe58a0ad6b6be3f9a7628a6209a871ac93f9a1a86c683c4d08cbc060915727df6454ce03a970a3232be0f25f739d6b0091458cf8394c52b03f79d00f3dc9d4dc85ba58f3b3c3f3e942e3a3247e41188ec91599116c370a5277d00083590f9c2574eb67c372e87a1b8a94070dee408fc35f9ef17ddf2c352338d930bcc172c225c073b6af52751d9207bc7c9171bc4175c464591f2d15d88cd303306f68fc8b1314902f6b33ddf33061bc855e04a1e9f217af1919dbc6e53bb4ef5035cb0a24b4ff1a33a8aa087d27e69d1fef3c27a4129841abf313691fbbf77bb0b03823adb660dcf458a8e717be0c2cf7a529b82ff6f5bb8f1a77a2872d6b7e602c3140d13ff77874a1979d8ca03aea32201acddded7efed0692931966a857100dd9eb085727a03507dbb91ced16febe6b8f4b37678bb175db65dfc67418f6530a288b5121439e87ca27e7bbdbc8fce4a0fe9a590583830765964fb0b1efb88408b94fabfbf0758e8d2dc4daca5858bbf2db1d9da543c99c7fcf2bc98484522308fbf5cd75f87ec3b5c02882fcc0d3199e3cd9806ac93e419d96b1f62a3954c20822a731cdd084013883e372ba83cf6054d1b645d7ed8a1a7e105ab74ed1c06b01bb9890548a8b4ddae80f4c9fe0b5280b31d069e6dc5aa0641bd5f09544c0c6010ec62b2d89e51f6de05201b184048afaf0d9a9ee1493534534661f4b641ad40b6a0e10c10a22490efbeb5da50cb640c2ecea1815f1a5f8b250bbdfa84c1b3807ff2ce64b4f3d320f8e22ca9f737d071d07ba431089c4e4191cc45b821ed91e0d4a3821f2d6059009aa627386bf7ec37557d86c8705f7490ffc501e7f946ede0e7eb8a022868941d9893a710fdab3e03f51ed9cd2ed052d574f306dcd8a1251087b004bcfd49cf5878246c998b0ac47b99589058e255943a75d109630e7ff0608d83274906b8c701258a6b7c5747ae636d4c16ac3b4434f94308706ca7ae09f0f0ee9f7a043fc5215a709f7bf781e39c1690cfb6c2274968ad4010a7b0593100aed0c0988c2372a6f512beb93fe4de49c85d7720c021714e0f0ea0ecc244c9509fa8e4597d34820189e90f401b4790f172121a8efc7099c4bdb2dae47f198ab00196b2e51d91b3596c149d08bb721310fc1550ffc008c3914ab3dcd57b0df000769ff93cac5f87e001fd5bfe8fb533583ac2016754d83ee2bdf27264f28cfed04f634c48d8b711a92d20254a4b239f8d18708af4eb0dca55f136422d85be5f602f9d5e5d8cc13b2d6ec9a001cb390b408da572092b5b47c1e5726dea174f4a7055b025042e037a6da883fad4dee7ebc5483f17df5dc5be47f2d19be56cd96820554c8ffeaf7a4f6b7922c86af30d262bb44386315c17d0edbefc8df1319c610027dc38100d12d6ec68650c93c1bdae6f66d03d30cb4c42b0c0fc66ecdd58f180a275b8dcf9673eb6368b8ed49d822a50eb230a71f1419bc3f9839c04061e8f405935e32ad90b6b9d38dcae803df0127c2cf906f7eb43024337c81883c891a2106e047e6c46ef28713fb07aec9965da068b2900a85157c8e077543f10de9621e0be00ce817ddb1a9f1a28da2d6d123da12f3f2190b3bc7e334da23b5df142b4301cf5de8acb9a272c1ba2c349cd87944d8ba2cdd2e0bb7718b30769cc948616b03402e2ab4934969f1d671784ead1829181c3796984055924b943a6e436d324e0e729d14e24a70f1f177565d9b6190d87516361af1336250a87ce0ac729278e40177f8663bb19232d80f6962664e7b49125e33c768241950c4d4f6a44aa263100ed1a12be7d0c7d26713290c2abc6dd9dbacb7f7934df12bb2f5139c26ba0d050a0650cd3b3646a1afbe8a4cb6a89b2a9560f47b42dda7efea4e0d3dbb6cf99609815d642f32bfaf62388f6a4e9d056b3b3ecc3b863252b51647e855ac53f22b0930f057f146a68880c20ca34c2383bfc162b06f5d06501db55bfb45d9838d110aaf455e9dcac9a80fe9f83c2be0144ca7a483af541d5012a850578f1a0863c70a9397f6ff3b71fd882e71ead752b8dbb03c5eb58c5068961033af4931544bc308c60256685f3f237a227ff7d1e799106fae953335b4af51c400ccc92adb098304c9aeb239ea0a17b803a5342a74c5111eab33db3b94bc3bf5d89505c92fce7800d518c738761937e91cc34621f0b6a29cabf4f19116b77432e0d1a9e8ee16030aea42c031c66623c66425b640d413c5c50ce526ce2a413d47194f7cd3cee16f0d4dfaf2ac7a66d94b89e8eed5b38c31bd1979c95060fa7a232f4a150aecf8d60fc9f7053b8c645f2d541031f80d2b135c5b9d8045eb2926d0194908a4df9ac40f3439250becb469ec376d654184b0b33582c13c50b03f624bffea2aae3ad28304d3c291868a84fa0e27723bbc903b94067e490d849c4632fc3e5fcac03d4f080a540e97800e0b25686df80c6394c9099f5a11bb11c78c807f34d9ff74d3b9060fafcabca15d166f4e509b1b2a9ce256909e5bbb65323264756b198c0d1335820cb7d35b23292c3d68f4f3ffdac6ea61f81354ffdfbddc10a6f9d0c6e61a9110074ffc8dadf3bf481dc431d5f11c3e1d93ed8d734d900e2fd3768fba1962c3f905a1360b9159e730c5c0b50341804b9f3dde9280d5317aa740a239e0427d7a3804c39602f33f6e19542c88c3d76685e8be045b3a3f3b4f01f5bc32a86303a0f20be2ae10f1dc6dcbc2fea2c2e968f24bb435010e0d2dc31ca5595f679be800a50d7a14544d8840c675638c76d6209a051a9d59491b2cc5279ee0a2657b8ad709087dda425d4c6325895f5c0aedf719e7ca96aa1dcbe942ed8a82b5839ea1dc2e092f5a23c87453aa002698469b40495039550ffb67adc873b8a60c42e704662000d46ba80bfcacd8dd984accc4763f6c3171e178b63263baf45e518c79f8d20c085fbd7f9f0a38f094cde7813d7a27b394338c84e94e6d4fd8092352df9c2fa90cb2118d0473f2f196c115c108b2bef4e1498c4190653b018a591968ead84ef70fff41596b2644d1a5d0d01ae87a308565948a97d673819b9fbfac7a5befa74103670f367e5f4514b34f4b4c158e45e42f248222fb7ae106e8a68c7825873ff40abd725e566797932c589e1c6c709e5958eb959ae7d26dc564fcc3c6ee0ed13508651c7ec8aceca9dd6e4222779a7cbafa143da54a20f9588ef58dc0314070800405c5b17d54e5661b956586f79390174829b7657e4df3b6f57566018a70d481016b14c60d1176eae87d2d33454ebfd0a8f3c531274221f091968869e9b752b4083a0cba45cd4644e60c1c40dca50a62a767f90253bb813f491348b5aa30e69d01ab1eae41c73c57490003f46c0bc917712067b3f0ec1a6db7eb01f42eb67e4508060419d73d64140b6b887cabde19a63d1a517980d6f1b84d31ab530371e5da08956e4779073cbbfced94a0acd1cafb768976cdec8e6e136ded201fe729cb9f0ebaa74e7e36ef8c5f15ac4b257a735fe82ac9431e86d3ad925c01284bfcbb9d04b56684f2b54a9042295c03b03887a41907d0be78eab3aa301c1982f83801a40f07b4440570e4a74de187c391be52ece374b537c06e577a056602c6714d11a90ff30b1e97f275bf3675df6522ec927ccfb945cf1d2bb90bc9ab19b5cc4ad1af0ee65a51b52280ff047d7636f0c5943839845442646a072c722d0a22c3f211c208b3790086a458987567770fe69f7206fa02efca9627dc94f1f77fa40050d6510a66e183ae6678f3c0da45e118a0538dbc518134a81c25d5471039563143bd5c0c false +check_ring_signature 467971c9848018544006ba52b4b310d5c65da66fce8f8533ab9221589245597e 06f67b99d353fca5a760a4a7d943f6170147483cd118a0e41b27a998b307728b 41 213adff0fbb2efc9b4feda8eec4cf207a3240e3bb7a7c14f7549ec015606067b ba779b0f68c2e53846f472a5b4803126572cab173bd7b18a00a4183548ca4e23 ce91d81bb0e9c7c7ac3cddb41293daca2d8f2a8cd65eac0f7b06c57fec6dfc06 13dc285a933e7e55419dcbd322605476cdd52836306602f2bd4992d3432eaf46 4dedf6c824d38651257972f817f14a8dc1c6639e2ef1e7d2733b52b9b32c7986 865d7c98199627d41eafdd5c26f4224ce88de27b33e349e4e306cb546aa88b19 ccf86a3f7c4e540002d57b7f249d26de1f6ef095c16e6856eae639475ccfd2cb 143fb43a0f3ed1e2c618c78fb66041c10b4cce0db666eb24a5f5ca1443ec7d4a 76698a05aa09bb757b60ad8204833c6efa23c1d25ebb89188e72e5b4f4805fd8 b4be73687bf5b7f6052030e6485b78c170f1b2c818df475e342ede3eddca59cc 0cdde95d35ee011211dfcdc193b66dfba5f8e3f3f62b98f9b16ccc07f4453651 b465a5fee0a15141759a7a96e9d27d3125860c771ab067aac6adab5ac7785e83 60f1b0b4eab297e25302457612cf9ebfa0a0f81af19efa5a5096dc23490cd261 cb5d171aae8447bb7fa6f7336b00a376912b5055319ceeb403018fbc229d1889 8625fb42cc5f0b7133b2f3ccb0f66344107b4f7a8cdfb8342b378abbbb3e4c1b 5e5972b500df61c43d40b68161adb7c89a085d45ac853e28f9f6c80a471a6349 037d1d39da8cc46953c123a54d453168e38fb7061895727f19ac32cadf3680f5 621ed8a2208436d284dd007b9da023462115c5345536e8f48a02f6dd925f55af cd37637c7b492db5d9c259759a92d1b5e05102fd53550ec301dfb677057e8807 1ddaa2ab69f55c259574e3ace167b8c6ffb3bcc75dca0ac05f051609e997bebd cf7abdcb5eee1867b32d51674b1f418779512ae4d451f610a8b5229dcef63807 3b39b870cbcfa864256567963b886a7c9a4a02c700656ea777023f5a5dade663 22bbab9496567cf564775db31e55fc6fa23026621f7c9894a04b83b7973df673 0fc53170780c579118f04c406e79deba221534f661a2a42ffd357bea4398da87 a1705dd77fbdc7aad9e776ae7ff129295cff6aad0d19d9d195214f33f6b960ed 470d08ca2d8504544e5192760ff50c1eb41f7472c3b17a2a33a10763cb87015c d29f60bc95904a70706287be1de8c481a5351bbd6e38a25fc3c94222cff09687 58afe05f726b6ad80fbdee94ab40f171bcdb6fb051833bb4b31195810b028360 7a7262960753565fed8fb02748434488f2ecdf3b90eea793e882df98f01d1e16 2ef3bec690ff3754e14a94d8c6aedd2cdf87562254e85dcb8af47e468ebf6c75 47b21febe6cf4155449de1f2087c1500c34ba602b4c71a2b09adc9d4c972dc3c 9461b080db7b7fd41e1d5e44e62cb7e5e18c328c8df520e40383165821bee6b7 6bed0848f9d111ec54f63fcc1ef2bdb4159e9fe9ceb52bf4b96cca5bc565c800 c869e13dd05372ac3ba973732a1cdd28d027baacde7afe183342f1a741d04035 7cc33530da3df21c70838a18845bf05d91621305903a4c2e37ee415607ed8d68 98f98a7c19defd53870ff55628e480179f419f0e9e6ad83d28c0794913cf1c2c 006a4d70f7dc3b7687aedca623cd98bd71366e1994aa9c38821dcb2021b44705 0dbd59616ad43a317aa96da49bd419c45dd9f7df21e107e9e8c300aaa6f95ce0 36f984c477da3e8ee8ca7b063f6cd0e4d9086d7794f75aebe7adc06babc22337 a872c357262e70fc38e63596babd8579f84bd30ab24838be4adcc885485241ce 93dda2d8d3a5e6d2c8dabb5bbbd872f8c401eeed99256027ca34e13c64270068 2d8f30a50198b0633f643195292396c3db4907beca5779ce840f40e38cf33e0444d7a3d4e567b6208bda7c972bf1e7602bdb36020cada29b8baa47650399be025fb5f8c088d713e0171288b8395f4106715292246639bb3cb1ef181229a0570e74be9123c8d6a6313676344334f9b84a9c46ff480ac81b6ca5b28819fb9fc302d963c9a7ca23064220df4fed5bee719f046f1464e45c389649ecd9e8638c03088caee6d43f2d5d2d316993af2494386196319233b4aaeb8c236a67f6387aca0439f6686ab20318748c3098b7495a51f1cfda61aac6a342c86de57195f9386b0d54d1abc3f3b235ad2274c4bce40177d4fc38e0e76d920af2915eab70b966ad0ed0989fc02d43eb7880cc57dac78e74a63598b074a1e8e6cf1720e3ac8796b705566fea036fedc3520457bf20a263a4fc1b218ef6f1aca5006767c318c9a6e50783cc9f1477b3ecb3453c7ef2c33023ec444b7a1746f1c58d50e61a1538f3670edfb049226ee345356b3606106743ad88b3d6a9bc953edf4f672e766d96e89d0eebede472defddeb10e9246cdb015adb5f384146f4eb4bed1d0f2b257251e2503b9c576b9358bcdad38c18fa9b46c451bd00825e8bb880255edc7eaa64f62b40d05d882985d30339de19ab0ee122abe85c3c5f2f88eff248717091791f3958d01cef3ba04c75883bf76953df9a7af14d0d7abcca0c923c9878d344b92f8a82e05f2a894ae7d8520e1327b99a197b3f1bcefe48f99d3202cef08daf94c1882f806b199641013d1761812ed853a47c90c8afcb6f4569b5e1fe3811c2d032a27d40473889dc8834c9fa3085ffbd9468914e083f6187a5885a476d2e5c08551cf650b52a66d79732561495ced0e51760cfe21bbafbca2524ca84c3792d178adc99b06c167b3435bf6b95dad7309e22f0fe1c7d47bf8c8a169635b5ca6d34844ec790ee8ce313ed7129dadbee6aab0b6f76f5e0edd94b1367988589f5821a5a9330e0415475fa2bd7cb31ad2efadfba688d3d6cbdc565dce9df3244b78575a0aa48003cf1c4acdfe495ad2b96f53108966e4fb1893fbf84af9e4efbed1d16f03135403e5518f3d578ae2656b23a2fb2fb25f1bd245dd46414262bb7efc3474e478c40e0aeb8ea4deb8985c7eaf3d49d8b5439d3346bd65a4b13624f2864ea5a108b1033cd9c7bb677d793083607f18c8017307db91de4ccc687f33bf97c1c92133480c83f71ce3ee4aa50139ef565d2ce779a41b6c746ce8fd546d81eb9449d378ba0fd60def3c2cc404f20717419629a8b2826be8b44fe390a7b680be6f0f06054206e0c5f932940eebbdc2fd55ea905a0d1e9db70cf3e74333b8c53ac65d3e19660ba4f40226397b65c9afd5b1311844a93b1b64d60d024b2bf4cba2610e917c490c8bcf0985dbc1ddd7e7f1fd729357d07d5c9d1c60c7e899eae5458effe77ce1024ff5fac3f53b1f371ca9cfedec214f625b7ac024aeecc385ce3893ab6fd8ba0e7b3ba30bf317b9c8557c2c8416dcf9fd2d3a99214c0b10812624c91ecc130706f0219d258d923d483dfb9f6a8e025e97c49bb9978e14c9d59fce0f5fd118e60ecb2572c4346ae70a706168c097be53f49a73b956fcea57cde6b5f177f17ca203bbe6196c8b21746f105bf2d729ad276b3c12924a766c8f680fe467375e25ae04087d037e4c6b3d917d49f830a42c67b66ac3494c35ad94af5dcc4db7ab7f170b2d19569e442ff7138ed9799f2795af00a56ce1538c3b3d2804604e16fd0bcb09e0fec8ce46e023977906c94603dc5c6ef426e8e0858e318406e23cca07d0d1060bdc56b88282dfcb1a970054af057f5ca5b487f6c37936f91164a254d93bc203fde00e2a2055aa62c5406cd383a03d991a4f46ced889a5dc287adbb340e41701ad0325e2a114c50f359e7b0e16d6259fa8ff97f1e20c7be4d995ebae7ed2f201191000739016016ece10b5aca86bccdd0a04022db86cdc957498325ea605ac0cc5d140184a79cae253eec2d7fed209b8023bef43cadcbac62155f88adb4faf01939e69916755e35834d80657056b96a4a52195c0a034c012dc2c6b8c67e6760ddcdddfbed403f7a95c50872be92c02fa6c2d1e74a9b994df96f871ffa7f3ab02f49203580d733f195a35932e04aeadffbf2bc0926ae78c8495894a2a918d12048f28063f246ebd222c42bd6173b226bb0cbb8c94292880da871892c7b7f12a05f35b976fe610d8c2e82cebae72225c242e28cc63654bb91ee77339ce3e0e420030f1f624c6443b38bba703ae497852e2fa9992a12ad6e57a479dc097a2d693015e8ed244ea07e8f7afd12b7fbc103ed00bf6f5b77db16bea9bec2fff3321620b3a58bafeaeda9131833564d005cd7f54498e8c552c8b528d98b79b9a549e7407a3f587cbe8c94d68f6fcd7f9ccde34be3cc25baed7dc341b3c688031d693a80dc839a59ae04d7eed837ab0410b295fc8e3848520133fbe54e57d99d475d1d20c44ae6ae16503d7524fb57a78f3821345ed1a9f932fca19a7709198b6ed90010a9c4eacff135f7a8b77065a346b4f47b3ff24d91ac3e27d5978a39ab56e9d350ec02a2d80b1909ce0b829a4293698b18ee942272618ce585a554bb319a3789e0d6927f01f4b216730162c0c6710be89e2d17155177f19b3b3f062fbfd7246cc0d70203c6f24a82886bce5423bdf148ef5f66e8b0e7ccdffc406aadacff5ab930b127746c6e314aabec0425eef7e20b81741a3d9c7a08d0554f8c0c2878a737507dd75403e48af472566a5d081a4d80a8fefbb10fc58939e140f99fd0316ff7a0a89fc5fa5c52c7e63fbe3b0f26dbccc2f9a689c61fda6c6203544d4cc92927d02c11b5969d24423bb625240412006a5da86b235bed224c9e3b37d4b2743c2c601ad2b3c78194946032c4bacf92f909d321f6429f70705e5df05ab659b0ba9e2003ac0e8ffa7896c8ce9f9c848c298e9e4ac69c1b359946cc723a5da7647547e055ba4db091748375f1ec940c44840098215c8a6813d20e5a0aba6dada230c5b0a535adad740b85c16160f2ed13bcb538517e96f156f154e62feac3b8194916c099cd2c6195dd6b2b99e24353aa4ff79af19da319cba503464fd2e64f353bb9f054cb2d5efd808e8f0d8ed98135f4b16038daaec0245929f2fae8d1f4f0bc9ee0fd319a51a284829b05c6075024f5d3122ce08d6ab9193ad78a4b0298b264a4b05c81a6a3d7bccb556a29ace6a60b030b24499b2ea0dd589d0d6042592c48e6c0d00177767ae04bcc2c3e330c4531a09f7b39fbd65a751d199d1e6ebee5377190c3ed2aa9df7287c693cdc742042291e9f7f20ffa8102f049fdeb346bb0e1d740ecfb89a289cac914626f22482266f57ee72fce25a60ee1a9537e1f114dcfeb304a256c9697483b2188a20ea000e7f9a42b0499e79fe5e60d8a185a5fed028950e57bb10d4e2dbc99ef3f3b7f2becd5948bd0c29261b09a48178d592852297a104d83c2086d674d4bd3a089cca1a6565ffd5cf87c1f4e426f7bfdbc6336081c70c452e06866aad1fded6cb52fd37ede1faaeecfdd66b89db3d7cc586e2ac8f7f06239f2a47c181bc3283dea951bd3308aff2871c0671fb17bf5d7cc5a42e038a09dd69f3aeca8c1dbce48cd04e4c08fbc3539bae337e75b87675d6404d3f98b903ffa2c33bcf0e5342f96594f7c5fbddcc81031abbf51ed683bb02e91180026007 false +check_ring_signature 993de35d0ecee1b81d39d074d33b9ee05a86cd8e726f5e9bdae3e75956535289 5e25c0a3fb4746a2f24aaace557e68126a0965514933f4def36536302fae79b9 28 486925f92b7a715c5e9655dc21b32330a4cbc7c91cb181b702a5fba0f33d0359 5cae53759745f0c1955b69c1efd361c45ddd3dbf4771a83dda6a6bc7bdd7bab9 472e162efa2d0dfe125499da3399706cd5dee1cacc635a1edf326fca0ac2a624 805bdec03b2859cbc1ca2237e146bde59939e9d8edfe133cdd1b5c9b10bfb350 1b6618d5c5ff61beebac7bcddddb5903353586adfe5252c6053758bf9abd1521 bf2abec486d17536d1fb60026d2d642420bed6fcfa51834b8b1d5096c8e01f74 1c63261e7dbdffea2b818f3db242d1825c2d66d8f9e8000fc040c96c89a8f39f 5eadbc26c2384de25cd4ba3b5ffc71ca00694829b8a93088cc13d2442c366b19 8896bc65bbad36488ccb79565e09cfc62bac937e4295b549f818a19e2f714752 07e44641bcbf9dbeea9215a2466d8c223a5dd3a3f094551bfb7008adc1465f83 6b531bb36de10bc1f8782321f61f50188be15d221b84f266150c8bd5aa952aa1 62f4a880198493f0d6ffa76e2d22de9353d68b90cb4500f4ff564a474401d5de 8a76f180a11b6d92c1e85ce1f5186ae79a608da5f8bcbb1766ce6db8a24b8031 695e08361af88ce63f15184f970dc24093a7828ff578d3346e9aa3e92069808c 43d390ce644bb5eb828ee8bb2400f88a736a71ebcfbd45629b8bf0f3445d93fc 981dfbef80d7d31127a474c460a8cbe885b4eafcfcfffcdd30a71d3e5809ec01 f75faa8c15d35f8c2587a11b2f463b581158a38ada0c8bc2db3af1e0a377e152 2735c7ed8ee1c425b5f3b487d0a9bcfc2ffaad36372f6c91c37cc4dbaa3323ed b09f343c9f6e38b7bffb609acf613e1888d8a6a313b7a618d222376f02952ccb fce29cccbb7403733c928382f2aad65488d0bbd595dbf1836f9bf44573aafb49 ccf9e4855b86777fa0d36b516b862557fba11105f26e486e0123169ea0bed517 c791aa0fa44174f3c1e6a0dfa5124af8dc3cdb5b93bd16a88c825998f3831cc0 5f8500274aef5603fef90494dcde4bfb8e76ceff9d73431f636c2c1cf480b48b f3d5560d48cf56ffee3cbc1a3998a13590ab0ae6f4af4b22c7eb655ba6c0a4d8 b446e3c467b6d828443d13fb39a84c19c5b5b1cd229913874e03babc2f772681 8c6448d1fdac01319e230a53418a782f3c06c454aa0575c1c4bbc63ed1bbd860 f7751214e350c276d5934813cacf9dce321cd1144c97870a4a332a5f7ba1603e 494bbba966cf7e1d565011802a00324cd27b6762b8cf0408cc28acdad0d79105 ad1bc681a74c18cce8d97d899f2e7e5b07363a5f6a05d2c8c3b7e896a997440c7f42b8133090e3b4c19c924c5b661c3ca46d6832a5f41fb72769d8b8329d9d0ad7417795d04e2515d9f28e428eb552c11aa7dcb2432d7745480eb9f10d862c0710dcc47a2d15ebc702145e4bb3db481b8390273cb3f394c6c831d0e00654a90de6e1776e98dc353dd1e3936d559296d6c6257dc4afa760bd7cabfa3c0b9a530634c4aea1e97d8a043cb270a4f7589e9d15dd3d7bfccb1f5bf07fc1bcca0c530398ab3e75685e3c18d824cdf6b648d8111220fba1f0773f92b4a15b59ef970b0b676c2e9172dbacc481c581841825bd84797122e6a354a2656065886f8beebf0bcb17cc5962431afa76845fc8eb626bde5b19179c40a925174bf6225454ebdf0a00c20e2a7080120a3dec09ba25a11344ce5b39677724769ee3b3b40aba09ae008b124892c9df7ae6c29b2e0a254627694d4e0ddc0a696e4606665e38c74087042a755d4d7153ff8d0cccd37755a3cb9a7a515e68324ac71b0224e2bc4c04890c7aa138e18d304f6589637efec2b8f497c02c92b2a654d716ab6af7e776d4e409ee11c65a132c07baac4a5f3d346eae5d7b4458a4ab9d82a2bbdc319db5e1ff0b28091acc3c7a49108b87e51f56d109d83be2df806dffd008a1c85c9fb5bf780b88c7b171b1532be47b80feb5d659f5c43b24cef20cc6b8bc9d8221a3e0e8a50c0966d7ba17c0119be0cd1841311659a7af791a7c71956888fa9abd6ea1b2810a0ac034dc7aeba4385a2eba4c7862af64e9b0483ff6acb00e87460313d44afa0d92effe1e422cddd9b0f90aa9089e5dfcb44f58c2fcf3f166a39dd7be2f18a00ea6f6f330933c2b26be1b7f506a1c8ef9497438b9318e18316f2a3551896d5b01138188dca2d36c63162aabe4b7c4bcc75e52751d0bb31a0a39a8a13e428afa05e3ae2861e0b67c5cb7f92bca307e4318e55731c87c08e021ce9e95f4964b48064bb5eb05ea779d469fe8a2df4d300b6a937c7a0cba8bae68effa5a929509780a414c8aec5c8b0649bd6196f87889673e5369b565979fada818c6826ff67e0e0ec297aed95e40967edb4ab37b0bfb57479ec42eca1f998af033111d24bb935d057ffb06c310273ae87f6ca754831de6d7d23713038d83535e3e5022a41f88bd06fa73be666399b3418c456e0d50057c0d31f404329a4c880403aa88ab6a82c30347fe77f0c1aa314915f30913e3b28d6eacb41c80b6eb900a345fc65d990190024887527fd9caa387e8f8664d0f12498b727574ea1bc143fc3e90e159977af609cf5bd80dc80271bd0a314c4d0df1e1450e5260adb475c6d3d80e5831ecef590c77a4f43cda6fe2d3e60d91ef33ec1c5531341bf7ab6ea25765f57433a1dc3b01f729cf84c7982ccaf0230e09b93ce2dd0b332fe43db6dc48844d58cb1ef5e906b45d1e47e4991158bc9e94d93d0f7048f9b4ee639e6f4e1bbf72f3abd9ac570cdaaa8802c1317c4343e4b0bf584cd245a3053b8aa7aa9da8a169db6c97b614044837b9ae434cbc780d968694196b6c4cae2e89c78dcc5952b405713e23c3fa0922f336632d23b1bf8e05b4ae93cb58628ce15d32de0daf0e3b02f1073dd241020d372b300bf75762f20bf3dfa6a5b837c7960dbf56fc9c77beb09ff0cdf33e0a4c1482ff8e9d89528dbf96c7b6f5847b9f9d8fb204ead6f8f98ceef4d3bf88013e7cd1d8489da004964b39ab4301f421f4b1ab88269517f6b94b8d15ab5ffa02eb375121c197191804e134c25a4d8d26fc30e5722b10652f6f22ec6e84e0230da75bee5594e876e28e8ef1e51a6b82c3ea22f866787779fce4ad1fb9d8d6fe06941556a6406730164b40f5d7f5a79292f6936c2509f53383b19363fe309eca0fbed23291b447fb6cbb881a6e919b1d24149c207da7050c6de71a5bf1e026e9045d4fc7560180d02c954fb87ff6b9fdfca829dcee6cecd558e1e4691e813efb08e03ad8466dc4e30c17341681b909011c2846a725da0292a2dbb4ce9fa177290d685194a69efa0198745b0d8fa66f9c386e9929152f670860f67ddb54e990ac0946a4756b99cc567be9043f346c647f54e433826004fc3baaeec23923504b3f0ff759d76bce3b8a55df5dc9c74030aabcef385e2807de0bae7ba7be1ee61cbfacf22db7291975c2c93bbcc611a24f5148ffed14794e5b8c2e38f6300859362d0844f4f4ff82a0d1364ea52b2875827e27043845447eabde9308db82f21ba7890cf1098e10dae0905e6536dd937751aec3789605ca517ea9a2a12acb14284a2d0be25f2ce4ee2c577b0e68fb46005fe52d290d06fc1af90cc8cd42d32b0870280be80e187f0e463e5196e7c5055c0135fe6fc076ef004fb1c786c701f1c7ffa109c3631120f7e6d2af3e48cde3eb2b131195d54a87765af5c04b2fa3c199a4980aa94911ee024da9b7e9eda36662a42666ddf520ba5a02e28d66b9f8d4c8f9200783b5935f69faca7100bb711294f98242609487b963da842b3ad2a14f0af55404 false +check_ring_signature c58f52fc809846d5e8f1b2ba94849c3c433bc427a58c1fdec079b070adea8e70 91e8f5ae5e57f7cf5a5ee9a0ddb437e81cd57c84cea920a8ced208fd96654a49 3 20d085f670429b9b6add151e261bea0222b5cb12648b03f056c040d2daae4114 719b911b3fc6efd402ab3257ea4f725d35cecb1cfce913c7c97bef185901c20a 61826c933d1dbac7dbe1071906e1804fa23e3cd7e8bbdb81912f10046666deb6 466a3eb0842ff5ef214f1ea5fc43323e3009f8cfb31f5afc8894c6e4ec7b15054da6c3bf2fd577a72170f1a5029ac9fde966f0f65e51ccde96759d9959c17219af39997b23c8c59695a2173f60b629e67ba0b84c6e817540ea57388575566d079aa31701bdb398679a095973f9e637e20785219774b9604e52130a304aad770fc4cc6376fb2b446d081a7ff7c110b29b86f86efe862fd29050cb70fe8f947601ff4c3bfeea62476510045eb1fec75cfb2c99879d522b2324d0c3f71ddcf23702 false +check_ring_signature 0ea1d415a66996be958e711db46569a3d16c23684a0ba64703589b6480192461 88d56bc7e86d599879c7c56a1a3a11387131eaa5b6321508e446d94b53eeb589 1 3ac79ea7e6db5b4f248bee6ea3b509be0a3d36b83194e4d6d06dbc556668c576 ed7ce14edb83ad60e74a1f13a4bb532a78a6631ef780e7d8ec0d60d77d7df1103ab72f97cef9e1ada19a574256c02a071042ec5411c7c25343b45574c729e224 false +check_ring_signature f552cfb545d880e434c3cf1f2c438f4cee0af1dc3016fa216c0cd448cc4e7671 d351278b9d60dade5f45887e4385c9172990ce398ffade644fdc3fd6c78131b0 2 29d22eb5b046bf29e96f75a35c78b8c0c9236f7470146730093810c14dff16fe 89cbd1816c7750f55938340cf4161511ea1c8c8ba82e292815eac17bfcda6192 57b4679a02dd5fc0664b856b85894d2f7a7bd8055d582f40c3747072adab3d03cf89f785dbe347d8ffb61d9ce29026e639c0f01a0b09c79d414750cb05d0ab07424690ebc516301eb7eaaadb807fa8ac22527d26b9f150c920c9bfd8dbb55e0a65a3f1a0cc79a8db6ab6e5658e771c4c54caffe64ed88413181312ef6afb570e true +check_ring_signature 52cf9583ec867db31025b742dd6f089fdedd9330f8b95e7297d1d694b5f21fc9 c19a53b1b4778ea4f59f5842cfcc7b1a6ebb56742c1f88d56705cb551a0fccbc 1 0f982c9888366a9ed26db966cb58816cd30c0160ae63c624b9f3a6bc19ec4885 fa2c0f4181b9ca6db464fc1a539c6f675f60dadb64ee4ac1baeba96d122dc3026ce994b510feb5924bac917e1d677162937ce37330a902b6595c26335e5ffb0d false +check_ring_signature 0e3cf2173f814c4e4bcff761f7613b2f08f0ef91801d71ebc77fc686a057ac9f 8ef14a6e53864d416bce35c502794ca2483b9d9585acd6aa269c63331cc56764 24 cf1c684d4a2759f855f14a4359e09ee65bf3456da9aa4fd2d7d0dcc018c733d6 049d9619a738ba0cc925618e302ab9f35700a04d23b5efdb2e7ba5eaadaf3ccc bc5bcc030b0e125f7f355657d66d95379d82a868034338818874f6104d27cb65 1c0be79d920d6343a495dbaa21cb7a4933db86de5e1a117a11433d54e0c9d715 02b0caa67b6c36f9da01eaf479b05aa94f3b450d9eb1dc94da571435cf519fa8 5a748df0da15887e0928bf36526605628f7a9d425eac318db8167b8a8a65bcec 0134697420405a424797a957b5842c673f361f40e4b13896d1dce43e49cdb738 ccfafe80038822a04f6398a35a39e43d97f57418efb362b547ed503cbe9d328e 175c950b7fa206dcc3f528432bbc98f33b87600f2bdad9b6c9651b8969406b8e 7ddce7f224ffa19c0dd7df66cf5f2fde6e6ff22ec8b4145dac5452be3effb766 c2b2a51bb4737cc23177e9b2c947e05808e267a6f408e26cd61eb257f84d9e2b 073ffd4ab98202a0972272673eaa89f83ad1fc22372ae39cdc8134707326229a c603efb8440ab063252a74108550b34a049dd93b74474489b4bff4ced677cfcd 837dead5d5a99b6914b7d9d0dceccabcdb3c419784848684295db08062e9951e 567fbbfcf45fecacd7a847ea543551704c5bfbca21a6e0960b8eaf448d563875 552ac0955ed77b8c4be34168af344f962fccdd990a8bf82396e7db34839a4757 33b1c2cdb529927d911e8142144f9014500fb322acb5c8133449e34c80f31558 3b8612ba3131c2b22f9b81f755b884e6b68613c2193fd7a4d695d4a70e7c5072 9e3c99ad643276e913a4bcbed8b110bf2e39440e5108b46867963874d0f71ad7 cdf43b232f46e41bebcec59f70283c55a1fe581b1c13991ef3ffecc159949eb3 c4fad1cd752f42cba5ee0e3aaa65a6a1eb0fabef4ad8c34c784f6e1bfdd90c5d cbeb9dd1d7dc0a7a5df23d13eda8dd38a6ed8f518f13064dcd8a40b4595088de 1bfe85a17f1736bdc4e17aa8606ae68eb11bdb6cbc2af08d742fd11e7846923d 463fa4b9f18a8b92cbd7fcb74670cd39dcfd7783d53363e055fe8d3b6af2528d 4fc3820dc7e2ef6312b53c68e03ccec997aecd7451af46419130f07af407c7054188d705a23997fbd1226cb32e3ab4499aebe39dbfc1cb8c90fb45068e589a0d2ab8b483db4589f0ad0381b023181c040524f1b0bc2877d8914c114f1611ad0d566a9bdd4efcda11e7e34330657f35f897ccbc0a55de964594de31af43a4fc057d35ca349e92fd3f0891a9e04c69e0b7d8bf136d36dd641378a00903de10e705ddab2d1dff09355f895ec028105b49c8fcbc3317ffa298ad0df194aa6b57f40eaa44dd8d4d6c59a4051c7c4d2c4fbd80d95ad8983177d9c3159db5aa145de307a73ef5fc61063b03e141122bdbd245f24ccb3c53c0248990ebd711928e7ebc0fd6d7d102a7d0667545c7f18fba954fdd3cf9b61e0c562a65415ac28753369f0c654a6b734622e6f73ec1d8f82ffb994b53b702e38736f13f06e9d1d89b6bd50ef42d98dc34e638c36a4a7c196678dc82cd6cdd73a4ff7c205f97944d37f36f0b8b89805d4a2535ee9954736873b2dd8781c1ffdd0f2bb386b04b70c6d187db005715b0ac334cd89d2e77a9afb3f6609af16eaa2631adcf018be13027d4516003d575ac519c31d464b685c462fa163aa799456b8c23f2efa0535cbfb38c643a0a36d46f744077717851b8ce4ee1dca82ce6c46babbb4e52bf222ca970b94b1d0ec577cd228511c2ac9e1aa8286397fb26f4e2f8fabb77187d8a200c003c562b0082a3a355b0fa104b1ed1e2926c8515a8ffdd2752534b8fe4a5902318760aca032c43e2362886a8ddea241f127a5e62d59b374e0e900a981e77f2503f03f84606b0cdc64e1ec23294bd7d35aeb12eddd24e66c3a497075383d83b511d982c9c04140e208bdbd7da0f4f8cefa8e3db82a7eec1b4073370140f40bc1f86cf4d3a01c807387d5f777295ddf9a1444d9bf0918dd5d9c418d51b1124f828aad3479402732b6b78b2f034fd2a5e4832abd56726fbfc79252f5e4bc613df9f517b4ef400a33cd1e4af678ad262530ac6dcc2df870fc27dd5e1fc3c9fe18a6710badc16009c3715f2a302500f26f43f263d124bf1c875e9c49315200c31351ceefec4350c23db66dc1d930e7aedab734bc5ebef933c8e47c02d420dbdee908a75a0514709b22ad91392455d64b619fba69d9fd61d1cff3b5b61d16cf4cc0ed67c5d36e707653e8a39ba6004ba45c1c94ed7beccab3f606259eb0b3e2df52a34e7ef373602634ec1687f9a2524a5752b3d4fe3e92729ecc5ae242e97d169efe6fe9a6cea0d6e4040bb21ec787e28512f41e1d3e3f3a6e26d6bba0cffd8369ccffdcaa89202d26b68de05bf8428602664c2a96ce27e62f60bbd96433e317820a4f8de2d760af67d64c37211a010440d52c8e70c6031fffd435a9d8d65522fa360e7eea20901a691c2b5d27deaefab473f52e2f690f3b126c02bab800121c5723c6afa0547018c727119eb53db89f5f84aee70cda29920594bc207a6017136a9da696005a302e41e7716b6d6695732a83d532d1528e10cf99fc6b40e460ef9f17dbf3ebb4b03e081c4b6e879343cdba81a5cecb0f7e74f39c0b0e7a79f5ebdcb5d069b3de504e83bab849fd3f8bb8395e6e1bc05d95249a2397048ceb6e8e443e42e8cdd6509c28243df9bfdec5b0ae6f281b8e067a307fbf0aee2b76efba564ec12542a7601224d8f2d97e00a10594f15ca075612fabdddff798dffd21ed485b7f4ad14fc05c09d7da734b8b78e3200915651fcf274083672f7981f7b4af844cc17c6f6fb041c69a12ce35652b75e9203135869dc2ab37f8380a89b84d7735f980b3fa77f0011fed6d8b1a6a1d892c15620ba74b6a47c025bc94c62eda28c6075a90cb43e0f3b2d2e87f0c981adbf50b915c2ee07d34b5f60e792fb870f3e3286739d48de016e67b4c565765fa48de29995fe16296d7237dbde5da67e534aabb202f2c61d0f05c5862c77cfd77a2586c1e32d900636961f42fae72af199ddcd61a224457305cb597ab8252ee3ff020aee6bbc976844a749885c5b879d78addeb1ff15b2650b5f4f9761ab913c513fb23c074a91751792d8559795816ce760638f286dca790611caecba188152a888c5e264afd090113a3b65dcb8e79fcbb3ec91e4baec860857d19e3077183ecf844c0db820e7a20b499ac31c07c78d07db19c7dc49a8820b false +check_ring_signature 960aee8ad5616c498f4bf255654605197fc9a1caf7e62568d8820c6ef1014d1e 24e68f6b05fab9ac3cfe041836ddcf78b3db2dd00fba881685606e9446565ca3 1 3ae4ce649865891f5fe565cf71623566608352b138b91dd1bdc3023e6c133a51 94e998a77f8d1094c06589cc240bcd932e6fd8050aad0f6228ae997b0e3078070ee5a0836b1c4a2c1be9fc8689a85cb06bcb6a03664c772f61f903196488490d false +check_ring_signature 83b4a6fc469c6f3e9dff6fcae322c882b9742e87e34131c09c3b7346f3dfca4e de7ab29fa0aebaed8da71b8ab3ecbb1bee4bcfea3220bbefb77ddf6d83a87535 2 3687ecf372c7630fc5d7e2e0de9a0e0d62eda884b555e9ab812302ef95ff5699 7c4c37020750f6644b2f28e16c67c6cc05234b77193db132083b3741d7e21d60 e081749ad424121ac7cb810731d252e4332b7db2697de66e6c6af8f1a580cb0bacdc322792c6a594e67ff958af3f3ab52b5d22fe4669fb61a816bc58a75e4408fae554c308d18666bced385d25aef13e5727c7bc4d40e309e2093aa02909f901b969d85c496c7d9b488433cdd5a547991ab4f17d684f92e8bf418c37e8544c08 false +check_ring_signature 11740da44942593836e26f266b515ef28ad532ea1b7259a9036767e8d0aa483e 5ac5336db561ac44b45a3fed49134030190447c20bce767b7b078e3e0e81363f 10 bf8aff212b4c29ace2eb1d806bea7fa91ad53a7a52ef9073392d25467e3fc94c 24e59cb29fa425735f849ee799a49bdffe979c3186a37a22fdd99eedec7de5c4 da6a3e3fedfbe95eab9b4ee93f0fd037c2240a80d37bc2c55de5e0521fa95a35 d19b12eb3be998b8952b85ee6326b8f5da2ab926e049650a8241fb448756d506 c3e891ab89955c96005ea9240fcaf15e12ae027659389c25eb6a1263893b9c30 1a3dd1eaae58d7af60a12adce4e32718d2b972029a254a0d7a364a9ff74c3454 aa9f281e2020d36e33b18d2814e912a5e7c2796f8770ac384b5369eadee2fe83 92b5b0997a713e9c51e9e0b2e3a5ab1a7548c8ca705a64f5ad790834d3a22f43 6bbeaf69cfdc80e403923b593d7c7a767ebda5a2738981ce432c5361a339bf13 bbf14fd25990a78b0eff1f048a16cd634bada36d6fa987dbd6837f8823a66646 960cf20e7cdd90cee6a7985232a1e757875c308cac57eda7e3616fc44bf5010851b5957c95650ec43956776cfc23bccc82e714c07c88e59a0bc749a01e5df5efdf2bc03b8d59ab46bdfc4b1ddeef490842379bec50143b4bf870153bd613cc09b9f175aa4bab3185df4694f69c14bdcbec5ad0bec3b45f5d1976c6fee595d60b9f429f17cdaca27fe821b62365310c578912a4b8a4de943482946fb118f8fd072f448dc017d6b63c5115078776a81af92989573ef6d894d0eb1692f5656acf0b9292f8b35543ff9c17271bf10fc2f433295fb5f988f3a3736faaced0738e9a0e16b7dc84ce50ed02962124f6cc4df60f3e61c50121ce820f4e4e86e81ae7b305da545a4b51145ac1d69b1d919f12ae2bd5610909c4a8ea206a45f46b510b220c0c3505d8b10c1cf49e3ed911ddc0e2e8da15c1a8e2e0ea58d14284bbf17a0107517b7da7c44ff39ac86a6a4303714653a9bbe2834c73d47a773d5d394ba35109969fdc4fd656b243ae0cb2dd0bbada834ba678304a8a8cd5f63b9d823a866201ffc9eedf7f29ae5858adf5abf158a9a14e17d174edbc0d7a8766064ceaa95c0b3ed15e3cacadbcdf954ba6acdf25ca946740ee729bfab1c34d572932c86871010ed5b12cd2b7b7f2900e68a762bcc8fb1dc556faa85f572408338688245ad70b35a7c5a54e4db839f5c9f92cd4f785fdbe7271ed33366f01a72a9cd00f806e0a5cc97b0b564d650921fed83c6f432e793a560e5edc18bb64e01b3b17913b700d229d373ea8a3803829b6c576d4613238f6c657a1564c10542ad94f0578ad9505b99f9c277fe2cd7217785585edce5aae5de6e83bfaa6cf732927c8473f2e250102b091cd5a56b8d6fc1a93d0d96c5e8e4be831b5d85dc8eab32bf1b61c9dfb09 false +check_ring_signature f592eb1b1e8bb7b0b78e7fa61479d765c2d2ec3e47c8f488165f6155dd1340be 630c2adbf5fa76c9fb660734813f23d7e6c4e32ded40784c337bd94c692dc225 8 06eebd1e6f93ab24f4b484beb80de3f6c4fe69be2efb28b04efe34aeee64fc15 5edd7a6a677310e2581398c384dcd8833b14b90a2d9c382ab2b6f252cbb85178 49ce445d91fb013eda964456d7bc19ac9a3d82e4c5e393f117205be1479f8685 927c26cba24c51e519c7bcbb043bacc956e0e8b15e3b7714d27bf4934e1eb1d3 ca62bd3974de124e897bff242a35a56b42e2528fc702a79f700c8a3db8bf4e27 13e0fbc5d1c44e3b5cf2456385dec8ac112a346594aef4a7a735e3b3615b19fe 0d379f3361c2a46aca1b990357d218f3f35c03b263757bbda0b6721b02992ebd 970e0d3dc189ecfaa06976e8327641f9130e03af942bcb9b6eda6be2812c59f6 d78ec505569b08a6e84cd2be2d9352a4421c4240965cdc44a90e8558bbbd620bd81ff1fe9cd21ac20c6db47d78b03430724ef94a26098322167ce85e448966097ca4c270b2373cccc00874fc09447ca65ce7b0db1fb403a705c75b24efa1e30440138bd060dbaf6ef67da1d9d5bcfc4c3e04ac9aecc67d2e1fd488a95b84ef0ea7e394ffbeba74c4df57a33fc1ddbb14e5a9f0c98b9b144281b72473e0b3550daa728bfe07eb81d4afd2aafa4a92e15f4605f3ef7136ca4f0408ad633011030f8caf9998697c9e5e108a386baf2aa1f5aa1b45bbdf499666ec5a1ab597361f0d996f9f2bf3a7834011e2f3012c0585bced75ac1b8828dbff71f48352bb9ff704ede6b872020a2839c1aaf76c14c89e2d664fee3afd95ef482f103c4823d9f50ac86109a473e710e3c279e3b2bd2cf04b1f105a43a13e0d2bcf4504d411374392d19421d1a200daea8a8efaababd238d52c63059350f489cd5b1e596c57b4ff0e608e7e788ee4ca7718be286f3d5b4061e692c81434e1b8969685f0086664b40f71f367231517b22c404f33b923f815b55ee1863a1deac374f73d5a3fde194801e4658f2cb721e0bcce6442f617df3eeb59858296cc69b80756434b4524600408e37aea995f136fdf3785247cf9c87f036e6477d4bb96fe0b7650c45f96837f0cdb76a168a98c9cbe60388833972935f4d94801c4d1d57e9ce4080dab386adc06 false +check_ring_signature d96c8dc4eb4fe7ac95c1eb2a9bf5979da3de02bf95c7f5924328abfc23ced1e1 4b43d9571c124bb7bc8057062a300abcd75454dbc16081c74c1b1d97fcdd8492 26 41fbae0fe40d38ad68cf26f6a15abd372345678987bf63ddb3852fa77fd25721 ff1d58d48b388890651dd319a157ea16e9a6b69812ca7bf7e9ddee3114fbf403 a610e6f9775163eda15a1631c39ce42468c2141f2bd5d11b92c235958fcb2fc7 530058b36e8e2bfe07071a4748d196bce98e391759014e0372f1261eb2357bfb 2141eab8d9ad7de4ce380edcb228334421ff0a8acc8854861e1d681311c5de5f 2bf4504361c6a78f75afcd3d4a5e3f1753fdeeeb9064c9e524dfd0fad571356a 78c19b71676eca25a38329872ea28e3d3860856a9b2f2460d96a7591f0fecb14 3a95b0643593aa7732ddd9eeb12145fbdddc00d77ea17fac66d4c11f9bca3626 370e640bc4b64443a51455afc02034e35d08d8e81ff39e6f9627d746d29b6c32 ff526035770642fc0481bf1043ec0d18fdaf5939c0316043f2728221baa6a14b f90bbee8c13070d982e8ad612cd048881583ba08ccfd0af99d7f97d77a1eed7d ae7977115e2843923156e32f1d780b7741ae39c2ccdcd61be8128f3d67008d79 5d21469dda95a7cdc543c29e5b48915199878ce870c68608d75e8fe950650f73 e10b89988dc2d95842a11f7740789ab11b498a3457efbbb9659289a0692da622 1cf0c58d6cb98f48820e172c515d8dcd200c033a3ac1176c5cd0804d4f6a5791 27eb530f83ca693d38923907683dca6b7f357968cfecdf402aa83aa3ba79e28b c3b2f7de421360641ce974e3ea5c656033889a1e5e6557bfbec45f571adf96ee 752814eaad1ee1dda01ecaecc199fc3312435ec8048c3c447c80f6faac3fca3b 65d519274cca454f29dae1cdca6ab0fd4caf5e0391277758083ec3ca339871d9 c9276d8268277e50cdda1658c512a62fe824ca7e3ac05547063d721e17fdd080 5ab1dbc97566d13ba6ff0aac0acd73e254131872cc57bfab539204a5134b0974 1bb86acaf8207eedfe40a10eaf6d519a47c5628be9e7074eb9567fa78521f71d a0efa5a19009beeb46e171c88962707f8c2a46b835cbe83a8348af9edc0067c1 ba02f1791361947fdc657667b27a65943454d5993528dbdd5808d144e34e2ee6 30372562d5dfa6cf3480611ca65cab725cec536c24a1e05289bb4f7ad939bc5c bcbc8074f5314d95b9aeaa6244e655c8f7a822aeb813a82d32f59567a166f41b 64fc271ac4460dc7b15967ff73c4bc2f85de84ad5c49b938f06f7bf988d93904e7a1b8ce033f5b4feecab43851221113b75d4ca0c5d3d95803f16c2bca599003d5e9e230e417bc2ead2a99a2a79161aad0dc907d9a5a4425f3e541d128fdb600b7de3c0bd42f4f4cf079823266d5cf416dc32ae43424d8cab16fe55044434b0ec63b1685c2bb8567ef580eae689812b34ce1a5f798228f662d85ea7f629c210e30bbaf3e712e71e95bf5b4d9b954d182fcec52bc41ef699ada9c0e80261eeb03cf88c8a7918c89839cd5c83d028cd96dab79765524197d1261bfe0250b87f10e3ac3dd97785c90645c452f7a58859276c2b3012b955471833b651b5b00f26400e7ea92d36c860a6a0cd93d056a6fb7d4966578ce89a96be7d2daf3c9d40ece026636e6151de19e770c549857ecf44547a5ef95d867194525bbea657352319c0e3970971b109b74c59267c76aae8effe1d47fa326ed9676529e53482d040bc604871b07e3a4937bb287075600c00a2d6f9480e70cfefccb74e3bb6c2b21c07204200f5356742c4600ca2cddad608fc3df97f6be44230e6d047263532308871f0caa5606cec562e5551ab9312e2d8626f0bf79026f4a353e36b915489c3eb80f0446e67767f300db455063ffb8bc658608e0b3e22f74b6d6885ab5cba4a3ede904c351664c6aabc934922e878e13238eb7a7efee3f19150683e4ac5425ac697108959b65355c845894bc85f373585c4938f51afbba77de078d2b8d34aab3afe50bafc1a7b56fc0b002ebe42768bdb9acb81c7bc1e631f8cafe95e2d64ab61f6e0e2c1ac998cd80124909893e67764aa3bf4b786370cef09b1dc6a2f2e51d65190bf6fd64f8e125b410db5e20bfc6a88e8ec0c9864050f73edb459179759d9aaa0d1538f4b03025690dfb5c15c26e699ba63a8c6f313dd1c6f6f024e993e58f8c0d024f5b1e60a9e32e91b1b6e641b7c0ef52569e0eaff51b5262be4b354fbe360b76031a189c26b85b6a7ea9814dabc027b67b48d41a42f7dabd17fef58f5af504ebf5d707eb535c4f28180255738b61f104bd8ac442050e75af86813441771a0e0f5619f97ac09499561d21728cc1b0fb9b07b44c2f31a5888a7b00d2d7eb2d0724730a88cc9e1d1f622f9e488140b7c68e78ed4083f0789e88a99a4d2d71460d2a839a1ab07e689f47113d42f78407302d609a92eacd142df690f24f7617e201f863845beebf8e9135f4974932ccffa24f2c57b44bbea509006bc89015067308fc92364834cea1d43f7593be824efe7b6abf3a8934c9a943f9181e8071b87e0765068ba2d0e8891110cfcc544c75709df981fa9adb6664bc232db130155ecf069f94d17b06b4df0d21278d73ea2251cc3bce8b1861066b22a16fb251e461960da17aeeaea2aa9bbf23b510949455719a0648c75eb3448773063f2fa74c495201a07ec2f24f93823ba1bb517a548c4f1cc97da68c2e7bf03558161b098a150304454bbbeb7376c055f38ab1e87849d7801ab481ad06f99268f60a1efa36b938019923081d4a346baedb6e9892fba267f2bd945a8bb078d7fe48d18c11753065072aa113f9a604e11169442c72f7d1b0f0077c7f607bacdb2101381695147737018349fb40afc526a91cf26c9c7456077142028852e38642c28cc278e40fd1a40466cbdf747a76bb2f24fc2295af4bd781a8d3baef7f576f0522a92c530d8e080e2e6b13ef5a4a8a02f738520dcb6c761f907b115cf10887f457c5dabc91e0f4018c0681bdb99f3fa02f938ca7c11d8ddf6c57ff642ca1d0b4c932ca175f27370d5ecda37b0b93b22fa733c2f69462b8a63b5b04fc635f86a0684ba6c564c5b20787daedc6b2e95da93cc8c522decfda2215ee2c1401bd3e082398eef54273fd051ae26af47cfd8bd3ad7e5d8246a6e2dbc8cd6d152072b54125ed5cd241a8970ecc205b165bb5e8736e5fb4f51be3ba5d2f3ecc115386141bddef87490ca5af0fc591a1ee041fb44e13038c41775830c071db6ecd18e038530d3c3016a8fae608d63853200fbdc441fd9b833404289248ea0430a89507f63bac7316590dc66a0e0985fc02f4aea35fef0f14212ad12587444788b182c53a5cdad4a67966d3b409b97e9e810e09811b1a00e8f6ed22f54050ee791579d989404d8b403dcab4120d508fa00608b51c63181d6602fefd84b6b4ca43afea1af883e3dab281d3386c0202e73de3140ab0f1585640d2bfe63b364688a94d9efc9d019bba2fe378e62d063b196d04ae01a1b5e12b8cf423ad839765747d4f8ff7245c1269728cf9b0c9067770a157f5d3417167618e237833c310f0c77408764fa1257212227322f3c002 true +check_ring_signature 62a78ffef7c269d61691c426e0e1e475d82f8d237d3303b670bcb675536f34cf 2ff96a589402c1b7b9eddb087063ad5709939873653b9b3f30d055f8a6a67bef 3 935812ba18d50020ccb6ced8e3d28944fb505fc8523e97b704119c0224a43c12 c9ae1491824008e892acbb4c1f24564465bbb559fd3957b600e43fc4b4aada79 efad56d7f31ece4e03b827a0c3e70359e9adde5719346c612c6eafe1b1fd3bd8 d1b959374481db52e007af07c0faa2e5e5680e79b2ba8d9e0a858539e27ac0fc2ce11624715955b610aec68f45511ede8381fe9f57a23ebae782875bcf800a052260f12d7bca5839c4fc77ea6dcb9e12dc7ba730ffae2e7dcf41d69aed457f014d86215d0a188a8638449c3f87ae4e0c5d6f6056d044e39a709c6acd90c7fb0b46908b728a233de3138fba76c5b3044df0e891fc937775cfd948aaab66b75700957642173673f8d9eb0e190a77a4688584762ede1ef94bbb4e0ef839cd7f1304 false +check_ring_signature 02f6c27a2e11e72d8c28e2773d901ec99e99a88ba5c414b155c89611688913c2 515bff289fe05ce41a11e0f95fc096a92ee9c17c8f601bcd4c0b0888bc8a742b 42 47fccb30b1eeab63ab7588775e005dd6c2703cf5272ebbe01d95f1928160c82d 9834848abee345ba1d9307ac68feb400509be098f15cc9fd36161a2656d9b548 274f9923cc635440eecdbf8309c0b62ac62455fb3ac9364697aab5b6976361bc e95f089d4cb5b75c10b7c363e9dbeea9d38e4a71366198c75b7fd5e21f20983f 4f7f1799b6741aa27f8367f384a86b83acbe3c7fad33c84a8c9bef348c34d6d7 1989b42b51d7cc3be7deffc09c50fa76240f12134fc751640c21b70bbc0ac114 7fb73a46564f5cb76280fa2ac8ba1939b663f80fb6c1ded10cd2a8825c732c79 c71ff89a3ccc8b5158d6c896415b38ce5710ff362e3952a870e8787d00419baa 8da45596db02b6bda8604a2257c80560b70f80f3a211e06f1851a40c28df390d 09cf50f5678f34bfec1500a249acd27caa87f0311e515b826a284da713bc9ac4 ad67a0661f0fca3a08f9eb0722c9935def5b614a5994a202d4810bb9a42f4ed3 ed750ec3d2be919c55efb1a65307071396be022c7e1c9c7ed1fd100932b7c37e b8f27d3f0522fb3dfa41a52a3cc5532ce862a903e2f9b9db182be684ca2fb3b4 3c26f91ec1fb3aa8eb9809999d1d40bd44666611885a26679a105362badef07b 7474499e447aff7406110abd9a54c4a728eb861392064412fd1b189a761d4324 48fd45949cef0ad5efcf6827136d5c24245ecb601fadf23467469b2cc13906e3 72384b3cef0535fec0097c47f314e56b22782152280c8ef109f0bdfd9d34c550 04c229847fd0d901c3209c8873c41b16f6642664b4b4bcd5701695430017ee27 abc16ba1def8bfade47f3bda0d0465f38dd92fa076d7c602b60caf8f0bec2c62 30e4d051245b352d1083bae91133fc00661158d459073bf8063dcc0a631d23fc 578b3bc0b41db4d7d5fda76be9e0d1d7cf353164d27c9e1cd8ef3edda5cf431d e4ff8cbd8807db05f94e1e8cdf4bcec0995672ca728969fadafaf6fd1935eece ab614d7875e311f300f95ea4b96fd14f8e6413ece270c0011596d76c8e43a3ac 4f9b2f3ac5b9622fe3a26f7e186faedf7d733c04ae22326edfb43bbaf501d40a 671fa9040173f26f28f38e0283bf8244711a2c05374ab2a92ce429803799805a afa45c17fa11578f46c738898aec85173711c9ba8355688f2dd03065f023e74d 948fbae24e75d2102f1bfd1c7fc348d51f4d9ad9697d4b810866d83dd9607c22 02632c5ff7a728acbec6655575d892c8b24e5eeb92bb0ea3b5232eb100d8a3f6 94aefdf68fb38b5fabd19dd4b5368d93e6b5b554016c957d2a4d15b83e382a8e 65efcb8f064cac45ef01f9db6296d5c876dec30619996a7d1ba50371c5130354 cf231b0ea0a3749fbe219f12d5429997391cf2d075dae7f96f768de28fa921da 9ec67242a641ae2f832f2ef88c2ab2db374eaad07ee99263788a9218b3b9ef7a 45d0e8051240f1ce7d0e7fbcf394f65f175ed6b4b3ea7477362f3d164a375c6e b017b734b8409fd03f74e9f36b078ead0eeecae9c5485f3453ea835de0bb391d 93d5cae63a3453616011584fa304f5c3c92829008f38654d6be9bd2a6d25827a 1208c2e6a7534d74227fc5e28c0fdb52b9fb072859447222f35489d4e66a246d daff8d052de2ddd0302be21bdd44f63486ee3562d93ee9f069311d9a0ce45d05 462448dfb1166c4fae63a583a59c0a15ff4f34eba17d4be81551d7a07ecb611f d1410600d909e7ab386ea27f4c1566ab2195c4df6fdce4c263b1d882ce622052 e899d790b0a41c12206705567e7c889878ebfe850a73d74a32e238a2f8620de8 5e8e60381427a4df176d66fdad20fc8eed0973869ef5b8f379054153d95615e4 190441c1ab9f51d67b31cac28e9baf5b7aefa51e0f9d9e4b8e06c6ee40e066c8 09b9ff929e62cf1b4eddf55b4e9accf4ef5dde0b7277f702d331234a1b9e930edf548c7cac8f06a7144a2c05bc4eaea625937a7b09807666145f3836dffcf70ed9efdd01fda666edd65d629b0b89f6b2ce1d4af8ead378244ed358af5e3dcd0fa56ef3ecf49c1bcb7e0f0e7c4dd832d07b9a6a820d4a82262c6b7462ac05890ba2a8225867f44ab4fd305f5204003435f695382743318efde3303cc7718b4d0718a1f2207dc21dfbb64c13131fe73ec4c5c8f2edb73b32ecc715da777cebad00b8a208cfcfc35edbe12ef996b402d7a65f76511411afc8d66af2ee71186b4c05e110cba8f917af43c8e777502e18534f03c31d41556c1bca34ae93eda41d2c095131bd611c6646a0bf5e01ed0cab82b9b5aefc8054487f81fa11cfc138e2970daffb7a7b7f8ba11a5e39dc74958d7f40f01058de3b35d4ac3c7058f2d997170b76ea0a75ed597acace0d3e6d6911a42db72841f4ec0748a108e7291c80d001005799e346cf5252272a072cc01b39f77f1bc1a7df26eb88ace0673ef2c659100a814abb3d13be8ab1a6554157b976095a4e0178e193a43644e71a6c620eed730fa7c242bc58aaeda59eec8515c0040a6ec586656cf06dc75baaf894954f8bc50e1635b0d6100805820323cf82bbe269a0df485882b8b71439fc44da5d372d7903618af7407de70d534cf6097a6d3b89c142c3596f2e9f87c7c9f1b19409c45d091dfd315777efdcb3a4737d646740fa0b4439d3f6a7925bb033ae1ce813aa6e08cfc1be0e55a75046cb35c99f00048d755e1f301b616a5b1898768eb077cffd0e87c6d979b0da6dc0df9ca7b8a859cbf7a2da42af044a2d0c161df0087aadc10437f6393c22ce570191d2c8e3c4552345634844fc8a447aa185c14f9d11cb1a065c029042925f06c2651e29e3a8d5ae6486aab5f5724f5e87fff3a23dcd11c5080f37baab9ad3f844c5a4c294a3e0dc97512e600578d6544fb9fea90e60a53200ea742d2724d466cc2e7f5a30ed4231f1e0ed04701e60a53c72c5a307f5be5d02e64578ea97ebd30fb1d443438a5d1da038d1a751f641cf8a8537af229514be0e11a0c1ee06de3cc9c7d490293366770020a806ae2286689e1c4053f24f24d70c413c500824f9646b8663e15f5a9add4b70e4d066e87b6ab25d3369753da8880e36028661aa7b3097ec1ef06b701df89bf5dd8e274fc19b2d0e5adb9338926f04ca1648ae7520f07d21c275d2a77871403e27a8580502955cc826c15f0fdc2408b16c48809e1a6bfe787b42c74dfab21ade71586fd2632d17211105301733470419ce9542259d3302cfeebc89ca90d4d31ddf934cc9af672fbc062a51c29be7062015f67832481bbd64d4ad8d8aa705f59897fe2f397a689c4eaeae0a8b3f9b040bd40f4f440ec6efa0835f19e607f7f7161328f494265fc6a43cd1e4dcb9d106613f998b0d9d5448a07177ed28aadc42a6b1841fc1423c791830aa5cbfb90e0e474d5862cd53c9cbfe4b54901502c6a1135ba692fcbdb8a18dddf1e49119760c921700a1b87d1b2e11ab02b983f2f4b771d75dd439bbf094e74e153a8588740f9c64962747d723ef92867881e9f9ff3d2fb4bb3286ce0d9442fb03ba8909710744a5bd216b7454f533a2f7b009a1a22b9a05e6b7b702375fd4c39434d292350067835beef7eda483ee4483e04a451c9d516c0d205b9902ddb9bad714e2b4270803a812b6c4497331ef5194eac9311bf0a96aef272e1b0f51ee418f26aa4cc90dd48dfa53688002932b56233c667fbf3580f0ab69dae2f0a6afbe39e702b1af0818e8f41fc39b97e30b14664a386a66086a3363b5f02f82af6510ffc060c0cc0b06677e187563301f498fd4844cbea44bd464ab24b0adc4186ac774060c822c07437874f26a147be420e6721752b0ff4790a97e707ef5425a849dac1e35c8810d758336f95070a94afdc09c070db1de9351feb145c301873150906548dbfc9b0bfa0b097465566672c72ddfdd6558ee0ab3fb53fc70c8e98fa5d4b6ad9de181068de4dd7889222141991091ba01ba0d71836440889d55615d7f1764cca9ad5b0419bb95c2d02f7ad493d4884da218fd7ae32798160843dabb07175e467bc4c804a70c058c5185ce55173fcb1fff5aa28416ef9410092fa9376f80a07377f4410e22d6646ec67fbbb16f7e6abc4351ba5df14bfe893b754154de97beba9825110e6a292adafcd5f99dc0febf47834fbb5068ea80111b2def19828d788c77213e030b925ea40954146ade18b90963651a4353f6001f3e47e6da06e2a4e89d55f6052509f329f43e60356a3179d920bc3c3bfa8411247e5f09a61a3e69cb36144c08883d30108da37013947dcbb5242ccf07cdc02597142fd5c60fe19ced182e6e09edf626adf08577e478ba4bf888c920759fd93d262864eaf3c0e6d6a690c3ec09ce32fb1bf63f4c0704f664eb4464bfa00208a37a09511fe543409d31f96ae10e71dd5c6feb4cbe07769c1673175f8b3ece6a57f1a339b5be4b0106f1a0882a0fb4a40ee5fef35cd1b38d2d00898188ebe434fb8e135918dc403bfaa2f2d18a0b1ec85236d61ce437686cf8d856d8206d15828f4bf95b6190122b01cfdbb43a06dfa7fd199bd25747dfe522d93bd94508fcaaa120d1e37c44b519ab5372d1a00f6c2fbc8a86dd917a74b1a313c3bcdb51bd69c2bba4652eef0ce06fffd882790f9503fb6855e3fd2e8dbd0843e70f994fcf09f97d770fdc21b6631b7fc8810301dc0c97e1061ace8e92c61309c6069d535aae24b0b0697a6573f9daeb416798046c60d0052db878817ef417626f7dbee302fe28a32ce5b2ef770df56065fb7604c5894a82d236bf63640be90180dc58783269df45fc1102eb1fe232b45fd1bd0289dd71c09f19c5815ebfc9e2bed42201baae7d019a4c75c3bb600dc47372cd096612e282de45239585a16d31741b018f3de1177152f962aa59a867a7ad3e7d00e03f444d39a59c349087b0489385093a4b810a0825268dbb96f31c28189d1809b32092278655f1ea687f7436415ddc83b542a3312ae49cd6efbf14ddf1def7006f0c623c3aafaa3bbceef7daddd4be65e4c19a8f658cb1d5f3512f6506cb930cdb23903543299dd76ffa4ac0a069910c378775c58c29876937cc8d0465ea2c0ec04ceda2512ad0d8a9b86d6a8e843e9888ccebb3a0b2a7310fcecbc4bc9b800e1db51ebcff0a0d28b90bcc406fa117a13e9c4e973b4e16022f84f8279d478506b80b4d0da1f481efa1977c3f26d4d918104c4f2fc2ba933aaf1a90bedfb70007b545de1523cb9eef14746707935e8063e033085850b1b327c05bd080e69d5905080c7ab45ec71aee436d65ebf7c439aae6673ab8d404c886df6a6588f4a0c00fa8410fe808f656e88a1b958b684cd2eb7c98df7a2b4efcd417c0c035eeaa5e0065857b1d264a052a6fd7643a8404dce2454ed4a410ab6244e821beedc848790da93d899991ff754e91cae821182ccfceb8771ae286c2d2a9178bb9e6cdcded053d1c90c67534acab9db3da5ac72c4d0a567c57ffbe0e61fe23f2c5bf31173e0ca044bcc241df4d75902c67395f4cdb754902dfabd71afdbfb237c9653d7fc901cd58b89837fefe7ed36aa6a6a977b184e9c996a6c65d17ddad18931293823b067b5d7155bcceb9db2f5f40caa65aa8e2a2f1e5f9684ad992a9272a8ce4987d0ef146e37790cf8040318e7a6b19d0f08ea1b9e11a37e1968509d8d7a00099340df02c245a33fd036aaf03a57e3617a5e1b264d7c428daf262ef39e971286cc60b false +check_ring_signature 7fd14f8b67c872761ac000e2cae84a0d8bfbe2df515e447ced7025bfe6be73ea ca8769433e1b068f0e7f77c2ea544b644b890e3ecb778bd2af1d639369bef37c 152 8e067099addad5f082b903c37a0f508079846d7675131f083f35ed3548a192d6 6fcc2fed0f20b9019d4e92aece14a7dd91dabad3e65138517118f565561f7e54 e285dbc2dd75bc73870013ec2da0f1f6fbb0cdf42ef1a6daeb0dccb6674682e5 31e9d9fe4ed77f1774c07e0c0af58aadd596d744cb303be5030ef4b71d7f5018 6b515806f8b670f71e9875cab8925672fe6fbb97fad7b9da8eae7718ab534cca 67fc82d4066187259f419d2c410a07a51c962706297d01b7642af3e1a3dbeeda 2720f0ccf47420cff4078d82fdbae13daea9d1ea10bdeec1da66fcf10daa8131 42255cebe5af2259fc892c41de0b50a0bba4388de20e9bb6319858f8b7ea52f7 8a6ad94997793774feddcd26d4ecca6ecab72c1f9f0af317020054fb68fd172c 8ef0ff25f2f05cca520f942fb5d6681fafd1b495f3661ca80fd3e9a3db683827 b1bc3f1523d30ae435035b4a18cd883656922ee9b1dc0ca4d69644a99d55fec8 3b8131be36edcc7fc4d2f471b6ced730314f9679a60be77185e367d189633f73 eeec518d14667b3c6eb638d5a6049d3a1343f7eef2fdfbe36b63966570d19ada 4e736f1ee8cc2b5227f65ad10a0b0b6ddcb11dc087688dc72754e882deae42f3 234d2d2e049de4d344c784f586b06ef68dd59cf4f735a5e6c0173517530f248a e65b2db107af7c17ea36fff98074996829848ad9cd9570701e8b216849d19e69 11f74bb9afeaa4b165a440ea9199148a503d8194853c98e213fdc0edb4c2a023 0f0ea4528200c3de5e4dd5a3098216f1b60b160ddf0f1ad1bfa4bdbac1134bdd beffca7612890f2c3ff2007c128a7b6ae8406c2a2ecb51f4e5c5a2b76f981253 77650f991eaa59bb025521a064257264a0e5ca419d34672313a2c30923b219f3 efc00cb18b314e2a931da171f5150f1d8367f44bd677941b83c2148cf6b78e7a de5cae725a36da92acd4fdc4819296199841f7bab80d56c0f313fac5d1117615 3ad80f82bf18c430a8477ec8a34c3e15f1e381ed3e27bbadf00c4b98c9720e5b 88ec993878b1c132d73f3e2922e54c40a470a0432c4686f40ec62d96cb1e83d6 575186be9fa21b43b385fea435f6670ea1cb78bae43ab13d770b0472f7ec9002 5b74b079f549be750edc7035923ab0c51da05615f4557bfcb3d488fc4704cb35 ce6a8ae68d5ed8846703bcbaff4a3d116dc820456354510803a6bee4629998cd 7259af4225e5e3cbb10d0d052668d7f1fae0ac520046355a87a6c297161d2095 d2f6b69697893273af2aec5a392fc3313c674af3570646d38c6d0d8dfc85dcc2 57be2b192e5011127ae4913df8ac52c123d68cfff5f90f0ba0e29548ac78f59e 881e16554a2810d0062c748bfaac8f6d6784fc30fae7ebc4dbf2698eb4de2b43 3c2bbb28b66304662ed0bd986233cad88e1437a97b33161fc96654a9e7cd517e f3bf1ee10fe882ddaed7185d57d8f20835dbb7f4f645a68ac4ec53e55bfc002b 2ae2d4786731ad7bc57d3bc867b2b819c6455d1833e79e1ddd877fe7d7f32866 4d218f7a01e4aeb1049a571a40695f40a6daf6bda4200d42535ef204cf57f656 e26d343a625354f4d74d6143d3c748c766af03ad5775b62c810c693f0cf83d7e e69f53ce647d3869032e6418156a6b3d66ad861b729e9bd1b5fe9fb99ee59b0d 2e061d0221d2692b074eda6d3843956d3d07c52f0f027bbd3f34d929e657de7b 5c920ce3645f614dc012e557ca1b2547e94287963f1b1ddd6972306c7864773e c82330fd2f4acc2d20347909944a4390ca75b91145d4cb93e67f8398eac04e31 433404a2f3eb24b88f4c5a0401fbeaaa5cb141c40620d0f92d31da89b7342773 cad6128fd6cccb21b6c91b19d09052ee977bdd7259509c300b21af21fd2a5171 668c16c70508afb5e58a06a95fb874e5f88644460f0921cefee8b511af44ada5 be431b7767c5c3e18f8ea7a126687aea2b073185ef885d1fd3137a81e6bc6206 bbc780ae0f5894b3e1a5a8534e430e0afa1df59407db5a6e54e23de829434166 84d6055a0f9e171f55163ba2b0f7d90108464e0de2f2ffa2cdcf08cb1fa51d3d 91d6acf490c9c4395bd0dd890b1297ca8fc4cc6dd402a129291d45ba8a023032 f33fe5fe49c932a9d713b48fcebff52bb4bac1c318aa88a04318e8083dcf2e54 a7792ae2b89a38e411b24bae02131050876f4a4ff65eb5e2e2eaf650225c96d8 d4c2ac7fb3c214c49a2475f63bbacbb99d0f902b2fa8111168fff5db931aba74 f1d51b56941e67b76bc96ad9072fb6b07e38b0a3af631446ba735bc0ff8abcaa 38af24543382c0f3f33e136e04a8c742144937e9d8bbe051e04695b15927bb4a 0f7a7857ae3b2f1fcdd1c15933a05fb77f732be4b2060d43e4b9fd5f95ba652b 8cf6488b9dc155026435dace99118001f22e656df8986574d546c8dc6dc0d1d7 bbc26984608cafd578e64ad4e1d919cfae2392622831345b9e012ec67cbecc51 95632366b52210de74f1628c365232fefdde7c6c9718b6788631f2eacc897eda e9f260944e630d082e09f985114c494a2d845783b9b8debb00e121bf526cebe1 eeef3509595172f5efec5d915862143b839b191f1469c75f7b62e7f0b4cd09b4 f2a78cdd088d91fa5a7d4afb4d7e1169d43f4e8ec60cdf5f366b221905a5e241 a7f61b1f67d42d5bfbe843194c9a4ef6f3b21d9e06d4dba08b51a348acb6e2c8 6d22f9eab939849d881146d8d5f52f938531414b8604be4984b60eda518ff830 c8013fee73c8ea7e39f23d171647f2fd0e98117b250aaa9f42afa20b8ced9a77 bd273516715be6bcd665b1b3be6e0cac25a8a7ed2aba481e1fdd2fa9a39f1c3d fa3d4c7247c30e45ea8f85eeb72665e1d0a3632568d1be5c2446d13f7e38091d ec565a5b211605417ce726f6342565ea1ef9c7b296b2b8ea0ce716cd7be92c05 6f5efbbb290d8f2293f9fe89e98a1595b28db8a9eb3777711357b16a97329bbf f839921024a0c40d374052e140163fff4f7cf718d8c93426846c6eab905e35f4 8dffe6b7b188c1b0dae48d47378b6ef4e177b0f234f8782e0d3f7dcd9486d114 62e866f901d4b5d79e32576d9478b2f69a1b757d03b78ef1627916c87fe93f36 e3ad9a8050140939c79b58746017b7a487ffad1dbf1fce8d3af26d04fde12d90 ec6d05df0d59dbade7399dcd39509315faf12630d933ab03a9eac60208de9729 4ce6cbd1759b067f1840ac56e5265609dd3c24be25bafe4f5050cd7fb142e65c eb2fe430dc29c819241dc0b161875d6476551a6ae71a28445fc48ac544a49f68 e2e963ceb89269b99fae86a5cc789daea6ebac219b02310e9977db279e331e1e f37da910723af2a8a0d97733634ef6d641a83361a87ab4d5fb00420a30ac5855 3c96931d0ea625dc1b3c16e49ef0d7bca98657b8365178bafddcf57c4ea01e9c 94c76f844f8f8a464536ec073e68980b935d4f58e27ed746bc80a11c4037b099 29521f0a3d7419c4b54585484b156ea8300864de629a05365f79358db222c3a0 7492337ea9052b712e39c28a0859f497a73370bc489855fa1e8c5320926991fc a48b87c98a956d6eac7b3f775dcff4f21cc5c71a4187f08cda7beb5207069435 7ce453d137747059c1031941442b670633c283e21fe82fa1119eb97f951f31b4 4b25a1e4a65bdc3cf62960ec45b2712653417d652c23e72ff38e06881d8d2b70 e5a88acf23eed1d6ed71f0d022769cceaf303e953230f949ab2e4f3d387d9c9c 793fb5ef2b58563d14f580007e2a853e95e1e9969bad82ded0ee18d23bc9eff2 ccd1421c44fcecddc5c2007910f84bb4354ee330203d84e2ac90bf0909a8afa0 6ad1542e60ab8bd4cee3862a6052d163171eacc11d1f73b84aaf5446725e5ad2 8992389cf723a2d79e447003707a09d25748d60ce03e154627a2f707fc093082 8059367d01bdfb1702ba575f9c864fb06185ff318d2039401f3e52fb79ee9b6d 3886842cd3aa89e57a18ab642e01d3994c16d3dc980edac5abc47892b823f515 726a60255776af89ad2796c5809b58aceaf60b0c2450e00c6537e8203a03fe8e 7751ba504f26a46813f2010dc7c15ff980880b6b3482d9cdee93bc2743b0e8ad 86f5cce0f0ebb86a74cfa161850d6226a8adf055da94851ac3c6ce217c88526e 00e2b5e7693310fcacba8b2ef362833c96f9a7350f0796fe91f71dd919ef3a3b 36ce0e8e98f5655329143a6e33451dcddc940af99313c7010027529cb61329dc b2a84193dee35020e67ced8a026b27e13dc73d065a037b0620fbb3bebf39c23e a6321ed083e0adbd7daaf151a86ea04cdd73c158064a90accc637afe944034c4 b4d10689cbafaa3a9899c3667b577897d0bc07e5e25e10bfa459aa5187d9e73f f6a7feacebbff7a9a8bb010a9f1576a78103fffce228a4d3bfac15a69b1042a8 a94b0510d9e6c38e9eb61239968828f463226eb6ff0972db87e3b463281723a5 b8540de72a9ac42192a3b177a2b9f88b49367f7e81b6b854e3ca95c981538522 c1880538b7864f2a6b0357946314578c6d03133a1ba29231edf6aca6d2885500 a17d4ef18ecc22538256843b55577602bc1c324a3bf11e11d234094069b3f19f 45076e809abe09aec65200ac2dbd6f275c6e7d7bfd7539ed34e4c80b5a93e848 16e7747245475051cc39c67e72c5e56e8a7b6909b9d5935d3fc57325bb9dbb16 fb4f874802dae7bc23d40d716e8cb868e27b87ef169b2db1ee32fdb6f3204c12 af362309161d5ef55193ec43c5c5ab75c8779ac21d3e8f9e4d7a9e4289d76ef3 c6f4c52e84463cb455810ff52dfb1c54ad60636d91638a74098a279105ca013d 717172a3227f49abc5391f6bf3a907b79f6469f4d9c9c1c4a46a0db0952a7de2 e065fa7f583e5e30864fe8e4ff065411960e6d2d166704f31a234ee8876cf545 bc743a9fc8c88f079d6a613260419067e813f938b9933925cc18a622bc2d2c73 6cf5facc9bc07efc7bda9264079afd8975876e834e2f382f84585e0c7a3053c4 ba4bdad819aaeb67ea087bacbf813040488f604de377d977eebeade2bbf38458 952870f640fe735b87a9c58449b797ca433e1fd5feda3c6bd18e2274606843b4 e5fa2b10fa97d09e4277d230ff9b40bacb5a7d065278c162ac1811d054c71060 0af9fb40f963d0e74cf2e33aab155464459d4fed8aad67ef7eef9e99d1264dca 12c876ce85bdce4f40fd20b5b92e0ce2b34fbcd23c1224f75cae07eb35e3ef77 b4b06da2881e05203bdf0a3c03f2dae8e5c62f57f873454dd71a0a5118c41ae4 e766caa06b94abc7a2197e7527988b3e657d2416ca306808b5647762f4015237 36e6d7baf642caf3e91d11998327745d66f53fc5fd45ed0b0e436053151ac1f5 885b2edfe9da08faae6bd36dbba3ffdd874b60880c4b2470a2f18ff52e745dd0 982963af52c7252f6e865b8246ab82c8b493fb7c38d38b06ba7fdd2abaf6fd30 f4617aa73497214b423e2e169f7b4666813d31ca5b84099decf65cd669027e6e 70d24dfe791023bcc673c02a2971ddd806930f2d05b3e6ccc2ad11798b5bece6 e0d1b57ac862ee99f03489dd144914a56874f0a237f26756cd2f1d3e04f6db5e 24d7be5f3602a0e7beb7638b1112a9feb2bc60cb4d979c42981ae4fa2b265721 1adb5c63835fc5e8bb93ba349e64e6a2f149d53affaa1309451799287bbdd0c0 806f1f138eea90528b9473e727106a7f1b5ec328621d52300dc70158dc08e8fb 38a11328e3aa31472bb7a429aad706b63647f33d63d93975adeb0596cfd7dc90 69edb935b14c9cf18c27dae76835a69acaed4370da689a3ab0746dcafc293ded 7172dbbafe9b7e4d5fa3bda8db965f924e52a65a56a903f2fa6a5fbf49dc3047 10012b0a3c15079ebddda2de11dbf8f913c43a51487655e76d835d1423a79c69 d644ca30ce527b5ec4fe070c40516a2d2051674272af073017baffea2aedb106 c72a688b66eddead109f35d9d8a174e2954668416727ff1297e368c379cb853b 655ca03455abf31212f791502ba4ef2924f1c53afe00462adb0a132593de8cea cbdd3679eb03b5779aa993839252613caf700e9749df16e20275a4374a42df3c df600ead15b1d31851bb62d935a3b7082910c04ef27d237653c448a2bc6924a1 1d70ee9e64b025e2223af625b0d87a587329b917ea0cf2a38d58b3174f6fde02 83045572775c131b7bf3f28b5e3ef96ddf674039dc2380ef5e29e427b7f777df acd92bdb18ce73694e0731d6034fc4f32a13ccf085f3caf4e65ecadd3ddc969b 8ea8f125a650933f381693a589ec1eb99f765bdf584c2c30bf370cd21b1c15c8 48d01014060d1dd06cbce0a6e52a863f39cf85f37fdf8d0d79a3633e9546ecb6 f89cc10f86b310ef9a03dc91118d1a2aed4af8710f3785d3f379ed2709c405a9 50369a3b3d7c0cbb0c22116407f4c46d19420043a63e27e2591e0da791d575af 5a56b2908c2a9ac0880ee483325a5630e547b46999683751eaac295f823870fe f73c8d83c2b32c2a4760edd631d88a8bf948e96aa2fefaeef3150a8c85f315b8 9fa14c2b3738458d15a09e343c9424ada826b5dad0585967a2d8573e50b5be0b 8a1d766d1737544ef7107f1e7eaa2ac15ebd8bc406d1c01b2c237830c1530b7f 7dff37116a2f63bfb9b518854c5aeac4b0c20be54c66a4b0e3a716a9c1484524 6f45e3e31e1732f220789fb9ec5192e5e7eef94bb821010b9f520f4af9ef0f95 50744c45489a9e1b3b5c4ee5bc6cc1aaea31dec97196f661a5028ce776be0271 be3331522fe1ee2ac68ba76d0a1deb4d2768ffa0fb3bee02989218a6c2e5411f b922ab70fce259bc12ce24edb1b4240752ca4d080b4863f48d1da5c1a7f3683f e77e323b16792e7d4e92a0d4565bcf050b4bf6daa2b5c0153372934bd7b9140a21117cacd502b6af01828a3c0ed2cac2bc3b22d6c2481a6006ed44f940b6f609722e12e2381846e8039c91b661929c2cde61f1eb1f7e9dffc56fc901a7970d092d064c5939b3240ceb29b18a6035097f4351e138fb97aaf5f0ff064a36fdda02a22ed0274aa56ff58d610f1e24c243ccaaac94bc20bda8db823624fd0c8a470d21ec627208f08c2d7689160d8dc6ef8b073c6c0d331cdd576abac7609e7d7d00d92ebf9131176180756e06ed9060f92f5215fc1ab67a4055bffff38130b03c072b60fb9484b7d02b4a73be137fee5151733bf3d33065c468ba05ccef0b8f37024edc45c581e4e095364ef17cc54779bfc757fd1b0f738f77e18507161f9b6d0698a573f3ee57bd4f443a92578e219dcad86bbdf03d44e0d630240c453fd6f7077344f77d1912f00ce27d7f2b840e19fda65fa88df9b6dbc87cdf81341c0e28046fbee2092fc6630f38246d1a9dcb2ecb6797bb8a037c9d01aa26a195f94c70092f312e106e3d97b453e9e3d438b1ffe4aa8ec7f4c7a7e1d3b4ffd6831af18e01a49e91c0fcec59c47273d5bbb54441653b5d4d82e3da3365de174397ba88e204fc4e4d40e01bf9def7030c3043bd5b021cb6612b24f966930afedb90903a9c08fbef2ca006b1db775b2279599b3ee21d5b1809cf7cc6b8e83764d2f3bd4c8b0dcaeffc3a65ece0d03d9ae963f2194ba2b524e901c9c766d69f9e824a51c0dd0395216dbc859ee5d79240f9959f62e7539c40a9a0e228cb187c530067c7a28a0e11d71cc0d31a1642c3603c310ea6123592f5e770322d9aaae47d6e516ee6860d49d9c62ec72eb495a88c7503e5b1af88079623e257ac292679a417a2e8f791025c6291c2cb2bc44e28164c361c599dff349e0c007ed0142affe163c6dd7b420080aaa2644dd2859dbca20615846cb4cef1cad479e10abaf9e7ab14a31673540bebe53c8e566319f3c5374ae2c3fcfc0c630643548d303ff5b830d03eec9446051999903ca9a700c3f14b54a11868eab31b2d8190c69fe8f297a6cd03471e4000b47354b7c8c71cf2eaa356f050d346b6b1d9b1e6cab25f9afe257eadc8de7b0af1710f50c532b3d979a28e20b83fe71867ebc6e3b0e28257e638f763479f2c0a5b9c7a371b59a727fce9f6f581e23e717ff22fbb20d0b81ce3ce0966ce118a09c49f6e8fdd6159bb9b3fffbf1bb534745c0e937b5197d7c4d892ec1a36f2c3031ccdc5d1da5808cb9ff4672bea4830a4d1bb0a3138ec2debe2addf1fd9074b008cc54a4e1131c2a34f073bca42379b779560357f199a7b66b8dc602e63d24b099a0b0f5ad257e7fee243c4119e9fb3d5b5681f08fc813f91864ad35e96a3b10b27bc250d28a84eba22ad5814a18ad82e068723fff8e5b3cf3f81f6a43de2bd01e820239f3af1ca1dc94f9179d8c0c645b5b2693b485c8754050c6a0af9146f0fc12776e9d87142473f9afc5b8ea418fb2f94e8253432529b60e66bc92661b804106737e091dd58d979e73dfb632ee59b258d20bd2560b2c851763f7b01a73705196ff04ecae1fdeb0d3e516b49866dec2362c9c7be1955a724d4f40d7e882d0fe83314c011ff86dc2b78b8c6be7e602c461a5df25e2b5668f62fe76b9d3f1c0ea0a0e6e525890e1ea0b24c0c212f3b085c68298b444aa873f1a4d6fcc77eab0c96c8f3987dafad2cade0422d689e1bbe18bd50f3b5dd6fe1dc9bf53f5c52e20685d65d3651c41459ca78dd6640cc85bca90a0e556474fe76301ffecc13445c0f52485b907cb5e9cc8cad5fc7a358539273b9d6f673c7a0e6d0a92b482cad8204b7d3885b8ec5d564622679949bb85dd75473bc7f9e2e0a919b9e91b3492fc408ee930df8f468c567d9e9632c5d12959ca43c2c43bfea367534295ca0d6ef820b5193c09e783ed3f784c384f139a78352cca6b7dcd1499a4f3827995f78eafe01638335e4f536885b30a7c9da979240d81fbf45104c989c49f64f522700337505b06e9d3c88253ee843480f9e72f629366bdf00ddee6b635d63c592f58b050306cfa0ece818f331ac018c8bbd259ae1dd480c9dd6019d8786fe60786124c7f50e187a04335e117673f98e5da523b9ade4fc7fa6d59d05343cd29ec467165f4807a52c5603016d7b85af7af10c5fb5af2a41e61110fb094b97ab72e16e5324f60d638af33c50e88868e5512e983877d8c3af5c7ce17fa50a2823eecc9846f9550fbc6b963c6574191460ecf44152610c8d26ad7ee78ecd42b6d8140cd7a36c420da9a6906127a63f27ba85c2428d700f78ce84204d4cd70a7831a1ccfe50ee440b87e5a04fa2475a69ea307fc595fb0ee7da4ee5325592d315db96a633b619db08b3bfcd17a16257eeeb119cb33283d0d34f81bd2e47c12b913e78f4e86726fb09945939575402ed381290da50a835c988cb8211e4c9f8b5833c3850914333fb0427edbd0254d85a0e20b58270b5bdd164ebb56294ac65b969db2bb1dfb7b43507f86d0f6de229f5a69ac105848678e19dc8eda8e997667a11a1b2b81a835cfc0dba062a71b17f9faa002724b79d1b5d5921d78928a04e4320dd5304573c3fe60933ab330468a1ef0bcf658cdbc2edfd8a06d36489710f9e8e07b6b52c798584070b34f8ea5c962946b02cef0275f1ad63e9520f1cb1fcae812f8cab11efa2ec0794308b979289f35be25905ea4ef91118906cc5e9eef7283ea2be369e11903f07e8723f8704dc8cfe0914671ee03fb59f10db997bc9504a8851b0b160c2fe8607edc61238220846a9d823f08f5762ccda8efc6f72e2102cc53828d384ee536b0e66f82ae7622cd7a9e8621624dbe0e41eab76e21e31ae287cc42e984214a9bd0f7701c7fd76029a6661252482c2563ca6e52e2649a4acbd74fc9be62ef86bed0ae026873614d554fc9c1c606bd8fa9f03f2a717703b0eee00e6296f8015679a06f49e053aee25d1a8c679e6b66444a9fe701af14bd5efd8d515413218fd7cdb0f1ee86d369c8dbcd36c360758bcba629e002e6adf1e7a92b393fef6da651d6104910ee809a1a74f4f7ed01b7131add0b70d18a4107901cf66df5c680ce741d70383defe3db61b979759d8c33f8dc88ec425d5d0afadf12d1ff54d9084316a8d0941466ae1448589416af44a3913d666a5cf22df1721059367d26fc0012a3d570d376c83df34789f6ae1c6c18c5c9a5b88c2ea8c679a49f510dfcc65bf3d5b3d06de69d188a6d93a519670b35a4609fb17d4f2248afbf21d1ad1403713de103e02849e94d2046ad5e47a4e9cb14c77684908c77c801fe9b7dfebf98f862fc35b03de9775bb7e050e9e0c21737eeaf97211c004de7be0dbd5a2009cbcbf88192b01d7145e482a77c980c81201a6c8e86d94c6910d9b400fdeb77c7c208ef828b507077e3e539d8cd32ce40fb6c08d938917a8f99a14647c7cb96bf63cbc4aac3a03f28299418988091de0d122df2c2cc121a152abc778e91f49518cee1e43cb290fbeab3cc6756e363c81f3c4cf952cc54eaff6dce82408ba5c36649773a5898d061204711f0e220f282608fdbc90e9a547b9f72d880546b3dcfeb4f2f8b8e2da0ab17c6c99fa6c1220032ea89629f8203e8d808f6770a7d4addc34f9e480ca1a0894181860681a65c7b556bb1ac6bbc442a433326ebf83c4f8d61557f1b0999b02457921651456b247722c759443b0efd8d5abaed6d0495c60f86085a529cfd8061a7981e17ca0cfb87615148e0d40eb2e2599fc20519499f08fc94296393aea0f08bd55c6f571121d4729e142127444c34859078910df047a979312772816bb0b592a4e7b071cf1c52ab860e3577c048a349fa7089921224c4a625466f0e47b030d59490b9054e749af6cd8464c605eb84609b938610145388f10b609ece8900577be66a5e5818001078aede35d00c514284178ad302d545d45edd0296980590f85a4687ba7670abff116a318f69f6dc693acfd0ead284995726f862f84c4340865934bc16c8ac0ded2be963f6579322b404031ee81af23ad51b0a8c93ffed502dd1ea079e31b303a670012249b53602b41167a634f95ad43a5e0130c4a347b005dac4b6cf2e079a784bb664d705363f6b8d3c03beb82c335268525f5d36dec05432e4bb33d8fa3414dc75c265270afadcc6a1db4d56bbec313096d6223a2aa0cfa53ce486de10390b16bfa7123c2021ef096f45a0468e1fdfc3dfa87238eea0fcbc94474b388e5184cdb80bff1124d761a54aac7435f8e1d3ef30b26e3cf020c7a2c1a75cabbf77fabded51bc41bccdb7ebfccbc70d4b920fddc99e2b2a1b505e5b3e4d56fa609214cf2e96a98d078f5b706479760afa9084b082f4e0eec3a0416fa1af43fcd95cd84f058c7c4ae313ee8e5feb614802dcbe38e20b4f29f6b0c1282fba669d51e211049071d1560ea4cdfd5033c38722d555b4c310917fb770abb80e72273aa5e129e90cf9ab0c72ae35ff3abfe9da3278cbbccc07ec343a80345fddb397ab7001bebe0d2f77a0d0338311b61a8047ef9157257a1a7d2f642023b3c965306ce0407bfc2fc1b0a05c6f6d9410e841cc00d24781a272d19a184096d4d2356698d5465c237a567e47eccf955a8ccdc2dc7d78fb195ba6e6c22f5039988739d35e89984ba0d07b76b42919f467af30ba24546b245a359d17a168f0995536f99215de36c043b159de534b85942940063c584e5d96ee05267f7327f02f433f3e21fdbc5f4267691bc6bcc073aa435d23ac3d061646fd4b72d3f4d400c2275ca1a2aa61615abd5ad541d6fe63243648c4b9fa1304f880e97af47fe66043529cd33d192cf69147a1ceb2802bdde0281ea962e8af5fe2668156b76e9da033844ccb3bd522df0a559f382662bdf74673879ed5dd9eed5079d2316b5dc4e07ca39d2855a3b738113109ddc8bd7faf1be4ef982caa0aa9048e3210caf53b902a9b172d48cc0ca1c614a74bb2020b5ed5ad56b6e8979b429bcf0e697e5615708c1ef35607c828d186e0e41711e6f9dc8b41c810f9430e8df45565c8a9704de0dd5c22dd8c90a203be44fd6f63a50767861cf81733d6ddccea31bb519898dd2064b4c8a61cc01c96d7cdcf54735cbcdf77b9620cc7d95b5ef612c908203f1fb065a903cbb00a226a3a5e9d62a41a78f2903688f8a050bc5dbc1ee7245edcd930f20b6711a74afaca35b95008c380cd07e69f0c1876dd5303ca8b407c1815701072db803d4bfa139f00b74a0883747168c0a05e01ad078da9212da6512f0912c0daf890c0e8ab2ffa447896c6277ac399cc508487e192521c1ea0d72c909567b079e4ac91b9e508f21088a9ef6c26b9563b4b33b6dd2bfd6842fec19bda8f52500d6ef9ae19d09a1586ed23ffa523beadb3e33715c6e17435dde2f95c19610cf06ed2faffc759b5c528d89fe6ab67cb9f05f22ff2264b3f7420d4daffc8c1e3f0ef7c900298a06e747d8c96bf7eaed2d30a60beaf96c3dff9824033d68dc405d0b86f0ad37e74d5ec3575f3b75cb997b7386e0109a30f7b6975c1e6647747a6309aa90df4ce9216814d418218328d086bc0f21131522153ba097cb72d6bdcf300e1a0a8c0e4a4c0aba16e583abcb506347cd90de464bc794eb43615c30aaa7bd000ed57eca0cb24c061c35a49305089337254edb9c0ab8e2cf553e7208f5c17e04bcf5871af59ece0521fe95d13c96108e8578ead951e8d99022d1e1851f44b5075d81546a7a5035e47253a6b2686b28b23a5f41d07df5b9863ea5ce8f8c1e8b01edcfc8caf0b241b5c342df198afdbdcffc144364da5f09abe69007ecaac1da0e01231a9b683eff6b4b99e4b05c49bd65a9ab780eb2e85ea1cbb118ad12223e0e6052d6da6807e6976ae8973424462af713e921b9304e0bf5281b664383817f0ad297cc1dfda8350774c6f50b97435a6d5b7cf77e1ad033490342f1ae9b50f909c4f284d690116649fc15092d241824515bf2737b233d846e319c88c162ae310fef3a500e48dcac50d53cc5b373f260440bb10cc41d9b788d4ccf33234e479f07cbe467b5d2fd78a3f28434685089d232d4afb8340d8cfc2d186d34476b51e40957f7e8b224c4fdfb10939ffa5ee884efd751f890f92f907c6ef2dfa6061c1909c2d26b7bf6e051e3493acee012990d5056588be1e2bdbeeeb26cdf4f4cdf4b0a4b83a12f65977b9d6fb0097ba6c77a8e6f6f23d3c1d26e0684eff0ac6d7c9002be55677f62723fcabf4958cb7926d7569232730f34c179ab5675e8c2757a9e03d58b3f5c78d906bfeb87c6650735b9a25163aca92af6796c7c3956fad9943d00be9ab07f86ddfc8687541cfd4f2e273ef9f85c9abb106c9312cca249db86a50e26e4db586d0790fc06f3a3b4f227c7a4a7cb3ba0f3447cbfa743743e648d3b06e678b60598d1b95ae163433f00661818db436e2814f76623c8ed240c16a098073ef693ef2c43b86d8a125eca62a29490430f305afa91f14858c30e7f6e07ff044f54bd44425f3bc4b5fe521d8ae466f86039537e627f035dd187c135cbd8010a90bb1e3d3c8fbd81814b8f2cd447bbf3df69bba513afd00b5a0f8282fadbbd06a3922fb569d7d6e99d326d3d6f219373a8e44f7f5e57ad56f0f69752b937c40f25a7dea34bd25a77dafa649065e64a354e758e7c8f35560da438015e0a8aa108def7625b2c0fa9e9d2671ccfdb455bc2a5d7c45f4f0a606cbb73d5e6b795110609701117ae2c5e09270047d00611e08b9d3a9800d447ed2c54b9214d710da8019555bc90013ef98edbca885f420d1d052c083873a720497555cdd433a457ee0d15d16c46dd5986c1986ec06c0a982f066c56c751a33b912efcd6602057a4570dd02afa5cbefdc15edc5315a82456dc861bc3e73c5bab3c69dd973e853d0ae805bac16c68034cd854fb48a7251bdfdb97b071bf05f4c32ff67484814bbacb340eef086f4178ae1d25f7afbe3630fa0855e76ddfc14f2f32b89a4b63caa1c55703605c65c77bf2062e51e729ad6c1fc5efdbc45aa0757868eb99cabb702881b00d5be1118956eeb01098e0c4c9e9d66c3f2335360d986820f760640445d2c9510074504ca0ae535ec10856ad0a6af6bfaf722f6e85eb4d3499212a67d0c115920516249202ae4ea7bda2dd1b4ab0d159c355e9e6821f51965f870addc7b7b94e090e81e718120d53ae21233c1f8b9a6724fc5c8fb950f7fdd199b6c7618537d20d12c5c59a4974fc43f2a4221e64b97228ddba0ca4c3f9e4f8668428fd295a7c0c615ab881196fd369636a3146bfa601397e4612fff2ac21ccbd9e27291cb7660f0c931316d384268e6c640abb5bfec2e6645b9b45e7a906e6fc11fb998837c20bae5e39eeb4a6b95c3fc95c9c775f64c1923caf8f7b5ee74d84cc393846ba6e03cd2bf9bbd3d29dd08c0d7cc3843f0397ea50743606a7a090a8d372f01223ec0f80cba87b4467a4f36a98908f4fbe1746fa3db5cf4e0eb9bb1e6a60e00ef6ba082ad14a3e76a3222fd9cc4864d38c4c0911f0c791627126a51daf977980356202beea3bb7eaccc9c28a0fcb0715b30e7956a3f0ff5b172bd06815a557f79ca1078c6cbbbdc803c1decb86ff976642bc15d1e4a122d2c14ed969b711223d6c3c04bc8290d540daf7e5420aab9b650b7f7b08b40deca7223f6988a65960d6ac360459c3c6f3a1b0972818b9957bf5be43d7051bdf032f77ae4b0d6fa472333ec30934e5eb14a82912e9e5e2be069fa3062d3aad3fe507e199c295fdedea691a45005639b06fa0ee31ce8539f61f956795b443b70c30fbcbfba4c92ddfa30ba5030f66f5662906e186abdec51ffd7856df7bb49af06ade7214326eb9a95b1465100cfc07308c4d4305a19b055f6be355a59dae40c902a5f582652fcd2cfe4d0be406780c278f82a42c14c3ddf0319f398ec532a56ef4c2a01c7a19dac3737cf2a80a3dae648d8e88b0eb0c02ff70b70d4d08ea0e5b3389af849ee6b99a01ccd75908bd34bfe72084a010da33951f0d642c610f2dc5bb3b882d41d6d34f6b4a10a205f5f8bdebad91b6355c6c8f221d9d1184ff5dc76349918c051f6312c4dbceaa080a6a91f2a7ab81d85f1629c33c556ae792b8b8f9cb413cfd9749c350ebe00a0210f00794bd0bc2f0f669e0812f3da916a1ee6a069e0f8a2bfe40d3a22237af00e8c8b507ed9c7b9c3d57abb017b775217207890fbcace2059f91fe8f56be210bb9318c55f1d735d752a2cd634137fa3fb8d9d7869ff422b6ee222bbce21b6d04d2e12ae5b531e43ec2a68e2e77346be8bd121c7b20a7b59e4305aa210a8feb04ad8e97f5c86eed9abb4d007717f6060a3f7a8dfca619aa3e3d3ae2dfe7cc010992abe66ce2e8a8695b209afd62ed37187d5010cbe080beb06c264c58a3fd280cce499121601361ffb78a1fa3e16fb3db56c76b0134b9c6823e3580ad18ba170f1433813d419360d9bfc9698a1f98e3a7afc3636fe8684e08383b2ac02c1f65053029a625678c63754ad9da2538a9a80ce0c6c8dfe66ced62cd0584b243fdc7069671588ea39d920c08fdacdf3ae846ef20f20bf4ea9e463404ccaab54dea630880bd5be75a8bcb869149e8421ea58b4e544326f7c487368b1183d9b58eeefe01755b06b4029d3f7e7a61fe9efcfab93783e85cd9914f833e4204d4638920400d669be9528da6797582bba0850baf1121f68c61aacc40f38fca8a196cf44c8a0129ec8a187ace1f5f6bed109b3653c98e3da45067a74c624c0b4c4543f22be70227be5a8163a2bcdf2df3957ceffd1f292b7283dff0f8f9c61fe99cd755d92705c00848a07bc403601f1f31248d49a5601b543b38845985fd64cd0277583f3505bb241f736e822df8d9d0ce073a3a3eeb10e61a2e53cdb692488cffd834d7180c0ee787a5de80d41297ffbcdd61727c7cbe7bd3efdc716aea261a1cb540d7e705eafa769bce85f0a8bd12aafef5d10af1fbaaee205595e80c4487cb12f255a306e6a742de0652036b3f69e021a3495469e7907134f90b0ec575289415eb3a8d03f7d9115f3e79dc01bea71149e04943a936f3deefcab1be3fd70d1ac298d3060ef6c7eb2be90a056eba099ba96e2e755fcf04e7cb90c66e2632f7bc96875a900cfb1a07c2cbe3f4f1e96af55f246e20e0a9cb09044725a2ce41efd9051138fe05e07aff60903616e3eb91a73f40b849172e7e40c6e6072bf15b20fb0bf367610f1ad76e0a0922a3f60832c39beebf7fbf3d789d1d93f222ed209bb9c088596e0065943492ba5c2f859b6e3c45048285ca8fa9fc754de5cea94ff2f8c7d0073605c2316311cdddb732fbed77d935f16a125375e6aa549b0a190832ddb7b916e60971ae73372b03a2d52008c1af50f52079723c3238bc37501e270158cef6e344079cfeb67cb7ab15c47570aebd6ecbed7565e717d3a59d96e3703e5c43e25ddc075dc4cbec385cfc3a25c5f325987db4f5e8ba9765585c6991f9f7e2af96f60b035af8b57228a0ff1e686ad6310c55db13a858f9dce2b90c4bd3167706b0b2e30ba650675f7fed8bc608d4472b95b17dc074b7b475c76462a71fd815226aa463064ea11a65d32ce5128cca845d9f20e250701d43502d31421983ad4dbbc8612e0faa55a51d5fd0429df2b6ccd9374b2c325b2289c1f1192ac0cce53cb0ef51240e887805dd841f27e471a5869f44d1c5307ea8f9371a52bfc588c223e73cdd83019431195ebe6edcf1b4511b9bc2dba1283d194d286f09f9eb64c9529d05177c0d1b8f76b8ae597be16345d22386cab0003a7f2ae8890d2b0f29fb79d133a6ca08078b2288879d1e566bba5a82931ce0d6fe5806277fd69c6c476d5bea9559cd07bba7a4857a985a306232bf1b6853f0ec2a66355e0f82febe9eca48c96354d5070d8bbdfcdd9a151ac34a91999f6013bbb795b6c5c111fc03daecd6771eb355019a7abd5cd0272e1255db183b8fae8834e7d3e161ed745d4b56c64529e750b808bf95daea9f683ee3a3cb44dd3e26163305ac7cf90d40d618405293539890e50884036a06cd582b5a488058f454487be3349d50279a8dcb7c7cce4c3209fd4b027fb625e9d0563b8b4c55757f9047d17d24b7cf04800a47be2fa7e4f49b699406a032745bfbea205f5193504ed72c7a76b26841d55f01e804336a3b0e5fdb160751744c66c223389a09179fc58f6cd1e03e5d7a1b7ed9b33fcd4d3dcef5884b0d938c766bc515fe6112d7755c6219141714131ec9b8ef55e076bec9a7b36375025ae71a77c54930d0e6ab9ee7a9aa6fdead1d2938b23add93239d23e5595d150b011b8d2f40676a15db1c6423f6d2b9a31a7a0dc01afbed9868fcbbece24fd5041b16912e9c63e4bbddd20ae99db8c2b90b713e81829b76c1241b7b24ccece20c63dbb81db1bd40066bfe65c48890b1a619c78aa733b6436030b17e797f1d160a84dc2cbbeaa4a6f6aa336a93198abcf4e14c0931bec180141f1591c5653cbc0f25c15f11502886ff3b5aa6a698e5d2b81e50f7fb45a9bf9c494c849e59fddc0a4f98c5096e04fac5113072ec5f7612c756a112a7228415093bd89d468fde150129d0d94b20a17d42ea01dc3bce4793122e4a8ba1bed137950ec011d2752c1a0c2dab5f710ad960777e67782d5a744eb11ba967ecea13ea0e4dcbd76ce35cfb01444ae0897f0108ed315da4032c973890aeb165a167586712e85d8deb6b4ba408889a32320aea3b33fa78c4b4f2a2a65515530898f385c258b072956c25707a07599406135871b6b25d71757db0b11224fb60f913a43e0ee0239b7cdfc340240051c4061df5efb92a99777bc21b76ebde773aed7c085470359186b79205ede104fc68a32b3c6f37c05972deb3da1f3d594ec4862548814a8d8ccdefcc6a91a1092adf23b68df9f4de57cbe4c0a63e4289c4ff9408cf906ba4eae21eeaf6059e08ea0b69d0720647946ceae7636bf89bee3afc4608f0c98194ff37fe4c8143eb0cbf8287ce9b403151d8992fbda737ba1b5db3e2ceacda25a3c217ee7359d10e066be81af606299707b0e258d23cbf12fac967d822c72ac7e79e352cb2ade35702e50262725ab3a2ca935f67691358dfc77faad0faab7d9eb646e2484787a79c08c77b7e25a2ceacbf43d21f563c4da55911d85a5ad3265e171faee35616634f05ec80b7f4df85d8c15e61b4d8bd1a14198cd39c2b03d77f557487d72687998c0f563d5c74b516cda3f1bf7444dafd8c357408477add3cebf2f2e57bd765577e0dc72b2c3dd66e6a6ac9ef30cb1b76b8a77996e222bfd92d019f42503d8282a903343e85b4d20b4180650842066ebd2ae96b4b30dcb6ae0964abee691d1dc92407ed2f86818738d9a235b2b0a675f364958ef19dba91c3110d3d80df3a808b9b0923d4f7420f958e1f7bc9014847a1ad7cb61e58e7c2278decb37f0e5d13f7b60dac973f072ab3ed549eed799170edc1cc8f84496550485c482cbbef14f3115c0598a81fb999d3093e56e109c15364921749b9f6a0eb597ec221159b932b79f40d045af6c1829a3f06d33735dfb5ad04436c2456a6593b99f371e40e97e1b436039aaf34e979acf87de039f2f4fd7e110303e76f2c98c1c55900cd5fa9a4e8050b1fa40e1955432e31369019a31972340454fd9e5984fea1dc9fae279477bdc600a20d461af0ec9a8a31a6eddc518124f6d09a547025184b6cccb118dabb45890df828878b5105768e5abdc817fee9d70b6da88b8343262f1328d20dbd17a06106b082ee867e0bbc918b8844465db96bf1b6985df60ffc7da871a1c57573703d0aaef7ff8acbca280e93d43d60027cdb3e0c19707d2c4142400d5d115f257f730451f428e12afc7f2fee60641e63ac5c71850fe486ed446aa2d0f272cbe6a0b70bf75f2091de825d30e299146274740448ef59cbf7bc70fc05f075c0f675253c008851c06260c92f203aa3dcaa353330dd4dd776a302eb7b41b9ea0e28c6a87409328f146537fdc5bfa29142ca6b809ffc4fd2a83e3fb3383bf88606e24b941b06cf16250cc87ff34f742728f16b5762ba58de8b4e6b666d93c8686cb798bf6f096affcb890f25eb24ec9ed492d9c009acd74a8d72f744c9ed85ac24d5c96ba9034bcba095d4352c8edb19c76fdc1b374bb6739484cb72d012371c63b3eef0cf0a9c144e3248280a380effbe94979667ffca6b6e0f404205391ee5c7c0f430ac0ad9c1d22ec04bd3ee13a32346a6f66d76910c173dc90b0c44bab02fe7fbd16508b1ebae36aeb25e82a729d2a48d43e3472ebb48f68670b06236fe0b30c9b48503faee00f84effcdde377cd7c5c1cd3cdc6f7683ec3befe30ebc7c99fe965ef304fbbe19008d2b5dc65f65e8b5b886dde3cd3bd947685eb21e9cbee72e2ec13c0078fc93eedb7713638dc30d136217af33c764d126944ca70a2614445de45595092ce2e4a4fb343f2e26b2abece58499cf57cdc2d581795f6cdf5133d7ee6b220d95bc3bd28338dd2a7446a1de9df66220b6d93ff831ec50704ccb9eb14d45d103d95cb78071ef673ba007d8a163088d21fe50abf87216f360647efbbfe17a8207e9779e5d00e440e1c1b8f14d79b9799fe9c1fe4337a73eb7492a28d59657ec0092c0dff0ad4102d301f1c80d150e3260ebc5ac3d3b4e95002962b9c4bf0a4404df4ad65077555955ba6e97367bc4edf0bb831ba22e1fc27a1a080c81643d4807f1ed49981d3f492f9dadc45739e37f088e998ff7753aa15cad489ae6c618040119c1f55bd999930d8dce6ec5946544c823b8339180b26ff0a5db417827da210d11e739e6ced639417e3f76755b9c90d77a93cc4d132d1f67179aefc85a9ee801937e72ad11cff4b34f99d226e3c123ace7eca58ffed77c0bfc73eaea37342a061f7d43b6ae49eb77947d5cd6f92ac8519686814ef892cfdcb8cccc0a0224e1080814fbdbd17ae7acbf2084dcfbb129d6a469cf8b2df358b208cdcfb5256ee40903c588e0ea28e8f2f84d066d966e1bdd17bd43b63af21af4dd434d020fb86b0201f791a826f6f38a7dee911b8a1c0d31bbe185347de20c4f1337acd0cf07ac0d4239842597dca2f2715617348c494bea2d99e02c205de55afd7f11edde2c1404b022be659de27173f73ff3e0d8e78eb8ae9b9bbaaf454d265951ab4bf82d3509f1997bc03873e2198c80836a20f36bb0c29ef71af4d20320633cbe01af9acf0b910c6233ecc349448b2cd3db4a0cfa071ac5ea5602ddf614c7b81fea7f52780e7bbd85bbc4a8f40e072346f5b0a626cf19e6e5cc4f11009cbafe630256df4308b6f84ae192d0f0dd6f47c6e1c7b86f0f1303315c5b2058a1fd062fae0cb2db0de97429ec81985c0164a56c8c0e0e3120a5b2133ccd2ad62eca1b88e8493b9407635084ef8191c42dc1ccb6b755bc02567b82dd8e5bfcf351298ff2d244b6b509497ccfeeeff958a71582c45c68f7b95b6612939c830a681d8c5f42f7783cd8064edb636f318c96ef876dd9abba9a26944c78165300d637b3449d2cc67b21f60817fb973bbdfa97bead0902cdcfdc651ae2c60bf12ba03e4e577dbe3a5e5fc50a67639b5b827740f51b124cebb66fc29a3eaa87bfe8ced2a8781fb14b0fd4800243e556fc2c9a996d57cb66feee069da4eea289800cd4deb804456b378e474300a2f1509fb70a7e08657bde14ae46174abb3ca8c4a8d49cc65402ac24c6f3eb0c21ac79c80c63cc30fde7f28ac60232662c1b7aaa9c23e5846c75a6dfb114af05 false +check_ring_signature ca3a9528f7f585ae1b124280bd382fa6c90eecccb16a3d2282229be8e0d37f1e 34e8f8998ecd77e8cd5eededaf9c408c4099c1de6554b60b85b178cc6a184f8f 51 1e39c98a34ec3452edf001c71084106d693d8eda272ba539d6015316475dfa1c 51b39baabcac6de09ab05768cd90ac38f0d8f03b8abe5b608c2c032f4035c167 b09471957a2335deaf00f41aaf87714a3724bf95c1af3d109968670d8bf55a10 22eb4afece9e7e02ffaddd0c28911fb19d51aa586778df49750066c23d6590ef b91d7f4329d08d46e7c2c65c1f2db96c7f575c5c07b7d4445e0a8a94db675cea 2b4f712fdfbca6e9d8a790e70aeb932e6d5caa11c5547df77b3b2980d4990084 3e4c1f46b1227f59e4b685fb928fd30dce63e1b61f89637e5fa5cd2e9eeb6c61 4697cc1e7cf24ff50b26d27d5c23da57538eb2c30a523a32988bca90ac318a87 96c23695da95f146ab9336c91f57c917aabe1304f960d4ce2030175f17adf9c6 51c275bc94dec997b1221174d66c523ec502a0fcf7c6452e58b2f54910062a3a 0c69deec72e4e88532586d5abfe7e658c676be4fc0594a2f9bf918d21c22c33e 85d10d747af1d17feb87ed60b155c57d9b57dea5713498ef4a013ad340353515 24293937f09101130ee98c904c4088f5c6a48aed0a43bb77bc6c94b159a08a38 81981baee3e41567e25b8d0a0b41534b9c98c1e68e3bd40939827c568e5f98d7 49de16c2f72cfc8958bb1da47cb88ae330a69753b9e1b9c070a97e2c9a04e8f3 1d2cecf603dbcf83a6cfd605d5e1a0833e9d182feaa76a137b6bc11d996efc36 80878b79287e37b3af3da528cb7a2664fa334ed65c8d1ffc33711beb52898f4b c752128e37f5ff5889d2f6a1befdf3a6017c5544aa8604e39221cdea892f652b d7504676968627a1528324356f4bceb6674aa16dea4acbf01955761abc70caa0 28b54d5422f33c6e9fe7a4f162787e3d46e272c6f79bb8df424d29b480030679 a92ee781c2ffc36a2cb26655201fcba0b6ca21778646d9600bfcc3257e711d9a 321d3d9d9a18bd5b748d22ff7afada597ad0a8bc612e2d62ca37cf46cf8d14d9 6ebdc4f0e13d63bf06274cc4d1a53177705b27d7c715439150851f215690320a 170617f12436bf7f4cf7956a0ffe1a5561c40f9e8f1cd838b59a8c64e3dc0e4c f117fcbc67cef9f1e65eb7eaf08b95bac60cea45775c6bf19db4a1fb2ef6c46c 62f2053f942e6f810287d4138c4e81cf67e2b0d4badfb429881b1230833532ee c09f4da6a2b6c25e7472af86e51b39331be7ba5d3c5227452520bcf840d741c8 22640aea35ccfcf11ba57f33aec1c8dd30f38465b1513bb1f2ef4ce3f2976935 ac0887f832ad98d215dfd8da4b8972da034e2897fbe52aaf2bb34b062f2a9e0f 449a5b3c5cb375869efc95f172fcb07425a3aad659bbfeef2bc47bc675b9f9ab 05e039fc61e5ac4ffd7827370721c50d6e5707167079d789e12af5616795695c 25ba79b62611525ec2f2844ac2a48bb0892ce04b2175512a80003f4e8e41a6f1 b35985e11f5f5cea8bf2ee8ea728d73b7ca3f44a7bc56305a73db838e11d40a4 709435661ed1149a4b80fa68fb824f57ca9b9c13aceff085ba46eb0720d49368 3e660dcdebb19efa822d0792a8cd4a8cacc19be6feb62ff88efd804eb7a0665c f76e4e01d5b363938b1eb0ca555b6e60a44f78e1bd4fb97331fff3d67ccd44d5 cef0b2f674ef0d77a1a441121029144f5df10781d2b98c45afd2c9c07bf721fb 39d7d4860b74f95d6c220599b8d7bf176f987974fa3a7d13f377e2ab7506b105 7b932b1048f7743aab3cc0d26305a79134dacc65687ea719fb3c0e8ab15754aa 1906a9627f827852cb8f8863ce860dd1536efdb9a0fab12e0d2dfec6b0c5d334 1c78d6ba6858bbcd713086551f3fae84a8d7b2fb647ad287328321ed4e8779f9 f2ff0230b4aa96ff3e74380fc45e92ce16710828ed539eb592713b32bd680bb5 213ece059c02115c9c3e239e83ed61f05b030c89221150327d5a52062a5a885b 4e295025ba900e98ed74a2447972e318d16b1d01d9249f5d001e8e8e0b38154b 66e2b651118f963dab0a65bb2f96a2d30b92afe229d59b08bfd6d54e62b2760b 6b2acc87822e02ff60bb7ee7ba40eefa32b425ec40140eceda9e0629d981ce05 ed570562700a22948687970c9a11b6e8ea6544108e3382226435cba711577429 5ddb1701e39c31b659b227589ba4371a9ccae4edd16bd26102759fd0131da2b1 99d634722b4ffe43708e2b1925e9eae0130437325e495a4dc0cf2e8b5a59a479 f57e54a0fcb4dcca6279eebfca407c15f4bc28e6a928ce2190c5891a7a902907 206010dde505735f89875cdfba085b8a970f388ad9cdaf0db9ad74184bcba852 bfe7ac39c0cf73e143f7fc41d937a5a3dbc3be7eec405a6000911dd2af6e260766f98e7b2ba63f49c630bce9c480820670a4b9b01edadcb919b1ed41f0b8e108b8629544884afb6bbe66810de14bfa95362ddd5950e4e7f7d1603d68d887e30a43b6869c74858830722924d7c3d6c7d474bb0ec3d9cb766deb432f1149306502ba26648b7fd42673dc724be017be6ef3803092fd62c279ea8c00aa846b3b490576c02775a0c1e825d6b4b6120cead2b286821036119952f57362cd4f4da2ec0d73363f850faa018d628d0ff3b79392cf9a8a05b88aa20dfe6793cda5239d3107d5964e572f7ac41219abc1ef79c3d2bc66071772181ad198a3ff049669d12b02ec884d5e8f40d0a567c37f4b2507e5f9cd6d6e7593f953c0266d9160fc3de20c8021749e9acd33aee56fa267cf68dbb047c5f4cda39a6ea96e54b8c1f61f6c0ea4376b4af6181360b2ab22d5150456d34b58e3c8d823b4fad3994e4a71410f0c4ba3fd48c48c6fbe800a3b3550aa3c8cb003cfb8d75736bece968165f2949e0ef2a699abfd7d9b153c39c314ac118181ebbcd750c81730221982072470d9f00adac727e3146ea4438c985e7aaf5b5f0871ced0376ab8faaebbdfa12cd61a56060f35352f5256266f0d367431b9b8c08ef9d0b08b44b2559d5f353fb825456dd80bc763fd3146b666a551d215e21879bca0186feb93b88e68e08718a71980dd000500396e38455d1c5c865b5f46661359c91d9f0e4569f700873c8e0750363b07c20ca50751ec2f318bbcafb181accab5c392b188e6025a569c76d107129c1808a57df84ca9ff7f08383be6ac0025cccb8cc0c7c6e923ee9d3492b7593a2025030d13267506082c8f120e2ddd983b5d4916e770b81bf658219e4fba707bebbc0ed31c04dcdcf6688c0148ffa49320d37300bfedb005b36410e974fc2784ceae08a2c9da58d73ba1af6d9ae2e30542ce09219ae4222ad2b061ceaec199d014500d0675500f10c9bdef6977aa077e7110a11b9571a752c6e46657cae6f81b33db0c8ac9195ebe90f963c619fd475eedbf951646116fc37f947aea496ebd7469e6028e20a4978a57e622fda40d0d6732fbc9f6f47c5d712140dd950e2f22227e030bd109cc3245bb16dd69e824bfb90052c1e1400e61b0fb23fe6f118212bdf99108068378eaabe3ae6153f6d470cf1dfb5a4141931fd4b209463aa9536f63252307b35b47a46aa679d09d9f6e83dda05cd06d596a0823a500682eec683279556d08a0e23e895e1c7ecc3b2ec2cb5a0f051c47d94526f1ed467b7ce6adbcf392440fc2e907104c374b5fae401509e3015fec851f27a29e9f0c5928219ab539e49105aeee7c1ec2eed7f8be569229309c17dd4f7a211e583442dfc665fb5263758c0f16353ae67f5d4b850fb37e672188ebb14ea1a06bc569277ec7219e6917011e0237fe8ad9ccf2c119230da14ed040dfef4176a3b63c3baacb3b5066f564dfc60c0ac370a4edce71928508edf23940919aa4d2a6a4481cea275851986cebd2f2000ae67f3e206d45b453f20e27e1d9357f6c263535b4034fb33aca8aa8021eb30ce29c26dba15cda8e9b254b25c930e66df9b4535a6fd7f359d3d2a4033ac77103f36747aa01641e0dd524df1e247ec56613e49593b7511313148eb92bc0ceaf0cd96a84485c441d911e4c94fc2dc3a4318e168a8b5a19afbaca142f4a2bb80c0600bfbf5f3ff3655baba58f7907b60eab2de139468762cd16db642c5add1be9084598ebad268dbd4ed63b82b00c516e08b56b05d7f5a6f85dabf114f3038c590a571aa1ac7ff07034d0bc6bd4602fa1b223341840fa365ac66712675fe4bfe80e5ff5f66f156ba4c0686e55f357cb8d8e0e65f2e03f0ab8a9e235e2f340aea80a52318c6b72fe61cf4028d0dfebf8f226e84f0db359c051f6a20eae04c5b00e05f29417881d84d0de2faf3d1ee15cd7b443baf251ae16fb0bf62354269664fe01ef451362b55ace2bad2c13fef3c9f3392222370b6025df8987fef4bafff12404eb95d99391fc318224055f0fdd91a1aa0efa418bcdb211cf9c8a6efb8a47620b4fd7c3df8fa4676c8476dfa589721b6e7412f74f60c8031739b806af367172047e219988a302904c67f8cd8a55f974221b1f9cdbf0c95996c8e3f51b07700f0b9928ee01ccde7e74c20f165bb71ecd005c1863fded7ba194c1cef67228bcc9063dda21cf331355a650ca2d98ea8ae6412c9e86aeb7b1001fd991330be57b8809b5fdc44627cefceb76d3bc485f8f4956488b4a0d7a858585f9c19063f186050e09b96521c6beb77635656a38b1f6b7c9b5b26a245bc6f3e6fbdadba123fb970b46f18b30ca1a91d86923d97e1ceb89bfadee8f0be392ae7e325bb70da902250f47dc3ea4dfe974bb208d58932ab3f53cafadc5afa2e16af92db85fc9b14aef0f5eda280e28c122573647c6673cbdcb84e1eb624f23b01dc57705e11f2e3d78091e5343b13d1544f5982ba72c76c76322d0fad5b91f13c76b893dfa6fe4dfa106d424e47fa2770f79f0181b22b160bbd7998f72766052b30525993a3d62e9cb094a4b31e6a23e7848ca2d2bf6964454736bd2d0e1e9ee842817017720b6ef08031e2bb4584688813ec4c1d729959bfa1f8a81d6197775f7de8309188dd6e8bb081b9a1151c18fd95094b6ac9330c44be3e76f14316bf1c867d0a9b528ac29af0e1a251f79b91fa8badce7f4ad0493367d2e0b26450905fa7445b2a5ab411adc023179ac3fa1bef367087518f015df95e526481e603da6849e24a30aded3d3ca04a98f1329f9ad030a8e840ffff7f845449f2fa73b6095dd01c0d3c4765500ea078f22152a8df28ae12b2a12468c199c135c9480709736348e7f0be85139d6b90e37e93091f0c2a2f643035d426e90137d4ac4bcf798242031b2431064c6cb9009dc528697bfe1b33cf5ac084254ce2bc59790cd23e60ca6ad8fd2215904d4b60861b622868fa62ceddb5628f9a30a261aa40c857b91c6efaf27e4b959ba4c1401841ce237316ba5a716fd9e2814f6194da516e8b946b6c178709d3b9c2c983f0a151517bd02fb85e6958024dd1382a1606999df35cec763f2c8c0ca7890b1430e0c2299a90c288741b49690b344ae2d6e9553ecc8c6486834ae091f811ff992027be57673980fe8ddd32eb98d7588e1228b5e9a8316b042137df3f3eceda0c908ac2a6f9596f1022bb8e499f5a1ff73877f8bae320959b130203a22cf144a050d581735a18091b72e640ee02349f2d29ac7ffeff8c444882e71752615172d6005bb33372e2656771dcbfe5519d1433329a4704d45251751c37c7939e7d5ce6508af48a2964cf5157e74d0364f57ca88be6fd2f811bd3a3b5edb9ea9e50a1e8404bf457c671d172cce60a3033321bb109dec668c26dec7842633077a08bc151a089f70c1c7e841dbcb8a9b7054d189b8050bf5902df26d788a77e14b9df8999c09b1fdfb124ca7cf1e4817d1fe628df13d94166d52630a1fedfabd2bd245da59066761ed3e1727ccf653317ecb418e9f26d1ff10be9d28f450688d87433785f80834fe7ec19a1c611eb3cffa80fb4a1f11a0f65a617a019162d8a27ff2cf7c0c07325c3491fdfc5c6f52f2c6249e97444ffff8698103872cef61c9265b49e0530ca78b78b5cf0b188c7b88d5bd83b26d9161e7441d2012fa9611b754aa33688295c45716d0bead2041f07228318cb46ea166efc4a9807c12620a70e02851dc4c06c22bea6af53796ffbf44c24ffebe7a1960b7e7d44a81936de95e2a9feafc5701ac8a1b8a1e67eb1e7cfab52ed56730ee17624608fafdec2fd0529e010d03530ee0f053cfc7da092744d1016358005394c4ce025e6cd3c920019d94247f0c4a0bc03f22ff55d84a9220df2304f8d9e7fc44bd9c8d50496e9ba6bcd7bdda28c703b1995bd6b7282b9b23bcd5f5812e7df40c4d85c559475c19b8a885c4d96f3608e873e09060d1209237960176ed9b8f2a3e46f60610c1cbc900d59c6bfd0ce00c898a91f1cff99827d85245fd71a95e71274842f1e2b0a118e32a0fb29267c705e8bb7606910801cb5124fbb5947fe3e94d5d745fa1ee7421b359b64e6f5446082c656d7dc5655c437d2241a9f103d873cd84dadf53501d8c5113634bb78f2a0cdcb6f24edcb40a048d7f9f1c3bda861be13a528be7e96f80b189548b58bab509ccd5ded8890b3347f3841ee32dddea69e0b05d07f95d3639f8ce123cb7d524039b1d2224905218ccde4980df3ad3c88299c428d3c886ce7df2e7afff8b84e40895339d694c92f88ee711d88902d9a85fefa5fc45f2ca490a137cfca950c3430f50febd8feb2f66e29114f418ab28e3fdba03458312efb116f9b934b3ad0eb801af6f371e97c3d1e7173d012aaf18de318a42d4e283abfb09476df5fc143788070540fad3e58a06ca58058dc59cff9ae1307efe410ccdbdee22e79863aa9161090cb5eb8c3e207e6adb75c03edfefaeb56a73f0f34ff5a7398bb2cf892d519306592d2463a5ac66ba5ea9eb8349e7b865accb637656fe5d0b018f7bf3f5f04100ea0ba2fb3ad2e52f6665e1fabf8ff7397af1640ae1590da8af053ade2cc3aa06 false +check_ring_signature 11a5d9fdf0b16f40cb43f8fb9ba1cb17fb5eaca8e8031401a4fd5ec6d1739859 0fe802bdccc7d23e35046e2a0f598903cdb4e532e8bd9a6c7d92e40a03bf84af 3 fa5f368c448bf7b009058740e97005b2999ea47bd7de4b1a0e61f1a59eea10ae 3b5b8de1c4b4669fbd14e51c61ce198efc27d2a7431983dfb448ab5578d13f3e 8773ec9bf50ad9f47c841fb695fb5589f49be948b04425ad70005ac3d9fb6017 5a2808664d1342425948d629ebaf02eca10bdffb8f252226931451e669f059069e8df0b40acbd7c347406f20274dc46a2c6e0a9c5900a578f2db598dd516eb04ac4ee49e332a3c6781604dda0acaed538ec8b16556f25365a17837d507cf9503b2d4ecf0b472fc7f4dc2862edeedd5b6d4f35224ac8f3f4201045e69252fc30fcd159f4153669c638ff641fb028ac9229a0876fec8e9238db18269738a4da2076bde6f9cae721dd034c84fbc25a5b71a440289a0d11ff0911e64d07404c8bb0d true +check_ring_signature 53d5059aebd6cfaf59d9f869a811465ab83b7678a16f896c457363848f473adc 11d31db7ef266d541a84f89145a4cff56b3a85516cb8eae26c84fc4ff40635d5 14 aed9a0585463bbd250688eeebfff9aeca0d8ddd2dd1ab5f2950497bdc7169bf4 93f127f6a0af3e8edd8e6d9811f72de8f892e1116aff660c02380e8d8299df2d b5258663603f7fbed8923e0c685ad57fd0eacc346ab2ae7e03c8d0f285e63fdc 12e37b5645a441a15f0bbcfd2ff2b7672d59c0f27ea91b57c12edc818eb2e6ee 6cf50e4ef599928a5f792d8d05bbc84f9577e3076f9a71b9a07859fa05cd37ec ea6c79811d2e07413a4014cf3a0d951ff0b9f8b9859ec68ce98ccab0ca9e7d76 7c665c28528b4bdabd16c6f111d1a6047e993e5a34e5d4f688b9d1763d5e4d80 83ae7f0370e0b996b7d078b033a9ceedbbdab0ed73a54d88096cfd18ba04aa59 f76f28586fe8948d5cc14e18ce90026f9aacbf9f39a64b6e1490eadf9abffc56 465eb2fd6ae815cc3c4225312232ab5d3e67f9eb7e6c4bf1e279931fe89ffe6a b2c3c5b67eb2a5c1e9116cad7c37015b5794614e3942a19d2d5c06cde4f74433 72d7738026436d9260aea40042d1b068a58d21dc55a34d05baf85da5280ed082 1f36fa87738cf3a8048a50137304e5743b8259384e00360aa608edfd73473cbf a7f55fe05c81fc9087c73277967bc265c38f8a5712dc35c6f9c1e0d4d6d4117a 8ccbeb9a25e143790d58e4457e479790fc1952cf2e5536bb3618c7e8b1e2080397c87c3b4a3713155b37cc07da7368980a6d909210f376668800ecdbad927304db071861877edd41b2e7e2ccd092f6ea79c723c992ddeda958036f74337b660a7fc85751370872cfcac8d126ded481ca47b56c9ebf1f0cd6cbce2f9f0adbe8026f82230fa8d5a990d0170af98db735d87bee8a4d4958301833a33ddd5ec33f0c969640ea4d9ee771ff5a1332e1e32552069c066a0179c9602f1971b3529577046c7e73dc813b42918f57931ce42e4f5d6e3907c66cd8dd776bda657a53a293010a3ae432ac63b85ccd1e23c054508ec641672a43fade0ec8dfda4dbbb6a1b807689201ad25fede73b0db77f79033e75cec3322f62366b4807ba2db092f9db90316d143704d098dcb06347417edc96a4b4538758d44d506c1d20f580b02da2c00676018dc926481f5f58050f0700dbf42513975465cf80ba80190dc6ed7ec540a2f17faa5b43cbce041422c04573b0843578f75c1b3d60e9d0757f71e509dcb0c75ebc7f5f5b6b9b68500a319949fbba222f7be068c81ef40daf2550412a33b0fe8775d9a4f3caf3c203c40ca24ca94682435a0242c30ab6c65fd5184e1c5680b126032211837e9df4d2a903332af841c118edfe9b11afa6469ffe7ff26c7fd0c7d0fa35a694712f3c063e5a575659e61d4b02f9995980c1bc336f6f9368de007b3fe76f11327a11eb30959c6d089848465662eac213e541f921f9614661ebe00120f88e1f00c376b6134982966cc57c9e6cbc4208acf82fe454826de44a73f0fed1fb37d0323eb4b0e11a9a38fc9cc3c43579770bc8444e19f040feb84dcbb019139629b1b48457540bef4cb16c8a1c3b8027068d30fdf55a640a0100af0340f16c43990daafd65aa91ba19d321cc91c6e558476c0f19021964ade4279bb14073fe87c10f2189f6b86c84166ac8c23f2fbf6e2c65344cb9d969b0ccb4d50340b893a72f96d4166840a20c0b2b3088f3408185ed77f275fc8023580592a5cdc074404f1e5c4ab8d2cbd12fda11acb284127d6cc59a8fe5c2279d6a59dc71d14052c4320a585fe21eaff770be302c172244a7c9441ac7036a22f5675d42839530c65eb846d3c77b003eb5daaa87d405b1c3d31330c6e949ed3001aa60ddb148c00263295ae1cbced284b4d40d46dd3e03c65e518bbc76c1edd2cfdba0469a88e083f77cb6d51182006ffaea271c8c2ded00dfd860c508fbcb63ae57ac0dababf00 false +check_ring_signature 502d5a293c5a9c94249fcddfac7b35b84e6ea7e020f2fae217283b76150fbf71 7b2b96d5be890332f4f0343240e3972a992489bd4b0c9f676f01f2fa1ca6092c 1 5919a478af6249754dccbde1a9fbefcac0b7fc6465b804d2a5accbc6b00b23ca f0096aa5b9ff1fb608ac66f64082a1da40958c695d1085547eecc6620e8f5bb40232900d5b421fa307ce94b3bff2e5fca1faa06ad681b6f2bfc550eab7879c05 false +check_ring_signature 0a43fec32658047776f8cdd64f3d89745c937337311bedfa54632fbe07baf243 ecf3383c4332a4af99703a6881ded434e37e135af27e34364e3d757e21813aa5 80 d7a00cc876cc66e6b7a96ae420a06eec1599d3da4765e97294e9e14fe37d6492 0090dd709882b78e42d8b42d3bf80b89b5b6be2d1f1cecfcd545c6ffad185d70 cd1693262a620fa5e93fc3d9c5751fd395976f2122a02226097fcc40755b01fc 17f2319a7acce0fff1b9d2f69ed07e6d8814981414629688d38543f06714af30 cbfc8c4758268b94bdd77331dd175669239185494e9295fe47facda35c7b9b03 246f93763ee81da367f5406de56aaa6ecf6480980c93676714d4fb8f116d9d0e 310cc0df62a1b1234e7380e4fb17c101e28b19e5551c726ca5d5acb98b4782ec 15def59c397465ae110afeabea584aabae98c33b0dbb1c750386cda34aa0caa5 969978c1c5585999f6ad97aad8b93105cbedc10fe7e93963d4568cb843cf1a60 663db22b98e7db825d292f54416f3033f8df8b5069f40422ee55ebfb4b026c6c 514fca2d76833b0b3b002c8409d07a80fa5994c56d83adbf4bf98e00c8e2afef ae542e25364d08465fef5081c749ef780c40596c0795a4002ef81f1e8bc4a075 a584343eb1a451e18fccd64d6584cee2da6630916fdda76c7d37d3f72af6ac3d c9dc459a6ffc4ea6cb583da39f5c76623beae467b4247a9c1c859d33f7fcd1b5 bc443172587313960f09f3df4d3f5ad4d02d85e3ce1b6bf694a274b8ddd71602 58fb4db16320ce5e14f9995b72df28388d6accaefa72b17ad7ba415b7cbc708f 10c161694e92e66db183229eebc7d3dfe7a67a0c8d94098bc0e7d096f1368135 d68c02cab3347c695bbd5865e3d2815381d8302a3db2a1422d5f9f12951d4f90 5f8e96e208af23c5eb09d2d8c32c4899106e00456f2e0e04bf58699cfb3accee 626b4a24b32533b631228825c4f0f9f458a5ebe9bd6491825077e4704d907f7f 86d686887dfc7f8d7c66d24abd504480c5f325cbb92587fc79748974b7f53e3c b5027084982658a271687a4fe3bc4b5657055e5ea73fc312639d831e4362ac1e 6e14a2aa1ad05d9c5aab73a7d891f183be8e7d79b0281aea264ad5d9f4f059c6 182b24f2b56d80867d29fcec4517950136903b64c627da12bbc9030e0015ae20 d3ef36438ad8e88516de8e38edad3a2ffaacdef2c7d85c01036d371500174a37 9d2b8c0ae7febcb09bf544d4a793035894d1e0a4c8967af8516bf42da2bd9ec0 83d2538fc6b54035adacfbc5b20b2884e4dd9804eb4639a3fbfa15c5b3ea4fed 776e743054189785f503d48c288884057e3814acb290e23ff94b859d2295720d ae02522cee573243a1d7bcacde736d88f1ca12e4c3003c0b1cefeabfb683b42b 4537e70520b2cdf9267db02a24f514cea13a4bb4b3c9665d6bf3570f743e0d5c 3345094b431226d501fad380072b35ad08dd5dd5e94d32ab488071a86423d61d b931cdfc927118846435b5dcdefe16c5cc9703a224ed5eb00b4be04bb8596b08 b269c5a387cad3fa8b02062acc144315344166f2833e72fc0e35e1e7189e779c 390c100d997c6fbeef243128664a15fd8d115c2340959a24e91d7ce09952cf01 92331332dff15f72b6c6c758d286780aab3b77d4f82e105145eb3fc5f9386bfa 762d939e405e49650069beae37b44b8ce2f2b842f7760c425051f93058038224 44771164549c90856533de8ea7c401d173105554d411435d57a78bc10518d562 64249652c0833a39fc141e38b9651d72e13a533fa7ceca52a2c34e192ec23df6 381962f7098086edcfad98cd2499ac95e07cf43e6b0a1128f66fc476add3e7fb b91aa3a9c39931553f7f1f480b1a6fef533017d88a6d640f1eaf07454b9294c7 be589ff6cd065944e4c557322dec5b17342c5497b940599c2a34362ff683b0d9 018d4495b5abbb2eb29155593f166ef556fb18d8fb67b7ca01b57118ca6cd42f 6d3fb042af8f7a413fb0c5e2ce4f78922ce853483028a5658a6f42928de9788b 5d7489eab45b6a44303e9382e721a8a7202a820fe864201677a0501c441fb422 fdc2581e5ec83e1770189c5869b566661eb6fcc99818368c02c3fdb0bc8d2d06 0abe8ab9e102b373284d5dfca4fbdd7a1a4b42b2c574192a8b06f61567b75625 6095bc85823c96cb9f5aa6ef1d9e8727f3db339d23415bb7ab9d8ad5a039a734 6a24852a7d075bdcf973dbd678a8f42e80f753ac3e5fd8da6f560a185b3f3871 d27a9fc00810484621349796d0cfcd7a66229817358349ce6ba4d3caae222220 2e91c3147026ea4a32e451b311233067c67d3fa167f7a85d2988b8ea874ec6b3 5c3c251159ff4f8e82511f084fdc337225a5222165265553736280d51ce5e40f 2431a0dc7b085377cf97ec4bced0837274e5a525bd1bf82f86962e67375d711d 3af31776936ea891397d6ae73bb61e4ef3c1155e11268a4246a7c3a920791547 fb5606f5e6fe0756543a47e7e1ea7b7d7c27e996c46fde3d8b4fff573f9c1af1 fc68eca6c4bbcc9e2aa132a6e6e13cdf58baead41aaf1bd53eb76d8698a4ebb9 4fb33ee133b271f9f6f74daa54e3d0a02331f5332f67fb737753ec883f17fcfe a57a9dc5cf3135d7a7d5f65ad341b57fc0142f1e4415655755bb688a8b381edf 4c5a8ba9156610db44023515a881b02fceae72d359f546e4abf8a8de10e302ac f997ee5318edf733910a253ba479357ba6d7c199004710d49dda902b7c845a97 cd1e5eccc329e5dd64e87d63877d2b61e794e23d132ba6aa114e222ada492e46 85fdc1001a5f7e386304f84dd63eea0fe8a16c33bc71b15dfe9723631b19970c e398ef6c864532e1e9f3b157397b4399853b89f72bf7bc1002b49fd9b8585be4 96fcd361393b0e853611820b70d69d661cf6d70ddf1402f5963e9c0921f5615b 849f3bc5ba8f3df452c4b477e1dee5e1dd8790fceb31f0b78a4da19c3a719004 1df6751503e1a6e4d76487b1f5420bad393cb6136a467bbd77c30c4fa9168fd6 c0585a7e1b382ddaaf6717c9e42075dc5db8b7b320a04bac8b1ea129a9f00ccb 6d9183ed7b733d606eaea547e8502abe5c5d1d5b73d50d3aaf30b2fc004a001f 84c0eb7755c076273a0840ce79f0687382afbabdd37b8c3dc014cf354d2aa8b5 6b05f0abf010bd1594c7bb005b31df04967c9fabb4a4f64258865539bd6af70b 41a0001f8d132d212dd7e62ff0790d12bcdd636836d644b25dd6121a46bb6675 8f6057b662e0e38f6cc50aa69ba917a1f60438b1031f487865e03ee45e667387 20aa7d25e55d240ea5c7bd6a82d614ed48d38f7f4be238cd8fc01e4a646071ea 0d6d25cd8d0a3e14935a6e8f910a0ed0b96842956eec2245b7cb6de7465ee062 c89ff2c0c531bdabdf7f8f9c84b4178f85368fa556c9b14b6fe277f1907cede7 94ffd1b917a39dffd6a14d4cece46d7b25c81bf9cab723c82ee81ddb64d673d6 8f4fe829a85adb66a8b52a1b0f05f0863b75e00dc5a49d65a1e9c21d2c0f03a9 39d95d927582683552891fed19e065bd896e22c7ece1f1427cec0b471f0bd4ff f7d02c076544aa25c62799eb2b6a4379dcba7b6b046629c7addd33977635e18d f88da6fcd336a973576c3d8b675f186b99bdd8eca6a774dbf5ad3e9555ada278 58f597a57a9bf8fd9c09c9adce213356e162c292e2f8b72ca46033e6366f3c6e 2a642ced2b4ccc0f568ee467ae31953b224a8160dd99ab6f7d49c3bbb32c450d739aa39d42ff5ce9278639b14d61be866f259eb428706f7df2afb47d12eff20694b72a9c3f2ddf6d060f5096cb21eff76f8151ae4e817a10c3467750517da00ae6f50e40fcb7cdf90f8c4a7216289af700a142c68c7429cc5a6ea1119e90ad05ac81dddec19a6a3db2570d379ac0abc94935530cc6d4311a60423db92293ec0418903f1e28f2b789e9d430e32204bea521eee8abf3314a2cae05c1b7e19cd10e1c434b345c03c6d2ca199f083977bd4feb8849f597673e814263f98f3334a30a363af985c878c2cebaaa19577b67948816a4bad4b0b0586af0e01bca58bf7b0b813d0a624d4f9034c40e52b7f5215927e6e0cbaf90154b32684ce910b551c304a3b003882c328f0fbebd445d2be3e47f2102729af4277cf55932476a1298520f0b8b2aaa2fdd48ad6869ded7609d937f883332cf396d10d2f26ca9395016cc00fffd6e249cb7c3de8cac4d3b9fd07aa92d207b924aee9ea436959920a6ad19047f376bfa19d88f73e14f3bbaf7820fc30ea41bbe5e52cd3c7da5ad3dbdab0301b499364283b6e48bc9fe1be621fb8c683159cfe4cd5b1d16a7cf808092b6010ff21a5449f87784c8625d52a7d497e893dfbb99e4f2de53b860fdd8e36f5b3e0558311786f223473d122f63e4f294f93abd4f36f9e5ab52367948cf9f1f88740a550008a503ef4a02f201099818b25c7d25c6d03c3d461c0cdd96e50b6265ce0255c940da846f7b2ec62544f3b6f29cf56d711322d6d1bcffee857dacd8417b0ef131a6ebc6e49eed24a9c3dc950378a8e9a77d86d7c6034813fb2791f4a178034e32f4bbc80c67e027b0282854d0d4fd1ef6843414980e6b55d7ca77ceed520965fe03762288f2a92b9c37f20cc8e3d3fcb70f948e6a1aa96383eee61347870c4450a33036117c9147a3ac294ade61fdf15326717928514ea33a0fca207d9f09a6dbf1b18d94c4e294cff37b869ed6a7b8f12673ec8a97b04a3522e55c94c40148c7e5d301328cd2acf1205883fb723c370b256bce01df3894c24f3773feb5006eb40c9657a8a434977b27a68aa7dd2de329a2f477a1e4acb1418d4f68d7dc0873c30ad77815fe51c7f725a3a9f378bd62cbcc126d69f145038bb77e0aca8504ac6e0fec22f6841a802a265efed22675f7b41724c727583df24f4ad76c919e086462cba1e7a8bf61b84da2e28dc7e33993d3ceb43c5d19de966d6300281be20fe6363557f06aedf01be2567d1b26433549aff717f2feb578ed2954b7c5f0b205ce8f6bf4e73ec50a60b441fa705256aaa6a6b1b3bcb3a5e638e19eaa3aac950667529dc2b2be1decdeba75016a347c0fe623dc3ccdda9b7236890e86cc159c00fe98f49d8a16e599ef199e1b7fc4dab3adee418df334ad28836cfe53e9975105d235e754447bf3332824b2b67f4571fa4f86908d84e4a1b55b4e94f1792a9305927b9aca67610703257b621f450a74ab85d45f0813edf763cf9def0ff68dfa0552d148c7e4c11567c5194bcba01e9fe56e84a1708931fb65f5fe5037aa32fe01462d321cce73ab0e1381b033a166710094fb6ca712d4a62d5985de3361dae10b7803957ea98c7db9fdf93697a5644be652e153ac0d651eb0c6fec9acfa5bbd001e0094b6ac7f24c58cbd18387943b303be72c4e1bb783dd5445aa81cf352f90fbccfefd1bff86754042ecc9e2c9f0d480b04a2f037434a6fc4ce0a22ec0f620ea0827e0867685d33418815d82f097554c6868659e0ca1be789c6c6d5083d900cd91a3750344de5ed0a5f470de0c360bee6d39bd9d093347f27d6e6d81992ef0908a6b4264fc862bcdf8353894764cc066cb31ebcd124af8c51111fb24c37b90d210522749a6290df74c65c2f091770f2fb5b10cc2ed527f0a08a8a7e4e604109f9d53366f73fe70f0d2544ed858d62fc56effdb30fa06dfd7a18751b3a71d40282beeb123ede9709b6a8e3f17aa295afd067e8dedbcbdb690187e4addf9f60078de8e5b08a3e367fd79b33c878f0dd7fe168e06411aaa6604ba3d0364640700eae72cffc59ae0ea0ba5c2e12c8f19b6b5a5864ad8d4992eaedfbdc35bd0f8a026b5eb664d7c1cb6bea959f1c709faa4f010d3917277060168964dfbeb9c74e0a065d986af83694bffd40d1835acf469ebbbee4841b0af2dd397651b55e207b0cba3168103b6b97e9134981e83f2048af31752f96d35d630dd2614bbe24c5830660f0e1a194b52c18273bb818f3dd8eac888bb33f9871e88c89335aa16b8eb30377a9da644d50229d09f2e364209a4aca7fd5ca67a311dca813c92fce2d65700445ad6a9ada935197d162641bfa1c1da0ea88f4a7e4763de0dda23cc7d43ed605a67ef12b4096693dc3a6530d0c781f9c63a5317c566c440530c60a2c758fbb0cba5eeccaf7aaccb676baf132be4328eeaeb58095ce5ff4dd9139a0796d79560cf01edd7d9f1ee9fad972ffdc49baf311418cd652dcb8ad95282acb441cd4d60886444d4875af180e9b28c8e21743298c496aa6769670db43984912a7c811ba002326f27f1065781642d5dbcb79c53a07b9351d4b1af1b241f06749b766075900d79f8652b3100f31bbd49e2dade60bce4d7ecfed758ab581edd13546403a3e036177e2905183c45ae97ae9df5292a7d2aedb92cdc5f86bf1152c553282d53006caf7d1073c1ddae8238d21b3e47c9289716b1a1f7c38ecf4965cce9a38cee7007fa8c360aad72f404dfccc69bb66b2d161db9e080c37328a33363f9209bfa50f9dc480f8ce1e2a395417483ca73be2f08a90ea4d75e88b0d6d0cb3496d67c504387658d6b8e440e44498f37014c57c5ba5e48d1a66bb75f23af7e5013e7a3e0a5d2b90257eb7c8287c98ed847a6878858f433b5660098720a595116692a61b014cd3c5e5b3c1babd433480abf88445dc4b453a7b426f90a67422f2e41c42650c06af3cc156020938fc0db5e619d80880c047d640939325cd01aa5ea10b6927013c38a213c1fd5ab0a4c5b5cf908c994c48303d0554493d2dbe85c60a12223e08d3b721b3247d8b347bcaf7b2bb0e373dd0b499f2780bfa8a606400d222b3b70dd72584084536e7b60989eba1d17c46da0784d50e0a4514ac149982952caf180500f7092f2bb201e3f0aeb85d1fddca16b5fc747d612c5915285c09e786e3250d5b91461aebddfabd5b62b8a917421abb96a83d3269527cbd94ab765ef603ae03c3fe897338aa5755c9902ff6619141184891876a04b0f1c338dfdee4a81d6f0a66f5a3dc58c96ad038aca9b5986dd735b3e73360bb6df53b09d72c8323bb660fe8390fd9cd487db1e2d58ff349a3bd8efb2fd50a66be4083dfb755197cc36f0ba056f6855ca4ee68b83529df1080def832d1f89fdbe88596f81a90b6d820c70d9b25c0bdc76c666d84cae55912c2901f953c970b6c6aa4bc862ff8db347a6804387eaa2d60f0822be9f9b5bf1f7f41f79f3a9d34b7a251721a640ce173ea800f293c26f9aebe09c26fbf1ad19261805e8795b470242b532032cd67dd7e6aea0d0415ddf01938ddac4e3e88eac62c9e155b507721c073a74e010b8eba6834790569a921aa8899a76b2e467108e630da1cc4d2969131f8f7df23fca07e98b80f0a501a158c4731c0ae387a8cb4206ff32a0a3bdf07ecda3ceec37cc43ee86062092f04ee9f30643498547e8a866d64b487c7beb10813690a4807b0f374031061065feb1d5b841d006e8a6384492a85d54f6c20774c9ecf87f6edb2c4b28c4abd00b4a5011f1dd950a518ed3fff9ec906d56a37ef672335ad34e00941cc388f0c0e40769ed66dbb07fb5eddf17f271e3a804455da9731803a8d592a6aea963c2b0e567babe57eb9022a057d864312f4186c0be074eb7585d6363a42abdd13e6910d637ff3a21319563355c354ecb7dbdff33ff93feb1d41a5206236ff32290d3e0b96a0275e3c09391d590cde2f7b634ab2e723563fca1b265da0bb8f74e46f4e079c775fafc736fff7869d9d2b1c29e79a9ccc5c23fa59c98b4f97689213c339044fbe199c1ddeeae0cd2171f35ed4f44a9c2ee89d4875246182b6cb35c4e6c50623da05608552b6cc8269b7134e39e0548e99eac66adcde6b5804220dc753a80282588ca01059799535ec0e73bfab553fec5d9296908fc9fb0c74039b8ae0040f13f8bcaad7c0fae449d92752bdcaad4c7389176b1f1cd4da1e20347eee48e8015753fab9db27b7a72a451e6b640489ed6fb6a860fbec614d32fb6d018cc4630cdac49dfd9fff3729f479d2fb585f5434a6c50c7b95da78b3a54dcb8bcbee6404007d6829de71f3061b64425ddd1ee4c0f5073c43ff7b0363b6e6ef03c88e02092b1ca0c88b77f471b4d0f990d9daf93119b657184814fff24e35822dd52fdc057277ade681cbcdedaa925f4a2f380834663aa60f620502d05a838d65512dd70d130a72bb9973a5c996c3d4d3a9d99827c9fbb7fdd74cb628e6e2237eb3d33d05cdc8d527a150d06e8d27970a8b66dd496402d838c53e584d13ecbeda137a1f021e11e207c9dc85076249c6bc76541fd44990116eb8be3a521b2df9b720488a08cf6643814eda8caf3d239eed04b96197a7715507bea0bd987d3881b3142ac008a1f749b2ed88cdb3d897a2be9ac4102a700a68a113ae328d632529624910b20a0b1113b573dc64ccf33ae8937f5b63d09d960783cebe4a1f1e68f177da499706097e460ade62b420d69976b79b8f275bd4c128a255d5d827b5b85819433ab60f83de12e02aa6523ba800acf7ae8e72e571aa827de3fbab690a561bcc0b3df0005ab83b2f97233f732a0a23f0c7ce1546b813e087e1858985008c233b7635d40cf0696fd06ec04d59ac17deca6062fd8a97267cd2fd5ddc9c89baf3e915c2fe06157d396c4ec743d9994f81d1fe3c0da8071cac7b5b0dd9d7ab9b16727f826c01f4050ce87b0e190dd05995dd79772a2872cd7ee7918226cf1a1785d7cba75202f4110dc4c530294b08275f4ccd7659be724ee28651c604df9f49f5dc1885f5069ab5cdd1e53397be73ec8d685086dfec5842cece8bfd08437c54664e9f30fd00151e5e68c826739e9c8dfd7d6bdf7b0fc11c6b63e41380ed9d55151055eb22044c8dffc3b8ecaca75eebc88f07aacdceba7b0a3c4fd596f8078991a38fea710153ba4fc4e71d85934c265e4fb1d1271243fa8a8b1e368000f15b1ab351a20e0822c1cd8aa0369682c4311bcb6399c1a92d27688ff05515d02c65ab9b8a74ee0ef85415a713bcdefad3a65511efdfc4b84513c95cc02bd9359666f2c0eb5f930d21f7feca14443014026fb15410c158625e7686434948f2ee881bbe68449f0207f99b3e4b018533fe6ea3c65dd70f99c3b896b4736404bcd254a103c351306a00f9f50677e59ec8e9f6db0100571c3f137e82063c19059c682fa0f87e9ff033085ad20b4d8c8dff8a1353b10e9fa21fc76e95f37726b01c3af7db32a800d5eb0080589d9703658754947d74a183d4f4d5ba46f26c83eb17daf0591ce9c04e710f48fba2c7e63534f83d29853d13ed967df6089b5cf67d98d19acf76d1d44288041112023b17b4cf57b0a4bc9099d56628bb9b955e9c6d09143c5639e505a2af07b8a91bdd5536f60550492dcbb228c89d724eec73c2adcd122385b4d937f64e03e1bc087c0beaa712a60bd8459574f7ece6724b53b7e485a69ab1924aa9c36900b792ae4e7c8a62728c08d00adf825c7066f0b8b94f482784109c5ba81b01eb0a0aa98c627674d13de9c771b359b22b7d98f4a1fb97424cbb0bb387deac6b7f09060f5b852be2d0fc054fc8cfe4e1096a2b6a947499339a565492fdbdda752709960cc61a9790b9bce3075e3c66ff4f8d89a35d1a088fbd9e29b1fc481e123f0b4b7e3963d31f8908ba823f6e0ab99ef2660c71d254be206cc812e2c6bde34f028cbbc16a8c86d312eaff3b4f2e0b30e990eed52f7daa385d062913331f68c802e2428bf2be54966f13ae20d3437a0fc5c74c9f18ee5ec469bbe62ca0369ef80a8f04d1eea86a3ff7f7a86268b76fcb52d37c8e88722b1294b1c8cce779ae150ce734218f27dcf2537fa4e11ed004a599c310f58fc960fab2d1a7e0fcf0c92e0cac29b6c6950d9a702c9ca38edfed57b1a8c4463ad8576da949bc64e74c968a0928c57451a9e5bae9922e003b6a2ba88533c04d1acadd9486197ef2dc02021106bdc7f70e0c1ff9b7b04b51dbbd2a4307153f959abbe8958d24e119411d82d4044125f019b157c90667e10b67ab68fb05667af3a5dcd7cbc40dd8380df77cef0752b60207aa8fd30a52634610d74b3773bdaa764b2fc19acc696a810497bb1e03de127f006fecb2970d3a29da886655d1421a21ecef282de96d047f04adf5cf0224a6a8c46a1319daadf18e23017c4895368e6f55929680c5f8cefd4ca63e8905d457c7b96aa88bcf692638f64d92b13d6eb38362975431bd16c2e64eac30800095535b6c3a062acc514491951ab3add2305ee4dc5c1734f43bd4163b5a5ce6058e787cd8f9e367d964ecbfe30cc2ff6ce43a969920134dfa877efdd62651f50b2315b5579eba2f224ed695ad2bb5d0005c0608ef859d63826cabbcb8d8f3c60438a81cc0e271e2117dc04bcf14c85c9ad15653506e1d5f2b50a3f17b8cbcc4072b86418173f853beb47545e47d4c4b7909bba0d16649160a1de1bbca55fcfd0ae1c5f4228f8a153dc8f3b26449f134e41c636f7a87dc2cfa2db797235c391d05eb6130a7fb2f53cca20c6b0173276e68b7d9f72e385286d372d25c6185b7720c8cdd2b310f20cf0af695075a506c68745d9bcb08a18dfcef89ea956116a9fb03e6858b471ec1ac331421a86e933db0d5d8f10d12426beb56edddd95484c62a0d2730677f1770fc4d1e54481d44f4d9cf86607087414fd4a469703aeb2d20330a2263bd17eba263819f23acd8e01a37c983d153d65f8b18a530c63a42820050094481ae41df5715fcf5b03612ad2cfe27f133bcb3b5b4d1d3099f034f9321c30ec7fd3713544ee18a806b446fb875d9c9b9a0a4203fd94d6f4a35facfe659d000be86b37ca8dbd1d9c724c083d94fb2fedb668e33498e99cabd0cc1791d7cfe03ca35f8f791153a4bd4ceb59ee2f7abb927f0f5035a6f0cb2c961b94eac9bcf02386dd7f771aa42df0ab669cacb768bd0e14c47842dd17f8b69c6427f3a9c6908 true +check_ring_signature 1eaac499982684e0ed9aaf2cc7c421a70bc5c52664e531dd04b1b9fcdd6428fb c2ecccfb5051fb16167b07f88fda7d52192da15d4d8acf850270bc755dbb65d9 24 517de9cc398ca72554a72e97d0666fa42cbd59ad471abcb232a96ad6836d31df 0120fda952a721a27eb4130f5fdee7b5a9038a3d4b4281e32cfa1ccc42fa21ff 1e10918ee5046d67ec7152c1d3c5b67b01bde17a61126b652ca8200e36bc8f0b 33a32e97153ee30f80a3fff8adcc9893964e9476c8b963e169ba4de89f93d218 2fa9413be6edfcc2d7071903fc8e7e01edbda39aa3c27930f93b7560abf19ca5 ff7b657d3833182136bf471c6fe476d2eed893ac9ad1bf9ae405a38f70e50006 c978b13eaf05895616fbd95d01febac0e1094285c7ccbed0f4966c08105abfa9 6923004e435b2347197dd618a0fa77572b88b1393311b81964470c347322e55a d94041b84be66401cea0232f103a4c01f27a19323384e7ee6ae6fd20717054cd e2e5fa00e1a346048db86739562e9ccfde6fdabff7ebef318f7db66a4a055a97 5774914d0bcba1428a22a3259931da653c0266a6fdc1ebcb4f874e0a4110fd0d eb0129ba0137c33fe03e7f135c60198e074cb535ff0de98fa23a7b1cd6e80f8e a96861896375760f8b70e4e43111bcae231780388abcdb0864d852463c6f0e51 eed5c2634462f00a613d99546168fb5cb736b29de1a8dbda55210c055bf418b1 59f751cc9c1f877ce0edc145bcd75c67738ed43d5d9a25ec7ed22a77754b5311 6848bf1a407e840b87dced91e051b9f65d931f8ef9cf4ea32c0f3516178d1531 a8c047dbed09ca29e15000449c04a80e49ae88b7f9f18f53a05e6849c794c097 2298543c1eb6ca8001badca059f4dcd0bcf68de07c2f6516a9dc2ba44a3848d9 03df7a714b67757793ea5b573302e9b1c5f842816e2292fea920fac3fa5e2074 b3bf8da8362f0f2131dbd9959d1de87586c2f8663d9c98b3bafe989c3536fd76 d4c22c6a6723dbaf4a59e5328ff8996e4bad98ef361b861c70dc1f96e9e1b5f2 1827c849f201a65f50c11a8cb54f11d0c7f31a31e1642dde0aa59a1f6eb21670 dc344eca351b1af9ec0526bff5df3653f127994720ce7f37749c826cb6d680cb f74f983a7e46048eed32741bdb09888f7238075a0b86c2f4b1b42a6371a25d70 17d533a1a13be8558bb304a0fe81abfda1f855425044fd24f6a2f1c08119f802f78d0fd6f1bf507f734edfa0d9659b3ec09282b3e49e1a108b1d5818be767a00078f889760fe7aa647bf95389b479526599c612538d46a5d3f1ff5a9e6f47e08194b223a8c642d71d979ae45799375c6c2857029cc49e7380e4226cdd98ccf0128ca1d82b358e957f614030a575d7f0061ee4eea43a338ec298a8dc0adb3110ec890c6d282da3b59dfb86281c20fa27333367efab28095c07843b04c5e31ca00625feb782cd4f6971692d146142967548e8d279cb4ce8bd6618ef8a26b58850c56e45360cc388ab4adc905d26e61ab85764e0d0900d7432adebe0a19d659300d003c663685724832acfe136fa7feb5e9edd7f93ff21542e8aba1e41ee98fb90c5658568ee5076d55d4475efa13ea719e8e591037b22273a7ef23b14a22e21403280f00714488f3876f5251f2b1b085f03606cf59ab1a1570a95ffa831d60860541e38927922e6b3483ef8673b7cbd493f28925abf47ba9dc2d20a8bde327040997054f991163069aa890177f4500b44aeb22c91d3ce92512dd98addf41b1d30371b973348d899e849e3e6b0e08ae755723705f9cf492479f180c92ae6a51df0990f0d4c8934b17bdb8b1a0c3216bf4bcbe714a61ea050580b4adeb51f8a6a000e6a7f4e50cd1813a7862c49dba7dfb8590a3b1d74253d56e3fbcedf2c0ebe50b2ca570458d54302f18aed078bbbdc88dfc8cca01dbb9614cb28ae97e8fdbb70b6f067355e4701caba6bd38a25ab00409ff796dcc7c94954f91891aadee82150a7dd720a0c01ee14ef0c0a22b911ec989959a99151660a8f615ccb0c45856610e206c1b035e1c5cf9bc5d9700b3107753b758af9cd9dd798374cdae0c6d2708021ad1290522dd0cee1f7f8e8c4906a6abd26591b4eed00b93e7ec7f5497356508a54e2381e66c7624959c01bc69543dd7a9b2b3de453af45f35e1dea95986f300c100a916511ecdca25d271ba99207d42a7b9e06a32faacf891eadaec389f94078fedb3b24dfcd3bd1498bd3829078a99ced1ad3c0356881160c30eb47fe9df0ccc5cb9c779c0df13295c92ade4bc7d4e566d8e261c2127913930dc7723814008349e84222d954c94fcf1fca077a42b754564741670cd8efabfd8de7947deec0e62b1329bc7542bea57785d131e2fe6197d15205d4caeff0fae16f4066603be0786b46c4b67f0d2848f0396b2b543723d4642d6416e5f4679f9f1e8fa47d9360dd32d5152019e03af2c9d6d2915c33307104a9cbe3aefb990524c114778acd60e99ad19ce400034324599356136af79ee2699928dbbc1151693e66a8fff9e340405309ac01ddbf3f7b80f8e396193aec2a9531f4dd965775bd541daa3ddd416014a9ceddca69afe0592aca7399846cafa644410fd51ac93d6b8fe894969525b029a6b5913029c1f1eaafbd89e5b977b7603caf0af69b17c3aa4ea80c3af47e40c3655b521c55e0bf1d7b4f0049f0900b2c16f1c51b0021f3c9c71a99ef53f230349ae518bd1a9fbc7ab6dcb37abda9c735000b9f11d079aa4517cb02462e7b1003ab778c0bfad50b7d177407cb37f32d2878336de4a5dc1f188a63f41da238700e798293720c92ace8599f20c6c647752513a437b9bc4e578fac854d35b8a6901220f2abfdeb6e7cc0a44223209380ee5adf237d9cdf6cd14de69334b4c07e3024479817daf154120ce86afd785e9942215304644a31eef3d58e9d3353d4f1f0c168755aedc79bdee4d3367d5ba27c21e6dd64109cc28abee0927a7e9fc8fef07c5455ca7efa76f8c923bfac4d10bedfad09efc4b58c8152e7c8578caaa7c620442122708ff5346500530f0ea1a5916e1974a42e718ab5d54227ea83b5cccf203f8c90ab4b573e7548ea478656108408607750070204bdee02363b8921358140b813a0ceb815d1c5acf779092e82a55ec68d6b4f90512d6abe8ec6280cbd578006837efc6d0b62e5b6d38c75e6a473bc15d1e32abbe41a272d3f308d76c3e98011e80d386bcc98fa400d4fb3057db20e1818e45edd7f53d19649f78671496e30511434b2c5479024f4241a09aa084a0c67af8e1b51a0c5900e7bc376464b72809b9c1803a422379b18c2c368d58f3d6326f0c570fdebaebd4c2a312923aa77606 true +check_ring_signature 7100bba35238fa7173bb0a3ed2fc35f1d3c3810005129ad9408d8017987d5179 f1f561522dd5eb0f2308a41fd72b6e82ccb5100a4ade2a2dc80ff640af7e9562 56 25b828e253e74f82e36b0a817b2d4fc15f6f1ebf055b63a7dc80222daf203aac 76877f205b212db801ac1bd3202610259d4c29c2f3d612d0974aba7beef0144c 634694510e92cb08ea2df960985477e2ae56190acd729509614d2d76f45a2f41 ac527ebfe0558dcb2ce6f7c6507fba3703e676d9a74841a32d6e0ca366866200 3031526e4d4f07ca9380d81486f48d82a211f152a013d134dd74955f411f43c9 ecadf1c3b63783d1e2831b6d8a17833776adcba21eaeaacb5cd235d36da3a9b7 4adfbac43e313645f56929520dfac7bdb908f423199b560708f9f2ca5c5a5b43 d1e6061b50080fd6445bd8db6c00762e5dc38755219845438fc062f7de38121c f34acd00fa319e59acf428006e468e040f50d6661db777e9cb288697f9be7e80 9e42e76188864a2d58d074411765d4f306d4d535f3957006d09574b2f895eed7 89913b95832fc525ef9807fbee4c4e6a0d14c0bddcf0ca2457d18e90f6a1bc73 a5d8fd092eac8553214639042ac628cffcc7132808ed39c13b4e00aa10149590 cc45a0a968707aa0dd1585e4348dd5df01bbf5b68cdda00904eb47b2ce568b06 229d0da9392e3300c7b12bd722ea2d39d2b4ded8b485d51f4caeee1029f77fe5 dd28be937620cf957a40ca22b5547e8030cc5df30c1baa3bd772880bda000857 7330cea88a5905673a66f3364edded35951683587154c0ac5482ed587903e199 48a0869464b5d95ef8e563521066b527348bb73e59e44565a979acf61333a259 2d94559b6f1ac8404ef274c247f35ecdb2e8edfbba590e66f6da05ac9b63a1ef eaeca6a912d8c51a3666ca088aaa1b2b42b2a6fd55165d5f5558f2e620428aea fa8c891130fb6aff1fcf560ff3551ccb45a25a06a26ccf58e71c43221fa85edb 5932e258261851d7212a319e7c0a9f38e4dd70cfcfd446759fa565e6d34f28a6 46779980552753c016968b3c5e8dbeceb068bad84012bb852d32d64fdb6570f0 7be53256fba4bd5976185a1c1626345b661dbc842e7d28a2fb7a4329bde0f953 e879b6007f90208fd746058c69777e72f30e60a367b4d8dae2ca0df4e1564a5d 407646313484fa5681875f3a24fa58e88901e8f6fa39283aa9cb05cf2694b2aa efd87420248585e324e5c222d76beff7956c36b29450e6d7557f0a6e3be94f71 ce33840dad3904badcd3d42f5687452fba240dd3dfb79ade7e87ff936b9e9d1e f5cca193f5ba98db244e38ba15c35ef2c7193efe61e127da63e74d1879a37087 eb4f4a83d78a28e8f319163d0ac2c063a2df464c1dc00b2c76dcc4f22c471068 81a42422a87b707f0c414a6f8e18bc7cd6201d50ea9f0e0683a08b01cfa128e6 f0a9751a313495d44065af794df63cf13f1eef11dcb493b85f42be2ef2d8ee87 3e1a36e31a51e09bf9a85d99f6c0ef2e344b0c8bd8c82d4cc3a84f7c50f04bfc a9802616f6d8319f2902a36cdc1ac78700b77a14649564b530eb4a8bc7f2f417 5dedd32deed2e6a79b08295ae3ea6475e5286a150c6d272da523ffb4c583326f 8d86e358be4c920b58da435260e0f42dce1f927a9f3a1970d0f6d64f2c79c754 8b5eeeecec9b0f805b124d4399ef5908cbde1c5646015b5f2d75db05292ccf10 f689a041c1559dfcbe5615c36dfc88e7cf48b4f301cb63d7b12f6e426bfbcbfa 86f9566c0df751723b9ba413e0b2b159bea9af2fc83997ae8048b88f4abfcd53 5e58f10ee884b09799c2d2c6d3478c20569d773cebc1e86a64938e6d7568940b 71725cdf6b097518b1e1e58db110c78fb3998597fd9b9e26d386bab48593238f 87b95eb61c9249ac588866dc2b93151d610229c30cf0571ea2d03c43e739a4ff 2e5d14caac02fc0729847ff5ffd114fefdaa5fd853851e9542e2c5368a52c007 21f66daac41fa1d071d92979e132d8741eec75e7fb327d4ee2ac515ff783bc0c 2a0703f4e9b161d2956ccd7305b138dc1b4c695712cd09c2511475523b718653 676d57421adedc0c43515c3884e86e12361112250e7fda86a9e5d70ea8a77ad0 7fb5c09aae53a19bec93d31833efe5560fe355f55709d626c4aea97a3a6772c0 1bc4f919c78cdb9cdd582fd3a4557511983b7e90d4d3d292a5e1b0bb50c79088 6a0689db6798e669f76cc793c25d1a068bb90420f89d57c71b9be326e66b9197 b278834ad139e54ad0c7daabae7d82554cae66421b10d9e18761bf0e9b2f049c 3edb634b677c27828256c44fae632828d6b9b6944ada303e3936cffe42018d59 751977d61ea77c49eb58e5441f0dcd802aeae188696b62db76c1510a5184e4c9 175e0a7ab589c008cf772b4189f642375e94593410b274217966d4487aba248f 3d01bd4e725292fff97ce5dc22e219007f41f8fb3e0551bcda7b237816b7926a fb1f4a813f905533a457e6ce6109c81d4bab78355be5da877562a94380243b8e 1f6174f9f0e47a9dc5a6476cfd77ce67a5aa23048ba624ca96f73f8000e6872e add012ca5e6fc277f203aca1ff956e686e65a7b59e1b87513bce9df180b8f94f af31148981651bee22102329bd142f9248649a8ed1fb1db316d31315cca72201e930b90e74e334510d772a8438d3f18f183538a3515922b7efbd28a7ca0cb40be07b190364d91f7d9821876069a2973e9f415c9310b74c5d29e46ff7f10701011cc07da90be09c1990e206bc445f7032ad067d5aeecd5417c15a68ab4a75790bda31436cb3170ab203b2b002e599360d21d8616766933fb818b386947fe93703ab323aa74c244f60ef3d13decd730cd202d7c78a0669e1c177236ed221c96f02814dd05100712254169c510a0a5693614b25520e42334555123086ea8ad21904b8977af2b76333201efbf6330b0f7206008bd2d9e6713b156563d54c865d09026dac1bd5b64e5212e957ab5e560c66cca5439d797399beaf68aed2199bf2e5041ec827b8c8e7fb0c328af6e16c0ed741a1586df9630ba03d2b112c8b24309c095914a140d61abafafc73e021ea8703cc64a3000bf26e22ca0332344ea73062033df76bd1fb2b509f2ebaf854d65c96cff0de17b62567991a086757200b15fa005e5dc98b3c4648a355999793b1df290569592f4ee5978dc0351b1b031d9e8c0d620c04cfbe44274ddb5454427339e9f7b7ffefc873d909d9c75e739f76488b0ea77c49d0b053cfa514da8ee4a98cf556797594cd7bce42321cdd408ba416890ce9fbcaf9b02cd57e55005b2633ff7981d3b78276a76304d52de16ff7945a0d0aed66ed91e403705bca6cb621579cb1be9a896ed877772264b2e68ec605bffb01081b6f9f5adf51c1b89e4356155d48c21afea9cd1f687dada147d63faae025035f533983bd1d9a52e83ff8ca36d280b8bf5861025fd597d651f6601a5f40cd043d832b40ff5096ad40d1426944a429f33cc87628c2482b6257e4cbcd5b50fb0bfd6663df219b65f33481e020b97df31843ac4b823f95073ab9ec3781d44c210a2987b0afc1eaf45dc601dc9f6a99f0a7dacaf3a4bfd522fb3d8c4a38fa995005cd04b1e78ab007616da186d5f492819bfd521afbd739d093ca3c6bfb1dcbae027eb205feeb6e1fd8cc67df8f5eeebce9b734963f1e02a01551212102331fc504e984afbd1a791a9a1e67db7cfc3a7639742a8138b6bb3cb134a4d14c65127600ebf65ab2d728167a6797d035166b86b761cb5f57b0e85e8c17c1608328cdca0f187354d3870ca853fae722b129bd21fb2c95940d3fcf24168d4fa9f74c53d20989a9235c4db5b0aae3c416634ddb43b47c94da873bbe3a7bdfcbe90c871aad0a5fecd39c1a46b63cd699a3e989fb52ee3dfb873252885ca67be8b09c97ab7e0b0f8cd56f418122eb18a0fa9e8099a85f40d91908ccc5b7f3745e0a4f8ae31b0a2bb41a60229addf6609f2fea262d95d50a6cc37f200f3fac196f7d95098be905162a11dbab63f0f5b9c04ddffa084dedb7aa0a24f5d7b259756224fe106ec00475f2cf21608ec489785fc3e18c9b75a4ea3e6db90ccddc827c2b01fd4a115b074a74c202027e9b65631d2f8ac7f9bf922ae8150b092e3b2bd3d9a9295a22b305e482d1871277ab7ee727e4bc57b4e95b37565853599d51464dfb00a7c4ab160bcfc1c17d73ae365860b5865064d8c846e6100ee36082fc6bf6405d7310c15a0579c8d245d5ec52c0210abdb5a323f7726d63a9dbabd48203ff682d7ef009580a34d64042804a2663787d8c081268458e845c1a2c0f7ebb6821060cea0791cf0524477304cd480e468b321c44ce3944563c60e795f985c22e53921eeec1f38d0a6d4b6e46cc363ecedfb3fbce54beefd89f4bb7cd08f49dcc1035750ff2ab160d43ccdc77c926cad6e12111a5f69b4f02a2e9a9a9a25624912473145a7469cb0e10f3d55e5f36d045832b540cfc966e06c5251a881dc114a02edf56a1db6d590e6135fa8ecf4fda905fdee9328f8da2f105b571faac0466b113a6af156e08ab046ecea39971c77711bb187307c195e196d05041e7938e2149b795bd8f43c09209ba3966d9a7fb6864fe964b1f0758577a74b113a253dee33cf366ba38611c840c7b0c8fed6b8f17c931296d9cacd447bb32992e72d65f15d385638543de97590acf39af84e245643398737776fe2d2da682d0b43dab8508dc80701eaab1417c0e42e054166a9bf09047732b48bd85a16b920085c1e1a9448b8208498aeead610bdb6ad3f9e1c894b1bb4d687c2ed0614647b2212ad957997619f4a08f48ecc50f4108a8cc44119ac8aa6415d549efc9411935f33a970a092b10ebdc4e3e7f24072e0b15e7b3455c3a4f016093ca825743b32a34dad9802ca304d3cdbe3891ba0876c9b41d132c1b6119b4222cb83fc58b239804eeb0e5824c42584a86674a3602f78e850c1d537d6f3e40d982bb27f2bb3b135b758962471be6071bbe4270980b5076edea0951599aae83211814e479408449c95b22ee60363442090037c3510a24436eb93e66643cced1a395db824fe3fd91b61b890fff89d1735f7c7f636309fce8b6c5f4f17ccdde12e856c2ab9115294bdaeb0361b29004571cf71ea57b0d4227b537cc6d387fa97c83289de426f5d254720b20566debcf617bc861963b01944e3150800e6995ddaa44975f8c5b91d64f388077d931cab200fb6252f6e802dfa04ee2ef0542cc9a83be11ac3b19ee420b48dc05fca62467db37c66ab52d0aa86ee630b05327b2e6324f421a6587f3693e8530e81b5b115185c5376d960d00562e3bbdcc54348913f589f3f899a09d2871ecb75490d0a21f7eb1e6da990b00d9af4b45c06ec7933e9ef9e84054a30070a2cb537cc2612ad4ecaab4e88cdd0a875bc2b36ab95d1517e53e4f957fc1c6aa8edfb590877c05a6bbd802227a5205d322ed03dc7d105102e0e2dd36619309e2c835bf0f1c0e4068801b9cf27ec704a144f936431af7e15045ad25bf0cc1e8274d7ea81079111aa22105ddd2ac5d0be9dceaa5cb22e49b7d161b25949f23882e849acb2d71ea932c027f5a95876d002be5a85cffc59bff98fadd9dc541914d97e5e02cdccb3a6d23b5959b00ed1a055bb2e61c920ddf5d5097bc6a6ed82844410b0098c3b7a5c91c0dd8ed38f3ed0f24733057f27d80ce675511d2bf0c08ae1ae19ad0fcf522ab0a8c8ded88a31a0816f72bf6a1cb77b3858652f9c4aeda71bd1fb7c6c14fafbcb80065031473d6011212356c1f5b14eca9feb49278167c788f830f6f7373cadf138417d602c0140b68dfe50b37ea584ebe960e9360145fd7ed728282efcc1a5c55373879cddbe802ae1cdd5754c8e9e7e9fa9090bb10b62f1022fd93f208338457628bb525bd0e0d27c89e7193c2783faec75e90d557569fb7af3e722222ebc922b9f31f2e30b608eecba5c9e4e19582b3677a029c409bc50c519691c1b56c4ace75394cede2c8025e03489cd39ddd49ae907fde3ed361ba6af75d5c9a6e9b5cdf019e87ce1446085fd6e3550c532946deba38efb8f8ead3858160c13818452b95c9cf126727e60d1125064913d002e3dd49dfaabdfd8cd8831e015f0b4913bb3af78f62e5deae03d04bfb7693b9c479d83fbd525cc34e0d725f2864eb6a8cdc1e5f2db1516a450f19f3ec64b98cff53316506ae251346be7c12de3b2f63b085ff419172a4a2df012555abe7f62cdf562410ee863d0c4b93aeccf9fdab62c614cf8fc90f52283a0967d901379b72ac2aae6762156ac39e9baa7e8cc45b30fee36c658662976bf00e3968f86406bcabe943ac1d51db30d2dd4cf3caf743ca6f093e971f8f83bade053e9d767bda385ea7bc835b91cab262bb1dd041ede3c5a9349007f6035e6bf602dc93acae59ea912d7058fab6d25089c39fcfd27024d53a0d48a0460e4bf5f90de68710b4cefa541ec3efd7ac730f52b241acc219771e2979263c40aea996190b9b373ead89719b2b7749cf12843b89555ca553c1896ddfe4762d47242de9de0434377e0df1e93ee5724bc1da26a64ed29a64e06216e8cd7fc26b215c8859a2054d27df3dcb4d0d306426ac7cd6049c55c855d700a4bb78322c5986d92929f00fcb12230d03df10858627a9f8bf1a3ab6d36094bfd40ae8b801a36d4a10e9170da0a5c914f4c20c3ca8caf8383d5b0666abfab4b298f4ad7fca3e35d9e6cf5e062c6a500a201d091e4f6c1440e607abd65cf4c9666ad20cd94a5e6ec855be04006a139a335b7669ddd35942f4a300b579d648d59d9b03dea6375ce5850028f90261222c10f7a75f14ffae181c98f944acdc1f7a1f1fecd8c2a620b841f541c1027a8628ea98e95fa2a2e15e722125367af23bbc3681af58844e0dd738ff1b3e0023c5d07875dc68ed1ecc6ad23357557d4584f2ad88644ae8304b2fa0cbe5270fd754f80f11de96f7159c995fdc2d31fdde39d9fe034a14c70a77d8f8210da808e0e8e684c71531438265d1eed37431894083fbe43a20c5026e1c7dd3bb46c106c38a215575b784b575d7707c99666be518de16745f01495e7a1e2246d560a207ae35620f1a42a05e90cbec1d6ccba4129820f99ed2e6a913b661119022bba50e9d4d357a1cb4ac1a02c06e2f4da52ab4990b12a9bf882fb1df386601ea05170b18a851d47436f99bb154c4480a4dc5e076cc20bd6d26a18bfcdd913ab3f7a50a802e74395f1c0ec5cffe498f73e949746246dc1c917111ba46a294bf0da10d07aa0f5ba8e10cd7281afee315c80d77cc8f4bdf03a82fbfe652d00ea6229d4c08059afd028236a9a528a15c96f7b5d91d7580be376b3292983a4818be5e0f54061677159861e76d366fbfd6d372cc8824b2715b4589a471abcac7deaa7a589f091c01ad9a00fcce9df4d56536e023df5e33be5b0ee19cd876c850a975ccc5150cf250e3ec58f94fd6a04d585167cd82b1bf78a4385fb98ce2ef0d00235f1ef00065c2d7420a2a28da0703f73cc4e124c0ae5965d407e00cab64a2ed719bc879041bc757c5124bfc2bc4aa8c571a56e7b6da6c4468b857b2e68a5f4024741fc90f788563355f60f66816c94553787504b51013cdbf8ec4d6babc2d490a960e9703b0eba4919318a4bb59cfbd08a8ad7439ae25c7b0b7d65b9b96e48dfc28f30b0c false +check_ring_signature e57e2027b66b49840bc53a9881a80a91c23a75cfb2e2023e2c4fb9ae9be90230 905c44a234caed13ef89fe6c0958928dac42ab46721093cd49cde4f8070dca29 21 982597d9098a7edcee98f67cbc29566807814eea4c47f0dc60f6a6965aad4b4d e9724efdfd5e3b16bea7de3ad99f01a67408e09b074624bb71ab975ad457b60f 60dee49f82c5151df0e40f25018fed1d8eddfd469b19c3502f322faad05c116e 6dfae26e1c1030468e2d98a896da84483daf2b27e9e55abbdd5f9652ce369d2e 48516b3b95c06c90e21fcb1fca7faba28edd81063ebab6293a235a1291b68663 4c105a126d29ec09d5ba866fab2ba26eb60cd512d152b72f3333cefc86e8a717 6b0c7baf3c01363234d09dba9dd4308e2d62b1cd5777bdc745f5489751d872a9 e27d38a0fcda08377cbd051d1546bd0d529ba2edacf459aa32dc76b51b09028d 43c6de3286328182dfd02366e1d5af7e9648c368ba7c3e27962bee7a4ffacfc4 b2a3a469734ca12e8bc50849b58b8b1f98fb39a27e496bea2ffcf03fdb41cdbd f8c01abc436c4fd33e3c780a68ace5a6ad2c5fee336ceb0b7d212fc863e65864 8caf511f808f92d9709f3e2a98a3604d7c22ef4b168a9a1500cd4e3a79335b88 202b90e20d3bce3add9956842b630eafdba2c5ad765ee80520dd5774582f545d 7228e02e0f088d9f0c8344f152c6cf904c8a31c52d7fda29f96b1bd32e86013f 8e4714b74701b4d960fb1c1404c3cb4299e61c902bb743b250f3f2b437fd27ba 8d42cb922d03d29d6917748a4deb4bc5ce6515e8a3fe52e040742f0afd6fcd1d 07ece7209cce42db84842766ff5f543df2db78a7b9dbee26b75cb51776249799 019c196194ab9e8bbcd80a945bd088283a36b8cfbec225c1e402ce73e82bbcbc 9df72be3721cf6a27648729a25f84d70efccffb4d8db6e4ee882f15ba57d7591 c738a55b76969040e48db1778521d42b1b3a9c30e340115036bf59c97930cc48 ca6447bb78cac5f9b3320245588592b0fc0b3c628e3eb13090e186913bca2479 24d8bcbd298f356202c36b3c31aa07922ebd653c66bfc58af06eda24fb407d027310d12731979b2e2f2c24b0417a6c7eea482a8beaeff63bb596075df21c1c0ac89965a63310b2e6755f665b1cc12475ebc261eada03ce2c418d1241a20807010f27c57c6275445ff3e999a732fb33ff35014c2cddede369c471de67f8c06c009663a0b093326daa0a5fc9ae39bf0cca9ef4e194ea1f2eb19f90e666a688db0abd3cca273b13aa77fbbbb5a9c4e1c3afb0b312888797e41402c5402519528001fd48f0ad78b47f9e0406b8b5bd0e453524f5f0ff7c86409c2b4654f15bf786078ba91a5cc3e36d7084c643da346f4704e7bcdcbf088dd98d03c5724398a948068cd36496b6c0b568b1f6f5d2a5cc934c4fdaa1b1b05af58691a1984a3f0cbb0cb9c3ccc1c8ac5379ece7bb1cac021f7113258bb579229735a68f78672dff46023ca0a6ca6cd5ca67fd6574daf8f6f34ded43cea207987d6467d4bc0cf4261d08557972adbab6af701e1c86b27e4c6ddca0193ebbe63e17f53c6bbb38d1c6320093057d74ddf9c6f98731317e83f27a02e50a1fbd945ab7b681477f2a37ca5008ad47d3a6fc619dc889e2dfc9a02ffed6084ef60b5d8f2c95a1c3caeea0a81f0770356693663c2881b27b8d5d0348e6ef2156f60c611097c4b598761556744e0c761b41b72013aeefb0b1a48bf9eb05cdcb9a661e92cc69a52ed9b61292613a05f14a6d20fbafeb0ec38f1836fe76b9c6a2a4ccb6de20cb0b6fcd85bc820ddd0f8e97e74ae65dfb4fb7799271ae1b58688408ae941d8ee2c55a4ba89a589e8106b73e4a45d36d1c4feaf55af5a1ad4793b857d8660af91a1d116b78e308918a06ac649bddb14196956aa7e9cc36aaa7a970b302c8431e0ce362b312f8fae9fb023843ae8944ba0dd056700e913006d1ada5009db1cfef67d3858488fe0d3e9c0481be98ba95cde6f663a6b877d04fb5d5b5c5dc27eb20fb8af1c04fec2e25fa047e603d9919fdbbe93a2edc9650d1e633e97c6ef4babe34296b40b6ba935a1c003ba08d7e0528dee296c39d0d5bff0bb50c583a4cbbd664bfb8ea2ac600cf2d03221321134dde9329261b1fc37ecd13d7b560554e0be967fe3ff27eb910a9bd055d9d1885c328fde4c0271b93d30abd96aaa3af4a69a560cf6050261b4711800babb8b82bb596536918c219661249d69c9846fff075f1184bfe6f66de4571c20aaf9c7ddecf5a3bf49655fbd5dbd922e35237cbc54fab483f121aba30fe62fd0b33bcf2cf301621799414b3cb294f80ff4a667aa3c07824a25ced04483f08b30a92e1e2c5443daf521f008d3c5b3a7f13893fe72e605fc06a1aa460130b9bb2037564db1e621af86b31af3843fd940cf6ae901bf4bcfc6eb2293806f6c733890f9088e5bed384f9b459bce6784ccb7942e52b5bd036229cf665506e8e9d01c30f7d800d4e0a17b31d9801b815c452dc85e3f557ff209f56a028840c4bccc2450d9f9adc8236b017966af3d40db8adf01b1f0ed1fca32fb0ab8874bc9aa49d660050687a1980fe50af6e876119a06f8481f86245ca96c8dc6d83b1636af38caf0683d211f3332edb369a7c8a8e4b295676cdf616b085679d756c2e3ef87a3ccf015cf5fd06f2a589d3371801e36b791f7c353458480f45eb1d38f8d80840752109c9eccbecadfa7bbc94bf2505c55cfc71db5051faf4639e043ee0e21909b3c701dc0c0eb1c771149bf386d4a4fd06b747148a5b6ed5fa1e904fea2af7158c9f02eccfba1d4bee1100c723298d4991e2cdd438430b4f4919924763c8bf66ad9b095a979d64e191ab041e2f7e3d27c4888ac909aa6db0436e3d3761affda20293018443d4924e9b2f5bbe5f2f27cbae51398bff26f8c1511e9180ccf589d67eed0d false +check_ring_signature 3b293844048b224e7ab0aa1699c38729933071b9ad6fdac9f1585bcd22e317bc 162693e30f150ab08c4a9e5077909a2a3377233a551f30451e40467b1ef10297 190 85427d6c40a655b58931733ac9acb9a3b9fd6ea688fa8f032bf568d51025cf4e ee88be803f7361fa75d93604c1edb0af70388f79848cc13a7c5721b4be9f1aaa 18472084a2917a30b4cdbca0fa37ebeac2e66d70dab05f37ac6810c7aec55573 b8410e741c1981b23a48bea8b020c69e05b1081f2aeda01e9031fa3cb78be31b 428fb60c5e209a90e2dcdd7124799f7a922ae77a27e85b7081abf6c973384cd6 430e7bfe84fb93cb69ba2ff7267706c2ac3d37afb09ccffc0d236bef5bfedf3a ff86811359199ef411ca42603cffeeaba5f50cd0a21065e276e4d5081e12627c df307d3a01e05ba15ff75ebf7473510d94e49f219638e33166bbdb01fb08ab6f 0d8444dffc86d6482728699f4dd2ce8cd4b0c73fc462e0ee507da7f795395e83 36ee38f6ee643f4dd085c52b678a4c06c2bf7a07222327a29cddf81842f9adfe a2c5af123d1751b7bbb914a5573d30564df592007ad9717d0ba9d831ba6148f3 a2c4abd9c7c516b553706948c18abde32d8823a8dcabb560c90a975a2147ea6a 0e7e755744e64c670d59a8e0d8ab096223892e8d83ea4ba39e3fc8a6a766727b 914612c49ce7f2c4ac19e750f678ae7866a530a401bf784b52a864aeca2b1601 57b44d13ad33f8bbd19fd91aabe5818b3abd9cba0a4542c9740740d99a79a580 1cf4da10c329703d0925bf7f17c951b9fa9752551f0fb0c1fe77661853360905 22b45eab5993a327cb22f6b6dd8ffe7a3e25332dcbf7b182c3ed576a4d7a17a5 8decfa84e2d4f8d57bc7950be32173585f627e0ef38949abeb5e10f0825a7f60 7bf40e952f675f6f5eb56c3842fb0cfb1dfddb31473e9ff39719e309f200df6e b1c65cf9cb874a212f70ba25f7cf043dfb915bd1bd3c97556636e290c4a60fd8 1644f4800508d2c18d264c1c618a28accd62905e99d451cdf7e461fc0d3af6c0 ad70d817f9dc6b2e02178ba5eaa5af869582d284705e6cfe2b4b70a11d00e98e f36c0752f5c8625d8c9fc7937939b3500f47509f054e6ccbabf44e4203375c3b 2d1c568d5ae10008130e0bef7ed427517416d9c2778dc089e44593aac8ab5cea 35faf6d108764797c14111336a5a3ca7b5f285bca1b851f75daebe7eeccbe282 031164059c20bda46f4a138af70d36366d38ba5016c70a5c8696241dedbaa5af eddc88a278e152a0f7804debb25e1151b01483ec048f3bfa10343b7124f8189b 1f74a1a648ad4f27a0e961355b25af20df9b4c448b3f256f21ad1cd510a87fc4 7a424f491447ac1c040fc73eb3e4cf8126ea9647c0175c96bc8504c3568ee7bb 5ae26c79b645cc18e1e1287856a799b0336126490e1accb8dbbace1505c1999b 97f7e7a4bc2fbbea5528edd41443a46f5d7ac857a4b175c413ecae824421f157 912c6e9fb99e61792fdff742fc9910fe0e3872a6bcd52b092e5048e75b8f6a8e a736df1463c198034b8b0ee688ae02076b1b777794e726f0a2888b72645023f9 57b083d89548884b1999657cbfb0ef99af616772bf88909b5a7dafcd1c4f81bd c67dc05e09b6323cc5de1eb231912c82a90b2bb8a9fb9ebb9fc05169922f1e8f 4a231c86ef2491ecf2168c70ff41421d82fa6087da9d8ad0250b60e9caa714da 63ae1a57da75074f0fdb72a9d2ff0589495ef54b12177fe93793e16fc5ab54d2 a1da7aad49a990d44f274394e3a15b1b7a6aff60992f8f594f0e349429917418 c3bc7ed17f240a1d5299752d57ff06f26f6f5b3583834fd56b86fcb7ff3ee3a3 4a41bd084a0bfb391d171821b183805b6c000587a0c91b40dc9bb15629e06454 6b5a532efa47a60629a85854e339fdacc821323c3f993fd30ce8b28c99cf4813 5d453a859042ba2087db2dd12f6ea9970f41a11c07d0eb88765efd4a83102045 68e1abf71e45ee95a1af12e69a6605ccb734e6fe64522e3606e18829d7c745e4 857dff17425e8346db357ce4a5de4250249f2e58828f1ce579871bdf670406f0 c8f4bec3540f0287d7eea607ea5d0a1b371203599bbbcb197b74f4c4f6bb6828 5212691bf94e3b858ddcbea5e2c58cba7eecc321c18a51f3e46b647227ec34be ebbe9e8031d57076f9b77a9517e05e9388c5a8bd96ff60fb45607b5e603e27de 24e140dd5703ac84b17d143bbf9bf6249d9d509c789eb999e5c878bb5d1796c6 78b06aed37c493b7123046904290d563d5ca5199a14318a9952e2e274c6ba750 7020a60cb9f83a7f73af057b7d78942cda6df5c318737de0fd3890f70b996237 ac9a2f79099dc540e350178e137fc7c27baaadbd8c2216c8917f975baf29f7ed e50861d239345381f45da9fde5ca91a28c059d2e3df65d5c585bf8e0b117abad b1748a7eca2708b06ba2537ffacee66da6fc51445cd8c4a94fc215cf59ec1b34 4f61100ebbff348cebadc5f5bc1a75d1a120ae616c3d545055f1955e4f2611eb 32a75427f3f071bd197093a9015980b2bd2f71ce6b801588cd3d2e7fd808a0f7 6dcd0ad4e851a379c9f1b3945deb60047e2b0073db903e15f25f363ec17bf139 b5f0ff1a549d3ac40411345cad6c5ff5dff23d3c3b4351774d1447d3ca51f56c 3849c71e73738bb7389077b78d78c77d33a86d35d048641972e0150131239e0a 120a5279f9e622963275ab936857d86b74aea8e72ecb8b7da2f4fb51656c8bb2 8447b6cff2ee24f621fe3a457fe5422be4388feff458abd81dd9b25b65f466a0 69a03cd451e3d975578c6540190f01d46103f820413191fd3dd485dead82fc20 869f1aa84d050e50a7a4133b30014249d10e3701bf0f5371c65f79c214677b66 75feceb8e588a727d1a31259829b61be7f9cc03d8ebaf775e672b48526efbf7b 155dd50aacf5e27a84290a9cf6689f8f8ca46d5287f9600f324f89cffe363533 eb3acfe6a6bdc9b239b960b234af5067f3fefb52e06440c40ba2aa96d6d12f5a 59443ec718b3d3ff824e6a30cfeee5811a5ce5a7f8aefaa629a15dac1b4acae5 a8277305325bb0a86efecadb773fcc54c0e10d642845bc1ab3d215a8b8cc0685 db39747c2c7a52b972c38e5a9c730d84fa2ee540c4b23f453cd70d454e1eb94e 0a308f79471014ed70a424d750fe6f59c2a5ebec67e42d2c87716df49b82fb30 25692d067869217f72612df321c4de7b19e83da8ddd748792ad2e2549f6390e8 4ed6d743fa1ed0b82efefa84eedea874dcae90a66a82cdbf9c715c238fb98dea be8407150dc5438a4d4bd57f8f353549656201a64a6828a621fc034f059e8677 8001f2a6046cd6188979bc7725778d526e0541a8172c3ec478c148212956f2d3 78ae5016de6060ad93c2b470993f5d7e2578e8390a67c36993c7944f074ec21d f89d4a492cd335837bbe26c7fc6e70e59cd473df3fd9a35e51dd90279b4a6358 171c12592921a15763c1117abdcb431a6fcf96a8c7bc0aa5dcb6bd4f8fc662b7 d38e7273f513d8faca4f763091e9b1f76cadd5ef79eeda81a3b4e705c3531ba0 5da8667bc19d6e4b2170eb17f55e4d3247e4ad2d3c77963f7c0f59cb8bc6797e 8dca6a6cb0b182587f1275c94cb45d3bec8fb3500fbd6fc4e16b16a348006247 797acbe53dfbdaf90141be622b2f62419665e78e321a52cc52d45611cbdd6f82 f1fed8d5c1ecea1230d1f15d474037ae3fc0d8e7ada213154196ca1975a59817 74e60b6aa52ddc720082da0e98e0a5edaae995cbda9d2a5eac77724eba67276a 4ad31e3074651393074f5cf19afe6c13926190ecdbbf77ea9821b78028250b71 22f953f2f46905abdd995b5605ed8aa1c6ab44fec0247387696b46f26085e4bc dbda6b1cae575e4469e904820bd69644fb768e1aa2b9a2b48c15ecd62373f9d4 c4308b6024303693ebe69e62b771faad4b1f79c236768b12191329d0db5bed4e a183db7475f3712e849f6cb288fd30206fafdfa5b987ca3dec3e568946152077 a2feb2fadd450063edc676f18885878d7176e17d2fdd404f3be41f4d283d1fc6 aa9ed9d9b3b60a29cd6009d5ae565a4f9f10f31aefb710c80811b4d0cdcb9814 923ddbfe0c508aa996f041ccc806e16ab83f9f2fead3f7f286d579d91c674e70 31784efa68227fd0f12f4dd8c4a6bb650a57dafc1ff7f92ca288993af5031e0b 473a1ea559f3d888e99d2bec72878db9f954c023ce06a9b456253a4fb88e863c 920b978c0225931848a6eee6eb27198a179a438e6ef627f55cd0578b2df28c9a 047b5620c2d97bbe3eff04a1368cd724cff5d71aa17ebccd5a44250716cccc78 b1076f3c20effed95750dc059d9e18a42b43489480454cc910cac1a99359bf7b abf1a33046ec4e7f3120e82c9babcfff796d2798cfd1796507ad5e077fb776e8 7f1b724d3fa2fddc2005d39c4b6ed25a2ae4f4e8cec9d0775a4727db51e86a90 f10eebd0b90b9122e07e74990c5c15ef9ae8bba166949dc3d8806fafdb4ac14a 51e5c1743d0a54efb8a1461bd0055cdfd3741f8b1e8b2b02dce5b33252bf0145 431148852763777a9aa7f839ae3acd8ae710f3418fbc72d58a4561f1fac7e443 7f1ac3d79ac59ad67316a1f6d8115cbb5f8f0da75ebdd6e70247c123cdcc43c7 fc525ef7629b3cd9ee38bf1049ad3edad67e852aebc8cefe609902f4b06aca52 19506ef9b1a193da2cb21e9f41d10cd60c14af99ee0aba399748c7b8e10f5d6e f0001cd33002e4af9ad06e707e113fcf0be4b42dd293e1eccf4eb4ce47713cab 737cb0049bb20a903a52070446e199b8e64b4543e1147123d2997673d4d6e9c6 a470ed20ac453853090a9415b3e1231b09d4fdcf4c80262ee4548d3a36247427 af86681033b17c8403a5f42cecce8c02321bd504e8dc24f00e1af45ad1412c93 5b903aafe28aa3a6d843b3de25339d5a0fdcbe8acbdcfdc74f04ec1a41ef65b6 368c44620b0833a9feedb258232203d5c61bc1c77a8a4ec3791bafbe2f6ab4c0 dd8c54c4bbbbc3a2c3480277b264dc9489b653939c4dcf96a529b08838c9e912 a2fbbebd9293ef60b1ab4c3b205a9120125be6109f88238af29b112172abe26c 842116f5e5ba5fcd77f8636ed91c114fc1cc5cfdc6f1a43e18da55085dced639 0d4d9686e37ca94d93ba43a959cc9ba9fa2c96cfc512ac253b2dd70d5963862a 01c7823a70a79e5fe486007313965fbbeae438d9c0a34fa34f2bceb75370ad5d f7795f6e9f8cc914e4ac0252955ed7329866ed5d20c485a037b3b1f3cba32d8e 3d681b130be4daac51c620f610c87ef1994c9c25406e6c2dd264f852eea8cdf2 65935be0cb6405d0fb0adfa351696804e325435b5e889ffc05c9537a0ea848d3 b79b1d36759849bdc9291ee61761060d3a7ca6a4571317fbddd33adc40f46c01 fde0e64f0e664df0f4b1d3d12ccc47e1b11853f39fe5dcb2bda244e3fa999578 654774002ccd9a84ffdc60b43506995b6866647a84996ae99e6d3b49775fec4c d8761967f36a32eb059e36778f37f9e6de6dac81831e86a68c5357c623bc4300 bade4b379dcc4364077b2faaa2115202984b30fc420e63553683d72b10203f27 e601d90a3621001b2c2280c20ad9c175629aed35fff702cc7bd93a35e2df644a 3485f40fe241fae2bd8aaeaaefd69636f3e544f3ccf608c03537159ca8038a1a 24d71ad206b934854e070193af36c45f1b6a0d89c75555263701b461ef65cd64 479a42190c53fbd8539294749e341c3fa565ff6d775dead755fa555969c064ec 75eaa1f215b59a7e67519f0576f0f5b5363e3812ec4aac87887ef8af3eca08f3 301fcb958a931f2de7227fab46a74fb37e0e1cc8d8cb25c6c104b59fd4941127 db42b363fd3bc43500c0c7d156a4efc112243dcfbc21fea79de7ba8ee862444d ca2f5522256db614531a59ea137579023f1f4fbfa857d265e40fff88d60a7228 800221df807dc8723fb11625c89983dd545ae261156dbee6dd84ac54608d3760 466d5f27a4b539329d1d436d9303d9a02eec29141fee0cca5fc08c1bfb9d4a37 19c98ff319f573b0eae7e55a5dbffdae4e34fd86989212caf1690d394ca6c176 96729ee7b6ec853784fe998ba8af0037bac4cb2c896657c515c66bc12f8c5db4 e7e6eedd7af87207ec4789a22767118f3bdac5eef65ff999edbc4c2c159827e0 24beaf66cfa5e6f769e10576a357dafbfdb101f07ab79bdd3c540be5eccefd84 c1b45887f732f23f410576d8c3412bd8aec3851b07e17448136828501f96059a 2f54a93c64faa49aaf7b132d431ae860bea49bb0e6471ca0d495c2877d6b8a51 ae4a9e8a6b3a46164d3bf08cf0fcf2e3ace97a33c6b94a1e671ac7100dc37903 a7e65dbd7f56bf3c2eaa209bd5ddabd22ea70f0adf660358726afd6ec6a4de73 f20ffcb0f62ce183d1688a3a319162395d6f9e9919d5790d40d86efe68bf6d28 a6c77faefdea5e31415f43d389aed6648ff9534eef5a8aa35b59c0ee2518ce64 d51f6ef076069c7e7a8b90ae35c88376fb7857f31e25f6c627860fab605ca25e 7e1b1938c73af520623f41e9974f3510dc1979c82f76acf1ab7da210da9d6958 113125dc4ee59ff0ca583eb4fd9bbaf13c46a433155ed4f716e2c01338cb83cf 76ca3a7a10c009cbee4ce61a52639c8431af6c964d63fb8911be409965f5cba3 80a7660a97e6ccef09c88a0d4e03d704d9e443fedf6613e7a3ea53b58c199d49 c6361a5fb862b971b8416cace07fb06b2e1b829581a868108adf84561cbfb41c 1a72bea096b6482a79b2e197b48ba2d2a0584672859c4260790654bbed4702e5 6b22d30b1fa9682fcd49e14043bb20fbc93dc65f5c36f11c53193fa8b9ed7bde 6862e1bb4122974cb319ccabdf6cfa3f61851b08b6a6c0540202d45577c042ca e9d8fc3a760e3f1e4b5117435ef856ff3bc7bd30c213484042136289026a103c 7c27dbde0147fd853fcaf93828aa6896556654bc3a429b6dc554327c9db2736b 81dd794ee6c6a0882bdbe7ec8b5891d89c9c743aafede0a8017e82c7612be3dc 7aef67edb22fe3389ed0adbada34558b2eeada570099cf18df0a3e1c38ec45fc c7296ea02277bd4bcc8812da0dd9911e8c147be474200877e8af7a26b6b39124 46021b1f6a29a220ef58d19479707e33731e2605ccd5b314163e34b52ef7633d 9976441a254ae64b01eda9cc78b34fc2d9d2cf200856a4daf5c172a6f44833fe ff0600beb34fe65faeda082cac5883c555478a165de9f747668fd46f24b8087f f78440e427e3879dd9564180de16b2cce1a84ee16c852da8233a7e237b2177fc 06761146d781e6e57c34d0086f2e2576f8fc66c978820e35c1a4071be49a4398 bb303b13c26d0b8a888a703c13b0761e3744dba9c7448f9f1fda464eb44e35bb 2c8dd73102a81ad30b94588209a3be037576d84432c2bcddb0c8d2e6e153cd10 8c3a31f1ff41469c0042f1647b0d0920be46c7570093074f4846a20d30711bd7 f5e28c0ad04e1f8933b755ca9b73b1c36e0aca91e20c683035431fd5a312ca48 03e7e568614e8687c8da15a27125f607965c899e2c911b4cf297aa43519f7822 0c4400196870440831a7208eda2f89e2b82a34d448acda70d97bd75ff00ebc8b 9c7d0f2013011a46ea0b8e31679d6f073e1215702fb18ca98a7992f6da4fe32e 814f5bc689779aaf9598329897a1754de09dd6f95eaadfd2f18a60d146a85730 382437ffd91156787af3e87bee9a0bfd566c76d0c4f3aa86e5a13bb88945330e 053b3660b51c7a60fb070a601b683d7dcfb52e509af6d9c1ad2932c6e13c3ae0 d8e79b033baa8a56b74da8512f4d924c229fad006df931c0db889586a0c14fea 1ef65413502ae0922310c9c83f2e0d763416f582f988f8a8d5458601672e0091 46e512e1dbf36426d842090bd1a31f1c13a37a5c87ea7accea6c1611f4f2c142 d9e9227cf60e1bddb9cc3e1d93e47de39ce58e5a091898372dcab99b170c8260 57b6b0e3b47227556385c9fb9ea18e33fd1d9d6cea68f8e18d9073ba4bd04374 c2eaa094d85bc3dce80d2947a3e99cb220256b740dc3399c0440f8820850c1f8 5b154b9dc2fa72ba5f204b13db2e6701564400d401cde0d2c5035f553291c63d ba9e7c459ec7a5e763212d11ad40a62afefc939c8c665a373a10318afe359524 ad98836e301e250ab87e5a162a78f50ef82083fb70691c83b3f323471a85ab74 ac4df8b39ca25049a01481962b2bdf0bd71511cba679301c5b522ebeabea1555 36175d5275a4bee167ea497803b0c0d84225373b48e30b325d578eb514211ee7 e37da5b307c3e526c4ad1995dc53d39cbeb9ff000e0342410d3cbffb5509dca1 32aa95bfd0cee6dbc1bbb1c7d3d6ad0654badcfcb34d1b65442d256051482356 89913de4df780d77bf119ea80609d9681ab199ec33c03c19334f162e29dd89e9 b8d3732ab6f3958945828439bc13081189bda486d97c60960c0f4243036cfda5 adbbb2c4b6edbf8b1f1af72762d16db949838f5c0395d76c69996c5ef3fd7565 ba9106b2c8aecd6724fa4a9566d2feb5ac7de2b0005c0fc8640e0ad3dc7ae6cc db3f5659b022f0967d2bd78617e037aa1f279700752da63909a9ee73959f7bc2 95addf02d4d23be56096567081830dce8d676dd91f655fe2343fb2cd5bfe1912 ba50f14e0aa71e7d73007986f8de64fc185430c5d8eaac76406dee295f44a8029823c4d794ec5c01ff0c07326bc273d9f0968293890c0df13bc4f9c643257a080375329fcb1f988b7a8fdeb1878381cd5c519abe0ac4cd375b0dd73a1f54570185316a5570896dd529d78f3df509d023deb4b788cf89f0935af16cdf7edfd50cd2a4ffabbc8f65bb6ebb553851a4d3a0662c1d662082b06f48c79a29dd9408000144708e805734a8f5c003790b0d6420568afc60badf6862ebbe56a14a44a8085fe2515ce152525966f96aa27d4d369037a852b77e56300c950fa0d42000e50328d4e637dce1ce0371f2fd42c9df3de8de1b67d2a170e27d79ffd7854b370f08abe73e8c1aaae003cf1b05630f312390a1ee1255b963105dcf85fb2a3a1e80009e9d7ca9f3c8a7f1a2e0730c3b43d4da3652ed5817b2b15992550d59bd4e68022419873c1c1facc3c32ef9398479009a389895f9222102fdeeef8a3eaa93bb07a5a477d8de7bf027f03cc09ac0eb7b0f2f6a87ed6037e358d16a7f260e90920364fa9c3a14d19aa29f6663fba2a0ab58b9cf1dd15ca3001bd81d8ecda83d850e74a2cfbc3ca51dd7252593b2ef69769f1f5db0de0b61459c7bbd9cebf644140b674953ddde0ca1ff570ffb0a56a40904101823e13e94d50ffa3c4f89aaed3209e9650043cfdb934ec575e6aba451ae576d102b5a3b13b809c87f7b2d43a80e06a52f4408e889bf032bb2315fe9f567eae386f548240628c1444368c66b6df10de3176894b849b1b3ba380a4a926c9c26530f8da56d96bd20c2d34b3e9bdde501bf976dc9a7ba0c761baf13948a84c2152192f18f5fb76745cb06177e5653340ad2c06c130ef7fc5c41874d24f5c7d120e9f415a7c44941093ef2c6457a47de0e892d863a00cc4d7db127a99650d07b67b8912b299bba653ba4760637438901037c0ade800e4bc1feaad34956bc57bf1ae6836559edb316bce7dcaecd9a19fb072504ca37793d5353ba27d330221d34a9037c28e4c7fc14182418f1f9573ac60242a9986873d9ee1ecc4515744f26f76a0e6c823b72162084926b181e629665022fc28ea7d6f1f1826a6961ffb8015df0ab1529a6804fc845ecc3762195d9a007bedb47655a3028d71105f63cb9f8fc0152bbc48b383737dfa7bf07030b5d1c096c046877832a6c90946a613b58701becfe86ebce6af6c9275b760e35668f480dc0dff4d43862650fdc83bf80bc7082c2efeb0811ace777a05640c03cad97950dd54eebff397484bc69c6bd37dbe8fdac4149bcd21cb9f34d06a43bcafb7ccd0007278cb4264b67ff450230208642e58a3d27b12e0640e3959cdd7fbbeecc6a0fe996af4defe36735ba61d490c7613f9ab66d31e0a645840e34a040df77dd5c090b7e8dd771a15a6c5c29bd0629bb2594f94749923c20e43acedceb2cc423e50205e44eda51d3f833b072e5f49982e7e6450490118d3d641235ef4e45ec574d06e5f2b9500218f5a622c04708e255dbd6511c1c6b35c884116a6eb88c917fb4026b8a3e83b02e82863a6422f58e51e5254722206c6d0d5fb8f73a93eac439c80a0abb01daf03de69a3c53c3fdf07c9f3916cae58dbb6ba54c6ae87d148bedf409aab9244d3d68b22c682117e1d703f6e14eb1a116ac7176cf84812708b92ea5006578a337232d7b65cd2af37a3b9f3f26742ed325d3313a9c56d98069fddd1001dd7d2424255ce341e70c1c463170929a8ad4cc93ddf3c219f4f90e17fcc5140a796d8cda0ea3b7d09a2c0eb33df26a419bd419f420a4447d7d9e83ad8784ac070dcefeeef60bfa0602c90b8106c3d9275f857fc3b6226d44307196d4d70c9805c4fdf326b3bb840d09792c43566dff2c3da8b28ed1110baf35c89086306ae8063f2bbf049a012644fbb0e54d1af0a4e3fd04e207f93f924036d4706d1f730b0c648b4f76d3cb9d26c31f9ddb190fb337bf8f6ff256ff1f2211576221a18eec0cc027df43df721450aa12bdd502bfb4ed112f20699d22aec93b803dec37d81c0ecb8030091d0a389d7500c364146b7de11b8345f0939aa9e91ad2877f3c797706e1174d58ff2cc4b4c296cbb9bca2cfa60b22f1e8f2b5b06e9b8df646361fc405a83477b9f728bf08702b0d5d5d965d988dfe1ccdca9238a5443b2ca23117c509a3c0fbe4a607da2d9ae6c2ea2a37c358fee4bf7e564874e076bed657ed0d44054785500bf6d6db6dd0eb222ef651cb0726cc3c0fcc840a99b19daddc6e1ba60a025d918bcfeb6373bac2965046e36c59cb385ca7cbc93c750f90d3a670dfb4087cec6cc6e647ffddd49fdab1aac64666e62a964cc741d91d83e34036de18f60818dc2db165e3bcdf9cf091dff0e542c4564206ea15f71ee68a9761198327e10a074e0ca0ccbd6e103a847337cf993d0c7067db7c6bcb604c92aac7a32aebfb00ba11da9c849b93e7e5fed0843cdbf3b12c3e116a2192742ed3ce0fb3d88c8a0492bfef1d299a0846400e67dc2e8308a1171946e592cd9207bc119cb9fb99e00fe5e3d2877db9ccc2cf383031be559a8c51b6c5df443b174e2ab897a38e022b0f97573ed995b4bf35c50c90478e33689221eee7baf79782df513651d9682eb9052ded53c071483ec3608edb4372efe24c53279fbc55a0294ecf7703692cd6fe053b19af8db7a3927c6b4eafbd28f457204b825ba1d8f9cc932fe846c13743c30c387f2409171f729090e4737098116e7ae6db9dad9abe730152176ff6349e08004a9e853549832a4be98596ea5926b65615f7b035a61d02c5072333887266810733c99386720c559eefafc2b9eb016845244ebb862463582260bad7ae1aba63031113693169d18fa6db29bf4b4378607c626b693b37b1179587f5dc8395cdc1021e7b826871ed3b6667423c816ff66d634e4a7f20f43a2f55cd7bd6ef18adce0a57148730f1d5bd92a60bac61446b46170f82196e35e15f69468d29a711e6080a98b7cebfccd31121c8223deef5d4e2d2466f0d9679e5247e832d0168b3137301b66caea97b1bdb3c8cdae5c42a339442fcf0123dd8acb5c53eec1c1ca42bb20b9eefff4155ddfcdaa632082f97c614533e553279e6508982e541801bbf26b507b38c9e9d87266095d1065412bf136a93a881f3e22c6eb6c53d559c364010d20a14ab9c356d3e15fc76d2eb4cb306342e482dbbb6311555248c5f182df8b4eb08453d3fc82db9f9240d746727b1d063ec71eeb350d407cac9cf5b28f9ad336d0e4ec39b4321ec37f88c490d2d2ea7f2b64dc86e28ceae565e77d4e7cb64e0cc011e1ca442475f9cb446de9058d823ef26afc26858d5dd0744f2b74354689b190eca9c62aabf019ebdbd0f35b731c2f5cdfbd00df464ff7b626f1910905cd7ef03acfdace3c317ca7013a8fa45e5daa3c1516052111d9558845f3f940ea91764030822f1da512c7f38f675c27e3c37639f042d5c2702e7a7a2022e6e5f705ac10945ed1755e24960d75867e349983a69bfd7cf4825ac4f40be126ebdfc770e3f07bee7ca5a7ed27bb55c8a380e0e2e88c10da2d8ac5a2e559f54c3c08f890e6700c4b9eeb3be25568d46b91fe0015a81c7043ac41e6018ac280cd97bcd4e45100de381e9c95cf70b2c21fbb2061e69673c1c3194cebc1895f84a8832955bd72408f5fc22719fa7a8520f436e6da1861c2e54cd7db886e3005b788a4c7d5cff9b0ed2d55e4ec14f96ddf358454bf839d7cf2b3f914f1d941ab8a9e45bb7bd3c1f0080ba7a43d39f73645c731ae22ff9c42bb3d36f5fc0170016702c19cc3ad83d0de3885987d05983b69c1e7356174b86f8a8f2cd1e2efdec2642949a6255b40109af19d28d9729d303223799f83e5463559ea361582062b14c86c3485c8df9000a0127f82ac18558f9fe6a06fa5670b1c1404b6fc88ebe2db458c1b08967653104eaf7e1e61e0bb512c59edfaa2549c5e086b8023c00a590cd6312f0d06e8e7e012038460479583428afe567e2635096fa6d0fbf1e983ed47a18569985dd67a9042b1447de91af317a594f8a5120a82515eb88283cacaf970c768ec6b0a35580074a9ded46efc052d9e1e1040a040b4ad00a3d1a7f49ddba81e89a709b52e9400a4f3e5456b8b09206da9be929cfd5af18a6a45ede5b27f9b1580be0f85b6bae097af0389b5ac726c85b2d4bb034e4b9b3388f1517cc69834a020fcf1d24bf2e0177448ea9b6e937b0d67c3a4df86b4106b6144f4b1abc9144d338a26942a46200cef9bb4923ff5a33a6ae1a5fb64bf08930da136958c0ed359ed748fc91267e072b44d8e063564e8f4c7ed54a3e4bdeae04aafba30239c43fafc7d63db797b30ba7db2dff1edfbf2b022c0157ce03bc011a4c0209e15ac17f475cd043f5113a070c084b2b6ca41505e68b98bba61f8b486f75d7aad1d30000362ffd3355a22006eb10b5d4c91b1210e9f70b68fa3e190c89dda8f3aec183255a41bd0efdb8a20ec0ddf82465a13b9833e59ed71b7b9c1832450c741b58f5069c5b6f608d7218076467bfad7411f22ccee664a2edf7e28e5c58bee9d6c7dc43b66310ea933caa05c530ddc262caf4dc675518a764b606a7a57c89a71e936da50417da361aa6fa065ef4107a2c65f782f20b898e6cff34ee8420ce9c21e7a1a7c9b6d306390e13013b1a78ca86dbf90dec9afd525e4b3d0284693e4843e9f3e642cbea141d6b4509063fa4f44fd96922c1ff3f7b7e05734b587e86a34568945a1a0981769c12f40b483f77e95f90653d9ba3cf7678150e4f10cc5b6d59a27a4ca25640fc9eed0f0d051788cb22b2a4d7f54fdd2dad7dc57a3965045977f82840ebce26b06f7ba30262c8698c7a43a1b63f670c6e957fb7962332b531b0563f66769305677808950f1de91c064476f24403b9b7598ebe258c00d7ef7a97ad8fb4bb23424d25504d08e111638f87eebe52fccd3514897d0ee2bbb5371bac70901ecf3a2e758caa0d099057ed0a700febbbf93e051584765f57aebaea9497163336687b64adba57180e6e03580bd4bef77b66d67a8e042240820c87ffdd735cc142251b1dc7825932068682f8381aa45b487551e6cd616238a073f48b151190a1a5b0108dfc26fc680c531c0ce427e8347b6b3a1f652093c831840abdb9bf4118c51e83314acb72a3085bc963f5e844aadd5c6e611538c97f6b9e3df5f211c7e379b2ef6a54669d270c88c659e46c1198e852461a98ceed6154bab31252963d64bd40e560dad280f001f0ac5c133edf6e36c6a4d114feac034831b6c40181f832e4202ef2855a579a0732ae112b20eed40c739369ba6b40a46d3c3fb01db8d37b1540e5d952a18fd60594848b3de7418a01ff6e9b8d9ea3f1ab5cbe915b5ada170d7cd269a8866c57083940ade433ab92c1dff15e1a5a14ed404b4c27303afba634aaa6bd11d8c08d0b1a494c542b55e1b544abab5f78542a9bb4dfa9a9dc9218fe03a96167ecf3d20c4266b6ed8b78d2c809dc5d370547d5b20f343a3c26c15d2dbeba190332cf7606b656f27d866ee66de7688add7260b663d38a9ac8bf8052fa884822d6f71b0108177cc5eebc889f033f4d6b71b6cef1515e01f4e1767f1467ee393e3acfcdd70201d995201ec0fd2181146c15020c12c37845ef70fa0b884f3625a76d7ee2a6073a8e19981578c8d224bbef57f47be2809c8843314a770afff750380375eb5f074169e3c8e0dc0eb61613f7979b31088061b5f959a33adc97d92c3c413405910107dc211af8e764398fdd56ffd0c77013674b8eea567dd5139c98ab2d16f5e8032fb19740b05759e0576a3633e80dc0049b34e58422be9d6dc4c2662035929b00d03e82106cbcc391b4deb681fa571aa1474eafeb751a43315ed0405e1b30b40e0384f91d38e8884dfd5f41aa9b9ce008936928ad720c0c6c8ebbc596cec953018464c58d5faa50296289b9bb7e946f7ddeee3b61c090c3574fdc2be9ed2d270a759b9865412048e5a5eca9e362372d6ddd1843294f58929a1aef97be8e910f0a7e00ad65db145fb533a6f0b2358cec2e9991803994c63cc965f35108e2ec0e0fe8f59927c8dccb37702e205575f7e558c05b1f7a2dba372c576202f10dc72704408ff4f0f45d35be56ff28a213817b64e31729623b6046b57c2b25e4f5c4ba0eac1def540de4812864f2ecd2dbb6d90ad99adcdaee4a9b7663bbbdc1f65aa70ae2380e3861ca167da4707c14e5c397462f198a777e2adb6d883ffc63cfaa40091cd1c0ef09c963e1795a8838257c99625df8972dc4256d67460bc4c305d2570d017dfcca5e0a85942e1e9dccd259cf3efae28825ae094495ac0b8ec28b3b0a070e96e0a185b596dd7cbe76317baa45541c664d987b96bb78d8feb738986c1b0bc9e2a440f5bf1c87868d686f46cc6606b0aa04b9343ec272fcbff6d0fcf258035dd3ea959c7f0ccc48eb6bf8f0235f006acfec9b8d55dd1293f01e778f11430527997d1c57e2dbbc357cb151c8d224c5a077c34f2bd3482e6b7e9723d1ce3107e33f2efb99258b36e46fbd368f9697b52a848435594d82b4a1bc01a807942307d446517ae0878f48322905f36448b83aa85e700384be45a5cbb32e4c5b9f2d0f36e9115782ce89932f60bed4c1686fd8f08bb08b5b776405d5da0a66b6f345042e058f48e177a53fa0c37583140f1dd39129eeb5a487cee140091f184a4d9901c760cee1e82b4f35a2fbcd03c1bb036fd7cb46e8a7f35dae5163c99051150d06edc9d5ac2a372517483a30f27bf0419cc76532707079c82d87ac87cdf8952107c39e6142614a05d01cb3f27400645d47e5e2be7787122aa64cf4b04deb35cc0b17bb08447c4be9b8656c079a7166cee3943a841882254b001d09b3fbec938a0cf6b0a39e77fa303c1b61e90534691d06fc292d8865368d617ff9c3d619a25d00c61461010aa9a71962065b2af0be754d9ae755224864a874994eec71e764730a454d886fc6cbe569d8ec2bd6148995d23c84ba44ef5325a64d3db0d5b7f3460ddbaa14de4524d08018a8a44b61ee1d4ee7cf94664e33dc209887e97052b20f00bcc825ee964c1d3d1ca569f0b298582c668061d53810ba12efe873c2cf30100726a63733f7cf1c5bad33111c54e060e22c8b1b6df9a31c70cdc7080f3a16a70debbd204199fc72272ee3daded1bd0c9f46096cffb95b103ba5f47a0710eed10c1ee2b045faee114bf48d70acae48f268fcd87286e970e865c31a9e84f785ce0220cb361f8153924a1ea7d6ffe94d0da8111514a66ef8b34592f58935a32cd50e1008894f6e495883b847b55376fd4ffeb1dd2d9f5696576014c1a4ca18adfb0b54177ead24d3cf24bd009e9fd90b39f4913da42fe4889bd19e791dd447aa8107897380d99628f63476c5fe22929221e829c60832a39e57671a66dc2d8a120e055c134d09963a9954f9e62ebccca150827254456687db46341b220a8ce4c6e30107b8e50e8a850eee2d94f06f6c8b66f8fd1c174915c29077c9b5e5b351dfac0f8b3ee957b5ce4c595c93a7a213b5bb49dc4a36570e0c7a77e34aac5ec2d0260394c173bb9de69e1adb16fce39de98260e8b041c51341cf50000dd776f571bb07039ff73236a4034f6229f8441c5d868a62b7132e0237ed901282083160c27c08d7c2accf8558a59a7795435900a9ac8499e05c2aa555703075e54b666203af0d33e9f6ff777a91cc59333a09dee850a23bc342884163cfb0a4a6e1e3450dc202256b56721376955a757921361a337330c6e2652d597729f16408c01f81298e011c3835c4316275839fef2e488ab7fb0e5ec7ecf95e4e39a4db8cdad7d88a0c03a364a3562dd6aac10d012abbd4913c3491350a0ed9fcb75f888a3c71435a1a00accab9f276df5b11e671bd827f0e819a322c6e75ba7ad211d47ff980f22a380d8693a9541ea84f26e85f86bb6d3cdbf220082ca3e5a6c992b3887e4cd361660d307cba135f6148cfa02d965a32c7ec4b5655a607d86f69816d70668857d15406223c59521be245f5ab6a2e48320c461da697f8c209426a45726318acf9ac1d09ae8f7ea7ac2a80a95d852edc07c6467aaeecbd51b73d1dec07fcbac54177ce04338965f471c42eaf682f8e29b13cd0987ae169903574102380578c8089a0700814ede9baaad3ec55aadc4aaa937448b26abe8d13fb1674f11c94c611ee41f50d7f7df4e6de5d5cff8aa683cdb44a271250f8be05ae4dd4a3a033d4511a566e01931d241ebadd90e692b7220e79c771fa850e503d2f0ff811cf3fa8922e675603b918211e44f9814d34215f0963c3330dde2c34a8739c9ded9697c3c51ba8c00463db986bf75b4272ff9b4315b81cab2937bf3888267565360933cec8e9727a04bcb0a3634d080006f568e4a5c71a0cdcf0e71b93a6af8aa3c98912b6b4b8e0089a37109164d08e770af10706dcaa28b3e1c477e4800d2f8a890612085f662b0537ecfe398d2276c35419c4a6b03564d9b18aa7860def3d42319851548f64470a0e81f87d08416aa635cf2985d2ae5a5b9b81f5e096d8a4069d99f1103a877a09effe019e8d88eb7e1a35ef33f46926ff720d1b589a03379f52adbeab9fb0f202512a1b8aafc52b7be8229765010b252e5c6b5568e1511f4599046f413ab7d30981d825ea3f7eeea327db44cad0324152783a106dfadf61333668f0e0962a7c09a97a0e239418212287b7454dccd5700c788b5b6104e8848a5a1b711f106e200cc6a8bd4cd91fee5752f36b14450b04f4a5ab2e58b7c0b70daa4af161e37c3a0eab63ae846d74e74edea307dbc9ce9bced3d4c0b829c70f75a92e743807059200d6f7de8991dee73ae6e382809d68e720a3e73a4ffd6ad2136f0c6754012de803ecfecd814ab7dbf4a323c78e968324abed46f56847d17b0eae5bd739b48221003e30f1c7ceaaccae3d3b2a5a79df37fa7347bb5a226809a05cb2c6562fa5430529c0510d27b9daeb3bb58e9ecf3910ac3bb80c4f7370aff99ab0578110f07201eb65e741b876a92c99736c99ff4e0ebe19054f2e64d61cf5e55a391f9611ed0a6137d349c4447f393a985e54edb900f26e083a85adca522120c6f60d85b98e077bbf5f9823c03b5c5a7cf5a9fc4163e04b04c02abd839b2a46d68c792c30d506873508961fb8ea66a5ee479f98e5efb4d5ebfdb6376e0900c58f6a2cc56add055f53a754d36c1af685974a565ccfe162a560e025975a81e6cf08ec3c61447e0edf66cbaf2c4071d39d6102196da10dd10eebf656968953a2ba6f1f5e08b92f05dccd582d9600f21a08282875daa646e8d7630a70c7ea46a708851637c3624909e3cacf349f3fc7e369ca0fb231c7d5efda6ddaa57936e98ef74b08c2d2ca3301bf97472cf324651657c41f5c628bfa2f9b3d5bf161f695d60c9f20d23ed96d03ee16a6583b81e1d211e8fa0bac27ce089090d9b62ec1945e731b20afa1790c02b71d585407b32f71a017794bb2c276c3deec525726fa3021aadb23f211bb00023827b14301a897c3206150fb38948691429bd5963392b68f0b9d3abaef28a20a8e33112216be28b821d648dd08f3d94a26115c0a6fa3220b2d43a18037a48b02ba01b80ee4d04c6ce74dd8f3e3a5cd648cad34d2ac3f680471376731d79bae093c4ec5769064abf6b7bfc14c546e08e131c088963a9b853059514d3dde2f87045f97de23b828d32aa20166433b1ff6a47b48247c4d66ca1a5dcf2fa2666b840eb15b3beeaa50b89a09b37be7922d989a704fb5e996835a8fdcacd79e6c94a500feeaccecc2c7af628ddcf0a2a2d3273b8e7131fa5b61508e36bc89428cf9f20cba13dadc590db23eca850ba69aa22a2d3d5a13683a138f9b14bd7ba33e1a37002bf9f698afde4eadc1fb0628ab3436af59c1b720dc7111a3099660258d60fe0975c06254876a598fa58a6690840b7ae401c8ac48f0175475c91837da2cdf8f0e1602aba9d64e550ceb3e7edcf29352ae32726b721a8b8bf0fe9f4d5de0cd0105b3eb41b3cc7470001087ed80adc2f40d7243e64af61046a18455607e3f95180d972c67b11984db4483bb3596aae0f77b9214b208e365340b9ad5bec5ed9b8a0dd1ef838210b0aed0fdccd511ebae68cb45bbc9026b247f4cd3bdc9212372c160a021211f252a3f2452e25e84d39ffb5112c9d051714a08d70a937cf54070a207990e2881c242e7d59421aa3245c6b673e113e64a050b0acb29e5b860c50834072d1c12951053edfc9863a1b8abfd44fef5b4cf8a2c4e8a282027f3444bf5c001baf7a184657634c0f20cdea57d57c3b3e7e9823ac05c7b8572aa825377e84f0cad00f37b174354e44d9b3df12410928a3ba299a35f59e9e05ade0ed4ed4e8505ad425709c27e4274d03d4ad66ef192ecd0fc6a1a1c0d917c20f18399374b6a0c5983dfd815aa11e63c05ffc7a8b06e2bed23d8d31d46cda52316e385c45eb106d45865dbc98bb6e693ca792c87d0771965d31a3be7322884bcd78c4ee8220402db9e4c55d0e7e2324717f72c4d5ede0a2cb3e90b9ae21c7d1b2f7a17f5323303a28486c3594976ea91b18bc07f346f994bcac6265035cff9a4723d14e054a5054c15c1c402ee113eeb7264ad89a70d47fe624588d3279ab41fde95c4aa470e0d65276e94030b17eaea054e905b70a74fc5ef4f0d8c4a3fd550348267b1bf2f0579225acba4a4b310638d5dabdec063b010661d85ecab00fe75ad5e15a9f6280a39da6a5715c991794b4bc83f75fdce78146de9fe5df78c0458b32e09a2cb0d0bd019b75d1454f959a0389e978a52c9a06b4c769d9a9df426815c2931431f3a002340291ed97021c0c5b0e0c01cbd9cbb6b4b80c35f9e32892d76ab709b147f073462da7ddee6a7cdb9fa1da6050a2140983a89cb9b0d45a773c11128ccf8b40e9a131130c3ad93f12e5e1c2227892f809b8a39cbb0d0157163cb0292839c7d0e686fd6704f2946c04ee9f8d82a1b2169c0bea812ddcba64fd6d525b01b4aef007bf3e8c49c5325f824824aa50a8d0c458ffa91f54c9670cc37edd68f4785b107c61821c622d9887b98bec6263c6bf9e0582ef8a3b82142c80b3e3aa259f7450a77509c5dbf79ec13b5c039c042e0281caf0cd15cefda78d62ae59853fedcaa09bd6a0ed73ca8376db8781c0bfb427e7403456281b512a8a7233ab02691fa4a0865e22d817b2c9a1e0d749caef95e2e992a12d3f3818338dcf64f4c69f7c40a0056889b2d3fbacc438a206913c0d57f469bf1ed11f691f723081332005c0f4b06e01506a2bfca18a549f58eb8d126bb3b8822b83cb783449e5e0c161c37ca6109077e5e19d5f65c8e76111a0f959416e42cb9a86bf43c1e791f39dafdbeff1b0086e809c5f3bae25e710a38f931b703c616f4781356514c2d56e382808c9f7d077b7ea3e64470f2cac6e8536f6dab0922f329db7476f04020ae09008a57173b0a164d1c28d7c90f0136fbfc98ea3f4eaa44cb75d754a28f041625e34deeaef60aeb5452e48b67fbc5844d5a091c84c892544e7562a231c8b788e0001984aec1036bb15abc980fab368717419850d7b51cc98cf9e5512c057ebc5706d74d7101088dadf76c1fcd4b17bc621e58b441e485534be811759a08700a5c1a4491d1a00ade92643d7b278be7cf50268553ea0b6d485461184f775fda4fa779c3d3be460474bf3658a7382991ac1c0a7216fab5223197a97275da6177fc1c700adc7cc20a1ea5b1c77e9b125488caaa5f62bcce9d5c34f3d93afdba720f99e8b8ba8fb40c66d1bde3754830f5ab0d29372096a7f5c262f9e6c8b07b19c1bc322e49b2690c36da8b377e661e5b7c406154cded6696ac22bd6ee18b8885b9f1bbfc56f7ed043ebab69534ae2031bb87b57b8a3d261a221f8bf48ea2fee0cb7176ad44557005e3cbb0c6ce5c1481d718a036f5e45cdf80afab71eb71a0a0a235c73a8cf262082d459857131fa39ea835d023374a96b7b6d04cb4086a1e59e6f8473018f7c8090048d89182f820af2da2fc3ca0724926e9c9383a9a1f77eb80096559cd4beb0ad84dec550cd3c0a1436ce6e9824a46f0e79fb21a792752f19a344ecf843f4101a46b17bb938f7faa6fcaf0d572a6da8ca38aa809248dc5aec094da50ee1f3408cdf62b5aefcdde7ccaf3d01fdd1ae8807a1572bc9fdb265252a33284884ff1079184e0f5b56ffc2e1b5d48bf607edd1485b6a0f57a5eee1abae73509e9058105ef993d85c948fbab5859f7f58014e0089d956201d39b5b28b6c575ff82bb9106d8013eccb661e998202e461de0b07edb5f789318e6980f4d81759bbb5209ab0ce7c2f3ed01bafee70964a6b366e38cdd3f5b028289fd45aa03fe9c6bdd27de068744e512db47402d8c7c1642af0c3a24fe741cbed8798d3997ce483b54490504b8328a0784357832644b7b770f135c284998f4b0f1b131f06769d28c026c6f085c0ea2f5e0d0e47460bfd3840ae0611c9bee34bea509a60e4f33de37bdbc480170662644dc3bf4e7f9cc2897640cebc84a2e15948c5c5a0d57242366c8d8360030fa602c7cfdd08d66a1a2e8d44c6dda229037276b68a5f76f4f7d5a8b1e94083a240f4f93b74edcf45ed1e6dc45f82ec543cea576137f3f13646b0fb9fd0505993e40c974325bb7a3548cae0807cab350ff85a48872eaab3a33ce0dd03aba0d487e7b50228c377441109509c50cc675bd2f9c394810ab113babda74044bdb0d1ad161e0c9a9f89f4a45e61aba994202f1ef0991a851873b20df569c2e9c6f0f4a838560a16f40dd384188797658bd70ffd230b2e0ae12568c3ab3e5dcbcd20a11b98db4099864e41218b8e953048534c7e4c7aed1121b042240aad2acefdb0ea743a2ca406f909b47e392ac62c9d420ae21d72102f3cec4c77490b6bf5ab0045dcdc6eac4c20359465949f37b39c9d4f8f814605576c75cd7ff52bfa4538901a7cad54ab3c36c0d6ff20ec3996f937be8acd49784be2373919523532821f90166f3ba8b44bc3a909e9ea62d54e32dc1e0310d72d92a096175de1ba5ab6a7803111c9f5a9c0d74737f69926e5c94d22bc6848e206578e32b55049847bea0ac0961446a5e814d0718dde1af6b4bcb41a62be830aa436cf254a6d8e32b6f1a2f01814734de7bc52468e637a0bf756e241f90f980820b84b84eb0328bfea4c8ed07879e06e34557d34b4dae922c4ef0e5487b0b2d4ca418ad608c8169216f4f3e02b2c6d5de01416befe03e67dfa2f5ff24a814368fa1fff34cf1b9d85c9443f60b0d6cc81eea64d1ce4ed0ae67c206e7fa2962b512c4edd715bd5340673c21f90386bfba1e218eb15b22b590b9a9a05b3b1dda6671672511c498f3b5eafc34ab05128e0440ac8daa1e60e6d93c5cc249571cf7547104e71dd1095d9e318b5f930206d67ed8038400f4e51fe048ec86b95eab7550c1d2dcd884b10a58d7472ef505a384d65a3d02d64d57d04fa801f2eee865257d4b0a8481611668551ef7471f0a1fdff5d5f2033ff713a337c3452ce27d089bce90fc7d0703c7088f0a93110808819786962f2a59484f11fcd19125aaf48b8f9f24fc41da697aad0dc62b073e013cd11514979edd3f8b2a517214c3b229009da8a22e7df02b3ee0f16ba430090197c82e9ee42c32a95bea255eca0bcee2a869922d4d2dbe3a5ca56b892822bf0520cc608e23e8f6f1ce5ffbce6fc355fad276cdfdf980fe80ffd4b5f4ddc5950d3cc10ebba0ba8b4b7da3549e708363d910feabb145f6058aa5e265dceb61080659c3f496ae6e8ab4ab03dc308055baacdf0d64f8cc0b69ff9af3d490fc87e500a6f3b9194878f0928d95c28ad21e80e3c35c472cf0a276828122a412589d860100b5c2d09dfa24e990e0be6b0475fcf657f3ec439151324216fdbd0b4dcbb10ccdecb983476d4277fd50ad8c1440b5a9380c801a708daec602d35d2a47e2000291bfe4ee88712297b5c73ad1ba7794479d8617cf2252384ccc4bea0a7c863e05ab33517080a7d0a55c4180204ccf8882d77fd23441347c5d73cce792cf7409042337a942af0ecbb94d615c26fb600dc3f25a926bf3e51a250bc35b3119a488017cdd05d5d8c93e6cb92acb1bac9003b49d680597f6d17e6e7b611a843f7d2c048b51c68301e01021785a88fa45ddc399c69cc124bc918f1d06ad639302d2cf07ed1aa21fef74bfb4e6683ad86cf8294aeb0200a6cec60327442d763308f9230fa453600e22c13a672b5157329b48e35b9db620b64850cc3fda9d36f02a397b0a4baf2e5280852e044f32ceaa38ba49da08003aeff304a4ce8c64a335dcb3fb061ccd858a008a0764202a87387e456e24fcb03a0af747ec9746477241c346840ccb423245d0b0bd327710ee45d628e252e26439bd2e24c24349859f26f37ad3053b1cc0f155fa14bc61608381e4e1abcacf26bc6dbe0bfe0b9d0771d35a9947038e0a1f3e5cf12c3392c7bf780af5180b98d9da4a560f3963a0729a6a21d6cc02d0c1525a2497edcda6746e786147c2223c6e3c9879e2842e9a4554c039c76307418a9b32278a79af10bad4e734ccb2cbac74fe4fe286c057a03bc200e357df002d27d62ee2b713572c552fe5736f49919cd62c9bdb062c7e57863880e4162f0a1de08279e1cf8ec1661d7bf94ef66e0d5155794806ae29f891a3eece4a52e607d5c0154c1a3f1506b91501053c05541d7eefac37526e3f353edf8d36c426a9006846a4ba92299d00bfa25002f13c139ef8918469149f54f14ead6b0acf30dd056944e9420d3375bf08c6c69c331507bb43d6229bc30cc6fa1b66a837ca67e80a654983438bf29ca1455d14da9502683bf8f03ba212e806410a4f228dfe5a9d0e15f161ccd78b304785c55cc89e6d6230cd362caca9a3559b1574a0a82a654904cd897a920cd1d33c93e159179c723a9a8d5c0c91a718ad9669c1037b5eef5005cc45f6df4dbfb26a115dcecb78ecc6bc2ca61c19cbc1750947611ac55bca2107c257ddab2318e3d0cc350da76197ae631421ccfa75d25e1d0646ce921b6ceb034e15d6ff2b8fdb1544d07f486cd1cc39b2262aaab4c6aa5436d51b313e4bc507af9407e05ab26e0b3c13faae3d40317e09466d583fbe8f674de767f1b2f1fe0e3e50629c7fb4805326ce537b6d71e32e70722aa36ec7a2ff8cedc79e51582e08c8710784c73938d9d9615f62f5867bb569695492e329a4fa576e9d26745a6f0d2666ef76e8b7e2a656e4ce34c5b3c22fab847b5d166b89c15d00e3bad460cc0e7a02f27f131057a634c85c7dc2653dcc1a299b332ff2730f7897cda0aa33660ed2edf32a482b1145a6e22d0212b53d4aa7cca5c2b21ed90c0c55136075b67a0676481a3d6b12f39564810570dfbe86470e518b2a7d8417a8e3620bd732dfb30c7bcd9167b11765016638e2e02ece9c7bb6168b79022c84da9bfcb15d8e3daa037e4de49f06066fbb7416714d3081cabd42baa3cc698c6b27b2734dd9b7d2d008adca907e3b7065f382828bef0d839ac2aabcaeff267da87caf3a58b486bb700f9ec021a659320f66df797e54f84633a656e39d924ed5b1845613813795218600a0367c8303827d3fc106c6b9974d982e6c90df39f74473896fe38eb2867604075612aa8cf5f006c380d307b1972421260e935de4ce1e0e7b488d8a94a8ab930781a2fca3e9d2a55442b1f035d2b42076a7e4cfcb95ea3c8fe53b2229cd48f3032ca820600eefd4946fa1563b116ff32f79bd42bd6c34aa4f716f42d3888e470f3842cc46400b2ef5bceb25a167b9d0d382a2d5fe1b15cdf736ad1bd51dcf1b01fe7dfd602a7b934cd76d6003b0882d3035c2070b59fed84e465a271f265c2409b8d7a666abb0ce99f518e2e62d1f73ac157e534d54e4a6cd730d90082e3cf901dfaab142dfa3704924eda879ec64bfccce90453d3115767b092051d9e52e8c0f828ac2f5774786c47150e04fda55a207d69ce3804db2e8638358f36770f67d05513d2baf2282b6db4aeb326f2f583dc2663cf8b6c650da1032e222b0bc49540e8a2570354f7843895bf105059e7e7c92824277d3471fbd9a64adafa01da19e0483971f7fde7d25685d5238509335ed372b15d49f7c380ca1e327b460558b8305a3ffa785de96b66af7e1649fc81a07f68504ea407cb65c5e2b0fd3d5b40db008e96b8823570f58a2c634a99efbcbeca3e1cca90c5843370e37bad0fcc8bd99015e92519b6c0d44d5107e2c17e19ed0dc3ea3619eb392b92231565e480784440364d9a8619133cf0376fb4ba786d53407f5f8c8e90351d19d3ecedd0d9209c60ffaa30e3a43163aeef03738f1113f1e4b1645f9316a79d4b89d7c40c45fddfe0d62bdbfaa3992e68f9dbb48eaf582dbd9e24ef83aea627d6afa31efe31105950b8ad408533c3b675d98441f4dbc87d05ccd3fc58afecf670d09be385e7589d70c02f4f819922093cf282e9bf8004cf1e5bd3a3d3093fde17711a6ba4184d2b3060d87d8dcc3b11838570e4e6d68906c444bbd9bbe36b47304d0c38238ee9d910a4051ccacdeb9fb0b1ef4a05f7676d99cff61cf902a1f2f580e6cf7a4946cd204a124c573f04646d733b35b1dcba884cd7b636e5e7a4b9105f7dfbbd552c722068f8c0c8ba2111205c4a69fdd8c875d7a6c8055f84bdfc014ffa59c396493a90622ab6bfadbe34d3155ea9b526a2e88607f9391a9752bbdb42f2181c173fcea0e55e786624ede2c0de3321d8f4b329d228256fc5aa1cc46725592e53b165ce8093c7e41529b60c46095734b5af6c26a9d9b841ad6ebcbab8568df57208488cc0833a2c910992d246e14023b9d528997214d3df1b530dda1a16fffe5995422fa0a8b8542bcdbf3941bccfa0aca746d64156ac28574c4cdf5859e3a7f78b346a2049956c9984f08d7b0fdd8271068b50eee8d294091cc8e470d57149ec0ffa98906eed26cc48e733d227f36af3e173aa84f71b75f31b7705a69b5531966bc74a204a9f6d3a9dd885b01a5b98eb4474329d7bafaba4431488b339ffab513aff3b80783840b311b7f40ea8abad78fe7bdcc570f16bac1d9b3d91ea43e6da3ef90690996ad1fa2135850adf80f8bee32309dac0c72fe8972f6178f7ef626d3d3ffd0089bcb60eb18917d7a31e45bb8f6df6f4b1bfc3d8b2b7b607fd5b32e38c9e1da0a68c40779fe6822fa9fc95e3e8f20a8f4bcb0f0b0fcd2ea8a24c68f7a9cfd470c false +check_ring_signature a9e94a9bb7eded0243f89cc904a148827057508c5c125cbf5cc5cd3c745901fd 00faac1cfc58f199b29a4722de7a8afbdf9ef91bf7b68f1e9f09145bf49232de 18 44cb26c55025be8bd4b2a59e8a9fff2aaad1a7fe0e9c891153e39a340c909cc4 9e0675e47d78e731acb61dfc9eaf15c437a8afb82be8581e216b44358534f31c 106c1754d6d88acfc17ba5e35496984900943f657329f7b24a634e887160a939 0960393a7bebb310cb69d98300e07c681ae2b84c455c6c9338bf5022a75af3b2 0d4fc50955f08ef1752a5d30155c5a7fe184636f36b887013d5e43fbb8e061b6 c8a57101ba9c562e272cc106d341dde473713520936a50b9cc31cfd672bd507c 4c024b2e27118afb68dcc0366c559ad88a6f479cc68182c19c98bd97ccfe557b 68345156b469f721561af943320cc5a17692bd73c4fc1171b62d2efe5caff2fd 924a7d6c5e8c79126d0b3f7d79f82c35b56c81533caf9ccb81d2f32079f3c473 f9dd2299bdc17224f1747232f94d65ecb75331c01c859743cc75785e858ed942 79ab79ffcf4c142e4d8f9f12ce64efec6a1d1f75bd3dd4b5ce607a2ae0a3ae77 1b21bc4dc741b133ea4404b8ddb94ea709f19de3dce4da1a1a272fb7aad41556 c03000232e1b749c6b99c724737e22a7550b1f7f04789d0c7bb91b3a055cdaef 8763d084a83ba02f6e4e4914076aa2226be1a1779199f862733e17b10083498e 86da18d05b4cd43267fad1181cd15b67bae45f2a68de8b49ecacb34944e94038 acfc590fc70549b44f5553480c9ce01d8b759cb92566db235f583075cef5c59f 7081200cd0b986d7d635e94dedc403934a5de27e5ee7e73a33d4110984a8e288 ac3e4863ccb658f3c837118a1c46e6475e42e7f8388f1df74a5c1f2b2f68ab4b 04ca2e2c9999e854dba40dd233a8a7685c8cfc0e82cb2f5c45b3cc9c0ecbe406aed62cb16f4f61c953658d46f8be37eeebf9cd113415752d00e6bde0d5776704b45665fa8ffd9b6f91aff72628c011903448b3674e9e8bc3649f40f26bb7b801443196d07f75690981bd731714cc7b8aa6418fce282432f490c7341be73e890ade753476ed83c906baf82a3606075e8fb6355223bcd61029a18c4289212feb01b90be1c73e21c887a1322006d90f29f6baddac833301138b186c2d39aba6a3043f48363ae7e7a0af163b3dc9837a6826d0ef170d8a2c045e0c1d047ff106510a308bc87c4c81301e2efc96a6a8d8ab9c3273673588dc113caae5250379d0c5030721e84a428c416631a7f2efeb1c6b69c1d17b9650476b62cfa23ec41d307b0a17cdbeacbe5bcb42a8cba7019c31dd24981d560921d295f9536ca01a61949d01f5b9a92497499521a29e8da1e924dbec7a89a4708a85a37cb2c587023b56a20de103405f3310a25c5dc9591a4ee01362fe6cd0a5b910c87f3ddefea0fc27cf00eecf74fc6f287761a690695cb5375108b925e5a2f05b3db7729564868bb5e006b74b672cd50623fa14484180cc9f2535ecd49a29879eaa981602001e2e505e077f29f7ac059b1f52b211f098e58e5add6f87f4372dbdf54b04437f3205122709ad22af6efc7edd0a26472f283ba2947040a24d39934a7989b8f5c3fdc538f30d6f3303f20d9553ef41e5f391a67161f411866be939b3dd9357c7b64271b4240452a2d8cf519eefaad81892eed96fe73fd8126a9e0dff9e6cff01d9a1ecf53e03acae15214fdb6d5f96de6c9e146653eb80414d670770bca93f1e8f809cc840038f96d1b37fbc1dcae0afef6d504bbe5698ca345b3071a85cbc4cd51c6c88cd090f6de1e0373b89365448b5f25701c9a1f54a77716baed67653352fc9aff0e10ccfb940e631fb2f6087a8b791dd0163f0e6dfc3d8f0342056710d4db77b17d30ccc2dd3e978cf28ad1118e9e47a1333447eda3ec74e4775a57e1f7e54444f40083a643e061e2542926441e64c261a902fa75ac1aa32b64f95efd5f4b5e613700b2b310fd44c0d663e85e49abd0ee633c971bcf917d51122436dd5ebf5cd03350c5802e352eaa64c3edcc5deaf6ffb286848c9d630e12cebeddacdfb6eeba61005302e03cd43d5e509bdf501c2e64f8576f59b4e4e53dd1fdd7e1ca746d7b17a00ba6575e1f5001d6790ca7dff3cf2b80dead8937869042e0fe2d6ade4ba9ea90c14191583d39de12bfd320aafdbe5e7054d6f26db06ff496a16506eca30165f3210dd1ad4c5f860833cc06ea1096e9c295d60f0c8b7c58ffef442c352223de70b7edcf43ae20a9dcfb9cb46afefd5027cc2732fd6c9c0cacce79c42ca88ffb40e8b94ab76ba9dcad00f6a289b0a1925ca5f9030bdb30cb9b76e6934bbe95e0f0b712a0c38360539380086f142a4782e3584c22707e6e82feec521c69434ab3f0eff352f54abf8cf942979275a1af44830b7ca1b927163b481ec13890c832fbc053dd060dd61c03235b1d264b3091ad74010044be991c8fc1893b666aa199c380e7d6e4455a7e119a3f24b342f1c8ec5825e012556dc552b69d66cb30e4127f103 false +check_ring_signature 256def654103737e5c87c210d6d5549193454d54511be7e7c001d20310987b38 d04625d2fc7293f3f7264a22eea9bef537ab1a6ce749f1d28f13e16b1a1640d1 2 5f84f7ed2e97e6370fb86b6fef913df6578754e3b06a06505750be0f57c5714c fe08932725f63a13895debde6fc87053a8f9120031b0bc8ffaffe1f6319a7113 718a9029637b21a91d3f61f58c45033e546914b5310551d1876c3a10ac52c706a61c88eec4938230a1e42dd1b65b45064819a0b9f68d4959d80550cef51cc90eace048dfe9f1b988a851d2ba165d00ce6510b0a46383e7f2a897f3e1551fd656fa5ae6767188d16866e4f574ca69b5ba3c198635dea353656d9d13c0b0e44703 false +check_ring_signature 003dbeb08ec9cbe89bc5495c216eb8ef02216bcaad6c5429ea9136bf7d6af1c2 429110c4ed01b4c686ce4e137bd8d20db6024838a9dc2a3e3b9daba32a9fb2f7 2 a5106070941db89999df2d6921e087a2d035385207f6fcd23115624a4fed6b94 e03800d154c8fb65caf40d3a012890478cb48e7a0b85185fb23890f2c0a7f7d2 6eef4bf507fdac00153e7cb2976c3e148e4d60791efdc7fbc171c4670e4f7103d4e3a4d2f9cfcb82ef69d4ae287c13300bfd7870de5df456d01114aeb145350d51c5ad961583d7d2506fa7c0a1da0ad1b99162c80a6837e7c2de71dcea19f805b6e77c36a017f2c4ddb9a0be83dad4073c5d4dc485a806f6059d842d9adf1803 true +check_ring_signature cdbcb4528b3b4fa2874ec85d7d6e60a1727d0ae1e61a4fccd9857c10407ad43c 0ebb7dadb7ba05d506f50a6fdef64875d43b1ca5f2686fa1aa6895e1462217f5 28 1835de1ed08434c6360462511aee2a6b89692e1662e91edfbe3c165b122b8ccd 435ba065c02bd28589852455a6ab3b5fc175ff1142085a76c0ac2d2cc1fadc4b 55aa1a440f318c964ffbd4c1757d3d05edc17862e4a894555a4beb9ca382db14 6c3d5abd1b0dd21f5df18d0bebc2e0347b46fab19e92eb95ca2d097947121ab8 0177e3faeaf1872f6aa3eafd7c063f189d6496ac1af83eec95b431ef4037434d 60e93c144f566d59bd6c0bfc3d34a4eb08cff52b48a4f7b5112db8a083a837aa 13d9f340b173331257c89d7f0de90838074976c9f5e616cf6fd5121d33feabb7 5abf09e25a6d293e021fef6f2ef5d85f48d4fdde874b2a1583c05757e627c2ff f05e23b382784d81f54c83fdef9e4d541ef6a25ac905629ae757dd6e348170b2 f78050a67dd1a62505dcc936eafb223b894999eff56dfaa0e48c3d53d1733bfc b152fdace47efa8f6bee531b6356277bcc7bcd0d08eadb6d6bd83eb53afb0c90 3f712defda9d5cfd78bd197e641dc9bef3e7655dbbc67cd3061ad4ba74b655b2 e9e6e9be550abc5c0b8cf3a38e41aa66dd00066cb936ac35b2175bc93c93c989 ea26f84688e968237b5f69140ae50ebc5cac9ba5b17605c5cc61d1888bf3ce9e 450418df73aa32267ae36a265adeab28cb2d141be76ec6e730875c1dee5b5c30 07bff6f2555c77bd7546e21ce8d00bbfc4a141c5149a5fc8c7a5da413f0886ff 8a7b14b8ede68dca81ebbc659d36e00209c8033b614858a37f707e429ca096be dd41d6ef20b561c91e7f0896e987bd26af7730ee70a5637694542ce95bc753db 2e982faf21d58ad8f9add6ae7a066a34160bfad55e0cfd18934b0e5db6ae13bc 98cebec3978e09dff517cf4ae5d72185736943a9153678b1eed7c97605052ef5 aaa7c2a6636463db382c54ce7610b807d5a4f079429fe18c5999d8e98a2921b6 001b306b3ee7382533b573b0e82e039e6faf731b52e267b81963900f84ac3bec 63698b88e4ae4baeebe213f9999a5b5d6d4ee738915c6c647903ee667b0e3408 516b89bb929120bc7366aba9adf9079e9c025f94f8590c9aadf5c82d5a898878 17d8b2e255a5c70a73b3c72bc1671d63c9353ccb675f306e0a9c694794932eb1 a2edf255ed8be2f85a80b8d35edaed3bc511744554a1a65014f7ff5d2df30d5e 8c3e042a7e07649527a7eac0bc2539e0d93aaaecbf1956972531f328c44dee03 c55a91f52ccb2b0eebabdeb887ba8438f5833ad610894ca111d45cd8bd957d47 9750f66d250d8bec4fb5ed932abb19d13f08510750e0050591ef1c286a273106f705b56eeb08b8ec3b930757cde7f3bf93b094d5443ede637198974498fb470465b472178286e5c54b311630bf09cdc3722825a366c2ee2e7b3cc3b0477911051ff390cc92f2d1414117cfece9bbd2748d122406b44b0b71b399cf83a0d88c02c7c7f0ada1b3e35c62fab07efde37b3b50e83fa3a2d5c52162056b02852a500304da4bdf76e0a9ce1983f86b2ea6d393d17458ad6eecf69e7d35480d2c032d00184edfbfb21f0626d5e63f102d4024bac7918a8076f2fc8002c147e3cb63340cac7419b1e773ca63a81bdc4fe1332d3f3a8997ee9303ce3cc3b8e5c82198fc05857fa104b57c2c4619657f13b6be1112a5d0e8d200bf1d4d1cd93e079986a701c826b4291f45422dbb2b93481beef81a6b280fe9f68dc320af3185ff8516ea0f31223512831d276cf9f7669bf825cb7ba3867b28755a270a78d7d20ebded810d564eecef6a2a81fb45b7e9bef4956530b2e8dd2e3e41bf19011ab2c7291cd201fffd4f8051175f8d7292ac4c60e6d18baf478f0a027e2135870ca208bfcd87013e93d7ed65e36d4bd9cb23efb5a4af40397dd94fbafae8564a87f78ff5320d0236d22f73b927783125e59c2fd076b45508511497394fed2b6739df308cef620d0593e2988ffc7f13d9b745f4dffd460270454abb74661f5818c2695b034ba30cf9117d9563fc8776726ed3f4c466c0c69be0629d1b4adf181a0d870033c209043a38a041e17c526804280ec23bac3406e531ca3cb0d1a6fab9b6ea783054040379aa2ab52bb62a7b1bc980ecd9b9a10bcde28e2f07724129b07978c39a9d5a06e664ca6b3d583bf3c276ec9b237d8752a59d03c625e12d60b9ccd82736c2f606419bad6487e06f497b3fd2661b0282bfc3f50dd3c31f6732d0602aebef8c540def5644691a1b149fe4e1f58bb8ec41d8f9828eaaa5b5d0c994bf9470324de202bcbe4ed11ed6af731c171c97a2738f495f367f48d808287ad5a23f105495e00dc4b216bb77b8eb7cb8dfd124369ada1fcd17012c3b485f5e66c03794d14f6f0c52a6d37889cbc220613098705e77ad8976b21121fc71fe8a92795e6602990b06ffee88e26148b249af68289fca8e78c9767d7507ef32db0ec8de8d6b5a7f2802bcf96bf1785979422d2541803e070b64fd0047c3e38e0ab7e660cf57b7fd180dc5a51edde26823f8f0dbab5b2bbacbbe6589bb2f0d589297b25a54c5a6ed3d0c65bffd93e496ab1bd54bd3f459188500a20b50be2afd6737a833fc4b947eb400406859ab77752e3bc54eca50d64e6ed81c85f23be50bbb155f01a3ea66191703677217109e0cfa7916535a0bc370371110f78e5ed5bdb66bc77162792cd2c603ae3e42de2d390f27ed8fa078481a5907aa6ba25398108ccb935d0e926bade805d432e3833ae0cd0b0d80541ae333667393d9fe987dc3eec1b184f1092cdf500f1b109cc94863cc59e1ad792e45a7da63450e586009d26a37962afc6f4a8368032f3234724ee7372069fcaebc620cce24a0cdc2fcd13272112241576999c733091adcb6ff4816351c804aea9b4b0d5efbf4ba94d40a359dabd71a6f6a0be8630b8257a333fbb96c6ca8e4223519e60e6dfc3be9e16435af26a5e0bdfb5a6284078f986537a859dc01508a3180a4241ddd3337b7c53e0ede1051275892a2e36e00230c5f8ad63e5a8ce3af956eae9291bba28d85028b8016b1d149748fa07ce70038aef35a37596a459e860c28461859668afd55ce1e7456988f03c82c6f480405fd159711d1006c994959942c8eec829bfd529c9dd1e882414a7ef6a9746d240a9d10551564ef0a2abcc1b0eace77130e8c7f4c58a1919075a4f96b47cc741d0be22f8876768afcc976f6669badd17eec1b8d2bdea7f72d20e29a896ce35f0603fcb99db7500c75d66f32f86575a6d3ef61d0bd984bad0fccc683e28b9a02a503e86f3290030b627c9ea00db01555172eb385e38950b0edbce5d72fdab775710a1fa84d8d2018ae92a3aee5361c1a90d5a5af1c78fd679d4f6c4f196eef9e7b020af9b8650ccf4413aed50d09d2a7ed010d8fb8cd8c7c5840608b77037522cd047ac8cacecc474d51e56f6993b2a9749f0e90b671aa17c14c51cbc2535bc7b8003fb0669675f95710a62dc52c17cc91fe977de827747354a0662442acf8bc970566f2417b0c959f2c4c129f588e71bda2dd900b3db6a8735672a2c28e9c49a80c46c98f8158acbd1542122004984e88615855c3273b60c775ba0ba8056a7cf90e16a0c710b017cd30036cd66bf2fd68827ce20e48021a5a0197d29692ca0492088d97bf23dacef70a88981c33da2b8e5392af66e7a1b4074ea05fd25dbe30f10244502420183bb6f03d799b7cd9c3ba6d081ede4619a42bd0a4b1a661eb23690df2b432a647b20fad3f8c96d3758bd97dba0cfa264c304f588d04c63f17d242069c5962a65bbbacb5d70b63725b8c80b773a0b08a326db90562d6df01267c3508 true +check_ring_signature abc2d91216e6a816148135d73fbdb7c8fb24f7c4ebd5f64cef8ea693b73e2f37 5e8137f8b39da4802577281cacad08affe9c44829ec510919559017123c80010 49 fddf08b6d523088c9dd48d33e5eb1378222ab53504f6d778e249fe6cf1e9a481 cd51dca4085b13fe53bc1dd1d00be6ee67c0c4c35a5c62637d313f792fd7e231 8ad13883e1c2517fc1a5b35914f0bb73ae991c355e591762c49fb434adb51ee7 821b44175848592e7acd200a374344fec322c428819847244641c3e9e450e244 93ec5e6f20f2ba3d85db82172f6b1c5d81e33fa06c47fd014ab618feed13643b 45cf803f127248973ebe9ecb8da0d6f4a07e9b64e803038459a82ab658d5bcbf a4da5f1d1a2594380d86e48f6a6247649e3501d2b2dcc2bf10d11d13075012e2 36583d3d38c660d39856079eb963a5df04ac6daa8de2f0e345ef80d1f59125a8 5bf13bc86d9e2fa898508b7cdf1c5ea1992f7a76c509d5ba2e0aa8bba22e795d e0c8a570c426e4d2887796204c8f2a2dd7cf86055eea9b950a17b8d29a3db6d4 e8ee8a55b11f6ecdf0a7bd28b0c7082293d737f734dd6de755c362aa44b5564b 5a0b079f04f21ef776394395dc3df3a49e72f8b8a04b01dbe116552c120ad5c5 2a539471d893f2a0f2a1ff247b3bd897ad9d941568af8d8f949297c64637a544 ece4a41a6289a4eddd4ee5d191ea786ec332c846e1c52a16801b7a9fbe4a0f63 5c86ab777b4926943d1198531cefa9495ee4b75f1c8e97a54429d3a350f818fb 538447053f662c7d60af43bcc66a8294f7e61d465d55adb2f0511fc3398c737d 54e16160dcef24fb1c610093deb597f4ad08bfc9dcf3ba40a9d13f3b0a193a73 82ec5a2e1c523141b67f27a4944250aed0023f755cabe85d2bd60e6ac238fdd6 4adcc00e1562b013080bc2b49634f264966b161f1063999ac96652c0624926cf a8a7ab1ce48cf3758dfae567552c156155abdd2c04f7d2718cc269e7e9d231c0 888acdae0c062b1584667a7d53cece2f89b5a066661309e0b86011251303fc38 902f99ae58bdb4d36c67c5de8b7a7c1e8dd3c346fec5c20999cb672e0fdab121 49c139c87a2b7010dc4c7d4d58663861883bfc69b2c19aea7d56a7be900ae6ed 4d2492f417e30b7c6fb95197ab41efb64e535f102e2b8fce21e63773dbe421dc fc2c0cacf1e724aedd7adb0a3c81d31ac535d2bc6d42ba0dd29a947eb690133f 966a0e7757fcb44c5db34905439d18c004a8e007283b4685a0e44e39eaf35540 51e6fb95b3eccf4f260fb6dea10fe6b8ab2f986d265cd7b6f6548565b0d2f2ab a28c7cfa0f08910fcf9d39f1785ea72a8b70ddc59c0e747766dc00a1014e83a3 a212e3b4c2c86ceefd85b2e160a2743429002b149a43add8c754c52b0b0b9821 79c9658a094a391b820531783f99e30cbf4c9f58bcb10415dfa7367ecb9f9c9d 098ff361fbd3f44b89d9c5ed7af8cd821208dbbfd0e3527c29de38e4a6dd2f3b e50696c5c5c773c006cbf3692ed4ca1c47441889329e58581f2eca8087ba08e4 a08aed1b489422ab02535a6bfe9e60c0f1c1c6a2efd693762606fcda16c6d106 8dfa8ae247e13d98a8593bcc5f5b7d64b6d49d4b094f418ad3960eb392954b6f 7bef1d6d5fefeaab24c70f94f091b2ef0ce48872175f9fc7b62e9ff5458cbb4e 05e30b9d7f7ae1b1d6f6f939aae6a0562dbb36164b6638e9003b989b7da00075 af758dee3ba03a8bf8f93e04e8f8b92165879c57436580c832b4987d0eef4f74 c978906650374b71e62cf16e006bb9d0771a0ec4174ace8abc5a81e43a05f37a 394ce72eaacba775fdf558977e31679e9458d4eb7df36fd17d2f689c520f41fa 8edb0c827a9c0c529c2303adc9a09766a37785cf0b32a1ed631479aca42ee132 a2305911f322e3289acd158e01eceff6a763716b7242862d55826745b2bd4ec4 1731a2a56a9568080d75e2a6cc3245a88e509910cd317f9365f820d5d5af5953 ccaf890b556c11f6431930d25adb3920d4de6563783b50ff23c2a5578f19a2f2 39bee6baa32197fcf2a50c0bcbe87bbee55cac40b0607dec9eaef45150a68e64 a18cee619337ffbe6b5dda99bda54ad580cd4fcff75a57718b251bfa8addc8bf 2f9c7940d8e483ba9a1f1b2add6c326f5900668c848a706d4164d135cc769e29 65127e5d45710c1bd3967b38cc87425022543eefbf8d3d04e31617cd160bf32f d4794b766ff7a796ab308f501c1f8ab4289eb8491a7235ae420c87bc71e7e214 076b31970fbf26e9278a199c52ccfc6a529c258e0df2c2f5ac7600f8da2acca5 e0a0e80c152cf8e523a66f9efc8f8df05a8df89c0a7682a059ee55a1263254075023c2a55d7af74ad01b885e6f54e7b3623fc035492637d355029f3e75b8e20b66c48cfe1de33f955f4d827372c27765f06ccc0f927bd2a92a4cb929c3333a0f5279cfd7460dba4008e7c7e0d8913982c17ae3fe9bcab085c1c601b4d19b64049e3ee8bfa875ff1153c4d9db673744862680d052f9127328fe324812cf6fec0b401d2681225157ca5cede4b37fae3a1a7b43b3513e38de587d1878292e168108235894702acb27e1ef6877790e9fea213c12bb07971fa53cbb1f2ab8431b050bb394154965dc72566dc31010220bee6a7a30cc835358f4763698ea9e07d15a0ba027c0d53b0cc359fa1ea1901ddc582bdd799c075df7aa98a2223ea69f965c0a5005d5b41418521a4625b446ad3c1f79e966158f83adf8dde46be91b6a3c310cad8fb498842c0feb90dce344514cf2fd04f855766565beda868406d6d77db70c3a40beb338e94f5795fbf88c7d9f83f4eec8795cd25873fefee81cb73b834802ff6f2cc8441d54a7d4e3ac84aba7d8171061557e22daa51da766d45235451002ae62857d358465517cd2555de862717d1874daa3cfc6b8c19977b6c5cda02601fac26f0c2c309ab53184f0391fdd6308598f4b1bff65150c89ef7a45f62a550bc9eb17816559404786c4a65b06ad1ed5e56423590b940e29181d4c72bca7260aa61fef459bbec70fda33b8422869b6fbd8d9705720ef03ae107a2fea11d3d6098f197d6e08a2e08488dbcdbeb127ae4f3f63918b795d48c811bccfda035e460e0b1e3063d824621395a1dacd747afb7889ff5c76b0cd01cd02369ba8ec032a0acaedfe1fc470356a073cb2594059aec6526dd6fbfa7cc280f5c8e12240a666040a7379e7919382e1c670368ebcb25e2b84edab4454200b9777e045ace0fdf408f479af9df6896999654890ad05f9d349e82ab5c0c89b8c147fcfdf94f108100539c7f0e7369879e4c4923a819fb0a5f95a20b21a48bbfa3f1d6fec904cc0650befcfcd1b93b0e3ff7196be34c61c0335dfcd381c45ee5a054c7047a0dcafed08c383faa3a51ab9972b4b58e99061130591c50bb512c741ed3420921569695f0cc1b0765f80ea23ed6c7d776ff5ae4e4221ee4f4018869ce0474c59f8f95a450af9b89de1c34cc0751d749db8221ea60a97c267afec2db6ddd144b99402e4ee03a669e97e0922529d09458e200b226ce6237e14f96e6f37fb4d5f01dac98e6507da3de79e0a24c3ef382fdaaa930915d37218c3d53e60b024f99aa98b1e4cf209946110c24efa37046a56b07450dc3fa30575a42be34607ea0c2924474b8be3082bb2d7ba986e79b9df94c5713f8d6656eae84f61ce6801222ea78ab2e60b600131ee2f8a5bccbff62f5b0f7cabc18f43c3b46429c816cfef40106d54e065b00e97148fedc51cc55e7fdf4250095bef45cb2102bfa2359bb475a9b1ca5463810429c62dbb9d40c55117551766b27ab13dc38b5a1ddb33372c86062fc42e372408eaaf28df6cfbbe8a2b2e9c28c90c5c495116ae6afa28d93add3101b0fd9d820ca6d3f5a5b715b3a86146dffa6d1519aa0af1504b20cdc970012d807347aebc064bf73a2a747e2769e499e80b3c2e5f23446a88f7b2c2b54b9eeb320d3eb2e502ed7aa17c8d25d6e335828e79f3accbb671d15a35a73e1836ac483067745c9008a87f036b3110f5a4e1dd493ab4ce251dffea1b40e25f5a197c2a5e94efddf806f28ceed59a551e6d84bce738ffb6d9c3a1ad899d0e4c9f8e9554d0a8c61efb03c507160aa57368c5e2c6c3aec501c39fa70856627c12fe9eabdcd6da788ffe0c404b2407d7120945180fad260daf2890c80b9fa5601bea407ae1ffeabaa276068c5b89cf66bce8583b928b73ac4c319c455f8195bae3829f05b3b862960b3705dbb343d75dcece1bf6489ae1c51404be3bfe7c589db54611c40248ab871ba80105bf475db86f7eeaa5090d44638293b78a7265d097c3e296e93b6b17316b660090f82fb0510a6303eaffa68a0ee4f832c180d045ccb5ee5d14b810ce04fb170bf5ea7bd9d96393377e5d7ff77117562c54d63d3ca2bac66b648148cb0f3095065ca7057e6dbedf73dcf6ee14f65ff65d3376023ce0421c0a357f9043cb4a6d09be8bbdf570e66c41fb12e24746159c352abc37dc625f305cbb631792fee7870a662157645a4556f2221042f9319d6a0f8b92498b0b7775a6b8ac22d21f09a50c46a5a6ebdce96e70fffd390de2c2a431cf0410345765afc824a38d13656a5c065e6b812aa2c412aa43ff1b114553cbec77b65f1bfed6c7aed5dd57f3b308d4055a0c72d09b976c5836d035dca0c7c7b17fa8566a36056bcf889f41b66fe719041abed6ba49e418ab9eff1960a439413cd287899a718a778c466b4d2e6673bc0a49a387c0abe5e7d5f5b0b9d9d738da2186d4f85a84a14c0071a058b36d065304fc70754544bd32aa5ccff03d7c699caaf58a8bd6adc422a7d74db1141db76c0cfc36476216e69c379fcaff5d77979a89f9f477d41804c218aa07fc629fcaab03268c13a2fe18a10904ca8b3590fd5315e1f2c6153820d034aab919b0a4fbbe04e334157f854cb600c9e0341289996d4298fa3cf0af73314f91c9af0dc5285c0bdffa3c7853255d574fc5575b9fb15a24a37ef318590e60877b7673c683f5a806f3ede7b4455efbf072847011f71f5883c5af16f2a6466266d588a58567250400dd2f604f7d2e658481ab67bcbea0fc5c813780dee14c6a639a8c07cf4e47e20a77bd374666bfc30594102d21e946f6839373ed34b55eae9e2649a37d4b55e90b45377cdcb9f14c4e9ea7a29c26f625c08398143a41912c828c9e4d8f8051a20a9dcc70739402b5336d9b9068249792ce532c5c9488abd3d9f5ec424b7b659807491c698b7c8dce9d8a15ab09398cff6cfa8c577ab4e913a22a98ab7d895ece098748ec8a47015dae914f6fd843bc86804b72c9e74acdfb1dd727e0418ecd630f04431927b918d15876116167c62408e36580f891819111bce7fbbda4e5d9d704c7add6fbcb07b815fb14f438e712e204c4736588f13bb69250d4a5bd5a4f6c0d4efc20e34de5e121903b3296bf25e9cab98f6758cbd0712e4131aa50a1e77803075bb54e3548b696d91794eb6ff60b7c6195a5ed57f81d702f8ed8555c30e707551264211c7472796b51fd4d16f0f1c22634d4eb87acf8a3f1d09116b382640a0298791a31b3a762320b8b2a51647e06ebcb0be022064503f20264603149d70b4a2dac9e6c0a4e0e3a304ec39c25d931ed613f075810da39801d9af1f9cfed0ba5ab8a4bc8e496105ffe15019d6e2c3fd40725eb0547714519b7802d70f4ac09d11b2c2394d9898c20769568d88bf87b2c9ae7c8bd779dfff28eb7a13da888024c21e142c784ab89b0603e5b0cf6330c37567e6bf2c00d879ec42a6285087906ee0428fd93752813044b5ff22688fb081bb063e1432704bdff26bb6b82aeca0528c64ecfff4271443d308e2631c5b37eb2e2d58c4a3fdad663ce2020916a3205c1328bb21bd6e6adea353e0c1457baeb8dbe00f43376bc42eea633460d4105065501a05e8d4461325a12a3f99263096cb2e3c2f695c03f75ed9f8fc271a3640c1b653d1932b8b8fc119cd6ae38601312ea8ddaa0db8baf1c01b73e03ddd8fa042713b63dee5bccb0a07f8a3de1344726d4d348215f69c4ab7173e0d176d85b08e5b67ef296cc4dce20534973d41ff9660112900170bf190ad85229be3e4ac1067f6a2854083f304f8214f7d7201b7718f9c53b50151cc809c935e044904a2f0b38dbc3fef69727ef130c289c62e9db7b80a071674ffc97ee789f58900ff5e50348ab953e3fdba48af80f7b9d30b9511a5c7d44ad98f6f9ce7343d302400a70057b0fd331718ad7954e1a7d98be248075ca601937469f502a18eb3e152dd95503986e32ac4e487a3ce77a56b1eefce2352bb3e47b43108169b0536f5eff6e120b2370ea313458ec9545ce650472f40aedff2aa0f6aff29816be1c09fe0b6004055cdba4bfd90896e35942892edf3932895b7a79c00f9e820e01190961c41d1108c482392107c065e1e9aa276a169f919af76c7fe7b5b7450532f1bca17d95f30947523f65caddd9b1d33e44f7d50a447faff989f0025c8a8a0f00b2c3067b530c5e2e1cb47f8a3f70ac901b3f7c9d9f2ea7fc94176b5ec65c0b8f1a1014881003566555d988d101beed2ccb8a94dd9fafffbefc1c0dee1a76a687e58d77105b092645fa35b65b710bbbd7d6f61ac4b94cc848d354eeab9a815693da4d6a0ebb0e3e2381a9fd8900fd16d5674baecceb3a498fd2d4c924df2f010a9a5810a2070b5705359eee2087601d8d88b6c07c76b00d54dcaa5d94b0a0eacfcdfe050f0508 true +check_ring_signature 90dedb7683d9cc42f3d2bbcdfd0222940528923788f4271ff4bf13a527b09063 be29d10fd7e20ac97afed960de4cce0f8a437c110edfe55aee02ce3e44c8c3a1 1 c892a31610fe39e63f7ffd0fb43d67362e1d8454dd6f27b53d124d58c105fb42 5b2fa757453bada87dc0e475e33760c7dc127c9ce1ebd53d7c062be22c69b096b108391b4c5db1898c8bd1e119363bed16d51c1459ee6fbccf9f77da95be1c08 false +check_ring_signature dbddf99b1da19588df09232d008a183b87ee346b57952c28d76937c309686524 75661f76bfb1272c0f835ca98f48fd9c3f07ed15234e417ea7eb2ba9707801ec 2 fe6390c8d85a5d54fd48a03e97a0effca2b2b1b23f4f3ad76a5e6cc90360cfaa cbee57aa651d83a51f2e79c0c61184f7dc8a98ed03591699e5acbe3641efde27 56e8e503124264fa957690705878b88630eca188b368d781b87abb8fa7815c0f80946ae144ca0a0684fcb1b57c6c9c2b0d915e6822ba4181a387599e172def01aae2e269a7e07fb0780a8853f0869afc59ff789f9722f533ea59a7b0639b050593cf095a51f0b03caade4efc1c1caf55ea1b07af4777754a5ae5f09c19fb8a05 true +check_ring_signature d4d7877fc8bb0e4cefbc182dfa863db1d5253a4621f8ba6e8c3280d537db82b1 5494ee2206cc58a5b3f79f8d89ac654894fea840822ad27138560f1fd430df69 1 6fb542102587638b5da1e53c66bda104474f5644e38ba199a05818f1acd64329 14f0226f4f8d104950078312dc13ab3bcf51c138043834bfb09ed2bfa255ba0b6ba584166588a550bd93755f010dbc830287602709949c406be21501ee9d7008 false +check_ring_signature 3ccff53fb1400dfdda39daaefc60d245d1e4fa4acb46662e50f67c24463dc9b1 2a77d74c1353f7e01a0e7d47a0258f0c3d2df62e218e10a9125385f6af3c6f7d 192 c8af3917c9caeeb95cd55d056332daddea4080ddae65a7fba62e644d6eb6e892 533e43d4e553d065bade63e4419755dfbccc98241e2795c778f861c2a6587569 e35a03873f9c8ee61290f56c7c8eb8275a33b0f9654f2aa3621a7e94a6a420b4 a9692ebf484964ddcf34c664f4c0e810f30074ae53a7a524a87e6203ab0f13ea 64291bf68bcfb387252b7bcf9352590a593f9e19d1623e9b596506048b9149db b0c65cd6f49fa04b409f3546656f30b4ea379394681a3c1a3f975040b8da9dc1 94214b74010dc2792a069870b62d74894f3046be2df5bdea513c4c7d4f06913b fa1ecd7abe6257a321f0fd42ebe9acd3931d68115dc49e1077068dce28cd49cd b7344012c108c3ba24c958650a771e872865d6344ddf40bf6b7b0cf160de8956 85c86ef3d80cd464c7e597936b96f8b8dae4ddf4d13429555fb555bd64747057 859d421808c263df24db5f6d329c48ab8f537c888b95d1b2129dd2404a8b4949 5854d6ad117c6f6a59587ade7b2a97b71f34e272431b8ccc965abd46d1b55292 3309ba072b24a9e5d62c3b2fd8f6e23bb5e0da553c757748b18a3afe01f87f6f b0927ff3f343230e469586caf0e056aad2529e9003cd5eb06302a0215d3d2ec1 a69cbe9b61fdf8ae91a993f688e5dc0dd62fd55559afba472636fc6b695f5c87 fb2741e3efaee8d016952e55c8d457e6d5252ee2e82d5fb3ea5528c1821e6baf 0a89a04bd0e2a0980b72f26444449decdcb109215f042220ec1470415ac3b40b 9f6b0ad2422539b86c00bfc12bcfe368d0a5063b690661ce96cd930d5a1e3391 7e96e69893167d0b0b3b4572f3c84cb90b6944e150ee79c150af358bb026ec50 59ed8f019a2d47dc30917b407912c69a5c0fa8c61acc5fd3427e1a1d83498818 70217350e1d7e2bc35f93aa738a06493c7bd1061d6bc1076187c300fb25d7a10 8523fb9f06332a4609fc3cf77ffa1b21bef09b62fa4f601de0daeb522f2f72d8 eb558567da1429ef352bb88d08e8ca4a4a6a8a84ed911f20833bcd38ebf451fe 7cbf5a06695ee324cbef68c3e006836b62ae4b4528774b2512d554e985e5a228 f49924cf82501fbec733b0443c7280357b15c2adb7c6d40572d61807a2a78e81 53ce8015151d659b150312d8ab0d991738a5a6fb410a7bec996a4b05f846a9c7 534d603781935a5fbcbc10ebee8eb3a4888aeb93af28e31b80aeb65a93407ce2 f8e7f9ecc009505781da0ae60c67ac3c26b56a9327c618c75faf5beec58acfc7 7c35540273ced30a91cb1c6beb114c6c602779d7984e01ab4d3e115f9fe5b7bb fe886cf39a82c858dbd25648ded37c2ad2328aafc62376763548983417a1b0f9 84c9ebdebd9e4e48dcf769b851c53ad857b9569effbbc203256e2838f1bc2bea b78753a142ba0adc39d2ee87bd86ef009a6b9f9798c700f7a7403257a4107760 4549d63576f235712f9d0b5c30e5525a2e66eda8028eec3648c96e0b514eb0d3 d1f19e92ba8a080fffbe4bafe810ae7af6be04df078987fa30107e4c091e9812 2c7bb99d02aa91a5a6d58c6f3e3dc2bbc2242945046f2cc05a713b90db8bb483 7965a696a3af6b9d542a523634e29b350fb31c8914e17cf5b2a28acbae8677ed c59506d8963385b394ea578f1a5a46404e3262e8ceda9d5f80ffa422bd772819 1377a74ff50ef66d9d5a08ae8beca4da01efafaefc57f89fa03b37f337e79d6b 33e51b93134ad431b94525f283f721e0cba10dd1006ff1bdd5c971151359a7dd b12091911a97bc7cca726f745bb0d9bd6a81deec409f6177302d592fc1bdffa8 61212d7608a8f9d54611e5c875ebb13ae8d448fc8d36d9fad77fe2b13102c908 9646bf327e5d68bc8aba392bdeedcd18e439c9f7871068c07a6b7765314a2f3b e3a3d3001369bb1f5e50fd9367acb3a4dcaf6955c66d04af987076fc7980175a 0dffc8208ad5f8e83cfed6f01ccf94117d1bbf4b0120f56b6e85ab7458939f96 bbcedb8ee9b5c7851493eb382cc02724e5be6d70922731d399f9b9a079f0bd3d d16931cc70282ee5a8d0354327ebd770cfeab9157ef9961c71abccbc5be995f8 b607a6f2944bad8e4c0104da54bbbbe66040a5a7c56976d0cc6d7ccdb647003b 827f83ac87a4d15c56d3c1d44b0b1a21840c081aad7a3346a3cf8a1e5016abfe 6eb3ffeae28571ba69c609a668986a2d91bfe04d76d85d552e32c37a3fa4e5d2 f80bebd42993ae335f1f54387300896b2e1a70ca3855c0f763c09745d79ab4dd 3759cdc71c343410e1e8796c7f1982400b62ec605b2b3b4b1a6a7ea800f57237 511c5ea3aca86a8052210ab34295ac97ee17516039a008bfdec2e935f255a720 4506b124001b3ad95007e77613c05d04fbb6e8f017b297d4ff55438a5f12b1d9 f175e90e349a1fb51b92b367aa35ee8e3f3cba81a75b67a7c3852b0716770944 ee49f0b119371f9c49d021459e32ea36ea3786590226647f08e401ebcfc5a194 a341f7317bda1cf810d0f9d4b037a1e723c286566b242c349946c3b354019d8a c4c7174aa7df28c92c81768ce780655fdeb0a666d499732f9808f995e69f69e7 6ba2717d217611eee8331e8596d4a8603af7ccf58a35a8bdc6cb2e8524de2f80 97a2306e3e7ebe1a9c766cf5019a9ae909aa3b9486e3ff590fc3664be0fb8112 8b073a883c22fb9af83167d4e9522bc24f51e1d664f2ea1216cb753c3959a3de 26be33a9e75ddd8ea8358ca6d489f890f852ee68834a9e35ec638550732f050f 83fb81136ca2e5040699c018ea1086c5084f31173d6d0105acc41fbeff2b227e 7e902f705ac00385fcf63f90b9949eba35a05292fac811c3b1f9a837b2abda5e 3d5609a1236ea4f0c69f9e194d6f76e3bce2df2529477ae911fb6eed04cfd214 31e1bde41a8ced17fef0a218ca8d5f48bf1fec39e61544c64a921d983a081c5a 64d877eecf0fd71706f320210d3d8f3cd9f8cc55d88019b96e2373225a913559 8f64ddf27089e01c603163f467c9cd8558207b9ca1d0a2cd9da83f961f468a50 21cce17bb4aada8debc4ece8f9999be54ad5c82c2507f81c1759ba22b3fde14f 870a09afc5e12ba3554d7de5a423297ed65dd84412904c6bb51688b40cc5b420 0dd5bb1a22e9ad95a85bcff3b4d42bea09ec9e47f3d29cfd91c9897674819c42 a098b562866126646eb9c787b711a595b88924aadbce4418bb68347c41fe5e03 8556ad35c87a6eb77607b9e32de64b783c4449b92380eee5296cfa30db42f3d3 dd0c9e82434a8a94edc59f8b70dc9ced85a687621960c8819b69fe8dc697cc3e 04e526fd0920aae4d11694ba439d79af5016ae8bd69fe8f79832df790be14ccf 30aa2c391db5e8ad3e053dbe8388d353afbadecf903e8350f712c3870574ca40 35a5efc3dca7feb57baf97b934fa3a5be4ad7ed529ac0b0d43c83047ea1f4003 c31b115fb687eb951664e2f66f1bdaa31a7ec0ab8819cac097f50476aba26c72 d7d180b028d2046d7cd5906220556ecd5f952b20d0f2503dbca1bf0903aa67bc 9aa776b4931ac8f1f678a82a8abee9f64ced64d5db5754be654b322d32fbdbe0 244fc9e064b81fbc414a848a2fd9575af63d6d87e0ab465d90127583d999de6b 300376ed8eeb00fde62c3212d862f60170c4511035c0b9c1edfb93db3e3ab2ae 5d2220d22c87ffcab1dc65cbea77f52282378e6cef1780ffdaa1f360b697ee23 ce33146c888863f63c9ead468b192c2b9f0b528889244b23db1e98a5e83569b0 e635e8837d9d70b76a4ddc7058ece794e1f7f1af3c18ef0df02cc5a43a540728 4272cfac5f2c18205c05c347bdf14c4d24abdeb2f1438dc616228fa084daf270 5d240a151e3ef8838cdbd1f5b43900df7dd191fa4facab349434aef6f506fb0c e28f98166f9f6915a7075d0712bdaf0fdcbca7a7d77397907ca62aaa88a12cf3 990ca17256c9a2112af06e755fff3c90b7c73c8f17439cfb338558fc95432db9 af0ccc61f6fa9ca2d8ab42a4623391ceb6539905cd358ce96158db8a67dfc895 b4d89b67cbba22cb5fec1767c220a938b96ae56928492c699b2c61f650c20ca1 962c1fc23d6a63b5a5b0075b890881fe6b6b56c6bb4200aeb6a06397af7d8212 b9d34c6c5037e0d9c8b3adaabd5d72e8dde69bb8012812762ea2c6372e907698 20eb8ea7e58c00a20e841b6956ce5b4f405a9ceefc2dd0e5a62a75ddf817b5ef 7be392f8051c024d58b362aff90a0f482c72d1fa01db55cdf39076ef9a7d3553 1c4d3a73c66972f8ce55dfbbdd4540063ce470cf0dbe7bee34bc96b13dd2a8e3 971c665e59bed4d70c4bb257bba926fa11cc5c72a2c425da9b22f0f309f0fdc5 951fd38ee1e9787a0083f299ff5fcf8397bcfae80e5e64106d637f16fbbee9a4 91025f75323e735dbc3f1405e083d8fcd6eab247f4304dcedc8332e02f03d3bf e302a42a8730637abc242aa25694c8dd1d8d1100f69781203a6f6a9fe5b01589 0c96e3191ed4969c43a0a05de4db6b615d4279d6b5a448963023e4072d0f132f 58d8c0fb04a854ab893900f29e39c0267a2c940520f6882153d64f81e944c1ac fd092d1f86970148250d91cf6feb66fbd690111d576033b38b7550ca4ec45d2e 9aa96f6e067479bc252ee631892b628487ac8ff1fef04f9484b68e2a8340fe36 feda79ede9fe684251c458802f63c98891c58a70dd8d06b123beaf4634cb3766 25fd35bbbcffdcf3700f5a7d33ca3642e31685c42611d6ae5f7844e0585178f5 15142fe4b306acc56fde9d7c32ffb1536bb41ec93c5f093ad7b0728a28b1ad85 0285db1f2312833af40fbbe664a3f88efd300f16a8556aa89f1271d3fd9a9f03 f2f8ae78f08ae0baf3ff65b56e593dca68689ea1984c527d1ca6d3a0d78754bd 57a8871be5dd4e282ccdf7eecc0407869560b8d971e72b5c4f3f9fe224d2cc23 3db21df55f7a10d1083a3f6e54c94ba12e75f1e29647099e0d1647398277ec72 525c894c051183893c8adc9a96eb82d4b441b0f0a5cf0cdca6035a30e456ce38 0a8bfd315f8c4d92b81214371f322913bdc3b2380ea9297d86dde79997c511cc 911b2cd3c8b62d6ac49fc0eb43f99c94e4f62a2cedf4ba57e48158cf6c776795 45c5b0cf776cc84e2afd4853f34ab0bcc453181a9c03ad1954672f2f7c3617ad 70be83383c154c98a16b9dfd22ed1860ccbf3473c601386da3d2dc8d654b3ec1 0de63ce301d7cf7e2425321dd90e81cdd9165dc64f9c32cef81b8aee1c7edc48 fe4460b16029b610320ba66f8407d8ff9cb558f4d99b14f4f56b7b95d051721a e5ef2a52a8967657517e4fc1f4b2d654125ea811ba7947fef5fb6eb0fe505ab8 0bd8c3341c64837b4f150fb674f81a125697a6e3633c5518f01d159ccbb6a593 d166b85682ca0e64d385d3a798244170911ce8e5d74fa3ec18aa13bf8005185e fb638ad0b2c5d185adba664a7cd81fef3d33e9ce73f68c3ced4c3f8dbfb011e1 248e1f7a6b6a546e0e27d42f28cff34ac6924b230656f2eae9bd4d9a22bdc47b 0601a3b05170eb6352c19a94f13c9131b804f3ac5aee280fb8996bce4345dc24 94d04b77cf495211b7746689091cbfc2c1520741cb7398dab8c59169de4a33b7 cdef5fb6fa6d3b79f212b5963a3878f93acf07ea3b305ab9a882bbd1815e094a afe1f8dddc602be44e9b125904ab57ec529ac75776a9691f4bb1ecef1da2ac03 de93ea4bf8fd593cda1ae7fd54cd6cc9ea77f937affa4befb4ac4bc966dc48e8 833bb7b13e47689c083b3e9dfa127b5ab8f46228f1e3495c8d4c29200294ed80 20f4e339cd09b4756edca761e7e44c931b84736e457d3158f6efe5d184f82450 1829cd0d51a628533aca699b6b33031251dd74663fbbc7c90f07ae6b86b217da f485c6373d087d305767cc051934be289aff15d1735acd5a04ca44ab56734f56 a725343b33fe68c4859bbccc0ba57adf56ae4858dda0cdbc4ea790c42c7fde7b a8dc0d95dab0b1d351efd629b2045025b6ff7365731bdd38d635fd90b497d61c a9769d38ccd15a0925bbe208e88f92f5a1d420956b396334e80b750dc7ebc2b4 de1162ea6c02106980fa706d483c241742853da626e4372f763e5858ba36a4b4 15bfa38aeb2292774174c0c04fdf970457c02f86541a90a5a61b54283eb36375 799f3863c824548f684599c572cc8a132871a6e76fbaae8b9fcd2db5b792cd14 d5291763afb97daf876f53f1234f15c2a3c043d23ccfd1a1741d153588289cc8 53824840ddb29b29de42970fdd2b18b898ee147b2c7ce1b49f2d79269fecc847 8c33f1bc6229e3a7df73cb11aed6fa65f309e8aefcd5ecd5c8d9d3f95d6b3d93 759c2ea9e4c9a33929df8529120ea739ecf742526d9be7480431440e4655d110 89675ef293a84cd8a6d3ae1abf2cd2a876a6ab9744a9c105539a5cc78bdb7daf 4074ee93d1d60146b60814aa2ec7a5ec7c7d4306aa25a7f1c21e31fe437ffe13 03470775dae0a3610d67bfb8bf5a12837bd71d3f5abaa79063e8fff32e415e51 707ccbabdeedfe6a8c52e7e6485f8b70d13609d1e879d7140ddc951829acbb0e 8246c0e8e1e52388c1bb5a8cc53288c6ffa56e88d1b99ae0f5d83c024b89d9b5 bf14a2012e449d65eeeb42006a27021b1447f00a8baf927b5426c6b607c46c23 2af56b18ab383bf938256e8daabf5a37d899407d53ce5ad3f0a2fd70365ac609 998bfbd4b9de7b782f138b574a7cdc87fe3d7aa77adc40a1e6f23475397e77d3 9d2643d3040bbea4f27ca6ce5f9609fa9f5b12202641c8faa578ee5c4b48ae57 4baccfacb3073feed3f772a49ea75a25a169eb3d32afd33efd0951a27f78cb56 0dc4edf0a3e28baa9adf8ece0fffe4732df79a9bf6aeddd28bf020bae1997864 f17d480667a2939b41e9edad9dc12a2ff91ce46e5647a57583d1127e56ea5ac6 38b3ea2541b4c55dcaf34bafe159adccb0bbde149823c5d0212ddde0b2aa8655 a81003f5d112f16b0efcc4b65ccd9dbb8acf8a7cc008267059a07db7598b953c 87bae16fc628c10e48876feebe1c5d4f84d56c834d15dbeeab2ff7979a900519 a302566ce1451973879039c7b36fb39003d40a2efcfd4b0c49af1525a338e45c e0fba7e1229cca495a2bf74245bfeac48110cf7653e0640876a08460669a225d 5700657197acd56e980bd9a641124e7c50562e017593548af6b7e4b2ff6cc63b bc93cf2d84723f499871959b8c548ee9443f65e4c5c16e4d50de55d3be510193 855ac97d240f36b0b0447f002f248c6ac832338ef86864a44b6c350381d2930f 0241cf2d3d9044e981d454741247df2a0f831f0fa5639f2829fc68bb9922fdf2 5ba7d8ce363f2c7d95ee20d14c6e846072f8f04c529cc719b46d3b282126267c 6120e099078b8503185be5514309a9b0f5d3ec374044fdcc6d1c8062dc626cd3 d1a6fc5eb17a36a64e8ad4369a5ad96ad1a0eb8f547caae0e3a17a4ec9eecc89 90f6f82cb0940f73579d1a7aac9ccc6311d05b10e47a8bc35de7bd6e9dfb386a 0dbedff0f739ade81bc61081d3e246f1f1579f0c40b796b6f03a6878f4264c01 dc39809a5a871210948bd76fca9731287af7ef3bb74345a656c38ea1badd87f0 a26742113e43569ecee2f5bd52bb3df393e9906f647fd8389bef8dbe827d1686 35a6db558bbe2367f0ff14388c7f9e9ff1b4820d1633d816c78aa729a1577bdd 9eabcdafc47130bf4bd2747efdc5506ead62a41e31427cbb89fbab3ef3cbfa6a ec27f3dfa5afc972663dd6b81d249d28472c2c91293acbd7d3ab3a0b0fb05966 85d4b3dfd17a818876d828aad0575806a0679e4c9b61d611f258576d218b4330 88f04c186ecb1f8206c8780aca1d2c725d3ea8f071c0de332565f4d226271c10 c21d810e03879376e4593f4a229e74f700ae06c1c2da90ed3d735056fac69b25 32edfac75e2a7687d711ed0c3dd0e23f9e7ff6b93fe1d2aae467778a0c57dee0 4f9c9c7343ac35f568e8dc47d7f3c49047b2fdd99ade7a9f70cd19225feb4660 9dc77bb99fc54d3522a6d3d67437937b325560e469d8ed77d415d3b1b9c3f4fc 7a138a5b496207e8095bdc5e8aea9b5f40fec94e7144d5a1fad7ce0b56f3a920 f0ed482d403ecf144cbe41a66a697fda9ae21472ffc0905ada92cdeffe66fcc6 451d7266c609871394648349a64079234de5da8ee09adba09bfae1b065e33b89 4f1ecee3c1faf9996b3fae06de4521e7ab7944f124f76bd729c48ca42f1845a4 036bbe26571b2e3a00b4b469ec5e7fe7ce1677fcef95aecb1de5ffa41f8014f7 1fa2f8129c37e282165739a9f3643cb6d114d4178c18a0bac1236059f421b289 cc038f307a46bacccff02dcc41a7a94fdf805174440aaaad1bb2d7ef34fcb331 3dcb62c19885dbd3540f345b6d5cc7742e830bd6180275e79b774629ebd6647a b2ac90a730b044fe92c20486bd144e557267db12d3a1a381c07a7604fc0061fe d0a93c97b8a95e3d96be10c27cad31c771deca5c91d968d9f839c04583f4ca35 d8ce51465928a9c18b996bcbe86396d3c49853398c35a26197e03e13d6944ea4 870b50432061cbd548ea5b01d07922d46a9551d02ad0ba653b8156377ebc125a aa4fcd7d32e0693eb56c6278c964e964d29b8e11d61f68ed2d3503bf8c0de76e 43befdb486004f5cf5eea43331665c9c09501ab404abf18df4f2849f6660a681 7e22898e4ae8dab1cdc7ca1b7007c9bdf503d476b48e9d52922c15e26db6ae0118f95c7a8c779055811849bea12dc7b2c6a7a32e1ad7715baa86f0dd6b731f075d58371d68aab53f46284faa1ab0f1de7b5e98895426232954f9dba83aa1070d2b5f4a65d75dfd85bc69cf3f0ad83e566dba3aafbf91e855dc86674c6ca88506662a28a107bebcac87fb81fe8cee86349dfee065368ed593a05a5bcef3daf7034759a930781b80dac6085013ad37565d7e0fbe66c4be036320c728074a45860afedd66cd535aa3674e023f29a37407967ecba83662ce1564c4927ea6e3700a0d6760bb7ab68da8de7b4735ce56656eb366758c24de4dac47e02e4485c99ec60db8577e903a38a9b973edaacd1590dd4d3f72d9bfa415f10f41945e1dc6810d02d16664926eac86a5599f1ce173b7e6ae358e8bc0fc5ad9535c3960910095640af65257a1ee4491cf04126cfec14702597ca4c5eca355c67f17d611af905953045ad0d1d3bc8aba4d1286245434f049a86de1a59b45bf4208e265bb044778e10fb5df660bfe3aef00d48ef0bf51d0b59be9a8625cd3fb09a7d50c1dfeb6686f0b2b7de818528a798b801dd2e13eee29aa922147250e2268d60bf55fa3daf67a021ac30792fb2bc5a4f281102cfc61fe1326e3de57adffbe57c768a766fa0b4a09a28a6c621a4979c5e95bc3a4382e808c01212d54cf591ad3a09b88f90afd9c070de707fb1be16a9f7d09e3b9e345c93f7022e647b1e9b2ca33de007c2d6d5900c29c2419356f8064726fe3d02c81644bfc071ccca734bc97b03778d3eb987206ac59535b316a5316bca044cc02595d37930773b2f8a15dbc2718385c55ee63029ad2286cc0cd5f62f981f59b5194c9bf8961f5af8f3b786a546ff88ccb161d057b504b6fef920aa099c3b55d44cbc621e561424aec7952739726b63975e5a90a824794b5af85718cfb3efa04e77cbb0e302f0420fbc21acd0ed1cdf3da9a3f0b0d0b9680d876028fb78b706d7135a4bdf4751c9464b66f1bbd392037ffbc03060e5b98d3006354f057def8215ed758a19a8796c9c78820e749741d5dbca69b0445eaa23c62ce262c23c1fb0be6954b8a4b543d99d8c7722d5920952122fd1e05ed2524f43428bdfa204b993ab5ec5ac91fccabe667d123bcb80a0fc062265e0d64affb06b8c92227c9594732a3fdce01a6e3172e0a583265d3b759a7254e5200cf926931a25351e446ffed9fb0a1d4a727a45f844f9e13a396d0f944a2b4c70dc12dc32c1033496e7657ed6e7213ffaee3f2b1154103503af2477c4ec8d512054cfddfb6a430973efa606c33350f35093a3dc97caadc51bf62d27e85bfbba605ff57aa7b637c3263309bd798390bada2a316170576c29954edc63773a322dd02f70763a3ae9092a9550c2733d55832a7fcdd92163e7cc4cce495e873699dc6065d2f0c36fa7d67c23ca65a95451e69d277f469de8f8fa309ab2245edb4b05f02bc931bdbde4d5b25e8b42101f4eeb384c9db3ba4523d5d9f366aa01872c6810b04804be65f8b8b885df7c81c4366193c97c89685614f730f11c396a873886d04ad278fa525bf7be7538bb66a620fb7c1f8024858ac86f06df6594521fabd010a3277042334ab12f31aa68a714775f0714d87216c2c23dd91e3adfcebe130e00ba40c7c38994c2fd6be120b40fb89a7512c665a9d099c66461b09451f69ded407d391977076b428b51c08bf9ecadfe7d75fa6bf87bff375edee7309a64033730cb6fb83c96e113086eac763d0ddc7ce533f9b77987a9b63cd4a4997ca09a221004753888257c4f0d9aa4680bfaff9d3e644be096541b85c4006912d3a6749730a9ccd7543cbee9d372dc9ec486d4e4533fb64016f6f5c0b1838cf555ed4c2280dd6e2832c0ec9d87dfd67d58ef0b400e5fae52c6425a4685dc2421ca1b123f4091fc2e0838a5b9be2fd858c4836a7cb7d8ca704ed334382e9c0fed8617c201901050c85ae0e26e1787ba4c3969372ccfe0a25424aeefda0becdbd2eee7732ca0375cca1c9f9334a01d145ff556c84888106aef8a286fe1d0b905a6e6bf8334b05a1b0b62e9b9489ff42dcf0d27305108eec802dc904ba7f3d8ef9076d53e3eb0ade190d34d0c22c8455972f58e0713ebca6128d7ceb91e7602405243c0da7ac0e00c64d8452d68bd818b4fd61f535649e9c25c887790972231015d7762ee9e50fa8cb9ef119c1cd911dfc2904ad10161187c5d81d5e37533af6d2abe0450c7801284edf88ff97ab693a21f1fa6461c7456c89d9117c9961a70d16840027d9c80d00d7d148117fc05a99da24b52d29f0addb33c9e80a40c6ff8a70f4c775d3c809f170f979ab65ceffaea7d2e9dc198a2c5cdfd10a05f26fb7f8665c29d653b202c3ed628576135bd8bc9b5b7ba394080a45e8fc130666d196f522f670001127030ba6d88baa7f9d0a87cecbdffe1bb7ef84de5e849c14baf722348d7195f2a3084c53eda005c2d425fd37e283d824fdf80ba69323cf634f3d6d23695b7cf38308df81b665314476d380e45a454beadc1e95b9e1d594e0970269f07929a2f064076b0a4370cfe06360e2c298918e9129c4c53c079d1d1d8cfe965bd9a12edf1e0615021d6992f257656e8832da35c069ecfb3bdf6363079f6680c3ed181ed84e0bd2850408555b1e3010abf9597750d11d850d80e1d4e7b56bc263bd0e16139d072efc1c36863a627ca88e841ad4b0eacaadab62d2d10d86cf39705ac94b34ac07091ca26e8ffeb7237ca5a79fa2a345f2347988bfe3c990038075a2aedcdd420b58de6b8a262226e2b03e95a3a020d74650b287d282a34469012f2e60781ac809b476ca31c4ef8d4d1c53a23a7edb544053539c518a1bc990f38db5406a546901fde3203fd18661e453f12a6297e59e941f005ecf1cb3351f582b42a1b743c8003909564372e0c48f640dfef9cd8d3ab6a1dd1ef53eb4534d4d37ddcbc95fc90291253c6ea0d800ee509d416b4b6076e4e9a07e4f6678fc51d2aa49dd3944170fde56ee603c96e0b306110068cf8878783b32d8fc1b6da96e96212a0bb38d250a29914c60394cfd26501ae03d6b686a1b53c87c3b73b3b211c776a35b1cfa1805c92a6aef0ac2708d56a468013630b5c0983d14b9f6bfa9f8eaf9e8385c9e460ba2fb3ccd10d4d334f45402929008221d564fe0baa56b43c85e4f5c9f7e4a5509cc74d8d132e4a874b7fd036f298c85ddc7a0c18de88b073ea14557bc72a07004b8800a2450ddac41bd2adff621fa59fdff2b02822bb03d4a954990dc44da0703f800c9e31289a22f2c5fc44a4d10e1ee2b64937fbdac4b7531d3d0d7fb38cf02c4828b35b44dda4788acf0c45c13c679ee8a14df0d46047b6671229f5aa09a0c80cbffe8f538ae1fd46beb93f46126edd6852edd598d9c2d4605dfb0c44083039e6ec90818eb6002186844fb8d2ba2ae732e3e430394c2c24ef8ea2c695b1100be44ab2a0173ab41a1265420916d5d7ebf2186e7d87b622c87100b022d15140eebf2e899ba9f1e8de4efd38f44fb529926e014deab6cabdbbd7c2b3ff5e39c057e14f4878330d5154e5a92585f760b298e53ab6418c9d0c1bad24af1133a290222bc93fae37190efd815d90ebe6609aeff10ed9f111d14116b82f1cfd4052002d582d52bae4bac1dedc63812092e34f6217ea56cb0703d8f65ba94f3b0c60e0dacbcf29cb7ed666bd6e0ff81ea09b92c5e74d8d8ed0e4f98b8375627f9db7f04a21e51d19db6bdec15e7410e7f8c4ba6f74c97c1609f356a094416b75f22e40aaa045f684f62d440843439825e74353b01f8ac4878b385d3bfe13784e2824f03513154abb1d5970e655310ce57ac675fd1855e2d98ddb78e49e0b521cb24f204517097f3bd2daa58e8d6fbd93e5cd12d58e15bcd9c1c604c47a76a617fddf30231c255ecdf61a145de971faa1b96f4681585b709ce5d8a12c6e179bcfc09f10f8ff3f2a9915b7dfc10cc56f25ca4bbcac49610a839688c5eb7e9efdc3b11a20026e5629910f910fccee25e64c77acc9cf2073549c054d06d2875a003586e5602256e43b561667d0fbf64577138725f46bea16869b6304e88d9daa119e27dcc010bc6e8a96a2db73a6a9a1ae7ef83299d7ce53604d52f5768cef40a39501dab0e2ab9eaf747aaf683ecbc73f5e94949d5c8dfc9adea2aacef37b08285bf155707386671d0e62718212b6d6f0af56abb1cf809abb025dda1b67aed95764132c700c8df3597e8e770a14554b3ee9e71b47b081e3009d7a120b6f4751a0de454800baa80aa8760ae5a0989e40d05b931848618aa80392758eafe0318de77e363760fd00815e488d6a19a83de6f3191e5641c1f7846e13676d0398011f56455980406a9be60603d3213155af2f4c2982d730c0eb5bea5a2a3178449de41fac14c600852fa4bd78535fd4872f8f93f9679ed1929d4de4d33b5e054d30d328cf8b2e50a842f5a79448758db6e82ca12806a079761b649081ea41a6fd76cf227ee958b07581f1a2448377d256f0559b57f7d01a1e15df9571ffecd1cbce14b7b11c5ac03064af868d6f11cba275681e87301103cbecdde19f94a2513ca5b3181b8bd2e0d375e154a67169d764719d28577325ab3be193097f6f746e2986855c15316c00c9e382e73e1a20618e295a3552cd7da1c90a88c614151158471d449582e9b5e02e178505ae93edb57cb9c193c594ac1b44834714d8e1bc4bc70fc9ba2444bd0020d1007dd4bc539da21c9eb7d6b4bfcbf1cdf4cb3ae89c9cd9195477571a0d30360aeed43d0a0b58756a4738a9bbabd67ffcb00073354fae0620523045aef2f004612267f70cdef576879136dcef7118c3cc61bb5abdeb7412d59657392d6d809b4c196cd0c468149d8c35e3ffb31c0f29b050ea851da99844a341e396dbe8209f9dbb73bb1b2d2e0ebecd5038de1f156ab0fc01e86fec3aba1b8a75c9ca5e5067041cad867932cfef051ee31513f5153fc3be13ad090ce25160478bf99a8a70c457d3bb2014f2352f0aeb55f5566b15241eb3679a1b62e3b89830c24675ff1019ce39eecbad84994aa8da44d71cb7de496e59d51ed1e41e327284b474dba750ee9e70f5a4ccac5a2887776d3ea6e7751305addeb656eb032381f8d33f8bf1f06a2827da726234812a1c32ee2a29cd1c730b61adcb3893cd86d2840e5d28a35007b1134ed99a9b7e48e9cbb90fbac7d6aa6883e7c1dc1feb95abc3100c3c65009c16fce531a46f4dcb6b2520c8c8b6a5a4ec49e36e6366f5fdd1e27011c5463020bab03dd2151c44b24fc8cf54cf8ddb6049e5e3ed88bd314b2cd93940e0daf0e35fd35a9e19b0dd81009069ffa6accff23e20fd5e97e4494d7029d69a3f9740dc6cfe2c98b19b41ec13d9f7d089ed8eaef33fa43be7b14d9986021aec17f2f098f603f2f4f4cb1163f2ca9a49787ed1ce6392408413a5de0e5765a63793cd50dec7ac32cb233561e40e5217e0783d9b27b7378b347c0bcdb8f4dddb01058380df76202ddc80c5bc47c36d6664a1d3c4ce31b848b4be89e7e49fec5a3817b97009acc96c467b2832ba199ad3e50a66f48cbc41cdc480ce1eaa81fc7037a108e0ad17bd0f0505c4f53d8b33c30edbf4f6588c1a4d15b25665cb41ddd05b7545e00f1e6d2342a227e18b7f5d2bb9026aabdde5f2c18460eb530f2b97a9fa8a10d014a3b03256cf63c4035b0f02f15136fb0d95d70b390d73c8b036bae4c126d190a8ce395376366ffa4678e6f7284b6509b0ffca07f7e9df273e6cc0fb7362d48095f1e0d3da7a225787c8b8571611d3ba90cec83fa1f846e2722364b3ed36d3c0d941329e9868126b2765000ff493ffcebe78dcb630cd7dd81b5024f744c1c6201347b96fbe243f2eceb8b374eda2a65046a20fb8384896e3862b0200b2e7b5e061ca75ae58a228f78eada3f56c9b739876a2716f435babc334db4c077328353004c59aab92f3b70da7bc0a230c8532b30541928adcb8b482369d6b939349b630b2cd89450e464ad914c73a7d89dcd33fcca7b538eee9aad1015f6b7f795e48702e0b597c748b027aa516f3bbc22a132f395a8c9271a1b6d05820ae4273ef2530f580f66c41fb8e9d1d77c28abfbaa5b91ec3969f01143a8cd7fc042224832cf0e0d0f0967f78b4f456ee02e5435be6b30208844e884d6c3c977b9802c7cf5580823e23a2d52c1ab53f1b7b1606677fda1729f940deb37d3562de3a6c18149fc05f00f5919f4e8055a1ee0d14dad9589d54ad8564d795c63247f7339c0d8acd602aaa89feb93e39e69a050148b4fa711416b60abdfcfc09bf8c63bde4949966e091d0b0971288d5f69a6d86e2049a5cdba4c3819c10a822d4180d35ad464caf90fa9232d1bd24f788f5b0297cdf364497e47984b934395b2f165135faf149965067d19f7f060d8c3d7c38ef7e385131da8dbdba3259b6e8dfc246b3cb03ee75a02a793ba60b031f399c0ca005d94deddbdfd8a54cf9faf496ffcb76196e9ce180a2f6ce3b4cc499c6337dac00aa3c246bd1732a4c4e8c7e0aa61ace44b2a62390d69835ec2b907000efbbfdae496290ee31ec75a66bc05e6b5f8465dbfbe1af70c209fd4ed07b0f913c661687fe5a2396bed6eded2283f13ab99b4f73482a77a0a562c2cdbafeafc4fcd523f74211a5993cc4ce8796b6ff09dcb4f3760e5ba540eea8b1d7ccce44c0b0508570aa51d459c62462679087a739af344b42222ba3106658ca7dd9b507d19cd518948cad4ee9e4ed089aa2ddf5bed3d540e46d98390085bed79b77ee2cd4105e6c75a80d72193d7ac1a82a7a5ef801db8f5e0ed3bcf0b8bee3c4b292b7f254b60365e1100a4d3ec640859f85fd27c827bb2d05a99430476505af7b01c828c32034e975511372144dd1a900c9d02a48a39f3a78efbf6066da2bc8d08ce5884fdd4d748636d3a205651a9a418f95843a2abee2c99f0d8007a546b97ae01c7a31c68b0bf76ba1a4b95c692e1e21452adf6fdddaa87d0690ffea643b85bd6abc29ce5430f83e948bd99904185257a52233ce8997f24fd090757618eb558912c9e9656a17369f5068a0480d2aa476522c058998a9d69b62809b0851f4a61612289e13b1f50425dbf3a185cc89131c95ea2c8139e6da684c905ed4911ba7c01ad13748808b2ecc1f53794f8cfebe1728843cde9a53776b2d907f1b4f95ddf83c1d5701643213951cf44f4360da09a95a8a633b12e277a57230d1f372c6fc379ed387b0e9b4c655895c35ef8cc241cc6f3f7e1672d7c3183020b82d3046d70967d008d029cec2864ce98a28bbf6eec2e688b7847a14a3845060c8d1be242d484f53cd1f1d0d24bea94eecc6025f506362cc8613845c366e9ec0fd21e5f141b222ff58391b72a9da427e537d935fc9d3f6116c0449ef37a50200d8c00f0c21925534b35dd18b57e87a53a6d7f0bada915b3e02a1819f08a35400c77dbd2297d738e2c1507dd66339a74eebcef09ffeb0ffe335256f44710e321008b1e38e41b35bd2e7f7ef6afb0e104cfb6949eac444b7455a7a99de1a074380f5fdb8c1ded657405030a2bab08cecad3a44473ab94b0548c68472b32a919660513851639c1e1eeb00a87185d85fd068d18d54440c052114119ce614e565c1c0fe72b807268abc2054ca4304455d0557094905b4b69b0cce255491da5d1322304aa3328adb670d87ef15983fe2713222f49948ec3621276c6cc26c0cdd6a7150a649774fcc8fdcfa7d3808da73c2ab2c757978f3ce8e643b5bd6bfabb8628570ea72bb66a949f2c6e501780c4011702746d05f8435a9842f31ddfc4e6ce06010da59de45df8d6d9c5813bf216c67ba0bfcff07c20d55a63c6eafd40882b2d7402eac785c97be13ed87d1496e221da1d0442db26206f637112a27fbc9e2028b70c4c41e7a3ca5bd213146c5472e3c8ad6627cfb25f01776aef1771b0645a1de90ec77ab606ac7a023aef0b45e5f26633099a9e0d51087a9e05a3023d53cbe9f300a3a4046c96547052de29f39cf2614b2052e9c7ac3367f9cabc668f7085b3fb0e6bbb5b3b6ff3db7f67069317dc097b3855b50c20c6209ab4704db9f8a199880b3c65c96a5e3c2ca8c88123b3d3a822b131ba54289ed0b7647ef5d3c0caffa309e13111207df2d60716c54c3bd1ff66cbc62e16f4cbf8fdd35fe7fe00e0f6d007c76ca84f9791cf5e35a3b1e045a91a37859e81b37b540806eeb414e24fd3b60e35d216eb51e8f6f5ddb5f128a7113e2f87213e934258ccba55163215345ba30309e9c306845b54947422b22e22452fa0410110b326de345cf40885aba995e20555974cfdc02809935227f372853b2b614299bf2038e85731c23ebddca67466079dfe3461413b1dafd2cfa1d588948be08918c3c1f6c074feab55d3800cc29503385edb81cf23f67c589e012840edb5d3c7b4d00f27dfc664c9b95200eba2be0624ec7772bb4b607962f42f178e7d258beefbc4670eb05b96e18ef7d8023b970c221851d8281de44b0ec980800a6ea527c9f3391a66ec786fb46afc91b982af01549954004a63f46576ef412124d97533aaa494f25b7d36b1af1f667acce0e104bf45666bb0b367a4eaeaa4ff35a745613fbe3a7d582beb31888eea7e5cd3110be33d0a9cbd6b9349f3b0da599c6314c10672195f4b30b92c8c6a0b8a780dd80d02b1c6b3ca58e6eed9f2c351a33c82bbd36c795342bf21679a43da048bfae60d2d47a30d3998b2a7d95119cef33b6ff152b5c22db718b4b1ac5578eb356ae809467359372f24ac39e95f9e18408cca3de66a5b0e7d7b5be95490e1e4e3c66805e4b9e29c1fa5cfd2936ef4ab77a45de925d4f0bdfe7adc44bb357729cd9a0c009d9fd6d350e025464b03f93bab840bcbf4c4471fdfe04bc44f029c36a0945005772b7991735eea2bea902bb371ee784799fa2cba7e56240259bbe9a7efdc6100c70ae29250cf3465e46094c1f395346519146cd01bf6cf7ae429c9b50cdc2d0dc568ecc78c587747f2ef6ddbe1e178c5986cd9eca3fb6bf7fe4087b929876f0bc05465583b6f573ec4022a031ae6cbd5de0e79f6bf89de1e76b0677ff3002903d6f755e6c15e6486f0307e51b82e5c288475f616d902e8186babf25a4f571a051e5ecb498a78768b142421a6b69b7f99d12472f48fa15c18f8d1e7e5e8aab803f28728bc8ccc4e99fd2a47122da69227146695b6126d3c467295444fd49b58090c0df577c6001f366c063f9601db176916552c64a43a457347140f41102748005f07c0099b34a53f231dc0fb482a32e5b0f4637f1bd9255758354b1000b3f40399d530ebfa4d76e6dd4cadf4a7971cc6037860dbfb11bd496df0a2dc403dbc0683a2f0abe332ba1300dc5c3cb2b2e2ccc19eddf645f5d728cd98af1cb22e29059734cbfa6653bbb39b821646c938c91ca354a6ffd32e1991b10879963bbbd403f2ae3d7d626559b252a91d03bbb8e74bbbbc1d762e1fb75472ca190368ca120941fd92b024d24b208c0c1f4ca6ac214c18952c73aae86fceae598c7f5b66a109346c1c05447c53604549dd8d60a8db18a27ff2891edbac8f092d3fcbc0c13a05bad045b32d6b5c7e71fbe8069eb0677b67274ecde59d1cac5374ebd0aedc8b025155efdfc08eb42e0f228a948652ebf17a9dc03cbec9584c463de40a1b7ef400de0810e6fbde189e024d057c1a01126803058231e821b4ee95f12bf0b876b40a131fc8dcd9bc9c5856bcca828de81ec6e5ccef5cdb8f85a68b479d10b5333c09dcbeacf0858660c9f8e9ae1d874f81c627a9367512999dd895cfc16a543b3205e725a62f007ecad748f9627739f9e56bb71330bc88a12bd62b948ceb5d480d0b1df95c9fcb517e68f08d41843d5568631efc0961894232f6c48ea4269c7c0a003d9648967de86b995f237f42ee94d2d0262c91ab748b21114be19073f60bad096dd38ec94f903a5a43620a92ce44d5c4540a9f4b1465c9787d913f119baa200ac02a79d90b7e28103bed564fb588f7ea7c8730701a140f0296307d15b5a3e5052c57ccb574776486a52a69e350c97faf4122e0dc5a278689bcd684c3ac119900dbee579c4460eefba0ec26845ef2b4ef6f20a31dde859689bf4a09d5d02bfa08667b526c3b1fe900723e299efcccab99d9da467259b0767175988f36498c130bb1ceb32d9ff37d617b2c56cd72ec3a75e2a6f336263a31a5a8530190790f6b04cfc7e07afe019e09d60bf1b0df2136f84b94abb59f6fe30a6bd34be72e09b4061024d77e04e17b2d8b9efc7ec52baa8e3c2558fa3f373b709d643e7e66032e08d893cf276fa1669c3f083d961cb2125058473814140f193559575c1dc5964e0ca68dea57739cb8b0999c56ee367d7f2e22344778a6a2331eb76f7be8395982086327bfebeb7aadfead20cbc0b18db90b994012ae191dae8aa8c06d2ba6af790598f67835742adcdf33aa5db5cc8fc3537e6d005dfaaddc1a65ad98f73470c107b93b08ca3b0fbc1a9b99acf9ec1799cd0fe51cf0d9ab092a4a1fc3c623e5e405d704a7e210eeeedeb3b8974e47ef57f3d591710bcaaf248da62595bc6e1dd102de14bc7b2140ca26906b626ccd777d68c21b4fdf531c193f11932172b6664f0c9d5469749743ac1771aa6a2358155d3563638b0c7d724ccf624089636e55bb0a41431e1f114fce63fdc6fd25a0f744b2557c7bd1f3ebdc36ea00a91cee903a09eba47894754ce81cae0a25659bd352b52250cdea325dec86a60f53e769af2c08166e983d5ed8e3514daf7baee719c1a71d8357e6c75106925b59ba801fb35d00277d0092ab57723f0069e566e41912e9e78fc19e4f7b83109c5296138dceb40dc41e9b89a03cb5b38fd60c28cf67c6a2d556e6946640279d7eb9f05caf399f0ecb3a2cc8575e37d85136d9e169f34b24b230453ec478e9bebea69a3f4c30c103811c0aa5212b365941b380cb406e6436ad2d777be667e90c84e0336ffb41e30cb6cd8bc0276e6eaea3cbf2a2cf3e8266fd44bf0ffcd9dabe8d0fb5cf9cd1fb0bfad8fc04fd890e52d999ac477917126d370e91bc7819ec6ae87eddb75120c906d156745ba7cb3d5e51b61accc92db9aa2ad40fc37f78d5701d8403464c7dbc0da68fc7232bc179fe2625fc7ad8a62b4c51f1a793c06b7129d4513fe7ab51030513bbb270cbca02d16e18d5dc8afc2e6d4926e4858e2834846806707deda9e9025abf7beb273931f56bbd57f65aaa994705f2f8a02f2d24d99d6f64ef50315807a8ee368effd5eec9ecf5b8a7c33890e645b2be4fc75650486da6b091a1e0070e66d4255e73569855fbac8fb4714641868d9bd23780b14b02e2541929d6cbfc047af1f553c5f0a33935f5a9726a98dd943199c6664176fa2529205446d60d50005c89fea09c7d1f687155734e1f334f7b7a288350cac6a7bb01b33fd0f7630e0edc2b4c486a6f60724e0b738e072b586eff4f431e555725df0d58ecdc196f1e0d330ad99bc31134aff81e780b8f0fefadc97f1d4a0cb805dc4281e8ff61d4ee00cf3adb066be7b197d26296acefb31597a84c385f54b50919c4006a49d7381e0f0bd5f1131210ec828ed29fcc5b141d479a84c5c92253bb6e5f6ec3019d244c082b75b72cfa47ce34f2e29fb1481bdb46ad20891bec334afd43afdf8f2d66760cc3eb4881445f76a13264ecf0135fa4b8220b65ded4036379929a9462252379035551c9ffea02b5242a207f055f68e743179e5bb210d89e8710a422cdd3486d06aa568d7563435e4c00f60e6e19c8c81b293346b80d7685652f04a2ad6f3e220c821b1500b6eb83148d00bbc82d80812378dc9ed46fc022d6fd96317585a68d0533d672794facacfd28ca05dbee4a771ecc6faec1e98175c0bbb2324e1fb17e051dce6243994431597f1d04b9257b44228677547713eda044773cc3ec6f4f420f66e09673d97fe0d96b3ab08c64b95c48e31c8ce0b85f317aa2354f27ed3a2c02f920e35e5f24939dd40992166c17b40dd89da44146e8deb77b0d6ab0f05d2f0ac4c8b733d227439d0de0d735fb357b90c9de58815417b4734df5685aba5fc9009013b654e9badafea495f2636c72c55e54a8c73a862996a4c8561ee4304f0e085f54e54aa522185a4bf2dc14cffb0cee029401ff1a1e530951477a6f54a6100ce787ed3e303b063901eafab9d3acfdfa94ecf50241857d579c9ccf9f273f160bff4f4e324c7f4ab43d2d6428e4c9b212fded854d441a615d48e0a0fad5e0e70105f1d97a0e718924052de6586f8e2d6d2e7b68e0519fab315f289d2127ecb201d268a0f75b4c9d523ed0765cc4736d0c2b8a6d8ad92a90971dab1299b1b02e03a8e44c26f7cfe8cc635ba6e12142a5aeeb7e16dbfb831512fadfedb1ceb49a026ae080bad42421b9af72b462fca5ee362b2137fc570246d79299277d2830c70ac0ef37a6921ba182f237a3f89481368330f0fd6b5b4dcfee8d5362aa40ba3e011cb0db6c28e786fe8914576f738c43e7b37af11f65c41d0dbd183b0ea5fff60d1531df50b3245c6fe524b2218f1c3e8531bd0f00b5221a8855ab0b30ee52da0f98bd887ab4d2f829b6ebac0bc26edbe44de0bd3690ec19f3c81fb6ae9b364a0ec810a1da23cb60276abb63be28ce46cc50e32a21c0515def623de271ab91bc0b77b0af5485b631c95fd7f43c0e491839a60169befedf4eaadbea6b66e6c8f20fa08667e473a90249a7dfb63beacdf261b9fe0bf42a84bc773fa21231482b880ca1c5d6fa5a82f7cf12a27a2dff694ac94b7d99395ab233681147c234ac7b3907f84971cb19ea5dd38d891ec763d8a69edcfb348290e0594da4a316cac9f9500712c107db116fc9fdf496899abba2ba6c0fc75c1c020a47ce08a91d2ab24ac40bf31cae4da729ee44f22ddb9732b32904aa19951a5776fad99e9002b4ca6cc303af5437f66ce6fb7e2c5af1b53c4ae21df9e342b2ca1cc11e255864aeb4fb45055943b5ae8382c164dd6b656f5b9833f11ff7a3b0457b31589181c9b0dcef5c02d8099435e86f237b79f6562147aa5907f45947a026ff67f93b515cde8c06c201e8b65b73f8732cca569a9108f5427c0df25a9ab1fe2baa4e06157db0de2fc20fc2b19b50d78a2938fa05c9f55da4988d217904cc21ddc33a5ee7178122e0040a83749878e77961960b91898e409c882547c46a167dbdd0bfcd9b2348072ac80af0c2d08d7b80b147ae24d6c3c971a5a87198c73430e7519a047f0f97807e8f0576d8e231e95bb72e73dbb13e573c236603b3e2edb7cf94128bbd42c36cfec806d211988bf01a5fe9d19779cb9a0fdb53bcf70b31e3edc2d88c595643c322b2076609750e7687ee42c4957f4047ba6752b627a02e80d10ba9816e70651e6879048599dbfa16cd7fe1b0b7fc25e73721621ba1d91624e099cd8b3c82e19576830a43df4a4c0a211f487a04cd5145e5b0575df65946f81fa83e3389a32957ce0d023afd81a89c4a63c68dd8b45bb5d637ae3b158231f23dcc4d09357c75433c7806bd9865f51d31c63728ff6acc54d7a04da40f7c178bbb5f535869f1b5d049d30c0270fb16e96044a97cec883f421cbea130e0daae1606c80229a42ce7e8d98f019b6078a865cf65ffe5b3793ecd1312cfd223c845f107860707016478132b1c0c4df2a42db7fa9b57504f16f4530f4b35a09430a5f4fa3b696d914c286924f60bfc7539f84d91729e4706d0dc196a0c34ed4d4939a880cbbb2a8dd7b5ddfefa061330ddd4d5593d6da4063bae6efe072ae3e69b7b011c76cd7a399a729865ef01f7263175e5147c36022e919a9d3df5374ed9a1ee1d7376c66ca108c8166b370eea1098fe7f8415edddf21011e07776af1aa697ef99552011d7848328524792028280659d927aee2f0ffd23485cab4b2387083cb7cd1ab4c664d5a8ca1890d10a1a7c6dd06ef691a4514355b4305675e839e497293e995c02c450688700f0640f120dc65ae756e8fbee515cd3c3abe322538a7674fc0ccbb8ed4f010b612aa5060c8256f60d3561b19a14f6a3f0a0828927ed79b705a589b1dc7e606e71e4190075c2a3765dc2a67ab8693061179ab56a7b64703d75e7ed76d0907449117be90c70b35dc3286aba4b38015fa90070ba1dbcc0cb38cb00b15d88e33e9d735d0205974876d79d451bde052ba98a435a8ab2d3b610608bc4980f7ab92bd570d1fb0429ba7385c88a4b827ba073cc52784136037d97f1353e4cf2b370f02acff327036c387fa8489007d384a417ef444cb7f4a77aa3f7f84debc6479725a0b7d88900bde02a2f46e2453a5fa0a3bf1de61f1f713f76ee17a6658b6eb0c01735028d05a27334026e2e99c15a4997298c44fec8ee9ac06c994e85e527305984ad5194040b1b3f668ded1e6827d850ee4f879cf9f582f7b1df890e285150bf6640f5000ddf8da170558bc354df551a30235dafe61f6a73046e6a13a77313d90e72e71f01530997f80d0ba6c0a710b2ab94d3d5f262206f40bc08873cf5b12bf45568c30965dbf80b6b45622823aaf2dcb39ebd443abe3bfe93889a1e72f73496dc2e8c0ea9dbeafccb39b5870edf07da26800d527893f066b3e30eccb3b8376795fbab031ff0fcab784a2f03f7dd9714b529b3eb3cb1212cf1c623df2273226f8b944807624d20dc81fe891f27b66574f739be81333c70471dbae536c121ce2c93db590120d588792e5c7252dc5fa0be5a44eb217bc8aa0ff51810fbb1b0e913d2856d02c6189c9b31e16ef0b688ecdced4398492fe607a193920b0920a840230c2d990e88fecc96b207515efc8b3246d2be7822b636db4f35897046a34a38d55bcb2d0772f05e861ced71d05b2f19e35d963687c1f8c7d3be245392eb33b77bfbdcc000319bb7456b2f56cc0c20b923a145ea273bf22ec81c3211efeaa01d65691d2801c37b1d25fdbb6fdf89a2ea2af24fe49b472b87f9734d7e4bfb3c4b39ef4c9c030af2fb623f9c127400d7f90ff827ee66ae1802db885ea0e2d231b9fc48293c03fda4317a202562e412ec980b3cd73cc4723c68145aedf38a80b6d07543151f02592c224da4e9953a82c2c1127db2fe2b3e3136a19cb5a31c2eeb3ee371ee9905bd2336880d3200517b079d171de27f6a6895b8373bfd3d4beb7f66bd508f8a00e84c0973a1cde6a56917b07f84758b775e306bb86d5a5fbce0fe5b2f5d61d700c210ba24a00d0a3a1661d93c80cac1d5811e92dd70ecc00b3b59d7260f4f210611f9c2929b03e46b5a1e47c6753463c65bc74694b5c99c59e9399c0a973cab086616dd604a735f9155cf508009b7cc80bf2517b908a46c0e1b7c0e9d4356a50f58d64763e6253bd39e5d9a2a0f6d3ac48cded0a198bcedff7457e3e9e7202300f3d9d4371369e8f58a782842cb0b81252209634f116df3e4e52421e7ffad8b0d72587a434603fb612e88a633cd2b738e183b4f1c4b6f84c66590b56f7d1abc02154c9fb0a61ccb0254a4ffc2e9248449ee14f17fa2605e59d9ec9a1a97126e08a202ba05cfc6863743af7830d528607fc2b35ea2c1f0ca84079c4a0cb9af820c0b35ee9bcc3c887cae701946201c2df30f28f5eec3c3790e12440d32c0658d0997be7055547326069badf071dcad34a0e479021f8cdb690b5d8984bca8d87a0c471ecd6e65590e17554c9d41b4d46bf686ceed1828343cac04fb8954824e61069cadb4756d6ca90d48fbb8a536fc8bf50c6dbe7af4bddf94087f34e8977b0a0b4955493d670ca920f917f9c746287b1ac0591af3d40d218be682004f809f1d0f39de0909e1312820adc9080f640d0ad5b07fb75127588b82a3867f257d02da094ea28fbea15341afd27720a79ba3eb6d2916a414ee307d4e432b85619c926f044ca8bf878088ba537ef6273f7011e44b57edeae7c0c566b96302fe9aceb44b005ee96657b4111466ada3c9bfbd2bf586cf5a43d609a5fe1b47d7e492aef4110e9ad9a2322c96f19042b281d4e6ad52bad7ac313cd75debed97bd9aa4b477ed0e1055746b3c6f335b97814d621c6984879f1b8f2f05b12c67b2d73d03391473094a7913c9ff98661066e0c6f097f871e9d0809865f3aa68f0278f1c4259ee760e7b226302dd38159724ada6809c53f94b82c9baf979ee9ffc5074c7c3417fc70e9474adc34c276ca9a4dc5f86a80b611d816aa3ebd00a29f9377d8c9f78d062068a03d9ce309e462a3719a1457e90de7a81a2128c1de4c80f297013c403286b04836012ea460ac91300d175a424e174c215eac029122d2d9948c00a52c2b7cd05bf1dd469d97114afc2f9b8b00e0417bc39ff7a3283f4cd13627795c511f7c109c601f182066c03df340d2027d82d186740e804df54fcbaa752712a842974050febc1e9e446885533de586fb1a96d563d4e18e618997f3693bc6ffdc29950f105b1da3f612d3901b59eb20723827b09aae61b07faf53cecea63eafb193858d30f77c6da0770576f12fa4a80cff9abad25c1db43d74b03a6a57b73f1987b60520101fc6548cdfd585b3fc42321061a796ed9cd71f932542d07fec1cc5ffa514a04e6f03228d262dfe3b52ef698b1eb11c737ec2c49ed60e4a7d6e47ea71a28ff03ffe6d9159b3b1c55a3132298aada749805a041ba17b56618c79711f59b0d1d03ce51099fc483706af56bb4b9a9fb2a589a4f7ed0b456420bada3966ac25b4700e41ce47b8d89f39e295c8bcea65137fe8396d6223795e36be102491e0627e602cd7ee656a9093b6382f227236782e3fba6618498b2210b8d0d2d601099eed60d22e548c6060a5efd000e4c54dfb7185594e3c35c8cb07d9e8064db8162c52a0650ba44ce53df77267350d85280d33c641b141dafad199f48361327bc3d53e301fc26006a70221b6569e13057584853becf6a7963da6f74d06af8a4f1a08e0d0c20a4e64e9fab3e9c0802ddf51bf210c6954c50573b7b9c1a2598d7e1501a670b3e3473fb95c99573361ecbf160d6925d73357bf5f95487eaa179fa17fdc4650a3ff81ad47cecf39eeaec20b0505a41e2a5ba008f52aa00037239158a8bf1700072126d6602477ae25cd3d572c62baefe434bf78e5b81a73d931be5de434c6c05ccaf99eeeed7dc9f383c1873a6cb482b7409903759bc3a66dca0bcb17575ae05687f35381f2810141b90fbb25cb17be9817a54452ff0c71d3559b47844850a01780431a5b00703b06a1e7a2f4b51c3c064e0ad6879d8ba2ab1fabf48b579ee07ecd2414ade2122e4aaec00d4c71b2b4c35ae578f4b22938ab98895d47f9ebc018a062507568f81e5b490cb0c849f7e06e4172c558b40a5b0cbabc5d67aec8500e6b6f9d8f2932a6aaa7caaacc36aec2023bca0ca1e6c63df777b262ae5c76500 true +check_ring_signature 608bc84f119f4eb7475301eb40ed9b70ca899d0feaf56a0012ed8216a4cedfc2 c1c6ab1d25f9b08b39e4b0740034420132f23b41c6ce06394a7ab5483c94218f 3 9675c922028fef92954783b8d9e2e3caa5d1e9f01dfb7236255d054b866fa91c 9bb4e93bf08259163d1ed703e6d21a6b6c559c23be3d7e11147b2f3c05c929e8 d057fe4f7b2ef5adb7c40ba6631840d70f464b2776e3506a965599c431dcce87 b21b428ed897376c8dd252ac74c45fdc5ac3695f82f21ce315580222bb755b0a91658bb5539f52b944ba040601ffa9b64488cdbfcd34be1b2640363defe580001fc64bb8ce3c2da0bf868c1c551dd3afd1eb139f4e546812c948f2bef496df0a7b2ed773e8e741f3d7832626570b9578ac8e12569af8a05047f87923a46f080932ce84dc8da0bc5459f3ef6098f37cec4c5031ceb0962534a15ccc6ba4dbab006dbc6351db2d90b3ed9f4ab7e7db619738cd0214c436405caf277f08a7b4630e true +check_ring_signature 811b186b8070f193a9243c9cd8e6594eef5b6b0e8b2df8dccbac488aa30664e5 636dceb9260462974be6cb374270646bfb95fb66aebb2fbdd908a94be714dd0a 2 a730020f46a2a7347bf03afb3cd617d8782f3bf9657bc36ac18cf06a506c7a62 6aa566e1919cbb6c4cf44fa39cb01b6a64de2b4d9b180caee7192d4a2ea47a66 cc2f1d714941aed315f3b1c36c014ccaf95eec8d9b41aaef072400fa6f09650b31482ff210202ba4e5e4a2c4dd1d4c2fc103b0a2ce3e6ab59246e07d60b5440f1346d84b36461e05cacfb5b98131b836a8b02f126ca6f1812fcd34e7a61d34058cc55a746026cbd330b14410bad26cf8cf2f89faaf833e4bde54143af8e5ad0a false +check_ring_signature 65c408834f5769203a8e05648f590344072e12128dd10f30f6ae48d621ab116f b0bbe13a94b233b6cfbc060feed5c4b8802e0e6a2c3c914e6c08e66220ebdcd1 16 9a44f0979899f955db91f050621cedbc89dc829764e1c14701e34708a3f81514 55ab3c6c38b587a043d8b18f19ad788e434f408067efc5c054533dd9b735a84d f9f62e5d71c93fa376e845dbadcd6fb61c76362d5150112aa7d5f4adec5d67f5 f961a4ffad1347168035b097f2eec9d57ac3cad0f8b973aff9c77a0fcace9eef 661719f67869d87aefc9a2aa2e1a4c703fb76e3fadb687e8a44971a718d9afda a66e68d404bc4306b7f98d5b1d38fc69d9d8c31a810a660e4929dcf6a8dc432b 46e615e467b86e9535c75517fe80b59c99d15e449735bd460aa3948ed8362352 3bd394fc485286655bf37a4b195c9ce4d4232dccb6897f9208a8bec6f2cb8564 606751dbd9d193f17e9ca1ac320de4d5ece1ba061c79e96e797119488f3fc037 a938d367f41eafc229d16a011731b0411b1aeb1c4525614325213bbb5ee94a6f 2aa79b5a11c5b1e369c463286096d559f72d82a93b98b575ca436e478e10654a fcc12a540cb3419d09dbb3604b6a0ca70e32b33fc6edba2e109090dfc57c760c 81fc6ad1d714905bd672cd799b0f4976e6a25fe676557dd33b3b2d45bdda6d44 a1e59247258eef8d29d12c8b4332a619a409f517cde875d40975a2a8b3f6c98e 2c17f8e79df0d832fed8faaf3d46bb5e8a00dc01ed6d8e590a40258da36d15f4 d3d6c2b96bdada58d4da89c91735463dba3049dcd94c10c29fd7c447ca963fb8 c55ef9b196e7559fb299c6139fc9053b1ca5f3c847db9c30824be49e3921e303b496f03541d421a53e1a22b379039df01b81d07d144159ea236e5cfe00c01205cb120ae3938f71e234179570dcc5dd0458e8f4ce38ad165f02cb11174b058e0825bbcebb218db6d19244e36538cdf5d408a3b6e1bdc19eb22ea62e26d30fc306739a22d1664f02a24c6d4e92afec53bb02a50efd3f71ba0a3a77185c7faba8041ff57c6f8eea8e7f70abfe536fe60ad6fcdac31119bfd4fa1c30b02aaa90e7040af546b259ed7cbfcc0bfc1dbeaf70e61ede41b2bbd21d8f319aeefeb0678400ba7a964c1989608c100f7099cf0d2f816240ea4da1c95f9d6f8c54a3f6bf9001ea504e7a5d8c25a52ab5163b512bd18512b81d5f4581f22a82a5f2d07d55a406fa9b320a247a4c05b8fbeafb26413cced8d4c940cc5da2c3278d049edb0dcc08224093ede938610a7825fdd92b1d880c93627235c9d13b9febcc5149fcd6260953f602ad43187edd70e97dc8cd60389a32386e621ed49a1851153fa71d0c1303778f3eebebd390a0a92d0f5f2923e87484aafab1f55f848f2493c0fb977ded00c680f479497133a96aac67df147b0074d016cd20eacc76740caa0e034cae71032ba38be50306b2d2cfce3898b598b543fae0fcaea12940d6b7429f36198619007a8bd164571c50b34026565ed7a4247b3c9bdd4471a2bfda7aa195db9a87760c7a3699008a5c0d6a5acdad04e34a4beadeceee64395d4b643dfbcda5f515a509047ac5bd0587d6633373fd80e74bbe8de14c2c7a5af1d3a5a50128748cd4630aa026284a28eb86cd8134a0da7b79e060fdc2cbe11b44ec9aacfdaf56e6e9820ce8a93ea05a6ee2e10f71e873e69aee2c674d84c0c43170272b7a57a50fd18003b98b597ac404ce26b24164ba9510e8cb46ddd30828288cdf60fc1586c643a2032a65d2bc39ac8d6bc80bfde3a8ec557889cf809b796e14364982f7cec9bfae0ccae1db958b5227de02cf68a2337bd5e99a7f7cf307c639a9ab2ccb1f68796f0033d648e04946a80aaf8417ce9bd2e06b46f12580c959209acf7923f4f5c7460cebc4ac05530e5001390e3962390bcf2193f5cd8d7bc224ec1e428d7abfc5da0f05df91719eca3036feb9f9b3c8c4925492d6909947565ac0632dacbca256e207c0d489622b05db999f010d6ed3aef62a6bb1271e3daad3be4825bf20391ce80a9aff556379ad79e620dd71b6e4de9f3a5200e5105c7accb4bcbda6156235e00bda1e43ba2fbb4866cda33f4d73195b7ac9665a434a8ed32cef31037f758b6f01409d022bacee83102b4d34c4f961a94b713e4827beea9ead34bce42c22edb60c36bdfbe55ffa143eef73e4c57e9f2000a6446b38ba3e1ad66589ecab4d814808142f6369040d6d3472bc59d64a5bdf8a164aead1955c1c9afe367c00cece1801 true +check_ring_signature 0010ca14ff7f670b467d11b5b5a456b959e66b1043f64aa50895da49bfb501a5 edc123486abe89e2ba3f73a2392b84a4ead3d9b4fe6e3e2b68479083bce395f0 4 971d6d24ecc05fab0cca64c7f47327839d044b0d77c7d5fddd457ffc91d08298 38744f19cd29ea037921a8ccbd886a7a26024fffb4a0837e5332937b3e9bf24a dc084583c7821a25f28f20a90925ce81da2e3b9c469316ab5c4461a264d36fad f6c57e2721829000ab4538a75afcd2fab4114779e4c1b3a2a6a58d0302e57dfe e6e5ba56adb0b500e705d8b49830a2f39c13e59be2ada5e586a6b48861290803bd29b26b3c18598320e198d11c36e32227af4b29c46df03f2ad99ad191417a0813f4965caa3b1f90dee84436fd103bbab7be45c981e2a2fad8350bcff2abd8016f1d6fdcbcc5c97e2ba336421d4022b200143d41c6488bd11d2aaae0fb22c103db3993c42a3cbffe5871d16d04145361af046045d5b86e9b7749f871870fa500d161614040fa479f3c7b9de09bfe52875a894671f11a4d5f977c78c943db7c082d9377b0a9ce904cc20d31d14151cd484632a3e3e6ceb25f67307278d6119a00f5b821e3509fe873c8b7a48ad9e7c110fcdef938f3b2753fbe91b046cd9e6a0d true +check_ring_signature 5609a0951fb58dd418e23165b0921c5ae08002ffebd88c6800f0fbfb5988fc16 7ba59cffdfa5b172650fc5bc48f62a1e3c2e7a0642eb15a11f0123b510cd8831 1 678680fb0b6a6bfbf8b1114f1175afc731d8f6a64f45248c8a0aebd218aff1ef c1f01245bb9a01ac801771e9e1edbc295f57974fe9f9f6f8af233e0809bdcf4dd5280bd87fffe9ab3a1db83b62a946c6d53b05eeecfa28619b5441c3318b3b09 false +check_ring_signature 5d349ee7e31b87cc35a4c2b4302cefad364e56101f6afccf88857e9fd8ec7e82 f06653667a34402136f4c675f9ec4aa7d1a10bec7e6f8b28f34786239f6ed822 21 bfadcf11126bfdfaa3d5ca1cdae8536c7f3237b5fe444fce0186badd0c1ea85f 314d61dcb108c60f4918ba21ed7815981072d28aefc9d7ab9fdfee625167cc80 0b67a488113038b42df75d6adcd7a909da2148b564e143ac51ff86b645f69ec8 2c6f8fd0d974c4172cd3b7425743ec95ebe6d98036bf31774f2692dcbb9ef444 813f5c2308302ea19cd50297258350bb26c770ff0ccdbd523659f41268bef4c7 fde10685576b79c45a4a3461cb783765aec64176d3a65e5fbc5a94e004e82822 c40db948e47f948b07711cdd4c625960d76b6562d680dde3ab6718ffe726c79e ea956d4cb317671154caa8cfe960c9f8550c517574b3f12e83477680d03b8f4b 7b815ef9ef9758cb140483c2644765da0dc1456509d5a40457276df52a078b12 e8a6c834e199a03782c0fc73c9f9db46d52350625e8bfee68bc08dd55bf3186a 95ffd6ed38e80b73e75a104737bc5884ac27cc291c77ea2ac3ed302fe302c63f 297d073594fec1611774b1f55fb8e315e4e15825ccacd766dae0b03df6bb3bd5 74dd128f6456d5a0747244d4b9adb813f9bcfa40852410549a4a924225a6b042 2dd9222663da198763215a89ecf7663f6e2c182c624a684f40d63ec12ec98b27 ca08f379913a78c26c6a2b478a3eff5cf6030f938b483eb54d02d177f7b6a1fe 5662257d17f3572a72c5b87628a188d52ceb82441db276f905cdb31fc7d01950 61467cbe7d70eadc06bae92ce2d9ce058e03d8d757065eef0d1d6ec50b4fa498 1e412801d976cd3d0bdbc7fe07a44b0f5988a57b5055651c2d7af8fb1f2e63df 933f6192e483db93455eafc32cc073567a0dbeb61fdb12da99a450c5267270c0 473534663775f3eca6a8bc9849a03f80502ebd0119efe045936baed72e821f96 be1195528fcda73f151b6a94510941724c1e9eeaf9877838f8be5f1b5506e87e 9979e52c985a470a798bc718aef4702cbf09f52dab680ddd975a27189f411c0215fe45da805430daf04e840fe5f2746a272a13a273c0368e9f3347293a4dda09be4dc383e6ad6e51fbc83375d030b29276f0af514b6a21e805e6b82e49769504f21364caa2070fa1853e06268ecfd35470d2ff9372ec0c501c3bd74314cc720a354695eecdc81bedb6943f1cc7822cd5b8960f51cbbb9a6e304d56504e15e70b04802fa267846472d162686b1ff7855f248c15942487a1ed2ba0086e9615f200667cd81b1577dd0401b2d4bcd021b3e8656492da4d64a0db3d75417222113e023845165fb7c740c59ce53fd8553c14836d8cc1002715de4d73e55a3b6b23bf0db4c07b831d10d917dea9de9949efae1a8a0f5a05d0c700ee33c13867ef865a01399f14841b62803fa5e2b1400ff7faa67f8fc0066fecd2f04d9d26a3f97f8f09775ad6105b6aba4183b1b61119f1fdd5a0847c89e88808a658b49a6046d40a0bc8ec4245b29bb0865e4d9c2c97479d49ba2320c3ce08451e0e4784048f4d730d1495d44bc806d66ae959fd7646e42857dbd5e881ea074ef1274d65739cc97606db8c107a6ee2942645113ceb2a0b3f853d1e4cb352a436c69d0119c670bf510e9185501e81c4993e97e626071c1e113396766d24d079deb2a496cb06196e4c020d6ac91995ba0b1fb9dd85913218e6a361c531d3b24e2dde049796e3d04e2f0091396263b08eeebaae99a9f4ab9a966d0f45e02042cbb6b3bd2c2d8ea37be90cc609f3ba717c928db0ae5786268995faa5f2dbe7838c3092489b7615f18e120292f1b9659d34aed988f062cb8b2b087aa6c19b590bb2d8403546042a5390830062c7dce403233530b58f40c9df6a0e63c146d153e41a670b6055fa0defec2c0a335bb324a2e1cd59e23810de25a95d4a0ff23a20f9e3606f3721cb4c29154a0d763658d3d505a92a488e7c46b211ecc64e3e5112ccbf49c8e826e2f52010490b02756d201c6b4117f1ba16a2bff12144ed25a273bb3aa5d75ae3353b0200be0deab3a55864e33b6b0a3462b632b0abed0b13b007bcb0acbf2de00a9a0553c8032f3c1f959e52f6cd7536db6c9e1579372581877b3d6475e03983ba7dacc78d09d0b90b5020d2fc179f4334a15e12cd94c5a50fde4f185ba91c7eb572dbf30b06108ddf33c2e93791cfc24bbba12293d350d14b392ff3d552fc24e196ee103902e82ef3d731bcb29fd5066f889769e164d775cb00f2824492ecbee8e5e5a69300ced102be1431c6fe63ba4e9611aad47797ff7034f6d61cbb19a62cff25ab1a0aa982339f14e582b8c1b0251f70826b338732b8da67f33f2bf8a5b320a793a509888c5b3e2c03c9208c933e8dec280f7a747663b00e9f438abc3145f031b10d0c86029ac281f614df86ea49dddd9f6cacfc1ca73e9803964124206709421f2b0cc6ee4586d4d083796930662f1ec7e1098ea16fefaca0b8d6244f8457b3d7330e0da74688378725f75fa58a3bcaa4668019c7dfb6db9f82cd4975121c652367072fc223309b38092a3853d1c2b7d735f8724823ed1328ebb5b7c39ac02e469c0e25f016e942991fb1d08922c9f28d79cb9b81cae2971f7a5044a8dec26e49fc0c4218ee9e64a69ddb9d5a4a7a7ac858c90b08c9ab8008087ad12c49cb33144e038bccea19120fae2e1fb0f9e15cb483d4177d7c2221bb0ba96376bd745b5bb9088c65cbf14b10624d37228801ee51af6dee2badb1ce9f3843729eeead41bc570c002cb8adee8408033c160ae444ef46f0a1df366ec29fbd5d00059df9fe5fca0e1c3fa7053dc1a241b3516aa951c164355e01d5942a882f67b6061a5b17b67b01939eab87174daaedfb0942e985525c26906e2bfd765ffeb3314f5be0c0a62f07 true +check_ring_signature da7828d9682b278a7c38ef1d01c1e3670023aea6a391447c91f615ff4cd1f08b 0d3460c5fe96f778ae4cff6a874509c72067e17142655a4591b1a653f23ca4cd 62 6d6b297cb2940396270bf00e597468ae0996827228dc44425c6ad84991382693 643e51df0ef96f8ce941441e2d02c84d7c75f4e183e7993698b7debda288b6df 02adefa44bea4d2830129cccaf053d9b926a02d394564435b335721a267b181d ed30bc1de8c774e13b4f105bc70d8766d5848a7c91f2512e07d225838ca3671a ff4f14d4f0419638b9c033406585c0730aa2c811c1eab2325ea898141dfbf85e 3be000ec2b5b7823f49ad0d39fc1befd1cfbedf54085da45b3394e4f58a96d72 4f0de60c745f5e8108266d6f5682b9cfb8664a137354c48cf0bd036c1f607059 127e5cc9bb858e65dbe9a4cb38266f8f4bfed6d269c0c6b5e9ac1b6fc47ee4bd 8ca055cbce48beca26cd0d19567cb99efeb09cf7af15202b09776dddeb6e2ad5 5659bddad2886ff1ac429a16ab026ecb6e466fd4fb2be56050a2a34049177281 72043cbf1a59183c2599e57b54c234ffba8ab5b9e57bda69e4d5228828e282fa b77fa24882f30f1d6cd5df7c918f6834d0142dd3cb1a753bd000d48151c0c890 5e231dbdd77a1688c29044d52eecc0f951a8ba688778a9597e90561b9e264ab6 e3f333204e9500d91be2aefc6d3ea5e1a0ef87b67b42ca91e3d68ce84f9e6964 458607a39085f3a7bf4af4863ef0ee7db2886dfde1600f3a8f47815b55bee82b ec39c744786772daf6a47a790fdbb5d5f9994ba41716997df11b62e62ce16a3b 5777b32218eb0aedb7e1cedecb50dd70e876ea35e8690aa00981634f9207296c 9c94b3816ff7720328ec3755b3fbfb359b5ec939bf945bded6f0800e353dd123 274abfda20b8fdc2553da8f96309010683dc22e257f9bded10b6c119e0d225aa b3b7ed680f838364764cd7195f7db17c4cb718710b5a973917a6f6cd38341cd1 5f1afbb00a67547f407a930b514fd5fec67fd98f83868fea4243dc4447f662c3 4630813ed431a936d81aa86bb9b4bef42b1c64bff5bd6d1b249919fd3d326ee1 c45caa5f707d6f1fa85d7ce9da8eae5790b119693a912cc53ae8cf629bb9a032 4eb8c9dc326724df2e993b0c67dbb5fe411bb6411d4a5c0bc9d7a17024d7d725 13cfc50214fbff1b4aa1948a85bbc47aacae2759b174fca638c571b2f000ed03 00adf2f75c7b2948016bf55d0d4ce409af2b409481fb601001149f0862a36024 9916f82233ef83fef9dc0a4c8bddf81a7d01dac8a76cc1aa98865f695c33daf3 0fe444e9304f31af8913dbd987cc34d5ae669a518f64f3a2e8e10ea39c4e24e2 108ce3160c93f21bded0a2e728efc980c2c7686b615592ab88aa3a6fe55952ab 5c5d24b908515e0a6b29b67479e3669d4538a3a2353ec4d8bf8c92db2cbe159e bd1cebe798fe4cd2cbc5228b5eccb8cbad07e6070f3645b44aafd787b24d68df 9109f1747c48d4841a45a00827899b2a861b6d1335fe6367ecbf6e6a1c0ec612 2a7a590ef0dcff29d8ae1eeb5b4efe5690637272dd3e51f3e89049485d5ff452 53fc1391c6fccce8029050b70d3ce48573d3cddb089ab85a0694cbf2f2334c8f b9cb08604b5052fe9adf65bd2a9ca477003eab947346c67a79839eec3c9db6bc b777c18c244c800f30d776f08d8528eda9e2ee871d35410d20f7a9ecb70b0a76 f0058df06f57848435971dd8566c5df152ae2e2d41244af75da315e67d2c01d1 461083fed3bfce6c61f34677c3bb47c67171554c66b35b9763efc4df18dc2734 cca54e3c02c067e046219086df83a077ea8500950ca1399ff13d9b3269106e81 0775f8d675fec81aebf14e5d1fda459f989f523bdea0435f23f721c5088eed2d 591bb9a0fc3fc9128a1893d345faab1c32048c8ef0cff64054053e4439e85a8f 7368fd567e02dc63e7eb4f18a3e9bab96aa21a4d0123055edd5d99c1fb12ffbe 84359ab8c37f73c05b6fe3e2c8d1b478bcf0c1622d452a193f4d821d3c8496e1 3d8c140cbd1f77f9917ad523538f756ef82a230e31ee3d0626d09f8b891ea211 7cf1d2c06c1a570d67a3274625823888aa7157d878589660d5daa499c4a73017 26ca5bc103a85c31fff9f77c6ae283d45577583e084f90b42a867e1c67970c59 53eda164612a53d9be831b99b1133e0fea53d99bc67b1a884b2d7021887abac6 498d16405648b276ce9f6e505e1097cb2a6979d8ef6b8052f4b379bb3f48f9d2 56f0cab5915749e2d1dafeee4bdc95a9a7d9f414a9a2814bc57cc5c2dc468083 70fb7eaef1080c4ea4adbe71ad5171ac3d1af58ea0d2e6957b08aa8b3220bd4e 0da6c4bcc2de0c93b0630507157e02eeacb81477042cea410027b2a5fd44afb3 e5a507f18dcf025b14d47ecece875c9d30b3c6ae821bfd9c8005f1820bfda022 d63f4b6a46e4a36536efe22128a7b655bb362cd26c7440ed79191f6373c41247 2831741aa59637a440c1e9516100e8a35812bf41c6c1a1b78ea4be4902ee7526 027f451c90bcb2f28b342e5ca9fda1b81b5cadd77e595c32dc499976239b6cac 25dfb4cd3eb87916afd04fca9bcaace861628ed1752842036c73359deebb287f 2e50dc2c267b834d1b2ad9132cbebaea8251abe3ba495e304d7c7c8811b18f7f 8e68400d9030ab4afc402d0bc92c0473468e7807bc30390dcff1ce736eb5eee8 69868602f3626dcd385407cee0c17948451a8c65b720070f85566fe9bc0d4bf4 714b435547eafaf7252979e4c0b286dc227d3cddf7f2aa0db788b0b5c353361d 777f7d8fff3b92793607549c8a65642b4baadc82a8d30506c27c4a838d8380bc dd8bf31959e09e353a335897c1d88c84afe5e51eeb4b1caeffe487169991f956 babfbb3f1968d63f71147b17813c6f01540a3faf613ca93d43952772ceec6f0ef3d7a7a61433f68ee27c7609c62e43ff28453d61c6a8524623617215e160b40d5be6d9108e6cc38677d3b89dece0474d8c25ced152b55ce6fb99aaeaf6ff9a0e6855586f39c22f7320570c9b4b70badad2943709e016a99e4f64a9dad51f8e00306e138cd0fe862da398ced566c9bc225f40cefb78a86e5610a317f229bd530895e1d9da5f2dc6bfe079f1ef013ca16ed2411fbfebd6d21ca39f50d3c79f8a0f5f750532c01d859610210dc099793ec27a3ede5d215352c9a9b255b983dc200665e8b3836fbd5704f6c5d8982e77149acdd57268f0a3b7f0fc31b852f728ef05707f1a240fd9d3aadd40e12617c44591df9af913815f0b7be24d0b863e44d10277017546234fb811975f3ba3843b86a3101b5be13e3b07c11f969a5cc991310eee53b17eb24f9af6497882044e492dab574f24bb2fb7a00216201736e29f10064f90c0bac1dc8b6dcfd681df918f3af4d68755811e50abc9133ef75926992d030670f7a05277369f140199c290c438d6c6a8e1bde6ec4b989bdd9b9a3bbbca0c2bd98fb124b2885b867f40a9425d7a0334a2a44769226c1d769a00ea6a443a0382d95cef199b504d30140b05827ceddf36eaa3bb1db5d982a1d24cd819919a0d6e52e6cb506fbd196553eeea3b09a2856db949c7201da9a5e8908552b3bfab0d0ad2a301d89aca8a6533b1155e1f94b7a06cd2ef7254a0f755256797f8d5a90e0eb5ec4d973f6537f09b2d58d8e33fa28376ff45c634b40f1bc10a0a06309b0b85fca26fdc360a5d073dbbf7e05aad4f8bdc422dc624e6bba45a43ebfa90e30a50d1c5380785857944e0c5dfa68b2b0ab40d6715378eebc6c494502dcfcf020f2c05b8b8c6563bab2236c0ca492c38577f97619eac265ac273211ea81a9a0c07a8119a1fe654458dc20608bb9779303fab689d87e134875c4d6554127c35b10a2835655d6f9915eed595886758385ab4a3b9c208c9fb86f7ae94b29e68fbe405abcf22bd541be0ae87bbec5b833857a1bcb2c7de63f07b489cada19a83d9090dc1836ad4c37cf71eaf7ff3618cab5ae1a4e61113c4fd55782c28c09d16b3560e3713d72fcd87e9986edb4a15cc4eec5231f4828f6dfb87c7034a52110ae6760272339ae04e3feba5a9388f17c9c974ddf92138b0ac3b701b497900ab4dcc2602478f4ec21affa72a73a29b77db0e1b538cdb689feb81c453b1b2e10ce333b00449c758137bf16bf242d6ead0a889230db74e6d795c32fbc3d277fcb7743c1e0fee1c700a3ab77f61c24c9843d5875e6810e0c190bf4aa45c1a7715bd51d16f0c9eb5ad77177aedcf0d4bb1cbdfbbebdb671fe573aecc20a9b01c9d469fb76d0e383feab977e479ede6a2a65442169164b3b7e190e862e41b2a99b95589887d02827190f40673776b7b7b5d2bed5f21faf5f1a69f25cc93737850c4d24c5b3b0bfb4e99a178406ff8a2a3c4bab2a5f6fbb97c6d016979c26220ddced086805b0a68e12cf4845e4d0731a45eed10d4b97cbcd57cf33ab86d2ba3745432651ef500b30369b44cbd9bacf79c2dd0441ed1a525bd7387814a4dc0f50aa0e8d7d3af022eb2b54cd4fd226e919efda5f27235554f179b4540b235a0f74896c532eafe030b6a6997a90b1fdbea9bd8964409f431ac21186a565d8a56874e69b47c10380396c15da2871cbc6147522e60e5bc8c60c14011e7bc101b8072cfad5f8ba2e80d46b27f4d959aca4129a2815910703dc2b9867f30b36be8d6205e0246cee4df0cd6c1ff365e52d1ed58679a00e53754ed35e7adfa607e89db9651f053908a8604b4f8c99904767b3589d14467cacdfb2043ada8e49ec14cbb8ed183ec8d65300ccdf16e6298361c966bcc60ab8a24cdc3eb42561ed8ce3050d7c0bddafb9a020ad4be5677dc3f057a09544e8cc02018508db69a7a7b66eb00e00d868df8b51f00fabdb46ba8b869bd43252f4764e4bb8ce4c2732b36a33bccb072308bc1f7000edd43ce02dcceb53fa213e6e15c2355af1f718fb01ec01ad9fe42a221c3cf6e09910301b04efb7c2dbc84a5a81fcff09bf4aa509e96482ddb843b7842cf44050eca3329e14c2e28830cfd75b3a488bdad85246b047a6f174ce799cfe14ee94e0fa0c7bd69b2fd333daaf34b33bb5c2e49532022186ec759f8b95b32e18662ff03f0a63826024c9e43dda21bdc014d849229e5fed2a59181d31eb4d4b440cd0702c0334a6318c842ffb30b20258180cf2214358ebb8fb1df0ee545cbed85accc0c84ead3817c398b89c3dd9b2781ab705f67c3b5875126ef62cee85db6000fed07725d49d08bb62c3788dc9b6a6c9bb599098192fb47eb1c86d2e6b5b81958810073aac2bec62ca5aefc4fff2fe4f14efedd3a5f22f2d7928e1173c7e60c8cf80d769857977b9fde9c6306e58a581d991df56987eedfc4645938284db6b48dc902098e65673c48af96ffb9c2df831ea8979057a158484bd41e217c128925c3bc0c7c74da9f25e81131becc1d9e8cf96832a987532a726308f6a6cbe14354a542087363272629f532ccbe232daa99e5d19841930591a1e0da9c013da87f9e606800176d1c382b8e4c3ca948c96089faeef3e1f8127220db73b4ab2216497ca2cc08814d820a27516995e4b07d50abffbc56671dee374f2599738205ba2221e7a8090dc602cb17cabc786268151843bff89da8533893057a9a03713d8bea5fc96005d6190d9e87e72ef2adb035e1535cde758a4d0b0b0dde83a1ca0d0c127bb1fe0a74d6f86ad3cb20ed7a38610127b065dfebd7e1a61f9d3919853c123a1a1d9400331f06affcdb97f61117dae6b1fcf602a4b9ef8f77905ee23c8cb4b03e79100744223a33b192774bc24c108a917ef7696f6cd79012928ad4c6c7adcca16a61081c764f31cda477a3e2089e8a470c034b2563cabbce98547dd096edd06a07f30769830d7fc1caf99f29d143a862080a9bf915bd3f05efa8ab0dbb0270c5a09f0e64e0a634d8ba06e8fa771818b39d7915e4fca53748dcd0a01d389c81f93be30f5cc9ac01f26574699965af38d70f07851c5a06c454fb5a079a09b799d81d8a0b29b5fd489256cdcb93385bfc705bec942741e576a0e81796714ddcc65e95a20fd0b1ce0190412f40a950c47876389a7434f55695f782a9d17657e3f08e55310df2737cab1e6bd3f386319098170a2778c63ab95b285571420e9bd483de9c5e0042c43e4f54ce4e15586f4cf3cd0bffe3bf64111cc11c80094d3ea55942b8e80b95c4d7c5e4a0ab7fdafe60b3ec7053258333bdcd1d1410c1f32774fbb0317b0a5e8419f26857bf30a271e6953281e4574d8989b2d392b6b62a901438fc1c2f0d30224789f543a5e46f1bc37f95197ac6eff6407684f8da5ec0034f36e2bc86059a5a1df48c6ab5f8c6ca75d9b64df2321b7a9486929e66a773e99263bb7813064712cf7806d5b00ba124fee7118a1b2b0492012bf7eb292eeab8f8543e4b8e0b00907bed9e98a3870766fc85275969edf6f27ce41f1cbc2b55be65d2b9fb7900691ee6dd62181a035d033698e64c2ce3a0542ba3e7da6aad9b7cfea5b031340b9e01534c3778692fa229b60a4ee47872c801670a8bf6ba775886f79544b4ed0bd6d5cd438ac358cbc1c06a7dccda187df2cffc0fefbf29916a26a77c4964a5033ae8288ec21671bbc7983667d4adf9c4a524c575b47aea2752eb74c163a2cb0660a4237ba6e61ec7b1ef407b80163679fa93695763aa9577501aa29cde2ec503bdf92da8a31a1d9338a75496a68856d9720d416fe473f35b404f32dfe208ca07998a3b07258b6b28ff80706c2ee445898c9c8963bf4b49f78e678498dbdd9c08e0c4e18607f2c1320b09eb8b2059f657125e3c091b6d51d43522b6f1b41d140ec9327a62e2f57accd300502643b3d9875c4b6afa2ecda4d5b1e5217917a1c3021087427d100cf32f6fc86a69d2b607694d4ffbd3cbacb164aeb279623e715f011021d710dec44dd00bf0cf2340667043ba9b06e9b8fbb8c3e3c657e164859f02655590499cbddd9cf229348448c325a214177124b53e3c6828635b697ebcd3084bd41325c0fb2f1bf7acd51f17e99c8efad8d9080dcda6c36a12e213de27fb00ac622464950a4a2d324d79c67f41dcabb47479b2aad642452c6b8cad9fd89805303e5becc068e39e40726b53ef1cc6ff17f9924b7553f5b79f556210688aae08409d2d868537f0fb3c09133b96e6f7c4bb755f3d65b048f337118ca554a4e40d69a917dd893c7544ea079a88bf4be1b97ea646e041bd48f258f9925912d4c801095c3691d5e327a976d87f90d456fd9e8414057c72046f1f20b8a7c8710a9e0559624189a98bb11372322d240d458c753d09dbf959f452fde2f7be7df1bce503c1ff58c8f0ddfd51782515c4bb28fbe423307d771ff291e2188841d59a805f017ba259d35a0c516ef512da9364ec0bcc8fec5d2388ac4e977492ecb737b53405323472f05592a8d33cebf8c57c203b0a4019d1058c3e64866f34cf161eb5950e0af03fbd8f9d1881eb0d22120853a7457a5160c810c094af7f5776dacdcc2c0f34765919a80a32f23cfb37e0c5a07521054351cd7296b1b95602699e0e6bda0b2dc88b8a7b3d8a2968048252fce0a5f305cc02d9b6e32030fe3f51d6273b100cc89f57068697af536217085bf2f07ddfa60de8d21276fc871145b2eba0d46e068e6015050efc7e35be383fbd13124d6804f408ff66c735497aabcbf8ab30c8085c41f31283a587fc0303679b448821fb2952ce54a63db154e97d531ad7caad06c936054179cd47199b4929dde1756eec7e0e016c213bdaa059302c4f630b1604c21dac35823b0a8808c322d88c60201cb7222554c9536774fdc1a8694f1e7106ff0f1decbbc6093e5cd02b078735509ea61bdb31f61aee9b98ce208b1448310c010b07095bbdc8adeb77b28ecab9adba42571ed49d519aee744b5937e2f8bc03f3040bab0220d92146195d6024ea988e7136a1bc6fce44d2bb510c5c199de807a75823779ecaeec59707d8cf6d60497bafee546f32da0fb6f574d5bb73840b0bfc7dd3b7d32ea96a5fbeca052034805829512897a1ac68e3baded74ba2ed290542ecb2b58e0e0081dd1b11f9022e7b027bd74bc7706c5e7507295b1255fdda0ce3dd3fd6ac2658b6b6e31cd02b907ee0e3e437b66d126b36e9d90eb15906ff048d4ab8bdc308f86d14a47ee848dae6995880b7bc15ee57f874dc2aa500d2b5064d9cbbcae01ba48389f6aa94841aed9c284f9ea123d8ed7768a9c83be0a941047d62022555a80fa738b4ade7b803e247c67bed889fd361fc1a87b53293442b0eeb4236c5f7d1a9421e525722e963a625f9c2cdb0c40c28dcf3e7b097f58e7c0a422e599e082598980dcf5e5e2dd271ecb5ba04a79b50a2ac59188fe57e77a50feb5dbe2242e70d497065815346d93aaeeb2938023e776d5e87ae32073671c30afe43902c45859d99a94f67ffc0a9d8ceacdd9bb8aed1588587e7112e14c59a07501d969469efd957d73d8944c905d3e7e7f3d895cb56320b16cc9b47ddff2306 false +check_ring_signature 6b27e72b341fb0728ba04ddc2765a49dd318a0d3549b4aad9269c5e04d8de650 a88d9b4b41c7f711b3bb10034330988d30a1303d9a493ee48d7cb9cbf4486572 163 fcf71fef11f3ce324165eb7a20b0e0f1ed598891c8a59b702c618a99650eb59f 30c816a45aaa3a848f25f121b8a9fb1554d2bd14a9d8f01f8006fcfcbbc25fd9 a890d8ceac7092f1c160f4c1e6c596c5840015e8e967a336f8209de95caffa3e da4888590061957df2147327682429fa666f84e9df7616e393692b28e21f9c76 755a9fbc64b86a54321e1dcac519be5f831f992956a28f9d95c4e9921e2e036d e8206c9ea7597e7daa6f5c8ec1287698ede0cf305a9d781f250d36631c60ae73 7c50863eeb87937519a4fdb168499b5c6bf3944d9efbd4f0d98c1e5d83d6483a df30c63ca67a63bd166277e83e460b4981e82fb7c63a3ddd6c24db3495f96809 02b6c99b4be49099f7f447c2cb9f82fedf859ffa36bc3132ffa5e3cec4611938 0a5fb7903311ba232fac5456482d5af399300e628fca85b5159e12ea43744ab7 fd22ad091f90c38e45d595904495120507640568ebf30947d935245bb80ccbfb 15f718132db4732bdeedaea67d4ab6ac3fa15f81498c4c8bad3b4c813076a35f fb429bda941300377ab7cb97aa2b568c07a5a3b281f84bff423b611f422ad007 1d29c3bf7205e3f462363205a81cd09175d98aebf7541d91b609b1a03baf0557 2632878c8c816d4a7bf987da9b84a8c4c2ddb59cd8b9d2ff4e07c0a9a9799e16 748eb2875728426259228ced6dcec075da20ec6fd36f1f4bddf544e3c02377ed e0e904a6bba85de6e6f56047992dcf09613235fcd5a3feaebbce88f6b727b22d 602068d1edfd2b1136711834db896b20379a47647696d6dba1095d865002596a 7388dd8e65a162630eb16070d3bf9c75a68afef47ddaa8bfcec7b301ee2bfc4a 8c4e80baee885292e606d936944f40c5264f342fbac2d4a1eb0ef00dae58f3ac 0248f9700d7af9432d82c0bc7a26d7b03c6930409fb66dfdd4f4db953f6b6ced 83237425202e730e5a167170b649556b3cdcda51df2a732239ede94daae8164b 0f9042ae3ac9a16172e432e887ddcd1bd1d45839e5c07b941665071951242e6c ecd1a9e39bb50fe352411a8ee598f987dbeef72795982e83aa972961238b28a9 13dfe0959788df372cbc26d29f02d7208dbe1201b386fa92f5e09b31a48f5476 27e56e004299be14390d81548a0035a5457c89793cec784d2566e34c28855794 d01f27cfd8ac185cb9ada359e501f0ffd40fded439a1d5cdd474d5dfb561cc4d 22f1965845aa3a09c3b523f670d9f9ed220b22a5208087045f055eeeeb96bfad b7bee99adfc681af49bd51896b44620b7d06e7d0f7918124141d907ff0d317af 3807ad02ff17743f6f000a33c14e556ebe1fb904de217e268dce054588872973 ca5c82f80fc998e9877f94c3886b1805d3fe66982fdeba735a8b5192fd9bbba4 5bea38cfc764222127c7554e45f4aaf1e6acba0b8108c3adb5809b3b320dae31 3b1e99b21ebd7f5d1f9b0ca7485786b86d891d54c3b5b9474641945b8c5ca9bc 6cdc626fda945b0fdd4dbfc428931d205e4f1c78912cb6f21d1114f6c00dbcd8 e2d8424928513362f25c65b9d53f496f8f191ecca26502452b672593c6664eee 04975b3f4f5b976307763ce7dc6e36162cc78991e6e0322c8ed122889dd6d663 5e8b5ae1e1f3910a885314f1f60cf8b81e84469da41fd644bd95ca50f87626ea cbc90acbb96ab28f0196c279551a965e93140e02a0f7f3812744966fa6f4e2e9 ed7a87063d2cc1c46c08c53bdf84487d3294cec59a454cb4a2ff5adf2449166c f28a5f29c324f69c87f2831adf927866cf2cdcc56acf2780ba908e3d52525f18 06ee444421dc8594c979abbcc7af406e608662ec8fd5475474d38350ff6acf7f c4c5b68a9bb6893c17b7ce1dcf69d32b13095bf32f540731b0b6b79d594a6dfd 2fb2ead5d45a487f0a90da382f5d6db5508499f178633c3f8287d3b870864c08 c5f9a744f397f74cfaf9126aa7980c448abf4c5b047778c37664ba29ce69b13c acc32384901d2136d1a1788f81db7479b9604473faa56c61260e1085ee5199de 8d22f78f865e600c0ce6f09dbf46ca7e7d3587543a3ad7da8a147076639dba81 ac7257dcd9b2cd970b53538b09ba92720dc82d2adc5eac08c76bf10e68c9cc30 551099a7e1a692e588476a03392fe9fef09cd8ec9404bb91e1706d7217f475f6 3ef00775326ab098dba2e0e7de951c44f1ac935c03ef4cc4b6b67002f0f75350 82921685a7db848a9342dbe59608b664fd2ce0c8c797cc381c40f0f0238b02f9 60d67f45e36b1d6fdbfcaa882dcfe5f255707b9c7d27263c61558aba8845aa1f bb132c91ee0a57b17bc6a917494a12c6c70a30f3396e646e3a68df81e538f936 033befd98df06714898b15ff969180644c96a7615d6722a2b994b79f647a08c3 45a09037ffa6f5256063b2c23abfc55bb661d65ea8317c83d1bd690f66f0616b 62d6af8a11385b2f3eafd22e3dcb01b83163f1af95f99bcf05ea4c9d391cc480 0422b1c2e36e934f2b143d87f99dc2e849d0ed5e9a79e50056c8b8e7de5934f1 d10d0e25a38e5575b5cb92078f0a191228530aebf25a45eb4e7dd8d7f48a769d e3186f5070208f65c617797cf34c5a57328c082479951ce37bc71d6e1fe626a5 e8857bba15ac4fff71c6640568548c9ecd7b1c37ae86cd1b73352c465ea5042a e911c3207e05fc28f52f90c4804e8183822fce41bfa31d57eec2e5f61cf03c81 7c9b0bd826be6638d6f063dccf96028b9c36e1927039e7267c33b40360ed5e7a 1e67069edc3ae4e0d479fdf8fe4a95168a793b80c78a47868ac60445c7ce80c6 47b79189af304f79cc6ce95df23aa46bd7cea4c9e0abec016f0a98fc7176abe2 47c9ba838eacbc98fd2e35aadd74e57c847202da0b678e3c0dcc8b5012bcd4b2 457c3801d514c780a2d2a821653b3814c4d8cb7aac1678f9a9499969c0cca92c e9bcc63c47214108334bc66ce6dde35af424cae101d3d824a026f5a0afc990b1 4298c4283cdd4f0e899c7aec112c10eb874cd3a33fee22e03f423d61c798d206 dcf1c33a7792cd49e516bd773465778324f1fc0351ee1f8b0aceb0b32cf7f3d6 6e7dd7f2a1ee5560c3e29cbeffbcbdb8b50086acf58706da4f6c10341a520aaa c85c7a7e25c55fe8a708ab063f4bc408e95e4f7f3ab9f1fc4fdd8f789c443b65 7ed8e722faedcc31be3993174ea7089b24a759fa6541f40e803fc594f0811a21 b922e1fd33b43c882fadbd3d2394faab90faba9ca222a385212cc17e42d6c820 3a8b76efaeb033141130765cd14632297bae8b88d55285e8d13dd02f7854170b 7a3a9adc0818cb0f039f8027d4c3585d209d50511a041788253ffcfd17860b1b 73c856dfdd54ac0395cb4db47b14ef2e6e6a945511885a03c478eed90644df9e d3852bac4323a98f42a0b2d40797fe08da65b6fab039e35f7aba62e2ec6b49ad 26e23d5665980d352eaa2730cb4dde2daaa5c23bd752d378ab56c3696e01ece3 c72b7316e24ff872c1629a36ad2dbb2d5af2f1c23dc1baf713583f8dbd26a8b7 79685e0ffd3f330d9fa34f69ad42c71b1d54442e9d6581e953b83e05c9c96ab0 6b1c4f2a0edaf18ed8005a1c968e0739f1c191bbd501b64db32998c69622ff0f 4d83d269e3fcdbe6370a8d31833b1b635f439d95101c58a9392b2a5a13ebd00b 07ee4278bf0e3529fac0aab88c7a4ad0b1ee036d3a15734f79501c7a217e5fe5 515441075d2d42c544c69a7b44c560432d2b2cf573647f11b90efeeda650d826 ee29442a3b6655dcf51d6d3f1a79a31f1b4831f0e69ec61d3152011592bcabc6 aa873afc390288e33f69dc65e8bf57302aa681f65b21480b4cefc570feb47820 6c31873aced35041cbc20573cb9c980d52ed55bb33f9574f895debbe092385c7 75d6370373211414fb92d71b6c6c57c9ac90bfb76d0b07c5a47bcc0dcce61200 b21e37fa2dd32e69f97ca7e5f2faaf111c30b32484ac117f27e5bf4528667584 df2920a2919531278ca7cfefdd609386ecc81aa284bf867c77cb0f60d33e4a8f ca442e531a223869db54fa6180aaec5da162842613b375b99400f80c6a86a19c ed285638639d37dbc6ee29d24a2c46b23ad6a955bca774eec78cc889d0d5b1e9 607df693b0f74a6d53947541615602531d7c257809b9691e2d76d13236102263 d1e2c21fb734ddc6f3e20218703b775a008ab11275fd9b3798cc05d7f49f9645 68d3895e408d08c5fdff6fbcd13c75f8f22484a70861977c58cdc894e46afbe5 6d911765e13ccc12d8850dcecd84ba1f2aa14d4891c0486c6f3651b53d5de629 844750c2e57140a40faf1b0967a8bca1ec6ee370d14fee8199345e746194e3fd 1186800cb8e622450e604d493345b8b818af41d1d1caa699024beef97f5a8ee7 9f5728872f97c3a79939abe8617038c7f7802ab5c5b483e5c759cb4075dad92d c1a5f8841ab35147c19d8eb402b1e4a5e2c2ef9ffa6674479a55f53f42d6de52 b0a5883b68cb522bbd87dc9cdfdd40d39a3d14c5439ee28bb01d8a569540dad7 bf92cd7c09a695fe0eecddce0882189ff5dee2065531c648bb2fa80a142ba51c f8bb716ea995d326e9881dc5cec750fe13d636eb06ffda984bcf31eb2b65ef59 75cc037bdce39980cadf7e44af2adb8b79df926554283e9340c3528073a8cf4f 8aed90744fdb9c1535e911d2313f0ec28bd878d8a57030880a8b9b3e14d4f6de 82c50724d48b7532d6758af0a7f3d3459743f7147489055da90d8ec86eae9f93 2f4e595dff8fa89fc8477c40d2b9ef7c81675566002933a43d66245717edaa41 ba87cea6607079bd6ab37c0abe2704287d530fb3a59d741d6db68564c265c6e1 49c3a952698124d0863ef3329ba96c15745f9dd62e3c9046b442be1e331d774f 177ee8cea45b106c1a59c6205dff3260e783a271127e203f2812b63be0520626 6637aa4d4fd97398c80277305f0df4f9e394351115a83ad755f89bda6203a365 52c6f60e9dbff14a20ddf522bed47b956077c9bf47fba15b3e311dcdaafb2ad3 b721615242b01120c6137581e18768c958b9b390fd44695427310ded4ebeaa6f 4c33415bb14f7df42ce22ca9ffff7758f99573165e09c6a4f32b45542ab4766c b171338c0454d18cc69a78d5418538b6861d9af3082e2390e3a17ce2dc4231b6 c063497d7b46a6792b6cc09e11fbc58fea3ec0212bdb594587e816e09f405323 4201572fdd54891c9d941fd8db8504e568ec46b7749a6a96ab446e52ffddddec 66b0c8b00e075bc8d93e29a070d0516acc1d128c1d28c0b9df6d077b1ca24398 bcfc873ec161d98743b6de65a5a4a527cc64862187e08c2293c6cb33550cd73b bbf895faaf7ec78269cd843261da5425f9a94c2aef3cce7754c768f1fa973ded 0301de2030df40d7ac8277f9e868934a0beb585d3e1afc15f9a289aa7076c798 24ff18e167e819875b1cefe5bcf21c4c4e99ebe9ba30bf4106feccf2a26a2119 937f95c9dd551fac0637cb04ab443acfecb9bc7f7a8b63a636a5e20cdcf0c55b 9b8517f1dce1310b91636222e9da38057561ae99d9e75c79ee434013fc13676b 1aaadcf22b43ee95d6b1e86a19e87a7ff996f2b91ce596c2ead90676572718aa 09d9949da2bc824d68a1f008e65e090252fa177df6336d83a258c836ce156c34 cfd37a5299b71aec0bd36e158c6e01b4c218527f3eeb7f79cdf612e9f2edda76 59e55b9c35a6c87d14f11343cf55cbd5a485c22595e6fc626803ef57f7c21e7e 77ad60f1b1f18bcd837957be86aa2f5b6501d683706383e9e3a70b84e4ecfdbe 2530b5475b2b78f92f06e41b876582cb24d178be272b78bfd118a80f328102be 9002fe12d9d5d50daa4ac6fb08486fe118575439ce537d0db4503c6010be5432 cb45ce66a2e58bd2b69e5c846de97604ae914e68a409697a609a3ad62cb1cb13 58c3d677be88a43d09e2d00c2603fa12aacfdfe3428d51e5839bafbed937de99 af6ae9bf8d1a4e3e315b4145ca1e4b5e43aa11280624c2570b89f21569594e03 9105507549fa55a556ed862c0a65c17a855c3630a9d52c9a7b525f7b713cbe27 ea52f9fae984f225c30edc598237363d28c5b3c625c830a4c79d40f7bf3b3676 d1420ee51ef0e386ede97f5b8cfa525685f9fcb7180f557ca0879c4d0e7741a1 4b83b951b24253231bb04edc8e048c1caf7841077d5257ce2a0315f08b39c397 adeaecbeadb652c78fb62fd3f3e764f885de443f63f8da0bc2391fb3d4e2e88b 7c156fab674f063e3df065c504f4c8cf075ddca782781ae7c3e62337c43c07d9 8239d77e505334133c7dacaebe97018e48d7daa1fd43d7596576f630e0757640 23e4f02d33f53f16420125980108832c3439ebbc1bc6c063fcd9c2ea25ef0b52 14cf8efec0bfb44cb1d7a07fb0a643556c27e880aed047ca80f54a2998cdd12e 70942c5fb8b3af01b1fff25eb1a49fd8782c1b585252aeeb24229e615f060292 e1fd8d81145aefa06212aada3920eb13763bec92676ad75f1b6c29298ad96112 6875b7ac4651705638d073653f5523712b086d8b15645147c003c5bb43fe988c fd16e1c19b649fcd2f1564025bb9a788df38a89d440de428f2d68746db0931bb 272506f150b1fc4f911331526d0ec1d6d54d182d3270fcd2d6c61db382416e3d 0cebb9fc2d84ca635f07ec724e4f389cd7d082b49bfba880b4c59b8bb83c23bc f41cf8780ae716d21221ab8b877d64192fe99a8619002950a24c139d6468855e adc839c6f75d6fad244468c451866eebff0c4972b61d22aad33c95a9893d9954 70c524ac87a0afee2e0c22b00b7f9cac2017802fc2d5dcfa7aa519c106aa8b87 e03f951c9b5bca58552eac8ebeff7f855c08097ed78062614c6170d5fb82a829 f85fea316202196743aeaa8af5a1951b98594ae42f6b7b96489915afaab09697 aa7edb876773f5bfa3d74a46d6d63b7fc1088dcea53b2cdac069f808405dc257 2fbf5e1dcbddd309095dee82168f243f594bb79611096e912f5292cb4fbe17a3 1cd320b1837747114a4ad4837f8ffe84253026a3417e0b96ec4789bc19433adc 1c97fb73d96d276960c2d2ee8692d99735c9dd164b7c2f39a0aa954ac531fbcd 0245b79c809b2fc2e85381136f638d6b95c9430c92e23b83c11aa5f51907a613 bc8e3c0a8af1a7f8507107c24ba93124817e9f990bd7a2f4e52e07f12326f54e 844b9e1246b2ad9d507f0d03bcc31df7a0de0a0d5340f23c809fd1a51eca048f 8a4ee1a902eddca36f1a7d3c2928862f44c9667463c283a0a2369714d6b30919 3664fd828fe92e8e6ac18c3c509f8dd89d15f91ff34f50e14da5e648e981117b ae30f4b2a1e76d179f7a89b08fef6b6ae1c66dfcff0814b07c856bd2bf6497ef 0d3de9000ac60918aa60baea64cf5e03b3269910320ed57c575f40619e652e0276cc68d6ef49b4e16bb41cec5a4f18f130998b7872a8a96f43fd1f1dbeff5f075e36a0737e4d90853e118a7ce04379110de76809b43a18044febbdf5874a0b0c98fbebcb9fb2673a8b079b9e5da75014ccddd94573419df9c60921adc587e90ac7887a8d490181a7f157376bf910e659ff0064651e94e79cc919259e293c2e008dfe41aa4e665c08a9dbb676eec4d07e1faa2207494f7163cc4eed9bec45eb0bde2535ffe7239ea70e973e6338705913740c426279a6af89ba205ee1b46d4705f641640ea91a7dbd44f387e806743224a9fa5c554b568efdea28ea1276a86a063d7fa8afdb08cc24c79e662fe2e730e741aed2f34c210c884519f4a0b0ad840f588d5a7d54e3bd9ce0cfcfa621fa5907ea2b3d657627855a62a61bb5b865920cfb8aea4b796996df5a105ef8ce9986815bb8dfcb9cf6ab3aa96d6c30848cfb017537f917e87f8f5c05e89761625e13bce9fb78170d502aa429b40178bc9ba6060f03802f3c3aaeb267e40a0ad2cec8d225c21beb742d14b2469a060b4a6c740b5125de22a639f861d4f9f0e1383b51799a2890768d41947f1613ac8b7e1e810c0fb4527b3648fce6371b61733a709fcd96f76ca3a8ac018b014fb47930aae704ab0179db5093ec7d389b05981c6138e65665cf9a1598ff2945e703cd405dfd097f2d440317975f9b86e48a0be3e599c6d6b62eadd684816093fb71989d3d0201f1cc6c1de1535c8fc1ef76c571a31b4e1df5c5a88a42e58f03e4e7b3f1338309f6fa1ea61a97ccbfd58d607d638617dc6aa79fb04b91b9728c6c7ea697546a0d6e74bbb64ca8431fbbb355678ae66652d4a54765e8c0c65cb341f65e2363bc02c0b8883962cbc49ca23d8ad9281686c2411cee3ae41628cf97cdc393916ff20e578be03c1b61cd65e7a8206a8e5e46116a52795e77abeeb682800c7f2d4ca107697028dcaa0e87cf1272f89dde51fa283f1f1561d55965530f1631b422248707d51697d227bf1aae74ed92ebc8ecaeda22dee39b1ae14ab21711860eb30700027458a3a75c9051ac81ca9609953402a774cfa4801c8ab6f66bb430a799770a0defa33b43713b500a77c4678fc9501a53d41efe007778744baddfb2cfdf70380ce1570e32acdded8569f4c6dcb1ccb6e553b120f5b2aab1139be5622555c198042f48341f480bbd83067673185f9bae26a9a444c6a3cdf319d4fe67e7347eba0267a5a11f84f9be00bc2d44bfdbc7f5506f1517bfcfcdb3a5d9624e80af2b7e028ca54a2413aba865a7a07b9d5f390128c59325ceab297c2eb49fbb648ea28602cbe4bc5d39e665282df5b23df1e3fc5dc4b71bdca5a2f3fb26ef1c7033c74008bc79f6d0e8f0d5879bed4a758ba38cc016479dd99da50ef43d346eb5aee5520f0a256d2e601da7d4cf7aea22237a0446b60ac34aaac66d8d26227ffe0e5439023bb0ae03c60dd82489759f06fe68d365112cd40d4557f7d66a6995559bbf0502dc4b14caf21f444f3eb1f31f622109f5eff041b86d2eaabc24c50a800def5a023d72dfcd0fb35ed98db7e605ddd16dde4c82ac88c40511639d6fe53b11ac490612d3f50a6e29e12ff0c9ca349e3117fa27d9aee1365cefc8112b25ad9b416000aecd91e78781851802bf5fd09cc303874c0f24c28a2e7f5f51dec2c198ee880002ff9223cf9c23bdcf77cebb97f75c668a6685bcccce49182f22814d7da3050fbfe0b2dac929b867e514b1b192d2bd4cd4183919ecae86870661d57bb129c6051bbc1bc68e2aecdc152a1887bb62f2e1594cb5fe6b4549538c3b72debe4b600c17345f9a44eda497c34cb947562e878708c7ec886caf4d30c2176ca7656c590be7cc0d3e2aee32a67a43a6999e8c4efa7b8ed4fd410a961fa36836d668c2cf078e03ab35a16c4e1637261a6bbc9deabc71976b1ef66199008e8929f71ec15e0d53d0e38f6112278c6f279c160e85e57ac16e37c7a62c0b05582bbbb2ecf1dd01f2317bbb2ea30b405dc5f8c19322db22346914f29a4dfc6dca9d3cf7ece6640bda09d18e23323e3b46f8f1adcf2a56e725f4b13295935e10ccff7977ab5f6d06041923c1e46a2ad098249ac2759f280e3720be94f50407b212232dc0f01fa500ef46cfd6b173091a464d86d07b7016ad8acdc246663398965d5de58a453a6107d0db241df2c227534baa5479dd59830a185b14bd0e4209c44fac372e88735a08bc3e4d85254f7210065f7a2dd2216dcadd38d39eca589099e6dd14ffb6c3ea0427e6a910d7b5f4012699a017cad2fa24cb61aef0f1709452a11718237d713d032205f8acfb5b069ba8d8d4f12b31a61c98efc7850dc52cfbe4b716980ea87e040daac7c5dce9a650cc67091a1764aa6bfbe27c3b0cb5ce9b835a3c60da83230b30a87353cfb97edfe25f5e1be92174dcd7a445cddb8b513cfa39e1434829f20540e12e49084666d4c1390495968fe8f2f5f4031f9583a35b402ebe6b9596760e2780ff4da3d394f08cb9b15f265f1248efc7a93ab12515fb943bf8e189d59a09fb3025f82a1359db653832e1008c6589416b163f5a58c5d527de690e36c7140212218d398efa55b9483a0b87b85c65e20e6379d72377b55f5796bfa64987ad0af7e84d56565074b1fd8dc10aba6dc49419daa473c4517d8d76703dd3d150550ad85b16288da6e9dd69c3bca6f11920fab63511dd6764477a14ea7db571e7f00c2b79eee20618c0671dfb544a6dcc05dc6adaae6fb7019401a2435243cf17530dc13934f58a484dc67da1cfaf9035ae05fb394bacc12d2b6d5a3d0ce83f4eb008cdfc13db3a56e8768a1345d80f3d85dec99a65e53d19383181afa40913280c066f7803770e6fc546c9ebe4a4809ea87f15a34ee63be73bbf0d74620017d83704586f8b44ec2142163f6141432c96741ba75e69690751fe8dd9e839987742fa0abfef06517380820c52b9171b5106df96ee18ae639e43a0b8031b15eee4c43e08dbd757be8bde91306b6e22c56cb8a8325ee6b3afebd1275252a3b4a1c8404e0eec45de8ccdfde4f17822aa60c752046a7ba8a677fdc1f2fb553eeb49652aed04736039710d6175c8f4e134c98b45109cb3337af65de81bb8830ea7a6376cf50fa3140190d965049ce731c5f4086235f46d2df35979c176318a8a8add2e95b20b407c3f427b9075e507be96cd18003304aa234cdea6e509f1997d936aa0cf0b07c6bdda2d70e1b524044f6b90326b1cbeba1ffc472d0cc03386d3ac5a4b201902778b01fd2d91abde4f44845f4279fd538d1515c4d09b18c297261ff4d1533f0af83eb96d4ef4c08dfc30b83f4179fff0a66557ccd54ecc1c9440e89c5e39d904245b69b509401617cdec597c8352fcbbed902bf2e1ba103c99088f9e425ec3051316fe06585c5916bf2e0ff79008400737de6b3ef366b362cb255124dc2244073714717497db923aff1f916d4addb3e1029f9bb91b6e2a731a70d2056d331d00cc36a8c823ae0a4a1e8c30398711cf5fc80d5f88a934618a5c9e71de2bf389002e88e977952615752217ea93793391a7fce493735f9839f1a5349916552bca05f226462c09476b0a25a1d04ef642567f783c579e32905362f35bf480a9a57b069d232befd49ce822c078ab44c22f3c8ce20edf9d6df409e96f5187d7b55805064a86542f6008028d20e0dde3d8e269167d9989e61371b9981ab70b43e506ff0f0c89817a1017fb55950beff130335bd3d74428381dc7c4bf34cd0298cd14b507cf4a2d782b1f3d04c6ce3fc0ed7d6d9ef3447ec9f240778c9a8c4f92a4e9690ff7fcb15726ee022e148a2994ddb05227426215ff2fc36365ddd0d24b1bd9ef073b7812725fb83ece974e9ffc10e3252c77a543d22b307e619a39deda2c2c9c07f3d755a76b40baaca44f3b607572a6f09663935d215f86daf371762ff0cb0b035a2efb41fb87ab8881d93faaa91a195013e7f75f98a0a613a77b16d53ad3570167a889b5a47f614742ab4383b95bd9e6a5b237f468955f495d096134d0d75603e2fc9b7cd84fb0bc5ed0bd8aa7aec6e2f9bed74d0e1edefaa6978823f194c10aee727147887471e16c938656763f2b73a0f0a7b2fccc4e0101c0cdbff0a5a2031e60a32113ea94ab1831138061a278e6974c735c85c3386d05a7409262ac100e2f8796f73d9192d796ff181660b0a4b8cf0998d7b9c659a72543698ef2653303c024f5b506a1a9e5f85aeba46882fa13bfe01008bdb3d4e61d73fd3866852705cfc53358faca6391100ec90ea3c0cad97812d70cf5eae7a051287f9c65bb480e10ec4a4a65dcc77096958e0c7aae176311e6da05ea9f30ab300426650a2dd70d1c747b719ea0a4abdeb8f0f9830eb2024533fbaa346d5b36678ad69032e0a207fb4f4e6a4c16bc23625c5bdd2af5db7b18d63005014238802e46c417392fd408f05d161362babeca2e5d980f814345f171df5b3cb27e7e460bdaea18be134e0eb7b5d1cb8369d04d221ba7374313c8b9e52de30a9a8b8d4a8d79165c7a5e890cb57c64d30fa6ba6353075eed791856944fca3db646eb9542f3461ca4d86d3b0db48c003f4c8193754fe3d05bc7f90259a650cab5dd7c90c0ffa8dc0cf407730cbab12608f1511dae620422c6bd9ce2b1d547e77121a45c2ae2aa4192f3a72503d64d02827098a516702ba8403506de96f3802a7ad1197480b886b34c86752300cd5e668ec5e1eaaa9233ab7772c715544e6e518c170567748a6fe51668903d0103006f8212e1b1a68b2ffef13943164557061769ca4ab1596d2ffe89990e2e0fe5a4a6badfa4021b21d3adf34d2d67ffde6cf98a97a16a713b66af8ddda88202ab497f101eaa6be0d7630ed93f157f9a604f33298f93fb50100756ad1377720722ecde67dc6e14253375ecf1a8b240f70867e8306f21908e1943a134a932f509f445728b2771f8d0fb8a1f0eff854f3624c37b15ff9f1ad00f16d861ebdbff04f750ec2aabb548cd468189e8f7a26eda73e0e5b06837299c43e576bc29c31f041f3196e41e58bfb6ef89df5f7c94270cd4286116ff22036e32259becf7ead60d336a45729d6057d71892d46a57da9efd0633c30012d5c65edc09b3945bc7c9058b19d0075bd0e193cef7162fcf430162f0dde2ef04db818a011d49ed0aa9e304aa826f47e1eae8a422104853451a39c32757736ca0b8a26e30a9ed31518cf20095faec1a3d58d9c3cb5fb8aa723f8a61a89ee71bffc0d31bea7d42098e7a2401fdda0ead3a21069db9dcd83d4611ca6f6e4b2824667ce41af9ec2769accf0e0492e55cb813f57364e47568ddd7072d2afbb6a8f0b56847f1fe9ca4750063e80c929091b1df15dfcfd74943cf504333c70d0a9d83b0abcc6e0c5b3b1506a942027fb08e91847a03e36116d051d0fa173b2763104093f8340acf20ce48919f7b034ee80766bb8ac2943c2673043bd400a3297183dac53031b85e160b4e95db5a00a7cb1f94693e13bc47a047d2fa5134d064c3174833e58f8bdb29fa24e0f9f7064a5f3dc1c93a5b8ce50a4e4b54be90d2e1b28dac838ebf5b64a1090543d7f00e9752e0bd32fbf7fd1cb37e4ebb277bb7d0116205ea3560a5ae8294ef25da40037d368a54ecaca5804ee762e74a44f69e11a2f66666b4211e6593db0470119c06ed1807dfe00ef1c491dd5c95237dcaedfbf56703b5d3279d08a978006bfe130c27f765f2684b0e5718dd95191e0597c2b2343f37b324076670011f0c2036aa05723bb1ba77608481fcf8d3a537405f6516f31c44591332d9207c548d0a6df002a03fcb3b98b41a6c97d0567fe922e242e367228865043850064dff894336240f511404aef4df18238b0a29fffe4629bdfc026645475f774382f6e04fd446ed02495d19f3300c7bbcaa863412a4c6bbe9c40e721f42620317b4a58b46aba5540a470423bf0c18a87dd0f82a89840ded5befc7cc32805e04a8c310aecadd2eaf06edff69fab0e8bddeb783f0320a6c77a095f443ec83c4e1f3f262f1aee795f6077fbed77d3d3ad10279f5a23c4c6b5bd8129ccb4d418edb05660ff8a3286fa50b6555df700204a1748887f6f7efad9d5e12eb5211e19d0ec0a97b7f3a95ba560d914482df4d5327cb932019ab57bb7307ba8ca3fe3cf1fa59f3621897d93d6705d67de4313ce87a9531663cb5ca77658d454228d22e4acaedd446ca968e752003ab8c1a8a1087cf267db66500d8f07e6dd0e69bc2d59f9311cee8295631add607e61f0ab0bb6f07429a0e4ed23a0edacb9fc9407c90babfc3d237f2567cb1dd0bf7bc227d805f565bedf38d6c7960efe07ffa26b95980ac98da45ee41b7b090094a3cd1eb3f301d185f4ef814ac8af06d6f1a2401c491dd8e70d0bc3a5b1d8e0f409b5d8b3a2aa9dbe91c2bbf4da3431d28f49ed2037bddaa4fe085690d0e3f00276c885db1d57a4e28e9d77eac7ef19f018c262cbf5a4dc8e461989e36fad7027a6751e8584ce2b67e4f16f9d0738ff51e94f1260fe5b0f4d6d563338fdb770075c482d808977ed29254d5812561e41d7409195b005c79687b448ae5f6dda90a5c6c5671c59e7f3034e195f68e998e053c2730d954b99049520e33c7f54efb047a010c5ffa32bb49582505827256e02e96ee443def0999616fd58d7a1d65a80a5c36606544e6194646cb04ee06ff90fb5eb8c73174678c26bb93b8fb086d6b029f50dd95b663d27c3208e3078745ded368c965e321de61749f8ec362c1320603447b09753e7874831e838bd7ccfb93e975e5ee7aa810b877502e3b9c630861022e0cc3debf5a0b0af81b2057c39bba51c5df957e32c6dec9ddcebbdffe87a80dc8aced559285b47c0e083096726cab80e17a6354d576a8d9afdb27cc2437ba01904a5cfefc30ee798c89e140d9cf354e8899e070f227969f54d75f14a30bb50258905033c97458f3c37a3bb6da6e25bab2f2258d1324a82d0e3364db80a4890db6b249bbe97db14fa30f16b990a366c2768afa40cb043f537d227585abd4ed0c9e35a1b5e440cd113e9ed76dea1182000ee5b0721bebe1fbfd5c899e93ebef0da402e0995ee1895c4c248600e0191fe6e9bda1a2da9e0f845b109bf0d6e7690398245f872dcc56a5d670f2cb3f1a31ff0bb014cf0acfb9027c90fce6078b6e059a68e33a80573be18d95b9b1c24ddfbf6606cb0ec1cd927aa1a16f6e65ca9d0451835215c0e15fa9ee9c7380cbb43e2ca227e6e1c66baf6fbc43dc92715cb309a11a9aa6785cb913c0b0f7e98f7f52bb32078f512943379d07ec4a5b9497180c30adcb4d67fa999f274bd8b48d0bd9c913e675725c89b7ec362b7865bd42ba0d26d249a9748732a543e0a2a6e60517b4966e8d1bb57043dacbe74d8e0971e701aa745eab396af5e51cc6c7100ab82d27277c7beef73b473cd147f23e3778d9070fc15dc0831c75682b952c46f548821fd85336c272efdd7e9aa4a2047ee92e02a757aab66ba29bd79d36978c6269dcb085ba39ef7cc2d090508a5fef3c052c0801d90fa95dac43a00145588b349fe00eceb7d02a53a697171fb587b0c6d0660427806c0257efead6425ba6eca9149a8bf8061a393d3b074115f9745228d2790d4ab42d3ef05ccd014a1620698e1e056e872fd5842253540814be2e8fddd80a0b91d8b07f8266629484b2625442d18b7fd72323b3e49a16f968a05c65f11c93020860ef96412ac396562932fd98c218e00ac964c22c09ea79806e87dd9fc3a10f4a79339872c92c21a165baa401038412b47ecdf10249c8597284d6b0838aa90200a0b44033c5d4d5c2ab9b28ba6125acbcd54d6766c46a85d81e4563e543520d210dbdb13731902ee244de87540f8de9e607f8ceea294eecddaf1078694ff607fbf20751c18540ada712fd1d46abaade744583ecd02855c46fe6eaaf29176805ece01b494d35c14039884a7c1067326f9650938dd07cdf0ca71f505ac3e32e0adbbdd84e3e24603e4421394a23a9ce546a5493a342de361e3cd48a9da2982a04e4625d80b928d84a8744cf485e93248676d14ff72677a24c6675c43a5a0da30a8ee28083f35b80eae773ee0225463957afabfb008417aef2f1333aebf353b60bf88c6918e583db5698bd9fc5ff522938a67e03d5530cfb17fca5b1bf2143940a3a44c5fa84dd344f42bcf84575555799bcf17db05d2d9ba6ecae4ce9848f1d052affc1d871038225a0cc8862e525fa991ba5a54d0b18b117437135a2859b730adddbb2eba16a7327a8919dbe5530a6786b6623870134204b2e83042a42b8940901e3261039016078d42caf002ceaa863bc7ef1e9f6591bdc94a6598bfcf7fe0d7a6546581b30a677695f12ca53d5db827781788f1dc8ea3a04c78c1417de0800e0d42bc4a31c9119f0a1f57cd66ce25a3da065ebbdb569c8944d59673cdebf01f36f67bb2d46d8758ebc35d3cf1042afedac76b9a3739bc0593bc9290cfb420a614a7d04f8cd4962dc83ddc04b7d13288e11d985510441451bbeb398a4cbd30275a1225ea1ef7fbf75ad76b1cc7c8445a7135a6e434c5be99c1622eb332f1b03994851fad422110b77927b6cd82ff69c72d3c40a9258d3ac229d8d8a17fbb2086f7b961eb774584e319db70c4c0045891d9e1362b34eaeeccf13906f2f9ec3064e8ed249add1fc8dbe23c57691dee782d1f74f3a97872ef2d80a54c7931f30075e6306a800bff08be5abdc3ed3dec9e003dbad06549168dea145f3557497c80d84bcf53227a0bc4815a8d9c1322c63e47b3ae7e0b841324e7b4f9bead4a73505215e278e5a4280e37088818c22f8308c99f531e2ce22a37364cfdcee93736c0c7380883c90ea0c5dcc7c648c9d310bcbc79b8499c49741c1f6158323d35a7d0bf8888fb8b0e0f53aa7af85419ed487b0ff13b800cd60aa6bf0ea1134f9748205930ce18ec3a61de2c2ce21d602336ae0037701e3f14a3bc9f0fdbb93a0af3304d47aecea1babcb67676c8240c2fc70f203bb6f7e3c9123ec09f703eaf5b0e90a9f3b6a811cb580bc8ca40a45d91fcebcfae0f4bb798eadcdf3c71a3f0875c102487fbea5171f1c2d154b0ff22007f2f1e366fde2c115cfb4dc4603c1bdc30d0339594e8a8d749a16fa8849d3655778e1fa7e23d76df5e76fe924aacdfe40f2073d8ef8b37e6bbeaa568181d9a9228da7b2a14a7a0b7454f72529e9754b6d7d075b9280d4583aa61a317ef61f52de428b0aed6fed09a4c711f81d44bc29bfdf08a3c24567dd7444b3443802ca76a7fab33828f6c5dd5d298d74a23183079a820efd9a3f632e538d40ece84c7bdf18918fc0aa4db6cafdae0c769873ac853ddf0c78e1cfbe04fe4021553cb1dacf93cae4e1932c05b12944cf42efbb5747b502071e524c5985b687962996a4170594666de3af637439e34744bf015977477546008402715fe0d9f204c2021c059270cbe8f5e2001efdd41212352858f5a616620589475143fcde58ff0a1f38f0500f4b2035ab07e0a7d3e473d5cd4f133c22e90224e05df5f37f49d383ffff869b3d34d1442077695999466b89e63b34823a4a0f53c2f5771bbff9685eacd70861a3bf1e0abb37ddfb752cd47af397f53b3542092e91bbdad29b50e7283b19a19e1522c5027e726edad917ae5bd3403edd40930a7c80a39d0829b4d3fc71e871a83f23f5fe34effc3df27863bed627949101fd06fe6459b431fe12bf28827e2709728b930ec3e8146f003eb494cd7e82fc50a60ce93855df25cfafae05a160a3971189b8b6a9dc97ef72d320df5931f98f168d0ef81c27587a863985b730fb135f9f7333cf462d1301d607fce84a8c98d80eae0087a211aca60514e3ff7a015dc5c0242ff2eb9ac8dafa9cf1f503b9e2a2937709a96d57e00275e2ab54d66b7201467bf4a75c49b88cf3eea74ed004f0a00ae40b376385b02e37d5ac645d791909fbab69fa7faf183d7092b9fa173443da68a60c1648bf070c933b59382d2b79cc0c0a0363594d682dfef7b8a35845ff63c73d035b2c046cd536feb14218316ba9f2365438b445b02bf13f050d7841838b334b078ee3b2f4234f3f0f455a51a9465207fc4b7e553af553cf6e47a1205f1002a303dd838c269cb580829c833cf945bd4d73d7c29ab53eb8738aa97080bcc97f52027218289cd7a53a6b6bc461f05f392ab7eca1def43f5895bb723093cb3cc5e00566d856f9aebdc3c1478195b4462d45ad1f29d55b4227a401266e8dd004efe0037f0f5497d1bdabe243541044c47335b24f6689fdf03313a14557f4162f6261055c39e2213e953e66790f0931fb744bf6808058a896840d07afde7d1c10c212069cfd835b0eb022bb15c6904c7a7578b70ce784f310d5969e65805319166b8002fc6c0c1a3e0755bbc89b475c53b1a5758fe9e8e3326d1006156b41da3d72b20ee6f28db3eaa3bce16ec6c617fb726623834e3f471bf5919789bdfb7ae80e51076512a5ade9d6cd0bbd2865ea16d09bcc425efd0df11e1bf2902be709f881e5058a3c65113ea100099ac6562cd7da4bc7d7051c835958da298f44ed700ebc9408dd38d20a6aca20811004b737a188c4193bbf8919bf98a7b57baada917b59cf0c914882e3b06bfd3d448ca049f4e19639a5e2db2f54796bcc3732bcd3fd2d1502dd52a73eddc3dff0f75c2ed109fb57432dfb6117bc81d7d7185e24f9827bf40f407cc9e03c688b3a4772b0ec9e4bb3ba6666692938f1afff3085c83095286f027dc15317575c7fd69579d566dea40057d2877ceb30efdcafd46bd3613ee50d09ac3f29b3869bc13553a2d9875381bddb94d82bfe6446a0f421761f79af03850ea8660d59a1f6cc25291617d58e5d507501afb698ba52b17242803f2e89d2040cc3dd99f2167e65c7d070b0dcb38623ad5fc71237a0a1dc48e250941b93119a03a3c5174cb17dfe16d806f4479668fbd02385ac2a5c1938f870846d0cf9e0c501a42c78e517e528bf2c2d2b16c485f8efd4612d1606fd5b0fb269f5b31d98f9069e1b82627a92144e2cee51fe4d77886c9877af6ff48ccfa175a94c0c4133d3016417b3bc083638142d2035312ce83ab1ccc7424eb7d0189782643fc491c1510c26f4ff435faa2f8d0cf1028ac1841f56bdec23383630ca9dfbb50fcb14f25b0ffcf9159c8ee3fdeed31a0e95da86174bdac835803f3d3321802d8f4bc739ce09c9003bdceab02c19d3fa75c052dc43600bc47ec49b304dc20f217bdeea3bf20cc79519ff2691a5de67e724908c6e40b1bb1c184b409af08de2d79f50a48140000ee203af689be1bbf4f686b903ba278492ee39df92306bfa67317d40ba68030ed2f751abb6e87e8e08ce2adba538039302c13b437aeec2115d6e2a8ccfaaeb00bd67b529f0243078af3d8421cd9bccfb7aa194128a2693aa0380abf81a34c7012848b04dfd99e2123389144e99b0632dc60b63e57cb055bc7432552338adaf0421aa2083ba79e3641c3ea20ec5b930d29a8a249b803dc0db10c5714ae5a7c8012ad491c9f30cc3e6512fdd270b6e1aab43f7ca7c0f8f7de0a99aac7336e40e0fda3678af1c9f9dcc78561cba379fc24159e5bf2b19f9e54e0dfcfd0363fbe503d1757d88cd40ca7d0ab6b6080f76334ef3c4e81f90c9fbd3be1d4e059d71e30fcdc800a4a0bc13c18febd32a017ac9923361facc8c069888d66a1e622ed2c4089b76131e6080035c1bd1c2a3d3147d026f88ed208dfdd8ef93d736172029ef07f89b1807e2d539b0717d6ad410da7cace613d2d09c935bacd1f2714853f05c00a3d4467b764673505ba9b8639ff24fb4577f20daeeab226b29f92b1c33ed8900fff28bf61c8e526908abc0410dc69d02694efc343e85c98eab16d1dd25790c0938b516687e9b3560e97fa60340723b9b77cb416f7c970f5d4f11d64d9e9a84032275f17ac164e915c6b593670b9ce5b373e01adad965d5de964e776bbe85570a5fadd302bd3da1d350f4fd2edf14e5dc86420a78899e3029a9453a2c4c340809e7687731e10e9dfdbeab4a49232ee1399f507f556b7bf804a824757343eb7d00ed0fe06956eb585f8b28db18bbc847ee8d0f6abe0a6f61daa24e51169fa2ce06bf220f790bf77f34006bd043b40980c1641b6079e4b1fcc2bbecdc01e8b10d0a24cbc2cad4dcf5bc11022a5cd872c9d43efb2fc464d553a815eeae33b9678208bdd5f2911969f4d6e6de8a4f51fa559db0e95319ac4b90157ab387d66acfe906b49224e4373f194a9ec41c32983b1aa2da79e0ef8637139e3d208629511f69080e35f5c53480e39f98d1c548f761c1e2925dc5521229a6dbaf8bde494850080b55cb330ff15cc0c6fc1de4953ed1dc64d41e6292136f06c475d6fc26c06b570ce17d7ecfc69407f4763541d231c2036c297b6c17ce0bc1ae6d329fa03861680838feea18b7c37cef7477c79a3dde1c9d35ebc5c855872b29083e46e87a6712038e326341d168600fab0345744db2147659b7a9068a49329b89b2649b47b22302ca17ec5fad0059436a5ee55fb3cd720af807fd526b28ff1916be5be7966658048de3181ec112b6f86b244f28b6b8f469504d438e7338683316d3a79265ec6f0cae07cc67b998938428b359eb1541a5d1cea5e47245777b7813dc049a8c86a20f90cdde303e1d9f977fe43ccf3bfd04d4c1eb3ebe4e83fc3d74a1d9f668df040a1c669857d79775319316f270dd84ab28aa9afdf32247f5efd30f2002ae749c05b79bd0b2cf2f83366744a1520aa7f250563f6fb71f4defa2162f11c052d15e0cc3bc25c48b826337799a42411ed0e595cdfc0024c6fb1ac8647a3379e5b4cd06def1d2032a8c696e8598f6d605bb69f848754bdae1afaf3c37b14e98b1f22204739d45f07e0fe8400dfae34daaf06ea02972a112f89fc7e2e3525f3502254c0de793e21c39aa55e184bd98a79ee55d1f8db89d1b4709c48189b5e616db309601f2c3d008c499b3047ef3beafffda1ac6b3e6c4a7e76d5a027c7206afb506f70bd598591b882a160c72f16fd22d2df756e6aadbb50142d8df82b725768e6b1e01e69af462ec8f06f0aec248a6aa0b985b225a746eeeb9c4a429b81e24b21dc20ad98cb2f46cb1335822d7b27f63f26b9dbfe599688905b227219c988d83f0800c21e327dca06c42711c468d70cf56adb0f4a10c03a4e840878e7f86e985ea6601405bd484afab2805633df0eac26e7d84b0be5248d7ceb918cb9920838dc0b90b39ae5b0b86f55448d0cd4e49ff002390a323ae1c4e886f9bda7db56b22ee2b089dfbc556cbed4da9465b68b769a363fa50827284598166a076cf47eb5ca6c4035d5016196ea314966922c34d3ff68976afc9cc711a9f65868cead274f7200b04b0763415b65fad788c2abb11afdb3f95be5a0c5677d2c752e7841b9b8b812b0a5a6d710a1d890e2b35920140a54a5ec5485f9157e4b3825b5de1a89110fad00ca9ba265cf5c17408514ca5a0413d4da06711338c3d2548b531682f0759c7de075bd7bd7b9ae2a91ec15e3c57a4948ba591227789ba350815413f10654a3d4104882dd8ce1495d1d1ce5f9390420c6601354c0afde068924c187d6f33c768d30bc0eb4edba831c04f3bb5f66ee96aa30833ef410ce86ee53108fde0c4dc51b90098249e52541c328612608c8ea8e1566245e962703ebb6ff10aa548d316408907d08694a11dae78e2666b02b8e53c2f86ba670bc198536082434d16a7124bb209ebfe679c0e101e1bc8000be02c6ab874dba3ce422112064fa617a01a3c6b0001fb0f891856505327ec8441043e3c3b846e9917a469d668282fa5de25bd06f50aeae710b195d2ecd5b5f6d24e7926c443f174197ea77232836a8f579d31ce760939019a7c40c880c6f23e48ed8c76427729410012446b6b505e30848d25d02000ced1634db874c62d7d32093527c0a213f260953d832d645d279eefd79f5da90282e5654a9fc6865ea6e17c98f8b2502644af6228831903badfa4c2d35c9e1d0e971037e45401dfd07f48360f8519c371caecf7a8bb99bd537cbd89588c66cc07c4e58ab443bab271ff2ef6b737e1eb3f78d992d90b2fd3d75b50ffda1cbfd80b44a9a25bafe9f224a31aa7e0569f0abf3a76244ccf0fab10b35aa03954fff70426d98e58b14568ea4acffc87529d616103bc571a37ac4bab011c4b741604ab0c38e486450de486f42e672aa8e170403feb6e0637c1c53032fdc5246b002dc705d81677fdd7b4f8001086be6b33e569df8670a3bcb20ce67a1d1c6629d3d4e90342d4ea1f62f0b66529dae6237c5e81f80f55d2e9c9152788d9cec9deda36160b0e2591e1e0e36ec52c742d61f11498da4292867c83d65b48883afe79f480bf0a8b102c43f97fd9499014db242b188fa768f47fa706ce8b5742920659c6c20c0f7019372e26b61f31804a3fd9d3aa4cfe98a8badc38d0ed35500c1ee8d37a53078539aabd337050df44aa6e6016af4ef38f4e4462156096f6e8734a7b94ea170a1756cd76d40f79979426f3147ebb4605ee22782a120d2f282f408701c8aae30a8542ee766fa3c9dfede73aa342480a9339e923709c903e34b895ffa732944d02d5f34445707d0c43a5aa2af8a74291dd07a4fa0b624c8c6890fc51f48c9e0b0e6ca1d57690d34cad9a170eaadc0fa7094b8c6f90d3bbff4c4a1972c9183df20fb8b7539508de04eaed769964954deb318525ba6f59f4046f485317d189d12e06 false +check_ring_signature b324c32a370c6ce008a1e10a01085448a4420e84ed90a145089f1af760d59438 13836e70f905976f13648bc41799b6ed548711dcecb20b8ae6951d01b7f786c9 4 8b3d50d7ea997b93fab7d65efc4a5d1bc33ae890c10dd18bb673c9ffceea3afc a76de2cff7670594885d331ffc4297d5e3d2ae71a42a6a13cce31867f96c1ab6 ade0a677596ce38493e7494a886ae2f256cdd2dad6940100254dae7a030ebeb7 0889a79d3ea7a7b34bdc2e4dcce8998c0fb1c2cc84144f6fb8aebc5f97939046 5badc999720bf471732a81d47a66a6a9ee2da2ef8bf80c3cad2fda76f3921107e937afd4d2e36bf8c39ca7be9df5dec4bf68e33ebd029f3f485afc91b74fa6066abd5b89cd8742d1542f7b0d1f89e510fe842d4f244d41d264349b5f03e23b0eb18b3f9704233acf5165f4e0a3428ad0f8b35e89e611f14253e7503cbfd6d207476abb77452a53ed1d25657af211d20db0caf4e2fc9c6438d073b97345a6ff0bfb727b9553676550f57d40bed9ff31b1baf20e19fe77b20584768e0597fbb90b364e4f6d80dfb6d57711139674a828a9bfd381187238fac505ce417dc014950ce9813c53ba7a39b061baeed2097380ba65f316c735e43750882815a493095800 false +check_ring_signature 236bcd328ffa4900c77ff19ad5b24d0bcea8ba093bbb412af95b6cfa79fc5935 715c810d6c0d62787dc612bcb4642a08ad25aa1cc1ce5b1878503eea7e6223dd 1 7102fcfaf04be7acc024e5a41064b0682b2fe2b43444dcbb1ac324004cd44bb1 49b45854bf48a4b7dab155baf730ef2d398e26d67d09d8fb5434b65056f34aa470b2df235d1e54eb08b88f97bb30ab7f336d0279d7f0bb5ae4d1699caa950f06 false +check_ring_signature 897c9e635d16ce428f23a916f7ac265a1d86d116f8383748cc2460e40d700d02 8acecf8a8c1d4b8330ff3f165da8bde25c847d15a154e0d816c2b1d811d24532 22 553cd2db8f53fbe80b5273e5b92f30977aeac1c1f50d785d5db2bf6ede5c4cdb 349181bf08f3ce57c4ec7c4a413906fdc60c77ba9eacbb2b33dfe2279418a3ab 8328499e882e90e9fc9af62e62a044c6fc1f054fe4ed67fbf21852ef8da57002 5d448979287675cbb5928a32083b08ff156e6f872ec0077669bd86be34caff11 2c68bc37a3ec62bd05d4ef3e025f809f2dfe7bb07e53912ec44d95aa81af881f 6f276afb3061dded05c2844c243d4183f4f68f8deb6d296875c1a60ed8e640d9 f0cca40c42091ca3500a3c9c9d89f332886c18263cefecd4908fca694ea50a2e 6d46a55f53a35d720249d18f94493389595a0b6b6a240febd6bb83321db88e27 734aed68f9a7ff4f36566dcd1de1c16faaebc3b46c5a10a5a8093fa6ab6ff57d 0dd5bfa1695eb0534e0f09f8639ab91ad5e497ab5dc144653d5739f811657283 18605a65891ee5f9defd92603c1e2ff994a7bf439a5c22bb5176f66ad19d2166 c2a14a21d949ab0c032998cd1874717255fc722fda9ed7d8bbee729aef79be30 a6a584ab618369f4c9ab1a25abaa627b3092b7c218a330ebc37019af8880aa3e f5a9d8117a95be915a17145054d37ce859c488878e8b132db38ecae4bfbbd784 1876b622dc38ccf80a11c6d9770e2035f0187b686c536b8c8dcd38b8dd4216e3 1c82715d985b66423aaf6cc88d488068bb157c1f1f5c73e9a370451c03dc64db 4e5c168aa6721db01deab083331ab7ebbad8ee89c135f3ac28ad080213cf892b e320e9b1eb9b70eb58cee8800e121abd14ac19b67bf5b8c7262bbb55a202ba4e 3fc2077310228aa3852ef274f235da91612b21b9182e492346b54d9f4046372e 739b0a0c377307abb07a8627ab54bfa257a6e93883e652f96b1884a5078cef4a 9054e7a0985ff665645eac2c350a125db0d307481b8c704102a04ef5a13319a2 02b31d97a43efe2c01a9cd553659068cd7e9d9ada48f3932c8e97acc3ad4cf08 f2e2a20cf1a5d6888e4215dff94568cc50ddc5d9247715607424445709c28c00b81a6f6128ae5003eb012e1f3c3967b8b42d1f4bd89d06879c41e6361b22790ea196a3d396fa96867377ac234debcf2ac3d3843e6c74475f995679b9bcf8da096f24ba830e478a3d70c4cd74b94934cf65251be09e0077adb0d71b1d97eab30fe1a0ba2af215bf9c99c33aa190e9a63e85791328fbeace00c2060738cc72410a7b8c84a769a9915cf0d12101a949b6327407f0be56251278cbdbb149e942630da9b216e1eafd1b2acc6f68a846eea9f8ec431a32619fc0334f15dea3ef10e404802517b398e7fcd342df6a60ad52fd5ba190ab8e2e9fa1f40d447749bf9c8a0d5924dcc5d58a73e829e9668401802ca519073a08bb5291948c6ba6fb17d71b0959a9a49a51812bc10ce8a0259d2a084042ae9500f1951ed7ae909afc29eb7904baa20a64934a971725efeb1bf8286f86a363e8f50915fef3992c61031783c606eaa9be4eb0442c969911fe8bc058ec4e1735f64d13530d9cf0557f68a1bdc80847b97b86be1023d45a9959377c01751d502a7b8d0b3eedc347bcf8b27a5b1b0b522a748175358d9d04058b4e1800b94783b5aa4d4afc60f91e874352ff886501d469bc50abe44ac5369d76c1a64c1084b922f0414d707dbe5255dddce0400c027881cb53a7a4af411b65effe2a246882597d3754a2b93a69690fe375618ecb05b7ab485a245f2b1fbb269b8c672b31c5a6c2268598a0c0911a349c1759acc707c99efc86ad540a7147fdfa4c4072bfd6151647476915236b7706392545be7300a8d03c37a96a77cdb09e3f44a316253017469c9813559a356e43663abfadb20f4a0f97cd44bd7c5f5c5fa8a8913900ed874345502331a9dce14175ce6d2af2067e9ae024e43dd980ac171997c6ce4188e7857314a349d436396dfc446726b004439bb42a5d9aa2a674aa8820695625c3dbd4c9f2eefea3b714b9bd8818be780384a88c35370da91c0f5a64f4c2a5e0d558f2087b0aff250dffde76b7e6cb040395c64f47220065b83cf0106b477aebbd3ee1d537fe2b5c99f57482cd4cb3250ccc5cff7f770180cd78ebc942092353f84e0ec8c9ab54b1392ffe06dcfd51cd05723b89a27d9859eca66d879f290e532d92d218e3e839d6e5aa6b989f91207a09e70a18723e699f2ecb346e38cfcb63841e2d3197c4faf7e1d57edae8887d6c09d8cf64e56becb12baf1379e3739fb7f4fcc95a143867ab58e6b8901074b3fc001994bed797610f2e138ec3bc89fd4c23a49d35503937fee3e05bbedf4e649804f6592d693e9c991240773c7516e299709320cc6bbbfbd1e8eec3fe272a24c30f03c421bda0069046f97aa3f9bb28f72f60f3cb3388e548d0032fa8fe01bb870324a277820bf1d42c70d5762cfc7de1841e33fe82d7a4da3807aa982d35e8410db81efa510d49e0af78d9ba977fec16aba639d9a66bf7a4b428d98d614d3c9803db1af1cd9f98fd6452cf5447ba9123618eb8667c2b0054303e9fe1bd301b1d0fe4e035016d3874964e3e3b11333aa89d0d87aee00f4f9fea8cb09c9f18fc680661625bfe9303a4082fa098e8fac7719d70a3170d7327fa04fc5bdd400556ba00ddb260503fd0d6adaf5cb0bd5ea892dd71610530f531141cbbbb27f5c9d94604dbf11f621cc594ddc916819fbbfa9fd7d9ec9bf8d59d4f113e3f13c283fe0b07a24dfdb43c52024382786b2ad23bf03705ddbb0cc517f7b5c87975ce83227d06000a85a9be58a691a83cb36b90b81d961984dbd9e1ce8195cacd57ee9497810bf85a96d5a000de98605d7bd3c09b2a124254741bba94cdc5a1eac9f5baadfe06b0bae0a5addfdfc13a075402933342acb68aaa3d637f585277ae5346f87f9d034e38567c973beb9791519e08e8617671d919c52433f20b0d7e40042620109907b2b1db5d7062ef3957a481bad459477c610a96924f1939f22f25865f69221900 false +check_ring_signature 6d744438b1d2f6ca20c3c8bd1ddd555f42912e5f974e4f363be98e07788b8780 618f491d19f9c9a6a449f7772a8d7a512a1234fa76fe786dd628135721c57c5e 5 e84b0a7a7ed7c082f58a55bdde442b64bc0ca049cf722982ac3845a6222b9b56 3c4951a9d0b67268f56491c94f276e7084c9a8cb7f82121701ef7ad0386b6379 f75e5de3f5a2335667d188ee34542bd617f2f14dfec6da4f9d4a00c578387eca 85ef7a2eabef0f55922315b918c8fc7e675da276027e874b95381ac3a70a3288 aa06e6a166a6eaa58959b8ef6fc8d6c26942e0dfe111111cee056285bf5071c1 ee2336bc15d36ea396c32bea197a42873d1e06f9825a5ba401100db22b54a00a5276cf3f404f4f548e419ff0d643e5218e08b82a4c9e5091d755d8355436130bec2e41b862fe50dcfd754b2235e5be94953b942ac2e1bb7dfcd3f16023c2980d8f6c52e599c9a4abdb6b6f1f8c236c32387f361aa63e43c0e2cb8bc97440ca01ecac982cd9ef8b9ddca69d24193dbff9c756d032e67557e6e527928ef1883f0028673d26f20f6a867e3d92c8a8357e5bb601f097c9cbeec6e16c723a07a23a07bc4d7993111a97169b93dc53510409f7b1cb56e0fbb74eda8c66f5ecc370ccf5897f0913add01bec8b91958f6c0d44942f572fb19cd10f10209a587b5d0aaaa19d5141b0de1cea5ebe0e38e25d79d7f70f4e0fdb230cd00cd6ec0ee5772f68a4722b1ea644d2b1f21f44fc6263c7dd41558e5156afd9985a7f3781138567ab01 false +check_ring_signature 0b85d41d43f15a174e88414647c7a191591185c861434f63f7c30e0aeb54a697 9226b93d58e273d35279fd35f7374e6fe1035efbf28d6e9c1c312fc9d51dc044 1 f8f011611cdace03569a007c1da0a66b8a4786ceaf27761028cff01b7c7ff173 86ba5e64fe344d9cbe25db79db5d782350a9a7c774e037e974b4cc043729c50e2e0f830ccdbd6f54ef74738cfea40e56df4d18d879538234c2e1e1848517e1c2 false +check_ring_signature 30e6d34d8d1ab1bdbc08926ca2e11713e8704cace30e053b5709b054fab9f91d cf2a0993ec09d1c1e532848f867f4fdf78a152f55842548a677a57fee5064e89 8 4fdf08412ddfc9bd0da15e2c85550e85a044850a74f3cd39e56f4f165e12800d 63a1a360a74a7c81cf598fa075d75b8f838764029487ce3e4b8b0cb850cbc491 ef19b222de3207829ddc12419a2ddc222265eaf251a5878fad89f7a101985ff9 24c750a8f95e5a6711301f5e8e5404ebc705e1664c822b3bfeaec3ff6df439b5 4e61c945cffee87e6888205037fb90e0941abf451b7ee01501794ec12885fbc7 f3ea9c2d5cec3656ce5e7368613efc946439ea6cfe5a564b5aedbfbf22a9a8d4 542ef301fe130a9b22bbff5bdefdc97facc9d0b53f144fd98f43fe0d6be84297 b4cd187577bbbf96e0d64d2ddea8235afa5e139bc9605944ec6f3083793a2acf 6916ff2d5031cbb86a59fe77b70f438cce6aa6099fc75b3e16e44dcfaacb73089bcbacfd8442b78b5f92591550ed66993439043d40d626e131ee5a6d00521e358f94f4421e2dbb6f2751402614f0c64cd229318498e2e688f94026d556973305399339f3a0dcdb48bd487ebdf0d0323b94454e9cbf4d2ca0d2ff843964cd8e70e9d82971c9a000e2fbeb2211b4fb1e8318daf54dfc771ecb9f6b0fe5ecfe56b3e11bf1ed17d20298394f1859078c585314a741eb5b525fed7842ed457c9620012098cb2887423be109febd2b840776e5394f90b810e82f526833e61ee952ce0066c2b1f7a4264fc56f08356e4cf013116952cb1d64172be74433cbc511594a0083e1b2d333902d60faf76819f3bf042da6a3c54c9313fbf2ba560d6e2f6a772e52e33e76781ca25d5d0d8783c4a8849d8ec2337e26fe306108ba88a406727d091ca357f955956204512f4ab1bf882105ebdb5fedb748499aa96a92a962ccd2d9b86db4d36f82a5b39303188ed050fd1534607051886fe9e9eccaedab98da537bbc5b80209297851ee4e5ea8f83d63f119628ed3250942054d8a735eb94be9f02405230d5e8b1c0bc02ec9a89a5be4ff28fe0c74160b0da4385ef4e74cca5e5038eadcf1adb3b62c2ac6fe3de51db0e9f52231497de453a41b8bf5227ea4f810d2e4d3b089a3b753ab04a1c471841087e595fe3c745f36293f96f3b08d8643b01 false +check_ring_signature 828a9a0dd2fddd93d735b64fe0dd07708e552adaff74e8ddffdaa7e6fc25da36 18f9508c05ca7bea0399f96630dde372af7ec7de273ab78ecc1e00fae9cc33ce 21 1e96d2ad481d5b8a7fd92a0bb3104e13210c7ba59f110226a1271961788472d0 1d3dde4240357fdfdf5ab3d53a5c9db904c88ec41029252a0367899809f789ed 70f6c066a0d2e5d388c70149b75816c14d4e73028f0a68d802933b753142c909 7a100a9e62bf88de4b5b39b3251963a80b5161655173a5250a6abaa9dd5e2adf e73265f3c3e3bb685fcf6907b2a022bbb28d11411e1226bd9aeb9db643b68f26 77223226387566f18760fd72df2b0de99e991c62f699b17642ef19ea6bcb7053 6e778d8543ba428a5278895ca8ad8044603f02f01378873db16b18394fd16b35 2300bba51d3b484a1fd89120aca13436b2c8073c41a7ef3290e4309040ccb4aa dfffbf236b9b62450e8bd848b1881e4f7a599dc2b821a29d22d8423373c38088 ce4425b2c4f8bc09dc57d3d41d4abcaedfac23186f000c14738455226f5ce166 527ab25a7484123b66b2f08f045734ec0bedefc593246e33c77f5f3f6ec81551 bd771f885e9cb9a12f3b24c73664daf6c57681f3bd2c7232d17bd8447cc8c33e 779008990e1317198c158b537f4633f1aca9d2ec9800995fe2cb980857fada89 c4e2016c88299783707bd1b722aaa7fd919cf771cebbcfc8930af49ee39c9f1f d1ef4eed65bf86cd529597ba229a5015516abd5dc3e20b57cbfbbf19af8013f0 c880ff2f03d0ee4406deb9845a614938d05a57d5e95d9761c4255be7285cf734 5cb1201e463d8ec8eaed663ddab483da1ebac6855459e757ddeaa7e048c32682 9154094a2b29ae858085f76a89c3966fc3cd473d343c4a5c1316f342ba05dcc0 1ce1d48ebb3b29e196f0520a6343fe9ad9b5cae04179cad9a8768e0d3344b992 116f1c6e87bebc742d79b9761dc51424483a052b19713fd3ac152ac9a002e26f ab1136be4be1377fce8d343bcd0eb6053e04ead119b1a40eb3905d04c65a54fe 98523b4b14d811f96ef43cd2515e00ec6682c407479075a7ace28bb72b2b340d19faf9df96c4ac7ffc3351720ed0027a61759eceac6b66eb3fd9ec6cbce2e102dcf2731351d9f1655b2e7dbc6ecb5c815eb02325d2aace3cc40c205ace1e9e038f42c65e161f1688075441bdad05a7a42fb185723a81f0aa7e8c5bf4b216d608c215465542b46fc8bd4234019ad1baa3c430b91b05ef68ebb9eb006f8bed7806b9590bc733c9796856773e16868cd2e9d0a74b9c3f38f89a9ae02e6c7e28700ed56e49eb1589d9bf8d59a7f14b61f33c3e24e10f39ce846ddcc33693bf2b83061c25bd574faafa5a2ba73d1e8b25eb804d565cc3e854b73432d27e31975c7a002262ba7efa8b74ea83655a89b7a576636d1198e368bbd3c0cbb7696e740b48040181991763077b5286b4eae5b0029d18a564649ec80836fecb5fe610f347960543edcfccf1fbce32720817e48bb387c445b8a040cfcc492a7586f947e826060a56b0b08b9eee05d4063a9b4b99f9d0e1b43e1daed95119edfa7b73054920eb07487f432afa6a3dc51cf21ec4a70d9ff73d62d57213d5641ceab690efdd1c81083295c014137803b020e57897197cca5ea8cbf84af7c5ae759af49116ec10d80263f7d506ac0eaceea7c1e20ff716fec2ef2efd2c52a0b32d22fec92ac9b58e07ff7d8d0f2d287831df9c074f88b2c049d07c6ec848fd3085dc032f16ccf8a60cd6d53535a21b00c65dea475da1fe7a52909639b303e6d00dc2688174315f7f9254bbe09bb129e18ecabf1f5771ae1ebf566eb4f37063a3b0b94652c45ca6ec05b7e6e6d1232f990fc184fbd6381e7931ea60238f2556b57c27c25ac8d353b407b3faa9bdd8aaea36ac4c5b8d1cc96e4d7a0d6a2a4ad2dd3759d8c2449cdae9058d6b56fde8944dc3c7a0e948ec07c96b41f9e1c2f8115c18eca6175bf72d210134aa3b4bd9f69338770d21406729221dc27ca128a4bc2442e4cb8f346a273e051c87562542e728a8bd2dfaa4d9cb75c6da0c5754446782fbb84790d07416f30781db25c7f38ee4c9d72c5cae928c15147ab8173c31313f2c99babd78f662cf02e654b1ead6a8edec68e9c2de4ab65815e4ba8b869781fa528bd19654714955056ecba031346f60068b9f24abcc3de0eb5a316945a75a40073bcc0af3fbca69c6dfb566488a08bb443a228efe730eca62062cbbd0a19920a54413bcbc02b1da05b199f6d1e476cd061792022d47b028235a77922bfe281b7755dc2fc45e72100eae1c4951758c48053cb838265d763f44b0d056aa3dd8b215966bdccb1d9f750f8917099149b0e1bf3f691e6d114cecb94d6ebe672590fb14d5379b74a20b3e01d53cba272d883cfa16ed5dede971104e447291ff301f93a9a9e32f0da606bb0a1cc8bb1479e8cac062386c6b126afe5faf5321dd176f22b116ea2fcea6b432075a9b8b63822c010fa1c26aa9323d5b70f70f46dd3a0a4d67b3833945161906052aa5ee6e2d9e071b2b1e82ee5802bbc251f9a81e85fce57b11a31a68deff4b01eaee5c00cff0315b33cc6fece2576a1333927bdc37058cb8521a4447eb6aa20ea038fb27c30ad286df9e79cf4dff41190c8dd6154c12cfff7acf1f6a3855f5010653c83c4873da56569cd8634ce662c8dfd83086da8ce01baca12f66ff653a0b8daa1189fc706d70c42fbb65dc106745288bfa468511c57f5c5a7e9c860b130de0ecfb3c74699bf066f2823d7d1c041fac2160fcd42e1b76c82abb4d3ad73e0e94912d5424b1c9827569ec7827144d3011cb541655623bf1302d924686acb50871036f4778d7d447cb740e86908e268f32d9d6d76b65cf94db5dc30ed6366f0c7d2c73141a79a5b0bd712906351395e49a3784b1d7c85299a458147aea0ae903 false +check_ring_signature ef11b1152db303a40094048ae370c77dcc852226f26606f5e16cacdacb2d0ff5 8d7a53d39f9addb541a1ac6ede0e0dc243eb2500d59c5e3ae6373c4510e40011 15 7f57bc4e42fd912c48819485af95c448b993d73f92e91f6f7a396c3c1ed85786 75ddba82e84fe1558df08a64dfc0d95a1601c6fb564c1c1708f81621ab84b775 73a671a091af8764b6e60979be9c68f5ff25334fcb0ddefc035d19eacb699bb9 9ccec0c8323348ec8d88ced648814930533acd314c1ee1805e207dd31aabf875 3eb1f5f6569362a2cc3e063ce3afa04472dfecc205518e0225bee8f7948b9c1c e9ec17e6580a779871725e3a0399e08caa9a6f5ee24c021d5b8807e261804566 e4852ca6c0378e64f2e7ebf4c707d08708fefdbbf9ad76ac0fe6e77c48f087c1 6fae56e61f9bc6283364284619c4276b838fc113c99117adba85b105bbdedb4e 1451c4147d9af1041746ab5bb1697986935b4d16e3624523c5d67b47eb56d82f 4aba6287cba38c5ff08f978917dd01f3ff30f609fefcf02f5cea9152bf32b6cc e79295e6e943e85672bfdd11ed96f43c7ceb5a89f0e840868b79ca1e9ed11d63 bf22d74a20fed4983bb08cd822eebf2b08adc02031c56d18806708372df50a5e c331f1eb61dc81c8bfe7c01a031321c137cfaaa70b48bf4d40ec484c5ad9900f 02f795f0f11744a7c311d167772996ad86a6782a4ee4386e693afa8e4465a4c8 09ad8959d4c1a58ce09e0317e7a37152de9b27de89de6bd5014f24d5bc02fab5 019db27ad676c3192c0511b07dcdd1638ef13329484207e074603bbc17d33005ed450b4e74fd63d1f093d49ea91bbcd7f9394e4e7cb1b4c135a27bb9fc47920d022f2a71654ad444f477eaf8f86a905106294a6b5f41919020d9cb900bab77042d6c95b9b9e08afcc21b042c43409b259869966dfa768e68d90de3fd99f8d105750f33e835a35945f6bd1e8f3ce6eca374902ebba0cda2d3218c8429229deb08b7b66086a2d1af704342a51a32b9cc4120612b6295737a6a8757dd3bc7aa140e204d31a8f53c2435059bc0937d66c0597493fb9b35a6dad6ba4087f57eee3c069874f03366924d928dfd7c0d476c0d3dbba0f4aaedb8131f6ed14e5644c4ac01340706274763eff8aa415f1e2a0f25876c861bf7e6b29af8f4cc2f93f1578600c5fe128adc8ccacf720b0f7cf583ce914b34d55ac7beb6ef60f6100fb6f72e0ee0e0405743f2c9f7458867888d87a2034919c8fc082cabd643cff53e9b71be0074b98de423748762251eab87cc3ef02be791fe06e3589ea2efc29e1e888f23064eaaec0485bc80af61e88c761d3bb489709494936d816662e09a66f957eff30243426a78583442b3f531498682d37bc1016bcd56ed4a6bec5f09d807d36c9c066a9b1bc6ba3ebd8d4a2efea14d0afd7cc26f93d50ab3e070209747d8e30d8a0f9ec0bb9491eab90796b983e497c8a5df3e09de0b9481a819d68e915ea515f60f287400d7cdb66fe8ca4ee2562360d3fdf72b5defed48a29e4a33b3d2a444d70b2a009707776e1ac20f5f11a48a5f135a13565bb888cb5ddf8b33224720c0f5072fe1d00bdbe1c0ebf1f763c916704b43f3cf3f6f070541fe5bda98e0be0f01054ada37070b097ca5d7c1cdf4935a6249d01e59e1c2378ec761cfadc46909d20b06d3863e2e72ca3099b693b0a9d7d4de138ff4194c0eb70fc946317cd680f8053d298baffd36aef77653ecceeee114442d0bd3f8824790b17a72bbcf722cd1093cfdd5f46478f16bb5837d51284f89894609edb65e4d0ae96ec1738ccb1bcc0d8c71a6a39458bd5ad780bb563e14f35741471c9a3a03f3a244a33c4d0c3af601e208b995bc008c0b9988863fc615012cc3baece6d38a106245a525d836e57307d0684dca996a15508a808db882aa90f5f63b2839af657cc0a158f7712f405d0fe36d364835ae28fc4e2be48faaf45bf784067f8eb2dc60ff21e7fc2724370d096362ac2f445b581677df71feaa49de5a61a682c3ff5192541f876454e193080ec35b613ba82efe08dd382c147c07ac15a3ea7c58c324e6d6e069037676c9ea0726f2f6a592ded609eb539218041f9a086df17ef86dfc77aca9e127f7f884030d false +check_ring_signature 351419d8d6ad243f9d56451e68b03530c142b45711a1f47379ebb4098deaf876 74371d131be58581e490537873554e32496c63b170b6b631f070be31b5144ae2 1 f55d95c4a83e1d7d4a78d8c02814c6a01e0dd4069ce244d7bc2944d770728731 b781a32336ccc4d587817676e6f60df45cab4e7e7e95ccf053fe6785deac98a6e0df45a1747fe3d310b4f67a28e85aa45e49405093c894a40895c7d51403840f false +check_ring_signature 47f7743bac77f4a5855f8002f2590abf81aa6bb5f21ea3e0f4d6528996b83539 a9709fd34d1024592643cb06df5419200636fa5fcf9fe97267d48ff45a7fc56b 151 09c406ea2af573dc59c885cb6bacfce738691a6e0d405b9040783a35ef65fe49 df2e40f6ed2abe262cb89e4bc0bcac09bef39f9d5a6280b28b5435c047889199 31a42b5c958425f31e4f6a898f7e32f9f12c2584f25a960beee4af0685c4ad71 f55def320103801c3d67350efca365b36aa92418ee2d1bc0cff83442d8f4884f ec3edeed4796f1e679bb12953be0c1839ae6dcfa00313b06925ae7509175e528 58087e9765fd45570705017c346a892ed38899329da55dd498356488480b9d2f 04e083f1d5c27694d287f2af8518cb07417c9fae1293d84671ac125f20020a44 144146de3f813d48818cac48d49ece063598202836929625ff4244c8a7403682 67add709311cc7a169ccfbce7b22b7936925f4e5d94369a299339b177fb982ec e5d639c78e67d5a36fc4e2a8ef1acae91f9506626c63dbce07400cf6b30191de f528d723c3350f1b5494c337532c04abbf33f4c865b8ba4bb7b745add612f7d3 53bbc8304fb73fe2f2a4016d04ef3eb00c3b4c6d0075aad25a784eaca9e91b3c 936cf6c0e77125f0535600e16ae94c5c31ef4d14b1d9aebfcc4b038a45e779d0 5f0e5ddbd92891228f333c2473517061bdc371376dc5b911b790b6bd1906446a ae422ca92ae25572dd7ca642c94834e485912ca645a8f1c7e56118a47d88fa7c 6caf237a97f72a29d43ba02279e463b14f37e0c07b3d664c2689e6c3a15bd45b 7c4144b04034338c65db3a8086d08f3ad39456a70f3d71acf7a3de0cc56ac8ed 3c7cb57e010e5bcc326a2f949b3b5e7712323b9b87c769c37a06de71ef69ba83 8302646beb5d934493039df151c469bc7662f3083bc02a1a3480ea9e77b886e5 5b0471042a93923b55c89e4bf3680e1ae53585c90a436f6c3bcbf303dd850b6d 88f52fbe41d0231bc3f64e2bcb8b59785c59405265b1520ed777cb5f21ce0ac4 12cf86595ffafdf53cda92fde5223268a41f90d589b8dfea540fb1f67365d52b dab4d9ec1c316c066331dfdda2e481f122b4c7d30b3b21017f28c3cac22b97a3 e5099909a0c2049f93288e0e46e9a6059062a9f22a693e5d00a9ab8fa86a8291 4ca9b3717511bad12aa7c6cad09fdfe5a4ff537fe016a1623a19469079d6e56d cf6e8a1850a58a7f332b21e159dbf38b5964ebce1ce005b92991b8fa4c62e5b6 c481b1f0cd8d23762ab20b1320e8f2890a0eb1f5ba740ec85564e4c7b5ebfd66 af412e79f88db53f77d02c4d6409fc06952906d4f9c9bfe88a51752ff67acaa6 bd02bf6f11b28e739c6ca280b52d69c860a9a53693ba7756b66857c955729ef0 463312f269b246487f7eec7a4aed0571ddc8e2b6086aaa0bf19a6a01ad2e8692 5a6f33bf0a6ca176f76a774a78a4e860ddaaf27acebbf8a32e2b68248b8b9452 2d16c437adc07593284b0118dfd7ab43bcc58c17464c078186a9efc447aa6712 c824b3fc85bd396c61c0def0f0c6960f54b320785ba0cd3342c90c8ddc3cbc01 44975521b6b52827c44bf51321ef3206b97749099b561920d69e69afb5056e67 023005bd449d76796a95d41ac972081bfa56e5711c8360d9724962e6ea18aa02 0deb510170bdcc95210390b170d75905e7a6188449f865e7e7929a91392a9fd5 30fe3ab09b40a1cb3b95eff2e497ca886661d57830d7495355bc46ce60d57f6e e0e5096377e3648928ef71b2031129843d62fbb92edc13a415e20776bafc804f 865c5955efcc1958765d50d85373d6f721c3720d684d91c740a466bb8e631858 869bd968640f16ad0bfc9d2bbfe9f5f73cd24e4cb333579d0699fe489cdaf819 d15965fc0f1ed84053a54e533ddb87e04d2b2dd28a0b7128fae8b2d1d70736de 3f54f69ea1496f7b1cfe5ab436a3c243d43fc48dd91a7694ea66b46b113d7a03 e751ac3e096ce582f4cacf782a9a80a14cc425d930612aa54562183df9760126 e2e1dcda92bd1c7bd714df6bf86d6c4269d16d6a2f6959d78786a7502aa81b8d 623e21550c728836683a5a534b96d61c638822a3ed7d8c71cdb8043791b5cffd 2afe2d652ee8f968a1b4c35c11275bcc0a0cd050d478722c960f99c7a61cc62d 17d3643b0d7ad1d59a629971e7441099356bf810e66967657e601f321e0917f7 f385d90484c22d61d0524356f1cc5c923723a0208273a2bdb9f6a1acdaaa2ac1 3a2365d28c524500f1187747510fa7014f892f0f940a7fe7b570b4e2e9455c8e cea56074ec0e10c70d7f35e499f332d6d42942cf3d8b6ff80d22369cd7f58dce 63cb3bafaf87d5ecd3dda0b9e43fcbfbe5e9afb5c66dec25d87d1b7e9a35e6ad 7f362850c30b4eac2c878a0b5730e1c4872c77d1f8011731b5180d17e2682583 01fd19c3ee4ad54f2e6943455bdd96a02b3c623dec65b3a20e96c7e1fb1e9977 1535a49c35f668987f44a6c83cadf03bc978bc3cac65ce68513dc1dbeec662a2 4fc5b3dea770e736b60698b1924bbf40ea3909e45f654f83ca015a85356681c8 ae83067104ef0f352740e41364d34993c3526051a752da0082d4efc8c981d677 4d544a4c931f03194c05bb424b129192f8d79899750fe2a5fe7cc3e9aef840b5 40fc24933d277677da7c7663055f53445fc5b3afc929cbae4178afad939d288e 716c2ea195ddc62ba386aa3780807c39c3e042a4e79f03452793475908b7d457 b8e0237cf785c18b2abeeee250e12fb5a22efdbdefd5894820a1d569899d4b12 071b81a23f3f26be39bb391910246b4a380cce444ad1cb1effd4ebab7f9c6040 bd2a99cb3090082644f99844cb2ab0f2d1f5cfce729133288d4496468f4db201 7f8544bb96216739311e29f07af9a1b52fc3895eadb31830ec8e8491350e8c0b bbbd4b454e4ff5f89c0acddd19b14484eafb004b70eaf5d0a7cfc188c756e7ea 24da1f46d42c08684705a548057780be21422ed814ee3f106d7a44f6bdf18dff 862c8f5641d50d389149eb0d0de284655c228afca4c11e852268845870a4e64d 430e1e32267cfbbbcd84c9de94dc5939058fa933954c97d158bb1693430729d1 4575013b7bcdbf078f6a87c185fd51d7a5ff02efe49289ce04f0e2ae159a0641 552701cb2d6d15b78fe3250963021e23b8a8cf3c0810ba00032b1bddcb0d1d94 6d6cf29242549b153c142e3c617ca0143eddd298e3ec3346babb0e8b4f2555f2 f5fd1b54ce149d11bd9f4b38c0a04cb95cac94aa0d9b1767b509415451e4d2a3 aa65aa232806ba61ee6f1872af72b4cf02583237d484d7e31c905af288f561e1 a55484fa076fb95dcedb088be3708f17d2961e32b967f61229732893752b6fc1 88cec6159d80d5607790a84b346efcc350a6cab758841b91810c012abb70454a 92aa60a6451edb9ecde0bc54fe7bf4da63dee074738094f5e15ea3f5a2b2ad7f c11d6a160fed8f3936eab007a0dc5a80644d95d8753c1daf340b14819eaa5ad9 5e91a004d7a133d8ff9ee2bfe77a6b00d56f640bdbe27221e7a058f00dc073bf 372e591a20ebb73efea2c9a3e37e0d7915cc6cb400d2312971a10771a283fd66 54e10539373b0c1ed9ef6f94c1cb7222aaddc1dfb439fb914546b5a396d2adca 8f80b94bccaa5a975691c0674a394ad43dc0321fb16b127a02389bce945e3806 723ba2c42789355d17c984b669e86c240ecd728f9600100a70b04b5b42605d3f 93415f3634a46876abf41b43a5b27a5f2ca23b9dd4c6d452c5928f5a45668ce7 b77a57ec378d9ac6037f8575054c8ac172f69ed5c7f8aa68c69e6c97c49b0cab 3ffee6e239e22b781e5358934c84ea176d770e58c792c1f6bb52ccb0b2ad7fa9 c6223ea642c54b0e1ad719b717d2db5cb48284b05628367967b80f81831c3982 88335cf17ddde0313c590e0a3fc02eeb37b2c49ca7f4b99431d3d9ee8c056975 02975c6132aa85156b82beb658c60fc2fc55665753177bbf608ae94af50ada6f 5dc628f20895914d199932dcf1eb7e0a06f3af65124ed03bb07182c623e851bd 2b1eda50cf20331aaa00a4dc7afc6cb2c1a76c25dad1613ca952a6098ccedc71 8e4402f14dccf95e436b50ad00e9cf46bdbe3f67d3194e0a8196b0f438a90082 f08e378ed99060caf02a8c0e6bbcd8b3fae1ab45350065478b645849e121d855 e89c9763296aeaf1ea12f4d1661938e6e75a83a1b842ffcf0732016bc09fbd47 10306053bd1563978713feaa4d64487385e6c7feccf0ef642ad316ac19dce373 ceeffa94fa6d95e71fd6ed7d267c0b38e618e2bdec515b0b4eae41027be9c1d7 0df92013b000d4ea7fec979d8926ea82c13222571729c94d4a8e095b2e7f5c22 7faaa18bca3a957f193a436f390257d1225bc443a573062bb2bf010314049003 3e2c0f701b8e52d3a9803b675f710ea5055ee77d7a91474a46d8a60babef2262 c5e388ae0f1406c5c9e42f555d602a0aef1c3aab4f450b49ecf59bb0fe45bceb 461bce36b23f88199cb74d8a639abafc550e13a0a99d5e409dc91b3a2db54a98 503f37bc81bc2598947b45fb9466ed80ca45fdf1410e4b61fb6c475ebbec4462 cfcea07d188a3b53a7ba20f1f2e8a738a5091c8ed6ff48676f54ad24280a843a 374a7ca36e13e16460e90f599e760e9b26edd397ec521a98e849d12affe5f065 27f20b49f5ae565d216221a8a86fe279457520ce5fa3456e0f0d97d8feb9b675 d1e8eaf7020896a413977a188540858f0e5a780b10dc6347f0bfb89617b47b70 88327db07d9c4ea2e498de9529e008a04856ed52b6bd4be1ac16a99cb8b44911 1fad39b0a7e4700df903ce629a4837123fd31767ce3d41e7f9dd132c1fcc144b 692811713faab578c67dae17ecdc0c560508b0677342ee881c594881f38e6187 db04dec14aae38098a4651f427e81023019ff883a252dfabb833af4fc149deca 06e30eb699385e70cc2ca6101e05b413e3abc9a4493777e19d2f8c7e46d81d64 dbcd388be56f646910b39d095224b933dd22fe5140dc910a17f013397870055c a29857e39a911eff525bb142caf166203182f6c54eeb2fc9dd7b816816c1dc2b 159364d641bd333c6de1147786e3b8a8c7a4628c32bbd150d163e3fb64b40464 21b698d7398749eb9fac98022bd6024bc3d62631afd3f4543986d39538b942cd c551ec21e8caa6b1ec4643a1df433338a09efa8cfb56178ff138fcbfe8863209 9aef8664983ea4a1c4fdedec23f3af3f55e9380bf2a6a36a9cdc571c5b9c4823 eb59e9570f04cfa22f0e7bf4354ab8dbebaf28358ff40d0392cb2a936f59061a 034242e3f934744e7f4e4f0a70314b3f9d1e5b37d1ef56e95742767445a09c9d 2583de3815085630e96c62976f369009dc61fefb8d4dae624156319425abc932 6245f39459e67cfb30cbe1f82bfa0bd4f3aa5a31aa5307b2fa66e910b825f8e8 f7d05c3b21124d16c241f084d538b22935882da4bd0a816585d9285b6ef17b6c 0293faf5cf2993d3a545d6064af13012c4d459cc48e25dded6566cddf18f8962 e35d41adf2d76ba4c6488d8b6a0402bab3aa1ee3d932f3da613756cec52274bf 0afd046ed3289e0a1551a448ba05bea73dfd5ae789e917cc38d8601fccb8ee74 b5bfe0639e7f7c04c199e2ead371a402db675023a9044a174855629e709c2588 e91d600915dbb3a25623553908443f0841d180fc026d592bc3fe5ac6db5cb270 a480a33cb9ee0171391a1d724c9d7d2323a50b8ed4a4cb069e83c350b87778f7 59c2a8e8ef967eb3ebc3720abb2a1a6828a8e4d1e044574bf9b571f066ceaf26 e153bb07a396c51ede01732b7cc5c78da684d837406d9acb9f93bcecd56d99df bff6bc51cadec4c00f89ba7c7bfae1eaf4b5a61d0b0f4b2a922608e01e1bd7d2 eb77114e4dd08804a086cba410d53202de100bd98ddbb56e0015dff6d7788d65 a924e09996aa85a63df7ee191b69a84f0640f64a111183dbc41660e5611676e7 4992455c072a088926409759c43397b547a8803a004ebccf797b3e59814ad45c 402cdd943049d1289d710ede1b65e18bdf886d01a4cfee13cdff2d3db6b514f3 66e0d20879d76a41eb75114ae6968675c24b0698f91cdc90a213f8a259c03718 ca7a075f51c180f8860a603035067404e4c1bfbaf6a58650f1ace35ce5495761 bc2dd735c082e1074ecaa485407adb6678fe15b246d038eded942348c81e9727 9dacf679ff2f4fa10f736575ef63003bfd33c8ae28a213a2939b60e937fe5036 64c898115e7a76208b26371fa54ae7b0c327c5ea4dbac4e3676fb9b43c873ec2 d460b9058539dfc84414de856c7809806e8cce51835c60384496be6c04033cac c90933fb830f72c0906bb771833689e635474eb38a1f24e10691074fe8e52381 ded93dbb790a14d3f189e373dbac72d3d4b160a2ed4519bb96ed06fa158002aa 70ab3125031cd988161e8b721a532ca8d7025f1674291c803952696e12aee43f a92c845887b357ce42f71f0c52050a087efdaac56f97bb8663f51fecd1a6b902 e310c87fc2f6da6bf59c9517537d29454875d99305e623e7fa0cde7b1d51f90e 5524c5ce9ffc7ee3dea3cbd07b9022928b8975a511a7b5110fa059ee0b7f3ae2 031e4d5802e4b30a161d61063872258b85ac07eac955b06b854682c99fda0a57 52148c25c37d6f91ba3944ba3da71fda1045b58c11852e40c7c47fee87fbc932 a2d261f6d3406cafb2204caa898a37b668bbe5ba20fe0a4464f5f0eb40f341a4 f036b100ef31ff647a7938bfac8f7b3de28c645388930ab4da6f11ee8c0334bb 1812c902962019dfae01617662dc7e2f8bb79daf32a340312d833fde9ab8a152 143eb5f75658e64684be6a4d2567e04f75c17af6d9b8f6b5de666e4bb27463c3 52c6b4054ff6967c6c98b63d287722c178697aa10e52b8ac91b994ed2f073c06464d23f199024cfcff967899015bec34facfdcd6125918eed5b8b11da2564b0f8a26bf744d7e51ad91725ea06e48cdd5c62d34521f6dd8dc25a6315d6b81880c521f52f97aaeff146ef8bea8119df3d4b1a1f0f8d50aae7aa18dd3693ff69a0051e30f02dc6fcb4d9e1f02561f70a6aaed873ee170dcb67527450f6794c8cb0dd113b253fba0aa25204e1d4cd7953483dc5c08fb708742b57c913e8302f56e044a7ea57fe8961949160f0d637c69311b8ac9eca88603b98553c39785b0404d024a31568e00784c677716c71b0b7c781b93db123e7fc78fb2fda1b3100c4ebd09680333b9c34d32467c758f83244da206ad35ba4be791d05b5a9f54c79b0e680de15485c0f5a6aa795653406511569541b07fb815120354e081fbf4edadf3f00332c0b86103d5b21fe0874b1429666fa9107fed8e437508c7163f705611ece0003497a0da2d996e583a441ff486d891dffaaa84a02e323104947797062a3da702398ec3625f6146ed5a3410d20c2aaf754d91de46d516c6ad547f6fa7627bd103945a5c58bba3a15782da5e309efd413241e97cbd2eead848af4cc81b02e13308aa18eee0861d118b73babb3f62e74bc1d14ab2e7959fa3511d9bb3a6e9aaa2042064be453e0fd9f5c43b2b54ef7c402632998ed4cbe53b7988d77b1ecc66460f07d93f63f0216cd5cef6a7b8de0d8fa432471b359ad5e854e08660f8c4d18a053c5354b5a331dd733d976df66998f985a3d6a41a10c1b026002ccd0e0ca5f201cbc613df8d19e49468a963f6d283b2cd3cc4cb2404d2db7be375a987b3f49a024c6fbb294739bdfbe469a404dd96af7e34b56dfc708c6ec36001a5cd106c9c0f8abef261c36f96cb89f9113623140b53b4780fcdf9ec439832884a80f149af013f368d0564049966d8369771fa43b0a0b29088a4f299898558e8f8ba36d109038efa662894ab12e7a963502ec8d27dc2a8da2f9aa70cc3ea9d5dfcc79ad1840c04c353cf83d96f6932715e3ee4d57805c6f790b87fc8d06ee374fcd3fd092b0d135ed61ccc94a02af29556d6b59c2e514bd3d3856be414fdb87e32dac5326e0e44ffdfb213c10e24d0f0a480e7571a3384070eefcfe81ccd1131048548b6d90a11fb62fb35cdd9736073ed465e5a835a17b47d5f08cfd5fb2123785e1077f30ba7e7009a1a79eeac4236066ba182a440ffa1c2e0f0d1be852d1576470303a60c4f23b13a04c6abe3e4708fb1161c48935fb0ca3e8789f9d6bc34a9a424d49f0cb543dfb7007f69892241cb045ec7a66ce69562f76f1b1b0074bd474abcb74108131bda2d80d2c92b7d0f880f2f60f53de94fd74b49194a2d28cc5f481219310a4adcf5c31c31729c97ffda8a1de24a61e3b1e887f7eaa7f8d97c6820865cec0d810d767c118955d8729219701e04ffa9d4d20d87186ff19b2f123a3f6feb9000659272888b832f576fec121acb1a9fdcc4c3724fc6ee1e27bf1d6c5c7cbdb60e84bca6d65ad76f12bb464faac2adf3b4f4f1ded01a7381d103eb136d1b4c4e0f9d5a638cb0a878411771760ce246c9af89176fad73e63762c8e45a19e614df0095f821e1d34c550d60c627276fb4874f67149bea9cdba11d7faade950cc67600e8a51ae1e849caab2cfea8f4b5028aa2d7ebf9a076378385949ad1d019f9fc06ae66640d68085b0b06a0a272e4d30cf44bac31888ebed7c5181530f583282d04c33f18fa0b04c8af55ea3627502e60bdddfa3114c193b334926edc1b8d88fe0772c5be9dd9ca20ac335b154bd8ea28ae5cc827168c23306c01222b35678a84037faab65e0794f63593dc38fe0fe032eefd24ff4e911d74ffb6d2c1c567a5d80943e92f544c297092065356329fc1cfadc58b0e2fcb0c6d02d5888174fc83b40cbc0adaf3217361e78d45916975ce7398636cbff1fac35230adab441ed96b6508741df36d63cbdc2cc5728476ad0a7d0f8babb13ffd196a4b060875289410590b5982dbb2fdfb12daa030a55c07a8018070263d2d63148f1a5d100d6836c7ba0acc39430a5852a9d8ae1c9c0577868329d681bc4a4b0b0d617c43bf5f1e1939069e228a818d9cb11ae4fa64989e8a4a11f4f200a0e0d24ccf1b148daca183940b331e6a88c4d93f1888079d80c7d50d27571396dd7115596d2fef996ec25dfa057a26be06328e1b9ec3abb222dd62a931ed03c3328efbb6b94dc3efb5ab76f50620b1e85a40cb2935e6e75737a20f10260fbda437f93d6d05276b320fbe95530a5aad2109fa0a28eb1bbdd241b5c97ab84a4ed670ea7f71039d418279956a130a9131c008b275c261998889e4c953d2dcace45cf158dfb0be6ee2a9f2a3b01d005499a4d6acbddcd3022bbf79cd7f8375f346d44b05829c0c5020efc408fdd50206838b8aaa3b892e8e220a6dba25c9d8e96cbe9e5bae388347c7ca53a6fec201fcfaffd7107c03e3f0aa8e250b4dc565c392e4cedc0aec288b3a02b1461743026ec05530366c4546e1a29ce92d069743f06bc84ce47ad0362cd28337966a150a621418ffd7bea7404ad372d5bda6638a818b84a340a5ec80b7cc10432f38920e337faf85cc30402c856ac8c9e2b57773f973dbd88e55f63b7fab3b968561110c288a2efeb5dac6db980bd636ecba8a5f8f70eeefbe64d143eabc49271435230a9f8e93c2bdc0e4a074af6a14a311da1488aaf1c9d65db536f792291880c7df0d53a458994faf17fe6d2261814b40ca049356934fce20e16f4a1501f8297c820672d25ed62c379ad57e6595c2220e294afbf530a869045d95309b6a5e58087e0ac330f7bed841e888ccda0b7ba0d2de7fc054614b463a8314a9c504c34978c50337eeefe9b8238442f1c186042dd0df22211b026cbc5f50cba477dadf56d26c0d8926e06dfa68a8132567d2cbb209dea85adda7d81607f8bd46a7ca10993aa90c9906bf32fd1ca588f1ebcc3390a3428b1f12e7db44534a358272ad0ac24ab5006f876d6906902d3acb2d6fb6cfc53298feb8807391eaaaffec3273857fb5150b1773a94d4c0b773f8a4a564718cd743aa3c9e985394a4fd8f64c3e3534e7b20568b2a67bce82d7db357d6240347df89db7750ddc72979edec7882f80d6577505e8bc804444d5cf29cfe0753a1c91104d3acab4ff21c77b7c872b47479602f8003d0d29272e4f97c73990f38311700faa0b5ecfaa1dcbb726ba72a598cc55d401f4099207bd086eb20091a17c41f6f853f5991fc0f87849e491017d8ff0ecb00149548caff3994e48f5aae6150fd1d4c3344ea7e0a0815befbe562577c513990925865c0a09026af423c1024a312f033e050a708d98d434f37ef40fb6404c2f02484f28594ef19e9bfe2b6fc6a7095b662a857bca101b08f045209b749d3a4308010d7b61189c5e130882a6bfdccca90af42ab6cd6303d6c4330d36862065d9054eec349844dc941762bcf604c8f8795ca90133fc1cb5439c2785fbfef20d7d068b2cbc40ade9401e19cfce09bb80e0c2d0fb58cde3d11c0396b2d6461c32c104cd5aeb3faf7783b0e5e2c799861ca8651d5663e2fbb39d1fa1b672565a614c0ca524f955a39ca9282a4c0cee1c601763d5cf0e93e454d0974ab7c5e70ff3c20c02bfaa2a7e334e0076267a09518f15e7f889c06c3175a9b5ee4b04a2c48fed037f87a54c866cbed57a2e097b0eded0aeacbf341bc4350d95ddafb8751e33760fa3c5f67be763521eeeaa843db99ef07910a1347e66b29cacc50bf075dce91904379763ebff4c52e6bdc6c54ca03e666ff9e8711b72be74ec2e9c59b0fdb53004e9b8f13d6629c5515260e7c2735ef239971c17edc62a8ed009a2e9f39b867101ee017e7ab570325e323fa8bb15d165f723e87e0543130193bc85758823acbc0f2ceb6b897d428a51a07608789c443aa64bbfc83209469d901aa1a54ca60095031341c14baa760241ca69011b9acef9e6501f17de565981b9c008f143b637c00c91422e2f691ae6bb2c638b4e7cda3cbb3e6d4f0c3456e31a502a70403848780f4181ecd03eaf2c00f3c4a49fdf37fa6f67f5aa65ae9485c2b2cfd096eefea70f0105be5c267b968a294d5c0a2e61354ad02105e7b1ec5e1a6d78d27f1efc9f0ed688ce44bd63f29b661862357083fc294ff5a4548ea9cb4468a245a2efa4d20f6978c3f365e52da555eb3b03f951c5a3553dd9e380225adfb03e67f3d983c5047eaa3f999835c33d802fa535ce301258c47ad526a1f0b63151edd01da155cf08feb33698ceb4daaf75b5579ad6c8af76fd7dec8a5df0a979890555dbbda7aa0d7a73f5cba1cbf641e52071a5b1acb69de1fe04c117ff65b435e2ad360ca4790dad3c6ce339b4878c4859be697c5e40f9ce7d376da4e7faf7af3766a89a825d02d9f22d88fd7827f0636bb3213774e945fc09e2a64916e789f42e4a279c31930631bc93396cdabe7ada246684f192b2f135b52fad439d02ae4c28d661f21539013e56ab3e9bf0ff72fb21a0493accffe10d253784ed55c1e39b8d54f521a8f6090ba9e6f63a0b0f3163ef9aaf8846b259efab40ee8b17e29e662f8afa54014f08480febf52d82d7efa950719ebba7afca3bc23ac74d530e3ea79b1040e89b9c026eed654cfc2083ef3cad31692690af1def14a039f6d8cd6db2c3befe1f969807976ab6e5adf01f7dcdefd7382a8d8506c9badef4cc574e80f8eac176ff7efd0591c5c4176b4be7a48ad2d90853fb89ca6c0c24a40256901b230831eb41e1500438f036ea7fa0dabfdb6e1dda4dceb98faba81c8b664fe40aa569ed037ffc360dc3ddae18f3ea237d69eb9b4fc6c0301f9dcb023638ba35d155e2416e39cce70bc7d34f03a585920e7663e283d53c3f6a6c4e3856dce8b2ca17afac1acdb89907e7939e822d00100180cfecd1ea3c9143462073e5938bce57b25f57e9423130097531b9adf0a2075506e7eb54c6e96cf772f5bc7f734a6f116dde15477334b301ccc816f6ed28e029efaac560002f3d5f1473ce588d4fcdfe36fce714b099ad0d78d919f5ad39d68b460c57d25591102931ffa9d942860757d30020964d987b0c175061ebaa2e8cf949dfea229f4cc53adae24db6696490e2e3a99e4c39a5e309aa3e867d503b7b2f60922e3cb14fa42ba0b5fa91e2c047709eb7691cde84980e0f044505067f1510dd600e333bf9ee195f0df5d6f4c81f5bbd8bfd090b5b190ad3afb9498c7aa8a7d4becbf387159ecffe1f435fd97b6a6bf26da9317defcb0e413a482f0c52ae00540d79fa559a184c81c12c374439f5de2a3977c1a25aed0dafc0ab6354a7a4f44fff7332e86e5e3d355c6e4972672e0eb18246d141f02307ad6bb3b598af6bf89231332c78a72f8b670b02cb76b4ec27a409ed9c32f065011e0d3cc395a9162048fcc10642e46737fcff293422fcdc805fba1cc32673bd0105c3665495d442b5610fa29169d316d422a7180d9c11b00c7584c5c3e1f435094351a1779fd3d8b587a679db819a9165133ed9bae3279aec8005899cd9d3890772b0aec735b3664dfd161d34d82d854ab940365fa7baaa7745a80c062a18f0029e39cb30f5976b00dcfa393602bab35c7f88595ce6da1833ad18f9b5f1508b002e4dd4d54c4aad08231ca741f36e408e9fec7e8265167d8367691d8f58ab0c06e16e0fd6860d1877db0bbbfb2d1fb8d8218fa082dba7e944c7adc1c21ffebf06bc84d456cdfd5730de0df9cfdb1881d5fd189b13bb019d7bd0838fc1e73f9e0db6e385fc1fe142d1ecc2c44e8d4721af9ac4b7e2692ef1a7d231c9b7c5fe240f11f2da961bb512e5e7f1cf1bbfb428455a49b851410ca5bdc3f3af4308af8d020322f3db14c56e82dfa53b15b0abc58634917c951b0ffb9cdd2da10536371e076b7aaa4117ab940fe68ea1e07ccfc7fbec6ea3ff43a788fc298e87f10a4c780aacc86bf3e180130924136706742ca0c11de01fb7104194a5dd71ebec57d5e60c4a6e08769d8b6f986825b0fe146e6f95364222dd2e3a38ab95cc9f476807410b16c6e60818992a8149b00895e5e8f30b1e59dee8bc3c7fa8c6436f98812b3b080cdc760befd1a0bf26b35d10fadbee0173d320cfbf4e7da5424868d6fdca95051e579a64c066c46e3a437734bb12143e3255e9b7799a51abf3ae8f6d5ac2ee03ccc8a21e60d1613004b0bfe36b07db1295313f03a81a55601df4a500c5abb40cd4bab874d1040e3e54b67fce9600c350462ffb0783bae5bf63832d341a06e601209edbfc3e9fe150f155ed4859efb18ceccb8c8d515481e78446e4e02028e603006256e9f0efee184e8d42c0356c6e83548f5333b45b6df5ce141c89ee02fd035d00f3484c40041f209b360d8e69fe481da11d113c708b0bae0e17ec0c52bc0de4ca892ef0d2e124abe7f4f1d4706e2b3e7b57d694d9723e8f25e6d3dbaa6a043ef1c011174ae41d254344970c10fe9a6daff15e46703fa063f214228739150e2bf294c49446255972b9a263b08b84b0ba06be9e9fd8312f1e4724e8864a510bfe9d2ead30dbba0331e6ff803c4fd89c9b3160b9221c6a1108735d5a6766270f8385ca6547285c4fa003d0f4a862e59d42e6c99bf97302eb63949ca0b9c97605ddac3253bf96742a48b8ce832f09fe91feeb8f1c807935a23dc5fc190e4795015ca719ab6a7218ae62a36f24f7e3a9812155e880af475c1f40910511fbebc305822abfb7e95fa25b4f9a5bc98a2a576739d2e5a30fa7ff7750a25bb3bfe83e0714e0734bede03f4a0c41e094cf2643393cf5a04ce92c7a82599f094c2a941b01827a8c1cfc9f02982659212a86f45d09b9af142c3510507fe69f90ccbb0c4b060c2f9d70445df74ea1138efc7117a4bf002f344368a3a7904cb79a9a9e7da40911b9f513662c06098fec1be7c40c97e9f05a331d60682f66d531a1f1f9d6dd0b3a7d4c809f9ec25363029208463f84cd7d70a8ec8cf60e5473403b83252552036199e7aeaa2b6c0c7bfd30e8968d1432f99a9eb2dc32d5147a1ea27c16f93f092d3bc57c1b3e6ab635e4277f83081cc8a0309dd6aecbd1b43e45723969adcf059225bfd33de7f009bee3c7d03d0c00a40825c28009cdcfbe5ef33b3ba01bb804256b65f7a78d560710ae78ebc0344d2a13c309a8f6ddceadc2f52af8dde7d20b9953d68f3872946bcf7edcf7a255bbe01440e60b04fc65ee8c1068b0cfd7be0ac368ff8ee680a2b00ba9ea7ea9e2b43fce3a18e10c75746ed2f91261d9ed650ca1abd106b78e7f6c2c10d4bb547889dba7481c797e0ec56bde0a6ef960289d023af4dde3bf411fdbadbe6830180eff9cc9d9e57f6dec09a98600cffab10f400debf2e41dbd4d61b4244a43f4d9ddb8ba633435e2fcf9e788cc43c3175196b107aa1a5f17b5181782f121c9921a7e906274d8df16e0f05cc5489d448b2c6e5d01310d0f642e44940c4a17a076bd5176e0e3dd4d53799f35f0f8a6359feed03c0d70ddd3e9d51d2cdaa654b04a4742d040cd70def7fecdffa90d2b515e9e92ef0976341d6a427e02fd096de387a5ec2e8c1cbcaf9c9006903dfaa4c535a74ea90f1cca68c93f48ea579c10d9b339b9e3e3c7e9a94299584e3b2678907c3316350ba009c46090918de0b589b3c43b7cd3706af65e73745e9ebb1fef069c2f343703d98290a20cf8684095b5815a769dcc535665ea7f8358a4b2f6319c7bdd12400bb19d9c80aa86dd146c6257dfc98e3ad3a2d1e94d83228799fe1c24827948860b742257c90b37efdebc99f830955f946875b74402ea4822eaa895d27ebbb3450b0da8550f5b5ccd99e1067a8f7656f5b10310449f0a8900487f67953c65fa9b03f3365eb6e9c4dfb0779c5393d4b7573988f7e6cc3e42096dc129ffc852d32f07458b2c0cb3e19f1a8614c4ff89b40c2839aedd2aa0544dff9ba322f9b45b7d040391f8aef742cb928ec60a29843214d726475a4ec67e3acf391ce09237d98a051d5b48064ffb6c573d531dd252c0ca51f9047e525dcb02e698364fda90ace50689b5b12d9e47a54b207322cca52f0aa8d981ef9299c7b29999cdfeae2e1a480b24d803726b62288eed917329d706ac9ddc67918de02f476ce40b7eb397c49d064d4ffb6452665dad0efe926343f7bdba954247ae43413b712619ad8c136edc03a61a1ef297cc0339a3bb83f460a7af5b8c13adbf538e6eea35ba0c1089d1fb063c9177790228b1e5b9998f0eff9a2b6a4e85e79e4fddb2be376669cc4329370dedad7859630a4308a01f3a0b7d51ffbe0418204bb2af557bed31164eaef23d0103b45f1bbc71255db93cba05bd43f4ac0839a2b7c016e755f000bf78e97b3a0792f4242b1f3ccb7175b282d43decead92f30edf5e4eb772faa087238f781900a4b3aab51c24ae533f50f7fe61424dda51156e4e3b7012ac2a4fab907dd6ac0077ee838e8010870b59990a364b7245b1acc019c710ad2155645990b954502be03ba509ecbe37619cf233866acfa8754dab123fd772e2a434511dd9c26dac1ec060f9ea52b4d4f035c67e363c2beb0e180135afb48448866139d48486e8f93c90de910ebb43deb2142acecd2a98b4c203a49d3b4041ed6c2499546730ede264609ce5585fab95326011da864ae8269ef1db2cad4c580e979b9048462a20d63e003098c2bf85dd7d132a13120c368aacbe72882ac176a2810b7c904eaa0d2cd3b0af47db21e641efeaac33623035255b9f72e799e7ab781817dea9aff92d15c010af6719044f6011d19e128d4e6f07d7742d5e35aae08680b31ac07d33e724b2009dc40fcf5a634dda4dffe7d7c601fc7516860ed24d8ba6576bae69820865919021ae16290e817da412cfe130d2cc322ee83cfb635548e5445845e20c5c4274b053d35973fe23c7abc39696336b194fba7e81b010bdaad9225521e41f311671805ae49e6a4e5f8ac3eade2dd62dedfd03df1d651342b824ab7e1c229ff87f2e304cab0e939d8ff193d798bc3098c9c5b2c2a7a83b448c1096cb67f92bb38007201b8bc5299fa572673f6c3f2c2a56e14593afc825f687ce03400515ddc1cac670ef52f72b53135ee5d42826ca63bdf7d25de0e92338f0b40cd8881691ce238f3047b78f74b5a14403746569a07345288df4bfc79dd1dbaa8846cdf4e04b5cad20d40d661190f640c2453c5a2d7c0227c6b3f8d44eb6a83266bd4a496c6a32adb0c89f90346d14ebd167f3f8dde8c8c77817fa2be15c6acc3ebee017d9a12ae60012cb6cde02dc42919acc1911195f075f18728adf71ca74ece5cc58852ba87c70f85fd2ea4d8e831fd5cb295ea508e3d68586b6494c1b4c9e7fa8a841fb9c4e50096031608634ee508c49c82716e3dafde761e1aa231cbfcce154a4012ecea2007911433e8e6161424a85013b6b44a6bf7466dfc080a1b00a6d55a7e98c917f8091c3554144725b7b6aaec529b7b85a810ea33cf722812fbbcc1a3b1d34feda304b7fc53ec2e37abc7534b2bc71e57966c7b28f5a76e8df1c0a33e365ee6bbb802af762bc43478424a170220a4dcf3d073e2cbc895ceeb29d32643a6018f1859060885440a55f87b175d7276f7b0b2ac1d0ffba14990ac669307295151c3966e077c0b9533e6ab612058e7975a5bd070e978576985a335e4412f9e68dfa4c1580aa40f906743fd66ba4b01e39461b29dfe4c2cb507ed457435409115d311c7b605e9a9053b7748f8236feb55c7006246b22f0cc2c2e7f6d409d9d44e6146d6c208f70c878f80e9d68a92589538162f679e94b28837a53f6ed0916365254f7805036316ec3ca0841cf63485c7cab91c2bae2cc036205a1d130f8516bb10831d0509a58aaaa72cc00f127c0d2d427c3d2859973be372c4dc07f0fcc6a9bfc2849f0ad195d6fc72d47a531b1b199a77eff81ed98e172ddbccbe3c4c847b82e99cba0f923ae89114000b620e307b8b4769f56ae89faac6a3a1080f106d3579a3b9f80d43f35016facd4a663c5508bd3f03ed2037dac396945189a1de3614e3e26516097f210b1dab42f671fbd1df45e2f13f535364f5b03b26c8fb3127f3b28edeaf048e74e09647232d4940e973b20e744a358fa0cd5cc464780cb9d6f08396abe1000421c05a0656b27ffeb47b444a481dd9f898311bbf2d44f465c8eb1b0261250e463c186cdda71cac8c3e997f28386bfdb7a7571dc38a433e44dded16e9fae303ecaede8dadd0dffafb46efb8fdeb39624e84d41ba3bcdcfbca5be60113a1700a59b499aa50312f6119155c134c97490f2b6453a201d64b5fa222856db02ff30289830cfee0f300581729954f48e9619a54d47b78d1b9e3934ed267ace9e3730cf8f2fd06791aa9020e5e1810b23e694ce29e3e6720d523b47ccbb6e55807860efabd30c56a98e06f132001fbd100f976742ea5a5898180244acf8ca1421a0b036f9c3306ca9292be6c00cd7854e85333e1d74561c5af29e30c0c483f9133880b07fa61e9e1bdefab7388886fef92b119b62b4904d0142cace02288cae79a2007353c9c021b1058037822b2651525e56b0d2e4bd74d91f8ca95921f96685edd068166077f816ac847dcf2e55234810d4b25656349843c0280a29309806fb1d00e5b0c1e781ca817efd27aa0dead44081c4427b2cf7ca498928aea70395bbf6401f85171c9236926402aad573df02bc8f932c7b7176ca749d47e2a4a3248e67007563672a0be5ce535704aac69cbc69e0cb4ec9762127161486a091977aff42c0e29c825848c167e7c39454ceed7a278dd41ecf0fc20e590924a571d45577a5e0cab46bc23ef848073ab6d4faa13a57b42cfc5fe6f9ebf032cbcbcb6aabf9de207410f8919fecfe8bb1be507c7b257b941583818d0a917ee94c9fe9231098dce0a8d545dc3488434b72c09a6604a0838845e79a2add520a62401d0f159e19dc10f75bbc164965dc4d918820409c9e7101cd23a0e65ab64ab6d9afa95394cb0f9073efdb316c1b8739fb8776bb310d63747332630f9c3e1334eb6b405484a584f05dfa77fee8490bd682f79df4bdd7ed1a468ea5512c412db0091215585aabb3308888fe1fcbaf6f3568f09a079d34baf22873e331f277deadd4709d2f015d88809ac990d244b6c8aa351c9d358194459740cccc24ea1d74c05be4f2a2740e1b800b4de796dbb914a73064db75b7c89f53264ad92d37f240a7dd4b6d1174d92db01b2febe3a37d013590514de648128004db87c23be67b3a18d4ec0ae5e0b129a09fe561f1aa6360e72d3e2743f41088d5c3b83af505051760553e8f2628e6ebe028dcfa9f8a3e665096b401c4e8f5dbe55cc4e71479dcec01cfbc95a16ae2e060637c9478d3752690ed3ed4f55bca993e2dc3278f88d6d2852ef90e14eacd8e8072b47605ee41f3468fbe3740a03d2a9cb3c4003953cc257c4289464d32cdd78020036412bbb1ecf296693027f782311e6788530babd27ad5af15babdcf83cfd00c31a6255df23d8546cf3b176bbe8eeb6730dd8083567ce537770fee19ce1fe011821a3a9f6d6b53d436eb691b5a5b0fec81413c157c6e00886cfdad20a462b0a14ab36e1a3e0260022829bdf8a6acc723c67f89e0e951744919f9ada35a0200893d8fdfe43e2309fa2248eb16fccf2b22f8049f3582e7f5b8c6cb60abb3c41001dc0326bc77d58a0b2fb990a4c9e85c29853e8764d94dc81734e9cc57b09360a32e1a7a4b2199b502a660175009eb6cf49073a032e9f3351ed6edc30c50a340dc0b8ff266421825a5563ac30da33913cdf64ef3f976450b490109c583f4d7608b64e95cbaa283a2cbea6f1a8d9f54c6dffbfb7b093c254d5f6e680033fef9a03132c02a17d73a81ead0710deba6b19dae5b6599169925a645d80743e0ddc820d79fe677f0cbdd4041af4cb1eaa9efe7dd494dc2f3b054e5be3742b6cf24e160f0d3fba95c25fa9800b48bc7ea561c5e0c529d9d3bc9e717d7654f882dee7b402eba743cc29ed5e15d6fdc6e00d6682ea27a14613003bfb31827b81db289ff00d9d09b75aae6264c8d8a31b7d790091d6ce6633396fd8951bfbadbf9d28f91405cd03503d9eef472897254a5eb7836986bf347a2ce7ad1e720606b3163677c40c2848cad29b82067181f540ac10b3ef8d2800dd1c6329614f7d56a0f762127e017b7a119f3aac7431afcf6965b3a37eb59b4813d94eb25d149e46e5536d6d8b0188843df5f4ce8963a5bc08c27202797b53c9387904f6d004e9b026b9acc07609175bd4679e328a016453aa5ecd0e67e8b5d0640aad4c6ef5f858b65847c21c0758bffab861c0219086ea36aecd3359770f9b4097b755c1e090a989d8ff08b20942c8af6f58d0b548b65814be6b652ef6f16055c5d4e5c0a1eabf8dec0e8e6f0fb67fc8a0dde2d2177b2ebe6bd56a4f50e082d5df40f5b0e9914846a283553b0972d8de38bc39ea05a6f8c2814ba5f7dcd3afff5dba6f1e49eed6b7510933840e2519facbbefcb89b98794a081674b1fa383d947bf8ec36fad063cb2a0c8caf02c04f26555b053c9118bbbbcf74eecebeb0010a2184105690c43aa54901a6ee007aa22fa7ede25287a12951cd2c1a367a5fdc29a38b4f2921c6603c8a3872e80015f9b634776c3ca611be1fd10f2c6b799c48a9198abd6f8a4259c070e36d7a03dda75ccb0a9a36f5208e0c358d68b8af6361a0c345e9d2554b6633564b5bac0d0db65f6cd09136745da928ff79bdc347289cde261ea047dad6b0749ad017c00a122c9b742f173efb635ff9219ab5754c712697e5d71b824d2daf4b7870263d0305f8502c756cb972e3b1f776e830195f9eaafa32742af2af3905c1e153fc5d0cb211b85281f9ac16507d0ee6b857dde601006c799a5cc458cfba7b8098eb0f0a0453595db4a8b0bc405e129d9ec94735ddbf76ec77af0fc028264976f331650685d348805b674ebc5dff3801c4bfeea12eb3cb9e9fd207c01c1c23efdac1f701f53d8f75ff4d68c7caec7de59351b93844c927d411767d938fe8f8cf580edc0662d8e3742787d3fac5756b9992d6dae4a392ba98dbd11688174671b3600da20a9dc307b7d2b37dfeb86ab3e1404764cc10a732fab512374a530ef86d687465077a93ac4b01421569a8c37e709b15dffcd8b34d1b401d569f051f1c51149dd60429643025b8e76d913839e8da5c06731cb5309a0962fa8acd4aae7ecf2e865b0926342123f41be7fc30bd14c535e9b85d5c71788237ad90ffda0c890828c25d039835c5ff87c73c9ba39e219176f064f5539411305d597af5010895fde7884d089992a08e04ecf41fbbfc359c79ee8179beb90ce2d713fb6c9a6791c91336c40bdc4dfc5fa3174a072f893f1b7f5d74efb0971bf42524309c529db951c48a4606811401b37b3b9b9867418fed37cd533cdf6d19acf194bb0d33865d7e2bf00a04551eff59fe3d9061f75a3435be20bd6adfac9025925b698ecb6f1bcb89d72007c37222a46486c31f249b6f1c1a217310c283edd742459462bccbbd2a8bdb270b765c117ed5a01cdb25ed63c55e83b906a54c903e4c8ed1281678e6b4323e5306c725c1d55bc10b13504bfcdde2297d918f18bd0385df814656c3b31920e26e0c9f459f1b4eab5d0dcc0284b3b4f5102ee112f9778d2fd98b2b46999061acc004 true +check_ring_signature 010b58be5435474e0a2db24b147587a84484ee6f71df21b8e373a040e2422149 d40d4553a971add7da3ba93d549a797b4d144ecbd66fc725bf5c5565b0d57587 59 171d9092195046fec7d1a5c14a8d9f557f33a04a87d5e9ff6c37aa2a7cbfa757 308f8e9cdd569dd3f4cb1412e00a451c46ed8296e0419c137d1934ece14765c2 7e62534b65a313c7c92cab906c9be3bdd408d4500a5b2d2ab83b5d1ea1440916 4955b08cf7bf2757dde48f20eab163533ca3a278f08cf50bc84550526e9920f9 46d6869931e4f980bbb6d372cb0609a15f6b5f9717810d8ae73fad76141d2c47 c6a2f3ad716005b53d536e6c05ebe1cc3d3fc67c71b96ce4bab58ea4917f5f2f 2bb0a23bb657ae03d33a2d0db84e6142d3dc9b06ec0acf8f416a445f85dd8f2b ca83a9b4a23e7e0d0c7e6d13835d9848a4172398cbda5539e9a878299c7adc4b e6bf3d8aaef0220d29dfa71d1f517795baefb8282ab76f0f4883fae4fd61de20 3c285e526b5902ed599126c78d98efce8b1a30f5af158ea8d5a07e4d54df2ef7 190f26db1d6ddfdedde3d0d331a975062b8c568457a8101ca7980d78ff27386c 210c4859e60b5fd8fd60b6d43186cf59095d94abe6932f3d6b88523e4bfebe57 440db1980066a316ee0f0f46532d460c2677105da88621fb83099f2596e584f0 494f2012570a9864fef785175b3b81890889d91a967ed26d04fb84176eee3aa7 c93cb93583b3434652a13f520fca8460665f1dddf5799afbc6168c3a1320c7af 2b3ed7416e1cb3a3e650f4fbda2ad389117ca0b427ca330635d628b3e6e6d7ed b70b76c598c4f174aa0da27f47090a407d3583bcaad4c9e4a018dbd34500e4d3 f3648244682b645522fdca13fde1a8cd4637e8f63092a18ef8ba30cf934c3846 a97adb5e0270f9ae559574ac60e6ae320f8ce2118e63a8bd4a8c639c2e4dfe00 0cf98b7411ca3f444bd643594364d62671a5552362d5df136e45efaea26ddf89 9c7ecb82a30cd1c54523d07f14addc04444a88cef50e7e72183855de17833fc2 b54071fb42c55ab41322c3688aba2ee1c562c95c3f30c3a0f60dc79ea2ac1531 7971994f8daddf6ca6a118835683b1238c22f50a40d8603f68cdb23e56ca9740 69eb9428d578945e5a20d2cb416f14a32b771d4f65106d3c557380e2b5d5feec 0d2352690f95b987bc0c52c4b35747060f2c4902b539e1550c4553b570bb6192 8e4c4b52b0d440f932e7e0ce5b772999f1cb88f1a3c3819f20ecb56533f327ee 839783345b45bf4367e860e669d5ffdaf0fb23abaddcba3f7d8dd8b920c5eae2 4b9b6806033d5fe34fef20c4cd4648b0ec376565cc3cee33cd796f612b69ef6d 7a611bfa3fda23269de87f2be122fe90bf449a9c591cf78c21eb77174fab1fd0 58470e4cc14f492a6cd9be33114013708358fbdb4541ed6ec4a1e8a17c137896 17a685810d0bb5a2df9fefef9a9cfa36a2dc2fd5e7eec0668ec7ab1221f75898 12d253c4ef4949e3a3cf06f3fe51b4b8ca17a0f3c945c9f0d05a4927c739e40c 77b70defaa62ab9e077223df012af02eb07268e66174ba99b826810555507cb5 c35d5f082a40ee7d9a736e8c977e298322204f0bbe898a245a333b5b5540bc42 84520bc609fb79e0c87d81da4b5ad98c1918a5736ecb028911c131612508f331 f4aa368e3a0e4885b532199de8fbb7062a39c35ceb045c4e51f83590d1f699f1 a428ba6b04a7c22d91e0df862ed755c8e0c83f8d72bf63db47d7f96ba310ba52 23df67a50782816e36b9a054a3bf7e304cad0dade0fe676f8a2109c83d322a5e 03592b8df12bd7aa72f1f523fc4e0a025612160ac1d1a6f1b06a9c51fbfbbab1 6d1edc39f74dfd93d23b4e667492389df86c9ace2353ea075aea2a000f181645 fd2f196a60d1ddff14a3bfb943366a54af7750fb5c78cc60e4322dafaa0f1d1d cb038d1eb90d74c8dc0beda8c94a2f6cdf0b7c8aba6cbecec54a8f494666276c d8d0bc22a4a353d4567788b69ba6973d4cd7b05fb95a401c13c19d049423b426 ce9bce2655789d068826a686059a5dc5a3637cfdd6a82120aba74835def371e7 fa37e9ae4da5f31712ba7aa49fbc8f243a7dd6c6e764e584e43954a24b352c9f e98ef7a05b1a996093e0a6354455a1c20e4e3112fdde246ea9222d5abbc5a4b1 44a2b9a395d24c007541f31fdde18ab5b0ec7ed4a7fd64558af63baafca4b414 023be8eb577dc2450be881285ebf3463361d135b882cbc7e85474bc375c606bd 0d47df4cb04627e139355cfa4475be42304b5995855f6f0bae7073356e4b4e84 0da6f71c8e719bbc71219bf5030831049b833212617c28ae700011b8d01a51c8 1f0bb05404e12c9fabcd9759fcf3880f84997ac4c92d8ecd03b298136ede44d3 56295a0cf0f49584f32c274fb1fe45d5d401eb89fa8fb103dffca0cc27f25746 0a4ea4196e7c6deb9c5af3b134b4ee086d3a26ee34777437191e7ef1722f8dff fc68738bb88689479b5cfa7d0f4b9af96a39d1447539b373127501233a781f92 a7f5e0ed8e67576559bd531a30c06ae8ce49296731a6a6d2bc48b28515141e07 6f981d7e0ff528290ad55ea48ec0a0cfee0db9265803098e92a585d1669099d1 d1044fce096308e4e3b82f0809df442c41fccc8194e10e9868c8d068413b9562 7c159b7e6598c27bebd5e19d15d4bb0e19b39c9daff826efce30175368f2b132 4fbb5e59498394f10ea533ebdd0f75b117c50f8d62e73c12a6fc2c31644326ca 3e61d9f330c72f6cbb62c455ee9b6e78727bef731c1a0df574ef23b8ea77950389d96495c5ee3094ca98ceeabe6ff964a429d6109b80a81755243e7c940271006b8c93cef7ffa60aa4d7ace80f6a8ee0e4e00db60ab86772840def1b4690ef067054251b060a8ee75600491e2e5a0317cbcd9b888be5b69100aff9c601d11f0a66193616acbf091bf2ac95e8e764a4b8bb78133098537f0418b26142877d3e01817f3df8a978a94326e82207a72882a679453cfdf2a4c5bca4dfced2de8034098327470dfc4596772cf94c0af334a16da7f5c7d0427ebf5a1df4c5d17a5074066eb9e252c352bcc8df850d6093d32ae3f0d59f1b0f09b69b3cade9e0e04f0d04fec165940a2e15e1024cc8da028cab71a03b289d16ee585bacd553a1c88bce092dba2fe15220045c0f7fea828383a3626b60445dd7f621435a86f27bd92b620ed5c7f6fad63c08b70c916e453be440fe9aed7e8a20bd7378c524bb65acf42607cdcbe2516d93201b4f153e9b5b7013b591add7bf1f8bc2504141852e223c2d06f36d495351da931a5ab77b398f6ce7445c626f8a6bb4b0fb4c5ddc1deabca90c978d52ed4910f8587f3e42f59193ffb9fef4ec8033b5341526317c19b51c9309eb2f6633cd921aaa77898e8f656b0d0aeafdc0d2cba5f631193802438c72810d09a92ab4d487ff194ab36c9a5ee4be1ff8e5176137160ed445ad9038c2c77b0394a771500208629db8a1d99ade8dc6d8e33c2365ea4d85399eb86afb2232f40ff803fe81d399669ec0a65969a9dfdca07afbd1c3cbcbafdfbfef97371a94400c42ae206c8d8ff3bf76a28de4647287adc08be3c4947748b777f661bff3c90e07c2721779eac2fe38e95971ef86a459c2c554b290832de7b578de09e8dba791029837b1c361b555085f3f29a931c839605b40542e5760274fde0401a1207fb60062495509e55c70a90b8475a0113725ab4c8cfc2b0452e3ffe42735019f6b6f0dd57ad6ca5c6940b5e00d79ef84413f5c9caf4043ce678c51e46dc271410e0f0c2d65c89075fe588e01ee55fe207bcfcfbd1fd8c2521c75f0c69d3008defb970a9bf1b8e9526b0eb0e42b9ab626e960491dfab29334420b7e2d86d81c85d98e0ce956d06b6feecb6c92f14ce8463ec728a663a1d697584b7630a09e900866460b040ec55bcb3f0bd03a939b569ffc0b96ba57daa59a3c83fe68b611e5eadf9e00ded784aeef0dcc19f283fdde731796b7e3292d3186ba0e09a9305fa721768c0c4d14c3daaf2334038177a217769c8b6f63051006723b7467144d7362a977ab064486185de75b7dba8a933aea129d4899e80785ac625ea18fdf75b1eda7ed000551a88b8d864bf3f261c24603bbaf44c23b457b7fca1444f763151021cd641b0975abed9e08b873ff9ac9c62a5a4b3c446c2a0dc1712fc3418f07131268fe220c4bc1c45d4d7b6ac2aa1bd1ba3af8f2ec78c6dd9e4380a58b7818e5d441c8dc0d20ae10a201589b388b2dcc5e8b646c52e6dd6231c762bce1aa6d599532a60c09e23b063025cb70c5b40ce17421f2f2d0802df76671711771500fbb0501ca69060241a4afda358fb3013f171ce5e48c9e25ca93bf1a7acfe756419bb96340ac0f57e81b4217c46c896e65038e9bcbb082ba0ce37c9a4a9102838f11d9e287c504dc4cc84cb24c9054f2482e8422973e8a7e1f5493e7a17a1163183c2dc919010b6e9cff4486ee411949d06882050c2e65811c0c1ce2860083254aebf68a152b0bdb93d2e5fa458a055278f742f34e34944d4126ce723b5c363f73cc5aa884db0285fe550aa972159cc4b1077e533daf2e5474a77c6bd881ccf6bdb79c525133077a206fa824970ed35e81fad48cd9962dd4ab858224ee681a22ad8ad14aa3f30003d81b065b75afe04099689c991df22afb5b823dc1a47734107df705b4d80b063b924f8c4c926db5aa29b4cd639ebca1430d286f9f72c7f5eb3a66cb6bbbd8030b6ee120c43e4d5c0ea38bcfa08c58b83e7e26636f4f2d7ce0b1e7253462a40794f7fc5358e137bd4bb081a91ede816802e61b82f0198231cc1af4a36f265d03cb91c3f7124bcb5c72791403ee1b17636b4db48149bcd4a9e06c4c1d9361e50b8afe55101cd7614e1c266c7eb241a2a66d5f233a448288153cdba14146df7c0ef5f2588bc58d7536d024b1f3458c7f26db045c6b844b940a52252e44e26ad90ddbade36f4982d2d80337f318c60dc1eb5621888166e9e38f07e5957bc3f0d70c4c43f818ac196de7aeee4514826d35823aea92ecdda4ddc7c438229eeef2b70565dbf8778e321506b02d0af1a9cf8e9a30fb8ef1a40404d7add500b6cb37cf0c5e219b567e8ccefd779f4eab72519c858cc2cff49dccde051c7e06cae5901500f99d85adc8ba4cdd4f7e1c8204ad60bc354ddd797fe1a5f68cdd320c9e31d70da51b4d4e9586dbbc7376e6b1b9baf1e9206e696680d7c9874f79195ad290a90f03d1f2e15137e107e646975782d3c352e89be12fc04fe4982b9547ddca40c80f7212ac2036b7c9b02b8cba8b82481f8ca98ef54a9e11092b158154568627a706906e557466c6755f7ddc016a85698a62001755b6ed0dee3f71b650be069c190f5def4aa413aeb59e8565c3da2d4ff5a7cae34629472f360061b7a355e0cc920ae6c3cfb96e7f333d12ce6de9627c20987654e2fb3f379e2c857b38f11685310037b573f138625d5b4595102c92da209d887a8a8479bfbecc3b277a7f0f48dd0c09149ee0989ef4f9362e21e4f785ffd36709eda2d3daa31a86c5e8b81e6037d1fe845d925d2a015606028790cdc1b6888138f23e44f1b26d487f191eead0760895264a0e655e9783116a537318f83b9528a161d141b302842143097ee7b6270ee471d5f37ddd2bbf8f6458da70b9d6014adc18bae8d480a03d69d4de1bf1eb0d419a313e03454efa90dea02dd523032a901e97347231a77b81516520ab5a400b9ecf5216aad35c67d831fef973934f3acfb616565fef83dfc7d1015dc6a1510684335e1fbcb00237fce338946b2c457aee0f23fbcd9c0e1aae99d48cd5d321000a6901f3dd359bb437ed294376cd33ebc1a281f77117d9682ec017a64618960d04b026f36bd7f53c9932719a7a27fee261913f51e78ccd99d859b04f9f7b500466d248ed1052e7d6e902941ad37e7ee5124fa5a0be721be6c3dc5025af5f990bb28ebb905f2fdd7caf9bdc48783bfc54e8d6e7741da267f42093d276c540a809601096bf85803204ce302c3dd12044f9dd03fed2c6dda4788648b96825ece40348166162375157934fb676d6edba91ba6d32172fe3b2d3ef49110271f5a6d704f6bbc00453aab6059e8c77fd3640fb4835b6e7ec9bd02a2b432ea7a9c605ea022048535cdd572935314ab8c42ecee87bcb48180936c7f68e644019684e258a0a11bdb8ad898b852213a2ea0ce8ed7f0721e9bf01baa25fc23fa845fecbf8720e6aebd02fa82d2f3bc8abc91d71aa4b552914fcc634c9000cd6aa059ed6c1fa01a8bb20b50d33d47247f382b81bd83b47c123a900c162b93831fe6ecbbf34450d443c4607a659f46aebffdd0984ab756cdc4886ee08478e415348a2644a5df907142dd27cf5a57d578fd38e196404c144056693f26be2ad96032550684a3b590e823db094bc04ca09142d5bbd723a2dd4c186ddd590960e146ac8ed58f8643c07cc56d2924196fa4abd3d4fccac34f9b7bf48c774611032bedb5113ca04dfee0df8bd551e3d56357ecc3c3c18161ad5cf1d0c853938c9d3e738a6fd756a3d710d9b21ec2f0b77ae857aa0a67ebe6b41733de032c4e439f45cb94dde65af08c70e33754b2b972740edcc9936c93bcc0b1cad95d9a54898a1c7abc11a765ec18c0143f4e16c205dccca3630ffb2d156326012be18b9d3698c17759e9c6e0e164b072a35cd9f77203c233f73a4fbb65d9cc6559e500000cf780c12b3b2d36ec84508bd4f8af43b2b895a7dea2b7b72c347dfbf85ed6ab42883261922367d8aa84b0cd58030ed5d2bd63f9183bcc54fff6a1c0d789453d79b3dfbb61ac19c7a2b2403ada099d6c8ac05dfdd2954ea8c25fa86567a5df0c42ab74270ec0e1d7b9c0301ddb1e71d3a1ab3680a6515f476ef6a7f64ee9cd0a7d350d7bf5b60de4aa4290c719f68ca4de4e95f948d217b07fd6ddad54abcd5f81866d87b6ef1b47ddd700a1411af21e6e3ed79fd21403d3e9b364a365e585ca2adfb693c1233b5b594ac0b8d5c09a239ab62ab9e884277503cc43a665887a817f2923dae635be22a5df807184fa3e9537ec7e10818a9510a97d2126dc23183953a4c8b6f4d00cf63d3370983e22e2e4ec4ac4e18c798fdfb49036950ef307b1f5b9e85538a2af1a7faad0322ceb238d65e4e1482a704f77167bad76e33a856203a85c62f12b2d062118a0848640ab27567b4d8fd835fe2e53cc33b61474876dd9bb89fe88ea97de178390cd10794a28ea0e7ea5504ee8e060174a4359cd2a8c5c74ec9e5ac86e7f90bbb02ccece8b4be36247701914d0b30a973422a9a511183103d0d4fdb571a7a1d4c00a584de5c71dca4be41d277abb69bbc8ee39c78bb2ed8da11e613bf3f17335e073341933caf371a21a37e80cae764fd35195da6508af654f33f6dc10ee7984e0ee12226d0798b87c8b0019e34511a1b9ada1977c484b6535f4aa51f8f45f1d60d8c08a2dc2b0c80780bff50b744e80af4408901f658dd3dd2b918a4619505490705ce8afd977b8ab5c44c0231a1c732373f5c1eefa2488dd3136705e3148e900f8ef90bcbb8e0830c5a5b037a6de6b1e452b097bbce7c63a053494c01d9e0de0be4d313d1b6aabc17fa6664ca4be83ae3df687a10b6cdb22ea8a01ea025730800577ff3388b2ef4eadc1c8654bf17502e9b929ecd49e9c87339d7ae846b9c3909907b9c9a87921ced7eafef629a0f9b8d210d05c80529d1fcbb3179eec4651d0d4e2b93fa050ae40fb5e75ad97c2f7a5c24f2402cfaffb8b6a9686e127c85e10372a6f31b83cbb02d8514f357a1aa68d1c98acb3a8e6d9911ce313dd3bc082c0e27eaefc3bbc19200c1f8a3622de0f1a34006e85c13d1446670986a32d2eb3a0132e32932e90c0a11a84fbe42a43506a4fc9c38f03794f6e9d853c6e87e76e80e2ebcc3cd1cb1c3aac2aa4a9ed713ffdd831e66c8c02696cabff0d38a1ed73209844cc9b0fb0c99e83a91956800cb677b815b4e535f80d8fa5035233d10ec3f0fd7a46ecbb24350a9c72dbade639f55c308aaed09286a75778a05df5416c4850d5883370fde01e9a39f169e1bae97043866b2eb44ff62ffc6af818f3fe2516c03 false +check_ring_signature a447d545f483367ea9d264caf34644ab55af400adec274f31cd4bacdb52281c5 62e27ab68cac0df83126fffb58a034c9fd6c7d905f422b90d25aad5abc0ae5ad 1 aea172b0a26fe1cd74d4da4e3800f14efd3f8191a1a4dd87bcec126e19ca30aa bd3f955fbff4a2aed24b9e6850623399c64f13f4fa49aa4fc250643df3488a0276d44a45b8a0df54deacb41b9dcba3502f70a17c6fb4048bd4824b511c5e440a true +check_ring_signature 0007733b37310f00940eaff1fd07c26ac2c843dede132c662b5a5dbec31336e1 b13eabb2e3874e07f68a38b37afec3608764fd2826034a9ecaf9fe10054cd3c6 1 e71eee4220f4e2ddd6e8509154de5e623c86f6372977c5657cc703b6e86fa916 049cd333d262267d92d33346384ca63f567da551fc27d66b58492ba15998270879d93b99f1ddfe43a547422e5a13415b1ff36553a4024d3951fb83eea192c805 true +check_ring_signature ec3240d39dc8e25582a0ca66b3678bca12838cddb762b86915e87aac5d8a103c 73d9705605ee2c7fc2a7294a525e093163948ce65f66d88c7ada79ed5e3ad705 15 28c49a201f64d67c050a8af3762dadb309b9a775dee57d048e61af53daa557c4 12940d45f5e85dd397ee5e7946d1ec683af93fe6ffc675f7565955721b3121d5 d12231ba1846b8906b8f1a22cdf0005e21cbb742e821f05c854d5bbaf5d25faa 12ce593d5036268cf40f55af198c9f6d6ed6e11d87ea6756798da52208073d61 aa682ba029bed775255eea7ffd8845eeab2dc0d6e75c9b0e4aaa80b23ca163e4 d953c08f1b258c955d9d760723cd74289ff38f35eaad10e4d247d9ce573b8729 f3c07c653f8f11d5c00e488be9d29514a71d2b499f5613624b8dbfa982175723 3e2f3f381f2027bbe0a1471ea06d30a01b668ac53439238e88ec956fcfdaf736 e5356afdd140404d5a6fd210f50494dad916ccf93a8862a30c80eccb992910eb fa331dd254aa3aac1084b7b5fd3e8d9a3824e17abb00ff0a956a2316f938f861 fcc218d63e19217a39483bf669e60a72d5a976c2efeb6167f8358024cef8d244 2da91bb97ecbb695427b3972e7fab6902337550a882b41cd13daea21b0214dda d3668c65b6b316af8bdf4c89a6339b75531993774711ad593877be097aa1df67 8c29e0d2daf7dc19b7d6bbd83caa9d8523335b436ef645c9d6d0a85e8e1be84c 2bc8bc7c2ad3a838fbdf3a0c13546084010d2b6bcb45f47cf45646cdbb858460 0f2f2db6569be36f945b07b25a63f82104a4ec6f4d69186be0f817ea50a38d003f8717dce07325e386385190cda8ff312030cb30278cecae6cf6d7b0f20a220efdc7e4162dbabfb0266c4bef2a93a82af208202a19da133196ec149d85fdab0df49020671093ef20548ba39a7c83089c5e00ec487814a6fef0fd3f56c82a52064375d2fdd78d0f28e2cd753141d2eb6fe248e2dee9c107f5b281344f815f1b07bbba5d9cf0a909ac4a194b57898e95603ff8733336d6f8c7fbc9c0308abdca0b4db648d1793193ac187dbf6e387bcada2fcfa3142d57c1ae94e3cf604966b805ab04b1d768e2e1125b4696af08818aa81667dbafac1b3168f8c0eb2ee2a6bd03a68e59644d930c848257c9f87f1891f144feb39fa75015c171f3f87c0a00f509ab8880dc6189845b884fcd4fab50445247da55d3dde89e269f0d5e39c48f810df61aa4a41494170dbebeecf66d7a43f460622ce3ac0327cd160aa8e45f0fd20b67457897011398a039a61f59967e5c5e8318d4cffd2a40f390e3fe6216a18606f7e6694c4f44d3e41af5f2837d32d5e025b3ab6e650fc52ad96a3ac24e3cd90afcfdf4e7e883dc7adc4a968edbb3c3683b4f0c84f0080c5b8d12594c2e374b05957c06a3d59acd2425058045dc6e9c3be0b508bdc57d7ab10d2a70e41ca2ef0b67da21ae98c60a6317f3a9df86388bc09c34a2e56cb4347512fc66a603cc180aeac044fa93df21acfeece1aeba6f5cb040a54bd15b5e2a546c4360d9f6b1be0c0a5885e18c1aef2b6becd38f8402bf9b9bac51d6631b7e327b83fee06cd84a087f7ef99c07b47de786d53a48f3e16803f1a6799701bd775dbccff6b056ea49080d61bd4a7d064c921b36759ce2a4a2c25c53beafdfa6c90e3e5f77e7ea6a1e0bae9b54f911978e3ae2b2335fee51f28939f11b0e60fe8d7798feedef814061005c6ef0d2c15e66914a8440e1578b227af9d8c9ef99f75762fd40dcd3f5f7e208c391872b59a01349770314ded9bdfe3d8615a31a86552c73907e8bff76b9cb0b66674b64381cf1c9e36fa7d72a61577de63d1f72dbbad6ae37f5712270baa90ded1f6c060b07d75dff3d6dae3703dc823c49248bf17d05a10b92af0993d4e10723e2c564a5d24a1bf4d079ced4fe31b4a11c3942bc81dfac286b45414a22420350d6a938950b546dc966322b9c0af86a47bc73c507acefa859aa9ec62a4af00801c02480f1ecc77ac8e4ce224abcab34aaa79d63836b1c1d5d774d1694a9fc0d47650019b03f5670a618e588f341b7b6991fc2514a288512965137d010ff2206065631bb0ef5a1d29d0e68a0667f332f025d93c898dab24eee7f39b50905f804 true +check_ring_signature 56c962928e0be7d31e6bfd9e69da10b1552e8c29394912d2261a80a2e85765ff d84ec3e2351a6ae31d5f344abbea5df0b7da00b8b9bf7d48c8328799a7d70821 4 a88f6d019038dffeb222e58c463bb75fca5d36d507625d5cba75a9daa617ac17 393de5e58de62423e38efe3823c9f89b8ef8ecad76346814324e5316cafb635c 04c5fe1abbfac0f085aef003a0478fd3b9ad35e8da89a34d9e46ab26102fa7f1 87a48e9b79a1ae15764e818a99d3b4fb60a773f9a90d40eb2c81cc4017678b0e 7383c9c62a14887e3f37b9148d8bf82411849ca3c9512ada7fa7fd7a19a218025435d8555abecd4701d64e50d70270f684019c93dc075181269ef391f5bf8b072ef97f160e6fc7db136c10e18b1c58c269de74caa747c32487619054902aa108ce69df77f8306972ceff368a9f218c67b96d347145363811caebfc081c04a10cbcbaec6f050c64dd4ebaa94d991dac213ec26699f4ec8e6fa107d0ebb59d8d09fc7b17d405a0ecc76ce66398554071d6a7fbd68aa622ea6cf9ce67df8bdd0b0f317ed9fdb116d156311bbdc685660788a04f2a8b33060a7de5bffec5f36d7d034320798c2b51af64ff196980eb510906d28f16f04bee4098d9d36dc045d8ea0b true +check_ring_signature 036836081cb503b6f4159338dda86f9d05b71b6861b8455cb9f5651d4d90ba8e d5b0f9eb52f4111edfeeed72edb50f6058a7c0f177e34cccd0dd965c762b11d5 13 98a47bcfcc281f8b8bb19b8f9050406ac3602a9d24934524dbad71638a552ce8 feaa9210f6f7ea53022fdade094661389d91c907cbb6f165cc4f9c2a8b9d043a 2cb79493288740eb36e1d4a042c3b6bf083233a1c2fcbd65d5899c4a84ee3a3d 371dd7600985732935ef0ebdaefaca105282447cc2b7825a5ccab1b3bb57a6f1 c2a0d8d57b14f1183243cb57296a38bf7312d926205a475e0ce4363c3521a715 5817b386901b628b8a9b832cec7d455783cd123f473d584a222e0e9f93625d9e 886ff9302ae85e3774030424c535dbd5c4bc88e3ca49ece52a3567f9d03b8785 0c1336f466fadd43a02662616d3f38bb99aa7197418293d1daca1e66b32fddfc 773b78e6287188d9a967df7f7e57763adc116464f2317d033d3b15a1502ca8cf 52a50431b3660ee81b1ab97af5dc1478311c70f3ca92cf01517bb64737a91d71 72f0ba91dd0bd72532f025b7a6a44c648639df473bb407216cd3d43d710baa7a 727c9e5499f95c81b595882cf10747f1fb9ff0749591e09ce4c6b34749692df3 0b99d96043ec9e2b41601ab73b02f859d59e3abf8c30656cf9b0a9090c4333eb 0c06f9bebcf3cca4be335a13abde3c5bb228202e90d3e7f1bc6a48bdf83a8e07e0579cad0020abc664758760267faa7d71ddd56454a196aa4c3f1e0f8895bb0663c82bd25f376ffc1175f365b475c4a04d35255e8eb4f97bfb287dc021bb800094e87bddbf6ec59bbbaa28db0b42eac0c8d8ea1c7724b89c2cda2771d4d19801d04a89f6bef065d650e2d5776b23b8092e8c87992a0f842652fd99c8e5dbbe0ea91e211c6cd1e76da598628e7d231849f56b16384b110d2b91c8fe6b3dee290b482e3445468615df632b8af4bad357c9474c505cc69e94837e33e88fceb3400d87b06763b2f62b8f4edede0df047204ceb335cdb57054fa095080f49de7b570902972b1bdf0304c4ad3a9bbe52e76ab8ec97349fa01cca2008442491dbd94c0b4131e5f04740941dfc3663147e081edc3034794dddd752071add29a774f4c1031c2b6fe2f27f04cd6a0cb3cf312a7378c4af734d4a1c788dae9ec1882263950e895df0de5a465e37f2dd319bc3299de8013b3519579151499e6836464a610306450965782af3fea1ced0fbc6e07cf2bfcea8f83a2c7bba1e019a46ed3a0ce50b83b43894a51d6ca67ea8fd741a9a7b85979ef0e10e8c6d3b921db0e2e0b24e09bddee343e706d404054490676930877ae41e4612b61622d1e013acd41ae7880724528569ab28151863f9f4d419b24dc678dd88fe49fb933abc1c1add4e62a9095253e81787860ea07f3d108b4c31744023c28898c1bb6d3a5451a4a05951260a7628cc3c08d105b04d4066b11428d206e914563eaa94eadb40c5c0decd14960167a62b9b9b7a60e45d787257aada4ac63fadbf0d0e2fe98743662f2881d01d076075e4709045260e5934c99c83dbd5c8061ba8ac508195d9b3512529daedaf0ebdbcc2a0a8308636f9d3afe502f276aa1013a46672bd47da4a8feb707d36fc0c069573e9f7b203e8b4c0240c35fd8d071bc0f05bfebd8f6b2317d74a8377890d03462d01e0d9e0e751d7491714954e91d2bb04a487f09db0bb7ef2927b70a609da185dffabe6d64b094ccf6b0d4ea9d12d7b50a6e8bfa5e813bdad29cdc7bb0574750713571e380e45d0c1362d552e422c1abeeef474edae961bfe9d19241c03a25530788c44c4702a97eaed9d20bc60e1f5d0fbca135ab43fe3b6f058dcc704 false +check_ring_signature 1e6ef7824e3d29acf67e57455aa7734b3f7e8b1b7b84d39f0a36a59a7de5aa93 bea0d9c185c1defcf0c0a238802b200f8f69d57f4681f56b4882be1cdea12255 63 0bfde3ee8d3385e34d26d1637248c4806c98fc3151bdf7dd515c3354b5d3c113 cbcbf98eb9e6738a5d533a4e818a30c02585b97a0f3efd1505268958a63c73c2 b6442ca6ae1fad8eb34b4ffc663e17420fcc6fad718db32717497e1059c3a220 5dd6838dd4a427ae5ad8f1d477dfee6739cb7d3539b3d1e932c1766c728e0d05 d5d8b399bcf068ea05357756382626802ac0c276b44bf119ecd46aed3839fae5 be29494ad391c0c5d7058e34578475515513fb585d7a78d48834fc0d9017a48a c196074629c1163a17cb51e8a957fdd5029b97ca93ed17f344d0367c443b3ee9 0a901f30973433f4a982ff2cae7a82910d44acc582c5dd753cdeea8a44d89f58 742acb6725b28eed3e23ea74998e0a6f6ed06c811ef4a7ce0bd29ca387a17361 0db4b2b3d51a425a7f167b93ddb787154e70233d7b7b9b5e6b51ba9a6d2719ba 31fbffc81c2ac8542b26c751dd96be5d71042eb3a9b9baa11b045655b31ea37e 734ebe424c00191407beca352907d96ccd1dc909659258557b09c1d78ecca50b 450f87baee727ee24096a74bf02987893c929399e75fe400b91afaa441c77699 286da5a7f425414aeb9c44173b6b728f0756231cf581db14904258a119ec636b 5a355680b5ba83dd3005cd2c307f1fe327e8e1354246c2f3e5e2fb77daf9768d 78b364f6434cf004650f162681a6cd5d7f4b423195d28daf3df6e9fc496fa281 dedd1d742c653835c5b28a010cae0acef80485a6933bd5fede838d306bfa1572 ae45f09db94e8cff59ad3b6b1d1ef16e79f0a663656e0105da75b677cc3c0a56 e09b98969326707e9273c6084cd0322b1805895ee90e512ceb749c836cd93ebe 4503a3643a18f00fe55d386a7f38d90feba195216b30d30b0cea5328e3e8324d 7125699bc563a62dcf01c7259b4758eeb53bdd7a74a07c4067b1386519a07892 268dfdf14555a3d698b90bd21c9c05456e856e02192484713ad57d72b1b234dd cbba605473c5da0a1b654132bc9559681299b9fab0ef9ce851215100f684d0a1 61691be3db3d1f4d94cd3a769924a81f48182c220e92aee5c9baac9c22b653aa 4a1c67e24f4be20799ddd6870fe63306be36e598736006a9b1b5bc5cf19efd77 1e7de33fb79034983b6a4273ef87a447475799b1bfe3238605090591f876e277 cf0aa0eda4b1aa1dd97020d759537cb8463bec6a381c519a4d0a1b3b40fba18d 59e6902793ecfdcb46b6f7aafea22cfa782a55f2da2f1d395097d8e0b7ff4bcc 156e90a4fcb8e4f0c950d38a3059851a5d0453f92becbe7b1f568b450daa9596 3f896dc0226860326be31710cf8f81804ea8502d03b2e6e42df525aecc959602 7de806c6df345d176d6184e9b392d48b0831fe3b03c2acce5d2bb5a12f51e6cf acd803dbc1c752c4d13adc8d5779ce003d34f00c63dda7bbb974649f8a9fc8fe 79d8a252a3feb15a4b8853eefafb8fcc7b53060951321e8c8a9572d9073eecb6 ff032ec272c123f75bd2a98a5a61dbb52685b6c903fd5481b1940ee1178eef5b 61f46c9b0d58448346a8b6bd87fcd61ce2ae0428c9f14203de923632a390b0fc b9b36ecb06e13b6860683f97390ac8d3879e71a954b487dcbe2a4b0bcd84ce62 2f9e7ba05b6fb606e08a4b073d48ce37678b935139d79cb17e22ee49bc786a99 93551328f87c1537672e3ede7810599e8ae02abdcf7aa250af0664579d3009af 62c66d614434efd7e9c670eacb3672c3d1804b5a2599816a5b301f6c7c75da8e 693100de549792e5f2c9fcbd107037da383c08bc7c0a8eb759263131a9e9055c 89f39dbf6082dffaccaad3c3bad4c000a5f7ace4520ba66273c161014d283e4a 0148081dc06e3dd30319f5f482202b678209d6369f1902dbc33126d3a10e0455 f979821dbbe0d305ff5e14ca3f77a02940c3fce802f58c4bda08960ba074824b 2bbeef53e18f8b2f0cbd77241b919e548022df41d68c354c17219465a8c953f9 c3cecfe2134a690d98254629b010e996933d01b1596ab66aba9f6ae52e8e6a20 053ec2b7a3a9461ace69858788430f12ab0fddfbaf0c4f9fbc232f5a145b7ed8 9fa28c4d4d874807bceac09c12559468b1f0ba88ab19fd63166f9b2d03d9ed14 41927da44de09c428d860638e1702437c2bd0ac6a6ab0c5213ec69ff44115a5e be13da0adf94debd1e49213b09c1af4fc3c3a69995b69e169d3d1dbd58748f2e e2bac298251f298b818e8d2e80eb3ae1ace291eb28f75de065bc9cb8699d21d9 8206d51d6bb8662253d4d334562ea726ead91d44bb91db642e3473b90616381b a2569340a5f697b45cdb55e329f397a4151817036a894d88d1f201f1d90e97a0 6f4bc47528683f8c3ea4f56064a6ac0b1eb1f4492ffa686373b90bbc9e1807f6 ac8ec909978b8ea7de21c81c92de4fca657c4e3679aae81dd53a779e3af347c6 36e26519978dc87034955da51e347a9835adfcc8d84e31d59244acdf0736aab1 43502634d0d528734a732950974f858321c252877411311b364ec14148892d7f 6942e1331fb637a21acab6ea4e9fab4e2d294c5ea6d805936e93a43dc514f868 72bc9805a59028930fa0fde05bfe525483beae0d652d9a9211ba51ecbc052f87 7f301d4d49c64a5c90f04f2db7beb353c46ed41d25df07c4a7e23530c9460735 3271a64d6c5c8bc2e7bf3b9da307c9120c19405122dfb7602bda303d114b1791 e7b72006a0a7f813fe1bf1b12c53c7f49d5a85b3e97237ffd040e51ffb539326 e9faeee34ed0877ae4906acf91dcfd2c046dbac32e4e2f4bd23013ce8ecdad36 733131df4138353b621bce9d6b14b56d35513cba40ab6a9c7957f87a61854ab7 faf57158216d333cfefbbc08db098c7847fd3ecb604a3bc72ee84e71336b1f0f9c879ac1ca12846d6ef8a3d4f30685e517f0ae379ccd3b3331a3a9edbfbf3f01bc9d61c74ed9cdc88ce343f37f66d9e5ee7bf4bcfd223c85c1e13b5e5447740b586a3896aeaa5acbf9a0fa4bbe667434f3293a3a9a2091b16f0f69428b883f02f34a4fe7794fa052a812e54ddf54272743be7cb8534a64dc44588e528c6f690b832d8c509e69140f76ae1161cb9ecd8b40a67ee37b69404dcb259ffbd65537002cc9ea6f1dfd25359e5dd20e0d65d97abc81e2244aea38a45159ff2f3a28790ac0daed9f854869dd8ffcb81b30024134955a3eb1f985acd0bbfad24644b3f10ccaac3862c94a4c6734916c3899c0329cf1605931fc5353367b6b2f2cf0471d03a9d1b8b83b3f11e59bd3689b3e8b4d322801e2fcc6fa92da9227ff39adc55a09770c821ed4b5263aea5d159111eb9cd1c5ec127572cd7fe93c5b229c31ec3304736074392a91972734bce680cc82d897ae68d2417e5e46792833c529193fd20a4656373147157cd3f8131314a50a232cb5adcbbf61b1ca0921f7c6e35e2d550412ea3d10d7307c03f41b84a399a8896c054dbb426e794e9c008a01f597f5c7040f08e7719f2370ce419a4ab5ae845642d14c4e9997bf3866dc0f70b5a57ab10fcc77a3a0ed0d955844934d8b1eaa280d46b32ee5fbc41226366380615ba1360b285331b8e9f96055897ef6376e291b838795c606bc89795cb96122b9f083c1061d2850abbf02d6ca6539921eba47e410cf866da67ee92b91cd82153b2f76720247b185316156e2043061671323f008f18e8a1777c49c3534c222f9120677bb0a5572fe0ec0a080b5e7b21e5f0aa111270f74213485ec57142feb54cbf7ec8205d4930388426bf51a7a3817d19b3cd0016455e6684ab13fba237d540e2392bc093a21e59ee23a6f0d8d436d55c2e45006f17afa92f07d10002536882d2a9cef0ab196daa6e803b5de7a3c38a9299e6ead3167dab1905631c1a557b0b4925403096ab71ec23bf340fac41968f1095afcf9387af3e1ccfadac37c9a8cc93749f102bc57ff5e50166c9739aa2f0aca94d604ecdd517fdb25f76a6159ef3cdca87d024d12889c32a419580ffff4311b542aeb7ad993fc34a199c75efdac0eeb60030ef1ed8d1f4f2edbfc6c0ab868eb59b84ca010d4ba55cbc24ab1e1d676101dba01b844c9fdd6750b1e4d4ef1c9a97f6b017791c0f60d1c7ba34aeb1148fcad790d2463382fdc0f384c9e669b0ae42201c49ca88241beafc4d6eb61e1b0971fec0e2e1a6b88ff818d17cb879777f36a343634a632927437cba1bb6b4e2a9d265808ac4d0b49ac7840c3b46c44d557c78fc6932756c74b5ac3f38cccffc1cd42160520ae9edf8fcf6543c9d6a65c101d950a00cac1def396500fff9e38eba626fb0c16f49d776bcb94bc03d57aa3e0cec05c3321a404634d93ef52b3867374bba30ba8603fb2ef76b43d0a747e0747c7a7d779f3635c93a89aac34e12798eb60d30c87ddff92d24295ccd04867153d04b823fd4ebe41a6511ddbd942f15a699dc400548d6a34e72af1ca3a21d29bf9282c0dcc03e8aef75980ecd4be26e1442599075ff90f51da66a63572995f24940157df0629712c0c62eb1e477261534c15d9052757c31f826456ad6240afb4b9d30ce1b70001b49e7ad1ccb1be1dd7cfa26b0825f533578ddf785637f21492a79267f8ed227013482228bef2b4f25a8f111b05377bb39ac97ca4e8e1f9e32c3e77f417969e7e5967b5b35de17c491ecedbac059b3a41785b4eae47914b3e304c05fade825c314e104f591735a3e9e870480108f5f1995e4fe0992602c53abba5303da4dc2d33082662326304bf6f9f5ffa0f073fdee16dfbc2ec6df14fe962cda9e6c42465d529efdb6a8bbce3d8991bedda01a04c0e82dd402eff02bee7e7152c0d2f137be5979cb9b9f33bf3c4d330b6b50c941b9db8278d23ae34af4201cd5ae2d5f4e87d3e6d6c6101fbec58264c61400cf6e5f4b22eeed20a5d401fda757f8004f5146eab5b4fe63f46722fba009e9104b1879e65e2d9f4aa1fbc2b3cc04a7f10fbdfe315753a4609d31436b775c7390bc6b93481f190d5ea52fd12cff1d4ceda545d579d2b96ca434dd14a9996cc6a0dd0bdf8a4765864fcaf4b14ec34b3fc4e12ac723536a347f3b3c8f615f026b100f14ebeafc3e523042d8eec0070450ddafa7313244f172321da279b094b34fc066ae321a08a04d07ddc4986cce93b5f56a5f8228e740fcd76c0c061ef9ca14d059a748445f5c0940c4b1348949e6a92da78b70ed171f11004093afa427652680aaba8022b79fc3c5f6092a34d09d92b4d4a07494c530e99aaf1d9a2d948ed030f612e47678334c366f91cff6854b7cebda812cc70bef0b32f8474f4e35739ed03bf88066a952ca66e691ecace7162c53886826a0ac92bea6abcb796aff7be980443bf184ba4ad1b95d3d112fe154fdc5151fb4982bb12dbf8e3d42ace3ccfd80dee1e3c37f29959414d51d9d57d01feae0a5fd51e6c312789776003504d3be50e9d38a28e3481c7db545189bb294bfd1480ae2fa0e2f1a78b3e078ea3edda0a05cef0e7e7b6acc037118f517dbfda2a54273551c7e87ddc24507c16fa06cdad0e76182fabe2f5eef601c08763df64d798d41df237c9f4bc7c0e4dd67ca8b8280d3f148392466b383fa0a828c30f38b8b4be5dd6f6aafa63f1b401f3f3a3d53a0034ded355044f5c98e4fc2b2fa9d3e620c0528e6c4995e6505c9d56b8c68bf20e8707ba9c724d98c28baf2f4aeb748fbd5f0b79d000213d643ce6aac45efbeb0b92387c158905a2aa20b9179c24439ffe5584a112ecf210b4aaa998a82076b1043457a7622e2e78d7ccc05461021c502a551e731788eacb742363492cb4be990f9886db109ae9eb9cba292712a227f492ad912e291dd5e2513f4130200544290d264c62745eed3f785c0b348be75ef128e90e7aeafa1e4a93b97774440febe600c7b266093f291127925e4f65ff85badd1ca8a513207c259d225b62af3402c2096c1673e7f93cbab6dd52a1e72c134d60bea48ad99127a20db5027d47aca41a01cd1dc04d9dd7a4a9e6a254ff6084c2f4637c3473fecc41f1f391b50b1ff39f0717277f4a450aa0bd0f14791a4875753bfe69810ed072bc3b223004125f14240d9c547122cdaf9d8b1d6169d431ff81d4fa30b93f52c3904b37015b62d091440569d57b8d3bcff46d05f6631714492cee588bd7e345f25d53b4609878b694d7057a547e0d963b95a54734a94c91fa5036bf668abfeb26b8bd8100c38179da4406fe9406041a4b2f56bbf42e7cae00f7702f0eb998881a8e37655c2a9184a4060a22645f3fa89360205c6046a201da4ecd70c8edefb7a13ace1de769090e52d40b59275a5146dd3cb1b24c6bdd53bb0ec8f1fa70c513880ce8bbad37e7dde6fc03fb829b062cac1abf62c5388a6dbf804db31e728497b9d4d7d49947147cb621049f5df07f87626fdaabbfe72c703a0ff01679e8ed2df3323391f181053ea171058e09640b950f5fcb1dfee6c9cc3d33f166bd9a67b280275b3d2083534195d30f3fc2b5b49d98f5c8c27211a7955a5ab42ee50614c9a0774f004f14596514d10a8ad9eba7d41f028fc58f855873dd26d45c57c5b70893a61a11fd1763c0b3ba0bb7f570260f17fe21c3befc4c72ec701d21f3b60c5d2a1585ea9efab50d20f40ce24899ac686e13b8dcf3332b1101bb17666bbd634ccf5a29a7717e199213320fe30cd62ee6e202e4c0581c412576f1b952a6e1a8b708e17a54bc22dc472d740871a5bc7af140c899449473594674b7276ca07f84ef1fcf7ffe79669eefb20808366cd18ee5817a216442bbd98b686b4152315635e05f70b0e01821815a8ced0190ec3aa36685a6c0d408d2c53f35d14527abf44e8dd66c31427b3296638d1808f95e36a99a9f3d486ad0762d07359d633d9368b7593ff0fd202ac017d3882009d30ea7e272c094a57a803a993f2cd5159e5c15c1722d700c768b7ffc5aa72b0dc7412b64e68a88c75872be3ec00c37a8bb7cf3d358da2ffb4725d184fc7e850f1c69d05455e1f980a2445c4daed0067f0eb973503213cbead3a1e8e2363d250d26117cc9020e01d97696d84b252f9e746766c082b4ed86b75c6a91a1466f2e06364496aed940dffd4fa12e848bc44ad44157e09206e45f65c717d134ea4a7d03d688b48311b0b616665ecb38f351bc25679b46db283acd6b46c7ec0e90861c09500ac3ac34316f65ca1a1099b72ed33cf43cc4160ba1a1d88b1846256f105406e588394276aa2515d3da459daf5286d8e51041123bef4c964bf861d15d416e0ae1b332c8f772463ca4e8e05ff24d146209869e851f539a4ea68cea29efb6bf0480e66b14731bfecf6f2370bd755e9df52fe6022233b6a896350df060faea010f707047d7916e942e690b3140b38cef90cdbdda8d0104982df96e13bfb540b00372c3e950a25b2b42a51b83f502bde1bf27019632e8d73d6b7cc6d31ae04daa0e136d5b20dd7ee6f5581c2e9489bf829961f6ca644753d6b4f4be6633502649080c3f149627f34337b20a2dc7d5acab6733d4a975ecd337a87676e4aca8377b003345abf87951621a8f19f2bb26a660cb55492d01848cf1b3e4db4e01087e830d4ac91f18371ad3188fe825298e0f847bd4944fc39a341cdf49a0fdfb98446b026c48a4b3db96dad198fdfcde0994805de854d6322b641a61176938f2c6f7fd0a6f1ebe625f92d7882559e7a76139a197a36a58f808c9ecc728c1989661d33d08c52fef728747cef20d2ca9fe80ca9911a7b3af43c63b597a894ee7353a345f0560c26d288addff7fd53911c66079fa0391f4fb6022a24d8fe835941fd23bd50fbce81c261d39fd43b99555f09dc3f607466a1273761a68e796a131ff6d433e0744f0f59cdb44e736b25e4b7d8b2ef05d8fc6a32a02b75aec0e2b5da7cadd7b0ff9365d711d5e764bd0580b89d42e9a07f4b5112508b53ed19b7513dd6b46de0de848d58f546072ee51ac3d6246fb07ba3e5ccc3f9ac9256156770cfde5431208a36f6c2491ef4fc328d86dc873d98a1cd60d2c62cc854818921b6f86c70c5f095dadf5593aa71370a06ded3f5cf6bc3bd3941f8009a9ba1dd441e48b80378f01ddac70820c1d3707fcc61e445fcd486756038b623e6debada8a3c9ec3e7d95016c491a9f1ddc2d7140d474f8b7c9a47a86bbe9f57fc46035ec8fe9ed696c390e35df89b589e226b2351ffd3b6e3689627d8a1ed4d53f4eb20fffa6a3b1aa340427709adcdeac0138626e63d741fd9ed6d8fe42bbb4114441801bbc44dc537500f2966e0c2185dad5a294702dc89f3689a509a54a0d2bf8e8a8df28119a82320cd64720a3f0a84a8cb40ab88470a8059fa8dd219f3c14be48a69fa3e0a8f616045ca3275b556ae4dbe9281f2ce246be800aaf7a3ed6b1c8565900fa5917f8170cace577e0dbc07328e05908a3b1db0c6a35cede891f5a15293ea15b3a9d7aaf0418c378a78e816c54ad4e225ba2b3740547a78f4d01a01fd5bb78dec18f513e082977d59852a30f0465df06a11f27107836399c5ae3ebf18b32b773577ee49c037557c7c661baac83c9b61ea5c844541b1c88bf44b7c5ea62b02640494ad6440b true +check_ring_signature 8e4e9e554a0b1ca2485c02ad92245116426f43f8bfd8f00d486a58a1d2225e2f d520ae235593c443e17621be984d6e0277e47040f209461e8931ef66741ba521 1 a5362871747bda264670e5b2c0593e31c3b8886b701157da562d5da83d11be2a 1b1cd9827574098ca50bbf82ddb5b27a2182d6f3f66671c48382a18eaa42420666606c80f071907ace1ba3642f5b1fcc0f5b59c2036539605a3598d6d2210404 false +check_ring_signature 9fdd111013b851347611a07ac1fb16770141fcdec50466c4443810e11e0e6325 2934cd2e3ad18a64ea30a5fbcc8ee7424749655d0404961308bed5456f12318b 2 bbfe48470b1c1c46c23b2ab9da649d7f10dadcc66743bf7cb0c0748b298b8492 5010f48a9dacfe90cc0ac1080ed8913298d2f7b1c4098bbfa5888b89f84c05db e9521a3030fdcc50a6eb415aa6b96aa9dac15878cb88618f62b8a7f1f75b9e074728618a426e4d0aa19f8d99962fb8ebfbb5f70bbc62ca24796aad985fa0bd04e29806b79d0bc5ee382e1fecd71a9f014b680ab4ee51476f55cfb97b6cc56d05d14602112d3bc72f4bd02da7cbc71d1301cd5af094605924aaf1a5ee2010b309 false +check_ring_signature d734fd3bd62cac761ee41ee48ac964199bbe95ed8fc1ab716f5a12013669b0ff fdc212ea4734e5c8537b8c30c95a32f92f7acc6081841238ef31c24dedeb94f8 13 b35123089f910ba7f3ccd5fdcc53772cbe089c3d5541cbd9b0e4ee317eccac93 b6b58c3608d4ecfca6bd581e43e7ecc14b6321dfffbaf961124c3bb0ee0957b8 f0cf203979bbe71fb91aceb0cd9d52065da037d353cf28fdc2e2537cf8a0cf05 50374180961982b7a8a51b931586d04ee51989b8d2d00c2ec3d04512a6e2b936 e607dcc00d6033ae276c8ae0cc45af6a5e0de9d9dd6f42c888bf079dffb5fc33 f81e504d6c76fadcdc4c36a2953227ead6ade045ae18478f70dde7b965629d53 ee3100bcef7c4ae303ec2f39f34e1910b6bf8fb14f0ecd4b3c7c503849409def cf7188baae40a74613c45b55619b9206f68bada30fd936fc1be2319228884c4d de6b876e54fe7b86fd334e3bddd8d5aad596b1edbe03fb1ab1b75062416c28ab 428f953b4cbe0f5c0a0d204ee989ba9fb6d5e068c11bd9a0e2ea4cd6f572db9a 396ee8ecdb6e749c8f15eded4e59bf7537b907a15d414b34f746dc154be461ac a1903ab04329206474d11828cbce72bac6c606d9c2f960c42eb4558dc7b584b5 7962cd8f2ff6da464339b29742fab023c669514272df45e7e96839a2646439ea a785f6fcc42837815f55017940b2e4ac4b276cdf59c4a8acb0f22a13c7538109555e426e2e174ef183be05713887d6b20d78ebc7521c8e7672c6cd22ed4ea20d696cbdb8a47ab7441473371250b3bf7685fe861e62737a7e62a8d6f9d22bef025dcebad515aa247c21be1f90863640fe939ba3998eb0fcdcbf0445b8ada4310b0d0cf6acdc18883e7f0f945920c71dd454e5158c7f77ecede877f7bae8046209ab8b571178872f2541f815f10fd41e45574824156a618622423f47ebc15e920e9af6dfe6dad162d1cc61c32b6d1b3a1c11d6bfa9b8bb3db4870107c24f8c9f06b416dadbd31cb2adb6402087d1e33775629c225a0042f6fa3768e86e9038fb05057096c63107e86e7b64457a379fe495e9f73a82fea3cb5aeb290651493c2e0bb913e9f84f67771b5bc634ac33542c12ab590997a322211fa49691897dcb7a0b0db1ffc773dc7c3212b00bc938599c428d24077e0c9c1a5fda9049203faa2600cdfad31b47717eb22e5ef32a3b506d5fa991302ecd01321af4682eb8eaeed80d01e860949b2be2ca8c44cd6d887c398f8510e41899ad2c413d17b916f5e98f04a8667da6bca38b59e5b8efc24e499dc09cb44049aed96eb12972f7e3a40e2d0a53e0725e146be95428ba7c028fefb929b5b4c844354f20ddb0481da3c17fbc0f6a22521e4814c3b5bb49d8ab633c5ca51815e6607fc3322232549c5fef9690002700816f47023663a7531c7900506cdf14cddc0a40d426a929dee0bb2cd523097fd0b7bdc8c2fc476770cb18c99a5d465fcb394885efb6aa81eafacf6216b10c58dcca6fe71e5374b8795ce3d7472e228b7616c4c893110d252cbcf0e3be5c066fc9a320771d2f26d5dfb63917434fc029795efe59c9752b05af67ba80300d0e21cbe794febbe875b023bafaaea6c2d50498b09286888c29e2bbecfa4536600052875e6f880d292e4d5ad6eca376fa121f6ddcc6dbc8cd1f9dc613630cd4300e047657f6f2eca2b5a8b5c3635cd129ca1d6218cc9ff394e6ebace4f4c4a45502918017ea9ca3892918204d6c562a9abfd2429bb4af51fcf3fac46339af2ef70bade8912f742920d1a9329108f75f92c8220158bf6e6c9fd66c298a92ecd136044a1ef6e50f3deec8aefac6ed1f3fe228ac5153070edbead9f3cd9dc55235b40e false +check_ring_signature a36e4278e357434badc83e4696b988806a4573a4765ba10bd3caa4cd44e21349 6b7d3559ad9bbc3cf9d1b3e1270e427fb5af2eb3b9c4b85ff65d7edf4dccb806 1 c1b30659b67cd13d4627111b88ca812eaa0472fac5ba71b85df96dd03c697301 ca79bd47080de1ae913c69b1e0e336bcabd5a79440435e902553221fbe11b902b7cc1704456e4d790327fe4f263ef43d9066339b683310646a08330916a4650f true +check_ring_signature 9b9ffb44ad24628f4b7a40728f47bff66f7c6e30e70981ea6fb610d8cb90ebf5 3286233242a3f56e6f72e904d5155bac6cf7de0019c858751982a74c37e5396d 12 20ecef64b374fc4cadbdaf644ec1d9a2a2eb0ea01746e2682c74a9162ef53fbc 2772b902af988c64785e181622ae8fd35cd75b32ce98dd6299d20a48823c5d4d 60f7f332191022d29d3d249ad725961fe28fd5b234ed1120fd52a0c32f1cc74f 07997edb678ae84ede7928c0991d42a3daebc1fbec6b38ceb8d1be07f6baa2cf f7b6ce2a26839b27bcce1b6d129fef2674760af902c9b0d24ba3fd04e2ea16c5 71fd9e211c3e49d64cef424154d6f74d10230f4a46a4d7a1eebdea55b6d8e946 bfb9679fbda699f3a58bca3caf820af009b273f290b2543ec125c9babff45ebf c59f65789103e254b8fc6c8efed92fcc3bf7bdd7a03506862cb9d20a31c93a67 60af685b59284b960b10b8675820a36ec822d26ccbc9c3e5cf9cc36482fddbe8 505d81b7c72039771ccaf0c4e348717f7102c7f23754c8eea8c660a6613de664 2f12ef2f4ee42a8d703b2b3b1cbe5daad67334da250233cd281593f192a09b2e 4825a882615ded1216a1821cd8928f6c4bdb48a7d7be80496dd4cfabeec563ca 52cf10404c47a9c1afb9bc0e8d2977f94878f7b0b9d9465553d78b2b59d4d0027b58b8728380de078d03c6781b2fcf6a2571ae3c67196370b7b55cbee6205409f05055f54367a06f367a7acae66db4a88a378874eea6b43bc23d6aa73f26250e82a774a94ad538b1fc72317d7c76771e783871c8df35be386a6592a2732a0700768999785895fc401d9d399d41fd26f38a9043026929f8c60ad80e55bbcab10ed7b510d3468bfd06f5de414128173fb2599f9a1b28b0081ad50d1401727e9c03552e4868e4fde08cbac95670643b7705183bc411594b4a08b15ae0caf68bf80f6abe6cf85378aa13d24289d6369d8d2f7061855925b80d7772313722a45227004024dfe4811ae3b11c29be74a5d42e60f0faae6d8f88843cb837e17501928f062ef3a3c9f467067e24ad5c3c31f8c900ad347e2dd8cea81c13997856b6542c0d03eef1cdb5dc2bb8ed2c75f4f2257ea1ff6ad21308d25eafcc1ba72f7fb8d4085cfcbce7cd1543799181aee76d1c9c3b32c84ff9f83aea1dc6e0beefe52fca0a9ef079f4e7bfe7f3ffc0fa978ddf76ca189d93914692338d5f4863db367a7004ca1e8208db896dab4823317e7da2695312d11204e57b3b9eb2f1cbaa22b9e909c23e7d109b9fe814f088ae2260cff48b7bbb1aeb3dfa1dc4dd5464512162e90729148dfb99a5d2551c82d5b49a23d960f5f050beccb76bb0c905733de7b7da0b6d8066fc59d3949a3079b04c9639c40ff4ce254c2c99c6f69f36cd5e53404b0f0545bb7f048db56dd7ae1ddbf110961c675baa890cf2880b7124b59e0b4cd805aafdf8262b1476ea78f955a3efda01d0ee18234fb4e563ce1c5aa0dfd0b2470aaedb637293de761e9e12cec170ebd902e3edb1a2ce567ab9d97882c6b75b520f5d809000753e7bae66db7643b7056c37c2e119a4dddefbf7506596f1707986031be0525feda1081026a1ff6b487ce3eecc2be8895394531772c9700dfeef03022492548429701a785bae34ba50d089553480d86c5f884696152187c5e4c3a0015a1dc108f05cf5be354bfd8f98217333d28401568b0a9165bd0048dfd4354b0a false +check_ring_signature 83ec814a8e8bb48a23fe81736b62efb148ea5301daad0bd1e9560448d0d4858b b6d105c1bea94ed3ee58d7df3c16ea82cc5fa9118994830425e5dfec793b96e1 90 5ddcbbb0ae2ca7f8801d40c64bed06e4ecf595dc6a94086f76c8be9896e2859b 69635ffe33a81688bc06d963e36121409f79788e27e129681c956b9543fa88a6 ce704cdb2dffd0f6e3e7b8ec111b131e02d47928d8490307ad8bf1528092972b 06ef9ad12abba7a43397a11c14997bfd9e5df23b7f3d5b07e6829d80a0c303da 0f83fcf0d51bb8a2822fead39fe92d41fa3e63c2a31579b454ae439a14125313 10667bc33c33c22f17ec4887834f3544d4c580de74a88035d9d055239c65aa29 44b64db75f0b98ec9810176f77750bea85360782db7670ff2903c80c21b9a83b b54b0a3b6086267382f8bc9a66d41c524134b49077c221d0da9da1a5eca7e00f 601e9773d45564f6c01e5478e9aca8be57ca8f926b116189b9f91eccbe9469c0 59d4904cd23fa22767d097a4c8dfa9e3abd0a01532c395defe1588a7a70c4b31 47683aec2256b03775d66692515774775f1b2dc16bf71ce2b4607a1c146a9144 b46a930001882c833639dce270bb329593c88328c7771aa780efce2e3af6a158 fe3e03a3ad14aaddd88c3498473314ce26fe1683eb574954196447f056a01a99 36410f06338b48b2d4e3f6857bf2ecb5b4ee2dfd373573ad35d8e3b245123d94 593253283a48dde61423e2a5d9d497b1e8bcb107d0c71cdfd1598e6a20292b24 5e2ef6efb7773d551c3bc968dbaef20e0b96a413cee66b7b87abf117f456a357 8af18576268cb8322e96ef1805052e828398ea00f2abe60a71c7b806608a6e62 38d05a255d3ea231f777e5cc040c0bb8f8830aed25aa135ea6a4a5b70ea0492b 4a181371753885d4526b236783533c3212f7fa215cf452adf410d74e4302b397 41b96c5269c22058643a0261d103fcbf3051bf4b5119d21676e6c7b182fc2166 1a1f33fcdbdd59f5cdaaf4910ed8336feb30e5063cdaf3b48629439672b2eec7 9ba7c270814ac5a47a1db4bdc4b9b97dca6082b4aa2b05354bee097423ced3d5 8506d622f131194b277e574c5bd98deead9b26414c9b4d9010ed0295564a4df9 220265a1bab2620a9082be9b3058fde3a8c58e0683ace01759c9cbd1be88d98b cb97c273d9396110afc0a68a9c498c3fcbb2504f49c1133e4c819f0b0c02aa96 ac957f43d155fa2d2b4b548ccdee81c7bae75683969adba84a0ff90defb16e5c f658f5a4bca7ff61df8498a15fff050c9c05325bc1f713ea4378c7ae98a6c5e6 817366200a6ba0efec012d87966448f7ef182e37ef25129046db59ed4164fb5a 4239785d8079bc5834d73f6ecca886488efc0276695b95ca24360ed42036f460 a98a72b400c439c5ecab6f9d6b726bf4152d00b75b05b0171f7614600da3043e bd69dfa67e8ba6825d78a3b41f0f8bb8ea914f8d46ddfc0c4d6c63cccf37c12c 39d28fa87456847b01e3934205a6aa2d8d41b804a3b5f527bb850f0ab5e41770 07be3611ade807edccc8ea2f03d8dd346b2d6b4ee484f5d0d8dfabbfca7d1b84 834d860e5abe2ddaafb4d59782515f5b8f209364cdc0b047e91869161cbc34f2 ca4f797022c73138833853b6d2340bbe8abc2d38da902fac03f79ce17868c8c4 0367993fc7ab7ddb1f458ae2e062e5939e9befa9fa109b8e68b93de9daed9a0c 4a96b67cdb30310dff53223d7ca4a97cd896b70f42ef99f8fecd480acbb2d699 9be9774d7f2d29bee6f8f710a37b83cfd75215609ca06de38e00469f2d772982 d8e189c97f8dcdbd3e71e34b8ecb7555667ed1d8f1d249bdcb5c781b94a3f8aa 9e5b79ad009322e7e81b1c936bab2630ac520c3c41621b28757ce7a49879a4a7 b91b8c966cd66d47d6f9994641edb170b1e5a80136fa58dccded4dbf1e58c612 f783a07a426e912ae552bc8c855d3fdccf340787707d1d5a3b44e2abbe1d6ed9 bdfc20f60eda83cdd6a3bb5c50682180ea609092a94f9cb155adb13dbb395434 ebb0d10523365672ba4a37b33c976c9e2fcaad62ab384d0702cdebcbb02cf9a4 4141faee95cb810f021bb4d9571078d3f31a47b7f7cbd122376dc8a4ca0367a1 b6f11ea64a5ed2fd8594dc83d1bf07fe69d1ad04ed577da548d88f594990fccd be6e83cd4c51ba524e776ae54f3d54e53c7849fe233a271c91233ee8b733262a 90b4e9f535eefde4c624a598671c2a584226e2aa778e9d4030fea9d6a32a2b63 afe7a64607f856ebb01b9d87de8641e0714df78721e7d6042d5f857cfa488ecb b3089f3cdc169b0e4701a901bda4c77ea7d87bc7249bf05d1e55b9a96e6d0b07 365386e2ef1e8be90ed7cfa262ae2701d5de8685dc24a893f9cd4ae54fd4257b 138e5cd0560991ea07810da0502814416772fc6f5501e5252c654f10950a60ce 038001b6894c17a07f5b8f98aca1b1e724dd34bca21b77f9a1c715a9c2f2aeea f8c34108364d933d5ecada927989307ebdb239c504149d21f7f658a434d6baf5 2c06eff399fd4658cce8b1f310c11cfe0c9eb8d085996d9995285a0d2d898c69 3dd551d81c6d53ec3052bd3f4aa12688f014fce276ef0b5e60f9f5774f19531c c6a222fa99d40e1bfa8c21f2ef8c88fbe2742fe01c90e21a1a5ad0d2a4650658 94d3abbbadbcb00d9092ff9f26920c9a1573ae0cb67c6ba7866f21e5dc7804c1 47308b02ad49fd85e47dea8d594dbe7aa5be73e227befb364ab1acea09f61786 453b1b0e3b731a5de5776d2c4b83f71df4446ca74940aecc1a827a13f61fcb5c 0bbd7481a3adb4023aa4c3f87ab75685d45522b01abee67d790132f4858039b0 8c42be61488bb2dcc860fa06917a6246173ce89b5f2a51349a4b79ec8277512d e7790df5693113f0a46010133ecaf68c0eb380f91743a43b442c68d446a9c523 d4471e9b3dfa1f89a19669a980cde03720b6c51c99e1be2bb064dfc82dd37ef8 12d0cb669e5a3bccd361106061127d97c98e0c10a288223777b81b303cc7d293 d40d2296ca5372f51860527b6d3bf6abf4691c32fdc5ef497e90dcaaa89d6d92 0e0514ef3cb4f67a563af536a7cb4637bb0832ac0697e62cee6f30054262217e fc6b97401727c757d9cad77a6c0de1f30e45572a44d503503bb1aef79607636d f0faa47194bd5f5aada74be53362ad502d50132d83d978065015ecc7d1bba0f1 613c80c787f0cd5aedaf1c35cd3fa6266989dd77e8fcf5cc05a99d9b980d3a36 74db04ad11547875830025ca3f4d3190a840c1ab54f101b152439b39a5ca67c2 97bec8efb4b857589c39816af46382eccae7e108d8cbf61c6e59e100e06f208e 02285e32bc13008eab7ed2010fa15e02a2eb263d90db5e0e90c35146bb909d68 85622273d48082b9978b8e112bb70648441a84c649eb69450289c28e292f1836 629462bacee3b3bc11c877cbf8d7ead38149d32c475e0fdd52baa02cd20b4cf6 04c247f0e6909ea6a70183a04ca0ed530be29a139f28a81f7c5aa25c250a8fb4 1e407219770df889d2f1ac4134591cd3d144b200691fdf228a4e0aacc1f64901 7a90234a68af4045e46a72315ea828de2fd6448ae12859aca7a781fc86132db8 39366b5a8d6e1ca1670f3e6b2c99cea9739f3afa09851089627449c71cb4d959 5508ef94c904f0d7a683227921da5e8d57e17a75419e750ebaab4a619fea1a6b 69e4561edcee8cd74b5e6e2b7831958fb1f4b291ddbaa4364385fdacea5dd86e d0db4aa7583859284fcf715deb40116df881153b21d8820da3a7790e5796c5aa 4b25ad7ee0e4d45e5e921a743e2920789898f9c61ba8619e7b45ea7ae86915a4 1299532e100f754e896a2f19b239cc9c1ea75ba7bb996119b1746b9991e9c0d4 2de3155ee4182d055046ca1ca854d9b9bc5af10fbeaee0066f936b6632a9e603 b83a5e4a4a2acad5c2825e70076b0355d7c3225f224b1df2b12763f7f9d7810d da58ed993a2dfda38c0333ef00ea37b5bbb4e1280be2fd740666cf8e3556fc37 da97498d8405c2d492c8f37e59cd9be55937d553236b7ef48ada0bf5c6042cf7 9dc54a3f64deadc166853404bc3b26242d2a064c9fa64d049aa2fa759de59558 8f07e964490e4f384975465c55829932eddba1dcc3386fdd475a9f5189f82c4d 59ae52182d955b98e85972e8f5fbb3e03a14e0191c592e8c26b2e8546c7e9f03303ac70b2e21b3a7ead755aa81c72cdad73453b7bf97eea96ca8cb6d9df3a8049b8eee0d6811cce4e9fba5cb67d29ed5696f3b08fcd89b4eb75d3108a3210a0e595786bbc835a74fadb0817657fe00223a0c4f5e829b7cc505a312fd4d83620f403f66bf9edef57b30c261bee392191cead47425db2de0835d5d2b5164570405c7857075251e895e7c0cee0db232d3e1ba7f6c60e6f211e2ec7e9eee94edac037090536a199471ece86b19a18d2a2793602b435040003d2b9fa90c582d1c4e069549da43b64bcc7bb1bf892677b753291412d22642aad632be3acb14a3a14c0e689ae518a4ac0d873194d1190a0771784dae99993d41a324dff3af8c4d0879078d1864ef03c6b759bd72102e4d91b3f8882dd10cffee61dba299eafde8c6510f84a4a435b5e091fe9ed97f23ed2be5015da86919f638563b13359f959cc07706d58afa10aa90caf63dd994c589b3778c979fea2c6d8960969c868a67d4d6320a3ab489c23fdd24c8d10dbb59f629748872d6e988124059d663c3ff1a195c280eaa43454f3ea843448f92995988ebcb116df7afefcff5c64fe352882209a1050e983a42e81635274b3da049350d3619a9f8ef84c88e301652e3b9bca868c89c0dd98fb8ecd8edc59b3ca245a790a9e084102dbde475d9229b8344e744ff312307036818deeef41fe9995e723210eac33966e6b2b02c797c23c20a9707e5dd2004175fb5febdc876853b782209e69279f863df4d28422232aaf838cbce5c155c0ee040049dda30e412027fba2e6fd9b5976cf15184d391db421971589facbd8208cee257335e64f8900f2efd9229892673fce6fc00229de02379ba4283be547e0fac5e33764527553ed603209908b2b8e3f7b9645515f5d7726a8cdafe2bfbac0154c044288a91a6126b85396e4f7c8886ced35b5ca1835c5d4a9cf1e6283971002b0864de8bd394e26cfc4a8bb8955781346ebb2a973acf2d07e37bb163211a0b3e2be53ac8518ec6f829a06d7e9076a3af800a7d017672e0c204e6b4a3b32e0d8c49d73f5f1ec74b03d618c9ad9d8c7663d67d7ab5b365637a14139fe145e808ae7edaf406df1ae28bfcd24fb2c238886065f0cbdb7fb2b47741f4540ea4d4076e1cb324f473308aca8e8f81006658ea88343032d10704674d21cea1a236810b871209baffb933e7790d66c8f5cce67e510f2b7947645312bdcb3ffe3fb9620a7d0830c59d7da4fe7106f4bcdeabbff1ba567e5fc3cc0df4004b6ff656589a0e6c00f132a4b600969e51d64c6ecbc800c45ecd90728ce24f1c202ee54e3b6807ac6c79bcd2acdac22b2f1acd758e606d13a0d10f68a592b06551cf817216750bd259bcce37ea62540fc85cd6e4738ae94fcfa98e8e17a34e89e45d13c1859406eb4228ce8153aaadbcbf195a6c16f4bf26d9f098732455488de42ed66a6aed00d1b191c611e5bffbe0bbae28b358f2d8a91909748242cd0f71511b901d6f0c01f14314a9646880632163f3317dac02315152f9c11d972fa2fe8c273064bbcd00a60df9501d6ed1183a7b8893f00e64dbb278b5260c1ecc40a57375a5669c9706744c6693700bd5d85d6867b2142b0c8bdba77b20f838ab15757a8de4e20b030b6a094e3fa0a00c7f3db1c9bd909329b2a2b32a673283cadcfc22ee7e4515aa035c4f8099d98ad0ca66bb0c597f6c22b0e7db88ea7aa5d339e1212c30512fcf0d56c78f1b466d55c889b38569333bc8d38f561d4f4f9f1a97deb6773b62323309387b94b943d95058aa62dc3bc86e81a8fb3f69d30c87d759383f802790c400038219d5d15721ca1a5396896cd8a241a62c829328f0a01de67e5d81c8d7bb890066c3c91edd949850592b2fc306eb8a4bc8cd33934b3a608b961fdebe2a0a3f002bc3eb6984a4999af76e2524978773ebad3474d688305529e81bf40f76df6e007e498548d6dfb0e98536571319f0ace72bb05ad7378e8851f540ca43310ed009a9c9c531677bcf45ff11fe92db8e9c9529ed263bbec201ec68b5bea13a7a410048b6efae5681cfb7daca483ed46f996534402936d054660745ce7cfa3ebaa70d2e7c8f2c32df8e0fd4e1049ef16e1c2eacb88a6d565ffff8c7b7e7638c30bb06267a7b11ceedb59ab56be79a4ad3f680a0ed08cff59cf5e729a4d2c2c313e8060422110e07f10907315815cede66a1389c5d0cecc49fa3b0b3bd7f2138769105b2f6bbefe7ff18f0ea9bfe6e465af3f2865f6d169bde222c11ed202b6546f90edbf06c334d5ee1f8fe416d409fbe002bd9407c14a63ef4189554118b074923084bc801cddec8cfd15f1598d11dee8378f117e6e1d70ca5e00701b2d2aa06c8091664a5119031f8652502fe7828d5657d018b6f4951a91d1d01a6ac2f6672720ce9680818fef27e877c9241af0714f825186601e6f40acd58b27313596e49100dd9769b3e019df81c92dfa38420a6068294384b471e90a3585d2e5a7e84e50502af2720a26f3a6a75fed0c7d672c4268dcc16d0beb9a567293b4e9fd675b414024c6716042e04a5488a87d7d4df024c07046b6915b81413f7ba54e2ade10b290e3fb1199232bf48a1ee921ddb4808df88f92f324dc3e3bfafd359bd41b24ff302cb7bd0ba28e1d58173a16d66c1bad32dc6babd7779e94414425a1c74aa2cc50c7485c6a610c2dae4737d0e01cde5eee6f5df23eef1a608df1e5a71d1e3a3e90e4ec8bf6c5fa76f97f121e282858b57800f651830b0800042fe4e4a883643340f4c34977b3738668bf794f9fb56347517ceb2eb1668939eff7dd8fdbda1f3370d8fe0be19b09b894040e21e1b9cdf73157f49d9ad2212663de1b1ec8a9562880a96d68736005a0b6175e31a45ce1497777fcc2fabd6a0010fe05e29a87a631d043f807b8fe6ed1ad016134963aa2314c37bbe97e25c90d3cd06a487d495b317057553dca04bddc39ef752f7cf07ddd0b0db8a270d127f189eefd597dcb5ad430bae3ed56db2c98332e457b7b323758b8278636bb8aead793ab2785a1fd4cd1b0096dc18ccf5691dce63684f876653bc8e2db5c001f4577c7cb6370484ae961d02c974fadc49233f40e0a336f79693340a05d1750df75d5541bbebb1f88ecc0e0d31ac519de540081912250b273e7bff4a76104f7101e2cf3b19394302db7074d198b7430a0f10487008493219678b6210b2584aa38ee4b1d7a88ae8c313323f03e670add03fe81d7b258e83ea59f09a6b97f7c04b2360c334ac4b2638b02533087b81aecf57c0f645d83ff198c98dbd6904f762a4674f7483130511271ce8b30136e484a2b3283bd55dedf616339915ac6976ffc35512f2d0554f5b1b3c19250d4d379217faae6359cc0f542c2d546d831ce01505cd29b1ae8003b6741d67dd0a8ee2121d6e51f4de1a56c6e7d85077a4f5ab8e147e8985778098c6741d81b00a73cb757348705884c4ccf1c1c348556a52c6adff47632e87d5ca490eff6a1a033fcf668909cf0e74a443a59283178fb5a0edc266c6ca7b0f450a08182eb3010d3957683b8fffc8cf910164516799880d06c8558d27e086a303ed8de409186d0475d9080151f408d72b7116e047cdeceffe79024332c176e5f1350bdd0947380e3584159b6a2cd7238efcb951f2d15d7507c1c7b21cebe98f179d104eff84980feb4ec023a7b572e5c59f9b7f0da813f9f1950c1ec0ab8a30242d667f908a0b052ffdf3db0c8222596fccea9a7038849c2c8eeb387e497d7e692b7ca290424d0f222da9c867f19681b82551c032dc172f495f3302809b4fd8911785a93ee54e06e74b2420332aced1ca91f4112e11225adb3d3c604c420cff0dafb9263dcf4a08ffdee411eefd8f0a9a2129863c1481247606df93d393ff44567ba3966160290440230c029d2c9b4349ac5e3a9b5a05f6eda1770f4c53ea1212831153b69f370ded0c13dbee485fde2d332c7c5316b198ae7f0b98c39ae0d3c3a8561a8feba0004e0dbe252970cc7bfa94d9968596278e062e383bd20d69cd979b0a9027d1d901021e4c82db9b05a670a49f0ed99e10a45338c6667fdc65ff7da26f9f87420804a8036864cdc8d1d73b1f4ac6e9df437fd649ad47c6b1ba522c2058b678e22c0c93db39245fbebb56be02744173043165a1b0e3af2259273fd8401e43cafb250e7f334a7efe46cfe0a9268965a3ee8af83227ef1293e8bb967fdcdcb5bdc26904484a3c0922f17ce8ad3320404dd60b7a54d4806d7e878aa4745ab2bd1dface0746e413d7c193683e692367a3b48c0fd6a03f20efd0c83f1d8e37057cbd428a0ed5c0bf0ba05920eeef48acf45f10c8f14ddf61ce8aadf7b35a76fd01eeee8f0db7f6aab6c847824faa04978c331d30d261899658a976d476841fc184fbe04f0f6767c5763db79bc0ce4ddc0b0e229bd711405aa41f67781b583c628851948906c0f6f3cecc54d2576d576fe0d1d711194c8e33ddb3df69148b4cf959fb62bd0e3edc7b29d8e7731edc4861aaeaf6e0f543e05f3797d3410ff99d869dc060bd0339a441866349ad03417d78703a388b3bc94b725612f6676f44abf849c09382060325a8616dab568b5ffa9bd4793a232356c1e2f89e95fc00016757888a8b8f0515ebdceb60bce064fb884837b6fa07b1a7cd7236649957595d53644dd1682904b9a496f2931867e1335b1ac64980e794fae405dfd83bea9bf5d9da8a90e4a8045bd8b0d6f6cf248c412c06eb3780dee145ca5db3ffa750a79cb86f0849ecc40a133ebcaedde07f02ec8b8c4269b1ce9e32c7ccb937be426cc744d5e17630d7056e83cd3e133c62dd98e28a8558e79fe2426e5fd2eaa19a5e658165f836389d0bb01ef808af61fa614c1822cfc9e6876b88aa789f0831c21322ca21f9169ff104fa2cdad7f99a1a5f2440a9b9b2aecd1b69d490116e8a2373460c8e8005299f0aa5368423c7c50f71b7d5db76b89989e54d329475f126118738ebefa917cb320f305f861d7257b0f032e0e9b3d770e96f3ab8bdba5061ee12fdbce91542123f0c06c91cba1fda6250bc74e569aed279449fb259eb6a2697fa10bb5e86bf27b001a29ef0065259aacf65938155d59935b83d7348b702c0b1e66b0f7ecf839b820f29c3a61d975057d7de2417c6bba76567156f2a1f5d4018eabe91df6d71a60b0713e2a9b01470d470a6ad28166703251fdaabe3cd07ef35efc34cd8de10a81b0f22d19f9f56a6b8597709600621c7a5fac5c4dbc372da0ef9d2a2be1b1f66490196f5696f661a081171f4c90d39586393e8f4f220671fa4b6434d02b8c71d460c249f1784d98f4b59fa7fd57d1fcdcb317c02f553d005a5ecd89280cfd50ae204fe2b2f87cad22e9af421c13149e4a3e7a12d289264bc50b27e0d4a3b04cfeb02bb17754b28d08ee382fce2794599fe35e353d826dc57dc5c85d54d2121d7000316d2cf4b99da1b9a47c174f6baaf2900be6d7dff72297870a57b9fe40ffa3705d05cc76a87581e0b49424ab3cede83d63d195185cd6fa50adb8498b766df350dfdaf323f6c1c6f45991f5243aa2d3ee92aa7afba2b6fa82151bf253573c2c70928f0b2743c4de9ba63a1d27de32185f851308ca9bc92859b980072b4d671b208cc8ae5945a133ce2860b455d57db96dc9c61bf187f382f7c57e526d3dddc950c806ded5450d9260a3246122c54a5d11729c079cbec39d01b5b2b8b2e812f5c0689ae7256e27e84998f575406a72e350659c8fb8a0debee7b6901e97d25f6810466ed94ed94d66894ec0a9e46a39f62491116a075f9c831b504726d837b42000b1d391863d2b22274b94cf4efe3c04f115c759e5be1a106eeb82395c60fe14007d7c71c6dd7144dda7109532022c95c9c0e152498a12ccad7a5b7f53e7f09f50548a9e1feb3551f21702d34f5bfdd6f958b0acdf0e58c49c66708712272b2fd0f26eaf78918dfd32647b9611b7f1bb5e6849f4a43a27c14bd1176ebcd02783703e3121bf9d3745a9510a1389ba3c1785d1612382caa2d2ab8d17e49df6648ac04048d506656d9f1332002f5eff6158b07bdd22dab323b5e58b8cb45cabe6f950b8b1b350df54db6933775b005a5fe7e44f267950ca874e753a6856fc69a86b7ff9e73a4515701195aab571f3efeb144b711a21fe61bb12a441bcd4764f16180091c7942d800c304581ebc2b8b4118b8573f51ef246e2142825cd052ba56079b026e1ae40257c54d22ce97fe889a006c9ca7aa30b4d470e206f46652fd6b10c8057c64719b2e3ba81d64e662fe582a20b6302aa152ec4000619a3115728cb0180971d3920794defa79c98045524ae17da715b5d812c987a16edf1c94ddffd0b806b286afc661baa68e0cf87459e7464bf2e063c07e392b0f54723319a18b8f70048ed6f5d55b7ce4ab872d813c7b448fc00277c6aef4b90ab66f2f4d91b75fbf05128b82bdd733ed136cd7f4f2d36e8f37c5e6e64e6f8e545fd3a656e9db491b04d77b6439d5b94c4f41d390b9b56854c64b565db530bea1e8491bc67061cad30034d13d213c735649434c36263a89c7a5a7d92db60c86db8a90c7cf294370e50ad385379c8880326cca4fb6169418c1aea28eba62a68cc0b4c50e8d12f6e765032ae550805909cb83d11ddf91234606d24493958f7df5dc996786e83a11f6d006d453ca2a32fffc29e1484b8306c0b71ed732eed49868f6c6152f9d77e7ddea07f394de38ad0a4a34dbb6ab237bcd0c579be0740970154a74f28197681b37850e7f19e449aeac83d059478db805b2af7f23c5128bae9f3649026cc165db01eb0023b8ca16f5dc1a81f3250ee309929768f3d83517f687f8e2afa0a5d195e912068d6abcd3173287fd7cfc6a727469b1b9946425d6676b0cd7c60246462f912a03cc856772ab08a34a93ea1a453d70a8977305918258937949ad0f40f1fc9e88050d70ee6310454515c357532d8795acb41ab0d59c44815f145b39ccf07ebfdf07778623f1e14103b976abcbadfb220818bcc1f318bf6e071664ff46b9b334ce0a70f4f373d49a55599886e96761de8621d6143a86a8f88365d12d33a282d47d0b7d05348db1138ade69ddd117df7cc6b872fcb3bf429d16919b3c60d9594876013f6a4ebbb2cacbeae876218b3505be22134de8cdc2620155399d22fd90a8060d0d7ee05dc13d4243cdd5dfcbd49a7c28f110374f865bb609bcd0bdac2168e309c0201c274ca0356cad119220d172e35834d85b048ebcc7db1a5a62a60c9c640a96220517d5cab733ef0aa7a60ce848baeae6fa30c056df30d2d589311168340446c76cf28080a6ae84f92cc971e85d4bc821ae2ca8db144bc032f28182986f07223fe7b116b3421a25f2d900bed60f593ec5bfe0dc3b243a1e383514aa14bd0a8cc581e1a698184173e9ecb5a3f23cc06b73f9a78cf6fa23c71bf76ed4d8b302a8daf6a8a6a75e0b120ae5210bc4527078a6d66335bdf62cdb26f8ff3a0532005d7ff9ed2308ddcda95d1eec9ad7f85ab84399382c232f7c04df25f964eae509f4fdae7ac39e26248ee87650539ada24819217677344ee6f3a32f36ecf55760bf61523bc2dbb0553faf32f9ed1316d55c840a81055b9250606e8865bafb55b0d24f913dea4dd01d0ec6745bf092255fc98d8d87c64fbcb399ecb5c194a122104c2485c91fdcd6dc5d7fac92a90e05a6f5150864222b6605455e24d621f64460065e2f54bdf5da7183310634526c3b4b3502cfd364e252201d4b836d6bc0fc50ce09a35790b30932863104b52603058e1124f58fa1dab6f6cdcff2b5d7a35340b841232f23c770b230087879aed44971c0b0262c8c1cd34a126ddcae0f0c3300ffa8c8f9b18eccf733b4a211dfaaa5e2caf24bf2377ae7dc9b792ea98707cd70ab515952d7679b509402242ca70d9fde832cc687eadbb8d9f453850ee3bd9560cb463fd2493c5300be69d8bb39749cff15aaf850ceeb931c39fcff070ed165501316f79c0e7129d380cd7f90a6392886786e1e84d8f41957c337e8437379c2c0bac1fa64c579cf28cb241d88fa5d4a844ae47b3f9e56661e7ce5563703303a10655f94afb9fd5aa2552961f282d101d12b39d2ca0d0439e4e21d8d70b31af8509 false +check_ring_signature 63ea27af4c216264e81f70b132678c14f69a371e09979eb1e3d5a02e4a315c74 2bf1a0c767933ea11c9f5afa72aa17ed324747f28c299a027fc49efc3560cca4 3 3968b224be0e251295e4d709a1a491adea24aa44e510b37da3ede36e8a1809b1 a8f366b1647487ef0ef4b7948b36125fe5dfc803880c341b3f4fac9efd181b24 f05e8ca005bba0ab9a6d8d2fe2c59b0d0ac24b3b272ba5a627fbc4265a4cc2d8 d73a0a81df86dbd70b266b64f606b73a0389065b932144dd8445262d6858e20f831e4eb47c930b4a18ae8ec94f475e83ee2f4097fe1d7e85c5e40eb99d686d06ee7c58acf6993da482086da26bcaad393c28cfde2efea3ff0de7c7dab77ac9046a0acd1b214b3ed99d677591bd3002eb245518290dafcee531accb254ecaca0a41dc911b0ffb0251821fdce9f0d61ba354a8b72885950085b90036dd2d46990f7c5f542260a6dbb6bf0f4c5cdbf76cd4d41b0faf2410c69959e216650779d90e true +check_ring_signature 42029ab564e9e836138dca52446d9379cb0386475e2b9dfb7a71ce2e128b1ec7 d17fbacac67dfc0b533f2e9b02ff4e4865b9e42061ecfe2cb8bfcaa24e18289e 1 010340714885716ffa7036f11fdb84975a09d8681c52757bd6c14119b61e7358 6032f191da4026dd41e16f7381ee0296019af0c291d43335b25f3516b4a03503d00f2af8101d4851b56fe6827ada4dabca6d6cf523c0e9ef80287064c052957d false +check_ring_signature 2331c79ac23404a2d4f6b2b76e74e47d9d8d9147659b397f9e64370234225286 286bba39a80fc9d464c173cd0f7dd5d9b98b9025c9bf3d35c7488a4379013b8a 20 925965ed1bf8fee3388a166a7c0127f6d1b1ca1fc12e1654c771aae1fe54170d 21823836c6ea8d8ecefdc5216411413393318cd59a5680e4206dd8ec0a23d91c d328b704894ecd027ec24bc748729fd2a7cae6777fe511cfab6d3e4f27715e4e 23cad345055901c8075df5a343a260bb03336fa586e0b3b1627bb7dc5903644b 769a197c47b3b0cac217acbc18e6abae211c1c4def8af56192ecbd621a8bbb85 3034332e8beee4609af5230e77a20f7ce8782e982ca9328574998c3ad03231f6 52b763a6fa1c9ca9c5fd26aa311c7c931816f276b62deca39b043138bede4a7d e094d9d6176c87913f82f7806bf0de0118262a419567e291848e43a71a182fd8 fdb5ee73cb9bca280f35954a01216a32554b7a23e9436ef90ff9e34fd9cffc9a cbc8b7f35cce723d8d7e2d53b2f7fb6ed1f3f65d408f965d6177238e36652cf2 c44d58355df8284281a423a146789a4a1f5d431dd75c62310f4510a939ea6b37 08bb0f8a5ef41c6e58d8ee2245c9af3be7f6724d8fa025703000b9a7ad603bfe 04fa90fc2b204e706ab6d9469276a4ceaa9ceec952e42a1c4981aecbf22211d4 d4945350d178001772c1602aae178aeacd41efd0ea7732086735b3eedfd61331 096359b01a2d7554d2734a61c3c9f47c10fe8e1403fcb1b1b14506c0c5d75edc 1413b6c6549e48a7e40d78eb66a1de7056ed698f4149616fe1cecde0b55bd4bf 19b0715dc08ce563bb40d80a7f98022486242ca3501285bc685a3f2d377f1f33 858c2ce27bf9c77c0f933ac081a78e9baed97084140c7a1e387638484d1b917a 71099120bcfcc6edbc0115bedfaab8605cb21d8acfc124e0599203e828efe727 1a3de69710b6f326391bde8786f0f45f24cf7e9f3339167f8257eaf47b289b39 57f5020468d35a23da3343465d9563d79ab77ddd2f510d4689325fbcd9800b0c0a6e083ecd9dad842bfe28e72e54bdb090d9573f407122d779e2dec3900bce09613d02fbed6a0314d3c908481a85cceec5ee8e77f24944440ebd71a36576bf07dcd72bc665f0057a6f4bd8b6961e672b9724c28741173a5ab8872924aa4aef0de929ceb86124a6b581c6d40b1533bb371d91a59017392d1375ca82edd891800bf113c4d6d841d48934ed98c1d8bb68de8132cc62613b8bcf44430c114960290407819871d21c48d31909ee79b7a20f0520380eff54ea4addf98af2065275d902b0f45b8d8da082b04792322997a40a462aeb6adb759dae0486f0e3e0fb9f9f0c4bb26a1c9099966b334118eee0750bf6fafd2befc2e20552572db7aced3b3a057106038dc39ec2a165b56846ff5b0a28367b034754ab0f83959d68930dafe606a5cd193c354390a40adc5ebbbb1ff6173605f6715e20ea011bf4f4db94393a0c6ff243179550da3cab255578b8137a002d8f82a211b31cf236d8f81c7bba9e0e131ffc1d3590d265726d6e68dd66ff2be1dfe250fb966812894622a91e5ebe0dab6d0cee86024e808a24d7c14151119b3386ed4d8d884aa5cfbb607c051c980ef838e9d4625790cb81d002aec13626bee001ee315afe7eda9b8cf2e4dd1e4705dfdc3f8a834bcf65b6397187d51f08fc141ab40705da24fbf0f68a557efceb0dda184a5ae771f3738b4aff5b41e4871dfa1f901da8b81b49c88f043dc5a5cb00a2e434074a058889cff1ac8df142cae72ed1dc26b502558adfac897dc29d4a0a2dfb01a57ffcef4ef1b83c57d1b95cbf04394eb7a9ba9d1a521bde0d7087bf0f7bc49300222af4f72ce660eb8a466f5d5751d77cc51dcc4aa205d15e5eedeb0b2dff065c65f2a00b21fce95adf36ecea91180ed21ace3004d3b97b54e32309037258eb8e2938d370878e78a42d26642a2a7728419ef4dbbfa4eb315f7ba09e0336f5987226450cfa977839e0ddd7159a29b1f1ec4e30c56b6ac80ff031b60f0c1fc404f50692ae6f5d74b3974fcf9b6fbc9ea367d342865723608089f2c534001bb02f5d747acedfbf115fef092779f58e31e0cdd78069cdf07dc7dc2e53080e596dc61501f3f2481e79e40f8e1a779c1d29ff8231006717580545f01cd6db0af2dbeff579c6b8b9d731e0cbea4d03d5af80af871ad206efbb095e97ea6faf0c2d99debefa8a1f4d45e684cff84a91c717455962f3e7fbc94fb962280c096b07ba3185f5535182a15eaf5470a27269870ef885773b0f4972d7d9da06fcd8850fe41ab190cb999eee0764c7aa4548c37d686ad19bd4dd68263bdc1e9659e44d05b90ae289523823b16be223f36904d46c3c86592bcce1915d562f3fb7446b01061e0560ff7fc4a54991097bae065b2f6656fcf1ef16c6f166a5e55a8fcb33f40a1ec8df7c64d57f6853530284e32d41c10a55174ca6ea9f3cddac28372dfb170ea2655b86c9426a9c49431f9b0c6503b3aaa2b2436527a9b76ae7dad73143f706dc7d653d265cbbdd07f3c5294d01d626108593bc133cf6da8b889640eb78ae081b1e13c858219f689161c5a785d071619f25d0f52f0a17ceebd35a9545a61a5c41b09ce74e3740f54620f4541210950bbc35a7536546c722888a3d3071ba2706974258f63cdd2b975a50ecff8651dc27509d2905c6254d2f842a04fc8581360e45356cc48c4e45cc1c14ce9655bd4581c20577f1bbbb171c99443d03c906df0ca31bfd29ca4ea95588056ca742a765192215eeb5863c65a802aa403241ca030c false +check_ring_signature 7f5b1fbd773a4a3726fd55942a2f0cae2f578c77c82093f455f4ab9cac5f8412 70a0faaabfb3f20c62dd938df8c85661a3422ec6a0c4f2b2943317375a0d3d50 87 752cc6e5338d730ba5bc3d260b0247ba555af4aacb2b293a90bdbbfcaade3067 4f1bd086d5654c8c7b3cf86129b2bd746e484c413159b83531549496a1ddf969 d569eddabf9db69ea2dd3416662b8d2f26a53c75ce6e88d9b8986009056ad926 6ef8feb95c8445b8ef5436e6f0d9a9be5c93ba5b05f64d81369f5a4f11b24d38 5d0ed706d4f4ec6cc0a7b1ef592c092fa5cb6dd4e75492b547e61545609a2a01 3e2431621486d26a48a809ad5ee40ef4f1ea5e499c04d8e79f098750b6844b3b 9b7966a1bf8729fba6c21f570051566db66923b105707a79592fc1af1bebabdb 08bbc679afd8ed8e971b9b650c1b9b866b54198b6a3e39f93344f4b99b21a85c dbd2235ada896a52f5ea43381eced25b015473404b1f0c06554d0a42cfeaf050 40c0842fc4abaa07625fc04f281a59fa197ae1c185f404255a29504a9f251f74 2a5dcdd3369e932ad067d894a2c328ca891d907e2c38d774875c8837f3f9534f ac01f106ead2835ee29cc312ab337248a3c0d53a0ee2a93d2bf558369e0e3c3c e615facb3917b9b7661ad146234f6f31df1d5bc396be9e6be9380794866e30d6 6e8c20d3127c941273f0dd4899bdbb0ee0c67fbb91efb3fa2bd8b89a868ace67 4abb2bf097fef936fe6204a0e9fd909c834bfa0270b773b55f3710f8c1fcfc05 b4d3e1cf399e94f5ad02fd04c364c85fec3fe735817b81e6e54ddd5c65156deb c5b170376fa7e91f6741f013a0778de1dc1b196034ba5f8e5943b94ba940e448 d633f696e0cce60e7c0e8c130852854ba90e4fc195cc9e90282545f3c7c4f4e3 24a02ee64c2cfc2977714f0ce57d9fafb1818c7057972e751420f02bc57f7732 2f920fbd08fa02428cb04bf4745dc828eece6faa87d6848bc9e37709b297826b b3feb20c88b53b7e6e6676c9042f3f0859ebfa8d3fd47e0cd3ebc06653da8f3d cbf28e7e8505196a7ac41c1e2943cd252ec919af7378f0d2e0edfc3e1e91f761 1f5e93206ff589c508badf078901c9458788c40b6c91caa3a68dfbafd7f5523c ed10fbf2df30892437b3047a87a661be36506e8cec5c9ee579e4397229f49a6d d00b56ece38dbd078e253bbf3f82546530cd639b9bda278801597de6895a3f9c f7109184508929d5b577b4d039b6966bd32b32fd4ff3cfde9b9e3f6ff3b38628 c17403f1660b213188d2477aa5fa0ae59109034901faf4f6b2ef2ca16b151837 33a626c889ee70cd5b3157b20733be1aeaef9799d7a430d6b7c0e8f1b6433be2 0334c363bb7ceb90e0bcd2f2daaae39bd1d5aa2b7df4020f4062c8275a474123 63eefd12d4e016eb1dd6cb9b899607c5581c56b91975394697c2df070d285f1f 24677036a0b6b91316c2e5462ebb5c44a856a0bf11fd884bf75e1ca0e339c659 01242579b3742053d72c299410d4b297efe69332f5589e0314061704d2053749 b8b9f403b5ec8a0d4b1baa82eb5a9f9e2b05c35dcbbddcb7314501a7f81abeeb 1b7eade37d8d99744594f8599bcb7a80a8e18bbb489df53074d1adc5abdf9a11 5b6838bf7d4939cc052e1b22805a454a26b2a934d6bb32b0b2559f1f94058982 77dd38ac14dbab65b980836b529e9185dec42d51d27de935d8f227a1d85200d6 fbf5dc2b8f1e5873d95354fa23f9edfd28439e9bd8421fe9eb48fd726cec1a06 9b2b409c76af942077a0da6888c27ccac2dade5169426d7537cee55c2ffb1650 f5a292985a5a78254b0880ba4b7f2967abff4e2ea0d7068b22cca26a08f0a6c4 b47de941db735155ed69c9c186bd5ab9763b748c7ce6321ee6207e9a261d383a f92116048d8014172b274ccafa493e41b8a0393ecdb4ae3c95f2375df5779c9d 4d5500164a90e4bc5f8da5991969331c106cd807070cff57739f2110f579998e 5ba074ff77c02838500d02ba8bee3a51dd74ae8d0443317c9791621c53c52dfd e55fbf3223a8b0945106e5117e9d17cdec81830089171ec437a21ad153bf572a 4f4aeffa3eeda0f50e44797fb6b09b12361f73797541be4198515bd7ee843bac 79f15523ec4247f2dd812dac9b7976fa3b720be2bb19362fe441b64c8bf9aeaf ac4f019bf7c1183dbacf4e772a304a7ad8ee3571701f51bebb99ca82753f3802 79cd58c9c431b0ad2578770b48168cb173438443e2b5b94db2151ec8d0a8961a 78c6822e0d1621802971b11443d65b90b670bf1f3d3f13d3e06c37a906f8823c 8107a333cd25fef3798eafee6124d978691a853a2f3e148fa8bf311d303b3b25 a209ddea31e864e7fd13142d7e9f4ce316556744843a745b6ffe2240c728a762 07d28afa70df11d176afae0668b3347d2282ec6729bb59dbca4318d636c97de3 248c916f0452eeda6fd51524d714ab03f08ecf46808f75be7143359d7ebe9cd8 41d4618937e188939b1874f5dc7f7bd9139266fdca3012b834aabcb47343cf3a d213e7982b9efc5319b6ee2eaab73576dd3512a83c0d72882ce572340e7a3076 8e1e156511caefccc94f0c74e6125ad9acccf06cf316998506ecd652e27bc19c b0f0b939f8689db591a882ad750c6f083b5ec1f879c055dd75b9341e2c883d76 a01641694665fc4f2f37a9f5193d300ff51224c3a5c77ca22aec802ee5b9e5e7 259a4ddb27b6fbef6d63a425ae9095a9c61ddfd6997b31452b85d672477a5d10 2b2674349f0309ccf38b291340f71e793b3e129bc44297c5ea288f6c04974719 38679a0fa8052875f76114aef5f27a52f7b29383dd2b9c2e487f4fb5d6694662 745ae22a611baf046c50cd7a9e819e1aa0030c7d868dd567aa30ee5786c67096 591a48238fc9e43832397e8c4a552d63e0eefc0e4d29392f3ef8b4635a91172c 18acc274ecddd5dfd1d72fd9e510f601d345d11da053ca84d7c6d49fc42d1391 4ffbcae0f44d41b692050d0fbad21bc62d5d549e1037e1912c0a27b532f2050b 57f93f38344d1720f779656215a0e0b97ef57a5c41a92c61ee506b05cd001726 480814f67c6fcd115930ea32faa1716480c7f042413c65bb2e1bafe445c1828f dea0e5855fd4775a75bf55a72ea618f739d318f85c53a49a450391783eb20f1a ff8d837cba368e7eaccd1784d65720dac9552c1771da3454261e33a197000feb 34493d1e5260e5ede5271f0ff9539ddce56415992165d25895371c58fbb12b4d 866174249912ac3a9de2b45faafe0fa3aa0a2392209d8fa57b6f22115128018c 0a5d1a80fe187c2e539400de2792beac38e0dc4f8e1445a416cca2f57c56db24 70324b28ea9a8ea2f2f159fcb9dcc3dccc0b3eb5b554a233624c14d40348cf7a a6378fd945fa6900d9168a4170c8a7f7f6276a4b68e8da71c963e3f63f64945e ecc0d9ca45f29eb14b4eb592b420f1a63b1c7130cfd4208eb82c950add638506 3dab12e211e3092d6ad778fbd3d8fa0791200289fab2e96b8a5085abb7eaf177 7ed7541e693b3e459a2ac8c312f66bdf6a6a41a70f2b01877aa6b76015402fb6 55eb3821c84d83b57c26bb3985b48894e478398703063398759dc6c6099a4de5 47a7ecdfdad2855ba89d8981b4a98583ace77990f361162c19314d71bc7892fc b2e3af23e7ee8bb50e00e772b35b7a5a16310eb452d69c0c78a6a5532cbc4696 229d93279a3e6582182f186b302f0f0c3e9be5016da845c477b17260d3fb83ba 889974e05cd320301cb6f73c50606ae94f5b87d001fdd51513b36cba9d0de68a 48bfcc2a3721dd356b2e446ae2a3195b84fd93e560c68e207103a9477d171e2d 75096057d4ff2d8517a0334adc57c3baf6e026b63e5c629e5633119c601ac47c 719dd98496eb704bc1daf3258808b816c3a95080af3f4ad8e1896c5bf0cb3dce 19f576054f855eb50541c62ba21274ccbb783dbeca461d0a1d6ad8acab72d15c 07de309cc5c524aa92f1250b59d63fd428be6f55165f6be2bb3727236b1e48e0 3fd21c26812e8b2246dfced6eeae1360ad498cf41b22bda5dc9a141d354adf034548f077356b1b7927511a36de11dc5568bbdc57cda6a92f1c8ad67790e536094575547d9eb5a08c650e1397c09d529789da09c9a20c0c366a5178c0da91b000f138d1a20562b844bb8ffff967f70633239dbc97efcdf60ee53c7f8193d21c0a51f9e707019c08277e30fe45df08ad4ac72d83b3452004ff63f711f8b598db0f2a3df33a83200ff33756b5e0a98ea8a6975eb8ac8b245d75b865052355db6a064fedab79ea4150991ef6875804d30ee3597704b51dbe6a30abdf5d68c679d902a42619bcac8e0ca50e8b078601abdecfad8c9fc8e56451e98f6fb0fff9731c0a26f8d056180e48b47747e18c01d9ea206b40afd6746233fdc246e66d41f53704fda9f211e909aef1adb60567c7b1bbd5922eef7949f18711802977a78d38b408ab1bfbc13d6451f858b845607ae6b26e6fcd08e658d220af1fb7dfebe7e8600063e2a53f3cf39aaa37386dca5825109452bfa39ae6ee4427fde8e08ec19b90035460f5286c6564a642b289b868649fb0d4c3db3920e2de109a4e85c8ef23520e8c053883f435bb954fa259e5171332e61758c775819a09178484fddf973cd809f95f0a130b89b7f3502009cd685b5b5fbc32a59236a1219a51dabfa13106ca025332518f73d49ed85ab5cae6bce38287bcad0f7556ba42d3c60d78d24b754f00e58473850f44e31950bfa6af9ae7738346dda2947da39977f5822ee07288750d254d326a4eca47e8cd8fac0d9630071836d1221b043cb0407af0a0c1b2339207a1aff3b3e4e21d3efa44c2c6d048d500b2d6b676f0a73ffe43a2c40cf7365b0641ff4a95d7bc44089570c474bbef1a991c76913b9c3a26b73cb3927baa7f50045252a82740aa056ef95cb231bb51b1397f6ea8f0ce5dcd36334151e7dc73ff003a4d808f5860431748dec7b62e9c6f262694a0084158e7d66255d82f4ec9ca076772665acaea519dd2c4cb18fb86c103842d4b077f65002b2f290dd5c62b5a070671a3ca5b8fae7cbd705d763c5ace8fe1808c9aa3a7c94703208d9da558bb045b7b6cc608a3ccc93c6cfeba9f725a6250229fb942a0eaa4fe79a4e6a06f160d1d0096f1499d026e6f30344df40614d3b02a325d58ab23d91b16a1d13a9ca107d101312ca6de9cfc3dcd93cb6f16e1118cba38ed2bf569069d0674b9a5dca900c697a8199f5da9f370bfb85b9c057826cb48fd6ce799dff642bbc2ddd2dc0f008d7e18b5bd285595eb9bb9b10af62c2a423cbb5fe5bbb6f22a58288f232cc40f88ae378be0d748d71e8f3a9a2e2bf34ba07736a31350fab5c70acce97de7dd05f436480675b5ef7450e9fca606874c46e6a2f83f9776f9f3add6304630fc71032c9a29c159389768e60413232c322626cc830cd1fb7000ac99ae9b00c24d4a0a63280ee4d511d61cf8435cc6352c2caeb2685139a9356dc59c8881a7fc7b7406e6dab458885fb6d73a41e6ebd2959a54b6d477091e8730d09b950e9a426c700317aca9e1f618535bf164c7eb7ae62a78d8f42a15bfb3f3c69bff48757194590911511e2978c0e266d092da5e069fc51138ad2c3accb20344a124f6a9de0b320c1f6ba6c94f217abb4ec32c42e1c97789d6ddd850b61193118bd14a93f3181103f4175eab61f276c07c4d236150b16d395be08b8a7851374c2a0e12f9a46f09076e02d780772d20375376ef610a4d54136c9a29d90a928e6eb6d4b14d3e0bbe0eb11fa1f9937a6dc03e3f73d282e3fa47bcba00c297ace7164802ec0ac816af07de9a25f26917cb73e64026c1433c1ed9e9c7b11c1f1b903ec0dd0738f7869b08780b9ee60f1bfe131ebcf963ba6171bb995255b1d27f76b884b2ba9d70fb16064d0b5fea7b315686e56635eee123aa4f1b9e4bf5d838e9979a8724cb68d8ea0572bc744a273468feb3cc5d3a96457d38bcfac9325fb8adff79590b6778cccf08e41208bbef31b99df3346148685207e4b795bd97f827ac3f644aed9babb962000053a763e0b2b9a9196fcfa241eb548826d7173b7c1c2fcee6f31cd7a8df090638bf9aa18298b7226f358a8e4307c10210501a2d67a04e4902941af8ac62e000dcf49e8f40d93e4dc7cceaccb879797e573b4dab3deda766dba683f2eb8909092aaac65c2525bca471a175bc1398ad19c77d7d7f42efa433461fdfed5f2693011f1df47c8dd9f62eecdc9de98858f1cff95672793b1b96219db2720959b868036fef50f42c6b32199a25f42f73228265b2ea0184db2ed20385daa2ffa096cc08a646ff4c34b4e5e5847f76d9c494b8338655cbcb65c2715802059344d88c87059638da58446eb4381d91f40ee4dbbd46112fb380cd1fa4891e45b6c6779a2008943c5b2c2b74cdadc08028fe9b3a3492a1d2a625c129fe65225e71c02b3f9c083b29c82ad079ddd0c1f358cc04ca028680f57c81b9483bfcb7f93b36177e7f07f265f570945001afbea7e127c5be89914b7fa1e8c308f8cc48c439dbf8fa3701fd6f49c6def6c83b4cf97fd14b457e23de1cbd4ce6e523c8c04eb8570f12450c4b6260e537b3d68d1563c36bcd81aa10efe2e9b43aa2cee6f4f458ac614b2301b5967d1c54b9e557335fe409f67e5b98652d6c34491be14bbb87dd8f78522a0b0f3045ca37cd6af235124340e4dc88489c23bc073d981556a00bd3a0f935d906c42d75dd6c7e8c633b3888453198abf7de9d5b9fa8cc780ab30793e60caee1067295dceb24abd662c72aa4e592bdd18dd2f548704c69fc0ae407bc392ffc840a39a766e796c56fc7a58ae0136b612dc31eb7ba21498e7bdd9a53706ec35d5f0639866060e646c9a2971527d1cfc8f36e3ff989a28bea5b2cd18d4b75fb755d0bb3446f4ff87e5a7db7e1fb6316700bfd86a82072cb9ef3fd7b172ce3d34e6500cb4cc2f5b41d39e30181270c7e5ba773d48dedafce9f771015bf670cf241ee099819e949d2d0e145bcea92d503f70fb3cb1e0cfc929a5023fd93d1733c2a320978a2494d4b5e3b7d08171698c650df576ed3d7d289cd38605eae46e7da3d170d212be70a1bbef6fd3d4f9455204caa993b51d476e46120b319f8a89a92621809575d0dc1eb4e717cb746095bf0be2c3b67501084f6a29d0e341548d85897170c503d3bc07eccd9ba29d15b27e77ad1b4238f2e55af074a1dd4f062e95732c507ac4d52ff4d83e929a09efd5f411785d999358cab150008024cc84219ab2d210362fc93aef35b42a8f34744aaffcc3504f894bf7e874e61990fb6424b38b17f0ccceb62e735b249ddb61af3e4d7efda5c0a86436bad66749c6d45741c599e820cebc4c9f2c947457ec23a0eca7edb39a8bc6217820d95e09043d1fdcd6cc5fb03febc1df2849efec529cbb0efcd1b06e90b2ec3197d64d721c3a8d598f0eec60010364c32b9cda7a82d002641ecb48280fdb280b14ddb205dcb8ab542d8eb62015e29b5de286a66567f4eabc8fbadcdeb42b5ad933b1c8abc4835e6a2aaad4704c1656b39e28b2f9edf47b9e4d23fce28c6be8e893d1d506646cbd15c64254b0b51e58e1f866df4ac1d19f663b0e9dc2b35a4584c5bb21cff9b4045094982ce0293cd21049915ea13cb0d4fd4bf636125cdd19963f0980eb3c22572fc1648430a1fc32d4a4a2c37cf0739c8a7442be6fb4bd95d4913f50440205a88b646d72c0143f5659f33363feebaade56fae1d4c5d5c31851db69d4dc5785ab411dc12ff0a14f014115a60d52fb5d1af4722d42e79c62c479baddb68835fd7afbfe1ab1d0133fb1ca1e842739edf807f9daf7e0a773b24bcc8ad2d2e2e69722976879abd0d0b2a6d9d89645e6a20ce2102f0c2d4a0c25b151e8d4c6801f8465a4e9cff40000649b478eb28c4d9014a82eb0499159ce1f5db6cc6f7902777bd1490a6a3aa06bbd971a13222368dff213ffe9dc4e08b68e98a4950476c956243211fcbd219060b4e6cfe6f48e4bf23bc004f67ea4363611d2a81ee9f4306a41a62cfb33a81053f8c6623bb3cee5510796a18887de24fb77d540ec235c74347947c4ab30de207f6939733c6c30a7eabc50553b7c71c3a032b7645c469a742a5ea8c256d791e098fd978c20c9c6f6bf0d43f6bd4185b37cabca9b785723f8095bfc50257bfa70b8c0c138bf0531fc4d1f6a1fc91f60fa6cc8f98186e8c6e434950719fa9ac7901d3a847ba7226f8195dca3d1c54cf31f6a2f76619d0e267cb4a238e569e87de061c539580ee35ce207ca0493b64a1c9c6238b4d24d5fa58fc5f7733abbd74140d851f6077ada353954c951821146e7cc2e0fba4272f67b6b38ff7c1ac198d10079b241c730f57548a0551d4521110b10dea018fb46c76a3a41cf2a7668e48d306c8e44c4c70d881acc1f276e894c61675e2d0b4c15cfd5e98ee492316d22ba7012490f0484cbff32d134912cb94fb739e21ed7d2bb02f6af1020f519c0fe26e0b0d6bf262c7026281db98e3d5d73faaa8ab64305ad4227fe40f5a600bcb4de3027672aa52ddc0ec38c8476fe4c87328c8d446c09eb9fcfbe38cb0515d9fc34f0b68687dbada1ce6fccc70557d45cb2bfa6ab1792e501c21b1ebafdc1ce9711207c36685503d9477787cd823ea1c62910e3718fea9164bc87d30dda35f919c6e0cbc76ee0726f380f7f0ed792f0c808ed408ab12baf0c6b8277cd627c498e5480f5e943b2751b56137e6ba354e79119de72b4b79ef1148e2df07aa6521a1134905b431e23f304e3d89538b56ad57cc79bfa62f169a940a1c49df8490514351d80d12d5900f0876cf63eb658af9bc8a2781f07606665cb5fde97989abe502e6df08c398f00dfa98b512724856cdfdadda6e8cd466e6fb6b165cea18aafa9e0c600e836f1be250bfa295402d9b1ccbd665e3845b8e78c3a25b676229b6711e2d620f7fede3765d62099fd5b70f59262978c3bfafb0cfe5f210827df5f186e46f4c009254c225efce6e2989b78a5ce5cc9c8bdfe5399a9cc2525b8d21d3c7a7a0740185bb79cc6f66421edb8892629d64dbacc68d8ca739659718da241fcf2099420c8dbcbd34f70a79c02077952b041ee3fc56eb319b1826ad54b550f8323493ab088a71272d5246d7e80a70be086d8f78976b1df83da6008c7a569d6a635469eb0e0013901dd4affd346ea74072b58789ff664e251db7cd679f5a69c05cb3d1170d8cf87da08ad28deb8a0c3b806ac5800a8b3e12e117220804976db7cdc3206b02ce38610b9e34e474c3d87f19a7be0c34bba54a9de1e7e4e3d6c4fb1222f8b509386664cd237961cbef286d1e892532a5a0ee65f4e7a179baea6983ebbdc481027a97ca29df72aac5fbe57c2c54c7df8a6c757df00429a2d55fe3e33244aad70f64c77a595ab8140dfc9cb979958cd5cbc5fd767adfb7528d7b683f8114224d0f6f274cb5219965ed38919bdf3d5da28a7dc74f1a75c207d917b863ccd27fb90a426fc40e4db31ce5824fd84f5230462a48fd829cdf6a044cff24d74f5a48a8026abe504cc56ab00c93b40fe689b963124584f992dd014d78e992e6b3375a7b012f71bbeca847cd15127834f7243a5138de7ffb91e6a658600f6dc410af713b0db8128a1d6b0b34587e5d71941edf40dac05a0b22c307c99e6ac40af360114c054a8318283d7fc78c5ee4d4144d5f0c289c64bbeef8dc0aa17559e5e62975d203a56281d5da7fd780ec38da86903b7b1dc8c3c645a1c58efacf4222004dbe6e0abc871a17e44f19028e110a7f458d298bef6d9ff8d67fcc85b3cf355016a25900e0141a68d444a4c7572760d17805e302380735c496f0462b606633d3caf4ed0c71cb7d4fe74a67d3ed6d2a9c0f0a2ed3e502cc6857cc14050537f511bb115c02ad3586acb4a276c009038e1d1c02ad87e8fe2ec399ed2c9650f18e78c458880165791ff3b4d5b4a70d709cf52a95864446909ac8259a49cb455b9278b7f2ac04c2d25dc6ed4dfd68cd8ac3e5db1d3333769b0507c7193497e762367f2d31172348682a70dfeaf9d2c213ba259aeb550a594ce25f1b35ba547e63efeb6d94d408c26ab4c765b71fa67731944b40ccad903b9d56cab8ef6b9fbe0a35bc9c2c33061c4edb22d71d9e74290e2db3652825da4896648052e792d54bfc9dd908401e05b9e760b96521c51e4df4ac8b530d538a569b9d5ca0fc741c9074bcdb05693608327b23084dae0de6a5ae9ec679eee78e2180bc47476f9c77bbaa72b9d129cb07431533d40f20c19e8526774bb380d5a4c13bd9701fc51cb449b237195cfe890a73add7a1f53bf5bf7ed6812b804e8d3b558528e52a71787d36ec65579205fd085a6cb63591bbaa5899fb76ada021d07d36cf821aa4c50ef2742b194d2e1b820c1a930a30ae42a71e600158eb2933d00999892dd2dc1573425b2855793465f0092a58fa1c5cd1bc5f4aab8038f4eda90b22f400a0d08c3cc8bb0b640bb700880fc9f960020641f0db027f9bb9a92853fca6d23b3f1af0ee6907f75c65644ae7070a92339d15afe0b683e88f5d77a36a740a0cc3fa937f94c01a0ed8d4f0584600b15d54b0d6e045c8e6e500d3b803769650619537da3a443d8731743b26f0530325c09f43c2cbfcd2b562aa250109cc58a9423b45a03fd1876f1fda48c1c02a03dcec9512bb3c4d99b52c109aa9b5a337d08fe23dbc5c843cbe7c9561c4626d0c6f982b447fe58c88a871a95fcc5db0c9f9a81b2753b76787d1820a57d3faf9051465251503a0606d12fcf9c21171fb94126160237c7ce529700e27349c2702073344e28742013e2eb7dcdd95e39efe39a48e009a7b67454420293fb68e70f30855da6a3b34ab24c5699a94cc628ba2d7e58f4cad6d6c2800ba71b7d227f1c7050441a2af2eb7b5918c095ad31f2672537275f13a1b7bbe302ae837c0d587e2076c24479bd4a93d880f9f63831bcba17caa8007aa0eab3f3d975815d94b837008c34d4f327ccbc8f32c8f708eba44d6d401b1e6a90c53621db726743164a2d006824cb2568d9ee0c1d2bb33e73cc2cf7cdb837322b624d5b97f32e2ee7fa282031c6505643ec75c0796f48ebcd02f3b886183a100afe92af1bf3f37706f4899037f2ad6a1b86f588503188e50a7e397ddbabb15b7ab427a1e1c6e4dc40dab8c0c111f64899b2be5c857ee674f8a69b6a8ee1d81e67706ad69ec3458b2432e8f0dd5ea5450c2c8f45e78290b8d2c439a28be778c98a04568ae85c2f2733af0c408be19fe178799fc5e6f0394d93bed640dec1b78950bb323a92b3946e51461cd0e3bfc8775418f09fb92d34ef60e1a3ca318f51686a947215376307e4a193a190722972f92c557904c1d7a8a0b82b44522bef448d0e402a3e3959258048b1ec90d5af2788be734528157f5efd75cf1e67aebdf52be8c975fd39b152e36bb75440455eb3f642a545006bb684b40cc7adb23b27faee823e74bef97d0ac4af56c650e69f6155c473fa15c79608849227feedc678c927dcca7de06e1739f99b66e040c2707c5b5594f836d38c4a452ba713afe841eef947970d656ead6984c2bf1b4079d287b6bcc3e8a869ed58925dffa9c73e905c627805f2106b5ae11073f6c440d6ae73a6006c6b4df85721921dd6d986a9354d942ebfe57b3d0af5b2ee1d0f60cb3811e7fe670a76749d489b62615d250186b720ec521793f7aff0daadabf6d019a3d209fc6a90092bc12d980de3afc4de82feca4874c88a150217eed9cdb21013b628e0bdcfee66fc4f883a81ad1832f6f8422ce85cc8ef6ce4ffb97f3ae39097cdb697346d5d5ab3439b5b50c9b90623498d23ceefa74c53ffdc56fbf222705fa64ab0d04567c5d6914e997b9cc72c7f6a2068d7fa12d1562d9c9098d44730c false +check_ring_signature f73501b732e04f72bc4a79943f544f1fa45686ed1a4c27d441cef1e3bbd682ed 8ee9b8bb34dc72576e92a948501be546f83780ebab05dfc3c526cd1ee945b924 1 0dffd4ac886f9bd05f097f136226eab719475f6e6d4c0bcf20e3a5bca9409b95 c01e7841f84ddc4e715e4ab7d250504da924ac6b5a01b0c5f8c76c472559ec07b60bae7e8b0125d4f7c908e20fb7b5bd24a62d9d0855b596997ddc2512393d06 true +check_ring_signature 67e5faa8af4d1295636ba84bf9e4c896b9f8c7dea021ed6e6bbd10b25d2da141 ed5998ada28378268f146afa6c3171cd01406cdcf58a701d8078949a75c60b91 53 7a1c3487e6fbd4157405b656d4c4cf83009ebee3d679d7cfeaefa741cbf1bfbf 08385c65bf2f124c884c87dbb8db36740ab0b7145d754051827a4147d9d6fb9f 638562b5a0a75cbb5485a9a306075f07fd6b80cbed4b30fddc4d3c110da62cef 536ea7b66f70c1576356b2ba25365c9998224a4486e33bd92ebad260196e22bb b77d391a82b57d9e5bc0c61391c4e327f6edf1f25677cc6ea43426653e4a4737 a22cb7e15328c5c321ea1b636e4f8564932fd43ce96f949a927ce4d9578ad479 453b040deecd5d398943616e550c677aa6388823365024a8707d24baea098441 84ecf181bca3a9830e01f346926931e8fb5b8e90c8c5a404b69c6c06720f729f 6f4779bce691975a39c862eba3b9c5cd6212dde1a5b1d368e9af49b0ca893974 d66bed48f62a908c68b87c013ff002e1b3d9c57b7b64b571945c0e3a00b8b36a 95aa9fc5965c7bf88bd35c6353046755061b9677a1d298c0e7585ec769676aa1 3e6a0cdc6c4daed2905ed1a53136f347c7a787c671e5443b4ecb1294eca19017 9a7f50d8c749b2a864794e2927013024bbf3b187306db8b327dcf6f6569c7b6e 8b0081c4c58f908e0a265b3ad09b2de181ece9fec36808897d775c4ee3caad62 b51646d677930edb356250f5bb05321a80970d1529204d3b8a0e49ccc1e29c81 4fae10339d99e319579c951d0464d4ab09f02d7ad513186a04ad2246f34bb5ba 73c2ba17b8f09d149fb264e51dae4d9f5d8566d22a3c32a95d2f00f82c8ed28d 7a65c49d17006be33a360e77297548865b8d5e7b1ee260f53808670b0807ef3d 2708324697a6aad3191aa74c9ecd4924b05a969edb7dacb6530fcca681485bf7 f1cafee1728038e077af14c04eefb4b0cee8c77c17e247f898c53564faa95129 34d0c08c2da5e35a51f6c6ef8eb626458d2ff3df295909311b2ce9875e0d34f5 8dff9a3bd5446ee6a4f0103e68d76979480eba93b40419574c4ad74ad77d92b0 6842a474817c9409a9a1e51e71bf392173aa1ddcd47e58aa76ec0f9f515ed45b 68fe01a5c4ac048e1cf645ca08c4b8b33c2b26f7f2be58b472fb5ea31f0f828f 59c04e962a7e565945936a59a1a9094bcfee55d0f42138910b68627c6c49ed29 4f65a537f4cba03d6bf8f7159da4228c75121ba0cfd3c4e47fb06b0d768ba827 d2179afdd22640b799a47f1736a7699b4713da37b279c12f98feaa3d8719c499 f526ba5ab87d6833de8494ada02f7af5c3fa30a1a17b62333115d1c7937835e7 aeba4a82049f9e5315543f2e02069b449e7b80a07da0abe628686f7593d85ba9 516654bf204a39c94793e47feb33cc16abb911800cbb642e799e7d6f3eea3e07 9f1341b1ab5e9fb253f40d0fc9c1a81c5ff6e0e77b19842878c25b2b6da5ba0e 96eddb26eb47e97f6541c8d9b9c65271ac37bff6c927ee2a62b80a425a268207 5576f5edf914e68e0c3e5da174e15b296abeeba83eba4685e02a4dfef1aa273d b666c215479f0415d117bea0a6fc781cdf3f48b5e64e7e1b2e5083d650696d05 9040f54dfa56dc791b8f4bf7e73ef8661edc22846b501d1e2ecb3b76b142015c d039388b8e33afd8f9d061889c600f33b7c3fa56d99b00f367f33e0f9fd5375f 6709b9854f8347cdff20a69314f36feac1f12a9d02d271f2c9067bafe593ea0c 8406c19ae6b53b4f7535f118cdd3d746076d7c0a9c39457d155a0c01ffdf93c5 092a120fed111f08d080c590d19a28cf5107f4b61b7905f0b83518b35643867c a045a2291c9f2fd9116bc4b8876634107abb0a8aafad0ae6d9f72b7bdfbaaa2a 625d6c197ac0c3c6e39945e577a0e563a7fe865defc0ed5ae1bb01467bfc5fa6 7daed66d01d1eb5800406ffd63ba399903bf5cde1e2f3266387a38d7d855491d f79d4b7e3597cd969548f96894499c67fe4d5e0313acf4616e799c19b40f21d5 20abbf9fe4fbc15a0ef5fb432115d0c29a82ccce66bb11dcfb8718e4c7db08bc e06d26ff530b4a4b95b1b9f0e5a2e9e52852475852ca999f853687881426d900 c45c22834a74274a29412ab4caf52713da7cf7213a2f15058eaedc26e521dd25 37dd2ce9baa87d7bfaf85f1005e836142d290d81b2ee400e0e2c682bc7ea9dde b75a0f0263270eb907911d65e339e73b474807aa9d7c30a0ca8826d440dcfcf9 260ad4218c9555d5295512ce011a00d985475883b53a4bd162f18a0d2431b4c2 03d55ec1f2817c7241f02940790c77949fbc35b397825c6baef018f5e077fd65 eac1bd49bf43095723d3225fa4e02ac82fa3d002144f3232c5c7bd1854169cfd 056041ffd3579e40d5a425e1326665f7d4a6241b50bd593558a5ce07f6200a3b dfca95045fb42f108ecd8e99bd1119f88ee42b896961967286bf5eeb401e2b9b a6f2c1da715446a800267e14323c371eee61676433e423e10e62bb9e4e85a8010e7d3b2d231bfb3348c7789f13e3914433fd8beba5fa6a23287aebd2491792061d159996cd0d3c3090e313247e79c719d04f83663f19d7002fcc8624473a050b1b2ad0e46a2f47203f6a70915a80d20658e2daca29c587310d99f6f9b49b500fc24543b1549909616f72c461152b0f6c946c5ae435f2414f6ac7899f5793cc097681adab58429e412f192f74c92a8956f8e9e38b4fe0dd2fdfe4441b5a30480afe8e9578c135e212eb68ee6eda012880dd552ba571837b9bf79f01c422cd910dbccca8e0fcb26c26a6fda8f32f1198a74388bf8b5d3d37839a6f251ed33ffb0fba42e2c9950e1b5265967005851d89272de903706947eeba4b6eeb478a876c0e09930954c6d2047c758fb21f139be6b2e0070f67da727b253cfbb64957b78202cb214f30eaaf775fc6a0ff335f8bcff79dd79689206c73e3941b1bccdd7c370d088c8417192216f6a865b44dd51e98b95e47d728cf66e73dd2234c46287847099a2d25bc98b23727dcab56f2709a520491a05f619ae38f1f62d31d7c6d993f071ce16fd4cee60be5259bab57659378f942c3af97d4c86c2bb530ad9913733e0180937ae87be80237ff586e8b21139509fd9fc254e7e9c3ce67013fe663d430023c3de99738580df5156f08f6746504b89075f119fdf5080164923cc9356d5e0be4309ec0e128ffbcbd685a6bf0cf05a22dbae0949ef1ed0799c456fac382670cddd3bafc6e45d61bfa8a99bdc42a616743c57b6cb000ca0836f2a6eb8f5c060c41d510459a5ad9426cf24b9f2799ec5acab9202e50831b8494089fab6cc02f0fec19794205f09cc39ebc6cd55cc43ceeb5f0599e45e5d7a75a7223f5d8976006a3d8afde40a31fb7b672613dd4c0e5f8f1e62f93847382404849a3cbb02da607e4035d30129cfae94c318cc1ac47c079616f4404fb376285a9b1c0135956fc03f68bebb45382a555507b4d48c5fc06d10fa56f30e8f78c6a3bf286302e265d00a4eb41f1ce0d9bf129cd6e8f88a7c83cb7c45313eb5182561393daf01991a80e8ea346fa632c94ceadcf69897bcb1b048b67171f962d41abb295c7ca74a16f0273c8d441a60fb6f13bae1ae04a1aa0778a74e245f9ceba22640052291315c809c41f4978e40d6573d14e67cd2e54c122fd4a6993de6add213f4e69e21085540566edfc682e9245a0169ca3d9eb3a853a1753c2d456656bffdfc904aae0da360af0931eef5551d3dc523c40ef3a1dc988ea690b43798a83e67c10b24fff27df06f103c8cfa3dce909d8e6e3401711434cadb68a650c9427a86c7bcc294b95b80286d1523dc4fe9800778e4003527bfe3bf23acf56bf6a352555b89a11d86fc90465e393454c2a1af36b2d343eee8a9d64103f428366aefcb6a57bd35bd931800d34ed954b97435756bcb2b1145190a77ee6731c3b79d79389aa793485a1b7a0039a2fb5414a22558bdd909b6d65a270dc31da222250198868c52e47a5771aeb04936c4447cd808fdcf02ba0d32538e466636f50598e007497f62ccba9a2a0970e1d73b9f334de4833bc38e9ed6dfeeda65f4854830efe121f341ded3b37128601934905adb8ddfb84adc2765a556f48970f974f0e08d369413e9dd5f8a707ba0be402a0e0b6e52c94cdecc9c0a4088d50fa07decf0ce7405df8d218496e43bc0edc07774d7247b9697a0295f44d2b8bd1258aecabe4038bd5a3cc04fa5a292300fb4d5059c09a1d652499a38c7970f81df664b4eba8d8ddaeb254dfb305ee460f015376c38c1041313c1187ec76f6068d98d88c686054dd34ce821fe5d57e05096b3cc0032917c15aa6c51f42b3ff18ae8d9f00d8999985978b495e4fa9b445080e0f3302a76a00165abf7f22fc9209409c3827c84e7a965e7beefd5f3abcad0453973299200eb7a4bbc8a2acc6c2f1c468e26b83bf079074465afc1bdf6d210258192abfe24ca775a5dbcebc5d2490c283580a31b76442651ae85ab0b079b00fa318ba5c7c57da0c0ff0ec075eff9fb631cbfd24182082ecc98c7954923cd10a3bc597a32d828293ed5238306cc047521ce566d4397b0206342bf6675826c40476f74cf21e15500de78b1d5144f78e94606e69bbd745f0b80ae0d02fcfc54907e6f14476a4b4b3c07d3688e502f11ae7a0ca9ba1c10a5cb92f7489c7d50fba0a29321fe084ee6f984ea493debe2140972cd44672b67f9580f6127766712e9d05ea0c169d1e5db8941ffd899291c28dae16fe201faab075ae1d1adf3731b73907da4bbb5f02326bc3ed2625866e299d041b1e02b85df2af635e54c2c707b72c05c82954a532a5ed018a038a91db8d97ca9f34daafee0c03a6127a57cb03d7e10303795719b1b90ee368a4b3470b2584de02a24023d1c377aee777fa10defdb909c1a429caf5ebae8b81a4dd08af0d14c4af69175820153ee75bcc7257d0d09904991948fd214fdf69525ef3953dd86e92066b30a27bd02a2272ad4bee6f92b90d1ea5378ee5b736b56834a758c3f3aef52cd356df6ca3432620e810f44f71b301e6d9e8d454d0d086a5cef6c4a80543602b3422b70ea99dcdf32b571e189fbb03267b6393129caaaeb77555daaf85b7d9167d1248c0db1e376c0177b1edf079063175372765962a5c98ce10aaf5afeb0309fb28991e1b39727e2104f7aa64e7022683f982da5e10a9c03e87fd3fb371b7d21fdc8f05c99512a06810f95f5768038826d225e40ca1a68e567680d3fbc72bc226af53d110fe1ee74edc9da299d603d49fa1e62b1da820696181738b1e943ecb93fc60c137f35526dd53af8ceb670b602c0754a5bad6a26065609fd7e2dca4c692ce06085d94480fc9652e5caff00f7db09fa251edf9baf43c37dd5fbec093e21ee8ec5447445aa7573acafd3986012e26462cca0b736f2ce6703f6c35428f63cf539e9cb00ccb0ac420dc5b459b08c9d57e30ac024ab1b3805e0154287707ff732ea3633e8374aa2fcc1f0ae0b906450898c2bb16dea83757bf8c1015619e882686e9c87f671e7cd026a73ed675062c0129f3fde70006382ba712bcc349278ccd9c098773b3e19273d024ae8bbf05f634a3576ca6a9bb0f4b960f7bec8747641447ad29075b0701ca81b2c46488089770fa6add13861c37059d5efd33bde9f80f9ce6310b9833fdb58cf352db98083d02e7bdf393cf8525a2a078dbe0391bc7eeb956f9b0189d3599353c30c6cf04c8dc13b5d2c26ca41cbd59d48a004c71f9686a0cac7aa343a785109df4efa90e8ba1dc67c54d4634497da3f5aa5ebf560f0de933a572dbd890a0bc6667e5450f6788a381e8188fde888b1f714c5cde5650314edf7e44c0be93662cc92be85903cdfff8cdaeac4b83a33cc34d70a3dd240df69a0c5f0883a542fb3579b0feb907d2a9be3103636386ee6415022cfe0f3bf5aa33c90c95f992945cc4d6919caf88bca37dfc125b1c32025bbf9d66a8016fd3881882bfffae5f1786e697b8a07a062bf6e15ba6a0d5748572630a0b22ed7d3092f600584f510062dbfc589203d803c928566312d160791dd08d137b7d0bbc0267b3bb5b6303d6da45eaa40dc4d30dec526a4af52b19966f884f9114cc1025a065eb5547284d3bcafbe82e372f5609e0e667c7450bd2e6aab6879cd93778eaf596c201d549104b54f62015762cfb0e2a8c6f99de918c3d17842bb8adc0498bc0bcc8f643f9a3d49d9b6f7752117e05ec61e83270aac56726749a924f5401d95d0a5ac3f4c30fae91adf5f725011d09d7f882d8a51cddbfc5cf8ec752790892ece7f224119d7f917c79526eb1fa2f0450143c194bc2857d3bfb474987ce69262765ff311c2ae932e2db625b11c1ab02dfe1cb10ee8ab38ae291fecd8121d4e9e0460a73fd30842cd5edccb299760a0f54ee01334143e73254e9851d94e59f2cfff2b59bf59abd16a123ea60dd26e101772da8326eae72d187d7017251d35b05e197f3b9c05e735f44affe1cefd42607194dc426df7ae745990ea49587d1b792ea279da3cde2e87fb2a8f7414cc5b7031fa800412c814df369b0f077870cda5822fb8660483f59cd8e1478eaebe7240a44bb43167e49e4a2da05902ce0b39648b6352133ea9cb97815c1329f17dea9025c2d314cc076e7eff6f8a472c9a75f13fbf91071250a45854d156f8f93daaa0bd3aa3d68caba19750346eb1cb122fc1800386e3c38e389fd8b22d0cf0074880049f7e723abf65415d11e0a2f3dca9e4bc0d9b81472b4b3d40653b7890bd38a0dd76d8532c7ce6fe1f5c7c8b6e25ade7b8d7110d4b3493da3817e2de9c7c19c04e123cbb7a704b852840b11e9c8598d84a180f163ab9cc4ca24f06816dfe05a0f312549d03dd4dd6cf32dd69eab6aceb310b3e5d769bece3c8c2337195d67f00dfc44267c3e5b01f8add51102752261a1c74ac46931674bb490c825d8582d560fb65f5c37bb9b840d5a8b69f80748a3db8820f28c0e85764bf22bb56b800dde0916a6ab57d7cef76f6d59c97d4502a5c3ee9fb2df71b324120bea34d98fe08005405233cc0861af774b019d6681aa6b259b2dc756b3b291f7e5cad4c6ee2fe80d199ec45328ea47a7ce1601f8d8885974613a0875ca771575d424893747f2320e3c542a8675bd3a646eaa9ebf5a55cbc899f45f5691ced54d1023ee58b093b2014c6429201f4fb8918dd61caf4840e9f74a9cbd65b1500fcb7a6b2dce5a664c02eb9882f107ef42cd87f35c1c01a4a8988ff94f00c83f3d699f2e9d5171726d0d false +check_ring_signature ae869d7c28fe5c41a93177a889b6f60455983aa7741617f10adfeee244fce5b6 ed8cee5c8b4e8d3f345482717745ab566b6adf4e654a163361a394ee5f76a3e6 31 8043aa39da9d45f8784d71e513349f4cc93bd4f8d3bb7e2ec1c34bed5e0bae98 55b072b4705af06a547e5f007b96040516320a14c8108204b65e3a4cfab533b6 cf20d9ce271bb24b5d8b97771ef3d75fc583f0f3a15378fd0d04bda5bc60439f 092a1832362f4f4fb0c3bfa31a2ef05f7000875b62254869c55be87c5ca1994e 5b5e492a16796a627f104157c012a86803691ccca7375f9977fb684922750148 e32aa8fd1ca9348fb1db61a5dafccc5729a8a1e7c93d979b7e358e0ae8d303ed 32be3e8605c329cd7aa9ebebcb34b0aef1546181cfe90dc7db3c4f2b8df5ef9f 5e687317d16159d267a1c990d1a86c54651e67662fc36537418fde73a5f5c675 3d37a212dbc36d56fae9a2ff9c9c6c12a7b8ae823b28db7e5b66bcec3ce419a1 bad593897ce5d30a4547abde5083f02d8f25dc48a8f8377b0ceb1c5faf74b205 edc2714510034aacdd742542c62c0b1cb59637245646babc69b55c1268117522 470fbc089f131f367fe38ff069ec4e4f5cd86c1337624227e29666e6c940c1e1 d0cfd84a9b5aca026968c0f07faccab9b0b0e81608e8959751f882bddb180773 921f7155e52dfa31e5c7c807352d06163b88407220785fde4552267a2c458b42 8ba5fa07130f8595e9c4939527c15806589e69e9d9f46390f46761aeaaa09433 27080058fdbc0efa39dc4e65b53a075506a9952825054bf3845e689446c7688d b78c94d62d7b023564f6202c3d59b58b34f12675c0e349ad8f11390f6bda8dcb c48fe1ac0f923f468e46a98eed18c831b301c0099a430bab1941aa3783eaaf8f fc669ed46b977a2b8bf4d2fdd4e376da18ab806ddc48486d94b3e0f5cab017a1 a3079ba66f4bdc6c2497fb0b5f9562d2654aea08beb1b908498ce439f6b3b5d0 2e94b9346840d33cdae7b215bcad5d6d828d7fb89667aafd46163c1e5ef06f51 41aaf602e7829237716d92625adee7232a40f8d2bbeab15fa60ff46ddf852152 9fd758a38825446ff8bd294cc4e91ff9d504fa3b349067bed04ee56acc4dc501 778f5469bf179702027f5b237500744b5c723109c94850bdc96d8edb5133ca9a 660f4eb36a16582bec40cccf68d81981d3a621d8e593d44b3ae56c977a034a06 8c80afe096bcaceb08e12e085c22b6b38a2348d03b4ec64ba849ccffade2ad0c 621d199c5556bad2257844dd79d6a20eaa109f61a1fda1df8e19bd68900a81c5 03f6ae0f46b2fe379e7dfae775e1e51632cd204abaf809203cf9ad9dd2d9a123 dd2a01d957206dfc2fe6f3b49d7967f2c9edb4ae1044aca186f3f4a6f197dfe8 0fec9d273a663e6bf804cd209c61c615dfe6f8911fe99b081db9bf5291217de8 e80fd3c7a7749e8f5beb0ee5cd6929a85aadac9ad12779ce4c9897f988165781 fd0e50ef2a8d615a9ad03da7f4f2567ed73c44be543624351d950cb3921c2c08f617e97717d0a2e4b5198fb88b7379d78724a668dc49baf8504409d2fc5f5d05daa37450b3272151bb9b5da6f4d8f0873cc349f7518cac4e0222f1889e069a05bbaea9651f9381bb510078d1bcce7e56394a4ea2076a3ca5f1e81da30b0e5e09032160bba9091a1550a7ccc3e463a5d87b423b6f85f782688f1ede5f17ad430f3a6c540964a7c5a5f3423cb578ea713595a223b941f1b72a6a880a6e0a6e850b0bdc965c2279c567075d7045dda141407eefd03c4fa96eac934760d55c752e05161ea702a6c617368bf5d2164ed79a52aea14bb993f5a467cc65b1d8114c9003d3529b8ddbf0c15c138a8e14739ceb6ea3a3907da25635c3b61d6ecb1bf4a5000061dd1f89d4e4439cfc39b4a66cacb32b210067dd805b72246a91bb7bd1ca03ba2faf81dfdbc8d062aba6b036bf2078dd6d0f2a4803df561cafbbe0038e6a0b403cc7a8ed8e24992d993675e551485e671453be3b289cfdad2c335a272ac300315064dcd1a91a248e4225ed9ac87f198a81378a9b4ed67bb9ece35f8d2fd50288f42c485cc9b2f25bf504d13821eff8716d270e379d716b7009e040eeee220353b8b64ba1fee0dce8927891e5239841295419ded332b0168cb49133f3ce210270093d9c9181b76b3e92dc40e5198589b6d7f117cd7721b81d091df519e438058fda769acf9e5a56db7e5338de778869f6cf2f51796779a20412d5962569da0eae00187b9797ecad3ad5b717bd67f671b232a546ebcf9b05eb0fe0db365ec70189317fcfd0b89fa0f3b3e31e5fc641e7ca976e2c4208745c380513ce2f1bd401f62fb488720de24d962ffca8baef43938db9b102091f006155ff604f593646083ade94ea1632065062b5053079de67905086444f37fdc522d43fe7d5be2f05062bb465a688ffd093a332e332ab1480be0ecd5a5f0a055a3dc9e0fa4a52ba4f0873fb198dfc52cb41bf1a8f3f6d347c180f771aee022b40cda9c73031790ab00ab7bf1d0bbdbedb61f4a27dda9c85fcd0afa713b0212e2b196e0bde5fcf514e0d138454a013271d4c66d50cd667d84b3aac0a0f19a89791928b72d993889062027651830cc0ee73b66b2fc8412971bcd8526516e91a98f074751b4242f671cb0056bdac1a1f1a9ddff530fd64c0a6233d64d0a653429f8c8aecf5b9bd9a44dd0e0733dae17249156c807c4e2dd51e3a8420326f21aa3800771d17a2056f00ec077c31de1c042bd211f2644c13c99511e9bc69392a957a5050fd77964a16363d043cd6ef44a9671393fa92b1d76bc3d40f21e97c61fb0c4e72689bb851080f34038cd58bc6619baaf83a065b862f00dbf3c72d325b0afd5f8ddd8a39afee82b609bf5f05db59274f9ab1c5aff5947dc3b51e34ed887b9d5a041737abeade61ef09c53d9930cff89c0bc7cd9b5e906301b202e3a673154c304e815ce5978a0d8708375ff7afbd2d701c5263c1a5846555068365ef6f94a5f3139127dca4e2be4e07ddd79d39c284121bbf16bca6293e599e6e69d55d5875fbbd43b21305fb9bf3079341ed2ff2b3dc016c03bba974aeac13f7228644c22d9a2aef770e970fa4d8095616a10dbfac270aaaeac83f97d6b308d219734c3ad04b10eb9ad8a4d851e4081248c0a86ef37b67d85edbc49e7509a50837d0b84c343ab40c054588638b36012dcee28bd590dda8c16d6d71c47692734060613d5b3bc36519712ba6b5b4e103006cfd50b915a21fa4d7b2d2b985d6a784f0582811404c5787d973a7781823072e10f2e9aba22e1562bfc857058798fba5f40095d309676364df2d447e3ed000437340e08e386248ba453305fa80dafaf7acf3c23ee73b6b77c2521dd93c8c00fcfc062870372402fdd6351b4736f639326424b1b0938dc5d1e97bc54cc251063daaa0aca2b7f558f2f253f1539afed577c2509ac915e1b246f719db8180e005ccf3b776438f58b6dffaae118a75007a71b0f1de2c9b4f4ad68dccdb4a4f3105ba2eef1803054a51db0e392e417b3b7db47a2099bdf236643874dbd5ee63fc03f8b781b3a20ce00cf7a1a562009f5a569bb4d5fa3bcb2460a064171398750307377d336e13d0c93f825a5e4414919f2fe4125c5bbac6d997a0e038934758610df6291640065f3b8033e1bf9bf2f0bf3c9f707ae66a0000e6442d34be8c9ec00f3a5ca253c4330a94f04f0143e258a1500087e34f5204a045aa7621d0679be40186467f9fc482a4c6cfd2f8456f07fe4c1c0ca0f3e1c152291fa04124eec99509748b49a2d79ef5e48a7854f4c38eb32a8a1e660ab940d28e3a36cc975946620d24819f62ef60b3b03c1bad636aa9ffbe768bc4f6a1851b1dc71088cbd3d0680f8156cb8d8db19636e8198fb3db09fd5865135291789821d9c597df50058a280e27b4f247d5223da1af6f22bb9473985c25959f70d1a7620a83a55d1be0a1510263b0018e49e1572b525c511b9625047641550f1d6708de48b494122cd985a801d02be83d11f6b0c07db60026bf7676e128dcf38f96871ff7398f7a4cdd571705d155407381d6b26a89ddaaa637e496bba4912617f922d2fed2651243ace86301573d3c08386a50d438c866385c7d677a2f4ed66bdba400b69832dcb27925470644681216ce55cf9b4f1f532b96b2f7072c43a7d046456a53d8c464ca51642303b3591ac3a05d29c5760d8428a553de38a73b843f77cfd0c3df5d563a816c040bc1096ba30834cf6fa8816f4acc657f776b5b3c890098bf5f53ad3e9e99509201 true +check_ring_signature b6e1a49694509ec43f5ee81a2a6c8a14257d0791888011af37af052b19873296 c23464ae35e37886b8d1a6c4c11c1cf33cc0e86b9484c614e54083cf2a83fdc5 3 9fbe5adb0170ecf9c1e368b300138a24514b52376119d82139e325b6a4701b26 83ba30e9752b2e2c949e05ad792a946edce11460261d1174a6ec5f8f3184e77c 182d50c4b5ed74e1fc8ba43b2bac9ecf11c55297d90b07b6d5fc4881cdf3eb94 d314a599a54132b77ccf98545643c84a87d87b828064658c743f9df03890eb0459eade165414f2952447cdfcdcad4aa16748c5a2ac5fe9b1d07910785cefa80e4114f62b19f92eaeb26b98588df7e102146802e5987a63a87069cf5e25a5ac0e51f6fdf942bfb0f8e1779a2d391b554b850d0ac3032b3f70cd5546fb65cc8e019188822b32c87e87658e5ed31eaae030e616373adee7113f370376b7efdb2207dfa96ff39a7b11d746f9229266e689bba00b63a76f316379daebaac584a0d80f false +check_ring_signature d06f6d3d5830dc8ed595ded7a798cfef182f4a5df1e33af0c78b9324101f984f c5acd00e6f9c7529a32d121885e2058905da52644c22a639370323361c7a97fe 4 590572ed25c65254c30c2288c98007f69ec94a041a818b95a377c83dd20b71b4 7bc803980fd2c380358abeb4b3a9e3473ed32d3f8075a1175f769897bb2a246b 711e6069a97ad1adae8b759ef512007f79c06f14e107a98cdc1dfe6b4ae532cb 964e198fe13cc08d194cba26493eb49e95a59d18617b5d9b61acbbc6872a3067 dd9e9aee560eaebddc01c32fefadc9ce567f20aa8d22b8deaf3961ae6d336c01790d15a1f9fe68851a192607d23189da08fd8dc6cab1b261b445f1e0590dd207d7c525e24574326d8d02a872b112b959027e3f07f6a6ac9fcccd7a3664e82a01d4368760648bcba246aa84ad0698f6d28e94b646b4cb34fc0978c0c9f9355d0ad349a8905d517f4646c554b60f7cf89ede068c1a1e4d0b9a8c93132a11be4907d8859a1e8ce649bf67f7826fb28717548b3205c410c8a0ae25cd891405d4430a44e94af6f79d97bdcfea0eebada6cfc5550e10c9f1ddc0a6c6eb3255c3557109a10d52a114b9c4dad4a3b8ab4ed2890006ae37c6a215c917c433fc70366b1e0d false +check_ring_signature 7f4ee90c4068b1900c6ab78857b636c37a32c420f90b43fbfe92988f9a56c8cb 39e8c17a5ba85cd7b401985c898aeae69b11cde1dacae2addbb706a015045a51 6 ea3eaedadeec8965c9009af2c4c68bea6d7f2d0257cef81147220570e47279ad e5e5589b02e2b5586e9f64707b0b44e2119484a42d2367baef0de73767b0766c 046dbe65b0e47827989db77759c86a5eb794cb5d40d54136929ccf593fd040b1 18b673f7b7d719bd59e06b21f12714ddaec40b9d7bdaad0d121381990926f2e6 638c2e82b3536477e8e67388fa7127e0018bacdb7847ea08c9887c7d88351015 4760566cd327a59e0b15c7830de6034e52a4dc0183d754fcf3304de76c596a79 4dcf11ac59fe73ba16d5e31d606dd4381b968662964f64efe516f4da7d43cb0924fd83086ed9ffe179ee44b7894633c2c028589a892f0b12bc85a2f9cbd67d000c60ad1c6768ec6fa84e867f3a17bf3cc51d57b292ac76750fd8b8ab73c7d10db0011c423afcd0c95d754f9a8ff8d24e8dfd8a82406a43a127a7a1fdecf19a0b9322ed5c2ef8f01cc483d75f57b2fe29f4d4f69c56c481efe195d932b460bc0586dc64e0c2a752d01b4c6f517851c9b9ae38e2caa434ca5a1a24ebb9a6326708f218d8c4efad1c06874b0b2ec67814d1aaf9daa15c3a16a2ec16edf64f5bb508127b811688fb0c46c75ba6ba4623d5bd779d8726d253307853948f2cf584510dbabea76451618726a265703e185311621050612ac3fe5674360b5c28ee2db509632e353b95ddae12a3242266857ab88d0cf03a7f0e0e2cd819508e3d884dd5027a0a3cce9e694e0e0844573105db473847cf4fd30e2cfc1a27b13bf0796c76056fe0610819f0dca7daa21bf5a39f2e589e7a3f5359dcd1ad75cb065fc587560a true +check_ring_signature 8681e0fac647db5c1e3ac796cc8deded798607b6c5248289424bd1b1a97dc526 060c1de325b2b199c06da172ab6c2139d896f7dac52f1d2084a6533af2e83d4f 6 a66ef536e61f407a4db5cc8b5751250eec3b118f6aed800a8377dadcdf4e69f1 a7d6cb372b3726d79379794aa412c4bb84cca5d616ac82b112fdbb3b7d2ed3ab c23b4f61b39c443230f39b12ce0761e9a01344c57377fdb6fd4f0d5917c0e2c5 bd98af99ff7cf6d183366839e527519f1b3cf5739688ff9161c1ce63d3bec112 8f1d35ed98c12e891a5fff7b9d3c9dcdc8f08df7166b10ec5b05323f5ef652ec d722acf9e9b8838e7a6969f16df4271edc8f6bc38270d6baee494aa7aa76a5a9 26649556d4d9bacde71b67da1a052ee4a3372511049e70eddf5c7cf74fb4c306753ca482b4f7ef89f3a470d51d2d9d2dd8868eb25d500bbcc7fda9ae98933e02c55d3a475e0f8009820ca90704af34d07b57146f391710b7322a5660324648095f01716b7fd4d0512541405c0a8809463fbc7a725c1d1bff223ac4aca6e886273f3365a391597aa72dea44f35b9d91114c9e5c57f53bbbaed52cd00fc475010fad27be1fc801065784c3eec8a243107220ae63989eca7ab996e2635edb616205fdd109940f81d0b51db5fe20c61b0dd90f4c82a4d4bb0346805651e629c35007b2f951f1476412d066e550b3e7ab56bfd4be2fba79b10acf7a7ea96b1b5ea701a5b2c6214c78e1fd5c1c6c38fb3dd1de8a49174fc86f6332210f656a98249d051c7cf8bd67b29e233de22cac19bd3c7482c46f43d7de10dc35d0044d4b45cf0091debdf8eaa02d785ff2667f64d4d3df9abcde78234e34b0538f1893cb09cc0035866b7a48c2760f74320d67fa65381c94d424f89d4dd4c57470ddf02798c804 false +check_ring_signature 088f3b0f51b207af72b67a3d958c3fdcc12fe664fa90aad98026fad6fa637b0b 11165b01fe6a27c608e4bfbd4da761bcd1735b76487e899fda7aefab3cb0f3dd 1 62260f73eb9fcf6b6fd361e458773699d41b3c9a3f97fa2cdb77c17f6cc9ab22 005777059cc6991ad5620a7bdb5a57a3eff4f666a03ac2f8d6ac8850af7f52073d4aa0b04e0f2c91cccde1267a79a1bb649811a253c0546c00be7b8b5d6cb806 false +check_ring_signature 32bd576f908dc4f2f76bf593b2f6f064884064d8b27d5da91f8467af39bb0ab2 9b4c59ff354dfd9b447d0c90cc15850c47298f1f94edaa015e03145e609f417f 57 f22a0205cde344b014019c4468299f71a9a54691e8c1b13b54530b26dbd381e0 55d08f676131bf0e5118a50faec5239792b0155f3dcf91ae6b89daf110175864 b91c9c0de3b28d3988878137ba5765df9cfc685cd32d8dec929b19426aee73d6 a9e120ec13993e65b45d6c2a0c2e3eda2831d06a9199a6806888dcbd2103318e 825850f01d19877e6779be9faace21e0c24eae2a45c4b8c54d6b457e8d811b3a ab8144100bc3627899db8a0c60b492bbda0c65cd1586ac8065013047008c7ef4 4a555a6115186465d13bbf105f209742008f8d190923d1e2b5ccce15154fbddb 495d05dab5684084d7d055c424fa96958e5a8521a1e5da9943fc2bd77a0dc3b1 22d77ed9cde8867e3f9369717c364aa8f468ba3217a7163534b923fdde3e4661 2963d30c527a6684695febdbce1a15a5337a1ceb70ae039416ea321a55ea93e8 9af1843d8710f196ba813a507bbc5192135855398ef22c6b26f1b304b43d9e07 51dde90667aa5213529f041c27c6e07caaaf2dbe4df04dcd444287632e35cde2 e17179b7ad1bf7da966412fdfa4af1550182f54961e58212066241e4d799bb8b 4d2c6ccfad4cab3ae928b883e588e4a0bdb6351620ac5302d3629729d1e14faf c24cdc0ede1db0c300bb3b190fc850a4006652431e8730506555ac71aac2aa44 01ab2499e40b7745b28de7e43229bc089d97b03db0132b32be72aeaba17dac91 86e301c954212a90bcb1db8b5869153b6bdbc129272777cf722999fdd90d0883 948e6e00afc04d0cb2a99d761f52a844fd7c0c3a50e7aac6b404f73f0e00da15 159ac6f2ba0c0184ce34740cffae8e8d77e1d2887639d4cb0ca2a63301f85b31 3a3797b9485e019f4f4680131081bee94bc57299b8813f437e0b025e6be1ca1e 2b386fbae51d413a92af5ca09b0ef55020623cdd0eaa593300aa893d16978ac3 e60f0d63e288f23125ddfdc56f7b0013bd62f6fed721cced1ddb913bd0788050 4fdd29200827f8602d174db85cda25a033e56e347a042094ba55dda592649759 b3cf434ad9f10ef23974a65d333684d7f72e93997388e7461aa64029056f2044 003873805056171bfba9f6d96c398408502031fb5f02ce61e9f2f770c30dc478 c676b748f62316abbc076723a52157e14854aa50a60d70606c96dc4023ffb65b 702a8cf205202d207d9d9813c451cfd7f0e183b35fd6be145c4279dd06e4fe82 9ae36634d9bd9b1451ec49b7f4401803b38b47e110e40ccd16cfb4f5b9e791c4 f6c0151d2a15d4238ddb93a2347445594830cc9825e794f8e46e034052ba963a 1ff77603819f62c8c19270b0c28b4064602259bc3bbdb2af788b6503e5f63c77 7fff810192d21a0ce8fa74fdcfb4f3fa981ef3ff995a07658eb07d9ef1c12ab0 67083568e37765b1a4d42bc0150c902a8ea454137794019fea584e1791e5fdd6 409fe351038f1fd063a47a8c0f1d5ce9f9405ac9d09a7daf4a55b89007053c22 e46213f7cdb6b64db5791c041f3edb6a66b252179652d21e991412bc25cac645 b9172aa2a9d76399290b6c549060ef01f19d174102d3e35eba902251fa2e6390 6334ed7c075ef0f7cef890798abc07342a47629fde3f2a35f31beb6006a57941 7d1b87c6c23042a7f19b3740c4cf733be3d6fc5bf34b939c01ff8f8bff59803a a7e4cae8b945b73fea3e9562ce14cf5e5f4e936ba9906f55fe43dad204549b35 bbe4fac3ee855280071afdecfab65fa38d0f1fd84e6d1cab385a3d1f162fa969 cffd3e41b9bf747bbca6d7677d7856627f0dbb6be13d68e9643be7e813ac27e4 aa26cfdff4ccd2a8c731eac2ab1bd6ad637c553291a65ba1a02f40f0d0554d16 ff60e0a966d982ac4315e27fc0bde3177bd3af472912dd831bcf52359aed4349 695e0129815b35f3df4aac3bf078354a121ebaff147a8038083fd1a43ae1b14a ef81811b4355bebe2fd330ddd9a2075a72de12f2e637f1898b2bd312a083e56c 42bd75135d922bbb3cf6bdf4189cf81ceb1e65a1e301033e57c89b9c7b136612 294705b2c92540dfb1662d032a7f59cdbfc649e14ac491584ae828886dc813bd 7c4b81a34ed234af2f78f0d4eb60e4a4cded9c8b6ecb4f47279bd30d49d570f3 6690570e1405576051663ae92e53861b649da741151560dc60d506cdc021c872 cffa42ae877ccdd14121f8c0c296bd96bbec78a4e2724ff486cc0e093baec134 8403a851e6e58238f8357e8f7dacb66afed889db300a7ea7eb45c84822cbf671 e2b0705da64029cd465b2d892fffa117a514b49769412496b5c60d79c439e3f3 14d642d1fa65e2e1b5944328cd6921e1dbea5b13ae064019b9819c59199c29bd 1c39c35020baa9377e001cb8665fe1c3b8f2f429bd033c742cbd8bdcb78138b7 a5eb1b6327dd71568cc70f6c320133d2d04a36f76b81736ba3813b8819e98422 28dfe7079590583f53285a05417ddfd8a076500740c9952b23e4fe5b3b078bc9 0e7fc708c986506e26123f07da9b4b8f2ec45156930b65330891e2e5fb005a17 70904e5aa5c2defa0e5fb9438b653c59f8471cbae1b91dbdf859dcb37471477c abbe23680a0732975aa555656fb9c872f6b3ff2c6bf0fa696c22568e7276360bb3d0919958bc14a2f8b95ee8c75a4ae3f1f50ee20331e88737e601373fee1608319bdf948433e1128cb5251312f35ae3cf4bc16cb18fcc29cbcb107a84d5000fb8b3153bfac44ad6351cdc6ac0dcbe0d8dda77d22aa6689f1efc75ae8969eb07c5d35b3b83660ea9f9cc712b308b6da2a873c2738e4fb440af9d866060bfe50b07f7dbba09a15c3a6fdb81255c94f9f93aa40498e1b7c7466a29291ab0ac6e0b64f022544a3f15d66b4bf49cd7538724a4b6b58e5e04ce48e5d9070309cec0020aad3145ff87434528b3a5c47132aaa8e21f05270c25cd64d2029b101ccd6206f2b4b8da93fe2b3d8d0f1b4b0fd72f8e7608f0a0caa62ca1608b6101bd36e00a6f473ccc7d481427399b0ae84030fc21995c6072b159ce4f942476bd5013330581293056c42f2f449256d6c7a79b8d05adffd0a27c19c5ea785e1e882460d208ec37f78b3993ffd598d5d4e0526e70d725b6bf825a450700ab6d75ad4749d30b1e4949cc6e7450bc7ff9b1d1d063c86e401c123906d8c33eadf4b86747c239072cddf13db642fcb091cbfd6b1aec83c22e85e0128efa1cfac8d7cf02896d2301eff066f3ce209404088d2765a4903e843adc3010016219c6a9b9e72c8f6ab30927b3feebb5249c92fa02db1686e3cf90ef76bc3ee35bc95052701e9e47e30808af77fab805125c411bf073f584caa158d8482bfa5884e69748965f8cfe529a0cdec5365861754399c779f46e7f5817034c05340ca1838e07887cd955f3d8ef078b2c2ff098a5d8d76faf9c869c16ef4c53f957bd01853afd947495e211d55c0089598bf43819601e6f71c4c4876ea5d9eb2f7e3d026160c28508c50732492c0c4fac86cc5b50cf096d1a3c27b1874de64d345465f80aeb1b1756b588052e2008d220b1aff9e206ce555737f81aec2606bfa63d1b039a0f62f1f0bdf9cc92c302a4d74a1f86df91bd0cb70bb6f62cbde1b40e3ed10ec146fa06b4cb4d6b89ad06845684a0cd64fd29dbb6ba6f98d6386052031005adf787b8b87ff0b73ccca701560631a882d62e9ecd804bdce6ecbd2cf3393f260bee23d721b286a941788805997df15a3177f1723b9502ee7b09addccb79ff95182d81dda42e56fac64ed00494919e77cb9c6bd2d2dd6ba242c01cf699dbe74e02634b9b0a7c601215b9cf0e07f426e6a3ccb7bf1b109b0cbbb7b9438e33a2f257462317d7a4dab3ef7bcc06c2794905730cb783bdf8e824b0127ddfd264cbbee8073e0a8d0c2092f5b37408a610037c61b49c26e0d9c98d6d021f62edf0eec5bc4764ac122dc67dcca5f307a3b1e6ec166b2865b394c53bd04b802be96b71dafb6baf689b12ae66c6e14d0c902a5e8e02c6777dcfb7211c69c6692fe0e9ed624025067879554bba07f0fb0c6fb06dacf2313f77554ccfca87f043cec436b1e784854f5c56841a4588d2e10cbbac24da4ac0ea655e485f909468e7330706b84fad70967989b2c9637e09c307890a32bd7d3200405eb46f18a3e498dc3b6b25b9d445b8a3199555e18a83e30941aae6f57982276e7b43c7617421b62da68ab620006a6f7697722697fa5cf90f8eaee57515de5df28ec76306be1b0f1bbb47a7d586e588ccdb66cc134b55bd0417dce9180b293e61722b7e6f1aeccb4f62e2cfc5e7a5b4ac8895c39cb05cdc0a86d594d0ba97dede53f9346f2421545d2dab58f50e59e32d995e5368b652300d6cd20dc3e8412d1ac66dd698242dca807fcf399fa9bacc19582a0f312581c807f9e09d2860970cdde2dedaa22c0f3b5e85c40b7131dbd5c3eeca4c5d709d8c0d38ca78df6819e431e3ffca3d2fe46e7aef22cf5a411d5373cdf5ebd5349124031cb1d386cdc9e0458a3f57071f547c8d76ed1205e8bf28017579bcd01838860845e10df190407ad8fc23acc539a162df4b82817e8a3aba70cbe24727f01cd708a3e77411678fe4b0b4c49a745261e1f058f7e47721cf8559cc4bef64b2765e0f2f528a1561fec6ffb9c05f6c7a0e0b53933b3140970b5923f75c4e2e24a6bf04f77be2a4810ac10c45d02302b5da13c7c7e7f9c7142adc5e7f2d0a0e565fb50a7eb7ace26a341c45e9e1d21520129cdc00aadef42c12cc52ebf8571a158265005647cae6487a319f0f852b818561cd50b197204509f8af2ddb9719f419d8fc0472eeeb7a72bb561190bb40ca8d0105415fb8edefdd0adfeae1e7aae5d713c408a0b569230603d26725056633f7b20fca4a1460406a9934756b0fce67c902ae06db670888078791f2b37f19a78ad2afe4f7f961e2cbb64a5efef71a59a35a650344fb30b2c9380eb06b263ee0fdb35272effb1b2ebad7a1a792971b95f01c1d03784c60aa1212f6d1f34170c2afa03370a3d312193ebfc5acb2b6638a614b4501f478b56a4e71ca370b9a5969cb4d7839ced6fa5e7e5b4a5c101c2833cbcba808b7c04dbecb4bdacd2edaa260fb2273efbe100eae7b395a085f26ac4de0779a00888b47113113bf4ce958e405e9044800fb37ca5e7cae949c763e3cfbaded4f089e734067270e49fa8a9c3fb2488381dd789e367bb2412c6dbd8e018349965805cc1a29e9cba8ba64f448ae17c82a976e8f63875f499a7b7e0022ad10eea6b5048180bbf773be87ca236a6b7598aeaf17f1244ab905f8a6dde31c58f20bc60c0fe74453c41808e925359b3aa251f8d4dce8bf96f53295e1a4459dbd19c1c6a50ee2abab76c511e9c1127e112681aa8ea78ae389bccda3ec6e179e08778dc58e094d1886d612885942065966cdb0111369157c22bcb3f6c1ededd84b664d183c0ef22011b60459136116804908516264b5779497d02da18aaafe0f3ecdf60d6605ac1e9617832726e24990ec0fe459ead93149da04684ec0444cadbfd6c28dd6046e19f4767fb09b65c709710dde964136faf8b188c89b07fe5c86a9865af5a10567ede93fe501476a942824837e2a87869460feceadafd769c730482910cece0f47a4d19067424aaa0d446fe9c81069582b4e2947eaa1e6d123a19ebe7c6b7603f32633c9039d72066f87505093f5f02d83a711e0152302eb75986a0df2cc6a0a3409db44179715eb704b6197b38cf8d4f366bafca794abf21ed44e655cca6006447338390d8eadfea4ba021c9769538a4c8ed52caa253beaa9a468b70f581d00dc8e2091fdfd7521f5a557a9b69d0ace1a2500c55466551621d34fee169d270c3ef908e01c7d0df31bd47444c74fbd0886c32e3adc1c32aeda519c6b01b51f0fc2ad07134de2c04f264921d0b95cf43f1af4060b3707c88c55741b17c7de800c6081e4d5e81e633891104ab1e6e4c7c1d96eb7c85dca18f119ceaf251bca4e0cc572cf0e1719181988fdfb9465aa9962aa9d4350b251f50460067e28e11ceb091d8c77ccc2fdbb52b328597b1485c1bdd8783c5e9c37bbe16f216e9bcf0f5300e725e64496cfaa18abc5ccaeace42072548b71339d230292a80449828aeb5d07bdae2beefc949872f488f1f05271f8523ace582273cdbeb35d3a742120bda308d84d2fee8a02d459396f59be37cea87f6b2c0b973772e228aa41dc93764bf80e04defa2fdfa0479b81ef4fdbe6d9d5f90c8f96910cb616db97de8ed13660bf0013bd8458f28110e42d94f9cf00139aa4fe8047bbfb4ea9e013d968b23e15ae033fcbec11dc95ffa668ff1f6e6857ca0c7ed84de52dd6247338b92e72a29d720a8213a21c0398095a18cebbe0bf58c8ef592c43de2b57c52f698fd69f45eadb0f4904b2f10669b56f1273cc1f9f9cc171089eb06f3842abe65fedb9841eca550b9b00abd5859168df1d61e537ff31e314a17064dfe427088078d4732f3f40d80b3cf54a76e0a2e2c0a532d4c683defa07d5fd66feec046e0c35958cb250810f081ade789f0183b5cc1d781dc2c9f54b5a8d086a708dd4879fc1f741e4b904a20fd82fc9986e9b4620e84bd39a98d27fbd784d710c48469939e198b0a49ece0106439e18e99ad9fe3e00a8931a344a78e41d244f0f661d5f8a7c39faffb92d0d01af74f7b79d59642680b6757d90ed5499acda3ad74d701c02a529f1768ca6d90e936113d09db48c1ba4cfe3a8d39886a97ebc360eec195a020be0e307c8bd6e0ceb299393920dd844994dda877c2941e7a939b7cf996bf22573252f5b0605a20f693aaa92251b3d1d4384d239bb0db802814e0cd3dd5e876141268159ec6ad109e1465d15c504ae9657dbbce08f2a7f2c1ce67257dc720331c0e4b34bc2d46d0761b22715ab91bcdc51686fd4a0e957db2bb9798a7af7eceb990352ce93ba390c2d0c28f70d895783f5e33711c6fad7d32855fbe557f7b83495233013b9d2be085d847eee76800d0b6e2e53327cfeabb4e7f36bde0ba994923350e8c527003c030b387493d7f3bbf2bf236babb917ab89f2d46eebcf76f744f22412516c73230953458cc25e1ec2dcc885fc40da120f5fa41cff803267fe6b6dfa5df343e2a109a4dc4cd3fd1281d99f2f01ff965ee820a8e197f7bd3d80876e762d93b1ccce0f2c1951457176f1773d13739e74f9992d6cf9d9c5f053ceb01075f6a999520a079c39c972b4c06e0732e54ccaef39d4f36369784a37558541c6e14a2949881509ebf25e04910917c325a7352fbd14971d7f94b2d969272b7896f0548d39ebd3007e49480640568fbd7e759c8254c8d8724b98ca18f2c441f3da612756a4626307addf250a6c5b8999c46a6025a425e7065d21384c59cdbb2f7dd315809e097d01b9984b5b0f404fef91adbcda4ca52aa2599cfcfcf09a4c57e826be5937648c044ada0a7e76abfa45405f1e119ca7ab4efec2ece1cc2d37eb9fd41639c4783906cbdda39a867a8dd5276dd81512050cc4629f4d38ee7285526ff562603152830a99e6ac58a4b934bdb046498378e9107f0d786cfd5c325d6298618a5e92abe5009d45c59c713d6841213ce42766f7dbb81b29a6177b73efa2c624f1ad3836b50883273f63dc35e466a25254ec512a4a666872f02145d8b3157b62961cd8ae020d8ae10c6864b02053fa8896380f643df057978cf742373c3344829858e369b1022eca60b0710e723094c2188c5245609083a80f7c4b0aeb3c1f9f279abac5ed00 false +check_ring_signature 9a9f31d3bf0e0d802bb1195f9eb56ccb5ae17233ad1c38833e8fd1790267cad8 83b5fe701b99f56651505925f52f375094bef468ab62fb144b9d80aeeadc86e7 1 6715b41f6d0cbad7c8c701156b508e6f0181a54b131a95c1e43428baaab5b6a3 cc2b7c4d7004aa45a755a1f7d4e5091adb167774a2e3382e377f5548143f7e0e6970a9a2cb3a6f54f4b5cfe8807525ec6273176928cbc5d1405279aeafad170d false +check_ring_signature 828dc299ef3717ed50efc20eaa8d8c1222dffbaa5c4bfc52a5b0bf6550c1c1be 226d3f9b6de51e6714d3178e03049f2191db204c4a7de9a93a014fd2dbca6cd9 1 f322577c4c5bd182425fb548b538eee7c60806ed58752674323718d5d3e53673 30ad9c1656a9f3a4f6852029bd623b245519e88f08e1b00860e2f44434fbfb443987b9cef2308cc786b34c9d3c52c2c4a962b673091b8b4358a2924781c4be09 false +check_ring_signature daf0b885d7848dc8d0637151759eb3838f791194658726e4fc098ff3dc0d9335 14289ef16f67118c9b09b48b06ae92b99a3d083cbac1470a4d479c268582ccb7 89 0d3a4db0edf0bce87228d96f1b607af341b9c6429c6ee33dbb2c4dd8090f48f7 bf6305a24bf4e9ca68ceb6b51f550846edb1e20251a3dc1e481758367c2c5076 40e9bd2359f527792f5a723cfae5828366aa0836a0fab75fe982fffd36a0ba7e 758ae424f97f3970eab32936361f9ae3c082226ea298e51558b05570b2603a24 17b567c262b98b6abbdbc3a6da2c74387ba5e31b19226bfaec16c70ef18abe2c 6eff91fb749fc8546d5519d44d4d52799f798eda9d3e17de7b018b3e74e97298 214baf8902931cd5bc6c3ae2e128ea7cdf69bb2cf08790d9054690ffae1c5f07 8c786f9707502230b16023b04320d518dbbd6d6e39802c64be455a3159a24dd7 5cd8194dbc09ecc9dbd90bc3418482ff2b66960d48a9c25f30ad63b490f04510 6f98b1ad1f064229413421716208d78d38fe976e6413d77d76af515cdbec6a39 09a763f30135043eda022c3eb6878e12f1ac61ccf21c1785bce5490381b65e48 4f758eaf5b8123a0c247b1d43d70eacc129ff95affe7aaa1e0a3ec5330642126 8618013cf69955aa410802c8003b1662c461e7d89cca76092fb2b53123ebd565 230ec664b07d03d44430db870b0dca2fb82215a0236a4f7ebe683f7308e19b45 a514fd3d6d7620a19f68c86ce822c1358acdf65fc8b4b1ce5230952e7131b323 7326d6bef20cb1bff4af9b12ca24098998a6112f8f1e077c774ee2d43deadfc6 e61ec9cd4bdc52c36507c864ff862b02bebbb1eabc3e14cca64c05f404c1571f 369011b9321393b3f529ae965d78d91e9f92e8b566b92a64926f92d4a946e583 259cdd5ed7d6fa778a971041fd190eda2b41fc1eadc373b5de1ef20e4b0b8bbb 148f0360b0609766f286f2f74b552275d84b63dff0dfd3b9f4f7a1b113722892 6fba85aa11fc4733adcc1c21f9f79f621d56c5479eb112783b57ddd367974eb5 c559ecc038f70c772996fe04f2588e7c832d0e1facb6e789b67dbc364178e543 72d1c513b62150c531fe8d8000cc868cba03dfe4363e5e8fddc7be448349f7df 25079974462b0bbd74a42e86e295470f0db3d424fcb9b29ae5df782e0b88136d 64154c700acaedbe7fff0a8e43701222378eace2c59b6c8a5a27dd7940d672ac df658eb84300c2528653fa8e56b866d2ac9544a1b940816538e361356ca93d51 7ab9bdb09e6d7e3b9f7eb83b3ed0458cae3254cc10c53d4de400729b3be92c53 a988aa631bf1b99acdf2d2d46ee3d18828f5d1fbc37e5864e974682b64856b3b da49c59dd6d92533255f36c91a58b3694c84c92eaef74ffabd393f1f9713745c cc4b5b78e6bccc44e77e6ce9488534116e0feaec5bed0b17000a0ff29d2160f3 4c320d456a70dd27045eb3832b294acf15aa773015a7934eb251732bf4f4129d 06933289354333c2e5cfa3bd58e8b8e6adacf053c4930cd68ff9717730aa008b 0c733f758d2b21e8ea19777d773c871ebe88795fdde115833de9142c798c0bbd cb7b54a3ab3f781800a263b7e4562bf7c66aa12af3c9f991e28d2b56edc7cf42 2365a7098ffa29e8c7603b17dc43d38130b44c9116788179c8d534bb0de55187 40f058747a136eee63345575959fb21b3331a313e0dec0c16a4f81ace4fbc201 7fcfcd63dd6564b926eaf891491636c1bdbb5f2c7018d39f2983bc06d340ae92 64507d33ee80a07adfc12385abb43c97917013518b3a44746d746cbb93bcc245 25ab0970fe4703cafc0b41d11a3f833127e613a9077217de757ee8b808efb6c8 ec9717cece3d4c9fdc664b0ed8e6fdf4db0ca8d8d7a6c3293b08d8be023ab968 4724de7ae4f2ada09fa344ee3637129703ed769f739ca2a2cd962cbbbfcb51be 067f3caa83ef725ec0804c94bef9f30736ebf7e3defd50ad9ced1477760c44de 60ece69607723bad009c822cd25a321b95ef4640aedc3065fce38d59a5243169 3fdbd39c0ed721133f9a5e97420d57e97203f6f8f7c536d4d4c95485e6dc37ed 77522eac6ca74c36c568b7e0d85e84f41997ff29a7034167b3af9e17c7c087cb 1f94908bf94a60681e6916ddafbd0072331082fecd5824b33213f8a32242d7c0 353fe70aa4d0319ba3c93bb1d5baa81ab42ee7a73ae90d082e2b06db934dddf4 f8db800ef9c63ede1d0ec702a2dddc18254f82a8426e5aec486bfe12d0ee8870 20a2b0c158fe2ccf6205cf71293d03e4fe792a6b8e4a26bc412e67f7d1516301 f3f83ffbb9766b8826e264be93d361cabfadcbb44682a6355dce5781f78e9bcc b98e9b11110dc27a0a94fa0dae821d959b88add1c02bcf1902cbe0234c538661 a5ac6b8d19cc9265aca00701dfaed08034081b18d6394ebc20fc6b2c39091ad5 4fa63b5d7caa0982ca177236e3f05f8e5bd49e0cf91abd7489a5f11aeac93922 e5b17ccde9f49661c54c204abcb002efe26285c1edcd79ec442e03f866012c04 d4f98fa6101d897166b59eeb469f81c3ffc2871be79a1f76430340e123ed5568 7d06ed62875d0333d74cea4287149d29a2479ef488b35ca7e9843dcbd80b9e12 c41abc6e0654f5a48d0cf914caa36fec86952afe7b531fbecb1a2cfaf102fc7a 80bed2dbc654ad74a3c5ef6bf281476c8b2a25eac7b4de30354eed7af23486a7 699aa78cc91813d43428f91ad1adf6b8ace5231ad14862eea82e3010ddb1a606 90b25827b555570a1935b3d72717a0280fff8c1b9779a4dcbd3fbadc483d9953 60d7f076264295140000f45f698833b220eb591b3884b13f487f2f6831c63a77 236ef30aef7da4f2bbd1ed3d29216211d37719920f59f6895a69cef96fd70420 11695d1dc7033eacc38e6773caa3d3ee1cab92f1ead36c49f0f3a372ee1a511f daee9f7e7a8e7ae0cf67459a72cc3ef3286235f789df96626ff9e3604e1a2667 555587a58a8505d7b2e818db397b17f0ba9e170a2f0b14d5327cf11bc6c0a13d 2e56e6801102bcbe1bb15278a6e444989a28cf44fa0374eeaba6da981d262808 bcc4305f35ec9f2b74eeda18a1538a47274b5a96095be4ceafd0e798e2c09916 1df32b4e94ed35e0ca5a8dbbfc5a166c4f49c50f799de167863792d734b46c75 1fc552cdf577616afd5c525df24ab777a52256589bcdba19acac02fa7b6a73b3 c3cbf0f9a00c97c4d4e978b787c9a4a1b1c7fdf75a75669073f654627319aa35 26f49839b432c4bf13285d41bee8cda26535b0ba54a67af327fa1fe38862edad 141c2ec5f6976314c9360ccc513a78442bffd7cb7113a5c6a22d503944fde906 e3105dcd54bc7b9917624939421fe9e1d17753c72de4b79fddb780093f4d161d f8d1190e076aa274890c3e7574c97c509478b199694a14094248b5a7e21c4156 3c0c20c3957a65383c608b1c75b465a623917f822ddce72b2a19de5d18d059a8 6211a38a5944021ce50bae79d7839c6ad8e1202200f3ccc70353bb0fe7fa323c 41e119675327f3e62299cbfb9c148a2ebfe0eef8c01c568fb86066ca08667a99 ad0b8072b96a25d39eb1c612264c6421046de3cc5157b7cfbf31f1f1acbeb9bc 714aece48619400bbfcea1c3ee6e6f7088620093340060094ce543dc23eb3e58 f1ba31da10c26e69681848853910a60719f3079834d05e208814a8261421c2f2 a92472d0052ac3bc33b85b98b78fba5abe7ec1728e96a1d69884fcd7b5a4ffe1 5edfc688d2862fbc549fd3be94b050dabc0224b60201c6223292dfde6f227102 a7906b3af35b9f70ce3b26106237f7f8fedaff1e13dd469c173154cb0e66bfd4 f772ac1b47d66b72774b059036807e9a823e0672019df38a16a1d8eda3dcd001 6f490d6ff18087d1a26de1455a2a6be30b76fa777cb707e04ae83bd3ccfaeb9d dfe9cfa8239ab5877d65cfed0f96d70cb91f858ca76bda5ff46f6046fd67f548 b4f8049ed1441c561aa4d1795352611fd86408352cd3f57b53bb35488980d2bb d78c39e3958f1a0105f7cf404147f9b10d2ab4acb030162bd73d16b3f21fb2b7 68487c40df637657880947b8dba6a6f5587daf00a1d607c687d9b8fd94476a89 1d9f8f26215fcb43eccd91e89078e6b012a6faa4fb778ec57b9dca99630de70d3e711a160fa90f6539c4063b53094bfbde1feba971494d991523f7bacad65177bd3854db47b70e9eccdc6ac4183f7df4d1326cec0fe43bd493359b681cccda0c396ca02bed5d74ff57c19c208f0014f0989b571deec99651b396fbf5e447930f2b4b3a11e57fed2ab6e7a856eab6c97334562bc1039fe79698da2a388cc1940d4c7f6c668c43cef5ff6adecbd1b49113478596354f0a03add8c356926c1c900a8419a2ac1438528f2439a3b5d97df18caef01f62c7b904b13ecf44c28e5f3c0d683b0a9ea966a139fa99175e09177ce76f0da60c6d7c590411ff0701124d6902154a8e93a7af929e17778ca357c9d5356877448f6462573f681df28db2109a0a67f510bf63bd8638bd382c5325194ae02778b38ebd05b9c91f65ee22d1e6f504fead234f92de5ca2799241fcab02ad951f09e72fc8252eaec31dd349f216570df00bd9cebcb2c68a55da7e5d111b447405574e1e2387fbb99634ba27a4037405c82cc7eb83c51bf8c1cbc12041e4cdeec585083475b1e0c52d400dde7fe8630ec600118632df629741afc8c182281728a58242b85563fceddc4a171e9370030b8c70006d6e0803638c48f11d7ad4cc5f3fcf862962e4418ad1de240a84fb4f0f48fee11477b50edd9bda61c9ab3cd5fa2114d40f5c0146c521759ad4a2c72309cf399173f702e486da80f3a2ce6642a908a5637bb194e846ad416bac720e900aa1d59b3e6d2c0894f23d191d231b4c3ae28153474bdcf53009f7e81fc391bb0821eeb2baafd2c0a8ec90ddb1b09d61caf6a7b6fcf85ba3ebceeef0f17997c60acd9b3e358be0507f4f8da5602fa16acf7d6c6278ac351771c135c681b94d0f0133f2a0b16304b91dc350a8e81563619d11a5c50dd920bfa1d5227e2b54f7950905e39b3b55acd5164dd4ddab427fa72302273ad1c920020479b6e9bb3d2d4403e1a1270245366e7329d10d5973b1f9997936a8f3742bad94a94dd42c68fc5d0d405c70e651df220b97577b64617fb5089b601eec3449b5431cb3ae172bea750b58f009f30a5623e0db99d21365f6cd43a824bff04849fb81e3509ebe8c46380cb4b61b3c899571d2582eb2cfe078988bd769691b6e2b2f583b1c7e6528db7e0bdef6501459dc3fe88e8c54d5e574ac1d9e556ffa139cf01fd64bbdc2dea3dd00542ee2baf9482eca5db1842fcc2a1c7432d2e32dcf21eb65c604c4bb844b310402183f742f5fe0712d4ffb8b389a76eee5cdfa78110799f4ecce3e876340df0f87f75f1ff1588677949a25f6d1615499206204a1ac429448979e8ea566c59607bf84d7a003973e5f955c6086518ebc59818c4f28aef0c6e47254ab057ca2d508a3c23b0bcea8099c26d92e5158b2ec66ce8084cea3421e3ccabb1a2bb65d3d0e1e7373f504ddccd0b50df7128010bb57fda914e86063b5abdf25ae903733fa0f373a3d37c7e806c7b5f8e649a63961e4969c653d671748cc9ed8b3cb133d29051411b48fde1adfe3cefcb13a673baec56fa01eda0268ca8ce79826b2a51a520512a6ad539395171417436664ba678243d3207ef44fa1cde5ef6a1c27592d880de981d0e743c73cc3629db395226a952d58b6a7a2829872cd12d962e6c49ffe0e6cafb05e903eaac7cbac1105f6c2243954e0cf6a0e24cb5f127072962af7a7020131e017dc8ee0874603b8a8f53165d0e467d0a09542bb8d5bdd42305c69c90f1af52706ceadeedf81f3f62deaacb9883b5d38ce979a6506e6784e7ef75c970956c30967ef346e6d31b27d36d9d9fdaff08f1d910900e03b74baf41d4779700fd1e959a2d64fab295c4d402d330b25ca4019c099b527080cb65dab656e629c009c878ed17c984d0a186f939b1491c6fcc4a63b023d4ce96fd6a3c9aeb2bbbb0b091345b27960dc66de1759adece0f8969af564653c495b3ed00109ea2d6c030a3e321d2f2c44344f9570f151327bc993bb21f63ff4d4da5ec3de38e2b214a40f16ca9af68e5750564bc1eef747da2c6079972c6426f26b8eade78ef7052e010c9ec89b13954759807249c40bca4632e56b323cc6da92910de52a358300cc5b07699f3f3ea45f9f695cfe0ac373086f20fb42c068087a6bb977bf70deabedd009635e5873b377f2810f4ab4e0b3856b54c48f340e44bd46e9a9c31654297d290e2a3d94d44f930c613bbc4e6cc01c2e2814a551855af8ab9a56bc13d10debcf021d6794cb2e342528a5999347fbc7ac54c5ef8ab118fda470abb9342dcf977c0f51abf584098b11cc1ff764fe7e6229546e90696bbdc0481cf5f95c1aec0e060be3e6bb818b4c8bd0bcd9d49e6459ce705870290e61777f5addefe4b4701b5c0f6b6d02cd965f892ec8b41fc81963c9e2523445ceb5234a80275091e0021ea808fbb9fd57d4b8814b1971d103d4bfd836f9b9557836a86192f9a458f77e92db0de2a0208ad8d8d11617751ed2df9b6e62a4858e1a29e2a1f81fa99739347601097609ee0c4fa46bf09acd5766e605f376ff76fc6a66a362f4d2fbeaececce7209c55b76a57bbc4c418114b0faf074744923eb647c3a5cd5b628ff48865811620d8f7dcf28270f2f3f2c1ea380e4006d94ae958816013cfb66a4f90fa8939f2d0c45c14662dec5ae248c434b9a3a5c42b13991ecf46cd91cdd39eda303ebeae80494f919d5f37ecfcc92130f4a6ee52b06ac24fc5907603f0e923e7d2041d74e0a1e2967a76f08125c105d878fb41921ce2e30f709eda38f40ad054e2aee854705e6f383659b30ee7944a2a18a31caedda102706c7efec8a84e0169a2a2c2d6f016f6f6e9c6469c0094050d4d9de1419c6a5d4d41ae189b3fcc0b3a3f55b5e300f8263b65a83ba22020758de324c897b695d127f1928c2186cc156a286ee92d607e0c4d0ebbf75822bcda0673ce4e4fc5269fb1ac04d489bd81463f4d33dcf220fa755da474bbba4a2a96d92825134d5b221c897a49620a4b8d901d3541eb9a40a18fa5995e0e657dbe3edac92171c70aa6c98cc0b46886c38ff0aef28a2230a01215f3d21dc2051a083fd4a59dfe88f921f935b6b920f4bd98465ab3ae9c86608eaff917648b8753430258e43ee2174a036f51345506bdbdea923888d82923a05c8bd6f1ca523416036a5ebd1dc3625180ba57a7274bccb93e7e1fe0f0bbcdb063fbceb48e97509c4568859412692180b505ab6dada5928a2995cf83780953e0bc9ef2a526da7dbcd913ff2544b2aa4e50a1147c215c10a2388ec3b32f94e8f0ac53111912dc4d866fdebb76f50708f5b8b1bac2dec09858d4d664b01279e9c088194a3e2647ce001c51b5a2ca6805e9ca7fa9107cbf20361b17865335ce62f031ab5d9d010a8e507369533d0206f5d29416fb73c8d9dfd3f9b05c150e15d89067a20e6e5ac3525807d4475ac77540e902774b21dec142262849b4db2cd8f7507ae1d23d0b07fa4f89d4d2785fe9f32d64990d0c2589bdaf1ae0ba50fa7308e0b548cde9abb9b54db4271b3854701e34e7d708d76e28d5c46b5dcb9aed49ff00f5ac7c9c54253e8984bce0640f29d28f17ff1cb46347ebb12482f64d32e99e60f50f38d52bc0dbcb700d786e3d0b6c27bfa01048f107b5196f2ee71dc0223bb095afa4c3dec5875e77158f79ff08bbf39d1f045716546fa02556316d0de7f3102373b161d7df83f19d635758b5b90456b8316a845869fa3b3275a6d86ef9e7e065d402503c40432083a4f6a9bcc3d2cadc73a5ae1a6f43d42b414fd45528a710a52542dc48a93193ee395b93e71ed3ee999295ae1fd511bb09fca59f45c20880a606c2e019505580047ebfd0fe05bfddf7ee570ea62d33c69b508f056e953520f9d7649b68a1e0e98c17a5a8db9bf7ec65eeb368766c2cd787cfe1086d74a5d0a37eeebc0624c014f0e27ccac9e6f84e0da759204706a7c0810cf3f2f5323280955a95408bf84b3adabd88e6256e7d882e1c691ac62fc22367deee90708cd4a0f3ee810f449859edddd6effdef542f5d8b1049e1675bc06f9cca0e90b11307a0387c797f3923543ebb061e2ad5992cd5b0aeae45828b34daa2a3eb3bd6cb4780f7b25786318c94234e5a22b53d16590dda0eb9770248ad4addfcdfa7469b4ef0169a187dc91a594aa84b8de286578cb849cbdc0e342dd3c6707a6784b30cb210886bdc80737c3e37d26110fc30d6ed0a5b2b9b065dcbd33863b05b7a9fdf69c09b8b2ab431afd34465f4e91f31eee0b443455697a345dc67ef3e2a13e8914540600c33847babe137f62fa720ac122ec80c1552797eeb2e04516033aac14a3a10d6f1c53bedd48a62cbb8c209634f5e91778201654aa5cc995115395ce93035908ae288adf4dc3ef57ffbb51bb57e2573780d89eba6fa388ab6109016915452302838dbb861db9443e93703e65511528936bb608c264ff0b957c5c10c1ebef930897fd015ad4b4bf3c99e69fac82d3970f6ee160a568645167e968b1b5488577073f95d5ec36e34c17a0c8da2ad88c0d20a0dc0c91aace5e5974f0cdacf5cd410d66d916bce9492e80b834335827185bf21b22e813a5bc4be11d3041c8acf2fd072f5a7b00137a4ab93da2a52f4e17682ccc571d6cd7fe7c50063168adb86f53030454fad30d3f0e5490e6111acde931085607cd0e5a8701fbc43893a48d6d7f0a885b5e1efe2ec5311cf6832a6d04831cdafc6ed6cfdbf11bed47e83310e2da0facf536705a662385a5a321113742a995b810d977c96af974d07d3b36d0d3520894ae3ecb022047e3f28d404f122c5a90e66bc28e380b6ee5f985e53b21db990055fb4106058d9706084cd30ae1ae8c8d98b27ba098ec723cf0a531d3e838c702f2d73c6603b38cdc9e77aa06b462ea6683bc88e7b4b1cf8e9f008627d4f1c50d3d263fdd67b087d934bccfe1ec7eddf6c0ff12426eeeb31f9dc6e508d49f1303d2b279b1c0db64cb42747bbfa082eb42baf7da38bd142b2f7a9c5ad78eb3a602ea343f2dba9087db8b530853666631dc3c95e1ab1412c79b279ca888e081570be2ee14c3e55e03de840130656fb74302636d03caddbee11f997d34944aeadf07edc9ce64a1dea4a6357ad9cbe2861efb61e249139f9d2a00310413ae51db260b4c23097f2c7d4ad95187d0ff7d5d1ca6d3b6a4279c4bf0789a050859d9c6b0083fc17f15af9c7d33a7e18a5c4241163bdf110a0638699008f1233f2f885fd100786356455da0de5776dfebe90e100951f8db9d8d2cf6f7745298c8ae3764cc0f9821e89fde890876b0f97d09ba58566b9a5b5a098ca967614717bf1bd503c3014953cd3e4c9afde255a72c962cd8ec7c287394ab74b09e43735e062f147fee0306deb1da7699e210768abb0acf47af0283a14a58fc3dbd3ead91fdd714ca700803c17f2671077bfc6d433aaac8736ad32a3f8a06825bed29bb1bbb6bce287c0bc74d3bf237bfac9b55828913d9b751029671eabbd8a278b1310e1beedffbe00f9d08b9ad2b74cd1e5a6c06ebcae7ddff5f834e3cf092c929c4de7bb96065ec0b1a62576ed3474d98fcaa3b9d491e2d828692feb4c6bfd2ea216ab3cf6d164b0f883622ae9eb48a9cd32d85e93c979e035ef58a37224f2d3b8574b30583a3d20f589784dbe1137d7722987cbbfc4d91f0f13b5b8b4eb8bdb8ff0b90db62538108593516b9f0ef9f1cf036f8c9ac4541f78fb62ae5307f82050cba6163de61990f8aef3f1a0122f37bc154497bca9f82ca31978e3af7f228ee8572fd6f973aa3003e365be7774ea5032a108980e3c39f22f73a3c1fe425773a2efd807bbf6edd00cdff8dc64e1fb3bcf1444c84b2a5d48a0fc63e1901fa0c382d285d971d675e0b1efc5bae4342959cdb498c37b12dccd2baa8380babc13da836b3a079991de50e1108a156680ff9be1dfadbdaaa325e83174c57d339a0a588407c9e1310663f0257b220f576c962f0995aa0193f577daa3c460f61285156678b01c3c501d80603f4ca4aa8f8fd77c69da5a3279995b5f072e47b85147c95c3cc1ce4f8e58bf00145f31c0361c93cb849c714500a641ae8006e1421566d3eae0eb6315cb63c710f222b0982cf546585db2a1384ca626dddd11156b0de5a1d6bd376b0be7c48100ab0de8c70c99516c25139688058a0271da1fba607a350648796853a8313197c053dda17a39e55532b3d3fde5aac7bea6192c3c002ec783bb8be16ef75132be502876b7b60c2b8630529c1253a82517106796340fe0f5215051aa151660d1a4c0b5c106e52a4ab91471daccd4850e08673a74bd437afbe36f20649471a87f03a04d509c820f74f2b58227838899c43d4d2dbcdf4f668666d55ea70487f501535059152c126de634f9973c1afe99c04a2b8dc8d4e7040551df7de5dbcdfbc54c30a38846e5f2ae675c0d5f6d1d1200b179a9c8e7968e752ba0da844e2513b7aa10bfb0e0d9e188fcd79dd820cc06b56f3e9c7d0fe2cba6db3854092b2d5cbacc80ba65d45f85cd0427243548e0876960dbcd11395a1182e9637f110f1b58ff96a046e5ead812d7fc075c36cbaf82dfbd514f9dace4c0f24da15b06425e87102d609fd5b71b768f917e819fe0c0ea7a6607f47bc916677fd308760c510e80ae14305e1c8bcd42b218026d2df2a73bbf79f36807a9bead64f6479e724a631e23bcc0b6e0afdae841769799a6859f63458799abc3bb9ff75eba917bbbe0bfcd55fed0e5ee9d6854bebd7790853055e6ad7f64d74e90cb753fa69d0bf36b9767a125e0b27f97c5b5e45028cb1c03f0e182724f1516ee40c1d5357370fed19b33d804909cc54b8d25d9ef1e786218abc49f874f4bb321b7dc914f136de5ef4c844adab06c731854c2416c48dc67ee421a860457ab2d36f1bcd7bffa54f7b2e13c245640859ccd3f47b63c0447654ea49fd613d74f44172ce372fcd384cfe2a4fb2ac8c0f9b2b3cdb556fc6d1e4b400d06a1e8d5ed29f11f8a7bf5c53887a213ddff97606eac2c7dc850736b4cebe0b32c14fc32ff37939f00c32830e522eee9b733d2b08b4f9a5504a3ba070bc637d4f98fd69539d470f891dcd3c36c79bbaf38f31af0f6ace0187bfcc4df3b8f48d7f28f83a0cfeddc67c6d54a5301a6ba06a82c90c0631ae958224867aa7f77d0c451dc073b39822cc84ac716c2c6b5bff1f0f1dab011706e18ab7d61895b1d32ff5405b5eba1a009a449a63b96fdfba047a740c15004659af16cb91d7e0b4a549368cc7e477e25d8a1916de263ff29573212a8c0a016a0dc92224729b188e250735d7c80536561a6bd69459dbf4f306b643c12db50be4a081b1b6e3955e5b50762e819380d988dfa5ca0f532f80ce6232f4fff26b0fcd2fe8e46a124e477dd148050817ef48b64678e2c34e6ed8407502b0ace09b09ce235cb3d3d0affba405dcab2e88e6a73eb7b0f25920b01f94f7510991085d075e14a78c259016c6df87d423481183b0661503324161a93df457feb805326704f7cb23022df85969e663d21c0ab16278407360f4e4c7ce036dce64e1cafb4702389d350e652a17a721f66bf4597b10e6e764213b90e32633bef74026987aaa0d6f66ddbf01415f01157592bad2691e816c6f8be4427a748a8c5413930b206e03377de110f37c7dea67a6a19707bb53eb5efbb114a778079ecca4aef3b01d920aa2d163973ba35df8f5a37517ad2b00635679b2061fa676c6feaa915658d2c00d4c340c0fdb49a59e9f4832b07e0edf2d093b6550741a85a7f83d6de8aa17ed09d647294c244a3292a3edd8e7bf9edfa27f08b892166c084b5b5e17fe5399930ceb6bf69e0e3ed0994f60f07c72c749e0423301f19af7dcbc358b3577000af106bb9d8a040f381aaafb4bd790c12395acf9d354f43a37697f7c1e1a96bff9d00a33afc1093cf4d4a7301af0baf8776adfd894bf3fe2788ec674eb1f1c0a7edd0354e03278388a8e95edd7aebb1fd087e991de1f04a676f14d7355507f8c8aea0b5f9f33c4f58f7a29731cf0e7e33171fe5589d4d742c2ac0619b1f2eace961b02 false +check_ring_signature 8fed53865c0c9f42ba755b4dff94c7026aadeb1bb9ec64f0994d54bcc144090e 692fc26aecf89143760d53c21b9a395e30f654f1c990643ee1b29276b16e6f8f 14 a325951394ce0a8d4275ca5db0cbbb55636b3296370a316beaff43a20e3dfbdc 610614d1ea2895d230bae8105ad5305a190e8d17f12f56d0680c3efbf937c35a c45c48b53b59f7d6574007d554e36436a03c0158dae2dbd331343ff45b8236bd 71a58f53cc81bc9f4107a54c9dcf52d80947d8b6da6591bf26e0df1cc3db47a8 1c2db705ed47cf957de1738481168ee5a1774b0b965b7e0bbab96211513c266f c4e2b0ab3209d9a489c37c5460c07665073278ce97be7ae96fbe8c9742cbb506 be2427af929d33ee6ba7c1943a5cf4be78945e43aefc753809da3d680228edc4 70ac12aa936823b1dbbf90d2692d72a71251ea6edb994accf23ccb064f784811 36971aeff7cbbd493e5de1dae82a4475379e352ba34dc6005998239296e482cd 9a6748121999a3cfb206a819adecf89bf11ac9bc6adfa88f3043a323b1e634da f630b984722380104bf3668989542bc3e73ab6a0a9daa9d9f49235df84e98ab5 098f09d6ca2e425e72005e8f95693ed8bfcc7815121bfd9ff4ebfa6c649fb7ec 9c5243235ac1dd3bdd5f310fd6172b0f9efa66feceda5c229cc87539e7bf6a5d 88fecbad224b39b12e7abdac3488c6edad1191926d05eebe900d5e4fe6cefd97 f7ada5aa55d2f23fcfa5c3ce1323bb62dae6c603af08c3f4ea5933b0d228c70f040e4adbb2f564d02bad05d053b16b4edfd511b398bef5c2b8c4f67e3a020f0e726be3b9f262a18eb46a956f26614361b8d69277b2dae08d4a7dd4641914360c8289c36f877515f101f9c37888e2c90295efc9a5c0d06c5b3fd4da596a61c90013c2d5649226734154c709278b6d63c57959140417f63e49abd3c9c237f72e0e5dbde23e44bfcb18143d4ea297032045a9ad0193e17a8f25f9f1c99dfd22e50030722e194c7f52689c0fd75f8467b56de06292305e989f5d7d32e4466a9fed0bdddb13c6ababa1b0e415fbe353ba4e9a575b703ae09eba658b41fd807fb1fb0ebc5e76bee9c1658507d09cd03cbe105de92a4bcb9ae1195bf1673b25b4e34008c168bab0477c836ef10ad8cc3a0713991434aaa2ccba8817091eeea8a9998c00f420768e7b5fc2710aa299fa426fd784177475f858f4784e7c96455547449c05c7d33ebd121fad5e7e58119b091a93e2f50842567d3dd9a964b8279a21e605091ced35b1e2cc15d09176d65f704bb713699c14f70f6e3c4d4cfeec70066689031a1bc8171a7d53ecf62468b07d994fb2019ed650414db7050abff34c8fbc760f23e73206b12923b4cc84e58544d46a79cf9314cde69ec5e68d8175c84ce23a018408c401965fa6643b19d029287e0fd47216fb893c0e03873d9f803e53970d0b9f60bb8180b52387a822ddbee09361b18ad86ccf3c146ac6fd016eee2fa9900e5f824db6f217a26e2a69f25f12584e9f8b1f50f670fffd17f1bea7657a00860c52864ee51b71ed23caadd99fd48a6eb15277184a66f590545c1c34b82125fe0ee4411ca46be1efe079867959999722b847b01a0146362820209bc171503c4204e6513bafa3f4eb660cb306c11aeacfcf89f6d9ff060501e24e01249ee742df0f5606f74b326be40a9cd979beda5b9b6bc66bc60ea50558904aa23979a76a7402de553cde0e31c23634021fc278ef17b100079fd8bdfb23cabf417ecae3754a07db6d4bc6b631e141559ec77ad6842ee1e81142fb207b1b93dc001cd3904d57031993c0c0515a11719deb873d1e69e674cdf6a4150888785680553d9c8c96ab0d6d91f5ea6b0c2047398220289c21a3ed9ece159fd773c2daef4fd51e3b8665020d09b95a7af45203f94fd1a8209f286d0d815b94830b241d8de1234ecbef86050e28bfab8835b5f7845749053263ec4778282c071c9cda1c258fe813a529880f true +check_ring_signature 95effd17d4a216fac0223330de8605dfd85a1fecfdd7f382972f7fc6c4b0de81 80a8df8fb093a049df1eb8342d7d6096a7433d567eeda016fc64ced0f7104c8f 4 42ce1cc8b40fa942a0046d8c14e6374eeada13f36c4eeafd7b28ce8024092bec 40134a2cd3c57b428b67bb4b7e4dbe6016566d5b8cf3e359c1c5217a01e2dc30 e1226ff11e84ece47ef0aad4a8096653b45fe847ab7d62915846bd43c53f2c0f 0d31ff2917f0260d06edfce1001dfe5aeb0a0e8015626286da058bf7d226647a 313ca8a27db7ad7e63bf13e06f318c904aa5acb2e266c92a1f43ba770dc615046f8c5ec41564f0ad3aab113579df2c5e2055fe715138e99221a5dfcdb72c010f6eb8000d143c09a500ef63354774961e45ded75c913b64ca0d6ef6b359f5790f9a3b97de2347204c3d7d770d4cebceb627d4597d65423f64cc8c4c456470a808d60ff38a54b781cb2e86c141d6d8b24d5605f9ab82d79fab9d556b236689de04ee0c7226a174b91c65de01aaaf8c1e81e546eb27bee30a560a814b8842bbfd0207dedf4552319683fcbfa13f890bdaa773d716d3d57fe25a8abe7c934d03d2078a31d370f04766552889a04cd5ce3d67fa5322d446d7e9d30875775c4c35561c false +check_ring_signature 4254c167843afd33dda4fd1d432c822d33387fbf66aefa69c4cd80977d7aa4d8 53f8054b6b5d8027ddc13f722a5ebca41aa7a123b5b3e757caf9ebde3f1e7dcc 4 d5399fcabcc4582a1c469305586f15c8491a50d119fb1b99abbe4c7412a9eea3 094cc9875c7bee69ef6723ab3584548587c480882a62c642258fc110897fb20e 23046a558f92d4a8d245dd8dd34320f2cfa9087e2d79c04713c05f0a9352d194 0f05f2c1c5e2726b6d936f49d1d40e4217197c94981ebb96694515ac17e0d624 2ed5401633aeda1fd9a3b9d90df735f5423f8c20247a09f155f938e7151e93011ce321eb5885e15192ac27cd154a1b9c7615bb42c8a99d4c43f4ffdba6eda20dfca8ab8080902012e964e9a2aa8d918196694f21558961c3fa3bae76c61dd10647e5a6cfd851c777a20feff2ffabceccf20ba033a9dee657afede196f1bf6b0f1051c210e8ea35c02359a3549e71aa9a710a09c3de4a07b9e9988e6348ad8904904c2783bbd36a8a66db45f0c4c8a50ea0f47cfe639bd19e496d36f5394b060c4369a3ea74295ccf1159e00e1eaaf053d8973c7266d4e5f4301ce42733611500525aed25a8adc32d7eeed7fb977aaf06457906ea475c83967322d674ad572c06 true +check_ring_signature 1bf8dd542863adcf3e9ce62696f527d7ab99266461cf5bab8b39e2f0f304cc91 8576f3ca8ae63979d8adc9a0ae6eca72dea6e9c60cd4a0e3610f0431c80ff4cd 41 f2e62cee017c608ea7a92e52681e50c2ea3a71ad02e3867e49726ccae3b17d49 e00aa59749553365c6f6ff0f2a133d2f586842af084d4fed8551759fc53fb249 d8d1ab24da0f2eb1c2a55435dbde738adf19752bad4cc3e9e017648b21cc3a6a 5f47459681e5dea7593d7922b2afa4fc10172eef2e9ab4c139338de5c834dc4e 78fc83b3e105c099be2430da873cea491ac109c721cc3e64797f3ffbee3fa627 7eb5ca2cc2dee71253884648df3c2e5671f512d0e48e2bed5f4b0b7fc9126017 5987078d9b0bf3cca4c584615e94503111f8f70edadf076a196c084406f1fdbf ce69620f6992c556e7441d0b91928c3fda7b2eda8a454a04e33ce227f498d208 d668e47c88cca9a6ca23dedaf245138c46b1f3b6e65d2a68a8bcf9b15a5b8e8b 3d33b0be218d7b95a002bbd756ca3bc568e653caec0d31b00e1d45883182206c 2507367e2baa892e7a79852ed274355477dff12c70867eee46ae6fd6643832a7 bb4cc850c5e0e49accf6da99d08a26e9d4c7b546000fbf7924631241c92e2ab4 71362a23c0763e07136433ae6135a50a762666f104dd4d2eabb989b96c99d057 ac872a9262e420d3a4d8b1dbde654f26fb00bd041ca42c3e62db9ee9f1230203 7be4ae99033fb5d93b3a3daed652fdc7d94a16693610714bb1670ab4609b082d e785c6db92959775012a52a4799a3d0db1df575296dcadc38863635531d0fa34 cf3bc9b897761c3e83f310b12f8f57cd2053025b61010f5daf6747230969e886 932eaa7502982ac399cb9a5a4cd3f889e01672317d01d6e9aeff170125897b4c 9359c6d2ab33797d025e0d5710784873f46b32f2c497cc055451e90f64a429ed af0d5693d8079626105cd26e46203ac493d858eb6146ad2bb029863edf238714 b35d20558a79a83856603883861f3f2e7f47a180feaf0901f69fec602f5d72b7 39588ce247d834590878a63db315de153eca4a2c628d7ecdfc39dd5defcdf0c1 51bc9e5c57aea7b049a6866baff605f7f1fdd7ac23cc7a014ea9f1eb6675cd5a 804805915e53c2f7aa85036a3e7420ded008c1bd3ced669a1deb318149b36348 21e69dcde1db59cfe746bf02e47b762c30c6098589a82fd1aaaa03d964863e16 e25106748a909a7a36d1a9d1517180150e07d6c44ee41b9310769c493f8d9449 e6b0e1df8e2e123561f13fa757de84633fb7edc4a9c7c8195413e7207fa79639 755422d92903d7ee12d74b0f0a5dedb94572cbf00195a5fd757d2024c46af39f 23f0c3ec188bd62c7f5ba636b067dd354592ce27c22c6f5d9db5dee68523b755 8f8e53d2a463d560da7c3dd5cb35005e6ae424b212494a0756d23b7d57e5e422 647aec7286a919669b08f9621b85716d3c7bc684b607de77d4cda660aea3fda1 50c2b4bb6f1cda2d8f9cbabee2d40378d4418ad6a9b5801f44a270b22c42cb71 5d39af2ac167167366aa4c23193691b744655b925eafe2539cc32c39b66d4fee c8bc381600750306c4be80d72bbc704be3ddc7e8cf551975624478b3ed6cd55a b5a020a5fcd7cfd444b981b03f6a84421b25c94e4daf5b7bbd0b828d79223b49 2c90a6f212246f52f430194c16b404e68835e3d2911aff3b016201ad374a8a84 49b5c5812db7e1c9c53e56742c204da1e5897717a068350d523a78710f7b6ccb 39778b110264f28ce9b570948ee1f3b91cd989f23390b594174b43cf005cb0f2 d887dd926310347643e8f658d1bf08b741edaa9e68c19e8ed99e3014f58a49b1 dbaff5b5e33ecf8afffb2b750b800023912069b14db641d9b109851c5239b022 3c1268e2823e8cf58fdddf777e031e91f7ddfdc39765fa2b3df534305dbe9bd5 1ca831105762d2929d1866e33de0ce9c00754f235c15b0f21a12ecdfd43f1b088aca3a74d467ba53ec94ece5102ef0468713a00eb393bea169a840214d051d0436afe07a58cdb68c83e1ea15be7c5a6ec507209c754d5b6f2a1e5af86ecab705e8b134cdf384cba38a0a94db1ffe031be27576a115d56f8d7cf1cc98b8fdcc19225cf846861192cde8eafbf421c1f3c1793398f6ea94caf8df609b548a12db0dc907d1a9c2009452fe753bc6b852db27e82c22ee994ec644c58db02fcbbc9b0831666636b985cb7336bb25eff360da356e513c976c4ad5bfe4e06968ac16280eff131edfe5386956044b35d89c0e69d071a3df67e3e8cd6145936e6a4412420253ed8a881f79b1591675e60157965ee9f3012262324378409e0c141da5525245359eef84ea9689a807713eecf410fe4365d3d7dff0f4bb807d5fc920f8db9e04dd49f2214729357983309525ec33717c11deec81eda465fcac631024fcc7ea0402dcf76ba9204ad979352879e8dec4de936ab28812faa12b573a3fdd07eff908fb129251b598e851eb930cb53a3bf89b5a58365e0d174146f2408a9455812005843ea97e2ec4fcee2ba3f835d240811fa17ad91d78f5443c14b3fecb4984fa0d5462091e2b275f500f764ed026bad768008871f46bcb7580e92aae32f5e5720c9e4fb836859c83ded6aa5a59594e1d278457ca76a591b8a4a1cec7705f41150469906a20542f835bdb32bd77e6cd51d3b0004b6b26e77520908064ee0aa786090d7246551413963ddfaccc725a02e7e5627a37c161c27229572771d274f4ae0406590d9f88ae47f3720a4101c91a4abb37d44c22928263b187cd998164e0e40b7c5731746eea4cc11c2310caf2fc63f5f67f72bddc9041879a99d2ddd1f730005d9cdd037c0c9dd45662297b4358b8cc442273d705b5e2093157621d48e33205ba6872105d93f6d7004e3ad60a6524c76e80e2541fd556c4d32b5e77e907bfc6fef00ec624cb8a3f4fa8ea2c8e9e485e9e5b38b2dfc9d3bb255dd569a73db002c198fd9e4159502131398500d4274ce4eed6e59510987f650ed7c0b56643c20ada82f3db9db6340614f1f02c0bf14fa66cd672537c9a53613bac8065b4eeae011a2620d4c0638907babcab69cd5e52e03880a6499aa9538fcdb5d25651821d09a76502be664e767376ba740eddd7e1aafc4c90e33f79e4fdba08efd144e5ff036b8deea62e2c8d94ac1ac869c3f859b28f1daffa6d6ce8eec2ece4144a8d830fae41068bedbdc1836bb908cffdcccd96573f5385ad23252b9579a93415dff60634916c566977d100b91f72337eb4e8dd99cf3477acfe0b9bb9a108fb59149d077eb99001868668eaf3e4b81f5be70e45ee15c074e05f29462741dd857aa11609720f4aced99ddee7de9d0f8916378aabe832c1cee6b51cb2588bc9f8ab2846077e8eaa27d7e081ea099f196955f106d07fa220bb817d47ffc56604d5a6682d01a4b96417ffb8d465118fd09a9d8e4de4672341bb155bce8b86d51beb5b28b103c0fb2757ad53bf4501c7f83f447618f96fc56e0586d1f7c5812c7ae65a58590e2f28edecd55ad067ee5b111e9f0d32d21cf09a1c687889b3114fc57e30490b038f07a42affe1a7a5565617184486dd6003b29837f5fa53688ee0bbf6784ba40c454338cd3819192ff3159dcb70e37e17969b7a3538f8f25e994de3a0e54dc10d28c3fb595741cd588e3fd9410f4788d5a9038e3c9aa05d447663b24eb61d060cb5572efdd00d68b5ae32a2410681750f29542f2ce5701d9f4dec6d4ec966d402308e59536ec03afb660ebe21bbc3a0eee5cf5601d1539179d839a875e7f30506eda8ff732bfcf46e8a8e50939f64d0559a56ae86595209233c8eff8bd2f34a04889ff553ec203de6a0b641a475fad76dbc68b44cb44402d204a7d04e64e0b60180b7c61248ba91184286f715a61930990a9e3c75e6d7732463bb08fda681ad06f3aa2f4f1e255078b45af8c1f2920284e4f232d213d4fae1a122fd7e08403d07103a7be4670e7d84b8d487fcad4706d1e20f5db3f36e4a317b18dd2c18a1d00f71376fdfbaced5b6065ca11d1f34980bd9d2aabc75c5c0e924759abe418dda044328fb0c28911f57a5093f80b8ec19325e58c27555c113ac8f79b0768f9f6c043c11bf12f2ee78c923cb2965023f8db7a643ff76595ee4da5efd2c64bdafab04abb98436bb0e8b9a716ee57a59e2b1b02a3d1cd8843ae4b821505866c520240c696ca9800354d1d71d2fd5d226dbf641d3ca4281e749b9369188efef3450a004931deff1887173d54773cbc9c2da48268b4a3060d862b9b6ea6ad3d262376a05520cb13dda28c908383ac5b68a4ed470df1ce0c40cee738236c9caae110c4f04394efcefdcc7fddab6217704fd16f4ddd428ccc276cb7052dab4571b65b2e40b84901ae46f6b8a3ccdb821ee1adf1c79560ff90c100afdb04e54b1398cc95e03de3a537b7d9faf2e5e583cace4f1d4facdf28dbab0d49e3ddc610e57e46e830bc14e4acd408ec0330ffcae8328adea8286f33b2b26c8549197888d319736ef0472df47f1b639a6920c5a572b49cf3274c314d870d02552e9ee3aef9566817e01db73a5c01e296984f117d1f4d554c690bd58f3bd39b93891176f8180c67cfc071f96f7ddf1153e3ffd1e0fd019da80b9b62f3aeb454ec1c6bd8361e5ab080d0be0249e2b6a353318ec4ea4f2d52e850a066bb403636e85ae68ec752cea069102f2b4912f5cc4b7e78ebd4e3519e073b60e47a2c2a19b8a90f09e8afa272eae01e4122c52039470876b2db08e9bf144641ee972d2ae976b53e70413d899084401ab93a77e4a552bf8d0241ccad41c7088c4359a41606c7e1e0a7866585843c801362b1693141ce4de383638b25a8c508425355046e0c7e30c362707d2ef671c01561c7ada021cc53a34b7228766f4ee3bbfed33984e1358d93d8d7ef59c58890d32d19ea8c2e405c8714ad014a2d413e55c146ad7b8aecd51348be4380bfcc20d9382a6b90e1b0b0046191ea0c9f246dc04a0ff9921acd3e1d8bc93f6c3ca4308f5b46b0a657e556f07cc077f6112930d9de41d324115b6acc1d4e2387b65430d1d9757477af086b09857cf1faf205a36f503310dd7377cf552174e11361d020bd5c59182680da76450b6bb771097a330b86983f525b2c7502905d60ab6a8ac0fed33e2a43b23b3377d415ad42488ebe0bde265a7d72bbedb46e9b7fe6c040e0f13f59849ca5ad0be99c8d7e169d904201e5890584f419a8ec95e1d05e13c57062768ed30d357e3aa5a1c2c5f9fde64cc07050d590ede62082b0e64fc0652520ce6edc4b849e515b82ded2282eafe188e4450099291bb3fc9730ff8bd395a0600ff6b7e74df09307d2eb2f6b1f19782acb2ab0d9f1ea9221e26adee2411292f098c4024556578153799c7dfd377cbb9eeaa094369fe909bd04eacea56a68bfd0be1b664b0ba01bed280de8a48ea497d2613e3d6a893d9622b0ef531f056ab6801e98fc161951ab3f0140783ec3cbdead3963aa29e945439098716a835222ec60dc2a61e1ca091cc4b4ea6ee001eda026864af927cd82eb9fbfbe9e7cae24d88020572a53b8dcfab47b633c5116c5853453db8bce1ca8ac6c03bc07f3d7ca3270c1f95de2fe008b993c5b1cd04c8eef797bce68d7de1d2e71302f89fad883f3501 false +check_ring_signature 13304050cc6bce22d99d2b312006ca610b8d8c2d8b45e3647c914cde9f105429 ec8d1e3f9ce79893375cf15f8235dc69058b0ab2ccb506b8f5c06631a75b6d54 1 f0a958d473a48f2f5fed55e561e18fda9d5dce222c1e8dc783f2bbdb6023a15c 3384bb2cb903938913d7ca909fdd01330ec3ef46d7e2396cb4bfdebb9f1c4700280ac3476e9aa147fd7feb9d2c99a48c364c5544a910f3a2a592a43cde61a26a false +check_ring_signature 2220863ec22233c8c56a0c1fe1fede2ab4a1fa4738e192312ce0cf96771bf993 0fc05943089c885422820ff9dcbd7388bd1bac76be1cca2798c73f631f7eafab 63 d8dac099d11ca701d54f2295aaaf155e0db978b56eddb3ffd3e787026aa6fa8c 6ab0d16b1c9a407cec336ddf67dde40d93a2bab506c604c20216577b21f73a64 f02b151ed6291b422fba5a38ad5c8f2368102486d575eea46939378f4be213f0 69cc5563e9ca914b1c389686357fce2b3a084677a95dc98e90c7d5e95817b4a3 be9f241cd5ea9344fce0359db8ecbb31a93e4ba02dd79a446601d88353a3d724 6f96ce773cef0ad39a6f39b577a0650cb130812a439f2372938c9732ece544b2 44feb775703678133a400f3abd0095694bfc3a13255cc49fe607d865cebda7ec caae28d35e988900cf319dbbf3f037e75c1c2c6eb4d72df6689b4794b46ed392 d6e48bb820b5e4993db093a32a4e507a6d971a82e314042a2a28abd17127f88b 99749db6818f2a898cb4d3cbd0dd3fcc8da14d88d52a9b452985330429563454 3681d8b1e666f5c91c1568291118287d44af15606532a6b8894007c3572de287 dea93eff7fc7494a46ad6390167bf39626ef51d10d8bb383c7c48efafc180ecf c5ac2958ef4a191ced5d0121cd80222447b7e83d09f16ff69cadfab996f8eff6 a74599f1c79217bc3f7a8d5f72de13aa8423c110ceef0713b3af99f5cd19dc79 f91042408d968222e114ec5d8ff01ca4632a0299580b78c4e128b8019b256099 66c7de7eec33c73eec3fba164312792671af0124014e996f8f4683b8b4549d58 bc7310ccc0451c14747bdd781a69965b25c6aff1a131501a1f14bc2823321530 3819a14f30bc392bc8ffe09c1e16a8b57ffff1aa29fa0ed1c5c1e30ecccc91d9 5376bacd66cfb54455fca01816f55551252aed5ce306127182ea167b17b8ae51 2c84ae4d18a1fd0029d1607db336f2fce3ca2b3e41763672faf6022c1b2a992d 6a53a83653eba71668c59cd1f89f1d0733bd055bc9c6866e2ebbc96e76f8db82 b685c6029b4dcc6ba3b5bd111625da9fce66345baf175ee157a67577431671a2 b826f5da9d6539ef8143a339bc62ac918b6b10040169cb88ecd33f1aabc0d92a 7375bbd8bed3fd2e9dbedd6be6bbe08209f809de40e0f74c0b7ad885d7a5786f 354211e6e34c61299e8065438d9d07f98b816a924426a95dc647ce49a90fcb3d aa7b87451377ccb803a11a28e953d889e2a50204e9b415a87cbc27d84d24fa98 3179944ab907315f250d71d7088acf33946c0de050a77fa1602f697407a02035 4da8ee97188454be702e15bfb21f0e16a1998afa7a020056c1e2019ddf833814 1f8cbb58e586dfad8dde5eb693ca3fe7966681a864d8f5454416d51cdcbf1c26 6314df3ad27355aa332c24b13bbe8bd8127c8d5a7953880eb87199e92d0aba01 26e2257c2551137f122c70e39373bd0a0093fe4b15755aa5dda3db3c3daa4944 d102c74e3c84d7d58b18bce90ba45f7b9b88d097a439f121944333a959a9f677 9ca8aae4faa6bf1f57fdcfd995e9fe8cfee69f12b0dfd006af7dcb5e0bd06862 16991ca727fdd593ea3e8acc35da06dd7c6c6fc0f3159f7d37a706e7f3d4db31 77f47ee337dab98a29d0d9473400d0bab3cc090ac2189692fcbbf8691ab7ddae 012bd1cf4f8e4ffd758e19df4ce641756bb786b4859f602651559ad302544ba8 52660ec5318073c947117221690b72b70f9e179b03b1f88c6c8a57d333e4a0f3 c269f6153cde171b82cdbe4e534e97a7e31db69fe31c037c414515c2c6048d71 ff2de0d8851da50258afcf40f5e21e6b8bc565304c15f57b2688f63a50f0dfc8 11a6aa2a6d081f5e0eb93345a9f18ee90b4dbdae68392294fb66104be0aafe74 ef3134f557696f4f866a62b06d902c8d6f809130ff782b6d5398a33d872816da fe51a28b0b4445a2262af8fb2bee9f46127e62c75e87084fc753dbc45e1353e8 1c230bb72228fe3f4ad822b891273f76a4dc2b71e2232f871dbd1db73a329d67 299044df643abbb49ec50dbcfbfc742d57da866bf15d3c9be05b11842cb43af0 d7852c08ea2b891f3f6156a3df96bf73bbb7c619c6a843b604dc09f0e8b61e18 d75709abafdcc7d24f7e13149daeb4b0d913ec9ded795a6598b0645fe1f28985 7add5af8ab507db3a93b9b587a2335939b3a6e4c05c609bdce00c8cf0168cadf 6f182f1cdbd83b046f73f601cfc03d38549003c66de0679a679df988e444f8ed f4dd9c3529824bf85fc0c40bc76e44816a85c993f90a441e8ada411933a8ecd2 d9fce1787caa591006d91dcfa0425f22595792f3faae096becd621a4acaeda02 a823f7786181635d222e82330628a48dc1579601be330a091a0c97ce60fe3bcd e8048a221b3b8ba2e674282b6b97aab07fec0a86d9e1fa664e5269d8bdbc8e2b 1b02b1333801443702e5197fe2be4a1e1342685d2cf3f8d01177870bebce9a14 fd0ca9517e57b40d7132b7c98f10689ca3ae72669e1d6f14ed0b0859393a4dd2 72d52048c06e92dc1e5b99a97200140b81551fa681947dde52d64329253fd6bb 809ff4bfd92cf0f94f43757ed52dd432d586d048e4dd632ed0fa0a1ec1402ab3 48a2d2be09a6e23372905496c6c6d2f10433059aca9c936119ecd82dbe6dadb9 f43f3337ebcaf8af888d4b356e6fb3042e9a4163205e7784f0a75e217718bcc1 e93dd6bfb0ab253dadb6f67e4a0e245f750a4f529eae076a1602c517d40cd290 cfbc9f8be97a3dcf295b68f9de9a7539495d6dc9277e2a4948022fefbe7e0cb2 ec736e140ef924bd80ffe8b1997dd842f9f6bf219cb606e19a524176e42f9fe2 e76e45d1a6fd7653399a840ab47a378c6a1cc5c3be3dd68caefec91219903aa3 7056b163d1743f0853676a9b928dcbac7ce4aa320494ac3ea77c499101795281 a39edc869c5ade053e38bd180bdb044c72b00c31e387316a8a78d076b7b28404cb85aee63bc21c0bf48a29b55a17c5338b5c6c38d3a9a59f57c981c4c342d30ee576f361427a9b28c44c556908335adedf237c777ee07f1b5394f97f165ac3074d498702a8840da34acd3a4172b93178f485dba1f6313ca0f789a6967564a70ee0ccc72413906e7d1d1a43044f91510eb4c5d8f6e8dea00961ad90e391cfef067d3f8554baeee5ec3545aa28c3a8492077830bd015c1ffb9bb744789e316370ff813b87557564d8fc1c9b9e6ec901129cc24d16e319dd1b88582445c5fbb6a07992b6bf7444b4d6b7befc1efecea3bf82054d25b0b9724d3ecf29e504ef2d303e183a9adcae2bd536ed110df91202b9771d5b99009494f023b59593554338c0d2c8b10395e7d70158b5cbf784119fe5b6151bdd103780af3b7ed9530ee71890aebf427fca765e70762f7a429680239b3d5beedf74d75e9842ac2a1d935504803a608fe46a3a1cd4f900e7cab27dcdd1a5a2d75da199863fb9dec8a8f210efd047511c1d7a0a04bd8a8eb2776ca4c2bb33db91121d7b5e250c27a12b5ecc92303ef2b6a65cc50c0e96d988fb5a9597c5b509169ddbfa687b1e4810f3a5003af0bd67ae172384375bd883325293abf75cc2dd53794334e53840dad4e2f86ad370d55a7eab5c43fa18386fd2890dec3ced17c54967709c303c3848866664ac6570cf068133f668f33437dcd2bcc92d7f20ec441c59852545d548ccbcddfb5c5c501f4aa83feedb0c621679ca7c26b877dd8511799ba32447cc3a59ac6b6617128019f25f18e8fd5e3483f30436f206afab2c591e95275757526d0d01ab6fd39db036025a8daca4dded49ca4e48cace24c3abf79a0bddb4eec19e3b800dcd8ede30532195ed6c9c327abe8012c612a49810322183cf297dea9dca5e15533cbbd9e0e4e073bea03768bdc2426816c2ac7af3caede38b0b1b34d8417aa965f15be040c686b034ca040e9719d2b2c4541b4d0a7b2b9c8370b5932324fe6c5f2e247910aabe17d78b0db2440554a82ea67143e62d8df8a3d140819b0320e3902feb14203cb7bef01433744f0f3ff3e4223978429ffd531126648cc286c2cd816aa7ac30f97896596caf8218a69712073250e8d00dc7168f80513178d9bf3c3084844fc001a90bafb66ca94e28a57d2a1f71ef5795aab43764dc410a2c586afa1ffb1ec0bd17d9c9beb64a05d8c0e8ad45acd30fb7a4737cc68ead2131d17687124b2af0a6db5935509a42d55cedaa32de4c6968fe19d87e5836d734271627d55df50dc02c57892981201b36df012dba58649c0e3eb6e9f93691c5ea691ea8b982a32c2076e2002f72e9d7a397fc692b4da94926790667a505025cfd8e9efb4482b306c04b1d723866abb699dd9d1e7d38a32fde6409f6bb75cee6335251cb738179cd10a8ee8be013120dfbce032f9a7e25ff36b976a43666c0fc38a81bfe40d20da2e08a1507f731bc7a6335be37ec725b49e8d1c8e1f571ef9f39586505002cb2afd0747ca18862ec2ff39503e196a25885723921ade44a2e3ea2890f6dfafbbbe280e1f92d6dddbf85b6194b2f07829d9da1b0b23d7a3c235e70a98e7c19c05870a0eae13b16dacaffd9429098556f89e0654d6e729920a02989b8555edda0f75e00ff8b9a8002c4892234f113af9c1d952ac32b4b94e934e460ec5fa8ec1512e430db309974b21d278d5452df54efd979b76028385ceefa2e4121bc91676ba429a09a14e770bfb30be997b81495a35f0b8e0070f4c38319ee43f8edb76663104af08077eeee0a90752786a6e77613002f81dba07f41ed223e5c853d87155c26c4a0020c45cec880679fc35c9876a63fb3f1466573d8bcbb249f11f1de6fa2f7fe203ef7120c98e9c9b67237be687122be02b331101a6f551392e64a5a37bb122900606fc665c03675e5daf10784ca03f30d4d299277f58c202d16dd3eab8b94d2906f4f0cfc622b84ff374ece6d00db314becbdcfeca4e5ebe70d5704c8a10f71303eff82780776438685aa978a5c28e42543bee9dae530ba9fb601ae8dade9ade02a721688681e44b8b1fc722e14afa25c370f89f02430b2a4bb0ad6d041ce04506cafc9dc1e6beaa233bf25ee07f45e64ac178672f02a1f1e3a063826c3338700bc8f051b9ae29bbff757867ddfc477aa104d0154ee1aabb83026d5b53a92d54093da45270e1f8002f657a1efdaca7a72f86508cbb6ff7700d5713c06e75c88b00dd7ef862162c4dd2b96398ed8e2754768729263fa346c937c260065073bad907ecb47699fcfe5f742eaa9dc7d903ecff5c2fc2788ea0a20c544eb253ca3575029ad834e55a46df6deb295ad8d376dd4a9fe779ed2c7e39da12a397fd6dc81c07705016cd16b2a2778806493963e94df6588151061e1d29b0430c21162f201000bf3b3f613514a91c8967dace4f6d0616733560ef45d03be954a837d8f533a603325ca5a8f9fe234f715e934e2adf54e45073145044766397d4270031be347803f0552808dc6b8abb54f4487d53844e786b4c7f85006a3e8ad056066e1810d204dd634aee7b2375ae34feeb42eb10526c4e5af0e69e0edfed0c111ea80bcb180d6f060065751b3e3477d9ea19d9a8acde001b0933f312094104a0511515337e0b63adbb03704143034e0bea4f3e35b48db04798ec1739e020c0186c99cf2d5d0e98f008e165b905681d4ce5695a51368e5ab7641f9e90a35756d691782d3cf60a4c23b00bc442cd390d7bda0d4663519b485f7c35e36fc9acf22f0ac08963590d60170865ccb389b359ad903685dfa2979edf332a66f7e8bbda5dcf0ea2efcd0f891ac8b4c1f83fad32fc39f02103b021e8413312604a2b3f47908a87997e540840987c466040c54b6360a2e9f70e348f2c31934a184d6194b7fa8662e2c665084bdcab5fd97490704d9ac828ddda6f199ce0533ffdcde3e6c5803a572f78c40679b58ecdcf12c897d5ca17a79c1e18a6a5f900d50c65475db754cd71e08f240733963a65c83de8429303b0f3fce01eed41e527e1f768c079b57a4fb58257aa0ef0dfe2d3aa624d853262d47438fed949979befb47986d976b5dc64fc98653f03583b9558fbfa1bc958b45a3e4803381d226f7c4a648770f43c43beea3dca9c094e104e17dc0746a13d1eea5f81e7fbbbc27752ebbec57bf0f7aa04b02ff1e901904d0b1409ebff65a4e85215b8722d9f8ea84c8dcc0105d7bb930cf55fdeb901ce8418a24cf1abaed04d015762fe5ac6f412a7937007c905ed56ba7deea4ee0c75f7e6a79f3813fea61cde2fe5473f6c835279c4518af1cfedaef17f2ee6d909aac669f1f1b57cc273f4f60e23215e89498ee7aaed9586d4745116c7379e2009a4c4c13927c56c45026390bbd35370b04b1c303ade3e8f68e405cb5936da5901b2b2f3f4b5744051cff6de800f60e0e5752062407c2e2398a11e3a69b4ec21041d306e7ca7e70ff43f8657def1fe0b31f12274ff29abbd1aeacb5e2ed526c30817ec4d54d08c2982655a104db2c587b04e79cabe91391e4efe0ec40bf1c1a702ea3162a272b94c57d607d33b15c018c2ecbe07194a7c531b32f933b43a269b0b5ecdefb58cc37275c4a7ce307c994de0647ced6f7ce8e4f05033d8dfcf89d80693a5482355c7cdcd458e3a6b52ce35c118db4f8256fe3aea26be5035efe4770a07cbbf3f438e63992acdd1e853eb5d8008bf4273932383d63d86251709dc2009be315ef7087e4393de26a167e6ab78118dad676fcbe55d9dc407d4a3b4c89e0bc36d5094a2c9800854bdd540ff13d7b0cc7513b0c15a41a2f6de2862cd68930db01f8993880118c94271d9b06afef81e513fde1d88beb385501b1e771c3e460e5fd3831f52281b6e77cea77a34706940500281554d6364c5482b28e5696630013f767041c86572f9b153976be5d975cc8c09cad9069c72ba99ba02f96c9f370b0a62804b9d9c1a59a94491683bc021e37e5b5eb49a8e3a69039eb39ba90852019ff5f83b7b56725307cf351ec20aedd73bef73f8048c77292d1c14e820e17f0027f87b76e6f4468446e30b8d6d1f4c3ee718a1337fecd876d718730ce3d3570e48828daf68e5042ce2cd128866ff3874261fff6237f7f35f6c88541d99e6540749ea9100858bbdbc4601c1b8d90b564c0e0607cf8e651927e87236c47d6218091147cd9345f7619f08491467e7ebf0e0d07451b7606b6e310bd8074eaaaf100510ee317ed818a60c102a588bcaea7e12c52701b9b40435dd9cbbdc303cfc920472396c802cafd4c90f084a1887ce70abd9092f417a74884e6136765f12f3250759b1a6fa8a2d51bd03041cb70a80275bbe92ba30cdf2e248c6b5024fcb201b0275c8c9c396616291f6012c5a9b8259e1cc5b533abf9f5535a4bbdf248bad3b023b7d0800b38af408e235bc5a4a336a041de69f60c2bc15815c5b3f1e6f23c60a6e08b0c06db9427ea66eba074039dd581749b38f1d7bed1942056478fb6bfb06cea2a2a01430804d02ed8b88f929d6294e8f8f2546c59103adb6ed1bdaf9340848368a017e1d2b8b8316df6773f027f361e729f5845172aaabe2ba95154b250de2a4a19781dee7f2f88c6dd716523c3a69025e85e362005c9c7016b6c2c7b90e70970b6e59094b78059fb01bc336626ddcb4ae2d9af741889e6b14b25933e90482ac42c19e1b5e2e2de711114057dd1f8c80e9a299f69a3593b228df9c2a3d0fbc6fa70092946035ae72fd12595ff998e24cabda0422c1f609539f2826749306a6aaada0e965408c07a28a9a7a5991ef3034b6583c675952f9a890eb743f7607536fe8f324593876a00dd001e62687919f42825746d90e8b7dbd349b58f4480bd4c0b8188b38cccd5980ca6e7a2fdf0a3ebc29afb56069562ec28f3f43b74a0e1a33080a9a46cb58e95c26825b5da5199ac629adb3ffa2259cb0d2206194b003d7e77c6c970c64c2ac9debc77d48417817b98c491b3968c14486ea96ee5e0c0b0349cc8a1ac247c617812e7f55053d5b0fe3fff62b6812ff118484f0b766af0efbc91f0f8f8cf629d594dcfdc845ca8552a4ad027a38b0545a607a7f9d2e60054ae976bdf2bb9cc564b26e40c66ec26d531105aafee71408d9b2b961f538f70f52e77043cdba91a746a2590535d935d7006cc4679849dcad84acde888580c00f52d34c848f0567b6dbcf3894f2f0a896233a8ac29ebe3e90f9f26d51b4779404d8eb66e0ba6dcf6040ccd88beed455375add9d37be7fcd833f6a2bd0898a9f0f7eae486631a10e630d382e73bff58d562d98da329e01cf9793aad44a49b4100bf41fdd20f6622353718284fd409a92f73b8a1896122fedfed29d0e368ad8fe0fc93e3980ab8e20c35419f82d4863fc65167734a99061613a5b6c0bdfa6b9b30998a8b38da1198df0c58d7ec9abdce7e638fdccb0342ad8b95437d5b5d8a32e0d62aa282b794f4ca27b3a7034a4ec00f01d9c8ceb810602f925b5ec142fdd250dc600dc5b2c397575a9ee6adb4b9423f1b409a6b385c8a575a9a36e09478c8305204a8cf2ba70871cea6dd8aaf9030b6399279a705486b2a2283b484a2f838f0da45f390ad5de94f8628d61fde38963bec474469ad039cacf4f0e67b719f6150667508a226bf2bdcc8a70f30164f04f2d87df2e4b94eef6c5fe8c90235897bf0a false +check_ring_signature f1f4b9a6d81d2800ba32ffadd74afa407d1a90369a33359e00602c8c1c6df2d4 376f5dc62823fe2f73f6c7b34f2c906ae5038371ee283f63b5177fcb2cbf0111 32 5ea4dcbd1bf0710e435b3e871e0fc019e2e9be5fc37b8da4a93d52dabc47a2f1 65bd54b908aee597ff0916be5967a7f36402a14075ebe29bcd8edeb39ada6863 e2df7ae74fd4838616878176fc99951bca71df302fedccf7b0b2cb1482a2a44d 59864af2e6e1c11680c4b5567a3a32c062cb2f2d80caa6db0669d06f99eb22f1 597e2c4067d0b311cf8bf7be4caffd54925e48e8f5008074bc5f8e8da7bee78b 8f85e003ca68cea07dcedff780bed74820047aca3eb2703b25484d1ba119907e ec6c6fb386fba364e52db56890f7b3ba872c134e0d596145c05a50f42ce2131e 4f4fb29e3a9a435603aa1096c0ba55c86a826e3e27fca20cb734dc7219c5ae82 366364aea1ab2e79c60cfd59e575dad266df5268708700d19cef013a64457f01 d65eeceb5a79b7a743cff212badcc2555e7e7b7798117385134e6ebedf45b941 0eb79f6471a6067919045826acbfdbc5080a8261b8b896581d59da9a0132bdfb bbe93081dce875b307007b08d773ed3074962d4a03b0f0afa23732e124629fbb e3cde992acaa6557aa5866c7febc02e29bfd35c9d74b0412cb7098b53120215f e1d247fd09eb6abe279326277a75f88afd25f979536a223c8901b16aa7ae7f63 90fb517ea3cacacc1e741875dd00f6df88879abf2811419b10170fa7f50aea22 49aabf3ecab44bbceb78aaddd5cbf3fcdd96c565effbc86d691de64b3b92b632 c6fca24cec33e82d8673999e24083b3ea62ad0faf45066c39c61b6b87d86b034 47ba5c4bbf83ba84b221b9fee9298f6b254c84c280c8900b0d501a716296cb22 0fb99d74629d4e51f559cfd6d99d99a39a535dc2e3bfcf9ddf9dafb40e6efd94 4e04278836dc5d6b7dd214c87cf610daa4e23e5b4147344d410c70984706bd16 65a827cacc4d766d609aa5447ffca053e994109d391cbdf5a7d5a574475ede96 c1c72ccdb59349d30e316fbdc47b6d8326876f72b03203d4bad192a363ca92a8 4ddfd90cce869d224a7cefd001717011fab743492962ecbab80c66edd4274921 2d9202c8cff9a2302b579f9b1ac3a5be9901a23980ef6e656543c49ce27301ca 8b08345a7760dc06fa0d8ba6494dc70314736b9335e66cc5368f2db9926af588 a882a825b4428472433d0474759197c886944f9dff9653906205ffd6d9ef38cf b1814c639b53f4ed798005461ce8756b663d3ef618af6efd35774fd598704353 de7857dfc856bb32f8617a841bd1b8df0662b5658d6a282daa3f7380004205b6 a62512d40eba9d046be76436bde3911e59c5d642368deda698d28b34b4fcddeb 0adad8afd888546aabaeedd47076fdbb6bdd0ceb53458368f94ac64131691df4 3f26ccd13ee2bd7d887eeeff852b3dd009f0487ed4be4fd59c937304a48d7bd6 94e7848c6b54590525ed76c5dccb849eeac8060713e3a0b75fb62d97f24a9c7e e756c40d6601ee324408ea4b6e0603aec40161d5d101127394e6d80d54ae330b1e8d4ac056ec4b4138c41ce0adcfbc39a5a2c69044e28f7efb691b93070d950b11a1144bbddf15bbd3bb1e1d51d8b268097702a737c7bfb32a7b1a64f404ca092d11db79734ee1f049d74a1abc41808e275f893099533d31444a2c0ab1f9ee0a6ba2e006c3a241a54b9aa31855937ddfc5a99c3c248f69c3eb15b342df2ad503b8abb7d5259b4ac28a3bd2735fb18d4ebe43d0bb9832f80a377a1635164a130a2174a92ec7f2651f730a4fb49bba006670b3f47d6f7b7dfcd0bccfd29f570c06859c33d2591431c9ce115dac5d57dad474639e8cf2de55f735df3a05f8acba0100419a5ae96c50ebf521cb2b75191b4e25d17f95fe4020cb5478f0175de75b057d1dfcc650fd23bce39a9efe81bc6dbbef0f5c7873e400dbfec9f4fcdee91d045e2f308d19359f3ddd49414191cbd41ec1ab2266c2356141c5321f0ae0501b0574bae1bf2ac9d9680c2541303e034eee23f037efe18ce59bcaa951e33212c103777eaa8c0ccb7ff3d7d769d29d9cbf5f503faba66f0e27d39a6599a20ff15c00ca2d69ac161d1398372b8a63ab6648511f92036b1784e95c5a49102ff368810a7dadc7713be8446a172c880d7fdfe1839d903f85d89ff76ffe982bc4408c8a0d00693449842a3c712e2ec69a7ebe3e66710efdd258486a37c1b40ad1ccfdf80e64f9603f377f2467d51d81b7db0858dbd2b51de0057ef579c678fa3d3771340310be5628316db239cb1bf90b1b1a9a4f51e8b8bf04d0de09411e6e0b1ec675083c23ba3f7d5b2b3e6099edc6b2222873fda630ae8b70c5f16eb137ad8e795605f7c6e497f5705577891041192f8c8fa37f355303ea28cd0a0d31e41b03d3c80e0709a47080adf55b8d15afa5d4cb3aa6ac8e254e3b85dbb874e83dd5d044450fc8233d3a073080c8b6b4aa55c869e7115dfb818aa1e2854a2d769427c6465c094ba8342e349b6019da009e02beb6b3ed6292a5a5d6050305e795f1453859a8088cf83d491d4f1611d5b3674b1372ea6b7bf39bd81a5a2b83896c1dd54beac3002660540fa0b2c2886edac643d5f0a7baa3f426fcda4ecff5a62020ddc97b040f695158e42ae85d67ca10acb49599d0ff251e63a1121445c9e81f2709722793017c575122908886650d7607c981d13d33b76084448896b935bc88571f378c66034816597e804e17c8f52ebc0892451b77b0aff72d25746e8a76b49623a6e0450a3ff282a8c426332646417b0fd34099d5aa7975fee85254feabf91ec0b9aabb0c6ca2cd66c9116f3861c06b9baf006dda7123dadd37795a60d0ff662c5a13c708d511da21dcfeff6fd29b25d56f61fe755c023b52846a091f47f2f962b3783d08e5950813c1737d58fcd1441aaf4cc2c07d241d10b6f9a914ba5f435fb190520c26172b9b445882d53c4970c4abd503159e6a82fe5ef8e38493277f04ddf6af0acd345a732a1a579a0d04319971034758962cc014583f90552456f2ce4d0b060e7cff10de66f23cb1c02894c0166596e9241d752abd0f272beaa7ac018a6afe04a94c20fc45a1e83008c8345fb52db4535b619334adaaa91986f6ec58933e47071d1902c27c93e840917761ee26ec79b158402c7ea4435f7d9ac51874c418e7052b70d14ea2a323190fcbdf1753ce31ddb5215fba0197f01224e029499d34b6074064d43e5350efd9ea435e5a638a42d4c10b8c1d41e513cede24a718b521bc01105478953df4249244029ebde4c41ab65c01301d775684ec9a2e212d974f470fd82daeee9ffca75676fbf3eb7dcc053995a610c105490477dc3a053ed1bebe08696b7d8c04f310242d7b81dc3a2e347668034365d7095742cffdc741b097cf093c367e6b196b79c1834a71a6fc3276463c505a309e90c4097245463b3d1819002b757666db41a249f8685f6c49b435d2baec0e1384b67bb326f3edc097d0c10d064835569a776f319a614f4705b7c2e2d02a7c58177b389a8b8a157880517107b28a790bd490d0c5707e5c3c96adce5fd65d876b0539c41319212ff432a0510a8c7ec31f15d4d73dfb7b9d76d2a89937c10b7f9cdfe308f46abbce76a671170a4360fc6e373003949a9bee3bf5725672fa70ddf5b202aaad0c4ff950fbad8807e6ee2c6101dbef4b4176e7947fd4d45a4b3389407589c6ff68c7091a02dddc0963317c1b748ab51114a9d105510e863d29c42e6e2aefc7a73e75453fc0511f0cd6debd465ebc5e71a5f873da2cdb1b15293ddc64020e0946b3a21ef22ec1e4010a1f7007aa20039537405a17a01844576d7fae74e4c31d1c5729979a69e1c6000adaf39dc1807db0507684e5bc060964765874ee094d44f2498850201038080754f2ace168ad867696c0d731bf87668e0c90ad2b196f04683aa8da4ccd35320c4ecb77934859237db1536765b29594ac111ba7343286aaa5ffb1d58312e660087db81696fe2d6686985b6fe1c1c07be7215facd8685c3bb88cd30d34bc38ad025c1cac82cc44d419fe4d3682009d8df5217ad3f91603382c8edaf696b68c5402bd5030094e78d315a9fbee3d4b41827279d919c881e5c4874ec0a18b0e43bd03f2e49685b801483a00ce6f86464adcd4df756d49e0037b6209e7eec36cc1a80d46b95b10f5b398f91d213d94fb2073940d1af051fbdc0483a4a951a6ed582a07f51dc40c4aca320604c1b5b6bb1d5a353fd8a157884f93d1522d99d693a64c0e9704253d3d035974d672011ed7184afdf3464d066f77567a8cc2e9509833ec0f1c11c76642c674eee44561b0ca9b69fb13f71634810d2dbad5d50e7d04c41f00c696e3ea759a7729fbddcb09b6e3a15e2f276e5ccb9a6b3ba1459d28d4a79900 false +check_ring_signature 4c25791df427eab9e440a6ae83bb261bee5b04c42a8dc817db22c3e6756e1664 110b1512bd98fc7acc4e585b9db8fb845e2a311dbecf03f60cf3dfc784f966cf 1 3351f06d2f773ecc11f8a3deae94f89d199186dcef7707dc4189c0f8020a081e a4c3011a3581ce3651109e30af8b78e22a0c52cba7ba41d8f3444b402578d9080afa6ae64805b53a045c09d89edf54e15341e47b031e812026c40e026abb4b0d false +check_ring_signature 0b9218b0c66358e37180b806a4a3a9f6d7b4dc06d2a5e139e0e2d1debb43d044 6f6604c37c519ba80d78ecac3276b8a79a836570314fdba51c45999df34d2261 1 08a19cff84d5bbc32a70ec530de65e711d051ba6656edc0a2179da8ae9b0177a e501d0c22c299d0013975f934d587f1d2e0ab378c2e5c9f6761b8d4d2d7132f1aa7a60f0d6c20a79ee3f8fac78e73e2797db2ac4c09f719fff437ed10db1b20d false +check_ring_signature 2609c9684a082f4483d8c549ba68eaa9b5dee85e96734e170ab8b71078454e53 8ed3d973a173c20c02ae20ea9e8923ceed531bb90e4277597e52cf41ffa3c823 28 4ab5eca04c82f8156fbc7f65e063812d8dba785d81c1c85e035fef3f26fb5bf2 3b5deffad0203137ee3fd116182e285d173c0dd6f5a94265bcada73101643290 37eb5b2a64a86e4ad93bcff1d466369966107cdd1e20a2a5dcfe46f69e409ee3 e5f5866b059edcbd3d182e2575c008f67c7025b8bb261a0d3cce9365384bb2ab d935b110692c8fad6d95fae63d95a1dd12fe8b6d53a0214514a3f223bc6b6a60 25d36892a3a2d5a7502e7c6f0686571f97fda1154bce366a651d2a9b52c1f9de e73e6a88e75caf47086c9b29396b5a2ada40165fdd18b7bfcbce00ba0352dc7f f50edfc836468e6b24b97a054285d30bf0b050b0235122ed43408f1162baab41 646e7d31fb3b27281cea0f7ddba3a07f551bd38a07de4dd51f6fb3cbd35afafc d9d34329d13051fa3441ba36701829f6829c26f61ecece13b3f82b94a72a2e5a d2772e635891e12e6ff1058769fe2a1dc1410c7a226bd3b041330c715a6abdb9 a6ee3c71cdb758fd3462334af29a28917990f3908e3d1ce5fa0e69e6f27cd2af 1667be43744674aae6a9c04dbe586f864b2b6bc116f3c876b3b925a6677f8fb0 88536ebe94b552e970f7222814779bd4010b03f4a5ebc7bc236b41a9aaaa8cbf 694b93d239df3d10363fff10f5c2f61c8aa00f72d186de59c113766b3c6d8761 15779da0fd44541bbfac3048de902edaaabdc158eb5e4d15b100f65910b5edfa 68c1fed2c55323a28c6bab045aaf77e3240dc1c8b28bc75e2105f62bcaf292c1 9ae1f53851b8217f8a10e8ba36dfc074e40862930f7c9d3ad9dac62bb3d16549 b393286c9ab2a363fe9aafb6aae7c92d586b24740909dde665295367e5273e90 3681c340e0a0c5fb225d5e3409f2a64565d3223fd7a47dc1349ba69b44b25714 8bf8ffbb8e9a1f2c4304fc8438fcecd9da1ebaf7bec591fe729af9ae32ba9f22 becd7c9b252b7eb730e3e25818ecb8f2cf0dadf79362821bdf3f67505dc3a946 79605f97fb555ca0e0383a9cdf61f40a9a1213629b363d10f48b9d6d9a5a210d f7f2342ac089ef0a3436b1ed5f6e8301459dfbe77d87aef9a42d9802d42e041d af6acad22bf5d626428d38a6bd648d0652d3068147a2c7f007f73860f6ae7630 23a8f939b505acf240809f36ac69867ac3788a1ea04b2e6917d4373b24d2f993 2c20b5093eaabc6ed27816a929d2447e7ed5dd7602ddaae64802e060c7e207a5 400a83d57aa9974cd21cb5dc5c0dc27a00fd7130214ffa3f53e86f369db35be4 be4761e108ffa6a0ce4eeeafef9002af6920cf76f958449825a3fe042693550fdfdca479a4bcc0ad726adb385d913fa10da5296e40d71424b4af1c54dba4e703298e4fe8451ae231335af12dfe02eaf8b64cbc8e81738e3fa601827f21cf040f4da1cd5ec9ab7c34b6537846a1f3397f9cb0bfe61c3179c404a4a0a5550e5402947f975de131b7bf7bdf2c1e791fa63893cadbab16ea236c40273787c314ba059785b1102822e496086b7bcd18a611ba8ab1f312f2f541cd5f75565a654cf805480f42c2be6d89a1c6b7cbebe80e93a98b1ab4ca3476b30271b7b58350da710949109bfd9c5664a39fb81f376ffcda805826473b91bbea5250268f2422a8510f83cbb60ae0d14fd9eebbbd1adb716cb54e4c5c19874547488684f6157e401b01a23b02dcf2480aa4a37059c487b88852dbd7f94c1d0d8545e1f6423887988d0d10c70c52ea4160b2c9de08ece41970ab1fd4a628451925895835565f6597fe0195c16937365e6a3286312f1feb1e2acf0b5ab7acbbe12e41d1b23547d976fc0780ac4dd1fb465567c2b67f762a3578b48cfd0e175229efc94942f2f71f325801635e8ae29aa185560238f951266c6865b13839c3d812ae2c60c88694e401ff0c388c230fed95d8abc3635d9eb8901e2f6aa5b3a879319906bfce92d79c715d05745f72b43c3f1df5d6a43aed66687261b0c07d7ec18578d14cdbe3cf86dd020423e25f8a97afe6bb3c4da5c897b146fa0318541585bf684c58793a93ae52440951d602a6dd8e5683003047d274c0331474d7abb16eeaee177a267dbd44136e07d07fb21055537a8ab71bcb85a90e014e528af8e167313b3093b1062123aef3008cde0920a57dcf8399c4818bc6eb458d5a0647a6ad1c9af7933179de4957aa0932d4d0e1067f862aef6b10df4fea90772928ea18d5078309702196f1b1b9b007b3567764e2ad91a5917246a6dfb87e1a2b42b72926225ef8d29ea4ae9e1bb70868d1452cc28bdbe393b88c26b05539ba7cee80d8e3b6a457fc2114212b3d06014cfc101a2da0ccede413b1c5844900a5372baf945c6e3e4955ef43879fab2d0083e917bcaf2fe12baa02cd68f3e487c93a2bd38b285a6916c575a8c3bd643e0cc18b7704aa2aca19faeaa96dd29ff2c848de298658a8a9bdd498f98a19f1250192f29266be2cb0e6c48a4cb4da81128978b1d702a0a88712174f5f72f9154602b16d28288715c20492d6d59314b464d426eca9878bca40fd3cad63cbf9eb150a759fc9402b536ccc58b4ee63acc8c569e833ca274b158218e5ec3f7dfe01e70bbd52f2d94a8ea4e3b07b22dc6550c1c8ae0aba0cbae191757ce03fc8cc35a50885812d3da0c0bf097f0cd5b2829e3fbb4bcc1ae0d66a4e808e506bceb163c3017733a8cf51e1a42730bd3d8531560f5e11fa8f5d0449f6514cd8587f4921d408254c990689006f055d10dc5ddb63396c78430a0c9094577df3a68d0e1ba7190729b5da2aee5850bd49abcbe901f7b1d003dc723aa4c622d63a271fb02a6b500d209a40c9d5e262fd1092be12117a6b1ad7d4af9f0f68baa1db35572d7a15c20322b1eed5a477b8c07b4236cb5398b958cdcff88549d8deee9288be97ca641304ad50109cae3c0532c1052065204e5e3dc9dc1e72051256b4907421799c2ea00fa257c9204937b805a97898a75a8b47ef1a1b6bacf7f26e8464643cef2d483f048780452558b9cf212be40990e8cbfeb8bafeec4e6b6bb486078c3a651436110557739e4d719145f68515961a0ae3ffc5bb73eb304dc82a9486024743a985af0c10a5f0b88ec617501b5152d4985ce986c2646c769dcda51c995234cec25fd8069f2cec5a2c03708ae71080dd5e193259e8a3cd47800984597ec2e866df71db090c2ceab2672276e1fb01c1018f11163cbf43d3c0ec5b774132ab9d1f7c061a0c78407a2bc8308617489fd15c4bc49fa0722b0868b09903e3f3f55aca87c0630126eb5ddca2c7656caad2815afd069ad6c527b866717898f87335c9685b39010654736b60b6f2d8d5139990768e0bd309f7b11cbb0dfd73180292a8501d38c808a26fde76c509d084e70fbb33ca7758747e94fdf49ee2f19b7d60cd580af6d30f5b4b67813f6828f55359dc10c047ed1252cb51c2efbde01a6d7c2e06d7b2dc01051e284ab0c2829626dbf7663c86e2e8491a620a71549706b58079cd65b1300c6b98e017e95215bd6af902673ffb8ab68d68ce3b29187fd473c1c7fe30ed4900335454c33fdf273f39b920a664cd7e4503151062675142a99e374c59a3ae6300098d6365eb0ecf28607c68a77a1ca9b3bf3e1b94add71e0d920a45ac185b2b028e2846e13799c56bdc1dfcdfe4c72a39d0c84b872bfae5ff1fd7a0979eab4409126bea2ed372a519a11b0d2b71524a20aff58de2c4b7a024658a07db79bdab033db0dbde244e95d6e99c8579547f6d00a75839bf16aa1665c73e911100e10302e97c893838853a6e37fd0504ad84eb281826d97f7eb3116702875e81a8b7be08 true +check_ring_signature 46ed120b5bfb57e6eb98cb8c006317192c0d3d98253d3f6f037a3198543deba9 820fa8416cf9c51a915220c3722327e6c1fa559e5b81f421a3336249ccaca4dd 30 d0b27deb567e46ab87ac9cf25f1ee4ced46a8593530da474b27f10cb0bdf61cb a6497a11ae6fbd55dd5b1ba144469e108f68dcb9db6bec7d25980f0221feb9ad 1ec518dcad7b476a41b187a45d05bfe6abfc45f540d1385c4876d46725d925b3 533cb8bc47baa4cc46900787ecde488cd88046ac9113ea60425b0688fc2c563a 17c8bbcad812d5894e528557ee7f53725f223875658af1adab6738b402decbf6 0ceef87cfebd007e670ea05369233b65d4e3fdc04f9b091fec077b56d2bd35ae 8dbd987005b3f3f09acd81d5f3c50411254f36fcdd7f9011afe52d3ac737254f bc8bd7102d26dde548394cfea1551f061c15737bedd94c5d53ea647e9047da4f 77bc285330eec7cb35eeda550b6c79837c8dc1bcb38a74a39115be22d5e3d7f3 b775b88881b4240016ac12ffd4c8eaa97a40cadfab5667fcb73044552839cfcb aa4f18777f4084b46341ae5cff47a85f4cc86775886552d5dbac278b762a6206 bdd5159e944e0818149e311aa53593e8d78d219b8282a61f50022eca7dc9ab4b 897bf7619623bea8ced67c7cc88cdf92900d0185c1ea9adae31f7df9b344207f 254a8fb886225f897afeb099b8c87cfaaf10746cbf21d5bd9ada563cd49eef50 4821514ea3f1a1cdf7c1f2ead6c73ccb3402030ed9e11a5945a14a32ff1256cf 0a580a9870f5a1652759dbf2cb404598169cef4ea08df7a5e52b580631a2e893 1c76d5f65f8a4b1ed427f80d3f96000a13fcef3bbfcc39ae27c71a4a5b22bd29 ce97f00e45eef1e95e5bb2f2ee454be394cce330aeba9f44baee077bbbbba2b4 2a15948154d0f9cc5cb1e71b5153fe38aa795088936160531680b1059681a09a 14b57417b6706c196a9c0f312c8ca7b1b1f9055382c89c52bb66add1f76d5a0c 923df9ab07792fad3cb97b11329cbc6a5eba4fff2270ac40883fe6c46a538674 7dbc6b43171cc5fc490130bc9141f82c876761444dbb1a49400f5c6a30d0998f cd7a08089a17824f05cff72995b6ffac89e4a9d396688cddc3063b7a5832086e f7ac6d9ca3b38cee0829d064717981e49b6aac49aa3d585149c8d1c7eb89c269 7c790a6b545fece9acddb4d3d04b3c858532447130a58a782b2d79640751afc4 3647b80fb46ba919e68658c40f2d553953719376862903321e59d310a993367d 446c135e981793c3225e22715ea715eafcfd7cf4088ec4463c10ee77e51f35fc ca71375522af15e732a8a553f88916cd66f0e4ab61a471cd8528daa205f221e0 1face70b02709b151f0b4b306abf7bf5686ce8fb0caf6d5e477e642e7eb28382 3e8fcf6f20dcccc1f41403f9e21dbbe58f08fad9efc34d7b3860831800771114 171e45e6e466cf4e22d957ca573e73fa79a4277efcd06caa9585c1d06a591a092a03af08eb3043b680cf64db363141a0ac5ff649f3bf65b9f61f58f491681d0e9d259242d5485aeabc44c53cc9a304c086271d75746744f39af6248db2d7110474edb6069a28fae7cbd0c4fc49a039934571eaba8f427e35453ea6d96ff1a4886a71ec9e35d29d5b625acbe2df3c90ba586ea6e725fe3f6b32071a226756110c761af251aadb3d8caa6cefc2e82765f8d79d33b2b13685ff2e5dcddf347fc70726b4975ec6e5f6143b5d3e5eec9b12d0a7ccb1fa997819011941bb1b2b260e08df670e6e5ae93f14e016ba1b1d7f3d539a55a6dadd5372ca26c9cf6f42d1370fa646b22e11ba56b146e69235560c9b4db6107b3555d70306292c29c399ab35097e342ccc7ecc18128f7fd180bfe440d7ccb6f5571ad339fb2506eeae31609d0596ffaf54bf7e08c25f4c24accbbb27dcc8eb51eeca35c4f8b5b63eb3ef0a120b1f25f1ede6aa982b1dc5810b5f10fbf039799e15de17b3cbb23fe2a8dd876d0c3f1b134b4aa89cd96281dd9d5e7effb50bdaef494c2de20452ca92e95e96c4098eabb877e2147bd6ceaac52ed3d1c2fe7469eac2eb53e5c777985d71f068bd009c8b79b1bcb91fb04f966b482a14548b70b6edd00b89fbcfe4955ed438886b0d919037d678f44870c4c5ed7a16259f5e973940434c812925368150b2348e6d0003ebcc743c8a9f847b4d9a801e016c5cd2f3b234ed0ff21a82e70908d26e580fdb8280bdc48f97428bfd08eccc9fdef4542b66dc27999d9df8defaed81445201913655180f0134c991f4dd7d217895745b6eecae3d597cff82c167eceab2f40c0244474ae23c83d194753c7c03e794a093251f4bd84397bb1739fa3c927c59009035103a827b4b4bce8639a710aa71f13d40766d3bb0aa45e94754323330a109547b1e00c33ab998fa0f2c903f8d6c864647beac46688dfe54f420280737010510060e19e1363e1345ea2f940300590a0c36593f07e5a834cf2ea2d67768aa02e830dc0d1c02db2327cda7ffd08355699c5c036c6600e0756340a8f52c06280916a831dd5d9fe4dd466027f2b6bda270960fb361091595871227ee27c387f503fbf41ebe983fa12fb680993e8a6c36bac264ade6f0d87126ff14f0a62b12b408d975990a6c05802c478c496c56cec7408074ee493757b91b2d1d0e1c03fe520e2db9a02f6924e3ce699942ca3a9cb61752664b334ee8dd3763411cc2d66be70f0fb29f929bac3937bdf5a70ebf3e0b6e9f2015b2c7eb64f8100cec53c652970e28a2b66e05aff7acc9770308e99468615f1632d2b14b14a595f8880df507e2042f7854e10af56d19d54318b91b10d81e64e825b81a4e716ab3855cde0f8c8d0dcbdd3d2ff11eb38b06dd1fc3fa0420d3773765a11a7b317cac00dd600d7f8d05feca10a046c88878a891fd71f63fecc6a6a79abf8165a6b9cc4d44f81b47f307307425df4a8f8fe86b6cd941dc4b2537af60506b782d4614f94bff55a37b67005c212975b88edaa4410778c8172c899b56b706afa6d8cca4e1f03f5bf21b9d067343d2a734a9822772ed60cbb2cc48e1a61550c9810f1226b5d547378425b60b0defcde6fa0eff51f72fefe65f27fc3cb39db2caddbf9b4a0602cebe61e3ee0eb90fafd4d78e10ca4663e91f8792604a787517e52e1e72f1cdefa3f4d4c5aa0b0fca3e9e25988110905e036b7b7ffb88a1c2a4d9cdc9ed7bc7b603a67ac67a0b5a872f3346996d6363a0d7fc5bdc28ba606c68746b416ec27be7f91db91c070d5766ab62b9198831a08af1518574428f2439f75020a87aec3ec4e595d487ad0a7d30293d758088921689a7795937ed12340a2c5505d300cb99cee4cdaba0230ec96bb75f25b8620948c23b0798a8ca7df2c0457ceacef582d6ea6f004a8b9e0ea113043b024267a3bc45aa4eaafa8a552065173973dda47e8043a7be443f0c084f953ba14f77a50f35d8997b7538e2d9966fe85d968e47635fa7c3bddf3ff6081ca74a1f206a6c26e502b8d659befa2b1f6ae03dce290762ec55b23a959ea40e207eb02f193a4c634fda850921718865504998cb455847664e62145d9938d104158e4a68e2c7c8e0abdc92bcc9693483f0a893ac5008f41f4a4cc3f275a70c038dea3253cb618aac938104e68b213aa4db77e7179bca5ff495aac6c4684a50002ba5091adb12fc12f271bacc8db994e4c8d4b75b7c17a06dc6313e5ca66ba0f5b135c864d32a2cca2993febe313c1d8452bf08b4fb2cce8d38e159d95f58bd0d96a30dd368cc77d40365893fc0fee822f7baf58dc299c7a2f3d1dbc603c81f051968f77da4bdd917b886db69a3f51a580b34b47a5f443f7c0ca9b25e9fb98401833fd427aa956e7739521503c3353b2393240fe39b10db25d71eb04765c9870ee213a566899803d7f35a503bcd6d5540c7b11f54364d863064447bf1d0356e0c91cfcc3cb42769a1fcf23622b46272164a90d55527c4df32370cfe3a4717100714ad92aa61eb28882539c0bd9882ffe96aacedfcba9ff1f5eb11e37b7729740e99dccab136ceaa8ac933707b2f643b8efc541b386f7553ca58b3e0046ea61608b3310bbc146ce5415247cff41521d7d53537ff2fa6020c64598c567e2baa3506a288189844b59ac299ba874bda5ea4a0c69a50d9f12ecb46c92e8f3bf5df520c false +check_ring_signature 9cbeaae6759d1fe213fa0ac4726473733106f2efe92d5cd130ac4fe61a7640d0 728e9e8bfe46a416baadafea966f435dd46da72e9507280d15b5a884848d05cc 8 27f186a1cf760e3371be3a8eebf39df20d8825d4b6b3ffe7fe4d1cbf1f62a1e7 235902d4594dee94e9ba396c061f9f4b3c09c7816636ea5c86af1020674b5bf3 b6832c7a1a7594e27e884234b491bf3373336043d6d7be2dd1fb36611d1db49c 839eb56a1dcaa470eb203d68ef40c87c1d5f15e3bffe8920604bd284c61e9be1 005e395132aad612707403c5660f89c37e118c9baf07d8b3c475482b8d928a85 3593d94ad5a8c917ec917340f56946afc445da7dd770fc09a5e0d05170220ae6 8e3b30bed729d97a63fa8658be99b89eb654773292ce30324314e3925520c868 98098366254809eac16dde62dc7b75e7134b1efb51575ac24cfc1f2e9f29351b b98071f2f088ff8020ec2888e0f3332eed93fd6c4d2ac761d7664176764b6f0d931c8544938ca890eedd6fd8e5965c9818aa30e85aed9d4b2a2aedcb9da9150c15d2d7dc75618a4a7d52602814b118c81d8909553c9881e6b9a874170919af0094a923aa27b512ed67d84aefb00f67a3bb3d88bd4668de829774d7c42d79100efcc41aec00fc21d1551d8e6a88113bc9407bc51c4465c48b3f31f1c24bcce30b9fdef4a0b0fc34a803afd7ca04d547788875ac5748577d662df00a2cd535ab00ea05c8f179f7ed2655fb5c81c9a8ff052f5a63520f822dbd3468a3c4c32a0c0f4c4259fe3399f765eacdcc428b2e799be9174b6fabcb2e29066b2dc044950c01b1cf9a62bb1c50b47b8d459a2c3a860eaf46448a992d9ae616bd111f5fee790b3001d40a898223aaaca2e1b9fd86d68a46d2d8ca3430c7a2899f29c623fa2b0443c07d482b6fc981f3c4740c3bd7998a6db70dee569d1df54bf415398e6b3809479e544600cc2c34f35c86a9b47a72e352b6075467b048924689840bd93718045dd9a0f749a6a4cfbf30a69a6b23d607174e60f1b8b4bb96fab5a3eaf5d1f60be486b9c8cf32696e817f84b939c7f30a28ace04bb3ea0bfa97a0bc3c6c0ed1048ac6bc27a1308b7eceb001845ec3d75007c6c5bf2edc735f99725a84d85fe9048460712f327f854dbb5ef2cd9c663c1356a26e293c96ff47c1b3dea53e28060d false +check_ring_signature a140fc279d7789511e046d02cd25d3791f243558f0d9f4366db1e468d6bc32bf 1e2767f4b5b9afed3bce8a4b608dd2aef0f6e8f2ffb5bfc0dd2f64f69926481e 4 297b59fd9af395d44fecac9749e1f0e58ead97dea5f645081e72a10fafe34d84 27822e39725b43b6479a1b8ffd9b457a528b8800c45a6ab9aa3d29a506889697 3cdebb0d0a18f96d1cd4e12de37339702c894945c4eeafd1b18dad89a8bb806e bb9c4ce3d2aa99a6caf153b931ccc056bc1700c5584c6aa4ef2ff09be4c12b41 17bd3857980de170d09c98b48b6f0225f4cb6a8524d5ca41e9c128bb89cd7c0208b410eee859ca366e1467e887422b9e50a729efbf1e6a08287ec34457bdb4026febfd9e4088dd5dec0c630eaafe7f4d4f70ebb35206acc673f19660aef06f01117c9b529801c5afec2213743d40ca3b307153f823403cb3effa5f2a2bf76d05d40910607719d4956b1846fa5318c838571bf2a237b995dce3062db65e1b8b0663c80ff863720a0598dc4dbeef886c8529f5328e2d03ba4a379588e997a3a22aa88088e973dffe11147d249108267f235e644c4b9c7963657a079428cb7d5b07b93da50aea32c6b80f226102cdae9530b0436182adcd5905af602ddd0c2df601 false +check_ring_signature 07de2deab5a7a8d529707689a945922ddea204dab275e8de42a4c6169c152719 708ae87b2c6a1e1cdec3b9be2b833c5be5de99d3c853404d2d2b32517f37de8e 1 2bf3fbe0d0b90c9f7cd1a4654ae56349ab3f3e588e9a84a64a14c0cef36dc28d dbe5bc43e813bc21ca5dc64f9836f8e07ecebc578b498a033f061adca9dde9f722731b24b8583503adf51b24f5c010fba8fda7593279dc8dc18c066bf9220d0e false +check_ring_signature 44edb63e6c4d6cb253f82ce1490acc8c75fa554639f981fc7077165157b13119 6fb270997c19bdd64ef7f5cf1c105d17f949a251613843dd80e9fda548ab937f 3 abe1e12237d03e8fd9740ed5f1a182f9ea9465cbcd6ec79fe094c6e034c018a8 a58866c30bf1487d5332e7bcf5a9ca0932cdd340fc41da5c5370385b125ca438 fb4e7c6e97a5f76b4280512b6140d8ac14453e6718e3ac305f28b7be8684ca5e 28bbc8b1fab28dd735c62ce0e8de0fab6a279707e87f48150875a280dd9e9a053f4581978eb823d933fc1ae95928dc6b3c64724699f0a0dcf29faa2f337ce30c5c903f883eba1e2410855bec110ed098388a6eae6d9a880a4206c9288f3d72ec55c224581f278adc546c4661f8c6f4b0af15fdfeea8d8a2b33042bb8be647401e30834039c96eecaae2013011cf603be98d931873395a9387eb0073eebbfbb0f6b7be144f9644b10394b9a88c527c1366ebdf65a28eaaf02953f8fe67c2e9201 false +check_ring_signature 64ae40af0886dcb6c417b40fd5bba9dcc6114139ced44c1d449bfb88e4570d2d 82fdeb4478a23708c69eba7b3e59639f0b10ee9802033acf44ea07e7a27879e2 12 a05a85e95124e1da66ebf1c1a8446f1c473fe4961f78a2bee7bc214d0a8efc22 aa86e5330d2d244e33a18c1ba3d745ce72531c0908cfdb5f3bb52a224d934e63 3f8ebec2bcd6981adb68f5d81fd2d3971eacfb3a8e3a5448a32e2986f20e279e c437f46625271e0928f27af846f89cf9abebe7d895a7cdd41edac5db42e69c3a 616578b8e15d2133a3f33fed4203d1cc52515b3e8c1efc9f400cbc6a561341c4 69edd0bd85235435b72be32c825990fcfb74dbec54d1b3bda3665cb2154abd44 ca821f389ef2a88cc28b63c04ed7476ea5542bd6fa248db6db3d292a0058f008 29636bd289df80288c0593d20195ba1dbf187ec41b032d90aafe2017a43e42fe 4bfceb85762d45cdf1142d3f0f306a0bebd4d379ed2ae4e39a83a7bf43a881ad 0fa89649b9765e5d228ca25f095bc2f07a3d032bf0557a406708b31c89f969b5 ea541b2d4f854bd09a95a823541c4753b5d61f058c2dd8f6a01d59208a3717e3 ded7540ae46ac74dc8d64bef95f0935727048f0b30a80dbb80efe06aeebde88c 510dfcd3cf3712292b127a88f98ad9a4aee1452834ee69d91dda18cb5ad02e056c669ba3c4afc21f9e085107290fbc0e00c42475f22dc865262bae09c2e30802efc1a7e3bb14e2ddb24089065bcf6e774d452cddc8321ec3866664aa4f983a057dee056ca2c4feacef36b7f1af6096942a17b6943a7d201a8019913e9b963f055d03005134a5dd2bc343150d7a73b9e72e27bb25e1823aa651f259ffc2d87ad0b7e6f4e3148fc06ebaf1139774920bd61eeabed669ff429b51e404ce9ead7006b33dba8e106853b7e6dd644f4e783419b214849fe329b90b75fdb8fccad1230a641c9437d89a314126bd963b677ac73b84c7aa8d8a5fcbb1f49cc7565e7cd4020ee0fd0ade27a6d01a412df83e31ff3607029fca70f246602dd0f49efab20b004fad6dee09b84580212164091f5da10a9d63b38afc8ad9e522e5f43c1a1d5600ac755a35ca637ca1a012cc0f688d1e854041db9e9dc36b00fe9ff527740ec2099be344c476dc3837be46b20dd39ce01722e12a5aabc14bf79dc7922db1d82308fe52a7fa82a519e0b0c65f70761ff09eb8fc167569677283cd0f53335427ed0b1d4b0fb90f40b4804a68c53f16d834827c89b1fb054fa54d02e6b41591d94b0f42492681d9e8e1c7132bc7e33956c0620f3f4a2cc5995efd016ab4855369c20a9074e351510bc155c6d7c971999717df0a46fee00cc6fa8c5115b3626124c60f3f5f6db78a56b5a0b1c28499e2cce53aa419b8fe6f9c9c5cf54df03f29821008f89d72a4d7ba8adb5ec63c48cd2f423119517609728f9fa8433562ea95bca60d1f5e2244a286dfa2ea235560aa041233f5855cae6ee6db361e738fbfe8aaad0dfdf328a63666e5af5087c20d568abba834b5b15fe9d81bc81479f1323c720d02fc954e3f3239dce8b553fb24792fc1d508788a53c73127a9df2f89854218640cacd47c94aefb23db2e3898d0e19e69c8a8d7c9084fd1d4367b3abaa472b9d20608ac4b090310bd661038cd81d48a2537d40f2f28f9336b7fca7fe4e1a15663020a07e865d4b72835720aff1a2df77a3958a77579ef0cc78a130984148f6b4906 false +check_ring_signature a31331ae08fc24ab562441f33505664d13be05eacde9b092a030b2623ab0fcbd 5763a79c4cc131f18bfb5f105893198ad1abc017c24be1d7f585c747b8c99460 62 4510f854eafb3b297699a0e121c7a702c9dda4524ca6200900a3745a34bc69c1 d6cb62a9ee516c439bc7a9b35d7eb21477d6ed22ef388c3f2d55154fb4d38ba7 eab6e62fa3dca4195781e32e119572a74b2b2d40cb607a61ca0c2aed53a1da7c 82b74e931e45c2fa6b6ac8b02762e1e62b66b98478824ed7d5ed5683965be501 fdb56eb914756cb4156feb9c332334aecb0e5c2eb2cdf262d74ddffbaba031d0 acdbb3f83f05d0d2fbf913435aac21e9ef9223abbca6653e2dc0c4618160bea0 608d14c147f0368176865cfc0ef1235f2cb95a1ed70df4ccadc630c88ec5116f 571c7f41d51e3a57ed5d70e493dc5b4464e28fe722649e25e0c564d99ad9d30e ba35d8bb9e397a8f4b96239f1df8d430541aa62c17175078f5256803c7866ea7 86edb3c56a025b1bc78b6196d377fdb634ae9abca491459fcb2d1c672c58e0c5 e95a0db01fffa4e2e6a9e33936b9ed9dc347a2eb0754385a046d1cd2e599f092 262c16725d95132f661d6c1f5d089ddefc19495675e90371b4885a95c6ad3801 a3f4c6f9bd12e65dd78c541494920ee580d3be7d9d5e40e6959ad33e00baf546 b3c470cb2ba66d719a1d0e99251fb1b2fa6d621b9c9370f8674e20df81d938d9 0bf4ac4effee32e80e34fdbc83e18e3d31e6a198c9388ab38f29383936026d7b 6f9764795b55bc4a6ae1d84cbc5ba7236e1b9120a78987dcfaa18b450e472119 4079d75df8022c47a6e9c9660d2a0124c8d1652ed5e677084dadc03513ffb687 46bb6ed3eaa24ea48db0972783e1ea3013385de02f03bc597efa23f6ab32fcc9 f11e138e3a716991b326bf99fb2233eab92f57c533518b94697b86cf847699cb 099ac2d5bd74a78c23a348e406ef7051499e68ecc70c973b1656b830d5ff9ef8 195d54c7d42196390266a6b7313323e367ac1addd2cb2e655f7cdaf3a92699d1 58435a574321dd157090665427d2f1248b4993896091a935aa820c4514ad766b eb6c7b3158e7ac0ac17ea6b20d2668398a5a37d713760156583a3c30ec58a9c1 23a55b70cbf71b55dc436df4cee9c581833856d1314636ae28197c25c11d619c 4c9634add36543dac65588ef0ccf207f29d2d4220ffee4cc47c407fd33944dee 470dd0121123abbe58baabf1dcea3faa3fe15ac631780806535e25d2ad98baac 7cbbfdf44c8c3c52dc6be40823ffeb92a1498a6cf604068318ec199f53bdaaff 0e33966a13498c463a344a7177bd2e99d969b879cba097cd75e75b3b704140c3 49d614630c8b583beedf5f1483d27261ef75536b7e97227bf006741eda87d785 3239712809b3dd290aaf18e048b18e06b435ec2b45dff32a63970426b559d9cc 138b461e24b778abc17fba1c0d3d8285d646f6260c3811010ff232032ba55677 461f324f6e8623d0315ba6896dfcc2cd1471135801a1f5114a6a93448cd3b311 6e220dced8a2dd01ba9f7d53887e25e9457f6d8ab6f54d5b9ff77786e65be91a 2a3eac9b0faaba601bca21c5a1c11849f0a1ef554966ccc590d6cdf5e8da3a1f 68591150c9952e9d138ff851a60770e73a6a4d77abc7e413a04fd89dc528662a 4878f420f7cb54e8e88b275a1504e20c1746abbeb717a3e6c80d91a5ac18ba31 b1fe5c8f07b01370e8f94b8247577545ea93976822f5e7934f894d5188f36a4b 472088729a6e3ec6b59e383159c1b1c7ded17601b4cb26935a2f2d93690b7caf ff01ff6304a61ad07c5e89def2d9de6d84d758a8a84110a1d991142c97f76083 91e9d39df06210ba2eee7af0100d50d3633175d40f711b9c871ac6eda7607a66 aa4042944892de4c5ab0a71b28907999bb1b6cae8bf25a9243c9403b9352cb60 633da7852727c98b5e3e68f8019c1bd8d58f3cdaf55f2f7a4a04b11b597b7812 74db011b0669a33d0d5cc5a43ee10f8f4751abddd8bac26e4b6ee3558498e29c aa481872cf8ff3d440f102ef1cbd8ec47501092bfddcd6299d320e7d025f6f31 8309b934954dd6b44194fab08938aa40745fcf077ef23129bbe11c76bf17b44a 11f88debe8f62feca3e6de598b0c8c82654435a9e74aadb0b758199062417174 a6a5b5a09dff8b01061fdafd84f5e8e786e59894b0ccc5a1e14fef945e02887a 47681bb452d875f1ae637867cf72ee43b68af72e3d08a968a39f0f103cf69c4d cf597a7a7c1d4e42ad09e5b7871c7643561e1f117df1d6ef2d005f39d03314ee 416c5199702027db72bf3aa9ab361fc3e41f85beb1448a2c356d651d8766288d 9f46e0f57e0321d6e8623f6737fb3b4002b40f35b73912da29ed4f53bdd3fbf8 c3ae561a744400ba2aed548ccffd1561bfe8274f08519d361922728d5f2bd3b7 0e815e0ed49eb19b4c7e48597406b46770e658bb36ff823669d5e5ec4b93b954 892202f957aa1aea6feea19f30a201fa67dcdd6e954d9bc36f526b11acaad70f 7a22af9a954c8c5187c8c9b954344f2502b979e43711a21c5f055974e90c7503 0013c38860b3546aada20c76591814fc25d72aafd0dc71e65bd2d4569c3dac5c fa10431929dbc297a88593d1572419f34799b5a8bf1e73bf2a69d3638ef6ae5c 865550df839c7629d9596d74bb2d76162c89bc6652f4f177b047d18479cbe0bd 56ab7d939ed2012031b9b626ae7dc567afacca24a1de94f8854bf1e937fd2439 a1529b5caaa8a95f0d2410f65ec9ebf428ae31515f619cb3e124510ea6b3987e add4c0aeac43384fba8ab65c0da905bc5b3e97b5102237eea46a5857f8ca8bb2 44e0e41712cc80bb5661191fb7fbcf3e06b7e0e11b57a7b55cd47d6d3d058863 58f4edc21ab9744c09f3aab144ec49245ce30e5cf83d35aa51e97e4fca5c9b02594bf2d9dfe2129c0b17b0f839293f54b6040d9da022f26006e76a7dd2cca205b8e7444a7990b39ec0ac78e983b2ce96528040ad5af81b13bb9f2c9a43305f008ab49da3871e1ed4b91874da3cbceb3a4fc823f71d6a977d5531f465e319290053e8843ed2d862d02f23cc27bd3d6f888b8745d0359924ab4563e83dd7b37b030ddbe7a22ce6de7118ad45704cb2023bdc443d45f990ecef048205f71069b30e798e62c0d7340bc3f1b36a1dab5cde2f8ca84506b299ff3c3dcbe7efb1bab4083de20e61ec2de8bfd21e3ae5572b373662f42d1ceb3b7b2e28c8a2f81c093a0696d06f854f9bcaa550c52652aec59e27f89d8473f243fe48d116b764e2ad0402f5a94686548d21a01e05ec186539458fed18485cd1a65ff56aa856fe1664a202e0c7bc36e17583b15eb7b16ddad1c33f4dffb07aeba745097c706da5a842920623fe31c1bf8d185127318906033d57850beb95839147222af97956f22b930501d71cdac606473bbc4e8c584847679ee60bd4f355b65cb841bacd763a1e8c132d0ed03a45dbfcbc22fb6e9bce791e18d78c1a1c834f28fb3a74397a2b26165a08b1363c62739ece65dfadb3bcc29cc007b6435ca8b5d5f15d432fc36d616d6a01c17936853f46eb3e6eb1afa98c125d18ab004df399e764a9b3101b378c156d04fc0d24123645bde0cdfafdc3459755fd7139503ce4a6d423639b4d4b62182f0de0e6d8b8a74ea5ba88bcdbb4aedf6c3d267cd5b1e7cc76fc9e8125bcccaadf03cdb25b001c24d2bae073ba0ba363f90b996286415e32c27af2e80a613539360a79b3547b9c6c2ce493b4363611fea9d980011c3cfee5abee51a5b107781a35006d9890852f6ff9534df400951c20ac5eaa57c0d45531dc2b221bca3c8e51780e1b50701d4d466ac351b257f282adfdf33287a6b1b654636ea1838025548bb6074133e7ea7345e18bdc145bbde504b91ebbcca8359081c48eb09e20ce857aa60994c591620ce649f5f2accb2d2d8efac14c1e65b55b4f90b1e8cc89f0bcec890c986c83002899ae54491926d565b88f1f38192cd2a94b8a8cb895b15e6dc8a20099d985078d2289ce3ff13c2e86dc333249ea7e1292ec072c2498b049951b6d0ee569511e01dc9cb8b680a252a1dec1656c86eb75de3271addcae6ab9ed10d30e49932ae3cd63a53e274c131690099f68ffc6ed9c35bf1cf46fbccd36215f59058459fb319e22e070a61087f2a773abd82a14a766d1cf4dfafea9b364281d830ef5cda79a58a792b8db79ffccea020accbd0016d6362525f852994b219ce9b103cf1b9860458c3ff144a8f592f886b6edeeb9898347522e4ea1036f437ccdae036ad3234b192c828bfebcc5be100f36182b7e7a0c79cc7c97ba39e884b5a1b80d1c2d267707b135082d9563aef6c527f2321db62ba46bee858ae2b4c49892610150af9b72b6c87e4738bfbfde67b98737806eb70e9527c1eb28533c9185361e0647b64a2b5df40f3f0b687aa8f94401f1de69cd3af550747da40a051c3bc1750136d4141a2a5e09452d96992c035815e785abe4b25e23c8f57b025e0028385601de69ac39054e0b5c039ab90d678794b8c16dc9874b74f7c78e3365f44087b20dadb183cafef2278195bbae15fa5ff47ce343ac74cba7276612aeff35c5f0272abd3b24a9207f5d0751dbcef6d3d23eb8037eaa78fb58e89d524af91a0e6d5e030429c2aea55e665f6c792d38a351628599891dd31658a52fda137efe34fc7d0764a4f95e7a7e57379acc8a95f2b8886386f6459fc235757992e4b2018b90690d5fb999f0700f3aaff4fe9c9bc4d1021ba277d98e3a19e84679b256cb7e8758018028e27e08dc266a0f1c8cc6b9991cff8ac9f0fa6ec6a67328f31b8a3f3409044304031d465371d26c4a18b0af981fe8de6477bfa86e1d63aefe9c082be6dc03a8ffdecaffa08ba01530568251f196771b429505c809b02f32e57cdf5aa3430b13e679f5099ae45bc7aaddd955e6b3175d897933e4beac35aff0416091e13006f67b8a324db2caf3d6c578d740788f89411b44c173b5c8cfc39e0e4e9cc4eb017893566d05cdf20b216342954ae4348ff0a38dc4806808bc4a605d15a7372a09652f6b5a313b7aa8285822c9cafce3a46fd47606d80b0be3dbc1af41c63a830170d3f26c6762b69603be0981b22848bf0efc1f66a2425d49ed3eb2b8c052d908767553b61a967b0c964570a36b2098520008f52835c48dd26690eaaa4305240826e02a29bd2b593d784e06791880ee53ac7a26e936d162ba2287c3e41752340dbdcab1784f311e49263ac027e97ca4222da3289e175c55698097203999522c0d4cd827f823a165c2a40739d5ae9ffb095b47f24e478c542127378e38d80b6a064897210b313146da5b303d95769b8bfcd039265ec3db448784cfbb39beb96b0bf5500bc13afaf1f08420ddf8d8f7ef67f97bb57820b98efb3777872eebf5180c660f57e8b16238c192330c6882e59b0b68bdd9c24672c415bc70b6b53f6ad3025d4fdc1342bd93cb877a89684f9c2f7c4e5062af8fce2fd86f2c34444e21b208f584042b7306965640a3698100d29a4d90dfc2233b15229ed2610a173cbc5405c5b332ec7af7f9dc9678b8073fc464b4f590c9147913d479faa5773d287460030691fb74cf64d32b05bb8a8740ac0ca2dba114a2e13c775e52e40a1c4d9a1900bd3b0744600377d09ecb50cfde674ebb9aa93b484eaa98d4893c361b75e9b50a10d94a6fc8216b5bce6a37eb8a4ddd2801447d93220a7938a81df66579114a0dae5bac78a7ab9e0c3b8a5f84298a3877baeedd51342acf2bc6abaaeb91474f0a9fce0cc48827a7b3c4f9a3559c73bc69e7e08e95c0e3d9717e72154ca5d760033835cd54d13d3a9966c1df313c399c63de2c2121b80795cf5baefe1bfe1ca20012bb870cfa94a947ee68685ff71f36ab108577eb4a67c52a510a120b0d3590040bc87d5ae54971e57651f94e15a3cf049f9820c47d2cca9b281b914184d1d309751b92f3e0ff56a7b30a7217f9f2332934d45fbb372759e7241d7d9ea4a926084c91ae6695829ddae3fd7b2d8f45b0b58575c9b92dda78db6a04f18e4d73c00c3d5941f8206132dc6df31abad1753fe656cad0713437b47edbee83f21a5f7d0bbca76e2c722ebf153f399c1a9872d483cddf74d62926fe8988b416ccef03e602d924fb3e99a3b939502de58895139a37df8679ec917b30d6be019a59bdcedd0348128fdc23bb8f01e374d8c78a623ae275f12f10e37c49055f4774a902d01f011f0cf029174cf41ad5aa5512eb370773182a0cf68b304c8ff31609601b5f820af2c857dee68afcec04f95afab11c6225e4f4ff7a7e1eb53790251a05d326b90ac01a17821584f3156c6fe830c84015230032d3d61325eb12f5278b6b56253b057941317b889949f8d4a2bd926a055d3936332c3a77b31fb15fc8a05fe3013d0ff7b0af7af5e039de51c7f6b394c4b922cf4b42e28f97e9e98311595e53b1860058d866f439662b8b33d9d4213aa7bc6a5663e5d089cdede56945a6ef3c58da091dc864ad3f2ca4b7b3240957340919dc553ab3ee15e990f53136d3a71fbb950603ef392a5c3e0179d0441e1bbc9e397d83528fdc76ab8f163a53dca74e18060981127144c68b20e4e416b2a6a7cc1a593f653f014a3e8b6cab83acc568ebcd0754cf5f520ac5108e1df5f5ec5d2d34abc08854152ecfbd1642c400466a99ac0b89b29fb6a3652ac5effce3603891beb514fae6763c698dda7f0b6df53a161e064450041aa0954534764f8aed7766b33544fee4e49c22d5044159891391dfd00f7268271568f8fb4d5eb93b814c70a26013987f6edf1fdfc92094e2604005330825cec59f00fa2941abe29b9afe683329ff844ec3c4ec25bc6f29dc9aa6a8650e89dc29eb459f59aced7cbd3c06b8486c7a6b193a205a57d54bc439d995151f0a0058b23f2399fada342b0fa3a370e892629783a0240576c762e8959672fbe50af89ddf8b9a92138c380c25f2440d48c69e5bfbb8ba0f9eaba7206b25bba38e086a5186bf61d83676215083e380db2b13684bc664cc5d3571bc8fad442199640c3efeffeba2362958ee8780eb1535d91060dff2ac2dbe7f1c0b0477ec1a9a2c07170d36fd1bbbd45ebd6ffa54e4ba4eeb2249f60e8f6dfc8548150652695bcb016a72d871606405789258f4c3621443f176872be6812c5bd332db68b6daa1030bcb605d7d83e5def941a5a4e97ecfa711b31c1545f5e8a7fee883a84ada3ff50f8752de658233cf75f05bc4d0a744f853b7a28f02e903eff788e26cab4682610781d58efba03e98b030d088ed61c92da6f0f6b36d56b63c21e0c4f70086ac210d631c40a63b8aa4b46b9119df4029efec7f55b982956d939c56e5643fc574870510886199ad4eebfd4f0c87116501c6ff2f94037cd9a56e324b62f3553689a406f91b3eca0b4e7351bce65103bbe2abddcd099629e3f45aa1476257dbaadbc90833a248648b6ea38d53bc9b28224cacfe7509edb5a4fc6720c79c67a96873e406160ab1b051a9d43bb8f3147270541815cb11f9dad3ff20e76a0e8c6c341355048b7a576fec8a0dc484a4c9a84a4c66c1735dd69141c75b139d59cab3cee582022de4653ca5c634f7d1361035f32800f320b4b746c58323521c1b773ef911da04342006a08d9a5d1d91e3e5b15ce13869f7c722fcb1dc22694e7e3c1c8af0fe0b105fc1d61215a5526aa5f266fc9785f851918a8795faa671c5aa265aaefb0b0d5f4416eeb1ea75424a15c6630718942efec36a494ccf38103c01fdb09975100204681a0a51eec2b1cfa867e4ebf03704bf1b75c513895aa305bd030058df3f00bc35edbdb216fe38a5eb492a1f7120c92cfa27eba959c3dcbf3878215e66d90a158475655a856866ba525f6f377b0b6530a731d38a05ff2cf295c3b2a0f0880a6dade8bcc0ef70ee6c7696a01f03ea615e422e05143e013bef7b881a2e08ef054e8b255e2b4438e3e82e0ea6473fd674bf6be572c591758b84fa32cbc3cc780e2dc36949cc2096ead91e03de0b16cfaaf66c0c7b5381bd06ecf6cd9371ba930d4acd2ed946c4350eb67b90722b1a5c7a545df4b72f26985c13149c5c757d9809f2393501d4f23c923853820ed1e9c270b748af648702d5c14ca521e10517380c996de87b8de2daf9ecf80a2bf40349f206493b5e5cd71b957b6bb8adab2c2c03b93052663ebd0034c543532dae62589020d4f3eadcbfd20c8f24383c1f894007841a085b3abfe068d5a0b120e0551f022935e334c013312c8875473a4940270992cbdd61d79bfee8c73599bf45caf00a7ae471b9558ec8edb6bbac85e9f2b40ae5c703fd989947ee80686ded59391b60730829c3e3ff9265cbe61ccf5e733a08d62897c643193436848cbf6d23876734eb4b9f1a05ea28a9c9b2e020b11d9c0ef48701be84898c10b1104a1f5a7677f6190233c4cfb70301e9de39d07449d00fe5556743beb5fa486d8ccc6cd6913a4430b1d6d2dc4ca291c3485522217b0701 false +check_ring_signature 008b1f434d8af4cea39071ca708f89432095f16ccad1110c6dff6457790e7bb6 8fc887243d9b4d7295ec536aa04486c13b35c4473d2ae0525d420e668bfae3ed 11 33579c8e8597fefc6bfd5b0692749ad154175a0841ea2556f9a5120edcd08cda 3771caf18118c29c3262bccf4ad7e70ef36628278b3ae04fc41592bff3db039a dcd65be833167d2101095191977538720f328072f08c0321625dae46e7168dec e65efc2ef39bd6561911a6ae7a1e30714cd21ab9a7fb02f79973d400ba849c2b 7cb0516ecb61e857dc15de4bd4bb5bcb5816bdc21f4170978670bc0c518b4272 26fffe85996f8f3c37a5752918764b3feb0451c07e16b7691cf6a461b09b1251 ed813fa07094f956a197f088a3cc3949caa021d854c79aadad1098b6cd131408 ec8f9c8665139c5c976ddb13af6c30340b34eb123189eee8fd8e08935396045f acdcb1a8041180631df2705a507e9f369c2ccdafca6095d8da0d7aa32adfbce2 18fc279832ee9028bf8b38b133a87ee9f2885754ec369cf13719e98076ca56b2 6d34b8a18a446f3ed8b8ab24a5a06bac47389bf70a487057593cbdca3fa6b667 71e98cf2fc1cb8be27a44c28e1966d3d9e8ddec3c65983a03b8d70e7fea0450b924183becb8b7847ab9ae8fc705ec7c8e1bcad110457bef60138ec73870d280b82fe9016ba166ff458c9065c888b75f961054f3d36f0d640e8a5033aa932eb043b8db43d2bf89b6b9740b881c12765a7e0b6a9dd48fb802b2d701528ed8ba80bf018a5d5c11a6dae7528cd2820ca4590ec3d31a8cb648c466120a13ef1ec220c875bf2af619dab89e36955e975aa06ca78311db761f368b051820dea70ac300c2209739dc69296a623f48dfd691b404a95d5092288c54731eafb271ecdb0e20b99280799a40c5ebf5a9388fd917e21c044189b15bfec8b1c7383c49bc3c52e0bfaa754d500f6a8d86fccfc1cbf6e9b6416bd27e78a526c82b2df6a0fde336c0ec6f6b57b371d17ca8a81876e2ccb31da4e6564c23e06b3d691a69f7aab38e901ca18e637a28bb864c4999f6a542ffc0bc227a9f8f27480ac7cc2a7bf8908d3072045ff3e467c5d054d432df38d2bdad1b191e64e5bf9070b1345c12c03c1800847f961b99186c32e64391467f784410651e2d5fe7f2f2411c4610a425fa13c0025b4c1db07402ff766351d33e07954faa3234cd69603d072e0e405e829b5d70280a4a5fcad376ed970c685a1d0cd35f2d2c0c3992192b6f063f5f6f7ea40100d3c02fe10adf3b02044c564ed1dae4a56f99163287d70283320f843271532170117033054ee542d3cf4f8c19cf7827ec6aa3face7a6c20ae79fa146db27d32c070f938cfbcfd5fb79694c03a8af7f45dd481322dbca36ae0ac760ee84c427380cceb5da2cd1748840b2a0f8d9812f28d2642cad1ba0ab3ccc739474c16157f9060e94b53747910ce300bc7babf89b0ea9fdcf39f1de0e5ebad3ca938d11a6d702a9e89974476af8e7231e22997f3e23df47536784927101fb1fc3b380995786065be7c4ea6a9fd1b116d9e1b45212de7230c2639cf0231f09c97d853cbed5d600 false +check_ring_signature d336e6ca1b8c52e1c2add6e2f12177a8e70411a87507d7e261095fb479d3d49f d4ca7bed0cb0c3b6125553f4d0bf3099ffc348640665b5b4229caa009431ce38 47 414131bd6c0c53b176a098e481b75365db657cfbc3274362d6305ffcc0ab8daa e64fac110730599d1aca360e8d374c2b4ec467ff8d8ec2c7a497aded5ac529b6 da9678e71ab69517ee0a0a4f7718e28dbfc3d017ef9aefd222092ee27c0f53ef 2caa08b9f6fff1fa7f69d83842383888a35259d7da18102775f8d6f2153f3f40 531c210fcd24d47d685556661134188736c5488c8f788dcc2000b494b7cca8f0 51eff23eb722a65231deb181a7558d452ba37210e2ec48e043edc3c01f6ecea3 d02f97c36bfd52db5ed4602c21701131bbbd004102ab1293bf35751c5e8251e6 f7d469221fa7a9b794a6f20407a52dab088bf1630aa4d7ae95c2d2945eaed08f fadef733204257ef3fdb25347ec0653e7606fb0bbd159bf7acee7e911d997037 20a0eba725d2886c95c3d3081d971827c9ef4dd9d0ee985aee35b3dfb67f9650 48fb1d6038f8656a70f17f8d043ac0a9b4acc178aeae4c6620bcde8bd3e0d69e f7ec10f828f8ed2d8d0f34521207803e5f886235e9676b9822eb487752cca091 a388de7eabee4c9b0a9156bca2c6e236a29ece36114b708f8aa26ebebe559242 7dca50d84e779cc082ed2ccf164f5f38107970428bf3474621ee9c281fa62f54 15446b5b56f40df7a329f2636aea872dd91f781ce31d2feeee38c157c0e3189a 8f028a5717a1bc1c7758f544f59cc8ea7e4e8ed8b1659484414b094ba8f0a55d 2c723622de0c57112a1c2db43cb66e98d24907579e9a0b66e244c263367132cb 060f3b0f75d77c266d8e2a2e3e11da90a9c4d33b9d111e01520802e7a7f5fc50 79dea523ee239891bf786e2b65213bcc4a76cd6686ddfb04f6ff43947538c508 854176392847196022287d62bc1db6594ce0cbdf0121d839565dc2ee38311260 68a612cd86f26bc32c2b77bf14803dca85e1fa771dee28732cbbcb847f1ac4f5 3d0ec83c67817a4619f9d8bfe4226504e19b29ff61e5bd554ae359ce6a3c0398 8e348cc5688850b83d12d5aa4aac88b05a8fda98ee89de9a6e70068eb4cbed77 b6c5a587fc5ab4536ff7487b22b691a47a0550f3227141c4b1ffae98348a2c92 5f0ebf3f6b73e0c2d023c7ab13e0362a195a4da9a8a5fb0385cec8a8afc4fbb5 999f9f0e25a3411ef3254ceacac9bea72d7dc7b9389329fb7053ddc7c1e43b57 d5a05073e87e2e86c18b54491b164476279a24c58ab6893173f94937f2fe5d65 730b7d7d7075f6be434264b4b2b5e1c4ed48e72877ab3b4e51b99895770ce655 6ba494bec6d49f445bdc92b21bc36feea291e2fffde46decef05c7cf692842dd c8f9215e2f8685eebc7f56e12f0290f4fb32f4caf30bdb2a39a2f30c1fee48b3 9cae47039f9c4f54a707c650c1e00d6db622c26ee0c09b5ba142b02237bb4c56 94d2c64a3aad15c584fc737e411065c99aff569ff8714c4e880a98c3d8b8d4f6 eba9bc7ea510759036514edf6d28d8923fb257d395a1a95fe95176127fc1fc29 c20861065d908fd2eef42418acfa276b460904501050508287773504bfc9c1c4 33ea25a1459027309196193a12c717077fa25672d8e7e6170f456a598f402ce1 ba5459405b1549bc64344417842108128aa6de32d90b2107cd5007b74e8e1e6a 32082a6b962710b3e637c894ab5696c741c3ca26f5f84391768ba9ad09339e1c 55d827b9755ae80a89e22bfee23b2a14504399067c84edfac964aff92a988534 5d2a35af9b9e9e63992e2e48c4c16216b06fde8aed3a6d3a8da9490ee4710744 eb25dc424f1abe86aeace3b073e91baf368e687bb5c6f85893adb450fc3680d9 4fcb6507fd0aedfd6e8494b968ccb67450122510dfc821e25a34d6569ddc1054 7d91f349e5be85b9c252c76a2e3701bb0189acab921524c05d7cff0028064f02 57c7e42677507b209ee86a8383cc4c5a4fac55013f0052c9bd7f075ba9b2a81b 69d7b45bb6184216b68f228a9b1fec6c34f354c630009753d83cef08f9faecbe 5850f33e515496eb034fbb3b1a4133fbaf9f5cb03e5250a06089295cc1423797 a776ee21d50bc1673d90f2cfef079f09d0efd3f006ab36b6f696f397c0e61aed a19463ef6f8cdee083c04f3d742b5f42581f76fcbaabf9782c8daf5542b9454f 9eff6227747b47dd3173d0c9e0bf5ce071ba9581622bdf45511a6cef2681170eef79dfdae03ddb2a4fe8a21ad1d52f2fd7acb4801dec69ca017ffe5c61f568006860737157907829ba8c96dee134112643b3d5c97c6444723994b32f99a2fa064bb6e2d706ca9f93092892cc7a79e41db439a41e37cf18e66a2cb90e2e7936079eeae90cbb4042947b2fbe2728761d1d92644400662b7456e1082f487950e00c3a1ccec907435baa493d1d9d4731387ce22e79c4ff1f558b397c0c00f487730391715af723421c915c3b96201d494f1ba15fdf37e8157160614662e9ab99ac0b1e174b8a05af3f330b0c4a6383d92c55c897fb1cf4ca811acc190f7162643f0d58f7d8e3266b73f22a26d0eeb70b0d391e3f9579c76ba710ccbe302552c12d05be5ddf5f081be8aa212793353fa0d4f83eee18bf97380ce5c93793d07779a50b2f7d2a7b739e5bb26a25453016cda9499547595c6f373e641420276e1a511b0e7cd31dba17a90733b09ef9622ef5b58408388f4ca0eca02e492cbc44a735080e58e6399637d1c20b1af17ee201f199b23bbedd9104704567d1daa6a4db61d90e5d4daa1b8340ab86665167aa19d1a370dccee311f77a08753aff9d10cbffda0cda18c1edcb739343465bb539301fddcc052657f618146a64267b22bc87c2a1062d03e1e7a11605f99186859ef8e62c9e4c23d885c68f805d234819869191a30c9672056dcae25107e8c7534ce8cb78af3dbd097e76fad58b3184c2b101bf0c0288c30265c2c56f39fc1739c8c14acdd26ab42cada5c50c3837c2a60b3608210f5803d18c1eea92f15d3d8bcb8dbcf8a4ace5225aab1396a95c98263118803407e2b9b6b3d873eb4ba8f45def9f7d01491d3760ef3af4368e855c76b1cc06c104fdd12dc07083b34ab971651c8a3b845031766f65c29e7fb1c5ec01e3c0e33d0ce262a254f52b24722c36c9ec62794ab8fa3511021784d7b4241a0a474c1df50d328655895ca097d22bb2e680440dd40c21410b2be127c63f5e56557ff28e300ebbbc9800747b2e583039a37af433d99f9265e1b0a2c71d60b19ed154ef97f107b7acf90f76e4e32e1829fa6f7a68c66fa00805e78bd204c18496711fc53f4001b13ac261772351b07a6d1ecc80f982ac3148c0c2c5a5a328c4859be921244303e9644495fc7134b24767537aa41588a8322c96546a96fce1f68f8a96c118ad0db1e08cb9bd8fb03e2de42759e8b23d9482fbee38d0368f4ab66e637dfdead30ad9d4702282e89b7d53c407936d9c3d23c223ae2bf5daf76920f56c11daa97d017a788e5b8fbb0379c734947baa8ed3851d70d37ca639bab7e693007697abc90ed25383ed19e3537d4cba45ab73971fadf03b1432624399219c8ef8ce7f3df2003379e941e2e3ea7bb2f04c46812be1ecded1fa298853ef9cc120ca725f3f090e97a8a5f59eeca2235145008b5bfe7722a65d131b49fee6fd6f491c59754ee60c9ae4ff7f95832e4d6b7437a72888bad6d40f3a64181bff656e166eac459e340d96edfed1f6d0be7641d7fc5b43634dc375a3ac17b8e59d00373f3a4e4c12dc0fe1be187f38706bcc43bbcc3fd561a54ce52618ad951da2ed004b3717f930fa02b1c9dbcc73520d35b3a65b19b2cb2eb84ac134355d906bdbd8f000365bdaad09d8e1328920f8a269e381a5fa7e484c98c68e4a45e812915e2dd83041b6241d0749ef24025885d69421b2aee079fe060b31190e9a5e6982c97f8d7ebf402c170af1cb755969cbaa627a05b253c45d27951a43c1e790f6ce977dc862fd9b1581055be6d8018ba468c3256a2ff854e6237a3f54cc112c6e51b1544dcd8a84f41208092f6802e7e439028d61ca63146293d541e89f515cd775ab197646f2f7d05f062f23a9c4fbb95e5843c089a5d1f8615264db21ceff25342587c41a922c412b00fc6af0dc03631a4a7067819dc322535a205c6e0a8d40f70f00e8ea467cb7e908f73b35f6d10a45def6de1a40dcf60ef32d13f35557bff1b1a78777acc67f86097ba69eb7631f5a1a0f8d75fd0349a8ca565a4f03b52ea6436548b31618517005336989c5509e4de9a1a7f946ce568e43d9cefbd61bd19c745786cda6257c5306e24622629c5c2d0a2a518aa465abde0cee6cabee1e43ebf5571ca4128df10109db58ac0d391459f74f264bb06d49bf0fbf2557b48ba732eb84e437bb436caf006e51a6c384aa356f81be291f7e44306f29c4c76e7d262c7373d6453f254cc9053bf313b794a80c3e9a36450b323a437c54326aaf1e02c983aa06be23471251034aec371871a508a8c402d55e54a5be08f3531bc657ef9945ac96018f278a6b0f15c34d4f25c6ce77177d9bbb3b08b95e32023d880ed3fed8617299b6c890090089f34676649505d8b56351cf32a2295fee41822f3b27fedea06bf5e265101304bb542198b836fb93f19429c5d75ed6df65c52600d2e5d0a8a38b67c63a0d7409f865483009bb429fce24a49a56f143a14619c4dfdbc225c89b4807c93cc0200de59ce20bec84fc0bf6176fbef7da7d8ffb0d7050df110aa108745da97dee19047d94485bcc88909579b97011a2db97e1db4facd8f198b0cd4be52d7a126bd806d56172ef159ca9ef2dda40ba391bcd936e6d65aada7ab7a1345d3a16feaf5a0b141a0e0089e99c9e3b9c6f87023a856e8a922ddf97f4fd57a2e35054d1676b0816bcc05ea5f5d0205481a938c495f1663917e888365a14ba59bd769fb4477e0b462d36d4bf96344c5151526886f2ce6905b9025800186b66e3554d7dcd4e610f68c0f7f049380381c0713cced8be422e7f29e6b03fc4547fec520c6f2c056104826a881ce3688e1f9fa394ee197a3c0929866699aff4ffb16fc9bcb68280df044f9926bc026e1434e08743a367db48884d77f773db3ea04572563d8cbfd0960742d8268e82fd319f8b4bc3bc9e3a0007c7e82c933bd27a6fb34b26957a71a703fe1e2be1795b06331948ad16ed43d948fdbbf9a242c0ccc2279685e125a3fa0ba55082165768bde0f17218d87cf551604653457c2d7db6ddd7215a3ae486080e6813ed181bce57219862b54cfbdde0e246c2084a21fe43ed48ea18092f6dd206866c64e1d4defeb568acc56ee1b199e23906080ccc830cdfef2e93980f835f049e5ddfd26d6047aec8000082c9305a8964fe19bb911a38939095c4041a80070b56efdf0e6bea3223cb92780e11047a11d32f7d80da8a65dad729d50965270d0c8f558646271fca07cd06760648cccd6334863b72eaf288aabe60c99936207507aa67adb0299b35f907fd4f9a02fb3808499c56ede7757ac25b7876ea4e87b40c26426087e8fe5872a744c2928b6bd951009dddd5f1305fec024259e66f88ea03fd8a775231cc8d9b7e48a39cb506a201462b420dd49744750391251fb7176a071be51bc1a283ab766449d42070ce52302bc5685dee3165eee408ee6520100e0c2e8d71686b6ab04d390280df069cacfd1e5e75108cdaa3c40553bea55e6cf2070ba35f15763b4ceca022a6c5848a8801fe4feefaeb75ef67e5a49d04fb034b0510b9d6b2c5f136ab3e35c5689ff7238bf23d4135cf5b65d77acca3eb065ef802925becf52cd9ef68f73d53e7a54255744082095db61eafc70d870947c60dfb00dfb4f49387e2ae3a52184752595ccd9ed4cbdebbf73e329217b3b114369d3d09436c4a463b4d0b9114aa80aa359bf11e6ff470b695ef9d24638829c157b206046a2e60c70bdee2630939d51a3f6a3f2225198f51ed06fa83ceaa95eab83b9f073e7c992223e2e38913ecbd9dd2d03324a25ae399aa9139b9cdd192965bdf16009feeee2d30f1f3e46403fdc1d65b795173ee5e8d1ebf5419476469f7fd55ff0e40e7326a63ca2188442b002f4edaff6acd5574181954dda32c9fbcafa52769094ca00edea1bc418c28fbdebabce4dba5dea53a164561912393d4578babbb8b0b9c3c0d3ea7af008522c701e680be69929236ce82ad0e37073a3c2e58416f960e7598c6a7821acfd99c7c0819f807e46ddd847b5b5ad74437d556f0e25c6e4101f4f6d1f7b193682fef5192c07a22d0a741862897bec0e9c43541302f139b6a0f5e28815222343d5ef40bb1312bb895dc002f48771a9fd31a4f389765b3afab053013feb7ca6e3277c4aacfaa465a8a6132330b1aed3956f37595a8dfefbea80393409ede180fef377bc5e514a18eabe8556e083d3a409feb19f7e6444ddabd0c true +check_ring_signature 9ddc15e29fefdae5ab1be3ccf9135519777afe49ccb65e5920b9a6150c1cb32e f2fe8fcf333642f071c381bfdae87367b158cd244d88460f7a5a6ba10db7669e 2 e7d14f18d9eca1317ce0b4353e7d3e0ebad24e34ce7d3514f3177e3e3825582e b4f3620afd3a20a17b46a9316d28ad1695a7aadfd925b6e06a37c421740dd31e d048ee03a599821c037dfae65ab49510a453001511d7b1eece79dca77191318e41bc2a62e3c70208e9a7d0809c725fe4d5833f1ce7e7a4493ccfa70e2d291f0e2912838f39bc95f92fa8ba2dfd98a4dd918f932f46f19575f2196c90ae3cb4e859aa3c1964f71bc908a31a32ceb10b59c148cf6a4f2c8adb9b7b0bae029b8804 false +check_ring_signature 4eceabb1deb11440681cc042a6f7d00af6d14ead25ee0ee8d0b8171fd47c1ec7 0b677d8e47e13cbe538ed83bc0f0000d30b6b6dffd271ae540f59c97dbd441ce 4 c432735f35dd777d844d699776d7bf3aa4194546202f968f330ac78a3adddaca 0a3a4ccf0c38eb31f11cfe92e1845d7f305a442e7673f59bfa7c14409a358afc cd2f17778b406d9be4a027d72c98e532f91f6fa4101a66913d9eae9eea5000e7 d97d3f640a41df50c1aba50e3404a4ca5b6f565907e6df1ce7d738d5e347c996 cb5d7186edea237111a624969365ee68b57c439690a90541b1c4344fff4fd50b2cefbffca129e5a683e87ad01b769ca11269758947151d2a22a0ffb8518f540e3e800ed3a89bc476e919eb39fcf21b1ed341858343f61af0dd2b048cd013190b46a954a066436a2a7ada299f1a7edf7125fb63c64528844887f62c97e8d79b03ddee1ea246f15373f159be7565d094dae19f6c11a1d2b0b90a2363a317160107f57fc8f3e493cf99963a9b8fc9877e9e6e46f6ac835efce53c8f0d9363e7f8053fc438935c375e6aba99742012af6e01ca56d27e995e94abdfd88c3f15e3880aeb34cd50366bac5c02b1cd52f7564fe6b0ab484bd2133fff0db989eaa96bbd0b true +check_ring_signature fc9a65cf57f6e6d69dd0adbca4fc1fa1f59459b593b4edc1b907c74704412589 4959247840c61cea5e37a050b317f56156358651a33a13a051129df06df75b83 1 959706a4ee85ff6fbe3251ccffb67b48a7799c54028b490bfcd493e214f31f43 4312a3d890c317231080b14c6baa3fc503fac65bf42f4abe940e0dd3a2b0f60b72af301c187085b713d05e207bff7e5123e36ccb06c19bbc16bf435d1b83e90f false +check_ring_signature e2b412c4f113c060ff8ecd9ba76f1673d92fe3744413caaa936f7db37c3a954c 4a8d2d84e2bd50c17d748a30885503cb232c7b7a5dba90fb5420c2e5faa38c6a 1 b19c808178fcefc39879fc21ac3c824eaf554e799640a214e8c37494b8e031b7 49a03a71390fbe965c2143d20f1a2b5e5da58b2e054b38152348297cec4dc00437ff914162ac25b13ec1f84cb581d922fa8f4484b947adb063057c8976559a0a false +check_ring_signature 6481c1ddcef2c1dcbeeffea037bf06ac344add31c52df8e5f59bb5542a18f806 4d4458c59b8ade5b711d27d1fba9486ad76a4da9de2c746378cd0b6e26f05d08 2 90e8e66fbc9852f95d9ee58f2450758560b09f68200fb0c3852aa39e648d7fbf e36b051805b225664d3e072b059360cb2b9a3226ad217a6dc55f6fd9a69ece6a af5cd720e34efb5985af28889b2351926dd7bf6a1212d9d640c362efe3963f06002d8e7eeafa485034bba8eb31a20586d749480a11d2f82fea81e715aa8d6e0a1090d72e42a0271b8eb8e983aa31fc6ab46917ccab9bf00d8513049acc47f5058e4c42359ba0280da1f642b917a3e144d9b73c4277cd35436b3766d8fbd89d09 true +check_ring_signature 38a60464749f706f7222a055ef043f2f87f9d91a7a0182435d7d900d51ba4672 61d12fa0b193fc84ce927a9c97a24068d8a26c24a4d2611273c5ff80e6b5e03c 53 37d0497d0c6f1fb98052bac6e908054a5e7e63bc0fef9476591276c1c716d4ba 852e3eea539008f5de26f25372a6a02912360d598d0aa64c6c33942419f9be68 51bff20e57a02661f45c98270e47066ecc379c08f45e182653cf77f744a0a6d4 143246325b5f7d7692c687ac589a552fb648d02d0c133d4551b2ffe91636b6fb 61bbc2c2b09e9ac601bd43e57185205184f41394e0f18034bed2c1e57610bbc1 9b1a02a4114ac52e75f8005229daf96f6a0aad4d519834884e8d957eb6df6f7d 3e7171ddaa33508eb28ed97caa50add6616193b548714be3317f081c025551a6 4434f4f2c01a288b9efa5bf6cf9c7329bb4b3b6de1648bab318c0757b59747c4 d55c59e0ac699bbdd980d0367b80813b3f8b3a159c120f43d1081f7198222652 4b00a8e080233a755314a1d6f3875720f238c8cf25f32bfd444013ae27a47ccb c7a3fd2d224acf3675cd5d2162cd143ae089dd150fcb6a2342270b72588070b8 fec7a844e5763e5140c2fc92e83c04a38c753b60109928802638ccc6a42fe1b9 2e51e5d0b09d5100707669fae0c1387b054c889c7abc547a3ec704cafb9cef57 89375caa00eb07b075e4718fe715928dd0e6c741c5e9dcfacda881fcc94408c8 c838ae7a5fd8395f7df4622718f4893b946707450b5a80f4f47820e94841da8b b63f5b682b40ec839afde219cf774d9ecff10c10960c3bb0c24f6f78fb1a8e72 a8559d9e7d34b3e27ca30961d6a89e7e8f9013bf0af1a66140382d13b92fb857 a74fca8932d0a7418b665043aa371aa1cf49ac5c2c228b0172b4919bfb6c5e60 9cec6b693a427c6e81333405f0dbdbc020e91e60960f3bb17dee8ec300697cf3 eba98c6dbd19e508ecff922d2028df90118a8260d382afc58574735bd1654757 a4379a9f7459b9048c26152a3869a0bd2672f8deb2e8b110104cd0c699b70c98 c974c0b8952aa28e0d7089f1f5b8fac87a0fe79437e5c64f7ea49480d2a5b533 adcb26fd9342ccbe73264fba4b4e9817f1e853927ea33620865e606d2a03ce1b 093dcb7a863f0f7f5512e79bab6455f6935eca2ec85cd9c5365e921ce6b675a2 ea57e8403c172794a6fb5c7e7a19eb2ab6c6ed6370cd6cbf3a628eae4b2a10fb 2a3d15f0fcc41462173a854a643adb7ccc51282542e3a2fa990ca827272a2a50 624429cd46d78a4227e4e2f6f8bc7b66f45289d897eaa5b4ec6cc1983d069669 f3a742680b90affa31621cb7473df88f1d1a5c711381204713c41319c1ee1a43 ae7d5a80f9f755068444af6b476026da0e2e6bfda4bc2025b06046e2726278ea 78ea830e868d3de109603cd4cf8e9ef607479f7470eed047101d1f66c33dd7f8 b56f6f54224ee7747ae780810678ffd9dd3170b10a5ccea82b6e47b54a4fbac5 31b9ab88a7f368b0286153c72a931a42ac42a68c1164383b5e179d49267f3af2 66d9e648ce2053af612f6dd70e515392cbde12cca726138f8fc665fe286afe9c 3c3fb7732e51f3c493815a6e50627daaef67aab1cf90227ef43b3db9c516fb07 d6681accc299376a4cf30bd6cf6f3e35764b593df1262904f4ab19e2a2e47cfe e335fec9e46d5efe2bf167286975e6c539bf0b2485c0eac356c92d3be674edca 5b34d4c91358c866636289d1d68d4c43f2fa3d876754380147fb33d449fc880e 43644c0d4028c296d9e1c55b131056a4b845ebf2c900504bead235d924884663 465aee0ebc4c3757d2445e16bcbb0ba94dd251263d93ba9c7af40924069ea025 d4846231887e79fb5158baf2b468b769b528d7a6f5f3f4256c46574549d98831 c997c99ba013c8ad37fa21a030a854b59225fe2a66a0580536389be3b03771fb 45113b3b373f56bbb9d86d13168f5460b183a3276c56d25a247ad59c7d91e9d3 56abb0ed80e0cb774eac85f554e0a2638586052bac10e4ebab1b4372c29ed163 9833182d73bcd8e6644fcada50b3dd675e8240279145a90f715d28d99ce46c14 e2ade750b3a8d375921caa517c08362ba1d7138dd7c38ccf8a50b40507cd8299 5fedd6084c5435fb845bf157d61596a21234529e7c0c999de2b60456fca75bef 64d4ca4a3d89a522f8947f7e539bba14de7656f6cfb791dd07d5d2ca77b4b9b1 70f19833846f9eb68d05d786ef1f9bdcd417b04c475533bc7f3d25fc0b2899c8 52efef4065cea0df0f56deab29a434f6d17911d001f65415399d1f22724d8c88 af09efd1e1bd4f000a699eb3e10ead299683b9af97fdc39db379337270ed121b f6b6c0e44cc8925864e7c6c84434b0e13e57f994d8fc2c19154f8438b5628250 ce7ab8ca4016e6eca6de1e78952ebc34467682475963ff74a45ecb768f8c13bc fdce3d6b10f20b2b6784dac15332b1344567c1948ec49e1532e8ef1ceea6fd1c 6c3f6c05286749c3a1d1f3a8bb50c7a6335b7c38d1fb7ccf2f2eca320a50ee0ef6eca6bc24589622ca2ce91b7d453ec558121e56f34ba7728ac28d1f47cfc80e6c15e12260a6099120199277dfd24b76cf6451d88e10b759cbc87a89d32c7802c0621e97ca52dc831b8c73e93d79ea145e7a52e3c49870b35c6e44c899bde90d4bcfef534cde2b7d36d21df36d6cf763387b4fb5151da0123d11c09685c9180acb9a5587362104dbd93105bd1e770922b674ad30a5f0fc20a52272d4ed82120a4e2c12f4d95cce93bc41e8088cfd75224c3bd6604e8206c7907e410426cef70fce5a29f01913ddad0dc90ad49005a757039615bf1a02c1827844eac2267ce103d77629b081989e5525536ed7fc2983bf1bf3f5a85463631fc7556ab85a154509ef516e35be30184b79e9a7f74ca95b6d4289ac6230758f472d5e837fe98d7a0b82a4feadffeeab1255aa78d717e53287f7cee4047de88503d7a263291a4a3f0492caff3eae898d5e2ae3acb6b22a7aa52f1946f83472fc89ca06df12947dca0952f7fb2ee308f183327832411341a8b34bd54e5a2f509309096d6ef99527710fe74ad6f0f0f0fb647a9df7c46f7e8f36358d09040ae8fbf608366eca646d02026520fec943628109bd0f9c76b1ea60697382f20baece4746b9826befffdf110c16088a6b0686601c32fad4a68a7026b6c17f466bc75ae6435fba05f3a230be0044f9cae74869c2ba71dd6175f60abfe23dc04f085ca1fca13660ca39f9930e0283a0e8167532b71345be83dd525ef12e91d048f112c6cf5c619dd1c0675a3002c4fbcc05c0752ec97f7e18ae54ebfbab6aae797cb2b670194e0b473034fd4b0d0fa6dc1c326714a3bd84bd3934fc156d460fbbf4e7ec48ad260368b6a07cce04cfff6090e3fb206452c998b0295aec077491ca6de02276cc119d585e1ab05102c888621e2a2c919860fe9e66260e079cbbbfb12962184a0e81508e1cb194230e20ef28789aab8a117c8f9b4e3d0a34aaf3378687e3d66538aa25bedaacd2bd075b44d0ad1e13b6427715c108312db78e7959f11b2539495577a2a74859ff0e08e8ccf8a67ebcd5fab064c45c5f70281f92fcddaac35e2df9b63b00f2bc471600c33288f5904d96ab58faa8b182837b789faff561492e9b228c7f4377f134f60cbc4753e9e0d5ea1e17a881cfcc15f53da18db3232d29ca6b5229934c0c6b41033da2116ca6559d6d0dc09a827345d929bb81734752965896d2591f6cccaca5038168f427195aa5e48e8035047e2df09ba0259c1edfafb2feab41da707c18d40723f57007c2cf540cc02d83f593985fb43c16933caadb64f82eac5bd591f95e079faa9446d0feb0b4e6f4c9f976645ca515804ac55e790cb34338944f02d31907601e05ea4ccbe2f80d1be6e558ef55e110376573677bd728191bfc14935a7501c0a50eafb69864274342373916882e8cee4c2c8170bea96b5dc8cce9f660140c28145386ce20f95e1dc328205ebf4df165173a6b0a2f86c075e11d553d1d410cdcc3ad891ed651e8de966e117ba6888cd478a96a3f0fdf531fbe8fd692775700c8e93fc90398ddee46495756ae7de1ad18df10dcc6eeba2a3f9c6a621a5a000bce5ba2dfa6694c8f95249675b2d2555dbde24d7c48cb43a296dc32677428ff02072a58e631dca2a3a4ed0fb76a98bf0363191593ae8c260ddce93f1511e96f001119f0ea393e0f0651df6b79462b73e0709dd05daa8dc0cdb610257f61b11f0d7b3798cad5878e0fd1ca5d336f64f61243d7fff43292316cdc1e8841d098990c5357fd6f7d4595531b5301ac9c249a87ad3a6318b617597c15946a201749390f66d4be38d4070ba97f9ae9ad0f64b25c211f8192a7600dd2b6972608c852f00848259272f95864438955c67c7cd9ba61a57d7cf143bd8013ccef6816c20cfe0f8c344425fcc82676b15bc8dd3dfe0580af5d278941b746105091a51d51218b00773bcf869e71a7d2691e21bfeb7ae03cc9f2a96e80c414ba2e2934ca31c9200bd5cd1b0beda2c8228a0c90aedb575e85e600a0f337820469bd9070a4f97c5907ad83c978476800fa3d30504efeee94556ae1ae22637ed124f7144fb926c58e08b5ac661757ab759af6a1675f015d06ca4a5db29d161c37387b795ccffb4f920865f4141aa0df97373bb2365c7d143e2ce0c4a62f70b50c31b858d2cddb801e0239c3d9e14551e4582676ec56fbd721fa1ab1819fa2177c93d511e53f1d308805734f4b31e4a1dbd2c87c77fd730329dc4bbd04e5aaf60b09a7a34d8e010a4201c338fd411c40b93e79df159a1cbf07c1002985f2b68b897303982f65781c7902b837045a615cad794067c2f2b098b9dae8098cfe741ea9b84380d0f3556f88023032a110958c317a139e5c6ee21719dddc3bf5a2dc089acda4bd13868e047108bba89365f566c8ca25ab48f6ff79f2cf571324d39c830afd080f543b07e3c10525cfe8a531761aabe6f88a5201f18575daf869f7ffb04f5df1c80c78334e7a0b4238d8e168f30d691e77d25e119fba0818fe1f040e47f180f4ce7cd690afd30e82b8c44b55151f2e69e4ba174400f4e37e2cfd1a9c5e0a2d774eb4679e17b9036019e77755e922ccba251bca1dbcbdc00442e0fb7500174c5b9b5b0c4f47b60677ff98ec9a783880f92703c423fa412201b63bed9575039e39d629641740810142017c030647027799463a9da95c54806fd77daf84390dacace1854fece01c093c580a17b96331a15ec681b299a56f94791460faf8efa9d641b4d2930d045c08e66429dabee506cde21242e20d940a76b6f09d47886be1153b026594cfae370f18d1dd8630484e47e7cba0191c82a321548c8ced26309a72f729984bfe023402cfdcf4056361372e501b046c27138bdf2e7cc2419888fda94c4a072b47632806bd1dd3952b8263d98416027b8a492c760b2cb664da439c23e6086878ed64c600c9007870ba74c51f71433b5417624e891d06c22db16cf722d3b1bffe616988082dd094f7024d1f4b782ef56d4c1abc8e7478845de025d81b59bc8ccd8c778a0d211479520847af485d2963fcc04e0212e0ee35c1b920278163ae969bafc5e4040863f27a534f1329ab6745125e6e700c1279a95ea0700aee9f80de0e67353b023d80db335624cd42d872586732b8822c8507641c22bab3ba7ffe983b4ad5ba03b764d7185d53777ea4e0ec6ec98230df2d270c975a9bd6ef978c4d6b8bba4b01f5567b2aec22a88533e5d7afeca5986c342440c5283ae9210daa41f8eb9520039c100056ae0153d0c5ec9e40c867c610a696a7196cd7d8ddca1ebd550e17d30fb156da95211d55dc213f8dfe31f9a792d521cbbf6b029de6c8c74fb432fa96003ff47784b8922e017beb7316ddce01da2d97eb39b5f18ed1183ae34e7d1850087af38c4b4db89bfb64faa2671c51dbc1e7441f7b038796829af6f296db73980d3173e234a6e6a6780a85c9e588274eb4c5f99028431f5be8ba41f9a31421a905829b6dbce2af0c50b0a1b41f2865eacf27232943ac9057c4cd2137922587500d8e70805765fdd95de9bcfc4672395c9f471c1f441fa6ca993a52cc579d7eb705e000be5531f5f165a7e06c14d4828bd7eeaa1420cf7d50fad32d6b50f1b1aa0198f2ee099dea1cf375ed579ddb62db041fde05252724c2bbb0e0080191cad80ec1d55a5850e95c7a289c6b5911a575de85e0a768da21e0c73e9da47dc80189067ca832b23dc3e28ca2e3de43ca526857d1e1774d785abe088636a9b80bdd6001bce0684a522c89cd77d57daa6bf39eb328af42f29077e3053dd44ace4544d50238011f864e6d1698475931cb6204051bf6ff857b8671c7ad265081a51bd7120751aa8e441ad9b878d2b39409ab63297bced7a7cddafedbf36cd3bc37adfbab06291145d32c388d0a4b4bab28d9b4694019232c41dadce4b5a69a14fd86fcd80b03af4fcaaef70c4cd74b17f974498f39d8827a059b990615b75c723e7a409b06f86789ec709576ffb1bd2142476145d03e0e0e0f34e22c112b8fc0539882790e449ac5a4adef371af8aca7256f14011f0bdd7ae4ba2253f9f72e0fab50f89c040cd33518fafd4a1cfa483c8a586123632edcdb21b8f4b24ec6ce572b8a052b0b0c334ae2bba8080554f2ec042a2d3305dc2e948a84ccc83023a197c0f929d40c4e807bfe49fbb4bd3e6c2184f62be78d8e535a67a02840ebe7d9fbfe180bb50ce0280cf29209483d3bdc0716c5b5c73743758e182b3748a95c90856f5cf0aa0a1326bd3ce4c1417993c7ee3983e38eb008dd91c79e80fee0846c8c2612e97d0fdff951ee86ece91682635977ab61af647bc69edf7b01c004930a6d91c1ae7f0e12c2fae49bbac29cac0ee4a0f8a34133fc6b81400984691c807558ccde5dff0757f4cd31dbf1870d66673ee01176880fbc770485491672c02e1590bb026e7b0950acd32a76859a783323f1db4067dbb327694f8fb747cad23563d4cb0f328d0f1d20de342f766c6695eca9322fa7614a88c04382daf390f59e1c8008777b5605fb2cc403992abb4049eac2efc58e38c3ef00946f949cf2a3a8768f9959f064016640eee76c8b887e09d7fa633770136653cb03f8b129ffd8806385380016bf0909b1abda1c770c9772387007c0370589a4b063df628c85b1331357d1b8c59b010904d8ef312e561dfda93a123314bde9a2194085a2b24fb1a0e062f777a94e0e5c7d356ec3f8589531e2d8bddec96fc9034a649cf4812398b1eef39387dd370a true +check_ring_signature 944dc50624e38ed466a430e5eeba96ccc5d6fbf4b2bf580156ddb60e36a9e343 2e0685244cc614d0284f2a15cf49544dadca0e5b1e692f08abd741d3534f2a61 8 e173c4485600e3c70584494bb11d824366c2e07d370e234662f5f58b9e94831f 97f54b7befa1d79cea05762236c33c26a9d6ac19fac83820d5c4f5a2755ab683 a33a93a8db559e9e3a17317f780a47cac8aba5787939a49d73eafcc953ffd97c 09d6f12661d8caa47d23adf8f824b1ddf6dc6e5465d543595ea1cfb557032666 0d94c5b77b8297f0bc02cd776ef42b3a26764aef72dba542b835fdaa701eb44c 95a4cb2797b86a6ebf5e82bf77a9af3f82cb7833774bfed7f5b1554d8966598d ca2c9ec01faa295611a576c2c343ac51d8f66db1711986afcb2f803e26afb588 1984648ecfb02fb943c04d0b25215661b9ef5d6325094b211b418384b5b23452 170e4c24b8f7dbae611fa51acc520f7c781ba794d7dc5f9cd140d342ec77fc01b8ba6d595f6d459f38c49631b6e384ae1417b9d04418833b146e8f11d32f72080a4242b9458662accc367a5a7289453241ddd7e71e42b4dceecc0f382fd26b036a11b62334a49dd8f267ca455c014eaf6b7c41497079a26326c0facb62a05306cf6eacd2768e62109a35da45bb85a0cc7724bdae51fd034587c3b167256b73060ccfc46401bd258e58a8a5ec439cb7e9acbb62b1453351755ddec35c8a7ba90ec5116d186dc9f539b7c6f7ff9ae3f7a5bf1c60ee1d5da5caaf509dc1c68c9e0e34067b7b3ea04494f274e9a274a61758982ef7847d4b8ec5cda08c0d8bcd6e090fba62cf4c95b212789441ef7995f7b37889002fb4f85002c007dbec189b350ad1eb4c8972392e9550c41300356a86b38df99e4e34c6b6790f0dcc46f62f3b2a62ecac0698c75e4f6c5f0630f8d1ad0b02371c29db049832b4f4fec8a140de0de2434eef4f5f921bbdecf961c13688688efabf2bb05621f2a70b3db990430acaa1a6a874c017cc141e6885fd69707f5b81be47da5298dd3b5f953916b270497ef535d2edebeccdd8863f3a1469e356dbdc11e5eee07b98ea03a24c5d9c6f88065b60e2d5e96d7af2fd6a5adbc0f5754e3069458a61987ba9a7467db958ab330244840341256e9679f8f96e2c247e3a883374083dbc6f6002bc1aa9f9da769007 false +check_ring_signature 6b2a71dc09d08a812b8819502df067282b0558dcba205f337d0f6bd5e6c4a891 caf0d7a92d1dedc12c505a7796ef671e1cfae3a4d2b9678e87a6a8ab89f378cd 127 e3264597ea64541107b1851c63f2c4444362c24ca85e4b67621683428f51009d e82870548eb4867b0bb2e1fb15bf4a6018381015e2e96bce638273acb6843932 5ec53a52bd9af4c05414017c98234d1f4da2fcdd8013745bdadfde5fab55daa7 23330ef8e8d0d1c1771190880098a6a7664875d16ce8a8950d9901a46c199546 cd12793350f42b79176fbe0240ab64a926e77baac7763093dd93958a361f2b33 cc679f94228b87d4ca2cbee7d4437884cbf5f15452e518480e582cbf58777398 7c02514edfd2f5f52ad0c5d0302693922c190733549d1b58f742efdc67cea17b cfb26bac63955cdb3c1be6485a944edc9d667dbc8c3a57be410335f179149509 578e97ab72d243ab94fc37dc7abe4ca90a506a1e454d9018d754e3697edb9e8a 1c3538d62efec6a37a771102106827da8bc8478e40551b5b6b88df161f6192fc 5b8f9f493a2969246990bf7514cdffed5f739e732813be8cb7df418c5ccceecf d53745b10c04711162899b8d4afcd19ae72906605c072c7edd01279f53e5a9eb 6b705aa3a9841e4caf1e5ad4aa1fa1fc44832909a9ddec7fcb2b26871b7853a4 39185dc0679095e7466edbec27f2da75da779152548ec6b7a65078ce785869d4 a330ffb09c12faee290788be77148f957319d10209d89c700cf426d846ba0c4f e2eb97959e90f0204789d6db874bb94ab54db266c35f583a7e39a0c3f699d015 701e0a43dc28b6ef9b659b0e2b5e237010abe0a6cf7338bdfbbbc7969fd87972 901362ace031be7cebbdb391bf43f45674a9eccaabd502a360186473c96adabe 5839bd8cf29ddb4ecdc3497fe9d6eaa26357bc716d5f94e6e336317b17404ee1 62d14a077fd2f65687f8ec848f8999ec622ed02f4074913873b590a51ce1e5eb c72ee714f6bc0dbf0d83a7d6dd6dc8cde46576e1bd3599160bf8d5582e556c18 a8656718e009bd22e3c5a1ec0c4215bd40238950529040a3552ddcf0aec2b187 54f2b0f29d59dc71bf97d0dfb09f861056b6e836bdb9b434f32ad913a36f2cd3 cbc7cd753aaa84412aac9ffb043a66064cb2a4dc959cf4493c9c779334a6d095 3f32331b120cb33dd6b378f7358b8076e3fc832f4c5933195d866c1103f2db85 905fc58c1a9ca31139d9bee109fdf5b466ce127268df5c24663af35aeff7a0ed 777a364de7041ed048db039021f24676bb7f28403fcf8067577d91370f930f31 ba8bef5ce55bc7afc4b92db75a5536acae26c6b2deddf39c804892ec7dbd0ffd b712c3dd054cf951ae130a77e0ea38774265a6134217e70961dc7ab86c448e4e b150908292740d47b1e21ab272bd6edf5aceed252943d871c2f7937f6f55140c 40b19f6187105c855c59cbe0b31bf4f6998e1d43d9cf80cd2d10d617b69e7108 45a1072edeacdff7dccf34fca7be407a74ae900f30b1d74f6635e273b60c118d 0a7409d28bc12c1179943dbb3f0ac43baea0744f0d96e68683f3100a918bc675 27f2ca04dc51eedfdc880fd3b5c4dc1e6882a90d2be7f34b4b96cc04ec297b55 ef62b68b8716d41bef9e54be078b81c79d9331ed5e587b75332143edf2955344 a3ac90dabb30527cb13fbc3ce452d576732624c9a35caa3efc7529aa44d15f81 d194686398acc42bd0f49063e1626d42d4b7a40caa9f84514d16769784213b59 4641bc8560e6d1f8607394ead95404fb8006fb6418cb7ff4fae1017cb150ba21 bdf45e23286ee176acda06a330c1839bf508e5605597ef15e85341048579ccaf 85fd15a76ff181a1c3615d5ee62c6945bae00cb40b27a3ef565d0beeb0582a98 ec8fde2503ba17890ac4c308e41af5d608ef0f8b27d7a48c2e8c8d086aeddb4c 6583bfd81131da3e25685828da628557998c431d56c13af4cf5e4ed0ddf8e81c cb00042ffeff3c3640ea677e34ca092545f725f927e7a66dd3ee2abb38769fa3 8c92fa71a35c7929f2f2d3669decb77018e8815b1279877806f22252ff01ee1c 9149d50184cd344fcefce28af2116d3032f7b7d11d1515955c099813f0db5b11 c821144df0f9a5c3c6ab86053165514fb45dd2c4fdeab1d1801fcb6d703a987f e723175aba8276c5035a096c805391d61bdf521a7485ef469d7124b1fb3c4317 d79b7b8fef660c7858d11f35ae7b5d96d0da7057807f8c384134f0741a056fc8 28e80c662d5fc7e07f6554b437ec103b84c6040dc2cd742415a7108a07cad294 04dcefc18a9436804e5c9c219a56fce2988c71310e3e4f6a11302de931812b93 821615c5068bd3c03037a111f34fa5defb997813281a52ad7003e72a0b4fd381 eb39021d9d94cd50ea07463f899de59ecfe287cd82c63297f29c4181e492fd01 92cb6079f616a96a2d2027a9603dd7451a723ba515e931831883215d8ec71b44 05ac0d4d48d45c867ad41c89c4c750156cfd73fa8be69737e6853a886c5499e4 f807c921dc052cba624edab27e50fea916aa770e3fe49437afcd420bd90ea9e8 3fa7779730c3538aacea5d0987fb36afdde87dfe310ddd18ca11d3c792b2d43a 0e3030177e788bc18e8081952639f5957100c5fb056af8fe038bf2ea593ff440 0ef70536582adf3033946256f2c1bf420e5e05442eff55ce26e8370dcbda4d61 ef70257d194795e312c453373536238146f7e0f9fa7e55f3c7002b3855c59b08 c11ce240416dc233b1e1ccb3fa047f49d2bbc96f65c5a74a3fbf980feabec1c1 a774b064e6e90741cc5296ca80997ece9006f4e50255c9818b2339979fb55638 602585978d55c0ecf6d3f62d935a3ea1fa279b8cc259ac5f3053a74aba84f98a 7a5b5f30fff5bcd230b9a4e39d326de023eae88e5618796b7c410cb4bfdf2756 ad3b38158751489fd9b3041d0bb59408c988283e6e54c51de9c71f04279d6354 4519cca5a3ba3b1c59ca5de74754ee6375de43c29790464dd107f1192f10bb68 dbb86cbdf16604a1a40d94c84d63f892c01ee9bdafd95529fa1b70d71b810f84 a1d873f782b4e034f0e0554410d65c1d96143e0166d6fb8e24eb342d4b15d196 73844a73fcb52f25cd0d292c69fd8549c079795428dce60432c43811ff38ab73 cdef3a73aa179b1be1ca95945a6827239f17b6ada587298f8c5c30181c87ef7f dc4a40a004a22b02324a27617bb4a113a9909d08910562b5cb46bea5eb3a7c67 dfd52f5af6502c60396ebc2ba8872b2447236689c54effb6c2670af0b04f2738 d9b72c4bdd354bf433af48d0cec2071298b6462949fface85afd3374f5c7ee01 71d29178e5b52e1c615c2680fff31b6d1617eb0096073714ce0d11a0fb7a546b 91954981149dd39fc24e2c3cf5be320f4a783bc56259b57e19afdb97edc3dcd4 8e42df766557a961a63bc132b1b106c6b067d424edeac4d2e391f47585c4de24 d58bc6977b8c3070eb1c29de6f5beec41ccdaa490acc456759975a7a52491244 9737e6a09c27a14f920a9c5e2862acb14ba31435a2b3f88e455b12e3fde86805 c9f3e392bb997a76281c289ef530eb137939fc282875bad535065343981b13b8 23e5ab16742865e5cd28bfb55b10aeee12a43300d4dc463b8b9cebfe9a30702b 09b61d0966dfda4682619b9367ec51557f5ab80193016eae0bc600651d0b360b 8eaac77a64f551f170a1eb5ac1cb9a76bd6c6bb81081760e4ad5d7ddcf1a4ee2 e3ba59df8ad0268b6223727f9797e9388ac773641cc9e0a4c9f8bad72579394a 2511a39268129f66ac9e4108c40f326150f5199aafa858dff1c96e6a90c7e00d 5c6e2db59cb3f1cc68fe11f9f59b462c4ed186c891a0270f91a2b4fdb316fcf0 b592e5e64343c3400335934bc9e46e6cf7ce0718c103a66a685c493c411796aa 71dca07a783e26fff3499f592903b7086c60c95c25bf5ee221cefba2f2b546b2 cce298f163494e67a68a7b97caad3b3ab778c5ed936397fa70a3b18f3d67c483 532b34fe135cf1c0279806f26bbbb8deb6d97748fe172e20ce879c2579c7e6db 2fb751cda970f3f5baa8e99588e8f174c13fcaf84fa6d97f240c16fa972d866f 618d49bbd192c2779a1014933b329f93bbf8639b6f8f0a2c7d6825ce3dfe32eb 5772fb57614dc0a62cbf373a39e6c8d40124509f7639368a3b016ed21c23214e 69136c99b7ccf8604d3dbeee0056a7409466f1a81fdae84b690bc245760e2c7d 6ceb2f58fbfbc37b8d7df32b0297afe2fe46c0201fadbb6ebcf5a81ecff34e81 8c64f284177b6b776c47044bf4791e7d446a8100c7bba395f96601caf0af5e4a 7a4bceda987c29fded42e929db9bbf8a76d0799d7c1142585f1183efe346e88f 9bd5bf40799c5509060e95a974aee29bf7b6ed625a17f866674a3b9e0a4d4505 e18b91d03be14f18faeb3d1491a4b8263905ff34585ee88e3aab0036d43067e3 92c3e91d8223ae20d80b714b68693a3bf12ea67816aedc70fb112ac65539d911 6b22684e21e2470d739772e6c5f243851a9698fb11a02ebe84f15e521b95f219 83828617a0c0a3e18cb1f94f3905535a9de70491ffe41eec0bd4e917ab2e2879 a37bc999cab640f567e16a468723341b50e3b938ee8dcf0cc5787022c1a8814c 716736d3ff9d4aeb35021070b73f2d08b7599c04ad3bc1154d3fe7741eee7137 73c5900199c080ce29d94cf2c755be3e41116bc1a2621da5ce54f1a73ae8c887 c4233e294deea09b5abf660a526125c977bec9583c1517a9e9a95477ae39b864 efed916310896ef26d4d17120830a0cebcb11e893ff81d11c36634ca6c9080b9 ed11a7e0052d5655949412a0b4588dbed29a72a7d7418aaa7347636329626637 5acd57cff6329b775f2986ae2647d784f41e953d9f6b007d054bcc6759ed87fd ade2044e2f49aab4544975d52c7a89e273cb2a3d54d561eb18160c687db7901e 19c604932c42fbe88d74cc09e90cec9cdd7e7cbefc4aca654f723743387e116b a8172e89ae4018fd9380061eabc552b72c7559da2e96d12e88e99e5b912ba28c e2dae3ece1354e82bafb75078108e3813570d95ea70b110141c71b53bcbbd3f9 a1edfc908d13adc135bdb284e95ace9396a9583e0b16c1cc64926819bb58a4e1 54cb7193f43206403225da7e69dd2c7d422484f500f92e28ec7f93e0d6661eee 0999c4f291a1b6fc03f7b6a2717a37ceb63581a2d20abda67b13d3afc608ac9a 511bcf40bd9c285020f81b8a925a0ca9c65adc3de2542b1cb0c9182029913663 d1f6eec40ab8b295fb62b04557922650c9457d894524cf2f494ff66a65a4f51e 4ae367cc60fc11d52276184a2c13ec20d28f4baeb71122cc53dd22419ebbc320 a15ad42726f50cef4f500b90ba6429b46118846b5bed1fe2b0ccded403192e3b 5371f00217e3f5d03ae306573eef80f4cf1615df1e17a68d36b57312ffc70933 57697e8277d83212fbcc7fd8feca75e1895721812ad7a799f96f850fcae3533f 2dd9b596f773233e6bce9cbc552de40917f23997419f4cbd9a0ce689511e2aa7 9f41620299a0f2400a42550b96ef5ea2afb266c42b234192ad0a69136ef2321a a73f729c28aa4082faaefd8757308838c423a66e36062a3d1c15a38c5fc7b50b ccbd9ad16f7ab5cd44b7d0d9f4a45de260ee54abdeddc6c13d92dc3ce56c4976 a9ae79d72021ffce4557bc22d134187d544d8b2fbb136a73408f2bf4e43ce0da 4629911da91219dafe727afd4dec369c2ae839b80c9c04db608a4e6b9b336f34 858e135f2c826095560f1ef9199c742b96388364824085e5e942675d91a1a087 1e287c244abaf607a39ca41871c8678bf1a20de73bb872fb25f08bbef4d5120007e33da108a40ee45b34b885a07bbaeb9fb667c3748349f9cbff4021b7adf506e3afc07a3e9e8765bc42179813d22d908c8112d74e53a272367b0e2000c17105d0ce989d4017c6cf7454ed984cad42526c3c1c091de333210a2b111601f9e50040848d1150812f846c3b416e9c732d86cc69b19d9cc1963f51c2335e90be12037c42f26af557c8ea11160380c41e9d3f675fa6003a500a90b2c487f74943150def4030765cb36b834480c510691c48ac5c340137be0d83371ae6c5f2646c6d0438017c0192b69bd9a3d95557547fe5dee74160e0d2387662b355c135a75d000333270c267424cf3374343b834b305d67e5d25363c06e23cc84c497f35b3fa106de9e0b9993613b4eacec272b7183365972a5d1482edb4c4652e89b81e9dcf10a45deee8fc60fb964ddfae858ed0fa7111f14c52e16909678a22402eeb63540056e9c9059612ca28c1e4ef8354ac532730c13d1df55b7696f80b2ed634519d20d9368341fc0878956a2660fd5f283c7c0541ceb58c0f9fc75eda0bc13542c2c04468eae0f7ccd38b4c7da493083dc7d3e1287e51093249b307e6cc62055c50c0295d3c229e322360797920f8c8d6255345d4a8bfe79df31177a1c09f2dd1e7309983027e8e4c9779e8e9adaac4b58cbb221638653b914009cb0dc3c0216a18e0a3f24996985a004d7f7156487d34da04bc6a52a2ad8f1a80a1a4a4a7654efc70556e4eef2395abd96bcd6c98ef300164bda91089671f79f4fd2040dc181b97f052fdaf8ab77b27dab78e4dd23d8287ba27500ae68c0b047e8fce1e789d8baf5031728035b18f63eabcf5ce4ec23f8c1a333ae04e5c33675a2d9a78f2197e84b0bfeef9347dccdaf58e59ec65857541988595ad4d1957d5f02307fa0f167f89b0e0fb9ed6d46ad586fed6f8dd711499430cf95bba958fc99517b4ccaab62745c0213b790f87e5bf5a88b820374fa4805a493f31159cb8b757fcfad5b989c69340e2dd89bd5d37c7a5e958eced8c00019a5c7fc9fca3bd23a46addba227919a2500d96efca9c7f0f7f7ce9f4088d3d87fb4e19c63dbfb758b64bf21afc595fa430b3acf2234c83e2509ff00219d65c737acc6e89e9f39318e6edc31939a7f4bce0bdc9a733cf494060221708a130b7834f3e477d0f2da39a4a8af6688d61c03b8037e2e17fabfe50189cd52f4d873717135e34365e70c2e7a143e3dbd38b7be3b0cf50006841be319475ada7ab52fccc0c95a58033939615d5866499b2ebf7b61066d2d4ab388166fc11d391e5c3270306e4adc84dcf99b48442873f195c3fbb101cee52e03ac293045e9e1f83a97fc961f202b2d442ca5f80db5dec206a32d220813ef51f2db9dd20765d06576ad767bee0d6247ed862f159cd853bcca63171d0623461b2fa85076898d735e0c016b13a344587ced2ef34b581acf6c89ad1ff40ef7af53ffbf9f5962fe2ba3e1e5b0feb429a65a024629aab902ac320efbd260005376ecd6e2f251aa4b2b685b88d06b6e1cd839952c7b63542b36b48d8007f3064db53cf43c2ed1b41ed706dac119543a0d195da52681258e1ae79a8cd59bd5061bba37da778ce5180dc86c5b4a36c51df6f0144f927701cf36129a92559dcd0c0ad7bbf0f01b4b43d298465839668d029b7727e7530252ebe14e1cc9b6264c0c0882768456f1d04af8dcbb561e813a9458c6a6bad4e437fc74aed5408abef50c07e91df8a96c36e651907bc45d822582e8e92119b688555a32af86a3d3931304bc52d7a067639f3d0414ff0c0ba5da493b6512bdb9cfe427d4a9ecc372dc160fa9f61bb24652563ef63d54e39b162c3cbf26d2cdbd67a5e15775a111462d4c081bcde856b664ed33f4ddedca73a4602bc6db2c4445ad1052d79131f2946c0b026409d979962b9b857acf1b57d80c76040a066f98b7e78427322fb96b5eb8380362e98c294c9a0b237e258d2eb05a5d7cf990a453340592d4823dcdfefddd0a0f0a425a9a04c3c05546eddb28dc3f68588d4455fbe0cd1b482a05e7d85ab7480b3879f5c31e965fa4b857e361ad14bbe8a399feac36bb2a6ef340dfea434a340b4f2ae9b9707cab69f4dd1bea84927d71ef3844f1aa9c3985846168be6ccdcb063156234a6113f3f82e64fdd98fddc7162176166c3ce3f2e4c8489f51f873ec07f58a30bf4a60c185f085339c79786cb2004fe8fa2ef6e412a761f8897653700175dda13b1f1f0416e6503093658dedb96d931b448d782797e70037560ee5670727ef1d3db72ebc6febb77d0ea3d874b99012d1d659b91d47b08d2b13d5ff210e7c0e41db2ec123a56a81d1ccfd4cb59aff1a39b67a9758cf35d8a6ac7e64130c44eea058ac62535e7fdf767d34aa97b24ced7c9a289b2764fe0f5486ba9f4c0c91fcc80577465c76783d50819cfe62720b9f52cb261c31e8e9ab9376a93dbf0130647d86466d405ffecb851699e116b2dd0fdd30af75cd581f0df83526ee470489896cc7a1415f57f505763f7a4dcbc911c02378b06c40aa5dea617171e6ef025c69b048decba9545b9ab661a4745b59bd011ae60cabce4f6ea6f33767e1c105e29fe512241278cf330f68e20bf112d717cbeb0de00479209a537871c49379052ab308be13ade2a6152728ac088e3a35d0ab387461c2edec17269a1284852609ab2c35f7a53982f30438dd7780c173624a13eca4180a1a655db5c66de9fffc0da45656bd136935be2a071a402640d0c290bc75f64fcc23ba34754ec93795d3017eb43f8cceaf47f72081b6b448b3914ea9b9fd86d402bce8c9891d7ca5c417007388e73b425e6f5c1f617a656c4940b40a854d9ed1df4ff3d291714d5ff4fa0a8611eb8c4055909357abde6ff9cf9d73dff395460f17084007b821adcf771902e127dd7997d412f152cccaf9e8128061316887839ea29f037937c7abad484b0429bdaf022f71abb6fd96c09f290ab646967b8e8ece6145df233ec58808a28c06203781a1c185e222a80028f3077d226265fdf40bc7dfa83abad2daf450a44f0984eb30befca0825095774111ecbc82675e4b2af2c8b59b8bb95c342a2731d202c5b359a34f810a0e894d40f67afeeb5201197718d4ec39ea3c65e79eb63ea6078d6447d2bdafcc0ed4cb55b686aea856f5d59ac9b69113230d3f711235f6bf02e26b7a794cbd40815ae091738046f89b401b4be320636aac3d422b205bb1ea064baf9120aca4870998e95ec7aefede472f23e17232ebe54fe36c90ad9693910dc9f41c599954072365683a198710758871cb79ddb150551b191c8de46a075904fab5fa71ff03155c9cb8446f38eed004ae7bc4f37c3581c37a412c21e6a5fa0dda3171fa1c97b3eb2cd8752dbe0c6163fbaff741214e9302c0256ac2a6035e0362527e0fcb4d8369b3296f2cd76f4cb4a54e78117f99212106bda07d72128c0e3692b24a72b0a608791f132704e135945144440a5ee348f5c6a7fe4b6c32890e1f039e81193808e6e8484a5d2f7ad741924dcebf98f22f4d3d030a800b2d61018f24259c0b84b16c30076756c8be2bee1091dbc76ee6a3ade9af67ad20ccb700e25a4af751b2f0559bb7170de2b24b78da7cb929d71e940146abe27ab92d350985dfb0ca89c54f905eb92249e74cdb0e4cb1d4d9f02cc295a5f71d6b1bd0d30d002d4aff2dc981a74896df33d1596fbcfb259af70ed5082a7310f3fce833020a7015f2b65afe203f5075faae9c92424ef21e798302c4ca1c8978153d754eaf0478ea341c63cc5d011bbf325fcee2789aeed1654ee839c73d9e1537a532029507f9f0d089ccc41a21487d614ca8140c0e375261087072a6bfaa1a52f9a402e50328d0b9b6494bd0f905e99f550d013b8a0ddb192aaa81d6e4ce861c9a662bf303f15a7c9d8fa0d70d9780c24d8457430dd52582f364e0450f63b718a37cb0260918353669b94811e49fad1c13f1a637fec406b686cf9f2d0d04dbb8d4bb35190b83e60fe04cfbb48f4b653e36d00c1da1fc63173092a28721bc8425faa90dda0f77f94b2c9c01cc7746f3bb7eeee863bd8a83675ab4baa281b495b17bbe81c304135fc916f31dc94f83b8dcdcd8dc164d8f931cc4e5df8489d88010ae0c51d10fc9cd964abef38805f9fb7ced5747d592435e8d9b5cb2ff8e9b498eccb001e3065c2ab3936dce77ea242498ef2f2a16841f6724dc42a9ae91749325991f78cc01d0deb801bb4ff2f4581c02b77648fae58365d512d8e9e64a438bb330ab3b6c05b805f18b706926944c73ae9720199e1ef70e6d6c059c14977b281f838e3d7507bcc427f8404bb6ddd79feae75a8d751a7605ed3e0910efef3edd4953260970088417b1c86f06e372d2194aabda3204ab6532f61d91a81e9cdadc9cc3f0ee24051d409150430e6a8f8aa1a44a776b6dba729e8e5f98f0f77d173461af54020401b59cbd25d9d5441feed5fe8acce8a3219fb330334cca7cfbbbf71247e6e1510a9c86924f69a08acb5d69f0b5e1583869beed6cfe0fbefd739e6f032bffc0680d5350fa97c119b79948b0e492f82a468643eb8050a0dee9ce7ca19aba141e01011a16f34787cd13ff06ed76b1abe68155dd9898df5400be6111865ee8c120b60b1fa4dd7ac578664b700f5cf0f07ae5e970a651802ca078829b17c1ff41a8160e7eb311dfa75eac4bd1f73dff9607d34cee478c9a0cb20339cbb42e0135749f03a886524dd191e835636851494fa6d75b325f62ea6ce511793cd746cb595be806aed44bf2da0fc576df7a42be6b3d15b479f32ffd94c3c34b5d6c66a3cd7c1a07f8d93a7e118040dbfe1f2046e6934ca14c6cd4c76efd10092100d7ff082de50e071aac6e83c30ab9e5d5efb3fabbec5c423e5a48780435c54977c51e7742c70dcc7c3e38731d5cd95275e4cf513e6e94ddcccedc47fba6b2f569429555645107bdc55a5592dd1b85c8f0696574f6886ed11ab1f7e70f6576e07f140eaba8ef029e8c7691cf6a0f72b53e171e29c6e50430a571ea7de1314f9314a4292a791d02c451766b63b87bdd189f905a62b9d9d78a373d5de6994f773a9ace2a1743a70e9e7106b4f204b4f83d558553770ef66d7d2fe536ffb8f19b3ccf6fdbb7abe30fc7b35c10b8be3e1e991e25b5da9e384eb5a8f54d2ba64217243827c8adf80c0ec976656bc444768873811a700427d47c18bc9f1fc064a1c3f2ede562fe9e6a0d5078c7d2ec4b3462019334a38ae7cb0d5cf38ef0feccd9304281eb5ae4934708213c62a0a34ead656b499e06bb621a636105c2aa00677cc431b0379825529b0edc742874255b24ef03fb9e87cc81d10caf15ecf893c22a00eb4aa34fd1dbbf08c9e95575814802357f5f96d32397afe3728701d6bf79ec2e495916949314400611afba35098676d685c9e860cab9d9ea62c64db5fabed833ee9de826dccc6f0863f5fc22d010340c4b7ff43ae463160ca7966cfc612f2897e55ef54e68ecaa0e56712e48ebbd45c39fe4f924500c091801b6f1f286d2a8978a3a91646a304704f3492f1ad887c4121520a64887cba9e29816f422970c4e056ff15097c861b5069f19cb480b4517fc065a5b22e8882b85fca0828bc8aba105c8eb110f88d9b903170e2bfbac08047236104d8b9bb25f384d504be26e1c70b88123c27d99d6230ecb7db236e167ae5a00488cc23f9d6d065082e70489e41e4ae95ef2ea06d7670296f9057578c0bb09ad64530e527eb4a0fb9241e89359fdb3c12be478917eb607a5480bba0b997c6588caa39d4cbd69d40b2feb30ee2e3662cf2b8b746a77f50bad77a3cf0835c5e022cdf429bb1d5381e6c6bbd8d044f8f322b79926bef94c02ed49c62f0323ee8ff4e35b83034eecee5b95d129d89a45746abd0f37a4f897035dde172e4236b7ebe7e548f8f9d3d9ccd62e7e3835303bf3d944f77be796700015f5262978195b41e145722318a94d16318dcf6a326a48e20ac815e48803190c2ed87a96e10c1144c3fdd963f744d0e8e977945decca30fe756f8d1d8c2c8b00d2ab881bed8c2e09210d37bd85f35981f9bf69f306b901e74e42669a177d0a05934e2180d2ba8ef1f5cb9d60acd7213f230f7270eb3f47fcb27d45dd52f8d70c28f8aeff27554c71114ccd46830172c75c89c44865dbeda027dd8bfc3554130b05cb99ca62e3c57d48104037ecd6d3c1438363f7c3c2029f49f092aab988ff026022a86a781a12cbaebe9aec086ab077904ebca529dae52257c160d7b8376d02f8805f30282ed832799b145c60dcf97b7a24bbaa6ccabe98b6aae965c2ba5e041d2de9c61b2660ea134b26d63a2def4be5838907b3172c025b530bbe3384770adee622eac3fcb2584df3ae4f515f7eb8c15b7e41d7df3cb386a2bc49534cc10bd33a7d375023ba0c0dd07e12b6244bd22f62bb3c3bd923e98e2345fce9c6690946ab73cf57697d5f4710bf01d7e89423f13e27cddae6800409b5540935dead09c119b8be476fe0c0aca204376393dfde0fea896403075d01d7f5631c4dcd2b0e267d0c9bc0ebedade98867df98a1522f1d90ffef48d5696702ef8d4d0b4a2207b944f78cdadcb3a4c9e9db66958cc1ee5dd8795424335d3eaa18ac8746cacb01a3a6211bf13038770b87d424034365c726dd93ea963cf71a1283a225deeadf005586eb14b4d2b320d4f766b598e2632eff473a6e9d5986a8b6cc50d5c4320d097560390c1e8ca3c287a43c27257193609762e806af48c5876e3d3c663fbb9601510691b81f346ecf63e05ef4ac98b258a6c45e515d31f54c7456b68fc0eec3059233b82799510d30f023ad6f1b08438eb9d54a90fd7e989d4005fb413b103903ef3deb74a88b917ec28cdf399590230a343a7864dbef94cdbde8bd1576e2680e3fcb89fd3fdaae12fa9c7270c5c35c3d0f7c28900597a72039c7fd784810f7079dfaeac85f30c35f750548a978d4b45388e538bb9e2455ed5c059cfda9144d04c7abeff0ed80e8a0368c56f0c5c4e185026d0f28f9cacc77dc3e1bf6ca8c3a0bd04be00ea765f450562683bc0bd6f027e06e7ce8f10359279aa765119274230c05ee7cbe45c1afebbfa256c5fbbbacb41cbf1677dacd6ff27519998784464802a230455cab5940c0ae60598afa8369949a7e93592466515119470ff7c6bc460548818f8b03bf13c70da12a00e42a7e466cf5eb46be0a70c703601df760e33e035924133c0d3995f4f1ed07618d6148d73d5254e47c4c4b8a5a5cf94f3c12620208d058354aec0975c236ee1b217c6608a0d69f496f5947d4d44077da3ae631099011fa9d61fe57be31ba1a93b7beb2a019dfcda6c981c613ee80fb698866dd03fad128933c3321838efd8ceae6a6b127332693342def10e859990c2b55d96c090bb188a36da9a521cd3ff7804e93526196a1507ae82c76b33508404bf7bf050b286b64ebdb8086c3e945a83ca942a168fd13ca6766e61900d0f7a502a414d0016e1551a341c06b2102211830a8edd15557394a4003936948c47717878477c40bd0f3f8fcc0a61118d22b42bf748a7eb0da2973821e14cea74138515ea1c0020730a20e2bc24ac1292ceb22f2586b0bfda8431300778328a86e45f1057a43130178ce91ef5af626414631e528520e88f25f98a366a5789d3261baa14b4ee5da0c431f2f340e0445737659af092f6b33f66920ad7add91c0834cc4f5c4831c2f00f050a5badc7ab0ea8883bfd05eb49402a35432bce9ed665c5c35ac124c43400397bdd05c5ae6132aa2e05ddbfaae9edc4df85732258919fdcdf4c9b8b6cb5c045685400dba94fa0a2a4f8500131f642f6d3e1b4fdb19a0941d6323030e39900ca81d13106d5b451dde968928b3a5fad6d816471bffd2795668b02a9614c2660812d35ba8238dda2b01cefb44eacfbe32d10f1402e458e940d457b091d8122401206522ea3d664ad5d1ca806544c02d415f1eb32c29801e87717fef6a1cf1bc07ccfacf3aee0d7876914280a6bfc6b81b3323060c924b870c00b9727316b822082bd66b50ca657d01e7f72875091ff9cbc3bd9e6cdf4ad7e327d195bcc000c909264de8d188126cc2ccb9dc33d9b06d8ce4643b54b7f93267b0389a96f246b40df27480c5f934bb6b74eac19bc463d3f7326195b67fedf6790e26e1d36cef8f0f19e36631f9c06c607e43692ce6d1eb3dfa7b2a78334bec26f8066ece5ad44a0931685f628051653cbf8c472e1b885afe6702224f9afb3060903ef6c617764a02678fcd4096ee02061d38cdb219f9ecc68b921bc835a886cd9892f6385b3c2806300d7caf6731deac7707b231b7e0134065fe0a25d1752d1f92bc2c277c14a3050e3ac949222ab2f39204ba2c17cea51ae129b64b43cb5fbb07f012588cf1770a2cdf03c910601c307f9aef3c67b2362ed1b7fc01f179b6e204be2848b160e407a7921fdc3cb0bc22d720b8c5c32fa1e04f45547ebbe890c718a1a451781b340c3afe57c2db72cb94371880d09a703661ec10558cd458f60fc439442e97d67709e0d6f22b64cdc0f1977336fcae311fa0dc8add135cea567151c03c1f66d7540dbd2cf602054f75631227b8d2f28c28110ef53908324f7edc10149dfc8d50300633048309c9d570a2343e89eefd134e6c87beeabd7089b9075b9465d7ad0f9a09ed0bcbb28317e271c5f55a44105de16e6b61ecc06e29104ec84f75f36bc2ef0dc3fb940adfe2e61782f7ac0f05289d5ca612185bfaea993eebc9919e2863c40c5f686f5c0bd0357b807ed77c308721886c1a01a572f320eb9187c8936f79d10aa727eb7ceeea0e0a0d7399c254b775bb24767443fa0eb89e7a6ab6692611b10b236848b2d64d2a85d8fbec4a70093ad42574de9827679cb4a123b6abbef4850dc32880ce04adfb52b1e3e8ba75bf63c08d8980f1b51722cf872b23bb30245a04d2b10c96a82dac6ce04b27da5de7ca0ce534770caab93a177619af3bcdb714001ceca7ce0b9d90a0f3f5307f663a7d8bd92cc819ac564c48521544f9c5d0f60aa9c52a5679dd2ad970d027d9695834c7336e88d5aef0908a0d23486a7523330d5a189b2fa0f924ff67657cab61039577ee1041c55077cb63166c998f0603ba0dc660e4e7fa16cc25cbcddeff0f2a0b707b8ae136f91501be297571f16b06ea071a2a40c9636d96048518b43392a4f7f348b0dcc7c00ad84177e4076c35e163082392e2051ff8e1a6e712247aed91300e0817e5182952b1e52edbd75212f5010ba628f5de8200ffb312afa7483fb03d13b29ee3c13a6a3a08196b2a698da38908603cfe5bf14f72f475ff5006c87f1c568ad8e79b3200dccbc65320381a917008c9079228996a82fc3c4f18a3d61cc8d34cc70b2212a87ff69771e43bd2a587005841737152ce8ac27524c494a77e28dc43bfcd28adebb9a6d2e30c36a5a106062289450e1fa96de2c0487013a60d30de5b2923a6b673825968d804f86403890a53487c0bff204539a6c6bdf04fca005e6c16da7e35ec1e6590fe8681d47c270733896b09ea282dc3d3a7fc7f15898c55c4873b267b58b03ec17224b8238a1c0c2618b510c2fbcffc5223975281957fc68528e2b732c5eae4968447ab93a12b0e2332bcc575817796c15a5458729931b0b2c9c5a43d6aec4b9735745ba21cdc0fdeeeb464c180eeb0a93091fef53e3ce775bebca66f786c03c37e967e746df008abe24891614737afa299a9c95eb184b41a83c808716e08582f10e14d07809c0db834ab52d16944c1820fe681a7ec315e13787f9a2d17aae1c100e23650ca04071e4665a2e1505c80768ffa64d8d8a0b5e4374d53d9dfdc6460306fe9d9cebd02854d7987a93c7c04b6fb0bb87731368084bd115f1f65389fe99317e2c581d500c0818c492eb4e9c59b561565a999cb444da81bc9e1fa64fbef19104c540342075cd98dda59ba801ce2fc2ea94252a9793b985afbee80dce4a3daf315895935007ee56136d1979b2028b0e02e415ae762c85b39b88d88ad30e6da25893a16e70667d1b7ec34e77bf8da2b082bbd7b5255ef263e8a87df08fe21f9b1b52602ec0a31b45ad904285874bc6daf36c4fd3e5d6c50820665cdcde3121e28ce9ab0ca0923b23909bc8fc1d7929411546f38dff47bd79868edae332afbac20ec0df1320f42cb35804c53fcf18ce091603975d7112dc6f36b744058b550ec0d0bfb687b03ea46a045f260310998fea3bdaffbca9344f4dc9f4494998b68197de459b68f014d8954092d2fd062d600b112b21205ab0ea085647b60b399d360c32dc02b7c04a992103c6360d862f263cfc7cc40e6e5d6df8a91bef5283486629494c4b0aa0f2485a3dc78b846e12139ee3eef0d32c8eaedadd58c835ac7ced89c4cae41cc0f603f86e2edba77d480345e0477a3e2fb1325faa42c12fef9470329ec91a34108da0431acfaf39872d4371f6617fa12da772c0de3f0f6957916a526a3e55f6e0a58526ebc4610203568d6772ef5eaecc71c48c86959a768b7f54e3d842fc5f106044f14ecf25f74c805035dd5f30b3401839013e4332655c796c9aa2f4a08de05ebf6ec3ed634215d0a0cb32834675313d59b131538506b2838f1906343e451061a26bb23c6ed3347cf02dfd8b719c3676fdb325a48882190daf89d8471e48c0483d91526951390263d46aba0b7b04eb00dc58e979396795cdb14c134fc929c0cb795779a13803f77a83ed21062c61d75bbedcac98477c07e8a0a83522eec0b0392727650748aaccb9bcf5339101b94772309b84dc5e1e606eff4ae619dcc6108a8369a8abb55ee18434b2ff94844368b0d2000d272a56b5f2ae46b32cdbf020b0ca2549a30e11511d3600a937981cb1fdfe669cd726378f65a7ab04e33a2340a4541dfbf277558bfa5b5daa7352273b37c00e5a8094fa3878e7831564184e400a0f978ede7b6250636ed91b846ea96c5a21b91e09f835f516f267a7bf346a40110b607e4ff1512825f71150902e23bc313e8d9bdd7307672e8feae302fb6bb00ad43b8593fd8cde1cc190fb5039a9506372ac1ed285a320e1d2ac982f41008017a694998bbdd97a1d8d5b8b38d314a66d9438ed7f1f29d6c68747b46747d2503969e217ba1051d850e32a12ba237948e44389a23f03a35bff83a764ccd9a32017bc1ba85cec052c6459a05cb990bf49d4c6499e66c4db32dcc486ce8de93590c2f06a6a05c94d459fa984dea3c95a2bfc641576d321839e5d007eba298712b03c02035fbaae76640c6c84d705d359b80dacab8a0721e1adfef8258d9b68737094e75d309a1a450ef3ac75a519a56dd1e87d4af12603836ff870059ae8d31e90a003dd8f7217c8f20717f06ad1d22452bfba720eea326db4449190796747e0d0d110cced5459d1c2dba8f915f42a353fea0cb359bde2ff8ef2473ea7da2de5b02bea6f26f5b23a3f2457c68b6d9f2965b4471d856e096438320514a28e19b4f06 true +check_ring_signature 448ed7b7c7979cbcc3593cd30bae259b155e9ddbd3e748ef5b37450c05ab2c9c 9de819303036231540299afeb839082d96b152e562b875fab0714fff18e57d43 1 9f9981b58855f94dcaaf3a0cff3b6c15704626fbe5ba705eaec2b10b83558e30 08e7839bfe96b3a7d73d2a31e7d00222b09d5176432a82affa6046230496b6024e4443aacc9797c6e294a5ca3a28919869d5d1c899fe3b604adbd3bbf33cd80b false +check_ring_signature 9bc809f6143b7c5082eb53e72572a1e9178a12f2c403312ea802f38829fa6b68 e7bc54594ef7df5a5a26f2101d0f8f824c0d233fd8cb13eafc2c4574f249c19c 5 ffb695d2e509e4fb050e902aaccd429e91c4da6fbeeb5f3185d452b337f16773 acb2b1f7a063cd28c1e48ce9cdefdfae25b63e10b30e424cbbd33de3189db7ce befe98b5e30b1e39a41ce8aac72666d45f758ebde6f3ca615467cf514378b3c5 e4238fcdd818909953640c2d26adec624c5117fc9441b28b5ae404c258cb1d1c 0bab7ca442e029255588002847f242412d9af52f1a878adf197638ee97fa45c2 cd173d46e25291bd030346f45c7c12feb88d3192d8a476e93cfa529928742a0d4300c7628774d569976d85cce4792ed02678ecbfe012490d49591242086cf00f7f2c871eeeecd91d2a23f2395d18ccf138febeedf3482d7c29176487019a2d054b9b6e9e94e2704d8761f5d89a770c8c7d75f8544dfdf6b761a56eebf46bbb0a77e74ec1ccd0f1b75d3428022c6a82ed6dfa028e91a3ee8325b9e70aae2ed10f815b3eb36a41daa4e5f3d0973f840d6d45ca19548276856f6d1bdbaca8eafe06d7b9fbcbe1975745c7c8006a9f2188621c756513bdab5a558c28b8234cafb20618bc688acb70349c4e70cf1066f63eca60ec06f954cbaf8c50ebf29e6f1d520b531ea83cde01ac4e199abc05528abaff15cd60ff022b73f7de38af4f95676e06807668e6cdf7c7f0801777764b4e6413b54c9ebd5cbdff907321a5d555b0fb08 true +check_ring_signature c08ac350ac07f719206b0de1d09b4904a4988834c9286501eb343380bd5e05d9 b71809bba86ab5ebbf2f26d20f9d4f3f4808b980d078a49a2c0de2895fd8504e 3 e2279468d55568ea398ef9fd606683feda8927e4a855cd0d3ef34ec71f531ea3 df82a11ad1463aa985f7353d02fa4555afa65f374d058d71b95cb272c92b334d 060352e11388536d6a191880830f22a5cd7132a514e2974954cf8359d4c87cb6 fb9c48c91506c56bdcd835da21a1b7c4213e26499c7dcc4041c4d7f17f22320ff6b8426ccb10c940d421f7f03bccfa5e5fad3071898d2a0393fed06f4aa55d0b47c6ee88cd05117305c0f0122fcf2681edbfdf4fffd89071065814f2b22c42011b804bd5f594d22a41eb7ceed5aa8f269353303757cc9a9a4b2e51d87ad15c07dead83d5925b731f7cc82f0b9a9a8c72519ea7bd4f04bda001bbd8c9672a36080f12b9c03faf330d1e7dc82b747da3fa67d2216c44da79676d59f26f809fc63e false +check_ring_signature 2d9e4bf3b158ac804a42959122ef27a2b60c81f1bf6c43d9fc26b40c384083f2 7c22ffee9b1c6587533aec44dc2b827c96b3797577b1753a0cb0cd3a31128921 27 53a247a0f479e905d6519d4e531560ee00ce2985296dd19d09cc5eea22934c79 4d61d8c228bfe94cb6bfb8222e42b060cefb52614d7f29de9f3e1657f2ce017b 577bee497f4ee751e1b65f35f52bc9352c3b95dd2b22b113d5ccead43cdb554d 3fe1ed211086647375181362f71d04684011e56fedac9eb6eb446634ad0035fc 8e5c2a6c6e2570c45ddc82c835e72fc78dbe0575369cb0e7554ae46bfb57e160 f754c75a8725c958b5835f6faa946cdfc447013c5239b8bbc9beb5f9950abed6 5b5a2ecfa261cdd609ad7554ef871edd18d8fd3dbc138dcf885b3d532c3c4eac fcd189950f517a8cc050aa87a67e56a26efb19aac15c8110bd990a0310cb2128 1ed2b0f5be813af54274b0ea9cf96ee1d66722595346706dafc73d4f4bdf8c2e 2f28ed338039250e260b522ba882c60b2dc209f1fba194a8b274226895ca7322 17ed1d4a499e8dfa031f16aceedb60c4de216d19441dad5e44de935807d090e1 c58e4c3cb585dffef31bb86b238dafc754707d082dc26bbcc63ad56276e0cc11 9f74b915868344be6fda921dc83ffc3ec8f04e25145984e48cb9ea2f468e4491 8c412d3f2c8574b5e386c3da6473fffa8d8356b1f63067f45732e0574c520652 3eb964a6d701079eca529f2fc91cbad654663f917a82f881070e8ce6d5ac9619 90ad172fc077538c182cf479fd4ac96c125e46ee97d85240d24d3a1dc02b5a21 644935649454d41cbf8fc73513d7e5f733a4d8c475d13100946fbe8cecc43ecf 78f708c0271944ffa39b3e0e1cae1231c89a28d407c8d84761213eb3416921af 14ce29dba50b994c21d33a411883d7acb1973ecb4f3c26fb2abf487358fbd637 6dc9dae6988415bf2092a6a77511041f5990cbcfc8d04feafbf2091ebd89655a ab51e0beef95d3f6e9fa6fd5c37f0ba691d34dafb9ef713a873b65f38c542402 265b7769696547c87ef937d6ca2556c87231d865d3cda3ee184e9675f3282cc4 77f941715fb8e9ca02eba197c1c49b8ab361114905e797cbd0419c8adcf4588f 8c048a56efd27912256cb963c4b6d389ba50ab6eac2b5b48deb59e6a2463d3c9 c43faf85f322013858e2106acf355f9f0a25e84c09670007898c86c08ccaab45 0efaeb2950fb0e3d27d19bdce9cf3ff9e8f348ce2f9b291d63b32d7714cd401c ee56a77ac8d5db82b29fd3285701c102bca22801eae252a530842c1c10a1ef2c 084cea874e188ffd3c9ac15064fd453617f571b8fa458fa320794b1c2d414d081c97022147ed0b498abcef3b85473cd98ce2b58866d13afe9c359a066555da0f9bbcb1035e41b1bb30a6ada87d6c95f8e12c166323f2467eaa98b89701539c0e6bb4f3f7fe000765ab72072f888130c6f8c8ebf80df48f648dd5842ba7d5640fea2758cb7e7dbff86564a1c9bb06af6dfe029236ff993db35f75236a92c3ac039a465794df25d812a681376117023796f0b56c3ac1d93e0577b95fa364b60e00ce962419e3fea8c798fec7528589af629bd5ab6a075e6cb84bfa113976483f0a61bf4e44c436fdfc3f730ecb4e29bf4f6e3f6c8dc8f16c088c4990523e012105efb536ed4694e7514902d478128148065304511e76c38b245e99c7a98e0a860d85f96883c8e12981b68384c3a8965e3af861ce66ff69d39509eeff778520930fc47ca1ff656c2f144b1cb8ffed100fad726bcce48366043703a37badc69f2e0f92df06b60b89ae287b74d840f27ba16d8d0c4c11f3f71e98d5f9c00e3f11fe09a56674667af3a3be6b68a853f62a706d12e2d6140be4effe40db9d0b51d1880936134febe2b84e4f61630681cd17a1d681d88395ff7e941facabf09833af1f0ba371eb09c545b65bfa993feee82c6ed41492775f6a8d96e2dbba8fef009daf04e8321ca5c93aa44b45966cb9b451317c4d5a1755dc325d27d39c34a610c6bf0d7c70b98639eac65e2a9a0ba4b85e4568cdb4369907d381aa6827b7a1c43b02008a9ae174ab16617cc3beba86130f08e7ab768f6499de51b47d67062bc392a800061cfc36d4e8058bd33663719b4ebf8f37338375371f876421825a27a878e20f37a82cf7938fa7dc06bbe44d292fc684aeddbb1ee5433c9607880a4d4715670efb76d7a6d6ec69350a0f8dee58ee2b86eb6f7f74b665af934cc0a78161f16004180f2a90888f62ebaa1e48dfae0b9c2fe001f20cedf910018797a68bf6b780048f0ade63ded454611fd832b8fe1523b16d7e9efed84008c49e369ed1470eb40195003fdaadecb32a22893c840b6a5d4dd08fb9e6b982113e53a8f770a2a10a0fb5d625e11e4ba73f1618ddc90fd3fcf85128ed76dd5d4486f9ef70ef0716040a99b90fd9a973d60ec45802866cda3e18ac6ba18b02c366f128e052e7f899cb0f5c3593a0c5df3df347a8e386ea45854520fc02175a8620a504193abb2fc8d102902e1fb2b27e84ec10863a6a3af008c40320b4f89ab65bd70ed7c96a30e9c4f98eb3a15d8c9c007a2e64ae20aaf7318889941f3bf7123a4b886e4a152a77d703865679bcb30b3ffd072c34c59b441ef162f215386d1d267c064bffc1638e8d04b4b7c9bbf22083a8bce5de3cd6059f826d4b8a1d7b65f8ad6f8d869b77e2f6088781c56edf9f71e1ca44c98d74d945413eb131cefcaa7d74139d7448c8e367083f1b15d5d7224506fafd7e7fd83bf9dd2368c37a4c7f2da02eada549acb32400b97980c1f300f986d8a39b9104373782844a873c9e8bfa60d68630c9502dfd001f4a009d5a0ec8faa0feb894535fea562b303768be117fd22f72b95453ed3207845d2a0d8883fa1b30884b8d47a840b0b5833eedf5d5001097e41d8efcfcc609755c0765719420df6cbe278ca2da8c6c74db61690bae445f2086f97766a1fc0a15fa9650f6caf9280a4c6a125da84e47018a9e2ed2cb732c86ade12faeac5004bb5828d4b6cdb1e10eb3d0ab3245b8a65538b3e8ea3f232adc5f2d371fc1ad0e378183541375eb12df1d4c9fdcae7ae4e965c56fa71f34e5e6bcaa0999c91c0de3a1fcc6d0b3fe06e78b2a4116ed72d3c3ffde8ffc55f2494caeae21bf2f8c0a7bd4ace2d45c5a59f91acad8189c2642f19a80654afda3af104c461db058b907dca37299e614dce598c22e5f15ff3662d854fc64325d5a858589f24760b2ac048261d37d7619b1b0f2d11720fd5a29f8bdf41e53cf20300226891312c51e970bb8f57e2615ecf0b8e50ffba0764289986bb099ef34f3ccd45c3cb445d5275708170c0c7c7a93c209981abf5ea78279f3c16864a418c0a7fa31a0194abfd2be09c48042e6b9d2887018b37a32e7720f74d1f30bdfa5b2afb4cd5bfbac56fe1b00797d9b6d31ea5d16e7e6fa86893fc2749a173f9f83a7b2a9bdfe6aefd6278605ccd0eb871a9529f6de1c92aa1fbb0a1d4bfb4c0708b133523e06ea76338f210d56709631d04de4feae5ee5f03dc90e7ec1cab31a50b37deb99610ddbfeacdb0c102d21146f64f83888d4c2666c1acdb2080e072e243be35470211e658456c70953cc43aec02121fcf649c2b96da610122921cb90266d5018ece2f21f30a5d90d2788b84f5d49a8c21e32309ce52c3dc1902fa5f21fce53c17157cb21ac29d4017d1a0feee366d0d850d25eb95172ba18002fc4523ff850c6c2538fd2baf3a80a false +check_ring_signature 77ebb6005e399ff65b9f50dd83711342597267b83b238ffa851e03d9b451246a e0e52399bbe881513595cbc3829da665cc7a198d1d470a6a4f098407ec3713f4 6 00bc4c2b9b190d318f7b175616d60a42332e6cd0f4f26b416a23bae6a5cb9f4e 7754ac5942576bd967da664107f388605dd692345c96a578e9262ec0df5c6a85 5eff378c8be553d86934c8de669f93702d61f92652e3b816e1e82035ce81fc9f b5ed62aa763c6bdb61111de071fe47e9a0c8df3be09d8c0b3b1d99b925f39513 6367343e4690c2218b22ce60d672cba84d59d40f5187ae313f63f91525398579 5cca622c1670e4278a782504aff771273c047ff65e2af9b1fc9437bce2fd9de2 615828ea20e1259a6bc27effd38bcdea2dfe975a47cf8bf656664ecc16d10b02917a8adb5bedfe2637d47b2b5e0dd6fca05eae44ec5697646d4c399fed2af4021f56fd721fa6b1e2f10d05f211817ac6bbd5df41d34519c10ea0db93362e6c0b0316297898ea58e7f95972a7e1880b6880da85309c098ecd1a91ca1d58b8650d3c44e1ab77c78b14c476a7fbd97e320062022c92bf3ff884a038245e2d7e894265fdf01fd202abe65c4d66713931ff171da3a573ba0291e767075d3cac9fc71f3cd2ab2ce6a9864514ddd511f7b7790ae8e9eeae992dd9b90e509ccfc2eabf0bd00c97055ac87f406599ebff4c4e138015201a1518684adc6588f10c812fbb060b73bc8f56521aa10e3ae656a2de4940e615bdbbb50caa83d66d8feade1abf0d39b1edbd076baeb3a4a7c48c5cf49c2226c5129cf09fb0a5e0568d8f6d3d0e0510ac568ad1c093cf4a540cf408cbf78e8f6d9e329de2a307f8d32414588b3809e92bd1139a713e3d294b60574419a03451245445ad28d0f13f42fea374b98700 false +check_ring_signature fa210f7cf8ce063a69528812947a3f5443e8846616b143bb65bb6e50439e25c2 47fa01eab12a3f265f236c4413880f9766f15cb01362dd630974ff0eebfe083d 2 29e814a4906d2220805359c8b012aaa165f58fedf74cd24e6d3e45282edc9059 2668d7b9413608ebb8760e9e172e632b72b85af5c7beb283f4afe3681610ea10 32c9624c6a4662d1a0d8c1a1011992b3553f8c4a39586c6692513cbbd1388703478d22d485368b2d4c2eac4bd069bc5365c0d41a053688f2668ff61027593c081f8a9df685be3f0f7ca8ea328b99103a563fb59c2b319a1d27322601f9ab77049c8fa73d59fe2b934142b44f5c7ab0035d329bf9bab621dae70bc32cf6d1c76f false +check_ring_signature cb38c8d6f39934cb34eadc0ef810359803d7ef0866defade037bdbeea34b38ad 950e76f5d75a7687884e03ac31560897b2d088cfb135c998a03301001dd1a113 29 80c8c181f1878f3cdc9dd86e5e724fb39840e48a0f4dd2014e3239b63e134dbe b8c1093189712f4a97bbb1faae698d74a913f2952239f81f6cb4a53f5064155b efff5a26d5a2c35de96ace2ea9050fc1094da30e587a17232bf8cabd1075f60e ccefb3b3a858938726744d7d71a2e06af670d6aed916481bfb66a63a51cd24c3 6959830c01b1442d304386d9ed5d6699bc5ad7375db77a741e89300307369584 98238393f2e286cf29244094c2ca3a917070e625a7c2bc75a0161dbfbd97ff1f b482e715d853cda306d1c58f04d27e41af3a583d77cd559d60cc581e4242dd3a f4c2f722a6a11384fd6faba72c7d302ff45843233f7a7dd1c3baaa9afbed9734 aa26d4e162d7c55d2808350b7dc9797a539d794d1e97e22aa7d799ed2c0949d0 0893c2007bab128bab954280dba3feb96e9509e8976531ce3b6d75d5f27b7d26 d548449dcbc2cc0657446663fbac880454fcef7688c7e25fa9df0498f1098880 973eee52fc516edbbe8e0018eb9f236794384cc4b0e9e3e59fbc1c18842ddc6a 9502e4d422439074eb34f27fb8d6f0efc03536d304d9edce62bd4fa108782df3 70a12de0a0511bd199454265beee94e8a304e5d6658977a80bfa509b3a463f55 d94d5d7dfdc0e5ac82a5a593842bcc98148abf5cf8132f42fb41fffbb24941ce cdf8753952a44a55ca852fb516ea0f06e46cf9fdddfa5bc2c460f19e56fe8e66 dc995cc835aa4aef46da27b4ec367cc8a73272d151b8f1a7d292c1b24b58e1f9 5c39dbf71b0a7f0d208f2063606418583dd36d0868caab466f2a8d2cb2dfc4ab 82d931ff0d3923ec26c04453e50118d469bdfc78cbee9151e5c785e3c3da3b18 d045af1fea4b7d68fe79da2a6f6d0397b1693505a026356958823114037431ed 2edeaf6dd0734e42aaff6ce2fc54cd3d2b08e42a40b8430525ccbffc055fb378 4c5f19938ddfd443ce03c8c32db345f7eef41400bf389cfafa482f6378a9d68a 99559a6a168893adc5dc7586f1cdeb2e4ac871a3a14c925d121ecafb8dfd6705 dfee1845af84e7559bc4030848eee974d1eb6bf0d0d91d068a63a8c07bb81aef b27f29fff1657f6720fcb84961ca2b71914acf2fbefb85b1b25f65de2e7a4f06 e4e4662a7f76348da3663e7773de2723fc35f72792d29c5ce0cf312448277f36 f65113921c2228e8a3ee215aa3805407635c0fd24197174e920eb5db386d15d4 79e74c2a72dd70a2863b2900dc82fd2251d5ff01594afe50753ff9a995ec96cd 69abf187f8ff2334eba3cb5d0af9d337f7a592347e78fa4ff06f89898d28a755 bb4a845a3e60dea584592356b4b61763d2cca3a0359bfbe51a168d341e64130e6cf01fbb4766a6c613a84e8688ecaf764d3bec6add58a2a1e08f9582a37cbf0de4fd1e40445f10493041782a4a0e19b13845fab9d79ae7573dc5e0489ff9cd00ae78fa9764c11b2eaf10a4b34a96dfabdead186a4b646603e82bf5e5c2f05a081b52103495911914a57a65bde68bdd660ac3e59b44de4e9441aae8076a002b0216b376da05a98082852117254c08d815cece96bedf3a989bad8c345c09001e046b98418c7cefe1deb4796eb774351d1f085221cec284abb94655ca316d655801a1af0cfb06df87c70118c8001723a36ecf9e3c1440c2077da847c356b04e4701cb648e0a55109eff0d7806aec706c36a2d84f040a6ef1e623f1b1c5ffbbeda0ca4568574e59bfcdadc0f749208cf16844e131dc94533d4a04e15829347f3f00346c16bd13363904330c43b21db0bbffcf1c0c743390fb96a7b9b8636d63c4506e8417ccec76a1fc8f2b2681f6382df0e44c57ac424b6e2fa17a24c905dd6300d0ea36fc22d893b57599bf4411fbc998e219b87b06011e8bd881606b60fda0c06375896f0aa1fd4b0cedb15461332917091cf192d056f252f56610401ab922d080c79adaae243dec34c1618e0eadb1f1c989392387134a5a6f6702e5e9771ad0a6ee6a1456c24bdff5a159bdb51e9468cef6a18cc54162b064d8dea0bf6523b061c9cf9ccf61dc28fa419d330bc614be707a31595f96759bcfedeea6afcb4ce084c3fd358e90bf7cc75845875ecad6471d701181212373f0cb9ac3f573e9a3c06a585c01a64f758a651fb4f8762b26ed12c626fea1ad217e5274fa13117129503ca17647143bc31b0d55196cbd8826b14ca7a8ca8c090e809d10c67b7390a270453dddb23ad032605b4b15406a565c647ce3cf7bf08b89ad0bd9b9868cf9deb0f0779b1ec7731d9d99f3005ad82864d4aa3a2273fdeb9a91f1571b0e52803d2032b5b50f241e638d0f6523316cdb98d25759da3140cabc12810f8f3daf0c84e02a31820b02808ef23c093294b76154d6fcdb5393ade6302bb58d60894974edc02cfcf7a9fe57189e857653c2ca1ea1f11fdb7ed9b7d2ac5a63803d9150f283801fd705e2c1c7f2659d5a6411b348441d2f77392b2d356def25d94bd78efa2f400cd62216e250acd35bd38b8b830f3a4f7f39c6ebed5de1cb3aa2f3c8237416007c1b634382c012f69a4343caa71eb4bdf37613036f1ebba80b920eddc98ce3a0f0c6f8784506618a83d25a01ce9f9bb43baec0e95e99118fe5c8a4c4c70ee4d00ac8d5091beb11375814a13b37f3149d3c39e7fed3b854e58069cf806ff89b80324d3680bbf2ceaaf80aa07f8787da60d91378fdef80c11fc150e4f95861b950a38b0d6b18485a4d0315a9a76d280fc10807d0bb69a59783c2cd2190c73978a07d41e0c641de091f880628f0eae293b28fa12db043fe45493e9ce54f7135919089402ec236b6103bd8f6137d6f714bd692bd3f0a990fddcbf7f73732450dff00601c5bc67bc98ac3b2037c829eb2dc06f65ea4875f40b7ac5afd45d3f8d47a1096a13b2df6884270dfbfe9683480a12e9680204987e3d85fbbabde4e4c5b1d50c3cfa05c68533f6063033327638a0304cb35fb247feded313371615230167550c11a1ebb76a705c5a309e60152d1f284efb6d4373040674bac58dbe675d0c3609ef4014474377f3a55e34dd17abe56b530b7dd0f87afc5843aab60bc71d5f2c0a09fd4ef13d66ab3072806b932011f6276e08a82bd0ce9779486feaa37eb94c0e2d5dac2a61a2a245c7b1404036d0845a01f52a6a92f6a2329b62152e3b8aea0d7d50180589d9314936f3b72a391e726b94c48c47a650fcdfea1bb29dd40b4505860c53cb160b0154a6cda7f234f904191220bbf8a9d18ae64fbb14b6d46e6a04483dc6834f99c33f33f16d47e1e89c4bea84fc11187143fd6c57c74bd4e7420fea11ce7a15993da0b56a0f6ba5a16ba5c7da1b07f8df362bfe608903dfe39b0e734c89203cc55efee2548732c4d968bba6e44991d42dabe44e47b659c6e6a80dac4bdc561f3ad154bad23b659110d3655df19ce5ada49b9176763c32b2e9db0d3faba1c1c7d069f6f8c1c980982d16bf4f72ff4de6138f03721598afd4c0cc0ff2b1afdca39bc7d817abca0a6792d871a71d01b009f025026e5636f287a3ab0c14c70e756c14930c1e8fa48ff53fc43eef71ee721253fb19372db4dfe702f302f65dd8e0f663dc9e81dc49edf1323aea484d63e3ea50394854a3ec816f56720d5ee0caa93d0f514a6c02ca0a420377f88196caec425091e4250310a77bd7f90479d1419168498f0ba14f2787c5fa201209ea482b07e56e2da332b6cd2aa4500622a5346bf78c36ae3836639d914508a23c77e52000dad32620f17febe8a70b0c713a9527c4387d33c44187bed7ee78a896c375f26b88f97ad474665153e74a0bd727b4bb8860ccd86723095010433d0e6c2776a36c1fb657cce3bb4230aad605934b87049ef84031baeca5e1bf5d4fc9c8d2beaa9b1632417a15058a72f0eb08af6e62a361ba3618d94e45a64ca81ba3705e2d60217bedbacffacf98f6b33109 false +check_ring_signature a542b4f0134acfd5a4f8f6a35befeb58db410db6bea05c5344bbbb640267ec75 585f6f5a6e50b124d7aba3740f51b2c81a862018c57a98aba1ecc971d8a3d9d0 1 6769184651096d8a48d0c4a825c397a0aa057121431e76d089b8fd7eb270b3a7 307ef04afccea2d18179fbcc865400a574191295062b1f5b4637b1b5c1e55f0bea8e68c9441239b58cf9d68fc27efc285c57e2680680aa6e2a1e15d32b35be09 false +check_ring_signature 0eeee13008e83cbef6f003613eb0d7db94b7bea8f74f6ee62fd9c027d35190fe 1a4da4d1a89f430aab6069a5582c2d2ac6468e19b3377d57372a11e7fca18a32 1 68645db897fe869d51b0c1cd85d2d2f49e734573e8b7a8a5cf342af8fadd3246 7e476f48054a9a38618899ce8acb26198f68394c0389a5afe0caabbd557a921ada88ea6ab26e42bde04632a2edc9b02ae5ab00c39049c9dcb5addd5474560b0c false +check_ring_signature e788b66d5d41016011cb27b588d38ae451e262df1edcfabd1bf4270d3cf0041f ade7dbd68cf92047eb41b1f0fab7331bc0c88fab3dadf77794c1b2d5558488c0 2 119888ee1b1c57a9e4d2f4ba53b7475cacc4f2fdb06a3c2504e3fa5210b78e74 36400e9df314a3fc8b30404ee2c1216eff2d062168064ef74a67f2b1490c83db a5f96ce5140d7b3c6206ac03f7b06667d2d1176cd8faae977383068ceb28b20ca88a73597c2fbb72260e87a22719cccd88875583c4624c22441e102ef8161d0f7d9fc825ae0db52a3ec56646073ae40843796a2fa156392ed6e8565779a9060beba880ab77ebbcdaff977ceead18c8b4c599b8680457aef46b081f7dbb725007 true +check_ring_signature f0f65925d263f52e688b3b42868eaf4c8477f0a965893fb698d661bff0725873 b23fea05ab777c4420af32f1037779000ac87260d61df051fb1c632368b3c974 1 e631572d2422b366c8da1535cb1f79218d9828e45af54d10063db3aef8839bdf 869149b7b8fb9b0f0530ef45bc9637526a124654cc263a3f26291fde97125f0893ac1571db8418f174795cf59a42d5cab11bc4ca19393086d7e8a40e52d856d6 false +check_ring_signature 32355f8416ef61f70c7bc8f3d5ba3aec874d91f2182b164e3a315c9489de3ca7 2ae2b17b03b979294848facc32de52a9d9c856bbf30d648a749ecc6335bb925f 2 1532ce62d82c8acbd32586d563ee901e447c3c49d32181a17de6b9ef93a993b3 c0c5bfcff5e8afb1f20a1958928b5790986d9381ae57a04d0dced02b3d840ed0 ed504c9b4cf92134fa82c862ea1f87791362661fda80fd0264aab92ad5c29b0bbf2f54de4ab4a28aae1a4c45e7a5504f22d90d9359add9d937b68cef3c311c0ea63ee21e5f51ec6671ab70bb794e88cbd2ccdf4deb1d9b803b3fa3608773c708e5e8770355a1495441bd04306353a3ad10b03b71f0bc48503d9ad637b1e5ff0c false +check_ring_signature 1153252c8953ea16022e7fcbdab603ec44df5fac7c9c148209a6855a5d15707e c14505ea7781117f69a32c4bc4f0c1fa92b6af1b45e0af26105a70c06a9ca2a6 4 ef71e0eb5ace8211adb6d9bee7f11f30f6dcdf884ae129eb40043bc7a39319d6 5c9c9c7094bfeaa02f16f323d4a1e4bb052d37cdd8ce74019051e9885b809825 74bed404b0543723391c11145f117568fbf5671e552cc6e00f418e31f28c9689 196e63f038bb5e00068111af63e18801e08f76ce71560bb0a9c8542747e123d3 f07432126dd04c703b01465cd9a34a1bb7e680ac6de1006b20c158f93e80880d2eb5ac46564d9a3d196e528d6771db9fd7991bfd7c340fcc308d465f16aa7107fe9df79916d64cb907c1b2a0c60cea46edef31d2ec528c60c5363f54b53b4e003aa4960a28219f11c764573a931fa9267d6a848cd7ff9541cc49d1df864e0f09ba62bbc25fc565962f0a28dc374ecc757cd2ac947415edb3a5721e620d2b470108cbbc109f5aa5f1e321f3d4254153396df90fbf280137ce2907bec0413eda0c962dd2d174becbb6810e4f36bc42e4a752652a732162925f468f6a1871bf940348c2c477995f43a06d77bd444ab19ca1536cc0773fec2059f4e083bac1dc5d00 true +check_ring_signature b918dd955b6436bc9922e2f52786e64566de14f564a647ebd80235e73fdb1d64 1c0d7900be965f4915913742e14a02adf87822cb1f5f82dec0ea835df8fee01b 141 90453a926320ddc4f45de37973827b097fe8009124e02743ddc0b805dbfd6adf 47b3976a970e8d335243b30620fb8a35530df297a00a31425176b1a4f2f9dced 52dc86e04dc9db106be1c7e0497472d2149415c5b5b3a757c9d71694042f6d7a fd55d9af629810c4467aad839e74d8429698e6dedefaddd5955a9ae85bc9b4fa 587bf5c279942a8141cf32075738d33a86707b2d3bb93399b93a93aff53acd27 99bf1795909403c283c329f88223fc951cfa510956d9484889c7fa797937f6c9 4b2d569edf783c69155dd54748a5cb7e4a8185529abce9c37a53b6691e90f0d4 09420d474e716a38839c8a89bfc8c2b6756ddc1df27a656229a18f7cee099019 0e236830c37a42f68d060c1b7f7a778b87ca1e46e10a8176a019f7123164feb2 e86eade018bb8ca1c9cee80cc0034068698af3233e28b89abd50fe6086ae2946 76bd9a2cd3c51f3da1966edc624bb5b6b7444deef709a1e8a417e089826959ca be9bd0de1683e4a813e6b033befb11c8f9932c5eda207e439fd2bcc27e2303af b72e49ec8ff1faa7b3575e9d57acc9d4eee9c89542f82bd741ec133a360fa234 bc1874e51ddc47db24f1abd3e0d7e538b0a925b21b40e39864741622a6bc9af8 54d49cc70f037414c60b919d4761bafa7e78038ef85fe9b7d6e17e6762e2db65 646fe09e6e93e812564106097336516256aa7c2abb9c44ae5e69d00de2db72bc d5f6b4efc3e98e43f8dcd661cb48f3f26cf6c4107a8343fe42a9517f4202bd7d 58a4b9285f224b7e1cc84f94080d78bfb09f397a5a1639a46bfc5e4d04000abf 7431a0bef1022685575b48972853eb2580fc9c34653b8c460e35fd065b51f2ce 6fd24d314ae8d2de62ee1eae274b415ae09644af858311410abd01723ece6ed1 c02316f3db61104e414e82ee34b04cc127c21e9197fcfad8fa35731b63012be0 c4d3a8a6ee91b35bba2d425704476af3254cce820e6c4398b3437e909f10b67a 2ca6266ea82f4556d218793e5c9084131d9163d0e509069458dfea95a26aedf4 a99efdb94160a9a42a796c192fbfd636707d356e5784ab0962879eb4f18f41fa b33e47c7d66b08c19007c7eb90a60b797153f0227065df45028ab6608b6c19c1 1d5e46c49ac2858da693da5e7654f54979b391b675783fdae5dc170a4075401f fd1ccabb12599e78d32bef9eba225bab0e98e1acc391c693556b80a05185a068 417e47d762050fca41d8947f0cb54691f6fbb21459d976383043f695728a86bd 1df989c5639a1e7e483a5c9e361d209611728881f8018665a8dc3b0ba51d39a2 2523e11725f6d9a216178ff01e36f8cc4a009b064dfbb83ea8be48ef21a4aa02 b9cda108fafa5a644d4241fb9268e4d188768d4ad681ade0feb3058a52647f42 f01d5a644b1c4fc59b3c82a8598de8b4afcc6c3f4bfcc21e58f7f83e32c13ff9 2691198a8b99ee5e5e8b67191aaeb14285f7a67d9c10da5bf748365e329cc673 254028ee256b5ba535453fc60b2606fbaf38150fcec1b8b69d666854a776d71c 268ad8e6ae8a6bf0f94b489099f8e09713d17bdf2e4e5841de97bbfb64e16213 a522fb02ffeb3045aac24fac56cb3fedba88d80b051a72d3a90212254b8bb049 4070f64fdb697da4ffaae32d7a748d9a5f6d5583195de503a8925a1554c09791 366ac68c553a88823a2d007a0b1c5a56d7190ebeb34c8666d9ae2b4fe4a676e4 62556333ed29483edf926c45d70105d9311b51b16ae81843eca22eacd425b0bc bb8b78f69027f9616008c40600d37cfb963d07179da323be9ed10dad40b3c1d6 4f03e543584a918029ef89a76f66b9dbbda4560214a3b8e9b7f65f5cb1848fa8 44dc34d6a44008d0f70a5bad0963fcbc83650fc22b5b1e82ae4d0d3c2c8a167e 040ab22b11621f5fdee70a8549ee0d60ad54a01048b0b03ed972d6873d31aa7a 72cd133b89a724670e02ff3ba2fa176815dd62d099f49ff1ec3a32c2327136df b061c77f9a18e87b9de2d5b253251316207686d2ca7567e01dc7ec8b93cdfaa6 95db641d07ee3600a72d65ad18a15a9b9ca01547c557f81d461e50ad2b51c147 5e73097624a0c3a0af676cb7dbea71f3adc2a282032c692e7a95090a1fbd8980 9f3297480057f0f881e95922af2e7eb3434ecdd6b81f115a426c3fa62ab94960 5ed58ec916367aab994751f4b2eedb3f9d56561eb5151a5074f66f288fb9e0d3 e304b45759435a1781658f08a397fe97ef13ac66caa4c58435e4b3409f6d808f 1d75bd63359543862a1c5fe3f1f69f07a5ef0a46a13171315e8dd06ada45bfa0 179c416a52bfc17c1f9026fffcd3dcf690e7509fd77515c25641095356be0bfc d57b0a245d21f6511fb0bef7d0763c7e7b1db713fa4cc818e4a36f4de3b961fa ace9ea56509d51fd50223044efab1f247492c0f647882b6b1c99b1ebad02845d f93e0ab41560f58f14eca70ab0f41aaca7a2c529a9b34cfc1c19bec0e6adee74 5de4a91419ad2ecb941d18f9ba6ceeb00bee1a0e5e7a5f2f4f33bfeab7ce5a69 cacda97ab4e698fbeff9f9956cfbaf7f1440449c5e3dd955cf4dc003053962d2 2bd962644fae46d52fbf72dd4ea136eb1a050a6b182eaf7429bff0a9093c3177 8571d89e9785964784df47973c1046291d83a00c488ec39e9eef805a56df4495 9e2fb85f1be0a4354f5a893f01d7ebcdf121fc09f3c8bdc5fe51e8145d728878 3d9e6f984264adfd312d41140f2f94cb293b3444d4f415a23da2de2ea78b7849 1100ce57826afd3c426b072221ce01b22ab081395c7eca0ff871ed9de3001693 f3096ce11bfbd6f66ffe2dbc00725a4e4cdd2730c19abee960db8dc16779dd49 6cffdc06019c582db8b9fb71bb93afbbb2b34913651ef2be5af8394fae996a4e 9949f85035f136404e80a990e81e21e914eeeea7064daeffe43d4460c3765713 a7e78880ee1ef86180907db973b5c38c9436945bc2f4a9f6f2a2e37731b5f05f bb5b8c63c33fa3ddb45d6a02b22db6b946fd67d1d479bdac287ed99ab9ca65ad 7e4853a789d314f6d96ffaff80636da50e6ec4910c69e2434748e07328f1c5ad db1616d0b9b707e5ebdf2b41cfe8bda3df24f79e84c1e973daaef0981413f9ad e220c323205e1841c095599f45fac86d7bd84c48f0a1e526015cc3edd1cb0e0d f1e2110b7d04af534ded119247bc395237805ca894bc0da70c2ff11787d5d3e7 5bf5be8c68e2b9c6a369e7a3d73bf6a90bfd043ab461aaa36941de9c49af6a37 674af613991feeb40c9f1ac415d27c0e7a7378a14a71584bb59c81fa474e1601 f63f685718b829aeab363ffed93f27b52f2a08cd4675c12799404dea83eeb1ea 82771dc8cdc5b6bceb4a3bf1f45b6af43cff8f8813c41de5e4bad9962990f7d1 0ac6d30f321564683ddb15a001411450a1eec1d3cec5ce28db01736dbd3cc371 bbc2673599261507062c4d16e4217767d40acb7d77801f2a0066bbf19cf27d90 8a728a4ea83a14e5ec772be82212fbbbc84aac26e30082bc536cfbe82b92c4d0 6868136249b23c859393ad946745c8d2b9a6c8ac44113a359128ee7483bf9562 45c60265b92d80bd759ea34a96785a3ea457d189784df4deae2bf70b102a4cce b80d6d91fb7e4bb1424d41d98dcf6165be0d11201f20eab5efb9d9e30c7feab1 3aab2e8c6a346b3e36e00e77ac931a3c6ddcaa7f581d8796b77fc4bb57b64414 3e8520ce7e96682a9d8a0cf5a159abcf1e63b53520f5023eb1f5fcd146aeeed2 fb67e67055a51bf209b13ef21f3f742a11e5b6a909b8c704addfbf5d00daf573 f2d95e7142e74312a431e39dbb3724c1f65296e4f51600ac06298741d447d3d0 adb81f99137215f4ff1fec63a41cf624c35beb52a2548fd5921fab6b106d2b10 2ed50fa2d60562b134fcbeafab3d87180afe915a511e40687d3b9048d3727da3 822c6a6586f787dfb9aaad6557fa7c9c289f3ec143df505d8cfcd887fd6fa307 1797dc3f89762dd9133136c95bfb843e517c0ac3002f7fde47a7cf4bdfb7d9d2 ead4fde0fec69bb630b815a04e4d4118cb41c51354d85b102eddef0d7ebef80f 989b2d17900a70273e8296baf2e78e5e929883f58da39d652ab8ae5fa38e1975 30a0b3e1b75e92f964fa291fa9d80f80bdcb93672b538ccf9a240e3fcb238f62 24cfa9f26bc065e6cf11f166fe660560e82406c3cd67484598c149c64027cdd9 fe75201907af5c0ccfefdd37cfd8714adf5b09334a968d1347f2076a82611197 9dbccb13957cd35910bf384913668aa42afd4c686340e9dabe12356aca7e46a7 2cc5f99d396215ab2fcfab02f77eab05eac0ec1c5625678a87217666b04ce086 d258a72211d28f394acb0bba44f25c028cf021666978d8526c6922d7bc7d9fce 351283db2763eff9b973c16506d3e8c1785c5416a20ba1588a45694dec74f7c9 9468eda59674ec1c2c863ca43bfb97e955253d974fb67f7182cec596e836a73e 7a2957476c39fa0a0cc79aeafeccd2d775d63d303486846130658e8fadce1e47 4780e9bb54133a60aa7781c74b11aa222119fca904f5435eb722d593a49b4626 3bc2ef0966dc591f92d3ef0cf146bc2046f34d93e81d7b4b3e1e46bf9b474098 0f7267f309613252c25d699544f95bca9314cc392bbfd6fbbb89c1c86dee3f32 c1a471cdf679fc0a7b2eef4344093751d9a6725bff58024eb3a6c07bd96832ec 8b8c11f40fa9494a7387348672583e51c670a3aec0c7bc9c0a21bc9f20979ff6 b7e010703a7486e0ad8f96624626c59ab50457180e3cdec1879ec075fb02e2d9 cf8bf7e7e6b07f494c6842a10517798b448932399184c7bd34e5d33e89bb423f cae094ee6f1eb961a59c43a5846a2fbf81ec06623912dffb477ef2fafa422f54 ab1fa10cc73565c51f766ca2a5097844b674bbb4aeb9f30f6c3a743a7b10f1ce c508fe3e588b3b5dee12af26b605a2bfb2d5200a4f9d703bb48dc8a0c1ee6caa 01be9a1e23b20d1527f6455decba6cba27d70f1c0bcd26cfabb18f16f3fd5f6b 9cd58b2693158c46cf75332fc9c0e46c9def36c2cf6c7713cd76975809a781cf c6da5ef04adc71a31af00e5ec6dcc2097c64d237ec8fec0db56f0f1929434422 9b07adbfdce57b9ec360bfbdfd5f8dada9fe62de0cf5e98096deebad20e052c8 c25c4c9b18023df421a9fdd86561778611085206e42d9adf3f3f48a6cce7f001 58edb35f29b752c2d3c09a21898f396891775369d9f839a0bea62362884dc85a 80b6929a6a1cc77ec7c79c0f2246308e69e1cfac24961173936d79e8b2945713 8eb36c2f8a0b2761462c8bc3d49142bb7887d84e494832e060e02e21a7bfdd3a d2de23c136dbd2d28b9a05c52e367de2134762ae53d4d101106dbdc4078cb138 f64f65730dfc2c6715861bb8b2c9202b1f82d6e0db873cef5e5d8a0b754f3b32 d35db1d682991350ecec61072c1009e07a2759afb9ea10bf59e6b8ed4c67b43e 676e43e3d16819d69910b3c950566fcc3167e0b6b2a5c5bc7a527b0ae730a0be 9e3685faabe6fa6b05bae15781f69abc3dfb03ceb24ff629d4711eeac5f7ccd1 61bd817a92659edfa5eb2d2508bac57e3c98e07bce0024dd8667e6cdeba61043 170ef0c8cac2ee9f59c35ef867302c8a0329e3d048292f8417a9dcb133a94d2b a885b09a08aa0cc39b12d08c9a1803ea9838f270a124847cb69c6da820c455dd 39e9bb81d27bf69ea661d84baf50e7b4ee85fa019f66e5e3049a271a7cd5a3bc e62e30b8cf883592df3843ba9b30188c8223415d2d804125956c91057a9e0371 ace864c5bbffa05ccd61d6a3f0e1f0a1abb0ea1e31193ecfaa4b72caf6eade56 fda204aa115758300282e1e5a3f63eb07714b7982a69cd5722b5b57461e9790f 35fbaeafbde40ad7dbb4bc1afa34947e72729edb19cd920bfcc676aff72594ce 7c4b7b5ed6375710333732379af42008a4317d9c61ea919adf778c93e6811804 77e2eb291be5d273bbf293dec2948d618af21a41203ecef0181a6700b759dd83 31a10805f8afc27541bb76e790b2baf2b011d711361f9e35c0c8b9b9c2406213 808201e1edad69088999896fc469e5d08d88a145a8bd6e9ba3a41abe2a816575 86a801c15fda7e4e08601146a16a47a133ca3791a429c1ac5db811cb30c7a716 6fa59279f9dec2349f2b3340315c8333ae52dbe66561d9b1d9cbc8892fe23cf9 4b0ba91327feb751b2db09373efd93d1bca719b257f5b47b4b148aa56eaa1c12 f862199b329282f0ab5bfec28085f450c0b681aa3742281ce23930d3c2492db4 eeef20e94bced11d6eba4358bc2291fe7dd0726947bdc8eb30086f392be73c27 4523b62259089562c5c383ea7ea07baecc1e48fbf0b9592855a6f1a814b5c2f1 b3771e9037206cf808193d7981d3d192f77311d40eef42662a0613a4192b5d020f2ecb2c4cbceb2ce1c19c90e458ef9009f0182e5d37254e040ecec60d0137099fb9778beb117cda07bfbfe1a7b8c476619e9917b950998cf1162f97c2cb8306cfa52aa0ad4ace19d37d8871a5f00e4664f80b6aa598a9503877ab8151da4406a677f949efe58d0ee6a84f765aa1f50d3ffb7c00aee4e307a1d5bc483bf36d098f6a2229f17cf0720567e8d39ae35398817c8da0ffd6ee89eab508e1a1dd6a0f2e772a092b7063a94f390645f64ff1116c62206392ab3ef9d4f4320f2c918c09e2961a0a67bf60380eace432f70c8d42970ec14b607c5192cdefb85a2f908607a55c0287da3733613bd1e5acd6ec3240fc8dcbb7902c9f9251bb43dbc07e220be2ab26f02828197c4654654750376b8c6b6b256bedb0007a7a1ba9415ab7dd07611dfdb4a8b1453bfe4c35887bab817608fa3ef454300f852d8ca5b58894fc098c5f81bf021c78f9b43136a68db480e54c498c50da0a8bed68d97c894c60600438fd926745e49b5b2ba9d3640cfb52e304deabd674ee680ee725adfa79dc9b036e1406777b539d5ec98228b93c1a9f243e4ffceabb8d07464db990483ba55209744afcf7f3fa64f6de6f6c1613b053bdc8cf372a52ab0d82f8d9a7602ae5db0488fb02f458e4cb8d8270fd80acad2325a234e9fc14655685b2dec6acf85865fb440ef3e618ec8d4c34e8ad3791e89ab82c6e60646f2666328375dacf3b5570002247531f1e2fd43d5c1aa9f99e9f16bb2a4b33ea8371e37981f460dcf7f46e0c9963fd750163d1fc501255fc63038bccb067ebe3822f1e238ddfc373b98ef6019cc100ab2f47cff05e721b979f79976807cf6d786fe318a51c4d83b6e6c67809b2b4a9b8c4c1ad9ee50a7814144d08bdaf056e66a70cfb06a6a3f1c916fd330bb7bf9774aadc155106966e7948e41f64b413317b5d5b10d4b82a2df0fa1f080736b6d6f15ec7ccd02fcf92af38bd5487d4dcb3f7e3bafea0cc58db38f1f60d07d36fd7e1dd41f16bffa23421efd5c44d418f55da39e25857545df21644c5c70deecc6166b14c8172bb773db8ce0c56b16a4a4f05efe0232b8a3417e24961df00d085a7aa83196135e1ac4bb8463b726d525a938724228ea3691f8c5be0081a0c9aec0d1bec84b684d3944f97a5ee1d106e6174b06f5515708cdc15efff4e3803be43c51050ea40e3431d8fde9bad5b4d506b62c502b8c50b6b1275ee5eeed30d7d6c9d0104f6961e04d37e376794be1a25ff5c7bd2008dd05b920d53c662b80d7743e16e719910c3192f1bcb4069235889d7e3ef76aba7204615872b96ffa005038c78f42a62c0a9f4872382da83c2fc90f255294dd85977c4e26ee2193d1607cfb66012d0295dd8bc3059c511eb4f91a6f882b8414787454df11c0ea37a28042d865297d7c287fc2299df5c5061721079c53f65989280336761c04aa09fa601c3886bb42f48793f3ecf8dbbcd76145af919335ec17cba26cb52617bf14c1407b1bf740a19d7562e311d87dfbe78ab404c1eb04fe1d27aca496c2c044a31a1029633aaed81795945a6de237762b5c0b032500d058b4ebc108937148ceff60302f34a10ace85b8db15b2a6631895c3acb74fd19b22b55be6cff42003a9a8289048a8d900acca2924cccd1201dfd6cb4a2e281ecb32466fc137bc46791b671e7005a732ed1984daa729f457f03e96cb0642f2aa0d8dbdd9a5f786e62171390d902b180e5c7a7592ecb9de4750e1c19089dfa221fb096b8950ddeb6b5e7f59b460c0df39ea4a37b8b204406526f366a10a55997ada1eb0076322d2f7396ebc3130ea478c10e060ef625a59433bcb725f395d20781843a44e50bdf53500f8b13df08ccd0c583c2f5ead5511879a905952e57b421c5cbdfdf2a77bb15e8bfdf727c0eb7701dd2ad4258e80394d4e694abd26b656453a133dd16cb1a7af2fb0a5702007734ee4d71b61dc2b00ee3ea4729645caa30572520bec35ae6ac44e94a60a30766ccd6ed4306a2c1c7e4ce72f91d04c41e005b46ecdbec4fd0618d5950a97a0145adf1ad310bea0ab0a95ad2c8ba985f0ce7163c5b9b36ade48b8491d1a5d609735b2f914f4a003d080de2bd00f657bcbe78c252987c987cca48b4b7c70277006afa8d0871dfe29baf1b0a4c283a1cda417ec9d77f93bec62f35e67ea1570008c1d56e0560d7510657925dadde60af33bda31481ba5ee275779bf7023a08ba0d890c92d9cc96bf78b0c034c878f3173a3210823e637cb6edec6628ee9b80510aed3fffb2058d2ecb0f957c7221512cb20760ac7e17ed7643c4c5eb9464008e05bdc04487649c562e6a144421d709a03408a4f9946f2414f81361e8ee4f7ccb03d38d01193d5796b4ba7c2700910ce94db606c2029005777be7bad3510a6002046189d96a25833e6cd9ffa1f1172d7055d9d4d7010a74fbf252369d076ee3d70baac28a0af38ff16189c1d9a3b37d1ce0c13eeee2c138148aa6f6b281a945b009a473abf6a4006a16d50f148ce75e2faab17fac604ea519997509b94217cf1a07dea9e54136ea066c80ac4afa330459fec923782d4a1ab16f4b0f730c33b39e02beb38e14a53398ca2a02ccc3f3e16ca372d80a032dee1a759d0378df7e806b0eb012e968482765988e5e1d993b4f1b67d4d788096cd27152d2e38b60a908d90726c5802348c56456be09c475f9f659dd7dab97b0eb1985d383335e34ac123e0d4ad05b80ac2609da1c3511a845ab4a0b015d1916db35152d11421086aec8850d290e910f01ba67b20727ad4c8c071550d199b7ffacf9cc66c6971700ca00060eb31edd45bbd9bd0396e5892da69e986061076176856fefe1a5d97a74a6a6ae00aab1267a76b7fe3abe0f77b7d83804cc737a56c31a15c8cb609014ff68466a0796c3732679bf2b6f87dca5dd05882f7ebb6734a70bee72f6a4e62c71e0624e0ed0a5210b8e85065b3a029427e08b4529562c06f2dce056b0a0ac0ebdac9d0a0cf7d5838d8b965bf8bf35ef05e7088a278d5e144aab1bc511c5f9ec5722ffb304cc0cf9b1c64466ca0bb78684e16e6662211c26ce069575bb417dac9ae494100af278cf540090efdc0af8401df0e86ee5ee748e0f8756bb6817eed9e8cfabd407045ee4dd87a13395d4ffc0dbdc1eace40b113dc51cce069c1d5b9bb13fd0f000594970d207d9c67c6b416856432632b6179fcf8a069d8d9c2542e674761e1a053e830c3aac91de8b4c0a55cde20ae296bbfebbc69bf7e839cc67ac8941d7fe028d35ea1541b062ad0a3d46e5cc6f61a20709154e94b76045c0f0a6d0be5d2e0e3859ee7f0ce99c42bf7701684639adf597f14c390aecc5b3ffb49ba08aa4d302a8dfc93ef2724a3786631339e7771112cc6e0da664350c6217ef68ba41f90a02cf89dc9f5e6c84b199cf039e932458f4852fbba8e63ef8901a6917471533f1005916712f438c6926cebef983b0553ab898f6515743a271dcd52f69d7254a6106a9fab6a3765808500fb15c2ed9ad7f658e57b162177011b40a74abe56e552a0f7ff945076b22ce60f48f696765f7600d35a0cc30e441a4f5f2d0c8dca782ad0ffd8ca1f2393556fd558775a27f49d5a41611fe08977ab04d6212b3e10cfa410c07c5784701685374ea7d93f264a6af6b8ba1d0734653e309000e394d036b0006a7db4401029f550ddc89c2d07dbfa24e0f2fdbea42416753409f33e52d2da905008b47b9bfae0c933313814c5c5cd6b39baddddf1a9843a2a1447d441be52100da732a6d1dd7111cda7eec498eaf63fc9fec844d617901cf1a75128081738b0db43e1707a91a37631dccedb2fadd56e76acae5d62c79b7e0b34a63bb07827f027521e79d05ebb41837e86c1a862a83b877ee757627f97fb3b8edeb9920f71904091badc0b869401099505748fc5e0c7d1a5a59f51f0028f09d12523c9c141a0bf2be94a448d73c8382c5a83bec5d857be8c2173ac2acf49cc7a08be657340e062062541926a573093e4ccf7c4220c052d983df04f606fe9e8f0b44861fb0c20de53bb3135415cc42312ae86446fe8f97e700c6826ce0494be672ffb24897cc0f1261f5f5de236f0c6dd116dab6d36e0a0ec2b17540add626b10a2a0c3a05860c2cfe217f96f1cfdd25c77b1bd5ee1ebe8b91e895f8c224a6cb894f86f54c930bf5682b79c284c201367fb16f89e786d9a9df3febbb1c366c116eaa05feb12202d3ff71a295bcfbb054311659221843de9e52ccc970ab79f260224233f058a001adb5ca5efb622ba34a3bbb86ca7ff4ebfd4a02d1b96add3e1c76865a37ad2a0a113f0da270c54bfc7c12f98392d8471a6ed1ebb532fa8ba50f1dfd93828ecf04cccfa6b2178fe0e6aa0051ca18a7d6be7031843406d4be203c3a057e95b7890035597c4240d459f809c693fe29586ad2999f610b7e56a8ce5850493dc33ead092327da19d603da96b1dff7fc701b98104a54f8b15762f9107d6582a004a2a40fc6e095f8dfeb66ac87df3f9669dcfc170f8fcc393c437f79ace652d7d945bc09e851d48be6d95180d4a7e84b4813137873d1ea0843eafa1f2e45a45ccfb5ea02739f5cf0d27a2dfdf8e8b40054368715778b382af45a6b532c83cf73f79cf500f05841d8beed579860b990f57c597afcba29c0055deccb59f4c0262751212107aa0ca501a0090dd22b264e0a80a5eaf2b682a5da92cd0df0019fa0a3565e6d0cbd8fd34c2674bcf983c59378aeb73246c03ac1a00fd77d033e316783f407390667f472c866f6d0086ca85ff7fbdd705effd797b14c09adce93c8f94974ac44093dc362b5cb4288a4f6126d19f6561f0d0f2370736b3bd4bd59281436ed7fa40b728649c58ea52d1e771dcfe64b1ea50bb22a38b85a6a04fcd0909ce1fe67a305ee18abbc0c567aa96ff5b216aef322623995fb85d1d813bf76c9f50ba145590710f7560b4a4142180cc7a928120c145003670325314afea99b16088dcbc2600ad48a9bb023240380516948c5030dfdecd9acbb7e29d2fb1148775bf3e0248c021d95b18f08023e5c36617acc003f83def2880e744f98b7a1f11dbce76029ae091dcdd2cb37934866259268a75f2629a1c0b1246cb7339fe02424aa6feae39b06549b4c0c6b3308d06053d11bde367d182e25a5210e281b6adac42d168d1e020fa5c51a69759b373216821c399a3b8dfba0d48a096cd8a8a88f7772a65ff5a7005b55319a9fdfb59300e5d4892f4e5cde92c4b56da10a20c47195ee1343f47d0c038d28502cb37aed32ff2fcfe9fbcd5402efa82a3ac7a58fc21185538744560749ef26b0f6852c31661a9b09d9765be8dfef0e9633171f45a282a05973dfb0083d021804ec11f98e726d9ceb51b64375b7389162142c24ed2d28a3aa09a9f90b1edc21d1adc08175e1bb79b512f00c44cebc12fd63626463f86b3e774b2c950b6f97bc2bcf101a61353cc39df875410ac596abe28325a36585184202363704049b7bb8fb991e21672bfd970b73c4b89315eec079fbb4edafbcdcbe8a6c066105e544f30b4cfa97d2f34a1d2a2b62da343c981259462c442933e5149a6a8fad0cb9ff66dadfae459ade68a012854dbccb3cd98ab03df7b326440f0707703c8d0ddd001adecd1a714157736c8dea0e03ce0e48693d3c3da09ca3fb5ab05dda9e038250e9e4c7e5b70f07c439b0e49186606b19b2d88b9a11c3e69c68223b3bc503149886b2bd7eb6deb9d0640620cc9879f8595e03f04bf42478e70653c9bd4900d75f0bc22d834cf529f6289c4ed02fb4422c56cfbdee1ea8fe7356ea5f09e40465a6f117a48f6248e550e0df4170f7dac56191f0eb6966dd648442539dfda3078d12c5d583c89a847702b63b36b04c8ce3a5688f6542ace2fd6e15e6fc58470a07ae569b8193d174b4023b6bffc7bfa66f42bdf09dbac11ec973591a02adaa04560a94fdeb94147529591c80227210a013d7aa16dddefb5a693c2a43c49eda0e355b75ca8ff17babbd904f7c849a3b86ab04ba142b393f891eb77e9b9a77170c98504e9e6c1f820a9dca6b758fd24ab81061ca012795ce3f24584f6866d1ed0b7f608ef7cb314c0075c5eb97b7f8d078fea5a249f46e5795a9d9c768ea632c00f2a86c0eba9b7a25791c120d984c7aa765437bdba025525d2229a7e99dd7990bfac0143469203fd272c9cf6f6bf98c90c88fac447be7c71f6cf94d34cc743403f5d7ee0a95de2d41bc33a483a8aed6070f5dc0e75f466bb1ced3cc429b2de503112ec2ebc4bab4236a4cf630a3edb72be38c2022c015062f4591833a51573e0f4313358c96c0ddfd2fb334783d9dd80bc4d7bffba7a928553dfb49e57c41e708ca72c9fa77fd49706b4ff88fb3915f5c40d61e42add5cca81eb910b903b7d4076467ad8a53b79466e9edd55e63baa7ad9ea2aeb3da8e27f4acd978d8a6b38f0ba153313a545beb342018e46e67eff1b1bd452d0722f2a61f2d5fe37c99e2e00acd26bac56cdd0769fedd0891bd679740446d7a85bad05631924b0e1cac7e810b1877a6099ebaf54aab9f5e966463eab58199cf5db05f64a33ac0750a7cc5f9014b10367ed0a3271acb7387ec3e5bada1e339337752c5a6393c198810d998630523eba284ca763b6510f39635ff3caf89f9cc9d9fa8153074857514a1252c440132b8b95c84531ffc988cc1e38f5f08946cf27ce7a505e36d8da0f12b243c3a04321a6a57f01dacc8a887db1565d31533d32d1994be5e452adadf7c61cffc7109bcb58851de48261a1447a52741cbcb02edc275004eac5280efde595a5ee58c01ff631b9a56cdde823cbaab4fea0f083ca7eb57d23d4aa139d290a9f2f1276a020c786d850a6fb2a11df6e11b751c26ba1e9a9100b4df92aa6daa0ae304f07c02201779154062363b0de53a4caf2e3c97dae76d4aa40b02621d12d4823d03e70214b0eecd526daa1c7fd1788816aed3a413a01a0e4332dc07a6bc2e751092590f9fa7d4e877ecfb73f2dc66a781950f318b43445e709ba61f09ad8e5c6f13e70062c79f9e5178b2691ec184a476e52f5a7bd352f5251182bdc49b13a3ed232406314185b1ae705d96d72a7bd134f90679aaa56806824f38eed142f2def481bb03c0c99d3d943c9f15da5adf098bd13ee5fd7420d973b67df8738c34f1dff9f00fc7fccad403bdf533e6f1b5a40729d3efdd60eddc0ba58555c200b60ce7d732004b02fc02c6da6104ccc1df3a7fb19a5b8f30d9400e4739c833eb40f845751b0a3a0cba2325426cb729f00c1a4ed7b1007ae7b2faf2c78bbf0674cbe6657ad00dd843a0876063e3f856929c06f24d59adffb79f29aff7f304d335c0f66ded140fe4694413ec6a678df03895ef51b0d625467224ee478b2d10a0e2d85e6e4b180228b0b8f47e77478416d8be8806d4f1520182974dd3307884af5454b2ef4ac30108126b848e81c7569aba2711748353501432a042222abdd10207dc42a8281306acbc1f7b8c55e4c98e3e90133f53ce06fc1de08022bc79690f07b4467eef0b05a7ea2222347cddafa444a3397545a321198cf43a3e0c65f5277a95ff999bf708d2cb5f1bfb17e0482dbf4928d565f6a004e28b6bc7680560b5e285e592a3dc0e43572cdb28982eaa60225bd4f1cc49c4974a7e9fec669a2959de67099a5ad805cc4b3c849ba5b77c070727f23b74b92599daeb424e3f5808acf9ec22bb078e00c74777ea489f346df3137edc02b2ddecb6dd19d98cd173fc5901df240069430d5dd29739d0b00c34406964aeb4ef7296c3d960f2886a72cfcebae31b146be4098dcb03d4a9da5b80e8f3dba300b65d6c564c7014d1e846eb77fbdb4f263d4b0a07144c5f34577ca19a85fd77e85b3d1a82994a33faa55e623149b71b4ec4d806d510aa83e6505437cfbaa02df0ba57ca14ac80e5b3c6b76fe07aec5ddac54d0120209b318e1f17b0d1790ac8269b1e447afb3d6e84694eb5d0ebae997f81a80c6b36433a2fa76ebd2e0ea6b813cc8fe690e097188db76579d808375940449f0fa16e46dbb61d2ef80523df462e294b8d1db0f212157409a7113b1e9a90dd0f00ed483c8955ee7296fc8799e7b2fe1c2bc384d51e86422781ffe69a6700931807021efeeb55f62c7c017305079d9b6c5381aaf7439d0e94dafa079d3f1db79d02678067f98baa05995776004bca2cff043767b8fd4328f16ef7639119293bd1085a230e49abaefc52441e5d6f276955225951c17d86df5535e21a49a95b8a8709befb23894fc168862ec4e3f0710f659e31322c1f9f7b056a332b0015d6db23084cecf319c04fbaf2df0c61565343113937e56728ccfe6f6d9e50e6cba553cd0d68ee77d9cd273ce190cd6cae41daf873fdfd448d94e2a314353b72c0f957b704c7f963048514906642b99a223e4dcfdb0524e33939bc77d9092b2773369e5900e7ba138f9da1d44639d3bf8ac50e5a2051d3264afc00b0770601379640edd507629c91f59b4264854c1599405aa5e546b0a64d76ebb6bae68ffc6f3b7df5cb0e1b7e6eb9247a8927215e71a9f12c594ed18870f6fc4b12afaed513dee8d4f70b5e289ef42c989322e7fcf3b21be9fd195ad24e5b3261aeba003fbc30d227320200abc1d2f52e4ae28ffbb7ca6ad286c9bb3c0cebe38bd3268d3e9f445b84510c0e338391c894139f54bb3c4f21ee006c5fb9eac68f3e86a9a038fa750682300a9ee9e7aa7a8ac9dcb61ffa61f45f46d50a7a0e0aadc5d288b203bdbeee003505c7836ca56f6b787abe816abfb633b4b6f04f74bb572d4cd03b6c9fb432557c01a2097d3d06b818f18bf9ac453b6baabaccd2f43c8bfc30ba1dd1deca821534053ff639196fe9f714ca5d10f269aa0fe52fc4f84c5d8ea569b4aece2e83c34506aee13515d9326ca671f26edf18aaf97748825d82f70d4ac0c01d187064797604ac2c5d06fa596e2769a538701c617f9d5d2b65d3c7279e23d13b1e40aeed2a00bec6fa5da3c8008d947086fccb119ba1fe9b408d3ebaddfb7dd2916947edf90e8bd801b714a845546c1017b89e51d0d56a4be011733cfd8e3027b4346710640457fb1fb9c4367cc372399b8d282c0132dd10831e27dfe6a1bed50e07289b4e0be22da8801e0a12e5669d886b350932d763c93aad00fb0471484063c6e2d49508e546847304407257b3f01638abd7378a4dca3d58bcb8badf8a59e1c8e987120e84c3b026edb95966d3a3380e4352f3c93c960d5aad1939c8785083c12cffec073856e371f2a7c4da6c45d804c408e13acc8ba13be27f0232b725d5c210a9c805d2165befca6c8c503ba28ae71cafe7e7972f1cbb78126e445a2fb7c0a6b93d02372475370238c0401d765ef8ea11a4f027b2e099a264964c60137fee3d7de50a03daf8a1f939763bd2cb126bc8eda23b4e55becae14e52606b0809d21df0db092a58c5940503711c2acfa5be4f4dcde0140d7dc2d8bc358b378facb13985520ce188ed855a5e7c4dfe71245783c7c073a190837140efd99b584110e94d0a9f0839302bf2d0ecad6598c078c716f091d2d8fefd469e1c2e529d36df4b06fbf703a5a0c6e9dd88e1fa85cbbc75fb305c7f2a5cdf817743ee42403dce62882a03048832a94ecaa956eab49f52566d0987013c154987e505f01152ee5f6f3fb51400dc6e1edb1027db881606e7d77e83de6933ec0eeb4264a29e0157c99d659ac10345dc6a625a774f748985f6d8cef32cb88dfb457172da5e33c6ab0a6c04f7c306633dd79f4cab609d56fab72dbf8963b4368afa2426f961f085f9074af2aa0f05ed0374b49bcd367997a918137f36d694fd314b5c93c8393237cf20d12c3b260cb34208aa590eb76af2e8e8b2c6148189f858f5b82bcd5aec8bac0570903d8a0282181261a90b6b517234f43f044206a0b870b1377b963fc8b48dd5f7fef38c0ac0ff3ea6bb1c28c53dc6875ea61aa0b0c2dfac6f9d545e213346681a55cc640f1ee2d170a373e767dc9e3a25b42bb9127d6ef81731f14b08f15d19226a34c9019b0cb19caf96ee98132e00d628268c7c49e12b031068c46596b21b089476d406c813f7356bb49a976779418df80207ef56f111db223be4c2ed7a86427201b703447a52cf02d135ff88a5655eedc64e5109f142c86589fe2bf990b57709cc4605ee486f3056077b052318033bc88bf95dd6247e1c4944187b41caf518940c7c05e946672a0b048aafeb7f40c7a1102bc76d692c7b8607e3234b650d384e03df0a1a922350b83695cae12ef8f896b601a59aaf73507a46c560af00ae85f627ac014781a55abb9303eef95b61f1ee73db370a3dfd5e432fa948b881b5d30a5a9a0421acad17abd6e64e3942465d145defdeb288078ae4f14176019789629dc594012342aa9bf27485a4714803a86922256216d1feb4e90a464a1299ef719f2f1a0bbddd096250779c8ae88b09b4924522e3d407569f0ecc1ac4c3e4addbcedad405b40b3d500e4b4e5b4b9921effcc1a1e7afba31c6426203db3ab8bdd7dfe7370d89ce03e5ad660a4eca017323164170da21410013ff8d75a54c4168aaed79f207a45e6f9f67ccba1ea193b9bb2f5b85a9de1ea17e927c0ad4546275d94ca1460aef2a946b8ca77081f594d318cdc14b5250c2ee8d371ef42955eea3e8280bd60093cd5184f6930a1ab1c6340aa06e860663d6f7c71993f8b9f36b798554f5600802f802422a6c67a70a685c9c1b8d2f0c0eb542bcbf9510d8724d0c48de4d340f8ce9578d05629e01537f2424f1eb21a6158c335117f03443d8869ae2acec4e08704c27930330b01c90dc0415b0907e7a61676d125677a3f878a0c0aad5fb800491f1f49996138574f190b2822786431b8d3fdb2148bd73c2b8e031c841721a05295cee8d6ac582525e3c2a827c5d8128aaf6a28f8360e9c74051a4e70e1d6c079c53c90d5430408224bed804ea4720b437893523cf7361e4d707caf9fd343608bc866261229095d765fdb16bbf5635074ce0f64d112703acd617173084fd14051b335427d7ec7a2a42a499cc7277035339838e6128687e37932e9bd6f2eb230a06229526c16ffdd40b11198605b9193689976ef17e19078870acd9d0d4ed650519dd44e403fae18528013da17c3ad737049841a9db92bc52332323ebfa5ea400c94fb856ac0dbaa1133a58f5331b4466212ea2db3078bcf61bdbba9c51a3bc07615fd0d9e344cca00e681491007754a49c622082a5e6dd681f26118cb37d050fcb15c8dfaea4d446dd83aff211bafda65cc75c4251140482e404f14a0f741507a98eaf1002aba7fcc34071d9e97f4f64eb2b305176b606a42e413f699a106001d85013add656ba78881c3eb8afd59bb9be5bdd4738c65565621a6b9706ca530bc70e6af8fac96a8677d1f189bc00b0ea2cdc7294008fad6e42c1293fb19beb0ad723db1517a9e16767962e5e403e361af2efb377d82d213c5ba52c7c848a58055a649de19483be08a69a5fcdd4bf7fe6385d7c6ab5ac193d8535b2ef4c000b0fb861b6c101ef59a2ba507383db77d9ac111d46dcd480d454067098a37618dd01ee7d336d3bc593131428ebdd182f4b4bdee2a6a69e63272fa25cf84a2dc7de0ee16e4152c41461a271c0a3792ec4648ceb5029337798ef5d08db0e0600c369031a05c8f5b6fa3589bab7ba2e179ea7a3004c5a4f833464be273377a23e75a50247d66a3ee076bb11626b3d95d5bb8e4c5a5f9f8553ae06a2343a9a5c23a20a0364920ea34c1f6398138fa09413765f42e8284fe681bf647a804a81016bf26c046200e05dbcaeec182b01cb14d53f3f56598b4eff39b7c53e906074a20b07db03b9d31dae35128141e48c191edb2d6c7f7201e6b71496ef95f00edbed6914910c2b7eda3bc4b73def34e42c9fb39a35473cb95d56907f3393f58577a5853ea30dd3c6796f0ae8366659ba357e0aec9e1b1c0e294dfafcf85a88f607469f287003711b9312093857ef6d16b129a3c76b8f4ce4b4cc476c9c2d5705239b81a5ed0bc8a25b209129368fafde5e331a86e7706244cae4770cedaedb98bb1de603ca0f246baf3684a9e62aba986e046ada681cd6a1845d887af5085e863260ac4c840c77a235a2756130f4540ae634457e73d47ca306dfa5a01e106595a98c3a39ae061fae8c77457334b295e4bd186a8ba830c9da02869963adfdceda12219051180a8957cdcae5d3b84b12add8d4416cd4f547b2722accb48b62dc4153b9f450870d0c061ad54011eff8cdb1b8643ab61a963d152cf7dc65b0839a4ae3dbb10a0d0140801f70bdd9facdebca57a14cfba21b23ee8e0a702f44369d3fca69762aad00129b72b97dc09b1743470c39ef52bcb63e74a61007c3db0a89fe3bead97a4a00a1ac06262c269af819bb6fd5d7a20f47952c07b2b5d7f5aca252051c3a4fea0071df65993ab554916d3be169c34d07ca92c826b059b922b0b36a001b2aeb670868efd7045f46d3f7e2d7be3ff75c2602ac06f404d9cefac248058126b5dd6a0b1cc0262475f97e6f8b8051feba7aa1eebc30891e0faddff5b5e1eb845e56cf0ffe1bdabad130e39d10b83e8ab17571aab6364276ee7c38851ac7f28b85d8a804ec7d7ff857ee619bc8b627950b0a9b3667c94d38954f6672fe8f35b34d9f6d050f57b662cec635cfd5a65a61e2f539c8dfe18b5b9862cc16f3ba6d4f5555050fddb6a703791ddfedf415e815b01ce8af9d208fba7b609638bc86d7f542d0f308 false +check_ring_signature db2b07494f56b785245bd791e0907bef6551dd5941fd23c83dda168d0280d507 67591ffe02719c64223578724b35ee25625d691b2b3a60f42136f933e6d571ac 42 904c54f663ab81ba29e0d2b2e86c6e01900f7da983222579b9929aa0b8bb0986 7139fceed61005e084bc5db023ec3812c648c8e0436c16738bdd46d8fed42544 6bbeba233f4eb3ef81a2327f55d39eb58724b2b98ba5ac75fa747030322f8ca1 7d22495cd71ca04a559a22a8f84e1c01bcaab10e90c1a4bc73c66979d261e8c2 c8b78b74d36c8a99abe0ba0c6fc9a3b5cf5025802870cd356b25642a1cf53d97 9b51d6a2bff00fc4f2cec3799afb2ec3ae3f1115e7e1607865efc311a1464424 ecbdc4fe3adc1f022bcab45fce10999f6be2319df5cdc835c9979f8d966c381d 6ef271e9af3057be18dd0768848ebc60e25e2f637227758929eb446591cbaf14 cdc99780afee87a7bd87d383ac36d043342f90b8a9261a293c6cfa037cb00c3b fe23673eaaa2ebc527ddf34b11ae3a59444495cffc3bec4dd50d63b6010ac7db 2042e8cccbd742bd1dfba8ae7063b5040b8f0f6d14bddb49ca4da1b9d880f617 e08b0843ee7e02d00a44333face90d6c034eb98eb6d0af575240f47b096bdd6b f6767ca9c720e1862d02763d72663bf29325d51f3532717b14fddc6ef1d2899a 1f858e870c6f5ac04000084c89e9828408bb16a62b0c3638ebaa36ac18dce11a 7c21a1715ad750761773369822b77870b8e6478785216845e4c37105803cbdb7 dcf842b2bd81baf0fdd74c9c34372a0033fa7db0901cc20e91206d8946ad1893 e79fa515e79ac06fcafc2f217b8d94117397222bf0d6d2de3efa9037bee37ed7 836e5ec26294983f1155bbe6e92613b9aeff3f20fb8caac9ec25f6fbcc1ba238 ae861771e50fe52f2c4a051e7ad62075bd6ab6bfcd69dfb3785de84dca8a0dd3 4969da47a048af17cd4d8ebb3cc1863f807489187ad1f160fbeefe14ed667c20 1d8998456a76aad2b34cdde58b988caee812504419228022fefcc8c600a05ea0 1c2b85f8750ebbebc96bdacbdc9c45a420c95122776a6fb18368fcfd85a23441 d0249d53ab9270713fda713f9fdc99e94f88c56f8c2eaeb450ceb38fe39dbf50 9bf5aad25bbfb36a82e4ccf409b343eea1f3aba84482b10d64c857ed087cdbbe a84dbd2749bb07e05c664d97e9ee0a35e1e2e3681fddf314218576456c699a6d b280ff80910d8fb383855de24ce45e573b0288d082ff2e52c938d9b7a67e8a4c 69b6aebacabe9b885e46f1f45feb6225cca4112a8d9fc7fc56ed0a77c6520c86 cc07dc5b98737de62101ee62aae61f27545560bd638dfdfa81553d02b76381ee 44d823ae4eac565bd1d26e0f44eda67fa03a607e2cb64224d2f162fa07fb6b07 93ebc4edd94a5f77ba296630c606e912bb228b26a5495ae66be0598aa7ea65da d59f23d2b372bbfcb2228f56c41a752d6e4dceb22b21f490cfabb400078a640d 1bae33333a6f36e977d2d4e3e7729ad99ca88ba7b27c07fcb3ef8d9c833288ec 9265da6286c8cbe9ef322af01917fff5eed450830d023e12d0cd297433b97cdd 2535487aba8999c443f0b1a9726a4258b6c127d7bcea9456bcf7aec9772c888c cc555ed9d05a4b7c882aba8e04678760c36c0c6655ff7fbbf885ba826a149528 dc8ad671511c5a94d8187def53a3ff4afc8c8a2e35c4f70c166d2fef229744f2 47a73de0cebd42096fd77ce294360d276d83f442347bc4c00dce97d4219d481f 99728e10033d59112f57e574ea46fe381b519abda11ccceac719bec9717596b5 4f888b07d1c888cc14f7a3d031b15d81a12b5d8ef8c9e37237b01a50dab0cf9c 4e69beaf1cbeba9194ab2212031872e4504910516296d7bf93e198afc164022a 937684405a2c00efc70150795f4411b784e28f2028d8725e237e1ec3400a9906 4a3304717ea641afc00dece0f3207cd203c5872d5a4660e6a1666d2fd91d09b1 880ed354e544fe9aa25d77a42dc766076f2e9a2934ef5b44e26a568539fbc10019b6b56ccc3fa6cc8f00bc9acf9621d379e0b23a4f15fa14f1f63bad6af16e0176212f67ecd36a8b80364c56d7750670e2ac30abe7bcb72ab3b49a7516749d0f490807aba992bd76e7cfdd1ad2879e7af2ca85dde6d57e21266d89509e0258078ccb6c28f2c915fa5d9c14d8aac478bee12579f83961a6eba020c8769e1ae101ad5269b994b5391511f09980bc2f9643fb49167dc43ce92059c247926e9f5b0ef22adc73134e9acf67c02d60f10c8075069e4c6200fe09b25a5bc5deebeb6f02fe3bae008b7ed59a0b4c8cf1de44a315daa133c4a50dd142057322701a8bfb02e7878319b269af3734cbc7957cac80cb596f27435e1e4bb87464518da860420b1b9f561cee6148a3935e4a8a3c881555f9c2587991c7a12f628464a1099f620b09c6019cfc58982d4fe66965d775864ac707754ea1733063a1f80ad9e769620dbd6039b14d7e0495617a14a1c030d8f36b6eeb8b9029a93d58e666c53dd040081b8a15f9f251e6256137cdddf55e005119d52b09f9858b72358fbd9ef317930da48642951957870f3212f901b6ced772bb8e48ec7423af50ad99693a43329f094277162d0dbe7e3702487d2def9f5fbae1cc4560c1f5abadd32e3efbff3c92054f6489e6d76ce313e5a002084138df47ba39bdb750276a316eb678110b2ae800fe492a8344bc666210bacc8a7df0b5359135d94c3024890086130d8bfb73e008376fb212c2d7bdfaa39a81f0b5c69a25ff909eb0acb55736f4b93d54c96fe309793650e5c614b563ee377acb63fd8d389a6f924be4944f7ccda7a9a21b21760295825cfa755583d930ff19ba7ff5a0dd5454afb2688b7cfb27c9ea8617c90a032ac600d6a323087358bca32ddc2045d13ce99f5c40f1b3fd098f0cd50d875206bdcc3d78e8fad87d7456f55de9509f181b35173423bb87bb28f9d3ad7a4e2f07278f261cf8eeffd35fa9b8cab4822987d176c08a5b88bb60464841a7b95568036104d55eac3be1b99588b29fa03375328cdced7765ab60ddda6bca2abd3ccd03396674c2f724258e9d09bd3b85b9355088c9ed555fa6ac247d75b36b2dd2b707401ac54ccaaa8e5df61cf8847f43491fc7c5fdce9e71e2ded9ce76e256ebad0bae99a5fdadb5dc665fc5dd1e66342d4d17447147382f5c32c83afa1e53f4be076d11343bfb323557828ea7684c4d25de63252c48cdf72a26aec2aa3d7626d5035bdbe8b6e5eb4d09f7be0fcfba7b73e65b581a79d80b2060d7fb32836fa3740da25c5d0b634e668a9273b6514f558594b5a89ea31b2652fa625429eaf8cbfe0547f91e442084a46af9d3b6f3e74d47825bd82e8d1cc129c7dd32ac6fc5dabf090e90128a3aefe16c6e9322338528c00ee3375f3d8a5b9d1e0f4d7ac568b0fd07004d194d8555a128345e1035f6c1c1543c472ad7c71200cd9acc375eb1c41a04c520a31494f12d8c35ccb72042309332c4ae0251fa1899f4ecb310b3598ca60dc7def2ac744a86a971ae04ae7dec4f5948cc1ac5462bc71080f889f90017ad0e369bb2487621dda7317ad2c95ae4b1ad5a4b4bd27d519c6e335cf7dd0609ee082d2cd835d189f34519e5faa1a4e418e6e6ad0f0f21274b3ebd539e5cda662805c81663631c9ff3d9923d9b90d82271e13eb7b1dacacdb9ad0c6574e0973482009f872f68478f64f8c6fcd1872011d4b8859fbf3a5dcd2933423c28a7e2ceaa00bf6e878a306e85a4c021d888ecc751d1777eb19a8d4f9bb8c4e01843b750970a405840cfce05ac85b53756bbeaf45369c2089bdad69aa00c98d749e376c1c9097ccf180f55f6fdeb08af04e87a2737c30d31b2035fb350b7e16d5f7919951409209b09350c47424181ed422dbb00cf2df3143c61978bf6cb0c04a3f1f78f340ead1639c6d99031e0f9645f699efabec3e0215ca3b0f7fb1c0015919f107c3a02afce2e9ff61e6dd47f631b58a488af61f57ef6d27db36acec7a653ffe098740fa7ed562d73b99c529d6ab1211189f68304e1547853e6e4501ab2f63ee476a60707c0205ffe2f11036595eb4161cef06cde096106fb98c57b76bcf3f46869120269401af2799979986d89918fde44caf62d3811df5e6da9435b5fc0f5b43e6d0d4b070470bad96c949122f40ad2a69a8ce6e4d12a45e354f36b77559298721c09ac1c80c4f1315cbb71c8fd858d0ff9b4bcbbc5b6ec1a2fd618a2f4249826f906509f2589de41b9d77c7d5aa7dff37aebd321f34367430d44217293ae5ea7120af4807e61aa7b3bede1770401ce5921c6348e8222c7a72b559134ba8b9965e90a5fd811713319eaaf3cefee326e7fc422f4e55292d07f6ef98bab09b1d7d4d7064ab295f143c8a48b0c7a0ed916ed4d7da149d4e23ebd1b2d974559c869ae84060382e750014d35a54209cb23f367cc1ef378ca7e7366369d5a143565a2acb30c903bbbc7e9f9bd6b3769e213e2367ae325a7a61156d57a03220df98c8df28606ddbce8508b84ee9c5c229e06785b53564d1bd94575041458b6d395e1b6ff13005a259c66300765e0d32b6c30cc67e0b431437dff48e05ccd096f1a3bd090750670b80920eb8c24e1c3a1db413a7d0bf360f20f3e689dcf7e279800a11682620daa3fde67a38dff71542baa8b675cc8dc3589a6946f33a13ea1212980667dd5074df01cfbd704beb9e8372120ccfa80492217b3a993bcb720b0c47e14b570f60b453a22b9d18325a7407679bfb88b9b1db2e0d667f1b5e240aa7a031bb30678030b2caca527df5097ee78bf1918620b1967aa22144e9e615dad2f5444420bcf059a281014c3637f5ad176cf83a221f948505c59cb95abf683d9d5c1785c9d9004ef15fa0ee06f476cd146a63eb4b824aaa38df960848296657a761bd33f155f0a5776ac056ef87e3644307c29945a9dda0b30de45e021fb4bee225b84af9d1d038314de656ba6c0ab567c707738e87ddb4ffbc772e20220e575cf3f8975a30b0ea01cd458cc62ecbab096f88747407575b9521877153d1224fa23a90bbf48e0010fe8d77b37ceb7a987fbdc01ea1514bb426efdc5145b1ea53025f6ba7b326a01eb4b1d6244892967014d1a562da5f55e40e2f131091f70621f34d80ace68370baf8b309f55b5bc889d36f3e51002547315498de60187d386d9de36b721528a0a533b2ae5525275d05889000ade5f87ecaeec514ef99e49dc7f4200b6b17d700a6d8f4dbc24811f0a2f963c1a8e0e0f68f76128f94cc98949705ccd909e5d860d7a1d65930e8e6157dd70a23de1b74052ff75a36778d40a91c684f2024e7fdc014cf6e93e3ffac151edf43e1cb40e4b5eb87362bb4e210e47533204f961116b02412ed0d246f8de9ae4aef9494fe33774cdadf764f31d3ca54e4d7afed9b1a307d3e9a6339e3de5769f2fcba59f7c2c1e29aed217be46071f9e71f4dedc62ba0da27d7ba17df96c13edf08b785ae25e404ad0306f4e721fe63803f8cad9a5a70dc9fc62b3e4a6b97c2f3f5df7d6af7aa07677b99ea8a1624839f2b0a4ddc2410b1b6cc37e06dd6718879e1225b64ba862e498167daf38f91ba2726f2079f1ad0d2d4c21429503eebc1bea945dec49d69f6f36545f730d84ea40c28505fcf85308939c36cb2eedfb4d1932541d4e820eddcb582a91f3a8967bed1d397b5d0604064b15b264b55cd3e7ef6e2a919b92f9139652bed5867461730387160cef42640e64dd499f7fd96da173e441cff7a5f76665e25d935d9ea060cd3d2328c73a1202 false +check_ring_signature c1535cb00a9d99eec9a936b04f490db19725035c2f324c1e189fc9ac8d49f101 4ca58b3f731e143b77632ea15b0249c60f828a953457679f3bface448192abf5 1 45b24c1bc38367e2db27cc13325c963cf84e5116e31e06d71bbd6b596c1517f2 ae7d3f6f4a28e99c2432c67332b4357e1fbb935a7b9a26afeded9b4aeb13a204e12c624e092bae11e743eb8c23c2b936e7111115f3158d229d3a14a3cc7ca002 false +check_ring_signature 9672489679239fa9d2a09f219bb8441b87365323a0ef23e7ff0b948cc4d5b449 db5202805ee7f805331344eabf9ae226d3fb9d3d8a81cf7e80a8b863480c63ae 2 6220989b720c79ef0355ce234fd61738444d4810b7d81b85eecc711b022a1790 f1a44b7ae657430b625a40d5b2b3e40dd564d5f73d802acaca86cbd30d04c9c1 19214f0f080c70854a84d249868744f93009f971d16d81e51ba2554a501752004643af387eff1b304a27ebcb39708e50f6d0b2e76d6c52b93c2e1223c166390768c7307555cee3f769196753147ea9c29851c62b0c3ce2f6749aa482eb0208011bc1c843b809f074c5a1c4f7c152daa423002baabc6eb31e18c067d2cfc81400 false +check_ring_signature 78e11802b78a32b26133b6726f1dcf56df2c008ffae517c2c4f7036d776ff3bc 4cb682450e6ce1ce87f61ff604e3dcff2f7172de1911a451bee04c7e71d7e3d0 25 ce44a7ca6666eaa164322709572f35154f6f7b299ffc5a3ea3ea8e1aae5b67a8 952c5322508d891d94c9557bea602ffd99567e43e636fd084ed397212face850 157dbd9ee171c7044cc01fc6189f7e13ba1ff946eb0373ed331f52f924641201 353fa1109aff2aaec1845e95f77d222eafa2e1462ca40e05d9c91686f94ac88a 56aa07b5c1828f681c2e5666058aee8096145a59402f8460758d8af2e74fea0d 1f6192401901186a4ed79cb74f7224630aa883f5e905b7f7f98fcca489e8e549 8397402fd203089ae8822b161a7c405fbb0d805526431ff5743afbbb1174899d 81051cad54ecb0928f83dd3e0daee00a22a4a163ff2cd032d0eac1ad40492abb 2fc4809c72596179caa56b1e6960e33a9b65b92196f30a325b3b557d9a420209 b131b012ab3505f44f7a31f26485ab242ec3817732841e770561982a723aec66 3f98bfd5c89017ad5470d057e5b8f2d2ac9b35b01598ea159ddde4f5fe7a1289 87328f7fff2ba0c4fffed756adcb20e8d77c6c1aacacaf68a09cfa45dcbcf739 e1320447de615138e785d098cf97917e081fc7115fa34d02ea0cbdb16ed33d9f 366462b1c212cc5ecadbf493796f97d6dacf24c7206e055b86ce61a079830f9f 26553b4ba909d22c77005bfcc22c7015d4e97d7c548834c4b4aa97d95266a7c8 8be3485005c8a5d0282c168d959bba89a3b198d9b53f53dcef0bb270c6142080 6895fa0481da2dbe17f860dabcf8680519d4b28ce4e07abb54e89657571a8b09 7c4694d89a9c630b922eaeb218152c4435da709f60bf08f256e62999fd6df929 361353b961c0b825b3ca30469fb6f3195bf01ddd4d9a096485ecf0535e0fdb38 103e5416285420bab73e55dd52149ea7446dcdd5757d67d504d7651d386fa3e5 cf2ec67174f65ef0ca03e7994b8a96bdc78af18c7cfcfed0139befdc77af7015 3e95b8ea70ff1e2e7b43faaf831d9766a912a434457fba0822d67f7e62848005 6346355d60594e2e53655fd917245be51bf5b196229dac8c59c72247ca8553e3 84a174fe7052a0f2741f19b986524b84d9de2c6b3cd3d5932ba2ccdc68e2e64a 8146a4a8128e7e260a12418db521446f257c55d853d1403ed85f34f9235c97ca 91ec7da472120e1951ab08b1089c68bf254093343b73ea6f9e50a0a6d61c660ba53bcebb18e2ecb277af5ee1f7df6d17fe5f2d9f253038215944e8118662ca05a899b08b05f5a072207f7e8fe2b360bb8b6537504d9e28c1bc4c2d4ba99b93099991b3ef6b2b35a1abd9b56bf7bc635ed63e90b91d79ef98ad7507937c3bdd0aa027eee36e89c7be74bb2ff1ec634d1776e4344475d1ef186d5bab223d630108b475237efbdefda6283c7b0dc6d02a8495623dfd8d991f09728947405086810c5c1d1d79be771375e45e5a73d11bcc6beef994565e2299385bc86d07af7ebc0c7f28003bea54c6a6f3c40c881cca935c92d4584b053ff91840d368fd09795005381a9f7c5536d0b8cd556312045a87e9cb33e78c4502ab249c667b60e0df07053d60a1b07965b5b4fb851594a7bf52b0e4a007c9aade0ff946c734c470a1e10962960146396e441a6806916bf72718837e851f533576472dac28293edb8739092beb216e782ea293f54e429e40fb9982c626bc78e3f87100b5a3382df4f148086f60be8249de547214572de337cf3bf1ef4e87c19beabb22c3e4ae233b36bf0f7ea2b52e0e1f32b2d30e5823e07f792c3866bb94f05d0368ce8689660e05c30002a5a889c958165dc326ce55c227344cb8fb9c2a5ee6c0c559d591b9782af80c197d402903504544b41ce1967ca0aee7685349c6d3854c91cd5b23c14db95000f157b16613c0d5d5dba5f10dbf57b629e34e84f181b8f14c1efe816aae5ce4012860b5da8ab5809b8c9e64c1892950d5049b7b32e7acf9aebc4036953f2f75015bb10f4158cb1d0e2633c08cf9b27d58d2317d07111e63ddb144a72f84c18d033adb5ab97011448433838f27c92826288cb4cd9777c8d79ef9ab91955d59dc0daa6a19adc51b97b386de4ae250e728b8be0e7ced267500c0023028ecfb2e0d0440404cffa28ac2e9b9891ca01d3fb2bae31002e0fc3110b741e41c9d4d41ac08d1fae2f8b6dba854967e0bc7c4c884bf949c5ee0d473eb9881ba18e0428a0f00762e421e71f886be9f8372c1f57686301945fa12515986f25b87cfe5afa6ba01e03749a072d41886c29499d8e3dd320a7f745dff5bcb462b7341ad5e40dce40ae6a49fbffb2e2a7be45ca4159957a88a45231cf53258bf02506bdff0eed0b7086cf0d1ff42f0006d4b75dd989dbda30cf58f18d386490e73a8db3de95677240ecc81269b944c7fde4007ace6426ac3ea6665a5e5ad49c1aef4dfe737483e320db1c812df1b711ae241a2a669a05c2067bc0b7d4bf6b778314910ac156dd671000d617c98fe63740043ac9e892590433c559ec51d921ad3405da50c0b844da002c91c89960e23aa1b8a3ae1a72df8a82fa5badb5850afe09a664cd511a809f004a124cccc2a3b0c98ba6b6ba5d465aac35f1a968adfae93841a85bad412191800ecd033fe22d0b6f6ac5f4e3a6392b7af5234db4172127de3dc80b912fc6e590761d2ca355f8ab0dce145d60b6cde3f4687f65b54435311f9d3585b3be2661a005be4b564a676ec36c184a3bb1d47da911e6a5023cf65a8c7c1abdc0c6d87ad06182f070106bda0fe9a866f9466cd39b1f32360debf924411993b6bd50302cb03f6ff73d36ecb236831496c8cc727a4d2752dba44a0d15f99981ef86e53a106000b5efa673821a08197f7eccc4f60b0b3887f117035bbb7ddb70138c5f54c3a0dc50a6090917c1be25776593f86b0d873acce4d7727b8b31e8007c807e4127c0e1a3254ec7b94c0dfc859dfd7f83f54417e2d0fb46689ef08ccf54951f12b3f0895fa07150acaba407d72b47efd2373b72c8cc3c951620534b15fdc176f3eab0c6ce1f1a622177ff7c95b932ae662fac6838ca73d769ad4b96eda4ba2e60ad700fbf9a60ad3e879e2cbd1d033296a9862d15d7ebab4443da9d06fdc3abd02c30e4d5865379f46c1fce749a5528f335a615ce26ec308f15da7ca296d70065d4e05c44f89d45eb6ac673616faac5eead9aafae3b1c921a965270a99772b4a15c1059ebcb23d21b10805f983755db62257b2ea921a3b333a342b5dbc606578e15307965eb42bd974efad19b766aaed519f39e0e64c22f4106cd2b8061edfa07d690aff20c2d02e80a543e67a9ecee87b3ac89baa75b6057b968744c42f66dc0f7a065b5f861329002bcc03529a7065d52259180014977764460952ce3bf48ada630a6b7366d2bce022cd6f30bc29bbc2f5608c8c728d9b6dd80a44930273e216c403 true +check_ring_signature fbadb4b7af4dcf24c43b80cab45e5ddf89d2032679fe23df1a97e52799df3601 287481920b9a2b7722bd97cdb38728f67e89e99fe3f0f2d5388b99a0b97a2760 2 1cf0620cf032d565ebaec65ac9e5fa63b2a588b8bafe91c430697c2607907d16 8f9bdd2d51ebd330dfa966192faba56d0071a3379528a308b4748af9b49c94ab 0f185928a0163e400889e4331185cd4d4176ee0fab14bc61852a417246d9fd04c6d34830d3db37ed1eae57c8760b54fb76b69460aed5674c6553677bbdaa470386f9d6465f59b7f22f2de21763816973b93d6684f3e064d63a6cf4366987890be36aca449bd7e1f119917143eec8df80c2802ee99fd965fd09979487a7b2ee06 false +check_ring_signature d67328b1b915a402e9c85d3b16db9d74c9cae43bed1fad5c4f466966a4c20f51 b64426d560c4574afd76f2cacdec5a9b8eef1537cf48b6232dff3511bf9c7143 1 28adf3ca3d336ecd24a481cef934c5dd865635c2ff367a89a16f93d9411bcb8d af6c3441cdd0ee2e38b77b30d43e1e4f7137f654202bf8f9ac79e9389df23f03806c17cba56532cbe11bd8d8519402400b3a0a28e56bdc67b376f6dc38f76c8f false +check_ring_signature df3187b155a082811cc9093814a7da820d15d8adbe9f816b77713f78fc73bcd0 c21677a3d7b8bb978a97b08dc80a2f494d6b61969c3138601c5a102de02b1e7c 2 97a3d3ec287066bcffdc760b1b8e41f159ab6297bf78fd36ccce13596bcae6c1 3d8b2a1f7fea55eab48bc843f9b8c0ce41f3eb7679ae0e50379c2998f0db018d fdd8fd07452251dab553d7db998429b0682b678bb61ce00dfad672912be15702d34e286235cba64312890756fa21cfa5500325f982ecd15bf599bcbf13594300899dce7f42bdeae38482f696a5330a6bf1cf076dd88ac361e97993f2dd76fc031efb8616c0335322bcb9c35d9d49bef53fad868eb58d4672485ad19b319cb500 false +check_ring_signature 7323a6402f1616cbedfdf802efa730919512c54fc39fa9a086affad6a7374e04 c572d415fac3de363a081417311594c094e491571697e0106b967e20101b99d0 155 7ad111b611cc1d8c907a6800c1e8ea0b61aadb346ccf398ae1eb4592bf61d494 84b778d6a2482e3584d2985a00604e4eb43f1f46a49dbb175f42a9035a412bb9 fcf44a8d88e6a1f8b830ad175d6e9e81eff579c6cbac8fef0d44bd2935345400 a29309c3f1e6d2c4ff1a53a31ad4f08342d52ddf918c7668cb352db89c1a8c9c dfaabb26321aee2a888bfaa077c06371dd40badff85f5286a054e85739502645 8b8bd4711b2e5cc3d74254795a506a526443e020a0dde243d3d9ea18360a7fb8 f5b5a9b7b25ebe2e951ee2ca11a6751ac2ad1cdbe026d5211a64d7d4b7761b6a 6afb922b0640b1438ebf0be841b530c0b0093baa1d52e1838208d377b6d67c2c bd3f291ca041c4cd8d2aab3c850017542c3f8db0d9b5d5257c8c46459fc122d7 a7713d4b7356fad68040ae832a7a27e3f37f129722d5370957d6d865314bd42f 8646ab42557bbd4329a951902642933027b83eb6cce30f2c046efb5a75865505 0a1ff1347758ca9307782f55ec60cb0bf97988e7080d32ff6f47388d410c1c8e 04fcf4bcfe592e2224acda1a559a88c0598cdb5118990025ae0ea920dc7dd493 4ca6c940a535e8737d443c677938db674f09a9e9f203f41b9cdc8115a0ad3ac1 fd11a1fa0525d05ec5ead577a9e913492940f2767cb6d2e077618993d30975f5 0461efdd2e2f62cc74e5e8017caa57d82effbabfd8506dcd5ffbbfa205ba4f73 5b38d3cc64f762b9c395ea57b68983c4748000d750843f43000d06778ed082f8 a136447d501f2c73638c3f75dd7909fb60d07af553f4f0619847931c41937c2d d0d434105b6cb09fd94c22ae84cc7aa8945cbd3c3cc6e3af8edbd2a328582ac3 833b0b0a3326f877652c907b69baa773bda4c07683794e5b3634a1c1101a2765 2c7268c84cffc0fa4eb17794429868ea27fdf8afdd93305693806d62c0ca8723 edea16d8eb2f1626c1a4d55c2284711b826db5a5a9406f451e007b19af1423ad a441040e8cffa56e2d28dc304f0fe17f04bc39315a3afb8b922d7c89f1de48eb cdd3d3ba79a516585960bd744cab7f5d00e18239c8096d27bd0fb4033624aa9e ce98c7387235bdc49102d77898a81e37cc322b3d1cb32677f026fb6abc6ebe5e e2260c5624b42c91042562670bcff28dcc40232be5a5d2486a36a6c49362f4c2 82c006cfa94f433ed596022c572ae541ed951a03dbfaa40fbd6ecd91270a9d7a 1685014b2a469df1c91bdd176fc19a13d636a4a41d8943ff7c5a17de666f1b0e 1f373653c2ab10284b4cb9a07249e90984e6438e19d5ac5661b07f523939fecd 6abfd4e5d24df9f0c3f6566ad1f322ce5fad44e56b963cdb2cc7c5c30dca5da3 46b5354ca2cbc9b859ab8fc49673e3acb12cea85675432a68a934fc73d43cd07 5521e76b4d0f618906ada48d82b4fa33cb6a884ec6f9dd8840997ccd02c9a33e 622edab34de85e7d81691a23e3d2643f8c41932364affa31ed0a5cc594f66f7f 02f7e4f05505d0c89b282511bdb20bba5015d1521098139bf9d8da54b32eba6e b713598702ac2c2b94cf815b98e6bee109572dd721c763f8ac5c3fa9d15df6d1 302b880e6161469b8a9081c38bb8882211d68142262267abf0c14a02e9dffb17 9b9ccb5870f645ca2e0dac1c1b806d80908046845bb5c107528d8c27b6a3b716 85bbc6bb45127a4c8d171c1e010a7ae4c8ceccc929ea9a01300c7d48b507d697 8c118f002752be5b0e8479214b892417aeb44bcf4751b917e16969e25fcfa17e 170c13b0389c5b8916875b1084e095c9c9ee299b04c07efe2aab6b6f76753d99 1baccfaec91278fec7173469d70c60b85c1f4955a249a2ffdf200324970a4066 818dea077c0b8e91599ce4375ffac8fb7902c68c71397dfb197ea8642a0e3467 a3320f79890316e76035dc1b5e951602593c4b726151744ebda62acfa45fca00 8cdb1e27ea808e9fe760d97f5d96f75de175b722e233a6c6c53992e1053660dd 0744202ef03f22b486185d6e6d51a6c96edca5fb47d8e06f35643a9f05370b36 4a5dde123b8b26f8345725f638ec9d4f8f42f1df72eec4cda59da046e1beacff bc2927bf482c0d9544a41d608834eda273e3e2fa724da4d674198462f165fbe8 2d9d82dbdd38c201dc45a0c7ed9f8dcdcb2e93325d28e0321ff4a9223e89cf71 5fdb883aa2801415452b73ddc8f32a1436f350badc04913bba59d7c925b20930 ee4bc144e57e5b41a324e1ce9322a0d59e8aa1acdb746762f9e88a457c31ecf1 7ac9b2c7ab005918e9a76e3bdfcecaad58c3f18f3cf83cd220d7d1088ee19a8b cdc395b0b1910cdfb841bec0e53a66a4b456162976b120af925041cf3e5f0fc8 82746c87bd17266a37f94d64762a0290f4c22c7e4fff64826cbda8fd5d2431c6 e048c2f4727f20fa1e3f2f671ff0b82b45e4de78558d78d02104b73bccb4a700 7808d03baf0c38ba374c15ecd63d9f9eaf5f3d4cf2ce7b7f0b5f858be7756588 013b3ac4627a042a4caed5017b1f617e58832eca827806b6dda344f0651ee07c 15d2b21f26064e46495acae8b2dcadf1007aecd8151f0810e7e3262aa6beea25 3611878e557c66aff2cc8ba0fcc62c163223b0aded11cdec5e77a11779141496 f06830063a084291bcf8ac55e50228fdd45c0e80c70a87b9a8a8db809290becf 2a39c3747de2279001a6d85e5d81530103d7c6bbfa133ce7d3486bd3c427eb1b 9fefbb9f06b5d1ea270ccadb6a0cbf839203ba209a85c4238249a510bb34d818 9f7ab52d812604cf6f516bc7cc6b0e8de9ae15d48221c6e861a236786dfaa620 c399a3d1b8d0f443010b7fbc728c2969870e65af657c15bd1f5a4eda244c8d3a aabcdf597fdcd4de2295c71004c100e4278e62a952fde93307a466dbca8e20f4 8133b6dd6c473c0209ad0d1230556352baadb9343a108d028733c9470e7f1bbb cd66e7aff3812a34adec75a4a0272c911df49f5f83afb2b1ab6b9e03df7ad8d2 552947e863be7c8cea73e7e6575e01710b7787367b29c079a6e0a9b68433a692 8f2ed7caeac0d863eced5aac15b0ba2b66dbbf894dd12afb77ab9d43249e7a89 29a90feee48eb82b3cb2b9ae881ffb61fb2bfba79b9a52077ec25502831261be 09a9de86c4d298f5130ee0dfc3ffdb507b152760d4e72a904679b1f9a28610c1 d80531f0de9d455b52e7b9f18d1a5a423d009d8b96a9c61e2e4a50d945e31635 3030749084a0de24c9cee194fd70943c9a976ba9636def9ed14bdcad80119a6c 9df9e32c57e5602da6b09d23f07de2995f3b0e9fe4a08c37a97f8fee8d4b7334 c3525b273776dd2a1227a3f323ed9ba1ba18fe3c7772c13069bb1aa05a5beaad e62114062ff7572ffcc4dde4a7165e8a75984c6570646421691c602b17169fd3 5c07a45a6c6aed0dca335d264538af91cdc84cfb1f7eea87bf7f7362f0e98ced d5bc22ec3d99a2e62a737b925ae7fd5ba7ff65d4a8f2ced5ab69eecbea790f4e 770cd037868583c2532a8f9284aafaa9ab8ce8af0ada93e8c1f22046c2fe93ea 1918efca7b74293302dfb6c4cc222c4fa5c649fe5e282f01fb38d6b710a4c120 f1f47c3db7f65f7ad52ae8ace75d27b908532494e5c83d1284ed633d99b6123b ddad32ae999d1ef4557a205274d0b96874df05bcc44599c5183f14b54f5f9f94 be629452726a36314c0f29856b4b29df75449d23776a1678edf0fd1e661878df 02ba0122b115d8596e1cc7250ba9545c9b5d30dc4c534b3bee1cdbf2cd0a9fc5 00038b34af7bc1824a9c298f134eeef5a61c8268baf23ccd05277d3d6ff02069 65eeb75b4bf09127dd06e19b2294bb2209d726168d3af3ffcc0e266c33f6e811 76e123948dfb53ac17ffa28e9eebfd752ab1a778e3704744d0e8ab445d397bc9 103b46b301995a6edadd7d484f4f222f75ace75222bb7bf3fe640e01860ce2f6 b3e27a7b6e888a72ed8c1fab922b61941d9464b225479c73a41ae3fc72a98449 55d8bcdacc4fd386bf301e50dfca162d9234bb8e17c709b6782396d08987cae8 fe7db6bf85df53b0e6a09dfaa548685d38ab8330ed3bd814c840de2c68ad6874 d610a839400ac893ce7326a287224379d06f5ee6528b95aa5965c49b999dbbbf 1e0c43ec2abe90214d39b683b9ae42643023ec656eccfb42acb1fa23bfa16f5d 15571e1a59e6567275997d0e31e335da8e3da5a667e9b3a7f0067e6a3411bab7 687ae2e55737ec19ec51b44a479792d8eec92ec7bd27ffca5cf6a533cc51f8f8 eb25c9ccd95de4484664a645bf67b7a3942866f2d0efd70391c9528e3374c94f f46c83b91d572a32f4c8a92945eb2d3035b17c56a6b9616b6fb58f3b0a30eab3 653966e51932b1cde41e2681b30fb539f7368a0a3ecf7e9dd8bf9eab9e84081c 0f902cbc287bd64b74c753054f943893a5fa5bdeb5b859b955b4e08b4a41a4f5 14eba0e2fbe629eeeed3062a7e5c1bde7073a9e608d4f0c740280f57c8498166 4b6090d539cf01f6782caf0a0b7103c3773434dfa97b60c00dbcb1f317776945 704ea5bde41b8d8126d470ea7b7cf6e9a373efee0ff37e03d89cf8c250c746fb d1399829e35ec9aeb801879d8c721b49eca9531673b208ce1479861a2d809bf6 b225004ece4805dda26fd7edafc1cd92580f9cae19c250287bf363e214454530 c6129fa0d902e3b208e76377912cdfd3f2a44d7075c7e183f46b73809dee4d97 5de991f9ba68cec63f21ede00babfabd746454c01784c7276506323187ce4766 94dd29481b80ba3aba523844a20baac70679cf27205aa9d13cf461a7db2a213e 3b166c3f9283fa7fc17d70c6badbfe425ff73e1d80ffdd07c3fefdff5ac68838 77834c1b9b7c5df56bed0c2afd0eef27f26c065df62900df9389d261925450a0 954b58974c92ae24878660e16b9e1470c1e9abfd728c6ae9813cf8875630ccda e26a2ee74526e696dcd0513ce7ed492f414b4cb10e112d2637a6cfec33e14c57 fb5cd38ac1308f489841ddb02a73b0a769d6afcf9caf4e5ffae7db06da62d999 9de2ed0b0b1a0f8fd875455ab076c1ca2506954fcc1f87f196311874e33ad02c d6da4ae67b79c28d78040f4df714630df9ec45818e3489a5b258dfc6113bf333 5ee0a9b319134168dfd3e6d09fabfcdab0a43031e96ebf40bc0a4b41845d1dc6 dad94af3b4aa20a47f2c7a52c1e364153f3a667e4c123445759984ee32c97062 726b0ae3f9d0753b9d67d223fe62144d0593cebf1491cfd0ea38bd5c93fc5b50 37c63f2902cc013e8f4994475345dfba371094edb41eb4d79e90a472db44f2b6 48ed2c59ebc4c11318c7bc8f28edb57df9a4c8470356378029396be904e01b00 91f03d0e9c5a087429ab051517b3a5baf4012f08cbd8c5a927cfb948b3b26f15 7a1dd92a088befbee23974254bb5532e028a74f192bd5f385e58ccdc4fe2984c 17fba0eb0946ed984da6cfc094b1ad9afbba3d35706c7b2f00fbdcf839172a10 bb9b4ace702dcd61312639c549dfd30b7a5dc13f2e04aa59a52e3c3e1eeecc2f a14420d072b349eaef4208b02ba689fb8ef98abce6743120a28c30d72ca65699 bbc07d00e23b9a81cf294d304676b15ba0f33ce28547b7b9f2812300a31a46d8 3030d166ecabec9b66274e2d7b2be0b65a742e835be5b803695ade9b2255f834 379c5f3c358cbeda37023753b7cdb1195454688dad8e743fcaf4f9b038a9abe8 5742ce08f03c77bd3a9dccaa9d1ad34bff51ccd4c76dd083f96c129a14af6667 62e0ae738440da626e2f033184b25c255231ebe1065020c8392052944d5308cb ab34d2521048351b3cc863ecc6c94081b6595874cd1e12de6cf8a32248fb07fd cb113da6e91bacf8c6ff007ba3c6a8788c291a239f1393b431dd122452ae7db8 9465bfc86bc7ed4b63f94e5959ba42862a502bce78540040494c031db416055e 0927738c044f82f9ed29a12a62ae2600477f55d688fd84ab256a2143b3156bd6 c8dd091f8d355cfbb30ff5e338671a5012e97dc6f5ce494352182718e6b55707 fdc24cd45e9217d78a4c8cf49fdbee28965e177fed132a3e4601cc3d00663186 14ab24eee8c3102900d4ed42fcfb4c5243aef7719253705d09c7c508e5ef576b b30f0665698599b3b5ebd896bcd908fbdbb691e20cfdd4c8f6856bc6d4a99028 eff366e7e323044f3588a58964dd1b8e308c0b007159bd2d518450ce28fae168 8c868c4fffe11a1ce47a075412cbc30bef78545a1341b2edb87cb75788810359 4e3bc087e71dc3be7872481bcadab0ce9ae76c34c3968596fc015cdcc46fa0b3 a6a453dbfcf51ef5d036faeb4106a2b30faff6d06285d629a89156c3b4a4c479 abec3f7a1973c684b07d00203343d5619e9d14761c9a937cad80d42818c28738 5e45aa2bfd4109974f5446fb053fbce6695dde062e7202d474db4c8173115d5a f7a015ab096fdfb5229b779a67f18eb4f8509d329d2570699d6fb8161f998994 7f7d7efa228e9ae24b5a70bafadced72214afffa226214ca3ec665bdd605f59c cf4e260c5c5912bee9073da829fc507dd44b2a147d77657922105ef032d3ae64 a9e571b76d328de7aa702d86e165a8fa8912c5859afd04e294ede3573994a9a4 dbdef748526d61f4bc35534f0aa5051534a9b76ed2cd1ad6ba5de715adabbade 8de189ade02d3d35a45783c99024c0646c645d89dbe704433ba74a7198b2c338 b00fca599445e024933e16131d1f3e310e46ca91e9343c8d52fd57fa9d720be2 d07855dcffc400f717d6588cad13e35ab3027cacd0f10a14c2a2ee16ba39b536 3f855263604570b3e506aaf6d55eaa88dc3f84a7252b11d2077e6d105692211c 90a3380e530819bb4c8343e45821d9f27fae471e0b0bae94b93c998579d405f9 76090ef6853ed940332746a171cb201c0685b492a99f081514f3919bd1cf722b 093fd23ce58a4f026bda757c154a8474e61220250fed19ae52e633fca5e5e55b 7018f317198581c026ddf7877ab083804744c6770e41f503a2c4b2e295705d1c 8f794660e035ec198a2d6e016d0ed894c9ba720ffd58540aeb889c701573c104563d1bc485dc2efada39c836ec60de892dc34e0bf3bd30cf0f4916360932b609ad3a22b87cd4caa36d6fbbe0c35ffa5c93bddba2d67de436b907547a876880001fdb2a1b6df110dbe4334d5d012e6e08b7768b165163533a1b373e3347c55c00f9f4f12c9f4e86529f2282e14493934af61d699d579d4f724fe1e615f464d80afd977cbb476c6dad8367d8067c63168dc82c4155c9195755e506409f0e6cc802170eb910d76ef883c773ec44101e7702815fa30941932aa26d285c1d25396b020fcefd29bc1ad37e8d862fa01d23fb27a1e3c169c5f28fe7e38d2fe858183d00dead40fadcaa259487860933a9eeeb8950997e6847ac13fa522d408c9254e60fe94c0f72888f77a5618ecf2f0936a5e9a04259b0fbdc2beee86d296099312409d684bdef69bf2a3c246c95b8c6575ff01c814340b33996c0d2957320004ad500dcc4adf83dde7734a072ddcaf8209dde53e08b350084cd7a989f9fd32c865a06fe328f37362ed1f15eaa55ed47810ac0371e96c9afb90cd1b008fe75b9dbe20c1dec6ec1ad374583209e52962b73b1f3d5d43f8966a49f46e75bf464a08cf10c7c6d6f6f8761acb5c2b7d06d50c84289e0436425c8e2908aac869ec90035c000001a333fce5c81c1552225431a27b09e508c978770a6abd09e2da422d34d3c0d36dcd07a568e05252a8965e65e0c3829dbce8c1761539d2a0d26262c4b01500fd6205b0e7849741bf319a4dbd3aa8575ecbb0050bc5540c83f38472f7caae1074ae1f49d0d7161a8cf3c20bcaa0e62186af3c1dc5d76a969efa0d3ef91f57a03a38f3aa86970f7c85438d62e5c44c41f919664992991fc07f6f96365aab72001a2fa9369545cda4a812d0fe9e8cd8a7ae601beeb1c713317c11c5c2758d9e706eaacce9e8d02e8d62aa1302fb7c995ad2ce001a503366b11cfb4764fcaa1e3082fd6963b2148fff71a830ea113ec6feef3959830cb80b53618723e149ba380060cb49aa5e2124e5df8f7cffdb030fbd9d396136f4c42a4273bd04c27138a580027fb75166384fc84b0a0d50cde4def749a78359d8aca865fef794699b471ea03f218a57baac7592dc2216a09348c3aedde3ea24002af33a17bc123dfb522d90373bf5d895b71aa359b7d4658bb6a387fe7259061e439b52e781059a5c5e4840f7907dbd363ea0d8fa5cecef895d7c72a465169e6926e90fcb914c86d902a23026b5359de9bfc6d46886a41735d7cb38b316666a5ccc58ff34c93a69b2579d608ff68213ef4c62d201d6617de3145b60b1bdfa68da473d5eca2a4daed2ea0af05b59349c5cf7bab5967a8113d5e71ea6f45a7dadad24e577508e03f608eadd50a3bf6125ad7c14ab1d9037e5c5c53824ff7f8777628bd8d77459d871f9fe6b2030a3511592accb47441ae168ff69af610c076a9d8b7e339e107493996f99f5100e27747ceecf294f9f4bfb885305035e67558d1058d885c85e5c8454ec3dd800d8173aad42ba8323a5c7364fa0bddb5b6372e81800e606bcccee72fb316ff930475671c53f5991a96c10d66e57871abcee6a0fb8040d8a881f3fc3bfa26c5cc0219c484d1bd922a13749ea3b3935b4a5c35da0e861d75f0f3e3cddfaf642c7e05173ee80a78ce14bfcdcbd0a5d623c185fa7c32d7c1ca8c29b2287b7ec9835a0046afbd36718a0616c89b9f914ff94e67a38f1be57ea3fe2cb28859afb4b6b302322461ceaaadcb09ec8c6f8538b31ba16812164a51f445c137edcf73205b9f0ec2b075f335ebc02b6fd9b63ef604529fcabc6805b66f1d1fa24f34e50e2a1706449aa786a1adaba833405c50d463a3af0c7e55a7513898f3ccffb920638ec40267fe716de0cd83817872e3758a13b58ea0f440c7411e3d8078137cfd2674f605d2323b751aaa8a997cf652fe99c3439199aab829624cef3d16fe285c016eec0b1ea5cc150c9947292404425ad4e296748aeb820cf0af737caf64b1b54ec0470650bad085a8eae1bf63fabe421fb0076b9ff15c24c7295cf1f7d0317be9456e04d0f290fb51143366c2e007382e55622758d0530e2bd21a0cd1f81640c6914d06516c0237b1cda19ad031b5b5ca44420ccb48561c7d14eebef888efc413e89c0f0ecad9b4435ac762c03746aa8e3040e2c382d7397f826d1b539ef528975f870eec935fa79bffb9e784ef1a8038076336eb1ddeec22e308273a424b925d30800f4788c0b8e12c24279366ee48181230f1328d3ca7043836aa077dc9873f2a530437bdb7c25bf9f0dfe533c2e4ee5821c4b3a8977c86b6def9ce8b6a3cd4ffcd0f862775a79c657ed2a2c1e4d0b0503bd6f7969cb8717192352638506f2b26600123e2be5cf7c38a0f07d703fc27e723a1dd126f6a617f5d0e48a8c236be2e8a0d10e575da3a98ac55cc948739fc859a8451286c7c5de55b9b684821f9d4d24802f229625fb7b770b3ceff427ea40537a336da26a50301173bf219d601f555ff0175431c0f5d5194b80ba847c7fdbde2d6a97d2c88775ec106f24e18fb2099ab092c437651ffa65315dabe0a9d31a91e41f7bb14f705afdc2577b3f37bc1b41b011459d375e952c11d9959abe6af3a50146e6bce38d5119945fcd2be23c5b8ca0ffe396cfc1165c082c7f2cd0ab18e50bd4e378e41e2924eceaeb551f5eec57a095daabe07888e26b2222916cef251953b0a120defb9048ac43ebe7394b23574028b416e397dac672e9683291faf20cdc032dac7ac84a205ec81f1072c2635f80acbcd09c379562e05340977b081346ab9a514bcee45af556f9edc6b922043260666de1abd81133fbfe3f7ad455d64653e9198e2a672d23a3c84bfec928684f502b5c38622e5cdfd4813bd70b5e4ec4c86fd042cd853512763879dc9b415f81800eb60c5b722bbb4a094b468d5b6b0b4f408d361013b3b1a06bffe0e0c8326650853b22f6e2230de368d15938f8a697c4c2381b783d14d0578f779df41173ed7047499caeb8964dc763d85f1fdd0672fa8492be52c9bfbe4913ef2a131e807b209e7de0a9974d6a8490c7db1ab995bcf2b4142f74b3e6b5c0847c970d4f94bfd07fc3f5fdcb87db587c3bda760d158974c50aa1bb5994f829cb205d57b9a8a540cff3cbc8aa8e18e150f1e05c870f9a264a43e1d0a690229d68be152c61b3e5e0790af36711e5afeb6d78dc9cd10c85444301173596c70f97e4b43538e29ffc90487fe970b21d4668de252d04e3d772c1d22690f527ff47dba284c5864f2420c0d9078862b048e611aeff3f0ef831a391500a0d24d593cc1c1abede567ae1c3200ef9cad711370425ceb532dd4004070e89f9b5bc665b1933efae7eabdc17f7d05995c65de02b1cde456875074d978888c23cb8e697f3063a9f43c1bccdb409004bb597bb40e0cbbe54a92d63d87185ebd6b5d89e650ff6401ac645714b2e76d0f5ac40e6e047617a72b717169f9e01878a74a32ea5601d2473cbb7ccfe964ce035617a7a7e10ff75e52cacdb4589df597aa72d2700dc8836c11b24f38828aae0d9c97ca76b9946b8180cfb57157c229128f3fff7db8d029fdf099cdbd38d06907591bee41d81aae157efbb328a4179f1e19d48c919510a0ba225a36880bb6930b48e8b64116737a170291ebad865ffb71ba6df094874ab19ada17da222c6cfc0aa895ba2775597037ef7a96772005280fd98509f4106a8a9b9d05cde44b0fbf0f18eadbe3643146edcac139aa5a2154582eace4b7d8413b9bb0936ff0e15fe4041f02f0a8511cc68983b66dfbcf071d5c5fddbafce1f48b6ad9bd10cd65e6920d1d393a19caad8158f7416f2da530c3f0b2ba23f96f6148a7bd98e663d1bf2f01ae555308e99b165ea75a4c13cf14b27623218a4f5cff6af7681fece2ebf70108d4a0f6e63361c93741858aeb1c2fc2b0394d24279a73eaa5fbc7a6663b408f0d665a9263b058525e152e7f71728d45dd5467023b42d22e55512de234cf6b6a09dc9616cbaae5715d3d3eeec22c0dd88c372ed98b3a5f128dcecd125416ca2c0862f796bf8ef7bdc066d956ed06a697e95513a9191480b0440c97ffcde8ca8b0d63651babd9856f719e9c366e8a60924ce417ee742bdd737b862f241910fe97050a57ee2efbf7ded6c56844bf96eedd6b35537625efe14588bf4694ea67ebb108dcbd505d06fc71ba57af5e4e17871f94bc2892f68f69031867b4884c2d6b2305602e82282996c0ad59a90e48167150153fffbc0c2d99ccd5ca176771ecdcf40cea542bb404ebd4de0080f511bf1d1bd5145df9af8205037ee6998b98688f6909d8d9986b95246eab57da357b6daa067cf5633aba3c6d3e11228c3e04a628e00a04c8e6bb54f0075bf9ed6abfb83e0f40ecb89d66f7a87a1b0deafb0bc74ca90f3159d34b95b02cf8b3e5f3087648a7aff581263958aef47cbef002f9172fb10260ca275318b30ebc75918ce605beeb8d89f11aeb857de4d03612ca7f2177aa0ca39001c25b0527ce31ecef526ac8c934be71ead5564373a05c8aa47018746e084e3d8ce4540774d5cc51e66f72b0aad7e0e47158c365e7bac0579193bdb70e0a58fd6661f5cc1bab019160d9f1baf9ade744141ddbb98c058f9d912362fba80a1bb530f0cc75201f57f97049b210d2db904c7cd7be82d1eeda9f397de290620d1156a472f28dc18429c1a858b621fe2bcaa5ac5d6e72b440f2e5916f89389b0fb7ff6fb5c11f6fc63fd851c4bf730369c4112dc2c5d188164d834870adf3a7060765cf045c0291a8706e3f64b9ee62202b987e728fffe6c009737356dbd834090490ac109e64081558158b50a2312042635cd4ff77f314c8c292ed1bd5d756096c2cc5bdcb919a8bcf767a9d4b18d5c83d95911731a2193b3acfa35cc84f410539439d383cba1ebabacf5a7e8a642af6b80c52164fc85d1e5d9bb7fc55cab007e4de06c771ad91d9273465633020a823310498a12e42cc5196a095b1f0e3c50838c6525c9190785897aed6819d98da76213d0ed785902fc468b87a3b9b5edd02527fd108df7f69c11c9008fc1ff32b55feb8ac7ac7fe1dc6fa1330830d31650288bab49b765a0f4df25027c55b05efeee0f4dc2ede5dc995f01eafd06669280e3d49efbb29b40911ea8ee2979790d2994e88c77c37cf435715657bf2b1dede0a747c02bf05d4cf8a33f394d3573b5aacffbac0ad1b25bf0e48ed181febd89f0b49aae1aeb1ceba6b9040426473d7d49e14e9f311bc5c6efc1b898c55fbb86c0808f2afd4c84635cb28a3b910d9d12c0674f562d0748dbc241276f99fa5258f03a66b8cbef2f8c25e426c87bafe3439092d227ba626841643c77f1e156525770e00b537821f7ae74c999a05f697e26461e44591b4764eb17f907a2558ab8b6108e7c44bb453fd8f56d6de77c07a322f78b1c3e31711dcba93a37899f6444a0c0d3d46feb2cc6fadff2285b8af85c054809d943f2e3e4c35f81c980590b579c802855f50696c03b48da1464304ccac9f3278fe1fb4ed6102a604dc6def155b0506c0e869c4967673b4c50d7edc3efa024237e5f75795c5aa4e006f7d73cfc6c402403c702fbd9afa3f237802640776f623b18fd9b3a2ef5d6090018af2a9232607aabc703aab2bd3a6974b4c12620bf9130984a92ef4ed16478b8d2b0e8724230cf6ab3abc151799de9ee91191ba6faf50dabd2e9f537cf21a70458f846a643c0664edf0a09eee088e2a1e188f39c1d4fdfdab301c336e51d98b899347bed3ed0dfb0ea72997af787b85f3e6fd72668adfa5004a911f17f1cf9f20fbdb499c32092aba857a9628e642dc5287f5d37bf94d2a14dd800f3277ed07b16e6c454c8b03a7875d81118a9ac0251042941caa8da7574cbfce903cca594f2af32df280a909f8966f30536543b8efdc2f687e58a1f8debf05548986349508b5cd08bd8cf001bd01de20255097ffdea879d9d9b328ea6c58a96984a84a900012ecbcc634e80cfd100a6c7930fdbb08dc61aa999a9836371fb9c05765fdd88fa9935777d0f305926c50045945600fdcabd841c19e68dfc2c19ab73867c8d526940ee6b52834015cda865373d68e8380502ee66ba9555da17a85cb8a583bc84a3122742a945e0ff22767516e86ecfcf6a17cdf60e0eb514f0714da45cc42085ca0b24aefacde0c9bfbcc5af865e429220712e535d23b5a3120f9632d81d3d723bb896778a6410732015a9f91675d2914040d9b828679a551445dac1dad479d39815dc566f009093cf3df7efbaadc829a337c162679c817ad051d0a584517d264b8a8fe72edc505577fe0c0c1022efdcd7627547e0e482c3a41d96791efa54ea6859761b5e5690f51661ae0dad9d181fca6dfc618f64b46976a85118ca11ead171fa187bc80920540b2c2e88caf36d1891e6353c889894c90966ac9f5d1427dfa7ffa99a448240fe39fe8d3e4fa376dcf632d57052db63b2730f9a8bd3cd9cc9923e8644e65d80066aed00793003f3186ea61c913f4b3a2a3b94f669293b8e4d6c28a203c9f8e0d92370006dfd3139fa907595fb8130944f5ffcd56ac13eb9b462dea48c590270d95a18cc707e260845a4408d37b36ebab899aefb63a428f300072e596b2092d0be015056f01549fe67672818cb755e61e273b9b16f3fdac06b81f65b8c643650ba41715d96abe40cc8a2db3c4f4ca0cc3eb876acb4634089618666dd38c7aed0484963c1e3869d768b4d5df55212dc64296186c33b31366497fada9966d64f20b72dcc0771a6c3e6326e204fdca98b29549f5ceb0a848d7d29700e08fef20a30fcdf0717b868ee30c331eb659de77891cddd72859e0f737b0356008c93815dd0e35791aea6f60e430a413e7b1170c7123649c3fcb1e51c6191ec71bff5553f10209321360b84305c4e0e06ab152475d8e8fd06dd7ae11939cbf9e5ef26bf554058d4a22dccdd9b9811bc05289cfabfd6168787a9e5c90d503cbd2290888a2c80e77f8939e9e93e4a9960a87ee62acebcde785b7bfda71a372e2b6d750d47c4305938b979e016739538768119b639f60c8e55d16d0694fb7a81ea5f064dfceb509e18c188cb6c7cef6e452d3d8c3dde7972203f83ecdf047519e2baa98889bcf064e9df31ccffa03a2ff91189164c4ad19fd73aafdd19198a4cd498186dce64701bd60c8c8059d2ff1685e5c79b6e4eb22d0b9f36650696555b26ffc256725b40b1458edba728da4308dac0aca33123651833d286747219125a3e2aced4b2a060892f0f22c3ef8bc15baecaebb6424a10c543d5333a31276416239ed0f7227ff081adac2b05744158a3d22541eea845371272eca91c3d86dfdd29f488f5b311407b7ca035066d44ff169d34a37336e2fe6a7f9f4f6a6bd4709f156c37c43c48003ab017c25b3d1e56b630cec191a3a45183c106b609e3ba20e446bb022deb9dd01111c4320d0c603a02cc174040bee422ee1c8e2a9c60d398c88a5aa5dcf4fde019752097e4c61b2310e9c1fd80d04d1df5735c896452e0efe3790c268d20fc80ba2513a5e254309226b8f9952a7281f2179fdff196bd4c469f9f86bc4da3b090cae923a9368e177dc7fda948ee2340d4f982b9211298212b74a8c1c2e06b5ef000dd4054f48f780f9aefe6144fd8eefadb674f6da0d4e85a8cb7dfe84bcca9c06ab336a514ad8f97e10c54b5d5d138f49c3a54661c2b2cb06adc4b1bf7c736d0fabf82e9eb3b0e7beab638c6badd060de8c3502511e9656c35259b5cf1e2d6b0bd55ca496dd243fbb8c05f34353d5511233f91c5224090b65b6c5e0f522309608b45bebe58fdff8ea3af77427e7566df316cef9d8c0c630bd9f679ad9a96b920f29f9d273827d43f8f51541fb61ea6eba9bfb9631769956115ec535039b4dd00d8f48be538390cdaea95a40ad4d646099ced25db2afaa8160cbcaec68317379037d09e70a1d629163d43c7b788ae8f5b410f6e2c03653bd71e1837bf4d591ef046623795c31b98abc64e57b02dbb938d09ac1ba03300fe291bb8d91e73b0c060314699c79e1a76247cc3a2523d505f04239d96d472f2cac6d219d9b54f4cd310ecaf24afaaeb7723bdcccb116a95e272b876abb4fc5b7e220ab9c70440c1f830ab333282c29c6042c6a4ca9f046d8dd11b180b7c1c82668a5a44baf106836b60a5e0c32876dbc200ba2f670017540bcdc6fc139f5f4e7134ce04e1ace7200560454faa94b12492bc2bb3e251bbbd25872e8de111f31077b934257d4a168caa80c3ad90a04d923e97a2921489c238e58cf3f79a94ea2bac2638426d7753986f60b7f7d1e5899e4abbd3d5e49b6f6d7ae92f74de1d536203ae76cf90826af771e08ba4abf499924d9c13311b211084ef341852e8d03f9f1fe21bb71a2c5e19a930e5ef9acc27bf36a964d7d99b9b9e8f5a7774a36ba10e9e666f03c7a6b5fc89a0ab4e3bf8c65f7ec68cd871a4b77c63b35de5e8a94a66436a598296f303d0702038b789b8fd859c85e106d17c301b58348dbd34433cda3b454c8652a685ff7820e7c88b380f07e594ce071c691f42e65f9281c53f920cd97a3e0b163d97c6afc0cad5cdb1023f23d2a5048e4d089fbb1f3d593fff38a47747ed02cd2b88e58e60b646487a880e6e22cb5fc9daadd631972f0b2bfdb59861650a7d4a9eff48c870637b6357f89ec7661f4f82a0602956203a87d7070768532abf53a29f65e7a31002f2083f923e7e39d6825aa1cb9f3de667a538aacbb6ecff76a4af263c4ba5c0db77c02a8e8c1034382ae6bc01d2c4e2d655f61163695cec5c6e8f38aa8d1770ba80abb1f25fd93eb9d02d28421ee39c13c3c4e6fddcfc9fa2c886a5d08f15e01ed6183c1e3cfe6bf810fda08d71b332a876b029dfc9c7ddc0fa3f15a2f94b100470707ef57b5d15ab402d285c719bca74f534d8d35604d8b0436bfe5bd3f400170901eb7186b95c1ad38cef473dfdd1692ea870a176f148bb9e9466fe591330c49a81599ceaf92c06559d0c1628721d855afdd7cb8911fb6d9ddfa22c9ebb90b870991bb89fead73855774c5a40184d55a3c5f0d8509281739fd07ba53f80403d3db1199ac9839caf6faad87b68be87af720f0c0e2183f0835edb33d3d4efd0d44787345402125327387b6db64195eb1de0aa9c85455c0e4fa1476924fd27305473f1056d80a24dcb64d06ec7426f970bdda5d3598e24d79ab9aa14d20a52907043598f38b48aa804fe979c0456505ea31149f4d5145c75a37dd877fbc5d8c08ef8c118cd312e9f0af07ea86e843d492c0b8ec1fdfbfc0ba5e28996c72f404050dabbfe3caf0f50d033f569efd07f10c1a6f950465ae1163390529bf1a860d00a4bf5a2c636353eb6e1cb39d7f411c62cd3720e208297cfa21d44e087f8a07018622be725cc614f8400d628001454c21eb695ffd7044dea818d4f60819ba600e1de08af332d061ab16a28d8ac26364b4a9f67763b4c12cb1ab949a68a3a3b00ae346d4779510626c42ccb99f2202c76200c659baccde3f44a1fbbea06431dc09e893c621d302e39d7db686e4412d1785f8fbe7b5522fc9b250fcf7911258d2e4e4901f284ba40a9bfe9dcc865d1483102f0d06ab3a336bfb05b892ccdfa7000abb07c82c4cd3eb466c5c1206223b3d1a4fdb010629b72b660df61b1a10a356002f33a72127ddb4325f29d9ecf6701696badb17e65debfc99e47d830a5246670aad8295be4353aa3e7cb69aac26e00fd19f0ef29f01c74b2bff4553377f9f9502dfac4ac003c560382df4c8022a6c06dfddd5bc74fc5a4bc8daa855a188c7a20d9e4244c7ebdda7fe00bbf50f55dfe56798017bde2e27ab5c3a1d449d267d6a04ddd9771a4392574f7bd0b211e4ef6a1f09d3afcccca66401b3e810d129572f0311dc6beb42feafd2e761120e1187a8e8f8e6f000431feeb5c8c9324b903245096a0428c505c40d1c0049ae7c8b8218624938de6bd03b52ca8c77b4c68d55b107c09dbc786717b60bca4b37d1b30b47b30143f08029c573ea6b2e7f7f68ffa8089999ff5908abb11378ea1182e7432175ab2727cd00e733e846f0a99bbd97cc0fad91a4f61141180f1f0090ce4311c9c4fb40114900c0c1c93225136cf8bea709c884d74767ec7e74e3703d1fd5de5b7681deb006ebc71e7875c065872438c801707ce3f434f1470b69733f122fbe8cc26b0389baca28bb53cc09bcff9129680e07d40430304a5480d20dc39a59407415f9b9c0daac6d5dbb414844464f0f2c008301aacb8325828e1408c858701304e3fc930a8f9aff9a1f487b326f3c5a7f01a35e0847f9f96f5e4951d251f7471e82ddf4a4b6df895a3e3c09a3ce5c0bd90a9703def4f7792b65f584ce934b7455a69a39aa05d19b375954dee4c6aed0720b10651a37f18c305706f986c19653cd105c8ef204b2b00cc5c2200b9790a86e0db960aa13477625b11b1b5b9a3bc2cb2044a68afe5c1c631fc17d07c922ae29080f412f2e9896ab01dd712c848f8d130f1faf2d95d71adf77aa5687c7454e8204fb18817ef3b6e9c1ad0ed766303b165bef815f13d84d093dfa2ac420bad2c70882657d2ada5bb0b29bef7202e633f213bf1d310059a4f4d0721c8318e9d91f01c8ee404a1086d6a5bd49af4aef4df84df44b31938d62d6ba16ff87d04c02a307ee7eacd1b7a71d816e1277c2b66b52bb360591662e2b56ea3ada8aee98128901ae5357d385fbc209390462d232d1e4d16685ff3758107cc89bbc529edd8b65034ce213e30f4b7e536b9b9119fa0cee2c0ae06a1851bdf8d5ec86638c98d29f0c0b8c8e5b7c0b9b0f42ff45c3173125cd7a1f28c9ea58e7c3259098649ab1b7093d5a864564177c61c066d43d2ed0451d88455e37f67459aa3f51df73f44a71058ca404c7674dc99a5f77f7941c3119e174d47d76c11b2a8308c4dcd3802351058482332d12e90f704a8cd4240cf8657ce878f52309fcc21f59e5b3b75279de0df2255f6def3a1c7adb5bd37cb5e874189c98ca023bd52de6aadbcebfe109ac0973c2cd2069aa338fe944941a4f22abda76bf66252873dbf979be769fcce32505978e412c8911df0c480b87871060f9553507f5ab6cbb0316a7d2f164aea86c0b6c90366d526c0d418f82cf827e9b95a59d1d174b8a1f4c029a81857db5a05c011b16b7bf9cc77e50c952f9a271a7428a8951ac44279ab9d868e85f711d1ba9035c8fe7a880d08f2e3675a49c41942b4ab73c1cd442a0b12af0d2e5a4bed2ab0ef90984bc57aadb8e27d01709606304afdff7b3640f880848d57d0c0f502484019b704c41fd10bb9cee4cea480542e49b758b1a444f429b41ba3b9b3844b25e054851865bfe7f42374b3a9d46be109d9a3702602fb310f071421258a974a4e9040bb8499e9ad8e0757b4b57734a0089488557d95e4ad6065639be4b9acd7b5d0fa92f816320292bbc11fef62fb9c24ac9017147172304ac92576eeb95d02e3a09898afeb2aec81f13dddd86962c92325983d3fe18409ec1944c1050beb506d602886079ba507bf341be010b8dc9f110ade038ab2585008b5ad1363e19007afa0c945640d4ff26783bbddaa2f002731f965315439f4419215d11af72c4ee680e067ec80d913a14a1f01d234cbae27a0d723e22be002004d93447415c38ac546b0ffa7040e63cc683836bce6970020ae015d0d3ced48bae2fb49b54e2e0c4b0d00ad5fa621ca51d8ee5666439129382ee5d0af7fa61d6a6bf734a3c09dfae7af7003ac69fac9787c3fe87981f605ba1f50e1076c028e7bd2f47e389df9f1e4b22099cd297e3282671067186b44385525bbbbf372f6d43b0b9076122871c457fe004e5364531f2f56b31b8563fb6904a9841fb2703ba4ca8be78596094f3c175010701cc20d0a8eb08abfedc36e421cdcfe1eae7091d5443bc630d6ca7c5639c2b0dceabcf430082a3f6b44bdbeae48c326a7f1516dcd46ec3140df97a36fe3fee08bfcce5e15015a9fac465ba2eb8a90edeca3be87d5a75fa94bc12c9791e354207ea9cf9a02ad1597003a9f9e7dbd5c8b033c20f4f1d0e859d978e30d46c72ae0552114a1b5ef350b6d850fe6ec1cc22b417c49513330bee6b0ed47a0a9f4ae609f030f488ffaf84dec66d6d9947704de66baa58ad22e4ec3f6379408990ca230ac5f384ea27c9835a3980363d0840ae049f3ebcc2786b4363f5a8663850cdd80a22300e0ac10f5e3c2a6dbc5c3ba58b4740936c82494f5b510de933f72b3b4e0f42ad8139bdd3225125bf49a75e4eaef91b83d1932485f23bce02f0331407f60dc2b019e28337239d0d6d093cb311f4ce2783862128f7a57cb2525a12c754cd0a4d5ff688a10b660ae0e19da0286c4a943ab95fdcf1627ff5e56e486bc11baa000645e1fd25d9abbe2f5ac60a295e2d46aa35d7493c629c2e2bac6e59c4d29307c646e9cfe94ca94a3a367c849523c4f002840b509cc35d6068a3c3168f0e8c0791ee7820a09a2802e81b8d5cabe699552667a8ef80ff214401035a845a1e4f0ef0f7c59c399bba63b725cfccac6d5e6a14e10176023cffdb890134274a91dc091017f1c00d92fcd6cb46944dda2c4264f57fbaa65fd856ab61b0a48e8512fc06ea40a628ed57d9693e7e1ef37d9349d36603364c9fe5d4842fb1ef92356fc4039b487c396759a546cc79e9b6b73620480fccd117a37dfa6f3e83138a4e025f0f83aad3a390013edf66ceb38845d57601b833d7fb714dae4c910a58c6eb69e906f5bb74471f3b01f002b68ffcd0626d9b27ce52a132aa1061a42a1780a3c7460ecb777452e31aeafb83943fd93930458cbece5d9b8edfcbb8b7a6fe24a98bae023ff3c28f84dc03db017da9ca0ed809528ceab445671d87c28eb55bd66c871b09e7a7596821bbc6cd2fd2e76ff91d8fef134ff20c2d474936cb47ef57042a410253eb54ebdac8e7db732fc6c245583f81eb6224f93bd49dd18df54ccc3ebb83038a8f56218bcd401e9a6e0bf1c7e2e83ceb0f29bf08cbd8f43b652a638587fd0f21aaaca9c6cdfc0d2fb1787a151f33ed05e2fe1ab3b055d27b8a8ea0b854af06ff166093d5cfbfafdf99f16725a1e9ed20712e72376be13b50c0e7cdb848c00412930ec31ef079b86d051458068878312b07252b46ca62ecb79c41087f7ed2012d9b98a141aaa0dafe16046752d85004e4d695d0f41ff6799c08c8a4f5422700f31f1c28d6cba61a19bb4a5288ef25674651b270bfd3f2442971f71bb5d69404d0a2231ca72a6a88903476be17e201d6b4be23ea85074453c68f76a489f5000fb7090380f6e3ba8777596571d1ef7c1981c32e408f1e290d2793d37278e60f0809eae11df5f9d09c50189383240eced2b5cd621b88136e0317ac56b8efa0fa0f61cd23eacdbbe611fb94aa4a3beda3a6a525f3306bf031d536b9b64798c181053c5713bb4a4e0ebc2f4f081df139dcf394983a38240c10b157f1d9e728e03d0445944a486a4a5996a479a38b388c4d416489042535035b772f2f29b06d529c0d906a43c3342b21695d572065d874829989b3d78feced05ad27ba3b69e72f4d01612eb6a26adf4b4cb9107d97ed79bc806ea996d949d7a3fade4e2ab6c70f6e0faf9c1586e7a1158d9b2459e6fb46b9e125bc828b16f678d436428e69eb57d90acf4ac8ce2848def46ec13896554c3e637b034c3768c93ea9d111e1ba3c4fdc06abfa6163361eaecc38f18302431e625e9e1c957a6157b0b198e2b6dc36ee98070967f078dc3b5944d35f7e4733874594f5f02f9da39cbf1a618ed0e48b5a99079886d5bc5f6ad4b5efd0dfa60b08376a46bae81311f638e88ba4140175359f05a00f25cb16566324e30a3bf1f7204a1c6ecf9208e2a92ccd1253cf8029082a0f39981ee31034d120312014389f426c842835880ea35cead4e6335c46a4a7b20a1027033511e601e32e582b45d22636736158c07826bf31c8373aeb1d280b0008b632f6967f6dbef021d6c4b3118917fff2bc13fb6a12c13eb07f7f211df0c70a false +check_ring_signature 969252710756bfe35e462ae63e536ae8142fd1033353b3f9bbb65ee098daa658 15d0d091921f239aab983cedccdd35e6864088ab1979835c4a471e9c8a490d42 1 8b1e2ac4d58cc9b045da9391284d98dbd5a448a3b6de7afcaefde0bedf77508b 4a22639cb3bec6d8ea149a21461f1f579b2f21aa630cc3a41842fd1a74c80700a310b1a2d7601483b70d94f47fcd26df8b99b00ee5650e980c6bf2169ad471c0 false +check_ring_signature 426558df6bec5a502002040f3edffd24aeb13f03a3aab06cebe54e5c621a264f a151e532244f0551337abd06be1b55c26400c49209c6f5a7c16bf064ed01d58d 224 83d9405f8f7ff87bb7f6e49df92f5e28b89a9539fa496f2e849025faa8cbb4aa 00c473234e417a8ae85f3582d612c47f3796c3be63c7134405ea503bec11aa26 fdef96dbf36723796dfbb3312e23a77e31e7da7da6f564be8e4cca9b54865783 0838b125f96deab52b018ed8ab434370beea07a0be1328dac5042c474b09c725 ea39d70efc719b875ececc1d63b3b185246f54f65c84c0272b88340946f68667 11cd7ca7702841e6364165af1f9d383ce6edffae42b328ecd2333b820a13799c 71a8f06dcc3acda556987449d27459fd0f4ce63cff3db743a2d29aade6b97f6b bea664893eb5ab507122a729bcb6b4a2b7f107d9781b22636ff637ff856442d1 8b1ea60ac4daa8cdf7fbb5f3e9a2a70aa3f1e2c06e3d68c6e82669b525207f68 2de1613e4c081f05d6856b89a7a9a7a819692faa35330e157240ccb743c31ec5 ab45ed5d62b9112f1aa4acf5cd2dc93fae8e1653ffcb6392dc7c187baab06d5f 80e299bc88adac4c9536ed0bed8ae71690ce84734fbbef97b9e077d3fdf7886e 52485c6ee3d57c68b5fa53ea5d2eb39a9ad380b1b47e7bbc6202a20b0905d7b9 f7cf932ce507c8ceaa27a5fc18f2f35d90bd4c0c3771664cb38a2340b918c8d1 f2a4e2292b9d5e21ceb6d2ee3d58037d1b8a3652d58eeb6f3ad38f1393d5caa7 8a284475b8b47b99dfbc95a11bf1b67b7481e93a2f7afa9eacc1b57ed6853a60 d2e7cd7b3b5e28c9beacfb7656e64542b4172453fe2e05c4553396f0a992aceb 2ce36afc0632ce36d650ffdddc7c186825c9e9d71a73b87e40eb3430465ddcdd 41d95b88df94abf4e82aea327ebe3cb4b47b1305c777ec9a2295fa82b54859e1 fb30193355a45bbfa8a97fd867aac2166de13c275cf73b7c1df938ab78230e0e 0426a59e33f9d1ecee7837b448603da95ad11df30b90c1b348e576e6ea4d5907 d23d2dfcad9b8f8641fb3e41904c920133e6272aef53f2b3a5b3788803364d4b 304fb103c3240735ed758d5077224d7514e91a2fa7d9ee358519d86f3bf39479 3abdf83f04baae16143dfdc10cd4167457033b0cd13f9c44a4bf4998086af210 1d5d4dd3918fefa31100f594a20e6dccac4c6001c828fd145d92d285a766769d 67de7c5b1681f5c18cc822a7d38a357ce9854cead63da01fb740fa4945aefc13 da55c4152b8a98d1e0b74b1c29f4f2e247c542932379f1999ef2492ecbf36dc0 a3e4748ff173b4c5b86865ed63a2b3a21f28b597a7c565c50616eb9c953268df 79051d98f713d72b44d8f6e4d5ad307ac1db61be1a355f2639dc06e4d07b3e7c 909bc198bb6b9e2fc82af2448333d136f8d1a5439b3869cb4874a20ab98793eb c425a95f74d591ca1221a766a75852d0091e62ade1c9336604c9a7466ac44939 893aa59304d5bbeb62c4c7b984159b68fd397d43d33da0d301dfb5a95bff9791 01688828db3db669f5f4dff25c6b21f77062f083e6bb5967293b4f102b9ba10d 4e5094915ed3e79d404559c4b5464a4accc03a9ca4c2c8c68ad2e18b53f8439d b30106187d37e5cf4dcc9cdb35f95efa749efb8773c65346993d6a1eb271b2b5 f9b5ee3d69864d1c9a106bc6d772ac740212abe7913bf53ee0527024f3a899de 7adfbeaf7379cbbabebdca5e09c7333292aff7b268d3ef0112ad8962ee6e7951 52ad96d2f327d2a6582d5d2324c0a1870385d9556f3c8a9571e7d4869e9d697b 09a916d3648989b2c1a926eb5f4c27deef6389c98e93e80d433dca2a4c4a7dc1 887658984c7fca562ac57233cc2d8a843850c54504b13ea00b24a4a28e7c6092 a7f6cfa368ca916f20a83c6c0f515668f6850ac1eeca2327d764fc408e948253 838c5dde8f49fc267025cb2f7ba01c55d3a084deeed914d6762b9537ae993bf3 0d7c6b327f499afef1458e1aa9ea136bd5326f6a313477f3b7d06b4bced60dd6 ff2bc8a30c5752c892e433ec3a98390ad6e6fc762773770947a4c691201a5801 a24d2b9cdce2b7c993ffcc0eb5ae97841a3f980cbffdb030215ae9a2e9a0d2d6 9aa8f52e480eca085140e9c842330cdc98f2724c4372e6912954168abe5bd3de 1dae4ad3dc5cabffe6b6790a1a8fec1e266ec6cef8e0ac0ce2c610ea10a33f9b 53eb7b400b70babe1886f24f3ead665d74392bc08acabd3a11f65df9abdc03a2 78265fe2e30fe180e335c25408b8362606ee33ed82f78fb050d991f410c31f9f 55dc5ecb4b5d6a3dc69ecc7009a54009764071233895c5f0e9555d37cadbb0a3 4b35a34b7a1ed620b4dbacd2af0c1b683f055714442e6e729f6cbd1040e87627 fd3ef539820fc642c91f3b8477f272b367cfc5fec58a59af006b7406c94172b9 d562220f49a7ff3341fd0899f90aa4a34ae3c025e647aa6ed8677a4608f7ada6 eccb926e8a4094eadca28e9a054ae40a238ca42038ce430557b9ef04f854bf97 5bd55be5debbf27c083f3ee49a7c372c3102cae66a04925d1398fe9f21951fc3 2c1e444670c414a016a18dc63f0a827a151f6286b2cec6ad30fa57476ea0b425 23efa24add134d2235f6e6e54f9827f96a2043969c94419c3cb1dc733c6ad299 6f2f9c72e8bc66490d5b837ba5dea4d6e6042a669a091f1e38c13be65ca1c446 2e58adae1f23e87302c30f232336fd464d6464d70688e2f66c091b724b296896 f9e49d80c51f224623716224b63c8e31c9daa98caff462679c7b6b0691ece8aa 0779f07242c8f91acfcc18cf716e04b5448b7f4bd9b7069207c45e4bd7974db4 b9d05cb95c1a446557e00d304af3f98344f326db6b78c57fd8c5ab82aa195262 602bf9f58bc030ab99ee7e2b0c53336ffbeda1224804c1cfa99f89b3431662d5 aa585846b8b5f1dbffceb8a955d7437303d93ba2afe5f88887027bdc6ef5ac9f f0dc75f51547306ea1da8bad30d2ce207f4cdbc7f0e60c6aa192f35e309eaf5b b58fe0edc06e19c27c20e3b44e203b6dc4eb7a1558b3643f2cbf9e9cf6f9edab 7bc91750c4d6bfc5a448f6958fe90c7be6e80956919ddb61eddf5d6a90117f54 d4a9d8e87acd3b1b2725fdf16c31d66b85e761c99f0919456da40dfcfcca151f 567de25420b633ef393e3c7a41cfb83eb7022d6578d45cff31c4150dfaa8da0f c8fa3acbdb74e8d4088ee4aae5b35b08e4c26039b55863de1a5cf8c6250da246 b3d27c45d667d735734006f019407677fe15e217ab1ae4da87b68f2dcd928377 73dfdc94063a704d6af0c4b03fc325f819a926355c38b616213844f420bf1f5f 85f6315a8a9d86e41ca91b19c40a628ddc54f90ca734a1febcd314a5dca1ee62 fdc1acb98bb51f8b1851e196fa85350d088668536b4b486da22e0e06ca26f5d0 5bb28bd69fda25c60066578aaa3a35d303edc1569fdbe6bd09744e0c05bced92 d5b55c471630678b7852b650982948568415b9aeadbc51fce9a31139593f7423 6abf7bce5fa5ca34f5005f0dad7cca2a477d15802ec99c15b297b0f14ccc0fbd 9c7fafebb2b25fc4ca215af41812b61b740ad3576007ac5ad3826961c61b9369 b305b56ced983f9af2cf99b0d6b2d329186acfd61a20f0b50bb4f816a4133d46 2026ca56bd530f972fd52473521a76f21b75441fe0f00ea240809fb29a49d1be a381d76f654a39427533119074559425b8c2c2e5ee228dfbe50157e8c993cd36 4a99803c67a6ca0cb721470460428abc4cc91521e9441c542e1fb57069d94292 57cf13bfa5183fe2fe34f2056c9f907b9acb353fecffbd36fa69c217ad2fee8b 25efc35de8618aa3773bbe001d4a6aa1b278cccdd508abb4262ba94016eeab53 cbeb135908eb0bdbbd113253bfe6e6cca620d3c945ae2514c8c1c05af8bc5bd5 2da5e6cfefa8405e0f0c409de62bf0d77eb5bc9f62ca7f0599722d5e5bb10f6c 40f41afdcb958af76fdb08fab0fdd1d3f42937eb9061db663d124fa9c9274b36 c5e64c4c5db4a8e5253bffb1b8640b498544bb4d70f50f57058f9101426f9538 884607138e0851b652d9a8007bf871f3e2e0170915e59f0a029e13845e05ca65 4e20e8e5af49589cbc7c909c59b8b02dd94d7a644f361a87c91234d699622128 9949bc8f6822dbd618ecc640e33bbc09d26c69367f403f000a52c1f6bf1a50ec f1afffcbcb04304b3e091c5f99687a4c41a2bb506b5a0de600115aa61d7654b8 ee27eccdb7d11799c4f76a1a14cb6121e31a2d73e524660937c062235d585f37 de0f74b49afca0d502028d98c08197a8f120c7d7899ac5e67919beaaf3c908b9 5d1d1bf6b033c97f346c6f6c387db3029c807c736e33de1972faf111be5d32f3 eea0e258691dc3e113e1210940ff725dd5fa8ccddeb6f183582edf33f2ccf54e a83e434f442e1afdd3da878f4c43384f396f86732b947f5c1ff50b2442a96f18 9046d53d9c2ffa662ebb695a54809343cbea567aeed49e506c4e8de149481e32 14179808d83b9556cd10aef32bd40ab3bf0b9fe861bf9f72041de52c017ee4f0 ab8be3c737c4c8fc71a40abd1608835fc32e5983478e417a486dc6db421af611 1162a04340cb9742d0b9f8a4ea946a70d6ad935699ded27fb26a9a7cb42391d1 f4a74992d1e9abfe40dc3f926f4680de0073c7814e77fb643f87cb2cfd4dac9c d13deb2b4b711f8f1b138a00158451aad925c24553a2129d0225b1bc7f7cc057 40e98c3cf36910844a0b2e9083877ae48f57fb0bb1931e7860e4b7259ce05b01 8ac2f21c05c675b4cd50d6aab4ae0eae19deafeedd6ac5daca11e7deca82f575 290d24be85f9b6c63e23ed36b7c8982f03931cc47b702fc9e03f468513357fa9 a07991d5a3092eeb84a252cf600174c00cead97858fba034c29507f900666a9e fc75a423820926b8de5a4ed96677aace8f374e8cc34306f089be2860a2eb61a8 bc09a34d5798b895b9262ec6ca71ab243bb43dfc797caa160aa0a2674c0c3ed8 268af1a96966776927c46500d91e58c97564c3dad3de2e68eb50404aa4b01acc fcc992b469e6eff2023f0a09c734ab3ee2e610fb990ca49a843e6c594460c358 fdb50169f1511f298a92628cb74a72669ac8648ef8f426e6f2acde71a5bf57d3 a78c754c51d1ebaa3d97168f629963e7af93e93861c448062ed3d60893f843b3 cbce941ffacf4871b7da9cb42ff810c6add721c2e2377c428ca8e2597623db73 43c3994e1274bd5f21b7c276f66f1691c95f72a1012a06d62ea623c929d73159 0544355799ad8cb4aca8466499bb60e0c58ccdf56d51231c248e67b3dcdc0c2f 71ae312cc324400b1398524614e4f20fa43569dbc2c803964388598908d6cc45 4b61bdd0d7349fdb243944a0aebdf57204207b01f97eca4b4e5dec95347acdc8 badcabbe1c9b4aef776e3d5e0e5d847dfed4773506575b4b4e3935fa09c194e8 7e96d7661bad361db779d86b80b0bf16e6c8b64da628ae7ca83df4e18981432c 264fc816763a4d5760eb5e6a736374f32a776ee285e0eb648cf19d71615c0175 bcb39b028c2fa1117b8c6087f815219ad10b95a221ef386fa0e255b4dced825e 63a55ad39426597e817125958c44e07eb76dc2eea6f4e06928973d3a7a240639 eadda861f14230050c295765bd1497a6c45623db0100e3ab7dbd5bec775cde43 8eef151b843f4f5f0130c463e9fd697784d10bbd58785d7ee2a4f06bee036acc 4e18836cc8b3a96882a4b0e0aeec56a6e959f8341e8a093322612972645b5e68 37335e80b0e48951727fb38f35c595b25b7fe250b29f359ec7d8c7a55bbbea5d 2083bbbff4e766a763cd9971a0ae157da41ba754e26a2daaf6f850ad52e72871 0f680e4ffe9c2900546526858a422224d377d7f07573331198fbb224c243d0c3 9c9dd6a679635c7dbc9248fd17f5230bfcb5efdd8d332d827240b9b507dbb6a3 fac09407add61e76348bf78a658a490040b847048eb724df384de8fffe037616 55597642fcc193cea654964249ee223dddbcd348e31085fa91085a9fcaef574a 0a1f6180ca19532f817a2c0556de1bf66e068dc0fc4a461597a03bb74329e418 6aeec16eef3aa21684b9941667b6bdbee617af69f1d31ad1be1b3e0c66a2cce7 cd608beca64e321f6ce0ea5cfc8aa962b75de4d92d2f6c6d7bdf3d0252a5e929 ca85247fb66cd0df4315c38db0de7b8f7ab5b1fd83a8bddb32eacb8be9653ee2 30008f25b0174d95df100b7348faf39530bb750ad64add900c587db12c6bcc4a f8d0bc5417a6b9e9ad59c70f41bd5d630ade4f06503ecfb3bd0eaed113f9a201 8214a043eaa96c3f737089fa59b493ce4939b6cdadaaad37f3deecb6f29b0bea 0fd1ec1369070ff04b112bbe70b221b47b5ab949ddaf4e2f0f583ea2f5f915e5 0817abffab879029050813b1eebb6b6f9aa0594ff031fd9dfbd59f7a6e1e66be 4613916ab886dec1d8a09050e99da9b5cea804a8087d0c512e77dbc0af913e64 b44297cae59abbb609890384e33fe429336446db22b39cf9c464b7a9bb585217 eb0e62857d302d84b2dd88df822aff15e58b5c489872f8000ca21a27a1083a07 2b01881d3e3aee6e6bc5a7c57e73e855feeb8f52c49c06b866cd5c660392aa94 0fa31bd6f467a577111667ad2a4da9b60c3bff3e4ee70f65e71c1d057727da2a b6a15bd0bfe636bad8fbcb57dddb2f028e57ee28b5a837dcf8ed559ab2350728 2b44e2879cd76f2ba98c28f102d0b2048ce76aee231fc16e1778abb8eb2d71f2 72e810a8164b3fe144ca5d64381c3b88297545438da5f84513572a702c37c87c 8f2f861ed949f38c20e5ba73e3164e54158f06fc405d449d4bfcf2d0f21bf89c 9eb8fd12b3bf228c090ff6eeb23890b8e4b5e417ab6bebe4b17757450f02e7e9 1c87191b20d048be0d866b3eafb13e7b778cd090310b88d0f1e34a8fd0e89ce0 08c0f0e73b04021fd142a1c63b8087eacbbbe095f1b23473da2420d1df0b10fb c9afbe37e1836c8a7e6ffc3f6b294fb8ac0d40226a9f231d36fc23f0c294471e edaf2bff43ea37dd0fa61613855be52228c3b27bb55bfe240fb8fca6ff2eb9f2 5c897cbd9de972fc153df2515afc7e2b3d26ed900cfd35ceb2dc589d6e100c83 678bb122bc5e8d9ebdf27e2f408c9de61471d64bc8c1903347b1ec96363d11dd 6b598ca0eaa930caec842f207d3d92e5ca1cbe5b87cd518e46a4a5b49c1b77a8 da9a010512768e3e0ea1ecc0bfd759995edd71190356899b9f8faba791bab2d4 9e4bc8975844e37f95ac07cacf63f403852364a70237196917e83247648115b4 426aae140615a2a329f0115d515bffe0ed81798cbc786ac5b6f2f34042e9216a 794e5dd2fe10156a903e657076fc0d6f05ac217f17487b5656955e51665792d0 27d4d30cd6065f9901230c174b23ee8646cc3f647be97a1270403db589f0574d 138fb1c9d5a1e656fb53c5966fe8a6bae5b88c116bf0bf14aa0c61d3ffd4590e 2f0eb99fc6b4ec1f9790c2a1047879e84e7bd644cf5c0c40950623341134bcfa cb2ac7f1986adb9f1c4a5b283d65fe3220b6d79e46d0c4b7e8d99fdd80578640 b16299cf1e5f3ab098ec74d264ed6e8c49a844fe2946c7a2d7f6b35e80c08b68 1ca147886ce62a371780c9686b0d4c81144c92a973b0726150b0a64f3742a529 53a5580ed45d1fcfac3b21311ce1cf63a21bd78e502d0894543b13d1abae3fcb f47c56388199bdd8a31eef68ddba4d90a54b31c6f29823e8205db10be07589a5 d2316e8f97fe9e4ea2647533506b9bcfea4c3123fc86834c54d4a2b6f6021c0e 17d31f65931dafe4d1c97f94058b513687607b4e83375aa26b924b2f3934e686 697cbe644f84659b171c90d74011433c7554400aa793af3b7d1589659e9dc9e2 e374ded5e5ce9196647b0a2a6f90b80906388c3c86bc0b2f172b2e393713f9b1 3ede2238765d5be50e6249048e20c87601c019c4725d251b04e3183cd3e64fda 8341ff4dd17e41f139bb555ba79c2cc815d6f0d01062ab48ff2247f3a54263e9 c7087d0c6422373da3c2f536a6661c7e0f9f91485b46e6c694f4c1b761ffe834 5e1a38a5775b94451195be4a6fb5bab06a8be5509c5c7138cbc3fc83c7f4d48a 5f59e26b8c89acb5212a32b407ebdccbcd35ccc37f535584f65dd81c2b8fdcae 146750ae68252f848af0aa4cd025ab719abbd635380e07608f47b4696b6ede1c cf871b88f0e55345d2a333cd7811e347d9a7157a34aee3aa85f0ad40549acf06 174d17a72bdb0a70b3066620f11a0733bf3c3ffa631e3b6aa976f6f81f8a479e 4c7747ea40d72cf1bdaad42f98001526b8e2b9193d9ae07cd65bd0da90d1c1b3 cecfd736a3cec11571a80042f460b9d899f6e4dfa59701bbf470849cd77e7dfc b677121d8523f0a62ef9d897b857c18721a65dcea840e23ed37d896207c91e44 7479a24b88c7de5450e4f983ef22e76ce760d7590e391449bfb915a3a7671d32 cd37a476533fb5a662a2d308e608458466cbbf227d33d58fafab341997a094df 82ae62b36d73686fb2a178fcc466a7c58696daf91c514680d0c2804fcc939ca7 79bbd613a14d070082a71bab0473b6dcd706b6c29c0ed7a1493a3e9bd75c7549 b88cdafa8081cf6045a39d771b3a1e5279961af0a2c59b2d53e61c37ec478013 70ad19e7d54bd2f36079a1733bc4db54baf97b1caf421edead5bb8c857a3caab 19a07ee43032d5bd1686d3ff0e08dc2e1730eac4c5be0a869c6bfd1c66dbd937 7a40c8e83db621a1ecc26f2b3ac431816ebc546978e5a45b6fb744bec74b73ff cfb4dea3ba96b7d53348b72cd229b0e0ba03bb6974d0a9e31140007f1cfc0b01 4993bd6957e2b65e705fa5fb32442eb918b4b1f14eb496366761ba672c3ba4b9 38abe9dcf5193353cfe8aeccc31942827c5c19ea5077d2b5eb7ed8a6a88eebb5 13efe92b585a12609c98eaa09ba90782e552a3214dbdcb724beae87087496755 ee19e193c0a9eaf92097065c59ef7f82e35e40e4926fffd8bd8a008f10c972b1 1321bfc20228b2da0799a832c73e047353a5ebe6f59552881eaffef6030dc9ca aa5dfeeb26578866cd7d3c6476c93d97f0ea31736a4d88183cac8c2ca34c0f40 02f21ef1dc3b0c0b9e1a9b85150700245cc1487708c92cca3946aea93fc0f68d 847e58dd28f5c1e5e10746692a53d4c91625b750d4961ad6d09dd9102e21827c ce8061662efbd8de7166a998e499607567c5fce76aa943f90661f08ab5826435 1679748cbdba94fd9c02f7665f805550fa81bcc92b5e0ef594b8c1288bf21991 669733729b68acaf2190267903164a9032be708a7076e438f35604f68a5cae09 ede413bd0ff1a285da14830cc4ea203f2ed22e49b0956450c1df06b5d58cbd7e cdebe2b437bde319d37012e1034cfdfbe3581278022e8f1e70dc48c6aa1fca94 198198d1e11760e56a6fbffca9a4c8b43155511227730662623ce59311fd90aa 5618731a833133fbba12790897ea944b8d1272853061d0d0246b056cad1d6357 93f7fb894635b23ffa0f9d1f610acbb7b13db8c5d9005316ececd354a493b37d 7f603e4d036e3e78295dc3b5af355a4200f661bfb1b848ac4eb42135c2696d8a e0fe38dd83601b59d16d405ad42ba6e50e57f27b5f39af51f934d894540d67ef 49f30a50ca829ae3b4b23b031adde21d8c86a6ecff9074c8b0941a548fef9c34 edd0ad10074c595dd138f17e27ac57c43d2a04a0d5a225fbfe2a998ac03d358b 72d943207e5bd9d4ab6eb49991839b472357e21aa7566e2b5fa7f9507c9c0d2e 280314d385b9382b12579bfc39cece3e820f2dd3f968b4886fbb3299e73ba073 dfb44d1f74d884f6ec8fa7b829d5b14150ac8459e57f420985f429941f83f714 e9e1ed538fdcd0999d73bb58799edc3d5871920f03eb11b964bb97a3d0f841a5 8a6ca5689155cfc068859b7c82034e0a71d77a09031938ff292737b4dc37e666 1b1240fe66a902939466e36a9e0b5e1d65d061b868cff08f38bdd80697993dd7 bc99f314bb866b5deaa45b3f81d1a465b7541483903f39640f57d029e1075a23 b9b65209a48a05500d3b6b1f7e94dd851ed60d7102d57136aaafdb875b610b23 9f52aba681720af6cb8a0471b4d384e1532064d027423972876c16c60d1e55ee 4409d0308688a57ede7b52cc232376c42880cf0d0f63674c30dda3b40aced87a d9272ca6444f94b9aea85152d7eb3ca63404966da2883061a5ca9681849ea7037a4c3ed622cd46d21b9ed46d8f468ef344d72db9bc9109658008215645ccc908f47d19db9acda8ddd7624542055569c76af9fceaa1903ddcd14e4e87260201020a081d1b8a52c5ea5353e07df294c06ad857e5dd1047ef571b0b0509c404a90788d548f54fd05dc105fa21f1d1f2dd293587331024825a78cab020a40b25ea089290c7e6838a1b7ec1de84e6f37a7e1aff051b11295c10d0b9a37a99944a2e0ee30dd862e28338405c5c95b7cd9b39f56a9e64e78baca96fd0cb56664d2748054b577db173d6a34e491b99b196565be361100a4e30c3107d2c03f5269c2af909750f7392fb9658fd8856c852ba6706295201e60eb44c56d0a03b5d0fcc56ac09ad48e8ad14988975094cfe030403c5ea64b1bef0abb68c7be7caea0b477b7c065d62ae295851c84cb6287df27ac6747571f9334b71f39ca914261c2ad2150d000c17d8733b67a780706fce13a10e9cbdd4a68dbf257747b2deb30629852a16080e4ef3eaf106a7738dd20c5edaa4e9c216e295e6addbcbd041c40fa763f6380bb4d0df830f513be66a910e3dbdf86be512d31a88a7e7bd8da034020273f948054afe253d966eb091405036f806ae7e598cc59a0b5047b085effafcebd7cb58038430d704ff2cba9b0fa88033a35022d2a4268600a6532be64cfd8a230ccace035cdcd13f9dbbadd85a663bc6ea126bf647ee70faab15b2b4ca9a69980f55cd050e8aada7db90c18ff1bf50cf90a5f3f3c105e6b24afdb69976f9c51c19af110cf029d1218fdd658de5b3f75dd834121c767c3db480b3453b1441ae986f3eba02f31fcbae158b484503ac37e484c01833b9238e9a2d123cb9216722518fe1e7088fcc7d8f7e14e9e39b6e18c7b1f688fd9351e90a077b53eb7b359abd2bb7ca06e130b05a8b6a2395bdab292d2874e214d784a68db90552984c4e5f4f7847da037fa5f2f129087b157ea8cedba98b51293cfb8b0e9bbb377d202f51c845ca6508613c9f762b89f262a3448152b46465798565918c30866422da9de824b5385a02538c521c9a754be0e168ca91269884fab9f6656d186916e98309232b34f2810f057c7890b8e556510734908ee9f2de69ccc50100ee5d9a0c67c5f8d44549ab01e144802360def00123748b6511b5e74d0b1e3bcf0988b41f3a16ca6625a5f60b74d3a8cbb55ba7b5f7e1bc542e25d7d641fda6ddaeaf3c88ad17957f6c11640594fdacf40586671ad94ca7fb94cb8ce65b53c959c21c70c06601950c496eb70b9a1b0e46ae25beba963f1028ffe54b549703edc09581fe7e6af150c51dbabf06f98134bed8ff7961d0382bb3aad1377c5f102c29b3364d0f4fd5ad1461d49109f1aef784673940958a7ceb4508d5fa2a8cde9d1f770743cbdcda89c228be440d1bff327b96f48410ccacb5c1264858f7b8be84cd4b9e9865f77d9f218175b005ba7a63e4e47a9fe676d93cf2893731ad7e3b65f46365e81ab80b52b0c790f003ab61d5f9f70f003eefaf718ac79f84d1821e062852964121dc3a5b20d2988e029415484f38b42e4d15c8a1a61bc93291a0e30f977f891fd8ee39666b39595403ff093f259ad75d8a9a83b934099df06f40ab6d1c70e530eb8465331cbe24430fd0eca65e5345b79f9ffa43c35263c290ada226d100623bf224058460ba1df30227aae9b20d8e02bbcfcbbc4e3ecd267792745215df3905435caf2ee028b8f100d84d06cb7c9b2636f5553dc8fc398b509373f4c8d1fe74176bc6a4f003fd310ef845dde1234758e0af00808b0cd6e67b729e04bfae380c83220b276dab10bb0949172c0a3cb6e75c45323f233d2e122c6f8302928dcbf4ca66d41717adc82f05c2a00108e841b38efc0ec824c6a132c5a2683002d8ddf86d6756d5577a5c3507efb568ef7be70f0857fc478a93a7f73000384f9e75cea64f229697ac84fca70ed597225c8e0a7c8bb7a4b80bd3baf7818ae79aa880aff822282e77987b01b805fdbf407eb76d501fd9f74cb5c5b64c6a00902d9b172328513a278a4321157904315d2bd6d26d239ea6901bed73c5d8902fd7c49430745d6ebefbfe77453a6b00afe150f398f1b2585fa354b22122cd1b6e48817bdc5e1a5228e17227cde39d0996f2328a8c4f550236875f53aaad5afc9ed81e31a4131c7353b9b03572382a0068a024bb6205c9977f7dd36c27fd9d70227aafc05b35922dd63fee17958a820d3e86fd0f89fe864850de76995ff6c73e0513fc9455d4e10ae593f1bb9422180af8d59e6e8a8367d080d67c84c8d28c23eb6c7ce6088d90bd750a16c95be51205ff0b36df7085e371838557ca93698835be08432c526c10406553707d65a24802c28a1ee23a3b9676b7732011c19ac26b7bcee118d4d4949240f38729805f4008ced469369d0bcb467ef42a1b72c098c177cce922a56a3cf15f6f9365d409290ccfe79e6b8d30bc9d381f584c875bc92bda9fbb42600b47983012729fb79c88024c39b64c5e1c6d8b84cd8d6a126718cdc5b5338261e3e4bd447f9addf460ef017705da6042229e37332a5dec46ef0eca0117526a7042f7543df41278cff848032ee2c9b067d1bffa99380b486f0940341a167a2dc2499a5d740f7f7ac2848b0dce91274c97d0d1b63fd094cefd622a47859d56eeef7740922730830677820a074beba0e0d6af3daaaa0dcb6b2a3a0cd9ce8ed913eefc6e5753f6829023a055025b9910376b1fbb64e5be0c52b5933c1b200b64b0ef1f82946deebf37c2454001a74d72b96bb89b5fe4442129b172565843de02c26ecb4a05e7ba315c30b8070ffc20f25314dbe7fe40836fc4d734a30845ab5d536412a9621af562ae6a921d06d09f28fb501a866536f93ef30becb881137349a9fb9a75a36329d4b0562584034c160374aadf5555f9ebb2895e89f00b16d6977903c37e580c6ffd2c9ddbb206b3ee8153e41dbb092b51fdccf9837c3e0f39af990c162d7cc0d111ea3ff8050fd05f7f6acae25b47df83ea62b9366d8017c09e8c858d94625357af5791d28c0ce4f47432add4fd6b5aca85f2b2e0b83f32981ade2b5bb1cf6a27fa157ae1910fff9fe06d82c99d1a5889c739e8340315c4d17521012d66d1c4e6ffaad58bc00d245b03037ea38b722a0828905da3d59f3579322ea90d97a1d32e330ee831800184118e66aebba0df10b9836c5a3d41f64b09886b2778f5bc9df53f0ee12b0206f16429b8aeeed78b8f4a97f68a9af80fb6d3f32b6f742779c90874c21f67540859cfd1064ebf4b99bbac17292a54a7c44521ff286013436733a54e85bc80480d66f2330562ded488eb90dec973284c2c9626dc21c7615ed9c3c1d11fe0ea970c86d1f32e340ae4b7fef3be12caa0741223200a08805bab29f0fff098ec245905264408eafeaf7b9cbaf3b1857a54a98598cf81243780632f55bf1f0d336dd00010092d1e40b73270cd101226fe937ce63bc6f09069dbea4376d1825344e47606963c828efa570b37bbe740d8c4058c7377fdf65b9052114e041bc137972b62040d3c646850b13fd91cd2655c0625b34560afaf4d998ecb52028f02767f94d60932ad56566ae4e2d7a5d730342abab51cec3d754f3d97f12172b10f7998b0f8064cb4e2793da26118c1e1ccacc0bd031c0a63fa66ee911e0c09505ff083919007530fdf1acf5bf0d8ec51eb9e6c894fac2e547b266322562b505eaf7cc22e9800f698c898d723727733132f5b50a4b744aa7e02af348bf12a03b8e583bb21a505c96bd9956ab951045df7de95bd33046a9662a6614193027fb2e99b77f7f50d04730eaf207e31a8cfb41559dbe7027d6d9a1327c38b53cf44a317c0d9efbd220026832a6a268c6186eb45298656550b1669f2ff642e6952fda78038b725ace80c6085efb620add6835fa43d4a19f59ea6c35f032ad15aadc1232156729f348b0aff0aef0c156ce73bee74ba4f56d653a7a36688663a0223b97c0ecdab53876e02eb72aa2ce98a0bd4a739232dc2938d278a8de48c90863a3afb04c9f0ef0ac603dda8f3a39e93bc025b162a7d6234a7005cb7ae0c7e83bbeadf1e148eb4918c0078919f63a6dd3a6198d454376775f725bb00564b041f698196807b53e1bda50460b7746d3c08659991b46d1364e088e24191840d2cc2368f7150c23a050e1e02479102e44879201cf61113a902c0c3853d3d8ab95924239affc4290c4b18690235a71a457576c21c18ce2b960cd1b6b509b7268e0eb7904a6bbbdeb78f85ba03159a31542b4b80fbd03f54a340af38bb7092a2b41b127bed20e989cb0e07dd0fd44c0dbe314c493ccf52ac3c044cfb388be0351be89b4843646264a81926aa0a24b9a57c775c425bca5a1bb6f4ffd5a437534d637e35f7e73f15212a9610ad0b69d65aead4916ca0cd211bded4a735f9735fc70642dd7e21c21fed8b32bf40051fe792b76d011e6e767b4c2c92a7bb3c22317b8241d3783a2e4ab7c010b6e801ec0cbbd8f6ceaafb5ff7af5497df936867776c291e94818c4388c9bf5d77b306edd14abf235cc86b25cacd38470095cf5dfb1be3c1882e5fb69dd2775b0dd8089d5b064c182a2a1b3e7716ef599cd4d19ef1e5e3abc343b95e91bf5c4c8f5b0b1a28e7a063e855bd24450f32e00a498194e31a6f76e075b8c1e28109e56ab50a3be5c86596cc84c2ad8e6d07f77d84ed6b486bcda0a7ff765cb0db8cd9966f0fd7baa19c008747417a229ec8abe718cc8736cad6727c452299978be7ab0f47040f506965189b36139e729b82f861448d769d28bad32de851d3221c191e64c30db31e24417542505597286d62b04e6cb5860f480901cea218fa050ad841ca920d9f79248ea578a0f66cd5160f3f50e145acfd0044731cb00bd9ff1caf2366f20c161d7c7f02168d36bc147869007399a49166e923d655218d9ead4d36b90c510718969d12a99dd6e1e47596c4fdd9e92ff8b84ff5c5f0e3268e56844bdfbc9602c9286729dc0f1c202c2dc61fb67cef9d45c07243bcd3e4d438b9b126b9137604da2380753209f8894786569a4d0bda2e53851d57cfb981ac63c7066e47c1b801eaa62f00ddab8b74474a66dd7bdf24c794ce4ee977cbe147be7e2b39cdd0b707e3128266c2d456ddfe4d257214eed7128d5bc4228f1f412fdf3420b2ba942d0c2a1333d0e3622bb536514fc27a074fc79f8a5e80fa843b318ac97743d66da50a745af7add2fe7169fe01e72c7bafff322ab8f84f18ea3dcb35fe8b356304c90606ad4e2ac1f3bcad2de4c6661096091b23588293308cddd49da9d85314364208d155fee51447a90073e38f93d98912de301ed716387e9cce3841c6e607e8f20d1b70cb766bb9c5e3303e7a7d15af6a8e5aca2ec185e016db15f8e730aea2470d5076c16adeae67547211c690076c3734bf0b26a73baf6289597def02bb0d88002f1ffd396c6f35d25c9af42aac88fc475971f978e581f97901aceb82f2d24f08eb251c13e355c85a7411757b259a2a2d8d6dac9715bb1f56635470c8178e1d01a0da00c8314ada0b39de432341c1cd801289d7d67f700e27580d21ae44e5ea08bc2d3f473c474eca8d1c383bbcf1f1364451c58db1f7375b0af94166e260a907269d2cdaf9faf8c27a2b65c3d6588d4ac0598450a3eeeeab7e77ee3e39796506b18c7e8e7201841db6276a4ed7253265490d0f3ad97ceedb0fbed900ad12b80003189db545a3f4bd042daee8d0390dfe711808117a46163579f81390e3f5bb047c297c2d5005dee61f78c76bd97b9136d7009ee840dd8ec2c4d2ec21d9c8020644f6d4e2e34dccacce921971832cd1ee6470f9c28cbfdbd3f721d1bd184df407f8de7875b564048ee14907219a1f3fa742d39a76dd3dc51584e9930b17c9a207fb3318e33c2a688ff478412f0ead3681424eb3d2ff15abd307aeb1b0a9796d0d2be742dc12375e801330dec24f9c7bb9054646b40d70ea81a6c361e99804320da1664db24dae75a6c383abc3f76ccfb032a7a42fc13fa526ab052287bca21909d8c002e0403fc91cce83b106a2b40265b214e7e2ad491848655dcd9d87668a08fb8df83cc550faa68abd3b2ebab63e8ac009222a4a15e720e693b4513114be0aa59352469da3268c813c273a35efe0acb5426d51439eb7a3f2215555b652690dd6982aa02f8c8dd97f1e9dac71b3fd978e592a5f416a11dc07d15e96ecc3e50c263a8d9df3c9bc2f102d38ec1663eb57bd07500db01bb9a895a4ede39f30d505dce8b09a3745cc7ee9b5654f1c1016196d478b3e9476866b5e25a320760d4a0fa7321a3e840e1fa4fd3b9f38b78641dae883cee9eb6c6847c7157cc9cb9bd006e1d5552581b400592a14001a853dd494df34acc6b5cc22ed49f42474ca1b710813be39446c3daac245519a12bfbad1b10b41c2eca95d7172e5bfa9b0d8cb850aa89175661b6292ba2c745d8a6bae6202f6358db2672771798a243b7842b4d203412fbdd6ecbe3c703c6d2969c179f3af4d4cc505f6da71b5ceb2a5dd5477e803858037dfd62be5ab02b56aa7519c1d5848d076adfe241fcbd095682068bf160db8edc20acf4801dcb2508349f6c654faddf71fa1135e2ce0782ab718ab96cf0cbb1a7b63e13400c8abbb2017e263aba84f926ff7ddaae61d36e96f28ed3dc908354dcdb298f1777d7eeef9084d025b7c1fce3df81e97c354e26318f7609c350adc741315973851807c2dac353a55430d1f08dbd7fcb9b4cbecfe0d57292c09026ee7a2c87186629f7137ed1cd0fa853b8a028594ddbbf5e0d4267172f4a02603decfeb7cf8880c1b9e940a6913c0ad1e1fa74b60be3b5eb2ed252da6f0804c00babdffbf92454f5676babd3b81d72a757a1fd0524af7fcf94c127316ee07fa0269aa69ab95250f412010f500e1dc6a12ad6a8566953eacd939b1756132976000ed35d0f71be0b404b91023f3d40bfc631ac0edc97c0aa3f1812de4bbd3e7560834714178e8df3530e32d612539fb1cad5205b4ca8e693caa2cf8af8f33bf0d00e5869e9eb60b6160201c4c213ce95b884ac5a6d91e514fefb4d4e892dd1d7a0648af71eadfdd3d5322463c7649951e8682ea1a781fe43b7429a8f6fb26fb9908e49a165cd71516e01a813f41053810c86310ce77238889faeec974c3da843007c6a97069d83e47b7531082e38d923b0a1105912a924e2df3ac5965e9793ae50dd109d8b9d65eedad30bd534bccd288244c9923255eb1de70858ae42101d273057714d4a95e59ac9e59b6788061ad575a82fe72de993b581b8aab9c4e9d873e0d78a5c2d36d94a17a2b38520f151efe0539dee5bc8e2ccc1a21cd294ac5551606ce82e9cbb184ffa1570dc367223c14a8064a97582831570a94bcf41a901f57066d178581d2cb260887074b05ef2602b30dbe5f20e494f006adc274dad342ca030793094a98d202285ee4e39e18273d38400a0588fb3a854d9d8e469c054b2f0390219e63aa6be5f27df2032bc06ee7c9891c2bd430e7fc1a687a98981459d30a693be3d53d5965de2800eac6588c1323a78652057d3d8013a93b3f225c30f70da03d83f064dae87bcf79160249503e1d90596f4edcdf1dc8e31fb413d9a46a05cfad9b0def3b14b79a5aabc096ac3b6a64b65e5ab2742819a2e7d2d9744df70161bc5f15297ba3499c223a1f2f75e52e9ebf0374199288aa1cd0a7fc1b3f89069645489b829fcf1ec57d5842240b6f91f76e7100d0a4f2fed74b14dfafb6d00b7564822ac6aefe20bee0d5360026ca03a55054ea6a7f6535776106e967ad8b0cbdce6f8cb143d9d5781b57f37880345c960a61c989169ff169c4136b47f9810827c41dd2179a5135927bdd0432bc537aa6924dfe406174170b9aa69e275db8070ff4cf2c5ec77453637e021cca26db4da4166302cc87907e585558f776b90c02ea299bd2acd812b69381597263e959204a8e64d20a2ba7d3c28f6f603285fa06cc89ac12796cedeb7532dbf59a02ecfc93286354f1787e4acbd960ec0ed4a408245c8bbda2e9505194b5f80fd9f7c6144439435e8a82534c12627f4a44af5003aa6ec261b346cadfb8b6e97eb2084c0474857e6909ff970df276ff6c7e28020234146dce5926855d4d8fcaec89339945aee4ca31337e761f3b527689f0476503cc976b3fc880d6272037484622f4d4199b9cdf98743716cce241cdde3245920e3da5354627ed377485f51431af5dffb067eff77462b7cf528ac669907b79d30390711df9859f66c242cd64507c49dc6ece41f8c3e2ccc86e25b24c2152d513046108f7908777efbc487081cb73cccf6224ef77a4b302e4521f657d278bfd530c59e6b8abe1e1468256b77c691005cebd055fab7477c451e9df329f1f425f140dfddc1e719d5e18ed4cbb3a5caa40bc2f1df6789689a674e675dc31ec1a0681096a89889bb292bd4dd3141f4a716b6596dd37e68e32d35e7ee83071a12c5a1f00210432f23c6360ffac9358edc893621f4751d53bc1b94f848fd8cde5d391e908dbc202732b70b21b8657e3faba687f2ea3d315f4c741b0ef9da93dde0bff2006fea7ce760007863d82fc0f20ab655c2a875642c569186eb87354854bbd4ea20144628858aad56132ec9dbf32288e218ab8eaf83686d0d4c6aecdb2ab75f2f7055476f6623af8f724b17c1b8a322072fd721fb4a26f2e43287f6bb7a87a401f0075a594b937bfcf6b99d512525efb741f796b55213d55dcf689dd09b93fedba0d0d481689715640a03c0e39f1dcdeceffbaecbc8363cecdc4bd17b3899c683a05f35574f7e13e051d38e3dbc6897af32d6c02e35f0d09fcc3fcdaa6dd7445da0b16f9f98f1da56d9b823a94fe9bd680133319dd845ce81ea003b4efae9cddae04b7d535c2ea682e046c812bdde460311165d8b60e9c0978eb7ec1d386db9344023816a36a8a69bf4592adc50c5236625468e05dd01d1badbcb615893087ebda00fd6df1e2e434f0d337d554a08c480a5788a48375b692879445c5092082938205c1f518516679da09db3d06a40cd6a9dc5a02e89221d528df31f7a55b54540a00c578feb7e472b06657eb531ed1e8bc4f459eca9e5c4b3c5a8d91bb935c6b980758174e13fc567ea916f2ab963b8e14f9cf4f2e6159240c80cafea0ff3676ec049a1d15988e65b1da802500ac42c2c4c52ca7b3d3763d3fecc75887e47f2d5309eb00f3e9fe4a34c706df86768ce909e88443a89af501da3da7d1dc6fa1269600ae63f0d5cbb8a0970c691df182196e190306dba858494c3a86ebd1fbf6372a0f374f72b50925a0bd54640de12739661d685184f0a06d5fc8687c55467d0fd3053726526f07d7dd21946462443d5c9d355617dee1d032218d114eb7c0ddbb100c4ad268ca411db049417794b0434ff7f489b2148cf9c4f5baad3706899ec935085811985257e532703941f603172ed1936da3d3dc6dd7caa5379c6d29435ed507cc3277239c4b3198286fb45b13d80f619a607829816090189b6fe3b9300636064aeae8588f3a482126a6760a4ef933d1ba65a630ba0216576640e4cabfb2d10bdd58dfca00db899be18d9e790a3544907889da38b142590cd647d0334bc4200fbf768262a462b84aa4ed41f6bd90ee48f6c4c70d4447576301ec0b06ba76d301b25d8023eed2c7b6cd28b356ce7cafa6827553f7dc50dd5929741c1a91df3603b2f4cca8a0918a20cc7e01d6652df2d7d8b3c6c531c2dbe4b394129a1662d3095eb17344e59f3e99ae9b344664a6e5e51551699f1bb0ef21e9c4539f0165f309d37294c333c910580b37ce0de30a08d888954dc84643ff3a3cb5c7a21fb08f0c43327883cb5598cdf6097a3c27ba88ae628a4ab01a7d59f415836d89d387ac053a01f79aaa4dcaf087b04b2f00a15ac96e8b9d15f53ab622bb74208b9dd7f0039f7dc31a99b87240b79196cae30361d4dce855619b6562406625c61afb438100bf37fb7b81e5d3a183a7d771ed60965d07ec99279b3d12303a07602e35660f0ec7f7796f137cf1f9872ab87778ab0d88b07b33db89a588977deb12192af3c6039bc76bd8cd5024998aac8c65f8e8858efa593f383584b5439ab3b7dccaf8d80cc86d3bd983df0aef8ae3e6e2a869c3830a8567a023e83542756de5ed07d7b7027afd08f7e07436d1da3d636335893a740e19dce734c849bf2740c940cd30a1099c41580165ddf2917151987a93b45d2560da8a877894ec2c11bb60562c3bd6067a3f9f7e00acd306e11ca205fbe992f52861de8e5220ae3e40f3c512308c3306fc047a2d6ee111e4ad971ef3f4614c6023c53a40424d45f85f6ea5e3a57d6201609296e12e72c2ae4c1e2248ed0d617e83d634f987bb563daf56c3d4f1adba09b537d09c05e25ed1e789f1b82b0f5f453ec73451b586228453cae0e3ef26ba0b54ce908e3c529c9b66696a2e8c051fe2b8e05337c91a3dce7c287ef151a34a011ad3c7d6771c2087d8ab3d798dcf98cf778b765362a7cedfc25029df27df140242f60830b72017402bd587906f2200403c66a5c435b2b3cd31a6b7a4515d71089c99ad4f3564103681a6a5bba6d2d6218d2943f5abf63ee9edb3822ade9a50056b876391e3c7a0ae0d2e184741c4f8aed6be1720c020ac03083696938428670ee4b3cfbd98ff3ea1ceca1830483bb9f8c816e831c8019cbb44583f0ceb97560b66546c2563531b4a665eb8eeb77df94d12f85a13f09b370260c5ba1e7e14f404a8046ff9c4720df0306c0209070678567e4dc1d3fc46f8cec6fdf62b0d2f6d01a7d1ea08a990c85af541531a7ccaf66e9f84cf176e9da7cfa0d39e9f040b800f950fa177470c67dc36386a87de68c7a93fe68497d33b96fbaa2edeff4c190b06685ce681e3bf85ac06e812a32d05995ab7dfaba0bf8d3db3271d5a803210660dc22cdaab28b52af7e940d57eb44e2dee5d1f3e99b14ea152b1171963b8b402080c3c6bda09d6fcbdb3e63fd14b3d617b75349797a4b2eba3c867bff10b4f7c0443473afe4e48ad32b18be9adab1d3389706d9aef5345c410e03bb6379da9da0aa82e867768249daae34465d52511e12bef57c645550edd184ac184e28af9e50d4a8846c3e04749cbdbbef3b1e0b1db8afed53518a8a32b7356785d362c4faa05b7b687318a144356631d147f2269026a674674755892c1a6f4702f785c803f083695e00aeebdc0a6ceeb9b7d3b9bf7d037243ca9a334117d8db84d7421868c06b7833fc09541806bf6a697c2ee71532e37d173c6354b8282588eb45a6dfb0f05603effaa64790e25466f7be457444c743b2ba9377f0a4d59da5097896057a9060ba07ebdc407af8cdde39ce57759a2293941fe490180007d176bd07374e42f00f0ccc238fb5e784c10da87fa827e6530025086109cd9ca992ea2bbc6d6e89d03f6ba7822f76e633a817fccac171c85c242b145a2ae9b6d013cb86c63199b1408d47d7cfbe696e626bfd50d206e468f030334701a7f613172781e5f28ac113a04b31604a002cfefc55a1d404fd3c0c093a4e6f40735666e6f08fc726f4cd5510b1950e916353dd175b2bef3c5c8386fd76fd35efdc324c49bc6e5eabcb8f8f40e9313c773f3eebef868d299e359ee37a34867844bf2bc9d570218b1650147110694ee6a00c836f88e700639bb7206b750fa1b5c3a2b338091dd80a1ed8bb0990e456af02fbf3f4fe8770e332a58d2391f9b2d8d20cff3112068e7e296cf28b60fcaaf9a8815463a3b850c0dc39a5f4525ebe7d9fd2f074cd52a7cefd0ba5b320d444197d954ab37139be51f9f1b7a2b144947f6c962fe93f172a9d72fe09c610e3722d6441970c06807e0121e429ee02c56ccbbd9c4adc8bae0360e7252189f0dd2a821b2ca912e98638ef72208e7a07b3ede6cc667d3d57feba3e1b09ef30e0df25cbf1b1f3b6739a646835dfe1ae3670a729276b4c642d78fda5425b7a1c00c613049e062b2e240441922167c2c508cbb6aabdaafd56cf55b3d5a0e8f8ce40309f05abae057437be9ac9bfae514e80962498ab3fb2a93323cea74b46522bb0dbe2473c439ef5ce22ab62da87758b68034b4ed439d913ed0fd18dd3db0c9be061844186285fe379ba04e7279626edece48de015a893b406ce3d4accc322a21008f8a51f47d2f41661d4514e2713bd09c9feaa6acb927bb0234062cdb7e2c360875598ca11bb534844525299f34b195bc605fd17e4793b275f40d44fc2a791d0786327c2233ae1c424d9e398b0aff38c9972e586817adc5f6da1ebf6443e9e90644e3244166e4157766b53cccce33628e2c2f7854fa797555b637f409dab5ca01106c32ffc7adb7661aa90d2fa2552f096c02b3e0bd4d25c50c12d2c3e692120153440ddb350b63da97d232e481cdb261dd221e1b34983e47642ce0e09b421703eae5fb3d98a568212459460a20b41c46c1e9fddb8b717a6313b71b160ef29e09a09188857819674c8a1a1129d4fb85ce7aaba0f0b13fde1f86e0646eaa73cf0b56abd69ccb916e213267dc39457c1624c32f3f12d3accf12ea20190a76220604a11786d96938eddd91def76fdbb3653c0746c4272a1b13e2c757c9c881d44c041b0ddd14f6ec03fe20ccc359a48b23146826bf8fcec9e307e3677a502677d008c6eb513e616180e4851b54bb25eadc5c75e97ea00bd781d6c3cb2196e7784405d7b31821039fb740809e9c9bece1d3f457ce2016b334a487c4ac2134ae08a70af82a4b6e465e46529ceffdd7512e2dd5d4194c0d75a92ffc32e8bd71158fc104e80d3d9207482056f78189834fdbfb5a755da64681505bd62b129783e5b83a07607b3f37fd1aa381acb46994e428fe88d4f471b2ae0f3555cc5c55f835f4860698a5b6c5435c1e8d8cdee4a5d11524f5b0ba17e03ea6fb5722b13cadb1e4db0c0185a419c06250cdab9d2dd385247f8fd4e05d29c0b9e2765350d9a570f38105913f3669336956d614ac665b9ee8171f64c6f282e34c2e6ea0d36f6045fcd108c64cfd6e0c8c0872a54d768bacfeb1171be59297a9455dd70b16cad701312c0127ceed9f5fa973dbe29acad600f7c1bbaff00772b49e043184a41f062a64a3096ca5a81575dfe8bdb579994fe1098dfda540bd023a285204755b7247aea7d20746922b6e1246360919b3d1f29ccad541dab242cf1255f39de2e5ebc513fa650adbaf7b4914e4b42ebfb7fb844416fe97e0b9f25f5e5420ed8f1bd28224b5c806355eeb230a177d7596a2c47276fbf324d31b83898ec15bcf3fd0a1eb5912e307ba6cf07536dbf0e065b346bef7a55715205fe0aa23636b6ab5ea3885db851b0a796a5bfe7382e3d1815e62d4016ba4de835efb27ae776d599acddac6f5f8880028ef7659d2fe7b58dc6f965229e1a3efdf77198108f5e683d46cc8686ede87006c9a33a5d6b224d466a08201c21feac7f47181fcc202bd3f04f5373c04c7090259a5c7733ab12f18dee71deb7d23f4e63fbe3c031e2e4aec53ef0ae77dac4b0bf99b4e1ef9a7484e9e4b1a6ab81a7b38b439c0bf281c3a3e7ec5575fde79860d14db7fd54e480517ee8512961b3d880445c3a716e0c30bb294d73e896b4c31041c61a64f54ee10d836c7aac2e4b867f0fc27ee7126ae8e9f36185bc2cf15290cca790344cce961da259d5b002c9bdc05cb4329474fce6562f388520900836c0a48b0029bb2e588ffd2ef36cce3af5959c114e9fd84cd87858cdbcd2b23055e093ac4bd7d91298eda8229fe12557779c2c5e3e44d54f3783b230fda63bf110f0f3fca8c46f9cd0f5b41897dbb69b8a1888d39ba5a51395f6ab5a777684c195b09274a320bab1f3a815f4fe9c69fef2ad8928cb331e31674355804e704d37f9f014bd41ae6683e34ca461051756fbcb95683a3a576b810b62df948f7dcd9d2430bfde3db782a54888adb97c1ce2ff8ed02ba445d6f15a192945223dcf030899e094eb55bd60b57740b518fb90308c94fe175aa95bc9d08703c322d550db2ebc404ce0e64d6d322740dfdb3dc5d89575b6bce568feb831d7165197cf362b9b96e0ec439b3a25b41b8b72a9183082a271fdccc73eda54aa10a890347141f3e15350e9b3981e5d32d1d5972d5a4167234cc9ec550dfee2f8a482e89d8c187340cc10dad93eaf8738c5d8cc274fe6475d1693345f1195b3503a9272cd287ddfbe0560b642905461b511e880902807e3383589aa771ed184c0e8ce80a68913a6788350ac836fb1f6573ea95a2d2be004b746bbbec7252ba2075aa718d872a7d4079c205dcbe7fda3340d096d9a6ffae34bc41de564bd3f10529580845c06b47a4663408482406f6a604372e0f1068c4e5a40958168267e7d9d060cce112dc4f59b4b80d192904c6c172fbd1ba25f425410b117c26fe64247fdecf6d0a5f7a3948477f05a0115bbb0576e646185040d4ad294e1e8a011ef689b7da8528fbe4f1273cc20ee93d1badbe75ba736bd2b7357492b87920efa048e76100f9ab5acb8b42b84e0606d8a6589cd34fef34f7163fdac072f97814e89b74929b0b057473d9a8774004aaa55ff0e192fb9e3a387a4c6faa0a83c31cdf071e104af9ee56d63022af550e78e4e9646f1f174243395b470a77d9f3571dc402e3e48cbf6f6a69c3019b520c3a24b7d8783ddf2cb90ef1f462f6f83548ba9e77e6a4b88c520e683a96a2e304f1419c3e77236030716c4b61c6e74a150201a04f7f47c290afb9db46140709098c2323d2d6a3065b7c50939b2a33052d78077792e22a9433f283550d97441e0c2cdf25100e60df2ae2ea222525126e0d0a72ac27b77cb57f9513a10b6c49d7094a2c8b9c48ce83a7f3f8c0cd821b92fd280974b6fef4846577776569d2b9140e3d09f2a2d1ac1afbafab1b403d7ea36b54dbd2bb53ce1d5eeb172a96d467d203f805d4d94cc7055b26a4087600fdc0d2a69da3cbaa1bca22677f6eaac9c0c6098a2edb3d1aa3106126252cc0bf04012e8db7948fb9b7b5549bca332d136d5e09cb072606e28f13cc9197acedcd2d53ae0b50c5377585e0378bfbad53f8583402aad0b56f3a843b195c96d6650bce6c6c09e696169a43264c4223673e390c820be7364bcc0f07052ab429c4a78ce183282af1c635c52af99e7798cff733ca3e06123ac268220373730d9d0649c8d0ff66da4088cb90818ccf13bb86bc6d9091076f5227eb89787537e2803689616530c6877ca8a6d3b4bba619ae2d0915220c0b0684c93ebfb724b25720d9eecfb6c23dd366d5f4afaed0f8ed3d7eae3075840bf9eaf906363f562324b5d905a2c205a19b1f31f22b4879210e397c0461c5420827187026ead37b988b5dbd5a18ccd0e52e011eae4fd583e47c97b9583d252e08c073109b0b77fd5dacfb253c19b7b97d31968e97552affcf87dbef38f220150dc180418a08617afd9b690220d2f96d95ac8062bcd39e0ef31c3634589fa0260350d3f9f2cee97eb643c36e54e1de1ce12fe1f0d7c34ec8f4f9b9bd1e7aa120010324218c45960bc89c1e720a39076cbfb532e48c5522558e70a2f1dc8fcfa8006e065f5874856bcdc985bb81efc844a225c5173daa987314f6347454dc42bd00b0f5af9d9634e595b327327b205db9315a20100b3fbb165ffaef0c456f183f0cf0cf43fd6491697ecd1c6497b143c6c46122f43c9b44aa5cc14213f5bb87e6055b13181e0f23f907ac2d76c5ece1746781884468cd9373ab2c8d809b5367060eea77dbe7fd46ec936c8461aa8fae2ff350828580d54fa3b211ba18f3943fa00770142655a3e9c8b286a9f27c384020f5b97218f5b1549c43280d63e646a86e0c57abe614b1586c7fa12f347fe122986c62c14ee06bdba173bd1da3ff26321e0630e5a7ac6e4b6e4b4921249c194f7eb5a3320bf987b1b0614868a389efd5120dbf2262f4199deb5650fa777ea3f79361799eab0471998cfbc2cd3e1e033e8c0a3779a714b009a7d8b0a2e69896762c8e75f23fbccf43795979af68f21587430cc83112afd6740e07d2a6ad8ec2037cd841de4d84c977218cdda177366ad2a1021264172c8997011674239633939a46425ebfa1e2204721bebb0c1724bd864406ce7d0a04051f6647e9cd66ef0b243a9b0fb993f78780920c9774eea77b72110637a0e7f7c155e2b9c82e1d672d5786224525d4b7a86500dbfda8bfb1adef8a06fead527e4fb8fd41ea551a95af44b62bd62449ed32e427a201c3ca7f87d0a10d8dd61bfbeac26e910ff795d24c2fa1e27f7f34762d629f7ec67ff169315c4f0ebe82540d3758005d64a04101323792c8b2e135944bbe13f8340fff45093df004f2ebffb7e69231411ced16c887aa0e9368fe9c8e6e9392d0685783c9e25e790bdafad1ecb3a558a487e1c03574966df29a417561d50f7596cb7c76723d84e30f2e9229c12152afd4690242c447a147efdbeb9de3bec0f7436bb13ddd3f30c10bc3b610de2c5bb248a18709689fd419815edff43993e9ea9e7ae96b0e9af7f20f48be2255153f63043414635bea30e3605c93cbaf38d6b35e5b1c0a5ebccc9c0bb34c35d28892fffa07a0cb34a9728e485912d7aa1f742c83ac0ae68052dfbb0cd1259979a76af7a528efdd757c3918d83b100cfb9ca9c6564e4779790291430dbd0edc262521fe9645f0d8c0168e1a2a07ec99bb4aed96d90df6e34b26448e022bd37511bbd29474d1ba671de2374f67647d82bc5a746f3291222c452f9acf0bb8608eb8d6100670e8b48ef102ffbf0f7145ade124b94a3f78ced969bcbcc80edf7653aef7f5ebdcbc3203aef91f32c2bd760399674ec8249e1109553bed6d009fd52c42e3a4dea2653b790dd9b5a9d6d67c632ba9f3a7ca0102b0fde874d00e7216cfa18f7d335e0bdbaeb0b600b4a03c697515e36856aaed98d8620ec6af0ab3a8a576631711eeb21ce1ac2dd27988286cd127dc85f15ac0a5b1cb1bfa940069b845a9cc2349bfc175cdc089a44cf66b3ad077979cf8e2a55eea4a064f7f034a21dfe7994768de1b6e354cb34a6f7c8085d1b53b375adb53be78f332298d047362dd9409b156cdcd66b6f163cb2ebf3b655524c5b075479457ec72d7f23a099f43b59885d190cbf40d068ffc4f7b11b3b40954b79b774e6b6d43fe8bb1c501ab15e37c41cc51368f6c9210edfe703e7cd75cd5dbd02773cff3ba27efd3f90b637480a2abc94b2ab2b8ae2d9fe598a30ed98a88d72f4b7d47bf726d4b4677032712d8c49c0d420a0de5495830465cfd0e0f6fd70d1da77272acf33fa97d780ab016c5a7ae5496f38cae3714628dbb5fb544507b2134dc6488cacededfd65309a67b54012237e289efd5a75b45224defae66051bc68656d58f4358c7136b7307de49764355a7fcb9b362e0176c368f0342ed20df9d659931bf2dbba8c62ed30f4cf94ef762456bad80c36e869a41e2d5db0adb339b626dd63f98d7442aee7604f9dde071f99fefa169c4cf97f83ac890039db3913bac39a7590ca8b45563d605133170dc6bc509e1822bf1d112c004cbced613d4140df088a03497ce42360202734648187482fa1ebeeec969e9d3389d95246628613ecc78e0ab7829a02db20a744d9aba03987c270199c4b39261aadf0bae6ac3e705ec2ecf1a41bb5cf018046bae7c0cdfb38bd712fe84ba67d29ef67c51cb91ea9b71be5571b3c4b37c8503f2c907e7acc5fb434b6f43e4cc1c7b54b485618a18686323f04ce86e6f634303c0fc45383235203f1af6bb3db16811961b686eeb5285d06324d2fffe1e9cea04e4a7df87abd292a4fbef08efbb408f61b4b64da8dbb0e5409c8a87beecd1bb0737117ebb49ddabcaea050b2f38a445d39c7f0c549667ec73677752a59caf800d534c616793a2ec6005b04d5b3378bdfc17826e611011acb3338820e7fc8b5f06b053415ad48ff38178192191d5bd63eab914fa91a061ba62c8ccac1816aa71021b81c3217898f1db03226a4a86f409335de23be94405ce9a457ca0852d2949042fee3926aee07daa63090970a4535e380a36583b3306fedf9448f216e5454a0a476701f249d13436df0359f2849b079e795e45786c68cc65568a99d83bbb240e58c681491f9b1809db6cf1add0d42da8e340f267ca4f3d33947aca61b5efcb004ca1f07be84cb1396b08a1de01e9891600b081911ac3325b488935ded6cb230bbb39b592a1a9745c84d7e82b40b55b853ff3fa57e14a9972ca87069a5f79880ec4956d3faa8120988891ea090a350d3798af0252500e74cf8a11ef16a07f34094b72a61c4e99351e9effc160690923f132638b0519c5e4a88e87f7a9e13d9602159893a7eef6d70749ac01927220a4c8e32b23e69c5d707d34e8f451bc472009e9bf6e88f5b3416970d852b7d6a120f213b1e127df326b550f59e82b3fad4405687caed18339f9c89f4c2c16ca06b7a16dc153bb8330d03fbede53932720440430b19852692041e29cc11c7315011ab4b8717523a01a28469ad1569c93a81f0bf839e763c58163b5e6d11b106a9b3c59724271bd6405b6ee010b53138c7bd502d2cada403f517679459e0c2ac8b7fe91462e692b5f974b6f4ee8c88874613b0d08d4d59cfd691bc7cbc01adeeef2dc6087537186bf1c3af6765b0eb7c187d00714d5094d41bb0439e5be29a65f4aafc86318b7a37992aa350826746b22204b03a4356f4e8a6481b050d335dc013424d18b7e20a4a98880571fb6df2087c25d0a7e4e5c1807f7505a93dc262c198fffb37942b3f5c87c009b88f80a7b4c05670e9d0fefac4dcc9d518be2cf8984b71bb080452e25cfd76bf50b4abb3b79eb2102273f1585653dd7285bd78f617201789d8413a489872ee69f300762ab8bd00b07006640468a4ceb8e2851ac454f21aced1c228abd6cb61f309ab49215ac045c04709d05441c6348b63769599446d4a4bc19e7c14dd66d34c26b11ee56f9ee85092bda7f61285c409689ffd5840dad1a4e7d17eb372da9262ef044f945da873e02224b0ca526e3ca3871dace9acf47c5106a0d34ba0b19de90019fdf77d31ca7011faa342dd97a52368b69b599ee01893b0c5e0a5b88f6bc2084c3131c7856c9097cb8f911a3ac378abdd1613598682dcf645b9103e662cd3bd19722fc745dbe0909e7fde767b2521ea6c050d1456ee64b595c7b19b686990e43809a42e06a3d039b9dc86d3ed097660d6e6defdd5b3f9adc7d944fb98877c9d4731ad7cdbf91033ab95ec60965a3129230ed1d9e83efb1c23613fd9c77810fc56488e44882820a0198d139df642bfa130c49b70527e61616f81ac3ba8eac985b8cee53b0757c01f4feb50badbf705c12d1a6cf867d64cb8b774858ba3af521f409d1ceed81a903b49f53f1feef4dfa48da196ab8cd3c91d8bf8a7e0f6fffc499865e6dcbbbdf0ead2ee814068825f34716476944dc5a4b5d9bcb29267097aa0326586139241904fc3705be60d4025110c6fc4f65099c703b7e14924e5fa953d70c1e1e8af87b019d15dd57d6df80a780ff03140a1a8f15284ec999be2f2f89f0783709e824a106fe03626bea7b1ba47965b744a2f1c79f5fb4ba2934571ef3082a8abc94067e0f5ab14e17f136b2c2626f3525c261d0683933e2d81c2fbab3bda5fd581ccb090e3b1b5109f26ab39915b387c6d6a6b7d59d4a205e12bd1afc56c407cdb9b0b00d20df5ef1380b26881c0908732d1807636cf7993db5171ca3299a5ece5e1c110f79753d705f8c68a2ce193068a513211766c6e891eadd66d4a3cb6c91717e4901f777c43fcd6cda630b8fe97a22efaeb4c4e23a5ef1eba9011745c944ea0ee904114e4ba7adc8ef30cc678f195d831e1638893c12e132fb19ceef5f5a5541c601c31c7cac15a28925848dd1ae52f2e5f356f293ace86728a3ceb538f6707647076d65e4713e902ef1b304c3696193e78b519b8b2b47f32c8d9c02727e7c762806882b16e2dc0083974211a93d7c1a9281d21bcd47e3028132063087965aab210ceeae0e00022f9f0d0583c028f8d34a6de2edca59b99e775bed24b92891382a03065e2f4335568db12ec0ea9b7827e6f32fc10a3c689c8b4d2cb87f2bc5b160004d37a4db8598e86c0c2821de598a1a7cbbf2510988c70c7c4b96bdb8e0e4450d5422e41e8fb5be1ade15cafedd7d64c2e059f76645cf8dc9dfc0cef154b11205a5bf824259d226c9aa553794775257563f729d4e31d00ecf17a241ad604a410e7e8d4669b3d8ce38f7e9ce832910bfaea87fc207ceb4b03e2c5d522ee76ba007 true +check_ring_signature 89001d945019ea8db33d91e84952f16b4bba28d1fd4d89fcfb8c629e41fbda01 1c91178dfc873f8d180056387acf42392661719c502a432723ea1e80d3404948 32 a3ace22713bcf72d0b1df012dd26b2949cdbbc100800ed82c7c2c730017b5682 86e38338a99059821f147517ffb0e350950468003b0d1e9dd088bce66029d543 fda96ffc960ff87ddcd84a65ccd74f229f2dfadf4e72e9269de3d0bfd7a4fe81 74b87223744891c4be2ef91c691e09abf7ea5fa81b7540b1b5cc728522470993 1ce87818ce0dd2c639fb957f2058ceaf89927ee10162fa7a1f95b3d808dcd679 e9a3f1696dfd12d7638d198cb78f9407eb17c695db634fc641b4348b3bd46e55 7fe7759f50bf518264418aa8c9f4f6aecd17e5269fa0a0ad9880b3f867c1e1bc db3feaad27edcfec805e7eb6b398837ae697e9e9e282bd0d428a8fd0f4b13fbd 90705a91301b20e9c2e83e725b8c35b5181b6dfc8f99615a0bdf8457ab85021f 1560897a515c1f70cdf3b9ebca1d59df9b7f7f54de8b64c5f41f941356589075 f53f05e1b899b423a557230371783862187c3d6ac13ab0cd4df5764788ff0f5b a8e66f908fa0cb706edc77cf7b16eab0e21a0285295eca9fe8b72748a18b907c e646da4c74f7cd57449b292b352665199654841c80f259c0632dfcae6b6eb6ec d824b0adb584e92065e0bf375fc756f3540a61f952a6ef86f21e78cb662b3e59 259f96d0c582b25cebd0298e3c1208eac0473d7ed7c2461099a7fb9248a671c7 c5266cc6f658bc39683e50c0683721a2715f3c28a02f35db785de801bf60777f 1f3e916dfef5655e74a04d25edc634a99d7261f87ba536c7b32591f21ee9ba4d 9e67bbe2adec8daaa4fae7c31869780b7a0e4d098b1257435b64f6f297986576 d42d96b9d0afd0c52b6208a650773d0951166dbd5b21cf6ec1cc1723c37b0886 8a591b5dcd1f3703965532df4b532b56ab0f0d11f74f007d1221cdd6a11f613c 926821c69c340f404c1d615b9e6a38b7dd566bf414867f89152f027c9633006b c34d58e095174582a7d2bd56ecdc71ccd8441e0075961b239c07c663df0a6a25 43eedc8316f456def801b3a0d78aa22a197cc81260d3593b677a0ce80da8809d e209003cfea2b45cf898cb903253796d3e386a623d874b3abb9476e374c60a11 42f498e7b0fe8105570cb8b5f582b4dce583a71c98628fdc6be57f70b513b1c0 92c7e50f2411f88d7acef3001fb4a66bbf2ac7a944f92d553dbf410a162c3a1d 470f36b599849d92f6d83f157bb4fd248e0e0a977b6bd055e9b6c2a922f5dc2a d523b14e40428dd699ea0056859268df49d4ba373dff8053dc3291ba2a680ab6 a535edc27e8c8a75b009280c95d434b11d539bff57bdc3288822cb17fed3d864 2d0eff1131edc4e91d7287a8fc37896e412d9a2ca22d5a473ee81826a58ee91d 5ed510112dfdc80be99aa79d1601882a960b5e61f1375236a8880a2200ba7e6e 9cbc68ad89b6f24b03873ec1110018ff58148921627f17abb5ffeae16cfcd3b9 8665dce61593f1d5e93d54d7181bb8a8d115dbf18148403c82eb9bc47727e109f2978b09d40e30a6ebae4049e7ea3fa91cf99ead626abb7e1012e11cd02f860c9ac89f47552d589ddfa57f425285e15d9b1a8e7290fc93aebdb18d4070a28f058b12ed443891788981c03b7e32c8eeb3dfa1daf914f4c45bb475400fc4579c077e35f798acf06cbba5025a845fc941c5e4964f410ae032f60b8686b964ed090bb93801f5140f497ee385084f4e6f27bd279cf22c8def7c1c952da711f51eb809ab0b8e76e542bf3b85f2c8d37ed96ff14113b709bb1f740a263dab0070a57005b164d7e4fe126b228b5603de7bc02984c4dd071a53ac57dbab98d27584e6150621264c7bec716613e34df2c1bc9e8b16bcf6f5214e603f8a662918d639125605845c17acebaf4a0ceb1f9ef2641b373e92550cf18b4a16514198a363df1d4504975f537c50cf4baa677d5177cbc6b8657106b3c49db190b9cc05576f85a5f5004cbd32f9262b5a7112d57d2b4964b3e80880907b97fcc2661b754be2f1273509524fae24b6da3bf83fe9bb71b316bc159dcd5bf03e71b025a0c26f6fb210180649a3a49051e5599b42073fd64cd8dd14cbe336346897f43774bb0984ae57870a55426f64b9037fb14c9c7cdc93855c21cfcb1b52f53726f00115ceb45a8f5c073e9bd90568d932ff6b34456a8f376d5270bb6147373e6503695b681abbc0700271c279c1267278a788b3e020b3cf07d8a7f49784b9a6f15adf4a131765ca04067448f87b1aebdfec2946f9c18c8956486787bb6c6b0b8be5d330b8e34fb46e035377c5a056fa2ef594651ccdc35ecec32a24d292f3089e0427dc9d92f50ae20285b879ffabef8d553fb6fc67b21058f5a770c04c26d7d7e6e4716605dc13aa015a9a907a820438327b1fb28b1ae0431d69c66e5f7c0f9f9c8c289a2a39bcf0084d29c10b47e9580ad0a40ac746489a811e8715b7becebb955cf9c0d9c6aad20370463c80d9c05933ee922c13a262cd20f68cada4c00f54ac021c0fc4bca58d0b11d642cfa4705a040430a7844e78f422b5ba0626a160f2b81a0a1415466de803067e5f5dd72428c09a80f0164fc9898183c2c1dc6b96f62a0f0c6e9301c23005abc5b518e0718d8ad1ce4a99f9392362d7c1f2e696947b1c176ca5cee21ae0034644f2e81a722e912974294682c500b56e50bf783792b99d476e928e0ba2cc095e6e69367aaec18f0cae09b90dde55cdd9552f47c3081a69042f87f323841404a04632eb1a4b11ef0562cab588e48e4a7cb486d40483b2a87f287dbbd314290387962df352d043cb95d7c323c1f77998c046c8440ea4ae3e792d025aa669ac00cbe67e29ebf7ec127c792f933434aa2fc9e4cb67aed057a643ea18a577a5540b091cfbd88a4666798a320413b098864c2534c4f8ecd315e2a626eb950edf9d02abb778b7ddfe62b9e6b364620d8f8e821bc67e3000129a4ed87f789236a893039e99daf6483c7d9c438686ee78071fe1f313f1e0d6c33cba11a18749d2cffa033218d4dff23f9d9f8c62e043a16cbaf843570bd9d741c4ddb96f79959acb4609e8180400df1fe5d65e44a760f88340f2eb4f439f0b4fc67426f5b0612fdb1908743100edbcd771f8934de78d7e970ff555ab61ba9f35a9c34c500510fb90150d4c964477013515a93de7d80af59a706d4e511ef83c17b1e22d9ecfd3f81d140522771694ff7fb03dcad709039ba46a7d77ebdaab6dfe70e24ad1df96cb7d020edfe7801b5187d077543282efa700ca3602c0af103a082a2c5ba3f41c779a490b373dcb51e9495acca06f11f16ca9c3d7e217ad42b8fe3f862ab08f70b8bf9c08dd32406e525849a229fcfe9e8f1cdb50d8fe374fc6613df5ad1a765aee05ae0e4871fd2fe8e734689e91908ffd642b846a1dfecbae4dcac3e7bb7fb944936008009521172aa05a6a19d97920f2cad245b21bf6139f2bab94a567f49e5b102508f0d41b5489225b55b7b00a627c29d6df041995698f64d8b06c8667fc117f0606640408994d77bfc71c1011fa6f0cf0661d53f5cff47938cd18d91da652a3d2054e5c9e3fabbfd93775b9010ae3011a4849da6b39d082fe44b2289b90ffc8d30937ca43a2f812e638731cfb8a0a3c45a5f8f77f3dee47e99e38a1e1dbe3e3170b3d36bf179a673c4947228b56ea3f9fc75f032c7e48dd70a2e805c85db22d7f0a15c3a253c5615879691483190f814ae4ef13980ff588d2e60b1a0bec5e6c0405d747262422bbe7892e0b8fa83476dbbc6ad2da35b52b8ccffc1f6ea06cd58202390697bd3d266ebb9f12b20e2477772770bde3cf2d285b91c4a7325bfc022201a29419c22acda1ae703a38d6f7d98e8f0213ea410e955c6e1d0772e1f569fa00bd45dc34fb9b8b34b2b6a7de5cf85bffee2ee8abf8e0250a1901923a11191c041237cddd43d00d9e4e9f2e66063fa33203218afdec0e68ab122875daac0ec8044b2591a6bcd647d2684185d31aed39ae2df8de29a4d9b8919d2d4fcfc76d3f04c10c6aa83ad3c2163084ca44dbd3e4d81b4c2801e8467898e1efa68f5059b6059614c2b654c14c901fd76f65e821110cc5f81d2f1eed3b577327cfc6ca49d80ff5e9874a7a747504ec7bd872f65ff6441091c91446808a046164662fa43a7d06fc1359a98c86024e107b5c7359fb525534c0757650c3d904be4cb6f9f53cfe00b4f023327ceab40beb32778ee52266d79863e317aade8ff126c7c983cd6a460d6601a5ecaff73897716ae77f2a3cdd759c5c8d75f24d5ec16331facd7e67fe03b5e044dfbf6a53fc16d3321a2da1da9de8f5f7bb63c5b18a3af218239f70ff07ed34710c30a8040666a92537532b9055989e4a6cedeab7efd023d7ddec440c0c false +check_ring_signature c6b72e847c7b9317f4bd7244edbe7bf542e835a04e95b1b92986adaee9218123 f4e541b0f4a992fe3e14de4326b7338542a667503eefa88eb34d0be32d2d6d73 8 53bcd3d8bdaf3ab4fe1d0feca8e6a08d83a46b585aef847a06adeefda001f855 9aaf43648438805c4ca98d27a697ae9eef6480db467bfa47b9084574e060a9df 1d1b744d6669fb4418463198b76a155858dd5f0f23e67572460f4c12786db423 f988883bcf41a9c33d28acf8d61b45360ed91577ea24d035db6b960ea709e133 a17cb5e924c05c9a8687251a101726523a00376cb1fb8e192070b6df998ae80d c590d3038e03afe477012bfcac43f2464c472f980803319ad58298b22c2f09ef f25954351bef22ac253a0725f01cce4b233d75a5b5a20ee1a051cb5b90bd12ac 11663738790a79d3156d852d3c8601eb1ad0562758d2226bb5f6925ad2a416c9 15be12e11834ec63665b162f689e747aa0d068d21841d4f6e3714389fad9c200bb4a5401966dc7a4b776184a02132db2cf0f1d369f3fae195042b8605acebf048c3fe936a4fcd469ea8804d0bb1e9d0d25e460433bf202ded40c87937e213e0a1aba6553633634844118e0377dde39835acd8005792e893c1fc54aff7d6a1301e7f1d4cb39871b9265c5d7417e6d0eed92924faa7d0677a4c0513ca3ccb3990ba50baff1df26453e840df6e318fb96df92fa3241122acaff37e0e304caa76a04cfad2cd435abf2bcac7007809430397856e87d24a263e45bc0b1b67042e121048d2283af8bb5e67c56dfc4beae20d974f5922b34f6bca5d279c691421aff970df032012bca11b78e3afd5b3c889b83cce9c55f8fefb1468d784c99df3d7534051951689b1f13f3d7c4bf2f9394e03da1723f0b9f8adcde4fd5e2e92fe557ac0ce3bee1bab302b25e8b9b247039ca7cd6c8cb5108ccaaa453a62c0935b27d3c0a3da7a48a01f51c6b5010c478b02cbd8d272f5714293477d94621c11f95f47401340bec21f89b803a6f29e085129cb34fbc9b35901188043f7a9229f38d7c7501096d42bf02d22983a08cca87f89928efa42e34c78f9018fbd33794e2bbec0b052ad2902524beb19ca3562e31c873820200bc6cf2ad616ea989a8176d57f86f00771e469270b395beeb46358a47364d328c707274969b01b502d4609146a25b08 false +check_ring_signature 2a42332a8ce0f83818e9d1a6ba74400287379557c816b31a6d5eac7f98e7b40b 58c274959656899756a461d9e91bd02e892b48fde9956e70a27f63d2885fe3f1 53 0df17157fb95dce2077bb25482f8ada7dda0cd751f35fb73defd6bd0543fd565 46c90d9cc4de2480b8a5412d6881f27e2aec7929f4c02e3602d17b356348f022 3526c909e8ee4865cbc093cb970bb341788cbbf3a56be4437c6aae8b1f08e726 06c58021a0d9a4a269d2448bc734bd49cc92c8d7e4ebb0e0ab45e7603a82e089 aa7a09982387a44b2b355047dbe021622e51d752ca793cb8730441df689230ac cb29b53a1a6ca4ccfc82ef5667342b0192359708dcb6d1c90f2e8390b0cb9be4 dd020a44a527a1806b45aa5e3365807228e5ba0f1c65c4b23130dde89a36b31c 5a4da66a5f124a899b015c48113881f2bb351de95adf7dc068a0d8145790dc4c 48ceb401bddc13dd88569b02863b76425377f07ed106c79800300498a4bd3b44 326676472f8abf172a20211b94e3031cdbf08bc594c0f70f093c63316a1bf869 490b89d49f6c3302679027b1c50b2e6198569071d1295044443211bda2d32dd5 d71d59718a963fbf6127e0953db465d8f56b73140b69f8ab908519d0ea2ca1cb f5b43028749e31ac98fa8e34d7b92d30b69c7ac99f3813bab1befe7796dd79ce 9e980a0f9cfecda8785dcc36f7d5f2776db6b377c5e4f6e78f4cde6028d9c19a db1c31489767ec5c858b013d91049075c5bc23bd4d8e3fb8ca9d3e94fb151e7b f11b94f6322a8de4fa868d89bea34882f57059aa21be25a87d8a131c9c8b9ec9 727a6d4b255df81a9ec7e8e667839e9f7bc0268845d18699f9627c2a0bb196a5 cc7a080d13f6e20e49326876f2349a01a1349d6b43bdbb285878ffdfaa60f2f8 86556e2f19cf1e9498992bef820254b546f770a6f3c199d9a4519d417b8414d5 6409e69231ff9154ff5d4fb99f402359fc51c33cc3e85fb02eaf414804c9ffa0 56545c0bb5811785e4639f36ed6a771c26ea75c3721abb1b9f2500ede546be1b e89a97c36265fd602ed49e9a600be958f1207c16966c07845daacefbf9678912 5cff3b286763ff506d45e7adcf28066492f5e5a427d9ae542caa9a4f3921ca33 955fe6f05f3f9f707cdc1d697ce8dc6658ad617768d309a5d5a20285f8e9c5dc add24cc21f47ac0e9ffb9e5c372d4559df2db6e3218bd0ae6353df34a75fd75d 2065e1e90f6b6f53f30b1fd3dc3bf09de493f68a5842c21d70b423446d884e34 2859a834bf55946cb1c0bc999c7d8e491265897b47c70d34f88af5932a864e56 94e7a26c63b4a9b2f235294d59e5aefddc5340fe00b6fb050e8463b7e1a5512a 9101ad3e2b83d88099b9f208651eef115f31175fc4c2b624f51c16f1a8b23303 bc99fc48e58ae60cd6362c79c356f1bfec31e5e55c5fd398d3991ead727032a8 81abdcedbdc6e3db80605b6b503e01ca5c5a1a545219d0c68d651af80088d02a c62e873637a78e6f8708116225af2334029243d4601a572fb7cb43b820b77303 9a2e9d31be831484fdb40e95a7d6952cbf73c1f9130f4088a1aaa424576874ec 324c02d0278ea0a75abb0c04a1c605ebb34e3871dddc412e47e5f77acdfd6a8e 554e3d195693fa091cf122830df5c867d712688c6799401b1a03d077ba4d9cbb ba5d4548684ca8deaa6705cae2e0cc16683500534710550103a549170d12b47a 22321183e9f529612a8a5e1292c70f87d9f8bd21347122e1fba66308823bdc7d b1e5cb625d70756465e9b912ea2044571120cb669342fb66525f69a18aaf5f5d 4019f9cd48bd7ade6600d33b3ffb3833012bb623dcab3762bcd7cd3bfea81362 6d71647a4d511d5c75008512d6eb51cb115a5bffadafc34c861000896517dd9f a00d681608e58d0797d6889be788afa68257157bdaa5e7c4df6e0d8b1c849950 b4ca52d2dcff705562e3bd37e4abb9c826814de9141fac27e51dd6486e989360 0189125a6dc1ae6a6e07a67d22489d4bfd4529346adaa9a1d0212dbd37dd8ee6 ffded77cc7078d5ab5fd09172a011e33f21e7180a8d5eef719d71103f4c19122 e76662e32ba98816e1ea817b062b52931bf5e9876f1a8a944b5f3126a9e53c69 56b4c81d2ec26668bc1d0d195484cf3bce74b6c618d344b39670eed441367739 271374bdc031cdc7168352ba91650e8045f64ada4c175ce5dee378cdd7b8fec2 67e93ea7bed6c0878c724611b459b2751ee53f251f05a57d5976802fad65d0c8 12261a87f7c6431428282ce9c8665bed58cb485ff1c579d3b8ea53a510ad0f87 d67bb133c3fcd87c2dbfc780c804de93b462572c6e8406ca5efbcba82acc2434 26a1262366d4fb8eebd35bb152f97a8943daf84fdb2fddc0a740380b2d4450b5 e2d4d3b64156675a83479a04e6894946dd965b8a62508aa5c44b98a8259ae789 619ffa97c3f083a3e1c8145f4a72a5ba3c695e147848203c50864b2b4cdca4c3 4d45857227c60de1845cdb610fcb342ad5c6e5e26b0b0f9aa11937ef4b4c880d00107b95af4d7d087b2d9d609e6246caa90d01cbdd53d628f2e07b44424cdd0c8befe0d01977242fbb62aee4fbe1739f808b40f1162eb17bef9fa14f7c9f7e0dc0e183d9fb2d889610314c554abd520aef087a08ef1c73c016e06bdf197cb000cd16fea57aa03915d6333f1a62956b070d1226caa48304e50f61da282e60ab080820c1522f6e8f41b66d42a6e982b8c591feebe46b2d7f28208d8ee450ca2509da0c831e56a4bd575a14436122dbdf27be66a939bf23fba394ea17adb02c9007e0a2231a6a94f665e9744fc7c14d4a05aa7b9bfe56d8a93f11879d09f078480b3bb26b12ec90047f5266755cb2ee858dac2f9bec34480d491eb493dc74f477004c6ddd926cacc3e7bfe24338de4b440f9894fc5386265963d5ea17e372c04c0c0f598e8cc1f287c26df639a113d53e3a9fdb14526de89720a01286c1aaeeb40492b8dd385128e2b9fac573a9d85f62678921e542528fbc7ce0c9d18c70851f0eaf97678ef910d14547165938fe3957e0de5f5862f641d13749656524fc6ccf0b12ff8291fb3f258eaa097b3aed69deeba9a34bf7b3a11637e95f6369c534ec0a06058d522fccb63a89308795774eaa0eb109b936e55660b33c67a701b89869002d0ba0168bf2bdfb61054ccb4e078d156e81ad532ac0ae76de6fafe4b51b6a05d97682ec58fd902cd92d0759e8ede8fb801e8b124b64bad8f164e2e55279230329157c5618d675d7efa9498d24c15122dc67cef7f425e09d54200dc2a9e24f0912a02752f7f77d4a714b2a5a3211c0aad39ca459fad6a746c6e27a34db558c00efe91c4f0eddcd544a374c00b5965254b9fa948059cc366bde085be769baac01cd23e70a5d62334e86e9984df44e4751c3ef37cc0ea71bbd8f92396b15f69e09c2d61dca97e2f391a8fac948200cca4ba6872cd98074291845e53c55f64e5c0a68190ea502fd0945e93fbe2185ec3b75cf1632fa8aa1458982f8ecbad342d70ca1e837614fe1c34edeb41c0ee6a73f535cd3ee99a7eb236fc01999d7db65810585537a04138f0be54ba826a9216ee98c29aa2c845b6baad563413234443ac204f894e1ec484efd04b957bbe2ba388caf28490bdfc75b8920842e3a71d6927c03dd30ed4e56e040327f572ca2bcb617f1e8d0996e1175dbb5835f5919b30b8509f1ec3684914db125c65953971fac8f3fc4c2bba82d60144e9685e61653dc6309c1da221912e3992190f6de6f8213d7a1bec5675635a7a365fe3ea3a62f654004fe00cba2bd737b25e3d5fe7202a06c9be81dc92b66311cf45217ad93313aab04ac9e7de1670f3e5343c5b2a21689c11d4d24f72633cf3caa4078b502c333a60d7a86d3480f6e0fa5b5d8c832c7f918850c1368eb8176e348283962572b66b902e4c34883f386e6148a3009dc371c491295c196174ce012d43dc9314ad8c014072e00dab535a5e77e3e1911add4f96439321929e0e2dff859ef80c6f559c2b00ebc5316c6c1138c861614409c76ec28effdb8dc9ecd3b925a0d5ce34e175ed5011aab284b20f68c26c33bdaec3d7f8d842e7fd697fe166c4eb5342bfbda3c06035173ce27ac51d42815fe95c5e8ae8f0c79e8be661560a431b3f8b2cb4fc433047f3dc1a03809cceccceb1469060d6d0c88aced80238bc738367db7fefa156f0d0563cb441c343f61baec53761c46aaa762708d68e2342da8039e84a9358c8f019554321d9345fe8bf72055589d15adbb3fd9cf5e4cd4778697851cd4ab54e20be976bb0ac5bcf3fdfbe2849e5a3bd65af82623e0ad9eb632b864d3c2c148df027f97ded6a686013bd34a66c81ad7b03f88ab1624adb83eb1ed3aa80e123a1c091f2d1d61a9bee0d055c05a44df15378c43aa42285393635f407a24bccfc31d0dc1fdcc11ebd1ce8e8857e72e648daf56e4db59476ce11cfc96ef2529d0cac90518ad492c96391c52a9ed995132634027c7195d2da32853a511b5e8b69821a80697d79455460aca5b08fa0d390478c0d6de6fcd3ff52a6f69ee3e481fcb295607c1ff16153b3b9f67ae363cb8aff4194146953d4c725a64836d6fb8288af8350675b95fad65668611bdac8d80aaf56e6b2a6d3a20101f3c0dbc5997b5f9e1480770402552b644d98ac6e23ee956f6fd5a67532494d76355500263c470d1de390a674e401147bdedf438ac427d6e0b7f2d04996ba2308e931fa136f3df0512f2095c47fd82ba78df8a3f40edf1a907bb2d44bcc26a803051c063bfa377c36ae30148e0e2d217a27a1c591b340c06c4b93fa6291cec1607ed49b6a5daa0e135cc049a739320c210664bd58db191064ee20b115a219b3e32d46b811815cccf466b0131f28ded926cc4cd7fc7d367c1880f76aaf26db91fbdcf644190f476eca2680f3b2d655669afa2ceb794636ac039b1edc394bca6ad7454e2dbb9a1300749d8055a9d719e512780223108f43780c814768488954f58bb83c9bdb2743f44d24405a1452ffcd8c0a876be89e3ee2c7484c2305ed5fbbfade1c051202494dbc7a00118ea7bf6cc6cd5a138c70533906da6df57a971713746c512a889185fff63c80e95cfe8d643fbd76d3f1d83b33733c6267393fed1a0c0fb0d232b66562c9f270e9a2304169d9118aa7829bd13d6f5a43a593656d9429b47d74759c0792cf6ec0863075c6f5d75a0f11c0418a90ae771d01c2abd4b5e540dddae6f36b88030a101e2aaa4a0445709b1a7bf7bda941d3d21865a9db9f5bb94e87f19659f27e5f800a4fa539d70f9ca53d25f02e1c4f8d5bef97ab1bcf89816e7d8a2799c9b14c3044956ac17d9526f9795e2e4b597545d56a2d8430a64b168afef64ded980ab730b7a419e5229eacaf2f983452b51dba63424b87016adfeb6f9d218596b0b58800f78398c5c3968486fbb0a541822eb6f53fe8ece7c03d477f7886fc047ffb2f604dc5892293925343b1f7fd52269582c026fad3fb430260fda805fd49242359301b9411c3cbc2d78c456ed3772bfa24a801625f1de5dd85f6d7d386fe83aa12a0908fc21a311e6a81553ee9e6007ef955b0c918be854bec38987ac23ff8a7ddf0d1bd8df10be0ea421d51d688dd34bbe3088123c70b2194d904fe131e42081a10c2d36bd096b839ec81dd67ef1bebd25c917a2cf8d38ca6443062321e3d150990a9a866004b9b2f77bdb887c4bfd0df88d86ff923056c8685f76ddc7359dcb2a0a94d2e1a8cc047998e54f7356cd98fef1ae2dcf4738884de25f1bfa2ecad6590a21517527982b80bfc416b4e758933b6d8565ef4f3b139d41b8aef80050e84e0815902165e29fa88db90ae16a9a74d3f742f25616b53112048d34e3717206ae0a9c7165d44cd6887d6d6f01e263e07c9e00d39842210a25336504f3cdb4595d036102c29fea894773f0fadc57914923b226013af976cf120910c683719bd55002acfdd9b7d4024b4aa8643fb6aaab57c773562948b3f6191db632cca11b9d6305528436c5480b61c715d88669b64687425ba68c9f8fcb5cfb64411f617646f50a8c0da6e76a9da90c071eed07f813df8877349d3e1e9a981833cf612ef56b8805cf6c7dc90e6e23021daf6d60881f16a6025b287ad44ca32df64f81cab9b1ee04616ffa7166c77887e3edf1798ec745822455ad6ce5a8a3a2a7032987b51f67071a481d3a6bed82458a85eeb53f153e04b6ba39cd633c3772ab1b5b021597000185f4081d850aa517688458ef732b662df9111838ebf61cb0e47ab10847f1c1080db61486da52e9f1acaf564ac6a3cd1fc0d3cdf8efe0ac7172f3b4e9c9ad530fa0cbce98856ad16e64cbf2b2d73a4f4ace6c46e02ae7cc4bace0615612117003d3b676885b1eab500bacbbeec2265d26355d069c65b68152226e284e4f65f90a2013e1631484857089cdac9f18c6947c7446cb858064617e16dc56e10d0fc2033ef0fbc1fc68487306f1624fd186a9a3c553b77d5475b4f0f3d06f32cf65550d16a0b9676993e7ad43ada0abc61d282d591d4ed3b44413758945a5d16ca7d60681ebcab82746f35980a0edb0eaef3e32be7920ecc2937d7d3f83ab3311e7ce00dab7d61b1af51ac00f6a81378035667326e8a6c24ad4c2d9600fb645e831bb0781ca108a0db8dcab6b64d682092e9ad8f658f2ab44057927f303867090c99c08443033202e1064c13dd466f0818bbe875ee7c48b694ed0530336ed8b11425fb6400e90917005904cbe365941595ae47e871922c9d28996dfb756687b06f57807cd58e4c3694a1a687c463eb6be93b0b44645f21db444d9114c6d913bf39aa70986177b5b5957678cbf1fbbe95413ca283f2a218b1b246f98211dd426fd929f04d6cced42831787f001eed174b3b861ca0a43e639c0932e81d0eb9f646e44b20dcf4922db2e391f272a8314a4ad35508b3c15b69741850e10473de26387d070058e59b8a6b5b2c37e2c5c7981a43e071aa8c0590df08232fcd39fce54610f9b049aa96de927d99b02581b3752d09c906526eac777902e19e06ddf5c4472874806f23d4191bac44af72b13bc569dff2fa89b9fdf499ee910dbaca6c0e0a455370a0a7e3fd8c148d64bb922fb05001a06272e0d52f1cb89e18c065400e5f0b4210788a22a6bd3875264a81e0581f957e0699addc170eb16f3f09553da97e3a0c3067fe6bda90e20b14507b063116ef06253a9f8a3bf15bb7b649b64ea607d59a8040f671ad557f143cfc70954d2ac17b93f8ac08a18f9d139a1e0770e4f89672f09 false +check_ring_signature 3cde9ab89ef318f9c28f8e621cdddf529a0e314d8e3b46d883c0afcd3ce79466 dc9e3de3ccd6079ffe78de2f97701e8165f42facbf3c447968ae613d1ee81a61 3 d5841c65b0a130ec30cf754c5c16e6f267de81414805e9b5ef287fc299502310 9aac0d6d6ffb36857d3c466c5a87045cad9f3a073da81cccf9a4991f38aa44b5 b15577e3a24c26dd53cf8b8ed1caa2b9bc6df68c75690d368a8db526a5f3373d d5b55e495524ef892425ce8bc53eaa45351652dfa7a0bef6daed8b426a42a8027c3914adaec28022c17e8ffbea5bf26ffcbf9d9fea04a6931f67e8de127bb0086ef6c26f09bdabb779637709b427df74b1ec6011f2b041b12b4434101ea7d20e3d60a5b7adce6af098865fdc67976719a2a07f53c00312e686f9a0a8d07abd003e4894632d1c8f8c248c51791759591ed845add10b10ac0030052ac1f443d509b482b8589e561757a4ada0b0217d638c34d840543044ca9cddd9c8264b1b180c false +check_ring_signature 45295e5a2b60c481e574c64e1b71fc91b3b065b6d923b3007c082638c03d9634 1901306f23f46bf757405ea5e212a47a3f130faf642276392cb95704f73d9eaf 1 b5eb4484ac99a061245214e2713eb03ca90bbe1da9310ec7f40bbd7548cb36cb 4cef799501bdc799a42cdeb9239d5017732ce20fed0893388cf9bc604f145600e7815dc616ff55e84e9a8b5c2b3250d2a6fd7f0c2ee0cdf3a645b1253b164c08 false +check_ring_signature 8932fa36f69c7cfb5f27c7e18bd0793d5453983716bad20d5f0f4e75ac4b3f59 396530e7de348a5664afc7cac53d012a576dca25cb123eee399d780e83f35661 5 f0c48da64f55fe1998ea11f980c9ecd96b5c3d3748f11fde04828fa4e04039fb ae5693ec660e7cb32da13609575c59e7b9fea4940f72bd9a157536b9e9670666 987680f34264f5d3ffb9e11064765d74033f4a28acede9186d523bf9df4c0ff5 38859e120d4fb69de87f60a91a21928f38f4882a78fdf91baaf187de5e04edfa 967f7a14d7774709b2f43ed062b6490c65743e2777a11cfb28ff85e31ecb9529 d766e9037794debcb3823fc6ce89b6bd9f7341497d0ba73a318b265972b9fc0ec47f1e538779beedaed61853a00b5b65b3a02d73d6834fc6e4624c16fe2cb004b433de8f27b22255d9fe1994df47febb535540b9a7c923d6f5c37451ea81d906402b69e08feb66995674a248dbb1c63cbf3ac6b76d3fe7aaaad946239e1e7707bfa9c5192ff325265dc345b02ed347a7d315cd541ac321e514e214f90b2b890997a463f54571683b77694b464689ed36b2b44d785dd89d082cc6e3d25dacca0774f3f65a43950546f99a8910746f9f777cc245d64934b6a8eab610fa36925f09d38bc441ddffa5c3af89d0e87bd523ada0210b54402b380e6c7418c107f71300d475ebcc3cd5ae2abd00773a907049623207520c3beb65e66c66d856a7e26002aa5b7d842fd72eb7e930c9d24139c50ee00478e5a6ac48ed2e61a3567ab61a0f false +check_ring_signature 2787d6d4fef905ce4cb93b9148fb34c48e0230579a2ae0c9574d9f05498621f1 c791779168a684e426a214bb587b12a329fb34007991ff503a14ff07efa66f46 16 2b81ff3fad6ce543a965b55400faf2b54900b804e9223a614b78957f09f0a3f2 5d6557e7dd62fde39170a9b0752cb9d60ed4dfcb19fb3d64fe7e61ee3719c216 3ebf0f8f61c9bebec8e758efb54762e9e815da0c2f56574c9f7fd07d8e5c0d80 9ec1cc9ba37847efcb190e65b62dab07f75fed76a7197a74aa4015a23aa1553f 20be40ffbd1356a39ee414ecb71f210d18800ae7226d51a0da0a9dc4f1c610cd 692c69eb5429639673f0023300563d6f6006fe4f83fbebb23babdeafb99f1df1 dd8070554fab95c695a97f4addf42a93abdace9883168dd87475e5244a77a1ea 416bbeb77897e533b05a70384d7800a3909499ff355857cecd3d374dc6f91e7f 0d550a62ec103175ae342f04e09fdc8a62394d619c941225ff6ff8e7ae27adf9 6da5ba97619a26b595da153ad83be583bfc9ab954a2fdb713f6ee67abc42daa3 bd7c5b727f207c244ad1ce09dba905ee0f1cb323c5bda44420c1a7cb49ee2a8c b73dce7e00869962c3c384dcf993b0205dcf221a276a39a89faa405321eb8f6e 497e14da7eb7b5f95f590cf5ef3710dc1c0117c251e07be5d47c6d15d7c3ee36 5d14d9c0de1876f84c0b29e9ab823600bf22feffffe0e3adbb80025f93fae297 6ab3a00a4f69a207b0cf2e7f96334f7b31fad2b222e0e0fc4e2d24e3f68e2c1e 38d1a52a31cf5adf92e9c9789683dc5e0e1cb5a2c58838e577f2c074774b4858 7c46fb3b4ed75b41235debaedb8b6f43036939ed62ec85697336c19d676c9b05701a79bc46d3db2c2d63585045acb0cb51590dfc141aff7b16e3b372d4ace40fb06721f599d8e5f3560b3c55f6f1b36df2a79372209a54f8525ffd2b9269d2047311ceb04079eff5cd3d9aec8a12960fd116e6c68ea0a6812820e63abca91b07d303a810c38bf8608af2d9373fb58627175f17777ca7855119cb15d502e9bc035b5c289308f2b8164f5bb6c03d8021260ea52ea3e83f52f3422d106dbfbf79070ea8b42ee9992f521d196fb9d0cc84b95dbe277d2b16afbc3358bedd4827b306d626ae54fa78ec17d693ea57c11ee5fba2008626b631229d91afc5995b50610221399810336699d6fe590f44c9756b7b9219927416655d6ae3d7ac45363d7406d6732f0b9b7b9d05b72de6a77ebf0c06a6c7394f7179d812c8b88b5e7e1dfe03d90fc0e1f212b5937864369d1c340c5049e7fb3c933b525204643e44d9bf810d974390946e2e1ca2afca9acd5b9518751abc74d0b6561f93990201a35fb78e0c591975bca8f73b8f7bf05657ee2e853b33c7944d309f226a14aa28e6795b20035f721f2e41d91f424203a282d3d8162e9c69626e50d8173d294111ade0dcdd0656bf0bf55e572c5eb4e67e56244ca57844956dfce03f32f820d99689cdb7b50f0294985d48bd8757125227e396a36e525f479926f7747071d77aaf87c3653104aa11d677d7adba40ce4a4aaf1e6ae6b9db31bc393f17f7d8f2a62d1bc30d54013bea5d08ae3b9b9bf18ddd858c6ca101c926b2c439574ca44f14ac3e4c74730e032bf80d82080e142e21c14ebdf0f204c5d3825be3a33aa9e46836c81ca8b60aef069c05e3b678e09c7ebac7e713729778491b962e0f56a50c3a09f47e88d00e1a0506f319f746e933ee037ba33282804873314d1171a1692fe016bc5787d00a464fd0fb697d0d6bef91b8945198a260162d53def26e8e65297733841d4eb3005ebe96e4839daa0f8f140c03a4ebd50cfb050a41044207f1cbda5654756c3a005dbd1754bb723c1af37b113b4968365220a80e1885a2a8a6270d053340434a0d590c8d6dc1e6c1d73bb0443214e68480c74a25871d55fec30221276f691dd802ba08e887cd30c0059165dfd3a6ebe9cc0c7ad6bc51c329fbca525556cae1f60bacc96387e771ac124716b7524bdb39a46959c725b443acbde998dae05ebf6801bce7a7c4818f203684ca635172b7be72a130bf233255e90b004651e4e47bad0a1d82d427a732bfd65ee1c3e1874f3c64847f8aef602faff73a26a5a496852d0a57db398d061a9d6f4669ab358b5dcc672f3c384454226bab4d246bab8728a5099732c884be613428adc386182f31772539a00facbb0df17f3cdb497b312e9b05c30b6a65dd073a3261cb9b1f1fa41211fe9368e62ee41b7b32b332dad7339b0e true +check_ring_signature 1e1f60b39da0e073868645e959a9403128da672b80960f30b785c0a9b61d73d0 1c052784da7daf467f580b4277e48afa394ae3141500240c6cefb83d02ce7e1e 3 6b99a47d31f724cab02eb378cc51339878fa0ac55405006d8a7388ead5393048 16f77ffb9ec53d424bfd3128e7b21791d5756b7c653803dce145289f6e3ff345 b18c215ed792661469cb5f0ec0ea758cf2c23557a28b26b281359f77bbb2f951 7b4fe3f518323374c2a75ffa882afb0a31c3983832ea1cbcbd97b13359252807d8d574946b3976b3ad6f2be13d624da2c0fb003292bb1352ada521b719106408e0397e10be512581e8a67576fc6a8c9badec4ca09b87b8ceaec368b9a3d238036ff7538c29e3afc5bbf5d024460af1782f8cfc852eb662d2dd476cc85df0610008997d2c8be93d87824c5c28181302daeafe279df983fe6ece977b893bd0e305d8b07568f266c2856908edf3a30130f0d42179f58a17400b206f3c3f8e93500f true +check_ring_signature a4ad961781d4989717dfc5a8f24a6226269969a32f92bbe06fa4d2c531761a69 352660a32ef6c810ba020bc04642c2c97a3a504981af9eddf1823590c754ae86 1 6c225bf02afd704c9d2f089dd70b813535aaa1e2f9ad6f53ab0f68dbf0148c2f 2fc2c4ffcbf3f0e8d00958dc5ad1af21d060f461ef7c6827ed80aae987416600705c7cc30900aee6ea4d26de029e9b50d22681607b5bbda7748f740775b1931a false +check_ring_signature 8f4cf9c8f5b74be1a2a88e9608f39588dd375f7b01598559a336c0a650193b74 9fe95ded3b0f216a557eaccbd0b1f1c0492b3aad53c85b1ce372aa1434779e32 69 648ca5ef0165c526bd41e8b8f65382fc429218998360c5b4cb584e0b3665d609 ea31c4a7c0d73294c93b1478019fc9ca98ded1ca293e934e1c878209a46170f0 75e2c4506b40c78f0dd3d594276e5f753e64c6ff20a9ecfd041d9352a6c77636 154dac0ff72faa0022f40c440b1459821d992c0d41ce251257fc8b6d4cbf1a2a c3bfa25b7a16450c4e884204594353fc58d39d54daabb85ab276aac3c5c40df2 d0a88826f81ec2007d9ccfae49c3a8b68222fd1b4708985e8024708aaf8b1b74 21407f1221a01fb0f7a7afeb1d93710dc7ad6c01400fb6dcb6b53dcd98f95098 c6ead4fdc479b00216fe0c75e7dd2e5146275b8c75591174eb5a1bffdc2f60b0 f2e2e58725e1331b7bc25168af1ecb80a353ef5c4525a2067a3a0463f87b7957 8433efa5e8ddf58313dbd6b31a7807b84475a16ba217f4faffc22ad03e566b4d c4a69544d0bbfc4bfd59e613f54432205ddeccf643ecb0ca3a914014eee545ea 62001ddadf4365837c4c092e4ae5d3384804d6d1a46eacc1aebcba581b3e47f0 f8d8db93e48730c8c46987129484ca03e8147d60ab9c97591c5a7450b5974930 827569b6f1e39e8f3ee0cdeda32257583a88d691729f83a95fe0e686bcd2e7b4 2bc4ec0c96301ae6c4853d4a949944baba908e6b003389ccdcb5b456c847446c cf2a0bab52e98169119778725e1e3b9ee9672bea7c51fd6d8137d09a9113f091 27f58a941ff1a76d75975c644e287b8cec735bd34a8dbf66d761323b9184bf53 1ad053fdb282a880d0ff4afff7c192237fd217019cccbf43dd5f1d57dd4777ac d2fd122bbf5453460afbafa81e8352f86161ea8c5d1f187704364faeaa784e71 5fa64df9ba0b50b73997fea2a830c9b7f416bfa64054523866cd03d8afacff1d 6b071b213e1e0a12e59ea99d0bba85c2fa49ee874ef6efa8ce372def2d1e999b 36ca0b69a8d366ca4893d3882752b4734be1fc9362e7e1b584c8be46b6e16235 b914c41b700a46c8ce633ee84b64f8eb2de2764d5caafcad4814a809957f62f8 31ac4ed6f60a7ca983736734e0e0f783f84dff7b2f00dc12ddfeed674756da63 7e1da75c2f1af5ccc3dd827985111e90084b5f117d6b03d3da52e562b992bede b3a8aaa91ecee0436df2c19dc8e8408ea6b136bf9635949616289e8c772ff8d2 7ff0be4614ee9a2ee3978df01ba0daf0eba83172c415d6b2685c2d9225b679e9 e5c88db829c70ef4f9dc97ccf0d30524c57fb9e5b5ef6892908cb256669ece36 7d489bb541ed596418ff9f38ebfd78e44b3ebe653fd78401cce56cbdab27a549 de3853f980e9523340e697b2eaf9ec1c312a8f551c6ae664e805062a3427fd2e ab871dec12d4e14035857830d4c8a75949c4c65981ed73ce4bc3a814dfc9bbe4 a625f022aaf9b6893dc4567c21e9e1cddf67ac5184f3e12e6e867ba145a5af5c 4b130568035a6fbffcd6b7bb6163cc64e4a0118d9f67108edc6b602cd4a25ad0 7466d539e85f66bfcc30cc5d697865966c5351353509f9f90899a5421017466e b72257cacec96b1d968438200565b4897c8b88a78e5f767542bc2b20bb549147 82bd2d11aa1e971b07291ff38f8e99fced43d224d93acef7d32588a7f2c94560 4459038fcb8fb33e9cc8f94459ff416a9e4231ae79a7c076a77929d368550d45 c9cd4d226985e86adacf4a8d777d97be50d353dd89d9abc409ef1ea30be9cfe5 89b9002d5f4b55b693d9cef67afa957349e4bfc2b4270b93667e132d8dc0de6b e6dc274d3e1105196ff38fba14e33ecdb94ec8bd2492230f6079e03d38346d5f ae10e33c5f348c54e882a0f26902738bf50c24c7f0e59335117c40d2eb7f88e5 dd6cae8b16a43e4003a571c2348b106a90fa66bba6ff8b109e2277f62dfd9674 1c9db9e50150d3cee3da251994965ddf7b49eaf892867432a2ec5fd0efca2d43 383d6ffbd91bb38d62da59f2ece536b0be649f113c117a24a195333a9bf376ce 21cd814b821072ca6a595dd720fc1a79b3da19567d1c500d95d9c33e1275927e bf2e4499b63f0d0bba2bea7bb00bfba000db4e516bf475ccc064019864b90cbe c53ed913510717c4c3114b240fb3afc938b4d396b49dc4ebf4322464990271ce 150f21376b516835c430d40adfba81adc927d842950c81a5e17c87fb858f99b0 5d31e74f6bd1116a66baf20b675b4773daba1606a4bf713e52853bce79e13b29 42c9792faf17ade48001eca5c8c32929f813f0cbbccaa09597770759d174b09e ff63e6f3249d847ba1b30a81ac3fabee6cc6714da7a7aa0e112914af3c0daeaf afecfd4d132366f78e9ccf4464cbbe4f96b9a5ef1618456f4480db8969e48b22 2d7f1e2e86c7fbaff701f2e5543fa131a860cf8f0ece359d6e047e3d14541942 c77587a1f6162ac8dd03bde038480bd98f78c7ac6e9088aa5bda88a046c8cd41 03a306c9b3b3e46e8936f1f63046c0e4e88d128b85fb9eed24c0323782961f9a 859009d34be46858a5d8013b35ffc7ae8faf4552bce5f06c8759aa608f68e89f 4234b0b7a60a3a6341270b1334803b033179aaf14f9d9a9fc49186cb52c47f5b 6d229c735fe8024900e8593968a453bf3893e138cd6ae02459e1ae63f755cdb9 ed74ac21871cf5318936e03a0d1e143c7f2b7e9d77c1827b680289122115ec6b 2b34e410bd49be953492a545f51251551b620b749194ca995c8b59ae94cae411 e221409a39fd6f5fc892698764c97fc12b778c62ccd5ad0b59680c89080db258 23714e64183e38004235c83ca15d333e68f01de8f49eda697700cb43fca78d32 4d2b4e76a24e6a3703c193bd079a15e131f2daa52094ce4d48e03379a4973699 6e8a251cb6b01c5a85c3a446939863af9c9120d9002de47e6586271245a136ea f71baff49796c36dbcab0ffc400afb3c3eed91440177023e0a6decec7c7d9502 b2a6c0fdaa42730c84b4c570b44d3d80b5b28b702ebdb88dcafbc143dd1429cc 2895e4eb8946007ada709da3590705bd4974b8b33fe37a30177d8a4bf41c5b1f 3a011b25717a69172d3dcfca68a111c9e95a8e1765319f8266d6161ff3cdbf5b abf594093d9242626e017b09331dfddd1089c68e4b8378275cca2cf3aecefb32 2b7d32619b3f4536d87f0b254f1d6073eab61c4008e6dc2ab270bd64786d770fff2311cca88c2fb711937c41edfd66813f495efeed20d9b5515d76aabbba3a099926e0dd8ec41bb487d83b79f9796fc7c317a1cd2b630d2f2e4bc7a3e14b4d05a7d5ab23b1843762f29719edd2afadea4005c2d7d375aaca37c12d74f37ccf029477fe599a98c43a15c8ca115d57f27d4dea7b9293274cdc0636d51780bc0e00331c8da1ba2eed8539027caa2b8b28e472a14b1bd52a7840d55701e2fc8bd0046fe511910e00017b53c3359eb231b9e81e0eefa3390047606cef93d981339b09fcc2f2588895287799235d71bca37063141644f309b55a86e77c997ed0bb3e0cd2ebd25d38e775d3e74b1e0270144ddc450ecb4276620e1357062d4442a6770b4e64973e086da0b6f36a29b2eedf0708659301092c4e0b43271166f5da712f0029bf15533cbcbf249b1f7ba0d2c9b224b0f10e53b4658f793d83af8b52d80b026d458847ba9cecc62e4fa569058ecf64b08378a9d02bed5dd674388abe885a019664c64484cef486e65e224a67aa0ab26fd8aaa6ae5b541fb1308dfac4acd002c3d5b5c1d9ffea62bfae6290f10d459e5a15d104acee8b5a857ac88eb9b2cf08c9e7a2907abb2b0e651c7ece93c0ed354b5ef03bba5fd6c727c593921aef9208c2d59ac643b85ecc6c8473eab6faff1499be3cb33a8b5ffa3ce68d21e5beee0252ed98c9cfa97d91cc8028f2b1dd15356580d620c3469921f0b23e655fed0e0fc3b82a4d7e3a32931fa9384cbe2afa9681a339997a939d861c10ab454ca1510743503a061794b2f0085ed8917cebba6738b79c10f06a06b9b1b61d9e5cdb0302fbb62254976e8fae63245eab268703c08769daaec702e8a1a7bfcd32cc61d5040fcdff524449eed1c08d4a4891015ba0e2f2d7013486a1347c0f7d0acf0f80038f89abba137b6ce11d550b02b0cea8b8588440260a67dd9e35272064f189ec04bd1a3440509d272a4659f2c510bd5549fcb3e89bdb6b9645b715363d73072f0c8f4df52a9d169875df4511bce1f405c2bb9e49e612e61ce219fc8addecc8780613e85e5b73dd3505dee414feb1962ddcacf5e1d02536d08af0004e0de17f3f0a51d38b4c78f1e3bc88eb81f8f593f18caeb7313e1e30df92e92d6a1bd6ea5c022de954c9e6fc433bbc52ed6fdc354baee0e772995baca004c90f0e35d6f55a075364adc63a473a7577b5eb3f0d7d86ffbbf22493db0aa65091b5434be3cd5506b11f7de6396526bb4d6af28c81f58ea8de4125e2c3f097ce78afb5001656ac0926a62dd640e7e8b9445649857dbb541c8ed770f916d21a8f4a278c29d53b110027347b19cb68e969d7e762255025f63943b3d3fc886e69d33eb2d04e4e3c3b08b8c728650bf832db32fa2bd6fc749736a242482593f417ae4e708b29255f5a0b62242f23cfa1df858ffed32e2293bfed14282700e0e5fcef6b434b6960b17303e8009496546422fe3c5fd6b2630790789baf7deb0a7c1c95a1c9db641ed9110fc3d4c9d2a345b79729c408115b5abf636a65c23f3567de4a6d2979d414e73402c43d627a8761fa6b20698c59838745a80a835ea9a6738205fbe73625df67a30288d33d0050d5724ba0ccff41d7b583b8c1eeec25673fe60fe5e347f1621ac4043266ba89ecfa6227c6b32bf0bd37971ab477cc8fa4d3e0b9b8ec871bdc78540551d933c54a9f6c018fcc20cea24245693d9e94432f9ff56789fca07fb91df901f4328c3dc0c8ed8bcd55805cf6f1d2dec02f3c89f16ac02666f707b6e9736707dc30fdf0fcd4622ea1732c5137d6a78b203e40411aafcdaef604777f63a0c409505d34fcdd8e1b4f39d82c889df4f590923f80ecace03024060b38b78c584504cb97b2d938ed2f129fad9f7ba3bc063da01e6e2bacd03da538987d55452bae06de2a867d017efcadffdfa44af5b87b1a6c66308485bdf38bc87aaf622b59530ce1bea8d9c18c6486aabd68f50dbf9d66aceaf369f383dd561fa4a2c5ffaba5069418854a1ad3d0c02f44113bd0052d0dc250932c5dcdeea308327f107da54f0c382532b29ffc9a965f4ccb6e1073af48fba47d8c02ea29f0de2a3c0d7c70990a574e92ed2f577b8b196953bccfdd386bb01ba8ed67ea3d348fa048c4cc9b460aa2590430f652d749366b36a9dc489e19bc9750d550c27cd947888a43419bad0b8abfebbf0e75a6d68844268a099406bfab40545d6f044cb58538dafd7c03990b84a0220f08046bb43694eeaf9eb6e61d3f9bb80f8c0be25fe3062bad5c91d807d55a38593ef3208d7ca51b7085170a2808c881ca4dea7008b9aca3726ee9070f4b6409322b933c0205debfa56e2d53fc9a19f4dc8a82912c74636e21083dea064e361deebf9e7d8b6eb7a4231e2ff52cfba918f92e95b7142e392582413b760dc58fb035f4d11a549e7bade885668073a6030f11a45b1282c31a5078566fe40a64bf15e833c1d34e8dde3cee5943254fa3a255968af4f9ba9bba4702cbb9a904896da5de146649f3792687dfcef27069b31564956d70c5d9b6b344f958c90b083e6f726ba486af97cb69192e831303aee7ef35f400abdda9a05a377ad1d6cd0257aaab758994b1908578a927864533f1f60025def06acaf19830937c25a5c207c1e0bfa40d0d4b8b26376eeca7f956c54a52e7d7074d5651720670920c7e850dda901205675b9bd26e4e53468a5eb9d78e5abeedae1f74e5cf20d8807ce69a0a74eab5bdf19e26ff5fc0c9020f1c2e5552dfbcae0d21eca4a5854e902c529c09b5c7fd79cb9866e731670a45bd70906a88aab75933f828d3d37ab2440f45d9003f6b324c4b03b4eface5f86f309fa4e037730ffb2da68243efb4347e7c84010ee7158d8652c9feeb9f81b8884e1b1f7953dc9165137eb71b713e639342371107ded0e7514cd5af66980cacfe2fd9a8997c36ad38247822ff450d9505f1d6230747b90709c541407a5824d9cc008f6e6df0aefde2c30e7545402a3bedad63b9038aa3eaee01808a4fd8ac3d077c20c7e656d17110cd37e29c8f093582a26a9a02aaf0681911140a02390907b680d4860a9dadf37a89425c7e68e939990253800e34a833f3b488294f7b9342b39b27042f02330e19f78d6894cc55cd299f96ff0873c9a0b6d6e1534da5e758a27dfddd56c4225fa2d6f85fe15daa963984fcbd0c24f6ec0f685f3ebb69b4cb706a03aef8f1454a0fa7381ecbfaf7e3d7dd53090087ce2fd671f474173bc28c44efd0b080ff627ac9695741b2c886812445634a044f0ef1e8e373fece048dbf0342e3decb5554cbee6e7082bfa5df9a14dad96007500441fba1b64cdc6d68037f5959a13dffff95837a14ec2e4f9dd28d5a8899002cc9d346f6501950e23086ec9985be58c8c715124591f339766cdd93788fa1096a946cab4466c57976af1a9e7720603bafec229003217d00558aaf0eaf1ca609039c6e6e4325bae526547f95016bdafaf676bef769095fca9285d6fe1f55010a8c43eff8a1e9777db1b7e43d51d0119085589f12cc1b0278a812cb092935f2039a2fdd4ed186dbb6f15aeea74de232589ec34c70e5d3f221c1c196eb2f850b062e0dac9c6d81849956bf9b4ccefac9eee3a057ecf6f7c6b8ce052158101c6409f7ada60238170f6165ceec727c2c1956e25aa6ab86af8f2fe9c992d6ab7e4302a9a4a90c2ff78f6737b3f61d5c6e5eba959c510680087ff23240a79ddd6cb30eab9725b9da46cb57a44d4c8a994142a42da276ffa7ee532429e4b85249daba00f8d06f7ce3a14bc408a4561cafed794b757797a331659521f99e435e1d07db0d807c25f70ebb8ca32a3c08b3f9de4e4a631e3f7c1d43fd013eabbbbf8300520a8e25c809ec5cde38642da144753d0f664c93e80722d84ec5cf42cc4b3fc42a0496bd28ac4602c131a4530689ca7a3e924f3a871ea8fc49ee32844da2b6f97c0c4ef3c7d55e93a860d9ffa7c72a3a549aea964535e84c0e7be22a15b482134f042ffa3902accbfdc2dd456cb51c653eb929b522c5f68dfa958c8e3bf3de729903ad1533607f791daefa8ff87283bad8adf1f0baa1a615bb98825236e6e7e6a8058ffc9e16d60d72b26047e59d51d55a3e964f8055658b5d46b7d5d6a8c885380b4bab8655f1b2b5d79a832588b4bbfea06dee46e4833d8deae619d551cfb854028473f36180950d1571c9b19dd70b48e24ec26b0c3af2160c1cd25864ea904b04ebfc3289afc962971a5cd4335e14c97e50e4a5800c256c3caa2020057dfa2a09f53c7f6c2896318f9050586b7d1d6739ceb8dbe6a723984c6b3a5b83b0fd34076d2433cbf7627a4a4d2a22f6c278884bc764f033c4190c6f87a83e2c6a5fb009e140533adbfb22df335649f0781b9de47d2d0203b45bbe5e7e33d03d39cf520740592becbf2120f2e748ca7423de22f2150dc1169acaa492c7a9e48a76e1df04936069e8c1eaef547ff5728e23b706546569358a705ef8d842de4fc930742b03eb89634c54c02313a3d090b5b1d96627426d67493926fad27935c89d9e9ff10ee937da4c7f5d83a04a409ebadba12a2030e743e81e51884b6f71fa3293418000588101cb67d2509e6afd07b71be870693dc395090468b48ea74cf601e45a270f15dc6469e953da04e5837fead98870138bd785dbfd8fa22df9c5e371ed86340225b48cea58256810e5564755b3358ac49f71ba55daeb98cc49f7c4be141c7001022f715d1020f2b04fb51d782822597e6b0df99c79956f083c43e23381c95205718307883146387de0418767246716b33bc6d6dfa82694065618d56ebe4fc009c272a98592259d4b7a643c1e84ea9f9ec1fe0760bf67a3f180b9b218ea2fd501a4725ac43043c24b7cc14dee5a058bbb848675c2ea1907b5de78fbdf941834013817c4e81dd255124c28e7fb781bbbbfcb011724819f0a6a5ef35a999ace83065abdfd4be0a3a5b33f5eb9243ba6b46fc29080645646791cc93159ed4aee77029def3678ada8b8888f99fa9aca875a4b5856f859970dace5329574fb375eb403e1adff534c68bbe4bd5a605ac68f6df271cbaac49b3814c89d808166291c57086143a370ceabc53fe46ad8ec002174f0c1d7ecd879f34767fb960bdf2daf4d0803ae9079887be644eb9d518de543f443c9f11f97d58ddd412f56e5c0e7de510f4f494dd6db382fd25a317e9c7f3ef95788bbdce17b6a9901a1906262e6e93000134b1432bf54d8e2c6ec04304ad600ebe7d3dde30624c6f177f4f3d54632f20f81329b424140f6b2a7acbaa27ee8aedee21f0ae5304f2a3b217d425355d2650ba1e4c661a8db7f956c8da866df4f8a4c67b3c65926df9e50446fbb05eb1ff4021e5770afaa6e8858933e9783cbdfa2948091f9746a0a72901a824d71cba6b7088aef7668c17dbceb5fc08424e35cd0dc42908be9e0d8dfe0eb8a6d30623be40bd65feba139814b2c1f026f130773da83a1160b173ef03621ac4266c94ad01b014fe750ab3e81e51074ad9cf3404df862ddd88ba87d096b57225f8b25431bcc011b37f5d8af39696cae1380e932c9a8c33be552568cc1f77714b9f3d3deb5f104425642ba1a337789169ae5cefaaea2dd94ed6d416e3257bad4976cbdc95ab504cd7faf04b84cd233b4b955a07858c8d7ea19c8bac59a26b9e819ab017df10b06acf0ab1c310f2d3f48c977ad0e9a71357503f1acc5b71c487202127305dd290069afda49252bccb25e7b69becca8e5a7dc0e2af2b44f253c114e72dce9e0a501c0711d038658d4546a89a1ed706d1d6ad9e9f47396188a26adfb802ef209fa01293ced1209654eb7fac3002b46f8f2c8769356df281ea694d7d31730c9c4e70f2070adbf9a284f152ebd08336b5bde82861126233806abdd00b6e01fbcd6ba0d1a282341e1b28f9eb193055cb015dddd84c053c15f3147d17c4405e09141b503a9cd85e6d64743b5c6c28044c2c1b5a59b4accee136075a1883b748d506d03071aaab0c6b9482f175374b0d3a7d94dbb60614c381f517df10b12155c8fbf68067d0c8637a08f3fdae68a9833fff16b6e7fe5b1724aebd238bf993c8ff371e90e4f3e6b5d5c48907693a03e258cf711452387931f2d1332aa03cd1733d22db80115687299348fd1f838b97fc0dbf71008e8fdc58bb2eea11f275de853cb3b1f0573d54ead8291e511745bf75121ee1ba7a254a9ed0da517348e627290805ee20e true +check_ring_signature 8748607e8b219b207742b89d292d7cc58762b447791c552ef71df3f69515046e b2b6e7791201528e8f9e1f92e3c629dafe90c2618949b9c6fe9c17eb1aa45f94 1 802477ae65d5991efc07fa738b9b60499536ec736252603ce67498215537421c 502bcac0ab8617a41ec57ae74b99697e8e8b89a7e5d308d1556ef6320c2e72073661f53716b263c02dcbea6760e48cfe588f2dd6b4533578f42c199b82ea2803 false +check_ring_signature 04653762637c66320134fad1a7fe1eb2b014b9cef86108f8b142b69cbcdfd59b d6bdf76903430d487081f0fc346173b14f37319c552ae4700e1cb064c73bb06b 7 7d21728ab66091c62c9b74ad19d1403980074f86644f8fe00380647aa7cad91c 2bb683195dd328002a3b8bfa528a7277f14484c3bf4b96a9274f7e322db0408a 5f3549b372bc2094b09e13f433d14d17ee770f48c204f5f91a75d6d489ba3c5b bcd9936b248ee785f852e3d05aa10474736481b1e6be4dba2a1fbe69a158808c 6e6eb106cf86a146a5fc993199182de939b40f551eaa48cd172ca09b40951214 ac8314d93b3ff20cf4d8f93c366fbd4e60113d20578aecebdefe210af7972812 053859637c09765f2fdbd1d53bc504a58951f0fb659498f53532638bf8ef5783 a4b61745543b508921c977c9fe16925bd9d8dcb9ae455c7ed8b05befe43b8e06605131167a9ae6799d353a46d1084b19f1ab7643b141d3b281bfefc9bb5c7f5e7c8382eeb7809b3549cf13bfe2a34a0f35b010e314de4f98666ef3b28075640f0f54d78ba8abb4a1a28a6435d3fac2b5bcf58789aeb1d5c0938822cfa399f6061e1f54ae9350582f3cba72a56fbd5586423a5a0f7273e247ea3a144180d47407ce5cc2f7da897d5ee9b369f7fdf4841a407453fe0f0eba98ee8a6e071766c30cd833f7f6625d598719cbb4da026dbc4916e7d07929f74f0f7bf2b896ee2baf059d31ec74b5fd0b45904a3bc6d0d07f327f69d41cf1b4662a52b3e9b04f86050c860047b76bae789c6f0af1498b191be3b8f286f33957689f12ad4276f47eac0114d12be1780f9a9382b5cc796b42772bb08efdc60e0ebc18dfba85bc68270604eda5cdf2451c68a308413ca6b202e084746811873c569561b3ca0c84929c1557fa8bf5168c36e61f12381005e0bfa854538ce4c95fd4c6607cb237671046c20651a7ff0b5c6e27cf880a3816e803337525ffaf66f9bc2e8fedad119ae04a1b0d50e545d26938b2ac1cb62a7ac1c5e4311ab72dc85a26774792d2df502b142609 false +check_ring_signature 294a989ba59a446b4304c16f23622b4ec566261233e58bef73b10174d09a1a11 73a9dc2625383d070b98b630db62e8b0a7a6ce6061a0349191faa2c469686531 129 d73f0f0a0038e92e2865f3c1d4bbdf5cc48dc8b59e1a581961bcbb9378eb1ffa 117a9137b801ea4c04a302dba4bf6930ba655a8609869d0711f4c1999aa6f748 f04d0dee1d4ad091c1fa8ca5acc6f1378844d37c12b57cc06e25b8ee89659fff 01b733e2e1e5cb3974d04535ccfb81fb8694b0f8c1166bc687a6af57f1c19342 f2a0e91afe30df8fcb28d1e778d2b79923b512c67f30e4d276277e312aee288b 6a618e9c49c7bf37a24b855814350afc1fe57e16dd0f0500ac018a4f9bd4ec7b fec75127260ed96032e567b46e9bb95128bdb19a9cc777ec9253e2b0e1a980a3 3401ae5bc91c4800d2b1855c153880b19be0c6be6a91ed237b131058dc7070d3 ae89c009c8961fb4018ee1fe602757a54851080b3bf01bde3e500f84c35332a1 49d8b108d79ad1e97b618ea063d34efc9f16d3ec4ea202c6d5b26004ee909510 2b7a6eea647b7407bca87552e26884ef0abfbbff5c7bb251a3062a8712153605 590f5dcf9f384e31675f470f1043ef57e5c204d03cde5680d408ff64c861c758 0a58aad3549741a82908aa7cbd142ec4fbaa871cd36aa1e3d5ae19465a9d32de 5eb8ce82c772ab4b21fb7b2d8b5e25e7e5c789b06965b8c14f564a48f4633a6c 6f4c8571e649befd8eb8569ea7d2978214e4de0eeddb5e3bafd6586f30274a4f ae6a5ad2593ddab3fee6b49d75e7a9e6ad628b32be64406fea6f12a9f78ed76e 5a6f606d799612d2033ae3064b70e82b423b6d079f6427b0eb740326adab6399 a9a2b34c1d7b6379327308df2868b2522df6c237c947f1e1a205c46740a8cf40 3cbe7d926a845658570d0f79e46cdef8c023d1267ff9cd570be7e19ccca52e72 35bb806d3817b4ebb367daa184f938323840517da180b5af1e346381db3e5943 9399acc46e05ff5fe4809b4b63ee3f0eab351482f570434c06fe9645507176f0 23121003cd78dd95cf2c6b4e0005e9521568afb10ffbf291f537f202ac2bdc9c 02267f01027e9df7e7a98cc65e944f654ae704bf8dbdca67e1ab561681e8c32a 786fcbcb9e81481917e13819bf2fceb9b6d06abc428bd3b49a91bedcf9836cc9 35063e5ac5980d6c7581c436a61cfe8bfdd51770ebd0495757004ae36b680b26 ba0bc50a6f1899ff626fc201b0b8ba5450ab9f972d987dec9e1d6bf9ebc42169 ab65ebbab1a4734b70afc46c4bcea925aa8367fc6ebb5b72666e56cb3ebe590b 33138a5dd8899044e850d0881888dd80bf7565cdb2f397da7d29a96057991473 898e1192d56ff9bbb2b9a5423c05176501c090506dfeefdacf88ec0122964c30 ce0fd41a3ace907d14f0dc2d99111f043f0299a29a6bf5f4bed679e7588aeed1 3d8d1bceb8491476024432d473d67d992466c9e3c3ce34fcf593eb79fabcb5d3 6ecca7e5e684c7007e0add6f5521bceff85a1b0f1f6f4888566f0621ea2532be df0beb38c6bf2ffab67d4b61d0d0f09296c213d38e3f028471a8c007c5055d48 9340c1f7496daed6e87c084b32a2d029f4b70c4515e1c5cf9b91fd38e7e519f4 9e002ea254c999a354e5f132af1a99f49e88d6c276147c4a437bc188b1d030e7 e244546ecb9f48441320125f356be2d7f7d33557561e2c820823949998abdfd1 edbf43db9b5995038845010f6071fb8dfd6cc5f457e64f5faac1c446dd983a1b 74ed199c48ae132df07967fd19ccab228e397f336c8fd980b62dc42dcc87b89a 4e335700fe81638dd10c6915bb7e191d53c9fd95f963e4fe4934d7667f5832e9 670ecd89c41db159445d3793c27316e72084d12c6cc02ad8985a3aa41a8c7c10 7d80140d5b3b17aa477f5ab0560e6b8dce72334a7dedf87c18e527588f443b9b 34486feca3e2fd376c59acc8fa05a9d38f01e30fe79465b8e58f445990cd894e f70d51997f8d27d26ba7d06054dda134a7ed5516cd511d47fd599655e0c6df10 0d6269e4bceea552640df9dce848e943e421dfdcee64f018233708dde5465eaf 540e2875d7e3843d5cafa739e4b576bf014608f22a3c35f67ee2702ec6b40be5 44b6e1108bd850f848b04f14e1a3da4b54ba115a8f6f982f8292da1eda6fec98 ffc93e39338e244b4898f73c25b72e75d6c64c1fe7d28ff7d4b0ce92793aa484 5a1f49db0615015b91323c8e3618469b1232a130821f076d58c835ab3e444fcb 062ab8d669a0514c0419f504bc8a4b5729c291f2b955f93211a6c3bf6d9544f5 3dc4e3ac2ea9dcf1c93beded8e336f97e8f9e9d57863ad73573acc69cb506627 4792f31f0d0203d3e6bdefbdf03b6382033fd1b39c09b0a3ad25e65de03849c2 d3e936dacff81760bc695a5107c2b9e3302a50404e451a3ae77e146a274d6e63 366f6fa775b6b8d7eea8fdcd7dde384933cedac7878659393543a17bca364a55 d24fc051ce94f2934af4d1ec7e616a6ab9685b700f1648c009ac217e166cb458 3f7b612aa3f178de6beaa485414f9fa05de66f0fde844bb8a01c6b65c5cd9250 61484b366b4023043c487fc268fd3c549854948d7d241a1d14ae9ef6aaeebd5a b44f6f91768db2f0d9ad903505b8fc4e53e65408e037761fc9f9821de33fa1a8 a125952a949b57cd10f376abee4b7de40a18bfa5c6e5ca5e529c2bb7b217d8ae d03d9e95ec863b967d5006bec4fab3f637df7c7f6c73e5b6129855e605b10661 cc8bf0124340c626f8ac21f308c20b504a8fba78165dfac999c10bb1928d2898 295d1446d662949d82d371d5237bd6845c99d1a0f4973fa4519280a9ce8536a2 c3446f006889441553da2f207e5fcb093318f11e45e06965f9a04e862bce0443 1d1a00dc71291150b83041bad6da5a047089c6a5927846f2730661b0e13a9fea d771a602c0aa7c2039806888c2ac8303e9c59e005824d49ba3409498cd8f6ca0 08b9f65575d7c2ef0a5049478267148d97c97159eea427b116b21060b4570e6f 4c279a79f20b7d69b38c8997c695cea7e512cdec4ba2ecf347d7684ec4a640fd 881c17c6ba3a42c783dad4f04bba4bd47837a320b6d4b3a5a9c7bd22e92fa029 151dbc583456dae83fbe5dc0ec9bf99dacf187239fab80a222b15339f6a399b7 cb4592c0b35d3c535b61557851bdf26ae2961434a63f19b12296eee5ddd4b432 19a368052d3f58b35aaf3bc64dadc65ae7657351ec3b8383a870f89bac6346d2 2d3b1ddae118f343d814c37e3a0cbca065be951175e1e17f205ac750dcc5aa52 616c8901c13fd5710fc93bb0cecb196713478a701d7a8997c920ed00771bd7d8 a5428b65fb0647eb6f986097064e2b02c75752d67d79d7520c2cec010a6b6d23 bbc1d82cdf159a2d868d3ab9c88370617092b57e5b554acfdd7b2e3bc91f747d 2f77fda075532679eca4230843a9ec67aa59b4cf99b6ec6ac69b0dba2b80c81b a16b94f1100f9ad6ff9d63765b8dd63180a04f89306a0ac933ff105d4c833ad2 7133765d46fb17f9a51462322eec551bfebb3d127452223f47fbe821ca904cbf fe7b3d2fecfdce01dce8d88589831a1657e3c51443bb381de6fdbc2da59a99cd 8cb62ae2cf607365be039a142515649a6d9b16303d49a80fd0968d20863cd177 332bcfc0e4606155ee6a979bebff899f5486de977124cbaa365b9a16822e3edd e5396cf83482f50b76eb604af3d7ece9a4e57c2058c993c9b2ce9fd67cdc35c3 ec50c3e00c0147a628bf95c233ccdd53dcb62ed0056c546457d3075675515c36 10d901d85ee8919e7af94e05c6d025858aeab9ffeed0d9f1f26fdcce9d9ea900 ffc126b1c7ecae45b5a79210923aba7cdf6fe77dcd2c081761a2fc6981c93141 48f0534ddce7f3897a0c4e83958901c4ad8a35af06357284119fe9aadb7eb9dc 94f766f0b653cee06c36d23cc9dbc408b2a47b2efdaf3c519a1886fc9cd71cf9 a4903a0909df20162a8afe30883bb08eda2f193ec0cac13651fc70789852b373 03f84d4d2342512c25043f43d5ad959be72f73d71280b5a15f7559a52642d8b3 ce4c0cea87b0bf476fb2dd4311edb888cef3b53dd48abfbcd7f2519f2f1a6961 2ce1c15290c87d284722a6bacbe40d2fbadecd93ffaf1ef818b4b984ad735506 b60feb52f4994990a4bb3992f6802b5903accf4a37c81e8c9e5b1e396f7faf38 12d0dd0d78a09e78e06c289750dc05aa906743d51e74a617f2979a44834f9849 a770e43d783bb5201f3145822024eac56b6f190dc51ebc021f6242376ea3c6a2 cab7bf4af4b85709db3b46658d01e5408d1d1a99833b299e1bfae7b60c592d43 1a04c3d635ef0afaf31eb8fe647d3784f992008a02994ba5c72b7c1ca0a883ea 8b359179a3a0e48a58241716dbd1b282a61c11d1da926ef02fdbd5f569d7c75e 392a3b7039e7f2820d4d52f03a44e42ae8e5b92331866e0308ecec691e37ed23 34eecbbda16c79fbf272cdf221c2b6fe72a6d781aa30e6188395b637b0f79a3d e84cd119b1a671dc6f791d3507b103f51e0cc34aa110543396b59bfda45b4834 1fb9d5652e993e0d949465304c620328d89eebdee4d78759e831dd4cbaed27fc d06856013fa403a20d81b64eb7c1b37bd8a74b95fefc0e1b7383a3fcb04bdd18 6b3ba7edbfb57cc5bcf75464dd57a969b2283bd8d7cd5057b18c5c0378618957 e0ac1f869722df72a20d92ab24f3022f423b3f4f6967e8b98da6e4be5cab2394 95c736e3bb6641dc8e086d808019af22a05be272741b3c19ae51d3470aedc990 695e2036afb84dc1e169e4aa7d77b74fc9f8eccba3038c02188ef858e21e68e9 79877d6bfba8c4b20999e7ff47d957bdf4483a13e47506a44fb59a37465b583b 518a826a8209eb79b8140cf5c3b0e129dc46c219dd053541115bb29206710eb6 acd71578b50181fce64398db69d9ac9420fa050519cc750d2e4fc778462af19d b1c7dc870436c80afcee048124ec09f1db721d2cb34e21fe9d053d5f10e33054 e44d9b769a9dacbadead85852cfe8d407f1f966fd289b8aef3ddbd4b6e4e76f1 da3b12b58aad2578bd7f2d1f6d98c0f703cee94620fbb4fb3b0ef062ea813c4d 53e87863b0782ca9dfaea36b582c1492577d5287cfd2f911cb9e53037cc8d3e9 76f34d3c25db5f09ec6bf683853ac5cc3b0e66e4a50a7d7c2ca69b0d523b63e2 0eff8e84ceb8f76d9940cb2ef0b069b4570635fe6c20bd298b9f57eb52934a32 efbb522660416a5d976a25bcdbee0be5f05cff6f86a3c0ac8a2e6d374aeba70c a202be7b883fe2973c8fcf1f528550a2bbc8821fd47c6bd0114e3dc668aa6ea2 8dba030ca7c6a2cecb2710c49dd8f708a36b274d33851d7b700d69918e707069 3e86539ece51c507f02fdc6d12d3aac27837a7f6491f5a7cfb4ec49621b04909 69c31c43ba8bb35c8f127a26037e9d040f242a865e30a19d7f14924b6705203b b11229855b064299a0669097fd3cd4fddca81f5750857ab2bd02e955ddecb4a9 7266dd96242f6022499f4953f6d1ab9726a551ca1a7c3254f9eac04551805d26 6f730c93eef694326604b95ce437edf7a0536607dc8ae0fae71ca91fe6e899d7 ce410c7521cb57a148cd277388f3127a2fa5d285da556451aacbb2a22fe630a7 ee3e0ca11975c3ce42c2386d2966f1f4d64fb1d4fb5a84701edecb84643d25f6 9da5e34ba3a25503e90d68a735696e52ddce2a858ed5afe0c10ffab57752f5db e1ee3685e8a2e4b80ac8d0f682420501f9febdaef2decc5de9d510979e31e0b6 ffdcf5b3ce262faa1e1f3241e106d7262d8050dc99447bf662a9f2c4ab78448e 808bcf508878f69262bbb3c3d28d6e3e175f0828275889052ebf3e4f84cbb2ed b4a38cfc07ea93186db7a702393c3edf9bdf51e8aa2887d6045b1defc1b80f41 a6cd7fbf2c66aece76429661173a795320528549854037210203710def6a6e0d4fd3a3bb36998a12d1ca28ed46e7f2b150d9fc3ff413a64c15c86aac2552280794fe042c43e030a064f0fa7161c5d6605bb3752ed0875d698ac157974992310e49dad04cb1cf80511628472c3c7904c9db0d482c200e2b6ef568d20b7ff6930b4a148a12b02b75e9c1c498b16b426be8e63462103a3ad9b31cfe8cbde822d609e283855ed4aa0ec36012d1bb670176f91bd342006a02746c4f1ed791a1e0450db7a2bc9f3d09a5cd05620a13cea931df2d66d8020e0f5a653ab97f3a837f8f0c40458633d8202625af8b34b5fc3d00f7d1277dad1f26583947700ecf4b12590a1bded6e071c85b5e2b7fa700beca2b00052ff1b1b5d9138979b427ca224a2104e264b608585fc387ab0fe6ac2fae2868822ad7ccc129fc089c3190e283917202ad0466199fcadbb44c6e3d7cbe514aab572a301db60f230029aa2e5db2485a0688a0b85c531e9bdd95b048ea2fc1dcfbffe2e6468ba6a7de3722951bafbb3b0bb9f29bbd7717e958a246bf672f1bb6aedc5b445fae5ea2b9524befc5e414d30bc9a1c4f10168b7bc7f056898bb5d2036b44f8ae5774741ecd206d048b19f02021c5afef0c964b6ba29f4ef0c616c9222cf9af830dbe399fd23383e57e2130106f5d71a70ad2d69280117c6311c2932deea5e99c99185466fccf4fb94f95d32063b05ae74c2ae4d134b97ce97998fe2de8e2028a2137fd1f00fd1ac31b60cfb0ba8532cf60294317ec5dc08a4b42127abceb454f9446aea62951011041b6d3c077e8ff7bcd1fc860e8f8f7f5aea86e612b2eb6c62872a41ff449389814957520b278f93a35c69a74f117c90284a281727a16b92e28b5b9a9457d21a70e6b2cf06d77ee96cae9f8a33564f7b6aa5769697a1492ffca6e976a4acb830492eabe307f789bd247ec91c2b57d13bff3ca72af9158336d441ca0686258b25d362a596009bc28da574993ea53c64eb2d5c28eaaaffe06bdb0ab2c2d7cf89bc4c5bb25106ab1cf65c7f99f9dd72ee11b62bbca4f0330ba607afc721cd6e543a50254a6c07beb06ab3a43c05a8e84cab37ea17a41b7cd220d5c90f1f2064144484423d0c0892c9e8b06f6f64f8961f3b9653be0510afef1139adfa0076a5f10ded113c740c63b985b769dc8d5434fdfeb782056edff83f513392eaca02b734be678cc8b708f84c0bff63cdf51154f2f19cc0b28c1e227aa11d6da366d66971defe38e4940f735b164372273a6bf731e6b15b1a80c6c19b5b4a2f5cae9953a0677197711f06fa5c5274c3b1c3f2b6769543d7bf6333c48d7e03c1dade77531235793cbc81025ecec1dd58d9cb5c0b194d23d9ba25e8122129999b87f1f2661cc2f092b6320c457876dabe3b12e347eaaa4539bc430e782bce6ce6c8bb9e79d3cb928ba36f08b8dbf539df101e6ae6ec54ad7372ee7f9c6e43be2234e0b6680b6f0337d2930ec06ce36ea015278df97993620d92088099c76b53be8f596e66aa7bbc5c0ed00d0d891be5d0d44f5027e31a4e5366cd70aad9e51782672fadfdc7b79ae4206d0c922dd4f889b7ea3d24fefcfd1ff653f4bb21e39342d1b9e4e7c1b1c08b41100b4ea457099d4aefd478e37cafbb44f50a42bce7ec428b347a803bb6c37e6b6a0300ebc996789a3a075cb0c51e061d68593f8527286d655e74158bd0279db43f0a2da7b22198577356817b83e44cfb197e2bdeae1db90448cb6b58f84c0fc5ca0c290decca3bd3538d1310c921aebc74ca9c2d86189ed278aeb7b50f415bdd770a961be1bac93cb0df92b37570e80101ba9f0869a71e83be67fa522b2f7dcb130fa6bc70245d0574418a71b6344eb0f535120aaaf4fe987ba770b8a8b1ba74630aeae57254dc53a3d7d93076f9c5a36b88fa1c18a2d6ae6fcee81b76013aea2c0aa1aec15748c4a6b971d5dea0ca25f019e31f5367b4817c1eed2e471a6a8699043a768a87b68a983fe8d60af0e9b86e79bb07828381f55b265aa1f975e0279f0b85457e1edc033f952cfd7612d248aa64fcd7d8b157c89ccf3a21724195db650481f554743f6d4ff65adb457491dba597b0b38b0e89d16e240b79b2e3d395c10e60cbfc6d1ab9538274945977de3613c17bb1546c2060b5dacd2b76f40e53bd005d4da754667195e6b80214894da69f78883665c09252367f971aaf76f9c48102cd8eea8e3edfb6c05bb16f682835746768048d8a6a2b680a20e43af0eca7ef06fd5f64961fe377e7464f4f706af93a036778a4a426df439ecbcb2305a048b2c33d8f662e3fb36b59e52a68e65f5785769660e408c36649a0b9f3c68fc796d509280b64c71050cf3d2bbf2660bdbf3e00ad28476bebf3f746364a2b09d0978406c22f0b65489f45e4776a15ef86c13354bc5dc864fb1df5244f3ef43fa138c00ab79d15af642d291cb554ce3b2014e01235a84df7a3f1308f4017f4ffceafb10f1f9f3688aa796b7e608157e4f01f41bf6d17309ba61a581092ed9648c19e19092e1c4fb9162515a6df5f7d8b979362b7e43843eb2e16f1421d898a1e563b5605c5d0d5487596c2eb976bdbbb38d3f8ae26c0b1aab62c89820a386825417db20d89deb86a83705c929cdf13a88e829d36f424d934f3d9dcfef5199f42da673304d57031d3c10509d35f059700986465c2f064f5e60ec2f7d99dc2a0d8b84de903bd68d1aee3b08edd50c669e86ae90376dedb19a1ece40bf40fb657149db77102b86e00400222a7c0a0c840e9da49faf87087cc486faddf3494578e5ed53991046ce2ee7088669f4f7fb62e8f5673bd3e7e4f6d136ff98787bf4efaa32307ca0a3cdcd837506f9e9940bb8fd95dc09001b85336dc5e8423d60ea60d77f7201b0e0f6446c9cb752868faa04968f703590395586afc3322b48332e1627d17ccf00a554a39e11d784dea07e891ca79359ebe6b039d2fa06a4c6cdac9d8fc7199890784d23c4370bdb411c2f990abbe1dd1fb4a32d76b0d6135477f4aa6996da40704172fffb0e77c97bfe180bcd42d8250042ab4f03cc38b2d1c93538674ac10ed01dabf4b61aae80f9f971031bfb7289cdcc047046f56e5a2851c6661a6ac0fc909aa32ce2c787d58a8b81682dc10ff9a094ace897e7b7a08c6e3bb12b367badd07a70f04fd388ec4d0e32577cc9eb739989fa2fc35dcd611093374837b90f5d804be9e382554a1abe5bb9f649c424dd9d01b7bcd8ad3118f968c7eff6dde6d2a0dd2c341cf5c79c15cc43a47efc62273cbb51694fe88b701bff794307ea9af36095c8e05d920fec0a5f6179f47630ee5403c1fd1b7f9782ebcb95f65b2b36ad6084c27020a88d953d8f30b45d3a4bb7cef6fc14a7341638b318f53414ed022c20a74e81c4c3f4183c7ca450e59733e7604b4126ddde422972b18d280101925310f1d1d0b0a403de244ff201472ec1eed83cba962d301d613e9c1622b1f7dd08c02c827484673d683e4b2d3dbd3f79a6491dbc9edaf3ce799506ef567a566af500374d58ff3f4fa9616771d96bd96c1ab2e878d051147e930cb1f893556b21fa604b657ce2fa4ac9af4ed9e76f1c354ea487d3a547412cbdcbf136f6c47e0b8a105a10e0c08bb38a260ef2217cf73839f4105dc6bd52428eb0b99ae03ba63b0ff08d1d25e0c730b0028b4921293a9b9ada15a09646995b3bf0fea20765120c7150398eec644edfd145a8fa327b8e861676c21e0bf3bbf35556f878ed856002bb809ecde593a48eeb4adf43af37cf39c943f6cd4785258490d06d053dcb5da62520d64745e329af011e20447e48fa9f758564071f4b0c18794f824039f397b33120906074193103b8bb7d9cb8ae9259553ac23c4a2c6d7cf66aaedff285d1ef2860a0ef812c49a7750e027dc213706a81bf6aa803e69b5c3ded6a47e2ec7fe4300020e49749bac821bd4210c4c0c662cd6c4588fa95c13b3734077ab84d8370a430cc2d98944dbcd87fe58c039a768c5d6d9b034a553c5a11c004734b43ab92ffb0afe34cd425c8bb7d1084d65d56bc608704114e52e9f78ee06e2f177ecd863eb0c1457bff5a54f8d2182eda85a212a9cfa731269e7d201562519373201996ce509a5509e92bb8b3e12c5121d9249efabec2acb2f97c9fb69f41c09113694bf990f2d85146f3f93d4b12e4cfd55c2a21eb81ab3042147236349464bfbb32a44b50d28c59c5405fe6e2b2d82896d84a9b8762508fed9c958b1a93df4f826a3d14d02aaec2119f4603799e80f68b1be953fb4a7c904ef72a190350579cec378e9060f16a2d1e4cadb9dcf44fb0ef4b4e6746b3d1d5ac227de0063463d3c8ff1adec0bcda88a21b5c226e820b0c61d9adda2db6abebfad67872c793c3b54af641649018687d51ca20e5d96edea741df5fe313cfb61011e123c7c883bf0284ef5f73d03de98ab55bb8e4c148bee321475b9704f54d7501acc8c7a2f53f554005419cc00f00890e91acf4e41e5269583a3582d5e82b6c468891bf642789971bae89f1909e30bdc3da55a50169377f45d8cdbfc91185749ad28afa8669d3de78da4dd0504484667412698b0e8259dbed22d8f6326bcccd3ee1ba6374652877a68b102870ccc50e7261d260d5350d7805d6be6f8612ac91752d1d18e290c2fb3539a380c0dd92951937eb1eb332dfb153621c345cf86fe1c6d5b0f23550edb40237401bd0ea082590ffbc591ad85253f439ed7231ce375d9779625f2e98919c7888adc610e4f62c4eb80ca13f7309b80c835d0943380f355976cc1ae6cebe9d93061819300ba151f853f3474706cd234a0683026140953ba82ee0abf7f1a5f05d63e13af0f58adf50231e8470823ebe5b125886c79c4665940cefcacab55afaf7832432b0b9cc0ba9fccc3f6a27e08bef719f4a2c971e371025ad1f4e7eb4e3867b984df00b0461d9de1e790b22515a7f2d9f1b527e3161395ad0fd5e6ecd831e5d3a87605842af7d698c89f2e82a1f980f13de49bcb397d2ccb87d4530e3e110133abb20957561e3a900e5272b5484cd35a024e2a2aa8f6b5ad1dc558af5f6d3aa5fcd1028a3cc0f384fc015441c81e34bb847c500fc86524c15977cd9c977d12fb533503c783a60c9c91cc7516d596eaa987617b72a473af372196be6e6c6657d4e1c60be7c30e1ec47424ffd79af75fef39c62e7442e9266e4090a81c1a8fe0bb97ef09cba94fe2a2ea30ee09480f0061c6636127ea39f6e97ce659aacda5c2dc19730da8679cef302d79095a903ff9595c23873b5025eb78ddb146ef3e1bb59d7ff90357edc699c0f581937488a779d2937e6b30e96ea0afb35a859abf61a422f25a0f6feb28f1c01ecf0ef31a269878e8f10160aeeaa63a03a083face00c84749f00421a1f9cf376bae0c57a3ea69f6e8f3eba3f03ba3a8189fb2bc6e5e1a749f840be2246d3cd7e5fda5b751a9c3ecf3e3edd66ad61d00d07003c4eeca0ee579db0f4e5f6ca9d340748f596e61ede13a8b4f9cea6c7ffc69676f9c8e3ff911bf080b36b42c28d8345f580475c5e6cfcc08485ebe03de617fa0d7563e5bab91b2a90f2a54a599de3bdd9362882717090fc34e08d0f5888b5bb8453dd365c240ef420b93b855ad05776a993d5e0f5e1777d3eb6b842180d2cc3616ee8b6d63e9440d0be09476df927323ec0c0ea9fb956cc637f6f69c49dbc889fa9bbe96cf7591c00ee194ddb8dfca398c0c3c7e3f229101541745dd6bcc143d2cb4f6fe34d8c50e09472735a8572731e31c49e33d5d4e2ecfb08dab7ed9fc8810cb5623f306676a0902d1c7863114bea8113ed4e805a81fc4baafbfc6086a06d0775f33e669c175095aaf114133812681e367c7ffeaab7fdd4d6187b3524eba3869e06da2fc73550f1ad0ac8fff601cf4a682ecdadd9adf8a33094de4456c3f898d4c190285534607fff6e86684c4870d50953064b1541837e582ffff8615ed1cd39e58b0f2abdf0f2b5841e37636be7dee7d8fafbcdf4723cbfdf4624db3c9e424cf3313bef2d60a8c07011aa2eede73b0c1c850aabac93bf408eca682d4a4f8983472072ad4850c6ab79ee5c6efe3cea0463b15c7182e42a1d1dca1542e6e1ca9fd98240773020ade532cd69e2e13befb41a96ec3f53d3164257154370236aafe7f5a03323e6302de153c8683aa41017153585736281d91cebd902117cd4ee6174a76ce6f8d740bba359552d6e4c483bd5321399167e81cf2ad566746d217b54a610c924c49480edea7129688b052f36af9db31e69155114475f36525dc0f69dbccabda6e00ca0090ac76fc12dcc3b6f1e5bcc8191ce2bb46ce0c350c59567bb25ae86ba42e820b7a0b52cdd12db9e548fc6aade1c4bfb975d83e3a7e2360b254ecd258f461480fbac60966cc8f0d1d697a681de65964bc8e19ca793ae27a71bd1fc2ee59210c04d830c8f646d709a9d66e905116ea774fdc684254b4008e7feec469139d0c0a0ee0b1059396d333d9e48739e07ac807f802d0a8d147bd5baf2b6e872cbef87f048806dd2e50070da9c41f0d501e0ba44230a706e5139003cccd144d6a365da80e191715fa1e1200ea4d3c5a2ec48b0dba05aadba18c82f9222166d342644e600e8c07e973599da0083d425d7dbe0c4fdd5bb6e4817258d0e43339c852d3fcc00a1d93976934d6a41b44e5cafafc171de7c78ad0ef5782a698397ff52f4408670054ec1bf47b4665c9ca1f348def3c95f797cf61d81b7fdbb9b8e676103a39bb04a106acd3bb86b55bf9d149d92582d981316c81acdc8bed7f31f47d957d2b9a03380a357595d5131eab5d802ee450b42b474a7887b856627f8dfd3592b8dc81070db06fb61469c5c98ac43ee058c54e6f0ac6c596c306d15e6155b88b661a89088871bb397567fabc6b3694a44fe7ef2845e11e7871333154a0fc401f0902ed00969e9be3fc9066d216fc9ec53c878c218fd724c8068be92f8f8e86dbd411920c647ed158c128031d2e9be80fa6c3a37d83cbca69378b1724eb79a47e2fcdd40e476a9b7d962e4436b6da52c3e90fd8932e331147cb6a247c35af24284524d40450d8df8250cc85eabe6e919560682704dfd94313111a39028dc2789e85b6b60b47088b775359ecb55d21cc0f1c08944911675e41a4b71692e653cc4cb801ab0b2bbc3b5251cca26c44dcf2817b53b218394a39e4e4d6f4725664cda440e9ba09f49754563812df87ece8e7b46ac5beb540da54cc48aee86bafca60cabc3f5e0ea35ff53f550ef38d1e5a9f0837f0d6eedbec3732f9276247457c46660de05f0084965eede7878fe9cd24a1242a7d3a5d2f8ad84abd699f16c75605c12961a0099a407902c35f359233defd65b3ded2c611ba98e6704545dcd807dc43cc92770cec613b00bfe4faf8c54a57dfdcf265ed6bfb3fcefd04a89f58e3c398e3549803cd3ebc03bf3d38ade796374b0a5e7dbbd724db2e9bcec017740619e273645607768179a08c355ea57f81df59c78304bac4ce05bca91b236de659652b9300cc037c672ec7d9c48e811773778c50fc392195c2f98c79e235a85822174d31175e0e22d94b91359a80c8d805f225d936ffc150f9610cddd13fa5230bc9fda28f210564142db1fdaae19071b2bf23b11e8104a1a13fb497392876892cc413186c6300afca181c5fa603face6f640e72ba4910b4afa2c0906c7d18121007f15d3ad80e618342e110194b7628540fd8689ae2706ebc82fc7fe5b76b105651306fa8170aa2f6de3479a6192e856ca01c5da4045644b908aed3a178c54f8308ca62b1a80f5c3cab1cbe352245326568cb6fa20e5618de0e8029b5069a2ee795713aeee605a4160d2894dcf42f3490bdc8968a5d3227b261b5045bd108868213e845494e046f015bf1b3158df3c1215c99f27224a867cef0c7a4b181aacf567d36df69b20bb4b22aca73646186fe6cdff7616be5212e3434865cfb3821d0686445088c310cb7e67a9ed4c76b06b3789b7fe55f6e537569203507605ec46996400d087bab08ef07de206ce88554b95042e09676a139a00a07d2463c49a56cfe529fcc1d98009520bf1fc714c4acc0b9185698282fe6c4a224a2e8e0f154dff8c6f4d51cd70fad7ba1c40465616202383991281f864dcbcad36739456b9722856c3f59ffe70964186e1245fe57082f9d991715afec5b3f149dba603651a1a9fe4627dfa6b3014aa952cd77afb056f247326268503d78dd96e83ebfdb4daa6b385aaddc68180ca53953b63c20958c2cf6a619ea65c6f03131b24c120ed1551ac7fa0d9707e304f6a73c40723a4f5abe976a8b94e84766fa3692c3c6fe9ff3ad6ab494f9ad830a699729b3b5cbdf4893d064b191c7fe1e62273c68c71b09ba3c5cb3a7a9bc7d0f7420bbcd0a4bd9cf8dfbf8b443a76623dc4baf0d2a79ff61944cccb52d31d60b0f0246a8109230171a2f1e4ef238cb32c0cfc5b332e3f3aa434e1e7bd5b5bc0af1333d7081bafa217f5d5bc57b8fdb60824cdd2edf0e6893452d1bf82e49ee0fe8dbc1fc2f58587f751655182b56bc0246c12bd2cf12ef63104581f2fff46f0018f7ef9cc28ccd305a18e8d537ad5df7e31e2221779d71ff6418428ad4c24a09a54faf7a5fc32d853aca130fac47fbe836568d94d8e4a920c4f1e644ea9b0b009cecf839ef28edd1c94149404825a427f0b319296a1b2bd03377188cb98c820b1a5a723df7a205d1beb0449bc58a1286323883e4bf7b501f80ec176121e5da0450a5190e22199a1bea0adcb552d86e312945212a8445f682dd0df76e70126d0ac89309a692712386dd26d50342e9eb3d399a89eab3b18b8a30500f8ca0607404717299c50cebc82dc94c40153bc5b61ca21b9f7085cffb4a7b30e9c42e1c1e047e15de5a7e761184cc314f5d4f11f3161c348fe0a50652d04cad2dbd397b3c0d4abb4a33606e4881cadaa49b92d43d7e21f2612fa9f3e8ec65fa75b74b37420c7a2af8d416022f503dd52437431e47b120dd17e2c2400589098a6fe8cb7fb5019953c7ce5e6b097ec5c4d8ddb37d81065ddbd1c7b01e9014223d1a04b188240e2974a26a967f01a80d8e9eccdbc58d29f910901dfd692481419e6a41d1299601b21e4f9bd55bc13988eb759d5247abf82132eb5eab022dd042d361495d91e301ba4da037dcb37d3e1375e50ff71b4e8608966a114d1e08d37188ddedc1269804bcc977d3cbf1cebb8ba055058cdc6c73cd9c41763c7c017a29a82e1f5ecadb0fee0e5e6c0dfbe089e04cb009cbb1505e233830ab05534b8fe558ea99a3eb720ae7e20f59a9e94a82624db6b84a4a2709af05199ab8078248f418152d5814220bdc3f8ae4080ad0fc503edcb24ac7bcc2479817ac3aa514ce0577da26ec52b4053b485c2e568c777be1c9efb304266d8dc8693ec3e0cfa83e4e053017011f950c1cc7449f891f7df6509fcb76bde5e694a773d365574c63b06bf9292dfdca7d0a96e0fb647e6dea6bf92b7a077191ed106c90569d5017698a40024a591ce00a09fc2f9b8dfbded924a3e79520c700a6388ad9c7a695d627ff112636a0255f610def128c9b7d1d439968a20a48a1fd0351281c452c1bc937fc593c253bdff162002f2cbe8c96590c8a20563f81b3d9a894ffd365633174b70fed7c2b6acd3aa3089e4167ccb3f8fec5e8d232cf462294ed65aa5055a03c3bed124c1b11fb2abe0499321b95d33962da90a75b9c8dc59b6808693d387aa6744b2aa96797cfbc3d0264448a08656881527c30f98299d4abc8adccd4a56c03588dc92f4193198b1d0b3e5d256eabb65464d9936c086c311b54b9ded44f2df289bca5e63b5a3247690fa638d88cf5362b0493088dfe3b487e707124a4c567d28276345871dc564e000d2d6d96e17e1f3d158d4ac19289925313568e9a8d6b759a295ca71a4a29d16e08071c07e1a4285381e2a7c29cff0dc28a846ca9640d41afffb36ee0d9bfe35b041f6ad4c6a2f1ea531193ba0b75a7ef47047abfddc823baefa8c28aec78c8720f3160082980bc45e4e72352a11505c65ea481eafda28b54770470263050f78602f3e8dd9c3c5a41b98a1f3775b74fbe49552e7a9a87de23e290baaea130a38f0ff9da750e82eb6cf7d23a805f7fbb2b2d8ee0482c2784026617143758fd434a0c62a708a37df00ccdbedffeceaee570a875751993d7f858846691c910a3564e07188c141b70aa8dea3f73bfbb3099f70795c88e9c50b07055446038b2aee50706afa4dae6e30857c4960164711596167803315eb35d7c0ad07fdab23fb51fab0461745f954e65ba010acbb7880329fcaaaed0570fd207bf340f12a5aecb6ea1007f8e6e54516be1ac1c662949138bdc735fe189d9981e661542a799f5dc908a0b01a7e35aa2e354dbd4eeadff0adbcfbf0f629a7946806543b3d82cc904ea4b0d4184796c0bf567904f4b77ef1a417f2f2b0549cbda16a858eb16e6378431b00901941eaf6fc55bc92c24dbfc1c22b5713bedb939aa10bf6a80c20f90ce71f209836c697ef4e254762ccca17139849ee62f517cab79815c48861866490f28350c38b691d8ac631e77a8b587cd24d5667a407d777f2e1a39ba458d5fb5c4b4950d2555c47c377d91c58ddd1a9e54afa5375f27e36c2a19bec5e99dd10928aa5a0b3fe6726762615f3364c6264d521a4014472d4d3859e4811f49ca94c92669450be53b27fb4af656ff4ea1e49b9bb7dbdc91fc6b5c3b63739c445f8b5d07c73901022ff6089a9132599ad68d300ad523567021f162f70bfa97b992af31cbeff9074f59b665581a70d96612c06ffce90a344180e6820b5bbb2b202435672f9f81059b2bb888374345c3b4ce108cca1f352726e0d54e453bb74f8c0b3217443b5e0de07b1fc7232bebb3568728483dfe998ce565458a6e1face3ae554bb1d068a70ba2627c880105469a3d931526e4d71efc76a4eea76973147297bb3b0361e4f10a9e5174f548cc1a4ef772842c78a4b0300bdb639428021be80a260bca61353702086ce0f44470edb89981f2602b23b71a598c92b28c07d7126b1d6bf7b71e160d4b1cdf61197041a4fddacf33a103a2ee5f752e48654f7769485a7d51f3ea3c0f51c0826ddb25e2d0ab4d65932cfc9c51a687fc83461764a22bafd562d7af970614397ef856256a355fab0525bcc9383dd580ba919c12bdde462174ff02c444071bf17978d50339610dd83a85a45db3c09d537cd9b22c7474aeb140617676bc01224c49fbc65543dda2fdedb22853fbe7ecedb137a4d39056cf6ebb7ba81021008898aa67b38590d7db4b190232dfb3985e3b2f48a7b1d7a234e41af087076a0dcdb7c78d16f844b47dab7aa9afe55f62636b7f89b801a56714e7d29508200b0f7b56a597b86a12c7df723baa94a4c7b8f98be8c7deb7d8369abb583013989805cc47b3bb8fb2e06b0c374a74a2c0be50bd90453ba72ab92259a62f1cf8e9370ca64ff588ebc199e0af825e521337682e9fe561f04b11bca21a8b4b3caa4c1304e24454d75350ede942f3a26a1a6c0e840f27bada63d8edbf62f2daf4259bfd0221aa39f2aaedaf5fdf89f29943bcfaa982b31558d8d2d3a1102219e356de14000d6347674810be8c0efc103b923971185df19f9d7e0160b09774e123397a1b0cc775d2c180942aa81a97127c8e457f587d034257eab71e3103823306d3e26003 false +check_ring_signature 1d309e35b2728d289ae2807262e1e1eb2d16e0c5868aa8a62504857102929aa3 15094f617c4cb1d0dc6ef0676e7cb42212beb634afd48c39696707075de109e0 19 dda3b26d1c42017d79a33b6da8b6cf8b9d98e429b1364380da795fc1b3abb640 1cf4a5d810bf37268945d6d6a3c033edefe942c97cc839854bb341d8d7932c54 a5bba9aca514e4c56945a317d69db41d7ccb086ee5a0e71e101b23a6d7299e0f dfdb4f67cffd9554950514cbd021e4f5f628da06312343d623500d1f66fed570 df3846a7f21912ae83fbfa43a1732e9d282084be1321461b28d669af56596637 9ccb012d77c6dfb896e3b42f299a9fa0de5402e618e2ecd3882d6b945ce6260c b62e991e654bf76f73a8435aaaad5e40605e33dbbaab45f45430554d594b7cae 10b7a8438a65d17a894aaede57aa968480799b6e40a6b833dffd41143b94bd2d 2b3710e83878e808255994a410e037a4d8dee6c0c4c0d1012183a0f442b078bb 062a5c2b3a776c22b9a8dab7c2c67e193e8b6e7ca58e33ac03513b75ba77bd4b a1b0dea900b7bdce3d0d944f863bc1a991f7926fee1b629213a78d618353f7cf 3dc9915b0e6a76bd0f1bdd50d629217cecbe2d0db3202040820fb6706185ebeb caef1f3a704842a78e98bf58e610cf52eaedb09ba209de74ecd96631e5ae9f05 ad6bda63b85c624d142c4c8f925fe7c0fc0fcc59bdd4101deca9efedfa6f67c5 cf65a5d3721fed757ea0e19b25811b01e0980fa3cf46497c59cd5ccf9f91149f 98ba727b73689066a776ff4f099014c17e109530b346abf606d0b84584c12ab7 6fa6776f2862ad70ee0c6d6f4ac74cc147b0fc9ab100924754b81e1edd59b6ac f0c36504244308bfcc34f01a2d5ebcb347993cfee553115daed566c3785a94f5 0906305bafced72bdc363877ba16e4a334106b8502b980a764bcae52ec2e4a32 5b35cd1acce95ae799e354fe4eff25d530670e72980a09fdb4a708b6cb3e050a788061dd5a466c861a3a577d008ef5ea92f9d9b51551a10fdddcc4abda35300f6bcf6b34ceb7134bc743b2a96f3f9560beea096954e1fb86e05ff414c02b780f305c2426b5d9718f67d98ac0948cf98dda7dd50ae8a219bd002319371364ef0c323b72068a2df05d4249c8f5615234437851ce7feed7a1b8109922e42599760c6f17fd029285ddb5ec76444d20ffe7bc3bef15bd3b5f4deed6f93619d6dd6e0c1e9a97661a7163cfbcfb9c734b977be9da5116e98e4f7d6316147e19a2c9d1062887f56fe73f824bed4bd4c5763036a4cd89159299396f99f92b55c9400ee704f310f65002a59f08539965cc2b179dee915f5396df3cc346f0c1e2f005a73208b5a9c3ef1a99559d8a8062969c6ab6dadc0469ec20c744207e946ae39c45f00a431b009f2253d195deddddaa6926331356fb3e65439da7788551fc27b063fd0ed8345ebd40010a6ffac992f72f672be022bef272fd278bc22e2cea40cdceee03e2e0f9f29bf83ade9291e244c1eb10f535128e1b110868a99084f84cd05ab40d4c6a7ffefc7c317a39d507730cc546cf525d86fd1fad16845c0ba826ad28a301395f8022c9aef0f48d2b6c88c4ea14a17d4c7cebb836d5d995829cad077cbd030b8d65144afa3585d2646a5f86369fa8195bde29f2256748a3633aef502ec60680476e3200568fef833c317aca375355bc19d77085f8b9e27f37ec27762e9501e6aa56b5d5bc5ef10e5d54d1997ec9f5bf2d5dc323b84850d16a13c68abf500a0f76d50ea2b04121b71950bef837912033e7beff30730f1c5d5d1d466ef5490f6ec1a1b85d85c4dcb2a66466c6a3095713297639393423363e624d6edb8ec007bf601f3385fa2c26f3d99133c708dacb631a6cd2a5c36e20222c14f7086e2c0539240b48acd0fe7f2c6b8cbf31202772e487b6ffca975ad12ed48fa84db03e0211fe8afd10c03c17251a2310124f73b631e4a6d6fad1e2cadda45982b68b3807c8c2f82cc88306e019d083c049819092178efdc55aba10433902aa752a3f191be4a8af06c4c1040fcff0fa091ee979635f41c8c77d103898e7325b001006fd064fc5ed2e45bfb90fa05319f55ac8a874d48d69781ea016ec9341ebf8f883190083997cd553757171ca6f2b93f4c85451086294c2ea655b89d56a0b3c3044d303d6512e9289281888ac747f2c8e48d3b64caf45f37aad423072bb0a673e08480c6e1f666581397aff160faba3cf10ccb84fff08ee496db1b2bcc7f7040c6ee20616c486671567c7402a03cf327ada19e1b9bd769691d6f7356b05c249420f5608c5c1f4f1e9ee191ff9d6b11c35108b8ddc92421ceaa76e6044679d43828c00036fb6c23a31fb944c0f701f540e3a0de6708d16ead63705018af37f5b652f730821e2fafd41ed71b50d530c0cd4ae24e8a854feb43fd7f667087b12e9bf5fac0996a1ec148492674351e58f3e887e519d66e2b225201fc06d36c5c302ad82b006c0470a9b4b01e2185a692456fd2db7926acae3777c710c47294f7db75f981c0ae816de751249fb33add370c34f55e2690b9234edfab4c43d5cda185a26faba04b7893f1e570404eef6e1f2bad9a1ed2b662e5f15b6b3970b704a2b2f9763b30e27231986ca5e92cf0ca428cc59bd85ab2a531f32f859d55a3711a985994e1304 false +check_ring_signature 8924e308435b1910118bc2dbd7aadae252a05a9c4e4ff5f7779a6278956af8e6 02f45704fefab2f17b9ed718ff3519fbac72710b24bb7813340b726568bc2d8f 104 bb3a53f1e338b3897d2b8fa9b88ed2c900fdeb960ce6db9aa93572d60c878e05 dd671df64c64d75dd78def5e32e915469c18e0f0f340d0ddc6723dc89c8bc9d9 bb3c1ca7944f54873e1bdb178102f71cfbfb30153eee71d87dd13a51ae4f081c f3b7d23bcd0604971262f93f18022c6a575e6baf05fc0a61f5247540c34475be d54f2a4602a41a63fa7fb24ad40bce6f5f1fcf1328d4ab3c9b8e2de6f288fc97 7aff5f3fa6cff64b595eaa8faf8b2d7e28b9e2200171d4e82effc7c3246e5ddf 80d90ac679081896e26c164fb7c049d8bb3ccc26acdf53dd2255bfe30473b220 7cd7d4835440232ac657c0b968e7650aa5065df4380885ae0dc0ad28f7b91725 cef810ee771eb4e7c4b7b5fdb8d719efeb96d46b821bd8dae13ecc98e0507fc7 3429e7d63c7511996aac1da2cdb87600ac806df35a6229e7c1aafcbb04f90517 6d028c857436507e778073a86725d2980f7ad67505ff67ac5fc6e0f5aa574dd8 e1d9ea87e3348b55726199ac96cc37a2c829bac49f4e0bf1f0f455152987e0c4 dbfbcc1f199e2df9609c5f47fec73c4c1afd5c2f14fb4e685dc00ff9a4b95cee 3b5d892eeb851ffdf9b026068bb1f4c6cef7d01126da0bfee6de78c726e1a22c b5bf6a85994ce1849f580b6fa3a050e83a5d1d67abfd8f6d57837e4074753906 11670385f9eb9fe0b3f8c49ca180ef985c1d518ed87d61347ee5e07b5818d121 4b7544165cd5e7c9bb7d98c052c8561fc2e96add97b42b71e462011a6d80c988 5f68bdaaf8b47a90aaa6b71f38476d9cfe00079a176e80ba8117615eff840cd8 aa0a83b925e395420b57ba9e43366363430a285953007befde9fdaa65e9c8de3 4d1bbe8cfe4ce7d19ae40656e02eb1cdea2ac57ea136c0ec0fd2a320cf77f137 f5fe1f31728f75b4d3b317405e99deb4dfcb2dad05c6989a07f554ce05936b17 d816397151fd0490894dfaa77f92220c61efc714174bf68b1d5cffcf285deb5e 100be9d3be8e3d9b0f0f47dab60849cb5ed046992bcb4ef991f1d256448210f5 bcb25943550f20fdb25d9f485fddd28d521bcc59377511a293cfea7a350ab796 9922ffdb6dfbf3ea2682f1bd9c6a1e080928d090ce12a6217149fa1931b529b1 579052e77437bd439b7ba2c68bf03a45bbdce00fa0e5c451b4dc9e712ed6c410 562572300a61dcf250b751130184df2547ba876681a64b73238172d4b4aa9c50 3719790529d90f1966fb00bf558f6ffe913fa59575eb356a0b6e030970e1b773 bd16fab5ab1084abbe8026858b3bb0dd30dfad653266cf49366d6b568317e756 402c0c3ade791971fa1c2c18fcdae3419912f7526aa230f8d862e6a8797e958c 172471f740d3bdb2d77f6b32f851926c5ffa309674fb9926e97106b8eb6fda03 a3eb5aae4ba6c236ae2f52b391bf34a2d8c1f93f371abb90871ca2421da1964c e80a0087b8e38e73d4fc36c4e7d21a349358af57d96fa4c2572a9db434330524 944cbe2c9bf0584b615cf0bf0c810f412edd611222553810759e9db3087e5bc7 48f1905070454fd0e52561c1f551f14b07a6c52c12880a94ee5c02e6d92ff248 a525e1f99d550345a421cc2a264d583b88f459d3d753f894c5cecf67b091867c 22d9d0b93817c54e120ad367b2daafedf9e84a8f3eab5977b7ea570f5d9c25d2 52416a5cc9d60cc35a16d70b68e1cefa3cd46606704e43c5f8795a55ee09d52d 8e7856388668b1515b9cdad66672923a5765e4ade578a314e43ef2470b135756 6e0c5fbe9e503ceae4d75aa217b2d162f75b21cbd083777239fd64fecfc8cd56 8ae719fbf36e8242c1bdab9a4b6ea1818f2901a9b4c7b165920745458656b087 d9e01a3a6b8e0a3f0f24aaf4f72a32a5f2924be632584d72dc55fe2812a2e9d0 e1c4f5ccb3a90a1b32c828b2d9826f4333879c79f81b812eafcf95d4384195d6 4661f3124bf0f56837dc746c888f427657a9ec91b68a647ee7f14cf06af9f02a 7797faab4e56ea0319018921de47f3079f47d171f37b6bb71a247c0b8303893b 6668421dc6236507c071591bed68f9e334becc976cda8fd9f1936fc119188faf f95c4d56a8b4f0ab6899486efde64790598776703c8dfecc21da3d25c227f200 66512a1bae207f09ecc4de8a8233a6d1d2baefdf84bdc3380a4cb155e486420d 46c5e6d9b0e92c9d033fad56330f14ade7e52f049455234d6bdba61c91e04d62 330ac713e376dd65d560612b0e86145729c0d5b67e583c85bef323af42c2dec1 2b86492b132faa1e75d4ea27d38cdf26e7caa51e386ac79685085b797afac8d9 8c3dfbb1a8a0a4b1aeb872474f874e391d0d499d639390c6c22b3cf8465d5fd9 dba897941fbd62ddfef72a79945a5d8baed9be98a9026025a98094dea30a92a1 3daf26c465e283e2bad5321aa09d389d39233ddf82596ae2eabb4a736c4da1ed 53dc1698aff3293d78aa11deb7529f5c27ce1591896525dd07be4105040a9236 617e293c5f7aebfae2c5959666c5502af6f29de0b84c7d1c341e6a7e1f51329c 8072cb833074ccdd0adbebe768a854e4450bf30465f2e4e787ea7c50ca0d4d42 5d53c5459d29ac3da7de82722744f4b2795c43f13b343d82d7af97aa51ee492d 252773eb290104180b34263c552361c7dfc8930c57dc5139b24241a5e01dddf1 be1a22efa447e3852e7bd76581fe42ba26ef73ac548655cbb4dbc3d60591c330 c8fe504eaf1eac93e6d2dadddbf85a59739546fdc3b9c71a18c089cead67c7a4 942eadc791dfb4c44bc45b48d909e1c8cbeb2d9e52a0942daab6b4b62721e788 ceaf1e93a479f380d5f135db30a9301781d478f597f65d3ca485aa8ed34a107b a95808c545fbd9c2728d0b41a499526b384072588b9c0fd3b75fb41bef12aab9 68d869b27eed4c829a8c247e0b475730bb6ebce6e06d149ba47a85dd7bb76b55 89862a7a60d5bef7d1f9301666093d788010453e72584a8ce4efc17c4c20a412 4494a190a1f40682735edcc21d32280a01b06a8d5d0de85d8d5568b79abd567e a88e019394acd81c0e5792fa792bee503dcebe817ff20d728de6f580348e84ac a21af405937d89c069861c6d98775fc8622cbe9ccb88920d53d521eabb886a40 9fa9e653b627208aef7287419ee0137493ebb8c0e7aeea437b4bfb6e4d13d1ab 5a9438c751169ca16d3a04ab80a9be7ece4c8a87c145a2bae0b70abba0811cc9 db194308d031bb2f3ea89a5b4f0bb0da9ea5ed6ac0939ac263da2f23e66be093 32fa09bd4377bf956f858c6d8a6a4a9f9dd8214f2ddc9a6e88caa96361dbd666 1451e79e22103160058f2b6922ab1893e5b6d967499d484e8fe3a3c9a6a062a7 cdb3690c5f4232f865d970c9c163c0c95ef1c73c44e8396a1d8b6060ca7061df cb0bbb8799d794beccc584331a4b705ea78daf41466310219f1206ff678d48ce 09cce813f6ae8d0fa1b01de8120362ad8a0ded4be6a94a32ac5a79e36aa0dd54 b682b474d371a95743cd4c1ed51efd398c0d3ed0fa07860d26c385c763a6870b 476bb51b762222b81a5b9ea7ce105a63a2737a71a6d613cbb9d59c8054e291a1 771cf87fd69869c5c1cfad26e08f57d9ef27d7cb5dc569aec137af191feb0c61 2e5ce6ec48e7b32e34783b44972fc2cfba3103f770ec11ea853baddb2f6e2fd1 13058b84a9c1d2e97ee62be828be73fb4a8875b1da3a9977a0cb1f7f60a6c2ea 545382dd7ed3620c254fda0114ad90cd9f4a0f2996a7b02ac1b67c746571afa1 add7ad75b5e3e432a1491ec7ef82b22dafa28d329c75bb9e6759a478c2c344db 089c2e49dfb23962de50946b1878740db92a1c4ff35638c610585f5dd9e70a14 2b81671bed34e595a93b3812831a02ec33135d6d590520169e142043cd429f64 5a6f2814bb9b4b1db729088023caed69be62fec747b83b231df0858da4791d20 4c9cdb9e284cf555fde74954b5d56235aaa13d8ef63a91960b457e6e89b0c55e a17858fa67b96a87b41234896f1a2b2fec400685fa1eb66cdf095d2dd4c74369 26d7898c8fb1462ddb3e560c0f7e68e99525077b7dd509d9bce687f6bb419922 0a64d747159ba74fc6462cfd691a9e02c1dbc4584f789407572f6fd263de0d99 88a1a607b9dd874f6956cfaf4fd5c286e501833ac8c54363ef26037c76b760d7 f69509356ad3fe92d5c5d970628d8ef81c2497d6d2d2f60cc3eb1840d1e41d56 fe8b8803602321db5dc16b6898b319b0141dd9b341173675867b4b6021e3c8e1 337fa00ff97a4e500b71e8549c6c9d2577e42018d9fad5a3ad3b33f04ceb918c d080f63f55f0cfd1b25d84f02a0c1e19c6e6f6de105f93ae824de18304c5c3e4 124ea52a4483355eb3a0121673467a5c43dab2df977225b0ccc7cf7a294e1b4b c0eb160dc068cf88efbc1cf17bc91cb03af74849a70460e8cb94c676002301d6 c7b7487881fbc497ab34c8b074dee3741871b9802605562f542c3cb4e205fdf4 a801dac400b1ffe155daaf5c8637fd43aa319b604cf00ccf52940abbde793ae9 5f5bdeba178ca499024e7621120c39bf0f478258feddf29143880a04265fea05 a4159c737063d8397c31f2838a4a0a778247da589699e9c2910498b8f3120526 47ab2a1fdf2ccc05fd9c039035bc15f54a2ebbae0fd6525459c62f78802c329b 27e3d8f5d977568a71df98eb650a4941d7f4c3698e3a4983c2163de9dbb6690b 5999ae66189fb7cb4e349a7597bd1357a9bf3ac5eec48319721ef2c58dfbc70bc74d1b8e0df52472823e321489a10e9f0da41008f8946654284ce8ddac04820aa536fda7a0fe933a0222f0895e627286cf48a4c29eeff97d9c3f3d357802670080676bd043cda253b551de2cab7ec59717bedc4356cd9960bde034fd0cc69c099ed6b9e2e1149d172d8ad05c651c84a63e34fc8770a483e1d8e67eb2dac1f402aee22b45ca54aaa5faffc23637553027e6b908cc7902118a66af779c8dd1010fbc54f7e83491d4c38bdeec47d6d3a232c6ec99480d61e2f69637ddcb9a463c056e63a0a8a503b584a59959af9f9b3e150fdc3476f202f763eb3e1979687020018628d8bf2437f68fc94b720fd8157d0178645a24a871ebb5e456cc9a123c12003b65812e6aea5bbc97645f55ab77f149872967c6d50b3b10c6b12a9ad2f5730c4f5c7060c3dc841e9f553e1aa69a314ab8d1eaac7c41f5d8b8f9f8a3a9a25809b9b6eb1e10638f55c701d4ee101aae535d222bc10f0a5602e6196579c44bb80cf49f5868ff09f59c518a404c9981bd108306f43b81d2594497877f156080b3074ba5c4b294704fd668c264b8d66b26072dce907423f2e1603e1940de6133e40c85ab9e46379520932e923670708064bb3d385dc734d12f5ffe14d2191b5e92003609b772c0ffa46d612685f31b7c67699dd5a138a7da51c7c5df50eca1489c07620bb5fc9c836054420bf7dc74cbe037a566f1f8724670df7dbb922d70bee10e795b9b638bb4394cdaa8a2b8491fc13f0a4dcbd9b63809bcdfc34de66854fe04cacd11c8ae3a9d94275ad9c5d135724b55a9b16b7a9bdb970363233e8eb3330852d35fdcd83c50ee166109d765c84d2690933c229324e13fe647bd6933e69a016e1c593f8cb40c771a415c9d6db2bfef31c96eb9efef32507bf4e6639b92af06c99e029fb2faba670a6ffaa185f33eccb3078fc911f3dc6bae89d11564766802bcdce02b745a47e4ce0ce1b2353c7a20dbbc5ab22f353fa62d1f2f621bd6220579c3d5fb2ee85fa48f95363933c99c27f75713dd69a825b5603573bc18f5600f15b0346bab393fe3f20a50701a92e4cbdf6d4c456cfc9e5ce6f945a1e6a6080f617c4465e0cc0f65173efe50686439b748fde27d41abcf3ed46429f696f1d00a36524c4927b8efc92ad494952024979f9a536c04665cae8070fbe4cfa5f30a0757a9e0c5b1cf5a49ee27c8e189773ea40a49d47c1718bedb64f0a9db8f0e280a4d1438e4fecd717f491873760c0a23f29e9a1a109ea516e661b2322fe5a6500e67843eaeba480f43c8b79a7fdbfddaa0681eb02bc69bfcea0ebe25aa5054510fe6cd051a431dea1362f945ce9042f2e31f5ee5f99c67e83bdf45fe1c5126cb07b8ebc6f06cd0e0ccc9c1fc0b94dd4c2e5d793285a7f3af2bd32e4cf898ba260176577db53daa2cf86bc844ab902bfa289b82f8350787d2093efff33931bc6f0dd2d928ed665ddc094dca193141e397e0de02c26a12a01efa012c23ca7b94a302a85cbe6630497a331962ec1d8947b0e475fb45d727a395d280476fb4fd4ae80e5a143b109466b62ae9035f1f123bd36fcfc8c14056a5031120782b989eb2510e8a6d46f5d6b017980968cd3a45a90cbd3eba833bc0b117b9445d26fb14d0920351c841cf8e558d757b532854c259c9d5d69b321688e06ca27d95c9ac6aada50bc74953bcf4486d5ab4fd393e270bea931bf9338f64f7d4db7281db4b2c7ab80f5f72b6505f97b5efc8d7ef2c65f9103348cd38741197d30ec506ce1d536fe80876b3a11fb67ff820c19158688dc9d1e46fca25b1e8918386d4498a57f92bf004e163ae54681aa2dfe40ccc6b074654340a5355d1e14ecfc56efa71a6cbb5290115cf806f18fb01553f7356dc77ec818e63166abe92cb2aff9f57986ac9a355036477ca0e506e3cc7dca5416fb09ec7e14e78d6792e814fa2ab1bb196be04c00223a69e22023322005eedf7bc05af44f97e9c2a3446b031caa035fef593a2cf08bff7f04faa6c3d2eed2fa7a11cc104b1b138850bcd6c68360f98ee334c927b08471b801cdf7099f66b9401ff7b29993d0c5ddde00cbc89720c47ba1625e0860931253cb8cba9f99a37c3e94615b19f4caeff7d4040f1449b9b3bc749d943930e87dfc39b9b4a0841be1da790e5e74db40d3a01f11f4616381dcbd777f762f50cf95d7aaa700a095c90c6eba7f49cca4e868d51d7d9c4082144ed20ef5a343a060da9e60dad1eef7f7f648ac7bc1bf48ef1c0fbfc6445ee58ef89cf4939f73e0f02b4981f58a024906ae07157b3bd6e507a76a7f0df3f461eeb413326c5afef068e2fe40a81b2e713008bba1e3b471989c52eaac1d2153a846585f0b5245f6d0ffbb9c081e41490a2ba7037d9e5041ffdd62f33e2826a81999f7e4c773fc22a07f235dc8225731b5f9206ef5da19ecc9d06ab2f502c126ad5db736c513caea90c69710d4125faf3bc1d0b798fb4f165258861a64ac54bf75eeb3e055bd3b85c02fe52795dc6f30bfee281f6419653d09f7c60387d05b7f24ff0ba0477885c6d0839a172dcac42a54ba05d18dab6cf5de3f97031ec7a77fc0f4c6e178c1d285509d3feaca531de82ea55803957fee7967a6f95b13c80d4a1c13e67e112eaa63c08eb0fd4017a935b84d9eb5d0c5940a972a88eef2f9219e64b19e3e38b806cb9033cdde2e9caa75157756289c1ce7762bbe4d7c79dde604f11053379085a1d78006aa8d8d357d7ccca657c3c2825e8fdc9f38427afcaffe8ae6a0b27226b65730112b1c748204e96b172ab95b8ae1bb487fbc081d37739736b842480735cbe250ce13fa4c234d02a7a1fed9bfa300a409215f00e27a5d7f5dbe502beba69547b02e861701d91b64a38adf0e9d43399882faa484e4bfba104da1a1fcc4664f94301dcef81551ded3b7bd10cf2e6141b25f5b89b6c1e6917edd8f41bfe1e3c4c010ebf89719ff66043cdd39517ea4715eca2e9b43219e7e64caaf62ef5c821f7220dbc6919a716c74ba00159409b48d66b03ce22e7d514acc51fd849e837a8362b041203ffb58003f4cf149bd970322c9ae4813de3310929c196702afbdc979e2b0bb01cf421178d6da3d53d98b584c3bf9e38c5a36e579089b3f3f8502f3718370e7e66100448cbba6e3118f7ddf73a8bab99f93666a0342636b81e37f3821d220949fa17200089266b86cd4815363970c6e0ed1fa1d7d574445c5126952e713703a62afb7c3a62f5799e9db880bc8c30f62a6d0cca7c36cf2c0e289ef6b7c1d1022f6fb4cfd03bb8ee395c91c95221e49b647f2b4b31c63b3771c3c544d2987b0b510e216b580a51aab651949a0fa7438690d87d7cdebda22e70c43703b62d7000fb7fdf49f63e323e22f0b8276413d6de168b12d97d6a2f04c05a7751ed506608a7bb4772a177362487ce38c1101f542236b78491f19320daa218784f9cc99700583dab82f97e5580207aa5ad4b1ef11927d759fd0ad6587aab4b745a4ff9e50096da2c41f269f8eec60690f63f5aeb008e0d0acabbd7ba3977adb97520bc8d0a3674a6c384496d2f24eaaa755f553b1c4070a1235799145ad94e70508815500a6c323bd714e635089da3adf97880aa3efde5af4251eb5df2eeb0770d7285b40fe0433f3a3ba38b2251959bec30fa75bd97edad633cfe8c0e234dad30ba823e0d44d78d7799404c630a4b898d7e8e09f488460be1fcac6bf60bb1256ab7f7b40fd05236bd6d340da3f07129f0d7afa226075baf1017feb632aa2633e459806e01b06ae703050fa21ab9ec4be213d2ef69b3ca9f57657e0e1e9cfe37830dcd890df71c586e2062038d870ec88b307a963abc4b42524e2fadf60ac1d24010781d07860fb98c21ce45cdbc3c3856ad27ebc3d187e528962743dc6b9c12bdba048c0b21212b2e47a5ec09e003a3ce3b37e43f882e34eec18a5f54f39ee4ba22cb2102b3c4f49f03162dca79d39addd57d6d0a1334d0911d694d056f9141d60af1e400b6e517b2ae06d5b97a5b23b837c3d71b5319c420772265abeb850c3fa5c5860a97ec9d89da885880ffe78f030d25ece7d10494c653b6582146305ece7a07c300c3c88f14e32e9af451d97fc69de1614cf09be349e10540320acb8258811ced0e332e0f171ea591f3d52197088c121007c393798ff840ebb0f5b273d96f9517055969494040a1fef577006c8234a41d64f33c4d31ee51e47d5ef406cc0d798b02f032f8397538132fe161a02d8f2c400eb7ed592661653b529258193b5a45ae0e4cd98e230c9af59b825620fdab00ba68d740ad1488230dc5c12b2f2a3483ea045f421ae49a9bfde42283f70bc366c0325655aa842ee00a5a586e5a854e0f9406df003a97d48590cf1e6e3413c7ddf75904defceb9a28dc116675bbb591150a08c20508959481f7b91955a4c4b18815b7a7246aa0de880e698ec7616e2fb8780261f09dd21ee3af4b70141dba96360e9278cd3ee874b99a41c394581338cc2108d483dfab6e937b29bfe3cbce99eb3b7c3f4f790ce8b53d2768fc3bd30054c90fa62498cc93b15a4e1f9b854c0c432dad4b8535601db821029bd078590490a00b0c382724fd873cfa5cbfa7122c185da3ec99163594553922b60af8307c9a9c0b23de96100f7406cb461548e95585e0575259f6d25b112f4c90f37ab9e30b420121306b5a7b283304d36bc1dc9423ed0947a38e1ba1ddd0fb1a4518ea849b4d08e485b9c1856b67a6b0f1be947463ff9d1665721607e311d7a7fddd7f0ade3e005c1f47b750e398f9058dc4bcb51c330837d3a4cedb5b684b51c1cb063c93f207b6a341d994e455106e070f827f392c78edb5d808da8f523537935d0b1b5abf03b289e8b6cc24c9c055cf360689e0401ccace6d4737eeddb63c0937e962ecd90c09a4d3dc654cdcb84a8d065903334483cb5697f224a7254b53270cce52314f0b003a01bc4fc4ee0e28ef149bb51ad8e6bcdd17165f9992f8dd54deb4c7ac48049874153fdbf432a2d2e8a48e436f75fab5d2d5e9d90fe03396c79aeb5577c10b55002f4cb9d92c84214eea9e5da53900a02fdf35f9e67f79f80a1f0f45261808e0808937b69ee7c8efc85333984e41e9df1f86a767c6b0e0f41b588fa8eb630f564d58087d36ac2bff411957df2b8631876217130e975270eef78fb25addcb01277cd7c899aa178d93aa693656a26c70b5f64fa0666080b03d13042f3510b306280f54299794785795910b4730a06b77da27397ae708c9c6b66da6a14b06a702d9c714871e0606433eb7258a8b54766d7941c14bd62dd515e5055d050ac493044ddaea211a1daa553813033a61d25801bf6089e75ef325787c096dbcebc84e02ecf261092e535fb5e5f401b569882da6945b241d928a2bf793af8a8ffe3ca10298e35ffe54517916610f15d0d182885f48b198c95e0d5a2d868ad525fbe7b10cda992b360c20ac3e56624ed69982a681780d831a03f8070a2a2c9eabae5e820d03e2ec4ce79ccf50623677316e440fb231cbf1c7d58c97edc889087eccd64c05524bc53fe2390bd5292f65e846b3970daea2738f3e77c2ef88ea08fd6dd13708a7cd573e78b98aa9190a9575082cc07b8ffbe46f3626d0c67da0b1430d82e00b8405aaf726c82ca119d5a9798fdea676f7f30d42488f83c3ecab98f0da88610aa0bd3f65150f69342529fc5245033179466d08a84ac3b7a266bc1aa317d73b0abdf73f815c8259b14e2fc43199d425bdb512af99d0b8382fa1780eae17c0900c4d8d4789e29fa234df2621c6af9a1ef04993cdceb642c046f62f126ee943190df45d55b1cdb145334b5c89f05a39566bdc1564ea30b332d0ae66bc57fbb1890defd671a23f0ef2276df22a7b5217fb981bbe6d29ad7298d4dfa1c13818b53105dd8f7bec8d2467cef5e5829e15450e3aac2650381f7a509dabc89cde724c1e031b769f6bbda1bba808287ca12c411d90ab3b59a711651ba42014b1ced01cf609016caeb176c52a29f90688d793d183922ab95ea3ec95aad1f5f1220f76bd5701be203aa5638912301840c4413a42368dec083262c24e0ff87c71472964023c0400b4cdc1ee395516a8e6960014fc30fd5adf821ae4b318043ca21297c94a3404b5e36d2e0e27c5698e07d3eff8fed959a137655ed590ffa76313d9d714b2a80e395e7b379f9f9b70fde1a485f1ce2f78722f8ce3ef07312e6687f075591b06081477920cddb12818edab5f1ce6ab7f6fb09a4728e9a73b41b07e4ca06b954809a318c4c6a3d32fd13d83505405bbb4a31f65bed10dcafac6cfee18f07a6ba807f519acbc199268a382d201a20b9e7462d5f1ec00778678119e9a0917f21ada0395804f4623fb81ac991dfbee2a24852014ff78c51db1bd840472c79bd115fc046556dd12c8debbd10285504c3227924b4c23634c754f5ad76ff2b03484deab0f800d7fef84454e347a1d328696aee718c27d7cf93f7fe492118cf9ae51ffe70db3873c4c047abc18c8810e8f35c476cf9a08b1ad3ea606418bcdd802ea1d1a00dc65063b5317f3f3d9dad95a5c56553da9c7401cc60465a6f3ff95a48c46e80f02a19f30cf24dd02a6852c5641254479f5b8305572a038a77866f96ccffcdd06380459fb677fc9df77791a6e603630128bdfcfe454c086b064c737a90f9850042dbf3cc4b3dc82d806ae89959e341cf24f0e67cddc58c7229f1f77aed0c47b0a2fbe8cffb6d7d9abbd497f496a4fa210076b6b5bb57c3e5139849755925969001969008fa55ed8ced285359afd548cc00e0977bfdb36278ae3399777e63f7704ec4877285cbacc7f6e7c87ef43a61ccc796ff1c0e6f6a3f6f9bcf714a4a3ad073a75590ef41e6cfd9a0973bf1d5081d063dd26cb3d7d6c0320987c021935e504c8fa95181fc44f04cdcaa57ddd50c4d84202e30094c8df13902d582d76a1860853809750437a306d9ecbc957df7ca54793cd8a2f478cde1c72fe225c35d70d0d0c34b17fbb82780e6f8731218307ab03ae297a54447255fde8b77752600e98054148bcd95535174f1ee7000a341dc2361b032eff3b8a83e1ddfdc4301d424f0b6242b1d4e84e23b2aa1b8f78ea1589c3c3142403c25724b6fe8f694415ecba00e7d611dbf6e38a7a73b8858c2c317baadabd3bd61a5b010a54413c962cffbd0faac577d2053b99cf4c095e4a870321632be7ba2a7bb8d58881824584be634f0a073bc7b7a3f058d02fcd5de3b44d4fcccbeb620702be5588b055a4f6ce254a0cda216d926e2d8898306b7e4fef3df647b2fa7cfa128cdc2a1c4549325799c90332c77a3b667a4a2c1089690474a27a570d6bd31225d4eb68eb5b5d0c82ed01066e29252158ab0d48735dbb1736a6925a98bec10805ee3491fdd85a892c0b9d05c1e1dbd4d4f7cf9ace2d249fd0be7a841da6c084ecda08f52624118470378b0ac03bd1c665a9482c14d9f9cb56ae916dda3d4252667e7f50ebacb01afa2ecc044480b149553df086bea2cb61825d443933855b5b491a0efe2fca817342b43d063b519f22e0d2bc7f3c269134bb9483cf3e44b30653cf5256efeede35dc8a0c027164daa7f35bd1099d15b401437a7a09adad3d4e7b4987deb97068e581107402f4efdede9189793087241b1a94738176b4079c51bc7a0fe0d4b3644d9523050498a8c0d5def3ef46433554794fc9bc076a4050f144241c5df0241175cd7d61046b78dab9d8105dcbb892a712a563fe5a63d2953cb8ff4a7b875968795deeae09d5dcfc063905b86ce214b87b1a6f53704c66bbeab9cf282a47fa4c73f076280f281ec7822a04e9c19b841745f8efcdafb3dc90d31fc5126e8b105966bed94103daf387765c13c73c3f08750a43f644d17eb6e1122b0bd1c65ac2ad00d58aff04e0df2e3e06642f9be67aebf9fa238d0392293ded1c09d7cc01f14a4984436809f3c96f8ef02a4908879171dff484373776dd54f28a63e67c3d791b284f93590745ef733f25cdb783ff76d13f2ac2d5de8eac911a30ffa7daa8a5bdc964f22d0d06197ce2ebd0b8b825e77bf554d1f0e4852143ffff4581ae9980c36825a47805a1210e6991ba479e5a4cf0d674ed62f8b704b99decab127d3a05783c415b0a0a3496c6f2fa731ca28748d3294e82f51d9757518260f5595fba53d691d50cbe0d097c72e356177c5882805dc661028275821e1f9adf870e7e1d8378395445a90e1773faf0f321ccb798d22fd10551af6ec1a20cb80adc67d5cfcc5fa85383d70bbd1304db2b46e317b6433518b3a2bb11c30210fef4b6ea28614aa71459856e0de4b8e77a5a4bda5c4cf0d8281e8e9e3d5a8895410dcc7727b15619af19c51f0f8a51d18cf6f10be900a08975d31bf37d673a21dc4b27977c1065b51fed977f02086e14d7f986d3600b26d26378eef69f86c2cc29c89a72e92d9c65b7cec883079b50be227511741e39aedb690625983b799b4cba8f940c588cdd4149eee08b0413261a1f3e2764dfd157055d463a4ce636c15af704f72a7a45def9f2028e00078b9d898e4558fab9079e09f17aa396f35bd3196a32412a28851487a0bab7970ad1dd4f9ee9a0a48acd51e5a783ad84b56719d5bdc6797c97fec14298078dd408377b0047800b7b8174a456285981e2ed91780329652682f4c64ff69b037e410ae50019ef054813c95f96a5c573eb3d2e4a5c0d6413cfff6f9b3320b2d410100433d4084e17e861feb4e08ae91fd8dc996a5793c5b24fa8d02292a79687574c01e48048b68d59b0bed5cf67135677bce116da6890910dbc2048d9e40b1695e60a035e1cee677db45542daa29ef594d5cc1559873e03137d14db096d3a337c0804b0040871c70f1632303e6a5ff87cfba28cfc760152120f1862649bb2e1b9dd0323489124844146d1396aeb50f2b6cffb9b417e8f76255e28e9d0cada8326ab0195837c996e1079aafb415f18b9224553c7c315341f717a2da1e5a373f557b50b2b6ac29f859fdb32dde98f5afff4a34ce77f31dccb3f29d0c00d00b657e15b0d1f4c9ae3252f3e450d2c42afce537b2be6cd0b64e73b08c416e30e4eef32c50c69aeb21b14fd85b02ede851c1ac559d6ae9aeac1608bded90b50505cfd936b0ee8307e2e1739136d8d3c2989a39503e094522c5575e069cd84390da880b12a0e0df0ba520a5ddb1736ca6d39c7ebc90735fd36998dbc582a363ac8ecfab1da06a5b08be94098263aa40af5d7ff5b4cfde6d543ac347997fa5f7f320d3f1dbd01dae712f7d0e065b55d9d61700671983088881b363e00d2f18aa3be284a2c2a0a52a74d213b09521b5cbcb40fab793a3ba8550471c01c3079e683c1f453e0d8001038731d062451de0ac1233926668277a7176afe5ebdbcbd48d48a04f00aab07 true +check_ring_signature f097eb8f22e596fed4e0dce861cc8f47b69883ef61412b25fe96c8f7b0d695ef 86b663c25ddaf5fcd6f9ca74b02216ba9162fe9e99aa3898aec9af1547267356 8 aa30cb0f7047e5bd7d2722666e92ec9b9499496374ffca831a5b60baf6897676 1902fde4cc605aa897e1434f367cd502c8a5178246857c6d85ea0279c1422876 f4560fc2c2669a02d945145ef466eb444a70fcba30194082ec92febe3d78922c 25512c2882a82828496c1ff23ef4336c87050f6b4018d56faab42a264d16db3e 9ec79db14d96ab6daf352f77e74986b692949d552797ee6149e44225fc7bdc38 49966acf375b688807ea832aa16b61aad4e9bf638e7952d8525226299edf57e5 94c1e487b97fcbcde639226b6b1952fed78ec762c17ee545dfad71dd3ff8d807 eb2295447e62753c6d75e9a269f54e1a79d45a5a4e7d6126f7c1accc788d2eaf aeb0627642bc10904040bb279a3b5c7ed1794c8b0609a914df70b4cc7357480e754ed68a997ea17fb5efd2e1625e8f0fa88c8450c24ab83591c14165639ae507fbca1e2e1b7be0cc68f8d2c20bc0471f8f98c665544c73bf4d52cf19282298088bddfffda00ea6981bc9d9b3b47fd40ccea8649311ec8887026fbca6742e9505d6fd5f0992a346c7711c9bf4a8cc8187c47fefe2204619c276ad808bc3f2fd075c2fa72fd01123b9a79006029f92e0f986083c490108b503775a3dbddca5a9083fe0c0015f90bfe85cce70bc5f98b776499c8c216097884f6c53ceb6ce1db60ddc500a66c6c4993172e1f640a5cae9f1b3118a38f74f03e48751e99ec789b70820ba6520572ec65cce8ccbf6deddfc00692f182867348f00d6b01e114fc14b096da38725bfdc72393bace11a4c62b081072459aad93677ce1eeda126385aea0da76351f17dd7503a07f0968de18a437a499c274a816b9f8e0d438d47aeb3830063838f47facd404608b4f57131a9ee0e8760f11a37428e385d1d1f084d5b9d0b3f60b94296af939e20b43153ff162b297e15516e60a785d15b8745d948ce174392dfa470d1d33685efab27db5175f33b3b0557b4337d59f009235d54c6d3a407f77abd2933003d0becf78351402fae64aa42a747f4d5784e69c46f3bd69d95024e92202ed033763ab7b2cd398615954a42be717dc98b19af22476a5ea664e104 false +check_ring_signature 77ec496b273a8fd100f4fad84c2d11e0d7f8694b302e7c2dee094a683daecf44 4c6745ea54e28a16f411252f73a7fa1550bd0ee28eac87ad9b8744fcbf861d34 15 bb325cc0f4ad9fb2287c7f505298927052d8fa6425beae4c80d06200bf1719a4 626939edfec70311bb42e2bec55e5cf2a02e99c588325ba57869aa61adce1871 5c1ec5d23ef12cda410f822ccba359d53adca16f5b9b1aa27453f8d32305fc72 bc3a825f135d1349c2dd6064a8c3821300cc33f282c36e643dfcd333e241ea84 dbb5ba36e6b8a9c0ca023e1de271d6bb9501a8b479bd2c0e7086b4b281e6ba1d 702e891ba36af12d54f407b6b05f1064c3cd9ec3fde98d552fb0ab4c1760a5fe b99775c71be15ae3013de3f38fe667fe62805ce42a8535c64c37b88f9e222524 15529b318697e148d7e9ecee8a1ef2654f183ef2cdec15365e2b9ff49e499d46 5b0fd1996ea6e472f4e6f9af4b34201bc04dcea2d9b8036412db9a2903086101 5745b68ee6e014062dd6ab29c6114383a3e65dffdf46381ffe65f7e95acc6e87 ed735de024f60da64167fb963d669eb5e534e0c81cf7bfe9c1a974637451bb54 9166bf5b20c2333c03bbe40aeb59f3d966e2d0bdc61aa0eaa94255bab5a7de84 0e2924eaba16fb06567fcbc73be22fd4b0b647bd918ef0bd2ea404735416a067 5d68837ab4e070e4b25a44f67f25d9527547dafc9187f565dacf5f9499bdc02f d2aeee98eaff4159499091cf3773384d42ec05095eebbe0e5b024b7d025e7fd3 8b5cb4c2fe584e07f9d9deaa721b1f11d4ab6f7c9e7904e1025349072be0d103747fc6271e329cc494d14da5e6fa17045794dbacb3ee4f7803f8cf0b9979ec0050c91b758a2a9695bcfb1a05f2f62a228424bd51f0137b35e32dbcc84110d409a4dd3ea64eca46b4b1108d268c22084eb1d032e2219e13b599c5fb12fc1c490e7ffdfd1ca0350dbfb77a653b8cc9cb2a45107128f45e3f85eff9f3f00e53ae09e070c13c995a3fee88e01d98be54ce2c820c756d3451efcfad85851f53ba350fb2663cd1ca7a63542f01aaa4007af5978a54702810f2c3cb0f2df110c111a50f5d2b0e19ac943933c51653e7afd9c3d89ac09c3a93f272549cf6a814d45de30ab95ddfeca7120e5827782e7da042423af6d386a74b0f1fc3e236b48f5d04360067867ad5d898c77d71f69fb79a8947c44831ca19fb03c0b8046c86334d7bc402ab964e5b4bcbca58493e652fee55f34df05bd2dad0ff1c41781455252dc24d0d73164ae00c935551c3c01d66fdf7d63ac7fc381b054b0d9a97f0cf7611b5fa009e61fe2df537840e853810a7db0142abb3a74ddc20fa339071641408fdf4a90dbe2472b89b97c72349e246869b183f32b7abf090ab4109e4a1bb926826c258079dddde27d92b43dc169e2a8748f6d952d95d9000d8837868b1899277e4d7fc02925b465237851689454cb7b78b3fba25a78b1c4a9f8ada615070d1df5eb55406435f971c6816393921e25174efc0f2a8f68bcb3b99da0355e61a0589afee7c00b556c575b416ba3f82dfb4c30de3f126a2fc0379fc6aaea72cfdb5e435ffd00b1a15a121b7f8d690483c509a2300edb2afb571e1b3bcc6a84b81c409c03622050b65542be54fc76732f00bc4e38ba3ff660abddc7f7db6aebe47baa22c485908b3975692575004a9a7e44e30d133892f33a8cd69d078b94cf4b29744c369e50fcaa4749b454cc5285a40d8e37927ce69c466436ef88c3b099f02b106f9f5c404e3c4de92a22207ed82b8eec4de7ce8909790eadaa85b5c3f41813bf5c742aa056be4d0f7d6bd066de51b82b0f27cdb33555de23a3e9f34c219278ae0886f8100b77f5d37d518b85ab34cc612d49810036db7948a4eaa34016374abba3f694b068bccf5221f9b4f4e80f024fffe69cde6dea2f18fcea537ed087d71b017e5920ec1869fa1d7603e1c2a1e2f39ea3aaea1f282fb08ac897cbcf5c3234929dab90f6333b828bb7a4fa4ac10eb1d8e3d522e25302421563b708c1408b1fea7e9790882832d3bb8a837180f0a266d70652f78bb83a0f3172d80b59f306d0b5927850817f3cd2dcaa3f0736d76c643c1de9fd5ebdf0253168ad76742a759e516459508 true +check_ring_signature 0dfa832eed898b340dbb7d0924c218023a3fb2163577d5d37efa7f2b79a2ff1d 8e48e5b86e9dfc85852a451d977bbb6621b6dec8a400acf239442d34fcb0eae1 1 b2ce365c61ef88cd2cc0e11ffb6947e8ba52a997880d56665d266437e2667fc6 425a4c3c021837973a9ddd91048b289168d2c7ac7343c7e7283f724f7b0c760df3df941ce0933f3bb3ba6a66f86a329f241b8bd87dd0ab382a5b50f2ba521d06 false +check_ring_signature 55c37abee88451a4b7f2f33e3fcf0a93aabfec2274376c71b50e9279efe3e131 76555da68ad415887fa45d4a393a96d31055d35a79fe6c45a9b15177c3279f09 2 2851e973248a91d91a3577e2f29e3d1e4f3cc9fabc563426beb5768293498090 4ff5753b0a2b2206a4eeb62bf5c31807c6581552c8a3916ce408dc1f5c9287e5 30124065e39e05b74c6d8a864496be5db071a85a886583b79affee432fda6209c7ccc641f38e4c7d192cffdacc8498f51efe9236a1a4c8ecf982a9639fce590d215ce3a6909eb7cff05096ff7db5e67234ef2de1e1bfae798e03949fcea22d0ad0b51fbcec1ebbf640d487bb9820624d74ecf18959fb7c4e0c97a51e3af1920f true +check_ring_signature e0eab77abb10152e22d9cda7e358c97a5d3c846d4ebd1ac08497768e4af2d937 958536181f427dd4a6c41645fb39694c673a1f06cfeee71c26b39edbbbc9efdf 1 63da7579135a099c50021461bfd165598f54b1ef3f1071a4ed3fff5c34cd9627 dfaacb466df752fecdb4ff4731a64a6d0b2c1cf3aac82e2c6090cc79fe908f090a08fe175afa6b58485d1647da5ba19ddeb434358326bdf7bd4ca0568831590f true +check_ring_signature 2764a233875e7599fc15fc8b2fd4ade5db509431990394a18decbf6c5c9df933 a52c9e24e14ae18ee00b4f65578a3eb02003f1e4ebb08bbbc1f97420c5dede92 6 2eed9e50353e82274194b101def9d890ec6d66d162e21cc074e3f5c90207eca5 bf54b8c6425a8fad76e0a15166fda1e1cd056381b919d5e1451f191613b2155e 04415ba17bdc9582e36a936746851250c92b7538c56c3ed58a06fa3182a1033f 3a47451baf7852e3ade072f4732109212ac7801a0aca4562982ffc10aebe4866 ce75728469da79c020bc894ca00132810a674f6c4d80b1d184ea83825bd62209 df08821534ff7b80cc7ecc12185cd8cc71722acdd2386cce801b0ba6794036f6 78c345e716ee5e365c3b78cb2eedb3cbe6d18d8c5e1bc3c22f1350aef42e280605b9721bfe4aaef3843e2e0c8cda8c72e0fc7822cd0804a4fb09b978d18aab0a2981148399824c5a4d78018abb4f67963895aa4e70e89374b98acbdfd415a50c82315e17e6a71a1472200cf7a3ea850cb4b09d0317e7cf11bb40f1d0bea9a602bfa729c578bd0e1993fc1889faf95f1972c8e50ce789112754646060e961c30ae9ed93a6044603163711f98c5be52d5b5f0a5370d52ac32e06cb63dc3b0b000bacc0da1d9de90a8f2b3bf9c4801eaad98c3bdd228b3367788ad4b3954c62e60374b4f8be8b523e7575332e674a957d39b3500e5274ac359f203cde76f5406a01f41471b35e10711878ebdaab79961ea183e0130170e6ee9c8899ec28978005023f408edba1fb824c84245b50c8f0a7d509a9952bc96e3f7e88ba83ecff515e03ed04dad32cb536d02cc875996fd2a10ca392f5f6041ec98b8216c95ea6834e0350f760d07dc3dfe14deeb34c000e81f01f5ac15da7751b3b500614bfe4a5ce00 false +check_ring_signature 5aa1d3f25b5bf818a0f82d094fc66c90bc450f7fd178b2cb93bebfa0a74801ae 77ba2f5751708c6453b0a6f6a57fd38bdcf7f5a2410fa0a6024e1310b188692b 1 6ec8842968f801987a7a75817a184da30d887532668f8f70f4a3ac8842b99798 ff9ac088153140717c6f618a0d791147a2d522af4a2170fc76c8884519299ab9492cc157e684aab803ab3c5652454851b3acf0366ea9935bf4842d8a6687e3c5 false +check_ring_signature f4528d0bc6e0c1bf3fe74eafb8952c9430a1a4bbdd6ff7476b877fc3c0afc390 4e5248b2f447c5b190a794b47dd22e3df2d226608102716f463247abef8209ae 9 02f2273e32e3ac58996c28ad0d9aeba3c4364f26e0488816c75f3e9ffb36c5d2 48a4856f818510097874b27919d87dd9b07acb6da2c67601538de7875f59c074 0819621613d9b45f1e0f7a1baca1c7c70380ea9ee4182a4bbd13b2e93f01f04c 12ae19787a3db75788840d429b5593d2de77da7815e69930734077b936c6d75c 22651a02a6ad5a91bf3eee46616a5c0b3ba6ac18ec016fe8e4c8344401b7be90 fa2d15464cb8bee7a5f217d3fbaabc5c250ce36db2eb0c144c4a7e35cd94ead6 3b285f3ab6597e3cd9753174d6da3023a00cd5c0834b06bab8be048d1402f455 c5bc6f3844627b299944af54187034d776968f7695b7ef02f89af4779b1bfac6 8007c06655d7dc08d658507ca009c57c06b3b7079263e3c1b2d9b588242835ee 4ccd66f7fe503eed3d0c0b2880e73ae40a1feeecba42f3e7b1e32a8e8e71ef08518a1ea9534ef262cd6d95f450e481ef8b7dfd230f5c8cb82217c14cbe8a020dcdb0cce8c2eafd0e6e8e34caa5ad4b2b9716bce3f98506a98985e0dd2ebb11d0bf622bafbcc43d5b934e6886d4b303179d90b702fb05edf451bffea6c656010d326cbed073d910cc387040c4ddb85a6937721c8c6899c934bfdc8949d8bafe0e62c8e73f70ab212bda1f85c32eb4d8bd95f3ccf1ef0898fac556b4f20b2841029a13218ec436a8e577786ff12f3ef66b07d5e8419fa65144f73cead75dd79d074171ad1fa9488a285b37a5081c7d4af5af1c4566cb88349844f0e9ded576710534da6b1cdf10b9bc31888f284fcc18c3cfabf5c7c68a22e8a2e3e4f70d09a6073f377841125a69cca799266dc97d8a651c3d40bb4c6a04ffd0d8aed24c864b05c7cd5d60d617f46fa9c3a0c651f756932d1090f535d263312e4c94f70d80120e696c30e15d9742b5dc8b79ed525bfcef6fabfba88b262f26895a508e575f2f025d17c942854891e3ffb9e5e3f796d7fbf75f60a02c5a33ca17a340b54c46e15fa25c32130a7c6ec7ecc6b59f5ef2254c515d211107df3db0d7603101512c0e0e4a0b844253795b03e068082fc1e19c3ea8d6c6a436984eead07bdaa8cd48c8020b675676ecc3a080d3bbb41dcf629d6a7f1896564b449078b6b4ae631b627a08581d2f4817d5d6e7e638f002c5abc54c618feda895bdfdbb568b02f9221bd70944ab034ce67ba9544ba95bc8c25ced86a45e6ad50c0e4d5721de929cc25f8e05 false +check_ring_signature 830b6dfdc76a1af0e66fe4d3f98588f392f1747be182570904086e7ec47f4b5b a0f6e4191c79828267c67d588c5a55639ffa9c2f8ce343263e455c58d239996c 24 88ebd22abf197598a2c23392e849fd70768f0e771ccb06deb524e2ffe87d9138 4b42346433af913fcb0a65c472a87e2d7a4e5a5d1c36d102fe52ba0cb4dcdad7 5d123ba66da8cb71581ec50dd9ef621cdff9a539df19f195a787d2104f8df1f2 62b7d342f24fd2415afef98e3393e933668ee846ca75c636e7c755deca89ab37 e7343f4c23d91d43965ce1bd1fa8651fefa70e9cee51faa72686965f4adb92a5 aa921a12437868794a1e63c75ba032f1bf9f4400cd9471ccef67f292d75d111b 1ec140e6d66168501a2de17a4bfb66d566e7edd9e1a7ca4a267f4ff4d6e3ef7c e54accd6517b90c795fbfde9b07563be0d5a95aea937e770abd5c2d76605903f 2441416fb53a461bae4ac8d29cef01515dd72184a125d8892f033305c94a4060 80d1a5d220e7f4cc38e6191a2f61a2767a57dee09f002a0c996ac593149d34c4 2710382ff455056409f1aa5579f6c9bf759b8cc07f46a26b392a720b5a9c90c4 1ce0d4de9de5328ccc57c73dc6aad357a61e1b6c65c43e2349819cbf5647346e 10ac558784f4abc4c3a6efe950faa9e6325dc1db4a3c7e9d325c14c116de7753 6e504ea056abc77fe60e12484e98996a92c23d69ec18e7b9e161a8b53d41845e a965759134002cbbd10bcadcf564eb6e42b2ddc779f77c5d1ebfbb6cfe704629 9d72457f6709fda9a16498f3f8abacfaf019900ae9d9f9692bcf45f482c62e4e f2364262d262c092c1ef870dfbebc80b4f08e1d14e9b3233070d15a7196cc831 358a6d66713333732a18ee56dbaad192a12db3c5e14b6d860f3267f52cfa58c4 816e3896b34263da410ce7575db3aaad082b4548dc31a58522e2c5b678a9a803 bdbdd8a2cb7f9289dab946a0a34f72cb049cb076fd1074b5488a81d587ddd7e2 532ec13af866f41be41cf04f6ad79c68af91afb844df0d310fc90451cab62639 27afc960f554982891b05145d0b823e2649dcddc872117bf465ac3b15e5fee48 fc78ccab3e90a95b1400f448a8cbf4bafb4232ae42f913326bf8f415432b5c7b 14219ebaded071256fc743922b1aa45363bd7b0f60a60673653914346d04ff5d 2840e45212d50da7dc2fbcf6697778a4bf0f57a2323b652bbca1505e0b2c4b0288b5e377c2d2afd25be7d342bc4c73e868fac454457b052149e22fc1deb4da02ee4e24c6c7f29644956093f3154c7787b5690fdd3ad27645ef41b5ffe3588b09680aeb8ee6382282c049752ac02b1c1478ab92b0a6043fbf7c6e91dc14349c08df6998940759f229bfcc9fad33af4f487c691fd52e7c35f59b20b6953411f106266fc26ed2880e2a257f0502b576ee5bb4493ed04ce7971e422776e196328e00885c3cf1cd1d1d021222dde9f18865c58cbec221a586910a69fa8d9ae295310a72fb7b1a316e02f4d545c4f44d11d9381760e4687888778d6d326eb3b91f69002d592677353120118a59ee89fb4ba797cb49c819b5d4d0255c9d278ea46c950ddca0d3126d4c0cb1a2945637a9390c39919499dea6c7ad42caabd698ae2a8f00f0748120cd8bf7d66e0277ecd093660b297364914e22645399fda34b68eea5024cfe70b0a2222c960d423345964627061e11d634d4e666bcabbc9a1c44def7088c01556770b3c46994082c451040e29be300a4f4f8638052aa2cd8b730ead80154c4c430ee0cd4cf605c854b84dfd74153603f24d81bb99d235d9b7b4ab986059547b4bca5f36cea430dacf6a06cf8588ce79284af40e0d4a2184b54fe504809bd5897901a7b2b85a77d9cffcc972987ffc8cc1d3ac20facaefef8ad6631330cf3e291bbf7d6318338436b6b4d14618e2bd92e9873de5d48144652e74b2b5602ff07b4a981a559ea62e6f67fd4054404ba066a58e9a2a681a6a6405e2a3b9709f2ed2d1e3d47767da41d1f43110ec3dfc7b7c874f3ec49886d4fefa928fd6809ec8c67f54e24b4c7d2efa3b05ed1c8046c61ad70a20e72e508c0166e14ac0704136c5a879b3e0889e1bc720778e566cb2618afc9bedf3b857448a455f891e90d85ac52bed579d287db492a27c8e05b3aa970adc4c41ee0e92d2103120bcef4020e12b9824464d27bdb884d3f97871d1f9b2600aa2694f786c598eeff39e7c704ab3247e5301a2a56b9a561f04e31ea73e1145abc5bda2e282f3df8bb6fbfcb00f308466b1a8ac43d402b6960405c8cc942bfa2f774656a74e9fdf553c0f9b60d0059a3995a329f5119a6966038b836559f6d9d2d491dfba3044a65f4d26c960e5286f9c78aabedfbbe1c2df38d3f569b34cbaa47add0dff1895d1ca96cdae108b5bd75e7d32d5901f4b9afd90e6e336501d037351a1621df6d2687c616765a08da94ab26a47fb61618ef40af4f122a7efa671db8b690eb6aa712d51a2417d204656e577a63f66ff356785f963cb2dfcf39fcdb1087b0e88268f0d750cec31e00a7684ad11bb12109777b2164186b49ce4ca3cc83161e23480a2fb36a6e1bc8085cf7b8fb53fe2d48878632b6057bc0e0e4dc6dbde7a8970fecf0223b616dd20eb30dfb2315e2b7c5a8cb1e71433a38babdef1ea73129ea77d5b96d965ea4c80b8ea03cc74d5ec65b7820d6422f0400fc6bf9fd30e1b814f235dab5992e39cf072cd42d504ac29306958c01ac9470bc2d4d54d3bc23fbd50847f6c3e4f54c360f4dfb359bab56a1bdb48f2c0de865221fae1c126cb4dc27650303eb37d73d2805053fb7106d44ea1295a3c6877f83ad1ba1b9727efe34df3a8a749662cdfa9b0c2a8ba13a171d462d060a106536736330a5011f3e240143f8aa93ba0d2df7ce0801937ef96ddf1a909358a469fa0a9c0c876bb97d56d07941758b592b5965a1053c00c5ae3575a74f17843fec0d75dbc06eea8ba497003acbcaa35bb94a94650012b329da4fbd102f7fc0df324f9ce32c39e277620d9e38edd6311075c78b1a0a4e5b3699945ceee3e96c471374d16f2127f2a689cabcaf1347ea538bbc21ca0021498940ce47f255ffee82fdf86abf622f3d0ce8d0feb536ec74ce7226ac9b0bf1846cfa3ab7ecf91e8c5c83b8b1ee44e6e4948f867f0247b960c708b806cf0922def058e90da715872695f775825d85117f0c9a79bce532089b969aa12d6f099608ffd66be3bfc8438b1f213df1e1ac0ee6a182d4efe5f8e0ba47516c1575045ff4436ee94aee43216e3c512133f5cce652d72acb38f4734c39c716493d1c05c0e3ea11857e9f26fe7875bf1d7d6549c8555f17e9b4ed18fdc000c583619a0a false +check_ring_signature 2fc4504e097c4c556a696155cb37dee4e0d3336af25003e5a02416c3f88bcf4f f9c71d09e1c46c37a0a41783fc05ffe7492252f0c11c7cd7118fc9ed785fff6e 4 be6c1848df532ed9bfd03fe4b195ec55f7a4b470f989b1f4ab53b09c943ae38e c60072b7ea8ffa38ef837b0c42bbc5da9f919c3af8e1cfea0ba1db667fe0f919 c4b877823db44bb023a810fcaad954c185e877cd00c47c054093927b3a24e434 730a80f833c9f3d981d8be6fc530c5d43d0f65744a91c48c19b580ff8a4af010 89c9ffab69aeb5e189602c72445d9ef5477844453dd214df7c3ff4568a0783050804d659b713fc2edc0fd2cb65b745f31300e6be0ea23d1b01fe186733ae51001a995f84ae5761dca2a0ad4daf40d9da265253021c0c50319cd8846f28e53e047fdd343bc97377da30c584e9eb3a7b5eb260348e227c7cbab98188b2a2bd4107e6bc55df81fce463f7980edb872e177e270464679128ca389d87eca2f28c540dcdd0c19f6b61d93cc4ad10c62797076fc0e05457dad19b8bbf57e02add22c590d3a72b06b13bd807273bc564086a18ae7341415b366758c7e864f1fe0c82530968d9789db012e9f60acbc584dfadb2ad1216c30d216ffb70a9f94a5228f0cf00 false +check_ring_signature a7833b0753c472b0db8baa76c2cc9983c04332772aba5cab4e2c4903baa84e30 13a4e3ce811c517b8575939d22f62a795fe4d105e7478d1987aa710ee9c1efe7 60 1fa310d7b3f0912183af23c9242ee1ddbbcf53887c7bcf68aafb88c082cbcc0c ee9027ba29af87dfc38aee563ec930fbbb7af883ce6fefa9beb91f827f6e3c1d 53d68e4f6d764a555f0011ce7d03778bcf7033922149a6bb998faa5e69343194 ce29cc2ca7bb354bcb4bae9f683405c6b8d93544513f2fe4522121f7f96f6f2c 3ecb0a02e160423140e48ff0078f4797d3cdd9ab5ba399035c9020cdd6ccd08a 18c0e9f99c7f558265d60102c2f00666b816aa2d8be1f74e2c58b6a6fb7debf0 27bbf9a03d1cb6f540cbd3d5802e08f725ee34bf0a58c2e7d7c76cc62699bf26 5cd5223c5b9077906bd83bbfe2151d78d91ae3fd0942dfc3d00be9cbcad383e9 cd5c483237d8cdd3776e3508f754522a8be7a5aa13df0030ba8c6d0ac1ca5876 61d9c163a3b3290c7fec9de339ee4ad6ae15d4ddaecb85a0165a14feaabf992a 27930bfca2c81dc1ca8da621babb0271b17dd5de03d84dd1d8bfe031af96abaf dbf38ebc31e2598fdb8f77faf455dd50c61f0d05cf922c012d09c2d87bbaa46f 7a7b418b4244f3929a8f142695d04a83a236afe0312f4a5e2a7f5130fadd50f9 a2860eca7dc94f6ead2751f5f84c1eab0be1328e789e0e41dd9f60c0040b8506 bfc07834593627a5e9a861f190e75e1df2275d546894ebf5ac1895a581c99625 30fea1fc21f7d444f130b8c6a4d5f633ea63fecab12b73c06b9523365181fa33 4d36afa4c93ee67a79a3dcfdbdfa7324fcaedf5057872fa0c1fedb0a358f03ef 83e1f9f1436068d4440809d1f62a7e41c260c59f2a748a3158d513a7da3898fc 6d2d237f808d28beb62c269a73240c64c4f4ee35ce0439aa705cfacc6fca8131 f1e2f3f39280dbdc76a0977f5ef99dfcbcfb55b0cc3359354fbcbc95a1a0dc94 6494117e3e2b90f31ea97de612040b2f1f8ad2bda759d632c41748d3597c4189 2e523679e86f2b4ec4a19d10740ad327feff9de9203e4996a37b9381ff2ddfb4 4231b83fd1ea9e8a5a6e396ac8dd57f69996d6948a457e654c2f7308caa447ec df1f213cd13261975325904b41b57bf0c4ed5ed3fbc73d8d1e582591861ad40b ef9ac4ef07a959c5f96360dbbfaebdd2804384676e128e0adc7c837aa5e4216b 1322bd38e8b06e8cca0b577450f9d47179aa5b69d2e3fdb44696e05e44eab030 f95f166b2a3496e8ee03c9e670fed7102bc3e37c9752eabaebe596d82d481e95 a8f55fb03f52e14cc5b96cfe6abe7f5e5b3555b88d0d203aac55aadc69ffd405 38a51d6576ff697cf6b3e5597f613fde916cdc101830056b4e4ba41565d321af d6f94c666bbc0c0333cfd0add78ce2c74927f0f826ccc3c54dc2ee0b272f37eb c90b4d29d9a03ae4d5e3dae0d6a5572585253940c89bcecd2b77f1aa61312230 bcc8b6f845498377c774646d6175ddd83c52941d415b978214a0dda43a64eac2 c27b295f836c9bd06b5b7c31c135a8a957dfe20c11edb4871254c1570de3c960 a8f3e9f46dc6cc294bcbb912d9f9ffe2cb1aa984a1f234366ee330f773a78e6a b93a6d7e06840c5bf9a7aaefce6ec568b77dbe60058a239c9eb7c3e7d9dd8bfe ce5ee129a71bd98c3e861f80fe6c83d455c26a84bc2976fc884a89595765b74e 1f7789f4e21076926c1381d2cca6995bab41db65d05ee31749b05c0a0644e9ff 6be48881ec8b8c0e9b40cee0d3ee2ef35b6c049b46f8c1982143ab61ccc1f3a4 1a0b05ad9d22e6a87458f48c152bb6e9fd9704896f353bc36fd57461ff4141c7 df354571e01369f6b597c0843a04454da774e5e33e79b18355c6ff531deb2924 badb53d7576e93209fcc18c2210042527406f0131014acbb1dc2cfbea6b6ebec dd6bd25a4a085f8aa85efee32ecdc9f6b1537bc43ac10db598ab68bb396c8bb2 421c38ba883679a38233ca4124e9046b7151ba795b19fa962e5f1f48d0c9578c d4e4143f86f03da9d8d05cc7c865548b6f200b1547078376fcf106a2527b1308 62a1e7ebb24527c87e2981d0dd5f76b090670212b8c4b648e66676a1789210b0 de322e12719d1887848134f6ceae5618990d91f5b53cfd49424b60bd78269d2a 0d0fdc954852113bfd2fd0cceeb4f23b7bc1b77ea2f88a2baafd30151a471060 3330e9526f124bf49671f00425a50db3eb263b42093a74b48fe46e13268f18be d09702416bb8de1d69a91e4d8c005485c7b2c22701ac324848850065f4727df4 32b8df5d4aca8f161bf92b5450d9a553fb4e4a3106a9c0960053457c14029336 1008a9b289e5acd07dacdc4d91e683d00a8e62e8f182b0b270847516c15bb209 06759aa0d48c2deae83738d93eadb5d4cf0982d96c8de5cc54bc796ad2c66cca 5d3ad49f8cf2a23adc2e97ae43714152d789ca711f0eb95883e890b954739036 ba9324077089ae506c266931b80e8b63274b4205c67b972e78e085909a4c1c68 932b8702c4cc0c4d6b920254e7dd0c470744ce7d08ff1bb33830e383d084737d 36263d2a6770e47bb9c8c6cbe947f61e49a8ce2112b933c7c9c68bb577b4fbe8 c236b1c9cf6ff509644fe36a7fdedb83771344625a04b3a4dd2a62f3eda131e2 c457e7214cccd58a2c06366d342147513167361d03436eb7a21f8106f2b6911d ff1898270aa4bc423724c20f13888a1b44766f3420182e448afec9206d6ecd45 5d7a6898a81609b414b409459c8ba9aa5bd36a008d43f812422cf0fab8de541f dffb4ab126d21f0d75c9ba72a2576c19e61222c9ed95ebeb32c2b517c5230303570bb0da08786b1526784db3691e025f458a173968d5dd3573537ad7bd749402d5bcbf9f8aadb0437739dd0baf05f904a10b16147e43da717da8609c26bed10d342660284f5787a4975138fc7e1fe7b6ad545134b0b3e464e8538cb5775b9602d0bbfa165707cc22b33d76b40e6a5b7c4d16422497ab14a4d18b6e3545596b0974f7283e8acc148fc133b763ba4fbddb3a9c38978c6fc6fc7aa213ce1178b30d1098997a3cd753f2257797490a5119fdb69ab9c31873d4c7a642caeaf139700911e839cb8850da5a9a6e7daf49b91ab1b20e3c071902f9594f7f5a3a852d6405f489c07426fed2917faef80b19f70fc8a6b60829ebed5d310087f2a0a5a5d90fbf8851f08eff27d36e9c3c99c1baba84a57bd4523a146b73730166331ca2ed052c53737dbe5e9a881c2ad6309a947c2a44d6761ac1da4e3343e2808dba2941013d195401b75adc55447f01adf1f9735ffa428960d274b6e9b78a4b2c8f2dd30af168c4c3d2445a5018801fe457306b4027119303faac4bfcc34f3b2b9a5b3f08c466ee30d672bbcc19a106f5dc8a2f3b52eb3914f8a9aca55b5854ebfec2930ede8ceb3cd45aae9360d02ef944faf49ab2ddd1cb7334ee25d3e7e93ace1d94028a110594b018c732883d85f0f6f0a086e9a090cb1ae2a5c789fd4f6fa1cbcb03652368c8be3d4280f4d81c3c1934e2b63c9bf5217693022297215907a25ba403dd014a874a9c0efdc12c2b970a395a1a8041d26d9f275fa7059e9156e3531e0401821f6fb8e31f8c71989dff4c9fdc8eda1d5126afc237a0fbdee41df5ecc6012fdfe70b8e254783ea393b4a9c6b75745836356e18d6c0972ab2f99cb78bc8064c590a4696d45de6fc717f2abf3566b252b33dd658bb71a928bceb08e3c6fa09cf5c069fb11040921ad5fd3b660d02c73fbebad7e1e2f2877a82ed912c2d850164bb4edae1aea095f5020e08137503cb6e5a8cd367dabb73cb375cf71425ad0dd5333744fe1f9946128f9a3c2fce915724d4362c68948a3c4abf4d6745c80b07cdc0943aec9c2ac240017a47fbe0599ebff749bd93bbf87568dab4fb809cdc0ca46651c90dfb4843167f8c0ac105fce54354c41ee7cfe24b2308424bf8f90a00caf073970c2d1f2898e8f20766a9082af88712635bc416a915defb6a3725a90af8d8afd24ca3bd1f34e6662e9f905f56d2a2f2d774eb34d63d5a82349c16b608c59102ded3dc41f8e52e140be21e43cc96437e667d57fe1ac20e9a0763c4170146304e73c3255aa0215f97f2f39b0dba5fcb70ac14c4d787eedf33f53480c601f022ec7dd9d9d1e544d0c2cc43a5bdf7f389b159f811a07f7e51a671a2d6e00d96e9067d219590b6d40c5caf7f5cfcb462a87d4b7bc96c76c10c392b83e3950d1e8e45b9fd9ea154acb0981125b483ee4124215f4d58ae27a3ce67310770ce09211c2f171da094772dff57a7b71b231b0453f0bf51b07b955be735cfa002bf0f1b62cadd90385a5b5dd22365341f22353da52e3eee0be47004e57792d366b4073c0481b920b63c71f1587f5de87064799714c2e81cf53e13306d0ace3ba36d022c7ee6c324c036942b82150df3ecb5bd937fdfcbc7d1c1d1e8df43bcffc3f4090b784f75eb6549aefdf92553dd7ab3c32a6672915763efc8c6e04e0f69e8ef061235f0d973833a92d09295725b49dea22e7ad2df2fff581c5740bed8d593240202a7d21011c2ec40f4bc32e71dcc337260c188ce298a344dfcae92a39792cd05b47a12cc5dc3e5054352715bbe8e25d4298e0aa61a4dd78b672983b6b10fba0d866990a4bd5f05f5e9918b1c3732fae645bb97d6c287788a8723cbd94eeb4809870138d1a2b8e88322fe0b9da9db2861a013cdd20c3bbdad296d1579606940018b58e5929813a1e174712265ddb4a5a47f18621b090d32e9d7da91c79d66f60ab6eb775c50d2cc95bdfb2fae442fee54f871fb50def0d0c334d03a98ff8d5e03a62cd531d15a490e796a69656b2c967d68a6e93331d5d6c60672de6c0ea6b10220aacfb0fd3424e200e6615a812e02202d4a9f653eb00d00a9a3122d06a38d0ee5e65704b79fa50066d5eb9954fe08915975e8d3226db6641a16bcf170cc0104ac29133dd51742b7cf87fbd3c9f61417ca96d498100a95d3854734475d94d6083d4906379eda9f2a94918cf9e6bb0a9991fd2a511b9089b200f6536917b384070b87b3064e9578326a23ecfe681411a836e1ef95cfd480a3e888cb52f8f12e04086afdca0407f02126ce3a0c253857dcb9f9e4a61c45499cc804de53d444970b14c8a45c0062ed6304c7e1cd7b8740c99b634a1bfe578c8b207df1ccfe1e8905a6a88b11a5f9fa1dec49ae8a9a9101079d6e5de74c9a7db6c99e63796e08fb0971a457e317c556081ac7082e590f8a1dda5287836fb3c27baac7ff46c1971e06d5b0c796eff0a9fe684ae24670b604f78224e58c0ec9e32e149fb5fe21f46505f182739b1e0977101ca6c87c884ad2bcca1c96926849c0ca095ca1746b62110779f9d63e140668df0ad33322956969694ca7f4b6d6f24feb30a692396bc7c3052c42dac481eed3003cc9f8b738a76496d9d485f1f139b008a717ef048be88f087d431443ab72dfba85320bdb290daac6c59403aa2ba1488b2b31ae918c0cd207a25ce090dadf3f20e510e7d6f412b451a0d6c2e556488f1a9e772cd0a2aac90fcf6d6d5b1501e1e6ae8ff670e00fe0bcac18a4a6c91f602a9c4192bd44cbfa05e6f05a9e0b55a37a0755c47eceda082fd2193adf312354e0323b0aa4dd800a06860fe031f788ad81a54cf676ed8737b800b8efa9e052845f5d903b8f0ec94109d7c0dacf53b748798645714c30962445645304db3f27c6c4270bc2beac8a4f0704f4a5ac097ea920e84c1f8885fc1f3f713179b4e381eb4b5272a7d8790998058c425c967a38fdff2645977de8884eea12d57a9d0ac4f29c635f77bba347d506ded49dbd6b016c7b112be20103427cdfe164934abca335f2fe63a6d9de21c8090d3af2e35e31431efd6ef901f72a347562075f4d5d7273b8ed304481388e2f0f23536ef3669840ce2f26355b36ccf182cfba1f45cc37e2b84682be702418a90c7cf22564d20483d4854ff90a7eb66c43c9e1be5bb68624cbc71b3dcc959eb30f2fc16fb322bbeb8e1a8942987a45b0237f7690ed5fe4317202002ec104fcab01e8e4a03ba05163aa7a08a3ece9f7f3feb81083782b59a1006cda158e1c77f20baf65831f39c9cfe1f4df333d51d5cc63aeef4cb787a21366638227f1b8d5ab0f01671c0c2a60eae0f53f1e46595263f6dfdb4451fc24b1671ff6f199b7edbc0ded29557c9703b995e4cfcb0e9fbc00637b5c22578ba219631dcd65eec7473a008aa116958b7f251432d5887d67d255082a9eb6b70ef85e9a5726f88fb1e42209fe74a6003df7c23cbdddc1c2cee31f2391dbe5848d70e33ada0612a706abab018b101c0430a68388904884c0f416d9c9b21a3fe514a90cd4320f33c3779a9705327af08347bf3433b28b20ac65b3f68e04a87c551b1b62bb6e6281bcd44b160927dd130fb39b94e2730c093e1af86336da96e72e12cf9533266f65ef1df0a90fa3c6fcab40202b14ef2bfa04421de651c11c41f80e608034eb38dcdafcce53055bcd828928e2412849edff8e80e151720ff595a97b02800a79e8ea1e94ea7d020c68cbcdeb488ed43a2de3dd238964bcba4836e13791718df9202a4a72ab7f0bf08e4e7bbdbf7c36896f849ac5f14a5cc10fdfcba2d9426726511b23d7d7f60c52e25a96349cc849e78bcad80f8f5b52d481b72c6ef47041c1ed4575c36d5f0533de34cf6fe5677583cea69257ed9e5a0afb29e3632882b01fb9dc172defcf05f074d0b2b6792be5077de0eb8acd7607df96e2982c83ca6ac38dd09b4d50c402711516158b0a7499afb16e4f66e5611559cc759e1715f9a5f10c05cf0d7ab302d8012c915825dc3759f5d955596a5528531267d3110a1953cec5191c3eaf4300ef72ed6dd9660e07086be65b41db2cd2f442ac2dffef5c17b8442dc3a8c117093d6639d670928e17aeca474f7cbd91c9a2d17725a7f0f3315b40535a2f3af60ea353f1ff084ac9e703263c38eb8a654dbce804436abaaebe72b24f37b8ff660777b547429a51bec86dd1c82f08790b2489edcb0b67bcdcbc62140ff91a5e0c07a26a48ef9e3a8659ea115d16dbe905f8b5d20e2219308ef73e2e6a65cf73190a60f50424dfee28125997094793ff97a217e8594c72b6c536eb6bb571020d0001f8cff088082c1b2b54f393f383756f80ed4dac134c1d51e8d9c39aae1925150b3d9081f159a73b6c677230fb9ec5bca112b378b32c7ccd9c42476b620f29d10ddeab069d21a5788e25aa28249e3a289cb686e9c0e6acaf6e3951c7660b23ee04b645b6e1a08664eca7b2f30ff690c80a4ba8795f9a316bbab696047a7f38850e41bb59c1fd3146bc09839ff0671e21e4834b814a6c398ee88890e83ad6eb5d031d530baab889bff1bff355cd79122360080fa15b959b8013384509a237f8fd0e775701e4c459a3efea7073bf75875d4c4425cd0edf3f5335c0544981a06b160e4b243ee6e40a9e7b44b1df1739130fc108ccb7339672920a2bbf5785d39cb204d9b94850663875b6edc2ede8c3a3e480eeeff6644373c5ed204d838d3666019160ed1f209d4b0576d3be5a344a9fbcfca075232fffa6bd8de1e64cc0044c3e0b64bb7366934e093f19ea2dc70d1785c68c42e887e86a9a5cd800d19f425c6b0f0a724246d14daca2971d0051b94dee9b5b319b0e822cdcd8ce9e04492af65502c143cf4e2e7e7fa665477b7d7c1a450118cd25a3d02854390d3ec559e066d209119ddbda8e47ae2a5c75b3ca11d0573e99c53070465172d028bac70e3bee2001d98abff6f247b67e729b184ea6f3c8c69c49bab69b03efe1dd67f8a8bd729b036be61a9912dc390032d3ecee145334d708c9d33cb8e32aa7ec4d7a65d5acd909add064080a6dd33277b6c5375984d098d9b242964f8d6b2314161fa329f9e607fa2393879252c0076868c785e2cc96d120314d7db923f526236e356da5d9e30c707727f75dcdf933afeefb9e149d23a9a94e83984e29d23c81ea49431237a605b3d76bc7ca94daa1743b12e56ab6f47b258bb8d20436e438d4099522add53a075e2e5d585235a9c8b9a8b8a31a5b4f09b39afee54d0df7c65bdcea6bdc36cb0b71bf2d04c59f6c8ee7f1a2e2cf2299db25d111589377a5431285b85f4f729b0590aab2a3338885a6f2a5ab377d9bb67e18961c7938290f3de1dfbb3d72feb300b47f867315ba1c640b9f4f1f7e9cf65238e7a5327cd8d9c705ca6f203c7d4508 false +check_ring_signature aa0cabac178c9826b2c3656973937d33d9bed09f3e20db335594ce39d0a812bd 6bfd0011ab7ba90ea7bfe0d916fb116e6cae571667535da2efbd71ed0d7622e7 1 f5f2fff57aa1e5a51dd61989ecc42dd8bd9f43f165f0050767430c73e7a3bebf bcbd9056f3712908d1ce6bf9ce16174686e7ead4774d3ff1ae2468a2fcae9e0be81c60150e6971f006e019477b5b271e9f1f57980bcd93ebb40e46a6c7810701 true +check_ring_signature 2dea67c22c2b964864544c323dbd3f85c5d36db6e45c3996b3cfbc21eb873c38 1679ea3c962f72bded1fdfa05cf60c022342de8fc0398e3a18356930b9b860b1 22 a82e9dcadcde20d7b91eca89c7103d0178bfbf88c11d11ec4933141beaf3621f fefb22797eb08d9ce9551424b1632f825fadda731d73c4d51aef4e9c88061d7c f05ccdce34af65344b4e5cae9bb9b36984e2d8f861d2670890958c2698e83219 74279f5dd6e02e53389468028fba4eca15a1cdc460410a225a39d6ac3af72eef 852e547a7cec5a67d3170638355641be3a48f8e5d00045cef4bbeadbaaefac37 77a04a49f199dea7e61d0b9778d3540fae356d329d9052c5c3a902c93d87ee62 86d8d2fbb4dece54765ff57696ee0f51a55790cc6a05dd4ef0afad51ef650a1d 70b2d16ffd72f15ecfa74fdb9c80e8059e5ebefc4c3e67fdac32609ce5990dd9 4ec9e801df4fd0b94361d1bddc947d1e799db80145dc760fdfe802cbc274875d c4eef91373c684c7de9b2d47fd8ad70aa63cc19108bbef32a2dea86385c770f4 375d0bb872bd9106764de922c3e12dfb58ff54c3d4ea32a714a78f827be1d5a9 084ab0059e5d11188b223c17282fc89df118ccd8a702973f8175ce45819d1a62 b6f619c30ef1e1255754438a2fe7f76fd4480ef997ab50a554dd42e214c5511a e8e5dee8f70e0264e7d477efaa52e8faab6e2ef269d5fb4efc003050ea1a27ac 00c369658ea1ae50bdcfa935b2e14c763ab1f5b1e200bb91bb5b984b640e4b9c 3be90b7bed6c821992e2bae612c5605045c6ef2e23a8a35d0ee0e8cad955eb4d 9fa9303cbe96463f3c4156f95373108966fcddfe04cd3cca89ed35f440e1df5b 61520522dceeda02c8da2d56d682380437bb7e7e7000337c0e0e5df2c03dc844 5c217b218227295aee83403482c71d52e81bd21894ccd286dd012f84568e6de3 35785e59cfc589b81946359f82fb6195b5fc5199edac11586b2330e08310a2f6 3d06ba8cda4ec6071ca2eab15ffb73510844118b51ac9a4fba2d472b2f8d1347 e3367c6244b8171d716f8d9cab51d726c586111244b55bbeed63c4ab30513cdf f597a4e514042f3bf17968c62e0c8f8b0583db695bab478de5b2db09911ab30242076471b8c040c76146df712389939eae2bd25ab016a8d4407883dc7f5a780f8070bb1f3dcdcd31b66ddd875bdd8a84a7858707b9d6313cd870255aa6dae80b794d19353fbb4b8841fcfa26e3d1c595ef3b44fccbb6a4fd3135fb1d4af8a905ea4e0aba931fd12c14099b37ec1c944e749a45a894290cdb0b2fe5a0b37858090aa3bdbd52e3f8110514fa1b15a907c75817a6010c64cf9b7d5bc3025ae2390b302a8749c7d75b0113c8425fb54904b79212c92bc4b51b30b28c96dae68c68006f4ae7ec38a3c8376bd842bdc5478dbd0695dc7195d0b9d9f627e5086c88ab41f88278609c7ad9751551f07e5d66050d9abca055c2667ad98d66771ae8a14500fa9c173c258536a4c7aff676e0daa9910a967447a648170169f64b6a0253d503c0341962d0b6607c6a7bdd87ea305d5bfde73db886be928ff7163db4d8c8e30ebd63eef2b28dcce2041b595e3f0aed4fa6002304314ea0a074b7da191235f503a617b00e85d42bfca91dd022b38648cf11bdc26aabfabfd1deac247f6ec5aa004baef7c0393f5f5d5fe4142b12d93abb452771c3753810fbd523bac2cd3397023d86bc7b1df04715b127b1408c5e2f8cc4dd2bb444547cd2a95adc02e7c56506950e48a32d1fc7c9ffd33698d1a353778975bdec225f06545a62d9e1e33d7507fd2b3fd187a03651adc35c5391e9f7814045ac18f2807018809f6f9f74ce2701bbf08a18794294f706f2c5e5d302f2caa8cea4a0d32ad7635dd9d49070c79d0f1c7e746fde3c8ece831af7a65716bb92aaf3cbc6459e7dcc7c1b8c197af67b0e3933e343c911d08af35f531cfe218821c021111c5b9aba575aa2c0ea0a68e60b76627abab269f93ef7e70a7809bd8ac808acfc6dd004799817972d73ce085d09e561496a6fd69e73e96b239637208b4a74bcb9f6ac2ed644035226294efcc507cbf289496b6c8a7ccc73acdf9f01170342ef0dc40ae1b80f46165357f3863d05a570116755f4375838f02a462c9e11f7aeca57fe38a5b85527e1e66a4c759000a44223cb6abcc696c7ecaafafa1a0d6e2bde108788151ee52f7bcc6522c765093cf336fca6ff5f25f8f192747d8025188e51a3356e063c734779e67991e42308ff85e5c67c5407b501c24d2cfa3003c15924e1a402749a3bb25e7c10a865bd0d597e4a16f1d46361a75e5f4440e771c51ab7276b28d8423f33de351b1da9c7068872ac6dffd5784f073e04d03ad5b8bfc6991ebbfd6e7ee48725d8bed1d3cb0002fedfac1d6e04589a35ff7d2f7cb4793d7a07aaa9915f0ca1bd81dfd80dd60f4a3d9adb1b9a291c4ad3f50d5b8088c89b8d3a3a94be2d07c4d032f649610c089260c4feeffa8575969eabcd0cdebe336440c932ba4450c69a0f739be697f20e5e860554e21238ee6c5a104d3125e2f1d37d3a14fdc9e392c545bb879ce321053d9d31df8b32cc58fc6405345c6829903a6f476ec7564cef08c4696692d5e50674f2673f42e7ed906a70ee1aec11afbde6f5e06fb8a63018e7047201be4fde05b5b3903e38495e54f9e7bec003abdd11a858043ecabf29d36647596b54c79808a8e2667903e81012a3836ed40ea2508b96ebfec33391683f5de92afde7deb705368016433e940d9cecb4f229c2371da0e5a52239c561ab3d276af59053d029024b61e32894515ce646d86b12c9176846707f322a44b5ca57f8f8b63dbd117008cef8476b36ded330b532aebc923cb7b8f57daec946702477a17557dce10a35032b3cdce10acd5c8b10340fc170cf46ff7cc6e8de5b73b2ef357706888f9ca803ff21e48418ffe2c6c8c51d3e26301cc0937b7b178b4d1d8d30d2f965855fea0c1741008138ba96dd5bc7164fe513dff17b4ff2128c07721e310d2ac2814b8c04db04e534756d16cf673b8ae692df3d068c1d1dde6ead25d34a629794509e2a08 false +check_ring_signature 7edd748e011f354f4baa18b68aedd5c090d28009ae2338ed88cec86df8796859 aaad50107128112ff13aca480f27e08554800e829da08933c26b712568504b1d 31 e41ceb2623ca5dd856adbb738d02e7adf7ebb769a03d7cbbeb093b550808311e af7daa9f8a91a0eb0a5f30ec406543879bac9d36f6cf47d65cfcd84a3dc646fa 35b4e1bc6a4819c628c8e30e721f8e284cb5b6a7f160bdab729406b63908c19f b367dd800d50f2b1ec759be0345dffcea85634cac7dc1352c89f1e51853deac6 e3ac825a147f1cda7aa77487638b858d413540c28a6272bced334ef6e01110e3 9f567f48969c43ac8dbadb625368ebfd80afa22450249613fa771e4f42238629 f8730687b11e7a68261621d97c73986f15ee9ba9bfe910ded065014b9171d382 2dfbc79fd56ef2986bb12f067cc03b40cc7bdffc0aa1f2d507e030638084d3be cb6e9742fef49ad9b91a920951f755c60f690383f819605d1013616e8bfb3125 42edc8832f50261317a9d80f8ad21fd6f0eb5700096a9b1531606b699dc1b17d b811054c1313228d5d28874d75f3034f2fcc3a1c00d1c2017af17efd8f148dde 7985290e1ca3c6a14862725b723f4a22621ffcf4fd416eddc04209b4a7cc8953 7f013d10100881a2cfbe58ed19dbe1fcb273c0f7a2c58f46193bc93d325ee744 666078bfd742616f75964d87d868ee3d2f13f5929eb7ac14eb6438ee789ece31 4ef11bc45b5884cbaff90b253fd4cdcf7ee109449734e53a13b498ce4cd85e53 4c553396309a230b73db8d42d8eec3a5969c75ac989eaf24b5305c5272cca054 5c0876d57d4450d3dd376c60f1f3e8af52641dddd0046d61839589d958de9283 85eef1c0b36c37922b7735552c1d1fcb0c201647585ac3d609c810d68f0c246c 72e83cf1d28dce64a37ee14feb2d62970832a86b562895e758d5e53470c61707 2ecd25e67e73ab51422487b0993c5f6db57b5d54bc892dbd25600c8b1d3b04fc dcb6138d04f58807aa1f5ae0826369215e7df49c9fa5b57cc836d68da29f9fc2 596938fbbab05eef5f13e4b41031107c421ab18d234c7d6834cd4ecf4358a2c4 03cd7e43333c8bc78845c14ed32e9f70c9278b85de01b972b846d77904911286 284fa1f0a55f2790f5851e34090f666deec05bbc5ddc42f35ef5406ae4008df0 3d0ab3718b8150e8df1efba4c1a66891fd76c2abd7ee376ad15d1ff0e1841838 58c98677d6c412a84a9fc0172cd1b6f3b27025bfcf55a6606cbbbfebf594e9ab eef5ead3b1f567b802f067d8ddff8074138b03e193cbe745d49559dcc6af4296 1170ba2d9e95fad236c48985005ccfcd9173a6536ca2a52e45fcbe931b3f38e9 4d890fb37776754984395722c63857566eae1bf44666ebe601d57df0165afb88 98e802b21a9ded58a27084c3d5fc1ff4bbda13c978b31b97fdc6729db9e470b3 b4531cddcb78c3dd04aa3dfe07ca038fe296e10025889948d2cebdb927258104 67b284163a7be4f41e89593f27ff34c8f115452d225fce0f075238940a1aef01a443b89299252b107e449c649fe28483f7bd7cd39e5cb2ce3b86b0aa4ede7402dae6026e6b93da216da6f09ba8181b1be3df3d8242940e55122f4e3e9d9d940e5532be9ad925760698d50125336a645c0c893528246e22efb3881b6a8b407c02d8a95da2299c2ec10fce6a377953c239953feac53e3dcbe8f53918bf7d90f30bfaf78fb30ed7e3fa58436386a2b691116d1e7e15987d3209d7e5581db9ed540db9b156704f1f20f13af0602a9a8f621c6be8ccc0c4de89c11e7c4c973813d20968fdedb2d1e4fe07d46e96bdecc3a584d57fffa01d92ddf43d530826cf226c09c31f5f3116463916ceaab40a9374c1d613b1028882ae13fce7667b625e73ff0a35f3e20b380bef84a94a5c511f222a0fe012dde7598f811f3c9525fd4976250569031e703d47327e77392f79879661e18e5a67ad05747bca0713230567051606209327e55a76796367588e3f2faac06eac2656cd22e48bf3498ca02f3ff7610abf00811da90bc934caf4361d6bf905bb7561275e1949a0a8d130b3e5f16f090bae647764ae047ae223498b508a129c5cb921b91bb2a5dbb2a6ef3077df97db00c4be97527950a13c09fc81d2f485e030858f3325eb00ccaf0dfe8bcfe4f5dc082c2c342f756cfe4126df8ccfe8d20b2131ef85a8b26d3a8ea67409f35accf60c41433e21bde2fdc2d85a52f8dc993561b04375b9c459fadb4c20578f82099b0c5f736f79f117889c063896ec90ba1dbb5d0851a8096fdfd350c049a487e3fd07a98b13414ad64b2938043bb283f847ba75d571469e817d00da39ee7141066700226be5370a0d5263a2ce2bfbea6379b7f0d52185e23bda15e9c6f2448968a00d7cbeab625c3f76d49a61999e2864fa0fa0a6e9417a314bf32bb741ff0ad3680a31dc86549bb36d8b38651e5c617742309746cbac1628ce0bd1e7a645be665e05c5dbd265948ab1209befe009efc9cdfd4bc9ba0f2571065ddad43bbf21420307a0a0b0f7c58a00a199bd8622f536123973d19f930d418d33c7fd49fbd844b9016b9c8747ad6e6fe32a82c3369777057a44d2f337e28cea218800201b8243e50dfd736283d68bf2e79c342cba2d60141fd3680c8bd07cd6fa12bf27051b6d6f09525a0f5611fe7c5f1bc4e30870b28312c9a211ba121375704bfa8c3955c5870c71c2d740510c464658d6192c071f5357078f47050e7c0c73b5d99087c698e90e24bff67ba03027c6cfb7d3d3f41123e9bae56e28c49b7f57cd64344e25e1f50a5f9c6702e8e57b0a1e0185fbe121fa37457371ff0794e24c3eab2b80ef9b3400b78f533d213edd645e49ae2ac2107e9d231a013a06542d6a3559cbcff9f15802b7c8aabb25ea3a66e7305e257639b52cf9918118e1e61b5b181e8459e3e2100668928481605f7128f4be9466f244e747ecb05137200a2ffac7dcf34507f23100a66100d0c81d262be20e91baf4fa9b37a2aa3ebdd85726736e59555d872ef50f7af80c4161fd4147696b085ef0a109f359c22e06bca10193494599e92c6c230519af4b6287c965c5ae02d6cbb074fa00ec17b9cbfa108255d0bdce6838591606278b2c6c68ed75ac683a00a5c96c8120a417544903987bc981962ceb9443180b3db2fef75c70342365dc0c1d8d33069fb09dedbe9fecb2c2221f4ff639952d0cda5ac22314456327d8bed6a35c83a94fd8f507da5abcbb6927b13f7a154bd305c9186fe6464fdff4059b1116c403d6193a486fd0470526bf08fc106557b0c1076795554eddbacf97dac9881468473e5d188b74ab77017e80fa5b4fcf5324a80d15dc598c316c3f7ef807b9065381a68cc02a447e972a5190d3402d9facaf94042a955dd08cfcdac69ab984d973b459fb9e2dce1d593d40f112149ff7c2c65d098ad0b1e5c30f236479107f0b3e554a73408a4c0bdf85f345fb416673b2252404b3787a016937685b0b2e1e9eb734589c35c2a1705769873850ecfaa64342d006ef16084e291abe6f6bd149e86b8bc6e06f751a0dea4d7983de24aed03500a805ee2caa48848a117ee562c30eefd07f5ee9ec1d451a0d5769e4b423ffef2d340a3025ad7f9182ce9152efed5a114505f39ec2545b3e6f531ee22782b71528af05339d218c87763513fa47c14e9cd10379265c63c07353de1db9bca70da5e68f0eafdb0bed506d68d8133dea4f1a47480b91b09a89fa81c3d1ef95512ec1cb1801686a764800d928a3c83b1c475e206284201962dbcf0f4d27b9e2c07c3e78cb084c51eb2549939cd0c696afb7079e17c51ef65f05992bcb83213d7e194ccab702baa3b693bf75f87bacb13297d11909ea50669ee008fdbca29bb960137694370137b96a30ccb1d6bb46ced7c835b303bd9c21e69e79abecec7ca416a3067a48061342c45ee0b761b3225d0ae6ef0ae58c282245345fa0c71ef7fa2d1c6f174104d88b0aca6cf892577b8f3cff7d99c17f898bde0ea46885f7a529f95456c69a0d8656ab4e3449aa369da1b94ea5427a634ff8231b2c90e65ac56ec2f322fa5d035d5c4f5cd4c0dced5a20f0af8ea83412f30451e9fa4688936e04c045fb52e70a00f84c213616ce02d3a92c399995036e0896a96e0dbecd0a2b8864e5653b3505490b507d1e8a7f93850ec3d06ee61957fae9a0a2b6604f2325e09b87f969b60ef17672ce98f9c5d23bd64a4d87e6e621de7f433f111ff5d353018dafff091f0090b0406252ec508a5d5fbf026d814301c43dc60204fff1dc6ea4f2ca29b28d0b true +check_ring_signature b1a90291c1aab08a64375dedfa7331be49d444305ebcc2962a051db0544206af 18ee32ecbf02d5acb3dcbe5e773c4d9e4d85241593afdda469da2cfc7d3935aa 1 7530621f44ea8ed76d6520b85f5aee10b2a7705a71992e6e1c10287fc4ecb771 18972e0d1dbdc156a6186a0d5caefcf69461c6eefed78314b50b4ef97b16730ebe6ec80f8b73c41258b2726480d6e9facad4453fa3a9936316e78a288b63d708 true +check_ring_signature 576d96596e575ad79c5734bd10fdc87df13f473ed51baaa85a9addc2f52d6d53 6ec1fa919b3a3a6bb3b636686045fb09a6ca117a47776d2cc74ae2a8e42bc6db 56 8edb889afad3547a7269e57d4b60179eb9cd609e52a049e6cfac1eac7aece717 a74c6ad51cca7f5474013fbe6ec67bc82b9138fad0fdcbf8071c99839e97f9e6 ba08d3dcd1fa91443738822a129c714d6a8bf205659fbdfaa56ebd8df903e23a 09340e9fc118e4673c703d8eea7f2804b9cb5f29f12ad376f5e7c17f7d2a64c0 763bdcf09523ca96a7dbaead18f5ce057d5e428abde02825ea7246bf5c4b68e1 1e55814a6134d55f4d9b153ccd169e8b70232280a9fa9552cd4f297490919671 ab86385ea5b8a5be74c11affe102daadd5a72e0ba8c523076cf758db3676c9a9 c17cdd8947564f9c3d5e3e00d54fa6ca8249c032ae71804fb95c8d39316869df 378e26241e4acd6764d3197faf3813d2988ee43f4b34eeb9b94454aae0bb7eaf 619072c958415ccdbcc367e5f1e70863121d4ab781ed4809ae5e6592bd3cb1d2 db2438dd8f8eab5ccb961dc5aeb41f1b24acef6758b3c5246b0a6187e9e06b6b 92463bca12a384b29ed1865975be3782bff7bd4958b83cb132c99c1ffa53db54 240bc6131d491f5abb4f4f4bb54549b42c408ad95b34419955f3a82fc069a974 fdea377ba7a44826a9a2547e0d577d890610d1322da76448de232241ff5f6be7 9b196d81e8c31c6833a41c4bf8be49466164cb0d42f6c71fedcd2eeb3b0d3a03 e07403468ce443b1a6469c3271a4cde303b463111a1fb3b087c9ae501ae99f30 59bd18425c6665e2c99fbbfde08732cea13c7992627fc025b71365a7a2a87746 fd60b61538a25714fdf6a7e0ef64c39739ddd304c95b4bce77ab7cfe33424662 b149c76a84c61da44961cdf7ea27f42a11140ebfcc1d81c75893ce09c55effbd 850a7aaa0d1ef680e19344c0ea676b16754473542b6e7dea3091a11e220f43ba afcd546d6b22f35f7a0d5f45f2e742c70c0eb696df0a821399e24cbb58bcf63d fcd1ec8efb65121dac2c9432eac0becc68c66e7a759803580727ab4e843f4c83 8de0897f65d40aa80f8150cbcd1dfbcd08c825a675c14837c6b540d6f4caa61c e1bbffc52625ed9289336dd140cb43b4296d3715363c22dda04d233e40cd518b d7da7e0cffbdcfd646e0b247289707a4bfee5e9e871fb7b53a38deb35d1e03fc 28b9fb47274a4ee62c244e65d1e75b9286fa43661834f9282508f36aea165a67 3eb127beec1ec46d15d76333a41bb64fc3f33c4cf8c00156f051d167e38ef149 31be56dfebab01cba4f8d1431bd97d4262e0eceeb557d49bd05ad95fcbac4dba 11af37372c489f462df0f6f0908d71de15842e568049a0e336342997d4cad414 1565a43cfb30c5d2d17b3b81227f0e775d90b65e2e20e39a9fac66d400676c85 1fdc96eaeb453221d4d564f55f446c6b5898772cda7dbbdee942fc1cbfeebfd2 5911d04963205e5b13a638c7c5d7a7447ccda6fc38cde32322ab55731db1ca7f 37ce852a721b556c308cdb08e1606ec840b49fef2b691257476d922b80e2ff60 520c5d10ba0ce30cd2243705561a5d808bb2beb475b54bd7c753a6606e04cd3c ecc431e4442899c58cef7771d75ee5b60b3465796460c8b5eca68c17f694744f 185477dd59c9d6dda956463cf1d98574f08c152d5a2e23fe0e7d311935fb6b09 57f16ddbbf74e7695e9bb2475065313b2eeefa210ba73878b2848789029d0b5a 2f0e87e9ce8172663d9cf7229368834dba4584ee0200deb94e1be671678cb0ee 9ac1eb42ec52423a9224299d9921b9ec324606dc1402d7cb03f4274b96616fff 6e0f1b7480c3c7798150c15f59d1f89f5d71f98e4a64ba82c8442ef086e30f7b 641c2b134cea9051a042a99ab77e59d0e4cd3616bae506141fa9de634c5c6ae1 e431a5e5df810a0171b5bf8536329bf85d4173004752ec1faa8741550225e97f 0769a7d02d42c1410944aea96a2c22f9dd08571989bcdaf1981309a0e989ed00 c530b03b33bc1d5e2739b0c66e3b00b45257f88a830a85603ba31f41d8bad91e 98d553258d8dc4cf6dc9daff062e0d988b109f3b106f4539e5e4e3c4547dfaf4 2aa9b29ee9ded61cc84959c93ed35bb402e251a57fb4446bb1f614e569dc8ac5 55559ce1cb254fa3b55b424477aeb9a4075cbc768cdfcc34782834efdbb634e9 39c172433b73051658e753994228584c8b1dde6b31190f8dca55a8272554e42e 99a4a3bcab261684f5a9edde0fca90716c6d1b35cbfdaf194ad2cd54f75c3ef3 581d89736bb39e0e2e3976be5220f307d674caea29e321ee5774e55eebead91e 2ad0144c41034ed1c13c292cf41120f0870cc89264046317f3f77f278a208fda 14422372f983cbc96c9ebb46297fd4bb5530d679fd5ea8f69a03f3e7833eaddd 45b858e9650a81006208ae9782f7459f47eee52f30402db6e8efc9137b0a0ff3 9d588ccd2d9f591498c8b2ae7246fcab681d641ab2701a94c41e836ba1ad1dd2 d919f53c4f56e48e23e24687a7881e95db9c2b64a5953540a1aae727845a9d21 71f708f9e4a4093b648e651011fcdd516936d5f124d36e9266df20416daa9dc8 003f737205db3d84fc51687b80028510eec30ca5f4bea63e7c0118ec47e63a071cc66b6b752bcdaaa72a7bf585ac739a6384e0d9ba931038a8c041eedf500b0d819b16933a1fd85af214fcbb07e5f78ddb3937a1e8f4648a756bb1020d3aa7082654f78f8e25f858e24c3b2cb3573308347e90d4ee1ddfc8f6083a911e95c409cf6f700e8dc003f0803f86be1d3e0c793db5758146bea9c8bc4646669f1b92018b05d8d292a7d8ad6969d3a47722a8086e49f00b88390dbb283c4ff06841fc00ab85ac32bedbca5cb0e175fc4708e7f259e41b4735af0b7fc34e022c0ef0d808ca0ff5ccb1041883a6991f9601aa0b837088dfb3c838a90e1b4578d3dc318001efa3d9e6b48117fa0dfcffa465cad73a224147d7473b246be3a6ee491344050a07cc06fdfcb66948a28a68425698a2ac65971bbfd3aa466e5977560407ea9201d4053cb2f79ee3984aef97feb67db4c4382d4316f38da097729e3633444bb80fad861498f25bf64ee5f9ac6b96a2215dfe6c5542ea228324b0b9f0512566e601ed441f4a49f00d24cbe92d7bfaaeae014116f788080419326cf9d5072d556f021f61151f34845c359032ae0be18729958b86ebef4f9dc9b728762d740959b800fb3bff595d581298b892cce440ff69c3af37a45a904bf5a7dda7a20dc60a7e07c79a92bf42ed79981172d12e7cb0fd25537d11bee86b0554e601cae34b2f0f069de8181203d75479adabbb5ea8c91da985f8a86403f6052dff19def3b0b7630b0f0752859bbafb573260e973414ceb55625bf0b8fd088f96036efa1ed06d420089b2ff20fc36ca6b457ecb201ddb9e1b4cc22b07a0a0e4a0084c4dbe9a74390244db9504dcd75c18dccf20855f5b8394014b031e762957405279e3f66faff1071e18adacf1f7d7c1d6ddc38be4a1d7f0a037d94aaff9f4fc8e9f0ce893b42b0e8ce5db00a12f1cbf033311cc59cd3bc65432e6c6ecd42cd90a2acdb657223c06d76e66daabae4f9985295594746932cb97071f371460cfb4dfe1a0ddd83262008f268436b7ee0568aa1d19f3d777151d9bd65f307a1ff8b5c2d61206050c1803705069f0cab1caa9e6664d94464594985f419cac3710419099479ba082d11d01b76ec46802427bdebff54cd1a1fd662ed4853eda40d85716a93eca975daa8a0cb2b3f73751aae2c3abd40609eb4d79b49f1bbfa7469d9dc5cc0f8ccae5cadf0903f15104347599ba172f421287c308c72d9cd701e5fe3e7d4036dc4d64b3d40b192cfd381336f90375470eb7946740b05bcf619b0a940e473ef5aeb0eacb33027e976a2b7d52adc6db7e84b73f841685e1a7e5d0ed8a5c7a7ef1d34dcf9a4f0397307a11c7b35d67e5f1acd91f2d7c6bd9ced2535e16dc8bdeb8475bda595f09f1d05276a134c223414c786202f8a64194ba1ea61523297afab7ace782f5df0bf57133ba2e906383fa5f6468a646bbc46f38c26b9737ec9f963894bf2c946b06ae51e3a507ccadfdc24b0708a85ba322f18c12012ee8e98da1ea2744b44e54091c4cd7e5d5f36b98288b6ef16adc758889a78ad3f45db821515c41b6a5f64d04f61820ca2bb33d25c3b6e4a06ea90ca3a33d23cb47b70d827850374db7b967054abbd17c1f269a2197d0fa084a3957098fdfa335c292c502e25bea6cd91c2100952543ee3ab9ee69672d23c74a537caa82d9d7af5989166bce3d239c8b86360b65b2d25d39cb3a032a0787c89658782ef017f1274cfee26a60925ba468848b0c809c79ad380c00343586b0ca0ccb8126d17891b999f86f8dbfdbf7b56d9ca20f2bf912b4e47d86a9d6d05a211bbce070e75401fe7711b196241bf550352926091e457c57e33fd5691af4d3e5410e988312e2fa7d478e062aa734b4cd0a758404f9cb74f89602a40f4d80a1ead16ef121d4f152caf4ba67f23ab17efb136eb608ee8c65cd014ffd3d16b9ff5fc79d5407595f60d56f22bd75c47bf539a9858c0d01add0451424b7dc8bc88527bc355f003b6962b31c834d9b595ce53cca30fb04e1b3118b8bb13d3204941c4a7fbc163c3328f1d041fe2e794ab9617acca7c1053db2b26ca32ba52dabf2f2ecf75d92d113fe4e37333c7fcad92b542137dfc1077fc5cef4a40bb95788ecbe6317d3a26f98f2b6bb228ceba5dfe88f5e6c762705c9cf64fcdd3a946f1c15997d4b61fa3049c112b8e4bb767c5521dd500f823f025276d4a24bb85ed5d719c2e2b6f29fc5ee6c600c6e544111ca8d147ceece700190440c9ed95ac3d2872ee779abacb946ce0d37eedc7d68ac8c72eceb01ac1400335a8a74b71f0a72b8e2f3ef78fd8527f4ba02471aad180a099a8e536a487a00920f1fe7045baec88a01d79ba48795625c4480055ed51d621886511793dbbb07fc5ac8f99284d663a054316b2e19ee0bf9fa91b3db86c1a32b450de833f8c5022571d94b46fcb04e839f36a10977a7b9900cf495780d9bcf04d05ab35b978906a05a0da0045932aaa3d0551e31144e1ef6006a5c27d098e8f8be6841099acb0af6cf6848514daf0c197dca3daf91a5e19cccf148425af8beae4c9dc0363039094d226993ce5bf3b9bb51bb714dd2e80f79cf7bf3911bc2e6fef34ff0308e1a013f5af6c92fdae50038a03773f106c73c6a9ebb29889a7b2050cec307d9862302dd9cf52c0fe57ba4ceae33e2e1ebf00dc5e96a882005f3337c9f2663d7f6510a610ec6003ef3c2d619cf58335e15fe745bc632376682e8b626d762bb85bde5008ef8290aa71b04c064e7cf99e52a8a08d7301e93ddf795b8f3373e7343a74f0f73d3729c40a82f03e4fad7de7569eb218fdb4cbddf11c49b928506f079fbf0088fcaea563f12bea70af6faa013da457ff07aa10b78c058d2a2ee1561beb86903e1f7429da71e2ddc80d820409d2cc65423dc28295819a8af26195f3aa09df00dde4ccb3d98f94a0ab18b6ccecdac60e698b73a5d1e79bea26e1147d5e701420add45667940e84c865ac802322e70f2a2b9e7394de1fa4aec09f1661e3344c302f6b1baee1fe0dff01ff70e9b337462f3c791eac8d9493a567967389f88d8f600db35e8c585e32f77ec2cae0ef9362aa0f6f09e586239e3b54defe46c17db6c085a0073f9ae1170f8fc37719cae3f1e521e38ebc028c90d5b2729d859f5f27b0912bec980840c398728c75042d186ef3eeec3f3fecb5c2f3fdeb0be7ef47c2c0b0f348bede256aab6a5bd4778917a508ca41d7cdaa7899a99eceb3295c8f52d072eb43c0664b8451676d664b8aab725ca75f9d153cb99b2b89e9cad8d77c8130cc4941de8e66bd3327e05c4866ad077fbb9f8a5a2f9bcd636bf67502f2a79aa049ef72b07ea7f07d7bf8d5df2ccbebe033646ae4577f646bd33c9296068687a09bad75adc291e6d8341ce81e74ff8c34dcdb4eb52e0c7b4094391e0d22e887e0b86933de96a1fe159f043c8e882870daca58553465ebae142b3d512d8999d760e15ebc66766a5ea585f821a65b0173a20774ae08231b43eecb9961d45d985990dd84ebd7b60d92fd8ed3637feab73c6e2b91d64ccb5fdb40416033c96eea9590f6f55e7fe9ebf0d38f4c99a072961bf4bb5105b99a24df885a4e08b7f98a90e0e6af98af684c31663a9aa629b75c3d694da71ceff033f2ef3cb69ee934e18bf04a19471000f4a8d51262a9bd65d2d333f329b20144b856922b172d50623d7e0003324ddc9106ec793169245167132df8ad54dce1c6b450904517cc2a0ecfd9702797dba6294cb364ee121563230faca6716d6b190f1cdc76c5fa47446a6aa3b0a732f8b1ce8547a4972f7f98b709a7bfe3d65b1e0b9ef5de4fa83688d349f5201dabd9103436b6456329b75a690b5994818421bd9965734095b60cee87bed80014fd1504fe258ef330c33d419ce6d32f438b8a3ba887ea1c2d20b68241231820e50bbdd072aa02d9c17a59f82ac01e874714cae75362a0c58a374f1d26c94cc0df85dcffc309695474f1d19ef9954e0a34102da23ea8c5432e57cc5e4d5e4de09e16a4ae77e5a1b23a592844da9553a7d694033eeaf3a425822802002f7f5b1085ed7368b8100807f7fb12caead754e94ccc53afc08fc462e74c5a6c772a77a093ba0b3014443d18f2f4f4a02cc7f59f35cf01f03b8170989e03e258c98cc250226359b9f7c9db5e454932105acd40205e988fb60dbd1d677f888f9d5dd01f900455fb79fd1eb715cca3c6bd07fb65787b4e2b51ca079acea072f3ba04431be07968593c64855374ccef522426d77f32c706fd67288c8e1a3ff6089287883410e119edb30667e9dfa5bf3b9b3f0d0e348d6122ed6f2f87b711d985e72fdc24c01f34c7245b7d3680b4efba9fd2a4291e4e620357dd7fa91d3a01bb95013d47d0f15cc2d217f0440141daaee50a9ee77e6793bf8feb0ec581d81d4950fa65b7f059e6fab0de880d7c21dd81151412d4a8f3a883015604aae4951817f11b8b7d700fea308589b53c4d981892aa6abf7b3c1b8ce346144f69755dd63dd5557caec0f44aa9266725bd63270202a460f4ca85419b151e9f8a6fdf085bc0a38dc4ffd03c1651c335fc658efd438d5792b67ec62ad0f6c5e5b07ecda2057e0c00d11ab02407e0944474c2613db26be5c98c356c929926c0ec5160151732c00355bdea3050c10c2c566889c9624c775de1f6554166854647c0a0b39f965def094155b260b342489de966f6a26296b5251230bb3e40a1d3dcabc13808199f39ee53772aa0179cad3f3b27a8d34de02d7664920577e7cb11991a66601bfd4c3d128b8285d0cc60091728f2693ae3645e2b1943f02de2a2a44ad5780770337c7d28fd632020235b1d0dac5d647a9b9a8d539cd7e054385533e171a9c97f52229af1a819f47077faf88956ea0126cae3dc536e45dd5d7a93968fff1163985bc0d0ebb3e9cfa02d92d6e53dc45c943372446bdfe59592fe89fd900594f9c43c9e879f64c499c0d92e9f8bbccbd8eeb6e6efacecb88c69d1119d9e7859e6b2b245a8278409213093c3d87d0474c0d9f20830dc01988a3c79bc5738d6cb5420219fdadb96b237f06 false +check_ring_signature 540027df1f078a40cd88e879e97e1f99d53e11a752e5136feb943aa8be1abb79 e4350229d0770a71853bbd4e54fec2abc56e425c7eff7b79333911c1054608cf 1 93e921e67021a5690f137e3e46d9d9f0306069bd1f63a5dde018edfbba9316cd 4c8849228529309ba79659435c80a264a6f1f0802d0f2a23513d6b132f5cb602e08a7c141ee09edb40f9f3154b1e23fba36be7a46d1a448d8c810ecf2cf71e0e true +check_ring_signature b6ebd1845e814d04c235a25f9dd9fa9735e303681205ebc104a73f0e53058b3f c00357a6b44263784f29e5ff5717ae004ff42ad3440e47d69985ec7ccdcf7c16 1 cc966c062133759b6fd0e9a1ea30f78982124a9273e2117872d3e07e90ffc6b5 8d60b5f7657c3d61133706ee52bf6c9004e69a930e4c35f334fa0e97beb357075b5425e465d1f20faa798e9edecbdba908b75ab54b3daca5052ef8e3d749870c false +check_ring_signature a6a17570d3dd1978a1e6592f85cd8f78c013313e3b0bdee5aa93733bf3b4994a faaa3038cf8f2f6ce17030a377a42e2ffdb2864f851847cb3ed01944369cbaab 3 f74496c6f2700e84b4493d81704890968b9df5ecdcc4e5065b1192fbe7c7d26e 273245bb570ac7991c105b3a30c1ebf236433a4b9bfc38a849d478f3ff1068c3 b928b948603d8f6e78aa110abd7c6c4e9613f3d804eb816af9a267a5cad1c773 94d5593792c0100e0c879039cafde868d96910206233345791c89ffa200d6e037ee9faa7cf920b1b1bde3852293d2b2e07a4802869a242621e6f8f02d92f6b09ae4f287ab8a24f6c863c9c7bbdc41d8b5349bbf6c847d090e8a7a5f01f42aa0134d6d17fb1587523d2aefec4819c9b87298f8a2f0bd404d207e12bf56cda51060beca112046d7158ee6c4d2479e8a828ede712c38b4427fdfcd951f41dbaf60d98ecd320db4d34ae2a81181405c2e6cc3f303769b0297f0621a2031a87706106 true +check_ring_signature b11ade7601d5d87b089008d06650879d129262bde1f97c9216606a367d87a432 c78744ac37f279ffc6e2221400d273e59a5d2d3ac4f5a3b3f443e6518863df48 11 f454224f2206b6abc6d63399edc5249a62bbf7328ff0f9962e328cf1bcd9b949 a0963d5a19729c7f2fae18220cb1f60ec1d7e4275538ab91d94a58e275ab8705 1244990509bf116e1bd73f39547150bc6f84cf9205195694f3fb52781426bdb7 75476962729a7e1896ced47ad11c6d3dd4b32a9daa4e6cc89790ebe14eefb4a3 80cd2d8fd03d5ab23c072e38a8e009816cf4b379969032fd54c8c0fa535fc06d 5dfb5121cb929ce2ae2538a4b5a6de91418603ad3228d0848f80b3a6d5b0ebad 0cd43d845df30991c60342c989aa13349cd76fa2d2bcd6cc973e21ca047edcea d21f4528ae67ac294cfcbd626c63633a9d238bc803682e45174381f960f8e7e5 8afb53ee0ea68683c25ad190a93f60d43567068cf07c89b1da4ab1ef3be97bb2 c01e9c130deed581013aab75600b2e8485742c61955c461ba8ec00de8d349bc9 18e648a620192c010cf1ffde1604dfb84642dbcf6b07a07ff7b3cdf7ec74aa6c 75711c49676a0b6ed865e6700fea0c91de42c98a1b1e526efbded0503b45dd4ab39188d9f0baf4c7d3085e55b7cea4dbd166f0d70b0b68d9d24f567595563005bb3dbadd8ae4a0ad00542ca2b6d204be7bc13b3bd594cadead94408c2348eb0d5839a14eca80ee72df3207c34bc132ddd599b2943c5a8addb425a7f72d1aec0d489145dc6cf5607041718f09f47e0d6ee9d6967a299197f8b3159396efd6c408d0a7c5f916a8dbcc32cfcc3470cdd0409a1fc9cf3c6b7a79d583b468e534480801420bc6ad1a7a1614d4604a4be4629313491061e6da1084e60392e18714160f453a3b80a60a2091125b091b61414fa76b8a6d0b1f9800e54619b015bd89aa016964243824e72b66e785189f01c0c9c204dee77d3f9437533d55b92062e6a10acf6587ddfc0cedf0fefbfa8e33fa217ffefb9e649ba651415dd163afab52200e60671447b1117210c2f78d33065786305f4b8a6940b62a6aeed3056aa9efad0d67e54b7951a4dcb4dc6a8f19316a8c9a5d9ca12dd198576e244711f4a7426e0999a4ffcb5346d8bd10b646472f18842ccf5a8c332cdae2e61892c8d9fa98cc006f1d5c81959a7b273f9a123d05528c3971fa75e20840983570f1d6904284b40da23aec53368fbec0dc84bf11ef8e923810ac8feeef4a568d54160096d7117400c1e6b6d6ae5559a1ca4187b7e0ba2d779e4bd37572b020a81af003c2d1e2de03e59312a0f21620e8b5c4e7ec62f61cb89f94d2d4c1c1ef4119f156edd2f08a0a1d6d2d674480a341d4883b2c1d4aa2c50792ad5e17e9e0b3e2effc154fadbe0d6a16bbb8232a32c71d4a24630d4a591a06e60f927a3b2205241fd910a981f20f21a7f2cce37872256ca2c3f130f747593b14b1aa051b665388c96134b9dd2406f644c9f91b00968d3db357a043495cce71500dd53411eca309d3810c75943c02d88cb33acd78153e1d383d984bf0a0334a66066d5fe6de56d919f0846a15ec0c false +check_ring_signature d0f809ba36f3080f3c683d30f753e7c8ca73032bdd476569219b1fcad6ec5bd5 71ae6a5c1826f4daf4619bd4d8f282b185e7654c45615e4e4585d3b994679646 14 3c64d669b60d11a6bf76de9f3c2ab85497f8bab458cd62d679961486455dcf63 a7768f62ef3b17e0de2f43f313d7bd478f7aa96ee06bed1c853d7693f7cd917b a5a4e6b669aaad185b7b053c58c959766c858c59332598a1f03cb7f3513abf30 28b291d5c6df7b44a5a896da9d2fff400faba4636f0e7f1ad33759e9dff61a48 56b94a75cdb04cd56b6098035892a2f438d8a162fe1cd0b707b3923192609914 b18303b652f5b90b479f7b60a66cf8bcd12d0e855a4b673f3a2b6afc5dadb8e9 37e9ca6bacb8d2b52a21e50b74dc2cd0184fee1827e18a6cbbad0551eda4266b 019d448dcd33e66056945f14102210ce32b344b86147b4e0cec7e2fd99264fdf 7f25e03de7478412817fd2e5334176e03a02370115997cdef22e8bc277edc4e1 c7e525914b448c64a4ec675bb23afc8b89ef56513e93816a58c9a4e1b726d6b2 2e90f10021172c9ce1247898d84e5c3b71d864bddb67a3011a5b97af04186b75 657c05fc4c13f223c5c45ea33ce5979f70fab7812d57a94fed607afa548f08cb 5198d4368caec9d02431cedb14f1e7b17d88ea70ba757f0d78d73fd43a96c4da 5081e210e35bea0b3f185be9e2cc325be9d6228e0e7c87b6aa13eb8cd1975874 696ac866378d77e488b5c87c002e782709ffd6eab5109104c7b8ee72bef693005261e3a3e1a2c6b4ad8b0ce6102134209c21e09439dc1fadee809540b711a10506531199d7ff6c21524083f6ea74342fb087be3ff2c11e252900cb784e48810521d3a02f5c672bd7cbc3926379b57075ba9ca4eebcc3749bbf2ae2acb13daa07f5ba529de34024fce0b0ab90167d9339c05f0089d3fb6f114a1a75faa9e22d02550c278b1c5c2013ceca58912e4d8e8a3247984b962dcd5c4ee81546587b700586364ca51ca935c7948d936716d3b21671b469d24bbbc8186ba8c96b7e58110c6df9b3ec55ce2e4e847a0af32a1ec7e63fc469f3e721ee9ce4882eb36bdb9807c2e234bd04b28187b7ed9663db3c25c2bd9a5fab22ba6dab3cb057be051e790a429ae1c3fb0946d21845ad147787b2faaa186e2712bd0a47b6c6c435b355430558ad1504f2db1b925463aebb6fdf9c10f96127380f676cb105d28802fd793a0ff5aba25bf0b06220743d1415760f5802a14ef3b48bada0bf1e1d8c42e60bfe0cc46ffae736104df7cabd6a25592b4df52c78ddc72d288166c15fc1825893670832448cb931fe9c779bda0bd2798fd7104b25c2f43089340d9a9080dd1f515707feb6f21796e4179debed58911723d2419e92b1de687ca6637b08bedadf05230823911ff389faee2cd6cd52c9ed627bab2b7fee551bd2d74379d292a89e3e640499d28e786bc84ad6e8b70ef826f1ed306f454e57e862248463e51f73dcb00b0a6e01189f5c08fd914219fdaa209cc1252d9dfbdde0c25b2a260f41e3634b1b0064528d2c739349bc2b03dc5194200e3e026b32a4a6c7807e8dcc7255e64c970e48d7f8cc7f1d42189ebc01ed5096943293c26c229a82a5d49796e4c0918973082a1c69b801277a89eb4b4f7cafddc80cffaebcb6515ae32c485e50fbbff9d50b4b80c616a004a508813684f9b137ad0d1bf931ccedf54e2fb604e2e247feb50118327cd63895ed0318f0759b02dd2a1f472cd8fe20df7cf1613bdb3a3ff5d40ea3d2b558c5b7e9bcc8cf67bd674c809cd3470aa23de6262f502d603f46581805878f103e33a70d35f10eed7233cc1a5747de8e279a3b788188965f8bd62a7c0b93634d6dc2b7fcd728d80cc4695263ebe5d8b98b657043e44c0b6c9b9abc0b0dca94c1e8fa60ae52b3ceb87d6f2a8963d3df1aa301ec93bbb19ed3099f5a6905315d118bf7e6a74c91e3cc92e008646791f80219c434bf3c3793d022f054f408 true +check_ring_signature 80a1e36d59c3220d016549203f9ad0d7ccf7c82f171ef45a5764fec5275a7d58 0464f008229676fc2a93ce86164a9df5b50e16cb5a79ef5db4ea6d78a743def3 4 e8b5a97f2102a9e17cc52d0d78c72b86ffd118acbf473159d1d512cfbb23b124 f7dfb1b5f0b601e8249290e8ca0974a42737c139ee6256ba64a84232dd95ffd6 b7057c04a1d8fc808dab225d30a5244634001bb632cfb7df95712c12cbb58743 db067073fe5eafa93b14f4314204c534f078e88a8debad47e4ba0507a17d4f11 37d875783a89987c44ffec8e17dd2ebe2ca574bca0d4c6ca83dd8ea4c5b2030749dfc90b4b87a87d021c4170154dc5709d485b00b9857570040f9b50fc60e501c24d20262d818bc7e208d362d6acdb2f75efb5fdfdbc21278bbb0a4aec37e606a8d07c525b5efea84bff67e37eda6bd3faed0692ccf7f548a53be82718dd8b0aa53b6a576aca827b89f52e216f47f0cf39856de4d88ac4074d809c9bf7e9d402dbc708417fd4838ff459c1ce970371212a977ad329e8520b0cd3c45e091d2b06bbd52cf4234305147b185cf2108f0ec9d955dd8e53629ab6e6470d30885e1a015f5276fa049d79bf29d75ed680a9009dd3be1cbb35476fe8a91d1cdd1f9d1528 false +check_ring_signature abc93fa4db397a94e00ae4656691cec311f5b2c1b7daa49876f1f5ff996cc580 0145a3fa83731cfdd8da7c97d1f78ca727c38179926b3de77ffd7523e458727b 15 8ee088d9dd09b6fc5f3e6fd93252451fb97acc8e3e56a0cdae527b26acef2a65 98ff2bb8b341c089bbb5a5d7b91a01bb29a5a94218f15b18277d0a2fee2191b0 4a1e4673b9ade5061fe78c11e96fa258e7c6d57a9d8da1f58fbde8d952666d2e 7fd84b10d4e2039b5cb1973b8d8b2c362210409e51ddf5d1cb49a965edb38783 628262cb17bfa511aaee0bbf152eb2d693142616d42da3a191883ea841090df4 b3bb8464a6eea1caeba2f2b0d9374f007e9743699d47eee92ec6056a2afedeb5 4e2b3773c522e3799a0fb30711eefb02f961857b7b93e41ca1ef088fdd9c35a5 03d7c51de26cb11846e9dda25fa244fcfe5f28a939b852354ff5d76692dadc9f 7c12f072014ad84eb2702d16d8fd1dd9652a93585e88594f8dbbf4d913866ec0 82e419de827dcbe58832d8aab49455f42f19b97361c83692fe38f6d8f8b032f6 3ef8651cb9db1a3e3c89d2c14f2e7041b3bbe1e701d4dd3561a87aa626d87090 5b0e7243e9821181f0226f0265df6b707ec8c78aff08d37c4a84e54d5afa7394 82ba7d693c30a46421b4b0e91db19d337e907385e6167cda2b7aa52e69011a2c a6f2e72d24afb08c6b66cfd19370b35bf56c0ca498c37a5ef068d3288592360e 93cbc2dfc2921953e75df99ce8f25dac551506e92f3926be57b933de044ba519 18cf70214e508f5ec1a0e0bd4f1f70fbf581fbf192d313ae9916a6ea1212b506fabe4a15733c60c112af9b3209fca210f242deb5da6ac629ee0dca56678b950c91074d4b8c7c9e72df5b956da1f2159df11eb29dce6eaa77617a4f463087d108133219f50f76169adf6886e8475e894ba3c956df7b8b9220a81e9c2a5f93680ca443274271d4da693da4844a4ee867bfdb61801969ba7b0b9a63b621780c8b0893de443074d36758f27d4fdc18492af2528fb2506952d63ba1d973a94786e502b0f0ad13efcfc4451e245eef1ed3197d0509f3576ee8ed4773cc84504dc2640a5aff485a02a5b9d54e8484031bec06586dad4bb9c98241824c3a6c2d72a1ea0f3cdcc1910d56973d509e4f37c55f6f3012b63d6afabf21b6983c7fa230eb97023aed3d308a0c61e0bf58a84e6cbd1e43272518526613a1f1e76d2e5b94abc7090d3efc99dffe08ee9291148d2980c536ae7149c308a42ea47d43c2cf04989407d29d1d55f0c3289b1a4e5aad5c9e7b5b0eebdcc0311c4deaaed111497dae6901030828d802a0e7a7fb226857aa6578997d8b52d6db1e8e49ff5d46fef4798f0542f77d37ecfc6744bf4685a318604617fa13fa6176987473d8d1a52b6b35980529767a7b9678dbb6fac81113cc6d93042086addb99160206ce6216c17cfc880c44aae80a9b196017020b406cdf730c534498c64738702203ca59c22d7c16f5012678b9f7538f4c31ba2a2cce7cc2eac78f2f93be40dc39108cfc5a9c363d310a984f01e8c1dd0fa60c07b39bab845cb5f2a9db2782f325ebc56d57442a56f906cd4ca3d94da9a96725617858f83b2a498a15cc75440f6ed20d8ef3825867e40fe2015138ff8bdd4c46a1307fe3c47934265a8ca651a895a7100e3846ae9ec80f600e2e4b8db9bdd9529286f9c98c78f511da2e5f16bc50054bce0331797771095f64b96f5e4d7182bcdd5e703b451a8097efcc31c552c33949f8410d58d513014742cce981d6e00e2bf9298188ac017d7be1ef21846ff8ed2ca1c423eebf0504859ab0bfe216c889d877f0177c155d979e37b424eb59b0cf48e798d79f113f054bf0efc6d964ca1ca545de26593e41b595764065d9f91d4382694f6feb6b140e9f66ba7fbe6d82c844c7fac9300851851508d4e4a287e1a17f4f6a88c2b40908faead08df61e3282dd22eff0d4e8c8205fb46da1c5ebbb3652bc95572df58b04f56daf1729c42ac0e98406a8f2212c82f2447f62eeee37e620306453fe9da2037dbfc0c8158fde5df49945dcd8579728ff85560fc0bf0c1311b22bf8440927c1e5f0bc6a15ae7269ad039ba75e1ea7d1e99eb98dfacb883a524eb5c678d4a506 false +check_ring_signature 57afde79f29690e8e65798674172571de610a728713806aafd25def441edfa16 e77b6b77061d2ab60ef89173c315b7268ab57d0be121621544160877b2bf3ed9 200 841a26f8db4bb162b0d3e6d5283db446f5eb415cc3941d4140a6d5331da75041 8e1579da57d1de7354dd4b644fb08d49e5f3aea1147bf252b511e997bacf1de4 90cd8d6e590b77e86cbaeb1031cecafa825066f20ba5f0494d7132e2ad0665db 65cfe51ae2359c0b6a785ff4ae25e17cedbf1b16bc5218c70ac627db2366ef23 1d9a9ae5eec10bad1a35656fa66d259ae1d18a746cf55a3de14bd5d4c49a81fb 6ab1ed10184034f68db84e10b6c4d44785b7140d898ef8afceb01e06023f193a 54ef3cbb26dcb76946c794c26959245f96fa5c105393307c8fc33f24b00fa824 920f0424ff53be13f317607229c9a67ad1d8b1992db30d58118fa6ddacd68d8a e0f09f89d4fab362ec3a9f976c28cc2ebacda5ea97d1f51d2c560a1f9a67ce90 dde51d6033196fb868d22e4e06e294e07824e728d2f519a683e0f443dfd5118f a44208cf8f459c48e762b7ac9682add6fa088f2c5e3a89cd74257997faae45a4 911cedf02c6c6a5db4ba8d23795dd08eeb317dbc11456c7f5dc9cca625066805 16cf68b95cfdf5e13885757a0047ffe016871428c80f9e834ddf6ff56855b3cd 9af5ffdf647b801ae40adf10b9d16fb6a4add2775fd298f2f2fb2e7e547a341a 958ea5c5b8a49c365ba0fd13ae8cee59e025a93d2717b5bbef8074d2660d8a32 9d4a71c1bf38c1528845514ef237ed8ad22083b585ddbf9fd56931ac2f6fb98d a9777484cfe9659c42833a6d8e2c94d2ed9c248b35ca4e5f0f39f76284eca0ca 47d0bfcc5e284c15e8438ecb154577fb17c30242873d4287947d768bc788c284 e53ee08f46853af15904aeb37bdc3f9272ff5e72eeacbed44083320ce7f0560b 902d735daf565258d49405294eb02dab9965ea57e482b0e53c796e188fea8bd2 2095ac44a82ce17f0e969c40b0038ba7927a4b5b36fabbf8cf5f6914437d0a1d 8df942cd54dacc82bcfdc2463e7532f2637c33b908e52d2921b140d4f31fb12a e5da1395781a0d64cd99e1068236250ec66443aeb50a883659bfa790cc05cbd6 a4fd487317f76d244a2a5da54745b38dd17746ef577c2c1b2c0ed12738d4764c 62785f716de46bfce182d51f1725fc2f0bcd4fb6c206b4ba8944a27b96dc58b0 5443386e622129f6be3c4ef3070c0c396ddb3f7300bda045507fb52b3f0fb686 9e89e815b526776dbd0ebb35846999520e5ce7fbd17058af5a1e4cd46ebd395d f045603a29467f20fd6e96215e8bf2fc56c56668eff56d75f250bc6e310fa5ab f32c8009868b5be8731e313eac09262e2e6156fad746038924541e9deacc225b 80c981c6811b948ee0211b71bb4632f7ac2b750646b77999cb3f30e8b64213ab 9349bfbae70d24d1402276ff979d93670b255a17a4273f54a288c0297c012b3a 0a6864562d5cb7f7508a6eca01bfb1615a58363afda8992b7a829303e9d444c5 c32c284d875c10057e729e5b33e9f228da8dabf59599aaffc0be45da09396647 c3ce419a6dea66eace29b57deec8ae29252166364a568dc09c75a66f81c9c159 eb0e09f582c75a57204b0c8fae6174ca8549025e6ba392949a614f6e578abd81 38648a7b17ce5a5c2fa18cd430aedaa0afc6983a0ce3d5ff50a3b6b465ff4705 8ee457ddbba4802408511de06ff315b642a46fc3438b9cf62ad67039af4c49f7 7e28deee0077562626dcfb8a9d0d9ef8c81bfe06a842be4dd5e0fd1f51f4762f 38818beda7591d2bfe4b703e57e6dca29f7e32f6524150879b74a46c980fc9b6 cf756c3f721a9b30592be2f274bc33f0b95e95913ed2abb795f064cc367aec9b 0332d75f0e6766f7048232d0c15fb3306efe247c8c53a6586fb8dd79af011811 0835baf380258ab3a2d5b39c57cdb476e03ae9f29c28ea06548a9397434edab9 4cbdccb1eb0de192e256f1f6e9701e6206409df11d5b9ea41c4aacff046e37d4 207a11e35d398b618437ba51b1c1f4d5b4e8df1647578f656542f777df8d74de e272a1056b63a5fb34ab3116fbc596fbb872d5a07310a9ac24922191777186e0 641e2e174d029518c7adf1da3c997dbe242c6e0854d112c667cb5dbef5c59fb2 d39c409a2334f98b543a47823543b3e07fa2261813329ab5f3e1352c7fa1f156 84e718c66b3f64fc03db08af661179602488e98ce25f3a58edd1a533a6964607 8e3fcc8088e25592e9f7c2bfd1c2cb61d3866c235850738a018248c5f10e6938 9497adc7071fced604db7f34c975cc966cd4194730114c001279002d4919b42b 72b903ba9469a82c8bd15c0df566687a8a8cc7678600181badf15a95f89d4b3c 0b8ae266eda871f2324d47607ec5b2cabb08cb82b0ed189a8b805b4502def03f 1a70a546b387cd7df43107f66886afafb64d9a76d56d925f34cdb958cd9811b1 7a0a39bf5359f8807ed0a74823eb18acacab4cfb549f881e96ed85baeabda177 6c1e964042a9cedf0af642446833555708c6a07e9190555ad95d7a9eab209c75 f71098779573566c5df11a6b3bf898a47549e07eab339118ce02aa16562a85b8 78aa00feb3a14b63065923b75e5bf971854e202000d76f6544a969c24e5940c9 e1a2674602d82fe04143dffd900136c8cf1273642470737382a4859238b541b1 105e96c1c5a7e817335abb7a3663028475d79f90c7e3589288258709f7f2d87d 30a5d4444716a0a5ad5c3737c0b6abfb1e03d0d43940743cefcdbeb38c0cb74f b28ccb4cbe6a2b27ff5f9530bc677f0c979e16acfcef7ed7f38999a37041f260 561a22e98d7216ed62375eb198d0a99967d871e934a3a7987c5b8eef2df82e90 06ed4d29426d4a062818c27681ee4fd24a7d59d0ad3eaa1953a4a37d13616629 377babbc71725fb5e8bac414cc0284f571e9d6eab706f4552febc7e1d77cd3db 28ee353b240206c26da956faa8b8c2f99979273256cdb29af3d577a912df3eca d1ae99f3e4c3b79f1b2ff59c120586beb75f136a3e618b083520941e081f3f44 5f64b06308400568b201dc040c04466e900e2b22288c332af11b120749e141c4 9c1cacc17a2b4c25dac633a4b6aa2acad9e4749b73c1661b4525be20ba22a127 e6e90582086e19d2610fcbcc8cb926cade38d72ff51ab1caf98f4a4a87b5a9e7 8eb3773567820e0bd3a607b6101eb65bfb7fcc2d49c8d49c331dfa312bbeb570 3ba2bd19aec9ae33731d1f4dbf8b6878d84c3b58e48a6ab00f671bb72d54c897 789e35baf5791c9baa7eb3faad2fa119e340d90fe7089153d2414b526258882e 87de45a0d9220cb609c2fdf1aaebf648eae40e9075fbe9749c475a1d5373f053 a4081f7b70d8f0d04fb7a7ea28799c2ca821317cb55699c86c3911ce6b75aa09 e60335735f2448d87093270a1a6f75197ab881962020aa4f754e47be934789b6 7ae3723f20bddf4117fced22f00dc6504865ff512898cb42775da3a64544b992 b54e862c71859d070d586a0f057d611f490d03c1011a407d79321e19664a9643 7cd2c8142b74c69887542a33e639c893b85489961e640fb1ec63e83040436277 c73985cfd6e7ca2cab3e51434d0a4dd5a1cf6e5760e5a3e02bb5d6ee6a15687a e2ced80fd53ac0a5ca22ef994cbeef0e58adeeab1e3a1abee422cdada21c3b2c ba3a80162fc09a1765d44f9c6b934692db2f09a10595a4d02a71d05b24eeff70 8093a4f1ead62477224769bd03cce3b6e1a94d052fe3caba171492c7e8f3aa12 299c4ec8bb73870b270e2647b4ec4b600e364576d55fb922994d063670fc2a65 bfd2913ab4dddefee7db152d3001cad2767d748fb3169cb0f73bfab71d51ecf6 8c76420702fdc0dbaa15a6a7dd0c4396424ca9fe0c86a3cdafdd76f8c4155106 81c5569c0d8dbe6fa2e4ec73623b55d1ddbcdfcec2c22561fda48489a712a291 0580bd64cd7294be0bf41f0e4064ea39b6c61ce458e9d344b75deecc598874b1 24b39db927683478d9be3c17c491b518c0bbbaa0b0faf90761a7873a8f5f969b 5212be93d2f7832394619f9693c8830445913fc4f4f683a229a649e4cf99eba8 5fd76f07f2b845ca034bac4461b27542ff1c5b3b1504938f8181f5628da76ba0 14cd78496b732c20fe3b9278225200a8229c1be1710bda006a603553a96c46a0 0a733f4706abff3d27d6cbda757bb667830958ad368d070c51bf4f5a5acaca49 4afbb4655d856becccb4df3926025d65bc01b5dab1928755d40e93cd3af7793d f5f68e57714e25c980741345124c28154bf33d75666f7d46c4baeb0396f270a9 5e0141c135040f21744688b93dcd4fe09871cce4c81d95f0fef3038694069fc5 0b576308d6d1ef3d18104b7997d6822f618f2ad02e020420895de0d2da3cf84a 0cfac39351879563208dcef5051b346d15c5c9ef7632553f9f59547acfff9b64 05b1ecd5a16e806b80bc81aaadb346199d74e65b2fe529aa3d0889d79de3d14b a302b686498cec85391a5c1e7baef1cf5b55eba68657541b3bbe15e35105fe60 77d205589d8c8b298a5e323559285feca536a09876d207913f50c54529a2da73 1b9be25a829ff9936c825f66934ad7fa7592d83271db41fadc8e96d674d90305 b292f3fb729bcd392c8ef6799bd77524670853bc3d8a9d46bf3944ff37256c6d e02c4a21494aed110f55193113e73c26de9cf068ae6cdaab0fd2c036ef71d7d4 20ef64ade042c11cb27f3b8d2907ed8f83505ab4d83158b3dd60fa2aa85c5a5f 961e4fbe216a1cbdcc150abca6ffba01c6a0efacd580f779beeb6e7388430e11 85a0af66c6fe2a9f9160568c913cc65a66760542e143eb6b8ade566977c7d691 0fd8bb14f1a117b9327153e7fbdb35b435bb316cb9c08cfcd642cabb04066372 17a0eaf749d804d8ba7970051e1544ecb2095c86de8d9c6bc6a0a267022298b4 431c4888421a0f28376794ae1e99bf3755939b157874ad5a33f4fc15819c9ce6 0379c5d6704a3f9ce4a2e89ebdf41d187abe3f8184057ce6e1b6ba37ad9bc056 d2ef69f1c8b6d82d91f1c0b9b9e94c7f61b9b0a87ad57b4c68ad9615a511c44f 3d1a79e42fea77d3ec147dc95851c9373940c0de7d83329b47eb4e7f21d93834 49b8c838ff41ea559d973624b96a0e0f3b963b406383861e3d0c4758406b019d 43facf9cadb624dfe26caaa9db0d3462c4437c8e00db3e56dad20accbc0608e7 105c63082c395e8c5f6bcba2f7926512cd5f56c59fcfa97fc419bcc2e4cb0045 f5bf43606c07a54402401b52ba1b9326a8ceab5b7aaa2d2dc41a7529519abd20 bb06d0e2ce57212b4b9618c077baf67387074878d22950cadea1608ce168f8ba f22c753c609d99b436f81b3aca91c19613fc7c79942520dd40cd79b8753d05a6 613da71b060612c66ee402ebb489adea563289b3ff0300fdd16c41465d01d857 00e784a05a8481b6b9c116a0cef55d44d5c5c87955f83af4ce99ff866f9617f4 2b758093284985690ed95482e3fdc44dca695a85ab3bf37ac8e5393ef8ad5610 1a2df56af1151f7bfd3c67e995cbee4e5383d4ec5226881a50d5f325a2e6df13 3ce859dcb8a96fbe81a313438107826631a7d25e2d7b0647aad319743921fbd5 2f9ff4c569db3f9e5290d7e5fb98c48b89ce46748aa9941f5fe60f1a3ef9e3c9 b17975a9ffd038fbabaed7d06c86ef26e1a91a81c14312f012e079312f284f03 1b2456bebf83166eca3804dd14c114163c429e9ea62f1a78e9d1d5ebadf99f30 5e3cc567af90ab418fb89ba417b7024bc2b0402537ea3ae7f6b7d0ea839dba7f 8d03fce43e91a49f1d9b55c14ed1e51257a70da2a5c35d50af80392d76ea9f32 af465ca91ff389ba99ba916d0a9db3f4bf7ac1fef84a1950da6e495d20546830 df3b24ec2d38a525cfc0ff808ff78fddc0b1efe5df6408df93bf9081f40d861c 0c1a39b1721c2e883c78992571371339e7d3631713479d314afb374715f18444 27559795f577bdec8bf4d617dd7675ec308b13fea7a04cd5014177e0fd7f664d f2e9d7e4b958741331122ec49242f678ce3b6677528dcf20bb16de3446dcd2c6 02ea91b5454fd39202422fa0a18988e5a6ee9e146e2e6c2dc757ed5d146fd950 28b6d69854b93c763a144a973a606135f8c6b760d69010ba5efaabefbc5d963a 6266ded7b756573282da2442a11363a5b7b4906014f75da0f3cdc4d4801e70ec 864435ad53a5b074b3852b8007dd818d958401d83e617dada5e44283b04f1497 0f80152a3e5aa5f2940f242ccd9e557ead306c42252ae4e0e7ebe520151be9cc 27d57fa4b10f536eade748aa008722a6e8ede5b788380c8c80b3897fcba6ca62 8c1c93392df26ed37ae2091229a1974b2715aede167cb38d08ec077c316165d6 1e59a4d0e635db7411505b9f64344d940d35366e63315ba16731217912e7ebfd 71e8b0510f6554d13232b5f2aa8a60dca95f75c9f2bd15f81894050e5e5806c3 32d84268cc15e03783e47ea2faeff61c199657e4d464d8bbd22616650dcbaf07 5d09cb4870c51946f497d6f000b610f0d85e1ba636fd5f09e9bd7c369ce13ada bae4483a22d32715668e43f26d574f0a3f7d38fa8e66185a0e0a2f163f1f58ca 83325c2238af94d3b4ec1898d22c46bdb6059d5065e60e025f0cfe3acf869e01 69e93e6914945e39bf61487de870e25c46761dd592279e1fbde43bbd63d17092 dd31be0385f67b9652fb9178489c0c0a3090fba10b24f5a62119626d797c8426 1a2f11b8718c756d88c9036aa32ab89429104bc9b9672e7f2824ae786927a200 585d5285d191388d56ca081b9d129f774060108d99387c5be90d7ce3acecc585 eafa6c69c8e5f068419f3f892430439a878fb01df504f5a4391f4fe238324221 616da68c8bef10530dfcc089186f74e98e30d35b88a29977494788fb83917a0b 1e4d32d0be63852cf72a51cf9a6431d8961de45e3b12264b7c0080bc610e76e5 6cb0265d772206b306df79140dcaa7751c6f81d194bc74e954e7a86b0f7f606c e66268c381995494f16b2352c8b914a5d686e4139a69c5a83bea896030ddef3c b186e6bd1e2b294ff23bc5b657ff7b8360859f212b8a74c10c61328977b2bf95 39aec8a688d2f06933724d8cc6b9f6317ec9fecd5d98b0201cfc62357e4e809e 23a06fbfd837cb92d70dc4ce040c1cdc2769f619ccb57680a4f84d0d44f4584f 6f20fa5a2cd31916fd67ca35296774535fabbe2bebc95f6881ea2d5312510f33 3371b5ee6296d793ef982216f5f65fa0c6ebc949309c32f98558a57df5864143 b95082ac9b01453fdd01bca185a7b4a8522731cb4f85342093bc9959349f84ba e912a132896378951e508f1c056b3c77eb1e0650aa82ebdfa39e042b44908d07 80da9b1015350edfdd2ca12c9461599c1b79e303e2ec6e6552c2cd57efa468ca a0f9bd242f34287f628cbc63a56bbe1231919ad3a9de0738f0956aebd209a259 41ee41ae5e57be8d57835fe34081886de3cbb19c47df28228a26bf96828aba3e 346c2cadcb564c07653ee15a260699f7c6155872e6a725632ee94cc6fbb2b086 812e13a8cbed9bb0fb874bff5b33863eae60ae1f0ab11f3829463a91165caf70 bd356266a06ea3d4abdd75f7faece821667212cc73c8fb11a5db98ebe85edd58 3b5dbeb683018c1658eb52e48ff2799397450762fde9f3b9802a6c29d28779d3 35ac07d46114c4385a5c3d2efb60dd35c52ea3a9042cdea262746a9285d92f12 387eee4e64586b6dbfb89a9da453768229c06f735ba93a3a546b485eb80c643f f3868ef279b35ec985c9a6535f972c18af8bfbee9806dde98f2258306ca3b4a6 74c0a00512f424ee690a398615b0abf770f3e74a9bb3696838971b6f1793aa43 463a92201eebbaadc1c7c2b48c945631ad945e10b5da9c03a81260e1ba0a3203 0d38a0992addb4ff52cdb40af796b17240cd0610ad0cd5da1e2de1284700921b 626aff3a30b6958d854e413a4e11f5c68a2ba312b418e854c898912dbd3fed50 aede81f073c4868d2d1d2a3385a85cd90851af9291371fe95ace37587ea614cd eb527ca26cc5d57a64b0ca0661acd1cb2d8b292d2db637ab9a7a69fafb5c45ef 47896eb9d8a5b22f23fe79d51f9b809894ce11c6666b45c66f83f66ccfcb2d07 1bad7ee5f47fafe0a9bb7367f9cb87942aecd080e5d137693a389953f402419e 9330df7e5bd373250ab3a86b3580035ff056e778bf6c8dbffbbd590299e63e9e 162b76e5b7cfa934c70dbe0594bdc4356ce79e746b166e8566e494be19973706 250423247e8a37d4fa4fb7dcdcc0ef076d339f2520dd786bc07483546cc5f00b 680d9412b2c323103958d4f0b065578de7fb0803873fb7e1052f766ac8449e3d 551be7f178f3ccce7829d591746a229bee20b94a9d5d7c717f30e2d89c56b58a e251a25bfd586634465eafc8a592044bd90b9f4e8eb94e91862e992b934aba02 e7a0f876995e3ebd959b28964b863c8e56c08d8ee3da7bd079ce05fbc6140b07 e32e3d7e53fe31c1993a0d3e76a3f1b9dd16d847b4c1a815a341bbd284791c88 57785bc41d467486679b6ac8d9603486627378e271ff612d399cb746dca25972 3896e6983f4b54fc9079d5ea07890562ffc019b8be04f6b0d40bf2809bafee0e e98f89c7cc35f6bb24ceaa593a1ac12fff6a24b0e35d504422611632fdf46bd8 493acab5008f5eb96758e9899388955ff64863dbd674787c14aedad832cce824 ed00dd6ea18b381d460bb76a9c244e3ee4b7d5994e98a0dd2f2b0fcc6b65b02a 7d2beddac5aef92af1d28c2441cd59a1b29ae16ac63d17f248a599ac2485d432 4e2d92b03d38752cc2a627d1c720f50ed82e6cd491de6687d430e17b9c77929e a8761d27c68069d9358de450b31ff1933749dff73131c547308c7874da073dc7 03c6759623694c058d6ea278a83872e65d1d303f61ee372e96e486aa1ac378e0 89547a24bea30592f21df9c6b82f7cc0474215463e3fd0eb415105acbe89c66b e7554abc3ca4831eeaa2d8af605317e6f7240746e02aeb39848d669eb1558682 369349c8220fa337f9ad038f15ca173fb734a31e5a1c2aa62ffcb7a91771dd20 2c76b6a829fb976d104a2c8ddc3e741299b7324cc8ff83c813c8edbbffae19085b5f67751e807fabbb80795e4a494fc84f034ed65bfe3c4e814366e3ac45d2098d45ff39208e477cf7d14eb9ff95b44c8579be30375be8500742d6354106270cf1b1c131377cc3501cad94f6e2ce87c708f90b6a97d55c96ae879e3ac039b20b34c5b5a4e8dad85f39f5a7037b1eaf4f62d228314793670dc3a4bf4648924b04b8bea5c6505398e70bf3caaa5a002d5ecde765460cb0c614e2dbc329d3f60301d5d665a7185d87e51cd9d3b1e5b5d1ce7b6186c79ab451cc2060914b75e02700c428c779cd383422ea7fb8e8d570b1a42d1cacb18b2b0e365eaefb8a7f0ce306ef714e236dd9c0b323ece38ca27766d71a9f6bca2bba18cdfc61443ec049320511f93b07e38900f641ee3edd46d3389be08c4f878a045325a9c701171e1ff10ed4477dee52500ad5cf31487d0265b22064e3a1aaa605fb2ebb0249900e400501dc2f6b7b85a34a3904e804e60033d32a8a84a50e926346fff61e777e20b4ba04c78ed519f1ffdc5df3a7313d84cbb2727761ba42f1980891bfdb7d26e321ac08702fd7aac532d0bf6834d2e7ff4c3f6d0401c5950332b3162215dd154d36f9013bc3690e0264165707179b349cce3cb9f5b59b7691ffef02a771b69e92edbe031186c8d7daea05c2d4dff07038ad4e3d792522f9ab7b9761c3b3f65b5237ed0211eb32b59b1838c16864cdab3e23c97f219bf43d4fd26913e48fe43c03f522085b2df8213391fafeccd817cbaa2be418faa930a55ed6be99ce5916f81a36d1091ea011c47e052bcd15ab30e7855347a451e0b457a6a5e12f8b0fb96247bd6f0d6117658dc6ecf0c6a594562021d2d68ee3d5b4fc3f4dcc6d6e1f1a9e0a363b04af98b0b46164a7547f4bc866f0a93d7b3524644dd9ba85c17c7fadc6f7be69003e37a55a5d1870110109253c89e577073fbecaafd7a9fd754aaca2f46b3133052a63675c2b6ce09845cf454ad9e629bea9607da6f01f8223144c0e6e3916a60ed318f9f3e573f85f46d62509624f70c3f61f4f006ec9ebf9eefcbeb5c19ff109bd71ea8ab5fa866a44e5c313d00f61217578bdf21bed04d8da28c145b43163046f4069ad4319f71469a98abfe71cd7a14686170d28fd8fe04936db06ae9dac0a6da3235d67506da92aab8f4a13ef0a4c27427e0fcee289da0c2abae94f924b0ee33e347b1ece22d2e1b1513a10e8fc5b41e9e3b416c130ac2d8f13faa3e9920ae245deb66af1146708f73698210fd4bce202e699feb8b26bb5c417443093f708b03ed23e173d82c4eb98227cfb09dd0345360dacbbe5a0052450c1e4c2f86702f19d47f8c2469e40d6726eda6810c183f58a679cc252d8cf90cb58b5d80fef0339197cb2a8bf8a045e687b2d7107b9b583ad347d885ef8da31741110fc8f2f0154c1d5076641e60ff358d68aaceb3fce2f6253a4201f30e02189966fabd90c0b7feb871286ddb1030b84d5ada5b753d2d654e0d90ec537923f8e6ede34ae900cf5989cd4947ad50d577e8fec68b12994f20461b642346438453a4e44a8f141090dc276f9a0acad2fda99f760bc226c5c6399033f8a4efecb2082f0df3320280a392d2819dd2262b6cf4d7a8b90f50c9197f003dec304ed4351d1182b3583260c918737554bf342a9019c72abfb0c3017931840aa41ada93f872c3f231f866d01bddedb1fa96b996541d25f4a706b8754963c0d94852c03f89cfd3f3d7d15170221a88318fe1176e803f1c315b13a1bdf867ac43ff41b8f465e5df28f8c9cb3012059b823b137d1c2bcb98e2494ee66570a7936141ddad8ef234c067a319dfd0198d07ed6013436e6f3bd6778123980c996eb8156a5564ad2c8b99435c12d09065a9f682911026142ad906e27ef239d03690d3214d04e03515d315e6788dc220650e28d4f57a8970e26233c0be88812131c9558f5f58b1913b0dc5d982de39904e9224dea42ff7f4e3cfc8369aa30e9fdb21b7ae9c9edfc8c97f1a3db319d8302f959906c425f5208325df8e293496856ec23eeb63a00421907bd99ad9d918100139fda6a6c1fd50ea46d795b156216bb58ddf730558ff52058e66dda9b1c1b0cf038f18bedfdb79454e895ffd6dd84535ada64b313607dbf741d6b9cd3107f01dd17ad60146330c794a76e901e5681cdcc6b4aea1d5980224b321785324e19053825ff70780791e97e76df3d7e5d3ed57452d5418064ee07d38a76d2e7b2070c5c3fe8b37a5b64191873097013328e359c7d9fd24c0e485da1074ddcd814ac04c3c68e1e1a82934b4d7c695353acafc444cf576f5c0a5e83db13f4afa13b5b09b001b1962354f8049f20640f8bd9b805a72b33330dc77a28b2fee67a889b110ae24db99ea733d246508202ab07d7b13fa8930a610ef568bafbd42b46d0e97c018c627497145080f653f9d9a2fe3713ec1c30e1edfeddb488c54079ec85d392063276af8530e8a896c92f5372ff7a5dde6e97d5ce2e96bb730311f01032de190f464499ecfc17b06ddae5a6a1df2d169a47407bf346be7460675b1800c0156d0ed6b3a4be3cf4027a25d79fe214ce2c6f0ed7b9218796c0f12630fb62c3004a0dc712f1ed0707138832dbf332ba0a43753f2739aefadcf61a860d4b956cc7c500388b1caf722c642c407e35ba07af7119b8ade542163eb06fca32bd79e75922070895c1e01012e16def4e85fc8557265b56c5df29874924ce4c77f3fa282f85005607b2d8c718a2dbeaf874c188ab621309710db66848c63cf685b208aaa19c089e3e343051129d129b581e63eefdc0162a92efdbf8ee0e8fbe47459653e7c80d56063103d0fdd9b1146d28e3bb38a4eae24f878b94014c5744d7038b41bcc60d875072ab896068c35c4c6f435ba32d3b18d3891a97d43576db38c132fc706b0fdb3c93e0afd507c4f03564d051d7acdced6c5c7a47557b89979b36501dfb630c3554a0fec9e96016154a3360b1e227a82bec54f797d7c23b8fa237b40c430a0f8111da11336ed1ba93f148f410891a8946c245a12e78ec0c97541ebb05f11304b170ae12f4e730d04ff8016e53eaaa53442618f7969c15537828e891e1dd640e44c6b7c0f13aba25c64d1958108fdab9cefdc22be7bde38f9386aefab2f2ca0db1fd18a24cb5888876c4a3b9c6f657ed87fdc3092c04eb5a128b7d37313df2050af83e8391a5256c9df020d444685702f71d50167d45494d3be7f7c0ef39f90d7f25b634236c759fe8494f533a2038456bf13a64b07d024f33aeb52b64fc8604fcb6d3f70c10fd3b1e41bbe7a3904308736be9bc5535da9d4d561062fc0c0f070e7286d3191e40a7104751f060017f660427dc2465c7a01abc87019792408a06558a48d3e9125ec8dd7cfe59229d73a871247d5fd89722037e2330d4d8ac360df431c2d21ade57c30d858bece04c651dc222660559dfa7deab6970e2623241047d04f1771f5adb8aa0b1f0a4a9561baaf5d9bd5482c2c0af6e6e1887133ecf03743fe401fb620831bcb4fa0d453443a8c3ac3fb0e8fc719decb6f03173de1c07b9951c0e5c41b308a16e9cfc4a05ad0fa3c3cd20fc5b99bca68bbc550f794608c14ca3f9f55d34ed4011d6ae77c5cdc1fffe35d2374bdadbfbeace3e94cad4055c1946762430641c80cdafa52c6b0e817854d460eee37b1afafbd46a389ac00bdb3ed6ffecf22a879da307bb9536d97c6cb97fa0b54f98ea31872b39058fd70ad4f57ad8c63d8273021e31a2d35b9125c56792dea50189f2d95846472bccba0800c5e13d713bb9a6173a33d1d27c22f119237829054fa4a87470f2ec54413d075c8cdf0590f2bded6b92f5a3fac386edcb04ecf43b8ab45a1122ee41cd10b50fd6d2d70d5d4574a7dee9bda2cb3dc0bc11a4032da91120d096af054a74f1050f9e8d1f3fc2cd3a2a56d23bdea9486253f08b12b3729d3356e44125d0539a5204875e96fb7784a751b55756959ac9b0171cc7e7156cc124be8a20bdb707058e000fbc5ed98dc8fef2b757e8c1197ad18ac0e49fe9aa481d9b2b286a0a357efc065d97a38be4a4789fe30e03ac13d616597eefda7963044f50362ccbb010935f0d07121ab3c86f5cb090e8b334ed79b8d72c3fedd7a984e43aa8e2564e51658109013a0149005a82c34cc076a6b841703954d97163ad50b90d4d1451d29711610ff50d40843b4f2e62c095c6cd726b6b6ca14407a09fcd155d852c55a29bc2400cfafd2c5046ceda65821d1aad0d3df17cfd5a86b471188f24629b9d423165ba0dedc208b504234103f9b4ff2b002bd612a06167c24fc86702d4b8bf14263ef9084daf5c23ae78b4fe8b2905675397a964ad80d1091ce340918da8939edd7ddd0226d4961f84e0715e12088cae9b40d6b80e1e8cfffbe432759aa53a74bac67f005ff73ba8d3298f86fb9a59ffa5d9e8ffaaa3dc9f905284c68253b91b2e398f095e0b750e1d7a0f22b78c084a2e8a4c38ef164cf19e52e8a3caad7fd37068be02482ef4ad3ac28df10b5ac80a077be276b60f5e808d90bd18ad60c616e9b7300a8d2364f3faeb33a8c75d376f9e11ee7a6cd95ba546539b43dd88325b14a23c0ee790c2cae668c44a4cdfb9e3db42fffbc576e9864ca0c918763c6f71a4b1a8038f334868d56abdcdc40031fc3e65b035c794ba1516b54c8885469078125bc70e6bfaa5e8d516425d5dcb30e9f459e6b7df293abe139c0dd49d8823fb35f1d40825360315d9cb7e26cb7145e5cd4835b129256777606af266621b676b989cf2054df7b630ce2d306f46189b06ce4bc6c301557c4606ed8a9242d6ae444f85e6094da4d9fac968be80d1e1bf46506f149bd846fe823ea05fb6199a825eb4afd30ec888f436275a7a02073ac111013278e1abd4423f28e3859f4f96092951d01a0fcd229344e9325e19bd9444191370dcb5b5116d0e2450ac7380683a91b5a8d00934bdfc9c798d4e2dd5b540aa479f30ef32b09d4f295b9bda32bfcd9c564b9c0d1ab3f08997cd9567bb1f696fd8d0bb1691980e082257d5b4ebc930f6e7ac1b0edacd1478a0235f865de02d3f93297111486f75b2d669b35d55596b7793e9110d9a01079582ad87297fa56adcad4de2b9fad70fdf809a2b9befccf7805ff3c803d258ee81a3ab8ed1ec311aa9035cfd28bcb53b5b5eea0f5c62c4daf52c252a0a8e8a36011d6099ae4110a9681b3fa7e90116c303fc4bd0c334b2313d03f57201a1dc24e29306701b8ca3a8c06ab6102f44cbbe4a56078256098522cab261740d13e0f6015974a51ae9d64f230ed02b3ac9e669863a84e7bf8f710681457fa40028c2476f08eca5124ea246cb4c64c49f7f51535232f75d34f46b76dfd6c28a04382c67bf46f0433fc0d8d6cec61aa41b36a7fd8f352be0256cfcb3e5f003470d7b9f1f945015ccdebe203606fe19b9d7ef693f8b02936d2d4fb6e2f61707960bb5f47e2b4c08218f0f5e85b79e81f25430b8c6495d0ad53e4da6c30fdd3004053de1b2e7d060b938b722c3a0aef38aa08bbe2cf2207f259be9aa52fa4a0d3808954d2b51f2a7dfe28f7a04b2fb0fc3854558934bad87cd963208cb52e3a0a500d3b25b8ba5372d587975d0881ed58066c4994f7c4bde46f2d20496717a4e8c018a003a3772ec39dde1550efe43918d4ed9d9b29d47b699549b99652fef1cab02aa63b6c2d5d07b17f46544c3a0680b5c4c4d7b0588f42b54df264ecfb34af00163ecfa18259530ce356b8af7d4c49139343ee265c9781617d500e033a955eb08f5e64e62fff5ee725f3fdbf902683c1806b0079b75e8d94c7e4b5d5185cd89033fd689f65062cf1366a3e914d4ff2af6604b6ad2a2e9f433a32a5ac5d12de606e8841f341b319d9eb118d6e3233e4e539aba093ecf935d1ba700223b9cc7cb07170b78d8e1320143eaa7070a9d0053596bc8f608f57aad97ce90f3fb0cfaeb0b636f0b5c2dab5f8dca60e623b854130434a6d6c432a168539b5952f938674f05c3a4d8a654b1a9d366a62dcff1d67b826deebbd57fecbbe4c0b33c069811f508b9f5fa5782de8f165b47de8fc48b75b293edc143e6d088259301b23ca64c99065863bb143b0a03e787dd6ee9942a6c624a31e0d6be3bdb4f99723a2f235819065074e8c975fc4dc0ad845f61906831c03e085907703d6bd4dc5282d36fa28e06070cd063d3fecf8f74973a2c60234eb9e875205c75652b89adc491e3386a8904394caeb656f5460856b6742e1a960cef1051f0b745d2566c513c944e758e8407d80eecd98c2721d65b1d6e1896b030231157bb2bbf09febf5d292d0c1c028a009e20c5308131817865f2cc746358172bfdc9bbd28f31cee2f876c3d552b19e032227899925d4d3b24cc729ebf3bc38b51d48ed1f6f07305b5e18528b429620005a3ed947f81b501eee8af7ed5a2808304e3c2a7a156d0414cd02429aa9a1b80b853b411a23dcd292e98b7aac2bd30607456f4f0ade30d8bbaa34852457e0d80b5eba690793ccf38820dfa12ec3f6030dce314c0c11c6af52124e57a5a7810f0756a0520bf575b59a0319046c8d14036c071ed4729822cedcbc8e0321aace400afe23c420ff456c5e82deae0c4e57e9fdeb363f8a4f244e454fa6f1517c17af003e6e936ed11752dc3b7a40604cb80c7f188d51fe9921679af1a07208b25e59063752ede9582c063acc57279132115abf36bdfb97e2f1f34930d80744a2526b08372576be2f1cf53cbaafd1836f84fbfdc898603845b5aef3f0b7044254b23f08ae6dca57b21164814cdcb884aa162b19879be9a47d41df3d00c8c12827f4c60f1cad6ad1be077941968c0236db0231e77445f8021246cf67a771959196925d0e02b47ec7592c88c4b5fcd9d4278bb2b59b111cbb18b88e5641b0a9ae8199df0e1b150718addd50103ea23f724fd1dfb517dfd9cd9a64fa52c0b792219d1b4f0b373d23464b42f9b5463b81caced2aea0a6ccb30528cb542b36ab166f1a33d904876882ca7baa5d07863710b845fdec7fb256978919851b9a0a4a02cd81a2210a574e9b6a6d00b0e3bd3a00deb9cfec25a4978b452395bd82375f23105a6d300d132c0f6c1bc652a6e6c9eea8e52a50d91997d4464cca2cd245afb16a5c3e4d09fd943b59c260afdf65a586a09b5a1aa5e4a78565eb8ac7ec65ba730fff034d0c3c475a1fc6ab4f4d87c8378834dc4c99242761d13168ffa905365ae2ea7f8805aa2a7e2a9f44d98a634205b64201c4491da8d34e29fa0c9d1529a3cafbe564062f4cee593404346699a864821455cc45d78634ba3126d0beb11459567a11fd07aaa3518b19b313daf27b4a92d21ac5869d1d5210c0cb2ff01c0a544af7e5750cfae2ad21c2b69178d526b32bbb7254eb14f89fd07c61d353754a8a04cc6f010f317c83d7d775c939d9b94bb233bc7aa7ff7d8b46c8faf9e7289d28835b03be0f819b573411755b42291ce3dc2ff7b4af38a023d9a9a65be0545d03224c41d60513b91e559ad3949dedf7caaed64801e66ca42b2bf04bc56f4101767e3aaf49011d89b15cb3e3ab949e0e0ce80efe97a5bfb695c5dd2a3fa4a0615ce50abb47074feb5f8ec97c57e5df30fab04f5106ee2d0a4119ed94f9c3c080817748cf5008e74ac9eb1ee0f9a87403ca803a310badee0a897edc7f10ba9adc8e63c0131308b1408cd8268e0cd0becfccb732a54ec555c87d04eb48408cd126a436c0915e076939b8db7cc820bbd0acaf665ed9e1a3a440064eacaf63568eba3b75b72d65087ea8413fb03c71ddaceb04d53d5836659b776a53b48a7e174d1356697723cf0b425af3432e49230dd8f815d865c42bf6b68a9b2e8bc4765314879b3431727e00d627cf1bebba0ed3bd6b8d1916d4f30c89ce76582b5a415043b709d143a4400f5122d1c67378bf18b76ba16bddcb285a398ed6bb9c30cd86dd185674c2485d0364102cbe898bd30de4782187c19c8c198faa07c2ed5c2d5ec18fbbcad3c38907cab6514e36c5b6e272d271a2b392fe06a78bda6ef636cb6787cc89789c19a700d74ec95930b42c7371a7531bbb195cbba3162274944dbf6154c200eba5c0900f245d80a97cec21d3ebc97a9ad8fb5e031d00d1d8f5705bbd39116fb90078d106c322a6f1ebd5d871d156f51f1c7eb4c410dd2efa7ffaed4066397f85876eab02f6b3e5802b2601ca0a723b6941237360afea86fa7d6c228c24174f615d41e0060cb3768f86e96710adbf134933e0d85727830a1a7bcec95e6ebe4561947fa501b8275d2a7f148baee4b51fcb2442ac280a3024eb3a1fb063bce7fb1df2bfab0ca37198ccc63b6b4fafc9a4848a2261000fd80de7136e889bdc6c97d136d4030c4e0d4125bd2656fe293f2f0a00c85009768c8c61324a40ed0487aaa6223b8e0bb8611fde277b6ed812f01975ba2935c6c92e58678fb26a0ed5f471cb70f996029755d9c67a40d3c1721db1c8f34b7fb8465f035322fa6a5ab5afb5834c86a60daa0388bbf7aac37f8cbbda862cbe2922ab6df613779129c1295a583549b5bc0117b30f54e9c84b0579bd898c607e17b4fa4f6627093d10b4ae585976ba4a7b0a19121a9b2723b1575060035e1f4cc6dd01aa33ca837b9f9aa191ae40300f310b990939cf2e94d4a4d833742b0a1743722fdfc01623aa47fcfc2914c4530c4e07ca9a794c6070148d0831300914e2213e83fefafd1d7976675065943422af6305d11a9ea9ff3a4e3702cb7893f7ffe131674a8b779fd01cf9a2a05399f6fad105f4ecb2b10ab39401c87b92a5b4a1d0e6aa9be69c2a14b6c4d8d8bbf638ff3804b52f8516adbf3a03ba1eb0a8f422933052a757519613d571c84316a976e6770c6caaca2a7b0c3f9c09e50e0ba6ebd203bd6ba97b494c7fa1d97cf3de11ec8d0bead26cb1ddec6b30ed8a7b0f882b24a79ceeadbb240d3e3991aafd0e1f91b90629b04de32a1a3c924ffdd6faa7139b6081708ff759e5e728fb10c3d104e3b20d3d0bcf2dcf7e787bb5f35d70eaffb63718c3a055a2d7f611b7c2030dc56599098cd1eda6249e962aae8ba8afc26f62c371c253788c2ff295b5517e5109e95c019f1c6db1a2b0fc99a3f456074df2f00782bcdb50ed6970db7575f7b0ba0eff0966399746bcc0df67c212b0d2b98be496770ced0924c2f825e84efb63c826060a15594ff5c852c388edcc6d1a57d8ba3a063effc350312ef83d9bb73b3689be0fedb94ad0e23186c1624cfa355b1e4d14c9118412818f279d538d5fffce766a02c516a0a33093e49e424c47ace1ae24e9136eb7df33c8dbb99a5d05493a58f90baaf2dd24b84e77785fd1c3df480af61814e432f02aad1d2e247c5bc5bd88e30a80be16192a8c4ceeb9fc008708ddeeba2750b83e0c0ba557de4c2be0089cc900465cee1c1a067cc30f6ca366ffa9e564663f9dda2e951d1e53c60475b7d0c60ada1256645f85b8dee690d68fd2458d8b1bb547badd4f9ac4aa38ac1dd81d1f0bd5fd0aac83179293b6bb4c34b00cd66a3347b431c454a7a50137847203463e0fa76cf854f7233284a1ff40bbe8909ae3aa0b9453e3278c4592165447d9cf4c0c806628c00247b2b0ac85b11e6957cd6fb6fd434b9d8b91603d182649a85a070a9c7b5a0b3a60cdb33cd05a9666865bd199da5df3387514aff3ae07ed5fb9d9033ee0bfbbb2ceb7d5486e4fd96b08b3575b34a0ed9d2c89b1e258b9e7b6e69108704226c39b28a318ecdc36397f3d371e8c2d74525129a70da00b33de1899d30db9e276613a3cf8be69fb0a41b180d8e74d7a6e2546e7424c1c3d63363b623d0956657f7e52fb158e30a98144023bb89dd5ce1b84ef106d2e20cebb51810ea80531a078cbdccb0c6e1743a8564cd6a9900d7c5d359de9e28a5be23464977614050bcf805d3e88b24bd3f64ca7c72a1cd27190cf809664639871d45edb24574a0bc246ac3be5adeb67bbd9f472f5ed31932451abf10715be27ae989f9a59f9f003d5c9fec55036402318339269d8f06e0650a90ea350f1646adf6f196362569b096449a78fee06fba436ba4ccbfa05d0da76c0a4ec54a02e662576425ac1c6fd06bf518d74cc63f35cf3fc70084ffcabbc0dc4167d6b2dc32ba6a3f62e6f852a0db275a9af0646f3e3a19964e0e7edef17e97a236b6eed1b04ef2db253ce255b094c493add2874300655a662c4ff4e25df2f0ebae4233673608dbe65bb8a7811073f434bc2464933d0db633bcf2d5173b75150fe5bc2408e67b4056ffc2c5c010040fc5e6a9a52a1502b2df8d6d1b71786d0e3cc5984ddffa790f97631cc1ff00ec0ea66a90cd2edd99a1094f2c0a06098492892c1554a17c67cb3102e7bc0a10a77776d76c5963a6b5dd217635472f541aa256b3c43428ccbc300f7ba7f65b20c4cab1724fce7ca174d2d282b6ff8a28e1d937078fe4ff836742a30304f853a073e4af03a8aee6ee611220d622e97e43630be9262a0e01054b7c3b904d7a94a06dad73bb518f37b9616d5500255fd3128ead4894fc47dbdb545bbf0f7d7ff360e1e7dc5c065b303d8cac3d0084bd1e18b21e80ee824d183c85309572c985e110bc2356fcec1e298a834dee052944ad548efb34fbffbabb21c10afb702c015110e86d4fe0f1ad90e11f6a6047b2e0cf0e1de1d163e18552efb22d0331969d6f30bac4a3e41e2fb2b16b0f5611e1814e1d56a4e21b90b2fdbca61ecd07fb4a4cc0ffa9dbab94a8a0b5c22869bcb22763fd3c83d8f2748d9e8c23dafdb5c84b0c602a2c2e28f6a09a5781d5e7d171469244dfca029143c83145773ab34a45de84301b88ddc1f3fd4f875b62b3036f82b146676c653e2b3fcb3ed913f237bba8c6604d713fe6723772f303d6798e19dea1e6a5c77dfeaffd61bd9b6c4588f6754d1092575a76f0bb83134bb2a4779037223e42ae2469ec475ac6874305a2e6915380dfb06f37d5e1cbb2afcf45315415b39f3dbce490f5911ce9e94ffa6c933415e03003a5fd8139bd62975f15998066c03af0301e5f913ea1e1175d68622fab77c0024dff8611d2710db3ebd22063c618cfdb7b9ad64a03f57a12217c91508cdd10095265cec80b678d822c0c486aac3b7af32e9a8cb8d4e2e721907e85d83997e08f355098593e8f5bff7b03e8fbfa4530b31d37bac30b84fb0dfbdd025abcfe90d907f108e5df676a32dea2e6290a720d15d44d28510687e4c704f65d1ddb04f0057dad555ac068ce38f2b357f67ba4aad8a9b491c628e92f2dd74f672a0fb460671960b1c0e27cfc57dc94f59606d3b76bb9aae7069aa6c87e35e1be4e13250052712bd4198433fff74ec887380db6a2d11a2e3a7e75181d63bb7707feca34d090ab27123fc704d8683c40bc17c5a1ff473b9f685b7c0ee443521d73e4bd0540aed3075c82067425f08cd9976f7e01ad7a7cdc7ebf639bd96a1b767a4c9e897096768729c2508dcdb6a3a3811287560c980b663773aef3d994571789970c7500651958f5f81ed909977ff982dc38bf6d844416fd584ad9a689800ac7349310c0d4d6d6fe83c41d7fd11456631ebb486ed97d9135c70533b540863536462cffd08aa0aa9a30db022feb1748d762fa5f9a86caafafc663558ebf6a223b4d5010f0c662672d6b8d8181be5981deaad2a7663203466e7973411daf6d76212e6d8530259627b2ed4d81d889d016ba578e6cc96d766259b920230b4eb4770391c62b00b472a1f64172def31224c4a2ce02aa003c9ddfb5777f4907b6311e712056e4d003d1d7f98494f8a03b39881e35da2762494b6c1aee2856a0d08cb4394641da008161cba313b815bbda2da12cdc08116df3ef32607610894b4e9d660368ed2d7034146c08fe1061e654d5cf27755ab40e0ef33952b1595074eefc8802fd08594080433b316c3e64e5a62a2889e90dac22bf2a5c2f1468b9cda44ef7aefd26ad503c2cc370a258ed1db075c5fbfc8492522bd2226ce291b076f4bff109546736e08347796a18f19dfed55900e68d72d57f9e96e0bf9e935a67baafd5cb6d7a79e0093cd1b7b7cc6afa51373a8845fde22829de2acd977951594f19bfd49be76ee0636e8dd5448e1ebaa76311a0ebadf06ba6457cf32fa5657c19c4ca1a753c7d00fb82afe5572d7095ccfbcf5b7ffc20eea7966f800e787f1892fc11b18c78df50500a3ef62325170eb956f5e10a999c97fb9e8004815e2433b4388cf5b80ab2e0adfc387f2318cd2d6760851e28b4a4f12372f0160499e6b1bfeb8a6cf9d0f7801239a094c6d3c02bb3ebf6def616d6b5a229ac5eed15961e95ef6bef8bd2d1e0a0ca6f80816f5ff61a2f273dce1340bbd472432b6001739a6c98ca14a37ab7c0094fec7f1ced15c7512ae4995dd9e8593659b17237eb2f3f51db6b2001b53cb0ade2c9a07e7607c3ee4d2a7dcee3dbc7a43a50732dd48578bdd8e6515ec53680ebcce80b9d2d92e9024199b0f561033fc03c7c38cf420993265ae26de0fb2440e563be9b9cf1869dee8331e7964e66c7a5c00924b4013a2e817ca4b90d0f25d0b9b0cc3d6497435af8d693ff44f9df0f4d09c66688502448e2919b11952fb280d5e494dea0fb424b435b1bd7d7ce18fda97be497b2ddc12370c7bfdccefdbb7024b6c38134814e67cf08684865a8502c0682a9ef5fc1f1e5fc505b52ac4ee9c0cfecfbd292412d7af236bc6495998e77c4585fbeb81859085170396fe2181ba08a5f8e984769342b1b5546c44aadd474acf934884161d3fa0e6728b2974c52f0062d1a64bc29206917eb8982110ba69a8787b8584c1d7954a761f27eb4aad9805e0850c9b7a793381602ba6fdcbafab4e4e0e0b1993141fb78b8629c0acf5c5004d382444b2b1e9690769dd3a69dbac80df84f61c54ccbcf3aa52e424e6d4c201d3bf629848e7e0bf9836733bb41d345407367b735de4f90243bf019314ae8500c9f3bb3165f7d4d45f14165e2bf17b832aaf935268d4d229643c1ea008bd440abe1893eda8367da3ce566acac37349e1bcb17706680ec83d48d78030ab153d0878392f224051f860f23059627a7ba507504266b0e56654cc38312c8a519fc4079579b7845d9565a106cef8c6761b12e1dd694e607a82f5e2e69a725366065b087d585e499fc8bc80d1f73d52917a593cbdf36c44a6393a26911b68e1d9527205dcbdd3c38dff8618adddd8f042124556d12b74746127cf1d30e27cf760235a05f0fa9de3a69a3186bb0c3431644a22b7b31048f7ddcbcb81615123c8010595049c25e7566c248e2738c445ea6fd74631611a2a0e8e0a7bfc8bf9a2b1cce27802b4834c950639958884f00f2c9c840dd16ca43f0dc4398f7c6b8952848ad684059d96be0fcd8bda54bc7d3606ea8cbb7462bc10f85fdfbfaa07e2c729105d1506a242624b09f0d2597c8523fd92c0e2090123fc9dc34462032606fd9483255b06469ab4118c4a68c541d2532b07b26159d23acd9528d6548e3cbba18713dabb099e957fb90c3e3378b5d3820076cf15fd95013220c1041e3d9890a3111998b50aae3e5d54c65a1cf4aa32f7069a5c703fb783181a37fee02a0f06d0ecefbda303d503c7c1ddeb91fd099d35ec2ee7a7d21d17aa32eff41360b88aad482ed4730f96df82d65f84ac7c4fe49c672766cb238338323324b70176586e5bbdcd1df50975cd3ea1f7f987485558f42328b169ad7285c1ba58c27bb280a36c104836ec07dc9c30654bd3337efe1bcb9daf0d93eaadedb9e33f98f3b83a301d1889d614081d939108cb4da32fad6413c7d7558709c649f71acc7be146d8ff45ab6fc970007de69a6d2f764e847a02b93bb3062f114c974d653e54d8a171a626b148bb0c05f5f4ea65ecc7b678738cf9ead88c2e10434561cea181685cce9409588125fe05c9132895625c321f4b91437636441a3ff761066ac6e90d813b26dd926774b10276748e1eac5027faa9b53c8df0b9494d3b580509f862bc57a93c7b2518aecd0740acba64239f220dc801db7c39f4b3c23736173440ef9e5a69537f9a76b9240feb6ae8a9d6944131358a1b74032c6f18e65b61b1ee6bc8e39452912df67fc406e315e665e1abfc399f1a4214e09131c7ae912c25acc0e61274bc60a3c038320ba6b3259c961b51a115a7f6399e226ae3e271c58967b28ee945082e0167fe4f062712df295a37bf2d87b767f202630100f796756c51e4ce92250b07a682915d0264f12fff7f363d10b65f29a552e62ae06705e4932d6bab34888fed319f2d9f00dfe4473d5dd6f273dacfb41803ceb9cfc3bd6c6324dcd11aaf7bee601fd7740bf79445d172b2676680dbc83329728c1b54a0bdc556d58cece32da66894a1380f8a68dde318b299e63741aeeb77e2118c2c22a52363da64c5160cf18d25cfeb0469dc94edb60846677de38ba35b025369b934885344a0137f70202f871a78dc0ec22450651dc0f3a6bd6a60f7b10949d1a89872a62dedac8bedcdfeb78b3ad605b7d7f3a00b1fe4aec72c77aee21ce0203a2d949c3c8849c1ecea6c7ef585e207d155fea16c50a95e35308bc4bbfb45504916b62c2d6de00a28bb0977332d54046e635f4f7836e1469a43eb13c57d7e4fe388ec4f1a98541574a497efa10a3c0ec39d8400c39272f43603178ce249f4429a7f734576f9884b964175bb387cb50cea9f821c8d09d8633fe1b17c6502c0ca12f6003e208648ec6f801d0766ce3f06cd13380f91aa35c39475d538fd731e5f94e7b5d8ab1fe33f59c580c19b7f9707a649f1d5e9af3fe41b1a82915ec48fac7dc07941039d61edf7f942eb79b509061bec4d9e2226c0a8acf19016869b14998bf2a56a331482c57e54978f4c91030a3d70b8a4f4aa29a6d0aa8ee1ba555b8e40188dd68eb028b09de94216842329023c6cd3beeb2c92eb8058f0197aa6b39ed23dabc9e72d89cf17d18ddccf3f79005a28d617c0237ad2c98c72e130070c1748484eda67aa913981e2bab3d020950f8908e557745bb88b74bcea5e019944539e349d047346cf584ad2b46ec4236d0e563c63a8763c12edecaf53a2e925e77d33d52401ee7d60222bec2ecfda3264086f7a311bb5e66337dbdbe499adab655c919293ca05b65ca948f4163206ecb80b1d7fd22bc04d4b5ac3999c8351b77aa940e8a1905851e363bb8c66aa082fb601ed537e6e2666a96fe6a8e1b1558ad8a12b8c74fae93f860623ccddf7410991033af7aa48d6774c41fa9c09d4631a314ff2715a7c2ffc6430c2d09cc2904c3e03570ac81f15eab833745b5a3b37a0be3d105d87d2ddeb74884070ebafdcb9c20b90032f7a48f5288153903773fca47c125bb20239e311d96b3344b87949f6740047f6aaf8104307728d76d7c0854b4e4f1ed266994477b590743bf86fb013b20959d58bfbee5c6bc087379bf7aab75ed26e06412551e35a3a9ce4f08c54a21109208d94ab5813dc1b76429df758b588d3427bb5bfbe264cf0afebc717c2e9ba08a4d94ac4e7eba71bc1c0a34cd4bae727b014b615efe88c0b5b8ee78391e82907ae5fb3698cb679b69c7788b18ca038d78fe2c8e36056f398c903c689d3c82f05d397a1aa5b9e64787d4350a705f5e87db9975caa86fb7343303670077b186c084656c5d3985fa05d093ffe6c33714faa0ae7f5ad22213b0fa6b5b68f06a1ca0b7d60fae64f541566d517c7ca353ce0d9aee4a2e29a29676863b1e8098e940308dd9014e461fddc7d342e89c74ea3ecfd1cf95a335353eb8ccf27ae289b2ada0deeb338738c876a6737c4b3e87b999411d848de29bdc84fb91a43ceb8591ad90bded8c08b559cc214ff93bf86a10a81dc1a29cf3bba4b2f4ccd925c3c62f6120d64ef1f0e021eed50aca6fec29821e8f15bffbdbd14f05e87b45c06b584467a00c46de425c3c677e98d5e54e215fd46968a00b3bcca254aafb7331b039041f304ad2655ff80076235fd4cf628033c814be2e9fa4e41a6b55b35d1e20e0713b703dfdec897a059156217d06ca151520b66dc43955cca3229e4afdc389bdf5c6204c1f23ac37dded7b01d200be666e3a428eee80289713bf15e3f5ca054acd8d60fd5e1b20eaa340a724d010bc678b2b6ac0f7f0988e787668f6338b67e154f5c0c5b472b8756a9afc48b10e8e0767bee82a07749843289efbe11f9f9b481bf3a02440b71dadc0c65ac413541b186249b6c9c9a69ef3c3d3f75ffd5af490f6f050cd580ad0eb093d6cf705da64c6bb8d9f8ac90d533d2197da155aa639e116e190ed6793798d7e32924d6158cfb786a4aa6309f9b3a25587b76c6c4091427a5b20ab871f7702f792b4c8376a36f5e05ae6b33b89b7e1f3d97faa1bf464f1a77b50ea40a40bfcabe43a360e3dd468f26b095cc1e3d1052c59109fd34a0391bca5704ddf7cf320781e78ba917c35f57743e1082858b7c86befd87b658ce3dc58fc00e43beaa9208a721e41fc28341407bbb036d42b4f3da9b8ac2fb171a1c684ba903d29971a56de8d4770a995b1e30add4a373de6931d61c18083fe7f8a7b514a00f0d5277fdc4845ca3f4f425a6997d309824aca4262d7809ad39d7f43cf7a3e409b68ee0318f7fa301b37b6753c29be8ae00f8db3c6edc38f8dbb91586e946fd0bd17505d43604d19285ca226dcce9338109132fa3bc440b42a44c3cbe36535a0a7ac9d5a6b5d2af4aa9ed941749352470481d27d0540d6b0ce6e70a6d6ce55908de83fec1e1652dddb6ec94da38379608e386362c0173f282e6c2e63b14fc19007c93072a737045953aeec43d170a4b9526db594d0e5c5da90db873e05513080d1cb9148a7c6659b68540fba963a468f7f99d80c0e68add34460796ea49d66a00d7490e1e3ec107f6ed7b443450ef118e95a4b1ba9da3184c08cadce40ef3fb0c5acf8e88ba02450a818a3de28260b098cef800177cac5ffe464bca1610e44a0d740da2e9bbb370eb76f89b7e53dc3b0b8d18683ec750e23715e8ce4427d8ae039b9cf5e126db4d787abe8778d3b60258254fa645099822d74af8a742762011063f39453b2a576829f2de5c1445d6800e3b7bed26a97278b2976539dd9aff8a06ded10c333455e18a70efd98fc53064ca74c7163254b772265fd363acb45ba7081e0341866c061b05896ce6b5b1958feac4cec4da90f4f880589726b3b6b360071d164bfec0978302177fdde69cb9eb45ff17aa9ec14501c6437e7661c311240a354936fc6e8ac22722bf2361db4236f6537be75ff7bd04b55045d6b3a6c0b80303007c746fea0d8c742a76074528ddf84616dcfeaf8e03dff89544031e098f06744c0299f3914e5179bb4118bbfc135df24db1e4e1a7853537a671631f72ef0aacb2f63bdc7b2749ff4be8b1f892743f731c3c5f3108b8412e0550a725f223017f31ee7904634da09376276aa223c394ef505975ac43aa449e9d9bb67e55e600467a7dcc94b5831bc55739479a62b6d7616c403bbc0b08492f13c8148b72bf0169319543408fb7418c98102e6a33d9c36b5f8b9af7cca94520178f49d34ef40f9f12d9103b2e69cf840066992df57c8bf59f45cd5c48ba4394d09afef4089606d4ad1d285058633898ff133c8c2276322ef9cd91f19e321aa25d74ae28bbe506128a9510b9180cf11ebbc05d6ddeaca9ea0bb4a5cbe3babca0c8e945c261970c64c48df8aef1ebdc9129860cca583b2747b932eaa4043b924ae93d2a0dd93b048bf8517207b815159351813c0f09b2dd6372ec8195ca49bcc23ad8942e04ee02da101e22eab3c33b3ee9f05c2ecc274b98d065366433a7677954de5f4fbcdb02af21a04e15f088d5336d268fe1bb81721f7b262225326cdccd66a20ec27b4e04b3a39921623a7bf04a1686afe243ea006a485390634670aeba84b0ec0044b80b54df80b994e38f3ab074f3ceab374832e9b8a9d130c996c3bf3a2a53c92cb304a9a47fe1a5db4ebfbf2962dff91a558915ec14eb532cb037cd5fc2605ef40a0cb2c960fd32d2eecb3c699d530a725dec22d774af1561502746a6a31333fddc068725c066ac57cafc3244a4a49f24712ae7eb1ccd8c0b008efabe44c580caf60973e1f7a0585e1577be773e285b31fa4b59c57846349691f37cc6459eb96c6402 false +check_ring_signature cfa51847271a8e38b99d77b2657f665b6b37d7ab1c76da2357e336edc529d72d 53129d938e3e1d0526fd4c2f4a6c94bd2646130f61187920a48ad2b6751876a6 174 3e10b6b9af2ed4a0f3f1801781fdcf673118f5ae607e02f53f73c8906903de10 05ae069d265c99ac0d262a0f7bd63be594938bd80245b2a5d5fcb06428cf10ef 60af393aacdef269d9a85e31e1c9ab28e59e64cbf827ec1dd71f283b77cbcbd0 1970587721ac8d355919ddfbc39128e14faafcb6c6046bb6ac43753c17986fd3 6e79941b4d4b5a96f70e3da722da97e8709238494e4fdf89e1c948178a6687a4 8e73fce95d11ddc9547e30aaeecf5114a215a5a32737bdcb0a83051b3661a2dd f5c3abdc9ce3e62f5fe682ff19a0b5fd6bb491ba71a43f5665ca904c74199bd1 bddf1ed2101b5b02e18ebd33bec621d6e219950cda7102482c3f685ee4708ef9 6e48d437b0a18c9713a33739901aea665ae1c76c006391fd8f329c9fb90f63c9 39c2dbb2a398b4222cc64e165a1924b01b4c5184312e2ebfd6429ef245c94ca6 984736c8cfa49c2da7dc3fa76bfeec6d6ae6ece0c1c603c366cd7ddd1b7f94f3 a224ee0de345d8a3aa504305980923735d6f6719d649d10edceef191cbeb8b8e 3f8315b1892430ba040d0b2d7671cf4f2c593261223c62fce415a4bfa23fd6ed 39eba091b0bb2ea3b44c65d13ba7888c2e382527d9ee8c2330416087b880a723 b297c5b4598b685ef227796c918675fa3008a1692051fa50b8328e64e2326208 cd7df41cd1474a1b26f9a6ec5f85fb2ca5c12e2a344d04381ad20875fcc4a0f0 f96c3556f2b95e698c24fe2ed1be9ca94705f4c004cef54786e473577decd260 e93025aea8ca4472e49b8097207c2b8171c4029d15321f0342bc58118e057807 8dc69b042b88e8530fe3e48f64ae09d6f3a6d21da59cfaac1d28aee85a7a1eee 0747f3f4484a81f16774c06df80e9809ba46d5080c47a17322215eb3a04b5cca 18a538d3a4ebaaa9019be2253d9c6e1910ce3560e5791858270e2f0f248b2adc 9d9074b5e4e56059d2578e174896a3745d961eef95c520c57830be8b4798840c 850d82c73055d74ad5ef403ce591d99457703363030f3a39a49efd32335f6635 b32df4a02d06c0abd29224449754a664c7581b53f0038883ee2c2e85d9944ee9 021446022c3e96583516b5558192f839ecff2abc54faa329868f6c1108268cb9 92a024d39626691b9497e8001edae6c551afa7820788951820223245a7ac6d7a efacb4818bb1cd8d4c4bd6bb4b7598b29eba6ee13bd852b03daedc416e97baee 408cdbaecac8c1c7d800dbbb8f407ae63db68d4855b5b04f3324f434a65412c7 c33649597973dd91edf67f722181dc5641e2e159ac4187d901d53ae067e4154d 740a51bbda0526c44964614115c038d879b13db84be058c86ef3ee7fd533d411 978bcb0cba28312c0503a1867f767ddd43e473292a430c50a66bf26c2886d33f 5ec6c624cbb493fd735f22e0dcfa5eae7afa49bdc2209a2ba6db552671f34eb9 97b0db7c7888c6332006add9971702b2340c38bb5d1253c632e03a0ffe8e362b e2c25b101a829c2fe6e69542b1ab63d9ce15fb5e474c24fe0f6101ee01b8d4af 3180b1096ef494a3f0a3ed84bfd7ed959a33c781b596873c4d38da4d90cecc46 f15d157c8d41e920737311dc1ea70ba24c72aa8353cc2efd2b6a699dbd94c344 2a1343bc927930db3e963f1ce9fa109936f2b55a96d53df45cce5078004ca7c1 004d614fd4fc371a9ad304f2dee2b3bf4c4c7409e0a3c4a4f7b1b865c8b07b0f 89d3074e05c6d4f27272cfcc5eaf259d7629a6d3ac7eae31178419e2a4d5006c 728051518bde2d22e91454da59fec21c60b511f8592ee9e958eee7dfaa55874f e6fb4e020cb0c576646630bf7bc99dbdfed43197bbd0d9b5d0041a5d7c4121be 97b7ee028330501abff94b9d31f4bed89e6bc860b7f3024c5e67c3e292fc70e3 119e1e6e20ddb71165ba59685a298584e5e0b0502386c54b2268492ae44ccca3 070bad5b0a70e7500edbe0f442e506120f058dd28e849f480c0b92e61fc581f1 a8f3608eb78268a5022b7d9929557f84c1c0aea236b5ba13f15037934d547ad6 a4df92ea698005b00ae48cecaecdc3b2af2a35bf4ad7750854988b76924cb80c e61da054696287fae08706cc31845144a87819e75c2c0e6a531e2d4ce68bdff3 40115e7f781286063a22690416547d0601a273ce005cac27d8b34bdee2ed888d dbf806d0594d64ebab8448d38b7e7e709592ef360cadafb3ba5e9f34e398eaa0 f047a3060b3c79ed754bb6e430bc19e823a134e9d44701095047e9f5ca27a185 4206f6bdd02a52e9025dbb8b01e29a460306d67dc9f6a601de15a1cf4e156725 39a6eebf8b17cdba8db4332f12075d0ea5d7bb591100bfd19d17f66ad4423322 45e1dbdfd5bf86a68a519ee2d6dc3513c58ea8d24b6090daa5c7f18f124860b1 73cbc5823ebe91fc917a518e346e259e9e064f4045438e799b19c44ed0d1afa4 f09ec5dfee4c14804274261cbd971aee47f5c55d667ee10a3b3045ec88c54b17 5598e9c5f33b21b4e2396be778d08f18bf0876625a51445dc07c254bd72f0c06 fb58335de2e8a2200b4e666e8500c4f633eacbc5ea5f00f4458b4fc30c9870ad a7eaea15361d264f9794c9cbab1d89979d7f5bdad2d9f6acf77c3c293482b60d c538d8869ee7cd12909d7104f58ce8901cfd3a960162386f4f14cc28ddbdb0cb 72f03434f3bcd1780093e06d4a119c29e718b2dee803d5ad67af99860c665a5d 2e05f66243f27febda51126e40c0bf3420d456c66de257584a528f750a8109c2 0cd942ace2fbb1d4d3ceb24f54fa4418ae9e29bb44c0525f0cac46f4614bb8e7 d92990f7f7d6961870aa8597131cfd118096aed29cd6b577106687376a613ba7 b02237e677048d136bd34364862545e8ca177cd433176bacfce6debb6c72f36c 215d16206ddfeeb6f198c40ddd382abad4fdb5dd372ea2062ec688ee9cb01014 2e7e604d9bc772bd82aec973f5e970816542cfab4fe5913c931c6b8b076f31e8 808fc0ef1fc938f5f3801e83e787fac2278e58dd6ed8bba7572c540496a64bce 8ae6b29996ea37a2507bc5635a5c217c1964e8a6bb1b7ed8f7afa04fe40d78a9 24514645835c05ab4ae163a15df8e250080a289dd98e9c2cb2212e83bb677a89 0fbdfe436be340d6a7fadf588a388cc173644625eb9fbbedf6e0fbb74fa920f1 43e6afca50cca97d82dc5cad5f50170060cfb4d45c3016b23d46a4b418963abf 0c5659ed7a85bb378d8ba72a7ec67a690e433db74175c5dae02c351af19b5097 4d0317840ecf44a82ddb78dbbdd08059e51522030dc1898f0c3b99f8b52a6dae 94060ae01bc56549156ea7d493e1c11a6249c8b5280c4497d0dbae05d7c7d65f c06005911661b5a6ddc410b7460dc2ccc27ca62deca8725034c310d83bcd3efe ed633dc8026fac5d18e37911deb720f474a38de398ade7d5e6805128db4ad1cf f6df50b32bc85f2d2ca78f322e9dbf7c0d5fe946ea75625170dd5b42db0d3ed9 b1ad95cb0124d478157311ce8999cfa78e0ee0c0e869014c5e21363b61e748a4 a891a0a29a51ec12a18751147955f8d6ce6b3dd046882258952831c8e306728a 94443e5b33666e284828f2fe898dbfeaba748a8f2a872732a4931b2dd1693649 86cf1332b090660e627889b2ad766c9783d3a1e9bb6d13251b24c56c65e34fb3 1e8048e62c8ec778cc0d5e5c8ff4d04aee50a2bb8e1bd5bb6d4550d0fd09676f 1634e6e2ad75e2c18831eb582ee67c44d0542128ae2af5b6191e5a75809c8ea5 412aa79eb52710f3aa63f21c2e5819258d0005b590beda2f810c3d2d3df7a58b 401c1f06c54e2d417ec34ab9f8892ccf8b0f7f6a3282d67c2e0a1c3a33fd9271 51bfb980d9c50acf920b1f805f31a938cf884140f18a7f89613c01e9fc772d5f 3e3db6923fd8127e9be16e49a9fde8b32ed8777be281f3b28f4069477251b5bc 447e7e630f109d9f0b7e475ebbea33d04a7e9cd3cd370159fdbe67de2b1f2790 d396a0299c761645a09d94a54d8cacbb33b194f70eee737415be2b14b93328d2 8479012b09547b8e5dd71545c7f41457154025a76ad70dd14fe87688b563794c 0b9a1a43430441ed05e1343b3664400060a094b0c25ea6ec95c3128ad3cfb81c ead0651f15cd2477e9460a355c7450381b7888bde0253831b6a943e7a2d85ec0 df2722f0803cbf595b6722f5beb2f7e355aac7c0940691d489abe89d9b9e29a4 b521cb9ee6d0e68528d095e4b23fe129fe2d9b2dac30081ccc601521f4e7eaee ce0ac943ba4ab4763aeab910903bfe37912cf287da3ed9d01290917592e536c0 da75fbe075414fa4881e1500f4e7563434726dc25b8fb8973e4de481e5f22e7e 4f86a4217e408ed1af3788e9e481563cf4845a9b190a8e9ee78cdcee79a1a993 492384a39dad649ec45b245abcf7515ee8160f0f086e89f913859629316a5fa4 ed541a88b406c3295112597bef39629e457d37da55cf29ab9a7dab3520c12392 37c5bb74d6ebeb3b130c650e79381bf402a1d3acdc5e4a32e05e850cacd979c1 cfdbc978424343ede469860e0ae8ba3fa68e92c58df571a0b8056e0f8bf8c40f d5945447c71e260f6e1e417a9d34395cef7746aeea03716264e8504f6c8e424f ac622de034ab7153245e89754025405d5a0a1f4a0ddcafcd2338b2b2983bcc9d 61fd8df0b33c9894c5d99f95be1f7a586e86bd228cd71cccaadd5da273409845 d2e5a752e19f17507b99471331f5e6107b8bc0f9b35e22c672c7333257cedd9f 9529b81ae5117e18d48cd99f2f139bf2f988828e528451043b4817e62bde26ef c48083d6a2cd0c3f2e51ee0fc37e728f04559e819c76a3938063c4280d719e85 6bec49a5bbd5f3efe9759f05b55739d5c7d51bb246dd72def28a9a997dfec807 1a2d24d69665eed2b7fb0ebb98ba7f9b6d528132cc89c2fa652edd69c78e9c3b a000814503187f316ae772b518b558e039db373297ae2815d4c8552390929163 aee881a8a73914d3c0c23d976b76b565cfd3d1affb0d402e7b4ca8c2106d7379 018d5ce278c1053435fc495726dfa2a80f663f60a4408e0c5e58478ef7b49e30 c7dcfe23d9f79cfa0a0dfb308735fdaf55503fa0a3bd39beb4e18633216cbf81 bf009c8e3f9db9a21cb28f778c3222f90af2e2e2065d96c5a58aaa732eb5a850 ad873e1c9d2aeb937d819a8c132799f806fdbc8694fdf1609aac031dd9845653 5eb596db7bef389a7ef8ed12a8a56a05a85e64e0e4bcc1690ec5fda69db6780b b6eb64c966944874eeff6962212c1c52076959643f6fe80e1165660eaf7544b3 5c9f2ca8c9a84dfc78f7cd36b9339de404fa90c0dcb79aaa4fa4121fddaa0d66 28378afe299b35b75fb8361385b61af8c79d1ed3d9ed18bc77876bfd70df85c1 7655f7d17974c3d932c59768833323020faebe90fac6318c9cca98e6711208d9 42ae06b6e4aecb2292633e656350199efb19f07e2a70128637d7eeeefca78b54 840c080aa57710b1bbd05228438bc22ae4b77c44333574f4e991a689a84d2dd0 b15546c7710d026cbd684b792fa85d64193bd367f8a9db7e44ee62cfca4a1cb5 fc77963df211cbb19bbe8595854253660b79c2d4d775a6ed7cbba7111bcaa75b a153c3e6bf6bb12824fd8115140490e11a5920c63369288ee8800e789baceee2 f8911fdcbf7b42cd36a0170e69688eb532ec87ea36d37d2999e3883846b8758b f345aaa32237971a3231c50b28f8a9e5a933905642436444734b0515c461bf35 b3995ac4c53229fe568e8bfa44f550d5e6a475a5fb7e8ef697b786de6a065958 4042f0b48c7fa5b416316b4ef679035b9c84a8bf486909cb00369ba5833e9f6a 89b0ae0db5a48103f7a29a3612f7c1fa17507a950407ea26651803efb95b2bf5 f9a6b67f2f690a14c02ae01a46dca6b1fae983ff55c0ab3a9db62afbc70a6f8c e86018e790cf0ff806bf9eb9c40bd323b0492206c38d82afef21b50d288f67ef efb3c855ede11e92d189abd5d18df56d3ef2d53bf88d0035897091aaaf467646 6cd01d80183966d6f24c88d635415ffd645f8a53a55b7ed286f766a136d514f7 f6440740a13f34825c30bfa5a185db668eb316ec0ca8de62abff0056f1d6dc16 bcb180e55fa2383138491c59edf4ecf0c594cfcf7ea40207fcd77e9f5c63fc83 4679b134c1da6fbd8766ca881adddc9c8ee34351d052db09954fff17579991bf c42c5bd13b67518af565f56b2e1ad721c8605ea82cfc4cd22d44402258dd0fc0 3c212461ffe146056f1c357f52e5f589ad3c8cf06806a57696be182bc7387842 3114ddc1d8069cbf07d5c31df3b4a86bd239879c038f3084fa2c7ea2d21edbac 71e6d47d0fe11fe5112ba8c82f70c3c28e2f64931189f5a2c6f596ea19f37f04 70d0d42e8bb51ba10974df6cc707771e8fb1e253821b5f6c787ac2783b03a024 73f42d75e3cc7c95a3070078f9b28cbf654adff1d6c2b770d440c0ac8dd14db2 37c868a4d94d04c353ab1607e240e84674eaaa2711b57f32d385991acee767db a1237c283a204caca57650eab885b0f83abb13a3431a76099265cf6d1bd980b3 c47fe24675ee151faf2e17803330803ddbf281d8a5273629d47de239cf638a1d b36bd6bf242e430587d66cdf708ba72817ab77efb47f232ce73d4886a337b28b 2f35a5ef3fcfdb3c8794a8d5383594d9af9d1549bf070d47a5fd15acbaa963c6 ecbdbab2cdbb56e5272243235323c52e249efc99e54cf7e8701c4136c922a51d 99c39758436ebfc26ff579b937074f29166577fe15602de094a5ec14152417b0 778a5fb15f03fe35e7582ff6c848d71ccd5375e0d4a17caccb1324e6ebead3fa db5f617b90c1865dc2f8cc9c0c525955a11c60101b48532e9c1c3d4bffa5c8d4 85c265062bddbabfae4ba13a68fce1ba98173100d6965b3629d0194b3768d762 196a7fbd060e9be0e52e11b92e12f399076bbdaf19fb588242dbf101e832ac41 46aa6bd46235881999628791e2919e9f6ae22515c6134013b5c5c9b1eaa2df47 6441baab9d88957fda02eefeae745375dba904842e85052fa5d9b8bede87dd44 433d05f23f423906882e058d7d39f2d4d87f54a71db382a8bea1c0e57a8e7aee e9067d05847c5351baa8b08e00f1a0293759a1eab038460e448152f58fbb3630 7787562ab8f285e8dafaa7370503b5a4dfe7de47a9a8031468f1914581dafce1 d8e3e3e28a03d28245dc362df7e790aefdc38439d3dbdb81070d29485641db52 7412e3d6120598f95278f80f1b70962c9368828bb1dfd8c52db21bcc63ec4af8 59a899c8a4f6917336ac434c3bb401bf7ff92b4e68449c3dc43650699f3ff601 dde6387719f7a194824551d75a62178d469a904b394ae45adbc883e8258c4e0c eccfd49e994d6042c57adfe77b09e11397741fb877aa62dbc37a962904917fa1 342bf3acbe354f8b3bad8899bb3770224a75c4a4477ae6113558855286a0cf0b 017ed2b0d8b7331b353a0d77c4739d0b84a33acfce5a6e37290179fc986b518f 764464da599c4da76eb4ef6828380762021324e27e858b16f238acf4390a2a24 12b19d4c985bfb1f1f7143758d5493af3c1713eeec9da11d77b1826ad8cacb6e b178e7c534015883ba903ed18b7bc81beb41f3533c2c44db01860dfc5c86480e c2f2af11abd6b73cd660b55d4681b65a4f6dbb7785c0dfa72a8b050b1bf65e1e a9a91a4a12236b881ccc492bc5b52249057a27cbd860bf31bf6a914a9d4946b8 79447cce93116a3c87e19a3f458160d9e5124ca742cb8eddeb7f838ec548f04e 3ac0b705ade3bded24728c348f02b3c0c7e15f8eadc64c8a70257f39005bea4c b120d98b69a8056dabac58201bdbfed67b86eaf30e51ac8c2f6f730aac970b8d ceda6b13bc628115652a533fcac359005aa32b41f98b39cabc859a93c957d006dd06bfa3841fb0b635d1645bc99d601d55d81ff5b90d03ad416c43cdd31f7d0ca3fe52e847431ea7adf2ef4023a81e76b7a25a4d870e5452b8ffd0bbbc9f3c02dc7ad0cc7c49123a36b0ba35ba054180121118622274f7f06f266f16dacd3c00b9286837c37153393ce3079b0aa2d3ab475f543b7aca043a4426de392f13840033cd14634b2637b90488659d0986975415e419c825e3c45add10033633fb79043208fa5500a5885dd2c1e42bcc75e89f2910771f614961d2eb49ed7a765bbd09216489ebc5ce7519e4b089d3384b2e204add7fd19b95a11c0541441744329a0cbcfb6a9623de60ae5c6297ce929bb598006066ad22f697ba685ba0a98b10be0e4fe94824bc5df1d0c110f6fe84eb8bbeb093a360f94ed018e12a3ab8c153920f8c0f0dd7c2e0e554a717ae60815e5c26d6f78161f59f42131d97c87337a85108046a82431737cfdac714ed0fe09fc1958c6b60b47664616e762ed089fdfbfe053753582bbff0aae1eba888098d5f74d467dbd534695b3c1a1bdadac8b8f3710d606b0c00f394819c668d920c88f133c668ff81af19d98ba327655aaf1681d10d31da03c7a70d9c2172afc1cc3da1fd7c8170d950b637126ba780317c07cce504180f369c8b6dc44e2816aaec6215d95d40f42c257c2b84be46c7f418eb50bf0c6bda985c8ea1d817dc352d8acc84bbf5fb14a8a5737cf4e57f36f3c2c8fc850fe76eab2e847097790e1065219884f8bce46eb161042fc4af82d6a9735dea8e02e943d7fe115b431fe922f7f90bbc848cc920cd481b0fb9068f4f06786f352907d2a94286812f2462000a1897266ba38730ccbebe9c07d0bdfd5971660b4a390de26664c23c00da0495f52c4b8c8fd61222690f132c64fe121163c8d9e14609029c60f348a4f387c00a5e1cd84a26056fd318898ec6d55fee07cace52e8b7190e98d28fc354e27f4115e3303b488a92a27cad192cde29d8b369557242af35730e2993e10bbd813d4bdbe3bfd76bd1af35533342aac201bf43b0eef29e793ee9093979fa9ac4dbac2bd28528a9addba8cc77d1a9aec4c699479b1917c4d3140d091ffdbd709a11bbbacf366bc1bb5e0bccbca3bd028f98bbf9ea7dc9a1eec9620e4b9651a21ad93f1bea1a467291a543525e2563287ee4f24faad0489c25fa020d722e4829e72432e0b70e6d7a6e8e1257079300fa34cd16be96adb70f971dd70582685d25411717ce7de5e7c8a247c382afa9cdc249f30a483add9b8d5216490be1979e123da2411d64beb9a13c60898d3b8dc32acd7fb4e0658370d6f379f3011d709546925d0889a1f0751ffcc66832e7b6a7c4fb5ec83f8f5bedebdf8a560ae7b6a5b3b252183053ede1b0364a94de3e038bfad472dcc1fb31898065290804cda054ea82ab5df0cf9ff0d6a7e554bdad9ad782dcb8f663a3f47957d5fcb30b5326ec555be990a2bccc1e3f706ef5ccd6307410654978af25e2e16339083208bf2bfec9c21f279573f9574c4eb8988993fb41094f89b054960af7bb8975dc06ea0d2424bf9f93d1b5ffb47b54a1ba12b7cac3891d335fb6084f5947792e330bdff1c19358ec43a627802dcec9019d22e6c863dda8ebbd07ce3be0fb68ae380d7d8b3e8ea96185f3fb20f89ff1dddfd77904b887f475552b038ada857fcd640f1f2777b4bc2fa94d6b0bddaf4eef1d7adf40331c2b97e71863cd2d2f6cc4d4087034b441afd91b25ac4f7fb3449fc3554c7976dd9c64c1735995065768b1740869b54e7a3932fd14a1723a8d97e11929d0dd9fd560c4c01a11596fda591dca0f5f801a5076e788799b79eece201f84ce0b6b0e57ed2bb1be494ce412af438607106990e50033c9cab83cd38ab7a6303f1a1064fc500a0fa9b454ecaed04580074ea2d985ad7a6ce5d158dddaa344570abad7f86dda1a915cc752a2087962fa029b142bf2d8b8579f9f6d17499d2407304c2a37bf969c66d9f7167b2651004b077e56d858e7495e868caed46e856f02f6de5f528f189accf2fb194b2930f58906cc06fdae4175180531dc2d772632bc7a608a03adf0ab6d5fc300dfe350463c02cc634ec4601484f5cbdf18bc04b015d691c57b0b1749b518b34e8d6e80f00c09e6a11c273df0a9164f916a27417075bdcd333ff086e9748b3fde5ccf1716b60f9de232060fcfd437b6edf461c34a82cd9b05954ee487ee7d1efe871004e4d20722796184f4707cbd44e45a3ad949e8557dc9726aaa7c4342960720c0a8726607ddf2add3cb4ed23dba4d44dc459bf86843259fe8cf0b4da42a492f572bfbe70d88fcde16dae00ed0074d1bb4c58bb87c8f85f8d764e809afac81c57eced1d407bd5c4b8fe05f9c5a84998ab1f80529272b8f847661adc25c7ed5ce4de99f720f3be07364af263363a5ff2bd29d4a5fa45978b3aec6441c3f959472f68e05410aa861108037619306c867133a8731b8a1a73dfd2411a7e065887baf71fdbcb205d3c36a01d8a844697617c84361e32b6274084e1ee5e58fc53620e4c4b4789b027e337aad18ee66debdc7a7f0f7f329e981f79e4e51be23e46070b8474756cb0ab35668c3a1e7f3efc9f997c0ed8355a475e2b64edb76c55fe4f59f02a0238807c9792c16da24395f190324c6d58b16b6d227c7e725b8d590541d754f2e9b9a0606fa5a460ea3afd34ce9da54765e9a76bad7e138c9c60eeef937fcf3cb9e8f0525a04f7d669965f786337904b1e87b753c72d13c7013ff285ac7ca73f1d7bd05808f3c5964c275f5dac01b4b2b8c77ad2afc9919b5fbfd7cfec91ff332b238090d5b529106e46bc4acb6f46ceba3984b30a5ebb5b3a5aaa148554f7295d70f0dc826e53297c0eaf09b768dbc19cd76091dcfdef8204a3b0bf3bb3f7fb2f13802726de74ea4c964e2725301d80f185920551037bafa273184aba58dc644a2fe005c08518a10569f9f8e6d1e4298e747b2590636cde38392045499910b9289db085b1073a0cc49eb5cf9ab42b35bfed3bafc928c86f2f4a323e61a36b65933af0e4521ff33f7d7a78db374f368fe2dfcac6d395d9fb5320dbf5640eab2d72660072b8b95c21ee6b4a2d3fa984f204b058768026cd7409c1ba6a237d13c65eea0007ed10c0e0d1fb513040d7813cbbfaccaf43083676c9df5055186c4a50fa7440c59086961670804cf149c9822b2892e2650aa516f5355c9669a5d151dc5d131045776b883d0f8c624c273af07de2854d798eeae862aac457b670b1baf436741001c4a866aab7efb049585cbb5346992b057aa4d120c47e077216ff40001ec2b025219e341996f74a6e1ec4ae9cac71735de35020b92b39603d70465cc6b9bfe0c81fbfd9b88fabafc290a8a0b71c788fd716ec328fcd623965a9c17b03ff2660d29db1a663f297823ca70c6309222c2bde72dbd012982acab85652fa8ae9f250adead960276fdf9a36cefe2f82a56949bfdc1b39c748038ae4d4a9b13e17c2c05f32b48aa94b3762b7e7538315701356a044c8c24877f31bac577f470decca2044bdd8a471e889e2933f28538db4034bcbed15a9e863b6305f5649bad69e541086c1dad54f3106fc9e83acaef821f6dc446b4fdce27ed69c8d8b6c8a8c9eb0509c74c035bae0607b36412ea28591673f8d4eee104ee0b031df858e87f4e9c96009178c607399285f0089b5ecdefd1e67d1717ab483e5e9851fbb86dbb754be1007bac2891c9073e22c12b25b6004fd0936515455804de914d91b98e65678bdf08a5a39f4be8723c27a8071353af54c1c1ece21d1855358cc24b3ed49b9cdb470e0212d96f507ed6131cae4b94d995a46b4783862d6f4bb07853c39c252706f20d4ba19cb02df788dec6c35c73bc3774f6558684329b9f98b1563bb128cbaebc0b4d4d2e164e2926d6fb21a27bf6db0e7a6bd42336f7348100218dfc4fa56f5702faed59b57dc26bc39cc92cbbb42497d9efe06765a0609e90411fa34b67cc20011e9125ae79296c5ebe1fbf79cd85cce3f28ad0e60581294dc9af9d95e0db0b0125a1bf2af47477eb18eaca3b49db4904c4745b7d8643f1f212442341ba33970a7da4d8a80aa434c10769197dcf8a1a15effc3f77512db90b676d8152cdd1240da56f6fd36bd59cc48ca30f5329f7ff38cc3ec10ec3f47e399bb91be3d0d414053585940ec0d60c33daf7dd67a4d320a95756d11c1009efc419222e2a9a4fb20b65d19872ac03012d0c66e351c967c8e01ee9a81e0318c6ea9537e150e289cf034f2b598f4f5fca205279e37f57738ddabee792303d7f74d7bef5214b6c867e03cb9e6d2d923f0278b8c04396a6ff8db7b6cc05a03aa9a0db939d6b5c25910f066fa7560de1aa1965b8da052580f065b8e4be53a7bddbb7de89e115f7712e0807dc99675747be2967bb993e3293945803cbf9cc9d26d2c7ff1336852645145c0fb2488da34f08bbd7a2260b6772be04c97f8438b3f6375f528e18bd6dfc4b0704179c544354e193fb2e4cd11a9000a028b9293544c13b4c03262ae01fbb85a40d9592b69dec668489971f38e55495b52717129ec3b50bf38f8876fa398c6b210de72944657e4c3567c46f9ea17c1b3603ad2ab13e25d3c8422655ab52668073054be0b78280a3d6027ccc20d4f886110fbff22be072f52e145043b2737a627904a68765f2689a4815695dc595c4babee98f080fc77dcc65eb1ca64aeeae91b5062358d9a5e821bc51f9ec747d3c7a8d4d5b4303490aa839c7b00ebe752f7cb30ce62c3d0ad809ecb414cd2ce53f60505fa2f49b869d67edfef339991722d3c5021d7dc4255d13ecb1c1bef4a10af7e46355960827feec910f405612e5b6af03066ef0eb1176678540cc8da47db7adb29b2473acd89e42ddfcf59932dc80e99d0dc911c6abc186e951f50436bca4dac7ea9c23cfb2b063ad1ba29fea97b12eed0aa28710bdfdfbef354e11b1c347a52eb9621d9f2bf530d7cf5b8de31cf108b9033601758709491ee348cb9c93e9e5a1ab9bc818ed496378ce9a07efa6b11b20051a5d4fb04a46fbd0bfaf5c95a58ad941d2f446e1f633ae5698eb0f4a26ea0a044ddd544669f15c4f185331aae67d7d5dee53a5df1d53ef15400a92be50a1c409ab8ac82576b989d34c6ba078e83b99d5d11eb7f0d3e5214f5308e265242c7f0a7bac9feede34e90c71698fcd295d20a9939490d5aefeb75bb382b631e591920b2460c9b0eba3f7f9a2d6472e780fdad046dd894e433d3c82e10fcf1a6c88b602061ac7f0cd060a291d8bfcadfc82bdbd153b6991c4f4098f96127f8606807d065c07ffac7dfb76b474e554bba690e984a4419de4d3f44c9f5b017ef2b59f870bc0ff442caf8fb2e5e6ce48aa685d96a9e9a461e44f568c095c1fae36bf53c8054d798bb3983275bbd418996eb671ceffabe674b6ec56fb70b7e7622383f8010454d220ba51cd236b6d0150aa18b50e28ff57e7a1fb31ca5aadd792487d100c0912852956b6b6253c9c67905de939e5528802c876992f378e92b8d8291f213d051e6f08c10c921386f26c0a7287a47a39dc65556b7be2be0545f410df4733e307a0a54c6fdcd45375ad707debbfb31521e8567d538bb25f98fe4181c67a8784099cae9fc51bf75d71891635edf647438fc59f90c5a76af9b291c354c870b6e201fe57cdd34a0db161546569ff7f50caab4bf074ff4fd6ff719c111d6f663bc40152d4fbfc3b92eb33e33d5d76eee1f8fbbebd83d43f597d60b56dc4f531811103c116ff376a35986b0c095f1828b13189366bb502a8c055b5debf5175e178c5052760472e608dd68148634ed687d99c1069527b029e0f8df1999229901b3d0e093ad4aed76cec312218511c50e4c53972e98cea6466ddacb176f39205056bcf098252736dcd4aca4362253bdbbf8845850a16df7679bc01ccf30ad07a4b80b4059ca84f3b6d138eb910d450580dabc9054781aa9e71eb59fc1491d7d45ebd720fc4ed4931cfeacbbc7c9de165db8c0eec098a96c9f2bbe3bc3d3e3ee18519c70202ae23846e21c46170f630ff7edfa10efcb91fdaf9f4721526836910b621590e5dadeb1fc9270ae59004b6739a8f155900320d92c3d2e27a604a4907149cee02004706397fefe0dd806bcb21484942289bc4264caff509cd2ec16d4f5e59940c9d37c46f21de8abbf23fe5b36681392450f1ce63d6e2fe176679d7a9f6180e050739b7ab5e3caea476c19d8e0eaeea7f06248e0d8098d39570caba3e795d6a069d86538e676683c669b6a24b7ae5b5acec9be392a58a5d14f7268dd6899db50524124373ad51ccc80e7ca01bd0a579d103f7c4f7fba859c49738fed0bdedf00552f53f14436545237a105bc14c64267e132bc6b5675ea4ad188e85b6513cd1062bc198a93355b635f7c427919e9ca6e1672fb762a65edc4ff3ad534ba3ca3e024c57a0116c4578debd637578157f3631643c1dfb1e77582d3b717b921bdd8b08c642571c9c9df02222ab9bafdf57903957fcc1030d51f14e3b47b316e011360750add868bc592e1adc244f4ff545bda55b9a657a944b5676f2e53a5f60650801d29e10d5dd18690adc8de6727326683d9f158604abd82e0fcf3df3ea927d1607aca330ab0563818d858a4744bfa868f50d927de79d1429294e600781c3acef065ae1ab1d55e74d9adb76d9ba27068d918fbd2626ddad4d1af80ffe4aa1455a0a80aec76056a8dd85ceb41c71ac3b87083e2f7e8816e221504b9e770b41e812017f4c31fd7f2e68cc28801ce0b9fc6633ea371e34a8de54d9650d09cf8a60b702011aa4909652853acf450213e3b043c9ab2ce6609c486158be1fb62e774a8b0daa4aaa8283035e8757f2d7e47a0f92a6d8bb9affe812b31a3f385f14dec42e076e303cca5955f4e6e7bba30f501cf56e5174439b41d2b7bdc0a7cc1ecea6610bd715bb04eba388fca5839774e72ff58133b4157dcff05f07115cf9566daa9d05ca673e4a537463c84df7e49a99e8116022836e378ba3ec590ce6287eb1041a0e031dfc60ab481945f2c483c25fd7bd71bd3b4006b5f45d9d2ca15c64289ca206862a472a6feb818b17c41d0f60e2d386c021a71c057c8e663e06d508f6cb07014f76e0fc3766109e1a672edb3e23fac4d65c9541408ce3edb79cf9bf0c543e0b50f0b4557ec681bd056b8871f1e18fe53dd886e4161d481b9e3437f649f7a5009077f8507ee1e8ae581430c53f7b2083d60f2d46fa1ef1ebb698112a9f36f60d877b213f954ff39f8b7d0db4f0b1336a0cb2d63150b594f301b55afaee655d097fa45d63e4d020eca6dc95f01fd05e431c59fd7afb090100347dcffff0e1f7060e789ffe1a2b9dfbcd91d7eb0750e7f638c96adac02431a21b47d7f096e5560a85f1168cf557d43a84da3bbb8d113f014316310b8aaa63487e2d02f78dfa7d0ba741df9a5f85eddcb91517758d2d7dd6945e4de42ce02861c460772183daa505f95c29188fc1976815061d35b832ffa17396f1524f4e6bd807f99e9c9834100027b7ff64921b76075fa6913873c70fdaa855409a7ab8a1451b63ed490303c201400584f0ce2d3eae0c871dcaa9e9697adf683695e129543fb844bcc2992e0f0a5bfa7d73b5421c5d782d669f7e42778b6f6e151513963ec80278862422f4c24f13a3583e0b2a55879feca72354dcc323061492596ee95993a394bb1cb59d8d0b428ef09511d47ea31d5a5dc01597746a40cf1ec34ea884b4a70fca9d5ba1280101f5374767a27fd414c151142db00d86083b384ad4a7bda0f40be16554e641016d178253245e2c5de740782ead07af071185e08a50962fd8839bde31a323810b747e220b21fbc16c1bf7ae5d74c8de3e5e56a49c7d7f5a4d06c539dac3bc3306b545d0a813c840c726321fb421dd8115f984dd4d00ee43516ba60f6d02552708f9709d3cbeb01000cbd08ab6978ca5cfd932f1d8f007ffd56a9c4ee9d1bdcb024e0b0ba406de8250165ad9f4d372010d9e656056f1ea5fa7899941f48ebf9d06a919d1f37d181b7ab1824ec0bd89d463ef410cbc1f50e57fab609b5d8c694c0a19010274130d8dff5ff3370fe96d9a7469131e4d01dabfd283a97a79b789fd0f31dd45193753b6f239272b2f856510c6b6cf73143363422577266233f43fe20436d03ca560c07ab7a25d907434455220653561c68e7aa93a0dc7254a3cb6380d8aa1488a21edfe1c94169a5a5352f95011840400e0e8abf3e5c5c70fc12992026924c5e101a964eeeef39bd2396d64d54c942f9f930524530d380d51e460fa02ed715f22eaa35183c96508c5d0ba49728f13406ea61254f8e179d3190701ac043d887e13f6baf3cbe738a29920f3047e8de52b0456b998ba440d340e4d45940f65c6196f7f298789a18b438bb31b16202d311a897c25237f5384473879257b0c4546b4e7d8f47e8f4194dd661bcc7c7d66a8f13d3e3fab6b4ba349de1f6bfb0fa3e74a6fc84ed25008d1ab96ec09c7ffa05a61dda39ebd3803bb056949780504b1eefad57ab52a0c63dc0a92999cb113e652e2759539becf8ffd3f9dac54830a5197436849f51ba36c8bc9b397f0b63538052646c18049d8e2f080c7c5792403c4e07132e3f37fb19f83f64d230da985631e81115005fcd7ea96faf4fa6a87079d5f202da09e0198398847caf97876191cb44274fc5aa98d8bb9c21c281c93052eaad805573ccef5788957201dd4f0479b0ccfd645b647952a2aef9b63ce8b091f8dcadf86f64049dfb32dddcb39045f88a7ca27af6669821c8c3c435ace0f0a837aabcd69d0abefe03a4b78bbda3f45432d4a997569f94785631298e3caaf0fa8d8275c59e4413ca58f8424e0291acb7e895cc0aa55d320eb3f4e15019b690aec54be6d86d421c8e07d2c247c545c2e6cda73886b04ad87a8b7bb6cd1ebfc033582c6b15d976f4db458d938adf8d0908f8c301d01077eba279af88ea8296a003bae3398368c6d62d64c7efe5fa5d8c78f15360dde09b75c471fed3ba6a7980ef1c6bf651a3a510212b11c75a525b919828862c8e06d9a485494e012bc13f6dba2df4c837ee13fe36b3445271ead6eac8fccf9d5cdf5faeaeb48b78353a11604b200eb4e0bb3d8f79eba294d289087eb56e2fc499bafebb4ac0513fa7f212a04c604775133aa58067b8ffa16209b7280923254231060e13efc7eb5c654fae6061f0d14b7583e264447c12f6333700c45b9afc78f5a51863210de108cd44a7901f79bad4610ec551e45779e436d5c4d6bafcd8c159df5bcd868f1d63f4af0d208b94551bf8a024ff6d9f4c2f2d1f1da47199638f5179a4f1ebe684776b06a240ab9408cbd2b3c1e9d1c5127ab275120ab711cc2d9946fe800d4ad1f6a03ace506b6ee94481db8600a8c1e58f8a64fc91ab37b23f11c73542eb70f53f5ad693a0c1ddd148f9d225bd47f5a99bd274c792b344bdea25c2bfec08572bb5951b13205f6e9496b775046ddad5bfd6c865e27a0263b36abf327fe647fae36c406a14f05f2d519bbca70998e8c3f0e3702ca0b0d67e0e49a86ab7d16121399935b4e390edb63e695c9101fd07739f5108f9efc7511f568bbba9927826b12fd8b3f9f9e046d2b917a9a54c8da945a8a95ec478414f2ad075cd97fe3e221083ed30855560cc37e47c3881dafdf2f862be4b47fad5cc86da45a471f6b4bb23c1747ce12380a52436eceaee8daa26c9e8a0dd09f1d22cd0b2c5037acc083867ccdc7c493b9095dbeaf2bf15817494fa521077d03761388f9fee55f867919d84be96ed2d5fe09f6247879563f4475d9a1427eed7529dd63545f0e0f5dd353718aef04536552044bceade3f76f598e3f8223dd5dfaf4b702a6f40f79908aecd718e782d813b90d6ddd7becacc96b682e0b6a133b2b6fb075948500cc35f032c1e8f0a402b34a040727edba37efb002d4f17eb6b8f26b838afad7a1af8980b76f4ad269784a340aea705ba4b096e6d9f12a894979e0ad57b3486dd5ddbfe851d9f19b3035b663066059458bebe7f8fcc08eff4b6a5efa724028fb5936a579f2b431f828d1805805ae7ff7dde607ef655a675fe7f4bdc67376d24d107f5d755bba053f49d2220c0a139f5dde416150014ac59ab7eab2abf6579e0cd4fd4c8d470a7d2d12dc5c6005e9d6389b7ea16c57dd02bafeaf740515b53e60e0a2dc7032a8a72977532f9503390a0c42599aaeb7890a7f09270e8908b80008b00ae100a5915e0f1a8d075f09221afc70f2adb95a352828e2776fe19f775afc9e1c1f7e22789d806d58536905a64421c47f6d245651060e3e0c2abed8ce51deb6451bbd81444e19aecf70a908c9b58e19593d5680bbe2656f9b28b183f88037038977187cd5ed831a10929e052050aa46a0f7930c7f63de487ed8a773b1d7c53c133e4d868c95afa4ec955f00ebf65b57509cd9fd2d8d8f8c295b94e729ed9775d8669050ff22584ce2534f0a5437c08f13486771ee8ff9f3bf2dffc5666cffa6922c95f6e2a4ca6943725101602cbc7e0bd4d61c7a56d58a1fff6c791ef4b01bc354beb5fe6272eb6631020fb2590cd40c5bd77399a49d4ffc4679074bcbf288a2a4b57cdbe02a05fdaab30b6f2eccdb1c7c80c007110fa60515fa20d8f798c5a5256c15d2a60fead47be50de1808e05e32a29e9987d954369ab215aa4c6f569d3842b6704ca3893420fcb0b00c32c065a9a6abf266279ad8c658bc02cb4f45ca8e76214a8afe5036aa920016735e0866a9a791aaf25be1914b701a5936e74e61bd8e7f7540f7ac3fd60b5078f90f2b68f3e40511f692c49fc13f7ffed9a1b1bed23656ecfa448385c127d032a08e402ddf5775ad56a658eb9891c5e4fa2c4f55c7ea2a6e8ad4867b55b430fc5a9e7c773d966384eb7fa3796fdaa9bc7c3442848d5936593e9fbcee376070f5789911dcad234204b4982aae1012ee23bf0e6e4eabe28fd0882347ae8344206795b692c3452646d54f596bb5f2c5e348ae267a1c826ab2dc31e2fe633c8950006c21a651f32c09184477bbcb7c77576d56081684d1c218cb960a384a327050a576e94f7fc5b77e923e17a19f440e0497eed78a7c2541d058b4544d331e81a0303f4beedb4fb68bf95acea92a346ba18b4b4b76fc82ee0c05244312f9da48d04b88c5c5a13cf5c322580a5d173bdf60cb5dca6993703f4182de9810cc41d6e0402be4a64ce6e61b254fae9cdea70f869177ed7d974ad00c3b02f932ed3e72c09211250815a2fcfc16882466f6faf8653f22f4ac07e4f3b8d78b30f6b16c28009973117170464b0257b0bf89897062e95092284310a744e2c0599556b5afc3a0c679d05128836ab7453da8b7d4a32782045ae6f64e19936fd3153a19192d40c0bc5d0d1c821d1d99ae1335747ceea452926ad9cadd00483dbce019483de7d32065c7956b4fb6f4e1d5da2368d32699ad63db9405143aa3d78ccc5f2e0fa64d0029885865198211dddd966510758c8347412f8394fdfa257f0df875920450daf06b4716e40accecb36b313aa63220e9de6e7eea7a901c4c4fffad036c49d444d0926501216bbac37f91967c3e03405881abbe5251eafc25356d7cef8e88dc9f90fc85b11087c9dcef37fa4e2c4362ecf538c3df69acdcb0a7a20fc0888124d860db63cc8d57a81f4a0ec90db5acf92a722e37109c634e66c15e304284d29d2aa0e97953996f9186d72241977b7852261823b77a2d52d65953c615199e8507da5046794ea871b0e999a7d80c537c6568fed80fdc0de6b780427a79ecc10a7d0420654396244d9c3ab2877829ad1341fe60b3add852a7709b42b8547ba081dc66f08918e9b1018332e389ab39d9b6ba8cb814516e2d92f7389506f275fead4e1f20143024b96162519f692068f58d4683b8ae81566065c6f64e62daebd75e6f1f7093bce0d9bf3ed819d78d2e6c4174ebba0b0772235a6fb5990d60d1e3c6eb72a0d4761be3f90f760e6da10563c564cdae97dc0c38cea07ff254b69019d28c0fa0debff5c89743b0da25b65399c1733f4c7fcea8a8bea5bea3070747674eb174705da29ff4dc2c5eb1e3a61f060205c44fc44cb279c6346a9ea80b91c2dd867f102405b1ac257c071685298e7d17bd0bc1871d6720540db3f64e32d1e6bcf4ca6079d23dfcd8106e614be5f7f5693dab61b4025e6e16ae3c38dadaf04e81c4f240ed1a89772b063acc45818fb470e2274e8f281c135eaafc3be8390d40534ff8f03d0d8ff280e53e8708736193894ba7292562417a7bfbcf723e41cb74df15a92034d9f358e1437332443e302aa4a38cd10bc73be805628b011bcf7b373c2128907f8ad45dd671769647bdf7b1f22b0016e0a4e71deece4b5430988aa01b6a82d014c568da48747a560219ce41bc378a23b9b072d0aba85aa65d888c5aa2350060f576cabc3d657ee138f754af80fdd40147692faafe4facdd6a82b6e8be157350b886009ef8021bf2c56d4676b1b6c71c15368ec25de7b23afa3a52e98930bbd0343772fcba8efe418dae1d4cf265c9a2f89e3a94ff460e74139d33bd8c9df8c05a81d351e8763b7c1e2c164795618a407ececcc3821ad27fbc9d8db87b4fbf4032fc45daa7a04586d3098cc84bbe4526ef727e96278921268dafd535b2c584e04d18ac642d442cdd882d4e260a9deba9b842cf76752e5ef5b44c7799c151762079312077f9c26f6499ac74b8b271cf08b644ffde3c804ac08c8dbd99ba25203070b332f30e63a3a0a92ff94a77e3b1a72870275e405855da86e016d9c4c5a030f7b933f3517840886d4f31330264ee8c6fcb13c88bc1a13d98685863d2c598005c256b5bd4aa9bf6bb4e6257e7f1564dcb42715afceab8a78eb7322bda9bdbc001360be6e65a561369c7bceef057b1b8fec39a08e38942e1996c7d4e0d99f770abe3044c344916c3c156b448c1b1fb86102ae548c0ba919c3142cd15d5211af06458b245667cdb1a644f24452409277a8b4b9a897dc8b2fab971c1e8d3c8acf06cd3fc1d572c1354dbd1d256d3fad70a7d585fd5166cf12171000cfe9634d3b0cdc7cd0562eb84a04fea119f51ad18d5733e628bbdc326dc2220a94e4885a260a80accf2b01357be9aa9649641097ba47c67965f7e61457ecfc4dce4108b79509e9ed374d26c0b2a0d77aa6378d1481bf091976ad2dca03f0ff5d202de3df8108d60a60ae01bbac26c767e6f29cf0cbd4d345c6a43b56121370294b66c18d6104282e360d7f85ed78615f752b88e3de1e1560c0b24a13e8ad9011e44205c8790111df0f430ee762c8290ec7e8121489d9184e2aa313b77c58f07755551086bc022d2f5133b513fcdaf812c02b3a4125777c0245cbc11b3f3a2d84bccd824ce20e79c65beaab9bd77063ad261cd58a37bb711db82207e74af769c038e1ea9b56052deb367147b69804c5236c30652d22a80d92ad5fc656477836dbc29a5227d107ad14f0c37365c2c96656662fe651ddc6f7cf2e3b9d073c495b139cf9ff98ee02db68156cc2a6f9346b931dc8fc6781df03cdf6313ad8adfd803dab2ad0d4f002aeb8d2a62097274ea56b354ff6345e52d56981e036d03af88d033936c1f79c044ab51cadb9028926ed39d52ee3ad4b56c497a32d70f45fa4c8ce048568e4fed5cf822e054a888dc3e1ed5e5829e3e4d909d6c8f64b7b8a03301a2dd7529927085bd436a48ee6a981f7a611e40306a31f18e9882d9c1c8a3008102fbb672c280b183230ba0d126b6b18bad6d6a2563740bc4146581d152814311a445a9b3a900778ef146ef793f535f6de8cf58f98ac134adb6fa5c896fb43d1796a1dd9772e076d973aacf51aed5e2c462b1b9ca423128c11db389e19343e3ba45933e760f1024200cf1df1ad737aeeaaf252994b2f31e57e9a59ed409b72d1b5bfce0e9f470fc66dd6b419bb48ad83baf42b8a0a6d7ba14b5cfd5a3a38fda685079ca395f20ac0717a999103b583c1ce2dc6f3f58c7bec165bb48975d820d351f9af1679460e2551b3003b560b3d67fb99d0582c2ed6caa1ca10cdfeec2ed6b062e85c291b0de551cdc820fc31d4a129a900fa5717a637974a68fedb67eefd87dafda0e81506f5534ec3dd1adf6c00d2f1b94ef87adef60077b7f6a2c553f5770a1261a79202ebce448d6a90b10aa40590132e683f5622a18ec89cd23efb57c6ccb2ef8e150d5a0665214f95340d946cada9bdfcfe6eb64d3031a7777ce9e61178466fda4f071ba39a50eea30da00513a8cea4af77c2b366d05983248fc665fdd13b4482050cbf5fef8303aed91c81c16decb6c2a6d3c7a0e93156751042793750e9f779b20979ec8c2e2dd8840b9e1ee6f0581fea1d5448e2193fdd9cd9d43bca83378639046f82bf70f2ee1928aa55bb93c2f84840d23e505c7376e0052321524acbe0b909b4eae25cfc700cd10ad458501019ce15c377ec00a16fcf359baeab000bffaa0a1d2915d69005bf7f9f263b9f5b5660cf56c714b986ba01f04bf902c8997ddf7c4a14498650ac7dbd87676ab46147813bea8db59b7c06450562b1de6f613f5f0ecc3d7f73ecc0ef02f3d1202b02e06836bd1ed52ff7ab84cc77e1aae1db0a460accfa11b40357f38749aff38410aad36a20f323007aca8275c676e486cc04d302b8c92c4f52a8cc4d4e827b983fb1bfb5941e6370160f77395db4fdcd2e9c5c0249491f8db794140d872d010086fce1af2e620212319c14c375fb655f5a8d6c0506088ea2208f66967f5fca15556bd0e134cc5ffbabedbc6456b781502180010d3162779b5f1ffe6dc7971b67b4d66a9381da5ea66e95e72fd1c01ba38947280f6100be78cf5a2aa285bb5ee3be6f67e8f98ed741a0c87de7c8740ac2848cc907ef59eaf634fb42c168f93fe50f81aa1ee3be0577835a306071a7cd4e3feace0641aef23567292460bc69437f2b19e20b33bcec50dd1708bb2cffcb9d77c140069f9895a967b629d33476ea8c8acf019bd9bd5cf696be9e910a8a1a22bc370c087189187e0665b2a1b1d59ec9fdaf3f381c4def3e9e35b8043632aa45483d3a0576efdabf4ef8365ab5b46e47a907dece636020c21e53f3bd20168365a335210bbfe3f9f1c4af84884f43e20405a3a24f65934ea7356a4de5dd836117b3ec17087e83367dfd83114c136a7e1a94681abe212f9c9f74a7413b92d5a24483d5ea0e3bae9f65118e2a5797ed7ac8007fb78f853e9c30f67cddd82703b664b18f870ea32b7935a793806ea757aca3e4793a75bbef2fee3f49ad50428659f53b18280e31bb554969ef658beecaec4861e5907250e7b34deb5efac1554b6b427312560918f55f0f8690ce1fa6ecb3183038302c44934cc1e7e496a1493f84e497ea47023cbba123f3ac4156e2f2160c69888d4e8777b1546b51122ff040123e730b55081c7cd22cd4d84e04a7565ddd3d963bbf59d21e40a008539898fb24f9a70d8d0651c5e2dd1f3bf9a14a607019ffbf0353f5b3af71e0daed4ac4c5ade5a38dbd0f7e893df089b5c9fc0b265e3cffeb3a599546bc191ee3d919013d16273ce77503d3f4766949fac6372edda2e9aee417d8d274afce4f69c990f1f414af968b9a0634eda27d44623a9f49c5e993dd33be5961905881a3cf47e54e6990b030fc2801fe1f7080a9a178787d28edb7080ff6d07269fd53adf1e8a4853afc9f0307bb003f2856abc238d657a2a29d8d91a6db0b5c67444feb22195613bd1d7625abb708 false +check_ring_signature b4ad2be155b8319e75175c5b88b3c9fc8002834bd0072155c4df9a32b2aceb8c 2204fa1c27b8c39c28ef203bc9cb7396e386198dbe23c4b0d80d712c2767ee0c 1 6c63aa9cf117d8656c3035c550170997c835e743518ed2a66c95afe37ba73d63 4de3e660dd92b30bd02099949c4499304e3797b50c645b13469754e9c2c5fb01a1f32e2535d2f514c88dfa9de827f4b1dcc1da1bf374e4645ccfc989ede69c25 false +check_ring_signature 4dab52116225bd4cb6777f7f5b581d919948d7a90cb1e899a1e23778b020ddde 3aa84d650a5419d525b650c7e1e77336eb65c1bb01d5716a538e9f7e709f673f 26 f3171f78f0edf884a650ff36eaa5f0d6192f716b1082e3cb26e3c550831125ac 550403d8654ca5131ca183eb53ee2b6f9eb4d66295c3a7c63a8870a1a2ad688f 7d21690c2dd2b53904f6c4061e4c4f93098ba06066ffced9c6473471896b5374 cd3e63125b12959ca3494666dae5e3b98aadd8870a1ef9db57a85fd0c2fb6014 a908ff69059c89aa5e7dc7d71bd971493de4ebe354e8e234e604230a95832349 22f185e92c74d7e33c3cca79c82019f0e41e2270f2a96dc7f8ee3111d80cb296 b79d92974e394372fdd6455feefae427fe428f254c76c7fb352cfa147103708f c0602ad01c103432bde3c885517f092b2e1ad5a01d209985d7b6fbe9e04ea0af 22a5a100ccdc08d4fed153e873021cbec89e886399eea0407acec7528809445a b2fc72dc818903616a08c0c47c17c24e3af36215f8c7aa44e9e9b73a5181f3f6 b6c2c58a2e05bef7583493a1bd7d455555b8f5e554999f557ce25cd1b515ec78 499620a03de690389e7aac21d138c179095d9e58d0dd3089447b23b7cfdd4e51 443d4fe8712c7a07902d2c40a7acd93cb6a65274a436013078628e85cbc8ed86 1ab0d9828cf48a349a3ba1f49f36aa22dd7eedf988a5d0efd366e8c9f1522a2e 0b36bc91d700b9aedb293037d022e5a4810667c91412872a829616de3b01a398 c703f3d9769441b41822aadfb9196720b7e78320331a6190a89b05f8e2deb293 2157e47a92eaefdd70deae48e7175590e9a43f83ca3709a0ee3da58eda437f7e 31344f8804313f60a528f0a7dbc136d2935bca29bc1c8ca699fe5f9b00a7e648 dcb42726359d2318e5604b5f922d09d49bfda80512a2244515847eb3161eb403 e6cbfa32cefc4944d76ab72c5dc2ed7fb529868aa595b3b08f40474be5b7e179 ba866154d481f615ac593cf6f2c29c6fcc2baf384cee497545fb9d741ebca7fd 36cde23c8c75f189966d71733a4382bb78fc2682a219fae8a789f515ad85128b 90dcc59c32f09d15634fda5ca9f8cacf11f6f78e6bdd7eb897d8d9bf368bdb7e 4f6de04edea2f73b34da7d9edd9a8270c929915c08a961a81997f523e991e2fe b0129305b386fcd123e927bcbed81f0b7c22c01ac09264315b0e96f5a1a27ddd de853071f31da406a0a7382364c3b4a5998c0e1325ecd183942960005de23317 dc100db78c0f33724a1077800c433b80389b7ecd1b1b336ee7046405540f390f3e9939cf651d869ec0ffaf18030ffc6d938e9dbb44e79bef55bcbaaabf26e207103576fed7e78afb7c9482b9d01da5157cd59715e9599b325612e1d07634170ad169ab56618472b46d039249fdc5c1db9815dfa49cbffe40ecd61fd8b7d99d06afff60cb1b8b61df4090659ac3c809c5256780554868d47913f31707369c86097a535e23cdba8846765beec5126a81de0f4d4ddcfb7426fbe1e86e9db48b0904f1c72cad72c859dc1056b46924c62a286bb58aaebf20b39e51c0ff7c219b85064583d2fc6428d4f8503e7410c7467797c4f9ed02d38e0c83c96d2672bffb560b6dd5d34a0a6eba2781426a41fa835adb1af102d668e70ea12cd8fa197253b50343f4a10a1b92aa9360287322d3222eb07d57a3fdf6f4d029bc4eb4e35dcb5f097ee09f6c91a6c112269a1d89b0dee0c9783282aeb47dc21f300567be2d43340043c8b7f6fecbb021fc1de2a938ebe0c383d9339e2e20eff4c381a8143a592e068218c340114b305a75634506d0743a88756492b070b3edb9989e6b6f77737b0d13e3bf8d1c128764a7b47ba16628405edbb725e636ace83384b6bc45099bb2064071008f13d2de11f2153ef4a1d98771e10dcf032fa46f3acf5d60505e7ec001c4d2200f6647a0254cea70080d1347541f2c48888c536331f11a6ab4e836ac03c8d0f17d7f462c29380ce71bb16710c9442a2061ea0aec78511e5df70b59dd0dc2bc40702ca1c2f3a1db0249a5aa1ae33df9edc041536646ec839ee58fea020eddc2ac768947bc106317d5d196e75b70e7d9b4432299f4189278daacc7df1709d9de476fe4530dab62190a9def0cb4934cdc687cc893c8f4a648122d8fd40c03953c25e2799fb872bc7ad9be1da7e2232affc25b9632776ba668d2301f86740bc481beb27b2399d95e94dd3bdb3a973493b9665f0c32ef9833446cfc79992b0c83100c6cebd4a6268086c0a42dc889e75fde1cde389b79ea4a9f3ad08058640825b75ee812209eb6a41bdf04a0a53934e647990d3fde7481caea480bda4964002fa7b2f1ccdaf62d75268d62596768c46d2bb7b6f35f193c7fc2f66a08684a037677930a1b40c47dd872a9d79d27b30b565c1a0e029cadc1454bd4cf1d0e9906f55521857af6944ab65e3d4ae038f63cd41967aa9d32c79ff17270c001cb9e0ce98b2eac37b047a4d423e7a8499d6a9d30b28fe184494f41229f30e28174bd0246c4497d295225f4e8c3162ea1a73953e58dedf7b8e30b6383427f6dfe890e0f83fcb5168b6ffe4b8bad44ca8ec60d26114915b01b6e5e107d702d45f3f4360fa32cb76427319fc57d1a3e0ea491a86060c73ca61de78b5e1934af85f0b1d80d2b6217905b79f773086ca2e5d6e346d11d9000897adcc5a3bcf3750d1dee44097581991b173811d87bcb3797eb99b5f9606f87ad5278582cb7448ada6bcdd20cf63005dab862efa26134a9938f3685c0b463cd32902008d14e01c8bf63b673076cf55d95f0e8c05444b36a2d4b127ac665bcad4fe04a19fb245bfe495442e00bfac18e59ba306541f49791888fde1cacc18e34864db67c7b9a3d803ac33f3700b2f8ade4e3609ead72953db44f095928ff9990a91e615cf5ac3d50dc8eaa4d0829af0e21311324144c4760c11905a659d56ae110ee55579672977318bc980e0822bdad66bbf6dda517714783c25d3330644a1e94a64445492f872012e88b8106e84b4d055eaf5e6b80eec1a103a2010d614bf9e51ebe2bdd5afcc7be8993910cec22b966c9acb0d7a2a09de2565762b618d0c4882875facbabe7bba9b7e1b201f67a49f6bb4b503c87629dec417795458d8f8ddc0c1e8ac6e0b8b33e9761e80af5d08b524553eaca0f5fabd21027cdbf70b5ced23466283e8059bc9f7d4161051226082d32e5b0bb275a46fe343b2b1a5a3812e0a0ec18d6416f9c2507bf400ef862e8c6eec4df7ca3dd9f6ca0941d9a776e1fcd7f5e8fb8a472b4263de5be02926101bb213f034d62cc1c843657aed1497569806039ca509e0de7a1fc128b0520a984dc25a23c6d4654d253eb5a1bf50c56672095258c1979fc554ae8af6c0923a8ab6bf49b6c39b8c12c44c465cae0bd51ee9a9ec94242a0f2f1669c12770d67b8719bd7c62a27ba99139076deedc07badae38233116968fc78e90196d650b89991b3e8429bc084e3d48df078ca4ae9ed2517aa15073914d922e40342a2c063f051ff4e280262d45c5112a68e3459ad475182e28df03c59a66998ed740de014fa43d18217c35d801eed13efec0aa8280b9c193b46f8c5944e92b1d69800504 true +check_ring_signature a2a3c90353cff7661c677f91fd28841ca2cdb552dcaea3036f0c4887e37b4f0d 2ba1abe7451549863d85ce3d3519c7a93fdcc9e6fbf4858ca93a3fee3975e48b 2 9e9df939daf514e63f052e40217088d1035ac68c64409c727ae00cc73c41472e 3c47b4ee696294fcb75dc5daca4c8915565755e1c472bba8feb788191a00c345 1528a79503c8d30b91275aa6732ff6fdc107833e31596b57a233fd0d6301c60ae95dca5ec47b2a93e9897d573eb70550ea9defb8e7d8b30451e98fa73aa51a06f31a397eff0affe1a32233276acc774c6b7c350d1452cfa053ea455d42a1b4094f3c213d2f0c711ee15a493fe1d7e2d8c35e83594baba5c42055e5bf8c21aa09 true +check_ring_signature 4e499a5c8bdac02b2b8a2360452377c0d9104f5862cb1698df8e0bace0bc0215 97ab25c954ef690a713dc7d95c5b066f2e438a7739a671c06feab5ffa36e1abb 1 71bf2a3f208bbc79f396dc22f9fef4af32b8e0d1c64332bf1341277828fa4abb a76ab451c7a5074f495d4187f3cadb3dc1e3380858c076ec9887722a7ba0c608d354c6d5d854c9bf91798505e56c0e68f4e3278654ff20c10013f0f74b01d70c false +check_ring_signature e52d3a9ceada1c4a930a821ecc6054e2a25ffcc3fcef8c96f849e5f6848d6056 a5901151eb1abfab17d352f05a355e6d30cb70973ba345371654c43da6f31332 8 7708547815865b400a4782b20a3dff959b4b01db2d058028eeb07ed30f61750e 8b3a99f2f8f5a524f5b252109f4d8496ec2cd1841ea83ab4b16a8b8181e99d85 16a90e4f014224b0be4f71107e1f17507c35da20dc7bb899fe3058b3dab9919e 0d9c85160b06a67e63638257589bf150db91f5c8e5007623ea6d7c8ce5b82070 5db32d279bffbca906772cc8c0313fb45210478f66d9417b64c687e6d868306a dc583d3a666174de2ebe3fab564db32a0fbf259e8ff80d1bb4fd364bf7dae71d b655f0562f65a912bdff579487bc6c4036cb25795e5a4ddbdc0cc74dff650c60 c5d2446b43e6da747e9a93bac657332cb0cd71657c6bf27ca332a28780a333a0 dfd939be78252c6123aa018ee45ce1fc02b4ac19f1dcf720bec61829bf0b3905200244dadffeda36f3b28d96decf43e04f208a37dd732d5199b0f4b5a69dd40999bbe23b575cef252dcce1be2aa2b38749c4aee281e6d4fe8d12bf1a41d1db059bf8f6dc872a61831252c3584d1825c1e9e902179e8fc0b2e2a0f37f95eb3c0652605509e40119a860f6807aedbe434bb89de034604c011b7692bc4d604968059f78a7d25195094447c83df05d4513faf71f0ec5405c3dabf3b1dbc8d6b4e90e4c74073f0fcb421a1b45a1a8bf799eeaaa28db44ebf671fbb14d748970fe2f020d13b9b72f65a7b9ec616e74adca2ba41534b4fd4b2b169b782cb6e73e80880ff398007e3face7ef5e22a4b5fa95704b4dcaa8f51657b2f5c648aaa958461b034aa66b14440ff8302c4c95178db094c00e9b30d8598fcfac5567fb002b2e3202b3c22b5775243aee372dc056a511ad02d8738fd6ca8fad7de5459af63baf560a44ab38b49967149498c7bbf93c939040d633ea07e949a063a6962937027d2703b98e349e3ead685c240ef46c61a0dbec776d45c008a8a8e0c5314ecff70b1d0e7f1b8b93f128c97275536212e4783077c9004d2c041870abb1b7bb3d340a0b006564959e5f29577c336fdb885745714c5d954b83089a47758391853a1b12ce002e578323ea9ecf2763eafae0f1862cb49afccd858abc5e617c60d839893bb904 true +check_ring_signature 0e6027ef67186754f50d823a43ede17f6547eddfc6d678d8ca24ba32815bb74f da6e2892260f024bb5befd3c6996048ca4d202ab6b872198462c54d7add9672c 118 7d70431ff70754855263dabe2b0a7ce10086ca9617c719c0e0813c384f4ea1a3 24cd66fd38860c64f5706db38f7c181f1ba8b638942dae92d3509c6734d7349f 6259f11aaa5fa7f62b51df2b31cdabfb3f90771982c4488ee7afa69442cfd7dd 4b0a50d24a8c6c474617eb3226d0d6a77576d6ef22565ce8b3ac99f083072939 8ecb1e322a0d60637db1c31486d3009df31355bf78da04de1cfe5fe0b23a61ca 07ec1c3b0a133f53b4b4c9028179f9f8836aaa75a03f514b519aeb482b4f0a44 efd305a4f805e498a578a867856cbb3d84542ba67ab0e5521eb23f3375ca0b63 6ea081bb3ade42b9e0809dc490385400c77070e6c67e9930e77fdc173e2ce45b 186a79d770be69a1ef842e69d07689bc2e23c8f9b2412af315eec8d211255abd d801406bf8eb70575f6bdc9ec498ce3a5343c561a6dcc17e41259032e2ca43fd 5bee7a339c0fed84f01c14f4d4192580909e8e5a70f816cb9da6c9ae500c2e54 9ff9255dcdfa8e2db59a03e2a6f6f3e8b84d5aafcd83114d4396a6507b78cc6d 95ac9096264cc002f5e3b5ca8faf4dd34cb981ed879ba8682c5034a2d2e7ee58 f848884dc90023045a0e74674394b4462e72efe9a118bb520ae20e1f6aacf3d6 b0af075376072746eb37774855dbb2bcb51a2bc81efe7212d34b7a808a78cd07 eff8cd6d31e3a90380e04ce16a2a6dc66bfe7e755f7e886a34e1b3c7ba32063c eeb970ff82f297fe6a1d5508ae43307149e76c15cef31073414575b330723703 869d6d0f6e426f42551b0d725e5877103c1dc34625f611cd51cbec0aa46abeb4 bcc3256b9804dc23b5aedbb5a3ab06d3c26c24167930822539a3376138db7f3e 34fcbb8911f567348afffe486c73ba91ad15c13548b23eba623006558c725286 7ef1f00ef9c8bf680499585ffc1b5b370350f1f1897f5168caa4b9b8e5d9fa24 2416ff7f55ed5a97b88842874642b44d6084a77a2eac04b59a91db5820bd7b6b a27b437dd7e384062d810b93ef92d54e6afd70ea5b816885ca3e6d01f105d810 1ded6c38ad8cd16622a274a0e7cb7ed3777a7e75dffd9fedc564133fd87d17b3 a9f03ff801bb12c4a304958d26c4c64588ac440d43c50ad97f0c5b9ec61f56da c15604050482afe3fdbdb60cdcbcee71e2cf84834cb658f8680ce9e6c5273600 44f842af057b3247461e7ce6de0fac3a5a5a2dc3740c02a05fbadc9aa4a74205 d639f6440268a23d83e7eb3463ab98857503848c4c4d29e0ed6bd9f7a1949637 fa199c156bd149c0a7f175de2daf2f42ab903d1dbe2399d169a752121b3e3ef8 69b5afa8429acd2a2720d52516f212273dd829992f41403727b85df99886abcd 5e84009721f6a053e7264e853ed6b3ce863429ed6f38667f7ea25a2968a42770 8cbcd061224f651640e1d70e54085e64df7f23ddc4555a5266e2beb88cc99fc3 b900a5ce3ab39b90f38a0388c0ae7a9ad31a84350a0c33dfcf06811cef50f6e6 847f4f62fd3d5e732a2ec76ea1a07e87e8627724e2a43f2d5355c2db243af1d7 65bb4afcea4c1d4e2e817c388bbc1666a0c954c148035fb5f50cfaa2bb75e4aa 774be0ea453feffda10ad67447e4bf7cb99988464e719d0f8d9aeddad269fb80 38b9a6c0a041c3be66cbe2efbad2a9497a6947cd690b69bcfe2605cdd1a7347a 7e9fc47d9ee93f5a149d759b6f3b94b4a268f029889821b8cc1c3f3dad40d224 eb67b3868f2c156150d8ebd340fd0640014c2c6fd55874a082853bea79b6e306 cfddbaaef6783a69177f1b826a6ac9cc67c3056acd305a4808fbee3b0ba21aa1 8792d1ad09458d83f736b8d239579aee23e1e9e2342741e6d5e41c8178314262 a7bfeb7637aeed3160f5aad8ca68996a14d65b1d8e9c1b10e206cc4101394d3d b9839624b578b787b8eef5cd76dc769bebae418a6928023095e9d82510f91ed1 50570a35cf274b5e5a6a6cbcc9da56ce75e96d533b8cc37e41a98ca32003392b 2c839b005764724cf2550579385507b11633e520005909101ad4ce516ed55775 e407318659a46e31a61a8476be95afba750a0cae68fadc3529ed368caf1fd6b1 b09be0e590eb5acfc6fc9c8bf6bd344aa1a371bfb5e412228742b43177bad282 297385cb790fa3501c5bb01cd07e7e341c049ebf69fb2d087a041c608ce2a04e b0f16d40831840293dda13580efd5bfe562fc556f5c9e64e6a147f937ab8283d b65b980b542fe8f7a2eb0d879ac82878a725dc64fefbbebb0223a4df7d3bd20e b729db25350b9f4f08d76f58858c19736819920beaf6d3595aecb106ccb55ecc c0af42c31d0dff40278adfed08a41a32ef092528c8a38b99cd4d7d0dc3409c10 7c8290a42c22ce747c3060cd5a939a633770cc26012cb3cb626e338307b7c4bf b3d2bc292f8e9109ac9ac943c8b0d8de4698ba002e2110aa2d46c3f01383321e 833bc9bf1e4dc7a6fdad4a71f9db898ec645d8e061d25173c391ac0f8b914a8b 81c40daec296765d42a3a2b86b80f8db829b8491e6515dd1d656c7f1ec598e61 5493bad954411a010ff3f985da6dfdfab8f636bb8283edbb16fb9c4d3f3e4f1d 03fa5ba94e8583376034a5cbd27f472dc4c8721a0b05c479d6de23ce8554124c 7b0a6f8ff25465b49e48238c970fd3460a2c434c04d33110b45360f8989c9540 4e0b2806f58067258f7728719187be630b8897326305a210f0a43214b0da4862 d0635b2d4466fb0ba7cdd7193b96c71af75afa7424dc755153e06679e3ea3128 3a4f5160101b1203b939e90860d48fbd659baf84633bb81d2c59bd2033e4c3ae fb533b0396c77d008f7a3fdad0d7161046fc2b8a1eeb316b87564adb7e04e169 dc3c7b0f798e4341fc21be75e03196615493bad8c2d9e66972766779bee2024f 06f819469c1e0149f7bd8f0187bdddbd0be3011679e71bf667686ff7f14de714 c882cae7e32f1602571ae9a72d76edf58feebbd12aa6b2c1069e5cafb03d18db 6130cb177ed98b26842df45b87acf9be336f55b9a397140fc0568045e82857c2 aed05e9ec4dc9a7a9899237acbbd03a5caf7b6e93137f6f4c78081016d8ce1e3 18f2d39ff765dd7246c99c7c7e5a9d415922f3b5a6d8116eeb7bf63ddbe36e0e 5d034b0896fe7742117dd5941f235a9119cc8830e54d6d92358b967a4129fbb7 f94c41d6a16c905ae5c11af70a0a9619b58bdedd2443e3b436e5e0df7364f2db 29b62bc8e58b358c2e016ca92dad1b0f58f7b101b0dbd54af04a648e4ec486f1 ef39587c226a863da122f7b530bb23a9eef9c13345dff619eb00b17bf4a2e001 fa994a4e2f848a731ec5ec376d9b6927e30cf8c9a4c2fc355af14f9d7911bb4b 335ea33a2da7e93bbba2f5eea0f1d1998df2e599c64b4dfbfe791ccbf9f45489 c58a1b5335f4212c049ae9a45cb6f8241523d8df68d413e8b5c4049273590076 003e89127d72f058b3c9fc96a6885e5e5e1e8327d45291ebb1632873503f3f53 dbb326bfc13e6be82965222f2c2f4e03827dfc24d8fb124018e9191e1a5a0361 398d6fa0ed73bdd898f93142f6778a657cea7c007c8656fc3842c2d44840eda6 0fb346fab6e108793396d62b03e6761d260c91f39bdfc9bf63bd7def41fdd2e0 8fcde5f347a6de26bb022409e6865827f62bf569b42628534b0e5fd08b00f064 db37b13c3b5120a34c26621747bbd28db75ab55dce393137f51105f7731c4749 b6c8b7538a827fac86caf7e9917c09cff9210d0c83d8a80ce4a0ace82a3f21fc f6fc2d1b037bdd3d219a29bed95cd0fc4cb8f15484fac494aedd08354f4410ec 8e8f7761540d08030615d05ef2c819709fae358d11576d5d2918ac8867312129 15469223fc56568073e1e1449b555b7bf74a6640b7a2a84cbd89e2c19bcac944 567f82ad4a5dcc8ac6fd10c8a40ca782063be293220fba667ee9bc83a2b354c8 c1a8009fbc81093936f760fa41fd4741a9c1857be18a737fdd801c42e159f8c1 7a46e54b13870bf1f796bc58b49a93348b5c0f3a758af7be779a8473fc865bc8 b0d070d2031c97a36bebde93c758abf656267177cd554ecd8425f4d375343eea 1007b17a3ce8ca41515bfefbe37a925a4fb7728dac8715f9a7543129c3d33638 3696dbe0d6679f0e55e3042f35544e1a44c85c334a9df3edd5bba54fd59e3b29 d165ffa01c526a85bbbbee01c00f97b8d35c60163e23fff4c142ee6430de34ff c81800d1d7fd0650ea12f579ef8d3eb560a0752cde88b4a48b41bac1dc94405b 49cd103ebaf1b0d1732f71748203d4975c4d7a7bfd7aa2567b77725bce69fe29 36068cef47c1c6660326cf1c2d3b6d74200a5c2974e101fbfb089e3e033117a1 4706a2c9fb5d1b2f4461b7d16ba1485cc8f5e5425af26c057d487b3c02b5992e 4f5acd82e3d4bfdd1818269c24c2ff163aac0761c53e4dd79d49760f910bc97f 4e78a57f8f0f4951e0da003a6fad4b673f8bc8fb8bfd4295588133ba5edca39d 4d25735dc5f913a949b152e0acf7d48cda96be8ab01386ebead799e3580be968 6a6c63b4b5bbf4e64258595507885d0d3b6ebc0ea17bad71c580f8d8bb4292d8 55225aabaad883ea640be25c1fb5f5547b22f2b8c09796366a74ac59f52db91b ee981e8e7e083c7bab53362669c4b80ea7f4cf98a9eec4561b20c4619e65f53f d1085e14b01d5334ed2b5950608843d1f970d81d73e1598005f80d7511219d90 2d0e9e02698963a32e0419ab567dacfe648c3dffa2be52e0feb6accfd5bade23 db09728c30590f013d02d6dcebdc50c653b6629630e5f0a90166a57c7e8e4073 9f4618100fb8d9d7eb50c539493999b446bde366680e743925adb53cddc2fb83 0b1187e99b90457f9e015956b56e465244f5a98266d70121bea896c210d3075f ff992e0c20dd97d9b81498f6e0452ecadbf03324c43ea2745b14565c381e42a2 0f11be1c7cc04ec25fd86ccd84ad363777f38dca92354710c7501a022734113b b5b5712e04e8bc2371df8629e3e3580a4b12245fddf5a3f051c6bf52527a9424 cc05e059d4707d6d385f057ed3bd95d5c05db3e2f39d147d79d00711edb5603d 3b0687ae543e3de54b0a21defd1ac56a2e072e9ec6ab93587ae5fb1e89f4226b e87ca4cfa586b3f09d2c0bdf68f4566f5a916f0e9831553b08937dd5791a0639 127dbeb299fcd4ef9360173d2bf1077c8bbbf884e16e89e59344792968aaf006 9fe0d0ee17e070923b69a12de297dfc11d790ba232590f1481c1ff48034d1114 b5556b30d4b472e75958ecb2245e693f532a31712fe4a34f53805f532755d394 4b546074f19d4f7b09b574167b4d0f785e07baf0d2a34b0577246e1d6a4ae349 9631d3e146579ed41dfd672d6395d06c93e56cf9c719cdc4ae57c22ca3de5107ccf0aa640516b8b12717fbac398952c8de650eb7077e4a1208b7f77dc8966b01a8ff783b2ad1b16fb45cd56718e7ac4b4499e76ed85c9e1510971049bcf23b0c506837007b2a7040f52b7411bd90b10c325b985bde943b937cc044c5eff63b0a3ddc4dcdfb31e7b602bc1a63b1c06ed1ad1b2a9f9c67c21e951cb29bbbffc607cca85367d98303de820f5ea98074b7177dacfb8a5a4f00251d1a68f810078c0121e05c52ad1fa1953d6f533e5a6205544b48c4534aa6a791b7960a39e713a60f0b2a65863a5bfe02217de565088d4096443d31a6d7f67bce4e875fbd3ab70b0ed7a50739cc92a88cf8fe4532ad0e53d9379dfe3d8986979a4641c74414377b0d6d3b7a77aea4d5e1a6831e9201505d64028b9337921dc367c8a3e04f1b1ac20bee005a95b4a2f60a852421575de3394cbb1738f977a7e5c729bad18f6dd10a04dfc2f39521ef53c1c5b9ca2c413ea0ea3d070fca0b879f6f646d0ad1beaae7054448a789dbf2d19a1d3cdd8d6254d94876c3e8b6d929fc1758ccaaf8a5d4630e3e631f52e62d5d0d95dac1f3edca3d4e7248b5a6f42343465d69a0064eaf0f0eced3fd2d2a6bddd3c2f6687b9fb5fa6756d87b05598dc59c09ae1724716f730a549ae472551dc772822be8bd849c6c13f6c410fb7ec31b4c2577d4247385d40d7ca6f9a2055a780330da2bcc41b4c2349d1093f30bc5d02632b98e249d15830131bca56f868db9761bc623055e7e81aa2dc5e68146cee9c2051f5b0af0ae17049f739dc7aeb46b5630ef8803de0cedefe41a4169a81c44a998c453bbde262e0d03334bcea1bb607c6a1760de0103293e438d95d25b1c0211795476b3c761b007b32c134ef7ea34c85caebb1a7b42d9acbc9dc01b45f1b2abda133475b2fff30c9a69083da2bbc792a9ffe0a3bb45d1096e70909f0d16ef3a69938ce395d42e05c92436cb7926013aeb54bf0d5ba886323ffd227456d0ed0f3ba366e4103cd00a66266fd12f3c2dd546e9be0fc918347aa7e1807f7c53f943d46392954c1e5c086f777adef70fe94b1d540e16cfaf5e8dc7fa7348576b54cbd3f415c3b85d7e0b4d0c4662bf755da162d8cf893e97a7ee827aed19b34631d5a32be6dfe0000304d3b962d41de0ab23c4890a7821c7ff8396559144a40f29ba73c49f22fc71160790d5ef0a9fd05c4984ab3a0f054f2dc28b0b2add7e8ecd35dbb491e73629a5074ad1cb622f65d854320037921c0c8c86ad018874d3843cce4b99296193bafb0c0aca4e346405972974bca2592529eea30528ae81536f2c95e6cf84d2c8b4090bd6b33cf4cd9e7fabe17dbb4ae041eae9672813e2296b7520761fa407b448f20741bab8bb94e02696c05e8f091312dfa7d4b723ba499044fdfa879767f21887004ab55e677f6d6e8c0fbaa280d49a8260598aad762a3f25355d6c2e1856cd4806bfbabf3be296ccdfc6ee6465a219191c0ffd54db29e1e2a49355391e57aa010fcc699fd3a10f419f8f4f18099ba8ee14101b4c5f661c4e3ac40b59dfe95c620d5da30b7e7aea96bdde31e203e6d582cd9345c63ba99a6c15c6223bdc6dd231057e44f4cf1b8ba8c09e656555d6c69f04dacbdce13a1f23d24c0209649858dc08e664488a9fb71cfd1b6214e0b35ce2ff5b2ea4b9c52722bfe43a920c05e2990798caedcc26fd2c5fee43c1bc68879e7e14f3aae53eff9e2b3fb8a59bda3c990c8365acedfc6b32968d56f3f8a5aa561237da0cd201b202dc748209f121ef45012c2df416bcdf2f294b51cc0cccbf00e19d707c669c385938020d9424091d0d07de9b77f9ea2743d10e42561aa9d3a20ab52732ddff29013d184af81fe21d5e0eda5ce021d63aad72aaa15d19f255bc831e0a0faf0f91676a30eb878f12fc010fae0d0238d56d20c023286e64d6e9d44b2ec6fbfcac23b6375ed378b8531cfb02252efd251119036b5511881d196c0d9703333c7c5b855034f553ed5b9c5e15098932950c2ec44c935bbe3ee8bb83e9e4019dee2130492d5ac8f9341a51e29f0561e68c2de120a717eaa70702b8dc7e838f3f333e8a30f3ca8015fa49da0604062e768289803859ec813f95669ade8880eb1bcc0201b9008c3d6e12200f0e320e54cd6d3298f463204900dbd99387a069ea9af26f3b8e1a28c9f971503ea1d2010481dcb5ff768350dd8f68ef9f343dead64ca9689d6310bc827436a767416803a31971474f7df15b2554b00018757fa7635128af34a60beb4a249202847e6408702cab3515c53c9368c8c58d836c6716a59f113c2f521ca4541f217b495feb0d409dc0d4f62a6628511a2b2302a4929cddc2bf752434dc7feaca79281898f60f82b5512913775d619b5099b48f93e2d77bebdfae5fc96e96346eb7858a461f00d2c225239367c054934bcf92d5a7a297428cba34faa3087dabb319d202c7b70356e6ac2020ffcdddad75a0d952f5e2cb0b18158a09a76eedda35fa2e7320d003288eff8f4113ec9c9627774a0d9e854b86274701c7c22f31ca7314ed395e5702dbd491ce6cbe7313808af7ea7017d4ecbea39e4c41851e34eb30c86db8d7530dbfc34fda696f68711cdb4b5c295028bc363b7cc0837d3afdd379cc5668ca79010f7aa21afe05fff714bde6050323b88d298a95547a026d33cdc47f2a635727060c4465c9c283d896ae070b2d356de6d93e0532ddfc3e9c6bacf8498e1db5ec04db7f58df58762de1ac68b5f4d508738b4224c5753cdc549c5b80327fdb2be404ff33a9cbb1f2c2d19dbdfa4b057d074847bedcedd58ebe14f89e48d9a837cb0362d2dd90673f5bafcf2f70282f99ff0c8c4f021246562bc460787482ce639e0cd9303c39fc9096063ca45c8b70b80de7a1280a28981cfb296d5f3677b63ced03e9c2d8dcc028c360262430a20cd7811b24f06f89ecd8a31392c6fe8bbae637020d0c665351dd7ea9c7ea6ada080cce2dba1db41278d92c6529c62429e773a00da2bacc7138663377bade28a97e1074659b1370285d270298979d3427a0bd5c04b2012d1c145cd36b79567a5af5865ed198d03c6fcddab1c89f3cfa19b3613305da3613a6fac6b3af7e275f708208038f3d3d30fbad2eea3f6aa66bb6fd343b0fb9c418111ad33a5cbc51c874d422a657a382cc35f3c661e090d8db0a16773008ed646d44b33893610226975d5a9e35d4f5cfac8f34c45c4de2c15733e72c390d980c1942101a10b132db784c2d8654398e99ed034fa3cc6164dd94ce1e77cf08c7a1351446a72d071e226728e7c7ac23f3d31a91212156921786c93a7f28f005123f7a3e5ef37534b8445b75415718de69f66811591007776c38ceb9ccd1520b3a469143fbcbb193fae979c8dd5c66d7053e2a94c123889d5870f3eab26fdb01db05d7ea6a86e22623a4ade72bf8f915a27d0e723f53c537a4b5fb13c7e7680fae93437dab7c79e7f7945c45a9e174ff30df24609886bf9e5f852bdc06d55e05eb24d5cb1dbf1dcbbed3c498ae66fa103e8aed164142ef280be556775f133d041979cc0bfcb9736ec240091e63284a5f37a3488279f9e4fa894de49182a8ea0c59e567796ea4a8bc83cebdb491d1b9186d73669ec722e2d1294977be81011d068867ba389490059b16816a0592d767f88b925a7c8e167de6ed076dab9d54e309da4893ffbbf635bb9adb85f9852e8f5fabf71be21ccde4d79ccc216e0e025907acba1b76665bfc94d80454384bae55cfcb1d6b2de835173ef92032cdbb02e80a3f822069af9e2ccbc95256b6b5ea229b6612770b845ac714bc54998fb62a870fa7a58e20d738823d081d25ce2f2db42efade7fc2c4d55897e571a1fd15cf1c0ca11e30e0efb8ac887fb94fa8a66acf4e7c5d2f4c3813c97eb64effb4dad69309cc5a877d09985a360eb96eacc07354c6ebdfcf058d193734dc4bcc802049f800d958a0698a2c05082f9994080db906a6b90393ecabfa0fe41a86b53893528e09470c2eca49bd9dcc0169adaf476041ed3b7afc33290a4ea920ae4a8fff8472027ccab7a388a8219ef988bd22864c3828c246d426b5e6e59ae4775e8abc30d70f35b383b382f4421f7a94dd01733646f3b7ba1d7827788c555dcf5c238ac5870c07440818be1f17f93a91227ecf0667ff0d8a6995e263260198474404413ca30912d360dddb549a4d44266223c40fb7ca31b4fd30e50d7166a783a5ffce128e05960988ff6e32946ffd628e292e3b33128f2300be6d0935fae8b8d5fcbecb99029fb368730e40ea1033415233bbce34fb5173f951d8f98a163775a7d80c1c0d0d56a4df9e009ba4d731b1963a7f836387ccf30b45124adeb40927bb248ed9980294ae5518cab499cc2707b8183f911e1b3980f697cab1c2392a384c3c606e8708c785ec1b28571e028143625bea4fc488ea2949fa9559ecd7d5060eef8ac6cc05f07c025599671f7924cdc9f9b3c63ee23289133db151f2514e005814ef911c04c2af44959f0f5cea49b0396a451fd9b93a79a381694c8f4d7462c074eb5c570213e83777a69547c7d8ddd6a137dfc543723c858b33b293670a6e793494d5140d02005c23404fa0f7df08c3112f93c0b09e95a0df1e038332351a634fcbcdeb084a9140c275533c77ae6df0b1e5e17c303cba52a0eb4a5862fea0fe90a23af10746cb50a9db64c04bd93d4ba1152281117e42ae3280bc0917a67d4c42351ae3e79bd404192deb4d354b7b6048d8e7eef13284dc67fb666cbf58feb5ab0d7e070e27c3377a4c31623735e3a12ecc6c0433755e4cad3ea748792c2b1202ba44bb0d0ac55818028d49b7799f22c59d15f9341732693da901dc97565d17d112081800d5feb468e5ce55de0c693f7ba7b3208bfad26bb1ad99f2f9992a7d70905be8038c3e7a6fe983fe9509d30e825829df1a51aa2871c8c288e7e269c6b9e5bdc409a90ae000ea8dbcd543f09655d6413b56b32c80098bc1e65cad7fdfe1816596000d17be8f7f33a3994fabe16e5e996feee1738c1d6c696f93f55f13ae053ee10bf6ce5663421b2516fc81ec57af74ef31cc386f8029ecdde54f5707153f5bd4074e95fd0f609603a8f246d89947998b26af23e35adf893f1f9a817d9cacb4a80e3cdf5bbbbdee715312afa5c39161619a56ba8338b439ba29fcfdf27e2f4e9a0e2d93a8ca87af33c4c0a7158ce44577126c2bf13a659116dc72567bd19025980e507b686dcadad5a2ff67e20bef5b9189e9285d51c9a6079c923a8e28da321a07a9f59ffa772bca417cf2687b40735aac8fa3eaaa23698229bd497204f9f5330aa7febbee441c206ab034430186fb86b4634a236dbb1bb22049ea3abd52da260e3c9a78658c2b968f74cd4fd33c8676f17713e6345be927b3dd44466913ce2d02d2a9afa1f4871b1a36e11361fef82d1f1167c00f227935a69562799a84045507a66982fa8ece16e3443b96f8bb58e1c6be8f8bf83643b321e018ee99cf5a85059af1d16f0fd839dc340c1f2bad179cdd4457bb615e06ce4967d21fa6edd2ff0172b9d650d363b7b0ea197dbd599f90862046ef804b62d857900dbb7c9ac5b70f4ac8cba495779c7f9097672ca37c27d621ef41e00a3476c3411ace56dfbc680d2e834b3f2f285339d71bd9a9eb4083215c5540a9c20407dd40b73260326dd6008a39b6801342542b33af142020332d469a06dc9fd128327f69aaa85e19f59708522100085195175b24adb2b8f35ed883c02c32821fdf7b3591bdc9a672dc520122b3a07da1bfc6adfd22a7357c89a7149874ce7557ba1c72c3d836740bc663017cb51c5ed2ee421cc661a83c9cbfba3810706d55c5b3d460193569d51a2fdf085ac697015861129ace3c4ab8ace143562e19ff11cc8d843eca57d7b2dbef1f0f96a2315d7a97d30323fb80cf455091b81bd4722f1338a8ead38ad4a07a65aa0edb2624ea38e2a2b01b9539783ebece7ffe4f1b54e186ebe14e69fb9b2cdd2a01a52253977d49ec1132294c5c8aee4f96bf1423f068357219a8f0e25ee0024b0d23a8565c2bee3818151cb3869f1d1939585d57b1483becf0060809c045ea140b0842f5bbde4c758f7ffa324a27ae4f98187eb93f73ea3a92c2b985f32ffac1099387051a429f9a60db6473fb735c6c08412084c83774aa110eea8dce71956207591df205a7033a72fc869cc9ea7db0673ded1750afe73260a8586c25e71f67069c10fb5afe783b0f6e0eb92b1653d00453b92bdb7845e295774a2cbcf253de0900d68fb32ca5bdf42a459358473662a74eb0df6ee11cf96d8cc671348f4c9700ad9f287cb507a6c37ef51328b24f6aeb3f4ad0e099112313df4cf860a2483f0bd88f74e9f567717a0a9889b5435f90c6e0d0da80b26ac6076b85bf83c7166e0b39f47911b657433bcb562dbe49308981ea7a1967f0ff867d65e64ba80f3e1b04d17838a1513fc1d329bed26175591637c9dd793d2bbeec300e848d3d431e1c07a3ef0e4ef5e6300bec772dca7a2c7dff299c064d3ec5bf04f2d9190c1f564f0530a2ef0805bb79992944346b68142d39ce98fa9ff658ebed2df80c7d1b90f501db1ee071314356903cba5262c764435f015a3c68273306c9189890a0a5d8930a4497be08f92f142908a5fa64bcb3267db2a99cd74d89ff1393833b838580b80cf2615acf56ba8a428198b4af87c6bc744534e24ac028688a63fdfeb1fc6629006cc063663887a59f6c73fd1cb662b84ea9c5ebc05ca3c80b25307e56baf63703aedf05ac0fa450e8734f7e4fc8ab3af581ba3ce318376588e03073c1a807490aa035c6670504369998cc913db51c3463e37cf83fed66d4c1195bf9194e35230a72002b1838011ed4d3e043007670d5da0ab17425271af5d80f3464d218dc4a044c7ce1966993445192b0f9e32cb849e525334a527c7d945228bea0d240ee7a02739a8204a8c1689288fc5f01454f2730795116bc37ed863ea013b805a74b4d0bf8e2cec6a165bec2d5efec324ae4db808398c9976805b511f36d15b86fa08f08d4a0e53ccf7fa71dfce01b6cffddbfe3c3e8a6c33a6f4f5e856c3b204b07d90eb0514cf86cbceb1730845b6f7065bc6b608a94da2d282339f666128e1ec194083c0b2610eaddc53154cca4dd9c78e23ab898477c853e15bcfefa697f5dc48803ae895801d29b77252441912c4a625486335401cb67fb991a75f83fbfa202540db00ceea0a6bbe566bbf1ae64bd3604d47ccc7dfed034140ecaf85d9162458a049371cb69c09338ce328b6e5c7fb4cdb6ccb4af47a9b182efd27d86ae3c60ff085de41409560752d2e93c52df23e7d4506144039a1556761faa8a124be0e71c03d7b70cdf97a6a7c6a91e5e9c5e9958cf113b4543cc227d91fbbf4d1bdf2e620bfe0cfd2f1efe69855c7f6da116e7f19d13567917ee68eee48f5f46a2a9f3800868418ad78594fd24b7858425070391fe3cf23dc769eed4a232ad56b683bad90437612d3dce35ff57770272727d044541f30e54c2dfdf9621b6f564bb5e131b0509c6434df7fc6daf9cb1419d82d3df62a8cc053b1ba4ad5cc6d62b0134899804a66c31dcff6fee282dba01b62aa2eae7a3daa5c7244eea804e276975941e110c7cfaa202b96364a28b45ef888f4728a4b12345d642999cbe93692e7f444b470ac65cfa3ff76b705b13566a493f9e733e90792391f1aa42fff2e5d2a17693330f5d769d18c52068f3372ac73c8f139e531c3f89badff4abc6be8b7391736b95087792233125c67e30f5e8e319ae8cb07ae43cb3c15ce8b56d95935e0ba0f4c0039dd24822091da0adebfee5ef56620a6af8847974f18ebb84041fbdd7037eff031a65a3f06c80ebb11b9c004ae8814d8d4f076c0a07570fe64352294cd1c5ea06f8b0cda48f50aa8e40acd351c5aa3c66a7f44d6ef009711905c5bca32c7fa8031f14ac3fd1b9709004ac71fad55bf1f5c24d549d790e79750ce4656d38417f0dbe5ec8dac1d71e6669794f9e4af2c2a0c178722771fbc2fae203fd2a58f56d0c948735c50a02fbb7f0556284a94c3c4d759303494a8f600e7783919a4635cc0c69d1352e764d147595ff0a454d57e0aae6c1453958e4b5834ecbe3b763337108ac5fe7ddb1f0ddb462208e87a8b7aee2fe651fd57f9d56bd26824d2e4308610087b2b55a1c572a72af495761d194dc2968e8573b2f19d1cfd5f570b05f281802abfc2b22f8fda74fad3ad10df89c0b5728b5f5896e71baae7438cc12f9a6ed0964c082b8df30881635030a6d38d8c9a43e897e69d9dd7efaeddd3de47372f90cc0fcca948459a472f96aa0a9d383f28aa48ab213e1d61c2a96fa3680dea0640b2c4313f5e5acb7680b92c90902cc04cff09011e8469be7f8f16dd894a9d71103c6d1956f99335238cc4b5ec1a2a75f10fadabf46c963475ebe30b86bd21cf60cb066277f905ac45d3b8719c93047d68c52c256e6a64215afd95af61468497b0b0dfafd7fcbe1441aaf4b54c66f5b9ee10f5569423cf18f4e111fe6ccfcaed607b814ee4e782d02c4c26830e88d3e41b0079ee0b7bdbf13cfbef77b428b737f0806491b46877e09de1c07a3ba7bb28e2b4f8f7b1fcf4f9214623e76fbb13fb60a49d3114f9e614a0fd90eeb0a2ef1dcfa0bec0b3dd950a2ba6b05a73a3673850b2d1196ec7297b98293d050887db5906a3a9a4394e320a710b038f1b74b1f13059ef4f1b03e6cf35b75fccf2d5a2ba0ef3f761d3c6300bafa99413dbefea47100b8b81bd1e5b6b3d2c679303a3d7890a9829cf95e73f8c9ba04af70ae74b56507911a310e7489de4de0ea5b0da923778d2e69c57eab86e0dc832e1e7e42d4950b2d66f8a8f322cfe9ab0033b359ae593d9d1de3077336719fc23aa55d197fb20f36c3e885b11137a5650c7a4e97751b12a96cc2c3b11f9af3ceedeee5ab52fc0d514eae52b0846d80843ad90bb9b5e61cd684b157da2c24130fd16b5ee297260a02540bbd721d8a766a4e78f47f60da76f5f3f9d40ff67bdbaf6c9d58c4e5050bf1bf07afd52fb87291aa0a758db30b23979b51ef8a32eba23eb3fa2488d69a0cdab880a8b817afc733dc60289969b7e616cf2bd79a946f0c3b52db6b759a7b0c1799ffba3c4ea58bdf2419ffcdaba65301e06d5fb196fbfe66969dc676ca9a08788918a3b5f72344b4bce2b6b2e65e55ef9f85ffb5ff24c6fcbe07d375fd940b1bad7e7197f6deda8d1fdc181566a77efb59640fc8d01a53f5cd0525cea2e90461d48fbd589f4ef72d521f75e97b0f49ca1397174e166e4b6b516dd172ea3607bf6dba52ca5a896c5c1607405458bc4d69228301d0e9c3b2f4927e6203f9c30f1302bbff7a2e7bc1de3736d1b8eb91ca7ab5cf910c49a7083d300b9b4985840c377ba17556acd06ab2b354f8d3df90f37672a675c1dfb08633ddc76eed30f70327ab74b487b0b840a2a5a7d7f853a651063c675f960ae1f6eb048b20dcf6990c2e7c84bfcec30ff30054f3a7e0b38a3e01db2de6b6dbc3f2eb5b9822debc1408ad357d89593c8095fe209ef1a0cdec17ceccbae3a54d26031b20b42fcc9cc504f7d21fee9d554de31bcb11534db773c1c2fea6a7763f051d18022d96d15d8a00ae23489a6632dd272c9ec3048103039b35af7195f6ed08b3f0ca6f6f10bd6f02b822d33064dc37f19a5992234ebf1ebd75d0402303d2454eec5da144fa4e6f0b3751fda249b4511403aa1bfd11012cdc36fbfd876486d97fff1f79575fcd460c9ec3b8b1c816494fd9e27b20ffc1fcae67c4cee14f4bf3e71d925d97fad37b0948f374ba589cae3a174e72bd1c3bd3506970b73a483134d1edaea0cbe8b3fa0db13370a6b2101546c4ea1d02375875a589c421ae475fbf30b0893810f831f908c3313a037602214069c00c9c3b313f15822568fc9aa22452fcc7ad228d2d00097d6422529043c7a3fdea396cd8b380b712cd2f75e767f09f6f2c4b9f9d19bd01032bad7344daa60a2be5bf3811ed6aec5ce6c2d915c8d71c0f5efef47d4ccc0cb997e25a8883edc8cfadb856b77e3a52aedf5fd3b5365022482cb1fe9eff3a0959607180f828e9426d2656e1e690b1c151089ae8b48ed9a7a135c7b21e8be20095271b4c25a49b9049716ff98d905c0ee6131b2e8d0d1ebf5da85ced25cd7a0bfd38d0fb13c1b69f5b9b1c1c9061d1cfdce67ea9f111803396d780c0a6cb4f045e2e0a245b8abc60ad59bbd7514e359403fa34bf27171daa4f074a75c55988047d894cdc64b09dda044360f0cb917b8224a84eb7655914658a8e40b11a69040e016dbde784b3cc6de184f5d319cabab20397578491f33e7c921f1e02029a7f0ab0de9796714cc9eb62261156fd917328bdfa60fc5d5d8b873dd50570cb3d150aa60965423a243bab4b3e7224fbc698bdb950d8165b8dfd7c149fe9eb5775f10b15fdf25a5b92d9c3ff43fe2b9587403bc343ddf7f83e4fbc371c597d05fdff025c19393a6244e336610d2c4e38d6a8065d91692ef93016aad4932b93b0fca30e2296ec08d174f87bee1f40a124ac097b6eb4a94c1fece1de9594379f661740078f8206864b595c1d39fb20b2c976d786885b1fdbca90538643ba18c19352e103096630ab19bacf8d1e5d1f85211cd62396287c58516c139082002786841d2609 false +check_ring_signature ce782fb3e967b3a8dcf2ed12e8ad51d0c05971f8df497e7cdb168b0e7e573d27 4e1665a7070cdad581e849c85a459a6bb4f25b8743f4fb13107cb090c9d642bc 1 251ea48bd1b5e17df798eec3149d2bf4eae98756e2aaac5dd8260d89766f071c 622e83e55ab6101d9fac37a78c24105d1986d55db6ef4f0255c91f9b61c43d005b94de833c0e8367d458b7ccbc32888026c28b794b77c3098bfea23021d05203 false +check_ring_signature 182299c4e62dad310671a682d5db2862f368f279827ccf0b7d2a46c8acf1727e 3b8ba888259285976ab78697285bc3c1e306b3b7b47fae8bdf328bae334c2c8e 2 7dc6537e5c529fcecc2df00b60523c5dd70485cea195d98526dcbabb7e84c50a 0f17b39329c420797a20b5b6c433f66569bbc206b88346554008e8192206dde5 e01cfbe9fbbbc49ea8cb141bf4ec182ad112e2ef8c8c0860702288c72014980db914b15e0c6e1c2ff5d5efe1c79e560efaf5b14bd67874f283ce5d9594e4260ee5a7afa9d5f72e3c28b8fb720326a1d2e9d280d3322dd58a62cb57a61259f3067232dbc0589bfa892a8f8f26b242d0497cd42b7d5299334ccacdcdb7b8525909 false +check_ring_signature 0fa08379c6f0f72b2b3e60e89398a10d958b918768431c0996dc5eaf63aaeb78 7b2accb6f088822acbffdc2d4f59e02d6df909ddf7e7ad1a20acd63aaffac01f 18 27de1aa7dd965972d9c53c9008db931530b53250940842e6cccfe75a7265bd29 21f8ca396b989c74ad89d4d86d2ab33e7ad4d403bd495b1f514fb247cefec5f3 13a9ac02ed4483d60f7fd9f2bc21084050af0511b7c264ec26759e0256c5a35e f880683e7362f49f454c522dacadebddea141f79e1156a2854199253e88c3f81 ce941434e096147ec74ed18978c48b0eba460be7696894bf043d160a8fea6927 ba0e35ea9913411520ec4a1d4b8f9054180ccac04af547fdb921a7474751e7bb bd2ea1450d7f82bbbee70859ee6334bd5865b6026ec350946b28c263dd5aa3c8 29866198abdfc8d7bffc8af877c8b09c4d2dfcd338f86154f9150fdc838f8a11 2cd0b06d1e527782f9df731d8118e5f9ada143c169def621c9dd5cb48c905652 04723cc14c10a3cf3e28ce550d2479cc50f710dc24f5c65e4762c43630abfb02 2c8a36eecc0be6e6670710154e7d54a6abab6fabe22f1cee8548130af9588de3 56aa0600427279027c3c6cf7d19c1abc4f90696e843dfe443331bf897be60864 7ad8349d521cc9653eb3ebfb8f22f010e3ef31aa591e44332dc8289deb01ab4a 14698bddc23af600aa9d0968602f2e0eca0750f766b0f6e42774f8b1df05e504 f56c615c27b4e86971d1b9b66220467456d6537d7abec7b1c792507d39da66b4 5ace959733dfabb5d9107fede2a5e96ed6c266977676da57ca91e2daaad00449 d5b83b208f72f52cc299c134a10dc7d2d5d7f048c84be3bd5f83b94f9bb45c32 56b53939d293f9affa9530b71d077888a495de69634bc42d898f0c26ac550d19 c536ffce6fc8282c6fafbd4d3abb39034144d5d7c3024527cf2a0c92301da7003b83e5b1bba78c78074471efc871ac7469763686c5df7e403cc7e559eae75e02dcea810d589748eec1417f652e15d10763166e4ac10d10a34f2e993b73e39304ae0080bb866afdc16c9115171a43090617f5a071da98a323ba0b5163d10319034af2686bfdb01a35a262f8c3a7e0e1b212d6048178e36008475ee3407c79950e00301b24e8345163aecd9883600408e5947b0c1b8438e57fd01007f8001e6f048b8bcad610357d49da4bdc7bcf2298b910730d7fee46b8166413d31d9b485106798073d679aab24d01616a50565a7dd28d6412484b5313af42704d1ee4552004ce2f921c5ad44effe56951f100775715a093a953f7e7792ec8dd3ce10c0e20050c6881b1f684fb5f14959b00209fe69c93e188e2c9db3b101f1d3b8d6663cd098c3e554e603885820a32eacc4debc766d6282c1d0a60b8b336076fc13f76870140562173344ef55b7835b3fb45e60cccd443d28180c25caf48cb89945cdaa70636fcc67100bb11d1f4045148a997b37ad214c074167bae0bdade5c36dc27570cd17e87b95e7bbe952463cf3504e87cb5311c74b68ec5f6fe6ea4ddc50e7ba006bf127a24d9968bc448c9cdd7e638b03ea38898d02900dc1844f3e551b83cde093d5f2f42f4c9526174ce16175f1d05e15293a573cc10871db7068d30769c1004af4f1ff7a629cd908ab64d5cb1b72ad329f9ff7600692036f9d4554155496006599f075f8f671a345ecd09b924448d473ba0fcd026e2b8c531cd92dc70879808b3dbd94f817d0b6e41cde706d00c2ed6e57bb26a967f07693eb2352ac635bc0466e3f73cd23cb580ae0e733a5ae2990f846bffb8519748173cc55e858da19c0cb8c53438384ab81e1e097ace4c848b9c1d53709f6f90f73208bed96644e7f40c8a9c2c7b2a529cef3adf22121e46344bad8212058ebd961413908cfbc0692d0ea6bf848747770da5a58f2f38139f8f3ecbc3d15cd6733dd1af3420d995b811400b09fa1746c52d52765f7cc5fce72a435b90c39c6fdda70d93ae7f4cf4ce91038b1d69486ce0796fe166d0103c4b4e1bb13eefa674c9fc790ef5d3c713d5145db97a903ba287f4da46c61990c83160616df2b054cc9248aab1e9d585e240df0879d8d3b3c4b28c6fbf049f0bbea3e96267e79bfdc5f8978b1f400999479eb504168ca344364ac7dc40763273389af4c7f6a158585a2da69698ea02572f96d70c3077f2e74cf2ea5e6b65ebed502e70e9b71374ff32426340758aaa09146ffe0c7244ddcb67fe28d3d31f270d1157365f4640e346970455f2833ef2f31eced9082bda16cf0fd6d742ce019bcd8f437cec2a156d59db6cce3550fef4e62f03770eeefd00ca6d956ba496955f97f0ee92e9726430eb1beab27e56daba4a59669cb0456896e7fd0f0e77e89842b400551e4264b4e05afafeff2f410b3616e49726061731d798b2138f367dd62460afd3721ee4d1bf25ffc7755e5e917fe912e8f003bd8cf037d4baa37464800ecd35a018d3c682bd6e124a0ef5d2092273f3aabf0a5a8e084175853e6fca85ca675a77634702f6b99c20a4287c610619580e101903 false +check_ring_signature 72324531aea91f66d98d67ad5271467675949ddee86518a2bb44465f09db3c18 59db82a4919d643979d86ce1784af71fc66bcec65cfcbbff7e38c89db0939ede 12 c6edf18e681f941715b1f2c19dd8b902273590b9f6c74dbf97c214f31a6f1e37 aace334049adddf072bfc6449978983baa9ecbb0b19cb5b393c3e6063cce2b89 4b0a217c62076da0431cbeca839d2524c9c952641bc0bbf23348c360a9479a15 1ca80e12c5aac964e64ca05fb9cd585a96994ba721946a189ff8122e54fd0ec8 b571270fab5c753439e570e79e08003612224ac2cf59cb606aca5e88bd661fbd 4205d8366c48e1460420235806298dad84fa1e07d0181598254eec21f7b2d5aa 8c0ecef32b3719e5656c9072bf5073f30569ea9de822493d99d304772d2c09c7 503afd360d797451283ccb15f9bcbe4cca24a6a19aa11581dee2c387a2bc54b1 f39c967356e6bb1be4206771f99c6c52243d954f11873619f69d66f7d0027afc fe845e66529afbc527174f35320fe4b03598f44bf8e252cb1cc05062514b49dc b29233f0467ffbe755200d1b752d96e3d4db0def6ef44ffe469dc3e7a3c489fc 82a726b3a16e0ebfcc8579df810cdceb6feca17bf6adb6f16d9347234c6ad8aa 7f5fadbbe9507d19ce262609c5e2a17d880b1b900fe2dc67b3465e950a7f4e0e412395fbde66e1fd2592d9bd1a1f7cd69551d9019445e0a1ab1695bb102fe8c58beab0a0b430f591149647dcf33e181b4db86f1b1ee85c38f1ac64d4a8e0040e75b12cc6b6073abe558bf258b0fdbd9f6b742231e5ff8b9b7b2e6836b7c8590e0074fae10e27084795b9f33bd24f8d579557b4f22cbcc281d2d2da9efe9d9b0904594ac58775f30108ada443635975971653df0e098cf66a8c14949155c2c6042d6b1338d87d35ce70146fa502039852b533ee62e595cad692de5049be26db06093e23fac9910743a9499dc66170f48747afa7534c93b32cebd96bac4c514906ec158fabeb0f974319b651858497d0089b4b1918521dcfcad5fca484d3ec2a0cdab7f71e4cde578eac6195241a132d49b86394c428f1d7d2a0d47626f1f4eb0424dd8bf50e0c645ca05183bf140dfc22592d14c98a223f1430e7ff1280bf890ebd9d7fb0762fd08ec582fc78c5bb080894b98198dd1bde4849a76fac476a66095f0435800077409ff3d793e4eae144411bc839b5f6683051b792d498b291dc027ccf6e7e6eff22e93f4e4b200c68590935e5d44b6d75c6b7d3448a3c6f4918057055e945f8313f4ff5f63ca1345868fbfff44e1b182ae2379170af2e3170390326aeebcfe10e161adaf5cb1bb29740bf1ddf1e6de97eecdc9181f59b662c4606db5d4c0e3781b2d20db8601ed7f83503eda66659fd7b87c679e56b03aba37c0db37c4014c44eb98b676ab24082cecefec0e7f049c2b4ce709b4f3bb615ce7c0cc306ba89ad8a0a488750ef301fdb37c2e46bca8267f2fadbf6cede4ffa4d940d2e1fe546cdea43811413ac97bca9454aea9b49a4a8c690c9178b203608688f05f50f44b72b7c286fd351b2b8af1c67fa15d2f9e7e2b4342d12d5f8f9f50ed80a390cdbc506e5771f48d5f9a21622938f15a87e71a58fcc1a105795aecca937094279859f751452c009a5c177aab76b387c03fc99b84b7a3062f6c7173399d006655803fb538171a9bdd9f8d551e89c7d81c3982245b7b99f3006550d3d08b500 false +check_ring_signature e8542a2b1eac120e2fe1c4274acd90e8069eaa5bdac941644012d0ec60d91f7b 93c4a23dca50c91b08449f423fae28344e6ce415cf1eba41f2c210b5e56c13a2 71 00d52665c90d2b437a550159f1d4c385ffd0603eb9ae72fccc8152afa19fa534 5d4d0748420e475421b7a45203045abbc6edac9014549e58d206a726474a67cc 44fd0994cea953ea9b58a9675c8c9eced64bf3d23eaed5da0c25b0697163f813 e3413ef30966bc82bdafe3d01c79585c88fbf10bdbb1b810222f6971701d7827 bfd0dc666c699f2203a58227e27c2a298d230c7f467e11e5020636453c88d0a7 e7a38c133409178bc29cd493d111b9d85a1a7e3e72a4dc5813d5fb13a378c828 84be9766884feb6b638a8db636c6f22266520bb953c665a8abcb7008dc9d6cd4 ef438db86a89738dd0baca568464b8f651592df010a926b86ae2058911f702de 3454d0b8e3a35b38ac497680036b5a2825e83c149114067ce23b04808b0f5b57 d9e9e1b262bb54317d11fc707e200dbadbb44344326a876f54c5f28ff62ccf93 d8378b0b7ebb71b00a6c89020a8e4acc895572a3729bc17afce67d7f00645459 ef55fea6617813dbcaee6d9a021d8c2862688b4c380140805d2be804add693ba 854e9027351ca40f6323d238bac1edb2d5455fe579aa1bf62cce29e6dded41e0 9a0d7dd329700ca9488372f5d3f00fe68229e6dd19d73226f879b5e8084de27e 4d3f1c0472bae73a1729d2db40e19b8efa2ef49be3a9018edef77137c8aa2a7c b10e42880e936d34f320c89c1e0a7a1f92fbf2f4bf1b75c83997846195d01452 87edc9d458523355c49ff7a511084b9b55d6a6a10e0511270b811518afc91b8b d0df067ff8312384c02d41f36f03d3acf8ff259ad510c344fe83d68637225046 8c5fc4b7331a1b795c57ef13f3062001bbf1612e4930344bf5de8b6f9433bb96 a022aa5a7efe06a4b304f788c2a4e12e3da8d7914624ad4ee82e91909c8483e6 819d4830283a181fee84f10c2294749a5ecb2a86b28371dd676d32ffa478f57f d666cc36c0792ad395e51dd94413bd6b549dd7d0b94d3656be9db6ae777b5584 31229222e844a5c6b3c738ff06b2aba7e90b097d655413f5fc15c7d748a47663 7f734e7392e601aa48739e854bc10c94d5779eda496961919b46b28141c4561f e5df9c7d33c45d7eeec897a4feca99526277c370e491ae8fea0e0d2df8a6678a 092d8ef468a20ef622cbe26e1eeba0a11ddbf86ee7ab1a9754cc3bfc49065792 3d6bdc3fc68c0eddb53f648f747c75f427da5ee6c46727b10d1a18b81b54644a 87e136fa9887404548e293f62861fc06ab05067738aa16ee1689084fb801cba5 5e42f05d277440fcaadd4d2d15f0bfc1d10fef97c52125e2a71760b34f606ea1 0c945692e3d956fb253dba6398552539bbdfe3393ee22dcfeb10b4408372d61a 1346153e3ecda4694dc98173cd7f1cba7466410d796899eaac88be46737198dd 9e9333a7e314030aec508bf7d43f485c42188d631c4c25a908f893aaf5c81380 4d8afefa52891263ddfd5958edb867741ad1f776f58c1d5be80ae48273819e92 819725a98b914250a62b08444a1f808576432f503ebddd16b9f77198e48af779 1097b0cea92a48739475de0a2c96728ba632ef537722a2e45eb37500871ffa65 1de66bd67a1c861b5111126328c22133e811e337f2c2934ae87fd85800d4d1d5 6fcb79d4af59229902dd607ee419d7215d50fa69012e3dc7e713e8307506adfe 7bf40703eb68cfe5c831050648feeca551ebf9930ff0e7d589e0c6b8e426eac8 bdff63577fcd5918ac67e2a79c1363a2838e4be29482cbd72766ff66d0bb204b 4129861f0c3026731ca9cf8e453868c982ea1fbdc9d0ac6589bcd7fa3bf11090 ccea34b2da48775fdb50d9c082e81da43c2ff896805ea98a9ea518f330bb188d da03bf3bb815fe481394b02bd12e5eb61379c06a2fff7fd60723a2d30886cc45 a65ce19b2a8d2ee411aa68eeb3574948d40005e285d1c640fd5adf90c7e74f45 5f4c09224191d28d47cd3faabb24571212b7dece4b3387a95e1a07eabcfad136 c5218718e83b111814be4325d95784545f864a3fd1108ceda3e9f971f4fb6d92 5388c2053e4b2323e23e7763ade24ffcb377192e81b69264ac0ac02bb0ca9799 ff43c1527af17a40cffc521b689810695b77acbabfdf0d8e77ea5c6602a7dc13 e900922258cd550acfb2f34ff15d5ed29c143cbc61f2f0038ea4b71de1545179 75b43df06486282d6b53a2dcbd675b4356d962493bf42e4855a227a27dec6d40 5ebfefb33224544380143a73245c35d56f5f9fdcaf3bb5826fa53e556b46fc4a 587fdbfb01aaa74e05d4701020c7ac2fde3cd2312c3f258d810c4010e67fc31b 2f58756ca8eb4b19082017defd294fdb32c14583191f4ff508e4375bd215c1fb 385dbc22a2beeb57dfcdb02df2c5e8e03bfe6a1161e2362a6888c749b5c7c327 a6370ab5aa8f02502d9c9e57445d1cb4a45c0aed4c8850fc12b3e35b512796e7 f34558f67582eb03ccac8900af32b9c07d52c66c48c4c7738fc2f7edbec09915 ace1d76361ac541f8a983a2b75ba9b67de692f13341b20c59af59b43e02ec446 21dc8e7cbfa32b7cd311ecc14b973503b6146e7929c1d73f88a87284899047c3 436db494cd4132a44716fca1ade9c061f2124f19db620815beace172f15b5fb8 edbb3f3b39826a9f92ec33bd6efe903d17759d83a4db5092a7ad07ad56b2a07c db9c4e474031f017f5b618973d63f4461337166897a7249c94dc04d771ce0e67 1387dfe3699ea5e7ad7e79d13dadea746aaff998d5de22f12032bcd0a480fd02 4bd20dd49ecfef9117cac3ae9a4a63b122582355eabeb07ddbe4cd7950dc4261 636ee094386673e9eaf3ef2d86b8b4c5c57e36d9e519ad120db53e1bec6efbe2 35b481477c8190bfadc0c571c10785ee9465f0e4c2f65f444602cbbef16055a4 29ed838309fc66da40de05fb224421b5ff66ad2b923f77ec517dd2a708379d26 3efe0b2b146ba110d81f5babafe30580e0ca2e67ee26df26fcc3601eaeb077fb 7c3870075a02c2ab96e3f3274737b19ed66a05132d42f8ceefe3ca657a1a183f d3fc237e06039d944ead7703ac56d128dcae31e0924e78570d1dde16931a139b 406fbfbeeb063e40c7a6413460b56132a8870c61a6b772d2878e89178ab001c4 1ae2ce20911d24e6f001ce50fda2e8ea48d01ffab9554e220fc0e075fef6d55c 9fcf4e47b97341010a967ced29ba4ac3c0e53b31e8d88e338114a5631f29745d be5da739fe0db571e26e8a621c8b559858ae946e0ad3d181aa236e7618332702b8dd9835be81b5ff5e094ffce829ab6c796f78ee02cc5249e44e5a8696e3bf0157b45514eea90a9cbff01f1dfe52cc25fb1eaf94dae16797e7716757de07ca0a6c57eef145d5f74409dd42dbbd8a3fe6c778b2622977058d44443965a2dbac097bbef1b7525168f77cb472834f350d6e481d7905c269ccaebfea25127a2eae0b3511c0851186edac211a2ba410b631979da79c837fa1568d032a6e78564f7b064a40ad9e573669e420d7870b8dd8191b5bcb1bd8072fda44586197b877387d087fbf4cc437bf692863cafb0d24cee14fe330ecd076f7bfbdf8bfd177768a9801a45a5ea8cea275e85fe27acf49b003ee2fa95bce02cc24e0ace5acacc0b3b90add7b42f6fda5d830564cfbfe19430d3ab08d0d1698f71b78abfcdf3495ca2809f26263dcbf9ef59f1f49b202c14059a9aa9b63c23de9e9f305f8f0100bbb29034c3c5f2c4802be36ca332fcb2d35bf109840c94349b0f04353af4644beaa6301c990616e803421ce36d9bf0fd1b98aaca5d0850e55c55ddd171f9be63c51b90240ec11c974bc3fa97dd37ffed08c37ee45920091b3fec17e9f18144432a36304000e037083870293a1f5f4207ccca9eb059c20434d82a51fafbabb87d3375f08751d9e89a6a901a1011a87abea28481c141d94eec8e84e601fd8a5f669db1203b1a0fd96918fdfa4f2a84872d993e9ef620058dbd6623acf6e4831d9f97a5609822b0b3e78e46eb7757796302c43fccd1141c2ba4393bb2a1fd646d70d75860952b7ff63b33e1dbeedd000b83ef2e7d8c84ed2e01a3a5b86322609d0081e8b08269c20af15cdb8167fa8fe7f8fe9efca01bafd0421a57e8cceb5d7a96b03d80518a4df87bf2a62ad4b9f99a35524ccc2f14a0cc3a7a5e0048f0efdc3db15d506e8a944716bf4930cc191a8a6aa5176d10389d0309c7d7287c29179baab508001ca4c1d0af7b87d4c8f41c20702b020c60df202266f90509dd8bb01bf08117809f9c23c0fc6a0c639ac4253584627d2bf905548534fa3f0cf16228dd51734f20c38ae0770097f9f7edc4e543868273d9323f28109edd50d3da65e1ba33df5f00c88684260d51701bcb9b94749167edd7aa453e7204b0d2d731ab44256812b2e0a8aafa9548e70f98b7c5245a96e1186ecf70d65883c48f2f15fdb61076c659d08c630880787a5ee14e0c5050ec819dcc136636e56a4fa3eed5ad1aaaef3f3d50a29e0481281780eaada5d509ba1e164026d031a25ca431f437a73ee3acbb91f0fa04e77789b8335c53a4d322b2851cfc1e32b91d2946984efc499bdf76c237502e30fdd770f89fe93c4fe5d3f592d57a1d3bae4336c2e04975287e352db181d03fba9b2515173c56d96adaa6e478434717d2ebd516898a4d7a89bd8a1188e5300544e6a1ffc0b180cbbfc8ed4d727ab7b7aff0e99c97c2dd114b33b952998d602d5b407c469bb8d45256323f31494131e2fa3bf922a64864a6aaf70f9e3f94208400c5ffbfb3b272d9a6b8ae2d95c12593b9855d6948ff4f53c237119f9d19d09ddf959c490f0b73b6c6ceb5dd9d9d66ad7470c4448cfc36a1745bc2d382a9205da741465f065aea93bc8b016be437bf3392400a3d2333ef0d33169c1a229000fb925443624919283bc70860949c339b170485ea0422b49c765fe8e7def4c460d18ed929fcdcf5d66ab6c21bbdef739c75cd2100e383d4a7f1e92f9243c70fd093a736c10a00393a8d74b6f9d1261a44aa8d7f7d061a166c5e8e9784e2b6c860626029f3dd4266d17b56f42b1f94f5c5f497828f269dc297e86d5c53cf1d39c02f2a397fef63290feba1769295273addd5279b778191886382ccc575c0f1ab50a5f053dd9be91bc30b460dd115b3d761ad8b52ceef2c048a414706796d37eed0fcfc2e20402a489af7a3e4bd74c2b398de9bf34fafe45e0bd1a23a4ca5340ee06c9562c91fca3c15fa55b5403900dd82860cdf3e7cf59fcf767b2604c14fe4b0f23e9be42980132282cb90ce49d10641984f9b16687f4cd4df3960a6f98ac580b246bd1ec80b7711e0c1f82443506d849f6256934857fe8c1f8529081fbf5db0648c9963312f1f4bce924ff01f747389fdab6e54ae17a98530c679f0b5288880bbac574687b0ba13974875dccbcc075b786ab8db947ea758ef647ae6a6f6ae30230ac0cccc559bf69cf4ca245ad9d4348e3dd3baf6eeb91b5254d27e4b7441507ff33e23b0f7845fa72722ed10b0d1ef9fd447f2ef2b98d3548fac6015744da09daf4a9bcd686bd132546d03c59d4c4e175c57ba95e645a5417940314d160bb0c230343041aa616bd717253b322ebfd480d0b3f3075951594758af59c9754980bd5a4cb714edb32fd3cf7720bee622f237facf3f454e70541ded343356045040e92deaad40635eb878d1f1f7111d832a868c73b07f28ea7ff5d287c015f542503f937ab75bab970ee38305188a6170295f7fa996ffd1653bde3aea19a02a8780e3908c5553280e146ebb5abd80b05e700ce8b2625c7b86ac6d3c60988c94de30369c4d7fcf22fb54a76d9cf56082c7db9f0cddc13f6db862e9f7a089350ec2e0785dab243f001c98f8b76970b858d699d0d3e22b56cd125384b1a7427bbd7540e50a17700b7e48be983eb28b83a82008279f75a274ae29792e7b43e6478036a0ab1302017403fd39ed02cbae1aa2950b53064337541543d180529104aaf56c206fef95d0bf5758af27cac141c7cf7712e7025efc2010f6bf47f071e49d6498b09d64ffc52ef14f00667be249b0108df833fa7c6f0d43ae9135e21721f90926b090ecec670fca3359ba8cad3db93321ba8fc5d1bde3f136178a4155de4f5a29d09eeb4e2d790f44a892d5d616a99f6b7a14b367872d4085a12ab54058f15009f03dc87a62cc9225826fadc1d09dd56b8acd7778dd85e0beba9cf916c420f5a400e898ee3c3aaf51c6f0d382039dd6eb202e9b905a56192a40aa6612eaa82dadc01fdc0205021f1e9ccf80c054f2fc5fd5c8b7a6a30c63fbd7a9673b2aff542c10e70002e1ae255dd446e69cdfffd8c49c204e58941a92fefcf667986749068e903eebd481c12df5be7403744ef6e212db5469e44c8e0260425ddc98578eb3b8308da3459501d7c2b8c46a29f4437a5dc6cfc46c321dd43a5d638114e121bc1180449836f4eebb2a43efbfa44a5f3de5ac9e1ca97affe04b3c3639f13182d41ca007a44a669dd81e79492c751158163762d8ea14d7def9b21250f74d2956b5ca3026d32de835d04ef69aa8665fb6b52b750c8158e28240b3cb4e8d875f9cec8b10999562cbb0fece741d487776765714cf5776c63107896330d3380f519f1f6c00671cc0200267f4e37bbfddbff2b8ac795e48ac271d02b598798b907b89fd126086aeeab7459c10f2d8866ae8bf6dbab30fb64770418369dc1a5639a999f778a016fb7ce9cb782fb44bf5842318c8dbbc18fb6352a9e24fdabaa4527ca0d354d022a2825a0ffdc66312e2bf81af8086c5f061087845ee2500407ee553c09894309b34dfdf8a4b33448500b837f122745c3bd7931cb9ebe20cd921b5ede10fd540f73f99ffe0189c4f40b4a1eea9676e1ffd5628bdcf09ee975ffe13101423bc106bfd92b7b223c2ca62bb5b23a010e7dcd62842eee0566b8d424cff811b5281f05a679b940a9ac01a1e25d31650a600e3201d806eba00028f88ceb07b3ee4a570f5c9fa2740bcd716b17fac4be53e086ce319788f2a13339ed7b589a99b7ca19054db90c0c89935203ad291fb9573a7d943d01ca006fec1f1dd3b81bde871020094796ec7b0bfec1e116a5bee73c18d4d43a55ec6271d64a2e8f075240d1e9c60111aa34faaa91e51a1eeb361096d233ec7757a2f495006a67d7c26ba583d4010647db67af58721e544f669cdaea6756e35e9c332aaabd279f9cc25a6c46deb109736cf185173aa66f8d60f34c0ec436d2cd559703fb93da0483d0676ecff0f1015ab15b33b8382c0f17f2fbd9c448ace175a4df135c53ec441512ea6ef6c4900c1e6cef74e7fcc3f3269a51173b25f11df6c9bcfdd99088e8c53321fac4155403347b2137a046f0a40bb4ff9c8a311d521abb234c716cce06962008aef84f9a04a586411b51da31d93e3bd7b06cd8e0ab9deb8079fbd8e46a66f26fc7a5555b0468149fc7596f584e67ec514fff61d8d3e1cdcad5b61985e24ad9748b349f58032c2279986e7ffbf6e92d09d7bd9ee9d108a8f433ceb5eea0f8e54a5a89871903fed1e99925999ab246aa86dc4bc3b4af27bd8d1bfcb1e15abcbda5a014922801acd87f22f5bc8106d421d4018fc786d78e0e0f758ee52cf3916843f712f74d067a13966171c6763be7833e76692ec7781c23c3b0b9ed58e5127f4de62750830a576f3615295428aea612b2ea797bfea7b71331b13cb2f559ddb8b5d60354f8020a9c5c54b29378bdc1aee10a25b983f02ec067f118f0f111a1f6662be31be80089586ee45415a6943aead983bed98f328b660264fddd7e06fbc713115a0d480ac6bad621aca2cf694f6d4be531921ad81a7a51be765f6d118f0e830a95f35c01ded192f37fbaa9e7a2167b528c14c17d1f0581c49c3f91fdee3c1469c8b549061bd76bba93b7b292f4d25e37d435120d389df40abd374fe2e897dc8b3462ae098d4f6dcaf25cce204a19fb737c3153c792b9ec38f32ef4bbc492a67d4e51600bc5698739edc9913271e3dde26950934dae41e890b1ecf4ed619c404b599ed507efb03d8d14e695191cda8bfd33afb227b47e20f660c5d1c1fe66191b7828230e57af5db4ff92b78cfc345554cee7759d4e8bf40e4f5c3ec5dd82b05af7dfb90ba43601da20dbab8d3f313328325dff32bfbe93da581c68e7c639c059ee7e3f0e15a43e2925152e78b34ef5df1c1d11e1f1b6506ec157bbaca738bcf4e41cdc09319036b3becebc9cf7206b8436778f887261ba3e9fb73445f9567a729008470e950a2a8139af9e0d757697b724deb0c1bc963dd0f97ccace7a0c696bd041f40adf1ac7b9a2d80cc00f7e39a94f954e552b6c583f633096ca157014c3aea40702f0d60b669e3ac14cbaf72f821c3a1e90782663e46d3727dda67becf77a32090de645d79ee475c7cf598618e97755ccbb52add7be299a88b55d2cafe6005a9007bf6082b821876c77b4ef8057d543479318360d5315c8d3490211c9f4f6c8da0d80c9ea520e9e648dbe33f226a68cee3db31382a838a03e86f74c6982d5563b0e985e12cbcfb99397b5141bbc904cfbb681e1f799964348c546d442ee0af5a80ee20c64fae6cecdfa947b3101620e0b04e607e4c352424bdafe2a196d31462c0a8f24b0a1fcd4a7755e821e5db9c3cbff47620c8c8a76d9edb636bb3c751b350067101f21444f876449b9aa14c64de4078a8e3a057e51d698607cef548466740a323ac46a6af41c8af541f93fbe4e0865e51ce82573e68a86c2c08a09346b0102762019ac0fa3281644a55c0829097e7689b88a96681fd2f25bd098a1c0be5607a17e3ec77906f71364362d1c1a12c15bc34cec20f66395f597ddf50350834b02d4642c799e1302772d8b8e025da614acb0d9f3ec5f670a367f3bfc73f40d0c0505c84ad6bce366b2fa4f60519f71b7f8987607600d98794966752c3c7f5a1c0e702c364f49962c9adf851fd63d81374c83547054482138663215bc4acc10a805c421f21f6ca3123523c687ce3d4853cc106a3bc712b31b969a0fbb2ac1b51e04070a4cdf55ed76af60dcbaa02bb07cf3eee8f8867d6e6069dec7ad73725fba0452fcb8023b7e61c236917717bc42a3c2be68f4ac11adddaf97aa40800452640ea0d42e2bc3791b50a34c55a0e370e479dd904b62e5a657ea134c2c17bd56a7076296cae6dfada4923bdc45f51dd9bbfc18e22e3320e839e892b0ded534dd53088181a28a6b42d3ce3147c6aa2f9be7b925c021206c136d2d32afc4e9094b480be3b315e6ec8b628263f6a3f98cab6821ff7f6943208ab6424b01b3ec498d2403fcaa925b13db7fb5a096663a0989a48ba9030243b5012f04037a7db52d2eba09848cf3f97f02277e52ef23bb4d58a8eb4aa9dc7a58b4c52ee2f50c200d34f20095b1ed769e1db38851f095670e3c6911b96734e070a1c78b9988dfaad71efe09dd35e6e8c2189ed8b124dabffbe24726612937ffd68b0fd1cfba8223d252d005648a8578fecca16105c424eac58481538b19b3696da0982e0b11987a866db60f592f8a404b39c9ca23ebb2bed02ba7b26223d88ec99d9747d80a3c6070b9bd01a467b97459be309c75d4f48f1b36a36ce5058f32805bef34f6b6f7b2a5b62709aba3613967139b5034dd0a5bd8f378d76bc84eeda9b4a87a59737586f9c0210a true +check_ring_signature 2c91ab5bb5d134b678ebf6aff6b93cd41387e7d20911132ed0d0f711199463a9 e0d8a412e876d909c48aba69ed8eb1e0288fcecb4a0a22b7ee631af5612b96cf 150 f86889f2754074d21c437ec6b1080b7e88f6eb6ba7569e4bf26f0b62b719fbd4 9aa3192626f0302c3611437a24c31cf82cb3d9efbc073460a021177a6041518a 55b4eb4eb691bf3ffc31083ebc765e44fe08575f53cc1a77d5c403380d40dcb8 6c0b8d8984cfbf1cb3591a153b17ce30e4f4da293ea0f513496e950a74fe3e36 4f28159f9809dd686d6ae95f7928d3c8a3d019edc5dad4d9f942e993dd54e512 ab61571e14803c7be686526f5d8dc6ee9b5be0a8331ff4ae8ea85027657e8b24 6aa677b77c6c81ff14af9d734e4bb6e5282ee6c71e4991ee45286ba6a90f09b3 44eb54d4d6a42a87372ec68c526960057d6254a312dd46735b986c9131085031 58a71e2e84a3d892ce30bcf418344f003f564c55529a1115a5cb85b74ac7f009 4c7b09a2d5b24374880763dd80fd8de91af6ce6470a6dafd23cdcc16210755b6 8d59c366fe6023233acc792fb49eab645dbda031c6bd64958f3ffe372e51f596 986a70cf818db77e156b039092e9611fee33ddef37c0faeac12a2b20397d4a41 4002ce29cc8ecb619820d3094a915188d31ce163b03f8bfe00a13b3577783d14 0cc09f49ccb961a45882f2fe768bdb4dbd090fef872ed1867e8a2e9e0fd03870 5efe61f985169b4331a590eb36adde9ded46ba30266613d4a89d18090233c82e c16c3a3b1bba9aae6670fa1006867fd1ef9bd9bde896e24d061788ffb78e07ed ab5581414b9629f92ee9b16eab916f86e3464b87ac3e2392dd380d8485df095f 36fd025f60799284db5d502d2bdd54ee1e4319b23b45973cfbede78a7823d4b5 95b147e8ccd6a14feaec67b30961e7dee3f5b806d700b44b4baa1ffa6b45f615 a121a437c30041c07f91a1e44b54e00465f3b9d11a1feee9b060f702ab6aa20e 8bd65553b6b97e735fa8896c194b968d8094c2af2263ffe7785cfb8ab8c11bda 9345719202d3bc3b2f27074353d7f3db898639f4a49584a4a4ddb2a86da2cc78 a54d42b7540a248e2895c283726aa59cbbc25d644745e4ca248bd2357b03e913 a9ee286192054b7b32b2052d7a3ae83542a101c85922bcfa184104fbf1c3b5c1 29858c18a61761a0371382b8f32f7e431e0807a0d02a3823b3f13e1d3df29ffb 1906fa7008e5d4a873722278fddd8834f5aadabbd6c9f2a1577cf87434395b93 d7b68bd451002b421cda3b076bcde77f024b7905e3b5a415cc31a012f05fb92a 76d804f8b9272074fcbff681ee8a67ecf9f6c65930cb338f7cd15dea5af78c31 9564a304bdf2969252d5d086fd95ce80fd87f943432028d678058c48a8455ead 637e2dc9346fa1dc827e87a504be11d56993187c996e301e88ae588b9819c3b3 1ca1e0c10d17ea965fd7a3426a0937359be750244af0d1c62b382a151d917518 04c930d66adbee8553ba6b5f73fa34bc13bc3894a59735d5866af0e3a5046063 b3d5fecc1b7202de2a134c14fe6aed6d4e7dd96ad16bdf79fb7d2d4d23987452 eb467c15ac908fed9b1a713ee0816e16e61d98476633ef8db991e40105cece01 9d5e570b2b4096b4011e044ee5e14060616e1b8481a9ffc8c66d653f011a4a91 20e3fd13324ad33eb8776bdb95fd8456b5c5be6a5ec1fd99b8aa97ec885fe947 7db2af3569c283bfcdbebdb2baf9a06b8f04dc767616805212d8188b287e585e 20d30820522698b6d01a6cbc6090cc22053a565ab0671e519653fe55f327b7ca 24370b53d4f11934b94b2f1aa8f802853c2317bcc9006931a0324638fd637920 2868ae3c2ac790b8c2075c1ae8167e9d49fbbe53d2e110328b5fb685a2fb8242 12cf4288c5bcfd91888e9859b406e4c0190d6e38ee66ae080bde89a5844c5554 97f0d8e591390bb82e7cb3d75407f59258eedebf79e215ba38d578623ca1a972 6406bcd61a0e3ca95bfe37c72e38dbb09d1ebfacefda37afb216571d2b5885f5 50d6edfce45dbe6bac2c62c604d43e37614f5e352620700c8e1f490378c99e53 f88ac8d4fb200e265b56c73b4cb925e358734c9a48521c1b79d901905c82bec6 127a051e8ee619dba2060e07be97f3ab47fcf4085a5d3a559b9cd0ec4e60eb3e f6034cb504e17d245ab4315ebf508bcc422d0dc05953423311f2301d38224cc1 87c38f345faaf989754629caadfe02d3d10302d41788a55cb193156e338c6249 484533bc5e7bce71d86df3e75ec314130353b781588f58ef6de2fd7568b3c7fe 12c51d458c44c5a206cc3dc9e1ac0619dec633b43b7bd1dcb34642421bd0691e 4761e919ca7c44e6945ddef313ec722bea4a1ed32e3225652f983d4793fa5e10 67df0902c94f4fe1da26cfb196d4ea8da9e5a8f28d5233dadefc634cf0ba1f1d 143c0c6dd91495a43c217f78a9404bd41808f36b77ed5f8bfde2076d7e19520b 33df8b57b6eeaa85cfb334ba77831c3139ad5c39ebbcb919a928fad442821ef5 f17c2701a0a487fd6a5cddd1a5985353c0a2441e55fdc99dab9660c35aef788a 4d37d3f9bf4c9bbab7d9cb0c2f812a3fdc8aaa14348d80461f86c8ffdf473d2c 2ef85023b3535f5c3958b24a3d0ad716199e232cbe47429f17fe50383e37a725 980aa04300023784b09f8cd0ab1164d7f8b4840ffbb0c8c7cfae7197ba6ede37 c31958830a4eb646db945548e2c8ead7f57c1b1219df18b6c605c27514ffd0d4 7c3f8f73bfe6f839b7028541f6f6450a040042f26b5d22e0f59bd9ef7c5544c6 79cbc803f2feff607a522e2e241a3a8bb3e49c7de8757cd70d6bb22897e63073 3c5560ad2d7205b520c958fb01d95ee39d633126ff08e339799f287f78d46cc5 16e6796f621229d46495d66488f5058dd9f22d1fe33b6cb52066b368af45c324 70eec3948bac474878f33343ad3511c0eb154c923a0dde4ded7ac34f9f03ace7 c90c3cd080326739416d2bdd06961d318b514a4f7c05dd0a4250377dd0685e62 e6102c66193194be2abcbe8eb3e044ae5b1cf8c2e8150e4699935728bea1f73a cb3eea2465fce44553fd81e9bf644a95e98e3a270a6b407f62b170c70965c322 7f8cbd56f01e27e832b69a47e40be1abd496ca15993f99654cdfecdae3676fbc 73379f5ab4975ad568ec1301ba12c18bddd193fc23a40941d7712623ad330ef9 feca716dcde7b20a49a877bfcd64f20c3812ba4b1033101621839de8efc09012 e6de60461bdbde37d5e8fc35c36f8bca771143901cf8f15639e497b9af4fbbc2 7869beb97ab618affe8a03e11d9ed9c2037d4692ba929dad3967cde19742a2e9 8e6ca51e144b72a6ccdba2d1a933ca2009e06e79204104fddb81c052be2a1373 89e6d609fe269fe24842c8535be55d87bf293b8b429aa43c8142b57266880af5 bd1500adeef3b3b05a167b5bb734ba069b2e396aefd87c116689e838af4708ce 6aa71ff2e67ae7a54be1bc6e73acb386810b4c396212a11052b0a5110dd57cda c28c03f67a0c1c2277a9ff9e2e19bb7fd2c84e61377bdd10f3f796b0b62c9032 22c80c415f195c4b12aca928415de6229bf54e844a14454ec4d10c2b18ec352a f6f4f9b0829bc72d6c9c517d22668ae060e0221736c610b35efe28bd882b9365 d8a2434449cfb30320a762a298a898d3408ae22ffc460a0cf6b4fa1ff14bb42f 29278603a7d75ce89b820c8fe8cfa368186059e6aff31db7498a6e4b79eb214e 93b1572d5dc24ddfb016bf0540f945f14b93ab0469f726a36547d87867e289d6 c43cc631a2b1ac6074a67c6d0b60c53eda1a09f7b2583057f4ae41f57b23ffa9 edf2ea7fe1c0032eafc1bae740a6dbc1e868b55e9aba6535be76b6c7afc6e61f 99986adaf1af40793c7281c8d5a8c4525049301264363e2415c0192d05513ecc dde01fdd437edd6ec768371486d856bfe6bcbcfd36197c02147c77dc8ef70183 0d527cc419c5c77f443e8f7755ff803cc3945a0cb094f10e967f5669ae33260a dd401ff5936421cfe6d3035133bd8ac43a8a1d1cb7379fc07303d9d9284d2439 3940a6b73f05ad36ce7d3eaba2e606e4e5f01cfb4512f0d1dc7a5c1c19de90b8 d5f898e7aebe6d554ebe6bc0626f967a83cbecca91a9a526ee96a745672618cb 5f623191cdc30acb035622461987af8b0c2ccdf10c4b61135cb992b1031d6e22 b85c09ace11a7da1c138a44723c3c580c939198963edc05076bc89f172197a79 ffcba223d0b19221718379a0784b99206b726c644aedc0d9323ae532701ae3be f6c9732b39f20f286b415a026e64803d84ad92a7f1e51a250f3d0b3d6c35267f bfaaa077ac637e53e82701babb343b4eb4be688c4a3e4137a7b3704bc2a0df24 8040a6b8b15c160faf160e297b1ea87ddd94b58af2fe6eceafb95346a6696f64 84e2d7e9fecbca4db33321d59ebdba04188130832a4ebb6aff551854307ebe8b 5740d497a96452ab628e3b79c970fa4821e0f41252f9913d34d1fd9795453a05 e7586c65e54f703ac5433f7f93a9f0fc1a48bee38896debe8a933332e2bd7e75 ac8e53120f1eb91f9148a20f3b3236f4bfbb7ae0927b99994c6ca7557793a835 9211ff01be6cc3dff3c9bdc79ad4f52a45ac89d80499cf65dbfac94f8bf4014a d5c81ed64fa030cb737dc565fd3a35c6a95543b5e69dc13ed27a1bca1171ad68 aea457294cbe01c308219e9a042673d96340829f734a1949913cf24ea75804e2 1a9bbd2878aa658a97370b5cb713196a051c8cf422bf405cb35ef03c1b6c3af7 2b51acf603b4b0860513830da64606f9f072ac3c14ba09330a990f9a8d44e13c 94815615cebacbe0eefd465f5c3431bf00172283624d9fd4939c96e7c6084dbd 3b05b4ebdfc2e96faeb3cfdc4b73755afb7c383fb93bf1a8bfa6cb58b16149fd fe7621886dee4d7217b8efa7f67d7cd5fdd57498cabf8aac5f085cd1f39a6273 eeb7d66410b5f15e9c8f8e5d1ede67ce3a22fd67a2ac0038c32cd4039483fbf5 8af06fc24578ffbdabb6c336df149eea62d959f0e71c344206a841ed6cf2994a fb8dfab85e8497c2281028814f206f0f0d37b6d3aa6603c8237ac7548b51341d 7a52e15ec53c8b54fd63f63e5012398469b7821114858b0012c842edaa9fcc4f a08b5744fe9ae209b8a5a0af3a5a92e169385ae9b994f493cf30eb7dba41cdad c8e1484f8f90ad5a893584a55b16a182236b82d2221c1d5099b4a6a0e8eb2dc7 dd80ade3e2d7bf6b7c9b9e20ddf869fd4406a69cb08460de31911c8a8d1cf64f 3d945fc06e062e227b5f9ffec890d7ee15fc7242fd3ce20bcd2fe412a940b541 7d764f4eabb9af4744fb75891f7c6fa80c828b3839a30145574c33d4f2d843d6 692b625556ac5592b98f6716f2ca5b67734f068d27898cf9b284ff3b072e4000 340af56340e0171ae6a3468c031fcd924be6fc86bb39439d973dc3a44b55e1ce ca3084c442e017fc38c6334f91ae0855cb43bbbefe219c5e17db6a01cf28a524 92c5b93c2ddc87ea595d45a991681d5a96cc3797d55356047d7a47f0a257b21d 6a24e66f95b8c93574ca7ca098848a3197f02e10fe0a057b209885b48e60934c e56d5657ab674228e0602cebcea01a7138ada65d92d400484deaa55a7908cbf0 9eaee0ba260e7b03027dd72a620877a9ad551f7e8c506af0474d61ca75d6be68 ef9e5074437f2bbf6e42934711d14f6c95e7bec5173d7d18f582348c19cc3a1c 5cc7a70657ff165c542583e4a632e05944f1729183863ef9184374d6421d353d 24305f8229db0ce7737c963109f89aee08d67746794b988585db51da4daea2c4 8a8861318b0a6f8e3288730180ca3afe448abf8d4d7b982cc2e483bd21e53733 af3ce5329b0b73f2cbffe8ec9d06f3829088f5328e94b697bc841d620295d224 a45d59b6169372dfd67a8c220c97bfc463f3d8303dcb80c865026149de228d1d 3d737df976e69b2c0b9e2b8d95db3c6c80ddf749efcbdd5e3aacef59cae37eb0 496148d217d3b2964d7b5744148dfee93c9afb4dcc46447002315afede161439 8b7f3eea35a1aeffdd561ded7b469937fadf2c64ad4b565cae54ccaaeb5ee1e5 971a59fab61a65ea822d4c6abadd1b77dfebdf73ae4cc5f7063f0ce5573fe472 08b3ec9498119d88e4300d73e4acd053d54215476955c8397125e74d0584eb43 cdef424753fab76873b8f03c1efcdb4d5bca8831dcd84a4bb36ee5ae99c7516c 77e8c4499b09c4583b8f2563f758396d357c24ac1b8ca261848600ee7e395bb1 5934e085cd54dd94334d9aca70c7789f034ff60a253e498381559e11ae08097c b343cc9f1bf9f0f888fb88786cbca53eae10c0778a8cef7793571a37e8fb8d4f 77df0ba9336c93ecb8f8092a088a40f910367e6d0225784cb207b34926eb6373 067adb23d24b0e94d15887144c65449f967889e177e8e5287b0e84d8110d82f5 fa80039935d0b0c259026d1fb0a378f7ecbbd911cc2688cd8a7763fa3f657f3a 4f31401068ea8803b000647a713178e2f14e14d100e84379bcea2c359a1a6a27 8b0936ba78008e13808be23d69382c0920988c2f79219109572b8ca11ac4ceea 84a77429054543b07d907a8e79c052b70cb732220c4783df50ea3eb993cd87d5 81dcb0bd34b33736d92951442c8c865fa3843069e70ec5e219f0dee0005812a4 3c62b71924ad21a435ee05fa3ebf00754d50b93081d4c65e48e8967d5d9b3a0f 55bb355da782d859649879a108b8e42eb94d11d5b774a46f0e90ca099941b41e 07a7f6622ca51d231d329b2624120ccab6c3c28b12d5c8beab393898940e00f5 4fee95706b77c1ae88b5a0553e07e2020c5ba8bf9b2b40dd3a04bd214cf8259d 572085fd61d98d7935f2bc4eec4db2f59ca8baa66f6ff18623a4ee01f87ee60dc6972ed59a251f3fd6c26a2d6855a73f904aba0aa8fed995c066a2d68759000d21585d9eca3aae10537f91872ce4e61584697bef68c4ab5770940adf410e650465f12b9fdd7a74237dd1e45654c1193ca6816cc8f14031acf750a4bcbe10c406016938f2d488d16517be76a533e40a97599a68f002f9ddea0968a82ccc643b07a6f563745e87ad5085788334ae396ba66ff8e51cd2260ca376790630b0a30c0ce6a335f7c3aa6ed360daf1a4ab6779a92dfdcc6e78162bd282a18c1fd403f5057fb35b0c7ebae99786cd2508d01e5bb513a6a253715f9a7b6f7df7aed0e6b7030bf61499694e601090407d138e8a9eeb045ba983f4640ea9ade5caf855434b0b8d872ff28d4b0365830ab2272acc2c70f6a6f7d38dc3972ec5f0ceb615d9480b533b3327f24767fa24e1cd10cb66ce94936c5703c5fcb38305ab00887982630f6f5fa5fbe8d55cf692d943e74646ab6399e1cc51513ed3b33b5516c91de56d05b335433f98cdf7b7d16cc26fd17ce82aa5106012bff48cead737e6c1fdac7b07c72ccce2d2a82a832e2e4a9b3fcd37af29979e2ec2c5b135a3f5afaeb6f8860ffa5b6f1bc05cafcff86ad934bef1db5cd7267b88fb709a125d35e2e01430d3041c5a3761240334760bb8a06bdad9c33bf4703ca5dcdcb35bf17788312bbd0002f4cf1c002b0f232f2e52454c0d9a074fae527de0fab34baaf36e72609042380872b6c59c28569eff32bc3b46852c1557ef5cd0cbac1081c57b5ebd34f0cb8804707ee74eb10e5591992d78cf9f1446296fe0809fd0dd431f394f89755e18440dcb6d3dbabad631cf693c2850429656a6d821c8f9176701751008138f6c54c302b60e5c12d521928e967749e2e2a5df58cc65940ced2dd3f00d49310d29deb20307429e9e46d62f37805986b4b6735fb18d5bc2d6eac6d0d4683f064827a8c30a7ad539b05eca834e996e7293bbe8eb87032cf2f762854632c8fd6d40e135f504c0534bfa0fcfa2c051424a4ea457148308d5562c65a210b494fceae50b7b0b08f3774f416f97919a31c047d1c54746432dd2b8fc9cadb1629110fea0a19bb80cc1c50a201031d963b6d42cec2c1a91885bdef1883594d37093b02e52f3d0a905c54f9133e173c2b9a45a0c933c5496accbd57fc129784560f05dc9943bd6e30c89f6f49ebdc890efddefa09344c8e477064e3fc401590ea111c8f47ee12f7b02bb971b78f33b37d9e9dfb4047e7674433991df5d1a870688ee67139e47cde20f6f3fd2d98a45da7f7cccce719b8f57257d6cfcf1de4b4fee7bb04fb34c10e30ed1e1967e72300238c727b9dec41e529062a4675c9fcbf3f03c3cefcec74c94001c8587f0dd35a094f135c6263c0d28f80d176adde2ca678cbc7aa4eb9c2cb203937af5140ea4b3de39c7585f685866a60b5c5e6f82062e2da603fb27aa90cb0901e69bc96d910e6f5aa61ba1f2f48014d9f46c37b85cd0ac182fca22abfd250ae774df6fcce5e4f84dcf1037466a1b151641b368c882a54df13a086dc3e7d70799168176872cae908217e62183089b5a55ca5feeb1575adb35bb41fa09a9f30a8c05e6a2a6028b7165d6b552d33a5406619074410ca5a0040bd6430b8c15a90701fea37c7fcbbac902f018d429bdbd96360d8a4c01ecaf3b160394d462797b0c5cbd7d64390dff74765dcec309eb2f5db16f958d77a8977f393692145191d20fb17edd0de293a63e19f6c1dfa40dd5c5ff3c0c169d709fed13662d48d80d800554fb986a47f7bf669c10a06a1e7f0c45989578f2ded5e7de527b4c16b0af8e0af81906f2859c255405fe6cf3b4b86688b21a1ad872e3b218f4fcc7bc799b0507ec152759228846df2e88ee31288d1ba99f010c21c4d8f9c63e27c42c9ea15a0a8a4804fd30403c4d2fbb49989f8374358c16bb3277d63550d4842a6d96c4cd0a4a25e94a354fdf4c140fc5ba040dccaefbcde770447a4898a7f45973ea2783030c2e74c9892ffd53ecce16b26f60ab4fd07caa84a8851888923db42d33ea1f04739c690e0fffaaf8d177b4444cddf9341d47c39980bdb5d3df0994134ac0580c05861b6614a7dae5bdc61235ed64df6d4e4eead51788a8a1e47478f6eeb525072e3b8a2ecc31892e5da36aa611c64598ebd73c4bc02ef5aa84ce5bb413bd86011e6cec9be0e75104aff742eac671f1dd5e5ddc7bc56c2c42866f4a4f5b760705064ec5c85bb0300c67e56634e5206856751c2f37e568155d1317ba7d75bfb20bf60ce080b9e0f21c74e1fdfcba1609132775fc5f6e58559e193b7e0a501ccc08884710c90d534c4738e7e3e240425c736adc25f1d891981303c3de904432b40b2ca48c95dde51bd9fff0671cc52b71e6cc8f137e7ec0a90ff95c55a0b4fc280d1646bc3d40ca6ae8d23d0e2e1416354f3f178c4eedf0c8c9be2ed0ff8e5965046c3ed1cdf797576ecba385f4563d1add62dc5d1b25e63ced59c3d25709e0e2016e48ccc10db712d8d389afa3100b23b92d97ccb6052918b0c4e4d7867424e209f662cfecbc94cdbef1fa5f2f34fe4a9507c53886f41d3a136508d4610e17f70695985376454868515fa48261ff5e3840b13e509a880fe932f5a012aac2d6440752771a2690aa897a996503e76fb564646e83ad3f6e05d70fa79fa0a4474cea0f9c56cd852478c83ed7027ac25aafe8ec708efa1f6cd74942f7f468785ba32e0a68b493b561c379e70c8cb5bf880a265e38c199090e2d7864f41f619b679f5b0429ceeef01c7d7fa8ca06158f468c5df7f9b708f1136d62055725db444fa33c08e69f01d23aea38418be5288f63cb661bf4bdb40203d2faa1dade560ec956a40b29a572ff173abd4a8488e34fb4ea72d8cf487ce1584afd092076f42e75e8600c37f97886a09141ec5d9cba6bfc01e9ba2fc1db1aac001ad6bb1701f494bc8f0e30fedd782a83b0f86d9e696f02d284c19cb6aee4baa795896744dcee9570d70d5e259d791e505105c1529dad60a79305c425b12ac39c3cd1c9bf50d208cda60d79741ccb2184135c2204c7b90d8e49c77c83209eb661c391e64d9829dc903e01d8118e6b9f1805592db8e01130df5ad70c29f278f6c8b44f8915b2c3c635a403e34e73498dc5c567e68d56b571684a17862f0a27adc510927e4f0271c77de40ee999b4e826b728577e82e10878e2fc19845dd3543be8678a805f23f3753f980617729a07db827c4b92383d8369e688156aab2f0a55472ffeebfbb987a8718905a5f151e7cdf0c1395a98dbb50358ac8ae7e74157600815234c835d85a611db0765140a490cc51fb6c046840ce814a986a3cac44c142b7a5543823ada22ee840cb41088355396705be8c9eff39a7016fbab5081779aaf62ef002287b35c51920869a7f5114674e8f55705595e62840c23e64ab5c68fdcfa3c4a01837c1f20ab0825d69bbd2bf9ae3e75e9e17ff0f117ea6e63cb4e8a56b65ae2d2e103b5ca8073a3380bf7ab882e6c4db8599d3dde228aae3e85eb74b85514251baff513e9cc0647cfdbb9118a0a98cbd41b9c37bd6eb1411c091eaae8cb37b08d42f4f30a3b0f4c8204b15cede8075069524ec405383d6b3e2d58f24f54afa3ccbc02fef5750fe5a19a48d2e74481f045a77923bf330481d3267263347e6f0182b19934093306bbe88443bb1e5ffb558ff0503f6f47304ff56250503192aa4de2ea8d91820e02333f340dcbfc8151146b481743d087c8caf9e1addbbcb4dcc50d46ca48473f0b7b00b8ba37f554ccd09a7984c57d3fd98e7d206efb77b84666133d191fa980075b7705d225394c5a253745f6d7899d0ac6f62f70a01feae6d38ac7056f92d7012483e42e1124c8a58d089dc1a1eb4d04abfafea98468d842ef628b94d775e30ed5d549a19860f1c2cda8a41368075fec152267e16f3b6289a429a32ab01a0307bc110eee75426d99b6394ffdf90108e0435cc6cdf6b2d1520822b14e428c560f4e9b191a8b91ada8faa7f9b33122d1232ad59338afa227223e7fb8369768e209045945f89ba9642ae6897e8dd0c26bbc6925a0ef68abc2031977a60d39e00707c4fb15d4a0515d8bc5d501ed756625035be9d7a2529eff77220f3c2e72b3ab0636e5298639fc6aaf1daab0ea54640b35dfe7a008eefee21a137715357031e901999f8cc97f262f4d9a085be8ac8889cc13e3e31e19c87b15696c003b709df00a2154c1f5e3434f3a0dcbe26e5470b7354696ecc89b5fd556695454c1fecc440cfb7fc66d05fdd62f20addebf0bb9b77d700e803f4b2f87842b6d21e4e601170eade5dbfac93ee846a2057e0a86623448b2745a71857031dfd4bca6f7fcf35e0a7711658e773987e46b303804e9ea9dab32549d5c8efb12bdad6092d585d3c6060a7ab7a05cd4cc67630b9ee7998e5758c2bd036428d6db1525a6b3f05a3b080a43277fd3d023f029401ef9dbc777821090b66fdeca905918f020c6eb7766d508d58e41b135e657836e07bfc19571b3f7c0b5303986f0c930f9209b3bffcefd08cadf09b4f39f3894739354cdf40efe559379ccaa095cc91b0385eef8a3b33c084e95a0f8683d694fcc56a0e2f4f43e2a9a502d976aef15d8805f3742351e870ce2e4c964b4fcfe2a4f766c89da372cba724c876ff77ee5a00373005991726b027cc4d61162790600e3cf0e670aeb444930b3cac645f248948d78579e88010e0709d250408285f2996c00e87a0c6f65e9986f4722906280b5eb702edd3003e80d882483c377ed78d382db14c7c180bbcc112685706f17de3a429ccca211ac2307c45101d297d1ccaed8ceb2d2c3c58204d75dd295b6e9c8fd9452e9dea777f403ff25176b8c67c7f97f3c390dcab8d2ef5193e2baf56fbd68c02a36bcf4d02504770c59d79a5df641cba7b3183bdaedb5c639175ed06fcc0c39f81556393ac308cfd4f8cb6d519b9736b3857fde7ab76e1a20d5463e541f9ae8cc6cc2a398af0a9253d155561bc7b362ff23398a57fb597ee7a6a6d3057838c34097ae9cf598062cd8862edeb5976ca15efa7048ce30c612a726f7b8ac18dd688588b06d0980027d57e690ce0bb299607cb4e4858600489ea2ccb093a03b98ee4f082d7c339f02852296653562b2adaa115c99e181a96dd87eeb753b77e7e481c94e5afbd9a8010a12e27de0b8887f56981fc5690abe7fc284b8b905ab4fad7c2a37a66147fc0c11c436104165a934e52f391b8b2625894bc6733a03343dbab262613981a331000b55fdad7c98a9eac57d701936c81b17522f766e7f470a6294d560e5e95cf60fdf54746529be66273b876d3c6dc51079797e426c2a572ec91418603cd0d8060b7878b2985ea900dd51e21f8bd47d721a613d0c8e79a172d6c63aff06fde62c07c787ac3f7da3339aa15f392f3397ac4fd7610f10938aa9da4ff86d72c976c00e7e280329d5ef7a735a8973423c8f450d4995975bbce84e1a3fabd037ed27650c788dc08fcf207b2ae02ea86ba929f1be349c4deb51485d0a4acb7920b551d505cde1c896aa84882001884d28919d66fd4917d514113df6e5f42b764b1680e00b2d1a4a837079eb7ea8f817b921f002d5ca820cfc0694765b60eed02ad0ad580e3c9bc29363e7b2c3902666d39651c8318503646e8ad1a0fdeae1fe108e2b50078f1ebe6304f1fac268fcd8c66d3c4246002058503fa5419929d305680e17770b8fb43f8ea653c000d6a38cc09d388a0b7c182d011c373aa7277ba79e99740b0a2dd88804ef48948f20f502f74a70b2f948536aa8e8c0d7e459ce1c4b8fd6600984f0a2b57ea37b775dd24ae8fac0f6e42328ac88add7ad6bc9d720b1b680e3014de8a6a79e07a1673e8ee41a35b652357812a7c1cad0a485544bfdfc03eb09023e4252094119f4632e091167915559e112b3473f13332fecfb3ad06ea0053306237eaccb3a89a828be6cd557711d2b8b845cda4adb96326dc08c77119c9be40c284884b1828097f0cce89cbba3e785b3a60ce470c3b6157aa375f46c2f9c5801750c726a28a76d012f41985c1bae01a8331283bad06f1dae61c6f3999cd6d8019cb2bdb16b78cdcdeb098fc53e41f11ca78d36f698a07d17deb09260156a560a7ee3c90aebccd65a1c1faac2298e974fc5bb4fd7daaea0fd707abb50e0a64300ab5964e940c95f13301f117dae075309c99a4f575c21bc5a9bf6de5712491302039e60d6a9424e82c049a2445b5c1ba3ce4608ff1cf2f26c573ba551f204380e6301d70152283cef45338e490ce5d8d8c996531d5e8f2131bb1e060d883659092e9b623ebb37e1a2d2f9a6d7eba58004224f8eddc9cb125d840f36c73a1b4009d5ca63ec55bafcded92d841899011cf015e709e193cb3de0273f7ba3db93310745534d8728b3ddd61fe9caff88c02c62215c313b18ec106275b30d647009e20f6b4144887200c88cc3ea8b08ff64ab5ed936de685fe53ce4ae4aaa694913930751a2e7bffeb40d269a0457e01078d7c74b240dfabb163a42e3976ce72cac37056a954c25dafb4d8591665f6575f33bd98c96f3df6c9c7b00f4db3880a18c310cab5a2e41b8c88576d87c8f32a5f792dc70c4efd7725a7a8c951379cf5899820b60744eb938dc1854ddbab08fc75d4ed357157d9a3a276b8d38dd3c1d21272e083eede7b56a5acec8bb4602e865a73912f779a7f0f658b3e55a111536caf54b0c1f286691b2c50919003dc93b2ce6f3a8afb55665a878195bba8efd5dd4bc12022ca7c80a211e945a6e5bbe71c4495414c7f8f01647ba7fc08e44cf39da971c028b4febccff618d87bafa8fa665470f513e756d344311e4f17a2a9124e7154b0a8901c97c691b0378fb320616c84a0b46b570e0367c269e03af4e95ecd0239c061b7c51c3cb90b51b25e572d1523157a3d5b09908cd202ca02047bdd0ce520e0019917f006d8c224aa9b9f3eceecbf96b94a947af6426baab7f222695a788ec0159ee1a6e3365dbc0dba8f4f1f87409ed0b19f28dadc09676000f9e3be4db740bc10d412f343c8c24f53fe4ea1db90b24e633275e83653297812250a8eb1c3f0e65238e273f68cce5cab42dcef57a329cf883d4137a47937a14dc4c30738c530c3be10342b0bd8ec6ddd9e54068c46e9d79e16154128c09c67bd7f824ffad41080ed9d8e3d4d16f94b259897eb54d17a8fa2700ce12994f6bd1e85113adfcdf0a1d5828799f9638994027d7426909e123598daf862cff4e8dfc3bb465f30efa0e34c7461343f15c86a30bb0bdbc01ae97d1863fea631b28ec9038309fa58898071b3433a87f3ab2fa8fecd0b4cb4b43f7a74a942a37417a489095fe1144ab4b0c929e6c89aa2b41ac1e849abd58c3915963b1d12486550e7bf7685e053901ec0f1d40794834cf52c062bddae8c617546ab47502e3511f82abda5aa3ff9886300dcb2a20d49de1cbb6f115c83ba73f3b57937bccd8ec9dc9197fafde4807eb820f01961c9ea8e2b8a9efa949423ae44468437189383504df410a16501ac9a8320cf1aa123e39f544e1960774adebbc34be5a5e304362080d2d37db747bed64090d2121c112081a21a7ece6bd5e6d3fea4d36bb0cc4bfb9a4b006803c349f323d062df7d6b048c7a55d9c9a4de6cb9afca04f8bc2a931245639a71174477e08a90bb8c936d92abd7ab623f68cb02126da710f12f25a2fb67a2e0eb850621b1ca3017580def17b2a7618e66371066cbdd946e2208c82dae187aea6eeda7227e9430116e6dc853bc2eb3a3d61c70a3aed1898bdcbb52d7c2957fc85c5aa8156214a0b764718642217a8fc26d42e7858f7bff84b8f7e9747d683016aa2b2decf6cba04505744e3231eeb89f753c37c8c7cb389ba336ccde7047d12bedfdfd1782a9d040302466f3f733fccde251daf70dbab48ed7eb24b5bcc45cdb9b95166bac08208b0aa57810f013a3f54aa08104de37cb2225cbb5893fd26dca6ffa7bb97bae40a9759d89308acf04ca85bde615f3abc67a4cad04cefdb31a7d886042451995c0bc5c566e1efd13fd1e67893a41afbf9f1a387a20888381559f25a4bcdc79b5502cd535faf2d5ed4d40b2f150ec7dedd9092bb68ec0987635a7a2e343b12e6610c59bf7c21c185171a426d019f0effb123ae38897e4224c0b0ce145c3000add20733f30d4243179ab38953fd4dbda340c1c5999462488e6f2fa59f74cd26e2520d23a680f68295c247a9db196b3974599eac9adbc7851a8089d5563366b1614b00b01b5f2de55769d0ecded06d1919d21617856f05b76c474e69c242e3b7a0b6051aa4cb327ab196759dbfa03f4a65e62a73fd1f3447f68ea6bebecc7fb2d35a047c06279ad14b290f098bb2c83718211d717a0c189bf86f205937531b29187c06b0aabadc7f6bb74d11c772a025bab1debd0e475e7e9a9a38e1a4cc2682430b083cca9b68b202f1c1e17a5f95cc59c89663aa643ace472c863c875da21de3c10aa7e519cb7d6a4fe074a064c1d4e10b7869d1f7494740294fd18b9f5e398454083467b0354c0cbce23076863b03c919ce8c4acf8d570af57b6e2514eb33adc90bcbbd5285e14f97d338f42e42fd545dfb15c837a7e82eb7aed9808edd2b6c8404a8084959f2c1b1bfb7031bb8dbd930f1c3004c088d90f98c50c6250675984e0159f301b7ddac79069f5677424794fa5fa81cc94dec449a318cbd0e7305618d0fa28c9df29f8249df2eafa52ab2432060efcd16a0a1d556c6cfad8f7fd3328003e2c784d8e3a505ed98b2a1647be0c0d1f62368d3bc6019677f5b70e17db1ad0a20a960a750825d8acc297a69941582cab9362051680c73cedcbb614f2f05b207235c77bc7dbc35a2b46beb85db144d16a1a978d14b4bfda359a49dea4203ed016346040594f56264138d259a9694ce26b617c0831ef7c5efa7d5b1a862e81b00132c70c2d4f4605d08caaaf911e3af1eaef1bdf201ac1acc0d2092b29c5065076ea62b5fdd34f4cd5cfceced53a71a037981f44b9eb3f8d8c7a3a1c24082d20247ac20ca134a9648399f7739647da1a2979668edcab9cc303422c8cd1f095d0e159176dc540d53c2ba30a4d3d5d2e68ff1f689f8a17c7447f86ce1decd0ed5036d271680b637b0d382e7a3ba8173b9f9404507aa9e8d09e6c9a8b7805d7d250068d924ee6db70ea52110c8036157aefc85f2c3d5976b8533f6784952e81bf9060e9099880ff5eeb1396bf48383c554d38227eddeaf60d99d2a267f023b56860a24f279f8ca704f3aa7fc5b929be7c0606c87ef856bd96dcf5b11c467ce17e8039f61dfbe00af5490f541261775df080258a04243d7b64a53d6a2bee38a1a5a009b1a28c9150899342cf9e32178d26e51f37e7ae2cebd06abe99db23344f4cb0a6cb1aacc5caa63749b47b18a044cc541d48c085cade2e400b63dad4906e6e6015616804ef56464eeb068c7905b4ecdeb54b157e165dd521aa5bf837fdf29b40c67229becfac709d04e4b58c9373ae60454b063e170f27537dc2cc29728072c06b01ca8ada54390ea15060768b9016686c52fffbd0c7df403e4d6f72f10008009a93a4efde60c699e77b98778c3116548f7df11bebc28401558d679922adff703e95bd959f891fd5b046893a9d7fe593b2066cbc4e179fc1b05b43f4494001500009406bf805bd2f48a2443ca80ef6abf7b23dc67b102362bb73abc1b24c5f10d605a789a9a5dda2151b3343b16a832d76e5937c8ff5afbe5cf1e2d8ced54fc0676dd6dab69e099237a9c6616c5b58dc1f91d94de4b090646dc5669e6c6e8cc0713e5bef3c7f7af94686e26c465cdea3531a434894077237e41c18921ea45a8084b7c458397256231a9ce94a34cfad4bbb7c702a952f08bb16e36d87a26cf240ea0215d8d7f7a792f9710bb6c0b9715491713c46f5280dea6f25ee84a9ca07e040c618da33bc3e10651fe967b6c41aafd3972fd55c733d6f7a24727c4b762d10ae89e85104c94309501e7cc555047361c4a38cf972e6675326a4d37905742fc051a2cabfa4f625e92dd7a7f8e53772dcbd6488ec4c794294c9aceb109eef3300fe318f2bee88d83f490d62a72dd28a3bd359ad5893f3a3b05d35bb8df1e72bf0578cbb8794ddcf0110d01bd9486b6f9d9f23f4f486081379eb823d2e1e41e790f48f7c894fa41f1945343147a69d62ab22936d97f716ede4553bac0b1c248ea0cdecf509fc41ed53066290858a5847e6cbe1da7bad310bdc7934890eba59ffe019d5ffefcb47b0ce5ecaec335b91839e58ecb21b948c95401a38594ad0a06230d00cb05205dec5f2d73a093800b965e24c20a2b0affed5bebf2c1ebb5e8dd7704a3a403374482844a91054447cf922dd6ef47f158d8b2597539eff4a5875df90cc6040bb121fe556d3f6e6f4d6b62601e693b3d98f024e246ac521bd25c551807a8328010cfdf8980c03b76bf8b03d715a80dcf984fea67431ea96f90a3fa4905ba36b1071680b2aaf6da9610227586c131f15bef523116144a9005c0079f6d03adc32d67cc5a31357515f82abb6865485e1effa92c0d982b0e8fcb5a3956f000352061a3092b53897d1e95b5e284e2657f50e732e352a7ca27b46590cb532d0bbea6a06affe27220741a6481d2c9fa7867e19db538cd3983efc9828daf3ec40e8fe3f2f4e2b0ff4f795fb4f8e3c0a11b831402aa805605d67aea36fbb62281000ecabf8d3dde7da55d023b8a5de527344702c0ef34ed472d4ce2dcca1ceb07091773b506ce18cc3c319b9ad0a573a27fb8305a887124b1fcbad981524308cf073cdf5d7344ba286ea02c3065249ae130f9fd01e626c173bc63d20a61ae5e7c0ee6f2b00dd4a68af2ebe51f739f6e438a6757c0c1baf11d4f1ee61d77f683c80a779b0d031ec1a1baf07b3b3e30c1082f5b7ec6a860753bda037d86bf1b73b002eca509857475b7316a7e61bd15b427aec8382148bc4d2b25d73b22dd7e28330b2d39e46f8599fd774dd8e636779e1cae797a9204616eabf0486b77001ed1150de661a2b73065f6ce02ce174ba53cd79685f3b8646cf75c2531760ef19ff0040ce92b2417759b8896eb5b702a643fbfde94fac790eb8fd26f45776ba8102f0c0f7dde7c232f4e34240d2fb7035ec9bfec84265798bd66d53aa2cdebee886d7902e8f95af08217c4eaa63318472db3f306042269e9351b140705b9936c3e2d8205ac0901cf146c5f99d00ea12fb8a4b791488f3261376c1ed3579e42e0e3fe010d2d9ed23d29657559b7adca5ee256bd38ebb00e32e2704914462ade07bce4d30575b38206b4fa4da900b6bc5818628926c4382d305eeb7317679b5b39476e1306af21f49c210a9ddb7a548e79a6b5a7c68c1fd7749ae43dd823bb05c7d09c3a00aec5604f0781c41e9e05496378a7dbfb9723b3769c8df5e5e6aff195345244020f74e37ce7c51b0101bdc5f35b3ce3857cefccc9823b09d203bafe20ec703603a566990cc3c5f870919d4b4ebaaba790c65066e513e95fec021b061531f3480d9e8cf221df99f04cb09e9a216a913b8b3053493fe875f5c62e52d39a753ee101c4ee28819d21a7d68bcb60ab7bb3e22e431e4f507ef267aa9a104b67c5e6540d0759f77175918ddd46702d6d86d06e75442133145ee7fc284de24e460bb1f1060d35698a0f67509a2d47d45648f0f3fb766c1fbf620dc09e682e8d81910fdb055305997701d2c4336f5d530ba3def13801c65cffe6ff98e877606a831079400647098258999959f73da2b8ae95c0d765aa7e78ef18188a3ca1cfbde54937de0b504c0b2c51e9de6fcfa4141bcf7581e269e5fd271cebccd75e391f4b2872750050aa90c8e06aa66b737aa9530ba16e4f1e72098f693d906c7338d2c6d2076008ec01fb300ded88f0802cd349b44248277ef437f435d34d9956e0d5a8f70b75002204584207125f67cd43b133d207ff10052a4457a42438f324bf98a99647710fd992ff5c0e57c92d348c5fee0ae56a85d03e4fe19b91081e0ad8c43a1c3e2b0dcceaae2bba4cda1b80ce0f23139eba8105c24ff83feb42b4f8a2397a99a5a305b55bad60cc4bd4bed4d8a3d89e5e6d182888189c947a28062a20265c5a70bb048e1e68c53639db30b7c67faa5a39a649dde473cb0bd1253d09f87dd9c0e07e0ad8afbfb01a8eebaf24d5f0a79c32443224b428d0102171b4ebfe2651e049a10e982669da06793605afb43263dde74ac37f54888d9101d45d86367f56f1c335036e93b5358c6f58cb84185f1b733df1dbb5b9d39ee8b87bc4ab84ffe71574a40e14502749470b6085236e611914f5573448e87b5fc59d5c19a7d51bdb69697d03752e6a6f77a8d34068983f913591a735361635143f3f75bf02e41745367bd509082db27fcba1e76307bc07f2cca5ca8d4981c2dd9c22cd25f1e9cab12051c604c4242bb11ef24e465678a36829b1f5e16cc51cd3e752e07ba65bb38471cb980913a52aa8db2cdf8936e5c77105f933dc50bac1961bf4db9159b25e8475de9c0ecdc9a24637d7c9fd7c68c2082f2508cc4ef87a560b8aaf977954d64c619500090777b1534dd498f87f4374b2318950d4f121b590bc2a8c6844242e02034be90084d39583e5ed957e490cb070ef0640d6142e78ff8e2a7ce8e507efcd04f6e50f67516f15996a810bc6fd324fceb21e5356da7db980c5c7c9f07676c9f2d2260ccd6f96e1b79d40903d467bb5518a3bb7e4ae7a1d16e1fb8329fa72a3d0f4dc0abdd7ced3235927aa10159d0becbd6a4a9b89fabe414c8a6d7e444b4d4974100e1a2bd134ea81bc2e4a684c7ccd8cd0585e72c99847f89a0f5109ec209407aa042cacefdb1dbf6b14738f98a8a57db5e4632a9ccbfb4afa0b264dd170f780140d5d5ca8dd9d63580bbcda7c5cc43e651d52380326a3fb8c9ba6bd68fa02f944021a7f5f47f5ab5c03ce4741360a7a3f63ade72f3ceaab959319259b7283ee4b0f4078f9fc8524bb278a3994ff0a0aa88fbe43e12237eefacc67cd294cfe8c0700821fadb05fd3410c1391f7d142e19dd3ab692856616264510437d89a03127e0abb93c0e4ad89b2575968aebbfc0d93341637d463ff742ee6fafb5768a2633f07ddba8a4bb365975256acf98f8843416630bc43ba53d0492d72311aa5a4a6120e368e6b4cb0bdf514cc545f66158a91dc208bbe9790327654cb7b7a0f13efb90bdcde1d30081bf9956437b4e2b5fd086f5a77d9c2f4bd247efebabfebc9696d0f09b8576d51630bce470f873597ea12ebe2fd1584420ef5d5bc8da08136fa43052874232899dd8c523c922a4230fb36942cdc521720367c1b433617c35c3f390709fbc920777da1aaf59d1bbd3c0ef6e959a565ab63910bc70a4bd776c6ec230bc214471b87af944c9760194ae3aa562bae7d714caf1b92e8f5d143fcafdb5402054f105b05ed1621dbbc3712224aa550985169a23f850e8f0f6d167484029f00df48286a6907a4fb6796180522dd65fcc3f2ef3e0de8b2bf9d0832fdf9a023088c95c10387ce2e06363e3170d13f310c13763e64d52b48011f30ff0458825702 false +check_ring_signature 74cb1181ddf278dd7ff8f623dde82c0582c0f294de22320e7d2c403a0067eb20 64f0d6a306620bbddcd3066558acafe6d878c69e1f26bcf4f2a52dc45ef8ef9b 2 2f19986f3eb08714753c3505e4cdea07697435e8662a1ef42fe6332c22536688 a77919e7dab6345e78e4f69760bdbae6876c043d5dff6d07753da50e530830f7 b73974781f58acb3c08e2f1b6d6f4bde823ca08254e6c68d1235edb818ac2e035d5023df956d7fd081781e2dd54db5b3081b87f7cca1f75748d0dffe3cbb2a0370b1d2c4437d8dfdfd8ae5196d81c77e836d76099b928c141749115482350d0547ecef082990fe66bd66fefa6838fc36017f94ff80620c139d284b67cc7b8a00 false +check_ring_signature 6aab1a8372d8e3ee18242079884d73c787e8f5a3e9133f05193af2036b9f468b 858392fdeced7b7983d1d97c84c285d8bc7595ebc22eabc9cf673aaa5ece4bb5 1 6a19c6a93521aa23ca520bd04833d87d49358938b90dc6d74a518a06e2466611 f851f58951b081d5be19443e70ddd96d7030c779f9a1fece1220621c206faccfed63bad27ff5df9bf86b488607921b9ffcfb4e0f606745fe218eb1dc9620a214 false +check_ring_signature 3e1c33cdafd2fb5e6e76d4dc2ff3f6e2da09e29ffdab3e2328734da45152ab9d 80b088a27761f6073e5fac49adb7bde7de1a5419db90aac8ed8de72a203fce82 11 b31708830d0eb0796eab5caca494c00d163399de19eb6174aea2676e95aeb0e8 346457c2c3f0d1decbd797fbe871802758a94b7af43d5b15650afb6e88acd944 a0a300e6b4e14fed256ef09ac0fcda9fca52c4ce93a73c4a2465373ecf2353d4 dd4bf104f80dd3043d174e60e50423eeb6669d70128fa6cb53804ea0499273e1 b24e552fee31911eb0c95f2f58468af871cf3315218443899db47a35f5ce0355 26182644409ac5f98a912c2b69dd3edbcff06768b6b50f2c480f3f108fb02efc 61e84ca495578055abcea324cbafb02c3988bdd79c41cde4d32498a4f1c55c56 2ef4a392e85e36c73915972ff0d60a7f32975877f8708039bff751ecdee25a07 abc783ade01033aa7f5173212b61e3db1212d1fbe96125e845be85a31778d374 0b04773b32860699121137cf0832877a46889a6fa85f924e8f106829d9297378 b9755251b8e9d99cbb8b228416048fc97caeb76bfa77bc25eec1fec126aaadc2 124aa12813df3ec111cdd39f9f3317eb0ab3cb5b860786e318557a4f06c3b10ecb7b242503957b7387c24d3ffb6607227fa0d6ecee0d32701a888ad1bd84850f60b5bf446e8fbf3a0d84a40c9098313e24a2fb844b1d42ef0445b6074c27dc0f110328f34731304006c9a81dc8422591521e36d32da16a3b34187d59d63e4a04bb76107f4877d01277662f6e94319e534a379d1658826e362c601dc3afb590001f02c0c4975042be07df5e1c74325be0376b56df2a5fed3cfd8e6ec2b357740e85cd6c59b3993ac5ab5ebee0fd7fdf308a34ff1665558008b0680ac7fb089b02f2bbf14ee1c88599b48ba41b12b614b5798f54d6df1ee7b92c6b12e759248009043d2a2dd092a324e902fe762d1668c214d0256388f1029122b4fabdd3ba4e04411c748aded9524fecbd9b1aa1a2522870098938a654fa452a32da8949de9b06b0b593bf4da419f12ba892341523d1b417ee41606d239b4635f0232bbf0d900e926ece93b6e0696cc6f60f27472e60478a40d3a2b5408655d39a785e45a7cb02e31ed0dad552687c91fe046def2fc1cdcde10f07442e0db8d349a79222986a09144fee8eb150a7d628c0da12a00ce09b6dd33323ee9f1c5f65ad73ef428081de99247d32fc537fedd3a193ba3009781fda362e68608f50a508fdef616d371505023c39db3790dcfd8471d9186b0d71209f4e0d7a25ddd0360910d5b977cdd70f517ecbe740aef7fc72c472d90517166a52226759df6282aeab1f3895a39ae40c893a167315fef1b48595472e1c3a994c588e80317fd23f76bb5760ddec7a1109e9d4632d496a28673a98eb699d20671e101748c9a46f14545444c99ccc247601272f1684f26b7c92d4faadc391d3557d3a7d2f0e42cea967b1a0a2e2eade9605ee947db404b9ed3c5ec2dcf4d93277d7d30f30bf799328864b0741c3bd071f0ca133a2f934c0483e0142e43e330f7ace8146cebba7c5c1c679bf1dd999bab509 false +check_ring_signature 5cbf8ffa6f95ffc158103ef98b7793f0cfe7ca615adc1c246d807bf1b8c4ab01 1db8ed273a7139052b7aee7c5b695bbad1bc9bc7a4b5b8e5671e5ef06fdea352 13 b9e9348130d20a4eec26c4da4c7104c72dd23a69fba7dd626d8e0a093433319b fafe03d04c3d6b5eeb41d76b1a7145587bbc1c54fce6eb89f2c4f8a3e4a2fae0 f4df32e24a0e9f565e8878026d24d1e83dfe5728107aabf0bd957691b7a85921 cd3b698e6265f55f416a22ffa452f3a5fa29a6caca0b226d3a68d05fb37d8136 e0a055a2399c4555062d659e3b6b9981a4892a54dc0ecb806bd3b4014a181547 12af7c18056fa2f88ec442803c46f71fc7cadca5a81e24f5667d9bb4c678d279 4923b303040edc35cc0ad5d78922dacd345aab974df27242e441a40df90e2741 726b77a358628d4dc7c00665c8cce6c3a1ea7e6830eb99cf0c778bdfb088ba3c da5af60fcb7bf9dae4106de7926dbce1fd787c15654b482600e488e5de5a376b 96c5060e79b7eb865e3e9f01ead7420a5440121dfc8e294f4056e95cb28ae4b0 8a713ef9db5ef529b42b2216a542634b754d29b4f776de3773bae10db5386061 ec45cc9d8759328fac55e79706ab2d51298eb7b6ff39857277571643bbbc2f52 7dba691e1b846534d6515311fe90b2b928f1824eaa1ee17ca6621d6c46d20aee 03ec0861647520505d9595ef822a5b7adc946d5e606c7f9659fff6b6b92f0f0d5e9f5558260c59dfcb2100033c1daf89ecfc09cc2aa880f9ef52f3a157a22c076dc0debec3601c0bff400f33c1bfd7a765157ddd570f4453f87ab50dfe4a4506a18fb4a69bcde6bb1f936bafbb46152f188d89ce95c9e59ec20efe592f3a8f0c146e97e785642668064b281d683bbb6c3a5913d87f037fc443723c48472e9f035ab949310e5eec04f1b373baef6961c315dac3f44e7786e006e4cabb6c3e26084d652c7dcc4c2916009efb79230251d358caef9f40b76d8f750427c3447be2083a2cb252250fc83496ec53fad906900c3a3474ca29ce92dbbc5b5e72e1db0809820b5c27dcb64d3e6fbb92c46a150dea70e5597af67a62741018aa33628e4902296946bf6aa76c4e201bb154303e402af989aff9724e648905cebb8e25bfda05d178628a64c52275ee7c3787867bdfaab4e7e53e38e0a9ad6bc85debb6e00108a97519f5c8641bfd8e846a2fb005abe47b0598a10fbe18b0a466a21c72f30d0cbdff38d1d7f6ff400713763f745a580da05ff2b785d2b317ca74ef61fefcee033487d32776706c00088621f9c7d09be9cc62735ff758e61a9a218966a323200c6257f7199909d7c98dbee6a9040160c32c38c87bd9285a5a61c76c4532b80e083f892ed30f34993ac00d0d441b0fadb669b8c909b1c20f7ef20a05fda032450124e68d71d0e1362edf4a83b85dcaf938b2bbae2c3688cf08c9c7b7987e246b0fff6c46a378ed26298413774b8087077b0560ee9009202841ac101f3071131103f5923d4cbeadaae7ce0c69a1327be590cae875af35b39d921cbe17195b27420cdf5a223651c3c6ec79f0686945d347f4dde4ce9f19eaeef28ef0f36ccc209102ad59f5440160713c68c9e42695cfa441e5c4fc43dd501b8dea813a7c2827ab078d6a3f738feb2411c0dcc2cc6a689a5e90ac290733fa58cdaf6fbda008a193057610606728b55d746fb57cb6f304edc57a422f267f09530b2acdb01f9e23100e60a5a545f613ecf99b477e147466de02a2bd934f371afd8c450a13c86df1ba01d73fd12921e94e19bb5eef7e15420663e8aa5055b377b634cec11ea77cf2910156f0d7b5dbc8e7d37e764694ca5556da01b7432f192b3b1697fe0d5e4ad1570e false +check_ring_signature e8d7ee6af5077e3d76bac5307c18bb38dcc0f8b885a59f68fa867643df13130c 099684e012bdc4e8a2effab5fda09f02f4f9dbaa4be3a417cc9b6c660c4fd914 2 4178f65530defccbc72bc83c4580fb5a80e7c868e230ef58362409f998800817 56f44e6a7dec6fbac2acd40306d8290e847259beb4e281f5e76df03cb7d49f43 85b4e7976c00bccad4db68c716364ef12343c14a1e2bc9528bc0c6a176a59602f5635b96f4c4cba57076b8a96d060efa834be5926717edab9b9efd7c84aab10edea9731c602e46aaec419cb8e4c115faf60675799606c7d0a99ee605b5164c0a1e3d5e3e08641e123a270cc9c75c06ce4256e584ad99f869d5e79ae33914530e true +check_ring_signature 3d4f1dc448604b9956fdfc21d9d2e40e786ba491551473c5b9ea557d00170090 49807928cea9357c801633c38b515c4e148e6f9549a553f18d1cd1590a740cde 30 d4483b337d835a253ff8dd468cd8750277347b46d54d4e9468c80c468d54c48a 47300734761bf3f560dfe63a804908a9881303cd97cde32e93d98ff754bec62f 5be5bd40e4e5dad35c425e1e06f810b3bc5a2f211db79ea01db0ed38389e1c40 a548db7f0aef41e759d274b4165f0fd2f9beb4e8250ea1c0e54260ffff06053c 6ae483fbcb2419ca7fbbb49a5b300f07806d3b50fcced60ed4c5e48a49ea540b 2d77ad1da09bd8c3c0b547f33e5b7f59080b395b31ca5fe9f4ed5347b662904b be14aa94e08063073409f518373500c2155cac3ef027e8ea57b2a604b055e3d4 27755d88720b7d3e93f541440ce8543f007b83b345becd1fd34afd6144cddede 7d3e7d18005733083d665c5e3825e6aec4b495cac84650ab505fc80f0388f031 ab714bc2d1e6b2355562b767c8e94d1609852c21ed11f21ae57a1a512a258338 472db822a01cec7c5064ee1b51bf0c45ed2e879ccf7cbe6a80a537db2554c8ed edc85a79cee27962bcee7005e4c3acead406de119aceac6afdff6ab682a46a4c 5a0ef5d3c339d32ca393f389e62dd152cd237a73f64721996b66e08c669e8495 a6a739369e746b56ba7b5ce6e635fe79b5f6740821d2894a8ab141c4f2f6b7d0 cbf2e5982120ca2fdc651fc9fd2221a11a208852315e921acd2e66c19a7bc894 22e8a6841ff77b5c685f435f5ecd0a37f328314aad0f4af161c06c7dd007cbaa 604e2dc616e9ac21f1fef38afa04f062d6e2c920a89ba44ac33bccdb535bb57e 0720d55bbe5d013245ea4fac2462b2a45a1bc8592b96d34e9ca5ea968bea3e7c 37ad4fc7925db370e866ec6ab1e99193d958e2a52a749ab6d8752f9174dfa03f f3601e7ee924116489ee0af0de751e0eadb9b7e087e55ea423e3100da07c413c 199008061026566516b4af3f34020d7f5288fcc0afec43e0e614915a4e8cc0ee f8cb1a3f72ac2f02464912589851910a2c9a80f6229da67f3f839601a848db2d 9b66daad84a28b929ffb2e2a2a3af7dc50cb047f86a66cf0abfc8bb2e445a466 d2aeff46fafbf3a2d2499d18ce20f0353797a6250c02b4b264844dd605681e4b a898da522cdc924fa2296c4f090d5aaba490e1fc2b28dbeff05a48f7d313d5bd 5dd854d258061ca00e1023b211d4b340b1d1fc4b15dc845a7949f69690886c27 ae6aba564761d3d3189090b9f02e0dbdb6b6c2f02e0df5222325862c33b4f369 506a7a3013e251e61152b7c9436504dd801e3ed13e646ac597313050e2675d4b 0862f426d6e99af1a3db457c65ce8985da57c0d362a9de19e5df4524e0e12349 8cbd7d665be0a976eff1992e31f79889180913ae5ab7504f12026590047bb644 6d3cec0e398cf89ca65cf0106cd091f7477709390239dc6e3c3ee6c67b961b0b0a824b7ebb212860705cbec073e50c1c32b319122c0562a215f2bbd9683251045f6bc6beba6fbc1e3c16cfae8f2a284b6839639d7852fb9f6415ce4249135e091c9672ac84d47698ebb16b737a8bd5dda9089109e6490429e7267f59b7161c0b80f70437f9e5be6517c9f77b0b1eb12477b76e1dcf30f20f0b1e2404a0d3d60a9fc4d29f1a3c9496885d7a251b5afa30554a245b179a59e888937d87a6d411014b24fb1ef261472fb894abb0e99412c25db55a012bcd942f021b6ff773d5b306f0eaca2af4352d9f425d166e7265e2b61c355171a36b8eb397a0259113583b08d8978e9a846a521acfe2e27a5ca55653c894a9ba7088cae5417b7b66a09d0200342bd949607bb3431993a5cf5a92659b56d80c934f3df3f73ee050e0a90f6905cdd54ace8c5ed545fcf269f645f68a6f2703990f37e802cfbae240ebe5e1ed082af1b670d695745325c6a042fedc6832f53d39c31e9e8501a0addbc7fabb160bab6d7223861d2b6f9e146b05c47920756390e6849f1916afd858fe48d16fa90f0b4b225eb4cd80b2e6f2af1b0eea79e06831d34c1fa83a714abf3c79460655009737b5b54f4b32482875c531c5876e46f81e8f91b9df4946d36bd2080695ed0f1b15ee59e8be4b982619cb732cc8a93dc16445cb03733d3f94b067ce0906f70d8a0b404f5f02542209e7b76bf7d0bf4ad918efc9bfef0b3b0b6ee3ab9929760d32c139e374f67fe0fbbccd091a00f52dff082c3858a9d5817b3d802011418f01454dd5c01355546a47d4fb13c3c88528f99686ab9c94694a2726bbfa635fde0a00e66642515af9b0c4d16e8c9f4d9d784965429991d4f3a54cfe9b58e9aa9d0700139c3690b093caf6c833224b2df0a6f2c6e49d1c01e1fb688fdf59f213fa0595ff7ec40883aa9577d3aafae23ac65b50f2e1e2fbdfeb7a35665b2f11c1c704daf5b3d58bb69df11f1fd0c79771181f557ba825c91713c6c6263b55cfe4360b11d05502120dcae5ae060da737a65cfcd247cdeb9ad96d411c9467378f6b4004bd0c4efad5a3590e66d897ac1f9d202f2d5278c6cfebe138415745415211e6034112b53df2e01607d1805ffd5aad1981247e0ccfcd7e77f0d299f10dab9e2c0f6bf39b240fb0fb26051da0be46b5690eb2b410dfab1c5a92d42033914b40550cf879095ceb72f05e1da144491bafb889cf913f30ee185c89131f3d4f4fe4da0a45342768ef2b57fc975964e8a3b2d805e39f6cbf6ceec6a8474abc94769f8e0ffe56297b3cedaa4485fa6cdaa506e7df9651d81e29df929c992a85025c1ae30248eacba08529df854792fdeb4b8988724f32974352a79e9d91d5284278393606b62240a345d22ce2f30d8f23e31e8e311e01db724f8b7dd232c25d4172a2cc012e7462fdf179db024c5d333e97823816636d0c1738ab6e634333c95a05cd8a01a1a460c65bc84d5cc9478db7aab3768bb56a3f30830197264672754cd5524d04f2b548d79cadf4e0923a11fd40cbe4f63ba47a1b95407c652ce21c5448b6e7025a0e2144bd746db50b22339c8d8aa6a7462bb731e12130bb6cd79d54004e35086593a286f013c997c8ae182b2a0a6eeea475b2f94d3846a81a10ca0057d7e503b0cefed0e4a9b3e25ed1a7e5aab1d412ba19fc14952d9074849491a4dce0f60f0748aa7d230119bb5f5ad2c75a0ad046a481416e0ae5dd1b54bcc3a27c755b02201c98bdb59948359ab2ec2afe3b5e6d4b72c23977a9292da7e13278c0bad50f82b2a5795b485e5991d1e0bdb6e6e8d76d5d9e7a1aead9f3fa862affa4c1750a3225de26440f1b1221f10b11a115c1275903adca23ff429646e687ddf4ebbd0557b46f56050133ecab7f9eb6532a2c8467e06b9ce493dc5c51bb389878026d002b4389129a2fda27341e5dc8482e94956042f30985b58a65b3d4fb9fe682ae0f5fc015afe0b9c992d0ae34b58b1d53dc7d207dd40b945ba1c25a0b0e406f6e0fcd68abe56e83e129b68ddde81d7643cc51fb3bf9cd330529ba5695b8e6901405fb8d50026902f321c52435f7e9500c2d4d8ec81077a8b732317980348e7de7029d65ebbfeddd3f7e351bb7c07b6069e20f7379eaad16016d59ff91b926498e06d5ddfb1f50b0bca1f775ac641b6b2bb2de118fffef921bb8d0d0926338484d0751ca1b56e23debfc4a179bfe0e860725c18fc27d3bf504399ccaa5599d95ec08e3ad1e13887811f257f6432729a829836d7b34754635284f639047a90c7a790b784cd062915767e6747eaf92d87a4b0e8d1cee3a2c91bccce967e2421066bf084cfa350efb15876650b9703ae8cab8d354db8f67b7a7f41ec4cafbf2cc7fb700fc886b6180daf9c68f34b93b67c7d6d0e2a3c8b7cf84509055e13288ad0d7f08139070dde1b25a989f1e75236a2009d9358a3d4a0fb5b611643940488e6da00be5959a3a83d58b5b245845c81a2f725ae9c9434049be23b52391f041b9ee850945743db07e733ea25e89af3a055e555bf90de9d6e14b84861972b6b88ecdf5099e0b82b269047ebf44344131f46bdecc666a45ba2c89cba386cca1f61907e20f6d3dbba16c36b7bcf4e01ebce0e48cba1b93659d65d39acc09f121eba6f9a40f18d1ed7a121aeef8b6759265490859a1cc93a4289b40c84b392bdb448a561009 false +check_ring_signature 5c63ca454afc55bbb9ccaf4ff2cb7d9163e21fc06e254400c50521373270743b 76055de1f66312342a06f53e0f030bc503e22f979186bf58fb42b005bec5f0da 2 e5f4d2d9cdaa80783ab3f38a6dcc9aa0561b08f93481c519cafde8ccf75b2421 0b22ea9d81f65da5e038d884a52b3a69b51d2232b93614cb0fffa69171b5e630 42ef5b1a50fd49a76085f479716521a682f3b7f193797e77c64aca6cc618380129b8a77c7df783ebfcda6df4852b32665a6f1f1fe514908f6007d3b52cf221032b4d29a23efcdbf0f07e70599d0918d4e5f8dec721334d524da9c8546573ae0d9aeb12e5e0892801d46fda2cd556f172f6d6de380da84f7e48ca57276cc78a0d true +check_ring_signature ec24300d8fde49ad853d6c41931966a9fe5072441b700690d41d81ad1ad18170 58f5b00ab9f3437d5e0b820cbb86314fcd7fc8e50b9fca1964f4e45a5f6dc447 12 104f3562a6518a02b54bacc6ae73bdceebf872f164f60bda1eab7a8ce862e300 2c3a10cce717b1b6cb88f8a40bc4a79fa9e37ec7bb8523ab1ac01b2c83976b08 db0a38ae0f920e17a58237e54b3160b25c3f67f3921bdd1c2fe3e2152dd6b902 1f126c9243d8999c1015c56e0b4dd85ded4118c586e74eddd0694a222507ebf9 2046605e3d1e564407f29c4eb5173ddfad4e8841e238ec0e79a19351b23b6214 3d7ac8874e77ef05802e01b2d41bbafd371a7dbe7ddf4c3adf49666c75386ad7 c8a55a7f1cc8fb38fca28fc24a80063acbaba7b66a445ac99f9a0e8efb365f3a 42c913778af5afd50d8011bff8027af39c5d05281608b7f2b2aeec7d511e5b2f 1960e9b8fa3666809d061bdb80ce8073651d0cff78e8168e3b4031b685fd7af1 ea63e636d7ecd448915252e5bd786d4632f37b492ea67b988acdb2ef271dead0 b83b40937c64e21a691b373f1c65db070bf0bb7bbcb23cbe005220ff7253a01b 8a06d45de21f2a08b2a0a00942c36eb83470e8f5fb185828525ad0104d5b63a2 12989e18132d683851eb7246579346ba456d5d665aa8af70c8283645ee681008cbc8a0bc281b3424417237b9db47b4ab9c6c3a7151909ec99b5e699243f70a0fc30ca0822a8c2a66344f691cd2e02c8d77a277191190aed126eddd678ff6820f9da92e8cfca1ce2d1da5e3224b4036f61a058839f7a87e491f1a264454f42507a20e8478974e1944d98e6b9c34c7b14bbd69cd88af7d2afd026e4271f6d5e4053021091b98615d0c4cf4fd257fdd873ff23840e480797f02b9e06c31d4e2f50334088296efc70a9341fd4552389037fd478258b8758cbcb8967757c53636480766b84f6371140f5063ceeee552558a8f4207009ebfc551267c08330f87d4fe0d773865efbdbf6fd359005c62126a6e8358a34cff5c7ebbf38fa7739dfc6df7097ea0cc6b4c83cbc7b61bff397735f54701278d448ec53e56051e664003238707897af3aa1220b33335b380bdda626a821a6e7c8a6ca05e4663daa9ba3da09108414fac9c63fad8b0fd0ec4a28dc49fc456dfb940c77fc9e1075f6987866945067ceb6761da90cac7431d2a2ac426a7151a03598bf05d744a27dc9e45f5892104596aa83c48a169bd23f076fc3c4849bc9fb48faa917a2d8ecdad6f751611f3028da03488fe27586c7df08a26009627074fb34c9a020e96387b36162bdbd864098523dc41af390e27f5c32783069f69debbd760d8cfa074c9e8b3aa63395c000cd4021b09896c74d5db132979e8162019d4b8ad1a877e8a34291ce7cf60514d09c79a14ab6bd3e58fa37fa61924ffc8ec9be597c5e04ec8795a7e72497b409a013cbf8f5840b0aae7a80bc052a990205109078100f4f3f6fe40908c199e39c3063657ff05bdfb3b0ba761b5d8f239a0913827fc7e254d35b48ba9aec58bdc2f010b679a82ebc4313926d707dc0a055889ab96694917bb1c603aaa119d23786f083498899dcb0a3d2bbf3c03c581662e30316aa6d8954f8d9a1f61b50f4ded090f3ab33d1f09569365caf33c888ee4f3cd3ec3a143385ae2836ee885236b773b072c3a98ebc86a498f404d892eb465a5b02e8f21891e3322380dff402da625ae05 false +check_ring_signature 1d33b02081f2092f3d4298c02226dda7842ed0d04e0e9d68277fdf8fd385f322 b1bb5c5cc2392968c59b6139549bf512048368b821f43f007b04a577d8bedd02 1 538a5eff1b57113a653fab26da8c86c9ba2cf70cac89e8c55fb8ab5857d9d1ba 5a95a956e05eda612972759b96ee8799ae6834940f1abc81e27dee2acd73f66ae90f91effa150c37c6dc4042b4b859a8a1d30e0229de710313471f65ecf641fd false +check_ring_signature 7a6d7eef158080b219cb2693d782b2d2969cad6a2dd7026709050b9f48a5d669 76e8e11d217ec5fa23a6d80d086cbd0f43d9077e0d4592ce7f9f1aba014f324b 69 c8349d8a9c039f9a8acd7b847eda29677fcf40d95453a5fbe7be751fec061b5e 6c7f7b39f4b98dd0b628120bc41f6bf247db4d7f6290c024a5d64ed9f46c3240 e62a963d0133315a5f8553e8dfeaa49836108b209f2889d4bd41c6fbf0064071 59d854d021a1edd6527b087bbb76553936cc5f1d33f86798af518f01fd70177d f8e76a1e11a764c68a09c6f8307ca7624109da74062dea3453b3ee2a04b3d8a4 5a835b863d504880945d338b9ae1f8985e4cdc49cf042e8163ad5ab70d1da35c 4114416cdfa80ffeeea0020ffcf1e958d8fde1650c909b63e662dd8449ff8cb2 bef83f249954cad3047dc3561ae27fd0f9f4ca0bfc685221a5f918e586baaa84 dbf674af164cf7a9e0abd8d89e7bc3b669460281df58737697ac8a03aebff224 998ede3f5d93225c7a0f15e8bb50d53a2217210164167279156dc287c268d6f4 4e05617d1ab230609754107dc3698bbbc8e323038dc0e1026ef5236db6d41b61 3028af6469ff813e09834d6af5dbce0df560ac4626134b04f3403f6d370295ef 83e8e033603b64bf234611d99256d31493beaaa3db1d6deb6640393ad41b7f41 19ada74e61e44a61ae5be6cac0534f80a55ee97209c8d7a93be6d3709ded3dd4 a8dfc1c80126eaf9aa7a7481d51a846bf1bce0ca7922143a9cb8457b38fc6171 601b563ca74c6907a25022e5adf7f605ba2613e7c59fb2baa308da4eefc1ed88 6ef8a68312ae39a3ff96610a2005a6719aea9eb38fb003246497e2da32c4cff6 5bf6a42707671c463c5c1e5f46a5bd9ad092bb30b8d95dc600ac5907d1014a7b a7f141595a736a43b2e1441ea61ef3f18e15d08cef4b586bc704ef712c088bf8 564a5d85949113d78a91ac0de49b5871f93f85fc57dfb4bfd6853fa9c52626dd 54d68f34bf32765e813ed62be68dc95a7c74e780f5bc8dcf914f3d8901443151 2173752326c5ca146e1aa87326934e0b643f29011ca98173df92c14fbfe065c8 c6591776c1c82b425d69cb6deb0275fddbd62d00d618daa8bc1b6f801a146d51 8fc15780bada18cdd161a7b51c006d35b3d44df47941a92ff87eb4566d7ee739 248d5dd210264b5ec4310901a3dbfd1e3758819857ed58fffdf76261c68bba5c 144fcfedd3b0633d606be4df45963e339cfd8279850a9dec828a866db1d30b4a efe79f99fd579e590382393cc8ddc8f15ca34d977fda7c1876e56915061803c7 cb0fc9a1abedd2e8ac62cb8c593b637dabcbf101daebca07ea96634a1faa10e9 22e58977a2daf6f95ce50a98a697904944d4d84613b2ff5362209045e755d411 712be4271976ef3e2416bc10c40d475ae277b07e45dd150fc4f88ea51328977e a01a2e4548533a910e0dfbfe56f0b16b040ea9dd0f61b58cc143142d31a243c8 3a48d15dcbf73c23106200f064bc38cab4038f93e06df12d27b3381d68f82fc5 ce2fbc723dde33312e09829591bb41e3ac60ea47d09128762698498d922e37f3 82d0b852ee88921d9aae470970676fa94a5e2a3f657bfc01dc9f2c76539c3b97 c48fc777484592380c034e375a96de217b8d45621d7f2c1e700195a467433989 b7e625567a1b32f88a82b48886f3c8eba7390613fcadd15bd35ada0957c72214 6ed0e451adb24e58cde45c0ec5c6453f9287acfebdc7c4d242a02eda3d4c61ae 90d3493697619d3fcde800c9c936dc35a64bca4a26606e0a963f4b0e539baef1 5dfeefb0cd39409a84cd9c50abff9091b54c51c3dbb85a348622444dfb2f1f8a 4d3ed53b5f50cc10cb3c67b68fb9ec19442d76bd64c00f8501406fddcf08ee6f ec73382e9644266a86080a03af555d06e7e3b8a30de7c20f44728dc830c893fd e20d00d37d13102d3056c6ef4b7a19e8a0a66c4f058195abc61c50e21b4e121f 297e41d3b253b2038ca614c03923bb2e266ed9f01af23e8544089993596aa1c5 7098fb39722d0c11729f20b24f8f6752d3cb25ea541396e0ae7e2302208929c5 64004a4987586315849873959ff244ac65353eaa46fba55c33fe560061f5553a feae1daeb20e79169faed2c69a37c90d4f57360ba1598637bd67a3dc201fe7fd 402bf820dcc57cfeac0c95980892a3e70da7136573e1868914a3698478dd6fd9 6a953b842c13dc12d243f4127ffb3d8d45e11fbef88927e6c5c4f3bbace3a373 e5b88ff1aac1cf05c175202a7f6df8e771684dd8578d2a504cb2240811ddaa21 13b76aaaac64f9dc3486d79cdf956c8ad96446b400a13bcc6a9cfc829a20a27e a7ed9ce920dcdd03ffbd44f64560d5e818630e5972b1a57349725a9d931a7a35 22ce1a6db1a8fc2abee0d78b2ae20a3fb9ffcd2b73af2e2dc1a2e5dbae50ed13 24d4f0a6afb8e44fe30b8d2b00e36db58e379a9db4e4285d6ceb1285a275880c 82fae7197304e763811b47da232776ac435d6b1d8cb88fdc67d7314c578f0346 9ff6bf4ba4048d7696779bc17cd75b3118a6e481b137294e57e90c2091a5c724 67801be9a256c4ca1b0cf1b83296eba9db445afec3d34ab7878a9a8a26bb73dc 6cc4b42dfd725a7b076ac6a2e073a6de4d1c0164270bac1eae2a402b88492fbd 1dbb749023241560db1081f5be818927a8658048587478f85778b6ee9b59cd77 e88eaa8947c57ae880316b03b072cc759de8cc36ea43d75da488ad684b9a0f21 08a5c80667c494b43bbf9897b3782dc70a7d55c4718577a8bfe7ada9713b094a dd704f6ac32838c8f483365c4c95034f92d6848542716bf1cc9dbb110b66c9a1 4b1e241f89340e0922d0677f7bcc259342469cba3b7b55ea3d46e3de812b9263 d99e1794ffbf0517f4599e7bf20f2d8b8a82fcb8699b7a87568c9a5e69af66d7 1c67f6feaddec49f16b170e159998b7e711ab23fc610444a5c21ce72d355cade bbbb1973d8b5d24b0a6f7ec9d9eaeff3ae992d4d256aebe890ea6065569a7833 e091cc1a9b511993a8f67e6e772e4271bf54dc3acf385417cd66958e9d1ebf0b 6d41476b02fd4e593b269f008c5b35df5b0ccde6472d4b686a0f2ae453448576 c52a574a3563f30b065996f01a054044d9490c3690a167dbeb07cfd464bc194f a3a1fa1339f9e05b62748cb70bb300c54a51455a84e59bd21fc283df75a21b08 8a88bb08dbfa4fd5ebabe19c1a6de7c415795657d05edc8497fb21d208710d0ae7fa752638c4e2ce41872fcc37ad3fc6c91bc899cf3554e0a5f61aaeb52bb10744b79380ee9bd1547e29996bd7f0f11e5e0b58c3126ffabd191eefc62489640ddaf0340f9b7d69684879a1a5d634f0be083160d3b7c57e27b2db5b9df3c8d40cc7544dce1322f35ce964fbbc6072680ed3b40b06a6940a8d455c035edfdd0203192ceb29b738780b9cbb9b480fe8e226ac2677022125f6e100984e02ced8f80e0cc5114819765161a45cc07d54a7f090659dcc9ef866434c60ee0a9615b34702dd6a31e2da35f785490929f3b936c12ff53ef772c26d8d569288c473c08a630fd99c755ca490c6969bd55c2922cc8cf1a4b29ed21a1fdd3e48ed2541506b470ae560e720333d40f50cca38a1e177b576efd48731b1dc87a7a85f8188dbc6d30f94ad0f2710b151bd922f8b7626b5f9162efb93b1924f8127f8e9294eb00e8e07fca77781299742548d081f64e4afb2471463a9c7c944f92cafce467c9b93190c00ddcd970352fdea2ef21a156c85a1f3072e5382126bda83a142d5e7cea1dd0aa3e4f79654ebf8609b670cc83f523476f23f8b288901c4d7faab894981b45408234c5254956eb6dbcb37d06275529519313623e7105eb4a0dca148e920c4fb0b31e946e7c194f1b8f857cf84b59f2e434156994e889ba4cafcfcd15f6c87b5007c26c3c364540c35ed5523b347620fcd81032a67459d6d781c181902f5c0220111e3ef336b4502bf3912239ee191c3a05651f296aa764a6bc494dc0b8cc6f70bb4f0edaf4104514edfffbad4348eeec4d85311562a03cc05badd630e9361f90a1767192a6a62428feba57a082033d587002b84072a844b11e0940942b1705e070867ede6c35fa5f4ddf08f80cea22859ee02ef0fbf7c13f4e943a6ad6404ec076b1936c8b8bd8b0a67ba5e45419488be97b2419b89fc42c31ff411918f13180498ac22f4ede53aa4f1eff131298645bf7dc50d108ab665fc96b84ded89d63e08fa1d7313929d3aa92ed62db5d3c7348fd764720440d430df70898a334fd1ad0ab025efa9bfecb483e817cf6a6bf03ab6d97012421fe4748c7c86f783ee9eed024e38e273b9e91b69585f48eacea9aea2bd2822ed1c15e7053b7ff939f8a2db0ac6f918d98c06ca2253faf65a0c01c2afa21503cfb261095e4de3d917f1081301508eeea3a354d08956d239e90786f88c686648e61291c9e8d5be7a197d03c603392d86811e43f8f64ce6a7c9f9b3eb436c8feaf267f81c295e1e62eec018c805401a942bfd56dc46e1b3fe7d2db9421662621b40d600a840dbf521190635de05769c4cd67642f1405107e3f31399b0faafd2813ffab99ded9842a79a9c9612007667690258b08c83cfbea70fab93bf2ed9f5eed18a17af0d7cf6f4072634b00ea76a2b569ce49cc81860c7c7124f5b9ff9a5e4635af8a211a804c4c328127505e5cd5fe04027c7eedb03c551cbad519e4e3065eee9a5ac3cf89f4b7d5c123b0e17a90a7d5eb1ce66d574a9dfe6201d985d0acaa5abaeb99284be58764703020be8cb5515f8904a8531e42bc7e0ed6d8ecac2d804e9a8025833a04086930eff0fbfee4825a57ecaa97a19575c62577b777029e3c2cf9d7558b67643cd0ae6fa05a16d10e7084f24d1e037f0921ad75deb3cfe20f9a53365bae730e016021dcd0faa7a309f1678ef76ab33f4e52850badaba9e87ce37d0473d6b6670cbfb1870049965fff3af79769a7eb78464554967ab977d1723cb2e89fb4bc22b72656d89031b201519f7ccd606d3d2d0d4d95f0db2d3d13fa6418bbd95c64bb6ccb4307007800023947c98307da1007844eb083acd379098def46e11ff081b7bfcdf76090e985d6b04cd763fca3d0f37bc8482c47b2336571c90be0a7bdca641c0c2e998069d583370dd2b174201b21c912e828f85c3adfee755f453ec433c963c9b6345044f5ce79614b0701650ced40986f4224ced838a4ce86a9f65f46f110b4d78b805fa231c81bb4f3fbe3320ee7c297c3c4498f26c1248c0f1e103140ee7341ccd00c066764192918a845ce978a9bf51cecb1d23617c2ba282a78814f2856628490a2352ddc85dab593f063c29067397b217d0083a765338a57b585bf34856760e07e0586ebf2be124528456f64ad3a7c8b006963606e5ff4017fb36bc7bd6a5e706cba3ba4c83b04d88395656cd89ce03738e2f4aca0886ec99448924d09dc07701352dc740db8146472d55c107143363c54563a39e6604a25aa2ad2b968716720b5a0ccf2f6a52381c3218635e15d7f4d45bd13f60859d01e2f17962e77e43e80a2c03a0140ade01840e003d50a8f6e83937db5d948e984008679f5b91b4af470fa3a8010b1ee68dd741f4343ec2481c573a6dce79b90a40fa7672f17259fd5507b21f0eaea1ca0f44ba83796e39f7a8749893c65d3cf55a8df938c24621e7370e113cf38dec84bede10086a566f9fb231a082ab9b297f757cb7a2ea9e85b20e03bfbf2fbc642102806d67203c222c0d05b80760de69dac36aae46a38c6d9fe508e4371575b83522d70b67c5fcc36bf180414a050a10b815fc3b294fdd417f330b1c410d7be6f40854a32fdc022b295d13069d2ae702d0b45bf4ed00e1ac94ab0b14354cc8c11c041a87c004fffcb9dad44096281cf41f7f1eb2da53692dc550010c114c9a0aed53d090359912bec4f9b7b2319ccce7bb9ea7dc48f3e483c21a0d74505a1c5cc6d51631b01bd2c1bc64feac02f450f73192413c2f484ff404720b0716b70a3ce2aca16b57ecac0ebbae5e188ac441571d6cc4cec45a1d5ab4350ea1d861ee01d08e6405c8638d22ba75052c5ceca757d22919bbbea92dd8b877011fa51ae4ed6368449cb25ed54595bcda401b30774e0d6eddc9fa52aac333e60d4664429e910099c83a8cf2f008aab3250494afd7b93b6cf30df19004589d5205b316d89bb14a25f26b12025278c9f931437fa36d1ec826165f8dc7300b665d02e239313c484799345d0942f0abac443ee5b6fda58d7809a6fab1c0448f006c09396b0d922f02ddf7dcf71ebe1bd8a71f000dc912912b38170b32bf21b9befb08e48186008fb291213cb604def127f6d2307bfe9476412013b07c1831921061075ed6ee1bb41f2afd356694754ebfa19e8ec126c8273f040292343181acf08308e4653429c1966dc32d745aaf56833e0283d0a781f10c8a0ea851b90b517dbf0299d9b5a8fdf3d5f81d928d1c2446d9cd3aff9cef6b28da9724ee3303117d0704492a97959a27fdd395313d51ae8001354bab2bec996aa2ada730b243e82b0f095ba016c3790b0e85dfb7532740267608db1b5b0ec6993d14eaba8b0200e91c05495a0eeb65b1fcae0c7087aaff8e0da5f32cdda1554be2e69f938a50b482490a46d32f623800f5fd151c02848034f536117b3124fbb157b9b0d679022b2aa80694dc92996d847afd7f7e1cc1a47fdac3a320ea206bdbe8fda728093e9176050fac9b75f3b5ea4fdb8056ad72b90d20112a59558c7f92b48cf858feb62d4d540bf42c942fad2185f9cd3398deeef7d26c518daeb611464c97cd5449cc242fd0008d7a3d6b6329940dec42f908ca80196dbb86a02f12627e9a688e2cf3193f160c165f9066c4bb1c7175ba8b94a926de3cadbecd55d85def89bc02fa85ad45cc0717b841178a5b664b06a332e12d8791fc99090c46d71c8d5f55fb60badc3c8a09c8fc77de3ce2910bb671c5861e4c870aa0ea866e25eb873ebf4562a5ce844c07eb412042068bc292f9574a0e29cc73fd038d7e20b8b4621d50bcef52347d1f030fc78eb1d046babcac5a363470fb326409302af875441d102ae92768d582c70b71ae9a86a72cd2fcfb185e7e82f94278e27a2ef4a3071ec8ac070013a05c8c0afe14aed853acfbda7c1c6fd4856964b87a8c57b02b26693b74b3d69dde0be40f1580b7a4637171cfa68fa941171f3031c32a45424aecb630ca3ad2603f9fc80b86350b60f5f162db3860b7eedcafb7461126acefb2e31b63199e3782a9581f04710464e1689909af3db8d9d2e0c3ff94e1be3febbd355e916fc37e8873f20c036e9bff896ed358bb9004eb7882f76e0f33931ba9852fccaf2b7e8d3411503c024d970163f50e0fbf2ac06fc35df8a19267b1a3548ee7f6f94ebaa59206890e03d635d8bb8952fffbcb674bc3f4b337364ba81c9a827b3f998a5a3bcde712f2026b6d31971585659fefb7f95c0bd595668145a2d6f904f0a257a6e07e2e80020a07f9f668b42f3cda70804895ac5e31e47c0304260b65ee0bef91311a1ce64b08b6020d2be469c3ca971e98f79bfd34c8a9cb2f932ccf4ba14ab0187c3057d90951483ba17a617688551ddf3b36551fa585b88c50c7892b37c20eb6b8de71260d961d36ec0b990a4520a5107eb340d567401265e0ddb804e10f6373b5becd7c0adf70f7067324b1db2452e737c09a2daba1b8767758f0b20c72ed9d15d49eb500ea8d5283a4fbccb11c5bb458585bbb2e0a764c42a3ef9a724342dbabfc305a038986667a07892dd8c81df7d29a92a0b83209693163039a9a126c9b33362e8002f04bd604e97d2e1c40b78a4b93914e44069db512877571a1aa4374432339690242183d7672e13252c1bd07877074594304196f6aaebae4fa6d268bbee4d758031159ced6d8b0c3201b28c9a4ddf424f3446589786e6b5784c183847396117104088feb5073921092dd1125d559f50913d459d683c581c6871ba628ab59de100b46524f48d5298f6ac004ff8a46b6d40fdd4a4347fa7725b9d253506d9107810c9416e9a7e0ca0831e3c9adbd8c762d9a34b496b91c70355284d44c25eeee3b05170b9f2d9ab424d9e4f2d5c2bc7ed8e66762bb648f4dc098091636e1354c350fdc4d4a085cbb2add7b7354ca13dcafebe9d93013dbc5d7af96100c0e884f0508416c047de7985214afd6ff5abb2b20e43ea5aa1b137920cc836f38d4aca7200ed8d7062969a645350c9c4d444ba35332ecd29ce628550b821bcb8829a8638304d374a04a6075137c1c6f1072154a7b0b85655ae4490e4af6ccb174530f43780eb131847dc7c4bb954f75fb61dd298a92a9a33efa29d787139e47991807276a0142436b576b1c156ca25dea68bf493205ee0505fcfa63c13c976a4fe84e81490d92157ca64098a62c4584f325bc40766829426f807d780dee132149ec0e32ca012a40399b0cfadb6abe1a2e4998f8e4a57a8330c35f7f2066af0bf87b26be870ba6338a8db67680e42b51decf96b2b6a6dcbd8f891d11704ba870780e73617d02a679a047687067b7f18a595a8a77a234170674a84049e5a23194d3c38b6c550131b34420a424c84c7f72539018d663dcdefea807f1d41a81dad6cf0d100754099c810393a7b62a524ab8187f2f4954e4e2c94120ff7fec438343169a4607290520ac67cc293de15e23dc342bcc98758ca24c550ce3be9e40b2b5eba513f1e705d346ea58de2f734a608534ca390fb728c5cf98af3fc63c3cd41c3cfff55e81046eee996801020852d812fb3a72b57895e2ac5e50361671eb173e32b704f27503d0140053a57bd3ee3f2ea595e8a883d319f4f36a457925424908aba3f16ea0021bb2b399fdf967e5dd8fc39132d1a896fd59b6f70d59aa759fa44c3c889614013def9f7788bc4891eb01f03fc7be9e8e320e45993d1725714e646bc79d04430e2c0d00fb8477fefb381d2b32e46a1b7459e6a00fca33f13016b3b299b5fc4d0b99bda4c5145be2c4043b39ad1be80be7bbe5cb8d2760a4a84d4238f13bd9910d41b2ca06d44ba81b797a43691dc0b5bd3a5efa1ee3d7b465d58d73019566271e6433001f97953640a578cecd522584126eb3f118491cf7d100239441b47f9b044406b9711f27647e95cabae100fef6e480872d3d407b6d6552e6093b28459f0f3ef6ab4129fc9fcc5cdce07c2d34198726fc0b563c20cf8d9d28a0e450062d0192cf69f27f8f06e3b61a30be898c0cf8157bbb3ca6a5ff44bc99f663ae502401f36f2cb7ac60221641a8ddc46533d950109f48d552ed9a4b70aa48ae3cf47f06000c535fce0a67055454f19820ec4772c43837629fdbf505215fc90bf7c6a3064a4059b6008088e7cd90d23f43d366bbb30fc14ec52971571362eb9e02f43305007cd6f82969781c72c19f70b3e18a4bbfe77a8285c7e09bc4703467ae4c160c false +check_ring_signature eea841224c1e655cd4f61e87fbf9dbfca7530a70957b7b7b2b5777a6547040b9 73ffd71ee4d5fc26a0b605f5d5525d27c27f94875196588ab30ef968e627cd59 3 6ca5a8fd2afdc18d0c0db40916e4fdedf4a4b724bbb6caaad1573f5c407a8917 7377c0c249c8e8430dedea86b8f70a1c1e9dd0becccbc3dbc1025a892ffa7d20 82ad40fd3a1bdb5a6c8f08581875b471b7f063a50e907bf46e50add94f51b572 cbca1d96a96660d571f165bacd09fd6628c4842dcb7f69ec9d32949e72b6510fa1b15088ee1f5e7a074a1137cb63704acf93f4dfc6faa6fa94fc2654df0b2209414891dc3a50619ea5f2e34c63980926dbe7d2932a6fe8d1b2e95f06a847ec06332671119cdd6a8df8dabe67d39556ed9fece768c3fd04b93c992a5d9d01d006e2944b62220f46920240ca3ed126ba237c230cb60668fe4ed0eba8ab8119fe0feed5e7db45353a9253293347480c9e7bc3dceaf35466ea2f109f827e8cd75801 false +check_ring_signature d6fc1c632949390c7a204c204d58141e261dac77524c19f87e62d68cf8ae1bc5 cf691a8cfbf15ced48ac79f25d9bd36d49decdba0efede0b3da4fed0bce5a298 1 0fe3af48f22769fdd70ea616096263af7abbf39309e85cbd4623229616a24ac5 0b23564a3d5fa98b8e4293404fb4a7a4cf4c5a202fa1e3104cb2a9cd57e802da597efdfba26df305b571e0ec22d6916d0899b20ed23f3d108eee80e291589e0d false +check_ring_signature ff8a39db0ab7395ba98fdcd69e52c3a1c128941f92adb3ac3711208928f9c523 e1230fd2bb0265261d2a89eba96789b828b5425a00aeca5c268112ff0b7983d4 2 e61a7609c6dd7ed6db298e710f7912e1e1e612068f1f4533bdc0b3c07bdc4334 f0df2cd0bfd76917328701b2fdfe391f3d8deaf7e85ffeae88ea2cd688257c07 71764466883947ef17daa2b7b932034aae3f18bb9e8c4d21ddbbe32702f159037308bc4f7328c036faa5b7c65a08de1c6e6f7af3891704617f4369756883870cc3b93949a34620e61ba38108c3de942dea9ae6e87d0559a12b5a67b5042fab9c5d50c7a720bd9723f6ddf0ea9a12b2dd5b42bbdacaf122bcd9c8ea675726360e false +check_ring_signature 2fcab33ec6e439ec301424c524d0b936ea271945487600b33478d21ba3e32494 7b0bb775652e05d39282abc2c50dbe5bf1989c38ecd37e8544035bc45d1a39fd 7 75d6342a8b792fc8da2688330f38066369f26de019130bd24d851623cd2cd786 a1854bb2aee607a12a4ef84421d229d24dfbf527e63517dcd84b5cd8995f570e 05a577e459712ad58e8001a578c907c6d09a712d072dacb261519a66b8ddcf56 4acec1ac611382ae12d65624ac45a0fb3ace095a299f9de835b9dcbd91acfa93 c2b17fa3e99e10252c180d9971b5c8a0757bb9c5fd1c8275460e5a73d050dca8 c418c25df504806555728d78513ea6c055ebeba5c0bc9776a33a0a3b3ced8899 cfca6efa3baedd289dddcb493302f5d31be3029bdb02a3a28c219935ea8aa531 6a113907bad08077dcd142372c8802962a8f6671f5f3d2d96292b89d8dea3b0d43029eba9e9153c45bc46cf87300d063f72a0cd0bd8cd386ca2663a8bed46e0c4b38f60cfa844e83a4016a4a5fd798ad49df110cb6d43581eb8af5f60e797004f7743c4a486b2c88cb709e815f07556a243ee418f25eb2485249a21d7bdbbf04397cefb2cf0abc91a460ea6fb3840d223d32807e4488697c585b4cbd25c84b078896d548fea7aa687740eeae37154cefd2af8d1d76659458f6c87b61c1e3e200ef7d0b539478f7aec06f72bf458841c7c0a243dfb68d9a7051ba187da83c6205c6a0af0b7deb9f2c64416532f97fa67fe61ae7544b7aacce3d70c1771a43ba0997ace76c5bc1722722a22a0745b469b2ae3fefcc12073acb6f85983ed8930b01f7c6a0155de3d29d3686ecbc63424c9be83896d4ea7541ef9c3e789757de350aaf3219fbf00959173d9987ebdfb5030430530ce34f72b9649115769e5769190a9420552efd66336b97e8a70842d7864d95d5531987e5b6799fe878da18eca10221f16404c3648b249baa7141236349e5b0f35c6dd16ab7770245322595a0780a1255a3d5b346e96c4e10b3818338ffa113372a6e0673f876147bde292b7fff0e true +check_ring_signature 763d52051526cca2d885674176c41b2dd85aedb8fc98490bb4dd0b06a4686a88 967e913e5703a4ec3f8b4f2b4d633c73069d02cfa3e5257529e0b58cc02a85c3 4 a48f996559c38329a8e1fcfbe29abb1ef08347e8ade354124c40ff0a50f7400c c3bf9ed0e21004be87087c7343456e766b1b393b0a64cf9a0c911ccbcc672c5f 27bf643aa6cafd3127222383a454d2a5807fc325c231bf1f4f762326677a4bc0 45b6cf4f2605dad53fd4180672fb8f7c7e5cffcd7331cb2b43f13dc30f25a6e7 fb1f1d8dd63087b7de4bb7364bd46e7f18ef4191ffc0168721a8b5a68f4975092518d33c09d41505ba4ab8a14d351c6630fab90416c849e8c9f9919842f7b605965ce11389885746e755543bccb0461013a72a046373465fa5f8b3e430e9a40e74ccb2940cce6476d6b53ba26f458a68fa10c4efbc0d7ee79bd04d1fd701500869b23788393db8264728020a12ad9cbb4252e4751b8d7c769001df73c67bf80bb0163240dff3d9613ef1932eda93fe364eab5e7aa295f19fe9cafbd45834720bfa1900ba1301ceec852efc13fc0def5d13846eddd5028239cf26c20e6cc67d01dfc222de1277f58c78c9518bf43a426551e5537c2ebf1753ea48f083a09ba00d true +check_ring_signature f4d85fb8e4773ecb7969e5a9557a2d0437dc961ed61e4305bddbb2a1e96dd3de 8505f3b699ee1b73a4927192d1e8ffb77ba2e56cbffcd31d65f2e1ec75176fa9 109 ececcf1ec80a624a7c9f64e04c37909bf3ebaf50f70a795b5953a7ed295b649e c2a089f7f2f56583984c5ad988bbd02f74051bb33f09e14166aa1d4b8029ca8b f9e80987a04333cc14bd9e36851bfcbc892b6e57746f71145e4d81729132c1e8 29efc9068bcf427508b1e747348437662d222dec4edc13e6107575edea7207f9 51ad39fbd273ef0214481877ab07df24d6f062f5bccfffa3408f2f2236cf1c17 27260068ff0e4d815404ca7862fffb22e7c4c2aa1b96dd48727313c63aa43747 dfe8899ec28cb420df3669118763420c34c07a6b7b866f5fe14bbfe19ca362b4 9e9cfafe3be2c1d461af4c2f2186cc4d9cea739d5b2a14da8d4d2d1d29eedfcb 96939dbaf9e08f479e2f8651dd0d78085b352f95484f536e77a858dcdf4363cc d493c034186d3b7ba23e7e7066f9c7980401a3075f6d361aed8bca5d5aa97fa5 57c7779fa2c2760270dd5a825a539b9c4ab3b73ce64213eaed948cbd6fc518e8 934570a5d255cdcf0515cc54966beab0d4a5313ebab2b0d69d877e9bc2b9821c 2c928a7a1d5010e674183f493a55252c9406343b54d237bb946b74a8d526b849 42dd881efb8a6b57c328b18adb55c8d9c32d335a36ca81ff3a35afed92cc698f 769d2743255545393e135504ff62ed663f52eb85c25ff7874ee8cfae76d9db0f 992ffd75962768c79e61977aeb0c4d4f5891b3a3b8dc075853fdc53980421d93 c2fc238c0dc230f084ff1c6e16b0999aa4d0107a834c96ee0a668f522cbe12d8 2ca34b590e62f0c847eaa0801a7a08696be2f83840e38002f3959b5e75a6ffaa b2683269399deab241974dee1eaacedc6d59be23341e30e386ad310d59c7e22e b62ff6c8657c7d718d1d3ac07e4896d145aff5261c3acbc4fd7386c81b7cc7de 0aedaf74f39944015c28748e05b8f0cd0893ad0a5bc6d9d2d115dab83627c8fb f726ee628d3ca5252fe85d643b5d1f410964c1fa3290029d7d5acecc37674ddf 5496250ed3c3e2076efc576ead14ef5e0fb49ff58729e757ee9a4c16518cac08 c3c7ec1e3d34808284cca6adf06f3fbfaf547d6e6112b173b4f1cc3046d7e36d 34c9a6b74706876e597cf4475416693d89858b0b66dd19b0e572e2cf17b913a9 4ae89c158d0962a1fe85e25e36a6ea5c5dd91826d8065390eec5f1ffc998029f ab85c82af706851f9e32d4fc26f9ab3fb2e58e488a4c6be36a3b5aa41d478ce4 b18b771958651f3424a42d3df51a9492831066729e023eceadbdcb23a0948e38 601c5b197091495dd80c2a5ddeca9dd7993bae91047ec2e1fec0c9266f1aa3fd 32a696ef243995234f358c234b1983824bcb7000bfe2a1111d8b32d346de25ff a57e00a400902b5be5dafcbda6d6e8f3799b96031cf3ab377ae2744ab4af1f91 b2902edf2a60242195eece1c95246d41dd5d9b27e63c898e328d2fdc7b064b0d 75c712e25e797e0582e4995e35b75c8f99359feedd905c81146f017c548665c6 a5d3e6051b35a4411a678fc6789279df92e133e49340ab4447f3be7457dc51ef 8c3969bea369a88bc658e1d74ec80239ba862888d843068c79123819da4940ef 7a63d4db40a5e541589eaacd68567c0ed6710147cf1838170c961aaf909c3f75 de3f4b3f53fc33f766fe7a75b39b3846855abc7ac66120344ddb73631cc38ae5 96b171b8ba081b70265ca8d5a47dd7f2ab244e4152a933da14c0b8f7d52a16e6 8a62e747ac0f2e1f85397e2332b2c825512737b5ab67d12598dc591d94eea8ca ca8e1620b93636ef483e80b0b6221ee955ec0429e14b1deeb0f6e5bb2ee94c04 ecac3f7883a037db6cdcc301aae4af3473312d52a29759c0f5207e40b2e84d8e 56732afb7b11ede7980454d36c11051f4fa4f6f4e5254f8a6aba3242b42cdc2e 395b669afef3218931db408b2114f24d8ccab9ac1c0b9610e8ae95236fbb9079 9bdfd5107d19966c57edbea23165d4ea32750d9cbea2310d92b4f34ada539911 e9b40f0003a3ae492911defee7c7d19e935b2f3b657cc8b3b28e8248a4e5074e 09b2ab65c632cb0d87d8407800699a6fa6a273acb13ad6d870861536ff74b7ee 0e044b15014af1483a43bb58a716795d3cefe94bb2a3406b24adbd7771e363d4 1c16bef73a63ccab3bf89631c3660a27c8fd49611c70c922c7c08dd0b36b4212 2f511cf74ee1e10a19d4521948ebf82dbec3512cd6a2c3087105a329c2e9872f fa6c34a4e55bede5417c5fad19c2da7fc7f6095cfd5597cd492d71b48050c3da 5959cdb332eab5d47b4a0578be6b6b4cd81d93965ac8b609fbec40da12b74c18 febb239a9111119fb5c0af8266dc282c3a72cbbc67fbf06512165f8f3822bae8 87ee57d20e1c7bb3435826d9c34c7839bc4df9b2b328c6013f0f4017af16c389 dcb07117d87ced247bdaf718c97cd8bee672f4fa273437948d1ba77bb9a97c58 59b1315e6633f1e867daa93b7b810d597b55f2b7db2c11753b57df0dca0abaea 32eb38b172ce7b534e4f98e63558972746147c47227044ba8350d7aac81c2c72 cad3c48b399edd71c40f7430e6dd66f161c44f30716cc040b0fccf156d1a2cac 619f7feddfae58a56c532bc3a7366987f9f36c089d62c7f421f77d886d76f8f4 725a792edbc564e386d431e5704e9e2d3e1a88b03d83418cef972e1aabd9fd73 0095173e6ed64df9a6ea128f2e48d9529335c29b59dd163a15060bf8fcfa2bfc 78dcaf174eeb3c1bc5ad9442dfc736eef5a17aed4d2121b771490386ef3c1278 11351c187f0d9f6e7c30cf431e49c02ae7fceb84ab5b9cd1c62bbc8c6535384a 336140f2c97bbffe7d5ffbfe580f4fa5467419741a48cac5b728e19876a28b1d 88f6d6a09d776ff769fb09dd72c278153be902394e588f15070db0e27d5e63e9 cd60e8ec1271f35bccc2d1d517df5679664e8e530e63b32090248260a36269e0 9995697ac0c23d471f698843c4c2d5f302aab8a2f4ddb7fe45e89a804e5769db 082aa9aecc411fb441949c31fafbda8f9836364efa9d2ba47120c9449b579a58 6445f490bd05c291c71bf507076507109a845dba9664b8baa16ec094d90e29c8 0f90858d305ab340788cf934b8175813042c28b7708d55bc7f9915939aee0251 cdeec48cfa350d2e9126989081550076aecb6108b6da02d2241d9ed1dff7446e d3acb6e610fe5701d288652d84118fde6bbbc50146c1d0c135ab4b5ab4b146ca 8bd97ba776d15a026ce122eda424a962532fe7261affa507e20620147f63e1c0 83d482154062eeea13dc563d5c108cba4e713e3c9c2f455617fb60871a167e77 7c782a83296cbcdd3837eb00c59b2869d04529c83d9003d807e07dd8f67c9b1d 5a2c8631e4371d67197150d7382713281cf045cf8f91da1fd0693b5063d734a4 4747c5f2910049bb6d13ed5f8ee491a20bd8ed60e6ced88731b1441853ee22fb 698d92da1e6cd3f16c42265148d633a038b05d19fe16fc3ff2d57d37c2ea070e e9ba956a0b2e63de5204188e56829510abd05c101ef64301fa8b38519519d30b fafec02228f0bad5a6dd04d95b9df3f56f97212141fc5aa501b486ac28caa9dc 2484efca8b16e48111995dba3da1c4ea789d63d334749027adc9a7ee7fed7408 257d4d60273dc5ab3b9fee22aadec68ecf71ee53b2ecf0f77e3c4f94b92f7608 01fea25b07642388010ad182690642dff916a9783d4a1b2ad88818c82f40d9f0 a65d04c2e03ec676ccf8e9fbd1f765260302329d71ec3b65180a86fa69d9b0a1 a4f7f2ce1a95780ed1c9e3c5b5be1c3bb5d2776483fb64c30142bd847c6df1de a618efffe61d398fb26b698c220a878d806bd28c2ea4563f310927daf042bf49 1fa2045c909ade25c28b6384b1d3bb9a6fdc91ac6ca21d4057973f9bc6cd6940 439949f15ed1d8c81290219068269685bfd025186169d899c30873e660d15555 4aafaf0d916e77f6452c0b251cba1f994076887953686bf194f749af6c3f44a5 7ff7e1a4433ae89dadd033a80bbb9a3f00ef956ebb4dc07c588f93ba8ead2030 79a6ccc8e4b1a186046f7fe7f79ed6253c29d0e186a2dbb6d072448d44a842d9 0ad1db519b33104ba6ff1551c809f51713ca849cf64db7075e992f338b8c2ad3 cbb814aa2208a05f7f7a3d0706dc76427f7acea97bc6241f7db71c81637298bb bf49c1eb01603eb21debcbd8ac25e1f84b7b0e6f714e8d1320751ae93386acc0 ab206e1dcbd19e37a7f5a4ee72633d3124e922150d381ba888e68329cfdfca69 ffe962d9f196a2587dc26c7da5c53e08119675cc109bab29558aca1461ab1a13 de5e5116d38c93dadf3dc9af41c5102f95bc10efa50a05e6974b011f20a8e7f3 42903aa8a3add4e463cfca2ad8038cc2c59ed41431b9c8513241e6545959a9fd e53236239b70a5187f5797914676ffddd468b51c2c31bb427848776a9f9c9120 1ecbcc386ccab96d654abcb43795df1fad5ce7951c09a95697620f8c8bf7b3d4 6abfc997dfb19722acdc2892f4517358d609a912d4e0b267425f3b1f170a4eed 513ce021b22de925953354a715150992e56d8c73a030f4fd7f37e7b5c0e84fb1 858ebe94275ec739f29e61642f648d1df5afc0917452eb7a0441976168f1276f 1fd23598d2ccf692f328a9b28f10439ad8460f9f79b19f0e8c4bcfed130a4e8b 2894d9cdd0c2d7fa12976f28164b7348183214314e4c218feb4a433924a65fc8 50fdfc417f57efd2f029978104a2e0d1491c17ce77cbb4d2dea7196e786ff33c 11ae7d1ef8cb3d68f324a4f1a8d268ae942bfad9b37e3d19e9cdb317f5afd831 3aeb78385eca58455d669dda0331b9969697e9f93eacbf4d5d5fd31f8c8e134e ddbde50a1d92596b9e4dabf82d0d0d84e2943ac834bf8273a4557d1623dde24c 0a454f428044201a33d0a734a568100ab85446372b5d3339f64269706baba8ef 2059524c4c83bb75f70779cd55a9ccf5b1c8ab39d36e5b231cfe9659971616000857db4abe8c5db7863db51a5a455d353a174133da32cf056204498238d5cc0fdf5a6e171aaf682dfa5be24a728cdd2d6d4702f3ed345427434236c259184d050d4a4eebc7a1e149aedccbf27ec125a6a248a42f0582364ecd5b6bc0d902e00d771dd67c7540c6a6d2593fc233d708dd3ee03691758ba947941a5a966242cd0093f6062983a53827bf82f2181da2107c6743dc2d5d030e7fea954ca9c3c646093fb257dd8ff96dd6033d1bd2184883ed8ed357112e43e960eacaf22fa125800acb38cb2580e44419d9f3801d529d3e559713ed50d20ed57e40dc5a0f68bd7404c835ee421e0ae912eeeefdf8e11a8c525b9792f68d349e0108693490c0548507fe934723cbab91c4137a0459dca511866a3aa789299d45ece5cbd8f214dd0d05ea05daa69b33ea18fe26e054c51d184ba616d29517b137518adb17c17ece20052deba8e1f76f72ebad997de13eac6dce7c88c29a64af1715cc22e6f58a4332083a5191550480b5c7a66a711233ff8469f649854e18d6101f94bd3a05ef77d30ffab9ade4bf6fa0f4772af9ea85b57f30eaecec143aa2597b11bf6775ec14740db7df3ece240c7f6030176932f185fb42114c3ce2bdc6ff9265e0968f7dcee507c674989eff454969406eaa6a1325a00319a316ca4679daed737fa039e80317012524666ce0d9841e147f5e3b03406882b7c3d4abbbc60925bcd93263a4b57407228bc536f384dee902e6f2520e54ecf3c7dc8f2f59c0a89e0192aa14a991f10c3442be14de001962118303b24d65decf078cc44d465603d5e16981dbc29b5700e997aaaf26cbc22930fc4d69b629182cc38b509a0d4ba968167ad6eb1f2da400eb1fc1d996417d568121cbfefd6722ff2be8c2c49027ecdf5a3d4a8832f93d0bab0642c413520196ecaa3efdcb4b2509a1ce9d30d7ec2a679ab8f3fc96ab69071a9a51269630d3657c56b409229f18bad6f9633c95f189e27d70c64e7c041d0e743dedc602789b6d99e5098cdc2db38466aaf5ed9d1d2f4da3d79c7c70886f0a5b2f585c2df11206c8cacf3a88954da0737a5556b24f861c38029796cd8b4b0872ba6b6901b20e2fa5fdf7614e229ccffba2d0ea7854b999cd7bfbba4580d1070e2cb4e554e28d9fdf0a548e692e1646eaecef001d512c1796b8253aa7735500721d3f0c3dfeaa0e01978c028a5dec4ae6089a6ada40517e292426b5c3fc600a3904498b0e4cfae25e0dd2ae34b766e579a176aeffb13cb209c7f1ed5c98cb0d0b270f124b4b75424eae5f176e323ad3324d0709e8d38368ceff6dff9e850a09cccdb7dcb767179b8fb6b7579b2a5cf32c95e749ed558147bbe64c1132ecdc0fee7f327d649fee9ddb80a623e8cb04026b403362681ba510b94913d0c013710be56e3d769f2b61de5dc7a21e6a6b74d284aabf17b205c6369f77efbdde7a7b0e93fcac977ab3b48fd22854c15ccb5b7bc06fd7cd619c6077bc8ea3f6fc2bd50f6c2e2cc4c551d1805b636add237ac6bf42050d3fa0d1bee13a1be38cb8ccee0d99cecfa91ed25835fdc5d802926b5a3423efb1306f9d734784f1989afc06690bed1aa70a88e8d4bfce492acc8c6bd0e56f8a3f229343b7ebe5075a232f3d7203bde35a80e8e69807f69f0749ec0f794df500e00b1d1084c7875377925bf313060e552b9087bab5c7268dd4889a66a6274cbdd2a7bbd8b7a121935998c729a20965e007147b87e61df2c0f1dc56d2f5e2469c60d3011b23153afa831535eee60877ab9a70a09384fa5ed8973ce5857889cabf813824d41cfc1fc9dbbedf22bd0b9058f7af00aed47311c0b7c14cf9491a77437a860de5ba02e9a10409daaf250cd4a90b11f6ce34091814e9c61d48c048e4ecfb140233a61c7724a2127a4c740b1f95d4a2797593282c75f55e4e028ea76cf61bffc4fe78ebe6f61bd9b3c7e0007c7a70c420a60c481e3c59a10ef4bfbd0c5028ff8bf074c1cd46e15c5f20e70bdc2f40e1867a308cb50f1dd84494dc59ab5be25adb33a08f935734ab83a4da0e31dfaa34a7f55035050e2ee4e2261a24306e333e2623d73cf63c28ec5d639f0ac4e894daf0d8db07dc1bb5d37ccef60f5f427a4c255aa5dc623e6b5aa9fdcc0b8a10bdcca6016354d45bbc41e7a09f581ba410d1330e1f4c2d797271034839098bc62c53219582f2846e4455fb79785c2773343abc4389b0e90e1acb39f33e0c2de4fb7f9a59a6431bacbdf01c4e6e731206c5ea92d4359f8f61a2830a00980d1c3d872a4e0f9281fe7074690c1def7959ffa0e5096a44060679de9fdae1f20e56878dd9245fb3a6795c811e71483448d6248ab4d4bef8bca46ec3827e67750d2f93adf5cbea93838bf6874860d801ebeaa0c718445014af804e7edfa800460cb7df03acb5f052d979be2868e25020545712de538e2a144c0372c40185193c0c1ecd42964634d8fbb907887a0e0562d32900080f19c4353fbe225a1a8ad6750ec901cd4d0d7411b4427c2d888fa6ae509969b442b2f2a0c3f86e01dd207b8801282ef52e40c862bb88002a7797b4e78eebd0571882d53412f4fb1fdc17a278093b79d80780538be51ccacef2dcd122842baca55d68e1e82f740d37baca762d029666258fae51d5781d05b6a00aedb370ef1b3beb5068569987595754be370e0209c913055a2d5c7a8aa3b91b60462423c50e17213b76bbaf82a5b0084db0d00acea80c4a0c48eed5b147a6d9cc4e58baa0bec6421180f2741c7ee1ca88ba7807c486748acf0fe463bd0586a988d2a683092e3117807ac01234ab7b0c1cab96092bd2ef7e0c2e8cbc73c54de063f2a64013303b9671c7928460f4b7240abfc10ce0a07a15270811a1a547e8028a329d24986d2971b8ce3e6882d4b6d0ad8e0202df65a259bcb3f680ea51ccef8a33fd9d193704a8af4c8c82e497bed916ffc20b272206086b9aab5d8f5b8c80c6acb733fdd9bca3308c69a28a759bfcd856e405fc4b5324160f3058bb95b81b95269e21468e6a86a5ab6743a81132088995a203833e1b04ba717235f90c39f940da54b5b0e9442887331431cad0c8f15f52b606a020c00f1adfc48811a1a976fe5a17c8a55089fa86a0a92889e6ff2fea3aa50398cd031d2aafa45a0a1e1ee8849865f57c4ed1e5e09c524de40d289459759409f3892665422a55dc0b56019befd9d0b271966d9b22425c8680ab1c0846e8d60cb289df2785262c8f3cc4b322be56e7371bfeb2d3c9c09e0b6978ea5593b61e06c542994aa80a21b336d874f6aec54fd85b22d7aaddacb5810773455beb6ab391fafc4cd652f1c180bfe1e2d96ad2de7c59c906cfd08b2d3834543aa48f493b0b4f711f8ebdefdef978184529b1070f93f8bad5d56a7494f2ab3971808f2fec06ac6cbc6fcc9ecbe0c0ac8107f2fe61f8ada0075f08bd2178ecee919621cb2609d88febb46e45f2a06bdf3e728b3e0b2125809d426203b67fee87b65664d132002089b2ea6aba1db56e0396ef665d6bb3cc62808c1bbc077536130e5487d99c00569ed88f044226d8d775324f72ed2f8d96395370dd7c1a31671e8cd74820aa08c20685d5dfe9b9e9b4d82f8e4ceb7ec6b4f25ac57717a962c12324fa2413c6058464290aba6c6a07616314de1146ccd981175e23c98725b2393dae03c270d70a101a50462143f572beb2185dd839fa2fa2799d18c36a28db589994c53ebb5f0e99d43999cc625e448b4d91de1e847d34c81721a33d2395fc1d34136f9353970d662c9c3e0eea6cfa3d0c5e7f83a27fd50346142433f33626b1e441013fe3c406b9e0277697df603676bddc8614cf3543bdef0623e4dd9a40dc9143d2e4c2ce0690837ed31e8da4e768a02f626c04803e6570189bb4703f58f9246c6323ae360cee9bc36913ead1d2a91e503468e86a5a43a04001ae08aa6fee9cde998f7a68098813c6484b01efffb54bf266a55802a7c6fb8dd9819d86c79cc0e3810e33e2034e016baf0da087d11c45f912935f13e35d83ba519c3ea697e570a8802fea530e968d846340d5b2d9a7814a692d860810f3e0a3ed46651a63f581c9eca6bb150edeb333780e8639e10bb75c3400729f2316e8c7faf9ef2ac274390cef96ef2d04d894906ca1cd59dd6a0250971ddd35ca5b83a5a904ae528ba0e92d63bae8af0a120d101ee89ec7fef824ef5894f7fa04ad58d2af4fa32ab45dfdbc54309e08022f46eb4fd9e522f8bc12eabaa0ec4e9e4e09282b94acc621267ac0f84ae1f4088da6c4e27a13db3457055c8d48334d7a9670c0a59492639be08801ffba66c40ae546349a30c23a7cbc7d9531904dcf2024403fef5dd176e704acba0c33de5101d03d9f33d754e4fbb1525000b82948601483cced8a2312846ece1d1cec850d055ef4a6be11a51bceacc9e16b415dd9f4dce25a2d2b600e067491a071e3a2d90493a1db1e719996a5f1be6bc4effa48002d5417264baa8c1323d92430e6190d05cff33723482e8342b002d47b7d5feb1f98680cb5ffd5e9f74dd7f899ac1ee505702980407da42517f69c97676b55b6c42379ab2e52d24f785659a22495f0f704566307877ec17e01c7f86640367ad2dfa1cfc0feab67e5b54d95d3ae58c1300002c4916dba127ab38c31de31770f61c2b38a5a9f8381c040261c97b51118c606b028026b3de72fc5ba01243b0841fc7aebaf58dbc1ed9849f9a6db3eecadbf038b63d090c9fb11883b574aa331b532f2308d4c4f650e2422424c25cd0416520c72def4cd1dc008041887c3c814d550d55396780ac562241c550882dc0a7f02077502a9f3b753514ab1504af68ce1c3038fd616e483a2be22327402d89e329403976870447f9b7229bc16a7a6bbaba8a0a208643d56dad7595e5d3448af664e0dd1024dd198bb259edc32c9c52c35e4690dda04b10ac9bbe74d69cea4ace38608a65824e8bb4ff0962ac84b84be83446ac4df72c1e7122cb12de0cdde0d74520c633358e7fcd291fdc430d45cfbaf88ece677525bc2d16ff640108836ab77bb0a546c53c1464a933d406880f60c6c481289137a5f5c735670ffafc8955efb9800678c85690ae7a8ed24d3afc6adea6039faa5cf129eb4da95dd1bb8c32e07760826609c4c81d1f212b6b9e0f321c72033f8b5a192799b3b5750566f976103980dc171e8b594755c43b9b2dfc6b6ce8e2f06d76019e4782ba23464c3dc520ded0700a6548a9c1772be9101341ff93ac3894a43c319ee10f33c88f7fa032647dc0fb242afbe9caf982735db52b6f5506a765d9ad54b4607a67e2f05b8c00578590cefefd839e10f3489f3e5a885d8c8305c0aba526aedbfd540d230db18853d3f0d24edb8ef0f9abbd41d4297c524fd493f57323ad05a7b5d61d83b6d2b410cd50cddc465004f0919065f13b84285817c625904ae0f92ffb75fe1c47c19cfaf2204816c1139efe71e5d8f86a84be4e56037212366636c07a6790f9603b663a7500cc8385e575c9c8794932cc5e01997d00c16fdf8a54d859708c50acfe50407850acff6d00f62e062e0552abb3540fc1085874f89b781332013cfa19f244a48dc067239553202a1e09f8429c307328da2a74c7b1a2e8ae1fc5ce9332fccf3db31068246f241f95f0e312e4bc39d3495dafc11d0e20566859577252244884155660904ccca0fdb06fb6b20e23d061942950848d8564cfacf9c2382dedcbc3b25c90a539abbadaef5df4498d5235badd90cd6a9c80a1cb638949de238e29c8a1eb6017e68135d9e5531b3909af507fe18ddc0979c02d932f9b4deaa2cbe3edd76270e4baf28d9d88acd1e64678cf86056c0391a1ad742e82be78fd05709ebf50b43030edb0cc5a59810bb6098fa88920780b2dce5aaa06aea371bc33d9dc67eef4d0d78af14984aadfc281adcc0a9b7cc9f02596cf857c49e8ade42f3cb87d914a6021cdba320fdfa1036ea8d1d34cdaf609cbf7413593ba31e13397ac3906567870d40c052e9ceaa812194009d2eba2ad3e685c30cd72b4c73c88aa8d2d872f6200b382a05f9c9d751a1a9de5d0979660f2bd59561aaa3fbddf3dcd6c9caf7427d00c7edbda5582d578996828079ff0ce5dbf6425fc932a867caa62c4fdf90ec5e0ae60106475e522cd3f136531db05ddf72d061ae3d73bcbc1f126d06467e4a0e09bae9a8abb873658a8cbd0faf50e22720b4fc18b70deb25521fdf0b0249f1fa0ccd0bf7ef9c27c6be9d808a48bbde039d8b663e7b22549471f007caeb1633200576ce0a875dc2968e9036f55f6dd6fbeeff702edcf37e6be8d224ae459b930e06061de08bafa1ebd9810049b5053c4f202bc0a7dc279885ea42fbd6cc658b820151af2bdf0d8c9b34fa920080cbe7a5d221a32fd24d10927a14ad59ed5ab5ca0bc4f51329c89e1fa24d13530218209dd22b8d5e88479537f4dc3f6362a17bbb09393bb7f2378077a88cda7702f0d7a6d0641941bcfc1410b82e48d37014dc390e2fd500ea6a30ffda14cc25a42c71dd799a1b8a6225efa3d66863064bf3d49b073a4108acddf7eacb1bc8e5ed41857ae8de16d1ba39343a9271b079e0cd5d900351594159729c7f37664b536b4bd05b7c076d857e1ce9542b6d0ee0a736450403f0bb44c7c20cad84c565702749943dea8bcf56e041c50fb40da7cca0eb549b05baa39a5e0d0d14ffaef672097621f64244ba52b7a3c4ae56b4176df146d3fa0bc4df38d97800629bf1810703bf6f0ae5dfe0f005afb5ffeb1dcdd497834df300d62899278e0e6623c3d0277428165a088aa9263cd1e139ec4b77c287983ad307c745ceffd40bb5398d9b9e34451beb8e420a5da691a85001813350f4be22b907647093f2d437c08cfe9cf1da9a8a4e9b324297235824a74aa78e5330d2217d02798a03e150cb092b3ac56ac2d767559f2015f32e43abedaafc0fc56a63cb5c040e198a622892c175ffcfed9e5f1cf4e12cfea6ec5c359cd068905dd744ad0202428b2af836f56ea219c399b821d9f4498f95d5035f38831634aeae8dae6e2c035ae79289ac9d19be253ca684918a891c0525320ba57c9c43513f7d54565b1d0fd289ae29b0f16ca8faff6ccbd68c4803f22f670d256010d604d7e3dd443cf106eea0f4a8ab96e4411d413ff49bd3594cd541ff19da644631a728323319df6800e9df748ab8b9db9a680ff359a95d75d03d1a0b1f3b69c3f2da72155e6022310b687d86ef5a63817bb6e790b9dc39716dc0be1b68e459389ee21c339d2b18000f928b4e99db9496647572ea979511e65358a5d4d5307ebb5b191f0e3432bf0102dbfb270c317b4f4b74ac05f67f762116d898b070f6d2617e4d2578ead30bbc0d582b8cfece108b39fb15bb564de433069866db8dda9698f66a53b6283da2e40c5d851ff27d95705bf504d6f0288d878067daa499351e6f0fd859eb70b9647b0151ea3c2cc5556a5f6f579eb7a274b94fa746de4a7dba2b6275aa1c2edc3608025071636695e134c5bc0910e684a5142aa2d34b7d6ed413d30992b6a4405def0f9e1ca8d9316a9331af65e39bbf6b782a4e67004fe870d1f55072322f4272a80b117c18ef2c26a21afa9040b045bf1b09683d4914c0794ccb8fdfa7d7c49fda0df51dc8cbbc90a7c1cea29a51737e2d1fb8ffc3cc84dc7fe0e3e203c77f02320441426d496a17449ee8eee3425a329565adf54bbbd1a893402ce666503b817f0384507a85e5982636c3aa91f917c5a8d3b10dacf48e74d97cdac915332d359f0f1480943e21c078cdcd43569a8f075f7853dabf91ff0e5b1d6506118abdc5eb068728c463bcd56d95e4ce5065c7baf006473a29d0bd1f22ea37437198e10ac504a3f0f22fe31269032232f0e2e26cc7d5d28a7f1829f5c54a02ecf64a38d2a206e36ee14d72038094de8784fa2aeb888a5c06ed98589d8b89e94e5b21e170790d202c91adb9b22be610aab6d30ffdd25a6398d3a230de3c78764cbb424be1380246b4e111ebb7a7cc5fff0acca1bdd7b91343616e7dc9babce677d720fe1c280b1f6959545cedb88d38a75631f8d622e174abe27659c49d3298098763f2095502842e1cd5ab0998a3391b8705c98efc895cd9b969279c7cb4b497284b32cbaf0896274733a57972efc69ab1df9801628b3869e99cee4c7c7db27540c10cfc9a0e534051f675cbfa211f8a2474677b099a4f08df9daba596ad2d1804fcdeb9ae0854ab0978660eeffb230fe66d1ad9094e441bbddb3a0c8ada6a8a70eb8e9c1705dcfe908ef521293a92548fcfa81592e270ba67a429bf54fed7c0fd96f7e1fa0c649d9dc9e95e6d495610db26a5c3e9dc23b61a11dc727bc81d5cf9c1df50590a7e1be8a9818e11f752cecda2de0e67d53c60deb953f2b79f54fc320c19a5940bcd3748b4d68e8ab3eb37de60b968aa5027d1333809f3d6e3568ff4cb26cbb70c155b6effb25a5cc9b57749a63e1d9d234873a754094c762ec24016cd0498090d17e346a092751c61fb69e0aada4ecab80cf62aebb04a0b13b57d8b706a4b9f0c15549333cd18c5cccfe3b8c17d783b827c83cce52132c7cb1f064a1dd6b0e90761701b0920b50a30bb0f24d3ab14a884391436f9f179263d3785abb426e2730dcb313c23d27065b5df832de2512c33a74e57aab02ce175c74292050a13a0b60a57c1f0e6fda052c6e97af4b04123aebfd1ebc15e861134adb975826e935e09073ca38eae3cee91f37897e6d486a24490892a68d959d282d86bfdbf251139e206681c673255dc338c4ec9ea3a856ae34a1cec190be2fb54f02df02a208d16330b45e185ca2861695d3a50182a3ebec42697e13917dcca128c860790c5bda3d4016e271262b6ffc07b68f8838303089dfd4714074023ba325ffd33d4f0c28ca905b7b23184ce368b368fadf7e07c7e175faf7ecfb258cbf21f7e7cb26458329b0eee40a40c624bcce6ce83434821f7dac26ae7975ea3e8de96bc7f3c70ca8b9609103dd66558ab5ad322c4a13b01d90b54b7c49245e4048f4e70f59be9cead4603bdfd4781e7484cd88b0d58df5f7f69235b3854f3af05bdc9c6830e371ac1b20f67ad6230656bab7dd01f84fa9914d186ba465238e705d522658b3a6086a2c904f3e5ae000ff85dd8c980f27914bfd6bad7ec72dabad08e5a4b78e119e81bc60eb9f677903da008f3cb82d850a1c022e1a3c089d7b5c84569cab55209506a1b0854733b620ae315b5884b86ffd948805394f84d34e2146136e838bb1ec6f1fa08bfd5731b3ee7c78368f12e2202900c950c962d70c701744bf1379894645b0307b8e546fb9f47dc5e235d65a16ac39b54be58226e28eb37a8a86e87c2bcd508085b8f1521ee483b73c22050c9f5d944a121c35f3077d9cad64572d4780b115c0d46ff41344dfc7552b5f6a295e5ce6644cc31337ae77d0a927ca2576515384a01564f6345734bcea3eb517e2621e5909026028ef781f5229b9e2793cd83f6b800b6ccf3cf24419c4778d85ea9d3e3cb5674d7e5a64d90f00777defbb33909e00f339a88c9856981b8d37fda6b80a15c8c63c50bff0d851e07b2151ae6a47ea10a034cf61bbbc6ffc536751bdc2710302c49adafa305b014a2baa9144871e9b808b89791c78d9174ae76157e6176d46f1b65820e89e2c14179e4670253a8ecae02a02228c0bb63383d2aa0c563ee4a75cb2a3ceea230f5de87694b158638730403e244e304f292d56330cc7ad13960588ea8ea677d74e5db6fa9dd9b4649df4003de6decd64d2eba9dec846567d5d96e0020c73fc5e659ec1b0143a6532a587a08275e603a9982c213cdbcbd8e3f8debeef27bffeadb1025cb3c251600069b2d0f false +check_ring_signature 5a29b57ab1a04e38c2ead48d17b81900299ffb0628917790a26cbb4147690335 8757a8993fdd589530cf7617dbbc4f4fde4e6e0e18d8b5978c8c016341f3b1e9 16 7111e0e034667f4d121be9d132944a7d288416ae75575c3cd73cb6248f6d39a0 3b42435ca3d67908fd26564614b8cd5631410eec580c6ad08e9f8d5c30d904e1 cc02a6fc79f00df82a16d45196672b8b9c55139e9c55ec7d533cabb621ebf9a2 749d861540bb8c14eca366132de3a91c02570e785c0e99eaa078ce6d9dc37365 06d97426c2e109488a53b6ea7ef9ac99b8216863287207a0671213a34e044df3 2998317a346a8d314aebb0b3127e53a5cc0801762d84cc80c4e2fde30cda4e9d 7a840d086620365f8e4d569c07c22625e6292a20d49a42d44abcd5771eb3a420 52d0a45a5ce9e6514873fbecf8c6c068d2fb59a922721230ad4b5f367decdc47 9df45e41c242e180413890dfc1b2e4a24d7fdddd66b086e4f790cb5e550e6eb9 d01e2452d23c5063d42d3375514a4fcb0071a5448ad5b45efdb484246b5878c8 8ededcf821f53a61efd6afa441ec06644e4d0b031ebe7e5ead27025fda70e274 8247e31c5389505d9ba3a872e02630f458a320ba680ecd1e6dfd6da188fa64f5 c501361351425548b51d014dfb7f8e7dd7eae7f44fef49ea89d7c607a6e232dd 19f458b8d116aa60c2aa2528541a8d902073b4abc01cfd1e4a475f242e73cbea 7e1b6265606173dbeba280c6d92f998de3b1f683de670da56afd5ee7cfa9ecc9 4d6fcf83ffbcd5b90fe5e10a30151895585f41da2a5d8358a2ae782261cd94ce b468e236f89d6245107a511282d8af431c5cde527ef16a6ef1625da688675689900e6d2a639d6ffc6c2bf417d9da6a167222ea18a2db591ae85c3a1d5480902d75d52d07ef3187cb14e3ee3d86ce19b0f99d9e626b83f6fbdde7b761e2d1ce047698cfb11795288ce39ed1bca4459c14f813fe522e3242bbe60340ac2aca0001e069b6771ac7d66cd6e74e09a41c322441ff6ff125479dd7e1227067ca14510fe2967b89545565cf4db2528010047d91b5a1e2c7fe32718046ffc27c7e5d8d0a5b40781cceca7d9b944f0285f99979dee52f367685551cad24b0af685c567c0b26c24a43fc0e4d691a7cf4502a7e4a5e57dea38000284c47da0c2cdabf3ddf0385bb99f703b792a5edc6c5efeb1b63173d048d7cb86c82ef2b7b61c95356430213203decd8d5b22746924ff1d7d578a3a5846b2fee2d5e1538f49c6045b05703340aff1b9116929fdbc552a09cb59894d0b1bfee89ee3c2717326839a661f6090668f44861a99edad518be4646c01ee30bf1517228ef4d55096b45048f2e830d238eac4c11e279a673f56f4aa6d5f249ee9fd82d1dc29aa1210f5c8787bcc000ad87fa5639d032b41d6368422fb59473fc7d9120915ac3abfd8a1ddf6b3cc10fa47bf64ff7b5ce26059550c20c82068ec72f946c79fb4a9a7939526614737206ad73b3c763f2af1219cacd04989da4e46a8aeed90672980271f75d61ee4af3031425f841b6e2528ad88e2f79459bef1125212ab5b89dc6b2a13036cef768a70c5c5c4ff94e49678b406a5895102fd8ad7407ad72e9ecefc74194f7e358675a025865c6c14e416896c10e25d73f37559a0371004ffe79a9759b8daf8b4db8740c309d6db661fe8186b954da127d45928f5c3d7697d77e9db8117c6bd15fd02f06150c4517dffa2268d31e96a3ab8118d354bb0f5c3437843f1c7b8323a353440dc8111eec0f94af12feb4a6003fdb6cfe509dd2380bd9e74cc2fe150a1805cb0671a6c4d0eb19d131c7b50eb9681621804d1c6afba9d3b4f19db850287c958100b425bf7567a9a7dcf403a3f59c4defe20e2b087bb3189d3e3be258ec815165059ec864dc4d9cbe631fd913a7b3d87d16b4d203cb326fb8e873aa376cae99c90d32a1aa43cba5508f22c99c293402aadcf7f26125772e9fc70826006f72a9e7027402e55f776c36df357d75a843cfe322107210129935ea62f883712a4102c0009ae1dab3579c8050284cddef51a110c534b348bc86f9686ebaea1ab2bc7d7209caf76fcbc05b16ad0a4fdb704082dfaa2235b98a2e2fa10cfecee936f1134d071e62095ac4485e77f95ed0cd8b828b3f34d3702af3bf047fa08cbb414d51c60c5e3a7a98351c4735c2719f890f04d70474f6fc063d73c5a04e130d4fad8cd20ffd35c89baaca97483db88f835660b11843a25fd786e9389c60a5f77007c4a90e false +check_ring_signature b9ae809bf7fefc111277706157b1994a57c83c145ed0a782e7e60e5a0e9b4053 6c61b3f598a56e8f5c3b666c72ba7526f8759fade8b97622d04c404cb2eeec43 10 9d1e1acb744555a5be7a813c6111021e4d2aed86d3094442de392df4ccaa2787 04d8f9163ddd765168c18578fa336d7b4212c988bd72d829a545f391918bec94 c3ac4089e9d7f250a4ee0a51a961470d97726aaa3c9197817e9f696a873d9308 b663a59e15193425263d3e5192d6d917b74b5e0b00fbec7ec1240d0ea5b50d1f 75d87ff534fd02971bfdcdf77e3caf17873692676d251282ec434a1bc9442b83 468fab0a227053a08b9aba0dc512951b824aba5e1dff47b7dbde3ef77d3ad33f ac53f10a7e922c17f7fb5298de58f49a05f335072751274e4cd442126fdf2d25 6795f54bb429376b6bbb6f272797b228c91cea0a81c9731a7a068b8c28f06447 0c4c452b67c57fa452ac0db3e991c83a0094295e162a543a059fb753844aee34 2a96834924347b1559cf0e3041e86058bfc4abae17399667ba71190420320375 8ec9715691d728ea5272cf012c3c8a59cbd98b22165e28dc37f5b66f979f9f077e998a1498ab1f040182265ec95518270aab0a7d1137eeb67eb42dad8050f400579effd76049ce68ceb10f799e3c57e306629505006acd4c3751add72095930880c2ce86c956d267566428cfdc7880686aba59b6827c7f77662ad8a4d8dd042ba8c5d57ec0387502c5e1607eb1a36f7ca39cd562bbfb070e69a4235fef1fad058ce72babc5dc043d52c561a89548c575b1c5e8f1e25c97104207d69e8b08db0846530589f346363ac3929f9922b3cd732972e3e0eca70cebf06aed06130d11082fad535f0994362594903aa03bfae71536b0064db33bb2f74d9dec2db0e84416253c139525230452b184d4772fccb2807fa136f28f8e4763a32d1fe1feb9a80f4af4449fd8b1aecf57b174a3cfaab005e91418d9098837eabb4290387b4df00e1ddd87d49921ac1405a001daa69ad2b868da3e6139ab475586e92346d66b4e03c70001668566ff32aa5070f8ff8776ff0080608ebf35752603a47400cec0a2073f59d653937b5d2e13fb2f12810215f9447ae67f07ff2fd1ddfe7008e852dd03aafcbb1a566b5283ddc18888ea70fba486c8dd36b0410672d8e6166658c6cd0326b56732be6b065341494d545282ccfb6a9c852cb6805fd603afb283fded9c0df22843f6b905d39c6b7a4213d69cd23d2fc079268c17dbf55734f8ba3fa8ee00dff39a0f71c2659504947dcba599fb7faf1d6bac3108e65c43c85d7837ad1f0f3c8f8e42a2ee49524ed2d02fb863c0d7983e405314fa6194a98990769f040504a4a73d0ea9abcfdf56a55ac69706f2f28dfefa7226566736c3a1166319c3b90b5e3a85756a673587060b645821190d8ad5402c592708d0c0866ceefb36055206 false +check_ring_signature 441c1c1e4712f8b13ebd23a82282b225db4309159c939981fbc27bcbfec67f30 c21b9ab2f64e0a715176130d87b8dd0e8a2da2732a5d98ed1ebb4ea2d245c2ae 43 de6155fa54c260fe81f2352fdb44d2dcd7b1ccb7786a96ced9f872da67d7c4b1 c2e5c120a1650d4ae543360bbb6f89e01b132e6bdf9e2ce1a600f1b49f411980 3583436b840800eb85b5bdbd3f0a90908edeb974eff8c9a6ee95962be202a1ca 5290929f2bc0f2d6e3cb15855158b7ba480b3273aac50be47ce741697ba26ad0 7f7525c3d30c5f829faf0002175888a58103db911c94a30154f03ea32e40393a 6db2a99f362e513ca3c3fecdbfddc016add953651110ba0d114c10ce39aa4a8c f62050759011d2f776d91453af9af6aa17f60608a75486c3fe7e60f2021b0a23 99495b9f4ad4acd6a8a873a5119e2fc8b29826c1f9a9a266c63cc4bfbced9894 0ee0ab154c92c519b60f8e100a75f93a3fbfee5b47bcf56af4f82ce83c09e92b e50aaa5f4c5b81a23cc9bdf85e7e151ac0f758200940e4b16bf748d36d9ed599 35cab6313d97f10cc215150850ef90ead36b9a84b06a4ddddb169fcb104b33d7 72725b44e40d82da4dcbac3f7ed98248e81e5c64835fdea92715469d57a1bbd5 fce4d1fd02dd159751eaddfeed72d0771f5ad8f6c812368216371a1729b8ec08 02dfaa8c213052bf70c3c55158564da70ff46a7ff4af853d144c5f81a102b9a8 dd653384da4cf8bc2106bbe2cd1d3aaef050af4790d623ef24b4dd2d016379d7 1658dbbb791568114e1e1632408c94025d3665eb3f9d6ac60d6e978f0e1986f1 66bb2f2d068bf7a87f6bfdf8e873a955618538029d3d177d13d1bf9b36f8a34d 99b4df683beecf2d95394b499cf7e87e7a3f7db05e006b16784d1fae9cb19a6c 0e00135df89bb321d6c8886c94d434c8fcd8809ee05d4d17f5eabc311e9b284a 70efc98cb90499c032cca3f254104764c7e3a1cd9d434119bbe4b664b4fa6a53 5abc3fc5384d45f5bb09097843f6b49ee7942dbd8d2bd3bcc10c76093555934e 4d9f73c7051460033f357cb244900608c2ed1aa3909f27e6dc10b0991119c295 4793bc5eab13863b708917483bfc5a19b3369be3114312289a8b1e7a432bab64 37fb66066f4a7779cd8292272858f62b7804ff64395e11fca66cc8868f0d9f3f be1ae295527db8a9e0d3d95e38cafb40281ecb0052b381f195aec0053f5eacc8 5c1e45ec1aec379648d710aa8dbb7da32b87405de78d738709d9af4570212d88 d4d662181a792f523877ed7157fe8c78bcaeee4fe069a8562518055b9fcd1f57 ee475c0efa49ea3df0a96faea53132d9a0f500abb2625b4c170d6b5e47fdaa19 f197a92ce0af82a2a4f60983e6f8fedaf343a3541b5f0a66f6f50ab20ddc6693 8da74761c5de557f1f35fbd0456bfb2c6ffe17035532c3aaf7154aa7efb3f5d7 00e5fd7e339100f28065c77352d040f293a9ad653c4bb00a569c92da251d20b6 293f7c00062dce727b0338fb95f079fc11eb83966cee4a4a4964ff156a6266c8 6e808d15dbb6d2b252701036f20e707dc55c6b91510e356163ddd3596902bee8 1b766863c8bc8b12112b236497e1f599cdc57a6e41ca1a10ec7f081652324a68 6982c3dcf978b4298ce7644887a1d1bf423d3d21e45c19f0b9eb60e724196b92 cd447592ca779dabe4dc8784fa0a9d11d1c8897eea6204e7f720b54d81cc7701 21e030257678c54842f64a6c1335d6316677b73f7e53a390e4e10a9de4363bfe 5cc65814fa5ebef8e6336fd460b9e0ba18db0fa0dd510feba650003e67eb4ae4 3f094c281955c25927d9f252abf415eaf756c35481e9ba6c500daa753022ec65 fc0c740391b743df93c2a3d76a3eea3ed7c3ed1d34237b06f8edfd53b53f19b3 5cd96bf971803f8bf71623da579e23e070a07efa85badd2a4f8cdcee767657fb 71706b7c932f50ca7f694962410ab3b6a81260f474250dfc11722e2f36deed60 0b59435b14ac60244d7eb0dffc50cd2d89081bad05bbf3c2447b8a81c0c81301 347542a9eddc6a4e3ac3d1953a476b9bcf4ebf6449a26f1b67f3c7fc5237ac07f61f356240bbb90b523b59f4cf911a264ea88de49a49df561986f7037239720fc8c3b0fdc398e53490dfba2634c0c05e5840f2690da0c50d6bbbb9c91bac85091670fa8428ee2bb41dd4877914cdef322a0726c99fd8462ad7b531d123091d03662c7e55f1819a3a1aa6248e75949a2b892e4ca24909f63c0667ebd7d9db53055fa42d6555d8b8f59edd5d8da747ba77f593d49f78a8b9aa827ed4dc7e3bec0c6dcf538bd8831f3af8d2b976de2383c3ac5dbeb671a4ff14af0f52f6ab0b5c039b87ee52ae7831138d8eceaaa667686b1481bb139d1dbd41cbc66c9f321d50045127905dd94c2cb3be47165a57bda8edf795df1a4b01f443eeeef05e9ab35002649e60c8cb6cd2e7af83b636d2e1d8737ac9bbbe8f82ff212ea56628fc05cd0e90989aa5fd63090e268f89ca0103d6cda8d092c5bac3d733e1a742ac67945f0a162d6caa186674dcdf88b9c2fca774d301f73da7e3a0cda5721625781188dd01a7830ceaaba6a4332e100e0911dc3426e49ae176d6f93b639ebf363ad003150c721c00d16f9aff052d0c22e05d530face5464027a164d292ba521ea8c4b34e0bcfe66267fadcb871ff49c61a88e33f67e06134d11348e429cfcee920e4da370a7949ce2cbf7e38160acdecabb70e52ac895f6d21da3e9521ed745b021c3aae044ed709d2ddfab49132ac88caf970e8567ccd2fdc403ea6039c27295b1956400739a8a1a81318fa36c461aa292147597612b5c96b6e1e3b3cd75f3a2553bf7f083071641fe181d703b559cd7cde0f3082680fd8df301d486934f230ea19da6106dd75b70cd58602c53ead080a3d69e1189bac5f9c85a636b665aee5d52e26bf0d8327170da9c31586b4cddfef2e5b8999a3b69748806e8391cbaef1af1d4e020c3074315d6efac35b062b266c28fd9e57273f7d6b51847c371159686936482d076d42b9fbbe9cdc37d5473d16902f7dbeeaf5b347b88f214396ab43c86175330676e093497243435714ea1a150c2fefcd37e6fbb42e2ba3d28301bf57f64a2c0cee6c7200f71ee0680f0b9e72c7ed1f4b7e27f2d5e71beb942147758ac39247081c6c187f85a7d48bb28b0879a6db2cb9e208fc9a31758b8a0aa7a9719e1dca07f5c2a7aeca7db977ee27362840cda93aef226262962f04d3003eb7c46599800d1b24cc0778d5675e0c0889644264446fabfbbfedc9e52dd66d07ed8fc697de06195214fb5eae577a6a196e9a1827fac7b49a0f7e9bb9a06aa8000aa68447c40c1e3c55e2d9e507f7b8ddf2ad85e8811e4417dd558ceb6614fda896522e11a00116d4aef689edee88e0b201ddd8ddce5527a7c4efb04bbb4024b0a95b9f74280c701440cc2b8a7ef7e178dd26d15786867df46bfdae73bbd2dfa18b61fcaa1606c221262070901ec404b2a5e09f9fe6d0b41fd6aadf0231cb0b82b1fa707a710a57beeeac2fdf9c16bb9675fdce8182fb054dc835a2e0e6c6ae86b2cdc807790bbb2e18b482220bdb729d36e65697f31884db362051f42b467299be8b6a01d5039ba36185a867a29676ea067c550f52cf3fc66f86974e39b92286e64d693c4102af008c3a271632c493b79915bef534316df4851c4db4707e57c9f7393726c30b6823d0bb73daee572454fd31841a5fb5d91dee8d2a191c85d97cded689ef2301757252f16db5c1c601904b2aed230ca8ca88babe7a74734443d6ebb9322fdc03e8de6ababdbfab12bd32d2c472d251dd027df2673b6c8831a18258a931e374057b942126c0578006a34322c5b11a26d53f5b39bfb717c13177ea40c9ad1eaa013a43961ba2a5e1f003e0af892314960da7658d870a33e43e2b2554c19497500c894525f0c07f8f588086b8c6430162f5cae70e963865953e5f51e47f2a43a50fe0d543f3c1acabb68236062fc34393628f7c2ba876e497f618badf960ed72f0d6c7ab746498f45bf345c7eb2683a1d2dcb3660fe12419dc3029052388208f60b99796e6bc398a0844ec81bc278240ece3344bbb9299bf8d8fe72a8f5d0511a0f59f00d5bdcb3dcd7719b0ba022fca510d4823c08a7f55cfa35136bf902b0a6080b93712a38b6b0b86e2c4eeedc7b6b05260fb6a351f63e227659a13ee2a55d06db2aee0a9e57118cbe8327b0db081bf052e1ff6c50f07bae7e5779c69c3eca0ec4380eb110b87de4586dcf45e49c6976f69b4095ce49b952018926c037fd7e04585295d7ad522d95b7a26bd06621d1d8f6bd5a98a7b2751315961c954dffb00d5e7d1b524279a8faba3e03ffecd635583aaa125952e4f85fde2af40261a9780ecc36a2888bbb2689741d7c32532db58cf38b2e4d2526c7450fab4d3b6a94110408935aad00f6dcbd271e39566bb129677f45b3b6cc63b46d2b4b1e81a16a3c05d831c964b6afbc4a82828b2f95358626541211a2551611a0a9a1ef35e23eaf0023b3ea190eccebc3a797ce0698e4628146ac7c1d38139157703d9f7c99e6e1070f941b4bce9a39191ff2bfa0e45dc1529ca18cb16aba55a54baca972abf9bb07270699eb295820004deb3b1fcf5c9aeb9e90e30d0fc451468864106bee3d100723e1ad429f3c7789879f83a89736820260066e2b59350091db943dde49e422026efef7fee4303cbe02b131cd0f5d8794f5c1c2ed2e36c20c8736c62035d374bbb7bc3cc9cb87a2b42d3eac6aff300d8e52ffc14d4caf0c70867c04dcf6223d0a6b954c4570789ff7d0b27cbc7589e73338ce479135a7fedc9cdea61169dc590d424fd771b73907b926e1a7d4d173a6a9621059c2c0871c4ec931592e152bca05933d108e4bb125f3ee1374d9de9faedbc88ce3d7b5fd0b421c7bf8e166f2c205eb07aceff533ef74e908e27d6461756c5b5773df7539c1680f2b8197f3e992037ff875b428410e95e6d594f8cb30117561a04875bfbd7a83736f8baf61b43b0b10895ac3d9d3513e3cf8d02b31d129e9c968f2902352912014f492c3ccb2d90abd71387ededabaea573789464122422d249f1f4c7dcae5bd59646333fb95fd0d70d9af8f7829960d72dc9db6c29dcae8af967e4509d25c9106746681ab22220e7801560135db03db024a87da0c5a97da6b54fbab37a29740bbb9184b3c80e407b9903f3b40187da89220f86349fd19bab6a1897e4f8588a4a49bd8c685f6ac0c2163fed11411885d87050ad8c17e404aa141cc69831ce7bb1ceefc52a5a67308581fb51b2dd2c460cafde6ab1a786e0c7f57ead0389491519cd7b8753a27db0941398696da6b07ea87da883dcfdca35fda4b6e0aa401e847639675fafc80e604716dc8a88627d64224f2609daca2db9d4cef56c3daa99e8d304492769cf3d30a46105881e847a2468e75eb4759c05de87ba82fa15374e7eba46505e64cedfc0c6a5419ef335824043a522fcc5a8001adae913a40ddebdcd7dd15aa226eade20da651403eb9cb7f76ef57cc08fc37fbf46b95a0e72f4f3da8c478b8dbe33ab00b79c9d2887b846898401c1d3b0e1bf6ed8aceb318b9603c8348d7bbe21455b30ba870b8343a5db465fe517a971875386afabd460bda6673e90d5bbd47605eb00e5ef5f4a6adbddb51b48f12df8eb372807835e7ff4d14b3c72c02d4a6204d570eee9ee4dc8d0a13da4d54bb96c513e17dbf12cc464b2627d7aa56da5d0cd7ac0e310a6e6b14f97d5c64d880c1dec250e9720934f460be511b688386200b5fb90375e5b0dfb47eaff2deb7a1b3357e0513ec609ae96f7bd4f499e2ac45732aa106efc9d7a516d2308506f520c0777d176cea7b0dc03220a5ff7d5025c455f25a0a54c9ab58170c21993c027bdc643e64013018fc368355c3e1ce1b425ef3bfae0a false +check_ring_signature d1538f293a7b661544e60796080e2ed4d8cc5cf02c00301092e3eaff44dc09f6 1734d13dbdf1fe611106e4291f9cb090feb25bf3a4e28cc8e37c320107fce4ce 1 f4178764375f2f6d7819214112bc27b39231ee25af20337773908ce4f7453c54 e9b8c542a3c309b09f930da27e52c11217ec65bd54baf0777a44ce05843bff011663f262894c5ec6295624239c5c6bd754bc903ecde638fb8f5cbb2392513e09 true +check_ring_signature 0d976ba9ba58fe9e2bf545d60c2028e6e7920246e239cb3926ab0372b7ccab1a e8646f8d1397aeecca6e0bcf212d3be5227b87983639f374d35482f57f6abdb3 1 b05af31ac994af07bf11457f26fa4bbaafb58ceeae515a952783007aae1957be dc7bea399c05e7d91dc879e51775bb5b0be30762eebbf38b1cfa8a62249f77072dbd71555b76131acfab58ddcf9d3542893d1e0ab0937d14003fc46fa9f4950c true +check_ring_signature 3b67ccaddbe607f4d4dd5362932d8010496ed22aad0777452873b4949585d9a2 09f090044cddee8719737b668ba114039d907cf01e7158b55492681de1ce11da 1 b96ebd72052e0d369441283d422a6c27365eebf7369b5ac77126c071c32efb6c 02ec605b330431e8c7b02e8f061e0dd75e4be5b7d96aedc48c5025492c0daae4cd8cadeec315969266554ad7ac301ad419e3d441ec1f872c9e4e358cbc60b73f false +check_ring_signature 6503fa351d1bd30b9bf58101a22a97a8530d44fb0a69a97ec3843f45053a2a83 5214928b4e8a59d25b46f705da681e88b4122acaf2ff8bf1d4f50768a8663916 5 f2a5bec33129a1cabea900029465442b84f48f184dd73a56e10685717cdaa799 33a277bc8ea953b544702bd6e80c55c7a28ef7124ea639bb958223bec2727f39 6397b97145b2565ba98234e491ca28721e34318ebb760f925f24cfefae1d3750 f85e0c4e57fcaa43a77c86ac53de976ebda8a451197e391adbc4635614d2eaeb 306d55cd3e5aa590d2b3a46dc95335de026b04fe635b716b9f73706704638951 5a88b962b77c2e4618f9384a1c6b29b837d6ba5468cd6a759349483a36697b0d1636c8d35feaaa9aa22667caf7e13e6fe5d2bfb84ab8c8d56f7a77fd19cb04066cec4c4216f1c4b810b837c87a41a695c6c508ae80dfbb90292736d0f393d8077c0930ffb6fa4ab367c9585d1b6ee23a76c04e8cdade9bd8113a2fffac2d9502ceef0193467a7948311163ded5a5b06b3a9f07f25b230fe8caf3f64ea6b62b0ea5244440a2dfd08e3f34b5949deb2592a3e8943790d9be58ac6bbfdfcf57130095060ca0bce5fc5e8b981fa55d023385d3969b2d32bebd244ba355ab1ecc1a02bcf6acae4d0342f3e272ff85f87582f283027a8c6f2e44c86dfd3695a5ec33037b5b2db7bb55917c223013429b5429a02a3572fccbcc99b90e713b8a84be5505f3ae8d800292f986fca28a6bb47ab6dff87a97044ac61de92fcfba839c69500f true +check_ring_signature 93be436d79ff47436494341cd2068b3d9e5451f5df8e4a4dab23b505d7495ffb c7b9a837b1cf1034065ae7fd7820194fd9dc024a1efa2ce47c836f1868fda076 2 3ad6ea5310613b0782adc15fb073c094dc6a1c7b476e878489ed423da616b874 bc4c33d376a3f9b4d64c048ff031694f5da3ad7071569831d1f56b81943caff9 550a832e40c0a1dbbbda85dfd5de5869bc4566e5e4a2ec1f067abaa5ebcd600e66dd4e5347251551b2dc70105e6b808ab43e4e0f881289ef0a46d692d67e73056ee10ca46b22e15ff23dae72bd61a4c5379aaef4994d875eae9d51f7a18a12014b85766b9d25a473a77d3fa6f00941d7e0f284cc6dfc8c26b42f8f0e2222e109 false +check_ring_signature ea84948d9af917c3b0e9eb84d934985f406f8d6603a4b0795a98c47376664bd3 5a378882419e76da1f1ba0bf4594de1e5b18211783852a88eb07a5939b14475a 2 457264b79595fe54e0c00bdf6c8fef7d0cd57bc25fa7461edb81a0287b85256c 480b04c34c1fa175b6383478b0a66be6d9cc57d4dffbb83865fcba86bc6dff04 2cd660a2d176282f799f8888e732fd3f170996041398c5132c3af8760e642109902d96a5056c106c653f52a35edf60f7f91b0949eda96fad0b752ba9ad8740c983ced0dc4f3a08c42f22570cca4617227ea769e3ed6233441e579fb346dfd3022bc503e82650d1e617514111a11d8cdb71ba34baa0b606ef2cef3d21800b620f false +check_ring_signature ca91a7bc1652badb3bf91ad8bd166a8589c1980d64c7b4687a981d2e9c87c701 961d7f622b76c76099d5225a83c8e2e839f7b54502a107fee9c5d0d00a55a005 126 e77b46204c74904de34262d1efbdeb915dd743ff5dfd5bb08001f1b9dd6fa421 aa442c0df644a0bbe1d3621adcbbf3671a65598afb940e0640a01ba0244fc3c1 1cff256e34d87ef6029e6672a5444b4f48e4e6ab1ee81b5128c0b05cdd3b4476 09a111424099407e36701caeb8a11b704ccbf7d1973b51ad3b2de982823ec032 fbecaa5607a01c75ef6728460a0e20f0591f7fafa88bddff796b1f77d18331bd dffb44bbbe6396bde62e8d0c5f82d777cf3766984852b516d358ad0206137fc3 95fe6bdd2b6921ef50dd1c6b274a2b8994f2a67815c1603e86805ab444bb0019 eec5650a4ae5315893b72f9ba437905e3db0f3087139696d8b6c4b1b63f2501a 5877fd10f7a79ad1346b9e727523a2c5677f2f71ed56e8711c50013a7ddd1180 1670dd9dbfe681f651bd7930e69b3cd7d648c22ebaf1b68d76ab36eaf3366293 d3c40a023bbd2f0d6f953bbf3c177eaa7b0d22930c149cbb94285eabfcff87e2 5e1c623220a376f8a999de5c2b215465f8d621d8aa938203b2bc5fd3c6e644ed b80bc13691706d892fe4b579e1a4ead1412c711f23a7215f4eb28c5ba0685ef9 77699d031334157a011215f2f69d5293f8cc4a4448cd9147e8e7f865b19c36e2 12d11fca86ed457ac457071365e3da3c9a90488189dd82660b5bf72f77ea2e1c 893456af5d6e531850be2455c380064e7ac242f1bfec782b162dfb2e1cfe004b 2dd351858ce8406fd5ad1069937ceb0b065c6266bda5dbee252d0a69933c1c50 e2617b0fc3272e760b8400d34e26ca3d7537662579c1a99ed77e6c46972fec5b 621c0e1dd91a41d47c0e6454ef0d1040d5846807d9c8bfe86f942aa89d341cc6 3f59d58dea565466d224092857f79253b65b63d186b7226ae8805affc0f3359d b4ed5c7c92f126c491acb0cd70c6b78adb2698b7971412062f3adf98114a17dc ae48ab501d8b6f4cd45eb1a2f3b4220b4bdf50a9b007e4f64a7dcab5db51eea2 7405d93607162785f0d09d54fb8afdb521b8140d53e7d77c0225a002e3a98c44 e1299b67a9fcdac9fbba480ca761d0fab3f36501ea1e51f6890461353ab5f753 15f089d669cb628d213bd8ab9770042fb064305c8a247afc0960394ebfcd8b7a d477ef83ffc73208e2d3cf69ac6682dfbd1d4e516a51dbe84c4eb3c3707ba105 a0483ee0cce13d6d38d7edf593f3701f2da8c078e8ae425e1edb158dcbdf199b 319702328dbe1e9cd88d46e041d90470d5cb6503af8bee7fb11c8e35a693c5b1 f11806f4d5dc94b9e3ed2a66b3907435b5283739e9fada599c16beddf3eb5428 1c95bef1921cf72f01cbd1e20c4cee01d73c07e826452044ad9c1f3431c723a6 3da6c13c3a07b610a347351a2e8a672d40cf8b2cb4c35984d1dcf80ccc2ab8db 2636401aee1cbd8df87085ac31af1e240ed5a65955ce278df1ab33a53951539b 2049cba16099f1acbe5784c80ee81b10f276ea9e0f150c46beab69232b457c66 a87f863ad72ca7c45f75c0d871f49712e581c151c1c34c8c515fb0ec191eebeb 5acdf93e88cfcf079a286cbecfe3b2ad8b947d3ca56c7e97453e1acaccd4eb3f 79dc4f4476dbb6e999e839f4357384338079dedecd38c5ea11d28f7f0ce70260 907f1ef39108295927217ea09be5456c021e63366fa3a13899055082719a62b1 7a2aaf612b78bb94d7f20f1fc07fe8bdfeccda0ff08656c7ed478f0217a39d05 7626469ddca715a62f0c4cfbbbf5e864e160fdeb1bfd883e2212a906adc7ade3 90ad90b5fd47fd3d7ceb55b3df1efd0238aa91a9b2a062fa9249f753bb7c0b3d 3deb5dfcbdba289febb4df4f3c3a209f213e188ffb9b349e53b2416fb234df0d 75802bb80f4d3668e333ca92679940c7b140531cf26f786410e59cdf18b12627 8f893c29714c0be685347072de480d9355c8354b44b1b5d2e94e08b56aef0155 2453fc2dbd16a3c6d87638e9c487159f8bdbaa0c556ffbe439b5587112033734 2017b0e006f402c3939613e912adecff92325f8348105eae6cd82eb02d840c34 26332fb81ead34f50bd3a6d893d2008eb7c7370860870dce1a1970b0ec0581ad 49fce4e885a7d007611568f626102bd4ecd078f02bf1413cde4788d60655cbe6 21ad830151502f65fc67ada14c61145a0b744e5b6cbdd0d14a30ef3866445737 e36864acf3b3ed279e97fe213bcc3dd5293f2ebbaf084e84f769e990e2e5e1f2 a4e6b3310e12f05a5f264a4d4f0bff83372d0065bdc0d256d8db545ef914d377 35e9dab811101bbf86b4673802494ce38c99635893a71189ff1cf71d086268e3 da75c2bec9511d3e940ec9f3dd7d7c00f14221bb55a1eab290b71b00222cae15 47bb9d9708082c9986d0dcf00ebca390094192c37bc1abffacf89e648c864012 099aac312a4bc9a9b28ca520ceffbac8c376cffad22326643caeddd544a7bbae f6c0b394249c5c3f1bdba0cade81a0ad631f835171ef981c8f4a5c3d84e7a2c9 b540cfb809cb267f25381e7da8e22c9a3471c3a132a9d857c26be10c9a4c1066 fa67f702a020ed445f27e27dc4582c03b10bc262f9c21ed384472857ac3fe91b 47ae7de93fe366a68c31de1db2d496935690cb9badac16c266c6ebf21ae0b472 045c0bdad4108cbaf2f3f3b2e13bf7139f25dc8f9bb921c3b6136a33f836aeac 5e88a777c9187edd0c353c6560f250c6a24dd8c4c9b309daff7f412446c0023e 55b060ff921ae38146bf58a1ec09a7cd0cb082351d88e0ba3a0d19bdb9e08aa8 4e00ebb4901b97f383f5294212237070772ddf4834200b8375df91eda7c98609 729aead4474ad217233a862cee03c5e4f5830139690cd010660de43753423ccd 9ed9cb30a6032df5afdaa932c0548935c1593f9486f69b8fcbe5984a824be16e a08bacc1b5470b4c10191852cb4cba73b9182b6fb48e2deea0812cd9428fe3a1 cf7b8a2f11cefd44238bb38a660e92e37e4a106d62e83cd41927dba76abf773f c8baf1092f11206c001126ee148235542a1f4ac6b72d924ea924bb4ffc5a25a5 af8b5a3266ec5b009d9d010cb8623efcf43426101d900f322e8be50f6afb59e0 464bdaf6d4807dd686bd9373a824eb8dded849955314eb158a10a6a9ebb6ea3f dd46796f643d9099e7ae03cf346e2a93a290523cd30afd3ae4a6f239f5abdaef e5a31e1d0823de1c4b0ecfc870795051754eb1afc15df655522d449e499960e0 835e32b7fdf4521bafb3e8eb82c4869bcb77532fbec4cc6a5a5728cac5e3ea27 6ce492ba617d9ad770e19a3fe3ef9f3dba36bda5402924550b6eec51fcc3f030 9a43c7ffd6a082ee128f793550b927d9ee1448124954deb44719ad76266d63ea e390731c3f7b858bb0ddddbe9685993efdf8a9afb457da4fd6def9c371632546 7eecb18d1b35ac533cfecfb0b7340f3f0a2636a7f0c1bc86e04c3e59d3d71007 7300252271633441e26fa48a82ddf59aa98da07ae368d39355452ef0d79f0e76 6b0275fe30ea743887b31e7fed5e26b6f2cf433bf4e6067a4306f82ebac283fe 6bc35a6069d65cd2a5b41f0b7cd75c0126b44417eb3bd0e49c709e7c792e7075 e5dac05edf50c46f688d416d3ec1147b595a8b267c55a47888926160b5262e6f f986222724904d01fd0afba70be5eb663f54660a21dd79b4ac02db3cb85419b1 a4e3354dd301c502463d9c37f65fb62db2fb069b7fdcc9be019e1b0b52e3d17a 79d3f608012f1eaaf488e818bf16912a18ecaf33525551e1a053fca493d46e15 ca8242d998824b287e2868d84ff6fd7ddfa1ef9cc9c202639f6535e66828c3f8 db2864dc94bda0ae6404fbba7f2ee33363aff57a09867ae65b4ef3ab145ad627 04c82b2cda3059b61602ecb2300324d89702a867c4035244f18c10d98ad22ea9 b5a36b817c7157848a1fea0423b5dd1e535ebb85d1873e3d4d09b36e24f9663d 6eeedb3add0d9d53f5a4b3818e6d0e51a255b9fae50f11d82b59cee5f30a61eb a5510468f0ae362df70d72ab1d0776bea1a499c4ab280fad2c5b0f10e73555e3 84b4e25c5c450f76955048e73b718ec1e84e67b33a1345ddd072464cc3be37f4 29ddf1d2a6fefe2fa4b76b981c83e3145136767d0a86dd71cace5af65346803c d73041760c37cf0027339e1d4a44d5dc1f834ea34c1f734e4b7f626627428eb5 4fb1eec3d90b9ff728dc795a8acdd931ab73cdaefb6459d5d72eaa6b9ecc1ef2 042e21cb2839c6626a4a10454c38d9eaf3602d31fd6299b4d1edc264f238dffd 18b098cd05e7f6920092d3e690cc1a0cd86c5991f5bdf103bc6961b3c6b4dad9 f8b3cb3588a1e96bc95e6281686f4bef5ef15d31d8e5ea708cab253ecc980ad3 88f3e22f9f03207026485cd7662cbc69d04e28889a818e66deb8e32cfc0c14b9 4927b115b6ef56fd44f84173627feafa98809d7ca682cf183fd4e5f622acce75 9e8e3ce93b3678d90efbb72ce128ff7c99d2e9622dc7d0c7ce804ec7c62e519e 331f2f51710571663cb788fd1808cf9eb24e3bd8e5abea16195f022bc6525ad1 6a26b609acbf252518696dbfb847b895dcfa3c2bdd679f62aa386aa3868db319 c07923cf7eeed896ab9a772ea29bc8e629c2f82702034a97148f42987d51741f f788b79335d372787ee44febe6c6dd95cfd6f9b56c73abc08c4bc059b37b53c4 fd8de70f18bd0fe784abd9c4692d99b5a1de17a5f42b728413b6dbcfc68da1b3 7ef319e282ab905e464ddd22c264608d1b2eea5f4dbe358d594a5740c1841e33 a86334765fcd2a6fa27e02e386bd500eb927bf73b825cf241c71461c4e7481f9 ae248b7b4bf0e301c02f5e5bfd8eaae9432c6e23bf70df9442240ab5ae11ad0b 7b1e2048ab7e4b0f349148285fd2781597d4e8d4b5151ee9a7f6d7b4ff00ff7b e068a41999fe534b5a39b7a7934373258aa9b363eea4b366f792fdc1bc3bf53a 2d97246015750402b530f37a9d71b597d5339c6876443302b68b2befd360fca3 7c62e5fc96f45a4cb5d304dbe9e6a0ec51e0a7a6d932941c1f1fdfcbe2c275a5 90a1dd51a2e546432dc3142f178fd79aa133cfdda3d609613f3655e2fffc1a53 fcf46b31af3c2ab6b252ccd05c45bbeb14167c0dbf54be82ce6b6a94c6ca5cd1 14416bc3c1520c80dea23456c10903c62a6227861ef45d6b767363da8364813b c9f28a7b1e3dd69b5dd235d23941c37f049f974e9a0b96fe77ac36099d71af24 127c53a99a71a3a4818b02a01b3a4877082efe2d488e0724a9343a192812121f 645184e58d66811162ed259e98e2321c86cb0f054415c031e0e030382803af4e 1e560fb07d9cca25b234e2939201c8cb6472cf1cd58fae9bd2ae5368e5a2d61b 5f98bec6da04804d48b5a4135dc2b65d756470abc26ebcee9f12af3becdde241 969af0a19ead2328a6e6a98069104b7c9ce59e90ae67bcfaccd33d7929b5cb99 5dbacfc4d1666595e5239e1d1275a2e2e4bb20d6c21a2dd5af992f24b4b69caa 1f6ca01d4e060fad1e78384d3752da9522f6294a2a6b4b1e74e7c8e777545562 22a9f1c50892d34f3ac66c37277f90ee39afbb37c71102c529ad50c62eddd418 485016802a2ab649adb6666a5c07312d69f363a1aef31c1cb1214b3f388fb489 293610713f494453871d2bab4f81581998bef1142698061881e64aaa8555f597 89d49ba34a8639bf4a332267260957a3a235995dbb91ecd4d4b59b16667f9059 8b0419f4bd4163630fd28ea7d43ee3214294f8af543464e063addc4e2281230a5a7064fa97a06bef215c1ea9bef9d7d5e67205d0a44c9902b25c01b5dcd11000ea8bb1b37ab779ed5acc1d8fa6ae4646667dd494d6877e665fd96696949ba9078912fc03830dd06b68840197b4254c5d81bbe11f5b07c148658dbed0a8965006eddedb00d8954ad6e2dcbd9a7f8bb746e8046bf1672e3f874d2d6c2b4d4bef0d3af5b2db44588e5eb7fa10e054de2d0c54434d1686894ecf64c9be3848c28f04dbb6bd0b0e7502fcf9dc97deca34926749fff2d52d231946ef1e8a4d26b95d09525b54072e66f064b93b26563e4a6f3d80551ae653810f358f86e703896c67067d76996fe6b501914afe18bd753c99bb40c032e311f62ddab76ffa654390ed07c65cf36a855aef9dfbe776752bcf6364d08835afc6904c38b6cff042fa4e2e0a7bc3cd317df804f4c585c2d2590a6de61a5131560b578ccea24db3e8d187e80615a376fdc8dce63d853ccff05ac1b18deacaba0f75cd24cc0b0754a91f2b7100ca3d1fc130d4297453fbef7d84ce5b39c0b17ba6ac16bf21c3e3d8bb2f3a0e01f99b8977f5d0cd45608399c131b741b79bd4ed0bbf4a2e20175f19c14fc6f10d8cdef5d853f52c270cccc35a57ac4c09df25d0f256e0d3d722266a67a3dffd08f5c3e500a4aaa286ff3b1a096e1a1f921e5049495a5245f6c9f989558106f9055675bb12242c9790c4eaf5434bf631484e4dfbb619061911da02b062226b1100732f59f37e802d272307d7c5bfe3034c40385503692c948f26306c82ed177708f519db79f2052f14233ac154454d82d0030027a89d065d256e83502beda29c023639ba0b8c850647e1863c52e2b01498ac047803eeb43ec5e94d3ecf9e02840e5d1579f27ffd11523ce48becde405918ff21f454e606f4abc15ae6c519bcd20e77d3e49d39e4d2a447b7092238b04e9af9d4d5ac060913e1918662efacffde0ca02f4ab30813c2030e02f6a603caa422cba9e781b8165d6e11a4f8d233d85b00578d74ef5d1578446f3dd6642bfc8e71e377befc55be9f378ca59ad24751a80c18a6ad01bfeaf240634e510dca41393c781eb44fa160b046f949162e6796a10052e18b80a37c36f15f1ef4ebf4245599da707c455147ad622ece7165f3950302beaeaa20c59fbbdaf2ec92ed920b9cea6119f90a4cec9c76744cb8a14edb090d818171825d518612bfdd5272dd7976fb4569be984ef95ef99959c12aca1e430bb58f087ab95fb5e3c952ae11c9e616ef789f2a35f5ee08c2014890597b2ec5061eff9de1f84d6607eae6cc4863c5188c34aa2d59cfb04072ef238a565bbfaf0ea75d3111b6120ef2df935e42bba347bc6603eb63c31087fe608ad6a57f67f20944de0fca455c20f865270067d8a3e9ccb12a9d13b75344ce88eaeb7c8e23e308d81f691204e672f02135da03af73a8639ee4bbbff7b5ae1acc489f8aff376c0029501052f4c58edabf1842ba17030983747715562869ad4b86db368f422c5a010bc4ad321be120fb08c2669af27a4658fca55204b1ccba47d1225a03787d23077d4c024e83d6feb47aea90131a29a103f91af48d13cb94b185a205901c533e06d3ba867491aa9456a1c872721d8ca18daab48a4d5e93d92bf2185d7931083e0660b8e0549ef3d665dcf1d3ffd308592a2bab9cd12155096782648c73512c0400eeb0b8d7493b14df19b709ef1c23e66d8588418885bb17aa87c4393100bdb704ee72013dde236a236010f5aa49a888a645f8226cd8bfbf2dd340ff9c73686b0f8143ce9f61519cef0bdaed7388b6b2d4bc2afc4d32eb8515cb35669a389d08052c655a7334b01c8292ee3a26dd5321bda5f60997a36d9fdfc1852a26f563f50ceb859c1ced5931764c5384c9c2ee098d7d58e2497d7758f7fe9a039183b84c03f29b112832111e0453766b794666a6b424cf3f6f2487034879f8145ced39590040a33cb9779869d680b8655e869bf6d64daaa613a7263554dfe444d4a3aa6701038c1b514ddfe4dce931fbe32d7010d59842a458ea8ca21f9308d100d547e90ba1c4ae4749ecf6d1e7724dbf4e21ccdfc07e2d60574380a427e4d03aafe5150a294c3937aed40a2aea81460cd853b316b36774ac5127c921248831d96e1b5d07b576e324f009e623b88aa9c8b4ce5710140d3068cb7aa4ec8fdf77f1c4128a088e7f811381d0b36b0cd7f38ed318347bc270cb52f2b549949ae84f9f966deb09a97da3874501cd724aa4843ed10be7ed8b62d7231ba7aa03a4d7faa5758d3b06a45ccaccd96e7e07c9b3a4e50ed4569a16b7b4d1781427cdcaee5908d855580f0a5914d599d0f0f572ce4164fdbc67e81514763ce909221095dfb1d22f05c0012486eded4319437a6179defa5f39c6b7e82ebd4e5382a868e6e5ca3c5299060687d31360132054594ac059fffdd7912e1bce8dba3c8558c8aab0799d7155fe0f82c95d8354ecd074ecda1bdd3af36f48fb8b3932d6a8923d325249be8b334c04d27dc52d7ec53f4284cd0c5fc49c3315a56583f42129912f30af8700aca1d008c39dcbb8b67b3764613dd5ca0a7169254dd87357a3e9a2ed7c4c59b151978806c6fce2de7354f68c874fa0a9d931b95b45fe978c4b4dda91b1bfc57a4df8c403a8aaf4e6c6cb32a93968c8165cd308f5a285ccb1c35f131d86f8854fd0e3720a92275e869add593d3f7547a0859592f34a82b823b7e8ceba32bb1e595dea800a5680c86608de9904bba3ab863068d8fd767bbcec053bd14d4ae849b49e8786081455acd2ad1b09596766da61131ce681a885c6ada6ab186042f2dd33f5f71e0c9adf2a943c4ee8d4fa870b71b9e6f120d0259a040ba1469536fa08b03ac8f50716e6b5bf4895eb4a665ca7883de873e9f2213fb79494e478f5cc46d967a97202f61c9d013bb9ef737e4c62e4c172403419b49050b6a8abeab2f88d44c1860c0edf6b34d44d179b67590b5f067b497e540c868a0aa36efddcfdb7078adbb18105fd93b0330ebbe1821999fdf5a7981e103bb6ce30babf33acdfcc5451aa67e60253104340cce1d4bbf4be06b8bcff5bfda452c03225c94ea4599c2fb1e4e704004833f38f9dbae88e8dbaf9f47ed7e95926023dae708dd785afa141a51f14d10d03b5fe0c5e141b81a73997507ee0699b9acdb78f6fa2077ff39bdfc9f3bf6808117b2d07ca4b0d3c77f72071992ffa47764b5d592583a1c1ef718df531299d0198db7f399acfab6768c0887ded8e9a62d743816e997847e51fc28d565a09a800e25c700daf4a05b9546de04198bcb2d74b3e74aa3cd3ef442a43a8ce3f43290a6b4f4ee6b4901ca6e42b10761588d1ad812dd83f744891dd44f92a8667dfee0eb0087da23638b8668af718922a4e2c499f2d00e1fb371f5d6b951fba6ce91a027eb1b376e1fde72747c44da42377481f3565aa0b74d74fd7d03a9bb1599705004995a3bfbf7273555e9a79a786cf9c4c0f5b5a114082fb6055dae47b35dcd70097c2e51be972b874750f7094691d6b5c663eb759e7488eece6ff99eb1a74d9074bf1c8e4fbf492a067d5105fd1f96ea4febf765029da3122519b4d012670c00d2c5eb96aa69d59102242bb61b9d2198760a75e9e0d12cfd7e6a5a6f516e3410a801e14af3c55de0b6726a3b06bd6328e0b8bbfe09d17a2cdc3dfe4a0b48ce50c6181f0e9f750b3e4cd33e4c438f0765d382323c2c9654b231ea10e452fe53b09e445bfb62defbf295a75e73bf527c535b98af93672a8ffaf87157471e5e2b208a289e9fad857ef7171bd9f5da91c217948ceff77bf01d12cecf4266f1437940892d30ab70e7c84cd7c22ec4016c135c787d4393ca431002ff38bfcccf9b2450c2a365da2c37a395e6245fc59d32f7c6dfce52e1ee0637ad766e2eaa184f1df07b71dda77f3529c663b1d5b9cb789bac695f9778b371b91f614a1033ff3b4620038e8a68714efb35aee061adef837365d8238e2a7ad913a292368049205948e014c41404fb93e99ee4caee5a64690fe4da1f134e1d4cc062d32a83e11b1d9780742980eef4a45afec57428bd6d8290df11cf2e734c0da43054d8ae2116cad3c0f8270bc203b54c30ad19fccf4afdc6b924f15f991f427e88ec2bec03e205f4507bdcf3dd797ca7dce0098fa867bdedf724b04a0cad87551a5d881800b2de8a00eab2b9d221064be06a1423df6b54626a9f4af38f14043ece4f7d617c6528a3c02ddfe629e19ce90536964a47ac2313b0342424dcdab1e969b0e4acd316ff50f0be23badbfeaa4775b0592a732d0209ea9618dee5991ff156106ac76786efcaf0af276a04bc4c8951c08f70ba28b28eb732a6e01fef18c4c1e889ecccfc3892708ca1879532cf3e75fb65dc1024641399bfd591d8b9968ecbe8a8085b7c32c3e0ef61e9b5108840a7d10256a40a3e11e2b35a3864a434c2f90824e9008c3e34d0a9551a2df40f7525cc0696e9ee1615261283712894ea86be13452942287c45c03fd6bf15a7703b71cdedaa20d95970cb0ce518e09e9acbdacaf94f1f0f640450865560d26a5f9f45b0784f5af353f231ea9835dcea64854096637fedafb4785018d82416b9aef2955a1428703dda04a2c881923878a7d034b3a781152e80c4505dd176944d86b317d161fcb3bb9236327c785e2a5692a5e3ef5496cf73a1dd40885aa8a1d23b4cc9121ad52902f4d77a6df8e96b89f07933a3afac79bffaf5408c9b2e291768ea66c289756f548f5b3b9aa6b5d37b4b6e2d94d5ea63d4dc321074922fb86bbaddbab4be308d20fad7468c3496ceb798dff4254ee26854a08ce055fe537dbc3624aaba1d4d5463d5b047f7cab6a8ccb80296304e4669938db7a0b46a4e96f843c2fe026497864f63c4b5e01f140a2de56b8b96536f47e8c9a65073f3a90aa5feb6cc9b7136296f429c015eba23e283d9408ec5030c9c2712519023667050263e9e098c22fff1f6d3d5a5e4b6d3b0d62f3be11d21e9e88ca48e60f5e046bd8f4467ddced5cac11b9d848fdb100cb6a9c5610436c1acfa37eb72206e861c1a1feadfa73d863f8fc94fd0b0bcec01bb8af5203b28152b90d9b2b410086d6de996caf7b873d7382caa975cb976eb924e181a099363657bb027dfe84038b98c99a5d2b1d93a133e9113eee0f9e06f1cd0ab8115c99d2de3cb65e7aa707eb0b55e25f3d4136571961b15eae4a35078370c277dc144f3cc1ad88ccb3930f35e78689fece118328aacf0b037a3a35ca88e2b6e717883cf1cd785a842c380f90a1f2b24757a32d222c1afbac08582cdb4a0f6dbb40bc1b31bf0a709b8d2b02303a80623558ba88b60164240719cf95ad12621a35b50900bcba9232aa59b80c697b8a7dd21155d32ba3f1b20620a4553589fe3fd906660608a8d140430021021c9743543fde21ffe734ef9bc7573d62465ca6fadbf30f7b03d3ecadb36802053610db61c8d03c580ccf8d8249850844b186e7bcee54abbcbed314b4c1f49e09285f87c5fa452ec1b1da23ff1d34b71f5f94f935d63ca74d00b7ed7b3e12af0f944bcc496983375d68ae17c0a6233c543b08b699e003c9373e99100b57bbf9079da8b27d7a3031e18fa003b56b70c91457b1f7dd765b601348c8bf9744c3800da170a8e10180505dc98a02c188636b0c3fdd42891a172ec99e69a26b1614be080f906310470d4e7895487aec1d1d4353af73867e72ceebcd9d99e9a7bef82809a2298edfcde6a4333694d66472b48f4335b324b3af43cea1910d89041a2da20afb24b9f21680081e54b9cf5184d0b4fc1e900d9912bfb9467dca5db1a0c58503caeebf5028b56668deacd9005c1034333ce25e312d11b9d60d5203d9631b27fee2ac7d5885d47c3749c7bab253b955a79e7c9f73bfb649d6d1ec31fddc6f1307a9d0a3d4fb0909b4bd879fda28b7f37fc3c3ba6b445ab0bc6d376815211bd80067b6546da1208b057e57cac3e85dc11954cfdcfeb854d1cb2db501eef452440e12c66cd19bc021f55976f5f5ecf40cb3f8298ac95190a9072be3947968ca4d0b657bd1caee592eeeb18118b1d11d56196fbdbe78569d866b547cf974824b570266823d8f5d14063d9088d058ccb5e0cc7afb40c858975eea3925125293eeda03875b9a44a063bbd26651ce7029c510a0f961f077c38d6ca95436b34040cc4d0a7fb717a8838929d0fbb9090e284522b68dfbcfd61961587cc49e1b4ab3c69509dece1a68096959cf2325016e077c8e5aa66cb1d3a06ba263bacc193be6f1bd0175e7450fe5532d0da779051f0090ce42938ae866f1d1fd164f801bdeff90c40ada71a1e5880a65ccf7c00d928650a50cafd5186e57f7024231e64f59b98782085ec6db5f49c860f84e0a69bdc3753680cbb3b4273e353109c136cfac86988d00fada7c0720d90e244388a624b16f3251624a99e5297262cc3dde8379daefc20f7697fec54df2e3d12c4cdd5057d7e2750f76bc28e8ea6daf9faa0f984eeaca0d3788360a1dd3af698152a46c401e1687a9b2860ae54a345e3cad9f3f8efaf1075877b8b2cfd1a94e999af2a37f36156ae697f52d9abc8a517b3b8d2cc05d7303b59bf735462ef922aa3edb61ea1d726c653faf4eccb33576751a3bcf340ee60cfb6a90930f37206616ed28df6050f6dc0dadd8db064f723a726c740b9e106c0fd3f6547c28997dbe62901855fd6cddf698421bd5b260930c28eb928d14d6820cbd6b620ab002fe9a7b954595c7b085a403e37834077ea098dee1eceb327b09021bd49424688d36134ec46bc0bbab80b709027189ae8da63ba306a9e24c67a20809cc746c5d1e1fdf233e30e2deb7a6a3bc540539d1169890d2cfd799543e6a00a28b54daeb258b9c8e4d0f5ee5f53595e01f2ff9868bee4ba2109c35337b9a0b9f841ef37cfa28531064bad875a0e3ccb497939d4846f55cda1df9d060f01800180cb90df5a4d57de1f8f4759ef2e0b0a7931781aa648f87358b8bd6a2f7350740836a038051e322b70b79758ccd209808ec75b42ff7c760f575a48b9d9a8b0914e7aedd5796719fa353355e3e1db6c322f40aaa451a83882c0cf4471a8e750efb182f5ea91de2d3fd3e142f858f9fc1805fec38fecd4b6d0ccb94d834c9210132d8e9aeb4ad7ef0281731f31303622cf8c2bdedaad60de736a8202e451b12030ed1ded85d83e12253d904b0789cf56f8571b06a3af453dbf203c5a825abf20fbf3c0c5738e9e1af035c27784edf0f21a89057682744855f5f48e78e8b83e600d4cb44d585c21a5424e319362b0d2510c1ea28982351e434301131e9c629470e8e6e16be7b9f25f465db73b8e9386da580ecaa8780cf617850dbff8e60a4f403e9efa82173917659de7870ad33913158dc34e34fda52f007f84ff24c40023300a7f798366f5549f0c9d1a77428896881ce5bb6dbd35f167a6cbaff329fea6800dc7e35bc8da4a1651711484426ba3726104cc8bce68a8d1871a92f860c584f02770874348c5067cbb05120d553fc153d24a5d0e2492c5145fda48e57cd7c79084c483e60e9bc0b2ac412596e138f75c318d317d6c0be92b17d9dbd328b57990f8eede23ac8691d318883e25638a738641a0a268147e783bcc8a455ad73aa68021a041ec0f9163bb59d4aefb1ebd2fe5957819da822ae476833916da21a08eb03a822d72e26cfa851e43e60453388dc0e126f66dacfbcac9747f6d8666bb0db085a4cb106aa9b30b3ba9d218a989e3fbe7f9af458f06459ef608722cc33945b0e4e1fc06c8156c1554559cb9bf2736a349b51b7cf7b06e4e1a5981541e86aa1004dab0c6ccb31eee0070aa2b5f897e19de9ee49decb9e8c784ade3deb133e330f5cfa5a8cfab017c73269715ae188eca4c5e5e9bf3920e31e3574d306f26eb1024dc4fa8484e9ce21fc7c61494f213bd7ae4414edbf066f64b9d9fc72b191260aed31a0700785d97be255f88d41a54911e8d8976f07b66d9ae730b10b7bed250fdc86b0e1a1caca8767e04fc8308fc29a9260248529f89de2a2260c94094b7d0bb87dc65d8fd8b32f88ef1590993e29c9682afcc44e7438408ba1656fb0c61b0580fdb421b6110172ed3dd3626e76f3636f585733327a7a85e2430b7c0ed9590c9cc5d5e5605be8114d35f65e11c2bf59a438e5c56d733ce3ee8fadcc85739107a4621e54d29f2ea22d222090a92988a0789f42ae4b7f755d29463bbd4f58a707709067bd2457dbe32a512edc2d5e9ac6de23cde26b4a1c533b7dceabff1894033945d835cacf5ec35897cc7bd7f741de47b753aea5d6a08a809325ddd3c5be0f813489d8678549155f768b0103e19d824a2fd83c3411b5d0923718a521d62f0b3c2dd1029cdff05dcb6fed1d5602d8668c1f63659f432e8e86999f0c1d7e7f049a70724c8be9c9f29efe8483ace742480502af649324ea548be8290b4ba04a0a5a9eae16c91899d06ef5454eec991121687b0bb968f1fe7958e8f3708670ea0b102efe2fc397a586f98062852f3fe85a82e0a775bec2c2f2b253c95c8008d100ebced899d23f1f0cfce1aa48bc50ac0891baa5e8ecf6bc1b37f1f26d13998d0f6706dfbe8142bd65a480a69309f02c02c9e1c3c6ad3535c920a47826d876e30f7e3d04e626ed065fbbc6ad7cbfb3c135b7ed1998114138b6c362a8318719dc007457eceee2c12fc118b3338e23133450a8c8c0d1ea33da0683a822cd0ecf840e698ffcb1933ea325ed4bc16128406ee98e0433f9754afa9fb2c70457b167770ad42f0170e47586a7bc1a34b55a5034a329742d94b2025fdf7dc336dc49af370159b1c50bbc7523effb49dcb2c74cedf81eebb89d4824b5773d19dc46c3358b0754c6534d162bf6ef38f6a42c274bf177b4cadf83d45683c33d84835ed0ca760b0968ab7663a6a33311925d6349f45b1db63a55d150860c9948085d54c80d88081261791fdb83e5009ffcdb82107ee9ce7a1a07e5e8e6889f2608815cc4e60006e278ddb05d80ead7ae7159dee5eeb9d60e913833e071005949635077929c9b0d5ddec76ed46b009585aa37268505827df4f1f77a5b94fa29ee0b919e3acbb00c36cdc27fa5bc86c0993fc13ff9c70556398223be58f56bf97ba9dbcd6058600f8e802308d55c8651a3866b2d41e953b521a002d2b0f5086da13478d7d056d501d299ef054577e41881d50648ec23125a6ef800d4e62af67b986d1ddc5fcd2107415fe820fdcede80ae15366a15109a846ab412f8bf5c4ae66c072fdb024cc801792980fecdc2aeb7d45981881aad36dba584bd7292bc321e370515c1949ca108ef0ebeee32197eedfd9e0860dc663c8da616c0803895170339f6c11efd3cd506dc31f063a488548e965f4aa280882b51202b15fc363c88de4aa14dca10bce20dcdc9f3444fbeab219ed58f66bc9ef628fc648cfb50207c16760fad417828f60ddef5fca6819e2bc4d4151deb7fa8012e61b89db5915fb247d0fbc652be5bcf0ceb23881c97e6b32abc229ec5a035e751527add5673527eec5513a881b485af089d0228f7dd6ebf371daf60f2285ad99eb3b169d300eb24a0c1e95e0ceb1c2802872334293bbf51fd41e4f45f0465ffe2666e8487f349ed48bf8517924ba5da0d4a1d123976d9e4513b7b7fb885004873d5460596395ab595781258bd3010f200a53843d1690131d8f18d9885797bc003d2bcd88ffd531f94a04ad5eb54a15c050f3db3d612848ac9d88c67f90fb911ba7c89c8af44792844738309f2420b0e07f765208abf8fcc80c855b89568f6dc04c7552fac7e0bae5a92e3b710bb563b0aaf7a8aab60e452da6754f508aab5d64f41da751194cae8dedd36091331a3c00cc9442b262e008ef2a3e7262f0d8805a3a79dbced627d31b0ff9414fc8f0afa09cfdd7a2c5a5aeb88587c4b2437b63a98ee6ea0c90b0b662a15f881223d65e70ae7a3f8009b6c16297126a36934edcca0e41ecb3da70f3ec30dd4aa6acfdc65097a8c3bafbbb817977df3b8376f4d2cef445e84a863e61f5cd056754092a55a0cf066b6d2ed5385e0e55e542bf100f83ddb3a41dfc3846c844cf7e436963e31039befbe9739956d04e130429033a16915e5f153dd5d1653c75dfbc197c90ee30b8c65af2d738c83f5d91a2a3fad34ed6aa7a88f02966cd7e9d67c089810cb840d751a8636e05f30016fec1a8c52e91606f596dc11cbe7f23b0a81ae07e54d1103b82c1be734cec3f7caee8d03022a61100969a6e8c75e9e50dada41dc7959e3053606f7497a7cab0bb8fce41356689c32dbc5534ea5e51e705b5540a972d0560ae4e9ba4272b63985fc715927f306627d6f70cd439103a8cccd2eb4ef7ec4490906acc62f5d903f18f268f87de99da09c9880b074c8d9949f3daa336107f17b00a00a3d7de44aa76df96eb66ea4e6cdfd976627fd3dc12ad79ca217016e1a0a07c3c83d128a57e1e893957402b453fd30f0eae3605dfa30a83a55097665a576020bd724d1b4cd00c049c1a41d62d11148e0029bf187dce570e0240cc684fbde092fd6521ed3484fee9d7b45cf879092cfe1a7359f56f0518fad68f2d226c6ef000b7c236ef5e5b57027ca47b959ddbe70f42610f8120c25617ecf9b0f970a1208cb0abb670396d303238272ee0faa98c6c12177d17fdd1f9965eb55761209c20fcaca17e03537c15f2f5664de06563b946a4fc722d5db2be4b663a892a6cf3c0c88335d9784c74f813e6ceb01d458cdda6e09ad9b7120ac24f17c04d4c5d1a80a440f29e6bf43dc1bb233ec204ec5ad303f0d74f26701a42c449f0a4b2f75ef0ecc14143cc106292a2badc76f355f107cd206cdfa9b9dc57fd35ab177896dc2004556f30ba3cd9c071fe6433daff2b1777564199e0b5b7339b0b3594462e7430b16cf66b31ba490b528989ea53e6822621b1ecbf8ddca7b7314addda66fc5520708c48e2cad702e69172c64d40fad13a83b7112f03021840cc9861c17f9652e06944bc153b006dcd8355b4e91228f0c5dc0d2126bf7b82d163d9e8673da95540b9c3237d6b709d53df1b24fb1df0a6cf71faf76dcf7ae8c1ff89c8add2547e30cc730e1dd4087ee1a77877df51f79660dff4f5d258db94db00c2cf05c40b69007706c8e80f40c616714b0f89426da9f10935fbd83deb499fc232142cce3ec350752ac9cb8fead43a0e131fde700486468992e45ee58e54a1afead33ac7cee040e2167570656714745058acbb0ce7b960d0000026bbebb6ab7a073b9592a594209d7e21ff972a7db720a47caf58139057f60df74ebb82629755ec550b09ba055066b4dc969ce52262c505ad4bce82714fa14a75cfe445c456b5bea376f74fff20dbd8384dc21bf2460a0e16e01af31d4c1e05e485fa22ba9a22d310b9733ee790e false +check_ring_signature e553bcd796adeef7134b9294af079f836c1625215abc8f6a71a164e2ae59842d fb483ed948affb2313b2c0351e568fe71417f0326738c1ae22c5fb26cd14e302 1 bde5a10c384c8d70e221cfa78d0dc85230c8895af2e9a4d587aea0d58608d235 8cd852e4e7eb35d1f9a404dbf116b23b0f97a388345725c27092566b4ad035584a6af477518b73178a9b656358c36eee794ac8712df2c532afa0d0bf28547e07 false +check_ring_signature 39d67ba57d034edffa50a1ed8ca167bedc8d7b18a7f00136850dfa7231c52dc8 494fe41eead99cb98669cadf4d115311df9f21b1a3897a773215b246c709aedb 5 537541997616b9574ad2e65fe038cb064450495cced9dae5739b648edc5af29e 80ff1ffff1e247a360fadb576927eab906b3e928d6ea8ce8981b05c635b7bb52 6b07851a555da6336bfcf727f61d20179c4bf070afeb282585308cf4ba64454f 65202cc456ffc59324de4dade12c03e9d27a25d37f37346c7c0ef045b1afdf0c 27e5e865bfbaed99107fdbceb8ba13f13d17f712b73b3e94cc229b9f30d801ca b1e68db78726c0271f278ffd4c40434ee36bf895fba51a8df77707d9462a6702922aa8e295ece6f26d987c8c80da93268cc2759ef4ff78ad4f7954640aad6e0fda0b14820cabb48004fd9fdd24e89eb45fa9991870d4f107df120a86513a2d0fc09e51946a018ffcc84cbd6873bd33e7d77cc618dec227c0e2e863b15588290b5694d0e8deeade41aac6f5bcdc0bb6aac8c4f7126c98ecdda7b5dbee37b2bb02ce3c7cdce08c23fc1fada8fb41e5c0a62036e559be33652290caac83bd85c90ce7cfdc1cd30ad9e24683e44d0c2f7c2a69d87eac05a09de38fe345b2f33a640021d0cd6b9f62b7d540353745a264b705c180e714bc1e33d709b1d294c12b14046ca10328daeb3d8fb2ef0106c321134c1890432cb31c7ba3a9494f1667630a010236c208a74503c445ff493dbdc1c8695dbf092013a0af6c31b6ddd6eb7fdf0e true +check_ring_signature 64d82410db9a6a9b09b096f425c5105082e85151c985ea60e8d52dfde4c96b7f 49cdc002788a6e4088659fcc4c27e8434c91e249406c0bdab5b0309157d61b15 5 315c1da3025d48fa3b9ff325ea998fb00ec2f45d121577e97a9139aa6675c8d3 f769b91c4aa74e32d019e3065a3bdf5d0fccbd322f7b238a2ffd2e130a64a4fb 0a73d355d6bf45b90545f7d1f1ec9ee7c40c72a04f09ad93786ca0257ad17203 82a702c08122369ed82e8c4baae25aeba51fe2bb668b5c8f77771ff7f68fd8cd f8f4598380c2183463a99d44166940a11d87a27b63f13899b218a3e1ef603ce1 f81c32e66566d999ebab2c9a4abba86ff34520f595c3b3724d9e2014274b9d01f5a3aaa62569a95eb82a51775346f77fcfc088b8f3ff14e8d8ccbdf60794860f03c1303a6e7e6093656e5e9e34604848d1389fe9c26ca02124b79c2e621ca604c5b28f35a58ab25c9b3eeb3acd712ff6f567823fd2edf13f17480aee8ad0a205f3c1b5d7d40152608616133b91393b77d1141fb0ad4b496c78479903f29dba07f2b44150fa918feb583a640d454b2c2fda38e4c1e1d86d9b790268719f471707710709b9b19155680c3330e93439c30c6d54b1f3fe2e974bef61374fa753490816d0be7e8a96d240c8552c1d1beec96633f58581e24ee0dc11d5e4e5f470b80cb5328001494b525f645b78f8f0263e41da3651140aabb9f0822b934a9f63ab0251ac13614413e1fb777154efb3ca95adb012c86af6da19c6378b4a464522a80e true +check_ring_signature a38d204b562cb2143e577854fca296cd95fe36c5cb9c830df257032c15aa3c65 56f5cc66406644e0e2540a163f22c51b9fae25106c297f0d3e8addd0221dfc7f 71 204c737e1b89727f164b87a02757aabf6c1b173b885ddfcb97381723a7097584 dd22fab384398883e39be3780dbcf97c7136115b80423dbb05eadaf7b3f31e0b ba40d5cdb137c0f8ddd9656beae442f34048a0bfadc0d04b9206e621e9524c00 1cf67bc7480382eb89ad78bd94f9d01c8cad8cb600a242458210698732200df2 5efec1d712da5dc0d24f594c3abdc7d3fd670d9ad83c7fb78e25c58fe39219c6 d1f525a4f797b7e7b730d52e37e37847eeb25ade5574de1108788435b19282ec 8ed1d4ca533cee48f23429eb4dbe68cd674dedf81b95f473d5aaf6b17f3c715a 48563d6e802c3ea38be171d6238c26725d7739b7bf72678c10637c364be80e4a 25a7c3a7b2dc154ed4202cb2ab386fcdc9e55ba79ef844708ff49c565ab59691 eb4e48e8763fe704de6d8eb231ff9b5aaab09aee0deea4169ad93f6f62630189 fc3cc2436bc0ab6fe2634f6e9c32bf20630120e3bc523e5faf1aa9272f99be70 9e1df7ba26d6c36badb70f4a9e2cec6622f34e441ffdbbf03c9615b8897eb562 5256ee1178f4757a930a169c6dd0b0340da03e130a3b838a8c191c597c17e81f f306d5735ca4a61245cda51d71d92f383397bd0cf173a4eebec045727b36e72e 944b42f99f54eb3a0ff04c93cb082059b977677d99b0af409b312ba8947afc14 c357b3b21c4752cafef97abb85124f59febbb8ac7fe6d77afd004eddecf51c2e ca6785edd3d84b470f7401a8ceb76ec8c8e2f04666a3bb850b42ae92bef438ea 6c6d801df27788ffc8a1146110b69e84d34fc20a318c0b26b20485b0e10be0bb 75dcbcc38f6dc50b2b03b21450b6282cadd301c193e03aa79cf14e6a4fb19fc0 067cd0ab9a90b8bea26c9a7c689ddb8c5ce9e96c9f0c5e3e1281fb3e1fa9229c f33f0301396b5a533b5e290beb662cf0fb6b56b94d5b5a5a9f63782cd6417b6c 55d65a2d265a7785147db877bf83df6c2072450c6f58a14fb0bccd89570722aa 7f3a356bb22928e2f30fe19968db8c4f5da7d9166d5135a86d2f1549812e873f 458efd828173858a8f0b227dd0ea3316e73b7cc40156cb3b732604acbfe58a5b 973b3af3ea653c5d589ac0d966e5545bd85201f5da8e4e4f5ca9fafd617d0250 8c8b553f091eeca075539f68781b889b578d98bee25ccd6e173c707390808cd2 0fbcc8b130e0f69306d6cafa932efe768a122721a2d7f6e20e25b53f00228890 3c787c891cb422b70b06d43855f3dd3eb9ee5f1b0700fa730cf6d0d4bf910256 34fe02c3781378244e9d794b4b82a305d525eace300743971483a3a89019d55d 896d01f20d1fada812b3bee7c4c2476daabe80eb0ee14cc25a342696fa7b17a2 897afcc4190d565939d33c122d59dca7affd9ce3a916750afb0e32c7c7486c43 d19c6b4afce4a4497a8078a97680cf36e9bb936fca25ba17c04a47349987ca65 5e1ed1be94cde1c1ef1b287e9ec3d893519502442f620a2d81ac3cb42b54cb85 54686e66b7b17a917357a514e1419ebf094bea9ebdd6128858fea602067d379a fda6407eabaf431753842e444c3cfbd3cf9d706c4cf161f73a751709c9432be8 320765dd4c4f3ff4d4b90c8b50ad5ce8e884465f6dcc8592cadb3ee99637cb39 f5a447e46215581bbfb309f980a25ab4dea78790b1d5b6453ca77500a9625bf7 153cbf72546da7d4dd829c4c4fba7d16b4ecd18fcfd104f5aac61e1d3fcd31ab d031d0fda06fc4bb9ca525286b3abd6482c1d3355d09d7e0f23da19eb3d2bad9 5a203c244c73be91c211b7da820f0f96168b424e15e8ec472bde57684f63194d 59e7c43a1ac01234c87932e7dd8f1127967418f6225e3e752eccecd844012655 6a34c352bf47b457df9807f677dcb4b9117db785411a7a524a67248051fb58a8 4020a9d864b4afe0fe7aff4e34f2aec047faff7d30c4d0dbc5aedfa450b52b9b 3fd0249820c53d7d8fccb7ee8dd4281a6f8f2648d3e8e5348fb1ad50b64e4088 4fe19d96e46d2ae4090f65a00aa551e1203cac669c8bb33783d222e29ae418da aaff5805c5cbbb18d45faa770848d0fe2fd188a3b5e7bcd86bc28902ffa5b742 cf9396b64e87b63d1ec2dd78373335510af313ee7766421654bc9074559ddfad 195710bf34ff9708b64eb03fd8c071a914e8c3cfe8c203d58f8e42b373572ba2 7ee41b5264b51bea849ea4e230b4b670a9d2e4d0b2a45af252718521d7473d51 902fb37e397c729930fcf1fe494c19153ecf32c0318294b05e2ff5f8b62ade71 ca671333aff077654b92766a0b712ea1e54b052ec20314a1488f662ec91a0e6d fcad85843e8624de16bc6f7ef807a6a516b1b9756046bfe6d721106169eb9760 5d6be2666b60ad8a4059a737c146253421097394081cd5fa636590b50d438a92 c942becc8f39d37b5e26373d9f299f415ecaa8af71e80162f70abea7a8a41eec e7398ee400639046f5674187041954d50225a87ab6a15ea8f3bd60647460f0b0 30992ee52f668012f26fea55a712563c0904cd01add4b096e808db3d5c8afacc 88e04adb571006ddeabaaf21bd3768c46a3bc5a109bec04a5e5fbdc93bb65178 abbccd0f52f23e0d00cf2a6f2fe74c421d1dce95be94a7c0651654e6b31d091b 5c8048ac8ba19c1820c55672da2e7b800ca8d9c05ad23434f46f377ece0a60b9 65db9e85045cca44135944ee9c99f8b9d9064dee2c110de4e841e79c30d9f80c d6c5721a7b7de8979f28ab1fb432ee188bbe57df5b3565985505bbe8e3cee87c ed2a71a6ebfa8f242c6225546bd36b93db1746bee5a7ecc3f52196d2a39358ab 5337de574ff990781f412130e3a7f2b011cc97d95f04afea502c6940b4965fc5 2fec707c8c7ccc99d20b80cf6b115577b011c5df5702ff924a989ee695d0e5f6 d00515da1895bea54293ffa663a882630035aa3bc6ba83352dff18bf2e847da7 9ea3878a5a979dd58d7076c024dfc4fdce7cc86563a1e2ffb01d2d8eb90bb593 cafb92ec3f683f8468826ac42ef9ee73c6c9b14d1775bdc17d3567dce07b44dd 0b0a646cac604445796f59bfc8b5e36cac8fc93b4d7d6fc3f512f5525df85cb2 2e8b081ad0dc8c1cee2ad84b75ce317297493c421fb46b1ac2fef559f4cb10bb 8582d7aca205a877352044601e555654d9a7c11d644408d0d3f9eabcc01158c3 736cd53854c07b792c901229f01ea45352a272664fb3aa879a0925e28162370f 00ad81ad49458af2bd9d77a3ee0318e194a44f3a7493583360bcd04e10a2bb0784d0605019fdef91a5a4597a26b12b87f8b0b651c7e192ad8dccb6595adbbf038c7fc93d8af09977809e2825e58265a71f3422559f4518648f0a121a6b29ab015103409dae68d8fa17c35503f9fa39f11e333ab45e430465d06e4d9c0a1abf0b6333d7171d1820a7ea0fa0be2e7b98daa825b1ed204f88e182964716d99b4d03b774b620ed6317c5872d5d009be4f23bb857722241e3f17eac5023fa5f88ca0bd4ad20ec314caaa5f95affe6f3bc4eb3030b41a11e95e84df0a388a89853590980d7177c163cb13d1344e99f8ed53ae627dbbfa98ac15fdb79393f5c2e7a8c0a05acad90e537c9d1ca3221ba4da52a1b21f1f85d375584f4b81778d2c213230174a4e252ddfb4e958508b069699cc483a28ca78cdbc3bd18ec9c63e870d5c30d02a518ca8d791c107e96da1bffec011758131c4779813047ee108dcfbf5adc01e9c09cf125f7f655c16eeb5e4b643bb08c119965bae3d081eefa72bd0a8c090651e12c5316f24d655a8d9b815e2655572c7d82b3c8f91db57794ef65b57ea20be6c7be25abee4cc63ad5736634a8e402d2373084be4bfd103791d23a02dc5d0270ab0d2556a0071be1aa127409a89d004d92c86e4b6f3ed5780133ebdd302b0331819f5690acfbc842c95571324616e7167ede3fd5e45f8d8478a17b867d8b08d593232a07602a5147300256ae6b6f328a3aa7c34a404d8afcc9289a46ac190b88a8a43ae53a2f670175e50f761fa3ce1b5a505b0845bbc290e3863548a56709327d1ecaaa3708396759137b063d740ffb083033e4b9056dae729e06a0754e05ccc22a219fdd9d29ebc1fe3cd2f8ffa41f9e94e6dd5893266fe558886088f409f726a1c380fa276204362a5b25d375ac51585591e6b3ec1639b5bb9591a92802aada936363906e19df5a03dad50143039d13c0e7ec7f57486967bc86aa3568014f251082cf0fbc2294393a7dc7563a88cc67a5ca21187c9550896380862d710e2af9d3adffd65f27b0ff247e7978c2c97485f31b54fa861bfc7c9abc5b6abd0512d19a2361c8feb9458dd4a76ad073078c4a05eae71d07a18fbaab34274c330b3eb290181d84894c34abe476c6055ecabff2f5afdfe66f310c6ed59254b2540a33729002a9cfffd62f1601a2fb38f07fcc0a21d904e8bcb08a3016cc03a56801d5f48a4a71b1979dbae120c97f702f9633ec90d9f65683d01d8db5c1148d560915352aabeacb26d22d37e98cf0b5083fbb4ecadeab198ff27f9ad5d717842c075564e0445cdc70f31fac36b3dbf79b1816a8d2d37d899b3be38c313cdd8dfe0a5ba445c1c4182df0b8270d91a7ea53cdeacb53a4269cfc86eb48c483f6b29704759fa0fedabba2071c46c28e618255a43a8105d01db411869bef05487669dc0e6699268f052253c460a74b2dbfa2da6bfcf1e84bc4e87c5029396b103ad9990818b650456b465cf6f04b388fc9c32753c53d4402437cdf4c1ac0c64018c20e0a9248ad4872bd1b60aa05c318ceb6bb4da852cd8600b0decd91d692fe1f42230b8613b2c5185dcfeea7a417ab911082987e76b610c201e299dca119df72b2fe060af2fc8ec9ad65f1b989b510de7785b95133f2c698c6bb7b16ab7b996d279b0b7a19fb48321483f3180ecf0d5076d5e030fd7266d3d295ae6558615983848a020b13e2966444cfe76f219dfc6a2e2127311253a4f331902267718bd016ce900f5b7aeca4cfb43aafc0d45d39c37858515ffb79adb4cea6c2222a41be4f58c402e306ef4d40016945d184bcbc0a6d4aeafd0f44376182596ebab8f36af378370351da0f2e9835135b21bf74916d52a9562a8d6ffb1c9442c830785ba94483c503ba0c82f3261b7f60f3c133bbf44738642fab7f0c2bad28ed6ec3ed1eba456a0da0317bae4ac1c6d2f9c973745dd23ced23291282997e8f846c5ada05dfbc5c05ee454fd0f994b7ba7d5ba0b00b47de498c08e3cacd442a2a2fed34f188b4ff0147caa2f69f48f771b0c13ab4a52bc1652c2c8ae84666dc46fe0ac2dad5b5e4025d3211235b2a98486218ba641092244f7da15d58add7cebe2a06ed50159d6f0fb0e4d74719f3992a8cbd8ea2da34891424a8402f8ded38c3efbcf3210b6a69022ab7329c8e5f1169f809ea2d9cc5b869b5e02841bd1f06a88681d24ed1221300e959949043e2a3e7e0422762d678adc57f10ba82eb023d5155fbf40c06ed4c05f69e89f24e5a55908b45d54dd00ced222c54278541f9773a7efe9d5df39b32032943e21f52f4402ad28e4f7e2cf632a292a83bba9e3b07cfad0b3e02285b180ade4545e03c280146dff04ff5b917f7db080560aa78c9af16426ab0187aa9700dc5f154ec4752f35a0ffe968ea885e516b84e3d993b81681d03f04eeb3f0bb209058b03d9bc195c8b80a563a45df88c8f30c7da6b384038929d4c468fa32c8a034c71120f339dfe2bea0a7ddd6b860c53c6c83ea82f7523af913ec2b2610a0c03719a8f08b7bcf1050b05dbd99c7691df51d855bdb7d77c31ed18a0c66f290201914c2105789e37aad5e5f1c12efed2faa10457518d85abd501eed527ceaeb902730c3cc1e64f45f87bc6cc465936d8809bbe8eaadaceada49f92b113be9d8706461d8b8923af5c6d48daf56817d5c72b6bf3f6d593b2b9a78c8d7e731db6c80880b5937449307b22d5a567dc442151cfdf234a7ed6b94fe0c6df3308ab9dc6044a855a00747643fa348eb0a6650f0c998c6de69858078ac8ddbda408a88138030f8bb6e16afbdccfc3187a62a5ad7dd0b1249da2e79b2c306223c1ccaaec1e07aab1f7203879de9af859ceaf2ac9a175392d2564ddda6bfbc4b9250eee848404ac02c72f003d95faa632151ce2ebd2a8a35c4005f41b0d88418582a257879a079cb77aef2a4f69d83cce66deb079ef6ada9513a63e67bd1a03a22c8a5dda4b0d3b16e9df7c7332bb8af361a0207b5abe8fd4994928bee9a93a9d10bd929afd07e0c3c4f7dc030649602196377d25bf0338ba1605e3ad02fa572c4178f121680017089f289720e1c052f0b262d3d2c826397fcb8ae165c54bcfe2a4c00fcc5a01ee7a50ec587f8b134c3f5cfc34d7319774bd2342d7ce7842b3154406c04bd400a65b5c024791a0f501c55afb2d089a51c4a37d5eacc43b7e6d23b1a8f802c40d8421a70138e9c96e10735886e4452b98c620d4102de3dc2ff10725fa0dde0d07462ae3eee3e3946da928288d64e5fbe3f283d8e74ce1153c3da94d51d71c840ac814b3226f8cc7083551cfda1f56db0ce058c39c741e2e0a805078a0043e9308fb291e909f33158b72180f16cc69ca2220c4f7d8401560846c4c4c212498c80924906961d7714b88f2be397258504ec48627ce4774df270cc05adebd2a1897070b7b54276c4b4eeb24903550b95ca89b71ab7d819c67590f90d247a23af16a02c7a0d44254e4d22628e3fd09114e175be1a9c0d822d9d47717e5ce115e9c450a1aa777b27e1b7e0b8454ebbf6fd81bbecd4381e6d4c107dd5f747935e09eb50601c1f4e45b5b1435ffef8e4654d4395c77b9f4cd794c882339e522aef7f94e0491044a9daa8a873e7a65744dc628730d9c884091201696b3897207eda1776d08cabdec7afe6722c2840c6a0ab205e270526140bf8769fb595704302350bbed0d211528528266d86a8ff3d0d2557eeb5362f8474874eacecf08c42dd6b0fb5b04e2ab9f7eff99b16c304c14b8e248b1d6035d26765bfb2d8e1e43c4956236d90b41626d744b8c6effdd0dcbf86d6fd7199cd4b23871d5f3d65925e4e2edece40b61296d043f1b50eea24c8f8c4278c3deae21d4e6d2f970414178fafa3ef3c60a1972bab68508cde89133c690501b2475d79654dfe8b5106ad94d99c7891164097383a63792074a75bcfa45c60074acfd151757701be1bf92783e537881c70600997e2277b303f9ef2b62f54ab857c72c8a760b94348a01ef6e7696ef4f4f810397247ed6a449e5ce94bb9251810adf32f2139f4fcfaad5b1f3656c361b5ef9066d956aac4b098ff225970d49af89c031875d0b6c455ee1e0fc6e29103448b00a586f9f84a96945d6d050f8b585e2bb2b32d3213eec618849adc475d11240a80418f1b18bcb2d2917bbb0867a9fded8224703a413ea71633b0ff825664b3d5508fcf079f5bfea5b2380cf736ff73ffac9d093cbcb361a9a25852c66d1a874a30e341f8cbf6d8653705883eaa119c195925a906ca9962aa0420243e187acf74208dd571e258f41c2ee9d09eac4feed21a012d332534836685981e1a526b498f90fd0157d5959311504faacec67685ca401e608c4326e57fca33cf92353947e970da21f7b20926372181bc55f341a9de50ee4bfd1acd0ea0b3dac80a323e70ba60542d3b2fba7c5e44b4dd919ced56ffe5e5881d0b8157eb9efdc86d2d281e728092b178c88e66c95558f3af0d808aff59c4064205a8a85e38d5fbd06c7975a160af70d40acef925eb0233ff2fa9c8bab6537504fcfc4f0502f1012c3de7185bc0c7ad3608b7cc2ad05aaba91b64e36519d7b0f38c592c34bb76e63226744839a0332d28c0dfadee6bf25b37400b38c13ae2bba6717136f91ba12fd39735948390b3067dd6fdc5f74611e26680fc9e9ce4dec9e8d111d8bac1fbf8be8ec5366cd039726bbfebbbaf17bdee00039ad2248d4140846a8f01a9d3c32b076480ba1660b24662e464b43e4154acf72e44a74c4b9b81f600d0595efebd662c0cee7802e02b5362d925f657fa2d3759519253b3d1aa1ebe0aac21b1562581c9d9db6e74b0b2f53eea05bda67966324412c7846b2bfeb3ad0e180940348a78987114a7c010be9bf10081585abc376d5771195ed94b3e09811f69d4266edc918a1f801a96a0b1f91587669074ddb32a9e8a3fe41d47daf12b97f7b1f35fa1c5c5f7ad8c3b80000fbf1c2a149102da5b9933c9a4941d3e596bdd277d8b893461b82577ce7c507977e5f33d973c7ee69e31af6bd2c8df271f50de95cece84a983e1f2f8bb29402582de2ef990c6a598e40a35716a71d73e6d6a9fe6ca1596652cde2a14b44a90faccf297e4aa21105d1eb182ca354f1b0863e74956ef4625d5f82297a63f83f010fcf55f89c89136865ee9dbbddbfc536606344f317816292d63ffa44d083bb01e2466c1feee077df8fc9c1acf05906b0b03e01f6149efbda5167e26c09b4f10a2fa7d45fd30b6c337598a6a118a1b0a70a8a1f85b47953c70575c8ee116a6605a2383d4acd60bec7fd3de38298f448e998f40ec0358870303af36a7d7dc4bb01b3a783bb929aa2d62aa642980831a16ce22f0a99069e26c1e9c5efd7d91d8b083ae16884b784b978faba9e2b360771872b8efdc0db27838376597ad25f6e7e018b77e1b57ddbfca6257e34256c3ad2c6856e1d33436fba59158e6a9054c9cb047a7ae0e84705b9699947385250371f20c434faa45c08682ce6217f3f3fbf3c07202ad18f3f726113fdd1c41817d082c1bbd5671a99463dfd96e9008085c4fa082802a3f8ecdb0a832c66449aa132a0841104c9dcc2aac626b5eab2f22d5fbd082e56d7f9e8f191101caa2fce867dd1d6fb6aae8f5e909a35634d8ecb9b81e70902f65b00bd1d41285625186c7ddab6cb9e5d8c80b965f7c55d32a07911b4a509e05480b0a8d8d64d2e4dd7aa65f1e3b607e52774a5c1ba9334b1c479eaaf6b02380b3a71727cc699df63d617fdf2edc512b065770e8b32b72739a94bde584b0acbd359c45cbab9ece348aaa61cbfbc830ae9633c1c6ebe85e375b020bc268a0a9697ee3977d9ab5c84da46e0b55b88e27ddafe35483c5413071cf72ee28dbd0fd39c6da06e0ee367202179c13ae224a54afd39cc9a90c7a05ca9a6dfe8c9e60ca1f58baa9b4b4a996dc7d56250051a55cb3ebc120b2078922cdd2ac910d87107383682f8e79ee7a78a691a66c361d838e995c2409329f9da64ea7c5e7564780a222edbf7e23e04b13954ab1c5aaae255220319fb21b74c7d01b752b3ae607b0fe51dc195cfea2331970cc79dea43d87a25eb0c81a9bf4bbd5232e2aecb649704c95508fc8c6e75096e3a97bdcc293323b24ec30ef17c6d572804618415a2d806c7a5aa49c4d7a007362fda13ef848953e8634f8300c01b3dd0d0b06ff4be2e050fecd8f001d8580bdd955ba120639ff6d0a4d3a7253ca049d66232ef5a392f0a3659602602dc425f95afa05548ae87092bb87cd1462bacc0fc492cd78553c308503959b7f9fc96ea6652da385e7a9f6592bbc6ad7c4300b4957e65e62fb035016bcd17d6c1efe801d648a435e6ef769febe17f53b60acb4ac7c56ba348cc7b0738c7241048f1da5b67fdcfb829fe188ca66d22c06fd7d7e8861fbed7d8f30800 false +check_ring_signature 52c1a365dd6cbc46060f9059b5cda9f0af5264b9214f25dddc1a2e495a7f4a44 fa70f27648a02aa0f4d785de937b9e5def004f56ce9175d71c289cb5b510899b 58 494372a1e5c5cdef85731996ac6b036ce3e8be06f6187f7283ed7ee04d35fac6 fe448554548d755bc1ea16ed0f6c62a355e59301a938f4c8b44504ddb149ca68 ab94a7f837591ba3dd03d2e9ee057ea82dd1b943ad7f6974f60e827f407069e9 82afbde8deda178d4fc9e8f5977039468292f9b50b5890740c452fe02cf71f30 cb2c6ad50f4e8a9b8cccd31d3b68cfb8ce3ef6d0e474adb13f935001aaab636e 40b922e6cb2cccbab5030bdb14f2ff198c7382c64272282905e4f482810f46ed 4dbabc07a588b37b6ae3666559fd3453bf79513d38e438535baec7c9d7350d84 3f6cd4b78d3744659dee12845d56a2d3853a2d169a6b488888db8aa3e6b1b611 a3fe3e8d97522a569059eba5f4a7290659a72d5747ba3ea75b059720662794ab 423058c5392065d137a53196914cb3603b92746330eefddba98ec8552bafd729 7d13ebdfeadf525887e3fc69993114feecac5445164eef5dc4a0006518b4c0f2 a023f2f5fb16cd00e704b18997a25f01b4a3506796c73e4f15a87d184e031a0e f6fe2b28b1d7245113c3622f261aafb7171e400d52377a7c02b4243ba1d3b2dc 9b2ad007dd7278f386779f8b49034f1865779d3b57f83b174a048b9655f64b47 7e40d2f653c8249b1b8ec5134bd25a19b68c1c7aac2db52db07fb8b6c08aaba5 7d5021e7414021962e01254437ed8c095f7cb50b9733b9ea6fd6d10330f5fe5e 47e4575a232c20e5c9fd4b8ce3310f0884224c6abb9fb0824849c881ec9cb0e5 da57f3a47dca64edafb9677f555e7948f0d0fb4c3375d3820875e29f2562c9c4 22900186de5b6cb5c8876af64cffc3dfa0b0c13018dd4e227e3bee5ac531d5f7 7a5dd3a0bb6b0eff87214ea366707cd24adf6721253eeeac820357e4e54ecd95 324eaa2678edec495f6238ab6d6a99aca4c915c20b68cb265552f9f6cf5780fa 5c0dcab1a92fc2d36d658b1e4777177a26cf9ee95c04778ad8b6d1b64c5da8b5 743c085e356e9779a36e5db34905ceed185ef020f2b7100ac8308efe32194924 fc5513ac650edec85df5cb0bbd498dbc19f3d06df44e3860d0dbad7f28d7f388 495519b377d0019aec7a9b5f1e778d8c435fefaae83497871ee8f1f58fd9292a b3aaaedca3aaaa0de8c28150d7575521f31111a76c89445c826898ecb1768f35 7ed37dfbebb44585267b84573e9f979d6e2ab331415a4bc0c96ce102b95abc19 3b5345cb489f026d572c183fc78f38e5d6da174d98588011c29721c4305a0cd5 124d1e7eceb7245b39167bde3b8f12c7fd23d4014d49a7b5a989665cc801f6ed a97dee4ae2901d982d3385e87a1c40ca21d95a5fc3a3777bf3b81eebad0acb81 2419ba765d8e7606ccd42ee63505f2b728c3b8262f84bdddab65924eb63b70f5 dd861dfde19dd5f137ae5140f52d56ef5a5a53f78717a7ef85133c15b56892eb 597ff3f24236b4f2c53813cb460b437605039d9ad1cb5b6c3892118c99158d81 bbe7a61785b22f80ec5333a7851bd8b07644dd90db88432cba0fad8659ceca39 6d3bfceb645d55076ab26a86e72afba63b44d1babdaf351773bd31dff0cef554 51989dce5f002607e9682ca5818963182f27862b9dfa12ec68fc14c75b954cd4 bf7a298366e3dfd2361862f51065ac14da13023bcd3555f3b80b5afe8c385169 dcfc5aad2c73fbe288d318332da5a07a3d33568ee999e4c18782773b33fb6509 e84d7810d1f59098201e8e2971ae04af5aa22e8424977030dacd43493e802292 1f9e54604d55dd9d4a2e993855fec59b784f8d021e23c543b5a390f427a002cb a13f8c222a4eb221a385b937bfff0e4ac1f40caf9481597735d81773c7e405dd 0301c0bacb590961984b0a96324e93fe04afbc3118aa9d092338f8f8fe304751 2b3b5e3a4adcbecbd3f092a195c2849097338f300a6100eaf11d44e3d341c871 f86b9809420abf7294bbdd33e997f54df2fec18e1b896e7cb66958182b7d2b07 5d9b4a36da153f7dea8d372a609150f26cdc8133c084103db2040e22a61f0c47 4b2430da03663db94565ebf759702cb3298beead605a72d42d1b7172c7dca7d7 4160cca6b9f30bc43ef629f81525e512e26d974bd5684c180f7bb741b542a65d 014ce9c5d91ab3a105d8d154c3344722955ce7d5d5bfc1f014f84ecc1de0da82 94cc1d3f011fa499f4dd18f4e3e07bb4e78601ee2852df32aebd3f047dbdcf17 cc7c61ddb64af33579dd81de5d4c15b2272a04ca0a67617ce80356926f05f5bb 63dc065affbe00724e298762d803e2a5181fa915ca45ecd16443c63ccb797020 28c34701d2ec1b7f4e56ab88185102b84352a5594f7a5fe42be42a920765f62f f9599071ae0e93de8da18a3a4fc2caf90b92bd6560d43992284c2d75ac268c4a 994a6668a9a470510c16934fa22b411fdd229ccaa09d5bf09c1df67c30347da0 e5b407f5b0fe7d7c4a7be450ee56adcbf8cc3d70c9aca63c062ae706a16192cc 07bb15d490a9d88a740c475d62992a3da7b4f75fa3b8443ec7db289fcfc0dc34 dde99ca622eab035baf6f524ae47cfe2a4ea051ddf518f6b52a6f6ac37d1a027 3a26b3d0ab5285147e6c024e2dbbacb7fabfaeb0e56b8eb70299f2c55e110174 5af3dd17c46c275963ade6c22da844d9321ef5dc6dbb3e18553c492b9698ff0b317d2890ab14840f4046422e6851b4485531f04530e5d1779a1803cbfec6130eeefb605f97e93ecb12dff217dc1c985477bc16ccdf73c42d132b7060626f8d0ca41e947bc07e826856e0c00066c68044a10ef63eae3c9d00fa6466fc3b42e80d90b0d44ccd4f028fe0152d7667ed4256ea752019e9be8f3adfbb1a4b7872de00b243cfa258ff0ffa9fd9688ccbb0d74b10fb9a5b89b76f1fa5244bd49f76a002abe380735439d8424b1afb4df98fa99b92d4fe95652c6443fb9d6bf3315c690169d1fdbeef97a5dc1c3cd8b488860de2d08f7f59c6063254df451f7fa9bba008c43cff4c817696cb111669182c79c9709063d80d91bd227ee9aa304964ce960b05d17158fb9ab79f438efe7357284343ac04392880e436d3824415bf58d1ca091fbdb667fc569adf8cfecd29e6f9f821013ea3efe7bb1642fb737142485d0b0e02d40bcf0e7446bb25e4a68a66017fcabcdc8b772ce077f186515dcca186b4020476d73437ab2ec2f4eb2fdbce768bfcc3e6151fb9e34847e04f4416e496ca06b3bfcf14f55fed1febd09b9fbd58b7edb08bc920950eb09554235a2c3b7597052c18f2e2804e3897b83e23ff0df78cb17e4cd493d5a334145ea52718e4b046002e34b47181e7b7b3afa81cf01917f26989a4a037c3a16539385ec7831f59ac01a339e8748c042de6766f7d18d487221d05e73d347524ff1d7e8caa62b2031b0053c7aa5370c1cdf7a6278845794d272f88e5bb39de8daaa4da135186676832013de1856bf145eabff370910a0f044c7857e98248095c75ee5bc0b5ff2485d503ad5488333a11b79f6a2424d2873ce7282f4a9120a2d4a74496eb6ba116e19504aa6fec752d8d58a41863be0c1a956837e44ab561d86554946c8d28f167ec9a0981696deefc40554ca88afe4f229751384e733075d4276ec6d8e91e0321180007af58ee5c094d210a832d5bf57d5fa5fa7e65c34c4340311854fe9548dcac1904f1bd3e0fc498f700e6efb3340f4b9b4a5f9200de8b758d935e9aa9e14229a80187edebec9085ffff019fc8f27802845c47512711dcf8bc59ce2d7c7fee8f0c09ba43c5eb38646b8f84c37e08fd1a447671be549431ef8b342c9e826c23a28d0085f10514990058c638562ba983668c0e994af62e182fb1fadbda745ea1106e0b247172f1aed77a9cf40ce55ef84f7f9af65a74cf00ba6fa5ae4f501cbabe8a076641d18e6259ef7056d581cda5590aead029d1f02b86d967f068e065188ea1094a6e2969ce4002327e151f0b579f750b35a0ce5b9721b505c67059eab7fed60a6dbb79e1326203c87af1b541f6125e7aa077b57eaf7d6a9fbcf22ae2a7638a051d21009e0bd0ca0c3757d04729bf6c0cdae2f604883e335ca33c7f739fbb9a061576a5e9397923ece2d87d58beacc3ea622871ed265fc3a331e6b59ac59cae051d3aef87e8ef6dd731e189f728746ff19a13b70c903c59c81bbbba5df27c290756bbf4ea336974e13e276a9f3f48033d295e575fe9a9a0f2597599c70808b6066e8932c3e8a992deb037f410c96e8fbf6182caaff2cf91ead11481b7079c5e053681f2df1e03537ce46bb47d456ead61981c0aa53c1ad0795dae870749b6890e17354d7f7de2a210c0f5f39029f82ab4560665041aa1816c2b57404f27f6de05b0b55732554276b8cceee183623870e755eeb4b778161374044e6a0861ee790649d56810ddff91275e16ed4c2e6478a8249ad5ba957ec7d6d3bdcb803f94a20b17e85e1c2949f342436465b60e7c297f223341e8acabfbd241e98048f5661807624cfe1ba415eef01e8920eb78c79eb0b13b4a3623a5875acaa3e8f763652404c00f76e7d1aab643764f009dd2c69a862a503a6bcd14b80a5b150c3e80ab9d0c943c771ae8d70da7e897d2070c720788c948eefbdcfecea596a17ca5d8f3ed0db31aeb8adf055f1e47bd8770c50153fbefbdc96a285bc49cacef1e9d2221050a571398e5acfc550f604cf5b7c4a7b3954343cd80bb6dda2b9873744f7b6b4e08d72805a7d985f0f9368d56563c6a402894659b3ecebf11289c583dd3a4eca805cce5d449f173488bd10a0dfaf1b3fb8376d6774bd0f1c162ac48e65cc71d50024b6ad3d67b1647a79bee2ce15cdfa71d8be3f8b7ba5ff645718d086202130605a971ab21cd7ecd0e1a9d1b7505ac2e4e49fdacfb8b28bb551c3e6098fd47e200e351e99be872d854470084b00271eda6419cfc185ed11ad7d09ca6409cf85c0456f61fbbe472196815e5923997352bbbfa2e5851a2b0913f876e718566b78e07d99d1f82dd573f269d77d51768b06be92a9c43a34fb6918babba858255e8960e3b1e83795064c91db90f2a74067840ccfda44d66a928401f0513d41f534864015ac6066f2e53ffc7e3ce2bd6513948d181b46d3f4921a09abb39909c95fb6402e618bec062c5841a586cac18f0616de990d016f755a42ba9ecbe510bf2f9a50f4fc489b6a7454c8ddfe45ac8bae30144465c82c8fb5f788aeed0374a76077c004fb95acd039e4ba2829e417f7026f749292e1090f19e93702d7179798e60e701b89ef712026fe1149a6176b55858160f8f0950e86af1a369b16d119a63fbd201341d82de516b5fe1090631764d3dd26dbf641340f7abbe01834e8314f6c4690094e8e9abaeecbabad4b09cd2ac3eedce58c8b88452807d7f308bea262adfdd0d6263926242b7ee1f999c76d83cc1e74e0203a64bd773640f2b30d25e8c9d520dbf63dcfb5509563f8be1d885218f1308d4a444ab47279f1de725a8d6176e7a07b38b3d09588b54d3fbbd27d3cd57ebafbd59006c219d79f605f7cb57894747051200dd51d954c6efbe2f3f6d616c6ff26392c041659d8722a91b4771259b7e0e87e1b7d3fd877848730c4f0ed429e6594ee758de3fc011989adf36a9beae220b37373214137288d9295fe7d2958fdf4ff1d5fd0ee98640399b842c22f54f480fa04b09f994cc91b5f3f35e0e63b5bb3a96bd735e24df625ba7eb2c12ae1ead0634e14477939e192051facf54f570a866bd16b17636a3ea3fa2b252edf75a7104772aa9c846f7c287a73f97cd24a6535098995e1a154154bc8b2f2506f315150142329c67d1815e718c3a1cd7225b426a0e0a2db49825d46aad245b11f76de50e4d659cdfe002f05d5f324e3800ea9bbbbe26695e2d07e1758f4957fdc3c2960cfaaf957032a4cc057f8466f7d223d8879630292e92d21b9445c7dcaa4cd3570893395800c92346964c37201005138bd6f4a0c41c2bf8cc2b568602f42f492d088d4e78eb9296f1d609a2c7889d68f4cdcf740d4ba95307b9b17ab9e18641c80f91829bc54dae8bd61848a58d7005c81ebd46f99ccf16a2183c3c94a134223f03a415224446d308762fa0daf3f745db01cff2346f93c3c6afcc3ddad858604804acd34cb8eb7634943272f0709e5ef972ed5870894283dab1b567d45bf10f900a52e85d1c992e37ab1c7c217072b90be3658c233ba20045b7fcbf4676893bc50e594ca3291eb1f66ab60b6ad4d8604ec3a99d29a6871435a6cff59b5011aa6e088def104818600a47d0eaa5d90109b741118aa93676b8a9f7799a4d0abe0d0d092dc0c2fc75f5b340f152b1feedc59bf1970fd3c2fd518b48cc4027b64f78af0f7ca42fc64c2a773294da9b9a7810e3ca7f1cf9fcf32b2df358dd13ecaed6b20937674a3b6f13ed0b4deb2d7737764f0fb50f8218979e8b877b677cb4c81fa00abf41d4f4a3ac2fc55f9ac15c4512d671db7ccf18d3526761a2c0cb7e25ce7905f91c5cef7cd2491b2c542ca3a18ab153cb332455692ec0ed8c0aa4929c30e702b9a43a22b73b0f0bb29d864f882b37788f854b1597f3d7de68c66a45207d340f77a12f4ea03b1d0352e7238679da008617d2a610e9c1f7109e7af37bd4b1e705eb44450dcdd282ef6f6996b632be3cde9d39d28b136833f663d235b0b93aab007af540b105130d9a139f31b3bcea9c72a689636da401cda80a6798b2c1d1a602600f7a9dbc4f5414adb275ce67d2758f497530de561da203be96772c1f99030183089a23762fe2c6868e730e114240a775992d5b8ad8b99fd859a6dde12bf7070b66bca7cdb90d52fc03fc21332ddb821e45cf07cb2bfdf12ab4405e7d967e03780a92f90d3c504d830e44be4804cfcd7db8cd18f8fb67e8f77308c080daee063b6421ceb19dcf7f24c47ef54a6c724177ff1189cea92b8a9e9301c972e2610c62c82350d6f6dde5ea47408f3da9f20ab53ea7ef81bfec433ab7b86eddc3190da73cb346cbb86e8327ac52483d5f396677737ce52b99b094410a775c1db3f00c238c3b399bdb3307b6d124eaf74009c84a5339b0515190cf4f3ca9386c16f80fabd21004541ac117648ed000adfad8f4445ccb88b15547835acf9b5a0e55360e03162a770e8c93a401a614b297fdd44c1084956074e0d23bea6494dc416ab50cfda4018741924edc1b63adb76863b8994036944f7dd382dcca3a36849b5a5c0dc69b2e0e01c900d0ad979e6ee80391c6fc8396a1726635622b9f3eb9cf28650e98d0ec476a314ea04055e58a686de5d13d82d63fe9c0b2edf478041c9221240ed28ff8d8a5d6c8ceffdba650fecba6558862b0709bab24a2a949449311901f0b904ce7df5a6b00046b851424a9cbc20ef7283231d796465e65de5bfefe3b250b74d53e550b75f55c7b903622047dd93c9fd2f7402bd29d585c654f80a247050472dc400cd9a5a077ccadaec874ac9dffb77b9135f09c116c1c7e7b8b42cb460e0cf25b38fb8f063fdc9b634d0ed6cb581b06a8b5e7c063cfade3df18dc4a9500b349707730fbc9608bae1eda55b905618e688f006b1fe586bbd8d18aa1e4740d430ef0ece185ff2d501c9ce4d6440950cb3ec9c52600e39141daf164aae65607ddd093b68a5bda7c6b389d1a3ac0f693d1d482ab5e37fa5854e10280c842990556086162e1733edc34f6f98409f89e43d8e301ef34c009570a2b38373cfc4f00ae37d36f38bcffee817bbebc7a23de76ec9ae3c9813b530ff00f33102fee99012e4498c63c6b1baf6805b2bb0f462941bd892fb1e85d5896bd1207e47fa6e4025a5f8655c5cab76b6d708b899feb4b8881bc0e8cb35573dc2dca63e5200a2902e45187eb88c359842fb8ebe3423c0c4e309bb015cd699de6ad7e13310aef630b false +check_ring_signature c2677c4134632229cb6f08f5f374d46244cb64c3aa0b7a13cc931075a498497a 15756dfdf27cb386a0d13c7401d644ad3a16fd9fa6e99f1801875404fcd06479 6 b73f9547ab38a77f9d5ad6c702b3d332181fff33cc01c4acedc9ce65917489ff 13b63896691df2bfc89c184cedfbe6be55de09e0e9a35ad75e903ad80bc21331 f44a824f34d83df78634548a59d3571008062e1b1d667da1ba89e31ba6a13bf6 e4b60f64f53003daeb359fe361adfd5bfd2e14d29a4fc6298ffa00ad24ba73a0 5fc710d3efa889da21c5c98030e22a89a0adeab23585d24caf569605793ecb85 206e9d0f25f6be316e0002d2170e291dd967dc339f64ccb8f95e86bcaa4e091d 1e71c169351b653c5ceaca548e7d37befe18aedd4548086158a2317c11a89a06a0cee45191cafde8b958bc1d2a444a8c2290aa9e8c6c2f17e89fb7e0a3b5140a198f20185616af2080f90eaac5263e3f79c6379261516728423cb6530dcf0007c22a5fd93d2b6f2eb247194f9b371a9b73d50783b95990c4d59ecd909dc362f29f019c84225a3fe81368bd8f058a9fdda89c8b07bcab1cae8626dd2a16c1b7003f82c84ef0398d1d90006f7558f24a76fc7607ab6f27955db4a5b6ac83370508df0ffb9b9f3e4f08becd442d201a9224b1b839d97015208808ffc92581d45a07fb4f60f9d71547bbf59a564c1fc4d5657b07cd6965b5992d76763701a530db065f87aae36c9cbc3c96e436b299d7c36a631da2807ff6d76f24bbe7d4b2e95503de890a24cb39638cdf8d2b2003840e27cfc3ccadde8b0f54eab29f14b9c2970a2df113fc458a9402a275871b88ddfdd770a6c67f4a36728555bdc4c8f4804f08aadeebc9d4e11d10a49403abfc0ce186d91d16650af820f43695ad6b6dcc3b08 false +check_ring_signature 992f2e7ebe99a6fd70a55474aecdee8e19fab3450519e7bdda45077d703726bb ba079efe568e5e8a8a35becae81f5caa75802edf6b8f5f78f370180f1398f7ed 16 7952853f2708f51db6f9e5b68acdacb045e5134b78d5998f61f691f176a44e33 3fb6cf6f5cf6f9c20385236b5e30691b27e96f7b48a1bfb6f4ec678b82fae14b ab2a2c93d5a39e9c51445f5f83b304c3bd76c906480eff09b6c9387659eef952 689c962307ff873f156611a306a6e87f6ff63ea4a9db73726f7f20297556d8cd 6db18a0b327874608a8522c5e1a0e774b4eaf5242519e9c89efd011454ae9353 52f6302e8e9eac0668f44569ad1b06643ca5d56c758b8706303e5f13abe6cdd2 38b27051a53ad9c1fffd6b9a82700e777da8d9300f417bebb56e17ec0087e8fc bec682f8069314b9f5dbb43d64fd9a7496dc18c6c7602d1b9546bd58cc6bb6cf 04e28fb5b3293c249920f68b0bb4fd9710e1e94ab2990127615712d0a63ac32a da6964dec452602bfa0b8bd5df95ef9e27c2a2dd9affad6455a0e6ec91e883f1 b12fe6559590abf6d5d0e033a40c79ed81f8dc5be7848ad832af4840ac8b459c 7866996c4f3b1c37e2d0a3c4ca4a79dd6d084dec19a0b431275c9f2603ee51ed a2527bbf2563c36c4f846556d40cdcc8b24d804d6e38806d744c58289963cf41 307760519537d1ed00abdb7b759459463ac9e0005bb86b8ff206874bb6946a61 8a1a4d526f06997fa74c2d5291e60bc230df8452736f08fbf186bb3d01c77e9a 81b885e6e9ac7fa30d1d2e3ca5f08b5b59c6f71306df74ff54f7f066c67c749e ad047d415f0e37f71f6ab38d9b1f72640e991dfd73bcec363c77a95f27cdcf057003af30d8bd976815427dc3004193dba1a92a42e4b21d2a0616d6c80e618505965fd99ee7690e173d2c4858f06457db46e25313f092ca3ec6486aad5e095804c5677324e40793aeae55dd886fa6a95990194106d7be0509d2b6b3f6bbc316098f91b8864ed812fc0f5703ebeb30de78a94232bb129c614daa646cfd0e2fc60fc1a405fbd1abafe58978d5023fec4c19eb76b83017ea63d33f484ac52d992a09b3ffca51b6587bfb547427acfa006dfc0fa89f7bd6c75e37c5ce6e83af3a920eaa8da1f560399270e2ec16ed5cdd4987216219fc08da219e2a65784382084a00d52207461c8075c2aa7283c32e4a02d7a692f68758f159de82ede492c7c04a04b0e00778f23b86aa784ec2ad8627414c7f7d1d63b59f8a4f79d5a635a2808604598fa1084a144a9e9d2e9894bb8db8e9273b6318c78721e438fbbf8c341b540d6c04d1ce9a92608a1499c7a7043a2e1600993b92e7c91913f077c1121e5e7609f1bc50aaa10d6d40f50d44c8fd91c0857b6a40127270ca3b10e880ba854be40d5161b878cb5f293a087edab94af7d9ecc2a173162b01b32c3fdc62a4ed22da05f69ddf6a46e7f65178ea284fe5353b9e03f2d5929c05affd31d0187df12c6a0e51e82f83a6b2aa11692d1d80528983db61682eb0e01bdfb9e0100204a63e4c0f81ebc2f46b444383e812168bb2c53a994f0345b3d13f386717d509fd689d1305fc795fc3e469dd20329c06832a655990724fac527b8d8f106b211f8fad3c7608793e200161f80608d021d1a7a84b94f68df1096c95e03040f579fb4eea83d300dcad71b9c4222b5c0e035e835fe3e101229053e52a72c3e4f558bc4da47b9b09e1489bf503fbd84c0d08be9f6b12789fa585d2777de6c8d7716a422c18d2d9001341494a17e93a810fed71738a4c3e5a462b32e764d192f3e7cb17cbb977e20fa661b1136f732f65b687988bccc0774d63e8fa5ca606b925564b126d0ff9d70bf584feb8ba12d360285332170f7db27ff84b80e53b690b61b1ef50eae805b80bd48134665d8ca257c9b5e65a5645c0fddd05a922a07c674d2d8d458bf0921a0f101a9c6ac975a30eb558221154d9d5c45f4c1407d66fda1fc7711f65eabbf009347a68bec2bbcabbe9c71b7c1787a0ef38000697bf2d585d233e932ac607f508c4647e2dc55f77df14990fd68b57171ff7cad3d4fe4a1cb88931018b4405b90573945a77a297448fc885e5c661cea5e30c4b761b7d77b459d1d755f3639fdc06c257d033549785043284a9cb8ef9aa6393bff6e7a6d6d452b87181f0bc213e027a206abb6f88f8d7eadb72ebc12484039e4e7932f07eb4bb86965f097cc8de07192f6c0e9f405f68ded1ff43e1c97dfb70f23343f645ac299f980c9c6b8c8800 true +check_ring_signature 3d443542930c0ef86c424385dc0f4c2d2b33e667c5e27f5ac2be81d2f5fe79e5 c2b399f9462663a01f9a675235825a67447c35c2e4f956863bd67a44c3e18f7a 58 05217451041cff100a6ba0eb77a7ee2bb5d8dff917590d00697f69cde0953aeb 0ce6cae3d878594cfc8d09d6f849d64c76cc77664d5ed7399c79c99612b373d1 20db9b8ebe0e31463747c6d00c562a8d9a398f5da519e0b15651859f622146fa 6647d1e344521e76d6fd59555d3872f3c64ddc8a1e40307b6a88c55326c46324 cd250b540adaf69874edbbbe50099b18f428b6ac6a2ae22038a245ead28c557c 53360f8a8e7063772a1e034549430e37de995bd9f190942c06ee92afe590a791 f0aac1822964ce2da86b4283b0754f3f65b5e2ef7a5ae22839be2417d9223fdb eea497d6712221004db03d56592a186e54497a05e7a39df858594555e6dbf1a5 88b879de4886a33fba6e8ed605b62a9f9fde47a348d6ce30dccda5cc963712a1 c623da00e425bc0c01b91692f001c76ff102d9b7eaa38263ea47903ba71cf9f5 0b178a4fa68817e7f0b21784206e329793f58593044f62f88ca557c844a555ce e770df0ca5724a353d19581e13db9bf2a384f71c606201fc754e94ad7c98b27a ea5b3af477259ade9d1ce7725c0f8eb993e93452c8359ff558f674b3cd003e4d 2cb1ff594be29cc6b5ded7730de8c760407df97cafc0456494db013c333885d1 037c91e1f0f3dc7b2ca9015752501809cd54bef3c4c273b974248584a263a6c9 4baae72d8856e1fa3bb1e3253edfaaca3c1b2456ed86f46277034cd5f902e10e 987cfe46fdae7bd28f316f34e29f65961d25e929af281a890b3209dab0539cb9 806fe732b530f38636d01a6bd3fd5b2f7a2aecff1b825679b941fa0267f3f300 9b4c1979df55a09901dc5cee9c111d7988da7905a5aaea8bff65e9e56bbf94ea 1123e77728e36707a6a09b074abd6e866dc55e8a5f176296b0e88ebecbcb6a2b ee01fc1bacb25bad65338653ecf9fa591636aaf21db08d04060e7fa478bc839d c2125cf916ad6614b55afb5f8ede5b77de9a884f15ba2bf9c6ffd758891d4b7f 8e1aa8b8b4a93ce3f2b8859ddf4782097b691835e3f28b00afa90474a9564f9e 7e2f5ae4551d38fa81cc01ddc69bf76c3ad483648521201566cab015d094bd84 cc4d29a7de6145130ffa3f48d3f4817a0b4aa01e06cb0ad4f2d129e78843c240 301f37aee14789f8dcc442c7240deb3602779f38412038dfb3bb301c9663fb47 5b3c5b0c35c189e8e1e1e1d4479eedfb96cf9733d5429ced8f2b76eeead2a7d1 edede7487f445b7eb12f10a9b38f69bc694516a111d0f42fdd1ff711ffc985c5 29c56ad05396e5ab51cadd45de44dd261e0a792f2638241fe6b1a18329052055 deb7638138ccad2c328306494cdbbc974b31757cfd073f631cca00b02d3397c5 6ff847dd0335f24c90b0db769daf36feb7dc975d29d0c505ca1fe4987f32b47d d5de47d6ecc5ea22544bc0187902be5e0e8e7be161f8a63009e62cea008d10c5 2c3ca1dab30cb650161d468914bd34865d28878c42e7aa3c96d6a4085d6e4e19 dd17856ada02d5c415367e3338c259b2c70f6204f6f184af8fd17c89cd42cf4f 967a313c03d3dab762362cc08c4daf9519a0df537db1fe7ccc754285ec99dfa8 9c64f820daa3fc0df0bbabcf91b091b8205bb78a2dedb659de9f1263b1a005dc 074fb827bbca131dca0cb006216d5a85f3b910dfef67670a85f9c5ffefdf6055 0851b67dd67a1e58b8bd1ed1ad4db29f0e367e0debebc3b0e4846ae9a8fd1285 cea8c218253f56803c0414fdf5be78b40a06a55c72b4f19892e456937a9c63ac fc1c645aa9ad01df4ccba4cc21cee9a65d8cf576ccc6bff36ff08251a14519e6 539da9486f6c3b9e9b2791414e6becbee6d77821937d916d4a32d05a7534dd03 ba720a5c1240cde822599957876bd2823422db812f33903767b5dcd5d77c30dc 101cc258c12d25657019a33234511151102c4bef366c750171e0beded55073a0 d99d24d7406b8a4a6c03aba98d8544bec99dcae0f61c16a0aac3ca2c4b11fc67 a1ef9c752597b3e9225351804d9b6971a02211278a1d43adaa5ed0ab293ef94b 6f825f0d6fe4b7cba3951247deedb39d8047c76ab41934ffa8c5149d4bdb98d3 fbdccaa3a937036953109c5ba55a4002472a9ff5fe9983f49d1fd05f98fe4adc 2e3e7e0a7b8cb55069de8e36d8d51595ff65ab94af03fe7cebd89be0362ba14c c7c0d2cd5159f388a2c03986cdb2bc7b7515ae17ac63495d47707f02c9671fad 553c5df1f4ee083630546089f5deb01f6cfaa394e367681b5e16083b39520700 f420df6f3ce37fa502a72cb1d6e4b8f6c44428aa47bc3c53b4d791064dc82dd4 798464b5881c32daecaef12a4d53cc4ecb8d72f1c1b0c85e65fd85bdc3a482b2 2d0f97cb6a3ffe711088e4f06efd31351fca76b5541916bbb6dde1544cf074ee 6a4f3e820f5042ea849d2ddbcf3c1d14b188c249f1db7ea70209856f24ea8f6a 6889cd8a174ea82f84d0adefb384808ad17cda5ceb1040bdb22d844be31330e1 318fabca6ac382fd8ce624249bec8a7516cde222995d5c9542cced22314cf48f da42de22170747043282a026d1ff9267d3cb0b6d6379d5e69686f8b768f9bbe9 0e6d726e09baac55699225c6efcdbdff3995da8ed7645a648be2a999de68a2ff dc233c2d22a8a44c0e31815baddaf7893a364e799fe90f64aa4d63c3a5ed630a510bd4b3262b54c2d81156e80ad0cea15202e7507d370ee66b4b7a91b886140caa20f4074c21f5046affbce908611e58fe43f2cb38acfc29f0362ba4f5503b04f49528b3f7fc825ba7498b75e74bb91461004e7877a9c916e82c10be3c9e39089195b897d3e654cac3611d4247b4b12b448286609e8c1b448b78498b8d14990d9be5b460a2eb28337da0000546e477ad1f6079cc0f43492757d38cfaba0ffc014f1175dbfe0c04304a719a7a026529831d00d9a277dc4ec925d0ac4d32a8ea066e77188b48efe1a9ea5fa39076c0899581129fc7be5b31afc98315c8cb1a6f0770a385654e1c685400a362724d88256343fbcc975398933b9e90b5a0cddeda0aac5116af5ec6e1db431f03816410e0bd97b7770297aa1272cd7f898d84c735099eea4a9968af951b6ca38aeedd3030c2d5bcda34b1a0594288535d7a6b0776048a5d5116f6703732180710da622bb62d5c88d3cdc9977ebe246f889156bc770360c102b52947dd6f7665a86984c18b5ccf8b52abd450cf3b515fe0d0946a820485de51cabad614760873585050392ed73d175e35b58b452978fbd695ffb58205836e53c98f0202263ad07b0478df91b1ba7334716cbd374031833de3f1f4300d485f2804d394df2d33bcc73b54eefd591b07a58d122b0c05eb15c4d25c2f620f79537a5881b4d0193d4ebbe1c63bbce0a423cdbd6db2a0bfb55243d8ef73100bc16d90b24c2d59f1a3bd9833ba80b765e972d78e631e7713af436a29aef8d404017ce9a0761cd39b78fcdb7f8345907a25b20d0f3becd81631d03102e5ba650f5344c033115294540a0bd8a535d80c80627df61df62a25e4f96acf2e3e02460204bd480d46a70d20c1efdbd5ab0e0e3d32d0f005563c7195e4b7eb5c0957550060209cb71a5435409fadd9612edc2652d58c853f32c2ce1e69c5f74264d1c800c1931bda6e3965b79bb77f1b8c6094d2003a84db44e34aa2dc94148d95ac76012785c1705b624d3efb9df6348d553637b10af95e00b07a582866369cb20f3907710c3f49da70bb2c0685c2d7c4c35f222ef6c0c8b9645d41b8c0204df60a3403dadcf213a476aba32e3cd5e68b4da9cc82e0275240f4b6e7a70b6418850a0800765aeb56f899f8affd6cecea2b8aef744add39ff908c472bfcc04b674953210fdc2c7a16a6ba56db35ee045a5db0af57bb9e2ffd2499ee3fea21ca3bdc394f0c55ee40af74d01b785986466824feebe49fba703dc4814a9d7461a6042896270e6ae4bce16f562d959a8ba1e0bc279db46b3a00dc1071dbb0c2eedde0a0106d065d4138621ecc797581f0f52bbdd659d349c030ef4f413bdc403a260ae789890bb0c64cc037f0e6a6d36d88f9ba5e35688ae802d929425ebfc5adec94cb4b310340493234d9fce8070f2a4723a55e8bb148c58391de66517b5d22a75f5ed7bb0526f506a97b7571c5075cd7ca83eeae391c5e3ceffd529cc547d6aa1198bcfc0385cb292cad51e7f2db93c13ee4dcb4f31d280c3d4b79ff68744da3954a17e20e7692812bc6e9141774c89017efc6d7d96c16691a0eba31e3470bffb219b18208c3719c963c445cbe42f6939858015565d1b59aa5d79a1c5bb3f2a29eebb1810899023e230856aa71f2dfe8946e6ec1233ca2f401311d255f211ba9e66b35910fd98619d519dbeced454c4d050322dc41fb3ab0734b2b60e3f3fe48e7e0d6490a5a8fef326e7e0271437a091a9c14d31cbb5fd8a50affbf7a07e19cb5a517280d15c92a743d061ab68ce0d1cf48926b92a855d189e8c3e2d772a4cce98ab19405255a1688792396e1524ed8c4ceb6f7e7f920ae38684fff8a66049bc9aef47809529b310c36710f20f760baa89a02e7ac0fbcc9d5d7b7926258d9c9b9c3d0e00b6f372ed8ce0558b5a7fb79c68a6fd06fe1b169c58e648e211fd0bedd47d428072d7e2176722bd3b43b1442bce135199cc972602c380d9040acdb73acdde2a6046a152b9df3812fdb94c5cae5fb2b6c8b3c42dd2ce5d20103c09616952859ee0512d99154875f2034ce922a65af7f7e2c147bf2dc3db5163d76e872d0991a6900bf8571b68ef74a33a59f3aa18542a33a31825c5162c23060f3adf411fb26ca077521ab7bda09bf59a4dcf2ca55c65bd636f0415d873ccfe57ec93ef274b1800edc7a9a6e8665490c65e086951a6c7eb27fd36896d2864630d2b736c02b4e3004ae2c6e20bc54bd34dafd8ea7b44595da076061ae12d1dd2e430353c6037a400d721933f984559d5b2d2816ac63b5667c18373dda9e64d95d7d478b239017b50fe06966ae86456862ea0d3a09aa494245a27e0b6243b81d4d87eb90da4a3471049e04dbb96abaa577334577b6e1c2853d23a6ef4cbf0debffd81e2bf644a0ef069606ae0e794297f805abd1ed874417bc2a44379cc1655628bcb9baa9aff851033fc4c1ddbc74258ff429b4129d2d057c82b76dce7395eaee53bf326937f3710cd98b21ae3677d96da79356afad37b8ff0af4c9d53fc05f317c1bf206db9f660fbc16745e2032faff750e598f6b41c44ef03d8b5f9f9d1bf9426272da3ea62c09795206be015fc502eda8d6df05457480ad27bf8ae152429b3fdec1a211f0750ae38cfd4d33822af30966654553ac86be6654322bc357d0141899b0d349157c0cfb28098f020e2b12b6b88a70df519c46b7173612603c54b572ba1cf8c9d765095fa0c944b901e8136b8a9f44aa83be0907f05df3bbbaf7dc8008a8791ece4b082bb6605039545d4c3404698607df3c89970d04d57615eb8362954394549616079b9af19623e70ed8ba14ac747778a9528c485a37ed4f38ca769422ff49b9c80cb03e6a5dc268484318ce53b8f895d0c07856daffc97aa05cbf37a27ea549590bc2ee05bdc0d76fbb21937a560173bbf078a4d267a65d63d6ff22b2efe382fc00502b15bb989c7a1419d0341ab0bad44d86a8e3ef9c155c4fdef84af9b7ee020832e2b1ffdc59e0b7d896d19ae3ca1a454c15eff2e812e5ff6c4c7af5e70cb60bc69e79464a38fa9554414935ae62b2be944666fc1ef67394225ea17038415b0a3aa663b58cfdb72dd5f9a3ec428b6d2febd800eac0042649aec227afe2b0bd0c4fa5e98689cfa2693c469a3cc12a17d559d0e7a8ca838a7f79d896740b233c0c781a98d9ecce788daeae113618a592b546aacbb3ce59c7d4332ae00d687b950acd7318659a0cfc274d505640cdd06738096f15912af4e37912373c3533a4d5092dc6f793f47f11a1e83d4a30378c37504d4cb134584b2d6d2e18e0890e0ca70eaa43359ec7428118e70274e5a3d88e92788e44e30231fdb9c432eb5dbe7bac08bba0b49e7dfe9492fe69d7e5cca5f26777eed2d4d57aced6142041b1a1a31a061df036d09d896587fa6cfe48ad01e3b7a9155771385ce46ceb6b21ca7211150031b1738a5bea156ac2c0e52f8df90fb718ed53d98ae3dd1d548293916b53910cc215cce226e5580ee518fd9f276758cbd6d2b4f060e0f26b6834abb9713d8304b936560a0b0503650a6d511fb0e891df5379a6735ba77019523af63c7989500af3db8c447b5182a43614c35f5b9d0e4e5efcf67da423605defa21bf01e86c50df05ed20338544450975295d883d03e5400cf6041cc09adef112d29d9e266c90f061043ca8ba7b5c9070e714772e55372296b89f16d72685ef33d267fc793690c57f81cda52fd08f5f692f4d39f893d7b4c658ae5e875503f99f866120c248b0db04667ed7d610507e909535e36c03926a36746af99206562cc4205a16d4a3c0f2ac95e0ff7d13f53fc1c0b123d2f1ebf672871e1270596d1c6130fb8058d8301c9819ef918bf2cbeda1855a119534da77a59cb95c0f68238e6271cbaa0f42a0d1dad8897761ba9f5f54f77bd6b59dbfa337a0a367f369190d82c00b3ea5c190d52d733ef48db15fce55e73e228012d0b4a842692e84be47aed0168890452ea0c2e7efe4b0f301ae5d043cdb6176ffa735c27d1378e638977e34c39921cf3e403a8fab696d558bf955c73f2232a685ac3442bfc12d1b10b1e153ef0ec5c8db505cc262351723569222c400a85239ad3f6670f578605f10089a9d083193d7b65075c1995409e4976b6a6154c87990d170a122bbf7d483666f1ff53049c504eaf034758ea2a4a7e806d3380217c772a937ca5b0ea9d85fbb39c8807fe023f59be0ebf238c0096e1bb3765670259aeaf043c82295c7bb86b77f948049490aa416f0a0c796c83db351a2fe6168b9a5b3adf0c5d8431cc0ef724b20c9299ec6c243e07380e3ff7e4e989020db989a512f906f8bb2eb9991129ecae59bb8585f1bd660e7409f06a8f136641f692abfecc7d0d9fe47d41e5c623d790368b06dfad4d6b0f69efcbd78000b6f82d8d9342797b6189fa80280f73cd180ae25aef5e4d8d490bdeed19a3a61475972cf03698ddb0545fe22f6fbf0db13956bcec8ecdaca6060bf2efd4952bb85edabb4bad1d1e0590d74b4fdec3587c9f53ffad27b7a4531806147ab97c73a86ec5ccd23c99e49243b307a2feeef3944e6692bca074addf250f57c2cd8d76a4cd81b7147a51bd3fa6cae497ae6f881cb2dd1707ab2dc1d8580b36e0bd73adbe1e780247b3e3419eba4c12dc9042e19fa0d571206524eb63c0068c5b96646e3af1865a6a1382c473b5cbac49723cd9baea7f51fc789da75df501dd216ae05b328786394391c0205a396335896bd17b1d758ed46e66007906d40f18f9b448f40348ff38537839bcb17511e6a52ef69d52af379b0704eb618c450a8f04d97e8d9a246fac251edbf826d551f07a1d301d48adc0dcb77e8116a6b30f94845f0590e2a2d54299aa75b7469cb0183fd17eb3ec12a96d6f3e8b0acb18024bb4fdc8cddb890324ba48e4d0c7ad4bbd3fb185d43945d0506111370c3b470faf361607a0bcd08709c5461f781c2021430b777b09798953dce758f832f0580a8d90e34c21872851756c0ff553fc5e47fa0e4dcc04434ec4fee80bcd66eea307011054a124825dfc884e402cf4323f0db35dd30ee931911f681183d530ec860311cdcaf238d7382acd75db88630be154c2bed8051c68688669467d8f876d2a0713c1fc91b911575b4fdcaae1aa54f36d10795a77b3bca08f853802cf4bfa8c09e8b1a855f955817b5690f98a997e0e6c529ba88bc7698a759a505ebbad00aa07 false +check_ring_signature 522742a9e1c227e4a0ec25f43516527ab9d505a0f5478327c1d1457285c74c31 01c53f3a11c92024913e5c01bcd5845fc2d331853e9180edd7c310b15ef90095 1 3dd92897046a96ba12a688b2fb9ff19d46e6b4560e779c11d71c50a56627eddc 9e7672d9f38dc7e8d75c4075ab7505f747b415864e923bb3ae1303e7080fa19221c3e8f82ef08a069277ada80680ea1c58f1ecc8d7ba024e5a3cb70177951fbc false +check_ring_signature fc17fdbd1f38f6b26b266b337b3d2efc3a91941cbaa20bee1cc1c5502b71480e 069a68ab90228659437e5b6524c3e3c63fe90b7043322613b1cfaa984423ee8e 4 923ddffe4a0406e5070859c6df65fa7f5cb8ee25542f493f7bd7fb09788e3b3e 4aaa0d44c5fc665a1f15af8699d69ab14afec3b9b7f165d34e2abd21e37cc4f2 68750cf2cd667d36ab5e401c119f1bb153a04508e6441635eb200197172a2a57 ae8eed71a66a350c67869ae66726be13acc39e98bc6061619203d267d4c24a89 7f9251d064e718921f7ac78c4b4d29c882976b514675d2fe932cbad4d382a30bcdfbe34e32ce627af517a296a1659b466853709130c38d89711b24e17dcb9a0d2fcbfa9257a6eaf732529a3c58a1700d3ba3fcc878512cfb5e19a685df8f4e0f75b5f3fd16bba5187d2e95fb20ef7444cde98a9cf6ae2cb0a6ac67bf85b8070633b5d1c96a97fe30ef158ab95f641eee09338a22331919f784c895d1bc677c04449a5afabf7e4727050b25c0faa29cbccb3dad75d81e553d98660a5467a605000fcc0bc0e52eba0a9238e938ba42fedff89309d27f4d97a1ad83d68e043a8c96e7ef0e930db556a17bbd0f48d5d5d06927fc598dea6e28c7700c1916d429070b false +check_ring_signature 13cd357690fa0e19087faeb50b865554661a87a1851960c572e5ceb2c9390af6 23ebdd5b361a6b9cf31368becd2b14458785e58f01f44ab3b6f2e63e7ad853c4 2 94ac2dfe91233104a93d4b1e64dd9bb68181bb534a245e5059ec7ed66f834808 288cb6bd070280342254fa58be4c80550f7a85c8a793f78ab387e29b12053fa4 8ec22cc02cf94de03a06c66ec34ba034f3cf9630837fb2acfa1b0fd615ead60e7446720f0682bacf5c937a6a1489ecd58bce9ad8ddfd29c6d8492c7928846f0c6d4cd99356b70cdc06cf100f54e4e322ac8a307311542741ffca7fe05fc8b9025681ea78432ac8c5c04071b3d4344cd8b316f40f28d9d186f3ebbdc04c3bbe00 true +check_ring_signature 30c3cba86d61a1b1ce8563e08f4cf64f53497c1132de75e35b4fcf81cbbdfaf6 049de3c90ea9ae71fae3996e7564e200f876dbf672c84c1252adc81d46991e9f 20 a75da730a182e56ecd89f185c7567bf2466a2d52c8d8692563c7170d8296e99b 63062cf5ac92ddaf57b60aabe0c507ff67d9fe3181ff4b0cb45d0babf2025c2b cd19529e8c4069bb5e971a901582f3d119e4b855e92d072e7225ac9db3e48ca7 281ea24f22966839304ab77bd1f243bb183ef7c524a545b2c16455ea21e09167 51d47fbabd6c24469424c6e1a3891e2eba5600edb7a9f89472298c1cba3ed22d 4eaea92cabb83c0202b5f92e2a3f10d8167499adc648d716f452fcde60b26a7d f05f357c1996f7f7dff32df2e2ce0a28466e8bdd492da6ad3060ef506467ae4c 7c673529981b45555369960b69d8251a29052d3df0a722d28a2cf9dc80f7b3b0 ad167c220c8d6ec03c18cbddb91085221e90cc12aefbbf61d00436e4567e32e3 117baf2542da97b1d88e15e3f3eed91e20de5a6cec059f91420ae93a381fb2b7 2d3870a9bd5cefa12c3a9a2a5c7914962fb955601a23b831d0b2d3c0130bbe87 3dcc5ff53058da854da72bd0c0df06831195319b1af8fed77281765be6c9209f 474c829113d727dc7ec723649368ea1d777c785afc75469bdf851b7211b58f82 d8cf6ab24f5276cf790d94c15ab88663f8b9cac0de938f85d0c8635c618a9e22 1059dac7eeedf621e0e9ae6c30c5124c19475205af5b881b136bf8936a9b6292 8d485ef2c578d528b770ca72617516897a7d245c30e7d6a22d323e856f4bf971 cc24d61b3452984ef4077540d265fb290b91ebce81e929faf95a307940039f6b a9fde3307ebef45129ed844254be91407460c2c8f77be10a13554d33c5b25dd8 44a5d2332f5da845f33db366b5c3a032d682b659cbabef1bef75e50dc73eb397 23cda888e839218f48251cebb2b33dc5a904092d0a570add916e2823958870f2 3adf07d2d1cac2d93bde569c22ff966b7347dd5bb1144330a80c37ac5f0d000fd2849e0a777c08a80017ae046a97f830af93066e280d42031b19ff23ab92e80d081e647a4f675da1f08b6cd51bc193baf1de3719822184afefb87235161adb0fbf584564f03daf0e7ed01d22de809b4dac429caa2c6871126f06ae505918460f5c273c564830334c7187d3550f611b04da38e052ff0d080478f69789ffbed804caafeb9a6deab401f95fbc457ac4c3654c2075b620f93a9c0f00022de7683604aa2b3d7518142662a63848e59e1a857e8a49cbd25ec7a8540cf6e8a9d1290301758681f652e06e5cc1d70aaa2232139098ffb5350e659ddf24fc9006a4a17c0108c53cdd145e9b80ed2036661edff3d4976cff4f98009d2712a899a9d182a203b0200e1e37ca7c7d7ad5c2a557a9a5a07265148c69e048205bb453a41eeb0e01802945cf58982098130740b4ad20b2c17f67bef926d29a655717f52cccc6e80b1fa5f5f577c1aa11a54ca6538f11e08ccc63869f956e268ceb1d3417c8dc0e0259fffc22153dcc9688888e804a10d76de72b394b3c75296ee4abd9267ae60e0e43589fa93adb224a12ece8d96f7deb90df42550cfdc927b6aba86bf457da4500d474e1ee2ce955cc425f3dd2486021b20a006846c1730884764cc3bb4dc32d0b717280942cb5cade73f76c2a679ece794380cb472b9b135037af56e67ffa6706228eb1f63f1305e43c2d08e6f47720a377817e2956aab7906efa3067c08d400af5c08b482abe29f4aa2b2b0523c4ed5f2670060a692f0d9f00d91992b1d8440fad34d00ef6b1b9794dc02539a4f6c73ecd1a93723fdc918ae795b4c6a4a50904bf54b81a660fcd566d2e176ec640cdb3320d246fb60bb0589fe965b8a586b00dffd1e672c6151587e0ad5f370c48cc0e007ff94209e3004409d00debb668e40c944a702e6cc9daab5a1c810a198dedd8f0e1d16d1be584997fe61854fe55b6085dea25c33d4fd903ab40fae64fc747de6af335666c80288623c68195bb442403801fbf272b7ab2e88a8760267a136afc724bc22c4b90cd96082566bf5a3bdd06c5b49da4b3d0ea820b2e8620c9bbbc3ed46e59eef9296d9a94341b788a173f009ede9704521ae538ff98cd541719e69ae98dea06bd5d42af97d5e03656f1030a4425319615896f1da471736947bb4989fc1c2cdd2d7689d83b2cd652714d580aefe51ebd199760e89b95025eddef63d02f22a625a1929476aa8eb85b5ea48900e5ea9b96e2ea90e24d25787419693b1854a9d83c731062585bb8fe7ad3f5520290b1611405f60a7c384e720aa29efb0e5c0862b449cf38080d33051d8e39140dfd581e845a5ea87312b5250e5524a0d51b933ad564977bf5711f15fa0b2638010770a3be416cbb28f22df92316ed042f928f8f4face5aee7d758d8b89877b00e67b27f2dfee85947806b1800403ae9889b4276d0201fd7e92813c8dddc3ab80d4e30332f6c0f3d968a492e8222fb1cb5d57572bb13539f5ccd4380a77777ec0b67c5d051bfeacae7abf0bf94b0db0d8e5d434f65cede6ee9655267e770a08400bc2997c31fa72a63dd770c19f00a3913930d306a1ee1f36a0482117ff67ba20c084dccd547b1422d14dd7bd0857b9c31ec2c48f6c43c6a2726f246d352f91b07293ad1bc7ef33aae38146ff6bf186a94df03c70cf3c7f6252f9c271cdc0cc5082403bff0ed1157683ab816ba4c35e6aa4adec27aeb7dadea5694dbdd7284ae03285c0346301cc75f71a3ba12b1a774faa794abe7947f2aa507cbe8069b1dc70f false +check_ring_signature ea3d4cc32745de10b589ac1f235b7b186f4351cdc49cd0846c9bbd13e9d37df3 cb487dda5ff9def597af0ba4bd4c3dd280f280a1e3fa37900fa6ddc003860de3 8 db01f11bbe23688f3d5996addf67b1daef76deefead259362f328059b1eba358 8498bd1a45aeaaee5b0547245c2430143f0ea7de313d708d5814650c41655cc6 6d66a91d89e37e7e53e50d6ddd90483ecd9d7d6910c1ae1dcf7734422318ec16 556d22ad5faf2efdbdc60634280ee43206f4d902264647c4fd811c54d4b9d60c 9a0a4c230d75ac2e475ca68eece18d1ff30741ef993824278a57b10987306d25 b438da5f85f8f9e040c0da85882a661ae1b4f60c16169350a8aac7b8765be240 b9c7276d39f0b562df070190f4502774c5f187eb76ca4be5c46a220bda0a7d1c d9c60b92f1b37463464a3f6c39db9de95de0968153844bfb36b4bbe804fe3b38 d0de1505bc4a4695e5015ed4d9aca1611e2c8cd85e630cf07e19e0cd78d6cd0be870079fc3d6bff06faf350c29b53ab7cacabadf785f6012c1e1d35842dc2e0a865d7c2006baf4b3fda6071dd0a021dfd0edc628c79c9e9a3987000a6b0d5803ce461c8bd24a3a3fbe93b67aea96b564f7f6e0f1567296c7b1d4ccf771e95d0ab0495e9f9f641526c8414a9d86aa066f1268e73d558958ee9d9655fd83fe7b005a54d1c5344c14d16250feb9d3024efd6f95969a649045885237d2083c37a70fb0acb317dccc26c7b83d9b62e871c621b7ccecbded23cc834f29af0aba492806b51978a98c6c43fa5b70dd86207d1d5f0584a1a2b1b6c0bbaba18a4a79166cd78202d538ca2618684a09a01c29ce6610e6593f704986aaee0675a3de81ceb10188e6fb7adddb21e47e5f40ef71c8cb8d513407f2bd4aadc69933d3d38ecbc2007a9fb443cce1756e29e727117164de3c6a2bbaad04d0d94e5a914826ccc8cb0cf7506ead1958a46b9a661f071b707524209a7b5da9e7a793e47a5551e4569d0fa03ca9cd595c9c6da86aba0f955fe9d14aa072130998532571461f9a43c2be0e59b5d2dbaa5585cdf9d8d57aeacac438a456c63182225b495856a50926394c07f5169db11954f2c0ef8de5f5bcca08d51ce3527aadcb1f207c263d35cb6aeb08d787a7ad50123da9cae96ecaf486ecd2ba3e7228875c1b88181e2dbb2f5d6e0f false +check_ring_signature 8545d2c66808a0162cb52090d086db3f88eb916eb324629d721442a4bbd1c3b4 b97cd0f71d190a8bc118e8a485409d9a0fb02f5fbd6dc74b7f06311adfaee5a7 1 fc866946c94f118c92983f8dfa9822d954737b79172d22cfe303f02ca9dde65e d8d9ea469849687c6c1aefd3592411f56629aa884d4ffa42cf8fcc767ccbc608fc60705cd7d9959f0248856830df6bcefae7eb7bfcafe177147aef08d4f84a0b true +check_ring_signature b2101fd649f2ab015ad7524eae9a7139bc47e676ac4e03318d19b31ffb204b10 2ee30cb6e01f4fbf1dd3a39879a38b624b3217f18ba6d12f167867f084934225 4 a842af037a6dda549f6fa07e955c8076a7374fa188198558867fed8ed81e92a4 27b92bfccd9da8cd86f491cabadb8a10125eeb60712623cdd3d31374106804bc f049669d412d0b74ab2e936714d702beb9dfde939e7519fce691e72b9a3a1a05 416823dd3c25254f97c60ad21dca18016ceb4d08879f0fe0bf570b9d139eeebc 48137d816ec54d5b8bd32c99e0ff6506bd869d361a1f397932713772e821ca59aae0849ffbf01fe2bd9dd7c8112f22ef02b9a103600c66882378e0e170e1c99a7942df1ae332d575e1ac048fca6c225cd82c2a218855ecb86480266040d47b6a1495af6c71e8050fa1c5757b5afec2fa3383d9b964f7bb188aa24505a9651b0def7dca09956e1f29e40e89e57720e2351740dc0d6a24fe23cdea8bb0ba841e0fc2d1eafb50134d01965e2e8b4bea46b89c0450da711ac030db02a3c33ccaf60893b527690f7df745e468a93587eda46b70c42252cee8dfaaa2ec92acaf357c0588ad5ebcb408ff3edefa5e37fb69be5ab9cca22b36cdf288ba797b67b8032844 false +check_ring_signature 1effcc95f6405d96759ad25b997e766125c3f5d05713a6460f78e4dcf7d804e9 83e4c2765c6da4e5fd1ce4bec7f639667b28711cd36c4b46dfd2c6e0c1fcf50f 1 bd60cc91dafabf82d3ef189da165ff9e3b35ea9ad8cf925bf8e7272357f121b3 99c000882ae55eefef9cd4c8599a240fafbe1529879fd5a2fd31a4e03a1f380fb8ecd15404ec0238de366c0a6842b7d34b30421817675b0f2877a725e69eee0c true +check_ring_signature 684b82c6f48dbaed8bbb82ac73d700263019b76168e76bdb81a40ea9ccd8ee8f 325ac4543b917363cc3d3c310376b9df0c4c9072c8a34635f675d9feaf986aac 12 f842f8227e364e5a0fdde57cbef445b4672cabbd0ddd8a52b84e906159ae8fee 864806e470749b9a9464506e85d33ad9be9c5ad50f9cb56a0c6b93114cb7132c 801e6e4928e70b3f838c1aba28d14473b22d5929a13f44a7d56bfa3e5fc4c91c bb4dd990c3bfda1bea825f195c9b5c44a53660067f598aed6b33f53b2c0b0f1d 9900f00c6ecba2de02d44a2d87fb8a776c78208d70e0a9e067a543f36b88e689 a677ddaaa71b61d3ac6c4112b2745b95fa3c8277180999193eef904bd157566a 008a5e707884245119da57a5e5399e95c3221226c6f22ed7827cde31424ca3bc 7f2f4a3b8db51fe8ea67f30b0693bb8c8ec99072eb9064f6c57c0c163c07f83d b5ede7810600d6b1b3d63388186d7ba17f1e3a9f9b46b990db7ad31d7e214e82 149034c1f16aaeeddd11d74f9c3a662f3c0e05177e8c9a6cebd23e8458531397 04aa1dc0d4e18384143464d413e4b6ef8ea4a0138681c59a916dad7d198fe0a3 6f1adef5cf77ce3001b000a4972aff35fc9d8c369cf62ba9aa30c3afe0e962be 1f41f61ed9552ffb1ca333abb29a2d4d4ab0b56cfb4b77590b0f40fb5c9d1a04e96921f44b92cc4eecccb7d407ec42b06fad9adadb114c77f2d9cb0155855102d9b25792f6efeb6e71f162c6f1d9ccc25f1743f79e77cd20c8a20bed290cb802c104b8e597b777bdfe735921e761f162f75224c327b394f7086de548c2f4fd09f82ca56b18e269432217c98b9e9617f6b1958009687606fcd07b01b81c7f1c09072404cb1a59a7661ef0a945633dd3a0219c8376e4d9bc307b2fc56e49f3e10402c211238a9531f9c9184010a2f9e911b9955ecdb1ede5cbaa2c59eb0b02845df689a78bf125dd088daf1aafbc47c30fe88246ebfefcb866461be91ee800110838761878f2a7e0b80d886e905a45f4044917f447fec6d0cd233aa5d06567d704db6704fbc613ab90259eef1833995e4b9c208b03ebbd283520bef0cfbd19dc0721e1538f438302cfa61380f234b27bc77c527249e5e2d7d2ce7968833f16f40996eac2e9308f574f89b82b3196be4c7bd9f30b607477abb03852e7467ec3cd0a6e099874220a53a36e8e2529d19a8e5a27205ca136fb776cde2050dc0a0ae309955242c6f84bb469a404d19dbed9d26e5ca1fb240eda1dbf62f44d4eb307610b8014a66d37ca0059dc08e7e186f30e92f1693b363662f740f3be9818e2cab30e3df5acd1987eec460443a331a062901e6ab4f734651b976f478f437025473d0e59272859bf28597c2cc3b9032860907cdbfad34a6ea276fe51f4e0e610fafa0e77f2e9fa592594e361ae283746f14f2d609706a1b2b3c2e47638954a13b0670d57216718a9115022bba332f484cb127c8235163cbe4e096221ef5f824f7b7d03db81dce1414fc8087dd3d2f1d75847ad45b8ab12e4820664380740cc445a1d09acd83f0bfdd252aa091da773105f48ed01ee814ec2490143d8ad71a4701f6b01fa3085bb7750e74792e1b8dad9525ba0fd06795cbb2f6c9fcbac797325edb307e36b6e4222c7745f61f8cb02959a4af5b352b269672ec548371d8f1a5097dd0c7af51d2576f6035d5b63c558956ffb87b670033a8a222d349b3c0a4d6fd3870d false +check_ring_signature 686b2fded75e2d6079533b39663114b2d11f0bf7327f46c7a0743284592fed0c 16ee93a737143f3dc7b0e546b108cb1b5b9c38b488e1675234b420d9e5ddc3b2 3 c8fbb0bedb39de72568b8c89d152ccfee730a504a1a9d8aa93e7de09d3da67fd 1a3019cfb5ec80c104b2f46117de42f2e7bbc8bc775d46cb1e6b83c7cd9a275f ea6500ccc90ce7208cc1e5bb54b5921d4a822540ec07ff5e6cd66d70ff32da53 b228e215eb8aa2d9ee725944aa65f06035b7cc435811aa9e211cd1ef157a6b075a0a3b368482dcd69e8b3b1e6d3d0202959a6504435cb9b868b55ddc631cd9081f7115a080fc6d3505cccf961c1376ba891ff0c474b6d1e573d57833a314910a575a955d0b95b14d4a3ac004fea84534f1beb39c53de7db0c1a4e4faf2a0230ccdcaa99b7e1368242acc1f3a41ebf170a3bff27875ff27711d730d6fff1d93045be4ef9f32dd59a257134a122d6e25b62969b904462bbfe3bd12745945ed8501 true +check_ring_signature 7473ead4451d289502e588308a1f91c6d0ca225ffb2c202f7be0a9d6adf38210 c5dd68252e4bc8fe37947c6519f3d23337a29986294593e0df4e5d394d24089c 2 2094114b4fb91a8c06975c83112010fd3a2a3401e37aaa3804b7f4dc5b1fe131 3b5f9bcd25977b42124388a1497cfdf8f599603bf9111843e871e8676bbf3cbc 72d38a5d404717c57e0af51e97627dec4dfa725e68059c65eaec15a7906a390d8a699f212a3a79df481f990f93203349e3b4cbccc26bbe62892fb5824e62b9040ebc71757f81c4e94506d2cdaf68aafbc6da713c247018cce8f8fa80267d89071db6cfe43b6d6d4b6358d9bcb94ef586d95981e4ca16f18bf628acb47ddef90d false +check_ring_signature 5430c6219a228b1cdcfb9c321a99486854240edc327e3be5d437b0c5103d91c8 3218a22e996ba4013877dc89b532f8203b4f4313c00d17067890f09e51ec85bb 27 e3345ca515fdd0afddc98781f1ca09d375c368acfe73101924a9cf3d01b439d6 f992f0ecd77497bbf6f6d59ae772b4b816a816f54b3fa6caaa3ce3fbab13c6ce 5124192ad728f62040145c9358354674184d38fda9b7d6cd305ddd2a17b5b1ea 22685e71acde05a5e7c0aec71322c4b9606c32f05558d375e9a784a2ca370e54 9ed7c8faf485b49dc845a186f57f8a865b96525f4aec2f7137d89899e5c34b43 775875fe9558cbd97555771b2f6d842a42e6bf147f51f0be0772186025957f23 f4b33c1d3e618d7ae2ad2bf25192eaf462c3881648986d4d82b0d5bad7566a3b 250a3e3ae53cb89d133148fc693fec10ef1b52083515e346d6cc585d2908f359 fecf488255ffefc39f2e9f9e47cff64097d4bead23c67ac9072affdf55c9af50 4258be429de6dc13cb6c75728bd08ac43a4c88d95cf1e8cdf7818deb69448b32 c8b244bcf38bb023e45c9ebeb3f73b228fb4d310ce03c95d44ec6911e57da3d9 e9763f9e4536f9143b960696c837cb74b7583643cf5ab41ddb4c7c4333f94347 be8fd0a4bc0e71cfa32d7f6092886ae889f2fed647342160d0a5fbc9fe2a1b22 a3ad74dd78f602eefd12e305c7850acb29c9fd6d9bb4f9d117d763ac66925eca 6798855b4d3784d6ce2fa4b878bdaaf6a30763bca196460671054632106dc921 e15ae02c6fc47d2713cef78bfe85dc399f41fd871eb7cf826f43f93315c2d1d4 660fd2894e4188f52ef2be7f2d164408c7924c937126d6f70e4c104ab0f870fa 899d4c6c3f1ead3a9f8c557d4c10cf2bcdd7ba66725e574b08a24f3ea1595f67 1017e90a05845c7a352e67d19c9ff58f2a79a824a75d0b0c762df3134fafccb7 97cec401daa965358d76171f9914771747d18dbc15e82df0b67b17e8e952bfec b77efc6f5a16bed498566285706b45274321f16d9990ead8b48dc0dc49cd9f0b a0bf394a3f8796acb198b095b70d837bf0d7c730dc026eddf33ae14ba003352b 93548988b0c55fe74b065f1d6c1042857d2e9ba1f921474608d68c6691062a71 9373c231add11729717434fb37969e2328dfc808aac8dd6af5b62c53d6785ed2 c966c8d4de8997d3cc03336e0b28e001ee3caeb241fe6e6fe3b6074d6c2904b5 735ff86f5b3c8b98128ae3be2eae9509ab49bf2b98c7e79fe8505ccc27242e72 25f3f505a931ff35e1219c6cac75637f96afc95d188027b660532477b2b07c6a 8fa3c9f4456ef10445140b9bb0de18579f5d155fc3c61525ca43b8a3dcc1540be440b9bf054c092d94b9e987aed77e93082267ce076cea4bff620479b828bf08e5a522982f09abf832f2c0181a3ce68edb2712f90cc4625fe5dcada59d112f091ca489e149ea80b7cac524e5c71ce96e3b4ee0abdf6c04cbccfbb27f45c10803ef899fdba4001a627d88c83a5d470da00c7c9b23574042e127a8333a0de2a30384cf635418ec6db28bb2871ea4a9ec26902b56961938c8ba25ff21eb07959007f208ac414e07fd919147da3d1f2463e5c1a475ef9ae7e025c5069056e3d3040d4786e44016307c55553aabdce8079927671c2f0f827d18de55ed16748e1c1608c1ab0664cebb980b9e98ac28cd2d2cbb29a33b4ef6945be1a0a51eb2a4ea7c0f210b747afaf6eaaca3f18d570138bbd7a3df76a9566605b3df0fda9836e39d069e66f7e6626be2d5e2f3d9652ecad84596ba4040a52e4a14c5cf3f8bbe7d0e04456dec778425db33067801a18c520438ead3c9ead5931fff8e1ea8889b3d1d0709ca584d173f1feaf286ce7c58866df40b029f10771374762becdb5aca83b307c5ff3d3baeef658043f5983fc5d4b778cf59f34abd3a9e45cb5ea13735f6870ff4e573a5a730e0cb39f4cfc703e2b45d77ae547e531973abe5efcef356bdc902ef57ce7f58930e2634d6825d808fac7224b8ad6e47c6b30d2479c2dc8fa4230ef6568fa619b28a959d4a96dd5895c5c8002a5e5a72d07c4c924c62c26239e90f6c7c779d7a7e75a77a81fe00b63a0ea49079369c6b9c9feb9af19b39f89ddd06d9ada8e8d7e87072bb7a3cc6b404bba08b759bdcfdbf33f12a4844008ce186041e566b636a58f1c74391893f9153fab84edd25357af0591004c06a042a078a0f6fdfaa658bafd37834e22f71c1664a07e4b8eeaec3dbf8d042d32ae08feee30cb4e26da90f4df91b7924f705f8484444d2d5f20eb866352176ddde82d015730176d57a1f06cc42806ffdac1c3682228b49f199b04cd61e01f755bbc3698e0400a6bfa70bada2ca1f40b8099797ab614574e409f06159eb801bd3ac0b6c039702f681f16473f2255b4ecd2bac2d80716548c8d7cf1962cd7b55edf602d214370d016615e36e4ecc25759bc3c2fb588de52d834ae7cac6b8ab73d81c8a17af7301adcd4c56e159b7ed37e39d6a89ccc3e169f7fdc4d7e88e3b32a022a2a9a6b2020e0362e4249771b84045b45eeb14606f2ae521bbb4cab2961ec8c69c4f28f00237793b5a78faea76cae0a4e5ed29aac8438551d4f98fea72abd45d01a23ab601e059ef2905bc270543374fe4b1baefefb370b10ce4523188bc0135ade9e0030ebb57a86251d4853bb1a3c9deae05c2983174f5e99a30bd23b49fdea1d477b30afb5c83abb68a1df421049d6172cbf786ae858700c3da6118928fd6bedea3a20c7831e08c988575f8040647688b96248a348d4ea9ae86f1e847026f2444aca303fa24d8e6bd7742049002911d8a5006f9cdcd8950cca9b936b5300311ab7fd2099754ad4d047c4b829683e165a1b9af73ed00eeba2f6b73de2edf3d07e4c74a0a590f5963c64585fd8593100ea264a7740983998df4b162a18cf75c0d10dccc000f6eda13faa61ee0d6af981568554e0008f9225a9f92ac76be3605b514ee6803a8d2bf845d395c087e0aac12e5a735c39a80bbbb8d10c1f749663201b587e20eb8bad43a0ec9a23c0c4fb4e75b63b01159b1ff8f4282b832c3d5b516830bff009d1498849d3fb2d117770868a343a203aa49952657e55dec9f5f50ced95aa50215fa0e0ba0c16a64c8bb52aa6999bc63a0fc3f8c588c1e749e5e1d0b24cebb01c76f61b5234b9b21c076545422d33a4dc9d9f8cd13e07995b22bd506526d7600c2b85ebb565489fe6abee5a4c78f58ef568e189c440822d62d40f5e9347be307737d5c243691d6c6b580b841ac985e0fc9eb15a149d7e29d5760cff23ac2a3010b66d06b9eb836a58e89c1aa03117678e999c2e3740d5b8b644cfe8bfb115201fe285a379fbc6411118f1a784fc200ee6016235a547509a2bd340a79a1cb7a086d85b97212b644c828e9f9903038747c4f726c62c25317a6ac1068695a6c240b78a874da13fb88670ccb6e0f973be90bd9b56390713eccd5bab990005fcafc0ec1b8a831e7f82b5ea7f651070af2c0cb753eb18058c0613ea98f4afdf770dd0a761cb08f99519881bf70226730f2c9483095753c17bb9810072cfc16dc68e9082993b2d13d356284db10f0c7625841e67e0ca25fb1d0af752a138b9b5f66ad04f4240889c52709444f293d86d044cca4a930c07b398b8ea2cc773b4364a0be0c509b00e848791db72f07ba93b1202ae58b2e3d37378aca6157535b6d0895650ff3cff0ab8a99499b7365b823b95b45073b05bbf80365d7b70dac285c6af4800f false +check_ring_signature 929bf4b5cfab64b6c6a31a5aa38e6aa437c685600a8afdf68e03bc944aaab21c 4eddf07e7555367e77def7cd4171fa8c8103f48769c5c431233ff74b234415a7 38 257877aa92c5f25b6d7c447aef938c5e64d585553ce062c1c25a946c4e134a2b 764e094bb9ab63ffce4c7ea952d833862b9fcf8d39b8c9cc6dc6de64530ba3ae ec0ded8568d088bcff801a1213f5c24798c57124f13e18dccc64565f1e89c11d 3ef93f2a78b6c271a30397d765595ceaff3a290c641dc4b0c6577e3260253d41 ffe6857fc9ab43a743047b57688a1e8d1d3abe9f23880a97cb807338f83c314f 62368e3a26e5e2d832a493291ab88ec48ad4c73f5e72c7fbfe6548a818c6134a 3ab361e70cfa9a18816ac2f8d36df1fc01f37f065fd939f27d828bd9a5ed068b 6371962ad863cd3111802a86f3893ab6af6d070fa59221a650e1e11e5c8dc4cd f52aad9cece9f4e1dc44ec4eecdf8da40c51d98e8a3e2bde75992230b21f5042 6cb84092be2714908a973cf4ce2863e0619da94fd5f63e2ee4c6e4d538c11eea cb5a463ff57997df5bbb6cc0b8b55730de639cd62bfcd358df277764fe7422db f4a70ffc264ed09b263d3d721753eb2fa710ab19a2e1dd97c612f58f17a61e37 d415b0328d45055f727090ce7f821e2018458e8c0805fb2fd5b55691ee86f920 8fb1529732090f7c8163127b752b8c4d2b289f80b70a121856ad3817f009bc36 a07bf98748d981075de07d9de28800d9e312d57fef27b3a4fa9d523fd95c2ed0 3fc12f063da5692cd23c5b738f90f6580db60050137c5b80d6d48bdfe6afea38 2186a6bbe19d54add036ff2e42f4593713a1d3e615db354a3f0f6c4d84dc1c45 db96958d371e4faded16329a241aa68f7e4af4026d89f64c06e33ca2b95655d8 703a7f9b43064d42ff2866a1343b02c8e9576fdb9e3edd6a11e347b2d0eb4811 553800f59bfc933e5694c0a946afe20751ff00edc56321265e12b32159b1cdad a6c1fd071145e62997607e096a2fac331f53a54573db28fd81c1dd1e32e8f64e 3facd8993cbe41e6fd5c9d131aada61bcd4def40d78c768ff4cda835f05f124e c827bac93da686c43a52dc316a6b6b70e4f2db95dc7bff736bc98aa35c23c61a 1442a3d926cbd04d35dec9b790b6e621a0fe7e6b3fd61aa75652bb7beac8c22a c07d92e33fe8496475162fa22556bb2d1681085ee6af13f93c2345f988afbead b63c913cf375e79fad29450f6c3fcf5aad1ac05b4be7c5ffc65fe30ee4493f44 8980ce5d0ff63b9a0baf0b5088d723b1ecf1d1195a20ee85e0d9d6fbae9b1fc0 3b9ee3810760f8ebe18e63ddaca20700bab571946f0f0031b446d188f95dadc9 ee8c91ccf18a85e1cd11c0464714e72c916b900040d4d3fec3e315857e73d827 4953d1607593fbe84d43957cd62b6d93984e862eb49c9df36497c47af4323221 3ec3b01e673db004cd7f2613a26932168d135de05226413aa6565b7841ce1600 06f4aa83c4d45dbf6e4dba5835f38844d76c6aab07de4ca8e84a1b7f94f87af8 a9e7a0b755d845c252df4416734758b620990d5f8ce07f3d8e488f4fb712b0af 47f50da90683ea13d49a19976f532050982b3a4cb1ce2bfa7b5c0c9a4557ab52 b57a287022462e1061aaea9e90ebd1212a44cc6c9f548ac98ba749dec25a11aa 1b32f222ec5bd12cddbf0307644a273265d6171f24dd006baaf82955bed0f655 48dad3c003d7eddfcc97984796922f7cad36504a9fc70dca88b6246d19ab666b d8b155d7f0597b438995bd6a5aeb3f27d3ee5671b9a5eec0dd2c4b05f9f7ff19 6e5c2b1f0483e68ba3b6556f5a9fedc5ff4cfdb8dc5ca7236734a5244ca6b60dbce68690108e8a388ed1d6482c1ed00b7e64160ce6b15edbc5cfff348b4aea01e4fd6b45799599ba7e774fa1a885c2040544f66c84f779efbf7015552e702200051feda096d4e964b08b00c422fedcc17a9c18097338eb8723d441ef66d1e5088f2a0ca8e2c11b337d0d76ada49f428227e46771e76e5f2bba00ba2e2ad69b0023d396e802d22c63f171423c131e0c32ce17cdae67db8aa0a5869e0ba27e5e0cfc737f5a3b0ca9619397dd1409b716b14d9b528e324d4c3b37ff775f5f216c02e8c7bcff0632048f84e44cab94217c257db30b389941828f3fd7e35fca8b3007f35b39926e891343b3de01e6a8f54b43cbb20843c31a991ee7a686f6e0a773009d7b3bf800ffbb931a81f0092e45f0315e2bc11747c8cc8ec93ed4249afc0503101b08990800218187dc577b8347ebd1cf265c0a9b2ad63d69fb8bfcd90bde0a27d845357c42f21da07c86700f9a1e37312ee7c7b14b418f2fe9c4c72444aa0a6fbeb8d7e7dec36f7c9c9e04df2a707fffc1c9b39d87ef1355fe5475f25a680bbd55b69a71db7260fddf0d06b419d998fa24f6ffda51b7693852240b4c8b3a0c286eb34ff5814f0e72f9de1b5002a87972df7a1a49cb4fa6db573539ec00c305e31434db82b5dffb0a5b7999c160df9ab2deabdc406ef647938bbbca77672209ff1fabfed0350a6325b004ffcfb675044abc31834e0bb951dff1314252a90b029d2b088572a8971d44d3f7e6f49b68725775c718473b90e0ddf55d1fc17b54073f403e2f80a3e5be1e4daa41588e3a2e671a3e5eeb6fcf732d062c199dd91c02a7b192562525933aee36949d4f401430ad1af36f92bc636acfcd81f6b3dffa057140936636547ac103823dc886c555ab23dfdaad5132028a5defb815e7dcdb0b51af990b18db2fc92d8bc4af51999a3633d724bfa309a5c64221aba8a7ce650225c124bef121162e04a0aeef43c2d74ca13aa47bf00fbde63b1fc7a7ba4b0f062eed460f2c43582ee2b05bdcf068ea6eb7dce5c3baacf01145a5c286ffcee20d7259dccdc3f6c252fa094f673b40571fea2528ca0823d5b80daabd65b7162900a4c0a5bcd32a604119b556548e34eda0365d07b630fa5d97603881a295c62405fcfd173601f0ccb1d970b2b74243412a0d34af4fd4b98589a35211ad50c0f70ef50572e6ea9ae741a3f69abeca347064ac42f1a306e166bfcb88baef101d0c097f056b9a358435b2d77a2dcb21359ff9a20af881f84592f179c1b8915ecc040f9dae22ac8a87c766c5b9f90a8893202af5a56b3211432eea3c7e29eff5f461005c6098632ed5d71b205930e252d131fb9bbd14a2de354d3abff7596577977402391a0e52cc99dff19acf47df999cad4b8c0dc5b4d0015b58ce5f7a3a1403dd03eb8ea07795d17f0bf53c53d74b81f5d25518eaf7257d223c7cd8ad37085e7f08330c495ec79997b4a1e86a8023ceb7beb121e3e6b1d3e3ae00e354d2f43a74034e074bc142137db28d4ac1a2a684aaed7c55008af5fcc541f81285c69fdda70ff7eed5ab414fc60458892b1d53556985928d4751b7ff3239674cfb21a6c40b0844edf9d9c704dfc4c47faa0dd2e9cab132e8abc6df8e981b457f38424e91850a8e1159a528682693a384599d06cdfd9de9695d62413f3f22fca5fd2e3bccf605433f4b06e33df4702a6dcea795836d0dbf21e478a69243b54972a4c7c24cd70c96704ead00a59a41778d4666de8b307e6fac5e0f17439b1182842bbaa24975067cab0a197bddfb06c0649df80f5fb7b7ed4f9acc552e50443b5f4235d341620b5a269164fa59fe10dd7cd05dff117efaae992aa9af7e228409be31d18211d30f31b16668f06a1eeaef6aba4b1e7371aae64b897c147a472fca05cf992a1910024c82045338300aef79657f4ef94099f1b46c3dd93b1b8e416c66af4c357ae40de7355bcdfee1fac3e1c2bf5105b7167bdf325193f4745dd3d613ed3da7e34c09efe713043d69ac020ffc9d3d3c3b97ad8495aae1cefc041a89d3a295986ff80ae53d459c41e1bc52212c1ac3d3a1a09a48ce1df7fe5c116f31d188871836e20332ce2b1b96f56cc37b8ff54e56b455ed4faf3c60338e3c7bc9438519e8fa9308ad606169fa4cf6fcbebe49f98efabff37e1466f50330bf737e3c3d98045e5006144cf1396799bc0611f3c20f0d6eea38249083105f8ba3059faabc4cb9289409669213f9d780aa9a94b63a5a3bbda471cd27d9600ad0bb3c7e5b8ea2b09a0f0d6c424fd809c9849a2064c85f5be23a8f6f78f30022c35d8bc84af42e836aad0f153b0430ca19fecd144ed0574197edf15f4dddf44ecd0eea2dfc60c8622fff0ca1b4496f8a0a1f1e938f89b095f970068c4ff7021dd0308108913dfb16ab4906eee290c088f15f708737f24378880ae58ae8c4654b65ddbe3c694a59512156068fc90c744638ab469d1c6c42aec0811bb78971cbf97f2cbe30392b7e661e8d05ecfcf9abf65a1e5a1d401908d64ac828e1eb402ede1a71726a96262907ef7202020992532391ea630aaf08cea98cb8177c01779197f7e9b4bb205d2c190e880c129180ae7d521cd5d06c891fcbe042775125a9475f7c22e6a070d242026389079f8e188dca8987040321baa6fc1eec25e0ff3642412c582a0782cf61fac5a00a07e664633032930c21bd52a9ebe97b14d92708541fdd9457ac9adf74bf18540179e52a5692b6889777d47ef7d3925c18db4d592bb7487b0fbacd99a7ea9a420ab77624c890dce092d7f6ae63c1b47c8804a142c028f7074ed507312594822f06c8866aae4d393469fde5bcecf25e27b198bfaee60fb2691608c189242760150b12917059bd54e9dc4d7b957305376ed7cf6f722929de2dfff52d276034821a0c92f5c01145f414f721138a525c7a8d985b3867c4cc7e631b4a95e0f52460c006c5c18f2be612aaaec3d5a5bf780b6e7aa0efe11a126007c6dd4c91ea2d5ecd0737b66f8158d51a0191cc12091aaeae0aa7e73cabe18e14846518549b319922079f0f9939327e05e84316a3abf1c44aa57453747750a48400bec2f23b86a96b0b63a28efbe3279e9e161387bea8aeeda0ded7eac4bdd8f1b21c5cad059c8ed700667d2ab64bf66df3fda0ee7ab4bec03a95e07761698e2f3aa99235aa586f730e057e5ef4c67c3bf2ecbad465e0e17757eded70a4266a77ee7d596c4d3845b00f02e37299ae7084df3e1ac64cfa6a143fc6f8782d080e731ca4fecef7080c4c05f53d25812ffd84838fb7ab2db8b29e5cece6ecbb09844fc9b5edba0a1fc32e07d84236534228ccc209ccb22fa2e314d0bf9bd19d7b058883f3ae82d62352650a25e38c3c5a9d5b7781624821fd7730a7464ba4bcfc098f5bc76242a6dbcc8a01 true +check_ring_signature 11360da7c0b89c57a91a359f1648ed8e3a52be59c72b2c3a021d6ed16194d27f d4508e2e0065c14fe95c527ea85dfa773a06937703401ac0fc66de3be4a80d3f 9 c885ea2dee13c0bb154e00a6a95a0e4b695a5033ae8459ecac7d23e4b90c31a4 7d1aec5220f55c86ea3bf4cb5bb613d90dacb21bf2ba847ece1b0943fd90e464 8cafb33bb79a5f309d291aeef0b58e69b2aa208c5f48d60cd683be264a208cde 18e673d5d1bac7f35f95f3b3612719f1dc8757f9d56e650015cdc785199ae10e 1d8dd8b28b69c92a5a004c60f8065ca9b01492f9b94fcf09de8c53832219de4a d7cbb6536febf909d44a696068c4d3f613a2f21e524448b07a2629cd0f89d057 b9cf6a50e661018cb957879c434146b459e755c0f0d8d4cf9dae44c5e7aefab0 c3f8b924cccbb6bc414cfc40df7afd0e3a10112f43d1b3d7c1b98dafc547b933 161cd88a8fe916c38dfd71ca2bc4c525ba225bbfed57ff8c9e3f606355326df5 6fbf9a1a727e0ffae7896c18cacba270db0f2d4e9ff4760bde739eb80b17ac08564e492e057fae87c662ef04403f190342172a455a3e89301429293767ca26073d29374317d19d057b262c45bf04a6327a36c4ed2b72cd690c2b9961820e33054dd84febed3467d73ca1f6497687c3e322f88df589357a15eec9ec6831c463029128a2fcd69adc4c69c498acd9891a25732298778fd4f80185f69eb9db374c0b42391fe0158bc540561f1e94ba39e267ae78261cfefd266d4ac2232aca277b02f341cce7330d507f5e460d231029c667f0c9d84751165bf553d2444bf51d2406600aca2fbd796d90c16733253a4bdf94367a1ab1d9c8ea9b5104b4a8d45a5002af42f8fb7f277a0433bf6723315820068ba1d1d3f00d117a47506fd5f675dd086778f088f92d1c73b7d41056ad884de948c8ae3dc4ab261912a978844942ad05fd94b2a298bb7eb06bdfffe099a0213882db76b651187d4b387fa29cfdb4bd09dd2f0a27ba2399396c158438a11d5199737b71201ac5866787494aca7a75330dcd4bc2816bb35fcbd38ccf965f0a6c50905de1ba3ae2cf7e8a61ca042b30ab0824f0287e94806a593b3236a1ecb10c7e92ebb62a1e41ad0047477dc24313f310a5157f330564eaa3e2d37578aa35e6ded427f945fdafd1a67ccce248c2d5cc0df6683901042ffc2a3bcec4f92c9ac7f8acd25ea6fe552f23a977ad616c094605784b95b22f1b3b474eb9a2be9c50f8df4a7e12041489d434649e81613e3f5e07278c8decf4ce5978e98ca1756b58ae3fa7fd5d6e2f92d0182a42102f3ff6c808 false +check_ring_signature 6810db2acb8058a3a3da092ee28075c77073fcb70322db8ed45a4a1cd85b70c6 b509926393b201360c4625415932fd355e9a6977ddb94309ebcf41b6b834b4d9 13 72281a52a5701edde0f39b69e40864556a48271dba4e54ba57a8ec762ed92fe8 c0b0e2af4f31995549f7f61a7f2639012068da6ab34042877e1023afca52796a f76023c67cc0fd77ccf55c472d0f3c8ef9bc83b35e6a0c8e9247cd13609d4558 21f21c6f381d74af62da38ad28f0fb90e25e6538d1882701fd9502b7a89fafdf 46123e567ce3cc970aea58143941e58b95e6acbfea351f2df045fe7b8a093ddb 7e38b95e62a629e39e0e3d052ab5944f76f4b975911233f158fccecd682f5644 0e18f1582cf165e7e80759a469827b08cb65d9c520ca1109b849d71e809b61d7 66c726be1abb5d1bda3934f620670e6dcf282790ccb5be1eb6c9f67ba9fc37a0 579fa81c10ecee91033e3ee88d6e19a8fea775cd5049bc82c47fbf34806a20f7 d5b681acdd1b8b2d9470440a50811bf1230dfb58c41756a84d62924d500fc74b b3ec14f9a8b635e1976c7947b9df45e374c388ae24d43deb663964fad40a057e 185630fb61c589801c6a7b569a7f5fecb6e8429c804306d7285ba9d22a7f33fd 3f6733a0c63e19db6bfcd81d6524cd65b3843bfee51c0e4d99eab5b1ea78c007 796ead9564e484aa8a1d0f653f27d16a56a6b311df5c7a8450c2234511a62b0ed240a94889fb2c416c261d6e982dfb209a47dab2e0b432a83a28ecd5253b1a0e0f1eb0438e8f019bbab7ddcd9ca72db8cb7f6a1ae90509180debd8fb844d58020707a8b35199f726484d5fb8435d968457bfb52f9710785bd5fba1b87a8b2d05ef43358178ed066a61a4a636e281f9151ec157ce67925eba02f7ad63fe86630eea0b831b5b715147251a0e1fa7cd2b2a1abe8fdc0778d831d2672b487f88f3027b1fe2076bfaf38bb690c78213395db94d250444917d48de2b929eb87f6e93080486bb722f9573527a4e2b907940f2feacc9b99bd7dc75af9beb35784af16d0a224a8ec308e6ff7d2ab58ae5c200f0c481a15a21f70b2497916152b63f87850be9759a3cfaadfc03f44336ffa9728f116508731846e03f83615191911c8c3203f1d0bac5f2ad9318be7a6544f6cc03fab192c3f86e59c3d4a9ff8973bca5a90824630bd34ee36e0554262c085e66542cd0cf46c925e38ff2dfc333b222cedb014dda254e896a64440270e745b178c7213af7cced5aee0a6e9c50c899ed8cdf0579077441791be2f28738fe767039f2c48a7aca703c9544da6a069d85c159700344b9f88eacb3f8890929bb24288b85838b0f46b82b1cabfa2a868ca936a95a0e02c3ca3682a9495e16186d9d3a39cbc4cb21c93ee72a23e123d73c95d627aa096a3094605136e1663bcc3d5cc6f1d41a040367e9d66f2c79cf848c2793c5420fbf21ab89eeca622fbd6347e0d5363cbcea88751d0399a2f5c3cc65f280f1c30c5f18b91fad976f3b7cafb10d90b36ae94e0fa919b18f3809999bfca5b92c040bd88ec91159186cdfb016dbab6da4c2520f87cddf4f64ddd0db83d60f93be6c00cef4dcfe32d6fea15e24f96101f34882fe686a76a5d656ae1af337dfd68388032ced2e3ec64f066b66b143efd070e452a8e3ba79f86deb832daea79c53ee410ec382771beec89a018df45d171d0cac834a27f92330b39f4439130e494374e90c21c82b935adecac3d506302afbe41be6b4d2c806eb6dcb7b7b8cd8c7c17f410add8d5a30afe8df863284a1d0c436904e498b9561112265e095611035191ad70b4fd05132ee5f596d67a86dd8405f8b512fd912dcbdb726183e1f04bb64a39b02 true +check_ring_signature a4ab86e1161ff76658eac127f541dd365537fe00ae35207feef68debcf902e93 fd1a6bec4f7dbd1ad167bef76a7f5d237c69b201c4e750b1fc87db64dd11cc33 1 e42134e67f2b6e90e980030f0d8d5cab92c0fca0b3c4013e5b26242000e07b99 ae0e5b8cad3e03792d8f61068f53933414922d72eff9a0eb08c797721e47a902c18e8e67ef5df6b25ea26c161068de499ca0126f10afbbe89504cc18898e8be0 false +check_ring_signature df17f5eda4a6c841fc0cd8b335b4c31c8e257fc3d75f0d08724ac061bd89f3f6 0642db959aced6c4bc691d61292311d209a0537461cd1ab43e24fc688783627e 253 550b73fef5589c0938e11ba29cb03b9dbb4655a914d0821f0e0f05d0dc1a0011 0ee21e5b2d9d4856992855d4b0abfc6ddddd003c90e1140cb6c470173814383d 8a8613de669bca4a1895ef2e83644976fdeb6f6ee144df8e10d9ccdae0240959 917c6678c0730fea2fdf76e05e57fe5ecb47bc122d67e3bb41ecdeb33730a6b9 29d6d85f50d935a9d4724d9dfb07298fab6cdd9d12087b3f4519dec4585dc731 4c9b75ad0d4e57bdd3211206c4a5037028422d2927cc6aa1dcf2969e379c14c4 7e5e0828609ebcdfb40d07c46de46e8f12cba5f9ef1cc1793d15fb44cbc5e08c 14014e3a5560d79bd1be93cfee34d53ca51fd48fdd64d547ee683bfc4d07ff7c 0c409635579e564a0a6b483b5284e3108a3e2c9c4c02c3cf93f1557fef44edd6 ae241e1104b68dabd74c69e5411b59c4aa696c2422edb59db1fa34f992a7b5ec 9f62065eaccc2acca45dacc8912af7a824c0389943d4ef6eb5eb9e9c98067798 7680ac07156b28c2019fb11b57994b09d8ce128f89814cd6ba277b58562945b7 d417e90d283bd2a86c795998e875f8da85bdd32d9262286d9415115646670bf3 227a8dd5ba6c771cb844af8bf30b0433c18f229430f3beacd4cc8d453d4033a8 6e2d470b6348ad3efca68d03d60b51bdc74b72fd23d356c643d71af391bb51fd 842c1b616dab12710072637445a68681858f7abfaf9f2eddca4e4b7f2762f084 d1ba3dcd6a2b16a444455bcfb87223ddae5de488459b44ad7527f490b340a329 6ab409238df6fa2e9536259a13cf8596dc74d99811d0fe88c3cb619e7e3730a5 95c1e466fb472ef16665ab87cd0e06ded75cdf4131ebfd53e419fcb1831cdb2f 52b615976019da8364ed88ef07c6c8d9e7cb72037e00efea2fd6584b08c35d16 e414a9e71c8f7929d663b6f82f25ebd5bece8cac34bf208404a1f06c46c6fa7f ccf4f0c6676ef32520bf69ccb2e6ccfaeb8e44091b7fa706568857245bd68221 4cfc82f0d550d364f58b2941adac4e8298b530388399ed408a09cf8d7323d5cf 221e71a7ed680dc110edc3fddfa10fbb070cbc92526cf6f1f457f1bceb6f4528 2d1e55893bdf69215ec7f57a31ab607fca82a12062409ed3f8ade6dcda69b61b 9a1f76d987bef1e0698f340aff5458f58c81c9c6ca6bee67e17d7c1af131f0e9 ea7cd7dd2429ec478dd3c36a5dee959c7f0a1c4ece76e6e41e49014da7df1421 d832f1a1d4b1e3d0d399d7b95e52a5936c294699c5b327ac0eb758aec5b2b601 b29d3d8cb8baa3e2e3ea1921ed0848785bd1588b0be8d5cb49905a62a9930215 61336a2cc2140496d2a71abc6608ea9f96be802dc0d5ac1855d6076ecb8ef24b cbf7cc6567f0dd148096c4fa433265a11ddd385931197f9e7706c4cf795c68e5 774feca1ca5ec4e6020ab5bed0c921a604dfb5e85be1525eaa8c821f81fc3514 80026c953d93754a1267d73381b1389c4510c814e06f7c4d5d3ae51850eb5581 52cee54800a00c5a31828b906136bceed441383ae3cd4e312c848eeb2a788ade 35d8125c29cf9284c731f289643ee9c216f664d11b028da66fa74c0e613825ad 831518ba6b48ca79a40e8594bbba2fa6302715a4a560e5a343dc9e5f9897e2d3 671bf171367bfeac45e739d059444c91ed622291b437182c0914a99eda0b3c91 9d2c99360ff861e513409ca4ef646e4e27be3ab5dca865fdbc4485cf9f6c2d3f 6e0cccab397d5e7ce8bcdd90fc3dcc7f0583d5374b3b2da89ebaf903788c4722 b7650ce2973ae8bd853fd7bb495dfff176fd447ed261cd73f645b9de6e4fbd32 813201d411e64da029a714feba25353e239d027a4f6c7067e0ca009c8207c831 cebaec007d1293068465c40baea8ca1a6a3a110f3f9449fc585f70c078ca5ba1 df42e617d754a6f5e513bc9e06c182bcfb9a7c954d8d96524de5b153a3592e41 350f68d5162e2a87e0806e99bd327325869fca5d28aa67e7e9ae7825f26be8a6 22901caa765a96e613826e4871726b30db088e565ca3129389c89ca11f726598 30bfddd2dbff1830926850df486389b37fc16aa1b2b60575f535c021b1e7d590 6c4a0f973578b8bd8435ba94336b933ad212a68e7cbd9208af3b8d7eb14fa196 c19a1294f3bf46203c6faa63538f5bb435cd3ca76a4d9eacd057f189b01c2a4a 94c5bfb543c4921f4a373d81484d0110fa22ff6413452956432ad48061ace267 670c94013b36962e21c522f39331ff0a2ece471de68797e8c15ac70bb8d4c018 57d1887a9b858e7955f3ab55c9a26d21fed87a6e0029e3b22ddee1673e1507af 74bba80ce7728e0496c77c3c4d65123a962fba2982cfc89edf71483d47a460a6 b00da7373a91e89d6bf1e0ffccf3d4a0b61a4bbc9eb456083524eb8c0f87be12 c0c14c925fff79e024f6118d7a2f473690f0e97312a305f9e637486b340a794b 230eca58a9e62a8f44948e01d3ee29c71fb2157196cc5865374fcd715f751e9e 7a028f26d4cd8dff3aa955f9e0a49f64d133d4ac872e9f84becb57375bc3cb96 e11f46a933362497f0e727a21f6b443ccd18db8ce61d1819a770732e38d81417 37eb4baf44f7a0a3422e44bed7a729a925e9ee49c7a72cd63e1102a2c7cc5107 885b40a04a550f91660769505664b65520131c255f7930fc1759b4132e4b380d 4272c8dd3b5cde69b347ad6ef947524f823c53cd63f5e079ce5a8e06eb342b80 fce14c327f5bc76ce1274ec2f4845e9f83a053647609ca65c93b4a06d23232cd 60033710a8df1cf54ba348e3a593ec311a7bd8211858e14b7e413a04b1d10f27 c0eec4eb0f9e0496aeb12063b643b81a97b964618e80ce2624c7167a919587d4 9eb30140090ed54ad2ef4282a344b26e5eb3ef4c0701c8ac71d355d84ba2ca68 0b985cc1b66d46f8fc0edce19bab0558422365e0a68ddd06136dd1148f54588f 7500a4f5e20c27aabd0c5995a35b5782b7cedeb4840a7ae11582623e4a8ba2e5 f4729102182eab0f10fb695b6d6b42817000a8e1e16a4fd31350f24c70af7332 8ff9df3e09e7a129c5023952fde96d17259862c5e3eea9efdb8ebbe5ea91e15a 390f36d6390b0ac0e037b588d510681c9d852ade2109ccde0d54beb26bf3e148 adeb24b92941d0571a1ecbe49964ad3e4c6c3707700f4c600e600088f38a803f 58a0ddef55740bacd26521eb4765c65d87afeb26d6e3d991fcb73c63ec8cbd0e fcacf93e68fdcb90fc099a1b8522634e66459f91b80a95c578c8fb25b9d0b1a9 d484ad3b8c4f3c87aaa4fbad63fb718d7c005bb084d12620148c49bac09b3995 ad28a60f07a67dcaf11c75a4447b9c2853ab47f1260bdbf2fed1877e50f75cea 25cddf43b8a5e67fa073df63eb5d0741bc26c4ee3d2f2258affe669fb45fac9d 9fb178ee673dcd24dd4926216af911a6ca75c03b47fdb90cd4aaaf2b004cbfb1 a09f8cff72e3da4e29fd8aa72521b42056ec21014d233a038ae121cd71e1beb7 9860e145a532c72525a68189a8301550f4e2c41fde9a9ee674878625ab754356 c2ed4ee0e3a2e2da9609bd0421a82b7f98177cb80d2718c5d32aa28cd25e885b d8984af5d1dc5732f37d151c57f38b7fc0ee1bb558ce8141eedf4badb61d63fc a1b65b276ce264a0c2c05b4916806c62045fe169fde3467918f17165edda6337 3a20a80c602094160056916beb8a65b7c3b123f939b042e1706a1c85f10acfd1 b68980a1ccb7e93fb29a8d19d319b173bc1dd28d79ac362c219060dee8e2e0e6 9ffaa95d82eacfda0f35e82daabbbe5b382e57313db8e364fd314aaf83fd8a3c 4e9622a16d458daaca48400de6f5e1fd278899f4fee778984e05f1b9350549d0 ee1a992ebdc2e2969502ea1e42a07f2c7e6fb2cb8682b466635184b837cb100f 69fccbad146ddfb83e2c8196dec3474cc88acc9fe6f4c12bb15a47df3542f471 7d0154066220bc1e875e66b8525e5700d3ea53758949421beecfc6fa05626827 31c527c463b5b7bed857dcd63de6e9e2505dfbef3c23815b11ce3ed466ead7d1 23b65ca5171e793329bd465e82f85b166e0924bae6925a14cebc4f1540bc551f 205ff2b685d6868c7824947c9f436713c77556229dfdca887ba285799056ed7b e6f75c144beb1f5fbd2a86239984c7f98dc8ff99ec0b71265366d73c43b9f112 7e50d19abb22286418287863683fa3f86d4bb3e63a67bcd8b5814171b14b1037 82c965cdffed75b2e3b084e30535ff1f4d7ce05d63b9d61ff349f5f9c1cc15d8 15b12a344913f1cd5be2718a44c21a57b5cb7269213f25feb3a1766a836a3cc2 dce0f196ccc0d002f90b8db722246918990e0405c27108a4971ff2cd7b7aca7f f0c5c8ee171d75d10b9872158961c53d6e1a69bf3e84048cd10d1f6d80aee95b 45d9a294ea9c4925646957fe7e7d9f3d91a58f0fb4ea9ad1303522d657b1fdf7 d57eabe4a3e99f7a27a11e46fcd43fb94c0b3cdc102792e306b8643f7a654643 a0ff5324fae4b7693c43b8a625e077dd11cf77febb1a7a462cf12760d830bc8e c7170f346f4224e185bf6beb4503ead936f8e3576b45b84715c91719c472cf17 314593c355e50aa5b3ee637e2bc41ad8201c0d09f948525a62b14649ce338fc2 d9c6e917ee39789a85e2a206d3fe59990c0da9df10ea08d4186cca19502b30ff 852d007ae31d73abc4539836fe03bdf718f314c1fc31a0518a594b1c9693c6be 5e006ea561f67fb538bb4871f0003f8b346f1afde8cad5bd820d47d4178c4994 0be16988ee6f51e6a1a9478c86af132f7166b7a40447212825cf207411e41dde 40d2195db0e0bf18a3de5c4c779791267028977f9ed2b31fa3610bce9fe6c6ff da90945a28482d8da771a3f0ec79d8e4e03e6878af47c241315872045c76a156 9b7ecd327522b2bf93ebfa0aaa97b13b4697bd5ac0f08e631aba11d67a447079 e97e060a435b343f829ddb99697981c7530f52a28526b15c6becf27411154c58 5ccc0a26c1a86b10f65e0a79309aad22c68ad7f1304a10bf4d9723c6b7ab29f9 1f66724de64b56984f2b0c664594a957f191014151d2d055c2518015193b313d 0b71685190178dcf18eed8a4a8fbfbe28b230fb0687a8d2a7a036832109b4f55 af6846774084c835e450cd1c0f3cb43527c8b28420b540adcf398a11d93f68d7 a46cd433725513ddacec8338aa0c2aa22385d024b8c7a0aad75301cf67d96c58 8d786ec4906831062bc02d24b0e52f21292e327af6761478d53c1bd382cf40a6 65859175bb10094a9c26f6a3a1b0aa402b0bbc0dcabb35d8d60aac2c5b361ba8 c367961d4d43b6b0af912f6e688d1acc59862b3a518b5024295e04b3c28f7f50 30932ffbc67e9f1e70a67a0a2db2f19e7a4ea6c54fc554aa66087990be2b7298 0a7bed72345cf0fcd9a6bb4e3878eea950793df7d796790666fc6af78355b1d2 3633a99bfa8cab9f2de9663106b1439c306d5571a19b3f024234c1bed3a445d1 b44fbe90ff078198ea341629a484c503b54d63f8824057b82864949ce191ac0a 099479e31ba8c698cf1100bdb715f12c9209a59557c6d7e46a74b4860c3b8433 d928b81d18fc51d5045873c78b2400cfc8c984e14df819e42dd8f103ebc51019 e1fc5e67fa49e9f5f8ead8088bc747c618f37e8c55ea0ad8c2f2c20a5ec73c3d b7c3b00a0caf903166db6f0dc357bd8c7f1b148f1ad25983c99a840aa2f420b4 77d3ad7fa8d5ab1c639661b78698e77a33294af50e4fa22898f18ca6deccbfef 41c0abdf05fa56d431ee0fa30b711f0ee61911b28d5dfa0b67952d7405c196b9 55fc7c9c248a7368c2c58a14301d41f63316fe2a454999f9391f80f99ed16fad 5a01657cc573e37851ce2697e692d13774b43dd3ceec3f9f85688baceb312a53 d10f876d9d7bc4359e9fef35d65527b23f839e32bcede99752c6e871473001e6 06b5bc5b73a536517e33558092dd6aae9935990a669524e684c1d26d755ebab3 6928ecef6dd843ae476a0c69edbf82c1b30c68ba106bffb4c586c090fa7d6395 74cc342a61f03b780919e463a2e729968bcb638517c35785cf040eb442a95c51 a8198528b926fd80cef0700915fcaa4014ecfc556fe7d38c260fa171b057d010 3f003b66c7df881f8e0bc54e5a3a6e9751b80eeaae58a1699fd5e7fddbb74f8c e7b0337915f28065861b8e73bec5b5b6b6aa50c275534b75cdb54ea8db6cf553 4e693c7667cc493925715bdd0a4be2d4e7fb681babf9ee4fe6b33ceaddc5b08a e3bdbd0ee5169ddb07237afbfea6eae16f0708793f101a72840098ba7ee9df05 32f5f9b5c211e157bd6d494a48d068ca2405b389d34f15ceb1022267415f424a 2939df334f4945d29181b229727b83be4d2d2aabe008f26afa637eae1e96b76e a0c1646a5e8f7c783627ce69ca1d678c3794519758921384a75522ca9abe2f9d fcf1a217b188ace7970d1148cfaaf537e1acb1b206374bd5c0d7b4a2b13cd562 8dbdee1d44b54d925035249ecf0374fe32bd952e819dc934cf9acc3c136b64bf b5f8af584a51cde8241635780c1e0b6aab359e5896fcb80ac9319162dd70f115 8bf9fdd0dc44dc1a570f791e3ce311dedc1516be9012868aeee36b5a5d34c24e deb67b1b58d95b91f57af773b882538bde29e4a676ea5c5950c11d3f51a34d0f 0b1978fc9a222209e17a305fd99cf50042ec366082b12071de35efeed000cab2 2982e6baf683142a23abed23681b7cf0186db1bee073d86af7e4036571e25ac0 3b7f4303d51764689727c194af5e6eda5eaefe06d63f19a6ed5ab5c7b526f617 a74c297bc6bb629716175cb99ad4a6533418042bf3346edadc76a6154ae06354 9bc1adc3c7912253ca3ef6a3547e223adc424196c638b3b40162fdece84f03f5 f72c94bf96e41ac6619f35d35af4abf8100d363da9e2e178009c96d201053cc3 ac931ca7ae865e24c6ad89534c8f184bbaf84fdb7b3e98f1f4a90ad17225337b 9ce0b5213b0c2f2b1d26f4fbb22fe8bd0dab6dae9f362913a5a5ba661c7e2c42 2ed97dce3f3d18f6cd6704fd0ce8eb583e7eb566f04e13e513429e1d4a4f387c 50e742dd5c300f6e820a1ae4795a5356082414e5c2c0197160acf0ea90d37c11 251c876ee968bbf81adc2d88ea9e602624b1983692507adfc5b054f8b8772278 cc4539f31b313b2a003b2b4ba2046aa6a1b22777b6004a32f85850dd87e3576e ced29f9c87ed4414f48d7d361df7895757fd601142807bc55c6a92aed41561eb 3fc62ac8eeaa2fd6ba63942c59ccea26996d0d9d3c11e073d03e102dec9703b0 e8fd366e3899ca8d2ee72f7d1d3c3cbbe990a40af1624f14e03024a83f34bc94 f0dbf182399f82c2f9541330a465ac66e2aabdd6199814e5baeccd0c759bdaaa cb47c79972e153b7288c63206848527eedce3f14d1e89da7e1f4ce8d4215d233 5a4b2e864c99526bf4ed8b4de21b21134dfc9eef4a2532d3242816bef11e783a e5c4797e06bcd61fba00fb5c67a57aafc15eb0f92bb608e17b59a9052c566d71 f83738820e5ece0e0149f6f7bc3a4266bb2d808b2b4bdd317d13e8b723f37e32 f9d73c8f24ae6557dcf9856f66697f52f07560a2abbadf558de9209b4a2a0588 60b1082649b3719c0e1eae5200f97f7e0d17f2bde702b2876266b2c5026cc8ee 2221a247a0c5306129bfd5ceeff97647af15a8f11f4c67dadf26189fdcc7b87d 4f3305405b2c2961bc71ac98abcf8be3b388ca39c8075a9ad35a32172ade8888 e5e549b5bd8863cb1c78f14d8544f3fecdc3dce28c1de324404d27a52bc2f6f3 b3337b2490bccbba868506ae34d5a2b0a7a9f9c3c5d6a4ae44484536a80b179d c05b178487f50e438f1f008036543941ee354c42f9b49d2d69af8d47e5fb20cf c60047a94d0cef67843664c3d63a40ee94afd3cb3e9f8a7337ffd5be34253136 10edf25ed3dcec947d77732e250e2464182a8bcff268aa6d7146eee5ff73f399 b9e8dcaa3ee455266507ef9566389d43aa3b58a969580c833555d3f9fd463d15 9321de2aa40036519f8dfead0741bf4c8f418201e784d5de3a95939da3dab864 ae6b9dada7de6634860c9b3c1c9ec71cfaa9e5733a40cc679012133aacf5992c c50c7891b48fea1ea614df2e50276a6936c064f8efcec9242ab0f3476807ed86 3cfb2f9e17ff0314859d1189b4aa84ccafa184f82aa82a6b4e3b6eebd7d29255 177d78d6f031e5025258a8d8388f27c3d05d26fd8dbd221041059bb33ccfe9ee 9c5461e3d1650ceb37e08b0f382400832e393f0d516e3e682eb42862d1243981 db7609f0a5b5f39f5db6e49454c804bfae308f17a1bd5ba5a00b65aff07211f8 957d1869263c34c416b83d59754e543918b1c8ade7f63ae0b503a196d48ea282 a472d64120b262d5030c6defbf8f9baba2396893d08e9718e1aeb251ef61548d 6f5bdc2b03bbaa9a577a94aeb335396e9a0a311153db9ec78f35e52cc9750709 a85aa507b4a9be1bad4c1aea310f8227769653f532d4b9908ef17f2f795765f2 f214ff298d400eb81e1cf54798205a007258497fcfa661062dd8ff50c2f51c22 6494a12218369129a214ccc2f305a132f20047ceb373254bfb58543b875de17c 952ccee0a86df54034ae0bd1d336292785110db4d26a5a1c8648c46c21611abd f7014a6b86c335fe59de19d28174cf5c4ed095a9549e90feef5dddd1919ff385 f5963716ad95a8302dee958ec5bfb1b1f027e324cd210f148a4b40e5ef607170 15c1dfcc13056ac4f15dcfc53083087613aebc960fdf9d27439f8e038eb02e4a e14f093287aa880aa30402c06a3ecfd0a02ce7a971ddde922a50dcf6c8ecc39f 145c7887b8099e8fb1da0e85bd7d4f6d5afdd0b1ddae1846f3680bee8d131802 6c78826b14ef4b6628cae8aa0f788e8bfb5ab0557dd9b50a57cf57471eb2e84c 06643bc7221a2f7b3e7fe88a4c77c5f5b09583777bad5a61e06a7cd492d76a5b 32b1e20e1275104ef573a79c3dfe0fe9353687bfb69f48693ac28323a021744a 04d3c35b2782190064c7dadac12e2043ff2700a7542b49cf659b8086867c2011 a0316d346f363de312c73ae5e881250fe841d72f57f9fac6208d522853b53a58 9641a9bcd996a91618ba9c4f46aeb54879126d9740d0a9b4dc173c4b66eea577 384a4e4df46b0f888fb5c7865794e20e46f8be9b48fdf6e66dc24497a429714c 86274bfc650dd6f82ae096f36eed7d5c13ee7f7b6a3e200fb5989ac48ec7ba02 1a2b9a8ca55510db89b14c7dcb1f01389a0e78140650eb03103684b91268d2f9 dc7efd4f0db021bc5acdfc2d5fae2827ea5f3d44d07054fb9ed83b72f3d1fe8b 383dd4f305985f6ed323b0516b3206dfcbaa5b19f5128b1b7c620b47b1ebd808 a50b48fae9f217d5bf5950d53de9a787ab5b54e310c51c3392d91af96b3400e1 0a2db20df56de3aea0da3f139dbe79937ce62b9a3d0b9378cbb65d4a79453474 79cfa1669d9062198f967473ff86565eac5cd7c1ce2a1cf6d5423881242980e6 89ca3f104c6895895ffebc34eed3a5cc25c5e08fb57bb86cabfb8d1fca59094f 229efc8e32c788eaaf27bf68ccbb842dfbe95916e2496a59642c61ce95cb308f 7d824c9f3e7ebf825e26e40f10c294b994258107d5c95b893498b97d90a61fd5 1281bd8749af81a0e4499633f2d6e857b8a5d8229ae5214f1a40f45c7aa19148 c12648239da0cb8fa588008bad77e1cb73289c83e52bb0103bf82a7d4c92ca03 4822d2d9d73bd910489110228b31c06e1641c15a231095ad3f363c9150426051 88325e4b2316b92925a1b89166904a67601b6878baefc27d9e4cc3bcda979152 300a58ce7ad7d15074a13d3e597c7a718085ad224b7146d1b871c93a57a5be40 fd6ab74c3dbf596dcc72476f7b506818af3cd367da1e9616427c90e4114ce96e 0312d55545dd602b34399b585ddc73dc8cc3f18073748bf13b1a9547cfaafd67 91fc8f483416016922432c6558f9df8280c3df14ed863088beaadba45c9be64d 98b3ca1be40344d138f6678c5cca6bec035b49f61128c8d597647464701e791b 57b03b102eea72f8cde551b3070aca9ba120ad18d3727b8bb6440d485ebc9b92 524ae4a5ef9b77b8d4c100b04caffabc5a6fed60a88286ce229a2bdc19e42330 d85d19221ac552d50a4f39b8c37f4abcba78ad092891dc5f6c19ccaa91ebbadb 9cd9cad24a7a549d926fb4fbdccc5a0c7cd306210b119d51c460568082f6d508 4e537b17b9a9f30bae63d9867f669477e476465c63c3e6d469800651834d320b 225396e850a27f2e5b88eabcccc40dfa757406d5ff8d10da9c51c54f72f0e780 c400422dcf0fb31e3bbff8750e7bd7b985d89b32e29cd9841ec4333fbd4be9e3 144c586044de2f662befeb8fb6580c5da023a5c68fa54191c909360662ceacd6 5feba37dcc7dca45ac5069805e551c4620c77a79de455a0d464e457c4aab8c5b 3040b0d62022322d94a796e9ab71fa39c3b24c019391fbf8ac39388c68856b41 97666d436694c387278288051f6c9a21f235888a74360ac9712a18a326afd6e1 0e8602b1b5cb8172d34b817e2dc83d7b473a40d5f610eb42a13aa9baac9925f2 45ea5560376468307519cd573d8f5f5eef3e297143b6057e5b2244377e587c27 3d7f2ea5ba34f66a248b8120be075d637ff235f57c4326b87d223dd25bf82274 d7ba656c9faf82de1e051323b03b47cd3dcece6dea6c78c0631eef963c08eb46 16f3d4b12b01b6e728d8c95c1c496420faf1842c3f2511a70d1203e2e065f0eb 6482d54ddb81f785742bb9e40b252e38521c0e6624d3dc6d77dcb763926802ec 57095acbe585b8ef7b9734e219b20d96e317f00010c298b9f038622b701c1082 7648e82ab04cfcf2f5540426644c21f64a93da59596a62384d8d28aa3721b990 234829fe158b31fdcd74cf426cafc365eb5f6c6baaa8357e04fc0e77d3c5886e 9b1747c3728ec8636c0661a7b95820f1101af29dae87bdd7e6b9d6289d74672d 09bb4f5d3a4f8b12aa6de57852b34c4f9d14445c061dcb22968b65ea05067624 4be72746e7cb27a74d6e5a287bbd534e00d5ac68e76ca3dbe0a9a5fd85033432 021ea99a16b0d3083dab03702a3ab88df429ea5baf0e4f44fc5e9b93563be743 d9dc60764663c2e01cbda7d354a6fbb89846909ee8e6103bc95b9a48d27fb110 8eec2687a049ce88975f0aef93a21648c05d210fcd22a0742d4de100bad70ce0 3760974cf53341598e0adc01e6b156911fa1c0d39e7f6b07ce14c0a7a85ab964 55c9d182133e194a87c940c3bad31b785ab7f995c4afad4a7a60ae75d806a55c 0a45c37d579cad09a285d9297dea8716727e765731491d823f6bd05dc4cdeab3 786ec24bf3b6f3354ca40a69674aba1dac8002f6ae1e03d6928b66f5e9cd0482 e05100292c38d041cba61bf7b784a98bf15e1595421a4dad687ce84270263360 726a319b21fb290a0d409ad3b970e0736c37031f03f4eefb0056fea635e48e0c9a5f58c53d7117d755f2aea21d04aeb89b997f807ebee2404aa352febdacde01f7601e6efdeed3167fa5d10fe30533ccbd7c2c3acba5c823fe2299a23182e60895c5dbf9bdb632ea4cd440f493727256c5b888479ddd15847a4ca8ed64ea6705b98d902b1488fc24482ad5ba57856826669f227458d3f6263eac815d6054730667e3d19a9bde71e15e9d4f0872b0cbb91bb9ff4fa7183fdf6c3d020e8bdcc705f5a59a8cba2f4e3ad324655c2c4d33ff2372168319a5c1cbaf6f9c481909430c1cdbe633bf17f8709cc1a96255df2747d3371ba3d1785bf71aa0ee6ebdf2cb0543a8fba142c2b2bbe5ce47e62accd6bdd0e49af9f3e19ce48b65886baf44e607ccd41ebf1534bf8bf217589b7439f428944857385dbdb1cad63412a323a3b3079f2f632b559194401649781bf6e8947db709c942aa000c93ac67d66ba27f3e0d2df3c9a44c2dae131537753944d0cc5fc7ce467da11f70d947c3f7db1604820f94b5811c41ca58d8825d02dc95086c1039ea3721f4dcf167d5cf4857fbca350c8211a9d7a38717087c7a9bd636f57284d6367afa168f9aefe91b5ce292f0320c972dad80a9b970bacd2ddbf68510e529f148d0d9812c3d694521dee8d0cdca0652fd8697d6cd000d6fb642624aed8bd9df4245dcfabfb738c147bfb84298e10ccd41d58a5d0180369acca8b8460808a4b9e24da70ddf74ea8587179fe459b203720f3dbb83c7e20db50c35a3b04d680f9b40622148a932d14fc2d2c559cac501f0fcc7f08c7e303ed7833a7cd981158b929bc5a9882fcd57d97e21bcbd4a82047cbf333b4014944ab286a465d0e84efbccad7dc83e9620025f5b73ed113767073f18495f48a05ff00c9e855c39eadeb40ca54b9c6c5a23f46f13d6adcfbb4a06cea43722203ee00899e6409525e7034d42b7cc51934fdc339d49cbed105e780d2211717c75af1c35bf15040e11ecf46d4e6ab6daa316487cd75bd21148113a096716802680256152aa843dc996147f47514001bc6dff9abdfeee3ee325fd4a0afa725ea228f62c4d78c0d81e6c4d6e34a27ca25072ece65ccd47bf979abef908bacebdc89ac54303b974cb65b9ba557176ffd74ae12020d154fbbaa52881eb0eff1fcd08f5e97ee7f4554e82f2e7163d181c15175f379b4f26fa60cb6d840b02950fcf27ef4689f3b760a1f0fd564dac42eff7eaf1c7a3eeed96d61800ab340371d19bd7ad563acfd584a972a3a75c1c7292aac555b3701709db4277229c420c61fe5039eba1d7c0ec2e7743d1515a7202e271fcede5fbb8f3c6d06f670df5033d06893b2bfae2713b5bc26b2fac60c5cc1fbfa678382453b161a5291a8ba30ec2638ce347965d2e3aa3af244cddbdac7b27b197ed8215094d41fade6e0f0800c756050a86c4968fc967451773a440cc3af5d168bac695b087a37d4d74d58907974a238a8886198f87686af46ff534f7730709f917e39284902df3dce508a40db0f240b09df2a6467c8a6e2d7d7797980c4ab020b3e12a8e098ccec5a10fa60e96a792696375049616d80c371283932445b6de162bc2f6f96178565f02605602956c9912c558e6f6434cfdfefbab35122376235e9c86caa2a570d4fd6e94f50e066fffd64f980cbc6c5f3c16e6daadd475f4e54ae71b5757e0842d343358b700d8dbbb39ba0323b8be623ff8397eeb5a30f99c01d85e6feecf1796b14b3ec40999fc14791ce81724ec7c3d4d65097fc3ca4039eb1df52dc2df455328151cef00752a70766a55570ad193a36b553f91460060a268101a0b2a72594dd9253a700b4882b80c24e069a773c31884ea0024d02321cc99099f876486194d99dcef120e9e2213e45c6271967160f4fb4e21a06627be55ecaa98a2022e1a5a3c930d0b03a56e25a461f65117a573a346d421bead393f48760717af58df2b39d6e88621051fe97acf829eeded4af1ce2991db66d47a300d5f57b6164cc5adefa054f74f02cd32eaa8d7e5e06eb49f78ec62abbea82888ffae1cef6e1c15f9638a219b6d0ed290db6327ab6f19501d3ff0d2b5a0106690a8862248e51ce58fec52a5e94b02308335dd23e4e74bc5a5c8f89cb86a17b1259d58e26b60fa635600571c569c06159740b3c4b5a7237b59dc9a5bc7ae5534073d59da334a55ced28e15f2ae9106d49c84a0b25f1228c741bda3edfcde00f7654e681ea1d80b69ce484ba7950c07fb09fcab382a22fb66d3b8a2e735b5c6f620e7528530d5f6c27d61555f9f980bfdb22135b7497b5dcb83f2ef22a7cc0555ccea2b1ce0083fec42eb74d2773808b774e733dfdcc179744e4cd48bcd146ef3c4a0fc5ceca813d339dc771b0dcc0302b6b9d5a4ad253a5a7fb5c8cba63198db37980df827fbd91f610bba60627e02efb1516e214076b2fdbf5cb44a1b593fbbcbd8d1a0c7d3ef66d26ae8e286160291bb430e557037879cb49b00cf3c8a161de490224069362a714fe4645ea65c0564c89f2af69b8c26676e3f61ac9037936ebabbbb638325b390661a83f601140e6bfb77b221d37031295a20000206f5b8c6951a4acdc45491645a859b787252017a06cc3dfeb432374ad67a694975cdddc9acdf52230174225cbf1b0d52b4190ae2c88ece909bc61c5fec14605c29abd96e73e61580e769bc66607dfc065d500731dd3476e8bd9e26c00cab979cfa55713c9d165d8079d2be5abdd269b37bce0a1d2c8edb22dbba81f418bf5eb395a86d3400f6d3a0c46f2fbb17ce107d87fe0315e6f179baf316b3e082fe7b2933dba2e71fcbde8e4fa445b2dacce6931c5103be39a687af30de83a1adcb113f37efce0433ae7bb0e7f282ab681e019fb4030ca80f602d9c08b61e78563dd9591710b61ddb91ce931d0df43ce1fe05c4e3fe06760c882991a08fd8732b1c160452c73fab35827bed8e33c6da0f653c046fba0eeaf46437877e12f7e587a0d75b5fd11538b4e6c3483225c50698193635b3c7055d414480c0558754ff75a333e2c741db056b040b17413e5f351ba1e8b4b85e0465800ef9674f50a4c59b482b56317d8f7efb24fdb06595ff4d0265abf42c4906c10bb5be29fae1b6d7bbbc5cde9dc59f31b5e75b178ea4762cb35516a575fb06539b8dd8a8b77b6364cd42384228e8998a501498c6faea302b1918416404f105d5efa5ab42b98ade39983b535f5725be454aa0967357206843cc428c5e0a8300ebfcbfdd74d39588fc145b3739bc8d656445f5e5f13a78b337894218d530b90b9a823640d7e95a06fdfbe0095d574e7b5bc28429c4bcfced664bde518862480b6b42dc3ea4792563c1a3419fc4313df6685cb0afee8c2e8401177e33ab4a8d07b61f32743a10e1d12a3adb6fd00f36bf475cf6a672e7f04de1266522d9ad790863f75cc0029f23c86530ef487e0376a89eeeff0d30708db2ae37a352ee0e210dd0ec6e65d042c506b21d4296ad6463b614266e0ce123b02d04f81f166e9fdd040f5b00c8717ceea0d552de12b0590228a30e27f9a915a994ae5b601178a3b30444cf81044dd7ef0ceb6f3f732e84335d541ddcf7ebcf76242f1596f1e719490dc0c3d16e80c43f44bb22521ab3481dc72ae55f26307dd0f7d91c657f05bf690f0645d539543ba38898b95308dde838e36a28162aa40fe4e47cd7e58b2dfb190935c868457e88aa823811b586dd16b53457a7703e1209d4e30981ee674771240caeb8af2925778252a7cde3d3b2caef4f623d758b66bcfdedcf0542f65e5802088037ad52705e7d39ec796eb0037f756536ca729acec5873daa2857442b97fe00318a63b8c21ed2061fdb3186cd8072646a3868aece2413c3c9f209eef931c008ee977965179ec45ca7d5ad7332f84a7882ef10850ed097bf86c51d1d2a1d830af19a54b17d4265d655b70a39df66f92dd521e480e5dfd4abd50b41d8d3619f068eda03e970a9d566e8316f1fedd0fbf8d860efefdbc47be4f4f9425c8708a409701eb0baf63b9b5b201ae2f8d575a2150b37f02dfef0209ccf6b1e848b6f4e02d517e42ba88a91ba6215ebf04b0c5c8321d5f4b8e86e6aa71edf4da7dde57f088b45400afc4818a92e9fd67b635c28a90c9e7c5bee348207eee147a65f79a102a5b0acaf1f2e372328990d54254ff5da9c06c0965edff88b82067568b0c76a0c280398efcc76b561dc86eeb8bf9aab07202f0915ab23394092e7bbe23464ad0d85e957db0f98b9ae3a0211d604d5aa4e5b0fe7d635b7eea0cb873e172407950d06163ac44a2b91081cae6de9b8db2ba2b0837830b58cb7bc43853233df86090011f4bba8a986e39aac6259cc88213bb88d0d47541e4de87bf1f7fdab2e333d0a08cf67d40c58849ffa0d88d9a6a92a6ad89ee94d568b0d13aa5c55b5a2f43d023fc8159445870cfdd8b7b264a249341c6816722bca39d0a1519e558f3284f701079aae55eaecaa919a1c09e22e3181ae00de617221bbde2182ea9841d9fdf308b13a3f80e87315bdfd3156adfe19ad052d7663828fc32ba36a8609fbd787fb0751ca522a4b272847a1d9be219548ea010166973c3d08ab292ff4af60b56e1d045cb22ca59ca634a9ca4889f79a7d84b3f010d689322a59056d48f02713311f059dfab21198310200eccc89f374db7b3f941fcaa4fddb4e1a0074780a1ee54108cee501d707918bf0050ce91f008495e076362b991db2f9fc4a640276998080035d75a9ebea073fa23dd63f22aee6b33d61ac127deeed332728bbfcd83a9f2e09451999a0037adbba75102e8990505210728fb5e47a9d29f804926143910603081e9d69ba366036f9d7324ceab9fef484183f274247ad7962873eab0cec67e80843424b96bcb0c7cfaa7322d46c99b813e70a639d3f992afa6f0800d75c739f0bdf849c1846235b526d280ffac39c68e6d8450fb82d89e825b49e6a4f47954201c2cce756283df7d7269f0269dbc3627499a6014e1eb6325f2a1c9d763bf5180b7acb607a819542e21d41ad1a6f0c3b6b22fecf6f6ed5e93547805ff2ca610406e56dcbbc2463cec52652e46f9d976dee9440bd9a26017cfec87125bc264fbc0bc10f40ffd9b6f9b29ecff9f0b3e9ff2435e9fd239652e721e5686d3779016208d369d46d2689761efe3b9aac1dcac768807b6250f0cc4fa4d5500648bba7970fd737256b82cb5523eff71504d992199c2be0ad70370676526357ff2fd057180f50f3e9cdc1766508c8c47c51417c98e63d8305703e2033708d08c0be0876f50177284d2777e4e133d4b379470e7242369c9ffc1bd51314e619077b05eb95c1059ae5571670471fe5f5d2ad2ca9bbcc0d4adc172b28772a40ef4c4fc15db92707aafe0965ae8f9931f992ed9f30879dffcd059139867ab2c0cb480b1c986d4d0471bdae2912ff4408b8047032d103364152e8371cca02027c46e7a3778b5b5b0df68c16cd9e2d5643c52a355faaf35ceddc48172378e42b9929e1f0eaab98e60c4d53c5ae482ae82a895d8a180b16e3317f8d1bd540b2591ce74f38492ad6c00373a00f68fac3b6301705cab8ab9178de1d5cfe7d6263d014deae62d43143f20075989b5fdcfecda8bea884c1afec79216116d94559687091b2c607645744c7059a21f58b6f0b77462ca0e629cc1a70541aee1a14e8632e25f7f75a7504c2fc01b4d68e3ecc960cebd73698a89187a341e4483552ed84ae88e7404f331bf3700eae9c4ca9487e586acdc80c43c92bbc6d9d651fdfc0741fbc98084df0aaccc303a376b1d834b16bfc2cac9f61d725386ebbd99538ae56c6f061792df97e83850edc2c15e324baea4fb5d06e7debed46e9fb44ab16cbc0ce66d50e7f4c3f04140710aa46bdb6310e7c62487e6cbc9cf763e8bfd3699714e8514d9b1dc8d69a5d02f10c29605d7d4807769553a5e740696a593d19b9b38f8944fbc7d8dea824d00db9fef6a07307ea4ad15eb83aba1b285dca70e6a49772fb9c15e260f94fb19e0247a09d89d3727c38a8e3de977c915e5b840208828ce1b11f35c3785cffd2840cb4d0c64dca40cf17e2c3427285ff82e21e292e37420c262d00b0175dbeffd7007ab285ccefba32dcaacdbb8ffc53c18312786dd324471e4babaf022bee98600e035a4f336ae4ea0e7404fa1a7dd271421338b9753d2a6ab4d30e734ccb9cf10444bb00c4da8f9e9919bf0727216948042bbd4ff96143bc1942505dbbfe05e10fa449300b2ea76fb36a0bcbc92b2266b188abdc8e6b48508afbba6c9eb404ce002494f2ccd6372f5ae002f7702963802487ac87d2d80a9dc6483eb505da9f9d015d017c92ce823250591e5def557fc1d171a131c429869197834ea26f16cb330aab06b7b1f07d20bac7818393b4cbb025d13c00928f5c250ddd0aa64676ae740cba83dfc69865f8d05851637a2edb2255839b0aca7651c4ac20f1616def80810790a987fe62c0d872c6a724bcb4731bde1e7c877dcf7b6219aa3b4eda2bc44d07604ae195e23b39bf3a9eacba377c00a194e565a8a7d2c154c2deeff8ea2a82071466d4f75270a077e3d80f444aa49a3237485ec0fb02ed637a9598d9844aa403d5fc28d1c90145a5e2773baa32bfe4dd8994607cd13f5003517674e4825da80d307fffc47a2fa53e337254bcc7fcf91ee225acb557b69614e0c252066c1807014fab1ca8b63a604a9d8a77bb000edbf9f5fe4515fcbee7224a957a3e472ccc03b6ee741511916318780231b4a067b489c79f70c042e251da944596ca45a42f016308a341aef871d1935693949c02dbf7db3708486f00744c207ec2dc53f1a90b88c0ee613961cfe4b9f2e823695ca10b8ab3184c4a6e46ffebef072a7677de0fe650ceadf160247796640d560129b07d498a6ea6426ef5e134155f830e8bb3076a1c4c100bd6d236eab20308b426865ea1f8ee09789c87733999f527fd863005994dbf5f3751e5eb0df3d9fc722e2d77ebf8305510c1bf8a7294be95082d25058e9e7948f179e69f78eeb278947120388e05b25ca296e472db81069423267d0eaca2d06ab7e1fc8b67eee8bb7ed409166f7e8f1ae5121a9973c4a99a140a79004d32b43af011f6ec453188d0617a69aa60b3ec5848016dfa0980cd41eb57a70f409b531dd7c0020835b6eeceaaf787f8917066d19439fec34fd2520533923d0cfacebec686034b861194ee93d6720850c1f1d8a02a50a51cb60d9f0f21beaf07cddd23c430edf41ce8d7fc072962742e204b307552168c4ff2a635ba33c946029383727003bbf378caae914fafaaff99c6824e7df79ecf6ed1af0b5c66e0500f54734373dd28294f7db4e3b6e8671d74ae0e2fe355c87b138d9014092ddb1400cb681f184c855d0a2abd99784a4b5a673a47b69f7051e86c6ed65258b0fc7a0902fa707f441d6b2ec6342360e94d61de4ad35234d7753e656b145294cba444003075df8d1caab926157557d4b1f670c0b3055bd3263562aeeb97d2446a357204d2094abd482b1a474ea1ddb7b70012fbc5c1558752b238e2d6996c9752964105ebd61e5c85ee1a35ebbc0b924efa03777f693c6c760d32150cdfa2d4de2cfb08b938680e4d82470b560546f3a80a335317e63e82c1e1d7a4471698fa83eb850f9eb1b527bbfbd77fb01bb50d4a5db3208bd541d878b5a6a3df63c25b5b524007039ee9641355fd8237ca4534c8463186b5aac6afa0f0cb41b027b1fe0ffa1103b027b016ad35066d65db2784a82d10a83ef6d61b7562540bbc4f4d586091120fe462f24d9f45f1a86131f6c1b10aea338797bfe16933764e8cf673b4cad01f01d5f9db1ec9081c84446d79b3e0209d5f2e9cec4f8d799bd6c2c3ef4aa9256d08b6bccfc04235b30a2dcf8137fc0c64fe368c9db4cac756cb0d885c3640696d057c15179f4940dcfe7c507952c68bfc978de88e9b7e0110150cbab8306c541b025569e324e9a67f7299c15d074e3933ed4e726229a72f9d5ca0e260329eae900390ef25e65cc845fa9601e743ddc1e03d73cee216a13f5700e1e99fccf9c8280f6ffdc7da5158fca87273e848c20e1b7bf2c1fe7cfc0b7014b63b5eab0555210d30346bb5bb2c52d7399169335997991e69be333c12ae1b32fbbb5e8f421de90851c19c3e0840b993afccf731124951cf62e1034b9c6efbc7dc992942effb5d097970b1f40bdce8586623c04b83cea6c6dcb3dc3506fd553eba9071d0c23d710acf562546d4d1dfb39e7db55c7c0ba5d9e2141c1f931cf46341711d4ac54f230e9689cc0d62c3922d8f3eabe9626c124086e88b37fe545b24c688ec1da59de4036a6b0c0a41c01ed20ee83eac00492a835a938b9e3cacd0e7e4c709456c1791080f76cfa99fc2cf7506e2a82140f76be0540a19389d6c2e6a28db46cbc3299e02caf3d46c54301b785a6c30f8880c33ef6349dc6b41a896d691c497f8622c01094a4f26d1afe283f143193255733b6f6d1cb9a86cd846845f0ebcfea22670c60153765244a317a0c0aa93d3575663e8c53517a9acf5f7a4066400a22c5bcd7b09b82fe224d370126e7f86645a88ea75ecab3bcc3f1d86a7df81688042fedb4303388c6edcd2ccc97ade2e78562e01a7d855fd87cde9aeb1a29c43abefe4b4e90dfe4921b4b50af88d3a748e1d6d706e0752e5ded45cb4db9088e864f05818ce0e77f7f2cd1cf4425dbeb98d22777dcb0a41ee232801f03d193de9f90ef4e61d0d70da298c73e05449a9672d247e6991a3d5baa8844ce9f22ce13a47a520e1a70fa0eb76c4c1c3056874933ee769250524158c36fa8c0f45f65b2bcd4f656ae205c57ad24cf25ae239f24b46e170a28545a832e29fae37bc1856fe7b001533160187faeea3ee998dc8141949dff93ed2c16fede94a6ba6bb8d7eee61a63ca4680549802e60fb53884d68981d9590628918a2b1622e3d62a1814e6ec47668e84e0f71b9781e60f51d7f60e999dfb6fbc465081e6908912e9c8286ba6448fe8a3505b58f7004ad9d7695cca8ea78c93af80eb26b9b5785b311785a3b62be209dce00410034db9ca45cb1276de491edc88f5967b54fe58933c04ae178a8400836d30e258ffd54de3a3154a12213a17f98f535f8215ac357b3cc8a7eca2ae0f1bc3f069c042a3eafbf9ff9ae88bc612b1cd826af73c655e2e63e85f20f25ee47a76c0f929c415c6f615405dc4f741d17e08ee254ec3eed864ae79d329f6cfaaccd22018c386b342c747a73445e43ce81f1a3439890e22df794a37f7132126df121120008bf0329145098d1cbec145fef8125c1833ce6cfaa4ac432e31684cce9c231081e42160951785679881bc44c291728f3e1b88619424d7f34a06f5f8aa6555e0c79cab2abdd88a8bd2cd8644fd9a46996de5d37eb5b43fa2a5178ae0c998b9c03fbb354df56179237bedd0b20d23b63f1edb2e22cdfa995338a5af72b94bb6f0dbf1f2dd0fc23104d7d65946caa466250a27b567c5acc7526672fdc9f8be16708a596e45da447bcd0c91118fcb616566c117e75829ec227203687685eb9b1460d20bd0d880cbcb8c57ff3ffbd893f478f7d70362f653b07325b1290af114b4201eddb071e72b499b6919a0f32f5db5ede7eeb538fe9cf1208a531fa50aeb9fc04332a8ba36f6a2c47f6c05fd88082dfe41ecea559295ae81b1a81b23a32362a0c6a0f468e47f00eff225906a81022ca10cff7cb97a0187c858d6959640c4cf704b753fb51169ea2705872b9db319d72b8a053fc6c36c2b9630a5681211fd162081de87589f7acbbc42b33471c858bf4ce4d6747c303b17dff8f4b96ce6140a806ef88f36d697f4d6df600a5c85bb2f4e411ce8d71f76402d18ffd8f2b229ab9012a6b1eefbbad6e9b1289ce6b3c877cc2fdbe5b77a44ce1a60b0b8ce123b25b0fd755c4870c8044a03edc8821e94bdb949b31f554f297763f07060dc561759a0601d8a6846871915bd5bc8edf306120b0b9a4e6390fe6ce2ad2b8dc5f53ace1094e2f8db57e33a1359b1d042943396582b8675b757faa7e82782bb066591d5503e5a21a0fb629c8d67645cd80e44194ffe2dd1b1f1038434a4a54181d9423aa0dd1ed16e3f5899260e835c0c4ace6b93548e89ebb08f5c3fb5a901f75c520b3040226f1ca485d462541d80bf2f52dacaa92d55bf617d01faa37b9f4ab5805f60ed9d15ba74f424a8e1e893bcad21379eb91cfb60e5d5c71a4001c3e73764c210062c38ed9d992336a85a7524809cd5aafdafa2b8fe707cb1b423350740cf2090ee7ab568ed31638cf152d454e48a0841f5dc63197e4df2252f84f9ce57426c507c709fb67b70c4c0cc8db40ec3ab9e94d4bc55552f9a5a2702a6d2a7bd90fe10f72d8a2a993cfd0d4a4245069b9aa5c43a41f36ab16e8d71b9ad98c15052e3904bf392848b3f06be553c66af2edf381725b685b8ff5ad765adf11aa6f6ccba203d5c8e7a82e65fa53e58d963d210ebe01ff0347b725d3c7d4cb905ecc546bb305077616e392fb5d91c23c745cb251bfae61f8be50b02ac27b74f3257d3863470dfa99a510fc2ce4429b3abc135fdf53230816e48f612ec6ce369b4539e9d9f90403544b9c050dd68d1f9fb23b68adb28468d3eba8fbf90024a7bfdf9a96dd560a57588a90c7527f34cb5531f3f0e0393ee83aed5e9d6775a1d3494d6d2070220abbc569f04f5ab95c52a744c7527e63694447d16fdc041f33a3b498b804865f0b5ffd51dca31abd4a7cc60c1c1a9db0b1df0999862c8cbb8993696ba1a67c0c05c59119a8d1ffb9a943883a0acbf47cea92f57b259c89b52eacc093a741137004c0232a4db51fce49212acad247bb56b613197e2076e469800a1017ef08c4770521fd951c54c561f3c334320f206e807b5b3b4868ae7c38c0b4a68a53951b8f05d985bf067c70cb7d805f417bbe0f3d641b7203160a87cfaef55c35489ac2730f2341c33233ce9231de6b70044df01429b485ace8ef45b94c0c181627b767700fbd7bc2d1aeef361199263c6f7503334e25041c7b45b5a8e14bd23dfd76026b06ef7e75796e48346873e12fb9f9cc52edd1e60c945f4f63f790387b3ea7d549048096fc1e7b9088a04d7ca41e3ebcb1a4631f8001c6503505766ab22f0c429e0d9e71ca8fba9c1e370d6ed94500a449ace4b59d7aead51b7cda3beada1947dc035f8c6a612142c4c311b3e64bd9441fd0aae27de19c96cc193c789f6a5e4fae0ddc3303c1de560eb1aad04304111999b4a5b59bd5052c0b437bfe1eecc45d0e0ab5465080e14da8fb19001c96624f9a75355c28aa66fdb99e0d8383f6cc27d105e642c34912cd5d5e461c4c83432085f317fa3b87389ba4b1306d94b536c1f3065b97a8baf8ad4272d516a5d26cbb61988aafbc0ac480fbc7e07e5eaee2e94b0cfbc9e7438b025679d458600b18ec3e618fb956f33ba965b120fd2a52ce99290aee0eda530342eaf9d2197cb7340960e365c2a7032af96fe01d38a6e086e8b204ba9783f8e8fd14e7ed5b9c90ddf17f0a557f76d2da851e7bae9194c13581f70ff788c37947b001cdecb047fd923f0452e3d3d8de310f06f555e92c521feaab03973f416f1a4ac1485be06dcfb4108f9aba0a787d5df45eebdc7cce81349ebc07ea7d0ce8355305c4f934e2401644f17a63af075dad45b25e6f709ebf65d491000458295bd59dad835495cc20297eb6faff9bed4e5e07dfc43677febc0a473b023f404ebc9474430457a693f469adcb515b88429f4f99d3a15cbaff4e53e21907c7f306597aae85e85f6a5686a2e2e00343ac81dae63d28d66d0449b4c549e901e6d0fb462c4b136f296f368a4178d50d1243851e30652bbf81b5d625bd3f330ef0a739dcb1cee7a069c708c0b40493cafb8997e5679835c7e46a9ad906bf5a0d3ad55be57e2e555de711a1879a1b260bc6927650b161ae8657493f1680eeb1016ed9784926cddcce19b87d31d2931473304cedf3a4d10d37d315e8d18e486409e647ac004f5c911fa53248232c03b3ad8e09575eedcfcd0136acf259ab243c02240b07a4f7134aa487df3a2831f083abde9c631224be5c77e8b27b69c454630d39460fcdfe81b766fedd00b61a587962c3962b089326757f6c3748f55f78a70f95a32f647ae44b91eafac202267b2f25d85d6c0bc6fe68b45d35ae758f11020e594246a3ebb69141488b8e5828054e979b9cb9750f099aba9274a261d053ab0e604164214e4509798629ab5a1ce2c54c80f04ce11d13db5c467412a1caeb500db8f4628bde22458a063d03b8be5d250f844fa7b4a1fc7147d56772fbfc1a5f055db462ee030737f384ed5c8c83dd5dd9d91f9555deaa5a2549b6ab0aba2d3a0a9cdeddc65abfc3e4680c58d12cc48d9b892dc98541400c5bfe02875e15024f0f03c77abf0f7f0740a71e7a3c75fe90202624625d9e17c23677ba717ab9157802c207132e93a32989bba83672e821121ea98f72711df953b9944d1e708a8e540dc824386a80f9e3edbd70011c427adf4643c407b09dfcb7c4a1eb5dac3fd33f01ea2d632e294f29c94df6535293f12a9d7cc2fe7450e200bba1e7a25c989b300b03b7a6ee6e1c188e899bcdbcb9510b90e8c5ea80e2453f38699859bfa60fdc04ae59c20fa35b0e5f1fae15a254b39b11f4d17e5342bb15de5bef531dd591fe0cb0673e11e9431ab096691a9ce6ee8470eb69bb8c267a6948787e23a84ab0520a987a6b24f075edd2b5e494601c1a654c13681a9ef2935d762531e55e2bb2010fb190c8ad579de20de2b5283130d3e6afb8ca6fdb661f0268d7957656d7315907043525bd76f49ef4609cd23b1274e6bc0543e182a2b6e0e884fa6ad86da55e0b333e298d71e7735ebd752f3b888c83d8565f2c2186c18f533a8578e3f774cf0d7dd2bc3d0b0af32c916484403d5b861da78be175bb2720e190cd765982f7d908df90996d36a33b249407bbaffa01d3527818c53d82f0e61841bd46626e03be07e6a02775cda6b8f6895e43521f7cb8a2b8bdf248137dc7192808ad8d79b14c043516934212d353b6fafabddbf4f1705c08a27304b6695886342987b77eb4ec03c87833548152d59cf64488e2a6e15e2568e2adf5c28bc4ee574d165dea269300350b9952591c2561e39647068dd5e6e8ae9bf1d3cdd95754c31673ce0ae6ca05af14682056b9038f0f831e9ac3428f9063e32ca515683babdf76f8613c447703e3876b42dafa2a96ade1cd5cf0d96c79543c9e4eacbec41ec0839baaeaba920d22bf70aa2958ba8f74a1d01bd3bec2fbad3c48e8639ff409e334cb113745bf014a65472ccb7efc8b873015b0b7af46046e828ca433512e49abb2d649f4019d00ccb961135b46c83cb598bc1d93829d37dc7cb3b0a80860ec0cb7eb318d0cb70698dd6022345acd1392fcd7e70a99006ae5091fefe080420649b31e74da1c4206699640eb76f274b47a2897936d1820417ce55c88f9071ad040926c2b9a878109bf98e8f920629b71e82fff18ce14ddf1025fade3b2999f086c123f02292d1101e99a9f3a5b6d15b58e4bd3a8767504df1b9d783d7d10bd78cf096b74f26a12010a27af4a3f8ac91e0da03b167fb24bbe4fa95402aa7c0523a7f99c142a5fa90eafa12d81f43a9f880d9b32bb73addf1ecf5664f3ce8e025b1e2283c3c064fa0bc6cebd692896a21d3ac1b3cc15652bfc35336a63a5abae38c0bdd45696972b0edfa16313c09c40ba8263eb18f8ee4c6f30d94525c5c68ff2428dcd753588bd034abc45f7569aef3544b996180940c248d7bc6e6904dcbc493466b7896d7a7b0461160f086b16ebaf45e0fe0428c762182949e36ecb8647a71a4fd2da9b42330e49cef742d7ef4fb56d2156285bff7d40b1abbd0814642993701374682d7cae0b1e0de3f87d1490317ec05107f3c92203f135e9839ee2ef5be400d8db9754330d74736f82521d8ed202dc5ddf5ba546f8e29df0e79ca0c6f86aca2f8b40207e0e86328fdf027797ebcfdbaaa77861db5d679632303199bf87bdc4cf643bdf490e5b41077a49866d80856190a76e09340c9d7bb7c2269441e7c91777024854c2015dda7f1f0fff25d665114c46f57e3b07d5e699f5867254bfd525a9fe6cf19400648630ffadbbfb3dae1348e9580aef3e9c9df945b31cda2d6ce7d3e82565d00eb331a0d236c419b5dd772f4c41ffe76afbd4a6575f5d5f3e2d871873f868700305b6f921685f20bad21132a4262511ce562b2a7e3815f2213d18c4e94a34e60935545b8d643706172cad11e9e63b0aab8dfb7ed8f3bbfd5c56dfe5f8aed1a7051b4b08eb414f24605dbe20cf9c095c49faf3fcf9b21e07f68b87d464702199048dc6e18a4737f6a908a9772811e19a828aaaa092be00e22e9cf7b35045533f0dbe2f9b2dba574a112357e0e0c90756315ba2da4bfd5c8cd86afaac865901ce0d3bd4e82694849a1b457783e53821b03ac6d284b9dd4355b0ea76eef21b18600a652c8c3b2e76465ce63f0160ce84c7ccde2d75d6e0ab490247c803ae062d080bf9dd06e2693103c1bab070d322ebd81fc5a6ca0b5e17b6b96bfb3ab4746ae006d1f8d0bfc7b0b504025231f403c76de10c8bf7f7227f57a2a3953b6917f513018799fc737f87c4a64a18d1bbeef659242b2bc294e2e637c4985e2e6eb703cb061bb4e70f74e917d47eb576600cad2086d7982fbc028dc86d8a01e50c98d6e20a0ca81f86130e22188e8a69221e75a22a270bed2c14a023de7708a048b0ba2e0a8bb71c713b47fcb2745d94239f5a5d4f745bd8d5bc134d59f3e87870dd252a01c5521406a354892b44129dbb03f4ba5b4dc06c4f4f9051f8c96813d50a36e9021fc736f8a26138aac8caf2914e886e426d9f2dd42b2c5a3e07313980fa635209938199e03eaece38ecf8a6d75c7a28126f3de2ba89e99363bcf6efc63e916f08771c51b512498f6657866bed707560eef2efd39587adf18c7c8355934b1c510deef1d106fa1e4ddd54381bce2e3a9c258b9c228c0e7b8bdecfaf230085cbf30e2f5c5046621fa64ca4a6d035f020f5799d18cd9792a1c7cb24a11a874c84e50275bcc680197d60cad1baa80a66fac374da8cd89cb689f8fb9e60ab19ca745c0d2edbceda4e8943bd10cdb382d64f285329ac194bf67c7274f436f744b290dc0ec331aa016808cfef7f2b7f17d799d15b5d81b15a9bd4713f1b60cd80d58e400f9862dc194861d469f857175fcc42e57adcdae7a560b3d8639150521c833b07009bee0e09d20aa65628e10585d368c2536454f0b58edecb650c462fe055293f050a9241e38a7555292b24abee132c7c3ccb9877f83ff692bbc252cbd9833a980c9403027164e61b20e1c90f47c9d88a20b403a8e14d4915f6d734f02b1ff9280318cbe3a73efdf31728a5037af9bbed45affc646c017c2895bc82d6dbb371940148d1902723c40eb9355205f99e57de3edec3d1191ba5733ae9d2424c74e50b08e8396d7af46a488a64a376369e8015395d90a0932ad1a6d845a804b8c15c3b056d1c9e9c766bd580e23a28489b9e373b13d7aa9455d7f80841014db4f7d1cc00efdb3e8397b2b00ed8c8ae1a3ec01faa169a17853611fdb8bb166030f2e4ad0cd75f893e7dc782dcebec24416ba9087768b35fb0a3bf6b1ebcd4a95effb1900e7205e3d6a2e97c9b98c6add1d1ec66fbc9ca96230e4c8e766e1877bbae6fa50142370e812ecbe14c2847d84d851a1a72ca092bae7cb1aa02407798d83c5d3a0e2f9cb54e77678a951912a175cc1c7dc43190aec93e0f7851fdffb30df236fe09e9d84e650c55b1d81b7c771969be3c1e618b42e332a3fba3ebdf4a9741dcdd0eed2de19744e11558cd46f45973f1094345bc4f5952ceb66e69db54875a22810b66f658b7c789b894c2d58b7579f8fddb460b3def5751f1cfba3b1de51ea6ec057f92d8044de626edb860077986d8e9fc99b5b090dbf5171539c005639b0e400176f389a975c6bb4371510d1262690e9344c15e1a934bdb5a17a98c4afe5e1503f9b020d7541498510c0af3d79cf1d53930dd43d278cac76822fd8786ffea9c053920ead7e6b03e057c782d70ec6e730805f88116d19995e06eb65e26790af306ef52559174197f8a44de36f3d346a51d3c2ddf53fc78e68b7969b55cdd0276084f38f4c7672dc736410c53cca54881daa5aa82e4337c32d78650cdabc76a4c0b70cbe623dfd3d54d09f1ab35be3d6d918306fdf9851d74f861ac44441b53680d3e2190c9ea257be397546bad460070c965a1b028f5e0192166c153c807ea66069891b844317b2b5b02ade935cb41fc8b33cbbed3f276cf63ef0b2b402d5d270dd055d09cfa4b515fd800a5783b4541dad5b099b0110bd82aed77954d043110091eb222ad815b43d8ec712b64f7af859f7efe33adec0c984c64a484196b56de0f8332ef44b20c00919ef870880068e3711b6df2c743d7758c928e77f6968f6706975c245f1c77e78f7fbea9e5355eff52e67c784cdc2d5bcf3a0536af5b88c1017504dcf31638f42ef4d81a5c52282859dbc2212bbddd475f4788c73a396dc10d251b71b30a5384ad313d7ccc0e1071d5205f3c917694e748a760d38a2ecf3609e747c445cbe4fedf9a6813c7459cb343b2335bdbd25f8c24821090a18edaa90e76073b374dd929b8a33ff75f5ae79038d836cf9889e47005dd863a9562dd190175c907c9b8746c788731233eeb3d0430216b6309c84b19cd185597f07c31c10cbe2694516c1cf680e3e05c06c106e41161f80cc2870cef46555a64bc9849bd057b5068e307fc9892101936d964b79e890ea1aa8a539e759dafa6a871484b530fb38dd402264bc70c46d6d5be4f795543a970fa82e0b432ed842c8ae34a8dcd09c8dede71cb973f40c0f8204a1d0c350b0308a2bc73dd67daf9601fd6f15e720628f191fedd82e6fabe3b9f8286e202c4ee9fb353046c93e9b2869c5b242f0f07131004649978d143fcb6bba3d50a342aa0a1e74c328f0c51578d51d3ded5d30b302561e18df72eed879f858d31f6f476b6afa7d0044093da3e4bfb3a92f42d021ab9b744173467f8743393d14a50a4833cef024d14a7291b0402a55e66bb680bc78ed53868cf26fb187232b09971be4b11fcc9ba6b515a2742b4415b38d4a30c6728a13d5992cf82dcbf0d2e04ed7438d80f60a2894ff983f82d0d07b46fac08edc87044efe79ba5725a44696a654dbf432a2ac8b0586acdaf40429bb60636046eabd52c295ea6f3cf41d2a1ada784d7631de469fb605d03e7156ee9dc547400ddadb4cb1f812af27056f946abef488e966228ff84704f9622ef5cb6b36cde07d587e459443edce664dd0a847e204b13a9225c60807730d9d8baa4ba594aaa0b1f23caf754f8e54e5e3bb30e52471d43f8f2a6f0bd03b18b71846ddb87830c0e982e1dec6facea172e08ce2c5a3c0eef59519627a0e75769895a370476f5e808e21a378a367510e9511309b6e3b2b7d847c52143e88aeec5e5d8f12dad52760a788c89975a793212f9dd64ec12a3b95425484615ba49cdd20ac58022d1ff3f0ef4529eaafd5f4a8e859d112e88f48caf6be8e534a67fa610ca0af22586451b0d69a97f54d76a6b4d9dd1debb11bd36edd88aa20d9946a73b4e2e95cf74707600512e957502c74495cfbb1ac034344444bb0aa2c4b8915a40a08862c7750ad60651a68f99442be6c2b7a8c17c6771440c23ee16e74fc01fdef74a5b4dc7b1d7021bf16ea925ecb949abcc263b1c3deac340ac03e7523bd15b1b9ea83e2bbe7605adeef01bd9fbd70bd6baa0fb3ebc70559419326e70e815e6928f998b71c8150bd682b3f0957b0f66b1728eba1d7672fcfdd1d52b25552fd51b98f16688a4ad025921b0b0552854c5b3302adfff901cd1534da713ff97fdef7a2ef9964ac9390ca1009c367697a59ea0641815657e5ee95cc7883e71b02f9001a4afa1dc48760d61f5243f5c3012773fd2700508c284335753dc5aea48956d5633e34daa736d0a46bbe8b03491908a774264693b9c52f4cec92c9fc0c51b4c66001fec92dede0d23c6608376b5198350c6dc17bc4387327b9d7c34cf5724942ea26897695512023c5539d44809afb8a6802e703f9574b364e26cb3bf32623be541edb12d496d09786606f529b1289f92d1ea00e39faaa45c30003d51f08a7dc142176ba600d80a674eaaec83c3e87ca9088dc8a2c940b06b4d7643b9ff93f3c2d44aafd04d6f063a39e398f8cdc0ab13dc331c151cbf19ebb073bbc5b3154674d44712482d5f03916475dcd7b7db10d35032ef879b67d7f08a37797aec5eca02ca625b05dbc704902a1de9d2d368cb9a3e8ad7eba66f7ac8d2efba57c98e99628929fea65d990c8415719059aea058a84e699baeeb4b2998e8784a8f67c01609de0ebb06d9120e336c192a118c31c06665b0e33a0373c54084eb7483440be51c3b0347af39950425894766599d70dd9dc97a4102e00cdc067e17eb4d59c3ca0355739416c1cd0d58abf115244aa5488beffdae60cee09714169eb3ad5c0cc967833fb4f145890b4c19431faa52a054ca0d8101e3f5d87897c463e8b73bb3e81c66a74ea19e8704e9f9e84484f9855bee218410024355aa845d262ddadb23601065d03f6f30380d436322645da53c936fd5cf949df157192b6f6823cd2f5e65d7f9ce8b76c7d80b41feceafea42463250bfba7bb945df40b8db80fe78ca1e4764745e89fd887c04a9507490cad3f12c29fcd57db877e37822e0aa1f9661bc21563a1dfd6f36bf0beeb7d1abd3d27b7c60014dacf5484d484f988792fca20eb3a7cf0df88acc6406f4ec3a7f9e0e39ad1e2739d7d2a1974c5c110b50cf98a98d774357d34bed3808fffab17747b01045b4e3f6a249a009aadf362231a3ae55d5e8e61078ba3d970d1ce5f3fd05aae712f9979a9bf91e89f5db51c4182ba29ab05edd06a7f3d3d60e7f01f6f700ffc4c554c9364e395ee8053a383261a87eaf2976a4d8d091061b048c68351e94b572ec64579174c366b2b8fa572958677dbf8d6df41132e440c50178d6f2789715283ed48fee203270978a97950ab9cece7a0c3e27e129ef1cf809732f49b46964848bb1482dceaa78bf7fac6d52d99ec3726d6b46fbc83823a402550fe810be1fec16ac88e84e77736f798e312bb953a3c906b20e952769da8803c0b7878757030a5b97c09031387dec4cb433fc9eed5d39f1b4422c9164a80209d4438f17c8f35c805b89ce9b1215cb41d94db6f2b4b38f2a568bf45dbbaf9d081451c0bde352f96090bad25f75a19a0cf55bd7abbf0a53ec32280970a0882b008ab6a91be22d8f0c7dd9c49594c250576ee640686d00afc33d136767a6f05c0a5a5d06143a331b1afa743ea9c9ce51354cb2644e7d3528a3196341d1a9469d0d39e0b19edbede003df50a65505508a6597256c6563532ab8a5d41f46b4818004edbdd5ff7a9745a4d8045846a7682ec3fcf5797ef767f2338186d76c87b6280bcc1c34d8d6491b6c2480fcd008d6a817376cb84155a30b5a2478d57af295bd02ded0f06ae48ab257b4886c3455af20495673a5cbc4d43ee7acd3b5e94da1fd0859372b8f830169f4810606b5a0a50d45e31e7cfed276ec45b0e4fcd4d56edc0a3aa697ae96778087c784a9ab653771e52a61c97b37cc04521403ce5c55db3b049a799dfa902f76cc71d54993fbdb5a5b8c2c7af805e66be52759c24c66c4bc0a42a55c6840c44b5dabca2e615e4c15e9478f4efd586e06ead943187c751e940441751654d77d81a6d346f5e8b2dfb8767a105c9f43e249b1069cc6d9fff991084dee86cdfe7bd460d226f751851b62d77a84f964e6edd7809e98971750c2f005466057c9324e17e9f687f6d91c873f8932d5f61d75158cb0292f41a84f412a03442afa86010e6f66fbbeabd806fba0655bfa4e7dbf742608b456a06f25aa4d09211f26167eef61ce1bb07ab8d4feda3fd12a29fb8e0bddb4ed93e8fec8865e0e51cfa4c5841abc09634fb41ccaa4eaea9bd888efd245a43209a56f79e000dd0f0be33db6366467ce41774364bfd089696e2dc4126187a34db60310992352110db63b71748b200c79b2ff2296bc42c6e8601109b94da669ac61faee01e7407703a9bcd6e785658847a4a65872ca688b47e089b475e77f3499e4a1bdcfa9a5b90c725f7980f90a9babe50b2ede14345daf25b26b7cae0285dfdb7fc76f1081930b92aa79d0b1453948ec5b84b400e9afe6bf9d048e96b5c368ffd672a640751b0f04ead7d7dcdc77f2299acdc7ca440f818609ba1e6ac93b2bbb2b955f5f8cff04c6dbcace6d53021dce16a953266443fe5b7d79c2b75474fd35c1aacc867fdf038e6ebe8da694fa7689ed84feb9c545da93caaa969b62f0dbf73e3ad8e0568f088abe2094062ed3d8ab098e1a5e3fec6235800c12d7cd297c4b12a97c05abf609de46090027c4c8adaa9ed320ce380c9647685d26a7d058e5cf686787d7e13a05f71bd9ca2fc33d421ef86d361a8b0413059a77195987c4357f7a26c756c3fd006b544ce1b43465a38445471548672c20fcb3b4d1beec145e0de062cc0273e70132e415cbaa5436a31271efb533217839790e53de6f2160bede5ceff3e36e5a015efad171765f3e5b604f5fada1489f231905391f34a66bb319a44c836ccb0e0e01a42891a50104d1928855fed47a7a240ed3ada66e468e4be28c7d0b21639d0d261155c8c205412c9cfa0eb7365fef38975c46ce2001eb737fb97e113b1d990510ec6f39fe74c66e255efe2a7cd04f5128728a63dad04b4a1d93c9adb8eee306c8ad813a4b9ffc55d9cded1500c3d1abce753b0dcc65f2e8ca4e354e65b35c0cc86ae001588f40543dbb8aaab6a84d616a9ebd55ae3ae1d04d6e4ee718303401c9bf0d48cf511b31a7b872d3386c58d33f78decf2d9520270f198f2eae0aa60c14b4ba0fcfe9e4eb2a9fb92aee6c946799f5c24261bdfe86cf92039d6ea2d80e5009033c3c2c45ace9282bf7e6530ca77bf30b43cd08706ca2a4a750a356ab009fcb4fe64c1baacca3f9ea132d8067930bb29716d923f83b2f4d5081f96166013e5a189a47e91531371af4bc8078d093a264420edd5a064629d6c00930a3920de18989cecfba1702d5cd03404314033e9caf9099c3dc6bd2c0a1e16cfe6758040685d9fe14111073933caa76bf52e6753c4c4b859f8c9ab383d2e4742784da0fdbaf0ee4ca9ab6acdc40aeac562e6ff7d1b03a7537b9a9b6c4eb96a971c7d700f69c31818caae2e8a335aac7070a93c297c0a14ab6816bf39bb4d95beb278c0cbd85f8f350ec3ee4d7e9abfcd33787b19d2a0c4d5e8aa080700470df021a190a402b32289915cabddea2b456a9d6b00588a5183a7788978ba979ad55047e8c097f9b30c6138b1367530be5700083fde1eb3c848708827e1f4893a1b1dc9d440d8335160c87677dcd4e5515848d53cdde056e89c46ddea43e2a9b5c18bf2dce01389589aecf15cc4f47b6e8510015ecf72ae41fc1a838bdb4dd94f06109713f0dabf8778c5e297045cd5f690379cd76e9d2257bf286ec9029a089dfafc67be9058520d8f891c9b553b3385c015f5fc883cce3f6e46eebcbccfdfb7e75f14cd8045324a51c779f89a0c0ce70052147fdaea010101749011b8fdc55e7f40113a001ac6c345862ab9afc176227150da435e6199c2234cc23bd0694ce13b44bd89a00435f11f614a916bbe7f8fbe7ce0cc4b1ff99466172bf1cee8b4ab2f92f44ff0c39d994141d372c61422a13cd6ea6cbb258dc1c3555b7c51c810d0d2f90aac50c0495d146db66bc4c40e68e41585533240ff15ed9f413c554425af6ee1eba2302df897ace87cbd97d072a68a2aa5f1092667c6a570f36ff95f1cfc386380f180da9c7cb05c108ea178d48652f02adec9e4ef5e35a37ea2f97721b7306ef733d0c1fc5715e9fff890bdb8fef4d2ba3a1dc47eecbd9c85c882f5f0dceb8a16621015f66b2aef44d577757c21d6154d79965d554d6005305c609af5124e59a21c304ae1157af974f978ee14505863950a5042250464e69c808da068d8b645f3a5303db16789633341bb0189af47fd0a150b405e42cebc49a6fc451f0d685c03b8f08f7453bcc6618d16cee488b24a8c96ce1fd54d630befc22d8641a784709620d0550f93190b718267555a879a7c196a20a9764702af08ee3edc8edbcf5ad128b0c14ac83850caa09f2788e55fd2e086006fef860d50a8953b0857067cdf2c2d501cee6edb8b9a2aabaaa0b07d839d77c527c30a6da99f427f30e3845e07a5a280f24f0dafda016392859f294c00ef29e931c06d9e2d0a03014f812f887391d66074a29bc4ff624943fbbf25a89b372a44ecac2fd586cef10e8bf7f907c526002067a5c95346e764f4dce4731d3b2a81f9e03a7ffb0ed8a1cbded841ecc9cf95a0f6cc8769f3495d69dfd9ceb949b4ba3ee374325406dd9cdf494d3bac61f98c30dd47697819eca7c6c97925bf5e3ae0c734b87fc7b6fb6b49fd2dc850f8c5ad20f18df2917782235e5bfed838b8659c3014429968d5fbf3f510d55d88f81c65a063633d6a9c785406a6ffdcac8c5d0c5b24e59174c1d1a7aa6f40706323345820a553435b43d7124cdae4bb37fa9ac7fc8ebd979ce3f109c6fb19d17580211a80c7af4e02f9aae7b3119fa0fd92f3b37e0a802e687143fdac173145295c283f20412354ed011e4016d0752e6338aa17ff7252fb8e4ca721d2f66b03032c987160ef20ad61a798227d2c2c35a6275db1572719a218bcbc01355a1ee2c084003240694e56cb2def1ccc99498fdfe45e35a03545c6033818f27e2c6ec21f03dbfed0b2e7aa6ff060013479c7b1f71f494ec18423ad8434df4edbb8b97a915227e5b0d true +check_ring_signature b1831ef226423791f975ae1c890b7432b77a24f6625a6e6d42185a5cf79a5c6c 22a5aaf269a00aaba720414847af58c9340b5199ac89029c206f1a02250eceb0 1 e9c62fca33bf6aec05853536c4850e1170b4863cf0fce00fe67c4bdfff72dc73 91914ef94042b7cccbaa650cc8e030073cbaeccc51384ec85342b843b657060f4984fee2776a5ce32bc9597af73f2e549a5dc10f932dec053e56a602f84ed5fd false +check_ring_signature a40682c4871fc33aa53a0fea4e829f07246af7f53c339a4bacef7532b8341e5c d30f4fa62d0d5bb48a489c0df8d86d440d6db8843cab2184e5929b1af350cdcb 3 ea688588d5a83281e5a88e464f9d5cb64305d541b7391e68a4128f31ce3fbc27 4ce8617860fee4179084635ab1e2b285463853cb92359b4bd90667c90d051b37 3a98f709c8a6897e036462f7728f651dc122f5120e9d112d964ae527c572b1e9 f90a325c56f9ea778566fec3d1c92b56c512d83198dac52f89f4b7548f1b270f8b50e17c4e916d10b64655cd6e8e03c3984841577c091a17fda90f4a2629e4010d06d411c2403e7af0ff678680015a90405abe21dce151a7cbfe973b0b682606608008f10d0eaff9b0813d6790b7faebc6376b518b4ee221748162dc651109fb38153d5d28e5d931687bc72b48201eaa7d7fdd93e48c58d060f9a0b66b084d000011e74d564ff8d8917138ab06ce4d1063111e2df101a32c21b5ac978e49dbc7 false +check_ring_signature eefd14c5454f53d7ae7ce26733356ea6c32e484b6d11ffab8f9535fd02e96422 d794100567e4b653f689a0a78b58a0f911f6f90b5bac1da2eaf14a3454a57c84 27 785318aaf27c21566cb57158a8fc510c5bfbccbc2ef634c83216942a2152ab42 50aa8b09377dc08edb6d9c6a0d2b14cec4a5521657b3b2d57d2c3fa4811266ca 7faf56aa5ad080fad48b979dbabcade49872f603c4351b513baf792dbebff5ea 2979c1d8c283ad0c3ebf340fa935c6f5d713a68ec0ab546a3af0595e38b0221d dc379931d5cb6a0d004ccf86c2e160611b16ea3ae0e6423029d19507a846b78d 684056eaf2365b9bc01d0e0b7bd653ad76aa4210c74904b34c25e81c9e07d20f daacff98d50318cf68016dbae625d4f12c6fb2f49cfe60006169e52209d88c69 daa276c63a9d37f186bd520bbb78d24f136b8e4ab5aadbb61401bf6016780e4b e4d45a16444e6cff2ab5f0561d25df9fc0e17137de16f4e705b99137d88e17e3 824255f88f26e764ce85eee3fcb559d0d44abab5c3ec8700a28e08992aa017a1 b90c10aa3bbb3fe6f47e77a9b81c9e23e765a31d72ea08ea3267d8e4ce4f5c94 a229a308a3b6fad22d06b6872483cb49bd45e8f64eca1f9e79841a7d98c7a514 e78709dbcece2ffec1a2214a610a1afaf5179285f826ec8253aea5e4e5c00577 1d95bc8dfb6ae8a412333e63228251971057fa35949dced982b157baac0df78f 11a35ea97681882a21e466258e6366ef5a5977a22e180f41cc106f4546f4e93d d2a426e4304f8af646bd4a0ba709d8402b6bdab8565cce3f90a25d26210a1256 64db91a987971cd16ae74e50e600bcffa9860e317bf77dad54dc6587b66ccd08 f5ee9115a86fd97b0e00e348a9a1d97ab8489395f9b720ff50350963330ca9cc 8ecda5f967b98429823979f997433eddccbbd37c84e4bb72495529314a13c7f6 a10bf4422917164d4df711498f62ece17c36df6439ff7f8fbbd0adde84059a98 7df854fac6a43d2fe2bdc48468b293e4a40672be9b02853470f690c746b78731 52fbf3799b545ae4d54f2fda0761b8be0d0bd1ef6809f14a0720d9716e90e101 61eef9ffed1d76881429eedc5d56c7050198e61ebcded6b042a0420c1c85b70f 8df7c702f57c8f4c96340e310bdeb6b6728747feb8b09ab8af3f7bfdc835d809 c402c171f3b3278e926228819b258ad369fc32c4d9b5edfe78784dea9983b200 6284d4837b0201f000fb5930eaa5854e4e992663c49a94cff75865d90e58b95e 0632220ee7f459105fec7ee91c38981ae81d76e67232b8d479f34e0d98ed98ca d20c208414fe56fb901082395036c6983c07f42c7e0c9ddef923e5e9190c2202105e2907c6b995dab1b51245976f964235c3e0aa76fd0cf1d374c3ee0d19630ae2f25d5e7a7c1edd28c59e2f12277a9a6829514837fff490b611f227b74f1b0ba240fa0e295e9843379e2b6a2dcc18183ef851a2fecf6f4c57c2fec46a6dad0ce03f0056cd15bdf5e6d304b93692212605d298cc92da17f6444bb6e2c2290e0a1a7c50d63f9b7b1089f95166931538755b611f6585c17cc4870e27642cc5530b4b61e742451c238a422b8465b492d4156865df660ccb33f259553364780243004608766d67a7075e36e09722b8bb71fdde287710d4568d5fa06e509d7caf350c3f68974b5919ac21ea04ef62ce5c02848b6a413e52a41f528e2ed50043d3cf0f3d06751b69fdf5797fa15eb8f6aa1fffe44611ff990f95bbf09971e30650020c712ee11d8ba11820fa93b85829a70cefc3227632a23f95b7168633da0c5b120d4f97df983183ad23aadea0e7134e2f29cb37dd566518ea61ba226faaf578d50127bad0617eb1accaf200ec1a47b0e359815fc276db41efdd2acc670e6cc49a05d0a2c2e5cc8095c724839eb64720d5c0c5510a8d3b9e583eae70f0428dba2307c678cd528a458e80fc3959a322902eea48552ab82e0623bcc69cdbc74ba0f50ad9705c5f7cbda5053fda80c2df70085f4f51b3ed3d394cc8a39d3c4c95397200d5a4d37016ec7dfcd56dc243aa43777a452926f650f4859854e0005a33f8740bb86c37953f3584324006bceb1f43cb0f57b63026849474127c7cfb8853fb340605f51b1381a077bce6ee73c50489ce307150728a3e37a702316a92c5908f33003e62276f9702398dc762ddda5834bbb75deb3619ccbea1bfd908eee72b9d800684104fed038e564f73f3be8e9d54194f365f3381a5d50ac1713420c24b98c20ef612fe4bc13a7c4f7954c9ebd43c3e77434f0597c2e72a6f0762c195ffca10083520b763833ada3197636607caef9141fcf0258249b50a155e3a4f1cacf2e7042e3b9a19f8ab8f4c14a02256777452cb947f5b1dcad800a6d3ef81773c9c45059c31517792542764877646611a496e169d7ba7cf788236376022d50e8681c40e7e8ee4efbff9114e4c3b16e9d223d450579a92efea3e41236d78efce6872e406e4b3a546d31e4a19ecae09e0311c495405f32d4a735e5be76895a25714d3c20d2e1f195c71c4962362ede46064e11376ab6991cdbbf15894b952431388aa080f88fa52030b3178aae9d91796a5f2a23539a8a21b8111e81235e5e3bbcb39d6025fe80ec3fbc24d7a4eaa5401c47cca7c3c51601d738436229ea3bb0959e02a02c0d807ecdf5ce5543e73cf26465450b1962232cb520976c51cc09fe60361080654d5f8a8b0d159ce1e93854add36701aeff284e6b37ef70352865fad5a902609530f836cdf6c89fb11a4b8f07012f438bd8f958b3b2a8cb09fea0acad994620af4ca7c4c1ce81f1401e86d22dc25485a86cc7f50fb9c01b1951fcbb1cf41db0497d9d31af76932efebbbc765e07bf04e259e120f1e57eea01b7f4b763ee6500336f81f7d6a0850287790cf26d5a2c7401689413378319f8e2205a54effc05f0c8151568332f1173a7cd4e36634b40e6a04dae3abfc8cc606666a82cfc0f6f9007d6aced4d735f28b295cd4fd88bae63636f21a8bb4e259cfc8c4cd54d6e4cc0547be4364ba1b764e73dfb7dd2ec6966b09de245c86517f581c1f72dc3b80d70bfcff6b84c278bb3a55e28e38c131ba200e542e1b653e55e32184578cef8f0c0586ab837eb19d3f6359692e5e5ca62673927a87ffe69ffd3dde32e576800cbb099cd85d0a32454d57c780490c60f95bfe1882820057b7f8ea34ccb9734f9c760083d55711f535c2094cdc6383048897b632337b39fac6826ef267b09f9346330a191a4f628a3ca4095d81cca6b5d3bb5839ed4f25a15ed07d12d3bcdcfcffdf05068bda71edd5d4564e76f7223dcf89f7c511945ef26798e99c2c373accc64506a5381233e14859a179a0378ca33cf42133d16e91f2b48e8adee37cced33aa60bbae8644294bba7f71ee52cbf0016a619eb55bb803c2d4c9d8ddb9dbceaae25059dd73722ed2890ac59cd330232d325c19816bdc9c72126fa8d0cd3c41663fc096413cb397f5cef4250d6dc9ce88f206d523f78348a39dc79331fe17fd28ef5045dc744bf27ca502889fae2896d677dd8a9d49f75571db6410930f80a028a3708bca638eec6c0a27aac75cc3cac28d279def137c4b152e08a6fbc0b7b1e673e064d47122a9565186e5cc5970e13e808d837df200d4ccd057fb8c48900a401660fd8ae2388931b3fa7e18cbc5efc477316ec6706e5e2e927733c408f59b78d5a0e045bb245bdb6de58da95d2d6c7d82ddc37237b1cb08ce1fddc32a62aa86bcb01 false +check_ring_signature bc077ad63aa8aee29ed84d17fa04157d7038203cdbad990d1554e5b0d88ced4d a0366c89c719faa256167d419291188b41304484f9e4cfc697faff6f61964fcf 3 0b78c75a806dc40f3485aab4144c9f99f406ebe0b9aaa49aff62d79554845232 906cfd3319270bdf4264965243610322766bbf7f1552d1a7fa0d30a2aff28fc4 ae46cd670dd2049ac6ee176ea72c6a6d892864ffe267bc766a8ecc0f34f7de9f 2f44d44129041ffbade01d6a75e2f4c5a165d44f439c3bb77e8a29b466c2e3067bd507ceaf3419072bebd3c663e157a61b80734695881206b3e4892b57cd9405174a1a63067390281877dd92e279c32c8a356617fdb1350a5e6f0fd2b2ba0301387184d6e2741181d39d254abb514603edad9ca8542f50c571c11b9ffa621f0c27d131a5bb7ad6855f2d076eb0055f94beca89fd0a07d1b9844018c6738b5c0103b51563c071acc23ddfe8f8f754680f52fe2af03a08280b619cfe45fde8820a true +check_ring_signature c8cefd3e8d56c926433c5ae6af23354af7fa570fc65fb4269af36d9695d0f5b6 3083c4e3435af07dbea0fd8502ff54a7cc43dfb0a5f2620e713c031b9c7d983e 248 f5a84e39433be776c3a7658bc0e2b4b53c1422aae01504e7f41c321b780f7ca8 2e827fccc22ca83c83288d492e264d15f7cb44cb442c0ceb4c2de633947390b0 44cde25bb71595db2c26b0dcc0c2a3127dd278d2145313d12e22a8ec86f09f50 7f3f7782ad124e45bf815e062207bddeb103891d3161820cabaa4c6c1618a86a 4961b603ac1bfcdbf47e2690496c55bc8e60577b13eb87b310c630e78614f98d fd42b808aeba138f53d02bbdf01b4702b1c444924de7763c16a54f9912c4026d d23cdd6de7e8359b3f6116100b7cf58663bb183ff7fbf051324c92a62c94d5bf 2b7c5cbc56a4476edeb52e7103386371f4d6bf1227638d18460b4db0a884fb75 a0c3aeeffd77f23932e1ca6b4cdc1f34fc25545befbd7392713872b50b251e64 4af26bb3dc8feb1f36ce60ed071d485be3ef7e3c2615b2649e7c0decfdbaef99 8934e6ad249b5230a2f2d589c6e13f1742551583e80823c1241175079002ff0e 516141896e10ec48a1d752d0a83ee495fc9abdc32415e2c940a3cc2198a85f2b cf0d4d1543ef6423bc51b8f04019eb177d06571fa0d7f01a1b1729802bd9cde4 e749d449abeaa795c2f700658273b243080fc0d99cbb258c3e6a19e751b57612 d5d1225195e86f689d479e1d279104bbeef92f2a7f2a477ec8b20e0b74525ad1 2129fff2e121ac6218af788b0001f46f13be7b297d31351be0809f95a69f8314 aecffac8e2aa26187951159222f1aaa52ef822bd84d5a26bdb1f4e12e1bd8e53 b5b41be5be57b0a352d672844ae1f7379fa9cb197250a72260751bb299f1072f 957863aa8502f02ee675cf710a7f6b56e7d550b3238b6d0982f70435f0c63723 2d6c2dcb65967f437f0b00f9e3efafa0a868396e775f50c87b3935bbcbe8e7e4 96d6cc7308c1941e32c450d8f9615364b6b7e614e51c51fc7af00b64e49c0572 41e10f18a05d2d0595fdea619bd534ab28c6f7137b510f3360b94a777e3d91b0 5690e977ecd5d8c19375a6a24f2166d2f63aaf7a64604518e726ff7ceb650bc5 a5f5e5a603c0f967f9cecdff0a01ff9ff62e84f7172e2a690864f950a94fc428 f22861c90c6b0809a9bcff418922c0f5b19f29813f8de800c6797acbb9b0fe5f a5d4ff0a83d87644065e5ea0bf2f75cdfb59dc313d159e90a9e94c3dbcc4764e 5c23bb782955ac5b470d29a1cde779d2e29e39356921a7ea9a503bfc0dbdfc9a 0136d464e2cd1783ccd774ae80366156eebbf3c7529b67ad4b7671c6ecac495b 4432c395cd4592e4ea16813dc381154c88754b93e8d76064f707bb3272b0a2cb 2104a1b111498ccb5e3ebced43bffbeeb15ddcebf9a5203078974c41ec7543ba d6d7c9afb3545f4d6aa9291e327aa7c25dceb2e104600d16887c0a5a49af94e9 2a110e62a7eb28ca29aa1b4cdf445670e88773ddbbc132ce56647f24019937ff 675c6c8408408574081675ae420593342a89ca9be83ef0f17e35b0f0c6138d68 de351895178701a062bda2c6e926c6634cea2e1e3a6e794b2e083213b2cad056 1f9eacbe116cf9db923641591975e75f7c22250c56554a6bcec7fc6cc3b3fe44 c6cd6f5a7abd26d5667892571a5f428ec02ca98ec55532e5e8cdaa26c8975b0d 18994a083ee099758a35cfe567df60142cb6bde277ac8f36635bafe579af9ca0 4475688f902b682bc2ae003f5e265ff554b16a99e305f4e49e8459e89b9f550f 6eb2c17179695650ea215128e166ee93dfd1e5207ecf748352d49acfc6c2ce59 edc264bba23b1e0714bc3fe02096e59774f39367e944e6af78fd5dd2714d870f c80535076fd8a74decfbf8acedba4cc6fdb97d972fe80c70403516b4836abfc4 f13566518fbe88544de3fe1a9e85dac02ca6a77796d079b2f5610956c2bb2c38 6f528a94b42c50cce0c07e1a14da367a784ad4c41471738dd9df6cc9a27ab258 672e98f10f7e6d866b747aba197ef395b42e5bb5552869e19690a5fed846b90c 6e45b47e8b7e1cd0d8e2efd3c37ffc622277dcb5cf39e4932b1d23c138425262 b9bede62cac8ccab9987ed9adb8a5a5b4cf562a042c79ef82df55dd57689f3e9 248e5a7755d64a1b5ae8b0ad895709aa67afe9f45635c86247fd1a15d0366ce5 38aa169601b5f0c7eba7259db373787ce8293d1981a40b1945c22df270eb7884 49952a74e67e6b9056af4be872fa46740cb3984c9b3d172e96b2384b20f331cd 4178afff4793df569738320af189d47ea6af6e4ecad8dacee0b8d136e47f62e5 c8fb1163a4d504b5ba52c43bc2c7bf4f1657d2325d57ec635c4e71a5e1afd096 0f029e103ce74e2a0c9b2ed69c400f66c45804b25d851b853c0885713c125d29 a4fefc688a6b4283d0a4d27dc10a874c80c69355a4d5eefdb1a12ede18a72d6c 6e1e73c4bc9b012361f2f670f097045ce108b3b59ba5c94a7dc037fd56a1a03f 850d98153d467970ef3991dcf6c36dddae5d1158b05918a75466d95f88a6b640 afbfcbfe11a5b300f7fb7b7cee64adfe286888876d609bd8f83059d3a1e958ee 2e09197d41c613dd72ed861ab377cd2078848b0d9873bdc8c56b70eb87cdf760 5816463744c26942e5ef038fc9a0feb83167b7a70a70a00aa4b924b935e27324 1a43adea5121bdc0afca3d0e19a10d8d58011092b472e23ed1139822928c7852 bc0dc2432f592cef35eb3066b8b55df5087934e4b896dcf9d4fdee0e3f0acff1 f85e45d934638a0238944cb52855c40796ad2e00a9402c0c5135fd343963219e 66e16d60737a60e492a4339a7558c90608901c9e7f393568766738134b33f0f5 537c413651db505541426a472a76e5bc9af834dd6f127c324212a56cc382b02e bca25f306bc603d05cdc45eabe7ccaf02c6b836af28bbb542e947c5118f66703 cd889dbaaac1a4cbd3ee2ba353953fe882831ce3f6476099bf458942ee46bba0 0bac6584073444e38a9a8b2a37f04b66e313e72feb06277e8789fd9361d8854a 22d6564edfa6ef14e5a09e510fd03876ba2c3a5e186c987806c20aba6db16259 180e4d65ee480c88becf99069dc1653678b886203ab7d0a67d285b00a6abc829 1136d1f50261b8b5aa0c1833951a5cc5f06e1f8c976aba32091b27c77c223ffb 9c320fb982225dac8554b8f6d62f482cd4a3866e4e02800714638bd849196ab9 08a237b2f91201875fad3600e940b68b789a950fe13f3db2589aba82a865502d 057feda29cb3445b6799dfab51af68800ff6bd1755f241f44c58d386743a170f eba56c5edaa3c007501ba1ae60e41fac0e90cc8f72ee7bc26c0ec224f10b381e e005aa97b1cc3cb20e4649ec5f6fe8c755f53de5c7634894ce81d1d9f2e8c5cb a10fbfb9f6484729d804351409a71a854bd6a3ef4a691be2f0770267b762651b 50b17f9e37f751ba5daa013d500b4275ffd347aeee1025ea5e34f40840fc0d29 a85afe74eb4d6260480be516cc8cfce0b30c1bc44f73c5f666779cdf0ed9d968 d9971cbe038ea80f83e5b58407f34611b5c70702f75cb273ae50678b91410fc1 3b6ec8f59a7a04e10d27ec12a75a4f9352bce682f62d6464c2640079cd020ea9 4c1d4bf877d629d0e1a6ca93774fb0907dcc312ab71726ecb9b5ed49dd10585a 9e2f8fe8b6edc09009df1d294c69782aef4b0445f9dbe4a449c9c070937917a7 7722586ba2beca2083874cb6eb4f06aa64d6a0cd58712944e29017ed79d802d6 5f6d84c47f8dff2664f9d09159f1a359cba0416793384ce836982db4131a5c10 8ff5f62a3ef6836185fdd6a10ea3dcdffed4923b19760ac98012f2b687fe3dc9 e29ae6940417b7058304143eb79e3a3a80c9af25fd3a94e6ee62270dee9d0ff5 37ca2c1fb618d0ed0dd76649098ad916fe2417c9eb3206d61eff4ceea1e74b30 7ad5c138fc712de4f52da7dbd28441bee375270acd6c18a8f37dfb01eacaf73f b5333ebf620d482745a231a2b6aacd4ef9d84ccba0c12d748285062cc40b3c01 1d8a3726f7ae63065917b2ec67e44330e26055c58d7250a5e395e50134e9f755 cd133f48e091a55842352d5770c6263792ba37fca60f21c3106d1bf4419cbb44 bb2f10f61c0d2a1ec24248762f09c36999e8779eb6c02022777d7f3b63c6055d c858f628df7cf8c01ed6121a440507b0d3d13956d0d86a5b2aa467d4e404bece 92c841cd6e441030b71cddbe300c500ab2727b6fc26928ee15f994fa260747db e5f8a9bb79ecadd30b442c1338fe94ce368ba260101523b4740d950a63bd1661 994c71acbb510268474f23fcf0918a648900add738565c0d726eea2331d5379a 1bf3d73c05f32a3c1217545e9fef74e4fabb7726382b11a7e72fec6aae12e3a5 a94f4cc40c0592cfc859490e8194880239a0029aa72133d3a4381d970028aa54 233c654b8dad27aac0e8a6fda77c904fe009f57242946b80d1d4420384a4d893 380e4f4b5141b9a16ca0b5ad1c795baa2883337c0022453491bfab67b88f718b 36a657667cbce7cc111737f5b223b01c6c8330e64eac0edc40bc80531959329f 823ae9c40cd2670a5a67de2f40005caf2b78f7a741c229cf8e8d01efb7c05498 fcae9bad5c26de902b20331e403570a2e15c9fad54a7c5e7465a7bc9a0cbfce5 66b81c8615c1a7cd839d536130cdba489b2f88bb6cb54089e3340e196f7dd063 0f2d2264386906c5af2e565709bcbb4bc843e486f355c39e500348caea4efdd2 f632e682aacfa09b4d410b4c9992f7267a5bc1ba5e5120aaa3d4d74bce7d3ee2 6e9cfae70733f1000fe1d43054109430371e94896ae2e692e8d0817833c63179 7cb5616f7018f25e493cb2ada05bbe62051dbf1eec8630a1696805a8870ae733 349796609707d3706456767c5a844c29df3b3eedba1fa9fb5234078a0bd67218 f76d0646ded37cb9491422283c3d8f99d6e132f9fac54ce9bad3a18b946e849c c2dec0b9b7ba68d71bd45c8a61476e91855149837d536f39f73c1c2d3086f4e8 ca757ea7b8ab511e7b440804cd1f6d2d41101fe2aaf2aee6113bf9e2ceb14c9d 04c2f56ff92bd4f356af35a7140d9c26988e4c92c3884bfc7f10a2dd5ad07734 6ba1e0494aa0c67cda979e2a261d164bd25afe9efaeec40ca6c0872b60d4e0c1 4f9a1ed365848b03f5434cb9ceb686e681a53acace40a9aa0b1baa2f76e4c5a5 dbfd12a48f4ee5c5124371e99e7232d56b599af6faddfea4e8553049710e67e7 3ed4e0b70e2ae3eeb989899860de51cc2e320726733ac08c2ab2ef3462c3398b 8beaa248d840347d25fa9064db83127b3107cc03c380eeef66904280c14107ef 94cf7df2aaa58e76c105f76a2d93e965976e1a5b9a9ca537c698a37653de1f79 297980f4aa9a3a336fc4bd2708cfd9541989291d68e4be514ab562d13c6dc8e4 ea060361dd73c4fbeb4c91c11fce140cd65a4e25bad05361ff948560889be579 fb45ed249da128cb920463aebe2d69659e55b6ad035e32676cdec1a1d14cb2e3 28f7552c6948066757856248fd73d01fa549a5c2e19a9e1182ee4200c512f520 e3312c953266ac251b9b65d66495a45735b4ce619d37cddeeae2c5ca34f36f7d b9547b8f4d42cea346112b9124dfed6cdfa426dbfa931b9a9f36c1241e76d76e d08d064fe18394493f1e1ff081ba118fe12bd040dfeb9ff4d5cc116e6adbb23a ce9cda5ccd4d64233d48f7d765ec9f7c3f4a8ab3fb6e4e381da76bdab8a6fb46 64357e22e1f55b28885dbb34274b0425b2a507d2e58ed46404387ec717c86f80 6b2f93855984eefa8d02533bc13494d5f148643e98a91c1f05b044815b51b9f3 1c9e7a57302e2037457a4e38573237987e232d0f29595372679d763a4f2a9377 6ca01576f343cd123f6ff5d45c411a0430f8218079cf7ef078e31a27ba3060aa dfc59548fecfc67f6d5fd706ebf6973f7c03a58c835e7216c615241aee37d45c 52ffdf1b1a41bcabc170b991fb38d86d961c4d8fbaa992eb8d0dc32ec0be1204 b436dcf1c9e2641d773ed0e9abc466cd200d2cbaf8a20fb45421d9b3f4b42610 7835a073ada07ab7a2389a19d941a45d42630ee590a7c0e0511e9ded7390bb53 b94f83a8c9924977fd93e016f2e30df5a3e91eecd0bbbd561bf733ca81b67c60 5f43f48814124b5190b76c918745c72cef4b03f32547f1f36a2de03067f8260b 76fb674b80856553d89199c77b31535b14906867fc10bcd2e187f8f3c65e88af 6b597fca0350bfb94f043f481b0de3e40090bdf07434ce428fae42096ff729e1 f443d48d22dc80b0b188e764007b36fb80ec9aa6aa71a95e0f64bdc90b8d4d07 22c555298bee9d47e9215e2743d1c4c02e88c2840b1d9d5f9587fc770f158bb7 19b5855e41e6cd2bb5a0107986d5236ff10154b189b3ca4f320ecbfd10e7f698 091649a5186597dde83d9ce0b5354b8f72c57b0180c64da96728c560c4757491 23bea56b61dcd09dc8e9402c539d5a404d1480ee6aaf87358a8a8a6ec624b125 7bdb584f6829fe670261ffbe6d21b34db5a1bb77d9062e51f67288992f838aab 58a3a2f21f4de14c4b6682d0d2fb8d3a3ed2f1c4586547d37eb509936f0acb57 c10c4a139eeaadc7a8b01fbc10fae4b72bd08a0cb7e3f6f97ff9883ff2732682 d20045369a32825bd6043350f5e5578be7a3f505595cca307b3f7c7849473ca5 4712d599e121356a6688a20da772064b738e5d8fc7044f313e98503bd8f4b84c 4ea4c19ec7a03f227cce12c45153ed1dda9fe06ec10494bcc23c6fe5f03b4fd3 a0fb5f5cd71e794e9df43e5085aba024aca3839323f9bf8741f40ea12d4709e7 981a7da3ce3dcc36c967116d74cefa168969f5ed1e39dcd2ee3a3760fd0f8822 dd069f30cd99b1e50a4d7f4dce69e022d171ee3c3619f31dd68ace08876ba282 f92fb6bd55716b3279976b15c280a6f1f21acc67a3e1c391cf65289a2d4e8f75 3a24ba681aee04753fc500805aeec1bdace50d0f51f5f8df7f61fb52ec320b35 bf4b9b4bfbfb31aad661651fe28ed89d281c3fcab18807546a361572899f980a 6879ced262599cb0bb91407621cf2a65491d3e84231dde92c18dd17386f58d78 afd7131f46d3d013977688031f83de99fdfee7cc01a4ec36a8edc9970f6007f7 5511e7b3a57a91fda7d30aaecec5397e6981ffcfeda8cf4dd01b22d0ac78d4e4 d45ffe95cccb37a8f47a46103167aa69659a4cc7c83d52f2f2998d55ed70aa9e be75532df61c1f8452dab235a6123251245f342cac94bb1b59033f316c3343f2 8a4b55a04396e2371359af0b054a854a43feaae80f0c86118f59ddd889031caf 7ed8458cd8886ec792cd0eab16392458853135ee86480b1a88bda60b49f5f292 571551b5da27d2bd1654b6dd8808f2916b0ddb967652b04acfa3c27058fe66e1 189d1ec795a6aa423b25ec3e1d71dd8265e44ccafd188b14b06ebc8752677e18 18600da467f5986023e3fdfb5ed4c5464c7cdae9f0be544f1df04d94ac2187df 037addc246208d23699d44dafdd80dd4288202dca11b7dd24d8627e23b625472 77161b34c9952e5ca4e35dbd5899bda260c4a740e6e4d6adf7ec71cd80970c9e 211cbf8871033b7fe86fe410b053874d7bfa8abd6f19d5283388c9b9d18fe2a7 a1152bc65e62623ac2463867d7684ca25738f80275fd924db80418f17bc16506 8f01af0e596c57f4b11ef3a8c3b8346748f4d0e303cc0cf8c47684629610e20f cd95d93aabfc633de8d8fada6430c6bf7df55afd0264e147ba53caa57175109c 169a9cc2487fc5dae84c87e53c38ebd22e85c897d6c554cc33ef5f796eeac7ca 52bba413cdedc5b4382ad13a3cd96173b6478cacc401fff710d8aed24ea1404e 9f8fcc53c588d6d7b9d97bfb7f04ba09148fecf25b54e0064e67c4de7766a688 e0c9ad641e5c92d2feacb0d0d2ac5f8b5fc0591fcbae407c25431aab6b5203ca f360ebf182e56c1e5c567a26dc320ef579f8c25b2eb07d1ee35d1f74b39dfe4e a261c050f4392e69347b1ac6f0d0fd2b65daa1d8111b48cb3510913853e35a75 5f7d47339ba685c25cea477d1419307abf91e348f5517841be8d095c257cbb2f 00d6c27e71dd1de8aa26d5ac113f307f83f3057e2a603687a4836c03a249f7d8 d38a43296a65b3c864953996bc58fd8d704d9944674c690273d5cca025e32888 3a57912f2ee6bbbb7b333414b72ec1c0b9ca548ee1b6f4aaa098f83eb8f662c9 5b518cfd15d5ab8ea12b97d26cf4f70040339b728bd0e77306f4e997c0815ec7 7968eccb2d5615701b9b49590e457e62dbcd99f3752a2a215472564c442528d1 d7adcc6496e70bd0a8635eacb244bd663f47ddbb150e67209aa07fcc1120786e d4c1b3de41fd7b773fee853e3df368db02e2974fb764949d751d07463c28cb5b 4a0ac094b5d377f451307fd2ead480d760a293a961fd9befe2865b5c031ac786 84e181ddc1328cdf9be50967d8015cdd22a96b06c622301a338378d3ed4d108d 4bddd5bda8ade73d5b07a1fc95994b78dcebaff9cdbace5e13755b2bd66298dd 4b78fea2b964c23bb409affb35e180704d8ccdbf28f844ede4d7c3870d61ffdf b59312ff2a5d2b9fc5a2e9d6ae6a5b4c1c7c03afbfca14e3fcc93f3b3a31851a 76d10c574e1eb51ec83813da9a21b227f6006f6cd3092b6dd7869b9280199ea6 bc24b473f19846a7f06f48df023b6257f61facefb1371dfe12a5ad205a9d7f66 6378634064ee7ba61f814fca0a150c3b3bdcc7332b17e6b6184e7aa66a24eba2 a7ffab8f4d202f20c3d80d4f9a29a9cd6c6622093579dfe2e22ba85704a4b1ac 900fb158ed4b5b51cf242b299a8da8a4db8910dbf83a4a7692b9ed76ebcd2f66 99305bde031466f3d4c0578b9b0b1f41f2092ddee05f034da153df3748d895cc a245ee22dc953756e7cc3baa920ab6f7fa059a7045dbd7fb654549e40c9bc693 d1adc83a0f63fb5645d9c9cb7867c67cb3e65a5328590a6ca5f811f21ca15f60 663b5e4567c84d28bb9715da4cab7c3565da920001b6a2cd30f54a16d7d60a94 f9dfcf48b29a800f2c6bbd37b315d7948f704b3a0c296e25ce1b03d224aa3b4b 73294b7e77c153db1e3be27089bfc36007a0965abaf492491f20a6b37807a995 c807ef24f2d3603527caca179b85e2650aec4caf8b76643132d735f6d30bb729 cc0a45a728fbf20fc65fab7d7e7f86799ff4db35f123b1fd782c45f4bb34dab8 74fe2bb80a2d46e716a4e14b6cafb5e35faf02e684f013d284ddd5b734690cf7 28768dea10bb5aff70813dbfad3691be96e93495bcbef12a3ae44a5c8900636e b60273586c2e06d878ee70023c0b9bacbdde42816e206225e6e220d49c439517 376080c91a0d2823bc939a73f0ad2122b9399117b866ec77e0136a7113e97ea5 e09f96e26c9f6d385c424e332fab5d08e760f83cad3174ff6eb8cb41da547417 6047506bb22ab38da2eaae359c6584f1d148fcfdcf40b25349a906b4401257ca a1440a31b5ed3c0bb2279c56c257dd7718b79c8db7e2bb0daa746b8612f4f7c4 400c8f5960dbc9ea5c0f5b93da80cd0030b0046b01c395ab6ad8f8e766596126 b0b2ac0cb2385abcb69ae8d64f23ef8d00b34a0734fef769b40dc25f055acc5d 7255811e07c9ee103bd6f3cedf2288cb8cc1a15108adcff7571930c3e71a9968 eb75fe0e601141e9066fd045fc2cab302022e7d1f74ffb3283828ccbadd4b32b 7deab5d58fb3a3b9eaca00086faf1d23e8c227e8e5f2c827fdb5ae7f620d4aa4 94870a1cc1c56e5c12eb4b8cb52e4adc0c544942bfe9d3c9a3b4ffa28da462a2 00cde5b7b89019823c798158a0e838ab713a270ed231f4a0897e8f0f432de983 009713966811eba6f44f5c341195ea953144ec0f9d48c578a2daf6c3ef01bb0f 9400bdc2b85443a294f1de84bac6387b34643226deda20500e707c019fedc251 4e1512f91b1ecbc0457e84835cd6afebf453402da4cccc1d8a0930f6d48d642b 5f860c4411a4692de087a9bf4aace468b3c80fb66f6f0b8b98f1cc42f10b326f a45c4c3dea031183ebc5287f2f7a9b162e470ee5f0a09a9aa35502b664aea188 dd2c9a5a5896637b289864ae2c99aef5071cefe1fd105b030a95d755233a4767 9a78edf0b9d7c622fbe9f1f456bdbc57be402e527f329f0dc7c3dc04581dca72 b1687db907a416335913412212960803490f99867699c4de5a9fd81872c2c36a 6f769437a5d3626684819276283e8433b085a0506872b91917ae2486fd8a59ba 2b1a6af4b27ac4117a146a26e0c43491e44dc599a6854e51826ccb5db864e690 ae7b1e1a0e8fbc1860cca842ba57090d0133292c91d612fd77cef18f57689480 2a58f33a363730258b62ce6cba0e55db2a9101dd2b4d0ac030ba86bd16fc7160 d25a48243a9e242d7ab962f1d5b5b3e6197ffd766b3165af8cd80c3265f0095a 55902a44e981e63be9232e671df772589faed37fb1b0a2375b3b003cb3c5ea77 ef74b090affb020f3eab90086409c83860a9e609f09a3ca0799381e373931706 f852e5b8fc7ed3dd9d800565719c23e74b3b3341a8b0d2abf2b6fdc55d241fe1 279eeffff33ad68f20424748bf612c2c176c0d25e240931c746fd1a48793ef97 18113756a37c2cb24a12a239bc9417b169c615ae5cc3b009afe5f1c7cec95a86 a1d871bea8b29d3e9b6bb81e9fe891b2f3fc0c8e7fba085230165ecba8405dc2 3e6b051523c1b7657d00caea70edae3651ce5b530b59876b7a2de302f916a575 4204f9e0a5dc40334b8350dda7f48c20450f1fed1a29d2f996053c4761f94580 aedb4caea89d355c23270e600eca3b07f5e5d39bdb52c6c78f36748a0854d2a0 bbc8e563fbed86e50a949cdbf3621ece02016751f0bd0c19c25ad66bb318aa2d 32a0ae67472b277af6bc20c198dfe80085719c603cb6780987f1cb275b684bf6 2d7782e26085cb75e2c2342515ca17f4215834b3dcfd741dccd3f2e7475d9c6c 75b11222a9d37fc454e8542d7187815c1bd5074c714d96b2ec5fbd3d33702215 e23f83842f66b6ce9ecff3e7a11ff07db8c57d68649b26bdc9c0d17f87e9ccef 517f37de7e37c00e00a546b12f1fd9760dcac2e55affbab643863bf36bb2b7ae ce1f3c68ae6ea3c9e8473e29a1952aa5fc4387e4b2dc3c93d7861e230a436240 1bcd982bedbb8360b2be80f554e012da4675119ebc00a9effe036a198fcf62b2 3abcdaab825031227d70802f5845a6a736756a4a54480f769f9e69acc2bb54ec 0fb97153d7641e2991610a0c6e1710f3d44d5a395aff9c340b0d9bbc09f2d60031e23d7f5ee57edb3d28fc6e6240313ede297efc20bfde397e9b8d7d6d3da90ec67a8458e3487b8e5ce5cfc3ca05ddd4a689986794a7c3d6a49887541770890e73b2fa92af69e15d367fc608d10ea250805ac4d0cfb3bbdff3b6795e4cdb120397ee46aa00f83c8b17481ffa4d2c2f8fc982ed704e60c37499a72c83f0bc6a05a78ea32c907855ac85a4a42c42f0f3d13c98b5ec422c9030f506e77df21ab60667c0c61032834305cd2ee475954784a4952b985419414f088204fbbd0518dd0d8ee57258f33057a93794f280e45bcfd7ca433cc5caf204b82d1620dea0360201e2166ca664bdd7d285e8f5b9464c55a91c74c185bf23acd22f0cd5e80d8f690b33f26159d5c05eaa4cdf27e4233e448182ea0a1b0c076bf12d5035764e3a090f367454f40bac9f3441a227d5bc35031b8a86041b69d9599fc0532a4ff9880200b3129f2924b5af1e953fb15d2b030be0e735da949be371f3c636524207486206321662aec303f90ded315a3c93ffb707d3e551246c57e6b11b6a59839c34d205cd97e4ba501750da653e6a521ddb4fe94178ebe36e1571a76008216a7c828808ea1e02b8fc58157fd7d349c45525476d61cac14a8c6c17f5c1a5f04263088c0e8e40920886d9b4341f8e9d360ec9700399caf2857c026c9f26adc1714609060b9fa3abc90fecb0ed0675492c6b5b8f0eca4fa7f656e564b39807899d5ed6240a4bf784366afeaaf9ade32dc21ac9d8e2f96f7a137be54fc4db4bddcafd53bb0ef079af2f438f9e618deb1035c0d75509118f58dad8b991fc6cb921a9b699f2067509377b7c3a4d93201d9f1a2e5068e2ea8a2d99bf5f07a183a6e75e55d7dc0e8f38db4526a3bd60e15d89230ff41fac6c47662fcbfac8e1456d2e14f7ec5109c63a288de37a76fd4486bf674719bc4d2853b1d04bbe01da08790e933f68750a491cdea49a09434d174ac44fc98d9259b3ccbf1d97a805aa7096dd95b42b760daa800e9abe62caba28d48da63cc2b21072ae1b827e8e2f2d59a651b6e3dce40637130c1502af52b18b6b1778bb1646cb460aa80f1deec70dc2ace0d275dc270e0794e0850b51f2b32a02a6db621388cadb7410aff62359100ddbc0c89279b00f7af02ff9a2e10d4a14118caa80fe972ad12bd6ff899c533baea23cd2b900b40ae0d5d676d6492fdfbb2251a5f50d0aae7cdf3165e0b9895760b51becdaa51f0d2337b5dbce943655fe84d38a92e856407e1977c5174604a51c2686c44cfce70c9cdea1a9c9d1931e722aab2021c81852a3e017d46892cf7fa4e257522ff5b00369bf49d695446814cd1e218f60f2b62b67703eb8548980b83b883bd1dc9629056ddc7422d8b40a10b47edb3900f4c3fb2735a631a2267acc8bc5ec724a72190d47b6348e53cb5df5df0cede6d94fadbbb76561d6dee38da15d973e81f4e70c0cc912bd976169be8029db31c2e314b73015e17235612158694b3b6969c6f2ca0a9dd1f8d0c0c848147204b02f7050ea947eb57118b9464f355d401495acd7b30578ebcd669cdece42d953fa77f03b46068e62e142b698ed28dca8de992a81d203fab483c5854fd980ab71934a80cda9ae2b8a285ea512fb6279d42f7270969c051624f41aeccea6b154d703c3a2ad202f9332f690f8fda0c822519ab7b8be8c01533e3f67739eeb3d35ed85a7ad59a8f0bbbf7500cbab6896e09839177d0c5a0671202e20dbc9b8bb55290179ce7dba77cd4f968ada637252faeb6a1a88a3fb0d9b2b0c4c1d9936459bcdb77953f26d1231dad0604ccfe6fa6a0cd7b5166740004f8b9dbda91fcbd7787783cdf6008ab1a3e880e56005948a5abf5baada68a20181c09b618baf89973e8ad1977566e898cf4a26ae7ba09c22b75c28219d644b08f7f428e3ddc0a0591adf1b64746d077a9b0150dd53f45baf9f4c28c197adfa04f1cf300e1205bde23594c22c0239b384e541ba8187a5a326aff1ea924d8da604d58647d61ec17352e9f16bfe29df55711d15b8a37c3dcf7899949bf5d5ac3b0d0b5ef0dd72c05994d5c6af4691db1bb3f742f21bf551b91c9ad41c57c72a3d076f0de2a7d93f6e789bb435d80e657b6c4002d863b669e5c542547dcef1d90600af647f2bf32a779008fad5b4e76f6e18df227a26002eaefcea6f621841a94606e1735960af7a9348d5d452b7931b7b31ac5c1a224305dc21280d21d61bafe5047b82039f77ee923331736f300628e1165f016d480d94b8c5796358076c6a9305a22f98b1428b6f748de67453db7bdf900e81fdeb1dbbd6413560f035cf69d4009839ae218441e8e692008444d83d4b35873ea53f057c0a28603157f07e7470059e4b17c3bfdbcccb5cfaacd13b902343b9d7b595ad17efbe5e583faf5b1ac806fe41472d1c318a038d8a888bb178663835f37142765de9a02119b2034cdd0b0031c704385476e317c4ed34539da819ebab67585c3b6cb9fb962aeebe1276380eebcf5d8a617da97b55d8362d82ce52138de3b35a02582fd65ad1d2670f09d703911c46fbfe11e937afe028ce741b82b956de32412564fd2add64d9add73f010a02b9c5a49139c859d7df7039bc80b95dde9ed585b91f7de8e58d8009fcc81309c25c0f990495c8af04f119145130c4f88373bb5b331dbb69fadc85794c7ab70989007314d9ca679bbca0c61b508d2777ce01c5ca4c475bc92e2c0f9740259401d9129cc3fbb20282f0a02b206c5cf100a78c0f9786e64f172e610553f8a65e0ba778f463337a7a2f7554e7bd823820502f2c69e31ddf2a350fb8ddbe8ae0d108db909acd150ab3028496ea2d226e89944c3914c92814854f28ec85e339d4150227f0b7e7814d41f30267027069fad5e18a3766d6cd1b8d7c348b9c0d137ab607587e8639afc9c339ddc078873e33f812ba6072800809dd8e6744dc226ed9950add4badfe69b017e5bf96cab4553722b63ca059e31b46a2d64d0dbb622d62130fd0b69f35f63825f1809ae8d757de1f0ca441661d0d3a1e4aab2b13cb835977005aab14b404d90ec0f079b3276574e16fc61a824c26c0a362b7ddc03151064b0b8609d9febc0f2310fc2fd8b1e0e8a05339f65281dc9675cfda4bd8954720fd06d485054e9c485a7070d30042e8bfb931f2f2670515a5185e4211504554e7970627e8a7f599c009dfa154ced2773dfd955ad63f4e759789d916905c17f0faed0a882e29a1d48ae7bac5750242b817ea797090090527e2caceab990f59321cff0c4a34b79bbbc2f6f576d4068f08dd36f8a3b1ba3ce18bb871b2652866d425580ed2e14811fbb4a4eb9660cf74b2747f4dd4c08728468018656472342b9f1c6501b13116465cac6677d323d28770383aa684d81d264ed5c5d7a87e336232899a0d888b6cf40dd1f8ad2395ff72c85ff0314768616efe2d58b7d1b644a8274d84040136999e7c2259f93208c6599e5a9c0105d3e22f4e027a602f569051f861b9069680c96c421c89deb47deb3cfe74d133a5fc45ff17533e83689745d5d83e2a0440da3515ec26bb39abcc3418d00e8e12460efa390dfb08b5c664b68afbc30609f190ffe50228997b7e9f57b8f2700557c385f48c6d42695a37c15dc2dbad150381a02a5adefff12b21da23fadc93de1a1e303b4bb9ed2751e0c8f6c3a24c240f22d6c6404843da188af8977bdeede663d25605a80e21cb45773615979c5f0e0202ce629129f8eeeb5fd258aac7a4bc03e961655e737ccd93bdba52013cf5be0dde2f4e5e57f5f6b42e0584772df87c68d8b3626b83a6d74e060a8605716a38027e1ed7b86cadc14e367e66eda53792dcb55f30569f64440d2df0a83759552c019de40f880b4465e2482f5f08bad2d958450efe0d380e34b830c1cf09e7376f07720d4ce59f051e50a18b2cbede3cea42a33ef91c0d9fbdd31e0239ac5eef2c0ce050fa252892e0d6a1e6ff7f7cddb419b47bc7631675219a83e03992efb9260fd0f2c02efbb430ac4d326829be0ccc340d1db7ddcfadeb7de691dc96357d1305ede8b75400b63a0417e3a139b9d932e37bd6398aaa486df7e9cc2d57e384410dc7b0480e75ee3f675e3fa5082ca29bb34a78acc4a284a55461d57842ddf9810a766a8cbfacbc0d348e698a3fc456959d75a5bdc2487de6d616427c3c524210039a1ebe9638b8c1d578acf2b39498ec292a563821bf48abc8098af2681f5a0b0e00b6e411aba3735bb902a57c4a24184bb0056deb46fa0649f573fa85a715b60b64fcaf2c623d3cb33c34400f8ed08a3c7f1451963ec4da9add06fce1dc05d309b2f071e9a72784ac1ef166f7de7697d13741324141b6ebacc575c7f1b5f92d0afd9d006a94ed6360929ac943391a20cf83c0dc286b81bb7a4d6034187104720dc49f0efce134cbf2a8569d7fbde382d25d5c20d55c466a3ae112704befdf2305ad31ad608bb8a2737f44fff58a65ab9776f2a7af707d3ce0ef6250fc6c3ce60f87c262bc0fad50eef9646da2d3e7afbbe4da23a9adb8052eaf0cc6b6307c9d03e64280fbe6606b3d2eec17301705aabc696c7997e723c36de6cc60a0f129e301b11137177793f385b615162c1d19d2313f25c4aab5adf53305fca19b32e2920af1fb7579a69a5bb8668b05389f408564deb6d769edaabb4669a387019bf3a8079b7452ddcf455db55873918ee1f78514ad98dc6007a3b0b9224d6dd85056a00524edce7c51ebe7d8f00e54c6aed1c28f14a3bd4f0790dab662d9dfc0dd086501fafe223a3348c3d19d9b46f50eba917c77dea9bf54d6da88e241eedb1718f1067bec44a382eb87e7b0b6d7acf446ee5dc8ab24e2905d1db4b87bf86e27e3b5062e3ba5dd02dd0c67bd7623e98bfd34d0b41d550369db2d2a7d89b7a6b1adb101090d125e2911a543cb88185f2c004e1cfa1a28cfaa9e13956f9a03e90a02850071e3abb533122a24085001e083a220257cb44365081f8d1924c4958797346b0cde6defa8cd00ae8425c282b0b5f38fc3bfe7368894bbfb5e75a90771cc501b020476ef5dc019ce7c31618cce43bd3e2dfa6df0aec77e2aa1e76eb861b85cde0a656ee4d7431e48a6f8932c275ec987d8b367ee6862c3b3c91847f57bd1d3bd06bc542f85111b718f1d84389f08fcf57adf0eb80b9df15731f106f869bdd2aa0d754641dcf45658b23d69fedeb603776ea4c690e5ae1c9bed729a0eb508c9ca098853adfc8a3b0202fb98a745f72ef60eb2386f8442c8955e563277d0f9d782005a72284917e953f4f7cdcf4e971f93310f3be8ef722c6dd6cbba0bb58c7e790529bd1e26c46120b36f28a533c51dd0f8c40e5504169ac32b297d6f4c3119b304e0b1f830920a4bdb90ff978ba8e164e99e11a107b474cd3d0f833264d942fd05caf5ee02c69344f563de292f6d7c9bc774a28405c3d3473ad748e765b038400863f908107ab46433653ed807e56916c0749c73f8a538f64191d5bb5ccdc38b0aa304338d3686305fe010e8e4828ff166f6b71a2760ad43fcfe53413704c1c50ab3f306ba5bd04c3ad682e656f3e50b1c5f10bdac9924e4092c5c24cc16dc8800e61efa3cccbf93fea4e2487f78c789b0acb0a8093b9ed2a5182a156d9f86540b9ba12d0c99925dc5f7aae1d85784b7437210b42c71a8afda15ad160e4557330ad1d458077f11650319f2792906267532f3aae325df3aee733593e4cf6ccebc05b2356ada6419bf85e6ac34a7691986c1e7af3664fa17ebbe0f3d622e9bb99f0d593db614f30aa3b1671fc274b010ffd455fbec07fbf7dafaa2194cb1f94e27033b0bcdc7eb9f88e64e07db90c83e45e8117834588ebb3d7cfebe7bffc8808e02f47de47e9d315f79480b58374f131050e0b82c726e78402d228c0298e5fb81052f85d14820202c74db5cc2277e745dc395c1a9679d421e341ff0b060bdfbaf01178595ff9076bc05a18526e1e56a863b65d80d47a6e2eb0677354313e6b9a509d81ed3be53d95aea42c70009f3c35e991688b4320687c66801649816a62b250b53f57b86a4c5018f2ba8d0e124db22f8194d5d25f60906801681a4c844937e0384584f31a7e659c1a85f50d021f9aa00a4702cfbbfe18c20b1c8941f4408bf0ba5bc0988c4d91bd1aa40767589dcc162b76b6864f02f1fd128c4deef7cea120f14a754a587e4a42c1031715bf6aad04453f6768db187660e02b01d6e0c831900be27c5eb9474f2a22324de5882e0d75de6bbdd711ef8df80164f50a16cb7d40549ea37d2ab3f865f486d632d283b2aae284f680430e22fa33d085902f25d8702c6fd670aa3de4e547df9d938f9f087a716f4615345be14fac3828777a76b130c2cbca5d8f98214269a7625b82c14b8e062f5b98c35b5a49da06c4a4e444bd30ae4f7c05c228f74ce5fdf299ed28d5a2cb7cf5ceeccd4fcf63e3ac8138e53ed070903b900ac4c5e7d764be3e14ff4c2af3c4fa302ce73803105ec71ce9898ec00f7e6e7322138596ed06d9bf71c4d11141ddcc0d7c4b5e49419cdd85a9f5bdf0241bf2b6fb9dfc82416b62507772c3a984af0a416f22920d5fdeef39b5df1cf087e6e47fa0cfc63f75e810d51ac265c2967631d477d2694a9327171c4cd51670b0954f09241ad7e50a10df1937039774debf9304e6d89976b0b22b81d78a0a20fc489092475252bc1f4d2abf7f24b85a3c4bd5a1e510cade59cc94d04ce829a02f30593107ee6c12b340c5d23abd86b3333f7e68e1ebe84dca5490f2aa7d3ad0e4fc7d35d58e91129d01cb45040b6e4d16ba0ab0c45642f3a1b48016f6f364603c5feb28844b58bce297df8abb2ac35c06ea863682199b5c80a0fd32318de0102d03faf765ea5c56e457c3cd3af7da3556d999c83992725d97487f704ff601806777b8bf3c933f45a5c2810b93687d10c0bf9919b8373c023a6074f3ab902d6028368d80c06e0d8fddd9138bd4164c60da0fc9d4bafd8e888d142181cbd2779022dc2f7be53e8766aeac24e78e06a199005f291217d7a3905b725a51934f25101c28a0a48c8d823f371710c3cc85717495f68e2c1f9293fd2a4ec86ebea30ad06066108307b4d36c5b70a33f848fdda495bc87b17c40333982288d3e3bf3edc01e6b4a8aefe40b5a3fe3837b808cbee140dde649262bdb3ee6e2ab39b8d8a1f0012a1f7c01d83913e7277fe12343a75a51df62868a300a25cfb7e7ea28368310e287d78aa05c74e7cfcc44a1e1ec51e2a9550c7f17e9bc7f68b77ae59703f9708548d1650c8612ff71727b98eaf5425024a8f01c944144638910773f401afba077399a599e875792fe8f1e537a1561f0c04262941c8352c370fd6c30c7ea75600e8cafd8bbffb7fb58c633c27eb6a39bd1f9301fb80bf8fd13f9ea2c758a2760e5de1304845297b5765f6e54cc4c28775b4491bf9f3b5ee336e2638065a5088006241fedf287a70ce056d623e8646d5e976ccf467b47f7725401e76bba37e050d52213baf376aac2f23a0692e2de96deb7116c6e538b13146ed6e5a550ad73f093acb85c2bf83d9fc01e1d4137ce55afb95ee7c15196fac3ab3beb161c5fa260dca4d32e2eca1fc851a10de0619cf5332040051b7da8c3daf710030caf4ffe60e84fed9086e92568a1e11ae387a5235f3e37ac1442d6525145ed713ca4ca7dc058f473373022db544b85fe7c4f94fd2ca1950eb286a14ebf99819d86b05f2be0669c96c735265b494b772911bed30149dd27c0a4b2785933e30ea9b74db4e9a05e3fa6f01c856217e9c38455f626613b4f809b7929a718a3dd168dca84becd6056795ced41dfcc465105bf0d8281a628962b26ad3d09c224a729e0651221f6e0e6072bff46defa0df7a1c56740e4bf8c024c048e52b81b80adc1613970e97a70dd7001e776e899122382223cfb84efea6b26f2b9282fd852e782d04d6038a2c0efaca7860fc06efca038ed439b483e91d5aa3c2f03c40a87fdd1675a909e1900a843aa2d992e7e8fea2412ab4ccbf3becd4e0b011b75293bdff230efa91366e0e4017116436dfb8a4fa2f96dbffdd4a1424628f1e6c27b1b8c769fdaa721c4901b61b3a06e32801ea338acbb6cfffe7a2f8acdd4b2eb318d1f9ca0779b860f504bede590dd782a998d7d9d043a66516c0abadbd0773a4e909f3f116fcfaa6c1073ec400c6deb9d35f1e1a832766fd3b2fc685764be94da1a4aed9a04b7718ff099d28bb0228db48b4d3e887aef22600bff96685d9d319a25eb8f163e5c3391e0d49f5c0818e95aef68ac2d942cc58396fbadf0ff784bd572dbce8e81d165bf1014bb34beb348cb1bc8ecc5b2921b6781815dae9d67de0004161b967b6cbe99004354ad4a7847b34930be0ad06a4dce4e22f1663375413828c0dc1004cdcbf9c08ba354dfce9ef6ef5e1c004458ece1c5ffb930dd21d24e548a307a5336a108a097226416bf3d4780918b6ea2a9dfff4fb52bd73d3a76cd3ad911f6d8c61fcf30c115b470ea21c39b79b916c53f6472709ec5d5fd2c330849464ba6ebbf83450077c04565913ae18eb4b47a2480d53fc1ab1ee9ea8f7a6f564e1cae82de2cd760cbbb47a270241e11d4361f18e2b7b2ccd15218b27419bd2d682254fbbede3880ff507fe761deab723ed1e19ffcf0bb38c59b16396d1c47d5502ff4df5545a6808963fd4585c5b27e68c6b21fbd45d3dd6aaeca01d6de517c369bc34066a7c7f0daebb7148a1484675940107f8d76ef5cba862c7d44162214813b7b1fbfeaffd00aa9ca90f31004ef3f07205587182efd7cd3848403edfbac00d7d121bfb1268020a63f5704a47472c2e642be225265cb4c75fc84087057cafb9f0f68c583a2d0dbfee61f303e94a68a3e99889cbee04d624692ce5fc04900c1e171fd719461705006f257ccad65757bf6d642d9cdc9043fbace541b5cf90584df1440cc3599e0ce5c0f9f27e51ebb29ac755dff1844be90016a7a71cec6382a6d434125d2a4d0a88f085be5b42d5cde42d5e796f27a0418c4302b50f94307b747adc84ebbe490d4e57ca8267a52687fac901a41a4b4074a8857a20452e0f56649cf96ee597840c94efba24aeca7426411d70d748d7488010834e0682d55205bbe8300cf17d4106fb4086652562bd0691230ef957ba8bdcf77670c9098ac0f12dcf1d715d4e390cf36b82b03419ec184579d433a456e16738955a92763b9f8a70294e9a279349007093b5bd53de4f81e5bfe226c713b6b7d9f0120dd5be294f99d91ce4061cfc04c9af714712e97e0aa41b2dea39cfbf78ac3f67ce83aaceda9cbd5b3c59963503b57348bc67c25d73aa9aa0f131eea1d87e6aa35e67226661ec1923ed4943db077baaf8a2265a07576299e25ae4256ba0c6cad52f73e00154f97bdda416858600ba3435c85e0bbd11e1dde591aa5f51054cf2b68230ab6c7b4bab806745f9680ef3a5c74a39e11adde76bab0183d3915eb8acfb61a8726ee06ba8be617710ca0647c064bdb26c6ed8bd1f32b1782bc9d7c4e2a6b978bee914f313dbb076ff1c03799a1d8ae960830df57ec8c4f5fa77339926546c822572b225f7aec5d8cd3707a94344e636527049f3d62a7841a8f06beebe8e27883afb91dff7c2cf83896b050a4d7fb94a89cf08b43e15bc2987d11ab3e1453ee981714f332c21273a979e02c33bc0699cb759b4d5cfeb4a4110a85762ebeb7bd78fe9056262d892a3dcb20b0075b3ea0f254ee026e20a9e134d264c289fd192681d7e698cf5157e9dd46e0d459052411e446c2a06c668195f77a5a16ca6917d556ad4ca652b906318b42e0428aaf79440934db0dd7d8386fdeafd2e2c22a1e06c426a829ffc7f7e885785099634753c7428e6009761af4eaea6744ecf174c137547fb8f3addc1846296c00eb2ce6bc549e150cd9d445ce463ed8ce2aedf33714ff3e012e24de63ef13dbc07f3de00b21939cbfdcf377487e2e0326616ce94598e187f66473e70f7b301bf05f50c069fc1109f5c714e8a5b264d517f28f3aa89f52435a4c687a648dcb9c70081c62fc105c0c8b006d7b931510c64e5d5bf0fd0887b40a8a3bfdc4501910309de96702a0f16b0852161bf6a24300779833c8cc3d4710acf2388df15e822d60be11844c3b9a628ee9f1b825179e683e9f42837128b5fca7152c44222b1456b0607dc808cdb05ebe38b39b2e425838d59a6d5c85d61b1560b19db9bbcddc2630a00faf33bebdc9b5db1c69018aa2848cbe2eaace8aa862f181e4a1cc19d13280db403b9afb6a1c8c381cd07bd6bddf9bed0b0196c403934526747abaa12c14a022f0fa42c8d4f0dbcbb5dfae766b277b2fb2c40067b1b8af8c6618e6b94095f09f9a31a848359cb4a81f129b455b8888599eed1d3bb1ed18c42c70bee79adf40f7cf93b4a0c50ade8f13bce1a94797ad0a473433dfb9ee8c2be8e145f80c0de034ba2d51db93a7d89e8e00f384c04331cacf9e04ebf31273664d96a8d9de7660872a69085d968a5e369566c6307ba550ec502faef49da5d2a0931ea06478f540b6403ad1d62d2c68e91102ea8ea889c89aa4df0c4818a28319f66cc5ccf1e5500eab761f4dd691ad2714db970bded581c5897c7f4fcc2e561a3a2b47d91b22a044e3c73ca0fa3ff546c8e54da8deb741246496a20ce152666a2d11d91b051f00c8b93f3a88343cdef6d8deba9a5915f3279f801a73fbdc9dbd7fed93aa16cc300cc898cca2f22156e29d1c29395ef80b503949051d5a4f3545b57bbd7508ac401b737f0ef582f31aba66df4c8289c9bb5ed1882f89d0f45e387d38f1ec8ca99022a87ff4964bcf1d4434153cadc9a06549c26ce387e4e361caa27c956024a990d45e63e7f502487add56037734a3b1a4bd153db567134c7be527361144696a50d0e91b78796abbacc2f9e109c8f5bd2e530d747b49f25a42fcdf1f568a4b3e50090850fa01e4118d7fb254d545eb0a5ad85f4878f44ee7b4db73421b957828c0fb9fae2ebe27dfb8440fe26b709221248d420d39fce567330a2b6c84b98cb400490c7068707a06eb7a6a5633acbfacd365240bc6c9323a051fcf262441efa8e0cd0fe89882770f42d6f28c3e4bea45cce79e5980850a3de16e2c0a719a4a8b302d7936d3d08f81ca048b5e70a210b948dea130fc58e24f026566ef8b22512a1050640dad9fc6fad7153e7e469d466265bc64ab97c172dadf6e3c8edf0a1aafe01777aaf5ccb446e26c267bf372468a4144a2f2cc2f309ffae4ec5845577c0c00aaba2e52b66b04eb56302ab230249cbdc3dc89f8d2e04c43871343f6a47b9680b03556c45c1d12b39da5dbf2c82cfe747e8b891754765a3687a87431fa8654e00822a0e0ab413d09e2fc8f02d82a0ca506e68d2a3e3134af546c98c3679f5660a221f0acb1a5230ee661ee7fa983b3affbda7385a6c25f42db14219f71c8d3e05fa996215d4a18210e00e26b950b0e1c1845e96a864efadf5835fbccc8cfdf50b614dc2340643ec17662107a97e460f350e3a2cde12453b3c52be2a63ee61cb0c349c8506658daef746aae56919f1925717aaf317eb28e43f21ee4f799b892e07346c9a29ad0ee52cb09a61c777c0f3af615f059c0fdf32deff14be089e73e902c9b42fcf5b6e6d76be355a3a3a2a079b91ff531815db832010b2f249f736be02741c41c4c76c50b6da4285fc988e59e75383289144ea9af0b1d9c92663ea9b0d893ec66252e6e35ea425b41c85d549f443bfe46182b62042dff3e4c2514f910eb09aeca464da7790b6204ece22da5b3f182d8cfe02ed29aa73e892dbb77995094cd73c73fdbf55aebb619e2489a37383ec0ea9d163113d2ff11f4931f2bf0c0f0958d342db7c26a7261c92b1995807514e713b00f17f8c9eec97099c6d6ea4037b4566aea1dde8131f8439ee0043310b9ed5c6a755aba6dd93965c8f6114cd05d6f2120a350f9a5f5c60dd377f34b3d58eac9bbf8b35b90a4debe57e63f6300afc69c2c33944eab12729699105bd5b51196516d4ee13a56d17492fd5c22d4e01ae04abc9410485f212bd49b67c06786b87bfe0fbb67b036a24fa5ed834fdb60a96635db443197b7af4689a1fd0ffa0fa47d70fadbc89355b1dd81c2f9fc1a209fb532472cbbb82fe907f06d46a6a7f98f3b92bd96f7b29665b47fc346b5f320adead593f192783d026c3aeca21ddd5572daac0e5864d18b6dd1fe2ef8b96510784364ca2da8ddc88264748e2dbc76cd6b01eb603186daeed26152974f8c836094257d9d6a79dab94509def28d9d128922a953e6d2f25a3b82340c853db75ab0bb82884085e8b8e72d76b571bb6c858e48e4dfa6868e85f66ab9f26028841060c9019d7e030533d95c59f436b7a5e186218c5206732051d9371ef1a03c5d31c019edaf969b584dc10d5739ba3f60ca6e786fa7b4e2dd5149e683571606026b20056b63b5d52409561c0958cd0dca20741ccfb5e705f0841b8fd24e64a4e1e4003dc0f1aeb0fea81888e9efe12e4798ff0718400371829e0cadb42100d22ddfa022018c9f1aa19a08a790729f21ff9271bf52b6decfbc5d666f089a717251b060183cf927135422a6b497068255fe64f14f10fd45be77b645df0b339f19e13270a5d3832cd807078ea7a22377c31d6df963ae54efd9c2bc9984136589630155a054f4cb94cede507c16f40d59eccdec638d75869edd18abb01def43eed1c5d030a6088832bab2c1e5b2c22c5ed18e3980bed377cbe7ad9142de7cc222166892a00cfa69290f82b66b80ffd91c6684cb249636ffc9e8332bff89664af8e1baecb099bc15d9e9c8823493942290bc58c46373b79df5c74e6b4b13a8d27b2fcd67804057520e1b21a59b0a768b3cd801871150f084473a2bcb34e1bdbd3351dedf902e66d6895ad5950532ece634bd92325e9d6c30d10131dec65a97c5ad804412a087b04045358bedcbe9cdfcf8b5693c1c0228098bbd6c82f0e31b0b67483e228004797fbcf8528a34765909587970e52bae7989ba3ce371e4a3d33879269d0ee07539c056bbc4d2aaab7c468dbb0e402b666f78f26a1a488d04ea2cc36a1c41a0e766631883ffc32f8ec339936cc58b47bcc1c69e491c1085021a21ee08be4be06f40066593a31bee976950954e711782a93247c3f29de201d9a9f8520830900079ef437550c1adffe751490c556023fc9039fa5ce5638c11778f0a8072057210e070c746a622c32b7e416bae91a3589da3e99298485410ee926bd348038aaa20cadefc4c953f3c760b6a0bb378ffc01c979d914025f13babdedb7facaa531f0025cf96d113c5664081ded1562fe03e9a9498aa80c36c7afcc4cfc597b563bda0fcd70c8e225a96139a62b58192091405d511532123dea3e048cb9c04d835db30050775c1f87c2f767d8bb87d5d3a911a1f9344ca041fa38f1cff3cb28cff03404a71e05b272cbbb94f37018114d567d7d53c4ac60e0ed41753bf32167fe2e440fada762b7416aa397de230566db98803ff952d049f9440caf5afc5561d0b32b02a2a798d2e0e1b9aff9472ae1cc997a7d56f589c7e26f8b8a3e11c894231a7e03199a5263d87778dfaf67d73a540ca7f674b32d0a3b6c96908ecc9a7734a55806b4a3ae1ebf8b59840b582cd3ec4d98319be4d93678625cbf9f3951e513c4b30b641e31040e4ced7434ce161689d86fd5b6c339175b999c77da530b6dd531cd072c69d251422986a5a31d2426b598e5263a4575c8f56a7dd3dacff1e2a82c850a97a5c72a416487e5d3613f6d2343071004bfefd9f0e2e40ccb3ad8c525691c00f42768e7bd89aec03e008ff06511a20dabd44c09c154bf14207a432a040cb601bd6cacc2f8c1a8c0a9f8ecd4a2fea1dd51cb78b256bac9504fbddbf622cb1202fcd8ef650fec6142b12f830d797d4ffd00b9a2bda88b74b3c64910842f4d680e637ccfdceeb2fa10a402c404b272f9b25f48665ac0260748a9323111c39799038d18b62d4b0f21da672fc7554c060dfecd67ef0a505f4d105fa6febc80f71705a980dbc760b0d6ca11e52f36c433b7285bd64a314d588ef0179201c33e119802947c17794f345d1317408757614575e5fc7639cda9c3f95599f0f5ab70bb390c640df57d20fff76eea8ff06318aaacbf97324a72834791a292fec4f0ecba6e0517f000f303b54f4744e840fe05cbea6bdead16973eae16a84cd1d35e82820507e93c5dd7da230ade3f04f6c9bb5a304a880574991b5219abf530f39f53f171022d5a48ac1f2d3e4543157b2aed1e938a81d50ca035ee7d3aad3c15b8bfbc2c037643a67f9bf2a0f4497e68899492697819e1113ea26d08f379708a46d891840e777d5c8a980b54aa5f82bc3452ccb8529da9b29b8c3521f84f812932b3534d05ba3ef2eadb47fbe6f65b963d9e5f01b3b3492618ecdceac59a77a167b3d5600c9a38912355b539563412fbb5fe76cf10ef58e434425282f6846960c79390e009ab330ff0a0aac640d9539756b2dcdd23f16d3d491c86408365fbcc0b545a85039f36f178089db40ebeb06c073b192ff98422b4fb8d13fdaa8c39a074b1741507586274af35ad94b40365bf789021994adba080f30088f09a043818ce732cf203c2c2a212fa75c87fc018ae40f8b47e04c7a95c58818f9b040ed6a00af9da9a02c39f68d81bfffc504013e66bc20d67b86d2c3c9fcffbe71e0ce7c16892534c03c6d58bfbcf5ed4477b7a9a8101a47af08856662d6f8ce2bee773b916c21bbf02c45088d2a6f27cd4149b4c1be4a5757f22e226a5684a21710af62a91eb063006d46a7d616663752997d74697fcf8ad8efc51bcd849711b489403277df0f76004b433c5e61b99d054ef6753cafa20f9e40bb287c6c6888b648d1a575e07378c07b2b24dea442643180527a3e63eb895f515d66e481f35a260149771d0033e060a04ba5050a3a149aaea8db33ec009f985c25939fd8c2314dc9b41a4bcc2f10a0bdcad429e31fa703ecc0302cf078181728187e65968e68083bb9207cb16296c0e3799b7522cfa0b23a07c930f1bf4407d0654269e6c2cc25f42b171d8321cca030afe929f009de241c07d6430c03ad7b38703502a7d6f5a229b047151813ada000d122971bab315c1122eed6acce702b8835079123d99d1d8ea3c690ec4ae2e0899ea22f84d55ad3c9528882263529ede124468b7def7a0cd2a8ab205c55e420cf900fec378edddcc97429be7246cd92a205931b215d644a61748f97d55d39d0d54c5108f053939d6e39c21422d330f996b6350962d0385be7d95d9eb6c6a300e2f97d619f91ceca0e76b11360e165b28cd3afe92adc3437141889846ffcc920c2800f6f7a8b918d3b17ae7cd5f0269431939995612f0743cc7ab8ef73b612709ae083158a631a24d5e8ce516a7467a957350f6f3e73af6eb53ae89713849be00a7e20c3a72771221ed4e951a18db629ba0e98fd3d590f9324806c3d9fade890d8ddd1eb8bf4e494711d080103c40abcde1b0a0848dba81afe2de8ebb9fed98091132dd806d9145d4cca0bac6f068286523cd45a057e912745365675604c918039d5f1eed60fe9fe5c98bab41ef647e260d54d9ba25c5f95afd4dc092d76b670ec68453803deedb3ec00547b26e9c820a5f7d3d382ae549473dbb553fa6a0510f963ce92b3e6e98a639f505495911fe80e6c718cfc6866099febce3757c16b60482a80d4c0d4e0cd1fcaa3a7e195e881b5243a234c52a013a60b180c72ef36808ec2b0977952cd63509280e1855011ba78c75c9b9b4a8911a82e3c36c9768a205771d9ac7b64268453249d3ead1e1d18dfeb65e3cd884f0d7b208dceb46d84f00bb8c9a6e1ebd87ec23b5774868eb1ba2352e33cf07e4482c02ac0cb12cc2de04bada50c9a85d3cb8ab66147e2b6bdf45f041572950e8ce9979a2d02f03383c0a35fdd43e0033a987c88a21bf80bff75e44bb2f928aca0a47adb2d6657263b9046e3d3e871c60a0f3938255e49d9528afe899e6ca1dbc23521e97d38c2771af07adcf78b60a244602cd0484427b0d71f7b3c0d61a05edbb0cf9ced3ebde7df60e2aee7a596f8611940312741d429f467431c863a0915e4265c27f05e080d22b0daea8d9ed851028aae1569bba24b7f392674f82ebeb3480811730e4763d81c70ff913aa19eb4c5e38e1dc27a4a67ae07d869339ca5d98fb270ba30b38378047016ddf02d533ef714e01489148acf624b9de942b47d92e0fd5cb59525bd2c38605aa0e8070c3ec4a5c6bf85b9ccd7e893602e6f68738c611a2e30a854f0352a2040fa05576921c9690ee5abff79b4b1470d8fb87997b8a25afa2db8a430dad45076d546c67081557e6680d0fa6999cd0a81fdc2f9b56daa44298d445a5d66bcd031fdde300885e0b4fb89dc189d939d6ab945634195e357d3d8f967ee22c3bb504026e13ef72285a717493a9f4a67dbd8541e3b04be2a995cf8edfe2a0f2161b014428e2f83067d4bf1a980a9a4535004b8dddaf29aec2ce06009b54598543a10552967e4dce9467dc2b9b349398ef7e4c0b837fa095d3dedd7a4f49839dd21106019798ea15d03aa201303a6505cceedfeb259ce005b81b00bba9c1d2cdd4710905fe636e27e212662c3cd7a1f1fbafb941f39975bc1f9e53c053df35d9b1e507383c3df79012747d17c2617efbc52e09ed85dd6b0441866d3fe5097173074307b1928f4475ab67f0759adbde21d7def192d4d071cccf274e39415223a7988e0209f6211e8f0ca98ec68c4b44560ede2279a7dc3c6a46ef7970585a9ebbc21a0e608045e3d8218da87add8a5de949327db5bae076bd9a1d7229bb6806ef9fd20fad5f8db19a6b0d00d7e04af54ff219ec67786bdaaf40f9ca7cda00bf794b2707b19e5e07bca95a0f8dfa23ac2a74980010f5e8747028724f6dfd86f70cbd6402c330084b78568b91d3bad446834d8830b192dc6fb8e308deb46b3da4eaacca0d7362b7bf23a47a3b1d628cbe4c10564a29f8204db72b9444bed5e5210aba3c08c01143346aa4c59c383838dc667f3a7c69dc17cc653d30e65a60307b65ce110bc20aa671ad1b1db04a481743ca90139efa38edbd819264185e483e937ea7e507a69e7bac142f4032077b12a45ff9800e331c00180493284bdd4e805e42c3d705e01bdb2d558f76b41d033359c57ea0299c52c88fd6ee29ac91ba5a85d5d344015bbc84f761062bcbd251aca9e052c0117afd75bedbc56eb0395c18082587a909c789f5b646b0cab18970a50e1e14555f24c6a654fcb8a69c9d6f24f78ddd4600a7c934bfa7ca05152976a0c4b567208c8974d12ab98aebf315613b1342ef730a9013a4c1076922f1494207ba02747b2971eafd5b9e1a0d8e63515a5ece9ac5079466d00c3dc1eae9e7554f3925919f6bed250b3a534633f49f21b8dfa0edbb06f65defd7dbb7e52ee063d214a46c39b1774e6c73c33a2ac9e6c0de78d0e2330a822b94883fc907a1708eaee62e7aefca257e504c6cf6c4828c7afd4ee1950907ef76f8fea2a9f8558b246e6fc5dcf74b270ab78d5e33bac759acf8f5c13a550ec43f8423994b1116e5116d8e8e11eddb1f7b57eb12003e32aa9567b157d0f00563fc8c4832f4ea2a2f2d7ea9fe7a4a83381819117473fcdd07bb24bef4c7f90d163d134335e3d72eda4dd649d233a7c0670f21dc19aa0fa7f35913d1fb9fd7018af4b453e6d8678033eab3f273518310f3af2543c47b384872e3d81770f38c0f81202680628dc2fefaa5c972117c4f7a5e8bca33f87bb780f6f2732fb4d20308c4e22187c46bd47cc0f7fdb3e603de6eda925790164b959e9840647e24c0920c5367b3d478198fe0838806acfc69c2b059fd358951a9dccd5b59c3604177ea05cf64c02c82c5c5db36257d6642ad2cf149b8cf6c627fec350a60182cdbf1340cb47a016517b4b84251c2b9a5dd0cac424871cf6a8e8dcb3ea95cab14cc9a680729246c38d592c2832e70f61e810186d659a14dc7b39469f3457bcef59b3eb00304646a8af9f9b66b170619d209f38669e93472c0324f49660822c4a098c47b02d6bd08f69937e685dc018413ce4a47bdc279b195eb33a1053b111e97f8e1be08cb9d7181e1db7b3bf5722175ce3fd577b8ef501e9951ef9002bf80ff9496a10c37681cf457a63993153af7eda71690ba732975ed7877fb810da0718b87584d0cf187d478411462a54acfc2a0128ebe443902837feb09dd8803f0812e3e39ff0295edaa15bd51e3694d5b9f32afade970874a9316d2a1f21f858ef4c93fca19096f0ffb46a6b21de12f61213eeb4e519acadebf8dc2de510609e6d54fdbd9c10975c7e13c21de354b6384985c4b02572199a68b8d29b9a0fd4fb6c692f41e16055951bf997ac26c583815848d1ca453ed2a60d6d1b71f6cb1e486e77a7c416a09cb2fe05d64ea0f89a6e3aea2fcac39ec83b37992656123c34dc33d63ca163e0319a024c492c843b28bb43f67e0acc09990759d13e294b01a9eb96b4d1c26ce0d83c33677d96f8792c7b06d24e17513c8380727255439e253ef2b4737b6e7ac0a9821f3c120796a5a50587a85b13ed97cb55ec6aead450874dc3d579a26fc280449b21ed81edf5da1dbb54c6e04ed1c0586d8db267e553df6e11b31d0aa227f029fed3ba12d72ad76174a462506286d82550a6ccaec666515180315ff6df22e0b994c85093761e5d4f1fe0cf58e36d384eb525aea4e4ae4c1647c2e4d3ee88402c24d7a6618b7653237e8f003a27452abc4e85f9faf45afa3c3c35dd6bde9cb08f84d52200f86f33f020f096d23741ad558a72166eab901a3c6ee208b61e4c105591a7c9acc82608bc951ed390e9dc810e18ad15e8bf015b8d94717e10a94ae09965fa4c86e42ce917e987fe49158d42de1e56d286d0e6ebf5e11440462c36003167d61c29c8482b449431948fbd0357b13ef9f3a6fac93b9fef299284e167a0e6a2dbd41167b8bd8f0872b77970dde7c1f0d49caed91653ea257630336dc160f1018f92f4481528156648ab68161eb8477a56d79cf5ca5b601c82b02667c260411628f25a84c79f0bd3b55e9ae77820a7fc4d72af903318bf997d677d02ff10cf84d2ce2c4bc0377a7a5a32b8274b67f1ee958f43e11ab51b1add08ca4c6640e652534a424527897eaa2060ed45c1437649b02cefdbf03bbe47fb499ac4bee0db8c03e6244c42cf8401dbc8711e38424b4a5bdb9a3ab43c521b0d0b0f187170d19c348c90060b7f882113ad10919ab0e2e411b73c5518c5366ad5e7f058baa08b1b2fa8258cf6360aa1fe0aeb263876ee855bd517247e993bf88287fe140690eeaa5a67f436a3e4e8f89d9610e0c5a49b6cb55010319b7345edb0f5e1cad3203aa75807a6def1a2730aaa756bb349fc8d7720bd0dbea74a645833c7263cb4f0fb372eaaa7a3debb6c9d259498df1e4b639917882c32112cb3f7f53277124a8026284c946950beee30fc1cd75b0b032f1945c47aad3e867abb1a71c572c9f770abb4c809a0e8ba4abe5cdbb2f81b5232bf5c2b9f2ff8091b12dbce871edf23902d760765e19d18fe032d18f515c9ffc04abf0d7fe7384281903273ed01b02150a1151f30b4c45cea5c5bbc3bbdd87f8699a8c1fdff0a38bacf6118c1b1c0404011fe5887e8ea3b514b5df8de751f3e2516fa0b833876c183f2b06a5a43f49930957ccce5942327187e6be4bd57076b8cd21f04e851864fa52f9c8d11dfd17ca06b17ac7bc59c1b5f52f85cbeeed121b95a28cda76f44aca834bae1c8bf02a3b0c061c18e8210ce0e04cc4d05dbdc60e127f908341b470e98fbe84625423b67d06c3154e454dbdb98e6a33001ef663b11c3657a464149bbc86e70e7f21555a65065c7e8abc473242e3c226322d90bb384d8d7c1101be27c0ad33db9422cef3180c869950caa9a85d7d2dfda96c984ff9127df68f310bd918090f64d465f5920103651766b4834c60ed5cd7a6f1cb5e8fd91eb8900e2c19484b9a47ae9a955750055dc59960f3868b37fa47baae1b8d9038d171551543e1568e853b4810f1587b0f6f04264aa5af5378364180e9ecb6aa72fe69bf38a63ba29810b1dfa83a76c9015a383f379def81cb0620106281f7e19b5e14e1ac553f61f54bb5a1c725a2820b897a0465572419d90d6286dd613202748e8b16470546276a55aa1fae3ae10a0b7650287645da12b497c46c8d78dc5092f8df82e2e5208bc058d9442d80615703d98f6d1b71aa9fd9e2490478dbb32182ff7a34ad7a0895bc3552863c49c19301192690d82487844dea0bde0a554a2e1d2d4e78190831fb7b4f4ba2583f1403066fbb814e4541977a945eebc3f9249fcc21039f946d07f67ca3eec6aa90927c0600021aaa0f6fafd1f76360ee55a4a81632c0432c1d50974b154e350651041805c757de5a295e6517e0b62797a57856418b538601302b2c65d07a6d9c89f3230b7231ca6e34930bd66d4746215c3c2839a424657a30a37a6c26ae61de4de5fe0afdea9355c2de2d59ee574eecf889f1469d2a440698a77db94338807e7ba8000604843bef1b637380bdbdddf4a3cef2bfcd9c75487ed87cb7066169002696100fa024ab5bb96c748b31b42c3db9c491c29ea9954d1ab516c66911c2659e13360a4ae73fd46a68cced5586cb8f13411b2121487b93ea43852332b80500ea387c0500fad715626d9a12838599ae4b63d983e035bfe590093a61a680faf23d963b0c44733f022cc515444ce799cc7f7b2aec61a0f81c4dd154889e2621db1a47530accd2139fb2f6c5f3032f2596ea78e4b9fb0a738dc7070d514bf056dce7d1ae00d88053b19f7dc84c74cbc034df4256f18c842284ed5b317d0519a97c88cfb70ebec16fdcc639ae32470b677d6ef3827ad740d418095be1de710e3a22d621d30f5a02d1a4adb18dcaac41813eded30e37b78fe4da64338dc21ba65bc5651bcd0fc5d65e3355b1ba614224e49477206da861e73b9e6dbc0ce45da725f86b69db0f693dcdef5217b6e3b6d35146bea75e4407efea4442ddc455fec854ad866a190e5612cd9ddaf7fac20864a3cef7d8d51964b409b07f4ad6bcab5d0639e4e423048ed5dcfa3ac34fd7bc8eb650061681f2ccd319f2c4ad136d86c8f446b552080b447bfaa50e8de8da17bec8b4f9e465ceedbe5d6deab840cee3447e6ef6adb7055ac744bd3535df74ab6d4e5b7fdeb4dad3f98e2f09c68742aa9edb90a4927106b6923221970eed23032797240f249a627c73a5acefb6c959387885361d44310519fefca0e574b83ca6301cabc054eb3938d12373bc5f28c61f58372e7deaf70f59afd5487672c4599d7c1646a6c2f917dd5f4668c8668b02dcc80d5a2867b10e381e348e208a7a5da02c63ac85202606c3cfe86ec3cef86f353c7dca2d9267041b06370682f529ce2a9c05dd78eba4e8629eb4742c094b29fdb9660d0c1e0c04407eae681d776a1c76cf5207cd6f90369b2bbadad3be2d26f439994de6e47d051f915b3fe66716dab1d7d88c846d20474e0e754e2d0d0fec4231ed650e11400d37b60ee57d93def48e812697a6dc19c0adfd5c7553d34a4ca72f68858af9d509aeba0e4214352dc708bccccf7615b3b6a2c6293a46bf6e923431ac554c68a90f596b004e2b706482f356f05292815f26059059814208745aa7432e710ae4d50cc5ac5fce926c692ed3e013bfe996ec244540efc7250af2af79987b4edd8fa4071cc757e9710c4389d22ea40c05a87deac18073b9740b53d88b32268a8f03d90c4e4af66764f77dd3142df76e325e1cdf690f981facaddbf9ec0072820f0bd20aa062ac0392de37f54b741ba6f4a8319420d8ba9ee0b4fc77faf59cddc063240b02182c3359059ff0dc673e3e968124faa608afb5ba5febe5f6a9b81240db730a5c83a36c086af4217b60d1e7249a8c01d749cde8d1f89601992e5131b072f805fa102e8a66cc8784a9bc9f41e8bc79478d91def4bb3f493bcf683d691c95370feec184ede3ac93d971db90bd2688a6e40ad5f41bb55ac8af6130b63be87c06067b4c2b8d0e0b24c1f8096f23c14364cca58bbd25dd9cb65f8f31144852870602487cbcd9f7b9a092af40bacdc89b1b3a55d16cc3c2bb704b50691cfa9926250777d1923d904a785ee493709bf337e119212baeb67cac563abb605cd848ae080c026dbb6e4b11d500afe87ed91743719912cdae593fb449f50c0fce4d619bf5041de0d54b8505dad45eb49e68cefabdbc96d029d4c6adebda96fb80bfb551b80fd68b657ed0b65432a7ff8d85cc8d533331878cacef9f230b3e01c2566c948f0491bb61c590a61c99d2aaeda98566c4c472fb25fc350db4a9e451ae780783a407ce80985f15e8be1bd06723cdd2428527d48e712a236816f777da04c9a080300387d79df50ea81db3fb9d2ae46e1f3ed445c7257acd62808d327aa93d14cd1b01 true +check_ring_signature 4ace2a9ca7758d5c3bf60e0ab0cf9463e9b3ef27b35abab80f0fbb2af9e6cc0b e692b1d74c682e2ac7ffb61b4d7fb9b00f52cd56cd9a1646edf2e9b8103d1fe7 2 c8482e705b80d96753934fdc8e10ecfaabf095996add98134a67fefd57703e97 3e0d92e82dd0a5c2fd737dba4bea14bca5e023b703b2f3c172c6ab1165e3f464 b43be81c75d3b3878b68eef68193b6314cd94c4389f099bf420525f030f53e03f2dea3e6614d6486a8d8aa8b1a0f85de020ec4206f5274ca41c34b31bd950c09476b51573a5ce2853fb1dc542f3d3a861379dc22902da0be187d6c1941be50086ad2cf9f1ee676d82e6c54c8c5fdf8e9917183c3ec7b8afeb313f6b2b76c1803 true +check_ring_signature 603fa8814368d922e1fd906c276cda783270c0886ec2db76b9b35dbc16f2e923 ce0f188ae317cc28cac7622ed0a92feb0ad6cb2c91865af6e44883b8eeb6a43f 2 19ecde0501a9083b8806ca010ebf8553b3ed6e70fbf414442a47d82c87a30341 ca95f64ff1ad61545530a03e83e133c0abf74269aaad548d035eb4649fbace7e ba76cd2a9eaaa7c500c9b44c55c2987e5725127d48efbff9928d8370aadc590464bc1b7bf56e248f9e666d54c84bfee69ece1b29b29a3886380fb007c6beb50c3e95f364f0edd0f598c519bc888700ef193c42e6374901a5807c34caa7faa509323ede3cc9557c3d3a307c234295693e046e915ab03e3116f5194e6cc3e5d701 false +check_ring_signature b40949efc0d6f55c9e1256c99bd6ef711cdf6890f1f94442ee72155384edf57f 5b9e30ad9b8a037f5435ad4a05d0faa5c3f4eabb2ff44eaf64a08b7a388cb9f3 11 9a79ecdd055f46b2d678eb8be85de05d3926c3d0cfd6581a1292e9d063cab1ae fb66fda823eb0f6e2c0d541bff3845b96a7b21b8718fc71885822479cb2c67d7 8f277f87660b6a80da642307cbec03c1a73926874a25865ab9fd2492df512931 414b1b49c8a8afc6ca5d8ce166f1e59fbb0a409e4652c3c56b830b435c346349 1b3bd6bc82047fe41ad6faf79755b4732a02786531312e6465d6b2f2ea6a4deb 8d5b7e0d2a02a5a3003e667f2cf63f092b9aecde33233fed8f218a3c4b7658b5 bd45428c3cf2160426176da9c037b75984410ee933c1a170ac016f63f1d0b8e2 a7a8651318d8cd2512babf853a34e5d8b9581e01362554106f27fa9f2a168320 5e6df1ef47d0a38be78ac8ae139928b06d4c9d0fbc5d61014774bff47a0a5ff5 560cdb32c614bca4c18021a070f3179b6e3a124466c01c8c052a838a753907e7 b9f27f2805a783f47a514f4f4c07371dd7e428f8fcedd89176c77a904e82e187 d50cea455e38f91b217d3985724ce4e8ffc7d22a983cabfabb1e49817130c90563c782576fbea9383d17d5ec534b54943f09754e8bd35e895a346345bc9c610dea376a86c6064ebd71982dd8894be6e5b98195b367494fc1a1cd6454b419200353ddbed34130cd3e5d49e00ceda41e570e6d04aae1edd05c0a0b78d836e86b0461028d1d5c4942ba5c6343678e650f3fe58ca51725665488d7b23afc4a538c0071e81d86d75045e56fe395350a133632861256cffd1e217129c84d68d29ca3056939c4fc1719b26776613c482d801032e79a1abad2353d55aa9a7b55e7f1cf0815b21405c822c19b8384b62edf2ec25048af6b567ef51a9252fc6c0a29b3ca5606336f41d809471a42b38417f594ee996b030eef21711ae4d62d0ad406e8400055e01a85046d74ec589698f47b08da8ded68bf9e7e497d37cb7b63edebbbf1078f3e90babcba9bd831441bd24a614aa311a654ce825a44076873bab51f51340b786c92b79d20ac10e372dd4066228a43fc5dbbb25593ed405cb5454ce828400d1828fe374172edd7cbe169e3a66205178ca2c3a4491c959d33b346fb60a5db000bf43186e9a8887ece67604fcb3caed79513a2c7b7b610a61ad5dba9483bac0e74260f6a8a4eed8b1444bbc0732002af83c3df9432b63b7853e011003b5d2209036a752fd5e162d03d0e33769492c87237a3ce662b371d53200b01422869b30499f81bcffa154a368f1e82f11c3ba7855243d4bcc1687661414e6a3fa0c3c50073d27990948bb1310d6e23069eee53c2c04f218a37642ef792f4b6fb0deeef0b6446165387035119b7be54f5b4b924dcc338c68c17bd2d7cc5023ddce7542a0807f6917709dba5881a8f242fa086637ba5939dd33cc66e691ae94630d0fd800b734ee593244ee45a021a8a73fddabb44120326fc824afdba56d594c7b14ba40174e1b028991b62e0fc6f3c193ed9c7ed068a8de415c0649e5088bfffd1715506 false +check_ring_signature 5de158f5151346caa546a7ab493000e85df7a44cd9fe84c9d29b0a8b67535dbe 90adc1faa37f985f0fd01f63a575d9ea6bc933a3974a5e0d67c652ee29f83a85 9 cac1b2661c7e9f01899d37b873a5b935b623efe10c7e8bfee8065cc651aca3a7 b5f900dd5f4c27964a8e5a95b43d7cedfd2b3eab80ef6f104de005d9d184510c 993882e9c116f0d2ef0d7b1c451c86882555e7399264b6be560554f505c11416 ceda9cbb7e8c0850de89b947e67e328a5886a177b5399fa4e3213f3b432f71cd 048c5acf43c3266acf9252f352aadcdfc93040b218c8afcc876bf597d5880903 17124af4e63c8037e686133feb9f50515bc294f33e54884b8a502ca981c64218 f56aaa0865e69fc435fe3cceb1496e5e0b3599900950e520b5eee3c842510aae dad81e52a25cef3017a0b6c0b6d6df9e21bc0257a4f8e965ea8124765ea6cd43 3055fde7ce3aefda662b236fa61d5bf09c1eb0c236027aa24a5d8324f18f0bd5 c093bafc38a63daf7f3d2d8f07b18f808b801ea66f752b3edb3c2ce0f3cad80001dc05b345e2868593bbe1e2efa17cfaf43317d71e3976eba9fad61f025cfb0877176d63922e2c7286ff4ca9414a473ece42f594369cc1507fdd354478474c00ba4374ab060075c0fa7415be230028b5c84c783f6ef97f1ece3d7ac71b96470805a52ee44d15b5753e5e6a9c3f07c210312a86fff0410cbe17f9747b22ff9f00185ff4aa9e7e47238055532b4254e7df2f91a12e75cef66869392572c8d3b20f56d35f7a323ff49115ef2728b56d043f4f4fe8c42b80a403e6aebacaf2c7a0065e25e6635ad47487593c70b9ef88051f82412ada6bb6188819b577c1bc731a0f6f45bf06e0bb3d6c78550eeac68f13107b0dfe8181f9e25e0cf1a9eedc61380269d33e88e8ac98eacb26286ffaf074ef23866eca9f93eccab7dedfaec77bee05f3d24b06449e0b943c660fcde8e7b8782e2e5c4576789196b17bf342cc0123060e7e92c9e7f9990b0c8a8bd1912520d72622b4a83be475899d1df5f32aff1c0917fb4fb90d064403529bc6001aba5f8f295003c71ae3aab76d05044dd1d276027c4a3513a03ac92e04b0f133cc1f00eda9b5375cae06656ddfb4af870cb25901f3e12d5987a5b784bfd2c2526b37f873682cdcceb5a859bd75a35dd66d6691080db6eae57996665cf7ed528bc907297c52883584d007231fcda512e663c954014bcf91725ee7c9640035abc8f6740932ef524d53517c9133f58cc1c59026d30589d83966e33a07cd22a259d9d620fc66833bdc3c732c8cb8acd1b8eb56e74105 true +check_ring_signature b32662f728dc3f981173659fd62e6c1f4c913ff379120ebab76f36aa3e8340ca f4b4ffaae93cc18240c697ed122863a25660498f60d8a8ba7e4bf73fc68b8d19 36 b655f35e2507b9f4f038bd16f12806172c8a89f45ca577abb42045251907bacd 6e2f02fd77399e24ea364aab8cae9ab237e150ed09b485bb49ed729cc0810e1f e19712fa00e5e8d4a5e8e0971d292e505493de9829c9dcbade39c289b7a9c431 9b874db3bf1f99240dbc35ab5498851bd22c9c8933019d9df97d9b410539e8b0 08b265ef50a938b849eb6bd6811dbaca07e96a24221ce5c049eee64a77c5d425 867ee3db9303a770b7a3b546a585fb4fbf669a780681f3e1d30d47e6ba930c82 a5265d6d1f6df526e19e28f9a27a7cadefbe322da34f3df24a3086f77cc5282d 3e9a8820a251f8b94f40b072a7c7584f1f6a64312140dd75d72c721347ab65a8 1b677c8e80f15f5ffae43209c31e2f430b31b08a78746b68f96e26695500d431 891db5b5af90a6a57a99c14ad2509cf42a10b0bb2335a67a60867bf634d63654 c699c04c25538880f014fbcd9beb0435b0eec693cb9260884d5ec32d45011908 d2e36eda769d10157c3a1af09929d2a57f5767ef7b9b5feed1233e4bc192a343 582179229ffc1a562c73974ee107a8b27c3d53b73001d0a5dd3625b4160ede72 4f0ae2c775b261fc19791b4d3bca90c8fee576f8059d5857e752f26ec9a46d83 7b794cf701b0547b21602d149c9b97ec3814516d31a2eaa046ea7c95eb0723a9 da5322c4b76997db9029ffe61366b442aef2120188b975a405d4d4b71003c1b3 be69586f832c1446974b07d7e54d684591e8222a45a1282417143379a9f7833a 6ca018a27d4f369fcdc6f5de3b17424da56628edcf4857250789fc140df7c12c 8984125a3b49ac9c93f4b5c776aba44005b650953bcb7fa3ffa0080a0209a68c 0f36d19bf836d74d821893ba66e22a5ca1a771c9c1aeca1a0f041ba911f929d0 610d4ea39a176b307d707e2ea017950890e1e0249400882dd75d5b628987abbe a63551b110ab01517cccaf69286d5cb369cea74d2f228a63c2a3441549f594bb ca6096a738274e7051b05fce2c34816f87a74febbcd8c4ba3004f9cf7999c913 f5b7050b582d8d867a9fb303e5ed200c6c864457b7d122c0a1a00f1b6d3e3319 656f2bcd834b23d51cb89040e5553a0dd0714bde4b54f128b144bc028ef5cab4 ad8dc7b5bedb9aec105dd2cbfc49d9b4a9fd0bba3f5f9b287d758b9e7c64e068 9a11fca0ad805aab4edbe25736d7ab7749b7ae1afdd537f5f53e23315fe907e8 d1a086fe68c6ca44bacba8607fae68cd45537fa8e5228f62aebc1ffcdf059033 02f334eebc434d4420ff556115eab22515150e8878bab295f9a04b0556f24e2d eaa2767d0ff9cfd3d4e9f338296137954e88da473fc4b4eee506ee0d074b2ba4 064fb3454d1b4c436da50e3baf4d4e054886138eb7710c29e2cb820b6837f117 cacd2827f69a4c3324df3eba0c4a35eb9e28a2ed6a8b2707841836f9a09b0784 2dec6424aff83da36168df850354d7b50b47067fd6af07ca52d39d7f32271376 e34e8d4ea96392633f8da73753e3af450aeac6de778ec52b6e0f4f7fcf56a4fa cb81f5f4031ed5e0d4b1d414183495e0b6d9c340e5b4135b95c8727bc05b76f3 4dbfce36a753f90caf3c31a5a9fc68cd551e379688b8411bf6731be581b17af3 1a9398f9c3d9f5cc2be6979ff2ab61bbb7e50dfb3dcbc83a0c48bc7eb1529206a6a9e1618a2544c54c5f56e3d7021eca508be4ef48325fcfaed725f4f135cc090faeb965a7caf72f3dedca1d2862472ac690fef78a7d1cd776906a67e6d82e0a478e164be7da0418c65efd367a8b2324ab0deef445ff42046c8df8989f52ad0fc85ba4bfb897b11fb20db8e360cc3a6a2b581bd92f56424bf735393e85421b032c4f988be996d681a0530d56fdac2ea9485bb71adb6ab0b4122edaba9a1adb090e49f6ce857837fec61485023f28048fba247d81045449254b4b68f2ccb61a00d423a548cf6ba5727f21e6a631d4ed6dd63937bc8a25da5a3214314436f8a809f71b94052d9272c35ef3daf013ed8319d9462fe43dedaee31d8c91bd2b9a250b4b45f4d15c587acba68111b0dcf2fb0d2fef0b7c7ce0fba8d88dd2954fdb27077a771dd8fe03e8dc8f34eeba68e7934c0e328598db044aee5ee303a68c96a007f9c745ac47d1e45f5d04602ad06625500aa5c237e25579f21366c51eaa6644078f1c713f8c3d267dc2b73b02f242558306ddb63f3c7e0b25fa8169e1fe6f1708f26c462c290220905772aebb6cdfd103c6c9b7cdf0407111ac40c7e9a94f620596f3fcca64cb8bcccce72be9db40b4d7360eb1171b01622baf78a218d68a4a094e6eb97259823816f1de92b857738104f378ac8c41be9c79ac33363f722b2b012f606f0966510b7100d30d8042e6aec7da6f3dcfa3834dba63fcce992584420bc8a90f8136e17ea0520d07cb5466774f34377641b03bcba2a2ec0fac2ecc9f0a05170eb7beedbe5d2740fc8e65eec2f0b39f61f864095642e275b7f745e10607172c55a3ab1b91cb1e95dee179104845b5c07eb3bc088c64916176bd5694940b1bcbecacf49cbf982f5ac185de333a00c77641a0e370601057966c64bddb54081b7e2c25fb03c4c45184c8a7ad150845a36cbed42b09260d4f49c53799f5440d19c602071560c7018903fa219279dde520b8a281471cf8843bb61c6be6f4c30e58ae6aa6e7c7ec71496f76daafa12ad243cfb17b91e500220b8669a7fc9ddd00fc0d168f262a8e233fc6e2edbcc16bc8c24385e334b8f3c2575a63712165760876b7a00231c396a77f99c71314f406eb038a2688cc27aeb087d378ce30c9960d9481baca724005d42d801968d85310cf5ab338a76caa4ce0cf889393e125a40a6e3c012afd5aac551fdcf3f5f4f7f896f71093489eb5548e868bb18af3e3ef04bc72e5e6162ffb3b18c97d92bd29d2960bcfe5c2f091d5bf71ae9a742d055b046c952ecb223c74c47381f7c6d2721954935f181eab8813cd4f9be7d0faf56a0551042ae96a379b927ce8f9d0fb1249c9003f800d35cfae031ce0ec6dfbb5cc0d702ebb148d51638ca0b0237769979bca274dd1648bab405d8f1ccbe43142fc020c2b3336587a1a0d9498a9a017495bc4505ef71b038e39d486926140bc60e508729c1c5d5251b3203cc6eab6b8e08267417a6bc0acbe22cd09857e7f6cba3500b7c9e4cc094e27a8057a4926f1d2c43a1237ffee54ef2564d0e384cd784cf704cb2b11aec2686c0300b8ce83ccf893047795cd577bcdb4e9bc1b1204300cbb0f2aebf9382f53b696c8d74d59249dfe39d773f10b700025d060ebfe4d5db9b30379381c84cb0bce7f90b30c33ed465d8b70de1144bee4326af9b1d0193fd1660b5387ebafaa29b77d52e52773594b40f188ea06e9406597437ceb3ca761e09a043e9d116440c4bb6c0273241f01df17d661f0f061760cc7f2be76cb59b8e3ef0ca26465be94c43f0b5edc0e4f0ee46ac6e6cc8f895ef5761953800ed4fdb1550a99bb874dd83f97c3316863edab5fb52dba3f188f1e83c53de9c693d533439802e2e78a2cad049407e4266e2201082c4bb6128b24c97a0398df1ad132f5860702b29f50161d5d5170eef3775324c57d31a590e760c9bf6e6536072208386a6d0462e9616103bb8c93eec019a9ebebf421c4ca88a2f2120f709c8fde1e9094620c4ab85a5c30d0d26d5e2cc07e05ed5cf0dcd32b8aed0c73895880f7a50cdf0e007c9d54d98059856138cb907db93c4a7bc19da184a66f31f270ff3d704a13c5018f4e805ed40b00028056431cf0e33fd6c611866da7028ba7620ef1c959abb505db2f620fd97d2c5fc5a292c0000d7e6e223d9af6e8c08b2a87cb4fe37f3e620dc23360339e850ad9ab274ce60e83d8bd43271ff8b347fd3d10ec4c5d8c6fad01db28668d55735b3596470076013f6fbc55f0ff15432f7a5c409d2424d8be6c095f2db32fe83009602b6344b4165f2a4a3679b8a9bee21882918cdeba08602b067da1c18cbc1c1e9f2aad1a8e1e18eb3650c0b9cf52f08048d76078c5e3c2f00a74d4b6202872ea5735357f93e8a006d9d99cd3f4d5a4461b5e76242b95b0da0a3e4b914e5b9a46afee0bd76954a2180a28e1bac40019e86cdf52aaa77e53d90187cbf75955a255c0878881a6f78044bc163ce39bc42956d20bb853fb3f1b9905c1e8b51b859efb1a6faec4e41692e472a550ff5c2dbf583586567b688caf410d9a04fb5e08730d3dbbc9a78d701c8195e11e7b7868aeea6882a3db1090a1c604c2700aeb19b0a064b9950d30a1a0d7c6e8dfba994c74b9503e14b9855ea6870e4a93d0bc9d06d2e47bc29173d6ef2a78b95f5bf52cdca163234d48849f20740b00b62edc6c510d08ca8dcf5e571ef00308c6a844face61db018199e1b40057071fc5d013273bb7cff3e9ef0f814ed245d1abfe4604ba54b93d0ce60863598d0bd08afe5e406b10bcfc11615529b0d97e12f62feb714f5265355dba5aa6afff021b6d5f6a0be082441e3d094dbbfc249feb9c0718f7ca34fb16668279072fb801ba94ddc5f662465124df8a4ac9c89cd518d470e4535e16e3dffd8ea5c770f90d0dbdaf78efb251e649e3248464f74f2790febee70bae44234aa31aa415732e02f90f36497d1a08e0f80943b68f5eb7c9fe9a10c969848a64072337013a649301e0903e409d29f5e17ffdbc06336c5b668b54c371ad05ddd1b23353f868448c09cefc58c685b15f4b25a8ffd5b291376f2ac0766c1791e0f2ceef4199ce0a2f091f707a60c4b4dc6ca7f06c8f5b5de4e6e58793c96a682e3da437de79dad0bb05b3016995d2e02e5e31d6a10cadf1be2d098214e370a8b55b45f03f41be4fa504bee67fc7a7c717cd9c09a7905ad10cf4f53510c66d504e3b36f1a8c8bb6cb203 false +check_ring_signature 223059cd7000ad9908e86aec9840c5ffd388d0b7cd330036569309474f48a63c 4d91f6a132fd82d84e02ad0a49caaab31db938f3d437edbf6dfe43704d493fd0 5 0287afa6920da8c0bc5958d959ea2e7ecd68a7215b67918dfdbce8b10f996420 92886f70ade1ba7b7526544aadfe6ffa6a39b4542cd4a391d62eeb5e6b5f7b92 713fe1d1bc87f132bec496958b4328cff4569e5aaca93cea6212aef427284103 66ad31cac48c07ee1c021c8b54ed24da79b42be196f96dd12987ed3f41653556 73ae329892cd09d36b35ea87eb34b4097fbfc9a8bd2032fc1a43666907ceadbb 8e802bdeea07018f274d4e2f40717533d756ce2e7966429d129b084f8f4c4502b25654388235edb7b4de754f2016aabfe7748bb16fda5d3c5d1f80132b6eab04780bea2663efb2d4fe953ed657e59d4e9d9f5f3ff8a2fdc85e91ac07e1ee6908b55244b416e050798ca84186bca1b21467b991eee93b518bf5f4825e0a1cc405d981f84afa9eb0fed904f35514d741be9342ae715d22614e562053bb370c4e022e562a9a807ed4b769471401805b8ca4c9b8778fc8e3cdf1f36f1e5954f0a702c50f6ce8b7a8a77b9ff00229814b9a6b9ec8ab734f9b95fb6895e1fa5ceccd03ffe33800cf9f2608ca3a6295d9586206039c1e88db11028da492e920cc9f2809328d2cde20032973fbb3caae275c1c8914229a409bcfa9363db73e0a33b9710dd18a5876c1bb8ba224c6ba85ac6b57531407400c289a21b64a1dfdac247a190b false +check_ring_signature 873552cda5abc158d5fa3d2604035a8f93dae5c30e8e0c4f5795f613d6c66fee b2a4a82f7528f1d3a2530aaf63d268e236a6587d4528557adba8e41cd801e91f 2 76edf08b52d56ad4947041b4242c627b82db30369c9adbd44863d0e582403402 f9f51a3fb8349b4571aed965177daf2fa0cab8f91b677e7c39b548d134ee60e5 9697549793e62bec037abff969a8485d95c048e653cc497759043efc503f9c0367790a1af91f2b2ae0e513913a1c9d5af4514da3fd461d04248eb1f20dde2009aefc224fa82e9a0703b1dafbbcc1d50b4172f1d1a0f20ecd327f51f638abaa6f4e9d08aa8252f39985beaa81e1d75cf6d799a3ff6623eaf4bb4e483882722f09 false +check_ring_signature dc3a7141e40d9a976de64ca22d8faa1e81f268220bbd4cb17d38966e97eb6d18 fa4721d82e50eaa307d994f77891148a87e54dfb6dab7b9d4521289156414f98 4 e54a63748e6f90f368712fd62d7ef6899c4c9a09ec513f4839a6441358e6ea29 601e8c3fdc95ad412a7700e76495c57d498fef73dc941c3c43cb823a79516f2a ea8ec1589276795ec64f0f4378ace3b147f533b64e6164fa0ce6b82ff040173f fcbc9d959f6f58f91e6fe1bde5edd4c0c018768a27abace02f72d223a4a23c74 75e4a0a65f9e6f2b8ca54f5eb3bb00158f77199a52941afbc3c892e79588ed030e00fcd89a426556a971ba93f9facdc5eb947bd419753e3224ead815387f1d07c1e38f18db2cf9c14026b1a0784b38ab3ebda663ff8321ef62533b4537dc8e00529ecb035deb020b2781e174291d5365d6ffbd3eb16424c7b840c0c63d51f4089f3f98230296dabba9f531d92e02881a175f9e2576c10ca339a12bec36da1d0700fb9a9aa01dd936f8caeb6fd48739f66e5707a50525dfeb72b96bf53966140c6999578f882152fb82552b1f43e3256d3c09332232c12f40169201f65e2c500867c4b8d92c715841b1272d1569c14ec78f225ca7c4d9e0754ac660e1d25be102 false +check_ring_signature d69cb61c9d8d06059afb90c6681ffa6b1bcb50c711157926465161a1d4d7a984 7ccc740e38ecaea2c0c9cdc048dd2458da78cd750c2e495d3451ca8719afd7fb 1 034af4b11e2e2405ff46c567594d1d37f0e03f91bd899a28e98e17ae1b0d7c89 6fea53f36bed3a1b66d4ab5f98182b9506ed001e88bccd2e6dc022b90a920b0cc8eb074f151f9543ec48d913219098036bf7bd5d30d625e1d1c934bfbf324900 true +check_ring_signature 38ac43175bcda9bda786412b396b5042f4afd4af69d3c887e35a9ccd3c1a125f 351b52f7c28ebdeeb22f81ab0351f5c0211899f2d85103596fc8608ad0f521b7 6 9e06a256a7fd2b1ec1c9e79d884cbd5e7e75a5611623239766052e9d797db36a 3fcb1280de5f1b2d4d7b43fdcbbacad4e0585f9b9273f73ec45b46bbac3c17e0 781b3bfca11f7c15527b06baf19e80bb01ef612a2269c2d485889c1227371187 c1ec6d3dd0b60601cf4a48ba63fd1c1288d22af9fd96faf000dc069a2bbd4b3b c597fa23a72baf2be36cdfb1833b6f076a0d326e2a81b1cecbb2e08748f9f40f 87c4d336728197a5726653e718dfbc6c232c79f02499e5baf95db7b9e70324c3 aba6d6b4d0571122a74e266413798c384c059595f1ace234696b6503f576aa03891dc0bbc4fe8118ac8a26335cbd868cdde5b206dcfebe2dcdb899c6952b6e09ef2f5d2a0835684d40777b7a0720b3d026f9a75c4f9868329be4ac63fc18ec09c3705e3698762a37dc0376f563c157610ebbb8e23eed8c20b8d6fd9206a48602bbb1e72b6151673bdd2089645c6e22d44b386295481e611103bbd2a4c20eca007bc617d9708c93825686700f2aa9b0df1c2190e6791441c8c62a605f3180490cb5ee83f2584c8c5c0b048c484aab2391add3fef8c65a4acfb651212b67d4ac069b685490da8a84d16d5c033a6dcb1786de60dd0f42504fe247ec7ed23cbbfa0022a07643a1db60a51d721f603197b28b7e225f9c2cf0a8f5c174647f3953fd02a666ce9ffc22e9a1236d0b586598fa9b0376b5cfbf77d667f92298b11afecc04331a01cd557a1370fc558e883d8e19247384fe9ab02e59b14683523da240bd062c353ccb6b8dc2272872bab2ec53fccff8c485ffe2b50589879a3b80b9b46c6f false +check_ring_signature d7c12653fe80bfc6f748356910d0145c9677503b14032faa9741a2fa743cdacf 782c152b63f914573cbb550b946c5bf077be30d8976bb2d8d8f809167560aba1 83 95e58c5e187585941840f37411da782fe513584ccb2ebf3b14eec3e5087a33e6 83cd6331ed69e8a404c25353350629b4fb04f245354130483879f10648447912 945352ad5cb60b04bf08a6b5e2513467d1fd4908f2bb34f01a71bf2a73b3f677 26310bec512a95b9eee059d811921255ce6e4680e09ce8e3723fa1e9c6f0f5cc 939d0bd6df000ab819fe9ee49e7a970b762bf723e61c9233dbd5953b44e85224 6d5159e9b3ca3990d63d3a967fbf69d49fd03d48bb4b709c26f8ad2f1f20f5ef 289cd6e10cac196bcb8aca11c171711ca536d203e8f7928215e36a8246b0a28e a73095f36f8b2f63764a674b9e91f7a1b1085f47775f6e1df1ac74ce0f2fe894 109ec3bf07e6c35bb4032afe0058a565de477ba6665b342eb044709448d9f0b4 576deef80de125d00cba9e2f21c5de982fb5d7e6e960efb225362179115d96ba 8feacab979fdd013cc78b8be4bf0360fc752b612ffd983537640ab31f9fd4b5d e42d03602468b7d80f08e9485daff427d27624b96e784aca83b3109707ad3efb 6daa35d44cbf41864aa38b33e23abfb7a9ba0e5f1a95b99e7f3b331f9b545a60 b88fd92d00fa387f8a01db19718c5cd8dde0ed15752fda20506f78aa62fb3557 2481feb91642892be68015c61f60b2bbb429c089c91c96c5abd0502ee4166aac 19cd47935ea7281ad782f7605c1feb5f7f3f2a3014226311ae64f0313f9c5e30 75c2f067a7df0c26a211101a945ed2966a90d0cfe1bebfcba4e7360db4187d21 7bb7f21107aea9f4a0269e66e55f6296d4baf13fed3d1c8415601fcd937ca75f c5f81d2e87a98c09967c42b424eaa08afe74124c413da9ff9805fe0cfef8ed4d 3fc6d3c8ca699e2dc884a2a800bd063cfc7cc64112dd88f01f3deda3eab16c3c f6b54062447f6507290a1e2c13c93b42a578eb41054b0976d861fbeaac52d1cd 15fb5520384aadd2fceca60941caab2a5a18dfa5b23d08910b7f42728f5fc24a 5d3f8f219c38325c12aa3d919b994e74ca44acbd7362ef7be56dfca7f3db8190 ec531561e360bce928fbb725f7f0f23587767a3b9b48d8df2ee79544b88cb766 4574ad416cb40dbb7a56227f5c567fa99455b096fa25f493187dd3252da12b18 0ec315261741862632043e41f5a8fe8b7f487b6bcf5f2405c0c6a0c7e0426e8a 524f8b5e8dd4fa6701c7951461b399c0b53ebb02c437bf00f69fd7cc06ac634a 13f17f1c2721e127b107a8059c4395d64bf66d25b858e12aa058ba98d5336855 955bd5dcb31779f07986d00f3cf27136963f69bf831a6bd4334652925688a3cd 94de7ba8a070f5483a6ccb0c35a5103167a42b3fa7622664f36642e98eed07b4 3cabaa6f4a8c5526ed83affe81e53d2d884bb12c53f5e3e557c1a2cade4e9e90 bd674239a34510fbed8e55fbe76e4d97944c5d3fabe1f170d199d7523f621ab2 da9155a92031f2a48c2d2a28b9e90d7f82f27362a0b5aa2de1793f2860f83d17 493ee54ab14e75435b82e9d18f1486d539047401d0fe99fcb2fea78111467236 6c38f40198d25db03c0aa527cba58e03644355a9ad27b5215e28713ded4292df 3506410eefc070bb6d906deb0fa8eda8cec5f685bc63ec9270d87f3417246d2b 05030fdb6cc34999bad8ba9ef111a703175b2e6f3b742b6e57b90f21482f27fa 6079b1204bc2c7de5c59b5b7e4826d7e1ba5e6985bd0468f32d8f864c3dd1152 ea7eee3247a1804bffbdcc002d056e89f56914937215cd0d5afceeb20912e3fd 77d935bcf1858c592a643a1f48083c415d2213998057fbfab5c269a9c8207ed3 bb782e69fd79bdda4aae92c57405f7a8a925ba20618bff844bb390ca531ac1fc 52904455e50672cf45a7f1fb6987537cfa978fb8d9bc03989dafa500c9ccae42 f3067f67e157d0aa50d89e289dce2b9716d343a9392a6cf29148c1f2e6e283a4 eb8ec4593a444b8dea72fcfb69c35269ac49b17dc0c3ff4c33815c793a3d3431 d7036112f3536500745f20ea706ab699ef415ad4b312d81bd25bbcfc4ac39684 467c340c7bf407c5db208dbb1b95439e19b10df420e091b1e440214033f4f2a8 c1cfee4bfd88590e9b718709ece4bdda86b244cd6f2e23e5f6ae617d77d566de 7f9e7d3beb6690d14e1e882232c64a5b02b442385a50da61831b81978dde2bc1 517f8953d110e6608260e5b903cd91e008bcab8aa29a14d39e1ef79e0409b382 3ee91a4af656c3bb8062c52992422a5f6713f77b35d2d31664c6bbbd64347fa8 0c482ca048987317d50c5e06a9e7659ccdc2a16e09476989dff5febf87ecc046 b65700f382fb6d48e7ce14792278d1b238e99ad96876f52afc5bff02d47dad59 4ce117507377226763e56ddd6a9699b2f3b83301e76fbb7ec6449963529a156b d814e87335c807058feb7ff455c1ee91eff64955457a2c66a3e792bd1d4cd54d 7a43ba1ed37e8daeab8120bbd0e8ed45424cfe9b0c2f35b2b16a53b25ded2f10 4707d8083a7b08f7624b46d00407d8c7bcbb6ee0a91371a1e16bdd5bff6388d5 47676c2faa51150cd924c6712a7a7dc56548c5ef96b4d4772c66e686ef1fa6f1 74af6f7d939e57c91e8dc42b4c1d9a934c5054e873766580417caebbbea4ce0d d293c3e7c0307d14dfa0f763d09fa42b0b772b1660c265c96679da438936fb05 fe1a2b1c6ba7bcccf4ccc5f2c2e82dac204c8374e67dabb317e87b03049395e9 6d9a6502e4f96679d2b57c517c86b27aa5a258281e779c1693239bed2b5cd08b 568299b86239e8eb70aa83a0c309da5b23e28d07a5d628c4f8c2ff36315d89eb cde07a649aa433bff8b1ee01b8cdebcae2273d0244fb3495ff2ec537b121c854 e373791537ac79a6053db58a5babad260692a835d200f445a6fa509ca872a545 fc968a6e4a3f17e53910e767eace1641f432e1ca65db32f2083fc82e9f1db84e 2eb947faa171a8ac310f0137ccacaa44645aecd53fa4e806f1c8e7e6dcff5e9d f449be8831d960342b2ae3e3f5497dcc93e815e0b74ef50ca59a1485c1148cd7 94df4e3fcd50a9365994d7e63cb6c6a189454b13ac2bab24f2ee4f8d80daebde 0c5b90a686235f2e591a8b3ecd3b2a9e330f509b3d76ebe180ca637a347ecd66 93f09809aa059841c1a86b600b018802b7daf5f50bc0acf52a11fb8a9780b372 5b40734970eb78acf87639ed80ef1089b3d59e5968dc34e5a8501f7a98e785d6 2588bcd0a7e2d8900c79954817befc9442a4342efdcb9563e7b19599dc7edddd 990938fa5563749e35ca2b3b0c679b4d5a3f85b6ffc5e5c7307d34fb74e18876 1171d847f5bfa1313af182743a156cb3e4d47b1244fe444ca01842308b7e353d c78ada34e9bb68b6cf7ab34cb920cbfaea92b012494e34c0b044c842226b983c 1eaf58b8e645b6152178769116c2be866c58e56de02b84790ee21c096893fc92 78b4c5f1851308b6fd42aa9a1c97527df863f2635b8d17257454832af721f6bc e741ca285e67a7c01368bf6dc3b371e2a4a0257458d68161f7f467faf6e029d9 ecd42c2fd2e9489c7f20115c604e8141b14b00c596e61bd32db061a88d39038b f43b9cc8e468dad888417960b712278911912b17a0c139a482eb87c468c88ede 4998298f962b317e72aa30f13842cc98e28750a8b837a7cc8e15de2e80056100 460cad533fd818414a8de9a76d8f5d5129d0cf5bfffef135678e27579b2e9116 1bb8872fa184b55e7103777da95311212efb76a1ccd1e1dfbf81e4f7b64afef8 71c68d216c36a8106ac828ec1ea0b83d75bcda058d55db8c85a40fdbd9bc03043ea6f695a46826ef21a44f525aa3da80c432233e8ac902d35a9ba1b44147200216288068d188cf3b75f1e8fa2ade810e683b9edb0ee0586f31d8a94eb11a9b07600c850554855b842e0aeb0f2063bafc0c49f8c05aecab5c261ad5a5af3f8206cf391bdc9779ebc4e0ceab2c7d81630efff173b5efdacd37b72e2bf51c655f0064ac4fe5d6fb57e97c782d1ccdf994e48408ab3b4a96bf3c5ff5bc72575c0f070a9753ba350ba904e067161e378116632bf5abbf712c4c65c774a1119090fb02d50103367412fb3a7bc89347903ef9ff62b9dbd30fa6ece954d81428b9cd8d0799f9dd34515e7e3dcca2448e3c7cd0e239c33eae5257881add599a389a3e9301cb86b849ccd544fc9aab8232cc6a97d00af92ccf960f9485396fd10c7d8a500c82782130d5efe969535c33046c920472c21d7ba7fd7015c2aafc229076341c03cf4db835cafb8dfdd65142fa48ddfc2bba15fca6dff3ae1930ddae2d4d72bc0b9c09898fa750f42fb6def136e2efde43b99b701566a60b14b37cd8571683a00913137c8e67e47fcd404b4ba039b0294163a6f7d3b28db6db1aa777d7869e03054a3855891e543928988e936b5275c289754d17101cfa90ccf12d7704d13fb105fe8c9eb6cfd2b968c19c56c67d25083edd25e02f10fa5f597c14a2d6236b8400bd0b79efd14d5d7f3978b40058f297008a3226cf97e9782de637808ac40e690ea7894a5f01cca543e1fb83710c059fe53728992c185ce3cd7cdea390750b78046521343e621c9a8f4b35808f786eea2f272e9a95fae081b22cc2a78303486a0419ec52bdbffd3965a3139a203d988dc3b026baf9d31bed225e56fd4b2a99ab074420c395fdcc1ee52877d5ac2ecf73b80d9c070c56267f212cc1e746d9042d0609db9d55502920a7f11e50166fcb6caa67423c618ab99164cc84bf4cf4233b048d881bc0caac24b4d03c056168d9755a21d88217d11da073f38dc93bf20b15084928e968c317ccab614ed79f682fb24233d3598adf7bf86363e359b855b1d009bc6967ae018ed6feb4d3cb208bd4d084f6f1bbf0a0ac95b74f3374fca53f63054dad5130d3d3c60c9cd79e10c1bb1cd9fd263c1467683ab4860da61c8bd7b10ab350cd26557991af6f230afe2750d7bcf27d0e17b6671442bff68904cd0301036305b7267e5ea34ef08ca1d47ab5958a89f372803c4693e9b71f684bed1dad015aa80006465c57dc20f81c7b9f10c08eed434e467e62702767c8c8d10de2540d4b689579960e68e03f866968b88ae4baa82cc70ab8900a2c772c5452fddcdb0cf8b485307756ac7b3d272f30cfed48eaa04b6b4d25afc6cda7341878b5bfa60e132f37764fed49195d4947b5f362dbc72bafeca8a370b4dfd629e500914b8d0033015621dd55d2468efcb9b3d60fa4bb1ce0547dd4efe75eb006aabc5e7c9a0d4ed40d62047625d8ee4748a8ed7b2c4bcfc401dbaaf4aa6bd871819f4f76910561fce3d24e87b9e5edb7ff4aa68255c479fc298160a8f96153045d9ed0017b00e38a7b779af8a66c921119656bb2c99bf8e79f54aabfeb03c9c05a0e552a2d0a9d5a6e34873f31e37c1203a012e734f1bd997c0202da2c52d596084374c679052ccc72c9c16556eda525ddd168c685ec4ffdf7bb427b96d3de936c308e15ee07c0395037d2837cf8605d9d919c45d832a7d97586de60ae5d58cbbac83284230590b30a1aeeb6035f55d693357eea8d9a0d62ffe5520df0bfddbae85c5866280ad06b7697ad4cabe786219b7a40b3ecc79bc19b4b5d1afa7cc9c68cf950bd8a00deb98dc98382304fd5c9674a7331dd52a6837f48fc04d349e62574cbe22887092637d89eaa62a57fed7f78a692ea1085777ebbf9d84d8cf34b0c7f34aa9cfa04e0331153df1a2787055dbc97af9a2a3fbd901dfb9f4311221d7f80394ed2c709d184baa1eacb803a18abc550b668be69c0dae409c7326d085b837ae70cf189039ec9ebca5552b19ef9c18d072c441ea4a8d9aec8f06d38496d524077b909d706c14bdf5cd49587cd59132a1f1a91a17181940d8287e2430dae1db580fc664b0baa5948cacf1ba234a93a1f3336374567d5a0d2bed527592eb1d75fcfbb6754067a426bd834cc6a9f0836cc19243eb23621849c570a0273f6f0c40a2e65dbda015ad07c23815722fe33bdb0aa31a1d717cea20d350c251246d7e297490768450438b4af162365dee39f12e0e9c15b370010c6e9b106f280b0b169b5b59697a907b4077c1d7197e86dfe164e9fafe20ea154e53c744e80afcae18af7423c6b68082c838a60c3a9b631e1e737d078234ef1f2a1657785bc1097bff87f506b27fa0c9a833a012e24753b592c02b6e5c7503c4ec46477471a05bbff6c4e3aed77880a54c527bd55913bc8cf7c04b73302c6ed9ef4279a37e60767d9f3cc9c3976a60434b28bd0e744b40ad89db83a8154d73eb04c00c51c5ec0e453fdc9d37c9b7003c3fa21a3ce05d3a0332143752e2d84c6908708bd55a459ac898a3d866e2f47010c0e367ff75838ffd1cf8dc9bd3e28af26903a4ee6b31b76ab992330c6eda107f6887d84b1d53b61c47f4568fedaeb53ca3ce4a7a2dec997801ee9971610430668e0ed7fbbc7dfe81ded318fead091049bae5826643e0ae3d38cd710957d4e080d2054a116812909877b61f245ffd3c3af1d0ae25b3f067681d7a80d968d1e03b847502d1b75e92199c9dcb7684793bbb2fc4a5f6bd3ecafe39ce9e61fc1eb0f70553e5470c2c1cb2e8c61bd8321cf09890acd639250d0d89578c9b927c47b0548f6ceec77e9bb59b8d15ff844f51f78794b977babb73c9e57ec9005db8cd70a97c5b48370b331d97f0a8fd6f63a30040a60b69cef10349bce79096ecf379f069e464ab7e8ea297fa8a6df0da3d3a787acc8ce3d858e00c102f770cfa522aa0530b3b1d02f5d93cd7ca1104262198258c9111a936ed5bbc525b419d6d9fe9c0a1d9ac382d91d703b3f5347c2d1cde97651a0ac910625b02c49c30f4321687a08e15e5929224ceb3fa61aa68bbc4900c8b51ac75348629596604be6bb4f10950becb101bc71493f8afe506fd0d2e60b03be876a91e48b6b400d7b3e6b26a753077cc7125af7e0676011f3dce973049e9d75e9c357b5163ba86d642e0de2d1530e4f82249a1524ac9db73bdd8f4e11936c8ab87767f45b4f0f019005dd035ef90cf8bbbaf66dec9d3be1871daa21a3897f22f5832321ab86cf40064b9b252c1e074bc49cbc86a4be3d9cb57165563304cbe57557a508c520ddbb17917434bbad01b9ab6097e87b4a865b6c7bf040593c6e9a49dc45226315495429bb5a1c92e7003cae04bc119d46acd40b25b6346a59dde41d4a7776de3012938cbabbfa42bf0765ef89483558dca916b96e50e7688a60f6a9bec938897793f2aa9766a15e3205a781bf6a085bead72e3668a0925d0775675ba306e27ce33286e5552d80f35f0a40705bac2b5770336405eba9d9733f5c2d6d9522526e2e15679d848afe822e00e49d5a3cbfc094ba5d06317df8af8bf3d00deb3de0276c2bce64d36824bba10e49fc79fda8f66725df1c48942286fc3b1326e1dd1f13d9152bf56d33635b860bb9b9e72d7c5c30fb16a1e1942b6725e7aee041169528ce0005f884c5e0ac700a9699e752d85d231e5ec848948c45029d2a8eef3c9313ce9b7c3ce5b095ca6000acdce5b87df26d09f95b7043a49f0f78da36cbfa930fb553d9464cda964f9f0d05bc3b294d5992fe5bfc194aaa9d76ea4495ca4b76753c8443eaa7d96e9da90d823aec09e4c66e4bab02541092daca1de240d0033bc02d5d89bc39efcf1b720d7817dba24882759428c1d51061d534068b58acf47cc0d4faf7bd6c0dea6e080008392ad9848031e8b34249b973ca5a54a1918355bdd31ee42bf5399a508447032ccc81d948f1a5f5f9a3664d351da888954983068aff6e1d91dd0e3c18bce909e9624ba136bc7f5b6a233637326883dcb8e8bee77359c421ff6449e929582f01669fa66097af48e9f679b6d1242c498bfc06505f2cc407281ca8736c2adb9c0b0094f743fc059531ff29caa1d5f20fc35e3ed6ed3411876a2e2061080b32a100348351b8570ee0ade2ae3c0167fd4c9e43e617a575fc5bd65e6a9447c8a6c60d25e8feaf4d36833035ef488481f921cd0e754562dc88179adda6d2bc961104062ce7a1f6262d7996caf9e53e9edda5fb664550225f9f3ee26cdf0aded55aef037cfa0794237c66e13c3b178e4c3f7695d883a5016123515f4614773e8d16550734053ab44f8e354f0d301984625a3e627f7670c72c5d0b6299b1a79c4244ac0bbb503ea6bbc9ad1ecb3d79b28c0e0b772f70fcb18a7dff0a0afaad837114a807cbbfe12555d19d2bf15e191232d5fcbff168de677d223acd781aa37803c544087ca37ec4a68ba275fffd3b22f5b606b1d5f259e5c7a6501327f18187fe925704cab65bf674d73c30c5a38fd9a66d673825007299d5ca2bba6372486db5cbc20ce536f6ef77e973104d75d8dd131f87daafb427883048332bd9a8018458859605085a2547ca17a43bdd47b9476c7c593dd5a2a3c648194b3e9794fe052934ba094255f5a3c6ed2e4fd95e8b671fd77c78ca1b6078c8ee81cc70fa4b6266703701081c82b47f79450ed50ad805cffbc79aa65a208a6e67acef0689ad2544e2d80bb747b0b38317e8d00ce408670a68fae5bdfde790f8f4ef2697deabfbbbc43e06dee7dafc9df9dc52b5a6924b318a0a327b2374dd033124baaf31015c478c2b0adefc03e58fdf4869deba58e7a97f5f173310d632d9ac5174477f390f563c90050eb08ffb0d88c5436e4407f4ee45f4445178e5f58a4598e40a70c7d91e07160e6cbf03b4a6a015a46eb0f9c50a1212552015bf88261c2a53bebaa6077d496f0911e844f5e1693c3fddebc2bb798fbc5eed0aa271042a6a3c82ef05f21a544506a14c70d03235b46eb5124804e33ffb40a47364483486ca5982c4cefbb9cdc906c1e4d61e51ff27454ac6fd0413fd6781024fa63664e8b114acb692c4fe9e2108be1bebc0625fceca191fe3efde398a44758b5395a44b3f798736d0ad1c55ae08df0cbf628dc4a54016388db3404202cf4737b8a15d14973a9b62fe3a375b8d05466d4cd957cf55c6324bfadbef6b37c491996dbe6a703162c9a290b9e96034015ce84b4bd9d5eeb6b59f30189457bbb67633ce6da32018a00af9d41666a7f009582028e8f4a20ca3439f18e2f109d7e1a2f75362ecfc049b3873be3d457bef03458215f106e989024597e080ecab326b7b9b1653c0a0a4701047766c8b162e0147f0875ba47964123a23cafd3c256f9fa098d763d0a09a713c046be184d27305a2771f29c4d353d9f44a6faf3be7e1aefd8a731f9b0b2bd915505369e3c1c10704367847ccc9fdf267a35b44592807faf2a012170299830831fd4f2cf335db03ec1fbf1b6aae4343317d1464d4a1481ec87032bedb5fa311c251420532d533031e2015628135e97e0c139303d43f63f49be47d3ec55b3f3d4c4d2b7c38c8df0edf27958703f5e2a82955014508ad44a5690c9424f6874ad98b18702d7527d40b55f3a428ec9110812d487c6080f8f5ed001158cb75887f4f2cfca819348af50fe2b1aa608f8911ffa314dc6829dcae4f95c116072dd495f82cefd6ce85b1820f8590dd547910423ed46b038517faa07158287bfd25263569a760e88747f68f01a9f28f2a84695296abd67f5fefcbe7d756e41dae4090c91dec2a2744cb04fa041eb8453685a626de4bb404bc387054396952779573d6d690179f81add042eb0f6307d83e5302faf460ccf42c41be006b26fa6633913d3594633c87bdd461b40dd392ab61cea4247eed1ff71d36529bd5474ecdab72a261cd426b0c3e7f992b0f122aeebcdab1c4278fea1d1cadeace9db5d336706d46c486400a8f51cc6c650ab403c23dc5c2a401afb36708aee3ae1566adacfd6fb689a0fa9e73c0828bb006bfb87e7ba74c6a278f453d26891fd18c63bdd16d67e78d5785c13f6431f68f09990a8d7214d837558af92ec2b8784e7ca24a64b7bff0d8406a72bd20e070b10c803d40b5429c676285855b2aab8b18e8098449c23fe660114e552a3412ada7026736ac5c62bf4cc6c9c4a49ecc85e2c2ea79ca8c5346e8e3e950a605ef5e42038f1b00ec86717066261b0f4b4a7b1c0d0644bedb763738d144dfdffb25014e0c4d64b69dd1cfe33735216106875754f05fe89bc0e3a8c1bc89a7e4601832ea0f16f58ab0cfc5d9a251e59c145e2792a045d171481504996b733b18736536680034e3024fcb3cc0a701020664505aaef30a0981bd90c84430a77790756935ab005d8b06030634729b554ee379d576adf91cbb7a1d0f3a6dfc29542a4c248ec1034e5cba5aa3976979f1f5dbe0fc1d2ff1b705542c01298809c8837321b25b3d06ce6018e5ac874124b1d61509184c704dd1c6a5e70de0c182b3d318f8ef484302ca5650d0a5df006823cec96715b08793a256626bdb2c24919a76b2b11689d8009871d74eef771c5b22033c2d9a9d3d863cb729dca8dc28fc5e1b78c5024b120df3fd197991384effa7285644e0597b4ac9aaf3028389e0262e03cff7ef5f2906320eb30b26583b7be4cb3cbdb38d9c839e7d065075fe2ba578238f36061d5f02454b172650aa7c3f50f621292b723e59cdf44405e51c6663d6a375517460310541383012a23751091940e02a81a2c07f59944b91b090dab900f3f3a0484ad40e9ba0039f543b440f3f39c4f1a64ba3d8f0a590c91e1c74c3c971e5083cdef80b95717cf5729a1c0e80730dc17a8e3f9d93b09550c4626f5a02df7c410b058f0d0a22cbef32b5313447fd29c389ad0a009c217e63dc2a45e12fbc1f217249b60f4a04d4a93cd481d5497d9beb7d2b4414a5e03be94118196e1ff364c65624830d0ea961ac5f97fbfd253d961e4f24707ef2a7924c012c92e3926b7ba0a3ffbf0b798c3f9ad400884afb15b0ecf7ec77d2089fd31cecfddd9c5fefe6e03673bb0ef9808d3762f9cda0ed1c46e136f000f03a8b4a3eca2bc9cf924327c49d97ce01387b59fc8b96da27b691fd5b4e2d0b75cf8dfceb7606ece98738a48a394c1205c3bcaaa785ba25fe9fa91d1e5d9d58db9e3e7c41394995ac9e907165dcc5320ef9fc4446b7dd03177318c565d61a7ee450b902c308e4275bc291ef30f6864f088cff0f057c5472f8de0b4ee4bd11a93e7b115559b8db1ab906adc2a25ac5bf01ee4a4d1c1be94fb172fbeddeb29ebf27eb80b988e4683464b03f604cc74fe10ae375dbf34eeb4cb6a72886a0d9dcd6b3d10ea94eaf2e5bd1f4609eb592ec250942b11441e2551db4d03776dfd722582167c4564bb8ddb75776e380a401158e0330f523038d2d1ec1569e9fdff9308bb8d8d98775972476aa78438464c220380c false +check_ring_signature 37ba4692ed61521cb2c1102b827573c7407c3a47df84084718223e0519042ee1 a383134a5c4f27e38a968731f522adccb3fe5e3fe1da8609d141a775ff219b32 19 ec033a807eeb49aee5ed2e11ece8dbc797333633edcb672c9c62d24c648b4c97 0abeb19e2ca40dbde259da545eba588a28d5d528e1c3dcd2e70e7f8856d54e6e f4962525d09826fc543cabe0c5e0970f666d6993521f6b3b43ef9f769c4aad46 dbbba35f4006a0cb8db2622e868475b3b976be6659d94990e733167fa8a92171 e2d435267f259224cdaf36760f0971fd03c99342fc3610d32a701cc8dcc9bd4f c308c670ec68781d22ee3b9709cccad64083d8c9624aa303f6598d0fcd216ed9 a863340f646a07e65158eeeff2b56d22642bf05c51fa4fa6680866057f28a91e e0a8a6b136d472fb821f82fd786142e825afd2712cc66fc0f52c69e2821fd0a6 5364dd3f623975b3f8d9842ed39e9f29fd855cc6d73375735a8fa8bd5d9d8ca3 d60b151dabb75d9e38f752086b8e20b60cce2c21b6358d95a8eae90e57c1f543 dfc43b330fc6d53515da3e9a9c5448f200556339bf7f4497a5c8e1746d1aa7b1 71f67a6dfc6d58a0a527c26320387db7655f33e346631caef49b520e163dc822 0cbec0d1e5bf52fa10e41f66860c1358c2a4d937c440a419582ee386a8976546 347091092ba6d2f57375b46151158d1f082ec6536959a26db6ecd408427a8535 2711447eccb4f9c0468326a26a6b67364e2169cbee814f02b6d43f71dbb7dc70 c3e184a5b0e09ff3cc96e936b1490153980c69829edd28f758411aa1d8836af8 f95d17180a8db92ab5d4bf7c7313d1addb4b5c03520ce7a6155df61a23bbcd11 0e61c352d75410ddcd70f86c3b5336d2c8c5e5a111ab437eca99febcc7fe68d6 d6f15c3d839eeef71ba7c69796904ef5e4ee3ef7d7b5156a79f45176470c22f0 693c2e7021f50d531ae1f95c7b6cfbd88c11ce545033c3f59ce397d2aafd9f05b7eedb9882b3e23bb41bc8a171ee1151ccf2956de111f7a825b3ffde3a45d10fb3ce95d3c865e48e857d1fa42b963700623b30880a39d3eb28aeefd3d3d52f021f1fb2e4d275575d86bc613c1ea70b76082b1e76ba1aab64fd553312735c3f03c4db15fd16878976f41cb9ad464cc6264faa7fdfe23c4b8d0416f94b64b52d05184914791293692b7174abaff714b92e562ca74918c67cfd3ead3e72dfa7ae0f395a59305a5d0781401d02c9f08f5e2b35491f6723455b0e8246daa60a1f3a0293850cdc5a214e2ad6d14b210f47e597e39c1f00fce119431e2eef7180b1c509be9e3af32500a2323ebfd94fed29efdd5c42be01644cbf3cf03dc663b55e57041430eb0170fddaa11cd84ae083fb821affe9901b233b329e02b447c545f0bd0af9359371237f5a50bd8e04f683d845bd03edd038f385ac1785b0af918c5ecb09bd6648a3dcc9a5121d11bf3688807929a35a49a38d61e230a216ff0188e33202384f4ee945a975d5e17d4b57580c5ea94338c180218034472baef790323a6007d6c6e829496a55e98cbb1852c99beabd651621583c8472779eac6a54101b90043c6e4db9977a50078915f8c89883c95d264b9c5d9b75ecd39b1f5a4d163adc0585b5e794594b04d8a7553658d2e8d5a84242050e90a6f8ae864860efc55e8a060c8b8a6417a1cf807a87b2d454055a4475810ba8f1372ef251026a3f119a0f0e064303f2ab2ef5c685dff9773009fc6b993cc21e7e01ab01b8bd79422ed24c0844c511ee4210b434b42ec15d908377ffffbb23a522915aafd2510addb4ed88091e46a21c25a937e5ecd4c39b9659ec4640c28790e76c7bcbbe4af7aca537a004dbffab78fcc42f2314e521fda3b5bd2c340d804125dc97ce20af0ca8bc190a05b9bda82b4bd092261254fca1289cc9657f08b9f6ff3059f2a691ad61e95e580fba51f9789d48400851015a8f279e76a94f79a7856cbf6705dccc68a0e6aed40f0f32bc2948695969c75d1a786e97337ca0049cfe58f7e46a864f8ea9ae5d530107e0aad61ac31981585fb6645558d064604be409e63f94bad806d539c4280ca6019575d26809babb30fc4ffac5d8bf8d537e0075df1cb3a0ef423810dce9b10b7bc3969b07dff972b0ef997529c20ad9a3fde5df1d213a2e9a276f2c19a0560bb528a5a9faba17bb92c54b9ec6de4621cbb44dfaafa8b362fe1c320ac6fadd0a1163c86158ea5bb7cb02ae0089fe920bbf5ef6a511f8748efefcc09015b9400b3fae1398f25e365df2cb42b1aa1b1b164d406a16f95e0258b08e607224b49704320364e6f66b855662b1096af642bc4fb4be4f5b3052e46c99dad4825ece300224b5e9019c40d4b7cb83dd8fe94f7546c5b826e0dce632b04bb45ce931a58302706b116905ac6a3f07f816666e9cc6e1b8ca3c1bf84878e2181706cdac4ce90be54c53f973dbe21a812c5e075e6e8296766361c0b370a46bdfd51478a80a190a453c5e1d303572f9fa01ae9b4b2ed6519aac394a1fc2197785101aeb53be49077ce91f46daeaf120ecc0b98fb9245929454af9b713648121905e1f29f678ca04679b8c998a7a93ecb8cbe0aba3273a4e78a625070b1d946308dea3b41bc9f105dbddec8e15ca9aebb223338b73c1c9e1ecd81bd1926e3966718c10d3a367f200 false +check_ring_signature 0951a93522cb4d89951c0a384398acddd9b6860e6516102c2a1148b6bc829d8d 2086f0dc2982576d308d071801c994d0f969227796c97b06a78c51c069632ed6 25 0605b52efc432189c57914dee19df7f2f152f5d78305ba3dfdb084f5fb02fcd0 4d25b8e55379c88c12d31940defecc781e55e6e77683b8912644b62785dd1a89 3e371dcb72d283141c130ff21a6616a28e504290a2e2697043fa363ca8138a6c 5d6859b85776ce6abc2aa20ec8cbf16fa9d2aa581d4d833dae5c5f2688a61d31 a80019365e1c521ea5545918cb11c4a95f6bbe822faaba7ce34f430065fba69d 49243f4d4a49a16d29ce4b058a783a1045e50f260ea61c3553c7d867b8ff7e80 464148f66292caa41667809b0230bc3a25582c36fa7597bd233efaee387c6085 060f7e6520fdaffe130cacec2654e4dca3fd77f728135b4b1311810acfa48be2 45f0e16e9121aae9082b95dc9c028b470e43341c9a61a27e65c5559e2effa2c1 10744e8a72fbbef01a83c6f113b7ded494d686db1773b40525259de4fae6c671 99cd9bb2f3ee45f9d5a1584d58eec6c76fd7ca5ae3a2c2175ffeb129a767e1ec d1601f3bd3b3ccb345b475c1a98fd1d4a7a6083b500e0d0b4c03c8c841eddbe8 e0dea137bda656ab13c37e37bf84f9d877d4c2ef762cc2e0dfbd3c254140865e 3d080b56fdce5dabd31b1304e3b5e2e30ea89b0dbed655930b6cda4b20dbc6fc 8db85e2898c24e3570f4ea3c27b036fa292b76b93da2cd786b90af9b76aa53c6 94889f2e9b5ebe5051ca2fe6e6ad2316dc71407cbe478865a2aa57066f29cbd9 89ecd1b2784c4fad3b2accd4e20d7831f868f2c76e7354448c10fa6e9cdfd78b 34a5cc5a8a7fecd4b030d29076a254a335e3c46b0fae1953e7b263ecb7dd95ac 1b59b349ae75d251db5c359f447fd4c47f89c2203da90c7e9317ed657e57217a 0c1923ca75a3b3ee477225d74a339d8bce741c8d58dd3179e5b4549041b711fe 6f17e3dee9dd2d0b4d0b49b64a364fb5661550db0c2a1cf642434fb8c435c83b b3da77b897f5ee6282dacc8300aeedde7ebc98126d10304d33d2a40fbbdfcfcc 0d2fa00b83af38d9d7f646416de4578298909315ba5a8b0df1ef145f8b9310a7 9390385871b9ae1256bcc0118e8b2f8e9bcdd923f92060f7e7fd28d4bb8d4425 ca9f94e10a39c2f6bd29d8a0427700b84b51b0b01932dc9039ad993fc170d1b3 9ba64f0450633ccff7a880ced57b5af068deb69069ac020ed5da9f900b34930f67bada37857afcc5205951c6900e713d32c8b31251a4e3d50c03ac1f5c79520d0e5660e50baf4a0aac85cf9c3bfafafc998e4926c0d51e004bacd4d014d60e0a35d9784e8ba75d3d9b65640e7a642c25ae5871c315fea27a39a6bca0a3079e0cbbb2ca4ae1b00354998c501bb6cead511288825adb7d762291e759f3cc9a210bc06ed4fed6e216170796ff6066d2b61f97b41e867f8f45a5c7fa360a27ff950467bdedbc4158da7cc6fcecaab3114f4b2c27f2a0e422e36520e125884a89b508b63ebd5692bccee86d66d8286fa8259ef3469a42981cb7cb7d01ebc0800b3b032d9df0e125335b8538fe088bba6b489a22f47ca940a753480cc9a22198d6b10de0f1e30aa54920b7ffb0e25b8e4bb25e8ae43afe7fe43aced1dcdcfc530ae10ae0ea1d65e9b676fa84c717c75c3a1cc289b9296085c82caa99352d37f29d4e0f7504c4f65710c585effb32c8e705343c77324683c0689918a88e7da30d465b0bc4c83bd8a032555f5632ce60458494d96fbfc91842f53fa692885d398eb4e60a3a53b0aadd81462f5ff9f72898b876473c3dc4ec507bd25849ec46685b484e06cdca6585d95c6f864b3e08258d2604607a1b608e3bf3a61b35e8a0667384d80060cfb6051e384d7d2e2cead5ae5b89952634636323646f54277b4d379172ea0f3f7344eb9c95ca8a329e78603bb1954d9322858663fed25c51129ee3db761a01c97f9694e0d3929274587ea0b9daa3968ed3c6e9396fdb7cf841e74e5a1bba0c536298a745f017cacf2a30d960885cea5c13b41fa417b891ec4b172fc3a12e0efb1b35e2946fb15bcacedc3a61fbb85369b9c956d82fe7660ab3933989267005d047fcf73fcea778206cdb18522269643cba9fb8a928f8fc301d1426b6920604f962f0f1444102e25e17a44b15eb9f9c6a02106713b10792fd795fb8dae32405265e181cf34ca54933ee4c360ab8aab685f167c406b2f28e016cccb16d43c70295dd5be1f69b11670bb34eb848bfb88ca982d737e4eebb213af8c8314d1a960650475190945a54fd1cef393272d0f5e79c1bf6aa1f705aede5bf1072b30ce907c7ef85120bd455645d01b779aaee607e26d7b9318895da126103c54c57118e0d9c257c144d866cb4b20c2d463258392c56320d3e71c9345a69b672ca56b5b300e834abe4e718c8bc8bf0755d67374a826afe02e170bf12cec5b57aa5fe892c07bf0092006400112d3e659dd962410ece66f73b014e6ce4f027673ade55c9980422fe7ba50a373d1566eadc1e0fbaf87473f80f62e35b564616ee8c50576b250c3f9be47899259d810869a12fedaf60caad3994a6dffda98d080ce35f1155db05be72e5090d57d3797fbb1b5db9283477f489a4de43b4dff4f0380b724bd60d01078d4d33cd668a06d2c745ea452794bff6d15e97b0ab9a273f11b43cc0290d041d8beb62727a7be92860d4b4d3bb49b6bc4703806c4251c78d41a708fd737f00629b8d002551eca17feb0abcfcdaa0a0d8808f24840c468d917b2899cdfe34068a1533e05875b588c392fe2e0265c6cf802cdc25f4aaead12c2a8889a3b07303f14fb9935824f1e827e239d534995c4169aaaf89db3d92a573b8a4f6f18a6a06d2c29271f7870df512b5573c7d2ce88f149a761887f9a06fba68abbf5a74dc09e9a281054775d097a96394ba185d525d9dd878bcf3916fc05a69b0067b59790a73530ff0a2f65f00c551e5cf758f6f1f1155f1dd9b1875fe79660467427a7b0380db5e116935244d37f76df61ee7710fa70e74ee0255112839cd8aafbe69c60c768b8db6b3c16d5bb099fabeae736827f019e9c139aa79f474b38b49eb839f02a0bbbff9f5b2f9cbde44fd46f89b3ce700fb3ea38d6b3bc4846b8e6a7ac00304d54ec81a8fb1084cb26db01b190f7c5d0e3b4e75d2e7253ea1a7c047156fc80981b9231062a6f25cb22fae24bddfbbfbdd223b4d81610487a4d79533dab94d0131a841ec60e4ba9c71b13ae750f55a7a438ace3305e5e190abd83b8a1646180b36b0fda2c496d1e990ce3a9759c12e8cb8022df648355a85718c96a30a9d150faa6798773e5e126be919486438188b0d13a5d51962a7240351cdd8344ee4d40d09db2421aaa85dbb93fe1f9c99ab8f5148c4b77f6ecb16c64f8150dc2745100d6e2c319e3f52c5bf7775da83bcf2c123a5df5f4792fe8131f1feef38b1246006 false +check_ring_signature 2d0402ff80286cfa4879e55655a194ce825951fc094621da377a7b544a9a8ea6 396db013c05cfb23379bbbe3b159fc98f22aa0b59f909c934e1a3a0e7b2a711a 1 e25bd3554bbe8b172e65d3499359e7f21d9b1fa876535fa055d9efe7c4bbcd9e 4e9150a5b4c04c736c589f41ec62fe5ba9a02669de06c3c0a49701d46313c40bbc708fc9f22d94f873718714db0afd170483205e7808e7fe5653701f79240d0b true +check_ring_signature 03120df067e4487d34d9d40f54adc5fc87c34a19d30b18a8019d5d186f72dfb6 362a8b815fa52b570ce4cec6323c4f04807324931df51e1446ec966edf38a061 17 7bb2cbb86700cf5fd56ca0e1ff334176d79b409ffe60d6f9c609b31bd1a203c4 3539c4f5203415435468d281e6332a9adf2c28c289e9993434b47c82ca39fbbe 92ecfb7f5fcfc912b543b34af33c99df7a25dade7aeca810d8c399d8d03e3952 fa17cbc776e19ef12eff4cca1c64a808e1dfd4d29864d110abb50bf4514982f5 2f9d80a5fa497e7e371be05199b010a14e53b3debe92d9b33b0c559ab767f982 f6e4ae8ab9a8e9050f5f75232ef1bc5af98fa3d553db672bb8f0df58fb5cfa87 a68053efd190db55c3ac9ca5ed6c9721b66f83f4c0208d1362c15461df2fc0ca bf6de7245d793af33c7507b884914b5893512c705f2b154fcbb1593a517f5343 46019b326a557b398f6fbfc39212580d7dae9c15eb6b4971399213f71890233a 58891f9547dde2a4238558bd6ad9ee6c7db291edba707ea7398443a922597f0b db8ead07a4acc17a25b60d6af3582cd13cf35d122b92b7bae8fbb7e43205f6b3 dc549a110c7478bd9fd7ebd9f0bc9efb58c5a543309e4f56b3080f9c97a3d04a 9c3fd82b7a6eea31204cccf0e67bc3dd05b734587e51995c2685aa326db151aa cfe838a42b34c9c3295b30d918c39c8391e88e54ea6e7b6a9ff3164b20138193 73f2f93ba8620489e772a4a4c85bffdb4e6226892cab9931853672b1e28791d6 586ed8f25d4a92c3d69690d2fae5a62274833af57256a7bd98281a6ddd6d34e8 a3b668e0442b685f3dd875d97178b704f281b0f0f4e98d7eab9c308e1e8c21df df3fa331994d21b4323174e514001d2682cfd2f27fbba8429e249748172d7b0633f538cc9344fa88897ff9a8c6aed5b79097d3abd9cc4c9cda4772c5f7538b0648b83e49cd9b752b612eb5b92d50cbfdb5d01fa389526bf88c0a33f5a7b66e0870b4099e4db400b437a9273e60b1ac69eaaebee037723179bbf2285182760302e20645d8c524d6eb0f42c31c05dbb17cacb357d99efe1c126dfd0a15529e7f0234b9c293235b1dc4ad3d4851be671d783a8ecbaddc495012a36cd1f0c2aee104910b94c80f86a6a754b44da2e035baf0f33551f16be0b897888dd39d9382a00c4c3717e39014c0f002993411dc57995f72b9883806e2107dd8a8b762141ac40dc8722dc0d084bca5e061d99ff8852736c5fb6c11561147e8c58b2d54108cb20fadd997f0791b15ef345f995f0fda057d952adc4fb53eb708b226c7c9468bb7012a51b188d55123e7725975f0b6b857574cba64ee8d2a50cb0b1a3133e7f51304d345aa89247bfde40a43af713b186f61ffb7cf0faffcc9f7319b4f01ebb5bb0b93d27645b0492f7eb214862b5469b26242bbaa50701f80539008c715b24278003896ac6088ca8d20c700fc00950c0b6896c4b0d6bcd5df3f20ef1011c7cc9e008c33de7b4e2a25ce54353a04e7f98e73c1063e5943aff48b97a504c8f10695013ff7c390f328b91fd8ce76f079a88ec6c50ddb0b0240b3e8bbfc9b7f1024b6092b96a4bbce9f5fae5f652aa40fb5222c92cb7943941d954acb35f5287fd45d0d5f09602ff1753356b23b9fdf0e69663ecb333598847fdc4f7da6442e796c1e017239a2f42ffc095429476c74c7b95ea35c040e7e844abd239d2eb1a375279cffd055de299f7687f64b1cf761dafb3a44165e391d51f54376c7311ada33326e047d0f03a59bb6f6eb4fdd8bc833aff55db787fabde9e3ad2972b27f8c4527eb0cd40af7462284a046e25c3debf2e53ede3ff5a0e5eaccfcfbd56e24b454ae310ae412721752a0f88a634788d8a20ff2051199942d0ea96ebc0d466f10d2bbd60e598b51330f50ba8a3b6f9957eb49ae9fb7d6977e6b126cc3ce4a95ff5f58920a8f3115082fd0ebfa14e3c19c47e9e994501624777065a8a0a728e17e787604068a4cc8cb4d7a085e8765779ba493fdd7734e884f81cb36bcae8d1b90ba38df04c218568028bee170a654cbb9670b28bfcb94b7e7b69c3c56727546acd3baef0f06a30fe237a649c9c5e706e38ba57d8f7173b3d61953c3e3e2ccc5ea2620a50cc44ac5be39a0624546f5971f245dafda6f60ab44c63b9baf3267cd4c7d16860d36f7d1e5f7ab3f9076fdb69af5b5e9f88098daa155f72a1b02fdb2d03395c80befcb29c5c525632dffd49879ba04f5b30c784e66ff9bc07cdd90f2b2f6914f0ae5096c4a4c65607c5becdaeb24bd5557961b8c24e733d8b3a349f8f7d0675b093b86a8ebf871caeee0704a4232d63b92215b2beed1146ca9a7523750e365ce0e3eaafab08854f30e317b636a32d74e4cca8b933623201603b5d08dc8a3f72bed false +check_ring_signature 12b4fc4819883a79a419d8b2f14e3c433dac409d9ef1934e43b59b9287721c7c 62f511a98937389bd77f3b8fd012e658d14167bdd12642bf56dac8dbc91d9aca 62 77f5ecfd05c37d0db33c95df4e7182c45a68ad160539ee3206088b1d6a1956c4 3d59d3b3f58bd4343e8bb7990e47a94bb77833669f544a91dab06c541fe8319e 0f30bda9b03d50abe543c1bebb64e3b17a289aed04b401449e258dd90185529b 67de8f7d404f5cba24edd1e9988b7a75dab7312b15d60e4ae774387b2e999057 223f7ad9a815c145fc01594f3913cd1d736a63bee2110efede37b9981ec0203d dd5bfb45a87fac0690f476edf3bfaf8b5529d8944f9de87c240c4f4ffe38bc81 5c5d575ce2288c11cf0056050bed10164983a076f29ec0c32667ce96ad7b2b49 edf7cb0985954d91bc8556bead1f576e7bacc073456c21d296d6f0f643d28ac5 48fa97b039a53602d343571255cb4bfaec0e0aff23598e66779ea2bfeca86fbe 94226db50abbb764d39f2611903abef6e0481bd84df137ef93c260c5b8f88d89 bba43f78256b2462054b04621a5f2993724a17300c3f0560151b34b34e4a933b 3e2f5bc84501b6f0315a5925628491b8f2b934ca6be8d7f3f24114582976906e 5c83aad90464c8e7f070bd7b19d8c1b9d916b9483067102678a8ef5881e35afd a85dc27fb5c26b32872ea96a585aa51769c343917982e75c7565a69d82868783 48f45dd727543312a20afa1e1949c586f8a0178e67bf629f3fe9aed4341ec418 664a0f6824b368160596d72fb810c13b3a22f2e5d7541d48ce1f7472d1226dbd f9df0c25052711bd46392f157a377d3c201fbbd83d1757a9f65372b2f03c34e9 f581e5ac7193155e7b085d82042922e7c3513b61bdbd1ff5fa1cf63dd43c738a 51adc2f0b8a7e012b93b71c713a0210f93f4266fecdc658f8a055ed2cc6835e8 07123fa8e09992733ed359b23fd2b67d6f50bc2f9d59305ff1c13f89d31d469e a61ce6b8110fa14b8e6ec82522cc16dbe5c2dce2cd871a5d922d398d0be954b7 e85c41550d6de1a9f6a698b46c2ec3409681c31aea2034b559cb8ac3d1b587e8 c495ade37e7afba9fcd0b06412edc2d1037890af7ae9625a7250061878e21c67 ab359f8581f765c7d764e2a03c07c7a224725c28ccdbe7d15957af7a3fbf1d1d 8ea36610e9dfdc89f61ac0801106d5c6cbcf09b5092b35f466283ad62dee7bdd e0485997f94c7b701562be6dec8e588b47eb98375c463f4ba7c0667299bba131 2af1ee7b103e2f55bca662f70d142b7074ab84ce42fe16d4548b11c7734db537 499862946a84210847db154147f40c66b056961a68a0cd95e009a343bd762d5e 3e871f18d65343ece32253d2a8c083af81b267c9ec88dae8e8294f3e8bf5e59a 24cad91e5f052beff04dbb624a1f19d2f1c9537954c816f2e1d71d3bf5dbdfa9 d5dbcad230da6b1d4d917e54a44e0513584642a8dc853c6ee01ec1a3b0d5ba3a 1490baa17c1ac2721ef2f9934a47bb2ab8ffd5807a6b9e5f0100b9f3d75499e8 8d7e62ac98b6ba37cf8f22f110dffc9143b58fe15f374aa36a1913e6dad48858 61e4fac56d37306d006ca080609611132c255d164628e316028a6ec23a260d9f 1e920e2484bd3b13b162169090bd84f3fa73e1e774679c4b73960dc88009b713 2fbd4502747a8806265370461a8d8bf2c7e23ce3b204c849d7773af313fe651c ccc8f974592206b38721b79141cef76936904769657776c6e681cfef1d144309 eb3c826d2de77ec1f3f9914864c32b4c4654cd0f87493d690812369d9df53462 a9ae29f7341fe624f31b2930b5a5d3ac423757f41d7319fd8b92a065de6a88cb 0954df0027578e40dcfa555fa415a1810c09842aded2b80345aefc2f3afb12cf 654e681a16c89079334aafd1eeb4723eb64c996d70b6f07e53fc7c543b930742 a519c626d8b456e9cefbf7737e91ce6220f15929509d8050966ffc39bf8e858b 0c3f7ce467e3a15a05a0190256994af7957334c118d08daee5d660817be2df1f 01d0b975d6baf9e294e7cc4090282125b9915853a5fb5be4df5953b8277c4cf1 e9fdcdadaa75555e3bb623d8f5b9099d72221eb4413ec1305972ddcd36fa95ee eb2a2364236e533c2e6451132eccb76b2a384e89ec2ca3e2fe546310a14cf96d 15130e1a179ba6dfa027e0b7909b6c5395930fc063e4aa6d6489f969bb1f6d20 1fea8a54d23d345711e226ed37b83246c15be5405f0aefb29304090164ce296e 119a5be94580f1ddc166bf69ea6deafab814e4e9edbd54f06e2e9f5a76140a7d 9063b1e6f39b8ca587d92ea5552e01317593a4fbce5856ba99963302f63e6dad a4f13ff9c7f62d8380b66959d0e204d1561d113a36c75e9494a65de269c84c48 13ba596926e76d34e342f2c5e2024dbf4382dd0eff8304e241d26b02bf8b9f40 5a8fccd54d34de3120b2118a7b88016d94d18e82400ac7b0ad75791bbe50ad8d 6d61d78bc0f571b8a0ea92d43e2516504cd9df522f2539225cf816972114fd18 ac8a673a0124805eb46a79dcdf16ff971805b88dd84e46a6e3cc1cae6c483d6b 8b51e7bf9a5ca61e48a590a67c84b7902f770009f2488832a178558f878e5500 620eee8fa4c713ce6473affb5e0ae4be5b0c1273b432987fadfccafb7f930a19 3b19dc585ede5a0efc6349bcdfa526989960095cf62d17a1bd6e3d9c916e2af0 ca93ee0b98374a1bafe3f496481f0a684b8ab7c1f1283956fc3fb1b04cbe71ec 94f5a2ba7a174fe35af2e6767ce69a15d5332a9d22bb59d79af0b0a0e97db5b6 9c2764568a3c8efe173bedfa816c116b2b594ba6684d898ee42e61fb4ce606ba 0892c45a31d670f5b41977b820b7734f6f655dfe5251b7af35826a69f6297b82 aa2f310bbd811e55619d11ea627edb52fdc0e266d2a3a3a60628a6e2e03eb60bff220d12026b11a4c69b66ec8b779ddc8dce1aaccd896b61a0ebd1b7464c1d0273367566f7cbb296a5735199c8928a64404807d877596bc1fbccd30199c485051fb5495f95d8c602c54014c622aa2761b06736fd1f564687e117e485b1b6be0e3f564588f5d0f7cee224f64ec7ab80598961e003665b9d458ec7b44df6360c08637fba038ac79966b68ef44dea9506019dfa7ece277be00f0021c8d0a76c280b9225bb8164577867d5b2897cf8cb8c6d1dca3cd680cf850f4a32a32fe5727203dccab804fc6a7ef457f72f0370b46d83fb753e6e31227cb493033b7ad8ef1106e201302ded5b976cb63e0f03fd16074cba3af1cbe0f95a673f6e7173705de50c8a28b8106f82f5b65597e6dee8eab39932938bb250829494038e6026b21c7a0f2c973761bb6199dbf7368740a800731e3f39f8ba26cff6b6b7b029fdba45160ec202f330b8d1546cc536e50c13f1afeaecee69285552a5a82af1b4b22bb2d8046b41ae10536a5946efe1e55e4c3e11c72a0d44d144dc6ca8248da1617243ce04925c66de401f91830f4c82276e5ab184413e5d90f260e29d5e11ae516e411404a342041f0866012e1c5449e1243c4f905600b0e01aee0f6846123a79bf65610501396da6897d6cf7c8fab3f234b7cf9d2e098922066e82ed0120626f489c1c0f59825414206650b10c8bf3fc04165c0d8bee87fceca3297e79bce4ddbfa5b2086afcede7c812146c5ecc04820d7737d8ecf41bc29f3c8d7b5ea9d56ef92e2e01b30357719ee00cce01430dc34209429c36d11b44518e99734f762bd9dbe5b3099e7ae1cad7515c8a95317d39aeaab2ef500895e095ce84fac535bbd4720f150677d6869d85df35e14ac76fd6aa61a601cde98435e0f88d89d288547e423c930118ecd6c305907d689f423a50778f21178767811811cc810aadee78fbdb8964072f8b17687f2d5ed632b36b6f579ce10b8f5992e380970aedd903edfbe5628b04c03259603bd7f13766772891df102984afe1c6a9502cd4939039bd58e5d9580b73824170d34baab2cb34ba91e6b455aa909d3d3bf810b503dec03bb9c19f6a0e55e4d12bcdb903335ff8ef0b94804bd3a4c9fbaf36bfb621e07e9680110bc60775d0338c73edb57cee32e44e9be084afe4238f0af8991563694f52fa9d6aea025bb5b7573fb224deacbde8d1a9d73e8c7881466aa8d52af0f44c504fac47b40200a4fa2b20ec3ba6128ba3982103391d3f01901e92aa253d30eee71bf10a35072a2d86f31451d7558ef80b30b4740b258821dffdd67be9c4b6fbbfd88aa19e0a7c5eb06ef782752efc1fc06d8ee1fa94b81e4bc63e6ad2ad3beee46bab20c505154fe0282a9268dac1cedd1e26f367d99d66747d05502e39731027a4ac09bd06f6d1572a418cf8e9eeba978162400f564788499ceee0cf326608ba62c3d2fa077801982c3f2818b9e4643ebfc345ead9e654b953fc37b56934b5ec1f11969c0fa3a4de2576726b13b799d9dee65e4fde9f635adab14501c86c209930c40cff0d28c6a7bc806cc89e4e8a2496e28f12750674b6dd402383b44a52af8c9fd194052be0d72b3aa2bf624472c691ffe002705de22e1ab000ed70e192f60076932e05d0eec093786f22a6fe99235e47d297575a8d25e5648ac5e14170fd74d1aa6a0c0fdc684bcc4f9e894b986c78fbe1f2078ee0d601c4e2288354eb17887a4bf60e8614bbd47f55fb499e27580a92274675b5076c84badab7bd292ec48854259d0500df3b72f3488f6e4f2511fffee3fe2d36f388e07dd5dee3b72a356315a91902955bb9701816dd740fb17098a4b85fa1d3e71565b0608d043c325ad1a9c5e10fd78a728db82bbc485b32c01ce318455f7d2be07f03d2d1baf55dad4f871a3500b614a052be2fe5fd672a110b37ac13dafb1d7e575f721ad7497f53ad55f66f0b7e85773614ae11bde96e7cf7682355973956ce1a4cecd9174c209f512165510bf0bf64d75fb5e293f72f58d22a0c82b4821a9f22e013e238cb1ef774a989e909dbbd99a7d09f4f383e2793f7ad34064c26531dfcfd64fb6f61e3c45b675eff01b2254aef4927d4c4b076f33cbe37665015176ccc89c5293aef0f67a816f9e5039d2cbb8c742f6fa785c0f58e50207bd5bba0b5dfdc7344cf03906e7bfb61c00bb830bb7f260b44eea0ed9a616fac080f30fe0711d18f9533746601c66f8af00aaa6983f65e412a1c14f73e4cd5d2d4f11db46f3595d92aca102b7e34662bf7053a20b1aa1f5fbcaadd6e7d3caa8d9ddf0d171c062664f8fbdda9a096a8d0d106adb09d8aa7c55fe57b65420c77dc5b71ef53ec5c153a8af5e80cecc3d92bae068895b2973049648038cd0e0d9c6d2281e6729f9be84adcb3d9dffcb447035c01e1a08a1bb578f146d93c8b783c99b40072d326c9f12df457d4c252892fbeae04eef41338e0feeb2504bc6f59d9b9d3d448bcc304888dfcbd7cf2f30d2efb240675f89c43fa6fdc68fbde826201d270b314e926319bd05828a7e94587fa00d70e718cd98d67919abf59de0961d0fd0ae0a4068feda4b688102c34036a1feb82075580a4aa47ab9fe45a26df9b6bd97dfa525ce4d044a0e01c6dde99af0c90d8086d68f0bf700817542f3a21ff709685d8211f797cd14e9bf6c8676f1324f50a0f63a1efb5b54500477609307d390efc3883b033ecffd801da01fa0eab1b87cf00163e89c3f30e517eaa91545d348a840c1b0a2ab39c051acd5d156463b74faa0eaa35335a1ea35a4df0e55c0b41c5d9d82734503a61b0862564e864a8bacdb4017b7df5377781f5c66d6f470e1f0713a1b86dfad91a4feff57eef6dd2410a5302298670e763ab707523c7213f8019294029662fd4b0380f2c168632de22c90f03b5d5e298bd42a4722cbf0d6bc7ec60d7cfcc4fcff9e1dd382ef09d50e0ddd6014aa89b69a5b6184ce4edcc8b29c169de8a4abe5efe52123925718785b4ec7202f06a25af33bd0511532724dff0775b757138946ff6f76819beeab07c7a1a0005d39cadf0ce25b4c5fa42649669e75dbeb24fee5d4fa9218d294ace460f1b5a057eb9e35c342b2781b10b4cd9c9527f0ad67932bde500b8f0c34218554369e0027b379d8557e87f4de0c7a8c6643936b5e3b5ff5edce5a704235ef10d5ceec10059263f24b7765428d33ba995f3e286b9a0706d3ddc497cf2e649c900e06fd00ac0e6756ac1003703f70e91ec9f203789195fec3f75f9cfb4d8c35cabb4b110056d2633025b02420bb539e61f600eed151337fcaa108fd0b6904b99973b06d30f30ace4a73c573c7ad969c15e08a07461f03ade5169b0ceaed8c8ac79451f740a1521d026b8d31328c90a8c9e224051ab4935902b1baa48adeb954dd6051ffa0ff8ee28f77db46c443253408af31d5ce8522a6052b6a40845cfbe4cfc5cf7020541eaec8d109f229ed1c44e398c593c1c53dfcbac20bd6cc4641dbc5a1e24d80230c896faead9c4cb35905f8fc2fc3428b6ab768f51617735c9bb7fa9effa5806ba93febdb3b0c10260bada8819ac028a7d5acfd55a99233f1c47c26514bdff072ca804144d01eb0a73eaf4d5151494aab9343630bd43106a8e0feef7c42da706f563b5fe92ec8b68eb99fa9dc02bdb18f87ef5a395bcab337c51bbdce3845f0ea1e6c2f0bead52071a0e48c7afd8c521b36ad8ae4ddbf12293a4e5ca3589a0030ff5b53e44d8b6b4b4f684bb3a1362f49a389a2796800954353a42284c870f0335279d5d83540d49668da4ddd3454991568666eee954e7833bc9db6c7dabf36bb058b8c09d563814b71f6f90f946e06744330dcbf2b75eae518e945fd454360a05aabfe94385c35a1ef82fdcddacec5360cd95b017fbf3f1ce11706b528d860bb8d7ed8977a61bbe115cc6639fc3a4736082e1613daf911d4481d78ae86de807a4ec7a9f8acde2e86a7948827fc1cb03ca6c2e00cff037d1c3cada7f5df082096b62f29d18cb7d38c3acb452a6f23cec3c72312b441e270f2ed89d524c42b905fd46a34e284954a3825707f67c16ef93d386897cf6ff6c97d453ac441e8bcc0e3b0e51c7412ee8309ea801ead3f51d92aaef94c4bc6824e17eb2e151c621ba004964299b47479cbc7d6fb96c3c1a47e5ee66bf2ce404ce55ae762e85d340ce0bfe58756204a83fb3f490c57d498e68118abf6ad602025bd879803409c9812b0bca4cf808fbd8edb7dd7a12f918c618a44893e59509755294ab6487e816fe96090a396cc6bbbaeff5507727a7b18283113b328c4991553e5bbbcc8be602440a0003d1274ceea0c654400a908232855c5a92df6449e4b2f8134ad3b17cc075e108d154897d7382a7ca73ed65c2eecdafc90a20a3be608860c62b35063a1618310619f8068801dcf14d8184956e1dc5f753aa57556ddb76277adb02573e1be1cf05d7da4f84ccc39e921edda02bde42115c8bc2527bbb025c58a848746926162d089344c6a65950b5db7b42cc26fe6517bc4133a6eb2348a083e75f761148eb4009ea7f79743fcd70fa5c547003fe435fbe9d909a15854ed38779f7554089894605ba04ed39136e99259c8fa19dccb32c5f7522f1c8fa515c4f818f6a21fba4de07f667ebf39d6aa1102358ae1e64af4494daef08ed03b727be1a18ab08ba96a90035a31edf451a2e6b154c82b2ad60410befff07aaa67fc838a757efb65e6364096964ae15f3f2393c5480dde3f894abd4b8cfa21ba69b0b3825e0edad07664b0ffc4f524c0bcc9c52c26d1410fe649af8f3aace175a16a36575b5dd16ac00370b8fbaca9324142a759649ef899fee1f60090d6f18282226e69dc6a1204886a10769035866d1154226616f3bf064e7aecc8ba7d17a82935fe5d060c476170699088e41f6d285d3ee906d7f270269c73bcb220fb5694293ce0de63f19d479c1d604cfbd77c566321027b91fdd2abc9e4a073c1310e13177af5c6331f657fab8210747599ac4ee70e0e18229b52770d64e42af147d404171905a08438f7411b66e0e17488e96b4768cfaedc6aadb7e5739085eeb9b17fba91a4372a698d79902600e37e5aec4e232ec295702353bb4c0b5eaf22d84ed2f048b40d7849bbb8fcd58055d47df12362bc611a22537912890182697364e5ad690062371c47f3859de910e67c1f61af6a79a596d998e96efc370888be0a935681258d79ae3fd4dcb53c905797d9b930a3a00d7e433adffd2e2b128a8cc6b6defce1e73a1346a78679004062ef343b912acba1acb59b6a8fbfe7b3534179e7352e8edc4dfdfeb05e62da00450b8ddeeb82e39c58adca07d35fdab6c9152401e161b0cfd82f1d0b9b74842040d65c55640b23f0de4c523a26c3002622e732b797eee53f7cb9f04fc72872e0c45a1ef93f1f471c7d88e699800183202b3d992bdefe1c1e6fc2f5a0ef62bd30bd9ec385d15a5d971bb12902a341f94ec9aac390dea5213aba2b6b2d451860e0482848409157b800bf44c40ffb7ffab31998c8e1ef6798df3356870868434c208173bb80bbc5c92b1b037a42235bf78010698e9b1d79322ea27e453672e78210e false +check_ring_signature 87d150082e72e3862a9e3a446e4277735453f957de3bd41ec26fd6fe440064f4 38996232d216ae5e342bbccb7818af1c893e3a6a04b8d953d9802203e46d5b22 16 556bb99bd0e5ef2743c1cd63a3f53f9bfc82206750ddc8eb2ff73529540c5d95 c4cc1f4ee35efb6721e6b372aed8635385e8fdd5bef56ba8489abccafb0fad37 fc06f5692ac76c6562fc8fefd993b1dbf8d955e54aa960f167d67f69ab8ba89b f422b3d48404a83f1427cd1049994878ee5582fa67b92d60fb79f218214079fb b9631e97c16bc7bc665d60617c424d850409d4848830eb411c435732c7615ff4 56d04b4caea1ef13d0efaaea86357a662d509e4b70d8e5485817c1056451a9d1 94a605964cfac1c568df7f424b24a739e9b39cc6924b6bbb1bb25edd8d94e75b 4894fea7cf3511919f4fda3fdc254a2e46a167b85696a98b435f6b368742a4be b740f81996e759e23ed69b19cc1dff24eb63d663daa582d27dc882d01f2a6b4a 9b70f8739762ed304b2132b07f649bf3f2044e7ec4a6f0a8fd497ad9837c1c17 5989796748b9f0f96f2840a1c3865ad65652ae74f257861b390d62fcac8e533b 89ce73b7d75a68232d096362ed7d00004d41abf10c3b05ee5faa84515d34cdf2 4297db6bfd34c72499f8d4666c7232d410a0e5540e52c6a78da219de5ef77f52 f885a62c97857fdf5f5a56fefce272ca81c544b314be713b0663914ea9eda9b4 448010ffc477d8504334b5a8e9701fa29f7d30293e63738c28a5a3cdc1518162 765532eb163941ab0daf59b7754ec3c2ddf1737b0cb7da958ab7a6c43cc52083 6ffa7511ac3d2455e1e5561af2affaa63068c2fdc462ed7c21e10a9ea452790ca37c4d17278e186df8a755cff375b1053fd5026852b317f1450a504ba39a560117d3ee088a2cb75853608fddcc3a88098a51709e370cc68d01032b8a5416820912aebf317fa441c19c908a31bb963696f787fba97f037bd5ad1306e8fe6fd30c7ca8cedbc8bb1b81c319bd4a0b14e5ee5c7bd22cb51428f603898a01589229031c1072719c7c40b5320504926f68d5c4409a3283e132af050a73b878006f850a8c22483094d0ef0fb995297901942496d3573b461b1957be26629194fe3a57067db24465d029ddbf64fd65a5cd414023999c9e4f879e3c6cc96a77830253eb002eb8d511572d68e740d7f298aca234577668f8d96514182e43a0391fac221c0463257fba95c924c3b35dd1777c1d6104143964ced506bd863c21e5da6ec836000ca77889c605c1d67c40820b43e77367488b0a59a9007e9edca27842be6bc50aaaa592ff47c0293aebc81d77e6bbdb59f1a0adc617edb269241b78de9f68260f87ddab543bfd5335184549bc351f93cbee0982fc060316f3096aabe0428e56009c871abdc49aede635cca4f94637a4ed2e3ef1b607b16dd87187202036b4e5005b3b034ce068c0ef41619673a247766bba28801becb6bd92268991dee04ac0080d2cd7787ede2115680e95267c640a1f66b6f6f12728d98a9f24468448dcf60d9f199298f5de2b3bcaeecabd7c914da72f9d1227a30a7cacee997af9e986a108e8a65a21d5d46c18c56a830ba1dd8ccc17ee4806901a8efdbbbd50e79d217006d920f9626708cbc023fda2be3ec62127a17155f52314281bfe141b7c6b0fad09d6da4d85b8b2cf3df490b376d8169e0a744ad32ec0b0d6d17836f55f7754140c1e2d3e1bd84165a7784184dcc3a4dbfdf997840ab68005c6cc0b8f530d94940756fabc4358a164f84cbad0d7ecf72a2733bfc0d716408b038f2c9a40d053a4067b04ec8ebc2aa265997e3b1bb538b3d1044369cacb1f61c13bb5010e376a3e0db9854c2a1e0b030af9d777a122f624ba3ec885f9bc26882f31f1b8519237b00002564d662605c2dc23ac0adb55b3f81ec63e71e30d340ede4f7e01e08fdf520a15289ad3885a86e79ef8214e28854118560b3dd9f3bb7abcd472c638e5df7804cf1bb84dc91a2ec9695b3e3c250fba67c4196aafa0ac505502c592b8c03cdd072f3104a6b471a6e83429f2001cff9de1faa76f96121e89d3e4b4134e7bbbe8099fd526ee0d381f77b40d66c4fe8dde68b929fe2849353ebeb343e07130b04b0539dac4d376519a2203734e9cd969d0c97a9dc8af2ea2788d94427c65cf8455030ad3a432df99ee718b4f0e14d56acc9ab06110e8c0ddfc76792c668029749f05ebf153349ce048c750ecfa763020ccef54d495e09fc204ad3f788ae1e13aa703 false +check_ring_signature 5253bbc9c36f779f5f1166081032a248b165d3186be59c086d34c8070aef6b25 bfe8d33c34b3106897238bbcd32aceb8d5742e32b7ef3aea9050501e1b76a73f 12 7ea35d6a088f683e883561424f39dce040c65abd4a2c038acd09c9435719c363 2fd8a4adeae062ea2ec3df2435be7503e6367e9d475220587436ffd71dd3d436 f0e9471a51eb1548cefcbd9e4b212e4cf4b02b33a373d1877ba956312cdfcf57 dbe982ba75fba5422bb8f6790aabc6656bf57efcc92944654663441c5ff7c1d7 d0e10979be623ddfa1fd56c7afbebd872e379e8fd9b6c7379ae363c88cf91800 107c44cec56ba3a869296049e90c4f70ad4db5b0f11d81e64bae7d9a92f95fe4 c4d52dadb9376611fe52ee84046e2527f51fd09cfe5769d0bbbbcc02415333e4 87e4e740fd9a58e40ad3f93c3174b0e00c16b291e98f396c1832dc88a76e509e 4ccd441c81d7ab856b2645856334c9483b132c6b3c94ce0ddbfe0f016f05a701 274f6d57338491a0cd60b6a7aa493c8b8c92c5de8f4178c9e4ad546e165b9e76 93e8ac68d66b905164f254cf81a6fe397bee9ed210db2d63d05b3e97c555a47f f6b038bcf393c3e8775a2c0274f3117539294528e20103ebc177aaa11769d46a 438942ce510f8d6eae781d94e2941ec0efd45b96f0f1105962bb3701ed00b204360969648261c991daa23bbe1c6133bac9c09f27a9a2f283f68794c3201bf80327fbec81026cd6b9188cd7e73ab07b1abe24f72413e96fef638d20562d121e031d6b17fd2998a1b49ec0bf827cdae1b890bd22b61efef2d161a5602aac72fa903110f749f729f2e95742e200660e3e2d5fb9b670333ed45a968c796be8d777045a07f2bd9209fb73501d33349b4e1d03485d23a8dfbf4a720e687a2abc0ed50a664e56cdd0fc8dcf77c1b108295f6f2c5d2ad253832a0c4770f7d3d7ed556b066328fcdcd08fac3f7ff860de35e222b0859b3e8638fff96343f958e40cc8970ba7b2f324d75e21564f3837e7c83fc5bad7df2689b6b4648294bf8aba6fb9d20e188e14933d6f328b09d4298cde129b919e75eed09676829fcb93a7e26cea6509a19ea16ec9e7318020e197448f143d03e09bda1fabfc71ca9616fc1f7b7ab2002a1eb2dae296a66a05174643a824781c434a36e251ecfb1683bd6170993c5002650eee81025795894c43bb8d160de11ca64bc0f2944c572cbe1a49ccc514b40e3a0ab935bdd46351e5f6394b8c9466fcf450fa91499022bdd936ebc15de75d07d68e4cc39ceb90d829bd95ab5d00c9e29219e380682601fa0686f64756df67090617a87c36a2bc033296b19761a14d1c4007ad559a6019dcb0e01c551728cc03310b08ff52687aef696079154cc3cf29f54769a207997393aba1650d0be0240d0e90a542de4199da646ecb8f80c34e9cac1ef9af3305aa24e4f0ee885511050ede5191bb2c7482dda00273a410b97ae58258837ed7cd313b8b8e09ac288f640d5a9ab61497c12a715a296783b3a5f50440a77f9d986556c274d89df4ceb8ec067067d18f2ac2415f6daef9cd193eef22e2d1a78adec071cd1796a705f6fe1b059d034ed7b9547a9f5e3dc0cd656f403c8c821b8f40a66fc08fddc80caa285f07ca16de309c7f3a732c88888505ac6283df6d7b5381c2136c875a3cba8242cc0b4cd1358ed7ab4ff653aab5088fc09d4f6ddc84929d1504d2b212d2ab18958206 false +check_ring_signature f6bdf6bc168e6e6cf37a67022f036d2188f78758d0a7253a5b64859159ef260f aab18f361e718a0efa8ebfe89eb963ae648853d712aac564ffdde8a5a6fc1c6d 11 48aa0c23ceb9ffbcc5244bd87ac95f2050a030add2fba0b8106a99713887ff99 acf6a25e842005096d9b4f8b9bd1545a5a11cf13eeb353b543dbc5f3107ade87 a23e15f4e0ea316aad6d8e88d42f32fc12a967c12fa6d9edb5b3578a8209e261 62cc26c2e353e0ff5830ce5b134b232ec1b0d65933904df98f7ffcf497a3aefd 29f4312d711b236bca93cb9c75611c53ab04deab49693de2d33ac7f45daa6bbb d4fd9c5aac3dc86608b19d898ac8700f0c278945121cef3c06cfd1bf3cdf0de8 71f6794184270d4647a06ce588ce845c214537e4e5572ae90e39586a98a966c6 626be5559be8c2f067ce44d2b69a09c0663d65fee97e54a4ee187c854d1a4623 e5404831748b3a2643f5c047b772ee6b05bea3035f13740b6f702903a1590724 1d74a1d52ae9d7e899c29e702178df9a74981a7a06db9417178e86facbb7f129 ba6a4262e3907d2e394606e1bec4f78738eed3f7164e2a15b9dc06930c435b2c d11bde199f7d12232b2fb744e270fbf05baed099cb75220312122e3685c4ad039432619ca3c90208a740c89cefd9cbcf037af0e53bc78346a3989da90deb990e86f062beca020d685baf326b0177508d0d8deacc8cef89d27ba315023091fb058137d24f8f49d2f22d6c94965fd39f393953db921957e1af305caf9d97dbc809d76579fb2dd8d47f3f505393f6b9ea55e239f884946d8911d2a2ddd0aff63a0d57498c3238e833699db40da8d31ccd189c3571b3367b698d162d4ea06a02dc004565742646bd4cf28e5553dd93f05d222362ea47bdd3475f34da139da9acc508869d0459fc75e502edcfb1dc647672871cda0f7d2c74ee4da1cb74a9498b820c2439ce5a0fff0c24051c6bcf969f717654f1973d7709cb9523672723020a2102dc5c57e3391b0eabd00017008d8dc7dccf6708fb27563c92c4998dca5fd9e609f20d6aae6faad5a26f2b61ee9cbdf57a6c39b4c3e99756ddb8fcce98ac3bc10fc50b9fc53f48efa6633e2c4d3be50e07bc91b996a86df245c89ca74543a6af0bd9d412f5083381b39add45890d0407b4d4e65685fe847f33f76f84dc6497130b82f00c405e6c16668f0af0b3ca2b3b907fd9be8fed68224da125e018b0167b004f22f346c65f010557e4adc94525d40cb7ffae8c98aa41ccfb9f82d4741b3007afad69b3730ddbe612a1782be0ee09df4fbed5e64e841d415b91643b7fc88d090ab37aa2dcfc2cb87c63a8172e431f675a815312e2ef73937d69e12afb27ba00514016a7a7fbe4deab7fc11e8032fdccd7e1036e257d367e925601922edfac07120dc884baabcff442124e43602abf646c2050c32c3948eec9b201a9d21017061f289a0200a472e62ba2acd33ebb53ef8c583641d8305d99b11e28adafe5830a441cbc1c1cfeedba602fd4fcd432f8c609987f343c3a18ae98985ad55a512d0fffccace19f0a8eaab3ea3f7fdb8f3110e666546a61ae3ea8894ba9ddefd7c205 false +check_ring_signature c2119873b7d0da7dc16e7af8dab21ae11ced8d3c293347c921efbd1e8854ddc8 da3299849353658878cb0793d8b6a683ff59213476f88e3491e1cf9c3531711b 30 73d4674caaa81b9f76538a37e0ec427365d00dd6b178886f280b5fef231650f1 7573a9dc5af925f121ed412fdddc17a380ff0b75df0ecb234a2ea0e2718de62d 4d18ef18bdf719556a22c651a32d1b3dea66a5a4a643e16ea11fb41925722b69 fcbf55b1b14666b5a51d34914a2f79b8b4bd34c302fdc5b81abd292fc470a97b c37a9028c94ac8b37108fb2e6df40e21f13e5fbae487a702d06fbefcd66cd1bf f5c4b8a52f219c3ed559e05ccd1e962cf87258ca01b65277b4db74e8a5be579c b8efd16236d3bd88abcd6578d604918be16c8c2d85c8bd8a8f7aa01517f66428 d32a3b0c0922af45123c6bd0c7bfd312ab657c3453cae786df65dcc96f65ddcf 8739db24e8f70f52380f2e7a4ecff2fc27ac890979830e0c65e723857a6bbd8e e519cc5676b98cfc532cd4fbb653e9e343c56bc942e858ed1d6106e5e1be3b8a 630813827fab24b2793545a886fd4550a32ac65c7e2012feb802f93721ecdba7 8a2e6fdb7ec5540f70255e6eb57cb75a9cfb46e3defea1ac3101c81f9a12c58c adebb1bd51948cf22ae446f9417bd1787dea89123d04a0944cc92533e126aaa7 fcc45bf3986b410041ec46fca2fdcedae4c0c69dffd57e821d203a2b93fcbe6c ddbb406842a571d96741a645b20415dc8014603ddb71846bea1e8e1e68085ff8 869e9224bca8038f1e6eb8587479629ca3d50eedff5592133a4e5ae075749da7 0e71c3854b2c2346d8241f7a7aff4fa8306d7d3ce8dd7419ad78a4be60abd4db cd9557e14bafdd2c2ff89bf4cd2de78c5f0bc2a7444bd7d4a2fdb87a1884ff5e a5645e2f8b6b2b983d27b368a7af457182fdefc3aa4a4a0a5b04fa20b58965db 790423a5a7a0dd87d07b3a8a7f4e3c52089ae2bfd6982ee8dba501320ee75923 4b7ded14553fc938a5b1cb54ff572f32aa601e4c9d882fffa61cefe7b1bc0129 1b60b119ff48a054d28f5fd918e18ebfd6831094b86a67fdd0168622c45f7a99 d3d637577262ed89f7bce1492de9e3ac6e73a19be30da5d543ed872fcad460a0 ac796118ce9303f9fc78d818ad0ecce94eae1f64a9a615df43044c4cc0399912 1f610ac83ceffd8218b9dd03db6d2710654aa5abb0ef233292f5a50db4ad5e2e c0ed256dc059cc8a356f77fdcde4d5a7fc5b01b3efab5eee0f98e25f522fd7a2 629b9d83cb0be08f9dbf7bc0a6b591d6155d151b28d4d9bdc4d0f6f2bcafb28a d45caf3a093f45256f52ee3e8c09cb574279a5cb7fb7290143c7d45988f2d518 0a82cd1f7902654742200a0f9ab89a65be2b89d73fc6d7a583a1e8aea26c4ebc 7f7fc65ecf98fbbd1c6c2d864dd913698bc573e975962e7e85cf7bdc80c2b3a5 902f8294117556155dba4480c0507298ba81b95224c1154d912e457c8e448008f3d0ef0fe583ad864c7947e8108b0e448534d1b09c372b69601e5a5eb5c8180102a9322d22a30a989564baf689acd3e6f641d947d1eec4d1404270f470a61304f7db66906743adb732c025c5a9a83d0761d8fe8f865ba85246ed7270b9091600b0d006711d0e5d379bac4d327087db457788d1dc0687962cbb2ca2c489decc0205ad7d3ca529af464afd9ff1de1f4980ac6e9b241da12616588b40375c38840cebf0c69b85899059020f0b6d43819f6055a315b57b65971ddf9cb284e6afb60c18b136cab2043f03cb962efa022e3f4e4735ee8b2ed866bc105f49cbc30ce5062e070e32e53c9135a3b2aa9c4d5cdfb6d15c2e84176a9eef728434dbf95ef7051317d62719360d1c1927f7a8222656ad40ed00f37875ea1615432129c4708e008fafa92f32952244ee4390482585b03523e15f8118bb4205b5fa2f65fc759603c2d317c1ab8199d2048c58ec786eba3757989e33f859ebd836711e80bda530002a0b9fe80408919f15b2e11ea5fd00b12b046065987133bc7d27e548fcfe02047ae87e7288e81d8a6059be5f16ec1d3ea7c9a753f09d908fddff16c00f03700a46fd23b623d77df8908ee9e99cb65c10dab61376909f75e6ac5db78034a1b20851a27d7c1ec3e786d3fe05f00f9d7857053f7b8e9165ec1fedc5ef9127950806a0aad3569e96b94e011dba878dd9e290d78a97d805366202eb683e9fd8c1c90af92d5fb09e82ec180bfbbc346d461a4bcd152f2c1d5e446b75494e94ab8c300ae656b1d49e853fc78a056f41c91f646dee32158f6872c57aa816a5148a675c00f358af387f8beb9cc018e76ade94720bd9982e8abef8990d438cf149c7eb4e0746e9d53975b578bda8033ac0e80a0208f47f32f7532b0e0a25e23eaa5fee67063c50df6d108734e2f873731bcdd23b2af340ec1c656c6548f825e555a36f4d03b260f6064255a2b7899d265e5068fd27ffcf497ad6dcfb84116b69172a0e5006df102526c7e44a6de23ac596676f55014806c2d9e1c14b0e11c9a64491be95037a4f4495a9e27e68c3369bee6f3561d3e3fb1e8e45618180891b5dae7f08060e9ca83649b8fd50dd5780de596de80afb42277786155533d6c0a7cf175a2c4601532313e0ed5513a5d5294917abbfeefaafe8b87ce5b2ccb33366c89308822d039dbfbbb4d54beb003fb9783a218a35bf9f696ab95896ccb9c962961b91c7ea0cb24f9d132e51d51d29b3970b41659eff0754b936eac84c2d2d3a8cc550fdc90e524a2df6d9d94e3cb00ff90cf3af4b7fc70d4de4681c84c2b0a36170d4c1a807e8f7a7b9abb1b6d3c0cbb8be3eeba179b9d50c1bbb168c7aefca4d25d4eae0061b1528dc586874658cc4308144bd7c1e05c0ec0e0f92ecdf6da10fc09ed7c602abcf429650df542be13833d009d3bac7616a8c2938ebf7e7467979e99c958a09e1a6f45426dba8f07d6eddaa918d549b39fb5450234949ccd758a99512e76c03322454f9511bb6520a073a5fd017ec72df475f596ce94e4d98ca4e23f3a3c702cdb931ff49e30f28c42048117f7c8f0006f629cb7f2f3f6814f76ac90c8db1076a55b8136300568d253d685a909b0c079aa2ed10470bfbea4551e649c14fbc05377a57aaca3350cfef4a0b9e3946d1bfd0817c81e0ebe3a01afefdc6f2cd2e07dc68aa934d1b1fc13d68ea459501e940384f45dd1962e47d908e26587585f90a8c8e880d19f3edbc9446f6284c1bbd83a2f6197296d3c4330b4eb2be71a35e06203c35d77e93556d814959c72fe0f4c8b2e2e058fc9ab4ded298807ecaa31c09bbc67bc760ad9efbc3bcd3cb03d82d2b40a74270d0b9f543ce02c6507005c90474315c2daae35c698644c6acfa6adcabbb479a365803c168a3224f6d9d4d73027466ae8c47e360b18e50ffd66e30fb772fdb0e35852daa7b98e7b5cdcce8bf07a44b2142bc1746741422c6e35c6c97c4ba7c886822a7b79ec62da3f96071df03196a84f75fb4c339b141d064dfc3df1a5000cf690b68f10d373b06cf064a290b04f13c44384caddb011050a88834621c3765d3de7b104bcd654330894fe7a70bd440e10d42f703be6140bf9642719a007aed7eca1e5e18de3ccc6b13d4beef029c13a41d33952fc7cbbc8a2a3768bc7dc9e9572f2896775900e297fb61a33301db3054ac1fadf7750fd3d030c80eb8bde349896e464a7ad949891332c4fc600cff98bbcca6e1dfb710acaf41c3d17f7884e8ef9ab64f3bf501f43ea190926f072c4906e9e88fd563706728189806e78cafff056131c4a8c409a3634b6ee6ea0e5fb559ccc2d065517e9937b7dee2bf561ce6113c4cdf6613a20c0a92cf9c7c0fcfa0869105064b8dc06efb804e1c00c78897c96d285908f4d3b590db60c8bd055ba74b0192d67467e6ed3ed69ffc4d63714bfad4d598173febf1799318a8940544cb8e5c11ac0f441dcb5b6a29a59faf1b876ec7477e4b472e89d1530bc8c10853bfbe844157af7d2ad43fcf7778515eb0baf9c6651c69298ec0fccbd02a28093547941f8b4dd633c918d8dfc7c834a656eb74b6fbdea5c0d71b4c9c0705480fced2fe215c67a2791ae8866bc39b0c009fa5d94ce5f1e488ccb1f902c18cdf0005f26198da83e4e64c50ff642ee724970bcc583b819085d08c4ce1ba00759702 true +check_ring_signature c84679215479a2abe156723896431772b21ae5c3c329eb7a9b7e0d8ce1374e35 e03849645cf58957445309f25960a9a58de4c743fb4541e4cfcfb3de3a85ef89 209 2b8570acf01a6aeda4a473dc2e0fc6813eae0f1a03298ca8648a8e1ae65f1fef 399390296329ea56678eb67f76f802e3046116ad0221228265a46e0f3eefd6bf 675a299089a0a097f2d5a76d7504e6c67c0df053efefa889828a2625f0a0ece9 518e9a3c88028cc9205dc9cf1614f020404c1e85c019794021fc66deafa988af e20fb2a658dcc4b95d8aafc79cfcee9ad3f83a44b1f7fc77d5f854c9d985ce17 04878ab3d419766e5498b06a0001d2f4c8905a2078607178a60baf4630e22e17 b22c54e0f8a3e10d6dbecbbedaeebf3885cccd31ad2856c389ed06c8f9535a62 435bab51b9ae32b3e2c976d1148a2c3d21c465ef77716988890bb8ef0dfcdcba f079a51a0385fac7717f3ef50335a9627e60f9cd0fe3234388694ed521752e39 13dc9f011dc69b5f91ab23aa21b14b5e68cfd81fde1eb3f31af845e92763d219 d07dc77a0fc4dae55f15699e772de8e0a1e8a7697588e0047a385c89e61b4eb2 938aeca17fc46261957d6f6d755114fae08c6f31ea4b3dd59fd9bdc43b587743 0f4133dca71d35a09e4e4610a1117ec6b9dd3b093bd8276ba6de39e12cbeea91 ba7a1288d012b5c867bd8294c58dc99ed2b68fffd94bbb226a8c7e6e25967123 53031a9713db67868df881b134b5eaaaa1e0c8d65cd5a2b20ad9b0c1192e1f0a c5e16d2e42c37ffb97b280062f2d1f3e5d53d60229c724c79a22a5c79e7439af 3c509907c5d8e7f7b56e8a215f6aaed24aed389c1ba1253891794134528420f1 7c1703ec1f7005ce720486716bab901611bc31dcaf6fd47778605f3f9de43716 4b2b10edc6e39446a1c24fb98a2eb5e12a48bf82bf8a99ad0c5bd342bfb28433 b5d9e98fe003fd55d2768860199e1546c711f13678902703e07c2fd158cdfc2d f3594b75c7b074b808f0c9622f1bf342f749a332c597a3831fc8a7755eec2d65 4115869e89a1f30dbfcfef8fca29babee128c26c6a98d33109735270f8d77b4a efef37af32e0f9a3dbde642660f60edd6666e936c18cad0883795682d1e43438 950a345c1659811ee429f17c13b25052f04b141d4e35a71fb3d44cad69c986e1 6037b31e60826459c621b336dbf4ef2ab0e89839065353bcb8aab142e696f011 35810e4d6ee7b1a57dbbce42db48d3863d5d8cf279372b143852c0b37d60641b 7c15bfe2bb741c5c9ee5c37ba51f632f29f85499409dffee7f5637569c9ccd86 ac5b04909e8bc7eec966660f18eb0a1750a48a3a5742014a6c6346ecc946ceb7 61cd098861cac5cdebe9070b59324f3b163d5f99980d0c932479735e0732f85b 42f064e59b3899b846eede71fcfc49dcce676c8eaa940e302778144fe185d0b0 ad610e74be3460c170066d0be39f34a086356fcbe7b4678ee474ded5fbe5c049 33803f7942ff080953a275b649f7f514c704c2964886bf57c79d1d46b5d26abd af610fa69ba7d717a79f376c8ce4c9fdf100247900393fc9a72808849452b95f 73c19bc656157dddee3ddabef51263a742968cbf8d414ca676aac2595ce148ce 529e8622b01dcadaec1aaf32e21c9f53e1e9e50e0b239332c8685cc5482e63e0 6de68f8145d1a31fb7895c5d3240b8b0510351188f32630c27a39f9c419d7d24 51a051cacc79ab239f3d222a5a8d33547e9762a0beb99f19126e42409a8c8056 7e440ba5d970948252cf9c55da8fcd9d54132c65cb6e15ea979a4ae68ec94928 b56eb456b2199598b6bd3ae626704501f3ae8b21951b6a630deec8cb0ae78d84 039f7914f5e3ee832211b80d22bea0928380b0af03430bc6b62bcad5dd8f2401 e620fd79f1073d7a64b1a1f49765223f2f93cdeb1d57343c816ae200012db46c c8072489d0ea21e81dc67e553a463a53e748bf72d5e0dd74dcfe44dbb2726245 9d9ccba74aefebfd205fef24bb5f474eb1280629bd8412507e9ef74b249971ae 4c17d06ba98b8e676bc37016e3bfaf67ebcd4b7d4a144cdfdea9aa3cd811e4da b2fbf95536f9ba9a9d695b64273e1e2580340fe3a899b89ee3d2c8e3d672be9e 55bf2f19d9763f1bd977ac50df99a807251bbbd9e6d552d25e4c63906397ebb8 54bba24dc89f287463a728c7a698b119537693f7c5b4f1f4b27094da103ac8a6 5ffc4a9c45fbb6d54ecf16a9736036946d6ea08ea5abe3094c5e756fdcd23705 95f999dcf3b75a9c32d3708d502c366682355e649d44b8eb4d3c674a408e1ed6 098f7a29146cd2ac545e492fc2ab0513ef1bd14bd27f8a319a15010373f5568e f3d1f062f2546e62888f00728a92686a79adbf7508f9c339197a1c37ec564b1f 46572ec508417b1853cfe830d5f096f217d088190f09b75635ed377ee217f11a 364858c45aef2212ec76a10e4156096c2cb69184d01fdff16a99a8c86682d4b2 a50cfe71a2bd1236ca99e9fbc47c97c1d06b9ba90d7b9529fce53a3d4b102016 d0fb15d681b9676f3700795368c0bc93cf8495e46ef28a4363e58fe0414d7c1e bb15f49ef46966dbe35ddc820f7b7ea3f11b6924199f1a838884780946ec2036 e309662cf897bd6388f23e32ee977b3c96a29385cfdc643dc73456c6549f06e2 28ffec5aa1d520069c525f1cf4cb82c406d647342f76c0ecdbbd1b517cc6693c a74855b196e1f0bdb342db2f145c24c6cc57540b9f4194e0340b6972ecce66f4 3f594b6038489e7fe8dffdc6ddc40ecf9fb1ee0e72d192b0362d7af9bf8a35eb e8df47bbe00d14f55e86db5f46ef277fc6a0fbe02b44b57b09288b401d83521f f00858f9e6a13f2b856092e642c41ef6db76bd2df305b58463231d8f93ea644a d1f21df94208ebf06025cf62ccb3834e5c041b0d4dadedd903d04df69b008e5e 5b727765dbdd1fde82f0d7ee6a7239464a944fd9e80628de9b7fd60f593389ff 52ddced6c62523fe03858d0c503b31935fe607fb88b2daecdbe1fadeb5bd8816 e52fe6d6126bea7c6b866eeba0974aa183b8aec2de6cec820fe0e94dd3b7fb7e e1f040ba534f72b981c9f56ea4b8de58116aea5030489c1e4e3dc76dfada9488 30a39725ee41a6e5b93774324598725f562dd2135ce78067714bcc9baf270d0e dac70c9a33dc37a0b2e653864ead8062f1e316e7486abf7aa4c61d4e62419add d7e7539a7facecb95d8fc3248d1b4bda3d60ea1c076b6d9c3529d58e93b5e4f8 9e9750d49c25d5056d9254bee8a1b559fa30120cb8e5d6311d92117adca2b16d 2f5b27cb231b77d49f760103f10b54244c083132d13661fc70659d6a0f8de66d a27bf53939f3d469113de12047594fa4b7e2559d5ede502d188c95b8a766fcc3 e4f62c78a33ead6b46e582936e130620c27c72bb4633a434212b6c10ee8dd7c9 eb55dd9165c5011a241d4b75a30175de5e2ec8d6ffe68dac5cb361618e010c84 1aba686bcce7ac5f479ade50fadc6d68f6defd512ec7a7bb9113fdfa5d7a18ae 422e57b5c8d240dc538ee058143c43840f5f164f503d0bbf76ebae840925b593 7176b6b605669af55b49d91fa07ce4500fdc68510444bef33fd15cf1b7d36f27 2cb39a39ec025c0a7136d85e7ef7c3747b212bc9e0ae3c46ec1e7c02203c93c3 3075780101301683d1924c360dd18f1dee21518b834857ecb93d0ab644a7a51e 2d41333b9b35aa26871dcd7cf9fac373e2a276baa633c14e9cbbef598805c30a 813f873417af758ba5dd94596c594c73d37adb92a42994c72bdfa7a41733ed00 1bbc37f87e634fb0c16b3f3263e58e9a67ce69614007bba157d6349ff298d51d e75d009adf5a7c21d23db9b12721f5b8b077998af354255c5ffed57316b8455a bacbb895476cc1fe2aff4d924c0abe25354887f5ba9afde4157efb454ebd2215 0be5b1a576ecff24348f306d3e0a60abce5eeeec495d89917e902ab4d2e716e8 b6391795034a36fa94b13d7a179e91f28a7f8920f4cf7fae53a3f219d8e753ed 89dfc887557ac7f6f50395e6f16f775473ef11498b313421b909d19b1bc94425 0db1692959cdcba5b00deff103bcf1edb4c358423f3d87e4b40bd54d374c6aa2 1b47a33f36a51d2d51a8208546c6dcb971e6ad103b661a8e7da6ce3b78bac7fa 413e9d3775a9aeea2fe9dc38eb24c9cf6d0a082c2edc9f2afee2cc7cc7507804 6863a1c7444f63339fe3efea8851826971218699cb2d6b46f1e7e00b3c7fee82 d6dd4bf9bc2044fbe920056df27dd71f6ed27cd47258ff6be4d54bd82689e67b 045b9d230c55b9b6e7403de28d1da724e3761b8f7e30c10d13b1be5401587fb8 adc187eec5d2b9343b79d0cc8a223662bbaf7155674749f8fd8936433c3e983c 38ba566b9a0c8a1be30be18f4d628a4c3c23df81783e7af532d3103ea5beac31 766bae6780edf17add07ec22d115528774e0879dffe1a86621a56f8e832acb42 8246bf0b993b586f612790e9c051b5d066ec00d9953a26c1f7c5b8e7edfeeb26 fd1c3a7dbffb48113d2b66fc9ab8fe9651e6794e51993231f233e12c08153f30 a2370747f52f483b31d52496d67cf5d1b821f435d7add25af81d901319a422f2 7a53ce6517699538dabd220cf213b8c9f071ff2408724ceaaba863c8b28f90de 93eb758552da2cca47f5f0b9ce8c41b60940157a14332da158794992351cef1b 6b3f99e7dc74afe9be13440ce46a30894dd96d853546a5a1c04c55913376ed8a dc86b966750b8d18e33781bf5117c75a3f061087a047f28010eddc18d074acb8 5011f3e37b31585fa3a4ced987b7dcc63db2a6352c89cf149dc64b029544fcc3 333f3d2d50445179723c0d50321944002367b4ceeb5a67ef4108a34f6fe84503 22c1ecf7e1d4479e3986e07a0a2194a55ff4af4cfe6a2a8fac8cde442babb418 0fee2dc8ece1e7075a5622b219e2d2050b2efcd453a9ecca80c3cf99d6cc8aa0 241e9624008469ae495e8f60f9ea21aa2c32daa0663ccae878a8aa2fd7fffe96 3f074e2e329181b10edc878d8c64d19d62724263ca206480e55d41aae3e76531 ca9d48b06a54be4b72f26a55d1edb18f48f7162f60196b030cff34ae32705519 8da5ab57634a57d221ac64b9ea47c52e04d387b03c384a169f754bebda8b35b7 f64da3c5629ef77d2957e0dc0d9b9880d836f14bad4d6d36c673c1fdc4d247e8 f074a061e24d751d3ac56b74aacc747e2e3e7ffe3488e0bcd79bdd1fd9999eee a9bb08dc6e27972ea543a6c78dccaca017ea1e49859eb2a8b12d1992c77a87f5 eb00031582517a7a9d779a1678c66716f6b8bc9279ecebd583ee9987cf8df519 c612763ed6d45937090f7212f145e5608e6fd268b76d118d0555dcfbbea4ae2f 8def6ecabd0c546178c6734f2fc3139f40d048e9ad53eeb3860e927481a5c5a0 8bd2c770d3f947ccd43735437cf9f79c90fa9c5cfa141ca585b9d963e31fcb52 0e283e545994a943cc1bc37b2a0ab029638d1af16b674c5d1d96470835f9b85a 5c02a78834a26e43ced7a5f3d8834222a532925ac0fcf9a64781f9a2767bf634 e54e5c919fe2b70ca6d6525c731e2c945100d3bf57fa35fd3ab629ee3814b6a7 e04fb69a24eef60d71e792a9505ade36cf05ef31a46aa85b30886550c6eea00d 399a066e8c2ec34551be2de2b6ced5f28fff8b9b36616e679becc87f1d25352f 2d3861c40edc77929d5ada7d0a11db62a7f12bfa999a7a470ce34cc5e9f5d125 8b0a8d3f354b9fb1ce5f7fdb135922285848a1c13c8cc0cd47840a9f2937723d d1d07f6f447e93d7bd4effcf281849b622456a1b7c8a699b072e613d256cb54c d11e8ac9691f31aff647850b3d3691c1fa598c4bc5c510255445c110e66fa513 0b966f16b26c8ef198ed630d2a33242813866b0c45d4bd9188194a8d528156b7 e04c9de30d35d3d367ffd695d261c602fafd5b55e5cb82bbe6ce77ea1f174a2f 7988c4c8a9881665d340df7e145db28bb86cc28301ccecde64bceed5056eaab5 d4816b2ee176387ffeb9b7584140d8b9afe4edb014012941aa80b008f15a73e6 4870e98699ef8d32da8704b77e13c3eecb5ba854f2910d9e01dc89735a457713 a12220444d43b0f6c0f88621b28e1d78375aeb647af49ca88e038e98f329f463 c06eea265e222f9140ba755d3db09dda063c0fca0fadb0fef4f62c572aa5f3a0 92c2d09ae41185df849510a4a82abb70b5ad4775fbaa0a6d3d1d1cb34a7be635 e864273763d5c423adfe193af1a275ad9159cc7b480d80959b794a33afad9d6c 10016ff22a2f85456362be0c1c8fbb648337cc733fc86c2d10d3acbd459c9622 4e6560c08e7841a732f9894feb5b59e07b51ca51e97ab94d8bece4d1cf1022b5 e38df5ac9bfb305ba77fd7f247eca03c9082237c8265eea4caba416635e05fb6 7feb59bbe4e04e4b44a34d2e33d3c87b51e4fe174110136ecc2cb2e8a0b0f41a 252f430756ce7de99aeaf156fe5eb69da2df0e448984f84d7f782e78be265b7b 75843536976db157ce5559d5862079341b151f11d9177d4b989f9e86d51ed1f4 fa6f24aa83e63f60359dfbe117199ae4c4fe1000deba04993e79c25ed3ea64e3 b02d42ec68af56960e008ca6f9f90a537b398b4a965bb7fee9b530815dbea9d9 5074cbf0a54a9cd95f896c67ad3616eea1257e8ca5e53ca009692d1add6f2199 1ddd41a3265a4bd514b161eb51d7f8b67e4b484c84a1b829db3a677c8b281928 936f579f5b6c4dac0ea0be07fe086662813218d92e4ffdd90e8fc09925396a56 de7a099a62c8ae7cca9d7606a654ff2526ed56614f02e4f2f75c3d9d2ce7a6c4 062bd5a3643f86283cdac0dee838d28583afe114ebec002d55094ec78352e1bf f7cee2c8556d60ab08869a4f17e24fe64eea0a680b61fac62230871e519396a7 b2d7353ae3de8c18ef8ddcf3e449ffdb22cb4a4882b763ff2dbba5b00bd8f9a5 af68115c683c7cc72bd85517a231482c35217ab60e3f1780c037fd6ea5ecf62d 6aeb26f7350ec1a900794acc55a8749930fd191ae649015c43c28e09ac6b7e8f 1cf27b05f5758d63605d2bac6e436b9937caa0efdb5065932ff74c2d31f3931b cafa65405d3799c417c1b51e0f68b716f41320989f59be5dde557096dd86b433 780e9ece610453ff408fa60ab4a87bec5fcd85a7ff3a8c991480684aac739ffd cd20a88c243355ce916c469653a75d5c8770bf4c963aa76e5098c64277f1d396 72cd28be5e9f17ebb9162b97df8488107f99623fa6d4ea9ba22711e300999fb1 43cbe912b468490a8480b3e1dc20ffc2643dce1d9ba400105ec1b6b80bf3a948 0883f230c6c21f7f5b9d66167e4e356abc49bac8886182f4a2419267025cb07d aa8439eca3649e65daad43cbb3ab60c7b59ede42f97935311eb6d9e95d801651 6fd5c298f14b849c05cc41e24434b02e0ef208165e9b26c5747a96f90a8d9301 990b0836247b93ce02bc2c9adcf3627cddc6dc3757d57b6bacbf43410489d757 2f35d58d5a78ca85cb7ab9518575930a79b67c9cf8a2cbaebad92ccc7ddb6a2e dc51d499c5a349dbfa29104222b139a9dfbaf1358a111db87e0641db463bb59c b45f28eb4d159e33b921a02a7874080dd82d8639247092e8abd7725f512608bf de2b6f20237155e40e6a619b4504959bdf9cef030265a67fa514d3d26c919b96 621ff482fa10d757b425d64ae89183089f96114407ef60b6fda3614d18e5a12b 7d43abeb0355d75a6c9362832d699c87592af972e51b9aa5c81124a7a4b6c148 a878cb0add5ec953d0ab2e379b960a4bc5fc47455924596ee2a94945bf21f5b7 b8947e2806664080266c5de95960ca5b74e999ea28b3928d9bb999192d55cdd2 60be869c7255cb128ff680e1ee8ae886b32eeebbb0e547b4550b41b0b47acb15 be3e882b7514df69c1abb6e63d3af67275a7b3b241729510b421a70a8de26e41 05e550d1e765cefbbb4c0b0f5f80666016a08ed4d6f815b5e7ba185c0bfdafc4 a5528588898e57bc48413a0bfe6b355932e6fee9550d8c2a641a9750dc071860 6ab7c706f1ec660d468b2b3f3499ba48d54218726a59b733068b51d99120b9b3 e0f1e57abeb433d2db0a17a78585fc4de9e97bde903b1499f4393b12c73f71da 848e3058a0c41d50eafde4b3ee5a3d6df45c8ce13fc854fd63bff44fe79ca87b 95b7db4d0a48a052764fc2b16275c2fc28a0cabcc4dd5f540f0bc05d48c926b6 9a3e695e64117dc75d2e8fab460b0c5756ddaed8feca3e0ab3fe39302c3a5498 1152c6de1c9aea4d4891209b9d39391ef9bed7a838eff63ba822737e3da81028 b23748f3db9dde320ccd4b056efb2738329fb1a74b2de001e264f60291d1fdaa e491e08ea2970c1d352a65883cc64c4938fa1968fa977ee12776d6f3522b0e61 a90af8294b83e38e19203a489fed70c92208e828f71f8f4688ab17951bc5eeac ab682b916520278cb98e472fc4d6a5eb3dad1bf610dda6476fc939e920d7dfd3 9b39e592a6e0f2d2e35f81f90c936421e0e97ea85d11180dd6af016eca174ea2 8cc0491de43d8aea6f2194ab0a3e0eab6b6f2626fdba18e10a9dd8a9c7b331ac f6c817f86c059d09854f94be4fbfa2bcc1dba2cbc60cf24906b2b220ce15510d 28b7d9d6c8c232488a74d0cad178597eb4edd777d30c75890d9551ad5d9a2d40 ae91944d81a1f197115f37dbf6d5e36ee9906e218860730b32c533bff35a29ea ab1bbb8d38e93e464e95c89d8d8ae8a35bc6d72f67fe9b25b2325d124137c93a 0e482594dd6cb72605870eba57600c6281def3181ee6326db9154d117845ebaf 6054f5737104b54e8886fa624c81e700f59ce8c3bf5fd818e7deaece033365b8 7367688b98761ef8395418b1f5be3bcbe5677ac450defc31c31fd602eac9dff1 6272ffc4bed06d57a9e53518f78052bd701c193d3761d34d073448aea028b62d 8e3a0ae896046db4de0d5f171255b16f179d1a45c6b7cef1bffeb816ba3083e4 4b65c37aba0ff40627060ef60b3c47de29029385f8ae61e33725025ac9871add 83bb604126c445aed961a7aac405c29af512b4a49f1f418df89c5d7221274fdc 816b0e8c4a960259f4d4b9168472f69c18c381a952fa9bb9e8f16c5d6753b41f e7a8bd9c59706af5dfabc4e0ab14471219893793f7aca52b7b637089cf3c425f 6fca18d95d7f995c1d28ddc416443d40d59492e0e98d7f15aea5a0487f026dd9 e9f835971c020b1df44f13e8f814d08cbae4f108f3789d86658ad4b71e259e3a b512b6fcabeb5dc5d550d98296759c823f461099bcf02a9bf6af96e8f3958553 132dabbe847bf0097cfa9273c6ff5ff731bf50852f1663d25fcff73e403a3640 f18a734951f7e6863179073766fa7485baa3c26dee296e1d3a77d685c0d72779 3f2aa9337a0e6034155563ee85ebe44964a9c7b02181225e892c0bdd7a72a8da 37a0ac0c4afac45c529b4e324189546bb694abca0d6d5a69bfd2e8f36a84870a 1e6b65eadf4dbf3887a17ad7aa4f5022445148198bcd5902cf50cc4282370c6f 103115de60e95b48967342b6c7b16b52e8b586bb5b17453787d03e0ece16100a638433c753882e380929c86b437451796e48bae9ce6c1b47940228c0a475000c4105ce579a4599a4064836e854de73999bbeee020f4afbdde65d39439447bd0e1b74fd7645db6d7b4a4f4ca5728fcff2cb387dad2d69faecc16eaae72d09300c652834aa2816efad9868b585da43a0ae45714d5d71770e094bd57da3973e1f0aaf4259efecfe42f9199074a75acf1e6430bbe2732111977707366f0fa9696e0d6661477585652c729f9170b8c1540e94ffb27078c31c4049b306c8ce933aeb0036306d973e2f762bcd7b89f7eb2c8c5c64b512e47adfb656d0a416860bb1c40acc0d36400eda6422d1027d8c38ff9444d43babfbf37d9ac947e80b094d23660c943aa452d810484974d4e6d69b84bbcc457df512a5a3e92adab783bd09a9a9089929b420470baf07bcca816498797efca6106b487df487678bbc37327a96720d640a7e687293372835f4388a204ec9b333097e2d1d371b14d5d3e6e8bd186f0ce1979d629cf0c893ef8479ecc8652e1da7f60b6747db144f9e34194e81007d0ea086e4d497c06a3edff23fe8f772547151517e5b94ca199cdd9e792e81574d0ed00210a8267f1ee956dbe54b26e47f267633203cc72e5aac813abd9ce19b8b02ff31b533e9473a0cdce6f31ad69e9404ad14ce0ee56fcd3a29e6197a95eaca0fd9362a00eef9cd4c7ff1ef336a920165b69ff93171bcae89f548432d85e37f046b564308010f8f821f91792af577e3ead6fb4055efaeca8240cd7305dfdc7b07a9d26e0946680e4f2f1ab48af21c0c124a56717e124a40973637dfe1af25fb048c3ed32fee3f2972bd3ed5e41c653aa60053bac3dd42732927f3b2491739120dea8ec778dcd16ee1e31d9d9708f1e2b523dc15dca56fba71da684cc0837f8d0daf1439eb8a2d21a39846b7b1e33be6f6165d11a0b985d41573291bf76e630606bd7b3d73c91d2121bbf1ddb00caa1612019f80f874eec83c4a96c618e51ee00aa6e674d99ca8e3b706b2cc7bd8178d263dede1053feab88f343121518cd1da022d5f1cb20b393a5e049deb20bc79748c80278f251a09e668da1f7fd85ddc5809e45ee676ee9c5dd44a7e3b2d83f254f6372c3d8530e582a49c09be90751b3e02e223ee29767a1f85c614acc87c4e63cbef76c14351f3b612b5e05489c6fd2504d4d332c9df1ce97b1d67a5f824b5deabf25cb9c4491cc6ca06bb68e918c7390d9008fa48d1d265e122350729268ae13000745470e72d8e8525b0342aae707b0947f6e043bdb690e4b60c98073672374de831f86ecf1d10e3fe5edd7ef42ebd05a2b2cbd3b827a8f9be17cb233b376a06b5a8c71afca9aa546e44356927519d0a6982238c9654f7ba7536c8095f89dda3565919e1f3ddadd21de9189b26f3180567d194fbd5d82427afa02afefacdb6b5831cc8abe94d75e72d554a640fcd060ef97490900e947ab729b538da71d74da302671f3c3cd2f50c036adabb65a9ef03bc3eda83cf8a1afcfce1999c80d20dfa0caac52f87ae2a08d3419b7291f02f0b67e8f9939cf726dc1b7972be32a4f8473e0f4c32acda82e1b4aec20cc90f6e090980039c9a28279877f20b1f006175bd6c5e9a7e0f3d82d69743e493ee03720df7dfb58a7ad37b56724ddf3823f8fa8f3e1876b6c5461c978ee412d3e29a0809e5efed52024266e1a3d91db57f30d4c87d8a2b39dfb913e1c3642614519db3085420890490ee741d53a0dd58d564574e95f9184bcd5b665cf3266883e31e860b72891337182eca956882628f630b956b4852768d8533e0eb25da89cd72063e0a93ae0a0d33264ed50b60a22107d1f3d22baf227d29b64bee0a77d81046620d01f5556f2c2247bf856a9a3aaf9cde9529b8024bd9298d9d8607e3acfee12e460ce15ff96f80e2980d89912bc59f4fcb009cb9bb81f389209afd08a87b3b6b620699b73040a52a3d9fcf47ddc2c989c97c562093d16fb587ccdbf213e5b589220f1de605b60ba0f96fa57fc1ace1d7b7ca9a5244b26dabb1ad40bd27a10029470bb7bc1fa88d006d36e47ad718a0a28973c79ff48e36252d06c107bfdeb6183a018d5e90b8157378828d65756d0f3b5a5484a4bc9ecbe75a1496abe8566dc4100ddba8d4834e734a947a6632b2a570006a6af8977670d531d439c4bfc10507ec0f314ec1d767de3db65691f8d0ee72a57fff0349377c3d72b97463a1089d46f802867feff24d6290579f914e99aeb54ba6d74923837a8576dd0ca824655c65920b2fbf8f53025a5257f40d17b2f603153534276ee2fe419133d7d2520013d39309bf1d99b4298265c740dd255a62d3d217457bf7a42c3def3dc4f5a1ca1ad8cf0b7628562a04017aa62b1bf0a2db123774c7e62a114f23bae6648cc509a12e9b02c5a0bdbe3d1472386b8a91a6d2e5500922dc17160cce09b87602dbda8454ea0f22e06b5d2eb4a45725a4d4874a60dbf27291bc880cf8da324a7c3b9f17df2807b4ec13990cb73cb79da4ae2cb59e2474ae9ee0c1374e24b8e5a9292ccf5ff70898d74fd145dadc534a23e159f0ee140ef2637b34213624e320f1a9a30b7bc901bbfca6a015b1614661582ee5d8fef6fe5966b888312e65c264da1a53d31753042d9e399092e093798cdbf869e5cd1112ebc8914644692051e00b2af309ffb704577cbbcc01861ce180b538643eac4b48e1b8e411aeb0977e431fb4593675040035665b9b58487d4fb4ba03e598a732cf31d9d238c37c0514e60ecb1fb858bb00457fe59b5ccfec260ac908ece5553669b1a0d0fe15dfe8646fa5121314f2150246bef4ea70cc80b102dd0f1b745a3c13b40267fc9d6b6fd2deae7df5353005002bb225cb1da5951b855ebea1a810911a249e9ace7fa2440d30fd8289958c990ee143d416f1e041f1c06f6ebf4df9dbfe1715da7f8d15ca05c0262ef52f490504ede126f84b4c3de5b3f1bd17c1d2bf4d2e674d9ab06799af18709bf17806f00390f7270df17bfe0e7938f7fe96dbd773985b5413f614e725f94336b6bf9f3b04eb2e72ce2d1815a850208e89cff26ac333cbba44e4ce72661bda843ca5c11801b19b572da8fe6160e37ac1e39762bfaf6f642020f4ad74d4fb7d01955e767703c377362220428113467e940bcbcdf4c7775f9e9c2da73ee46fb7d3c8f12e2e0c03aa37fd68e5c715a743fe0dc0f53bbeae1f2c5295fd924a5ea12d8bc0b00b07b8877fa62f6b914640cfa85594bd2cdcc22870bd1d3857c5f0d9874b6e888e0d3c54d7ecc6ccce1a0c66130ac95e75ec57124492737b0a0334163cc4c2c41b098e57dc3023e3cb4523841049c04ef22d84a93ed04036e4ba663d7e7add770e0ac41417584884386e5d15185f15557260c777d2f62a50ee09073b992d8e8e1703c5a97dc86b074062822002b65f7424055bad91881baf3a1e9ff10f3369d30a03abc5764b9a8406f328a004d59ad6ce4f2265b332d668f2f3bf80d23c5e6c700780897193a2cc9cb545fc9d20e4889ac2b4a4549c4db0cee4cb01abd34c0c1d02972a516d7318e019577f82fc12d966f94cac643ddff5529d290855e843f7eb0be5c48c50bbde26b61ef283eef0e56efa1dbd3e0aeaddbd22e2fa29f89fe12f08d17ea544186dbea6aad9b37a55a11d3e0ac1f30ac3b1d20b994d0b74be8ae2024a7c3da5a201d67aa593d4a23572f9ae5d8de448ee67c9ec0721fbc956153c046ed89a1ab278432de2dcadb2e33d9b170dc29d63bf0938397091ce89a3b090076e4ef1e02ab3c0facc0d848224cf69cc83dd4e0bfa63c33e1ecfb2c125b3ba08cdaa99120f178591519028590c26300a526cd99ff847d1b250724374c6b11001b02eba931beb8657f520376eacdd3923c0f0954f03b7674985949c32a81dcd003bab603f8970c24862237457626e1b78eb46e2688391e3c8eb024adcc250430a76dbd1124cad0c84767ba53acca8873bfe7a65e0ae07ec79a8ad748e56736705b08c8aef348b847b2d3ba76c1c9aa6325e5f856d3c333256eb024bcf2936d90fe28a3067379d7e2dad4606d36c8d633c1b94deb17ebaba5818ef6295faa7810e8334868bb921d485021843f8bae510a847dea2f0864d7bc8443ff77d5358510e7c7da1a375922ccdd1623855fa2da33ffd96720da252b75f4b70d871e56556042264752437dab6a2c4ad8b2c0d138c115a445102b1e1f1a75df973648aa2b80bfc7e9cde53994d36a5b9787b6d250d41ac6bb3969225bedd6f45b9c86470600107c9dc69de2782858559ac19cad30de643e3a3186dcf8575d672575074e3f60c8f96d5976ec9e0d0e5584e884ac40e694149d2313c2cd51723174baedc0e5b092cc551d738fc8b85fa932a8964e5d4846a5868029e28a2a5d86f9c4cc09eaa04891c1fb0cdab87d4c1b984bfe0132758cdf2c1626740976e62e1acbd41a3e8051ad99ea2f3896228a712f4495a0f585f64728e766d78b005f0300e54ed44c40215a371161799c7f7a86735b81bfc498dd54652486b97ca7a9244df11aedcda0f7d29323ba45d142c0939faf824feea61f7bda5f1ddd1ede8a3d48fbbc8242003f8c62bb3c7a6a4fcb7115da26345a5206568fc79ede04b24551e1eb688575a01c857d02188066985d50f1b51a2abd805df806e629cb5ac1388dde90182892a04e7601b9e87cefa1a8e24ef704df4e9395f8f8c8181010d223e4f04d212258b084593a8add0390a561110e592d5a234a8abe7c004df240044c951335a5c0a8a042b2cc3e7501e5c425a422f0b10390b9770676b824b6b674512a64bfa13c8a50dfabdb5165cb1da939a3a113e57a1f53a2791309beafc1b81b410e3c9d7c3280edfb8a6f76ed02eb926da7b9951d7b43fc3d3715ef26fd6a1294ae33ba27d79097be068e0f1362c0afe5a520a76e36148854c7079eab4b378362540bacd9f2208d49f1b67d9248b19cae0e36709d3d185287929fbeb85851bf3f844350a872008d4083edfd5e1bb950924a57521d7470ce5c37cb013cf23a0a3c46d18649ee9055dccb5b68090ba6e3458c7c470932c55606bdf53ab1d0b3217a4d078b7e78d04639791110d925d3af2156384ad7ab5cb2c4f1f335d71880c5eebdf62966fc104a2f7c5073862cdb9e9d79d1461fa825ffe2116895251041e42dd037c7a814c04aac63ff433debd8d2e9d71c81f4e517c5c996d54ef0fee06ea0468ea46fdcb0b447ec5998ca4f3b344fd55c4d4f36d2cddd977ca7f652080d1b054a404da730e6f4ee818d044ddb5cb585d71dbdbfd2cca62dee35667f748f12cd12443a39708a21d7e5a60a2b54a20705e8e8562b3300a4e9919ee655bdaf6e261fba620590bf6ab4a9fed3cdb931d67a33be7318852ccf769a429f01c3b7991e0a8aca6420aec5a93828b2ea01feff35ac8312c63bc7929dcc4efafff15d2ae0cd2ea3cd40b62f9703864396f030bc1af2ff65195ffab35984cf6da7a4b829bc7085438b20ede624075b2476b3d3e6bc5b1939b56b30fccfd5d31de658d05f9240de651110ab10a855a02884b6158d099f34e5a16d83f1b396b2367d650da2d810a4659a5021e5761571682bb12957f3649853af6253a3dd29a6561378af4731e15bae08a0a6d2ce2d153f1d621947a76b95790183367488208b2c9d34d036fae06bfb7bc0f2a43b724abbe6b2a2fbdce478a8037d30bfbc6d499e6be00692066aa31777a07f3ab773571774d05e204a605508589835f858463cf85247540931cee5ed43b072254656e23e85911f56ab5ba67dc08a60f8298bba9308c482f1ff0911d99be076257ef44489d71314ab638908dba7eb1e804b0866579962f23dff2a9bd5ce40690f55e3b18cfc92ce3e18a8e7ed7e9e8795ed3958cadc41d921d8c12cb555e07b5a7746258254acc6f337a67f2955e3b1affc70a9e61bd1903697e1c34b24c06334225935d383f9f01c5944ebf1c9a9b303077a66e64f7cdc0922640a2793e05981732b478fef5dd2c6bb8e57a9d9ca540d215591844ea95a38ed099ce33e90e72cafccd45f642797451b7c45c9753f7ec55a2955a6cb327349b84e74a7f9801bb9048d58e921009e96428ba1f0eb9f6c24d974f480d49a47f26387194f6a1037b8e26310602c472229bd1c1d5e2ac82b461dfdab7f87bab960017ea8c0f450e14d04cc814d48a7aae7f8a5197eab341f2cb34514b12d28732e0eda6a6d3ca0cdcedd635da3a3472cd415ef030e8cc63727b940249b86cd7a228e895064f530ca5b2f9d82ab7c6d3c14a23c8c87090ec3f54e959751ed8b9991a08ebf6242e03bd3fb00f75d3be3a4a2baa375e52d3a37d23fc2f1f4e97911d2e90f7cdb8460fe711a01f60a1a0676070c19c8c18117cce77cc780d0e62faf976186bb75af20f468bddccd0011a03cbddffb965d872ce024eb7566be549ecf8cc0c769d01820fda7adb90001368aa3889756a9e8566a61ecd2c007d635d5e7d43d3a6afab27003eb0fef320ccacba1a9d8e6329bacac599950a119ad163b4e9a7d05c98fade072f8ade6e81d7c6e2a158d6b1a726821f4d7ea29db85237daabd7106df86c3e005f78df57e0a10223ad59ce1f4205d22f3198763549202e86607766e965afd40677e6ef954605d7c221ba2b4af619462b4c655b689a46e4ba9c6c9fb0985bb402a4fabe1a68d1c6f5031c3c5327cf3aa43038dfe173346de7e8adbc460199b30b1686229336f03472824a26c5a56ca874e42599f272e265d7d99d40d6bcba78058ef96bf4012118900afdab0b14f053b3b3ad80fd7e4ec779dea1bcef525936072bba238f55d8adb5718491e7eb4ec21ca1344ebe66312df73a2c5c536bd67d09871d31685e4960108d4928ee2030ba5f18bcd3c471218324e39c3fccdb35aa026570f655c948b29e1ddd346186aed70068d642114c6381372fd53e839bcc6509de47b3e81d5c1807b5a831c5660bae69bf3b318521b8785171857b003dcd1f05ba0724dc87edc1c46471c6434185346decd0352e61cbae43408e43826313ad0ac932844fca16eb03042c86fdb3ae13fb9462840e1d472cc11ca0c584db46b4087e79045af98114449d74a8b6271a3d1a4b73afece82200968dd39011d137e709780716aadfdc13b47de629f7fe63f71ff9f6f0675045aaa6736dd7181801fc036a300e851d937bad7ccb6861d958cb9d34cfd7956b489003a57ea21c52b5420f3f10d9e5ae14a69be0148d593768bad3f7c3224f6ff8328f20e453b5cb2ca500baa5fa7a60e89d8d703b61ad4b2f94084070ea62b8bffd4b269955fd0c387105dd5aad61bbbd714e1b0e7614695787cb028264fee7aeb964c3c41c4fde97840b7fad5c2c48dc522b3cc3a23d65859176aca57b802ddfabbd7348998a0463190330d23be5b242b452cf34ab7dd18aeee3aec5db4bec399e8bd1fc1bb6efb222017ef093261559889a2523d7a5ee0fcb4f909bfac0532f91c61f2d59b9db15ad0e40b792c7e5c02b029787be3a23dcfa7d4a3a51b8b0dd4ecf80e3c8c413c39501fc7e5a33626e1fdb909d17121cbeb97bafe91f9b7893afb1ea3c06b6fa4bc20b3edd1ae5e2357f77a0e4c844ed17e99c467560aea4fc0c504b63e634ce32df06df9030c181070a874ffd3043ef8a359020bf607498f598dd476c7cd553112b0eccd54d890dcca7aada76ba13ba855682819545752c9aac20faa88d4414208701e0966727d6d6aacd4106d2317153128f85a29241e79f6efaafc99f2da456e70ad8ac85cdc25d524fbe624966905bd31b5cc83919f0999801615c0ae150bc5601fc07eab7f07374c060695d48d4732c61544d2a723e231e3087e00255a85d9e03d75976ca4b50373ce7d8986f9d12787aa726c36a83208f18532ebbbf05f6d3042cfff63aec7e65de60a4926fd041ec69aabbeb51fa70bedfc569ad1593ba07072f8f755467f610d805ce4591be597b345bbe5ec0f54b45cb97117f1b4504ab0a6766137f2845018e41175039bdf7abf880def38e741a6c24288c24ff2805810aa629ef288b01db3a860792386189d520218b73f6a2cce37e85929910b4a51900cac2d001202f48bbcd00b9b06ae773fc3f60fe2a4cf8cf91c035de5f48c4510cf59350c684b2e62a601db4c889a57ee142e1a47bc1297f2561074c857b67a50f3ec973fae8d77e805d847c52e32fcff653248bdf668323ab19200546c2786d07e6535eeb78e359a90800c01d405043e095cbe26a9dc2936275c2cdee9f2e4d0b766c946e60871896769d710052a2cb448e1645defbdc8a9b0ce70e630631000d2f6e30c65c6139ac2eb011989bf2b9039e068c499b0777985472ad8a4bb3ab0f1cb9257c60b1506cd80a28f86c79ec5028a00224ccd6653d2937d06c8f987d0b15fdb2eac6c5f9108ad72a7a8fcb1bccc6c011bfdb586d068538b5d0fcb73a0992c34bdaac1a6d09eb18b8fc676a91cb5526387fe1ffb58a16b7b08a0ade19034ee86279a7d26180cea6e1f1c2a5ade3e8a91f8befb83718553487376771660d951ef987af4785ad5ccb0550c97a9e551f8ea247666ffddf258da350523ff6016a74ee40c28b38534ba89aa866606d0351cda31729d7bc43be8fe4dffb72b0059df135ac781bfa78492db011aaaeadda42960d798f7e830fbca90a946aa38902d92cf048d843cb0f2deb5d1f8087e301c0c77a302f4e4ec8ee16e72d141fff0f2e40db9fcc313dea811315f073b298f983359c2c704f57933e1172d55752e303b4450ceb7a4d5115ae57890fb0bb0612b84d661350a15fede57968608332580d134bf6b454d1161a04032b8fd6fb5a2a5e795ad5eb37b65e55a976a12a10c5012b2cdb52751c7ced646dbe0233d8ec74af90cd8575bbd4707e8fe989008eac0047009262636b93c939bcc2e61c99dd6bc7ad627369260b65ac2f0ec5c60fec0694bfe308d0a055bd2fea967ee3c3402c60021a6da0bc291d0487d73903342c0986d6dfbf62f38f4b31cbbd54f0017ffd8597e0768ca268e4c70ac0d0325c9e06a787224ca2720659796a229fca5af3105878fd4d0ceabd0c71368178c401b70ba552f7bec8fa6f615e7b15aeb7ecb10a1b89c97e8f180b1c30b5ead8511432067b472d7a69860b3a3acc72b5b6c07f2ef79bb28948204d63fb437550fe4e6605262c2d190e54ac08efd51429fec071c24c880e878ed9fcf2255d87f70bc2ce004abda367e1a42efbda65273f244346012f38391845dbe768f54e8efa70a64d0e6418348bd907f6e3bed9829c855d10427611dda5aa942c9a1b690e46f477d007445b68d26238e5ba839713734874e2ebd8dd01b2c62f7dd7cdd41c90264be208d6cff02cdcb7cc45db311fa751ab4d0c1a94d53b58403ac204768ecbfd014a079e7d34a4f4b6797f602bc2773b08635eeba8a7c60102262f0b2ddacef2764e0a62b51838d55403510dfde0a54a86849247f22b920cf4711276c20b39f26f9d06d03b9d985d9c0e8a20ac55285bf733a71b0e13b13bf9b66a4fbc35bd3fa6cc0a8fe1ac20307a360e62db642831e8db721fe1518ee975a10bb6232e30a3267907f60982e864d3e58011626f41cc7d21f40a9ebbbd09fe764e4ae02780586601063f6715a621bc35c0bfb6417e181edd8397d3624e03630dae05007f2f9be916092bcbc7faa34eac029f1a11720e9a2ede79c3376db45b904482e551cbc0d6860e91ab0892e07b0a77429fcfe56caf4fb29da80b65cc11cbba9994b9a2eb89ee06de803f2500b96f87a8698d19ef4b7419820a1c4a9ef8bdeb43f3439e9e51da0246d406b41b4a5474263022176497441e9c5b83f57f3aa3705571fa3200b11a0cfc74f4e23ba90cd8727bc4a3971df5d9ed59437f94d7c389732921021f40d00ccf2098d1389d5c47c886490af09e0956422b388ae0ce01af2b770a2f2a52c002608abf53de6b5be6fa1d520285318d05fbf487acfd341540edbf5814e92a18043396be32eb0247f473d3cc4bbb92e8a75c6aa68ee6d33fa9dbc9178c9a2393031b255f51b79faeeb2210c98c8b4831bc875e4d6320069658d630d76502596507a2bbe6e4883977fa000101e3c0810fd2a03b5d39af06e4f24b4ca2a3e2068e03065613593d3a5c5d87b453e8083a6c1f2354b4fcd724d1e37a5abe0bd3c84805bb403747bbd14a96dee63c256f32d76a09992c0241db8600a580c0cb02748d046988eb1fb4253b64febe8219ef424623994704cfec4f59a169ae1f9fe24c0f074ab8e8e21ac88a16ddff21b48d605d341fd2a5880bd5c7c665378e3ebe56f70945dc9c13726c7c562b13c5c87c5aaa2471326af20df585c3972a91d4c435790b6bb2ed3dbbb2c27db22532a45a592cd6131322726487d50fec4187cad71d6505a7dde1dc5c934b71ba8ff7280bb216a699f5fd28bb8e4c8f2c834b8c9cb082051db0b97581fd4f7f3928645ea8487643333ff582d3a63c30a7291c95544ca40f33711d88eabc4dab03a1f27fb0c38cd7ddfa2c929b56bf1b508d3d897c3c11040774f9233ee9dbee435e40ca44fd18a3aac1c4166648ad746be10d80d32b9f0f61ba697696ca4876f9d0a329ad9db11daa4ab541ab644ced8388dc8b660344080128d682c4e49a779b91008cfbffcb29a6fd773cd10e8dbb3a2c08474d3f040bab1d82c79bda84e54fe41658e97d10c97fef51057146d8ecca813e72ac29840727bf87e507dc0369bd1fc30c48ba5869672b07cfc41712b4dd5f421052954e0654d537662cf9cf5e2c9e52513091a2b11d90c442d100848401ca63b019c8960b459230f89b3523177b6df8e6f70bec54e1881c669a146b1f628c7e7e08e39a037ab01e9439efc40ae18d27abbe4072af2cfe0025173eb6c547609ef3f19ebe04f72fb358069a5b73463a6f59d75cd20c6762c369ddc85f43e4a51fd23901f4012a32a9a16057a65fe835a6b7dda016cfc3942da8636004a48cfcc05572a858071d6253cdf33b8a3a7176cc9674abec0717dbe4921e565be30b4d4596093ed20836a94abbf27d0a6dbb35e6c9380fb64baf330ab9758985398080365186ab850a8788f996faac2e195627c2ec97ea0642439363767b0592c4109d17c78d885d0db9b9a89d569208199c6391bf16d9449edf41148990c8bf7c7515c61e61459c043d6c9542bcb022d1b30549978450c1bcf18e6e1b7c15bd483c1608d54a695a01765eb68105ef4fbf6970599a19b81e5cb07689f642ba0ed2c814999034e82b00170f81ebad71ea727c78d39db61a1dd1ddfbfda86d88bfc8851514211d12910638e87a26f3b5d03cde8e9b711d8b785decd49ce30fab4ed4dc5449900e47b10010a41d1ae3208eec9256e26c9b647b56d04ef7311a4621f45b5a75ee36c9ab0043c0e00b7fa6744ac8265d511d527714353be39afda620e60713640af49b310e990011dfaa72a9126c711b18d70eee29df8ed2ad95de35c9f16b22e963f6570e194b7ebcc4de5d65f934c796e7971fabf98a81a97d457c1cd0a1327fcbe0160d675279034271639c6128510d0f943e7ec9ea1309f67cf2a3e48d883a7a6e380333612bca5d0a5464612fa88a900199657df677c0076e7b5549547333e42a700af0ce555e61b94ef5fe79fc77b0e18bf914467e3272a9c9d26f6de76eeb55780895c903673819167631b927fa8f26cf8ffea3c85d77c8d56c4dc0f0a4504b07003abdf8b546e9489c7242f9e78d0209a2f8d2f53b4bb22ed258eb65e99077aa0fb8712f98f06c686f6bdffb8fb08dfa8ca268c54c59ead112993b3a3f9e16cc0830fe449c81a4f9366dde199190a582c5fbf6b056d5e8332dde24f190079fdf0cc45af4299adcf49d052e87e8d0686bc4db31623b4bcbef927b975e00b33ab908d402724ba5fc0b539d637b8c1ce65bb4e026dc34a1bdf6abbe4ae8d44bda29009f2ff75b492b7ac44123a8ce8d3aa59c68f8fb58fd109dc1c70dea8a9902fe00b433aff662a425ebf7f536c70670535dba2ef60cdf989e4921b74c56882ee2070de12c518ae9eccfd99723c04d11e1fe6b1095520fda8026ee8eae6ae6ea290eb43a5196b559232234c581ac018673109688b0141572bdfbca9bba97b9895d03299b7fcacb24661d17ae95890254de370e0528925e0c06baabf6acba9dd1cb05e945550d39012e20ec59f7bfa16cdd065c3fc5d183bd16fea79b101308241909d0136f7107daddd3c98f005b97db32f5369e6e4c12ac4ec9fafaf40b1ba5fa015907a374593e54b553b98b898cd1a41cc1e7a8f699b258a65779a8481852040300b2c777a4a7ffba48ec751b26d284c06dea4c531451d562f2fbb83e7f42ee041a4c3be01638ea6c289897379f9945faf2c5d2b9431dfa16f114d4ffe40da40fe6288819342ad88f9e87ca37b05bda6928439e0bed3b2ec33591a691885d7d0b1bc6ea47d25a38c90358b7af250e1c5e3b5b5ab1d1ca32c2f620d4dd40e1980621ebe4b37be3a2ab82b41e29a7a1132b8c449c8a830a7117fb2cf648192b3c01b6b19130a5804dc69be5c0f4eea3a599077ae491c432405aab6f9c143759980e78902d6433193c007ac8a63d3a72310beb96b22462b825c98a16d40bfddc0a04881032e9a8864f0f0c893a215a9609464c571732422e4d0cd6243eab6f932a0c4c5c194b42332443cc49ca29f1f1fd8f232d77823266d5a96d5037624a0c8f0e6080d712275887fe494edc72c3c9e3eb76326ad9bf7776dbe5d813db28f1680b36220141ffee1431b820ae0a3444428e751f0391f9f6cd12a623a2a08465090dbb93332b8eb42321bdc26b5da95a65616f2cbfff5d98ef7b885c6b96dfe7d10e847c46538a35606094e4f7bd16517595dc4003a475d547cd28517f03b59b8e048b62c6e56af79eaf1fa27d41b5ff06c41038cbd7aa821bcaf9b2cd4b3969840ddcb6747056feeff645cccd1dbee66d8d0ce6b0a41253342829e3224a8c01270b0ee2fd4dcfb86240795829c81bccdc581b7a56ccd507179bc401a958bfe63d02da344f8c861175cfab376309b3f3a2bb93700664845a0fba8196e4928dbfec08f57e18caa89b8779debc40e5741c61414a3c531218605b58ae839fcb6ddc4f078a9b1cd39d226a44db258161586052fb18234fae6e6650080bc75f913ae8110c1fae7c298542625cae54d1e2b56947f10d83d8b8bc77fbcfb554acea7ebb7204083b5aa8d08de3cd56012d9bba181701c2eb9e0b38a211d198c062fa3de6b60fb525e8546f8fb0fd8b85ecbeb158c69772dd2e6232a94e73002f3ddd060f850dfa0a8690841e3fe95b6489f95627296641f2cd178545325a347012b26f06bf020b28b628f3757c298692088e6578b819d89102af85f0573d42bae88f39b8f80a5c9ba8e70953d41c1adc920fa15f1d5043ce485073d257446cde7b8b1f2aaf0c00d40aec5d167aaa9b91aebcd2daaf69f10b029c6206df295e6e4a02f0e3ab0cd9322ffd841240a1c6ac5f5e7c03ca4305109cd9cad64a1954dbb942a961350bd2f838132e0ba9f3171e2d883c4e716c8ebd87d0c55651ecf764c75d6b91cb048259205ec9b9bf2bfdb58d146a384d0fe9321e3435638aef087852539094750db7bdc1ead3c49a15d312fb18ad29387f31d7005cd9ff20cc224bd18548f19d0fd650edf45c64231ac75b5a134d73b57cf0a31830a05f51d3091a837f04de310edaeaa85da5e51a503417ca33a0d2898a7029cda58708e142be3c57b38b3a5c0a1e8b9f29db9e2d5df07118c323c19fbbe9b718a509f0cedc2ffb69dcf2fcbe0a359d147d62d73cd0012b722636f84eddfc4fc87318778f344ce3915e20d9940290feb0c809e63f5cfa30bb929ae24f96767c7b2f252f9d14bcc2801c285cb6042caab3d7830561612f44cece02d2776f96023593a1c9770dce5820d98fe74501ae4b82c010907d5067ca73922f665014bc57edcac74ed57901de8a4b09188f071b96d2fe6c62c1634306f9b0acedf1c364781cff41f2ca3f8d050969e08f980b2445ad192295e8dcdbac7e8d9a41f310800231e9b9bba7e22077be8fff02010e53654ea6b22f4087d357acbac0901d7b05c8f6324456f14d130cc0580540d70bffe76c31fb7528dcc3659945f8a6e86ae473e20dfa3970eca25a4bb9f8c57f0ce8f32d16300c41a86cb6927faa52b8485d8588be3b8b6369383cb153ef496301f58c38fb79e17ea9383100e6e976bec2b95f6ccb1d87bf15455010e31e12d1023a85824bc88f356df5880620c5045cf064ad98a1d0e8665875de11ee265aa80737688953d5dde018c5711ae198e374ccaaa7ab656311ad6b7521e720b1080702ee346d1325307a35b3e0cc1c8398b6e17a7c8687c77046bbc01c411cd8afee0ed3d8bb20fd1af6e93c04c74635ac7463c5c61c62e5c061319cc5b6f1cf675105550bdf75c0918e49516366f90c2c29198692f53a761d74176605137e1c4e4e0c562cb5501ad5603a8fd3e321c0dbdef77b37a7450eb1fb95bdcd59bc7694d406b0028015adc973b349e78af36fb6df22755f8ad5a721a68b15e7e9c8c3d8dd0b41774638554f03473b362539152db99c51d9f07b8fefea492a9156f86cdd3f0c06d42e62347bd994740c9dc7b86881876c96405aba102e1dffac407b25637b0fc27d091ac36bd9e8b36b2d194a58295aaa255bd9ec3ea8572974c3731f9b65025092c9d1f527ffc6241de1b5a4ffd1aae05381eb6395ad3f0e0d96aad8e92c03f930afda0e404d2c0097c9d9547e3ba26a42b5222f457c91c896b7d2a7d0b50ab1c117528db75dea98cf782c3231d254ecc98e8db50b472f5d34f94c1f6a1b056c8376d6d276d1cadb5f90b64939017431a190bdff5526b91ba50277ee270c0e7ecdde9ec5918756aad99d9d7f11381b18b5d490cddd427c5c27ffd06fbfaf0a9f98b1e2ba410f57067036480ddf8a1215e5663c3666e7b55beab0b86e8f850caf34b4c41d6018140c830100dfc687e077a1a0873067e529f7aa6d1d35695905ad7979efcfeecfb94237339e980b639c2bfe2ecad0c9215dea68fe7dc30fac0a4a061c8b9839cd6aab8ba6374e07939efa7ca9ad882859eb5dc3ddbb9049340656395539cd0c66ed9c69379bfc37b0bfe54ee673ce92c0faecb1012388ba150da225e4767bba577f9731f1c246c5c2b6b40b9356d00d5648ed0ae00e7e7de10f1c5f382e932ca83f3f0600e7650d81ad66a256bad6ddb140d6bfaa68c9e0ab0f56a4725f86dca6711919948571d83f8df60ce019b2e0292bbbaa4075a573d40f9c669b2e0e2d340c5df54b31d35809d536a6e17b400adbc86e52ec393e3e500d10b768670a6f260434e42a06af25c3af7dc517e3bd750bb8a8e39c813918e3000bec5f143cf54eed5ffca2072917bcea8b5601535a6bea831e1b0d2a3df1220e37b9cd1ba9a7c7e608565c71d4f564f48bb6c72d871c7f0c03978507549d7a0ac39871840633b4379b18fec15f7771a030d5da041f4a60947fb6dc00e6044309edcca30737b242309102becf04c54d1e7bcbfe1cff7e989bc0860917599836009f8f87a4b52f124ac5a1e5ca3b41979338eff707eb4c79aebec38e8b3be821082e2aa63efb43af424d890bc9f9cf76043d3bd8f67a08de378db71f95f83c790daaea1cb1dd95a47a8e912aa8a4bb4504689830770276fa00fee0fc919748e50c11948bbfcff6ff38c25379f84af4a6522be26c2842ec55cba9a5592aa51afc07d04f0b4fc580e79822b898aa8e379cbbecd95cdc73bb715f673a51d53cc0410d699d97008cda3172603982c2bc777840fc72a37a2b80e61b7ee1dac423692d0f90134e52d3336e711d782d8af306ae4278a3b29d8e64e5e8b607891ac4219109178cae840546f679f03ee2be4a810bea20072a4983b2790fac873317045faf0f8f25996860dc3c5864ad4e7f794e317e602786107ceea39c124f82157433cf0209ea6f705079bfeac23533f658699baa55ada178f295e9ce06fe96147a2de90d0ac61c24141cdc9aa1b264b1602780bacc2154d8eebfeefae23f85f458e3f1034e42f4b0c544b17f976710311a3a55313fcbce0492e45fde26c690b9434d4b07e878a5c6cac2a7c29029b094641fad5ec41222d894c3162961f692a3663530066022426ac35b50f69cf205d5640b9c31929a65266db38652610b7129a88038064eee3e46f4ee3b7129ec544e2facdf59d8c376f265463f0b0975da9c9799b70aed3ce168b84d754292f529b3362d2dae8afca0c050478eb43261737f8d23f603b74bf2b702ab4311e901d114f4ef4e4f9585a5a7fad02371e4678ff543dcc500f789eb1fe2415a491df39d8b7cb51166ce374f0f8cad14437ab35688adfaf8022145e2dbbc6a84d8964ddef130a7c2ffbc02256ecec1b52c542c1a8cd3b5ab0c93c61e4cb4ca9a81e56329ad28af8ce9c89e7ea521aac80fd825b8841ad6e5038eb65321238feb1d07e161388bb06a767e638aef203783ddf084ba2ab2a7980278b75cf9216ad0ac2afda8125cfa32c6313be882135b559ff76516e628fe380dbad034ab574705e79eb8391b017c0370738d0d9a66febde3f758b9d71c3ffa0dcb346636e6026b6397054012801952b0e0f434b9d997f4061b023ae9093a8000937896ee28277e1c6ebd4d84f5b3e691a6e7e967ad87adcf518a56e7d2261c04d33e171a1b5904c38e7a4b5cdf178dfbc0305d9068fc762ac473a50ff9ae9f0addfe25a85f0635eb3760eabfac09e53bb1b842dae5462820746ee7403659a100a970b18adce7965fd359144ffbf4e063520506e4e3998951412442b2e3546b0a3369962fef871842eb024db8d69894fb902eacb37b74796de84f7b3c79a1000119c737359d418ec981c498d8742e8f5b254d849dea035cc6ca2b822271640a0b4df2574236813bfc227bfb85b38e08ddbc200d8e7a2e1cf70fb2a5db35fad9041b2fd5e8af9570f7f9e8f1fc6d2aebc36b75bdd2adcca93bcd521451be76aa0cd52edb7e564862184b32724831ba7172675d01e37064959bc75bf271f353450056b97af512a3c216938aec074e4567de0be2bfa740d633190bdcc3348b21c10da41a1f12a62d892d5c1d96eb33e66bbfe4cbbb05c34dd21060b95dbe9a386d092813bd859c9c3fa49bc5ba26bfff6045aaeb535efd465ff60a04a9febf8f4b0ce8ba830ca1fffea442e78433a57638cbec59434f23aa5208714c22cb0b34e60499a5caf0e62e41589b2b15b071c94e56195cf2b07fdcd89618bcb57a1e1557030dc9ccbba8fdbd9e60c6bfb76930936b5c7c9abbabe4495d75ba985287c7dd09925b6a358530ee325df3481688ff87605daad4d23f097ca1c571ae091fcadd002856cdefc5d7b0cab35de1881662632eeea5979b47949dc7d65358e318f190069d1f0f44dd1fb558d57efa5f471786bd845eaccf2e7059e87d29c3856d1cf205464acf498180ff5e218d0595cf644f4f3bf303c5fdbb3effd1c90e595b3c060569ee17e0bb8d49cff66c3e558c7c6ec81ae342b5cfbb1328e25a78a1c003c104de9ee7e5913032f3ffd21cca498ea14edd53fe075d95f71f5cb7fc676f930c02f61a2c9d7b40d1dd9574de2e1776d656dc2a4e7322f3dfd4ad13cc8b2e898b024fe05cd3c0b264b5db17dd14a1fd2cdaba80f6615745e09cdeb8c2d2081ef30b70a587982ca30936b760f26f8b228fb2371ec79f4921d38b302a6cb0d6071403c4a63d46c2ce0361947c27336878cc061f565e2cb72938a0a4487f6e709b130202636a05b474cde8f62623aee046e87348776147d6c7c7c2ceef77f895f66e0ecad6c024e8bc9b34b68e8faa533660d9e376da588efdeb8dd65192c99bb16a08dccaf16f892b8d076b38fc9fb4c4cf54d13c586668d7b5eb1325fe47b9a89d071e16ab01212bbd0812c87cf75282a0f6c1ac6d34e804820f68b0e9a7486f2302b7e722c6b98630f711b8f1c55c6755881d16b65c282a2e192c43953cca027a093f9635876f8eb961eb66c3112770b502d4fdc85079e54ee8591f779a8365c40fd1af899a5c2591be770b53480d6b2592c2b0eae3d8cef7d839ad28224dde9c022660fe9bb613c7c017cd1475bac6ce591041801a6fe5d1c5fc67877d57e96f027f4ffd3934c50ac29184ab33d71f11b1c6b8403e36641a9c63974280825b6c0f1716bb8c8380408e69f9b7830d26dff66e3e0e15a4e708307a1fcdbb8232a307e2eac786a960b434255c6831ab259307da6f49ed73539fa93a6d5d74e4179a0a042022cce8dd476d2404715d4c182fe4da9778deaa8f6aec06d07f6806edf9057ece4c749be108d1837835a9d819f875058c84d67703700c98636ece27836b097550d9149c53c8e698ab40ae25fe6a2c54244f16a8728efbe0e447757b16f20d8abc7ca15da89757a842f0ab71efa7238022ae58c291c513c353a0635648ff0013a81290c33f01f01f6b273285bf2a13f6d6d793d831bfaed238a98fcfe6410bf5dd34ca375928b2b95a786ff6ae8a2313a41ab23168b8398aa19d0b8ca2f4030249a0d1ea73f3babbb91fd82ce679fce7f6e1a8d4c738d3b6d3e8cff15eba06847709f93c0f6fa07629dd610b4eafa1f576ad777b40b3097640f7adbccf080d728e4459a5334f72af286dd98d41238bca6ee9f6e792bb31e911009620a3e2040be69371630e567c9eeef056f6b689f180c90c4ef03449a8752450209d371d0c3e4ce4900be1352be118038a5643d12011890f77832eed373695c4d8ce7f630a3378fdc41283470b0587f1623f217aaf865361fffe74680fe6b254df4d30390a6485ba95a21920d3cee896f77dca565bca616b763fb126d015ceb9b6424818065abc3e9d9e56c3df84e2ceb781541035c75958a34597dbf7b17a26fa6f001200 false +check_ring_signature 43dc53822211a3833393d1906cfb63667994e7305ccf4c2d0055f075d0250751 3470b14b83281ebdc8b19c1263a1764dcaf9e39a521dec4f3ef5677e3c4127ae 10 9f4e0c88979ba80cb720bf0e511027c4564190ab611f45c5da1906dbb18234e8 828e02acb8e630bb66a360bb31172ac8cbe848973878bb0c148ed6c61bcc8d34 9d74c78a1672f2118b09dc9be6816ec7de162444b3e7dfadd60a9882ea9a9de7 d72538ebf7354cc48ce68d051c9b815320cc3a52048926f01a0cfe753067f196 1b59f8bd11d0459b0e29250e018b667a123a4e6453dbea2438369bfa7f7ce687 b1d445f5bc868a642f3febdb388c906242b7c39782b9b2e12b4035d8cd7f94c3 082fa45761da9951b0f165f05497e4c50317fd2220cf2667ba16bcd4e1166f2f 1de23ed8df812dd0a1390134d275f5bd9add4b18376ceb8977d18358947128d1 391a00e04f10ed3101f651eb203a51c90e8463a610018f4511b9346545ce3343 38001c999c51986bacbc91a28344084258ec0ae4c163d0cffbbcaadbb3498742 61abc1e1428f4a0cd043d5e0359f02b3b9157b77e809d196b07d533d7517690cafc7d6f78a097c4d5de910948bd7cb97e153afa17e1ff0cd3676004953cf2808a0da14efff5ac1824822746c3ebcf24832e5de27092de89ff73e3b3fa01ddf00e8ef4a76585cf82f097271b06e265c3a27a8b16ba293dac1b68fac78d56c1c08ee69f3d26d47064599008e5241722654dfe0d6d04b76cccb999ca6ef9ef803089a548986286f16051bdf55cdbf3448b59ed9ac32109c969f717685d5d8415400445200929f709f5db4285921faebb73b5841f98ac44cd4226cb1fa40c5b5a509336a8941b2ed98367c82b07046ebcacd04d42e8293269eaebc6537f65bcbb50eda579b2799abfcdc1120c0d6f61bd3e22b0c45f0754baaeba49d680e729eff0503041756a462621715dec829730ad353e66c8cf8c07536cff35ac8b435e31d0209ca9b4de77df92dac8b21c46ebeb8e58885c6bc1dd823f8dd3392755e2feb0dba4754c68925210d5d6aca65fd437622bf599960a556ff288a64a09c2bb8b3017da60574847967bfc6a2f1e3d2900dad341521cc27138ce92116b4582c22000dad4341a264e22b22002f4fe031b0a50ee6857bfa5b537d2ec0d4a4b4d1746d086ed9b8bf1f251d5a6a16be31e5a84dde2775a37922b3d7310bbde8dcb81f5604f7bf09adbb09cef1568cd6d441b26960f5e8d7c8452077bb35e5b4b29e79700aa0a5055ca8fe45bc612acb6ddca25632f72165801bf105bd8eff2d09b336a4e3c76a58b8a43e7218a630e117e68e7f7bacbd1b4db366abdc97fdc2c448ef860eee39e35baa1a20bae24514e940c7aa165dcc1ea883463834ca71704b8cb74c018b7c4880030fefe0ff654b3a143c7d9763e4d6cf4ca61d4a28122fc4a5632a0f false +check_ring_signature 4a7d6598380043ac10d21bc7335bc3eb285357ef4e047d4c179f78c3e040aef8 a021590d705c6b6431367816f179d735b3227ff637c2d3f8ea7d069d2314aef4 1 a1f66e49fcb248158dc8cb73185f9d983b1b5b3eab076892437660a1b73ce019 3e1b59709caec6eea686c68f472452a9eb6610ed40330f3a4a6de5b944cca50a61ed99f4a561f5072b0c0455425ac057664c4fcd9d724c37289568a45ffb920b false +check_ring_signature ae3e5175508b8dd56d5381c24cd7906e8389dd6996f3c784d27bd7b452945269 27228f6666d8f7d0d67cda4ad01348c4ceed2a1db213c93235453b4d2afeb48b 15 adacbe47c850224657c4e364a9f6302b4e4aff98b9bc98827d652534f5199e37 b878cb365526274b2cf0632e0109a774879003a9b53219a2fdfbb6659dc22853 e89ed5e83f2b0c6c39f1aba2aeddc14192f17607e83f6f481727fecc6c4bc946 4622dc710847a9f6270f645ba8319c8d8a4dac02aa6dd635464dd38012ee9495 9a94d154ec40d898cca1b6653ea86ab59667c5a945dcaf2ea30de818c46df7c8 850a5f0973f57e32f455474a4fa57b567127a34de681c6bb5b6eaba59a8b86a5 0b72e8e4b92e33a9eabce7e75153284ef81ee0c70e4ce2376c116495dd484c17 c64e144bfbbc060e350d157d9b8674415be875e94e7fec061d4676ca95bcea2d 4a1e8e59a58b79dbda22aa6a603f630a63a19d2963ee9f11f12b54d61088e264 4c0757ed6c0eec0c28a34df47703e23edf498d33ca3921a465b3fed3e881da9a e26dd7f46a6760e42591c55a2c101c521b6a78f1122beea4cbdd13359598c650 99da56441a12a92b8b76ebc9cb4dbca4647b2871fd3fedbb604f8101dd8624c7 ec8b2614aefae1c97082540dc7efa9ebc1cab27babf61d80653c5befece67210 5ec5945a5c2032f8a4da417f847e0dd9543eed3a0f50c42472ddb0687469a5bc afac5c77dadf2ef8c43ef9948de1f348a925ad4d6b8c51fb9b7df5cb1684a6d2 4adcba31685a8b74ae357bb9d871f4cdaafd6a3c639e5f57085f6f397c8e320188f8f2fab4bae4ce9d64ce45eaee494d11becf5aa613393718262efe6d38c5015cfd3d8dac10e2e79c36b23d454e8771dbd157d2a0a8257ea985cea66f936a03ed31c35575db2f5070df5d9c6e31ee6e055da8857fd725bccbc697a43199ab031dabee2fd9bc35de551b1fa5e5e8226ac5a503ff02e328e9ef020e92fc0d0a0cc6b3f85f2571a2538dd97d381e9010f017c6755ee414839b556d63d8efec8907b4225dff7b8939567d3515071f01b58ec562a224aa39a7b0800d4f020507980c72ae38854ab3acc101baf959fcca16f266e5a147bbc105f37b5b63b90eacbc0d62a91698f6a91f5aa1709ec4161bce72f6655f6d6eb7737e0e91f46dbe96e8028aa4cdf88ead8dc88e7ed5212e902aa79103f70ef5cdb8db15f60cd3d34b56026ecbdf865a10723f4d265f5b3092f043026004e15b8fb9650bf4b2556277b203a2271cb8fea8844b1bf511c3b6e175c5e2eefff875c3549cae814cfaea4e970eb6cb16306ce342f85623d369d09639df834ff85de116536e30ece7265a4fff072c8def70956c876e136b376ca20d65f3fb5c39836914d57fa2b41ab638bda703a94e8cbbc7488fe8508cf96b8a0ce9b8f40ef03018048d7ad2f5e10f7edff001d7793e895e35d65332c4a0c5afa00e77fbba6588f69b9cb364ea1d11796b76011d7185c279cfef403039b597917ab86b301eebf402abdbccdb435e5be52ff4062cbbb48b8fdf2e561cc14b8ab73faced794f2d6a9b914a3c0021569b038190084f5ea811414ca7b4de5d058cc4359d9c96888b748a5b3bf7623fb7bf6ae7ce0029ef55f858a48cd34407d78615957cd7a98d864923ed31ed6efd6e678c1ecd033c71088e5b3febb389cdf6ed9351f87aa8c9a8a1c2dd9fe1a0baaa7a6776380bd368cb05d44edd8adbcf4073c27c183d981e6cc0c83022aa584aa74c0f1357002fb054c2876420e97bf523972080ed0bc49871d72ad52e7f82c0202402022f03443a6885713d71f29ce49119db1fec15def9a42c98425a521dab28842293fd0ce771dbc7368bd3cfca3e89d554b1068f3699f77022300645ce0f15fc7b2d6d088c137786fab8faa0f7125c3d98ee4678a53c15d157bc664c820a4ecacb1c620b5601ef061c46b457a1ad72ee1a3c333714029da5a631bec01c26af3dcc004e0069cfaa3769398d4bfe8909b59fd7da8eda43b3d7944501ab2bd670207b2b6f04b6465f3e998025fb2c9a6f21342131a206573fbb9e06e7179a47a0781b9ac905ac1489cc4504de1ae9ce2d3e8c65c586de514aec9c375a451c32e06cceecae07 true +check_ring_signature f7e03999f4498e9d3dcac903982abcf2129d59255f254d822416926994e3452b 7b12a334112deafa87c3ec68f266afb2d2970176620313aef6173ce456c1fcde 1 e1efb70e03c82132d035f546c15862efc3cec7456a4d54a1e88dfffac7062dfa c460f0fc0a1e412262523864550534f9a404857687fce869dea4bac0332920535c1ba07790e62fb996118fae86e4bee01e6f61de24a53eb5f7b453cd22930805 false +check_ring_signature 400b06dc20337489c06004f0e1b02a467484da12248bfd9368c0c098281b2558 24de95918b202e9db429e675a16bf5e5b81f4f21f52c0030095b936a65afc44e 1 a26af0165e1f2e6c46a089240320114f5d6be135ea29e04dfde23174a65ff08d 7550d56363a59ee5617fd86a38e979b5f99d5acbcaa19bfc39cb4c8d169b650c400d4937b6446007ba06074c0ffd35ad08f55a355c7b04c7bfe10b4617981b00 false +check_ring_signature 084ef95bb6b42ad07adffffae099b6438514250616df26efe154837dd8ef1ec7 01ba2df27ac0dc6f8222e0db58f01cfec83fc5f2a74ce84a5eba0533cd76d274 1 18037f414b896d768300bd86ec2b5da0ccf8afc76087038c80bf7441645409ad d0250d10ddf0a3ed1e4022723fdfa2bf910857b707b0817d52ac660d5d48d30d4a3c7b405b3301191ab928d55541edcb76a3c95d82e22b017c1f953ba2ca0254 false +check_ring_signature 098157602d982fdfd74d0e3bcb637185034261fb13004721c082ae3bad44db20 48bc4bf1704d8627310ed78b0b24ca89f4cc18d13d025f8b283a1ec70255f71e 40 a8af0246f43be4d9d955a288caecd8db838a239a89ae785eebca00b09a460084 10ccfb7532cec63af9980cdc73d88d63cbfa8be4d90fb32cb3f07a28afa138a4 94c9bcaeb2eb7820b7f3cd1e49765bbd5583429de8f11c935ba06bfdd31188e5 ab9af3308510246b96691e7f447b037bd50db2b2d407ffc8b89fda35199876f1 a73bd03b09467b5af4e1a62e2e7d19b97adb11a0c5569fb3a560b2159d82dddc 2d50d8b78a4f25a64614d8036c5a853717ee796dd6e399be87c3dbfed736481b 15f0ae5d6e6b1326b20c5b3b2eb5440459f03537fbf9e93ce5e9c3eefcaa0676 467acba8d9352d51469fd42157af99ecaa71c23eda2928e1e11f4501fb3a3799 6b569ef16748d819e7e32e6a3675dee614623f4670978dc324e0513d682a95a3 de777d2347cf505f4d0d035bccb7332d5acc2ce8e9779852d004e47ce093abf8 5e60472954917ed939490678c87bf84b02f4911c8b846d5926aa0cbe1af6b71a b5b2f1fce96f2016396e64f888be82651994b1dd7d3405090933c9778b21f4d0 4ea560a431751f4143f5efe6e68e978f1bf0779d453e5825871000304df9e484 579a963106d5475e06f85e13ed4b03672218b3dee88a3c60d348d5d179c2e3ab 2e336a3b67076fe366f009c4a9d9f0009c445f19c3c22dcf3b1b0d9cbe4beddc ae061ce08cdb360bd683d6e368a9801f6d3cfeaa7a009c66a25a3bf9205edb84 d0bb8028082e28fbbc4ec8cdfe27001ad7abc1d67ad56c62a40758124a274895 b242fbf3a665ff042f7727fccac8a29718fd5428cb2776b46e2cb4696d2e7c18 01ceeeaa35a2ceb97a12c0f5c5ce4f04ff0a7e8cc8b3e835ed35c5749d956ae8 57bad3c0c6b5a9ea896fceca9ffcf2353da6b63a35be3e263fac522d7e2aa1e5 55cc6f27ac566f2366001cd520e6019e141f025da8ce17dce9ddca9dac050903 da28a451945c1219e9743879d068071cb3bc10702fce1e6440783b658b146e22 cc0cc162ab7b95b7dd280441d60e17567e8e84f69ee85b2d0cc9a9f0db4c518f cf0c4d294b3bd2ea956940fb169410fe570ff23e8ec63cb116d83528c53d9dfd ff32102da079cb00e59ab175494e3ec227fd51e6419653ba237e6fbe6534081d e33bcef22a0bdaeb8de6183ce47571583bcde9cf67f989c726a6f891c441323c 3fd4ed6f1d535c221e07f10d4a671b523b7723b38bb44b1245cf330246cf68b0 6db689f73cbef7fcaa830d5dbe111025286065ee5aa85b0cd0c93895a5f4a336 103c138a6e196938bdf24108957044e9169d77ca24cfbfc1a37dea16d3732345 97582a86149faeac030b237358f8f8c76dba20146aec8bfdfd11b6389a40e134 97fbd6be218d181fae9b3bea9fe3699d7014728888610881c7f4c14bddcbdf69 3edd0c3f3873a1e5e8a36e9ccf1d7f012840ef3e8d6369513291079ad6687190 22c99aea9b547ef2d0999c58b5998e016be743442310c2b5b1505876bdf2cbfe 5aed79fd8836a75eb0d25f033f707c3bdcb49e0ce299f777f8167f5e793c127f ad799ae8fb59b93a021a54756968f6031d8af57a1c3b7f8ddeb12e487a4769c9 72c351c6c4627ab386041e372e34babd431490717f7ac812977e6cf9a9846e37 5a0e97a3921bfdc24c91b83a03c91c5b6ae84a46b1d8aa71d41266ccdb7fa8cb 2c645547f3b81014a49bbca030601c23e65a1f77a92ef3b5e9615fb9528bbfbc c4f2fe6fc72aa35c69d4523c4f4dc5eb460994e8df8914aeddaa141728b281f0 d85d6ab42a345b9ea98742c666efab02b2b77bbba63f592ef8d340419c32412a 3ad4206f94e43d1849f961d8cd5eb013aa7c6d1983d9b3a87b7d45ab688b55099ace28ff0e9f16cab8e8dc6b1288d57628d9a1573dbb650fa5a6bd2265c6ca0d9f8be5f514f19deb7f7da41e1d5b8d3c215c1cae252c76ee26b6191ac289bc0901100539958361d50c91d267b2e33f5e31b6d354adf836b8f40798e40eb6e40d67f8f046c9cc28edd8489631f4ad40f2dc8f56b422fbf2b23e1b5f18d733e208aa0307858ed407a1b4b529ab1da5257a0a1876bf53491c6c13c880c2d409a80404272e1e91eacb66dcadae139b7403a95ed79396a67c875639675470c42f79078946c03ee8359b706bd728c75fffab965af7148ae8c535751e4bbc49e830720d7e84788d0a40d50d8733e0fcc87f6fad710a9eca4c59d3140cc3a09443017204e8d256d3f1bd2c523ba862419e4ff232706f99abf47233111ba7e89c8990660716f09b085d9dfc1108665f166e5b1fc4cf957b0958a44997c02434a9acc9cb00d29beef913442968c7fc48fb14529e0c5b1a45fac4bfc289cf97d1603187cf01124dddfae784550a2ac5397c18e988503ef40f3c87b205174fb0410c0f9ab70e4f7f4df25c063010847c1845bff1063aff59b0537166b29651364dbd183b480f42eac38cb9029dd935fca6951865123af1dc0bd58498f662f4570eed0057fa093f6070a1bf64af91af47a54b846c9db40b4805e8e43ed4d3053577022b64db0fdb14cbc3e1e11c9f9c885aaf4e5860714576f250eef9640ac602258540cb8e0d13aa597652e3904edc52f4f69f10e3d153bc82b7116aa2704f062c3884e20b077ec60e1aba36652eafc889a6097aeaa1772ec822e23b330b40e4cf4a86b20e0a1512d4b776847b534ce8e3bd484b68fd239bfe8124d24e91205046b623f35e06e0739f8ebd525a599bd7afe5d1b65e09d21319481d8ed52cf9bcd9b867047b0e086e1b3ffff0bfcd04d2cf985ff2a648d49fe7d267f3baf944ec9bee184fb80ff3cf75c197b00cece5a24a50bb6084fe21d37b2fcc3a6e3550bda302fd16870f264262c1c176724ff71d6cfa00d40f4e939bc6918e59c4b5a593b77b547d5b0f04db2154cbc32c06be73cb987fec22b80ec663d15f924c65001462857df5a808046022fbad3725052456b7ef51c2effd3d54913259639e445c7df02ebdd480032028fd4af62c65eafc185656733cb015108be4346795538ed9d3bbdc9764ac0dcd7fc5b8854e1bf16c426564429b24fecdabea4c8f5d535cbcbcb27101acf209ff8697bb99e71c40d3ed28149b61613ea1fe8018ce6ddd66cda712486520440007c76f6635221e63d8875e7d12f1007feb687a18104cdc84668e2a9c1b29a50b242d047ef8c7ba6bad3a977dd88ebcf89b48d27d3201fca641cb45ad6092f606180802adc7e573749408b9c598d79376939c39fdfea6698dc605338179114504f8eb01c091e37f24582f662d50d1a3bad19eb81f5052279a77d7962b41f582049c81721b252deba418d9a62eba78cf83eb15ac4d9a3a256699f7043f33f9c307132f5e08ccb596ab6585da31ddad98564d879aa6d34de55e9e1862e613a9fc0e06240d9d65b654b5d389d38d790096ed94af06ceaf84b172ba95f2632d97450225dca8758865b83a207f6887357d2d23f091eae2229ec27905cbbf0589692f04324b51d884ac1814c7b0e28519ba2f258b0db3db6f86e594656a71b1533d5100c041b503c6cfcf9991388fdb4a27f73ac0b25116d63dce7e437fa704f313c80369693978bd9a225eedc42ee72cf2b63bdb01e41bb582c97288d05e4d70e3d306c73e2811985fdbc511d7b2e69b3b82fa7a456f7836cd8f0ec20d5153b162540ee38e7a6308666d9882b426ad82460789047aa964baf4e49721818a710a11d40f2a71050ba20f04e5b5c9f9b6805568702c1952075678f5a8eb07481c3a22630c3337d3dc1aa47490c7e54879ced7a6270f44768f90afb64f5469922dacfa42035c988b0a267f62b1f134d427893ed0455add1e2fbd1bba4f985d8ed98274160c998034b699791ef3882d9e22f41bcb28b17c2485b3a309be04dd3fbdbd3d5100b8fc7e8d056c800a66b740b71361e9daf1950487f38b0f6103bbc9efed549807780e634cb6395f223341faf1b1aef1d9a644c0bd3c1df282ea00b7348484a60cbf7bc9d9df529ac0a7bdfa58d3cb5f876efc8ab16efa10fe926a9376a759d40e548d01cbd531755da8a8600cde0366e82d7879c7af4c2f1e0a1ceb704167b507d5b97df87715c622a4839684ce05267c1d834db74e6ef5b9fd3ac6c9c6f9520dca30c8e052d71783468cdf7aea54678a7453af88ead1dbfdcc4df55f2cb8680fbd740951be8e4dfd3824c89a3b0f180f5907ac8f3130d163527149343ee5590be0892117c11770eb5d6c0e931bf9ded644af7b3a194fc15d4a03d7d5835a80020e7528fa4654c368ba0f69556babb98d4d0bee750905991baeafc4c069c506076e971a234067427bbb516e26b790502a8649609f5ac13b79a8f3360f6c7bf10e36d197e26ed809fb381a140acdf2b3f96f42e23c93dd03eaa7d96aae6b81850b350727d9cd51e7b2379e9471d053cad4ae706e545553af4e3432b4e3efd7931cf47cb90c0fb7fabe2f8f682a5ff85496c0e855b68a7d7947c740ebb78ef60c017ce674749460064644535cfac05238d2893ffebfaf39b5470317594fef38a0043df62621e7765dc9d19d2e5545cbe2ab7725baab72b33ef2d8452d4a5f3fc20d4482522ee663ae7504940b9920ca92f6f2ede56fd03e82d8901104543acb650e2e90a920bee30fa7c7b13ffe77b72c865952212bb2f5062dbe414342ea11630bd8aca721e7e64f5720b7ab801c0d9dbb4e87440e8870e7e5e75afa3a24fd590999e8452faa2a1f6f1ed954876297190ddca14e0bfe471c2ac12f67866d750e0f654466a592baada541b5d3fba17a1c13a8c81d81c99478a4fb0a75c8aa0e520621ddedb209ed93d073de7696be997adb00710f388976a0c3b1bd12f9c964030c0e7368c6b7d4e074fcfca40881380cc2ad87296b55e3746d4bc5f933fc62bb0e6e5783fbc61356e6346e2d116786a7a11eaa77122017e10ee52dc01a1ceb0306065c24f3e6448493f07f85175c871b99e6354388da5563cf35b26fd79a01660446143dfa53eefaca200b3e42198655821ca8c0b2e57aeb30a62ef9c83d7fe106e261df906afd8ffa6c883d423e86f51c80bcd195f0e83d2acb562127a9163f0eeee0d235e2db6a910e127d8457fa74f826aba90208c2932e70eeabafdfc1730d22825145d73357fbaf581b10aa12c8464d39367294a4b763f8d3233023611d08e34ea751ebe5abdda211b247485bff725f9e5bdb933ee4dbaaec10aff3b84a0202ad38c8ce7c1157dc48dcb65b97db8edd7ef063544eeacf0558bb2bf1f97407942af4427d13d452e67bb22a2b57bf3e083e48ac313cb9170f773777bb0dbb0ede280a00aa5a068cc34953ad04a0e5542f30315a6c341ad2bc42f49df3f4970b7670d7747c220286d15637664320f87c8108d1c791b34feff31bda5c323bc80584ee02c315d9568394301507e946aff020280b24387caa778db22aaf260f7303 false +check_ring_signature ce338e9ef5d1c8341d5be236068fd35fd41e4e03f0873fc9301f1e602c062cac cee8581b240b20044663729cc51e8f88d42eeae5972feee3157f73ad3c433700 10 76cb1302fb4855a19a0ba1db1e99dc75e3bb610e537b9ebd3eab4791c6c27c4c 8aa6acaddb5420a0a43bc9a3f7df5d7008194f19d9cf5f6efa5a4b4ca3f4d70e a10ba1539a2c3b6c53de2b271e0bbf1dd947d4ea7fddff76ddadcad6251f2812 644a80a55a9ad06168a8fa7a7635eed572d921542a405c39daa270273d51eb3e 3b7e5d77bc4603e233d952421ca7e877f0c1f0ca5b2dd8f4c72b570a4fc3d69a c1582812d0a87afe38e0cbe934dfec22164c56b966e6693338ed82839185d12b e4fb5db591c6ff1653b5d6221dfec1cf0e8110dd39b9ab55f3215b5969c43b89 55da0ad1286ce531a11cbd9354f4e419adf46e3e66096468b230b3cf42bb5d72 a5635c0d8abb43e6a6ed30ab1f72a5e680fd81912138d3a0dde58a1afed590d3 feb775c2784ba330f9c7dec9aa28c8d49ffa79d3bbe826d35f1868b313157781 d9bb8cc6090de5249304869c0fc209c5c4e612c7e8d0491dd0fdc41172efbd03e775f60c6df410757b9c0a0f3143d5cd027e058a9aee40c291ec3bb9914f7602ad2e1447a01f0e62142981522bc71976594ad5598a497539e1f1d2da037c7f0ee278240baad0cda7977653afa3552ef3a78cc0c3a5e28eb759b8b4761c889c0b36eef084a4156d1ec2c87e7325fd5c92a81881eaaa920b641f5cfd53c505cc0dcd348e0c997b0cd9f33543680c2b542b234374a3e0382fac3b838d3881d6e60c1990dda27b70fe01fcaf1f6413a517135b879a06cb42f97614164f887f3e7506925a58137162759a94f34f14ceaf38c22a5f46b4c5a74ac7e761e82ec720ca0c965806e8c20a0a4ebbfef97178400592d4e845c21a6b6c8a02e7b5ec9cf4b701cdfce6bbef8941e42d2ce5ab7e44839d733c083c6d276e388dfce00d1190e803bc624d7250eb60101a0f9bff6c34dbe82679901462871f315146eb5fb04bae093d81f4b897cfcc5f7070c880a9ae2674f7c5d090f96c846a16a72b0548c5dd0c553a92ec7d4bc5646dc410cee7110762d06f4d0964df9d06277a06ec1623f1072ee766926e1122c9f8b51456da9a58957ccdaf820bb0ed1f7275d55454edce069866012f68cc824ec34093fe2e4a28dc774e43fdd7b832a3da0687f276575c0de2e7edeab01b203fbd5b2ff9d151ddc7b99a8880e1ea535cd99db70a175c040197f5b849602596bb46a2013c5631514fc0e5cd93a670e7e70937b51a25084508a0a14ec30db017fa84f2eb9b9aa48c1d5cc30402eb498817fe6b247f8854b5024673852daad947bba2284fe15200062937f90c837d23d21dba05ba252cdba60c585c94348cfc6af03557b437abee88371d3e453436f62a111bb4be44013cdf0d false +check_ring_signature 701fb177f40a200c0bdeef7b6ce8fb1b5ae4be89e5f5f7084b179baf90f2207b 4eca2bf278d63ceb72c38b67bf7b322b90fa99925c4128b02e99fefb88a80999 1 71e45559ab8db9466a96e7227dad8f8f18a19451cea455ed467b7bf506dd545b 7c6a5fc521c3a138bd493835514eb76a6f2cfcf879f2c4cc661b4efda03211039e44738ddce3ed73ce9760f55a9f4964e436881a8849396d58c7d4ea195b0605 true +check_ring_signature 4b1c88ae3e8b9d3817ca34df1b8da74c8384ae26b5d4f51594a4be8b63e9aea5 7292edaca46dac7b30414ea55447cffc2e818beccf9883ac49559d623be4415b 1 22c8a056071006b95cef410dc8b1814ac7035bf641d2a7f7e606805b0f22672f a65569d69d75171b8a2fbc0ccc7e4a34678f39a2db7d2cb36b88ac1575c51e0622167d70d80f853d9febe588ba6803514ce4e51000618c28130fb982f8f64909 true +check_ring_signature 397987ef45a6f119051341f1d8ed3c4b71f8978a2c607e5cb399c4488151d936 1ef483824ec23f038ddff39386f18ef28af78b0c203a97e85d46228d02e771ff 29 637d52b579079492090878ccf576c3d80f0a2ed2aeffa1b9246d9e7ac305d82e 857b74e39be46cb06b02805c2687939ab887ea07590fb5e892f418ba5a9ce4d0 4897ed855ecd65be8d0cc1e05392941ca28cf1e54bcd6a09c8d3b04b223a9f73 e814819c0e8822fa0e1cffe5f42232aa5d3c75f2af1a54c815c5da97f21e865f 13efdd55814066798150707936280b72243576635857a5e5aae5a31af9cdc370 27c88729ace95709ac02666adb657c35a043069e0f18c8de4ddabaaf786a6166 48222ac753d00af8e33ad45d0e5d3f575d7f2a588d0b5e64636617a405c5acab 17024566c2e22d99ed06027f1bb84d71bd373bb1f4f99fac0258ccc22392f057 9743dba21729546b08d923e326ca778508bec7770a05178d54c0faa402f158c5 cd0d8482090198d5d1cd348500dbb2d182fb6096b6f60951e01c789d55e2d4d3 858f18dd173c8b09ce13e0b0f4d7981a8ab844a80771962539bdbd4a4f35ebc0 d7ea5c1c4f1df1931186d83f05b2802c260668f43d9814bd584d7194dc13c7de f5fd270b039ca9216b02aba707237e02e3f3ad96d2ea8136060338617362e2d2 afd5c1265a59e4ca95574eb81cf09859a9038ae0d7dca0e12805eb2ba54897a8 5b817349c2fcdb5a7ada657efc76a434b33f75485faa6a0268e71edee860aaa3 5bb650983dd640dc72f51f11d58d11840184961569bcd40023dfb24e3ad41186 6b2e1c901a782150b3c6e2f7909c9f947ecfc9018147ae8fe33aa69797cc3fcc 4fc3735411e95b9e01c44faa18bd0c8083dc61f6d9f4c98e64b7378888c7dde2 5322c909022d047d13e3c5ace98b889008793e10331f5ed798c4bfce2033b3df 717527f4592801569230fd77982d1e2aef3602cb8844bc00b38c4df4d39a24db 7babd83bf5af06eb9600eea1070129109ba03a00f174412e5c96c15050c96905 04ff48234f503d954abbebf500300446f7570512fadd43ac77cd73f044488db1 97510bb0d22d4c3ffe3e0c6f060d91f591877cc095b135779adbd6678aed9b07 8fbeafcfe4090b880b73d80580b125955f81bfcc86cb77c4d1e26f8b31d6d72a c8fea2b84272d6d115c4049e986ff32dec80d75f9753c3b25d9ff231003bb17c 6a4e460b7e12dee0e3aec659ff420f09240010f09b261c71fb210dd97dcad3d9 f8bd182c86b817b3b9d4c240402ae2e220ddc2fe062b3a5c1a35021d6de8d983 8df22ee54ecd38d5f4ce972545084b5695263ad5af118f7beaeddb31a8dc6b5a b7fdc6d4296c88d0d2f9f5d5896975f6827024e48c8a9665ff335448598869a2 e562bbb335c57fd2a6f3744e8d65de81c74cde006064ed64183a199ace9d5f0a949f826c51d35cd6772e9feec6dcb7fc101b94e8509c7a7b220878ede5abf9077c2ca4dfc6d827559dd0ed3b686c83cf6a013f655f9adfb6ded3f832d7c0420849652f9e7f0eef4588b744f4737dce52e4e0c93026ad326a1a2a43b98c76f900a51b294c24cd65a45b3df4bfd95557ccba37b2377924d2a8f97233a8639ad90ab131e77546749fd19f29fea235341da312a50a05b95cecc52c5ac1b379559a0b757d0bfc4ba3cb6cfd2680156b52bef5d371bcd36eb3c43e18819f5672f6180ebd9bf5cb24c665b3f30dd694e6c6c20c487ed3f0e90cbb996a98b1957bdfc60244637200c821ebb6aa595423408ee310e8d6b8f44a93b372a420c7aab02c460a0cbfaeb0bc4190714be007542e4762e0e42449edcedc6eb29c8b7722020bf60ea83df3ad221b5d7adb2436b94aeb2ce6a7d0f6c1b2c4f90d4f0dda0fc434de0e35b19073b76fdbdb54a764fc049495b5abeae8f5d26b55da445b75197d5ba70bd9cf879cb65971fca0fab9bf18a1ae1f3b95c70922839b06d6734c1ff91ad500bae85fc1aa1db7efecdda330e8a12b84b5fe71963cd39005c40a9a7c08c376079e165eac18e4350a4753500a83451a4d2b43570e695b75014d77816ee4c37d08a499a49e06a503c5e762403fd43ee0dddddd73c5d0afec064856b5bbd45dfb0ad6dbf2232af6a0b3430c0d32f172d7c599e79ad62ce84af9228570a31017550f3e094cc6774e2c3abd299fe4b00ce01db5165a600907a3ee236b6d65eef0e00bd95f15222e69d7bb96396f9c4d45a6f44cec93f3fdea1e8ada495a3fcdf97700bb00c0bedc4b8a7b73089d5e87347210071c85d34162902ba7b134ef3a35af075d6cdc0d09e11c7adb0d5cf47f31bdd97b762f3c119c93a643cef0bae524f10f019616a7d1b32d8326fb9b2847ffe73b3fb9659efa18d2999a291b59c0265700e2db5648526ac11fec7a378853c737b2af675c89e3a0ba220aa1882654d0080d5f9caa5e18c96d607acd02858954c118a9796c19e6ac4ca1deb011bf69804b0c971d2269d00f48c6f8e3290314f9759ff44be4e4f7b7b7125841934bf0af79054cbeb6a1a8184c5e6c99bb81a86a7e4fed7ec94cff32d08dc28c3ede87ab010c2099154349e85bd43d12af6bc933877acdb94caa05ae8b5e9cbd458964314704882f8a56c37f6a92eeb77714163f9a3b5358211020cea062f310586d4976d9081888beeeb9d11a9aba4b71f3240f7335d139f7441d9fe4dab1d44eb95562310ce9c235a876f700dfcdf6cb55cca285a8b811c72e6cb96a0a253e5fd71024280eb0768d44f6500397cb45acec0f8e2e0dfd665faef52a8fd048845dc15c025708f01117fd06849e6636c643d5006dd2d3abe768bf114ed4142532b0198cb75f04e3f4412b1b870af3d2bdef5eff84dd8985b5745b6084310d4054fbf365a6e105017f748370cfeedabe947b720e45633010d972da878e9d1651b7a034f8564203ac4072d43a59427d9916cdf41837982599ec1abf9fc8ce72ca80e3a03277c206842fc920175a691b11e8420881a37acfa264bb3645f705bf780434a184115f00246722ad0b60b13702fb042de8e788b88d5d2760348f28eaafead21f65981c08256aba48742a8edf74b5c617d55c322a71f89851e98b023bf3d4e36c65bb1201659859b55af134a66a77e0f8ce3b541fe1962250a85ba50b46d69d3e8769b10cd94ea5336b2501d615b03d7802e09dc452b009810071e6a639adbfb1630b6206c05d374a6693945b99f2641de79aef5cd2be9bfb2ca8fac187623be4c660740450230f490c7234ab804c778c08066190b791da61883bbee4c4c8382272c76e0a14102eee372e97910b78ed092166e02f1c36f29e1e929bf9d759221c89bac308165cb0d9e7b9286d87d35d5127d74f7b86f10833d57d99d4caca37da1beb2a023b49af133d8be51f6179bb274abfac18f2db323c784ed984d53ff6ae98e61d00ec1e04cec74ddc557f625db207219267a9c862e4bdd7bf99a9a6807a9e233d07661e6fa66773c89731e8f31fb9337d09a5433c533ee3b7264ea68e48a6a8280aa6ee6971cde644d3b13f596395e51b2839840251d2bfe256755885fb3d84150932866e18b0513b828caf9a58672a59625a7fe5288635ad2f42ad5c89c9fab90a0e4752006ae3159ff89df91deb3d5b53e3a04390e75ecdecbe1177cb9e324f005fd568fa8c7c3263fabb73e840c639107317b0dedf35e5c4fa02b6ee906ac50aad5253d3bfc79b208119c77349c7c1a607d549d06134a00e04299a01b4d97a085507a7971252f5f3ef6845518f89786c45c38d28cfbead3ec691f43a1e54f708592282ff272af4950dc605892665b7fb48a1b86614ad4e4902fd1c5db6f10109be70bfb7cdd4c5dfba4d47573056678ec50e47bfbc0f9e36be6fb715522e320b87bc3a0bc726ed0ec642b42a73977f046d0e8bcf0421ba724ccd7bd32fb6610459a6de4bcf58c8300d83e10b6f63a2ff7440c6e21dd94076600613b2e4317206ee5fa51bf86bce66a3d71966b9b3f6c4278b96488c51dacb15aae589dbe5de06 false +check_ring_signature 9e5f402daa4ea57ae8659495c4a28b6032afc028d5f9098bf123b971783a20bd 6969395b214afb973517ded4c59589d5d993849d5abdb28d4de26896c1c9a3c6 1 4f7a150ad78b06e42d2d44c66cd79d060816cb4140f3515c8b59a2f35b45ab81 ccd80c6c77df1fb58922a6480f3b87425fe01f27ca187f79509cc4abba8eec02f7e5b7065d7a474cbcb515d044ee00640913a6909d3819c47eb0bc11e8ad940e false +check_ring_signature fbed485908ac4af6536345f0666e36ec3b236230ba7ec6b50fff306282c82b6c e9e83a8ac5ad8c2017d905db58508215ec292d9377cee82c8ae082ea8508d653 8 d18dca55936e73c37bf388200424943f4ed0c5c7c3dcedd13c9a394232f7b284 b0152bdb40f89b26ad2e6f36e7cf5f29a1b3dd4ae0d3b572c1373a0c510c23d6 0245c461b3dd8314137a88135af757e4de2cfe78b88c3813bf293ed0b6c1a6d5 42da88187ed628e6e6a599372edd2e3d9cdcd355535a1f9dee50fe5fa8090608 bec60e9dcc47aa620ff2de265dcd839ffbc598d06aad99f5461ea83a149c21cf 8b3e31bf1148f196681ae65fb03b4406dcde4f8d842eeeb76b03b7aef695a3a6 4e6563fc1f63e613f374e1ff5ddcb794406dfd5da65f00d879d0430141b0a7ff f9ece1122e4d53abdd1b0435477568b83e4b7cc6186b6c070cd9db8d7cdc4a73 55f41f9965d0c76c8465f1a873ee3d73ab4ce7c24b3d63fe0b68655477faa6027ae525bae14587e7967192712f7c6d18a124b9de1b5be229a8252267fec72c0534e5aeb97a0ce037e647d67ad011020cab5ceab409ba71be14e9d9a5c1c879050a60f99a072d0f32a1e6f8184aadf2cd6722d977426a951766a01ed7dcb20c0a554f739888cd224f8be60da164e4adb08bb7b812ef82aa4a605451a4781f390d06e09964f1bb1e4eb78acee0713c57c21dd0c0f8147e71871948d1bb9bee3407b49ec48cc4bf88467edd0545c4126f6e7a7a54e27f30857fe999593ed00aef0982d5024775f9198c1e123fc3e7741dbe66b4fe3cf6fac0905fd8de7314e871025f44f66ea1e04430543c77e73dc6d75f2578942fdf8d2fb7fea55c4b7190a77245c450b3f2ba582dd313c7c3ccd182b4436bdde632512f9b1ec88cb5aa3ef70939fa1caf7d47e6ef0cb094f6f632bfac9a1664d2a3b64d3f4096896f7b169700520402e5bd02b2192c0d9da5fc11e64291aabf8516d3c254348eed533190c505cea9c3ca3f795e6d5e905975c3a72c84c2817a376a19889952bf410d3a58c80ffaf5bd51cdc88decba1af0990309568d944a446b527fb7722c76fe749ffa702935f87adbbf542a0a2d57df7036f84cb86b71ed43f9330296abdc893f6b510500434fff9d6288d905ee5ac9f2698dd8f51d691a1731627be221392e9741f20809 false +check_ring_signature f26a9a4933b38f1595c9999b7dcd802bdc0f5e5c47d4a23a0b55b1ec1008b102 add50829a49aa0990e2c4175ba8821853cdf0be25641949a4cc209b0ca0334a7 92 cf24c744d45e792aabf60dfb41e1f490660736597f5b391e2ab192619cd4e27b 65a57cde474e7e96110ed3cc3e5ae1285aaf9ef6ca67fc82e40eade059e06e3d 068947f3375f5e7939c442528b8f6f3ed0cfa2cf602b0b0f81d0b71ffac00051 b7abf26fde72b528d80054de9577c72cba77382d80a14477cb036cbb419064fa 287c060328fc55d8ba86df955a68a19fefdcc87f1ee3c472b79d33eb7f84bf04 35fb91a59daee627998caf1f51c8af527fa4228041d6bd1ef183923936d1821e f15513e5d929786d564f4ed23b29baa5853f5ec8ae3cd3a1c67c4da5083f54b7 11d229b080a71d5bbaf34e24cb7491ee40cd71dba1750f7af46aa7ed6f512364 1ab9c8dda7387c1760a09f3193028a182832dedd9127b6467dcec26cc0c74749 cbb592ffcea9901e64d5ae7543d0e7536603e156168df7abf4c4d35162f6e9ac 554464dd9fa164a74e50452ab82f52497a2d8862eb8ac4d80d5a0324adc34139 3a9c2c6a1d2e457b71e098b62b07fda549ab50aa9972b5b82c4fbd4e52cb8ec3 2c2fb6bdde58c9fbfe2412d7bf5457aad42996b724ca000271e1892444a549e2 da0ca7dafa39ded06b70b52aef662332b97c06a46fb9424e82e7f5b14e82dd9f 7670ca7863b75a9d6833e69e3a67f4230e012f4326da37114ecf45b52ad3ddc7 ff02c326499aa1b744d00ada5e8fe396039a59d757061b2671c64716300ee79b 078addeebbac705183b9311e203148d0a2c549e66bdc6ee2ceb004eb9e36d8b6 b1c0fb0d78e181bcd114177197f32ac724f6eee49671625b308583d173c5d683 6f739930b5fb9292a2f1c5ae4f11bc109d9abac4b3b4329daf8edb232ee02bf9 e8b8df49eefb7099298d34c04a0f1de5f285077ff96298c8fdbe0060a94219f3 dae4a2d24424bb04939fd7c37b902fb1183a63c14cb0ea3fdaa5ce79d9efe5f3 47d28bc4d3e40a294a2e3482862968030f4a7b72164abde4fde0b942f3864d1c c96c3fed20f03dabe43f540c7cc5e447536f6270a8709d69836ff1a03a4f66db c67278386fdf60a5a1931f96b95f3b721e8ee840466d44d4dc0f4e907265b093 25e9feeadf73cf32baf630f0a6d016bd83348858544576aa44942194b645e4d8 147f32c6969f4d10be3f8231ef12238a4c4b86977af736e239b7e41175dad5ab d1fbe707d88717295adea22b96962486df4da550c376ea30cadded3fe91424ef 389d9ae4df681b3a0afab37a05ab1037abc6a8f68e7ce502357086511671e99d fa6fcfb2e3a95d1cd2a74d5c81dbb5b37a120bdcf3725da8d6fd56e4d44ee778 80c0491c29b4a11625b3955a05a1e6d8288af7369ecd5cc3e9b9bc3268fa6ae7 787ef27c55df60a841f471d832c7cdd5f4539904ff0a9dfad81149a1796c3a28 3a54f809a8a5e1014daf04e1549bb11860e75ae1c8a270273eafadb9bb7d62f4 0da14d4425096e9347334628071ce2a0127030907db1bc30e896026b415dc7b3 b8ba9fad87196700afbbca591527eae18045e6240d860aaf86c725335e1a3a47 c29e0a9f21db43d596fe45e24d58a7f0bd159fea82c748d65100752b110506cd 03926cc8e962775b88dda907c03431aac1147e91201e5fe41f175eeb1ce25bb5 bbb20ebc6a4d6194a286f0fc9877909cf99f96b60616e6cec054fb249dce0ff2 6e2f3b20057505894f9ccebc4ec4bf9a1943b54d3510ce2ac12cbb81ffab7cb1 d77f0f3f99f0855dcf0ab1b6f0876f375ac331d1936b48c4e2f6316337189d16 84c4e721000b19e398ff261abbcd62024820bfffa66526b61e5089051e596b8f 83ef339a94b9c9c003a0350aa9b78d5c260d794e6cbb7726999e5a7b93de86db 10bd68547e85b2b671d472f2ae8b759568b28c6d9b86bbe9ddd1e0ce86bc55c8 724596664c2c13e76a3aa778f29c6d4f378f30ee9594605548cd48862bba4fd0 2ceb86911e5287958a0d7d38a0f8e15bd761e5a576f0059c4235cf3f51568e35 4b76412b08e588204f890de120a4fef6cbfe054791a644fdf4fde074b3c8e610 6b058856bfed7d4477fe3ab73d87bb78fd091323bfc9d4dea2110a3de7d383bb 1982cf03675ee060adfecac46776100df36195cbd23779119fd5d7c802d3981a 3b3b564b3876cd8999287968ffceb58617217806f7e14227c5f8d51e28058993 f0e46b6a9488df6bb87f38b82659099b05aff23902b9cc3749337548536a5927 b88073e584c98398d8b480ccacac98f4533911ee921be63b3c034e59699c3bbc 19901bd4475adc3a5a00f48bdb21b069c767bdf6273dead00cd0456efa5f4fe9 6802e95476e3679929109b9aefc6850e5e88d69471e4fd9a5bd14544e41829ec d2d921889c77487a9aa8881d102aacc583c9811686fd17f70e441f70dd430982 d5295df68d150db1923bedda9bd37f9d605c54984e7df270e5c0c67c86055a30 ec28937a4c0f8d83ab1dd7180dca95079690f36eef3cdb57ab395ea138eec9cb 2580d2a4d665de6d1366cf1f5dfc138d6fc7b3ab0b007ff5f0e5dc8d779027ea 179b84abb079aa02d25376a6bd585adff98f0297831895ff31fe5eeba486514c 93ff45d56c9ebaf20f4b67dee8057fbdb545905b455bff6c43c0f355452dc827 a69ea9b90585b4dbe74a9c1108b94f7a822da13a9f545db8a813312aceb9a3d7 cd37ff265af94b6ae0862cda1bfb0e1c09ba04182ebaf8544c695f3f4de26e8f e5d50976a4c47e0659d441ebb3a825dc5a371f55c0575e63d022f682dedef749 ee0abcc4afc476a287b216a15273149a044c0338d3d3ff9f736ced361a64661e 8110cebefebacb10cbe03ca8fa72b6ab6f533d9aea4ebfefbad331a6dead6d94 0ac42a0321b668667ec1a5f37675b1fd7a9d993c511abf0391ff1cf4900c0143 6dbe5ecd2e5c9abed29bc26b1e789be9191adee8b8dca509f3d94a830977b25a 52998c906d6d533439db745d9dcbafb109ce20afcea8fd0f7979379dbc8892b1 f79feacf7134ae2f64e0713ff6637512a53a5b014602005b865e276f2a597797 309b95b91a70b073e907e0c3596787a2af272eb45f7b7d84c5a3e0a58323621b 2655914c4d3d86705328b6e35e45183f428a3575d50bdfd479bcd938f9d1da56 c99c5456d81e6b372592d5e7da57511d3020a75e39d6bb8a6b8f32137b85e4eb d68ea56c62b6941c490678345f9d013b7e935b75b936376597edbc75e04d42aa 3561e1ade50a2a4169422ed270c5f58861725a6393b71c02c1bb9e1730762c1e 8f953c0f7420bc0af8624c97e35fe99e2977cc597414db64e73dacc52a481722 b8dd38b19ee21ca14e805480dc876303b02dfe0659f94bbf2534723357bcfc52 65cbafdfe9c08fac612eedc624af7ae9dc81325655a221bd36472e65ac01dc8e 2714d7c097f84e990a95b2d33cc5d5063d9f3ba8a2bc0b768ef828d38b1fc4e8 60ff8a267b614bff4f78cd09a5af95610cd47abdc6f0ff25cbea61028bcdc1e3 a2f2da8109b5561247a69bf907b09b62851e59ecd580c52bdc74cb325d754ddf 00a18e9cd7f9a606a3a6f3b3c6121c04c6da43aa4c04b11c0e6787a670062f88 22dfdb59bbc6913b98a7ab563e3eb6f903832a9382c4ca2d385304041b933b57 fdce2752b9dbb77730c67b7c675e6a9857f86b336f40df552d2212a975929e72 d3b6ee0b4d139b6c883e027f2f994b56d42aeb74b866093cc612899f6cce634f feb44b961ebe086b88f942906afa6a4cd8b50cf02b58bdf1d6672d99cf9e9884 8b6c03d8c8fd89bc1428f0a990cae8cb23f6f0ac65fa41a937c3a9d415b20939 7cd0dbb8343483b74e8aaf85d1696c790811f1df47650d16fa2a497861af7a14 029d639288afe0665c2d50a58822ecf46b1ab09bd95cf6145d49245031f5823b 9b3e82e93dc6372b53fa924fd57a9773a9cd1c119a3948da0718b7100b1619da fd1ce9556dffd677dc6868e022dc7cb89eb2b9d4f19476e8f604b6a8888dc5ff accb52bf25b5b4d77425487b032bb06e6e26e97d796dacc15f2d428e2889fc91 faa1fa47ff510e92667a6b947ac9f83b4881d02376d997d3c37fa7fd117db0b3 45e1957726b9f807b7913f00765072b502545ecbe1dcf885c103805752bfb4f4 280c82711c22ad819cc81618ad1ca7f7c1bae7f51826aa889350799a24927a69 53130569921f53724aff85d5d4e024d3e895713f1475e74400444e04af11f90d7102d107a0a5b771fcb23cf5357c2a3e42eac80af9b921ad35e8794bbb13550b6d8135e9094a467c15ea74a5843e6746532860866e7dbf8bc80c33a8b98fa39310d69bf8e8f0f5151987cbe9dd7a1ddaecb8f96e28ca9d4daa7feec3b683e80295431cfa616627691a9c848d3c2f5208383dcbe253b10f8400444f77ea877002c93f7e57b76d628a55dd0d17baa64ec9b7c8d9a664e7c7df643c18f937bbb608d63de6c48f771e26d40ae5ca6615f3a93404ca6dbb1e80630756314d4b821d09c4d2e319f9a81be38a2df55486eafa49663cf01bd5bce5603ab8114cf8eb690b5c07de73875ec47363f0b1e4d2e4062a777555d814d190f1da75c8891035560d547f68417b3908f6fd92ab922f9d8a18a3af00b7bc1b3f18d4c95f47fa80140751d34cf51ec66531163333803c8e62ed642536d94c69208c506ffa0d43199a01287156db78607b6826c6033872d46955be0b53a19e11ccbe181aab99c431a70e6f901351321b1121e5fcec6e8a690611c7e0d5d206478b2fb4466748c4f3140758c1e2db799fa4cc4b6edbae4d4a44d5ccc7c58eabb8b98f3a83dc9547e3380c54397fba0371fad35e0cd263aa3e8a737250791e03366368eb0999f720ce500091d3a8873b418ab7ad4b6445313fdc3eff9ac824781f7118a12ca1dda18343069190888fa9fae93ecda25bad27bdee089a2efc3a4ceffd18abc83449de549e0823442ecfdee5a1c6071895ae7d42709631668f94c79dbe0a94faf38af7dde80d23edcd0da5725bfac30b7aeb90a3c97ffdc8136dd38b73dc4ed2f8d15fb2b903c06b2cfd61db6b808bf30152fca4cc91b1185879a597312c337ea68fb2b43f08a5c4f0a4fa5d57c75a46fbcdc90ac57f04b853e0a5295f08fa1a7b1e440fc20f7578c44247758d586e429bb6fd3a8733afe2b6b376c8bc603199d0b16dc0900c40d6a76a60f9f36754fa0d4e34cdcd85a250f3fd55117b667aadab238e3cdc0d4f36dd51e68f5f6119a03a060200c011993a99b456f856f88bfabcadb919d20c2793dfc8b39d5bf1fc16493305c8f56ec2c71e36de333658f994d76fab015e02c76a475f7922e654259efbc14adf498f6793f45dce40b2b429d009d028926a0075cbeda2b3984ccd04e415a780ea526d19279a7937a3e2d3ee366d6d06e922069695deb55b86371e1511cd05939022aaf258bacdcd52174b6fef656d7993d00ae4ea4290c94d2f040c12397797bb1c6a1b2e43cd845cbd4077d85a56db64c00af9c7072dc4f00580abdfa432b9e1f612b0c950c9428ff3016d31a35e4e8ee407f6131b1e0ff5be27bbad0040d0f335fdb1ee7ee749ee5edd8fdb7acce4d0760fb0c384501eb9add690cead70ce7274eaa0051961392c951819bd6101fef1290510337b8495b86dfa4d8db97c880959bd907df77ccb5a0fd87fb3b59b19fd5b063fdcbf645c4c2b1a0a03536af48d401106132105bec2224d2f34b0517423ef02e5fe64b7cacd7f26aec58a68fb15e3e704ef49ff6ae3795519e398d719a03d06a746328cabbab8578464017d62efdfd23f06eef0e3fb121074d15178c22d490e8dadc490a86bed35a326d775693f4f70d06384b1f62b08509ac9a4daaf94dd0ee015aba0de782fdb782b97cacee22149dc88f213288b928f1bace2ab3435fc0d637942697b17e8b2be1e73f495817a135a35c01f7183848d2641fcfd9d4d4a002763563a6cd9db1e7cfbcddef081ddc14ca651ee11ac76a0eb7cd01a2d011e0d7f34ac045f95072d16c45801d07511dc89fa7026dc933c84c698ea3507bc5d00fd426ff9392a76e991635f91e1462dbc84b44f4f7236d4f2b2d23deb9698920a9b973c25e4987235f1e313bc8c641a71794c0b4223345634438cbc7c73fd2d0b6a84794e4d5f484b94f0613022315fcdb462cbce66f6efa489a3a67f5af63b0bfe8b04ec5c1a1f564b16e5eebb5b7e561488142c92d2ee6d46ed08ab1318a60dc36bb99844d11ed01da5be0dff5dcbb50d9d6caacef2174cc3875fcdf08d4908f85a91cb0f80be571efeb2b1454490849f21ed922029bb732b2309f16702bf0bd6586cf2280137390ad0a5552e250f3b52564dd548dfe590b305bd34d4411b09e3667a5f30c1515fb988b7e2b3c01719ee5feb94e74ca1d2f3702fbd871dd3038b84804ab635d41e5ad2b86632a03b0d53a8c18e92c72f8c006b4be90d0c7809b74fa6bb070d639c694659783934303861db2d3fbafbcb703f26b51cbc976a0f6e95355c22d7b1eec34813efb57ada419eeca1dd062627541a03f1f6998fcd0bef74e6d253631db7fecaf5950dd378976354a15cda3de07fa79fb636c719b6091f5742766e7485dd00459eba87c55f014f99634d34c6f3667df4631b443f750e644231d4b14bf44e33105f95cd7b2830bef325cae4dfc4f6fccc59e292b9c5096dfc38025ba4d801edc649c8c9ca2f6653583da3d44f42de0fe32a90b293a10abdfd4608f40f34cd6ba7b4329237c176d217c4b82866b3719bc6e87ed1e90b0c141b23797ffd8309320cd31c91aab823395dd3dfcd1601f587072ef9c8b7020d0b733233292cb49ab1ef3e46adbee0bcc61491e36eb4e293cca105d39924ca0aa27fbfc0518a63a1eb573dca9a8fa0d1b640923bcf91747558eae1207624b00c00eb626456464a8ae24ef30613c45824440c92104274c7a5fc593f1c412f790b28867ee4d411a2f2e0fea1a3cd165bc57ffe72ca585967a69ad5384ed21ee700f26c82d24bfc6d35cada0a5d45a26e99fca07878dd2fba7cc8cb0d8cfbd1d403483dd12a8f1052471631fde35c5e58c76d7587b9eae02338b67c6fa9414556098281fae339aaf7a0fddfd4506477c53588cd2e817240db510e9fd16a2bbb7f0cfc9fa9e45affa0226faf9ac50b089a32efb23cfaec55f3d81447152f3349140bcc1ba88ad036ab945b6372d4e418b497cc5b1c97d37468549f1db7cc4749fa05bbe2c026b46d8f6f35b9af593fcaeca3761eb3560e540e2eb5307379a58a1e0dfee5ea9f9e6827d43337ccacefbf5ceaf8b49c310e38d69b79537ef4f5af5500c4015ac5150fb0745ac6c0c9aafab13f9f187d53fabe05cec7ea23589028f9029bebe0ad9d36e36ae731a8ce4bdfd5b25aa4457e12c2ecf8baabdc1fbbfcb409797307c9f61ae0cd822a39063f4e868c3ad7cbdba2e2a25078ca9491b301ac055e0df44617673de6591aa3cc4f1a358d2b784e28033c1ba63597a4c011e91d0fc6327722edc04e1d9a24ae2983eeeae5fca6afbb41bf007053e194e71a024a0c2df9814225839a7caacf95390475dc34f0827ecb4049505d1d20b38b360eee01b7198547d528b3e5975f16b95c818a5dc58422fa9a4637ea2e6de9cb31397d0b35b09cd112d79246e238a2712534dfb019a8cb9cb8a683dd69aecd34fef13d04158ff15e2cde838859837f7644ac3b95db89b0db1578dc7edda85cb3d0052a013c2eda2f8599814594b4e28bfa405849c9590f37bec2cf7fec1b43d15dfba208d59cdab2d77287c2f71d88bc2f3b790e30e50c471f8b7976c8bb739f706a4500978c73018bd3e8eb3a223d3e18f06a72b9d6e1e2ede69b99406bc615d23de40907c4526316824ea25083963c7f1c0bfde86aeda48f0cc9965a665e074a35780704322f833cca7ef93fad5bab25254941c827d0af25f74c876d5e0ff4c99195013075125fa8ce36f91a771753b32d5969381181c6b558bee9e30b85aaa19ba9022aed7b0a8d761d18c8b19d8b85ab8e55deb6a516e8b43184c6181c214e0ccd01c64347a83e46669e6cca117de93cc98414b41a4c6bac127d451b3fa296e98805ca98bbafc985651f505b1fb5b37762b93e1aedfaeb722f7acc619f5afa1ed80e31ad4ae2dad78c2a3fcd3e3bd6312cbde864229c03358863428a0a849d62d80f1ce41cf34600cf6fb8f76ecb59460a248be6afd8a74acd453fa2494967d3f40ea94a144ced378fb0641bb8c8b4650a512af61a7252af4cfc44e018bda5441005f7c75527ffbed84ab4b852548e19476efec6a456a1a29305c34c6eea0e955d06e1f5fe3677d732979d2c97fb4705bf618b61ac3ad35c441935eeb97bc0fd12037eb64465d050cf5f6131a090bb24b64fa6212a86204e3b92eace4439e36f4006112738ce5811ef1d3bc9aaec0907a1aaba48c7584b5803c42b159dd6a179c3061af23d1a2f2b517d7490fa445cf30bf7af2b9b9f84334bffc8a3633096eb5d09f0f114f8e198157e801322e15c54075076f03c4c16ff22f4e7b71f222a11bf041633ade7d7e9f08b47a293cb096ed6ae34ae65ee62a522c2c13cb8627d7f330780d65c6ce19184e4a86bbcdd9f315ba14df357b264605e4b059bd14a58f4a80f3f58124ca08798f0bf1c9539e0cea2f98c5cbe735c838531f85c7bc9283a610bd09838958c941214828cc9cef410d410d5924e9475d1a77e7c7424833075960506c0bbaa089b7e7a7c1a629e3396c28d27d4de6a445ff667861102ec65cba204e254e4f34b021c3dbbb17d251323b53ba5bf509e2384551bd558569ad285690565d888b3c9e54b0a0bff4195a1d8f889d84627d473fd977ad1fdf9857f96d907866026dc809eb543c6e2b2c90cd6f9ef64d4a6e2455c5b31bfdb51651bf84901a0d6843b547bc3f90a7025a0e2fcd22c05639ecaa0bc7906c6b29035036aad07f6333c762d47c1614a6a143df80ea87bd2ef7852c00e9b443d4ba719103a290c8b75d1d4325142d45ba9c3c17f3f7ddbbe483ffcb38a7e42a477ed9d224cff0fa78a26774b0e990770c5dfbdad1f6b1447dcffc36c141d0ea746aa07303eb50f7b561b20a245572acad020f2325ebe76dd18eef0dcab72200561989c99de7c0f369194c181d795aa36cee7b33ca6dcf2b217ad5728115b9fab3c042160ab320894bc4b3656485bb1922d669d54855647798c555cb09b12e23a853d7c7810410161fdfef24d21d8c14ca225450845acaa8f35f8be09e777a28f4e70eb273e3a0b744cd547497ec71128ed4c24d89eb3069722bc8e3e7909065adb800717b80a0cedca471f33a9dea2402d55d28113f16551353ca929bd0d57451a2e856631d10fa6c64b927794b12025f092ebde1230b7288dd0a5f04cc5d4fe303ff6a419b8041432b3faeb1c2972768eb5dd28ec59249aca7c8558cb84defdc5284a4337f107b68c4e7359cb5cf69ca76b51dbabf1a73b3dc6240c7c574ad96b1df79a121608ae4a1e440992dc8ab55b916e0b15ee3b2b6e78ce8981f569b053c25ff9a0350d6b28d3875ca52f73d524bccb1c58c300d689bae4438f2db550abd0a58c263507ca08019e912d6214b85718aaa91d06c06b3790c4f81b86d49240312d8ab9d30618bd35398c099bbe205ebb2690a1fd83974953657602b1ea38636fb59c67ce0942f831153c8bd457d583a62552a98c7262829d0b3449fed1713c81c54f8be30b8703e2edb6414e5ec9038d21c3a43b068713d7cb827572c9cf51a5c26f19b60f29f33ea38e5f16d76cb1bf5b15ce12ef200d91393c6f126643973afbf658ec0a3a88af4951818ff28e9b78a019a0739ad2dbcf76aad56331c696859e8789c4066b7465de592bff94a720ae5ad1e7f87c985f01370c349075e379dba12aaa8a05525e028092d660e131632d2bbc2235f3f5fb7105727332ecb5bd00dd1fa91d0b09cdc8ce79d7c0e79fc5413501d89534b9540c44e9c7fc23bc6c6adc0b04f908ebd13a2350898e54614e21f296a475a035a5f0b1bb8f65d1ed095c7bf3b5850fed236d85b228ec1cc748125e4867cbd2c3ecf402212cbe635b271625a027b90e6803cb1f5b871440943c7fd24166c1d1427417f450f241f472574159ff96f90baefd205d76aac30d04079192b02ad9bc535d3a39736a4d252517a7e5e47aa202a31c294c01aeef5f8559630a28ad59e6bec71ea443fd6116e034a6d6e3c3670cbf45e55742f7ec72d9757c5d79ff93e6eb796ec5450ea56abf366658dcf5560d6d08072d3d2d4fb1a9235ecabc8abd48766c927eb6abe872d0244b2e8b0eed091c1e46d1e2a4ce06ec5e4022cc86a2efe2f059a4c4a8683e4f3e0a3d2f5460051e97fa0b458806fe6df2857ef1a2c944d95b1d9a2df36d2813f68c9f693e7b0ac56f24112dd58e068447863470a35bdb5c355f7c70631d5ccfbdf01884b2e30e9e1f832853d75a997cbb3c6c67d5bbd8bac21981b1ab3c360554e13562669e0195b278df1286311a7348a72bbf3a83ec0a324e69e6d941cb48081f453555ad0cc7ddda251d5e17e7a03b3731099cb360253c9b7c43b44b74ed1413ccb2fb620a069d67f0a32c0e88c2dfbda4595d0ad0b3a43822149366d4fe56a508928a710fef62e8a3e74a0c76afd06b4dc8714a578eab0fef54f2dd7cdc647c7b2fc29f0b62293802c5194502d9a51700fc87bd6b1b13baba1fac4b1f85b20f2d9b5ca3092710cb153290700a1d7a570e850ea0b2be8c063fec1a76924b3b6d7f5ba15e061a1e63efbf81086a771b408c80e9a6bb3cf4c014ba7513051b28e36f26f1ed039308614cd1dae1e75120ed0ffb13ef4fbbb4cf31fc582f9a949f8653895f1c007cf4cc97755656bb42a9aeb4aed9b6fd9aeb2e5f59d2b71ed19d8a9e3a39ab09fe87aa240d82b8db846ac21a35284a95e738f2c8d9e774cd2f812174a8a2cb09894372d73db554569c6aa5833f6e429c1d91aadb3ed8ef750cd44be2a1e93c0e8e6bbfeeab22a09bc92ab639e8ae3882b647cd95f76a4d1eaeb89e7e926989044527e8f41eb5bc4f216bcbd2297e68ef5bc3c03ce74ee091f9b75807a8bc35082cf72a3a1984fa2c52b0b2efacf54f9bb7bfd75ff3a240e2d87df0cf6c9eb70d0fb65e3b2098bd0242d310857660781e701121dad06911582984cbe706db3b0071ace08110e256a8cfa42f93b3deab54a8bfb9ccfdae0aba56ad97eb936dcf0a04adfea05b7ff59fcbe109e0161ab8a9a2637e3cc6feb14f97e1ea38cba73f03244c89b6c25200618449f48fc550c178eb8f7ecd5e241107a27e2fe2588950024775a450a210d393e308db1ee56854b62d487732a5c2beebf2e34279d72f7b0fdbd52506da754ef654c3c3c0c16f9ff2927a3b3490baacffbe7c18aa6e5caa0dd8f8f81560e47234629232073b679d3b9fcacd8c923759621e1d58b25d25f70a564604b6c84ca89d84ba2313d79b3bf59bfdcf21d35a1c3fde9880b081a6dc03e15fb9922ef7458440e96d8535b1ca5049122650855702f9e0113121a6c1ac0c9948800fc412cb1815458a60ff5572b19215bc8318112adb33c9a355af78c90f7bc1b9cc25047a08e97003751eb29a6147e113c550c65b63879fb51f417fff02a983818784dfa567ee441b2365f52f7c64ef6544a5880af3f63b08bdcc43750c1b328f9f5a52307a79baa7f26935f275c2192d7839ca68cce831546c615b0a004e63f9fbd6d0869f2111f7aefc0ee941d856c353607d9ba2dc936b21964a1007db0e2df5cd6f1732999c19116cc3b1f4a10f9e0e426262c8ecb7d8e52f7a9803b1b601adff3b0e0bd8ec5664a9bccd01d648c2e8886f1f149890283b078a0e042a39019867de7c8d7e1114a692cb93190a9b7e96d2203903c1b48d295eb1250b04ad7df83bf434ff75894d21216a6a82447630759c0a1e618a7f26006832f608d9a067b3e458496cb22423c7fad0ec8fc4e9bcf5241e60d29b89af9d6c5c8c03462a4722aa0224af9ae57b95b2c2d315de4bd2075d4012659b8ae655c7c8cc049a356d8eef19c7ebaf3bef9bbde52df4e4b97dc07fa037e6688b3aadb3201a09081299f8be3d89ba0abe10f22db45c491182dfcff4a5fa0afb24e0b974b81f017a95a1d1acc4a4a02ab4893cc5193578cc83e03bc67b931bf5c4cf60b0ee1108beb976258e4d49fc88ffb682e166ff570eacac4afb218c92c46393e1f3b93a0e8892d80df0ad0c9d14ea90debf8ed29db90a922a84b4dea1da8ed98f8f7de80a5a32613a8589f7bb979c4fbe5f4ea823a9ca655557f31d2d3c9af6213e2691026b9a0c7b977a4ef390748fba0634f46ba6f83275baa9bde4acd3d29f8d89550993e5508deedc6770b73379b7de8d9ea9dad0dd81b9c48e4556fde64f09a9d4687ebe0f741d4933cc55eafbebeceaa4f94168a72d517e7deb8c5fb556325249055fc75d724707fac8f6314e18dd66e5280c9768f164bc0692794a67e8cace3d00acd74a6a9e772ae0cb6f2f0baade98a4a443840d5e1b184a90d8e922537c9a01 false +check_ring_signature 493d596b24fa2cea05f1d24ec28fdd07cfc1cf8bdb71914b197650e29d485798 04e9b669f858865d97757215050ece3a111eb0176506de2e4559b44a835ea8ad 7 d0a4b80ab279e28b96bb9ba66d77daa1588525903a6ac65ae1b3d34ef22be005 63fb76a2be3dceae00f4e976e362e237d525a1db94608c2b6335ee7e472740b4 557a42e92a0ddc2acc36e66e74054d098916520320f0b754b3352067481de1de 5e1276ae0b70d09be29ba875e27ea7c26e8f28547e648c74c400dffd9a9c3556 1a7fdc7ca5b069aa47711a36d15e8fab81a18d23bbd9b15c6692217f5a27d60e 0497ada6bd5215aec73414e338d079b81649090847ec60677e9a67bf720127a7 d6fa1a0ae08f83645e15983c52324088b2191c8154ef3b353d43108cb52d63f0 d18c4a23cd802353619239c69dad463687df372fb1682f8b4fb04348b6638001a2ab5685e769f2d91d47dc74f6f2b3e375ef5fe542ccb339b2486f0940e4870f3f5a2b57f1bfd4ceeaa55ee66351f46239f0786a079580318f60857b88b559068927fa0912b05b15e49c9c2267792699a931ca13ee25a6dd656da1689c176409f067230af69d2e18216246d28ef6f98e9c200a3226b5b823a497abd3f73fdc06476a25a3587964ce88b56e5b38eee93c8f02a5759c1772b1358786609978d50c908f4b55a07a5ba7c2e7a4b30952240e54c025ccba58f2ed3ae4589640b5eb0e8ea29591e1321d9a6c5dbdfaec3f6d18d6d0c47710d6de36c66aa086266f520bd38501a1b5b2ff8fc27c78ad536b42bbf77f1d8fc28bef70e7b551abbd48f90e5a73756f192f2d93e52b599ee412186e3cd8c03bc6c56b0693331e97d88e6e00a3e362189af57c210bef9b28ad599018e14e7a0d47e33cdb43fc789dd00fa306434907cce0a2adc17cb8afc27faaa58981070b520a8ac1c094c4b74c1014970e91f22ac1d7aea0ce216c9034545621b865f806bbe44687334dbddaeb60da920d7839ced416792b1a7b01ecef95fb6d5562c42c8fdcb3b233facb05c3e2b08106 true +check_ring_signature f37dce85cd63a70730a863d6df5dfcdb070f2969972067821d90fe7aac41854b d2d04173a0f9d7ba8ff3f95d4f17fe6f6270cabd800b2d8a3b3c331aad462248 2 78e5219845f513197f2922aef1e080be3611900da4e6b915a44bf4b1468793a9 fdf1b8df4f609674738af58cacd52cf7704d92661d4fcc2507451000bbe8caff 67f5ed9e54f3ee2ee72e587b857b171c45bf269ea80896a5d85030d8f1f52d0d36c076c2b46b64bab2d9f7331d21edd3bbc35ce16b7fc8602c152863be380d02b8f5674c3357acb2ac2ce8a62035ddef4fc902b7f937fb1e5fa0e23b704e454453a7b6995ac2c20986e6e67fe2e5603fd138c2815ed61680d02a8030cf170564 false +check_ring_signature 093d28b5868c07cddfcf2847bc9a67aa8899130d2acb9093f69abf7072f32b33 d617462d3d7e4ff2d84bc706a91fd4d47db6225d922780273482c5aef5d4dadf 1 254fd21929bf209d37bc0addfe01d86b6e008eab80176270054a1d9fa7e5f288 c7e3d40e98ed4ff035db3712caeb68850ebaaf71f9b99ac0f601273e3c19830a78afb199cb3612db60063da2bb845d4526c0d6be02ba757a941e87e295cad04a false +check_ring_signature 346bf7e5963e0e7d53e735a2bcf76728573593bf9f4848f809d0e2d1e4bd247d 8b21dff113072b6b3d471247524422c0cda7783d164edd4aed1e95f7dbb8fead 12 f47a198a4949f539a3c8a39b77846420dfcb357157553ee05864603e90e42603 f89216d4f80eb0c926afbedd6ca27bf814b232d0703a09c6c3a14c0fcebe81ac 808baea490f7d72ccbce1e6881936119bda9af7ac6dacd01585c2d7e0339dc54 c96761c7a6df259052b29c0f3e65c918894b8a950a7d427ab32b10e9035c3396 48f06dc3c9f1be11b31f4dc2d6a33f21c029ed23972cf015e6ab53e96d55ac27 9787c758952c532c686d4fa60ffaf0a3f86baea3242405db3a61980c384daa9d 4045d817381976f121fbb30bd480311a9dfa8b8a209d53ab650cd95c4a300783 18f1722268c3ba6224dab0a622d3f109291870f96529c2b9f65f688e0b7b9fab 553d1b055090355b560e89d01d812086fd457e0f23ada8ac28e31d2f3727aa32 b54cfe99b09400720199ce1d72831716409953c62d4c7e14e68d7c886b17e4c5 b07bfb9d6c8833a431ec7ae68dbcbac27186e33c02e6cc1e1d2a9da0060e0abc ca2bae12e8cb45a58a9637276f421a90bcfe99b8e567a68fd7dff46a3ea89017 09c04fbab161f0e6df77ac6a2532be384d0becab061f9df839f42d6be8961c0c08a5a27b6afb7243e6661cb9b966f642155fa6b7ddbf1014e9cc4044feae27002ad6806a4bb82bb270e88d44dcd7280e071d08617986c74dff7e6ec27679bf03dcbdfe7578615991c6c68cb8a23778b84d8fc3f03219d5499278be5b3aa3070d4168f4a32d8df911023b2d468f360e2f6f7b0ecc060c10004b738a5c41db5902c46c3836b69c33a7075d1cc1437f415d941e4693778e859feb85581f04479d9f99d2aaf1ea22455693b8a7089cecb1c713f3c60c656d94c9a3cadbd52b6cae05eb36408d16edf8d07cec7ddf27c8fc9b028a9626ac628159c93e45a5f3bdcb05f5ac6ad504764e3489ee02fae0f23cf1480ccbb4350d8e517a5887d0c8745b0078693c7e950091a5f9758043aa2e1a73a8fc8271ff7880c70c8a2f930c94d904b5f984c28fd781b6fe7f111224acb1b7aad47452b898f28eb1ab4875a4370e0a860f03f5c7aec09d4195635a4576b7c046660f29e3db0707303e87cf08b43b08f75111f2074815f19a56c41b015f2ac7c4910ec3d0f9cac75ebae350896bf50467865119803e3f18c57aa7f346d9eaa5b24c88fc16efad00919262ab55ecb10d098836285616def373a905af511136f7c63d872ba5d1d551bc94a3c104d8c406a155f0ef1190dd570f84d2a1d5132e3c8f9a3f706308c8b85ff758e57a8d560ee2d4b1a10d13eeb3f5a72598b4bd4f0447d4e0bf3cb9ce3781aba6c8b526620f04fd34b660a871bfc139d92f261f809daa4289f1da90d17a28428e377535450ccba9ae2870165c5bb216f45ca476e8b29860c042a12dcd20d906fdc520b2df03f8c2a9c23ab7f5f17e572ac3fbba8279687e1b6af33df17392d6ea8b5442060a72f8875b51403c5cb32dcf2d878d75e75744b7170047873d4c6ef57c48c6930e0debe51e82c6b38699d5dda0511e6d9774db98cf28cc5c61142322bed32671087f5f866d3088f98e9c31ad8465ee060806e69833aedd081d257f8a92c518130ccd303e27ac3d5ffd19328d58f57b326a888df85bac8a86aaac15bb554e181c02 false +check_ring_signature 9906570fa5227ca6c02fa3517c996d1fde065bd29a57303a1e64b1351cb480b8 b5a94388ec721e939e4d5e27123ce3637aa3bc068d6740578375bb364ca5d4f0 4 c19eeff3630399ffd9af4de2ac0ea6a01a1e2bfc29260ef145e720e375b986e2 2849e4d7ef327794917f6412bdbd108469e88b49e2f658eebd102d345341a25d f24f011ef697909c0cd1a869bbbfa827ca9c3f73e81f30cea87df9f34c94eeb2 e51e47b49aceffb205b3bef655e4238f28f1ccfc7bc1323ad8e31d519040a2ef c096b97b7faa8deed24c6b238386b576bef8d95a4414d332f9f162a7a155cd09035276440678e4e0d26cff6d21a3bc186ee1f0323f6687e02fcf24e273a2df0350d1d76ab0c02581d49a816957a11808167232c92c1af93bdcd830dbaa7cc3020f79425f0b98e4cf4519084c17ab71efa496736799aed929ef6a94cf46234902378a76497a2e8343c92e6d452bcbd473a9c06d96ccb318ac306cbe9018384a0f85fa58b7bda7634bf50cb0ee372106751a59c044919849c0cdb53fd96def2a04edf7b97a2ed5669fedccc56a33849f62020469862cf20be73fba73a70239a50900be9a4845b7133130a1b2b957bc35634679d374c895a786186af49bf9eb3f01 false +check_ring_signature 87af2d4c4cbb0486fc9251d16fbebe7431559210393f589ebc5b4716836538bf eb9a2eb4aafe350ff3c6f72c1bd6b578f0ef0eb443254311ec48df14078bf3c3 25 7b82d347d808338e02cdc525e0fd4555cf9cbb9aedc1c2d3ff0db1fa1466614f bf1dd1ba54bfc0a6a3c50166f4b5b6910537f299705e48f08efd0d7de491cded 1183539d54b3d35c4399724839d3995b46dabdc130c587345727302b7eef587f 3060bf98d069b1be7d00a1cc3c00be2c00d4cd1fa8f041cf774d19b176542100 7a8f114089155bd40831f9ed89559ff0e2749742c94ed3d6ec57d11f159b298e a744e59a54fa51246b38c08dadc92fd07178f6380e830f90b39441d045767d06 1188a7bcbea1805400443c7a3052f3df03b6915386908e025ec350858f88e2dd 02a57ca61a9fb8962cdc96c67c5d041b891c2db491bea81338769c182c99c62c a38feffbc1c50c723b02cb5277855a50143d05b8d56b39f2fe156f913443ba80 5cd3f93f75a737c8dc98196bf72ad4dbd3f1fb4b4ab99d70046234cba3a1b348 006c7bba5f16e9bd54e1c6ef882d6ee055e1121cbe383848987c2b825ff04035 f325e9464ac3a70e386ec103474a14935999d03907d20819e69a9db122c827a1 63feab49a9752a19afeb971849772f34d9af3f8f123284219a307261fdb0e028 4bd6c85108aebdc574273b47a0b51575e36b766e0d21e3f66d055869203ec656 286f85c83c5e2def6a80d85214ef80dba1387a2ee8063e95f9112678f7395bbb c415ea676c2f68959ac71f002e053c21df8bd06bcba46ee0128bf923562be6d7 ab06b4fb841a9bc5f20337eab6a1ec89dccea87e80e737d8637eafafffe0b79b 670ae28c257df5c8536053cbacc337cb095c652fe5887c804a2e221e3d831b1d 51cdfcb31f4084780977fa59dc07f4f9944461525ec49d3bb02813bfcf775680 9ca0ad8a823c8fbd1adff001c6934f94de2856e41f75a2ef82c58230f8bea38b 317ffb8126f19734b22bd1002227f6c1474a74d2575afc68077725b7fb94630d bbc8625e3c8aa7c9ec45943a9263a09348d055002a2d169f0995d7d99161beea 7b3b8166ce586141a435a7bdfec8f5f6f95a762fb94ad039f0b3c8879a95e599 9874cf805fe494635872656c7215df13e9bced47686187e5935ccf7ab048a8af 3999ba7d88eea6daf61643fb4a60c74b299e8012185622e2bc4da167ce3c0edd 13ab46655803fef9c7f9be95317e3a897bb4ef25c4a901a5f11be9f97cc76a06143abefdbec667cde01b88627d2da2c08677a8fc77afd40d5c792ee495e16f055f2f8fdd8d9806a51bf70830720b20e3433044839eb08f40abe75165b1eccc0c460d1cf9705e87cd6d47a376d0e7a3fa5e575c9cb1635eb40e42b7517ba3510f8d66a30511ea70725b17b3bb3d48e672cdd43f4d1b83d4aa240d2e090d1eba09116ef93bac10d2990541f9e79a10f4be7c24fc7e536940e8c3dc2fadaba3460b1e85c5195a417f313b74bf55b4ac9ba012923cb2dde60d4d3efcdaca9d470106c635744e37a1a05e895d2658180a89c3799f9e60a9b876f53fe070230bdbe60f2751a7205ed86f281172d2265c99a71946b8c9474b3bfbc5478a6b74e16d77042d9ad993e3274556a740796c63f8bafb1865a7ea9e7aa09c00fd5dea10fb830d9b76d1fb7bbf314627af833def6c461ed608d26e290773406441e0b2815cb40b6bd548738f2296e15217fe34ee90669dc1e02d16f26e3322eb0e375de31fd901ea61ef023561e6d0fa10b89e9a0d8c937961a5f06ba064e0f395ab1acf9bc80027cc848c0c581d52ccfb54979e92d5866ed8ec8b878b45c7b860926585768b03ae07cac50402dba728f1dead0efea9f75bd859f8216a7c1ba4684538a6abf606debd58ba98b98e3e139e95cf25d0525f5d74976f3c91967ee82e5d8a6051b305623b128ac092b2924af7faf64433413f69e0713db0fa2ac2700ed34e0a58bf09a54025ce574f466369f0ffa76d92b8f035e8ec6a9c9e876118de4e9e22f79200626d4d722041a4445918d4f1bd689b1c00e4b7654f1e17a8cacc56fa7bcbd60ec83b3d039325ca17069809438a6fcc70ced60725729be257eb97bfb50a2e560556a14864244619b92d86ee03bfcfe19ec2f2700f047127bf9aaca125731d7d0124e69a1a24bbca7ec7c82a5567e2d86c0ba68a5ac6ecf54cd09dd46eda223a0f79cfb6efba4f3029b9dabd6565e6ea372fabd6666523bb9c9e225f3272cb6e0c125d0c871c002a9a04c6c6d7595cbbc6af9c01e87e7fa5f03f38afdea28749089d58f7ecb8b9daf99d6645199bc137ab89fc58a833e5c6b2660bc405cbab02007e35717fa8fd1121ee94169f3de1ceb5482fab329ae60341d893a1864ad05503c985027f1bcd5c859e66d85c549933234d48380f4f7c8d7087a1e717173f6d0e397c91f75d1210231bb336fe8ffbeb41e12610329a9d7a313a26bf65a5751c07961b6a5e4f28d725bac891d5e15a04d116e64cb32144cce0d9cf69ceeeceb705895327b5800400a6423d7930966c28194d9c87eb976b26b6509bb136ef38dd08b76a86c320ca502d8055e9a8b0d87bee07130bb647a942f9421d772ae632270b91e1f350c5a2a49134c3c971d94b61228c45e6e890c82a722f128f1ca5e13c0538885852bef179e4d3b25125c41f369ab3f5a7413b503ce7f789562d3157690a49e6a95685a94440ce84cbadf35b6185c789c65de5825e66924a3ec307372d0b6f4321e95aed067b2325c7937f9128f67051264a851049a74682ed0c2b1cdf059f18009ca5127fbad314ab34d7fa248de097950228264cdd551ba40b97ad6f0a589e35ffa42b9e91e85f5084cbabd2af033c104a1d8715120e352b1feeeb51034092163b10b15609c7dc566644e31f100279d1752b23398668728308b1b449007d1b49607e5fb24d2b869293520ad9c8c0a23e3e0f36e84080981211d4d4fd0550bacdbea201debec0710a3479cbb41dfd18ca80fce8edea9da5f86daa848a0ce026b3b37f6b9535eb4c10eb9b67f531220e19e0bf1942979905c5052a0f700c490589acab28ebb27078544047d0ff1021d48e10be128695ffbb112dce1fac0de376b2d239abbb77c09db89a3037eb5a7cbe2fb13b0b8c8d1389e800a7fc4c081dc7ba8cfa1e857ab3be05d5908817ea8daf452e57d2097fa38c67ba7fa9640a254edcf886525a00e5f9adcfc88970341634655a9e16598374a4590ddd24ca0f3fa5323ebb7ad56992499f1c220e55dec22a7aabf582af77f52b9804ee96bd019cef35d2714650e5e1348db5b8ae20f8b2632e56523998fb57931c4a2d9d93000decba41ddfb188430e92e7c77970411a55aade7681ffe80e3641e5fb150ae0840338cedee2126c0cee76e99d6479dd0f24c0667258e553faf6c8026de418801e0dd0572ef5481a12d60f43385b6459dc0996c9d246207e4291fb6e43823ab05 true +check_ring_signature 4f1d54e1bcae6a6f1ce96605162c361622bd7e1d6aa977261f6e0511cf03620b ac5f15672cd7d82e944b8035a0d1a78700151fc307f3118464114021463651b6 7 68c37b316df5b7a3779dcfde11f49fd4c077bdfc4ff16d12d2329bc1cee437b5 19644ad1ebfe1997557b522b8c79d03250b1a1cc638e871611f0d1ada6dae3a0 0010745d867d114424e531ea38936c5de17f13d772543056bee852b959b089e6 1679cc97653a8ee2668ecf21594c1d1aa51fc022ad3f61fc152f351f439d7845 5d70c4ae60325fdbd9a8480d8a7ea3b056c9f44ec58faaa894c4a90b3f18a904 2e1d6c766668fc79d943327dae09677f8fd8693c536edd7520d465836fddac0b 11595ef83c8ce79f680188427fb9bad195751476661616c4920b184f6b018082 fdf7f3e54761b8fd5b692aebf8220d46c262ce4159f401e0c3fbfca181631d0b9a96fed56c84f3c4f83a21ff620cf5718f95ee1098c37ac2e1e736dfd261e10e36c1c5ef7c899b2722d561f4b8689f29ce9787950a6e4f6d5ef7b9b534314b0ac706bfa693a7bb36467ffbbf01183acc8b980a3973a6c308f3a1570b2dc5080956881281a1de46a337bb115a5f1e53ffe430f5039bc9e537973e7a23b6f97c06f9dd2739703e9c0350735e8811d7df5ff5129d263974708d895d54fbb7dded6a5f2036add0263ceaa63c0272d8dc39a733ade29c411cc82fb96ca7b85dad98079c118065d8a875879d4f785310a8e4ac8e607e031278aa7cab0b3a515c9aaa0d10da5e70423edb875bd9e6454e852ed175a2d051f873e74136bb6a7d534f0f0cb8ee54fd89eb2451f69c3b25433e19c247babbdadb24e1984135e05022cf42081fa196fee3b4b686135702c49394348558fed4067cdcb05a994060367f2f470fe3552853ab1f0a8a1305c0c2706853ac906494ed91ec7071fc412a24a8fe380398aadb734f8849fe445c05f5e3db119c30c48c335b17f9b96214038a75488d0b352f1b3b09ad27a65f29811e91072934d8a7e592ab1fa9cc675aa4f7233b1909 false +check_ring_signature 8394ffbd1e8e94aa487b54f99c9b9604b23320e0bcffff368f0ee4f7f089c505 6da9d2af28a85cc05aea2805ee2a7dd48f23d922be97f119f506bdb1b41c5446 1 c43ca0977efa501254310dd1612703bf962a684367e6bacdfa252be7d2549dc9 f181878691944959c4c52f04972f0b1fa5e655a2f2afaf28195d8d746a2845033f3fecc8a897cf124e7149840a5a5ef9f3a0917c7389ea261f841f93a5b45805 false +check_ring_signature 9aff9a8b5dd1fe585a5dfbdce7b601495028758cda27060050024a0803cc6e91 dd495fa9b6af1216c925d53d2ddeb290844620a93a6c1ac7da63e56201354d29 63 ed674539de04d169d221fe11eac01829fefbd0f809a6d314d26f6cf5b0c36113 73b6614c3af152c375d0aaf180ced2253ef3bb49e8916e19f6c33b7ac108bad7 a99dde6b2103c5b4d8fd087d71536401a3425c7698818996a3027075b7bd8945 c94034525b03e9770cf094f4f0ad06992b03cb13e8c5ff044ea6a139a6325f03 b76c9a9124037b77749deb50099fdc931a75f01e8b9fb7dbd7e03a95f7627b5d 89db9f5b48ef834f088d0ac83e815fec9f47fb3dd60aec834263188fa3fb8d9d 6b2b307b20269c5c9e7dc85b78696ab94fc984577b9bed65fdc862e172f7b03a 4e465322a75e45e7d5ae396f35975b2193a90c3ffd9980dbae43f6f77c8b3ff3 a4030541a0aa274490fbb1767a1835b4a38acdae3cade07651931fef6c21c4a4 e84cc0d46feaad89d59f9ce44264be63f4cc846fb0cdcad32601af06ce031c05 f4f845f7df102ed0c295acbc40338f9d86d22b520984daeb93ea3f35945e5512 46d9819d41120bdfaa962af57c275f89ae4dc3e9baedbff003dae0d36f10d4a4 d4447332ff3a76c36aa4887e8c5ae1e5af002f372ace23911e88407406e3e7c8 598ecb6f215fe46f4c028f0f37b0e7c380087df861e864575102fc0cddfe6264 3c7c61e160255259ab958c376170832689aa27667ee6ee96a1174969a25f405f 3d53335ae0b5e66ddeb3cd10f6541f5f61ba359657c46250207c7473e4f7fbce 1b6a9e408a49a3b26a4ad01f125b5c570267edad97074ef0ca91f2a300adc9e0 fc503e426b4a882aa52468f5ce487420e8e851753714bcc6d18a6908c285bcdc 076f2efdd51f7445cf6af5d9ca76b63de5c30fe830899db35d6ff492599f8e4a 95cf7a2011ca0d9f5a5cc52a25e0d83da23d74cec3ed9ffd4506d7b9f908fa8d 593f00d4441d37dd196cbc934b3dc3b1f64ac86153ef3f8791bd2dbb35297d15 90b2c605843e0f04018d17d7ccabd8ed3e7cfa1b046d6920ce0e191cc8ca1198 335521dc099b270bcc945bafd38a1c5653b8e27e135e94272429dc728b64682c 6318071cd703b439684e8add964ccdc75402d1f1359174a88ae6573220e071e3 9a57dfd4e8310041ada71a3fbaff6da4b30fef9b619e180340904132887abbec cfb02c724c8bce35e669855d12b5d36845aeebb75454275fe730ca77481ae6f3 c916bbada5575cfaa3f363f9e4b7721ee6fe4d0236551a2faca0dc6d2e55f146 f3f395ef87b86cb385e60e4d7dad9ffd91fa5957db41eba4495ddf346292d6c2 b8a34ecaa73367a871b4818b0d1fee5274d2248a629f7d720fa2d20cdc62cf2c 10c5b412fa8155f6786e5cf754f800413282da90ec3f3baf37c07558a1afdc43 498e5c135bf295556962a0eefd7316ecb0ef7e6b009e7982d101330995047e8f 7c546d55520c98b1fc68c2f100d73ed892671da67f99906411e32f94f18265d4 9b51865e51f963840ea0a4cddca08042471a5abac59618926d0d75e638e4dad4 1816b041ac63451fd382850c1d41b80e05c4baf7741d0f888ae8474200c09975 f4339456acb2e050db57e1a4fb3251757b78d524f70480c156699ce3eaef5a65 ba7df19ccde517ccfdc80081d8d831c4b9611d3b736bb3c68ceaa8ae5e2dc7c7 afcd10fc3776cff59a870019be79042964ff5bee8663c30e9718775abb3433f4 8eb54de6da63e77c11fd31191ad6fa2f55c63e5d7db8e110f769e11cbae48109 7d333b3e1f5230aa57990e16ec6428a98619273f1f591809e6d46bf853713cc7 88a90c09ce35c7387b0688f6b8328a9151261fe271cfdd25be3f4fe6ff85612e 5a43903515c39a9e763e5de9256787da38236cad5fcaa505c49fe3db68c78cc9 65f63df79d23dfdfdf5b9dcf83919eec116d51280e73e3804c53c4ab2565453a 4b260c727ad504c05ba58dcfac6b7249e883deec27b9a9b70557a7c48540683c e5a4f7fd7a988f23019727a5932e75086af324781a19dab0d7c741863efe0e98 90befced90e7bee41a88b0c87f4ac9307c4eab3aeb4ef33ceb309c586c8feb0f ea55aec271d225ad59a01be17ac743b76d94fc2dec6d2fbe3fa0b3a215b0a79c 35765db8859d17486ff06c3271e0380d92df65d0c4c2506e4d519536af5ce3ef 28de6dee092ff5ee6fa9e828231ace10e9d58d4e44351dc0a8ded42c616ff6b1 4d2111c18ac3c3275381976c5c26b66345396ffb53344718c70741a39b3f3776 1aee475612b1736d2b1461dea97ac685dd86621ec5ec54e43c1af3ea383f07fe 543d2b0f88663b829cf0c0855098d1b968d8ed550ac94c61d346bbf1aa585313 b4bd5391d4df41ba2ae64b17df576bfb22b8e548b3d6f152c4541b00bff32b26 508ee43ada36b7a1de6e335ca8ce5cc5909648c930fd5e04f6fbfd64a53a8a9d ea6b341dafe60684ae9a727da6798c5ae430e37586efd94640245096002a2c4e 2f41b177d9a1a86e0887f868f29c9664c5cca0f74af5704b9b84bae2f58aba91 95e854e6d275eba5a66c4feef8b1d453cc9074ba3bc60bb9c5dc8334150cbf1e d536254b4d85a0039a8fcc9b713b58c73b46df31c0a43f3e49bef6aedac73e09 73467fda54033184dd2a5150bbb1e49a9452f4998762d6150f488a340aeff7fa d20f76f00d64abbe463566a2f34cfd2fe1189d3f63a8b2feca862bafae4380a4 ff2acdb0b718c2eaab3fce04de801972298ae9634ff0a86530895563ce945dd1 54233fbd6c2199df2088faca04414b0c8e25d8ddff0783089e566b85aab25e60 58f2aed8f2b235d926256d8f36e9ab81cac6b2f4dcf6350828db4c1f8922854f 3e525316639f41d390503aa72348784b083fd15012e4795c71fd7aae73aa1b59 8d695049f370bc9047505171f651eb655caca99c8f37126f4dce9f29c209c70ac64d53964863839d03f2fd95c16699c09c28b474b8b18c153c6160eb73354c002485fb689f64520a88d2fcf4a7a319ce9d62060071624ab5cc07373084b032016ca1f02543004b531fdc70aa5db747ed7ce2368e531b0db89ad50c200236490cd0d514b74ffb560fc247f8e770e23df9ded3d40cf9c3f5d4f9c3465da382960f54061d2eb11a66384dd85407710859796bea3b3bd887986783131f67cc242d0573e903b3ebe02aaae20fdad47f3004566e02f94309a2429fdd87fc80f55a1b079632be77c28875655de449c08444d9257abeb0c1e4048a3c9ba96b280fd808051f3d980cefbef66c7d916f0c2cec4c374ca5d46bd274baa95b6bcafed7764f0ff582d53b6eaf38758fe5fbd0fa7ff09b2e990b74a18a00b214c2130fdafb8f0648d827103f290a0a1dcfee06989a18aa5a0aaf1f9c0ac3a6f1fe570fd0019908297f683f5a5d28fbe92bcbfd3c5128481378794e94181694e59e828a5ac8ac08a90972631ee5378bb2e260e0471ac61293e736e9548aaed8b76ce72bfd7fd30a3342a58662269fac249274e9f0f8eae9e33b51f61b3629e94f2ccd33d037d007c2997f14e8d5df6ef1a7275a26c328e22a9d986dfa9ca0f6fef7cc682da27c07e0393cef3b9493d5aace6ef8f7f21f9290e63e6808fd2345c02b228deb5e56056db2c4ff4e1e33a3b70a5a4550bedbde21dbfc290c4c7a44d17991d208b7520281b328af34583566d4f50bf76ee6ea170c754c802b0a441e0ac4418e67a6530737d965a5099652af02b5f26d9d3401748e9dd14c445391541f115672c83a0c00eaabcb02b8db4d26fd88a5c1dda622a3c72f8ad9d0b80ecc7c89aa01bdd320012eeb754992bdd7d1bfb1811b91d5ebe17ea5b0d9d345df4de1f4269dc1bdd10dca4d178647f6ce4c7b0dc0bb4ee0fc31c3c79f5379dc6af2f75070300fe26e0cd4cbb55c78de00e2fac65d8aafb58d728b2f02c8b4b662dd1b9754f3a17eef0af8d9d1ca85539b7cfa5308346ff4909bc5ae7cb35c728ec51d922ab56ce56b0ee72358b4fe149c86260a603614598e252824302dc738eda0e2dfbea888239f079a30016f5c1707c534505f563699025972265888ee8b03a0c53440357b70a90240f2f9968526c3966bed891e0c9e3ff92900bf9a164ba4313e1bd48e2bb1c30f058f8c232133c216989ee3df52cd4024caa7d0d340995054d65335c72b34410e2804e0307d54e0c9b24be777ea4f48de00b5e525a6e7b2ac67732f4ea10a220c28e8a3144681bafb6c26e210b5e32e4d3dea68055c7799ec385add989df11605fdca35ce444148e785ab0098540ae230da102cab1f0a74c430a041c9fc10160ef23c9de3514461d2cb63d8ad8622ab98e94f9f7fc1c84f542a00a23a46e7ca09bb329f34b19c27ac26d5a317f3ceccd35f894ab1e497779ee6593b12e4e9ca0f2050484e089a64e9768e99365d6d0ff992b7ebe876de312050af4e129015e200e1d1bf001bbf0e8e92c6a88682a7761d4e10ff7eeb4d729a7cb1b55b778a6f072df53903ecec25cf61caee3bb51825da8b4aa0dd8e3af8030f9bb3c6674dbf064b3ad5da33cb89f48c9aae4dda08efac29992c088bf6d51e463d726dcfb62e0f0453c4dfd218914fe8b19c72512b2521d2b46716ad72f66b44e6e043b021860d18aaf9f18bd1e07927163c91dabd46d88c613de62252f5650ab014c6a0abec0af6fda099e74105fa1ca75b36e8cec38b9e2f9129bf1811c01156312f2790ae0ee6e4c877037435af5c26f3221785655d573141df353dc38bb9bd496bed06fe034c3c30ecd037255f1d8ce5a4429c693e16c2ed6baa9cbf6e43e4a205aaa6640124106c9a2d8e52f3c105aa7a168e33cf100614bc8a3f4a173f2f35025fc60406d42c76a447f10b828a142b107465442cf729128b710733def3e3c44a4eeee509b5b63647cf6378d5e483dfbd26b3a923fcf7e4d7de3b15da277f67492aef350a61e5d21a50966c591c9dba5ac162a285dee3ea62a40df8992d176294703fd40f0fc6d1b255ab6a9e66f194bd7f198f8d2db956d844ce7e8cead5b2c46371010e31514212386d8a656ac594a09efa522c1993ca89fa7dae5a65652b0fd2c9eb06685e7b82cb13bc90841913cf3cd4cea280af08b4ccc1b79cb18bd12635fbde0ca2e76bd416bfc66215b63401fec0238a8aba851d88b4f55e6c7e0d70c8e8b109830de3642b0a59ebe7ca0513050641c678c5dfedc3ec9a93cbb95df1877ccf0aa028ac24baf1b846a23897bedd0b3d2fdd7e76de1e6c03df0a95855b838ad303769d50b32902f3dc3def3e6616c22aa5a22f3a0571b49f77717b58a96beecd056e588765ddc812493fa051bc7bb0657d1fe12e670f79bbd0fe453f1809826c0a0c267be69bf47f9164f81cd99687664c6a555366c62a58c1bea79e4b9132980fe54229b06a43fad1e0430d2f20e1c0cfb26723869cef3d340ab351bdf711460c8b76d6d220072ec9f126e1aa1a399f1164f096c586fe687975a89935cc669c01a40a5ac14340868e6bc68c669667546301bf01c5d87ccb047c0032cd2e2001065ea8e5934c180b21842628020d72699981b7cf2dac53c765cdd6376d0b8cf307f4997618850e98b36dde2ec78121006864dce16317072508e0458c94813db205696693e26540218e66a225376dbde58ad1b4926ae7882e48e0bad61e99849608c85174bff1712a7fc23534dc66bf3e22aa00a3e83a1182ee5161d8023a52cd001772c6d4fa40f0ae6c309855bd1c21d03806d7d6537cb98878950419490b390bee131890c6ecda5f5a588cfd449c7f7bef973c6fc8a7dd6041becd3ef8a4140d25fdab45b1d9cad9be0adca1c9dd9c3f4b4daf6ceb3b90785400e13818f53d09e997e81d984dda05e1e879fd36927ba6d279fd10e54b41a9927946d0f662950de713caad05a634054931f07cfafe19bfdb1e6d6c563e98d3e4a648e85d6eb10f85f1e552a7de2d5687702e68cfeabb4f4e83b809336f4c39f3ba42dc702c440d968d3ba858a04de33c006093be8659f86e03fc51abd1621879b94a7894095201c2eac5a24ab02e54de1f231bbffdf3d282319580e1fe8cc428c18b7a88c8ff0f8523f5af84a37e78ac035d7b9bc17f9aaadc72c57bc15ab52673550193e4d403fc7fbcff51d2c8a071bcf06c4fd88a61d09a4c4f6e6323e29773469513d06b0f2b4228c33a4e106d4fc79f6cadf82d8f4711519e9ac182abe0cbe4c15d673909e8cf7eac6854f6195edef6f7a4b3435bba91db0bb65b9115eec017c58f2b7f0e925b1c5ae4856d47c40839112b175f6347fcfc49ff24decabd00447bf3543e08427d5ff7ad07fc32601e43f6359106d956b8d57a1361d850c1aae2a6be71960c07fff276e29e074a100fc503b8bbda9725dd9a7acfddc22151cea563e7f4b00e205103ba4132ab3f539b919f735a6ea10168e367792ce3baadba09a519742d09b16f2c657c40f9fe54786cc5ec6ddbad85b4ef4ede56cca97dad127b26745800d496aea5b4f2eddd7caec01552443cc76a1d0d744a9326293ce31c53f1008600921e3b9c9d12f1ccd254ab5e4cc502f615bf4ed6564ffd9de61602f37958b108812a92a8f4ebc7d88bcb845bd2d846d9e6faa8883b8f1b399158ae34df786000bb5296bf72e0ee1d1b708c17f2ebe864235f5f9596059888f71766807be505080b468a3c191b834665d263e6f71571e72fa7e964d9ba8d5f78ba8f4369dd5e082be866c377dfddb7578ba5237e92ccdc4ed54c95e8462dbe5f56b61f4f7c0309d9a444dbe63c48c40c8a7a4751f94b455e2efb60b96226ba0f5602aa5678380fbfa707fb9e2d637827b75bc436c9a9aedb99050df2b435c4e8cee624bab5fd04415f0cd8d5ea6217d05689e5c6c8813b0e842114a61fe249c840528644fd930c6f7ce128e9ecbac61b3b33fb226626f989fde2ddef6daa3ed21c91328d578b03419ff0834a7bdc1373c9f8315dbe6bb29514f1705c375950cec7b3980177bb00fff5a8ee3779a75989f0c5dd286f747744b9e9403a439b416a69ee06c65ef505a5bc66303bae3b9b17a0ac30fdea93929c491cfd8b92ab99420da6695ee2a207e0c5275236d858775e4ae115ae369a4e4cc70410f87207e1b50b67b981ab5d0c3876d1e092c0877234eb977da628cea3ff301500780dbe9c2754c9e0b656600d1c44c86566a1d6dea486aedc510ebae05d4bd9b06c7206ced3e3f560e7a40100e23f23d8b7e88361f3118463becacc76dad2fe0207a6a1ce35614fa27cf44e0e402045342aca2ebf5796fbf3ba4a3f1d7c13f369af6b05e60a07d288ac5ea7045c78a6ef6490289162e80e44e12989f6c85ae1b9fd4a8954d096497f73e3b1033be5240575a52d5b4354758acef768e780abdad0d056c18620a8f8309ed19c073a0d97d4e42a5ac90c216045d4d9b7a07ee401171291afcbaea49c84a872ae0e898cc58e599de77d2a5d0b799dbe2e8d1dd64318f2674abba9f330788f3bce0fd75ce0c31f518a95a5116074799bfda851f668d9ad2858bd1c8c16a49d75f102bb25e2ebf6c9cf18c962e43a9259e26e5f1ec47fe1d09c6aacaa3bf0f1c8f501b594ab345dd2fac304068842c0c99aae3eca026dd81ccca1c410deaea058d8042294e6de58d3d51cc99799f655b3ff656c1e61a8c75a85ac0552f1f84132be094e60b5122de912d896347b68542546511ebabea9f2702e7ace83efd2ceadc6012e37c20313a64f4ba60cb504d0cb411ef97fc8401cffe9017f49b5f3129d400acabc6c95059d7ac6729553b307281231b2766896ec0f720f3cf6024cb354e50caafb63f83e74026c3f74c16485d2de572e38613f7c10bfd1ee094639d86d170dd2269e961ba8aaa09529ef2722b3df78c3213033d967d1413bab8504971cb80a10712d0350bfa4ab8f604836ab54e00e2f5ec854a6adaa07d295681c7b902809ab9dacd08e996963d18fea731b89dace93a87a96a95971a5a416a8716522e80641add37cb0463951cb521cf2360fa25018a34584ff036c3284f5e47e83ed1a0349edecc75fc8f1da90066c046f557fc668f91ab0eacbbb05dfa1797c294e630a78405a22bb5e84c1ac1c139b76ebb5d332dc13451f3a04cfe1501d098598b20acdd1071a105b5f41650a5c0fb526c27eb6e52f59b2ee0d50ea80c444e1120e08b3316abe835cdf8790a8a13628b8ad58aeab687b59cc4e962f08db385c73760b2fbf61bc5cb019a136ee7910a517e4d4f3fd6007bfd0e1a87395ede4d8a89000b75fb19a70e06b4dbd3e4f0b7f2611821e53629c7ea0d2d02db1e9ee3d5eb1002f5f6e03dd42908b06f252d42b1901c1b704481d4d48a94b199b6d4afe89bf0080f55ac525e805d6255163410d3cc25848a835c681b8a1fc577cd94a86c0a40de055f0986d25df9e2293412997fd286c46370b228f30955a1e20c6980c2cca08258c86558aa9cc3525c3aca960b0c45b2101d96a41d60ba270db4c5573f6360f6b14956ea985bf27c812a8dfe9a206e77b09ebd88cf9bd1fcb2841ba0cf33f00dc2b8cf407c68238e4dea108fd9ab5d8d63515857191b44ae2da6a21214ffb0abb584d29df200f1af304af0ef8af015345c44b6215b3da98d8968e8b3e7b480c true +check_ring_signature 4baab58f216da2608212a7b94a29ec9f5c081c4141ccf7a8f02269424c859aac 0d74430b3d21519315cb80fee53512f0ab8e3e3c19753a23b6eb62c4c3959566 41 209e3e1fdb2762c060c0d7fcf82336f4b9e4f49dd90716b690f8ab9d486e37d6 36f39c352739f3427d26396ff79b01e946362c3921ec840ee1f2c5412127eb87 91db4b7774fd6aefb063bd4ce85d67a0af49bcd9f1a91c216259689a2c2b0c52 4f29cae9bdbd5ed472b78ab31840acf02a596bb807e40090d081b23301113e1e 14818e653e37d8bfb914837ac904e9a00ff76b0c4abbfe37bdeecd06a1974e7d d5ba5f11cb6df01f8cdee9dcfb266ca4e4424fe1fcca6be084cd6dedf03e6c12 85c33ce153bee20b9f0bf519c9a46cd05ddf9207d618338f8e54b8d4dac922f6 004771ee96555e05d172005ad4e7a05c52bae59d42e62b16afe0a3915898742f 6aa7b324a3f9fec1a69627e09423c7f04b79f8ef1eaa5192aec7554615afac73 066c4cb2d357f68372b965a2628a8743c40e8236bf4268c1383041b3ac81a96b 96e0c0fa6f83d50406aea92f03443aced30873473a32860b4a7bf66da0219c29 a2a2460d6dd459a86fb08604bd6adc562d7c58e2402fb9d2d55c888964735cb2 33ae3d9e1abc67b04fcd11f8846a56f4073e17ff288df3aa5f49cd5aced4a068 a9483c976ee3eeded5c559acac86fe046f9bf85fe906653382680ffef09626c5 e44d7e7d7a0a794d77f304bddd03d75f5cd1486418bdcdabaa817b290b49c9f5 c589324e5013411c13b97415c75a8931e096323ef06c1ef814de81df9977d562 10a11d88a88df16c8e83a3ac03507aabce5d6d338fd2787d69f2b374ffe32b74 e30493e48213a416a706028aae59b4a893888f27c5d126fa5c8e70e76e8b3438 171103a757672f7ad53cfdefabe0202ebdcf8fada001000d3a3be69da48d199c 3eaa9b933bb3824f046ac2dd9663f314849519368c6662b0253059ab10b9d886 fc969a220cb9c20244edd6929ee5c4245331efe929f742d38bb074346b1f990f 976225aaed07346e5c7b99055211e1cfe70d0e63d2677391a699c1598f764f6b 59887d09ea5e58aa1e5573307f2ac60397de3fb7590f9f8754985a3918737ebd 9ca298733642f0ce2a51db20dfdbc54511b441fb32087e310d9228b4aa446f3d acc87b242b997aae39faaffd35e3373f2b2ab01f498e3612d9a3e0b8841179f5 5c641b01d65f30cd8b316bac3df29b08ab38ace38e7becfaae9752fe208ff99c 48fa51bb7545d6ce8b0a161301c030ebd10fb173395dff3bafb472f37392d8ef 20c56dfc4526f7f9d8461b1fb15bd3a80552464984bb5d882f6921e4bf8a77c8 82dac074668f572c9b8da1955d0ff722d6767210ecf0ddfe53dd49ea9d7b3469 92f8bc4ecedf667a099a75e13634a21ba60da93dcb3c95abb9afb2e93b0eb319 1d0fc48f927d398eced77c227b7a3f5381bb425fa84b7dcea783265ea0a66e48 dd09a23cd2f68da33ee91647ce1409abe95014a9145ded8968a8cc82a0b13c81 4c0ba7932aeb0b3eb05e1cea3b54091e6e2e00e9e8d9bd48c7ed4e1080b9f3f1 aa0d6a42c1f574418a71557a4bedbb121e9ef778b7806ea4b9cc33cc3df7e2fc 402375bdfb105ca6d89de1dd8888b8bea68fc289d63ef51ae2150a7e5d8e76f7 66728c6128ffd07a4f5d70fb9f74ab42d2c828fdba1034b75702f3325c4cfd52 858bc2a7901d32411003728251337bd679515dccc5a90abfd2ba4e927c7f6eec 6b10e15a1f7f57470000b75d40f8b5d3d7d6a0e3bd2341436388c26b19ace543 5d7c5838cca3206e44c64b726e3de088efbdebfd40a43baa902ad4e9f09dc0b9 69af44aa0100cabab3391104b18d6f3c0b1b1006364b39c001fc206fd0f6f44a 8d2299ca4f502aae3d0b90f4e61d5b5d17f5581a1722f0bec17b7cc836a4d6a1 532a4f1662ffe2e362f587e28dd8d96c6c2e5442149719521f9828fc5f3aeb05575f5a5df9ae437adba46066c91c2be261ecf876aafc4b0b3e8ba1f967b1da03f54145ae5c749a12f8f4190e0a788ebc46123b39850138d35f092cd95c4cb80ed44997610a935402bf2debeb1f4784e481b9c6fecc211c753c6ca37fb106be072e9f3d637f2cea963f26cc39514ac2ab790b0dafe3715aa5ec3edccfb9b28231fe5cb111f87d8175dd3ef52a7e0ff20a8218aefca1a5c75ec712af25a796af0babc4659ac54529f3890b9b9b6aa1f9d62804a8ce5d47405cc6630fea05be84072d9e49d451b062e63df0e06691a3d2e1f010fb23e31290b51ecd694fba182a084db89c04cb845b2a5cd6ff1f411b24f093451b0f3aab6488199f6b7d45cce874a54f2c387dbf9c9cf49787cf83d56c8e3c8dcafcb66561958c410ddd7369a909f00ca556998ec9e6e33158edece0fd1db406c758823c74207c41fb164c36ad00346d067d45bdadc2cd18c834510486bc400e2bc520b9cbbcc6672309b8bc9a016e77c3baf29fccced1969986a5f19653f813993ce828d2817f6c8dd6d54d3601bc1a876305b56f06d34f8f5bd72ffbbbc81c945cb25fe8798f4f478e49ffc9079b561f218f45f904abd6227340be2f684ec437fc5cf16a54c26ce5e8b2c46d0d64178cfb2dcd3ca5480d78c4998db91d96612e5695d5aea91b50836866078d02fa4069f4bbb2e6a6b11d98a05ef26f3e3e59c6965ebf8f42c48eb4ab0863ed058c56481a89ac7db997e18a5780dea8c9010ab8fc13aa8a8aab6ed5a872075303e4ebeb40900d08aae05bfee133d18631845b48831d26c0b04bcc94d3019cdf0a18667a744135550fbad75a03575120771fec050806e439746494ad2c85b9d5019f3b7a15f9eec2635d750292671dbdcf134c64cf1680bd0d9d7af7adb7f60901168f1d5dab61608766ba4fca438a5e51418cf89ebea0bd0d3e35949358abec0f6b8216f3f8ab54744eae6f89a4185c121f62d0a2ca2e54242b330874ee14ee0b003e937c419602cfd8da190ae12e6f2cd2e2196c6a0d66f5aa19871552c60c0773a7ee59574596ccd4061c954cd2fef275f408a7890d55049304e4f2e6d96407b0ad31f30514b1df58fff2be2f3ba1f7a703c65c4d5078b9a8aeb88af4a5d73d7b8d3daefc85141f6f7d5e69cd31914206bf22fe2669f6f0bd398a938cce8304049c80ad5e7f3a59ad1f72de184f8fdeffa032a1a9b13b7741f1acc85939e80aa10a1b2ee607273f0c1068015593c9f5fc930a52a96cc2d6b79d3ec1dc37cb02cb9b1245a302bf4589270bc3cda449e5a159c10bf6be3e35f7ae04c2df72b2073aa54b8b68fcecf6d439efd22e41a2afb0dd8c570a052c5fcc36b875bb78df0c3129ca3b28b8f363d90b1268a842a7e39720c95a5d36d2dfce238e7b4eb60d0dff344bfd266f802314af226b996ad234e70195acafbc61c2ac0917a305687301dee486c053717a58416af62d3e82654e13492bb1756a3f0608e1ae433755a40adfb87020709b47b7dc6c6781ed2e09d792e20d2a7054b9cbacb5ad8a62a14c0f10e229b26b1297c2c53b3b73e0cb18a1726ef42dd7c6634257c2aeabc2bba401f4f3be9e20bd7af71e6957775f67f50e1c5725b07d73ae96eb0abea80fdb6808e5778b467366df94ab230516d452757e1e5bbb95bd272cffcd420c31d2254500d9117c758cbeafbb36d11d987763caafb8bebd579be77153d8a68650cd361d03a819085c2dd3f5e4258637522d4dde6951af1349fdc12fe08bb6594f8537df0df686c0668aa7a3be34fb0f1cd15077dda89ca4ab9056d2c533fd45aaebf6db00b6f0a63d9d49874027d63c3e8b84232d50f63aa8aac8ecdb69651c196e12d102f7b9189e43dc341760f26eaf7416c4c342fe7364f34cff766377802c1fa5d30ee34bdbd346dc9339f2fddde185839cc4f90ad59e9bd83fceab708ac25e1c500741d76ea5d81a3f3bed01d530ca92ab6f1a8790df6c741afb923816d5e33ad5036b4daa3d60a96a8685abd0ca6823f317bb44b153860796f3faf03f3c7b0f8b0b9bb4f8a787204300e742383506218a6be2e8543b1ee308347bfc591ed7b2600f67e8fd284bb6d0c2c72b8b081ddfb7bf098bd93354e89f53038f5df73e694403e9afad5d5de9211da403654bafd4c5d67d9b472d842ade96bfe47877878e55068012d6a1084149bba5b177252c1980aacd95fa54a2ae048331d67877ba732b0299aa2d07426e2083158ec2531d89fe166f41311bc039245c291089b5dca2e90af92268ba8299061a67c65c215e4e2a274cb2ade5932c8ab3e4562a96c91c7a0faab72fa7aa1303e55144d52ca3245eaf073b1279a1beed87a1794f1399a9d10db776005bf13e90131a742f5ce9ba221274c991e24ebd1251a2c3c6521515890d78e70c97c5daff813337a5bd1b19f2ac073cd67dc1f9f2dd8d73c26d9d43350b09c621d31b94b7bd19222d8f7e014825971039cffff250182f487c073bb01809673216a70119ab3074f17c33a36f6ac9e6821c7ec04ca93b6f8a13a7910cca0526a5eb39585528e6d8133831ebd9db1a6fb7ffab75d14098b19bb886d809840ccbe69e266d29a12a1722396573128eea054cf6c439c67800d2fea5506dc0ef0d01082b03e21717b1cd11b857e82189834f10879fe4f50c79f5eaf4e0a5891f8f35f50974bf9a0c0b9f174779dca69a57f2e955c78cb02099b4a06ff759e9020a8c00dac671b5b790fbaa515f17ee9e31cddaf85f090d961feab82302f1aaf20d3028df6dc8c985c3fa1436f3d0b516b433948c5f73dacbb78a8f8191ad124201c1ab7201a4934de8b6dd55cd7dfaaa639688e97e9d79ba3fbdc3bdf490720c021c948278600aafd05920f8b2cea13f1ee7fa80f8a4ac5a36398dc4473a51c40840204df7ea1cb80953a9d9cb5e42945258dc2caead723dbe8daa550a294872e88e8a666bb9e0005a978844aafcebda43613ea872e9c0e0f172501c84f704b90f9f082a598c109d4a72705646b850831a69463f1150e0a529f636d0411ce797003180ad9f1bcca46e00443a69421b8ab6494d2da8ac5273bacb84a94b7508100df7b497729de114aac73a4f420aed2ca27497a0836329054b379ae49edea70a03794fcf2c9779f0be5812503daec1bfc609e775690195c1e497b955ec67875b01e4188b9265586550e8c9279b2b374c9c8b4a7f61ba7b76faa061ee60ea6b710c466589a6417a464a0bd4659ddcc4b4ad2200076d8e622f8fe628edd0e73bb50ae9dff55c43fb3a308443e017456f7d59455dc3c8cc266a16b0f9360a71de9b03399654fdbeaec703dbbd4d0203d910b96fe878c2ab70999a4ca807167a4bd709f3e7f18369f4c8747c436a2c89465622430b9c7a51687dcce3c66efdbe93a90613a79d287dccc3364d7d3d5b9a2c32d95074a1d56d9077ea04c818f92d431a0237283cfc30f56c6dee35abadb0e5cb631e8520314b94855c7672c4815ed60e08df2d7e22b7db3f491b5cbe4fb31f8041a446d95c8fcae7e3a522895cc15cfb01505c260423f46473a82e2445ab234b10244e8d4819c19f7f6c594e3025d1260cace0163e6d701070c5678efaddc36bbc6236e6a8ddbd2c8b35611388e01ed40b54707db0cb8094234404bf225729c9ccd3d18ace48b84521f11e6abff6223603 false +check_ring_signature caa408d732ffb0ef6010c882f55f37659a9e9001b28152c121e1249b8488d44e d40d0fe1c7a42174fc827a9e64882cb6462b882d9a336a1511c4c2fed6c16b93 2 bf21e9744f5f9ca71185a2147fb22638121dee2d6a463b8df55723a8f3ac89be 8dfa29e4d0caca1ddb0bcc64f4ca78f0bdb8526ef4e50af6b513a36e421ac453 ce65e73cd8ef2988bafd82276850e59945c2d1a312a0ee101405de038fc83f09d84a1097439d0c53c29f848d9ef3936cb9f362fabc24e03537e7bdb2cd6339071031cbc209744ec037f3f9c36b37a08b792d0b1dcfc1bf4160d8797514c39b03d39eaeb03f02b943fc121fe7b157ad68f0ed5af943e22d08da556d9f1f622506 true +check_ring_signature 995095e2de916ab4020bf97a9e24205191e31e8573ad69ee0a9fbf53fd72a5c2 9ca113dbf381b46b60afe9d4f7aee42fc93283d0bc9b8cf0f647e233ba7bc0b2 2 2d837a1fe2cf9757a54ae2601667ffd123f124751cb96fad74dcfcba40795948 95be98582c5e7fef7c3aebe5689a760353d303fd09035a705e23e6d5b5bbb253 752f597c153c7612177661ed3147e04c929e8f8a7e70f7be584f13de8725880504ef008e6f2a90fcd00a62d96cbc797ef5e489fce23c8cd6f5946e56eee7ab007fdeee7b9f0dea0ed26039026289e9f7bedb82b9b1cd4ac748c93911d1c063005b9057a1ae944bf54aa907a54156cb6f8e2e7c67c8da503e8cae460dacbfe101 false +check_ring_signature 7c55a5c01133a98f570657670bdf4856f4e922b516d064a591bb9ccfaaf27b48 d9ca41a7c48f114ef7ca74976a12f6ee93c1d85aaefce7617f7c43c4824d025c 234 2874bd461992e1cb9c34080a7ee48d70c50d17f13247ed7ae1d13b5469708d19 500a04ecfe02e43699e6798370213339366ac220b2e1fadffcf88143ec492e6e 0b5b4eb09d6827eb01aa7e5c63cc9ffa36ca1c2677f2619b038a8c4b818a3a97 01d3f63bce568570a74630ec84495409b5b75fb29528f5093824306587686e7d 8e6ad5602c1e5129cbc845f584e360cec29c8e9a809610108515066c8d87fdb0 f89bc55e270a9f2ea04400344fcf6cf0a3d3060f75047220b29127f4a86f8c08 ecf11412f05631cd93c25f9ac3cdf5f631348bab1545a184d7548191a2c10272 79497e0b8ab9ff486ebc65e91d7082b6db912d19e88d33f7596b6b38ccd6684b dad27fd7d3a7240b9acb60ae14a3fe5049bfe904fe81688a87526f84ca6142cd aa6eaa03fb399e594b4cd07deaafbb10c4f766167082c8a5801f4069e85f32ef 43f4e029241c12388cd374fad0e6a8f381a64e81c5d632d1f7a5e4a470e643fc 97ef9cf38ed55c84b68aadb3dabd14f1d296e8c20b77e0439e9fdde4c889da70 099a647650d131cdad7bf32032034505ced5487b5bb53c4e03ef357f5018cee5 ab0d17f8e845645103c86ddacfe1e776c4f6abf4af0899ba45ecf1b861c11cc5 da779eaca19d747801ddbb56633837db9314cb897ee94d8c22ff7a12f3b4e6ba 9da0e073be60833aaa1b1efcd9dbdd264a5deadb10d31bea6000c6f668b7d3ad 797e8b1ccae55b359274da9acbcd32b28037eaa76f235a04ac8d68514c12093f 9eff975129643f2f7b5a6dde61beb10fb995292dabd30efa9d55cddc7edce0df 78168d0c791db06b77bd9eceeb6bcde1f1d1c1a7597765894e3badc3b05e918d 7068929852bff478146fc643f3d89addb8dbe7416c8afcadaf025aeeca78dc7e 888d0553acc36a9975de254e87876b14aa756732cfe14c9aba046ab114bf272e be943d0d2be1c9393b4bd765897d2fe24fa61a1d9aafb834a3b1b9babdaf1e3e 14a0697de94f0ceec6b3f3dd9564f4e4187bc6eeeecf60d42aa28cc7c1b1dcdd 07ac1e4763dfdc128692d3c8880351cd6969f4f1aedee55dd5e5804ba199643c b5dafd222cb5d4a74b7cc703dd106a1616cad507e5975ef567aca3b13c311f80 43dbbefbecf89199d00cea0339cb943680ec2bd2e24cf543ee3afacfafdfd608 3a1278fbafd4d81bbd2f0b20d66e6bee0512a45b35b2dd6f1dec5aa8b8050f09 916106a0aae5bf5921d73f98ac95a7a402e1c533026ae7ae37fadee91567adb0 1d07774f0a75dd6cc5a9c125c96aede79c00b503f08023d70aed2f5442a767b6 2864aa0c20952e6223f0f3ddfd4a39e03fd16f7aae4ae72804cb5836a6ca18e5 55a6783765f23d2ae1d398f9d73494a9bd3a89d183238e2a69d6254aa8e659a3 85d60f608dd8af54f262f5579bf3a8718477ebbfd7b9b8671c75939f9fe782c4 0c7c77720506f6d0dfa839a13641f7b8d4417508d6d7cd32b428bf3b2553cdb1 37124dadf5407e5fdabb2ddf37428c758c9b6cb1041a7d90b5b428d07c5ea9ee cb18de40d4a2979fa71bfc53ad3131ba23b3aa420f40439eb9019548ecadb795 6a4bcdf1b5126cb6d57d96818ce2410fe895e3e66c648666276d730a9fc255f4 c31a9c1be05aa5015da9eda81c6eafa719a722fc518203d3eb3a290292e30410 3a2995595415a6de6719d5af3a5523f37a8142a38a6ac7028b66139bdb95373a aa550ad1c1be0bb9ab76e032b3681e019fc1c6be426780159d7cb70bc284795b ecd2aeaf2a25a2148ee79ac9d325c80246721288b8bdb1ea7a00fccc52be3843 a4412d91598bc8a39477bc9cf859bceb1a84b349f1cefc50d40bdb76bbb880c9 d20b9a1fd34e2955c185e1d4adab39efb83acc8cf34a5face720c1cc48de48c0 299de23c0fff7d7351e27e3dee3d0ae55174184a4ad5aa17a10e0fa58f98559b 514ddfbc9a75a8f98cd636ae85cb2fa58a4977c6ae6b73881e27db443461e392 b80fb8cfddc6424bfce37cd9cdd0148a8bddc73ab730072053b488aff17ebde0 db13aa25b77f672875a34651d2572f5d5de93f912991d257d50eb0ee053c1ffc 146931c8eb3ca3126cab21e7088c59ba3bbb4eb606b35cc6441245cc8e187e49 b6b519ecea0d4bbe6a9d0b38d766e6e3b63e38ac739445579cd58f0a3253abf7 012b3ca990452c3cdbcba160763fe145e2812c3c8b07a8cd4d25dbf99bf30ea2 d44b1380fd2a82b24e0c9c4ae801a23f0b63236d2bf76e58fa656d03af70c886 db3afe88a8de37bd9071a7f41a8d64e19d6e2226a588cfd47632e1906ac7c261 fdb7cc6a1b03bf565fca56e0dd0e59a593ed702e4519b223919f17e985b89d3a dbd9ee226ebcf9d8354be7195c6500b3bc4f30773080130461e643c3bebeb082 1d5c03fdfab1adad9209769df887ecd5484598f1c7a662c5978de3eaa537e830 fa2a4af39928433ba5776f81bce9e87d18388c80fe87fa4d0c58e258adba9c37 af0b35898fba30bdac00fb7862e97616eb49797c56168ecd746e3b342c09f728 6daf144527e4bded863f947f188a0ced479fbc0cc0325f7a3e746ede1125e83a b2c9e267e6dee0f0b8424b48d17c5628c9c658823cbac31b1b614402abfa3479 7815a78994f6895de2693317d9d6568e81e2331c788f72030c3f8fbc0857a036 ec714b23d979986f86ca3ef7bde5248b488b5f78f80640aac7b154ee635eede7 94d9850dc73686054d891c325f4741757f41027087e4000fab258f28e4bddce0 0b4839450169a4d0a9d3bf5870d36cd31dcf71a202133b8346a522101369861a 985532e94e8997c0472ff69de178ba9330294b75f1ac6c848154b8875b889e5f ea00e14f8d3f4fd017694d62f86cf563def72b8b39367d0519e4a85fc52c5de3 7f88b2da3b72dd642fc4f7b5a1f15a7fe5e11f073e4adf3c80d47abf791ee66a ae05deaf3a76f0b1c5eb3f0517ca25e0d7c11509671a2f95b7e30aa1d920a580 b031a9629c2757778eeb720b5bac1b582e6e3c81eb09cf88fa7f12f12182fe12 de8252574f45d03f05d53bbfb2f3f9107c57c978b82c3578bbde3746174e77b7 d81fcde1668e1d5f11db5cad0a7a352d914f279f67c07ef18a366a5316620bc9 9bf8047ddbc3d6d5cdf1332a21af0e4fd3b76eb59dbcd31979a7361805cf9b01 5d84109014689c4b6f95709c4504738198f56ddc1f52bc82339710e878b01cb5 e93a08ddba227e134e4d26898728b699667d257e1af09d31935d5fd249d87ce8 9e7d8670fddd1861031046ff098f7ff35d53c2677ee5f106403aca9d79ffaae7 1164e7018720bc048fd9d340b450b1da80d3746b7efeb6e532c6a2d7ce5916a9 24e9811a8b89853db2bfc999018c05399ada1e498c8566b75252e916da29235b d99199fb7d6bd214e16dd68f4e0537d23cd0a0867ffadb835ba22b34a6df74a7 de65c24c2b5a263d9acc81a389f6e7946fd644a002b193187e0b743d7f967c37 2fbc5c8f173a5c8eefe80678139869fd5936a9dffb603119aa0490f1b0ea59c3 a7698cb60451815cdc35c69f06b85c97f765c5d1f08ffc33225841e2ade397a4 ad3ed3cf2ad2511d4b0932eba3219703a7df91178bf2b8d6b6525643e2db1065 5727b4aa8bcfc69ffb58c48dd772587c0016671399cab5261bceac8070b93e10 81bd8430d015a493e7985cc0f21abb439dd6f95cd1dcc51ce2a9705a3d08ecec 284da199391dda4259d6dba4536ec92446c1e97aaf0507b3465ba6314895a0d3 8ea0cb1a0e39bf5fe373bb77f548766001afb9c74351bfbf6cbe94f104defed1 ee44a62f504e72819fc01d4b20457a6ffd026062791461a7cccd8d390b353829 9a241fdd683bcef470a441a4066cff2f9ab293debc7ba9af2c71f92eea7c36d8 9c1cb065d4a5b6767d6d104a790fbc512e82d86f56794fd5b0ce473d749b3a9c de504aa0a1b394acc3e62dd4ebfe5e9003bb1949375a052659ec657387050ac6 0e59fc22603d355256ce50216a0f15334a6dfef2df3c56c3a7f51ebcf0975d08 4dd528558dbe43a1266d69609d521802a07ae8e51e184a41b1f2a77682dd41c5 bdb54abf6f5ff0791cc1bb6b08486143a0f8864e323a9f39f3debdd9065595ab 47523b6e62d9880d89f4e5f9e3540817b3bca7a14eaefec3fb334ca376f01554 2a199c4ffb0f11bef5e6fb063bd082629fbcdd9a172ec972ffcc61157511c184 f81cf2232c6f13e51a34ea387f0a36b29229fdca8b838fe05d1ddc20c92b9640 5903b44ffb360e48e613e91e0131540f501e8870ff76db314ce6a43b19e3046f 68f801379e09d3e595705d00c94d7c9fc819a5d982c28df898c28c941aa98e5e 6beea10824c06303b88c9bc3a754a3f4bd06432b4c3ee3fc2510fb99405f089f 3225fbf5445ab29b8e5bbce3371ebb5c270a8296188ea92a2fab9b3e88c76b57 8ca66ee0d6f7e9b6fa8ebb2baca4405f55d73db058f7de972de9f6e2be44fb60 66a7755bcd2ccc5e4e4214c8b66c2e2b01670d238b47fc2ff20845fab8bc9164 c214bd37dd18bee8bc80211e597c39b06a066cb19ecd742072a37bf84880a3f9 3de73a47150a6c7746d0164a0bb5c5cda625b1c6f219f3ff7eaa24678889f295 9f0d9231c985d7e2ba3d665f4fc6b7148555774cc37b8b5172d3569472453a37 317beda0569eadf921f137cf103ee79b17dc7014056474879dcfde78bb6359cc 3a8692e028f0031504d50bc747216f5826f760091bd6fcd107164e9fb6541aef 78cef68d8a14a098db69659449e5650aff2a3bf45c3a5d18f8c5f1c8cc9ab16d 5562e1bc7b857dc1343be7f3c70736e4344465e2eafd6a2bd5d12f2b6a9f5d99 34d691feedb4cb5133f3c23419da96f6d6c10d4901403ee737203ca70b3d136a 8accf385ebaa9816710953465edf39f7e676553976d0d3956c5e6efdc1d0b157 b652dff522e7b92b23f767f512a0d6915dcb099efeb1c21e02024a26bac33340 2c13d0cea5c9d4aa0e1998d3c48e0e3a405f04f28d2fe9f2d2d291c4be33f28f 069d6496ab1c24b579b0e86af558d97b9aaffc1b0542657ecf8d10c578520bfa c97fcfb566da9ff1f19fa1fd90e1e617f220a67aa15dad0037b62af31c0560b8 ceb3601b96bd7b30859df6bd80c16ffd318530886f116ac7b6200284c25d7c27 99fbe2101deb78394ebab312bb51a316a470980f7ea1b8236525d8950619ff08 3d2ede5f6fb981037fb5a6251d9cf345b1dbd63e4f142a8484c04e5f9b80a277 9ba4738ed1318ca6f94188ca63fa22b4f515be37b7ed50c6d768b85ee3760dd9 f73bd76b7f25e16a7440c8ae2f198e1c3f9572d7fbf89705d19171a027b9c7ad 99ee9b1b346f0522833c9c4a6de6a7bbfa881079763c1775af6ee4c1b294bfdd 84315cb7a877153a776ff0f83ec25a41a09fa630d2445b8d2414a1342c78cc74 006ca70bddb277811456970dd1384893201bcb79844a0e6684a834446cb222b1 e8520722cae7bb8fdf4265c5cb98028116fb48078c127d1622b644261296b9ef 91e82022c31401856148625ed0472557e0d5708cc717a2e5b6eb99dbc78fc982 75ddecbee4f09f1a8b717f852b9e92246bc7250004d7a8a3dd51f05c55754ee0 8539d7ce898370d3f9de38c13bd295a6cccc4048cb1cad122882dc14632ef264 80bbc8d58689e7769b1be7584b37689559153ac3ec413fb9d64a77a1554c46ac 7bb50aa052052857d395bd4d34ea8735dac1c0f9c766e3e2bb08e343b40f88a9 08c828e4aa57cd4f14b5374cb95728b7edac18827f38e9f35625ddf3bcd6fac6 f85e5dc245b5eaaf68b6f7c1c518d3dab193e9f30cf0b2126b49cfd568dc3865 b1254e8f4484e957d0ea0e77557904ad5eda2630fe71d59c6d49de4ff85ef07a d8cb4f695a29724f1a68eaadf4d00fb474a2154cdad447779c105f5675307257 3d5b5f7f5e4d9b21763e6c508d24aa4d1cd98261bdf7b7edb1be374131dc67d9 7a6b7229455d60ceb42e8c0d79bd90bdadca6f4cd9f52bc87ef8266c1e6bfe8c 2e772998850b71fff42240574ab6de28c42afb3aa8d641a7d8675f95fd0b8670 b1a9f07686b0f4390bc6919e05e7e4965cd1fa8c224f10d7f0a9e767162b90b3 9af96ed85d7ef53c83bae39c8f50991a8c2237b5f801fb95f5e561ba6b127e35 b8fc7e7def0caf64895b4c89b7901dd380323dbe38e102ec6275fa441ff00254 8bb806b40dec121ddc769982cf6706fb20a7f742bd13d2b13ff6b5e80e8d03ff 984b0cb015af025b7f28ba84034bcaa73a595a9fd5ef8d9455b4f425c0d6b195 afd009a2eb1fb0e8773374834314cc6955f4a474e1f4af33601dfd2f2c8e13a4 ea872749979c41007303027aabffd10c84f8979cffd9a7169c4e9bdb9434d732 f416f11478db6b38cefbc641bf359a6e90e354a7c60c95aca9fd83af66dd51f9 cd87c1e69dcf324f0341aac5d4e106d673b25243ab164ae9b341e48b6e35ae83 17e46431a8b3df3208e8b077d0ec96ea06db66a289a184c2f7d94e11f233df63 29a7047b44ca2fa15644c0b63c5e85a0736881c8f2abf924a4dbc779779d930f f978261c0234c896c3aa773d47f17fb35d523f44e01e8f4893cc9cc8e043fdd3 55c7eec03b6b39af686392483be5a392780f1dfef3e26637fe60e4fa9fb892a6 1d8d3bc372fdb60442be6f38a7489e4d1f8f646c62a8e0171648a89152e3ae6e d0e70c9a41ab43979c4ea386a518f772af9ab9aebca3873ff9c4ca7e5bd0e2b4 517d2cc8227add01ed83187f299ccd4116f55ac6c91c8a54a5199aa4d5de392a 1a069e106957770c5fdd562d10e66d3dcce8837805b9519eb0761753be67eedf 911c79adb5a6a14fab9939b4e284d073159754ebeaec302cb7406c4594f42236 ad2510ad6c72f1f6b4a67244a7daccfd855d77a1e0712f06788a304557511e72 95f3ae7e2cd076453885fc2fb02f7fd4b9e9000f2bf3dd093b1aa7d088fa80f1 bd6c2b750b436231b2283a38d9473da88e2a08fc268c97b50c662a23d2413d66 7a293cb05f2750b6bfa38c963d24ef2ed1eb1c721a57232a037fea8fe0d79094 395afcf93ea5bc917522f1a950f43b4a6363b8596b25d36bc111ef553e7a4ebf 5ef65e59e41b9ae4aa72eda0cbe42becf8ecd725b51f87a8704b9a151039082a febab4a1afab6ba84e0a24aae7caa30036cebb157dac5928225845d8ba548851 04dc2c330d7fb3d78c2fae9a130c9536a20f3a53e933df1e2924d2cba326e941 ea226f6c98296d020b394b7d093f26d0192609d4d1bff09ae52519484b552afe a18c9fc7c3ea1c5eccb643b828ea033ee14826fd7d7b36313bc2ca03af156d91 8b2feffe7e7321ec8ea172f6703c34e6741115b094bf4a0902200d038d7688b8 bd9a971329b9a7fe9847106ae0e5118f0de5b3989439e2efe779869357417e6f fa5eef146c29f1cd7073356f955fb5a8382c171a0293ef7733af31d09073a4c6 0d03088d8c377904e7c93888312d70d90e1539ff37eb9e2ca87bf70da40b187a fcd72be04f8de1c52a9bc4347c9747c7bc172c9ced5a96dbd6522f93905e05bf 116f8f3340fcbd08f79947631463e4c76030815924e00ec3defa49a6fcc780a1 6912f719c03ccb861fa8ec711284e40c5bf831a6421a898880fc1840f6a0fce8 45546fa150f70efd8dd8f1e75e41f67ecb341987a174027d06607d80935373a7 0b0c8c2459d61ee2acc69f8a0954d4b6458b7cdec634401fc12210d059ef66fa 259fe8a52ab3860d51265acfff9354634778c99339ddb402e9076db62b81bacf 1bc80f13453d6e060d017d7f4f71ae9f7f30f99750be081b1e86fa33fd260a43 816c8d89b2fc4099e74698bfa2f0b8e3b526dde631d8b3142eabb5554b08dc1c 7f7a97c0ba5db1d80e11d73725d6bc8696a1479a1656256a74e4b920bc0de752 85ea946e12bc40106604f0b60e3ba9bfc538d7aba4f13066200ca56e7680e06d 090ac92ad1fb8dad65f71ca5d64cb332fd2b5e02f140ca0a4ca8a65b9839b90d 0a81232d5e19020de7cf6ce6809d1dbb7bbdd2e9f5c41ed4f07434c6695d9945 8ba082ea37b505745625d81cd97af8fc7655b3678e76a76bedbd58873d07b076 a414c9fab50f43e4fd69329cb6dddf3a8232573bdca95a25b8e8ff257a9e1959 d9fea5f066f950f1758ff23226bffdf9739b976fbca57db70a7ea73ef5bdbeac 4ecc7a37aca8dae97c7ae115d84fb17050231601bbd0695e7c2f729caf143cd4 585f0308ac9a7e18cf40b96f6d6de1554f457859309a792ca8667f7ec8ef87fa 135480726fe121cc4633897f69f1f8ab2491bf65c9ade2d69f5deef578909f8a 64734a1970b2fb1604fc11329ab4918c98459ee30eee1e5d5c645a8bdc473a9b f9f1ad734fa9f55596faf8863202b337dc63f877ec357b860d3345c28b5c61f1 4798d5ad0da1ff17643051c7d1a889bdd7f50534c1f14c3d291a7a12b1035c94 81c4eb76766838d410149f538a8c17bb5b2bd23e8231185f19bcec323b7beb6e 48acf54bc0e69a7bef009016ce273ba94226b5354f40356e69513b520d71d1ab ec5efb75f028b94bfb3d32a3a763ecec4a3b7b8a3db8f1c500c34dccdb6e3abe 532c8bea3759022aaafbf6b2ef8a4b61e5aa0938aa08077c8e9e7e224b636991 a46926abf4c148a088523e9760e2bdc7ea08a19e72ec73ba3fb7bba9b7d23015 931115ea130d6132cdd39c2daa69bce1ede682aca827c200ca212b2356014446 ee4738eeede9b84935dfe2f6efda30bce8687094d53f5bfaaed2e5b64c18b2b8 3391db3e29a86ef108258c27c16035ff4ec737789af54eccc83bebc8d1aefc64 b21cd54b56cdb3f38d6e75bac1b2dcd5382761844418448bf8e637eac4729393 d3024b78aeaba087eb287c47ec52c0a50f91209da33372009453301a172e9ec0 61f0287cd92fb8d451e77c907def8e7e7547011935fdcfc5db07119b6dddbf55 c5ae31d48b5018e75de0a6dc43013ef5be534d4931aa4ede51bc7541706e5092 e2abe3b91569646ea7d1004634ff95be8b0387e781839fd2f0dd4c890e8a8939 895e97088678e7123b52a58a47d0741ccb17fca89294ba42e200f2fef07a0de4 6493e95eda0591656f237d00f03a481cc8f078269daa84134be5d11e0d16c6ab 54408c66c599797c4f03148dcbaa321f3466757888c85f87304f9b129a589f9f 60a10b3e6ba1204384e0cfd54f7ed746a8adbf685da09e7fe63922153160292b dcb1fcf11fa8013657cc4f896255781adf68d9173d9e1632ceb938ff3bf10f99 60188b6889cd2ccb4186074fca08f6827e1ca7ebe31f01e85eb499aefb281c8d 3ef7a2de86b0215cc5d3142cedbc24b36bca89f0781697e47083c1f00454b8a9 0172877a451614e175cffcdff56495a84c8b2338b2f9cf8ef3f591744abf6f8f 7eaed8d643b26d0965b2ced8f786677042ed80f3101230508aa5c00e31f4af4c 82f9d9383d1c2c54a12f27a99496fc0d60a6993c2bb6fc7cf2ea34e9c7a86f50 52aa8897032dc4b5cdbe894fdcd01f1793926597cf2a11c4274467907b52a322 4c5d963fa0ac812aee10ca74c79234b26c13d4d977e9381615539a2c0004fb9b 8fedadd0950b30ac5c6c14786b291f698f76d754b4e03b5b4283e2ed00ae6272 a7922b714538008f6ab53e9c401bedbf6037d7798f877dbe78c2fbc7df8b0587 c30d7f43128af8e0126322d58207cca597a59cb590417b649e22a0b85adef839 e138d4f6907727184133e4e66a08253184d57653a30709c19557cedbefd74372 285f9e743c1cb5b4a88389fbe9cea4c15c97a6535dcd58728bfc351166025010 1026b95f8978d02bfcee064c74bfdb5cac030f58f610086428ba9a7b21950a6c 0056045e7681bb654cb62a401e7007bd0212f5a3c5b5596a55f1aa1761e38cf6 f515d67cd9680622a33507d272aecf9542e795094a5ad122c38c63be63c18de1 eb4dc818e3572e4973d4969a933a56c75e872c6ab84c9564c2924958c9221437 3c4ab9504c4a340fd98d02b652255f718654075446063062294c08417338fb6c 68ecf96f1df8ac28d8577239aa1f943520d31c1b349f6997d6a2a502215b2431 b424222cba24fd13f76e7d10cda3b94e736e6f646865c702228e9116b0fbe2c3 38dbaa7738328918f72a3105e6065fa3ad7599caef853fab1907cebcd1b8de84 8bfcb503aa70612f694d5fae1b3b9590d4aac724fa41b38a1757ec80b8f2ce56 f6e150a02d74df84e9a9024618e68d4863fbc6a2ebfd064a3692300d92e3ee22 f8b47e0aa0916ab409f98143ce1b38e7d9d4b14e77cdc741fdb8e647ef7c741e bce1ba7ffeacc8778e168033046589630d4ccb268c19af1cb8821d424ab9d020 0e42b3f20783eda23d9b80ebc6d9401b09db8c07a30f9a1416bbc205b431f35c 09a73e2531320aa73acce0031d7a7e712c52a9d763c4c68711094d6ba29499a0 7a4ad0c67ceff4530ae9372fc0414954aa09f224c80794493d908ded88d2e09a b583f0e75c65ee04953159b71a9984253400428dc82cec3820c63b387db69739 47dde2226d07378319bed1833e6a459ebf26bccafda6d9e516071a23e637b1f6 7af49646e069b6d377c5a4259030de1b09e749cd70b8e3108aa459fc0f10350f4bd1dd56b1a32cf927106c4db74c7ee063e4af7de8b084c90b3727fac1f65204b12f11a657fab9f5554e5617a90e17024d559cebb9a13612cdc90a8715a119056f9531e97906f84137b3cf678e26943cd4a016b3c148d2658c5822f350cb450ec837f92ec1d87495995dd4eda7373ccec035b33d22cfb0a967cd9123664679031c67d80ffccf34b6bd7ce618cad235711844e993baa95120890c6d1cd3f3a308894cc99c41b811be942c509eac57f0b2404ad2e603b6cd6628bd17a2b8466a0ce81f15fa918ce08e7c0102007ddad2ef71300523cc20e363215e43566ecbed0afc162417e4e97ae1bf845f5585915e286f691af48aaadd524fad0010233f1603b9800fc837827a0f08aa5cf2d4e6e152c1f56cc9e663b8d076b79b89575f9d0a0100c5a1177a8291a0da28a579812b89a52def6c57a976853e2bc4919b015f05e57e2164c0c74000d40a6cbbaba147a8e9d7bf20a7f23e67c8aa4032ce5cb104af17585b0eb4de90fc715161e1db82f9b6a113c05cfa5ae5dc183e4692da0c0b70d3979edf62376d1c6275c8fc5018e9cbe094c9a56384efa7f2c07ce57151030f8edb68764bf9a23f4752cacfab7bed796abb82eb6b66a8143561b0c2923e0f452df7689394fefd9873442f9194a1c85c9976b144aef96ff4225d20c96c4d09fce1336f8f472ed1dd6a67768eb0577f83b195a2ffa957d064e44379bc9f16022a9dccc2a133f8386ee6851a66a7ee0842386a4aaa1355399e8bdd4095dfa80b7f324f31cc27544517e40221462602709b2dd6a917ab9d5ee5208552217b790a55e0b9ee4e798a0072522f85fc03cf7ea71e3439476eab9fdd3e787e1d7d7f0dd782789de19cefeb0cf117af0efaa840456e9f96a10e979c9c63ff1072d4d805c77cd25c827f68e16d0da1ccd88114c2cb3633f7b278d462a31a6a453fdddd0358e74eda5d6a1ee977f851d04cddaa4100123675c2f99f2b3731f19d90be41019dd9224fcdc590aaf50fb43ede6d1df4468c524f5922103f8179adcc8837fe01553bfe5da61f723e404492fb8d81a35c4bc3d243c7144661e40dd47e0b4664068b1741b1911e45f057ff6eb2f980cf160384470cd988079c3c5f6a52856d9d07d99afde6314c245b677b71670b4c452e1daca8383d824989163f07bd992306077ee20d7188569772a03b098857fb8bc3faa87423c67ee2c655fd4e1185bbe30c5ab190a97fd845c8e3117e4a48de6326016cf0ec994470cc710483dc1829880bd4313893bdfbecd4dc98091ed50b65ecb7890c161e9df56be2fc98c52473be0ecb14cb7b483539937a62bc08ba2a9f3b6b7a28608011196a9c37d73e65da6f079d402879b1945b4f1e12c1bd255a0364e3b058365ecff2fd83bc7c5c8ebbf702576c302371feed9b11f8f1c098d91a33d0b37f00f6f5ea8965f50eeec72200003a163231a1e51a1f9a003a726caa0b4fcdcdfb3f542f30119d63d3ee5b1e4a078cbdbe5ce3eacc3da484ba478780ecb661d5dc3273d2ced2bf67ef10b2329a0a1ca41dc3c2d754cd622009b24dde9d4ef07d0e6a3798c0fa215b02d735072903aa0bb1384cccb8ffbc791c535ea1d2e59327e8258633b8670729862c0c2c2e0c8b85f1d39b1b8000127ec658b41263851d97b4945dd8ea682a7b50f11535390b5968c7fa6af5c0b08e5bfb2758d1fa6b6db6175dd5cc28f6cc797da488b8ec028de796f8d2367188a56354ec9397502190b4629ac6797bfaad11647f730c000b79e45db2fc61aed3d90541881e4883549087c859248811b8ba43d5b386f19d0c427810fa5b2bfcd5179ede4eabb757ec03226a4da25f0f00fcca20a4c683f50f78ab5f2e4d53191bc5c55a4bfa9e0d4814f094541a125c2cc56ff406ad5cf70ad7fde8977e0f4aac19e4fbac97c34e9bdeaaee7d81708dc48fff80023f21a7098f52aa21611c91c30f9d3aae73a4826ec483d611bd2937e51f10a326f483d7047db6143b987c6c85bd293023ac40ddb998a4c86e2478bde0426c645e18e726045008c01b5467a91c5d0bb0b55d8060cde2738cfcbf76c10e527865abbceb8001d0a427d98c8cd73e9e6ce81d2dc0d279468af4eef7203782cdfb186059e28300320f0d408410fb5518c75eeae53c4d27938d14048886af5a2c7280a3adb8ef078ef6fa34dac82cf04425291a679f2e458ee8ac4f06a41ca7a6f6d75e07f66d02a1dd82f8284d6130c523fd12d74ae45ccae6fe7442b6885ea77fe2dbf8307a0368d1158d1abc923a428e8c3f133aabbf0b7605d1ce2786657540b692134cd90675fbabc842f83610b0dbf90293bb831c8a96fe187410dc657aeead7ccef3e50b40f90d087952107dac4daefc2d92455dda66498162ba252df675c307a09aa1058da2ca468d8d6be656cb37c284b7ca53cad2004a2b2e61a449e913dec91a450311673af8ea7e73fb983ba3d5841b515f3abd36850ccf34e9bec250cba961ba0bdfe846bb2e35a4da2a2885c2255bf6277794e7b5550595167473d8104ec115093a1ffa8bd5e65531b7bcccbe5d2245ef7557f67909c55ea2f3fa0645513f77c252fc0ed2ef79d939349683a0c572dbbb84a13938d5b2c875d85207e14cc0860b6cc5ad2df73d5a43dbe117c168e466539493b79b67d7c15deb1b54a92802e308be735e8a5bda49ed9c1fcc836ebb7df5b1b3a2ff0d2ed4d76f562f2e64ea2a0f7a291a6dcee0162d292fde2fec0855425cf639024e5b2c1ccec6f664c4d91f02bdda2109c7899f326120e23a483116cb3824a7937b76f5bcb26bc4c6fc64fb065738ff46354af9fc726a0833fcb255a93e56f6b6d3507237e7f6f33421ddd402ecb021c795434d178d49dd45aeb8904f3d382eb70311dda3490cef6b6e348c049d4355190816782dd6a860e5d58c9566769c4ba532f0198f1d37825f4cebb2082ebf51db74681d1473135fd97d88ad436f98099b517d428569c3b64d499347083581c91c18e480a0d5c317a0b51a3f182e6529c49e7105467a4162c82185f80032dda5983f0ce4facfcb286d260f5fa760a11e5a34b12df36ca9eee9fcec200c896f1dd2bbbb022d9259dea9e6045e9bb378f0434350aa3da071d53f08940f002adddf17b73d3fbfbe39865be138ad87bb53223225aa178f15874cb730ef330dc62c5dc67bd10f5216bb4dbea3209ebc6380c387ece776ee47d3a589bb7a1907e3df0122c7253eea129d751587f1007241946984f159943a3dc54fa8c572480d8be1a9dc24f6cefc79dbb3b12ca3c72a7e10600b82a59e5f50a7fd6f486ffa06db8cf7d33458323cf269fee573c78c656587db404f669625eda3b4285a147006012f6e134e99224927cdbadf267e30f6547794dace21b045372830a99f6fab03393b018a853f2b3b131266c7364e11f16b86fc79ad506bd509b59f3f157c640cb5f352a276afa8d3de813d815a9a17c5001f6d30b2e49a5efa13440630807308d86761da0fa0164576d898a4ab736f7311ad27339e505800639d62cad06ce2050e751d9d1fc25be19c55082774f1a0e1b61f60eb35e2a599e044a4bf107cb00159ef72db94a8e079f5992f396fec40568911252029e2e04406e9b4750b35050e157928f1b4c29bee4973688b461ed89bd544f1af042ea8eb383319f0da81fd0950ad5ba063712328fea67d154719c5771873447f7ea0d09efe58bf9c22764704d26db44c5c77ad43515cc23c39d8bce4a624072266f90ec7a278402ee016cb0dd237682784220e2b8ef16e6a1944fafe406d290189b9ec58d74c7e365f8146098775592ee6e5f1ff2f345d28adbf82b0c6767364520df1261f90eb73a3853e04a11c95cc50b2ff8d03cf0ac87a0a226d2edcb14bb2ab0599c16b1ebc3e60960697499965db36ec0efe0ae96b1aa8111ca5d52000e41583bf8683d2391c61690037e56ec36b8da1e136968c97517e503df35d12edd807c129f8920ab44143a00369627d41eea085e7139e5e092e3fd753429905304c06e80eaed6657786caa204a85b66e589947889617218685bacc6cbf9ea0dbdce920f5c295a2f8c621c890401d8821e24853b4b8782c24785d15963c42312528f195cd9a2b050d46921f40c97d66ca376b3db2c95a1248b034fef64e1f04b68e0de57eeef899fc8726134004771b183713f42306c6acda3f5705d7c874a668d2beebf931f42c8b589ab6907ab20668cc0b4a78b255da8ce7a4cb05a05726bfc3f5e73da0c5a9ce51c308e017af1d3212e0e87d929ef6fe1bdd178c5c520ce28af18861804209a57050754044d122976a81f7c42b46ac0c26623e50e4086cc96bdc3133244928aa1689b4c08f9998c5147c41536dba5f4e65320d8d591aa406a250037a8988eb534f1b4200a69a0e8387ae5e67629bd58088c1c60160c4228b6588dd49128f7271b06de64072d1da0d30e31c004f85a10f99925c07bb4622f1fd725907059d84432ad2fbc06422e8803deec3e8f751fc875e95cdb79956ac573ec598e363d4cb8324aa5810dad3dee815c9751609d34b5b2a8b0ddc49b2d6ef4d60b92741185f9b556469c0dd25a2981024fd973fd52753803f590b46755bcbf23d5bd08fc95cb4969953f0148f525e0ad558c2c98a1accef78387b65b5ba57de15cbb4ae6b49ab71481180a0b63d593704d06a1a00c2cace917d8d04161b19457784b94f38eb8056224c001e02333fc955c1713b7ba69cb98f00c32be13abef45fbf9647c522867c6c58d04fc708e0d9bd1ccdc7f1be6b53293dabbda793c3a33bc023d2f30689bcd227503adf1e1a750c2e0dbbc42fcaf56de5014bdd61212f7754b84307355311fda2b0259308220aa601b4c5954a35ecb217bb4bacdd997061346fcc5b475c0d8347305d980a53517e52b56f3302a4bf8bbf0541500411acdc25f23ae4e45095c050702f5d66419c327a677d9f7aa4db5646ba89382e562dde13e0ef7940fbc5eb9b8067d5424536de5b9c672cd61b64d033a083b8672d138559b44be9e8e3143ba6f0f0747cf701fa46cba24f07004c03e26de87f52ce1335f5b09871e0df4040c3c0b043170a942288b10f30664eca5edf8b0dc626b560bbb1e49eb2504417519390a255bf914a81bc781988af66abb02e7b232bfb675dcafee80db356dd462cb240aa2f479c1cdc313b1b1d03ceb628884d00277b12f1c8982a8809a28abb6b7f205bc6b49b5017369a4c80bc12c3f58e1fc67496742f8c2e4f28e1c86226f0c750d132d4869ac1556c271e699d3b2a9aa162543e168e72183335a206576bf83c303bef0d4725c4fc4faa0aa14a9c08ff5a92a116ff2177d020afbb94cf27f35970fd0c9f5d9468937878960bc5f97105d85e67e7ad42778aa26c9b168c480e041076b00248d37f8ae3bdd5f3e465f73084c2a12de8b9fac138253852f3e621ea004d1b33465eb224c9197e9b0a4aa09a882c0da601a36f12044abc65170b1d8fd0b48a0bfaa71416ed869c5eec1b2ece37b92c1bd91907d71e337fa2eb8624788058d8aac60bbb7f2e2b1035df588cf0100d5abb7e20fb7bf3ab66cbc3361ab3d0dadf952b2a8d3241f4e5963f64619937a00923fc83a2d2ab93726e0c59d60a005515aafecac4a5ab3d631ae9fbf78271e255a089670d2ea773351f6de8111b808b60590140a4d6e25f5369ebc6315026a7ecff50d583083ae30a91d2f20e0f40753c67eeeb19bb5175e46d0d569fbdb95225735f382277937bd2807d5455ca1086c81150151bf4b885240171fb41006e7702c8879b5a799605959a0248d886e0d42b12dc5e0fc4534710e73befc5fb017b19f730d923a36a50c451e9147cd3e0746a4434ad67d228b4ff2bd1bc07bc645eb2f47e7d6cb94889eeb29c0612ae0077aceb1f9b5cea9b70e34fde35905e9d8b20d7f1ba2282a408f9868520786bc0c5e4d23544969d4fbb484b372afd9e3dc4bf88e3d345fcc6a1afc23094f98c304d54ef7744d165193f5a47ebd7c9e993377bc0c2128b09f252f0e2d0a8f0b380867535c925c17d3e3fe5028a0b73a67bcddaa0e7840d0ea1901a26cdb18c1cc0c15ef73c97de9c552f53e1de2db7e01b7c176984a3b628821237658cc93cf950536e1435da8a803dfd551b915cdaaa28c95c1fecdaef09367c93b5412f62f37033b302fe4b718e9bbe709f55e9958b85784b34020265e4a2c1c4b06c3bfb3790bc31594ec88a76c4c466d1dee034dc5a5291131366ce9e91935ae938f1525f10f7147ed1f45f519f9c62c8b7027d7a21fff7af0c4b83d8881b22991789a766b085521c9f9abb3a206d0075fdae2c8dc9b81e892d6564f1068c1c093a6187e350c0e5405e5b177d5398eb98542cfb0ea87b8a9386fa6d68669277fbea6479b29081f5311fcaa74bf0298f57aa0a8e5562d54f9ddad5bab61de29b1a824a40c7d03e199cc02720dc17d8892a72e010deb4fc1de76de760556810857d666e4962e0b8852bb7e74c222b49a147e5468443e849064c8ac29f10bf222f96fcc37d84104c6777e0b6c455b24345363d363304aec809477f934017860c807480282ef460dd0477188541e1540e507eb4918b45bb8da28eb41e09b5d7ca515080fe5dfbb0612e40a0b9bed3af135f3cc0935130b0ef37725c979a4d719ce624482fe64ef013da85e3836c17886a28a8bfc5f450f87af3155603714f9ff6fd8b03df76e0000c0f6ba3616dedf134f98ec6885fa1eaf4e270b6488105ee8dd1917d41033550456ec0d93f046271d6bf7ba57df269b1ddf8afaec5792a2de53b59dcc57398b0991ce70555cb1108c73b6b9d88a9fb8cf816944ebf0baab07644e2e802ccbd00bbe30b36e17963acd87114be2f9e970f270ea04ff6350fcda1b0dc270c9437a02b9d2dc637ea68c177d96f084b74a8d99788016b274bb65283ff5e846bdcc450c969b2ff9904e4888ee7a58e9728fe38d531ef119fbbbe2049ee7c3fdaa910d083361022489e8afc25f42b8ba05f2bf9e31667dbe90cfd5c783fdd2e6ce1a0507b2c88ff4cc733f4e171df7753c46106bd9d4fd7fb43574e4000f12ecfde510037f0ca908b7baa30d702925f590bbe8f536098aa227ee7be78dcb535c2e54bd0abf9f77d770fdfe1f87c868f9a0d0fa9358b8919dd356dcc3bb6c5c51f13ec00116fcded4c15fc35d85d18d87705bbea837a6041099e11c6907b70881358c57091a3998ccff4db79ad3fc1791a3af1222eaec4cbc1017359c56b006f0a9ec36ca1c1c34bf57b50a0159406225bf3cdd500a5a04c4b09600d50f616aba38e165035cc785463a718c8842a27527e4308d0f44966532eecfd897829eed3dbb54d6089ad2b740b68984cac04a3a98188e13db766daf4495e9c0e6df2f7b70e889030fcd424b561708f4680d2c05ac9aad18d5426a0e7217ad71fdbfc7881e73f07909701c99cc409e7fc9e97027ca9560711f914e87dba76db6e000160645691ee10fa73b9e07a02a614d196b37066b8eddd2fd5c8a464bcf0fbd5820ff217c7a4a00c50baa3ef98ef4ea18d77470d15dfe437cb7aa68b822337c2d3686c0293fc90b39c4b8ca395528ee51e74b2e5fced88a560dbf6d425e20a3a3c2292e7d193b04622559f786b5a0e3283929edae0bf41158fbb12a93f766cd34179933f2ec030d32ed5735c3193f337cf155933153ffeccea560466e4526e274f4664646ad9001a166e0d8e33a274c0deea6cff332dd30315bb965fffc0a5b8fc9fe80ce0dda0bd794fee38dc29eaaae367f4c0dee5294e36dabdc51a93c6214b86d2ba29b300c623a1660dd92f43ee8477ec78661216b84f0ebb33fd37e3db98f2cbf65ce460518c7de4977e665126d9872327fdd1e2702a097bdfbe6720d6c9a5c570cc758016e9cc368e0e837997c8643ad1a5323b440cc7885be9358afebea39e6cd87c6011ebaed58b8ea775048e6480b8df9458ef4e17184e3103bb4aa7db03a4eec94012359de247e9addc255353418cb394a3ba85a48cc500db5227939602cf0e1db0784aa26d50e0949cbcd91f4bba29c38ac3fd5c52674704e2d953149475fc4200a87f4578fd1664caf1701a7a63faac2a37dc18eec4a5fdeaca6e8fc63dfbcff008ea6a3270f0c54edaed4eb5a1e5ca851df55eb6be3b9158ed9f57f7c2d399705bc36963886c090bc4e98236814d9f91854ca249dd3b7c7e0cf97b47c1df7dc05be1b9b5d2bc50019c7226569a485ad2bf70553594a6bdf68d435f57890b858057b3c01c66c5ec216be78edb6d00ba0d159e43779c16bc5496342e36e8eb4d704fa41a1c4d65dd998d853ed42d33a35dba82927429b7c3d93b8f82ad71ef0600f5724b1c787c36e8b2f9b47049aaafa78128efc524014beb70fc77046238fc9007051aef7f86d61253494af30be29d0a916fb8c9b65005b1a1d19e6fd15ded801e280903fb7f694f04afa27989b4e70dc6a05181669b7ffb4e2fc98e6cef56f0fe1f9f45d1763ce37610e2cb1f4e2fdc9b302fceef06802f699ba3262caf54b05b78ea8979edf805c8eadfe28a15b1065fdd937ad71c610269afd2cf70ed8a60ec80528e862400072f7cd0fcd07c6fcbf14c8fe6bfc5785d20b307dbf09f09405d4954ca883304c6b8a08a8139bace1b0563bcf925bedb43e2c80295fbef7da06bd43e20a321d4c18e380b3b2cc142636cf359122280fe17cbebe165f0d484f089fd2dd3513e1c2098d6bf0315643ac79cddcb287feb8177874013ad7e0443d0f26a031c3a96812008850bd48477e417c2ba94dba985f0c4dd07c933af917a805d8c50b7c252421657670fbd682504a12dfbdf5f3252d6ff498953a07ad6bec0a2e70c0cfc6c643177b4dab6d974cced6ab64277943a958a24d00d009c8356e0254ce37afc4b4ce3b77f99efa07b75e84bffe56f6081140a809234e7f29737b03c843182359db3f7b2c776d55bc7cfca241769af6af9342a8ad10270078a8900c2621130973a178b891974fa6ee1e588c3fe61f0191f90db54abf2fb70c05a3016e0bd5416f0c6fe3e2a2f06515a8b10ed3b594ae6c5c976c8daa266868eae802af7e9918ac9fd5f29e6214085771e9e3c4df9a031d7bab247612d6879dc7d0038e7a3f6747ac31207432f6e8a4c97a20211d66ad13a4da684273bb39e9420307734609ee956766d9c6b1b9d4786fe9b8fd76dbbfd15a3b303be6d95ba3d58003e036e8e1a35595300044f1ce0e038b7f899129c461ed70a9a9b41c37541db80526ab23bc6a0eb153d0aaf3f1c5a79bed44e9c808906ee7b82c0f974b607d690efefe0b744c40e9b47d6b63e5e8c2a3a5ff9ec38b7871a30a691b7fc5d3fb0e03d5db1981cb9ea475bedfd532e194ec5ec3b597d862ed637df7234b4978d1790e2dfad0f884f1bbf4f31c3774be20f343180ef06890ad7b61f3cc4335953e620eacedee8f4f07374150098bbcd930e934a2e775c05fdcfdc1e6700ec664400307cb04fa88ac485906918630a4ee956c1cf9e923a0336bd6cc26b05d2bed546a0be0bf82df2945410eeee6dbd46dd9475401aa9b53b81dbb5aa7fbf4db7c0ae00d31a1d4d87e3367aecbbfb194c29f71f6c375b9d3cc5734dec4a640edc065790dcd94edd09599112ce745e1c060d664937a5097b547edeffe4252d98e9a6aaa00597c2f17581ff832c83f7dec34315b200a8d5a48e242ba04ef2990bcf591500c079a46e7abe83b07b52b7cb09dc30e9af8bf34932a77f8305abfa43263837c009c6856e94d752e575ce80ff1c9ef103b8c5784c00c1bd6536de6d656481d510ce29a980c8838acf70ac521b5454dd0b9803d9f44bbd6d77ce8e8013b92d0f20294d6b3376e021350532530e5ec2b729de565967b3c403c73d90094350446c4086e3a6bda12d7e760b4bdce6b7884581a4c8adc837f333648fa1ffeba27a34a08b31cd2fb0e156f1f2ba20d4895c1f2b337b528e054fdb32b67aac4dcd0d0610ad3643e550e39af56664ec4b3889a9951728fcc3b21addd21c682aee9f18e8a012a600b69a04029602ebd44fb50186e5de32415ecab13d0562f679b00ca6c710ba006ce025a47061a9d064c3fe6246c2cc8ff00a4771d79206b9d2e9b278dd90acf5fe3d12f95df3c9ff2703d0601f31475bcc5fc5cd1ebd1685f84c852160609c2ee1eb8b24a41842de685390ed8bd01f6cae02704cf427de19c4480b20a1502523c1e39bd142f81850a7a18e774d1b81f05ea59c8f1f17611dc0f7ca1f35509c967a27caac5ce35008845b52f76eaf773eabf752d79327c239e51229934cd0fdb5fb00a90d0f7cf604b962c48eaa4b8f2c42fa5f549f2546592e221cbd50006781e1e1f919fedb3842dcdaf7a4512ac261bef1e605538020d4f2e6285f2450295ebab4a992f0dba4ff9941a80847d3ba8fcc5db9f7c388ba79e84862d5c84014abae099713d0ea53d01e141c14d4f79485e76ae9b4b26e62e28320a74d6bf0c96b906f5ce03a3cc1128444063fda048716b00b82cc21d86bdca218f0cdb7304c41e689b266bfee57f64277d6e46dcb5afcd2d262dc3c14192cbc000aa5c75029ad3c2294b4f5932abd2350413fecbbbdc74208ebbe92daf770e09b41b87f50a3e803ea604d60edb3ff6d6af78fcedd004e7564494ae94d9d860dcb8c71b6a06d2217254f9282469f10110033867e77aa94b93f38a7ff18a66c7b3b53bbfd80cfaff720f051571a18a34406b5e1f6444b89604ef078395b0ab9f8780660c590522c63ac48531c7f6291c079daa968229cb4ef1948075f3fd18e75ba453b2120e22b790df021ab01f04bb8b6df2d0732f9aa47ebb2c84904d053a9f314b3142021c31dc141e67b0560bad76d15c64c2e591fb356a6c54127d208e2c1bb7aec00e70df920097371f9091e4d6aab03e28994cc33e1daef4d034d8dbb8982fda9100cad39cf60eaa3d2d605067181eee4d50530087ca94d3392b9f534461dbb1ea0f521290d63a6a00a9c667e931fd551de66eba665ae74e244bf531d3ece452cf0c4df537a9a5600fb33d6a4537f66e53e1dc43d40a4f9a57120aa0ec72f772a304fb8775c284e3e474d7d8d298d9d5f4d21917e4de1bfe6c24217d65c2cedf04087ec2e4d89ea35303bc32073cd7cd3c0a040e54b3a20602bb520c1006b2c5940311fbe55aba9f8b4844392fc47c5eb0f0a2445e6975319979ac4edb6d474c9a0e84011345083c3b843573be561a915ffdd95f926f84db78823abe64205d3fbd0f5bd0301b099942c4796b0b77f119368f8580e97516a8e7de6e67fb2350d2d9034d59c479fbd0faf118bd7b42224272157e56ea3787b9da999317449e19003304868692deb7f83937579ae333891f63330e1b625ee633492ed23b2d1b2b35c708c4a506ed1871d78f39899048e4e0aeb12d71fb5cacf3e425d47fb6940e41d00db4bb92358fb90bda9da58056dd461526fdecb4e191cd33e856200409aed0d20626784431b9c52031e97535d4d24a803e0734dab7d937d1e4f131b9bcf622b904318188511330b1065bb6fb081e4d667653c254717c1f8321add78ac35970070dc6f053901cf0a4d52c43c31ebee0bd0484f67a51fc9ffa5dbbf8277171ac3a03da00508f63842f3623dceefe74f3037523f394dc6f0951462ad2e26cc3f23a0f8b1709e3aab5aa2a119f811e1a1f3bd3a30dcab989d02d26d6507b7a16ee8305e107b295ac03f2795215eae5594a0c4d3ed815c9b3292669292890b09c76fc0e35d0cabc2bd7ffd06ce6d5dd425236fd1ca826eb3ac2fb3fd6a32169e899a00dfbe36419ae75b876257db5dc8b16b8df49f0839a7f9323ee2da72a6e884f300c320f1450d811e50564e0812f24c6ac373930d608fa9a8ec98953a08b1fb6650f2371a52dfa69f50c3862a087658134157b7259a71bfa2c497a035b13fca5fb0a1f9a53100a4be747072d157028130a0a97e908a283667af82407168270a9200c7286d373db82f547efcac7d0d578e229f0be0c684cc953a908ca92c588430b0cbe79c24f762f555f2f8a2875cf1d0a165a701ca40cab32c3348a71fdd6db280fde2397b0af032510c3efb4a001be57b864dd55011ad6379c6ec4c00fbe21760a96ba86f416df045714a66849fecc42a183a2ec7be8ee403a4c2ea071a4b02d0efcffaf092fb298afa18b538470efdb85e83ba05fd51ef50ddbf35c5886ffb7098d988966661ff1e76354c6ae4e1b795b810eeb374f051ef183545db9c7de2109cf6f267d0707c7af7a1bf56446c4f2fdefc723f0291752b34c216bb62614ce089773022eb047528411a94e3e4d4550121fead56d8a8868268e33d2f3a4423007569ddeecd198d13d2bd78d8e35e03e2c45e80dbb4b382e55df9bde29570a9303c3c9a653322c75639c0ee6611525dbfc1566e240c3f271cca57953d04721f102e305fbdd75ceca06d6fc596e9cef6a217564b7e4f1516b1d7d9ff6993da5610a4582a7b7e28e7178c15c7cdceb6dfc88aafe68c4688905e6a53f1f68c7d14907b5b1635d4d26ca811058d3f7649d7541b1450abd0def6bab4248f93dce456302378c837c13f639c8d80ebc746704c9f7921bb9bea27afc43f5ccd9783c79630b58b40daf001f02a6f7a78b209d2d2494b9193fdf622730134e8c783a77bb390b90ddb91278275bd2cf9ed613beacc4a2df651296a5d020aa497db910cbe0070381de4f2c2aed9003a54ea4ded0a4810f23e771ec1c2c328d587945d22b049806876ecafbcdb359d9db8deb3b6c4b538eadc6b5ed5c83f140702cbc13c1c94b0081cf13e1c9fb956e0245bd6aefa13a41ad57009bbd0691b0429a36dd4e00eb081d4d9b6ddcec57e2d8f8a533a3e855a975c97da8d90a62df56fcd366113d2b0571738f53c52a1cf7ef643a714902582589bb2752247125bc5a642d0b792e580e1ecaab37f81cfb8bd3ce88588e2c2be6922b0194659a1139690edb5ff8ca980538c52b647464f16f1a5c70ce6151f4c8564a60627b4cac42577fe81a1813bb022f082c541a5962b21fa2d4efaeb5aeddcb2266e5749cde46320e88c665a1190d5e7b01297ced3a4ddf1d2df3446b53eb997e7b51cfa4e1eccc733a3ed16e260f3909d59658bda5586c52adf0663e5d05c6c28296fa796f74345b6592b011fc07c8fcb7a91bcbf24c8ee3bbabd5da92df273b3e17dd78a58c8524aeed1c280501d1fdc38060358ed6ef5f7ca392c1ace8ea6af47b5f791fc503f414a1abc258083c7524b6c4b1722ac9849bafd5d5667c230016d3385333988233121a4102ef0d3001df36d2a68e68e951bc73f20a81c53183bdced6af1bd81b31eec37c0f0804413084de86e9ac6474f233cb5f1ee1c848506e0f087372b42d94ea85e5cbab051b1ed3cb284f2ceb2e07fef5973774c4c13d1a7d921673537a66050930897204141f29f9505d325621a5c2f99e7a80c1e860e3ee79543da6506ad9749695260b9da0a2f591f70da0680a2b3359dc25fda9e4fe18efceeb26ab3e2d9027159e0f869993addf166dda383bfc9f0fb7aa3ffbcb5ee11d122e5a0c3996a2adeaa7016320bc6a971d5304ffd9f054bb95c838a4d461e7a1a7682796aab36de1fe47015d268ee489c326e5ec3ad5910a8b91cfc9203801ecb18905cb0aeb1ad160eb040556d3329935ff2a51c9e55ba5ca82f68c71d6be19a68dc1d059ae407904730af16c0e190fb7155de5662a91f1e010e85509af9b0fd05e3a3e6eb76f9b64a00692ccd5b67e0446ae73a8f4bf696ba176d4c2a72633901276ca5ffdaf042ef90093bbbcd89a18f66275b1be6ed3f8dbe13befc4ad79d08e1bb86e992755227b0133b5ab187182a13c8003a720fb2bd30f7624263455416d500f9f35cf55310809384eb3187a23624f7818fd59edae5a29b2ee1593861e0339fafc5a5f3bd65209f6ea5b6bf28ffa8f7e96081fb350a1ab4b4ad5fb8789fff321bd8ccf44df1d0c0f6dd49c94ab594b4be5a6f2ba7a00a1dc16ac1f803d22ed703e45ae7f23ff0a4db3d9c20486c0a7e8617d7f2d50bc65e281a82869cb79b1a842d16af70f400e6bd00375d3a45f4b9ce7967cbeb5064a01be941f59bcab75c2d317e91d1043009b5d903af9166b1a2c81116e01d154b01f1eced951801b4f1016be58f7ff0a0ff83308c49a4d05ea79f6933c5312b502bd0db81a9d2f41ff81c2f61f1acebd099af136fd08745c5ff5a501dc98a7625d5413b79bbd12e200bf2ba4e64fbe0b0134ab3bcbb6b09ebad1a0d71d2c05a1f75142b39c1ef32b8707680d923fd76607edf35b3d63bbf1a6f081fab3f56a4c17739200ae0a12019c8b54c137ff2b1709487f7cbc9378ec171e0c64c20223b643ab0caa042a39783997c69223ccb4a405e84bafd149d811f2bb344b80db451b304565151e26568541262b59b8fadedd02a6c2743fa8d180f64f98d45a3eeb40ad470a1275b6c17c00cc80ff31e8f03c0aaa788c2a1159302deeaf916e6c823e8ff39cff4a56df88b1729f7137cabb7b0adaa79f11b20264b3558eabf9cbc2ed3cf07fff655bc1b714cc6567ee5fd49b05cb52e9febe3fc394e0ee869d0709ca1616c7eb18b43b046beb360b2dac272f0c04a5ec883fae7f2babe11a9e6059a8eb7ca75c9af16d31a08d0a4563c37bb907adf879a593c5938777f9661fb9b618fed0e2cefe32db6e8a3f02697e596a0605533dbdd5b07719c8521fb06567376f029afea2eb7e0944263b50f05b1a981e0fc2ece95963d5ec5a3d69273a61b5359973a029afee49f98904e09a781b9c1c0c4a09fe47345202fa806d11ffee9ce8d3dfa8b58e8c27d46db784f0603e95d404679208a8fcc29618ba698e6572ce9b76cca717320a4cb5480bff82f813096d00a1d3ed3ba5227c6b98074707ebe330231ac470c3c9cd9ce6fa402cf4d382cf0d4870e459f040fbfd16ef15141fa89da756043e089f3602b503a2e7106b62ff0ed5a9605b21c9518240480e4436d0a65756276218bb077e0042913102ca70790fd0fc7bfa8571b2f5a510804deea5f534569ac20e8c1e9e7efbafd98560989205559bc9d4d0b825d9da21a24ccf1c578bd92cdc1db9fdb515c7a6923f3a3eb00e89d8b7c32afe3509727216ff843a85ed94901ad41aff7897bd4f0a86c522eb07abf41cb4e5d41a951e6f22c3a09bc441b517d62add79753089f83da4fb1f9200e17da08f245ddadf43f639c46929256ec317285fc35c812144160dda55ab5708129449daabc72488736a2f791b745d8212d2c5ce62f5086d733a1888a710bd0c70410382c2796687c8869b12c978de7ac2389cdd0aee7a5bd761786b00aa360152a67c74a1c48e26fbe86d74d1f98a7aea9d25ef7a43e990f35df9ca92d13a0d9b30482ab367ed62634902371c2a135ec8cfdc34f18a8232395a9bc332a55e0d79ac35b49f7c7e114911d88d2fac7c270bb5217689bcd4209668455783ea630379e37fd2648078740ae0c78e6bdeea6d31303d311f2794f2f54fa3de8c061e05ab7e5d27ae41510603062874a905141a3d2404d1ddd0b86595cb1b038c044903c4262b56785ac61ee198046376756ac41be72c6397f6a5bae793b671bef63907f6ceb852c12fdcb5269de4f96c621403c19de7266d95ddd60e4208bd0edf2d0507ed3f606966411916a1692e3b5f4bd71af16c683fa65ba92bc84ced8a99ef03027bf1ac18c56519ff2e062f5af6317be76f08f1566a7f035236b62ae04f5306580b8bcb56126c0a5c5fce727802c1ec49d5b60305f87a14b469c81ca37c160db4b700d558ed9ac9ab384905045afca6babbe1ca5306576519a9b747939c8e0ce520d16f7f3631ca9a05d2c56ba13e678dec8ea2412db4d2f615f28400b5840ec67a0e6bb4e17c2353c1046a32c1ead282792c08c5ad4dd306b0413ab935240dd4177d181b31ea56ecd9da905b733f104ecdb2840ffe5d9f39e92c049091b2004db3949252117c7f8c4178091ce8116b188be085d97e18621903a704b8fb290df29cb51e82e049ad347b7af4bed5c1a3de4d1ef85e10c3c6649c5792087f850fdea2ba32b9e4722a1be598499972ff954c3b372db5aa89429a49df6a145b450411a3935affaa1f8f938381d3f07962ff548cfc1070daf7e641fc377100d5350ad5187bb432ba3947158ec92c08f1e15091f7953bdaf1c1dee659f2229b4aa40629f82792bb5087c8cea987aa72e4c34e0a016523ac145338136466274667a109be5d53ff83c7b32794175d7a494097db414712dfc3bfd33fbfad81408efdf9005be7c8ea5a3bf8d1515ce4b4fe88dca2d217def27c58eabbc9b9fb594e5a0b0c6939b67b05f3d7b45223770b1d0bca85b33d4f7228cb3ca64a2a6b54c74cc10ea07af552e4075d734728df92583d96534b17ebd266bc670c25b2713ac6f4aa034a1dfdd676258b384871b39a12309099626bd315c741ebc27799067d302e88027d58fa14d35c87c5cdfb5caa38555bda31a1f35bb9112eae64290cb1ac2fe1048426e73241f9f3dcfd1699a996c20c69e9d570a0e485b83f11885c00a6bf3705b5b476cd15e108d913779caaa95ad7d02a2bcd11051a16ee68c139406bc5140dea3ec075821d0e1213012af02fe2772645822d93e7604007fa1456860c532a09758eadc18d849ccb425debfb5e147df991628068d129f24df6288e03e54e8e028cd6f45a4da7b8c36068aaeaa164d637424d88d7818882912f3502178f599102214501f4fc278b5cad2f81ad5b5bd1808e04487ea49eeda31ed1da5240d5970f3d0eb26998f6794ae0f36b5b5c1669a86baa772bd39a1195f3d459eaf33d5f022510b10561d7fe832f964ec9b372cf3937930d5ad18d4062cc70ee1b6358840154ede54e231b6f6aa97a2034fce68a70306d6d01c226a49278bf6f92a5d8830615eee83b904cd483034128f3a2d064a20fdc12506a78545b063a651594690f0d036108adb02f14a4cd33589f1ca81f55c6a46c01ad4348ee0fc418a92ffa4c04f12830edd92264bceb8d518c2e9e632625271c6ec550d923793303ffbca13a01b710e72d43d1def8fef34129715aa1ead521f362c0ed1827a204eb2a33917e03abc4e328a68eaaefc5e73cb6f5f906bbe50449925d679696a05ef251e145110c09b7a23514cfa064f445dba3e033639da9953f54a6924a774cfd5f445169c10a6359dd5578b7b945712de4d6cf49c6ebc115071386eb75fe90f017c55f0f530aefff9a7122dc604bd6935501365042e06885f18fbea1c3f9729555b9573dc30204f39d1fcfb409be440128b907d1b73ee999e02340d83af1dfe52aa6582b710fe1a1ed0aafa71c37a370c95152eb99daf020834683416c3c356ff56a9aa99607ee1510fb3115a4dc290ed46018f88d916e69bebeba6988a875fbd0240fab18054f07137a5bc78b0bf03a7539b870849cef24a3ff540600a0194cbf9e29d795039467ca257063cc4d58dc7e4a81467258a133290cdeec81efc2cbd27ee06c8f07782d4cccef7526ef326d97167cf2c1ce2e3cfe9a752bb2aba285ed1f23b5d1050043321ec01769027d20e168ae3bb3f5fcf12ac20b4534ff4fa95e244cd7020d56b992b33357ae9556fb260e956e235c5245e9ef7842b21f0538ff943a4070083988f208d07759e0d7469d32b762f76748d8b92c83c1b4649d9ee5c44711350c7f303549075dc9fd77530d4a984bbce6d62fac62432e2ee8a54ee99c1af1000d79bb1051438e52b9970be83946ec764d02b8ef973ce54e318ed93b5a4d39bd0f4a0df7dcd8787670db99fe421e076ffb6d61879c9f63b782b7b03cf21c0d950151731410211ba484dbc982fa2d4ef6bf2d93cdf7610e471adfee0aecabdff2072bc08027c0a813c42cb2d449b9e1e9e0e4e018bc498e254ef596d6a67f964a0ac51941aa35b34792f4fc5244477d3002d714044ced05a196bd182701b9c77706a460bc7914a51e7fdf8af9038be29be770d63f4bb0919d6830b74520118c3e0da23c21f9140a5086371dc94edf546346d20a4c5ce9d62a7b70814d28ad968f036b6c80e832c5eee8b6a073cda01cf22904d839f6e68b071ad99d454895a3770a9b87646439d0c7c7b5c4e2dc51aeaa9da4d6a24c1ffd98a0bfbec0200953060eecdeb7a182707e28a2f3101faafbb1a9ff9bd55eb99b5a33f49cdf09b393bc0b1708a9483c6e47e5b296d3f56f1e09e085e52797a850a3173ff6d78ac508e8093be5138ad00bc32c1f2fcc65904ce7eae8842cc15e96013d8dfbb1d9b1498206c01fbc9190f7613ee5882cc53bc5d3c88fb0451ca6a418412e3d35efd6583a03de5b460fa7cf51bdb94f04fc14c0e38e32feb1058db7da17bf26dfa3877bf207063ead4979c7c5392a66d03ec15cfca14bd4f8eb95a7beacd290a975744ba300686839da4a14bdd75790d30644a59aa5d7d1deff4f1be7672cf12602bf1ce7027bcbd4350c127a2c9c121328a12dfa11e15d5cc19cbbdd8853de78ee5430560141de49177528985fa462af194147b631d0025e20310f1107340cf0c847f8ef06518511334c1e70c06f7449879ecc5d551361856983abcee08f8e358708f8ae0d25ceff8d323573aa317afc4788258b0240623c2c196fd42ffbf1cb2e718b320a95f1c163602ae2e623306e70e563ff53175e498946e3a68c89737edccf7b6201fe78ae3ca274925ab37eec68cac081c96810603b1e1385a4557cfd1b5a39760e0345377a87236b6007d81b925abc9f274549646cbcc4b5582068985e067d39081a8b7edbef54dd970f41d653ce1e8773f67a088dbdd05afb3413478a7740e30caa7e812dc0dd88b7e038ac24de644a7bb7d9ac719ee89e0db7c4e86a799daa042499486dc71e29404e0136f5de510066eadb9c3923feb0da4400918f69cfba0dba232e01dedc6590c17260810a196f467441f21184fb82564d6a9b564c497b0530b61c032eed2d3fd02cd1df025d23a3323ad8ba79cc3fe08da266eb20aace057e52db94855ec244da6d440421d1a626120dd34bef4ac31c399b9d21657c090a617d49a7fc7959a135bc72d945b00588b572f7cb8ed20301d768ccce19dec5076e27480ab62a1bf16fa6c6d23cede13d6c9baec84bb33b7e38d1dc45854bbd0950f78695c9c68f32e31b99b12fb0a0bf9b0c7c17c7a8e7e80039b018d43fe805c4234b27dd4bd74afebf45c5b714fdcf85d76718955a3bbc08f73492088981018df36baf033113ad8e923ba9aff5afc1bab1ef83ca4311c0b5f654c9a3a80d0d29931dde099c0be87a1b6386e6cce777c18e544fb5cce057fe8d568a8f22810f2bc71d26d3639e83b2349eb29a3d532a88565252cbf5374bea724b2fe4f4c40dfe687f0b651718fa7c285a13312999de3d5c6467c0035fdfc6093c17b3cf310c51a6d402b9388a20397b8d7bbb05f35db0f2091488837ec347300a828ecae60376296573e8dc217b536fefcc24d6dc64a68d95fc94982955e42d97a5eb376606efd828e600eb0c63fb2b61f4733d4a1e5530e0c050b87410fc0795d937a8f40fa515c17eae9178de07c5608c943bbe5258631c9f2c2b87244f7bea77b2ad400f43b6303f06d1a52f320d9d8a3053de36763e4fe616067d0a6093dd1e4d94fb0ce477bdd96ac07f7204beb4a7c1cc3e22a7c5953c4101ff19c585ec5de54f42074a83e9c7f05ad392feccd4836bb5123277c154d362abe34072af6fd81697c5011221644c6198206bf55c73842e3338d6380539280b655e58e447999bf3c32c0c0cd7af5dbfaaa7572ff78c413a4bf52b67c513a5fd01ee594060b6ec5e5a400ce9438a568282e07614505a0b7704d4710a50cb6653a9935bc7ec9d431ace550fbe20b9c7de4834bef23386891ee90bae7308e631d34f0db83ed94dc9b1b20c08744f23960023bac7e2d089529dfb5001bac68016a421327dc2ecaa9b47323c04d77b0337d1704278d44f96eff8365e0a2023b8d52ce8d46b79f17739ae22f705023a09f148ff6341af56db1da8572ebe4650b883dc32d932992e028f5051690a3128cbcef162be35ef0144f01dbf28636c244c90afc3a89ad84e37a3b46a15016195e4f70a51b2a7d2b682c359b84252f04ac36ab76cae634041ae144e1fbc00b270cb17204e08b351bea1581146c546409e74f3203b64b9f07a677523e7070e91e8eb40a0e06778319ca2e1efd631642c1e58840faa21fbb506834e4063350b20589417fbe05dbf70d170c164c90b4bd0b07ab8a7632fd0864a9c078595470d2ac3f8c63e53e8361314d656cf91c4953a0a14bf75330aec4c49bb21ea0412038d396b7af74ab46f1f1e9068a07bfad3126bb0cfc0aa86056b6dda4fdb49be080a231321c6eb23246a7b505735a668d4a66e3563fa7459f2b092d422fb16de06e807488db0ade5b2ed03f1d8e8b921b1296b63f0a678d35bc95f159ecefdc00c1201a8eca15d828e1eee749e0f3adfe52d707d8167b97732a3847e0824098b0a5758bf1517cd98468641c9228308d9a6804a67c3cbc9c0e5ce999079463af00500bd099766a3f48807e63cc2ab21a26b370e189aee6c2ceb9b0593fadcf7bf0098d5f161e9d8e749a27f8344149fff90f28b267247e0c0d177c014a57a1fda0e93b3bc2f1b6cea037f13d130194099b9401d871991b034c62b807798fa5f7300ee43b55a3981c2ca8ff94778e5e682f34068ad20ac4997f1299869ae7648060f66d75c691eb2b460a1a5fae1c2c01674937b82b385bf5f3bf625e28385b77306aaddb289e85c3bbe6d1ad045bae9333c79150d468fbdfabd1b007d3e2ad8e20ae71afd4d4073c9a9e7fbcd885b1f4fa7c376f53f8304f02ecbfb602974a2a3055ae30c89c72f8ccc46543f906ac0cfd72c279beaf465d1fe0d1c70f7c929b60c3fd7c9d00bd85d62b52deec48e57e84fbe55bab3668d93cec2644a7f508f7b07501e2f027c3cca38e27181818a918128f11bd10b26d95984f0e0705ccd2f480e5639125eda0c1817d3609fafba27058ca5398a668bafd4cb1dbfd6aa4bdbd9089d0ba841abe138d27c09385cd593d2f59c6d05d7812c3337f8c1c1c733bd0702333f87456109bfdf5cf1daab8191f814b9bd9adb6fddb5d293ba50e4cca9a7088c125abd1f2587152eb6d7a4e8ffc949afe72246d38611c722f0b4aa0e0a5a0e false +check_ring_signature 85890cb959cf77301649dd8953d2b95a26d0109d5a16b7ca1e891718811ef7a3 d28fd3558c99a058d0a3650437e1af4b92f8eae80b52a949ce672f78aed18ea6 1 db72b345451eee10e87b69bce7a0e447062df7ab937990a411efeb6048632f46 326eee7b4cdf1432d2c4669a0ad7c973b7f310e43acca1537394894a525d460b99912ba6064c0197b778af34345ae0e4b13ad34d7ebc6263bd3badd69ddcc106 true +check_ring_signature a1baa1ba1add73295f9e8dc08311744bfbce113b8524dcd124989c954a88bc13 1ce585c543759aced51cbc5e2aa64ca14139c2a6b2706ee8a7f7b63e907e2b8b 2 40d9e90e9221a6c43ab2ead12f41c087e38eea71c24d5422f86d87a8d9cd6fe8 6fd702c1468250e77176712607be3fca4fa949de575369e5c5778441447eaeb1 482e0457c58ec717021872c97c071da011773d63eeac6af22f29ab14277e1206e5e89f3de8baa042ce78f8d7a6338bf92575529cd203f0994ed23296712a090b669f1a9c0d23135f52e9aa17bad749452c48c405ca70f87addebba3fd9088e046e50df7941d2e333842862755bc92e398db79b3183e027389afed6f51fe3e50d true +check_ring_signature 1232f2507c407cfb4a6c4498259173257684ade60ca0a3af043a9ea8c0aae36a 2001bd7cffd56489eb70a774132c530a200ff2420ed4ea3c5623fd7595722c1e 126 1e53352b8dfe19844778536654b89fe62ef62f33ad92598d851475b515407ad8 311492f01719ca4a353c3c4eee247be08842fbe00c4dced945818fc37754d1b0 1aaa2aaeac6891eba4b6b756f174c45619d3d004f581fcbf29312d483a18c8ad 886fa67f956a68c6dc318bb204848a1bb280b6f6ad4cfb305ae71d0df83208ef a0cb80ca8d369542a85b38c7903925d1bfa6dc9907f2bdf7b7c17287e9d22526 b95aa5e65729010dec5daf799cf09f79a4f64f3b851a1733d17f79d4d8899fee 572c0db1ee094195b9b768fc78f48ad40b11ac169de44d098f95a5bd0d332d7e fc70a35357bf921ba5ddf3f4b742d0076b4d172e00d071e888b43c4ccc2319c1 4f896b84d21d5e62a216c4370d67fd30f9dd4c5b0183f25ab6b6d17b7dec46f4 fc0116063ccddbb823148dd006cd3439419213e730559832010499c8a01cc775 ac1717a377a8ff340fa6812c2b65096f26b506d8b52ecf08901a714e650e7eaa 74e6cd7f5e96382daab712c88c9f6e4b476e6ffdcdb84684411e8e4e51ed5ed5 627336890fd7b039bd9a7d1feff6cb54aa031245244aa58abe699eefb3a477de ec7b5b2d60c343193a2df5b5d6d1c371925a947f1345d88e876da167073c3ab1 3924bc6e9400873ea0c07e6bec293e6ae3df313f9b1015da2e0695309d4934a9 bf64fa08e64653500e0ab7def9b5b05a947c4f3b3d06299b2f732e71fead503a ed7239a2a949e7d7dfd953c4ba671aad3404417933632c6faf284216865a480d 82123d99355a135c977f2784dcb116c1dc225a4045fce75566911fbefc6a73cc ee7e6eaeea5ce6c2e5f20d22fb8b85e71d0dcd9e2f191c90d4461b3adf57b89b 4e92e9ca072e975120d20c0450fe0f8866a9f3bd5edf7bbc8d18d96fe74418a2 718be08a0dc67ddcbc6fd161143e4bc49efe478a3fbd946e7beb9dccfb9536e9 49868f4508cd81657594422f6e5c35d0c08a391d271a3780ac21f3cdde142310 35b8ca2a1de5c8b1f3cbd2dd9247a82843f0b014c91784f11a448d5185a5e1aa 5a817e007fa0d86bcf133563708de99a4c6620b9513572fbfd18e2ea025ba97e 5866c0409cd127a34211d3cb9b5985bbd04ed43b498392612cc1d9d4997bcb96 b43c103a4d95f3399897fc52c76719a21ae8f60d0d0943bc99df8b81c0572599 f131b653a04b6ee5c3d0d270b4a533c32afbd2e54ac9c4e2b23cc5e799234886 5ce060ae15d08c11201142e546181ffa0a947accc0864e723fc1118e12551cb9 56e4fb125bc73dafdfd4b07b0dc999f7ecf76bb377ff99e8667a4b5cbb5f2965 29f2995f0ec9a0b34caf2da74579b28ab48a9ff4a700fa85e456171aa7b94bdf 90b9e3b5e30e3d28aae2cdffa8a7592ceb3428c684884582128b8d8908231f66 0272173f9247a623dc621fc75aced5f2928f75ead47dcf6bd57f123f8f1286ba 828e14529ea2d4fd0c11b4390b8ddce2cb1a65b5d9eee9eac64d06ab64415997 106b49df18e9fae61b7c0986fd78125390776429d237f192f1dc135348c30d66 3fe497f3b955f502b146114050e12035e4132ab609f3b6e42ca8f4d56e5d2978 f4257c1f927924d838bbae077a5d61684b8f86b7a2e6f20e3e7de87abca9e40d 2200b6962d8057f26e048ac1bcc22147663b5ce3206a4e3dbff46f6ea535f15b 08f310bfd985cafe1a856874e4eb66a98d9f5e3d92f6674e8258f819fe810a89 4879bd6a55486835d814afe44bbb9743f47d511ce68120002af5b248376e37a9 a84e0052fda64c9583db93af40ead2d84749712d94ac9e3c7c3fa080362a4caf a413447967c450a4c46b606d6b9e9fc8e7adb6244c9f520933e38e3f5caae3b8 f5637e72d796eb6b997141dc3ed7bfc9b968738eb7f2191ba19f70ee12895a60 39b7b24e75caed653ac5ff247bc024c57154326568b37f526ce3f2ba0828d341 54086aff269abc90cb5534cc6e959eb9c50fc357c9ec9b11d5231c55c151d261 a8c37597914470d1a305e61908d26995da11a7f8c45f31211caa4824a809ce69 854038a456af9538c7b134f12e0b9eeee16254a8276caf4c37e4ccd83ec55075 61bacb5813619a37091e540f5977a413ef29bbe7717e0c8920b464d78afa13db d24d7d618b02dac17d071c76640767192f987c9daf25c72d685070cf15eb4bb5 31ab348d05f3f0d9048cefa0f3e597eea5e70eae7d848f4fd40887a0e661cea9 78187b94f5aa4009c93b46e8ddc1eb6f6ef085f324ce59bab4640a8418f657a2 8bf06d06ef2caf3aec0caf3db40d0497310f8d23ced136540909e011e3bbb3d7 31cc919a730c2fe7985819d6686e4d2fc53cd00973d7d78b68327838c0cabd61 3092b94d11add861f3950af3d605d6741c258d482608465fd8b5b8336fc20026 8137a92c9ea9ca43836c6d4460796a18b1ae88b97936f07216cf85a4a7272eba e56ab7642410afbae39da74ee688ccd9ee1192fa3fc72af778dad24a86807af8 41ac7be2c39a15d6e6b94d102d14df5c9c16425fb44e7a609abd43761be7ca19 00882b0a43588d9b54977d042f256534f1369c4ce4186e71a26aa567e8a2c01a 202e7629ea9d1ea4fcf8c2c6ba0b5ea516d0eebac55f8fe811de4325aad299e6 cbe0035fab92213610abcf9c943b2871c8a192103d9cbfc7ed8d310878e1b4f4 f8a46503ebb59d2c2def2abff484a7901c27af655fcd20a855ff7ed0199b066f 682901b74a1c3e15ac266de60662ad442610c63f653022c365679cde3f286326 fdf2245f8fa553cb12cebac2d172298c76278b390ba77fa5c354bb72da35a58c f8d71b481a88c5403425746e1b76ee8b7808a9b3bbcb6e61fbe0d5a3b83a5c37 088ec2c6dcd0e64399886eb11091e8a72445ec00508def4b2c59cec9216151d8 cddb17c3eba8eabbd93817354b91f532e4526e252b91b636f3d4a79dc64e95c0 dda722d87217a19d8e4244bc4e634cab2ddb9969a77a96cbff411ddb0311d57b 9256041baa330fca39c0c8fe2ee9e96b57ceac28e78394c34868a3529afee1b6 b6bc2011921a2b4487903486e520e547dd7c31e0c41244fa359ca527bedab72a f2a69915d872304af15fd9c9c005f8bb9d72e67532b44b71d07b3ea8d076f287 1acbe8a071cd80c27cf01d55af6b40ab87c72fdb2bb90240b4057c5d1a2bc794 953322193f24ab00ea1dd1d2a5677a5eb6c783a71b1956404c743f96f0662266 c0083eedf82adc55616d2be2a9b21ffca0ede2a46a5e0929c4466849d85df486 d02ee15b9e7149f0982a61a338e213239a71be3c06e4a3928714300d26e15707 f04a433835eca668562058a27589a802a8ebc86ad59a71964c09147856007c85 1a1c1157763ff97a9269db83041895aaab21c648fcafd0aaeb8d05e5504232fc 20d784e95f62fc49c5158ca1306078cc2563231872ff6cd4319d8a287965c312 b82648117528b7f67bad7cb37cd0f585e3a6f5695c6b9d5974866ed72cb3da82 6fa58271883ede0801c15b2da13d8ca5500a00d68c9d2014258d37d6018af896 240023146f7c04013fb17e8f3596a0cbe05b34e096b0ec87e8fc192de7435339 e85bca1eccb62949a6d88a62eea7d9b93845c89b98b1d2bf5fcfdccb5c5d464e 73c693ef4fb5b2f780764d53b4766722022f4e197690bd0d0c700af5c7351b7a 76841074a63987a1784c1bba2e4bac058934205f4734ee82e272ef410c628736 689f2d98570e88bd9cfd49d6bd230706a0e1386997b648ef8b7d2dd59a11f893 95f5a8a2e5f13534f77722c6ba8e9b8d133b517933ebeabbb60a732fce257515 ddedf243d651cfa0a809f54622bf6918a4efb627ecb79e1ae7f865cf8d1ee3a6 533908e2567f10a52215422fe9a82b2e6e26e464043225032f46a2e2aadad419 4e322c3d4a4f64fba4783a5f3fe05861ee3cd54999d84fdb8a17a45bc110fb4e 79aab7911b65ed51c437d6735cc8a17a06d059355d369cdbb02941a95e3861fb 1f03528aeaa54eb441adc463605e358f9317a40cd257af50792931ae5144672a e8fd5cd844c2d49c315cb80ae3d3f04b33a1b866002413308ff83cc110c01be6 52570046c25e9ae5a530e8403e7658bab441372f3dadebec1b0524fb7410b6a1 ea39ab53988ff2dbf6d8410356ecaff038982d3d4eb5d95e01919a95bbda776b 8018493c8c86bee7c8a28a82f2b89e328dc7ea28dd3cb6e8d8e898f61e92d2ab 06272af85a034e4864203c6e6b1e00edd16ebe9939833001110db75cd54a6af1 ed4b2c996abde1651b5bc4349fc1d299ba90647fcfd634c1aedad11beed170e4 cb436cce2e86dfb865464bf07503d6ef268889283307d9939b9fe5e3d06c47a4 a54593c84040168c0b38b086f2f06bf808b4d8f5cc41795cf453c169f39e19f1 75367ac755c796c32ade85b10c5edad8bf05d118013eaed4d2041e568723fd8b 010c551d6fed3de08f45fa42b0dd52d73f2df9227efc685c8d034e452d902063 0ea0cac6e7821fec48a6eb599a0c10cd21a809d7969e85c5c202520198e574d4 a345dcde9c2a7f623bdb2b73305bc6ffeba46aaa78ae4a994c99b95220579f59 580c2ec2dc0ee9f9575c747a0575d58517148602759c580612c6835dbcc355f8 decc53233f50b6e60c96cc09351a6099638339f688fd47122f4b8e78278a71f2 676a2dd48249b00a6945ef3238f04c1134e6a495b4c8e2e97a25eaf88170f611 d74aa0d5f93b165f9404759696563344656b7a7370ebad2c0601b4f16d9131e0 d5297c52cfb3e580eaefeff00f20f95343785dd0d7d1ad5fceffe5fb0ba5464f 9e20aa3617166ff39be76633d8bc67e38ee0f29915e971b898328005838f08d4 e491fef2b83a94f4a70117c4d302ec49b7eb99c3233a30167649fcd8f950e94e d2eb63ea56efeaff34b84f6a360b3acc296ccc2e3696004bfed958856d34721e 8be095532b28b433ffb3b2974ce0b5fe89861b614e4702e80e81654071b1f46d 2af8b45c004cd0fa1aa88946b8c82f2346f8c1b6dc4cd1f3ddaa8dc13ad1b029 b4d8d25739ae6b3ef3d57aa791a2cfc9b170406d9362103cb0aa7bd507faa795 a453b79f312ba5a38a8a612e2e247356910756f16c59be653fedd74d5c37311a 3f0cc1d8bdecda48f5a5776cab5120ad64966ff8a50a22773b5b913b979e372a 624fd8e9d040a2779ebb6fe0e9fc5d036e3c414647cc6cb3c33bd09a6ad4b0bf dc6f6e2f3199d04ad9ed6ea0d256984c496936581245da435cc0d71cd0876524 7ed45aa8d7c958bde78bf8b6493df948153c4a152bce2b2a5860e7e8ae7aa412 33d1a20b06a60d3b731c88d14b1315bd82ed0bf2e15c1c09d12979fcd21bdca6 7af22e01a2cea46dba5aaae6b4733e91b575f3493bf05682bd61dda27ea7fbda fd35607e77a65c5c91b94a1cce5031739f3b7ba36bb87f99e0334d97fb0866f5 4ec13bbda39e0dc31c9812bfd97570bde33b3ccee0301efd2a3282b55ac53a9c 06ebcf9eaf025c076c5509ed5636349192a63cea9b3eef1210826b1a7d9cc6cd 40b4093b3a699bc0a10d2fc013be1311e899521fc94a1c1ea2e1525763df43a1 132e8fcf59f7a59a358000bc4f688f498af0da039c5dc0f75be2447bf741bc95 2f0403520f1c3a02697a2dc1dab480d1dbf1360d7357570451fda49493d2a95c 6f7e9084115d6c4e5287490f49e8eab3b0181ec0c1a96fe8e603fccd226972e6 98e400cbcffa983f3dd6903d466f3d1c9e0900091eaa7e191ce08d4f54b54b058e386a3dd4eea09de32a9eda4ecea2416c4e8cdff41af265c2430416d0c41a0f8321647cff30e26589a30b8bceb1518c480de2f783ec80c35b4de6583d85f60aae647676d894bb834fd341010ab505feea0391666c8ededde7aaf3e7df011f0eaa82752f194d1078306ae58c1fb7864791c477db7b43df735016aae26813fc0e2b8525fd8ba3969d1579f2a1ec3c86c214fb931ef72496058e72d254745f82027d72edca20c306d63602d4d2f7e8ccf0e0c2580ddb3b73797464b3f6f74f67061bc8b125747b584d3c39e7b53a69dd0f4cfdde0a9e826674a8aa264d8d2a4003682523a4d842951d54c26ed7331a8b402b2aa949e84c4263e14fb383f217590390386b45aa50a4b2e4e06325118c93b0cd0696481336a0ee6e1b5174ef811d09ba58de6259f591b6c59ae6db22cf88c996ef7470a9c50db80e0f42b2f27cef0e8d2aa541a41fea56a882aaeacf95d03602a3009151f7f30734c5b7de0489b40d815eb81604084e7a4c5e3c2b6cfefe8226cfa75437e6436646a9d2be34f6660add0ecc7bd4a961486092e2e1c49d105cb923849a0519dda9a9edef7a3bcbe60719707e5f747526adb68dfc0b557bd97bda0fc855b7898b3e0fd26cceab2577063bb5062d18e4fbe1a749c0741458cc4330642baa55d97947a1b34661c9764c0f80fa332377a4779255653cedd3c756970e685b2ebb2ec9e7c40edfc3b92e590d9832e92d2a3f1c6edae9430cb7cf7355dfe66c3734f0019420e26f85a6a8b604e8ce6812f3b0cb5ebe4d16a4d03f68356bad487cb74829d14500a45a714efc05069b7aaafdc576a960b8d91c5212a4abbc4731768dbd1070fe04935535232108a31de8a59ee45e334a7415c5aaedeeefa3a9c210a46a82af138428114aca9c0695936c21781c70c6840a20fde2fa17db0e85b33cd4ec7a2a7592059576d9a4027c339b5f6cde98a215b128209fd8b194d1dc96cc2a247df92cb118375aea5fc88560c903b723a47c0b916c5ac7101a851a5466c605b30b1f712f57d29b99c103ac511bb1a362a940e3d444e50b3fdf37876598c8e5d238bb620d59287b4a6104e4a139229e707f3bca99f689ecf2b29b1f47d13500817c6ce39c8713f22d600ede3dcc053869321f583eddb3d78524f8dc6e6670febb55da9f4a4989d5be2a0379f59a71c17fd0de40132d3530624f881a895843882dc3dedeb3f2352b23340b84ba596e6c20789ec643a1f3d49aeb57f65cdb23ac15813f13cadbbeb1b80007a7749563cf6293f9b88d77983aa988e63ac9a8e2dba822563272251a37f4f703bb065813510777ea83d95f48f51af9bfd287b0e265c0c98094e107ac68628c06d79a9a3f5963ce1dc749dda6d0bced2fbfb9de4686ec9fa36231d3cc3e57840f453c1f97c090f7f9003a01f018e5a30dac466a7219784f01c384a25c4cf77c0d1b2f00b6a5512d15ce5ebc24391d9cac8447396bb387c78e525a1c53309416014f93db882b087458a420d45ad973166f6918e81974cdfd9177bd10be2cc76603265b19800d7ef354fe8e450709185b897460337ecaccd63ae51be440fab13b743bd2065bd3b9e573784baad1266abdad96014abbb28ce4047b795ee6d8c6e30e0da7cddcda8cf989205231bc9b168cf92a946c31067f3fe959d4cea8c4b6c00f7fe3d0a35c00e83eb7f28b2d55ea503e0c546d232d816a3ea25ecefa039f720b56695cb58d3bb337ef238a030352e6a66e3492fa52196a7e43a2c6892395230c6150b9bfb723a143169476164da7aaae61c797eb47556dede7bd32f909d9c60260c4e07f436bab78e1bb420c0c6ea40f1a1a4ccfe71c07d3ab2e30ad683a6f03a6c85131c60ae7e28a79345e85b81503b4e97e16a6b676f5c24943a8f3a72703a4d745b823ddac0cfaea1d5552f39392c13f958a1ad1899c5a178ed1a71f1f0dfad08a6c6ce128aef8c4621d988d21c27db4aea8fd4f6ceb2df0d4d92acd06d366c729dd54894e9b5b928cccb34b712fd16e233d98e55b71f65b9dc567c117cbe8d58eb05081c1a30c6b3ce4fee0019fcc1027f771dd7c36daf3acf05537ea0611b7d41c5ebc874eeceed2e865ae716a9fc8a0f3a645ea7479c65fadbd1b0f08bd0bb6aeea4603a8af58ac11e8209cad5024505fc2a8be57e2d648d57bcb8101ebbcfbee96d1daa20eb1ef1728af389e2c32ab169fd63b4f9e2681673a2de506b4953ad8e5af0c9288888c4832873cfeb59bd4bc18bb7800233ff4e33252d7bd6619c20df0f28295dece0f6135e00d69b954c5f58baaaa4140780a629f01cb0bfab293f1d074cbea199c6b632109f5634b844f1da60b3fcb6d551c112c4090086596fb755bc5191add2c2f1da89233b03e5ae50828a1294ea507b74d796c0c0502af0d04e72defdb0e164b4fc462760813c1a856cfd9f2f4d6ba9246afa21006d801b200aec8f49468e316c54da893ae7538b9614b0d904ab3237391793a5706a91c6832a8b026f8201799d51050980acd30a9ec206c6e58312248cfd107bf0b051deaf7f3acc799e1278a6746ce935a0ed9ec351eadb091bf525a2e9217760d0ae42dcb8535e197f11a444c8b777ddf28e51f423e27bef31c7b9632e1bdee05d2387c5b4d15d9e83840206155cc5d94f289fa7b6b93face1c331d55629fe8033b1c4fd4f7dc83837706fbc1de85061aa26874f248fa421b99f898a649538c0e594a1ffcd80b1440468b5325fc74acb5013b55bd9dc39b7158ea07b3af026407d915db8fbb7902d6d6aaf45f221ee0e55ef8425ababb47d74bd90f35378db00ef23dd6db8c3f6cf94f1000b49187d3ceb3554cd83e713fab63f2355452ea0f0fd91389cab3775464783182d0b5f598fd37c4299362548fd501f117c4b54da10d2a46a493e5f7dcdc5b96fd035b527e8977ccd054ab26147b08959a22878bf70fd5f2ab8ff750d775a175234a824f413593b8d2371eb879f3b834ecb0e0a430067965b32bf9064ee7ba749392c38e9e1d718fb5b6ea7d4b7b6ed0de4f5489fb0bcc0d8b849f31f11b229726a022af86f4d017058e70d1b48346180a65d7e9d6027fb0d7e127318a358129bf631edb84b7c0bac43ab64af0de1bece87078040c07d540aeb4ae7741ea77bbe95192bde5d2ab9d3674af82848291395475e77e220b6897613acc14475370e08dfa223b8d89fc87d8359d2aac99bd5fdcc4266dbb0cfe00324c0716d9eb91f8119bbd263189d4ba594003dc8835358d82513f26740504ac1a525a2841df9829a78597416793f9b69e6825f572178a670c32d942010b4dae6aa3ae4ce2fe17dd96446cda57d461a444149215a064ace61c0d027d2a0c11ee2d693c46dd2f4fe83f5066da9c63fa9a65d826c54d89298f0dd9af2d970d498c702acdfbdc9894d1b7fd973385a0bf039686363e8a69c62cb93cdcf2560ed68aa730ae7f8b0177e4de47e4b290bf528bdc63b661d71078b5c9d06be6a70d47a17b33a4eccdfbe2ca650273fed9efb1d448a30c9a550c21ffc60a4340a0010781e7d2c54e66016a0e5d6d384529a594096e6d240d6ea6ed9965f6a5aadc0a1393816a333ecf7dd3a87e5cf2e3a8c790f554e356691a3baf56b0a2ab8081023b04163c5e8071e508ddc12226afe5f83889866cf2ba94842074a15a18283807a916934b9ae183f74eff3d7f8118368e0dc7a36c822c593b6a90ab8ee470b408124880b620273971fb9262f00a2fe02bde5c29d88765a606bf28e2b5f9b13c0be0ed676855053cd752c5890d2c097460592ad432038c9648af91c297fc63a9009166ebb78b16948c94e1c2ea7d22b00a45f6769bbf7d33eb4390c0ff18ba0c066d033206c22d0dccd166dc7de5bfbc64f9a5284f8fa6a7b1e086e8dc20022b094bd20befcd377dd7519eded9f3e400e66dc5a2d97ad3d9070d8ffc42f70f4a05b465441bfe778b3fbad35e2ec08a66dec6814af7daa660787cd03fd666afb00d02e24a9597dafae59d923680ab4756e3312deda3ef93377dc4da88c23c84750cce68dfc92b62c539452c00dd9b5b56309223addf6837a31e9f2fd8dd71c71b04d65f621d62c88bf51543ec58e8ceeba885372e02778544aa00ab00d7634f39067b8e70c6cf3327c53c55f2769650a50fb7f52c9d42e89828a272853954315404bfa2d09a6b9f41d7d9211372d99edb49a41a1f64d65839b1c198c9b4f3b9e1092204cb982441b06e88c24574758f1c4936e409ac3da643ea3c7fd67986688e05d26e194fa4a2759e5a11bbdf018bdf6276208cdf7d161f0d6ee7bdfe677bcf08a40d06ea21f502adbbae8a529d4abf273c0065b4b3d216b7395e3e12fe385a079f49fed62198b721526d5a2569a22aa25a6b383f793fe9b81a548ea23a517f086753264f6e76c569c634971d90aa3c558bc4767163fb4394d2b5f2e624b29bf937d83ce1e6a5232e3dbf8907bff2e5abb626f2cf3b0801725175d317de39170ea08e41f50f6420009dcf4ed06c5cdbdcd0c447d85ff00d82f7b0868f9ca558010876d06355ba47f857280a43a73261f92bb9c1c89b51b18eec162031af2c80011ed22d6fa96fc80fc9b0df07a9abd64ebb821926b3f1da916d78a3874e87cb079e6f18c2d6f4deeaf9564a68ad41aa2885de232fe7bc110e4c4b8d4a7977e308d5ef173d3a6fb0f520caa36e57a5f317cc64c30a4546db18c557fe0ab4ad2204a18db272890638278278c37cf0dd0b2a46012829dfd58b8f10295791c985dd025b5175a943475b6d51fb11c432e4a741dbfb6f6bd809d6a3b9e3f99b82580f0590f5ca705a33a387cf36cfdfae062f0297f42fe2e79a14692aa2ac09e681f0095b1249413cb7ef45a55fcd2f02ca64f52a5b484c90178922ce0da4e44ed85b0cc7f44b49bcd28933289930a7d3ae7f2d6cc245aac919e9c324be7ad187de230276353a7b0d2af6f4cd41cde72aeb24b1c987a2ad0570c2e528f9fc45837e980ee1c39ca634c4a37616aec3e71f5ba3c92c7d0a8afb0351ce1d539ef26cdaed0385b8a3d7486e4692089b7dd34e82a650bc05ad2766bbc153f95c261de32801012ab813584d1dfa7bab904ac6462ec0ead429d83600aaaf949a5cab2b3c2f37096903466f40f7477f3b61c9fb4e536957ac777cf3f2e1a126507b1ba2a237c50e22ed59e0e36d54053b6acb941113ff04c1298e5b50e8ee82ab7ca11ef1aafb05596467309c6b491063d83de4b4f35a308000df914fe879f1f779e0de0e26c9009765f5d60bde41d35468439a384674aa9292aca78aabc7db617287e64211b30021dd4b85cb7383454069a64d52fd112518438219e30b9a3d26df12fb8118c1099e5d01a9be32103089a7b9b559262d3e4d9f59d243e13ac85aa9031aaf34760cc84295fd00d3fde3bc2cf13d0e43664dc7ab048494a5b178e5d3493c6577e5039dc260b073b980d7a08724039e504b0e5566a25ad19ed0961c7bd6364448360d543c501eae0e2fd7f3e867992c4d3f828896b1046e87cf563431f58d9ca57f029da48f1e7f245909630e2aa8e2757aeeb6e13d60ccab95e8b0c8f4bd411d030fc960a49860f81ce56b9df0d12ebc22aedbdf7e1e66f5061b678b2b426104470abc040144712e1b529689d97820cbb9909eaf0be108dde608ef494239bba34c0244d99382a1d922d59ab6bb7880bcc009131c90e113c8565585095d366f12930f9c0b358ab1209921a99a991df5c88d8cc997bfd3fb819fb3bbbdad56982abe0e2da84db3f6dbb8f703e7bf5abfb33bd42d537e5a76b76001b5fc9e4151f638019d986d670794be27a522a187a136384c675ad2f114a45ad32060be03cfda790f8ee63fccb2674495630deaa86f9466d2fd0c12881a469f8cadeb6c2c6ed35909cc9f5b06b7ae349177c0fb5971a74559b038da078aa577361619a765e8dd1800c0e021d14beab3f904924b61b3b744c0c898e69b4cc2e792c70dcaf922b942047f2194deb6843f39930a4a4a4649054e37005a69e63db518a6b1950dd0b52d0e3783f704ff838f5e27a194440ca27768d91762e61f0536e08abf8c185384710ba476c8362d5ac07690517fc8aa56f86477a37c0293e1ef0b459cc7da825d03023660c917672dcb6dcb9a1ae262da4cab0ed66dc780e76baa1f21207c91d918043816c15bcc9643d202e81b9e0e7702f1887c1e6a002a36c123703cc2811f980758d9b0739bc63c639ebb4b59d3288ea21dbc0dbabeea083b8430c374c8a9700a2fe3fa7b5a632f8acc42a6ff6b1c9e745de15bd6a366cf4cd4092a79fb4b690828e76c06f72507f8751eaccb446ef2b48f5e48144a72cd01bdc01c99f98cbb058311613136582fff6d7e91f0ea0fc0edcdba4d82246a58e0749bcb4c6e3a6401feacdbb33db350f49dbd06edda7323b796324411e719a53be07f87b9d06afc067d3edc0fb120f1fb582667fcb71b8fefcdb9185fbbbf88f61d2a0907b846270f69f9a784f37718866a540e50b5c70e92a4749188afc6ed523d83b333151514048c2d854d71466d680c27d2c7a8e3b556d9e76b63d24f8d1f1f9eb58ae9cb38089b5bae18d2bfd1a84261978f70c0106e2f1c4a5306a7d6f7945e333ca6900304876450b042343c633c10f6779ecbe6bb6cfe83304316e1ed4c0615c7b830cc0cd1869676244ec1cf7145f8b04df5f8b3122ce989aeba86c8abe27a0cb43d540d685127a5134a6e58526953c07f3cb999b6ab98bcaa24af9ce639828aa7cb53009b878671865d05097bcccda3520da801bfb5d1b48c8475992828a7203f6ab80967eb938743c106f9bd4bbdfe67c88e6a89b947dc86014f45e998005547defa094c93aff74e23153923f901103884bfefc5f46cddcd29dfa6de228aa6e1964e00e8e000720a1a1d78fb69ccfbaefe094263c6aeed23d07a2e032ee5f7a2a2ba080986b19a0ad701218fce92fae4687fed7d880a88884b92fb7290309a7e77940d8e2edbef17296fcba4f5b62f6868f94fa3931912b9b4eaa75a28193117ee08072a45346253e5d8792c8ad318c15212be2ba718b6d2ac6c8e12dc9690f297870fd37492fd7ff28d571d04a57d46e8ceb51889e1c7683452f82d9f9a43b8b8a00147fd934c5bdb960a0772196bf432180c4a906c2e72a13a87aa4e83560fcbb40f56214607950a3b29f0baf8d8715851a0b2a5433ee9606895175090d6d3a39c018e6701de13f57178422e95468f01dbd5ebf1906d320f491b8312e8909a4ae50ae4bc9bbf981b09866e8e37bd51fb42ca4bb86495e1dc8e444215a7a1be119f0c1f9c9c6efd05f8d1f59cc6e3ee3da09c14137315d462d7971be7111b33129b0cd22532b3a10dcbe079660dcd627eba4a641c7b161f2fa115620cf5336339a802eaaf2bdb6b2bdb0add85537b7c85f96d4f8ee5cff8166e5e66d8c68ff480e50c70dd978ef0f714385cbb5c415249eaf988a611f193b869efba62f4eadf8c150280cff17eb6a9b76788e863eb11353c705596af3492adf7ba23db2ff37b94c308c5550999f0b209fd1dfea54d148b4ad3d57201c5fdf3880871ad8d29ad9c6906a492dd71c15ad0651656170edbb05b7c1ea1c1c4feb2679fd0ebfbc3c553fe042a0b0afb49e22fa2cecee2e031a54970931771e65596cf072c6144bfe2c1e20f4bb9a32fc87f2ee215134e0dcaf7abc6dfbc7c804605626d5d565c03117aa20f963bcd6bf0c185c6b2109da72ddf564dfb6911a9a8c4a5c4a516a31e9b7755038950ba810697b5c23d08aebd62147e6e7b5c5582afc79f485088c2d591566207e4f990c2e7e2f5f0a04ab1e26b8927da9ff23269c1ebd5082ae838dfb2c69702cf78d2dc1b55f19a8012fe4783b62939effdeb1eab4f49fce1c537249edb600a8f82d7691e7aa747b88e7a866a7b1c2d7cf466e33b080b2612f41f4a55b20a0fca8943153a82a220ef3e4c587a4436ff3736597b44f4cdde9140db23c236520d12b8dd650a8759cbd1493383eac66503e1d522f427afc41548aa81a86d32a60c8d0f120fb56b24b8c77b96588d5259a08f5f7b8ceca1846c7c63cbb713d5510f81704b81babd2d6d69d23f26df4bf52993dde5d992329aedc90113aeecdc5f4724907affa3ebc4a2fde9b30cedf63b34538f706710d34597de1cb29a216e0c0977349589a4f7a332ed8020f669d7ced48061743cccd65e93d1acf5fe6807d80bae84f69e59b4f620d406366bbea9167a854add7b9d6f52021f013770595e5d02f856775a168f35d1c98ccc5c0072c9cb623663e138bf944fe65cd43dc74bf5023c5b47135632c27d897566ae2c7c0d8fe634d3fc65ea2e60c9bd5a58ac3b020ef509810776604c195d48c42e15d288fd3033a30a2051da86ece3df0e2117ce087a6c10e0204b77242b5ffabf58209df85a39d46a438c515970ea11f90b60ec05b477acbba38cf75515018573016053f25e50a7798357ae0965003dd6c11172097459a12f5add3209f1cb83d16b748d8fec9384f7b3534a5b35b8f78475af160456f1f064c7ff4905b154975319f9c9152911f16d13aba7c0a23c23e147baab017d22e39cd0de8db64a1c03efac5c7e366e8b6809faa877a046ecdb10d7cc4804fccf1117d724c8432babb7e372d66fac5ec41b14ebaf3849d597685a7c46100afda41a36eb71e1639e8eb355e4da7d28a261ad5a03ea82c63190d271b5d4180f8cc3bd047c35e8aeee851d63b6bb30fb06078bdcc1914dc8848bb8cfd054bd086161a6347c63f1175d45e2deabb312c3e110359cb22f053e5dcf6810f5a58c0f552bc0f414ba09df9855ca56b0446157a0c2fb9ee298f5e29f529b0e37761c0586f9df97467e329c60c36118f505663f9a1b43a0636899e474a9850e2b9ed4073c0f67b67c1d557fe7c2a4ea1f628a5dc4e6f1cf6f19901a3d47cd5c8468b60772be737a447644cff35d0767574e1958646d907a26390f8073f2b2a2dc320801bf6b71e77cdd528645a8dd78e18725accd4cb52a3595083fe6db3f8f55e3d10aab75aede106df49d630d329ee63a0468ca8e711a21585dbb90a0fc3922f72f0131463c182df06680f603a0af7de55dd04c44668a60d09a7b4a79e77b8d3aea0c6bbb2a93947010312e0cd6f1a0070ee822ecf4188e6dbbea966d4b1314281008ad1ace8c339d1da61cc3a3a771c155c2b971bb24a8f2c9c65b7c676db8f8e30ee3b4d94f79a24b9cace2821fc769a7c3d3016c9f4c3a40f11f9796f3fd3db30156f4e05d3e46075d4cda41e492f0885c9143c9c5cffb6ee2d1658b17a5ae28018596f7b8edaf029953590aa28e7494b00ecbf9ed5e190a0499ee553955097301b87bfe55e41a2ed4eaa87c6bf00e7fdba7f48c3e635c016cb9697cdc978c840df247168a89b000821202c6f5e7b1bbfd03c79203ef65570477c58eda6eacfd0d7c6960b0e71ef833b0e89995f1786bc7cf1d2c74b66e60b3abfe3766f5eb9205334869937612c4474194f87179013d761df24c9a04f1de91902c143bb694de0460d0345a7dd46afad86fb87a20402279fae85c8108c3cb89c7302b1ee0c70806c482e11e29fe06fa4dcb8b35168b819dc944be4d147358aeec00f43db8cdc6038967b2782bf6c475a670f836ac53c34656bf2d56b706931c60f8c54a8783f407041ccb9422005f8dfa87f5003d5934bdf3251c49bbbb05914515f08521707b02097e27614026d135a147d5f2c427668c511b998ece31384d65d6f56ca46f120c8d4d180f6510e985606107f82fa2194648616d292e4e4a913d7c8f414f64a00c9ef6d2bca52f765dd84ac14389cfbbd62d2083c5963033dbfb67f760ca15a2085faf5d76b284e3236561a3b5e4414e2a46238ea154184763f8bc5a5078353104189768a87e274cf599cbdcf258b71c25815a5bde5513f85f83e0d9fbf464cb02e98520f7df7c125d0a42ca51c33036e06e7b43541148ba8c4bb41491c2c1cf0281c859a6f964d153cd231613a4472a2b3a09630ff1d3fd4fa22a604efdeca609d7c60655fc73a5e6089466a207c9b2c17113cb40dda9e38da72c5c0fda2e700c67d80f71331d232cb5403dbdad3c86205735215f0ae997c9eefcffa9c8f81203b36e91c111a48b10881150e8c683e88a501127c82bae27722d78e0c5517f3a0c7e8eeb04c389f5d325703ae3c503fb7e2cc512b29e01f04f4721d6b6a1e3300b3f8ab493e635fa6d8e828ba2c078493e506ec475a01b34997ca743b19cf2ac063fce1e8c71f77ec382ec2d1da56bf896094f008dbf052ceb91a2f0fcebc4510bd1a0aa855610d9ce2b29d423f9d23fd17ed39ce5fa698ee7bbe58dc99f366000e2b8d539bd76f7fcef979e3658bd7e9acac171276cdc921bf603aee7211f60011e5b66b67cd2a293e2f2f19165d952f4941c7220136e94168c8176d55ae0000d6a93757311c7925cc29077f3628af31b30a82f745ad57a2e4f0cb43703c5b808156e8f07190033bfa011ac6942e7c2ad064465f2abdbeaf497d5b504ecdfb50ef6b1953b8f9a5e5fa329eb831da31ec524f0639aafe830b5ac1fdde41bfc8303143aa9df4a30ca744d348b7c2342e5f5eac106ff78efcf5d4affc8fdd32a5e05e2acb1e3afae086b1bc1ba1f261267f24de165241b90f198bfe6f4d926183709ae31ecbc319c20c9d68e589df00b0f7ef3c795cfb1c27829021643f37b9c61099de077a8400aa2b470730e40d022022c39e400e6b0fb376f9682214f39843406de08db5afc364cc4035bc0838cc537a0eb42be425240f165a256f9aafffe3608c9456ecbad313c93d4027d94ecdad5e1f22560ccaa6f3195c77d0097ecedbf0f21f8aa10ebe7690553c85c2a9bca70b3e50f34cc845fba66df3c5ad2088e2df9a386fa8f4f2fb142ec5c5f8e95189d0dfe3ec690f8a69edfa2291d929ef1a903662fc49269ff4059842bfacf5102b9f959bdc5e78393c2bff3dbc55e219e720f9908f4d5ee3fbeed35dd50f343f14566d3855b65d7522b037b19d38be3f59101aa39062bcab74e2e13b603340008be7be8d7b01bafdeed9c13418794187000045d2262370151481c19e71a61fab8bec895eae366b6b01a092efac6226bcea50d80654bf0574aa3eff8327df66d86778fc1e9e04d331938e0d786447fadb5c80e9a8ec46cec7cb35763e1f0d4119d9da413e024dd926e5adc7842b44faa22b10bbd71eb986dc867aeb3385400ccc7258c8a09441b5b47123f62e101f115b0c7075eb1030d2cfeeb179e6b42b086fe2238caed00ba00aec037e5bdb19f0e52d60ca8f57090f6190ff7514be8be4ed2e5b3ebda479b6a8ed3e6cefa9aa03d37340e290d1be72190b573476198c604d55146ec68f0d341d6dc968d00f79dab28f503 false +check_ring_signature 1b30e75e34853c502891abc0f5d1733395d840fe7a9e214e0efba3af17c381f7 b9610d0e0ed1d0f295647a3046958f78fea3edeed206dddacb0700722c1be06b 4 6e53fa27613aaaa1d067b81271b333ab66c5d3c899641a32655ebcd4fa6d4784 a203bf202eafb2e986565423561178ce3da5aac30454e4d6cd3a69e4f4ae60a5 e14c90077296fa888cec1232c3f5ab2592182b907b38f08e331ffc5f19f7ef6b 82fb7c7d11e7142c5ef30732d95fa48537723f4092ddfb55c56d3a08955a23d7 1be7ddbab3161703d28331b5d789f09f6f5859aa7c787d10353a084528236341cba4aa16448e701f857364a1781d7de02e180c556adbd44f4fa80d90811502065b90941b1c105e7c99ce85b21a92233a937f8db6c2d685f6bcd9e632d5854e0501030c24be01ac75c8592ccf1211ee765d1e900c1d1a3f340a09cdac06c0f70558efc2c30c352f994f713801313f0eb9a35ce83f834c5ba87b3dc4f688d7bf04a4462b5191949f6c60bfdb7c86df165278989d490bbe6d3c39c300f67a61d1089dacafecd1cc776ca86bdbfa9185a45d2931f2098b5f25a178918768603c0f04695d4e04bce9135e90d272d587eae0f0846811c849fd79f047ff93abcefa3a08 false +check_ring_signature cff4964d1fa19952b5a4ca06084579658d9cadfe9dafcfc82ffe760c1feeb35c 37a78b19a33559bc997c9d376997960798860b9bb16dbd341743855d4b349ac0 2 030b560ab520fdb16f5d3e95fd6825c4b9cee958a807d0531be7a9b93725e4f3 4897bfdb0f3cf274eb76f678c4fa0ea164012a080a54d5a775774295850521f3 2a2c44546fd42f672072303b37ecf5507a5fa557259c1e6cb350b6a7febae1fa61cf536b061bfe7c0e19de409eab6ed8b379d077aef1c9885bac19edd718ce02a7f0965238baf27091736b6b0d5190e01030020ead70e926da153cc11905290f010c770766b697e70f47b1ee5c036e0b5722eff24f812b0e959963db8e302e06 false +check_ring_signature efc9c349df790e387029171493ba077d5cf3ba67f3eec4d46d40a659d17ce293 3196c4874926aaae5375e496271538f4d3dd8efa0a4171d48de502af8cf9348e 7 10c51536484c4a602183bf5c3328dc33ba3f8f0378c20f73399efe93a89203a2 f7e128778727b2e7e95a646c19b07b0edb01829f5ddfe6f61e6e2d355435611b 40a4c127577980a1e8145f1807b4b41c484714a867368103196022091a74aac0 d15c2b7ba76c04f7c17c1465150900b56ab0dae1920454e5b1aa547c3ead9968 447914d0102b63d98bd0d9d17e2094c370fb894f705c09bdb97c6c00fc6db677 de0b02643ecf8e75bc583d326029733014fd8a7a6f65365a292903f6ae526e59 9180b0a5497b96092392323aa35e190182b6c6563da902a09da7b0e4b72ef64a ef4e536e149de7e09f581de5d126a57f80cf59e301cd400dc5c7588da64f2e0c9a3e0a1b15330f32c4195d50e0cdba5379f5f5ef2f8bbb4ccca4567d2168b20ca1a51fdd8a317878c51d158fc4f72c948f5e64cfb0f558f2d861823d1edd48d5c2214c271ae88baf0005c03469254e1105bf0d133e4f9fdb0ab00095e7f91903adb9fc974ca77e34f9f837f5c814013bca2c65c399b3ab940c07e6ed97ce8307757b40261a6472b5b162870d12fc5dbb91bd882d6d53bc9959a3e3bcc8e1e80c2fe1dc6fc529738333eb8fd97865fa1ba70fbe6c51d37d88dc8a2d8c0feef509139b51c82c188cd3a3ed092d0c7663a7ea8c9df79167dfbeef47ff6a66bf310612f9ff9f87c2b6010b64735e5a50dfb6014fe02801c2b8f88ac540c873bddc05dcf8ccca6dbfb5b3193127714c41842009cdc5c9806fc7ee84a060b44d7fc70faeea200958a9d9dfb700b46a11d3dce47fe328965c89c15429a85a1ee70d370268bd20458798cb363278fd0a723ef3b9fc8cd617e6b2c3fb6e4024d062f659038bdf1ed8751bca5c11a665a01e25bde16773b6dcafa8ab34f4c46e8e3a94030aeb3cd4c543ca8b0f627aaa1e61ad1bccdddc277cf5e26300f4972cf07ce75502 false +check_ring_signature 39afeaab3f11f9bad744b7c45852c705a0e2daeab2abc1518f4a79e525a15548 35aa6d9d23ec22c482ded0a9f989e053ec1e4cf52fdee13197774c0a80b68307 1 09bdc0769255892d4e64faddf67321283cc7c2bddc8f6283e28e73d0be12eda9 00a33dc688eb0dd1b29f3531fea2bb14335a22ba51a0ade08321805b63aea50cfd74e40dbedb5ffe87a50efe617d9e927e00403caf6528d1b5e8ffc81c82a902 true +check_ring_signature 6ad4078fe4245950b72bb84422bf31e9d7ddfe2f9564aba9b73bc84657b99b8a f102df1b9a7e50d8ce6d885ef54456b5c3251c358f034ff9ec181c57c9dbf5c1 1 fb72a196a816c854790585378e655cb31252e653d5ff18e1b3f1f5d07893dda6 ea8831e545cdc9429c4bbcc8f1fb3f07fdf1287ed857fae459563592bf888006d4bea427846c6b4c7d13e11ebc264a96a1d7daf207b46688e53bf11688db2808 true +check_ring_signature d1d9f74625468a0172dc7a8d4298d40de6d90900d16eef1c855abe7976ca8c7e 0fdc934d7ac3d8aecc934d1e7fb939b83a7ccd00c13cb175126abd08f71fd313 19 f1969109672a6db3614c8b9f52adce6f708c40f12e54c4169420a8d4a329ac10 e981e8c946036fe1301b5a1a930b00ee4376568ad4fea43451e809393c2f109d fc687e3d228c126264f168d1a69526560851189179d22fa34fd6810463b72a9a b4ba1983d0e284f7dd3b95ef20364c23b8964151b71330568d4ffec2e7095f23 3fc8db56edf50197ce3601098d5454e9f3e5ec7163e3dd8085242f6254f19dec 869f8d7fb9f874b219f63241cd39d79e100201cab62ab2937a8190e7ba280729 077dc13a7eb83ef50815c1c4082b7694e018d838b38b1659e855691c92df2dae 961552c7873c0c677525d26a114e6c8d624abd9ae73aa7f3b1dfe2f93ee9c249 be9cd8ca43f808e78547f9945af58553f4f7a5c5022d39ba3d8d0a727ae6c199 f52c06e797797528f5f366174e43130b3360c7538b1c52d98dffc4d93ce110ec 5f475479aff2af46934f7c1bf17db509c451a2358c9ec3ca44720d784c1314a3 e8160d7bbaeacad06f629bb471752ecd7fee57b2d85b4223ee5af888e47b8ff9 4174bf94d73eb70a94ff9e6763bf4d011c842aaaaa01415d1ba7648aa2bcd800 7754a57d6b135d81967ad5ab8ce41fe6f4ce667995b314af0fa4e1b231e4ae26 299b09e88c6d19c916b0c02adc8d127fdc0cc8bcd46a5aaed8b55fe909e38746 c7a3893d5eceef43804721b20da2fa173bd2d3351d57f8cc910c40d361905b25 63be1dd2a13e287c26627a09cd1558d0c44c9d11e723bb75a8dcc2fbd26e1d8d 80e6c9ff0a0fa8721fbaf7af2c62f104c4b0a243e0817bf27540684dcb6d5803 299fa5634285f68c08009fa19d0fea0ae341cdb71b62fe7e34662d9d6ab03735 6c3748e69fc097f85e3c9dfe7a8b02ab2b611c921de956e9e0ed5d74f98a3e02b231f17e4172af38ec753040148ceb73db86723a1601fdf07cf90074cdb0730ac3d8500250f9747cac0bc4b869d804a8dc13a6aa796e49b9c492cf8e091544008feca513475d33296ba57e3182d7fc44d042714db9baf685d8db3219490a830b54b29887a90779062f1f03fe31a31c5ab8560fb827738f6dc64fb290faa58b06685ef8497a2aa994aace72e95d732c3d7f3941f2da32de5c2a0cd10a3a82cc0994f494bc9f232e0cad1791ebc35bd57cb9997753b762eb1809aaf761f77bfa0b4ffdb53ccfc6745238a67d029870d7483165993ca7c376f525994c5ee08169037d080779eab8d4abbc9d23bec560b676a8c66505fc586206ea3972f00f1aa906fc666a990b30252213af9c605c4e6081c387270714808e2f7c49c3715c8b510ed0d14be2b703e4515df38575ed47e0ab8a6cb9cc073874cfa153811e7e6f79057c60753d4a3d557083b1e57ac05c33031b5774f312778077593cc4487cff7b0cbf5927f1035f3111e0c8e4b883621915493172f51bc6a739d756d0e60f2856015ba4849f3e7e6b35b3e3c459f4bd8d7b023628aeed167377fd40d9aa9eb7ee0b5b440ffef020e1c17fcc477f8579f85eeaf6c77c5a4e43b3ea0a301bc063580cbe43a0afe926a8b59e50b567a9f477cf562e8d8347719935970f080cd936c408596b22823f3916296635ede9761de2f8ba48c2e4eda5d6bc2ddab5d60b19c50440963c547bdc929f0652fe9e260857c0fbf6484bad6aad16036a4c7ccb25ef096b42235160fae33d093e0c4abd87530945072386adcef44c1ab2c56f9dd711039f0f3fb9fda39d3a9237b337c5a5b07ddb78741874e82eef73b6eae02a6cbb049111b5b0ed6ec43c6a6c3ca2452c9914fe6868380c0da1bd51f7ba792554b80445e9351a7119785653d3c58364efa61ec97607b5a9dacc1b4655c9e2feb0730843a31e324275a5b59bbf62140ac78ec3e01193bcb00395c29f235ee1f4735605a16386f0a1192139135b96068f2450c8d118ae8195a9f65ba2523c4e479afa05106287f195af9a69038ad90df9986ddc6a77159675b0d41714e7f871c81e240f51884701de678f9a8aae9514239929c9816b2aa1057a4321e5417c1027a6d00951481e176c9477bd1713803e9585c099d520851899815d1a2f2bfca03a0cf70a64c9d6b6d732700bc14c252ba67d0d0acfd42ad835b1d6d618a082d2ff838807e9af35e754f6154ad58fdb52f1092b169e8c94e9b30cf6b6c623562d6b498f0cf1ca6f8d633a8bffcc375748082c55e9a19cdc38b0b900b0408370a07f16aa03a2bc9309000bb734260b575f61b41211301f5adc829bbbbf234d5afb5f07850f8d94c0d27e36e33fa37280493f1ba8f89a9e8ca5ed6d33a528c4b3e011a641079539d73883a5e508bbf792ee0b75e1b85e192249f2e845269128cc9b6453470fa1f40bec606d5f3268447a52396af6cc818c97652d619cc7091906e035203c018fbedef5343efb1a88f4112074ea5e5864170defc3a7119668fbc379f058fc0e06a433da08603e93af1142b0a0560d7b4f229663ba77f1d4ed9bec931c21700a7cc1ac438594ce8501e7b250ba38d46b059e68d0b1753d8fbca93eccd730c404f128e8a670c2aa40fc7d571b677095a4edd3c1fcfaac7ef9e349bba9dcae3b6b false +check_ring_signature dc644addd312281d807a4669415ae534f7d49ee70dd5c7697abdc86afd2a1b4a 3de2e31fb1a390bf4c4d9f7daa48a64724096c98a44a11c5d48ecf82388f37be 1 8833a902a41118c99907151cb84acaf79fc38347f3f759218fa4c0ac057b0918 a8301a8fdaf2ae741ccf7ac1705aaa7b0cead2c65e2883762c0d245bc0fcfc0d7f5fd46c7f4c68b0bbc76dcd1eee1cc537d36b6f831ff89e2f083367c1c5ae0e false +check_ring_signature e9269ede3ef3187c064617ccaa7be16bc7117242e55f6c06e8a1157f61dc9b0e 9eaf4c30a318be8d83dc9f9a4c6b521d1fca1d0538e3e26e919dba8f1ff1daae 31 461d314d6684e267bc2e368293822cad3625598565e181a76644d59fc354a819 25881f41c28559b289986cbbce27d33adf318e62024c18d5e5e4399ea69b1575 674bd7b98e9811d8710b5311896ef35a7b5d4580369b7550e5265e02452e5107 9674f8121abc0bbc882059960440e2e966f643d6f7010cac7fc14a8888d30efa 01f305954c281053c1dd2c40815bd25b0434bfe0877287169200506484ae5a0f 6d41f4f08e5a606c73ee6358872a246508483c1d087e7e7b57a8d69b6ebac529 9f2ce1afad39c737126531b2cd8786e9f470c603c9f46570f807d149222f3c85 a35a9fa532d04bd7f91c39f3c2a7cdb4776a5d4c15c2c6b6bfc4d5909e11e231 f8cbaa08d39cb93f4b8ac5ff01fbbe30801348dc8e08f5735339c73439d8fd16 00c14855f61a66e25625301dd2810bdd2633df82a6d7b82fb77ed3f3cafd0bb3 ee25202e7cf84fbbc04478443fa99b6a8202825552b7239fbce8205ccca0b480 8f3e46ca8c910a9c49686cb49ceeaf1371a0ec65519aa8c4a561539f74e165c0 1adf76664f505d060cabc5d077139990dccee835ee0b83c3459de8f891643c00 a9de8d07a851583b2af10b5f98f606428df1d01a3fc86a5dcdba747f1247742c 1b7ba0134b187da55a4cc412707fca661043c5d33cfe096f070717e7fa59fb44 bc11ffcd0e196c86d6a2300ac3377b3d13cf37495d88c76ddd6cfe91e695c19a 10a57d6811be1637f2aabdae82c4c4a2425f0a9873cae6638973f6911ea99984 7bc056509a02b9893b58d09172fc92649eb62378de9573e453671e381a92fdff 7e690d4574f361687084dbb49f6cfdd2af905abec3c65a1caef1c5969e08a91d 7390d3cce7b73bdcad7b8c9ca7887041ffee32eeec0d4fb6d710d097f197c69f 52c15775a94c61d71dd1001862105669a6bbe3a464a4d006b6011e194e555dd8 d1396b56d964e49a6218884e55669b72661aebc32781b99e15345ad0eb04220e 503590944d3f6a4eee2dcd2e5e2174526f630df4b9aa3ab9acec5c0fb7c97ee7 bed70fccf551ee2e714a1b20db4254a8b8073d1764582ce79e3649d927131d3e caffe870c8f8a47028a6a0c4441b6f8628b201a6070fde5be5ff69650fdb066e d1b857e4b34d1b8ed9b2fa8588ae75427a33fc8c3ee2e11f46117f2b292aa816 39e67d41dd5aa3cb3aeeb86399221eb431a2910d7773c75d3bd0909aa716d84d 03a85d21d3f68536781bd500394b63e8db03d5255acc58519900885d16732bae 98f4cf51c1884f0629cb91e8cbbc3d384eee9a001ed225b1029ed9006ee2d056 6a0dc1e26e5974bceb2944b1297dd4251869008211c811af4ae8de93234068a6 72dffc83fd33e331e45e4c84b61e25eb498adb3c3e7557a4a455923bb10e5591 7fb251b2075dbf221f1060dd0dc8c47377d8917b3e124241ab6d03caa6d3d107b503c98eac1786a1ad717fceacdb45c3ee97bc273b0b44a8ba53034391834306a190c66fde42a5a06e7689142b4babc92b349644c14fc900f5a1671bdffc1c070b0cd583f1e3b1ff4cdd044ba9460ffa30965a9f5405287ca9c55e8e2d3bb908c01e92371fc0cd0fbb35dcac54da563edc73025a9dadf7731a1ee22438476406587148a366d75da1f0636f8f8de2a8bf44ea4c0fcee8841c51ee3c22e1581e05362d840399be0c8d983c124881accff33c63818457195572f89049647ee2e300851d187065cca44e20da0a159ddf6c13542f81dbb78c8e9d64fc38e7d6de990dd1cfdb7d69c3937e418050df1a932a59bfa3a8fabb387128740509a59853f20b1c5cd4598474321babd1cad3e5847be0b6ab125ac584adee8c2c22b7cff0a1036f4de5e02f4b0c612b0fac6db15dc499dbbae81959c9ec4b6f3c200dacad8b0ada5416bebb2208cb60209a24262732b98f48c7a3bbbb4d65db1941d0d90f0e0436dc1cb1c6493ff5e2d0c9cdebdb690fcfbca4967291ff1d146312483ad67f0431c3538e2099c0f8d478f053f48376c974c9b739ed46359559b7c9156d46170408c784f1daddb03fd320897c19ab564f48c6a63882281f8b7fe1443eb1bcc600a413cf7783ec932bed29f602af4db39512db53f09ed330816b17437e67167c062b3d6adc5721d9d032b950253df324828b2764c3e30ba49403560c634f216601f9ca1dbfb83244885eeb297483f34963d2b4ee0e03d89148faa2586842cb080375f662f8c3560adafe0447c8cd78d9676483d808ef800ff60b5cc186d97f2c07b9346aacfae332f16f1df3eed9d77b14d041a66ab60c073b9ed7092fe10f090f072f52bbc9e98f69c721deb357aee377a7a9fbf37f7638b9cd2cd92c319bfb044cb9aef552c7b288cd9d1918ef705cced8f97487d0dde3c55a86bec83119090adfc253baa3a04d88a99a3ac80ae0279f8792832f52b5b64493be252d73949408ee357c3c480b028f264fb8d513d487000838851552ae93e017fc31bcc6f7270446373ee1c4b2463bb6329a0f419ec1703fe7bd373052873d7df62b1be9f6de0618bfa45d459d51a34fdbcdb55bdaae36f143a5896991d03609240ff37e0f2b00ec10bfcc5e9cc57bae9c8aabc11c23f34ae4fdf52fe0c37251d87f579dd098062ca87da4b8bd1719ace56c018391e818593a471e65ef262816ca3f8f207ea808b5ceab23298eef699e964571fb8a65d9a841958c346d27c6ad7a904158b70f0c12c343d220fcdb324bfd7e6fe9244070148af9ac766b2225934fcba9b0a53c0cd74745cdac320278f1ca3a50ef33b184c10e061fb7818585e8407f53e74d1309bc9d64ae09ed9fdd92a518cb2b38a7ec8a53355ad53c2fe053d23c2bc124b902d70b3b32194a037be9c6bf722f3d7271d5795e504f354410ca78442d9d89750c66f2679d730af1eaced7280af869d4dc081e78f854ba60962c033c28abf13d00cb2933bdecaada2c129bdde64c8092eac9ab19f80a9e8dc16696e67185f0b50cec2474acb76c71e4f4540aeac9b0c5acfd2f35b3bbdf1780be4bf0b611c2d004673f741dd096314260c7b7f0a3cc22001e1af5059fe80d521c0a882e7d6deb0bccc55fd4d5caab4982c53259a6ad5435422652795d8ad31370d414c0b13a4909e9063f36094f2450e8891467c44820842e9db9ce4032441106d43a232312eb0337dfa2cf1aab863c8d693b29da03e04f390f0c4de65077e5e5e9f99f4a97500818b0aa64547c14a241c4928776849af7610e441dcec22ab94522a3c3488c21078cb7daeae51952ff5851b93b0605ac791d72156af694cba05562725f0e5ce900436b57b5eebca92377cc081bf8aa669e2b4cd4b67a8e5e1450d2db180c3abc09ac63ef22a55d86dc206b151d403ea217f542c94c2275c875389b20eebdc56801d38facff94408ee8072ec0aefbfe344b8c1a0fa1f2f4952bff15aa7c948810062c2738ba6466039b9a9a971d896a99e80346729f7ad660a36324a198f7a47303b734240932280a8c77b52ad68bbeaa6899a5069d0fbce145f0f424d389a97b0df7ccc02c84be2b296b325eb5eaa4fb9d2b36fed89680e92b0d10bdbb85894f08dcc6985809081a9ad83fae15df7ddb98c5c062c02aca08cb0ae3bc505ecf79088dde4bbbf644b8c060ff33f723dffe6c8d73f7978a037a2611144e54ea29be08b8a416b8ec93e6a517efb3a892f189506dc2b1e42a3dfe3c8f0ad4273d81980661e5ba23d3ec98963c42e9a8042dfe22ef32b56e1ba9749c63036fb200798e07110151cdfc563a8013acfc6086fcbe21e775470513ed84d44bc81c7f3e86780281b54455706752c4c529759191ec13ab57715569014d62162dbec12c41167308ca62cec5d69e33cf9da35c49c72cf3b4c52b2fd7b09c212959fc04f6a6b5f40703c0981d5cd8d766853fbb01adfabdd1f6533f002c3c8bcdec92514b7af77a0c8a9599b5f1b802df48e6b6b40e668a99951400f4fd20f5eeb53bf47006a3970173df395011c5bc9d53b2ad79cc4668730cce1ca694727b11265d00479ae1c608169d7fa6eec951956e440852ba02f8d5cd7e391c926c4c9ff3bdf252667f080a1f80ba0b192ec2f14d08f845c3057aa8f06b183fb86fcb254996719ab1c921017ffbe758250d46e97977c1c097ab5fd83a830ef3a884cd1734e2d0428b1d790f4bad960b3b3f867ee3673ee38edd224a2c08447202691b68b0115517a879980e false +check_ring_signature 36698840300e2a9fbe434718873b5b1db51f139436bf269007e76cb0294f0b4d f910a515c1ccf164a4766a516613f6ac02f824e01096d13bd277a1bf0b0828fc 123 ca6dd3ae9610af2ff41ee79064b99dc37ced71e1847fb9e32b015c122d1e89c2 6d3aea700ebd545cdc35d81317620e0fdfe303d8956b890a66f8c94872d3f47d e8853bfd952dddd8be1040538cc17a25b5db7a33a2490d7997bfa14f9f7f1e1f 991ce56bf4aa50be11a4d36816719ea9087c7551feaac6e4a25fb15ae9e3e12b f72e697bd6acd8466fbf7e94dbbf65f50d840fc8d4f5783f7d367ec6526103d9 d40ffc5f69520830411b2ae37ebef8db250ee6c621e17403c50d28062d048365 565280cc737ee61094a08529d20ebbc0c60d1b45f0d4bebec5022a3346366361 8cd3f5215d666b8a0a1d85e6cdcb0a5c10fa3581e2f225f2f3e828cbc0330223 e46eccc66157f32dd1e2428d8cdd1eca1077dce88e4f2f1c9ddf4a5c204a57c0 36b3503236a4bf8b0db17beed16d1f545544f36c5b6dad14bb1ad505588e642b af72169e94f2334b3af52fcfe9bc60bb1b82c8dad96ce366712c8b2c302d8537 a69f3dbf8b17c8a212d3d45c62395df100c083d3b30f7a611d56525043bb5c85 931408795917732eddb4c1abdad9d644e54bc7a1fdb3046e44a28017127ee8a2 ecb9bf03789eaa926f3ddafeb7bb7741de70b657f57ba01673b2e243130ef3c6 7a8cc13f4e92c782c1a12473b8913027768bec096299089bc9994da6844c6341 b826ac54b41b616792404d3bcee9c4764675ca32dc74bcc34050f377a8d90b95 8a48aabcfa6344a19a25279bf063ac6bfc67955bd8cc9fbf3b577442e3bc1b4b 3be1959e00724e57f1d04ec7351d91a4068f6dca6ec525b370b0f2babd4dfced d2db50d37d05115e3d557aa57bcf27ed7463f968428b854bd6c225ed4bb602b3 f6e8ba773bde22051140ca0efac1afeffde9d937be5ebfbb98fa13ace598c608 3d31ab50e199d8f141288be50d83fd3fe5b92061b0165647b73402843444ab83 fb182aa2f2e4639bfa7d458100d9026cb4498cf3a642bcc2cfc5cfd583efaddf 4917b54fd4068abe5dc9bca6b14cdd3a3a539574dd8915a401d7856e79ac7593 94d97f787c4b6a67f5eea5982dfcab0d2ecda02207a60e617952eaf502e034b2 4e76fce0439d696aff2cbb81c4408536736bfcc805b073711e39d39d3b0f0932 2efbfda7c3624cc1c168a90372ff605e9088272e8301e2560ab07a7740c48f40 ad837590d4a31a74f68be2b4737bb57ba04b9375769c16f2bfa788caed41c508 5f51f3510ee3e6eb7f742c500632c4312ac113e75b525bb709e7c1e664b7a12c dd52b22e8fad94980b5873f21d47f7909aa343d5bd1db951a3947730c282ca0d 079cbf13b95d9cd79be75066edd424f7548c1735c6a28d09f673c67377b18b75 a574b4017d211705120348a7df41b61cc98fa155fabc938389f6367efcbd0e59 41f927d3e57bd72851a742b3e572ba6c60dd9b90c4a227552cc9bbb1359033fc eed1d67ce408c201877afd30f7e3510ca763199d04b5cea3d85658b94f539794 b8f33f968b1bcebf19180b6eb58b0a9c9b8bf05b0e4c6d847d2ccd6d916a6f1c 2bd3cdd28ef2adde0c72819a532f0c492b0160cea93b0a46a67162b97f1893ec b765c145e5d542daec64f354e589171250d876f058e29d29b7286e739e6526ab 65adbb4c8a75dd98b656f8355e922d085086a5f94286af62fb94646348f5b5ab 352cdd382d2877ed6453a3fccd8f04e7e7e75e9131b97d2ad0d4b1e017a5d4cf 6447067903dab9744576ae8f4ada98bae262539c7581d7db171501afb777d40a ff88151d85167098fba3e4929aaf2f5493b51afdf560082bbe99c8798ac59445 8975b314659042ca2ee936efeed3c0662828b5b4b093c11156080b38f3cf7dfb 1dd2d28d586e6641ec03be53305e8ad22dbea22e517e6e1f67e8b77aad92e832 0e8869bfe36a3c14e8ec01692be7c5266f1e92bd6a19716279c82de5e5db9e35 94ac22b1f0e68f63c03c686e521d3541c4bc2b8096e808023e060abdd19c1e78 1b1966ff5eb79c4062dae7df6448e421bbcbcf1d8461154bdd8edaa078cb9a57 8c627826eacb9b65cd49c2063ab63bdc4caec8ec39e3efecb69f1f58b9999cbc f78be5de483cbf5148971157af7e0162414ceb7a78d7de76112225458d7f9c26 7f468a68b5ad1e798603be856550d65c6fc7e0a9577423248b3b4a0cf1a30e57 92928041eeea08d02e44d124214b8dd5efe0a54e1d71b04aa27af204001bd8a0 e7369549310393b0fe5f69efe610f2ea2ab8558dc12890ceb9d42d9ba8d3c331 ef47f7a305edbd494d6c52f6a3e41aa7b51ef242c1d42040b54ed6578ee430b9 27b690bbbc362679bdc887272e7b9f45ea68ffdc1e3dd89e8b7339faad79dbc5 ec31e2cb00fddea71efdb35950b97b30ccfeb5686999406180793a210eae8077 668643232fc773ab55f50ebfc2c5a993c0bb849c1ccbebdac25a8be5e12e2bad 7641308eb4d8966735651e66da83fd818082cc639faae165ff00d386b7af196e a7e4a067371c000b370810c5266e70a5313c8dd5802c3aea4bd947e3e1fc13db 1be862c585a9eb2e280c28c48a598db970e428533708ec9875c077e2ddca7347 e6bae26e81f01d1898fb2e48bffd7db9d94ef87a40b92ed84cce2d26806fd551 7749e285cbfa4646e105ce11b630cdaa36a39664e1edcee187ce9239d6efbd9d 7077da430f5519791a0c11842c3a3527d1c3d5d110e54da8cb419c72a3b28ae6 b02946503f3825175ff94c832008d9de80de85ee7f13d0f2ada9cf963e1e6af4 c792672751476796658159e4affa789b747272b665ae176a4b04d3f5839b09bc 8d471c1b77c93e738b40e2f71275f11c2e9d76e4f524391f96581168d5264038 0de0decffe5c9ff6ef72cd74cefad01bd41c8499f3d7dcf4e895c8a8b14e257f db2c0d2f2b761c2387d03a90f505a8f2fd2a9a97395d7727c811312284ebae82 ad9c977ba1a5335cb65cc5ef1483bd2a36dd043697927bd8326499427d0cfb8a 94e772ca46c2473fc7e68a7b06fb30c6315ea906635b6c362c9a5fe1c14c4cf9 b86eb53817bd31296a5154c927fb35d292057c62ff94739db650b148ce4271ac 1e022dc5583524f9a4163e5d8e12352bb011892c051a440df116bdf51aefc591 53945244f9702162fc46e6616013dda3b744983c53d3a81e15a7728b7cf6d4a0 290d33c46c43f286b6cbf9c5e4ad3af0fc2c2d2e20243612b36897633ae0de55 6ff915911ad1a03613dbf8f3e249dca422f601de61a447b9ad6c10167d1e103a e940f46f763281bbe4650b98ea961e0be1204db7e975a1d2de9de1fee03acd59 d1bb967691a65df30fad06b167f74aa890653e624f385d3a294272514a3882a2 f7210b5b28ddfec4cf27fcd7de3edc42d41e53d5b5bf9ca78930cca4d5b3239e 066bf1f6b7850afca8d37cdbd4a7d620e0976eeb788423ac2fe17e776d052856 29f6a392ce265997583152fd2c8db89eac40b61e93c3ae8d0a2c635603143c55 df1334fff678f9aaf4829bb7851da2ee821b6d569a8a8c99804785c282a0774b b8dbc13c42cd626d1391375b8a63c3cf0a71ce9d24e568a5416a13486eb31a8e 95025d6da7a342232c783b048e1d40a83ee4b79ec137e917914295f945f01781 79f14427e0dd94433015ccdaca108ff3367e72f540388675c117b325be7991c0 51a46bf0bf31bdca4b5c6e17637041c8b379570963a8f0b0486a8d316ba8b1e7 d357af88fba18001ce8398aa9c6a4a504ed7ee045490cf0e83f1e6c4431bf383 c07df2d8ee2586f10c64cf4ab6adddee7fb4d3bd9d1fa2befbbd9a262eb951dd 232f5c7e7957da7e09fa4240c5941be488ad09a4422d43d9521fe74915a8b6e6 81964a5ed6d5337e589fdccef4295f005659f12909e4c93505d113cebe753cb1 71f525f4690adf220b647de03aac9f023bcac665049c9a1aa70f737007144d6e 00a0fadeb664a89cb31afad65a3ebb3057e003c272880e4a1489413b2867593e e6fa5ea667ec12254de3eafd3d28260d65cb3fe7f5c9490781f0292bc3f209b5 4bd127cb236d192d89c5baebe28936fe6169371026305cb156934156021a221d e07b5885951b55bf0092dbd2c5dad7ff4317e65f73d0c98c5099fe3e06930906 6e09b39bf67a1a3f64092ca4186d188f639b8e31d12dcacf7115f4233f033d1e 55033efb5d86b420c2243a92bf6c00bbce7312a517f76691bb61adf194770350 1b3e3856573ae9971711d1df41fc00c97884f9c02bcdee51e55fb380939d162a fe3eb36ca15ced48bffe9c837149a0bd0df5111be5fff284a1125bb8b20734ff aea57e867d4cc35ba20e84b10e92bab2e45805203b409a4905b5ac5a9e56c073 e729c3b06b44e9d7a1708687724c1a7a1f95e302b8376e4fe416eb62bbef8908 b7c4d57ce759aa39b9b7c2ca863c16cf63df7028be0ccce766021e74f8ec1372 59e0578a1017a60e3bb6923a7f1f92f7b7d679b86c0380bf7f303420bd70217f 67ed1133b7e377a2865198be0dfa37471d662116bf8a0ef0043855edb36e506f bd0064105699479b8d50130b06ae72b7296dbfe40e18f1d8ab5b98cbd9a8972c 155ba7d5d1312703f0e94745720b1b37adc68d9bc57574ad0a6ed1e2e4177ef0 277ff5d186fd3d370afedd08cb1bdc9e944ff504cf741bc86c56b2e8e8994f22 dbbf6792d057fd8645352e8ca3a39708dc6602c8b729c8defb164d6af1456b72 59e527ae109b6fe4aa2a2df4e89929cd064a70973a3b1f5c3e597f58ce19b04a b54d9f794df47037edc5ef1dabdf70969a438646e81670b286cc03cb86b7a06c 094e80634ccd98e04d8892f3725f3f4b7cc100746371a81fd255e2fb5e756b27 8c1a8fe53604fedf73b668a8756b078a503b45c2adf307eaec2d449f4f5058c4 2e005f2596b03e9fbecf2458eb28059239cd0ce8c797abf27043f0d14836b900 23c6c7933acb951d144c63ead174885ba8a81ab6c5b097db3eafa5cdc09956d4 70b63aeebac99ce19b1e8ca170435b775e9fcac039479a1f6d45c60484eb89eb 2346d940798a9dd1f48386b2d7d1bbefe73daa7dc82bcfe618c76ef9d9481d7d 388a2731abb8fd2f8205b53dc746d628d3a2d92ff3a723c02233a8e1922f5185 049d2bd55e01014e73ce7bbf184e62df036ffa9b660b558703eac96b9f4cd03d 3b52b71e321afe7d2350b686dfc3fab85e31fbe8e8b2fc0dfa3a69c7944c5421 cc983d2e22e7fe489d8965f041bef4987e5cf398057c862e370a9d2297401daf cc4176807c51ad02c75ffa5dc973f947d689929dd7ff18206224c326ddd3f849 b4a3e603c00c39d0d35170dc4c78fd4f457b6db74cd1ef40adcf9c85e0a9a249 3058a9e4125df0573dff6958a158ba473324622edd728f303a768793939999bc 8496564ce4e0134a5717b5438cdb61649cbdc4504d690190e826eb9e13504edd d147866e99d3005552a1e9d2efabd6b807ff6bce184ee4c0d8d8fbb24cd296d0 4c99ac85b41788d42eeafdce77892e99433c9fddd2a62e35aed77eb0aab8a775 fdb8e5803e6c3017c873a49fea115e3930fe2a41c4556355da7b990c7de3ddb9 97dea1b011219de268fd80fc575ad3b9a9e1ba9f308785cd6c89b244f708a3061294b6dd6a632034d574218799c137eb32b38b63d018c886b057f547778d530bb461761da4bdfd105a4a9f0b08d9a61887071085e60dc8aba3ea9a696984260d2d9a7f7d494aeca04c30699932a5a928ad87f439c8be33f81075d7c5b5489d00da075529decc55386326e70f8b65233b53d574b667fd7c82ae12201f55482d03b1e106e143601a2d22557cc7e75ff1ac1f95edd96ec00a66e1f3f38b2606e9002faed3e99c83534ae4199cf96080444837eb00e0f1c7310a66a79c2a5b4ba60f3ee9e3275466d01204922dfa8019ecfd999510a49bd6d4ef4e8d9f357884bb0132539711a5963f6b7a2dc4282a04a6a93c2ab7ead50d68046b1a9c339c45c2051764a6684621f84dd47a98988dfa97cfa125532306d7f46b16ab5bb451752c0fe4c95e30094cb908b07a7d0810d67011f5951c5b77461e7614701749b0d9d40b6b2f572ac8016a1fbb7e8253cd136f2f653e7ae96297f269f91ab81694e2f30d95b931074fbf1a4686d4a6691794dcf62de0cba5828fa2ad1f5158b4caea5706160cd735e0640d1d1130da8672e8ee085e66a054f52f7b9ac68c4923475eb30bda8dd0122003327cf727e0c3f4ffc47a3fe5d3157c6777d41d78572044997b039cf39ed2b3ac8eb005ef02999c7fed1f3d50ab1fdbda04a056ff147edcdf1e061c2a289dd996c18bc5afb2bf00a7d75d88e4744fb2c05815748e57b88e57890ae0f1f22747867676b88a314e1b50335a009ca5274713d8428121372627ec670fc45068d7cf3ce941c62ba80428839049169c1dc2e469e41bc5d94ccf1e2aa20275acc820bd16f13a7244e9aed55ebafb241cc02bebdf6c234b625afcbe0e87046d7db7ae852cc0d935487426a8470b73d9f8901442ef2f8c7e42fb8774f83502225124f9db829ae581a4ef3263780cf79b3c9e8095cdf5328c8ac3434746f006b1d59c798204034d4ba240df626eaf8f9d3524a507456451e80d06a4fde0cd051c72d843075c9bf9f2c5ec660a0eb8274485bdc77832dcc4dff2d492fce15b0387e0d92905c6cf92c22051f7c65dae553fb8b097604560754088ddccbdf0220035db4b7b27b5f00f2b4cb5125e8f47508543f165e9c368506b15047c2042f00b6368816f3b355ed707013fa3f3b9aded8d0469146424dbe0f3522ff064eb8605cfc209b2f4bbcc6dacc578638c75048ab4904c0b894b679978a64dd62f5dfe00481e4b54089e8b403364769503898eab80fda1321c886bf42f99007dc0a19b07f7ee2856b44494d68392b21f26f96f52abdf6fa2917dd3d335b9ce90c070030f01496c4bc8d4257959b34c6d9c5b5901736b10ac2d60f912b8b4b10e22612f0d5853ef14bce0199a9a4537299ffd89e9e4187f6b70a2d57413be8018da151104715f49a415a12a3921edb4ac01fe26c073fc79060479cdaf7dce629c93b8a90140b3617d7524a317ed027730a5cae3e77bcc81761fe99ef5279f4751bdcecb0438909b02c0f3d78dd256af88858db914785ae1758854cd0266bb8089f9e43d017ebe97876ea0c700613154d88602ec5ba68ac8ff85a485f5dad63bb84f31550bf5a982376283f420aa36f923d09052972d22a1ab5ef86e59b065f65dac777b029a507b7c3fd7efbca25e24f00f3e6c153eb98b0e02a67a99624f3c8444801c0fbf77cba4de70bb94e080902828f56be3d1484b55f31db98da4b66bd33d20e30ae4d1ce78fa4de00c41ab15e1e5c55a6c031bf01abd9c94d467b01d2e1e096e077efe4236f1a7a0bfe1d037c0aacad04070df793043f28becd3cfca7772bfd100d41c1c001ec6173ce9a92e3888f059bc49bac80fa94e7e7d9868e821b888a80b83f7f09b369ae00bf80d9e3fb60e3ae1f97df80501b72fe4fbe948280106030ba2973532fc7529f7cc31f0cf974362721b8fe05206ff4945c39fbc9eef71ec07a42bc40aa5dface6ada52eec022ce3003533dc84e6c683d5aa179f6b70a5f4047f24d45b0622e024ec429dbd621bb7659db465f25b4bbd9f8f3a9e5246337c07110a82d7a451d7b432cecc5c5bb5ca1845c1a6e955023bef1dcd6f622e924804ba2927a099f7f978ccfeb0b35ca276c2084705d61b4581767ed34d0221b975086075f3572610c534130ee99841512016f34be80fc16b0bbde87e9e7f93054804a2baf72ab08e8e324c640c0d2f401af1072b7da5bdd2d7f43f52daeac3581a05ea6812526103cb07d116279ab96e58799d4d3ac3d9854e0dd3505f7808fc2c0274c31f38347cf01350dfb105b2d69cc197528ed05ba537e72c834bd0a4e51001b1a03d1c5c57c5205637f533f18beeeb5f44abb27cbbb068fb1a94b31a5891055a81a3d52bd4972371af44cca7d6f130fd4720e439f77dfede7266562afd660405427ba4cb5b3c6f48f4c7348b7ff91788843f1dae34c42d6ae2d0520eb96607fb4bfe0692c95d98d1180c50b4b4306358e42b18417ee95449e89d10fd543d08425fd4f141dca83fda5dbabf66bcaeee928e1b1062486895542a502e563aa609eff522307d55c60f74cde6bcdd95c0f463cc03cc8431353ef9544eb7c7f18b07e12325763b83edc851cfb44d365cc66889af0a1b01bc025f148d5290338804063960961fbfed021ae25967742feaf71c859ed00cf4b2eb1d35f843e68b017f05917e16a1d49cbbf52deb8b537579739653d90e48646eb6911f65a684bce528061cb33de3570d9e16eedaad1ada0fc31d4fe98081089688847e8c20abdeffeb0b17ca5aefa26a7e13d0d0f3675c04623dd73f4f74f95deac3b7b7bcfab94b2b0a10acc1a9cc36c48f8013142e097bf16f1f3828d1bd50ffacb587fd9941a96f0472f2834e8a84d3d632ae16db7563fb1ed9a293a75b7b86664606d3b1a64f1003d721b2bc06f429f0325e7ba1d86767fe089e5beaf9b9026aac9c0a329608cd0df63898afdf7f1e7876c06fa20c595e5c4dfe14be9e73e5077c2dac408ad05a0b770a474d07b7d9e22b44fb0d9bb8615bef2be9ad618b9dcba108f7817f1b2c02c9feb1b00e2282766476b35a9c35d7cc1d48a3f5d22006e045e2aecd240cae0b699738f38a77568f00bf659e5158104c4de37230fd50259b1ca56382ec253204b2c2ec330dcf4e4e79eeaef40f50c2e15d2fa559944d0e309509128fcddb51036a22c7781fcdb703d40a9f020debdf4158a9ce6117df5b39044860a7f5ce9c0731105dce268457e3012afc730ad669d9ead9e69614ad6f5f27733d64ffd996025c5731f68951388f57228a90898b3234e6061960f9822fe2945c78f7913cab036b6e2fe36a511ee4123bf264a23ed4943c00f022fc9d7387252e37087660e60633cd7fbda9221e7bc972c5c2bbca2908e6f2095039286bba4d6bb29f20ff670ac45f1fff570754704fa27e81c6d51d4541b718a2eeb5e8c3d5dba0f4f16b3d08223d19046e9235b3e6bfff31ea2dabb96fc163c1cd46ef00df07354ee1022709637fe13b6f07ea1abf0bc407f4df9f6a5bf8216e6c294672d391fddfbeae060eda931ee441c7a9c15a97d272cc9b3cafe52712ce0bb0735eea1a36c65e523103bf4f60965779569fd177171e7df6ca662f9ef80b381ab492366a86dd3be3760cea2b2228be0f40bab4ef25c2a217d0094ba16143464cfdf5b4125cfab1ab8005c822e5a5c4aec0d71dfa34f2c72335d3795e57a5d4f38a78625ccdae66de53090ab3fbf8a90d520f4469275dae8c1e94d11ade3da32667a653e951697ae558059f9e38fd0dd29da3644458c9d7edb28f867e2912440a7a1b521fa1054506c80b6bdcade24a329da7aeea4c7ec5834abda1807199524de82aae6b54b9a375f40cd09aa0d64f2f7c95f6df201e59fcedb7099c3474846877d909597df3e95faa01f0869c85c435797f2c20563c212b7bdaa7271323ff1127e8b27702138abe44007d1e09606344687471d053b6c0b9d456b2f99276352ddd7b30805ad2edc1670f4dba85d288e3871e4257819f077a72e97eae19cc626ad192fb2da56d70efec08a7a21628de613d275cae380c1a30bf95ceb657aa4b8b8f66d99454ee5b7c37091ee130a3b8e3b12276abd70309697867ecaa3f9eb680f371bdef0d9ac1afbd0ecfd8103032f54e459c2645fb2bc55ce76dd029403f6012510ad6028003e8df0de0006b83c4fcbbcf8eb6a5109fec65931ec1107b17230ac66f215c52e2a84f0dd433430508426f406574646c3572cbcc47b6a1d43536a048e9fb42d69d0a400f46e33178cf37d30a87ec914d46db4977737a79722371aa547be3c514d7fc050e221c160ba145d47cbd975b451fd7e004b954570428bdcbef5b856bfc9eeae705a61bbe91798dbed88cf5d2f9d2565dc5eec92c9fe6c89d5e29b562119ca0a60e527a7b2e14f25e9cba92f456bdd05293a7b82d2840c4eeb38e2a8c4def857f048bf70ed193fa076d1771aa954a2c6f5a8cd9ac31b5ef73335aaf915df7544301e9a823fe69b6bb1b4b1d3867e3735bafdaf6ba01ae11f1ba5f19d776847eae0272ee71d14361b7023976c4870b2bbbcac2df37a7b4db4b46b9c95f51afbc130464e31f8ba7d2ab1e6bc8424753672fc91fecf826c1b5dfa55e90f1f809d982015b2bc2d62b0498930e2e3a509235b9edf74271f25987a554d331cea57bfafc0f192ae0577835f90fa68a70bdea8b183e17a0e07d49896d0b6570505531c1e202f78788544729d11ef152b717877788fb761fc53ac3887338d0a7da0d47c2d206fd70eb3f550ba4b70916d574bdfd41b05e5256b21f92ea319f70f5417de37904a21c10e739072b7d10cf7479d357f9c7d30397390930d51ba8d7599aede31606a56a12ba49dcb71194d4316d59322ee1e8edecbe75f86e5a3479ee97aa8ea90d8073eea55c9cb37f94f5ffc6c9615977c5a4931b473e5d18c8bc65ce827661077c7bb016ebf4c924030e372a89cd49bfa22ee88f42aebef94a214327bb0cb00315970e37533d0f907428add21bb77c6584b17e9f4dd4a65388770d8754c6d70e22d1ee27eeee7b8e480daf26ae2961230f535f66044b2e58d4e4452d5b916f0e3e8d8557d7d36d871b2cc73448818d51f47504c5933e8f9833e1f7054e2f000de6fdcf05fb97dade6c0949236552a39698201f4b2e2c3525af6a74064d140b098ada7fd91c5058fcf4b03b82ddd9ccd666cab4bfae88b50b2b3f2561a698930fd8ac5a03cd87750646fb33c9ba0891e50dd7453b82d634694b660092fa77db04156a6b0bfdfec48242e7b9069a7b90ab976abc3ad7b91f3a6c7f10b26131c209b51b3457cafe8864d2e691a88f5fd3a98c2608107f7b47a189134642765b050543f6b8ab4cbe69da523e179af4cef0fcf9a8fe0a62859415363136a8b4cb0700dee1f999cceb4bfd34b3cdf786a5eb76ff143b09ce27109d70309ce5086972033936d68452ac9c0236defc50752ed47fdc6774565e1ce27f940226c5cb2bb20d1d4cfd0d34f59754ded31ad30a59e03bf14dae0754f767e83142ce794a5dc70baddf5cbf3590e2d5196939c3fd62426eab9ae77091b7692fa65dd6da6f64b10cf7dbaf53c330752c4b62abf95c967937ba9160f0f7d224db98b6428b554cbb064e3907621a1001881af55ea5558151ebbdf60bb29f37fcad3946b8284f14aa0e412b081b2b7ac62714c28f4d0fdd1cfe48e887284be917808d151db1283766011323cdc35dc94733b06db8a93bba273eb61cb6d5b9edf91dcc767b43e5139705998d333513ed8646451a742c565ade4307c74e5eafc4a645f1acdeb5825dae0aaf3019ffad39ec5df13cb6d6357e6710075028536617fb258f30ebd61369a603960019a69279407b4b91b7a35ea5cc5100f083e053dc5a204929c7f6f456790cf451c23b26e3bcca9b193a10f60bcf86523dcad48aec33652ecef131fbf567039e65dd3b8bff0bc61f0308ae74b2665033c4273b91ee92806a3e6e3a24473a01311372b42bae95b5e65b220459b5053238288f446c9937ce80ceef2c87299d020460b9698ebcebfbc2a06959755d1a63114890e67dc43b728cc430bd3cb3690b2edc0458434e8d936a72482e99ff679daae0f1c876c778df57ef35cfd0cc5f057eb22d7929adc86784df1ecf34de2d097b26afaae99bbe4d9484c10c89178e0480c6728e090022e02190ba65cc8b96171aac67f295ef30b2348f222adb8fa70eae9bafbf3c3cf63bee76e67772047bc26c71c5611d8084dadc5ef30653683505bd708fbddb686652e98c0439b3995d596b2bcbffa9ffbb2e86324517c59cbc0ff749dcea6c01c256b4b04d0862a128a12005613d58fe64327ddb8aa61584e500195b8cd5be69cc04c006dc5c24124ca6b487c0c5d716b41dcbc29cdd56d57f0a4ba9055e6cbf481b8d1e79f60320bfea178f37679f8732cdd9f4d1723d4f600abcd21936611de7cb3f00a3bf27c0a40e9f0b52e2dddd67d3092152e754a88701f4b7a798542a9cdcce991ee999696421cd071ac93c5d00ddfeb271c28cc7690d51332f17ec970f9d56ad6684b524e926034025cbeb4fd8ea292cac2c647aeb0e5c67fdae18add0586b9af073186faabdf71081f8eba04feda18e336cb6061b0c1ba8259d987ccfe999765da27b07c380c0f7662e175ac2cb80a14238c5019d0936f5d4abcd5c2124c6c00cd37b2c406bb3d7e4faad9fd8ec4269be605d24f001f3212e8b209b9fc489dcb85cc213d6b655b9e4b018ee396aa321e826c391ea0b3831e35907f97c906cfeaef32cdcfd15f54a4ca40bbc3e958ce394615bdede0431d54e2620dbe04a5a19b089284a30c94b69be586a6389e03e68dc9334c6df0c96aa75c27c7b875866d732bde548ef11cc41f52143494a0e31ad59c06ee07905b05e51974275dc5d968b0f842074a1420d360152bfc7c4fc7c8a77b50aad120c7cf93e54f18a4895c333cf28a883a3ebbba149232b315a1a71dcd88a4fc311074ef2d4d12d9d6f49a6b16bddf28fd6dacab8c4e996bfaaded70148703d9fe6019286e43af793a2e160cf60ddf42b2c4481bba3d09d2ade89733451b1ff9aa30f6afa0dd2cfb820c91ae89e10f69706f695df531480bc5fb15122a6c7513a060ae736d1a21e258c1a971b3596ea02bd20fc9b6745d10ac751a62660a743e4570ac6a98ab41ea5b4e423eaff0ebba1f7dd67c081e41f808dfa7b1a4363056ee6036fc77a915fe7136e73eeeb450df5221ef5a043943f07104dc800b1ce7f928808c58a23bed7672b1fd8b85552e8e4487ed6a39ac86d0717bb18b365fdeb29720f82eb309a0feb14c33d68c4ceeb58ab43e62c5bc4b77b89ed839ada39f7f3fa00a538332c091096029784760f39f5ce1f5714b3cdbdc20cf83f4d0aa313735f0a581bd380494523d07ea2979717156a46a4140119f6349d10ec4fd3da5669100cfeccc1f99dd7f02b3eefbb9c0e7e070c39a3a48b4f7833751781bef2e4d8da0773cf2b7bbf6c9640a1afd8257c518b892b457a9af08224885408d87169345b054b1b3dd4a26fcc00bfd5ae9a94f88336ad9d0cb6a59072c42bc63e2796fef0046e75b1889115f67bccae3767dfa7f7f71ad0a19f7b53cabcba4338ce26e54c05387cd6265f680a7ee8534355c2d930f68f3a4a0b29f7057b1ee13a3f65c75e0292bc06cf38993fb3c285af211588f15e78bc34909fd06c7fd9cd68a17c04930a20d489f372b3600aba701c04a933ecaf1858cbbe01ade9c8ac71f921e4d8e70281d7b37291c4ac0be3256275300b53db87a7a7f8a7ae77f63e9fe93d0592d20f5890ba85b8b71980bd10c0a55b18ad7b9146032dc1b301bd16e09251e3719d013b31ce2fc6a4cc97cbc5c4fdbdb51b7fe659f099771a33aef0662b8834d0280510dd7fd6d0d0624ac9f48f2a3219bc365800caf6eff06d6d6cacab23ae40c30ca923a993a39c269910c7c0a856b4d14657983306b7587409f79d10eeca9fdb054af1bc7787bf7a60ee296a7aba9869d9361f0a30111fc16a6f73cc59c6e0cf8e1c94fc4d03874abbf2be85a88f46e0b0765d796be16168699a5652481437b70dcf429334c0afac99eb8ccb695196b51264ef1fd9f37acc0fd13c3cd59300c408b7b3a59f23b766cf63be3e47aacd186d41c9f69234994cda1515d45f58f5340f99612dffdec010cc047243efd0fd224c3de47d68ec05644c7661bcf48d54e8088c7003a2af4c5007bc11e116b1061b859c75241e779e479421f370054178be0b442e183036d37bb3db4267bd6d9e5d2058118c279e7d0f2f752a5cacfc20c00b6a0c2fb220801c1b5b548921342f770d418f6383add79c55ff1d763d7863900af8444fbb888efddbb9939aa0cad093898666257e0c19a4414153b29c1b7a340fdd4d6fdcde5034edb74a7de408b8ab1b01dd123d408fec9599e0efec22fcc502bf26bb58ffae8d1d8929c62f6810094468054b11c64373a013fc169c89ed750ed8ad6d6525b03fbc8aa6c32ed68fc603ae2e604616f0cf8e507aa2c87c210b0f759402b58adeecd847143700926c36aab7ce22b7a21de238c102b455871f4c0ab59e00679418b559df5df03ba3a93718ba5030b5f8aad3b0e8671cbec0be330b275d11ea35d14e3b076391b2e175c322366aebcdafe883960c26986f1a753107cc0120664d199d097c8fc1e07511c93f170cb7a68d32bff89cfc12f2d26ee70ae1ab46778d813c43efb3c5dcc4a5637e8b7ede3bfe245af2c23dc91c58e557000d07b37c531aa1b0a3fa2f5b3357fd331255e15a83fd88aa45968ae8d55c9c0d750c7aa2d2e54d3c6500eb36bb34bd64de0c5ec7ca30cbfd0579ef27ce2fb50408ec005d45b39df7780e7dc39dc2e3c26be42b55e314734f96373f25bc73960f08e9f8f64a5ae2af4ba7ef9f68fe32f874ac54ac0d8263d3111f433f5139ad0628313fcdc8a7b3d74b7c0c7055745faf46419cba6c08390425d9c2e2909bd00cdf2a9e4e2da294840855599da45c840b0817eed48e47c273402199a7e9219c0f079119679eed71dfec4cd0ffb686882e2d28f40f3922c05b877fa2134b78640449eca45e5c743668f6c67b290996120556519f9e09d47bb607cd8ea9f3da6106d21eb3abe093a5ea7e84aaf4a7ee274ae114974439fc8419db7368068059a60cb99089d662e50e158a27f170ed0948cd5ba433d062cfbdc66067cd3216ef3f0e426eb89eb7b05a505b300375a8883bae51855dfc4e1579da5ab53eca4940d2027a307e40adad10f06eb0169fad0cacaadb2d493ca6037f89e58c4fc1c756ed0f0397cbd982e98b92fdd350662e7f9972e25bf85b802aeab7898bd9e455ec98047bb041eea1bcd547a82e2ab1f002044dec469d52d1d7ae5b9980606a7f6c2d06594f51b2157ab12aa6b0f942082981a20d6cc56f21b0e633c3e4178600a3e406649358fd8d4bd63f20e4ffad760d52f5e70f9c88495d3bcad7534e4c5cd8760abb74b550a66fb6435c518c44273d19ae3ac00207cdaa4b27b5925e81f178df0df0d1811e1ff9759b9d8bb2d9ac9052afe86a52ba37b7005e5bd4f852bcd9a20662d9042a6f5ac38fc4e5d8dc65dae773e81aacab7197cc4365baef72b56fb301c90319fde1f6f78cc717fb19be65bc80a2acd225b809b8d57c933292aae8cf0c41f4d6f32ebd4bfb3e4842f68735eb96bb9cfe3afb3a0205970a6d9a999fe80565350dd0b243c0f7d14a0dd5007fb1fb0331c4d30742efafad130ef1a9a98801691fae65e3244eb4266012873aa9688fc74c5677383a807e952b7efeff96c3079b35d25dd0bbd089b1d601b1dde4786eb713de086a62c2ebf22d5ef58776610a9e4fb28e2a2121156a165d85e133c1353c15f4404f54e56aecd2289ddf48920f4a4b78f95963d562426e34b68763c6454acaca45b6437e06b231be650ed7e30f2b9787e46bd019b155a0559de5ef4fe38997973f9ddc07e16ed0a0be951f9f0a30c37fe2290b9b11b7eae00ea5e412fd5746df71af440274fbb632a30f7c87085834b39168f4f943cddc92e894d4712c27cd4085092b2f7c3c1d1294f9806703f06228d56fcf83ff8c32db9be2be6b3951a10a0fa034c90b7ce13e0ad67ab60be663c7d17d2c5a4722b7ac2309e3f1e1979b8bc4633c7d359b4c6f834862f7084d4924a82fbea9fe13936ea1804c185273ccad58c4879f3c1acfeacba3e5ce07155c05fe7e6a27fdf19119a9333148e4be8717b8be6ebb3a2bd9fb1ef7f29e0664186d7b86a47cf64a9b13244aefd707320a6c20c8ef7c9db741b8b7f4980603071350df6c7580aff042615db9d9157094d884ea8960b23c9be80241691b2508f29b48415ddcdbf3242e0a83600600171588c8c68b44e471fa762d5de869420c64e06607b297ad15903ff0d3cba58820e8b29ee1159d17131546712b7336900656f55cc0ff23b360dbea1ef646f6102ecaa0e6ee67a3537e1bedcea6862c290d1e91614634ad4df9fc3dd0457441516e0942a455a5d067d4d157d7d073b07e04619a8986555d78cfa7301367077d591115688fe0045628e77666e915aaf1930774a129d1c0a50aaefe5d7becf7da99b9b23331af3f6e626ae596c8cd461d06041fd507f544a88cee4299699e0f2151bf670427c9324156f1066db635fe696406a08c94c356b35f5bf6bd6d984d6c9499c95b0ca0a131a996a54ac4b8e04e1709e9e39de524a5e7dd0384e4ce537ec2ec9566e1c341166657dea43d357b30ef01953ccb7419972aebb0bdab8694e14903e09634d768d9e3ac081d6ee656580400240577aa801e9733ff5e15ca0e99bea3604c7d62d981a6691e00d67003b2240c72ea6264a4dba5c25c056d3e2b409ebe2e45356f1c858993b43f46167318cb098245239743575d2dbbfcb65606db71834cc81223fc5555cae03139b80f1ac701b094b97ae9e29d9fcfed5c7403e13ba89cf36ca2ff755c5941e91b823c37b30aaf4544f32f9706782699df705079f206b15c3df127e322eef626414ece74650a800b388e0562e392a213a361e236b5466aa1416c7d5887d2c12a285f2545a10bdde6cc931f7c7190e0900a765738ea2131af27bf26e9e6f8a33ac58e669b4c06 false +check_ring_signature bd40188a423e61bd3445d2d47ac64765b3289fe64af19387909f697d94cc3b03 f99631f187246bb4612ec331dfe2dc7754327ae4e356df8c13d96f2246b2c000 30 23e59529743cf1ac4a900e3af6b8a2c569148a07b94665ee42853f16445e11ba 5dc616ae4c080c922dbd9951605d9e0e6e7a0c4af7a30eccf56a4e81bd30b15f 81bcf7a0095a30dbedbef805e78318a4f3f94ccd7084251e636b645cd8cca180 c1aa43cf003b4f03a36a9d50a83b47a0573e8058ac0fde8c5f2c9fd5cc33928c 8506de801df9faffbf0ce26d760d4ad0107ba69a61ed643f71e057be3fc90821 b526bc7f591ad0a83921025f0d3ffd5cb2a027d70e8d89990cae523467ddabb4 2aec3f6e9cffe5f524e510931637c488978701be36d9aaa451ddf218ee092c9f 038e0370c609372b9f1b775217b55f1fc3c96e92424956d6c3b717eeeae03b74 42ede5266220e06b42fe2e8187e1c4c6d3e5d081fb13381f501850ec89fe11a7 6a305b6d5782744f1c61550bfe095a4df8f9b4f0b7a5f8cc55755c6574674476 82302383c7b2c2bde204a1afa500443a0f291b0b8850df7da8363390a57f3a7c 244c777f348d140c088a7bbc008c486025f9dd9e92e11d9fbd189758eedb886f 790b0e2bcc5010f4cd2246557fae713aef13ba9648261edb7b4959f8490dc394 35328c8e2c116b537a23220776f8e7f300af373c3438ed51a0dee7856d97a92b 9a91bfb0115a387dbc713b6cbb4bc970eaa486814aabc564c0021ea81cbf9b09 3033d30928d143b7dee00ab86fc389b8607850fd2ce7ef6b182d4e343e9fd314 db987539f7ed649bd95e8daef8e85107cca0e3f857cac1559aa67316679e8f45 cb9ff937278c0b2fb6858f7ae10527ba9d97abdd46b23437454d060899790390 ab96f032e882b08e93aa08382a936189e37eddf6b0b2b5165e368330e4ebb4b5 32e875224595e71206d1b1ef80a1f44c0f74625569cedb70bdb9218236541b78 372945ecb3f8166661d46b620206c0e345a48eb0fdaf3fc1baa15129a6e6cc94 31593c1d9aa33839719c3ba90d0c03004e0987e47658be335b0ca89a74fa039b 1ebe79d06ae79d80a91a58b1d1e8ca4a645c1682bfb2c2007ef87ed686aeaadc 877afb27036a8644f779c23652248be945dc1a191f0e98dd0dd21d34191984a7 840f872a9ab203335b7c45edc151990b67e3afef52e9d7aefe92fa9657fd522e 81745fae250ab87a14dd366a10e7aad41402b3c6fe19c9a80a93ae1c02ab506c 957555a4f37025d89b35eba619a267996cd391fa90958454e42529c1cdcc0363 4e8cbe3d2ac5394395755a2863184a784d2db16498206619c27bbad4746728a1 38751f85ccbef047fffc7437a007b229646fb5f2bdacb5be7c4a75658a403ed3 bcaadf207c88d78f84fba35e0952c6efd995e085edb1a40f7ed2e1ef51fd89e6 380a0462e0e9be941f4506174b855ec0c3ec3d36d2c8e36058955fadde0f8c01cea40b98e0be8223f9707c16c5a5bc03b28623a02e6e4b6c8dd0bb155d27100d433f38ef774323f1e37a455d932400d29ce2df177602693c39bdc82244524b08bcc23be7a37e8b5d95b091d1fb27906de89048c10fc011954ff8886f1ac30207a6740272629da52a4650ad0682b4b3226a252f7006bca114df29008b1378910552622f271dcad51c2b6e8694bd07c278d128a98a1b79e5a43a7cbff6e0296b08e7d843b26900567de237f07962633403d0728ca4eb184041985b4e8d09a0f7019b8e66da3ddc40811dfb4f30be6a121d7adae3ad98c23de1dfc600df462752082f813f61f49eb30896118157babb125d01d6121e70315b287a6fe2fddcd5490d0588aadc8ba76b106cc25f162ba75194c10e210e6ba73cb6a485273688684c0b5824d78944fa787bae31a518c90842c0f2f0ff3ce178863d7dda290d1fe2bd0c4d4ff7d7d8659a7db7ec4d730176851a1802b044ea132103ae630bdc8ce8b60e3de1a5ff34753665fccc8f8f4393c18dcab43becd2291668686e38381e86710a1762e9cada49e745d073df046a01b3d2a102bb6e09d40d1a895142bcf9307c0a93fab06db35cf6b3e47bb151b42c6371adbcc30e8135d7848857b67c3957e809535e8a834b8013060eec039a9094b9e41211741c2ffcddf694b8ad6e562a4c08a1200f3f10284dc8f6bd43ff7743a270d3bb3c1dc1d5ac9265ac33b59df9b50a87f31ad453b74a46e9583e14282414f8fa5eb807aa33793e866a6f79ec793809d27380857914cb0d9e8230d8ad6804d96094d3f01b535e62edf4a03c4967830ad12ad84cf5d6e78bfe7f620a43e3c55a4e6c97a98855d0124a56659da061730aa20e677c7fffa397718dfa728b59345a12565252062adb55ce3fde4633d73f0f0ddc30f1fad9ad72e20355bd3bc9e928bd21f27dc2ffabc4a0f4ad09a3670d010241e42b513de5ae8b429625be1272f00946fac75866927dd4cee8405501b10764fcc2f1ab707438721d3472dd5940b4e5e4719c9d3b7a09d83fdd131defde08a3114b51002d0333c5cffd3805a67269798073c73439862710475e2b4468ed029e549ccbd768a105b981999c27b8d4e7fbbce3ac21ca9d2874147a4799cb8801aa28425c272b89b3d21cb553962a68320cef2416ca9fa5376a44f2a20152c4065743b9be0b13a752f2c3721da1b92734e355bb1256f71758f7d5c666fc106207ac52182ebfb0ffb2a9c869f7921cc702be01be82c6e492f28f91688318151e071d62080d63306ea5c7534083aa018cede72f05adbbb2107317feb8cd3f091d0cdf37860834c8585e38227773e4f616a1aef84554a9330be729282b60090cd30a86b22ab79458d1e060e5f2d16006360f55d51eb89c6b8d281546bc980c976502f8b8e79b90c033d5ea85e7ca45bee20501ee89e0e0175f0d8a1226a480a66c0ce05dd315f407876752f6fc3d5199d22db58bb1542df0ff8ee2b868548a956f070733e3498144ee32c00b7cbf5f78d56662ac25ce2f7369e8b02c69e9eef96b09274d9c61bf8fde6991ae4dbe0d11bc36deb200b08e4bbb05d3d4ec8958c13500b0328928617c2e4e404df78679e3f09c42bb9e92a1a923ef54c487e149490e0a075d1c2107e831ed3588cd8a6ba5560bf2874ceb629680bdd682bd274c7e08069ccd519e02e8d7dbeb8ccb58c7987c101e22e681b27f4cc5ec7a69121b46140db8e595eb439a91469bab2ec78cae31928d8206335bdc2cb7eff3e46d08b1d400f4fcdb0a7d1525956f2012f8468338119f386acb276f74a2463b416707f1e5014f4ea3af8df2fa1d11d3d7659edfc385e36a860ed1a571d6eaf5e2a585d26c0884352010062f648c4397ccafe67df856a8278d255d58090a33875ab7348f320d65aabe90bbd3508a4cf13d9cee92248257ac1727fdd83f7060d7a248c135fd06ad8db085f2035eaba82304c6518c3750f24f95974623c64b783cb74138d74801da2c548ada3ab31057952d96a04f588972085ca327d466d42ce0cdd8f892b0033c915b3935e981da71336526ed58768d9d5224db15241e4f21543b3be9b3110ab7e6d89f89dacaa0825319efcb7ed3097a75119b1af7ebcab2d9788d8ed9410a765d000ce3a42f49ce26dc98a095961e9e1e38ef7e7db1d14d80a5ff0ef28d0f99767961bb135daadfbee528a9b852d02dc1cdfd03924595890d5fb092b066a6722688f1d9c0004f1821f57ea0415b9326297f3c1883e199dd519e610401fc06a7ec336bb6025e7d6d5a9f82af4167052607ab368cef15cbc54241a41852f809e3685b6007598ad2f22d504ee0fc51f68bd98350c5f4c36ec3cf479d477de2070d0b9939e53f32f7060bd5ccba92750c819799740310810508bdf8ee8d67f6076756c76175e0a6bb993d1d75f011f0bb33771305b3f31079b1ef6d53c41e47017952d62e910e254a3bdc9ef3c5a502f86f1297617b44e2ba5f9d882aa0b3ca0bce41df28619e4eb1f3fdabf9d7f6b7f3d87228331db6e976fdd3e1d7f75445097f838825f258eb1a8330f04a4404fe529f7e3e8972df0bcfaa23125cf8b6b607bfb660139b94032d3f736062be1df961d1677b89b5628ba9e09678624136e90d3b004240bca9da24d29e12a613be47e4ef0ef0acb88f6631e57771b88f768a05 false +check_ring_signature d7a1bb24f5db65ca3687c84b7229a60af7a148428fbab83772ea04684623a144 514826138a2ca0467c814aea6fae52a5513ec209dced1dbbbeac6f7a3847e315 4 3950ec75b260e7d0bf7d20cd8ef02b733bea743d46d0a457f368d84b85970a8d 8afde111cbbe8b0e35ae1ba92fe072b625f21cbce2afab22dbbe435843328572 4201197279e0788f6c6db9ca0b600ec4ace8b35f9c33a08c193701d9ab1bbb62 dded27946a7971cdceb4aea0691c359c2cb45dd9e70fd0ccb240f1934e9b0905 52108cd48f0e77d88bf1252b4a06b01dc3ea5768339d459c219fc37b1c703e04a5036a805ed9a428250b8282eaacc6c8aa108e7dd7e34209883412a8d792380f8c5bdfb5468feeb99f47219d5e67b88a03beb42af866df8a6a679fca32df0902fbecd6c4ecb0244586ddf53e3d61f1b8954db84a68cc42e942df3cbd43cd5301a790b88114be0e435d9d364476738d77e37092ab8133af6b9d4c4b085559451daa7e50f777a7ec408c9fd3cde2a8c3230377de459ea8346bb434477ea47b170ee18701a0ab0bdd912050d16937833b3193d98db64b4e3696e6f4cd5a3a24be3196c4bc06a6043971d89b2739dc2f2dfac8bd7b44bcd4c9cd313f8bbd5f099b03 false +check_ring_signature 4e3e8b5768cddf1b54c28b6cc8962d9751acace7057c0b5da90b7cca9ad82adf d3c1ac4208090ff377504986b0c4e6884d61a941fdfd332bf52a570670b6402e 3 b37c0bedaa2c348ed2f314792e46bcb6164cee2a2a3a969afd2379ece05765d9 4d06005434f336cced3a3c5cd692fa9d10b6a2b22912b5f2bd970d42e8628e8d 4c46c2444cab7dbc7287f44c65d514609dceeb05bfecc42cb5fa8a39546f8f55 58bd309f89de469b5950e66de5be714af5ca8cb6c546986b8da15609dd4bb306d614d150f8a1ad415a6173e2f0f2fb56b173b5d54c6214723a5025f1b6b9e30e604dac25295780d5b11c8e0168d33c2ca00e7313d9e91da4864229bad3d59d0af975ef6c3c7a919c442d7bb63698b902c4e19416c4315b09bb5f510e2166b00e316eced9f5fbc6272e7d5e6a09be842da694ac9786bf44281efe9947efdd28068ce7a521eb60a22c3b45201685743adbff6b546135fb55bfb9a038075387da0d false +check_ring_signature 39723564f7add6ed173ded9138cc80d03ecff79fffb276ed3e7f6d19266e961f 0677b1fa927104795126a178d07c2e67e67e3da96595b6644dbb8dbb8be6a9d9 2 22961d9f9713f5e5a4876df90f1c35afb7f6029ba4da0f4198dc2ebc68d022b3 8cca590d941950219517cf87d8910478228fe3ba70c5fa36fae3b3cd7e32b67b d70b67f88c54d68f8cabcdb71e7319acdbd504eb12c26352484a46390bcfa207240da34462d1ea79e07a4df7914033760e029607d1f908c6a2f91be0322b340a7a4f98b60bee66ba495c63ec848e304813d00bd6c58852d6603ee804efb6070b997f976d82b5d6cc4b242bea211aa52875fca92f8b43ba61e1e678c60d0f9406 true +check_ring_signature f83d96a086f7dcb57633f2195c0ea0e03c2088952a74889b251b39ecf48daaf0 c848d46337c7760ae9a1338a2d5e7d2b5809a88f089eb5464a10ed689c5f12f8 4 d549c74ce06589ce64d4501484b8b9e20128c11dbf0f7d7b76999943890cb6d1 c7dcf47cf313cb6347c503223cd3995b5d731722ab118a11f83201d6d0311d4f 4625d934d46e0512a17bd3107b0193f3f7a0fad40c910cfb7271c374be3af981 a71427b6d2330b4b82221f816da833deb771819ed4ffd92aaaef7b18eebd6a23 1317aa51dc0f09dd2d189fa525abd9b7076bceac68668918f6464b03272a8a0c51c87d701357158b42f66a87902e7480e98f4de3440d323f75c70f369fe01f04ca20cf8b4ec0e693980b74e73d8481c13c97fada61db776fb052e112529a34085da2a5756978e0d210ce8406ece7f4ef2eca49f026c2463d7e522ed02685200988360e624c7b03e27b677b287290a310a48608361b3a1327d8acadaeb7806c08ce83b980373f69ccb471860cc17c1f111d076f765558998a3dee8291dab6890824907c3e4177168a6da5913b3f2b0ba4c78acc3d68382bd17f43701b5316e20da2a8d35ef01fd8f2ab19cc3f6c726259c4106ba6a4a8b058dc447b37ab983000 false +check_ring_signature 1c83acb3e5cd4eeb5673ef69144ac095beb5416fbd13a11a963df9196e1881cf 4f61b736d52ab21e98d95f1746aea8e327ee303a8ff6b1eca4e23da513cf5e44 19 88376bc580a17580c12c7974283eb304c83907462ad3d1dd0df3038b63ca2835 800db3f2a8f3304341db35ea0a40df5a0b2a6d27dbc5a0abf50a6abdf84bc6da 28eec93e0f4af102b512626255c4bcf49fd00fe8582823e76eca9c93b1d02f0a 4129880f57d17cd4de0edf60cbeb8894701ce721853abb742e4ee387a3a5c2e6 5e13bb184adcb762935571288f2cd1aaa380f287fc99d206667006026d81b6ea 0d740eb1f02ba3250a4053c0b6f56e5259111a2c5b1df227b58f554f2dabda06 8aa9c5e6d73645c292817e3d01cc3733a4090f10962a5b4b63bc08ce5112b630 adaf56b71d368a91428a2fe206656fcb502659f5cffa864d81ca5576c8d9901c 0af4b5b5e93341604ccb1cd17be3be0c1442f6ec11fdb052c53c44ee20f3a1b8 8f011f291a2d015968fa91ca9ada60f16a5345fec9b747472e3a0ab5cd5a01e0 6533ac1862980d523583200ecba02c22d6c36738c8d006a0882fbace69e7b7ca c6e3fcdc04508bfbc6bf1ad433aea4c2bde413e8f1517297ae882ee21a3efdcd 1dd9b5444c8045cb1a8c6aa2b2c6bb647822fa22ebf032c184609dc4109460a4 8b6dba4f00d8bbfee933989693d330b0fc47cc97d3568d430526c35afeb67f73 e678a795a05cfbf25973a04ecab6792ac1baed98e7b5b89c06a69336690e5692 96cad459076eaffb4cd656ca4d81e52748ed2d3b4066bb7037275aa6426b1657 f270d06f2187fa90718d75eda70945498a48956cc1eaf556b5aeb49091008d58 2bcb75847205444b0d2d8e48daa06227531148abfdc4c14dc1a64a7a2148371c d01783bee86b9be199676e2b63f238d0e237c5c0028b70b2bed15a4cce3e00a2 2c0037ee3c9a2e3f3c1f0f8add44bd9cc2ecc5dfc9f48242477e87a3e0e68d092753a65c949456a44ae3220c1a7a52015d92385926a7bcdbdfbe03e210afd604bd629bdd7ce73db661e4108f78011564d58c8382f09fc5defcebab65d653300426fe4f80f02b677b94fa28e4ce25fc3460dd0b61892db33325941ce86f1734078b1a848c832d0de909b6d175c3cdaba1e74647e3e88675f6ee99c92725bda7047310a147ea919639b3a4eca0dcb09e15c8a8b1942157728477cebd6195e76d0dd555045a75eed32921b0f04951a9b70baefaa300aec07dc5ac1e24c41b091301f6c7b6836435160b0649535e108fb13cb1dad0e837facfee6a3055785d08ef0f1b5958cfbc017c6dbd715069345a9eec9f55e330c20d671d059ccbd9ab3e930b4500666c4d1eaea44c740d69029093c1e609a4ae57b35b0c979dbeca7848870e405fab0ec4e1c8b1df4ec9cc606e5d711bab8a609076f5587bd26633a7718405ebfb264eb42cac8450c67990e0150a9a828f0c0296664203c9915feaa3ffc004600bb1acc7022c3a64ccca5707074ca939c28dd91e6a0b34d769d46dd7b4dd02d6c1e9d172fbba57ab4bae15d801d732e3fd36b2976a406f919441920f74b401650f85e7720ae8581283d38e97534a97f796e3eba28236910705971ad8b85203cf2c2b3c22f1799d7472bc3372fa10a2dbcb3e104710a1fdc950eaadcf6e50023aa1e66174bcc6678d6f704fd72110ad4339a826fe11ad4bcbd54113b9397708bb7e77d15ae14fa2ffc94ace020ff66695f6e9e828583033c84532604348b30ede550b71368a3fd8785bc1f071528d991280fde9261f65b977e09d03d1b3850438829f90f2fe413dcf60b2d48962990dfdf926db1aad159901870547cb87210e3b36232dc0337895c44e200f88637b1628c387ed40698b1e7d539c1609280a086574e2e768bd0a74e8dbc69e061f9c874f81e25f3753211835ac3c1bb7769e08cdcad6fcf06bfc0a9bfaa339a174860f8f504dc6d0839f0a5b9400f3c4f7da02420ab38314a838dab13ccf33c1105275f027d1a8999ef03180aa5cdc6e490a0f520917b001b0c5e0e0f90f66165260138e1e2446d38110fba9af7c9493b3cc046c39c50395871e1e1ffdd742f7c2b0aa84ec80e71cefff445296b3b15fe323023fc1f2f5e44ffea3af4409b199e277d905cbace9941e8b2b1a91bc91c7a7a803694169fe08c7573addd145d9fabe9b5799112c8161db243f354fede3e30eaa0d1928297940f61944441fabd41de4430ac3cd9babb29235eafcb7397e8cd1280f149a2a539c28a4796ad79c2823182a5c7d5cc0796a9fa297bb479f4a8f896309a9988579fc431eb77b13cc2043c36ea1f7fda3d790a0f033970ce2b8c71e1f015ae1face994f265bc27d84e60dad1b6451ed3e55be87f65cdc3aa0a1ada31c0a722fe3352bc8b194dbee456cb331bb589f63cd033cfb07232547ed2dfc1898029bee061f266dc3f5c2562eb5ae27cb1945d39a6a614d6b61c9d512ebcea4170303d28bc1a3c4618830ceefc9e17a59d744d8484a4051329d1332957540c494075287245144d22c79bb387c398bed3d6ed1f3d4073caddbf556792d595bc5e80d4a96554aaddb5910aa12005a969d07b5b29181cfca793eff57ef854517274b0734f2c14cee5f7db51b589772ad2a76504266529bf46a12674ab74605cc6bb603 true +check_ring_signature c3c6d196b13aa60cdf1d2887482f0be87ec5679ecc25134e05825781a1baff79 278196c85cd410fcf2ceb6d578f787755f5b5e1d594edbafe95fd1b6a7b8a9cc 4 29d96506cb3486215c335427cd069252a2a93bc4573d362e29954f07ce2443dd 5799ba555ac40a85ed1287a28889e53dc3c07a48e5d18abd54b4411fe8aa6305 511cc99f3af8560bc829fb18502432761bc8832e79bdd6da2748dc2aa10e1a87 428b239d33a76723f5d670ccab90a06872cb54a6fba873bac7220218f616ae88 4dc638ac15c61069536daa1d75b47fd269c186c0bf3869f8a885a5ce40b5560578387913641850a31d8a942556a3b8b6764f17d06d9206251f22ce4e675fc80fac2de79252fe8cfd3f14ae76488d06f0aee8554ff85ccdd57bf2abd91ae7f608e2dbed14499c9be43bf742129e1ca365737b233d1b104ebb9f75a457ace86f0d49e9ba7d297aa921f54f5ad6a16837880b33c3c7ea34f71928b72f61190b2c0fac6ba6535d9220774e0a25a9044a8e27d27f8b651ae48d9a97c326d3a50fe40efc1bde7d41ac0943c370b93891c2a45eb49ad11d4d7e9e73857c5df3e9843103af06d7bfef603f671aa8907a7e8182ca5e09b63755e67c800d8013e458790a0e false +check_ring_signature 47ba27b306eb03890282b826b80737cf04a2216ba66620a0e91395ad36ae1f1b ac471c64f5faa6e45ebfa21ade49c764b3946821bfc41814babd9898b08a9342 8 bd18c49ed81dbbdb35d7fd6d7b3cc4beb0b35320193c2321e5a701c102859bce b6b992aef2154aa6116de940dbae1eb1419e43d52bbfd67174d56fc61b54071a 5b11f7315ac292d469a734748aa9b24cbe7aa9500e7e445a05b70ad74106b871 bd12be5dc2ee0e23b861b708a8f259bf9afe8c8c3c4ea3dd6d07d54ff02f236b 563d75d37a2ebcc64a473cd258ad833c1cab13f4cb7bfcfd4667f3ff9afdb771 0e8e4b4fe46bf75c80a1d4a4ac2271825ab283867ae5ffc0811b301ababda0ac 9f3a9594e68e17d6073d34f1bc1f4c0019887014aa8a2b4ca4cf3a789325bfaf 41b5c9abf854e92c8932146349a30d343175d31f260906217f3ee518f09d1ec1 c3d3b9702ce05c8d02e4f124ded98a08d42fe7b2bb71273bcdbd4cb4831b9b088299bcc1ab9015f94d47f6276d5a9bf72d74ad755263bb1864dc7e116bc46058644f1349c4072766e1c0998db4d3c485459e1171448f3f1c9f1d756188eeee03ca25c455656c175cfa90ac144b0829100adb3d15617ef9c28efeef75e33bf10db0dc2726c36316b82dc0b3db1bdd2bec722e93a65405022bcfdff2cb47e45b0bae1e68c71fa4f093d94b0a32d4245f3357fc0246903a570d211f69c95705330448f6ce35eef4320e428465ee5eb6f7cec5244a2c069f6664a0cb91e16b08d10e7f253f63edccaef23b62b60fc726303e4276ff51c84c42eca6c2523931545107375b74ae560d82aab5f10278aeaf2f9b45281df01011465a601545f8e0b25d0e2f42894a3338c631652304a6050e84eae84cdb12f902aa9dcfcbb32e93d8430cb526b277dde3e7c0f788c6d7252ae6111f35183b2e014b804ffd13c6340a9c0247df344c08e8cec7e40e779835fff6e0c9eb1796b97b2cea06ded88a6730e003c1a1c480986c70f46446606cefb61ef38438b83897efef692ed4b629a2d63d0ec0849cfd8af6d58946f92834b31653e7d68bdf9b68e6b289cc4ec88215cf940aea7269002fd165a76ec8350fa0613db6cc7b8b9faea989145ee5246d1fcc37b7895b533eb5f37f2339cf63572bd8262b3d8bec6719df08bd9ed84c1d98f5240e false +check_ring_signature 693f10f7269cd1f41026a722311edfffabeafa030f76bdf0b1e40deb4bbd1744 6949162861c9ed84a2e7e79e884d8f4aeef5a7a061a6cf5e5d5eb3b7eca42be1 1 3596904bdbc145a8de28abd87b8cd8a3e158ff42930c2cb8198ea25312f20586 6d8b22a46181f8c89e30eecc81dec9a84565bf97d43b3540bc7997a4bee4b10cf2d6fcdeeca88439376459d6afbb607796761f11da9d81a8b2c05c762eec7a07 true +check_ring_signature 4a5aaa62e05480312f9c0a6047db2968c2c1085402dedaa88ec6d06e7d11a494 e8546fa88a2d34a8a58cd277e7d4bd481ae9b8a78806e0232b0535ec3b60146a 1 e904e5398d3ab291e3c9f8cc4969ff2504ba4adc792431cc3a236bed3d33563e 65b541b63253c033d57199600116569455a2b4b3ebb241c9660a321d715e950e3d9d8a2848a82d12492ff53c8d52cc0633e8ff825b896e022254e19fb4e52e04 true +check_ring_signature 648ec122678f4a7a21bbf3ace391ce828ae08a428a393c2a981ef8322bd2c823 7b9e55e686cf3881119c936a4a5320a93952bb661af1b09a15addcd720c3ecf4 2 626eaff6ed831d854269a19c51625c8b4af1e29eba37daa119330b7a05808c8a 83526bf81479b151e339ab3450192b445c45c41f7d13a05a75c0bd23d0e7a0bd 3918c1e77fe5e5cd41e6ddbaba3280029b2824d9d856860a7b60c88683a1890b1da865748f18c76c366202552fe78fc292dd609be49609d6db06b6fd1ef98c0ae85277de67665f194738d4a5f95e7ed3fb0778fc8f99fd24c01fec396a755005e27eda108ac06ed515172c1a749841e97a975b4749e5e7dbfb5c9beadb3da60e true +check_ring_signature ec0d21ac6b6b1a2721cf07c66ee1ffea1442c2c95d5db462b12872eb682670e6 9695fd1e188abf90d8a0ccbe3391757bc7fbd9ac6020d026f5e820d30b320ed0 1 811e54d0f9522202ea95444343a57896e1918edf291d43a806b3b9fe02872977 bdb34aa9aa691b34ef481c57c2615dcf28678e61d6a919b1bb45747aadf8260477bc509b1f4685417a7babe5fa0007dc959c24316afcf08900b0f580fda42409 true +check_ring_signature 48e9fc40d2b471dd43a49f00ea42669043ab32fdb2f8cc6b0ff64ad520d53958 9624596a1152cc038d52f5535c5ac5c8fb3ab603d3b1027261a2d5472ba81051 1 38ece7da2aed1f32f68c6cde78ab10191e7a23579c3ca53f1f1bf3a4458a12a2 5d9137e105d069848784465829d92bfd0b6b46d5f0ec19571e9630c8d3154c0ae871c442c562e62a7217416d40e63e8c40720044b9cf10ee23ca2bad8fefc20c false +check_ring_signature fe38e90f670d5717369347333330b7e8571630dcb1e830750cdee1e32008a80c 20c20fa07f7fc0ea336c1cbfa2bc63440ccffd9417ad4efe338bc9209185534f 7 58f03d68482940daf551c71114a698c6745725c2b4ed77f7f2e844539e68bf4d 7173e4fbfdb03646b796cc1530e470742349da34bba0f4f13aaaa4e966266770 b3fe84791379822ca5c60b8044800d68997f46060906155b52ac4d56a03fd3a4 01dd8c928f7ca749765c79cfbe6dd03eba2e9ea5c1ccf59d178df10dd1dfa0d7 050aa0937a74403266183a3b3ce4c9984da109076708b149cc89e32985126c66 e03fa766abd2ed39596f4b42cec2aaa4980a033cb4814eed766f12299655a6b7 3e5fa122644466e3535a7739dd37adcb3a097bf7d68072830ac0eed87c3d4b4b 2fec87b20611218269ffd5e0dec21b2b7713222b2bd594911043569fff08f60776225fe0080586e56f63f4794bd4b6812f5ae7ff93dbce4205c7cd2cc19d870ca935376ffec7205f53333d4d7b2b8be8a80882d9ec091ab848be29a2dae7290e26c72e9fe1fcd37db430b5bec4adee928207ccd95da3d467425cd98fb45b1500f016fd0f6a170ccf428297429dc16aff424eca9d53be80d4572ac0934b902208ca8d14b94b9645838f3fb2803ffc472248e5a63c6238f701c1275771ad1ca700c153e4c45dcc47dff8cac6cffbf0e0c8893cc5a147e9e840cb415efc80c17c00217505b095030064ee4666b55be9c1bf63f3e06631505e41cdd3d705f0fdbe05d931d369b3765496d60a051b8aa215d10e79169e1c9fbddf9bd0a66699995e0d4835a193442a9a5f13c1824f6b99439446c374c43dcddeabb2e943b96758ed041ea0ca01a479c920cd2a918ca034cc8823e3bb998ebde64c476f5ccd29eb620e2d3e602a7cf705ecdff0a744d5ba0fb47baf80dc3ed78c94ca1e24ad6742ff042a9af289e3b31e66a64623fe4235ba6e458f3022b88e21a9edfe782f4077d709033dc3e750bf6cead4327a5d4d7d31c1390b3d1db9a9c23363d0d2960e81e90c false +check_ring_signature 160604bbc2574346a5eec6a3aae9e98512bd4edbd630203d6a9c551f8ac4241d d55d3b5d1c45f589aef0bed59e16c7264bb38c170eda0b8b277bf4d19ad98e34 20 d81d54a93153a3382f475f31907e6c3a6aa4d6cd1ef962603707085525a0f373 a64b2ba79c22164cf4629dbcb7ff63f870904305199de08df305601273ca29ac afc4eacade20e54c254377933a87dc3ea72eb3e17718680ad4f03be82dffc21b c2172f93581e4c81e3cc38f740c21455faf793e1000a915d80b00a8f8f137229 6b60f939569bf0928dffaf85c787c54f2d559df744261f9445f4487cabfd6a0d fb56f868a8654bd48b1e2948bdaf357ad16f825d3dac9a1cb3aa3cdadcf70c9b 6df3e8028836284ed732a3e1813def3b3a2b0b7b258db1b94f9e1bd19e81f18e 833a9883d814e4951cbb0fb831d68060c95226ec6c4b18937e7a6c1fe2bf7f3d 20118224f01be8040c91a99da8de0688b5af8691102d485ea186505c22c93989 4965bbc185fd2e9b97b9ec02a84b6e0ff1c81a9cb51a42f2ab1167b627ecfe7a 66af4ed330c59efe8bf7ec99381080a15cedd30243a590e792fc96f8388faba0 1247cd65d7f1362af69761b19d264f2de34d83050cd6a7c9bbb940c257fe511a 7bb1adbb63605cf3f65a9ea2f9ff497ed90e7623c04e3f9843064d40615578c7 a587268eb69acf4bf3022ba2a16ef3e289e98d4c9669be50caf9eb7593711b26 df0d9924e73e6c8d5b727ee61308a445545bd2382b5308c18ae6be8c8d67866c c0e417836cad0604ccf14b83efce2b34aba3cdb12b5d90f28b008dd2e5c7d28b fdd6fe604a20ad16099094fe7e8635b1190db4187d65f2a93f2ab028239a3ae1 c5994bc655cd5e8d2e61fe28ea9098b1383162c8c9129b8fd1ac273978c5e995 7e0567b7b88191819a865ee01470e2bfcf6236d5755ec5f6ddf2fe240417a7c7 744260e8026f88f11c8055c7cd774f3b450a0361991c66529dfa9c25d02c9e2c cc7ba3e3ed64a94893090d054fef317e1b0397decbe6b8c63c41e4872f21820dcf13813d732f3118933593d661a65eb1bf463d1000d70c59b0f3c1dff0df55030b5865403762d3b31937973e3bef6e85c899dcf496bd0c4ff2c3752d8cab56052e5956f82b17c570e5e307a47209619f76186a6648c22d1435c799031cd8b500bf75bd24e80bcbbc77739d765e50da3ca3cdf37fd813bcb6010fe914412f2404cbf4ae840e739a6539b96eb370ebacc7ed02a30fba751dcf13f75321ea49a60cd7a8c88bf7865f1557fd147201b5773fa1ede3b87e88f7e165741843ea6c6c055f8c044c287c0f5260cab14fbd0dd3134cba70bd3d8fc169af39a0cb4452d007c14785cceb026428670ee8bddf12677365dfa30781fc2b714169cb3df7072c09e930e06d2d9bd172698c81659eace0a4a4ebe3feb4051d82f72e8dde6921e80ef957bf216e4b6d28dfde491602155a959ae65d29fe2c00d9256d6fda37be9b01dac11346031bcacc8aaacff8be0db61011de574d1cabcc2c6f349585a90026046582b07330c7669c2e46fd31d07227fb44a196cca717c87569c5428b84cf610d56bab1f5f14612d4ee1e0dc4b7beee47054fdf1a019e13dfe3f8747f9df2070c44332f7064c7aadb4203314a54d5f0fa3569a34881837d06605161a15a1f0f09261661741f13c0a0860141b89599c76fbcb7fe0230f7f8350bffdab55ddfb9028a6180e8b38edf669344f26d7211359ed7f94d71609a29720b3a0b5aa707ba074e965cba7de05a65c1d2eb9587186fa0f4d9b82af21c3c9df55cc2284316b20ffcdd58838d123a1aaa95ca269cb4ad188e14aa20584612f2e7e6d2c9c538070d01a90932dcf395d5bc994fa68ca0e784f9b9eab738941983dc5ee578ed924a0c1c97442ee4503473c0f665b9bcf98233888a7b15e8b04bc2fa732086d59f4008e71af0ff0a08379f7fba2361f3b6f30a8f90f4cb32a25bfc92ce9a5f23238001d8b3467458a93f2f3ceb24dc04634533f8cadd6a0da97993427b532dd03b4f0d8ad38f2073c9feb48ef40d2a22b4e8df516437d1f8266bf47a54a784608a7e0833107fa8c30c83475f60cb369d8d3a436f3d23614a0d6f796d4998f9a0918f06aecd29dacaca024dacd7307b68e9513cd4b201522abdb1def96e54f9684b5d06da8c968334af86d30b2cd00d155cb7531aada6b199d87a30e4d6fbbe4be0100007d829774cd614b73628a47278efdaebc2839a319f45ab192f7a2abf07ce580396825dfc2ba3943b77bde78dea23edfbd66935ff6ebb0aec97902aabbec6820561e19bc5dabf2320955a931861f28e7268f96a18b11e49376eddccb1415cd00ccf48ef4f4b1127695d8912bef072f65c6b289371356fa71ecbf49396de03eb0575dcdb04cf74c934843b5a39da6d9c64ae54b4274d9439b840ea723ac89b7503a54d5aaf2414c1136b3d78d22ecdfe6cb39194cfe85a0e21bf6e78960cd3e808acf3f646a123256f2d3e8d058865efbaefe9d46f96fec8b6a888029c663a300fb65e27fce8764f3bcc7830e408c99e6708f1d560fb0e3bb401c0ac5bbd67c40d46c705f77c038f4b47f056c6250d54b8a5738197bb4b01e4b81dc4ee701a950ba5c60e9dce84bab9777568f0ebedb0c1e507f96509c3ecd577c8677d55d78e00460cc76e22a5463b058f6c7c8aded21f0032dd1332534a4ed98a9d2f4a052008825aa431811d7a9232a264b93ed24e8dd31f2cbdee1e7bc413f89d7257c2c40ab6f098db771f898b62fb38b85ac7055212b8f874f33c2c781e5b3b1061a5110e true +check_ring_signature d730a3513ff7f2bc82239c65c41a5218ad3a8b97128b01a4a6e78abe513118c2 f30bb89bb30680bd25cc9d080b825ee63d16631c374386d47443955ea5af93bd 2 c201254ca4fa851dfc1a9148998bf42d5764cfc28c881b7ed69789139b9490f0 f6e5d84f1d327113442dc2069a0a42f4f607670283de04c9d10b82acfae2c331 499559c386fbc07b3d24e6343fcde34faba09d1ab2bdf467911a4812aea9b405302ba8fd6759f4a0e97d0705c6b8275d22f1c67e517b3b58e7400f02ea557b077b8d9d46065c2fe34a574493e325ea70dff6d34a66e7938466bfe1474e432c0e62c806f2492bbab3c437f8e3d271a1f6229f687c5a126bb98420a4b8e30e1109 true +check_ring_signature 09808d1a3a4e37f73311b2b18b616d8f0c99b90f751305b4b6d895e32b2c5f7f ddf06a1996447381e9d667ef15a1cff75dabdc96e17aea4dd797722086ade143 1 ce0baec1e9575988cc42348f3ac59d7185451746f45d26d45fc812747fd05b08 9f75fbff5ee3dea134a6fe4d6f768684b9e89211fddd0af26124b99470829a6b7c96f40545e0372c33246d3b14a049d5156670a61310f529d4abe0c236702602 false +check_ring_signature c756edcb94d1cb02cb7962bedbc8df4367e118ff1eb764630ac7f337f3337d2d b33df4189984b652c8c583d08b86c9c2bc713b1ad0e6c286563bdfc29b07ba6b 4 29de4cd3fe5f7ca7fc1b0e67c16772fd2eba39a627ecf1ac6a356296dcfd6222 9d370c35fc6655c3eb7e6540c22d2fe4da3e87b1cc11736228453ca4b1e5569d 447397d83d4cc6783491b5511b97adc0b8d4f3060fc67cfb68b8f0cc3846b32c c8b3c25cd127ed08463aacf4eac8c114a86f3f7919cfe087f81b1d8cc73fc46e 360ac7cc64de0781d81936e8cb036e2d3b90783cf3bff5f8bfbcd898f3f99e0024c1bba9be0291efbfd7b6e375e1145bfd7f9bedfa8ef9448b54c1af9e063f0682cf6798e0264b21cb15fed4db391082094407876cdc60dea10dd7d993ebbf0edb0a686eb64591398814e6fab19accc9dc98f0b16e54a6b35b898c95362851c7642e0d4a85020810ba3d139bcc2c1bd0ceb8f3db8d281c69aa9b80c49ba5910d947fa9bcef2d08982bcf530e8c93afb610b3b929abca1b2a2798f6b6707fcd0d1948bf7d26cd9524c76f866820512a73c638f9165b93525733ca74f1f26c66076fa98be65b9012ef835d1cf480f8440c70994f745591f56f46986ca867e7e748 false +check_ring_signature 0396c32cc100f10522c404d0d021a1632af26cd1425e2e231f69f77975273841 c30788d6505bd5a2fb6665063d6f5f07f458f4f4a4aa1d89719cc7316de94add 109 f68deb723ec9ea8a8d4eb34e86f500451d76e19133957330cffdf6f83de0347d cd1789da5c42020dd76d14dee0052c1f2815f55a684766f5e5e8cdfded95067f 050e3ad5f6e99e91018cada1011ddb06153062c695ff0216768f148b2dc9507c c4501f98524350340ca4ccb4b8ba73a5f24730a0b588f16ac71e62be0551a4de 720e1513eeec170ffa64a6abca86e46bafb15564436d7c87bcd523abb3cd5b1b b94c584b4e27b939f77d5a6a3da20395cf2e71d79a684e0e800f8155504934d0 3024b32767058707521c8d00cfd3a03642e2e854e49457ada0bc1986e90e6c5d 0ca70d8753fe96e02b0f7f0cf8f775e2a28079d709556d9302e06039b95a798f 52d8cb897945678dac163c56a070952082e1f1ebba2aad53f8642530b342194e f8326ebd3ecfb59b3ae847ce34ae9b4de6e42ae3506337cc566c1b6fb042b3df b99874f677e0ce5d699bbe35e18047326587f0e03b42f6b9f150456ee6839eaa 089a984ed15f1260f6ec6efb8d111228a8a7657ae813720e62130e64adbb069e e1bfb6a3e8949f117680fd8db5b2042ebb62c87d5e5932241ae1c14b55ac4046 72fc0278e3c37d73dede608cb1dd31f788f689e4f0db1a7035522fcdbaf94ebe 8b78a79e4bd5fcde57d25bb78cc7ed49bf42ee8cedf97516ae77751d61d41f67 d2d71512bd39872c056b5a78418b4e4dc1f8e4abeeffb9b76a00075800b98ed4 8bf567612e20914e8026e00aeef7985d35807f8184d9b0c75e49e0fd3070467e dd6b6fa19eb9ee1393b4da5d2eead4bfc75d98222c9dd1e789ed55f0e1ca6f03 52893703e2da741f957d9ee9992daedece00c0b9d6e41ab7f0cd97ebf3045e4d 5b0cefe001823d1db0c63e871514326e74bde5558cb6f401d515cc99572dcb6b fecb681a884fa235d7bbc88e6b28aaa7b437c017b5694924fb8c92fafd4f6a8d e7f7a42765593deec2680f4a653f1bb74bc39786e6bc3f851c16dd6ef3fb28f6 eef694544ea10d82fc92afafebf3e8ca68e50718b7fb1fb61d576c2a7cb48526 705c310118ed0faca1aa970fe7b97e0f2e13f78b097d4e6b269719a0f50a755f 872efe64480caff4e9cfe4169cb8e9c19ef55a9bf76cf2ea4231370ff4cb06e4 f7b47efe03f1b2b4fabbb6f8526b7914b7167f9736ed1d5d51b6a795e09d37fa 0d0870b5c8c3ddf5866aea0fac28eb0c14902485770748bcc6647190c2872aa2 071cf5411efe8befaff40b3704d991a49b296f9da9a2e4ae8e088bb7eaefa6d9 415ce9dcb5b04b92309a38690eb050790de2d81e60e50ac75878cabc36d1727f bfef4e37e679b2cfcbae71e78b5bb24b601c0ef877a7f13627bc0c509ff46445 875018ae8b93e637c00d6d79e67b900c68236ae8e5a98c682b63cb6279769971 a6125089dc9695dd075f9ce34c358cfab3025b92d83bb072536a23a146ed366a 87c7fe27d3ae4d43d2c92d08bdecce4f4ad77675f8e1452cc7be9b21669028a0 c8d19b3173551bb58636fc94bdc284ad4a082470504feb0ed7d5d6c77815af85 81a327b3603dbb3eea37e1e730f20567923f16a732f855a9ea92150253bee242 5ce6f63c502f68bdab13648ec5decfe580608b6742790ffe7b182095fd8f0a5b 0e587f98c73e1cccddec807a8f69ecce8fe8116b643ebed70219807028d385b0 d2b83bf43796a7e5587d39be6ca97d12e10a38b6a97cd8e397009cde2fe62933 edfe422d3422a9250fe5df94da3261e1eba561db481d2b2aea6baaee7c562b1c df2823103c1e4a8f047477077b97086ba78bc3214d4f612556b10384573b0884 bcf97e9973cf6d44bedf785986f0745f638023eda306ae8ca7f6bed95034bb7a b29b0301d314b01580da4eddd532d4ea6ed99e676b5901008aa455e19057e3a7 de33d4fed6da756fa5422f0ef6327fa75a03229fa327275c1a0edd1ab218f2e6 6413f669faee7dfca166f79a3373d3a8145fd9bd582a5446450dc9c658ca80b1 7c99673919625e75ba079330f57d3c3767efb9724842610d5dcfdd89e1a01552 94cadadc52b314bf83579f363cea5c4fe1b47080f18d1a02dcb3e5755eea1e45 8b7a7d3875f38bd5e81016a8931d9ed52cd05db05173be615e32626869863c87 31abe012781697407423a35f51e85a1aaecc80172bd8227b8cd91b777223f3fe d60b37d37b6478651efd58570f4b28b67a10a807ba5b8ac4bc8add8ebbdc3961 8a4448ecefc16d85239db7c82a17c23abda618307215753c044a59f16e23abaf 2fa98e0afd18be236d5bb2540c531493194b60f6dcf60c6dea8e490c092547fb 36b2009d35b56fadd6cac62f2845cb26cdb2a1011627b3416fd6e3cc26282bf5 b3caa1e20df756a96c30a94054a55f4b9cda5e31a82af77b6d33efcc5dd80ac5 7a7698d7228bd0be0679e8eafa1181698cd25062a77024abb084c305f3d2d8d3 7ebd3759f469cbafd3e7f49b264cbb6e31de95fddff4bb556d9cbcc09b41addd f80facd7fdc23ff2b4d1388565554411f2178305e04d80574da1c18acdc6b7db 9a8bf621e202c6d9d1517947313a79858c847c6ec07d1ff53829aadb73a98d52 7c718ecd616922e8d943f5e301a2849938f83a45533f4be3372521215d813922 675919c19a266ac60d0dd85aa711ee7105dda95a771c8632a386fc7adc3b09df b0fa57ce5eb27a383779e107660647311a56f81181fea8cba6bf1ff5d0636beb 3e3cf2e07522c9f7c77ca2a9f44a88dfe58e52778b882c17699bdab28a888677 8f5b1a4650ec7f9c08c812bb77f37cf5cada113ffa1ec7593520d7781b203660 6525b6691d9ecc38e64953f51d1c99689eb0347e1701bd607bdbd82d2901a29a ebd7313c28ab51775480cac9854ce4a63f82b4dfe4d9bed880a35c05d39e6970 b4aa9b9a922794628adcf01f3be1d836b2f1f7d4ef0abdce530ea8654dc9923e 67cc6439aef4e2c776ebcf28accd990443252708e4c12c2ac585423c3ad52cec 700711f9596607e1fbdb91f1ff7495fbaddf5a10095f59cfdfc52a8fa8f32d21 a75164ca7a0b93b91c8ff5bb9b56ac9667e89216f3d16398c8eaf69fa2f46f39 fb825cf710cec0978d43261f0c125794c753dcc2bf00b012dda71943f128d567 2255c904737395a8b60104d0b465caf5294767224d7c7d439a17d7d1a623b51e 81c9cea2a248792b2f70db0757f84504e9e8bddb708ea5bd5962d7a1ecd0db9d 4b65006b7ccf9fba2f7fe0e771f9cd914b57e470093c9ddf635c764672042444 f3c88ab8cad6847ac1e75eb6bceb084870f5a8d19e3e753694232259b2bcaeee c2563e31b4fdc2f8b1272e5528aa9fd67fea998f79bfecc0fa69d54a99e0c98d da82f94534daedb646209b82751142f574837da44655ffcf0aaac883a3fcbd36 575bb16bd0c295b28363bf19ad6166736e7076cd5c8352572de47384d06d1f60 f5c660438add81061245191f8328060c931d9b51d28ace8320c087e0a365ee2a 24b868939cbbab3f6ea127f336e312bc7c973be24fd357971503f7321344528d b3303e05f95de61c86006fae6b226a60e5f60ca93f9d97400bc95aa1bcc74991 6cdf35c3dd685625c4a35ef1b357df7a22091deecbc724e10c82c52cf199405b 205bee1c76ee442f4e6122ed00874ae6783aae771d8e16d999f0ff149c2bf723 49723346fe6b7c386a0fdcc4c60d674f3c9cf26ff4ed7dde16f902ddccf71004 57919105075ad7986d2c7828c92e9d996baf6aceca088dcb09b04c52f5ace0a1 3b089c93f6e6d3d3e471023fff57a846f422a3a5ea5810055f4eed848d35f098 01b97043e1f1439ab664658bfa094ee133e42f57e8d1deb84e50e48039df4343 21636931cb581a9153dc7bf34e1b8f0457075d19d7e21e6922cfec07812926e0 12084a789947d439c4be7cbe64070c17d3afa14b1e1188f7d4ef187ebe952700 9959f5aa872a4c4e13997f980e25d389a497cf964caf346221d93ec187d26db8 c3508563a2a48d82746fa511cfebf41d7ffafd9cb4b49701aecd174515db8280 0aa6da48f43a0c2513e8861805df297fcd04edd3d3f13adc9f580595498667a9 334a000f6efaa8c9efe29d924b62b304c3c5446f8ceb2a305512b5a89f1ea839 6e6c171c39a3fc95551d6a91ce08ac64349f30eb4d4a15959b5f62ad261dbf4b c494aa6d6fa5d89d60ea537ba0629e41f67189264bda265f1daa2af215848a76 207afca9ca4dbbbb28c6114ae80e9f260be49579d4128f4834d09fc79043e7ea 4bc55be2c75c48685534aa3302a60769610958806e35ecb156049ca9e5f3206d c70d7e03fa1c6a349551546bd4434172529e7038a7697e923932e91d8b0192ca b3e7728127b6b8f1e8c3e0b9c84f5cb568d87ea69f0d127a01d2ba88f7c9df01 14d77d62f1ee5b89606f593d48182ab7c268f9776b232e15833535cfdae5b9a7 9b390bf7c6740667fc0ce01126d7dfaf38439ca1d54316fa05795164ef39f786 1f9cce64914ab9c44e57d99571c5366373e03115e099784ae837c96713eef1b6 06714aae5a598b578143db340d6cb2bb24da9215bc9230b6b7765865f6f86de6 b843863668020f61a0c4eae6af0dc622d3744b7baec997e8fb33b48782fdc53b cd159c11cc0121aab8cbab8412412480682673ef6413d968a31dcfc743a8f5f6 6900124cbbfb48de291b04ea7d1d15aca7860611a5c43bcaae9ad2f491729f11 88751082c2b62f756cf674355d76e8ec5dd09e1ec009f919f98d0bcd9d961837 43a62ce5aa034b046234e784e04fe5653059b440b6d03e93259a96d55973437e 2109b2c5128c23933397c30acf7d99e5f9a33a7ae3417f581f656342a838147d 1b614425ca7aabace24db90e3c70574633344e5fbc5749e59c32123cc2d87994 27d356fa32cfcc21fbc96346426e0c575064ada0b5e96df547014386d8b33c0e 6f08d1e1b157bdb0e87b38d74adfc078221739e7b72ab9f58024d55fc907db0090d0bb8794e53e5e8157473541dce0181c15c9c3d25a560a6959947ae7c50205cfd3c12736e19431ae5b5595114ab4b8213622318699c9a7e5d30781d26043013cc2ceaf2dff5391b7cc64874886a343aee21815f3da91eee83de46a2f87690caca33ac0ceeb705eb48792e52e1feacce62b109fe44f7272f20e6f5e2ebfcd0f4e84add36b15fe448af4150a0e80fedaf218593f6fa8438891240a8196686800c8cc9b9d453df4c3731aa34286a4aaf99f1f2c15ab906c3035e4616c6d523f00179987dd9f5dd6c600d7295deb8740bc94a4b37be43628a94336a6b8891a510c699b45e168c84a18e20b0ce60e82f355c70eb5247729269f373f359839eb6309d240101c7c9aa373dd25ddcd5f39f1a9c382534bb222e8265ac7291c26e4cf06810c56293cfea7f0d2276830785e652117eeead636ce0915d54e505ca78e370fbafc2fd0d71b3f3384dcbc00f1b663014d57777db4dc66091e101d0242f9e000dc36d40d8a0f357ed7e5d293fc6bb9a0e0e34d7b7413443b85bb753b4ee9300680be0f250e64eb364204bbed42d907dd1e89a95bde4a638c5ced74027fdb7d085b24f5a2982fc448d1e9dc63b56d30b201be3fab3d740a348963e1c87807190276b36c86f1f21a87c81ec06e02f1763423db43220b2a9f435a5ce4a96fb576025b699e0fce19b377cdc45b1aa337c13a9c065238098ee1cd39f93892b31f2509955f059f346bdc4d5ff575cd5afc202101ee30a8751f61a00fb1554f3fcbbe03924ececb829fb3bb407da97a4da68569e57befd3ad1ac88e135b283b77db010588f6d32968ffbeff2f7d9ef59b047c70d491a39dbb4f69be1bcf0f09cccb660f6efabf1c0b1e96ef65c9ff9aece11accd5f63bf2a9d719eb3d63eeb648aafc0d3f264b4a82232f51a7f721090b68e9bad948aecc09e97a98791d2d5ecd90690f4040cf09a4c47161bf89af23451b14f075e507c12ca094ef867a5044057de70df8dd42690767890edb7a6d673cdf6d1295e8b098f9a2c566975a221c71badb0687407560c64598bf0bc55d515828ceb987b829d2f0558de3061c4fe4ce618508917c09a371a1ac5270c14be3ac86178cb78e8df14dd9a4da635c7aacb799da0fafa9e0d6b4a39ac34ade48676e4345de719a468fa0633e529b5865c123b435093116dc328168cf4ac1899e8b3321b27ecc46fd205c57fb8664214762be9b5d08e3e314053d34d1cf3c3be750c2e5fb45bba40f6354a28bf9c3a6e49eea3c6901a72949895b8464a7ba8d15f36d0800f0bdc96c7aeeb17deb57adb2c0695d4504013c78c1741c2c1d326ecde82d582a185a4ca780a5581a35381b8eb32fe8980aa5444bbe27d8ecfcffb6736d0e5908887df20f0e98edb3e2ad84772772f93509fe079abcc7fc739695f12cb14abcb0afca6e2f57be5a37374322f510cadf6f03bcec329d76a138c7bcc222b11812f7946bf7865d24463838762b0c378709d502e10918ba68abc0cd0a8d08f6bc50cb44adee350df0bb6d8e8fff79401dfe2206bb4263892a0fc1eb4d6fe34bba6e582d7611dfb10a7c3b0f6d3ea2b13c7036007baf8fb1ba4fa35e7e65c8ccc191c9b776aecd4a0c37a86bbf3e2eb9b1978d0cbde728726a7de46a72fbaf58cc4baa9e660bc1c32c852788b5e502302546450f0a2c5d900e614d5b5da4ebdcb89e9539abafdd3cc8c89e9544fdd4cb24a40205317ce890836f5484f9986be33327be43242242b7fc84cea6097643a60ede160401ec66106d523567d5b9337f8d3aed0a0a98ca289e49b5c3a080b9380b898c0ece4e651020afbd0deb48e93e0e6ce4b1d0d32009432620b12a3de28682f5f401fc245d5479ce03dae4943863846177abffe3e8f123104304325a394b66adba0a43978d6f9701f2a6d1eef40e91e029d49a2437451c94669ce367a8a184679d0358d2b4d30d925ec42fc68946cd6ac69e9f69c7a6136c9a0873f488d644cd800394825b6b79e7d82646d7274e62791c78519ca356ee0eae859bccae1bbbe94b04e7621c6a64bddba023877438f6684b0cc8d1634ffb20436435d4b2fa8615a60222ccefef7ebea06ab60aa06f0a55d988268b9b2b1fa9a3473a6c8b1f0c53970cf975ca6ffe3169e5354bb47bc202d9c6882c7a75687e8e780e0701630638e8057dedef34d76dd2ee2b0fc043eef9b9178241f09c70c52c044070e5bfb830a808a225a6aaabbd9ba614a06569660a7bef57ba562ea5ec96253fd28acdd782280d9e596271d1f180c2826e5da0ca74088ecb34909bef5bf82af42c1c682e85f40f44c196b682f9293f516d33a911560592cf09982ef55c54789b0cd8c9305b1003601b1459ce2b8763f1bb1c4b3381bd3fc09404127fcb64b393239d5d94ad2f033d76db02a65de13126ed1f29a74273eaa249be203fa2d71f5d8df8c8dfbdb809b772c18fc166ef9248c955c2ad0b352cfcf7f604965f0cbfd77d1463cae3c3090cd4f9e243893f86a79f6ccbd105d9c8ae9c91bd45b23d390f0894c1c7f8700895fcbe476d32c81e63b4965e5961bf1da1f79530091a61211954e1391145ac0783f2296076bab594d900eb1a55c2788b85532127d5d6bf8873caf504b4282e0c44c9a311c57664a9e830f8df65e7103449305f25eb58bcdd09997779d50ddf0f64ccf84bfac75e106862354243ad9dc16885fdf6714d80c5d2390345ae463b0f1496959b1beb16e2a82fffe72e3a2255eaf7b61bcf1b138847f03a5173a1be0a880fd71dcde6eb0389ef0b510cf315378ef630715aaa7e949d11b39cf799540851b17d878ee31afbdae182169aa88285042718a49803a53de44ddb4ca67577087c985327c8f0de09d443278165a7ab147fda099d94946aa37d871c424062a40505a72b6260925bfd0f7b89c3f40e4ccb028e7a38918a21862bab675c5078890ea6d0e675ae385fc0f8a391288c183e9966f796013ae3626d57eae6c6b0a283064d0ab0bce625366e88fbf7faa76eb388a3016793e00e8ba077ce11c4e291a2085588450efc9b491bc5c80b347f55411092a10253c7abcb54dd71f3506e2a0d016dbe12c2e39d928282afb3f15dc1678bd56b6d738d5c23b98033873a005a5909c18b732950f598a9febbe9f37e02cd266681f946361c27476b3755821235f602508012ae4a11850ca8d82e6a52d129fd2eab551f237edbd6d94471636f98dd02d2f70ac810f09dc0d8a5951af01ecdea90593a35216ea00fb8c9d98e4d44a80fc63da821715c3383d382157a08cd0f25de37e9fd4e296693e5cb779a51208700db34a6fd01d6a27ff8cc642ce28715d063152ddb9aee249683a9076b2bc65c094b19df819cd39f75e8db1ee36db08926203b1a5a7b7e3f2cab4b3f0781cd2502d8dfae186894cfd35f2e46e20da552f8acafa30e147bfcad23eb48be44002201f1b468dc0bb78731afab1c517cda4c32d7eef5d1446aa25c82661a73385494022e13b78a77762609f556b24fccf00daff5daef9e11b4e575f7b79f4c675adf08fbb5d702dc016fc6536dfdffdae0529fc913ffca30f14d33237e9d89843bbb027615e83a881aaff6d830f3ca7b5fc00bf7a1f29ad174541e7cfe519b1e490c0b9168ae4c7ce18a2e440db93facf5c975cc87eb21608052b89bc8b8f17d85140536167d6a2ab289ad6589a59a59eca1b0ff66280283f714550b4a66802afa9e0afec7d0ac30ff4421dc271e2cd85bfee9a6daba6b25d2b47cd490387e0a005b0db3754f9a85b8c5fcbe652f3e3fe06c32ddf68996b6c2481c14b17d79084889001e4ab1b17557bae8026f79e964015a881cac1a206c36785277f3913dff5fe80a1e92ee904aa435f16ab56de80d41d4cb03df16968a2d6e5ec9915edaa3016e058a08aeb1828af1e883d0f0bf02bf4bf1a45adea4cc7db12e60481e0dbbb9d70b7374a3e04cd550471aedf756ac455f9bccef2d5ba2c01228961b96324edd6104fd5b0e501a84a938c022eb9096c4a12d0c05044c7edbc08af34284a0869bb200f5fb45fe7c822f005369d3e2cdb60ec52ee1c768225afdd6644c118593c28a018537e9d6ae1dc1963b8c94fffb8e21f5f2e7d14284464ad9bf63f24324627a0b677a2ce8f7938bd33cbad6bd43cb7152f61fa27dac30abe71e17076b5d607e06996905842095e1efbfd9609d079984f07852eb3a9b44d88d523df3cb505e1400f11702a597e3ff49d554f714ad58cf15176519d80345a297754f89a99906e5024ec98759c831060b969de1cd300c2b056b4b74cefa5ccf42e299858520125b06a39b2a7006d5dfd68c215efe8d079b808e15acc4bcf33ebef588a0dff6651d0ff27cf1e369699f398c90ad579cefc620e69d90bf958815bd95af32d1862cae0cbb0291b8803d3373ed3fa61765dce4e8390c88f2b87f20402f9c9afd9b16b0097d8df6c8d76685c0ec347b23fe475fa7a22b8088d05ebbf23661876d9582070f2bc967585e89a1d95452ae7c45b45c21ade91d86261507c96d111814e2301c04ebf3dbd2eee7600dbb6f97c9f7d29279314dbcb4eace2397c69bb0d8a1cab30b74b15cdf61d835315333869634da09f1b7b7852260db3c095b728b7f9b985105c0ebfefd7862460351e64f7bdb60c2de2b12e5929988484e29f79b50ee59430dc60c920c35f845504ee8e73fca0f090543a89650415a408f6f4c502545230e0dc85f82b1e474130b8e0854291932575506e1be06b095a160ff6a59ca7ecfbd0ee1a81bb1880146165dbe3f2397cdc1691368303135ab36dd7dcbbd07fa44f809eef8536136df17d1892845c69ce6fcb56082f07ab1bfc7c82305cc4aee468e0d64c58eab31dd4da98bd8e1fc62beb4e2a5f88977edab6199c5ba430881832f065fa99026b983cb80de95070806bf74d6afa348159684f6d2666a95be51905205d752bb99810fe4be25b91e912aa90020751c476208ae5c5a73ddfb4d9071e7021167d62636d8fae2cd6e9ec9a78b18a70cf2050b1227fd57e10b2aa0df0d7e0afcf5d4abdd7a375b1ac348579e9818906d212c56b797bcef41dd692581b5430cf0e8ad57e2a5adca57aa1614d70c963c200e9abd3929310596fe9f480fe2940d08ce73d27cea554c5ae2df01a7db110900b7680db3d2ba9bbbb78562cdf3e7036daef1a680acb0c3075358426294fe4f5b8e24e2b000aa6f718e37897d93c80f68f0ed839a79a2f0cf24d9d90128a099670ec7b29e91c496b98c42e542b8d20b8dd50003c5f6a2a020e716237d6967bb5ff705f49222b91e7005e77a6f37a80b9189ce6438fafd5b437a3d214b36a13337c28271a4f57d91bad643168219bf07c3f974c86885e35c082f338409d2221c12cda2a0e95ae12efd686bb002a7e50a0a2ce13095a563df428d7ef5b58d0012b2404d2aba12acdf9139298a274c610555b33e591e2113b5b617095435a3d944965f7d9ecb0d0b8a20979a761a1dc0009843202ae616ce0f0d519be666a3837a57aa265caef6aaeaf22a78760476f90e30dea3020ee962e6bba48ae1e259c05b1fc431cbacef06f6d00c52c4aa36bf04bb6f6c14c4c09e62d89840b97602b946405031e0dca5b500c517745156226d0f31df847ccb8fa498f23eb35444fb610da91f264603bf30485832e5654004980db21ddb460813c2fea9c4278191d1856dda30c07a35b283fc73b5ea7235ba3308948bca9ae2acc4c1c049609f12b7353d7800d3c41d1e96e83dd040080769a20268df14002f7231413da7ec9608c5de26a52f0481eaeb0041dc0c07943bc3df0157f4a709fa6d83573ade87c0ffd52543b3a2f45367331f601097d7fc061e710b2153edbaec3909ecc9148882df0c5617016aacef6c7cfe1a8e7951beb6dfee485f5ee6fb64142d9a4ce8f239875eb1de9d6fc8ee2582e4d6d93f4f181eaeef0fd8acc6a3a8ca9a5dd2cacc72086ec6ea51b18065649029174f2287d84d9d7d02721ed8f5a1d5c9e5374f9e0d3e38c857a916f3f1535fbf88e6e84939fb7a1a0aa76d8208353c8ba1a60dbfbd60a2d289fd2cb16958911faa6d002bbd023f6205071adbb5338c69218d3e234e4b7ab8e6d897674a20542fbff904830dfbb7400f7a213595583ee1edd5876d6c61120a2fd206d360b9846751363ef1e689add70c5225a1e09ae464b8e2e6ee78b99442edde7bc417cfe86abe75c4758376a4310149704c0144de749b97a1a2a5f9cc53050770a015119eb2b85ae8b968ae6e6904bc8c80f27f284e41bd99c5481455b5ccd83f72a6776c19007b979229202ecf013c5c196d4715fcc835184a8b02b2d9c2da7643b728f26104a3a2429808bd9a00092fd1cc54d16fef3c03c9eb86457fe40b31847e3b345a90d51f6003338b0a0a52540b4f417886f5d5bf5f66ea7e49dba1cb49d1475d3db4a1a3ec43a810cf081c51945426a400b4d8983adb460293ad7cb21dfcbb0d179f71a49e393f70570aaa225d038e2fc368e191c0830acab2078dd94a7d43ad4ed283dbc6a647fbb70494d9917f472d45f81eaffce36f1eedc6649ff087757ab50a1f8ed6775d4c06006c89106ea39f71080e478b819ad2a0cc4a0c41713fb2885211b8dfb073888f01c04206479154012fbac61723e4afb95b5fd9ebedade876643e6724ecd88dfe0191d7706cec12180415ad9b8f12a8f6e665505362f6b68c7ca0073803cd1e8d0fe2465169eaa8e8017ec4f68b11ff0156a2ec40d40f816081e4e54b84548e7008e69c134cc9a0a6cd41e0bfa4b04e31919a54dee3b05d386a78f4aeee86d34d04e5295ef54af73e7af6478c43a3746115a5c9daf8f96b41077ea8febb40077a0ca01524e66c9f64bce5b05103f1ac578096bbc5e1a7e49025b96adb05380e6407c91079efef8f4d0de368e8fc2a11f3b05db6c7cecd792cc6700b7fb89499f0035dfdafe180751f30a806b5157dfa8690a6459d054f439cfd3dd4c225ee349a00be001ddc9478ed79bc59e61a39d1bbfcb40e6c99f2e906d7788fb70300933a0901040fa12df8ae6918835a73be8c99faf30b8af6e106f9cdfca58eeca47cb401beee195701f4886085ac0586663863302229cd2eb722a45e82164f3391b5450bd90230a9211dd3fa2188703c2470cbaf91838a0ab50a9a2e0015d15f80021a0e2deffd968e983930cfb1d6538460a067be21c7c0a57dbebfcf53bbbf7e446602e3fe20634e30f00b9859aa08d0f1f8eee2571d67f6ea40bc3fb4138b5a9ad202df91ec4107245bdab9b814b80ca22b03771c38eb19ed006775f1df273d47a203064cfd0a38db0067172f2a6fb33783bfe1a7ead8d1028d4794c455b04baed50f7a6bafe6349f649a5dd07bacc684e3592015e2be3b9aaf459b4efe848be101038cbb662e2be7cb268462732da6ade53948a079b4153e76f4d1511d07a08130094c26da5f03bf9b62ecf42f9dbf2a63f718eea65fed4751c23d5abd3fd8384006ee51be3223ccfa2b780bf48b308b893ba17a302a997176a4aa7e82cc24bfe10c5fc661f0160be7c5945c0e42cd7c63f45da6042f17e30b7a8f0bb3dae9568e03acc5b9ce195a140283a82157fefc34fdf683f2b6892f57d7e83567065779180a8b923708d6beb0a6ae8a3e90334f726b8d33fe739f27b02fec37cbf630c4ec0b22fc6be11f8515d76e21424a2cf5827de5fba539ebfc07c814a930839b5ae00aed274ddb3e11361bdeed8b96cf350f1a72bf0cb49f3009a4f6d5d23ac34f1301e9223f15913f03e1f36b4d8662b8d43a7e3e82e9a8a8b3103279f26344f1ee0d07a189b4f63daa9e4bd7e1f070c79acb67d77b8ca9752cbe95a79ed06a99050eb0ed32dc4c5262f50e42288e553362fc6a00b60111bbfb3ffd2c0430868d800af1aefec4ca9ee05b546914bf23c889484696c96f81cc7d2a663e0d5a419e700e7ef1509793a1b6bf0f3e2ef341d567965a6d4cb13e5d6310b1a68629d2bb300ce4bb8fe77fe8276ad755a453cde52e097c18361d09a0f93edf130804c45bb80466c969e4c3344b789511db4cd0c0cfadca74e5bcfcea2d42a298d12b1b6de009482453b857546e5a75fa77e2b4b3b324e4cc5aeb2549610803076976beff9d088b47aa87fc90e181f6089b24ace26e0c5737f9b00e693494df8a2cc69efc1905b9c6f10bac8a58923fb36cd3a5186971d7e3eff1c5f5932da9225fda6a2fe004d979f67a8330c962d4affad4c7586142e3f22052c2dd3084e2761e3dd8c37705277a03b704d0b77d8e40583651ccb42fb688a542142443eed14370658fe9830a40a168b8b610b7276d125f6461218b857be33a7fabcf6b3a4ee3ee256b860e01d105948b0aec5bb26762c55971e09e1b887742e20ce8a78f01e5a1695dfb63058693182e645037eadfde7774dc58862e6684b6f379585231e93f24a1052ee30a8591bb8c63a00a9842d068b8640dc5f215f36dd7701d39c314f33435afac91062de676bad65ddf83f625e953d0f68870a669cfbea4ab224c6cd67313d3fd0d0a2dcc63a8ea490a5a7a8a1b7a574d1cfeb9d841299f50b765fa8f5fce1bea950eaad88fab156573f3df6ccbea2789013ceecece30f046f699eab3654de112190b2fd4c9b48ed365831d4ba25acb1d68af937c774ad5f7e30297bff92acfb8e3004b38dcf8e4a4251561ebe6c293c2bceae67729ffe13ec7a083daa8360ba287096fef58d8427f1587fe2ae5d44fcc164bfe948b27e950f6e3f9d06d73a7df9c0e9592fc45e74f7ba9e016653b14f45eb6f517bf2b22f3bb72b5991ec1f5fb8a0079c0b4db7a4e5d2f9aaa22c70b406a4b9f1d1d4fde18df2170e2395ea75eef051889cfb5ef2503409ffadbe87f694205b06bde87d23c5e507ddd391f8ca21901d64ca87f9215aabc4a1cb49da3adb0005a8b4242d4536f80bf8266ccfaf8c1056cbe6628f90190250eb9e3676caaecc27ae0a6301c7d9f9fc25e8b3d006c4507eb22b4c79ae2960b79942be5daf766383f68ab3fea354ebd7b23bb9c1b176b0244e31b52d4584df84e4637d5ba1a940faea0ee25810a993f07d66a3efc1abd0303ebdfbe40e4cda14ea88969c44772aa2b265ddcf188f35dc5438c6bb136f30d75d180d13d53b161ee8553fb5fc7ed38dcefd7d7984e9b456025b884994ed50aa557ab6acfaab75ac57a6b6a1cf177b61b723f4cbc741992ef700c2cde573b0357c5dbe314c04499fc760540bd4779787ad2e33fc5a731d21b0a5081119a620ea8d8e3916c46b32c79662ed645f119306a87c230256f3f20bb9fed072619d00e0f54418500de8978bd42622117952a9bfffebf34bd9cc387db6be539e067fd0223c7adcba2e91d2c63d789b8a61a0718d1c906f88699ba470722b8bc40fa530f1bad47f5457ef2d37f4d235ba3ba168cd546715f00ee530b7c2abdd51b36b601fedfb8737432ab3e53298d5d4484e14d69cb4fda02af1d99e7c6082989316a051c936f75657681e2546e94e7e12987d9bcb7ddbff3528fcf8f4b50c0aa7b0e0edebb25bf24d4ee50577671e77962e83a5631a3cebd160a51c1bba007642b690bb319adf6babf5ab0dfb2073abad58bf4d844b2f167f8250a89586e83dac53b0c51181882f825adaf5377f4f84600d761747c546dc733528dae5d0ec538f09806fb2255989d0d5fbc2cbeb28ae8cb94f83e0a611b2b8390834655279cd6566a0dd5c33d897002fb0ec0da0f0843b826df5ad77ca896b7683f200650e07813d709ab72a5e53a98502e74f1293f9aba46018206304477072b7bd08613e7790f920186c3c429813c1ceca0782073c80676ea0486f648c88b21e71bcb1c415db32905 false +check_ring_signature 430813f358eacaffdd0c164939aafb0ee6d271eef6dd0dfeee6b4f90900ebb45 7941de7a455752b04ed21a4bb9d2c68d3fa7020744839fe74c29fb29ce779c1d 11 e02b93f6202e1de914f400789e8694cb37156f83d03bed9e5e33eeb4acca139e 0eecf2b8cbc7b5fa478ffe87d4821e9f9b174e22b75e2a553ebde3b709ca4369 705b93826901b0ee513e84a384066d6eb16f66ed0295d76ad899ecf940deef49 df6242f7d44113ffd5a6c4f33c11ea6b56c86bb38f14ad50962731678b3c628c 3ef9c74c612dc587dce37fa512762c88f8fd920bed47b4f416b92792a3674f0c 9b4e8067838eec91c7fabcb48fec9ca8123f664df4db3168c61679e15828927d ca7eef5a5dd580fc1719ec90364eb4f846d3ac108cf880272c794b233c603d6d a746eed7def9902be2c1f8603561c9a3f8d3e0f749991ab60bf36105c782bdbf 699429fca0e81c17c37b90b7a763b905711908b5238bed7249754f8108e0201b 64678ae8e04208e47c3743fc7500526e390c3b261c8d921df66c732e5e9421f8 a14edde1c22717b2d377e6509861b860cc01e9034069a1824a73de9568d195a0 d03b5b20a0a25c49a18165f4b9e75cfe0d68702d74d01cf94e169d9fd0558e09fb1ae67b6d0d9bb070f0755da4ab78e30355331772af68788527c053a595fc0b1b563c28dab602fabf948452b8f3393e25420ae72a236738d106cbcccb97d5010371507ae105a2cf060630f40dc6e68656420deda04929c59486b2cbec97bc079c937dec3301d6b7641a79cc146f411bf9dcb650a82342f9f2fbf99651d7ec06f161b3f8a37f816aaee3b8f0de76a391eb71b0f3284d6039a08d5ee53544250ba8655eac7f779b27852ed37e5486e13dbd87eb90e1069cd14b74a62ac10d1b0ef996e8d636d84bd3b163100f87a2a6fc78f1dd87ee9352efadf22b14faff6c062b7114cb2f1dc3c969c50978e93039e59ec58058524454cddaa3f3057d6f250c713813e01017e7f10262b002951747afa8220fe064dfe9b41aa29df3855bb30846a6be00697d6ca4826ab6d8019fb6c44a18ee2b859c15b112bdfdacab0c5202b5037c0cb04002b92c4a968711036f5414e95b01f138b16cd3f33f558bdaed0909638dadb8540e1cc7f8cbf9e65a98529ce3b2c607106e78601ce27b9aed53027a465e02f15309f0f7a6735c3bcc654492de2e2a7a2637c3ee4a7674169b5d0f3149ad7bf84450c4102c32a8fbf059fb7e62da3098892ba549c6ff22fc322b088e8f13611e3216d9d6c7dcba369e967e0197d65de713eb020ce7abcb9feca407b60bd3ca2dfc0b453137226bb72c56d2f0ba3e3bd752808b0e1b64f819b082071c598206ae7a03c632c42a93ad24481ec96bc2478092574f66775f3b6e9f4a0565574cd11ea0130d9897b1064298ae65c54b6b7059219a5b38e0084dd25a570271740f6a18b5a3e350cdaa2e10797053f05e19ba5a85034e2565b44a76a5fe0a180eb1af76f6b659bb2c2f89c1064808639ed96cc5f6f66b323eed7e3ac6f10ebb10c3369f27b752a81553b29bae9690543aa75075f4f238d25b8bcce3380f09 false +check_ring_signature 4797ad9a3cc701a93c31cc216a8273e9506f17058be43ff43899f38626b74eac f186f4c4046b7e938ea53856d86004c121cb35a060a68878a5cfe2fb97ae2414 1 d85655c5eef8c55376b5b517c3a95cd5d13bc5fa19d4d11a434dc1bd1efbe4d2 6da2aa513acb05af63174340dbce57889b1c5213dfaaa1531752422e571e7a0825ec6efcbe42e749109ff05d218ad65c10d42aa81dd8a3ca96f79e0c2edf6b0d false +check_ring_signature 1333f0e8bc2b483515fdf3ebd4bfa77dfe9778cf07e8a11b3c18e476406ed77d c0396ac75026f1a4b4274aacf25492f506ed4631cc189e1e124be4cd51d56716 22 2c37d6f191373bde6a4217af9375c97e74d684617940685627e051d061dd430e f15a5a7a95e0c3a61258a90f938c0a0189f532431941bbf1507e9bd545eed078 bf20d5e08d3478ae4af04e4877c9126ea605665a86684a552d56910e0c19eca8 d5370aca04da1818a33ed2123ea7b6421fea29a3f9dc0cafec2aae46641e1a6b 8bc24a23c62cb9add92083500c8307fdbff84fa77f8c0bab369a3816017b87e6 8602eead9d4975053f853e1716445603ae147f3cab54eb45599613995e08681b ee394a3fd50556870ef0de4d1e1bd6a93efc131bae5b0f0c26c39a716a1286ea 6cb7f303ee8e87f759c09f0e7e6b179da9744285a8132ba30884a0c9b8649cba b806022b01e02765180d0963e4cc9060a3489043e24c7fac03b0dd583eea28c9 760069e17808486490837dc94ed2aac1145028edf798081dff7b52498f001dcd abf217f5394da618b9ea5fad3bb80cb37bdd722868e4fc3e7e5081cba5dcced5 bfbfb7a7c08622799208dbd834af77c3018946d07607dd5338d9956e286830e3 aa5ed3b67655fbb888e6cc1e5e1b605f9c4c4f15bc6a780c4cf5b2e70b93c4a2 1213937211e3e618b134a9c190fee53a54c67ce3d4ac852c9e0fdd0be1d56f7b 1ed9318a324d3b7275b3fbe7f21196a1009af5dc94e1ba0f6077621eea3f3603 6948ada39fb77ab64a27af09049d96b0287862fa15321ad91483cbcab41b9093 433229248f12a910dddb4d36e2f37757448eea56aa92a69d418d62d24ccd9743 30e2be3d04ab26d1e243815541ccc9437fe47a16b6b3f725f3bd644c051e79f5 8ebf5da7edc30187d73cfc51318239b752a576f490cf711fb90e4165ce0bca7a 86740cd77c22cc6b8e8a368e1491e562fcde7f580b652f2613f6a1d761b38abf a86a9a486524ac04d5f06d763a0258625b0849f4fd791a7bd6d0bf723e113a9d fc40455985811e6626d7d6f81d9a7fbec102e798812378e7bc757a44350b197e d149945d527ac8e4d985b97c930a6569aa0720a9523779bdfb11dc7b9cef690a0d5804726b9d2255ca418a6bbe6e7647e055d9641d518c05aaabf3ca72bcd60d0c49c5bfaf37e972468134058c2354141bd9b2c81b475121ff93c3e779c27b0cb29561a0b67631b9377a66c75b93470b0508fc954fdd17beb9747b9b9629410e0bbc9ee77ebbea937283371359584412b0c08a9102b7b2e8156bd21e581e8a006ef90cfa7afa2c1728cd5e2ed2ecef822b6f9bc195213eafe4ad9f5f862a8e0b93c19c5c0294311114816da16ccb23a0a067fa69bd0daf99623dff956c4333011af04ba68e3e30301c9db4e818f7a80bd479ccd30ea3b1c27ef08f3ffa9626021cf402b3134d9d5001e1b5e01e181c3f43a9d8d5d51dd3827170f5c191f80807b0281da46d235536d0584d09812b5b380ca16a8636be3c6773703756f121d8062f4c4726f1f84b8775cff60c24c8807cc4ec3cd4efaf0cf8c14cd1e467e3000414955f4453acabefbda8953e0f8ad7e9c6ee1ef7ff338b2a30817ba8ab86af05873f8b232eb3b63b74d72cdaeef2d956d2dfba6c8222da7046e9070ba0b13c0b2489041f85e6cc3a78eb5a7438f404ee90cf00ad2c8f0eeda1f7c08b9a94ab0742ed0672cb863b1fda3bdeabfb0ec52a23c583a58e7dc8bedd49e6ec2dc2c9037bcb6ac5a44f56d69086220c76e988bda5b11d0f4e5dc8cde108ce7a77463501ff5ccafb3b55eb4d73e2f72246853a6b8f712b93fcf78be2bd1d87f544ee5c078a6c511878572d92f7ad7006f51ae8c04dded8c4f23169a46eab74129f6a4803223f5f02f312edb8695e78311239a0fb3f81f2f3f82ea7486ed07d215d09d408d848eb013ddff986450d424911f18ed3fa0ee3b3978a27395c29ebb57d72d508432b1ade9162fe547440982478c6f0702a0eb861f012d5b6d33ab70bc706a804423c13f7a58da61406b3cb3d1af5a9102bacfc7e15029695c8b6a7203c4b600a37e7ec26cb2f0903ed850f768d0ddb9aa61dd4aaa3da26e8a85e4f25841a010a615ccb1bf7fdbe283c0ee42e3b8270aebbb6c45311f930a44bdcfc688c6991012d59ee247a5e893192bfcb21d58a4abc2f0b6ff7722918dce860d5452097e802517130793507c7eec37cebc8e8b13dc69da8173f554df065a359fa04bc6a9f006d41c2cdf90875eaa2ba47cb9c6aca496489d776113dae2a6db998bae1b6bd0cb8636a7fce8e9af3f0ef6aacb552675d7ea86c9afe3e6c961d58ab9188f4c701b8cf18d4bd51f92e9253e34cc98a6b5944b70c9278029e912dd4c35b29ca950885f8479364e82d702c91941a84a7fa6dc6ae7bc46033226c4297d2bdbeb36b0499ef0dbedafa2c6553ae8f5232e4b3fbbfc1384c69c5a364877d7f5adc61d20d5401a47ae4b6c20cfbae02f20b06b6029cc2d215079d35afb86877390228d201eb68eebccf538802d9ee35f849fcd8bd8f2629a9dbed486bc7f78d0d81d864081a79f45731f453f8de1e8c79b8d1285085ec56270ca337832fcf021fc34a1c0a4e56df190b9ee17c2236930dbeff1a78383f0037cca5f6ebf4ff08c886b09a015b3cc19c94ee115a837088547cec9aed489afe5a59042c83a6bb5e2229a4c809641d6e034d0e7416e1bf92b3249ac2137030b4663154ecaa1d552118f0d9f30f05f02eaa878bc78f6c887d6c3c730154ba9ee48abe0a9ba52a9523773f49050c8f1fee98e53e59fbb136a8cb21838f83ffbc4fcef68a650e88d687201127af053875a7a7dea2d7b3286cd7393d2d5acc6dcb82b3c49554c618026e1e93ae420c39cf87f87f79547b9bd61bb60d802e26bdc30f7e46815f98158693e35deded0c5a7df45e4a89a0f7df243c49810455ade7e86e757b94c0f69fa330750ff8ab077ed2d1ea3b7e5c4ffb376cab5b04caefb28c59df6d7a83b3e8d48cde67faf10642f0bb48309c90f9e79c60d733abd5b0940043d4e5e222ad479e50f390182a01 false +check_ring_signature f6ddbf4cba75fb15ff4a2bdee1a4a5049dc30b86e0368416ef3b066d11e2be55 00d3a321051d322a3d7736cd2b3e955fb7e035f2f6c3f7a054d45c2e829aca2b 92 45ce73796de4a70c36732181a66bb71ca341a93d50448d14ecd574aff0d46303 a28b8e71f80c091a06ee08ff93c083a4055928557108ef0bbd7de5117e57ea96 1a02895466626351ba87160113036b7d6846b4efa5d328a481198bbdf32aa49b 414a20e5a835ae2fab16a2d549160bb92369cd767fdd777b2f8999887f6c7e8f a5a36f91c2a6a6d2f0f1dc793ee3ca073c8c334b72be7cbeb035007bd1103a4f 5a4aaee27fe0ff7611597170d0c525a5ba61740b6d2d35fec0c9a83bf97b8742 1bfb4ea6e27201ec2425a51af44a74188ad49984196753cbf7c847e4c86891df 415b7ed263fd25f918d7024cff3d4679f74f9b440b522cb91667337699a9d371 5924bed0239175b32c6f180ce776d49b5785e28708566bc05a7afc1032f094cf 9987067777c055d79a545f656b2f596cf3db57dba83e88e0ead4b72c99303d55 08ca8385b0b6f3a51e5502d399bde49353abb7254074a084ba452c58c7e825a0 26e0ab6ca9cdbfbe238ef4221048e4951c60668bd8daaf7b6ab1f7227f2be6a3 3c5d5ae27b3109fc1f74946a31eac5238ae3a576fe2ad56c165c4195189df42b 7aadcfe4148979c62f937f1a230dddac2d8e7a69f34b99d8345de53a9e228913 1cc08f6dda9ace427dd3c7bf4933386b7d1a8b0a57a711770efd5a0892280e0c d9c19a82ac0562a993ffaa4cd46e8cf0742e9f6c9c4ef92e8e6060dac8f0e588 d183ed9f0ccaf876b6c00f47077e85b0d0becd93ccf480feb5e038769dac049f 72a7c68c9c50aed6433d92b8ed2d59f8e51f44a2fea049d24cda9a5fa214ff48 f8d795d0eb8eac34fbe8128024a19c964fe4cf591df03eb74e8bcd22d0862072 7370f2f97fa832872344cbd8db3df38cc191fb169654d76fd646737593e54846 20f5ef531a8c7ccec09e630335848eb6fe654c1243618adacdaa1b5f85bd4d1b ecea89c693dddc3d3dfb1f3bc59fd5a2917d70f82b3a301012ba9feb1b84bd17 775d01b1b844491bb9a333a13c7a2a18ea7b0d782f6c317fba685e8b0ae7a4bc 61101b6643b896bbc28c9226b9ea3c46d6ec23ab9ac6e380b1d07a0f24afd801 dbd7c5f1ba5c83684dda1efeb2d55cc61de43f9370c56a5cb9e1a43efbcc8f1c 094573186fb05aad25974a9df443a5f8126b931b2a3dcea46bd3e664ce3a412e 1d17ce3902179e1127160e2cca7d7bcb6e9521cd42d4d97bc9c35304b94d0523 3e30aaded24fd0b534bd6d88732730d73d1bc466d0bf951c41d5014500787651 97e27ee200364eea31aff625f4808ee20709ce94cf4728c99a16cb41e2bb29e2 775953908e3ac1f9e2115b5c68f3c114c1ed7786e6b9ffc873386197ef9f9cc4 897a9243a8a550ca704e562e9759484370ff064b2248bda989a8af6a8b3a3fdc aaee9e6042160157ba681d518528edaeb0689ea01eed4cd81caae72ace9f76e0 ed3afc019decbddcaba7ea48ad66abd7233d61f8f18161cb3810d5f5c6d44e0d fa06596fd9d29f2eec0c9a16bcc0e1addfa135db00b7e389e8d361fcdb305e76 f0e97874aa6151dc196f2b6eaa7d4973deb772c3d929dd6ea1de35e19725c662 92bafb2395e3cea6bea5d933891cccb00e4eb7ebfce7097c38648c37f1e64856 c9e15842639504de46d9f7fff5d4fe1906276caa2656e7c6e49101849c3434e2 f8f98c3749f86e1e29f6175070d79f7583cb69856ac55c3c64c2747e468a9d10 11c32adadb412523f63ca26272e5efd401e38ca6b4abc17534f370ee577eed31 2be1104a60ebbd139c89552fd168c3b5629d77aa7f9a5e2f87a0a87c1a8bd163 653c6a2db82d138428bad1bcadde07150d831775bf139d276112f83549710df2 ebdaea83f70bf7e8055f3cae0692b8bac1a2f62f501802d3e7272cacb1c85a85 93560b4e658b62631b3f91939be70d3eb7c4131ead75fd78aa53e9555ea70354 a4d4d07ca04f5fb574e6a1288c8fd1a5f159a43c07d93da23c3b567044a7e63c 30f9672ad64e15913d4ca2bff52cd76cefb74238a0895777e2e3e22d27e2ea32 8aaf0e2d49f9956e91c0d03de292284f453b6442a6edb9e0abcefb67dce33ef3 dd333547b5fa6068dfd13e0360367679144de24d8d9e7aecacbee88359f00637 70a1997ee4babdaafcd85a1fc4aca4dbd13207238f458a1fd8050ac6adde5ad4 1d8a5dd55f6e7d3b876db8a8e4e3cd9d580f7c2e8c63e9dfeb987337052b9040 cded58cc9e3d9b5c577d248657f112c06f59d9275b89acf83895a66dfcd8d803 049d36004c25b293f968bddf18d53f3b68b7c184c74ffb7aea66d3047b3a8fa2 32b82f3e720a97435b9c49c649951acf806a22b957ed7ed9c55fb967a6598efe efb5cd7e5d53af57cf428f8d3eeaa1df60287182cb6549edc4efc7659be45353 c911a7bb92770aead389711e5328181f47d0b20fe9b13b9fd1a5499e45400726 e1d0ad5c2e8622a8be3f85bc10ac0bd0bbd1ca6b5dfbd72b9b1660e71372a989 4cf5b338e332c7ddf7e6ddaa4050271f1216f4504e98769a74cd314f951fcf59 5c57468319ebbc684e29bbf80825450c437b62dfbcce3f68e790475851e32283 06b2f56fa289762d201dad2482ba25029e3f57170d7fa1c0b8039cdda9128b85 14019710198b8e4c889106a77067b4a2aeff827adde1c06aab65042565695e80 6973cd133f543e7d6f32dab42bafd0a44e8f1b496c656f6d7ede1c3ae7eed9a0 42a5fa2c86d21cb28ea160d90903b660c1d38eb01c9618efd5cb0b65bfabaf36 4f35a05d13c99a845c2a0af3f46d88dd8d0991bd85bda3e816e1a965b3fba8af 52dea9683112a60cd7abae93fdeb27fd395db5d5bf9529dab009d3bfb539957a 1521f03681b8699020adf6bb28b5147c3c37d118a7437ec0dc10d26988853565 e317b5e64fe590622a2b7bfe6d1d716dee9dc248e22564c1fd3a492c3f583327 9fbff0accaedc52a9ed93596e8988e7b7f86fd88e91738c84c21d07f20af0d48 14bb50b329c21a6f1da1ad64df1060b45036b82355228cbb9fdda0d3c66ac4d5 1c4eb79e5f647fa18f3afa2aa86a18c9a38f723bc9ade441e50831e94fa88611 6daac6094a78b1e649565c6218ee1b5ea7d09b5af70ce15159d32d8c717964cd 4c9619486ec67963ff17b972a9a269e5256c593f2f46388f953b113a6efd7bc6 f0a3f0a4da7dd7a62f1416bf0687ad0cc2069e4fe1cd7f984c321a6460f504e5 da5e47b66aeeae30042164311bc8b21b6f970e8493a27b90a1a15d0efedcdabd 47f8022a4053ed4ec80a2fd2e182d77ab3360cff47fea1a34f31eae2a749441d 2105c78fba66f4aa49e4bc773cf30d892df371a93f19d2fed2b2d13d9fcea11d fb6a6cd43b28197059c5cb3d90948dd42ac3e30587702114a23c36c4662526a9 9a759d9f382f50ffb118b12e06973cb31ceac937d27cf69ad09bea847cefb282 bc4882290a215b98e0933789a5803cf8c07c60c5a166d6be9f132ead43629248 c1042ddbd4fb628b1fcd6cad5b7ef88e78d7e8cd734c26400c5274a01375c48c 25dfbb3e611abf1641d281f5abf05cd48237518bb11aa907d205356403b457f6 6fa20f219ebe3f487af31d8a293f084c84acfab7c613c7cc2fd103f7042df9eb 3d2e5ad0767c43b68afcca67ae551df9b79ac9ba722d291c14ebbb19842e45fa 2523791d7a12a21a145c5aff3329a89befa264fcaf93053df83293529e670e57 d69bb906831225ed4528013aced2d081be4b7667e4dc5d9e299ab0ef56709302 38421cf92f9b3d6e87628a362c41b717e9809027401f9858aa98764710e4af3a 93e68e96d5c2477b7b37dcd440cbf935fcfc3656ef429a01eb2839d559516223 47fb58b3e57639fdaa7b01083b5b994165ec416353cb7a30d4c5a30be57b4b95 f846137ed5f7c84adc39b9b4c230f0c0c01deb9747e50c8e71b33c07b3b239ad 6088a812fb7cb5529101aad75ae21406da3c522e7441c2d36a6ebb858b53c7b1 6996288589c80d3f505cbf245e55f87b5159c5ecf625c7f7a92b9eab9ac0580c 18538af2df285d37f4ae92d6b63beea8e5142db91e380f738839750b53fe9624 afe6f21f34f86fcdb59c6e411ef648e0bade15c9b4ae6c8265ffa47ae05a0d6a 7ac6ca89fd39936541d7ccacc0958155b9890103ecd2bb104128b3cba972082f e32f706fce92961525c03dfca55fbcc16ccd5aef8757803883de1ece1dacbb09680ff3d5f4588355de370d3c05f576c844840c0779cd0f8a1b34e1ed013c7508304d618bc85df03b767b306bb91c193cc9b1975c99c78c25bea23c021402b80ebf5b5cd0a7745fde7f322e372b01fabee0b91c26e6ff8d09fa94fd3c792456054f756ce448f7e0baf8e02d19b5f5639a63391873918a016a71206ee46cb81a04ca8a11a03da4f15537e8f8e07dfcbba493b398c501b3aa1bd6a1d1ca499f9c0cc0c7526d8e444c461679aabb9ead1507b9494e27f737c1e2401a0f14df0a320de3f29eb3b3d6a867821324f9547883401dd726bd8dcc09a7be0e78095e472f038a40e41b9601658c9a924ccf521b5d79092a4eb8b0108006b27a02ba3b96010e364598fec604a9d46d1ebe7cc2752ce730a439bf599a169148555529c8cc5c0c8a70dba98e0b730dc5b998d21c8136f427648182788c32ebf3f1f5192534aa0f8b648a40207c4f3730ed2b82af1844c9ffdbb27d61163f14131776a51a0f6a01de17c7b6a28cb5860fb3d9f235d15ba3dac73a9fdb8c3045cae3ef5e717e5e0d8824e84b5458654fa223f283e7b1b460e50486b886b16180d10df3e728f9f503d9c5bdcbfb4125c7c382b2785f7faa09848b24a566671428c4a0aadc9b722f0759a712c2dba23d81a98036363a0bcbd4d172c91584803289894cdbed382ba3091eb50930e72b1dfad9635518c7acd8175d5024d78d09bfa8a6d16a737001a40ddaab17fc5225d2493ffb70ffa1b8d2233dff2922ce445225ecc628d3a277a403e1136f39d75e0fab342a5cf64c397fb8e925efa1950db16a8a5bc3237edf56083d723520e6831b68a0d6b2df36cce0dbaa3fce74e05945ce45ea4b8af81d8c0e42f0fe693adcc936302a30b5bc1a37d48d8b5b9b7eea776058acd50822321004e1a057a78085ceb27e59d7bd6577c42ae9aa588be01413a034d37eb458b7aa00cb431a57baa1001b216e909703c8db855224322a60e842a73322433c6db3bb077166539db7a0d45d8f389a31ed8d75eeceac41df54fcd28953cece91ec52b405e504d56df86c159765e80b12b662908cab811e226e4a31a53e1a5910ee782b03ed98f959676cf5d7fb14d481815769fe51e223a792156a9f42ada0bdb3d2b70ebe0fbc30dde94803cf063ea0c30e41f7d8ce8ffefa3fa6190a733d6fa2fd370979b28de3d643dbed9ccb8a497b194b97d0e60bfac9863e9196443d80e24dd80bdca0a1b7447a11dc5d340df3e199f9a23636f04e2533568dde9b69481d0b14082e1e6efa089ee2fea976f70cbb8197de0e4fd76a81f3a09a15f4d07ca51aed03ec95fb95ccf638fe8b2fce7dcf6056d314071d79385e05a2f7295dad5c7ae10b60b2ad3fb7c8ae4338bef1d54c323959e654ffff61a250cbe4622143f123800dddf1da4124b3c68102e5fc9ff9a9b1a9a638662729b2eb802334e6d71e3ca40af13b29d3902e462437eae9ae84bcee70c0af1723ceaeaeb5e4701e3604cca30916966084f9c77fb634e828e11db58897c1f5ab1c0823dc73faa817c1d1b36c0f166fd730123984274d814a950d80483e3d28c51e3be0a66d8a9922602591ed04883aacfdf5c434f584548a03db3d9aaf067627926cc1a06fbd46a8080737670c4a76633fb658479c99a77d412c1e20f12f0c26d5476dbac34da82fac02a9bd095a50415655d2da1cf70277e792645b14d558a31df9386a4cc7e287fd80df620c903ceb821f1200e2933ac616b4417539ee0cb0b18e336cf0fab3adc059c71a04e5ef133b223f4d2e73aa6a4cd74d10b6579b05ddc965be3d17cbe44660fd770556e001989f5e6786b1ad3c7500cde6c7cd30afb202ac5339a130420f4a925205dc9358d0a2db24b49f8939bf1cdbd95621f03ff46c5573b2d08aab8af6d7d80916da2ac0425c1c59954b18e3329a84b2001149038d66376eb22c98c15d4e720bc7782d7ca9fbe8cdd3ebb64b0a1cf58a48c2e1fe652a06a93744e4e85909870029b1fd77699483439ccfa4d7f9d4704492b46532b45fc2d14fc0984d72372108406e12f6d73fdf222b2f9e1e78ac0097723d68b98a66c2c0feee465e0feb8800bcabceb66013c513ff8014ec5463ec5a37757ab334a8a1c2ed2afc80f803c106df8182d7be2933880ad8e27784e217b5428f86ff44d094f8885b87e00b9b630a642db7554835afb6ea04312311195e8dfdd3e4b7c9f0e69123261d8535f324038d666622b1854942682c7ec335f8377a7ee4d94d0e58e56b4b28915ee581770cc795c4793f68a068678d3be5d366aa5f62ce616b680d7abd24cba3a7d640bf0d666213277889c126f140a21a630752398c4fd3af96eed1891c09ccdda39b970f523a9d0147e512c24f1d6ac1a346207b446ea504da0b0078a0031c9c63585007771d41612e18aeea26b82e49d166604740dac47d67449a61871fce3df87e9f0add02534a5a4d5ebada1e84be9215641f43053009b2a7e5c56423ca6201ba730eb76dcc21a3767b69e7c7151801624a9a9f96a84acf024f75d250b3c40480c50242bc4ea91a6b866fdc38ae60cddb5f0a5b497f9b5714af8806327c3841268c089e2d20e7cbd00f221686dfaa3f36db9dee501fe06d1e36d288ef5b87da23b80fe6418021ffdece752a7b38ee2706351fc213401e935ca585c1db5366c4af2e0d28f1ce16be428d6d0adab57716246f8b7a39588037ebc0b20da31ae1a5a6540736962f76b20b2cd9d0721abb0b79c8ae1bf0041ce718dd136b39ca0b71531c0b261078dbceebf1ac160c6f5181c17046c73843e07fbfd2adbf25122be90629022ce05760b6e256f78e39ad74e78a8972c38cd684e74bea1c080f38720757fe04c3f4a4539472c9754c812efe6e40f0d99a8fe482c4436e9b7bc2e471f5ba530efa9e7177912507acf169c905d2c6e4888ca56445ef07a87461e924d50caa2704f1c22a5dc3d460c48dd88a57ce1e9ff0313f6a4dd4be4b08414fdebcdb11a907f0aba3c51834b734ecde357eaec38b8b6dfb5cd9cd5b62dd3fc5b21ce8134705d5d5b9c9d4439013b0c6cc1d1a922315cfae7ea26e59a13e6b784254d83d7f056815ce3de4267a98a1880e1377ddc6beead919b8cc701074b42f0d773de7fb024169cb92239b35259bc00ebdb292a392d4bb3cf47328425ea4d8c05a704a1501d40bc0c9145363690dc835af70db29a66dfe3a1f0564bb0505dc18f77d47fc041041d5798f27fe616dcbdb059f494ee1f15a5bb37a7b5a540479a648372856033afd0ddaa3963f453d2f5ab11f6cbc62333d516a247691807227b0016b66bf0f875af644c83b3922cc2af18f5ac42feccdf27849ec7292bee3cfbea30381530160ab44518e3034a7548bbe03b6ceb483cd1ee017be10bb3e8fc01e915e027e0a7d5e4e06c05726a24b1631cedd2e865d713667dc683ad1477af9a4df961ea40686cc6ea8af6664c035ac0c4724a0859bde77f32d59bdf6da9b600363ec2560085f172d38b008deff673244089adbafb377e52718f38706581da2f312ae5236003c15635a825cf1a577e5a00d0956d583c31e4370e2976f913b2f464e8254890945f9ffe8e6169a95d6d4c3845816972eb12e6e698c6ad3687e6d63187515ce032e6c695d45a80c726dd10bd9031e7ff234e948cf3fcfc2a2ad451148b52f040affebb00e698b75c849813d58c34d0d2bf4f703b4b41ca7c26ee83c2ce97d0108f1946daf0b59d9d08356414c79c471b3b6483f3679ad9e9438f9aa47d5140f0db0e1d458e5ca3c154231bfde82b5eb3962dfb3519b74a07e436d260a93d5a0013647b81fff7ad1e648ad9e51194866af01089be40e556b97e7372a2a304b290755f82771c849f09eb813b49622c9a23d7833535c08f84840002de80fe74690011e8234c797ee56be44f3e4eeaf0d1df2b15d0c5f83d9321a3e36626203fb700ffd8e66c67449bf6d851216e6999b3e644df04f3ebd084c67f7f6dcf252cb2a0f51c6604b0ce9070c0357d3de2a3e9d168f94ebc73a539913bb56afc05600630f35c074f6f09d22d51132ed0ad06c356748cdf76a500d0d1153d517d49fb4a8034092701986648c7e77463e60cfccdf199816d3193009e4ed91120ae92251580f6184c66bedf8ce3e560e234975f2b5106912fd1831502f556dbbdd13edc8f907b61392102751059e43dfb5e103ac0590c3f707a71ca08032a0ad4ca00e2d34093e2aa5ff49599929c081983c0250b3d9eadce60943456fde2043ebd71d035f09d2ef2b347435454802cc466786e812bac3edf2d087cbeabc436071f9d0f87b0f17394ae6b79e0d9d08c1418a86233442359e19bfb6c81b610ec7b37ebe8a3d05c7f4c47548f6876171b10444b90de33eeda755a57d9829327f489168364b3e0d9e8c8e170d12ca01dd268b75f7820370e6294c4f7d404d0c6452e8814c11730f1c903c0aaa4438ee642fce60ffe90a1a6d3c3d36d9f1269fa4e2860e48bc6100dbb86a6a5ce904553e8e437be3e658b29e0f6d0cca0dca9d1e6ccde891945305b334ce9728ee5af8db982b6969af4dd6c29eb720b6ea09fa27236c65b1ec080f9705cc799293716be07b8ce9cad0fa1a4616dea6ea234117568ce9f160222e001293022e93f61a363ad96c49bc09dcc75dd0e702f1b7a9fab2c42ccdd744d80b5c997df2dbbb21657f22b3c36da2b198a672872de945a51fc794ee3000583102882eab6c2d9dd3dd997d76ee1e5c4420dace5317aa9c8086b43c6567536a170063e1e1aef1c086fc78564561a3d01ae673f846bc9ec498f29ea7a456c5cc720ef474962030737049a1ddde957ef150bea06efcd580d4fa2e04b9dee6dbfae70ea75d1acdfcf88d1d49fa93c136a8fc37ade7d4a04633c8dd1bef3478e04f01071b737d3bd5f52fd37d0d8ae836a5d08a304911e892a14ebfb16a28ddeb6806067a4424cf151d46e0b679fdb183e870cf121739b424fc04bfa4b148fc7500ef09b15c6415a6e40a99439d6c993e76c2114c532e9d95aed624e6f920898ab647035953caad9f4b7f7a138d08e5d79ca8647f2e3723dab5b358022b412d227b35018fbfe484549064331c13636456a3af42184f10f5fa76e7050125351a52dd8a05c3e76149daddbd6553c0ce8ce672e55e4f842f5517b5fd2a41f9471223d510096e37cfc5076351162c9687871ac9cc1428121fb1c5d94136ca1fbef61dd2c0029d7544841a9e5a8fb9e0c8ded711eaa3f225f83f7ea1f4bf3f80bb41198b2c0ed9765498f57f124029dc43e37c95b0cee2492af96c78ca352d601e76e4692b0d831fd51d083e65d8fe0812a1de3a372bbab270ef507316eaa87aa721e12a82057ddd5df6f61f32bb84961a4de24333a79510e5e97d3fa743904d7e490a48eb09fcc031c30d0e5e9838426eb2b182e56b64d1ec86d239ff55d2bb477fe201320e266515e5de3f0bf7e0c5415048141e5db8b9c137c9d66d9c7d59971071bb500d2522438a2cf1c115498cf5be83a560c4e71c5bf5a33eb2e97adcc943a1386201d79a63f1dbe5a314dfe0d851d6f3d3e104296f2582aaa920b74039d7a90b4c003a1f099f8f85fdafcc9c28ed3531035dd0da7cffed92a4ab4bc8988f9b985f0ea7d6eb8c4ebf6b435c7e5be8d48fc1a56f98b0015b87b77241940b297ec620031d66ab7add45c0cb1ee8129f8a921f81e2b257a4c8acf8f03ba49be1266fcf082c7ae417f17d6120b892b42f9b4ea9dcef3f59ddddb065eaf021e0eaa8262406c19599252e3024edc18f421f14391a132b5a7bac1f3719cc92f5b49680b8f504a6be31a188630ed471554f081911b15b111a01db83cba7c970385a2ef31f8e05856dbbe94e28ae14942672aa030eaa1b01f4fd033c0ebf8995063fb12b518b050dace5baa7ea3542f472754f27fc1d6509eb31a75598daadcfdc6f1949cceb09dc9e164c422132bbf4ed2aec771eab0e7802151ead0bb257246d975273cd110351a2b57e9becfc482092620efa6f34577c24ce2f49854b17a53c0dd100d8040b81b3c67fef1ea9ffb9fa8e82b154b4f520ffa8c3c50809a9fe2c44ec6cbd620576f1079fdeccc4a22d74ee3bbe4a48f0c704f1faef93e39dfa0959d06030050d4a3721955802f3180467ac8543a44a35f13d8b645e366961d734fe1315fb4d08ab19a3fb41ea808a6ad6abc80627458f78df47f90eacf84d9df1124d201d770e628cae4739935e3e796f5a261992ffbc4052b3f9a5f69c3f5fd3273bd0f48000deee08157982c1cec94f4023697ea8e54469c0f1a0545f03978abc5577f95200e49327a387d067e9566e1d58ea67db13017ccd71b2a1730d466aaeb7ae55bf0b8f2a2f53d8775173e9f64421f23dbc154af61c5e36a33d4800b78fea9e52d10818878611e571c08d2bb20755f6d31f4fecf2af0ea971cd40dc9db0f5c4a0bc08652f4e3a9982e210e75c0f126efde36967fa1d2b35f34708853dad137350dc01753e2c7081999c91e9c620c213c5afb578c2c65dec963e37fd0de9c2cdfa4d0fdf23a55675582a0a232a5b8b889f420e615ab4a083c91dbf2c1e367ddda59f010df6391b7054d94d9d233327d04f64f49f2ed696760cc904a7f6e7a7efcec40c9f92a88a4fbcf146356faf58cbb55d86c25daba3934a56f603a730026b6f350a0b04b13cd991b1ab49713e879b9eb3a6c49cf694469cc7cfaf0997ff3ceef2019933bee215117329a0db39e6283c7a219eede9b9e3bc9dfb6d24ece08bccf40506309b6bd42a071b018c970deb34bdc713585fdc0d998c8df2aad58a80371309341715bfd7f10d49ea74c20beeaa172432cc4de0fbd54df802a16942ce2a62086fc67123e35c6649e5947b5945416275e5f5f0e9ebb6913d95f304f068d524066a0f98aebe1bed1a674137d6ed33b2b45d87ab6a87dbd0fa0f03390ab90650039700197e1038bd59634b926df4042a48a86386b03be22c12fdf201817b95a002cd6d56d0ec689d2cb51d179c02c12245c586796da6de0ef6ac585aafcc02cb0f5d248b7d9846aa5f65c1748d5e649468d3de5043c9045263b6a686bc4377be094dca7ba7dc0d49edf2c48465fef730d1eca2b55e6640abb0297054de729a1503eed0945c1213cb16c2cd88d691408c61b2482f083e662b62ccd31254b65a1e0548ffcfd23982471ced80ddda38e473fd9054e39b8f9998b4b6673f99e6ae08030e4d3fcbe61a3ccd5129d4c3e41dd0315045e657522314afb0aaa12f399d120ab9af73938517f1206df962bdc7d3a61a9d9b3480052ce1c0c10aef5cb3e1ea0ed74a998e212c964c2c4db91c3ff2dfcad799f6fdc60104e7d24ba2b51490930e6bf28fad9b0a9a19df6050ea09fdb0cb399d68fdcf312fff6668588f186db80cdb62aab22e92ef6b37437a036130560a51ff485d96b0f4beb880e248f763ef0d20b62d94d1b6da6ac45938ac28ff56b0459af8ea21992fa9095bbb979043c90bbe42a0b6903f9b1a049e8736d35867fd0851577975b88814acc2a14a3bc4860aa656649ce874b0f4554c70c750f54d01f1104ca8caabdc26f54b75856b29420d077a0a89428eb8967480c638582d87e00dd7d6927210f10d08a8e0cd3e886c02f0c445f52c1006af6743723d17e512a1d80bd537ab39f9053c91dfb6f4f70903e91d5b3823f4087e80f28a865795ce3be9f61f675000fbb9ffb9de92ece059057f5313c996d764393ffb88727b6738005565f599c3524f7a545d16f73da2c2000443274c82e99a0c7ad15bbbd3741d36eeef92047035983e9836b26eb58718044e54d4c806b7d634eb71f8f32cd475e725f3766b519dfbd3313f5c0766c08702260205f1dc2387946c91750de6869f0233567f5f12ff3e1135331422a086190ab7c992d87ff93453945cc41d97d735791615ab88d4134d57fd396637835d620a5280b6d183e13f3708bf69b94de3cc2cafd406d87e34d7fd75bf1acdb2f1fe00fce6fe93df1953ad9b24d972098d2c146dffc28ce415f28a898c10f0a824c1027ba70d302bdd65a5f53d68deb276a9884382458fcc7eabfc41cad45aca64d002a42c80ad462c8283e2355c272e82dbe925238e27720d336366572f29bd59420f8ca9f68d79c8590a286b47491ba43d0ce0dac7ca88ba25622383bb567b46d60059be00ff954a112ee8d2e855d9613f45da8bdab4b93fed3c6cd1922622a55206114a7a080512642f67a2a4f64f7cf71652a79fe8bb2f2caf1e2de978aedd540058dccc28fa6437aff9829c3e62190f99762c3e51d96c3eedb4678e03e6b43609 true +check_ring_signature c54f80e29595be0c2da1b6db06838c8ae0d6d7069d59b48481d3335b9e06e437 4d263d0bd289a8b3e788924c97b6cab71d4c8367d4e91ed1dc2d6e59b2c745ad 49 25acb3edf993c2947a7ca9cf25f9441d4dffb3fe00a14355d972d83ea7b939ce 870e4ec677f8d4b904d8cedb3f81c7dac737cde2016d2a4b373b873df81355ff 01e48e33c7b974b3b53fec43a2a2e150b7c07cbda5bee76f6c4b1478537824da 056ad33126834a0acefc510f7c14ef8467622aa37622495822764049f6921034 a1f9d7dcfa1a4c611c971efa20403c0751465b3e2b1819870bdd40c5b1004195 c6ec4dc69944077945db1702eeb0e9574b6d61fec2f8aba58dfa8b54c9fe93a7 272f9ba9975778366b38deea4db2416c216b33037be517398abd47137c7ec0c4 59adc1925982e346b130680f31b4b8184bb4b014d2005e8da0b0c7f9f98836e8 4586d9d01242065955faf427d39bc3a9cd27d3fa938b2d5470a3c8f1a712dfe0 ba4b37d7dec5832d2f4d389cf505ae42e9e1fb4d84e8a21aa5dbb258530c7a19 841502e002bc74314941f745a3138ef016792774b07879ac4c7a973e9e4b5506 a36e57abbc4fb6d919a6565653950aff7c8b0809b153f874b6cedddce6f56a2d 472b932362a966debd9ebe28992e8fa70c5027e995f720c86979bd81b4ab5da1 b61279e8a5d49de7d1ca100675db18ab9a267e7503819252d171fdd0b3a5b14b 900c22368f258f29e3d5eda47a65adbed0ca5c35099b8ea5c55f2a7981475ddb a260b9fdd6208f0b6bb7c0b481297062608c9cae406c92bdd3f86107ba859493 b0c8e58289c9e6e32d2671bc6be7a9312f75fb39c898b4f80461e16acf8bd911 f191069de62f6f5a7e828e66d4687fefedf1070e46b0e8bd3a8b02f0f1bed6c9 6c131207ddcc6fc5321dcbb56a9b6e82bb083ee692301376046bfa35707bf384 da791152fb56d27805a626dad611a7d511320b3fe2033c54b9ae4a20f20f85a9 58fc7475cfb030018d14e79c08c52611eb0106edc97e55471a38d3ab452e35e5 d8fc3db635e2b0772bc2e7dd3fa6114a6388a8e9df688fb0cbbd8bcd6ce0efe1 d28fad5fe5bd8063c3bb0f6c0af3ceb1615f9b3d54ce86a4744f3f1b020c37a8 67b007ab3bda9193ef4690422b90505af0032b5d1d7198a0bdf859267fca80b6 beb29c7c1d75a6b400168bb66390b2cf147f810ab9db0c6bbf3ef4c23c30086b cbcf77c2cd1b7cc6b44caddd430ca52196f1b825d9597b07ca018ced19f7cc0e 2722129b733ee31c92709d8234417e9c4e96af288480fec4ae055c169bbc6e65 43c1a26cfd31abbd60e19d36fcaf48dc9987924bf9a1f9c12a37919f8b43822f 21a55dc79ebac7a313abaa174939a608f14b6b7d771c59e1fa2d6b24a97323fe d6086acfb8cde0d4f3fce5f52b4146cd7ad6e5eaff28efd50ce01ceaa7704b2e 6d951319cbc2781d6b10814c7f6700124f6daed2a1897dbf85b56c3292d60b11 2fb0af69ec6eb4ed31152fd86ae826215a8761b655b96b560e4846dd65db0bff cdae9dc6992acf1fcf63fcba9eda87b8aa6d354d1e91c2126af5f70fc3bc5e45 8985feb466f347a903031a68ac4fcec74f0e2b22d1395be46b07f383b653db1f d5c09273a7f09346cc1449dea9259c5557703cbb82f7f4633d77538cbeaecc79 5dafdde5c862be4eb6d0369a38ab2cf6f1ec1e1c3a83f9bff5a2da7a5132a034 e68d8c4dd4cea21c4415f1650a6f6651042d233645220b29ed1992e42b0a7920 eea796cf266d1ab140a664b833caa2685202cb50767e6a38dfa27c05a5f20748 e346b7cdcb180d74ac131a7916da608cb890f973d820663579637cee4d5cb0cf 8a84e580ee78825c6509c7c0ff979ce66291b997f154fc25ff6ea529adbeec7b 47b972a12f09b1a98eeb4a540403dddc3f6886e96c2a69f53e70149543e0c333 f397f59ebfdc46c8cf63f6728c784637a2608f935b6e62e5c6180f96043d44b9 55e96c895457bd88f06f1130c771cd3506cea50106edd5e0cf98b7916fb6e276 00343fed8092752966ba3aa4f33ffd531aa8aa4653cc28d8241e7b2b4c9850bd 2cc51709d550f349a99fd1ee1f8a2f21d5c3e58fdff8d080d1053a4f094be5ae ac50cc17e93ce9912094bc3561d450b0ec29cf812a9108b4fc041130c2ec6a13 17585d4dc59fea4c1d24a19c7ffa4d776f0aa645d1da0b49ef10be6065280733 37ff107ec583e3ad674754d3695bf141a529adb698e59cc7f441c06378bed085 a36d9fd1eea3bb765efac25844466c74ff484774b8a527644dea98aa1f8befc0 8a52648047581f627c7c9fad281f07207d1290a325349714a5e7588234a5bd06948f04e5d7aa7c804ab03b04bbcca4268eaf5cc34532793208353fb90773a803ddf8d0633d688c9e347e38a4e4190b50cdb56fca801ae1e1efe70140da053b01f88f5c6e2a1d92ba423500dc1e852069422e32e33a86dd6c22b0f9b38fd3cd0a257d28965aeb50c7ee762c5388793a0c75cf71aadf59bb9754e9c018075cb7c5a66763d3910a6b0a54c11117faa898e6a27ae4f5e36330a07ffe64778be6140083ef8ba1b187aa03664a2d311ae0fb4cb30f23fd5df5f1771f9511ba91aff1089cdfda562bee65b43ba7dca7ae0543d44499f1b22bcb4ab5b4bb9a26f4ec24092982f9293c86a226ff507810ce3e1fafe3fa7b89909e6d9f3cf1e6e54654f80690a622832525a5600d901018665a8b5a1ddee5631265d4a6d8fd0804db4127077880e721432d2fbf832c908fe7b885f00920031c7edbeea40cf87b520029f801020ec3ff567302ec0553c99c475e17f6e22e803e7e25701cf8ce543503722b089102a91df6159058d0ae2ac2a38b0072e1399082c541569dc907493cfb3793013957b0e1960d56b115d8a75a3d4271269a1863f3fc4bc654d70d3239e4bcc7053015c6c8994ddc9be1698b49ea4cc888600233966d19bd6e35edcc1990296b06917a192b61ae0c5de346bc89985279b3336113a60d77d800b39369ecb4c7cd0408c03d3e97db9f90ab3f35aebfe781f0cd3c837cd1ea69bc93c9ffb4f6d010053f91f7e5715769cae61ea66d4abc39f64bbee2ba1c19044ba8f5fbdc35a5510d2733fbf7af02d4865973558c1ba19551372b6ce284fed3cbc595234fc29a180897381762311aa7db693aecc945df628943c8fbf0d60f21a61a4acbc0f0f8b30841b043fa45541dd42d87c6a452e2033422f06e47cc839360d824480e468b730e423f2ee633cd25cc5f81b072122bf09aec1c852842fde752c39174d331c42a0f0246bb16a4c336bd662914fb2cdd07ebfd0bef65854f59d2c08b4a011628050de049e9d43ff225b596f78e9f7744796ecf1b9a6df95897fa6a6016159749d706c1d28367c531ba4aa7937954824a82a6cef39efb1578acb769af4fc4d29462065e124914b3608ad473d53d91be59e7de47d8d052c0159f0cd8509170e4405c0a4161571ee10bb0a356ce879e1c742feea4537a9f5599c312a0eb7e07f25bc0079e476bf3e23fd90cf007ff6f3a138c2f56fb04f6c0022f1940e94fda0d2b9b034fb178968e78365e9a404beaa84409d2267a6265177715f7105485a5371bf10fef3d73febb942b97e96ddeb86d1fce338abcaba921c997347a19b2ef5a34080485490693449b60bf02abdec608a69badbc844a8dd6111ea4ca17d17f4a381d079d73b858df75f3a8ab454aaed2e65240a59359ab4307335666ffebaccdb8db00d80e325de0ac77ef35e6cfebbfeb1ed7a56c5004d513fec247a5e11b7dc2800616526cbc0954eb130702aefe049b069076ed17940677b1b26e3b39aef690a20e75b9e6aeced4776f917645b2789ea01597b160e263ccb93dda3d6a5bf93a7306a1fb3f749635a6339587a15ed1b2be12a53880a0d27ba4b58a7f21c4430a010df8803bbffd07d2629ac3a8866179dcabd66b78406687eef7629fb79172464607ca79e4b046c7b3f9b84c22c496ed98efffa41ad924650b320b5eb7868f46460b14fec119a93b25f1817498cd1cb5211447b6d19bc0db8f343d2359f2cf0cd903592ea519ea12d2a8d4d2121d7f18daf806cedcf6fa86b8d61a5cc95b99d2ad0cb3932e5aba3486f9879895c58e53848963e4211d1bcc2113cf690c140ed4ef0036245cb1de283602edcaa95437a8f109b201504f418bb1edf5713a6c64f0040ea9c19ad730efd7b957e5c776b275997851fc8545e300a57d1a1934ad4102c0088971462c98de51b14211d0b010b684c0fbcbd946cf4e690205da510d345e070b3ba6d6a151af892703385a26836129968e3d8da86ddf2529852db2c237fee80a5f0c6dcf1eaea95f00aea7d4d0bde803c53d0fa2c963a92dd5e304eb36227e0517b3c011862194cf33cc8d33c204874eb74e839f3e7e1fd51cd52b188d49d00306e824051f7843340aeb456dcf19f53ec1bc86f5eed49f3c1d1272af5d46530763291ca4daf4e9929e8ecd8a216c827eac93bbef7a08cb9ac730183fdbafcaa3e99cef6461d70330f15960a474734b00ae82e92fd02e474f0823b023689db4b4bee438c679176ee80d83ec485c446f8202f466cc678bb1720541c8ed8ab3eb076c76db81c4f2be6e6ceaa72c2d685098fdf5c7c4d1a585eb4797e4e11c8c2b00dcf163712ede5f442737c06ecbdc6bc9b43d991847a34cdd95e79b805b34830f80f4c78fa78beae5daac6308354869b3d8d190e3bb1a8a2c3feacd827b5b3600542ce4844b4efa10b61102ed66bc341265fc623e659886fdeb2eddce314e1d04ce4b9a80331acffe941763e4e0968adaff6d43131cedf112bb67b77ad3208e0ebd27172ce63345a2fad790c0e2e48c6653d2b942a6e7a7e60d4d36edf869d506c850cc70fc3be39974757d28b0722c43ac6028f5b0a9b4e65e38b739e9fe160e5aea39ff28797e9389316aa7dc1d0ca7b45e0cf9e000804112630331f98c9c0a1f30a86cb642c96c3fad44cc69b65d4ed73d608e679314ed9a2eb3f3963c330e5958b43398963a8426b02c6ab561aa342aa24c1f7e7c4b9753ddf3ab26211e066989ea5e680944919facc833ac8609ebf6ee81506195a37638439095dc453f08a0cdb4ca4ca56a250c31ba1cf1486f70b6e05b8faeaec07360c686f80b81e80befad2f380531c9ec6d775033b1d8f906e9fffa00af67785e79639ce24e4d710d9c170039b65bbad78d4b9c60dd81967d55cfc204a91f84f11e48323388fc900857ddd9c0bed81cde749cfd401caf652bd197bdd69a8de0f04ceda82efba4600ce149f3d4a33cbe219f5d63945ccf8749b8ca9df765a39dd58db04868785b21063d32b2f3ae82639b0bd1f016551b5b62604ba81a393491aef4abbbbe4e7db9079ed16c6a6378b365b29e51ea0ad0e995dd004ca352885c428583ae64922c7a0ce43dc5534140847ef862defe977f5b2c87187b6e50c268bfe13787ab8473770b824fe2e4afdb4900b39a32d6ff9f032138b73ba1817bb32708a5d9e7739a7c031150796b71699296476422e0a0de47f3333cd977beccacaccef880e7a75c4c0f9f8eb964df36d738243fdd175ecce59ebcfef71cc8cf196e112335d1a23e090947de074989649030cea17c5a5e46d7d55c65f56a1f1fb92ad167dcb5beec7b0e6ffc28c47e68ab51e129426a0eb5ceafc3c5936e5b8d75b916e1d0929214c002552cb2f84605605a3c6cbb1bdec8e0812dd5bdc3760a4341da72efba208e0f0d0b1daa727236ec9889ef8a696ef534e30d30fdae912218221654127f311100027a0b7edaf0e49bc265f3f0ac832e17b3d6036420bdd81fbd97fb760852467205ff7f5accc194de6f2daa2440d6a37df227280dd376fff4c92b9ac145da0e4103b3a69b3f082d499defbcd169aa6c5841edcce7fa295bd5ea433ac27b3b09e30cc68d9fe5392b7e3b334cd4c9a977b82865cf4916797cdd98de930c7fd7f0f30f2126ca2ca6c79bae05fdde8d982716224ba05c6c7848ef48dea44432f283b40c8350a66a5fbf251ea6c3604c17fe8207b15bda6b1c989cd98fdf6da124c03f0d5504e966f423728e38ef7618ce99def3defd76ba3656830ed46c4d106f188708a0d6c363ba2e8ff6b52fe3b422c27cc1522b13cd17aa434e2222af5a6ffbe90a06cfcc2570891ffc61f0ee18c0d5d12ddf5952c35bb60056ac3f0f8e4f336509cf5619d51015ca8315a001901b4d5426f4f9d14c7b067be74378b86a0cef7c08af7cadeff4a51b4728fc72eeed123a3629746d119c9bbf3b18d52e0515c5c40cba702fed561d70b16a3a0275c64e242c0f7035c745435b63af92db5c456e3904e44d9466eaec508855e90f8695c3ff09ef4acd5a8b9ca1c5ef31fc69baa1840addfa7c16be5968e781992c084cb94c80630d02c68e46cd798914d829b3762b033141bec056e39d36e6c5da3657a917dbac96c49e466799b938d5fba439252f017f54e42929d32221d66ecd1878f35387346548c46ec58af330b05ede0225ca0e8de3c3daf86b58b86729d4c5c27b43f697978344acc77edf54fab046a667680a21eaa4c9f997a9a389ce98773cc4e16bd3788332f3b1b696bcf8e1e8704a03013afb44a5f296c1f4d359c402cf7214f6e3d2f620c3f2570a5d271318a53a5a0100e52e4e3fe138edce96e1c574c55c3df40d0ff5b220fb3d21e30803f794b40bf769cc15390e2a2872c096850f577e3086172a38cfdf2c54371e6fde4b357505 false +check_ring_signature f7e710a49d476f690de1e3665ce33e93a535c8a8a7e7e2bf85e66b9bc2747a98 37f209c332115738c28ea28488c7071a1b243ea2060b1dbc23681bd281852876 36 4e4ba73424880ff97259bbc080a594a57d7c90337dfce176963e919eedf3b778 470a269fb19d7da5c14540be60f5edb99ceb30b247629148758471690a0b2709 17541440f6aade11c54a584bdde185851c1f24a38fd3a17ced257035b8dc4e6b dfb3ff3e5679268336fb64e29602b5d36f743fb3d170e469927cbf26d9ae011e 575c826baf8988b3a91f4c3c436dc415a093c921b9146dabd8f49a70c75d36bb b97dafad22887bbc9d6966f29ccd1be2db7ebc45fc86ab93df036f3186de5ed2 20f19989ee8403a1ace4fe06668cd288329b21852e2c23043a9787f598d746c0 0de409fc21b2382344dbd7ce44a80133a52cc2d948c9a5b08c1dd002d9b8ce71 95806891d3d32730a4770ebfd1d12a91ebee8b15af46c51abd6c11bfe483c302 78145b858c8c96b7d8b5faccd6d5d73723e84b42cdeee51a2824ca4a72104888 eaba7b6d9b8600e5fda2f054c72eecb4e386a3464bd8c2ba631dea3d8ae4448b 35af9a9487723a9bcbae79fb481cbe46485ae5a1264d396a5c3a7075ef579a09 d05c4c6a361befa5807b53de4a53df7361ff2776695bef5a7610b944e54a533d 1d16a1d4499593279fa7e4827144c750c8226c4108e385feca78ab08085691d1 022bde7703620e74e1700d221e008fb85e7a9189c912269ca20d2381f4555727 1221e29a571d0de52ae160dff5ea1588c885a58253876bda967312a5241ff90a bcd49ac4a0cb253ef013053520624cb6167c637c768b2314fcc5f72870cd9771 0bda7a59c728fb1b941df5fe53bc9f5c7189863719f9d0442fa676173a01ee05 c9113d6f5fe65a8e7bbe1ae032078ec9dddeaf1d0d684acfafa8d5e294374716 48696fffcca621b9280a20832bd1d298e83183b3f87c0f93f4d08cef4a68bcc9 02a8b940d7725dd176dc935eb3599cbec368d041316d82030a8c328fb3c9b2f2 aee6ddf3039736c666a0215caf649f206b5d0216815ad51b3a27158240c0fea5 5d5c2df0bfc0b68dec21673f8da13eaf544c1f321447f9a9acb0d23b56c571db 769e1bdde8626dcbb9091713d2c0cf8d53638a931d19656cf4a3ee1661a99400 6dbd3590c9963bf03d1779f126d9c1a9b7bbadbb0958278730a085dea7bf7325 68562a2024f5687da4555f2a0a172196e74f11964c27caa93c128e1337af0aa2 027238b15eaf40e3f5e6e5fc9acc1a8d8ca9d360c9404bdf563b15308afbe6e3 202a97e122321995e42d6b1df6d2ca3c9e9c7f83d8ed5eab5614a345bee3fe74 e250b51683eff0ca37384d4cfb2a74a4586e4139f21a70edad84e8acdfd4b554 263102a5782da0919fbb264ad73e1a3fefac0aa0c76354f23f42f5a50a267829 5f54969192229819018d32f54d8766d4d4ece4f97c31d07a744c768f636238d9 9eaa2fe201b2b411e36df793501ed162fc8c292e99d4e319646a050a4c5afd72 451e6793df1803fb0d1d3b9341c009bd4308d955f054159301b7e297e002e7c3 40fbf79816108f46d1b1686afa567ff917b575bbbf547b89603a4ef34e041ac3 f55df145ad254f9dedd2de5e09f31febc4ea3ee305fc13c6727d946ef2ceee3d f6fb945cec5e5ab2fe2654413da87fbaf38d7840003449e2e1439c73ec78dfd5 4238f03a15a0829c8f568f93c48af057d89b87a0e0bbe466b5bbb2af1064cf02c8aec5dc56a0e9a7699bd8cb6cae0b70633b62821522ae1cc53d17558b84ce0c925f7d0301a18a9db89eec4ffb2f7be932969c300c929e2872c62479c5706e03aa8f91fe5215425df1b7c91759f64a9f1344ef58cc0c911c9362964d40bb3c098b904636588d7cfe2ec4ec2e58275e95f08cfd2840a983847a56e2f5a926750d6d2c406eaa782245e1fe2c4b4865e258ec0b78f43cd686e8f9449997f9b5630deca2ce5658bffa7cf83bde8acd3aede7dc3cfe3d92604dea4eeba2e2dd6dea07c7695cfca933763081d2f38081f72e50b74de18165a7b8cfa86ca6e6344f7604142a8e067b957b4e386c86f068afea0f2e4f02baafa088dae018c9eefcd665076af309421d26ed76fbcea69af4121a28c9ad57a486af80ebd2eccfa8653c3808d65191e6abd2511a525e9e2be640c9e6ecc0b8f2f05c1850fb887bc80372670fcddd61eafea739d311df93b36010fcd220d2267f5050e945d5a310185c22840ab21613cac5006d59ab0b3008a3a63ad60952c474ba21ee663f10b4652700780f1a7721a99c6985abbaf4f3943e7ff91fd9c3bc805bef9a9917b59725f2d6590181b207a23d7472e5ab096bd097652c6044aa8075ed116ce97a2c69f1839a6a0bd817b99a8a4036721c906451475e074ffcc602d8c3f55f7f75d0aa158b03680280af3066069b466d5fe4d957471d0e5c2064bf45075c315e103ee02042884c0064e065a479fcdc341a85d5e680045d5600db45d2656837cbcfe660a6361cb50f039f65badaacef46f4f4935294e7f160044c6af0c57720368da74d7116513902933fcbc96ce2deb109b7c11ccca413cd8babbb6a5dda073a03abaa5b99381a035f9d3a0e7cdfa6133b66d35bbf7728a3fcae8f02619d1a2a45d7b5092ea6590a47c515a943c08325a6168ef75bac6b10994a17a7cdc20ae167cce3c11d3d320f6d87dab18ec1aaf68a7e4d8ada1caed43c0d273e48bbe87117b90d2a7616b6020618fc02949742f42b5ced5b4adb74229cd0b985fabb32b8e435aafee1a76509c6de72d4b1b984093c2dc0eb5248349d9329f5fc58ff22cc8021c80a259335087ab3e5a19909edea9e78bab92503c9219adee1993c3e853c3786f2d9ab936200a51040d985b772233f54a05486e1fb6ee7541127aff576aeec7b8fa73bfafb0696bcc82e70b52aa3c023315d181a8d7287a9bf4389a26c72b70473f7e3f1f00db4e62899bd51596b91f3e8c39afbe2c8adc6ae5f1c557809faf1787281e45706c1aff3d4755dc48363b2349ecb12dbf702f64b82c6c094e4264982e0094a530fc915125d8108fd734263d02de1e41a2df2604ad5fc00f34d23cc77aa6e2704034c6459887ce4922e8fcff2b948e2a4fc9d276452c1bc313dce6178cbbbf70d01c625edd713a823e51846430dc5229871d1eb7ae3944e6e657367fac615cfe5050f7c94b307fb42c3c76dfcba93deee3b2a9bd6716a1eafd03a755abe5c00ce088e5dfda7e82518f72c10d3791c09b278e60e9f55b8c3726ca777b106e2387a06d444da407e8eca6434ba6b4ccf4f78111e5b78e0dd373904211d65773a5f3206f4c79898959831aea6e975a1d30c7bb3b75e0d66f12e1f9c9b42ceb9c88d490a2f32dffd0a281369a47a60c965af51d503a7b0b2ebeb061ca1fcb6f3503bc50286e590c15b9ae7bad03d25dd6ef82b73af00985fca814eca8591664f1ecdec0da95fe1d589cb9d56629c552593f57f25bb4a53778e1c8ceefd56b685d891430bb665aef1afd03ae60ce69592fff4feb3171563fd95d697c6c93f9f4c407a2a0099da2806422905afcca649c1496f4dd951cf29ada7e19e59fef339a72eca6b072fc0d492543eb9b97ed9e39a869e57b4c8180f3af4698cb8cc9f349f122b8000abacc817d1eaaa289c66a098b41b635a7f5bb0c11add8ff7145f2e3bb262b80643125a2a46dd441df0c9154930da28e3469c65235e0e70dcbe626e04b672070cd283e76f77412d5d7748003013b23ccd2ad846cb04f93021005301ad5999370e72dfe906210bc8b8dcdfcc2a6677c5e2e9e722419de2690632da1c1dd256750bd29f2098c23c8238c95f10a71e02422bcacad2f33a3f7e91c27afacba5dca10f2fb414b853e9636cf3475df059e56d012e4ae94c4c2506e44445393d16c9c00dd4b43dade4ba271a384e2ee2152d7ae5ba9e02af643ba725da5c96a9a9545a081589b09c03c05263e7f70b831f7917c5d737d5733a7ed9ff41b33c03c908eb0a2143133d988c2e9a23567eb6aa3ec83e87f34c3c5d57d1c0ba780f8905b1270641a88ae2efd97cb9e9fcc508732c36b78217269dadba17929c25620c134cdd02252256b40e731049b728adb857de36f7afcdbc7ac2a1be8b4e9d0379a65137019288c2b5dd30a97f988c4f1fba90e2b79f92c2063f906cb1b26bff168abb1c02f9d2581c4ab43acae86a7887592be85c6c3ba502059f64b78480b5d974d1850661df5364eda3badc319e62c41d1cb3fcd0321b44a2ca23c59c2e019ecb76e90fb51aaaa9bfea639e207da216ae2b81a2db0e7c1c53978405f59fe407c4caca0fa0feaf3ced2701ab21cc053be39bdc88961eb26757b4c3eb2b9c1837f505fb008661b135adc79887b76c6c95d5681963a122f0b15744e3ad8169a4ff1e9c99098f84b4d0a01e47c7174bec7f3a189150bc13e1e9930fa217322d48154315da02ea061c58c8475610b569ddbc667d35a7a7174fc16be9d52f88d37e274374040e02ee749ffe9e77c362dd5ec508ebc6d84b3b056703edaf5d67b78887ae9dea01083b2e1843bc445dbdd7e206e4fcfa36566a701b5cc3056eca78f7f65edede0351c2de270f3c602e22954f9efc0e0a55962e94f2e3cb3d18b6beaa41c57eb30a14464555dde723d9d7f1515906fd9a89ad0c33c8738987bfae81eb8a382e7206391dc73537836ae7bd57b91196a224d7dc1d9affddef176fff4991092db5ee0bbc68ee1caf004122edec568b1fbff9a4afbafdb0b3fa6762d43d68dd0fe3300d217e3e0785f62087eea157866531a0149876fe95cc27846617d6c0d4c663b50646c5049ba0e83ca908b3750123ef6bc097b7598a439297bbd2493b6e86664404b27cbce3d533b296181f6d3f7946d0a43dc95f7965c0f988c463c782a041f00b08b41e0bf0a77068007f2de43a11fb2161a2d0a4c4ad51164ec894e18b168a0c true +check_ring_signature a3c5831b71d5dd85cd5c947683dac464f11752797c1024aedae16cc56f836777 176a94b5aba61d928039228be2c1518cff9d479c127495936590103b5c2ffd9a 44 fab4ae18da66f5cc3591674ab837e5694f3094948877dc9492de71af89ed2dd5 82af65683b04360f7b6833efbe5b502029519b827b86c8a5210a1db8db69ff97 01f39d8a67a2485d5fe32abaaeba0e6682bafb60e9700b791aae035485010d6c 8632a8473c561827dbaecbc5ffaafff9098f3843ed4c2295c766f0edbddad7f8 05ba1aa280c1c41bb338f403da221d21d67a3f19959a2853ac41339c97e29c93 9876a544f9a64eb467980f08893bb59547d6eb80ea1b1e22177eafc6094c5988 0c8a3a49768a58d2c4f7b836537113ca6b6582c3dca1bf862f6a8f7a9736f552 f04cb02939d2bffb205d2857cf24b970c8b25dae32f42c8a1055ac9c7ee433b2 cd86ef686bb538f3292b6246e53901a3d1a48602932783fda4d96a50eb9976e3 a7d5613555c591f6b7a9c36b1f053200af8759b67ed8b30d14f9bdf0edbe8043 712de41d3db62bb6be4b1afa1ea91402b78897b06c96403da59c75f8360d103b aa5f6eabce66997c68e308c1ff692cef89b2f432733afa068c38cb9dbc056e3c 2b0816ed73c834ef7e8e388d0275da9fb7508e97a44dd97676de5e6a2bc2c60f 913ba2a6b8e0f16095dfee23a3fe3dec57971d3d15acdafe9beb33c22083159b 5353720a36586c31b8e3442d7ee689f4f7897781483416a0e3a6450ad7e36f0f 3abc24e92eb294cb8d94a7edf446a831cb0a14d5871ae188e1ccf40aa30d77df d58c439ee1d83bdd6eeb5805853d8ed03246fa99ca738331526f334ae1d8866f de38d0d9210d0c6d78ef8a9c6200b49212b1519cff4ff94eb8800e633aff9fa9 ea81fa6a38da9f3be55e063f2eddfe40da00bdffa2b1569ef9286374d0d63d09 19009b61eb71b47c0ac46fa3af83b68f1f8a0469c8afd0899c7a2886b55c0b7e 7b47b9f388564e7d0103d90d3468e7701524f90c75d34a8cfd9fe9296c765ea6 7ca3c9acecf92d015a7a8f5f81f75b4889d0c777dd381a0913ce62f5c7e1d888 c2bf3f3cf51bac89b71e981ee05600c45ecd54a27cc4f4fef291ff1c4e698ccf d8b2e109e93bed39c67eb027d0f5a7d14a99a3701fb77e32bc99ad3be8d15e07 86e866ea76bcda379f03737c4356aa281807449f462cc86900bb698cd2c003ed bbc49971b70823a68efc7b82352e21582cb758ff6bd5ac27300c368a7ca1d465 377ee1c8905f8d8b0625914b27daae7448ddb2b3c51aafcdbfde32c8e86e30dc 110ae3f232eb306cbc259ecc535469e7bd3f28ca93b304095b68f19e04f1b710 b5b1ed4594603b4f70259c24d0c1fb6fca499af78b0abc6a7af1e1d04cdf2a96 bbe7d7650a955f67dcfd118ea70a16bf5d0ef276f9f931b91d5d8d2bdb059233 cdd6a657187b8cd7f62f51c31159443162ab6a7f1ef7fe7ac320f505cf439f10 b33e241d52651ab81b9648a481a879cdbfe370fefc76fb00759ad154e398fee3 151fbf1a253508b52b813757fc16f041953bb61628374152bb3f07558aa8ed02 1ddb086d7a3eb59ee4f10f85d9498e2dd685f883172cf6793753b34014c43116 cf5a8a65ee8c066613c1978776fc4ee475d00122e2f52c47d9697f2f5e1759ec e65e98cb882af61ef8cb7c95881e179516eb964cf7d18eb5dd7003397efe5e70 430f3d75481ab7e08c3e4b460a69a3e214f38027e40163df4c742cda0ff5a888 ce42b2ba7dcbc7fc18ab772cf8be587b3bed37c9e4702ff81ccfd595dde30b5d 5ec2bdbc507ee93d5956eac4429774982ca30dad59588d4d3e8efeb6578f4e17 c87ef47b1d557e9c022fad6bff876c326ccf3d9661e948fa921f301e4d60b19a 548fbf31b073b81d4aa4f4d36436dd22fc3655dfc52d9f9c73fa4b723583815b 0652c4552765263ab8bc8a43daf519b613768c2e59a77670c7854baac2c18c28 309e11c1eb310a57b54b1269e72231a13613edcc42f69a2a94a613d30e5ef517 2021f717f24dade4772cfda838f5b7238c09ffd1fcf1c6543905de454abda6c1 d5a15e4d0f47b998848b890c13d1b041b8988429012030d523d2ba63d3d0350dcd7ab91f28b8da34b269201138f22eb396bb66f5c40bbfe9616dd5e2c8c8c90ca944c42d0a007de87fd06c04d127c2756fe169187650e24b18211ef04bab760727e45203cafc90e7f17a1dd023365908e5a17648367d90be1d6aa272fe82f70d63eeb560c1a27657feee304d90459683e5b5a158ce7f7b695c7ffe55bc2be304ea2fe519850985f701f914ba9498ddaee0ba245b80e89b4f73d8e9329eca4d095e2b244754605e71275105bc44dafa4763342e062d9c8160659841bb6ce8230b9c7396de3ae46c0ec09b8262abc2b7cd2dd51b8c61038f189ff20cd385a00c08986197563a7d61bab8705f6682aaf7b9cfdcc0efa1e33d5b1f301f0066d52f07ecc9ccea59c34769e59e0fa92486969e1477be647377c968c4a9c64250d2bc05ea2474d158f41beec3271b4cd97fd11118b27f3d4d32b911905c86459d99ec006926e5e13243593d2b4fa14cbaefdf65b05ab380e8fe44dd21dcaf78ab1a770d5e0a9e8147a3c8f660510ff78a38b04d7cb5fba9dabc9bfd961577e52ca8d8009da5c9c99c7a3617404f6606c89c1e1154dd7ffb24938cbfe3991988568c1a0f9d9aa8064ef5c005a5312a37904bacc461f1e7b03853a810e5e9c4308d1d150e024e03e99b8e88002fa05a0ddf20ba968aed182c65e7fd7e05062c11ab0b15052073de16de6fa3e20f070d05b820310d830f54f6c424759f227d6eeb89dd020bff6459a55e659ffcae0b478a6015ff80cbfd1d41b96b811e19b54a746d03d20db7d4b365ad960a1b2be0b26206cf21dee986c3591fce3f9afa8c0ed8f133810c6bbb6cb81176cf602750f9d1b9d4566596281372bace04c196c2e16a4abbb00c1559b9ae24e4bc5b2be54b2e19d6e7a65b5cb23dd758b9efa98190e805a47b0f9fda14506329d6a316d55adcc204754b4e19d3e04d3ce0e4e29495a1e05e54073789d20ecc1cddc768b3be11c4860026e1044808fc0ae4165e7d6817bb2aae016767d491b8d27426c947f500b2852d998efa531b52b6882b51e136b46e88190d2d81f1764a9738a3d52c9e896322121ce0052a7a8d8c28b5783815dcbda0830900310e12cbfe1235cb770bff9c512e22bc31afc0614aa979dfa3c3a89388130ed28549b8b703bdd02db0bd901215c7346877e57ff26f55b1ed26d2dffafcf50d478e6526b8b0d96eb2721d4a2619767dd4081ce46a3bbddbd1185d646d5ed30250ea5a94c78146bd849b950e0a880544b827f3203ef0eb1f306de1f7044ad40ac331727299f6504222bc5a5d3e63d4e09fb3679ef2d1f8809d5cba3ab16da40f73dccbe9fab58183cde30bc598115a98825bb50ef2eda0b9bd821876c712bc06ca6671abca5f3e9f3ef4cdfa4e5b1e7e5c7d52a9116b6fa52894736c0640ec0ca522ea4e9cc1c04cf4149cae62baee7186e7710908844104f2df51dede2e780eeb8b3ce1e87693d124c825ee725c9912ea13abff7d93bc0a23bcebd9180ac90c4fc79a72e153e837acb23e2993c5ce3aece4d89baff585f1ac1376ea8c51bd051b4ece3695c733fe0717fa67e22d8dc59deceda787c53b88f231058340a403416349a592b023ed686b44f656245b7e20f904b46da51f9e242524b0d076860a03120836b11e1578a71928ce80303a4c94d52f622d2754b5f2cc55ea00cf64240fa3632ee4dd01b39e19e170c59b01f0b1dc6e75099419a019d10e30425564f90b31b18ce92d13d75d60d77409515e9e759ed7be21eb14bd9c0e797b495091fe0b928ac27e99724e223b8d6604f05497067cc02a62724b588d27fe4aa50def250006fce0dacc19210832548de744ccbc4b89f93df744ca1f46206ea821393db10618f44cbe7ea87d02dfef89bf39073e4499b6d281172bbb13f53cf9e8cce9d307beb706c18b1c05cb9108638d4bc6a916811e4c8562c54be4e68eff045fc5ba06198bd9bac9af820c131c0686532dcffd5bf5aa4b9492df0c60b51f0d7b4a7300c3a88da794ab5f41c2eb8f685771e9e158f41aa51fa5b61dba5d0502cdf3ff0a4ea78f2709961288858b0b3ea9fea4fdcebd941432fc2e2a2582e595b66a8d0e2c39cd676d0018264c92bb0a50196a96685047e0bdc397533199f3b0e606fc0b2deaab7dd5b7c1bdfe8cba75dbf2a02d1449bf4b430fa905344cea5f78227706339511ebf91eed69087eaabc4e355e1d0cc58ddbcf5d92cab750da7ac82d830b916e387392e441c9c03813cb399658758d2ff017ec975a20b67027c05444e20f8c50d36087dd89c169f92e10411cceec8acdd6085ff43f72ca2280bd92a143042bc450c4be593d675a99c561a4daa7857963a57233d3c50407d79d89db07ea091df12f03e8f44460de6a8010b2708bf0e68e18345aa6cc9fdf68505af7956d02ec160476f6a30f107f6891edf577e45f2160d27446e259095eb8f012183b2a0b07f69508589785d5bae0c2f5b86c7ccbe136e2a44bbe9317747ca282c7bcfa0270e08197c999f7821b1b36cff53cf17afa938b907a083b3c52cc2e6d4e0aff0ce529ed2498f3b5363912a999970bfd38216114aa1d1461147e6e253366438202ab763fa1196c35397e6465d34e602df7398c915af7a4a261d5630d12480a510c249448d1dba67e6013aba6a8f7e33b5f7c6a66c4ee455f4fbd3c3c15daee690c20cc5525dfed3ac272025bbc24c9eb20559f65fe5bf7c180b65af62f60c482037703314358be171a7158ef8db33127155140f6385eb4606f83d15bcbcf122302210d6991e671b64e04f37aeacb3afa5b81a2bd39c855bd9d22d2ca405d752f07fedf668338ded996d1c4439e12188a1bd108102f5375165d218184a6c295a70bbe09df328627959ce0b0508aeb111c55b28374c44b8a1fbcd198daa5847cffd1129802e6c5568819eb7260c88903d7794170e1d5677927bceb74d5434c651e0bedbe0d26a80d1619a71079b75fa905e39c32c44de19f62a96c1da53f3e9c8403fb653af48469b36c71b6e90b84bf234caf4489fa1a70c2f101b2cc7b6c3661071cddc8214a0e1ab8b8fc1aba8d057944bb80602c79e09149f210797cbfb28f02a0d58f51f68f0b9caab47dcc19a1639b6225b8f2f2bda78ef6f29fa84e5d4c0c40a65ca8ca18f20045574b2a483a1255e26979c85a88201245d9429325eaaf094b0e8833c3fe6be41506fd954f29b21237d9d827cadd76b2cc4674a29e6ce302100131fab0f1a091f77c370a4da20a291fe0d4547194cc4ce1c225d613f2700e70ff05923adfe7b5fdf64af9fa82f16ec5eb8c8441a46d7317bfa7e8cf572b0c3a886f36532b2a86cb59329453d7931104006dc639cd99777d0eb1b32c18fe0a5503db8c8a7d995c710509fda77b11944d1bf1a5802e3508ba16b3e19c114803bf1e5547121b5d1a5b1a3f8ccb60771d8d2641c894c2091ee2001792fe1beb01f8d49a796d800f59fd66e28941a7b9796040b18c30be11186b3091b117b0110826c445a21349dc3caded1348f066fa2256d6f99e382f3391e558b52114e6290deb31e8aca6737e487f13ac9c5c6e06b7b4e457e3647c97325aa809dd85b11604a94fdb84b959458fef847c168ec85ed23e40415f980b7a52475f0be71027ea03cbaa4f62bf3e91f1ab7b9d9856d9fd4baf8f13cea5662627b859c14813c5c0076488646cbc6693d20a4ba2e8313e3a3b371c7cc83ae39c5c1c149c0ade808a044b3bb6cdd7991956b49af04c8af372419e143d4e93a79255df10a2bf608bf1f24cfd5d8e9eacec4bf56a1ad4e2765b0a0fbf787f0fab776843baf7e3bb8684093249c51cf1d47ac8cf6726e31066eaf85b16020ad04321895d2c8fc0f32f5d0cec71bf3044c001c8e046e9beb33aabd453e13cc73ed5d498a58aee90f743640720fca0a16300a861567708b51ec5237288e74559eae539f7a4e25199fd7bb807 false +check_ring_signature c6baced02f46169000f556f07f3d310310b693dc7c08794a135ee819bfa00de0 2d5016ec4973fbe251b21d72a5ec9c67ac600fa2d758e976853392243a11ee36 8 696d7b29f06fb057251d2729ddad4412021b24305ae6706bd355c91fff027c9d c1f4e52cc5d6de4826030ba5b398948e8c79e1a69cb9bb98e5bde2987bf39ba0 2686ef9cf9783647629811f5a929e5ab8c421026687dfb92d8d051fef6cb678d 910fd4ef9dac24c45a41ce931c3e70e6295fb5f93a76db095475c9f72e2e58e3 7a09dd184d3134dfdb21c817a1b9ebf5886f944ca4c0d85c787e990c65f3b966 9883440c4c189ee2089e6f4cb730359b7fe717839931b8fe6ea664fda0c567ae 4af4ec08f0a192283a2436f04b16d3c7da77c04b1de344abd491d2c49e6e8051 44e72a71701af799b79f40630852646effd5ecc4f01b481671db7bf0849afa45 aadaaf1cbc764c0e043589e85d5e0f7b13c45f513e9452efae704a1131460a0882ac98db5873015aaf6398af53461620fab9b3954312cb4dde714ad17cb8d3014e6ba877aff86012159a3e03cc15c3868eec49ed86880611fa4d71767b29b4018ca55a6f83caa6824f4da837101b341f2355e8bf047fbd10f2c9b3d87ac9d80747b5109fcd9e63693cdfa9f8b3b0f6f4ca8ed95557e088ad783dd973419caa047898927c8936358f52a366d439684d0e94d4daceaf5f5dfa728294e1843cea0e90e052528faf17a11b0a384b83bf5b3c81f94f8c9ea2e6dac13777878458460733749ae1f37b39b30a5307fc97b1ed9193b7385758958b079acb79bb367b830de7f389bfe5e5c001082a442da2f6bbdcbefabf8f67979430abb946f963da7107f3fd3313e20c06c242a04ce657403cfaa1e396abab0f104720acbb361501f008a894afe0f33120c4600887f09f081bcc1f31c5b36889a088b532767c1008ac4d8e56595b8dc942f511374cc0454fe69a0a9ac1b5387d62fb9372ab10ff4e68046fc51506f9e4066c8fc56e2f942c2a65821594737ff5a91e104029fb24593308c0dde2fd5930ac553135b1a4f7747ec8067d623bd20b69e3272ad3d1d6784b0ec3e2d0e597d32e892b4ba58ea8ff41d50e52b25724129afb491d7c828c0a580ab973188eb32bcd98ead056d0cb054f3f8087fa5ff2e65cd2a0a6b4c186bde10a false +check_ring_signature 958be19c272ab716e6885a697efe4377c13354a7fa905fe76ae3afe80e09c87b bc0891f775ebfd97081ac453c14c29e375d77c248e6092d6b2abc62cb85a4af3 12 ec9fc5024e8ceffd93c41bfc064c8353058aa17fc737a1e9c8fcfddc85f3b1a6 f5a57063cabdeb2defd59a7aca8b7e58d14b56ce683644de87ac164377d9b4ea 1a5448796af46d103c3fe3c25d0771e5f471615f5d259abddca02c720112d76f e1308da3ae44d207939abc4d5edf2e1bbba111995bbee32a493afbdfb4d2e719 48a46dd46abd26d29114d603126b4576711d4566beaf799c1983dca39d2a1354 e4474a814fadcb34e58ebef9a0555431423aca772c46c26b3ba2b4c362ed3485 97e8d2231840fde007f42c40136f3a55a50fc1dae4307f1eb8bb95cf9e188441 18b9fc7583d616672c203a393bf1d346da185c88ca22af16ba6eccdcd119d443 84397b06a9270cc830ba273be9af09401365465fdb45ed94469a9305c77ab122 f372aa3ba134a0331d5d80aa4abdaac73d59e9d33db6a42b0aa3b44d437ac306 1962105a0df8b741e7d0cd8df7f632ba70d48a36428f274aa4438dbc44b07adb 08a2a376b6655114b771018a74a41352220625dd64fc7967cc47c4c64c37fbfd 96f923e5d7171a96fa20828f7ed738ac95d86fb7221e74b13f35cd5289da0c0c4f5197c3c0c785e0adff7b8515a23bb312db4c69878993979a1beacd62d7ce01ea7bcdcc7a6604f56e2034282b2ae754d85fe734c0abd5680ae559688d56a0030ee50456966de19c777727a29043adc049909e65018159341aa7b593566f600a54a2cd0a28e0f895e23ae70030c0e74249d2afa792315f98816733d232e5c10f9a63afc88bcb62170001013441105725316808e9adb62868af83d733db6c2c0135f18b580dab1efd38f97be49d757d92f77934cc48df2900350c8a056a414c07255d9419229440978cfe536045e0c8a643db1bd06082a3ad635bfec906bbdf0ecfc2b9732d1011ad0a60e6e1ca2c6960f1c108bce2c311103a54cbd9e51dee0246d76d34793f77c4eafb97e26cd89fb684584d7d3d52c836517df3840d4a7704ee954b3f8fdf5b1004939f32627d0f53f25b46f2dda9515e7db0b985bd06360c0e3d55d536f7d1c426e028af8d2d71c0581f5b20321a27bafebd30841628fa04e9259660f1413812571d769f7bbf3b7fffb096452a05b004c225adadcfd5aa049dbc1a5ae13aac4a10c920af3b021b7642b22bd586121808f8dd60f69fe3510f876111a762344d2e8cdbb76134f9bc8512d6430f5c9e9ec11c0aa2185f9461333171f69f570bca394f1a0f9e60cb33a0e2083ba8eda00749784d1847c50f4306bca6d5311e5ddd3b7488feeef108fbe6022c63c86813704e08069b52ccd2d0058bf497c6aa66b70b82f7270148d5756f4619a80bb8ff86f3f12d952df5900707b4876f21041bb87502e8e5e5fc52508a5ce4f950d9839dfb01f7984e48a91e01f7072a99dc73fd550301e215e3cb5b4059b6257320be8ec338df9f214da14c0b80c014c4f6d0f89dbfebbae5a8ee12e79a4748c20c48774b978aad6a8f963d0f2cbebde209d607d73c6cdc44c8f6580f78ae0387bdb252ed5ce43da9e7f8ea0cc100ddba1fe1ecdac10fe88b8f7d107e22dfecc4e8d81d3d92700349d52849091b7cef7a8705d998d0aff740727aedc37d7ee474c0d09249f224b5c20e0a4c02 false +check_ring_signature 638962809341104e99956b5a3df75004d8de578e7f9705c7f36e449516283793 cdf1dfbe26f842431cdac0d500238ab50c131d76deb9a673ed85072fe44b3f1e 14 c8e4c03750c67443e146e0af8c3eeabeff9bf1835b435fb1edbf5408b9179b00 822fb0f4bae32e7bfb802b9192f669409d4bd08c46b5a6edd6964c63f890c6fd 435913f8104685748de515bee9ce641756bb0c722d06391cad7c18c3c38ed9ca f22e8180f6cc62a7d12119835e2cc477d072c84035b09f0af3894a0184c5c5ab c1cac4fa953be7bf30f19c82cfcac94531d7d561356d38f8b990b152250da4aa 1141ef2526bd03a0a7509a24ba29468bdd3480f14f413298cc5999c09593e3d4 8bac66dd98256015b3fe9afcabe8e01eaf6a4045bf7353e55765b4e21a36d52f fe1839d0009770e15145dbb578466a006ef201d1bf64b81b487190a1b1e4d162 12900c7d7ed5957b9d715285d0d2560f6ebbaaf0b1422d44708576c12d84217a 9e1ed1185b7feee60da0bbb52b1cfb781dc277e0b63beb726cda9090b83d283e 3f8aed952d03693ed911041bad4fa6042accfc80c0dad9b78c34482a34e50d0f 4b238b8f52424aca7446d4be69821ad0df6cfb4c049c83c76e5c5ff45a0d8c70 e626379192781f8c8814e259447151c6351e8dc9bcd1e8e7c007277bc6393c2e 1d61e528df6a377bf10e7fdd347e58409c816cbafcd5252455dfa500a1c91893 1a99c279deb9c779b1f942befbd01075062e5eeb88dbce35202e957a8c2fa50049e69156eeee443ed5a9764042576b7df53506755b99dfb30aee61747eb9d60c6d641ebee060bfd12eaa0208037c5d92acbf992fa15534afb73c2833d4a9940212c0628659fe324c7f036bc48b9c58f0aeabb0b3fd638124482741412f3988033e185854c483e9d7f438ed4d69f5f8f2bd2969669a55ab016224bb1b7b38440614cab3c2e2a90b456d1429f24a127b764e25ff85b525bf3030ccdcb8ea46740b8e0ae585ebba0f1eb197e1505d55945f258924ca3b6829ca469c4a758ef88308e2d66b15f992b12e2b4a6a5c75d814b587e453ec0c3d3d4263364bf1e98eb200806805d2f3ba4e89c2bad69d7925c7f6b8d224dffacb999156b13a404000630a5a1f143a412b730f3b5653ae9038fca6fa6ed73712b0b10cf9ab276cb372c40ceb2845a6f1192f0432328de83fa267c5f767bf01b720d2d56b068e6aa4d0e4075c21a445f7f7cb00a87a46b105551acb6adb9a7e40e7607528738f663b92de0a05c7db01bac2d527fb3971ba110f188674f48d9d4aacafb88c299fbda93d7d0c9d1442b4be534815299941b5a1f13d5b0eb0cbfd8ae1c565a818bd38ecefb804796f6392f086285f63d740ce257509680032bd4b9cfed3b240d8885172194008dc6f9eb84610316e41fbc8a2ae2b43db8b1bde5ae71cbbccf57141e3f41cc74bbafa836317bd4b415e18041c8f46a32d35a0b0c3de27ffdaafb961d54f541f06657b1e8a72b3e3199f9bd162131db6116c1683cca6d6e1dea539f9ec221cc9053478df5116116106cb87e83234db516a3adb9f6628f0df4d22d5653d9d3c3b02140d6d8ea8111f81b34b96e73b733cc29f6ec897e4d853d8a72e9b3b18dffb05d924ab78dce7392204f2775d993fa1900e4aa1bef644dff50c42ac06f456aa06941b39686fabad30bcfc0cf1d2a09dc842c5fd04f3b46b215ef080bf93ab360f9c07a0c8ac40eb51656e804ae138de9eca47e1ab63c1386f7abaf49d4a97650e89d97f7bf1389af6c2aa647e52ecab4478c234d066902e51a25b8a4fd106ce0f80d0c6596091762b38c5e84956ed950d4350d27830423a385598f36db337b10a0d98f3b102450c6b6ad41bba66ab18e7161be5ae6a1ac912cb6feebee3577d0c57e4a580df3672563a3ef6ac1b21a8a628d03e202cf1834d708d4518641c0a04e6e45a347e3a3535cdfcead4dc82d8534c03a52d36c515e960b6db361290210d false +check_ring_signature a838511c3c562ff494b60edff9dd81670cc5c243fe67e86c1919b884e083aba8 c43f606ccc4ddcf8014b4700db8db953ab8737b82225b54a814bc56cb1ae7e21 4 14dbc7e41c1ef8f56493583a5fcea16422f8b9ec6fdedb4c41d05d092faf12d1 77f7d896fca067c8c9fd769acc900422759c976ec4070eb20045bb3ba66ec7a9 6aba991db54e4d8400700d439b29007f23630e74ef052cf4a9dcfa5ebcfee10f 578edc3fc3b9dbb29d2e7f75a633495e68fe9679c610096ec916899f8cf4d6bb 48c77c5b909f60157b1787aa29dd478d35b7a06be05a27d38a92b00971382103547fe60da0aca014045301298ee0836c93e38b72e94dd5992b605a04f41898025f34085961af343ef7bd3b00c752d21d1ae41c1bd54bdb9367e788fb686080047b551c15df05a340f07c569910e44814e79c91dd6c6a0d6640557acc50695002052c68ee8777b7d7c9f81fc024855abbcc5a3aa917fc8ba496213ffd9548800de2aac44308f657be718041aa583e5252c9f661291832c59b5b7b9c5543f3220a22cbdd4ef1b1b578150129ea7004e2bbc782d8f14cf1ebeb9d00610ba7ac07059a73b1690643e3a5872c92d21fe5bc17ee264cbcd14044be8067759928703e73 false +check_ring_signature 62b4c384e0a0bb2a4f7aacf19caeb70755427844e825857c52090ed85e80e9e8 cd68826df6767963ab3ba3e3e5dcd38592fa5db88f793f69ecc43043416c86eb 24 a93250a23c4cef6c4539f892ed41f9ce7ef360931fdb6069132028b820974e25 91c9ed1339e36c90d0ab3ba3597f02db6041299ab8abc811f252ddf68efdb2c1 68c92b3d25e9be4d646e696530e6f87b8a435b7149394d92ceb721942d562a72 e5b07460dc0cd581e82d2e2a2530df34541e1a6980a3944ea864e334fd91e205 b5e72ff7ee775ba32ca669392ddc5143aad2ffa5f512cdd8c74736c3c76f0520 8c7f8662a154abdbb968901b94857c797d11aa202af24f7bc1f4798596f5d045 e90617296db5e947b602f027bf53139d34707bcd05eff98be61ae137e1730891 1d238a370a013c4f4cfbdc3ca6d6b6e2e2d679804a9589039dbf165baf7eeece 0921dfd3da12aef60eaea60ae7acd8487835e64631e2ea1b578512484eaabee3 0476197d9e329f75d12c4cbff9185855c8efd9ee500a444308983f3bf250f659 867dfaf36a506d66fd0017b1b3709f7bb72b06263e7cfced59be61565ba8aba3 75895b331810c063552c673c726ad78f90814bd3f7e81a9ffc30f65c3536e18d 840c671e990886958d3e15021289d2d594f1943ecf8f675689e953aa17502b8e 84131b2dc0f32df2ed9e6a2cfec1c29ee66134e424db77b67899c56c0da8b9a2 f85c9c92857808edc394a9a6da396d388940e6c6b880c925f2d568223720ebcd 60a3c28c55be59e17430581973ae5ce4f9263a9d528318d9de10a9289f70670e de838718da2d9c8ebc10ef3db59dc8d63c54d2958d2d442056746ff53aa70b2f ccd87278803b7e9466c49eabaf07d6af01d5da47c2a3d546e61762ec47e8c380 40a01210ae15bc7353971ee09657668ee72705b5f694a72eb17ad863c54c1577 39c783109b779eb2ccf63232eb83e33a89043dd4584a8c7368d8cac951ddbfcd 9163dd76b3a508d1c6613ed50e8260eb91e15e37d24b97ec2f92fea1a9867431 0a50b7ed07bf4a02a88084b38e501740237eb967f92d566e380111a62bd706fe b862ed4e5eabd50a327acd229a9df75f52df6faba0718de5c4e52cc718979fdc 9f27a93a0c473323e14dd534c30035beeaa50fc8ac86e16066a2cc0e94076c57 b3d9a3f568327c6eed53b741125f3a8125876305916670101d05e1deb2d9c1046e0f3d0b4dfdafa942bcb30dc9a30230947cc7fae9c02829d81b668a6e6d4f0fb36604989754947ffc04e6b8e22cb630f4aa52396742c5c8735aa4f857eea600f5d7b8699aae5fa790da47070503c4faac573c8b9a68095cec7460256499a90230f483c168970eb34cb4b74770cfd2d475881557701cfe6ef584a7ca15e2fb03376dd29c43c2d637bdf314021f2b98b418bd1537d9566609b6f302c1968a8402f5410ae2cc84b3eaad8a9f344dc716a2684724e67631fa1d08023bcb0f88ce0ba5bce66cb547d5e36be7c0c45b7ffa13cabd6db0b01c1a7925ea18b7d1d6e00ab8d532eb6d9a694d922f82dc684afc77fed99a1cb4642f0620013284e814590b42c227fa2fcc3df127cf9c0c095fc90b91e663ce479f3241b8491ca0d608400c33fd33038f0bc5d6cf7df6a48df9cc51b79025d3ae4a3a485e5fc621f77df601529d8f76321177e9cc7e39455f0ed43a0bfc5f50b33c339a05d3b7904a383d0df5fefb8581758458911b6106a09570dcccdaaca01c7b37490908e8debbb56704d73a2daf733116ba3848bd1385facc04f469a0873b9db9c18cf23e697e6217d75d2d79e549660ffc855cb0157d2cf9d613c6a8c48a221f4654ac51dd5376c70d02ca909c24bfe31f35e4832bb9e2447587741b7066c790c5508f8ad7970978084527897617d09ed612c87fe5a61fe63da0bb73ebf637c3ac9e32a6a9233fd20d5d22f28ad1ef6478b2777e8b29c04ef15ec7f077558f791a797223704b61530105bcc69d4e64ab45072fea0a0a932ba3044a2f5a801944e1753af9d7a878e909088bb2a3106cde4c22679eef592f765a9da1d2ec4168453d78a0a18deef8d00348bfdee5a31cea874cd0f7a9b3dc8119cdd0767d9e8f2ee03ccb8837eda72301a06652a0631754bdb7b5cc19ab59aed586a5530d115364cabddc991512136f07e6a68416a2602205a97b1d3afc7f33a215e5c0bf3ad30cdf63c096862a880f01719da737519149beb943719e706e2e483682e5bf502abde5fa0a5752bdcdb305abdb7cfd455d0cab0f977486bc095d52ba5bcb513a6b9c021d33d9969d4d740c46377e1b4aba539b2ef81fb7d9a0be5d57171ac3b485707724f69f382a587f09867043d7b2bef826b5725c9baa40eab3970a1d54436ec8d5cbc40360f6ddcd05216936075b6694ad5c67899f427d20db8e9e534293a15ac255737bba98d88b0bf5410331516ecb72c84820e14f8df6e7a0561354e926ad9c5743d6e474204c036c99c7ca85f10f0ce3a99176e4aad671053c272020d059c9892d3f099b041e0a713ea874e6756688b506638da555c8c8b8c2561f46fe06324855d412922d59081ba3ff63801fd194b0768ac91bf66455ffa94c5fb02862dc72a7d56889d3e00a21b4851c54a65b68296397d169b9a705a06d93f03dafd17d0d21e638cd8c7d069383db18dc62aaf9e8f56e5f3a1512a2921d2b4ddf31b930400e69531d70cf0b7dc47cb960162d013d6f6957ce0ea36b7db4e64824666f487b9a5cded189140dafd299947ebcc7a4fb7947ca3aae1424ee7fb95cb608532c642538585f00f9011f22d6410a3bd1e41320848a928b447d3e9792a092da9355e719d7c84d42600aa9f5b0f0650746906f27f696a676bab1d1ebac20081aaa8e2dd2db7ab4b6670d023cfe93f7ec960a1ddecd1d5d93c5d6b28802295c998a16159e0a2403344507a5116c840de6f923085a5b28d7caed5d62252277c3e05a872ad2c34b645c840b1d41a4c27730d288ab19af88f4fbf428ebd3fabecac815592bbc9e4f4092770f6c18cb181b5e71a8d257f0acde369c540b4a33dc4c26e088850f84171390f000f802800cb77e0ca4470ac293fc06c35b6fe5a931e83169fcdd559bb90dff53057c3d058d30bbed8509907ecb4ede665beaecce75e841f3be0685d64f199f0a018d05cb396c5ffa2ce242adae6289859de145317ad9c9c48bccc96e800ccf560554ade9194a8d863cacdf92b09216c7e23d18fc25c1a9d7f98d34b2dcc854c30bd335aa31e53225eb00fa1b8553122d0b29a1eb214edb535c53912d90c95bf7047cc31797720434ac1efad0b04a0d4c263c8707f1a3b720b296824bc9797deb08 false +check_ring_signature 9e8dabb29f61fa6bba1c6e6c976ff8867c07c09c9121878d6707fd3247001916 4fb88d92407fa38322b331d8cb2f8366331902033228f7415d14e568f3842368 17 9d9a18a2c073e110702de46e112571695fefb1d3c232d7ab74d7ec5138a71ce5 29139134a084742d37da890432688ca8fcb2690388564d874aae2963272919b9 0a1f9d37513a81d393e26d9d642befac2b059eab1eba6a15ba7cb6f72e371d52 c6ca38426c1c3e5f0f807a4b0a6b4538734c2000364141f7690fa0457a950dfa a9b100711accd0bb72caaa980c0976639a94051b135ed19f19c76daa5114f080 ee90c815cf802416dc0a93e69cf4354e4f52ce5db09f26a89af1bbeacba12158 a75eab8d35cfc524633ddd39fe8074c2a2066d8519ae3a496bfa52babc84f78a 19920c075df1dc3a733e8600f381b565c47ce3bba5bbf8bd387d3b3e6634d658 a33c9b9afc3130c774ee51f40b5f59d74031f5607479b62260651d26b00fec18 e2a38d04ce7160ee1df80fc730f43f3a789099b3398e3041dbf7b113a42a407e 74628ef4a210bfc90961f16de073ce7756898511ac2b03430c02e5f9395fb13a 528d3ebd9b99488445d2ee6756c77a696988bb5cc25d6598ce1b1aa51a10e81f 080a67e2fd6c8e74856eac1c957bd1f0bd401487d3f516622241517e9deee1c5 aeb56f2a6439878085b8083a95576a8bcf795e59e4b341c630977aebbf8fd9ff e8904e051454acefb864ca0b24bd5a1a2e5cadeb34ea044d92ad0d831c76016f aa9b8d744ed8601f6800275e91806502f7df9f033fa416eea059572c649158e6 66c5599bf407420fad1af45b948af9f23f4d358d970f409629ca778260a8e59c cb7c1b11a95cc08858507c6756d3161416a04649248af587a64655086220b30f957218b9fe621709db8ab47591599e2885b6258df90ca17f8c558f5e4790f00d4c610c0e65da219ab7e65bf5a179707269da212276780d8bf6cfa537cba87e0472be7f7e814be5f6cf35249fd250ca53ea8a41590d1fccdf3b25dffff1e3110a791ae37ef750df515556d1a16e94f5afb8b874ecb7245adf421f15d213967e06e3c3b8345099f58b7dc0cd6c654c1466b64d12e83d36ff45b29a6bb5a3068a076fde0d9f0d071c6f3afb6337411b738d7bfd08dcbbdf95fa0689879a4c0a4a00b3c2576920e206a4d42c694d83d182d02208115be0b7623e69e5a8156ba0870b3382e9c9278043a6162d1a7ebb058c601e9bb664514115f9787d24a5a6315607bfee72b41933a99a364da2cebcea59186d7ba467053542082dd3c782a0c1710b1c492d159ce64f2abe171732f4463913f5faa7bfe3b9ce41bd6087ebc1a98c06d48eb2bc952805a1088afa4fd17065e1289cb4df48ca05afcdac305bc8b4aa072adc07e721ca3e89547ab9a07214a6bee664f854fb787dbbe5c9f948640d59097986be9a9fa1a0e561e0c9a69e811228597697686e446fec9ca7392daff12509fce2dad639224babddd205b05705e58ece93b26aa56c8090e20fec13e4cd0d009fd6edb6f05cc8896fa219d2d97b60010d772aa67544e06d55909a37d25a01aef6486f81ab3bc3a715458e6d2ccd1e3010b5993e0b9ae326426d7809cc0c6b02ab2c931926496fd310e90c29007c702672f12ef719b610ae52b63fb32d4e800b5689efc189a664d4a371927714d67708bf2132633cf6847f3b971dee9db9d5053518eab542bcfd695a3d2e1b9a0c218395d9e0c76bd442642f388de5f6b07e0cbdde9561fa6e8c9d41210bbe2f47079b546c13d7e2f0193c6af83925d3e6f30d845982e3c1dc7adce53c127e4fbc51b982c2a71befc03f730f7b9d5e658f7d0e44c1224ad1f3479a3938233780c62fec8eabbcc03b5c3fb1b4956b64b0426d57c4d27c8f76ca27eb3cdf809a993772b2461180bc3540f098e123a0672c5572057f5e28592e7d712d42ba4c30eb758727b4e8885bae509b13c13fb626521dde07a3dd9a305d934c9225b8681374ebd2544eaaebe8edb087c753be81af1549aa072b75b6c00c92a77612b13abc288dfb5203bb59c23852dbb3defba4c6d492310f2c9ca1fd8839e44b99e246065e10fbf30114f6ff2e6b48f1b8b2a38a0239230b270e0543731ed2ba347cedd48f61ed3865a68ff5ec0a3f3b0ac48fb08851730d1f3ef3ab72dbcd4042fc9971be72ce6cd51708f4f08dae85a0c26ba7f22a9f071508a4724198e166ff68457a80010c33aec7bddc3a91e9399912d5a9a2be9c0f0504507f096a18c50e92784963df4cc65d001f8af3eccebb85439acdc5c501002a5a1c137e481b85e7dcc89410612177460489386f04cf5c6fcf115c89a502281e9b15cf3ff9441aa468b6cc68ff585282013fb301968615f87c11aafef84405 false +check_ring_signature f99bdcd648fec920e3a81a15b23535d4e07eec123fa8880f673f359ea2341ded ee5c990209dd3009fcf6767877226e4677d38ab652b724108bf1198e08ab5721 12 23032d3cacf3f0a61bd3bf1c325831d34bffbaa7aff3ee342f82573eb401dd40 5abab3c98173186de63c099079b0afe1298130462c96e7d5cc695d95c80e35d8 b5034b339630092b6c0fd7642cf839a2a44be25ef556d3aeb3bf64afd5a7a313 b920fa79842996cd10922543dbb7d2269b24520cafc82c73531ecaaeb989e994 ec048b5bb4ee540893e140849641aac3cffcc5cdba7b88a85f2960d3eaa02c53 414076cd7dd162c39569471dc1bbb85d54b6584f34bad7f36ee45f60339eb55a 4d2d1cea5e2e87cc9e9c4c8c9d9379b34b1500ba90aefe2eaf433ddf0e311b93 6c3169b77943ac3df6f58e5c01fe85e8037e8e6bbb8a99931fd0518f258588c3 e8901e0a764cf5e275b3e0cea1263ae968896d770d85ab1417505b224fa2a51c d53a90480c9f79677b53f6fe40d72e695a6a9fd7715acc1916b5133b3354c79b 6e597d9fa5ad3016a1b9d9977c934c391d0400a34af6f8c1f88d1d0285ff42b9 1c3c2a7b8276b8541c421fdeadfdc6262b7d880b6c17317389699a5522d34c9c ae235669957fd65aaf2cce2ecb0ffd77b71deb70c4c055a46f0fc1dcc58bab0522f86398b97e2873d5c01c419d40236fd2bfacc784687aa63a52fbeea4e20c00114d88e9417fdac4e16116a4859bc1928f4577b12e7e8139a35d247068af8908258745d105ca1c3306e3c7eb5556eea298b5d7a2a1f3cceb5080e77bd2fabd069a759c9fedbae1d2621aa3f763e3573aa438797207abdf9aab1af0abb2f65d016a1730b04000b45db3b30d8777f351aaebe5722a2843147f3e21cba3d297f6068422c22cc89ce40ac41a028570bb6fd7f922303cc145011fb9acc6a9b707a70a72381dec3e16c152c3297598b5c6a1ff7e62f935ca045d4fdfb940ed96e20a048b85fce11967d1b8d78c05bf6b3f06b2347874566b2c4f1a544581d7c18d710025d4236926bef1a477b51325445ddab41a0d955efc019fa2f68af38de4907a0c203930665f4e12f983593e829034483768033e07ad3ae16b31bfa0e66fd98707ed04a58ed1156f1e5066f88245b0a032870d0a65fa3fb8d9ecb91d223093a00e6d8fbbfbd50a70a0d4e7807f69896c5f5c2c665c88a1f6d4324522e5f006af050444341532ba7bd9c39bf904278f301676dae18ddd23542598ed63c3f8ae4907ab5b3d7eec65a52738be87a4379ce66375c344dac0fc14c699fb2d18759743088e575278361d7d5c19ab12bfac2b2307d516dc5c3cd93da1e7bb94caa2937a03b04dd34a1f4da5124ddeaf692c54e002977a4fbfa9626790232de70835481b09709535666dc32faeddd698ade4d799079882f06c42cc08fc2681e90f6de1450833a4c53c519c3563fae3a000cfdc35d42bf74df291bd0f80eb511e76153f690e71f1434ecc4b90716647ac86256b6051b1ce649e0742f9ced7440ba7f667b600cc212113db4847c7040b58ae5d95c9af2edcaa22d8409a354c1e531791f7240bace27f9c8e21ba3bd3cf42bff9f18fd8887288d951b33c081b4e185b652c350fc44703a750fcec7ead347ca9ccdcbeec34940aa753b3ea94fa8c7f5830a117072d5ef65aefb2bb0630c84eb2aa9c72cb9b1a826235af6f11fe31af148025250f true +check_ring_signature 65caaf46845e941fc32f1ef47b3f896f12475364f62c2e0d25c73ef976c8bc5f 072ec5051fde8facb699022684268c0f45b7e496322ff75eb938ca0fad090a18 48 f0743fc9e6bf938381c6df9f1b2e3126d44ae215d19e9352c4aaf7b033dda33f b06b4e97937911c629b6cf54b27fe82aace3be364b86520ed0acf1dfe5bb55af a17ec4d4f6379010461d0f30be8fa604cdc1653407fb7f5b69081052b6e28e33 f5c8fcb16b206a3c510df24b4c0b0b8ebaf2f7ea28441b482ccea8b604bf538c 47ed7a1622f484e407b5b9e863f50ea0915d079c5952b6d0a6ce448769cd37f6 e67eef7de3970eb46294f8225292fda6a743bd4aed62d15c05ba28cb9c1721f1 a85e8ae8aeb9840aa4a7e061d0680b11481b5328c111ffc0c6d6a0da2e215877 69ee9a67bb2c2f67c5d5b29c078b1fa79928ee9e0dcce106840e22f79f4ffab1 dfa475c76ce2d90d008d460e3581bdb1af23a995672c25b17bd9fc63e57d3851 bccd67c8dfaebd4c601f34a76ed9338e4fb79c88364d318e4047ed22313b726d 435cefda03f4281816d2dbe05270b7198483663ea94145701ea412abbfb43c5d 5fa05cda67ec12afc22cada616129e1817763151db0b86bc17c8040030262bb9 7d3f204430bd011a670e21221beed6bd13432b67f4487a52efab7b840bfcf04d 67ebb01990a39695818d4e7c57e89298c9086ec56b7f0e757e648445497b91a8 e390cc7e228fb82ce7c7facb2101b86f7a43994fd9e1df762f367276fca265a7 6dceb1258ea6c73ca3fae242aab6123aeea6127bf280d661ac4778ce298490cc 2bed2eab72d7ca303474cfd03e0ecddd392490d743afcd63bf4d0bc85ba2b788 46a900bfb9fcf597754789957a45ff3405b4b6bed06c651bc2b2f72896a1d694 160a3d3738198ecb3bd0c7e0b306b9c323d47a81e6ccdc6270d10551a0557cb4 8ee24c5b699fcc7999bb89800d7021f6c203f6177692c5a4f5d8a43b54ccbba1 0bc45ab376184b07d7c8173b7d8e5c866cad923c076623f62e0e7ef63b8bd75c 2a4e44940f1a2ec6a4bdac035105c1fabcaef6e93215510b869d829ee002e953 f17775043e2dfecc3eec0fbc29413366ce3eee70abc5aaa50292ed1fc1799137 15217f128e91c3357a3f6b0c9fcadf12bb8fdf2ca7a94a11192860003ff6c929 614d1350e1584675607755e12682fa1f688fe5f54482b08cd224c230cf1ed926 4c573dc249ac867f7806afb3467e8f96dcc655c02d515786d53f9da6393c9378 39b5e88b265069fa81d32cc219273c8fae321ad56150bf472267e71820797b66 48e7f827ee80fda0bc81c139a6b1a08e11b8cb039ff6110326625cd4aa5f161c efb581f1c6e0dae2892b51756796c835d0ebcdf7e10fcad492ec3646464e9ec0 33b410f5b82b4e448f904016d5862fe1a69195635842e927e42ff01f1845dabc 86bc1a0fcce9be126483def106c2fd58cc096c96df84934cbbfaab8e12e9b244 12a95974512cda080155819ff1c06e9ddd2c7cdc2bbbeab6991d6132c5aba379 c90f690d119dc3745d5067f88b178b07caa4bd53b8135d09d04eef4c9519ffed a0b63612f4aa39d60577c94f7c57fba44da5acd23e371520fa007647686532ae e6b3a83f5d793222c0e95843a56732dc22bf24e57cd26d814d8b80c42007fd4b 9c7a7e249bea5034b0d6296dba96e6307118c626789de3a7fca0b26d8ed272cc 6d41bda66b730b4a7f5f5b1ac1e3c62d740e1155759b1ffb70f1bb67b0463659 67c0a8344fe5f9aa93edffa0a9231efb01637f6257c5660b1fac597347237d99 85583aa35476ff2c096090f90e674c05ae7182ef90ebcab298f33fe96f188847 b7f6ec8b1fcde2df0ccfa72c1f87c62be56ea301c9eda9bcea1716cb68b370b3 7adb67e562c1b4523c7ee060b561d755099ea0fbe8d0d4cbdc5e45aae89317f0 15ecd9415c24d93c93faeed47a47c6fd54871f72cff55f69347d23d5223a94b6 4305270bad6d6bdbac349912e7c9de552526fa6ca598ecc937736bd6e3ba6460 e2fd634f13c54581d2bf4bf4160b56ff0241ff2b1aeb171fade0bd81aacb2037 7ac724954ef3d2df9422740d60d021426fc02feb842b2d5fa39be8f8631d27cf bd5b0f4b1625504b09fe6ee140c02ee659eb55f196ffa6b447b0c28ba6553c79 a5feb3eb33e1044f625221e74463a86f8ee553a5d2ea5e40bf3a455c247c6f84 66a560d62550f716e8b2e264ee3dc5c92dc3813c171d59cf24cb8742a788f876 a3444e420b227c49cb5e948db396dd21d276d119918edf3a7f5abdf4d21cb2068b6eca1f14e64c8193b2959838d6b49385c5dc19aaa6a93dbc799eed5dbe940121c341296c45935f6912124958c8480c307d2faf17df430013a5fef223fc8d03a59168578836d4a0146d385102d8a3051ddbfcf99021be96d948efe0e1155308965bdb81f37847338fbb727811ec56a6084c2ac81ab7ea335ccc0ac9b2f18e0468f106e4e56de8925a664de7048d4552022472952d755cdf97f1b078a5fa2105ec3492c831b6840960ae7d4201589f395d6700e37c0b8f02cd3a261bb981a908ac25139cba45ccf8c0d8e08824ab70a124381fed90b05953fc0818131f083d0e986abdbc14de1ae59822c6ba5dcd8e34a26a6e1e6524398825bdd68987a2e601c6d098d9681c3c46953174ce2d6d4d1f926443272511812ecf2561e48b10120829bac26a4749f22630f82fcb11e79930bd7745264a4de4b8d0ba6b7d082db90650109d90f25478b89c7a284f1661050ea7a8836cb89fc1a6da8285cf8df4a60c6e8898b5dece5ab73b8a209c6e90f8957ea5fdeb7627cbce7812205a1215730024d34990cae5f9975117dcd3ee0f32d7decf29692805654cef1d42eeb675e2015b1c785e6b6235a74add88033c41cabc9c960b560424ded180b50a71592ae00d0d29a71ef29dde1de1b8b1de5f48ffa9338fb24aadac7d76d7e4324a1117ec03de145f343d34760f487053d9bda1552ec0e6570dcb5d71505d90322a99dcd70d52c48fd96b81531dd8e12929ffce06cbe535cbfb6e24435f5bb28362f0eaec04fb8254b2e4e65473a1859175a4e55206354668a39de33e6f37e895934911790ceec7474f0d3cb4beac69ddf0b66e3d0a666fb2d234b30c7ffa8d8036ffa84e08db382a3e6783080109bbf788949757e061bb55bf1e4d344ceaeb3b8f9d592d0966610451f5c4db9370ac1cb6d387e12fd332dff2727b8b29b3bf1b9011611f083e045896c190b18bf891f829a22c56bb51571b13d4274e9858032438557d1b0721ccddbaff98cb20cc201f45a72822b715828adae4fc06831297e2bfa9e8c802a23ffb86ee41de9fbbefd56c5bcdfe653f461b151ce0f4597ba9e608af7adc0ec1e7fc2ebdc501f531ec256c5235cb31e27915b972f003de3d27abd8557ffd09ce3d8386424ddf18adddbb3b8abe16e357ac5fee902af8301ac76173ded5f3097468595765dbd9c8ebc21b15123a9d20e1348f0ba3159ff909529ea894eb290d581b5140336c44ec0e0475df407740b1ed0a47dfa2cd1bdb7644604842ff02025bad1f366ddb11f0f2da03984eb51c3ebca536f9dc84dd7d9cd4ee320692990b80787d0f7875439a644721315ad1ab83d091f40cbe46ac3d861a14fc943561013abc3ab2f73967eb18633488542a70242e7ad665817f97f1506c8386422e2a09dbd98f765b63cbb639a1581a2a6ecbc0d974fc515ffcbbe8d18ed452f06c5e0bbc2f619eb6cd8d267127e085e3f14de35fa1d42e2b5d48cf930d5fae6f83890aa5579dafe353ef16c0c9f9e8213274315f18a6c8a23f8b01378d617b0f27360a920a0d36742448f7b586682483601760c3aa76e990d4ef568db5342dd0c54e0d45ce80aee4e76ece159c13c1950a22f933b70b46bc3cdcf7144a609007e6bd0009a0191e06f7b54b61acf39dd27e9d624b3a5dcaa9c78db1c8b4d6d83dcc59003568e3cf9bfcca3cd4a4ce1cbee0d9865e3805b05ac82ee61ece7ad355c4a8019519c9f68cea8cff1da42f96bd1185f3b7808fd622a46790b2e9cf71873ab9094d91ef51bc06b53908608dde10f4fccd88bc52274a83e4b9b22e709853cb910f25ff0b0cb3d0a6326022a2ad1aa746af15d61936d638e96f1015ac2eb2a49f00e76e48a3a9b958d568ee74a097350bd6e46339ba45ff273bf3cbe81828b0be0181034f1ee8aafa5f37d75fba49b0ef5fbdbddc05c07b4d8e570046b8ea37240a9b72df3f804ff8c459d75082ba521164219a170e035236570096514bf8f8750360ef75199036188ab4e965bd6f152eaf66ed9fb4a0bf3a825366c41c7b850403b35e0bfe7710d4e49253ae31e072399c54e229446c3fb7ea4521ce86cf3de707671ad68e75ad2569273a5349d5e39406d7d8e3e64cc65a90f1c13c41e814a701fb260f78e43eac020937a1140e617afea0ded24bb16c7f76a6a8731bcdc60403edf17eb44c60dac3d79bb13469afce6f86fa289c9591123e7dad1b8619d083030390cfd6ade0585b8c6aad8eada1119acae0ede88efe92038caab5672a373303571d087a93fcc014eff4dfef3835b1abd66315ee3a750cf519d3c2893403340e72c5fcc6a676d0437b6994c7d29a9b5154e3e226398979fdaf81e44ec8a84d05aeb4744dfbf5cc1caaaa897c398c0952d5b3b343332e81300e7b06286774ce04c42b2b8d84393d1791c12860badf039da598bb8e35953fc94e8e8a80a4618603c81d267f6dc07b2c44cb9b8fb310efbe9648da01fb6936ea6ee952c54c10ad0e1e368c37462939e00b2c9a4b475457e0326f03d2f24347e11ec182467bb93e09af3e4b9281eb5578cc84ad185777cf42b6bae6844cdb9511ef4278c62cade501384b2d8250415da07819d056495655aae41ad94988d173e713c0efe1416f830b8f0a9ede1b38804905843e8c8585505201fbb9e50c8af23da0ec18bd07ea9b037303ec62ab8fcec7f2e8d7ecbda63f1ea97f08393dd709a8c243ff033dc3d609a385c09774bfd8c097156ec73e3f12fa37cc494e782718bb99c5115bd4dfde03d5c10be2dcf30c1f687f0fcb2d51529167c3fae94cbf2cf345bcd5de20369c024f96d18046de1250c280438d1b949e068be74483c5b40ac8d14c87af79fe210b32e3109fd907bb521d171a0705231d8ab72b31b2e309c73622f935b2be8165008edd2ca2097dd8d65b6c341529832925f1c0350ecacd3e93b265c22675082b0148d07a909540550eb6d84ffc89fb5b0a6a34be4b2a4584827e682f699ce1670a4b97053638bf9d1d84797eeff302f287fd2c1b06dcd532b12c08e02e0b0c3502269f2a7d75d9e3af839fa0d2f2f009d0eb2810dc0733dcb9a46f6db0241f880a33372bdaa601da9b04bba8f4704da565da23040f8a039bc2327efe9b053f63076e68c5bad0fb0e660912cf85678b705247047b96debc1f614b50f68969f6320af606f5fe8e7907b3ee32b922f484ae939730cbb883e20b6dacfdd396e233180154503a603b5abdb97f628af6fcc190cc2d5c68cdc01027efc2acf433b7aafb0e45fbcbbe92cd6af4fad46603c73f1dc9fe7058ba08a4f1815bcb4666456815075048391226a256ba692305e2292a377cde93b3cd32b3826a2d57a47543f29b02d681d458b80d6af7d0cc9c6e1915f6b4b3ffa24b801606d8191a6e4753d8f306722d951b22c88bd3be3cd468e79771c8feeecae0133eb4b71580b7c9c60801038c459c0bf688fedd7d1899f74680dc108c79ba744cebac4a98d4c92787f0fd0ea2be7618541d752967e3a2ec556a5894c57a205363e90ce2657d377693de960ebc4c9eed31ead999d1b3387f72607f0c619ca84cea63eb54243db58854befc02d0d012693dc4d39b90be959a03776cf37dfa2c72e4f214d1a56076a79486b7053b4d3cb464d8077fcc6d5d8dc1bcaf349e271b8923f13948deb1a07ac5fef4016243336c6135aaba7f4433af765d1dadcba69761550f3cffd12c38f9d9e2c508ce1fcb85d167a4447a39ab4d0b15afa0a1172b52e839da167633a743313fd60234478e12c10aa5dd3396a4038078ee9d506e0aaea1ff8fb23a148ee0452798026261579f873d56a9592f35f90a2e3c8262250d46e12f0869ddf9a1bd8649fd0f7d9117421c1976d6176e4c4496bde0466887a1f389bdc1fd64c5ec1686e4c00ddf918e3c82ede32ba33669e202837974886d2be4c4f0d6987a25ab42e08f650e9eb3eb05af27cd51932ff3c842ce73484601c4540327c8170c186ee30f21e70a5fb1937afbbd5801f940697ecca4143b14c569037a3b7452ec9871e524a93f0e8949ae71a64a5849985e51416f7478f169b9d2253b3b1a9728239831ff73120e864da21d745245e4206aa41d627ba51915a5669ecdfb5edbb0e50c66437bf807828c54a11474b68b304547eda936dab21b0910bab23c59389144b5fea280ce0bb3bc16f57e94e42a1e6c99f2bfd9e70ce94a165602d8e215c754f6eb3144dd0f0258c753f64cb110e4a6f6302913debd8b15665c1f4cb2d541bb0bc522f056037766149b9d6a9128960826f36ed0d79bcf6e113288e2f0acb96f01d0e23c1903 false +check_ring_signature f779ea1e0a79bd59e17fcd9e9bc9759630c4f9be868dfe85a702799e56297c9e 5e79649b778df66d08a87997783e6c5bc76580c48bbb1b73689a452e136084ed 3 c0dfa31a05044567423de79d3c3802e5458729d5cbb1d1b716b5bc1cdc803967 cb653a93ceb5dd265a2000596c9ca4252776f9abf274889ae19d360f53cfa08a 241a29b7a1133d08bf190d25fea859b55fad9e9068b51eb6837467dfa407017c 17c0b034e025b640c2b80452993182b9aeca39760adfa447256ecead82bd900d5eeeea73e3bab35b971d824e84987b0b135a4647432bf9a02df0a392dd59498625fc013dd53f203f7c9b7f9bcb4e3f1c6f8449d74aacec1068d8ce80ea5d8904dea8449c89f98703379fe209a668b08feeefd23179429be8700dec2867d49c0a66adbb1f781fc05a31937c81e2dafa619d901e3c5b49467a69c63172bf0b190828451cf3972fe40717f3bf6f85f051e4baaabfc826fa8fba9d7e206cc274a6d7 false +check_ring_signature 75b5148a368c9a5ffb0aa98376792f7e3ae7291809f6083c3146dabcac1b047d 97460aa0280dcf5dcafadf9285f0e857e85a16cbf723e60280aa00814896c96d 1 66c4168524ef3cac36495138068c60fbb0029f35679b1f826ce4ca49c3a7e897 c7ce1e1c5d17957b439d3fa0eab905f943705f2f9ca1419a972c692f27c29d77ddc2de194b6186ee7d954608f53f344a0b875c253bd8af1db69d12697d57880e false +check_ring_signature 9d57ad4e4a094e1b42b224fddab8501be426cb2a19739bdf61e0e462525e50ec fff0425b7f079e5e0d1c52dbe4fbf499e5ceba8549ea14511c74e331c08f5da5 1 f58de3c07a524a7b973a4f04d919b9a5530466125389c162af57bfa1d409952e 2338325914544eab22a9fd6394d06861d9075794a13dbf23e69cb6cc86d7d7422bb4529574e2a0b7f665acb644d27d9f97fe92f95e462521ed18ce756df9e9ec false +check_ring_signature 2a71b6de3f2b52ce45729c11e505a0e18c43bf86a3f46779869e6ac9b390cf7c 79bdb6f8f4542050348be911563888469f47338fd21166b90be41035465b118b 82 dc6e70210bfa964b8a4aec651d116c20b2c5864f565b057408da8872d86dd23f d863963795b438cdf6cdd0707c209c295c3d3dc3efe958090cd655ec030d8d5b 0233164f8cdd6bfa76f7b9489816448b44028d9964158be7c7767a5d0f753f48 da00a0beb7f65d5db11ce4e895fdc3e1fa551a9c617ccf74ee2409274322cb7d 0a99f6f14ac1247e49785e4178741b5df7643ce43cbc35939d380626e1be3971 6904af444011d30e8f38e5a29b737608ee6f6c7b27bc8eef2c127e9567b123e9 d702d609add30bacb482858808e73bccd329d796b83a26a9ab879069c1304076 708af61278431510e367e6b7ddb31f46e8b549bb8c84ca5d2631780bb8b41c38 77b8f425bc0fdaa7b378057a28f191867a820ad0ff9c2bf9b2e2f1268576202c 57c7fa952f8ec0dd4ec01d8f58aef1b08117665a9f0857af4dde5ac660505038 2dbcda544165ec4970f84b68c9ed0cdb04989569043217a2621e474e429731a4 c31b557fce7924fa1c660720a3ab7b25a2d0d95a773931ac6f6cdd6bea0a6526 3723be86e9e46a054154fcaad1a228725fecdf7ee13538a5dab76d869eaa37bf a9cd79aa1a698281844c4447fcbc24aa2b65a2e386147c44a50946534b1564e0 7310739023ba475f55e6a2c276310e83784a9f89c159223a47cd2e7b1eec6550 bd4f392f17260949e2eb39b81bb26980eebcdd849b0f1df3992be1d6b371e53f fc2fd8ca844fa9fb20038c1c822c283b098f11363188ab6829c1d11362a2c590 6e1e81e2005c1a54f0ab5d4193e2412b26aa37ea505e60e38c8f02797924de32 df55bd1948f9308890f3bcabf1d133881c29b809b40c0b34cb4e2513a92934c8 52a70ae19164f7050e80185339e6e3d7c516adfef5a2575bdc96fec8998f530e 65fe7032c20dd7352fea21bf9cf813303d7f61237ac597f82e5f06a85049bf89 13fdc6226dba4b9fe172ad833e3fbc0c28dee4897717c99bb6568441662b13c1 ea09a8cdf3db65aec9550ef6de7cd170f27f6018074f59bded8754a3cc2511f7 8ca65fcff6ae9c98b2da07c25376523d858cb66c168fab091e8391c9b2cdc83c 35a4573b928edb6654abd50cb6e0a6b6790d19a7d865869bcf7528828147f855 aab9adb1cd05a132609c3821727420288c251600321ce574836ea9dd33fc17f7 c3bbc93aca30b521d835eb51c73b81027bbb9f9848770eea68507476b18e2350 fe961f6d74518520275048a105c43b56ca4d7dc03c1c4a28d001810aa4d56e9a 0e932e4a59763e44a2e831a1d52870be6793a12f628ebc4141186961d518e570 da35ad955ae8cd7921e77ffc5f61c354f3b848ee451647029ecba0563668c6aa ecea2a4e26a42f0f75c796c16ac710d02cab414dc2e209b6fe5525ede60404dd 4fdb59e7f36ea72b42af24f167175d6ff38141b88e7d128fad2c1c9d4718992b 7a61d0d55105f185eeeb508211ce90631c61c18b48b6921fe7dd47416454e0b1 80a1ce39360f65e3982ea1536819a7d9372a0ea281ac94706cdbff35336f1b20 ed271a6dc1773da0295b88a9e7941acdcfca3e4f569db371a1c89b2411869716 29fc1d97c60d1fb4eb65835d9a2f6159202578a58c3b84491718864e56c8e034 735be882831d6ca1d554cadcff79dcc2428f65d5ca8d86529991fd0cabe29a9c f8fca4d61cee4d7a38809787d57fd670cc1f9fb86b89c18439585e90cfe813c0 802e86a5b08612c1afefabc60049ff7b377ac2db9a530f021e82019eaac3e760 e9db2b52cc792d8e191c1ac53ab93d0d2ae77bb3e02df0d1a1c8e1e21a17c5be 5808b7d5cc2bd0336c9ce89d61e54ce0792db510397f218492b4a74ca5c575c5 05a2929b7f8fef19c19eee8dea96d2d75b5c8197aa2b6b1d29aadb0ad13b44b5 7cb71ff7d47273ac0c9923f582a4e19bfe72bad50d03adb065abb9c559922075 b38b90196cc7e5a6eb881f6a6034aeb266a8c6f32c6775d6c6a5b3bbb06d7e5a 623f002c4e34aa050ce467d95630b8779bf67dc6fe71ffe0aa368d816baca786 5cd1b819322dc7ba95e3ae93cc744928f7531c4915f668127e554fceddf7d562 71b69367b48b9599d3f0d03f5d26d140b2960110c92e47759671c1721a696657 d1390fdefb6a44f275a75c38324284de3095394f9b5f97742c7071cdbeba7839 fba2c3a4f08df4ba775d334f307913fa7c123ac11c0774dc14b30b5d75d0160c b93c34a0b2a95eb6e774d2cb2b9275aa5d4d0c60f2c294ee19e952dbee9f25e5 bd13762cdf81aaf359937ba0d0b72a4f618e7386cb4d62ccda43e2f31dc40d84 af5a354eb051651af96fa0a072ef1b23186305a76cd788fd55588fcab1bd2412 3f7a1798c0c6c52c9df7e5eeec26ce5e30aa887dd499d04f973eab9c58b7619b 404fb7e8495febc6849b3bfc427a6321cafaa72e43faffa4f5c0160c5e184438 46bc5e0330f0b41541dd8317eec263d6ac6b44066857af29c3d22a8c2dcf192a e12d60c224b1bac0345b96c34cb903202341824b27fe56e9bc5a456a8497939a 001af612ad72f6e25a2118e87782eee368cc8e532f495611227ce3f1d32bff32 cc25d5f90ccd015b49a7fcdb5cc871b0d27b74e8108b4e1fbd57ca412d1aea59 8ebaa1f72c53131e1835ee53cd6d14038a269f59cc94009259549c992cab2530 f0cb8169dc040eec694e6a0642a9191582f56aa1aeff53acf4b635621efbdd02 a5ae76063040398de221340b467fd32b16aecbc255bb859aa8bd289a871b3afd b07136df849f85421557c32ac79e1cf11219ea6500fa5f6b6cd61a5d599076d6 8b7a305463b44c74e3a6d10f75c144b3f84d3ab4fad7ca80b51ae76dfcf508e9 d1e591b0b365d899234739b35775e106e1ddec50da314076ee453cd9f9504513 7bd4801663d1be26ad3a30ca200983416c26b6f908d77375eb151897a227955a d304db614ea9c1f805aff5d2677abcc4b2487c3d654c3f006c3f607410824780 7d8de6204bda687fb312e8e9f7331bf40a423ea770a677501628baa04975f549 655c36a5dfab55360e082fa548de68b529eac6f7fa29301a2912091fc8af1b2f 84c5e8d5c85ec6b04695d7e791d1742b23c3587f7f92f0f064541c9f1b8654ca e97eebabac94138d24e3176ae74a8268b5b1f85f0aecc57ec424f62ad88ef82f ef4520c763d8f0ec86c9bcf064b24d17e9f4bb0ea58aa4086eab1b004c404c36 f3b6bfda92a56a96eb564aef241947750a1291bb1ab183bc292bad85f0e50bfa b2d07bf6b96395e12ac1bee9c91bd5d2eae841614c30f386b0b8e6312b624448 5d24255f25ecb191c1660a7a1a8b88d46417632078af95749e702915ffcfcb45 44155a2247ed0745f869c9fbe2fbfe31270a77930ba59f16124a13ee8a6daab0 01e73cf92e54ce76591c9eac161dcc825e4792c73502870aff8ef06b17a28cc9 d921592afaf6e5dd339c10c64da29b94af63596bf22a3fe0b3bd81055bbe1934 d48202a43d1649a786efedf6f1349787bd4f14a6020c8d706e1bbcd0b0864df7 44b25d19b60f63492ffdfa914afc194f488cbc7fe2e4e51e22aa6b97c345326c 9d2a0cf1b4193300546bbcbbae55755e53d98f163b987249975248d71b40d27e cecfe2ffa1b37edfad2e243558ff41487555441a59326dbf00559f0f10e93d89 d777c5954a41dc0dc86b4e9582bfca84e48cc0d18c4f0a7e4babbd8d86a473d7 de70c6be688d6aa3d99a96d273c715395f5f8640028f35b8f3b78d2317a7f103f0ef4e1b15fc827b95a6039b4188a07cca7964a617274e16eb6876d0ff9819064d7e0b12195862a16e07c95e4361a667c9d4af544a49c494246636de0f4971021e3db196a45825c5ffb8c86e0aefa0ec5c8a61eb0ee465435348e5feaee21809f88dc51f657342497f5af2e344e1fffae577fb907c29f12b1a2b5bca3a1e8e0878e94581e73497fc6896ce3d718203ed71c809b08c62aafde17399791cc9c808e483567596f1f97a753681c87e9fc3eb7d7e2d06ab4d59c11d510dea06f9725c5f6a815d61270f6a69723d383dc1819712170d1afbfbdac7acb7c1484e4cb70a7956b7a0f1818fa472b717001912bac97ada7016ad5c5859caede072b7329f0bb8829d5f4a542e4aaa7bd9fa053e74cc494072c21d1633845c51c97c2253e40fdde3d25882ec161dd763bd53714ef9605bfd6b27bb0f4c7362585ae17635a708bffe9411dec3cae91b35a0a57c27ac1c70cffe04384b1d557f127149a669530fab0935ee31697588634921056ef117df4babdb1b94c8953decef7ae1373fcc0bf64307088ea18d3c85e23278f37c1bcc761dfa2259a241e701a93cc9b026ce0b9f9e2f06265fa370ab13ad353a9528819cc743ce28c7fbbf161287c1917b5606fa1653fecf8c4d8cb07960a1c37cf52c6960f277e04c7f6b319d232464965c0912613826e67cbe70c51e801d64d54d288962cdde825dd95d1cda502842a79f0ed5f4b9c44efe305fab0bec63134a82eef353fdfbe367dc968d3c6163d81fdd04ce84578d353db31da5927188b321bae5f01a6c710006a18e79b567c47039d409baa478a14df45dcb4696a4d420558810f21e518b47ba7dcd539c9247d0240406d2a173fb58b5ab68e609fecc5efca6e585a62538085f1e88ae1054ef80b929055da53df28e40936521aa7924db30ae0ff8f5c3ce9b70b5bb453cef045652c60bc7d2fa9fc15e7568981b9fc00c47e89b611ac95c19886e0e3e0fee8e4bb56d08e804ba9c69fb0d3ba902128ae94026d0ea39ec1f44cce125d50ddfdb8fc5d20d86e5b9fc31b8d2b7a763f33d48b47e96dff22ffc476c0adf1dc913dc002e140aa86525432b8c06aa27c346429c86b48c750048a696770a32514390c82b03e9060e8d7e939e9d6ea652b078aa12c898e2fcae1c8377dc45247abf9f93f596c203ec1c1a34dac5a3e503df5005e89d4bffc189f93adf2d917f47e9cca97959f907111a47425761467115a4c2644a7b00323ec7bd005180a696be54be328828f3099888f927ed27e896ed62717fa6e14953e6c58692f24545150d17483405df2e080f9447719d6a2d398a031511784dbc842efa270f1fa582f92310393667e6a706315fe63071cc595d114dee04cf517d5c68c66318d5ef6ee497f322ecfa73780678e35e62539dc4ece74f65648b4dfa39f47dab95df6d4ef58e646f42fddbb40f62b351234e6ec3219903b4ef047a478506ada1404c166213499aa05d2d2af50dcb2361882b00a77aad98ebf0135aaa7d7146bc9f71fde0018cf8bfa5f6eccf0891ef56b375af8ace5a6826cdcb2c2877f52834f73630ebc7affc1047902118067664d9d5face27755020715cdcfbfd36b5c5ec9364b1aa39d6276f3ff7e7bd0cafd58d99ced9cb0c93912a11bf83d5687bb2a0562a528e46044317bbb6d2c70a9f8e27d71263fd48d1d3d2009a0d2c9ce953916535a4b256e4592c944656eb030f4aa308a439258a7491690bf237dea927fcc4b8014c793aa5b925a9d2ea840b2d2557972c8ad71f1423336bce2d7b9b7598db4975e903c26306738cf085f20e86fe8e8d62bc05066defaff15e5c8209c81dfe1ff3944beb1f3a6cb030102d0f2581c8b44d40f00388f206f120ebcd0feb1b0e628d404478ff61b4e6e757a60b5a285224b64f6f360625a36625147e783b5d7c180bb60dc0aa0fe40f85a07a007b5943c7e7d79355abbc3ffe2b4483def5d7d46db434e3a3ad3fa235915b1701f67ca9095bdb3257fde543e7741160a3d4c16a8e5c7a8741db80762d2c2363021944380498a41cfeef1929a425dc4c1755399b1aa22d687c36b20f83f9a4ee08d21ded749b01201737d6b03d51b9f42183e2a397a5f3b31038a8f6ec40551308985d757b45ca4f3e9d51c416c80dfca02b3a9e5088ff5cee8ce6480284ffc60b9e9c0ebec45a1a061d332b200a7603cae5a1f795745dc2e284f66c0154e00c0e0ee2bdc7052b5d86f97df67e35c74aba8cc3d4b26605ea12b6575203793c640a3f6c1eec87c7f6f1429ff21c4550ccf66eda820da00d4486165dfc45008a04024b2ea5e7e9003b2483042f473a29f9fe9582dd6e2c1d1886895ea78d356d480d2bbdb3300df6980fa4ba5423404af1da6209ff1c6aa40ba1147ab92200f72d008087cd9bb6a16307eb1377b6ca77a424700cee2250825bf0e362b4093b546a0dee5ba5c8565c29ad3c83148ca8ca2dbc14521103e1f4ea751979efa8aa12270689210559014e24ff3a49adbc0f2106ecfd030d74be7beba86274fbb268d0f808e72e25df18dfaca6e472d05e3d7337446eb0defb3357c6672da890b65ea09d00a44efa7254594dc181065f034689f67095256bef662fd5aaad3aace68b1e78095be473e291deb1d65a816d9d03c6e3d30b343de7c28a3a997958f647fbc379002e6d8473134b3a37601948dad63c4c44949f23df44b40294cb0e3af4aac3d003f215d2ecad609efba536c3e00338e5ef833a55af378119d1e4e6539fa4c68a097b062612e53d2fbde510329381b7db79dc54a362ffd7d47a0c86ea3f56ac13059467fe835eb006803be839009e5e233d80f65b4d1ee1fcf8adbad3e74fb31e0a171ddfd57d01bed185f8a3311c09ecda7862f0b0a08fdbdcee5416454530400f2d3ef4b9bd4da0e4f458886e6384bfd06148edc60a303ef5b769a580234c830494132892d73be1ba1ba1814ba452fc9f53191d4311717a3c4ca96722785cae03e236131b2278ac6854a7202960331a0374d4a7efcf3b82d3fe85154bf4d10806909aeaaf562829766663c602e76f4832ddde88bc6c09ce586f8efb6b78b18c05117a5ba0bd83cdd8ad84fe5601dd79a1994c99bf2e2beb71c2b53ee9ea8377041f1765ab660eb95e18e11003c68f61d2e67fa793b6436544c3cd2a32d2f92003692fc47c16c6dd1cb654fb4b70421fb278ac0e636fb8aa7d0bb1875339d40b06d57f65b3e5573ce4d34c3f1a742798f52cb577bfe19e2f4c4fd3e1f0f7e2300a4d1ec3cf6cdca0cf9d53fd3c8b549152f3cf1b2d1db5a720d974526aa148d50a626d466084ec93322327072ea23521dc90674469aa469ac9aaf1347ac0e7f203cbfa9eb9bf62d1ecddef6ce402cd27d8020db09fcb1c00d79a7f5d282625470d86cec060c5baf6e415cc302abf1c8f3c1ee22a1d900e06c312a7f1d5a5089b025acb23eeacb86c1b5a57fd9156acb2b55a19b2b2c799cc497df9177782ba5b02ad519a5a2763e6b36b8e1abb1335c5d572fd31bfcb3eb84b3383654218092c0f2300f4b9890029faf599c34a9d17c10fe04a74a99d1b1378b703eb411ca7110b55c5007c3cc9c9b925912a3ea96855d02f5b0cd2f0dae6ab664ca39e99eae90ac961bdcf842834b846a9c52e62b5dde2ba5d7403b2e550db39fa5e6ef699b90f72cb55a4f55da7a56bd99d9a57f42f7e1170e89de7110fe4eb71a57b4f0779044987789edd57dc96ab811fbdddcf4d5c7bf81f54ae1f50f9934d8f6c36a97b0cc844663631bab6166a1523b63623edf60653e069eba9f3682e71f6a7fb689009047b9ddd284e57ef0a8f430714a42c729be1b227175dd5f71e056e535d532306b9d21cb84f63c929581afc5e1ffec868151bf2a34f23e049802eaf8f2230670f06acb965ea00e3e06cd26b64024345e58f8be173095cbb8f5df338b0814de00939d5390fc7f80312bf2ed19aed47c0484e61e6611773dd1f16a7681d9ee1d100e638b8e6b115b135de6eb110d6a47bac4b43941f84bcccc2263b71457e9a2c0ad2cb35f9f3f1e220d04bd2955275952ba74e78e34971bf0e2094e2b61e01c20402f83553425d37dff5c81b781ed7dbd9de5de441850e5e573a13cd345c00890d82b6f0dd0ae443a4df2d57eb81d0388bd5e1cf2f46ebddd503567fda932f4106b04a185bc2fc6187018256e2f273c8cb6d5cf16c2d1602fb8d82ecda93b0cc006dd963e0489c2fc1bd1adf03d2ecb8b333c21245c15f4b836e148ab65de2b308cbe724259c5cd622cacd5a8ea867872a7398579498556b1185b2033c005b9b0c66b89f4cdfd8ea4023ecbcf3cdd59ed69ea30d797b98ef2ab8447b16035c6d08dd94ecaf518d794bdae05935376fa529c9b6ceda76b6af322c1d8cb01ae47508c71548c99b45e4f49402ed9a69e43d215f08af4a2a52866a91fec9820e82500ab14f56eb83d0c4bf22108adc28d55e407399712581283ed6293355a3db9f990f8992e98c0652c92d29e07713320a45550706e586d8032c212b81340878d6840a174943485c434e1209d397553f448fb82de289495d45c0a0e82f89b012177601355172dc36ea61a7171b4c90ba94d2f3d6f9efb8312697cf65ae2a21b1749e0435395a5e3086299eebc929dc6ea8d30e6ec16189977af3681703740636110d0e16fe36038afec89f7f028e4f13afc11d4e9eb4b0157d3fd5386cc8190952660c6674e1399a63911f0176fa85501e58cfc88ecac14e31deffa26a4fb76c1c2005b8086a6578256ceccd08dd85d5e3687dc5fa60ba3ca478980093917cad0dfb03131f92af0e398cc80c3386f8efddf03efc6c021e390db0f3fd4d1207f256f6074c56e2618c4e5cc08406fdeeddb25186afe7bf301d5618d2d25537643173ff0f042e9d35cdb7a5c8160952a5bcd41d21a9035a28850e8a5631d3cc97403e9b0f9c379964d78798fdb3e0e45cea2ca96390d4e22f160520eb41a6090270142c046fcb3b4108736ed642c1859e9b8973d4b02939a8baa7f4615305d5723434c605b7813369e8b280162f202b951cc36a9f2996277d49bdfd44fd46c4b055767108e60cd62ee795c133e8ab8c81a3cb4c53278de022b5e18858b2adb5ffbffdc406c256e7eeb44f675a4fc421991a592d60ec3b378ccb7cb283fcadb6d15fe79d0e626cdb1aed39a40f71ba2ddfd9a7e974a23d749480a06db31ccb627f60ea68004585db6c2b245fd6c191a3f7a28b9f118ea245446dcc66b3a6d21a17c618d109e8cb1c69acd1700a147c8f10cfa5d1d8ed4609e5caf5acf584e967192681390e3650d8cc354287de5079cdd6df1c8dfc456776756df81db205bb0f6d13823408798e1815d76cfbe9cf840519abd7a4f361dab02890d9ce30a2b7f46cfdd98f01c1ea6b88de1ac2a94bfca4fe4af99c943541c6c0889abc8d3472dcc165b3040dd000db536203694887888e37ed269307d15432e919a61ea2b6e96e9f2c300f03adc6d083b4e7093fa023ea6263eb80df8a44fbfd5e6a5a01c2c7e25040593a0e8ae1f60470c85b66428bb36b999ee2e15ec0d6b1acf7f94dd6c4cda57e046a0ec0884d993c72910303b222512c1055894893b30ad36c1725afe0df0738977f0b576e9384411f425428cb88275dca91e850fffc3219864def0818e26dd7603506c7e5a6543bec30b338a5f4bfbcb3e84c29b9f4bf178cf7fc2c83429645f0470f5d3506f4cdc88aaa67f6c77031b654813eee038179a7c230e2fe93168bde3f083702692a57012ba6fbb8ed88a406df6b82d8ed3f51ec85453ee6a03a3f7455090c0711e7361b2927326f6bba8ab7dfec6e6eae114fcad3a76863d8794f53e00c8734320d446061f9ddd91b4b380c750892cf924deaca48be5e12fdc1793ed6071dec391d7ad17273cef2483139b5b1724f9c4be1360a7efe340cc4c68619f80b2ff6adfc2e34b898558228d305fe221a18884abd4241f28adf7f84cbc7322c01fb8c8fa7f8cc16be4fd22230d8274a7685b5d033461e2d1b1e7efaf817ca4502c1a9a0b8c2517030b12c0279b3e45d228be828f360a26280958edee3f99cdf00a1adff793dab0821ee5473e3870e507f3d94abd86875947edb18786296225e0037749fc27bf359b9493f8adc45c2ecd37d6b1cc32c558bb0dc60f2afac7f4b06ecddab9873c7afaf273cf100c632829a03e9e831aa5c3671c0978ffed951420d138c70bf1cbd27720e359290c3748a209073fc3147e792e08ac8edd36e3703f567dd8c29a83f44976392ec1f292573821714c3ffd7e4bf07f081416f3f2cda02aba454675f770d1d5578260caed90f563e09f0b901c0223249416341a06a6309679eb2c44703c373e83fde474692cfa98a90f09b8301615acd72b3599c155c091400364c8b999b12e79f72b6992ed05f6da6320daf26ce233c53d454b163ed0f35a13945ec81881ed2596366605dda8a62c8e1a430a2821503e41b3e85947806f168c49fc8debc8327e3cfdf51d418fcb409935d2c4164f82b46ac4d77198b0252e98197e99688995ef1210b33b36a1fde9d0cb83294eff6273bedea445b710c9e2e69674dec0d31aab5bebbf04d2599c19bdea4b515ef290af99690f835d30ed9aa27f9e197e05a3cc668d1c42c0ddd459f233b0866980265bec85876ba9d0febfb0aa3c081459ddc22aadd4a38ae1fc7b585629f7a24efdf3f1e3b7b8fe80e7608453c777559f61a9a4ad9da368994b01d7c27241f300d0f88eba079c9620159f83faffe8e8fd1275c9641a03df89a4a22b79c22c55aca57d9f48bcb3ad70f2131daaa6cba61e21abc581983b80b4830b7a482bc30514073f339119a947004ca702e749b648ac00a789e2ceffd51880d805b1572e6e3ced26aa1346c367200d296bb07860e80be27bf3286eb9167bc3cd5dda183cc5573787b52432c622802e3f3e53c33628b2a602688043e1834d2857658c57678796571204635d65e5a02cdc954730682880c229fa96dece03070f8fd7b3f28ebf934141cc6d14ef8690b4259723f8210230100c34d82bf58ae9d7322455925258083fd906b3a0fb7c80723ad2b9791685951aefbad4b17abcf260ca0c36bf0e9de7ca9767e4714242603e7c38921004507dd3b28c489bf102a52d14b3f6e3038babd0f3d7d5ecf0fda0a3d06698f40a0a8a0eceefd87c5597ff8b984ef27e59983f56109fb87edd1a80922ed0f0c17398fced3e4c4a87ec194b78c0c457742793681950781a60986c10ad9feeb2a2f46c746e5da5dc56b3f452ddad3bad3fce1d4c22daa9175a0fcc50593ac21396840c15b14e4ad1d9d2fb172f8cce06346a1468fd85df35ef9a8bb0305bcdc99ad82c6a4f4fa55675bf83e49516778e14e130b2ea785d9d1ecccd80f false +check_ring_signature 1edcda3459e6a2f2e84a96efe86d26bfeaf315cec6b491b7cbf96c6e70785c57 17f0df632858505933a884fd406605b85485ac7a889dcc6c42d48c34de5a748a 2 e092497a45c9d7ac55222fff6f260ddd7d93e7c3c1492b6e68db002da45a9e77 97bf886fe857f44315b6faa0320c57e4637ec7d417bd9cd335bc509418072e5f 1cf25e13b70097b1540ad6684c2faeb0b102dd5c28a1cbb8f6482e60b5a2a800e519669ecb811feaad51dd3d4945cd482c602f340aac145020d1150dc0ba3406b17377df95b0213d35d78d1d9214b5ca9a43e338227f82467883ae224578d801c5c145f4e6ba2b000f519150d37334f3a27a4686478d7d613f26d1469b600003 true +check_ring_signature 54d8fa2b2526d85545e48b922c62de05508b95d2cfe925e67c9ab95c5f893eb3 29090f92a072b308f1b2edb0cfede2c62c5f68532b46f5556922fe2f2da8aa7b 14 6e923a757482c8146e7c0fd4ad4a57b00bdc7c5555dd85cb5790a0d5e21dcd20 cb127b63c1a673e2362b7aa94aa0d9646e9ad9245de1815f5cf81103026a8884 40310595fd1f3a741cc1c66b8433e1aa529f227f3c29dcb1693ce988b443a4e8 a6c57b1d19bd8ff33d4966db9d606d2c54d126387ebfb84033200ed4a736415a 22f8dc392e38173e681fb3bd3ab214d7258a176762a8cae6c6f9b22cd68623bc da17420cbd865d666f4b43406fff6e0d1c70c91c580d14a083a3d19828e4d7bc 1fd136cf247e798025e53c1bda6740d40eb34bfb69d48398ae4fddb55f86fb07 cb9f25255d0f910482e2ac0a24111ff1e81ebc8537df4a925b388b7ed7d37dd3 faa0cf232a85130f73210f5d788555e0bac74778de22beed7b3bd0f1b9476812 d5dadf7b6c8ecf6fc6cb1801d48e5e5ed98ae0fa41f7ac31b8b43986a8d59921 9355bde44da692c614674c4d560c8ee2f4c64aa73dd3264007777d8fb01698a1 d3c32eb52dcddbe343294c8f758e09d58a859f866de09d0deb8353d916a11513 f53fa3b8800ec94fd19d5e41df932a1365813ea00faca92b392f94d003c84a8e 1adfef165ed94cbbe3a25ae5c7074b670b13b7e62b499b3776442157aba15c49 97ef237f6cff9971d4a1f438e972efc52f6adad819617b9620b3ea7322051c0b7c18ecdc012eaff6f2bac14020413f919d44dc4759fdb9511b3a0f9fbafc640db0eb6300f0b39b82c5713520f02065c4d71464bbb0b615ff3e597b7492fad00dfd2e69849eed79a04ffec55c4ffc399adf8239b5fa615c59409a28fcdb5b9104d2eb4b186aa4f53bb5289ebbade042a6d40051273e95b4f18e7eec295b6ad9024c837538256274ae682f1fb104c53c1a2308b7acb22422a07c7d3481c56da207183785f68a70cef4738ca20bb4dca729a850e52761c2d2bd5ff78b92fdb0100455712956e16eeee21d9c35b848c0c7b226693c3336240e1c00321142801e78012f44979a560b76e871ccc571040ef7ecc7d9ef2652c927f653a066f61a1f54025dbe738fa4b5d1a71768532e97ac3203d42e30f042e0f8b52adea594517e4f0acc51655b36db444b88e1064b561e339511d839496e2044d2dcf36ab0fd7cd901e62a9f303852f2648abaab3de7c285f551c321bb99536b49c3da1f2224e9b1087aa3604a225775b1b200eb0ec6e6da188265d3b9b692a0b4eb4e727a63611c086577a37d26cfbc9322deec130be099ee324f99a944d6b9c40be83a1749be3a0fed0918fecd29294567f269e98036d0c5f6d1a7939e19b0aae87c8374218f150688dca5cc6c08494a67938686ff73e421e116e11ac7b839a94d143e3c9ba91a079949e798b08fe43e3e19d27c5c0b8215928c7603efcf09e90e591ae21bc947090d50e41c49ce02e9bf0ced90d97a64e02fc2f69b368a37d4d435b4e86c6d8c0a38a2c6c0277d11de8cd168231b40644e18f7cc188099362f2cf3ff90cadd4904f5f66bc50a5e599d05b56eb8308430de307eca6c8c325ab4b361a6f64b37100825af8124c6b0ae5ccc6c37ef4f65d6b28295202b6e0df6d003e36129bd5d100555f691e67afaf3fdcf6e07a19d88ba7f5c41982eb0018bb8706a1ba91f0bec06fda21d067f51224d21564491dcaaf21040db834e2846000907e5b2cbdf5c250865606cc91df294252376e56da40097dc8cea39ed617cbea3db99dee7401a660fafee3751317ceccdc2cbdbd073c03a7f9a2f40bad814f63bd20fc5f1f93f940025124559273b6cde5b0779aecdc53a335a5618afb1f0c697b39b5e025f94d7057498d9daf5bce0bbdd1a9f0fba11401dddb6c567f0f304ac0f8d9bc3343ab6079fb48cd2bdb75d206f7f006826fd9dc110ca5949eb0b19a86dea9fc41a1e1400 false +check_ring_signature b82a0a56fc47be14390c489b83b2ec2db734a7fd2be30e04a1bdaedea098758e 0668c79ad432db4be37bc8d066c498f9b978cf2236a77270258dd17fe9a38003 234 d4ea594763990a67dd48e2140bcb9b04ce2916a123b1204b50508ab5c1004b5b d79040b3f44786abbdbe46c04483d61db6db1c63698e51cd3176b32f3f6833a4 096656840b0ce1ccf0e10ba3dee0d3176ce5942114dc4790fa2defbce362af32 0d6528018da03b80c532eb2c2a0fcfe350048091684ea5575b1ca6c7a5c4ab67 f90d3179c0593a8feea2117ce9e34d2fa064269403786a0542e53a657ba7f32d e26a4abfb8ab26fc120e752fe03ba9f07370d870a4029be55a07e6d3d713f739 f7ffaa684a1110aba1e6024ef73c875bbf2bcc89f64a4d0c057fd381125607f3 4cf96ce43a28e2097824cc8348615771f621dda81aca81b93b197b1e1bcaa211 e34b51714609a12742f14d8bf6af16264f2ecd922bd51e88b93887f52440814d 01e9b4bb47292e532364207d5b845f9239be5d069d9f320fd479e6d31108715a 89225f4eebe05572ce9481bec78271be07254e8714e29adf539d6cc1ead8e39a 7bc8f7ff3a55f5c0b38baa92eccab265a4d9f795d5eb19746e17eaad14c77f9b ba81fe37356e1674ed495fc7824e1c6fa1c053509c07722993ea845c253c8522 483761eab0e63e0aab849f5c56bdb48734c053665c513c58eb1e0ea2623ac5e7 85bab5d8e6a2b702763dcdd8861e3622cefec27171aa537d48dc328dcdbd1bee e9040e78be0b65c2f4addded174e4d6560b8a619681ede23bdbe1ae526574fad ff9f74ba0883cf0a312ed166f70571d7326cea8c69c606c71914f8fd427d4a21 f73bc33f6d76bf5b679dafabac84b0305cff3c91fc9fda8d7237d4d01acb8ab0 fedf795de66bdb4bce1f95a75ef1eca090362442f86e069e9f9c7d97b8c34e2e e8797ac386c4cb7ca71e9240ba0d02ac0b6c6e2621584c9cedd5d0b5bb725bb6 0288051f217b63573db6d81f1289edaf11b082e6166653b83538ebf2d2531dcf 15a10b7858bef3f2e180b5e3c2d47e7382a8504fc2b9fad8e06f3ce284f2ee07 553cfda2fc916fd2e33819db943cc003db3d66ecc0051f0b138ed434232ca4ee bd084259a23aee3882baf9fa06049288b00a0361ec15d52cb1c634d6086a3e5e 6cd8464baab355c3b8b23e6f6b96fe2ca6669240a92803e9013ccb2efdc19123 81270a9e4bfbcb48a6181b40ed9f3ef2ea5f4dbeaadbf5a755851aef9ba83337 dcbc4b64ecc9d76c4de5cc1849ba07f03a06f4d0a5e3e47e3a6cb1cdb717255c 7f7517ce1c7d600a5708c386bbdf9c7f7b329b9cac0a7c599d1aa2d2786ebc86 60aea23ca0236e9a6b2d53dd508a6bdb7be90be7dbb744c5c026b13038341376 756a96d918e073ae58b5827e2f9e79cbad43bfbb2245835c0f5ab37549ff59d7 90cddbba7ec0c7807ee27170aeca3262a37c1bbb43b71690c7bea1b71e864129 b8c5cdd385c50bf430d1c2844f5e986fde5fc8cd03d85e0ad9135a18e4f1fafa 3e6f678efd7fa38739673b3eb5663bab442a690a25047d86e02ec39fb3627ccc 5477122d3fd5d5065972ca33062aaa5957829b1f51a171f2a8a42e73bcec9847 456010799645bbc110eaa9529b76d3a60d4e39cd1858abcdf8874893e5f636d7 41da679fc2f9dbd2ebb637f5e1654399c8566d014c2833122f5b22249201170f 726aa3fa22ac2d28059ae0bc8686b41dd0ca6023673e5b3a7b66028781d9f489 6537b09d086250ceadfa2f2825d17dddfbcd999255786acb7edda5c71f3b6b1f fef72103cec9094186efc4a936af672e5ddb1b35fd05f8004411db220df85fbc c976b0bb9660d66b594c28668027841547bda945f921a0bcc7d1b1f0a8ec80ff f3bd80989d538ad834cff821757ef2e5a9216d71195808add9f02747d7bd1d26 a9efca1054a85d00c406040afd93445df685d72b9700cadbd2a12031276d2bd9 74b89769dfbc76fe37bf22476d8439c226760b8c374e9c65ce44d40dedf01d4a bcc163b4c0f4fb1e0721edbd5b1753956dbaa9bba30146bacdad0101d6b3d183 762c8cb198a8e3173d191bb2b17f7de7d5e921efbdae5c71a25763a730acb3c3 9b53d872202bad28365f99a7d96ec0249a0047709b45d1912344c5e4472fd307 4cdf3f4ebad893f56e97d4eed2a9e595307ce4288eaa945693e919a4b5c6fdca dde8ca40f6e10c75c8fd4e868200958f8589b3ee98b8edb734eb476c5d60a500 7fd84f6d11e4965250f42995903723500a8205e8acaecee5a397385c092b83f1 82b53da8609eec3cac9a235c311cff2c2dbd806d49d2b5863a357c8036aade0c 9518de4a370d555e0c1667c6b57828904deb187b3b5a8dc755cf0045bf8c69f0 bc9dd7c9887891defe2f4eb53dd63d89571c745927f65fed809a70fe8c1e42ee f3a4b95a765406013217a18aeeba1cbcef2c5d63e3a9fc5cda7f9b64f5a5122a d47c8f65466de0645f6244447ab1fba08fa8090ee4b629a719adff86f34cbf42 a217bbd771d8ab988427638c7e80a3970f6ff9283c63e15caf2758af42a0e327 d03f2d85f20aee6050f412a94fc89e5b7a0423c91ed429799d6d0db692f48465 bcf0ef728db0595578506658a602ac7c45cdf8133f61d339aeef91b4e93df459 7146b0f85fdbe23043d9acb7658522f339dc06d87ae235b518b0953b0fa68cb9 fec656b6b260a141d2a436d7ce4b4614cc7808807245aec6812b4b2db4f85e48 10be63cc8cbfa811f9f2bcfb8188aae6c3edab3188d3356bcdc69729c136cbca 1ad9628bd83b90464be02e906418076e5c096b118f7665c57c5d1ac8ffa238a3 3a4e9a24ce68ab83ed9177929ece225cb63566bdcfc276e61dfc2e00a6ca6668 cebefcb8dd69e3b4a804a9aade88ea1d2dde3c6bbac1e682645b38ccd386fb80 07c8fd7c47d3560815ccc5b98ed35c10f005867d0907bdd56609c46b217489ae fdfa31226145eaeb43b263c6b9ab165f2613d3777c2c78609bd8b69ad9ba7405 ea1694ddc7165b01c33221fff4710a36a86f62f8ca6f52725c0e54d50be45903 7c7ffe551f825ae57274cd3feeec87e703e7719520b006dc0ebd43cba32136ae 67819e3f1e12f79c79bfddc1b38c2bc3227d1d23efaa6dc4818dcb65f1a44c0e d9174de15fe5cdc43754c317e9496136b99d3ae79b5abe84080979dd11b370d2 7d30fcdca42b5c3d595da0c4d1b4a9026a9ee97c035864f05287e4932e9e36d1 513a209b98e476a367af8461d0270de6ecfed946ace6f64093a7c61883d4cb4d 5df4c68da7d3b676d9cea6a271248d8ec5ecac6372c25870026db02827ef5a8b 600c64658a74b0b4163c79c92595e29b853177b897bdc3d99f4796f386b0cf6e 8a82cbb2c28a95b2f9f9bcb3a07498bb98890b640f197eac7c2131677f28a5e9 5be00598f1d859e33d164aaf31b909a90b27b70cee7c91942268abe350fe640a c4b52dfe186c3c3651033d81a7a1411dbb1b7519856556de049d08e69b0b35fb 015694dd1ce38d02c49a1c860c0f1dbc52178814a3a565a35d29dd3ab5e81e87 12852b9387fe84c4feb3c109bf9b087bfcea2ee56b628a02678654f54db7a682 20896292d444fa474c08d0a060398e76d877c4af2930ee95ef1af72a36502129 93b40cb1773adc6225d70721fbbb033974bf046a8416e98f3a7f4e67371f335e d64c81d41b7d95b461429142b4363bad0d98166cd8304266eb44c9b51788bb12 57757b8ed0643ce18fca6aff65ae950846c7b104419151016cf5bb26b3550378 6c61ec70e59a97f831bdcf187b31ab9492d75731bcb217950b4a0bf1abb286bf 911ca1dc0855c94bdf9599f40b18a130b41d22845653278f56f54d7cf1ef1e4b cad6881eb547c251fb5012e449dc8fbbb839827d7b4d4c233c9bda9c0c8a770c 9f5f6a5a1e6292a23cbe7e5a0d1b649d7ccdc8d1928490213359c7cfdd9bb478 fb96560637b801fef115202b2387b623c07cc2d5eca16d32adc7d482875622b9 cf9cc3a044ec44c3dceb10f429bc9114e29106826ef9582b93d5ef282006fb4a 8b6cbb8c68bf5e6d6feb98a688b8c2be385ed8813e83b6c52d4d6f977f884a97 33cee8219863acfe5feb5a648fea1f46faabcc6c5d46881a30cf989426f8b308 ee780298d907f8cbaa53d2a2df4277a96768ab509bf8ef2a38de90531eb330b3 dacf9a3330c4a5270823a51f24d1260a129ef27cf3bcc315d1eaf0b37f5c5e20 5c62e0af21212a9a103b0410d65334a1ce8aff8c11dfeb82595ca3084d7e0542 1254dc969d77084b44b19438d152173cd410630f3dc49962c1ceef4eec7836e7 8ce873a41d6b71f888cb4e532d53bb913c6ded975123267c59902b0c3437fa75 b8e7cba05f9a55acc2ba13dd8bdc72201d5ddfb5b463d325ef5be7500fa847e1 9f1239e25491ecbf6751224aec06172eee22edd6cebf13875433afca3184b8f0 13d3cdc2fde0cb02d5ea9ab9bfc001bc14526ed8a838333f26eae04b80be8dd8 d76ec930c46ca11eb8b3499d55bb62bcdde87f8ba3975460c4368545efaf40a2 50a018471122842d7fdc337949d8d838a5a6dea4f47b928342cf8aafdef7b7da 75c1164adaac6b46570b8ff1dbade92c2b7d77d125c11871b68bf8fcfd4b4db0 80deca353bc7344f419a2c38117326474744aee632e8568cb8f288c3ba628212 4b8a964f5cda4f3a12c80f5a395262f478c33070845b0cc5eb80391c20c5cf94 306670190569ecaf40eb35eb2016e64b735c9eac12b22f4a9c3441bf6b7bcd0f f7f01676244d9228571fb5f34e6e2b8f357145f4040e10d7d13a75ef3ab87a27 ff9094fc51a4ae0b16803a0dc30a77dfe798af34c113248c9f7435811a8aba18 c5ae095e3b18fb754999843a3feaaeac6e2ae54cac3ae9339ac0d51eb745fa10 f4db33022a7563151b55ec5a5085b1e3f7f0e84908494eae0e290617037817d4 b8eb06356b005dcae8bc2f4cb5dc30b6a5c36927d0267d6287ad43c38d84ef95 9f4e3fc651aee924be6372b398aeec2fe12918cb5526c877dea29ad2a9242768 67856c938b46230808c897f490caccb33ffc1efd7813be9f048ddc8651da590f 5f61aa88135488ef3fcfc332b6f840f33f9b2eb38a8509e9e7e7386f250bc310 07941045b52e5dac9e87b4e62f373071e3c76c4dfabb689bfb9bc25ec0030d50 3034f027e865200804062e681c154925c6d71e95ee841358ece96999fea4e435 d09d5fc2cd8cb70d7e5cfbee29545e59bb06b33d5ead935d7c8e4731a7290730 e88358d0a8d445b2c44ec7975c833cb5bcd067ada0dffc92490debb4dfcf74d6 effb326310fa570a357ad224d64fcbf02852009719dd5b89457dfa695597910a c23e070b441448b6c1ec3798313bc34613f2a075ec3cf1a7acb7871ad78a0244 199a54c431a0c3eed0a7bfdb1257fb1a1e660c5caa3fc6627bcbb8612a6dddbe 99c256a893f85083ddc128dd8448765cce739aa727cc4b85812f0cf4501455ce f026b2e92edd614e2ccd160187f5be9a7df54698e880117c198cee04df6a44ee 2fd93a77299d66904b436b4bd5faf1715576d92955f372a19d31f4456be42e3e 09c2e52a028e30a0725621931677ebc3d747a24e9b1c10ac1c4e01687dd79dcc b7cdc41a36ba52006ba537901b0cbf107513064420af5087c526ab1f2df8176f f4341e5a75e89cc3583834b88e5d7477cda35bbafe13f7224a1f3a005b0af22c 342f921866337df4ab4f600dea46dd6350c731d0b70b3742cf8a39176a06a310 0873104958b544813d869926e1d6b869c61389e21a546487ecdec229458d6bb4 fec0756c8cc91b162dd0d8891fd14ea636a49662637816577353d87280e4cc7b fabf542f22f347d2fcdf02e5c67283a7ae6c687fdd68dc034fe828bfc8ab6628 57e87821c354db1c6a7d310822cce6f12eef8c53727a69dc27b7ac1ac93f92da 71f63c2ce8b3ef062179054869ae8f5df6b93bc694b94e2cfb374ef94d19a925 4c2ae0be8dc311329f29ba019eca5343699af5f621b54e88251282b2e3746de2 213871557cd20a9a2838ff4982dd1a8a24d23b8fc0b886249edeb1fedade3add 31724c095723cbb6d7bc2bfb0a9ee7dc9271679464457fb2c5c57b4b8ce98efa 7ea7914ebb97fb4d93c3752a44c226883c56e2346ce11422f4f6b6b26b57fa60 f75161c26bac3c5a99c7cfb5d6f146e147312559e73930ea1106890ef5c82cc1 6851052b6bf8638cba202a3fb44323d6410275856faa2d43811b2d22b1d8c485 f00649d86b4eb1d003871603c46d35157fd7ad8b1ea65b14e98505507ee340aa 2cffa5fefaea9192b9e7f8dbc72ae865f85e42621532b3b24c752276f8bc3be4 5da8d3a15f311afc31dbf4fa52fea8696657479244e7c71682ae0c35c986885a c53e7d29e8c83b77f787c301d1ed36f4b1b18285140851fece81259b1187af3e 9ae27344953a0cd88e98e6ea2b9dd22800c2e5bb620666a6ffaba2c7cc78229a 32d485476edf97b3de09197181a40655cf96e6a127a49e7bcf6b7b84f6b8c29d 52513d0562d4117c1fd681dc00b0cff28f4e263d6179941e5efc26110f8766b4 a89d2fca880453476968e069604fafca8f502f7276a20feb03d2a7b794901c87 bb455762d98f8ddea140c63c923bfebd05e8c28c414baf9ab51eeb5a33575742 76474da8463df217256c7a569a3bb15bbe635fef31d95a5be8ed3acbae6ffcdf 575d02211955f1ba52226ddc942fee0b9f2c9b3e06a203d4c816bf68611b98a9 9f06ac6435f3d79d3bd5b2f6d5a95f56af66226422d6697609c23b104e99d289 af11050992412d535d5460a84346f6ccc07e01f6292a23d0072b1e7fabf98bef 35a239625a06f97314ae64e9076170e3d2eb962eb6e35df13592b0cef8efd754 75b8a08d115728daee34912027569bcdc9c002f9207b99fe9c634ed89816340f ed307aee18c9edf955be10f2223bf3e479f0fbb3b4a05d5e1efa86f4d9e86ab5 c0347464d885f2e21fb6489372ad30cd5fd4718079d893ac4c1a14638ebd1289 816e503acb66feaf11f025023dbf2c302fafa41122cb8c65e0ed11f304185eb9 5fbc308095ea4f982c9a9cee4eaa91eb60c09a6d92de66ccd141505c10c3a268 985cc85062ac544144059947e21acb8b4b12cc975ed4dd099f173553a7669506 ae86d9687671c25726ae1c15a941255d1a46657048787064511f0dab4b674e10 2475839d27d75972873eb676354cc57cec2af3e398314ef9dedd71686cc0d0e3 cc8de51b9a4065edcc07212e51a2c21515f850402d7f755360cacdc4a06dbf9a 3425494acea85db0398c533a459ae024d366e79f5337ea1a4299a5feb3971886 7bf942d060825ea3564f0c53fb2d16b5ea9b2a1835c6ec4d168b16f2cdd602c2 3ebf32d37444a09ef19253c5a67170274a2a3daee4c12f192c373a58020ba4eb 7491700b62aadb90209ee3430ec2b4219a1ca99d074177afd54879ed35f9a058 4b95489cc08ee04f6359b4b70f7f201f291d276fe86486b833d1db0b5a07161b 84c85c50445290b9811e066f79753a4942aabcf5b330951c8926c651f2645abf abbc1355bc9d2fd2091188b5d6031f2eb10d3b1ded57ddf29075c4fca10e9a85 c1a8df87daa01e4ac5d9a5c01d1cc6a424f9b64488f2611db3da879b855394af 50a4306fdb369d9a4735336a9b19c243da4871a55f061da53ecbdb64375d83d2 5295b1f715cf4c34a3964b258ff898dcfd08af81245918219210d028fb5018c7 7b9fdacffbe3f49311ec5c4001cae747a308b923dc3ee42058c4de09bda0d3b0 0dda712485d213103ed7ca7092df4f1a7cc34859009c2b8f4afbc1e320b86d17 e5a5d1c03fa93e637867f8b35973b2dc444b07ed01983d0f605cabe7c99ee139 016e2d8c5271e5a2d4cee1a72ca69e18f246a0fba9e4c46c723c2f7c3e10b4f4 e90860ef9525b2b507adcd68f5f94e99ce9728ba124551e30e47e9c772d0326e 3ac27445b5d1bc51fc7c19255b458171d15a6cda4f7e53dee0e1e7756b29f56d 4ed7de423c084486560475d157c80b1901d0fba85d55bf341ecaa9a3d26ab357 19446dec6ed7f026359be8508176b1614657b3a74f045c09661d915ae6a0a04d 55080d24bb7dc73e10f6f1e63d5feba428c93936d584e6fc78ca9addc8131ae5 6112d672259f7701195bd55d634948971191e43d21fce8e73f10cb6874b26fb7 0bf373a7e5b739fd924e74d22d1d8195da4a39c3db94e22ee3b28218850ecb27 e4f7bf2c8c28316dc7d7f2431500d21d647b3abc4b118527b35f6c11d903b469 b5767ae0be7da4ceef8a138ad58f4175416a5eb8e8a232bf947139c37a1f7d46 8394c8701354d56e7291ca54fc38d655dc9e64f428260f3699a51859541e74af 4e7192933b6ff8883925a107b6ac045dbd25a4f8e613e5f28c4b0574cb80eae0 bc2d6f097e0b094d452398383d52cbcdf4ba3d0a359f83d4298c7d7be5f75eb2 d7a0d2a73678f8db8e90dd7af89ef3a9a7b4b18be47da823b20bd9a64f81835b 7e4fdbe982b3389ba0a79b8ed9acb5df811c2bfd804ffc754d3bc0dab046f306 c0709aba9d0c2f903eec08036d7a4384cac0d60e77acbaf8f5800a8e64b13e15 0c360152626e993bfacacf728f17aa4b94d306117e9d2327feda6fa3f0cf8916 a64fca1eeba7132fc70a3fa6ddcc9d793370eb15004a857963fa623748082520 162c3e9708452ef10f1691030f6eae4f01228d82a0780fe8d72bffba165a1ee2 aeec3c3e1ee5736ba30fef407401efb52900407683e36444cb7f85f294d3c426 1d7e7bc85d64147542e6648c497ba165c7ac640f7d2580665b133a6ee9b32155 4380b76280dfcc1f1d1d0253699a77faf0321fa443bbb331b812744d3566e9b7 6e8f18e9317bb3a7a4ae9969e37c7c83433d3738250ab5648c036e6e7f44a1c2 216be1bc22bd5d16bf30906a9e1dd3988c1a30e4127db415fb0bbc29059d568b 0bb22a1c9a46087701789b47cedc5218c69cac09d093afc14a6617c4c3c703ec 59fe66aa5933d3e1b0089fe5c384af93a02a034e4bb35fcdd6924d1f2911d651 c1c43ea24c79d29620a49a400b98cf1010c15383913e5e4650c5b8a7db57f006 b37ae2751fdd948749fc5be525ab3208733b40cc06e342f72be88e3ce190a20a 0aef5df04a95e8de623db39464b1abab925ec4ae5f443d55d51f6a86d9820f5c be07fee27757c162b599c35d3c13863ed8cc5d3ed7c0f99642e76ced2d6460bd d6ff152fad9ee44a4520f74333c89a19226b75c6eca97b6e7fa1b5e9457e005c 508d396af146329adaf415738c80fac849e03b6edd77f7bc6ac41b9ce9269471 24731bbe655aedc61788290a17e14ae1202114c05790d969cd6865b392a72b8c 324c851c62754b2cf4f90ebc5199c8a5bb297d92c83c421e4691033974561296 3e7abf88a7c8a8cda8c7aaddc3f53d37ceeda664bd7b49343944492419886d92 12542be21f97f33837cef44a897127dd97cebecbbb7c2ba2488a4841bf5130a4 b21447a35d04c5a6aa31387418e8672e0de0e7ddf024a5415e872981c376e8fe b388765d43b2325bd741b184767a46a4d2b07e89489fe3b8f64cc4423c56fe77 9ffa2851005267e312a3200f28cb36df083ed7b4aaf9e07e9cc3e1eb4f8ee4eb 4ba997f110dbc9117d683fb076abc7ac46caa3232be90f3f729f1cd9b5a4d5b2 f4ce5c3ce8e547535602336df63f40da51d8a66610bd4cf17ddac996e8fdf757 3308f816fa22860bf1a7ac67019054b1c95eeb075841ae5afd7c073e30563f9a 592a4070278c9ab374d5874aa99182b1b6df4ca1d53d99ca5a6bb924b2265935 9d2da8240fd4f4ac5671f3ed423631b375e84c10249cec4e90782a77d438bc7e 018fde2172edf487a87f643798f9df39da8f485ad689103a39e00f5ee7e95ac2 254b66d591f4d226a2117868e8d83440ca331a802493f4413cc0a68c7128ce29 a3c5dfd50924f295aa60d4c71c43360e65f4deef0f728131b82adc7783532709 56a51b4bd5faa58c6e420b3ca799d4f12898682bf84fc86026db4672ccc5433d b523dda8e9001f1918034e5df7a88c9d3dc5e323cd327e68685a03d90e9221db 34d00a787cc3ffc00638746614b1a8af85fe05bbf21a613bce27bc2fd3b3e623 9a8d5f8bbd3061771ddb96205cb05207c93f749f58908ff058d1c1d0550ff46b 6d980251aaef419692a7c1572d67f08e2a36babe8f0f77ae832c2d0fd3091fc7 783a9c452f8160b7a8bdf7aa9f532313b3682d9c14283d3ed50c1b27e56e33ff 8832fbc6c717ec6cdc9920057bdd0b71545b0b4f1725c6d0ab47cc9285144ef2 fa48c87847bd8e43611912a8d0158066a3c1360278dc900485789860f0073243 56140642e35ac6c8923bd0b67c5ed79b6b658327dfcba579cb6a567d40f21d5b f164a91a10469854212046b6bcb90463738e86506cd6ca23860ebb6b9375146c 3824ecbb304389af0b349fa5a53333bf07db89fde7461fd16d3e113dbf9cca2a f37835840763055c82645a0c8aeb89fc5a268de7da33c90a72aff94f7bdf8d1f 560744e9c934a61d82a5ea3b45760e5a042455a32341d3b3f05d8087548ea703 b881762cad80d5f64bb5ad26ac91c13c24900ab8dec0065dce6f41501e45a113 bf37f38fffb30c2fc486cd59d955490a6e172a72786d272ccd8f78d49b9cf804686a30b01963b98ec762064adf493e646e8d765b5fe105f267aad6d373f18f0e8af01368f26b9202f06e8efc99cb9f1f996da35e5a5cf7f4bcfaf037fee9710eb150640b335220c95294f1acd574b768f8beb9b681a80ac7e97a47bf8f59450f499cbe31ede5dd2a75abd52ddd3ef4a03919495f1e29db28ac14a2b8498fd60873b09385ecaa8c845b3cb1b85ab0a7703e21e4cad5c36c98f6624f963e27dc0f755bf36716e5f0830d8d824bb585a318df20168c3d1eebb84f55664ed174ff05830657c7a27aa94d613ed190d6ebc4299f7a9d1f18719e4a07f09383b3052c01a9bc1a3dd2855e73de98d33c2821fc38184d203171dca14de9dcb119eca507020367e964bba2a4cdd21bb7daa8902169d5417506a41111510b04591a2370f003b1c9dee2ccdeb39d60dac90884cc447ed9ef8be4a604471ed07aecac8b01850c40053b81990bea5ee74cf0f512e5c57698f6ad8c43882441ef302a51cf829404b53d9f598426e0699d49bba3dbbd9321c14d7d3b7bdbafa4156bb97c2106a90bfb448e730b9b49f428e49861e9bfd763716220c81051a68bedc45f908f44cf0a817e7dca0bd008d686d47006f50f090ce0ed2886996820ee59fcf0b561df440073efa8c23a3ac1d76dff076a685881538501f0f3b86a96bea88ed3bcf33b1607050ebde795dd52216ad08a818bedc3bd489b30cebbbc3dfb2b09e14ca4478d0b9e623e9574d643933cd04c3ecaa181daaf089d750c8e466117ea4a25d2c9540dd89514aa50be85bdc8bb90a86f33ec2f470266cee953d11516775bf8b432520f1844975e12ddb05c5616e8ac428db9c7d922174e07c79f5b289c760f64218908e769e35d330c38805d717921242386bbd17438a9d50670e089e03a8e61614508c73ab7757b208da352018abfec42acfbeedbcf56bd866d5fb27e6da9859f4e084d6cdcff68975bd14130f53ba88858ee50d2cfcd88873af20551ad9caf823309e44366c9b43c1091575ea1469bd8cc7893a68bc27b36006e98fabe3c7ac0950ea3adf98597a753cd53454ab52bd76644138bbfa38d99c8597fb1dc779e3a520874290f12926f35e1c16e8b392437cf47d0d40543c0a35bed001a71f792da2d06079ff84543a3649a7c8091c3e23f611bee3fcde5f7e6ec75924a7867dd0cbe0b7c95175794beed9fb7c5a17d1279f4353e1ea856117db6ef038eae37b04b3a0ff10478c6e6e3acf0aa1b4b299f57465d7242aa71e5d35e047e53687d1de70d0f13c1a06c915a5329f924fba735e0c2c0782fb015097f673d9d136267778c540b59625448ce38e9cdeb3fbb6728e881520b1d5dc8ce6096985c05904d301851065b90483deb0306f7345bdc5794d819e13892881fc7190c540b7f0c92c6280202ccaab84ee64bdb738b5ed0b2021812aa93479c4dc34aa883b9c71ac807c5bb0c10d0adf8329a61146ccfe8b7b760fcff622379b01000e1bb4e0fefe767ff6601e5cdf0fbfc5e4bcfa88b838cbcc6ab8f85b2d25c96c698eb56edcea16bdf3b08a55dc30be6b82530953a3477c837d0b30c4b2713eb5e0f00cf27ec4472a2f00d72beaab17dc94fccc288d129ad967c678003300c185d7bf86a173b79e93e53032db2a8e78f5b3bd3543356faa452f2b4d62827192e6bdcd025f96619eefbe10700c2ab1734fe1ca5a8043570aea42bfcf6f5dde62a35c6e872eb85df781a5c0149b2ca9ef83053f17ef34cba1fd2369dcd8ffd9e89b53366b4c063282b425a0413bb6b28036b3b60470161a488dd34ab7e30067981ad4b9600e55a0dc87a5e009d485d5a402c843a4d9b98cc35d8fcec347872a18da5bb0d679c06010d95b806376cc00fccd6a39395a4f6adae983a0ecc5fc754c54275a207c186c4c8cf6c0163c21bbd3f7cbd25cd73fa4bf6e0a28e8ceb15c3a32aa4ebc5fb8ef3f9dafa00d4f966606220e5cbcd4ab7f09dcc734bd0a195c3a1586d46722ea89706ae740ad290d70ae2facdfe06ed0b4b64e7ee8b85d0e694b16e8d1d2bee979dbe027b01e3215c29ef0ab35bc908e1beb1bf1e6efe1fe2a7a687f0d56395965b0921360177548ab2af2261b963ed43022c658c438c77b3ed09be2d012a7f53373dc10c01d5f9a4734b6321ba8395e17b1b9cacc27b84e9bc4410bb4f3aee8b37b9e7140a90fe36f150a103edb2f1dbe94844f4fc4a1933b71aa64f8796c0fe47f380d00d2c42a01539740adf993d8b2fa6e309a0dc223b0076f7f4d5159e453ddea90e07ac3bb02bd28639aafc64607efcd7319430fc5f521aea76b4311eafb3cae99b0904c4edb92c5ed642bb6307b9de0f55e3403f53c22241fc475cce7ca32bc90c02e801479971a48ac0c6fd1a646d4ee59852f3a9df46348dfd39502be5d00b8d061b80588d4d8ccb5e05794ad664b0c33ce168836ad525a2ef92c731e217eaf40597e7c5fba5a8f39c753cdba0f148ccdd6404c60eecd63e3e33b6c4691aef0c0b36641bee03ae9f6595c8ec1e96fc29b9f79c31ef902ef3f046936850601a47076ba91054181baf5c6a978db104b4595f0d2107690012d4f94a557d93e7ddec0ed32470a3b8e179ba45360abfdac9f39c38c825d08384f19b33784ca8818fa703600e488edf977dacd3441160f70d3b198242b868b16368c05a5d5e0562b2ba0b6c2343317b3833e5e94954c76599bd83ae9db4a5e6213fc75e9ae03496d84208fdb06ebb7da29b40e7e1d6f0f272536f3ae70dd4dbe5540669d4c97d2864b40640f65f303e54b091e2a134b993c6f161a6dc7c38c063fb4551edece061f95f0161c7da1f4e2a3cdeac50d9a9faf56c1c4e7d9403b5f2e7492f6fd9f8cdfa1a0763bcaafe97be53b2c5482b175e841c633851da21ae971327baf38ec3ed48820e4120ed58bbfc3e008a3fc025a182f6ffe800fc4db1c7d6ba1dc1968fa213210ce4e1466cf1d6cc808ddce4a6179d24fa72918a004af469a55bb92758b759290171e92776f66203ed2b1bccd46d56587a35d27094c62765990275cc67fadb5f0cc5421ee022a1c81566359413567245736b2d5a443b67abe02b88dff7d4b2550769b25f0be5f195925eacf697bc941cfe1eeb23f893b3650399efb14599cf310da01b5000523915d1c142babb273ea8dff59669f1a774fd61a728dffb283e4d0e518b2d6f68a2986263247bc40f2f9dcb243e91ebf8d469b13864dad05be2b90823c526b50778a897597f7328fad2ecab806c49661216bfc74301244b47464c01f18f46dd3fdeb08b4a2496cd071d866078543dd528e7f2e9969750bcfa84f80d84b9beb480af40d6b8b2684920b6abc4e1a303f8c914dfde0079b64b53c1f60a8e700e53f6920a5be1cccf33d2fa32e2e00809126ded10543b26bd2ba726b40338c910bbddebef97cff140e19e61769cdc07d2034488b73e348970dd0004ff00f775d1fed012d42a32e7cf3f11e57b6067c22f16ffdb29d117052ea78485d900a732500b947ba793a112253e69d3919d072a0eeabf8311fca7768cc3d9fbba04d41d84a02c803f2e5a494726bdf2a2cf453ca64dc93b8830bb0913d4119ed507c35d0d1bacf8466e6d4bba42798930aca6e059f85a0e728cebdb7019e3b08200353d08a538a283b55cc7c004af272732e6eb57a442cf60077d5c0898adf8710f339031f8a313963a3550d468e025991b27c344790bfede0cd78e2c4b9ed520034c31494b9f7f5d9b6c520ca0deca96118c10cae7daacc7153a0d20a5bde7750b7685ac3bfbba9b262672c7022d7a37014ac2420c5ead07fc00effdba95b5d30d232004285ea4b3c28c2234b1bfaa412c03715308f1c04c2aaf159c21ddd7aa08133f1074d318f8ba0eefcfdd1d6050b007c4e958be3c9c3c7ba15a01bd63ab0ce150c600a3bdd0ec87dbb14c5595639dff5e117207e9f386eec3d6ac8e239006674fb54368bf7622d64bb368705e475819d255721a43f884e738c6c0bef6b90c30af3d1821b3f2eb2b4c45025cd8a4154e61c87ad8d1a4fde08d6a5aeb8b230d449517bd62b1fb4c201dca0f22f3bf59a60c35651a6a91b376856395df442509f241be7f91b9343f42192a8a0df602c9a8f776c37e10c5a0835a9d5d357d740bb8509f9e31b0ee1a960cd75a92019e54a4997e1b21ba11de79728fc74b7a1201b6751840dd743720172beaee634c084ba170f858ba036a218c1109d4f53303035e6e2a5f075e78772ff908d6f7b828e7a0bf86c8b08922c04246c54561ce3201386ab89ef48becd9fea94c6c5f6ea959ed4f243c414c19d542c691ab77741e090fbc6124a5c46a1296226539f805cf23df3124811b5837e3cc3b950ad0a94c0a2fabfb57f1a7ccbc7218895940835a9f980ed27ae5fc31f22e2748e44832c20b6d4fda3597ac5a960e05e2beb844c1f907e978a82cd35f53fce9a67d32e3cc01aa78e5c02496a7d1639e8fe959faf203f1538ce85bd85f3a64f0f85b3fef8b062530913cfcffd6fd5fc42ce109fb599481aa8a1800f67b1b6ec4dd954f348901cdf179eb2f8b0c504590431762d0a5c92fe47bd514f37022b9a9b720ca6c7103679a5398548f7ff68234a330f57d7f66c944bd42e0b289dbe4932789dddf0f0f882a710f7d9bd04aa630545650ae46dd6d4392bc111816d922c0856fec93fb098561cbba8956f7b5b3f54e36b2f47abeefd93196b5c36e5e73fd45a5a709e001001082ee69cfd8aac548a0e6eb7b4edbebe9ec17913c92ec6bdf2028106ea7074aa17de1c4052e308f0098359aff5e8ec4b1d060d4e3495c75abf5aa44da5e09b9971113e82cc03f897089d6eefe1f4035cbb027bf08b31818131baba3f9c9038e38efd44e03d9d6aa038f8d5909f3b6f0f5d23b16713b9f0ffe8834644f6c0ec0e7699d1c1f7d92f47f06b1e7d87c0bc8448abeaf7bc5d60cad350d056d2f041f27c5948f6913e69cf01a30cc51b98df501f509c441eba7a984dc83432d83082e52aa0e7665c0cece22da2af55ec44189cc606cd353af900e4e77123b430d039276f2c54889a95f69646ea7e4cc47b9b9a2f01ccc5899484f8c85a06afa500b204e350db20ff0a62d3917f908985e0e91f8520ed194e83ecfe067d7d76f8d0d53383552cde233ab4798fb6f679029bf9a1c764f826fa2d1ff4f87e245ccbb0bfd2cfc08ed1032bb95156ae283c085d5f4a422e4abcef9ee4e831b5874017404d5f7222793009bd80ee8d15078332db9b076cbc52f59e3e5098adbe8cf0d2a0bb1eefbfaf05601de5175e975a6087e6d89afd2d53e7d5a68b20cce2abb55b202422ab26c5781cb5fea5321bc1b332e1abcbbd8307214db471265c0df6dfdd501f6d77ecea2ef376bd42d3c21c64ed253349bc2993ec79daa2f79b6dae089a30c570c4cfcae6810d1681b0882048d088973ac76f61f9e82aea5cd9f052f823a0e274ab5021c856da3a4a3faf5a5d0db9d9e48736a8d58a5fee731d4909a3d2d0fe47c31747ddc48f918e4690788246a2106b0ff9b36222549106cdf30068b790d879df061734bb16ff891cd1dac14a3652c79d0cf31d5d7649b1454abc9535d0ea427e35b79fa567c71fce48b861f446decc78d7904b00e6832ff458deb4ccc0114013042f59b211177368c94b8e8bbed3e7e1953c6c1ff79df45ee8bfd858f0ac40b858f530bb82866203dc2392191494eb1b25fe5608ba8da540f6fd16bba017bab1707203d7525a4b1f2233d1916e72e2526b3ccfb7b98afcfda74830d210f93a64feb8f6cee12f05873d8f0e3caf32b91ae0aeb6e4d416ac402da1287cb01a60832cce1a0a2f747688316dc608d328d07b86baf32fb258b6e04e6811d870494f15d91e0a7f880f00dc8567325a4b6d83dd4a79fb10e1ffd81ac2e5c73970047d8d19d327290559fb18ac7573d766f05fc5bc8e0250c167411cccab15e77061050cbb873aaeb2375d3f2d073bb12aada6840ea0563fa18a0e4e9c2483b0209ff7dcc7a2bf552dfa3cfdbe41649572c6c5515561c7a053854eda25713f4d501f621182b255a3faf7fcd3a0c9de21a9b24906befd65fb0bd89f61e5663d065010002d64bc80ad81f0605eeade8d2c785e79a73b7b9932bec160c8e6696d1e50253ab861e302ea9abc35bbec24a2ae5b8100f9cdd2e4bb93d96e7de6617990503ef4d419f882a8f4d50e80c65be4c07043c1a06d89c0b22cc533772aed3b4d80d1f076a3a479cc79f422506d564c538f7cd48d172ca8735e7d8d489014cc089038e427fa6e1b102f80ec536845c25c8017905bf1a94bdd25a9b3f58a21c8c530fab2248b14cae5406fc6736930d8c485c6819b09756466a2e4511b77f75eea50c37d8b6f6c387d6bd0be674a9268b877fd67d17d1d18d68662dbd29e425626009689474227897bd28d126c2b17a41c20442a2d4bc6e913b2a391268f26a6b650aa2f0f5a3a5e486a159c408343ffa8a5299e73c460685982ad1e1dd22da9e9e0901f6ce513a03e527e2014315037a2cf1cb9de85f263cf33254a04f2741cac50f336a3eaa0c8dad12975fff693034c3c3aff149bed49a64dc29a5ba8ba269d5081dee2f5e5b4ea1836ad58acd1a62476c117f09bcaf72d5fe9f0e9529dbda720706448ad95301d3e0e19fbe28fed2617ef51c57843e457103af9732b013fd860195ee4dc88132806800546a71be7d52b6c976990226bb42194830a3a953abf702d238fb75ee3510d5b107306fe84886028f841686197a34ad9926f4c821f3d80ac7975ee937c57b1dde563e4a25b1b9db0468a24f489630ec6562a3fe5fdb6b0604dc7236d20bbdbb24d5e0cb1c4e54bf894fbed17f7a56ef94cf7b5015c93d0b1bf3158a061481ce54cb317d9c56396feb0130603a08a85fe5081e840c66af0f212e6ed755059fd21bb9dd1820bad73173400aa46783bc37e7396238d721a80cc1bd976111b96a560f0994772cecfa0a1bdec5418793f0cbb817ff3c536ff00b2c1e57fd5beec557b14f0a10dbab5a0e6ab34e234c1262c4c38f41917cddd40a56de22fc89682da723110480642a1217a7a153101cb94eb437dc5df6a34931038b0872d3ec664d31df3053cea1f84ac14fa4a5fddeb1f313032d4b10b49c1d07560547b6667884d92d7d39bba3db98bfbf1d5fddd18b70c1b9f6ec4403671e078b90c48e73ae9d0010863e88fc1352f1ddbe902125c7fd6b75f9e45a6c621204a277ad8f9a90d245d451dab05f75e26fe015ac3dea09ba27eb01c3235ddcbb0dbb7cb312521068b64294096f33dc4a03f1f749e8a24690cedddcd82408cc720768574a1c3a6830244e0707eb33ff80352c7c949958ef57ba8408d75ab0985f06aea84a1d8e4daf5770c2c3bc90923b8f701aaa041709510a9d5fe6b95eccd10d4d72cb51c13fce05984eb39f63a67c948e2de7275d5759311c35081bace7270a4b05c4b1d8cbfad63df92fd696d1e6a6ac14124f806fcedc6e896f6ef6c06706e10cde807351bfa3da63772c35736d92ec0b9de184ca8f357e0baa89ccf60f09362df52b3082022f97f694ad8cfb4b25255dc668bc583bb23c2ada7045f0ae0f180c042f257ecb668807937c38b225fcabb81f8645cfd970418f44cc8e2214008f576274baa0ecf27aef48b94c924637eff4aff3ad756ff60fa611d999022d0f28f87f260f526ddce2b84ec5deecc0bbba2baa76aa8a4a26fa00e08f82f2ed091ba5cbffb859ff7d73c5e5903239ca9f898337c538cb86aa9e29ac19c5402f02171b3f2bad1ed545bd0bf0d9f4ce538350a5fd00399e5e5c6bdb3307b9ccfb0e75ab12b8f68ee440546896bae530493f837aef4458b921750ffa555abb16eb0b2224850a844ed113e46d601e9c93e4d91aed483b8e0693e46e860e845cfccb0b6bf14a8123baf81e4fbd7c41bb306c9e71bf50a623eedb69d973aa1c0243e9007cf6407c07d43fdf6afcd21f4f25ab6b36147f0f7730ab6cfb9b24c8d548260a12ac9ff7dee87969d881f036ad90380476bcd9c7828f88d16564696fc0e53d0049982c171b1c5c75eb64f9c3fe5e05f6f68de093f89a157ed90e2a6f17e24409c04c57b78010e7df49fcf9a352b56b0a84ea06058aece33a258c4fbf623ac80be3cc79323a51f5be4e9f25f90ae22e24252f1e6a1114e6d0cf92ca3939e1e008cb235d759106b03d73f4e365cb8265315e3cbf2a1f958e396c25856ba0cae80d28a3a07e5241d97fceed26aa4c266951de5e5a3dfb31d9a2bfdd2cd6fb96cf0ed360a9fa8841af2e37e19fc8107f7b9559be6edea50d9847ba09b1fc595d5808910fcb5509590d9964cc8e781c9a017d1c3ebae104f68a1e24ee90e61cf7960d06f2390123cba4bb08fa13c205b21e60eefeb41280b9aa4b9f4859def5fca60f8616809f1ea5db5b352e21cd4ef7efc3af8fb916235ce4c3420cb09a2ed9c00e762eaf5a9ee7100d9b08ce64fa7461e90151c97f72d485e296a12d9e1514560945eefa35c8fc8ec6ff8e7adf00de2a64151c39fe77b7c60a2f8445211bb6930ac4efe4bbffeeee0b9441aec11e57bcce2637292052c9d54c648261504fe24b0eee4442093462c58985fda77db061024e8798be144371d396b7e62b6ca631000f7833b0202c9ac64043a28aabbbcacd952a81ebf0bf3e2b837175069f4c419e0b55b680f4d0be995fbfc7f4e7da850799f1420218d6b2ed41462fac01df5c06032f0bb1cf5c7f0549584c276dba17f0e0ac31e0c0ab5c4a67fb71fa5c558fba0156faa6ab3fbe712583c9def6bffef2556ec3c78f170398e8699bd5e5a16f8d002f517e1c6ae8ed5c320893b42fef47f8dc701b5eb35fa2eb431a626624127f0e08e0a267bb09d24f8a57a55a3b2e6a8cd0fe52bbd44a9ddb203cefcd39aa6a07e2dd7e3d0873e37111c08d6ff8fa0206681c7bc5ce277471748a6ec818e98c0e65dd873536a4e614f0932f09958a46b9aa9bb29b34bce7745779b05be8ee310e63b881f8c920731a62295664581258f5d9e8ebd02b8574be874717a4298b4000382d7a66e9f897a3982d86808991b0e865d18c5669e37a4d1f3585edba7451021488bac2c13e2feb3f592670ab682e0a29d3d62e707aa9f3b01af7c9aa90f70e7c7c98a02cac691c9ff1fe3de1610cb7169d4fd46be4b5f7e46e54647d8f85061a8445496d41d1b5a9ccb315e9d726e1e9be245fdca086193cd1dd791a966406a2746bc895f7a5dfd1bb31204b8a10dac1d8b8932b133afb673d75b355a8e8088b676c96c0109bd064d2f2d7619922458550e68e7c43bd599d41a1d988c2d20e23e04906480303e339ad1f029971c5393ef96cea38f8c5de7074be646dc82603eb6d77348c0acf7a1480c66c2e7707b33f58bdffecc047a1b3043b4d9fdc7c08afc62d98d0b756401c8d4a2ddfe58c71eddaec1a01bb723db115e0eaa33e19072769c7c82d53fbd364c743d0326160eab5e41029f71c1954fafe980bfd8a6b09a99f884439e98aad3fcc32a452853dc03ba5143d73ffc267762b911d61590602f97340558a723940c7e400087374691a83cf9945eb5487468a2d9f604411ee034b7a650dcf12f811474619d48ffd7d43c67ca99229412811d26063884801c20646a47b323f66f80403a0db25afa0645d4fac8f6684fd0e818d27d173f1595801534d290f16ce425857ba8d712d29f601716e51b89e3ed2592f621ab69164520aaeefbd8db6c59246ee803a1f18b1891402433f891f4aeaee87aa9b09c02c230b65df58abc050e531f464d0fd32b2d01c903a558e893f1bbf82bddaed3c532d04ea2991a0fde550c654110bc5d5b082a0be67ae6a6e23a2ad71b787673ebeee041865e98c45535f9b2ce4c073b5235469fa9340cf71d44a733c114d85c0972605b878a4861369edcc1b1fe17d8c121050ec1e289c6d4a68c55a5313e085d29a07f2ebff6bc0c2a28c3e2aa07acb2f263b304ca7c733d37cc14ee78c92cfb3870c8633f497538426d97defbe4a8084ed3e2ede68d8c9722dc1180b8d2f2df2b0019ebcdf64d79a10553d0eae53cd8c54f19173bacbc8e503a577a8458cf364c3081e65b188c033c7067a73ff3f513453722ebe5096ce63591e4ef9dd3e7523a804503de6054a343426eca415ad1296083cce307ae750ac50c8f34a910471b23105be4779a6d2f9a356d2433f22d63fc91e6fb2f7895db68fb789b998369d4309094445216cc3893998e71fe588312f82724673aaec7d98bc0f1e31dc6447c4690d9915e642cb89d8ab9d7e20b643962706429ace269d86b7cc7baa9b81bd898d09cc148fb124667f7e9f9cd5bba89ebd0badbe4b9d91116af6ac7646987ea33b09e8942318994642ee0405274ae2c9463ebd85add3c23ecaa67857634daa380c03a9f61b452a4f873b1d72523180233bfb05c4c4be96863bbcb019fd010c9db3049f97cd2d82658a116195b294a354334ed3612a4b33073bb4e6d3e39cdbf4df040b2496d9245afeb52f2bad00c07cf3f88b51353b469e9c34a6a460d4d9a0910f08bb8f0e1d2b1c4c070b3a0d94189d276f4b250a95fd011f5e39d989887e0b043b9345db9af7db5a162a869a854e6ca6da58c64de82927a7d84e8279ea10260dcbd7b627524c8fafd4bbbd979d1aabba2c959d263f27dd30c999495f23a4be0684306a654975dd2f9d9cb9bbe3ba8471f99634ac406bdcdd4aef15daae342a04f5f279d472257f89294a5531a05b56b8fed664d8b758d1961f2ecff328601701ea49d308f04e2d8c23dded298e2d7262ccfd4f239950620b245f269bf06e4902af930c71f7e2149d6ce7057770caa308e157421ccaeda5f02d84563c9f6c6a07c5119d0d9af837b2b782fabf03e2e2db61df46c4568193719186f8442004fe0991dd7b07457ff14d961b9b1a6eccbda81722b5db0ccc8412c45897f6b6fae805d3de540a5bc1a81d6951d018a408f945548084be1a7613fcc45169733e1aa6004e4922f2e6e1d1c7a5e06d29800c0537e100633538b0909842c94af87fe667072186bc72c219f0a3ef1c781ae1c180b64b53f88d66cd5b18ae667b3cce40d50d2b376ef6169e68f31606139a1bfc9601d3cc181a32638fb99dbe2d088d4f9a0907403c8ac7610934755e5f0057b51c4dcb1df899155b73a3a3736aec29ae3807699ec6b304b8b43b8b54b90fddac398cdd25f8d787e8fbefbc72e670e9d9ac05cc9375cb5ae3406588a35a97f26eff0431fc0770eb2522107f33c4f269b5e30c01fc30213617ebbaee50f7cd0feb75b6cd43ffaa00f69ec279fed17eee739d0c5f2fdb821729990aa3f61aaaf313310a06d3240631f298314200d792c757750d567ab70310e7d0ccf7d7b11d5cd0b209b1c39baecada67c617af429187cedd0e136cf08bd7a9d05366f1d1820fd83af0e1baa45c021887885d62f64a8301bf02b556088b3dc381979c8ffd313e6df33cacb2fdaaaddfb503a8ada9dc8ed8e30cbcabc56ad91930fe11605c7ce1d35da6c2d2f7eed79fe9fc59604cf1f0b6a80142f24611f0898ef62854fccad5d6b6f07aae8dbb6782ed571323281271b7a807e3e1a3155ac0d86f74a186933761a97971462a51962636638d3ddffd505a26059fe02bf228e71ccb323008fd72d84c9b9a5d4b94a7dfef8b2e525969efb8450e7ff6d61838c5f270cf699bcc8167763f572f86c6c03f22cb90b3adf7864d6700ca95c61e8cf4609899219d08607137e0d2c4f84655739c9b231a09b011c69b0e498936a5819d0bb6eaf0b453a3a6f214fbe6a256c96b7195ba9aad0279c14d034d1808c26841204444c3ad1a6dc5498f9ef8af0d609e3425a41af379977a530150fa8a589960f225321fe4f53c001146c64003e8bbe276cf013e4a5478fbe90ae3070d9ea7ee52b9ccad5b06766fbcc4a57cb9c0491a65c15935be365c9c5e09d560713ddd1e7a52b0ed55fa723b3319b7c8a2e18e8386aecbfc0352c203380201c957c4046112f5e1243fd7b0087a1a3cb981c6249785eca615e5a92c5b440cdd3707da179ef2cc09a0bd0f3d667827966eb1707ea4022576121a99d339700879cb516e2003d76e6f0eec3508f613f8db8bc50bad27483c9d01b7663806d5080ec0a18b4ad6c1ea46115e9b973436e30938b8e7c6097641433d2054ca78960825869a9a43d818efd0d91e31b4c71ac6f2efa07cadafa62878dfb75ebbc8e40be0216a6d79c8a6f7c5418c0434f5d0ee3218391ed38a784700b815f19ae783016d387db101729bc048d2e1c78237531ae0da68f7fb5b01a3362d58fae541290b7cdc76a44f40dae1a29e04a288f386476bdc7c0c57efee7a0751797c221c8108cbd5cbac4eb447f28ee351116047d162da4673ca2fe35bb7d62adc08352dd002daa54a9e744341c463f78757b31e0cb9559de3f557ce903b7287b0f2c0edbd0f6547513b03fe7d2821a534ba541ab9df257e8e2ad8b3494a203d62bda22d510b6e075f7455eac9618516f569bd95058f7aa8b37c1c2b5b53c6e699a73683be00d8b51af1ececa71805d7994508b7195c70cfe81988aa123126171636578d72075c80e23853695990ca4d9530bbf6929b7ac56bfa5a0eb306071470b8ac4b650a2fb9b2f322c1c302aa9fa538468a590c98424a7962cad03cb554daf6dd4cc50390752fe896a3cf4d878128fe86a961593d8a81c60a189951932b21df32be21034c17987b6e26b5c010286a577162e66351e1d617a7ed73bab277a6e89acaf30e5930fb1d12ca00500108297a19471e523f5ab54421a80769e660704986cad601d77179e8226a64d458f0091ae305c9a6d0512914735843406447d199a6cc9106ee60c701ea4a7013104f9a16252960f8fe846768b8dd8c480d07651d8843e005b5cf89eb2f493007bfd400b6e3dc0f6a931956be013fc3f7fa0ddaa6f911cb048369851915cdec12faa6efa59c28abf04308b617bd1be57720df4821ec6e030e7f1a8e64c15501980dfb156950706d61d42eed629ffc460a4a9df9699f4ba4031e4f8fb65f1901979ff2bf22a27f472e12a22f16cbe953188c6275a50f088b071f4d0cfd462ca83304fb56d671107d93f127558f8ffa7cf5fd40f9724f09e80322d6351241f572b6cf12b722f5a7335129f0dc81aa0615ccb746853517824a0f66427528c0b6b5e8fd8faea54b6dd9774e8b76ba3a9fb97cae8850c5052c1002cc44244d0e779194ceac47b6c52e7461c7af1481efffbf758fd23f02b5820f01baa635209e4657d46bdd32faa1c4e801829660b2657ac5cb2114ef6c827d4c084f406f4c28b4bfc27d2f77141579f506c1814fc6762ac910e508e0abe9c0ff0cff48188bbd08a854cf0201ba2de11daff95591ba451d240e9623993b7e1db005c22fd6142c5d116896e2824ac3c39a084bd5fa22ffe8a84a0ee1e07bb14157038a6a4f0183cf7c160e4a124128b21eea0fa707c19ba2b8b8bc9de07c6fb72a0d07d5ae5aca2d563f2fe5cfdec29b38992b1104e6f2be9f0746c5156f4e7d7f0e73dc7d8c9edc50ee17c5796c1b3219d701d0ad4bd119d0d5c7669cff53d594011181b8be250edeec9e49bf9076dd5f1a1ea7460bcf145e8d24dd3841c369ad038d61f0387ad726996a66759bb88ab611aaf5e47cd57c52314170f833e74706027fb07370d4b90bbae30267ba691add31bfb229ae71d526b00358b1644144080650660a0f2e4fb5c179c5f2d374a731afbc137c497a23e13efd6842d5b80ea9064a0ac638453ae29ee8c943b85d9eb2a8a5c787694722ba2f1f1ba32100bfd20f714014076c3c6d278502779e270eb5613a1b0c49341db10b195da6943e2ae30538c4e2b5aff66ed96bd4b614940bb4c26e5c892abc3102fbf40ecec2c7567105772606a8a0562d7fce776430b6c9835d651f5e09a42fedd63830ae0ffc6b8102500518afff60e18696f5d5eceb6b5fd363e523649e1e23ddfb1ab5715258850acb072f20ac03b7f4db86e2a96cf2c31d6ff82f02ca44810dbcf09c6a7f9762023534ae609d3e0ee7c657545c08e67c0243a97ed3ca3a5e067553da723a7a130c029d49fe7cb12c46617f4f27ca4027b8eae6af91d458db88028ec9ea94c70702aa773285f363b4d157adeac7d6da3df69a1545ba653e4bcdfe710908a9f88601858fb6983d44288188d6a80cecc33b560a4e05a026ea907c3e439e252723fa03dad6c5541da65d808fd40dcd7e7d3a878cceb9ce7575e288a182b9dd41bda00e5c667721da5cedd5b5088a6860f2032106e0d7c453aeac73900417ed1572e30b54316b961372ab6d9cbe9dfac7fba8cd8a94493216d99a7c55c5ec2a45f71e0f34e308c61d3721777901aaf0be1b34ea9b1c018ad84f9d85ec252a017bbd760218ca36a883d5b3b5156f3be6197ec2a64e0d331b23c2d8db642c04bf3a1188028c8829bcd38e9399166c18d8347ec0ea12be9f0c88e77b10aa2317f6bc221e0bbda5b31be9dd8e17678dd0bbe341fc81afbcecc6dc9cf4930e4a481c2741a50d2a4723038ffe2c53bddd4e40ede8b3832163b76fb6c9c925c7a0591c019f7300d00d8945056d9abab1fe621935140dd65ad0e124e82544f5b6d70097cb3c630e2b884cd3de6d8e6d88c3abc9c7a45083b4b27c2cccb34af81134d903dbf10b0a6d5c15a926a2183a932629dc4f4dd81cc32db82abed07088c0d51198ba79b209d2db9fa2fc2bc271ef89b43c42f56a2c36451051c9163c44058cd7c64b94e40a6f6dc31d1807a21cfd50233ab93a2306b721c823ab86445baf3388955447270bc251e294debd522dbc0fb0c35412732a6b7490d2089d2fbca50ee105b8bae707b64d4dc28709a34921781e8d1cc9f7493d5d242f0f37f9c261ad9da19b8eaa0f51d51ba67d3d261896f25c9b12e3efb5127c8748a822010e5384b8f395f279053877d4f4e12df2c2a11281d560b46edd0c7b759b625bbdac63bc26fd442e75049ba0a6db811097f7d40c38c1796c24562edcc748b734103378a773002ecbcd0bc70eae7c025ca4ff4d591394f2e4c5f8c091b114e1e781a831fa7380ef481b075405ee498d4205136e9ce564b4f0f13e5d7f0e74efb5eaab181214c9039f90077f03a12c879db5babbabeea1e6fca3a8eafde0b42e0272f8bd68a5999dd3c10d4543b5753533bb36a70ab17d81611b9a3ffe604f611671d41839d4111a50a506669b3ecb9455b675d7418565fa39ef8923656ee557e26359de5e6f99b5180e02ced543fc64d9e94b83a0c51da4e097d8b2ce76c223f07d82e079bada7b87320435bdc59f262863f6d98d813fd9dacec302bc957c4c4ae372723012dcbb02e208cefa29b09dd15fca28cffb1eaccdbcc563ecd7189f410e1353cefe47268f5609d3262f72f5c5f9a1650dc880387aac7a66f8240f1c6d4ba30c475f41f3c8ab0ddabb4ff5d75a0b41a1ed8cce9c0a4c2e72672b559cc8793b5885c0de24e5d606c57063bf1d677b12daa576bd245ab13deca63282651edbbc3579358cdebdd508e4f1b7786004ac051f9ff07f3c05918a6d84539b81d584d115f81888b5ae570c80599325f3c4d414861f50d14ca33b9f836122723c0b7eb0798c7fedb16b680f64c3f0bf1413cdf9b2b5347fb63129c4f59adaa692707c8d208bd796897b4d09109a7b070be6cc16fd0dddfbc077210c6390c1010aaf77830cd7f05b979d5801cc1f7fc0edcb73c72741cb2285fb4354e59e35d1cf559c40d303247885d0b10ab32a43c0604fed60d3ce11b6d1c8ff9c73ef5f9e9e3ceadb5b34962008761201ff142f6cbe3812dd3ab9a42f5865d25960aa18e2e485478fbe436c54aab46b060a833b0cb0094b2202552b78d38423c6e527807f5a7a550dac85155aa2790301f2bd34bb5caa5dfd329d4d04d86320b8bd601c54bb7410bbfaa04bc5289610016df759c537dce234f6787c1ff34c4182fa6a66192bcac5903c640ee50b45580fa192deeaaf36303078948a17ca68a9ddb137d8de4b0510ab10ed2e298c6f0d08f9436efed38bd734e8304e8e84609f3bdd656c781b790a0bd6585eb1b537a90ab935232a1c0534ffbc90175f669b74404d4e444fb394f53d60b7058e471c610b1345ae9371a369e6bdf9d2b279815463d6ee8937e14e06761544b0c58118c50275b16fde048dfd878061e378467d29bf8a55892b7dbbaa1964c69ecb28ebd80a0e9648881d2bf20845415013077c5711fec8d0d1d082c686a5bf05a093d9db0c5c0f8651d15acbdad2a9196d43185a8a899761f11998897312c4f8a0ba0fda0e02469c06dea893f1ba7db3cc204cc53ceb2dd1764e0ecbcfbde71d4723ccc205a6269e41679322a1e61347a02287c501e4326c995c5ee3bc0572ea18bba6b402dc4680e55c729bc145f0ad127f68af4a332e443e526a5c873930784c8045bc0b602c3b6b6e98a178582fd85854b14b88e6e67c7fdedce3683ee028111f11ba0c9411bf2dd475a7a51a03c2b0ae23c945aafde3d5e57b17a52aae310a9e6a6102a2b6222fb90543d6ef76792696d4b9026e84e55834e5928586b2eba290eaad0d85a7b74792c66727d2224e6518d8e415315cd55cb8c427b7773e2178d34d6701ebdeef88984204c975db6d1596a6960bc6c84af626b0b8c35d7f62eca9bbcf0e6737038dbba56db0ed8e79492040439fd8baacbc4d4cd7569bc588f69537f700265997fdc84aafaf565821ebb7de74105fcbb64473488442082d581f96a35f0c1f0140a51936ed0806da0bc7a23038911819b34742a33e4d8c1c7c7f9becac042ab185b731494b35c3ffa9064e23abecd1d593bf879044ec70d21ae67b0bc5055d0f4502462565a654f8b1a3c547bf1db3501c9a27f2432b002ceeb6827ba505fbb0ce705e191d3a37ae98a11a99e1a578251e6f10c8b8a830da3b8285ee6003d7f6d02beb1762614edfd5e3df7e4c60bead144e4bc9470cca72430956e4d0076e9fd7cdc1baa9d2715e971c1a7e71ccbab50a668e4055a5552587bd6e56070c025ae6fb4000ae12c1e63c17021084c8d9146f69040a6aaea41b2b7d577c36089785a33cc6bacbf1d4844551bdf0fb50bc5c445d78d8a5c69364bc65d543880c3b3ece43b43628e620e45b86b22a5df63bb2c8daa74d7fa99450063175ea8f00babf73a75462530bd3775473992e2759951d1a80a5bba1de7ede5292fe642004f4d1c347bc5b6e669882d7ebc00ca444619148aa740fbd6f8020d0ab01d6c70a0cc9330683b120779aef8a8671bf537e488d6e07f0ce6fcac7c7acc3124a230cbbd5e7e97a3296ec5eeab861dbbbefc022afcf72ca0a42ce153361a65f025102e1fe9c557ce83f76f30ba2d54e535b3dc1951a0447dc47f1ce1933074771460dbe084120ed4a30b7c8a852d7bdb4fa79aa514c8670aac0156035bbe2eef50b08ad226e58f8b880c9c67eb787f731223915b2647fa342571352f5c8a56bd83202bee62c5c09f4707120bed37bafe085701d24d96b60992b8c398c4a5525279a05a61ee8321f5dba2edf3d2828631358f3f3917d4880f67747f24d9220fc1b7d006b8dfa12beaaa6b5f9ca69814e7b3258946846ea5ced2c797406e4af8402bc07433bc76fe43104be8c4d0704913fcae9a0b440fd92fb55edc416219882e2120102779cd8b87dc6ca56342d2bf8e0cf534237386fa60709426ae5f4158085e4073070188ece8109d7e664033aad1942bb83925bb36c509f6b34c69037bccf26064204ce196f8f2411b8dbfadcaba9773fe5f712a5868453978bdc3ebf3e1ddc005970e0ee153909ea2ab587bb72ffc465cbd30d0d7c6075d4b00001d111e2a30e8d4057ed17a3c6e2a0478958834649d070000b953750f24c5f9a5c906c1d7508e64a85730504e3ed07808480cf51fd11bc9091303a10f5e1716a38a296240b070e80f11e6f2e4feadc460ce51bbb2c6bd8a48b4cd11f44f97cf81566d579a700e0f0fe051590b2eca0d18eb6f01ee120a11666bdfb585280eb2fd642039eb30f74b1b0163fe658aa640247570cdcb5508bb6d2f04259348e6685c2070bf1180ff11255b0dfc086dd89de99e3c6d3585547053eda6999f7e9088d4360747cfd0ccfd05ac5e567a3ac37fe4c67d24b62f62a5efa391a0f4c2489dde029353b020ec2cdf6d70b6bceb1e0a7ba31a2c0af778a040255874eecb4aa67e6f87074c20b441645d254bf71cf8198038420edadebe06c2194cb458aafb29a7902a9373600b4eb322b69c094f686c966408788ebe4fccf4ccbab0f62eb9a7e6c693a9a240494796a0bb034cbc17eb8a8f2f73a2aba08c20c63904ae5a8f3281004c53a4a05ccb652f21798528f33008ad704f60b4c10600eafa99801ca7667b4b299b01b0cabdfb0f4bb9aab777a6d9092d4fe343346ccc35d7f9ddaf1b86b5855f67f760f731ff62ca4eaecb60bc89872b5f4f6077356ce0a2b8cc2075c3a856b8103eb075929415f44c20dbd4a70d3d75e12cd95a1da8c029ed8dc6aa40912718635480526a5369655023bd37680a11b01c2ae9818c3de176b06c2f28f56bfb5980b58080601ab83d3c82ebc87682c11965b3b534046dde81ae083744d7833510548840c9205306b36b97860a78c9b2ad1cd929e4c90c227c37783fcd08b1454d97e450887e1d9abae69bbe9834b53024e6a5e989ee75614e2632b036474fcc28448090569f2a339d74abbc6b2fe4dd775ee976855acd7314168789f3756b74475879b0c26dc93d2f93b45ad1fa0798206c98d670d676bfc04aa8346d0a14a8a40d83e001a747a12d30f777200e85d1c3380b738d60cb9dc50a3bf296690ad9046ded900e26f0302e62b0edba9c6fe1214d3cf7ccb779b779a5ef4533005f582acf22703be8134bb3e42424dc0b5088c0cc143cff4582da6d834c062cdf72b71d22a2b0bee7fc4bfb4d20f37b7c53cde19115b96bf9e0f44954f0901806896e9b8d51204faec08eefbbf016d6992840db92b4a27b9f30b4e050f48d3dacef63ca3a816034c1246cfd977d181c19329cf45c10b0720a8c7c00e716c155b6031286bbd0803c0d5acc64a62d1294b5be343b08a97e08d2d918e3b7649b57c747d6b6b0da30085555bb6ff770d25f6a2b7aaf84f72153281ca7bb64ffecf68f73138201b3b08cad7191a786543c5d68d6816d3b52df35cfdfd0edde8db56664a2e4dd2495402df5a0a36286fb9de6d5bb92ce8e5c7ff5c0abca7b90be01a6c131d5d1e2ab706ee831c726f581eda90521d3afc4daa42f9c66b8366bd0300d47692e66300c4095a3c21012948e6b41afbf30778e66400529a558c46867c18130dbb8c6dacda0141071ee89e337cb23079ae2fd19c5f1ac15d3d5e80b9bc8a3662275db9ed5800451c7004b3927239d202e66a160adf0bf7bc7d0a2c5fe9821c966a159f85920a94cf44fd8a943f02c8c9fff8656f962aef2a9057808b99c6834bccf9029b160ac1466ff15143c7c4f4e5c074a9171eda5f8d3b61b2a8c70f8e63372b181bcc098d41e65ffbef967e91be9ac49a44f73e0b35912e6ca552b290f4671cd3a6e90ed52f2126436328f9b431a1c028629e8b206b405913fa936b93bd8501114de5029e83e86ed8526caeabf27f9cdacf207abc9308c3e206e735468949b0ed2570052882513a6f09f4ae45852e51c8ff99e9b7e2fe632e33d52ae1c710bf9382850949a943bf487546caf93cea0c0234f50c8f55da70dceeaa84e83d797ad8bf8704689e66406278122743b9f5c70a97f3d7a59ea9d8f9e00510d5508044e5d0e80b5bfab7da021515763b495f81ac71e54d483d749c9eaecf2b0c849928223265051bcd23f4f8852182319e163d336e9ac0494a0251fe78ab4a5b6cb660c177a900a3b8965f5569e1456d3b81df25ead247ad3789af1fe15f775a358c78a6a0c30c2405b25f59a0b74a309db3c497ffb8f82b801e7cf77afc7a0457b089ca27a508e1176414805bff4601f47de1e4d8aa26dd50e38d33e93767ae676d31d3bd3d0585c6fd8a8ac17e8ee0821bc09330c4b828d7000f30375a68dd641183175f450ca3063e786ee833710135921265f821baaccdc0c2edab211fd25e66bf4e3aa1044cc5fc7d84db49fc3371b03a96318910f8fb87791f6c5565e8da692f8991fd09bae806ae62d19a3cf65a650a9fe5614a4305e9540be21db45bac49a280ec9806cdac23c170a530c65c9c097263575a89f4413b1734954b68112a3370e4046507c55930826985d476b053069330b3725e7f263f1b3eb96862caf84670552388014e42be894511ca10eb139d637daa974e215d63e93dfd851487a8cca3a7948b04fe82171acf67f2bd64cf847ae3073708bf278fdf5c23468ecb2d6b4fe6c11c0fa093b2f9f929dc2ed956db3409ec07d6f052f91a6388ffe766176a7451b87a083b113c65745d49970f5e4de293df312c1981f8c7c3d72c53737c92a1502c710df922c32074b8e6205e4e1b071f6300efec4d91d1ab5e4ae432e1cee3e39a68080e84bfedba4e08e81dd2b788cae1b3c52ed183275cfe5e92635c46352605770672bb13c8e48ad98b40132e496d16498a61a86693007df4662d7395318279ba09f8cea591be9b63d2597fd0f4eff2e6191b3af45a88644cbfdb42c29f8e33170b8ee0188e206fda6a1a0e7edbf20c4fa68ac41a27979e052805d1166a07f6300d349f8c8212a439c71230f34823974d887cf8aaf16f1a4833096103a0da513c084ea540a6261add3301703fd1d5b1b62ed6427f2c6bde65457c7fae01b744530e92da69761a76924e48840cdae9d6e7b0a9ac97907479f22f8b2dfea0eb35c50f4736de0aae0afe4aae557f7f0263757012a35bbd6cfb521be315d19422c09909d72ef7d41c263bee124cb65075829b4ecbdc5b78c3d90dade547d24d9e9e150ef8e0aaca7e7a1bab469d1181fcb700b330117d1f9b0de21e3b5e67ea40ac560d79eadb25d33a9fcae1adb9a0cc0111b4151869241752e7ccbe091467f4f6c8075902ec272e73c41ed440abeaa0be2da7bd53e0fe96435caeecdae121a167b20fb0865794fd374ce6e008ba91c1f02e1cc0412c74103a1c39ec789396e957430f775326c1a22bddbc8f3a03db2bddf39d18bfe42e756e946fcc5886d737a3350fa536e6d5c3a862012959abd9b6c5414942f9a511bb23bfc965c9e4e168c9160d false +check_ring_signature 5fe8baaae38c121f84fa03e53b98e898fa2030f6413063431379a3fe0dccb7c3 2adeda2caf137a49eeb745a2d224bfe8de9368fec0f24a7e4d68c3d453e46f3b 13 9210903763c2d26d58edd0b87615362f47559e458c7bd27d4da5bfe819a5ff07 82791fb5719331a31a8bc8c99e24131ddf9ea7c2d99968755fa176899a88a5d2 b401fd6373fc638ef44102388fa1e6aff3bbdec4249cde66de4ae8236cf5c73e f97c6ee71169729ecc11d06be4086fed11a37a27ac4908d073aa18dbba2fc701 17df7ab79b90e50a0480022030b007a5bcad99c188a34db4518cb1462da82ac6 5ec22840f405c6aad13eb96a13a2b9bb4c4e401b89f16871f2785c40621df83c b7647c6f74ce732759022b44ecf3f5fb37482bffd63e7016a1b461032414e38d 10f609eb0e0453152ba55ed58ef96f93e1c6570d93850c83092e5971bff94c14 94a34300e24822508366d448c1757e1feeac3f8728811c3993446cf62f63b6f9 da9f2c8bedfc9dda211396c36b3f7fc89f395a161aeb3300c882da3b50894038 157f7bf559fa01967177637f2b3dad27d5ba45a3c9ae87361df5b99d54e5c776 11baabeb3e99e724d7cd3751be4ceee6d9758a432ace37e0bc5b12a98b2f3257 e84de9d3b1d48a53d76ad3eddff17c590dfc74bf61ec5084a4000115a55b0d25 8a2983dff66dcffc153cf14dcc0a465389bd5a624e1ab2748bf33b93fd0190006f3ca96f7752e237279c45d01ab45b9831d4d9c7f628a4fb94a3ed5791160c024649505243ac23b5b889623318fd63c74ccbb50787085fd7676f309bcba70e0c8a1aace4d2d21a74308c4d56b349913fb934ce05a7752506d155b3a9c5f38409be1a5a8d4da09bc21cf725b7df82b88b0c9c440734890b5a7e81634d24cda00296071d0e43eec68143b4c25b982ad85399c25abccb0fb667e5add6542262a60b57b4fe9b9852f1d0d1e285b03aec6755e1936e0aedb29555181c7c5fb6b6cc014164af17e9f03b9a61a9a5fb7147a1691e9e24d5220a2d02ef940a635487c70b586831db66e07516fc1c6ca1a387d18edc39a4a61c3886fcb0d5144235cdf40d9c4c280dda4d5bce34b94085b2088e358fc539c9fc63e86a7c7eed0122677708c8f08535b6a4f3956bee450db21f45487c870c07ee1a61a905cd63cc417f4404a684bf12cc518b61f7c70559d16dc2f2109737ff077e3b5e8d67cce0caf41707a3c2e29981f9ef6bdafa6bfbeb2b8bb5b0bb9865316a3b083167813aa4f27e0b063ee79372e1ed9bb000eac460090b7e185ef1eb66968cd7eaf17ba0453b2f7fde98696fd29c0760617d3f4473afcc00bb9c87c9136810763bb54767bc4d5205e8e9ae28e7bd8ac7c46d4b0c57b75b0890313d4ce866434f342243b468ac9708c6d3e6d8d045dc61780eea1556406810d0bf075b5145b5d470a8630698c5cf083eb889ebfab7199bbc1e7c905e7169b61df8be9714e8c9e94fa8f5c2b78ad3011ef9f259b8458431e564db671624eead9d6d1bb7e37245fecd04a0708ded1203ef3aedfb0fd715b0153470a7024e90e85fef80d157432a6afcb4a918bd0e120f5cbdacd00a536d3326f193f28c7c7cae6c381653f865b91901beea5cc07bbd04d2c044c940c57b5e378e32eac77d3b4e88a140db87480ab0798cd35dd51540008fa7288395b816e29c1276eccddaae81560d8f7d038f9a234aa1917539146300fff0357b2f797c1862d5ddb5804107133fc2c098083add3d43ed64fb6ff6320d3e8a6a7b0f4f164636a49acea04e167ee0076a3977118d1a27d4e7024d2396013f6bb81e0c2d7fa42c8ed4defc0798b4e5ef78f636d6781b7f691db7fca7540e false +check_ring_signature f7406431b5e5049c945774348c9d3b64693aa46572a32b1389bfcc904bbddec3 188e1cda3236701088b70dc6f6e2c5f559878757011844638b7d54d5484ae748 146 6fa727c5a54a64bd39381f295e56d3b61fa8e6050ed00df0e7acba3775964327 8bd44f4f3205b182d5748b309c9c81a2a67852ebec4d8789e9bf6aa903a5baf7 8e3be6bb7a6ff11d79e9dde776486793700ef9e3998cb313e4e42b8c060279a7 0a6bacbd01f6b5487753205a61b129305bc13122a2f4c41795ef5290d0fe0b81 3149f17c89878220be206644bdabd275bf106d0ed37e6489a41b2697f7f5fd22 81cd5068c05b7b6b35dc2e3eed74129c0e09d5e3f85749eedf1f94d72715f5dc 1df0897b26e5ce89ff945b2ca6ca35015e02c50f8789abb2938e3db80c41fc58 dd6ddf9109bb8561628ea9ba14dbabdad22d185c054253025670118226f6fdc8 7cb341ae6927087492d4c4ed50b2f25289b494e58746ba29cdca766b29f4c23b b9e88fd9785d5a351b6979e7863722e6a1fde4f084d0c9b9b48e14b0c8709663 bb648d10af2c3ac122f4ee7d5dec4cd9de48b4e91bc7e4b6c6691a445df95031 dffff3d4165ff91ab419d48661573ddf50279326cb2130bddc6a4bd5484128ba b2bb95de97fa0c5a2b1ded6a487ed91516b79ef4f355c39ef40a7f2b1e58a76b 925347d66d41a020a4b207c2b172c836af8511aafb944b02566a3b9c79f7f90a 48e742013cf515521d7d355f04123bf1c59f18da6c6e5ee4a448a887f608b92e c553ad071a4c9c47a5e1faf236c9ea7755f1d3db0033243f8d7933bbfcdab580 560d5b72369c63b11554e8ccc90873b22843d429202e2986f8d3db9d978a6bef e9e7d112a5e182679c0061cc1b0c38d1f316d20989311c34d2bea9763fdadb59 7cb3ee6dd2de3bc9fb9d60fea1e8546fba935942987fc232fd9b4310c5971f1a df487ef85ae6045aa5a84095155d3053226296462a1cf520ece8ec60d6dd230d 11f34203393560068ab25b6fe18b92c3c15d23868d4ad7d8657b7fa412474625 028975317b964a8cead2b41fe0905d5476a11caee0e112a333557f52ca0eb3f7 1211fd735233c766a6a31f0808ff2968b6d4b12755f4df4905cc25998ff4d691 d40555cad67dfca394ad7b7d188b00e0598a39ebadd5de6b386226e58fed9ce5 d5316809df1e4cc627e114516634cfbfbef9d545462de6f0ae7c712b00bd8a16 1111b92de900241e9cb93f1c5810c00a997639e704251ff78c4ac603d2cf3eea 2c21ddaa9f20ef5cac1412c98f90996ec9546f51d8b61f7134beab4684f78e2c 1abf8593810313264f004b0b73228660cc4344be18287835d81be6d8429045cc 27a13600b0f57f6128d2a8c15f650c149ee43fdabe16d46143ee744f092c7101 df35026911f886e1d1a3dc380fc3f8049cff7c51a4db077f0a6288211598af68 d362dfb2811dded0fef75ec0c56f2aea37c7d2d99d7fa09fc91bb15b6f4645da 7fb13310c756db7702a7b466078579ce548b812adb69b8fdaf0da140de2fc500 18bb96e544f2f8dbad8bf14e83a93a968a5c3f87ff3b4e882ddc9ce4e79be427 8b8000ff4f9920a3dc128846e2d0ca05899f6e0f97face1779e4f7752911058a 178887a43de1dd0828e10f9d612c4a4b7d4201243b6e48e32327f6cff456f9af 1d2b4672a129f7e19f18347488d10b93b70a1ea294fe955b27746ae52bfdef12 060bbd8c22431f0f49d8d5d573c1a7f5c85c972397a2885b56caaabebabc551e a50aa332d6b8b501370b9b17189c93aa07f0040fcbb26c6fefbc57ff09e3ab46 761e277db05305591c67fa17f23fbf63ac5ae0f53423d242dfc5be7c3a9929fb 67b61846af5465c24d40396a7c9ac0be11647129684f168533d2621ec63c391c d3b2410a34cf613bb4806187840ca66a62fe5c18c0b5f87470ab268076c7bf02 d8781accd34ba1d2984831e58c630b38e86f703c2d77ad6a5684f01107ac4b6b ac85dfc50ad01484ed3b62dcb29c6ffe8ddced5119f367620273dadd836286a6 8dc558e28d922b26572a185751c52be241b812b1989b615dbf1eb7369fcf6101 750d495923ac9fc69c4a459e6662d7c4186fab5875b7134fb0cf181834e9a41f 8ff0436bb48ad0cce45bcf0127c461d6213e4255e8042e87a243d228e58c1bbe b15c0b59fb5320fb60d2d684727d9a8a4cd14c94e13b7a0c68fd99b9928b1433 7efe86d12bc098667fccc7255579fee040ea4265436677f8a5671a431a372394 0aa8d1b9bac2fa18bf84b83befc6a04f5001469cf648d2109fe5d1539108917f 135db16a10a9a367545edd11a029821c32595a82851484bd4a1fde650573ed47 636bcd423fbc203606ef3f8dfd76955229e6e42343087f3ef7e19be741f19219 af214280a017178fb663ec30cb89fa15341c5a281748d1d8319c0417920b96bd 766cf2f97c41502a96dcf54b11d758a235dac60517ac41c7ca617d8f3cd1f7a9 f69ef1519f4a4a1efd9f3d84a208f55082c7cbb3d1769d2ae48a6beb446e2da9 c5773e3098a95574cb938d53c074e852f2a4e1d71f7ec35078d9dbe5903dc23f 30f890525325a31d52c1d062e74026d3c386785bddd8623a29e9e90fdced744f 00fe526315ae854896b562206797421fba7a501b24d08cf5a9a464e44b6c8ad9 71a0f9b2019953a483054b1c78723c80a39edd6b1b675489af941ab89a591bf8 c18219cbfac116941e2c07d7c1575c68ed0ec497d0217c0d2fd8a24484c46e9a 7847003df830ce984d26329e835d9231878b3f32d0623b24f7ee7cfefafe6971 97f034d3376ad7260f5a673fa8ca0e84a66a9315b51dbf8d1a5c513e439389a8 80fa6a89b9725b950930090c101bd9768c558fd3418c8c24dc132b6839251dd1 ef657daccf81ff98bfe3e8b40cb187d741a25f5ae119d675b8bf83ea4ae5b3df 49e37bf1d96987e55f914d8a794f54374864539d6d035220683215541a2f601f bc9328dc55a20358d47537a162815bc2843f5e42fe4f16f81f0e6ebf984cf739 640fef85e3529f236d3e7a0065b787570976aaae1ea82418d6893f48ace7d7e2 12c23e453bcd54885f38ac9403fad4fdb38fe355ae79035957b8308ac3bc6438 d73957a63a96d6c37d4f58f1e947035e6deb4328ac28f94b4d7b046e6fc327eb bb1c6641cced1e0c2bb2cb255b096555e891160fa1950fa4e69bce3342241ca3 9b64d2fb316ebf3f6aec38240fc83f49400f4be096a86ee586fc59d6c872f3e9 fb061e1b9157ad56643520e8dbb376e003be0a40f69c74f4294178bd71b8ec3f 0b2bfe090cf4a859743c9f92e94aec2af1c319a6c8ccb7bca261d0d436be5fa9 6df7c7c4977d0c9f7f977a52639f3c3e498ad780f0cd9b98880b46b29911aa01 34c0f28493f35ffe144140c3eea8b1e47edc7ad0965bf78036c4c042d2199299 490292fcef9f542a3ec502aa3ebdba3792c97a151dbc01dfc21c3cb22ac12606 f5d9c392ff6379b5c84207327303d1fe3989089a3aecce59b39a763de6ba1d52 579d60948815379c14450c5b6d8162ec06ebb8db1dca623ba7e33c5ffef9adbd 861948f00fb30d2c00061b0e82cb25f57bfcd036265cfbd89fc9de28456d52e0 497d6fe48e8392ce375d4990f236d4c0e061ea97c3fa3a791e29bca580115032 4cefb8ac5e18ff8041ae11ed7105d56850cc142b1ccb5fd4389f3e68797a88d3 66f4a85dcba70d58cd68dd5827d71e48b5fb3506cc72d0dee4ea3d71f1af51c4 b19ae851b1b7e10a246a52ecb03d7d42b97b4bd66f183633dbc642b1ff424fe5 eca8705f8e5e3a2e3813279821433adff768967366837b9bc58a55c2342da353 fcaf75bb3745624c86f669c47474e55e598567a1b7fcb1701a0f1072b0a45798 e3b24be9f28d9af0d12f6683c80c89d68f925ac7507bd9b9c1ad77f87f427b8b 35d1e4226de899eb39beaba955894ab580ae8c631ddd680e0e761fff9ff105af 5fdeda00cd500b1a37b1e3b7809293c6e70ec831e1e67055bfa55659dfa8a58b 122a286c4c5493d466126954def1054e4c2c63b2237ddec9b5caaf33f5ec07c9 e4967bb85ff84b755bf9d356ec335fdd0456115652c7fbdf875d915fb3a1ba21 a86bf52827c2e9b6604da044333e1be312d53d953dcc2ddec9f61f01a9e40db9 dc244d236c65a92b6180d6c04e7a4a40f62828b41438852de04b283a85a91ba4 5e014574b85ac02c9602e7f3d14dafd450d00d1e020e84827ea91bafb2d67803 e68943d3595c557faae8b4cd6f55ecfa38df8c06458b27b5d096ead146075e3e 4d1cd24a08e134c3d7796c722b0ddb03f561a6d9235d445767ba09298982be14 d93353c77cb33ea22cb474d7e0b365f29dae689a7d62617cc84fa8c170a82fe4 74630be5253ae5b80b6e9504e53f3c26cd968ffdecd5d704f6eae94ca14e9d32 30c23482b9150abd680cd31b3b8eced306448e4070e0cffca72e0f84d03cd047 7d63c049c5bbb2d965c6594b94def6f471f2fe928704b2ad9ec16273f7f7e7ff 5afa77d8491a66ffacddb82ab56cfe9e8aab693db4359b2c0eccab8b35eba389 9a340abbc14882a315ef6ffa683fb1ed4a803cef96019bf24c2781b9a91fef8b afa81ded913ab52627a345aef4838da7ef620b4d9a3fe791a4c165c224a2d55f a332c05282add6be3b5568077d574921ec20570b472e6c1926a9e5bc6091f132 8fe0f21d9021b1e950fea5ea9b9f318b2674726cc2e3a9983638f3ffdb2e1cdf ba732be2e18695e9eb7de7072d1e51c3c76e64314585eebf9d6dd852d3d4b585 acf382b2246182115b388eb8979279523f61dc5fa10ccf2ddea6ea170d14c4ca c3ca1b66c0541a8156781c787af8e0e57007c3b3bde9646401834a57a0b44db4 6364d58c4be7c03353248074c040a9b95210732e17bc80d72f84f96282bb49c5 bfb75e9c75db5ee2add898b5b3eacd2f783088d5c375eab192dcd56be4697b75 bc9783bad7640fc4e4f589dd5325580674123b1edadaca41bc7564acc011e160 1f6e87452ad9f107ce87d43b21ad019320e8fb3628447b8e46157032d44379be b7a35aca09397133b188c57cf7eb23526b7d38719dbc004acc2f3d16984896be 0d8d4bdeb3f77fb392f0de15f6e60acd94fa81423c03b12acc68cbdd645b25d8 abd238adac6b6e38de5402a93eca3c72a234254fe3463f7996852ae3bce0264a 41666fa77b79b0aa41e05e149fdc21b056e02fe75b933d6f845e25ba9bd3b5cd 83c492cb5070495c6328a602033c030c4218c2042652c37cc367cea72456e966 13c951d2e2ec630b62c12a633c50806d575e37b0f47eb96a3beb59e20a72910e 7f5b4162d77c404229b94e3cba18c0377ca7bd8141d6146c162c193a891b0786 f8294f2451c8e80106324cc4f220d9d5dac8dab9dab929a3d68c91fca8ad01d9 5e698e8c16665e3dec309819a910e4c93c7e7a64c669772a251ded928966f990 2750d7d08491dfaa08f7641ccf5e7a133eff8167af0b563d2120b8612aa8a992 2ad49b3e8a72d757ae9599d8ccc844d0cff39348f18803556941f70b535fac1e 288b113de40566b9ab9b734eb0dec3f5d99b60a88b78abcd600b481816cfae07 ce673072b4bfb2c0319e01aa6341cadbacaa86fb5f41b4e8bfec20fdd4b21be0 e27263e38ab7fcd5f4a67e35ece9230accde274d2a24b55f80510e1924e052b9 adbcc92306eeb1c50ffc5bde68c6760de3d95a8aac6d35596aa751a184dbc7d7 e912c9f2de983a060d6ed593e7e7e0f6b9fcc053a72d82b9569f29292797b4f3 62ebf390a833b1f3e78d98ca49e043b74b364e14f1905d6f7d4288118fb204d3 59c4fd0e2aac423423c652e8735765c06088501ec34106e85bfd1c48adc81479 3cc17dfecae98c75e74f0234d20f980ab9d88072d8e7d45fbd402ecda52ddcdf e3a46070ff913c9caca1344160a44e40e1583dd128fe8ad7fd54ddc640c5f97f 37acd580af63ccc9501b87175804a787e20f46df447d085b3f7c9a1cfd03ba86 d5696a001a5f2b25476cd9d3429f4dfb73601a67e7ebca8fa323f6867b8fed61 4bfe13d016db72c5e4abc82de073475c5e14521763082c3f07d421ea81d2ba5f 3cd82011ec243e93f7efaae9b8cac1dc17a3fbdd1d5fff7610ff4c7867041ad0 26335d61a62bce7e5b26a4ceb3f5b30b9a681291449845e9abeecfcb70cc5bb2 fce056c031ed52194d99baa39e57fef0d0c0bb72144e22338c4dcef4904be920 f78aed577bf64eecdfc45d8d4c6176f7a7dfce4982e1bc88c44427d22bb2f807 8a153f381e11dcce079412271983c43b5f027a15cd03c402f653c5b919772aaf ee34c2cb7884af9d68a31c9bdfbce12d83876a41727f8e0627ba5afff7d26d14 da07621991a8961e95b95b217a85b95adad9e9f2ffdcbc4c154a74345c5fc0ec 1f0b717043454ce8f8ba2b0d0680c9ae969755aa9e9f0bd368cd4a7fe91ae893 eb521aa574d745aba3c81402e9a3b2e51b3cbc847e4b46191d2291fadbdf97ca d41134fd9443ab455426d92808f838e8c2ccd80625076b82a9c796136b0faa3b aea1954217b836b11b495961202918125de8cc8cfd1c704a76a909d2d0d760de cab53c2902783b0302af084db1a87a3620fffc188324c0f18016108236e61d84 f0c5aef84487eea98b445b02f346310399cbb29e3baefcfa250f6720820f21bc 219c579f93b82772704e47ad23f389de7e563b2ae8112e51067769f0777da504c44917a9d87184c95f5b83721ba23f21cb12af3e562d9a36b059c8e10299ea0762f7a65eeff57c27ead4543b0462e32d6dd494e051ddbccd9e68eba0d2d1bd083e92b0c13b8d699b14f75b2a634c222e246b40889f1b6878ac065d4e40afaf0695b8217d1eedaae717cd1a5f82589c95d285200249578653482bd358fe587102827a0a0ffa732c821121b3f3b8ca6e6a0eb40c9f579582dc9c532eb0a93f1904ead37f52306a9482c648b7b9bd14bd58c790f54cb9b934b6125e508afb5cf3027c76ed4843af11584e1f919198106b50d35b9e37c3f370aca0bdb79bc9cda401395a8531a99123251eb5de2f6ac02b815bef89b5c9abec55ce77efc45e6f2d0ed39dcea8e9790a519b417f04f1250c84e097c738e9d0462a488bb4fb763a5b026ee0975317304139dedd92276d99cb8f24ecbd88dbdd73e0c17e206549c32903a0d5ebb8362d6264ae320bd915d57ed17aa8f9b6ebb48291b263cf2b76909c06231435a78d339c3ce0de025662a37173ccaf4a82b046839d742bc0b1ccd361033271b8a3040f7579eec2c5d76ca2755fc6935a74c01cd6a3f3b1fd746418ab056ecd436ffb08dbbd11624aeaa8a174154663e84d540715458d6c4fb9e0656204be8089214c100d264986fd3feba9e92d5310c1467aad71a6377cb64705a0350ddeabd40f39cc7f9922bbcd4b423bf97d7e2a804abd2a545cd2e057d47a35e10fc4df0fe23739b64d98b9bb8dfb7de71d5fc0effa61a5a2aeae76a788497d5005b39419de9a88866c8f730df62d41682bf603a441f18235b68cece3de17a1e20fd6bf2144bd84fcc51eda5ddbe42b1f79df101636a776282f0e9d8764d214d20d1cfa0274d538b934d2cc22054c4e4e5dda27ac47279f399db93ceb21ccde1a0838a6acfe8deb805e7b19b329f47ca4727b965190c86fa32601a0dcf361978105d2cfcd105a6963929face6e14c57f2e0e8579db93c4a0aae6084be3f8aa2a00518d8f00f0b7a55cf5bb7918d33a056f6c3c158e2395a36ffc1591edead111903744401839d631f6001fcd1b3bbbc9f4a60bca69f1b3a179aa31088b082ca580ccf4a7c2d636c0895e09fa5100d6c2a68f2dd663641b1a2f423c60c3387fb990e2e9e7df5a2e3397cdb452a7f2182be590a2aaaa7ba2d82037e96f84978799a0fd7ec619251a3a4bb05c1cce96f315ac59146bf32b682d1b59f48cdfa170bc105a7cad00d8f9365c2cb16516831fa8eab58db51930e7941fb0d01e7fad9006e0588b460ff57a02d6cdeb365e671f74b1d02143ff1f8dc70c6d2d5f808ca07470a55a5c2789e42a061675f1c945caa4319bcd5638cb0ef5c32ab5b5b617b3b07038f0a7bfe81ffa405e029e4bf7d665511e6ee6ead87744506fed5f7b03b6df009a959bbcac87ccce6a35d9ff08b3b774b8187b09fe53951b2fd59e0be6266e300332117a2323542713d1a4cf948f77236618b6655206cb2cc79ecd87178a0a006ca3b4ff0789fd8536fa01d1298f338d0032a38fc6316fe68d9cf7b791cf0870430c7433f2b11625480dfbcc78bdb2feb8b8fbd8f445cde4a1d2b90a17544e20c5cf2ba8c813821c5372d05433774c177714c1c363d323dbde8d05b15e39c1a09606e21350a6db59cf24febcc4aa416415439e32fd1d071c5edf2432e9cc274036aa827b9383d08e88d967ab1b1a4e2d1d1721c894bf5c234d890b1a08cb64c06fa64814438ee3cbe1aedfba74b0efb4a131070ec6513d3e5e7ad85ae842a3c0a24288d367cefdfef664a4eba475ae495242d1c0da67d02961e0f7ec5de2b62024d5b72707f83b786be039136b20673d6c1011e0c116d7b38686c03fd493a03070cb1d1833e1c8006f862d4ceb806df7c17a26f9f0c3d936a94851b3dbb430b021eb42094d46f12449028ece66ad3ccbaccdb1616550ca6924bc8512906ef34046d36f87e5a1cbb47563c21fed334b452cc2b89e3d3abef92a5d022ec19654d0a364e55de53765e7432438803b19a97763bad849d1e30231751a8a03c1b83120dfc42734feeb4b34d6791908a3f6d402b6df901e09d536aaa56f428dbccef02092fa82ad50388f7193c501c0d72e124a61c421992f38f7d183463e20e2f41220e77d92c0660e0863cfdc97a9c6006e6e7145297c20c9a5b3c180d68f48c391b0d9a25b571ee69bf8036723dc765f6b369df4bc32597de1abcd33ce6aec39cf801cf582b399bedeb1b7969e2504ca150355d741601c038846c3fd13c5084c0890b02457e00b23fd34daa2e5c70c1a7e4b179b6c0e4c3411291acbd6dabc0ea9a0dc0983c5690ef80e9d323627da81481fb2ade3193dd1b72a2fbf4fa5133c28e042173a53830fdf10ddc45914dd4aee8cc93ef93cbfdb039364eba82487af1ff0a96852a694c3e44edc7d5b20ffb30400225b178d44e6baafceb7ea0c769e592050fc1c7c77cd15671aa62fca3b617d88c228deb20c1544910975b308a0cc0fc04ebea43452d3a86d2d54e210af46e8ee9223024f0036a9fd1f89d197b414def08bc7077f0cc4d3f5d7ecca6867f78652e0272d9c3b11e6badc35bb09bd68451009a5d7be3c07be391d6e5daffcf9a32a7914e9419a87f8950bfb49ef901f532025d8ba2d5ffe6f0c601e24487ba28a79c0b7cc8357c4ea7472c3f00c8aea94b03b0caff63c90fd9ea4a52df5089ebed3f0c88604d1dcab84e65941c6cd7c25a0134a83af6218ec84ee3a956eda3c4e0f71899a99086c111a8f8d5b73198673d09078aeaae3361e52e9e40ce485406b657a1ec49c03d1d32995da6bf1f9a2df10ea612fd969c2d2ba022dc200d08196451ce3eee6ab490349636f79460cf229a0d6a54e1d31b21393e7a39a205d6a8eee040711f22c34ebfc58ef839f591f05d010be7c40be7d4a7c54c5577d9d2f2f4a87765a9b2aaf1dcfe6ba4e8e95476c206f0cdd2dad5c84e052af1062cf33358da83479b1e8e0bd7efa637a12a29a2d3045902c3684729b8f71887e6b432d9050cb4b1cdf46ad7a27b13a3411feb29790b620bc354406b4c62503aecde036e0a53980d19fbd52ff525002a5c8444b0be016b25af6e6ee9a6a33e007e00330d3894217aa57f3e8f537bc3074433f0ed190752eaa65cfea7c9f53a6d9be91074143fa6d1184c2d85c6fba9805afcf73b260c231e74f50e874744ff5616f4c6ae1a7de0b65ea0d9b8b42087ab3aca47ad4d0fb9f4150a7be6734eec9afcaecb0c21bb55aaad906cf245b5d04cdce26b950900506124eef578551460fca26bb05cd15bee44af7eeb9688313d39b88d2589d40bea62288ab601caac9f963929f77f0b81b25b0b2dac4a0cbadc4ee34d0cafa50696f692387a68a54572df991805f93edac488ac3f8cef68460a47fd4401b09f0dc3ff2ffac9fb5edf0dab0aef9bae19e9756068352f01176ed45b9d04ddaf730423f6de16e592a9386e8cedce0c009b58c3c55e542c12fd76717466240e30d80560289a9b44d230157220dc2ba059dd0531ac16e036989685a1b9178603e2bb05ea23f48ecdd372f1660ad300b9fd9430d32fb8a0f81034d60fa8ce7498ac860f76482c02a226f17c27721658ef592d5f5179a7620fc534a4181d577b9fe39b068eb2f02710d70b004d319757d0aea02046f3ad11b9dfe6009b9ee7b4d20dc008d28808ed2cf5cdb961e898f662538fd46686471f11bb4cbf6739ddaf701c1105f1a2074e8b10a86e3762633574f8ddf16c0ebced260f2ce0b04b3183a85bc2062b82360296f76de56f4d0eccf74cb1beed125fe37d036d49dd76e4428c373b09411f1d0bc491f8459322c6d1509bd8e5c2da9427c6c95cb967459bada9fa2b0c6c4b8949b1c41126bb706536f3e402ec8a63716cbc275d0f72aae39111b507034d5962ba05cf5f342ad0931e0d93ed1df6cc989087f9781c8fbf3912a6a2f000a228908b3471f68d07da2471f64f6990f1bf01f90c09f14b87e7c84c6c12050797a5646c658443ef91288ed20f3c80b23247786b9ebee529ab4b5c5b34dcea0ad9f57edd028a6f4cd6ab0774003f7038ff43649f7c07853640709ff5cdf43701c47c81fa9410aa564bd090d0f9a10b2335555b164565687d709746cb5a96570447ccff5cc02030701e06123b8b7077a22e5a0fb649d25f3d1b60af8c69e38d0909683bb86ed98db662e0bd8ef21a4b65170252ab4ab38bba3ae5370a755f4d040a607a3efdff6b01beae440bbb7b6a3daee72d6c56392417fc98fd3227a1a6092002eba95d9b6948cfbd1e58a6b1411cf7fd12fcffbe9ec0e5b409e37fdcd80c389d6d732f9acfa020a82913239f553fdafcd9de8bdff04edc5e83387fcd600ef7db631d7aebfdeac2a654e0843d6d05255bd571a66dfd9b90c12dda05c048049823d501966d6d87c90d0ad389061ae588929a44f2696d161e73ce339a58f503a033e4b26db6c2b2d6366253c347609e1aebd0e54d9324b2c4fce42eba308304f2e22ed4db71ceee0caefce07967a3b9bc479ee05a5c935d7f07698af2e39b0b434c8c035c011863929805f35591ee60ecff4a27c0079e83ed5aa696a9383408ebdf0b80613e9be9d993dd0bc19d9419161a2d15fe88eaf0f89784c101cda9021594949ec966bc746a108095917f1ac02a613a5a5094d1c76763079350c5630850c50d00ad4e6024f77c89162e6336956686b8a5d42412bedd042dd8b5bdf402b59429d0a5a564a9d1a62765399824ac66c7d60d0111704e0f2b8d8b6268c9033b292c294415cfd272b4f2975d6d285a4d06ee1adf57be445100d4b963b8dc0e5834611ae13ed856e08a5bd463768aeaa9ab410cdf9ab63f833b03ef37b7f608e5f148294cdedd1dd00c1678a1abc198f2d566858e9802abf0940591345f4b04f736cd2487f67db9d2b96cafaa2ab10dc821312ad5cb291862b9a9a4777d690fd6b4bb5097a2b9eed139b2990106dcc71f7f70fa187271df354bc7f102de9304dce8698a04f2abfbe7ec33edeb70f3ed222ff8135e3fa91829d04d1955463e065d1d4528f62f1248aedf8c95f918a6b80ab0f2351094c3a325b42ad55b45ce069b521e187822ab9b43ba0c98d0142562e4b75b98dc9120e84a32485e7776d9085d48cd062b9dab3f691f9fd068e6d4a4592c012b0030e1460c93cd1f964b530e4e48631cbe59ca335aa8c8d4dee1da8784b04b48257b16cdaa855fac4111dd094a3f99d0827881e1a2a1def5ddb60b187198ad912895ac7b7c57c24d18696a0ae2d3feeacbaea6c080edb9001480deb8b74e1aeab30191433da17e92d08bee0902d3e369d7a7e68b933eed9c8d8098ed444123b9e8fe517751fdc65a17c7cd086382d896bfc1eccd3457a8cdfa03e07854c026b3f2eee9fba029cdc505b1f60c474b7caaf5773f5912bca3a2ec26e8e66f7590e0b48e1a3bba99db000c8e0905e561775545aa89efba412a8b0546189cd437d91730058a5d9e19657eeff3d909b776c50ded61619b850e8b0dd7e482bc7086cca585993352d9c18c29011c940ac0239b5e3397e440f72a9e90379c02ca78840d9bd07d2edab97995f2c1fa8d07039d30f34a76701578c1a07b28af4aa559ee21d3e0e7ec94e26af3c3a5927f0872f186a57dbd0c421129186cd102cab177bcdbd95856d82eaee52641cb70d1016bb486cefc1d9e9e4508eff648a9a00d4ef74ea2cd2f5f9ef409a21823d2d3038b6bea9ec329bd794bd7d11d4d9827da52cf0f63e38a297c31ae68c2ebd80b075c97a61a750804e0614a8f792a155f1ccdbdb49504077e9c78b388b5c7b1c20de0bb2e0d0a71cf420a307c6bf95c194baa4e75f1ae17329f2347136c9165d80c906968a4d8b4fe4cc47316b4b4d4fc7323f55eb3bb48525f743371fd9229490f555a8391102cc32a215b869244c912661ff5bbec53458dcbe826346fe130c20560b6f67e0a116507cdcfaff714120419a62b7c5a920168f3c16f90a6901f160b8b7456033dc2121a15a1e8f5841a419046a8fd4c0138639e43bcdf3136729104887033af4c01ea7a7c484e7594a8c3695fe2de5b2371cbb90a42caacee20c60e25677f52cd8a5309d2dbf306ee049a376f5dcc72edc8ee8ca3bc748f2985dd0e5f41757cc5be26e6598b3f1ae09bd7f4ebc352a2598911afc413a65a5cc9830db90c001c0d05d7c2cc42caf697abcfce817c464c8499d75c90c04220d5cb6902a00d305a456e79a5b2f8b12116192131a7512a14e8b59fa4563378e7fc112603e55f74254654890225d8cd9761a759bcf95b7f2255b781fbf69978e79725e4089e64747c1e3f4734166160d24f485c3dfded24b5b78f118c3dbdccd2dc9f4b0eb1a1e21bf4082a8fefef292622bb184dbc961f3b3ad1c9bc7c22127aa2c8140a84f33400a161d3e997fdcb7abac42c16e758cc25572003040fc77d798b0ec7008f2e925a60b214a2290a991222bd207d03f5873079cd360cdb098dffd4332b026fa6706e93e0717dfea9ddf9882dd82356ea1853e9ed0ce5cca9e828f3fcab0cc94114cc9a4aed39acaefd5279658a9305505d49ae4b4c1c06e1dcf6616d8104db620b2143d261d90ae6727c7f1d2b1d24278027265546fff249fabc25c79b07690bdd91064ee5a97cd76cca1554aba02eeed6adf8d6b2a9dbf8838f9fd7de0298c0cac35fe25390e36d75ead5acb13b7dd7600a44870254dc3565eb5fd0d10b9c1334d3cf8505e5330a277a8d084f813e747c16e50566637f4df1bf6f961c0c3cf647d5ce746bd7539f791ca007208b771213df396bc6f5897fd5f821ab180fb038579a52d897c153685bac93ea47f17d06e1cde1548c12447076827eac8a068bcc6e6672f1586c1cfe9a6f4468939dbd9166201beff718b5723245384d29080feacaa40146f928d60edf2e92b1f19a33bb8a5f8fd62c4b1baf9a71d370d30abc620cabdc341217b5de729252ccf906bd457474dd6816d323878d9e5303c50b0bbaaf4354133dc1c1767b6fa52aa38bdba00ecdee583d9227f02f497a1534011664efa91388b65456eb45c838b13358ad7fc1badff00ae3d57a902774f95f067bb191dfa0f0c5c393c97a0f7447e204a0d707a51dbc58b59baff4c8ac814a0efe6769999a342a26b1244af9e938d280ab60ae4f22361998abd56372a14bd109a233b134964e695ffeafd77f0b27e6ad8395c3c48afb7fe63bc18646618e3c0a63e3633eb2356c49305208d35131836cf0d1fe996f38b722d3522b769303c10a60c9cfb37611c4fe96c0cd5b0bc9b5e7cf3168810ac24bd3100bf149fea0b20042f83d1bc10b79cca30753e67ae8c3caa7a792395eb2bcf41efa7a7baf11c80ddb507db44738dad23f87409dd0fa130031b649d7cb4b21163494835384736d0951a1e3a43dcd29e692b7baca3a7c95a03f727ba504f67378c72823e4122f800408f1791dfce084af98c361469209da76055fcd1b761ac380fd97a83bd6e57a0c1a4f37f3b2cf5a0661b0b932c8168eb3b159a7aefe6bd953c43399773d5289034a9500446af4714fd3ebf236201a82a533e6520fb075ec653c577e77a356e10873c60bb209c2b6910a1a3d299cfd17c7292ad85d04a2c124ca4c59002416270abe260bf7fbbb74277b642a771c29bf25b32149773cbdd013a575bc84fb845800f42a6f7e2d4797bb4469625bff4c5007b6800d646b0f6edd4f8b5e538f8b1c0a1246a2a1970ce2f4d8917a9366052cea3146dc1fb7a7f5d31db9d833f90e74054ee41634da5fb72aced1b2a2f87574429613842ef8914867b5cfd440e1d52a004193daa12b35e75a1e6576a0397258fc0f2af05f944b9b264c54715fe0062d01d627a1f246cd4ece8d54ac4c40b83c003e289ed0a372a64891f66e9d9d61d80a450aa2795c53b1d9f9679b15cf5dc2156955e50df45910d404becd6452e22d0852a1e45c822bf3bb0b40313bd5102d7c68f3c59032e2638160ec582afffc56052a157d03b88c64cc64d24f9699a3409d1684287c7cb0ddbc629e09394769ba0cf71e6b1b7be20b46f27207add691bf393cc3e3b54a2b4e317ab477d3d68a0a0f6c8c36a5bd42cc813604cc8d8ad0a5f72a1086b7865dd6c11b8e6b42d644b3089cfa81831f549818fbd8ad186b23c3fc0dd34682cb9c644e07295ea627184b0df2a27157fe589d5e26bfd5196f35c6202a33eba3ae21038cd93f0bbc50daa8072bd4416975582b4bc5adaf3c9644484202f77bf3453c4cb4af42332e88de300ddb2327c63ae0d3c974ef916a71582536582aee6b302c0e0dc3bd8aaf9313c70a487f6492b723494540a4984e58d9fd3db0fc1231c6f0940cdd4b98dce00f500356e9d377b4aa01b92320db1d1db924e8c05da7b6aeb671193d82976a4803e40d06e5604dfd8e45f17a4e3ef487111addc5720c2b7ddc1110d1c2c6a0011792099889e776a331c883d359936bf7b85d9cbf67b42053c3a30078e0f51546af05035d033948460e620ea548d4f8f2eed8f36eebe864677e53afae9ec191e9ea770d010cbaf4156dbc163c6bf0f99982b176ce2cec386ee9d2ea8b1c223ca2d3e20bf17b25d3a161e347347cc4ca01a7c8c41c14d3f25f74e7282625a9d235ca9d00abc1fd389774bd02ba0476bd72af671d2f548743fdc31d616ae5e9ccc89ec0008282db143701b8a282c21c37a2cbd56ba8ae81b8483a769a1035e05aa443310bf4489dfb9fff1d6dc1a7104a2a88ffbbe176b11e60e77eb52d6e74ca84705c0a0df454c694057b6287d3239785c44cbbccae87aabae162403f0a0c89d649dc0e2082b6c11ad7f326dcd40adca1429fc14403ee857084dcac8cadd0c608a79c0aeef9183115fbca34df4f6a86df927cc62c00302c65c0c863c270b56040ad310c58b4e78506b71bae613bdc56406e1af979eb2359ea5542873d443989b2f66c01d278c0f29352a3d8412daf8e930f77a167ad13e5e2bc38c1dd044991daf88b0d99e50a9c24744b9181dc891a9d09039bc5541e71f09937659291403a09c4b6076dab84f16b4f5676efdaa19a1cfb96ff8a0f85851c6c876a7929f836b90f5206fa7b022df38269f683e57d4cb09162a0972043ab5d5abbb526264a025a866805fa5511542dda7abd403439be2581d5b4ebee2c975a8521b0451590a554db9800880e2d208a319ddb7bbfe1ec385ab4015b3c6771533a0d30bbca0503cbda8e0249d88edbbe0193e12f6562e0553d701fbd708dced15d171d0637f5128ea693028ede240b557533b1e0e12bfb9adc7a17e099807d28a545b2a1f3f3c0d8c24201be9f0656309bc45d7b5b572fd4e96f3bab41e0bdd3fb4f2a050d2334abfe960550255920e982d76bf216bb445c258092887dcaab8708cc7d8a2e6d7ad881b60dd18387865df854d58d6ebf2162585d1def62fdee06d1af2df4d2a5b35739dc00de6db2656bb20f4ccd7534040d464df6a3772b3fb00ad11d1a9dae9b69f0230c15416ca5e7bd452bf06eb3ac5d883e1daa2e750c1699ff985555520d8ac088054c0e943df76849f57193ec7aac717efe203cecb944cfc59cf5a1f0940faf890649295011521e6dd9e38d21eeafc1a8ec97b102291e1869edfa5cb16f6eac990564657e755c9eb16deef92e52533c7cbebf73470071ffd9d3ac6d91691d59470aff858570c83cba543cc545df8fa7214f7a824b4d35932cce0393089e37b9500c153cfdf662abe89832b0943728d4915090346735fbe79d3604c27cd1fb7f960c958705e091f7ea77c738ac42d5b4fb02e8034532307aa3ca6e52a1b2d00e9b061385c00673e5684479a1366a0b224a64743dd22057d926b904df1ed35bf2060908d848a1c940c37016491d63576327ed8ca365edc95197ac08e1ea1e0a75790fcecf56ce4401b4769450e36266a51a01300b48fe84686ece0879ed42b5592d07f0416185169197a5a085868f89014a0063ea238331f9ad25861dc9ff594660074d1f74ebce39967d9d5de5b2d3e688c41d6013948b180f847aa050a35a8f97033a0bc7a2a83bd0a5ed7abe2f69e24b7f5ac2a328394c4875de65370119a99305e578ff9cf48091d7267bb634f1748733134b85a1901f73ea0ca88f885054cd0fe44ddcc32b6a6dc2a3c2151d4b0ce48f7bf8abfb3954bab84bb2d181f1ed2a03570039e6dcf28b20937abb8e423e7cf5784b94fc7cb9fa7ae268ffbe4efa7e099c04ee2a3f66d19b2eadbe776cbef826ce2026ca86ea725f4634ae850c47480e18a8453bd9b44f39eae8a97e2d7bfb38385ffd047d794d3f386b230deffb5902b598fe43e56a12376348d0ce7736030b63eac83159d556dcdc1b3fe3f5a37809439f6f4596981646cf1ae85f37cb8660078730b1b48280c732ae332b5b517b089c20b537217769e362d017f03c9f7dbefeb554e6e75f38f1d9c063f7fb1ef2023d90e4a7dbd9f068682f36ed02e9206315adfede9da8542f2de2eb048428d906331a4993d49c0fac8be7e987313d4f8532f56bd4ad4e54f045c80d5f32719f0f34aac3ecf843c4376ea8e5619a485732df5af87a8a65a59fc13ebc1cc05f1907f311a14323306907bcad14e4a6b0a1587b1e2aff8af672e9d086fdc65e1b8f077fa1c089b0c0ceff5f701b48047dddb5c3e231f59fe596160fdcccd2c6988a092e95e5594201d23071af49a4f4500832c47e7aefae0adf2bca6d06f915d4e401b4e16cf00c7b89982377e3c811e9f7ec94d18a4448ebf2691ea23a9b44925c0411963fcac0fecbfb3729d6de275d945510ec0a9eb4aa9f10026814160d32c00acee59e1250503392930732a9bd72b149232f0c4237ed3ca50e5ece7e1f116a0b2f2681c4f5e1d335b4d512bab0d60e1ea5ffd635b6ac1bac35269effbff579073ae19be93bb63c756a48f7f5cd7f728999d3c7bc8140ebbbd3aa2c326e3b570ce5cb5e6ce9bc057ce1cd440d6719bf7c3cce3d00e98c05689d900a1c6095600c58aa7e142bd8886bb60150271a7fa135f3ae067fcedacc87ba96d948b88c1005c7a653114987a2658105f1b3ff53c7738ae5fbb42a0b99b1e40657c3398f1e0ab1424e18a808c5a0e3ee2c32713b9fa9f23acc10bbc2e2aadabc649da7c085008cffff31aef2015db84b20e33407e22c741bab16ad1cf0d8c6159f2a1115cd08be8c8540d56023b88f64341cc9fab35e6e878d3c75ec1222ea1ecf833631940140f1342917aa8f20a78270e7c8a4d42f3a5f229575d054aac782da7c5deb7501170f1cbaf76795f5d2559444e59d48bed692feeac7784d9adf8405112cf0a00689d78a7f3899cef4cfa6ef860cb726be93c6c97d0d5a9e6a0f0b70563c0ad20598186841046b76eade9e7f3e0afa702c95a67be51bfa87f0ceaf378250696201670e3b021aa70b7c6fccd2a3345581ed7bee9d5c723f9429f7e3e196d621c8052011ea76e172cbb4984e7bf79d5d03c172f14f76f2a3cb67d00f34c2b03be409a6a588024d9f582dc6b807f856029dcaf90d8b84dde1d1789a7c09b9a7003a0dc62fefad3e7fb2a3fc415e54482af1156b61e0b7e0f75968510af00ce79ddd06154b53325c40738b11dad55f1bda101dd098e3500164f7eab2e1e8f0331f670578a1b8603bd31c4f0b208878f1013d50a4de70e08a6bdfbfd43f7e5baa18840e803494cfde8284e20f997f57b5480cfc8ef7365f5a53d49c6a55430f5a7309096704abdba8744a29ced60c9f4a88c0a732678f73468104d552ed53afd8368f0f887f6a232721f563489c742c0e3e3c4e57da1111dff7850a54a2d39e22f0fd098c39d32f69828aed77b9e5623fa036dce6607dc0efaa3fb90bf785db2abb0e0929938e6fbb486d5fecf11d0ee03df5407aaf9de8c1388aca53f78a1f1f1b53081c366d3dfc2bf9fc8befd539177f2ed74f8c7945d4600ef3aff572ba5f0bd2037426773fe7e1729249be1b1e717dec5606adf17d5265940ad264871f9bdcca08d86031fbdef4c992936aa8f57dee6dec44457f459f497b594938b93c78bd7e038a136cee6e73a81d381dbb4c9eb20744d34d68df7374fab64e286b455caf810f6893e5fe5263d08253cc373491c78f5253ecd362ce3eaa0f403415f640424b015b731b4870ac80c566b61becb5099d592b89e5d8051f19cbb2f548bde869b50df01fe224afd37144ffdf2df7c8fb9dcf3b6eba0ce5b541c2502ea3427a4d8d07d9f6ff43766dafa2e4cc3ee0e58d5e1ca2036e3019cf1c6bc30e9e044e3a0e062d6805fcf3123e1262e492f567b3fb97e53fdf17c438e14c8e4542e61830980740e5c9b5e7222c1470dc61c432430bb9d32675516c25d7ad8565c99216d0220c14859e8c44220b360f8e0b48019252cd73ff234502904d8337e05192b756570d20ccbbdc8004d79e6333c03c55f1a2c44d09ecda561ad35f597965231333fa0d838654e28cf2d23eb267d848eb159c6b287e5a896aa9522ac2a8a5f39db14405b0084ab75df132539c5e79047aa8bd75cda64ef0333ebce6c612a4d62476eb0a0e3e678bf857af73ebdb958e165fa5623fdb728319ddd10ceca2a86edf56e80f1378f5cf9c095f39ceb0f842261209a4d41cc0eda0c2add0bc9722d551969f0dea3b6b646efb8451bec032f29a5730cb872055de7c4559f354dfefe3e33537097ba91e97dcdf66274faee2cbb7a6fc4adb55ea9816076d97fdb815d88f281d06b85e83b08149ad093c15801c5f64d918f3ab27b10f5c86bf6977492f4ee6280e5069845c92595b342e211c20c7d62e47f27c3e9d0bf6de85b3d5c2a9bf3df908a4ddb45d2026fff1d00b7a93493147bb68abcdaba5450a4193f2574d51465d00eda3e0b27c71a2d7d3c4abc792d3ed2a953353af7b4ec968705a2bcf4c448900ee24a0c653a76a8fc808c968602ccdc6bca11807f5303e30b95ea785bc8862058d33313568ceaa74e247641d3625ded998d0ebe0be4622d6fe89c243e6116a000232570be9cd8bbc3ff17397f32e844c412cde9f7ef109dcd88a346356726805d598f1fe01a41c594a93eb0f153784949b03a26f1010b2d431b3704da7cd1e0aec9c28314f86bc3b5c755716f518048fdcdbcf124adb0fa9a4b7b563cda1f30ae12207f828e6deebc24db3f6166a625de721f4d824994249a9d1717b0dc91b0de165b11ea57d592ccdb429b085da522e0d0f88beb1e5ed7bb48f61fb1eff540e false +check_ring_signature 7a45c06c6d3fab2d9b153d2d480d1dd1e64f6026b3af67ec6e9e849512de5452 6d7775a7e7c66c4227da3a7e17ca2441b3438b88ff6f1979e06d1d4b7a2780b1 19 0509c8f26d051cf1e50dad64dbe1a141d5ae15b4efdb9059b3044111fd3b95ea 2fb7c099baded0b236f521f784f4bff5603ba0d25df3fae7a1528f67781d145c 64158a70b91649b577ecf7481e9f310169e2b7eea256e5bf84c1864669be8ba7 47271a573ee27416a0b53c5da53e5af5c5eb33025d3e564613739cb94d259954 51c9075d19d36e25c8aa191a0d9c2d1812c8785bc6af826f5eb0d9ade509fd6c 8758f79cbafa016c8140354d85bf3d4dd95889bccf707b38a3c0cca47cced098 ac399048f96e408e44c1a1399a73ca0e30a29d804475e88df738427eeb850a87 48c148c8f9e4348402cc3a9f8c0f7d4aa3224b1e1dc8a0cfa996b1460ef383fd 2e810309f667c3b5b967bcf2b15ad1e2a8aae85df7360c67c8d0c0946d2d1a05 7303542b44bfba80f4d530cfc602f036b1684870dc01f91860d4fa70df8ecf45 c8840c485467c9cee34afcd1432ade879f7336f27f0fdd9f93f73f12cda0dcbe 29bb7027883b68a0dd8b5c6c2fe928f8657813e3f6bc3232ab7b6cb86b76c8a3 6cd241387948d5f80e9609a5d64a8b59b645dbfd6e29172a628a86b7e12bea10 729c8ed726f41cfd1385269c00291288737b2eb14d3278a317d5574d37a8c988 e68cf94f1a7baf9509391ff9a7167113d26b7b375dc43552e216b8cbfb0f9940 fe25521eb6cf3d70cf9bd4efa93a724b0187ae542c2fddeba41ecb7413e32818 888c631f44124cb4608d13b7bcc66fb368f614365ab42b28a661a7dac300a230 9bb02776a326ed51f2352de649cdfa351d09c2e87e5f1c640d58b04cd3152afb 7d647b8288eed7927ddb638ebb8a09c38fb84a6b97aacbce284da8f8a99a2278 6d596282e23ec02dc90c3982634ab42cc171338e2af451488995e72bf3a7a903b4d92a9c0ee05080dfeb8d2ef82da3ab94c5e981aa11c47deac3338bafee48001010c2abf9845e510f34a3f3142364912bd0bbf14e3109956da0f19947eb180e141c8007b6b3f4b990463b57ea5d4e07de86bc39666a9f6b7983b7a705dc5c06f79b6ecfc436cb9ecc09701ae0bbc4edd0529910d451080c48de3915b1698304e5efbe0994794759aa0d8f99d52caa258cfecf60898b460e4f1c40312cf5ab0af59608976a3ee06a3c3311f58875ed6cc25fb7e89899fba643a6bd8949f67501966c361faf3cc5a7a97afa55c1f5dc9886df5bdfe4dcd2572a17c13daddd2f0b9a30dd677151c27e3af4b0b31216fea658e6695f2f12fc59e1f3b37fa17b4d038d8f015441dd7b31570149455700bc714266409655830885b76030a3d70eb3028741f5f21d3f4c118cb7ef97cedd305eff2c35b30df83edb82ae556044c5e2079bc7ca1ed68c703036d1fa6eac7c638d34b578347063ab2c3d9fa53f088c9a03fb1d7044e20bb13721d5ddf57963d82bd994b989e9b84f402e516a270ad4f30e84eb2a01aa797e0f43a64eacf00c2010c2d29dea6cd3d5e2fc06d7b267ab230877fbf22d1f437a04715664a028cbbabc3e1dfc2c57d741ec277bf24b0f6a5401414adfe70a7c0cff452d03c5392665a3742e6099b9c6a523a302937913f2e40124825150ff0148970e7d4ce6e9951fe7c498abea0d5ba9626e6e2df9dbc2020e65794f1e4373aab5e9ea612e4a833b8163ce06caf4834c002dde2ba72fdc8d0c0d904d6d6bc97d35a152ef91e9d8a7a47183019a001bb755d3eaacefbb573c06b9b17ed6f50c3bc41cf8c605ce68786d58f3f04804c8261994129c47be756a043570020ef11db514bb87a4891ca4ece1e5f47ffd3777deac4e727c1f5ccb5a0c493bff11fc5e780a44a0f6d9475dbed5d3cbcd4e60a5cbf55895e4ab51b6470029cc56eb5286f6703062aadeb53ec7c89d3ec78075842f21b6fd2ac9f608da0658e2a7ab571bd7d6c7e995a3b1c05743b4b1ba938e711bbaa23c98cc7c1a480134fddaadb9bd434d8590dbf1b419cd714f024d5512facfdfc339fbc591a79d0ab2de16cba186c45659875194d4b6fded1a8410e1eaf4b871b679d6146c3fc607a2e82f65b4143d4802cc50432dc34831a45bc92808fbab5fadf87b733aab6306fccb5e046b3d23fbf944f93810e1d6047c1476c2604e419337efe0ff060feb0e6be8078fc073ffb526ca81a4d7e19279c8ff886a2b0ee400c98053eac4aa860b01a68e301344071199613985636706f602825640750c08f015b8bfb3de249d0421a1b9d7ac0672c87b803e4aa1a3c038b92af8a3734f6c73f427a5da527cae00dc7b5ae58a2cd1e03ad477e250801227b81300fcede19adb36d958c0d389330d904f9729757bbb4569e44f90317421d3e4582418b7bddcff4839497c1078b30d8a9679ed2c288cd46be408ce14d42e264cdde1f9678ba5d294070e743277270e962e7cbb3798d077a9e6579df42ee4923af47e51dce634026901f4930dac0b06b04aeb816344ae8fb0c8c0f922395bb920265daad43aae2701629d1321715a070b45da6524eb5b39ba0ff42d103db8b885489fc4d31e05ed10cbf7810cc776066266ce8b159b4fc52010af56f627cc78c552f0c1f49b48a21a830097f251530b true +check_ring_signature b8858bba3eab313839fb2be39bd11e7a604b6c77ae252cc3d16983585bc243ed d458a040c4c1cfab426415981b00b52c68d767c4c8f17bad1ed0eac154c9b336 31 24ed64409475a080954011ac88bb3b8409f940cf15d545f07d213f7587ec1fb3 5b486e1a3b027799823c10e92bb469483e209c92a14a1de08c13e9390d049033 0304baced1679088405b55e95b28b8a1906b4730cda359b1ec0c23828f0e7584 0bb16a273ba1590f0c4cd9b033b9919a6bc82c62a99131f0a18f1a84a759045d 158353272d264067d449e18271ce6305cc8d5ad08385ca870f4f1a75d733d9a7 941f541647f9d401ec5ed8fc7d4b8ffa9d1f1ed6bae863d19f6cc7ddafe688bf 82b2820164245550dcc40bf61b4d470ba7870d185ce66cf288636ad081ec4538 749cfe8f9810a33a92510f23abc465e0fcc09dad458d1ad6d2b08c6586ca17eb d675bf09031ef51834a9f65e7b2bda429bdcfa9a7425b8a3dfccd842a3974a2d 4471362a8d3e0a2a348afc7a6822a73f78d367bf4e515c7e289a963d8d612aed 2b516d5f058eb5b7e95debaa980873a01bb08c57abdcb299c3c0d987c3b71649 7cae18e686fd96bf936312fd28d6befc35eb170ab0a4e0b7eaf529f0b57feb0b 137646fcbcae65e57a6cc19547291572e64945e978d10771501b904cf6f6a81f db7acd81596421f21c986b28c9e641336eabf3238a53e2e4b284d3bf1cf39b1e 8d9448826f59cb7c8a54424e7d20edaa8be7580f7ad940007aacf540bd44a1b3 a715b648fe2f24e3d8f45f9b1144cf5b2b5b5c4d2ac5abca977589a1f3211763 d1ec2e9fb1cd36d8446f56cf9bbca26efe1d9756131abed6b49cc2237714ba04 b7f1f0f32da07f03107aeed273975a55ed5cde9e477bb82fea9c54b50ae5da5a 52a2b2054b7e132ef02f69e784e911ffccae20e5dfce2f15fa65185c626ac714 2abe8959bc43af5a570282e4120527660278cde1f1be084014d5046069ce5523 ec520188b8e9620c5c678ccb5e79dbcef0b182b8fd2c33258c534ebd971da1a3 2e21ce1f6e921e4e7101e7bd81eb3496163877d8819446f5d2f1b73ce76200ee 7dde824da1d88af8d44067536986fbc25952a45e959ddad436248d1bebd91c86 46beefb4a384ca3642acb09a4c861af5af2885d01c0e294505e8a5f41a0d1ab5 46bfeb2835720a8cac6f8f194de3d749f68c51627d1cba3f3800097496f6667c e18cbbfe3b0a323e59713502ab65ba5c8ab3c707e749eac14e376329cd464cbb 9ace767a36fc01732fb60fd5d5656ac918f256c67a4f10359e231434875d4cc3 6d2731d145bfbb0a4f6b4e550aad15c7ee6a90f2c4476fbdd52fc62064e9f5ff 0eadeb8da9707dcd3a119418e85fc61441e63a79ed40696c6dfe7f178bfaf993 0fae6ff65450d7584136fbc47eac166266e245449eb680537982a691b312d704 361c03072459f3fd6db86c2f8c31e6d12d0968e22ca21622f4076f2f4f93a661 cd3f9454d8c913773e2e95e3f3929714dcff884ec206374b3c9a23e8efb70a01d7f3a43e15e87355613cfe3fd3994eed7f760e96b8b06d7a4f9fcddb7f298e080dacff099241d843f04bbdda8bc82cef871dfba2bd65f4bb69cb0770b48fa60823f2de34f3e8e8c05f6c5c637c747699dc3cba1d2bac40d4f353baac00e192086875a2c8fc558d55e1d3bb6ccdab823c42f9ef58d0bc8d66473a2c802c9ea20b45f40a48df19c1064bc30614d6bf204f50a8e2fb7312f50f297e2b9de0ff7804eff7906a8d12b55963129ff515941353c2095118f58aa387dc7b02ac750ac402b652d66c0a42a8ee3cce3389057a7b6e0f6da25305fa10a5b7e54d630d953f076d2a4372440c93df27ee2f2b6422867761a38acdcc16eb33a661bc90b89e150b8204af943fdb16aae3f052eb9afd7d7c123bb07e621ef8b63e65dbc0c36d840743ceeeb052b23405403a2c9c2c35c9a0c71217952614e7831c830375af9e5806846579400a5e55f89dcdfeffaa7945506f548686dc265d164a8de83bc0be4100bd4f9630a388d456d0174f3a307db54593d894963834b2fbca0becf36fddb30e288b30e6320b4bf54d6118b56e796149381c965394ef6705cc57d8f4e8e4d30abf1e7f58be1f33a07278bb684e822ef2773baf2a5240eee65a4633ffec92a20c31bdb5542deb61b68c079e755aef843b266353ba33fe2818e9943fac91803304e0a4da844719323194d90ef496c9070281d60764cb5c2dfe3140c16ca391340d92e60624181fbe23f15239756ccdac4a70ad54a770a1ca57d72d1a5b93a88e019f54d014b0901f9b37333d07e8ed26ba2af5b9515625d09c9d2a27f4d1086407df5b35b4bf2ed08e687e18940fb77187c56e5619e34bd8116bf149bbdaab9e0f56e5a57a78049f88a3fadb4a8ada82baeed3a666a37777fde21bd060a3139e02256b0c9980393e80fb814b9d67c3e4f9d93ee2a302e642899c2c4056b174880cf658771d39c74054d97ef34caa63bf382811fdc64d4c5e9db23cc1bcb99f8a09198f94bbf45d7888d6cccc016490c4987a5a9611d72140d16ae126530448310c9a6b0c6cd15161a85dd1e7a68f8a1e5ff5c38d7e22a525700c3ce6dd563fd106d9992c10f6b80d155f31c4a36bc280398f8c568c28ec67ece8e95c730914d60812bd17b321c2abf6dd37b1a27c7ffde139a649d3438b8da04c22a37b09205f008d65aefefd7ed82aff9650887a245d62ec562a94050ef21cee6c3e9c13dd2b0b9af86768a193734ce7ff8f43b6027afadc389c6db21e3370d74e5cff639e3f012f75a91dbe7db86c4df8bab55217489249c4d575af337cab1f77acfeb7f8b007511e0f0705b9702c452c6081d6a8a88a35a21fcc79a305e5c7848488b487720416ad18fcf6483ea6a7f2326044f817ccf7655c0e91238cb3ed4c577e6c503d0dee1522abf4db0e1b4efca1caad4c11535dcd953b01e1f65e32087ccfce1e0b0a82ee6f6db8cd18961e26c5de790ebda81cf2a34c7d770fd835c318a5603c8a0803321900997c718644b30b2f001f3333591b544baef8b485dd1b504246adb90bbbfb403125ae43959308a07ff41e98c06c0e980b2fd920b287f5a346bcce7e0821e62f212d75999f21cacf6dfc41c0cee6e32f8e40eaa08788f8d79b2c61410f0246ed76de51cb8dba47ae48e89b4850ae5637212ffd5d0ee3a33dc93ac5de06a277feacdfe9e315d01ed37d0ab3dfb9a5d6454bf07c5d3de331963d0840a3033c1c817df8e0b4c7ef9cfb4785fcbabfd5d202418613a90e03a14fe44838f106f225d68d569926f700df4e85abb30e288079b8d8536b9bb94c35ae13e48a1004b572d7699c5d02f150ae9552f31dce2bd8f6cf66328bd2711d4bba4148be2d05b402314af9d9770f59b5a1b80ce55a9ff19285b7cd8a4756ded52a1ecc58840d926ff04f58e52895bdaacdc68605efde87157925de689cee9f2ac18def4ae80b516b061d281164ef0520392911f4805fdba7fba7cc26b0c72834b6fa9f6a73001018e18acd71257d770239c00c42f475aee35e83f7baf0e50e45ed74ec561c0959410b558045ef553b8119de4d19b09598f4f2565e7089bb34c7390848aee505e911e6b19e6e9709c980e88f6131e73e5e5f8ed0da9f0d8caa1c05267c1111086bd28a053b5842a9bc43507ba31b22e6146ae317e0693dab02739a4c6ee077090575101bc35e59c078ba318e5bd0691f56a299ca5fac00296277951450597503e1071c13d727b41146f97b1c80e7fe210e141b6929a31b2a9c4145fee5e05c0b6af81d1e4cbfaa9c6253009a3d1bb8015829af8a95b2c10454ca58ee29e6730c3e82963570c1c0b43217f9a90fda25a7bfa253e998ddc1b26af3f8338961d70ea48011303a480639d36808eb7ca61b5deaf7116cd9004640fa95567faf9bd603aee82afb9619fd044a20d66ddbf83e52644ef543ff22e62ced3c2327e8cec50ed334f0ef038f394aa5c86fa167523148045f605d8aa3bd33d33b4083b1e5050d06e8e8389c87ea814840082d37560b815b26b0ec5aed28f67bae75adad1d5600760ba3dd08af5c1a53c7f34d9f58b6ff9359a334b7a8aa206f62818ead271f0228be309ead3cd614b3ef51655a936abeaf88866cce48d4641ed96feccf15c4038ac43485fa8f43bc6cc583fd37a11208469383c407d6fc3023566aa2d8330f0a7d2f9e44fbbac9f687aed2e22c1877a9ed25e64dc3d4297529ffcc837c60bb0c520eb72374634ad954a93338eb6cb37434af12ecb4812f07c1caf39a7ee25e03 false +check_ring_signature ffe4bcd76d3857e4ef6e0e5363744af06fc14e1ba12674eff911690bb5bf9340 341c42035bc98f92faf9c90912056de75c18150bef7b0afd80c49dc4dc25850d 14 2b0d1a61e503b1f5fb90f377b9efb9fe0c2f333ef80ef130a31b76ce9696982a 6cc1000c2dae14289bde8b3b4b69dac52ff1462dfe47d6eae66f1ea6695b8565 19e4d00e71b5f0caa503b0164958afef6a1d5a0a220d93cd315a854ddedb7a43 afd6b2ba66fec74218d8d617ee0476b362a2307cb4567e9cfdddb3322d9c4b99 c9502a213b6ae9cf22407d85c1e44931b20a57b2a7c496a585790a3e879c11c2 0f0df8bba6de025b1eed71306de8f4143aeb289f3a014177dc75f10c296b2532 b9d2683194dae980283dcd444e6060f0af9bad5eb9b1b9bddb06d91ac180a2be 22cd7bf3a23e84e658cfa13e76685ded6362460edf45e5254587345023e81c39 62caf397214e87227b12b0110899e5c8585bb81019e4a3ddf56a382885185d9d 61538e86af603fa5fe08be4cbef2bc934107bb69edf16df157f9913159adf238 736d9317ae54ec54617036bad811e6721445f41c9e841e488a936e16978756aa edcd4f077ebffe8f73fa7f1745da3d54f82d1849ca458a6feca2cd10d9be0f95 536b801dba2a2c3ac9777abbdc013d730a488affef4dff279ddf826fc3dc5046 95626b9715f7c77e564ec609976b817ef2814edea8614d41185e818ad26b0948 72a3efa699c459de550b7c36edcacda508a8b699c12091474e20e6a833dffe0d119d8b7a02271c7996c84ff6a16c7032132f83eaa8ae18c7bc8f071e67ea540787ee2a94e78c75c210c2d54863ad59a427265e0343c8b2af01a8b218ded5fc00c0f0987ea8f6820e004109f55e547b36e9ba5dc77afdaa85080716482f762f0816caa1c4c879ee9829f5ebcdd154bac0fe5ade15b9beec18e2d99ce026ec5b075e3e54d761f7c18683ff8fb5439a9e8725fd4c3415b693f48c537eb458abee068f26c4dc4e2cbe9165601304c747b22ea7195d3d148f9d991ce6502cd41121081857b7ec0bcf80036678836fed08b6d885f850552ff369529e458b05c4337f0d4643ce7ecd90866afa00129d3c6733e52c10ec8c7824a77fd9910ac7b6093d04e40ef01e6a62e79ac2c601126ddeda333d09d0336c5872b00b9305ea9820180b5feccca8bfb79b11caf35adad30d5175d40b33b3d0f0a0b8e34d6cea8045e8000294904f79752c1eec3f5582fd113f8c546e9223052659e9e3c88f3214a7ef04c64e3eb142576ee09db3476ecddd11ca7fb869efda28e0f61011f5c227616003e5402505b984876a1ba76dffac935a2e3295e586ebc17c32f083cbea38626b0356b99f1f5d709ae6c72152fa91233c6fc41e0ae94832f411ec764bd6a4bd2a0347edb93780925902a48dc6d7368d98be95ae8b54fa2fcb1c865e5f17446288e425f674d5d06a9ee03c24be2adba701e6ba77856147fb4c2615681af3bf417f07ed8712f91f065444192a6811293a91f026a9ca4b767cc85619834a6d5987800eea7cba3468d3ab49ec13c01815ec6ecb8c20d980155a675963c66721b63ddd09ca9779f98f1a56a0d037353aaef6225f6767b6d79b3df3fa0fcaa36eef279c0e6e81ef783ff413b4721ab74f082a00b34415da8f493b60856bbce76f6f80b0078b84ca375ac2a53537b9365fe5557aca72e0b7ec4442101d1613be87f5950809f84b0711d47fed47d9e585a4aae266249b76e3f40910243313de785a80bb7704e7fa41025e3f8b953db87654dfbfd0424be2b376889a023012524e03a8222203cb04e2b907335df1b1fd1c5e4539f5210084d599353499a8e8e7a5d79d68e6001dbd58143fe66858082242953db7aa141403ae71c139babb11fe8d9644929d25f0ff3913a8ede73c63d98dd3140dcfd2d7d74d6faf66f790e97654b85ca2b90db3f2e6f7fe497a461beb83856d027dd97e2ecf443f30331a86279130ea7f640d false +check_ring_signature 302292c6a9c8e0ed98cfe37f9072db902d2f97b45acf478f697b0ee35b77cc21 cef30b2c4eab8b7048b9819d66928051fa2c2d123dea37ce8a6bd6c18c735313 136 fdc142c2307a84a381d57e9704da3f608df04e0a573f17efd1afe1c990586d18 c8ffa3b22004c455c8f75bb43706ed67e5772c5aba87f05252bed9e2bcda5687 baf30f732e0ecfbd9114bff60734bd137c1db617e7eb9e06cd1c3b21377ff8d2 338ef6eac1cf8fca33bf5a1a419f1af0c5f9af0f94e5f74c3251cf6033d6d7fa 452264bfc0233d91008f7459ddd4c8951572a615e25646718b8905fa92f228c2 93a1a2dbe1c02bd2117b84dfc206fc79ab436918446fe7b4eeb6d682de41b7a6 711de64b724bc344218d577b99076feb3003874785dc97ae7432b001ff3609d6 ca7fd4523f357b287198576a97edee09a0d9ed6348240ba37daf9c1baa71e83a 3045fd4095242eed6262c411247f6a00397d0eae364ad48fa51a08599b05af96 6a941829ab95a94cfa79b7fca0a1ff70401621ebd3b560c180e9411d66dbef78 504a3ab82664a3814d2d2e1fcb5598d8511726497848f4a5a8396455b7eed4c9 e9d6160e2a62e0bdd5cc67f6c72689b861c9ea47a9199ec8746b684d0fc2c1dd 0efb52a05dda304c25e97654630a38dfc3a32530a2bff4d0bb4820481bbe07f0 a02a8fc18bc644dc6253fc6a5120948f6a5cca9f9811ed527c70f91d8091927d 1bb0d75808eb2f8cdcdb131332055e6d38d397ca336ba3e515891e4e1de1486f 0fdcc201c5c6367b4ce736cdcb53358d72e56f3f4c127eeecce94fea84a8efb4 d438034f8454b408c8d7c2650449f3de55ca6005c5708452fac19515fdf779c2 304cdd40685dd9558c90bf7e022720b00a86c1683fb8c30da5ee53f13d06413b 6ffb1e05472bfe5d829f56179b23968eda0eb960304d8842f89128b84a0fdd74 66ee656f4bed5146deff89f2f95e6762a3014e1f585531dfd0d998f80568d360 2c82a00745ad3ac9b74dc812b59826969256c1abdb3d4367173d0b6305758a07 bb2bafb95c8842937b346b1b3243eecb0092d3b4b4f712f413919f26c5890f89 2ce9e7a78723381f80714f01a88b26697b788c5a4ff4b6d820c530a987a1e7ee e3ff45456a67cfef3da1ea7fec5c1b6604b64a753ece265ab847c1361809dfa8 fc2565fd9dedb24c6d863c50812278458c8af96efe2974bebbc0d66589366187 5aa809299bba66495e94058b6cff4a3d8f827c9bf5feac78ef02b58cb34367df 89ebfece3573f524c87bd99e3a0c488803014dea7d18d6ad8166f036b934a8c0 bca1320c21965956eb90d39ff83c2256cfe05cbc7da527fd41c6925612cbbabe 0822fedb08c690c9217c403970bace20678b66231f5b0c495e75fcc23fbcc39f b588c7b1f8a4b7bc7ad55f8fb2a13a6227275668d9da6eafd27a8d28dcb75275 986a1ef9c0853d0e88d46c5c95c24bcb33416f61a49c4b8aecf9b303b9cd4910 aa26f913ccb98c1204423ef3e52cf3662b91bd33a4331543c2820c2575acd21a 5476da6f2a29ada9b46de68545ea516f2234af3c793c98b896d2d76c51d8b460 b34a1c33b8a00c463cc5a0335b71555b975ea804ed3a88a020ca0c6abe35b54c 0be356249aa36ece40a52516a141177c57bc0d727c4a68d37f5d9dd1ba23d6e5 2d278f7e4ecb69af7d90e3b894693b824decf07dff1578edde1a28d794001df9 2bfe1a23317d83d9fc8e502c3c21e1a497ad1ddec077c4f725f65241ec08760a afa0a9caf43bde99d9868da0c370c7c20560f75404bef95bb3cff1f0b5082484 30d01aa2deb6e9736dc22ed337d6ec0566150fd37d8cdf0ba95509efff555aca ae51748c82e177b9b01f0c44c5f7eea0db8ae2b91a035fa3b99b2ac300e26749 69ca915dc47326241c9c1fc31d191cc8d5fdfe59b8c8ff225bc1811e4929416c d292ebe5a05f6a204bdcbac7379fbbae678f4c00ebbe464c4b9f38ca75498515 1c84c5a7be9e6c771cb1ab7411afb97ab4544cc2cbfe9b184e90705b55e36b72 da305ec73fc11ab26b5e24a3878bf2cfd60d1d4a2cd72658b572ce2f1f07186c b24b2a341ccf6495bcf14ab288e6155302ee3014ecbe8246c46ea275ecf69949 e6923a0f2052b0ee9896340c11268c6b9f87018569c49995c85aedecf6b07dc2 f6cc7cd67929ed2eac1b5e4bea2f9414f6e4fa2f0de6f1efe28d558092ed15ec 68ba4df861770afcc272ddf34b3feea516fdf3fe15d00efa9f5043c0f95f09f7 4f50102e2c91cff6066ec36625e1a7674220b0565accc62186b8241a3d586fdf faa60624e3d48a431cc3badfd93b3a96d10df2faeb0eb142e8626caed4369b50 ea8e52db62d0fe3118020ce97a7ec6f9bf713a2144634e1fa40850019a74d50c 5f069785d3062de253185268392140630780ece86087a06f26e0980bee486071 3bc02493828ce4ff34ef9551b40947caf9dfeaa5d2d2e3bc9bd1841af74dd865 b1d3199dcfe9c6c9dfcd6e9889c4a928558dd8a8e02fddbc2fe87e06446a3679 5564ef8b992b60303063c58b024f33155eba3afd99a399799cae6f3c51305c9f c44f247ad7f3a41ba4efdbb6b000d7d01d17be0f2e96cd7fa844c991d37559e8 658e024d2440dcbda50f5a7be2aa1193f9d0546b4a7fc4f78415bb49df984b60 50523b7c625db0b45ce9e5ffe36093e45bbfa28207c610960f7ad7437257d3a4 9c239080943b133b237cb148c599405b2fe2b876eb8d5d347667dd25e7e7391e bd897b3c521951a3443e8bca4a43e79a383fdc93ad74df90610e4e79b0c0386f b8fcf598f5c3c76c151d51c76b5e43475a7decf304620bae40565f7aad153ca5 f4625faaaaa794fd1c3a11ff8172fbc0d75938e9ce91ae81882cdd98415bf950 51f4aa8eee6363710df00e77829e659678b7c641f9e7f086f16c0cae9e8333e9 153d701bce90438ada21e5f87f6aee87d67037fc68eab933e1d2fad208e92079 a0d6d395849bd5ae64a95e814623c4580c96ec99ec1605ca9dc52affc03d3487 f49dfa65db4c4057c24742b78dd5bef2ef745edae1f223af021fca60556cec09 904c06548640e22e3c92da7a76950dd924127f816b4528a836b3676f2ca90733 8763a4b8cc048b35ad780453e7c5c3dd79be28e8e565db660b513b4f70c0c97d 172f2eb3aac75b2e78cdf981fbcc315dbcd9a8e2a2e54eecd0527c87a0b26d01 dab1201087bfb3455df0a11e4761912bbaff3d061212d42f1d3f6602a3c41b03 725461ce1bbbcd79597a052af8f4b0c8fa552f1831a6204f9e2ab71facca40fe 148d80b06af3c37a1f608fed9df153e42445940adbb8039ffdf07358e74ca734 62f73ef6cfb83603b33978f3aed94dc1ced41299aae271c7b5bc346c99954060 f147ba5477b3178c19fde35a084bb45c05029a5c57e0bcdecec4d61c0403a2d7 d75b63a3995d5b6bc95ddf581f8674d1adf82daf15158cbeeaec9af764361f55 72ea197203c96d47d9d8872827da1a247506722f71a9c2422f277813dce2849e 9e09558d3b92a593125e28f650451dcaa166b1263e1d60ae046be5ff9f24f93d b0c380f6fd1c23f84d2a2d943febd0cc7fa1cff65562e12e5539cda6c2e28561 064635cec78edaafd2e8197fd080dab3e58db76a92fa15fced7087c389874008 5920723a19693ceedfe1b5802faddf150f721c773c66072dcaa37a918a5195a9 0bc0992bd696635d1c7e797c40707a4ee90994eb2ef2cf6ce5d072c1af455522 28ebb8ea2d60a8105b48f28a8edf3921b0007e93d973cff368b83c788290f9ab f6123510466bafbd65d86dd87ba484dc85138c5d724a748d62a0dd1cf4a915e6 dd33972cf4a7aed68abbecfcabcfb238c793bd25a83cdb6ffb883af7f0641099 203f277e27c410f619aa464d630f1185475d160c9724b7936bf6b43228815a70 13c484b10b3e7067bd62586dbe9b90f6fb4d767779edbb04cde4bf22a2960945 b0749227c1a3ba0bae07ac34be2611be649b7fca04fbc71ffb4d9d5eb9786947 8dbe111265b5c5db604a1fecb78b64703bde4e07bac3a80da96ad74f4c8bad76 bcf150594053fc5b985390930bc5275ca7f9594cec9d4c5266b03f5215a23d94 0e984043000ffdc52a4be5d30087ed9a702ede376192bda758113a26bf13acfe 32466a993526acdfe23e264f5c87d2ab508c35fcd7c76927016ddbdc4d693579 c90c281b0bbabb8508ad6688b236848a7ff2906cf2e98d37d9d256cdb7f0d0b8 a4ef7e1f06025be05c4bf18f9cd7c1ac6fc2942bfd2f13e520c32e0164bacb1a 494336930333750a6ffe674f2eacd07f8db4550731ff6b8090f222c5eb847e82 17969149a9ab79be124452dc570467d45434991367a96e7edde0bd9ccd7ea81f 205a822496aa71372030175aa4de2a803489a2f2ad682515aec8a942d37cccc4 8fb8995b48662e138b0a7c86d2c366125be33a6c1fd1a6b5395db5f8c95eef09 1af8d5dcd96ed98cf131e5d97573632eb256f31528b8b295ff86c0ab59d3e749 876875d08cb04520844a7ee5ab808d4f542e611befe0ea5e2fa6136ac7cdae25 ce772c93f9d05005c7d91d0010b26daa77da4b1887e264ef9ea8a856d4e48de1 59d3d0eeee019ace86d6ebf96bdc3661bf62f1bf161408a05561dbb2bd9bf3c9 aaf4586bc89d6ac87514c86397f67b0efa034a5a9366ca2c6922d80f60078525 1ae9b1d4fc3a152848c3bbd265d75b20ef5f1229e562f2e4d89a5c0ee9d50bfb 214570cfc04f68114289bf8365d134be4c34f168e5ef5f686f36e653b7942d21 eb69d60d49dff57d64a37de771475563631ba2ff42a5c1ba3b5bdafab8582f99 261d6f5678d97dab8a9302affdcb57c965ed4913c0c23e4e85c1d0aebdd32a82 9c3e25490a9416b0f20cf708a0ea92710573625497662739926c3d14e58f97fc 664b3714d9678279648479f0819c6addcfc98f3b525696dad8a37012fd18ccf1 8b912499d1048c87e90dbdc9af6cbc664250e7ad28300de7763452793ba0ee49 6c6be354f8e5ed688ee939c77119e5003e8a1f5064d40a276fd1d77ab9b2152f 4b926aebafb32cc4f6903a30baffa8fc7bb5093d2e9b87f17e9d8fbd2492c1be f211fb3663e3d4c8e1c1bc051f999e1aed7a97e814cca557d1eef2d9aa959cd8 288c2300ece4e31a0a6d1d5c00c4315a4989db663464c46476adbaef2adeac61 f7a7b0bc5f4fb11995096cb6478725c6fd1c4abacc783b9c5cb170e2c821b3c2 b474ec321d57534ed17167580ea5a188365f8055212d2ad2b6f3a1c47280f99a 1aa81843de5038edf324a1e5404fee3075412df5a3c9ae8f153c3098258e6b1e 231d0c6b9ff77162a3d06352e9171e384fdfd45746aedeb714ffe1faacb3f670 31be505407b60212a3d6e6c7ebb4350a45b6ee24d611e8e52a8db45149c892c8 3f9de978112d03e5f3495f878ed6d0b79f5e03a77dbaddb5f9e79959b1a36dbf eab690e086a85f2931887e6d5b939fa93f44d6e6f1a7acc04f5fc42d5023fc4a e0d918cedfd78f9e7273ac6602b419503246007939977f287fc6cca70f4b92c1 43a894c0f26bbe55daa8f15af4fb347e715b529a8b476fa5e9f21c7fdb14e5ca c51b82c968c3008a3082ff066147e40b04187682f70c7bdbb46c41e5264ca228 5e01fce0a9e9749f3a918fa596e6df1474e3efcabf85d798f5529f0a71f1cb83 88127d4544a1937b55d3d4e1d6d6eef07e9fd2df271441c7aaf26b8cfe432fbb 8f0b2b9c292d7467743d73f9261ce63e623e466d7ca3f6965c474cc95d76e595 20986a8a726bf44e38da679f83baeade12058bff4ae7b256a477193bb385cddd 73a23e1451aeb3b9161da36ac1544f4404645f350ba46bc087a838d2539ac228 ce55ee3d8b7d2ef36af71155f9555c3d7a195f5695c51da290f3631493f5312d fac813520269eadd457401553e0c3e9fe650cad04c4fb5bcdee3543ec9442650 b5a8101185242b23150063e2fc0f6d537a7ae8edf9da02bc63ba06ac6cfa20b8 0e432b0a725dc2777852e073b807cbe6263fb9d195a9d9cd538bc759fc1660df 4bfe0d7a0663b92ced67c7368d919da1460c0a881a95a4d6d04ee96adab6691f 780afb3756ea15db62069be94da13bca2c2c8e9a1afe6c4ae4da2f0dd458535f c82b28c6073ae219b8307932ce993935bb7ee420d8848ce652fcc4cb82763f9f 2e682287d0ff758b1e66ba20467a7f3552c5d8b5fc616df5cf200654c7643a51 e7a37d4098f114ce505d5c56d23ece5472e729bf7f3659a825ad63bc4a8bc20c9c385d81eeb08408e45354c6bf337bf4b2bcb7ac79478a407d1eeeeb556b8a03fe4fb2ca5c8ae9271e0ac76edf83e8b56df61acf5032175205a4b877d7045f0a55b5c9b792c50f16c7d864fbe34b1fc7480bc878fd654ce93a6318415de7b90f581a056d9a2f2421cbcf7cd0358c30766cba2dd7a3a26d333e137a8c43e8a20af1c9f615c777dba0482bfe2d881a062fd039f9af21f750cf04d98dc8ad657b00a02eba808d67a3a49b6621e9c3b250f913379f991ce8a7d900e5d006f8874e0b4951032626cdd0d64a15f24f1793b3a4dca648c4d5e08401ba145ce0d78d9c08504d0c840aa136b7b9b854781845e03a13c8211df90c9a44b3350054b5605c007926e00ab1dd53d25228fe758c0065e24933e27263c93cc97ef9939de633420438065b7b55d0094f1da7d6071b81acfff72272123f4e34339bdac7e68e36be04da51ff025d1d8f9ffce62a812f75acf022eee30f9d66f9200ca1baf95df1d1047efc9ebcd9d137691ab75c74c830cda9c43b6b092ede7cacfc99d4da69401000b521601621fc6ff65fef40161c3e9ff5e4959e5af47712ba270e051c9b32ac028c8bab597490d24052e6e0a2a7a18519854aedddcf84f4d8bb7371e9d1e28801e8331d7ec2f56023b24928335c09c3b6fc18bfa0c53284515eab08c8c7a3d4023f4f6e9e166de6a3f4ff6b560d41cc7e88e0533ce7058ff68a279dcdc53b3e00dc74f8ee752c92ddb21911dcf08b71e9959accb7a63a7b3d6e639c1b05066904053d1c19eacbcc09b2193fadd72cd89db47e5159664318251e795878c42220015cd397375c3b7b00ce96531e06dc141186c626bb38c0bb6498f7cefad47ad602b160f99f32c7f17dd7271b8ad6f198b7412375e1e54bf9d1c4b426fe21d5560f60d72ba292fbf661ad5519cfa39e772a5dde7c74fed0a13f0c9333c2f4a93e09795e750ab86867de98890eba794720f7b13085e21c338fdc58bee55f78d73f03153a33673795e2ad0ba48477f8d13d92d7d6cc14a2d931d279ae37ccfaf70f0032951b3b292d69e0f54487c6f51635c1d07de2b08cce277e24da1c5d019e4f0c535243d42dc17d1c65a44f81c178355d6fedf7a5f22df8fc86278a4befbe2a0258682b41843a81e55c1e4c020a041c4c1395d5457585272f470f3d5ae086490f70931d493fccaf3489a6ee7b627db23f4fc33c7998ba2617025d223c2df18f0cd5ca1836bd813b976b31cbc3e0591ecba4664b2524d3ea468cc8d223b195320de0df9f478a2e7557da075670e0ac87ad7809cb5926f700d8696e2f6331bd170e327463529fe42f73a3ad5944757abce93b4cf43b4638ac00763515631d5d7f00c0c5253df893a1a910f402e07d98786675cdc66f5b93b54fb43ae783de7dc606a9334c3a7ebcf846b0a612a0d40aa60e2226013b60e49c8cfa11ae302815bf05dfbeedf0427e7abdbd250349685d6bd4a5b0acf15cd0f69c1be9e887f804740a874ba2e1fc3ea7c14715e0d06d14a7c0e65e5fdd2bff021735d9f05efcf5590d9c5a51346d75d8b83455d08206bd88ab46f522be0e2cb7f25b6f7241e2163a0fe78561b4293752b78d841bf0ea50369d2a78944624fb91e861281728b1d0e50f9020146e3bca44ebb2739108f853f81bc180a3ada77271db5744c45dcd14be0eda5c540ea1beb04da2eb804586758bba5da94b7aae54b65c13a534375add1f0a34408843076abbb3441683633c4b7a952adf4f2ba65cfbefb1de3905b16f8b0c65312b2cd8a9eb90417dfbf9831df173d05b86ca6a6e483764380857645c0b0710f4276c5bc348732104bf6da1a25935b880f275216dcdc1b2d3081386fab30d364cb6efec26ccc7797d7af7f962d72e3eaa847d6a0d7e8c09a272c96da0a908d01741517294377e79d84f4eb5e55940a32293316580e4ef397f9394bb061400842010b116e26baddd26ec6244b44bd1c03fd7d82856979d3f421404548acf042c56b69776a0bd6cfd3d3ddd6b134f8cdce59b99206824df839b4807ca0f9f0942ea819f55f82feb4addc3179f4bdf6664763cffaccea10179a7e16636394904a2a6d14d99ce4294bc0f469027fbe3ce9519fae58486c20c600d17b8a8430900e14d9876e6e4fdcd4ba86bd7d4cba8968bd29a6257005625533dd3acb915bf07f89806eb6c32d30529ad1c08f4c498688cc09e8ae7820eb2b28bbaab1f03ca0694789edf6b0929904db81815f78d643ca1491cb7d30996877a353f656427640e6efdd4f374f745ad3ecf82f06bb0e2632e9e4d77bae98e18727eb80a0692ec0c2396969e752b861f6bf8651f2d42d253681c11fb489f8ac0b1c5085c7b15eb0587515441a245dbb697354584ff50c649f838a4227b55d672615e34fdc65723040b5adda4b3c8aa5adde3c362c2faa5a70e7df3b1afef0baeabf2754a8d6d5806a87bfeff717129e90e5951e8ff43b9eea62f6a990fccf54ad8c28925d0be3303be4afafa73351b47ff6bc3634cbd9834690fa5f897a0ab79eb336b5f6664a607af5b8181d8ae75f57f1eadd43b5881859e4e622412fdb9c2c2376d75fcb0e50d45a4c15fbecf00c66c3776ff3fc79065e75546b1f28419644e1f5a94c9be68007c817b255655246b3746d7dfa7b284c1846aafe4a7f430b86c63d90ee9712e0eb266ae7640e7ebbf4d9905e0bd3e7d8c8a211eb25875bed17ded948ccbc5a106b46c55ba67efdf9b25bbdc05624b5806b2546af87e9a0ffa209f16ab8919a003fdc28ad109e17fa742f5f14817ec6c7ca3150f7ece6c7a89714f0571988a81011de633e063d4c4b8269ed00e28716e3fcccc7a5992130bc72bcb07b06834090bc0ef37f361526ec63001fc46d3a47aeeb97584182b2b4ebe02f2dd95f95e2c09ac32a90db923451c5e78661c97375643ceaf7b9d63a72a5398c0b44fcd50070cf27d222408ea06d464dc8478e9e9ed50297dc83b98d7741f9252a431ede91c04c5dabdab53fa497216f5c1c7af4385bdcb25b32def0e996f4a809d7689b71b0252934cb68263ff792a655f6c963db6d0327a4b4144e0af121e25b6fcd7125b0b3847cbb28ce490ea0ac1978861d9dd593282569f8d3484754ceae6282128f40ba11437ccb9dcf3f976dc9520250f3b0d53292862f72f31cb815c029772402e0f7d8884046f865bd59687a6559660f9e3b0b808567d46619629b6b2de13a74200ec902f468ee657c1c31151c9e88bae55f3c9d1353f4adf30517f641db61d4007b84961f7cfe739fb133c0935231d745459dd458c042970d70cc277edf3ad2a00f5ec2a7fcc88406736256d725fc6a021c8139e2d4f8388c936cc7e6b2709f80169f82d45fff2cb287302418c99502c4aa8674a205988e80b2d1a321932cf3c095f50c4f5d8e6c098621f7118a14f44f6ba6cb8054ebf1ae9a8a397d76a8273056a5f0cc6563dbcc638b9122191d22607a1a52f99f1f593070124f52901a102013a73c7c6b43c1a034f7e76f3bc44425578180be98f5cbf1ef4686a84a620df014a738f89643d766ffd20d2d01a526c5fb784300fd161dc1f0ae8077e02fe28058e928bbf135c83b95fffaee5c669d4ee498fb435f74e0a27bb5cf542bd6a960f084df3f3d8a5b6362fb86d6fb983f6860c88b08f6e179d942a55f44e27bef20c9f8b06789a615a0fe4b841b7005385aa94ea32ba5e2dbecb0a5ff9df61131d05aea0fead6859e9e4dc2e3481e7e193ca7a1218b1e0ac889dbd44c18b3ccc6802936fe95c0c01e00a5b11085a25d54c649a3b8a2877781c38c720af8729349f0c0c2dc6b45ec7ed68c580ac3ce288b2be2104cd421eac39d6619325639fb2a00f27fbffdd908d857640c54245a614d56478f83aae5f25e2737e08b9254ad2f507450b3890f4103072bdc43bf03e3ab6fba7e49a4185eedf6a9cee5fd65c3a09090ced036e417d5e0458310178a4b88cc6f7a9f0a22298d1092fa0bb7ad021e7086924adcc264959538cca5c5c71d94cb1b962d3e1fccb0a89e98451beee6c210544e4692faee667e7e3e761742144a62fbb4f14eb9be71fd3cdbee2e83c588a0d07c457f7103f00c03f4451e02c4b88b666b1831ab7e83a76b9eeb30c13938102c91fc63375602992a3fb2df88154e59af6e46875b186b0eeaca1b69b1c255804928a33ab0e03932ce76b32009f1c72a8dff5d9be010e1b19aba387044374c7075d3009cd7e38d1dd1143ecf001907862f6e83a1e912e982a01f06dc44cfcd40743d4245c9eb23184aaf0ba952d0a260b9c45790d5513c3ac8e3cbb2560e55b03d1eeae3a6955254c5abe72db4960d2c282c31da46a7368a7b01dc9ddc9d6780d85e3d3e62640813d91f7f935e9feeff59cdae2eebf7052791312bea1207e730972c5e15a4f9743a1672b96f6750a519f3bdc72057f8834bcda215255f729f4038c0d57354e5f885c1e2f90ff182f1eb498a678743a0ea1149000ba43b2b393069ba66aae08a71ef321e35ae06c24af9e45921890b5a5f73fbd942db9c90ec4044d3b918a65989e535d1d3643f427a63669ec1e0a2669258dce6ba7f9231f6f0b83d9410c716893a1da682e0f7bbdfc78b37f61f00bb683137cd59d514f811d0a29e071e2a0fc5f0cd1094a8fdaf891ce467ede06e7921b6ba667319357a7370424621434535e2e663c163ab52bbf01671be1ae67a0c6b354106b2ab3a22a630baf1bd8a05ae8e7df83e3e2a8a68bfe6ff145285a9c8deccd3647b17193579d0cd3523fd447f676f1675ec48147c4d8dbdaf98110ab386f4778fb746ff511c50e8e88af6e290c5b5d574d2255e09045ac3ef9b394d60b2bfca18279a987b69d0b478e4d531efc61c8c8096cb993249e35e1d75cd538f080d3814595388ae5e509407a53d7b0d06cdc1148ebb5c9f066fce35e5bad9c1d9ab64dc6ffb7e6360b098f5d3708069c4b1a334b186e4c888621e5f220d51b484006f808b0b0b22c400c740b990a5702fc5bc0fa23062db235d27724c55ffaf5688ebd55eb9c77e86906e62573028be41a435c80bb1d900f81f0bf111d5f6edef6fa00fc7cb9b2b2bb08869940d16a6e4549c9c08b8add3a3bfddcfeb4e8d39ad0f3592031aa00ae94096dfc4c7e3a6e034c9df6c87a5f063ed426d383c115657db0cb01a25f01d2c10558e8bd26028e9d2746edf9dc0be5631e459f9a53a8384ce561454874bd13da0992471df72e8ec29c36329dc65c973f8a977efd8302069d568dd8ae832c94270f40c93523b6edcf12aa3b34b11484a7b0f31a434f109d0d2d12a459eb7bca4508794ed8757e43e534a84edc15716221f5a8001ab76a891ef345a3ac809c84c9003800aec0c40cba91526c984d9ca38a5b36a44708d7d0bd1eeeba056acbb8a30483e65469b26117575fe079429e3c9cfcb362d37325d63f04ff45d38c8f6c8b01d9fc81d249108c478f46993e42a40669306b7f4342e3bdedffa96254a05ec60046d4cd748200e41d8a33411b37ed3d8ee611b33cf55724d18348a46661df7000018f9f122960602c8cab5141b44797bb025dd37fb4581434299689a99d59770492c86b6e33cd48112d150b2ac246b9b0108d70b4269aebba456376ad937efa04a4a4307fb6236ec12414023d0b1cbe7c3f1967bc56eb252a1c8cb1fb96ce8d0164c1fd2cca7c0bc84886787a8fe212f286bd0c870029fd2bf4e2db9d1e22cc0f27a553a4d47a39af64e1000ecb7ee4b26ac8d8c435a6598763def68fa29f64024b698da2cea3f8af3ded9db5bced4e6fb78ba6c9c54c84239b116eeefc005000a36651d5d8f3f7c282f6f4307d30680b1215ca99261dad642db98c5f2d171d020357b443594de8d6202536b710a86e11b4f9a02fccc1f356584926f810116b0c155e1b6c65d5be30dfb26257b506526a8ee6553e2b795891968e5dffca3541011cf46aed64d9c70a945ee4178945e88c3812b72c959fe3cd7415ce6e3dbd1104f82f1983ed7777e359743e081f52b6fff8907b27361eadee8af3d09eb550400db589344b9b9d85ec877823335be0b1864752abd3fcfa1e6997a52c046efe42075127ad3f943dbecda9d2a85810f14f1f1b27186e29b34edea336ab1deabf8b04cc063e04ad5deab296bc4039702803ccd56a2cdf9a7031caf953c3af0bf28e009ea69039deb661693446ce7ae30c82260c3989b47075d141502363b2eab48b0f3a4c51598d37e628983118e394619dee9e6ce37a308af94a9b393eaac73c0803e94e512bdd51edb87988dfc1b8c0d76b6901489241488c01e6613269d759980137826493b63d78cdd473b3090123e1bdc31b33c8de1aafb5c007eea7482c8907940090646be21be09e2f23f287493d96fc046b6367639026c9d2c45ddc74620dc4e872a4738be83490590c37f40f4dd23d585130e2501e4552fe6f5e0040c006ff14c10beb2f1bddb9ddd01cf5a0077b41ae4a415c437920ea9deb8d6e9ea40c784bb5bab0c0abe537a5d0464a2c7e7f2d8fef09c5013cfa657c990168ffe00f2916ffacda05a58fb8db0b38f5a996e836767cca489a6bd6d95349da126e3b014c017e5eb56110d09e8d9cfeeb0a2e9ff3b44650540dfbe001882d38ef7351030abd1d0cc8a377d63e8d86f7b7e8436f61c94badc7e9e4d788ef267d094f120a95d7560ee4ee444f98916b1668264668de25266870bed133d4939bae7544830533a521a050f78b0680b68cf8f2b06e599a3b2e85a585e22af49d9f2f4e26b60e019487e16e183eed6b2d0cbbe2bfd76387357ba961bb86b76cc36177bf00d90f0e58ec98490ae636323a74fb99990dac815e4cfd293d826d8a6aa42d4ee07304367f7ef7b40b3a541d9b47ac0edaff4e33f8b58d39911e70000d93087fd3e4064f80f13bb91d0011d072e8058177b96b1a9389b92bda3244f53b34dfbc46690cbfecfc423fd53fab8bfd1cbb88dcb9efc175820762c8fd673e5386bc6ab8390d448d329d4ced5fcd1efe499f2d523032436d2b03b34f5abe42e91a3aa09552072e224fe2b50c2b1009a35b6c47795171882d0d112e72319acbe5394f0aca4105a24be068eefe211f437f5c6aea3a9457745bc4eb53bb5a4f5475f3d54050d50e1be10d3ce9ebc88c69e024b01cb6aeb89f6e6bcd66a2c0915b5207d4c6cf4d09d87c8e9755647ffe7fc50ed571eda1302b01b33a8efb2e29cd0f5427c99517038b4d39488558f1f73c4aec0c3adc5a5d41e9c77e6b224b3e52500be3e6ff8f003df0081c1143eb3a6b2b3cf78a7b058c2dae67a0d353862eec0c8fa3b3ec2401d1c20644a6c754b7c11b1ce08efa5cc87b173c0cc83f45f0ce4ef728c055c60d131248b05ae661b514204863bdbd05359990c2d3623aceb57ad181d85f83d9048e341035068f0e98709be6716055ca12498230217a91ede614b0cf60c5f1580c7afd2d538ed03407df42beb4bce4101f8aa2cb37bbb1d83f232f51dc9cddcf083ba892307fd939e3a5e440a17ee82a6db7ab96f292939e6a3089777f8b91ad04dbb390451761d813e8d35b4607d85ef2ff8a4568fee8c894166c0538337abf035458af8b0556d07d927042911baf710efed4a8bb10c4ee6d4214c5bee9b21a09f70c06fbfe538dca65aad268538cbccf2e09eb27872ab91ce0461074687b0b086896fd024a8ae9ee53d1ab45da4fc33270ecd6b0e28309478e0f4e55dd6f8c0ce253580f91f7c1eb942554bf23909b2f334bc3997cb19a26bcdbd7456f5e8803697d46e0125eead29d999cd52e3838db12403ef0c1850235c71853413735320bc3f0fe1defae27d4ac6a8de7e601470e55dbc56c9c5c2c26b2e54ec5aca5320ed18bedf929095b42fbce7bed0b0387b7c49f92cf57dca50f6d39567ff86a5c08c4383bd8a75c926b057101ee6cd40e687fba72220dce2114f4bb3d75b7ce810b279e24a2141676a115632ea534c4e9fc320a8e66004e42205163db5f0515a10d99dbfcabf89f789ee40019deaadac4a5e706b7be762975a806234bab982c42002fd78787dc2f624bf8bd1d91ffe3aca091975e818f8ffb006463f750e74961004237173d245c0ae9175cf731add67a5feb827babe243b51d0268db1a2bb426059e6c4d602deabea80989753f30d195301791f041eb6a29fc9e8d2a00d17503071635ba4c48ab159a76c2c680b8bd91a5b6d8d7a8099cc42005132a6357190e018e1a48e83bde883b3c62be1e372db53e5832b92c7031734f6f7b91f284aeea0ebfe3ccd199af6fb6aca9c4e643cda2285d8cab164853a2aef8bd925cc0cde902c21f53bd81b93f62040c48ad9f8b901c2104d7d67ae55d8876ac45bd234d4a05652a13f2b8970a0c175b9c5d2166efc998bd610c12f0488375776976544bce024f7b359b61661b7dfdcb3a3abe463280d5548c02f32a84b8719c5a21c4e3d305ded2acc244ba1747d3f8c677e60eeed22875e1e82daa0a21d9929ea0691d8006017996f0edcf29f7a3f24cc4b383462f0439dd0ffbdb6203081e583418639801cae39749f0a6426c844a8f8b85127c2af599a818a2badd155505edab625afc000b9f2e56ee3e59e15b22631e0a2e374f5535506b8ac193bff910119e5390cf0a30c22a5025b226e27c58cc4a4f0236756e27116a45ede431d5eb906305152b0d73be2885d848f00145054475e22b9ecab68083bdd0fd31b4f665fa3f7395800b4b16ee31989df5faf32fca3f63bb42b1d8278ab0fe34642f76e3aa5d9668620c0620c5707cd0d3226e930daa7150d17a497074446656c7b462b6777f159793005be6242790931e3cd9f8ee039fb061523790e70ba646d38d343662078a459608ae95fbe4eb60042bdbf52001b75b47d44ef4da834f05554b08efc8303605380eed811daa6224f9549896b1d49ea406dec730b9f09d3d938948ed68c4f7dcfa0cd1f82e7e390195d6b98558047267c31512debdb1753d6f2fea0331589314d5030d64114a03817b062dab6f53c747a2f23cbc2bdad266eafb7f36307497949308d2e16a43c3d1e3231464e76cfe3dfb6d5e1351530abfd9438d22e7e167550b0a2d3d386d6ad2dd40f810d0e0b9735989e487281f150f6cbbcc5b322e3de1cb0110037741a475e293571e9a36c9f575e02ac637f13be479973ac8a4c2fa62c10aa471a7eb5b2d4cfe81728b4aabf2d85937fd0c2ceb6b2e45c27e8142be5772054d763e8176e6f7d088d41bcf186ecd1db555feca976b2b247953580606ee6f0a77eee14e2e4717a95f33a65bef223d1e51ce5fd96ee51b41669ddd15fa501402b7cbe1aa940a35690dc16fcc1fbc9f3471d606be84c0b0e1c7081d20a59bf00e3443ded42592ec3d2ab9b6f75fbe6ef5abb032566e85d2ab733cf5b25e9d3e04c0609a481ff17d2b4721ee660a47ffa4a0217b42c313cca26e863be57d5b760794f7e303927fe2e2b70bb2a1b716c43f2afb0399e9f4f638c8fb7d9078b97305ecf533234dc5eb74ca151e7fc061aad07c67ba3f4b741fa52c2e1f75de2c300974964cb4f491d00cc7cbf16a7c3585608566c4cc02e91ecae6dc7201a5acb50dfc4771c03aee31994883fc10ba6295e737f89b2459ee6c4ab6541c7c08ec09070fb6328182e3eb8e1d8f84a5cb994cfdc1daecd40be6b1b15a2991d1762f2b0da1d4df817b104a7d64453ae09dd439dfc89ca72b123b98bc752d2e9f31714300c94b96e4070b42cd5ba5c6989ef0392565aca5e98923c0ff91797905cbe4fc0a545faa30f5146bded118a3aca9a561bacc154d59d1aea5dd0aa7a957db137b0834d3ba10fc28b71043a35c30b13ebdb14da7f837da83dc5d09e903c1e8d67709b15b6981004c77c3f7ac75c7122883c33b6583e0ca3d095db58d22eaaa8efb00fe112a4bb777c3471baccfd87d86a65fe344cc9e5d2fb8b95d717f09bb62c8035253232b1da51a2dd820dcb8976eb828fc937f7584faf047eef21932ba939b0e4858aabc07209797c633e16c0010579b698ca6d80d0274434856fd87129774012f33a82bee667ec505d9986be2a33dc37f296e88ab5b6e992d46b3ec93c0770d8b3c579f3f90eba730eadc7dc31e35d00071f69c4c32305e362fb091052b9b0f9b588f9ff20ec9b6e019ba0690492f25e5912f0312cf8f7c2e44f51104cea0029b8d7b34eb5f7e58ba5e8d9d1529f1cc8d8fd1a5622826ef59567e1e396e61032a50116958107ef732d9456ac7428fe0a6c9ecad854426ca59657dd0aa87ba0ee2598fd49601c3d173602bfa2dbe601bb5cda9bd02dc278559fa8790974a4c0cf9bb4f55425c8ed46eaf74d845b4f52cfc583c48b99a58d41e8f0bd15a59a30d6b01f6fda550cd74c078677d4051d2cc81c03a67af645c923a433242091db70b599e170b68cba3bc47799faec42f303d8ee26213608bf960c36c488c89d32d061b62d13ed036a33186b0faae98efc44a30a3cdc68a70463a4d5081ec34e4a3060ed2a6d1d2f43205de9f5a732481e6bcd42448709a4dcd6a549e85afb4e78c02076c74018abbc0142fe39449befffb020aa48fdcca83030026d100cc8697600e37fb75ddbea7e23fe5ce25da2a437a1030101d0774a7464e5be90e9775230c0b2df9caf68b9755215343635fb2fb0ce3c764765d60156968d92a15acad04360540f65a62cf2c664fadaa5eec1a4e177ff4b168dd48a401b52409357bb07bfa066a590b69a06d39201cb7bb8f5cc640cdf123119fa5961b0d769426b5c15367065b785f4efbf811867cae37c98a54e74f2e5eba31fd6b8990ef75657292eb46034352e8904792efb6078e96577e9f7443e6a6a6ea167df49db97d6b315f1dbe0ddee147f2693deeb3216226d9de9c2bc72da0b644715da3f0f324fbbbb82ac60e433715acff2121eb4fbcb2a6ebb06b5effad3dcdd9a0098545d1d56b69bb7f0de62dc6f627055867d274c91b9a518fbbafa3f6e65f13d781086263160f23b304193e1b192fcc6c0b624aa933b58bbfbf0f3261dec94392442cdf828713358a021050135afe13bd53151942cb01e484c5da0d5cf8eca9f2403570309da4c1ee0f23585524a6cc98a476b07bfef760222db5c87597d38fcee8eccaa9513bed7a02bbac500ed6825cededb10a9ed6fa3c040f2cb4d21b1072485a032a08f0e601073cf58a6cc4d3a9280a245acbda7675a74522f465eb884a72244048cf2b83500a0fa2b21069554ca226ea6906ff0c34c548bc86d0f141c0048453c63624ee7e0c95448a53efcd4fd16cffc87db6f3c0f3cc7fc68fc8dd11846cb9b039ad9f290b5e75dd73447f42538d1dc78ecbab94d2cf19c424f86bcdfe5ec421958f940a0517bc2a627a3344055f55ec3e365c61dd3a492042d14c2b6c676f2c6473d0660c490b1093e69542b97dbc75e5542513f3f19045aa7dacaa0d2fa89e49d1a1a200196a639d21b5b16add143198abfc19b776d00d94fbde0fea8322ba5d4b2d560c99d7d40580cbe3977977630e35bf056fe9e64a6e80df3d9ab5c70096e90c490de47b0e8a0368946c8e2bb8ff2a8b1ce1ad5151946b29f06e1d730ae15eefdc014b8265ef1c0838cea137f36844ae394dfef5bb1034e0ae36471fee12845c5509089bdead11d53e82da74ec28befee237918e24a46a7a5f1beaec8cb84ddcdf09b004017e9f451fcf906cadc887c4a803b1e50811842c3fe5fbff99374b050209b72c9d1e2efc3b4484bf5ec7c176934fd3d98e23afce94300598943f1a8f400416ee4784c2fc2b461813de15074120a6299c91d61ffd7641b57461ccace8460ec2205d76e085dee4cd5ff2169c62debc780e925428ad1097a21491a228bdd4057324d4824801dadedd0b9f99be05888e45acf251da7ba0f00379380f620fe20b1cffe1a534a7e4fd2ec9fe3fb26b8c2827fd2495eb96d6ae72634eef44974b0e2d0daa7bcccb93ea873b98d9a93bbbc7d439ccb842c1949af62e98eb92f6750ebded88ea7a001ce2d3bd24cfa39d995868471b0a0ef446e27527043badde56037556283734eb37b5c0da9ca56f03aae44317930c458e07cc7d06c0362da45000d653a09a5b00678c41c0dec03a3a29ad439b8fc7ebe24ad1ba074b358d6d2702e2e1855c3af9e1531adaef5456f5cb96a2599873a84254614a80f7e3cb24ea099f61473aead806e04fe50445624cca42520b6f84c287aca7536351c806f3f9017adae45262e88cfc9ad5f3969885e333d8e9ca4466f25d854dae6abdb87a290951fefc536cf46e7dd0571b7b4f959bc5f98a848f6cefc367bfbdba541a1bc003 true +check_ring_signature 8339e9d015101ffb3ad0a2dd05540ab47d9dc1c2cf0aaa974ba3c175075d742b e7bfa2a6ea7ce3b46970779623c593087b8cdf5d13b1a00de58a300f06eabea5 5 3be6c7d7a1553489ca46644ca7dcb27070a14693f8cd1f1ce73f3dac57277de0 f7d92f51bd8d4bd24f7a3c6a8f8b2a2cbfbe68c75113bffc9a606041beeb0330 84144f6c329755af8880164ca35ef4f0b02139c94b20982617b93551d3343679 adea0305723807c521d696a0f68de9a925c9c8038e7771e39bac8bf7e3211bda 9574e9d7130cf8a8d7251a1a873028d988899ac3e4fc9fd0a7a0c36ea2722584 8a5c43ffc870b1af004e3fc14781ef4aff466f79a580b1a58d22d7fc8239e00d523e12fa83932c71de2538966770ed99a4a6a9bc90123b96b6df01cacc177c05eaacca24edbc73b2f257c3a2c15a985d18275876281d7e1519cc2f5973005b0413594b1d93833b7278b0c1d0e4538537ee17c4c247fba92377ddb2512790810f342b72df6727253791c89f5f4281247e256085c5e7700c220e5ae818cca69d087aaadf4effddb43c7297bbc68994287e325f95b2f53dd5c69d19e38cabc99a0a83572c947f0d86016abc003440f42a1c9c0e33c907212082f65d8e71bbe302064a4cc9ec32f5db95b423f44acb226f082fe4b769429c3476c635bc9a5c3efb0c42c381552d641f11cb9d0fe682040d7a759e69bfe38b99d957c63b9255bc1f0cce9a282eb57c918c49e0709693473ddb59198555451d17807f46e93e1b243c02 true +check_ring_signature 9d10ee26e161fc37177e655309b90a8b17bfbb9e36db130bcdbdb532c6f6ccda 1928bb86e926db9ffd0f5a3532c1e00700145d9bf1181a39f48324d24d0bce53 33 b9ebfb79e488c2055b7314d0c37d3ac96257673d67d6459ccf9bb2536742fca0 44e33460c2f92244708db4da7b5300b64ef33c56c9c240bfdd185c165dafd1df 92caa774a3776ffd307456573ee17a2b6a5e3363ac493ce9c9505da1b8128400 cb21a763b0d55851041520d041513642eb2654e45a68d8684b5b7e0748dda2b6 6a35b5368187a3b64e784daa2d36dbaf5d60479d9c8a47f4efba46ac4a82902a 61d961555a0cc432990345142d136355b898ae1fb0c2b87477b667ca27c822ab d5c19749db9ebc054799a9c0776f078cf580d83ab42a766bce865b7872bba870 6ff9fba98ec477121c51ddcd245e304abc5ba5074328075b214650a366735a55 be757884cb170b2add5f0d5ec7c550f72e4f76e338790eb7055c066b668aa22f 227a01b5ba94d21bf7c65d46eae090001a00c0b820a14df98b5297a91215a260 c61f3ca1ee7cb938beb3d8d7e10001831e1b61318b6ee046334775a8627c396c 4efd5f4371127c0bf9e9cb71dc10099d3555ceac71d51c80a390c99cf710312f 831476b0b1a8a4c5cad965c730934444f4e76015cdb7b5da6915974f2ba6c5c8 8c3693f30ec719d97f6b89bfdcc1a7feca3cdb724e8c344a70cbfb065e8f2ef9 9f6b20046abb780d43d87547afdb842d2189d82a0b58d991545fce45b571a193 904e418f24260d98d6a13b77c0705adff9b98e84a088866c360b847d0013c5ad 85fbc4d97d5c610936e7e9d8eb665d26005fcd6e056a8e3bb73ca59aa90502ad 568334a8cd769531ec27511c2cb2d4a9dab25b8575c75e3d09476bdb05097a5f 2faba4a1ffc496f6c2db34b043b1a3eb0da5c588bc93de75224f2b73dedbaeb5 749cfc3ae6be70de0942421f12101bfe2a1c9ef85e71cdbecdaf76736762f893 ab39cfdd75d57de36b9200816fece1ff2fcc02f2ac32010468e78059cdcbdef1 bf39035917624b8df967cb0c7c8ba1f2ddb8178e1fb67a7302f159aae065a713 2c04f738400d505a80c2d6d34ce19b2717aa5b0ffa907d200db8d0ba8527035a 9bd216c0b2b96e25481c0f6c43b15921550c69943bc52c14d23715a560eec96e 2b7a8fdcce2f126e5db0ca0fa39147e3bd32719b240fdf2b76b505c49266217e 2c4a34971fd1266f7e9816093335dc6c88f1af015c4ce8efb41bf1005ce1b04a 2ab74116d0fdbbe874d874d2b20e784fbf58ef648935bd9520cf12037350dc71 e6a7b5a05cd8fc61c320597e48b7c2c6a83938427ac56f1f284959d7ff90ef56 1aca3ca90b6236995c02651bcd60f643800cca89ed609d960787ad53471f38a7 7b34d704a44dad85e49ced8a2d9ecce027650b3da3642f22782c640466ad786a deb3f2f492a610c204b7493c07701f3c06055a0e3f6c5e53f8316a6fa83ed732 3e0de276a61664ddc38511b367fa685e16147f297c59e480ca516ac56663423b ae126fcf8ac80e4a4c8b5d94fd6bd0bc1312a1adab0ccf588ed4a60e6410dec3 c9b5dcf8ed52d7971e7fbed7d513bc3460c69f69311dc336c51e6685da91820557a03c7d57fd58c8698cf49d7bdf297da3e4b6e5814086dc382df4f71253570cc9cdcf9a348bf7c67e5d7ce13908e4efe5337c58ed5ad08623822cac76be1b06b4ca19408e6fe6e2297d4c50e7a281f41123c3f0cf363fd2d4c5f7f6f5727408b64cba348325ca08ae15f49e3803dd57557082b212db2b7354d2a02da7e5fc087f83fbae4f1a34e4b8315b136cd7ffb23f056e1819798be6a3e6f23bd11da60f04809bd1c667ddb6e29e7b4a1413134332cf485dd931cf1bdee0a2827f7a400b52b2a0d79a6c79541e242c4a0bf7d373d44ce708b339f6449406372b1e7ad1023723e50fc48300e5808a98c1f74542361e7e3f61c4d6f0ceae17a86c8dc73f0ed6d1432ae5b56b7794876f21ca18301f5d8622140b5aa4ed7b27ddd248436f0b48a05eb48400dc02eefd0bc51bd29312dfab1b91580130438cef0c2a06308703a88c0e8b4a991ce68b095c528b205e8cd2abcc0735bea93a1830a04f270cf702308a17f6bfb0e55339037511648c53190d7e50bd4ada87622111fb7366fbf50a99e34a16b123c16bd1ec286ba27fc50a2ede3fbacda130cda8e43c1b9a79330af96d74b4956e45a0222f23aa9a2eb11b2a959ba45f89ccc9ff8a47d5fdf0eb08d986ad77b9b257c9ba2b19625860b17acc9b1f80bfc60a008928cff46171c00e4172220b040ee526377ff43a3ae6db215dfcd863b2bc166fd47e1a55c48f6e0bf12cb4e9d8a26c2d6c789d3947060569beeeb57902646bc86d845ef27ee29b097f11abb91d9f12432ebe3714524a315679847e559539ccdd4d9a6556d2b46c07481bb890258ca3706093c0ce5ee0486c35c937ea71bd0eebfc7c5acba092a606a8acc4067eb8843134c92f56a7579fe492135b14d1f6c4276002b8fb830af90bd5909aa3dd2f03d1981480a79855cfc1034fcc9220cc663d156537ad62a6e60297a906c9aaec29e468df3eb9fb8c4e09776b61aa879adfe2dd3e2ae73a9d1001dfb0128a1ce7ef2035f7f6241c8ff7dbea8fbdd77f573e9babe9804612d9210ade99d81ae9160083240790c98c64aa51eb6d74c2d31521eca2a5b65d69717f0ce8102fbabccd1b239e1eae763db2e0fd69231adcd9bbf51404a3d4bad3243b05fa5f3896172b37e515ea443b5623f7867a2288dc4ab6bb15b3b43c6e4637d200e0eca6aae1e57844cba06e0920cef4bf46f77f319167594e4ce4b116da64960cf14352e57c2223c16a043eae2c1d0780478c776d731039c405d62960184a72090630731e76c01f41233b205fc30b4b0df3c24df0a87d05739840e3697e6e950ec6745743cfc861a82002a8632d55ab64b52a3b91d4c9dcfaac06e54a94e6bc0bce219e9c132324afeb0773aeed7af7662fc9ead6189c8ecc160a49d3c2a0e60cb027da777411e3fbeeda985704370375374e581b3082c989af6b7f6139e2170309a3515022f1ba9acf3603456f5175f361434beddf99295d50e47912b91b9c055653cb82e315e34914a79dcc006fd1b000c38911a2bced02a03d94ad9350380438c233e09a6c977fb546943fbf3c2ba8b9bd223e54e528769e1044e26cc17607d7345bf281d20e69dad938111f8d1b20cacf20fe9f88db8a2bc581a2decd800fcfaff9dcc20520028552303e0c2c51549f1e5c6da04413af76ae934f93168d0f4a3984e07a9bb5aa1c1be519a80bdb6acc558df6bce555b13815e34466cee30846971c0f4d6f080ae6c91047147459d5fb38ea9406aad912fba5e0a8d6f2570039cf122932dd1a10dd036922d3af9236ce5c65b3a86221000776f7a924a6330653ab150e02e534db5ea4fd3cd1b29b493b8c7f435e97c0eb610ec263443cb10a276b7959c6f32656456da440a7a5c3f23dc78ff992211d976f8740093648a3048daccb92e74a5f2840b17501fa2c628e62275a00d9b032587d66c1d561529105cb6dfa31d13714e5f51cb7be0293ddf7a9467b6a38f18d0951aa14de4b223c0fb06a330444430febae97786be2872c14a06a660611ac9afa335444c9308c850da8c242890de6ef178a6c9525da14ecddb8e4809e4f81d2966ec795232990ed090278803235aaee5acd3dff926d938c32118f297696a4f32047ee85829c027f0d9615232bfc6e29a123c36ecf6abe03518570500338b5698c82e5ab4e1a16680d82ca7236cb98f3c7754227e59c600aaa6fe4fe61c9e8986f23485a2a3a20b70598294514132c77aa6a47c8854759aa01cad706fb868a6c627197ff6967d9fc0e0effc84113157fb6308da41ccb4a985be5571141c530fca65f57c24afa5fd4052b0ef551dee835024ea577584e0c2ff835ed4adea7423baeb1c88fede3e096041a5bd4480126db328ba369d6807737a2bd9147c01db237fab97c7879f8ecc808c81d6755e0ec35767b516d22d6c5a68478839ea56604ac7bbd9af8ad1e45a2091e8ac1718368a8e74dcddab2e24b6ca772a07c1d608def7923f3206f2bbb500b4f6fe2869956f7deb3ff94fb62e58d5c0222a5acbf474b91ed88907457945406c100cf9534b771122995a23e9a49806bb4f28dbe189c4570538eb7eab242dd9269cc53dff298f7042ea4b13d4db586795666f00ca404265d79a3d005104de40faeda09ce1ac8aca1821aca4df58d49601f070d98eb64a1bd27a763137f171401ad59d89dcba338f350cac5e2b50c7c8b9674c5719242632c7c3afb11c542d8363507d097323364e48c23bbf40a40d2cbf7b18187d611fc6616656997ecc5b7065995d6ab50724b96b8eb1f4b25e2e2bee36553c9afb8a8000f22c50ac8f50b065e672fc14f70889bfc5cf4e0a7328fc7febe3a94829bfa858ba128843c63a502c005c755afe6da3be2c45d1053db309156aaf27a53f9865309cfd6912b6f8f0fd7d8abcd246441e9a2bcb75ba267b9e2c5382b2eea86394277c9ced2076ec900 false +check_ring_signature 07f7cecf8dd41dfe469680038e8bbdd783fd980117a3e978788f5fe41d29882a d7ab32406c00cc3aca38261e78d62e0b56a0562567254b310349253d595e88c2 101 1fcbed84d8d2bd3b62b6fb3b03161aa153bcf5d6faeadedca22d5f255ebab0a9 308089e68e37be671a1f54b0d9b82f3692a60fbccdd0f66d399a71262b9b2052 eb319c509115bb59036eb44fe56884e90b89f1518a98adb5585b7a705c2aaa0b 084dcae75887a4519d3b30ec07d6c4cc6d67bc05382684acc9cc5dc4fcf0c077 e983150ae9ec32e512574d6f56bd76a3d12a8a7d35768177cff44fcdca1ce22d 743a10cb292c5e25550dee2cf5f9cceab02d8a06e6c773f218a02df9b3e53342 6cea3892d9d3a946590317ebd3cfc1ed4728b5636e01e71bca531320fa76aa85 85e1e7b5de04391a7bc8bd5eb16a26d909b77b4ec36997c4be4ab24f797c0040 2101f21ed4e10c80669ab9ac894514006d7bc4c42863da3ccd9ac09fa3a0c163 50ff05d91b47ed47d2fb89b852736ee3ae0f3a735399e5ede233db4857b6021a 3f2fcc9ac3b65c5902cb6853526829617c03243e995a57c6292c50367158ec49 ee78165f1874ced133859844f6afac954f50b7d45f42ff55bdf6827b432cefe5 902a370b18a8039c003124a9ba161c6c9f589226fb442298bf513a2fcff19cda 7cb790dd90f8eb81df201fd5908164d3ac9dd026fad4a69eb96df6061cf68cf7 2b9d5ed42f62540c8224e1bba408d2dfd05fa5d8fa4118d4817cbe8c243fb2cc a54efbdf173970f05c626e1eee2d5e82121eaf6b4379af0269a6b58382e7e6b2 85bba49a9b8d177d44687bece4b70fec5ad54a6097a40fb9f7069b49b0d2c352 46f5c0f2c616a1d3813c0d05bc15b40adc9b7ff0a7142b3f664d44d6604907cf be138d644b0dc5765c9e5d01cbadd182357293af5d9adc39362cc0fd968b3fcd 7a28112e34ec52663726a5a4c2aa1129cb8ba2d1d015ecccf2b26fdda7d3b539 ae48bb951d20b9884eef64fc9af15d45db21bb1e32e77131981af63490e5f489 2818f001c2c0fe02b52fe195798b3afdce489b57b8ff1af72301fe55e5e42004 958c19d2bd30a6b09f94dac9d3daeca54b26162ce97fe7c63eaa9c99c001672c 6ce2b602a609cf704d5af1f32709b81c05fa2c0663eec6400710c61d8d47e472 659dd723434e0fd278da0f497d6c63e9f150e1e9a1fa104b09813d3f6c68d354 667bb74e6eaa46d96c9714c9ef48ddbd394d58cff477c392cdf56bb882b501ba 87e96ff5ed7950d5bcf3d090fd7ed247e639dc5e4b6125014144ae57c82040e5 a02ed4bbd8e46619e5a0a0768c16beb106ee455bcc02df8b55216a359a953279 0e04f6d7296f521ed1dbb41af0fda1257fee7fae2c374bd74f22caa6d0ebd560 48e3952c278fc0d43bf11211a860056a1ac79a0f1834c3c0e4af7e6448c7bcc3 305cf7d155df9ddbcc89b4811a0929868a9f4bfdbbeb743b8dc6e22376cc4759 8af41289c2080b33c7fa4d32496d15fa93086783098e37437ec620da0c182b76 dd0575855a8012d59ed84b23fda8bfac6c16f1da0a486f84fc2b3776577b9d97 30df00636cad6759dd72a1d0227ab1370e1eabf232b3247c0a05b353df6c0354 75f0d847bb06b986c55f62755f5c6310dffa1596eb44c39a2013885809e4320c 9a07fc1ed3df84871167882f72449d7c26ce5e716420a18bf4b0d50a1cb9ce22 a6222b0b8469876d066f418aaa5bc155ac0c2eeb7611107082750a1147beedfa 3233b99e99c6df9bc51959aaef79eff455aec9e02ff05b133349c36307e75328 7108a22c4831d93f142ff74b473a435a83b165e9635f0d779ab7fb1feea69500 d71b335dc0e9669686964b66f18efe6def6c326713f4d42b82eb18ab5f461fef 17728bc90d45b78d3800f26c7174773be688fe70ea4b329d04aaa36cbd50a9ec ba2e305762d814e92d45c11cd24ee6963e7ecc5f2bf5ec6abc1f3a08ee264533 49fff3d291f0c3acad5a3513ea44268041f3bc061cd8c3b1310ae098307add67 5644d27226f406d09a1122aaeb5347be5de4c7d05b5e023355474fe955b9f054 489828106daff4e24526ed942f337aa3ca858bbc8408553a9f404099e23c827a 5bbd6957356653bc81c84a217ac7f6cad369bdc333cb34c70f9cd2136c55bdb9 fed632dd78614493cbbb536792c882748028ba1dea7c81fde8af042536ee0fe8 a089cec264bcddfb1d07e5835687b7c11bf587e88585635246cac5da0339474c 72f88fab3cd7df13332bd28f2f4e5cea28673ee275bc3257ce251fadfa731b80 869dba835e63df18f99efff0614627b7126f23d45fe3f5b9bb6cbefdee425810 1765b26cee41451ae4cfeeea5729bd8b5952122304cd4e281025b2617eabff20 ed356036ca93c0ebc5f492dd06284277d281040a0054320a201eb4d59ef47152 5e001374fb22450713e4d04a535fa40ab10bfad07389eaf50430bf3e85bf08d5 545a78c8c5d3af05fcd796a88e4c7c5fc29bc53fc676694c2966010750343ddb 01b3dae74e472c32c73d2b8bb1e53405d6e2fb4c234abb1f8073a99f09c467db ef382f61508105e4f6af198c77dcef1b8d3baa00e71922c611ee612a08765369 e66252c92a16448d55f80bb09b9795e6b547a9a4ecdc60f2a8ec5b1cec673b3a 1fe34e6380ed6b3858b79f36e62f295695bd19df245c3d210b3dbd3e3cd9516f d5649e5d943ca5c32b1e2922307d37a3fc00eb21ca63b8561da6918f77ccf2d0 11ea50548ac970cfcaae58a83b663825535a836e33dbc94d57b215bd3c5c8aea 277cb7e74149044af2ea35d51af4359a882aa8e49d5db2ee2ac916604ab0879e a82c399707b5de9de88d508f86a7e9958f8d6536a7415e4210b7ff2dd33fa580 7881c8114870f3fdbff5297cd0a48d67e67000681232393906f8f73f8e050566 4db21ed07a2d4fdd890043522bf13081a0c36161b2d729043ee8fa6ff8e923c1 1e2e5194e242ba25779bba8b8732ed213133ecd3c79e7385c8b14d921283e671 b659f0e561b30ef62dd6ab91ee1af7fa1361d4fa12c9577e1e02dbef1477b2bd 89e9e6e4310b655f803135816b32cedcd454461c4a988110d63b7d93d6bbd271 d198771144505810b80dfb029ed973423ccf53e67c4b484dea6f12e24cc78ab6 5e1eac8284ae37d6e99c02976b3875f09b3d1480ce3c653ddd20935ede0f1c23 d053c173dc8859f2a1c671df2f3a56638c5421e354d3bf1d6fe19273f1fa538f 886de75200c92f2b52e06fe7304d43dc78d3d5ade17a31ab2209e2f39a625813 bcf636e50b7fe5fa9791a5e014dadf97d3ef0ea6410de1180bc9ad987294efdc 584820a8088ef4a98ec882b8e0b400f06b861573808e1dd63919e6c43006c72a ce63188150dec309d67caa522075b70da3878e15096d931b111e599b05240c8c a123190003c27f8dfc59485430ff293318530a9508961561caf042746dfce8d6 40f79a1b84a641d60772fe697751790b51e46bf85eb18780db324514d4270aef 7f83437faae4d05b44d1f555498480b96d12c18b468db1abbd407bb5a034b3b7 227d58303f6233ef326703afcbc8a9e8266a4b8a83e82a34f2338ae05e3641fb 7b9e7ac62a5ca973ef2a5b94d837a6f56a5104064f3ea9f4bec94a04984a905f e04f75df06d5fd0eb0bdf4f7c92fe805b4a8cf582ff4276e1b987e4f028ac2f1 1895e532332f635feb548fb511a5cfbe46a85e852dfc33e510a879db4da05e16 8e36511ac16c90f241201a3f900353929c1b9c785540b8f358d6d5f0ca602d9e b36d72725d45ace83f384b903ba585113bc6e692626b70c12188e138c9fe0475 785a3c19d34c96d10f037d5e32dd0568e2972fc9ecea4fa4e60c6b0ff930cdbe e9288af6563dd66b7969000cc1ff0a47812e7cf6c1c64d33201c190976e5b14b 020b9062ecd94f8f7a0a8c3fc847e3f67664de3ee6f291e68cf689b82f44dbde b839075344588cc021c1758e5c446f861917b29c2405f3cf128f7488acc31db1 9089fc1555c8fbebe50208c67f6368798550eb1d49e93bedba8dd9ac50fa5f56 4d13977c44289e5c4f344643bd46db55be811880f061fb97f50480e8f0bd9d25 371b018fa531419cb075783103781a94ba86d08d18d2a2ecdbe523c052a6af29 e1f375fde14e0e2fad052da76ca0280bbdd5e904be4be27babffa57de16cfe59 ca085ea5ae8ecc6d4bb0042745079dfb12e822c63cdd35ef6d369cb84f4c46a6 ca1ee84423d9857df8f2bb10d024249ce55e9f6413358e5ac99e22ba0dd619c2 c0e0e97bf42765174cb89a1ba436ee485d42ec6a3b2877ba4a966d782d54b479 9b547927af0b67d97ba956689267e7bfaedde7962f7df1fac8f1c5bc0bb6d294 14bd57b9ce4f0bf2a8e2043d7832957ff2405d25a3e2bf6adb91ab47b9c84d40 4e45ec385190f5ab2a290f1f65fc3c0a1e636d998ca5bf341d65a47c337ea695 e4fb5408aa2d5a35941ec646debbba86e3ba9058851f6b06ec03a46e241383d7 e36d9628c32974a2e1875a4a00df66483ae10b3293799a97ef1725a71c16db87 b3f0aecaa27dd712c9fb67ca72a946b0ece4d916af99ca2851f5ff6671c4639c 2446a6d4e93ca0f5f37ce0da01a4f7c9b781c0e30d2dc600bc46008b7dff9452 2eea1cb5f287acf47024192058286d5c08259889207b9affba92109c82dadf0e9f8ae6720b99854da58c1be6a707b674c6f1b35fdbe86ec37c1bcba9b855840476baa7a5e854d61b70ed2195060e504e6fa0e8e1bea8bad39bd4705fa4285f0eef46bd02e446ae5699c6d14fd0cbdf02dac0beaad6fa06eec610c408df488505e9f9e44c53e266591804b9761643b5b8929d71826dd91af3e086be42d7304f02278ac702fee483a604b0a9dc5ecc1043aec1b7e486e85aa6e2639344d2cff609a3b31364e43d47abc91f4b8408dd9cd809fb5233626a8737ca9d386c5707650a931df0a12c5792bac8d50ffb8991c5a7c90517a522c53c324ab5568a01e4ee0abbb625826ea2a0710d18099de140c3d771937b9928362bbe3af22a824482f70ef8b6a36195442146b58e0754901e11ca271e169c159ef412fdff39ef962db30b57374a4c33d7918993ba88849a53c17885d5855de1735381da84f397c094a1066af10b970cf9fffdfec50616851c411e841fe99fd9f0deb72061953e199ee909c1b510de29f7117725a7002b64c9800cdf099a571c5575b4262e25c2e59de303e2cae3638a992057b510a3e149ad0c0f638c1470f014b67ac5f662ab60401b00c88d9444416546586cc9e55503c8821b631e37abc63b4df839c0fba3c40f4c0fcca2cfbbcb294ffb57ad0ff5a6e526d6ef3331927fd5d58feaafe9f119ce2300e5fc5256f5f921db0c985ed71995b4249661bfdbfc13850c1377c5b1d0638b0619eba43fb65225c148963797aefdaaf57cedc65638aabe3baa72b6c6330cac0582e564aecdef8069c64a3d3e5193f89ef078239d6e4dbe5323665b8fdcee71051dc936a54682f96a1e3429fb10f533b01020448cc3daa22cef3a78924b4b290ccb21faaf5a779832574e2d3c92f61b5066f1690f6d96ee8be03f46f5856412091bb37186b4f54faa49daeda9292008f90a25abe26187770cf79fcd46464d2803d6a54fcdb1ced6d06de8a7e2b1c31d819a92122b8f657d9b116bfa5f679f340d54a4769cae62d5132b5af617d5e1c11e435c02d1bf3fd83e1147e966c7ab1208a00c13e29ea8961057f14c5cf18ce1663c379a1db98422746321e70433794b0f4ef8f6a7bee9e8e04283f3bfd28fdf91624b119ed15149e0bf201187eddb9b09936d59d77ea0a1cf65025f203de3bbd033cc1f6b3fdf622b0f65e326b8448809d7497de427928ce772e3d5be30df487e6e6f9e918eb4404b8ad5e80f8ab1e60002397908bc3ebfbc66aa88755578a9994ffca86b21bca9dc820456c87238b50cb9e85c6b013abf273b06274b058ed6f5a192afc7596ae31750c1e96e030b4c00574f4b5ebe36f09c7d983efe49584288095abb259ac0d34a8af0714be6b2c4071fecd2d83db0f548e646802500f63a25efa4f124abf5774d8b7ad42ed72ed80b67ed0f5cafd4272cdb175204c24767a6766701b33a4dcabaf72d4f745d82fb08f70e27abb6b9865357313ad26d60809305b79a6bf0f00e9c622d133dbbfe59019254d1bd9a59bb8c61f06eedd07f8987424ffbd9feeadc0fc8aa021c9bb54b0bcf295bda73dff866fbda8761179a81496f03a0e4a16fc1d5efa8f7f7ef6fb50ed3d50a44af9a590e9fdfe6ecef495a1926482d089084d5ae4d7b44632122d30235c44dc4b7a9ce6be98944aa46d794087e585b0df6b8d62bc64a1c6fac54c603b2e9d8eb5b421fea19ea8f3235a84c32fe92395e13c1029907487b9033b9480b3c0fa61e3160d8ce578083745744cdad4c25edaf5cf43b44075f64fe95cb9d02fe655cda5eeef19e0ed4e2034d55c3848d924004796767711b0b1e95bf04440973a1a332928019fff19cfa947eee7c1e5fd3e4f7b09917a127a4d6b5118ec602fb76174194afc78de3fdf7ba411d016fd596c9c4d11f308224376b57360c200510e38c76db3d351b9698e7e9543933a355f96a15104e71fa0e4296a404c5c60330c27c0808a150491313212d5b4e123d2cbc4c5e6dc8b53e0c3c77fb15acfb09748f9d551053e3661999762d66bfa36f351733ca1ebfbd7e140896ff5efc2600c411da279c1f4f5fefcbad5cad990db0046a477a9d149b5e985fe7b2ae319c03b75d692513eb8f758313e7d45067e149de3692df0adecf7c6f7062254294cb071ee3304b783765e4d05ada31413b5bf4ed9510fb52ae2645f3280659a197f00e67258dda1c4d9787b94e2ac3d533e5b385ee786052bf53e2528a2a6a627aff0ea007d95de23caa7d8a470617b949dddaf95bb1bebd6118324465baa49a7c4c0c20052a13712e74cde48a53d6b0e66662f010dc699d19cfae9b1f463477b0f107e76ff5821216ce88e18177bc98c540b01665729e978d9ca40851a7f049a12308c4fe6a4e741a6169fab0100c1eebb95d51ba48f6b81c57e1982c6f33dd827f02c6cb3079db8a0dfc06086000e697448e77f6fadf1cbf6a4b6235685913fb890d3f2de398c3d7079aa0f119aec16fb5933c8fe3d422a24cc04644f80e0eec58096571f29efa830a0998cb9448cebeb784f290b77213a8b4645f703c12adbb0002c84b479f2196cc49a6170f44421a68242a2f371b813e5484f06900d47827390675c270b94ba78dab524fe82970ee9412235fc116643d85057b6f097cc5f07f0c4a4e6c757628520d50e1d0493006c5f83ed20552e0e96d34e773a1e115f2d809c1f3259cba63105d0457762690e8f91596535d37c664afd21e7c7948da7126047b1d9e32e96dc00164555056c576226cac3021f360e57c9781b5f29df7062309142d29ba7aef7db18ad7ebc4b4b52a52fc23a2dce5f0af747663b21970aaac035a12cce815f1c7b15413126bf2a64b3481ef85887a37efe0a0832f6cec1beb0ad5449f0a86097d7fa7d302b3af74babf4f49a4e48e1481b2aa8b059f85937d000e966521600759272816072988644d3942717aa2d6935e7a0de70f12ace71109375691f2b6104ed048b8ddc3234f6bab71f08cd3784dd4ea3e3ed79c928c970f6c48765113f1bc2ee31b2a51338c866fd2a02d4631bc41cca41133d461a6da0e6f64748cfbccf58ac13467b8f91a30f8f5857ef4b059ec9bff84038f6b4ebf04c167089b971d50c030b3239481bfeb024121855766126e36a9e67280ad43180dbe7e67ea756e9a5fc83339e47b399584720471bd72fef6cc49f80aa670c4710afed1bde1fe534162d90af10c031a4509cc1ffffa2e2338575f24889f44f7e90abd296713edc29fb22d9a57f8d2d6553e4d6147f70189767b5f83d9ca7a0fcc0bd5b33971ed8cdfec9b1c393de56d46a2adf4e25023930436bb334498e142d0018601496bb8e6543a111f94bda5929f5210960ed3ac7a8bc4a3ed9cbae9164a041d9ac0fbd6feb376625e7e5c1a940641640ce68013bd37545c9dca3b4db26700d1e9f5c654e5b51156255b1c20d3cb3218e381eda781be2c14f24082e075060a0c9ad815c1c45f1b475829c8919c77da83ee48da25704d796f6715ba50352305cae1f5f9737fa2e1beb195fc466d8e8a6eb87d38f5bada2d84f26accba9d930599d865f140350805112a3a6f777806b7e46582776bacbd722de2af4810bcb40efcb5c9d4dc97ecb022544f2a6eddedd591472dc52d77da9bc92799b067adea03040a0e70ecd951827df823c30f3fceac40174a3c8f1ea3b14529bb661b806a07a13de1f8bf0068e78a96b092b8c3429ea423df9e6637a632bde7a0f9f2318704d5d6e4d94e5668cd9614e466153fb913dbcf1d54aa0e6cec6a89c7cc38d3fc02c540c8144f0bd25edf6c37155143fdce6a095b8422217169935dff15b9635b055c77a740102fa0428d6aa5f4e8d9580c915bc3b923628c5381920b203a20db016728c99573b48f6d81eb72fbaa5c54199a29dfa900899b914d5905bd8ce61e08fd03c0a78446d0e54da1cf9777ab5756dd91c0c2d1c468f5d9fcd6c07f46230ea8f36581bf01dab70a5a6e1ec4cabecf7fd3fa41219c7fa5cd9e57154339650dc3949be4931c5e0bb7086382c37c29ef6422894a6447767bceb32bf6c2f3f00b170928d2a73b388d8d3fcd5736167303c70021da12f8b9dda9a1d5aa439cbd0215ea180f62044b59aa4f9b549d117cf331dd0470c0ba38591c232fb4bde6f4043d69d037ebc722ccdc46d7cd4b64a5eb3792b97cb73b8e9b002a960c4610e10eb709a93c6a33e8a5a0373d7efd27f70dbe95fa530ef88b37974689e28912ae0ba58644ee09fa3b9e78b64cf0134d7e0f55ab90bcc24c56ac3bfec66f803e0d0f395987df7e8b14e8d5a6fcba3e731f240e186e0207c9e6076259d2f2a7485d09a21bffc6c5688b6c95dd9e2a4e867f8c974e274451725255b6767ebb31b829086458e6ffb9c81909deff2dc3392729a763e75f9eb079fd87e76d428166bcb008b35d4f2e341ab314741e0c45718d3bb952d0d61087a9de7d43425730a700640f408b1aa86333a079492379f4f518efd56a5b58c2b23cc4d70a212ecf27c9ea0298fccfb5e97e1ad05f3b9933a63de16da3f4f08c73376452dc048669a78f8b0352fbb55c0c714bbd6a74b9cc37f7ce12f8e522c848a33c74460685aa117337062bb0a6847f07fef89ec80ec7b1a61926aa5e6414dd244fbc53c08e485b3915006b4e7e16136c12bcface9d2cc1269b428f2113e145a0ea37516df4210155be0f9df20f075af7a7eb294d39d6699571cc00b43a8aeab304edd3dca8926df6fa056d77f9d169f11b786e343b086ee73c53a0d284178676207dd664202350a7f200478311f86537265efc4fb013615c2d916d0d7e15e29fde2b951134a300fe2309bea465cb11933cc7f70f244218c3b1ee2b29d0365a56c787ab19cc485b97df003567e773751453513f5d6af8460c656d3c64b8f2b73902e51f0e2296560c7002734cb9d2c6dd2eeed69a487c34d9610fe31826abde8917881b9b9c42da1d1a0fa79001347fd30cbcb48db48c378d08d7fb7c64938b864863c2a77132673d5300ccdb45ba6f20de99d1fad7f8599498ebb38ba687bea07873f080c83af205b105c200d4ec7f5b6abbee6f4a3fbe92c00b0e8c1e5183fcc1047c2896d4a7f1d202d10fdae85e2a662972aae19f11965cf09169d75751d82c14926d672156fcfe0c09208b3ae29d8d9b97db379eb81ce933c13673a60869a887b718e499f0bc040892dd4469cd94b06dbca51caa3f991d1d09534af71895a8a1745a059ca68c840dec545b166d7e2966d64b00bd7c7449068db14e61bbbbfd3d93773acaacd01e0e7b293509c690ca3fc4a2c0b265123b7eca4bf1b920a65c86ff852a879dbede0857843cd5f5c8f0d58ce32228bd8b99255898669eb83cf1e78087379543742b0724b9a8ea1b31640eacd74f2e4c5962df1a05a3a4e9775bcc3bfd9b35b1f9ef0bf93b1046340db9c79271763d69786b4d503eef99454e47e2c8fe565f9fd6130ffa227b64102a860489416b3dc3e5afdfae622af3e266184589b5f1a4fe0ce209090e726bb56af25c77274fa23273a73fafba5508c80b41e9a2c489fefa68da0f69c77320d56e16932385daebcbf1b2c2b39dd4e5adc217acd7fc3968f2c7590bd18e65a6df320a5ab41fc841c6fd10170c2e8f67ef4d58145a18f8d76d34a507cc644189103e45935de93141e9963f7d0ae9b45ff5402e05f59df0286146ab0c193b5f326650172ccd40038ac8d24367b4c06d65c3ec3e6586af9a3fa6cb3507a73d4a4ecfa7bbde80a9412d0abda8be31e9a7f23e7e2456c0747a2dd8ade400a6e26d637d49ecf6d2688401bb4d45ef498293d5c6d05099b8616ec844b4270e2c88689ce41315e61de1fc99df2a1341848b2a1f56338e10334aa32b16451b0d09d29ff49773311a0728828428cff6500061677232b52da440f1a4e8d5a2970765f81581c6881515299d450276512a0408563ef3c3220af7ca31d8ea6db7e606907c140a95d255de782174655bd86a37eb493f317a34ba1798fdc4b98026e403134fca3fd47d84b427ee9d0daf76c386055e09bded3ae1f40d4c439672da48043c455bddabb1675d5cd62647d5abf46b0472422dc51c518e3114a6f616a6d40f57030bc4c58a3e439f699a6c747b5c913b0441f20b4ddc0d3c7992251a47f600e36cd22dfbfcbf1ec22677b3210e4183b0b3067c7032ac31e70bfee28f1b2d00ed16236e4381f3ed0b861e5236394685bd1e6637ec65481af807352be59d5b0b16b93c4f39f41c7d46fdde7efa8a0525c589e73b197519c490f3557f93e1a50c33f0c9a9469be754f7bbcc0b20e8809092931c2268a680d3ab640050ee795e040fbc484d024832897559a38347afcd07278070ab980e49070fd6c18b00e4ef05bd81bac576528ffef0bb60fa39cba47bc668d87ec5e90ba56f8cf6c72b1bb90b6c9d9674cea966bea1526d428cb5928419a5bf7c5919ca6818c1af840f5bf9023b5aa007a86803f3d70f12b1d7d44ef819e86cc4eb02baa4ac48f4221f18fe0b1e081012f7b0e12d48febd77342b41904df72437562959dbc87c291f18d67000066732a134029852503fc2b4e5eafde91bc173d55c30f50bf7e7049d5552df0e6d830843ddbefecd1a51899ca6e594ddca26493eb591249208c850ad15668d06714fe55de9f4726b9ec51bc03b620538c19f7f6a3f5223072e4a7faa55bb82030d96f28bdca0ecda7a8d2aaea32ff65db86b9436b8969b3216c2572193f3210b2cdbef926efac00f66694b8de832d000732f56b023bc7cd56f0c4b7101969402a461e25eb303f72b1ba2ca955ee8b2d9caa67ab82b7e4a5e2b846dc44f5787077f16cde87b949e9666a1ef5588cc68a31379a095df023e2c5a3c6c86c2fb43026258c9f2d0056a58f81d69d575a618d1ed476ef005542ebfba5ff0fa5541290b0363eafec0c3ab364de4116afaf2d9978175c31980613738a4244800fec49807bf03cb48b89992102951399563fa0bf128f8c42724ef38292bbce06fa193a6016d7ba6fb401f16e29a15efee3b0456607120111808ad42ac50abbc63c931ac080f1ae0e7eac3037bbd3ce0cdb56686c3bc4ff0a830b134bc17f368d8c6bd5d090448aef34e1152b6317574c702557bff2dc8abbf3697a146cf25101671743d02a06e17fe7d3dd8101fb91830d11c53f1d9a3d3e7925a1037ab89a07600e71d08e547f9fe8b92683ad190342faeb8864e2a3f942376088d65d180fae2c55e1208e75c2d45d33d8804fa0f50020e49801fc618c5789325d76d65ffe815ff37e3050a1795ce6b6c5e0610a8472ad61684e19c345c9ae57a1eb9484f3d3b31f7ec0c5c3d5876fe84cbb90c9c54cf27df10d506497f3988e2ca59740778d7b9dbb10a538be5e87a05c59e13eeb6ca7abdafe61b9075183acf66e96857f32a632a8d0f0614a9f395b4e7f8af6e418a5296d494921cc1b1d7dbddfdb4eccbc16e814d0e9a6460770b15406c1e2450c3ede7cbc7bea99cb60ad1a8866a451c13b56a840f2752439cab10cf879d20a791d9f0a851ef4aa2e9f61e6c4562ca831bf85330017f6f72e76934c4a4a05b4a012de21ca79fe7dfb3e6343e100814c5472f6423090cd2c336dd4edf1f5186ed622928ce79391ba6009186991aa65bcf327f10e005f198c181748eb0cac463fbbc1fedacd281750e5faf11f808cbd20e711e96d80e52b2216f39023251a0eaa2376996fec0d2bd7259bba022c7da37174e8b3f8108452ac39f931bcd3e9d1c904364a674ce91c2f43f1d3f672dc445e9dcd91a3a07d8bf1fbc046a5a7e0d84661ba0fa13812eef16e5a76121a9b65b99a566016c05e5a693f4f67612e40453fb872c2cd4bda7021cdec1b6966a081b128af924f1059ab253c95886713c18642e2a199096b44142a10b747bea1c9e899d577a7cc5014b35bc6b8e873e0361a6182866cc182dbea67f4999ae0930fa921423e855fd0fb8ef23913d88136bc04a8c130cc9edee76513907bc0435d925ead18bb2f14f07c4284433965dc7dae19b988e0417b6cb32f9ab442a621f1e7dd1f1183e65a4086b67111c6f70418854b8a5d8654068a5712aa4728d256e54eeffb990902f3b0a3c30f505d98cc1bbfaaa292dd1f700d586f83c324e38c0016badca5400235c00263978a8f64787b861015fec6dad151812f992eabaaa066f5db6542dee92910c12a8cfd993b2802d04cfce95eaee4efce900f9dffa045ee335eec30feb09800f53c535c1cd642de5d19c2b775be6979c9657976113515a77035ae67af41a7e005f3a0cdfdce02bde0a84d5d74700b1caf027b477a66484747e8e237e87611101e4ba63a6a5f6c813755bb0a9d68fc649119cf7f72b45694b99c390739e1f520353879462cbcb581f65148feeaf2117d4988d035c07427c177ff9d02aeb8377082d7d01afb170770fc8e7b04709abf49cc0535ba8add277895cae44dbde3f360afcf27dc22dc3b9d59345eebbfcf4819ee6ec83495d096be2ee1a22ab91ef890ebf032618f1ce9f32cbfd479995b037c27f1045fdb2e184fa5e5465937b833909cffadef1508a3150aa576146901c9c8ea90cfd5772cdcd98d963b31b62d4ec048c4cb171667ce956671c7b5d8eceaa56880a8b63fa6e71fbce857268522bd40cd6be1be752f877224135b9c5f093708d263f1edd5e8a532e6f138e51a9d58103e13cb3c8e1015c7f0871165d98fe99e14eb8a7750c514208c3f36d12846b120eb90e3f94650b747a5874dc234ffabc3e07b6266cfb9a005d59bd06e32624fe01acb7b032a4ce789d43a607418ef5478cdb5de8a9f9da3f69fee90c032db16e051db5742c8ac8f61ab17d55fc9a854276c8802b33ec59b6c6e6120de6eef8b000ace1f48c33423cd0372a827ad9713340af020030c8db702b3c9f1bf451af05011e54e387802fedcd4f65e919d1aa5aa0599f8a53c41327bef2e93fabc090740cf2dd05b8a6f937109cf52ef1381407a07055f9607e781669a578e981d8e6520369256de94ad30f5e05323f7384f96ce27ecf0f53fec08626d052a977b9d7a10bcfe4c4249962e6e407b61a341da4ccada96b53e81083c1ca2d36222a551c3404df2ceb724e3b84914651888e023e7053e474abf4587cf794e315b3fd880c870a false +check_ring_signature 406e21f219d8743032573aab261c7f13739887c63d2573708450695554a2bb0d 86493a14603fdd892a3f1b6d168afde742de619522e82f657beb81e6802078bd 2 da5c40ec822b54008b22b6f0772ee446a78d5b39fdbaed0c93d4a3eb9be023ff 346d8ed13713093d73826e2500798dbe68ecaeea54031d8ef2d9d5bba3f6d400 a2a6aa9ab15a57651cc4870e0f17d71ba1980f2bb9f271d26b5eaaab4600d9099c1166fe738e720a3bb395ef84c560ab223f20b609bdb1cd3ea8c80d3ab338046c17c36b05b31dbdfe21bdee5f10f8e892bab7f066887e73de2169e309746401ac18ab445a0d6cfdc371f12fd74fae4112917aad14e2d9be07e747e77a16b801 true +check_ring_signature 5ae19e6255931936c62de386a42475a8ae827581b49e78360aa97d530aefa699 ae5e3973190a3930513299ccf32027b36523eadd94198fd66d33043d2d6ee287 1 51a1ddf0bc10fee76c0df56fd6a4722f105bbd907f57195f16e7540996c3c09f 716f26d668217137a2d4927c75a68d68cacdf3d2129de282cdcd96a3e821420bad2f7053faff47c625469a943d86acf83ae055fe7d1a8c2e76dbdc6566c17bae false +check_ring_signature ba49592807ea096a87f8c2145dd153560a61529193ed289e3bde7a1c0d4d31e6 ff813234cb4a2c7a4ca38178d09c4ab674d99aede175934bf8a77dacefaef988 19 997197934de1ea8d43fc6cda6c0f072d2214886879afc1bc4b92d8d101e9e8fc d37b437d6a5068ea41626872224d3b76522080d74a49397dbc78637171799bd2 e2420e15b63b508bfc7431a2848541c7bab25d4834b38bc3b71ea4f8ee75775e 0fa43ee5ed6ac149c0235d5b6f02d0bfee8688f607b878a809835c036f9b8ba4 6f402d0355d55f62dd321bd102f656fe199d37bb4766fa91e38f44c8f2551cc1 467ed9d1ddd44d372db52db48ae5c8c60f25bf50468fe1036386521214d9accd 31edc75fb88b5e0ed343ac80679a68ea4bd6996ab310fbef30f3dead6589dbef f1185735e0f3ce702de4e2c73af229762c07c893fadfebb206fb2bddf96817d8 97a831363840451c29da729e4a2de5a0d7d9fa20b78ba02eb1ff96d385209109 517d3637d4185c3950f2eadc36a49d6f0bba4eb806d40d8af54176f0d3723db5 d1ff0d42f1c91756096f7c0fe5d6a5cf63e4a0ab6fbddbb27329462fa8a90025 8891d3edb6bda29bda24af78aeacc3d227da02b2374bc04a6313d45ebf4acd16 6135cc302270987a6d048f6aabbf7898bfa1eeda4c524242a7c8d9932b18bd8c 5c603e99808a10ea7492eb6bc6e533031a2b498ca4c57f37caba6991832cd6cc 916caef810ce17783ca34474ef5afaf094852fc2b6d1556f41f118c167c8df86 2ea5ae2293c21006c7be73feff0cddd1e0e7d93fec67e075ff6fe3fb234195f0 e743386e73e1b2a04e6e1051dd9c63e053ef3454228a34507e6e8deee8b91318 f18b2bc763ea5d4ffc305a942c5e6c3cf1e96b3868f408408d8752ff0c340436 d32ba437318885210fd26c141f9164fd2f92fa0130ef181fbfb055ddfe0cdb70 56898fae444cebe10dbf8619e4f611e70add353cc3a2b204e9f844a46ebee90464c6e49274e0130de9818559765ee69deb6ccb223a7389002d6217a87ea8ef0583b09321e3e2f46dfae11297d3f81a1a77fff558e2e7967aaa36ed631e1137084e592defd9d0912d12f9600da71fc4efef1a78c277a39455205a54377c04ba0527bf98138e13bde644255986cc0d22c1f519079a7f885c8bad4da45d42cb1000fb872c68716ea4a372a485273c08f9db247c47a32c2e6eceb836696f9926fa06ecd1028d83cf05f6f6c3fba4aca88b23e03d0cd68ba46e80d176a2d53b305a06718b051378db7b872dc2fea6e76be4cee821eb25fef48c91820ecf62bd57ed0661872767b378f5a74e65ee03c501c524c522b8d85e988b4b56240ea9d476110c0641815dcd5be34434a959f33d07d308f21b0860cec81e4b6d67bf9afc28710108406e69d86c580897da4dc7f519d4f2a4ae56c9c45f8f0895f1c8c2964f9a0a427626976b914eeb99b5b813b3573d3360e7771a36b9618e01444170b3f0710e4401a4408ed5e83f1cfe979989f7d3d0c97d3759ffc92747ad0a07568bef5c0c6e68d461b8e61de251f95da925b0ed12fd1a9033d2d043bbd686b980924ca704beedc1eeadd1e93b249a97dba2721085e2711ed82bbed7ccd81af5013a1c860bbcd866cc33a11106fcb2da203752368b12022b9056cc55af4f76b8ae6d56860bb6bab16f3fa9af466687458f0f41d8fd6e848546c6252815c364da794b8cbb03f77adf6bf194840783633405c4d0eed2e5e8e55bcaa2aa2fd7797a79f75e1c05d9cc406a01c97642dca04a70d11e4fc4507306bdc35c84abf19257397a530709a38d07f478443d749a89d35dd2138329a85df5e41009efe090d4e91ab982ae02d888cc635222e88e353a8fb2f972ba4b1549224d51f1c218a8369dfb51a86d08c0542a15c91c78f40ec6029aa271273759f889f476b9d0d3071a150003f55801ca57f36385b05deb7966a56439d6f6a947c19e8078040239c126fc10ed9ffd010d8ab0de84bd788d9c1a6c51cf304d65195f1067d03a3c50ef8d5417239cfa0a5b41fd1fde2caec3cf56eee5af1bdf7df748318eaa99a47bde24b3c5b6eef7082b399dc2620e77a16e22b4697450a458ae77b32d7d6dbfac99df1276797c02064d51809ff525d22444469cc7195b5c4568d484307de7a313897041292e9c490bee2815cfe46a7dc02c35a7ac122427f56dea9687bbc008c47c0087da1e982901ae8370310afd138dbc8a16c393f56886f90a25c5199bd4bfa69c14f3328dfc0d4a0257f6164514f7d3db92589fdce88dbbb58ad4ac6ff8a38d832e2eb222530296769dc451a91b51ec3b0bf03471d4afeceb2c23bbbeef6f1369b78eca68930e3b349d243a1b31538d4ab7df201d80d42db9d714dd9dbe5f28337243e706b5031a6688ca212b20c55d03ea4c42f3c0bf0ea9590d9ea05caa528279098704b201a6d16f26c26ca8980260d8108f2026185ef5984bd06058549ff6e6e5e15d9f0e2603bff03a9f1d0983419202f9d6e032c67d066f90cb234608669122b1122e044323d9c898a3a4842cd536335ec41a703f63aae699a27e36855aa4bbc817dd098ab3cfaee9207b0635718529f3255e04c1dc590b1c5e705ce4cb1bdfbfe0570448473db8885b985e10c23e7495ac9356e371f78bd0e915b33142ea9bcca99a0e false +check_ring_signature b76ab0574bc6a3dc2ee9a809abc81a83adbee25c51a1e28c4c576c73e956be3c c4abf904618a645d221c3d5c8fe152de880ac23efeddff21bfad771f8e238c76 189 a351ebe970cc9898afa1c72fb553126bdd654c0889f859651ed27e04a7d9f68e d84f0253ca8ff432eab407be8cfa47792aa891b76f53ca61c3b19148d4e6bf63 259a6186d1a8dfdbc499bc2f0bcbf6963929c5dcc3ef8d9f66573242ac6b4b28 37cfe97a6808b713fe7a6d397f52f6bc1fd730214114e96a0ab223dcefc8e884 e5e6225fc78fbfc324f8f6272a629e487aa0f2e39ab55ecf1023c366b65377f2 f6949040e19d7f4b9c1434f7b159d004a190451e1860b448dfcbe4c0cf047de1 713a95b2b622d6f5f18b45fb274e6cb8a3eb445908ed8f828500e6adafd0acb4 9631501ce9323432df32a3603d2db5ca882074b914f96a5d0ea133859a7af6db 37dd54a4098b4d9deada7bf611ec9582294d89096484a6cc6953851f77148cdb 6dbddba857dc0b4727dc4ec62fc6c1cb0b193d6d8a5efbd035cd79e04fab5107 b87c0ee944e606eb162a44d5aa58d70226c0f2ea672e23758bfe90cd3ca0edc4 3e09ed22646925b2640538c9bf099a0eb9092632952a7590632a46b4b10d3ddf 70794b3c95eb395e28c7d8bb64df97b6150d8478215efc36fe9fab8c7c04530d 844317879c0db494c47870bd697ef3c25f26d47c77778ba2631cb1ccc6577e20 22b204bab58f179e19755b4b06cb54d0b06b2322729eb28730aee9650a1aad86 28d48f4a5aba567fdf83fae4017d6830f5238a70a9e169aeffee0a79f1396025 447f4eba107f893e460e63a7bebc83861c3185cf7075025a2dc33a3661c608fc f6913d6ed2a147657de5badeb03bd221e1ec64a2797d67dc1bfb6a775f661a05 0231f26cf953823adb83a8e09d553176794b2d66216bad33a202ff093bb7dfe9 b65cfec4e27c4b26a6ffa9b93f6b23775168d3dace2fa73c2afc6886ee7d3d89 004efe8516c357add4fdb1f6c0aaf9f105d16b12967a4134c09818a16242e08f 236d51c3639e20bfc444c40667e97b3843653dba36d6e7b49e3ca06d9c0e02a5 17b34da61024145f620b3bae28192942554b9305b98345e35f88a60ddc8855e3 8ece425c8d32ce25ac34b129558a66a3460e498041b2231e744ff48f1fbf0eac 2644cc8aecbb222dfa7b7865544530709c0f47f9037f567a7ac71f1363f24e5d 4f3f52e3367e01a6a1a4cee9af74a22bf05c6f163f935782d6ac35585410890f bf6cfc8fe2942320d7a90fc8ecbc977f708aa49cf2ecb01648db46d1cff1647a a7fad9e5a4c7f13c42079d256b19eed7bdd799fa2beedb952a62664ee50bb315 b3cfe18b91c7d15030a9a5a296f8525ffaf880bc0f2f8a3a0de9f5173afdb093 3edac807715243b766b0da867c3b5e1a064f7e3fd337602210be0b6ed33f0071 b755daf33e63a7487173826914ae16d7fb15488c1fbef7c1edff9110ebd70051 063e83c3ea71db6fe1be0eb9ba17f85c1b1394f8d4f7ef923dc56bb03c386879 b27335a23296f228cc2495bcb08f7797f931750a9737ec71636054ed52004a2b d2e254f25551841cec0fa661b8540158222e3130060c09b6b9bb30937f2509b9 0d08cfc6707514d52d85f5e5969a71c6435004144229a71e12902c144df8c7ed 406ba50da95f4dc72d5a29f33e2b4a7d407c586b03ec14384e34fc3087618bb0 bde06ad0af59f739956061a6b55dab3be1db123d4d7249e261f9d35ef304befe d34fa5c516dc96a1f2aaded9261d166b1a90ed2f373e96945656b534e65fd717 ec0bdc69fd39ba70838c1d5ad15e2e021d9a12b084133f056be39aa03f2e2336 cbd56d9140143dcf4c2afcac025953711c331032cd2a5d02b2c4fe390ad54414 78294d2504903bfaf00c4c2ee1382f10ebf540855ebc58582d2634efc92ea224 223f159ac07e0d0b7f196cdc9b1175d755b8e9928cf0867ff731ec1eb800a081 345a84c1151e96966f5be1e90c4549e693627f83dad5ee19adb6d5fcdba8436f 4b49a26f18d9055f5d524061995faed736ae4eebe9b21930212066f448a42bc5 4561e7320530e3bbeca1ae8b2e776f81b7cf2e5f17de0268eb36afb40a6e1aa2 1fc35c359f069dbc111c73c4e3994f9e532a6d674338f5f14762625857d70f6e 86d8d3938a7f6339947ba1308a8c6bf205f6437f48a483f014e2d996d6ced72d 80a1e68d573a068695b0c527813ceb9bec12f049e3efc9ff959e7046bd6cc12b 8811fd51ca49e43379e247d01cb070fcc6576379fb20839b2e7639990f065773 9b05689cacf02e8af9c29bf5eb0d5bf799fa558ce42cd8599ca23ab767408313 cee1476ea2ca920e6b4f68c72e86d6c4877a29be7f14a0d43168881b2a338616 2cd8f1151859c34e845dfa794a5315218c0f532daf920f38f3c811b1971675a2 bebee5bc7c2a8c12aac7b8c386da1f299704e8efd04d62b69dfc0e16e8697b71 396dfaaed3180a295bfab396750d326439e8b9bfc2fe8ce19c5c4c1bfbcf16e3 0c8d4252d700ed496ddf97591af790b8c133cab1aa1f15457a7fb80f8eca7444 6356db4da841ba2ac8971e4fb5994d088057b546975914cfc019f779727d96a3 675e390c156033be2a196d0875054456c2fa3e3aa81ec2476399c1b4b7dcb5bb f00076c9d0c1a3c7129c93d341dac3f4b904a77e8fcc3edba3577e022f0866c9 01213f8c048300529436fa9ef911d07d177e541fc454f8cb25575669d14b484e a83137708b4e32b6ae470e6ba775653ebf94e47074b3d8729eeaac58a454a38a 3d7b4856fc253cf66f1560814834afced25050e1b29d2d0cb89c2fabf4e981d4 3859f1359b9fa81031b39f2b7c7f2545cd415b407edfeb2364411b3f52847083 3cbcd0e7e11ec981513489648445c601343ee687ee1b85eed9344b0da54bd071 6422f6c777e922ca398506973091a74f05e04480b20795e02cd031ccf24968cc dcf4fbe1d8967eef7cdede945843b2ccc2627a7e503f7b2017d5b9c187257010 d29a7458aa3b1707906d9a9b682c7ee760594052dbb427aed4cccda3c22a1392 f922b5096d92a23c89d22331e0faae570961b46cdde1a3107a1acc60a1c874ea c9c816d0f12177579f32c5aaf27ce24ce73c64826c09bc1149b046c14bb01d56 33f8dd40efb88cdabc1d21606ab478f3470f6ca4e59b358662d39ab119ecf575 930755551532e2c6cf6a5c5acbacf2f047f52c6f5764985980644800fbc2cd34 adb4dcca53f83033b11474911634c2103c4406e2e9fc53a7f97acdb6f0046423 2693eae9daab808061a6f74660c9ed98ff47dd484984a52ab647f7a92ea2f575 b62a8eb8aaa57b59e2adedad290be564139c8718da65b5ca339f72f21e4037cd 2bc609ced5d6be332ddf517dbd1f9321a1b94d3b5145530e66ea8890b25de152 b749fcd4fa788179579c1b828ad0d5b6a0f7c244dd8477c9633249ca5757f674 20f6289bd9345a282637ecff3dddb262b3f2457d6a9761ef6a30d219dc2c20c6 26e388c3eb120e5d0134c8d9daaa658e2f0998296b33ecfd3b0f9a87217a1ecc 1b4e16c3d8fcfedc5a14dc1e7df6a72ab769910540c9b5cf58e9a9c83f9a581e 1b7138a4372a41b49c5747370ef65027b49caa9848e4130d7694fa03ad01a0e6 44ce2b5b949caf073bd6476e4d3e0d444c481eb67f3b11c54159f292ede8f308 6fa6090d16adda5373210ec129b8fd2acbe58d5492402c89083bf5b384deb840 dc2c4cd69941524451a868d6223411b19a628419a1f0350b5d16e911d0ee221f 2ddae5d58a74e5ad290ed3d442ea38ada593d5ad5cdef5b2cccee5f702b7e1a5 d7ad8b61fa34a5b40961371d78af92dbfd57643e3c4d5bdd5529bdf824e992b3 c11e1ab24fd13a3b8931d68d532a2821f794679b20de6214847be4ea3b002ad0 3006c9c6e17d2addee27517adf69e64a8321672e6579d673e8434d928175cc06 db5e8badb7ef91fd33e4c4b4cf9a4c8cc6f8488f65f217e6e5ed21227752b6f1 24114c438a3748da27d491e64130a2807cca64f7a0639e766b831a3ac7a664a8 56e3cc8cb539761c0fc9eb63d1a4be1ea0300c23254a2e97b08f3c69d3506468 c636b323914d9bd89220b8a5f0ce90d8234211ef6dae218525776e8311112f1c 1ad713ca3a4dc4541141e79f09a9264fa3eeea1fc4ed9025461665b897cd98f9 3f5bf895f33ec73a1bb6ee715e2198f70b683466eaaa74716df2cd4271bf34af 2e2f1943eed1a5664cc89d77881a406a59570985e7484c49f11f996afb2b18e8 ebd85b49fa88316426a3356bb256cf797fb668027b38db979f11c32e17facc83 5dad19fe08948e01dc347f7e1e7a582655e4db2c05799381cebeb31c4f8c8704 aa65ed041719d91adc8a03581670fc966082cb89b850f174874338d08d80241f f612b71f26391d2ade64799abc2e41a02d1692e5518cd5fa032866e765f56bbc 22a8662f0a4cfc0cebe38fa4e431afc1369117facf6bc0e5050f3043d4148ec9 c859593a6b6bdfba64b02b5e60a7fc3e8fe70e83ff4834e8734c30d6d32b2225 de35529d9d5b5ea4aa6f416405c8b648faf4301185112c937d68c539c1d41793 1efe2623756a337c19417e91d8b0ff218a6877c06b9aa02c92f8cf0c81be1df4 f8d2692fdde02e13309b2d250f9a24db38ef80d6904bfed74155054478b87cab 59fd131340ddbd16ad2e8a38444b40c4f2798c645d06ef4165e55b29255a19d0 41ee49c21259340679294b4413ddbd8c0c31d8e7f532365d0507f0a9ce4caa0b 9e943b5f843fbc86209a7be834e24aca8c64b8710b3572cc5c84d1040027dfcd 558b4937643350c485621e46f943fd0c1dde2eb74c8a7166de54ed8ed4419991 8d7b1e493495a3848b6dd3eb43a12cef45eb46c0acd7ec13f5ba96f025f7cac6 566982e16c11e5595a3888d4d31434bb3e263f89ccf122f63b9e076db556c31e 94f547f07420d165131a34bf2182270eae0f12bbce377e69378ee7caff1265d9 b4b09ef24033575c2a9c7bd7c5fdf037de8311957b106b3ff1dec0dea92e2b42 415cedd76b0384b9793bf3c0a70dda0dda4acc36d97358cf7b65c646ebb4aaf1 077f423e65b520e08e5de7be8464d6629c970024228d989c70f42ed7207ca607 8bccb66614c1790a8beac630b4225d06327179bf4a3937a1fca3bec038fe7bad 5931f3a3bbb870a3a4607924187b1c64ba96504a9f231669a7848fac8121b30b 7c6dd453cc69ed5eeed52eb3831b990991ed32cfbc7e53a66d552900afe3f598 5453abe5fe2086d06c6db03b4ade540df413a6ffa537b6e4ff0f76cd4f6401af 585e04ef98cffe573d246928ffcc69e2c82ef039c23d9648900cf8c41e6acded af3287cf355f97facc9e64430833f76eada8e5fdad595b531870820c33b0e10b b7751b79eedd295b991751f1ca15e169972bca027eebe2ed9a374266ec5e0973 41044b9ac2a335a4b144a59b6010aa2da16ae50664f59c49c6e70596402623a2 a78c93ee94d35e917d85d044cf28e90efe81dc84e30f687db8affcc652fa308a 19f2b06c81ac0faa8a404f43a6ae3cb8296edc90aeb03b27e7b112f100b038e9 b3d3344058b9ad9879fe118c8b5fcdbbf1fb29da05b9de5cb55f88e28b43d3d5 5c5832d48977c0f7802e487c6fcf73ce2da24f1c8b90750d9faf9626f704e4d1 f14df2ccc9c37a15ec0a084810d742707ed440772103436024cebc986092a4f4 7817b99c8c54cc90945510022032c9053ee7e15d67e6181a76eb4383d6a60a69 f6be589fb6d8c4fbe0a34dbf2ccea7724fb8eddb3e03f51ff9d8a7834534dba0 781b051d1e3c4d4f5daa2e88fe105dfaf497a60dd61675aab7ed93cdcd805bd8 cedb7ff42ed3531fcaa9b9011583e18c46683af6c67d3d69efccc0e157cb7377 a5c6e2e693534a08a6bf946ad1f0cb0c22fbe974a869709c216dbf7edf2be063 14ea00dd02bbd869c0ea3c3831b26a83ba5ad920ce305dd87c0fe05a80724b52 2b17fdf46166095425aff7cc4acd0dc3ef0ce96e14112730969d0dde3f4e640c e711ac89eec13ea568fe050669febfcec5ff10dfe74e6a2e7de6744649325548 b9f5ffba9ae26b68fa63496c3446334aa33c67ecc31dc1f60cccf7071864fb8f 88348f36170362d3dbfd8727fc7b77f5b06f07db86327b03dec4559a877d365a 3da1929ed8d98f7a663051f209db986bf751ea3e75f6a2c934dea092a897d456 c3c734bb278910812fa6c23f5640fed370fd9d982572b195f80611c93cbb79ce c2b18e3a6d2ffd276e1d04e86d41ce4b17a18ad66b5ae8a3619bf961cd28332d 3ab7fcf77d18de10164ec7b59a520c88bd2c1f5c45ed651940e3e1441b98dc95 b91179ef7f693f032092dbdbcb1033b9c7cc6b9c7cb66a372f8d49c38709eda9 b46872a1611dc5d194be8ba18a944dde694941f665df6d035bc4a60e5c591033 35ed15b51b2537820882f4b54ebf640c833c41c6d7cbc09baf76e0e408953669 e5a3f1055075a29727f3cc0381a69ec5db87160c25b10599e45a3b1b5571a0d7 e33e5dc0ec8c18453eeda4657a64c43741b265102d5ddced63078a51b539cabf 4ff1253fe0d835c9d65b14d8acfb9a8d9c7eb59abddd250259a9d50aacfbcfd2 3782b5eae2b99d714edcd0fa507fa3caac8fc56c321837f1b281428ec06f8032 bf879adc4fc9f354362f205f9a4a03c993570b936e4c0b6146c7c9f51851c2ff 842c27ca464cbfd5529959f11f49b2fd5d16cc99bf99153b591764f978a09720 081f5b16d5c93ff6d8563f1216199c1d7dc920898924d1460ce7ad71e872675a 927ce86323050cdf93d926c4cd925f9ff80772b134e097f398e01c80adf7ca84 093b1ab0b4592dfe8178e75b01ac08d931d0aed8ff2bb852af15fe3506ef831b 09e2e946c5362b3fce7f8c46c8414461694091a3c6a4c3e2351970dcec015ef7 0724f09435235fcb9884e8de9faa78e4d7d9dace14af7ec30d0635177d710363 369179e0d9fec391685167c7f497bfca5b261e24c9ed01cfa14738572d4917fb 61419e8f8c428c6067212983e0ecea2f8bed8e6254e3bd3a6dd16045a9e2e62d 6fe5208e973dee644b2cb1edcfe1990c082751bfef86fa7406d693f2cf01a1f9 a7c683f8d2efb1d53675a1b720cf56f7f1b85c6738213b4488c7417c82d5ac73 e0ee15caa62ae2c4de89a52056bf92ec532f7fb73bf41895bb2234dff8fabcc9 6f98b147a4f4c4591eb00b5d9101e09a756a139aefc6009adcb2dacd18bea86e bc062ad3d1693dea1be5403d8296be79a08f43c8f090efca97928b2eaf2afd69 55669ed8ef3b1ca3cf652b899031e0cd2460bc13bf841d354f4a3595c1f189d4 d39e62deae881134d775a82ffb91054811c7c5783ff770e527e6aee2a55b2ccf 5525ecfbcd7c9c002a2efd2c4fe193730fe1517b24f47e6221f8a76f1bd719e4 4f9ab634029e6104ddb7dff8d3487f1254a5e7f9170af4dfff01cd63ee7032bd f834c310622f451804a8db382b8a762eef4543fdba610b05b58abfa2a88374db 94e13bea1477cd4df90065b3e72ecfbaaff6cf44a43fcc4290cc678f63bd0c64 5ae8c1879d4bf2d7bc9b4eea0c062c814a5885c38adbb80763fb9bebe9500cef bc088f549789b01fca648c9cc89238772fc3cc37dbb9fcfe3475e1764626e529 aefc01107238a4d70ee623cce1927eda1f4ab4356f60819add36454082b92baa d4f682fa9f676d554281f2b338c9491397d266a9cc838a01ff9b7b6c8db6a29a 21dea9112d6dc296fceceea1b79ecfda805478eb91c9c57facdfb931d37c1de9 91dd5bbecbcc00f6b5853a2dd2efdbf9e320208dbded1be0758ceaecf741d050 9c16aa4095208d563f222054144e98d9c2efcc72c871fe1a3357f2b51a989ab4 2fcecbc18ebed36cd1ff3ec507857bb3b0d917b5099f0a4bc60de7b0cce0780e ba031768e50dfea6e49651151aaf0eb698211defc470771db4a1348d4d599e2d 1d45d1127466d019c81043e6ae8803ffc2bee62823b681cfc632af906d282084 7336254ac303d75c1532b2cf6c53e03726f904a6a07b65bf50b3759ee5aa3dbd ad4f1b4907af1a375a7dd90191596d5c198a1b505010d2ee7da12c808c4d3d54 45968bf49c34fe39290a1b5095ce2de5d37717d08df15e522c0cda6481c49674 cd9f4c7f4a7a4692cd03308c1d3e8b71a52b4672a39826cb6dcab4be3a7e04ec 4f8af18c30256dbca91702daf3e5467c88ad9798b03dea41c8611d727125b829 7e54c62532b2126001fe273d6a0b65ef7ee30d2865f952422486d77bcf0b1bef 808b5c4860635250968f83b4b32354e4b159806f0e37d79e1b4572ec63841707 5aab5cee16ef7343b63f6b0f15b66b80d23484d8448402f626b3067c09b91f7a a75006e3dc0d3ec1be0386b881eb68be7b62c0102259e3b286a035efcd492a12 be526adb5ae0751ced29b5018822f46d9c066c0c061e9b90c9e9676864fcd030 8ea3965f4666bfaf52d0a396ead307dffdf97d806ef4a31ec4976076eaae760f c16c91d84887130793e2919bd1971acc2f9c282c10242cac31af0add1088cca7 e48bce64d672ec6afa78b580f3d05dec6faa20a0c1b0d4e7a05d1076f9878484 d0a77bed825ce8d4d2b0dcda2c5cb7295505b41f511fd05ffb47360f001fec05b50a726c4ac53707567d6004c0ae6ff292015d083e27caae80a4b0daf1eca9077a88cb74234b0c2840512ddb2ec41c992e2ce57f5d1093bb597694c4756dbe00bc012a2612b5fd13940c66bdb1ae7d0633988bd8ccf591979e73c20b36626a062ed3636779770b3aff143ff815fadffba3248f49862e97c29e60ab7c22432b0cc92925d7df5cb01ca30156dee783f9de0bcc0bb2000cbc88cc81d0447402e40d1de9b0694e298c2f2c677dc64bbabca384795ea006d21bbbd485bb92304f6b0f208208fbd91602139430af0c090109129859c5ab1da704c596df952fa672680bf1e67264a5ebb6d2e77a789f5f36e8604fc8242e7aa9fdcacf9ffcf616ccf10bf3d6688b81d07875ba2e0672e321626576e21d151d04bb569889bb8d9e368e07df2dc0bc5e38143a0da752eae1b74dd122a229235923bf615f79c8548bc9dc03a654793f8700f983a4cf7acd55a8effe03e35083cc08d1ec0c150d798850f50ee06681e010382e1c0e69f365b163dfe04e7530b48a617ca968dd3cefdbdeb806f34f0023c52bc21b779fa8818bd169ae49940fa5b839ed02eb8e72decdff6206f094c21b6d0b82874174761aefe9105ac57f06c303279b4012cca43ab42b120ee93c0b29be300ac5dc4e064e697f38dd7cf1e4aae49c522081e7c3406eedf20f865e856a52d9ec8db3d517e0dd54bc6478240b4add481b3a5cb985ac2e7a6f0bf864dbac46043650e241685db9d75bdaf43ef8fc60f3a562ef810f4977c6eb0b615cbae9a1f8aa899c4ac01990c818258c1cf8d554cc5bc9fa80ab58593e8f077c3b70541bf7804598869ae75c6e606549f51f4a9c3c92cbb8362b57713715075faa1950cf6955ba7b82afd6ca45f59703627d83590cde84db26f925e9837f0b8cca17f2a54754a16cf0fc9386d9e2e3ecfc86ce05c8aa7eeff8db5875f72401f0f5a37f90ceb20bb740234bacbc5110e2fbcba310bc75074beda2d6e9bf1e08d0091ca56961c54d23f3c175fe14a7f27daf48e45cf07e83d63adfa583309b08099e1060f672127cf08066362f080464e0d6a24c7f85740a4679b70c7a7697051bf5068edf3ae86e4183054f887aabfa658050b161109cbc0a840323dbb486079e3c3db4fe0489dfce1e54e662d24e9cbc5c40b7ce1ce2f6bbf45b4a09a9990b53d6aa97a69fa713f4fa9acdef2661916ab1695dbe5991a2389ea6dfc597c20dedbdb0006012fd57c2329a179c05145dc65364803aa7a404e3f5dcbbafcbbd00be956e4733b6f0db0d686ab0d6d284453cb6e5e0d0a32dcc367ebb452eac240da0c961f3ace4cddfdb5abdcbe5b11b587448c31257b739b653da56f86e0df60a0e98d3e57c4e1584d9174627c60ee6700d9b3a7df25bbc979840b3620affc306108568c998a110b6ca8e7b0a59a7da1a791a1b7041dbbbee6b74e0e104429c013cbd0c5a46acb439634ffa80c55b1914021663b3a7bf1e32aa035acd80e2c8092c9ab00b2299da96a3f75a94baa9585931d489e16e9cda99c1b5b072edccf70176a0af2bfa2c214e7e027de4cb7c60b7c4d4974275801500a955f18446dbeb080c75b11d9ec560564b0f8e6119aadd7fba7db4fe586717ff413623944f4237007755ac03edcbaa9eb44c0a70072e234a469620e2e937ded14931e8be5cb4c80922062449fbc4f38fff1bd3239356f11c1156b5a6649e866d534be6286227c10a7e51e77c52ad020f6f0ddaef6437e8c4b247478eabe4e0c8340ac4c6e489540f9a4df3b41c70d5231001f4521141e354a19fefa0ddb3f63c54633a4028d7c70932dbde791d4bcd37db0bdf8fc73fe1ad451fb430b70582f6faa4728db211a405ad944e1edb2f13e4d3cf4946bce58eb1bd194c0813b27750097c2fd095c16f01bb290273f825429a110ec74034da0d2f570426342d1619c365aecc4f7e14f50963cba1ac7b0a65801c34eec8fd2647b48145e1f2282197e04be5ce9dcc52be01b5b5df598a148d0ea291fbbbae531d1f97e190da3472be3ef950b15afd607d0305bd2c85e63f940f696421b30ff0bec6f8b0f9e4fe3d2463bdabe0c078db7b04fcaeaf9a5abd67e99e5bb7d67302b5f9c4f3183c4f84a0ffd66091195e2884058b01cbfcc72090f9d3d63dd5a87afb2581cc6da6c134142ec537ead766f3c50e9a4eba7ad67e623eeb9d88cc2857126d2f69b7effc166c9e65a49e80ba54cf019999b51a1ea83f17e8b6a69bc729fcbfb6d1ed4c74d3011a4d1edc915346280d4d963cbc113a3d5b55fcf0c841e02c5aac352e08499d14b4a9176a79224c5b0959440b7c360f8e7d0bc7ea66614a1b40a3f0c56af067b8c060a0cd5fd0f6ed092476f02cd1f8ffe8a5d1ed1b7aff68560c01c82375228abfcee6dd0f2a75a90d92372f819c3e680a23ee2a9e76021a3050c83596c4f3d878990875329fe3a501f3d7eae68796d44795fffc3df3b4d60ca0319643a56a71e3861c93a5b029470ab948461e9b364a204a27baf687c9a0d0fb5f5b9a9dfe4429743a6b95b5fcb102795f0a92a90ec3033e8fad9d442fb8ab62dfe7d42f82f495623230d369ae330ba120667290a782f7ce17f70f2a0fb538956c69926ed2150a30507e6b6c6a3c0f17308e0d745b7c5b2286dc6e205f9a78506bc5ce5b1569f260883a1124af89074c7495b9ae137850e6bdd828e789cbea26105466104cf28e032770ab4c8df60fbeb486969546619b746ff7e28effffdd22d25a1542f928e91b2f38e5f11a040ccf0afcb869a01536465faf253e33a3423d1338235dfaff951345475c3d40e00797440190bb6cf55c170a8b094765d5766b2cd227ef17b530dc8f213753456f02724e293d69bfd28054c60cbdf427773691505e9853db6dc0ace4d9b5bb357f0b5ad077e0a3a9825178a2f7c8774812df50f4b73922c5841a9b6c8309594065052e7348c5518de5d46ea408ee46c9c642ea724f47b25aac3bafa3c8db12b6a50ce70eb2f084f092b2e147db7c70762932c744d7449bf01a075103a8e6c250b0013f3aed197818870bf95f7a5643186e3791d79bfebeee88dd87ab9e12f8ac900ec61798424ce02824ce83e3a90ae01775bc77157176ec371755f3342138b54301c2a99a1081557b59114d028f30f329a780fb7f49c05894ca6a87a825ea40ce074a1e9b1e5c92c43ee181d88635c13f9f316fc7793b80b2d852c2f1435c46e7099a4f37ef8a0e79faa6695297988b2bd220c6b0d160b799d3c61693c824af2f0667043f24063cfe9e4c67c440f06c92e1cd58130d17cff279c39961cac2cf9c02deaa048818908350b3e615366086204640e768fe4a229a7c2ba530395d17bc0521680d50484dc3686adddae4f28a84a6e4d9ce51e0077a8d5b951ebb9218a109fd5b96bc0fd91d878f19f0fbafee140f72a3bf5d3a26fc2fa73740d7a38a770eaf23363402511e8d176c6a80675cbd53b587cfc183434c619214f73cc752f6081828d9fe1d671e855693282cc903f26e2235738ed9f80d66cc4a303a130ab7075ac62c229ccb9d4a1721ba7b098be6d9afcfaaebe76544b9ca3b567a5b1db40bee5d75edc6addeec0867bc4212b019e855468ade7d9dfa8055dd54817193ca0e8d71da78a2e65e44abc88f7036bff7ce7ecb944021b66dff7f51c73f7a766b02f16fdea691f8c4a62428fcb4340e53664a2fba9413e97d33a06ad4fa741d7f08827f15900061ad03b765737ff7bf49a9f83199dbe945660c9104b7236ca905006e54931e48b5d19e54a3e3e50562ec28caca9a4092dcaf88e6312fd1c76b8601ade9a676568b89f48996ed28de31fe66be3fea0409ddfe9ff75f722f26717b0e2904fc696ec365b3a23811706e5e8e87bd02624ffebbaa6c9d209f425d81e101605b9d81f90cbd547ce17c622927b0e336370acd550eedc46a41ca2e977dd7097a1ad8833a1d4e2c1ac7fea0229318b8663693d1da1dd5ac376d204d4af8d50f9496c259c644723b85ae111beba29164c2422a8f4dd06ccd497d3682e111860500ed9a37e187cba2a7aec09896fdb23667c71143fddbc2500d5529496359f70d42dafcefe10298e774995cdfe5eff4605f27efcbd6873525a6f014c9406f8b02d75b1c5e183a878ac4bce0617a8dab3323a96721de858f084f1d4d69fff78a0b10fe1310898f3327cf8ba2dc2a2f2fbc10e052c82cb0d97b1e260f223fe9450461f35cd8b222b99e4048559284e8a74ce7606f0c51c1d528ebc3f362cd43200c3876616f13be1d8fb807f726ff3b1e5852e91d4db52da3c818ea3b809174f10c1d802a856dc69b38d0bab78229ed58fc56cbe346538bb1d305ba38038932d90c1dc03ca83489829ed6549fff01118cb3e4b2cb151147b09742c7b6d88a9d2608653964a08aa03b7f91e380635542cd1815d60e4f1b43016b6e9101f57535590627a5ed5e822918d4e73a98b62e7079b5e4bc5c7e025cdbb36adfe09c11adac04b066cf8bb0f3e3092dd7e06e65d1f70eea91b35d4381b856a693b94644f8ee0437295d3ee5dde1f0c981c31345236021ad9da096ad720643ce011ba12daf1f0db352414f598a29fe23c7f035af9f8c88de2e2b7679479eb4190c5688ccf04104fd09fc23407d56a9400621a3d6a98fbcf9b823984da74f7b6ffa482036bcf40682401c3edbe2c1fe0af2bfd4a761b2bacb719f4b7ad68589a4b35318b7db7105dc19503fed7738d3e1d72c473efd2b3b78a368678ed8b85fa730795aa145370930f626b5d3083ea676d55f959d213647d39635523c374f1ca58f1edd524bee02ae372fbf7cd88ccdb35c61f8c0c9d66e1551e394510be0c975a83572f06bb2074f7f25c850c73ffce003b4b9a3bfe49e0a6b6235d0db4eb6288397c27671cd0b0e50901e21478b1536bd26364df05a5b19b1493eaade39dffd000e4fd4a72c0144a37724f3dc33391dd4d859d0636b501299cd5501803e9a1c3a2e330cc0a40d5808975bd0c78e9159961104699fa5431d589caaa5a5e8704dfd30ea8006040b8df23817da4063ffba4d1acde38081cf1dab88dc4c18638c8fac78f1e91ef402914d5f8f25e4f37b65bf28820ba1be10a41cb57198c65523591a1063f7a3d90cd073c6a7c1b7b288a7833acd4548cc5cc78281e15dffffab25d6ec82bc4bc6067024846024aafcdae5f9507473ccc84a6beb7c0f37bc83a98c87507a4c3820011fc001cb882f03d29f39e9f7d5bb3c83a1377d9e76d99369cdb850f3107c6d020b08b398bec0840786c56fa1ae45b0cbd2b32d40ab1146699071b0bb963ccb033c8be62635880ae194aeddaffeb36367282c15f0461485b36edbb000d4af65051a7da76d086dc1cb39d91ac49250afda73bea9e5eba6836089fd083861dca400df6a3d3d956ad52b35eaeec8c88d5271474c6f2606c95b85eb141cccd214b402e298cf6b5aedbcba3f8beac391477fe318d6b081d4a7917e399e2f07a713ff0d34adb7372ddfe66f15d8bb5c381a35802d13123ea0c2d27c391e5e54d916960478936be87ffdecdbd7c07b98778f4e4b6cded5e8d91b4db275397e855e983b0d120fd72ed2a733153640b57de8dc5c428c55f2fffd83c58c00d30071856e710e86bd2dcf48a6ce15b110422f89477b67316ccf3248c0a134cf64014011a27506b6915ebbdd81a0fe4177c438a45c3bea157c748309aee60e1586b2886a81c00ba4904f015d6942e4b79b71851c36cd14c83aff562bbd8cb89e9d924d429ddf0fb251135cc3b8d87b8d9afa2d698c80458a814a998afeb1dad4c19ab52b9f1402eb238c4da808af2473ff8f2348d3b5d1c0165f6269b5cd730327096a4d78f20bd40dea98520d17b23818934703b68074bf58aa724895e6954b747adf591a6a0afbd66450510a1336e87277425527e74921d0d3c1482b34daf68b4cb70988870b718137e3dc00b7a50ce9cc451a9752c3082b91c5cf6190034bee3692ef090b02e4d0caf7aeec1ed77c460f2c51324f81241965d89c16ff061bd7ab6eee9a280ee1663bff50157169f25ef58862ad1400689a73dfd4c650c0b6ab7b8a19781802c292dc79242ec0985f248f5c8e69f9ef68e82bdacb9a00e786c1de154723810e174a75231703104419f746860ab2300d70ca9fda65085d02020f4fdc66631d07ca8e56238ed6fce1f5835f1f050efacce463beef2d745956b5adf36abb349d05227b3e4883200757a5cf92fe78c920ed0203ffab2376a298442162c35360fa09221204d7ec52882dfc070930d9ffb9ae8dc1b87dbb4581fdfb5578540504300e0cf0ed7bacc34ee5f0df87ea75ccd8fde64557273840e1843dccda843a095e0356ddc1a4f90286806539bba6071531bb6d9a5a61f6d282e826a5a8e3df1c6f0898a1a48271b88d032cf750d408e43515e59ccfe7c61016e2cbeca98df995080bc93e95b3d65e5cc0cbe340f49fdb6294cea8b3419b0c38ecae4fb33c304fe009af9245e14da18b93da8152dc3d2d3cdc9fc507ea15722bd914659f12f3919a07af7194ba5175a51e0526f6a052b6a49d362b6080b65cbae0e23f3e5d44265a0f9b5c1df5bcea94116ccfdf8f58492442fb9a4ac207aa93eed607edde571f0d0279850902da35b0dfc945a26f4b9089b3d5d35b8b1168020243e9663e0343bf052885b5787ae283783e9c33f570dd68d996a11422332e366c4485e34dde7b500763834019e83ae046de8522978aacaf59f9a3c52d281197c36384c9a56866a50f5c78888aad4c3726eae5baf5c02755bc22ea74e2878db979e962027f2ebee508d3b8216ec47f7790d10bd8c302b56d64b4f3852754b0729f2bd59b771a191e019e30109cc5a1d5ba88e3c61501053ccc529d53a0b13a75c920d271e5410d5d057587621427a74efc0243a1c2e590daa25ae7532d54aae99e20b525438f7ea40db18377d4c200fc9398eee13839b27877abab70043112003fe8ec01c3f07be10bd346a8056cace5a1e301fb08ea651c9678512845a00f83ebc1361e3c2e88b70052ecb64ce86ad1b997f67f3c1eca414bdbac035326fb1c4772fe2c31eb3fa105306aed0ae8612907fb1751b5f6e7628418878b0c11ab19e691dfd24752830c05e7c047c1a5f910b6b64f17272f4912778bd18604525f4fe14bc88ce2aec8930982d222a7451297c092c7b1fa6bacb228c7ea9b14505eea963b8aa59da1d8fb0aaa7a1fbfe41c4e19764911ade677bb7f2dbc5d52930c98241a66e8cad7e035059fe794cf3c9f942e509b60baf6327b1d57129fd937a5fff1e8dc25c39ce7c8079f00b37dd088e992176c5c2c76cd86e58c22ced7398d11c52e2980af60ed9c0b82dca01adc138f8122b1cf0413d364b988672097bad6f3ea9ab2beb54bde0a0d65f0402472b252a8df77b743a352b3e350bd0c737c1128e325519fda86c32707a2a4a728b32ece6e810bf849ad415f4077f2a15905b03bf3670d7db49be3cc07a9581c77012efea0bde7c8aeb5b9408fa1a5d3fdf9ca38abf4bce57e20f09a08a1c07947961b62a72a515447f0c3795292bbdedfb46ce0491d5f596b87d1fe0f6d871b92a9c2e987fb65e5cf9ccfd43f0889f813a10cd933dc8509bae346b9055e284121ea27de4cff183f5bd408045790e4cc332ccb1397a7511fc0ae50ca037408b902bd8a68fa19ddca2f51359f97707566612f545e27d48e97c63bd2250ee7a4bb770941cbf6068cf648a71446d7909b43fc3dd35c5f5a23ef2c2cf23e08794a7d4677a840ec77205b7cc0e4d9e8d2b2ac2b69e2dec99e283dd9985003084eb588a7585e4a6a98f10d20ef3522ae2e0626b5b359b8361771e9cf6d4cfb08a5e3edafc893dc7c56b9082b24ec1ec14092ee6b5d8f90ef423ce0f5e6e15f0331f81d3a34d260a7b3f8a657404244efe3d0cc2a1fb823348776528e3f1eaa081742c9a1510c9b50f8b6b46f3a052c2971768ea3fb218a20a28f5fa6e42ec2039e433046774444af0475a91b094f2a714f1cadfcb4ad14aa6838b8348de4920faafb107c67af583f0e0c856b1aee4a9e98b133f745c659db5941ad5a0359bd0a2b88a775088670411f7ba49399bdccd938904d19bfb28f84cdb5ff05c1485400e9c365285c032833dfb69f3504a2983edcc5f3b4919a16c23ccdabb9a319540aecef6bf08473ed5600df1750462adbfcaa9de5971c764ae0b006ff8637fa870ee132e2a2130818439c9b90373ee2a4d1ac593a062fb59bde79571321c4f1180389195f5dd7944f3d78c19e628eb21b18b660a934d040718541cbf9fa52558f086e57d3d80137521c6cc78cf551c6dde3a4ec030e0dda105daace73f0894bfc071f708e90fd4263cf2795b3ce1bf35da429a67c150d96a7f5709603a7826a170c39e96a2679aee43af4c058c385e99171d7c5d9126308c97f7a1c9527bc9a2f0573b26659512f78179c7d0edbb3d97fd6188907079b51f5a660a39163b9523e0d35edb3519ba8c76f86e1c315c0a44e3278c837c69e31cbdb9b8bdc5d95bce50987fb71205b77050ca0b1a5f16c001b9fc4f73ba07c9da74755db5a9077599c07fbcf741d71491b4242c31f10bacdfb1dbdacb19b00f2d27c4cedb28c04281b0b4eccdbaf08550ea763ef100ee79968b3d5861024fe5345cc602c6165f554c007bd77a65a758bbc9aadf181fd067ea474d70d0e3bda6e3064d8987c9a83e9a706105c9715069a811f9f673ae2d5d2c0cc38884da6c30e200db89132838ab78607ffca52ec6d9778601384caefad0958fe339a9b9bb133796ad2b9aeb222a1b80ddae2fa4cd166340bbc3ecfb36de1c0fc67e11fc7f587cdd3f239b1af4c966a0a56e6a7e63c42d1728ed2f3f907e98f344f6d158ce62340961431777301f5880f4c4bc97791e0238698049212bbdc4d02714d8e5a000fc2de363354958c5e5d0226645894ca88457d1511198b61c18304dd4bcbeb8774626c6396793cf0dd5e0c167e62af21aedbe9a4e6e1920ac54aeca78d2075b4ccf1d2c07b23fcc5b5a706855b867aafeac8f27ff44ced0450bdb7c238f053d99345bbf95e08bf0e6881041b7b1cdb18b8b25c023c5c35c5e761bc91d2fe5b44336674e5091d05b8b1610d5281eaeb26ab7dd560ab8c7e23f3d195fe8f89272fc8ec51a78cdc458e4c1209998b95a93738e9dfd8434462e1b011e6a19402d54b80cbf57ad8cfed1814ca0d8c5c8b8eb144105afd3ae6b4252e9a95dd7a3a6e9b920268dc0eed01f5f3d90793db1792ff2bc2ad4434f2f00289190ab1ab59b8cc6ce4c3acdaef1f0135c209b2e01f728b6575b1e229b5dfc0b236e72a33ca1c32713c77e026f4f41043d9023f5e1d4bcbc3834f7a3f4e8bff8c661c2f8f2c7908e262ef5063f3f29e3ae602d2420a2f014fe623793b168c13a9b74b4da0a4758b8e434e8ffdf760363efc06dd9f24a374539a99b3a6faf03ed130d39544f49b4a3e7cb514c3d02869da2f056839e3de2c9d4b6d713af91cab020c98cd6b853f60d917c1a63ba2dc10558e0241f65b6d141a09d3aec3140e68ec43a7dae8c79ff86444ecbb1b1da2adadf403c71f269aeb3e63bd3759666e91c1d59bf7e29aac3d1c2a01ec7d30ae8ecbf9073bfcc2d2b4bc57bdf2ec5ee1bac14fbc284b9e9276d1a550d558bda13f2c650910b610e1297859e8713660ffb65dcd8d3d84a9978e5f056b81290d52b96a930b6435f5239b616c992e09f5f9f0dd9a7f19ef34f4b59dce2f18aa1c9fe0417e033108d6f4386a5d0c8a90190f8ca5fb43a6049079a6186014f108dc94cd91ec0cebdd2af816efffd71c34bcaa1a69ef65978c3bc86b882455c788bdc0e671ac0424d2f967991f2af6c7ad77e24a0dad5c0f78bd311abf837f67cac255878d4900b45368680bba2f84796ee60addc84911465240c94c1fe39b7fc695efd070260516c90d3b72c67841686af0635076e67b5a3acbc3b36b7e4ceb452aa77b496c0ed05f9d62202eae4fc446ad0b0d453257bf2aad4102b912b2715f75452dead504b1ad2a572a53dea1ad370d53e5c7a77653f17b42a5ba214086d33357ea88690f10b78d5d647129fca84a3cca52aa136a6af2009e1a5d1253ec26d482137a4a0fc51ba2939dc19707d6e168cbbab223334b33ab1582560abf907621dc525e100efbd447693c38cf9049118b6f6e79d23f3705e72efd0bd3703d18a13c7389880bad039f5ca2024d8e1b2f8f65ddab4c3aba1aa1bacf5289b511840bd8943aa40262a96bb19f2ba84760f81cf7427b2d6b927b8de486b7c667772c5e919d4f45057678f9ccf38fcbca6b018e1a3dca42891891eae9ed7c5ee9cbc884c32adec003fe9351968548bad94aeda6300a877cf3fe277b91406fe3f52f998ae936fc960137b803d81f35c7562c109cdd50de0a012253b4d70273388e925f4b74ea957f0f5a062475dccae823ec51a45e6872468c5ab625090ab48485dfe448b530da330912172949c1db5c8f194269cec3247c89f06e015e6252dd1ba3514853ff6d6e08e0a09769d5eaa19d4959e070036068a6a30074561b38fe648d3bd05c25751602e70336aee0e3ad1ac756f466d7302c2bde86dda0aea880c1fb13225997d049035b97d684273e8235c48aaff45a7da514cd8fee72fa04d18fbed301c3d2a5ca0d714d8a882b3750a369841d73a9d2e49c7a787ca0d3e32bb74bdeebcfb4ee1f0a86b8a4e24bf08c295b76565ed185c81556823724ec7c9195833ef3408fd2650718b416a1c843ecb70bb1043e417e28b9bc694cd844db2dc4d0e169bc241680073aa6c0cacc2dce2c88204895c245509a4f33ef737cc3c2a45da3d9ce8187b80d6019fa35361d22514fbf5e10153143a37e724e48c3b7ec49a11c3259bf1cf00e5685edb55390f538c040d1f2ceaa1d5b251d9c2a9c0efdbbc15762f97c60760be32ae2ddd0d8fc7a518c8debbb018d2e4b435cac6ca1650d540e5656a9416a00b0501b78b02c1ec26a074b9000d2d739a71d06ab2a6ab0b38d3ee5f685bc310a6e830534c0ff01921489e174793bf27c4b82a5dbdcf47ac9c1bc8242c72ec60d0fceb7186f1260fc3107355493abb4f406efadcbb7ec408a82d9f475281d840ed569098f121f0260d21c2b64a39879a7ace60b8527121473daaeeddc0655d20cd40f4506417e91149df7846bf0c2e4aa3f3eb97d36313db71a874be736f549087fa04cb98eebe10acbc7af020554e9f37ebe08dd52953aa52b1e4cb7a76ff80782109faad1bfc11a66897b1fdd21aa837b6eba42141722f0bb7faff41e1943066e626d5767d789936d7f58bd68bf0265051d653c516e9bf0a35bb6ce74a7de0d5f05d0c1869f5710f0217e21423be10fbdf03276b2efbfe8f12c7a379a5d76040d5ec8a1cad3451b0437b78b456b4ff212adaf00c3b68f46c196f488ab3c520092283a10bb7496e68a265c87f1cdc03110c034d1341bf9037fdd4752e3df7c01d24dc2b76f5dd745d98b768bfd023e3fec949d662385598d6062a3946aa65b044e6702e3299e5fa9be168932680acf8115ba87b595ba776d10938feda7f683052200a88bbaa9eb8b2815c002476149d09dbc3bd3eaafb3aea81c85d091f5f20e08a681f97181a5bcb57c530e494a3a49e63b4a9c734b909b6f114ef195e454008ac74f93829e95c4489507e63872cd536d2177cee6306d8115a1491072885509d2f8b41331ef7eead09b265e0ddc434d2666c1e63b5fcb5d0476abe5163d7609fe736fbfd90a9dc4e45a17bd58f2b297b7c7980120b3d5c7154504ff37ce560ddbe6bb4fe9c9cc36186eaafff3df756392bc4e2a4ed42ddfa648424537089f0f5da8bc93184fd6d8a5521daa793902064f2693756a6b0c08d6208979ba35a406684f4ec9f25b7a4d8827ec3741923309f1ba47cb485b6c21bef6bf8e914fa4051f91c803d7f394c0dcb4230655f68786aa79fac6924c50919284c6d44955d9052a982dca142cd88585663623caf83a90ea88887f54ebf709bef7ab22b8d698011ad2165dd9455678d4b5bd463afa576b32602d4781d4969d608f82045a3ff0052949d1b70e69acef81d03346bf280ac7598b5d0680a04cadde14008fae7bb60ca134cdeaf3ae9698949f10e7d9e25966ed5a9936bc8d2fb293f79f78348f2d02d0c0de57f0fa0c24838449aaf0d2b33950ef25e521614faea1d1751887aa960e3bb66ea8b9bc263bf21982f12be2e037a47e43ec990bbd36bceef15b20bcf3050667552e575ca4e4e852f6bb0c1f56560a54c046c883227052abf60d8fa3cf09bdee2bc6ca1514d1f587f940b5a70f2ec380e626eb04867a58844efd759a160f18dd96d67d8ffc6764b82069f587474440b02fb68cafbabd8727f912f3528a02e7849ab5a23d56c7d280f220fdacff7eac910c174eaf00ce68a7f4e8289ed5054f088306c229c707e19316e5734d6e2e35d4bc8d12042d1051060c063343080325fcc8efaf8394e125c6c875ff3f682ce92281cb87fb1a8d69a40bdc2e89f305a17430e90b14cb57ff5aea843b1dd2ac9dc195290050aaff2987308a9a6a8d04959009123a17f505c84378bde22ee025184d7083cab31edaba183c7c53c32d0f03d0fb3a5aa35c98537070d556371c5255d3b7d4de605dda1acc59fa1da0e00d1f2c07e5cdbaf39019044c2c2079e238577ff252f8b0f13ebd63088b90643a0489aae7039b2d5d1437c861b20cd3b5bde17142fbf8b6efb3eef9c2b8d8db550df559f3129bf05efaa03c622cba0eeffdcdeb3aa718cb36eab9cf4ad86dce660dcbb1ee119bc514b4a8ceb5e11059fab04a4b43d17bffe321186f369350431d0b2c32205e435b20c7846145b116796120bbbc674c703fdcf1eed9b48d643e8603a80740137046411dc6a2deccf3ba031174d341a525506ee03131a6eae7944e09fb4f0c2e222c49e98abc055c3c529ed8b7f6e8cf38d42222cae3b1b209106a0656e2f3cfed125d4435e4a5f335e08b5e039efe0959126c4a24f014d121dff00289be6e8681404fa012e400cb05ec034ae50c9593f0cf8179f87f85daa532b808633e7c351eed75f1629f22e72816f85907b7aa7542f520c0d0c46f914bf9410fbec9e3d6cbef3895be3ac61534c63e084678937834d9ede7c1742329da2c9c06419b0bfc3668c021ca35bebbee5c8a902fedefd194f76e1156d6caa159746e096bec94d7f68fbe4ef345ea829c37f29fa6eb6ddbe644f2bb2f5bd1e0515c15004f93b5f8d3f9606bb9108343a0b1c6967eb5ecc91cb048c08b2de1e71cdf3d0f9d1899f684b3bce60745885a6c8dd481440f4887596ef1502b30a55db6af2707701135694f94630c09df3780bfb18db0fdb32eab034d316d84d48c50a04c8d020360e41cdc44246c9817d4020a02271b6d146621f49e7b2fbe6a0c1690939f044e2e30eebdef66e500652240f4f6078d6044cd53331ebf4f0b4981a5f276e6096ccfa176ec9a073539528531168f978013ec53b4ff022374ee301aef3b37bf0f191aa93d93d1c739224d29c05d3e08c115df502cacf10663470679253074e30f49ad92304899fa7fac6cceeb53fcbe7350e4550367272f30a310bad5db0f910f636bb199ab74d71fe46bee87812996806aa3a05d159bd241f52eaed7bc62a20224cddd283ed5ce79a1b736a8ce4bcfd516fa4ff65bf8252ab088dc4b856fc3098457e084cb573d76cefddaf2b29b6be04dbf9b21899cc71c1025ea110b28820128cb101b6be26f36f9bff0e66ab63c809e83be21d76998bad57f9b0b7d376507ec77e183e1ea1379883ca6de2b28551cea829cab8353423867120e04b281ed0d10af2a2412b7ad51285de008590e0878c243152e617015be7da6f5b9775b14087a3271459d599870cd5158a677779fbc0d488728e1535bab04d8103997294500ee5b5eec8b99cd1417c9a201f5499a35cb9680fd71468e55f0bec08f9f85470394953b2344af4e4bc872f4d33dc9319a5fb3cba2df82e33c6acf04c5704406092ce620c238887a519659b61b3fec908070766cfaabf0f51dccd8d2988aa2c70bdf93871336746eb78a13fac85ba2fef6f9b48605832db8f6164f53a23c76ae074c53502e7d1ad98e6cf460117bc38e3ad72fff40edd9371f7f7d967118af740bdd1037e133829200420e7c6ee1c6b98991ba3e5182eebfd941920859445eb6072a734e3e5cf2d1ff3e8bafa25745041017a85e77af47d9f38129065cfd73690e7ef9867359d7445295e78676284763cbc9c43544495e578358f0d8f97a8cac0bf805330713c31aff24584b99898fa62a47071dd139e3ce1562c19330b314e109a5ceec3448ace6dcc6bc9b30c946fcb23ec809be21729127f0ec0aeb2232a3054b3b17d14a9f42d0edd01d3f8ef808ed0421f0067e4bebb490ddb1aaddc6be0a84667522eb449c6e2499af02a62f8a772633417a3253ead773293a3a12d7580c4413e699fd08cb03d225b664f7b74ad93f7ef3105329e4049bef04baa90a0f0c87c3cfb0239b199c9dc52e0acd0cf3ab51f53cb00f0a37373b78d2d9c1f98f03547a8ceff3018b49e02296bbc49f5d72f6655810c4aa7f3810ebf79579cdf007cd035f544ecba5f404890d90e2e0132c8a1e0257f91aab53ad91f827ea3ff307e696e4644f5939b98f11350d1ab5bcb74211c067a421841d1a2fd014a8d2560f06e58d5e512660db140f63d54039e310f928c274bc9efe0d466a5882cd2b060891b1f582b7246045da38dd04f26cce128dd6d4bd4ac8a62e7749f9dda5318f0787a8bb2f4782fca0baea8ab09f40c6a0dc962522f853e82e09bd98efab9850062caac55831aef3efff90a3f0cafdbf59844a0ef5857a31242e81e00baa6a9d0c8e40a4e6d10621cf3ff0328f34e7b50156ad9ec1e401dcd8aa381911ce3e300fde3d7eb29a141508943b1118a1c42ac298f505eedd74fe7bd1aec5f332a6ee03e81ab4b68ef8213c613dbfdb93e574736a8287f23d8f1cb2bbe3eeddae3bbd001c65049539073a4f5c2320b3b79e58e53dea16632a99b2086af275a8e892e90076a48dc6364762c4e8e580922b0c5715e2504adf036aa2fdb05bbe312fb72904485c9b957035a60f3083720ea0cbe51f472a210c1289c14dfc3040d94fd82502b33f30b96e14c9710ea929e59d320345e49bc3e08077a166af89076522dbf30910b5847b35d0d825343c8ec73015c02432212743de14c7b0e6c69bfd46716e0ad40ac274f6bd1c81b87b690fbb4324e207fac6ca08d35d913daf1803b19843028dd65a7ffbaacae7a26f834ec1c5963ebc637a14bcdac42c561f1fdbaaadf10da73a777135eec29548b5d828e6ccc4546fbdacd4cc269083a3a0604b5cfb640c8a3d72381d22084400992b1fe8d46be3e05f98087954a3565b5baf61e41f9407bb20e4b80d99d845bbd7fdbc07f2db22f46cfc7d07495ff2e453b33f8bc07f0836c2034c7cf0a0e5e88d424ddc68c437c16044a558100dab9b46e144c240f90ca4e9dc86b87245738f0fc3749b39ae0779712b80f5c7f91e4165093575f264001926d4ce258e923355eb348f3a72ea9ff499b2697b7ec86f0b67a0baf10bf50d4adbfc323a9bf7b06daf1e6a5ede673d0c9c42885f494a6dbc78da372725e608d3386529f9cdc0eec72a122a6ccf931faff125ac083e8c10d42f5a45c6ac820656a7e8d884990d39193dbf4801b4f8691ef71a304035e832246b49bd11ede602276036e29d72fe0b7ac4c8b442cf959b2d188008e1ab8d23b2a1cd27a6bf1b0c88eb176498f9e79319dfc47b1e0e2a16dbe994786bb796eaf2ed6a09cd6fa7050b6a6b84aa7881f6112509d515334d1c3793bb97a9a58d37fc28938e1a20d3078fda411bb1a79bd1bfc94924e0f54ce33584fd9bdac13a5f4aa483177d388f099eac5ac980d23a209822b51c746cc00378649c741fbfdecc0f20cdda4a9b1a0c7aa3897efbe59ee9d589d416ac1cdac2bf89ada1c37232d013ccd053950c3507a38edfbe52727b98f60919ffe20e9df941b7cf1291e3321e3bdc6e6264f4960cbba5d487a669ddc9d8bf994c1ba11a2a5581e29078d5ba1d6a38c0e4d9599009e60cb3f305d8110cb46b564b80a3c98dd1e046c412443c598d2a8448efd1e1068fa5b7689fdbe11c5a52f45bd3b46b63a0ab31b5b7f51f70744eb2702e296b019cfe71aff205a419d274a3b5438ccc0e8d1a6df7f7c97506bf5874a7e13d23037f3a285c7288a3437b28a150ee35910f051d84f1d7bc8a2dd8522d4ec5ce030a0bac3d91b0900a1a20a573855f3a64234f6ed1e5626853f4fa3245d5c3b09004bf462a4c14d053c8ffea37f881b50726d2b49532c51360f8359b56949beee2006cfc6396f1d15df5420b99565c0aa5ace78fa388396f2acafc4e9edb1f7fbe0e79d3cfbd517ba9d25ae4c3ba40fa6ab0b4b4b121d3cae1012e9f61efdad16a0f75cb7ada8a02f9ceae4db624e9fead44e366189e9bb9c46b48bb401a08b944020038c2eef5efeacd49cd778c0d116de7a9e7c79ea5a713c6c7de76408967e90412ae1c0f8b3c48db8e2316e5570fdddd782034449533cbd801361fac92e4aa000477b6668e995ff0684947e3890bcf89399fbe6a488477179499f84656ba650d833452f07c026c8570a8b58c674eaf80014c3cb8155b3a30ad35c205b8876b04b9c66a22af4a62fdebbcf944eb7cd20f0ac95e66710ba6a5259331da6890ae0dc9bac31a7f3ae53f99aff6b8295ed716ad08486f88012dbd25b0a4c7419aa7049af54b78fee7f4ddeb4d535b80160c7623efb775cca9276079697a254510660bdc25831e97b10d0160c61abb3f1c636fd5d813d2543beea9574882b1650b47018abfb8eda65b2dd14e032bfced62aefd7c1e247687fe0da6999b6c1981ced10f0a3b4996dc596104dbff63b599dd1250452f985d985b5797fe8ac3358562da0c547e327c0b628f7cc152808970c86b4d4a7e5b7256ebfd68b8696ef5ee879b09865d53046c2cf460b4b2228bf63decd574a111429e19f8c00d45111ba611860dae29ac5eac6ac0efbbc2d75c46e69a5680f81cbb8cb28682f757528f0996e805 true +check_ring_signature 7c39e57559bdc13fbca8e3f8e81ed4220d72709b9da80c95553c925a97fe63c2 bcb4203caf16b7f011aef6b7ba74c4927ec8f6cadf39e32e697484714c064cb7 1 1dedd621f417f439433af476efdf3f224e5b5012a06d8ae5817734be73ff7b4f a2e0bb62b12914dda2ac44ca68006d3195f2daa8618cadbba1a40415e68a5b03424b76dc62444db6c79e51d2f9506134e2df61555a8332f5c727058e80d4ff0e true +check_ring_signature a8f6bc340f8351d877ede66632baabda881a32fbf974ad0e0680498572c7a21b 7a27a338bedf083d4ba3572607c96974b7590d34690be601b57175824e950369 247 305c4f5b7df12d8eff0c1d7bf5d6967fb4981889aa43f31f7a65d243015e83c5 5e5866770a3340f153a88e769fd2f2304bd8541f291b227963cc911b02bbcbe3 12fad09918c0e030ad9e4c8d535121c23a5a4b9d47e8e56915ae549410ecf4f9 cdc436f84ae8f785ac7f7d7dbef96260df52a045db80dd42f25ac32c99d7cb59 f3a2d64b81174a89c15f54ede06c0fffa67554a703e381dd2c4f50aa7f5eb2c9 251ee9e919087e44a3e0a9444c66747f49ef319e47fcc6c9a3f41a9cdd8e0de7 866a9ec6c6c741c041c03730f327127a62a8733f56ab9e7e182527fd53490453 280d87d1120a628ecec4c5be145d44d1edac6b73262194403a45ad6daeb3e93b 19f8b28bdc183570e36dfc37890e0e7a4d68749b97297ab48c21bbb90dce6acf 653f6351ef22176ad5ba9cb92b00d6a1b0fa4870ee5408928f4676af1f537ea5 aba881beef2a1ce2754d7b542d204c3fc196f215beb91f2ca69914c7f58545d6 b18091224b6ac608750d96b76f0c4e8588a666652c111ec1cea81876e44569d1 ce5108f2a0e23176edb1cc7882e4b5ede75a3d6f62df495d5f43c60a24c3a387 d4ad962d4209922461fad7bfe14eb26b3d0ddccd743460dd4f2e81d2ee6f854a a2230fae0217c8ff247931885cc3a4274a3df5ab98e058fef2e29594ceab7c95 e78bcb465ceb69b5e738a864017b034febb29bd96992eb71b02004bb8e6628f8 b2395a8a75b03afa297764f449ed5bc879f52df8ec7404c801f12aeedfef3b8a dcb9bb5432c1bb264fe48f1f83d6dceb1e4cfac0ac45402652293bb6c25c2327 be3e7e9cd8b1b4b8ac14596f36b07ef8cc0f804550f939887c8f5b2767f6e91a c8f5897235205d258358d0e7f6dcaa81bde60711e95c5d04746d12282675b1ab 0d164e5dc9893a6b13180e8a6caf88a2bd2c54acbe6205e3f992dd893fd5c3a2 a60af58f1ef4c9fbc03e96ebff5f2fd51075f16993bba9205752079ddb41bc83 2e0e43d1cd69305bae1ea7df87317421f854522aa2a9166e3deb26be9f52aaf7 8775f9be90847cc5126c2fdb0859e6ce13677c55b4fcf883fb4deb6959b51c0f 6ad5038dae60b7242f8b72baef4566647f036ac58ce76b87cdbdf8c94543c8eb 886a6b4bf4bf51fddf8f0df6f0f1de89f360a4e06359d41b727d9eeabc994e19 e51afdcf3181c2558f7d51d14d889b554f8d177b1dd953bfaebc9c2208966f3f 9936b600f470232a53edc2d9a6bc2ef5cb908f4b40757a58a30cf6e6a0b3382d c36a21ab04b8cd22a6de1ca58b75e284a0fad04f209256a3507b757ff55ec40d 20ceccd88c72dff504fc86aa5bf74cd6ba43b44e871cd010b80d3d877135242b 33eea18d6cf9e5572b764df5c16b7b83391da4b90eb87ff28807b8b7e84c3e36 7564406542b6eee903939e1cdeebd4ed8e44ec7bbb4757f73e464815bdc814fd 49edcd8aaffa1fd340e05aec6779b1fe6926485fa36edc924d9f57636fe63655 d5109e5804751f978f51c9cac2067c312980688f670c6e20770da1ffd3ef7c6a 1a47d113ff46429ed92c4624a352dfe82817dfbeba2b9d56f21fbe68e9580945 8f89070d87a8f5e15c1483de4b62eed41534db5205b4174f49d74c39f29fa91a 04afcbfa77b327baae843c0aa36e0bb0431c5deee190915ff39e4248905e1ce5 0e01500394321c4247727b0029f4fe8cddbdd630ce28b485a3ca3fb8e1fae00c edcabc7e12be1047891443174fa215acf758f660af9af9e1fb42ce9956f9ad77 9f8b2972099f09836ea6219edabfea3e4d370b4a2ac0905923153d927bd3ade7 aff61ec4bf29e508631195ec7ef99a8beddcf57fa0d344296bf2a4af4a2bba1a daf40574a196e06b286695644f0d37de2720bf9a9c5b9069266b99a409531aa9 9b69559b54adfdfed86f4b7d225b844c9b7526c24cc75977af050c55c8e264fa 6d65f96eb0db3486216dfe3d2d48f21c8a961139ed09a84fbeb80a030f845e80 f80a7571bfaec2a9746ca4f81094ab232d397536531d7005a332a9ff522f7899 552ee963503ae0d4b97faa4594afc1f76e3ebd48a56e31ca9aba206a462bf71d 8092e8621744290298ad16579b28ced52666b6c2315b80402124df3cc969a250 ccde8183a75772e6d703204ec2dd6139649edb9cc255ea3606aa0e7c67b4b525 73b56ac5d25f3872365fab43bf4b5083ef8063b15c2ce8235a679953be875bee cc6cfd9a0a74210366f39a9030f2e348c39e89ca5c587dc6057f630abcecc297 de27301a98ee5bbb745681a27d014a5b716747c0f0878493ebeb84e41cbeff3a d77372abc423bab6aa4939e99cde534c5c09d4c40de53a4eb25fde2e859666a2 dccb216753ef5547f1f3c134e539a33b3aa86dddd29fff2149464eca191832e0 64ec991ca90bf5fbddbb6478248d23aabbaa3af4c18c0b04ae59a69a13712484 b48118df3e13f9df654815f83032fb51489644d6bde980b414986a8f1b441a25 7d5e3866524a1c16410035eab01a8606fd63195105e1d617838092483459aa19 446bbfdf54bdadbebf2fead9a8ece4de31beab57a164e9f9742c28bdd385b5df 59abd18cd232fe32c269ad13a42a729d8f7fb71a47ffb948ebc7661e0709cfce 930364153fdd5b6c94869984c82d71f5facb6879af6e29932d39ede05873ec7e a9382ea959849753067a22a2178e0c410a67c8dc20f01e58230b1b4eabfdf6f5 b2a2e39f3227ad16d4bee568915d859c020fbe637e257a507421646c6ac7bee4 fe8f749248bc0c59afe0e4d0b11d95b978e178deddd327a22501d2793184af64 1e9eb3e0588de323a40ce3d421846c4d8da2d4d668192b098681f79321948cdc 8bb4190ceedc7d51461c6a2b665f94c024af57dce5eced90accbcb42171ca8e5 56e17498b29a86be3796ca36e63b86edbb06970d8c4fb919cfa1e96f0c34e217 4cffef8e39eecad2eb7ba799874dcafbdc0a56bce79c8f77e2635440b401cf2a 8b4932b93ca87e15801ed99f8fedc69ab95d4a186223bfb4b5429af4747c6a61 4ba9ec0cffeaf07032b4ec9253a41d06956620a343f2fa91b0548bbdbe1e596f 38f1e4beb9b213fad1c72d42e47e9ed70bf674059a3523f8b528a87c03b0a979 ce4fd15f52e56caefc0de61f3cbfa82b8f643f8960e4402d0877363a77e779a1 71ba319dca10a62312d5402de88b63bbcde0c74b424f76ef1a81cd99e2c42557 a42b807e59f4b4a42d765ca853118a7c949c67d207763dac84f9fdc426183fa5 7f447084befa17d3dc7684cdd24443ce6729e4e3d07bd5977e7f16788b10fc77 f69d1f9a86b471da99b77864a13129e06bb86a1a9c62d399a5f44fbb5154ee84 80c52d81bf397de16d8fca7e2977351e261f8a28a6cf082008204d8237866bcd c2bf102649bf9260bfe9edbc448ce55d71d2c5d8990ea453a96c35257a4e0459 38470fd51e71e9961bcfb8edf770e17e798d7724d02fdb4e174214da850e2898 a210d213076b2fcb4e3643a6ce8990894a16bfa60462c5d1a62f48c7d4578832 de77d88a5f7cddab63552cd9a4b023e0c67a8ad9e717451be706ba41f3056387 9d2087f8a31b845e6c98d1d047bcee0eab6396442af4514b515894eb6cf7d4e5 361db106e5a88845aa7666dfbd4e61e4ebdabade4d62bbc1043c119436c9a56a 3088cbc1ce45f94a9e9801792d7c277d43c8f3d3810f4d5c5ce2917d0f0bf3a6 5e3f3cea1f3effe249bff0e6983af2c52d06214f6241c784fa8370f5682fabe0 1fb612835cfcd1ab1b4f6ec895fd1c71cf38cbc32700198d7934193a553510ed 11ccaeb6ec7c5b4f65e925f25c49bac83be3e1432f02a93df7639bfcb7484a5f 97d0dee52f858375380da420c06a00cdbccfd01906db53f62164b423d76584b1 7bb67843d4ff675cad4d416ae1fec1995f0e64873053b9da7c8c10ced44e6c3b 245d0481c560000c755f617b90b704d11db054443afbef5ad1a3be7db5e376f8 61c3126648ebc4ccfb586c7f80c55c073f2c66608de356ed471dd0014bfd44d3 b8a2e3a09a242e379b79272f140baf34efc57537e525b559ccd6b426f13c1033 6389b658daf535793a09b69d90938defd9da1082231b462b758365673ed0d5cd 178994c05c1e6d5f2a385b6634ee10f6e85f5f00f70d5dc1df4e8bf93daa5c6c 63a6c46346ded728a85214a9148fd3fabff43dc52d0e1cd52001d94034681525 34a04688aea2ae6538e6ed70a680a44138ac7b8cc68d47bdd6b4e5e4c6c70461 10f231e4b9fd8b74a9414a80134010c6574fcb1b0ce4519fd5c8ed40bc08bdb3 9c6278543de9a947567430faf066ad7314333e7a655a9c74bae1f3074f8bb42f 9cf8ada1851a47b1cc3e0ed5d543b743e0733f5c8e9bd02b89e7e17d0c90e641 db6f4c0ab79a6278423b2b9a1ae1705c65b6f3211b309d802f6ce0d7259a35d9 125538ec3047df6d6ae5117af39791aedd7c42125d5fbec350577528e3145ff3 c1ad9392e4ca56b5fc50ed9c7565c5b3a34c0150452956ad5da995ac2e637be5 cb221231afeaefc67774e4b6994332c884eb3d779969c50d763fc53e689d90dd 919d847fa384eebcaf62de88c4796a6d52494e77c94e311de5f058048ec9ef98 033d928257e0b2a16ef44450ecfa969c7c68650e6ac5b1600798c5fa148af1f5 151a7719cecef7e1a72f6b36a2e4de34ba1d24f8ce0761309e2b224d714a5232 1b5bcd2a309cbfd232270ff95c05628c68876a388750e269eacc01f2cecf8560 3396e76c601495c9a59f37789ae80de1bdab902e837807b57875ea124ae0fb34 32085c2a64452b99ec7d45da31d7d066320e564aff5a6a275cc4045308450d48 b88ab50ea4573162c26463dd290c770d1e6f56eb45681af7c9894a34a72465ea 163f6a275174451dcefc408ece0feadc30c0ab9248f4aecbaf906f989666ece9 e3863faf1bdb3c52d7b3e69a28355da83b1c284f582bf288a79de401a27335ba 2a59f9fb0f85a80484ae8a4b1c9cd9b24630cee9cf1ffd27e2c84d2ed7a33bc4 0822dcb5982f4bbf0c317bc60fc03e5378a2c5b2afaafb3e71982c5febb56b07 c08f414ad8bde5fd28a7cff86000963bc774cbcf93716a804324dedb59b8b322 49cae95a0347b69a854a218a325d606996780de3798a0673a778b085f042c3a7 d5c1c10456433de2e44f504a60bcb9eb6e232f5d0d3fe9baeab3d44d0ed59cd2 6642f12f4462e5c07eb27c136a8e64c47a23edbfb5049460176e14655d90d1da 6b6607e50f462b0f164dad6c254d090338ad06d50b7b3eac954c55a18266a167 74eccdc4dec11dc635a4d8ab9e50541844361fc30e29b0d3d11d6773a9f7e485 47537678f12337fc12908396cd086efde756b1a094d13d553a3053e3760edb3c b15d3c13709bfbba7698a82ea9af114d2b5f347a12d71fe7b8ecb8850f93896e 4fefc4e5da7f44b96ec4a15ebbe84828ce2b667852ec8c95df5c062446ceb4e4 dd60926308bd9fa1ccdaf5c8da5452b83af945d771b0f72a6b00cd57f136b14e ea542d903f06dd25e5a172436ee517fd51c1785a48d98a32755e9282e12e90c1 27d6a58a18d2dfc5c97ffa22a89387bcaac17bcd58ce21aac7e0fff8861def32 255b1a7348bdda2df2eae709347366fb23ffd8151972c33358ae46d38f4fd531 9502943e3a831f5e6a5c7f0185d04e20f99d1b11b0850bbfb16df651d9545f65 606191849aa739b87eea541894ea920c5c9b3023f5a04819a9c9915375a47732 e1bb970cdf2bde5c39f94938ca7b4ca0580b6aea0795c5161e6e864c6c60c0d9 eb932e23b03ea04a4bf48ceace2667a9edfb034a581c1e66697ac29a3ff12e27 2f2f546c180a5c294dacf0566f43eac96a04521e0b1706fae57043f6ec6bfc7c f685987ddc01c50199b60f16f72ac17a4fc8a4a3a032b26e7782dc3285dd732e c83bd55594f3ff3782e2edb51145437b5772215cb6c6863a4e9a54bcf26236bf c6337f2383b636258b10e37e56c56754821f0c3aee1e174558d9d51afc7a5fff 21c2760d7d592a21067b5d02b40bb5cb77e4999412bc645bbeccf2dd9dade2d2 8d44de02a7c8ead87e636154fde3a971d2e9530a6ac4cd1efb128fd06e96eb74 299737133d26e7d8b91890bdfe5c278af9208142c0fb41b70667a7e4fdd41057 d47c4f947d5b43a44ce5967ada12d43de7da18ab1a500740ece548bea40c6595 de66a3723ae4ac56311293b92f46528950a784c09ffc7103c612280994a622d6 3eb051898ace70811b9d666a427409905fb6b7392ae3d58bb90c0b6a8ff6a2be b386e6989beba735b5d0d0ff0cf9e9f738f2cf6cf88f5a2bfe51ae71f4e909cd ff342845dfd8733be557250eec9aff28776aab34d5d44a9c61e61ad3aba34721 8b3aa06b7df6360b2d06e4122a9abe8642052353f5d259559211d1135422b410 5d3e6bae9c33c20cb32ef395b224ee7e0bc293e56e983798e3467f82c06598c4 4f90292b89c452cb63acf5888764d77256a4f01dd69e16ae8a22a68674301e09 c3df1c6f69b3735afa15c21bc0b9b1d561d775e4b4d5116b6d64f88cee1ff65d 3385297ccae73b179cabea89c41bab8111d2bf43b43e76bf13f32edcf261c33a 60d3f71ec267850b3b7b0c448b3e71a014bb186a2640e098e86fea4aeb4b3d03 2b52605da799e3be94ef760b3915b52ea2041e791f0ebb3566550521adcb1c33 b83c82b560ca8e45f384407311bf39ea2fbf2f5e666905665386167c1f6e6d73 21e3503b7509ecf44807b69f3467a89614a4de7cbff38093d424517ee3f22237 e9e6d533f3e08f0bd79e58958ef0650dc1087c6568d6514a6a031be03c118ebf 83a1707ec0d21d12117edbf7d67298ac43b9814cad23856bca7ea2dcda8d8821 e734fed8cedd0609c8ef4c07c4d10e8dca6d36c672b83a63d1a42a894a5328f6 d5166be03728cd1496f8e85e810be8c57ddceb73901ff05e4cfabe52c3883493 711caf931d52a4dbe5cfa733f82501b5e8a4247d75c52e452dd61cdd600a934d ac3e25169cb552e7ad1f3717a113baf70641c5f551429699fa10c6e90089c5b0 d3fe5bbaefa333f69b6095a2862a6225120e94b1b0d1429f24a5420a27431c47 4878aca3186b3cd249b8b66d542073356c4c40272850cb6b30081990bb337e7b 07be44f8c85b9f9ed37dfb3bb7d4b0b490e893f8d300cf668bf9c1b27424dd02 b296e9e9bc625882bcd4915df083349f1baf2aa1257aec5191d407c3f529ff59 9fb506f9379a29d4b0ee2eb83b963c83cb7fb36ea76c0dca8d3c13c0bf289fc0 d66ed0311c683740b9d0117ce16df88cb7898afbabe34522edfaaa12455e4cd8 226d05bb1013f541f20a45dfcae30a31f74dffdc853161f471dbecf9c19b3149 82820cab2cbd905353fc8ca365950e05c6565a9276533dda28ccef86fd458f38 662f6582380a9fbb028014bf54008c36cbf12273dd4a4452f15f22816a2aaf88 1f7585fd7cc5d39ee6b6213bf0287a357562569e673a9a7b55ae35e4132ec975 3948753e3e5712d149dfaabb6727ab566904d1c3567db69eb24bd38c282ddb1d 22c7911f2f9a4d783ebcad53ab42c80acbee4873f8eb7c8611842e5a92076839 286c8b0146d261eed74c415b647c1e10e4327215ecd770758448aa70b0bb42b3 ebc04ed97b8d9a3b6b27abe5f01b1945cd7d0363cca45dd094143fa71121e0a5 e9dbe93bc963217c7edec5128843be5bc1f67fa33e86c4a2a5c044aefb948bb7 62f1aa384df6f88a9a4a268ea6c1c8b9e1eaa9502e1fad40ad71d38567d3f390 3c6027a1eb223073253b31c0f23a51c340168248bf035ac6fea6a344ba0b5638 94224460efdb9503b421b2ffb2fc9681c68dac65364421aa2913a6b6eec102a9 67ea01af59aa825a6972e02abcef473cd2906cc7b34e19509a31e0b3970dbad0 64f4c76e8e1f3a23523fcc606be2f8c3401df78bf8836cd387d0dd6768169a98 fff353b0f271b1866bed156eb041cdaa1eb5b061188e8e3a9db20566a65c4e72 c81ae284310f3464b764833b7068f436e3af8aaa33aef29943e3641392c90429 1dcc62c7ab396c6c005bf9e072e310ebf575dc430115dd08a0d154b38dd96ba9 2dcd647d5e213a6acb1e315a1755b4a3dd0618b41bf8e4ce81c5c405049e0be2 0ecd90e577ce6f1b4b39bad635c583a9eb15dc41a80b0c867b7293107fe026ad 4abc045d9c6633cb923aa42e2b7ba6aa422a6bdd3e2408b3ad706f157c4e8880 a329fe31fbf5482d593f3281389d4ad65a73ec2c4036a4481aca259b46b8c1ef f2237ed03ea739c3bc8bc0b52c2dcd0dd32c41176634b9c1ee0a953172059c59 1bb7b9377f32624d31bffbac5dd88beb254ff7970c8a080926740045373dc217 2dec344d78fd5f1911f489213a5798c9683c30ef03ce07a25cc5ac20689fa7cf 3d2fa09e911ea777815b014783ff817768f22a80c31b6b73a38a7a8a33b8fa9f 985e147d8b8c39a5f1227fd20a0983eeda75c56f779431dbbeba1843ac2471ad 2eea5dc1eafae10d5ecbc9623144f0b904acb831c597c917a031cbeca3911c24 29e46f92b1587beaf659aa0fa0456702f2df5c9d2990d387b72342d2c76f3c81 1d5552f01c947704c0f8f519651c632bc58d260aa2c9a1076ae66f1b48d61f1b a0e84ba8059ec06cf6e699f4a50ee6cef107f6180972290b58950c887d79ca9c 828cae93ba588dbc78c1a8a3e2298a1d601851edaddd58b85b4a9263bf77e251 faf0a45d3e76f76fd535a105b0ab599fb270957d5da9750fd7bbe0e30b80d1a4 4bbf5f1c0803225a3810b30b181c57f8b721e719c0f070ddcc859096a4f93187 4e771cc981d63fa9dda64dcfba579d1631bc1cb9a5c87fbfcad1b9b86bbd7e42 dca96bd34f9e5800e82a98d7cdda018484f24b6f5c39820cce5ee712d7f3caf8 b65260677c9112a7115e816feeecf5e1c50ec1b1ac624f4bf4ff224593a3dc08 3375b581dd3019d108b48eda3929631083cafa01399a750af3dd5b819003df06 0575872d92f5b381ea19820b3023152a4ffaf5357bd71429520f3c55fdf6f333 d113d9f17c5f0f9e5bb46b9481f428a174b93a6c0b7ae680bc18bfd3eac23087 290d58fcb5e74bd763aa57f8e41e4e6c3021a3059c7ce4b8c89dae3a56bcb702 a628aee7890dfa1708b432f04314790a15a04e2877404fbcfb2ef467c284df8a 3830a192b91dde07a12e664bb275569ec5f0503cb6cf4439e09051cd0ae5782c 69a7dc7eff398bc7b828b46cc8cca42e5636bd45632d55140e642d78b2515b81 cc2d38621fd0471078a081dbaf63d220e970610e32753c200f33260b094969fe 370353ae32fee3bb89d0870d3f184886ca7e5fa11e61beff4307ba59e4175bd9 72070fe4250abe534da74bab06d7ec68306e084c6292ced298d908b01594302c ed0e29016c1cf6b6ce56b4e219b220cfd5288b11253609adc907f096ceb48366 d45e2a9a3b8ffbcc17a21cfc9aeb8cc344a898c790b87974ac8a8c381412d01f 701ee40104e4cf1307796546f4f338fe90396d267a0684dd27b22e717a3469af f0ee5f68446a6c5ba989a0f6683ff60051029f0d6b07b1a788a4b303caa27200 43eb03825cfd7c9bf4f12a1ccf92ca375a012a989fa13cb2b22b6c47ab81f1b8 e670aa7696faedeaddfbbcec74492eadedc95eed1e6fe36c8f3e05f0d537bc19 98dcd9968d433926dc35aece0f420ea2021e072999587d4d9d7dd1efea415c66 0221a9496dcc09f3a241d994c9110c4938fcbc8386b7ed0e4dc9c9b46ce8de9b 34bdf3d49fc052302d26dbf366b281e431e83bb1d3e0c023f07f1a982c7aad0c 8f7c152f558af67519addf47dc6772bd3289cf38074bc66e0220f90ccb46f072 59611391714b86444c5b3e4a9d19eff03ffbfec9c9bbcc79eed3e5acc6fce257 c6ae150a41d5f87111f63948298fb50db46c973cf2b05f741872e795c63ec35b f9ddea923561c33e9078f8190330e3d2ddd11156894d15dfcb4823327ecbb162 da7ae70e139732149014fd3fb44210868d0bccd8bfc7b5baa745167ad8a8d364 d3f9643826b9e034ccc7617cdd787b0b3924857a3ed39d9e1fa491d64220d843 db2764fedb47ee132bf53ff21938b20a2a07e5a5655cbbc21494c46b88ead9c9 a6d6e4fcad285f532421d7f4184b7fbd3a0fd9d85641c72c87018c5d9af029a5 c232fbb72c481109af004724044c585cc3dbf0abc0bbce73db59b66f71199175 ab0904bb3dbc052f468b7c36b4cf192ecbe490c7e52b3ee738ce68f5df4e0ee8 968c11d4cd8b559485d781c01f8d06388621aebc5482b29cc05dad1beaeeaa62 e9a1191025205972295a1f4029567aea50c1a526419a2c2d6b39c19340e6746c c5646a911f84a728151941e3a9ad44f6599a135aa9c241c95cc172ceb5040dc8 a4f8d71a503ded04996260fe794d48887237cd24c653bb00132c13e1ba0cfb8a b31fea054c515df40a2477871be166a387252023e615123436cb59df87001e8f e5b89e73f46795acc627f34ba112190482952f849ed7dddd46d0c7afa0c51c0c 6c22f853c8223d26b5ff9c4735f38bdb7593e924f243cc417ad133b07927ba94 599c6dd0121b78c5a77de3158461e450ac67bf15447eff139b9d821d329c5009 b0aa9c3820a20375306b65755d0a684d392efbdad07cc5cfb6b6b88ef761e48c 2309fb867c59c966d09d1f04276e5c08e39dc0088c5d63165453d40b596669fa c28f157e8eb47a7a0d11231ce2ac39d66407c3850c0a28615f4e745305e0ddd7 4624632bcf7f441ea33ba3c0c1b6d749ce0cd66399ba9bc8e1115bde5f2770d0 6b33223ade3fb47a418b356cbfe458fbca6cc0aafecf7805469b1a06a9a718c8 54604495d672ce75f53cc2981fb7072ad5de5cb419673c020bd763d1e9254c4d 6feb66b520eaf5ca417ebc48f8023e71b776f43e4ccd46e580823b8e146a9c63 4ee0166e701aa72ea2813a546b23ca7da8104786d23deb3fbec7a6292ecf3ac9 6ef92290fe21342578c015701e75523cc78a3ed7ee311eea41b8ba29383422be 9ff01e11f281f0d79a9ce4982606872bbd04d715da71eb91e4bf472a21ce19a8 71261afd22bfb4303aeab685a6d834ad1f332ef695c2160672aa187363d334f5 8c50e27b94b74a6a4d55e4a48766c660ef3862d31ac30292171449682794ed6b 9f0f74e5eb0eb312fd67baa22665193f1d71566ffb5476d6e009e048af0feb0907cb0706fe3d2534f3f07a890cfa86278c751a04e47b0570a456fb18df211f0653ab8c6c9944fb385835333a06a7c8bf382c0fabe407722fdd223bbaf047300e98adfd4d82dabf1bec6134d7785f6f00f1da86d903144294442eee62e2dea5039a30b7a9c1690f3de7f541941befcd0b4fc1fb719f7c3dc068ded050181e270561f50879e4d573e886120d923955515353285e8b881dad93bea0a651bd8f470dd8c4358d15cca9159b246496e74ece451e74d353e3dcb98add2d43e6ccd3310bab5748883de35e5bfb3431d4eebe2f0e465be221d6754e26ed65831428b31b04bf7945e5f8428cbf428047be0143c361527bc4ec958ac6cced57e553f7c17a0e3ca56b31375201675cce88f9e6a2b8c0a30ebab4b61e10e1c580d09507cca70c43bfebde17634d76d5df8556d9e769cccf3cfe178bf514e07264451cfda2f709e7a4956b0a535cc45a98a3e2af8038be445da8e2390c3314229c61591df9cc0bee04e7e06e45362fd26881861b03758b284f70c9eb7bcd472701ccc79fbfca090bcc099cff43eb8f1cdfcfcead945e38d193c9a45273edb0b5273c920b97c9053982ca1c9cd18941725273f9a3272b5041f924f1afa4aeb448994458b1313708582cfd1605dd714d66f5b66b13113e17129caaf2f12bbaf13a225c4c219056045ea25e049a4bb87bd31372834c60d68bc16f1b41626504347b8ffc56fe0e41097c5bd1056853d8b2a7017b6b7d75b25a3083e5969dc7c54b4fbdb4bb96c2bc01ff91c5e89d3bcf7afd1c10e6ea31739099b5a73b37078ffcf98159213f2cea035271848e95122897e3b54ac5b1eee45a30885ad4fb9d85a76ca49e8f6b45340d4f6528ebd6bfd188503230bc5f2587f1d28660aa25f20b95e8a65e0620a3270f870743b082c1e673cd52917ab3978927976fc263c1cec032c53fd08528cdac01f9728d922c413e97af356c9c8b56194d29a85453bc2fe005ed53d3a7269e790840e716313986cc530732a801623437b74bc3241ae6d22149eb6bac03d62f9c09777d1c7038a3f26563f71f4cccdd2453e88f924c86ea28b9b664492b38a2c9025a874f81e17d6343988a2dd3f22e55618a7de912d1d4e4dbabd6858001b89a0c6f9d5c902c40eb88b6d0240598d7ab37225892a0b976290c76f0522019a192091f2475eed4d952c1359efe5b1c1d74ce42eb7896c87aec8e122d3defee393c00c22c6b0f6cd8068151db4c2db50f60e339831c00d5c3365b13419d10dc09bf052193327d822aaf7a06d7d43f8793eeb6876afc62c264e8bab0e721d3751f500a934806305fa30b7cda233c781f1fd6ab9541e758272b939881e50a6c0bc90a04a15fbe86afd8e38c432b9bbfa9c6600ac87cd09b5b191cdb351cf47666131a02a0a692663c9c6c1ab2cf29acb80751798649abbc1a2703a0757d1766aa7ab4088ac2d05bb1b1e0c157282ed589d6c5cf288a0f769c12c22e197887768eb7290cdbe9f4adad893101660d2138d28f04bc3d2d82efd53875b5dda4bbb1d001f806745c4e4a6a7e705ebbe1cfddb0ddad6ea458606b09785a048b3b226a3aeb39006086ffd5a7b7a6cde26515d2977892db0d48e956e25ac0d3cf5ca6fb9161100daf2486b9fd64e9a37756d6e0e3f9d0482adbde3a3b991a99e75834c01375d2080448b06e259f37a060d9184bf694b959a412802befca9698948d72891edc2800ec5143a2b036820d725a67448ba594406dbdc4580da29995a9f324d433e65e0b40a526ed3eadeb1c75f3743f909d44382ec3a14d49a4e412b3c885be3b6ceb0fe83d3a10f1ad3a81c10dbcada65dbe65f6eaf9bac643ee7305adde59a722fa05732eb1b3eb031febcc832e9a552c93b9095dc1229c69fbd5b67fc9bbebfa7808fc713e4284827b410635a90010c8f2598e4494f909b02c6403c6fad13361b1095ee9d6900ca45f43f480e3f803d7553ce5d3ebaad202f3f1bf37e6f11e07bc096eb47add5b76c988cd878008abcb00854fa9d4bcc2394cdc47f3bc1f899737029749bf03ec4599dc8d977741153c76a9434ad2d5840080bafef4676bf6f8fe09c7a27f0953eb538dcd09b2ee1caecc283a66c7c16b0da60186d506c5e6a61209e09c4596c49bd679fd31743b22790e21e6eb71bdc3f3e466528e20f27b878b03ce44a5528309e8fc6514b8c83635672004323cd36f53fe747bdeba536f82ce0d3c9b322c7df42bfd67e050d74ffcc4d0a0b16ddcb115f7e4824453fe8b9aed03f4b7221997ae1f67ae70caf0414fbe1061b347fc03a395c2590e77b4a035b3029710f14a40840264e970e4c62d23472b7031a196008fda32a09fc0d4d4e6c203816d0a387f857dae90b51ecab3e23134158c884e716ccc6161d08b9a35287c0efd3bb1badff8faa0f06dfac2f999c1deab19e695ecf3e9251d6181d708520e073004ae6ad64eadf402660ae29b4f8d9ed4f5b32830d4f9af812dad073ef28f02c6461de28f40dcb04cd65ce25fa273bdf40c5a4da0e938fbf8717ec10953cc0e5ea08a4f2f368ab319c412b1a3cf8252ac13ca153d2412766c75fc599a726905dfd4f3ef6a91408fe4ffa8afb826066b3399351a5ba1a50675eeb589623bd4021aa424eac26be8b55187af1adc100a426ca6a2b9bf76f965a780eedb1a07bc023cd135141654f4812f1491f4859a779eb08f2d7aec6fb24f03cb3609f4d2260e43e830aa3a4bf3964fee1bf77b969ef4c3139484ee1cc8b975b8b788101bd60b5e4ba3e275ed1ae958e71e7b4bdceadef83ac38630d1a4cca88fa2544a9aad0008865f48f26d69eb6c519c14406cbe7801b29c4f524357456331b556bd04ce073805159d6b2c8cc283cfd6ab95fae70d01a78e2d252ffe013d9586b3780d54063c065c344e91104e7aca3228a000ac9c1e97aaa1104387989cefa620c3d4690d9fad239c350455f241d4bb736b1e7698158ccd4785eacb749f452da008f8060136f11fab8cb16e0e297fdbe05d9a6f40e6bc188f81cd8c2dc132c99babab53060db01cf09a2d83ee7a9cfcda7bf120358fd51d8cded96ffaad64f5445a1bba0eb574c73503a395bbf580af974ba122be5f77532a9443824a79fdd53312643c04fe43ccff3ca264a028081b2dd4c0fdc57a7d1e0de6afff1cb69a23debb365508d2a602fa883ec689f131d9932e50a291f62e17af5815139ed33ae55b858a680f6b539aea7dd1241e2889e9a256317e93ade41b670af3dcb12621d4dfccd021011309e6d112f2af9473c7045f8e015ec8471374e1113a50a2cd6b95b5dcb50404cc35edaa0b0cba2f32615adde94752b7e5f97c7b722bcedfa6b6543ff935bb02baabbae804677e6c7878a94500f9f76866d87378ccc654f0eff8f254fcf29c045443b279f0b3bf131c7c9126385959fbfcff97012c59ce3ede22eca1a79a550471e313cef1d3568c8ada3789bac120f9f4797e1ba9abfabe172d7be975be6d0237709207dd972e8e126980dddd437c59e4af1e5e87403913059e8f46ab05c304978f69230111e94737460920d6cfb4aa7d24e7abf197a7413aaaf64984ac120073afdb7397839a73d1fe2ee0e8b37ecf1984ad98136ba4b4a76d69949284760c91b40ec0babebd8d93b8afd35e07c4b9880a1f18a0adb3171705d8c67c57b100b7e216d44e5acc5044da5fa0123c053cd5a5ae1d2f452008606b57ad0ec5000fea79dd0c47f4e69442d3e5962210a36db25e6eb96ac460014a7de61fcf777205da7e737d3be9b4cd127ed0807e05fa870b03508f83ad5b37a8f14aca6b555607ed27bebbcd866caef43597110ce997d8912f2c19577d81c9052d345d376925086d5eefabb96a3b70fac2f94b50ab904b63e1dc00fea9edface447afe42cc0f049dc6ee4ac5eb27988d548bfaa086feb740392884efa822aa93d1ee02e23b8906d0c242d2d95aff310a3944a50b578fb0df1765b35a8039a0ade8e493113ac606ab441f3602ca78136e60b522a9ce67a922478fd50574561ddc2bd83b1b759602f99f974477d1c03b8a872f0abb8905c66c29d004436cd1180c73f742f9307800a07a51dcfe141a218b79101c67f9bdb164f51791ef042becd6242831b5c3250325df381436d56546a27c6e423795a6a6c6440d0486175d9689ca88690b26950220b48bcf3d46ba6f81b0ac6432c195f412225a80d6fd39f1eb0817c193cc980e99570607296dcca3547044c20ae15f3d79ab98828da33672f9db66b8834a1c0e4c3a22f3267eb24032e637a1d3faba45604ca9485f088ec57d63115ae31f740aa4d403c6e57de9866329084fc3ae30fad8c302f5d3ff184785a223446802c709511a04e9ec20ba44d7d422f00c8626bc44c061a10298052b5cf8758cbbca8400653c40841cac7c585de5d092d9eeb41e91f4de0cc1854d7cbf921bf3f258560808e3ed7e12ff484c41221707cdbcd968cbfa7adf97ea0d09ee4d0a2d3e4eb10eb24d8a19a77d4842aea7a3a4525e68756938e5bf14d53e05b356e90d3b17400b09320dbc7653c336410055da6ef7c637815b025284632af3434958973fc2ea0f956f0462af1caa0fb6cf7cebb7f23872908bf17bf4cd55b059ea3f2337064a00b26988720f74ce3d37f8ad71a0665bf31757fd4736f79532dc3326d0cc8845053075b5672c1bdbc232b084f69bd085845fa1c25939b4eda1d1ee031fa19ab00bbb2c23467889cb78ad271e03d4c627b1406c1a6f5c9c62a73c22db391e85120fe6b0563bfdbff568ab7b4148f6d7642cfd8a2cd3a6704c54b6cc0a73302f130b8e21dbd77dfee76e53aeec48d5e1a95ca2a747a4a4d996cd85134cfba621e60c3a26717a09292ced9857a1eed0537fa7ac481e0edb78fa7d485ce6b91cd93909934a1091a0c20701bb2b088267fa89fabde646481ec49aabca7732904b287a03535567f6812f81630ec4b8d35a7de03377b2ec415a501cc17cdfc92841f94101f675aca13e3863caf6a2463ddc0d3fe725c1d9f94597279f39f1df4261d0ef0f70c8c5f468270cffdfab180e0fce8d83b0b6716dacb75e31bd622ffd2ee02d0252e6860e0489a83b772d77a6af0170b81b96db80ab6b6d0da9a2db805836190503bc2df4665833130aa36b62a16edf9d9508cd5dfc1865237a9c891c91f70503d987b80edf9d4dcb95326557cb13636b67e0cb0d4da6f10dc5a81da76c93f90d5a24d11afa336f5b400c0db70c6f9adcd601f1458050bb6447c3644bf6a8320521798bb8067ae0ba57449a03eec4ffa5fba63b4537e108386db2ff364a1212036e5ab189df139b175da55ba576e22b55c542d5782473d3a9fd81b0364520960066fa2a3d0436e89f900b5a9012e3e4ddc24c212a3206ceb7da2b358f34c3b40e4e5c3ec67c6e5f248b5f9f32dda1fe3003bcee53156214542f5a6d76cd475b0a3b10380961e493e95594a5f1f7c5e368c8ee46a8089a71355159fcc8f2a70a082d5ff23b40491af2d027ec3c69171c6f2b3c27d84257ce00b58d195f4e1fee06457fef3a89301e72dd1fbc6ad013a508af9af26e4f396c83b0163a3985c38103ce590a33d3d1008a81350e8e1c2401b799b13dceed8f3df52bae8e3aeacc0b0558bb769ec8b3c7b3fad3636fec208177ba39e820ce84360152ff823f9b20da01066463623c61db89f81f452e4c1624e83b9f6c4f0432b4f303a9037a5275540089d96974c1525aedc58c6363c176d4aaf6ca4b0dff9b146147a96e7c5e0d27044717b7732f89e3db3b0b61dc9c7323e78f60eb0d12a011bc1bc33ed54c91500ee769d995860908d5f39bf96c4fdcb3360697433f693d41102a0a12515dcbf800a9fa3becca2bd3a7ee237b07ea844b8ae5a95bece306728a00b06d2d3819d5010284b1d6047f8d9ab02b433975b086b1ba9ba964d701f297fdca497dba81a901014907cd6ca792cace62d9bebeadcee2869cc167b6acbe557df138eaf56ceb018a57ddab0371af9399353246016f672a2bfba6e33a1381f4f219c588b843da0f8ac40eed49ae11488f242cda90662d770d711928d956cdac219a9e91c7f3e8034ff5d842a09906609de83b6b891407ca4c58e601ba16a85a943ba3fbd904c20be653e2694ff8a28ea3d673cb9b70a9a2e0df286211d614e5c786508c9601ec057367a6f1cbb6aa842230b4f077e6f315ed7389dd0ef6c37336be5b0b6ed49f0c2bdcf97a8435c795b24eeb9db745359ca56ce58f6d2c550fb2fdaefb32727800a0a5f6e6ee125b9ade40d84aad595e2ad735b91a5e0331875b1b20ffbb43580bc9cd969db8bf3d606526a6ffdfc22507e27fb2e9244b559b53a5a2244007e6045fe13443e39db5b18a34dd190aad563d1e1f0065ba74ae7504cc440a5137b4088d34d28e33a5282982a1cefcc69fa0b2bbb48e244123689f6c4b2c6d856e5401bb77c02de59d769f5f92e7b9e0a39e5bc14a6df1a2320b902ce815c8b683160cd4c393713c66ceb7d196d1f14818e8de8dacf345ebf6312ce4d8a2e232715102c8f1182383e58d3c25ca642346d15cf9c59ff2192eaabce3660a71b500c4cb0cc0de884c36defc10dd92585b667392ab8bd20b17304258b3d66596b2cff6ee00f44a264fca2df3215951a59a76563fa16fe710b88a03ba9a9993431e2fed380d3aa0c35093060f48b270ededebb8f079ff415f432f7f2f3de3164af5cf79ce0e5643417684e2a388af36d38a821ec8baf95214da0ef47e1fc1d12d8ca3cf000de3acf1922c2ac74fb0da9962722c82e7925dd90f64821d5541b8d01ef776a30a0be093bdbccc9d24ddbdf2b0f17aa3092186d3d807d14978485a73caa7edc90ebb22c946e7a8cf228d7920b7dbdb442298aa7fe2f1dbfdbc80800eeb408ca80b3f2ffe1ac15f7e8e08fbf2e8a9fb5ab5b31dbb9360510b6d8559c640d217f80093136dfbfe40a17ee6591cc642d74b1cc538157d825a9d80eb981a72e06b8c0064e36e51a3f929e6d33fab13fad6373f8477d7dd1d5ac0e1fc1598c5b2fc5907cfa08da404829e6c419af92cc708edef7267cebe420272f82a3bf640e41ccd0a2116a8bc8fe00497ed8a5c8d7eabdf4965a3cd309f2bb82833a9fb104954050b260db9baebd34832dee17c18d62e5de7d53581329286c7de2f79a1320323010af5308484ca2bc44ca1b7edc20e597af10d4dea20fa258cb2ed59a06d56245b0f0a7f053a754d0165ef5669c95ab759e3154cc4a04b34496d48b1e8460a20fe0bac4510b3edb295cf51fafefc98f286e38e37e3047b4a9cfb6c8a9cdffe6e3a0fa50f42d248513525b2d361e337cc09c4b02f7ecb07199d77324cd27f51a3340657f9c4441ab180d2e0ecef31807d9960a2d82f60f52838b17a8da167ff491000192984745efeadb28a1c907437c1b78201803a592f1e82e9b428097e103f270086b064f204d4b8262af8a5a9e8f1352c722e23f16700e5135d88fc8e0a6f0d05d450d8c8e4374728c55c333885e1405eb3f21c92d4895185ec64f2b081a9900d096004261f7411c0a6ec0a01c1300748d77fe4dd6ee3e3531c5045a65580ad05889e22054aa8ac1766e2a56199c5e4a55adf8e5ccdf57c6c2518a7e3840fbc0d90abb3e7b855ff8de5aec281035f959a9023b699e25a31a9784edc850ba0e5095ba3f902cc5da0da52000d3b0158cab07633ac120cb1c40ca162a91d98780d038860c349a96cf6dbfdd89289b1b449df90be4a0de4da3bfdf236a4d6927e030b0e7183b4d5c70918526da42a95310b52c9323b1fc184f12b4a0720819f07e307b5dad15fa9de1651af878ccd17216ec3b98d51f752d7359ba7d00aa6d3c12401bcfca1770e288cb9f6eb632a20093df3750d705a8bb4e16b0840e4beb91e7d0d1e9f651c48c589b2a11a578365f09d7135c520d4f8fd0a99c6d6cc1b4716ba02b51af296602222a2009b6bca1033b6e514b87e97a40dfc44d36d5c894992e800dc6a70d9e066b6a1b9a027a6aa2d7c1287c59cbc3ff4b007b37fcb797ddbd507c07647171f0892be48b908dc59985327ec17662e37231b28435a17986ef3e20554da16d8f96accdd5cd1f29f171ad3c0c771f2668271515488539236cc33490655c31735ce04bb5e16397cef6c9a839d2b3434a9fbe9971b1878aca0885aa505dfb6b599d1ef6893475242aeb4f59e4031fc4116854869803a8249dce077be015c75009f59f1c919c19c0aa158c2ce412ac0ec3419ecedee29c8772fa28e3003bbc81297ee0a7f57588459be3e00029c058eb679dd6593d3abdab1f5fc4b4b00ac91657c8d3a6d81d8193f68f897dbf3e7a67ecd8c1fed1cd7683d5d374b1a0a0fcbfb8b586dcade942b1ac76472f7cc136e08e826751aafb122d309e2703407ea3f21eb0622b6d2023d0e3f34f447213bc1164ea4a96286ca38d93086bae60516885550da4cb440838727751c1d8585f49548a6dbd6ffc35370f2e8d632aa0eb633e407257178e651395d43ccc369dcd8e0fa306c853d6a6cf9c2afaefa750ecdbb3973e897774b1f8bfff501968129d879dc8b06297356315bdb7b7ec6020326397ad8b6e6db05f9713425883260bed05403e24855bb30d3da5fa63970e4053c12e60f02dc276eecf088441902b86c9306bb692fb31216bcb40f4ca234db063e2851acccd35188bb9f119c9d836af886a49bba94e6a0100f772ded04535505ebf4ab98da18dd6c670819371a246f5f6819be19395b2f04369d7e3f0fd8b00931fe2946c2d2aea7c8a3abf0ed1efebbbf12451b5e2275a06c69dfc49a66fb09dbc66c628c1c07ff530c8a233568997984cdb368e901b80831e331bc16433309d887d3bf1ad5b09f589ef9ee795d799c01dcfdfd1f07135f973b3efd8cca3f0b65c20f306214af751564e78d5ccd04378467f5b112c839401253337fb1da9d091415438be1b3bd22af86fca536008c2c9f221b73db57ac88d72cf005ce6bfe0e9d12ad7d14c1764cf7ebe8b5a6cdbb366a2922241d803b8fd02b9f4acdf9b30476eebb59fabb3199709647573c65f103aa27c68e2839ecd6ba6d269651bb6c037304522321679f04a8be409de037280148bf1c313d26d1d5c2f799722690cb075699defebb8784f3f5a1919ba9e753252a55d0513b13eeddcf214aeebffa2f030e2a1b0b81fa60e85dd4e758b71d3159254b26864ed471357537c7b028d8750c87f79b09f0f790e98212dbb5aa147062d0aac620208d051a1a0484d20f1eaf07777c45ba076e7c9ab579513e83acc7a692eec80d4f90bac998037caaddb7be0aab6999de652a55dacb9c32c12b4093b219ccd11ef2b72c88e8cc16c6ee04b30a5e14eb081ca77aef287cf3ac8a30ecf24f9afd45286f6869ea6d6ac45d37dd0331695e0126155afb22c4c53b455326aec008ca275cabca8fafe311d209c66b0014f2be44cc7a383a2b5ef712b3a0663f2e7db06b4042dabf4febe6ac5e603c05f6e6a6d97243375f76a64a05b94eb26078ea63feb02609b1269c88c980b4c20fb111594197483543d84f437365f05a5b45d22614dfd6771ebdaa7af4c1f0e30ba5e0ca141ca282140e0a05bad9b6e21b3f610b359c1082bf2d69b9362451df039663875e4a7a4cf59712ad75c1bb2d4e6ccd89c0eeccf981b4698804f63d1a08d1368b6e57f1207d1521d36a230725fd39462a0d3e67efd687ccf4b0b24c0a0c7edae3b9e9957caebfe3f3e8d72aa4f5ff3dbd32dc1984b81981bbd697e5920d75db140ef8f388b1b05fa5653656c6ae88b6f6e1280b1c8d9f8a087d6d517207c8dd22379a912614d87e5acc1625e251e586d63fb94980e2891afbb1ff49250f47e2d3edb1d212226ba56a0c66a1eb5aa077c3d65764a685a46979b299ccd70790829b2d8705a71ce4993f384c0553707eb90e605d47582763a92608076e6600ddb67bcecb2afa8280d459d780af12a448a16aa3958ce193ff449dd042f75b0bb9063b2f6749cda548205cf38599481a32abaebd4401035a7dfb28756f7b5401d8eb69349a3d3d331bffb1fc2159e6fdd85f8a0f7c83d1ade0119eaee68aaf01b89b8ab821049fc6d45110c86eeeeb9e9d1069814fa59cc21c0452e22fca4a024cfaa75983f315ec5d0bed49fdadc9d19a1454641bd815b8e4215dd4e2b3ea08f44b513d2e03fa4d910063662df59aa1955735c384bf16ac422d2118a899e30b0e9aaf41af204804437299655d10b5925517755e0bdbd35178d32eb427551f0b8dcf6ed28dd9ece707b77d074802825d8d43b01d57e3028c78eee46d36d60f0c7ae590a64edb96edc9dd5b0bae2691e14b9be29811d476b3473dbc40c50ae000f6c9e233af4b26c35d45a92aa32caa0260bd41a8f9fdfa4d6b8039bc4cd6e90c8da5810700e3a44b283b0b0ee67177256ca9d3cf536ca2467159aa7ee4c7e304145cf3740b36bd93bd81b42937039009429937ae35076902269e47dc61614909710d650a56290618fc76c46584b800dba4e29a7a1618ff5870865d9191d075091fac9f94b80c59d3ae7f1e0c7eb932dc234258b8a0f4b86382ed95eab06190086acc758833d9ac30a31c83b068c19ddbdd03a11942e6b7ce82194e4aec6f1702989f6261c19157a8ba4cc82f5a31861497dc2347422d7cd3865efc79ba77d80e37d6929fc253c51c1b9764ac3c34167098fa7e20eae0981121829ff53b319a012f7ed5a2e0808291bea29c913797442e4568670c9ad7ecf9657aa6fa42f9f105b0ea2490920d3248df68abbb539ddbaa6144d0ceb129ff2e694f1177b5eb1601b21179de6cd24d48faff8896e538099c548ef495e495a2d29eb67ceca66b600e7fc3495fd356dcb436afaaec3f3820b6c6d4ee32a8e5f2d4dec649e1128f310945bc3a57bbc7735f5041c8dc7248f7891c3d0b59e52f234497b7e5af534e250b9594efe43bc4169a0f51b2c6f5d1c01fcbc338c8d8438ce7deced21b9f165907216ee589e53a6067a706f2e09a70de26c63619612de0dbf6bbd4c7dacf509c00eef53423aff5816a5c5b81de83736cc176a463b3fd4db0703b667a58beb8f201e446e6eb41eba4dc1139283eaa91783b77f8aa61d104510b81b5a98a4c8e80007106533e01ea3a51ea32b530cb943549112b3d27e4271b9db782b8bcfebcc900bb87d03c0d89706d83881c670a3af4ae7560c6306118092327cd563a2331d60a812f1205e5898108af8f3892274b0a96adb63149f56bdaae6f034d37a86483043ea3c79391ccdfdf9d1c66f6c1e2e8843e677d77100ace7dc05ad3a3fcb9380455acb770f4ede8942180d249c078d573dadb2fbea5ad32376efde18b606ba20c770707d22f473ae757b186804fe18dbbb0a049ae430585d534a4a4ee943834063a4c430611553f534b1a7f792808afab93fdff80e2f17c4958b2a17ac86989071838a21498be121b5d0e67db48739b3189a87289fcb22a2a30b06a6bf454bc0efcce204294dacd52d8a827c094f7f1dbd716f031cf65e7072befbc3f713f9d0db4a8e7942ef83a19672272ab151c4d6cc3550b7ac615e4bc4f329d88a83a6a0ed363d65dce0fdf5e8666a40ff6503a0c79bc8daabb542f52c424aaf8c66b7401340e6db0a39dfd7184026f33fae67feeff2be8d08f961363fd80fa29bc82c90886c7452d72404b07ad1ddf8aa3d05d37027a80b26b472263248be2b291ed6e07854c2fc97e9131c8b1661d2066ac1fc71d52d6c8397487448e5a8159a70bc3032e0cf1ef5ff2015af0fa08eef5dbc3834f45028cca45c91614cff00f8435880abe2c76d559e7ee7dc8516bfea29366e56737c26e9035be12804a74f9e4a6210127c1e57a364ca39e125873c2cec570b62c49493e033abaca5ada6b1e399c1703da21fb1797a9f793c90baa339340346ea9482aaba65d6d509d83ffb944410806a03f18a7c1cf09f1c37e3ccfcbc9ef9d485c4ff06ddb7ca0f9d80f3849cbe50e3e151d98ec6001df183d0994f4d59bdf1f716ae757a3ce02902f22826681fe059de2bb4bf798b0e098badb6ebb83f8216c0a82b7a521346d99ddb3ebb0763e0d61a69310e92f6794c35b7e2d819e9b493f40bd40169e70959d132c3fe20a0b0a94e8d881f1460f3d0d1ca68860b5f39ad846071bb2f92690bac6a7178045a800f22d36623f8bfd60e98b4dd04fbe6c2336ec9358d16866223a5501c3256fc9045ad8645cfa556dcd8af9298f271ee89d13ebd6e21a376afd67a0c7812ffadf0c1f6e5d9bb533c78e817b59e8faa08071c6408447a6a50aa9fa6287cee9d6cd0434768188612b7bdc1ebe65ccea0e45b883ab7a40bdde7e34eb469d2d4ce42c0455af41ef66b52802b637122b6f98c0db12355760203428e48ca10960799a8f09915fb9b28046ec2363d595bde78b8f878dfeead7892fdb8dd5730c7d25176a0c35f7a9bf8a50c44335be2aeabe6d72946492d62b86bd8d33a3391ab184131101cfe410465b6fcd407edab9c0ea59b70927738e9330d65e4515647f1417de7f083a8f590c7ae89d248c42bd73d4c502381b2b4101e9749cf184dec60be412120cd5b2c777f4193c2e8c24fb678c5b54af426fd9a24cdd754402483438b0cd2203fb900767219c7ee507c8fde4a2a66ffaea03f4c0be1cc5e21141a648e614470b834eabae627e3e9ecb4a73ce63b65a3dae7f847701d439e9d3af7efc9e82f60cb274e4e80e98516f241f80e6e4b942662c31b715a68f8e6303983f09f2166e0d6cd4b98598a5e5cf9c5302700c28a0d6b525bc8d891e44feedaabb268da3f4040a57bd76b2a66654ee719fe3da93d78ee6769c5af6a6013f784523b58812830883538fa175c4833c4e4ca9a8f278f202d1a2854176a94ac7f83f88f7d1ef7009b63a88e18489ec0aeabdb9b792010d6ed3982f40faf16a6ef8a686a9d371670859d8e8434752dff88ebad1bc302359c1134a3814d36894769a4da75d3b5d840f483b9395c48b487c959b11a1d415a285823b99d63508a7f837cd77312b6349062d8407e5cbf9a9837b206260ec624f9d751d872bd329e14058b4e66ac0699f03721635aefb75ee4ab4c143774488cbae58ebd50b240123f25c37d095dac18f082283bd121e2c49a5e9ed92775479525152b2b42eb6e39fd83b15196b5fb9410514d3cc0de2c4037f38206a335ea08bb56615f2b52e43fc9a1106a0ad99e8f20dd3b8b153d2e167fd0849d7e0df27725ac0c12f674535298c998f9e168ac55106ead0fd8932c2d4e03895f640a3efcf20c7c871bc05209d3dfc163e494309680ce5cc2f2142d603569491c9c9cefc1d122b82832d9aed557ad4add53557714804eb9d2b512c0a59b6f45ae602098a7305e3197727875409658230406d1fc24f0bab8cc6f90eb5e4e67e5db4816cea2ed77d5e36ad5937759b2e36bb2e09c1d709c4069f5827ada50685862003cd9dbff5adefd8aa230938e7cae98d1efe4b260aaba13c02d9f3b52f37ddd40025aea26d79bb571ce8def252dfec37116cf2c50d899cefb2013bfc97c48996826921044ff0e365f19d846508571562ad442ac201af3f5e3632e9f6d7815e7ad116d46b3b37744edcbaae9d35368e9b4a140b5607c53c4d490d8406786ee1a7e9fb67295d6013be0d6306abaf049d04160f806b0c4b5c6590943fc731672754f9ca100ce1a879ab63e48e75299e96f3b17e09ee02c9bc99a08cb5edc476ec6034cc6265f0969374a954d1180ecbca21f1bcb3960d1a968f5f92bbe05b08560e874e90d8152944362b070a105de1345aa9837a680375d39db18b250df69d95e7e02813a865ebc4d0f64837176620c61d8a1ca56b09dee8ecfea782bf287e864720a495fa0ad6a98168ac7bc902ba756d4f54c330088f6cdc3bb4b9dd995b910546e511d98b5bf07bee414afa9d6839996f23a6010819eedd87187318d86829483d99acd67d014dfc4f3f9491f3a18e415b0ae285013cc1e6061f0d592130fa0337ae7bdcb91728664d6f13e2555fdd957a3c87ce0282e6ec7d2f59d16f2ccba9979d113c631ef27ea0d90599ea4ed1eee7a053d70d6261fab668917e2de6b54c0761ed2cc22816f6e1031a43a4ecfc88d46c878007c97832de57afa09f4d59b55148fac16f7268c048cc23d99594c92266c56663046b93ff441a419fa0261b230113409296498aaf1b1911a2793ead1198d74896069a60d8b1e0851379d2a9b0d0833d19d411f14b2a787dbe4b7d03840f49f1060a07322642f74652e9c3da1696a544119ed2c97e14af6c6aed10a249113a02d40c9ececc9d09864dee6c2a7ea1809266e3a208ace887f6552a4d82e91f86171b819fa22ed953bba1d97fc2959c456cb88b254c9b6ec3ad8460b7684cd792f6fa02d407bbc802760b7e98747a7ebf4742fba948eb6e4ad7be06e37aef4e3bc54b09abddeabd170f2473105ae5f938c38a387c937776397e0b48c882d3eb7f11b00bf73239dd954b7c451aaa2ff7cbf20e3eba05fed54277dc05086f534609ae200b52b40a118d2503cfe74ebed72e5c59b8df22b47374bfc0906e0c9b9e85afa3088ba2f04bb3c320708d79035962c6c46502cc4a477440b3dd02e8ba33c2585b07d7e37f23157f3ce30bf96ed8e7b495a4aef4eb6bca9fb81ae03b8134e47ff804b34cddbf0cd86a2964f73f7087fc2acf276fadb70cdd08fc2736e048a199b70625ecf3da7d6d98f97a5ce7d45ed14a04542867e428d9dc7ce0b0897b5bae800f0b3c8f0c8dcf3f54630e3141a851fedd4f74c2986723831a168e605d519fb4036977601a47fa6cb92fcc9d9c7d721b57c62cea58104009652908e030f1781c038f414a45c13e5ee0abab313a9732af2952b28f93993eccac50f8255c26422206e615a3158b9a3edb62e5c1ad6a9f816b9ac0eaaa827a0bfb5edf0c6251893d0b9dfd8c13ccb6057a2528ddddf6263b41cc59016bb58e642b57a028b43eda9a02e77f296c5d0f349255990b61ea9b81f38ab173a4c59f48491805c9a43521900e54b061b6ffa14834948d6a96b3a5f21adec82abebe7bcb786966f567355eb6024fd88809da5390d7cfec00b6bf1874375000d6eae95fc2f00c0c1e29c89cca0324ebaeec88d695a2da49a73372275a2019bc3d478f63e7666976d8f10960420cb49058c932f3b5d6658d428f1edea45932f31db21715a24fd602ab4de0dfef02ddb34154a686624437200450fb7dcc82cbfc186d34cb5fffb18789abcae2b80426c1156e530f054110f6b04796173622ba538e1032be478605d01f53126fe80c9f0f32d50abb12c804d9ed31b34d53b0dc326488051e01ca75d26a251f33870d852e53b9325a66aadff458c25415c1eccd97d743ea967eaf1845f9b7d70c15041df423f402516a929bb2e137ea7630367b7257ae13adba295ec645bfef00b90e20e2dc95ee4737071470a2263581dd9df292f080c98ec9e16f846ab52b23df0ea335057a3cbb3ecb7fd83d85a4e0b207a13f78342cd7936f7552b063b296c5094a56426525e782d4f5bd0c8512a941be54ffc2f9b3e6e6967947ca942b8660027ba069dd0c69d0d5146a7258a6c31349ba9356533058c33be1421b539206680d05aa7a87810580e7d9a928c826f9ad7586a7b75a7dd8ece67ce0d3d3cf71d0061bc6df939b2a868237fcb47d9dd8f3551e825799af7bc8a9c76315745926740a066e114462b811a3ec24917b81624b0439ab56186211f1b05acbfaf603ed640fd9540027112dfaf6b1520b650ad7075a288dc0643fd3e546430fd71621d4ce091828c0fc075ffabb681943d32a3d53ce8ed17ff82482066865a3cb428cc6a10b52f5e5706de6a4652039155f10bd2317541efdfb5f69a7f083f74afd3bd027088c26497f5200403b16ca1a52583e94555421cb78605f593487c93b34f8173c0579aae1b7058e7239e82098f7c4b90e502e269d286b0afee758dcb3eadca2150af4254fe8dac388581034ad7dad6ee978758a2a6295fee07c8cf2b8b59ad27e06cb4a54be6e21a3e523363f83404ee838839a1a8e6c6db398df6576dde017760476303a2a336c4c6a03c3602660aa537dd22e65eeaf56051f254e5fee5491cc003c8dc4e09235564196c985bb0677c886224c3b4a8932cd465a6cac4946ba620a5436d1d7283fddb4058f5d6cf3cc1de66e3e31d4de99642d177dc0c50dd7cf0633b8d1f52f2c3a4213ca3d7f08d7436c483ee75e47e709680780cf39f0baf40d324e0340da004d7b85bfb35df6db4d1a99665aa539ce6b457471af77a42c480ede0c0ca7406cb6505bcc88100dc82acfd585d46a0eb6b6b1deec13204e01fe024d8dc42d4cf8b7cdcde5f1dad7442da8147ea08a12e37e2004f467a7e379a2080239d8014ef61f74a0536e3df36473e07fa94039db6df362b5ba813a2fb7870e50d82fc5b787b06b01d87d24de883a3f4a76b8c967af1413bc852642aea6a8047653a64171a9e314160050f1bf52e2cb0fa4f85aa15f16277b579e783dc6f7074ec9f64fbf713b991cf1b32608b40cb864eb7db8bf1c41b7ddd9d673782fa80325c334b3f4cfb9a4d659e1b3e92fc959761a4ca4afa625948ec7ce97b11e400aec1a31e6e4edde28bc61fe55fafcefd5116fa37b6d24f742c46daf1df6441307e1514cb14c2632d07a8cff869941d6db78e11686cdbd57302c473974f41f460b423bb375bcf48b0ba673a36d5dae1038b64a53dd9145ccce64bbf217f3c1320c00a610da499562074c63f563d1e9fee29479b37959023fe6b2abef63e91e4607aa3a8a688e3eacf6bfbb9e1ce88130dfab35e400bfce3f0cf0455eb14a25f30e4dcaa9d5004a3db023eabef9cdf97f0ed186d7729cee86cfe3d075539d31370838f9765bed72f940e23226dab570b485812f56a2b69b8710477f7a4f11532c0319faae9cacdc0624568ef67bfdd51186741056ff7ef3f05dc5db202263776f070be2d7fcb267f9b542a4865b881b0dd1853be8ade40a68fd42382b6680ef3c0301058f5b322bb5dc047b6e3a64a75896dceae9d4ec0021811ae941c122e93305a780310ab29c3d9c945b98afb43ab748b3232b00f0d028a7f63d8bd51aa9a2008212f2296f540d8bdeeb2b50ff8768e0e05d2429ff86b7a36f72c3d951f3250858969f0d4e47fe046497b440d244071da8912b0eee0bd333ebf0e0305a9941000f01bbcb2e58dc43fd6180f3248ab4877be01274b667893543b2528e5c672b05b5e4fe490c420f9694f40aaff65349cc92d0efe7b821afb34bc017b8cff1820a5e37cd65eba69a21ff9294fb57344d6fd09eb8254ef24d84b121f3d6575eaf00e4dde4edc2e17a67de8e603f659fb0402e963b6823989669c5435c81ee12e705bb96f2bd037633a52749ece676f19780e1077c9dd6d890f151dc47814bba0c01330ef86b57e5e1570da5c3bb2f666941ef3db18bd1ca6bdc06b17be13f76060372fcca484e2c23d4cf929b71c3f64c3adb9b025972fe94f0b84386284ddaa10e85ed3fae7cb19a67b2730970659acd2fa7b6ce33d9cb4747ab862909893b760c90e349747cda9f8c4bcb44d08502e916bceaace1693ea1c1a8240b7d40a9dd04089f75954e616a6a108c5d9f8599b9355fed8356762dc149d970cb2b300198075e58b882cdc70cf53ac45173e5e1fe34bd93a431fb4f23c3110d2ea5e3ab8c09c3405a29f430dbb34021657c9be9b8aa926333fdd1602c2efb731b0134ae2b039e323521de2a326663c5c481fadf226618f0e67aada054a41b35b0986bcd1d0208c156b5157996753a1577ce04aec7f4dd5c5ef9d6258f00bfd9f3ebd9d03a07792b01964efb33645e52c6c5ea95711778ac5b3cff178d69edfce96497a460095d38030c6bc96b2bbe391ec6b601b919141ecda9c19d6e2686deae579411690bb2f448c01900a2bf175aee826cfa60abd7713c4dce435e401f87e8f1244dc50958d192c53eef373bdf8e4d9d96507a8e1d8ddf8a37fc0306b56f9396c57aa900c84a98ec0b1418acf43025fa71906066f5872926082d581ef8026e32e30e780993f184da7c7b4f7fdd48aeac497442a5a8056c6d3f54569bd0cdaa568c710b00604e23d2c082f5df40e488d784740c93c95471723c40239ec03d7662260a920758c7a0d3e1aadf7c2d7a47a4ab9ab38c5108a5a87e2495586ba3acc351db29068dd00858637d070041d500b3cc245e647806add4d818b279e95c4b361d1c120ad23f7bf1fd1a2372bac2ec78db1157334f05c50953c709979c26f9e98dcda00d5af344c63844c40619b93d5295146c600099ae2a682769d448e0443b051efe0b7f16ab20dd01de327ed3f8c439baee0706b377a3addfe6cd1d5d5e2641a7e60a6607c6d23991b8d1b1d33710442f154d5add75b59552a927cc456737261d54051190ed22330dfbbc1bfc409820682099603b2ac3f733c8eb266c43715066ad087b91bb7fd3f5b07b684b0afa6aaf5be770263b17b47f7068edc3fd8d4e885607fa4e5abfc156be7d03f3f46a1a1de6fa6533fac788a14b61466fb2faa1e0bf0a62cfb8188faf3117b692c95edf3d1dbc2bb74e956831d84e0ec96df6a39deb0627e2f407448f3c6b308afa0c544bbae037cf9b1da448654507582c22f2d8000a5ca5a1a3dc79076b0877e42fa8227f522c349e436462c441cf53a59fc298b30859e5faa27059aa10c4ef878761be5dc3df5a3562015b14bc184940e7cce49d0a6baf6fb02aa535f4f21bc66b07bd09dbfb59a37533cb3587b23baf00bd9afc0b9c3451d015ef3007af0310d7dcb0d49fa0cb439813eb5ef9e7b1bcbf678b220f87ee706adfbf29422c3cc857cd20a3545819ecd66f6b386a90242e68721b1d0a7bc1807643bf793c034f85ae1487915591656914416dc7762a93a119e4384d0407a82f68545621af72c06e34a13cc24f40bb4f21d262487239ec768bed50510932f9d6a9fc59c5b972a1b97cea5d9b4ff70915bfb6a30d06c2421953cb3f900842be855312a55e55689f4e8784ee810c840306bcc12530957eaf2b1a225bee09619755e4aadc0d6aa0d6bc660230b2f632de6d92c4b81ee8d90196244dc28c00b743802d49be6c78f952bfaffd4836fd39dbff6cead1c796e02756e459d15f02a7166258130bc972255b2296302dd4d2c9df22302ea82bb52060948a52872d0e8b74cf44b94a61e6dcffc024394b358099d3ce814344014d9df31a57ecde4d00ab0265a9b9a9456128fb92157218d9758a5a2ad734fee2802fa97fc5a75c0204a4f27b0935d6c3b4e38246bef1df87ff97d7c7b7c0822531ac0df459a9e0b20bf050114bd403227381ae456a7b646a6803a08c93ec297aebd30ccad70352570cf329f24ef6054c5863d8a797dbaeab71e28049e22b2d9c45ae6ccf6178aaf402860c2d3cedf3f50c3bd65213ae347c1fe25d1a316080ef13a6e6416651afaa0ebc6b5a40cef284a0ffc6a9d4e6b9bcfbb2a33809a88131d9e2682a123ac9cc0663a923f3beefae79a03736bfbc6c7bb2fc687f916ab6ce5a042132028b0ab70887b703c8a8e1173ae47f959f0288404297a28296ed24389155b49ad25ca16504a3ee14ce35c8c726449a156ee5a2cdfdd2d9534a71e0d15412c764af502daa03c6688b7180221e630b033e5c787dbb865c6e07db98986d760404d1a92c5031062f08ccb88dc0ba9762efaa6d9759cbfa7257cdd2f6ab6a3d3154d9a82fd0eb097018ab35dd5f832a22d7ecda3cfca16892f685c810e00e5a9a15c20e5bb52604af1453cc5fef44a8176cb7d8327e087a81ad5e39b55f8fcd91c58111ef21a9092b5d85c5de58eb53308bab7671200b187074def5f41e928cbe8ab9744554cc0a4f9bc640e8195477b4223a1fea3d285156729794c4008b9705edf25fcf76480020bf2ec13aae8acc7d329a43aafda52df33e55d16e4f895ea999e83f6f6fb60dd623cf14f406d01055a84b2980734c194be88d333cd56d2a98c68a982e42a80327ea6da429140c136ccaabb7479898959c979aff21170d1f6df2de1df3195d00f9889ef1af75f0e775cf8fa65cac091ee49f093ecc9ad35af77484c7cac8dd0917167043e00a34197902deefadff85778b9803964d608194087c42cd4248670eca9385fd7271cf2a175c6ecba7ee9890a3410b2f137371ef99551afe6e2f4708fc7397b2b0cc320f9812642681233d8f52eed6b583170fcdb5d99f0a28ac2a0f5d10f3a106038fa65b61fca99a1863f8b2e2ebaff7d261c8efc6b191845c99025b79339235c1c23f569251b26582e09327bb2ad8d450b961982bda5d0b8122097c173b4d52c757a638de724aa3718fdc82e02a8ee4eaed10b3391a66141da802b718dbf7d4b9ce00b2c62ed94bb7b3e082608b8a6e970483256675dd77bd52025aea652a757701404b42d272e3ad4d866945699dba28f9101bad3f83bda72104a4fea4879cde4e5307af3c6126c2419cf478d34b53a46402fa0506f76e66000b02af101bdbf77616d96fe75ba7f3262bfacbb766f43cfb419adf668194e5590ca0e3c2431d555d2a181ba27eae4738a27044dea076ffe7a07719dd56fc662d0ce51f456b65f6ae4ed5c79a9f5f59d844356e41ff4d93ce42b909b73923ba300718a0cc80b81b36c66d90336d703c8f146a3087ff0295e85327b9c057e6b6570abdfdc4438f4f0bc8ece13f55869827e9541c269def556aab7d0e3a0f09f861097e46cf547a2736996f462887b9f9ec0a4c8fcbbe3bfc6910c1ee51afaa85a601f464cff3ac850be8e43556c4ea3a78c7b6a59a51a12496ecb08b88e0b1c8310b1ab68e7d9feb591ff20382d40105662b400f87fc0ccb2f0a7c89808ddbf50c0c5441563934a9b109ad05bc834f847cd9c4cbecbfd1188f1c5fddb9e9709bc402924a820d93099c8c37a63c433a82c78661af7fe483f8ce83fe9127fbf44ff501dfbe02e1c3cf3e3ad3ba406ae338b932b833cc367af5761f87c25405da3d290c3b0de574909bc169f2dd18578fcdcdd7b5a91207ef9d80ac539c1fe57c743c0a9fd9c0e6c025240da9c4e3f28639ff47328b75757601610709a9d60c198bb9069b73ebbeace6225668c0c293422cfc08a97405058f57f49fcf9573281c52fe014c7f3fb3fdd10a58814607b90fe2362d1a8bace52331a58a0d51fbb1f2bfe207d88235e33ed6a1bbd05fe7ec5725a6ef482a3b70f9eb95d691cce63b199ac1095e777badcd8ed25e7e3816f122de27f97f877a4550f2abebe1206c11207c5d0fa66ad821a2e027bd4e972847a60c5d584918e7dbb428e636b49c69800d47d005be5fad2d7a31ec9442c665922fd801d2fbf7b961d705e09ebf0eaf7a63662403f4cad67c49355dae97d66d68c2af535f14b5df1ec5514b6e3383fcaa2432ec0b06b07146e16ad4f783e1d36512b4e4db5976408b2ca39ea667e65f70405d6e0c2460944c869a386ef13a4eeeb0ee4c5e22bf8360712b9d724e95d9b783cdd80d4fd285f6fa3fc96d2380d6f946cb925d81abb4b8a39fe20c4ade3222ee801b0be283ec817bc63c4260cb7ad99dcf758fabbe7b0fc7bf96f8d6ccffa94cf0fe060647ba80b8506c7406623020ec83fc727fbbac6f54cb952236dbfa5f6a887a02d41b15e39053b9f66d5b32af655bcafb159a4f05426813f6c36adaf0e20b72094cc7de12fed572b58f3488c1fcd80d9bb2dff368b4562e3bf48a31cebc517b080d1afbb886d7dfaed5a52fb7e9138abe60ffbfd95d358df714e430e3ef77bb018baa9704b600ff82e3cdbddf6c886c571fd95a5b35351934525a01545ecc9b0aff07b455130d2d0e40864563a0127ec8c26dda78ff25a960f475e3ee8587ca0602d2fb5d48e158b08399ac0378f232be20d66c5012c74b9966b73d6bfdd424038fce5663161063aa34c742ec9702428d0833ded7654caee80d1eaceb7ef32102d437ecf5235f56105dae09b962b3f46d0a8187f4d808180793f57d6a974bf807201198bea6928c81902aa29d498f51d241f4539da429630c10734bf15a8f57086b2fb94933606eff7b3b183aef2b3ecd3f295a092b2279b2f7449941d06bc608d7c16fa0afce241d0336a617c97d9684f7c79d42c0cb28eadc664a24d094d60564990b4bb3bd31305b95e79516693fd162bf617c53059cf4f8445932f85e9b0ed2ab2581f1b9758fc506765039894356631bb491e61778f96468c22405e4a508f1a887910faf7efbfb7936f54d161abf527445849cec0cb5133f4e852c188907853a3dc71a8d8513d192dcf6e88e620f19f75e791616e58077cf24e61876830dc989a100e3e181a979c5ef81afc0c026a2e6f4b9bd8cf42036a78bb2ab3e6004 false +check_ring_signature 2a4453674524455a4a13bae5cfebbaeb6fca93295050f573eda2839f1e3fdda8 ce270ea47f8c64a47d8506670b6f27c7a6179d33c08e0dbfb1f1811fd2d98bce 81 1e6cd8d3f86c7fa51efae2d22d16bf35021e94957d607c356af996a8e1cf2140 d2f6912fc548cd44a4134c483deabf7c7950eb6d5c53e4993cd53239eadcf65e 53bffd189423a9a73abaa5bf35661c6a6ec7e8c2ed871253b98b932f1a2aa9b4 33d611b9089932c8c620377dc00d9e6890c7d0533a005767551ae9a3b350712e 79ce6209e41f3a62ea01f9074507cb29be938c14631982965f42c2163f939e01 45e041629933e08baeaf67112d5179d3c2cdb5ac1501c1b65f435be34a4bafb6 b91fad0119e4e5699d09c843b8e9bf332a8cefbc4cb848019d0fd10d29cbf328 7997352f4fccc03f94630ea6ea36094339b4b948d54493b75c7be7aa42c04636 17224197201ecb611e00c63201f33c6c62c991159f94c90043ea9a772c4b11b6 97c221bcfed5a56e343317ae4ba316e10feba1ef16bfb9ff256cd79cb02dc6ca b4abbaabaf0b2f44ba342a82bd5a59f92565218e94697b5a9902981c6b0cc763 fa19781f82d73f4883e0b8b892ec33795cc3643b68f4f1761dd0552de23ba70d fce1dd2417f3e0cc7fbd770d08f4b4bcade4015b2c52c029f612bc6b586ea0d6 0da64926a0bf88a5a42b50a5ee96dd459dabd406cc0c4421c28ada322c7e090a 131af2d4de9a677a034442fb7489929b7a82dbf0ef5cf633d148c497f2c3eea2 5c765799888fe26106cdc49ac14a5435e5496b57e5477fb9dc22864a0d9e3666 3ec0cc0199331bb0394485dd680af69afa6e509b6e65aaeaf9006530b841a559 d7632d92db34b7489c23ffdc7a7c423faa7271445ad1f0b99c051c132a985701 a17cc49af57f1774fbc25076d867f3a6cb75d605336de8fc84ed19dcc14f585c 289b1f6ebeb242143d8aa077fd1c278034baf09b8362461906d63fff7ce15b4c 71b65a1e8313795a7a85e225e790f4f2137bbd48feee4d5af9a657f5acf9b074 429031e55fdf1f6f5c6af8a1527690081d210a67062336e3869061b32d09f5b4 f8797ef54e2a0e8bfa1903af735350188cb2cf7aaa96b18f4c69b0a85d170822 3bc1f6fe876da8ac4fd1e2876928394fbf8d43a974335d6a847c5528c8b91bf1 89e8dc3306bc43912b0353cc37be19c70b6b9d3e8bbded475db2f44979616760 01e51cd845666e91a84b22db1a6a7830009f1aea0dad279c0f671e564d0ae576 32721e261ff8ee600280409b9dd30b75875a35dda0b094a9a752af67c0960276 10070d6cd5155cea02e00e1a538bf1e420c47cef126504b12f2340cbe3551d5b 66034303d6f35a0a1ed9d409514140343c8f98a08ed444ded952bb1a2da4e5c9 eddded99b70e472a70e1944d8bbb1c173b6ac941ebf86c4f74c7e4d1edfecccd ca3836d06fd595db5da3f39b1ad927062ba509dc97d2afd5dd031ce49de40e7c 1dfb7dcd85cec1cc13a2aa531ab9fe272287d34787d8acc5c08707bba61eaa31 0a03913ea2b269b5e303d38e266f60c4cbb94996311cc3fcb6700b0eea668f7a 6552f34c14cca743bfa152b675773b22faa34d3fc725af257c830c06785d4e32 71de74fc613eaf5fc621803ae5b0cc8abdc01f7088743b961ff74b3514e367fc de6c1d5807cae84fb2f53527de46f3a5c6cbcd882c6657a7dd545d18eb84ede8 7ca68bf0bd63e23dafa86e07c2d4b05312578799d47ebd644eed86ef82277d30 32e3fc5076c3e1391544b4df2a6566a83422edffbcfb25d50389a808b71ea7c0 4da1a6819723d946669f19dc7aed1d76173dab64c2d50ce140732ca2706aa9e5 fb68bbbbb8b4a1ae806052c5ba599eff6f9cf2993b2a55cfe91e12f18aaad2f1 3a0a17577bed2bdf4e8456a68127521173286eedfd68be47eb14285a8134068a 2465db755c49732ec7043239fff435f5fed35f42773cc2818295e6ba8ff1a040 43c71b15b9d9d2bbb7c32c5fa9771f3bf97e06216b12e93dd306b1b4b250f983 9ae3e59b132083e7c5950eca2a116afec8be9c8f8a0b0a1334934c1a74d62fe3 8f2cee7c8727c5f4b1ce5c0afadf3b221f3f9258cdbcb4f65ace25fdc1d4e2ad 226481122252bee52ab33a3e33cce23e827e23e21935cf69ff71422226b31009 2d819559150c0476b0c2b6bb8d9c4f34809f38ebd0907090fc45b35452625750 bc6dc77b8a2a96a5c1ba733ca86248ed4ce96158708f0797c19c08389f589a08 ad795ec1e3abfdd64f87dd5543637ef1f3850d1fcdfb37949368741390ccf5af 03ca3271effb9b963bc1082e7ad8daa813b996674b0f79a9817ca338b599be0c ea619868401a5fcb8b686a423fe0138d7af637eea264a6eae66fb11ca9eccbf5 5878c839bf87e2fc0298f302a5b20de6dcf7fdc2b88bc893695a00511677fea0 0e01f64848f034374928487bc41c7a261c564a96e05fa38ba0039d4dc1b19632 d76bb7899b087933669466ec4121fa0538e7a85a15b1d2d110036be4a27f2acf 32d67de5a9451ca5dbfa1e6cc0f3394080692ce71cc1d64211566d5aea0a9c35 d542f0a274fb73375e88e6936d7f9e22837bf50526f4ed22cab1ea69daffc2d7 3e921706409fcea987353710c07dc9bbefe1e58416791053b867f07b55f6b028 296ebd52c653f4038bce68cb1105f6510b8d7bd66645035999704775271a2fe4 27e843b8e4a1309b592b98ae3c3410256385085d28a3dbbe9751b86f469021a1 b8123c51110073d663261fdd7323c911b51254ac6bb5a85b95a7dfa6787afc48 ef0b0ff43f26b143b9c759240d86eb4b6e425c73ff7c6b89111eb70cb992d520 eca6c5e3eaa38f3e4d31647c51e0b9b4e514c99cbed8e1ed6c98c985595edbfe 38bba2b7b34770694aac29571b062ec5e76a3ae32675ef1ed4cc45a0b05ddbb3 b69af8a7b33262bffadc5451cef22c52ee8d8b2b5c421be87dac0a3db466a86c 997a223d5b589b9728a8d805c8e0a367a27e169225716e5e86121010d047b56e f7723773e7d3789ce588626437d62afd97b53c29ba72fef29c725d44852aae33 93cc3b1ab83a310692a2a03b36f35adae48a91834a8a1b669a329fa5d3f2946d e3d8b081ef8ff32576d356063725af7e2754ccf6264385c9b6e197c1065220bf 0c740bd1e703a6a588b9a413076cfc032d5cb1e075e12d8e347c2061c1e1f58b 81d81ca8cc98ef228267b9d00d8b8768a23a386f7710b688bfd2639b305226fa 68cfe8031db3f3f98da5b1df90e9bb6e0b0e282180862a78a04abb1e9c65a43d 12fb393cb93906931a7fcab1a13b194300a76ac7e9abe38f42e61592bc8f48b2 db4e98aedc94b8e772a3682e4734357ea80f5bc9cff6ba65b83fc11a9237dcc0 56d6a18afc751d30d7672b737accbc5c9608b3574cd6fc2fcc9285d285ead1b5 b7f4a6af4bed69b64cac0ca5b2b5c0cb793ea062b9c1bc4082797420a0a02936 a7642c02af7110d96191c5e05f935077d18aa8f6cfe5130756b6804131fe7b96 15041990a7c5b0447c146017501dc64c1705d442aae1a885e80b3a30fcf7c19b 470ae4d8884ee3c8cb499508384da16ebf256c42fe1fe66a9d986edbe6ca459c e00b80325083cb24bfc8f22d244fc5a0fa2ce2bfbcb53e41c02152fd46d701aa c8de03f8e00283f108d02e96f883510cada77740d2505890515419d52c10211f 8dfdcda59e75be8b9574d7eee294c86e724e84bff0ce4b7198a365684f7ef687 c6236b21d8399368e01aa62a06dc9a147058c3dd2b1d8600a31781e3bcc306024eb39984e41742bf4660fcee8a9e12d061426b4d7019cd30a05635eaa53dea0342f6e37d267bd516f1d545427ded56bb39cbbd38af4fb31d1ae7137667d01a099bb49d0f7c5bd7e8aaf00f453f27a409d797b78f25bdf130f453dd9f6d1027012493ec201f5ab6a21b62be13cef40162adc8ce23b097182678c6a35174dfcd0315def794acebc5cc8fc2a72a57e232628105b5e91948c4e4896459a892bb2c0dd13dc750975167787346c4a750e45c82b88fc4caf3f8579bf0ac0b1f9cb11007371807e6a4b4572f2b03449978efed4e5af370eff471d50bf71c88415721e30333ec45d24bed1526b06ede763e10031e2563835dd473ec5a1ca205fa0ea13706b6931485f013ecfe59654aecd58b45b05ec7d09f819054611ba663453157b205b7fe62907b346dd99af7ce87ebd8be71d48e13af9d81404e869308a85bb21d000d43170b4d11318ebcf3c3214a4e35b14cb5194f109919c93a3daf300a09e007e7aa00cc6a180f83710e866b9d7dfdab2baaaf0e5649d9d8a4fe23e163ef8e03f162255586a3d40c89a08f014d72c86f2e6421f7dba84fa9e7613e9cb2a01e0834e5e2d096bfa955ffb5b329a2adda361000c74a27141c85f4ff51188701ce0b0ea91fec6c8d40cf4e54852efd84f0eb8ca98f80cfd7f59aa675d37321caa207ffcfdd139b204c95b0593aa4a52d68a24efc10d4c050b93987a232b5f254dc02d93fd99b7a4b4811d67975e72e7f1a95c05cda756068663bece037bcc259150b81325afdaeb30570a7c192224347bf17669d44a7d6f6c5a35fd5ba36c477e70f8c94ba3f361d1c2996dfde76208b8dd472c4fdc35ec28e53b97de93c60327a0a958ebc2728d60b2d56fc257851127d725ae97f4c6e566566224ddee81a97b30af60c191032c0df5d92bbe1b65ceb6649864416d9b844dbe7471b5afa12ec3604b02e74d98681d78a614b2f56db793309b10009a4a247837fa79666cba05ec60b81321fe2476d34e11ed86c07c7f489c7b7fbc5d1c179e5f6c5fdc7533121910c121d12c7f1e39ac4387bf37339d0016882fe9bb47bf9394959ec704f004ddf007aefa0952203b73314e803e39f9955517eaa4aa14f5f12a70bbb45c724993c03fe258f16f113426f1bd979f382935be683e3ddc84b139892390e75238a54c60e89e89be0c6578a53423837a2e3b9804e1ccf3bdf27401e5f21076567ed65b708860b6bee8730979f44005fbbb89f0110323061980d55530cad06c5cb4fca8b01a7be242162bda812deee4adf7f0ae992824cd11743ba1a425b0215953b067000bb7123adf68b950bfcf85071a86778574a8b3136c918871b271d18bfc609db0d9c508453f4c04a1203f792f151d1d4de0ec0a395542f4c00f5094896f0924401ed42a178f5ec2731114f2b6c46987c10b09d1dd50266ea5fb393ff1aeca9410df3e18552f05f9ccdb39906c8b8c7791168b8d8000d75cfe41dd2691ee65a9f04909b8026cfd8709386ed87fe2f8426eed95f29e05230d4f887515cfbc585d60e0430519ad7227670a897ce2f98d5a7875bf2108214cce77174328d1425285300e03849fe264a81861a26c327e2003b8c6c934ca7f651797d945037af3156520dd53cdb5ed61e0df1846c29d254b7263d52656dd1c79506ed20b0c444bd32170fc3df1f07c45812e13b53e1cad469d96bfe18de1cf8ac074c63971a9301e1880a2c8caa4ef319d9a9a62776f9cdaa42b934ca689ae51fdfb6024224de02d5b40fa46ce1a002773bf23bdbf71896a7b9979dbc4dc7dcd1565e1139eb5702f32f06d87519e1b2d0275a8afde7f58f9cfda5749f0a1ebcb7ec1dde4da3bcb453d50be325886139e1d22b83e57a0cdaedd16d22ef4363a4ce69da0cf5032176c20a0b1d53dd3941e0aab4837adef87be07142552863a9c73f1a0896991ac70c16e705d31ce451577283a2a06c7a377bf850ab8f6fcc79cd372e84a8c106f965e3a80fa187c29d3f6b7da336cd9dc7c27ef34f32c9c44974febe8368f71489703f5f01f3ef3a46c78db8aae0cc69d510ef9a34e3d795a9a22827ebb2a5f44472133702795227f61dd8a319deb329c3540361ae53c5ed31645aa983ad34147070a3690ba30dbf4fb76748227496e34fd7e11cc3a8d8bca35ebf49d245c049cdd0abaa0ca2fd2998c76c382dccb08d153eb03b25ccb0feaa32f574832b4ae86df65004018872c5c948f3203ab2851d2c42ecb09ec9e540be7210ea0aec12f80d8b12da0319ed1c1acd2b9be932165366caf16be6e3ad39b18fa0f50e10dc20fc8e28ed0e5279515af84d0506c85396bdf9f95b3196f5d9da0d7029235931dcab1235d80e472a5e6e6cb4eaa208f7a26a02a929600f01cc625fc14d60a42bf8249615af0df07724b772674389706e7d9f9b1f461cc379d9d4e000f1c82080fc32af1990085171b2621c8353360ad69808305071bb2ac88131c736fb967448a39537c9b2028e07a437d086f57836e172d04d947a0c3e589c25e7d1f38a34f2bebe807b6b0f33595dd07d21bd1e412e7668ebcc452493c8a34022586276e3c4cbcf67e2cf00d80d0c541f351df41cb025594079336cc3d88049c78b9ed4ed88ede0121ddc0576a10486f8c4c29e10d9c8ad64a446a94b13608876654b304ba283424d7d9d0134c1adae0e396680622b1e02b8a94469d7e9698eb4230c821e95f341d3821b0bdc7e762ddd43103d990d115256accddc2dde6f2d95351c9f7291d0c744fe0d058abc93aabf142912c5d652cc6b4f2706ad449edb2b64298736fa598b0139a2095f9e1941db1bfac714355e17b953fe5ff90d96b5939652367e44b2b728dca901e51c53a7544d8e1733e832cad957c4c7c308b55750e8f04306140f710e64cc06370e9efc351e7d3231764c99667cabdf8e4f3e470dd6d99308459b9710db56027f1d8b2a0f86ca08dee279ba0f070699ebc3d3cc6ff945e9aa50604a6879a70a94b0bae3751dee771c47c27c24adc16fbe16d3d3d5e5d994390ba671dafa240c1f7b687f53145f2d9d46f4537aa10c54230819b2f0f6a105f2289d3b7ce7aa039b8a26c62a78f2a6c905837c0744d2f38cabdda7c4721c20b51e0e0c87778a0f15d324454d11fbd00bb644363caedd6c893202172451d49bcf1ee49c1efc410e895ac9f5b779538388cdd9550112a956aa7b9086967a65c865478728ddf3700238f25ffd6a74da93eb64e713a75c803e2c877f2517a9be01029b755d6cf1c703abb2f2b9df31964318487294d34ee640c9f1419759056887abaa06a47ea1f4091f3936c069b61994ffe9e42d69ea1e91b10f45cadb60f6fb989cf28aebf2fc03347908a999107fece538ebbb39b54ef3a331441c67c9450bb7d893af3cbe7204c5ac0845df328575f37c647bcc8c598724546c8674b053cf255f3ab1335d3107021c931a4c0ba47159c5a2d54bb921f47397746a205e39da210879452532690476d133754ceeabbc01864fa3f9bc92ea7521a10b5004ffac27b9ebc778b81f060ab2c90a9b387c0348791572c272cb0d75a3068308f11aa2bc789d8ef1ff350762763ffdda10292f892d324881db904c9946d3ea0e66ebd9c2104bb4ed958707c3b1399fb0b4ab1e5e3fa81172f93fa91906dd4b502f00384cc2e1d3ba8f310b54ca21959a5bbaeea5402a1c1a60026596b78d4b28eb9325b183214cfd937e0ad5b265f56301eae3076547eb9973706211e37c32df10586d85776d1aa132e301ea11ce43dc83269411587fb6e18c721713569f9312c86ac883cdb079d8aee90dd61cfd2c6cd1ddd85f8cd7ec94c238e679ff542217702c9a5ce59a3610a03805f1f721f2289c6a80af1752d949b38cc196b1a21eba36b22ec86a178d3fb8dd04ffff86e59787c4bab815de51db1caa1e97d67417473ea6d87fe08206532288053d493e22e7406633d85bbb500912dbf3703daea6ecf77d4f8ea6c45da4032909da3a7ca41a9186a36fa91cb0d27b0e69ef0535c48ca43c3c9c31da9a0ab4b70d63c5d23974e14ca00680c147b1f51532ddcb435d853734ad22fd817eaba8210133923e4a175ba5c6eb4b21fdb56c375c04bdf11281cc2f3a2643d352470bf50beac2a9aefa5d2979983cafe6fd0cf6859c6d770497b492ba4f170b10f1226904434a636535ae59db69d2250be5a9f28a4405052b0db9215985c01b2e0d53420d67742a7f09a9a1576894bc25d5019335b0f23245d4af0cd955923410ff05040043a90d3d4f8dc17cb8338a79d4783cbd175a61a74b13a3134362fd1eab9d66070c53bb571fe89b7b6a766bda8646cfdf9a862620cbb9b8da711235a855bcc909ff53646ffb5e2c11a6e03918a7dbc60bd4afdbf76d5cd6b3b56d4dc937106a0cfeed1cf96f90b3ca3f3dca0ec6b68b8c2e56c3ef2e68af0a260d7c10811c2e0e221f612ef070ad7943ae9e9e522aa0bbc98b0e8a743103592d9925d235227d0ecf782315ebfbeecd71a8b6331962e0243ff54585585674aab109941dcc1f81035c057aba6e5a2f3bb0cf9527de656c5269c1f53a498c067a6ab361a050de420aa4457861acd78ae9f2b5b0cf709a9e316fc5b1dd65f1eb5f0dc7e28931e66800a46d5f53a088880b6aa1d1822e57f2268ab4167a6f265fe8e4c57b9d9fe33c043d30aa2ba0e21cba879d6c6c507769f284cde753abdf8198dbf810e7c41fcf08db23da04cfe4bef1729aa3c44e9289451239c90e14df49dee5a58fa274ee85015e8ef5189809e18bd6e362c07b9278925c98470ea9488a78b5da7e2fcc7cb40c7a8d146e2cf32d97ee12f2cba6c01209c78ec27fc9e4bb351c14a4b6c2722d02535ca453d1fd3d3c28e0454d7853faccffadb06a53ac9a1f7622dc1a0e8bd60e06c9d42ee1fd7af51e58705ee299caf5845f33e7796e9ffb199bb88282f73b086b26e00b0190c3a6bddbd7a975e3e51ff9c0bb44302180112e47fc299ed2e3058146070a71ae13e1309b82b34ac82940da2974c905fab5ceba5940c07c75660c342d9991fe359509291e34d5fe5e947886218e8bd022f035b16e6307e784910825910826ddb89c3b92118c3cbb394e566d6ed21caccef36d04b6da9ee2b6e10327be79656f4d463da2d68f7734c02708d5107f72efcdabf3868eb9ce5dd8a707f6438aa408d5d5c0644e6282c3ee1b47bad0c590f51d94d65ff384e95cd5060c0d720241c5e387adc9af0f8313a00ecc580fdd60cb23ef16c285119205f7640776454a8f11221be4b5f6282a67891c57cae5a155a46cb596f50dd1d55ff2b202cc2128cdcaa59789892178a8bcf317e04a7b8a344dc0a19dc422c11d02ef1a01d1a9df9abda1336e6926b92e97870256fc4c74691914ac3b2fc6d7dbc8a0e00cf5f55fb28930d05b1989c17f0a1edd875fd01dad81e95d4939d95b81b629f50568f6f1aabae321d6a102561e4d46ff8c35f93e60e4b14c092b60300c67bc3c0bf50dd6c6e614c30695495d9df3957d248cf81ad5d3c75d53dc22d34627432803ca28bf2c9643682db5b8c34daaf5f89f01a2c3a6dd1327e69a93e2ea9e21a00bc07d6f1a072827c01993566948c1c35d4eb1172fe149dc7fbd7c7ce1e8ebb007de0fc480da5f1d4acfb2fb960096ea51bc0db7347ecf69ed21a42a346a05a7025eb8dc6cfd37dab063cca4ce5ae78d57d3127770dfce251a86d8a1415b259903cb8a9403bb91a8c719983b24de44108f60a4ffdc0ecd16e9433464a29d293f07db4d2bac99046081150e5875cbe92ac47eb126258c5f7bdd9098efed4654ae0d875b5f0586e681c847c418b390f00e8b2ff22454715c70837ff0fdf84aa8bf0d3f0c8fc8ebb7dc01e65e0c86826556ef6e081ef3dcbe034689a2ed4d6748ef0f1d726318ce5d8b1aad3a34c5ea87fff7cdef7091d3a0efbad9762362aa6035019e182cc67af5db29b1c4655e4f24e4bf4ec6055fa30c9d1a25491769cd7ac8093654449a8b6ff12ce08c80855e5516d17eca596ac06c4d431f1050dffc27ea028e4207390de7df6fcb31b9fb00c25ffa754f854c864dd6221c7a75e75a224308fadb8d83e2a15beb3cf6aa8c317ccdc0155fc0a58c2fd76119870f046b40f707d1a6d8345085b74e6f0f1d1d76d6c0f17775d600b5aebd61b981f766ddcbd10fb5f66e88d21a155c09bbb6e92974cb076fe083985994cbe79aad6aaa0aab020ac649dd0b48f204c8e47da9226e23f34e2b257ab49d6da1dc804d3477ad05fb0403e0519926813e8c24fef38f2ca2f0e7ac5dd0e899954a5b4c7c99ac82509806195942931e403d099ed1023fcb47677312a90367dff320324d09cb6f340575097330830cd9a91f502236da135f7fdcda00b32874bea62868a42953c736a90b02683862ad1d71ee1bf5abaa6b13a851797813e8721ffe16e8b6b4682ca58ff70f3b4ab4600eff5135850063b1ec8434619d212cc95d4b9458fa72bf8c4d5e3c09f6e5832513386acebfd2be5c7baeea3a5dfa6f774257b96330689d2cab4be80cd11f556646bc40ea2450ef7558473bb9c9ff55a1631524361c51e6ba40607f0b9f9ecb2e62bafeee2e4b386c8a6dfa186ab3a67214db899be26d2cd2a02cbf0c3ed9c015ff6a61f8c53275695a8828f4b4d44a170dbf83b161d9d1c98c4acd0fbca1d5e84e36691c8c333df9b2eedb29c0fb5d351f3ade24a03fd316cc29950a1dcc17a655d01b5950f61035791e9573ae7aec1b6c79a78cabf8fa3faf70e007e9338b6f13dce7610c32913e443fa46ed6c887b63eaa1ba216c33c481e71de0607b9b61240524d5130685c9aa8c039d39d6e5efa8aa28edca4e5d85d749d8c083743648fe74a45e9d956e91b52d7a6089c36b06e505b639d628711e983ab1c0379c6f3742d08e62ed5209b0e04a9ebc45a4a1df0d7cf162ca59c466a335485087b9a001a749018e1ecbc1715cba7cb54834d497e6421b5849e96cdee373840031c1fd746fac0a67ef386ee2c7891ec0f1a3b5c57cc60a875544d2d1dc8dabc0cc15d17642010ddcd87765701461aa9952df95bb17e19680d08dd2b85f628080a61c2ba1df9011cd7cae57435f6e54491605315dcc8c878a7ed46c05c7737d204ccb0fff2ddfc85cea4fe72ce7764e1ed211501cbbc0ff2ee7c73501b837d360261c0f8edb5d165b65fea599a0f3c9a2c087c8e9dc9e6453d846235be9c2fa006a4851d01189c0669932e4563936755cc9675f5b577c55c37935c49c271e399070f7ec2e450da2556838cecc58743bf46dffdba32e6b5fe5f9cd0e0192dd92f0b false +check_ring_signature 301a3cb1408ecad9db415e260fad06f4410fcb8142212c4613cc49b6fab18c90 37c7ab4bfa8cca5c4668a2cb3e8691fd7fbd7326929525f54da9c9a163f623db 2 1b5118e3d266523a21575b51b44329691fda389e0cd3092ee319854ceefb4198 8da6abb30289a6c6ae1eb0f1fc3e95fe2aa28cc92128d494d04421e5d4cccf40 87ef8baaea42d834d01dd49b698120b7aa7ade1c816c5b268dbf44b8e384a904b789d8823e5053cc95eefbb9d251b053a341395de8913f473ec249c1667cc20f7dbebb90192805b6347e26850d5a30e69c83a231c8c0a9273bb1fdcae9ddd105567579777aa5ebb4060a0f09b7ed4735ecb3c4ee639405f52e77c7b79698400c true +check_ring_signature dee0df07b22500a4d1d3882c23cd25ed122bc6ba9fb86cdf7724ec9673ab9176 83d9ce2cd77f74b559615a65d78f9e56ab97e56d9edd52f11d993e8f3e2d6c73 1 8bcb8b95f6f150e5040f9d7a8b269361f3b89c372949a55483ed6d893b704eb4 349fa8e846c74cf71951361a7e28cb56c415303f5289faca21a5757fe5ee30c802661c79975ed27b3a975280df2419f7f17a472322919d8c77aefa44ee614e09 false +check_ring_signature 59b7cecc502496c7fc4884eb47f1fe6860e91dbf662eea6fabda00a81e41248e f8870d8a87ee38417939cce2128735a4633f511e34968063600fb650667a0840 223 50b2eb9833dee854d5958b140a549a36a2f30df5d4ea5a324cab4eb14bf87a1a 5d365ba304476874fac245b965af07ab642595be740e9394c21af51ed4a3fc62 c1cdb6157b7c39dd3355502a9d9ab616e6325c31657355703b2e0afdbe8ee66d e407aa2bbaa978990a771eb76b58f6a80bb83afbb82276a27c037360121f9549 2245fb88541ea41799092eefe1bff69157684d14c2be45e66bbb9891c034c212 8136600a343bd4c6dfd7ed5ae582cf014b4c2f3c85c5b9509ce1f26a4dc2a5db da46c66c171e457448e754a0acad35564e6be23fa72eac64e444951542d970c8 ae7401719d2a8cfb5e8a1e074713dfb6095e5fb849bada52ed1853d559199b0b 4ae02d252200facd2df52699fe8ca7cb7ad26c4171da4164a6eb46cfd08a5e64 87c45e45ea01c03b974b5f56429fbf976d61b398efe853b1b756f075719c8559 a7618d7d4af6c2a4530b3675a464e308f6d216e194e6461612e70ce9dc1b025a e7814623559003d1df10a7d6d5bb852a1537c5b9c5579bb6d0984b1cff946b4e 0ecda343f351e55542e02e9b124be8a72f8409a17d8ea0d2ffd62eba64c35c72 92694a508ce27290e0f2a507cc06a952fd94506ce86bfc98fde64bd20229e110 5807964eda5654399e6bae463f0839c94a0a5e0ebae0aa7f1b71917b074a6dfc 798a346ad4fa6e54e369462ff388529560cfd201f3a16d984ef83d366a23db5f 9e8e6a637caaa227ed0911cf829c84de66e1d0a60ac7691942bad850e89126b2 b6db22cbaf468970840affe9557a79fe3a11f4ddc785baa1b8fc0d5cb068e87c 8da9c4457e0ca821c59007bb24539585d8f1ea5c48c71a2c8dab96c0ad9bd578 ee7bb9412a970a738c5f5cb2f5eef320d747c0cc948524769bedc5c2914b48a2 b1281b0ae2185b8e33317e9bece40a40ed59d23534a7679f24e4f82a86037982 16fd24cca2030612f604a876b1b52a2b39446e9ce9f5413b18fec4a0f5118d96 e7968631ab51697c39410d2c4ccb5332a43080975279d061b96d85caa3d1ea17 3478b45aeaedaa19602868b2fb2fe2d82c2b1fb93858f51c6216276257d29a6d 8affc09776735cc4370a514a12d1a392d79d14ccbc34defcf08a41ebfd27063d bc55e9539ec7b7bbb06c3ba5526f14c16efb0be0bdcaf036fc29b5fae772d828 5cfb174372ea90f6f201b61b4c2e4772ac6e965ebc28db3128aa33721fb7b3c8 73f372a44714a3a1fa8f436746cb10aa24c2ceb42baf17c02127b3302a21f7f2 7246d549349733bf91d9e0f619f63d6d9bbad59494b2892a8b47d0af9ac8ec49 4f038c0d0ee8ee36ae69d80c724f0f1eb7f48219b7233c965863ccae8578a774 e7d880da64472b907c804a2aded26f4360e9875843c78e3e216cd6d3c4a52c03 24381bc1346e1a165ba0817d4ee6b5e1498a6038d7e1cdd1fe96d70458ab0b14 433e2d8ebd07fbd379227bf8ce9c192a245d19dfcce4c5849737d7696d971dc1 9110ea4f496e8d83ffca75997c8902dec412728150020c8366f04e29a270742a 8dbdcba4f8ff6b9e6ee3090ac4c52693d136ef344bf1919d451e5454976a1c3c d1418cfd6123057ac49ca180862a51821d7eaab59391fded773120e5751b2b74 8e8f5702de89eb552dd820bd827ecd73ece79c5c934915e49775ba507dceb94d 2e490ec3af13377aa572818acaa60e1490e2c25b2a805a3203faa2363ad02239 b02cce8c458a8f420882f9736a04e4f804b006ff29f768ef4f596278d909ad9f 981772921bc76ec6262e4d28208336e27678beef28af97f29e27a02b26a70b58 c96e9105a47a1ee553173bd30c000a1bd4f3aef235aa6bc78da65c6813fae68b c78694a8ee080ba32524e8d5daea141f788ff6527c6bf101b5244e4e5aaca93d 91d35490796279ae5ce99979dd1f1eb1e2211230a056df0c9248e24ad14ec02d 53204e91f86ce189a0347cbd731a620511ed5a5995984c28a05400e1fc69bba4 87de91bf7025a5ef7f5cc2785b50c7357ccb6adfb27608e3c5f501e02379c4d6 74377f227fdbc9602cd688304d9d6e1c5556bd241ffc0dd67c16ad05e171714b bcb04c114b5dec60dab6977e70096740b69c49900c40502fffc8e967179f5300 679460633791a3daf4ba67682a3011a3dd464344e2ab9dca672e90f1656f7ef2 dc139eb8c638c5bfc46784510a9bd2b7f620f920f3b050ddf8880851d34fba0b 57fd6e640bf2038f43f0f49e54da9984b8e768c00ef2f0bb015d7ff0620c2d31 3cb07527df2f39a4ba1f0c90a084dbee3865de8cd41d286df3b98db36c49525e d6883cfe85cc1abaa96fc6a9df1f11b6b3d3171b8adafe26408a238360ef3c83 f5b1913ad10ae29552b02b0952dfb2d877e8dabc0d1d67d7bac4f5ba948d8a36 798ae38730f6b0e69a34fd86909cc07498ff73b80e84ec4ce541733c7a399620 bc760d244891692e5440f6cde080798938c19345c4f0069571b8118013ab9c80 a2cb06028105d4804d55306588fe072744291c6645c8127cd488c93051e31bda 565523bac70d30db99aebf7a634690db0188e8e995e7948ef1918bf9d27a63be e7f9a466d855935d45a846dc19b45f821bf269689ca306e58017dbe9b2c53ba1 7b9b4114881a54318620d5ebaa3c4e4c466e7199f3df767b38fc75981dac2d7d 21714bc9b8f25ceba57dce3b192d61306abece1577cdc9a6ca29c5dd0bdd1771 11d11f610021b4f2d340ed44a06da48591dac54eb4f3bc9bc861c30da2be84b7 e40215efd2c813ee628b17111184c9ec1bcc7dc67cbb3e4b7981dbf039121dc2 e7c91284bc896ea250729b4aacddc85063831635ae77f3162476ad7d6099c6aa 59bb736d637bd91395c22ad4021f99c496425666b6a4aea0bb00aa6e18af74e8 516f00163b3e0e667616a6090de8da543a1f8a6172a49e1664fddfc4d54a9fda b0045a191e7201849c474cf2e857895b26a2d6510a843f2a11e155491e561ba9 6fbf7f6373ebe54354e32b8324720ed8088febbe927157a8410eec0e451ed443 10945813cdad074bc828d11d88cc4e8e2a1504201b72f761fc7de9be82305825 74c87b0ecf2ee3bcd5670e9eb4758f1649463e53f9fa6a60f89a1a0e84923522 97b64c7532cea070e93cace245ec1c2030ec1bda768a3007121d363d1e01cb3e 8687ac0def5be7263c39febad7fb3554950c084e81f85fd50a6c61d86964ec7f 68ac18866e0fa6f6a68d7bba1da1f031d5d041199a118e28a9fe1a050548502e 9ecbddce807fbc4588d17ec51191ec655dffe32cc623c248e1257716a509d084 e31d8f69ab21f1aea78528626be0b618bc0fab50274922ac0c633778eaccffda 0d9d90e3ff709ab9fb551163229d11a0bd364b70ac9fe9088186a2030fb1fe6c 9ca6a9e4345e81791bfdff9b0134f5b06a5b1bfdc400b2a93f16401ea20a456b 5ee198f6303dcf68daaa189082d3810c15275ec468ee6cbca2314107339db385 2b647a1c7b60099668ab17e43fba056a3448078be445fe055a363a40ff459de0 b941864ec4cae3384ce718d1c4d6699ddf35d87745b677c5349d3fe957cd1a79 5e2a21546a1256bad99949f1b4f6301aab442d7958f04caa5a0964ac9360c7fb 67ddba95483b767febb162e5f2ae7ed1141c26324bd46d5a2e17df68f89253c6 4dba966f5053c61167e568db2d6317c9f6b344a060b19885617fcd8df391e535 90050158924989d79ce5a6f08a1a09449d3ef9d842744fc5dfd449b9c168b5d1 ea4de21fd660d5477641c9b93c1850428637a693722243f7401e3c0ad0206a58 4c8521bf37dedf107d4d8167f4abbd2056273eaf61ed5bbeaac60858590b0839 39d3949c97e230226bb860b8723648fceddd34a192bf976a5ba0625168736012 c6b3f09be957cba26270cdb3c44096e1f234ff6d29ffbf200d7de02dcee55f8e 9ba32b83508326e078ebb21cf2f0fd2a744e2056d4dd6c85bf30da0733290886 6419b7cbaa3b8ad376d567378e0c7aa1c01cd9551f1d060aee53f17a6f866d24 a02ac253c8b7b6210ecfc5ccb095d2b265f21c9a710843d91c5c75dc649f21a2 426259bc570569de6a5828604aaadfc6ed215155ca3741ff1c0ede3175072b57 dd79d3056e0bc184c0a29972fc2749652a00287183d9913a285147f8eadbeef6 299b66ca0f07fe45e57352e12a744703dfac199d3d9676f67ff3f1d0cc91dba5 eef688723051d07a53c93f0460676c5b597bd1f614733495447cd173cab44dc7 2a5688e76286301ac486f9690f5622e9650e67acb49484a8e8761c5b3da6dbf9 850708ad0163d34aee6be350a8e57f078b24a8c92ab245d6db48ea7699d1379e 29d80ddfc3c9c8f584846c22e7e8ef6319c7ec2f29c86dda766e57efe6996dc3 0c33640867eab5c1a1b5b42aa2644c9f077d85b5449178dd54c6c464219fb04d c6dac8e73949802dfef1a9eba69ec4a1c2cda58110b9a3035bcde7e12c34c2a0 f342cc78891976f592ac9646266ab685c984b6207dcaacc335c64403d44cbbed 11bea9614aaa5a9e8883d74a2224bb068dea33478c88e9d0d25f82e16591a1a7 f5c4d6f48b47b134974106147c2783b4eabf1d3c1873a880a5873cd53f0a8654 e1791978d57c36dc328dbd0055751cbb5ced59103bb99234b8971982b14fffc9 e30678ff91669b0ce9c1c25ba0cb3a58bba55c3479ba02ab434da37c338e28e7 29516d628ed6b2aef053f7e0e52afb2e687b8951a1aad520a80dfc6f72230db6 0a765d545d0f876d1a2e34e72925f3a70eeed8233ea6f1b0a0ce34f711fc03d5 372d09293dd67dd04cccd6f5856b1a1def9de547b194c377d83dbbc052cc022f 089df1d4ad3d7981e32e14983c45efe35ec605f09ca56f03a084f1bb52c7eeca a152e7debbefa8d97e2af2c6a4431a9368b306daf9a3151aaecf52bb539fe712 1dfcd4821c79c1f104f93cc407120f40e2ab4556bda10ab61a416f965ae1b3d1 df71cc1c3a14d094b8f6fef60f1c4c14ce33bfa2e4ea9a1e0cfaa1c33c5f79d1 f9c5ac154ad869ebcef3ad861d80220e40aaadb89f650f60f1cd6d4225b8bdd8 f35f2fdb8a356b3527c1c8ef1eaa66bc4fabfab8229ef75b41b274b83869a449 1520c98e7257f7eccb9dea0dbd2b1ad45250a0513a4977a39a6d78ba352bd1d8 c525202984e7477d649c9796626830a252b64cd634794b3e0b465ddc58176ac8 aa3157a8aef0c2d49eafeb0dff768c88a8e4bbcf107875155f58a3b8d8bafaf3 60b1f7cef104d646023d68f29902e43b4d01b2fe72522e557500aaf04705909e f10e80cd45dbf5c58fd459e81987ebb6861925c4f6e4370f3292be1c2f15952b ba08350c8093fac033b54b74136090a1f4975bbeb8dd3b77aed093f468de3b34 1909db930773df6f33d994d513b4c6cb8c2a257558751b086f822bea463d270d b907205c4e81789f32db52667ffd6123f1353effcfb2a687c6c98e56ab7982a8 9162cddb668dbcf3259a2918c6a203731fb7ed9778537d948aa68d5071b69179 2163edea684c858ed3c4bff14f4c7d1e09011f944c1fa4e32f410d98112bacc3 267f4649c7ed2c2d6f9d5e5c367e870f6bdf686331144e68bbb0f2db95779c4f c59390bd1cf50b2c2bf9c8f6035ba19970c379a24dbeb7e1d222bf96caaf56ec 2bc15ca4820db4fa56ed01668ae04c5b33c9d58eb9a7f976dca8c35f8129a9a3 ec2b1daa16559bd681a071dfb65baf42248d4f0aa9f66bf80ce10a56e05d3efd c17510d3f7dcdd55fcfacec6633dc26ab001a4088584a680f45852954f3ef0ae 3bf4a6bd1ec6dfd76693444d0ad769a8e4f535434cfec83864f8076629ffc8cd f733dd358522f691e20d12469121f5c74e78e0b297ae2b87cfbd29fcbbfdaf08 72215a7dd1a365add9931881773ce5510758bd34731d64f7b2143cbcd1fd9048 7ed42eebb4505f39ef7ed6557e85f2ec4c0affd66d8feaa685d3a4bcbdf50746 b131f20bf2566b9a3d4a64af043836add322ff67dccff34820f849f1c58aae56 8cede565dade1bd2bac82e754e536fb995e397ded3e7d561e3f4aa15b5809426 4e5c086c246245e3849b4ace339dc4450a521211cedd556aaf7fcc1dfe9c5c0a 375cb1c86942fdcef817eed4061b2949da4bf1331463fdc784fdf7db3a353770 a6cb6de7223411c6a9a121b22d7a1ac9f70c717687847149345535d3dba80687 b1789400c6f892afdb7d430b3d2f9c2d7f1c2f54bc444bd9bb2e7e96612e82e3 02fbc894a2e8f3070d6aebb18012254c17377f8abaa8aa0e1c1fcb160f9b36bd a6f2ee00643e2498cd0c7deccdd63bc258cf442b49e0b82f33231a02de3a08bd 2864cb9f5807dcf0896fb82fc6bdd5ead4160d07ae9561b0e3e22650dcd01a54 81084be532a275b086c31674f04710aeb693eda722d74f49332700df119ccce3 25e89abe92daaae62bcd5d579da350c24f4db6ae2391cd6b291aede312039a98 82a56eb70e95fe5fe19fca7f05620b90cae61867681bf78cffb9de1ec807c896 54bdaf6675f4eb4dda917d031270937aa597244f9ac6bc383ee9f9da6ed10a91 94c52bcf8c797cd74c322c558d5ff6770ecc221ede612672d5a544abe8917a03 c69697ed9471e1a6455db0a640aadeb3ce2abc6295ea1fe1da80ca106bf37785 a1230b2b85d95ba74b8d59c56f303cdeadcd805e156f24971e5f5320211f0458 559f3415e16f0f12bcd4489ef7ce04da38961037c645d18fc1fd1a5b7edab019 b744f116069fb3ac0f7fd78e095fe901b0a41a2c37e1e4827b1da67dc18047e9 177c94e3bb0eaeb80723577d269eefab2d92298a574a0c771380d7aacef04f0b 55a4e43261875b59d75b42a75244a0d36dcc4e625b57c0f852627101b1a07771 a83afbdfbb46e6a0a58aa1ff8f44e95d3ae33f34abc3b2a5316adee4ce84faaa 574ecf378cd8d207cfe47eab7fb59848f13fab66df48130ba6b1ebc3d1aa704f c3bf347f4f8aff1079712b7e240df6b51d543ab672a045b54ed298ae44e9402f 7d5be523f1e235ad5f31160cb6ab1f07780d3beda5e6ff205e5f243712125992 b48bedbe602e50ed859b2b0cfbf6d1c621501d388e1377617440cde6c975dfe1 859b04801e48731e5669c48270df47e6bd286a30970fd425f07b08d3e3dbe130 155927aa9cfdac6a3591e172427ec9fedcb7345fb29d6190bc9ff64ac57d2167 e9f62d8cfb12230700f386791ace7d2cecfea01fd7e55b510bc51a1257808f09 44d5e37fcbbf155b9520956e43d791e5814288581d92aa9fc4b4968129e5170f d38cb5149270a48f1a0154933eec72aef02c51fc98cd4476217c73d84541ff92 2c73029fe57beb010aecb21ff675b4de79131a1370900e915b5be124bce30b13 f5bedafb064fcaa830fb5eb8c2bd16e3d3969bcc3b181195196c776886b1efdb 9eb4fe9ca13a006316b7418e85ea41ec9d08b7cb4aee86f4684f98982d80576c 9af3905fa03a9f9ca5ff5dcbacd5fc630614da8a1f17e68c752e73689bbb7fe2 2f027b4764c29cde97d62e2aa98982254238e6e61d3e0afe1c4a6d9875b569e5 d11b68081c731c4c197f43af64454b73c7ef8cfab07246fe6bacd1a29b83853e ad7e7dad5014207e3a318068576651a9b2e97eac046e4abe24d2168fb7a450a9 ccb0265b3cb1a2ab89721f8ad69564d6c18c0364c7a457bb6d6bb40cd6f97614 43013e5eb50ba11cb7b0893305ac05fd630947ad767502e99f6a38c97df0f825 38d7175b35e0c9801a701b8ec710cf8e1d6013002c48f1e3c70b2805c101d545 6afaba13147b60dd3ef1a06e3c850cc632b66100e9680d01de00d9ad5f57f9ba 73019c74339abfd9bec67258ab9724ff38fd1a1dfeccd99506aa0ac8867ddef2 4ccd812455d50bd3c407b03e5dba1fabdf7fa9629ea003385893bffa7155724e caafaa74739abe11488f220f026030ffaffb7efb8ba51aa7ed7cb1af08ceee62 2842ecb2a38c76610b5f09fc8e8ce88f349802ec97661a4a99e4862301a8c4f9 06c3bc4d914965b637d4e59769cfb2ac80f52156a745a52eaae331b0c0a440b8 d79c86cb065121f7a922821dcb9b297cc09ff3af854bc759dcbd441632e5dfb3 5c44fdc0e9d3a2549d2b309b089489a00ec95dc784770459f578117732ae7dbe 751339fc2560f82aa19712e3c8f9fe3494871b4b60d0eaaccb8a820f80ae508d 84c994a0bf558114337ee285de6cb331ddbe50f7849e4101e5e8394bfaddd6ef dc433ec009b743c9696dd75c972822dc125a54900638f4dd134438c85b95be76 35bfff537375fad8ffcd69013613402749ac15e9409b9414bd897e713e442c77 7208492a883e674ec5d18203f39de8cc429d438b748512d76416ed8324c88b64 30bcf39b2dd888ac6f42833c5fbe4b612e672efd8cbe12368c2edcfef460bede 4f5f01619487e0cb9a172d7194ac9e8cb019cfce8ad56c4c8639de8a9455f4eb 3e314a5354d52ff13e27dea81ceaab8451aadf3d09f955c3c43148a40fbcad81 7a0dfefa6d15192bd60356dc6409e2dc2fe1cee0c98cc5bf61c96fc90af5e822 0d404e416ea4415da175915eb2ab05a8307845e44381bc744efeafae69517161 e5a6b779dc5bec67b4503397e6cce8911c7612fbe8889af41d1b80dc414892a7 dcf121e01bdc0086bd587126038a7ced736f61720c67e9472144d1de1a73976f 557f24bb38ad3006bccd41652b15efb0b8d372696a5cb428622c18407a57e9ad 6b674ab91dfdf970d558f43e389dbaef7c30cf8fd9fe04781cd028453d4f7fa9 190733df295297c40548d634d0837a90a91509d5ac05c9dd19972ed9d764504c bfcdfddabdb3e305dc3ad9a6050851f2bee2f0a00d91b86de7009de696aca887 a428907b10934977214375323d8c259988c9a31a4b11b3a58d4b0f62db9ceb0e 9d2d0656e07986b9205a1536597385975552ae3a3c82861bafc3a84225464812 1fe2effca6bac889beb6a9125a401c27bc411f5c6e1159242e4ac40a583fbe17 2b497e7ae98afb154d1a9da574dbfa52659a73141e60eae2fd9b8b4035bbbdcb 58738001a1aad1881e2a30c9620c008436f8f2ff7fe92a31a8f08ec263e34b80 e51befd245ac6885136af054bf4d69534f8693ee14d62019e536cd1514f58274 7914cc70cfe8d6a19b8903cb940b7a47c94e26adfceece456de13d7564445593 e0f2b367b7a32332763c68fb9a9a175f6cb1fc59d1f6e5febc4849a9a37a42f7 5d93602de2e2ebab3fa9509af7a58bf87e285d2ee2b2cbb7d15f287460dfcd86 57c7d212a74968b716e82ca8b708497295bcaf90f5bca0c463dc76717b8b48eb b09802fe58b9355c6da7925d8cb7aa869594dc89c4d0260bf69bae43aea3a20f 770089ab27bd89dae7141c403e20b7ce2ecee928f680bd992b3ba78e35d9e1dd 9933a5d3c9e4c481d1ae482c7d4c8960850cbe567c6317a24527b5e5a719db38 b7db0776f6d797b89299ebb3f03eb1e28849c5c2bb0fdc470a6f03a5ef7a7799 a3d12406f906a0354bb829c1a57401a7d91e83118bf62bc89037ce7eb683e970 99697c5cf9346b389ee4d3d4e4fbc0dcd938dfbc035e9d8e3e33af8c10fd754c 89f86b8dd4003b2b6c25b87775ccc882329b2fa97aa03b5d9b650bcb275cf496 0895f81cb4f112710ac09aa2ccf02e3983e9fdcf8a1f4c785cf10941ef055dd0 dd09bcb05c803ad1f8dff08ad0790e814d8d26b4d7f4b439da574b9e3e2ac05a c02bdc13ddf41164e765bcba63719be93f699ffb9390a95cce0e2f952cafafad 01b268127a92a0726c121d28376e3a53c72f7ea9d7f6caa9009735c557bd0e3a feb27e3ae7a9c878447ca59e502f2ad5e9f4af09972908ee31d6fe5aa46e5868 45dfab236e1c7c1c85f05703004e97debd6dfc2860238745056bcf5deb523662 6c57ed0535afaa6a5daed8651e011f8b518be60ea76b2825275f72a574fe0f06 ea32b52f63a302c554329364741dbbf471190cb0ccc12810c55af9974481f047 423000d9fff61f490d5a8578f223cc895963e332007e696e04768df82750ec12 143ced971b5c4c541bb8ab4bba5b6116d7e75714d5f4aeada6490a681b06c9d8 c46f57580c9862348f98ce7eecad4dcfb1a5da228bc5572f2c27aa00d4d4e80967fee4c053b9350387d826138f7af87fba34a87cf7165c2d7f042ed535664d034cd35d66632afb5d0a579a44b13ffb180d6ecbc2aac82892ef78a4221b2e060bc415ce1774efa0903ffd09cf32735bec0a91d57120d8d368e602872e3b670207c5af8083cb7bd00505bf2e0d76c7d3cf34714d6feb51e17351a3c9b633306805aad61efc215e7854f14bb310f5a75940c9b101329add04f0b5e9634e27b94c0f5f78c4263099ee66a07f74e990489f172fb0b111d37c1d43c6d52899abf47f0bef27126148a434099d7c38399011afce43bfbe6b4a2ed2fce9a835fd4854fb064b2d16ba605e307011d6f78c582aa6baff27271b9c53e16a3ea374820c406d0cb0938b6debd32b6019e1d7e6972ede8119a57d28cae08192568a7688f58a9c02df801de047eff01fc0a3fe0f896c15a8014b1db4650b484e85188e9118a96f0ff9211e8febd304ab6db86bca78e14b0caa2bd0837c10d1aa095a987c53ee8b0c6322e52b6f6f276be1fc55c1faaa673f71c7f2d488a854a02d7b799a45f81c025e8fd647222a0001e1f639ec315fd01d46367f2891e73f604ca04bb04fe3cb0f94e30b413fbdb18778965045212ab37f5089837bf0713d562ea4b91fc607490efbf4bb47ffac71cc568260b499f94934df578f4f0737ffea7e0b1f20c93bc00723723a9bc34843441191aadf4e5492e87d6f8a49d647b8d20298b8f7a4dc1601b2a530ea4ecc90e39e7df5fb05afa60b88fa57de58d2de8df428bed416005800f8ac12ff0d98a0da4fc7e8c95b5d9718c60dcb02eec1193018f3e61dd398480bd4568b7bdbe81a3e14d04630a5ee08ecdae1a8863e4867a11b013de492f3d0073374d81647d8e6392f2ae0deb033ec66cd72d29bebff98c000a831da42efbf0c544f9d4a1fb48928610a135a49379d2cb30391af71049c5f0e4484c45cdf2f0974d28a7fb6cef438db6f94453a337a2436d17412db3455a34cee7a4c77d36206dc9d2855b9b2e793c19ddc236854ed797d46e1ec3575d27bfb7ece08918a9d030b886413af553c9e599b7242ea8b4853592a618cc8a8d95b145e0d91037462021548c782dc3fb10ede90635401c4e8dd4e7b14777b3170f9cc88b06a537dda05fb69444816442289d48fc08fbab7761d7fd58bdaa4ad189fcd9a844e3b786a01dcf58c615b84defe28ffd6112cd1ccd206aa0ff0242175ec68dd69a4fd161d055a84611284cfbbeb45df8f3ac73131f2dd3e38c24dcae66ff8cd9c922a5e720a32d354aa6fc9cdef9070faaeb0f613acdc0e605f9bf138c88a61324f602ed80efe279433968823dbe81c8be6dd684b616e86d3e3071abe44227215bc6a194a0a951603669ec15db63631968d7bcf68c3feee20f697e793dd89117eb61aad6d099a5c1bbc3e6e11747a8a86f95c02c81325564092ce8de60bc0cd1b66f9d0c308e06ccc643560874232e5b65e30bbf57d8b5bb8a294b42a9853ce03521b133208f504223e097ecbe2907be4962aae9aee158331e13603f847ed247d508faf5a0e2bf9eb73ceb4907ca0b24fe4f8e0b3f96d3c94371057044f6f9621f56a12be001062834cfc4eff5a198ef35c0a6684bb297dae8c4661dd45114e360ef499fd0db33bd2f9038ad8408ad1bf08da99f0272e57bdb55c11de6fcbc7692e4873580e8c6fbf72e953629d94855d80ac44a137740efd26e04c2cd871bdb259ea604102695838b771a703fc8db67b611fc839008c23929eb71929b98bd5db38de47cd01f6934f12982b284a7beb2d50df53572a130f5e4fa9b6a50cc12b7a305d10c10b72afaec2572ccc3648f0ef29d1c74d72fe16f51bd0c261615e0988f69299f80ce2d8639ca1e1ac8d4d104af01937eaa7f99ca45c5f2e28b9949fc7fe163b320f21f162bc1df02e046de2f03ec67a45445911831cfe0e5e049bb4a4a8e47dc40e812b3e9a368e4fd5dc72766876142dfe1be0be6a9ec93bce4c8d9ee0e013e50b58fd4c77cd0a8239a3a09bcc50870e74ae52dcd1bac5f121d386db9088e76b006e5d1eb52674c1545357f69c61eb115fa9c9d60126f3ef985ea51124487ce30ddfc13f07a808a511e524d79436769931ab3ab3c536c395ec1a57049fbb72d80256ed53446c320e2777f3cab076489c409e9504c7bf05cc9aa34aa4fde30b6f012150705c06b904e1110ff605342c5c3e7be5c6bb4a5b42c38771208bb24a4400b7221923faa759e9a0227b949a9f41c3865710ac7b62428c31dbffbf3b79d50b2c4df44448ecce6b97c579dae76af149e9956440f7767f3a620e38410e5c04043b07fd226e6684ac8279a160c9d676a45c101e5fcaa1e8576435e69160ac130122e5399e5c02f0acc44a35a346f43054885fa0ab4e23b5a356bc0655c0f49703723c00cad3f05f02360480345c5392457a7f754ef769d118d507d97efe49490e80eb8063cb963d255085a02a1aaa9bc9b584dfbbe867a24365fe585d59d07f051712b61d943db440d9734128e4d118b7a0ac6df4677ebb0402c58c9489d8f00f684a77defc270953d5d77ff4e522a03af2d37eb570db3704573ee36906c7a30e484e1ad7fb23b789c13db0ab188acf8ac4cc2dd4905a0e736851019cdc94bd010f54c28428ddc46605fababd680aca2886da08fb0b02fa73e8cd60e7995fe4042fd73020d5a1dd61284110492e3b4caf4227af7156fa85b71ad1d45b0dfa2a0ad945e8e6d48d4ed703ca5efa0f9e8b5505f0957e84627bf38517e3f35d60990fbe6483f204b0d34bd231ad7cb7a5fe664664a1eaff054f21ebeae4f7b9ef380cb2365a06e5198984c96170ef75b2b573bc0769a5896dd4fd38de91646e60c80a452af740f6cbdf3f392eb4ed3cbc8234cbf356215b9c3324fa5613d1c30cfb0824210252537874da16b1449daf2b5c34a78880481d7d4a83944e237b5e7cdb0a82fb3ee418709c44fff544418222ed99caaa105df0d38efc19cecd35eaf2180a179fd7589ce9f26d98eda2cfc5e9ca44d5746ad2c1148940e197d91299f64f0c1f40f039f513ca84c0b6dc0c346c00bdb498548678474489c7c9741a883fe60e5452afcfa2b84bc93668b0bd254601a045b9a498ebdfaf336b2230b8fdffd30be87ffe294e3b83084619f0f4688d04c39e0a85d344053a879ec2c3f533c15208e00ec3fbb231b25fcbb9869da89f09722286ebe6b6da8618ced27530c9a82e03341341e1ecc8d9b065d00699cea7e783f80afd96e33767fff54e7a550c21bd07aacf4bda39d6dad9ad0ac2300c53ab5285013b3c897e02e43ff107ce62d85a04b8b320f011615c3f7a98df2063ea28ad5da50667985a0a1ab764bd16f6831b088cf8e701a60d7243beee076e04e1e5825997c4fe12d35c2affed7d82dacaef0faa8c50aef9014c9f50157a43c4ccda342dbafa085b3e0e8d092f7b577cb81e0af8863b00cd6ac9923a4cc9d374740c9a26efde4cdd7b8628cebcae3ac89afc0dcfb55a2e8a20678b412e5be51feeb93c395bb6024014b64f1bab71e4249d59044f10d0a65a27f5a87635c39407072ec04f1612472e6f716a02235dd69d204d03b7156333fa5a74e0ef69b2f516d7de9af584069ddb5ced9fe9bd3d251a9e97028c042b38eb178648204c80f3056b318456ca49a3fafb9fee676869ff68c2a00485b643172bc5f7fb55fda7027e525233726a027fe115e96aef0d37b85714710399896579f2781cc9354b2b3036745ee077c104b05d4ab5038a6a208ec8ae1e0dbc62d2eaf72e27fdc985f7975e6ac4708dacc65f073c04a1c7190c9380f3fe09c314e488a2a188113b438ff5d66d75e6c1d9bc3de8bd6b26c3fe2b281d2f6b0ac0a5cca1dc519922a9aa86e27ad218866edb6a40f4f54039a6d07c32af83d90daa885ec45eb053768479e0e1de540dc737dd1b18e9b682fa132f82fb8adb180002a58ffbdf3f7477a45793883a0b1338998a1dc24a5afcf8e31bfaa0361e6e07bfed5c813fcbf7e9771bc25e147d5d5ebce6a173d5d2dfef550921e932b5a400d9023fa2de7a4fef0dbfe7a018fc91334b37fa8a45d802594c646a1ec833720835563a5e26accf67fb2de25a89e4230140b4acdf7de50948daa651f9eca3850b38364aa82084a35125c3833f5589c882c7002c4a0d21d2908d992c4dc529e40f417c3ed44ac419b314a20def57dfc07ba46a05d84b85de35df8d5aee2d3ecf0fece1eb4a59e9dce44b44e83fd114bd29c75548de2ac50160b1f81e29e2a97e027fc4b9d1d6710fbeda22e7f5d73d39822855b9946e8bb9e8d25fb42cc9a4b5085430ebc037ff3efb450fdb9381420273af47bab5e071ce5607734d2e0c34a9095ca262af1ad7d8a02f66557935af86b0e4135b30e6eede3082470b503389340095d0ee7a725f7aaa9b161edac0527439b799b877bc6c36c760020a1de989de0d06af8b1c0c5ff03fd375d50cbe7283a8b7d88c365c7cd2f4cf9dc08e6ada3a032c62febcf1d3ceb5e58f9c81e927f8b5fb67d2153e0725db8c073e00fe756c070f2a745e78d5adcb0ea7da5e99a21ff17bf4954dc61b44a4d86d9922e9319e033cd2ec165e770bec0269781584fe1b18d34c5e5ed60a44c0c3fe92163f69020f455bf670832e1beb68f2bf2cd0875642318c9d4c3ce55411af32d17e3a5e540e3b46e0d68f30c2de46ccd914d8520a266d9c204a004c7f66549f61d2f28f760cfcc7e51a01326bf2c530731379da225741b0046283622fc69cc2517b63263b0c9daeefaae5221aacdec0734184ea3a3fb89b458b7d53b1a7f81700e2c3c66006b39d89f95ecd6f3efefde38ebdf1363e29ed87a4b7c51ef1202d71b0941ea3017a64e5b84d2e6ab7f355244b467c5e680ef2a8dfea6e4d6070ad3e6586d4a60b68c25f1008af503ec8a8ff17c94ad71471999a0e0af560b556ea77285b865e0fc08b10ad747c441d47a732e755c3f45309de2e13adb0f5eca035b9e2276da90148642be3684f89b7b544288ffef3dd46a3c291293c7632318c1cb1458e97440b18c6508b45619e9c8931b7cf8129de118ba17b3d3e4c63439c45feacddcf930877566d224e7554827c94f0850ba1535850a9b09b8a2bcd6a18c4c960d39f4a05c7028ec8689769f22765000e05b4650491dd2c3acab5b0553602d1dbd80bef028e0bb2a9ad6819f8bc0452e54ea3257628e797dd338a70411da4a347f9006a066d73502396d092b0c65e26580175d1fcb7846b7453638bb20baec172de608c0f5b408061543690f9879b7b29840de735352ef1deb141e49e47d169ae1cdf9f0202365aed9fe1d099f04babc4d0e7de27764ddf11603f0554f360b1496b12c606ebbf65e8519fb51c601e9ddc8b2ffd28e6b64db1b5ab3ca58b1bb33c2565f40dd8ac37f50329a8289fce1052ebb67faa78c8bcd092f37a7fedd497ab7815c908ed0efc882f31581e8ee5224dcf5c17d0714832600957704eb77f35099c99ee01e9df395267fd299d232f81c3682cc3e7b69413862698efe9e9e67d782eb1270fbf6a8e33f8680e0b27cc26d959b949787601d44b5319c80c73ef92bf35b493073793110efc8699e23e11d80972f9fe675e57d759dc3073686b4a079225871909238e0cc06458c111fe07e3b04a59d6f2a64ee9554d00a74a86ae9f73d6d27d0af558c18d2cd8719e54ce3b71c5bbcd8d6a543a598f8966a046c06587b403180bb67adbc7456dfb2f46a30e93f246b2d40994dbaa3dd6bf36c07ad1dfcc5a4d0ec60be534d86007c7e54d5d799426d68a98e58ac1905da37e945c907e582aa80710ada77deceb0476ab00a092faa1ae22b8053c01a6242673e42e8746cc27f400c15c7d5b268608ffce878dd67310129f66f0c1800d0e489e22f1f99c569a9108fa84199c4855da26436d02c9626caf353a5f403c55281534b1cf407160911100b5987d48b16d557f4c90944599c3f94d125a9461d373bbeff4de57119bae1a005e49b5fba6977150d4661bca78417e817412ef85c5b252d08ec4c2ea140fbe06e8766c0079c9c6edea1a0ae10b588655598c2687233b12e61eb7ad44bb9cdf0dcda7d27965b13fb2d80e494457ce255e9be620d9a8c756a64428b4d057f01907ccc00bd099a4d4b1c3a5645a06dc86e31dcd32b47b79e04e5f97feced404b70f94dcf9393a5c50e0b4786a39e79fbd148632e59a31343a7cff680523c8e40e07526a2fac2f39b78c3ad12f9186f26d1a123455ccb267c0d0348f297247837a046c16fc1f01610df4d3a20931b8f760a3eac97b119bb0f73562edf79b3842e0033f4b17c439d406fecd1faeccde794d6defe9561e49bbedec18faa2026039de06b8b7a4c32beac2b49a08a6c68ed6410bd6055c967f5f6854cb9e22efcedd2703849372bc9cd41ee31a9559231a6566099a1d798414118c3c41e201300838d10bb70693ef1b644a5b1f797e135ed6d0130146d327d42006cd133518a7d7f9ce0999b2136dc18e6ec9e27598309c5ac1a82e81c360f1e62bef55b5580a5d31370b66f3f8209b03fc311b01a61adba8fc319d28dc1f64f7f11cd10a78ad63fb1f0d39c9a49c5e18eb82fe124af7aa42dc51d44da8a0cfccb2b4e75ca725a2ebee0b26ebc5f46692e02fc34531ba8eb6c9288a18518c12275ab9d851a5df834f8f0ad1bde19e481a6301752e79c1270de047ff9c4c5c7580c8d86362c5983ef73b088c059ed413f64f8bb3a1df324479df8c906e017fa26b1f0458bb55803303000617c74f888500d2e8fb1473b62a04dab3175391497343318fc7a02535678efd030400f2ec24b6a88ef0ead4b59ca5d1753c8fdc6b7f9c96a489da4416ce4a6d085a6252fbb32dd32430081d9be966e9c50d0c575c88d91132d08b11eeca86df0e52993f83eb2a3bac7fcf54e90e7ec396b63761e2e1714fef1d5e11e445a4de02172fbca31fb894048f3ab207cd9ebf94e80304ab50c2bf55a062dd1d7f66790f9c96f39987be1aefec8ea57e7e6bdfa7bba44806eae480ee954fb665b6f8890f7b6bf0a69276757f51f85a1eec47444a654fc223be31c71eee44d84365b24e085d2aa9630575a5cb322bcb5fbeacc3c43d705616ce9b04263e5f3b81bfed73015237e432c29d4866dfa261d935e58e967cfc8557a1a1f740d631be6dc29f5a0ab12ea96c50d3bd20cf85ab3d47c0d60d1146b26ebdd1a97a53a877226307db08e801f606ba7ab35df01817e6b66a3053af210d6404e2cdb6292e0db681f8f809a3ce2bb9ceb399856e9f5e0082b0548d63b6f4a9f580e54be2349ecef021cd0690cc9080914da9f3de762c9495d7b50b48101949df8b69c19aaa7e18ebd1190b0e5917e63b114e4485adafd136fb06db0ff6d6331895d6bcf3c761672601fe00ae7ad87e226e87b92f70a38c5c2a310ed9d5795070be296891ed308cca5e6a02312a4ee0fb2f9b69b8a7c6d396f3f9228c0382f0cbc5589c7e6a17f35b6514032c7c626c2f275ce57d1a27c05b0994ddaa26ff43c90ebae0563107238804e70f0c2474114a28c45c936382d36725d7b1655a90745de51d2feb0b5a886a651b06247eb75364aa920d148b52a05669577446d3b75cc8af85edc12aa3beb70595013f8d25be1e37fc68d548a7e7a1797f57e63f22dede54fdf83f561c7d0fff0605ab1cc45e4100d0736b4bb9d7bb6c8b9f17616746eeb3233f01636a9c444e8f0d262ff31d61f38ea1c0ce131e6220ccab0ef74ed8f7528aebcf11e4fb7f16b70a86543ccf7d42ac003ae38ce8f0970df1659ee5d49908c777bdf36435f9cc05003ab9dee42694f74fc15c40c3dc49c72056546b6aa473f3ce9f71b07e7585840cc1b3e7b4d4bc2c5dce8bde2a3c184479c8e361730d4aa28df1a0a342f9671c0c27df45737e6819108a1e907cd94d957f1a4ab1be1c60bd3146195af77577ea0204c6b9271ef4da7fe25cdce43d0b510df81c6b7a3cf61a9e303ca41694ad510e6b511e967337fca45a729ffc9404b0505dda910cb539021dfdbd5bfd5c673b045a3cb5cef1dad16256e2dfbce8fd224de3579007adf3ab079d1be7fd92e17d02770761c58fd447f92dff7960ffb6560f20dc57f8763293aeea594ca7f00bcf0b0d6f548d8bad5ad4577ec461880bf294021ec3dafe4f0fb3f45900e9554c9a02572a9cd54dc540a95bb7afba7120f66aae935e473946fe1add59c878772e050d5ae8a933553d3f1ec92befbe199ed40dee7dffc477b81dc3d16091af77f839074d5aab40492a3f094eba51d366d3af016c9d3dceab73be586ebb13e3bfc988015aa84065ab8a099d257c00674cb14d26adecdbb987cd820f55221f812f69160a70a7652a882040c601d34a3647cfa189b9089bc00e92dfc3fa357c99391129026a9b132ca57c8521dc9bc10f0ce9007be3489207288cbe01363626bcc8c935091c4b4a1533f7689db4417cc4e293d9bec74e5beaecd1a9885248f25114f9880aa39c1165079a327d8e0a1208c4f53eaedbaa590cb1a5137b56c00280aed9610d59da36e9de99efc68bbc20e14d88d2c6dfbc842e77ad63f321cd9fc88164c20958b28dd9fa93da07d7bd013acb9d78ac04d6e47059594a8ff79bf4e25dd739094f1f1c4ab8a7ebc8a3c36b2e5bc9736017590efeeb023f6005c6af20c56a3c03d63872008594c16bddaa714e258c750ec0a4934cf2a551a63197aa7b5baf3a0fbebf6690924a3d4b8fa67fbd3782df6420dc3df897a2a7b0af1151c9c45c800bd2ac6f84ad698e9148ca953598244db55535ac281d9ef7f904c74b2b7443210670a9806aced2b3aa82425ae99d1b0211f2d01c052e3baa4513806b61670bbe0c920048b13a9efa61873edb554dec1883ee38e777d99513b4ce63d5692a05ef00d53f24ac8a97f09021bc609f48b31202050247016d40d38ca6a5bd0a5b47a203ff09191dd03a0ffa0b177f6532872b85dda7de8fe5b91cdaeca2347e2705fd0052ad6acb8ca7b91ecfad9215430cac0a68986773d48e4ba3826b0edad6a145056da43d2d3536be6fb3e5a54ca4d379a92afb4e1905f2d52526c6216eb768760fd52d9479471a8e40c556ba25116cb25f197978385077a83c342644200c17340ddfe2df00d9d6bb2b9e5e7008258485e0dcb7a577b62268741b3047180e40a70d44579eaf78b6b1450da0a8e35e1609eb4ea597f831d27180e5073c92ad2dca0dd0abfe177b3f076c86123607bdbb860c471b636024dc121e4d9b049887b3750baf44a7a507ad11ea70b035d8326c0d0ac209663219a83315f4ecf32dd40b1f0cfd3945a88cfd1275d5044abfb8a511bcf4ba55f25c5612a6816ca71c041abd0b4b903f0914a7610e0bec48d89ec0d4fedc36a6428f8f8bbc5a21e169e5637a04bcda78f7240595e8397b01bdf744598b4dbba92203db15793ff617374d06b1035d7b9aabb3f617a162769957b415972ec495786565230cdf095130e4e5ac470fbae842ac7e057abc4b9ec7f53b91abc425de1b657a30b33774cd02329c37f90357907684386bb7dd6b5989425a54ccd4fa1b31126d760cbda02bbb03eeaa7a0712ff2c1e8da2f9e9664f5edeb72cb4295ac77f4f1363eaaf3d8c25770909180790a65de51ddcd867f0dd13b58797dfb1c01c2750d48d8064d04fd023ced1aa0ee53f1d89f76a70e55e4e899726aaad34f76d5420de19143466c55a8827afd40c2be8741567cb6382f63ed9589acd4662ba1d860146facfbfaf0e7506fa355507a06ebc5c142ff464b41be68ea3a0ce26dd775216e0847f72b4a129719e43c30bcef7749b9a529f358def743471b8b96e497e1852fc030229b5379a13ba81ef0be6bde121ea3b42b48c90d2cac07634b35dd574df6c626b85e990ae4d55e6a90c653a9ef96a43d3fc8d10ee9c10cc2b7409d8fe2896f9d724166f2369da9933090847043327784d5f1e46e7fd8b0ab79de69464670faacad7ee2062776cd0f20cbfba86301b76312673972fccb6eb59fb51ca93ce43e0216ebaf1537486254101e2d9a602e45e8e6cdb3cef635bef29dc9ea9208004c973dca3b220d99aab330e1681656ab05c5cafcf56a51238e58fe8f407da3a235d6f952cb1b979a7c37105f58e6328910a8d9c60c671c0376c229470ed7561b9601f17832e4a7593d884012234ef9789020e3910f214cfc52aebc4afcf6167ccb97f041043e1b5110939055c08ef4ea7f52f4d882bc6d7442b3cb18c663aae34ccc67b309e7011d3cac0041307eb5effe2d85678a97c33e70af21be5812b1a1ea034b4c623aa945d02a70cc4851deb88278233067e9295609e83e60742e209222a45b694f4217917c337093d1ceb2db1d16d67e6b753bd3c331dabbeef0494137caade5530c6f9b113ca0be156778d8284be34f8c743465609d84422b23329d50ee2b3596c3de9147d540f20314c487cfc94877887360fbb77a8b7a4f3275e12c339d41b54c30cd6a2a907bd54bfb96693ac7600feffea832bd3cab3f75489ee0e28a2392bdf75e2569a0bb1513af542929898be511ebf3f8253f681e79b90c88635949361aaf3235d040510f74ef278b397f88c96f65dcd47711be8082ba6c2da98a461f6bee2ea4beb00835e6423311a44c3b32807fa0584e5aedcb6887cd25023d5317819519263250f85fbb7a76569f6f402275d02935c462da78c2e87533f82cf5062f776f3fb760d64499bbb909bc13f05fd4bb3d972cb2605d0dc4dd9544829cbde4fc5dc9df10eed909bd87de4f4365906d165c165da5f9fddbe4826dc891a3e6f31a593b561099b28eac33f324fb1717e8e6446578536e4709dc372e668f95a94f810fc05c50d152c1babaa29dbe5119fe4359f54fb766a7489ecab5bf48508ee7cceff604b0fd5613cb8b4b1693c450ed5ed269616671d80a35cfe861a4a110153abc2108f09706ccf5bbdadf61319b4c523390bd2785a0480930c18f1f45904ec4614a0d00ff34e84e6353fadf714aa9c8d491c26b3e30524717aa13ba852394765f6660c09e80005a68f83d9c4e0cddfed0605e69cb1c315d821f32ad93307f64abdcc7d07b3ebf2038f605116c9dccb97bbd4f2dd46d70c5344e4694558a93b010cf57e0b4c61388f8d194aa21717c544f1fd6a98f596098dd5a7baead233bc74e20a84039e76abbb7850473d7583f278e21c39a0b4f972cdb88e602832bcbeb503927205bebc3da2b7dbc535c3d8f7589b80896d5539f32a17cf206ccc9d555c4704c302dfd21db1617c4fe118d2b8d5d472f7feeadbded7a3f5d2d655a3aa6b306817004fd6fb89ca976fa8481ae460d3ec3fccfa9d8963e399d973b71c76d0f90b330d9e28e02d98280b73695b2318e52ccafdf80895c9b976ce0d88a17f869b22ac0642d9c7f4aaab31a494de797fd5eaf3c19e0622dbb16f181e742a64b713457a0a1fd3af05a11e9cd8d78046faa901287e03d6432f71f985be85a59d095ee50805d8a80dd51e8f21e59a4212199afb7d414f4c658ae2b5bf4734a0f8107ae1c7040fb7ac8db6b152ab853ff27d24fb715fe9a008e37c85f4341f2ca216bb924907f7ea332ab1e7fec1d2b4864f4c563a0d1a46d116bd0d352ef72664e2e9caff0f8baa0a0137888682ca7f9c20c227a986f8295a5b183c35c543846bdc24b0fe04ee2e663bc8055af985436d8d5f795361acfab2c58204893b1805a1683030b600fa16f96878453361d0cf671f7daf0ff869704dddb632fc9cb206242669f97f0b0ee6a2ab7a26839f772486fdb18a4205ef96bc239f1d63b1a0a6ef7f2c86c909a0b9181db547a9b8b7844ef95e11241c207ea3bd3fd59605eb1fa7bdd3ba88066a7f1728aa15e1819feead0d740e4a635010883be9c3c881aa8719c0b24c530d08cb796220cb91b79b6f8f3e640888550faa55f1cbf9ff46faae5832ce03a404f78efe80fd37e1b291fd91194a92780ea417fbeaff7d6c4a1d398e3c1ed70507cc7bf92b9a9f311e3ced330f8a5043bdb2355f6d07500d48efb840c9bcc3da0a5cbe99aeb745c3f8152442b955ca80be62bb2568ac38912a1aa8dc795802be07044a7d05b3bacb6c706c7d7ab49fbbee5e2447cf2750e284567924fd1a2d7007f864f108c2251b0f98e1cf9571400bddfbcafa908be5734153dcb1041a62eb09d94bbba3c679f686f24e34ca954e901dc73a8c0621c3c9257d00e56481771d0a80cceba95f2aea431d3078d742efa350112893fef14a86377b3fcb6a367781015cd044209bd941e78a2fa523951da1ba3d0b6e5230689611487b9e4cb3d3940233e8236e82bd52850418b364fde48782995d0a5a3e601e6ce559745e27bb5405797f602a0d3c276a553ec57873e15052d6fce70477fff3ff37e316a7aca286024730fb594344717b7705e519667a4cc4e4231a974714736a9a6e4ea94ebbc00f61c09b4cdd2878cc191af38d49606a85a96a9621f8a10791641ff79f3a9276049bae551c51e0dd934cec77d08d65f15a91460d050a17dea53e5402bbb42c5606074d374378d2393a064869458b0bee09fac1b7be3d78b2aa254504ba9d86e30e76b4340156f7ad9d78282c6986eaa31f7ad7a934b03e45cd65cf9d7ff8f03f091ca4fd0289310ded06e1b006df7c11b366e07374f3b7c62cb9f2241b878a170de50718bd8ed463cb2ee6fb83da9ca4f01d46b50da54ddbe0b4bd66f50edba70178aa8f0638fc26710f362ddc1d3c50dea10c8552860c61b89a8b2b2a1df81a0122f78adb814978df037c286c2eb0828585eb2723fcfd82066ef0c833f1127407c74a49e07dccbd46c3975023ebd324a176453115bbba91f7acaff7beebcdab03a34095d489c897b855cd4071e6d25fbcb1b4d20ce43cdb7a574cad281800660ca4ebf044a3954d837ef7d864ba265de7715ef6fbf6cbe2d5a7f7b075ef028902c2e3c4fced6e4783bd3b51be71633e2798b0ea921b8e84eb7055d5365e2beb071a510a76950dc865196a582abe5ef943600525b70bf3b51d47ffde33ab7de9081795a3313d7192ad225d5b44617b20f6c813b9abc6bce54ea4786b1ad2dbb009440b3838521b92718902048c9e4a296ac49c6a3aeb4f2a37163ec9a4b1f8e40494e5851e00e98b15b39e53bb0fc88574b364cf513bf6e85713537b208c1f710e0d8b79b03c5fc669cf4f918886d58db00d04dcd7c4c7ddff91c78582b7e1390dc4b765cceb6d020257fdd2d9a56b13be1f4c04f1b52170f27212924458e6f7052f8f4fbcc79acefde8f0c65ebec7ca2559053d280e1f10c7ba3d7859cc800f0d41cf86b0f6d0f8d0c798f9ba44121bca3a6e52edb29bcd5f54157891444ffa0e88d8933b7e9645df44ac050bfc429d91a79f09110005239339d961c4479d3a0e3650ccc833af20b2be4c1db34b7ccf52ebe1f027612be8f6bd1edd999c543309d4ecf10a7a130e2b75c145a49b15579078a20c0006407b5ae61388945a738903c5d2898fb3ec9e8117ac4e89e0c9356fcadf115c02cd784445c635751139b20b4aa76d745d17b8d70bb0adf8ff3e2e7aa696627dea45db5186391c35aee12b0f64cbf4993024bad6373cca9c2a8ca66bd8375b16a0a64a237145caf937cdf3040a8959896d9a431cc9758a379f67b65bb11e66325e1d2b5dae7389a401415101286f922ca8711e0ffb35e8a9346eee3cabdd9272c5445126be2bfe3e2f0b7408354306df4f9cb441f58d0dbde18f64c5a6a4283260f1eb7dbdeb386f5889ba0efacc73c9360d4b043f6ac79bf1ad96240a3214c7f612de4897f7ec208329260fd5af67ce6aa4db3d47db3414b9ad0a71fd702ef21d9497556d7518b5d2144905cd44eb637301c74639a29abb6ac90dab9a99d27db76f70eae2dd6b5ef3ca230a38914ce9351d46c355d270c1a3226bdd1d4fc2a3b376e6d48fd7cf04366b0806aaec096747e0c9c6b86c9c72e6fb1033252ea8ae82840fa6f038123338ce1a0e6be3f6fd7c8b5ef94e1089d626941b943f58edb93fe3208a1aa26dbb2f5bf307093d38be783afdb818198571a51fa2503bf56d952b2473d62ce2f75194acf809451ba9a6ffc4d6d1f73e10e41a72d5463743116b5ce6b684ee857c0d42041c053599830ba3b762c279bc3d47c145c7c526c1b72c00e59a230ac836a24d03bc0949f2c87dd04be8f6d8968f714e7db89d9ced69e2eeda3d0192bc99450ad54e0e708d0b50a729469e591ac599bec24609f05f734ee8c19f9bf872cd4d5d72a40c48e48f47531f46929be7c06b99f90fc02bb2c841861169647b0939172439260ae1ec5bad419d06143a2d5b1a63a7ba0297a62852610efb6891753a856ae5770d00eb4be59b4def5d1cd6c679f858c112ca8520596a3dcb3959cce45c9eb9d50f9faaa99d67f3dad1a1f598105cd6c637b6564b8bcdb49c9888fe624a7d0436057f84012f302acc34a8ad27dbe7df86e26085229c71a482d5105e96c052c5630507752242b87f457967045d6f89cddac78ce2bf269d1a4babe06c33f19681c30b2dbb45048b0cd4ba8a0aed6594dddc3eca977ebe0d87515572a9a031bde190058569492fe22a5bee011d957e3c7171205603d2ab3bdf9ac1de8a4ee3f9b01b08552af2b587e91927c76e2f7b6133154fa14fa898f51e1338b2724aa07843190cc8b9ff7246516ab43b8b6a474df4ceb103c63f8910ed62a6d5b6633ecda0e8088578f7c0eefb8767f829672a845d8ceedacba380110ef0f840beab572779310626057f921d1f8040a8eb8ea8a9665e6a89daf2d866316c5515204e2c35e67b0013858eda266787edb83be9627325cbf276e254adda5b4a8559251806a403be037f8647e2c57b50d0e02f3690075b15f2774fbb748124849866a20d9593a93f0778ca087906258d09b31f92473c4035ad57588e5ae5282bf328c3eb429c204c08abe857672f5326006c0b0bfc1c6644056f92fbddf0a1c3e91eafdedde5ba1904594c73918c82af2faf3b1110df432c1da63ef11b16fc603cae4001a5c927420d2130316e05721900539de822d4f7a92241e061ba60c3e2c8358c0191a26fdb0c615c1f35df20a0214a19f4bf6c6aa94c5b305281beafa37741c6c1228933df04a7c8eb8ac612a47a77d5f02a192674c52954c780699650beddd1bbc0f1a6ea0c6de3cba78141db48514e4c1a01a2cdf25a4401bbb9f9a2916ced2149cecf4706e239251dfdbc40f6c734cc1aade951cdce65a4743a265a5d75576a7097e0610087f1767c3fabacc3348da44e3cc2231370578d8611e6901bce1816b4102e0c0cbe1954a6f0214b5b773f9cb5ee3eb934b35977e71d5543e3d5b240392bdb5d04ed19782ade9d6524bdd3ad7bc7be465c83e461c5dda326277452b2f077a08a0d78ec53eb45833fdfe9be63c6926ec2ec55f5e1abe5661b74cecc2adf6cc4b00e689d47c84f20528b686a1afd5b4127e0bdbd4258f213d39e507492a796e916063c871dd6127b3c9c8f00be608cd32045ecca4cfcbbb632bd172a567e73ff42089c3f1b72871ec74ab2db782333ce9ed3364b671eae1814593b5257913d54f2058e5e86e2d371f8324f32fa8e12a526d6c47dfbaa39b383a4276cfacd2ec601070345880d430d732debfd39e11ee94cde4d2530b685b6860132e5826748e4940c82e017931503673462eb6f3ee8e5ed58d4d32498861a651f667a736cce171f08cd74be10b34e5f27ebea0f1a6c8c67923a024b5b72b6d770d153a700497dc7000432a0e9c62d7e7c391e09cdf3dee7551883bceec4315450156cc7ad905204012a128302b7ef6828303ddbff0592f2d83c34345b6d01262bfc23fa54cff4cf0b5303ec31a8dabd69f3c8386bdb0393b9f4afb04598f91e0727525081a46d9405f3de715274ef060e4a8b7a1483eb9783da47c4eaeb2cf65228966dd70b9ff00bfa7ee7c9afbeae8e22913dc9880554e206fe738da89456a20cbb43cdf8711f0c4ce5b6385df6bb6b896b9ecb95656c97c3ca98e4cecdff2b95d4d205d1adfd0879ff071291b0c752a2ab9e50b88a9e0892540832ef0086ece32cc4f9e81b900c3358e0d063d05ab61e746e810bf1afc3e7425c9ef03091500a96f55448031009b1ba4b676dddbafc16f6c64e2772cff84b59be1fd42b4d2ca67987f88b4d7607aeb55f8bb9de0c7fa3c782815ea6958ebbcd4f9b29811a60217b8bde71db0b064bfd75740be3963c21e59c80fbbe30ce96db1495ab9b368525c98196d967740da7eed33564d61adebfa36b89f0000b3e2e469ad92e570c4205117e605950ed0aaba219133f6c1365a5eff6c39552c179a54eb2ab3dd66ea64948dabf2aced90f41ce063ccc22217bf7d07f14834abf091261065310ab59e7d44ce2890974de0a61c9da78f81f85a7208c61d633da05f627b3f67116d37cda3380c13253996c05cfc394fa67afe12efa2aa2b5d05ae8b69588eeab925e243bc664dc837e743b0bc18c5ddfc24add9a4a1262d3f9b2a34b3edede1bff0d98e7c2cd14ac77e0c900d380ef98c770368a5959fa834b7eb3fa2c70056c0fee803e6a8caf691529410a5edbcf9255fee073711e5fe67f2ba06edcce12009f437b18b9de6d726d042203b03214daacf42b8f7f665b46cc0738a84f57aaa53736977036f8f1da3b9f440a1796956362ff498ca2d99ed8a7c3acbb2393ae010c41b81e7ccc6db2cc74340a3b0a4222f1441f8604074841ce4acee3a75f2d2fac02d1c08e2877435274ba04262d965a88cb476344c0820c01d5ba2e5fac8a21a7bd1c5b519e2521c1042b0141636f2b9f2b8a6dec59c99adf4a2b7094d23a2ab278719f03362f380a14ec01a514a42e508df7fe1762a2fa7deebf992d6fa3b0780c190f375e02f265d8e906eb7417d1069a7628fd0527571030a5b70c8acb646421165b53fdf31851c245067d649aeb71aaf952452dbf6732f8418b4826e4aeb7a11af0acd59f7a76ceb50fa29f0455c136073fd63bfa01d2e954a59c6152b1416226618fa04b229a1191094114e50945b84bd2c29bec9117cfa0dd5cebe6e1b7dedb0a3cb9bd05cecb940f5d5b458d803127d78a40e1f1c10e771dcbd2d9dfacb97b06a30b6a8e7523410035d73555ccefa1a70fea0e8ecaef738f38175821c40db3d1a7a871dcb1d6ad00d39f0c1797e5e0a42d606a1b164334a8474da92e3e8d07a4670a2f4a79ccbe07f555cc3d11659d902ba1fdee1ba7bd228b4843953c9efb3959ae4a28bfa43202584f185f4692d401f5e2e463064431ff81fc71eb105874dd79854213d44e120cd5147ed1c76a914f1c9a8c83036164de142c4d464d473792b602351093ed4c00fc5432b330f9cf748458188bc8e9881027d276aae99d9cf960b44e45acb2d20771e7eab1d47451e822bf7846a0e92160d7fa100ad00d4659f8c8e6e7c0f0020d9743fe73ef34bcaa2fb9d6cfdbcaf12401c6d50e71a6bae244de588925bdef0496bbaf3146ebe42038606998e55acb39bc3c7c8d1f22c8277b5ce11bd34f9f0bcfe2b130404887a61290a6877f1a8a6999448b62ce194c01cf0305383741330c1bd669b45a2e6e405a47c00609f6306d12384694b29073b32160f92270a51a050452b186bbc41d49f3d2a26cee50a8fdfe0aa704da42a947144b0aea9adb2b021be6d9cb7aef00109868ab18d2a3d7d74ac6e077d4c982a25004997e7efb3005fd6c01c963ed8556fac3af444ccc0c51dfe8f63988639f3948ae642b57ef7d0f8e389acef2dca6883d529811b9a59466903f3562463bb2d98679deaac0bb920e906e532ecdfe3fcdc76e127e028ce2c2e542c5f820f8f200a15c8c0a714b35097764888a6701dbb04118e7cfe184d7af86081d17b3efa5f7db5cbe2f70de790cfc6f024e70fe81a4541c997969e6d972731c71caa2f67a9de8f0cb6ebedfc90fbb4995ade2ea71889026588d6c1b904f0220f1424eb2eea9e653e11b3beee2009080d95704b04d0cac186713f1a1334bf4ced11e858964add5df4e143da73503447f53ecc7c6e176d7e497f8d9f894029dc4b00a645cf7777ca4088b96b40f0f5795f8c59ec29abdbc46b39da29d0d3adaaf088555c78f1abda7479f4d900501e5299e6fa8b70655c0426ed1e0076127a364ac97364776dd10d4f601047610053842878229b86025ba9180aed8f6fa4c407b5de0e1ca9e5975df33a3a8232b069574748cca0c1a668bc994a895916e8234da22e825c2a7e0854c54b63de92b091920b68a51381662f051db1fc84f4ab2832df8ef74c91f67cd3a9a1a7325530ed7c1a6e060f7d6cb0d7784ebdf24a1a3834062d423e3479e759fe654402d2f085b8faa0b177967c15298fa91f3bf449d25efab8f1bfbaadee43bb9fe5d4ace0de2f33e0d248e26c23ae170e4ad598b29f31fcac1e3c6bef187bd293bcefbd809ddb6d540bb306126288915b457177c068610ee7a3fc12f41867b3516a119700b4089a0f582ee377e48908ea787a85eb687164508d33ec27c8a1e5b687eb6bd01aae041be3420f63c3c8dd83d2a1e91d33036693256cc5f84122025e35d3a2b0e56d7c572a30d6d925836b0649bac2da05ad1a284366dcf1c53a1dda19e24da0637945ae36d0bf09c64437918e4d49793cffe4e79f2413a31c4e8fe9432e80f02f8f3a84457673cbe4b83cbb13a1b184ba85b9c65cc5a8521bdb5d435f20a680d528f65fea13d8016603ee636288f1c9f56e8149ff6bfe55d77620fc8189fba063f9404aa0b41b41ef07dd19ebe6b5a77fdcb1fc664a0824717d109c236d11f0116905f4cef2af505523578c6f58ac7e7ff534378aaf87a6c6f6998c2ad384901309b71f174314ba9c5bae31e6614d72db41ec8fcd5b93ca05fa2c2fca0f0ba07082d464b9e31a5aa9771987a8c671c7913fb803be67dfa124f66993a1266c40adf58a6393025b013f9229aba01a8025f6a3c2d86859cb0f2f160b15b41b110019757d16edf26a9485a3a98e26047f60010255de269513b8080c6d0ac593f900d9396dbd6c919873e0821ccbc962c535c74d93986d06377cb232e45ecea8ae90cb3af6ba90aa94e5b07b0257693faca7a8ffc85f9566e642f629d6e93368f8007c09ba85a25b428e1392aa72b9985db2b762beb2026efe59a017e8bc9ff96fd0bcd9bc2ad121069f7bf1948c7238d769bcafb013eba1c94049b3c7f4f1c8bcf018e40d4ea2524bc9c615b368225bb448d42d6d9fb7741cbe5a845953fbabfde05c94af20f67de4bd23e5414c32ea25f723f6641554a32aa304eb0ed7d96ea020ad207efd35106df038f3709f7949be69809321623460f38531db17de357bcb9061bd53209ca8199e1d6ab6131dbb9a1c2a1a4e80a9b67fc6c218af326a6080d09d4f0f1496a924beef1f158fa2efb05e93a33d9d507fb00a268cc577c1bba0d0a165b0e1636e25d7457163e4e8a881923a38cef0a520aae10932c1580b4eb190e7fc0011f71f06f221eacbed454fd6fed8c83674f4ed1e0085a73184d73d01e085e15a297f677e13aa92ba71db3d91e5d565a71bef0c328fe5e9bc0b1fe868101353ec1d4d23a7ca8c942c816cc6a51afd2ab0cb0ce831d60ba9a519dcc845e00d2578165a3bb417cbdd9b9b7e496f806e66f8a1b23552b153f2a7d496767240dfc329b94e0cbec095f848325a6deed45ce19733c9174ae5e8987b1f05c20610efe6b35b227b558fa972d7e78d0c50934e335e5cedb7988e7c2635f2d31b712005c99b3f33234be02de9f2e3e6e76144153b0272b321dd0e97ce701d89a6340094f5bcaa795a0f1b0bc95cfdc31934811f9a26a0bc4bfb04c638a252f6b35150292b393953b2d808854d5d1ff6897cd318d291d65fb4e07ea32b7de0b3c728d08d1b518d3f9809b93cdcdb23af96d3d0bc669011e71b6c8d6eafd741d635b060be798a869dcf1eb977308577f3a4c448c12775cdb7268cc578b80ae5d27fdf7062736af03dcfcbdc1dad2a0cbbe05643a6d9e648f5e0c26be504b2851ead73a094d459a97d990b0dba91b88de3ffaebbd122ff9047a8e87e760d6ca043708ee043d3d6ac50624018a8d9608883b826118d662e9a124ed3d0461cae3019d97b709a172475e08e725e5a677be39e86f5f0144af0c807be7e45e76ddf7921dd2d70cdebcf2401e37784b5d1c6003538ba937f035869d369436f49047afa528489c03 true +check_ring_signature dee99920819b2082df04866f1d6352349c6455dce49b68faf04624ef32161bb0 d383fa0b0ced4a3e2eb37dbb72a7a728e7ba17f193ca5989bf0e52aaf1170cbc 46 1528239fa989d705460c6454439d5896960966ccd6230efa25e30ebc39fa7c83 4fdc8977f51573a60cb8670ea2ae475e25f7801e03c47b8676df47f5c8751d4f 6c1d5ce59c104f4623a118799cbbec68883e813888cf96a14a00d1e02cfbfb86 5ed829820bcdfd862dc02c6afa6aa2b29ec5a7c87cd483e3e1884740ecaba0ef 9b65e6b735c775f15ff2f3aed3da5ef11eb3006ccc9b6e3bede2a33bc3f5a40c 921ba323e339dc5c3dd124ccbc13174a077b7652972d4ffdb11be669e003fd73 5ff5171212584fb732f94253efd2b07edb35ac83bf98eb9debe6ffa9c219e078 f4e18c5be0804c55d79979c542f62bef567f6151385851dadd096951717e5bb3 6217fc74fd0b359f5b6657038390c3919f48ca9c9882d681ad2bf7e985fa653d f42810f33563767e45ecf06972956e86b2ce88d1cfda527a11a9385e8563a1cf 52a10f78b123882ac5c7ea7231238fd7e75254f9679898b07aaa74b6f8b9d1b5 2da1fb1db5d2bdc4f0f6213c0ef2bd21fecbb17afab23d99fd03853d7b122d22 611217e5ee323e5ac7ef227620cd9fa0f236d41aa2b18d7a982e5f7ddcda4f94 f208dffbbacccf94aa469c94abaa597157f42e1e23fa861c51d350f1b0f6a950 a5d135b53494ef48eb91873a5a7cd5d4c521b424e5fbb7b0dca104a422366593 94da9cf4a9d0ab8b0b0c0fb668da750cac531ff5a989446f589a226d3af3ef4d 30f8b6e26d30996decb73fa5d0e5cb7b841fd7d84f8efa636cee8fcc6a7a5df1 1e15d44dded8ef8f6270539bd93959ccad92fac1fd94c4c56f748471037fbc3d e50c26a0ecfa41a2d7499a0c1a7bc89a74ba70185e72402c651f1e15199dd5ad b15c5dfff6120650841714816e959a2ac481b8270bb2dadaa2eb777b77af0324 20f450900c03d1db11eb01a50c74c9e0a3df90c9dc60f547a80fd61a17bd1c1d 0d66db165882f32e58b69408611ba17424a8be468029c1ce499bdb9979960d0a 75fd88c1e4a3f6d8d59450ab70a15a081357fcf71f282b0df8cafd47c5d414c2 9d322e0ad57b8ea22638286c3440efcd359e57f0d03d645fcb042ef5615ff83b c02c2bba856276f1b9adf2bbae6d82fff9ab558618bcf14b3166810c73709f04 2b49a59c4dd2cc3c50b022759a429a9baa337e29cb3389298ea22102a6dfc967 5867ed7636c30d2e9b5e4b22e8ec883905201b65485c421864e0a2b338c11243 5dc48dcce6ac06e23a3f6f7f090a92199d1ae414b32cf46459b628116c63bbec d324712450068ff816e735d6ec33af47050f1c729eca5adc54a7d8693a3fe889 ae205498106ba6e7fa814b0d657fec562175be260bf571588f359681901683de 88ccd7ecd4e5c7f3e9fe9b4854a45b0930a81d366cbb0c3e4ee216330029c7bf 707fc2f1ddf457769ae138f70f4d476a335442e93a30eea335e5a7b81673848c 844f4c377d419c95e438e44e9d2b5b731868b151d4b3b98fd39aaf6c1e4ebbf1 e433956abc3665f41806cf5362b150070229d22639e92cda64b3f9dce06dbf64 fab2ccf446aa6dfd325c554cc1e739e4f1fae864c0ac4d09e7f6c9ccff05374e e3735013cad3e09b4e994308f41fd121757de04d3c383214b44d97bfcd241403 ec2c4d18e28c791afc4b59c8f3b2be64fbd581a22131ef58da4c446a622abcfb 6ebccf149d18320990a77e2d28029167ff5edb76df6f6dd51193f8442b05bc4d 5c7db8bb82118bcb678a1b12099575d0d7cdc7491d519ed527021eeaaa35927e 9710cf097c38bad2c48b2da1215e496895b8af926ff8fd15d45c0ddb77ea2c00 00a765ebc910508568abe4605b9dc93b5080157847c599a5f6587fd5d0449eb5 e3c9f8243491e1f8a57ac60412d0cddd8cbe133cc5413704b9a2c9c299af1b60 99c62ce6da6cd6efd32225398451d1eb810db17d74ac0cf2b4187a63e7848c1c b5b99d9876d801e98c6d9856f45f7e587424e616e1a8005ccf6f06673982d9bb b14803796d10d3a13ed08d7d3356c5eec80129b0b44c456cb269e4bcd673ad40 d3123587bc3e8f8c3dfb6517086b71442a6b446a5a097e85156e57c0a4f3312f 92a9e77f47d8705418bc8086442e9059f60f9938bcf93a29d5b9d75d61940d0dec5f56929272f0613a00e7880837ad081d4c6d8e98396dc064e3b3ba510c5e053d01a23a9c70275040f3dc858b23f921de397b1a42a65727b45e20053a77140b82ab19efac65953f23ee6bbb5d1e213d19af4db844e8460469cffd220e071c0ca71f79f45b2e91a7f6c55f365dde47aa4296356e9993f1e753a10494d1323d0a4286cf0dbb4f864ab46ec650f942afb64e22b6f609e0276cae98fc8e42ac8303908e1b25b299555ad9832cef4441b079b0a6b439ef7903096477ea8eca14710021bd51e62c8bccc0fa32689e9889add6a2d565b19a10fbe8ffa817c43ebfaa06ff2fb47955af24aa58260cc5110b5848b287e2a704c86f5dfadbdf232b14430445c1bfabd0646e0dec321e16f1a43a66fb177495cfc22a740c0b9028b351e107f1d8d2d0718254683970de41a191db93cf6aa77656e3b484b2dfaa390551d10da2afab36134a59734732f6eab62edbafe7a90c1755b84d4c17f1fc4fd6bad005295d5eed597d852c9e9b79b7eff10b962ac0a05818859cdeda1706741956ec023e2b63e48ecce64574d531e8dd60826189cd643eb1e48474e5842aaa909ef0037d6a8b792dcc67424e9f1497fb02f65b71b006ca4d983a5f4a787c22281cd10f12fc30cf4acd2619f2c114b17047d4430c78e981c21cf1f42badff23e4326907fa2c371a6e435022de75afdfad9b03aab5aad609cafed694819ece7e7f3c340424214e81492c704cfd5d02405b4227f82537dc8b0426c2854021ed1c7da0d40080698b930b6d6be202c37b8ac20c90b0197082c0fa9f6e117cd0fd4e590b030e63b9d3acdca41c68ea7dda55aa4c982700b6137c01cff6a2b00bdc49e49ee50a9d21f4bc8962829069d044a9d6dbfb8f2c8b476c523c5dbedabd97d07ef69c04101080329f1db7582d8440eb81870cd7b4bfac4bb1f95d2bbf4df09df0d46e0d2f794ecfac05c71b57f205e2b35ce1cea374b8325cd3ded903e43cdd3f4540012aefeeda384c598762be55ab5855801d3bf3c29e46578e40f5c3d3df5a393605722cc9cc58688803a2ef40b715f0a3c589eade74902ecb1f38400c404f9ffb09fa785173fe7bc9bf06d84f903a5d8bf6aaa237df2e49a7f98c708e788149f7014156a49c57857d75c4a8b45beae324a9de505dd9c92f556ad56a605486d73f0c3c20f28453e8980afe811efc301c5f101b935bdce383a457aa0258898d5db90db166ea7d5014b27f5f7671356b930ba2b87c7fc720dbcb4e8fe446e278e2090a0c4cda4156eb2db6ba57ee457d1b065070af23a01c8680be6e76a1b990424b04b3215930762123c65ff9ef9c98130e27a5d60c2c660cdfc15e2616dae6f64609d1c5a239db33daec853e0777bdd9abfe37883087e3e8412aea37044aa336df0c86d40bd4ac0b058d371b4a0ae5d2cff8092b4b16d30989107eb435d2a391770a1150fbe53e522bb0b2ae62b559d692f9e76d624d6253a7f66c33c382297d8901b28a3bc1bb8c760c0ccb20add5b78050ba5897c32b1aab470787562700efcd0738357ede8fabcc7c3bbaf0b18cd96b6fc26003ae623e4480536a18b897c8fb0c63a9272a76aec85e990cf5c789ec679950c00e66bd8338664770097e1ee1fb04a600fc9d1bc9fc901192e921a7c2fbeb30d40f6c40214d07f0f9f127428ebc08198f666db5ab8a3c38e3d17f6c449f1f44ebe9f51411934d98ec748d346653029cdadb1137faa6471c6b5b95c27164f231357371d84876ee1142572c86c1fd0599da8aea0869a14f670f5f8964edf4b701357cd7cd1a0d52b7a129fd0f980e068e6a31325fb28923ff1749f48daf0c7031029eabb34bf5108f3c156a2a2e2d0f1b9bcf182c522981077c1515fec36e4c9d403cfa7ae2b15d2bed1274fd789001dccc7349bbf476c5934a3dd452fd6bea9f355df45be5bbb8ec0d194b90c4bb0b62b82f86e0bd57ae26454d87c3cbc4634ad6d5f52d43708909ff3f95ca3a2b08b886c87fad3dc7342674d7e9650c197432793f14a7cc20a989df9823959e140c23112646d80486a3c8d69486aa9d4d6a54603aa83698614907a141029ad02308daf1c226956c7d2aba715ace07b765f09f463f625a369e09a0bae5411f8ddf0a29471a54bcdb47970cbed75ba1d4d786e2992e09c78b79286b8c7b74f3433000a0a35fc728cfd9078d757f852a3a98e235980e4ef02181bc7bf8a78c30699304f64a6b76c01dd900522a07c6fc1f4efabcf814fe5e73c00adc307215324c5c0d0343dd6737e85a7e2a7412db368c3f832cf426e50ce4fef6370893efd03ff2062aab9ab8a59ca266c84dd493a35b2d3322b9a3799e8792e25582b2ac67a9430bb55284dfbe9d62413ef61ffb5466091d22de4ae49bffa7f47ef05cecc68f080b1388268f636604c85c28abaa639a5ba8bd2016299018dbc2bb3b1dac0c12bc0e29497e9e1a4a1c94ddb6797bbbba21ca92e2547590fb271391598d1d710fd30f6a400b1f638b65353e00f99e7c7653fc94f70fef4dfe79f4a5d09ba1e3c05b01e5e3aacfaf952f882419218a0bdae5b725c62a44e718f0a5437737857f8ce707bd1e9cfa7a1be30877748be70a59ea513245676a0040970ac57c8b84803f1f0aa437cc6c942d733635d4960bb2a8c94172690a39ea2d8985248e8f094fddb10c5c1b8ffa0adcc4f0141e791412af8888620e7aa7a2a61a46afce42893dbb68099c1c67fc731a6cb5e8b0b751ad462af0041523f77725b9db1a11d4be9a2b140ce1f58d098a63047788d4db3af6f91462db6388b2df3acb55496280a45ca90f024b6c3b1c5a0f8a5bf4905ed73fb5f6b0d649ab287c5a15220e30051ddb4997030f021b4da1ec3b9d56e1789a8bab0da5494966ae3ecc11c7c28b33ec7a51790abbdcb509645a7aef4ec6596a4f6ce106d85f9090a0387d7ff4589b1f39717b087de89865a0a9285212f0e7ab3116b64e39ba4deeeb3193ea235db4010595c30b18196380e19dec1b950e17e3105c9818f880bff7e80d7bed9d21ac6045c4d90a7bc0ea13a164403d15bf68d2908eefa347e48f50344751358a8c7064c582dd05a6a5050b9d49a757cd050b4c4ae817510ccb23a820d98cc17d7d8271266f140ce64c7c7de672d5fb0743ace21996e22f04514575055d4c329a2a0233dfffa40e78e99909c5fd51bd6ffbc61058b751e9b9c12a3666cf256822eb070fad6a9808bc5d9de516cc8eeaf28506a829045eb2e0b72f74d2843acce06da7b2da87f80442c3d7e6da3209c3a86c7f2c39c90609e7a999941081318749781501cad49c0849eeb3507ae909ab221266b9b635a589b6501d0ec2884b32cce02ffdddfa400df91c4c749937ac9613fa25f9e3e4ee89733e00175b767d389173ca9b6032670f2b8e9c6a7872f22aa8c76039ab76f9e332837ac47a8ae08bf991254ccfa86409e476455da6fc8f9f96aedebe5b64a3dad07fb210625e2929d3f04452f8033d0e057e86a2ad501e9a739c23694e6a7afc941f2dec3a244370f68de726f318da0b86e9add620eadc33c595d44b0e5e186bcedc9ef74a85c1ede7df53c5749eea060e81caab4e86773ba605218fb1f97202a01cf256f502c443ef334a9924c1b60a0ee230f2db12757d9ad9e5e9dbdcff5bb9fc1559bdcaa62d0c6635b02cd3e2061dc1d5acd1fe62bb74373de3bc883929bd464fb3dea0e0ddecdb497fcec1d80eb966f1a7a3e54cc217bb4f19a82a8f25c90b59d8c68884b50525ee496746cd0786dbd7224448a3e5aedb7a2b62f94028a686013b0a566fa12df43124d83e8c0963fc5897e11815b3d4e743ce83269679c058c91969c98aef67cda0795ff4560185eeb6a8e892905346b6d2a8efd5665f20189fd5d078e9379a1938b29332db41110a52618218a803521904cb51e59323e2df52ca4fe58fc89ff2471f5dcd1101219e39b673e3025e1482913467350e70da66c27d7ddde57f09602bf0e5ac8502dbb3bb1531029630f8ee89a1dec0551b85fdbc35d53fb08fb69fb8c35a6d160618083687476df52dd8869e1fb6f72e2cccc172b114904781d6c8bdd8fbbf5b0d58d7f3cc5c15783ce0586f532ec98e5d260f6eef04fa0fd64c588f7f0607c702 false +check_ring_signature ae35cca2c6dea9fd3cbb7029edaa3f29532224599098ad0f5c931339f7dd2477 bd429df7b5631971014499a7aff1d1dde69896803cf0c526aa84b3786a43368b 1 81e67d1a1fba5fbcc6033a98c069f3d6ccbce795c83630121bfc26a6dc143a02 8ef8bde32733ee6898b35ba9ec1c059fd41b0927c7507bb78082a34c5645ddcb5d3ad7f5ccdf290527d6ced4442464bba8a061f01fa257545fcff3cfd749ff0f false +check_ring_signature 9105c5e22b0ac2725bc56d2fe0a54da53010b961d5ff8cd73ff6b30b5f9aa718 d2a53797aec1f6340ae1e50e8945104dacfce477cceec5c6e2603f5e6cae865c 2 ce1db54675040ac0ee1f94f7f4da703033e40932674d9aa70574db62bb8aa393 0b3c911b305aafbacdf768c7900459f7de7506ec3df34b648f07bcd70c190397 c3b4c7a3d69b86a98692ad6d756bcb98a8fa9f1bc7db5181d8bd47efcb39ef0664c93949d82236f721ad5b0a229e0f9bd5d7226b8d5b3988081dc7680c7aa862aabff92bdb5055df68738329927e427c5ec2056a4f693e02f8c7333e92892c0ec32a35cd0564dc53496f3a0fc76d27399205893632e8b5c8868c26d58a99260e false +check_ring_signature f4e895ab224f47d41e2a51320a67f04a6b49cd1158a7d1aa43e310336b310573 9530b5e686a6035dc00d21182abff5447a96cecc8a50877077dd52c940fd3df9 2 83c0777d057c41f38082449e54572ef3dbe5864b716df1e288a70bcd2241dd82 95eb00b8f1b7732d02222b6b64c2ed5088abbf05247968fcd29076cbd979f927 51f8b22cadad43c711af139a0fff39a4386c7f857e73090901e42ad0640a4f099538e72b05f3b8b58bc68cff6ce237cd3f04041aa7cf2eb68ea209e820fedb005ec94004b170d5fe23a019e9af67118d4d2e73be2a6a0325abe340d7e38c8c0e33655675fe13aa863a2c5212395d72a59354927d3d44191a564383df4b0e9008 true +check_ring_signature 845d82fef2f4fc94503de4a6a87895e2070069eea805dd402779525dd564c184 1d702eaa27ddb3f92a10fe45f665770fd138bfcb06b99634223e77a092b0f4a0 16 be99129d8d936f3f57e8361c35b4bdf1172279bdd619f0b5b7754c2f55df7a60 70ce0b404141a14d12bc4c8aab0040ff5795cf9d33e0ab2c24c27b53b625536a 8f790327990b5314d591f64b851e1c58fcbe15a2009b5ffa7ee1d411ff850ea4 3a45fcfda42bed09a20c96d693fc1838c241167e1787231b0fdd988510a7c37f ff33643278d53127fb4cf2c1d6278a21adc93222207b760c81d2077104326bd2 7d6edee9da28a511f9cb2479ae318721b488da10b489f9936b35177a29aed38e 15f1bc3031aedf58a1dd77bc2c9694942a1f8df6627bff1f72724f6806d05cb1 49179fd041bb53bf8e4a4e491d01f4366056ee25cf297969e265ffc33d2f9413 f141ec8bcace43904e86436dfab00d2c91150314abc86b879c134a2d4c0a72fb 737b2e8678ebdf73e7bc1eb24b25fa57439b306438960042ab65456406dee2cf 249f4d51682b57443eb4d635296c9c5a1b3fd22e0ade9ed3c76a2bb845d9c1b7 50eabb6919077fc49b5b9e13065941a8d4dcb3f30273b8931fe123657469cc8a 328876571b807b1a1b0a5320ea61164fc7841824579d473a348fc99a058233a7 b12ebc7b0215118f2708d548a4e01f357e6755cd0f770ba270b320cd8a365776 2a6ce2492323613971395597d7779c26a0960a13a076bb2583ff49052c5cdc50 7401c06cb1d2797c0a62aa5ebdd6526e6912ee07e0ffdfc7b97711a3f99ea5fe aa4367407ea36ce4eafd5baa73c697c7f3cf1de6d11f4d91a02391b3f3b2ce02ae69575a98bfef2284b0cafe7030b26c16309af7e8ff7274f151c690ca2c180191d62d8f4869fbfe82bb8509da3d2a3eadee0685ea29c97485d6e7ac1337cb05e3a6c20acf487fbecaf01fe9c2f13be94fd4033b540e474aec526591a615a107c7a439899b1312782c76c3385556259138785c87ab555b2204487d7dde1815048132525b4b804a5ca94edb22e5cbed7997e7308a5b92a8a9174fd26f7e9c9504db0fa3891efa6114d7a97da8f25689fbcfee6039bf828ade9dcc97230415a004698cfe834e75bfaeae40fc76b25f1901508da565e98f86c9741b60aa7454300c5f5f78d59e682cf13da2c4aff929468ac6bbab64e38db1fe1ea8eb04290a830aa016bc245362ea5e2deaa4854d538098b9b90ed6aa8b5a4926cf0fec6854b30181e5fd0532c47bd36bc335e0dd2ff440b5019b19cb8a58c69c852cbd89dbc904e956663a38240927c58a6e408cdd544ed7917d110f8476c34d5c5ee3a5c43d06cab533d8caed2c0405ee19aeecec589925791e11339c136ac62958834a67070ff769aedb6bca4338f906fa3069b7336fff8cee9ef3f994093563e474034c2f045b19035fd5081508676eea60156a80fd5d1ac3a33120ed4b81ecbeb91d8bff01d0088652f2c8ecbbcf90d444b20979d548af02ae773b2d51aaf6af5787cf120e83e5064b78e44d674328b0280255f712263e0aa88132b8adf8071d0eb8f4f10355ccf7ba7c59674bc671481c488557f42d56447280192b67a93b39cf4faea0089a19ebd924788fdcfbd7b9e41491ba9d6cd7506b4fce1394dd1225f6cf1fba0624585c299c09a6fd0a9963516d674dc211ca8883de8147bf8677fa5c3bbdc609df7cc575d7c0c46e24b80ddcc5fe825e077ea2429292088c023b40c4be2e8f0bf51ac51c74a285978e72f45b04429d19a3798099ca9695fcb36ed4b7c41b0b0fab501f315a9544ce36fd4864cc795207a353d572839a0810f69395a74da33c061d100cb88af1ecaa72d9040c91fb408f13b082d2dd2fe8cfe3c46ddd5511a802a5dac3043bc0a06c08b4e2bb537cc82ec5b98c767b07ca77ac6bef289d7e740840486f1875a264ff319baa57374fb6ecc292a715a2d86a4a7a3d40466b607107c0b7ef8aea03782b347782dfbb4438b93bcb3fdfb565ccae285c8cb68a8b490f337e9fcd429499348b839d54ceb556fef0ecd9d45e0fbbbb07e2bbc9f148f100f24ab0b49ff612f43e0b59cf4360d71ab93446f706fd7c4416ed373213ea7e063546c52c1a752f406104774c9f46d21034798edbf82f9c1156bb89ee37fca90e111ad9ce0acfd2b8332fe5ae2313cc13f73809eef50488bef06ac80e4563450b99365dacf49ba23d8d2e66a61a190f8ab11f21da0e8cb38882b686d01a70e10e true +check_ring_signature ee4b90b5f9bfdefe1ca0f73a0d00824ee59f16d4f3a7e1123a69930d83e9e689 7d39745ca69b1075277eefe148473221629d1d6b92a6b43ec680ffe9f9228c82 6 ce9dae20ed4cbc0addaa7f34dff8a71489f7fb26194b31bdbd6fb2900190203a 2498fb4d74593c11514225eb9173ec9dd227ef245a6388cad48ed76384bf68fb 59ac45d9d25d33cc2dad7528bd4225c42b13577e601adae11136e54f5ed70cc1 73c0b36f2f3e562fea3d1ce387387affedf76fe1d0f4fb2958e03ff06e31f46a cb164f191a49dc6da9f4d6d484fa82296988fa19f8b98a88d60e39aabb57959b 9b0d2df4f900041cd5d29a8c96eb0de3f4774319032bdbf67c7ef539da9ec4db 61c508e5b46bd1d0757b39eaa3c61e7ae03ca79a2720fba19fbc2747b12d280659e746b0e80cfc97a16cb617f70d00b39cc1806cb30b5908a4220f5e00879e03fea7dc36b9b02270440b0a21f3b50e07f3d99f1104e1e132a8c4eb5e70f637c4761f2a06019aaa67c91530967ef1b019b363ce003b56efd3fdf555c4dc93e20d405fe7bf8888a7ab6cf00a5398d859294b2bbfe7f403c0e3c32a820c4e65230277922f0d5c7bffd74428a9c2759b759fcf6fa3c5d46cb571da408b85fff03b093f6b0754d243fa6bc97a7803ef89cc736020c95b96bdb3d8067ef36a4db19903e4f207960233ed4c58c47a2deabe0627c40c4ca3bca0b6e8857fdc80a41e4d065fb7ab621f745e87c07dc3e7703a27f5dfc3f220832ffbb29f480eee50a8f20436667ee374f0e8fe4e2ff20b38a13bbb4c8b34d59f0c024e965d3311389fbc0a9f249c1921ba55b5c443648c23983dbe7b4b0941c0e410788930bd6d33d8df0c25a26a30ef7a83fe68b34f572e356c756655a496ad90497fec26bc487cc5e00b false +check_ring_signature 47578738b34c861c195eb52e129bfa5c0f1d7f2f2fc9dcef107f3a8fdf73aca7 260b54b0d5455835923a8376deb5e5563ea64bdf85e0ba21c516d5627d05b207 57 39e0362214d89763acba3fd8f8fde84b677ea426f8cb2d4513af9e53e9b7646e 774d2f6ead9a93a213abc0c932ccfe5b7ee690bcaa1b62aaf2ab234011687901 c1e059aeb5b243adfda6504a1a4e457dd6d94ab7d6b3d24dcb6fca1a325649ef 64aef149472aaa8b5143cbdbaabee2d909516c36e4c0af7fb3cbdb0afeca0270 fb728642610026a3fff713ff3fc0f3c821809f12854c8a201391639ca8c62cd7 80aec4392a48f2c81f7021ffca7958a441b0b9e92714d44f4b573ae890cdba3a 808588fa0739961d265854b6d79c1e743ff1a5f6b822afe923e78b98ea9e7422 9cd44636090e0e064924334f65988f83adc3b48919d95b1f9b73586cc6d46e83 e8666bd5d7e5c1f52e5f2a77767191f986c0dd5ca6e8930703bc9958f630a898 07c1993329978774f67066973ee584efa51c1c12a9ccdb2fa1b3f9760e896d9b d243ba113745e4bc791175e9f004df382ecf91cd90c59d61621dbfc2c6ca2a95 9ebc955fe7c65a005778621f1e860fadd043c82a51d825215af5e2e8a72bbaff d5d8a8ccfa8d2026d1f617b3a6709eef3e5b111d50d41a520f86bd31b724596f bc06bb78ab82d760fca73757726560f9856d21ca236f35ea202ebac8de5ca667 038ae58b7a596e471ca0bf1a297033c7042ad50694279504ae80af6901b74935 83aac08c07576f93bf4637f8dcb2edc3770ce8472b2491162137195746b0b724 2e6ec54bc59e857c0a7019894901085deced8e057921a08ba1c68662844259d7 041a06b83f99adb2b882205c13a33cbc00b79b528572de9f2a1336b058bbf9f9 6bd251e09aeeda72a96bcc680d385739d3e80696ed0a54804b2a926b4f5b7f3e 7e0bb5412e9874d3f53e4bcd42ec1c7ea049abd14d41535fac57019391724201 cea882807b2770d1f9ec893762240cca643eff1b67a1406da335a3f39579c35f 86ec0330295ca43d01a34a7d446f25479c94c687ca63db57ff8484d7d5ed4734 461a9eb9fefa324f9747f953f73e2d058003c381f6caa334af59f7d843e043c4 c0da75ec4a23f2591b784f82c3db9d741dc7689194de8bf3419ff68859041553 eb91e2ae5375fbbd793ffda598dbb28fa3e59c651f7ef900596771931d8a6e08 e2729b92da07f999668af6d83fc2599c50bb0f2909055542d6269105ff1a93b0 b406c199c5ac1d2b09af2365007f4ebe084a0d6df186b02c7a36f9069ed1faad 2ee5f48eb2de08b07e46f44d75368985bae03d076ce9a75626432554ea73a92c 408235b258fe05f64802ce08a2608595536f33606773a71945af760a6373ab41 bc4eba087a98fc97f28b5acb3005c3afd5a356ef583493f2a41d5f708eb42253 6ce637bfb4acc94f1d17b1749603d587539b5bbbc02a3cc7a387ce47fb322156 32e420a6ed5cf99dbd8d9c86ef7429ef6dad9bc3fd619809bf56cc76e0124d9c 8b26f5ae5a3a8163d39b87b48b9fd7ad2d461a19dae6f262a3c899b733bd76de 25a2b241c1fdefef4e7fcef83117787a86805d6da3b85e7403fa72351500bcd3 ccc671f7c4d0192c6a7a6acb18723c704bc95f876e4fc23ce13f928aeebf75f2 ada3b36917f8cd6ab49f3877c9ec9b00ee1b80fff725b4cbba43fbf8831c3253 1662119a5000f011dadd3c825b0a6dea6e9efc1665ba7444f24cb1c5256ddc6e 6082237aeaa242f957159197e299e84cb90ecf854618e21d32ef6d29670b643a 20ff6bc636c44f7371ca1ca003b2117479b494eebe1690d4e25ca1ac8f05976b 6e6f53271d38627ab1d6ba0d848b9005af1f9608003261c28ec88535f3c71944 65be6580432a7bdd4754b4fd13a0c67a07881a213119fccf06b3ec9c853be252 aa2efe4c224f87a205d8bb251e734f5d17b88ae4314938a405634dedc0a74b7f 04b38abd2d9964c3a1bebf0078eae2a8eba3ab3aa0fb78707acdeb5e36d18418 1287ecca1e06b0a83a66a3a1403a1a27609317b68da75ed3b2abf310cb70bf1d d5608f99a561bb1591f91e6fecb0b8abc06f136c48595d2c6be0e0d28cafd7fb 20206218eba589c7215153c2210426c35b86a8775b1899fb696e8fd16db0084f dbc21c8d2e9e72afad33bad0230356f7b55ce086614f7b436e4f031e3afe142f 2b9cc33bc7a1606f820902cf190e77499cae593aa20d3a41c774b28c61e82e1c 586b197f120cfc9c06067718103ad33f77cb982f099edb96d3dc4c408e59211a 15510eb3b56f9d99aa66536f9a0e7a662f0326c6968265437eaa5e9133190aba e5dc627843c90d3438005722680937dd8ae359a25134b495b82c8e5bae7eb188 05c4ad57f4c7169ffab8d361708ca4e9552fd03ee86b601405eb6ff59b033cf3 7ada8afbaf54ffaf69145e2ba2041d8b2b0d6fc7538a38a8de0c121832db57b7 8126c18882760b10736aa281bce26e66c15ac63439f05c5854a8eeb2351cc245 5a225a5a21715a63a6cdaca831224a3605fee9d1ba737cd5382b53f99b6d685c 99d59d9c703668639834826681d419738dc981a9effe29150a79dd57d8777cda eedfa2a7e5578ac8680db875fa26e3a9c0e56c3ab4a1b7d75444d5c5398a16e4 e6ca5912bfcf8e84c282f47a91014764a76f056c69351764b2f7e91fa8dc40038d6890c692af23873cc46b7becc84bb9a0e74507085a272ab130ddc2399ded0a68a43021e8b0d162b94a0115f708445dc1a6c24889cd065261a91bcbbca61d088d6fa168f09307d2d53240db5cfc9dfb825cbed7d91349a9f3d0cd83929b4509e79c0fe804d22a7f1eebf24ca471c06de384b1cf868283e131854af0c8311d0eecb59df40d25bff5581ee5d773c0599a316ae6ff5398223bdc9fa98b093b8f08209b9a2bd50e40b535fd2fd338c6b0ade585c41f1fd31c10a791ebdc3a722809bfde1ea711b209bbb76977fb54bcfc676797c9bbd7a04c8809a36e7a17e3920c351b457759ebf944ed3c69bca279a580cca6091e91a1ac6825a2e185fae0b607b8d2518bf211579ee8ae23bb7538e2b0a81e24b1a4eff45f707d03ae4901b500c35abe32ea713287d8e313ebf4e1e58ec6a3a0d5e1acd25ee8481ea598d46e0861a60fc8a575b2dd5b07079b404729a789f3b1c8e572bb519b62b928a66acb0a1b550b91e3ea0409eff8a8dcb3e51b1a84f6158be1c0c5b6b698827a8a3dd706243727cd4b1cd20192625e7bfc1500a05c838767084564d6909234370bc82f073e3aefd4ed051fc823dffe2c2d4a08be3250b531e7dbd13710cff74e08f4a70771233fe26918490053b949f796da0eb4ca6f7f5b03dd299a2a16f9b4f11bac0b5b64390b2a8c4051d51e896f41ee0be00a0c743c70a7179e1f85f63f27b1250e915c898d19ba550d71046920633f81456e842e0a7677062dccd6f6626dea330f49635ae38cbe1c9e477d87e5c577200fbd14aa9f64bffd368e1e02f0957c0b0cb4d8440492b16ce9e48d082f14a4e685f0c400d6e891daa4d755dbccd1eec10e15ef5588dcd6eed648fedea48921ccd9b2d2c41c9174690819a444726571160aa310daa2275047bcd0eb2f314a2586cd8dcde71d07a32d7a205436677ae6440d24901409b72b2efc7f7cf138eb07ff2da169a4aeb5496afbf15f0f376b02a309368a70f15a01c3ac2cf7361afb063635612e3dc948fbf8271c6cb59b46063a090efbba33f5b39e43168e6f4abc9d811dd8ae6a2543fdfef6ec51b60ff389990e996ef55226d7c1f5a8daa5fb241ae6ae6449bf33fdd851bdebac8e2d9e67a007012681638d012d5b1a964b07feac2373d6c80332bcea73eaa6f8d4f30d6db906330c863287a5e7b5652d723aa8193394cb79fd862788c65adf6500f5ad57f30bfa1f785c23a8064794611c5e3a3acc34b2c78d9ce4842bbdddda79db5be2cb0c98b5c828e952bed880660e165e475e4ce3203f606ffbf66a28cccf37e876b30ad4a108ab61c6c494c42dbf4ecfbfc9b9b710b524eb7813170de61a5877ecfe0c0daf23071451809bf6a86434283d4126c5843864415fa8df51b3593f6f45d50cfb0614858a0d51ef983b3bbd082d79432464d2bc7a63b0458c7ce2209db2b30ed8d14d10c022255958af3d41ada79ce37bd098ddfa882d011522647210bb830085ec4162bfc1a9e48470af47d5edcf01c6b8175218807cb527c31ba22fbc6c02dc25e8152954aa6a7c74479f18f79af35a3fcf0e604378bc8912d21eac378f0de84e26fa3fd14d0bfcf9db2a8c6b8f27ffad7c394a1e4101de923f27aaf0990da55cd4a2a39d08bd38847c6be1aa3f3b25bd3997c2fec1081765db14043140028d73b8c563d774690015cf824c19eeb05d8225d509491a7e7fd1b2ddf10a26079c2ff0695b71c00ebf865295c2532deff8c14998b1770cfa695af66d05f2a007186956a460f676501b59c01cc54113cbc6a934656ce8662959f772bf8c10360a06b903d760f80d20e58e54d6963dd90626f300a3698e7da5345ecc3b78bb540c93d5bd682617fedd0d0c256db9cca47609965d9bc0127393bafecd9a56adb30223f5833d55d701fffaa95f5aca09f86ea9238fcd087e678aad83a9d0f7eb0c0da3cee56ce0c4bc3499be3a42cc66ad4af5386452b8f73b6fedac43200c8daf0a6cbf2fdf2c7341bcd6a82d53809fb2c85fe036f9dd4dd4018991584eabea7205bc96bd4bc5db3ae26a8242e018f6be54753231f8e94408e93f76efc33215070a4be5a66af1a785229ab4e8da5726a486d672bafed02f9dc41668e0e453388e075e23fc1cdc103019c0d1c02ad2e927fb9046944fcc4e3606c29d1422c70b610b73abf75fda6557be32e4dfd800deeddb17510641267219f4a175291a55971205d267341646c0ac175d68a640b5cedca076ab198f317d50bdbcbe1ae9a301140f58a3194afb5e884d134799f8a45a52f4e5121ed124ecb97646c0893adf7ae70558fbc797cf947f6584da9e011a3a04f554ac75f93583caae322ab176eda09505e15cfe26352b5bf06fd1ae2ec37b1f16d1ae2074fbc3bad0d9646c708d67c5042194201603fda20510b413dd88d4831bec13fece98f566d3f16b1631f18ef90b24dadf0f9058a5a993dd07b4d1af1e3a344f9e1ec000027be437549e146eea080d567e9605e8219e9e2dedb1aa40d0bdc0f5fcd345ef01081784b96244bd1d0a7cba2c23ad8eb572bd5ca9e5cf8b1cf9ae2d9b9dc32f4750a8446459ce012f068f1345f5788742d22b458280c61c71d62910f5bd17ff26fedc31e727ce9f800336c253a08b89744af90d4ac57def22cd17679f28dc73893a90271e08cb42b70e271f32612618f7da437e82f741db179e0875f2e231cada82707732544f69fc037356e29bc18ae9ab68f75bd1a5e13529b2829fda0fd854cb19814d60a963f40a83aedfd041438361f2ee74dc53df3a8442ed6303f20243e435c4148932135b07c6a52079e0cc6938ec2a049f6c91be1b6d14fd6c6d68570469a22b3fa71ce10b86abf0d9a610e7053a43cd18c93400388561b70b80fa426c10a130bd4212540e6ff65e35a0f58e5050aa25385833bee8016ab26d45b5b4fe94fa2989c24b44080fafb3e94f25ba748a7f5acd7121dd17ca7560ff58c9ff7d0139d32d9db78b0dd1c9f4d833751aa7366a021b2fcf9548cbe60acb340b14282738e08c0a0a490f505f601e4d5b90f33f7c1edf7228dd0970bdfd31b0ec9382c6701f830fc9cf05adb7c0ee5123e8c7f098f4a30060bdffc64904f85727bbd8c46d4724bbe9be06d0a54e78e902e8ba79b8922ccd2d871bb3af7ab589881a702fa21d3d48bad403030207a2ef610ade438af56d5599833ec89faf86c49a2a4befb9f25bbde9ca04e7c833f7726da44fa135401cbbf6ba63b7b17764f983aefeba2f7cd6346f23057dd7a53ad5f4670d89e6c9fafed7f042a5c8bd20aee5943d31bcac4f63236b0bf9055a81bed4bb55080852e41e389cc5034bdae62ce6a288aa7b9a905806b7013800a92a91a82a6b27b2f54e22523620cc3e3c912f100a0fc161da4cb5ec1d0ff65d27ae2fb25d4527f170cea3646945caf1c59072ec32ae16b57f298b8c860af067b073614bce9b8c488aee48e8cd9d80ea0f3442c275ce17fd2a7506cee905cc9841c723ba2983b74534d323c438f352021652e4d02d0329c948d78048860c9dda1259e76209f37667822c2e69be99dc1734c3b24a18bbb859cefa0532bf0cc57a182ac8e87a0ec0954f6bac6a6aebf9ca9e043b39e2846eb877c8734e1108b4e9df59b553bcdd9530a30728ecebae4656e83f7e4b709309fdaee6c26ce5045403930af1687f76b45520fbd3615212a5673ff977b8fbed35ab6628c576320e98e0d8b76b1b8155e21d31b852950a0404e531228bf3584491a04859a891b00a67b552206bd957c88ee88a322fdb04260f60caa273431fa779d46f893775b4096c160e4e9d3b03c36948135ec0649b3a2b481ba5c6f0d449347bbf482d7e0608e0a0755d05d4900584cc09655a81731b2de56296af86d93e2ef069086da51608a3d7c9193af8d16e92e358ed97da8a26270ce2573b823f04e09cbb3ca0a49602d4b940b095bf62b9850feb99f84aa7e1deba6ebdbd8d1661b5158ab4a5dd6d02e0ac3e2623117895cf76d51187b3acf345851318819992d030a7c8d220b6f9080a31097836f456c3f6aeac835606f525cd2af00e1ee6cebf974875f5f4ef540e7b6f25c74fa2a8b5aea691475eb84302ed9d35882858e2e667f8a8f056bbb408cfa81e0d9b69f94322d71af22a17799a1333210794865ebcdf8f8cd191ac6009e3e834ab41a3c64d4858e98d8d6586c6c7fdc0458f69ce7f8b3e7c10d6c0ca03c4a60b09d11566b6303467b4943f3358ffdf67690ac0241f3514483858a20b04fdd7ebc809f4d9a0ecdc4c2da5f7f28ccad9a691bd4fdb07e7a4cd9587d19300705b73001c334b6823f2023cff89e824d9e1ff49410f7d185725577cf6e62002eb476ee394297c47d7945f648d8edd0b2cf6e03efd761a8e88d3d4c76d367909bebfcfac49f5f1e618f127bc54f2b999c3728b86eb2b63843c52d4f8d1d7c7003a2d1c760dc8147d4cea3a0a4ac3435fd149413a2f320771cfd16ff650ad5e001283206761fcc1237f46e8b01fb0535786b3a17c420b0e7b488da9c0664ba005b34749a6728c86285f8927bf6a82ad7ed44c436ba03199a711f2d1d45d56fd06d0f15a93a42aa6ee9c02e2b7369b63e39ceff765c4befce7986b696a60e0d803d2cee2be2c4197984fea69572925a1364cc6acea775f728a5d79071a86a496052ee1ca729c6603940396480780fbb604e2b13daaa12cc7f3f95dd94a781534071113d9679c798239250090d2ebd91de6649d2cf8b1c5cd88d084e8b11a1fef0a663beb45f6ec5264b0a2df0083918ba563912c384e9adeb8349d79564a07d5021eea43c1be89f337da0311a46257c52b83213d5f88d7d22ccbde58cb6e972e0df7520f6b569d7a416430f1dd651b05642ea05527d4bc6442d71e9f947c30e30c8095e6a3bb3b8cf28746a73a7ea9f71e022f65d02cacc5f15e80fa45d8b593048263b0033d05b764f88b30847c23407fdcdb3431eed840be6ce53da7887c680695f9faf897f03b8f3cf4202e1d7ef1f58d93d2e443ec7a2b26511c9a574d7b0e29a3dad44bea8184dc5f971186bf9f009f9ccd261baf44ffe5df6c8afc4fa900cd52fc7306616461a54c04bcbafaa0b3132f3672a632849e3247117274bce803 true +check_ring_signature 57c85fde9c0f8605dd0a3f52dc228699bb106ba39e00f448ca1c246c8780fbcf 61e5f12c0a48c2710a791c4bb3d3187537a372a278cdb3ae2187256c68fcc44c 1 9dee982ed0d2817ae9e5a91c755994eb5039d491329a405dc29094af65ad83c2 a2d9f85f8f57dc72b5adc13eb15246e10c1b0a473bbcc71a0f3745c056ba7d013b3339719bcd21b0b9ea43248fb900922f1622241e81de43e19e340da7e86b0d true +check_ring_signature 779fd6461c87b2b83e347424ecafed4d49c31016ce3d548a57eb738cac1c8132 fd48e5a69baa54ff74df5fcf7528f508ab87bb2ab6b887ec0e9d5766a6904d07 2 d20e73aa9b0b54cdcf6067dff64a6a016771965ee95739412ceddccc6f5f5eb4 03ec2f0dc9e587c7b63eb7edb620b38c43657e0cb5a27cec25d35fd15f03d72b e64dbedcf7e6c3811919de8486cdf72a8c9240fbd67f7c1bcf1178e8183bdd0d313f2b9ee7baac6e9731951d0683e7f4a9648a1675b22aee69c4a6d02c442a07bc25bd587b16b304d92bdf091e2917677b3fa5bf4e3336022680ab92ed064e0b1897a023ef14d3e4b0051497771be8c8462f051cd1a3faad68c7dd2c31598008 true +check_ring_signature d9012c38da13a8d1956dd679a9cabe812b159231f633b3458d06912cbd116346 bfa550bf4b376c92cf8db38eedd04d852384f8fcebd7df95d7fb3c3856125a9f 9 bba7fc2d9dcd4c958b19415c06f80418186e570c40acb554cd724cea97b2aa00 068373e963cd3e0f77946078d2b8c7e1bd04b5f103561c3c44a0f3111c2b7b87 eb63c3fa8a4b224615dcb3665ae7992b68d74322bd7f7aa45509f04d61c92ce4 3c76f142a4baa861f9337034968d7e4263f9f5f6fa2060947afef5c90eda1826 b43500a1d2daff088ffce4d15f8a5ce512a167e46576066fc81939275eddcec1 cead5378aa16bae99ed01b6e0d10cb2b95a4191a6f0380208afe18313e11efc2 bd01cba94cbc411c7a425c1c3f111ecd3f583c9f54957f2affea83685bfa7920 7fd717f845c128fb901b114cd5a83dd5d0aed15c7f0fb6ac888f7c90a0b990df a514955827104460eeae8a3623993c02db4bb66dc0c1794d10ecc3a8875907c9 394f101f55438d15a4ef7bf6325b41227ec204b8bc5c4eebab87b9429e39a69b099dde2fa2e5005d240d835e7af5159f8086b9e0dfd947cc49af7f9edfc037016db3a666ce64142a00247c2db2f973c8ac55649bfc7007e25e29d672e66cac09a09531828a4a63ab3ccf179d558ed28660218064c06652b22bfd2a15ea47da0d629cbde501e0e7701c3152bf9f56cf7a1282d610c1065f9156d468ff369bb203dfc52c838f2b20d9002b802ca15708291be3dd0c739ca39e516207b38cefcf0fdfa46140352418eb82ce52b8d7f4ee2796db544404a6c9bc2ed470df65d43fd5034515fd1b77d13db3de057e793ef4c2f61c7e37375f80a6406efa7538192e07fcf8fbf3dfcd0f4726a4c80e10751eb18e31b02361703c194cc0cede2fad0d010eac5e3cef47bd792bc1afc7116e071c55b8420ddee99e732d6731ae0013aa018a726970da92cbe741cbd5af6eb5d1809832645ff387d6a0e8d830c6ccbf8b00f9aba8585bbfd0ecab2ad038e2ff8eef6e3f67e1dca957339dbda27c18924d01e82bee4531befa10223ef8a2ed0c2416074a68483cf685e463efb1ccfba6801ff52c9033fefef0ef4aa8747c4158160ff89dceca1776a2f8d8e476507344c2045b98297e668e25b6ae758e69a6cde0611cdffa768e9bb814170edede8d4c000a2135416d65fc79ae2ca3e0ca22e3c9be2aec59eec1cd092235b4dea11d99970d3c1f2319b5d6ac035ef900f390014e079eed718dadde922413df977dffe3a7041234a24fdcda26f8571fc8901a3e7abd5c7338a5b8dcc4aa75d7e451d92b840f false +check_ring_signature 20cb739a347e257d9c7cd0bc7f1df295647b963ca7b587ce313d69483605e654 74f9951f66b14e993720dc408dde0e9dc0152ea73f35dba34e7e11037f86b9e5 1 eba76caf406acf9df119cbae901a8a2d7d7872309f7844d640043fc818321e0e 01f43530448e4eb8b536632fdbb319e0c1bb25fa215b5e5c22d6da0f9a76350ce01621f403d28192ee4cef996d22ee9b732a16bfdb1f3e7d7f134f317a26e40e true +check_ring_signature b8c6ee3883ff50cc542358172e9cbdd24f2659bf76da761e63878370e840e8de 1c32353e7864604f04c6972831d2ba7b116fad9f403d55eec235da4ea41bc31b 2 f9e0826b28c10321957627aafee2f548dae9e3432e82bf55a228e523babbae82 c91c60c18122a6b7a0a1409b06aa2558ca4fd1a12e4f8c6e9cf6b6e44e24f6e4 d8eb5427ce9746789741920b256789bbc1920cbc38f666019f8e4d60c9c3480d59e73d14d3c2d512eb3e024fd6cbc05a4d9c5204c4498ec9d119449058ed3d02551c46d5de55a5ee7d02500c0f886a18236ac93500a036334c2436eed892e306ef3279a6660e33c0ab32ce9db06f343f1f835526fc74cd117eeebd1931ac2e07 false +check_ring_signature 24accbb49d6f2e11c5b9147499e649f885475cbe3583b22bac3dd0d1c428b7bf 5df4f595d0817c40680752baf2348d7b75493dca917018abeca88b78055118ad 19 e4b996fb341a5c1bc330e457ded8bbc14a59aa51303bdb974b5547ffe120807d 04ee12673f41a43e7611b11be23485e03a2a76a75c30597b43872e38adf258a4 dd21dae5c0d29fca38e6148c2f7bf1eb7147d37dd9443d8eebd568ae51548d2d aa066b9701605ec181565927c81b30eff20d9eb01835fc2ab8aac84920a60d4f b454505556009f2da179e2622bc88553fa367a84d2b39d1133b125b17e9351cf d32e979a6d0e28658a08bc14a0c104d3c979fef7296788af7f9a24d2c83873d8 d35dc8f81ffbb9d595acee7fa7cbcb2fd141c30a4e9603541e44780e54d74338 cd06fbee894668ca3a43a78396b52bcf89398a176af87b8e50ddd371698d518b 8c8a887907662707006c8701e95aef6d9239d49a19162ad2d467181632885ed2 74aa741314438f3441ce52dc1bc94bcbccb43b202d221eceab97984e505ef23a 8e7cf299322351c8cbeb5506176e76d9d98a3be9f91a7d80f03179a713749aef bad287eea2950a5dfc19446ab2d704e215a0845b8df717fad23b963b0a448df2 85aae386b77b8e1b355610abfdc022e88c112b5decb9f26cbe7d12e5dad38621 9be240432cea6004d983b1bf09efb367dcd69fb13988870574d2c1e3e43cf81c 64c188edd05039bbb4a725709317c2d39ba7c7756395767dcb31cde8475135ae f94a63c7594d33cf675c2402462d855ae291d14393306ee890ceee202e822c17 cd95e0ec70590f08ad84805d05a6ee104cab67a0c1a6343295843cb510ca5224 c8feea22fe1f4a3665243cf9cb1ed3d24f3f33771a6ca64115234eb7b01a5fa9 9b1bf479fae7b1380ba4ccb9ba3c2711627f23c0c15390e8d7bc2eb34aba14c3 e87f0db932a213dd9c1c5b094572e89500ed50701a7b7a56c139d0129dabf9027d6fc33de3cfdb8a5f7aad22b8947256e384ae27b1bf750b90e7c38236199600f0153417fa2b33bde508a8e55bd0be8639b32c67f92a287f1ab2f42d39e28804bc8da18e2fef4a2231082f033a30655bcff9814b39aec8feb7a2e5d61facb7030f01b5ff4a01b4a8d5e5be973f20bc99c3448928283b5587ae8462793d6f8f03d1752a3d195095d8519b15f5ce77c3015602f88ad3849c47dfdb9d441cbdcd0f004fc6d64b0faf26a02f08156ae574446cfb5410b7049bc973d76a3dbebe6400619852dab319a8707e5e78e75305c21c999e214bf864d5e111386061a476cd04f56dd072bc32bb2ff55ee551a9ef6898ba70ff7ad88968ff0de79b80a5dfaf0f4a2e9aeefc61613b96c5d2c38b6b4ac9d613c1d5d3233c984611a3af7cba1e0c9acdf326ad87c78c9cf998676b266cfe3a325f07161c9c08524d00cf0dd68f023018e3485eff5854fa73919763872046c9107b9f0ebc4a8474bb167a6662ee00658f01dc9289e515de8b7ed4050890fea0ebeeb0d6ca75f4e6466425075a28069a022b44e7d6ed8e10013840216572e6f32f7713066a85a043731ac6ed272c0ca2518e209445f0e2122a91e08292588a98ccd524f3b252cb34cf9d3edffde5048449b009771e6530bb03984171e88fdfca15402ed0db6c0e1e78817610b27205a2d1e6657f3fda95e2578b0829d17196dda43dc9a5c1eab3906ca95bd32b9e0efde97ddc46a5b99b20b428dbee7ba85b120813ed2fdddee16d16425bcd745e053dd5d20359799948d4c198807d436390c2848eb5d937a521967fc2c198379b01b76e022ef7caf6c603a322739a298a396d9cad057bef9948c9222b9eaa16080a32f8ecda36c32bcf2ee383cab327b0bc2898e5d5dd0080062eb21b30afe25f0ff6c01f77a877487d199bf6cac308dd144f66ad761f289d7b7d185c0b645224073e0b10fa112db3bf31e3933f1676ea234321a8f7e0357b087a5452d0562e4e07b9ffca503756fa8fa0fa8e541c837f7a01398d55dacde74030a64251712448070f98571207206da431fca7a7d6c968ab1fdcd5315bdb9ab38cc407bb34c22608e5cc2ab65469fa698cfa98e165eff23ae2574ce135f69d85112a1bf260869500ba9127bcd1b9449822915926e494500df4250a29d27787689df168f2c1c82401888e8473920f63f912029f07bf5b498d8d0e727a2ed27477022bf78aa184e009cbe00d9fe5ba8cbf1b8091a3a463beef0015c7ed8347a07bdc71a12feabfa3097b6793e763ab55aa629415016b677a35e06e534a8807857d525f8f529dbec203cf5d0139c57b336a024a142690d74f6a843121cad16f118f9a479cef242dc60f76ca9a2f331d83edf493d0c04787cc930206f4ca37d9d6632c6dc0bf90e8f3050fc931e81a3f506965c85db48a9472db710495ad02d967a812610866d1c28f06ba8bee2e705e51dc1759696a2f91057682702993d40a0c8814c7f9b42d96c604a121cee4c811f19ced2ad84d8f9486883ac984f12cbd6c40ead3b3ebebc5990e5819101eca289e27218a942a8f34325e3a5b256060824f981e9ce1484637710020b1743638a2017596912cf8c68959d537f27e1ea3f99044b811fa3020059d09733c62f50bfe978a98b16499a173afa545a4a2d191e94397745ca99ea900c807 true +check_ring_signature c1c5eb4cce1092a0de4e8a28dfe620b87bfe4333ab453724d15c46319d319bb9 d1e3af54604bfa59c8dc1420a8037fe15db435b410ed1673a053c99925b4b219 61 a0cc151ea98a93fd12c45e0af47212931066e04e32f9ee17707b82ea6345ccd2 9be383dbc70e6f7ad61e04d5ee89d801a101160d39ce7e748564e5c30553a7a7 8ca17b631e334e4b2882948f598146c97f1903c54ccfcdd2c027e936d7a4362a 64494c185864fb465b10deca53f65b48a0e353b3d9618f055c0380b5c3a35284 ac2ff085bc4230c752edd40e68c810b43bbacd42f6acb7f7aecb934a23ba5597 b2adc3ee261715f84b8b4e51569336e487e41083e25d1344aaad2fff07fb291d 2dd53f6922340010e4e4c604dc9e5e149007ee05e021346f5449292c08086dd5 8a69c81225769cf94c601d9ca35cdf21e1431edd75c8a41d74bd5bf73b2d3959 baac28331668c0f687bf9084250eda998990b42ae48265a6600f46834e395f60 f7378b4598f881f16cb789fc58049f68a573d9d8570e56d2d93e05802600a277 0892efac798797d797826596740fcab4e09e01c988b12ceb37b6299696812b72 f6ddafa8a734e4878ce38580078bb5f18e8a48b3f9c3364cf719bc9de02775eb b5ebab986db3dbb7e8215c781208e626d3dd7d094fa2f3fe6068269c5006d410 ff400b534fbe57242890d1aa70824608798bfdba1372681e4e3dc455c3274700 fbbbede7941b719769cf519a93ab6a6b6aa3bc4530f8eb6314c199b9f5c02062 ff7a6f7ff3095ef78c93256de2b2e1a429efd5c79f42e3439f0da04d3042c426 27faf33859436fea557fc3d4035984a376f254ddb5b166bf831870874d7d34a0 9750c411be2e2da8921e75d63d2610e27ff58ccce4b9910302eaaa7b53f5e984 3776082e8e913cca404c59a9f5cb03b1b688a9b96e7d015409c283f74c01c27c dd36521df78e5f667856510136942c6c75516a08587d588f20666cd6446e9279 e25215f26ae3e4e0d8967238c76e2c6accbe679a39328271fa1135756c5d840a ab540e23868e93392514b2dc5f9bcfc36f0ebd1819aa92a33e7c7c26099660fe 90f455c4042a6b55a0d61f6b6ea287de88d29cca284fd3b5f9a1fd0189b822e8 b24058f4d867b93eda2b3fd7075bf63676addde839d0e5dbaf42a94a3131c84d d557e6464caca55ed488670757cf7c3ccc5b6172d099b73c4e98039c630a7eba 353a2d0642eb0bfb780017f92d2a16edb314483547ad3b1cf9bd84b5e3ae48a8 a1de2a6a778c3cdfe455c8983380b5d031c03db962def92c09c01e955bfed711 4230d187a09b2f08033701e567d3e2b988d2642b69329f312a23d022929f1cb6 9c18596b412701dbaa971b9c99f550517c160722b89679bd8d2ef0aa8b986805 7fe8ed97fe8b77c361cdd02c482c4b8f8aea523f0c24e94beea5979e048fe2f5 0953f582295f627fd17cf53b3a4dc81621dfb68f3ac2b7902fecd40f243e159d 62d99a0c08928cedc3ec63e657435036631592ce65e7c3a3f39801dff74fec5a e2623b171ef36526a89fb1be0ad782a9ebfb99cb74ca8cca7454f3aa2897a76c 72de37f6d84e07ab6b90d76ef549a986b4fa081cc1b5587678973e5c828eecef 9b74f24a975ea31654843441f18bc60915872543fc736edf4ec72954da80ac3d 75c7b74ff0fe7c93f7392eac6246c747c1e5dbc0d281964b01b441e8df67097b e4a6c5eb82053aacfd81f772cdc6605753279ec40d7dfd1d97a2b54d711bcfa6 fb696af4f259c4723404563d06274cc01673f72edba92a592267a04e3f20b92b f8527a04379bb73b734f0f4572b35dd485a15536a93bfa6846068792039012aa 02b930b2ae3ea809fe67678149e816bacd3de755fa5cac8bb7ec613b128e7b06 44c872f8bf513c5ff3e733bde13bd546aff0b74e391726f7b2cb8627ff260302 ea9a10f3c4d7e156458727f4ca76c83ef22732700c4d39c638f501da3bc044b7 d1852e7815f1e606beb3b5e9397b1b6fafcaccc2b8665484a84a9239631ac322 130be5fa3d075bd49bfb1c539fa3f38ecda4c8b8194e82634f958da3132d0956 a96995560ec3609a25f85476ef1c654184341ae6c6ac8703cba2a770b329e5c3 d777b16dff8ca9bdeb0771c145f35cbd88d0228df9e963cab8a7a46d3840c78c 6438b0a0d45fff3c3c77b2050a5ecdc4d08319574078a99a25f5956d689d986a 706815af75fa54159eec915f09c6fb14fb2945739b193a187940d63eaf3fd4f5 38cb00e1a0f0339e9f96babc32bb5ebcb454a995c2e45faffa1cabea4c70298d 55ec13648c3330b078c649ec7e32e7960e72e01b239ae0b849d16365bf584d33 85818ab9e4ae930176b9d6594912fee09a31ed0a6e0d212ddeb6ff59a83b20b7 d7e9dfd70f238a55638190b0043ff16c3c0bf120c1e610553468b7a12fd29f7b 458fc5d41871d3da537a35ddb97280a43ffb312043113045ab7c62b525e05381 a83ea33d039f17b12ecfe85f49e50a3515e8e968e186733f1238ed8960bced1a 5de6d167705b50e57b575a683db4a1c93e71cc8a2c1268a12217205b0911ad9a bb1e6baed4387aa83945e5b2539657105d422f6f5c4f82c0cfc2d548fa4c2932 bad9b621da2b41e9a8678253ce22d16fd11c48a03b3a0be76e585cf75655fe3c 6a757247e790da80164723234e4580e06009d685314e8783c9b25e95f308bf42 ecd141aa2d170e04715fe692a2382915993faa8b20a207284c791726e63bcd7c 9878d381dea74bb6306d26605870c0c3399f58d15b58aed44f8f5c499998b16e 7d90430183c2baaa15cf7adb818e676bf3fd14d87f7c43d2f0abfd46c15cab56 06606cb5f314e0e03595bb3f6ac0428db9ed11ae486d38ed442838d54831c00c869a4925310d63555fd12738430d9c6e877a9187813db0d1c193e6ceeadfdb0f8bbad77ae28c2143d34b760e88252041dfdb477038cd18939ceb5af8e368b304916f6e71cdf598f814b2a82617bfb4ac93497699ff72adc2694c311bedef6400fb3185d753baa8ac137c4a9e7165ea6e3a9aad9af1506723f648c8d5f95c790681282fe00e9f7aab6ef28c0ac3ce343ddf9b65f29da57097abb51ada2712ea0d55b9473f87e593a630583801dde2e75e4ce97c5342b5ec76b363ebcc4281470dc48ad03c36618b9eb136392a7371cc20cbfe77f885b22d756cd2fd6f6b12c107bca3d0076d48bae32bee7af79ab8e5717449ac4106f885d052741047256bbe0aebc82eb6210cabdaeb88fff6273b23d1d812cb6c69fca3519667d372a0277a047f358a8a63a5e662b0c36c1f809a21a2060329fa361bfe25339f741a434df200eb6eed0190ebfe2672d50d69d521fff8836e6937e4cb91d18ced60108bcfdc056749bcfe62e35b3cdc431edb7016b4235001ea517fde37b54948d13b7ff4000f21244304831af7110d50e9cd12c471a05db9e0bbafbca71d6e9c83613fb1290c987bd023013e9467feabf5090db6b1b40d674e810df3d87279b37f3681b72204dec24ce86ea9a2bec1761187e5e158e9808cb988e8a62387f523e29bcbe3c40a1f280e4433430a74827d2e5e65f1c62297031b32eb748ed6646e2a99e530f50a277e080c2107d96df7995e3e9e2901203e5cfec40f652c13e06f81052efaef022c3263de93a6110b7a57dbdc9bfa46d5a73bc3e29b847a7659b3c76a9f07eb0a1d5ee9ee2346c8e431ca1b7263b5502a2397b847f57b584ccc34c99324349f0757b4a267b8235c1aeded33fd3974014370227c5da258a558e922ff66148b1b063e5034c2aabfda1fd15b95596260c5fb7d5020cdceb1ec470003743c0bdabe019c00689569c42b919625da0a540d974b4002e9b5e119247fdb4baa7f2a606401b1f5f5b7294c487dfa90287fb6fb16116e687c42860981ba0c40c235fffcbf0d6d53b9a8cdb81cb0d2c4f281c2e20b891c4c2c2482d3e49d4695e77a52e2ef0dee25ef3277d6289bdfe108959f0dbe618bfb0025c5cb6ddb38865e29d611aa076b3fb7fd437718348664f6fb236ad12ad2ddf3c85baa2b1ca820f4f241de33095db4098d49e352171a5a491f30d213f956353981991163dc3fe2003184481b0045720ea0876c875538e39a2db38157e387ecfe565dbeaedf4661a7b83696320a1bc9ab375220cb39e25e9dba405330e4feed329e2a5a5f2b3f8b9f2903ad1802e3285896bb2c0b02b19fa01dad68266c62e6f280cb0e9c233a3af5480432a705a98a32ed52550a0cf48550ddac6158f186e95c5cda7317f3534280368e25a1073aa391609b22542a86ff16f1196275ed03def8286ef71197770cbcad3270fb04513ae77f10ea5744e3c66e0c935c8804d8cec90abe6a21697cd090aada0db909fd89417ead983391c67f77bc3e7627fe60a3cfba88a17eccc606b629d4b5050a6089f42ab01a54c8b53914fd2219791521becc6bc7f956fd00cd1ff5c015c6044e0d9511f937b6237142b88cd3d2e7b9757f531ad150fe54eabe73d45901270fd46b3cef20cf8c0238dd8596bd6fc60641da203bb1cf93c6cdf5bf1dd0f36d0b215cc2d84bc9da9c7ba8728d3a84e2f477b3c68255adcfca49904340a00caa0ad9f11fd906aef065011aa3cc2746bf185050bd4a1ff7e7f3cc2a7956a16eb60d3f286c0ae59d81307f799b08765350caef53920df4d5cbc5a4bcdb5b1efad5037b87ef90459ef4374a80915c93bfd70144aeae20773d98ed713f9af8adfe000623837beefc1f93b9bc22fb89ee9e64297afe4031723a8b753e87773d76bf22052b49795617e8b1b5f000f75106cd4d13cfb2efb910de24d1aed8c47220722a0ff82e4b8c26ce1ff134e493c0d4c6b14a9a31812be6f64c0b579bccd9eb413f0fb496ead6da39e6b2c3b9494e9e06240e82126802c76a38345fbbca642d2ef90f87954c7c8ac19e15d92b661e1e9528e137273867cae8e44e31307a1b1844420afce13cf15b22bc075606e44d3f98ef4365ce5db2d555c940ee5ee2a3ca0a0e09b12d493e7b701655d7187425a3fad0b013cadb11b778881bf2b508b81843080bec535aa56ef3c820fa89181b3d2075bc9beeb6af33ca29d8ffd496f0af49b60c1e9acba48206b73b7f9380fb20a17f609499535695a391374377201e3724cb0e94eda2abb58fe1484d18593fdd45d8a34ffede60d73bdfb704990a2fef2257016c7374bd37fa5ba4997bee1f21ec2fb0a3f89fc005224d263f96288063300504449ca19525c19074a31cfcdd7c9cf6c910143904f72f46e897a7122029f24c00343503e3c6521ba5f96ba106ed4f402af2eccb2deb783d102e2dd428e4068907177ccae2edf49ed772034be32988d43a8c1c74982b8669210ad87e80ce1ca0058fd8fdb9e2cb85de9c9492a8eef25689bd7128effff15542eeb4dc004ab8390f1def3686917a9713d7afefeade0734d1218a395295ded87e02709b724700df009d8aa775b470e38130b77b15e4c62dfc885505097cd2d5610cdb08362e82a90e9c89c2bc634279d81179afea71914de0968bc9a71a66a5742640b234ad40860075e57f7a3b4927ccbb71b69d7757613ed426e420b751c2f13fd2688e2639b60645cd72907335370d759aff530aaa17cdf5cf24914525ae9a824b609b573d6207b72f9b2d070ed79eac3f4ac5970f39f7f82937081b6b400cd5a3f7c35fae050aeaa5d65067678cbf503d377509d8977137b312bb5756e5a6e742f77484133c07ca5771fd96d1fa8f08718053d7cdbb547150ab61e55410cfaf6653a846c5970e951cb0ceff8b81cc41b8b4c85321d11c7e126a887732a4a624d637dbc5d0ad0ec07f753d00707277cc82c939e970e428f45d3d7e7f53342e9c97bb3e5cac370a57aca77b638501dd2c4bd4409184b016aae6e7d8be998c6a56bbf04aa595fd04d57e0902c52afde5d639170d29ab4b5d5f6f013f0af44ec4c187f18224eca70f3e612747e784d017cb0721b7f22cfc1ef0ad1e7bd64220e90a2edd4add742b0a61746d3e8f7c5fec090714cf1b94c9651c453f3241b69bfd39bb7ef250fc1b04d97ef2430900ac92f5dd56300a6a505bbb3b29612d7c95f1480c1eb64751d509637e5003f70376c0cd116d9e0c43ef23e11b1e95fe7f3a0bebeaf99b301a3107876018ff33c5a19a494994fa21ea59a82ccf885b7b9055ad57e970470dc30c0633eba9f7e273dd222b8270efda7436ee4747430a5189449107941f792b68b90b11fefc0ddea01c8abf48afd325fc177b9f28587e34077aa41c164379e0a5e70a11e069c8c29203fea89ff4a8b9cf42324b8a9134a02d852b69a80e028c24dc0ab920217815a322c3b8a20c2ac801088bff7cc96b9eb96b23918ec3aaecb9aa082c7447b4e3c6cb6ca101d2522456e57e9e96c8f2136b6814f1c44b4402c5ee0be65c34f278228ed01fce8b33627d4a44f3142d1ffb2916a716f6090dec663007d4ff9ae579c9e15ced57f18c8fab21b6d7a93c7e0c427aa0e900647794bc530eb3daa875496ddb787b7804600e2fed30412ccc2f42c9dec003ec983a84a1cf0642da5fae92ea5de727b76f28910b29246027405b2ef8eab524e053743f49ed077ea9c96ab11bdd832472f8554b90de5dcd7a7a05885f6ee1fc07843727e61e0304d4975da363b579b7368d8dfd4f1e335911ff74775133ef8411bda7c9b89c02ca5c6981c25fedcab33ab364fdbe282554ae3f6d42cc616e24a7d4796a6e58044d6019b26e9890d714559c5244976b38b16b80bc91f07c98365e20d95c2cd409c773038f423c86f05acb0b31b2942e0d855477f22901b2c6538d1a47f4e7280b2a21b5b79e0cb68cd0c27e121158b110e37216a1f9a01df73a07462790a45d0f8fe35fd0f7f3f0402a5e9ea78b5f6852f55da56750f942624e5cee211108c80a2e98a1ff98b6df8ed0a4d003d45ee59d3b9ed7cfcd98e242bf7ab32bbb753a0797b3fd8bfefc177e0e8c7a7cf39b646144be15c1b71610f98c01cda2ae8c0c0f387c99a643b9367a15f3a06790c0841e9efce2387781d1a8457071d1058e1b075f140ae83cca4a6a1137eff4100b838b4241153990ef1211401fe828bd8bad091909d95f10360ecf2f6c3514348897dac1f8bd3e4eed7b4fe61a39b778a1f604d82a203dec6c33aeb6742f2fb76e6e3bd5224f6a2297b8da68614dd484df930d44cc4581bbe00f2b9ddfec625246c1785868480b34c565bcf4e20059ad73fe0d4b9763b9ffe86a44e26a0e95dd1a18a4d79db2fb60f990b4b186e04a434190086f93b44ceed4bb459a254cde159f58cbe5ee18ba3905e19ceea0c3b702e6220f78ddbd4641c65aa0fe1feedc64272f304732a9744da64e0e185cd8450e96e70407a470775cdd67f18d5f1e2e5d1c99dfb31e6a62efc17333d7571ccd11fae20ec2ce79b4c5dcf487e7109eda335cfb409b05cb44da199e5169eefbc60b1c74035d4c52b588f615f5601b1f05ec13db575a4349df53a91e5b40f62649c597c70ee524cdf66c6a174aed0be137dd21aa1a6707c3b2af32c612914816a15ee8b50840dcc45b852a998e9849aefa092dd6848a5aeefebee6edab5309526933644b0d2cb86b36cbae2eb04e77c9256175d030deab1650a90cc05a2613cc7282c1a800da4556219fe2b1c03e387a165399050ca24b1c47490c9ee087ae218817b97e0127918543517baa5271a5e26da373a396686b33fe4ce7bc76d2b0e6edc6b23e06e70eec5190296467815436d01acab660da934be7d7197f71218491ea46fd60029fb423874f2c14d5a025a77380d107ee565c605e1ff03495a5ef936cd4f9ce03f6420d43205587ef3b98e14f4bdcf88597f1e664d911342629e8c32d32e11e07090c3adac246bc5564c507c6d398832cde567c4bb47bff12ea0f152da9b34602373aa535b65643cd6944cce25d7a0a85635fe26c3c58fe0a9934cbb472145f0e934e3d5f8dbd11f8c969e85aca541507d0ddf26121972f4d790b0fcefd54040e208dee3459874f87ba9ac41008a8f72c409db8ca06dd10b1bc7c084a8884da057711ef78b73092bae51b5bb66bb90e9b7b795007429472979a58a1f82a8b3004078401ec5b4187d32efe50cbb757f5aa86979efe92b3362739181106b9bf5c05c076c463cdc193af2a508f35f597fc7b707a2f31802ded2ab1670cfce787f800ed3a1ac61f1457eb3d91768f5b874877840e952889c679a0302a60c393b4b50ec35d67500fa4b5d51b099092bf06c7b042f50d116e02c1d871d2a500681221019892288d15e0f8fe2bfa0d71dac5283d408efb9c704763c408dd1d137c6e8603c8bffaa50006a42468913272402f12d0b7c44bea499de8273449657cbb8cad0d false +check_ring_signature 41cfdb958ae694106622566c2698313af827d800e452112576989cfaba9a0e1b 74ffd5c2e00bd7a97d99f2240136e992f9f2c4bb49d387faee845563e9ed24aa 83 94bfe59104ac330c2b0e3bae2a6a751c6fa317d0cfa2af2e8a3656cd98541197 5528a6542659cc98fe38d3221052c74d153d5013d29fb4a25cd39f8a6b36388b a9fdff273d27a5f15071220538786ed7a4ed7e7f6ea97c53faeca7364a4521f2 8338601d8b30992b4701f1b1de9b9651cc5b9c2c6bac2bf8bb11d85a528d8c10 daf168c685296030c3260b6e0071ed878eff9284986cb80bc3a41c60c1e4e9ec b594a96b4cb7eaf02380e81dfeb4387eb37551600d3b8ec99dc517507c3679c8 70d7d590951e2d431b1eff962e2ba7b7417a787745a4dbb001abf9a8c5177dee 413c154bf66a88b5ada130a1e6eb58fe9c65a3521fe38deaf399b81df1372a2a 1d0ec359b979553cbbad8b8c04d5c07e9f70c3a7535d67681a37a007cee34207 3531cb94b5bcf36d6c42521420796c6cc0034017e3a5c78563a711ba5a097553 7629c4adc7e1e713cfe1b3061a32a699f4dcb3873223c3bf5ee38be939b63026 15a2035b924becb28fdeccc097ed5823a8f494397afb8d1377543096b2d2258f 66448ecd1eb43bd94910e280eca415c1289bd9b10e09df2bd0ad6b2a55cb958e a7d1af724731c76549d34df70dd3c3bd899c392762725766d71aba3794e45b55 7a1c1e50444b8860e92ea9b5b90c36659f7806e861317b706485bab5eeb7c7d5 1f91bc3e4091191e8f62fc42a77fd13f6dea235a87882d52e8fd08511e08c361 61cdea052567aa36ef710df68ca0b30a6d74137acc6ec9fe268e71cc606c29e4 968c4b0249a1e3448dd27ad4299efce5f6cead3787f5aa148ae2c94b7a7989c7 bd568e51e8cf4e293b54bd0121d5affb07e3a7addfafe2e37319614f4ffb528f 669689567cdb1b2c63ea72cd99d7ecbd2c19f989f8cb741e70258395e3bdd4df d9543fa46783581db04027b840dfc3b90e31a415560ebe1e57336a77a721e8cd b5dc33a215bc730726edbefa8ea4ae68c76dbaed315c70455d1797328a93464c 4b1f1c54f9704e214e27c99b07673d498a211176b9f27b64dcc8e78463e18b08 276d1e0b87e63595e74ec778e869c6c997b0c75f430c60978d7164847ebc10b6 4273bb54b06097efcab0bb8c6a86b6504428dffa69ed03b52dc29d1546b41d6d 92e1a7f8d7d7180ea898c73fd8e75eeb916f0f3d05c8e99cc616071fa43940e7 e3e1b1ffaf0d632484176be934daa3f908282327bb47df6e93a7c8c80c6c3120 0f511fc22693bd5be6b4cb8e45a70e153e236bf534301b193bdb9a36a25792fb 16d1171b94b994cf8b853c8fb1e4ce7cf7423630a3e56d5f178bbc382348049b b5b8cfc81023fd632eb7a29eb2b3cc8e85953a42516ed45702824d5656722e4e 92a16047d804df6a7b593b7b8d9824c245a3a99e66f96320c3693eac4338c882 d4f0942bf42e22928025346d5c2fca5963bfde793451de5970c6df4967fedb8e f987dc4429ce8cba4dfa30de548c5fff54b4bd048de84a7d0b0e9b881c557597 9b52cca9084be8a796fc24295d52a9eb09fcea1256888c6a5287da7d00796828 5ce3dfc08e91e1f7a56b5d14ddafa913bc38fed730b82096675182b5bee2ae47 8768e5a17a906fdbe65e4996bbaf71e9e407a6172d0bef289b4e02845fc00f40 7740b710c262808afef6ad8225bf749b0949981b9889b3b0d6f7547061c582cf 0bf7cd090e814fa2c4342a2ce5e378bfdc0c3cb28600158e416a5711d81c3a10 bff35d9ba10fd861116611b7ba010eeca27924f407c070068bcac96d372111d2 82c73e22d284a5924c7ca1689c64a6dfa77813dfee8c7df705204279279a134f 908f7d58cf24a22ea8d32f481a3884a740b47819840ca95c43e488d75506d241 2b6201c786a0808e92e9c053681489ad5bf9daf46090ad0ee20082c26fbf185d 6acc677bd24884fb2264afce466d70f75f697d096a582a57d54d89f30afd5ee3 408dcfd9cafe665b4d18dc21b9ce3d5d5da5ab5ea93cb8ad3db1c375b0b70d89 44fda67435d61d2c59a883ceac6b0e17726e207a0f4d24f0f154c9aeaa9c352a 69ffe40892440490c4b3ca65979f9124848b152d87212ffe4a235ae8fa88fcf3 3e28a966f3f51cbe95ad3516da395728f6bc6f382209ff1950a76ca141ed4378 8beebec25edd5bce9a86f4e94b72d14162ff11378ede3bddff0f2be61bb74b3e 318fc58c4476ebe2a5494abee2493488c2a7a7f130c47c25b9e44956ebe84344 4e7f2e188675ec16fa2b7f16eb51a32580b64870e1a5765bbdad564f5e70d4a7 4d23de73a3021d5ed603840f73872744b1ee9ade831d60bbf685318b30291e0c f416f1648d2c58927083addc884be42dcfd18cc4cad9913a6aa37bc3d8139a6e 7df90e66da148c07b62542bd87515210841e6273761289bcf8e2429ef60c19d2 c43d580ae551be52d8495193736803b98e5a63cd8770ba6cc695dbfd5a3e3090 d8bfc67d9110a9ba63eb89132eb26f089666e62d666c4a374a1cd142a20c8efa be2c83477b5427724548a886bb116a6c719aaee74feddeeb5e96552ca2a1cc11 9c4d3c41541c1416bb51b70ac29f67fea69910159bf8a66bde84042cc2a9eabb 74f8c23869b44fcd1f3d6dcbe9c58fdd2d9ec028a9f565fe0f34c76a6d466f4e 9d2283a8ef60517d74a4dddf97b10acf4af0b58be3f6c741fd4f6f02f5355c14 31930f1fc8619ecc8849d24cdfc9788c750d455bcc609867b58e58f4fda9ca30 2fcc8acefc17e2186991d9b114966647e4b968c931a9203318a04e4fc3b54b16 8984c2f947ff842d5dc98472a16257ca83581d2ae8cfdc173c8b23c3e6becb35 6e9ded7e9df8091ec0df5ca36b7e1f0d4d2281d0b59c08e711be33d41738fd86 1f6a15489015994cf1d9e00d6c93910ae1fec189fa973e784eaf7de5a44e83f5 124d0dc9b15652bd8b7160ecc05dbb139673e41e5bc8fc30c5da1836055a6fd9 2917cdeff7e4ace2b4280bae4a959f7a37f4806ba308ac4c9b9586ca4bba3ec3 bbf8db8364d1137895161cae0a69f320f0f06ae7d2e960ff90a582db99d76707 5de7cc1254d9637a1b4624ab0fc46c83a85c53487601eaa265027f7612045fba 03921249251a2c82b7a1c61d55110f7e5fdb524843a3c07456847ab285675863 f63235430225856def06eda8da527aab93e398399250709f719bde07f47f8f69 35e2f659a83db3d0ec2cf04f9d99a5ee16b47e7d60cf76323e18684b70702768 4cbe840447a0c220376977c74c410d0ee517a405da46edf6b1df30aadb45995c d2da2f5b338f79fe1c3f7e3332c2342bca36803366e7b7db370811d578c200b4 58176ea30e74f7115f9bd3391a86fcbaca46455aaaea957d610b63e5c7011c61 ec88e90dd7f62e99c08b91aa802d337001ea495a7ee41a1fb024a993ec39edd2 1d0be6aecd80d1fef7dbcaef94bb326999309bc1fc368580345adcddab488970 11b7bb403c8cb4d15698f0274967e53b9c4fed42fa5174bf2efb86bbca523ee6 a9b7899bbb3ff8de0f670847636baa2d4de9d209ef87953599e2a3afee28848c 93c79d75f55d50ad0aca6f745a8f34eb27dcfc6fb533dc39451b171458797003 fe168bc1936fdba319cb61c76c079b26e23eb45be7e13088e63a9cdf013ea2a7 419c44835794ccd812ee147bb3b3b67eba65e11ea5f347d52e38bb85f599d946 014a134b1826dc313f382b28a797ed63bc65d56a7c4cb56a1b87c82829060b70 e98d1d2f00e2810f592f874a98d4a5491e3b0095e99cbc3bb81d01ecaaf7977d 34d9d16e5d332d7c09470bfcdf59a07fff3c373f8444cf1e4ce7d36c03616504c1ad6974a5367f88d03bdace1722fad7f952f28433d0cb158100b1a9883a2d010d2c6dc844f0317bb965f8fb94a9a1c273fcb4f6f37ffe23eb46663dd590a3030d9341394337d68bc7a301379542470b8b805a876b207f68c93f069746166e0c6f3eac76847cf5a314c6d5123fff43fe08808804bc7c85efe44789b4fc1d5e0445a8de039d051a535aad402d9c7118b4e4743fdac55cc3b00c4dd2e632db2d0be59ea24b79468e3e82e862bdb727d6964f6c1f05f8cb9444b015505e11cb3001290623ef0baf748e29bf67a54166fe19a794d84fde5b0d1ed34727be6a4f530346e6ff8ee4cace1d8bef61f14ed89599c1378502581902ad628de5fd41308f0714a5bd519188e0502edd116adea754c5559ded69b0fdda271a4d6539ca446a004716fead668f3c117e6815ed9e24fa866a0cd81088799dd94ed8f4831a24280fd8007d5816b9a4c2bf5d15bc66f952c2d51101704df721616b4252cf47c16a0db45cff07b9a50860cda8850a6b650e2e3ba75031ee55207e7acaa6142c02f403d977912add975879b6a0d43eef0dfa5b45b8b959bd665218462048fd78ecd00618f73163d6f67324d0438fa1fec3af69e1e63166305ca7e4189c83757a499900f4cdc09adf62a1673da89df6723d7c593da972228f1343f0477763d601e2df06cb6fe21c31933db0e04e281c640d0ff072b136b362b81234031d95f25a95f00ae5ab903fe0cd168004f5e94f7955d2159b5274c3eafc130aaa64f3e02f1ab2005c7de0e61a5836e649cb4052e08c9f84bc805d2b4d98262796fcc86e46edf10404ac5e6c91c7c76541dfe5cb10325691d1347993167c41e7e21e8e2431a7b406e27ba685f5262a18cd8b56553d05dd60275ecf86d3dd7a296d4d263515d12d0c87b9ef6d9766389ad300ab4fa2f18dec85b0d074bf262ed2bbac9657d1d254081ac3edbc0548fd2c7b1518c0b420957886d9e33997099b3d73fa7ff9dcce5c0e85e55b5354d85f99bbe08a480eae58159f44e9f907ec446d0ee82290d6546f06af5598d4036815b68cace3a2d123f4530383e4638d95c235fb9906b986e40106d12436bc74b0219583436f1a81f008b4c8222284ca906629f1c7a79202f9b403d646be9a83f83116ab4d4b93ff112701cda62d6ec9e757009b0ad99c4ed05e0eb8ec176eb03f714f5b0f90ba130f39456baedc245cd8b76fc97fc5f0e8334e03f62cef588645744a86056cffe18106a1b680be16ac222bf6f7aba8303dda3901a6b6287844ecdbff0a7eb4e34ee0e9c4135d493dbb1819f3d2d390c2f1bd5003233a6bcbab600abc97e7c1b78806332b614a113752f574a446f00c3ab7fe6003655910264e1beb376b4cbf072a34127873004ba6da824ccdcba8f30aeec3ae0f941ed0736353cc9377272d584c26ce9ac28f599899869c202b2c8e31da212306e00bfa5e7b0e4a678cee87b8f5364bbfa20e02bbcbbddcdfd6626fa4e74aa0061b943331a5dc9799fcccc70a23a0de9baa8211a8448d455303b7f738586907091affad2f3472eb385178c0fcb125963784ca8b2c3b78221497e0d1e82ca1e10fd02873b78c09d25f38da7d4f94cbaca6852b2545259ab2906b9b0694041fca0d75fdcda852aadd54a953662320f3215ad78eacb58ad8caa574d95d8c6c3d7b00e5959d4d3096e3464ce2f250300522a50edd1e7c11a9988729af0f59e7d5bf0c5f45678208cf2e1e1ef2c2b588df0ca44980e81684c66d86df7a387bf3f260009975de842278451c784331bd36d031e771522b1606ddf230d298e7f204c1ff0f1ca8ae5b3a10216118957898e7ea0257b734666cc6e6d7105848c4e5028adc039ededda74f9915c2f64d2c42136645c784f3648a768278c9d7557c9d0b39f8037e62d5139ae88695020690b4a99d0613bf083e84504a0086a5dd6b820042d607dfed4ad3500d82583125b2947628d2b404f78ad2b8d7583412eb9e5ad50fd9032b1a8bf74a8a94e59f71441e33754b413a1b302d41ba71af26f8fc91d4dca3078be4ca00186d907219fc2586b9bd1e0046046f4227f8001c3f742ac6fe5eb4037722ea680bf4ff775e2bdc238b4aab4723b7844f0caa7aaf435ea346392e710d053cdcf00c8e21890c2c7db41ad8edd476181926e75eba04820a795af8fe50046e56ef7744b2521f184c4ac7a2583ff93da1e3095502896082943e0390ede90092a9c9fb4fc8e29510bc865a47dec94857b915d29f4ad266d5b368bd4d404209b4809a79fcca24ddbf669877908b769f8c9063eb852d686c3d677ef6e0e98a01179db5f1e53449bf2f050477d48f7850b7c36d66b32b732434e0fd4f06e90808eabb82a38425717018274e8907553632e91d13ed7a619f44ada5d9c019a05209fc05c5396c5de111f4a9868e55d256c4b6fc4e68bfeb24d4599eb378ae1da90c14e174cc7dba46844deee5103d71c48b61bc70a8bfce169c9bae59b187136307d8d606e1e7432ace909aeb1fcf2ac6ad7663eda8f9ccec827149488f611b4a005a4e2ee3f6d77cac4d43872ec84e41055774a46d9b7ed6d5fe29d57e933bdc0d374d8a6c51f84a5c5608d3005aa0872b8ced0ae6ff34b298225fcda0b8eced0e3d72645c0411ae8b2cb05de6625732c03ce77c10904c426d96e7066c139693029838a07951c1c4dbd3ca65ad71f49e472366a5a702f1e516a9c441de46306a080e86c0ad95c9b9046973ec8888e95c3c2bff8290f3b412af81dc40a105d5550d631a0967390f98cd9a2aac7c8b4494a09a4fa606b6fcf5a2f59f2a7ef3f9110ac53cce0df7ccc5b05fe6d4b18b1c9c5186bc1f9d5fdbc81607b5a78e0dba220780b7cec2011b63456dcb2ffc516739d541bf3d14adc0e0e49b9d8c98ea267009f6551c4aa26330cab901b9312fb9a80aaf8a13213974517a0d1b75e55aab25003e1cc86a1c4c0ebdc36573c0d34d85935717086702851cd194efba6d6643200061706e7a35b03ad1220ff14c20195245a3fc76db8b4009bde2ebee086e20990c6ff40a7cec92005b7fc468c756798465d04bd0e44e058b387ff1a034de70d004bc286b98673fcb76d1c4215e3d47a27851c11dfb6b9f39b04da62ce33fc00304570ef93c44cc3324ef025fb9f9a3a8f581fc0e2cef4a82bf788ebe7410a19f0cb18d7bbb163bc4c0a1c98a8f4c3bea3efac9d8d80fd0ff542ef4c4fde43c530e5c2502beed0a648668557d6d0ce9d544acb53ffd74fbc3f01bb0c4ddacf1af07eb088d4b5c12841701239b430818ca8b6dadaddc5eb4cd795ee5e9ee897c12040edcc73e6e18b39aea9c31b71d0edfa9a023253ed021cb30e042c20263e0310d76c91a2a224406ff68bab8c3840b4f365c7aecbfe88cb895984844764ada960903561c0310e8433453ec79e62d4796a85a647bc3bc81a04750f526548b5577064e6551e30c5f5bf9bde85babd02c134180df8a719b991c77c48f9acab1d4a7028a09bc6e0703b4e45b6ec0053fba832f23a291c8b6f4a76c186572621a8ebb07c3659fad7dbd5deac8ed113588e28cec401b3fbb82b9e6f4ebbcdec67c279e090b4c692ff5404c825a963ca95bee3d2a3b92bd1149a4704452d156872e4fc108b00e4ab3e73e3fcf69f8971be6cd355947619b2b2b2512eeb359ac4a39431c0f0253d9bdc70d4b828039f1a39b5ae6fd7b2bd9fc1c1b827b3644767df99d5c00e335358dfee5341bf75f89aa3f02f3ce8fbdcc2c73910961cab686495e166100a77e37da8debaa687ec36c5d3a99997f0cd776436a4df359a864dfdf85f016060afda24d3f06744f356766b82f00a7c0c469e6ebe8a3381ea030e436bf98de0ddc7c7600ec1f50d9dff8951336699bcf6726a0a5a2abe7bea129f9d40fb2670e8f438eb813bf0815482ea9913495678d83b147b81ae023946b4f7d4ad6116805ace9c5843b5f242e9396ee68a11cd16c70aeadb06d496bf9b476eeaa2222c3048075019fdd90eb81b945f031b756018ee6520dbbadc972b36ba93eeda45ff20f5de3f0f8ad473370cfec83033d1302662b720006d6b2abb9282171fcebeee30cf988068bfaf82b48a5ff255a5f8edb925c3f3ca0eadfcdbd61ad210288489201d8cd659d558db9b0be37243b06b1a5519ec4c5da0fa221f47764d611640f6d046825ce783be6938080912daabe84986509f0ec73aaa74fb81473e5602f825e018678333c17ea1f89ec7c7b0addaed600503a63c6501cdcc07b8f9efdd1da2502cf85f08ad681db4fe933525e40bc3811258b0702a800835c050d611a4f2b4c067a3971efb54b890a26076c16a7628a2e44fa508be370e152a05e3196f67f8902173888ebf0ff0131b6c1eff987bffa8fc62d730f53d550fc6d08b5b2eac5e303f1c2c9c2da0710075d4bcfb99d434011cef08f4b2ae50690cc703f8c38e5760df4190ffff43f9afa00beff66e406ad94ee7391cbf6d87e905dbc0994786e2d007841bd62c58687222f39e889e849d8086f57ae0a510dd7d2a1b10fb4fa891a0238262485f9a416a28381e56e9d422d2b6be573d1ccee3c736443adcae8927d0ef035aff91722c4c55548cf22492d9c851bd5006997001ef2f9e7b717f498980ed0d4e41808afc835d80e354dccfc028ca54da99fa3cc5d3872f4103b79871207bd93f8316f79b376771f8fa7e572d7914a5cdbf5e8c8f43141e4cf367077210e7860557d5780092e3b83bc7863452c07162da9770bb0d0ef6cff78b074c0cc03709ca90517d38cdc67cd6da74cf63d25c692ff7b6c14101deda10ebf7c11ca0e2dbe51ea23a3cc91906910bd037e5e8c9d6f319151b957e0fcb428010fcd3b05d98a7b0feefc9e616ab99f751e919367dd66d72388aa3b8b16c0eca1605e630a23b983bc3495468e193d59574f0f21ae2c69137ad5a0fdd1ec64ac251e1fc00b513f46a3acbeb8b03c8a58a401f9926543c1bea1ef8da5bcc733e68e180c8108bedbf8c0c7fa2bd869027d8bcef1c5d5b3c55301773c5fbc778ec789f9727d037228b1c1718a2d3555115b66b8710b0d29d8d5a1674c81dbffaa4dc1590a9d016b95572f514030b4b7bc4ad4993782796b40a4aee6a2de974612ecb9801f75054f99bbc932c249e4fcd06fa0b7a53dad80cd2b135bfd067e23550d3c2e638a0fe867ee41fa4684e3d55d64547853c76b01e42af5771e11b5ba2ed61db3c7d1097d8db7064738e0530c0052c53eb13bdebb90c63d55867129915f515b159115070b4d9cb66869418e3f2dac143eea7dd9ff4d7524fcc29449e9d90ed9f0cfbb00e8ea749bfd10f23622775e9cedc5d65ad924405e6785816eafaea42a4a05620560017acf910ccbf6eed38fe6d4e95b2cd0f3f493f6a5f9b5626c9189a7d2920dde062b0d8f230ce7f2258c1d2cd8196f5bb473a71bac86d4c0b78c6dba59020488710232aa2a9e03061eb39dcfac813bcd4244b5c5ef6663088a10dc8844d20e2086a474540eabc4a461cf597cc336730d7419a2206434dcd1a8208fc86b9a02b6f9422b473386f3fad1824f415ce3c45e948f219c54c146081e52e0fa278e0e291d28a72519e30a92da0f687b2d993c3718b332262f3c4649ccbfab8ff64e0d1c48acf7256918670833da45aea51c882217368c77af9b61658433ed4ba7f70158b91342a53d9cfb4d1644ddd8e7b9fa3b8f7959cd560163c3505123802e6201f2df07c14473a52581d23e1d51613aaa1befb3dfb773a5041c7a195c65125200898c314c0526d812c4e3a77636dc0ce4822a64543e19deff9e63fc3906cc960c8e138d4ccf79de0cae70602e121817d45401244946718c5ae1a785a7b465bd0ea81fc8e91b3db7d1c533f26e8419257bc2abfde9c44b2cb963181e246d416908b986e528bcaad765a0f760545d719135b0f5056f9476e5fe959144e402eaec0b2a081a878641c705d7703ca6a9e1d7876fbbdb70fa1c512809ae16180e2d8506cb1a9d7e3fa47341afeef31f7610778016e9f65cd061ecaa903f6f65b3b4f00e4dd6d30eea30930a13e0f4411e76e9ef2cd12ae784a25da5313fa0a161b5bd0f6f86c163abcc739200cc045e1b44cd147bcfd1105c5be699b89ad549a30df007b9cd0b2a943078d30ec1702e5bf3619ac9761bccb6b43da7194b187e0ead1007222075442663d12b5c0ba7303272246f6b1095aba7acdf959247c892d691c70940447c831b1444d1c89f89d32c4ae4599c039fd117fb8385d47e0aeca05c030e4040fa901e5a6f60f141548dc0e3b3061b4427e14739513f350db7d6819b54011eccc3d39aaf1e6061e4466a2f696a93fb1c533738e3f9411b75d79f31f79001abd75f6bfbf43faa97bd0b383cd168e2cd51ccbfa35ada97fdd5edb0c7a11c0280f658a2c07cc77fc43437106147b8f6ca9747a42adf0bec2435006143756607ba7395e7862284803dbb38b086dc57b552ba72ee8c0c25ae81ec2f8b1796410f87660f7a60b33818c30008bc92b9dce8734ff49577d1914a041e395aa03f600e34aa3facfd6d960c1c67ac05bfb756d8c823282613fbdea17bcfde969c023600bca329ff46f77beafd3fb4eda2cc64a7b7fada6dadbd27e7478c2c69add7c50c91e218678e30677ced3d55d96d193fd95f485db7595b3de25fef6c33efd7a90d25fbada541c4681d00defb21d4d4057cb53fbb9ee4c39a1feec2adacdf4d8b0d5c559f5d6725145b0a44f8a9b0e48d72222fb490f0868b417de957ccac6cd50b5311a4e8e604d2f7c37566c49c499aabe7df8790d4fe3fafc432411980a62e0ee1f674c8f2a90970a3c0efc7bc160d44f4ade7345df7bbf6fc8d7485b83d900620902f91263e534355ccea30a568dd20cde0f4872e56ca33e29f355d976e24014b33bcb3823df0bca79ff3c4bf04fab5d93eb96e4336f76e25b31b96e002cb02054005fb19cb75091d5459249fef93872ac20faffd0b9aa2dfcc42e06b2dee00cb85b940366c5eb31b23cd1f27048c18ba3d2124abaa20c3ef84c59e6caa8007723a8515b1bb071be518022c0a5319bd809d33f77411de9fd5510545c30bae0f57e329326b68e60e83567f1a57ba0a2f4ccd2729aaaf9d11aec5406c6e251f08c86b2e84c10c2f08c9ab0bcd4ef4c20b1da69c4238617da4264223d42d27d502d03a957ae32aa579fc4cd10b8a2610686bb4887372c671e14be937a289a6e70063df903baf32ddaf1d498de5b431b25a211ae4b0a14fbc35cd359c4fca524303e5d823d12a122c0798890e4b54b8368facfb17de3393af0bd5975a147e60a906c9327281bf48418653d867f69835ddd00ad6a9ce2bb9853e67504aa24589a70156483a7d8608ce8a5cb9391d89d53a758eb9fddaf7e91e8aafa78c185c43890f41e3182874f22cbaaa25cdd8b68a69d0eaf56a6093d94250e7d08118a6292d022ead82f9834afb03faa4b881fa099b348c5e72e301068ac8b0eb375445f1d903 false +check_ring_signature b1b269c189f06e9091a019578b82995cc4368ffa049b6ced75c5ee79b7d267c7 d464e81b5cef48e1a90a6f012a95f5eea493014c63c849f79ef404d8924edf07 124 55fb499f12da908cdad140f8c2bb154f3a00578e7fc007e8cde075f44442b9d4 0ba4782ebab464b0824810764ef536e3b99e4c4f5dbee81be2938602d81def8e 261c34f0b6b0dcf520aaa5d1a1c056f56f6152c3f1905b19b42d5e6b8f417932 3ee9b6bd8bae2c66261cd66e5c5dc63b22b4d856e1d726bc40ae9f3e2264de75 5ef18efd3bc68a39bf684a3f7c7e10a94e181a241827dde22c2968383c88899c 07b0fd4f82c832a21d7d9313d8c9239f742592c0e14fbc01824d585670b91e7f b0cda66543fb4f807a79ab231f3310056e1f6702286eaccdc5ab371b0df27def 3285f33bac41e0f031aa7733a3f544b56f9496755993be430f769113ac9c7597 e93530cff7039078e94e62a822f1e14b1c6f7d93b6f2029fc215be8a81d7efd9 eef045fbd97a73267ff83e53b263a701219eab93f3a6e5ee55a7d3826d602e22 21b398b64f97e7cc39cff7754cdbd61a3c958b4aa3384c77b197be66286c97b3 ecfcb44634f6d27995f546b7be860f4e459000ae29cd0dd4d4f0458ba445ac4a 830ac5aaa8ff94925af02dff8a0033ec497758da479d91d09a2ea04135d8a9d9 910172153eacc1ecfc6351b70db996aaa60e1158074ac4b9bb4897ed7fac7915 76141bb3c8fc3663212417a33e720a36f630aab6237e6a23a8e27aad03def9a9 538ec63cb941e78f249d75c1a54fba82e98dc4fbaf791484badde506f96e4daa dced53d01af3ba0404affdada0987e7f1ee922c40405253866733732358125c2 75cb4427434178776a8f26c52f64f20e3e52670af79022180009573171a86c8f 9563df9fed879bdaa1d9325e37e972ada30aaf6e7f4c776587c219b82cd106e8 ca4bb57ef201523b34e383de19edb9a21b63eb1371a7ddc127367269d98d1338 10d4df03bb058c659fc0a055dcdbeec650315f531cb19d0b9b9230a0d1605d63 c41c67f2ffe9b617027be90d9ae90a32063bfc42b15c4687e76010eb9ea92976 ba58ec8e2aa26b1fd41c2f05964739342cbee36329803cb8bd6d3ce77c249b77 47f21959f6f77c1dc330d5af8d0d4943ebf26237fac9d091d99267e15db72762 617b7b164455a0c5a91a4895fb1daf492b84163e8d7486cdd1db3e2e7c0a1a84 a465562b7eba6499f2a90972f8fcaff51a685a46912034f178c7f341855a6025 f1391829856d753dc0ee240c1b314d3e671b6654c23fd4a474627b0a39808dcd ce7a550b8dd76d29f29673d098cfc229118aa861571d0edcab01200d4946c3c3 49e9aac9a0755b9e7a920139dbec668bcc115e52015863af395fcb84a89dd123 02d6a4964daeea5d07be1f4b89cc13df688d0b0d5d8e6b1c52562b400ddb4c08 d329878470831d8d70dcd4aad02d2f06a0071a557135dd849981e9a457286252 e607a41231fe1d530707e0ecda2e8accd5ed688d930cfb1aacfeeadd743491ef c7c75c28fda18f20a5131dac396534d01b9bc2a3ffc39debcac55056c5715e55 6df9b77b71c6f114a07acba9a75f01826d0e9eb25b618dd1ccc6b87e93215f9d 633e42e44c0a6b88bd6c049561ef89c00153a27ac47e69c5db634b22ac555fa2 879bad38a824343739308771c3028030dc4e0d46c7e5c1640362283f00fddf13 e4a29268e246f9e838e3051806ccca9714613dc24e2d3aba3d145473fd330f9f 0ac13d781dd4f9dc026b7680763e3f91786c25f2389be38886d4be1b61bee3fc 543438baf828b819b590edb5f19296a2c90d9a6086cc3c8c49f59d1ed89cdd7a 97b54b88cb662d1fe43349c0eb71a94dccb25c46d1c157a6e401ea713e49dc2d 97cf499fe661ed59f34414bfc65858bca8b4480ec3d73771d3963e3672889686 a0ae6de6f53cf9cfa051cc5cb12cd606f7955fa8efdbe6bce7d92fe04d966f25 cba8190e26af7aa953695fc21b08b4a3961dd7e5e34c48704141d4529e7181cf d129aa1acc7800a6a78278aad1c2a64d78e3e007b692cb7f1de072dc1ad0b036 8c596809278887b3a6102eb2bb7ec0ba6656712ea90a3440afb5e018d7edb681 0b8dced2c76bb52e0c80c7348f3fc6519dea32dcec5776000fffa75f93559789 fdd1b63504998efff9b818d263722ff33e17d9b07c058c55bbb4be595b9fdb73 7f0e1a9fe91624f39ba7a41ccc6606ee1f0c0aa1a97fc29dad86d91ddd717dcb c9caa6dcc0b75824b2245e8222713c5eee283972c10059090c3e330a712b1377 d90d01b0f5cda6777ddd49079dbc705f6632c32c88f6364e948e25589ba46eb2 47aa285545ad7e6a27eae5fb99c798d45c07f27205d7f027b3422b186b4f7f2a 8c334b3a449d7a97c313881d429201cc95300c918e17cf6e32e4b30e9eb243ce 9a5f90f1efe7cceecf7a0914ab1dac935947c53d59c8e18dc93724f244500a76 b86259badd87438ad3dcc9d5fed898ad97ed9797bd84307022732ff4d191e94b 5fa9fcb2ae71ad5289e6e7b9ec3e68ce0df04ebca826d1fa6ff9a06e6df60956 a9920c439c1c1e1a1457b6d41cb1e827015536d0f15ba524e7f9e366bf193dc5 4ad3e8d7853d06017239797faa0db2a1d43b6d63bf439d5f9e88a0a61f6b7957 3cfe69b55ef8fc95a9d2352af7d70a1eaba0485c642de718653056ea32888170 827effd7111aaf18ca23377a7f8a234dfd79aa118d80f8db3e31d8388b0ee292 4723b1edebe1fd7f6b14631e1d0caa7ce72a1f75c8fb65bd90b8da97c9826cd4 5652660f35aa4c725d7b5ee44c722802d70f6b2b299115bc8dfa1b6b69cac21f 948d82b82b0206708781f3a8a1706d81dde1f36c5e7a4033900d21099f8125ed 7b06b2a7a66faf8d185287ef3eb3a1b4033a7c1fce7b56a811c434ecc121661b 867b967b71df6a542887bb3764e3019bbe5c21dc826baad59d4dd7426b3eeabc 0bbc9b2748076bad0c74246f1dcafa20777b46d1d38298d5b0e8dbc0a380d905 daced0315fb34d986e3904fbc58b404b2e896c7cf3551467c799369223131fa7 ee173cf89d3cdc12d0433f24bf1766068d4f236fd69ef806f74a27f1ee4a3f17 885a68f5407ab1d97a504b55111cc7e99e3aa0b93aebeba1fdc3a4c8c4febb0a 42a3a152e66357d47ce8cd636d3c0e0513f0d6d6c078c3bee710d2af6caf9f3c 703bf9581a546fa63cd7214bcc08ca8d3c6991b3814e57a7ebe90595535ccf2c 9b9a094507061deaa8eccc241d855a16a6f7680a9ed4e725af31f406d55a3d33 2322518b28c2a7cedaf022232c25e14abddeb8bfe8644e27791652dfcd77ca8c 92b4404e1336c27467be45efe6ade7a451ba0183bedf96c016721c7857658b11 1cb2ff37ab66a8f815382901e09d6dcb0476ed3ccd8d25259b09aa4c9b4cbd50 adb7c67e89ca5a4a0bd3388c427c94e7a3f8c5aa051d86877cc3d5f5a670a969 e7b83704820f03d1ebbd4f43562784192eb483265fc478453907ab51c57b786c 327e96e764f10ed84bdfe118c662d3120180efcd01bd67dddc4807ecfe6ab3ba 327fe8aebc4be2e4d2b1a427220f86013bbcf08146d55456d6ca71b20f068e45 dc75f8c63393a1e9763e0731a1a2e4f4f658b28f0389b3fba44f6ccdf6d286f6 93a5cf8155c68f0ee15d1964f306ba0944869a0727cd09dd6c6072dfdd728e1f e1ac1aa5eaeb5fb2f1f8d8ba752bf656324e4621ffc47587843f8cfbebf9d73a 8aafcf0d4fe205cb8f2852e0951d164204650646f24a9003bda0173bad70f7b9 3cd10adda37e2807305bea1b3b128905efb278c0ca9eaf46dcd5a3e9667ddc7c 9081d9c157b8982631fe1911c8840b5e5ea64069e03defcb4156b0bf1d63de6f 5a2417de8107083fd3a16fc2dfd52da58d33345f368f106d7411022a0556dd1c 5aa418fad116f4d5bae04dd4f6907ebd0c6d09676f1597cdbd51e72d1f2ecc60 17430e10c89709b9f77cccae458be2da8971b04a00dba2570d6dbb7652a3ebb9 2fbb9009ad48e638ebb08a18984710eee41fd9a3cbe43706efe7bb6d90aea2fe 91fe42a77cbdf82526ecb01d5f14aaa6c5857d8a795434a5bb3d8202206192ae 0040b3e1bdb11f12ceb69758631f5a74fb620bfe664bc0a255840cc89e9b1fc2 1441ade23e30b9b59a1c164d313212960273e272d184331e3b9a5cbbc09b59cf c0cc8cc1ebf557290dc9d235b8ea65bf0ee5d3eb224c5e31009bd72bb561a8dc 1baead9444767a4705d3fabbb60d08aed7c07dc0a63db7e3396a186414271211 430db6e197a361e94298407194c355274f8b2bf00ec2b7812774c7dc62fa27b2 000adda01f5803ed3bac00c449a3eadd1232858f826075d4a62ed78bf101484b 65838b469c2d8f1ecf2a5fb03ef91ceadd3fed3c0848da21d90bff3f0ed186f6 85e5c7ab93fd4c155eea71bca8c87b34a81c932efef60f89d88f64878a8545c2 35c47055582be8a6bef3938e78eeeefdc81ab4f2b2b78358c35c638a1ff7fbbe a89d9b655be6fd46f73cb21eea3887d28bb9f26ced9c94e03fa00214e2c7c73f 583144d479c624a72d4b0157ba678368cf7b51434061337ab0f2cef14826e259 120599693ab16e6e1bb16a445e59d90433d08444adbbad1475fb3e42a87bbba2 93817d54eb50025ea2447baafc050ade9c57649676a3561a2689c4a23fc81a7a a08f09fff6c39f226545bcd300dc9eb2b8e2d2f12d6e6f47056b5f48368d7ec6 6562f0f0788ddc7aba5551b07de90437a71f2388943bce4eb16e04580dc35419 3167f6f89acefd8b57bfafe11f47dbc8e1a37c051bfc062c6d53a623aebbfaeb 34c150aa271d550b33b78ecc461e5ca574d5001dd03db5c490293ecd01961993 cf6c278f86fe0895dfec2ef2a7828696e251cd422a630e99c7e83b2e6f3496ce 82933d5a2dd6f2beb9f07642044dff77753c3eb6650f09515da6aa22353ff8bb c77e14453d4f9690d908d9f546f4132f76b235ad597da59cc4b17d3912f81b6c f2cb067bc5591df91fc2b8d811594e6771815d1b014f02af790106a51ad32b4c be1325c060f4d4c78cdd0d76412e914484d602756273a540d0857e905e67dedd 9de249c6e32be9d32e1959497e3202842d04677ad5de794eed8dc9d1675178e8 b6f820903861282b22a52e5cd6946b9eae0681101f119d46ba1c1f21baeb8f37 313aae2da39b7cdda20131d98d56c93894990653e911c7a13d3b0c016bd4d6ba c3be7cc1325c0bdff02f49b6eb1ab3426a57fe362796601168ce8629c6129051 6e4bf1d0cb430530de0427aeefdcdc0380bd1dc9de36ebbccf5b92148ccd5895 9714c3bf3e141eab031616d0382cd0e87b16e0b63a8e48d2cb8f1e5101b4ff2d 201986afac3d41198fe920cc9fccb3e2f2392a8bbd0703ec4fa3dd41a4c2e37a 5b8a198518b67509a7a9359c0305d603ac490b337c5d1f77c66c292e68aea5a2 0ace644a3a733d999e17ac79fc0370e558d67019e2509d20c10b5e08dff9aa53 7877222ed3136a263c251d58b32a7e9cbd65ecedfb4990cc3c401162d1214f1c 2d965f3b2327213378a9131b3d8d30b0d7004c17afe917763e1ea5aab3ecab37 dfde650e0bb250c73fd860aa8b02f7d9ef6d731485b608247adf727d28bd9845 a5e478c4878734e4305ea83410400d2b5f976585f5e21ea76f4f8b307c8ee9f9 ce65b158b13df61057968d90e4a75c3a12a46f4ade7dec1fa9157d83d01c1a0bc171ac345bcc76e01f3aa2e4e569ac0b9e39b54a67cd3714950298f1f6eee300b2e6b2fba6b309071c42a65b724d7f41a978b7acfe8ec292ad36fc41badcce04b37a0d00167e37673f30a7c01f91fa9d473c2faddb72a2cb0607f3038b439905ca92a2246f8877926b12bdf0688b6d078a2af1a4498ac54b5b11322dda2bc40a1050ea92e32142f427abb432219c7a25f3f57a3f8d80b3daaace4de4ba35a90910b1f45c2bec368bceb0f50da78367d1845f81eaaba59032dd49259b28e44e05ca6d525728be0d9d3408c8293fdc6e051c79b18e37e94d272b4aa4f9dfa7d60887e995cf173e924b1fb35cfed0e2e642793182ceee65a1570dc3207e48391003113f1dd5845909cb64a1db1d3cdcc1108b7c714e2637602a9612b8df86957a02d2444f92a5da19322f42f16c18e1f42d36469995c6c2b6cd646fe74e1a303f037913bb1c6ed360213c6a94ab25f47d69d72e5a5e48c195cd37e7ecfc4181730153d24184df1777115f61215c2062e7caa977d00ee35cea51ca9478b7af107b01a5f1bb4090fb9367cc99a98c18d7865d3bc77d24a5cbc753a45bf4da97ce2107272214597b853c4e80191ab9f2cf3e170c2d896a32ecc0813720fe1419b0830faa9aef9ee8902aba0c93c6f89dabd4ac5246a751d36414c0b41d6edceb7f4e001dcfacffd87a572ab0d107e2daaefb8c2e4b54763682379f235e83c0db4dad0ef97ca086bcc185f3209e731ed3d9a753427a2b23aeafec568fd968765edf140a145f531c36fe817a843cd7f129c7f83aedd6770a62b0cd53a6d8bf8de6db47075625601d5ac0371e754890753de650767131eb402531dfbaa3e41ef01cf9b807c16dc84608137711aaa84f8738903a3f17cb4a3d3ee608bb6e249ae670d6370f723f5bb750960a2d35701cc1bfa59d7c5eea86089405d70db4d9caf4c515be0bdf7710dbf138a58d8e2a941b4dad06079eafd965869c3d2f2535d302fa578c0de871e1bfd20e126196c95fe816d3159da5d738ac91a64f96ffbd68f912ae440f10054e8237bf1df88233d1707e37e2cee325fe117625b22babdd0a7c84bcac03e5975bed09f5f7938620306dbc09bb29927a1658e2eca71f56a52095c257a20bdf3170202883cd43a4d21c4a52c7b4bc14cd8408cb07afd29c2d568a7225b90aa08e1e544bdd23d4b8cb9482b98f0699a273ab6b132d850a85839bc3e232c202fdef50da8037de3479e462867a94ea9883f4f3ee4526c686d30eee8a78b5030c99e68c4335bfadc21319ee24f70b429273cb69f78af326e86f785bb535c3280a6243c18516ba79bd830b95530b3df1556471857885f05cdddabe3cbfe555c10763c390d0e072e31b3a055db4eec800bcc985f77d25cb601fa86830670052a409894c23b34ac0319330c5aae815eac6de269ce3c81b7f2b473cd9daee87d84503485b9b2f517a35615755d130a88062c11ed7d5f0ec97d2aee06af8edc9100e07ae25885b1bf7925ef56296a9516c0ed7512402a470a549cee7ae886a4b654a0ef8d3c36775a76074ceecdebff46c1695212c5b69003271c40c5d4750ca464c0bde631a60c0ec3d0766f9dcd79307843767010deb95bb7dfd4633a6c1bc2cae0a783f22651500f19d4c0c87a4b54e3bb6f2a7b311899a5444b3e1b58721e9c40cdaf4998c408b0670ebc5539c2699b863b0aca9c4e0c6c60bb838c11d8e88a2008ca846e9935ea6ad3e382a1eb5ece4ae1bb852a26e7eda06e8231ce372b59609947ffe7a1519babef41360371d4032c127fff31f4cff691a374a5fd3a722110eb3e3522ac75906a6dd76898e444a7a336c6f21c2453ecf2644a053cbad1147034e7c42547e0b8fb8a6512d462cf64349111d1ed382e582207fe22f04bb0e7a0a695a1c4cf0df04e5577101f37dcf5b7afb1d1cdf3ada0b72922dbfd182391c0d5f42d99962674ceda9ab7b4551657380c874164908486f6991a938eaf173dd055af1de6353f5fbda33d01d071bac17579c04d1609d182a736abca38ca3c548094614d04c2f81af18db083e8ac69cdd0e4bb02d5b04bb8e950b0651874d0db207a836a13f8cc89aa2f64b0e9de0e4e61b9e89db9093396d6fbb8fc3f484845d0f6a91c4caa71f19ce31656494b55ca0c5d1313a050e908215a9db8d359b041902f6f2ebde26c83dc853616d79fdc7f27c156ceb59e3549e15d5e4db48913ac203764ca6db66e5198a88eed55f5b22cc7a5920560b021f789c716412cf8cf1da09fd11dcf986a285bc6f7a2ad732bcc0ad591698014a3932cd5b1c103ddd11ee0d2aad91e9398850ad09e30870d67726ce48cefdbf0084dd493ade45fcbee022086b8b8c12d2b02c61f7f062e6bb1497adccd628bcf52e4b1b98272407b17e880347f5473fa284eed6cf48fac866e701133b4ce72d3ffbd3df5cfabdb5f1aad602ff59f8a2f96e42134b5a84fe66fe914bf2a1fc0b687021bd6142e451ee64be077f5e14d2625cd9eb28b239ed49601f3531037e4b37b2360a57e72b8a48824c03f780b0c9fce19f15005ec45034dd22b95af48cd7f1e307c6bd56753c2c982500fcf1d823d6a79d009cd65c6a90cdab43e44dfec87c9b3efe7b33842cdda81c0be9b14ef25310d0fcd9bbccb327d17f17b3833e6acd50b9ef9fbac0ffdf8e7c0ca9f267b202ca8a88a4284c42a6e058cb0e4bf2a96de4b89d4ddde753e609de0dc012172190f69eff451d32e4e46e2cb4d392cbf24d1f1c265037f2d73755f507202d9f1e702dbfd1f89490fe7367ce9543baa230d4ae2022df53a1bacfffaa09514befa5230c7cc54da322a13d13116882a14264ad119276eb7315f448b2230756224aef010777660d4f80ecdfae2e94a33e4fb008384e415f3ad5540d638f00662b843ebbbac1a2a165e0bff35b9c49300a8e3a150e2c111ebf92787d35260f0ec2404261d15c15df64431a634a5085594fe1a94a6b91a7de77c4c8fbee820b96f844c665cb4f82d9b2a1ebeddd7d7643c3028385fb433102e2ce7f20d5bf02b45bca72153f5a749f51d0bbde2897c0b52af7872e42f8ac28e3fe5dcc67b806848b232aa21ee5df37654460f6a55b6b54f01c728229ad9a20b3422d22562d0cfc142fb91ebf094ad33b1b70594ded44a1ed022399da87fb25c206b7e214cf03606a43e300cd52018b1ec24bb2fb84cc9f5d9d7b60412b2578ae9bc2ce23c607f69bbb73130df37e3287af471c6c847ab1fe7248722ef3899c6f8b2bd9d50f0a43006fb6c6b719ea5e92b3e8ab2e198cf37002831201ed5127e23d4bcaab6702593f970db4475b97ecc02d5760f2380c375c68dbc0dd327443ea734f1d4dc800d62d1aaadbb7815c291cfc30df8f3a1d186159dbc10016a8b8f8faf674d1480c48110bc8ce8341b84fe88ff18a5685c7a9cde3f85614a97095c02d8500b2bf0e885e1cb4b6ba1fc4bd3161132b81ed37b813cc9f55e4362b163d5c99168eab0cc9fae84ba211b45fcb7f64113c5879c87620599d05bfc0a4121dcdfd95a20201df3dc022aa0d75433bc88b45786ce1a9806552fed6c3ba968aa81e1ec6b1ba0cfea80bc29e90cefe3e025ea726617dfbc58060e737a2bcb7a80f4a8c419142057610f4b064ac3149a883fb1a6e3d9e76f6d95d6936ffdb97f54f20475b0ae106cfc9118d53ff35271a0ca8706e606e62792ad835498f5c11b38790ef6bb5ca0279cc35cccf800ecec3ae61016a9f02e0267df1c7fae667ccebe9b4908f2d2b08f07bba6f8d7ab5442042ec30cec1b38314b3f27c8d4f0a4b806311bf0949760f8c0417e2349d648a7703885419afdb91c55ef283093faf0ed492ed9233b7a70286ac3c852a7ee24020b3c0374832762b9c21458e6c3c36c40395e69ce183c207761e44f05ad489709c01a67db65365848da90644520a8d12c15e4134e7903c06d86db0671adfbff6cfd915fc204969fbbdea8ff61ccc238378ef385f6e3a3f0d2fba8f3a2d69337ffaaafb339dc15ae4c99a9a7557f91b7a24fdccff9df4d70b89833553cf33ae51d17a756dc04cd860e1bf54c0d205293f2fdabe863841cc023c9b30e8abdccd19c8ec1a763dd073233bedc1f111e9fe48cb6746e2d0e3ea0d9d388d7de8fd3e2c8a307f9c01a4df07e620003befddb1130013550fb030890010f1155ec61a0275c2fa1964c591a68ea56f4b20ae1ef024f8bd1b6eca472c0f8c3ac9ad7a39fdcba1739ed34d711648903650bda547bc14df4eda8700933b0138008497ad984d942568d53e793349f70daff22ce6593bde29a3b9e03d69190867a6715790c4fc3cfb7d40026f5957b9a9dc139c4b66cb84ed8974319f2c6e064727821379be53300e6de003bfaa7e9e6d3d1befe8b7e957fa63c3326750050065ed7111828ae6a69a36d7ea7bd2f747caec74b40a6f093cab349d0c0ba5f002acd7143c2f6b9326030ca10561bc885fb98d860ddfdeb0298368e960b399a508d4109db3b18e61af8dd7915f694b2c571b87f9a1831c91ca79f3381405a8c300a845ad433e90c5fa83166f11958dfd473ff6f52884ac49dc6e03562fdb8999023d2bd5a171a99fe6493dc914d5cebb8593e1173bb7caf6762495f8ffeba46a01721b844e2152bcb76047791f5bf87edf0bb1d967a231c33f2a40036e34d9660caf6a14e83a72e2d73737fe59420175dc4bd51477bb9ef750bacabce980d1c30e5ba392c4b8587e1676b72ad27a6c77020d610657b76f9426b473d7f9231aa40bc6f4eb52eb58a7b8c7e0d7d13fe7fc6c83d4a3d404f964d44784230162007d03fcb5356a5cc1a45eb7185194b9fafbb6f076ef5b8636434168ea76cc0f689e0155a55172bb898c19f90cc092b4f8e6eea0126fecee0b66bf8f6c866d6d476c081c6d58790fb36803f56e3d46fb21f301e70bb8bfa3abf6b9e432f83ed6645a0e6dc314cfba48eaf57feaf1a8ca43dd4975250fa97d74610582f16e567e10410cffee8223b9fbe97f3b70d2a129db43b0c10fb4376da768314f924b3de76b6a029b53cbd389ec4368a9844063cd7241342c079330a729820332deb01476c22705926859d072731e77a199fefb8e4be4f6a239dcd9013a49ff948abf061d69330a07c1dd3af35ef06dac43dea96c3254f22abdf0417c41ef94f0b7b10b7e71cb0e4eed14065ca28aeeb5049185ec1c8d6abd46b46e2ec9c56da90d8e78ef005a0f6c201bdc14e941befa457f7b946f49f2a7c965654d43e32341f192d028a5e10eb94a3d08b3ca6fc8f19df88d9df4f8a94579f44693766eb3d1aabcfc01935204ada6ed44fbed3aedadd61e40b008007a92ec62971656e238ebc01dbdc393d7005425c86fc14f2da27023e67dd448113bf1d618fa11cd55f42676aa5e7026cb08df0bc9fe5a561dcb7dd8813c4cb7d9d5e5a17380a843631b77384ed10f250c0ee72ff5ff10dd59fbc3075064c5e59ff60578f56eda4a8469981cb8beb6b51700718ff12186e970fc7f7bd2cfcbe5cb4ec05f23ef1d41c2b0b033bc0e6615d10788eae1d7de6131a8b6bfbeb30664d1d8000dca8285c792d37d7b0fcf5cac9d0e48bbacbfbca878c51cacade77c87274f56245536ac0c4a0c12d3d8874a78450535cb71553bd4f81bf55bcbb471dd2593443134be3bf0f3d1dc6a7453046dc40d57e1762846ab105e6d68539abbd0dced702d0c353635f1e8cf7287ff063c5702236d6223397d35f19d7b75774bb109d7d8f3e7ea8e815872e4c05b3b3e9bc60050deec2cc55f7ae4d29a0962cf454f1887449e0b451a8c8f06034c4f7477340f5fc28c7d7ed5fd5eba4c41a83ddd9726fe0fa03fadbc8350b5bb368577aaf30313216a9c1cbbe091bd480e2fd91e3e640a2b3d909596c5952cdc7c74206f910bc35f6f0eb722a9ce0e38c92e448e167431fe82954698b6f3a5930221a79a7b0aa9a6a50150b2ee66dbbf45d28b5f06be932b221061722b0e9388e572d2ef5508b03a3001d8fb3a3409b5ecdfc6c1b8fd5fe8231c25032b9ff82c6c2a39396d0e91890cd0e14f0ca94a854d1f86378520de9bc326a79a414a2ef0861e5c20cc020438bde7923f54827cf647867ef35e46701f484cec7d37ee162dd58e91ad160fdcf5422e77504ff375e7a84e556c1401bb8ef1e12270908b5272c260cccd4f03e9e01278b8341b6035c541acbb700912204a0401355426c3ebc0725dce3f710cb61a1d75ecf2f70eb9f2c3cf4379934f06b68636f1f77a2433a64d14994bc5036a80da9594ab9a08faed507d0f63d1a589b686ff44a87c082eb94316d977fc0111a1aa851f3e602b604a1bd8b92575fd0a893871caf5eb89d94931aa01aa4209a33c018f669ad5b87b6a4777b0ccca30f86a23118142e58a58a6602b2962db098c48010f04e0174eb01fbf9609f9f7d765ca117cb3552cd4a1e44e8355f0560b44ee0e2e4fe66c4da8a1d9810855e506f8d919d6d605954fa2a29484581dfe0d4439193df5fba7579a37b5de7b484aa8604c23098b178014acbded520c069b02690359abc08377908b72c5e64b02a2acc81021f0fe585d484b3b09f52516dd096325b2ee40bb7ca99baaeb126f7c3b07e296cf5cdfffeb7884026e9b23abe6090f7df397f51f34484a7cbdacd4617216050b11e2e66053168292a1d0ad663e06355f95c87e221b5e25078d8f4c4edade2da0a9277f29f3b0e4f058bf3faeeb0d577ddaff0239d6a3a476f8c8df813fafb7e8c137a92774eb8e1fa12f7d01d10edddc9d39da90aee34da92767e00aafdb5eb7f52720d5423df2cf42507f147e05486ac3b313886c9de15e9f05b31dedf5b0a67efbfb84637f01acaf497e15c90c410f29757f1b96caa8ca6c3020f782cb0af63454903d5d021a82eaf87cd37e0098d08c43bfba51cc556098366870b3ea64b49fab9cb9f6d1dab651b390217a09b9170fbd21f8d6cde91c0b07e525677b3b2d441ba7452b347ff28934fc67450a3a5655e936f178598391444ba5ca8bf859d56ad5335301939f1ba8756075700f9c8a5c48e30c1680fa97ca9f1ccb83407f5ca1ca6a84682f38825e85a0800102591ec3b7c917da0b6abd4ab82f9e1a34b6b043040bcb77cc1b7fb0a3a5159f044b23392a85e444445904ac0cf17fa57c37f212d4627a3da38679a5eec1e2f50f0ff84146a47b2fed8db1f4266ace508a923fe46fbdd4ddc0b693c3a65b5a62080f6f0a120c696c3a962227c43cc2aaae3597be98ebd36c4e696e7888b5f01c0eff3992ceaadba9883e15d2bd9ff786d2d025e1f69045ed49e4406b94e2af8a0bb4afc0061a30fc88c9fd9b7dfe57106809e108b4a45558d9a822a95c51056707f6999d41fe956dc2362af7d00fc89d64e64fdfe5e5e2b0c28b3d3dfce4560c0c59e1c1589db5fdee1ca733d298c557fd83a6f53b68542e14d928b13696c7b30b0964ba51e94dbbac67df57cb7bddc3530435b255658891a79eefcc998fcc74099b39671eb1e05407f0adca45163686a577b9e5659842bb80baa6df7c28e18308d8103874759176c593a081eebf9470166227546cb2d27f46dd2219bc2df02a013abecf8a4da477bf214be7d2eccaedc0b7800dc6f73bd7bccc07bef2d9aeec0767f985f8262e4be2a1ea8dc8a4cada42d8491e6fa1f7811fe90165fa0e7c9300e3acdbbb3c3ad221a33871a3fa2367fda54aba63e85e643a60e2df2bc7fe8909d10545c17c979d302856f742f93c6c95ceaa222390b19250b86360bc09faff0782f0f287872a4e908065c9e46393111b05cd18345d051e31b1396d50bd43da0fcae0d543585454b099110c2c51581c510a41042f8c9a3ecc8312449809c17b07aa14d435ad62a89345cae4429d6055dc792c84b313de0f96e57e587a320ce107bdd52fb4cf819c1f704734e9a387fe8c9ab0c8725981b0334c8b95790bd8b30c4685464811a09cad53586f042ab5b5a29f1f2e56e42f7bcbe192caaece078d050ccdb0bb1c2c86d9ea74554e611dd570b296dd2f014e87f4735dfeb6518a8801263d359a86d9b3568c65584efaa0cfcf6fd07c9de6dc6ba05704e91aa545940357c556423504bfea5171addcd0a1f7a63f0d0344988f82f084bbc352a2641506b953a2dea687f164b3bffba7f4d8cb367e85d063ebacfde383fd60a607af3906a045fad998ce9cbef6c7736db04226bea099e397bfb4facb9a0dc4fc0cc8e605d15b90e3839f1bf1ba76ba7f4fdd2dc76e540973a6a63e146acfa91a4ac5ed007dd76a4d314e523bdacb4d6f1af897157716bd0abfc02dcbc10019ab5099e40d3aaf9411d071612fad8934cbed9cfe87093714e8b4b89bb44b483a086f97dd014edcdb070739fe820ba4f3a5a585a3a59eb1d1fcd38941d455474cda2be39a0be405018a2afcbbe2cd92c091e4fc3408e4a0638c38fdc0bfc88cede019875a083c0397fabf4c0e393e383632a241e4058b6ef8e1b0f4b805fa86ada73c7cea00b390bb2128b6fd0cbc4959903fcc8d38bc7f60bec011ddb7abd7bd57f2f05b004115c885d3d3f504a8ed4764305527d25e94ea5e80d2a43de4485f6b7726510cc9af52bab8cec4a29d4236d182c7e133b673d753550d502e2358c8dabc52d404a9b0cf27ca753169d365cc3ccecac63adb31ae134244df8952372ddc8d5ab7045d494045235d4384de5a3692f549a305535963e51bc49779bdb0ef5f177ea7097dc052ca51d2a9efa565049021985f5a28239f61bf048d87bcdddc888fcbd10cc4f01f296f9912dfa6ffc77fc120e9a8e1bc1e03df98d898e75e4b36c6b5d90461d45f2fd65c0ea11b5ac6568c2ca0981cbb1964b0aa5d28574e11113350ef0feddbacce6fc155a24a9dc9bafafd40c203fd2c55ce5b7a606cb755c08c818c0fc4ee497668429553c4edc577d184361ce645463ffb107222f5acf7f04103630828e0801796949844ccde3e36f20122f73bd150a4aa70c3551e206cdfc17738094ae2667e8bc1b0cfb6d7b2c73c25cb456aecbfd6c6d80e95e278db4d877d460c3f88d44a3471de1b7c0bd6ea2b0eb637497dc9df897ef6b865e0b345605668062e1baf427000712160abf133834700f3e3181e7e9cac148de1fd745e2809ea0afa2ab2c35612d3bf3a1f71d446f5e185e0b49336a21d04dfec3dd70347ec7b081ee8923b02d38fa855c32eeb4b5af226ec4f7f336e4c2c4be9741c2cc25e170cf4ef204bc647826b52fbb7b15aec10c6d7b858fb2dbb60ae858938583becfb00002a06dbafb824a02430dcf3c24903bad831fc3eaec9a5927e206682f83c430872d56cfa8275a297e4ed1c50ff689bfbb60bc22b97eaa48cab2b5ec89ec6e00c87fe2372431065df553def0f206ada3af3e6e63caba3b819a1b408c472fdea03927b0aa19c20bab5d7b66c4343927b22af65bb08403a5ea5b8bc799c009fd50944d6fe0ba168a93f3e52e9b14bb8255489711ed5e75ad1990dd9b31c4a8f59022cabc26c067d58fc3f908a7e7553af6d0a606c75eedd0f7dec7b2c7a5477890a32fe2b9bb95e23d3a7c858fb59a6f8e42e0ed4a6b665b065448ca59d417ddc00930eb33040a7eea8308377248379c480fee9fb76092ed77a3033b514b9e9440926d408687756a293e705b6ce9976fea0ebc1af6cf2226f3f045fe4686780290998bcaff714435741e1705f94aa1ee4f61f5713c2ce1ca4203c18e167f4a0ad0ad3a8e8551dedb65e8f780d5c135b9becf6b90ba3116d791a9171b8ce4668690722b49a19a820b40693bb32d15d405c064b5c62165b2c8e8197daa30054235d0dbabfcb497ae4b048091df16fcdfaa6ca831c0b9fdb8589a70f196ea928eddf098274a24ebed46a3678d6fdb06530e5eff2866d87be1933c373373acc62cd4f023d7a86be723048253459670dea410473e960361c63c75dbe6b4c00bc832ee004f49ca446164d502f33b2886b9a3e3f76f3dda13666617eb5e98706187ae6ee0e856385747bfe3e18fd5843c0cd245978edf828bf7542dfe3bd2a5652d226c309f61b4503f617db33939888f7627602bf20e0c18b10429e3ee466a0339b9697097d420b80fd1a637e2fd6a4976d6d4058944ff3e001de0c5033f016eb4313520919fe01967536a72763e59deff5c43637f02c068234395b003ea607c0779e760c8e497bb776792b52b4eb86e642810c07416372b24f5b48c417d4d13cb7ee2b0f8daac42aad61f9dbad5333bb21e6e7d018345a1c45f570cd1bffbd55c7c36e072cffb2fe2a6461e6005be2fa78da327365dda5d0c74bc9aca3710788f79e9a0d49f7e6ad43256ab2794d339e4bafe7382528014280a1312c8e9ee41f00625f020b105a838d6175fbe802633834ac62abc7b589a5d16d2bf4c9984f1f75098d0af591ff70e38c524fd230eaed7d7d54d38e74d495361b422d268151a6b00423086afdae1ea7a55a10f25f64934f4d5fb74e9a70444df03629dd0b66ad0a212707235b352fdba5422cfef04fb5f1dd3009f7b6cf9d3ba7a38947e94f00a4122e0892abd4ad48d0a366ce93772d6ef98d22ac360a84dd35ce402063a6a08809ba0723383ecdcd9ef45a8f777c4a5fe7fe5d78f7d6dbc76cdc0b73cf725cea41e2099697a447a53cbb55f4e76b5e809eb8c88ad8e5dadc38ef09943ce1e70198d801b12b314d6f771a6c5383d8bb371eacb12f69fe3724de58599920cd6feef9710a5a2e8eb8a2eb8cd84b20f135fbb62a68309627894a68b0bfc29152316cabb106dac16308ed923b40588d1f5e355c4f4fdf56ba13ae66baaa10d22f1def4560021035d6f9b2e78f3011a3b5d22bffd210584d3796fa0cee040d889d6958af8c0edf2011e8827e88d5f203013bced7c4b486522acdde16cfc238cd0b29db39ce077ccadf4a8efa35863cec05bb5268cfcccb4c7259bfa716edb839cdb345f5bc06cce1647ca616fb0aaa9cef5c4414cd722f3e5025bf30e87763bae31e608d760f3e4bd352a728b8822484459882027949d838c2ed086f9db5f0fbadf6df33b10a90680795ab8f4ac7e2ccf4fa290dd183b492eba04a61f484778c3736b2752a03f426c8d48833ff9a4168a0ccf739cbad4c443a296b1d94187cf6d020c37de9072ef2365ae4a5722a06da5ac101577cc1db69d976d96d701424b2ee7ab93c010812af15ea8c203c16ae46b6417f793fd2cb95bac1868509e684f1ce7fd104a701 true +check_ring_signature 86af746b3c315dd5337ad13f99269724b78d2591ab141c0dafce0652d2e8fcc5 59a32b12da32fe55e553fefa3561024d622252b15abb6baffa277901ca99c99f 1 112543b542bb5207d42fb8ffe33d529e57ae2e77759fff2bfbf810d0c0585633 166e73bb012d4dbf789e4aef7ed52d8c5f904d95315e54c83f13a0c7e8bb430449a3fc6ef01cd5726a02ce4aee5cce563fdabf38fabc9a5b6de9c9babc8f6203 true +check_ring_signature 551eabd77698205afe74da57f92c7bb8e842b67c754fc1445f182131f022f1a9 8e5f4553ae3201764ea27f790cfb8a8b59a76f8f816eed86d51a5964f8c19a61 8 bae1b7f6a9358d30eff56dddaf7598a15b8a8eb20c4636fcbded5abe6590b64c ff32461b959412abed65632a12cbb9f119199c7a16639e91ddf7b416e4ea305e 766477cd79014d9c971453445335ef1a8ddfb42e5a170b4d0edf8bde23accf1b 0cd69bb682916135344f6ae239ee11e3168863482601ab4fe8699a70089f019d f8b44bbd282e6958e858e5fc169294d984899dfb85814fcb52da24316bfdfcdc e0774904a07e1fea88f8ac60d354999280c881d5eed7b2eded709dfd16566539 6fc91c6dd3f462636a09765691c97961f8810c0fa7cb64c8a106c1b71e18d835 99f1d61773914199749a07c727d3364577ca3dfe6f1aeca29b907185b8319b05 41aceeaca35d63dc4908b785caef1f02c253b9927bb276bbd948a9f57e001401256f91b80dca180c0dc43bcde688431c8f7fa55e43d5710d734f874060530d0c4d7a078402105e54dd6e861e73622839c5d04649117b57c031fdc502ea812a01eb2f463377f1f087a7a2f968b376e2d409afe2b7654914a8b3c492a547a0bc0d34ae53e843d039d46a751bbdf0e4264bb7f38024f22106540889116cf4583406845ac9e8f252da44f82a9640c894ee8724e8b5a1a26cbfd33be74e7f61ec9a0da57e4f56c270dd835ccdfe2e96a4b61aa913654e54042d19030389401a1004010fadec67663fb031c817b77e863ffbe595ded6d7c5bb1518c80635e78bc50c0fca4845a1f8024633d1a4b38e032e0e4ddd6ce6b32b80c01cff44a2530183570afb7e055ea8371a84e0871b18989fbabb84a3690b86a80a622891db4eb7f0070aac32f6bf3c30b0af7c0f2a669344505907feac98830a1961128379dcc51b920a2446faeb87b8a8bcc855237450a7664eba7ffe185b0b9438d0ca289f78331e070584d9551ff555c5347d5c035368e664bba7edcdd3b05b4ddd07322d2fd263ac3c79b163b08cce1234d77ab3a5f23357ba386b49ccf978868c0a2b75a736c60c09236e43d164a1747d201d7df28ce8a5b5073efdb45e1eb59061960e5a18040c1072cb91d76570bb45f44602c119dcfe8b1f877d6d1926bfbc7b86d44fdb970c false +check_ring_signature 887816579ab30f385354da3fed84805456200e01f7285fa466adc118fb11e922 572247cb076547f63af599f31f334a87f09130ac6b743c8a2d9d8b466cfda1b0 3 675a35d51c8c09c4899d49d71406e9b8c0af45b0b3ba38e7fae6957e95765be9 264516541b489c876fce98bffacaf9f47f5857034b15d9325f6f29b1d40c4682 104424a142657ff8d9a6d1c1e400af76317049d3ad905c83925768c41df67baf 36b61c9f788c972a1bea28abb069e45f7969793b059d759f842bb89c91be137630075e3572bf9e71958b200bdcbfc95868a24d2e546a468b6d621cab33f6a9013bf15c0be6edcad77df12db83ff8f611358c8ac83488da9ad1982e83c700e30d9e4f6c19246353847fb325c724c07a776be405f257aecc353161412cb06760023fab1bea64e51809d7a59bafc7d817e981caca569e1f288a1282774cde68550cf433c319291118ca5c7618c6f55365bb3d1bfac2a311978163c0db3e6d752d08 false +check_ring_signature 1e3a64417dc488239ea367485cd6f66d4b0361486d178007b943986012cba8d7 7e189455136f43d11118b805423285ce75d640147916a5b145b299b14561f3c6 1 7db99af8341c73ee460ba417acf285797cb11bc594ebf57145f300b16b796a6e bcb803eefdf9b46cbbab8a85da1f33179945ac9e15d0fa8e53a2b15bb177a90c9d83554ad3a8f0a7080452e209d4aaa363990d531536b64dbfbb1ebc22525807 false +check_ring_signature 1d9f2db1535572a15e93e01bd4375f142b2cb76dba58d89b2a95a35103bebae0 8c9492de2dca30078328f950e1edd126c1bdb266e2f319e63d789fce9e66c813 1 fbef33914e01854838c5ee1ea533ab7eeab3cd5173fe3aedc788219eda199fac 783f6aa381b77035f257e76727136d21d14f2de82d49f4d3fce2088462017a03016217981d8235bddfa386c066506ce13a6bf8a625727a2a386d32854ba2ce0b false +check_ring_signature 087dc39c07c7daa469ae90b29ef2382643d3445cd96e093d0f519f46544521e5 8a402b61815ecf37b7a86e2770599170480d19a02a5d28bf1f4d4965545748f7 7 f292eeb2343d56bc889818a3d508c0aa76d2e50c2e08fbc8a2a766b50ea44b04 b7447e1ed4862092139c94fd594d74c0d5aca7a8135b9298fe08380a0a240aa3 8941aec4c468bd7697556f15350d30affca966c4c21677fdfbed4b6307c7bf1a ec8ed7cceda99dd3604c6678c103be67eda71b05e66db752644faec28f8e5aae c627a073e076633f2d920e8f85eb31137f3a4ce8789bfb7cbb44c2d5616fafc6 7885b3b4515e1e6ff285396c00f69208b2150fe0acdc3d99b4a28eb249faa7b0 8c372258420c15312dfe9a8109259b169a0be59cf47181af67135ca1356f55c8 bc54e248408cad1f67807ded0fb97c97f9f6902aa94f81e14cb39e63bfaf32052b66a00bc234d245e765c0e411b4f5707c95e8c5bf902e9138ca2a297233e70054581006a007b5ef88b0891c4f19c54817cf7fdee8a29e013b2e52c46223130d3721e1af983fd800bc4fabce814f9117e183d4b65242d558357e5074ec2b0009410c1da4f9bef4cc110dca22549e080554a6d25dceb7ecf19a4ec0cdb95b26089f1460e896332014639aa2340aa8c8292877eb2d0a5bdb6eddef86d1bde70b0267dac8f8f0a0b7bf2f8f883c9d0bd9022efe146ca7b26e929d1aeb519ebc7c007a6b56a8ca008e818869317919b760704b51a09f98bff3dbd632157b2a35ec0048f57d3963361629ae5c34cf9332ba60b60ac1cfa7454f7c0ccb94eb0e48a506f6418b3d30e2c368645c02b24a56be19e616b2f4a617c0c7662f654be9d5e2018d1dacde9c4d069d1789ca3b7c03c03a4cc1b9bc6ea9198680a1014670b5bf0b33d2b5ebbe8ff9d18f5d7a763d35a10e4ebb2b24c7d88410e5be4a6c0e5c0801bd63e95a58fc396f58efeb938769ae79cc9f7a398b147c11d7d1b5b62cfed10c84a9a44c0351d0c20931c7399fe4900b21916a7e2c94854e1aadd09a2015a864 false +check_ring_signature f9751f3a1808d1f79ee95cd9a50d7f0af8752d72faaaae6b7e430d8a11308b8e 1c40d7cbeae1a95100edca34c9d9c3a698ae0441428ef5c2b74cec1d9787b244 1 1640a0b55d67857e6718b37a66400006c61ba7cac8f845d3a658bb61edf5e888 f89d054d1051a4a319324623ddfd4dee467d184c11530f81588e84706984c405a20dad32f3b9ee1b54cb8b7c430d3d9a453179d279330c37f70d3312f00a01bd false +check_ring_signature 9beb6c1154ed9f94c146e84804f53528807067a3c3d3cdd213ff430dca6e9f6b 42a72442c75fbc7ef5a26c0b2574e06ba80deb49c180cdf033955da182fee064 15 244b69a3472d5f0598e24c9fd97cd8bf0c8236b17fff3fb1aeddb215df94567c 62604440b426eaf38733196c9b3a3a8ef76da154da7a92b0c750335c05c44e84 6e9cfe3925ab7f2c7d7822adfe6a1455ac5e908b124a3e3068cb16d51f462b52 1c9bffaaf5160bb5a21aaaa572c81a989a2aa534da59bd55f3be88250963776c e36ddff89fd72c4f17245243634e9bb758273230d88a00115da9ce5a7f9ab6c1 e1f720f6c43eaa9462db8310f1cf6bd452a34673513682cfeb17525821292c1b 12c76230697812416b5bb72e550ac858bd54c83d2cabc49d04bcc38f59afafff 41adb8d6d57f5d0c9abc4f7dfac677209f8bb2e9e3ff90d3f97ee38542ab4c98 401de0167d21a727e0171a3547ae075eb49bb179e16ce6ab6a9515ea4d881e1a 9375ec5a0996ccd922a6a2034ee62d75a672a56c32a71fa04b3da52d76139a2d e7949b109404561ed70999d5e17dd8fe437e45568e044a503ec609d40a1a7db0 afaedeaa9573da3e8785df4d8ad3ec499aa60e07d4c48c580a0565810a739845 fd7f5c8a04bc2c6bd19123a08a9bd6180be6fac7e85faaab273387a64fc6cac1 672df9fe9abacdc3e22f8d6878912cca19a63a6dffd460e6196cbc23053aa9b8 47f4f01fe578d18c4fab5618a1de21d1941489455b45c79ef48f12a7afce140f e92a58e91cecca277f317ff3d2e05dd01e31c8d7d456456945f834a7b018430ec7b0a667d409c91ac7336ee1000a6f5b8e78b8a236de4ad703fa4603f56dd902bb002c9d684fcd1b7c42c3a7351b92984fe943de5f745cad59c14f31822d4902f6e72eb47a2ab7d9549a593f534a54376d9ff0d46eabd7e37e94a1353e06c40f41177c2af0d2ae8d4424f189e2dbc8cf170fa0f896692d7fffba74c9b0662a0eb20775331cd803db5a22896418eaf09696654b18c72112403a8b67fd2b33f80fe89ad2c9ee219163496ccbefc81654e5e52dc4e966c8c49fbde7a11477cf908873516b82a7a398c24176bf0c06e4083138099bb6b9f53b2beea9ee8ee04f2209c1b1c14538f9fa6ed1efe2a29533c5bfec9fad841cd807df3cc56ea9b869750a74aeb3f4a1698783cc54214c5a4b38aa400055d939517e067ff9240762db42088898affdf8240a4dff14c00d1c23fee97ea9190d41ad66117b803fd0c46b5a0bc1eeeaa80acf13c3b29eddaa36287e70cc634f0060f0fa044c14f2b4a732660f6278af907970712f560925e15e5ae4d72affb36ede31b6c5c4ff67a7a0b8ca0284954a963ec2596ae505fd18c484d43ff5ae064d7cc91911060c74b88078d80097626265affae7bbaa78626f7cba245575fc3a1c593c2b9caf5b948de6a50909738cc3c4d4b5ad05d1c59d8ea9ce88f887ec4dc3413ef1088279422bbab22e07d1e26f22228ed6a22400107233540c5454b0e265ae388821f0024bf14422a60182b2bf2535e32a6fe1e00528ca03b6ab3443767f7581ec5cec0bf336a82ccd0299eaeacd20e7db122c4700466132ca9cdfd01edb2b04492ea4bc22d33d2e4865972dcc34bb8cba3d679c402c104c4f2d15618465a95e3fbc34d2ddbd5f1281093a5bbb1d3d43e098663775fdc01eebd25d275c8c38c7b55685ee8e7ed94b8b0ac1f8cb78d6361ee109135b0959c08e196472f6c0b007676509088899e214cd041b252abd61990aa8990afb93492ccf3622785fdfc84951f5f5ca216c97b8140365f593cbf3585302d4f76ccfd880b9c9168bce69e1c3acd1f8796ce16701f201a71d57c3e822a0f0c2cb1cd77631b3373925a8fbb7dde20a9aa9c0bb51cf750e10d596cf0eb6da07b294f84fc1eda8ae294a05584c8c8130f2bae3e01e4b46092f0447279268032e19f145a142a253ed26ea790b20d00aded5e759ba2c0d540aabb2d85d1daed6e5795cc66cb35a77fc66e94c46b7d1ff3725cba0dbb4505501ca5f90f0380fe27cabd0dc8b14ea049c2b5e90d2ec393d27142b747ee209cec6f8b2f3bf86d8130396bba9ebb4a8d624dfdebb4e692c2199e635a1c86f43a208 false +check_ring_signature ff938c6a8a67330dc3e13bfb7f5eca51d837511a2af8f2b3cda5a096a5d9701c 45f6cc71786155accb3f8f4f5e737ab7cc1dbf427381a6626347f85d8a9aa9c2 2 19fafba4b19f6b78fa7115403373d05464e1147e99f43047651dcf0e31bcde9e 7b06f9600248395870f3aae2155838085abdba7ae83523241d29a60ad86a8ae1 df6d1026da705d1761369bcb1431e19ae6a3ea1335b8338d0288263cc2a62804a424b648d39e671a3b4bb109a52e36e43d05af82b61da9201c6a7d21c846590d4eca2f88ccd9d33dfc0e49e81ce2bd869958f7ec7d2cfc100f6547f1a93e9a06556c0dd1e90f4870b72f0510b0c967b25589866504ccc7b67ef115bd4d451f0b true +check_ring_signature 81d68cd974b89c25dd9003196207565d482ec732d5bbdfae5fe001e71f764477 4e870224c8d876e4b0ceffd7851f47cf619e93f8d06b49f7f71fa456db46888b 7 12dc88c875d14e1074c929ba35ff3fc090268846814cc2c2c22fe85a527369aa e99046c2e91af0cd66ac88a6a19a6bae2be25fcd0668150acb74a595fa364a84 8c2418e162b6966822af72101e2655cd43e015735aaf25902c963d754d8ac8ce 2ba7b0d597331c4306fa90935abbe3fa7ab22032803b8e498ff8c8480f414963 b91aa774f1d37789f94614fdd75940f17753f879a93cb3727589b6aa3c4b6725 64601d4f6031911baeda9f46d2d45f40fb36ca024e6847897d1b89082228d00a 268c2d4e8cb36c3aa661798c710a77904b75b888eb3af247726467cd0005042c 000c233458b33c6e2ac1dab49f65e3799b484858213e2d620280f53f353a0702465666cef19da4495aacccbb4c362339eb4a9d9a2fe435efb3401c4e8168de0297e89eb1d9788f269d4a9af7856d57f5173064ea0a82e4f4625150e778add50f1c39e247bd1fef0d6347ac59dc439a5a27ba3097c1446383227b0350cc45df0205bc0ac8de1847821e4e16b771d23523ff2e293c85de2e76db223db2bafe3009a83f909c243e6852bb6248a6e14fdf5a3458ff1441196f2916e6ba3df8202506f674336c4d1dce3ef42e46ae1bd86c3e2228926c8a0f3424dc7162e390376b0a5c629ccc127524b2ebef53089a33393374596038744d4b96ab02afab45c5a40017bf2e0ce0122d5cc2e310a68c4fc23e60c38d9805f52fe494e2e2c68bf33601b4be540fb6ce1df4348bdbb33f130e5774b035a348b28113924b887c7e489d05188b6abf7d498f9bcc56db8138eba8dd787182c22039e95b9fc46e2a17af1603589ec8818c1faffa6f59359ad9ab4eee4ac3e53d272bc78a668ba4e4c708520e16311f30e3912454724c42c82bb4a9f5763db40c35b2ccc2884bcbe627dc49029f0b132d2b528da64ce9096a5d4262a6a2bd944f48eb081f1279de7ddaf9330f false +check_ring_signature 446a501d485141c2610ac7f3a0d3dd44727fc78c8ab9f7a084d63240c1cac988 0fa764cd6b8411d65f1393268b215087eff9261660639731cf833a2a9ab8c06d 1 7b06243d37f0bebaaf62923f548c66d0eb847eb254b2ef63b3de5c361f3c77cc 46f3087ca796bf6fde3d8e72fb5c118e8b043aa8abbbe3b88e7e2ec0e2c4f60fe80284eeb850c67e9045b62a24fbadc73b071d8b6e1e9dec7782fa0eef1e0b0b false +check_ring_signature dd9541b6dd1955df71679b30bf91326328bbf70912da604a12b6efa7ffa56407 306e6ebd4018c65dc9e1392c9206ed585d9db6dff3a80f07f332dedba6625c95 15 2a739c8b13476e3cdde3c93310e28aadc6b642359a9fcaa34e7d2401005b77b7 852c1080652553e24cb8581701a651ce1c00269230f899a04de56580e94d0076 48f77b12f87239dbdfbb5d05e27eb54c9173c96c2aa61e6b0fc3a72707a10f78 aa17e4445875874a25eb0f985f8f4677aedc3f38810a3bc299082d9cef8df1dc efaba0c0b2db14073f5f7bc623c16b1e811b7ce9bd29da1f0c6288e2f6737156 7dfb3cd2734e7776925726da810de9f0ea5b6476c22ba201da4475628607ea33 7b25331e2e2949d78b83ed566e61d266c4719052a429decc61700cf451729fe3 7f1650933355680958593445defb57dedb97c4ac93f7375b0b45e3a180c79625 634b1ef53a5e870df8cb545acb2d218b425787aa8623de92ba14a6b6e4cf60de 1151f7ce97681f8a47dbcad54cb6a7bbaa39d8a59b152d99774f3cc20b47fd1b b4c4d5ef416fad732b097e66bea385dc6b6d27d9b5060c7778661a229b5aded2 42612c05f6949b1e4f9b506bd132d269d7ef9e56f0d4c204d25aceae1d97ca19 5e271ec090e273bea010347b9f71460f0ae00c99f1c1186f5a9e55021716120f 76ed39c69b05221b70946a1e920539f4688285626d916c5b7f4cc8550074324a 1767c7424dafeff290bc6f21afbe712094952c4fafdb7ff76dcf0d1b6371db14 653a85ba4cf2faf845a6d353e3fc115d7fb110daa26d2eb0727e644df11a4e08e9899779ae045bf972e99ddc7b32c5ecf6eaec16f8054e1707d27d49edaa2a0a71227d423d16a4bc3c7a2d660a239d11ad544cd521461a31d060c53a856d690f339cb4b6562c9748e54f4e52d341557e244fd9418a24200a8c6b1791ffd33d03a867a53939d29de94104d4a7edbce5cdccc8122f3055f685adcda8f137f9dc02de142b297eed31a185868905a8e0bee3fe93a75aa6e44b6099d114b72d863500464ff2482a47fd8dd29279383f05412c4c5bb1412e85aac0a1b88521d189b00903336d13ac34859bdf8b2803b3e036a117bd2439106414fc37337b836634c60cac867418397fcc92992d1790969ea1704de9de8979f6ba2c287b505ef22ce10c015287d78d2bdacabdafba3e26ad7ebccdc5de5736942eb6a4a05ceca721dd00c6307db01c75a2b4ae0900fa748e3d194cba946ebb15ba5afbddf214db3a3f06194003d8160e17392a567ced8f290e71eb4330038bcfc64d49df84e6d8f2a209f0ed0de5bc0538539ae3cb29421c25149442dda30269e304b5fc17947bad720913dbb9f9e899db30dcb983526ce58770306407e55e815974ab50a1f42dfb8f0b56244bcaa342fd79001fb0cdd850424a3eb0c464a38fd632cbb4125f42577c0adac443fb6798811d75b46f669779750291617f0de3296a3ed512aaf16770a50d00cce70f55e66dd101f4bb7b02a45022d6f6f3fc44abc9e5a13f0d46cfc3370d850b36b8a7ebc8d4a32687797e4040e06b8217ccefee178e1bcad0193c7e5f0dc6c255a238ef4959fa2459b8badc77034a1a85bbc65e2b51139783eb949e630508a806aa436a8f8b6ce2cdb1ae1d3aac626431718811ab2c5cebea810b2d4905ae6084f172f7a48c994428efef45f9986696436c97244cb3f2e56db3371b4c0e953b18b313b59f1e7ab60b3e590512683c541d0e86e9ebb6b65c3cd47cec1502cad73c9fba94230d6e393ecc865d2aac27805517c8943511fbfc74419d82b0009a4979443e681cc6c00b0eba25c6629beb65585a5f9728b62724990f782f5e0544e6eb5736eb59ca0b15da90f7e785cd2f9f455ad36fa85ddf40e77f0d8f1008647fcc49c3e2661f74cae626014eecb7620ce3e5958ec5fa3365c20191ae0507b9276a0484f2ffa45b67acaf10443c5e322c3cce1e39625772d840eab9f3520bebc09cbfc44a68c3c9a6c9ce3f0241a1561c0dbc6717eab88c2b516c02b12703a33f74ae075fe1745348681996fab0462c3c8894450c5583abe4abba8704a602216e35932d05d2ffb99461045bec151162e9805a775bda05626541355ffd3909 true +check_ring_signature c4976d03f962c094a2ed6d9a897b94c05d2f1e7e45a383ee960699c5270ff5d0 bb20322b057b707d78437401b3493c63d3abb02402c17f72dea6ec08b0d59696 38 387937b323aec5340ef73c813926b7019fbb10864e2d2a68f3024e58550a1baa dc5feb01443573aa77d90dd9f003e052f5343ffd93078177b11371cb5f415492 30ada187c8179d7607d8478bd8b0f3de16bba2695f7ae53e56e3d7b400066d2a 9feea134638a51eff5343655b4a4b2d77ec984ea08394f8e7b0a4d29541933b9 fb12743e40fa0a1d2671b0bed6791e80c9020b63d5dd9340b8ec75e504dc8887 9681c3a94984eb6524348a03898435428141e7a4338c65f43fa395cfd897172a 80ee964814df665004f6f276a49f2e283ad0b72a3149948a1f48b659f6cb8789 3202162729d651f16012cf13d2be48f8750a291f6b2fcdb0cc41edc95a9bb7c6 b1ad85db18864c1ead13100f05d1f51f140d2ca9bbc00dbcf551e578c8b1b4e9 3a77c6e833d792fa77e867e3ec809be046f42bdb917a0c94ff52eaeacb124650 e1613c70653c6bc2859812dd22db164e78aa38780eec130e6bef0c34b5abb87c a2373f0de66097d0ffaea94b39be9acadc699908def99224f3a466d54efc17cf 7679533e6c04764911e01f1f8db038b8f4dde7a3f38c5266162c06f80628e048 10fc147a3006a6dfccef4f0f1ab8f466c285e5de18710dd2696d299ec193f518 9af148c1d3b08457dc1fd6519dba8be79b1cc029a8d4b60625da93be49170310 d1920ceb77d8f547ca6ae780796d5b238c3d242c415e84ab218f6fd39973c605 8b8f8e36ec349750397a04853a068bab83f794287ad1ffa8d60e7f755c7aba84 2b6123abb7009a047763c6a5e6471ad1309108cd51a760bff210043449ea206f 41e66d39e45c0c727d5e1e2e463120737d240b07f1bb04e32368eb812a8f0c02 9168bb39badac318b3d124fc2b9390990f9b1dceba26b1acfd8ad6e33eb3247c 11c185b43f29c54d5af6d685348345c67e64a3130f8d90534513b0281a341cb2 dfc426903f5a7d823e904449fb66756dca9f9ec9ff6df1d6300e94825b8f6e1b ba4d61143843356750b2c6cbf419458341f1118b12531f183b947878f0adf2fa 90a278f3ba3be784e6220f5bf3596819243eb418f1c33e7408a3868e983b19d0 d7dbf0338a9ff25992cdaaea91595a33c5cd7890d840a1c7dd4fbe9ee0e89199 da96d02c05353b640a1a3d6d9770313a2804bc7385428bc9daad3958ac004219 f4067857f2d6b1ce180de38eaa6bf7f6af9969132ceea1ac09aba65f555a3f5d 50372d98d5017809e789e2e248b048ccb2b7ab24133c264ac96d7fce8b044b7d 1f8b84d2c3274d3a774e27a4536de718af30e8e1d3af82bb0809a8fe7e5d47a5 dd4a904448def1f8ba38716fb935b8dc6410212fcc03f4d9d1f3a04707862b13 2a6db9fa30f3f6794ddd870a78f22e7de28426020b62e9bf74d04ea4ece99d3d 2363b10501b31b93450380930db828a77570a270f41e2c9e9f0d42250d591eda a8fd81560a4e58c972e3acfd85a56ce8135309b690a3e2716c86e7bcbe41254f c081149f4d395db67e3db264f03d0be5a4ae56789fd72c3193afa91e38aad253 9f80ced1a339773116a97855ff9e9688d6d3b3669096badf9dc28d196e2435d8 9915707fc1cf1263239f3a5de10227867638a427c92a739549ddc53229d13d89 82f184cb24d5cbe437014c774c194fa6289c382f6975457938808f018276345d 7e2c13b7c655b3dd919996738a85a6be8916045e764593a8913d77f857c77ab8 0f1f346ce0ab79cdb39d795df75ad7d0f8d244a5da5a80eed03b5a1a645d310e3198f2f5239623bb03e96569424026ffed1c658a6777425f60939e4f87122b0da264c297e5172872c0e56d0857854d7e7b1746b1061754e2064f84421ea43a0e567c4d03b1a068325a22ef5bd1ca2c2e5145845d35c184bf880f56c305c69a0733a46cdd1187538c11ee39d39f9eed604cd3b0d994cb3b4b49876ac8355dcb03984ea2d6da24de53ebfc0d3009dfc1161cd18635fd98678c4075947fb4f2ed09980ad7d0ef32cdcf93f5b69ca6bf2868e81c620c9ad0b3da4b0dce1fc25d9c03380bb6ac4204359fb3f66ba3f09ca21c4b2a96e3dd950bad069c9e123d95230aa0e2b5ff5903fbe65e7ad2db41510ec5f826b4461b3d133b79a3de7da75517002938061b23a43f37efc616c3803ef8f982bf6022a98afc35d6a5d1fa36ed9508d56c257c41fc75027ce912398063c5142ef3491dcba774f75363b10ff67fd10126c2f933164bcb282978add6f3881ec2992df98c83d3180c79921ba66f8f6b05f3166383b2cba24eedb8dddb1f47a99f563d43057ecdc4a682261ca6e1d81308b3d11de6e7a149b203fb8e6fc934f0dad142ad36d7afca6801f0dcc9de46f00999d9146eb2e9d76bab80f82353c93a2d49ddf4a48e51ccece6611ea2ea9e2605f63669024b4b53f05243efd8f6634b3654bb94c23e8f16c230d51b7d45afb80a4501deb17e4fdaf795f735e0accb8cf5fe0f4cdc5aab06f19a0c9742d6819005dbd10bc5870058b1a2e7b822a729de70c22165681bd952b2b4fefb6469f9320f9ecaa4306803d0c3ee79f92dcacd16ec4ac2609733cb5c9deefba9c548ea900fff2a1fa1d1336d5591cbbb15d15d98a79fa1d30f5fc6d378e92ceccdc9f9030c87450aed50f512d768b584486aaa0d8f2d99dadf867525e35fc70e1af6ea230bd128a744b03d3d756e3ce57a0817862ac8d21c3017f60ec4bbdef14bd296bc0708cc9ddd9169f9b80f0a4c54a5c98dd7806f825d23724ccefa75de65ccd58d0507f7649ba6230f872e365d3bc959628fef7c0918c46e9dcd74aeba2d93146f0c68a5cd439fd55574ffca5906cd1fcd2a5ac27d51fd7d187c4a70b6ad2b045301eeed396b5d6c0c6aeb247233d116b0fbead50433134d7069c036fe8ecc49e304b430a21fb596c908a34c17dda0f5157f9fe095be72be1af4471c04cc9899bc0df0f64a6957c689ca8268c5adf8e3616f87775b589821ea6b758d0cdac6a7e6087a9be7538470b85116133d5fec80aca7f4e7bfc653f80d1cd05e2d6a6c71400f0d3c4480b210076a490383ddf3c90e5b9a404abfe5ed58de8419955953a166050fa21beb4446f70394ea5119eb4591059f7ae78f056da51edf18a2d2cdfaf1041c763be23b7f46b5359984e66bea2128ec342e59b151529b659b8f615b34b60c1dc3d226f77b2f63de6a8d3b10423940a7c22c739982a5e32f8cee62957e5306d1673718939f8542165ea86bd37cf4dbe32a067d65e670ccfe6dcdd2ca68420bb82184429dcb31f291ee2a9135d7ffddbdfae7d7b0066f073ee1df2f259d67081fa59b50fba0c190fd5a6e54793b4d6950c22d9987d816a303c896bc52b9810e422bf18efe747d02d095ff4ba96fe078b8fb20d307007d2e8b5fafdc3987d7025451cdb7f4e187336e4457d5a5463ae61ddc3ddbab41ed8d03f575737d0706091090e4c64dfc50d7aef244b64ececc1cbc5dda07a91bceeb3316b23a0d8b8b07d92ec48d95eea28339e19e9db7279d598a9dcdbfacccdee3c7c30d4135fe5b0d62027bce241ab42f6b1e0ea8921ccd6d9358ab3a50649489ab02d702838645028ea27616b4b5d8dabbbe02de4b42eff63f5637bda29db07f6eab9cb44c899506557257f41a151915c067da652916a482ef4584802dd49cc613960fdbcf287c03b5e85e234b5db70f084df69c075ebf8f62ed8e0254fe8c7e1c2e4f39ff341400011dcdbabaed3eff390fcb6b3c3a8d674391bc757556634e7c38ce9aec81450916a9bf40b733767dc0a8b3d81645d3ace560fd264fafe0509b1f3b66518e5f05c7e6f17b649346915aff8e4c1256cfd306cdfece60ebc7638f6f0e690b43ae02b2139dc6db26722718ec9932ed3818e5f31feace87456445fc816f642b7c03042dbe027f2c9fc3f59c235dba8c8288d617e2bcfba4b1d7a3a57b65eb5321bb0c3ce6b522193fe96cd0ffdd02fb957b12663dff581fe078d9a74680e1cf883e0ef75121810810feee4ed90430bd6dbe08c9aeb0b82c27a0153f69f8facdbb580dba95d31856e18e638e423d3ace6800c507399195edee7caa40f350e15a54860ffa7c40f0274430c27168003be14c5c46639939c577de20b6ed5069cd9d21c501787cf313cc998768fb2bba2ba914aa3944f961be88cd671d5c7d42ac1f9d2209376d91bdc020bbc1e040a15607221d3b9c004dca74c7caab3069ab027aff8d0f5fc716a839280c0bcb21e33bbf191d0b6e2a929b33db82f1d988ebbd2a610707a3eb0d236723ee87ee6b09f44e69702a7761bcec33e946ed0ad475dd915d8200198bec34bb885adb9c3efda36c6938e5b763be2cded323467dc788405498420a622b9e05c43ec9bc5dc01c9a7570689bfe35409e4fc0181d8645dbb363f9ae0d7f82ea10ad3af3831c65ad8b19421dd3fd9fd61718f8f682ceee948cd7e98c01a0eeee41c9e56f32b346d9e464eef799746475132517ee6bf89169f5799b910863eb103b21e428b585cd9183e1657cc147bbffaf9c6cf99c53a25985a6a31f0dfc83a8f735380a075a524ad5587a1c6ed2a5c13cf74f293039255865e7f97e03f48433deb6b138199de156d5c974614c2466189528d5961f133c7e8c03fab3084c513f2be08df473fae317042a6d84a7ffe4ccb36feeb74eb052b9f5f919360a61473e1f666711c675fd1a8a2e2a254a9562bcdc3718def3b08d8d4dca80400b5d6b7bfd8d36ad8cc337cf8a4de18034d625b268734e4f99a15c2ca1884a94098e17bc80bc8289f90934f13e050e90dadacdc0c6a53b1f2e2122c8c614f0da062d7437d6228b0076e106cc81d85435576ad7188bb27e6c1ac8d61adb36a0d40f43efdcb065f9ffa5c53ef9a445c1b7a26b543093630863155466e67baba56d0ed0940aedcf8df7dca9b07ac477889c517ee9f0532d5580ea4df508b0a7d52c0fa6cebfb56191ef1cfe211800b8c7ec028759e47613cd14ee3fa41c1c72b029015af14304cd83273c9d17175d2c92c715b0d2a4b6ebb75232e524ec031635720d399ffd61da12e570efd6a8b105ee228fcfcb1a4f1ac1775d6f32fb4e79a86e05975c2291625f390e24fb5607a702864fc1eacd7af586c50b8ef409c7a45275094d67057b9288b10ab4b88e49cf3cbda27e22a659dfb625b722f8a70357423e06 false +check_ring_signature 7b3514642271940275405f99ac75ed336e26fa792cc8b7e5a6c74662ab87bbb6 1055e73a50612e0336922339ead78190b654d42a4f1ef437b82b6769790ac547 102 60607afae19afe5dc562d3be8626ef8f0357f369622b5949f76aff428b13f8e7 f8418bc9818ce00645054fe62452ff754fca5ecd87833676ffc088afd2476a29 328ce4675d08d7ddd840334e3aefde254c2567f0d6dcddc36982b8b55c56383e 8abb70903bc78148d883dfcb66c744c1a8c1482823271c0a3ffa4d48e456498d 9b54df3bfab8894e33554f68660ccaeb28e77902bfac3304e2e5090fa9baa3eb 38d3edcb7e21bd7be744063cdf177458469cf52bb1acf82a0a9104b50ea30dfd 174e5bb9c736af2b6b5cdce4194928052e85ae4661f4ad6a0c716fec92da2d18 4231df539b2ad8673578f595ab6cfe3010a09564fc46b4cd740378a2833ba9fd ce67b06fe3fe1e58f5773ef815c1a0ffda5c470d3a97be9b1aee642e09b1f4a2 75cdc845a4e91ad972e06ca7be92ab3584970cdcbd4d99cfeddd8c4a33c64cea cb687ab823e93deec46ba348412d330c5c49469f777274415d18d8334e687273 aaef86166228ee5abf069bd90201fa247f40c0d7fc34e54f04a5e3863d501b54 d84c44d2042d1172260340325fbb7f35cac1be347e585f719e98deb8a86e0892 55d9a74c26908abfdb32c67fa334eb5857d8dc27b21696caffb8fffae7c83621 559b05684d08c8f2be56dcb307726ca6d773db2b06fca7b96e69d7a38747b1c1 43ee532bb1eeb1d1693db2ee864899e8feedb6d7fc5d1c69abf64500bceb6875 69ef97cfc43ea7ecde35f6c8edff7c75dbbf96f9e4251e3d5bd24397a51be0c5 4279b00eb8bf1f2e187fc027198d7f3c796028da2ed7b2b8a667b2b3659731e4 cffb5143dc2cc18db9ad9475b58ad156a3f57df40f2a79bbcde4eac271ec302e 9d95fbff1da2ab7dc8accd5a9585e2805eb9715db51eedbb504cc252fbed6a18 9caf749f036d57a0125d1622a95584a35d2125466e7b00e5949ad945ed2590b7 2d6930be10d13f39997ef0234b91a62dc08df6f634bd6154dbdc12f183ebb823 a1e2b392fd85b908a6b70ca6332b98218f4e21b70aae8e40155ae2032734969d ee2bdcac3e9de3ae931cee80bfa5d0677b7ff1c63273b2dac8e89702aa0df657 ca866f5036b2a6eb68231dbc03453789f38282bfbfa22818a2a1de127b69b602 c2efa26b5524f693b1ee43cbd100b79b342389e408c00e25897e1147ccc8954f 433666f806d5effcadb4c49aa7ef22a9889985a113c79428800ab2bfc36f2ccc 5712e52f90add7f6c8ba6c98cf4d6069b40066764ad3f41233c42ca6da892da7 72a309879e7d7e12f3b22a22403c4c938b96f3c7add64726b80818577f1ed6ef d4144b77c66b997004d54cf65af63a1a7f3c379ddf7af6ecab62d18e96e141b5 baf408e7d58bf4483e9c8a5a773b875bb4fd647aec8dca953b0fcc6da8af5b0a 984bc0ec24f07f08b94705269cc7df7130370badc972ac9d7041d6497db420e8 3fffa1496eb3b90e419464ceeaa9afbe829e9558cb9ca730fb837d9633446ee7 eef4b9178a2dd69e4e3a35013af24c2daf06c2782f4058e77663b4c6291f305d 9a753f891e43ee59cff7f68911004b634a928458fb8f2a7e9eff05f952505d00 70f55afb4b6e57f0e1c3beac36247c26f1655e63b0e1e1dc210d48e10ba124ad 33efe57fe29bc0fac1dea7a157e7bde36ec9ed28414d989f8efff73a01c1feaf 79006cdb7df2474711d6588181edd75e88321f19a6aed32da7395ccedc7bcc2c 8b94d44b16e4530ff0e46f0de376c31e1ed340d2f0f43f3b135c6440797038ca 9b93c292cddee5adc584b2478b33ea8547a57967fa1706ea2e791dd39e2b3be5 2053091eca9ff2a952519e31635358569d75416c9aa29d3a83c0549ce6dfed94 b31d82949bb00c22fdcaefda8743e89ab3412d5a857c7e465d6f9396e6c0409b 465ceccaef08c531cd18a75a3e976e0e1534442abbacfce89482cee150e403c5 9de16debfe04b28b69f8375ed2ea60dbbb2487482bcb1fd6600d6f3d21f15efb e8e013d0dc7706a5198354493ad3c17c556802cf6acdc6b0a49831a76d80c22b efec7b122c102c7428d42a3de818701b9afa5065f9f44e50cf8541c6dfaa58db 7e5b8a43dc5b0cd3c2d05a3c0aa4a20291b5ab99a79fc5ebd5f9d5ab393181e5 e93619cb1c7f92633b2863dd524a704dee6206e5d9b96e3cef1b65dfc043678f 3c93d93a94187bfb92f98e7ecfc95abdbbf879aa536a891d7a59b17575330ac9 781fbf187464a28b400e0bc7f7d185cf3a784c6d54ccb56f83fdc6194ef71634 b3754692aa8ad5cdc5fde7058e01e9ab5870a6a29a2495b3f5d2a1e6d8ad5517 678dc79ef082670dc11c80f3d91ded999fd7185cf23b561fedc768d7e84765a1 e67023eea8b1690b0526f4328603b7ca5d7b06657d29269f84c22f9926de1311 76433ecbcbb177ef05c29d18b4dfb8cef95a71c430ac102b5549bcb71a3b86f6 7d6832d48503a8c0ce79537f37a011c2fd6ca21773197b2c0ecc99c1e5fa9bd0 0b906560711ba4807032a1f7edac31019cd1d37ff31e08fa6cd26fc65892b423 e3cf5f415eff17b7c704a2e96ca5b8d82ffc94fd56a69f4b08f2b3839d1910d6 9a67149ba18453d74c037df97adb6c524fc7f4f1e02c7e3e9d6e5d49cedb6b99 7fee2b2631f308be8979e0d27ba5bf6c5b68301bfc014d322babe1ec20cdf91c e692eea200ea7b1018cf25a81681a09c18274420b93122bfbe56f4daa0379ee8 e6bea2de79f67b6316659f3a674535314c64508fb053258338b0f6a064950314 2bc29e2ee8396f891948a89e1bba681ae19f627bbe301cde7589838544c3e8b2 9ed8b416f6f17caeab128e2dbb535027af20857fbb7f5b7f3d0b2856d8ff6ae9 765415a16f919524cf41636a7a773dab24a487e6d2f84698205cb21e6f5ba5b0 d76499b5ca81723dfddf809f36c27109943d12d355c7405632df230513b7ecee 5777a5be5018364f4ae7ed6c5a92771f9162f490e0026c43dfd548927404242b 2344031cd91dbdcabcf77b50513cde4a37c0f32a289b186361cf7ec0b371b482 2d3604b70911e32b1ff21fdc600e6dee35e2a31a8c10478e293d0926e50dffcc 87c0bf16ad5f9dee7335486b9e34dd182e71ae4edcfae8582b823bc58cac1e27 daa9b2c3b04211c4e549a4a0028c46d9d6ef1423da80f824027b1bbc1123fa62 0b5911cb3b47c3cfc66cdfac4f7eedd2d5be926a92c1075baeff2a4561ef5515 181f49a42e369dd47a5610926e39bb301b771944b685c758cb585bf67eb48040 78758e19f25a9c69b462cba1e5661238c8413aaccfaf7ab37d0fd5fcb0b4ed9f 868995b03185b74196aebc4811e5bc227193817319863fc84a50760574826e21 e5240ab357c2a0833c558a3c67a7d055d63f6d625777e1329d452c90ee522140 6b144b114180c431b76532dabc3aefffd5e88e445afcb3f2e23e88045931d901 5490ca1e7ea5009c3d2ba90649af9ba2b5debcf3b24798fee4f1dc5b612655d3 aa320daca6a1b7fad73973e6761b85879ef8a3f1c5819345c0551308b2e4fafa 5e0de7d06a6bd6d4c516a2583fa23c303d6ab3535acfc7bb2c00a6e2d71d1403 2b2a21c63e84aea3a4bd1d8e036133076083cbddd3cee422ce5fcd8f835adf3d 73de097bd437a75470a1832d9707e0ec972a8ef4a82363c21f36275e0e298e8b 97fde0340162c0938d20757fd34b9d15e27ba0a3b9536465e7e2662f7c08e052 1691d2897ab9b9366ba97e876c5d9271adb2d28bf5aa891337969a634f1e65e5 6c802451a113296b9af0f266356d5a836279923856110ef280802dc8a04257c2 323bb090cb51c1bafa0a6bb202135230da8dde47f7ac415ccb7bfeab304f80dd fa704101732135b9456748eb127a1e550ae240a6e9355e8d2db37179e62ea72a cf87794aa5ebde9fda97ac10219bf34c3140a81a12a8113a4b6be815725b7154 5f861c92983da3b14bcd29ff81aef132734a2ef9c7e5de6d0ced028f999deade eb3d288a2dc88e66b6f7ded715d0a472f9bbcf3af1afdacc1056db2fb1a9c6dd 56ed86a2fc2f6277a113f7ff2b87ce9d0a641eb9b5cf08ac7ba1cd29be2313f4 403d870c5c786f2fbb31c895947a889a8963e599fed7095b7f8e1d6bb25483e2 8c239540c749557ee5a534390661ba5a23d7aed9b6226361104c245406531994 4824d2fc62405f94c0a528c96d48b2aa3ec95b7e657f6e279be31694feeb2de8 ef4dbafaa48b7722573b2616f588449233fab4df47aecac9575b0429cb9aecf5 dfd721de829caa1e27bb5bebbacb2b39ad33e62b4867cc9c35403821f23949ae 7a109d35fc05691ffa52cb165aba5d8a1e9a284a122d55edb75612d1cc77b016 701b9e3f66fc65220ec3312df89e1bd42011f7f301bf5fb044bcd76ad2f63b13 8cb5955e6e6a0bc0f1c987752b0f0a3d05705849c53a645b9e68b0b9bdac3389 ffd782c41185d8882bb1fe1927ab095eacd568e19df32cc811ca97aa3e8958c9 0e9766aa7ca1cee2477b184f81f25271774861395fff59703d806f9feed60bfc ce7c0a5fe26700804889940990e97bc04a8e9da81ca87ff61bcbf6781b4eee02 aefd5d3c69d59b20b87a68a504f21df47d1735adf441d330bd29edccbe982fec c4f9c94cd219066f1f488a27b6ddbd3982231db65a63429c6ad17065d4a4e2088454bb2f87e69a947b4031d53dbda1bcff7aa44c3fbd63c8b1ae3fadad33e7092367c996e0653162be1c2346eb111e57f1be39eaefc4681ba440e628211ec00e89b6733711840964f5ba2df5f91a5c11ebf0d6a6fe1595380d04d28da0f2630125e9d63da21cd300f6a7e2d3f4d19f3973e7c1c46d4e506d4f953a6bbb61a306b45620bf2696cbb4f10ccbe2e126bf1f21240afb8a759dc54543a0e28f520f021372e3566d4e1c2f37a14714eab661e5c2f3080ecbd7ba953b42cecc3b9e9b0e256e5c216c3e5649ffe29e58edcb6670ec1b66802a22871855eeef24d477f90edbe1af79c17379e6da0c90c83d260e7bf06c1be9a19490842126fd5d5f980b016688a307844adae90d19d250ea21de03901613920339be928b2f3457a565780b40dec34089c313a3e0d55ab24551709dddf8a4301ed5477196cfd61e2a62950a6278ba5c6c4e2b0d3e572555a8c8f8f8129bd20b59829b4e2468d7baab0d3c0c12500574f5669e91e797acc0404889128ebc902ef8bf0d136dee626704aa74012589718cd55ecdfd94d51afea84af4624b8b9863f63c74fb756d294232bdf00a315cfaf68faa96583db093570fc633703a1f0359b307df964a78dfde78be1f0a5f0f9e292c07af541b5b69ae50d9e5cf6df3ad984a34eae63d81ad74a12a750050333e7d640d8f3b6b4c2832b37533c428a27d7cc381199004a06252341db70dba85f980e9669ed8df66f0f0eb1bf055600be11293c0cfcca7abc31cfe5fd304710801b771b0422f01bfdd5e07bd746bad2e905f1c137177198c3f57179640094597441fead308dfb476943bab2eae13aabaebc62f24cb2117926d57132ca80e2ff59a399fc83998baf66d3e5b87f6467d0c0ea2a3907c812dff1a7dbddd030a973694861a527ce37e303fd2adc8a60d30852b25a436a25e6f88a307e46cdb0d69b7a8d29303f079490623d2eb6db8d9217150d8b50445637cda0fb31bb0a209b856c1e9a729891f43c6715bf71a9b81d6d4dffa05168d19c35c9d71f42bfe0148cab9d91ff570ebe8703dbfb053586512abbe0165b861065de9ab376dca980c0f0103167ece5fe60f4eed59a0fadabb491df197c948ddfdc6fda0dce876d40d4bbb35d86d164a1a7aa09d3d4f969849e00aa21ba53da67eac782007fb652805dd69091ab6cf78bdd33baae34f87f6f9e6152980b36e41e6f8642b7f0d323700f7efdab9e4a0e2df52dcdd11c3df9feb987bf830ebcc474e1096a1700f218a0199f48f941e57db5a634e4f204e2a4c8dafd511f82d0b86dcc724b96a7ade010aa76d35268ed6be886f111a3df71455473a0482335ec7d4e383fcfea7e54e170261507a3655b40831257f0040e08666c282ec4e0580909a73b4867780b21901056d8bb61e5994cc99245c138027ddd0732c5a22507aec7a90c6d055986ce864026a4447a5f9e5275a0ae17f8c3edd713523f222c42c5ef8599859d20f8b2b4c0e168b8df3dea25c2afb70b30609b9c8dd88cea1a80666d93a8d1ce23231a79a097769eace528048e0e3d70bd8183ecee75cf78a2aa01e4dc9d35604fac1edca01191bb9e340331585ce5ba8db0533964b0ccbb5a39a3efefe6f653c10f99fe0072c2a65a5f9b0cb441ffb79cfbe6906b26218f160e292cad2f84357cf2c5da30071fb2f62e4aef5c118735902a4be56e4601b6a227af18e7f8e12025ee1911604be93e9702f1f068daa4f76e888d9bbffd1d53acf8b4edb1ed3db52081273d30452c3496ca7829351c2ac851ffb574833f00c8ff40c01c2160f34689ece65a70281d6eb40ccc54007a729a852c633ddf443a02f3de5393ae131c0dfc68997fc087dcc6c711f26c248a49ce5ccb7af8b075b7c8bf48c37c493a24a243d2d4610034413bcb149b3f49e131771621371ad1b6f754f0c2c39c290d8b97a0ac29e620097f0957de0805666270f5caf4e8929656b48ab6299986355864ac04315709c051a6799cf7df224e080081ffbe2bfaad992376d85911666d93308245f8c0b250149036b034875168b7b91d16683e51201e5c8abcd69d36407a9794ab7ffbcc803c0bcbac34203d534e79fe8c5d10fd3ca7154bccdf688ac92096197083c062c0ea85bbd3bd43d229ee38f409783388eabc0994209651a50515f56f6536e7e2b0495fe15247e9d939afc9d4c5adbf313da50c073d9826e89c53e119c0013872f0e957661ce0c74bb2c084a3183b0a2d35cc67825486cb1ec2142588206bbc4f60f7a4f99bbc2a7e6b338ed02508902bcb26e3577af3c7525fce505f778894fd201abf13c4f40ff433d88986e1f5333ceb0f97066db923adffb045f6fa7f81d640a51ba2bea41d14c6dadb9420a680b023446cb35f9612966d68b8517e8f9c0fb0936420de667f5d42055a5e69fc715bc3c89397309cce47c31f7c46ec9244e65003bcc814b2d6a51da27e1cab81858dafe35500d9dfc7dfb1e840c78633cb5a60075952830e343fd8cd8f36aa5055b3081d3dca4da8fd920c7745b83946679c406337f580201097e973f000b5a4b0d519077f298414ddb7bdbf2860531a7c65a0a77fd86c017a91289ecb56fde54cc972042ff59ecbf0315ed6afabbaabb6781017e9deed77b331ca016782a639a5539e935ab4552f0d83dd5fb69fb8081d7550107155001afa42a64e76e1560d680867a36b21c5fe71fc24b679c508fca030a0af772a9a0c7910162cb0384c8e6d4fdbc8d59e53455388bd0fd9d0c2957e9210fc099d529fa1d122c161099ef9dc5a840612363162c022d729bd486e135d8a10d1452dc9f76115d8c8a585e3da0e54e314425d28a6807ea8a2682cb1c574a700cd7f958006533ac8f21a78552be1b5e0bc69d35e4d40e82cd155abb1ebec14a0aa7dc5e57aa89a1c2f214cacc2c2d47d301b7f36a94fa6416831fe9a68e8b2c0041b5117485a7246163faa0ce6804a5722ea5f5805a27d8da80c8a62f50efcc0b4d5503761b56a74a9a4d1d893be1c04d3784868d8b12a6dbce83b9d96b34a009291ff8566f519c034cd14d5eaab6449dd681850ae481960cfeda5781f71143083ef15f2a27ad46ec4dbc07d03bacf81fdbb3a0caba938bcc0adefb04999c070593aa42f16b80b916535c5c7b8565edc348b84cb3e1bcbfa223233cc1edf0fc06f80b9ebf7bd3cb3d4b8c98ad1e9c9466323392f2dd624545ca5d7af4bf81f40931bd8697043ac0320b230bde9e25e18692637b3a316d48e61e3cde709f8759031ef8107bd6fcb365e3787cdb727d97514e7ce523d207a7d0053fc8202112f4008370a6ba568559b5be5e469caff7494d3e74c8dca3b8cfe82cdf5ec9debfcf0b91aa9691a92eca412f438b748c519bc041c74b8b01f0f0f768df73a313b67a076282b3e2e58f6d024e0e2d34e7afefd0c3870ce5e15ce729e5f3615b67233404063efa5e9a5d6028f4ecab3875417ef97fad6eaad0a12ce5be528cfbb8762b0a28e1d1d23caefd0d8066520efe299cb527d950887e8230686e0528418247d101e25f66ee678a90b63722fbff111f93a54240b27bee1c9531b76655086aa756014455fd10b13dce1c072201179e3d4edd0df4ce933df09f121e6a97cef7e25b0cb056bd39474960596a3fcc600aec7c256f0e6b87492e74a7b0f1f21eb6e9b30f8ebf408757c4b431a716111f1b132222222edb65ed7cbab492051e8103e5930b8c934ae5bc4334128d537f6631be6c24b0d2f4b13c0a36476e7288446f92a00ff5a48c7456452edf8db2a05dabd8348f4e8397f1c74d937490608b8292e067035115f1081d0d6d7709294579400c369afaf1733a9def1ff3be9f7b26d4990a0f6d57cce4ae7017f81061c213372e597a25593e9938b791a89e91e8149372d202fcfcbf00dfc5c7ee84b45383fc26a193888177ea4c7b725fbcabbbbb6ec23e0dbb95c3aa683a5e89fa7ca06385452a2762fc72118b9f6b1dc28ca9cdfa74a90665ca624cd9fc933b4428d2290a6daed7052e35b5374d3b1825913ec1c74f7c05c7f769f860d89b49276c9b3a00171ce01abcc67caab1a123f2fc9eebfdf9ea063ec51ef183e37460bc355be0f6776e96785fa7d91564bc59054eb8e7dd300b020bd73247b567390f15bc6b391b1a2c32593a35b121e521097f1c1d0e3171a50fb3f633fea774c20459ff4cf1418c043c2e86adc2188d47764ee91e7d9706080644a4e424be35f5b7378d8419da0a85b80c8a05c10604016ecf27d558608bf40db96c9ddb1ffe48c43456014c7f74843d205c71333db20491fac422af746425010b4a2aabd77caf53f76f31e54decd8fb984311249ca58a66f7e97c0eeaa3f502a16f0460cb2b1a022fef8f97d8a76db837529093b742e5b6c93e3282700aa601b740e13e75bc916236d442f96e4918e80ff327cff790e75b004df2f4f709ba07138628442eb7a0a84b8425f73cf09ff8b2ec271607d49737aed4dd987dc83606cbb56e8fc2466bbe1d4fa709ed3ac6345c91fcf7ba908dae207d4a43f9f5620d94c632edb0836440e4d5b74f6c177c8e5f79668f3b482646c49fe59a3ffd1305f3e138ffbcb90091b629faaaae3dd1702a7ac6dd64c3a5719663c8b54eb2dc053ec4420e72f33e820787d1d6fadb3c1bf923ca9f9fd15fa8d801c18713de0c041816127fcf3a3c783aab8f34aa8f5b8cc33f529667dbdd78bfaa3547c116820bd787caee4c24177906c617102e03a7cb02f1e6ce46dcf60ba5f21cd64fb8f2013f4a2a0a7f5ce7a5356b6caeb1258f0c2808556ceaef61d35626faa61f77ab07957cd340eb89090ff0ad34717ca66c7c6e1c765d9616404fdb764a87d1d2330cc2709663b4e78be630782964dbf5edf5bc9ea32f563acacdead57dd241df0f080e4550320c360d70521436023f841230f38e51a4083fb5f1d8e4ee9b16809d094489e1f97240109a3a8b02e53c987fa56253c4bebb50642ff2ac214e47f424069b6ec281276042df3482c7f50b79e622318d836e9d9ca86a1ed8bbdf81f87a0e1644005d78216fb4a1fd1ef7d44298e30fd37b9c4cfde48a2d339fb5c8e355017e1323de2b8ffa0d30c7a642ce302dbe737ca24745f618875821c5bf321a4709386086a9e53d696c890cb5b3f5bdc4d9a07ada4d95dfe26c7a54c408fe561406ab2d71d3001b8bf0ae1076db8d511cd4a70338c5130366295ec4ba3ba73a2d039172bad5d7a0edddbc8b24d1454824f9a0541a54f050ceb1dfe3f7dae0fdfa0df4ab8039889ccdd238cddf5a0ccebd5a0c4b736738be4e812f49ec466be4f7005b925b9891dea770022a6435b3e7b8ef882c619c3f1255b592b7fc268082960074ca3328614e9253947da384e691bae598a50466d3f66a6abff0a09453d995092b80c9c931b689a7485837e75ad1a89bc55c0657a048a256b0f99dff2d6b1605cb13f8a5246713700424de1fff1034c9c3e9c1e0acc07bd9a6e4ebd6297ed10e35b55c3807892e8dc6309a3f3dbee5317af7568d4238af2cdbca7f95804dc80d9c42d0a651765c405806449753aecec98a070c6e1217dd3ed60abe86841ad90b7255ae9d2cabe8cf7bcc0c20a6eef2b130996222038c6b8f7718a44dc3147c05380880312c443a4054602daba49c717430a1eff3a374928361516a8531db7b0c4e521257b06171dd76f5db0d01ffb2b8b79591db51f755426a97e08c7a35f607116ec680a7b8cd1a8fa859a619594856ed7cbcf6621e07b24b2e1c0362f6c10527672f204b164dd1fd9b4da5412a54533a83b39a03ba5920462ac612c7e9160d8ab1fe41d3564ca99e3a177f6d547d9411f86cfa5eea0e57abb8b4611237a80f6f28a413ef21e105cb9792243eb2d054333a823c9ea9344930f6790e38353efd09f4e7ed6101878c00cfeedbc6879d08d05af28dc6dae910c632f270fc7a2a08654117e6aea18a2e85307825aa46aaf77faa9985074bc74ddfd5267079eba70e961b06d1d59c667e154d259207fe7fd2f65d958f754e86edff32faf2b1f7b101e1629b231f9b22f308d68f7ec9c4137bad4ea45ff85fe32e2f9cdceff868eb03207c90d6793326568630033dcbc63d2b8ea76f924cf11fcbd9f6afbf70980c0fd4d7c33c376f1b243261b44758ad73d2f3a56904ed9af9649e5edb533ec47a0e4827bd22f56618330e32b511959891f472235ea463d242daadd3ad9d051af90b8c070767e677184b191c073c567c22e51b3ae10fc1b3f898a464c51e5a193c0ab8982acd4828e04099a942009309fd1ccf546695291570ebc119b140a5a7c8dcfe4098d80ed5827bc5f9b522dbe46f545848fa5838f4c499ad129273adff290619385f150ae9a5375abbaea1a47ed17a2a8efa0ecda5c5c83c3c3f09583e590801dbb37adab072df962b8987d25dec1a6709e3d93584e9184bea8a02c5f4da048e2eebac86721cfba5dbfb0c1ea54828c360fd334ad8bc7c9e82fb45995ad10bc5580a84866f5165666660f4d156b7d9407b71d12663f248de0309f941c1490ad16e0147ce9c15c22efa6ce945bf948a3950a737fdf4e30480fa5fa05bc2960043bc4a4256a020077341a01894a974c78120ff556e3448c6b726dba81b76980d00107e4e38121fbb2a5ae6541b7772b60513d0b10f3de03968abfc2a746b84061cc3ef9f1d704a6832fc684e1e589a74aa5d20b8bfa931eff10f7108db73260b9d9cb2017aebc5cd9afe96c50609ce4bb1b1808ba5a9538d049f7f9c8dd3f60486b355c1301cddcf5b3cece010fe5bd4318bb56a965d2243a23ceb5d51c15305e43ee108d1bcf48625d061a2ad34fa15b8af5dfbbc53faa792530794915d200d9effd50fa920c8e055a9750915ea0d1fda87a25038d98b49ab4b1de438d5bc06855c33fc7620759d66a137faefc3b3d29f486b264a2c7918b11d0134e681bf01c715861ca2f885eb4862fe92605bd7291a23eba0fd9488703fec4ea2f1b2d1070955716c744b53acddf8ad9a2716867a4e031e0f5a139b2464ac9dd541ea1a0de5c7bc1cb1f6c1c1079c915e77441d11a38d730bf72bbc848d3c6b4d8f0cb40d1678112bcb094dd9fd16bedca73c6cc877a2290a678d4db91274a16aa4fe86095faa19ae009fd4ab15e0a7f29d4d6d495b701c15d37e79d44bb507ec4d13c702425e7861f6c32b9387067f1879c941469ba5e76ec6cf1fd5093b2c3187ed5e0493730fc01d3af125e2f526ae31fdffd6a792df91dcc89d793dcbcec3940eb10359ce8d5843debace7c21f4718c38012d01a2f053b4c435ad4e56862935c2f902238797f4d9bf38ce898566ba77a47df1604c3d5c7c12f048cc050a1122347e0c7f53cdaabdc662d4ce21b1ea5a7c482b17e5a6e33a5e50d4e6fe4d2b8faec9015cdd441716a79d9b5b5cd1215c655d7e1ba3f5dcd5b2c57622a265ab7c37ce0638ab60fd3f1bb7f43dd7d0180eaf2459bec8d4a00c6fe288a23119aa60c2a1046e11174ae747a0f64e4bd57d8b4eac79c5b2bded9d0e5b5c31bd48265b28e30a0371c02a51bfcb2a7dac359b456222751549a5f1b980c8cb0b33b80fb1b8750a78a4538802a73613230b187caa9a1350eaea02ce34e30c1467166256a999fa0e5a7a48bb8e24b1144578171dca0716607fb632948c55142d1c2c379e3ba9ec04ba67d25eb233a2933c7a4466dbd7c413f7af3da140e97531b5fda015c761f003065e23c2a192ec6a929a2ba0e84070ad9097ad6fba437ca61dbec5e646eb0e07d6e1705f9ea98735707017759ceac1ab5d4474ef5c4228712e9bc39c4f2b3a07bb381541c2546c46557142c9e642ab2dc07697eafaf6c743a2df85afdc5fa80ef6b39b32a61e6825c203afe5d90f53ec726fe7cb82fc315308258ba66a39500135eb542c9558b6cf5b9562da94fea6213a4ce2dbb75a26f326711c116568c407e7644ca931dc0e9f36aacd6bd4ffa5a53a9d29863985d07c61bcd892dfa461059401c137ef15604fda0b1058bf014e641152bd20b6eba1ce695e7fdac9b6e00bbf4f436003d6abd8138bd5abea3aeff2b3a389d663e084ccb1173d45999abc06c2ed46dc3a70956eeb44da6d6a2be5a4cc91f27cafbb9e804cf298812f70fa090dada4098dd035f977f30caaf457a776835e3e2297583356fb56ce8f09d20e0b0c840bdd9fa45805f013b5ff6b56c15667079f38218bd0e0a9043715f5cb660ce20bd53ab03fc69a88c33dcb44a7fff1eeaf52533ce8127b5262be1aaf1f7306847ac4bf0873cb629635f21c2d5e0ef042926330212591953f2e921fb54c6604b8235447936ac854978d77596e39e1aaf5f0a5a3176d2f6e941ba95f81c8f10d3bf1d0c07caedfad216319c3a8d8b7cce00ef210e7d4202d7e39119b4ac08b0ecf333828c1eb357378afba875583ef2db7421f058875c50cc0a4a2cbf0717a0ed63962f30252dad2c1c21d9c388a1c553d64a216435efa9fd8efaa78b512660aea53c9206a2e2860d0aa468b2292659fbbbff324dc728680b1fad058e422bb0b162a739664dccff35a1078c00d4c25a6d8923f5bb18c26abd26424ab650232022bff46ff3b94a1a5467d2129d01f74d55d3c2843db0375da04bb4cade08ad102e01c0c190197354801a81df1dfeac3f01361c84d85ee4c3bf1dd43243ccc1607152e0e480bc38a0913387daccab30a29a0808192fb5f5718bea4ba5f03d7506d01445c7a8bee31c6fb246a4905570023448facfcb4774ae7b5dacaff52a1ca08b14f9522135d795a91bfa0f6997d019f0cfa804eedf1f8e31de6d5a25909150da240f929a16687ab8d190189e843be7eca3134738d92241478cc68a6cece3e0f7b9ff7282a701333128b2e7ae6e948bc3c433f463a77d6294ecde1fc300adb0ca15720a05db7b5d7fdc0884aa6d30a0f07271202ad8b030a70de99ac5f7c7e0d38175ba4b5300875e575d5f6e119eb213dbf085a91f38767918518bd4dac780e83d9a4d78a03157a08edd18ffd35118c3489c42c0c63fa1d0d35e43bd6890407c8617bee02757d88db8b0174e75d81b886a06a3652bf6ada63f78544d95f56097c7f07fff15e70dcd19a625986f1f87c21d5c98d41767bab0a379737874d27046507b5d3b51bafccfa8de40e172ceb4be23026477dac7bb647c91871b975e10859021414b38a1366dc18a3eccf9aefe6122e71be64c843e22886331a47cd7e66 false +check_ring_signature 833581d290766f4c2c40f24e395b259c4ce844fba2f0025f201a919b759835da d30a74d9c77e6ebaed50a9c004541b563247b82dff64dccbdbbdec3c14b647ef 9 52b0a1f164142f6a28d58bf7fac750b3c586a1d6ddf84cb236ddcf3bcc0e0ace b80c1dffefaf790160d14c7b09c3c5b2b22f41f9ea2eed1bbd8f91771c73f1c1 46dcac5e782bc737c866ff248449679c0253cc5f5e47ba7f83c010122309f315 d81eea85f12c210e8507304592bb1ad9844632a38b09a14331a27560f22f4671 5d62fa33be48ff47113200c3fa1b75a51c6f3d25d6225f029b2729b6ae06377a f69b60ebf24c467b77d918080d9eb1ffca6a95b3707161aecc35f18a209d6f10 c80f88705792e7cdfccbfa61e4a97dcdea8fa04a356795df3c90ff18d31702d4 2ce4ddaef318e2ae8ac72988032ee9a55b80c7b35e380c7800adf9fbe79f39fa f6a8cb94d5fa287c89d4a6ef7aa6e2ed2343910b9c05603566ae0be97d00feea 972748532c75c397e3a4806c859f9e5099d31ba6ff6a2d5cb89f2037e6cb5200bcb202ca1e16bf828e1c204bffea2ffd3ba092e4f08c20b837a60f98128f01071104cc31d25a6975d033ed5d41b27d75a885a5d2a9393ce0d5e673def0962c080982936f9f530caef9a942f5fc2fa95748e0007f68619ac408697c85cc2cbb0ea8c5fe34543e996bc021feabeae3c43822c78d62408e5b9aacb4c12b38d9320982e522680b5077b428ca32ced5abca1f24147c8bf6344379a3bed958652d4313f7a5ac6a56f117b2fbf914a784f6902b5e9c26314ef866f67b78570eb3dd2e05d06d8e1282e75b412d0cb3b68edb3297fdccb8b70c4e48ba191019e3d02bc500b66c4667bb23aa3264c84d4b365b66af236c5f920c5bb2a60b86156e2bf29806228439434568552b4bfb10376ddcffec4b67dfcd8b7d5fda989dbc2f7ae2ec0bc41ab970337a551311bc7da9a31b533994f0fe001cc75c61719d6f52106a3f0cbb7f383e268a02577c0b14742171fe6c403aacf8ee21d77c3117c7ac43e3ff0d3f95a01bec814b7fb9793cbe5fbf4d226a2d697b2be66c7a9866d81bc23450014f76cae0db034af34bd277d9ef2e928d6e684ff52b98b2dfc3e9e0d81470150a456fc5a54ccfdc3da3e93551f8d623e5134009f7cd1b282efbe1e051c2de590166536ca2bc277a777fee6c2cdb526637dfa3f1170cdcd4a2ad8ad0c4207876046d4a8f2e2c07cd658fe3b9b0ed295fbbef655307797f9628b08d6de63eb5380911d096aba61bd280735a8bf916b9d861a6f17e05ce5990ca1685d9d10c80ed05 false +check_ring_signature b750c8c17c039d548e9744d32cc08d0378c0da4e43f9bb0a8c0dbfe0419cce41 0d421a67e34384bf97f1c2c1ac401737cc576ecfca652bf2dcea1047083a1518 2 ac508b3dae3dbb7a728bc4f1cd6636cefb8d4646ff5c3c8b1564ee2a2f7619c6 5b273c66ae8db9be0ab32b0b9e5341d2bfee14a04d07fa36b24052115328050c 04735a2629c49440c18513670747e5aff743054b331ab5df0d46ad20aae04e0b30673171a064279c2fa96092e54ef1e983373d8d33154fec70a25e544ecf2d0ab5116c9f8b5eb269f620a88d61045eae46dbc4514923aeb9ed5097e7bd1959083bd4070abb95fe94fa4ac78d6426fdcb573b3aebd8d0857b96cacbd68dab9001 false +check_ring_signature 183140c75f80604eeacd7417717a62c57be37f51f947a5ae5165e7bae486b66b e5b56a232df2c041170b77191f1bb574b1710f9e7b4b5366572ed5db553eef51 20 d435e144f26bf9ea4aac37a023acf047518b1d3829bf9d7857e2da38ead1ce61 5de29d286591709f48d58c8e964273caf18679fd82d91a22db0659b88f3afeec 1cc5b466598b45d07541d026ae408f4069cefe083e4513398fb4423569be7ca2 983ff422df4c3a17fcd065ba136e7b55df3868b3cbf25c534f4ce1d87c0559d5 82f6b4e3388b65e8c16e9ce9e3ba273f4059f2cf278826448d23a492027e4dcc 416213586d09c3e7895039e5000028242703faad51a90839fce00cf38cceb324 af99443506c80f0171e52f710a58dc47a86872fb1bd6f1c898d072b5deb51d4c d21986f247d78db9f9fe8d082682e5cee2731faf3b1bac73d39fd094f6986280 d3bd854d16547b9cf70351d823471c03530e9029796b7bbfd9955ffc5a34b045 9d8646affe8996bc38a1650e8b118487650b3dd9726eba443a841ae68f1c68ac 37a624166359f4e1351d89f1f5d960a9a2628541bbf2c8f2c0a57850bcff18ec 22b84bc66b836276b578242cf9838c300901e311f749bd63a2dbf7d64928161c 573cbeff5c7caddae9e5e721ab5e3c3cfa8e5cfc626ed3551918ad4eb4aa1905 191f3d38e90ea9db896fdb3c08a2cf912506c04c750a3b3f9070cc905295631c f74901c8a5f626c62ed5f0c2d2e4eb669287a0e306cdaf319e972778246f8caf 3178d048c650d4a441a1b2f9578b7b0c07cb524e57b7668d7e9b80a7c482569b 7b71b2e3cb877186badfd53ba203394def9e3daa2af0ad9ddec6a5b35b1902f7 37c6e829686b51294f6d558ad15ec134f3398069437cb7b20027df86bb7bc3ed bd1039dffee62739935270de0b058aed38c6cb35f9bb7fc43f0ddcaaeeb5d006 2b445fb675a115482d44cbef487b1da0eaea0cf7d03fff05a4e9a1d012378153 e1a5587332f423cdbefe46eb347413899ec3fd94383add4492b37730fb20c80698ab64c223310adbee02be767776f8a714244bfb494ba0e708fbb0829b89c90156849fc9056518251d8607d34b685e6d814caa9d715bcf0e31344e7dcfff6a03b0d2927497e6b6c0e701e827e9a94c8f7ffe09d8877410a6a7678e6dedaec30edaf3e5f4f76e5bc8757527fb0128b676aa9744e2b2b466fd92dde93572dbc60589056bb1a63a1da53e6c34bcea1265bbb4bbc22fdf94b422f9d3266b8d62ff06f13f6f0844a66afce47ec26d4c2d534b3f3419d682842b9ea8729e2683dc810ecb4a6468265a824a2e4c4e4a811acee5ad183362c96e92511ce795aa3f12e70b8069ca71df0bfdac360313740be3a6f948ddd1c60e090726f83fb160f7b4f40ea1129376e4862f3f58746467d224b4d6c34ab595b29fe9c1f59dc8f9425c790593396530be2ebf4c7fbdad893f5bf1814b6d9af379ae5b414221bdeffd1e9509465ef4f00e2cac467965f1b634ee525196565aa4fa116618901a8f0ff2892c0d6c009d303482e3224b618097a64aaf51cc237e024b411cdf43b05f6149d90d0c2ad1726ade7174898b374b0757630379791d9dcd34449ed672c84f71da469800136c09c54318a2a7c116ca2462a0d74bcfbfdb3bf6856e5de32f54398b4e8f0c9f62858a86fba5d04bc6c139c83436ee52fc2cb0ccc183627f59071bc404490a1aa651ec97bc250f2cbca16fb62e6527dae894b7d98c0d3e168a62fbd5fe5f0c2648e50a6f9bf2771bf2ff1b811c4219165e567d714031855c6451d96ded4d09427d16f95455cdd4978f10c869c82a10a53e601c01396db6b5934753393c2f0cb885ec91ebdd773a38998f58531d68eb4b3ac4ddeb214806f659a1d95367200a92f69bef3aad1c23b68c407d8110698226446829cdf65dda3fdede5b01b31b07e9bf6fa51203888a23749d6ffb77c7b813a882b2aaee06cd64203c2f8a6a6008790815a102da5ba42eb1edadfeef73897195ea8243164fd4d5ee41ac723ef5048628cf9fac53b3919fd70c1ba68fd194598288341d343810d845c29f8a85a50ca100a796021882170d5169b3b0902da79e86153432aa63862a2f1dee1dfe200f911d2f83dde79f4ca76edb72fa1e3b8d2065726ca5064a3f785ae5f07451ac0afc0918a6ed028c8e2b52e36296619c58c82b0c39f9a53524984c95ee9e6bf0071210d8cdf17362393b29dbdc919db35cca34dddf77ba8dc3a0ba87289a90b50b2bb51ef8f5da3a8de612aa029da29a8201acd728dc4cac76dc378f4dd826d40d8d5b2a19f87c9aaec4c1ff4a05f53d1c71e98ff14cb7590b8d667e072d5f3c00ac64e7dea307432d30e811869d700939d2ddfad6e496194448e3f5af6988a0091fa046623bc6516c9750738bb5b9abde37c13cc77afe1b7deed0b3a6cc65dd05700bed640c5ad05b9b9fac589edea4332a5ceed49638b5d6500697ce2627590307dbd7b212508628a94e6d69d2fed66bf8aebc5c1a0f9fe7d3cb4d147bc5d70c478c7b2a70b0092119e149f6377a122052ff47e879ef19942eb253c1cd6923005b417d00a78eb581d747ab7eda4b50495ae79fdea90c09e9d874c405aff6d107b4422880ce32380172390ae0a579167a6d0551bfa5ffc3bb84ab99c426322b05f15b6ae4d80bf4ed94d9100ca38f88f7ae10c21ea9224358aa1c6bd68b5ff70e359b9a938fe5fb4a0526a2d8ae7857e856e5decde39ce09966863fd233511f03f6a29e831e3db4ecd3df8b967ba0beec9635760fdfc0e2db858939dfc468fb01 false +check_ring_signature f19a5c48f417271f03a32aa0f38f206611eb200515c9cb107e75a508423303bd 24aa343f346c62d1be0b45dc9c05d16a242fe463d3aea66e6d7d69f7f9d5283f 13 f3a832808a00f63c9ef2fc89169930d49a51e1920090dc76b2ce6bd5a47f3df6 11a85a6ba398c2c4ffd786ea5ba0fd51eaffd74d8a83bf7926d5368d8420d563 377341d0a411ea1adcc02ed6f6f2d26136e1e1e4eed08a2dd0184ebf04586b06 429f69815b223564849e5835e23e0dcce800305bcc018fd0a601ac9b655f4544 575c004c556a7e462f89399d8ef1cfada5f6c5d6c566f772a78149ecb7a144ef 29bc1e6eb1d158bd25b74f78240296c3bb4ae5d5dd7a25ed485398d363e81a57 141be392ec56ea9feb964fc9cb12e4b9dd2b1221f6312b4e6e8af98eb82602eb 2250da1b96aefddd41291cedae262220dd125ddc12f8eee2e9d3d2149d8af939 822889f2f7280f325fa1f87129c71c2160b91efd223f7619ee0b1fb7f2469188 d40582118588e673ab42e2189a5d21687757a6681771e8664f4c0bddef81f620 07c1ef2b0b081c62015bbd2f0a7e51eb15f46dafc8f115ba60bbe2896b858cc1 e9fe4807a0dfd988c86e7ea6bcf5ecbc88e9b635505c1289ee72d11db3cb9af3 7df454eb0320aedff2a3fdeca348cef47a1678ff30271aab92fc8f86b5a72d3b 3561cf38e897d8a5119fe48e3013990409cdc210750c2122e8b4c5c9f46de10524f12006917340772ae12e73ddd24538471905d83d615baea66c4447a6e20a05732d216c7320a6c50ec3f7520888ada51f52e7c3752f6d268eeda0586d5d7700b3e3e3c3847bd0c3fa4567748299d83924bf1be4b2e8f72e2ea7a2b65c467e09357b691ea851a4e72cac164bb12723c985843da353cc6ec734b8c356b6b9700f050a788b6e9205490e0bd29f83025456f1592279194bec573d9193acaa1eb90da0873525ff18ecd6eab2dd5bdda6aea6fd94767c5d1c22995910f6bcb3de28048d4da326698eb79662946b873c6d61b5b6f065e33429ef60c448bc417251c507fcec78ccaf7d86d716c7817f3f7da6eb92864ce3a78f546c5798d5fe88b17a05871c636c8f1da4a2a07678678faee6ec9d18f852e5a4ae2a145f8eb25f6e90c079ce2bd6c91dc93645bde31c7ccd53527825f84d805292bf92c11066fc3e6a0845cefb9fd13584935fb535b356cba426eb7e819ac09d3f78e2f39c0b0b218208669df72685e81379d6cc5b75e501ace9fcef5f080f83c749c2a15f68fd36cd0e85955cd40914871ad306734aba5aa6d4a0652c0ae431dba3ba993443ed3eee0ab2429c7d694799ff057a35caac1548db91fc4620b9382143cd311421ddd4a606fb6b3a39676025d64848071b3f42aa890a684646f9784211f5fa5fb1170678098e188840797b2a6cfaee8e02fb9977ea1ab985301f54eef6f3d48cefa722c405489f437cb360c415f6ea30c8485e8e6ae052501f48cf89c87c664ad836192805b287bf20e99c28804956739a4cba2953cad1142664c857608089fb5b0aeee503ca198b6a5515478b0f9b886f6e04ce469b21e48a564c8dd61cd27296dfc41a03a3b9e7c30e236cff40cba73875eb3e1e676f532fa0c336133356f43fea4f71063b91bbde260c21199b8b01ec56058a7811571a7b6f7f50b21675ef7e756ed007bbb6e9feeb7339ef23eb8e95db7594209e2789c4da7ba9817ebb30b7696030084dfca0d522cb883f087d0e7b9aa5a496e876034fe6e482fe82ee678d1bc67d0755de39dcc603adf091468ced133b8b549935e9474fa91b9eebb437dd4321bf00a42b39d571fc4578c53cb49f81d1fe4a73ca90cb5c9dec6903c9addc75ce8704 false +check_ring_signature bb2e3b54982e6523f722a3739bd3f935facccc32c00916df21c54b16c1ae91f5 ccbfdcd312977dc916cd150ca148f693c45dda93a4da44151e8bbf3b67156ebc 3 4cc323ad732d856d015aa56d8d6726c319b480507756753d19ca54a87c0d4c4a cdfee788ac3b1eff7ce5a4ef327544f9714f2fc4a814faa378699f5680169782 8369faa93f4b4310c1d228be3741d0d81660fe0b3d7d9c19c8a1fe288447ced7 082deb1a466aaddf54973a2d8fb903bb558d76e12e38cd902a360d132852d504397b257657e07b95a830436f78c720a8da6f0de5cd9e32d036d98b802126d40a370c97297ee6b901fd315a61bb4cb3ab737912d3b8ac1967543eee979760bf03cf2e15c45714b48faa481b1667df12eaee010a146f6743dd1cb2ccdff687434a402f6e729f87078aab5d02ee7db0a301fb853f82d1753bc7673f812e6040ce0b631fc729f63d5671d488077129c5175e7557646f42f06cecc87d1e6da2e9b902 false +check_ring_signature 943af0beb1cd3e287e20059408156fc3ee29c63589bb4103e6a9f8517ff24c56 a2d380e872128b5765515b06351380c0c8b9af7042d97c5cff9de89909017def 9 48c70fcfd20476b5e01239324518d1d8ba9eda5efa92087e3e8e654ee3447ca2 5f6054c1df8fc4b2be0116cd11a738054e3dad3e46971567844e39fb31c948a4 b798aed7bd2397f64c0a392e752c955b2e4bdc37b0b0ac172918c6ecf661b779 c5b23a357ef6ba94a3ae80ded82c5c9fc7716c519dc1023db60ed4c3ee589f52 1ae3b83458e7b267189c67b54f891773c02287b55dd39586417c163b0fdcf303 fd4a9761db69dcc04c7300970d831ce945fd3bca3c939af83f95227a8de324cb b7066d8a91f0d6d2fd0b08f736c1de8439555efc36ccb3140a62528fbb8511dd b98b3574847c3c9407e725aab51b8c322a7ae0a010abece673ed5063686468f7 18ad2675011e0511d34516f5b0096abdd8f890723dac57543f8c7be8ee002a2a b855340c2fbb8a05a48d569835408f2ac1dff6b3391bd177c9cf3f3b0e455800364709c4b0ec92db8dc5d29e32bc3031458d6f5860b8f46e4dcf41792e8aa30fdbaa51262e26cfbee4938a75a7bb12debbe655a13c1775d80c8682dc0e39b00c75ab79504df47e780fd7f6365f6297b048feed89aab5b707963afb42a8a4d2041c8d1137a8637b13a2200df09ec7a5faefbfe993f60724bcf6f27ca59e7dda010130636b18b22667622eccba1053ee8cbb4a4f7f6d8a38fce349907bc4e84c0da833c25f90de88f84ee9606bcdf718c9e3029052b8ed602abcfbf4d7df34e40211d973b96a26ee00fd9d8858ca87a635f01f2d6111533df6c00280f7ce99da02fea34426910e0223af26319ee27e6ebcadb676e708561311e12bee73ac95af08d87a4043ea93fecefaa0adbbc1153195c5635d4adb17a6bfb6a8ccfc4f2a650605a0b8c10d21b419c36173c9e08921bf2354b0e45de9cac08b6a0cb53c721d0926cdf70354431aca5516a576e2e43a720553db4e8dfb2a3f0a9b966e6e65990ba819a5862c927af811b98b3f7e41b8b5d7e9e91807e2ba731d05098ea5ac820f3ae06aa084f85dc623ed7e0c7b232fd2815183115bfaf7a35bb9744382d7c104f8707bd4bc1b75f7b204e6ba4495a4bf5d4fa635b73ff5e603054aac39540b03c1aa1372f99bee7b70401c2ca9d65064496c3e7669400b7f65c29b018474e108def3903355344145e3d5bd8309fda54108730a9522b6294fa18cd27b74ace70edffe544b2ab63a59cd9678883f376b18a21a0b8a0e0e5776bafb611488c3d90a false +check_ring_signature a48bd13e19ffcaf858a6ee488650c3cf7a0c23793f82b22d03f8e9f1110b4b3a cfe0c0bfc58297114becae9f8f44286e1c48e47ee352c0abb16dce8ab089bd2e 1 6499dceecb65fbbce156ac4e48394cb84823c98dd7078b08dd9ef24d1544433d baec32f9068255168bcdeb2ca9f744e8e213858a18d88fb4f541dbb48becc30acac6296c86085dab6ef8ae062668b93517d13fa9c7a7caf8ed4e1d0edda50d0f true +check_ring_signature 37b44a90e08c829c94f09121d18b90515d7d65784982e91e89a1bd0f4c5f7ffc cd3ecb084d790aafca17d3bed41fb7ea32a101c20226237587c60e7bc96827be 1 c7a073be7118675a917f65028ff0ef4bfcea8af854fc5ae4042cb97a73a87c37 60a25e66422bede2c9762857f4e9183f8347b1f105037952c41d12942d4e050de151e9606632db4de4312d63712c9a59b64234f7abb10cc6a1633ca7baf0fb06 false +check_ring_signature dd160e889009a13f4476cca5d3a812fc2c20885c8657a6d2f3e2543a38e2b731 70ad219cbf17cacd78542f650a2cb5dd2df65cb95a05a0851710513183f19f2c 3 01778edccc2c809add71b6786d447e0878988eef23edaab12b58ab35d5a38bc4 efe2a783811bbd26141dad23ed7394f8644acae82bece548b6f36e87507501dd fe4b569e5a6eda73e8ec8c5782828aad63b8582f115ca1c5a8afb519c3b18599 9a6ae230c3d06b1390012a1c4a68df3e98b3573d02e2b40af311896bc78c3603a1a71a30ed0fe2c839a65ba599eeaac89d0f8e1bf82b024c6c0f0f2759a5a5017b5e3a005476730c8c259d929c7aee42d3cf2088f5880210d637cfb6385e4306abdc3fb7f3038d97cee5c8f8d87a29d66aa73e189d24257f69104bd00e0e2107659697a0c6fc1ae73f5b032ffe828f4d98baa832e0074ccd4a583bdc409503048d662abcc010268eaeebb919cac115bd4696575c077be1e39ee66de3b9386a08 true +check_ring_signature b9c11098030c59fa54c4060c392e68b4cf7987f2b27b25886d38b9bf70bca711 457cf0b2479a5fac555bb42c3903f83726c6e486bfe7517cf9e88a418175aaea 13 4bad3c6f93280028e4d423965c4494afcf88157b9127e8e319350bd9fbc45151 3d160198bf092e7f265c78ff98bbc36b5284f50b8e0521c8da1c18ad4b4b2dba e3e5669369e736c03d5d60f4f44a25b4133a21cb5e754c36c026236bb375325d 58df03b31e432f97536a7fc19ed6c45c770ba2d200427f6a474050afb937ceba 9dc2cdf0dcb937ab4c827da904171653098ace3bd833873bbd3e5e89ce167b0d e9fa777e3acf946296b116f7c993bad9c88b8bc5fbd3edeacae2df5944d17a77 a85d0be0b83e25d9e6d2a51867ea8748833d18deffbc85f2a4358554fc9b39e3 560e4a496172b1ac3c97165cb5f4c4c3bd7ba800ae022a88e38c8ef8700f46d2 75fa8429dfed06a09943db9ae9f750b216bc83d7eeaaac3b901ae9716d13c619 8c7fc6e6d0ec76987b02d89d612c2f82b0e060140dcb960640a1f224868f700c 01ebd1d46f57ea368f2a86895ba31381e53a77c40ffcfe640e358ef7d5df7cfd 954502783b027450ae4102702c24cdb15f21bc0a84f78479288b4e47dcb89717 3555eeb3ffd74fb81a17fc69f4d1adb9ab6ff90527030356795fc4cb4fde0df3 c538f49e4599c9a0cb9d166647659e117bd027ee5350512f98f92536e0ac2d0d9c8bc48654c55fa6858598d56ddc9e40f508540f8fb9ba2eb7e3e143a1a366041857dd48e413b2e4d0fccc59b546768f22e3e406edf90638156b01786638fbfcdecba2d8e9abddf6dcab4f5bfb81e3f9c9b98843244b48ea3633a3df1ee5610d3c1837ceadeebd00a50edc8f0bab8937a6dee4ccd85689a0d77b2f4af9ade402bb248f1abb3e80fa35673065982513224f4b5bc2dbd88b82904e48a7941920059e30a55cd335ae5b12923133364461ced669e33b93ef6e67689ad1fc7a57f2064d573491167fe847a06fc9e53281d7a4fa05ebd885bd345c729ed86d15b0f60880f65f9bd3097e9bed0407d5c9e0bf09dc553c992425f949477cb06e745e9a0130cd8aa08bc83e8c3b043dbe4fddd3c303bc4e0eda5e95c1ad6cb75cdb681205489b5256f2f423baaff2b6a40875d5197950930a5aaeb62db6eff35d2d4a8009d76b35bbb298b04ade2339e2080faaee57dfd1733770faffc47e78d58d381504252800cb814412f02e718b216aa11ae495c214af42a57badda070da450349c0e7b1b7a3446f36964994e8fe5fc56c59981630556833c26f42e86262685c5180484bc8f2653f9630ba8ae3b603fa6d74841717f5df0e4c72d0a81f6d19903f4012c9771140e6a8f6838480fa1098ccbfb869f42759dcf749beb17498c8554800a54037eb3827e1a474f5c8ecfa62c72948851e202d1162c229f75ade2628fff0cb17a05260a9f128c98246cddf7bf6bea723b05bc5882cdd49ba566ca03b06801927dbc08b9eef847ea38bb8e960c6f63ad165adccd810c97505db8f201ed3e0bac81a98e6cd56d5d9483e1741a08bff6a009fbf22709e9998355f8fbbcdcbf0323dfc036f75ee0f489e2e12c0bcf35fac176ac4a700343a5373744d030696c036c766345348912e21cab2b820414b621deddd80fa8e5399eba13830bba9bda0584a8dcbcfd850ad5ef0cb32837fbc5cedf9b596a17cb37e218069ea83d4679c071812a8a966fc78321b1ea140a7f1b752027317b3bae8beb07030219ca8e110a1dfb865b96df42970e2817acbcd1c9c180219e93d41ba0daf9b61439d2445001cbd0c54cfd11c7de6fcbbd394c3e4e3a06cdd1ae14362e34951225ab00006a0f false +check_ring_signature 85db263754ae9e738fffc4f133b06b729dde16ac09df0709a0570d63b99079d1 ffddb052ca69f83588491ee9241b1808f0007ec91ec2946a333900de414af5fd 8 c3366fb5cd5785db36ce9190ff643314d22feab3d5e8a160ed57a7499da4d4d8 0f040610ea3725d6efab116fb5d71fc175e7cd65299e9b66bf1b5782e4251af1 9cee6c1a9833268226650e21eb054e5aa916372bff967783ff7309e6b9dc7518 7319f6963635e348a79fb5f324cbf47e68d962633b796ce3fc81e804a1898c80 cc87c63e3c0489a17777d0bb5ca3c45db2ec4593c8d527bc2f274c9d6095303b f3abbaf5bdd06d1d6a4fb8e0c24c3c03970fbb99f86c1d3f8afbcb412f6cee53 a0ba36e6c74a38e0d7e33c08d6442a514c69df3fb001ce902a4316ea2cf7c213 96ea1738906f96f3c4cd35f5b4d50198cffa22d2acd2bf596bb7d3fee0b63005 e04456f678994d44fbc267b3989e5ca0a4d5ebb89ebbf280417fba26e8266d0a37e511f52f055360d0edf0b8f224da4e3472119424c9725304615a7e3f851f04a1339cf82517d10b1f7af23df820065d4935139b872f2d7dcba12cdb674db30dc736f91335b5d4ad2a7e6b50b3da89b99b73167bb64e5b5d27ab56cf4396720d93e46f9e97e95ddb7ff442973cc8a5f276173c6564a8058c9085158d086594055406982aa06ac835e9c4f0ab65cda74a6c59f6674da246b079e90da1553c3c0aa7f39e1fdf119b095953f19f3efb03c159dc8bed91c32b68b246666f486f190bcca441e89327fca5dde05d0d91bf4a4d332ed68c7ccecf1800c43fca0fc9f508ec023b33d89007bb9130f4a04b3c90e98e04b066e94bd1ebd43e0ead58c8dec1d6102f7517cc81047e8eaba47b1bb3a6cee1a7952a7a7a6735f0a35ff8663e090d451089841e37155fac69b3efcda9f62cee7e806a448149e70525ae334d9e0dd991656b1ea137c67270b68aed3bfd7ccfcb7d84a115ea06581e59e9a845aa01ccbd8300207de1ee8ebddae4c9828f5dcb2a379f2eb9940f0ce14b6ca024b60ed7518f99814bff0bf9e4274ad62593e5d1ddb565cbbe81891a6d6cf03a0ebd06992bdf2007d0147864694d6265e9da1b1add76c2b82e2ac7243f19fb24b62c06f3e3b8723bfeecd7fc57e8409ea725e50af49c11b5d0051409e6ef65ce056707 false +check_ring_signature 27e04da28602462c1841188db411dd11d2135f7f542b897bb3fb0e20fb130e8e bf27541768d466f94cc99ae818f16a8f6a58478a459f234bd9e271333d874ed7 14 d6a52e29ff6f89cd5019da91dbd6cbff2fc26872d6d4b8561b71b142fa8221aa 79821555efc9b6f907cbb27993aacb87c819a5c446e2acc0ef4fa4ed3c5f684c 638de1a47552d2e2e6f6be8c88817de15f01822854cd1b41cd5972e8f982fb56 ea7d2708fe00cf96c8658691f6455a08e6155483b75a93f8f14fab97cdcf9b7a a7684111477c02738330e475087b28840dbf76569ee5f39612754b3c76c6a823 056b3fd422a6b069c532adf56a7b6ef4f312ebf0a4068a69180d0775f15f4ee7 58c31bba4edbacdd0447e1aa9166daf30d7e74856bba7653185659406f0d900c daf1c2bd8c4f9d228d2586211ae0a781f791a6b9234da087d16f2e65d13fbe9a 52b55d7040b91ed54d87981d33d2bff111e38f35dfe8198a52115fb7f5514a57 db4c94b7a20983495f8262c3928c64343d0a19e63a7f7f86da862b6dedc560cc e8e387e4095b4ec9887de1f9a553b6adf323fd75e5524915f3cee15932d631c2 cb5bf3601bc5878d46ab4978d6ad40789b2469695f4d7d1ad4c772d9303fb59c e8974cd6f49f688c98210fa98c9421327d09f36b15502599d201e4dd1963d6eb 13d4583a542b07ed85d1b08e87bc1c577caf0ebfba31794e38d4dcab2b951b40 aaed827b86a18410f28aafc5670f5056cd18ada76e0ff36138439558ea80e8067c5d2b43d56de6527f515ba7b5dc35cbebc5e0d7cad0cce496c19f0bf58b420b290a873cbda58fc9e8cc6bcef3adcf4b71a9a4d60bc0dffa063759594ac39703d603e8ccae5ee3a76a7d97b65cbb2be05cfff17c105f20e3a6a27a6906877602393f47a99d792f809e3a57c13b92b1efddf866c37733143684f8a3b9cf5c85072b54251c2ef53f9b0bf4e81a77c4529387ca3bcd78f96a49129229dba3cb4a088e54e28417a555a51420f612396d3156ceb8862880e5978f5ef06b27ca39f503d820eb0aac9394fe0049e9fa1a70b57633e40a9baf993d0ae1bcb0e8844368039e076d82fc2f7a0a9240157aaf010400d3f23cb8db85d568b612b9d7b6e82e713f5c1530f42be50bc4b2709cef7f021659eb71fab48da73c01155885c706d58042c774b815065745d9312ece39cde25cefe961d6266eadca1f957c0dd3d8a50c303c6ff89fe40b9d5347ba5ebf31e22495b12da1d217760ce631bb4b8fa241004fe7c17a1a537a13bb0fd8afd020c324c44d8f7ae91a2ecb025d2da4f3870e0ea4e1568137c876b3d344013c038dd3a2ceccd28f392fbf55b49d73e07e76c00835af13bceb0eea7cc6a92023a76e0adc95537c4b92628cf79fb18443d598270504b5c7ebffcd2ff27b211c49572f3248bdff5ee3ceb551bd6e8a3266d4df2f0511e8804fad431514a8d7f3cdcff3f20f50db49718f28a64f1f528d35ad540b058a0bce17eb8032efeef3528eb8d074da36ae4298956d17d338e5ab439f253f052ba914eead8847523a263b3c9c075a1171c6636a09c056e6b5df8d9def01a903d1727ad303a7f03fee3a9b9a789fc9319be167e4c1e14e32727d8da6f32ca70c1a6511bae49eb8ccf3ac6aab0a49a3f78260ae0cef6630b94b4fc97a6875900f1731e36723f96853a58829219ae5a40e18b76143ef6218926f61ee81a2bca40a788855c1a6dfacf73688114045bad73027b90ed0c232de13b78daa6dca9aba0e1c2194b7862a7d80a7dda8684735081b8de16c4e5d112310987487e7fa79a707ac4313d272d83bf3c932e7bae39ce357878ff7409869092d20e25ebfcd217000c6ad41a58e7bb969ce11b50871253685b0e1652e373c0d6ea19032b79fe1120d9d758274392526ccf2afc26f33a6a0b63c511270d6f87b14c8c2e83555f668046f93e5427718e580ddfa1789c7974324b616282ea566db7a48e2d3d502f84309 false +check_ring_signature 815524631b698c13993b53216a4fb73faf53a22f0a93c792062138f3b9549815 e65cc58bfa88b1ee528df212711dda709c312f81bf4eb4d82058c8c7aeee4236 6 11edf373a79a49736aeec3ac53f77e1a285779ec404ab9752c0f73ed7db523d6 44666aeaf43ee6d4d86e41eaadd7e2a96be36ba5a7616b30b54cfb3ef4b23643 b847e5551958d6b668a08da27585f0299f58497ddceed01e9958d9d73c6576e4 f74de70975b5e45feb7a8a273cdea83fc6063ef56a7843c7f43ee91383db8f55 1f29b46a9e02cad148927f7da6272ae4e0f4947944d593296f9b9c94eee2198a 2760883a11fa8b6ba1d62db56323e667b23e0b1b308860ba02765c989e014e01 172c5c9d1603b5bb5cf00444ca0e0910d237560306632f9c6e8f0efd560b360f001da78ffc7848de9f50c1d2aebd480b9ef246362eccf4b38b0243f258fde803afeb1a6213c1945b8eb29b340124980edb15e26860b2d02dd222eb29cfc9910c74084984be70cdaa544b88d135fedfa5e7f6a5a7b6c50c8b93c8ff7ac275450843ff933f05669be801660142ffea2bb2bbf34ba509608ca6eae5cae9aae50900d298183b32729f7af495de2536c64e4136b16295f27a085a5d39dc63105d00007d1e042f02f3f2330f71e568a0db921b064d951e53e3f15b684d5c83267b7c029264be43f45c6bb1591bb94138eb04eb3053bfdbe150d8aaf58dbf13a71fc50d4724bd23d9251197ee6d44e815f79e6841d6fc605ffe0de7eed2cd8bd7e63a04f12be3d8723c7eb7343be1a647b03275f00c742c2f0f1ccb59f3ae420c5ea801e2ff5c8b0b953cbd0e85da77967fe3b861ce7f520a96fe7d52b572c76b303004680eb3143c5cae17ee74c68ead277a687aa2096424f24cd36383d89cdde04c08 true +check_ring_signature 5289aede6698e3dd72bffa59a3ade4b216af7b5de5a0765514648d612374bb4d a7ea1674229bfabb73757481f33f68580f876da4ab55ae78dffae2b0f0ea6683 2 0fe53de5dcf73e59a087813146ea7cafa8dc02b0c02ea16247bbc0ee2cda3c20 96f7edec9eea236f8a5d53aaa070ae70f68ca98f91ca7fd441effaa6dbbcc040 c65d742a2ad02bcef0864562bfc370f10c43d09e383638d8d2c46edc3c79cc06eef80dd152e22f0bf00fe7c16d0a5f7567fa9ee3e9e5ebcf04d3dc23d3df089ccede1c96d00d1df95b422a1f87d8111d13d9ac175f6c70a14674b2e6b8696807ad2e018f639adea08753ce5a5256890815725692bde4fd8bc01a6a712c6b3d03 false +check_ring_signature 4c1afc82a3fbc7ef2cbc68c083216b1118bf357c0bbf6c68e6496f139587dacd 700a42293633f0ec79e4940a3b83cee4882b061e3e60250e80917f07b661840d 3 84f491c93db43aadb5f34b4c42f617fc397ffd7aaea41f5a45c5949c3af40a17 1d9b687ccae9d28158f9c9d170200c9fea464faf54cf02124dbc3cf5dcf3cdff cf1f1b0fd9438ce6243c3cf953bbf15a4d29791d6eb65af5da434f7bc20f62c9 283ffff3b30b06bcac266c1ee619765f0a0113fdb45ce58608ba004eb0c4ed03960487d12f463e001b88d91b17112fb7d43f2e6c8f5de9f52e08f6eaedbbc803e0998d55837b99b33363b2d1226299db590504c212bed15f4bbb7f5ea1af870ef15c69acccbe36b60771375a170ecb382f47c60708bbdab07d24efecb9dcca008f4c9f53fd0665bf1389161af2a1895c910b24099f11d57d038cb2c45495f20bc207d87b0e32488d83abd65ca8a1bd49757b89202dd6834d49159183151d8c0d true +check_ring_signature c541fc21cc73157bd2e25fd0b19085ea0df9351bf801611cb2005f07aed56c94 16d12314c824d9f7324ade38ddd71ff95525fa40515736f6177ae02cb43b1f5b 25 9c2afc7bb968029d757e290a61b9c53eae30102cb846cfd788d83d76f63e7c65 aafb97f3f3e0d7e0310efdc41506eae9732e0b906b092df566103115c6fffe54 49a7e08da2ecccc7544d42e9036415843a39b5484adf4100672748936bb1fde5 83985f3b7a2808648116cdeef986ad7636239d93852ee5eabbfd0447e72ad21b 77fb7be80cccecfa0340aef1ad7c11120b293fdfa00dddd000f609c637ed55a2 05be8e0af4e74633004795aa65869f2705003e28dbe2832d7359abbe3a53969d 14fd11fed508ea96781bced75c648cbc1ce211de5fb2ce797c166a7c0a5fff01 ed8b5430b698e593c22aaaa580d1013b712066ff8da1a16dd8060fba84c0e64a e358543a0e6741bc33727e08a86d9b1f0ff712fbaabd3854d66009405a4b5750 499a2fd133b3c2f7c7ac873dc4359aee28feb53c70c8de4552580e55dee1e9de 42f34dcf80ebb9a277266f424a3ca9aec413e0cd3418602d2d46568f5103b37d 99bfa31b85a297cc1da66b3456d22cd6bf45d655b259873d9c7236c795385eff c2baea3c91d3a587fcc8738cfc3d496e77e28ec6cf5d27d29820c7baa658a100 5bb79f654445595dd3a94e2a30afb4e3d0b6eba3179be223bc243169226673e9 5987dd728d3e957b11bb2aaefe96d83e989473b2db770185ea1ec75c12c54709 32e957fe13068a27d05e2c6ba3cfb7ab7fe0bb19f89f4d2f4535a5c77cdff4a8 eb24ce7ec9eb8e1bfd4f5edfd42ac8f2fdd2b787806378381e34caa78342d7e5 b1f36d0c08666f36b1d701370dfe5af11ce870043fd4eff1ab7af764a8d659b8 eab83bce7ea0fb17667b67ba0abbc7db5effa1a0d80cf03e43fd2201d7327ca7 f043479891e22c9541569a3524529c4fbfe89a78827ba37e947bb7ff105ddbdb d596c952772c38c928027ecaedda41e6bfd9cffd3a88a1ef3d162526db35c46d 09e42ad1c3639badc8ad1c0044a4712a79d8c026c38a9ad27c6c7d963264b3ac 64c7e62a701988706dfdb5d17c516380261bac0e0333a435d7b5f4c410f233ad a124f88cc2417822dd202854cd0d7947b75c29df1aaa3381a01473ebbe512b68 f4ef85d0d3b1b1f06cc3bd6e2f74fac2274feb4bb2ec243eba0236dc241553dd 4543df770161065787ff412ea9b202653af9604eb6605c6ee5128afad19d9e0158ed8b10ce3b6ea232aefea49b4190aa039306af096d50a36fba327ff3c6510d4abfa4e31f5e3de2bf21238cd28db066bd41a5e9a7e4f7069ee1ddd8af438c08a74ca6a586cf4ffdf2cde13e96760181ac8b3bdc13513142488cd525ec12510887e5e0bb747034b58a18e041d5ae310472902dfcfe0fb53e51bf202c4181110de69ad4c23878e539eb9f459e2c1d0ecd6bcaf8d35b864b651e72e54fb0ce880e88cbe1946e7e527c6c3e4c7578a1f9b5bf2c822decaa44ad6afa85894d56e10bce8a9ced0e9290da50e8a08d15a2a3570a6627f98ccdea08950b511ee9e6af07e559f1c111603bd5b9a758e3a3644a5c28bb23ab5b0250b89bc78b39366b61035bf62c76de341bc0a9250fbc5ab1f2652802befad5348fd3b5e7dd818e994102af8f62e5a598d5ba11254334f2cfdb749cd1cb2eb24eddb202a5f59853102f0b3150f264a714cf33db461e95a9142d7410d8fab11d50c1d686779fdedd9e35042f7e86d6606f047cf74dc5525b5c6973e44c704ffae2c7f2df599b35d1ae590c0ba0934c72fc8927cb98fcd852a654f86803fc346260ed829ee5a3c42f945d0c411441324ff00e65d76ce292a7acf104d1a30178f6aea9d4811595ed7c6708027063283f0b7c68c7837a377457e9556a2d88125eb90d87cbc88f872cd050fe0831c48a6bb143f62a777fa59d72e70ff41d78d884bd735a90dfe2880971a8270b56d4f0199a1819a34fa1e0fd839e21d1de0d60bb2b375a7a5ca4f5af4d57a20e6ae2a2a8290d698771c9dfd268cd9937d7a730099395fc3583a4630911c3f2045f1f0c8f0a425d08960692c66515990e441a98fbffbc6bd67340c558f2ea80f044f8b91db4f4cb064ffca6fda7873a58cc7003a1e8441e9b051e3823b606f5007ce1fd91a0911c444efa97aaa9d4ca9925cbfc758220b15228b7501105f33e051ada8c5ff86ca3b4327bc6e0fb847fcd7286d4a7db21ef8ec793f2f06253ba0bb1018357983af6d241e0370829646c74db87034638e78d4db070e37eb1d3a40818d245d20be25d8879a45ee041689ea0139a7e367c6e61a231bb0aa76ee4b303390b814f7c0355ccc57a2f7b279b87a6d615de00e747795de9b32f075e8e2e0bd454bce3f7af51f3d4cc51d008d14f5be918ece9bb3db1d3b91ed53ff58ff8061b8281f1367813f149830b9e865d8e8d39d82787c1ecce8f8dda437f8557c109ba00a822fe8286541641ec120d4a70a63b56227fc8a8dd480266afe18749ff0b2580b365759ad63ee6a66f02ffebf51479c873b2427ba2f4196cec941cd9200fefc3657059fb2468c41d4a3e919ffc381d4a9197b8c3eae29d7d63e4eb54a10f762ae797c7d9aaee9df1efc1db517d43c0d1bfc646d0985fdabe70d7774e6d09b7c5279360dbbb3d057572ef2adcb5bc5e33d90415de9998c29b1b3d9b0a2d04e5ea8e959b895b3af93d7ef1adb3e10e2a4cce2ca7d0c048ccdd7b4877a95a0cdb9abf642aded61db74521c0a7b4cee5f61159afc5c89e41eab0a44d5c3c0f008b6d575910a0a7e4da354b454a2ae9082c9b063b7914adc09ac6f2d789082e047618673641bb3453f91ed51f7ed55aaf87980fea702d65371f29208ced2c580cfaabe2ce6b295bb6e9f1d441f681f44324e3ee74b4dcf94d299d02e5c1ecef00fff2c13edc1906fd7078eafd08909e34db37282bd354bceb1a1fb4da6ce6680b39c7ab365108436e42b3427349f0fcad746023e5702740bb3f1b53644a618a03b02ab5a93f9767dcdb6b9f14bc6df07ac280fe9d77cc5c1399a1dd19560a370382a1ba53681c32c82700d5f28fb06c217bbdf5a804a3141e97a8dd19f81ac506e157e3784b54bf22384feb79d2ee9cd24533f637ac97eb52fd5a12bd2f7a230dee69eda8d0608008e2bab34bf8ef62fce071b7a3303be728593e7105074c7d07fc02275dc89b69869adfd8e96c8c1d86a100dfbb643077a3568fcc708c9175021d452a2785b8044d93eaeddb433dc8ad414f96a14868cb113570a28579a1ba025833002689d6190e4b00ac4fe7b165d2278c3d1051d8c9ab78a47bc2aabcb6040992dbeee72a7afa07aca4386ea5f73a333c6432f293d4c54b75df1590805c05a5a9e2c466cc4d357e258de5e879f38e0bdc4c2bd6b837f2b91c6d42909d8804bb3bb32e8616aa71d4a7ca3aa45ab96d932fda3c2069d35add13b697966d8108 false +check_ring_signature d5633740fa4ebb88866fcbebe489e8fa9069c0a2adef02628d06cf5a0adcb9fb 8f99745f89f0cfb96cef7a7dcdf83e7d455da3429e20754d70fe8cc4eb249524 2 fe09f575b69b15cfd69ad3c135a2dbc7b15c7f9670ca779baca7d84f07b681a3 ebb0447cfb35e6b222e8835451eeb5a93d378b5c470960b0c2639f57a2b34816 6cb09516b4828fc0ef146df59ed350867d05426467155ed705fd817ff1791c0bfc1db805e6ebe881d04b38f3c3db261c8f230ddcd36373af298358f4bce81d00862b46523a27237156d4f0cfde1cb7f98122b7fe03f908ca22407146e0291004ba917d37b74faf47f020bb2c130dc5e9a7cd7beb50ddb2a4aa1029e4d896990d true +check_ring_signature 5c7a56bf755291730fa8f7339181453f7aafbdacc12d93485a14e3fbe9ef2c32 b57f146c23e84d6321137755d320cf6cd907782c84543f009117885c9c0e08a6 5 e1eab17fdca1d5c2d76ada88e811798a3830175636db53268ce97ddcc077190d 413ba938791518d034b7f4152a0bec24000e3b8435a45158285c75834503c0a5 058a14c806cfd59021b81ab8d1fab6f7e34d2daaca77bb2c9932737ede9fb7f2 6e0ba12bb1aa8fdc94b4488ae76378f657dfe9cd22a8e68f4a87f657916fff68 95ba365a3fbe16910c1585424211183f886273111f9387ccc153fd740b9868d0 5e6e592cf059157af7d6a46378d7df715918ec4baaba49c78772f0666030820a161b09b99bfd17c6945adf1492c42f027fe02d0d4ce3b39a9002ffc3d3357a001e82ec9ed8b73ae3c1795c30cc697edc40e166fe96ab92f4505320536154d90d867c6d3e5b408b5ee391781586e14fbf11be2b0ee5478a8c049ddabaced43706e00333664315e0106fcac56ef53520009e1ee41991a916327ae7eb7f37c5cd7ae7021e5d7edf5271dd59106ce0d7a186dfa6f8862b8cc56e5861eafd236e6a3fb59f55ee2d537342eb617638b4e26aff5613f9804feda8b65e51d49f4cb0720dd5223be377ec54a8b037be309574e2520e3290fd36bb3cfe039107075494ed0d3defa81bac6cfd361841cb972d1aff474fb421e45202719e44a0d25974dc9b0411f75af6efcfc1d07db725e99faf83b76d646435b1b3d9ab1f5a9509d63c4008 false +check_ring_signature b520311e28cd253f1638459fe5069ee664aa9bf06b199e73f05853f17869cbf5 c1a6fc5efc278d70997025ac2fbfde81362c31316a288f933e02ff411949f7c8 1 67b7848a3afe8b913eb92843d6ee9e8138ed01fcc935f4780ad885ed68887cbd d6d700cc06c0355719b9ea3d5693c544bc6e573e5a7623f87b68cd775bfaea1dc678de3e7b419ce5ecff9b6e690ac9f051e11560f8b39ea350e4dfcb397b3305 false +check_ring_signature 64ff505150f9e9c3d2a1afa1387acd968870f3f851986d92209a60d20f0005a2 7dba629c86f9d7f2d070585635c19dfb862538f2b1115060bdeba94dfebd6ae8 33 fbc29cfda7b20e7ca7b28578123cbb5be6caf3ae7ba70eabc90d25bb04d508be 41df36bf57c1dafb7529ed273396216f85bea18da590d31fe4cb05e3ef4166ea 3c08b614355ca3c1e5df3c83baae1e61bb1381124e4f3fbccffb6c2f4126e351 41f11767a3d6333146dab5c8ccf98d630a2ba60850585d5cd5bb2202b770d342 e4c9ce38963b85d695c38c797af812065426a528e9a2aef867215ffe3c78ab9a 312d9ffd93d2efa70f1993ec8efe3200951a5313239572505a6b1eb638c9ab22 186a0f9a49b2a0abda9462f640f3c131b5d9af5ddb82731bd47c7e09ea685010 6971eb123db43c61600c4b020918e1fbab03be7a8c3e8eced675049521e7f324 cf1dbdc7b72f09ec48434092ba0ecb241f8be558a286790431e19f6d01da7539 360a4dc476e5013a931407ff3f8e50bae33b602183a35718a50b4c6d3c3d56ff 891f0a53fd0a87ca24f16fc1cb0628cafc6b3ba8602af7aa086537b8144059df 8f4f9e7ab4d6818cc77ac96869b42b4eec844bb1485aa1fa882cf90af8a2624d 0e2a48a8190e6ccb8caf3df70ceabc72fc8dbd9caf83686ff0af6ee10619f1d1 7d4ce174043c1d9d7e9786b628b08b4a3a8edf9624918128fcc377265e1b6b7b cbb9491c37b7817c6d34ebf253f8d0510facb6ade83cd2b275a064fabccd122a 043635ded16dfee7526ea19a77bcde9f9381d6f05cd6c103b831c0b2497f7441 9605d8e21e33f6d620d720321bfb0af63805778140303fe2025bdeda699d78e7 80b087f7eebb4cbf518233aefe50b31dc886110dcbc9ba2f192e90dc4b6c38ba 0124db7768e9dfdafd39baf4f1dbc7716372af4849cb3fef227d67f04d59551e 315d2ef9314b6aa2de1bc37eb108944fce1b0d139711c4875a2508a668daa3c0 0ceefa96804e074c3914f04ce0760501bf3622f757f7b00c32d7b61feee3c462 ac4f3446e6167a3da8a27f67f292a70af4b086b02964575053b0425eed5eff5d b406954586a4b2aeeb87e7d86677a4ff41c7a8ebbf49742accfcc7438aa7d030 36016fe554fb8420bdd9fa452e12df3fa5221b2a2c669826c3492a98bda4c336 d64cfbbd8354fd72695e2712dbc1ea8f780b509fbb1ccf6af97a616b90e9274f eb5828c5a423cf7ae90117871ccba038212b642032ac648825c2a29dbe9e7c60 48056e496a1d9e4102e9c5ccbcb8ca373adf060f2905b0b8d4a76c9af6bf47f2 b57efee662a06899d5bebdd99a4cb273d6c0aaff6e075ee519d4b2b62747db4c 7c980250d248764b437c48a108026b633d47071779ba95d185d431498be30edd 6eeb351a4df0d2345215e8f5c358eb5d7854b4cbee41dba5f3aafd7931c790c8 a9f25cff3e7c52ca2a309823760311bf0a71b3ab4356cdf6608965c8c9cabac9 80eb0bfed8c0aea73f49a5fb4d84bdffcb73cee2735541b83f18a4b3695d3c26 ec56d868c8df0ad424bea86c40349e23892324ca103a167a6b7b2b88d8822ef0 7ce4f1a2c0e67cd4f88ab84c8d1a4ab94f03f2589050e0c519024024dcbced0a084154e1c42e894af6f4d205cba3cc19eedce0a37ec79de4f1afbda6637a7e01b6f4d4d0fbb63c1bfd5d888bb7a0cabc488b23a75736da56f13c6a8508a05c001827c0e0d75cdeb6efd54a0c463f3ea4b9c5d5ea37345885c4b5900e9e94a10835a3f55f573c8309f1117f54a4a246326d672a2752c71a7d7d7a15462bdee909c0318df9f882b064157575b5694b0de5381b9385ed9df7d4b2546b6e7b604e01e488804a98306797f64c53cafa599720a84c9336faffa3ca5995f462d96ded0cda3a3779004986f5dea46335f4f2d65561d469d3f637b793db98788307ea7800c79903cfbcdc8fb314f2313f9d08bae29893fd8023df1f355d75619f841dcc0218d7c236d150a73320ab259cbd923b19b515ee8ee673824ad7cd397f8bfeee0dda65c6b457d1cd56fbf301d15a16ddfcb67edfe5f222684e1ccb88050899b40ca482dae47d07eb40586794c451813b74490c342afe43ba2fc9b5f0928003350823687083f8dceb7f5983d97e886a5e306f510ea0057a77b5af9e3cc432f0bc0c19a9a7db8f329679f7b3f9b88938437cfcbb071f228040b7647710bd3c240200ba6968ba98570de2e3e62aae8cce532ecea323b522813d96f6a1cf84958392092e7e450233fdfbc51e60d63deb4dafebb4aa9c630c4b6fe06832c76918375601ba1d9172ba66a06f4045a2471753226ae8463c579ce79b323a1cfbbc4ad4560216f84df7801f3744b88e82b0f52cf73f0a9c0e9957b88c5587d8e315eef32c0792994624240cabf04ab4faa06709ad9f23cd558b42eca3faacf3d9457b739904e98fb57d9c66d891c9fa3147b444585f8c2b8f0920cf90950eff3599ea31bf0103cd227abf0f4a1131ad0c2a681b28bba3f098c3273a797570ba6e1283724b046429856c13e105214da94152ebc3033de2da5796c5ef78e1b7dc03d5d3faf505c91e0759a38fe2ba72e80ce06d033c11f0cffd0cc1338cff6e646f90922fc30daf5fdedfd9ddfa4f4da47a6fe5656c59102a57c45e307dd19835ffadad3ab50eb53316cac6ad2d6eaa9c7c69fcfe2f87ee4ad4e659b8b7f2904d1762841a7e0a2bb1b729476e6c50afd78f97fd0e05790885fbb956e4853e4a37ebec8f22aa072f3fa50f39d82f67ea1ca7962894004bb3ba545aecb56ea469ff3999087b9c021a69df65645dc2c0417adf86a43131f31530a5443726bb1a341c3ebe9f8c08060a4c345c58fa8cf0f3d3fefc0b3b64d0a37ff3e04ea0b1c86d008d247116710dac3b5abe08bd341ef0da68b078e83c584540a9f184e306316411ba6562c21b0283c45237bec13a1bc59b6758b96afbd075f952f0fbc052e67192ccb84b781106f47a2bf0e8ccad83b223042e7af0f1ad03e37c6f3c3601fb81e55efb6f37ed0a305a364bac6c3d953b596ae5a45056f298901e534243ee341614716e0082740654de19e514ce0849f6c3604a483010ef0fcbca53fc27560d7e3479955f2bd402ead39fb2dd886d30dc8825ac9c1965ae065f5b8efdbf5d406c8ebb72ec09c708aa31e8b8f9a5fb1ddf869536db8e63433d064b4370499cedcbbada5ddc0973005d1d389cd076dddc4fb394b4a14fda181ab6af7a97339a238192c112dd3fb60228f2d03da7651bdfb214857f803b7e711257f9b2e9b24b6cfed60766665ff70313108672be1f67c7e501661781ad10f9c1aa4eadeecc41d14ada943bf78b410cf4226ef3a2b93ca8fba143c74d5d4aebfa3dd94f3fea89b7dc0da35a0bc0710d6a04be789572cef7b68554e823ee20079af2c5d7011177c9cb87f5724f300d0cd41bd66b8f82e2c3db96138cf99dd5313d825d5306ca6966e05996b7fef7ce0878a6261a59ed237c47c3321ae4e43702fbd24ac194cab6e5d615432189c1830514a4ddd394bac25b778a23f16c6383b51d5ec86725540ec46a243d9a6e6a8c00296f1ab9efe318adff8cb5104f4fc2c46ebdc6574c1aede6bd592cd343650901b9071d2f49f109394d4bdefce6043ea41cf6d202fb139fa2e4e7a22e6626a20957030db386f33027ce2cd0da368f9ca4d037c19452c2a60cfd71af87b8d3c002cad54e07556e007ee29c7632a8f3a325f1eae01351a6f37a19fceb5fb6880505a3931f0770c7c6a7b5ef4e059783b73faca237ef874eabc8229047a6fc8e7e026a5ff912a9e2c84129fc087186158ea6cbdcde7e00ac7518b80a30b7a83ea408431c62a758536d1be277a8bc851f88a20db7f76d2ce5496e4af1e9796a73410659939af4eee17c80bc59a29081feeccd1c50474bb39136676970c07827dad00e6adb5b781945d7732a4b8ca9324c9602101cbd844de26b3375e7d75b2afb3202211c9f2a74a9148525c58b1c1a483dec0a7540df679967214f18b8b2240dd2073cb7ccf19f940f0beed245d6048aa50101532416fd8ae813cc0a41e275480d09dfea3fedbe6735fb197ac22b52e8ed9e5666dc182bd1f95e585f9f6821ee790f41a994022ec7de8f2fe6187b4731fff7b596b509760436ee3ad018377f55a00a4eb2860a073576666282cec208431bb08cc59eea120f3c308817b13c2e2cb1033b6d6bea020fd3bafbbab4f9df918790bed3a317fe8a50d70f9f088144caf306ae93d4e3bfc15906425010b260b413015df0f821aed95d051a7d4d1d0310e807b304e42269e38d735f2fdb51d1a6149e69928a941a2f60c899afa18597791704ae6565687ce968ae2b95a885fdea39552885bbe56e4333211b869b391bb44904d0322636663e84e826aff0b3f7d39b5ce552176652d4407909d2d6ae9f70cb05b108a8e4189de3bf37a0a26814526a507543bb0964980a808226387bcbd2e608e9922d52e45f8721fab23e24533f506f65c30229aab913fc8e87c036e3b71805141cba67a8c1959cc49ad4ff6265914996cf7928965e8156153f8d9b8482b709 true +check_ring_signature 080110db935a5d3a2f9e5937e4fbf5986a5508fbde64a53d5c0f6665d303ffa7 549ffdf2baae67269717568a398ae779c8171b872c81dbdf78e53f8d8bef4094 2 cac6803dc15f543c72bdcbfaa37dfe5d0abae999c93ad97f8734a84f9549f1e5 77958d199c54355fc3dfaa47f13c17cab102b34963a28b088871aa24da1b7d2f 8d07b37406a292c8b543d651750b5e9ad6ed739488665571eaa59636ed34f808865080a5d8485b03c954a658705a97993cfefd49981af50eda73b3ff17b79e0ce1dca11314093a09fba9d110540f0520cf705ddb9100988356dd8ab8aebb05981acc86bb453aa2c0b285d5ef6dc046aa2d65437462070d260954056f72fb600c false +check_ring_signature d96cb974a0275caaec47a2825e3912b511bc2c3497049c64b729b5141ee64c02 7450a545c4b7e4f5b721dff6ccbf2579ed8d612e3cc94f0726bf6f73801b0062 122 bdd10ef7914e58c920a74cbac15bf6cfe8861d072b80be15f6e4bdb500754acc afff2cb60a69f506dacb4c6acafb3b0e2e7d71f60a590f0323abce72ce61515e dfe35609851844324c66b6f697329ac0566e6568e22e6b8c6cfc939f9f45be57 ad9bf253224bc8f16e76d174b95c845b9701d4fa62611b331f5ec524721ef59f db259cbdedbc83b52817c2436c9ee34efbff2c97cb9afb260963bb6137943a1f c13fc906698cf5ebcffb2649badd9f60ee4eb7bbb05b609d8193250970536071 40c09ac1756c536f7f0af6ec1a768836ceffe40f9f6264dce2685320ca7bfe76 e1a7d4b5dce7e444f7e1695080ab10615d545ac5af868af6b793e366cc65404c 4e60d178c7e528dee4688a2cb7d1d919647526aba75c76f2127c915250d017bb 43d395544dbad1e584d0de156fbe0bee70ebc49669be27d6c15224cac8b30df5 4de764c0d5cb537741a76d85cd188b2ca3761e0e74701bdfab1e9682a1306df8 3706df22a1652cab6eda7637eefd4d0a2f0feb91e004c3dcabc3d5be2fca08b1 f789af065d9c12c38d4844db8129f4a2f28a20010b12f088cb0f3a329617b683 0d883d6c412c794e65753d8e0840679f3458c4f6d3655ed9debdaab9084ba51a fec1361edec84831a326ac8928699c5606a9b064d8daa3140e05db1d56758484 c8d092dab7b6d720423217af2b8ee06ce0786cfc9c6cff1f62cbf678808c7a9d b8f91dd0ce98959af6be87cc7a10a4c90ab87e6554c5c7d0235c6b515b2f1288 d726c0da8f5604a7d711139c1e4b21e9874ee5a28deb11ec62757810d01eb45b ea61bec2d4b64669773fb27fbe84ad739a5eb4faace937d6f3697b353e896a5f 9bb6b5a409e7d53e5198b126429e926cb6c8e9b1a5dceced6ce607caa0f85576 2b664f37b09489060f4f87f8455bff78c083e6337a18b2ef7f17b91b27ef913e 8db11c880a6a2bddf9e147cf43b4bb9710c720b4960b99640e27a95b6ad9683c 66a2fb41e04902b214732513a0a753808b37d9243ec3bd56e33e6907c3417042 312b427bef7f3a5599f00c227bbbab011238d96f96180ea6fca214366c3d49b9 3c854a1ec5b8fd986c838f14abfa7ba87a466896eb57f7e538a935bd150f051f a6077ba3cb6cb42e400f9a18aae60d69e61fe3518053a1e101446d31db6a114e 4424df21d8ca20203afa60d95da70375fef8ec81614a40f68a0a3a7474e528c3 77f8dce550eb2b8bf866a0bd53e525e5ec468bd3ba21d061e02004e2685a3b93 a5f88bd648e9c4afee7c3f7e4b1872f55b490b541166fa419249b18d3e2a8ea1 88940daa1504327c79a23127aaffc81903908755620ec1133b262a5cd1499d7d 11ddbf8ba12bcf60d03c18444d81cb2f79e4952661fd6a0a76896ca60ab1b684 22ab0f8207541f268425225524104d2284e0bddadf84c291990f1be4f24a3d82 4f6db7b38a7064f7febd25660ccdb1b3702a5ba27858bf661008dd1947ee93a2 2b23d02e61d5a28ae1712820195acdef4d847f4c83b89289c16c0f4e531d61d8 f4e97578790b496cfada15e297630532263cb09a43cb2a2c02ef84987109b9d1 179c51ca0a5537e221157428cf647f3c0cbb2c9d91c86302acf451ae51a0bc4f 4ab70266ff4bb208ce558c5b1641cf2974fb1c13440049d1baabfd4c7c1b1c49 386def2fa68405a7bdafd39f1e8f96e995d90bb6df992f64f52590c817c27392 6b79b3d2a456cc09c54012f66d3af7854c4aaf084855cb476272a62f89f7b65e eae7c2e154a69d0683ab6f48210b786fea28a0147f499f84b3a5b26fb3a8c5ec c971a3e59cdc10d91595c7fa19cb28cbbc2c845701da80dbf8f43e36d4712457 e87e4c7a7ad5542a0569140c03a9309d861a4f9857d1795afeb81e34f0c0106d 87f7fd8c11b35378defaf75c26bc223e92e22af9c293a36876f41f53b1aac8b7 ee94513088ec65798f57a2ccc62a0c658ac28b926b18d499a95ced4d21d1c22d 0a4eefbc2c4774ef4890ce85a948c2e039c22190054d5198f9d599f8c801f075 34f6586984d24969d585c3f79e2a6b21a3a39a71e3e9fefb6862920525f978bd 48b8aa6be9c78fb0152335e4396f626271a62e68efd32441edac87c6e15194bf 04473140bab7c8075d6a121cb6d34cdd27d1c55e88d0e3c5618cd92a83ca5c75 c9e4b0a37f5d18cd1471c6bdb0289a8a84099c3311bb220e304e8080416ffae6 f5f870beaf2934d3d2a3cb8d368e49c7df29e640257b7c3ea57738a8aa67b27b 793ffad4897407575ed1123a0f8e34dc2e390db6fefe92d550f31d34582c6d83 5c0f97be997cece3176d3566914e2e011f0dc4ffec0c7e474195436824d602ad 892ad26679d05186a94661699717faa118f1d674929ba825c835ef9160e00d01 6b8c3fe3e2dce20597dc207d3d06581c78161d5f5a39bce204476fd7463832e4 525e153895d3ebe7128d00216c311a61464cb9029a85474e2051fdff53417cdd 16382996b738994a868af60fb06952b1ab91d2f05c1c4183f5c7029f7b4fe6cd 60ac067bf5ab865b2d664075f7dcb00e30babb7eab9c66ab405698d261440167 6bfadce5adbd80997de83e831687f6c4561b86e7e3a1b615783da3613e1781db 7bb40990e9a285f861ba3219b572a889aafd4f48600c66700129f1f6b4bbd216 9b8884a2f26e81ea49a09bda692498292cc23191b424e6489cffc9373ac3faa8 e42d080ff75a9dc0bd75f5f57741cbf3be9d11c417735a1ea05e382a5edbe8ef 3646ebb1098240bade288a352da7591cd03ab06783e27bb3381384343d274a3e 414ae5358c67f60269167fd821a602b02ea0c40b9e4b533c13ed050ff4caad88 8016d98aa110436502fda86e185b35ebd65114023b66d92f31e90adf7c564f34 939793eea47696aa5bed2bebf78b2d5701169ca3c7473d97a1bd776077c5360c 4c24d3a2b3a0c116dfb69df5713b2056116778ca786c36d32fefbee6d99bf0eb f3a2693a5a2e7eb0bdc6866c6c7dae4ddaf6db80173c5364460452ff3ff836d5 666da26e425b1d0000f2f7ce351d0daaae5d5c3f85700f9949e8f5dc0b8a9885 1ab58df3e9c42f2f4013357458a715d52b44838867368377023efe95d705edd6 b3a582e42cf52c43e70622f86aee9c42bc3a4aeed0498369c7752df8936778bf ed77f446ba4654c5e01b74828fccb9f93a09656335e5fbb0325d1581e5f1493c 6517d372e96da9c9812abf10e485b40afe1c4f7362a94def993b87eb23538ad1 2125b8710cab1282ab27823beeca34b6f0558942e66cec4ba4e3a5c452d3d39b a02d7f2a486d172fc5245b12e569393ee337901e61f9581297ee76a5172f4ebe a017f66aca85b59ed4e419ae002690f69ed94c7767b727450288a31ebc27dc77 6f6e73cc1a84599e98fb257355a86ea1692bb8962c562eb04fa3e1932b383fa8 75b189c0767cef611d07a4f9e76e326bf079ed04748be3427ffdd06fbae43935 2f39a3e3442067264a2dc2e1cac6394fc1e77340c914c783ea1eb348b01d4e58 c8c10ba1539b918930a473c6ceb92fc18d059f357ce8065bb15902604ad1d1f9 16e97dc91259fa0f4721ed012a89a4a7707fb7ddb1483815cbccb949f6d9d7f9 013ec9fa1e539b8a65f132c18f527b9713c5517ff294e5eb40df033396edd5df ae3dad5e92c9feab17c446d6fc3e4af4d60dbe76be698eab0507718054e750f0 b3e805220a20caf7410317fa023acba7b2156c3bf69bcdb1cc05eff52faa197a 885fd5196410292ec8bdd088e09f646ea4f572c795edf3c5e1d11b770f4e243a 613c49ba067a5113f17068c9dd0237c0588a89059a99da4495a7d8ccc3865ce5 97c0c625c4ba94987863968a969621487dc2aa0cc46a87757984dc03e2fac4a0 f0591fe68590ac90ce7b1bce4befc9a0d33f0bfdfe02161cbc18a7bb5fb7519c c5c966b02c3833a5d8b8218088dff17e15f4d62534caab5d75abf833f0aa3846 1510b5789f05eea68341e000f73c0bb73ac7600183b8916446f578d01ada8002 47172e7a008011cc0fdaab584ea87a7edc668985e57e19461a96c3765f366c60 29729ae5880f23a7c84728ce8a3ba5279bd0c0a0ab58655c8ff6a6154d8d451f b0b882785c9377564fcca176d80160c0a0723eba37ca4bcd6a1bdeb54571cef0 239404d14b7762bd6c8c4973b0ae381215079addd2ffc78c1230ac47568dafc1 dc7a3925a6301bd132bd5dfc0f45b170caaeb5f9db9e387938816696d3566f49 10946065a95c7bf37dda58517fe18e0bfe3a6d4a15c0c10de2edce80a5c50177 7148e7f2a80ef723fab3f2852711111f387afa3986bdc5851a0c2b9f5dd85f44 a76c6f116cc437a31eb9f718568be8300d64dbb14b74cccb9a7769d83e59b9d7 32911d06325b9fde0eac17e0a42c5ceedb4a4ed63f1a78513537049b4ee04278 7adfe9144a92a2ffccd4fddebd106ed15c38bc45f696ab723caf2df5b45338c0 87845065ff02b88020f8866ed70393ebb0c4419a4fda5836333008da142c09d9 0d155aa07657d66d063f302a0decc1409052dd8f9cf13e26dfc062e39c0adf34 a3774a740d092e9e177f00c564816495a0980ea630942ba5602843149d5b97e9 e10509d263b977ab04dae3e1e97fc497918b71b66f471faa6acece39a48983dc 87f360f648007719e7e4041437bb0a6a5ef5076919f39e1a530e857043c4423a 665ff28ff715e5b41dfb16132b4b57ac4c6f0dd9fb469b0a7b2102b1fa71e8aa ce4bcc3c6eec81d2f98b91d0870c41eca8dd03295ca80894aad8b9e7066f5cd8 c75a8fa6e1643d321965127ba23f03e34b479e7a894c5ed98fa47ce271205af1 2f54e4fa37ac6e89f4c2b8464b2e4b332371221aa3535208735f3109f25d2fca 988ade1c5e53590f018c35105dbbb8b2fb317b04fa67f98c70d135d0a8c3a953 c402d3d158b1c93ea2c9f1fec1c3775f0d65017b4d99d32c70e41dfc3da2419d e7a6335fc0e66212fabcf95b8886b0d5112b8d5d3cb25db0497df946d7119fa7 695ba210e9f8b0dc6ea70f50b48d0b1726c7802934c9d2ae6081515b9347ddb5 9d03e8e2bd51638c03c2dba6b245e10e7167703e819174e8338708ecf3bd6ec4 7b296dc0b0e54574b691522bd1d8007d4c42cba987462ff20e2d41309dfc97c5 8c6879857b48d9899a872bd8a58b72d76548c8b53dc909956c48534410eeb1a7 b42850fc643195154d47a5cb9718e11bc5e9a9d6ef6e255acc3b5c3f5eb11718 c477294e24c92178975690892a926ee1290db00c1e0f0caa44192f2655839a73 0312f5f9f7dd69f28bf07ef81d2d0ce2594fcc4eaff30f398e50c9b7277fbde6 0c8d8fbb2999ec25b9f8b6ae35365a2201de46a32af144af896f21013b23baa1 58ffe189e93852913d28cf01a1ec098dbc7090df05f1e658574936c241ad05c9 9a857135c79a2f81e6b8d059642fdfcc7a93374b6c7de309f4f4a5d2391a9b16 a575be5e2318c65a1864bfa57e08dac68dd4863e1c6bd0070ae5df3393d77b68 f6049152c907864d3878265f59601239889374696ea49291e8644b6cf239e706faccfa74652e2eb280af4b2621d76fa6bd28d91ce9e6bb7149d2caf3ddba7707cb0be8c5b64d853f0bad502340df4111ff01eb6bb3cf436cc8c65e919ed5870f30e0d92c1139ef82002280ac601e758f87232943cc79b1979f8c6a447b7c260b94c083b32920d51b70a901d264bd286431a2e8f93231c3de078b46cf8918d2087970a2a7c6bea2264c4556353abca63e62ccdec8f6bd728cef9b6f84b36747041c7a2c9b1d4a23df77173d8141158b850c6a5a1bcf0d219189afa783e7ea3d05d1f407a85d8746a21c6bcc5dc36bfc608e4add64001a1614f7ffdbc2068ca503bb669e05a7a68c4890d351e04a8a18dff9748c73f873bcf89dd3f36283f3b804449aca13dc6fdb90d45ac0f0ac0ad4fbe0b3a5cd1b7c604a7a42b476fe812708846e9f73a46190b93a157a7853e3b48c8a09eb375bd7d28dc26c387fb124020fb4cdb47248094c9e5269fda08a7b2b259a1cd85b80a702084a71a05bf5fd650167d8fe4c407aa7cea7662d712fb52f5457d8d945b670e23a8e212d29fe33170320dce413a639c150a9a63eef25a17a9e1a6d8c7ef51a73ca1b649e7aeb27c20d13fa7b7013880513e8826cdb35874ead9e9b009188a49fd5e9a31c0941a28d0871e40e8a43f3b555b28091a641691f93c9e504a83db931d29202862c95c92d0c51016fb37684118a1e654c870cfc807d79ac73bed0be1315cbeb11f309e84201c5a1acd6fea7bda892ccd9c93eafd0412881c43d23529552391f56a9e1449e0fdcaf8ee727a80388fab8aca3bc56cb2e3131bd77fcee53d602ec5bc2d4343a0e81922d6480b0f272851d239061c8c9c2ec6bfa63c2c882a3ddc08ecdd0d2e306f4a83a5ab33c545550ad2facba47fa84d380e2afb6a593da89c2d92ea6ff0c07800fad184ab62594b1d158751d64db708893c3168e9ce2decec2aa9ca2c4430f681c5026e4833e5dcbff61816890c8b067bc6971e73b1feaaeee88ace43e750b8a2510f0e3062b01d753f7e6a7c348b32feca6207f40ab9464e8aca6f5c58903764b9574e026114eac9327bc43cf582507126888a34a06b3fc1f8a58ee824b09934b1ccd6c3ce621df6988e5b4352304140146868a0603c1df11ec83fd49f408de00fc663a6f13cbe07b8ac970a8530706494ffd7f24baad53b6611b47af5901a7d828446dcfceb4a439c5df23156e6c3109315622dc5a55f249fc5dc3ec3d0df64ea1c1f0a80ba47732b07d1caa3a566d026b6a8de2cbdad718a325982af800cb66a47d7126c23f1b7487866827a7b9827f5dedc0afdb53f06767c118deb70184c393500dba86000e9a936491c0b371cfc80b1002723eb9d795d18db72fd20532cced3b05dcbd029bc464535472a65bea1614bc5b1a17fc34f953a36005aa04760f79d8c7f5fa2834473575bd005c1a4114ed0c995925036bb9d6b129ae8f04027ea50e875620c248542ccb03d8c6309c4022caba844b4dfa065e5f3459d00bb3a5123c7aa291ec105179765f372a787771ff88bbffedf7cc6c936329c2ef062f668b267e48b81ed40e719a15cb1e02b70580787f548625751ee20f0e05d106b39f1a3b073b70eb6031f1f281b4c7168f3b10d9aab8c5cd89fa2b5e149bf20f2c6b46366b10acf15cd8938a9d946c7c5bbc5d0506e41b22f9ef2770e9d3d701823e7a82d0a1c6f54eb0a8aa1228ee1129aca11c8b22749ed21f8242675594005febcd08291ec5214a5684d00e1e7cca1d9f2bca3a17e9bb0b52812289fd2601b34f742e25ad1b6c53eaee53e259f2edbaf831be944fb282946512d52dad0e0fd34beb78f0cd3aaaa987b36693836d7dbb1b73646e30997e4b532f5783248a0090ba275be2bab73370ff66affacbba33e74d0bb6ac414b9f76c5fa9b7a4b8b0b2ce5592f8ae15969d244f53e68e2a1f7d9ce777cb89dbe4aed2d5cb81360a100fc54d72e9b92ea0d7b958e2184c9113094d3e05939fec8a5edc5116e7f3d080c8c2289eb45779ee3663576d5f3923a360333d66d7ff5f6627545efe316131c02b9a8f639fd4f421e3c44aaff5684f3836d4245f7db68bc4c687cb28a326c260560f761b5b42cdd834438e45c0e4598a94aed2d00cb306014336db3256fc32a0bbc43a1f1a734d08d32a4b65ab1b9bd08349a8d20cdbb8f60005b8208032cd10036395ab28861afa3a4cc86b8b90eeb878a667d9b9321dfecb857804efe44030c24a7d361c408cb971f849fcd9a9d45172f76e1884343229a9242b6c70de45205e648debe709e744dc2442e52fa0d957e5a5c73f9a02a177d4159a742ea84390d902eca209ac9bd4ee2ca4bc349fdfa7a8bd056851d8eb99c472d68455107e003ac74fc6e2d0772ff538ab200fa81bb37d2e2db972df98ae6d4c3e39bb3e9150fd871122264a04507ed1fe72c2928f5d0208d9dfcbf3e851316f44ba19d347a04db675d1dfede3033a27ed6eed7e5605cfd08508bd20f0f892077f47c624ace02d43e2837e3b6dda02ce250b31fe758511a4ea890572b75be453a049614f0ea0dc1fcde6a105219b7aa7325826e4548810b3394f03f4706de19aeccb96e4a930b0ac767aef495d039cbc71decdb4c92aca6d61f04e75e4f27d8184c507b5aa80a7566feb3d0832926c411875acf16349b7fe87c7d3f6a587c564bd228232e8a08b6846629115928fd22c8ec61f40cf2526e06118e9e6aa1bdf680bc133eeac30846e2a8f8ad76e0956492a5c17442bfa071903a46c9066fcab68830cad78d660dc17db3023483bf6a775fb0851e848eecd9615120743d1d6c5e270007a60c2907fb638e11d7f122a74cc4f6acaa0feea7877c5cd36503b9d035bf928c90aa8607003288b57f1d2ec3a9b231cb88b236cbcc74e8f284a3cad63992605fa2c9950a6c0e50a0ee0d78b6c482f0e26d014f24b7a602d228d7b81e203102aacc60b50a6e568af1b6c9e6ae7bbc0159b88339110edacae76185faf080103295f55b8e0fc52ae2bb9e3b18a34ef0c7b99bf7b7bea8f3e4fafc1cdefbcf006ff3e7338a03081bec662071e4152431148e4ae5d2e69c2fc195688e385f039fcfe17302940a1cbf93864adacbc774f78576b2617acf388412ffc6ff5878cd4b1dc2b805b90cb6c17c1b00a59d2d59ea13e8615ade77a93f9658160079bdbd35aa906a36050bee132450d2c6c41d90a0f0d187926352bd8cf147502e52f596cb0ef042b90e0941cefe3148f8a8eec6019820afdf8836a52e9f26e34c0e4f0af02348573ed501fa5de4a801189c975a68a39c3f572e1872e3ed3891e57eadbc9971ba8c9924000d1b71c18da6ded18c8ba705d5e9e19c137dcd3595b8586087ac4463d30e2b09d886ec495549e1397aaaabbe88b258485a092cc3b8e7548b1e6583790100880f2435532e3cbe78fe42875e671d813bc988374d9387f8a685393c20f620abb902dfbf6c0fd511b82118c7a953f7e29286beee30faa42d409a12af2cc0afc72f046a80f256cde481a0ef2f1eb656b946b9bc109c755a4356d81e4f25c973104d0e795e378639caeb61ebd2eefa42e2b188e91b93e732b1fdf9d806ff4cc4d98c0e603307981b259626a2fffaa3ddf08434cc319b5ae5fd33c46e7de0ae254fba00c4e2dc462164e377d9f70b7e65064c3acec9c0ff4f49b59a2aafd0066ebd7008f124175d10660a1d09d3166073c04d464f1f27af992f1eacea991a780a9bc9094e1e540a4567fd2c5145013b739cfff1da2a857861d1b0f3fb9a969cd6d8e2021b0ca92d2f7404f88d88ddf21486c7b66ae6d9969d219d52759f344d4749d70194e27ac69a586930ec254f2b9806a14f2db2c4ae269780f9c8f6b2b65234720bde99f59a6c7a54a8b4adbdee6bd95e821ae6454d46596932cf33da0b94c12307d102a3536fa40fb98ba84bc51dad957dffabc782651aaf7b2b79f2164fc11001fd8ba79f595555261060f90be257be06329e5fad2e0b6e45861d3328f75f0606eaebdd671567fbaf78384d4a503700241c5e7034d50e1fd3f02cfa8d6f0b42013cd92e8ab511d1ba48132b9eed9274cce432a67d3bc933c58907f34f8aa7d80c951d1a8b9bf26adcf0b20ede9d80d544d9063fd6005f4c40ca7621f0e7f8f701439c5c8effa399a9228c1de0598687feafab921c9c1ea81e2e1895d4ebcea60ea78d9d5f20f106e9bb8d535de2294e32a3d258c0aad712d2a520a3ba712d250a264ae586a96d64ff77bc243c8e0568fa35e6c761ea48b0604cc82d834086460566250e945a60d37642c1f7dbeb90402137234e1e2bc25fea6fd253c4b5864d0fa7c5d8a098acc95c56a57cc25940893987d9ebf8bd0861a452d2e330c062070de2c12ac20c639e57e11c655ce5e4e15553beddebc1007372bf5c9b6ce40b170f7648c3e177ef6f0deeb1aca605c8390fdde663b7b5be7d542d5335bb275fd00d72c592118fb5226333140ebec174f5c297500c82ed4fd454f526f89f7c1d220564e8caecfc810a241220614ecf757ba3db89aed630edf50a520529ec3a570a0dff1938044cc1e03d39ebf01bdc32991900bd821bf5e1a5ac5ee3066f9a009d0a4f7d89c2fe8082e0ec82167a0c21eb29c05e638717c31fcac16a453a888f990444d10566d5af4324328dbedc70f6f3e04524f28b9cecbd3714d9fa08bf7e8e093e8445b0f3df9eb11c643fccfb578bd38218b66e0f806bcb4cd682973a2d3808f1bfbae3125f8a0e7875888d8e61e1b9669c893887e9728dd0e88d6e58807c02a8d8950ee04e45321adca43464700cba9bb13b5dd8657734a6ed25ab95c3f805d6a9b5ef5d99b4271edd1a9c393c7fc6ce745fc0d7fa0faffc9eb9ddcab8730d2415df334e4f4ddeb28a4322454c5a74acbdbbf3e42a37470a4f028e072ff204ac4ee4d2023c401072c3b927b7345e67407f46e511efa6077d851589856346029023641246522a5500c269ac7becfe30cb4a7fed95c11337592a9962a07dee020e776eb146bbe462361acf540d4dfb5732ea71a377524776d06999baae6a100606ee2d92859f068e5604a690a64f9a5ceea28f0ddcb34fc09d69d971561eb0065320126b8eaac93f0ec97cc95f8681e3f74709139cad74fb9f17d8e9c0d2c3041538994b735e30186e736c13ed02c2271796b9f2328035b26aa1cb502559fe058b87a3f4168c50ab8e07512f59dafab50a48ead45e465eb164feb8247ba7c5005dbda02a0da12aafa23009d338eb85627581af8268562ec53bfcf9f570cf090ce6e7b4d1347c66f83ff047f040f4d92a6fe7d971ed048e89b775c9f5d0f2260c63a4a68ab478eda9493c1a9aa89e3cdb14e7eb70d84bbe8ae1d578aff246df05ff4b48fa5a83fee48bce4677be265c2eae813cda0ccd3ab0ebcd297188adc40c162bed38c390b0be189bb9c19e157f2467840981e28fd814b440517be94f6f0451ca712722a7453ef938b359cb3310d50910ddc6652c17695b6dd1e47f860802137721dfc6403e8ea9c7f42624f89a745dc1808af723339743fb3cf8e3af440683f4157ca239b94d6195f5285c44436b6f6acfcfc106273f76ad02576e49d405d7b2a2e67c8896e48681de253db6488f9ce472dc8e615cb42566346c3370290d89ec5244ec602a4f0b45792b216ac4312549330fec184387bcce7ed50399b600f741c68c179ac5715cacfec974e74bbe80f7d162a89ddba54cd04710dba58e091f7a781d1471ef9fb61431f6cb524c5b97a0790bd7dc35ffb8ecb7a87f9b3e023c8f59404123b3e99a4ef355f485bcd2903b2c1a29af8de0c750cb7c5d2a9a09e16e043ea5bab700d1c44b7d6423360f7dba4ea71570141ee71803604a4d740ea536e2bebd8049efb9df4a08c415df93bf2d3d59fed80ee1308994e08fe28102e6947b83f31dc64167154c6fc54fe96f02f68762696e2f6e19470f0629c2480d80978c0a042dbd7b7c41694ebacdce2e72d16d539b7cc6d84dce27de0ffc3e0d4e56106c5997bfe6b6f20b6cb0547b952dbbd39bc30cd8937cd501a99cb4f40a8e1a083aa2339f7035b5463222956cb9ce16ad8fb4a5d14895f367c25acf380cc16cdcdc70ea41fcdfb0d79effbc835255bf2195230228bb2a4d32a9c6cd5b0d73cd9303235499cc26bf73ee7e66a1714095c3f270eb692cd154ed302f0ad7021d46d73b14e97ba560c355c5a435557fdec1fe0fb113c0f13aedfac103b218066a0c9dbbc751a9584e98bce1f9660e8317491290215051ad41315849f8d0cb0b47387d1919e8fe311afa54b88ddcd9d20d1d381900178bc235dc3019e871880585e21587aa3beca36b545706b70e446e96a1541f796161f5247c81cc7f6b5f0506fe6d957a68399a5b4e8e6ee1486c2fcb9c614bccc29480822ec94f20677409d7da9a118da3044ab5ebba0587a4ac0352574fac27e2330ce01e9fda594d8b01a370656a2b794f5e2b57db5639db7287f0aa1fae665be0bb25454f17ca7b8509c0b4f911188ac0bb03867d647a59ed1d4e3b948715f62899725f91d4e2c2f50d8155878c4017b7e39865701c423003bb5bee72c38c27327696c5f059d28b580c9a2e325839f40dd5a933523c2ce9df5c4620740ff0437044e2bb13709c3ead0d6c4faa45d4af98b856895da678b618516c718fdfacb6558e486bd81a7fee0302d59707a411f2850647c8bdceda5440c2c2ea8483d68be0ed0f4f592639c0870351a159ac9069c93d65d8067deff5bde55f9906c820c77f4bdfd28f06371fc40d0a60393a173746c51411c88e230da51252d9b10416efc7696d464eaa86456a0489bec782c04d69bc3dc3e0e42c63a5007dec891555a7442c8cb0ac79add3c605ae1f836cf81c3ad4e063bd31313232b1bf114272d8c4924c8440bb6712a42408702256e3180bcc4aa5cb2f3adb050984c8a63d4eca513d67ece8357c61ab6d0808d092d83356ac040ff307eeeb7e6ee09348f9b756e30a2a22babcf578e26506929316d9f3b73a625e82ff4715ac580ecd288c2193ad30c2cc2d90a8c218d40a4dd5a717fcd7097f36a935ae98597b48753ffd013efd88b9a23695e59985940ce4b10bbf026fefaeb087333cf0553b2493cbe57545a5eafc8f15f315b8653000582a4069e6535eca800156d77650312564e76259dde6c1d693b2191ceffff50d87df4a068667494fe8b73f112ef8c2eb282ed0fd9fb300515b8b7de45546330632fad178cf540324cb4a996458d7775eb9a007dcd9fd8677fc7599beb2dcee021979e7b6981d6f16c6f0597daa1b2baece3afbb3dc8047373b258f15a38fbb3d6122329e4fd322b509b8a737292d74b3f9631ec4c770d5694ae523f1cfc4a409f13131b19aac1fd6e0bd14e7dcb4d8416129cc1acd4cf68a5addc99f00718a06664d4082681d509a3a9aad2d5f3d0ac5b1514882194395a5653a7c4abb3e9e0014087a284d8677ef1e84d216437dac331278fa72e6a7c5f4e0cf4360c3ec030c97622b58855907227987ab9a57558c409cafb9e8d4161ee3ae7fcff685a34d06c302f9b3270b97e7ef41b4ea0df45d3913d8b9f021278d48a6bb384ba2f1fd01263e4c5d3b014690ba44041fcd3d0f6dc2ae492aff39d1d51229a4f168ba8308a5df50b9d6e019f37ffdc61a42ecbb261a2d29d3b404b7ab55b76bf994cb760352b7262ddc2a3d6f47ce6df7e307a0c025c7960b77c4b4e910ffb8f7646db005efcba253ff55344b7578fc02c8f52936e1d884f44cd49573b9d2c24861f2bf057f1763d68e2a5ef1cda5e2583afb45c3e1a3bb0f669d907df639e9307b39ac0734adb13aea18c0f2cd70ae2b850c4a1d36c5ca0d719118970b5c1ae55bd1040dee227b8fc1430c5de5c9e366fce116958ee60cd0d3b9396ce6b64c5da13f6f00050baee58e4254435e0a5074c3cf1e6361df01b22e7aec7529cc1ad419bae2058c6cad4ebd8b536fe35f1dd86f2b119090edd143608a3f050b2850278e92ca0b3280ebcbdc9d840b077dc1ecbd7a33f787daf1d6c84fe0b83c3b9251ebd8d504df6c3e33c1a622f76e64fb31d913d98a31f4d69759f80c8962d677a7fd85fa03244fd34c3a03bf2fffbc2a1f4b2e2fb873c6ce6e90cf5cde3c68c7f5ee42600a6d2c44e7c9756a5015a281342d15f8c73a4d62ba57296d939d0907afe32fe9055c63612fce21635ce5f573f06708e03467998e0f96a2cf995bc426846d784e04d170c11650112fbdf2fe0b9aebb361985b3ea71b005f18ba7fdae6c126a981073500bf84868cc5e94fd1667900e1dfca0fb9d2cbf7eda04e14d4dca63bede606937e5e8dcdb0052b332a7f05bf465eaba95e44e9a7e388ec7193bcd8cc2f160511422c2b6ce3e77f40491ea2fc0b989ddb753e84165a5fbb66dd10fe212c360e3b5ce04f69dafcaf18a34f551000ad4aff99866ec18652386296223cf939780f66356ebca900ea3b7f293552f36c129af4c8add775f93c0a1a2fa10eb50c910ba0cac0ff4936f3d23167a377c8d3fafdd0270d09e6d49b18f66d883f8c3f260a614e54d951d8c909bdd3f42650068f45da78f0a513696d8fb4215a913a8f390c4cc7beacfff3d9ea86e8cd92b62ec01afe2ed2c59c49bc8281660fe7e6720f08e7b406e7f58da5eff9d4526511c3c3dd93d70a57b432084b0117f4d3ac464407dd0146e032fa56f7674335f8f4424646ab6bbd95dae0ea1720b53512023a830f7597731325d3f366377e7ea75c196b4daf81a95dc37b5a139332950f4bf08302797d9d4677f2f950b8514306a4b74bd7c479ee9245d58ac6f381d2fafbd1e70479b6460e5a91a8c11e75bff2bab21348f52fc2b02c67ebe05be604aa6c63ad003fa15bf026d58d010341ee08d3c9c35624f4101501d40ddbd3bb7d90e22c3202c79d3b373180b5bdddd6c27de4d4c97527704c22e7d13be6b44f2c891e06ff0bb6e9b7c5821714ab84abc15509d141b237ed901eda2cf01d7dc68803f48afe03e4f39e7f2a9f01727b728daa4c388730d9c382245448d2ca1d482145606cc400fb90ba269dec4fc2ec68abc0aa7af74e6eed3314a9a9fb9127d81e96f30a68060a2fb1343e4610de65904cb55a236f1e02acd5d87b7db34b95e11cf24d2dbd0aa71f097d390bee136d9f037b0a86689a575be56fbe28386ea7a47bff83810307913954061b549a1cd2758b6d136679338938ab91e07267922a8bc1a0e5948d079bd426b02d352ada58a9376c412bd7fcc6d256df69fb618c3e58f5b855c29b0035bf0b5693f5f88ee29f2f41af27a3ce3ad05e0ee404b5448c669eb41d490a0718259654c95af89c42320d79f9f6b02a592befd1c4188fa61da6123625c9890160f3d8f22dbd789810a8a91712a9c02a4509f4c9fd5085ece26083734927cf0ca1d248590f96acd8e408a7431f9dd024a117fc3c87086e2b7149f0b099fcb70b9e4290ed35252573cbb474157e412afe50b6b53eeae6fda08935b66c73a60808f4d0454ca1a3a0c48b22a2f9c2eae2131b2337c796b0a1b700482315e37eed0c718544be1d95abb5648360f598cd96301a29ea4ebccf8bdc1942e32a12938601e58ec536158972d2748075cf7274d680675307d10451595cf5eac821b47a7e02e2e6077a68caaecb867cfcf472cd1e4eb5336d0bd5967205a2069240b41bed02bab44237f56d880072cf42085ebd40db09f928e00bf5f9f651552150f35b840e958f3c70b1eaa16fb86409d877e9ec28e5c7ccf7b83eabe30d98bcdbdfa8f50ddb4083a343b8bf1416cf12b10d6282d9274f05b761c25e446df1e5bd22bd4e03d22a3627a108b081dccc0d4c80c0aa8052e51dbdf0a510fac0e55134c0f27708cc9b5ae7a703b39beb65384114005fe5985c6e53d72d6e91db69aa26ba822e050007b6fa2e7e3c8cbdfd15390b6ee65fa8a17aff1329cadcef933ab9c84a070618ee60f525a3f5282201f4c85cb52ae517c13534f63ce06f7e7f734b9bf461089e9452fd0bafbc824530bfbf5d98e0cd21ec3e01e17e16d25a7ffb5b6166710215fe8b655525e55460a9043d6de9249faa8ab6c872e4d7d8469f0be24d18140790550f6bac804cb8d2f9f4dbc6793fc68ff689983cac35a90f77075e114fe20320e5e912c8ddfe719f0a0bdff5ddf4ce8f70ecd7c80576c9ac5d17b2b2f09f04a9dcd933c041c33cbb8c72190e31f42aedf1a676f59996608c2de98697e542015fef22ec2c61143aaaf69fc48498071f63709dd84647da34c59b1b3510ef180dd91b690579cb2a20dcb0fb1087676af8ff2a7791d1d4db4dc6b9657606c46e070348d70f48a77bea10a1d50037bd2852796ff1b0f3e8989169bbe5a222b99e0d76f1ef81c71d5bb9386c0f893ffc142636c60f119df30f1c95ff43bb81167f05a5e4fd52b4cbebf6f5f8a6f85e64d6782287bccadf5ecbfb92a5f50760aea60445d31add5e7a1bf45392e10cb21f3f76e5b978afe214edd5bc7781397a1c1404ee3858912b18ce728c0643bb581f33faaad68be1dc8f6a728d4df39f3792d707c9070a16b0213084304c9f6746cd4bf9c04160fd8b1923ff85d2d66842610304273de4454d7a651e560131a720533bfe1bba9e7e359742b80b161d503b0d5d068fe004b4adf77a44bb45b800e9740da9274081ecf5811a4b1c9fe81c9599e70f6015e67156e45e7acf6ce6a9c14fdd1972e52616820b24f1d0d4b0ea3a08bb0cf2ee19aacd65f250d3a28a2a3181c267efb28997330dbce2a288f071707a15023d5cad5f85b8ef1e52c905e2bfddd303a502d024f961fc6d09d7ae05af928300410e182b94c9098afbf1fb9112b15fa1d85e6569c3dd13eb30baf8ce01f7af0140baf595b14abaff9bd902e3a6e9b37023581b1fb2b9d01d5dac6f2578d6680f681d754c6a4a560ab9ab7269f28d3efabe06950165477e32539fb003611c7105841cf72903d46c0c10f4c3eb5878bb92af8486dedd37c4029f528f32ce5d3f011ccec08865a6301c00e9b097a282e9c1b0a663f6246ed9b7f3e56c80e44bfa02 false +check_ring_signature 7d54a6b61cf60c8b2cda6a00c5797ec8e61c50f40cfb1158f1cea76201e64f5a 3341136280d93d6eaa6abc0d98e1b65520d686c13d1b760cfc728f8a5cdccddf 8 39ad7267759eb6c82888fa9d8e97bb509459a771e0865a1badd58e9b4ddc8475 82a806e548336561cfedf95940420b9be2c974e77fc7db3efa19a01dfbaa395b cdda63abfc959e4ff25861aa3a6e7f02231e58a580cda8db7f5794f9f6a03cd8 b948716963cb3963a4893663dc0fb44fb4002bb72847bdeb8f51f6f227265440 e7522c3a9ef5aef6a4cc4ca6e55b50f85859b13b786e65c289c264d64502ca0c c09d85461e2253292b0768030297ae40abd19ee9146b27cd95aa639ad5d1da0d d70cfd6c85b738cf1d6f3161b41aca6801df3249fc4227d5dd18f55c00fe6a5c 2b1f5a2d78ce33e71f2b418f556dd969a9dced2d48956ef304242961b7a8ba55 5c72e2579fb0ed83b84455252fe4a6a662248362a96b8e409171387111f70a0ec569c2a3d339fe1d474e5c405d0dd2ea7b05ad2e4da3315f34ba823c93d58405816682f0ccee0747ff203f8b83e8de3378d3d492bf72b5540fd486b54f92380c0c1bc759eb2abc75d2cb3f6644e8114b1fc53a2e0e65c5055b6138976a12520a3ed4993e9f368d033943a687331ae4308c7ba411bdfc613d898fccfa6ba5270b1aaa14dcba3690a6a195eb0ae79c4c217f5e9eef168b2b4bc317debb18babc0c4af7d2f4e27b4dee987c8cfeff63c61c98e59fd730a20bb6f654431cb8316e012e00554eb900c00f3346b7368c69307ffa3f50654201cf3e1aa2143c0140f30c8810ba9811c38f51c7d455fb9b638114b59a06c485ce626a86c7d80aeccde00e8c88030183c71a8505c15822ea563336be9bfba658bce45362f0228637766c01a2c6331eb79c54fb77e74dda34c8e22628c7905cd0e39c1bae6965b0dc2f8603dafac9a100c0876dddf0fe799238dfb7d0975b8e68a69321570adc2bfab6130de2ab024a663db9abb42d67d959a284d32e25c07b58f5d279f9dee1602266820449e0aa8a566544fb37a64d719d8dbc52334d82833e3b83479671230a4959b605a911151b72158c4fc67d6f10fe81f2f2ee7e06e705e2142ecf5352eed91a16062abb980be6299b68060d45bc75cdd7debf6641090ccae02118d6f19000a38a0c false +check_ring_signature 8822b116a3452bc801a17ab2fe3b691174479c00b56c178383d99893261c1aab 995b17a8409424b57e044b3ef984d98ac23be928c802121df89eb86d16cdb994 1 dea671d7da08df15ab082cbbd5d2b3e2e7a366ef6b89100b093c3da9669efaa6 a30c6073e5fabb0d927440b9b27ff62f10ffa228ef9b24985a8c849150ea6e0e5ee94fba7b462a67aefa5bb0bd681e48db7765261608f14270143c04921b2f01 false +check_ring_signature d811db11bd619e1ab9ddb5b351728c4d3adc9609501a1cf8e71840ae9edcb053 0dddfc44be8493c5990621ec5d04f0db195e46f85720c1e456086c67fc8e37ea 28 766e512b29c73f7fd85d65feb1cf07bfeabb58160da85cf4940bf8956fc5cf4d 7411327516e8594f4603dc9e3f72546ec7cb9248bef0121bbe2de69bb5e9321b 89e58e371f3cb30d2cd9c4dd08ecc88b95922a4d7ee00fa6c76656d80e26e97b 73c4c5100d83468f31061c0d14954afbd9b3853d688c2c63844f1a8a9200ebab 26ecfa41d8e0d32972983ac6d4a43a41513bc8c41b7571f3cd9d03f9a776a2cd f22ae577b6687cb54aaa41371a228f920d3834d733b682be4800fc4b7881d2a0 9c6d793aa4725206debea8206b9f4d24e910267953fabc39cff76584dde2816b b3fe4cde4aac62a883fd89a7123b365ea7da8d04cd409a88d5b2ad1a7c2c0739 242a1eaa128d1a3690a736afcc081120aa631e208de8020ef8e594c3f03913d8 c31c95d06f0ff0bf85799fcb2b34ab3a12d6416da3f3facce36ccf2863b26f53 b06f0bb47dcfc70df7cc07fddd48b05618a5d8b05e0df4c7a0bcc4cf11ddbd07 1bfb5eeb387f098775aa71a7901a3fb81c328309bc0dba857c05234dbe05e4de 27aeff4e3a2570690d2b55f055d8bf561ea807d39beceb81ce608aa0a232101c d3d279eca91400cf392622787b63aad36a50596dd147b8c60ab3dbfe472fc73a 52f36f361f7eb317eb5c00a8ce180c605d2ddc80b5ed83d6c4d9999e364eab7b 6a6bbc8b39d57cab996a360e0f8cdda026452e943088c486b26ad99bed014133 ed2f2f644b27a2df9bf3e16ad2e41f40ae96198b94a2180a14b5c2b69090c662 e9fcc107342e1beef1ffabca47f572aec091dd68d42c8c986fbc0c51a86daed5 4f0ccb4e3cf07a26bb01d0cc89970933df6cf21d479b2ba0acb41ea2abdfdbc5 e984e70ef60bcc9061074682c03ac9ca7997f5ca47a1d4894142940e0b369793 9588fd28fd7bab422645b5e1c7b25f320f48521fcd8fb31755005388300f7dff 3494ff12be152d71b4e91096151c0942c6e7377c297ec98bf7386fb275dc2037 e1a9591c20d90436c75530c99c263a2c6cb614df4826578dc6f262d1d503e65c c3f638689c75d9a37ed9396e47fa853c1e3645bd635ebd1e9a19454f1d32da3b bb2b01ad3f6ad8ea4d1cd420427be86d8d85b48079c749b92d421bb771b71d18 ecb892b732af6ba64171729baa86553c529b94ab1871567d61086f395b9f7242 7abc8c7f4d3a56f756b8266550ff4b696aa796a1132d98531b600a48991d1ee3 6beeedac4f132dae8b66c66df47a8c90efdcb548189fd2343c6afeb361a851ca a27bfb4b074b9e2698cfcbd47ad1392f6fa94c2c37d4b2ba47a7ba944ad9400669dfdc4ebd5eeefebc2e8cf702f7bb072d692d98cddc1586f2e53d8eb36c4d0dd120addc706fc8cb486367bf98d82f77c05ac78714353cd7e14522d0b0a8a00ed9b4011525e02a9125ef730db6bc2d3af565be23d3627d387ede5111aaa6cb01634d9650a34915ae8c5a1736f8585cc6fdc2369e9e25a18db25619b5b964a305be28eed47e0a8519f7678afcab36dd7811f2addb676084a348af08393ae8830b73c0759b288e20cf6343d6ea549c1202303c485275ab2763e7e2c18b56859b01ecdca2c1e49286d40500ef3952da1033f44f94b7629a736a0b8edffdc0314908b1026d491efc8e3c7974d4fcd743607e1a00906b22f97bf1da9513a6b9877e00ea8eaf2f39687b38320215b3692edda7b11531185be7738188d70c13caf8ba02409f463ed150eb801793bfcdfd1f46408f976d95558c3044062d780927dff10ea0020a935d45f36bcae4f00b0102da52159ce3d6bb6e99db1bd95eadea5a580385aa64ecd2f772143753da1fd96da11bb16474bab63d6fbcd3197db1ea4b460f7b1d723bbd303925ab5e1390ef828afb5140b86bbf4dab81badf1b206ef82406732871b9d29e3d7c3ab284c728e58c260748f5444528cfd2962c1d1bf56aa70104879759fcde24c6babfd22801e1b9be1140de62c9f0cae889c02f8157e1d206e766e76dc757631597da9e6e2e873c04face4e031a5d3d5924cb7855b1179c07a41d64973dc59a57f7e492851f7733211879f5ea6c3041cffe2f8e913740f30babfeec069f4bb5553c5155bfe6285dec1919382277dd3517f5b9e1dc0221e30103ad92338e443fd84b03ad4385c5e6b58f2b9b3c646bf4c919d5dee623bf2100cc2dec4db068cc30675a2b50b0e6776cc4d47083ecc249e08dab8ea54a1edf09c22f72a063c183cb9584b199902b8b4eebfe931e29395f39ce7604afde52f805fbc36fa502b270a0431e0bee60da36656dbf91b084f3d1001dd0779cc6ff01024adc3b9086abfc91bff16e4d838133931966b07b2727accc563e44494d48d10d5fc96ad81253547b33b1dba90e56f7d42e3793160a67d79aeed7d23bb23ca80f4e1d0e193d6726598b75150c6295da74374106ab13a14a09d38d92fc39e6130518f4c149edd444f85b318a0f15bb35edc1af4b2de942ed6b2633f5880d47a40ae292f34ce80c766dd5ee7b0667fbe2d0e7a331e3f1e8571d106ca719a9090206dca961f7a703375b4188b02dc9d4a7c27363cf05e9aa75bda63f7ce2f751a308a18239e8ea4c0354100f6c4d8949dcd897fdce6c5feed6e8ca1788a01e79f2098f7673c38984ff0e18e2b8af881f5f2c3b3e1bcb85745e8c8304ebb8fdd9a806914bfda6db41c3c9c7e024a30b6ca42106bc14591ba1d02fde426e08bf44160dc3f9fe8ddd6a9b7c3541aab83eccb0c1052b453b9066c046fe1d71d761121a0d86ed57399b86eae2db0e044cba92ad908014c5753eea1b623a88c35ec375100e04f009f3217ecaa5c4178745067be9152a2bc5d80b91ac29d0274b1a0909620eebca809510013e1ed34d16aba1051a68ee45e92912223df0573fd71d7a0f210dde74ace51fc28ea624f44fb7a477a1ccbe9c4517cf2389f30b109176e7e64b03a8bc66265a8aed53fe1d97b0741024c1724c4cf3475370dc6f3c7724459ae306e7d4fb397fe360df513896b78700da1e0f73d0aac924801c270908c8535a1c0ac0f1798c901ebd67e69e6f2eb952647fd267fe08c1415ea8905db94328e06505abf9e386f3aea55fff9f211156d98560c9394497b620657cf19d04553f01f80be3f62460c60f000175d156fc48468c460424e6fc448c65449d6f35d47d6ffe0566842b636c4c4bddc29c7a07798676b0b87b4cbd5846822825e60145db8378083c4c2205022f96df34bc3993c067d29e608670fcff48a51334ba7202ea4c2b08081a280f6f9b9c082c6249e28d488127954b903b356e0adc453e834b021d770d3ab3d2bc998ef1bd2c2847f0dca371b4384276a00bb2388cfe7f85afbd894d0332de7f28380dd52e234d04b82feaadbda319da0996757fc4c55515a58e36f906464f90510321b064a3f574fd800f918a4746e088d4f2ae9946f9cac5418997099b037afd4596c5c6d66daaf1586dae2993993c66b7ffae8881d19f2f094dd10473f1881ecab5895aeae28b78f8c74f51dd197f0f7e7bfd0efc5320002b736106ecef9c47156826370578bf401482bfed155343a7e5020e5ef1283d4d6df6c900ebcf931c3295d8ef0b93a62bb4df6d0bc7bfdc6b236fef0bbcefecda81483900036ea58c95bbda62af223cc47510dc830887ff447a872d6196157eafaccbc10b2ba8e2ec1f6ed9a851ce488bd76de5f2a5dfa7df64055cd658f4026e000fd809e2d813d2870212c01db48278f3ab6adc72328d32e344043d3e50d68614cfeb0d08dc550a066272773f15581b57280148ebccad417a6093fd093b054e7aeeb00d false +check_ring_signature 7f8e6accb3a7b6597be99b6cd3d51898dfc4d3e55d1eedb6f2421baa7a502001 2424426d232c5fb53964e5d7ad9c34ad2ee64524f461a393e3cc06626477f8dd 2 69eae83fc03661256320f9bb59562bbcd617fe7942b60e01d6ff14d5cd4c2e63 eb02f9c176dfb6adc61d25df6419eb61bdeb092292c41eed0431e3ce122040ad 781d5cef74de577d6c8150ddb287f413ebbb0602154bf23e5131c7b16a16a00c30637b2ca3c457b46d9ee1bebd456c6be58cacb624e915d2c58bfc35d3ff81097cd8b0e7f1d301f7772e9ea8a98c3e43070bed18b46468fd6075e81ed67a6a0d172ac55082095fa01abf36ec7394b672301423bc3a9f084a916073fc4390a808 false +check_ring_signature 0861c902a24ef1eb5b0159b792ac91e4611c6af3a0277930c351218737ee2247 1fd6ac016e9e7b7f40583875e785f077e6290db099e139a39c26dd407eca26af 2 a885b168ed478c6eba1098340ba81e9a8f553feec6191e2ad644ed83549d0d7a 8b44665eb5be909d9b64c0a07ede38bd909927baeb8b32989104efe5a55ef7bb 8ed5f3b94b02a0eee095512636c55ad5332965e0e56825be34074e2f065c15024e1e1fe38010ec2da944b4243c7496b6606c71ac5da3630c2a063b2da17cc10542185b77894c5372909faab86c4ccd04bb55244b04d0d41ae2ecd9e956b040039b05f6a21aa60732c08b912a173248861213a6ffd78764119a639faf5036fc01 true +check_ring_signature fc0acf8b0028d35095c2a4ea682bb71f31102d754f3b540d24ccc4f96a043e00 6a56eab5ab21ef2fb121a25dbc99bb5d9e5eafe732ddf8f999a86fad82f47692 136 b4ef481156c15e0743cc685f5285c2961c6bca7c2f68c93f32d06e85a0c19991 f4ede55cd86a3980144a9d45d809fa8d4c309772cb7b321d18ad7d871ce7c259 27d6f87233a737d7314f8e0d76736de4315c7dd44657ad555e5d7e190e5f5f50 51d57a093719348cc90886f6291facc88dadd89d7e44fd8b82ab24fbc9afe0e0 bf2cb7cfcce7155aedfc81b53df3412ab6c7f123a31fad55c8b0b46af928cf85 f715e82bf834c5179690b77171ebc39f2479b44c683bad6606e30de4a19c5c64 dfc75eecfde2b85186bc54acb902d44a64b25471d86f605566b9bc6eb427f259 cab3f4489d2839ba8d7de53592104787d621bf1d437a99830a3615c0c82f9785 a6da7dfeeba975c1a811cffad423595b0d31aa39bce5131a692ca9c1cad92273 0b3980440e2803a86a39bbabb3355e5d303aa1b735ad99fb5daf573abfde83e1 be28b5d0d3463dae508d68da6f77734a44a220185828f6c5393a8d28ba0bb539 ca2ea97499515e1377b5afcad4baa7f5df1aa5562e3c46488d3248a50d3ed7f3 20d4d4d0b05186940f4c629c4b813d8e3de5f9f842b4c2064ce308e78d541e48 37b2948e3535f469c59b6978e538ce2874d4278f0db48757c7b809a2d493e60f 25b8dc816a1bafbeccb79dfffdc96a94d3166025c0987eae5832d02c0287b5a2 10a38473aa91914216ed382f46471b59e92d9d9f9806eeb5006d3cfb0e628688 e1bf2e1a1530dc3fb7837c619ea3c04f2d5e47f947f7958684cdcc9b00da2b89 fafd6d3313755a4d551e164b3c602c887a9c8907482dd32a901097c686f95474 53a6f912085f44c37dc5bd669e2342728e3be40ebed11d2edad937d637e663a4 9389fa220019f792288171897af024c5944c6c3c92d88d68ab0fd250e8674ed4 ed3e276efa362941caaddcf808aa890505b3d3fbbb6093ec584ff10467827a35 ed8e3e3d9ea98d4e3e256ec7c154b627b1eba359835cd3cfe3dd3533edc22d52 6ee15d19f3512e23f3b45888d6fc51bb72fdfcadd448cd5ca666fd405aa82db3 a5f18afdcdb1ba956bdada425c9ba7a863301c5ab964515304733bbec15ec3f8 d34f486482584d960baabb522b616e5df10b365afdaa1a842983e62f5a9f9de1 ec6dec59ea236aca37d46ccd2e14896558c1bbd680699396d9ff3ebb6b6e191a f1c19a86c3dd42660138e248db7d1805fb411011ef5df379d46cd694cd9e9856 426fcd3ab76267d3f71f0caf3e4925f14810162e3594f30ad6997b5f7bfeea36 2ff3f9e6a64fffde3be3825a9de8c172e6a698b3f8fb777f8038e10c28e577cf 9f5b9803f9a92cd0cb175273438cff64d078011fd03aa788771d801e7dae0398 4183048f074f5ae7ce4b9f47658f717ab10253c00d273f031e1eb74f9893deea 195757032eb8970d0f6635bccf1e4478021a91a9b2cfb081a7cf133900c856c1 f468cd6e525c579401f13314d3667ed6603a41c555bd5d8d0ed1b53c689262bf ce43cf6bf3b042f66e1b95f5d6991caacaf65ae419eb183ead4e2038c17c05cc bb05891d6e3fa2add4d867062495a0ae6f04979994b83f659e4795cd82b841d1 ad9bcff4a7ff529efc908a47010db5520d0063ec02092bcc1536c57a96344c13 8cc10284c3db96c260e736a6a2d835f2d05cac16f0ba42dbf373ceb6dfd00e7d f9944a943fe0088a2c7da83c7b67458f724bfed9ad8e4533d086738ecaebf82e f4cb9163fe1ffe9e5fcfbe9fd48a0a36032ee9d49176c865692e5b4e8ba79fc6 47cf8c08b1ac1391f9df680d18a03af2ca5f5a97745d62c37ebce2bc956e3602 bce678c6f4ba9c2d056313db7fc0f8e87fa0c1f8f73f9bea69835c6e68495b42 7e7916bd9c73e8d33d47516bf80dffb8f88634f3be0648becf55bf169c9bf13c 38b49faf05e8410682f4769437f01015710977ceaadfe780ac7a1fbac365109a 00b541f84593dbf92ef0e4b80bdc1add4182e39d5c193873c55dad55c89599ad 87691430ad4def3a2224fcf61e3167487f9e448055a39b4ca61d7a92b9a7067d 02d584d4f958f313bf6138dc6dcce2bf97b770c9a5c69df845fc8a226f612593 290901ede213c186e2f26b93ebaca93dcefb65e0b2fe4d330776dc6673c6e5d3 44962430fb7f17131351168e3f9b3187d9afa13ab9ea5b215e1f36b8b731f979 d02c0eb7bce5b4f5a7891a21fc9c6ef172407c8dffbc38b5dc94c338fed86628 0c3f0cc4b677fcab0d2c39bd76e47aeb9049fb6f661b03c3c7b85a20231c6729 61267c45ba76e5343bf4ecbe4604046b99c52e59eacd2a29f01bd2fd25507615 5e2c38d57a2d59d71e686ed20a74187a8fc599868e28f37911c708dbc9e3ddde fb6d73ddfbb9b936c5864329f2dc591c11fa99cc3cbb5b7462a4673617210c2a f311cbf6e947cdcf0d28ae7927be8a17b1c214e68ace710bc1b4bb0723bc46c3 bf57cf0b8e51f24a15e522dbb9ba0c11af0c1d0670dba7876acdcb0ff3911208 76d18ca673e2a0543c1b330018a927fc06b3ff70c4daa31b5a467f54f3a71f77 28c142260ab4c6fbe78058c3f8d2ac688f6358ceb5048905b581de40d7414113 7826269215426881da801c4b15bd946cf1eda3e130be673089a69f879abdcd46 343000d0fb28f4bb9bdfbff3c9988bfd9de9df6f7af2d70f79a987615e6fadf8 340c2b879544ae23e68d734fb548f4d580213d5cc8c2390421149f949a82d564 9b3c0b55a63a42d5c40e545ed0294c14c831940f0e45ba162226e0fe3a204fb1 565f6b9ff7f41ed759dc6d9a3c2fd8199d5743f2bc868f8926400eb4da574599 cf89472ccbd8c40c03f8bca4779b91079a27174a0f4b5c79b16f9b4868e8d0d6 0c9d1f2d33601c144e7ed407663038be31c9b305e6912a251eff17a3745219bb 8ff9c4bcb05b6987d62f600ee5b203561d7923693038ba732eeb6e100648da95 d691d5f4875a9f92703ea19d23bec6f5f32654b620fea8bad43fb1d7cc07e36d e416c96057e0b0787aed64be91d784ee5ab22edd8dd5b52902f2cbe41767f8a5 ac8574008df55c9d57c64e181d19bde9275bc7da3df33f22aa234b1e5965d33c 4f4dbe6887879fbef962697bd131638f39360d3abb91d34beda0635d753074e8 2fbdc3dcffaab7203fca85ec86866d5b88d2a5acc832377bc9c8a51f0c1507e1 0dde04fee1c77358ceebc1caebbff53c6a95d6585e19c33e528cd8a5a692c92a 328e071fbfeed8bc16a476e19d954db554a9d476e1e1a2cad5a1ca4bfc552d70 8950f4797c3dd98ddceba7eb87ff6d3dbde14d89a00a88e41790681e87d5b558 cd83aed65c8b43ced843127822a4a88ce95fcc121c73dcfe4b67c5ab7ad3a089 2c9fc6142383bea30bd183cea4e8411ea90cbebb41e3933ffdcc2b2866f7cfd7 a0a0ab38cf49320c65ec183611251595a6a7a121f0902ec41a495b073a2f8f25 4ed1d29a28370a5d642e3f31152e502da70eb38e53ca69948fd1c6ee21545a94 86bf772c555e6c3912df1151ef01dd610c4998abbdf2e5e52ca06fe5f2802497 b3ae27ecc2e6e69f47a38dc76a4560a65ac79bd6161d037b9b28c3a736623c3e eb998854f995eed57e18a2bebcf9a0d180422c9848d3a3b8bdb4ab0c901437b0 4c76480ce6d964e781f0c3a05865a14b724a71f1a5c38009a6292a3051f20ce3 53cf0e2cbff48a26e1ac91f1d6011c776b9e65485eab7abaadcc9f28525f5aed 95476a82a86b005cf5b687e872ed05275fc7cce0b133aff6d1086b85d25e6740 4e34d49759949d4cfe4c4d41c0cc19dfaf1184a3fd70986a127ebd97302fe7fb 09d0ebef2d9b55a9931054476d8506b2b86d48ae52a4d673098f1d1ccbee4766 97edb97cde78a3de702f339110b4d74a3ec2749efccd47fda269a66c2a63ee82 22aeae5767d88fd0396b1c109cd43780cd8779542ef1b6a01de6c1fb6fbc04de ff81e960356902a4d79591995df7015fbd8a4b52e154bac6ae49656f3b846e34 da6ecec477a5449e2fc24190c6d9b447c0a182ec1e7848f52a098343c5cd5828 f5ec8f34259a54ca89502ce2c0733904a93591cf785f30f77c1c1f9469a2f842 af97ff4b463070d0e0f6d1f2c88e53f1f4714960c5bc6541892529e08c9e4dc6 2276f9f8b8a61f63fc03223b24a8bea89b3a03d849a9023c200c92035bdb072b 7a748c3a4bf20f9fe0e9f84095ed33f199ad976ba857ea775e2aa51fa1b5b102 467bcdae9d384cbf7c128f61daf2e10ab919dd2992ee3ea82d079cd2242efc30 7116e4dcb527f5b623437e235ff308fabba28332e89ee3b29290abfc0c07d951 a7d34a022f4f347bc8f6faeae42b0981bae83ed33f51ea396236cb1b35cdf2ba f012d31ed49775d247f3255a0b78b77a043eabfac1a88a6822263f63040ce7ee f3edc26ea9cc28d27737a60ee6d33618beeab8a121281797e0f7809d61de1860 d7167e395ac23c210fca655504e1437e90158018d6a08b9543214ace4f80c908 6284d2bfb572b1cb2bf206adb8784fe0ee1d65e6385a9736500d805582081739 dabaa91767141c6421e1615da387bcfd9143cb2c3cb48b5645c6cf10f7e16b6f b1abc0cbcc44f69eb6ca4923b9f44d68d664c0c1f4ff92c89877209168e7e8a1 312d3627a92f60fa010cdb1032ca73bf4988b2a1ec177fed9ce25653147e1930 c9968e92efa75d88d420062acd85c7340633391d2d6470b15e00c46efe0aa1da 0cb6c6dc134f0ead2d27d46a6a63ad5f07792901e709001f875ab82e0b722dd4 d3e5039666681f75ac7239ab76bf24891619ab1e7b62c490e86a50900832f679 cfa8731bdd53665445e186053d80737925df83e293aaaa1b3381289c76f918f9 7e359ba6b26eb683010347873ab14b1f51dcb74b628765720aabc9f0af847902 67b81b6c8a5f67c666e778f335972a91b8596c6c6127fc9b4fd17d69552b049f 48c8adc470a2212ab838d2026bac592082d60b2471ff0f347ac4294b7bab4d96 fc2e824a8776b8f8ba22434ed131259a0f34ecc8dab88c3deb29df75af91a049 82a6e15853c55f51fba7ff273c0868496bb9c89d81046c9bb91567df976a6654 8ce878b47d2acde18d51a472884860468f261a4823f3ae206ca50aaf28bd5ed6 b6062777e81d4ed75d44757382681160aa91ef741bf81e21679a4caa41e65663 0e349b0a911fa80426ab3ae7e8e101844dbfe70003155abce70ce64165c6ccb2 e5cf9aab87c79ff0e5ad4cb314a9f7649c5ca147b210996d708395a771624e79 3e74826daa8bd063620119728b6dc1a612cfe0801de6438f6b49784172591adf 0bd402d4236c397e6f379f3c935f5335ad20c764df76f5eb65d3f9ee798f5368 64e4e5b300f75512a200f24917574f985550e110cb751d8b8c6e6394f5643f49 6cdae0468b0a613546c417da9757d30ffff80d89542874330e7ebfb72020d0a8 1aaaa06d674da3cffda8acc3fce7b05c42e0ce33b381aaa39d4834abf541d5c6 6fd451bf55ff8570f781b1d573321233aada1413eb1ba23d63d603a55b5a2273 ecdb570b652aaccea9f2a4c546c3103955040ed829fc9f46fddd07ff0f98baa8 305187969268aed8d6238bd61477956f1fff68fc0ac5a62dbf410d10ddf34f78 77c82a67e259d460720acdc176ae85743695fc9da8a85a8310534025af4f297d 4cf6ad684df5022c60e03fe04889b600c9061b9c95b0cd6d4c4476d80e27aee8 af1668bc90ea60702d1ba8ce4a4db3c3a50c3784480dfba99db0399976fdc212 4c7fe4ba5f183111eb79f568a7d4a0ad7c385692eff45fafaa05e3bfb62caac3 9f8ad0966b8a4e05af6c11932f03eae7fa7f54624698e76d062432244aa2b975 5af99bd8e606e8204559fae8d83463fcaceebb0eb3b81d3b0824d38611a71d12 dab402791f0a8eef5afc4d554513ce56106092708737da36778f7bfddb09459a 42a2581e5cd074de8d8a9e033f44e0a42f6a5bb3e98a4b561a3226a591bce860 a0874d117c1600242f6a3ef8b0a4e2112390db4719bf62875c1acd118a1d8e47 75d36953076cda048d39c1c6a4564801f57ae6d064dd34e7e9f6fec001d3888a 6b867339e137a5d3ce184140ac9cc001bb3e7e52ed2a68d1b82c53978e3939be 4fc44e0ac0679bd756e3f8c3323280fd3b6d15866ff9d89a37dc98c4864a3a82 329571558b39f9d910c31dcf9540d246f45fc26566c111edf6c534116f69a30163081311fe370290aeb79aa7ea79b6fd9b66bcc834d0b7764bb16474f64b910c91c5d60c20fd3e912bc33d4a40b3ae20b45eaac00b2076bde21f6b2501c6440a0bbfdd0427eefd007050de15f48eb00b0ec9f099cb7704c027b360d553329f0ff26930c426d0c30c27df7ab9e469bace05674330861465494d7fa719c08aa5063bd2160d92fd1a6dca5b875b6abdfd7ef6cf7edaf4f22baa839e7835ad60a00f6ab03be24aa127e8dd59d5d4fa0f8d401c54c223908435d47040cb90bc28db061f14dc009c5a87b02fa8acd7369728ef04f77be0147a5efaadef2670c8a0170074b4c367bf2bdcc92deae026a9c29f53dc03282ce764dd172be2bd5deb07d40bd9becde6844a9c09a824acd0dad22e13f14bfa0d273e8117b4fe98847ce6760db10c81f688adbacadc8bc46a7ce8bf9d2398ae87ccc5948e0686d686be248f08e78c080cfe38f4b95b7bb0fe85830427d3b77a4ff9a082713adbae2ba511fe0a97cf02e1b961a89f0b85079727dffb4d18d7ecc44df38f7737b01fe7ea73f00254b5398e404993f581549e313a23d914fdba56226466d6b9421b592f8b9abc0ba11ff1263a988ed3993d0c8a33f46be3ab0c27dcd0ed4755819252d7ab59040c8137ee53f75784a2b9f497d9db619c5b581ed2fda1771a829190a3c7635522098459e69fb77a0b7bc8dac865e7e5dc597982a48fa84222e51f8c188699a90d0f8a3e52d82bac62064200d4fa609867583d2550ced9944a55d9147d80a08af30824ff17d7a106073a7caed6b67e198f0189dab934a41a73be3e720c100d3faf0936184c4e6d820315ac6eef1817faa09372f2bec164f4170974c6388a8c66e30ee04138c489566418ef2da7006c42555456d2bb6b29da32b457ee6c66f52ca10b8e9a5d4d6591b67cb10a0ba197cd1c3ea2320182c97b5f67a50349d884c9310e6066cef47bcf975e358a26aa35bfc608e9816edc1a8feb0af5cd06177d8a9f09901901b003d652149ac44bae6398e5a0546dda8221fa35dde2e3e69737fa770b05e1108ecf4a1b3543d3f038c31dc2e9f859183fed71dd1cdde54704ae5a130e9b306fa45cc1ace741faf7a184a25cdaad767509e4a5ca5c0d6d317ea89a0f01e0c1cf6fd340e7fa51dd6106bc34ea02f48f504421d6450d5f9fdeb7b3aed20673d499d81089524e7b021e31d1f2fabea8b5c707f74c92db2377c2c895378f048ac0d23f848c1506d53e6786fb1aabf849bbb903a7f547ad8d3b89211841d5072bfbef2f70e96e362f2697aae02a85424ec9280f5dfa65449800e5a6a1389d0f0cbf456cb50ebab6c0caaa08108e6655e460fa40ae84425a1c598d8b641a560526306d5551cb8850618962103767a1b38414a919a99d81ec0b9e251a96204508d46eb8dd6e66c0c64a2552023485b3c326a9625cc56130a78197d0f435bc810447b1a2a4ab63b736319f96888ce0091f7b3d31f1dfa35d6539053c07beafa70a6c2c5df9a37093c6aff1526b8e4a18a4433b467f36313031712a0988017ecb0d5117d5e84bde51a1ea46f208a87dd8d04c964c3eacf633164f5a128bd2625705556f49f541a39252379270cf18ebff098bf28c51522c1986b321bd8a88f0620db97386ea71f1523a57a1d853530a1eb593c698177f97ef8e1928f05cf3395b0a4c60b085381d7ebc77311eb0119526a1c85337d79480e8858db4635ba9854a00131374a77e92ef19b1baa058b0f8e3bfc3e07663cfaec10014e31530c966280995038c7d8640d4ed61b7b3bee143ab22bb8cf52616bed6c133222b12f85f1301e05014bab74eb8dee716077bef10748b3fad5536791c9199aa1929fac7275f0bf662490114558f53a3f314903e7584cfa7ba743b203bffa54a30738bef955809dd305f62182a3dc4e3e14127854573e7bb472866f2e068ee11d8001cb3277703878d6b066c6813230543092d4cb1eaac40814e7a142f86db6b6ff30a7aa28a00dea1a8c195382f7b21ad876d7276ad956d631084e6aa419610d32c887549820e79862c9215873c6701b74d133556979402a2ff898042692a2283649781e4020845af8d62eb173dffbe33578355f9fe23e2d9f6215aea4b2305e66229050f1400778896992a325a28ef4a5ce794bf309a7f9fc48c264fc25690fb8c29fe61920724ce21d8b3e60269ab91c06870623f88aed8c3d0c154adc8bc267b6f04004707b4a37e95f5e9edbbf2df29e8bbf2897f8c4ecf0a0d3c3b0574dfea723e5a4200519ed1492298cab0053a3dbdf1a29d0c56e1fd4121bfa9fa843070e4fe3f33044a1d3276a4c5220cb17b4ffd5c5bdd8118193f098e2cec0581b2feb36e744303416d753b1931406abc8fd502a05adffdbf22459ea0a286674a651e8147e409069dd38a6e6b429f9f42d6c166e5e93e1576da9ba71db6a8847a3477db87f20203e9bff674c480425258acd6f71e66157ef0af164a7cdfc6a55b26e884856fa501867fb1e9eb40260fcd763476953962edd41ace6cfd34af5a527cc0852cf6020180367fbc9674f9a7ab128b82c9d573483522e394e1400e9812c484d1c5c980092e42b356ae73127160ac2e761a8e5106d4db22511f9107336aaa2e88ec38bc0eb86e8b223f4f3a40d78e83eb9f9c109744462188951f1f10f0f8fe1253f5aa007d54e75cfade736102966794dd59c73e391b57afeb89a803bcd5139d9481ae00bb7741a702ef930279e074139f4d2792d9d0498b0cd7abbfb414b8e3dc7c4a061f95fe9d986c215f04611f443939d3ffe2ec70faf96766fe5e24daaf6b50f60a578526c8c857f0f04544c1368897b376eec698bb85da1380b8e664e057fb1608155a2ae8963d2b82cda83824fc25b19772fbba66798966847f047be894707a08692137bd570b51b80f375b99a1391984a5f60b34aaab3769cb2abdb8d941c20dd50f1ec9253321fa26cf7276ed3cbd4adc327a7c4ee8ccd4e24ad5d795936009251b9d535f45b85e9ac2a5a50d90e94fc06d7bcee8b981d1b03c0f7df4419603cc8e3ae20b6d44c1f05c8be45131169f349807533f14ba5c8b7cdefbdb2ddf0355c3dd2a713eec12e26fbcf559dfd9267f6e974272b3c653a57188732b651307a32418a7fe76ca9b47f982ae929a4510d36581832c6ccb0c390dcdf281ffaa00f2408f7b791eeff04129f7b3212cd9001cafd6ae97e5bd537c7d5f65f5640b09ec5219f3f2a6d00e183f16584ced26e383cd17253f7cb57879cecccb16bc50086a11bc5ae9c3e510f0739be9de57a458f2d4065dead5a0c87934f0d84582d00b8ba053c7c722a7d3a1c5c2f3179f7b63642a17359b6976c3cb30ad0afa518702637fbeb2fdccfafb9d877efd1b7bc57763d9d5e8fc38c23bdfde4ee076ffb101bc0bd58dd5035f2306953b1c3472502d0d24d58da781bd24f0d45bc9e58d59018bb38ce1d1c5f3ac4ab2be98a9038aa4940af2f346635be24b41ad2032692206d3887010b6a68db796f3bb8bee816e9d5f7c7cac9dbbc51562e7725784810b0783c3a18b2cf2fc48c0b2530c0010f300800c3312345e18af516577245a9abd0c57b8b333511ee0b9d5143015c411d1cc3748e1bc7fd16736efd6587239a21900b2fedc9a468267bc1c872759aea932c9446d2b74f1e384dc576d6fbb364766076b3dfa25521b883d4fa52efd7cbacd171ceabb8b592cac5a0ead9ba0ae54420a314f35d7289a951692f473cb0a3a972b5cc11a575dfe1d9f731b90b202d2870c6dd6aa882fd29ba7331be1b76a9c4a46c12862eee07bba07d9968b32ac3e070662d5318c4805e0ce49c2500739f19a15b5b6a547050f615cb30be3ad59152e079f796ab3143b60ae2371ae83bcfaba1b3667ffe144d459ecca11d84607f58606c0e2bb0d98075fbadca0972176725be8ac20bfc8f330246b2d6e36a47921c809ce8e53bf6fb9be4be3559758323f9f00405b664d865050525fb1c268f89b8203cbb5299753016836de8c384a5490c2be91c581eab4386978e6ea8dd8ef505501c0402513cf4e1dce79432d7257462bbb4309c18438b0a0f56eb401ff66b45b0f49c5937b165c922ea6c69421ba4923a4b39d85177de94d56c4a2789d3a5601090cc491fce91eec25292fbdfc7cba8bf90303bd8e203f4639cf768a7c81f14b013661b1d6670990c300e6061f64028deb07276080b3d53da0b76fc12ce671fa0a4ec62a993f55bbc23c7331a6faf68a2b23be9f6caad669efbae712933e6dcc07ad9d87296ffe556e7a9e178de00311941cbb3905c2c54669a8af933db3750907f63129c3589413b9074ce2155a8e2b48cba031b2fdf00695a2ebd8b6b7570a0d2304ebe4e5a2dbe0ff3d31d5c549b4742a9063c7c14d67eb0c64a6983d84ad02a53a399ed1298e6d963d223d12f19098fc9b486079cf366e82a04691842e360ea030d4692affe40b186ad2d942dcd6ad20f1943c877277a3dd7c1b71b33d820a65257dac8cec7305f517f481b3a4038aa65685ab82dd92628dabbd4ef136930feb3f50ee9317cac3d708931a48445f9abe746707487b6b0eb5cde8ab0e36710565e0dc41aaa77195482169197e7421f42552c379d4c221dd08d57a00a04c000cc7f9b7302db8ed82dbb9ebccc3c49f0ac989ee4ac19a7152cfe0d9bb3f6d800b8623d0902c39d0fd7e9d0f5a92265bf0f82e91f5ba7915f31c70e58be444cf04f0c9eb1008b0fb9757e2bf0ba86f72debc915e1a5845c44356e06a2bff72680438ba71b5b21649ce6f096b142cabd3fbfff6a66bf3b4d440faf81b259a81cb0b3a42aa73fe98f85f301170fdc2896575bce96d52a8d916067e75b88c45d98a0acd380e0cc5c98e0393f8e6fe38d1ec011ef83465fb6417e6431bd6003fc9910b808784254bb40f9ac0278d6a75301c52d0d2809a6eea7dc180c7aff348e32b0c44fe6281dd2db102a2c31aad7b82022bc380ed444e7b849841e96619a49b5a022dc83dba9bb2b89779cbd66fbf4d7a3a933933434cc5f000a18443f148ec0005e619b9ece8e98a7a2987b4e66905243c4e3da106ef7a458ed5df0795fcbb830cda83a6294b9ae430d0c95252159d2a76347c789589a3683a6231a065a7c67e00d763366437c967fd2cc13cc1a1b44de96877cc5bb77d6f0ea3e6a06c47de0108da220f25540861d3ae631901849c3c52ae9ae5fb2a79d5cc377479b22b49e60178ef2fc48d4748939437a3b218fef692b466bbf4f00c7782f4b65497b4f92a08d1449bea26bf4223ef244f8006bffca9c478876bfd8f3e005c0c27b650cf3f09687606b3fabaa0d2a1fb5d39edc1576af4051a4259410de1fadf732d8cdc150e86eb2d13a35f4aeef4f8d0154148aef93b2b2b81da1e19981aa4d58259b3e105d876e269da2daca47f81acf95fd472cde27645b096d3f0c05f2fff9100540909714fcbce5664cfb093a7e396b4deca7cc0608533de44a6ff363ca73ba5be4f025bae5a4938785d92f715c60eebb421510488db690523e324600d79162c41180e5029083d7e6481b98d1a9e8a2591ea18aae6b05ace415a292c2e9e0750a54100bf602dac6da329b1f4cb30bbdc5442f40b5577581e61fa705425f5fad0efbb0a32eb94037d743477373aef031010c32b1c91e54e31b24b8b079c3eeca279000c30f5ff4fecb78c571ab73ac8cc9fc0d2ff9da15c9cd4b9c1782e32ee03c8820b038db843de22866dbc5653e58371142cd4d209a6d5370fe8774ddab49d413500673e568ca8610ebce73fb9f0f99c32b5df82eaa2855eda5d33932e35579d270588668588defd3b50abacead6bd5ba4c3ade67a4561585ba013e562f734f61b0af83c954da931ab13fbbd6cae74123bbbef3337e3c57c10b37dac17b0587a550e64313e4b45c1d94c4526c7074f12519316fcd262298d80c6db0ab3b814d6110039d4097e296bc0b63a72ce104aa2aee7442097cbb87422851e7ce772fe57b30dd41a5b58fde9b018c7c09c926beb0fcf54e97611be73a0ddc0428edb8c9eaf023c4a8a7a2d0283163879a7f4c7957691da0a205d6aa6ed5e8d4dfd5ccc0e5d02740e9804ccdf76860b341bd733e1c44115eb25da9a8642c4bfde599061244a08a8426af24f3dfe2225eec9095c7487d6aa2834d99b898a004b134d3d6515e40b305f2992599a27805ace96f42cb7f24507fb6e4519b618bbd185989a4940320c3c4798cdf5244c3d7cd4688ed8000f5d30cc26ae6c0e896438e273b9559ff10ababf2be98a66389c6c691875eb708c23cfc33cef52220884f35cd90ff892aa0c7e5ccbcf0e7689543debc750d856e091200a1e721294abe9aa461486c1c13004e2af7470247129e4e32d7c912863b0a5f4ceb8856c2b40e700f701bfac6cf60f6377ab85d0d7177ec37eb2f453f9b994474d1da22922300afea0361c9020c200fd41b0bc43dff2818e2eb6d0c9d4a9f13a671a03f5c1b5d0531b3f34a4e209032233c7979058318c091a37899851ee480b7b268d863b5c801146566f309fb405cd2be21f68f0ce225b5d31b282420cc74dbdcf49773b9b89160b8efed20c9f00e07850be5c27483001f705eaef6ef8028623abd49635922cacb2cfa285a6a20c7361a62efc88360eebefede0dfe5c518fae771a865958fa5c87f2f73039a71085a540fc9c4ee4ad54eb406bff6db92c322ae5b9c43a4be6626b5ccf86337c0085fee43ff1dfb8e0486594a47b445175b7a6d0ff3ac83b749ff7203f96d8ce20bd13dbf87b2b9acfa93cb48e69c7141fe87132977776848163c52328a417e010f05e45b247afe85b6a603243869b99e853e366ed3e532dfdea3eb0446839ee00ea1c32187e01c1be1e70efb55a63fe704047e11238fb8a984a5b989895f0543057a46d49e9f7d5555e706f8afc0c9fde160ecd90fd4955951de644fe5c7857f00f2345fc86ba9d55ca120131771ff26d2c59afb23629e60a4979c4319997d48035f5b68477b542e113185a2d36e252dab0edc3f356f125d4b4f29736e50412f0c24229559540d252659af08e82181cf9eb20ee43a46945b48aa11be0e6280fa079efd99a48248e127f21849302749dd8ed348bb0190caf32b24bea60208b4460b9520a0a7ede3b5988b2ee8ed554e3423da745223039febea94422e13a151e90ca6e4ff9987ddaa6adc42b6a02ae2c4306bcb20f0ecc454b6aeaf915d3d308309ba644100a8fbedd0701d15609c137a7643ad851932085718cc547c8e7bf3d90a4767c62c8465eac16f456d62950b9fc72966cd80d9583828cea414ab99a044086c4f4d9a2f98c4c49c127ac23e29e8defc6bd64ba3b11bf59ca75e50d873080d182d1855a89997f6f3d0fd896a8d8fa258fe2148de97ebd472da4154bfcf12033c8ef6d6ed9286159e233fdca625f9ad754244a884cc76f78c278f60ed253d0ab83f0b3a7c87200ed3023c361b160d298e58957ed1953cc67f82cd1591f86308e0792d3c1c7fe2ccea72433d6eba40290b8d4869727f201c223607b2356cbd04e4097db07210868e05fa7d22e4f1b9254e726d57f00d647815a27affd08237003e342fdcf5095fcea30e0691ddd6fb3c01ef403e57ded9ef3a15edc6effcaa0c8e7b5c337c60a907405364bd9b20afaae0ac28ae1b252df6166d03b6b2af800fed291909c76061353eff1bc1d0ff47436a03a49f32cf76576a21814bdbd8b40be78780d39785c265f0194621afee18e78f4011f2b87a8e9f73b0a81d219bb9018a0ef07ceb7a43c5cc1b109923a2fda7c76633af938d731a28468104629c2902101e9cc53203f433cb601cff459be55b5723d0113a2523e7ada2b17b1f07f20b1b24e8e87ff4c576a249de65656e4b46a8b444fd7a739e01b903d1bf1d231c0dbfd43fed01634fd990f0abd31f80c0ea26c2c6883cb3957e6b11260beee5aa07f88886adf5d0d37faa066e9f216be53d64242c3595d48c1a5c6bf94cc0648f0960afafa89b4360d21aff175a236a4afda4f52f3db3553d8581182bfca0b2000a0f3d95ce7e047026ba17b5a771291dda350706656b7c6133b9bdbea1e8574c023e184899c11372f27476bb13e6c5ed45d295334652c5eae84eeba33d9fd00c0ad727a8f7d7525f400d69fd6f582dcb7df7dd4e673998decfd519ef2d7d46330f185affb568c2bf2702af4a8699ce3919b9f359109b670674a97efa2e55e76c0163557452e34b2ac16a9b3710f8531743c54bce57c68e4f3734b9cd426a97d000a7220286c5dca11bb4dec54be53a91156fa4775219e88f214b9ba548ae312e0ab41ede2b3e0bc83dfcfce8863b09def5c27e1eaf7cdf81cf10634bafa16638088f002ee8bad7ddb71e9849b032b013ceacf56a3f1431d5469d77119a62a85105ed1b47b093dcedf1e0262a2566a581fd7cab9469e70044441bf7b5f45e88c80c570833ec51de2358daf03f066e2047ae4788a4793cf3ce5086faf1637fe2fe0ac203bf309b863a5e7df368924752cfffee40cfaa0515ff051e86abc8fb83ed0e7c2aecd4b291962b44a7802f5153b965af734498092fe96b5de32c9ac319fa0d607eb6639175bb63f8fbcefc6c3a20530074431908d194418fc60f59c8db480476f1744d5032ba3164f9c19a438281811a0640a37fcbff2f091dd6774c97c20767aa580d5b570534c5b59a3ca386354ec5fc65fa2b1a847f9af6ec4d2fb07c0f480651288636396e10d3316db5179e0eab70353adb5051a7219f0018b39c59097a36ced99f659b903522eeab3a31011575cd1e25c4d9638553bcfbfc36b89802342714d22c0f55a2dac5c371a9961062b325e183aa3ab737db6c9153a183f100e8ca3c7878e2a947dc56ace3be0d6740fb0814fb522c7fe170c2cfb784bdca020c44b61a4c7f267b45e0128fdd51f600501f9b730ada52c1a18b0b6dac466c0918c4d59f89bdbeb36e42308a331ba8ae031fd085758adeeb0d6012ef888f06017821ebe2862522c743208fb5e6b57e6d5bdbb1622e422a08db13792cd1922a0aaf9512704a40421aebd3b11e1167c46d3efaaa99c905edb3e212b07c9fad1b054f5e8b1a79fe126249aa77af8e0494bab36538f12c9575d5d47dba786ede0c042e88346febb4d1544fa323ec9d4fadebe1f37bd9c06487e525b539651e9cd506a0110dc7cd6e03fd9b6379abea155e1dc1bed1243fc52f6e6ac376e2d4ab370221174d78a522c68ee6bd598286c5c0adbc7afdbea70e8a31635aa14bb1533d0b3fc0ea112881d80b7ef7240777006d520f9891d40f1d314dba1259f1fb61730d1a76c86396d8413b8e5192044d840f517f8adca053cf4cca3dbb14f3a5c87c075adb9ac619595c3a6cd043104a695dc124156f01990d9e944633b7e90876f4018c2615d30d2661417c05bf3532443ae67327b61cf7c2f8215091ec43d1170404dc12f2d7e80a4f6b90f33ee291b6d57624fb5384050be320c78b893fc580f509230e572a80c8be170b6d25a9b203d1f76b33bc12ea075518e53de41975a66609cb2a774817e4eae79575e1bc6ab08c6673364dc6e54d5d3fe6a218e9e4b8a10fff03e3cdd74f3eb2e9729e2de45e3d9eafdca1b0be59875a64e21976fe8bb30dcb586d281b00eb730d740a6b28e75af6165a8a5456ec8aaeccbfbbde5f577e00cdace7db7d454b728fe819dd05d4a4d32669b5c19a2f203e49916ba495cf0a09346bb8d490d25a1c7a621dbc707f65ff954191f2b0a1d03d39f357684c5c8305fadfe5c93d0b293608fbcf80c4c44a171133c6e20194d26a9e362645f791b40554236e16fb09ac4456ffcc87fa609e28420107edec501661ed8a58e5f22e2b001aec713f7c1158f0978baac53e9f35fb802428f8af45545b31cdc652ed93e906558b3279cd500415257e02c949530f60e74a20a23fac8b3ac5498b13b0668106fc6b7f5f6c5df087288839953d8eb81172e836f7efc1eb707a3fc93e95151e0fcc2552b86f27e2d77f86bbea368f64fd2b38179f1d500796bfe9d6e560f0380ac8f88a5e99009784b54f7873903ba169794031a5ec3adfd5843bbe3658401504681a80b8b500e5f4fa1421f8a3547328ae55d3a15e8793cc1fe05dd044459106b87a81986a82db6a634628bd75e80220092f889e044a280d11c2c0655680db08a03ab9e60fcfde5b298ee7d0ca14975045b6a12bbfcb5727b21976a15a3230051324933f8553a4d720fa362a3cfd46c92a0f1032aaef49213716419992c0510ee6c71feeeaabaa5c4e9f7dfc607debe8ea986b0c9ef967be3319761262e7510a0bcb827c3ed5f8bdd2ef5c7a0c57dce5a5b49024524ec4fe609d53a2d71d6f034bfaecaed84a873e147ff7d406bd9a6339d4edfc340a265b17e6524c5ce31d0a7cd35605119623353aaff2547bdd9ae85d14881883db349c8ca6fce24ea9870422a0b72c49b2b9349f50b029be45aa45c90b3f8b2cea7b12dcd694fee4492d000a766c039264bbf6352eafcfa6d872ec456a3dc6f3394d24a696fed6c94a2d018d5c0ed0dcc097ec2f4fef7ad62ac671cca7b883edd92e0c104fca3b5505fa0401c4c20dc9208f2219845e9209712fc8e0c924692fa1819107f9b85fdf39750a23e15d13c293c0773cdbe9a13c2bf525ad759981d3d975f9121d059c692dfa0218329efd399338426eebc2eb989b1a9d3691a44d1fee3f2e04240bcc22306e0e901d9121df7e87d16bf60415d0df1759f84864449199a4a26400d1b9e6fb2908abd49dd60d92fd2ead5850dc9e0c2f7b76a614e575ea0dd9a3eaf749b3d7ba0386ed247198be63653bfe5796e14843edda1fc6c25b791ce5392d2e11ef612e036561f49dde6d483b465ec04b86ca5bba170d4901c470cf3a6c5cb665fd760e0035e942ee2319385989521c231ed856d570084353af123d81fe75683d78413a06a2957d39ef55b758436d03a1fa21665720a0c7eb8024b957cd67ed6286a2b90bd1f8c510f3feb36dd8c5d00e41b902164ceee79416fb56d89bf28c76b718f4012e8dd2c7650555c5bd40ba8022044da1ab981fcf3a187084e92aa8b34c750e067a1133e2218c0326a6efc01d1d8c2e26194b0de7e032c70a456d202d6326390e9bb9160dcd506c8a755e11e86db4dec3727e1854b6370d50b669771d5393ac0e1a4f7a38aa209d1fd972b3cab6a53d21c286335d41386e85c8a80bec476f910d03802b4e1e3dffbd72a0a2f073d882e7c06bc50c2161fa183f9fb013dabb090cfd04077769c63f14fe182a24c127e633ab7ebf7e910b13ce97512853eb685906024b9ae0dbeac87c9d1b945a5ad68f9541125311719d8e515a752c1a133d6103d7d8723ce9812caf6a773584a25a4bb04672cf176086bfd7b7f48a988bdde20d1edff6a889fa56b1556ee59e8185a56fb1cce16bab9ba897e7185f9bbad4d10d2b8c6405867deb384791b3d6d4f3023ec57a494a47fd0e182a01f6f40636aa0310bc3d7c67b27c4b6ee8498ade63ad80eefb0d2d594070a1f57de7b8a01c1002f00706c2595ceaec9398f686d752521118cac28e32b0249b830230ae11d03e001b36d8ce64f295eccded10db72a0c69243ec91882f08b1cce8daebd35ecb40082a650cb51d43a3a54059bb47fbc8abd74cfc71f79bf12f8441d17f1a0906ac00ff77f5d478d8f1bde8a27ce90d95fa3e0bdf1ce060953848c88bf051d6d3b209b70d1699ccd0b10437e73ac1c1547288b2a3e0e83ee012a4e0112786bb97a70da92694dc8cf6e0e83db370213c5dffb93c9f4819e24447828490529f4b86100be21b121df1b6939a288f7da07d18f46e85c443581b7642f5ac2e447ed91fbc03c7bd79ca338e7cf32a66d9b26a898e5246f929d4171dfdb64f4aacde75159506f0feaa9e34bc39658ec7e2cdaf61b180cd718af086355c3717a737477d7ab202c6d69d5d0eb06b4dcb9f0df055a90cd25eeda3730a3d186a8e59fde69af3980716a01c21f39596c306342bef578e1e3a84917467b51c2ccca30339b45caa690891bcdfc3e2a0927d454fdd2b987cbbf650ada87d3b05533c1ff08f34acf8ab06ed0f32f5e295a355adb3d91e7607e0cbbd2cd782605b0d1014eb094b1ab7a90fe5291754bf8659aca085444412d750fb35f27d53b1070b10ee771ce47d2023081dab86a47abfbf81af68be51d1d495094e447f982db42ac377924123b7254c06088e1573f1643fed4843bf045073aec15927035f858ba6a002f4376ae7f5a30269ba25c33c556e200efe1a9ef5b3d4a5372e5a737dc1fae6eb3add3d61731106 false +check_ring_signature fda44b03ae2e06cba5f73551867ae1a182030c7c338c8035a103410f412f467f 445c1ecc229964bc6891a4e70917b576cd966413c9bfaa961499b10cec122a6b 112 66970798edafb91fafb87d4738d45e8ec4003cdd3b4f554c77956daf96eb71ea e185c9bc048bad565aa055272a96ea24739bfd7a8bcf75956c888a4d2ace4051 e2f1c38508cc6f3403df9361b5c0906ddc4867bcdfd7b4a46280085c115f4d00 a7828a5eabe844661373f8644778c8b4c73c071a177fc96553a0c6fea838a396 4df315bd85a0aaff1e59d367dd8347dc464984eff721e012b77c0263868f2c21 3439be5b42afe94b414d8dac29f85bb6ecfa211b575c0c7b1dfef7022ba7534e 4d0c406769226f279130a82c9eea1e598efb3f7e889a4f40203b9525e1cdbf6d d1fa112866a95a3166f1e9de529251306576273dfa16192cab6b635e1d995409 7c7ed52c714b588a106dc22e90d454e2b5d91983390a0cf466cdcaa836154fe6 f3c58caab85b2695d2d79d5b8aaf6fa9370a02b29f3c803cd3c6dbb53b4b415d 5a8d606af7d484e97da536b039ea7342485a7611697f05f60561674d0f021cac 9899519bcb8b65eab85763edb47aacafd21d60cd585499230400968d3fcd32b5 28b2a4176a6d5c2ee178715bf61644da60285027bf85cbf8a4c647572704e5d6 b9e761a96d025871ad82e8fb5990a27af9332789aa81102b073f439018cb2c9e 222e569e3cbcb02e8a3e3217bf9939124a805d8bb104308c3af534d554f3bece 95da27f82c4aa2c0ff9b6503f3b9660fddb53cc422ab72b0ed37f51712b928fa 848541288ddd147b8ee79192d42f5d93b52476cf554664f87fac499c069d55ab d8009d7f1603ea774852366cd74bf0056940b47f5ba00c7e50c1800cd7b66049 7106823634995837880fba45cf3deb8ba627ea31e7c18092f6474b26c4276b37 73db283bd59708d62470cd67b262ed7b8620aeb4c242188c3a6a457a1d5e2683 2849ed7a8649541f562290ed3a5c575587adb63b6525038f3637e401edb032e9 e7403a753af97628b6e8e07ce647fb15719c83792a759033b89696e275cf2a8f 5e4bfd2057073b5cc96634e37233a365a4951e72c79cf53ce26ed1217f0c940d 01d042268dc3571f5bc434d03feac450c667a264810a833542682dfa6f6e712c 69a11327f639785d84d91622e8b2c4286d3d16502bbe187645cacb75c921e794 d9525e9b98529363893d29d176db5c139e83af218efe727180c39773dd56c2eb 2aceddbf6566557a632890a2b4eebc7ba4bf2b7db41a58b52352fb254a35b0d7 b4799aa5cd579e8aa0d669f9dab7a04b3df21e48079a24d85c312018f02768d7 71746d104a86796c918d789a2a6fb9313a557ee3b01866d61e79a7854eb4f53f 1cee806b09e621b5c5754a75debd54ea3e42964dfeae5947024a27da6770c83d 68a822d86c5de14f6292e4a0ba845d897e8ebb15cb63e4c88fb135dc5f149d48 ad9d69bfd443ec0ad9941dcce0a7f6f9ab462d0fd647a2d20512026a29b0816d a75cc9acdb97f221e8b2aa375118c6d89ed2142868289dad433dd26bc624eef7 b267143aa50b609d18550c2225cf6be2aaaf3fe9383792dd1d15d49ba8b1b93d 81e117771db5cbf87d26066172fda45619255c76a6852bc3eaed46c471d5c44a 2f26cd124e3526a4bf3e9e40f23e483554f257104b5c30dbbde0482358820bbe 460f7b8e44ce87b6c1bcc260646d73b52dcc622a69a25f9ca975a46d7af2c3a5 37cea62fff8269fce3507fe40f83f281b464789b90f3601bccec2f4d54cc497d 690391258716e03716cbc36031d3fe4ecf7cfa7d4dc6e3c71d137391e5fed97e 14241b3b083850909d7ad62e50d7bd1c473848e5d0619e51322d99851362a234 0cbbd174e191e0fc17713fba3aa22d2522870d73666022bf159dc0b5b918ede2 e785e121ae1d1113bf4b078667b328e3914554154467efd3fbcad0cd4bcebe7a e605cce192fca0e2ea060badd8a39aae146d479d6b79182233b14f6c91bdfc47 504570c781de599344e1f7f0f75159a35dbb697e206af5fb909472f79256c21a d98306311ab5098bb1b22b573d3c87f18cbd63bb6be7dedecf610ab7a24906a7 d5100ed20c01342f1ba81c14f9206a4ac9424106dae624e23a5aa90951aac1f7 58f45368e36d2bc7c4b5017b011de2f7d63457a3e115d8ae474514a792f480e7 b0848610955e2f4d27fdfa8d8569888e24a9c0a66d54635dc44488a18e5f8c23 6eea85f56c1d91d9c9a033a59b80f74df75c3fcb754263d441c3b9fa071683ff 75b45f8fd0ec94d24dd3b698f55dac5e9d05e1a46208f8fe1d7a065d22bb005e 914b0bcabaa94b6938ef84fae65ff2d2118d368ea84e20028cd8637a05950513 198fbf7d6983ec88c69ad4b128e3344db53277af41e9dde490f29991f050eb52 15e3a97162923304a77310f1360b7394ce6ed46156b20c2343c78ce63b6c0d2b 1fb86fa38b162563071c9c7b3f8276e6e383d56e88db2ced7367818d4b0d4717 b553365ceea842a7b4d41b802613da0c51dfa62ec087a1798b24d2e74c18c031 91ab38bd957e55df3e2f2b91ca65d52931f8a36c338a84012a4b9cecef1436de 6ba0003553431f2cbfff7bfcc3b6e4cf43d85da2aa4968062cb67d30bbf46987 ddadba67d47050c532a8cdb5f3f39e6238d5c00d9cba80b97ab77b88843d7986 490ac46d7a9c0e1982abb8c3a4548de6f4a25923cca9a4639880214efa7510ba 20e51538fc383963ea840e6afb873c2cc3823fbe503f49b1b8cd9535b7299e72 32c85896dfe7d67457ebf33f1062fada7d8a3f9e606cbdcca596060ad53e3b22 129b49d647e7459eb9c33857834272e673f6c77418e854783fb39b786ec6da2d 70d5e69e56dcc1c2d8f38c6dec2998865efb7fe42c5811bb82b70c5544d7d4c2 180499bc6d7f5b4ba96968b204c19856de1b6ea2f5c54072d7571e454d0681eb b6206f8991097953251433c0367eafba5c708cf20af3685dfb19de1bb9c89c29 c03e71e27ff6b41d1586bdb9a3bcfb3db0a3e8bbc2428ef21a0e973e816e839a fa6539ffc52579060d3241860832afb7e7b1c788bae6d9b8564a5ef844edec68 2ac4831c62522d60fa59842d5a7eddb057d646e06e37a844d5534f3421d5cc5c cc9a26cf34c3bf9d78bf33f5c13e5f2383bddbcf22ee4d6fd9aa0fd910d95141 c72a0fa61a085893c7dcda284b8b56ad0a0b18a3bcd2a06d71c8c646d44119fe f90b77dd6efe3e0ac3f0dae789f5c9b3056ab520067f25bba7182cfec0113908 ebbbe7fb74ab831dbe7d1dd88f993a281e93f70045e4485dd17805447e9db4a2 c46b7e6c938798b04a31ed60e97beeff138bda5fe98c41ab021c8e45733a6cda 1c3dfc8781577c13eebfbfc1e3bd17187b6a8ec5aa907270c0c5422d2c4d7ccf b48df5f4f0aa7575fc6de8b59b719eb22110a3a5d153ee632457ed8e3bbcc9fd 410d536e5311463016264515fecc8e7760f13e32980787f1fdadb10b049a320f c2cf7071f4d7aaed6799cae6f93a1762b5278537168077a4cd473a65cb1dae72 03a531f56da84b79dee167f01b3444877adaaa4a2ea03a9f1b2b5dbca8115299 b848e2d425d5a1995c4caaa464f0450caca7593ca5bca38b3b9794e9caa99c30 fe11f8d83b61d596229d8de3380773203478508cc90da1c49d328457d87d333b 8b612a3c008b8c606ec59b8a530363c915570e6c33929c40bf7180f1507468d3 582aeed4e8e58583849f69ccbc0c3fcc11bbffb92bdd31569f2af9206a2f4e83 920d1f191c9b9e249ad38a9fd326609420c58feec00b6d2070427d710257b16a 61eefe73364dfe6a7d0979d728d397f89ab7913c6c0d4831575cdb2bab7ea776 9e4c16da7f07a9d2875de559b02112b164c80f33af77cc8dc3940c4a7a231aed 6b7fb088b1da8233806f36e9c67083a50f5540a76244a34f19bd06a6590a07ed 90c2fd519ea8d3b8268f24edf91a4434d6725157225ccf0a1a0829496ddafa47 68ea505fff7c7e35df97fbd18038111063aadc5720fa22393c4105c80c1b063b 0ca74860769c0173ce14340bbd564c3bf341bbc2ea723fd319eb9ebc7ec23214 f2ca7d415de04afaa8d11d4298fb4c9b6871f680d0c2e4392ece6d4bd36a3eb9 ccb1b3bc33511510ea144b01884d1490aea0289b18d85c092b78d9c4b4f70689 96ef4b95cc8eb6f1f66652b92c873d59bb3b7536fd4d49b70fe27ea7f6be27e5 f74c5f92f936de29e9458d3a51f67a942cb7a701cea4df261dfa8ae5a8f3e59b 087c98ff05195e4e6d126f7fc817b3e088f4775c99cd2a5facb57676706a2d90 94aa2c04d1cd79a6884337d4e80a447286fab30a758d1aa14816db7ba29a097e cf285263b4009d53428947ff0acd984398b2f0a80f2d9bd42381e5540c812031 6b0f63e137f4f2df466755df06d62d0e0a72a68a0227901e3d1a6bce88f971fe a54243327a04925bfa16f0fb5c3be5ca6c371a92f1a94dab83099b80b9978c3a f86050026bfdc73954f6feef008b3e9d9e6c1e295f2af0a91e879f1744e1be0f 47702c4dda7c8dbda4986d9e938eb8fc6f90955019bfaeb08e1b8b144e2222ba d088da09617d9d6c4c2c171fc4127cfbb71ea193361c8147fc678dfcc035afec d804529cf025c6487a61aeefd197b8199dde87982b3b79242abdbcdd08ce3f72 abf9c0dd4196703c1fb7c5c0eaa2c234c9df83f5c8ecd2c5bbe004bfaa934bef 3446a0986c07492aceb0d29507c50789e3db3b87f7725369f94175f19a36b707 116b982256a0dc0567fead207f4df2536019a165f8c0feb615d574e3dbb4c5c1 2c6c86c4d2b3840b30070aa59a443ae8eaa0def0f6291331bdf92ecada56cd7f 891749e07df701584b68840fb4ae8e03ae2a43f3d1cf4dd12ae7ba5c6a4132f0 1c0103a0e416466647fe10a48606337049b158f5091ef4faaf7f1bd8bb963888 7ac217967eba1d34f05029ea6ac9f9c1c9d8cffa71fcc3f00d86d46accb88b9b 6da2a5c21a49ab507498160b5c21bcaa04da1bc0ac299be03d96297877405f44 d94a25bc81f63d983b3153265b3128a6d328b4c50354d961dd4befd31248b93d 3d6f3ca7eeb31d932f204089450d996b30f7bc2bddd4368768d21904ec9bd317 973027b54fa60826f88549b7f14e6875df18c5b5ed811950764ef8270256430a1c07ff7e2f1d872832ae68b4b4f6d32a89576a0e8fb20356c691d6a0d5fdf903d6177f03a921b6e9e98574c880c1bc3054939229f51b23c3cef30d821629060fb433aaf20846b9e5bec0b4ea6170fa63c0599e45e1931fcbf0371c132e29800559e68d465224d2c5bd3f804094976c507f924f760e7e3c6c9102b90953ee640901931bdb5c622738e5c171afcc8a7744c91f52860db158e9322ce7ee40e4ca073380aa21ab0c277fbc3d9cca5da78a3265e60d6d55913a92b22e780807903002743cdc4686c195934b3aa46ba11ae533cc76ea74f713fcded947c39d7af0d004b691a2ed6431a50175e7bc5712461dee73abd9c9b41cf888d7a70de288411102f892c3c22ff680ed8b84d40cecad2a27633e0d75489666865b283b1c33e0b00c30cc0ce5aeae8cd442d69b7e6280c95d2cfcebc2c1e5648c85bba5aadd04b10d255336b73ac7edd1dca6388c67141064566dc9414a058f6490f9aa6f71e1a10e88c886f65b340860a9e97323b0cf5e6fd100bb0ff5ff120e96964caabccd68088d520a10a16462531f4a61b2b1a1a725501e31b292584e13866769b12d4956099bb560a637f1a5d3743e8cd3c4e73c51bfd57f62028c97bec4315e6793dbd80ea1e966de7ae5ae29bf5d0a79049a3522cf9babc87d6e8f6c6e465cda3eae99020c4f255b40e11f2f492617b81af891e6e48eb9a28d8b7ab6ab32b391532cdb0113ca795cfec626fc2535695f0050ee31b68c5748e9e6927f7fe9d8b41f4f5a00cb9d1a90139d4eeeedd0861760610011c1e1a6244adb8638af54b5702711a4068cd5a4ace1ad4603bb7d917e012900ae830043aa3f8fbabdc6db89c9e449790a605cb3a84d1153e0ca4decb8e951aadb2f2391d3e20427d9bdf93b6e5fb3cf04a4cc9c0af5d65e60748e66d9d52b508873a82a322e471d03a901198e0a002108e1f8b34a5acd5e3a73bd15b320582d1b0c96b1639f526ae4e11af354ce90ad0719ff4aa95379c65501758f22aac42d94f2fae3954b9c6e33481c6be27ebb670707b93467e0ae74bae0ad630bc1f7924f0133afbee489669656378275d3d3e0059d397cc85d86912e88c4465c5b5c16eb4b99620c360a1d31be27669c9432cb057b6d2908080472d71118214ea1da1a27ac3fd29d4e9bd6662609dd77b7e629023e226a0c4880d4e35ffe944887f3701f22109295a83df037d36c39c44ceb1f0d1da1ede13d9c4bffa23ff8d8e9ccfbde70118e9bad5359a8ae9babfca3cd5b03d367d7841e802ad4a7d204ae85d6827651b6a19777e2f7120f83a9eb7652ae01e9ee78ef6a0ef9d43c22f5cb23687c10f60f89b96b11abbe3dd7927c0496910a8a5534554534f6280591f978d5732260f3faa1208a6e2810b2fcac14958365030dfb2bca1c9677d06d46ff75952da35b6d3ccd9333c57353bc4ed14d9fdcb5001b7276316d942a3cf6329687ca64bfde4227e999966fbb14fb1868a5435d470008bc8d9bac1b72cb18a23496fc11b9b0ed937d1d905a0491a789a5370b9ffc0f8033a8302128338a304ec9837385dae35a49b129d01bf9f0e7baf03357af780c5d813a412335e012dfd60fd64cf77642f8462f504e71887273ffa49034a48b0335918092f3f61bc8a54d29d173565ea3b28f9f45cf324a9a8e94593505c5160094f94ede798441d09c70f351b43dd73de69f83e0bf14d3bd6a8564fd8f3e6f07b38dc7f4299d463726ff29ad3b2f851541af519f4d79f0d9c64b47242470da0ae5cabc4230ee4cb99e4420d16ddacfed764130a3e984ef55df55b8803454de05446f554c0caa272ab9248037f62880482f26ef41bef69b0c301084293ce4ba0f1b603adcb42a32ac91e2244072b746de37476d295666a628cc3fa9a9695ab8076d914a00ab2c0d8abf262a050233c984c9c7c563fd52eac01ebd2c1cca74f50a1be588de6fbc85d3659cf1236fab7732239185ef21698e529b35d62b1d1b74077d4524106c876c9c25e15006eb352ffa1811017b30e456fb47b632a69958d406071e05b9df7279103255349f22cd0f23fd6897f91c02cbf36ae77b15f0464f017f93ec3035480abf98f56ed6a85211b7cac8386c7aef89f96f7c52d105a64c080c1eac237f88e8b84c0d7b621db3ce2c685e480897b92f05cce29720cc82fd06b5eb222e870cbf05f63be85e2d34e49abb7f3b31cdc472ed798370086edc9f02c64cf6bceb04beda20bac07ff3f3a0e909c00d4c6fb467d03ed814286f10d50ceb95963c7e946ed71d640a80be962b58b40938ac201b37037ad030709bb6770dbe3d38affd80ceedeb767d6b006731fbec977e4f9a55261ebd94f8b2c5340d02fa250aad461eea77bf5fa5058daa745b6bfe74ffc653c86ff7d6629f98b87b02896d329d40c3c55a19efddd7e50d1997bb481ae794b6ec415afc59f492f9ec093aa3a4f3e702f9fdc3be83c9c3bddf40fbb4fd4d8657e5818c536ae237686c052f31742c5d2138061eedcfc552e14efcb82a3494f79970da945c4e4144b24705527bf4fb2ce9f7fa3d8724aedfa6f84e5d5d0f92eeeb340c88c2daa4b5984007cd873adfa37360e745c1be2cf08b965143c7dce84fcc3760b75ef178fda06c0b13d42c2cc0dca0e9aa4e8d6b4a8db612afeb55e4d7327faa725038165c021e098fd25d0f763e09fa1e0fe799df35d6fae9665323f8e6ddbb169dd93c347c900b74156f0325d8c244fe9c27863dcf2366346962331543562e86752b2024eca0062518c2b2067c041e1d6975a5529b06ce4fe067c02cb624f2e4332f1f453dda004cb9e7d0aa3918630177b3712f2d1d98877ce7ecea2ecaee832d55e4ab4a210d6f6c85dd7c5e0bc3d76e2874b58454960d88085564051ca281f9a236858fd502ab059d8ba4642306f10d8d10586eea854db674a6ca07f9bcef3b6a8eda00df099a7998161de7788d07a76dcdd40f96bdd33886e1d02aa4178ba54014e53647052a2f08f3eece427751fe9065d7ac155c361890337dcf05a61ff9a63f11517e0a8642417a03c52c7786c2d84197ab31aca11e85798d34bdff3865031ceec59907242e4976f5d2685ce5333a5730f2aa30d9981d7573783cabd2e1910619a07602d3399804d9b28cd698e0c1b9c7fcd4154c20a74b63fbe9d6c54f1f22f88ef505d68dd9f4fc13fd418c8721c0f55ebf72e5e090d1317963ef1f299cd50b023c002a29afe6ad09bd0c2e6a83c31c8019512ae9abe449aa1519fae40b62a6b8f4010e2680a19aeaa8fcb74a7060e6917a8e117d53543a9bd8d5748b398ae4fd9003ef64a37af263e3f5993d2f0b8499fdaa0f503a2b08bed60da73caf35ae614c05ff197dd98ef75cde5622fc9ed60a03c278103e7e8094eac989b6402bc012710acc441ddbd9713cd36d5c31cdab8b37d99e9846d97e3cc941de18d614d0527b091ee797950f3224ffab282dc5c9d7590fe90ed480b08e9ea9fe42a8f19210ee0bc5a5293119a9d6e31e18735fe761b85d2bd8a447888fee3e18fdbe83a580210c3f857194c0c39121d1c750d5635cff6d553f2ba6c70fcd06c899c911c33e2509d21adb55ef8f647ab97855d6cdad8a3ca0aa24a28ef1f91fac384471a39089012c6a1b7b546381f232719cbe8ae329dca41e6e991b12d94c9e2cacb2235cce0864a57190e667ffce9cf4a73d62e37ef10c2c11e543806875afa3712c57d9b20abafa6d62bec0c2efd6e9bc32957a07d70d0e90216cc70eb910741f89178c5c0ed67324455b6f9116c2a810574be211a06075192926d18088359a5b77b0ff4f0fe8da74cc502982da40f1141276a1093c4cf41a82e47042058389c89637d303027c78322de89618c8fb63e16e507f60292a65db0fdfef63210f5595d636f3a007c329f092ecb5e1e6197f4c2a4a5f167f57b3dcef9af20a5ef1cef360e9c66b09be2c6ca522782fc01f0c76c9a451f9dec01e82717d4048f57228e4d0bc653906c830feb50097710ced5c237e3145bcdaa2e0f96e13310177b03e848ed29320064ba23ec5feca19aad340afe5180e397b241b39cc3cc43a800c03bbe6c1c2c20c0285cd25a224f737fb05ea8c5a021a4a8cc7dacd785b5dbbe47eb40c044d090b921b0fb42fb4e50d700ca54ede58ad87e686a07f2791d45f19f2d68274286a01c00158fe7441546a15e99949c9dd26ddf2678f04ab7dc1a5a0fd1619c6c5f20613b8e42d65f9e031286f533046ef5c73bb40b8af1fd00d1f6d038d7951df6808060e1c774b082dc6efdc5e2391fbe9cbc5ff0e9af8bc1d71f758720d5f7b2b09dc132a648ebff6ca877ebabfd659cda9b64892dd12a1c0a599caa24a3b36770723f0fbd25b9f400ccb1a04638e27a1873771e8f0689e54b3084cb9efc124b5025d8247f6eae1e60f9e6ffd428b52c6433ee586b55e5a2c9024aa8e04eb627a0b00a7cdd7b99ed0b7973345188c9f4fb42d88e4c30095c59f9b93c195c74aca05e569713cbdf2e5160358a1df45c9ecf22829aeb310a4e149c4fa2bdf30e97807d97ad6eea562b80822e8ab480763b54495b2c0161e2ca3a8745321e18a2fba06b51a467b2207af031df359357816195580651ce794be24b5a3dca73006b1fa0e01ca6d6ee75f117a9d62348f095d8c13d883408aec4ac9f65fa19ba381dde40ec57d8f65d2729806e5e903e530056bbdd47cea14f762bd34c9710e2d21a0ce04de837aff8b8c8ccd5ea2921f356066d5854304720de7d199f025a182b9051f0464e923646f9b4ab6d4881bb88cef1920dd7747b522580b00843f27d07577f5099463e99dcfd5be0b72bf4e20fefa3209f730c9adbf8726a9a1075507779d7e073c54e3f834610de1da514d367dd32e66f27acf8ba8820a063fb3c72d5dc5030c6cdc62a2383d6fb115e76ed7b45e2528c4580e495adf1a351dcc8b2160709407d405eec763bf84e2b9ec2ce97bdc9ae1e9127845e92f014d2ffcc136fd084503db75595fd420b8a7dddb14cc7f8059412f9dda46ed7596b11660ff20046f16082e8b35bdd5b7a8c10d677bee4a8c556c6d03bc0d0c97bf2e6862dfdb8ae5ce0dc639bf9568d03c717ca043672a7dbe4241576c749d7d2e91e7ba9532e9b8ef084e3c4794d74f24cf803482936e111e0248e00fa0e8514cbfe3edd74a93d1aa0a9c0527d496d021caf7954201e2f092991ce5a845b5d00f85591c664c22f0ef036476d36b2d81e67f18578d7565d100c40a51f8d70fdc27c39370986a07ce6a036c4e6db098965f8610fa3392565762fe75a0f5855809938dcfc1746006ce3608981a3f3af87785a24dffaccd713c2439c4b582715838b32eb975c1c6c1ac9409094c061970b1ea9b4ecd0cebbba3dc9629d178df1957c42d305661a9c68ec50df656c2e07c726108d6c88d863dc93a3e724b878289d6627d8e61f85983c10b05096b8d1323e8759465e2a221b041f75315bbc276d7c025881ca0298ce4a0c809df2eef46db656ec844a4349be1fa5ed00db8ed20b09d60b4a7cb6fdda5689e056d4fd94b11505c2117194505e70b4baf4804f3cad0153e88d4673e494f36bd0508d651b61f7742b96ea48789a197b8e3be76473cae6b9b7fcaa7fabb23b33101b682aa545c9c4a02078b1c6aef702758fc1b0e7cf19a8861ce9202d71a10ee0d46df321cd7e495ae0848505116145de21b9f777d6cbbb2ed93f80f308e78820cc13cca9ff06aa72e4af6b20e21bdded93a509ed4da8e3c54284a665f60f02e05d2ee26e12942c063661eb2db0d01a534401bc204cf2a7351451dc9da4947c40965bd9866450e8dfeb3b7e8023eb047212fa63414824e46e7ae34b2f12437ff0150b659fda386d686a7c00eba79aad23767d7b5d6978d97a089d646c4339e5107f366da168b01b376e1558bad76e2b65091002e00e4931e85f949e64dbd6dc00b4d2d5a9f24e46129e3e118f8e06f9a22476f5fce1e6a2cc8f93e61e2da37300ebd220f9285e3c5341370bd1a7488322815311afd7664f6f6b86fd47977e76a02f06fd5169acbb6dc7d6ee7e2bf1ac6fdcbc5488a3646a5f845ad0a427c68fd0f5a99370f6da2b6127dc9d6da4d97fbaa5a83cfcf795741fe6c6eecb393a40f0dc31aa572bfecb3ac63628855b3aec9c546f8e7c9b4144280a07f107b1decbf0c8dcb257482cc5eaed38b436860b0af11a2940a657aea7849d374039695f86509b4595d3d52225004daf1d5369688badd1c64ed68174697e255c6e83a84ff470d69a53c0ddbe1033af7a27c58c7e74d70ed0117f68d077b5cb099d30230b8950a8c390a806aed471166ab7c5207b8a80f1d87a9aec4a8fc717ec56b98fbcf7e091a339ce93273eba1c4cf56be9433af7a0cfda14c5f2de8b28e63aa776ed37f0fd96bf6139a2d88c712727a32d3ce4ec4830481d5b1bf8fcad9d334006bf0190ebf3f0de1d08b741a54147ab3cc90899667bcdb86e69c818c8bdb2f599245e001499fc7169b7186c605c59ecabf6901e329abf3e13739c3982288094f9271be07a4f40eb891d803827c83b9d8a0bba6ada622d8ceccd7bfd16523675da25f9c05fe42ac5a88869397c32b1a60c95df962838750171e47784e4cd61f10c8c5e40d1ed9c27eda7138ab72c2a80e43074c16f157ae8c721e709533982ea0b201ac04cda5a7fe09b8cfcb9c27672c1870d6d03c65b249bf5910b5112d0e384003fa0f0bf2d64165916b90a54efa180c96da43545eeaf130585fa603f0b50f35b50803a192bb5cee1b362bb4d19d1e72b157b7be395a048272f0448aa3ca5652aa320ad1a810066db0bb7ce764a2ccc353c96b0606972f1d1f987a2280c5a9b58cef02aaf88c08c5d53cdae3d96d23b0cd5330007a317afd0532dfdea2c622d3bc53081a650c2cefa573b456fff9faafedb15d291b45b4fb4b142d3e63ac31c098fa04f36326c8d016eab65a049de5eea677ce7e623fb576e67cb99b8a9137ebce7300ac60599957fe56e4d61230ac835ee3a15bd1c678ead9d00672f860adced001007e1e7cb06968c170a16529be8d85d6ecde6778889369483d61b1c4e6c3a0100471fab2e3257a870d0f75edb39c7ebc74472f5afbf8301a190e68576eb464c40aa11a0b0c87a192df4633919945d048969d1f9582fc271cc6ff87463603291a0ecf775e8be31a9b863a40ab67f0e94bb26c396d92d15235a0db0ab605a22ce6079bec5a8c556190e483b0bd06ca979b6ce5748739963a3f3fe13dcc92ac6e67081163f2af47b7d681754f5b09b720793084ba033f8929991a3b2a8b2aed307509d2a921c3de6af1a7d08215ee4b9d46c806968e6faef4bc6c7fb7e8955e487607bba839a0daaa9fb5781b3795ff1569684288e1a7063c837f5cddab57d305780101d271a67e1c33bef4225572baffcdd33921a89252d3ada4fe6812edf02a1004ee2737ea249f00dd0c8aeb692ebaeaec692dabb5619a2df477c2dbf3964ed006162ba9f874ec94a6746ba37348dc8c7ce39e24c28fa819da53f019429328e307fff4dd944d2906037af3c3d898dd092a814b5d281b93fdcc6d42582cc5699f04c2b9c8a6a88d2710eba7c0ecb8667b0011f17ae0876ecc75c766146ec3e94b062b095c8244d313cdc7637a6f46eef96789b2d0ebc7163d26559755ed954fb3055e0d8e14092b448bb09450d526cf4a239f88b115506811aa899a86aabdb871049a6c3f077c48dc9fc41c1d16858f656674bcc941dea2e094d058725dacb6fa065810732c066f16654bc8456b1eb072f020d1ee251b850767b82f4774dcae770042d87abbb45ca34edabb33d6c43c9cb22d7b42d6a42d432c066c6f504140440e243171b5856bb2488b34e427456ab46a2f0f3f95e45d6e86ddc3aec31570020225309e3059dd67b9e8f64489673e826f030c74aac9f13dbed5cd8f3aa0b683091d416f119bab7563526c734328a4ca1a3d80c3d4b312514821f54e1eab4882085485bd63e7ee50b4e96b9726a97553917c15ff2ca150b8c15f60f44c8dc2c90c26804db46a701e7205bf72e8f8a7919651b8c9ba75d9b833a4d40ef7238d69073024258a35f4e48d6590cdf2820088cef3179394fabbd395a8683a9752f2db0afd28a18da1a71c6a2b932bb292905a5ebe8bba63fd00d50691b9c4a7fe7bfe0ceb0afe19b86199f1315d77fef7155a4a3b1ace55bd8946cd435a35a5d8a1cb054d9b0c672fca38f341f585b8bbed7d092eb859163488b815c3c73c6c2d21c2029efaee9e5d3fb3fda114aa96b73b2cc2636cef26d4919252661a1311b699bc0dc08cb14f563dbbf9ea3cff4e7de887d90284f08dff6d67073b0ae15dd9df5c033904708a576612bc33e09f3467ad6f0b89da3b96db9ae638667d5d20ed2c2e0de9f9615b5a81d0a7149c61db0cb543842ca9a917973b4084f2494d29a07cfb0b22ae61fc5dcd7e85e9f10e09f30f09f4fdc429434d087b595fe0ef6d02558b05a478fd55763d9bdd7bc3f7aaf3e70a7634612bca2725c07ccb3c5caf4b94820125187163ef230fa9a02818953d0c3943c3fc4ba5e67e744d2d8d1f409a05df05c1d0af7c025a4cd0adc50ad8543551adc7b9a22c3aef943098d2d5bc7ea76406c822d0a8f74013f63e99ed4899a301d2b59c4ca8ccdd38ae5cd17482eb75d40505b3fca554ccf7a61d65a1f7edcecaa6ed316d24e41233111f98bda2742f4702c66282815cc03459f49683005c89fcc7090ce9ec8fd509f3705f2c529727df055d22674341862a7bfcdc4d42c89633350e0f511c7a6c569fd05d5b839114140284f4bd1b02e5603866a914b240cd5e96bf6cfd1abfc6513571ec8156f0dd400fb43b6b3251858e7bbf711c088f445199195979022c79171ced68beffdc89ed010cdf32ba93253bdf3ad461017b1c3c5da4f5df5366420ffb5a2e7bcff50ee40e3ab376e267270adc1f8884e5531ceb9ddb013965272c6125fbca1ff85b306906ebb4e1865edb4f68df173735bdf0049db1c5687497276d7bb53079f55870ca029eb76590d5fab62bf9698a38b578cb1ab14cb5437277fa9883a175f0398e62001bcbab21cf6292c6080533b2bb77b7b2021fff0395897405eda816edf0cd3305d6c2b951347c07c9b3ad7a88b81288fbd4cc2b2aee67b95d7a099890b8a7430706fdef1356acc08dcff2d0e70c7bd3828ac8b1cb7cddeda84038163d6695f503b036d35d9c645a6f776e5bc6a3c7024f7559e5ebe457c044f4784fd3aa507f0190ccb4c3f487cb24a634405fa8bb4e6d37e43ff0371313db3e822bea9dd08a0e18d60d987cc8cffe181df9119601dc83d7a9d926bf361a4635cd12c5656d0307857ce24962e78429bc87853e98fe3bf90a0a9d13f4a553f077dbc50cf40f1d010f2647954d7ff71365e6590e540626a4013346367c5a3a2c84ea4a7df3b41106686bba50446443d47a0db6e258298dc1ef35626ef8dc27f4644962d08291d309a4366efebc574b10dbd215f09f57054db02a15d1167fba42be3db0e7039f0e0b6a27974e4b67248f97a668e13d56ead75ebe948e2e767e2195ae447a214f2a02563854604d23a81c9eb4b022c3638c29c1461c3b187f792c2809c3c1f52d59052b9ae46dcd7706f57799746d3879e2485a41b175f920421bf14e0a0d03fe630ee596d33049884bc1aa57f6a9d58965837e2bb93da72d29d35451bb198c4c600206373460171c2f0d188618b5f49972faeb85d7c218889273e2ac11fab52f1808132695c28735aa4464b2819e02536158585506a233ce485c0eefa69c3a604806bd13af62bb2734826bfa312ae57d8522cc09c216c83a096a545cfbcf7e997501b2748a9beaeeb926edaf292b680d931dbe9a6b693e57ba49392d8e8c8dafa80152d9cc3dca33a73e4e3b3b0dbae65f52e8065264bb2d70c6ee0e0a309e241509449c78caecf052f1abd341ce1d889427c6516bd44a1137fdb5a087aa736bc0083c510450369cb519a3ef846bda82d862eb880b09fd8806a9e6887a1ca7ce280ab3a8e88bc96c92b6885b87cb43f760854c7639675472f60edc603e64f5e2f90ef5232ab61a9dfd8f1d1402155642a7e7bbc2aede03c33fc526935b32b671d406 true +check_ring_signature aa29e54e925a5d0165b1488f4256ac2f451eb7f7c8b880f97a33c979587923bf 8ed31197104d807bba2b31537950b70ce00cc5b74b09403fbe03ea363d1882c4 20 42297ad6d91634e5c29997d66f507dcf908b006abc3fd45e33857120bf323b2a 24de5735320855a7854dd644767d8c765a095edca1995a7f55373c91ca4e5207 1109e5a1bc403405fe7ac0ad3161e60abb9e3dc8a69afd259e59c3b1f46cb909 e1870f4029dcd7debc43ff669aeb267d150a50ddf93ed99c641db5c4910c8304 0f5e793ba31e460971296a761a1522aa31603888b57d2423e9925779fc562b4b 99daf450785f68392698ce162f6aa0bf1b2949968ad97cab40a9c258ec26a666 f4baefa548ec2ea0acff2ad0e8a65ce4b410f4d33d68301cc990b8eb241b9268 4137e0631272b7af707270fd5628dabe33e911392d06989b9f3dd9041a66270d 09010681f5cddb4aac90cc9d671c2f74d4fd82209dac11f49b79a178847046a3 70f18ac341447dc04d82f682c13976a9cc19731ed8e60c2b14200ae23785940d 07ca71112f54b2cc5bb311735b438b8accf2b921d6408b3abf530a526a40df75 c51740b1152266c11217564edbd032bdc4d48bfd3ca892fca11ef5b0175bd818 da9ec37f64c6edd60bbc5c08eba0b035ca905f8397e44ca94b0037b81f0d2891 d40a48ddd5396f0083f807e4c224b6bd758fcb9fdb087b58ee66f019a5eb6782 3327c4437aae9c39c123c364eb54f8fd4c63ba1508f33027d3177ee249cb1ab3 5137396b92659e7feb1ea99b2a79aeaff16f1cc5d1831df2b5726ea64d4a19a4 6b8736ef08bc7c0756c9a109ea0952d1d8b72de2b260fdd7f3d13706dec0fca1 8e38daa0cfe30a99edcb492f3fd478cc28e94abffb8567581bee0080cf76543c 75d843d361553f4ebd73c26a462b6035a65a8b380d629b9cfba968f803ff66ac 475620fb64dcb919d0c9278bbc5690599c38365620daf97fd7c97d3577ec15a1 c459b10c7c79e32994fe40cf4e3dd99e28facf49e9d6803707de10ec2162e20e38be63d7d98d5d8b0f608e5656b664ba518645d387b91413e63a82c27cbca60fb77846b32d5e06341a2758c9982f8a577a7dcf2db670cce30ce21cb7932c71032e1be4dc464603e0c4ab383703446fb15c1d1dc1247a3cedc79977a93fc66c0b67e57e56b9d8e65fee9dd2438e2437a1c8f3fee94989f7155b3885fd044e670f5df4384665509294a33d50309b9016c1db50512b3e978279b03141e39bbd2b004e3b31afa669f91c5ba6cad8d7e0edd169027415b1003f9311557a035b043e09cf67786dec6df5a56a63c1fe06825dcbbe66e7bd35db24f78141ff6ca105a40f250e6f6d73fb0af82967560c6752c9c34366843f57d55c72ed5e8988c5b8d60e118b0424ab77b89a4f8970b842182f3a899a111800de2a3af7b6b1c095c8da07f22f5180c10a8ad26067d23f8bcdecdb665cc94b0b7f65d07f9806c378346b0ae7744c47a0009440c8d71043d554bfee209a3fc44fa4a46ca1cf9c971bfc180b0649fe357042477853f7fdc2b363f62ca4bff767418679d47125c4692e9904040b6899070ba604984985264f2818371416c41098a0d41514c81b19c12c2e620cc991ec833941dc95890764bcd45d85e9831153583d12c53ab0cab12aab39ce05ff0c613c96ca79c56b84128ca19b199a35d8904284c10b0e4ceac1406331350bdf5185f5b4d05df1b47b8185572dc37f08bd898936498a8ec9a33d9194dc320f585a1297f975c254212de15eac0e81a4f3cc953f52753439d720a280ad13cb00d213f76dd0e43c735c5c8ee88d12d933f685ca66d42ad714f36c5503bf8c960080cc6968406871da494e444f4f8840e9d7a40ac745781c220444e7ae4287d604abe7724cb9c5ea77bde0f6fcb29367eab15281dc8e981709433eebeecb53a00bbd2bee58ec02439f1c0a9bb69a2c07288dd99c07de0f819f834e5db578d56105dc6cf08b5b8911bfec29c7817a5e5d64b925e5a336c68ad1876fb4d0a6c627095388b68cb07dcd3babfd2ced0fdac12c9e0ab847d2e95717ef3010c8a8bbbe03437213bf6901d7290dc83970f176647eefd0be2d3ce0a49af5298972eeb81c0434d53a8f64f5478e5680e8c93cbe9686807c287b8d7706806c7176dc962bac07659bfb2320c1c8eda276e841b05868fc136c1eb757205fd5a790cc52e13ab00a919815b9a9b69598d247c7ff56ca3e499159e01196b04ec851ed3e6efd81510bd83a62693f2499386d07c05388ac41e6dad7194d9949aa57b6f7b9bb5a0a2e0c087dc3dc9930e476f408d8873fa0fd8f53d83c8c13933dbd5ca2d6145f739807d7fb98a46ffb310a503a9f903adaa47c18ce2dea3fa87bfca0ded83094ff4102face2dc8626113268a35a50056eb4f87f3ccff723c4ddcd16ea224adcd13c9025c157aec1b80356ee31ecb045445759ad6f358e61bfed57365d686aba8ca72033f5a2329bf9a51ebfe376aef12def169e5e18a9afb1378e38564c79bbca52600498af3b53daaef7ab6ac76e66c32b82ed3011de3189e44c6f235aab510b93606277435c4edbaf480240991fba9740cc9df520c023a9e8a8bf88115e7fa37b20bab588148746928491b5af9f1d48ce7f20125b275fc30a3e92704da8d94d76c0ee850d5d92e3ab1130dbf3fc24e5f239746c3edc0bc9ab5d408c2d2c98a9f1d0b691327745b8ba134eb0b21f45620d560678ab64de27fb1759312f5de255cd707f080ddb294e662cc684ee4dbab24cb6b540bd60f8a4db1e6866f13fe4a41a404 false +check_ring_signature 4f1078ba405ad189712a83538382ce38986d52e490918282809438a36cd47f69 dc38929fafda0db98dfedafc0ca88ffe5cb0a14780d30f59b875760e54d039e3 199 f55c509ee93665bec9b05e632f8ebd48b32e10d3bf87db0312619d13fff278d7 50648099df07b5d15f8db8bbbba7cd9bd692a4f6f4610bded7d44f9563894945 cbdcd2807e088e4d3c66fef80dd45061f1cfda00bedab1c110d7206a9264fecd 958cda27b7e5e5d8734fd6baf6702715cdb1e887c2c3fd47a9c67cf2466f6f63 3e5e8baba77e7adb5c95c725e604ba7ab74d1628d165ef7c334fcf6d4f18d201 1a37b429dcf8ef39034b8ff525e8a38362d499bd2f51059b837a583dfd17b410 595d042f141b374ba9dfdef79b5c4861dfc88b1ad4a59e04fe8000707359fc0f 011c477e913c117c37da926241099e23afd5362854bfc302b19f60e7fa4e1226 98e64ac5b2b544a2b5e044c24abe9582ffdcfb6067b29e348cd712b2f7fc0abb 82f097b41c8e885047345926e1c57e83140aa1574be9e56e3814936a4fecb862 2c3575111527a90a51a54f8cf3b9623341608d4e85637f8969e121ee18b99a2c 842660a8735dbb556d5a8ca81c0439c238655af40d1acff4dffb0a527a7e4b0b c5661abc92181ec35ab5f1c07ea44e6f4c45e71e2da5c56292e5a28411a936ee 5580e434c3f7e4e5eae523be6b63e77bc8dd217a957e0527630bc26900c75fe6 70c59fe86b4d012e24e321d91a845a6e4f9fcd5d0121165ad80f6bf26d99cbc9 5c14fd7e8e90965425c8cae69b5cbcc4506905f6f132f9ddbd3d8c234c4acd27 426478f4a9636dea0dc15be2462fadb1d20e13b556e615cbb6c4fd812830ef57 ef9e0392028c0d9fd5f6b9ee0327e73a97bf9e0bd37637031e8f8b13873fd20b a1cd36cac2a3543ea272a18064ea46115750ea5cb1abbaefcb6ca85549821dce d9540f04851511cc60c3c982545028c32f1dc8c41c826314f8568c9f69a3b974 e5cf3628fb71dbb9ea921cf2e8f5dca37cf3328e6b5decce3d7ee842d64e1cb9 edd6df2dd6388b9ef2a1e270a98406eee6133be2f6915be848421a6f6ba24c20 a4e4d8ad8596cf8207230fb77b56fd1464cdddf30ce55aa8b4ec69e57e9a38d7 787f6bef5a09b14ac07cbd281a718daa4cd9b53b2a8ba0ae047ae8e970143ac4 8b2891d6656640c3ecaf34b1186a8d4cd936955e7e52b148d607bd0946d237ec b0a35a0382705d3f04b1162085737c0889d7e320e697f9f6c11b00dc4177fd52 8f7bee82c59c7fcb2ca52ea0b196eecedfd1ddd4a811bf1a4db99a2ea6abbd70 3fc8ea1c66e7d25a61b10eea076e9130d3e7f3aa07d83e6c23cd22cb7f3faba0 9af469c78bfab48053028ec2e9892edeeeb250a3aa520e5f1a0540c511d0f6c1 983384124dd1a1935fcd2f01af958ddfa4eb4ed2a6a120a7d9cc8d58f0391add ace35130ecba300bdf39f03d68e89eccf2687075d30af483d07a7c3813f84eee 98b22fd8d8dfaad8d8e1acf2b3c73114d2f7cc516d35f4cef1756d89cbd223c9 45a25b2ee2c8bcee7a997cfb68db68835a57e405a9b01dff6582814066efc400 f7cf9f66dd218e1eb96c01405c11d8544bd59a0f5ef124440ce980d27bbc55fe 18012ed01db9f72a44d4562015da6cafe99bf68deb560816a2cbee513b1e28e4 435cb6621cdaab6d7fb42e43ae65e67e8499c49cfec327d117c238574b1325ea 56d7b3fa09f640d204c9d57f2a6a20897e4813cbf0a380c3a34ed552e3d5afa3 64b5872eb57d789bf42515552049c145091e60dc67c6b368202c07abf349e244 83a7c9c51b2c6883905ae7b975d9b6d1d6fa727dabbd399dc42f39f26d852997 f1a1faa441ecd98028b24fad2ab8606bdad78ee985f919b5e242cce29a5ecfce 82aff91954589c241d55e5c420525890eb71f4a79d7a02c931e66f0c35d08182 62613a143c692f8f2d81cf1598803316d5ab97db16970b62f0ed2ec5916f628e 41a3f8451b8e498fd6f3b71f3313a82ec8946b40128d5df2dd79c7c4f6016257 fd69a5d52a54e6182d2e16b263ddce97833f66c5c371e14f19423e8bcb909105 f1c1e5cb51728256a4ff4dddc89eee610fe3be96cf75e8e37a86feca018f8c32 363eca672e28ad653cc16a8d4e6cf43ca029c47794ad8966b77981fc63a4c747 26ea60e1e73715266ffd8f128210a7ad4854ee00da5017d81a2e16202ef94a87 cf870e67377a3f4aa21e710d280477c100af25eb308984c572dce623fb370be3 5cb031ab564dfd6fd000b201b61d1a5ede826d34dbceb4c198c4fc70e55e4a12 57a366e86ad15bfbcf898ccc3de9cb0bd605e7f291e235a8a936cea54e132e6f eba16d0525f5816fcb979c0aa28882ec32c165a3688f84933c96d59c210e3322 2c8dc58b310f4ac4347ca2a78b6f8a13cd51271b531aee58dc3bd0bcb09a06fa 7395122c7f48fe5a52a80248b28ff5f669c1ccc81f9ee31245ed75e440ded7a2 f3b9b52605b2299ab5c66933701d9a29656b0f87b3fee5b3285c717cbd3a8139 f779b3e12f63c1daca9192cf454cf94d072470ca2631c2c91afe3b2e0d4eadc9 a91a556fb2dccf1c9a25216bacee7fb6a6a6836a95783cffc84273cc2f9856b0 1ef809b388f13508d8f3f87c5c999b1fc775453331b27b592df4aadb0efd0db6 931c7552035c969fe93ea5176c4928ca05943a7ca6e80511d4d90d8922ec0b1b b815924623b51bb97d3eb6e14c5d6e52fda47f0d2189008c74d1c83d390c00d3 c82c7923aa2554b4f7cafe196cc6a266727b851c4667938d940c89b548b58df2 0f30dc0e9323e9e0e00e84fb7cfe5183d8275c736c468b08d3af991359588c87 7d77cdb69fc1ed074f33c27d14dcaf04d5db9c104bee39a8642293292ce16622 7c867eb13f89b21f97669e9b127c8f44d0f294003ab4c8d70fd2da5f9bd0a38b 6a49c2ccc8c820e5c3f9786aa9aa7a6527dcef2b8739e8d270e863e8d4768cb0 e919234a7c2e5d70cfae52e0f034fb6536e234f7e03be421f09c77d40a8abf0c 5d4f5885cc907a0653ebd7c2683b00dd68c485de0c91c44dcec20651a646f90d 0d0763bd87ca58191b0a375e8ac612461a91ce60d5b3ce93accc1f1acb84637f ed7a1073400945f7f441c2f01f2cd7be689caea9a8675844ad0571eb82ade9da 38f6e50428552877c5245eb53481c1cc71b84562aeaf6310aedcaacfb0d3be4b af5e051f69ab2b69b152049f786176c5f80e8818550a0dd978edc2b3499f2d32 697728f29b27eb142d2e84f8b5c9bcab63679b77ac7fdaa80538c597f79cde2f 618f2f2af10ca53d545da0bc151e9d6fc8055b79c09318b44639b9f91fc00ab4 8b14c6e6450130f2bcfa20b5f696a49da70479db78cd3d02362b36fb78a3a72f 7771fe63d3e584ca5ef567a70aa04bbecd5889405aa8fb440326c425a6f5f859 f577cb9ee2df6461636e8bd28ee2154db2d8d60dca38ee78e017868b73da824c 3de9131f0bb2b991341d9b5da5cda85e31b7c26eba727428dab26dbd9f835f9e 71a2e59a57a945178a0097bddfec59aa94d032879871b101c6b5cf9835854aeb 52cdb04d7932e781e5c11019d3d5587cc3be8fd0e9848919ceb05f39498782b0 25b03263e765ebedcbacec4a121d38d29a02ad256cac525bd673af03bac7a956 05fbad0cfe9c4ea4ec8440fae939228a4621e86aa84642848110222febfd1e3a 6e4f0c4bea50b6c362132f82d8e7a30ce1ef147ecc62c364da37fcb7dd790faf 7f174be163bc1f5e250a6a6eddc432fd9766a6e9abbab269e2808cf6cb59bb6f 268e15ece08540f00d1f68de6391a871a8655d8d8ba4e72d13fb0d33cc9811d1 500e6a9187ca1cf4a9baf66391f7b86fe9861c72f7811dde994cadf02df29649 42c9e936fbcb6f9d03ee6ef18ca239fbfc24529aeb5f726ed115332bcd23ddfb dc78cce2c0ea32ba2a66d966717d2c1de463273506d09d98b55476ae5cd79f83 682b71c724f9bbcaf9e7279a831f655fcee87e4dda77fbb398cc230eaf1ede85 dfcdf42a2ac6c66d5053f89260cbdf88e5a3a985602dc9e0f558adf0220a73b5 5df76cf7777ee2aa9ef56e50e553c73bb38e6a38e73466e07c1b74df194bad33 8357aecd5df964bea84ac0be0bc22c743f432085c772294dbbaf8c7a6768b816 940835ae8c43d83f624e10a1fe960171135dc9117a51534c9fd1f4ca7ab37131 2f758dc7a5cce7c30f22cf502194ec609ea7985dd037daa3fc48d6c9e11e0804 4766e1f400c3fb84c2a294359d6c93e6db2bb4cb4e2a5532fa0cdaa95ffc6b12 2f45234f52f235295bee6f2dc1c577b032b9b2abfd32629a9592ae958f19f234 9502df54707adc2371b6d642529646b1a5d33ce0bd5ec71f039d814f494c39ca 73cdc6887a3910382c85f72e015b921abb809cb979e2d41fb60c07a92534a37b fca00127471150d22497d4a6e246f412cfff4f0a349a4c71c827d6364eaf13bd 2a32bf4fff54e450152f091173aa515f5cc7c86bab31af105f0a0053072427d6 883e728629534d708e8598b80f2e45d9adaf897413e319160bde3f2f4d2c259e 0202969438add1a8714384f9d5c9e301ae3a6c17373aee217030afbcde937266 e59c3949efd54d8886c88da55845428d55533fc8a3db2509981101c91ffd2f0e 5e01cfda88ba5104a6c6e441dfb0e60e986e689f1d2d4e89cd318425cc7cc5a2 21c17fca4b39d535031b7cd93776a0f4e79dcc32cb54d1ac7c009e961cc29705 74acfac9751ee8b923fa08f70096f5daab8e81af11c8cdc1e78cd88a8a852b74 fc371f6579efa8728b4c73495803a2f40d8b2a90b775294ccad6f2ffe994c885 ef91da190c345d3d54df0431ecc496a819da8c5f3098c37741d9e74cb22ce626 ef4866a827ae086f6f14f1f346c292d708a2df8f7b307e22847a3c753ffcdd46 2cf8e994420af0c82d942c968ed5496111c8d196aa3d4003c4d423c159ae3bc7 7cf257bac240e8b57ed3b45c085cf25977ab205abca90360a106bf05f8d0fdc6 79391ac0623786f90eebeebc372d275c19906b401b6d6bd8d13c50ba7350c4d8 8ebf33caf13b38dbe9723b7a5ae83ef35456fdd6b0639a058465feee79d46277 fa0e3aac8cb758269f529c60bd409ebcf41d310783d9f8bf8725648b89ddf967 bb0aa55b77fa2f722054657b23fd4cbfbecb8ce7aeec4647dca99d09a7f97694 36f3557b9b1224faa22286cae11a106a8fd3e8ff87a8367d06e01df6a61f4c4f 190465a98207ea0d0331b6e91457b62d95068f6dfc4968604b875b5a45959c48 47dcc793a8653d417df0c2aa16e511ec980b8c6766554e34894cbe28db8a3066 ede2897da5d6fae0b5ab0ea13cc66aff2a20de41ccf53d35bf3a05e94fda19ef 4f57dba635be55cb6b7b9194272733395ae6bcc699035779910e903f953be6c9 826b109ca9517d08c0f0552fc5b4642000c90e7e3cdaa6350cbf80d50b69c839 6b624b63ef2200ed89b7994cec0652fe28820ab65958e256e0ebec745417515b 83f36633f3d906d9f8c9872236a38dc442830db6ff57b0559e6df06492e78f15 ca036b793cdd185c8d1b764310d7332f6167c16d74a8376e93563074be0c2e3b 465195569823ffc3bc19b3c5c07fb293763754d371810abef295a81985603a52 a5db90fc48dee88c93ea70fdc62247b047d721881bc71ee06a094edc81686086 1b1fa076e5cc6314d6397b944826d1692218552e3fa040f2fa6b93e564f1755d 8a8033b0270a8a2f64265ec8d7e26a2601bccf0a99f832f386f1ed29d7256eac 3dc4cae6d4fdaa570e345d2779cb6fe7df7074ec7888a9e0000dc753be1afbfc 5b2c0d944b09b8c5d0b3e9d8a9640ee33cd79813849477238db928e0089e53c1 5b36467a88370088865a2af3cbdd37dfbbbde35fb3d19fc3c6babda58ad6949c d54105068bfd52fdc005f8e7af5933678fb7c1b1dc81cadb403b2a298c579c4a 36306ae8509ff7bc8168a58690f0a9144453b5a58efcab5450c56552b4b1dd53 3048d4ade856f09a73653378280b7553fcfb3d0baf035c7c82180c5c63cd6f63 bd5bf909af162c66edbee7662f1648ff5d0ac25e54d09c6e3c6e7882a890fa55 5dc3d03f9b463cc98dc9d9192177a2bc7eade343269b24d7fe3ff00d2628dc18 45e5da25ba47f6a50c42266c9fac09b78e22baf4b5962a035b25fac8f0d83cca 37f6f0e14567b89f7f81c9fc03d39dc6e1b589a1375ec34531ab4ffdd380c803 d20ff522677b82526f21bc380cf24666706117921305e2025036365cccbad337 fa73fd71243ac046b5399deecdd70539019262c213b1c973ff760ad5f0eba2eb 67975ccb0069277c60846bc00de98e59f865b3dda37bf65f284e8f9894a0cdb1 9f6f0fbd26ab744742f1ab92cb2a8362e909f8d0920fef4e012bfbbee3d82649 51583b02259a54d68ab9a420c3805c6b9c4d222999267678fc97909399837768 a893341d64e3f29d4dfe15c5d9772c4c8f299cc38e7d443daf14ef425a669eb8 fa6bbf3d8f8425a0cb7510b087123afef1e77d448dc52d7c593e6f9629ec66ec 39c094870ef4f34ec999b0daed3d3f4e3f3acb8035cca25d328b0c421623b10d fbc9df27d0789ecc5f373ce78f9d6d67cf832c4239906f76edf5d9120893b6c7 edf1aaf3f2258c4f8aa88f35ad6748431fcec4114a74b5dc8bd56dd103ce699b c1c2922a5d2923c80b1ef352b3d022acd234aba5a1f442801c96860a35852885 62257830f2339f48108cf9b56135505b657036470829507b1a0d8f0fe5de42d3 c835069dceff2f8856e1ae905f87c03ee4e92a475035bb9a135ee09e2c370e79 cee893556b6905b079299125f62d3054e70297185f8bf52683931bbbadb3b94a b2e65156b50fc703612acd09efe6191e3d190e73096a76e9250e993908ee369d 4fc8e9dc9e50c391f8eb52590cf9114445a9fe3731f67688440ec0f956ee6774 25367bb0c44fe055b7a5acaf7824c3a2e682d5182bea0526035f5a74b8b85294 c2883b6531f7a8a79237ed5c888e12759ebf7754a65be534c6d668aa178d93a6 280ee1718fae393108620e1858e0e6f813a3a165854f49fc319a216b843d699f 50d6fd6f47f9be925ae2f2f719d468a3e24576f2bad8157434228d1521ec33a2 21e9e79a8113839c20e427179e2e44acd492ec558dbcdaf34a30c9c86c9c5ec1 6daeb3245021c4d980df866a3141a28f5ed321cc0dc6558793cc3565262793d7 a20d86c6b6689628c98427a0b20587bdb53ee31b6e9c4fd0d18c09c1b4bf8e91 4c5061e7a1442ed59f4bbda48d01f96fe623db5484de06a130aa2d8d4ddff113 7a156863f0e41ad011472657171d317c5bb45c140778826b3513152aea382b16 df192b835f3213944e3ab2d2fcf1891e82781b120f07b270bfce41cd1bf53f03 7c3b00ee1a1a5153a968c1c860799fdb8e939d6daf2697ed5dbdb700497b3ec3 9d033e38f66a1ce56768ca441139c219717377cfdea427dd2b97206218d1f466 c10f4211197205db4524380939d6cc9d4497570c6e2e94c9bf8282465026927c ff8e7f9d867374e5c9925c7f2feefd42162b7c2e0f7dd426f244740d2607a7a5 91559c9013281889012db2cbd37ce8dea3c3daee270b72c6b38bbbf376a25285 b7e9c06ea87d0ea929f598979812c0d5902f65a172bfea88b91117d6e98d9d5c 35d252c7326fe1af3c1d39f75f0a1f5343106125a96d9c40333c4f869483d7a7 c71471680a978863a7cffdebbfe48a86b45c35e948b16474d4ebaaf08b89584b 9dac09a5a5ad871b13bc674c9c35ccbe62dcde915ee1e5e1748dbd38e3ae95dc 91c666535bab90da26f064a44146b5963aa7c84bd0aa583677f4d94b4286bc3b 23626856b798b761041aa1308b34facbecc529b26e0bf5803d5043701e0bb9dc 3071bc5b1e895dfd6dbbf5d05e3c6f33cf06702fdd0e46910195e5f3985eaf0b 28fc0aba11dba526b1f6b0813a4ba687089a092cb62c49da44fd598acfd24bbd 9693bd8fa7ee5f9ffa201d3f1c58e8d6ceebf9b62811879087e2b18cc7f42ca0 ffc1feb225c8f0a82c2d69b41f1e6133b023745258e71a0b71ac1d63aab60cbf 51f14ef73d71c85bbf31612eeb2c7be31e9770faf5dc13be2fb996d68baa7856 49a95d5c162a3fd4b4d679d173259d233dbf96a01f49115c9eb96a20ee18f83c 34a9211da9fa827d81c668f51b599fcca0757a3c0d9bd6656d80a647dc591063 54d3ba7b028d1c9919af912fb3cafd833c3c128b7ddd2ad5ebd1b4e0b8dd0327 ba7766c3689ba6c926a713a720124f7f69cc9c02b4c03d3a076397a75bd6b388 474e6ad990b87a79e2cd693a33df67d6acf7c048e708f12acbe1f485fd190411 61380ac2d07d29dac51d5d381a3aac9839c04f854f9fb05616c30d7bce3061fb db8d5889160595c6eb976fad83660382cd66c2b0605d158498042b2b9e1286bf b641bae8bdbf4d795f4c5d2a274530123c1ceaadb01bddd5f5b1a2bf293d4a55 caa69f7dcbe5869a64bc6b24541ed4112023e987e0743fc97a477485918a496a ef17a91547a069ccce584b96143152c178b812d319a63028442d354868a9fea4 c6aecb218a519238c29309b5aef309ef537e7818412dd2a9d72dbb801bd20c07 27026114de796ab7a539273d2690d6c772731f8fd53153d07d1f023884c2f0e5 482ec2b3201363b20fbd09626b179025926a48ad013fb3cc31bdebd54fd87260 44b77dfbc3bb80e9d0894b6f8c76b7f07f7b0a452267feb3382900fa9a4cb750 3891f47d6728a5ddc52d0e8a9635cde0e0c61b7b9ce5c06e3d523c2f14719f8a 2bf8a8c31aa05dd032c4ee10233c17b3271b4aa856381e1e4876557ebc712cc3 678d1c172a2b4ca49662334a369c1ce72e494b21b864c9d73cfb634f00e9f680 328faf6f88f8162187bc45119589089dcda6a657e20a72326308e742a235afa3 8f803e9ad4370e2e2d291f210e73c6cbd11f3f2ee2a499edb12300d9d2b08e22 8cec4b8de3d4178ae576ee7015ad62b09005e938e5877a8a9ef4bf119d132a54 43262daf52dc5c7b8858f4a19699d587732437c44ac724a22a9f33f6b1f0d825 cf76f42665bcbe72506d63800f16bdb2ae4276d22c79f44ad028949bd8d6ed0ebd28770215f4b40804548782254aaf7a68f3018e985cb556c9a57bd0efb5150135679c1e73075251351f78d3051a97b30b1c6e481044c64e4d440b1b9dd1aa0e43de823d220e95cb43f05ddcf3b9479eb49570333e7a8e5cbb1792fb86d7520fa492985f321378e0ae81a11dcbb26f774d2676196efc632df88bc2754a8e600595f452311ec01e91116205ebb774be78600242d8364e218a50b81f1a81f64e0f126e754204367e1d22f4a569d26e5ca38d7e03fc519a80b7837afd911eb7430cda799d656911f330f835df986e818b2b3774a4f641088cd9b5e3585ce4dac70ca9dc795348915050ec42347faedaba92e6f351c34e4cc241a12c4f060275380cbf8037d334dbc36c9bdadbf4a25b6fe793f0ee428094539d065d91be707e1b0452c1b3e29d6da857a75946fb54f1512a22ed7df9dea45997dfd5ac7ec00a430637589db408b8868a970e72cd8691a8726de3e6c5f8cf5560e6ab28c9e1f56502fb64bf1755c63f305320daee6433579f3428d2e29fbbe2af7a2902a074fc50023aa6201df688f08f77e147d216a87701a6ae9e914ac0784977d45d18a780c203a9e3984f2d3915a8f62c866ea3653ff3cf63d5194476c08a5ff4374929101f087d4a0645eb83d03c9d8b8f561001b3eff2e59f662c352a4a63777dd255fe9c022c3e7a60370db156b687a6b6c07e90cb537e29c72668dac8ce9cb9b61b9ff50117d1df57ca644472974bdb8fe8ee63547db29bf07506647a092546b469c469003fe5197541eb191896e982c7e39d3c592f2378498754bee1b57e1dad5450280dc2a78710fe05e0b5db40e562dc2d73d4c7c5aa1e8072f431cc605f0741bfc5079e15d9f1d70d638c271f328d47737dd18d299eac671e68d4181268aaa4553501e990df0bebebb01f781ee9415e727cf0e523ff5b30256c96f49d736a53ca5e0152863cf3dbe5a5d4b0e3ce53a61524094ee42ad143f3568842a049eb88a43900569094de98d9ed2020f5e0fbf9d978c4799c22428295df63890d84bd107422050b2f1d82d676d8591917810af6435aa2db6624bb804a19409a57f4e65bb8f7039e09f3e268cba6e60b2e8f361f29565fca0fe17d356ee1b8b51541571fa50c077d0c4bdb560a29b2a0b74232c4786f2c0f049a1a66b515a9e8e3fcf2eaa0890dabca42df8721e8fd8452f6def4570ea3e054b5b54d783f8910522261609f9101567299089b56bbe3f2800621e42e5333a5daa73943eadf61d59daf42a789d40e02ec77e5385187e06ff138bf5a297dee983fcd8935c1845263a692cfa7db7a039976f1760efd9dbd16e1817c250df52601f4ebd9590e6a2dad97d231755d160a411bcf5932a01865a65704f58807dbd01f5c4c5e3f9f24c931072ebd409b9a05761f27d83a35bec1f00d32aafe64a88db59877aa1456783b7ac0ea07ef285a04482e0eed6037e8a5871811378eb77801052fb7ca43421ce7b040c19e79c8220ce2fd81f12ca6ecc276f99cc3f6947f9aaddc74beb642b435b5fd94938a1e16088b9be9e873a3e3e9447331810ef12f257a4f3fa36fd2ffd80527d7ca1f46880845465abc55405a4f96b6d67d715cc1421d95765b7ee8788f23ec596c0723c90dffaf1e4f03b66c3e5c0f40d000d8580eb277fa4d3ad61363efd8e83bfb1e2a0a925e695a5ce93d1d417ed0a1e26dc614cba472bb94ef7fa2750bc2b83b1e7d0f62ec22bb63823ba44c89030306a9d3de39926ff0a7b9e972decf731a5e14e5050353014510777ea413350375cf07522ea60f6ed046c6565366a8a8e8dc5f78025e25bcde9578845fdf823964ca48e5e3b3f91db783ab3f7205215f7543cc1f00ad639117dd21529b48ec06e03b9d31a4a2a6ff058ac6626a556d92209c43d106fee1f7da10569a0daacddaad2eae1566b9dc46ac579295cf84140ebf7a4fcc0c25f09a39b38fe5c3426db2b02a13a82c76a6604650a764a734e775b92605260bf7631b0e25eeca57b51c507e616c00711307bae1f01c9cd427640504387dec02d741691bf1671de0129c5b41c4ba315c893dd3e3a6bad1f5d3b243c9a79f73061ffb5761c2ca6aca05344ad5be4fb2001a9d72fbbdcc4da784c200f72b644a0611098de1f52978366e36abbb05aa75289d590bc072955851a7f9b3ec55d2ac00953c829858d8e8fc5d78479c3b12ac8688a3d98f9595ece56f5621e5bf77640130111ed8a1236d1eddbbeb54763c2520e9741955f70330722155d78cd796720bba1aca62c43ad24b8466bdc5d573b635e920ed0f7365db78693617a14560fc0eb3930261d0b14c5685551b15e20aad7c5b6862e0c4df52fbc0c5e4f71b17320da314f4cef5598ad92d9a0611ca799d823f39a09c59e861d7e4afc7ab01b04803021c033b0123168fcfe2d2001fb7b7390519da7c4e5b86ca88abe97abece1509e11fce9a90d4e358a5a7e39a29364fecd43c188cb973aff79309d20684b1240b9f8ca8e0bbc449074d37aa1e8b5f88a07f765f782420a0cca676c28f85bca707b120b27b557f1b0dd7ed446e07635ada9f87153bd18af35aed5de26db38dcc0a2cc6ab13a517aca8663fa3f227f19cdfd09c633cc71ca474020e1292809f9f0e4ca3816e646eb69d85cf2d1151d103849966e4f52891d1c29648af6e3d754e067fa64e411f95ba29d7bdd861edd644e1d449d97cfe414a7591cd2de6be6f040c4711710ef25d209e8840b7153ea30d8b11a4b76db1c8aada0d2c28990c4cf50b4b5f670a90222fdeea99c59e0fce1e8d48c9937fa61c39d008d877566b449b09140e2ce5d803af48412fb1da78f12a440bcc3c38307457c30db4dd7654f92a086a293ed2256e7c0116b671d2ff2e985684349129dd3127d560e9f045673c8a0dbe67780ea9fae9315ce5b6ab8654c738616043558e5afbbc089020b313509900338bddf6072c3dfd24e49419d35f9acfc0342805dcc49d5661c4724ba8f79f0efe8518f170950d9af7a6802be8219ea7f52810ef69ffd2a7d4261f9c3c43b60076dcc9b4681423c870bfcf4a486297d61a04c06c7eacb0cb7ac75fc5ab58370d764614472c38025378f536b2557970117b56f242b27a39a9824361cdc931f901f041283ddd31602fdbc4e470f251f6583d9ae9aa7c09ac7a3ca8572015d3d70c3913c5aceab72bd0ffdeaedcc7de9d4f82db63d8de0e5b655fdb40e4b7ec240b50c2e0dbcd20fd7a2a7fe28fde3a74cd8da17377d98388a52c7ba4950f432909c67bb1eb36558d09217e3350b7d108dba773381db57b12d5d2eda79e57844f05e989826df4646f8ba8f74b463c32143dd2ea99ca58ecc18968828c86a2764509e786a035da0613dd6fbc5265ea00e788d66ddd85df114499afc10531aa9ad609759950bed91be261e97b5c9f496fc309d8a5aec997c45fcc24297f2f39f235053cad7b385ff9169fdc56e123226578f26476c21e1128ea5ad7b4d707d4613b0c3f0d3022e4f11d0fe5cffd5e2c1a92ef5850ee8a4d0f251200f99c978d6b9a0f5e3eb518d9430c4e9c358feccc6bdcfae47426f695b3a93d332ba1441908bc0d69f9d715f3959a5a8b48bf2ad9496b6138f1fc308da3d2e994ba79acb8dbfe03b55fd1b6e2d770e6083ea1de89ab12e1379ec7ec5a6d518be0c322dd230c9c01ed3cd9aa6a9bd9b59ce457431dac1ba271f2577f34380aece6ef0b4090727e03bffdd9e41dc52ff4d626c8804c4b4affebe6f0bb3ed047aaff1ef807509a2f039e7bb7946802f9f1b844cd33bc1d5dbb6f30da9ee8adfc0241e5ca0404fd000096b2d4fe6fc62c7efdb9743a13f1e5e793ea1d35e2357dcafee92495098a1f00920e76e50f2c3fe3b4794ce89900033065c4488e00ea6faa63d67dca5bad010f3bf2179ee34f39330ad2e5212c4f6183f92313b294666843dde1b239daa43305a738ddbda883d1f07f869065e836ab973f8702ac178230a1f702013c124084093793bea8d035cf919845fd78ad00b5da2b7f9702ee3d8b573b239416b6b4580cee4afd1c90d245714dc876874bf5594c52493c75a7d55ac81140fb20fb8b48039661b9632616badcd8b601b422b25644eef023b432c958939f320450887b4b0f9f2e57e39969f1eddffcc13bb6d837cdd9d8a7cbad8029ec406047bae693d70428ad0b3aea50644492e2e06ae5b953f6f3ad4395bf3211240c2b5e06fa5cfe0dd0d46b627261c8bead3c023bf6135640b8ddc731848cefd5d5d1110b7c06f90c887bf59cb68d8a17e8811a3c952c69ca4115b347e3363b81acd09b92a9754601349d24433ca73149aeb6108c78acbca110a2c36159c27609c22affe17164790e2adcfe61eaa1ea92155aa99c5e197b2f716a7f65c0ceb625d0caf540d8eddd00ae50269483fdf423c0b125f23f822bcfe57f62674394850f16ddcaa92f11a00ebf44a0f940cfe8ab76c5e7887b852ca58d53cfb2a952ebfdc12d4eece279e906c1b7f55eeb1b0c4f41eccba5fa9cbf90000e4477871bb28e3e724c3f4f19b306d93211f53cc495c90550419ec1d85341e97054d0d2d41f311d8a9594e184eb0369270545233eeea8844c7eb24501a96141d353aeca1734b33804b89a9ae04600e15c51e74285f8ce0927fd88b8b2f3b894cabc71d08d0b6bccd05bea58dce30810ea05a1eda3a671bfde6388661a16f7a83605120f91b98a2f904d3dae01d70a6eb0531744c1b24f16026c9aaf466c7e4abff5aa375777ca9f92f4d66660c109eef8da51927ad658b663cebb2cce7a323175d12396d9199decbf9c7b0ff31903eecdf10c05f15e12c577fcda17c788193dcc54813d1172670d98661226190f008512488293923b5ad30d1dfdfadc26c4a8ded0d11683ae27a89684ae04f7140398e0a624e1094462ed37e22bd6fb23af0cb638456a1e5e67c2048a55160b330d5e2c93a572e92c67044385c0367df69ecb614ef8e61740bf129b75e79a6f09005728b107eae6cb16ebb7baaf530b9fb1ad8567b54b654152e212ae1dab8c7e0d38788191795a45d3782b8d769da256f4156d3e11922d113a014378d730a6c506532dfa3d94b1b4ccd512de36ce70a7463d4d10a3bf7c3601974d5f362ce2290f43a635f44f0449f585ae7b15f35e37e0d334afc4d8ed7777e629b1b3107cc30e257ffe7e492a6452463b579162aa943c4839caf3983b4d5ec06b1c55280bb506f1c506ef8b39733cf5cf7550cf93e7eb7fb1ea10d15b915bb2be7b3fef57fe03ba98ecb11ccad44c0c91d755aa8f79c95b95f2a4dde245ece05e05d06d8c1f05601ebd5911813878536cab0ee98db646949e8746a7306c59ff0a62f50ecf160fcd8728623f08a0f9ffeb877a13b383366946f62dc89f86929fe9828594a19d0b8a22839bbd346919ea0c902c6a99da64f121247334d0f08f090d5e52a9c38200ade4a28cfeaf40f97433d141e361d7132f5ac3f8c29dcdc0763ea0ba4e0c2809a8742105dd796f660287b67e5e48f799b2d1016601e28b5ff309fb04af91ba0a82dbf3dbf49d59341263f4a403f4463445ca8b5f67793f77fdbe02631a7253048ac3ee692001003de96418423aeb330c99a3932db7aecb759e92bec4efe8240e42e1db0191e6dc2f5340b621854a2f5ceff8886d08bff751e68a8d8ab0a5d90cc92c13cb5432343c2b3a6ec79093c907bea92c8d856ab591ed380889ee26040afc64c6796b50a4cc1fe20b729e5ecc9971db7a3bc44f42da13371e1d9bab480a7edb513d44c38ffa67436115830e6368673eeaba8cf106b761d7a631ae395e048d92c2246f94e306603742e5b5b37bbcf1e00b8fca454f0fc25dfb7e7845ec00a2b905a97dd5a704edd2155f715e0bad3df846cf9d0eab74dedfd3900c060602d115956c4a0d9243695442f7016d4f7d97aa425d1087881b0afc83b2337ea8081b8d5c4e5f896444e6dbcd6fb2afa7ff67de0c87ac3d599d79b93456cd9ede05b1803dfee2b983e2150c0a870318954a1d2809e3d961e9d598733ee33a7df505068fd467f1dfec6f723b6f83bdf3a55283673d1d2211a0837b727b0ef106620cd96493ad8ebf0db4c510066e9a4f29a5ab4f0419e05c93782a05ba89278fa10aa750f62d46e30c6271d17d7f8af745f4e17ef61f2d0635ba243fb0c5416f540ee879e72a90a12ae1627730b62c530723b90d55c33d93290f13a237c32785740834fcca5d048d98df7406f71b63c79c032bab9ef33163a3ce29dab5013d37df09f17a09bdbd1322c80ed4ec8f0239f7d70c2239178ec295e4317a41e59a086a0cb159a8da9eb94c559cd00760e70a79f24121a828351990b1e9a33b0ed3f91e0abfa6606d8e2d54ff9cc1a6514e3aa693578596a64f5f5da6d8919fc17502ad036eb194fce643cb591479c608a5ce951435cd66c24c31f0b261412d9365d7fb00383a9f1d5129948e48a25c4251e843e640dac08eb901cb62f020fc73d1142306d2245b438edcf4e50e484d23a0367e5d26e128e4d8049052451c483315d09102ad0041024c2b4e65db05474c968db9edbe486e7ef6de42ecb3c61a6bb0fbea036938525fffd916dc277d1e97f5e16091af9e1fe46c812e2f779d4685bc54490ce4a307b5a23e8ed024cdf5b5f0294a6ab0acaf05cc055b950086efff32aacf00e70f682b666859761b09bcde5990e8a6f984218f14d6cc6f84621ff9118ddd0218b8feae9aaff678871a0521261b8ea3fe8194c6edcb60db79df4860b4dba4010954d7eaad925ff06104dcd5ea96270f051749722ca9bce3e217ca4052a2e50f254a7e3de6d9cbba6dcaa53af8095fdca0a4e4c65db10f41cfd06d4413374a0cfe808279f637f6169384178add46052dc1293cc52111174774a9fbfbad746c0560573444df58d7fd73ce0fbfd544021ce6e2f95fa9806ef5083cd514ef083f08c5288936ff9afcc693e0c4325be64f1a17f69502e96139a9036d38c80356960db874f24fc393a9ddabc604214c4e92494544a5e6c8be19c3ae7057c2402f9d0925a4dac10f464ea43ebeda63009915f4bdb482f578825cd0745f19e9192c1e01758f8025e9af7af3707944cbe98f98c7aad2d019035ce4c9c25affcb5736870efa14a1de8f6b424e48747aafc28d548b748c0e42f23fa3a574fd38c7a8aa0f0748a3909d90d2b9c8085cb528a32fee98636d1355f8f85ae0263974bf64d9e80c700e7a9ec61c8cc9d84e53f1b52f812683f5ce57257fec797d97b39790db9c0add7b2b3b4e4cc1272e0344d3a1986fc80322915e312b7e59074926630217a200b50573fe545b15a971ee89f9bb900be72a3e6a62c193caace3618b4f558f28007272d23b7fb04f87dbafdffc9efcf569b154bf71645ae1db1c013eaed89f860d85347d880fb40e4f0fdb36bec72202306e4692d49ecfcc39ac55a1d700f48e02d9e3180d5d1cf4a7f76f95b3796855a216b8c1facf6e50a0fe29ecb08c6e66071a1b8ffc35a071e9730fa60839bff0e60ef83ea628b224e4856ba0f81449df0440ee0b50e4c2ea0319e1292a7bb31181cfc1ebd105102dc1e45ad6d73a10c80c93f2ec849f34e7e37d98de3b5fa47b7215e2ef962a2c0a16cc89f4a71794c70a8eab7bc38df0091f898f013b337d7e846f9db3f09efd4ea3c5aef2f6ec27b50b31dfab2a10f9a68e0b8065ffbf858fc5033fabc14899975ec2cb884e679a580ac513b7ed72ade1301007ebaebfafd7c74e40b4f520cf99bce1a6c4a2d1b0340b8bd985bf16cc2f286d26963a504292ed63d37e7a25a4eb2dfd626b0c5b01110448559850b864a1011f03bbda490debf2eb2abdf62bfb0985d481f0833b044a03e92738d6c1b34b159e9f915cfef0c01a37b5bca8e60e264fa2e8aeb1fd75b8037daf244fc596957b0685d9f39b3221964ea1325322673f85f6c1a9ab25362105111af58fd1f9f8bc41a724e8c7e97e7711e0862d544949ca7b0257410b51d5076975d7f91cd46b717e7657b110ef1d65de801586f7fcee5350ac110aca02cd03d08f13b4e8722970f80309bdb98bcc3dd197e6c7c8076da2d19e26e58e28dc088647e16054a95244d825790ce7dd89bfc33a90761e391de16f7d11a1dc1f100b2ff739683abdf137b235a0225b5c57cd72a75a27f73bdb50b1814f5d431ad40cbc8948a978d4a84ac1d7465ccb15fe2767bbf8ec97ef6f162100651f5105720d786119b95a2d01acea94267c215e5bf34309a8fa1a888856910f3e74f0c2fe0ea4a4bf3b1e4b3bcd952a269ab43305c5eb56d8e870f64111da1b38540480eb0df977943a7a3e099482731df4cf7b5b6e52116ed5a1e160d0144445e06c76e8088ae56389ef436bc8b8f1cf70eff8dd63522e0216d69978bccad86f852d6f78093c1d9317ce3729b5a3d0a767654db0cdd2b6aa95831c8c8bfb15460dc49a6e084a1dfcc07e12f3a5920182f2f832f9185b273ece7de1b82e5c54c84e1cb4ec0e8beef028a1e4e6c453c8e80fb9148411bc3de2a56ef28c7b423b993ccf12150500e611a5877db91a14a1b4f236ef95868db5d329b0c0deeaa5693d966688f40a7ccd3efd05b57008ea81486a9f3d2dd640d8b0fbbfbbe04c93d00b02e765fb090cc18813f990d8454232bcf69c18cf3b0f06d756f3b7e19138750dd2b435f80a9dbda2b0b721975b8a512f0eaba03766dcc4e2acdcbaf9133fa9a3a363b6d006e5a6a305823945ab86d275e354ebac076a8f288681afcc7388d208b3becf8205797412cf140eeb2e484661af317c0c918fc204fc0b7a723d6f77ce06ce31610a1380d8b858a8848f379c8de86f97c684f627fc0268b22905d087b2cd4e2d58048c2896a90001ad364645a30314b87b0012c189562bb8cb840b69450caf71e30629a8ce15102cd263385ef7c606209088ee8c1b710c27de2d5c2335dcbf697800b32fc7f21aea02bf4aff6d54257083443b9153e9c2c32cf446fc412b7f773106ed079f19049b28232b9e3be32c70376eb68e3141c312b298143971ed3afb9c021f5e0d91d6fd421635a2b6d9095cef3be78cb80f925829ea1d36f51dfdee8f0cc55967c3ab665c383649e3caa33aaf9435b8a28f2b4be5721974b86de9c7260e964247031262844516d6cd18ca13e56e3344e6748c6e77ab6d868181d20fdc00a12c9fbf6c1af3b9af34c22c2af1b88d0abc8fb098bdc27eed91ef53c4d73a093f8b4acffc09399c91f8494e9fe84062e7cdf0eed7c7c36de2bf8f880cc7c90e3d28954931ec2353d01940308bf5e7d5ec297fa054fc3c9514672c6b88737b0f47a3d69d7cc77135da5d2be01e8e99c4eb02760a0254bc14b27be21d43bfbe012b710f850e8b90a00b7494ee2cbf4ce58d7143792131ffc6557c13fe5abd7808ce9169be6c80e24cf6581d6df0064f9c08f12b98355a32d08c6e1d91fc6cbc0d9243681731c65ad1cd0a6e01dc2cd0a5a79ac916ab0b612900f611efdc77260d39dbde6b9dbbb0e4805d04a80f1077532d0804dff14f988a6dca74ac7ff3030294faceb70927b11cd13741d982dee56f8afa6d8fe495ad1c416047e03c7c9c0e8d45d32db09e34192354bcca5d7a7d9a44524f58f6ac90c8b56275f869e4ca0b026d7d1950f15125b186db90e4e653890ae3c452329bf01becbeda4179b0d40ab833f75ec0a730396a1c28708f70b322f587b233a7a42edf82953682b902e70f7d71cdede129b112ba4274c097ee6d809e493a74995350492ef4576e0275420a1d0775dd188925bcd7b69ea0a37fb058fb714773cdafd28dc55b1b2d74cfa30eb5b7309ce356bb75c04562a87b7dc29455a28a1475bdc35cc9b3e1a1ae87ea0360d4fb29be19d4d1fee5a7b87b9ff981575f819fe84678af5f067f65abb308019a80310b7bfd0dfa6aec4e5e6f969957abdd37c93c90c1d61bbed53b4e670e0d3c16c2a5364ffa378e3a9555f6fe3da655dee48c7f51274b186670589dd4680e45a7f61ac616848073bcc339d4ad64d9fa639d4ffe0c4b5f4ff949463e79370cc092d9d6f23c4e37e444e54e7c795ec346a018d672f26ae77b6d0cc3b043d307634f6cce81ce57360e1535ce34ccb446f4e9c699aee20d1da44f262e3426800a29b3bcb608e49e5e5c3d9ff4edf78cb426b9bbf8c637819c5771c83ee1e8cc0895717816d93356d607b9709f939d23f0e637251fe54cb73fa9a55ddbc720140cc23b79acd773a1dea59c72dfa20ac2c1c9fed69fa92436fb5956504c7d43a906b34e295b520dd5fec44459061dee2ea3e2520ce8f8865285a9c2e01b5614f209e6f049c6ceed3f0ae413cecdb9114cbb3fc301bde2d30934daf4faf74a651e0b7522058f14ce4a3e3a011f8f627b9ff2193b7b8275a90b85520c97cb23fad802f8d21c4a29b55e9a8605b010158daab01a18f37c6439932fc46bf51527adf2009c548b2e019e67c28373ebdc5de84a06da42fda4ad738a1a0b57145e0c08240e5e0585b4946d4c8a7f050d570205b5ac7edc56047f75a41b3905861704feab02a6bd2b90f629a1646d4e8389d209c37a6d94dfa4e9e89beeefb9caec8ce6010f8a814790149612c4716f79d217ec91e443467184106c813d0b4bd6dbe588520aa27fe0282c973b6fb5d119a6366dd6bab5c081a1efd564fe35aba239e193ba0583813bc3c45253edb72c0405b785ee018345e4226129771742fa428233c58c0e93e579a8871dfba71f9b7512c5bc183400d54748e4c138f7e2ec3bb350d3f707eaf3b7c10ebcd8d3e2c1f98a64b0cee78bd97763f022429a6f0274dbaefa570c6f89512c637e5d194f40267d832f730ca76841730da84011a68e5656384bca0e6b5fbf708ffa25919817a9677ef0d9cd1ab3fd182ba14e2ef0ade7548526430da13556ab424aa1f9563d2f4eee2580168afb8d7c9a1acc796f45c2c8d3a0150e746c2e025f949fd65539ac420c0c73188cdddd6334ba8bb1a472f686c85bf401d2fb01896d78fdf0df8b632ab38f3db8310faefb50f88f922e50c75bdc95ee00f04b2c0604f22a4d4e7f132eea76ff978ac50a25fabf0cdf05aeb2ef5639e903dd77e95cbcde803c147a46b3597b3ab1019829e09b46727c17ba1aadb6fdbd028013f5d406f8c8aabacd8f00c601f68e8bcb9f5d5b692c8b9185dd6054a97f0af1dbb88bf2f58cea8243a1f291ddd6aab8bdeb422e3b035b96d8ff752bd5130df21bb358a161dfbe373dc759e5fca0724a236b2a734212a294a86382db23e1040eff18f4a6c60e52a13eec9ac029c86945db2fb5b22c2c6e132ae1e41ff7000069ebdcfe798ed85dcd36ff855028bc36dcda173b79dc2f0676d8a14f48a47d056f53b5691d4c3ddfacc7d42afcfe45886b5986390fd4564057e880be65ba990e32b2c2b80a6729722ac8acbbb402af602c1bec1876f26fa4f31a7937dc422a095f03041847588c837c67678df28a3443ffb112af449a0b098f64e0479f9c04007173ecb9f1919826d148ce917eaf62561f089524b784fb8723b671253646a80ff84eb32d2149d58740eb411392f1067de54bd9da80ded0b9b20d14df52a0c00d9a86737fa675d62c1f94b00df01d11716474457642c94588f64807067024dc047f8221d62f5308aa1646c3c359992fcb980150f7ee7faf0e8ac4bdddd7ce670b1e4a6a42cd3e3ed37a6988d641924775e35ec1eebcccae1416564ea2bcaa850bac2edf7cafc529d7a8b4b54c65f946443ae452d181960c07f8da2c98aa1947085ca04038b867a9b0b53ad493f12b95dd6982f238a8f908f9ffb3d03d30ac70021ab9a120ac4550055f806a3f210eba4d5ec3457a90fd5e54fd1ec84d1aaca6036a13c15dcce9ab28965619bfd42863d5da32545e42e5ad83f0568ca9c1fc3d006113401c192bff6912306e810ca5b90ff8c2f2f8465f5856e1d72a9ef50ede05080838e646f76f75a1dd1dc0b3b2fcdc972e6a5d4066b528ad80c6d24a971f0191c5162a7e452cb8d18736544ed62c58d738aab476958e884f9fbf9f42246e01232bbd41a5a641585422d8cf143c8bc5810dd5f6e9a608a9b71202b7e40d0508881e974b932ac96668b5f2246ec87a09523630e1acc1ed72b3350f38b40f1d06be9ecea3939e72ef9b5e9ccb185f36e12fa45177e071e832be6aab95de61b20360cfadcdacb3ebf9211e3769c64e16b485fedc0bba6f4cca446553e28d75ff0d7c223aa06cd825018cd6b59b75b29228e697cb5c51a0f97438e89cc47c2f5003b7d4dc0f7256aa74429efcc7edeeacbaf2c54a43b6fa6e1bf31768c0a0acba05eb35fbad839031daf07d7400e89d3f9e8c28093d18b0773e4ab3501d885f900d2111d14f809b1820813c457365c39282909311c8b6037b514c30471d7bb20604465e9a1b082431d4c037681391711ce1456c0b04aacac2f78cb075f09b964c0e7d76fa73bd6302b65ef4a9b89691e93c4441324f913fd61ede57f029097c150242ef63816fc86a3d05d4a91018d11221173ebf670206fca70623a5e93552b600466eca99990fde99de50468423147181fa9c129a9884b280921c5c1c4db7e70c0e4d3c6d40a243abc3003dd667ac50e529c9fbd9a15d6e215d286105c70f190a2252114ebf13449cb4cb2b8137846a56fa4fd78fabd650c9a2e28012f157b50be602e34c1509c803f9e4e3bcb63b08fe8390b7053f39b8eab694867196f648011f683ef3f555c9a5b1ab77cfdbddb4a352d26e3499cd732e950054fb21023a053610d75f7c31714f00d20930fcb0767d9ca9095424cb6eab7ba7758f2d56d00b4c134795eac9d0731970aed45db4e1a12f4199621b2bf7a2c19a9538191f410328066ecf078e7a3b1c8f38a2bf09c77af78732f63f5cffe2f090c7e4440de6038cf8fb2a5adca33332dfc430fd678a002d372d5d6ce6322d466a31dac9d6210196c263ee59e9eca08a86bcdf1e21cedc4918c241a4d367885b6c69680248f90fb82e7c5e3b38e1ca3ab50ce522e464fcfb8bd5a81c11345af9deea4cadb92a0a1628607de36f548f6e464d068c11f4254147e6f9fdf74741cd54037125422a0639acdfd1646240af446479ce8cfc945567c20b3c6529fced0252d2d779eb3c013ebb0d22bec0246c9b18bc58c0a0233935c0dc4e9040480558bf3a66ad9082056e9bc35110905751d1b3823d2530d045686ba02196473fe4261fd5e15218a709ce4d0c1da14a5ffa61775729ea9c628f01a97a13adf573e95e00ee068208ce055cde6094cb7091c5c88c47a7758c284c0dc59eb4c03344d68c453a05f0b156014a237c62233dcdb476cb49f3f687c133ab63dc4bd5a514757df71dca15b1070ba11f786b2d0be14e3bab087d169ad2be7fc7e367fffe16c332879bd4d61501053cb90ba0c35162616778c8a909de7f7f588170d964813131f7a431153d7d0400a273c86a062146f9c0e5da2b93f6bcd23d1abe0859c4eedfa1fc72dd61f6390941736af02f394cd03766c36ba7aebfe153dc0149bdd73a842d0c9f1d65d479021606011af620167636b0b44472e13fb71dd760d607f92ba0263fcbe2bf22a00e0a542baf062de7fd1e89c46ec0e636e299fcadc625981100a40af59e7f5c760e18504f1dd0be2c3dcbe31ac58f5f5378427f9c35a5348f0dc3df6d5f59c00e016b0185d868240d799160ccef7edd0cba19cef7ae6c0b2225531a14248a2ae000cd6313d1fe7acf2724c751112f8c3762710a77d688311964a0f2858941acd50718f92eee7b25bda0ecd9861aba93bc352224cc0be686acf7fed557f62a00e00604f7fcabe334185dc4ad1cacf47b793aede9e17e480262c277d891d6b75f860c4b66cad3ae837566431de125623141b312612bd13d7a7833a976956b05850c03221ccae9659450daa7dd98fd482355924f8fe47e4fb7b28b3538a1a30154080e6261f87bb4707632784c63bae3d1b14c5ef174ba2a452b165ee9284750ee970aefa10a65c0cc51701e2d652a46c6c44180ab876449bac2743b5931b1d4c81d0f793e7c7ba411e4f2ea098cc10b25dfedab0b34ad91ee4572b01886496fec130664bd81e120b691d95eee3d975727d483f0d877dadf60138318edd07ec6d0da0cf8dfa8cf9f1d992c4421b028ad4ece1b69f8eaea2aeda51407fe00958618210fc622fdd7a9955ccb6a435f515a0ba9c56a0bf8b3546029355e21af4449d8d10807a29ec454dfdbe9f3c7f621b878d661288f848921e26e05384ab0be0d56c70bfe2db34d830159524f4056491748cfa0e685e470494152c77a8f069bc460cc0976de034826a51c64c795ac41a5630045a6447198916f39795e334676d352cb0675ec77935889ab0f797031dfc62e638285f3c562f836b02e84b459359e3a0f0dc5cba4f2bd567a0612c1143a32c7d9d638fca84392ff17d93fa89c16d91bd104777185554df36068877f3915cd2593da693b041a6f55f280e06cffdd0e80670e62335f40d9541a7192a9e7ca4a85d5ea8e9642ccece7981ae2550fbf81997a03a32380fbbad0ea46e0756afd71e22b8c2ff01e117b63e2bb8c89b027a49ee506a48c72ade8b16ad3df1edd1eb34283635a9639cda7dffed6c0d88eadf01c8007a4eb7183d4453af13645ae4da9de020d76427c6d785bdad0fe2164fd921219057ca5c6aa93818bf0a273d1532024d168382e71d589ab621da839cfa29d8724080bea794f146030c92f82a3b36e7ab3d2cff9285f7684197d4b9094ce57ef35036f5336dd5b6b133058bf52504c4eeeca25b28c3cd6d2afe0cbb08692cbe8930cbe82cdd1f3d7028e065f685dc5762b01473de8d7d37e9c838c12191156dae201b99d979e6236d29fbd46a18fa6ec29feed790e3a6c555c3a00b06c7dee71fb058203e846300a238515894cddb46e9015a84bbbef258df7388fd82c1b82350d00ddfd51a05e32aa804bf8099b733b85d87fb66013c70b2ddb60bc5f0a98abbe0daada776f94e17f8d2c3be5b01d8572e5fb32f1197253d63213225ec208ffaf0d2c80186e8ab0233b89f493de47e7eab5739dbe6bcaae810eb81bfd36729d4b029d7c3bcab2b88af5c3f9588b6a89df20b433af8eb835936e9d50d72c5a6e5d043639071ff25884f919cefbc4d1de289571d6e99a912dac4166d737c8383b06086b1cff0a71f1a01b5d3724e6f4911f195d7364d1fab543cc99b4244ff08b2f03b6aa75a8ad1c07413297466545eeac1a807990a2f23cac98edea4c5cf0e2de0e1952d2921e79ceb428028c9d413578a5536f9d221084c393fbc3551b7be0900b236df738ace23a4f3e904e99f630b838342d1c9266798fe7914c8e5113895c0433c46468e2a37a581b1aa5c9af490b2053d5bd6afbf1afc599747663a54a3a0dbc2197430d03a352968d39ac5854dda4221f1db6b8a3d746a2f7dd5cb77f2f01ba7d637a3e64f4c4210325a259937719454b9c31644d2ed4a35fcda9679b8e02cebc04c56330a3db899cb8ccd2e784499c3ca424e3e28df78142ffca4055be027b8d493f4637d32103b999f49080b1d671c920b80fcc7aaf7fb58dde4330da04f52bc9aa3aa7a4bee1ec376fae9fdd161bfddb366647311570747ed642eb3e07d534c1817203f78d0a49bae5e2cad940a67e1c33c21289ea8e5ef5633ea4d504f7d7e4d26932b623df5ab1ffb6e6f0a0870f7f40b3f87c2a83570d3d868d650e9e65201140001e053e0ddb78efc35ef09cdaff6cdf52c65a63ad2d0c2c17440b2893ab17a6da079da562d08395a92542fcf2090aeb3e239836c4703096b6870c93dea0d957247892ecd8c3962d5410428171b7e4c4a76d7abfdca7296c3fde0ce5f12e9ce3cf12a65dc61c642dcd66d03a531863a91015c1abd823af6bbaae07ff222e2c6b6eab646d18ad906d61b947ca42bcce6f5b39468372714508169904b44d82d94458e4e40a4c4757302858632addc982201b1f999d7e278c010b7701264cb7fa457b8a0256793751b36d3483c7d5f104b5114c56557a166faec4f50d977e2a9b422c07f9209904063f20280d245442c0377b6c61f4f5732d8b8cc20dcfc49f140ef38c3ac8b74d28e9846c509fe3e28035f9f157eaa80dc8a7dd7602221fc86d1d1f6a4a8d05abddc5ee31acdfb930d0d49ffda623c5c85599203208eb15c539dd3d1e609572fb75f8cbd504ea0705273cb0987ccff3722255c35a0e5ce5161cf6d14a56e0cd8acde78a8845bc63dc16a6e5e75568a937ff90ce310730421532d2709b9cbac52d39b51652bbaeded6bd1e911bd628d58f737232b405d5b765698d6e7cdf8b441ecdd6f9cf298862e3afe9af58d793108eade3bec70d6a1549693cd95a233f4cdae2e20b55fc5babb505467c512a81f2aeaaf71c810f92fdf1fe17635ea4e3078256eb3a8a5c07666bee6132852fb3c238e75cea050c9b0d1796cdea6f11674431913c76485b1eb4f79b675343f5d9dec1099ba39b0f7b4ca5392f9551cf1ac8d8de9eca4eb3919e8d61e1555f7ea73649cebffa5e034bbee83bb6b713200b98c63ca45806d791dd7759ed365428821bec3e2f05860c9a0cde150d997e93c2dcd9b5ddbebb6cbc1a9224b1a305fd7c19c7bc1530290e6e72ff8262b5f092882b6a42686c9bfa1ce365b85dc279b2ed84063cdc62df05e2aae52f00d8a668ee490b41e844711907b3cfad543e7c2ce5a077eb2d0fc30aadb2dc630f02001c93030e17350e0645e48be0252b491d3fd9ba22f8bcff2d08c3ad116829f5f7719aeebc3762976f90aaad5ed469b68b0c7ec9d175b644c4082144c5c7ffd4bdfd94ba182cc1e34c2f44bd607425913a3113065a13c52ae1084c4d762bd071f4f734849ed3020ac42b28c496d9b4febc32bcbc952666e64503064def63528cee528c1312fc4c734f6c3cfecbbc46637370384148916f972e0175198b7659c7894a76fbce8f896ced2ecb356331653137863e5e932e90f325093558a450e0373dd949f2ce8629ea4cf0a1e19eb55074e0633c520602a38dcf01da7a2c220f8cb3829e607bbc2d3cfee8e7dba022dcec9e8b8de0253de6fc8900a41c0948ab364ae4318681d769344f26a1b3a0ac778b5d0dc4ed8d426356d20919ff59a89ed40988016f06934fb20b7d883e8f6f71cd43d9debcc54bdb2cca00b620b9ec075fac456adbd15cb36f844692535aeef707a3ab2a693ee3ea67c5008a9d61465ac036a3fb2005828c1d907a9a3a8aa6368e5c0a994383ed31079e0386b13a5f1bda117b4f7b276ea449bb057c62914648d7f9a927a48e82ae89c400677db6557872031c900c005d8afc457c11d44cac2b6ca619c01966222f95980d769d532cf0d0083566fb7ada7631943432e76f1fa4299e46909df67aff7f70095c82763b18c891f0bca31789cf83bd85cd64f0ee214fdc76dbac6dd26a0bbd0c1e1f7ecef80c9d0c71e3e6f94ccd4e86a528dce357310f61c0cbedc7435f160b1a7bcf3a73dc5d0d4e0a85a39d25c08d7d3a22ff2973b986c08f0b683666d20f2b35a002346bf69f39b870dffecf08a91c6c74afab4f82e586f1d35b7cb6ee0ceb69374d0fc58edae5cbc973b93d651197e5eaaa938fa1b7ccece305148fd50c215653fdeab80094abf2ad108dbc1b3762d6d75ed11458401a800875a52cb602a69db4eeddf447b93fe0a2808b8ebd764fa2d4b866396e502afb6abba222d307913141bc4173e0e1b05941cbf6328c1bee2d44295d38909cd4895d659abd6f05b2dc38dce30f97eff2e0c8981ab1012ed7a629ee96efa41776f800086901170c5a2d0a564277d3f4609bfe329ddc1719302cd9ddac4152b51e9528f140ebfd0f4aa6a5d19ccbe85082cf95bdf3e7a80c8bf7f349eea8c5aedbb59452de2e2e04ea30ced0825ad5c7299d3dd6200ce865e3088b309a1255816a125dcd5738d00530c21ece3e1fb05353019a0314d624364ffb81f647d65e36796e59a7020b42066bce7708948c015eefd462b34184fbfade700c044a4980c43869f7bcfc5a860d true +check_ring_signature aa9b5abd2265d892bd0c12bb90b0ee78afe0400374c90a5c2813b6c6a1208664 19d8d08b3225be4d79449647d81ee340a935253dca5dd18dc8306107d3999e9b 1 87b69834bb81749804fa9ce99324583fb683690c0d29187010ad4b8649b1efae 2a4a5d85ae0f17242744650108c1e6e6b83a279e01dfe1c5b4784ce89bb24c00856a104a2672595ab5848b3ca0000443adfbf2faa964448d6a132eadb589b405 false +check_ring_signature b52a98146d4709f459c84445a41b070b173d35840dc0a8b74330d97b5e2dc7b8 70dbd330ab9bbd1fe5c42935568a5d539504a87e5853a712e9aefea8e87de6fe 12 edb888d04cbf09d9dfdb5cfba162b1f01996838b9807061278ec4189b9d648d6 8efc3dc223beef21b4c30055d6ee7d0eecfd09be32c16c2f7e48d888d51ceb40 92f4a124981269de6ac0f2960ea60c8599bae3173fbc4357561eff40b44e9b2b 471cb4f605662d01d896ea94ee49555377921ce9e73f94164bf69492449ef05d a481ed75e7f9cb126f869cea8e4b6db934fc4cb5f2b9cf063cc6ca717303a28b 6b7ddd59b70fc854c41c650dbefe44d2164bd6078ec1912a2d6f17f90cdc1300 73f5055a815447d08a65b3048009564bd7aec1e2b76eb93ab8badd1cfc368563 4ea0cade1e6f94015e15a174e00568cf3fdbdde20a155544dffc97b375daf08a 5d512588dfbf29dc2b96d134fc614def708cdf1407536fee6ce3ae2f60478981 7fcee5ecb734302b5d6dc637e1dbfc9b4352113379fec62842c8c86093f1e41a 000bbafa53a832cff2b30b32d925e3e93fec197d1606c3616b1611b7334e03e7 b425c5f8b2c2f85e49c964d943ba94728c6e090aef99ed11defed2cbe64e8916 7f9c778df7b00faf56cd14d92faa55e1464d151a0d2a12c50acd17bb86c916065251808f818aeb50f27ed8feb7bd9be9c32fc5bdc19563b53307ea2e7dca260bf3e46d41ce883999b9958ee51911f91db23764e79ebd9f0809137c8863063c035d8d96a3f5d68f81c1f74a9d7d89b85cd7a2063b5e059f596022556a6b24de01f54c5715679c501308356bf7bae59b826cca1efb01e63b73a246bb0b7ce7280450ec1a64ac274b1846eaf324f6648006e381a00c7009b0cb497912833624560b228ebd08c816485530e7bb7c2f74783519304aab464e5e9f01a9975e8d4c4f014097cd2d09e3788966dcb83c097105e9b98ee524c6df413eb1377e22c1d1960db6c9814b550ab66ae32d59e5723bd1faa7e79f513e3c8716199c49cf5184f90a244941a76a5b3b6f0c8d29c610a1bf6f414499f2e5204ca3d66f1700150c230d3cfeda3755633d3f36a6d5ec79b10deea8629c79d8d77632eb2a0f6f7dd3480a24f5ed2b333ef59e73e60baabd66c591564251328923bed1bfc8a5b76ee4510e4893f121946314f27d1cc6615d590e5c961628a4eed7bc702574c6c480756c042ed03d894e2fd6d4beb8565b6791d6f85d79f167db04916c233a7dffeaaf5007683cf66c99416c300d1f16da0e22feccda13bfbae96c47beaf1b02f5bdb44309bfdd9092f1c1fc023431616cf80c91163b28240b9919f92f381f73934d2843075bb85edbf5a36b0a00a5ba82f5ee575c3fce523fb4a7603a92079a3ffea46f0aaad18e984fc70fe179449a1900e38523245d1beaa58d5188b3f2dc9db54cf90840a5edd326f1f59c57414cc5ca601ef740c4b208197687e6e5451a8b7e1d1905b2654f0608075079e7eff39f6a64bf30a3af0a6039b813e62716d7079d62ea01a586db969a42a1cf535fc8d1ba35c93c973e73b28eb99b9c190156f14a7f13036c0773847e27f48b3f658c612bb387887b1c69e0454687ec1098dd0118286202c5967d4a72e91869eaab5b52fc2bd0625b89c5f2a1e62f0ca8a10434b92d07021275bbfb37a293b8f2d37fe66a21f0de9939fd03f36b38f7df7aa4e13531a80e true +check_ring_signature 069449c7deb493c7bfc5e742b183463ba823702b49434d0922eb3534569f73ee 39e9602ac10db9183675e2c0f2ee94ace17400f03fffe7b97f9c6b750caee4b8 1 65500f1fc3c2a61184a902b43313e9dd0fef542213fa4836eacd1843b15b670f 0d4daeb4d81e07294866dbf052b186773c7a99102fb50496a6178998dec03476079d5e5828fbc2aaf65d9e43282820152367c578cf307687de296063ec754403 false +check_ring_signature 2716f4f65d45ce6a3a505c77972bc5bb54a57928d63a9fe05b7f5f3ac7b817f7 5159620804c6c9c902608af7529e194ade4946bd36cc17e3a32d0e57a95d91de 1 f14a37756a6f1482919cb30b36caf48cb15cf335a0fae4499be621341de62164 67db0f5bb2bb40a2b8b5177046aac3837453bdcbf69089a7268159aef9c81c03ae92158f9516520f6fb0f306252def719305216b19c42d94159a3cf5b0c2a40c false +check_ring_signature d2486baba9d642961eb2dc91cb81de3b68a86b6484d03a5e8317b0f3b9334a0d b96f47a9110dfa2dea1055397eab26a3672a36b2afbe763934ce58683abf7958 2 9b59671ee4b28b22bb70a25075169d8cbde08d612cceed78cfe48b17ae98d4ca a3cc15bb1394e07125b1a4f4c95d28fd51034b46ceab044dc02c335c40528b91 d0b733aa47f8085c121f05586eb5dbab588f49f4267c6e6e7d1e9d871936c00848a51d82950d9f8f64ecc3a810706296bc897886ffd4b7b9f9b1eca75f269c039c52cce36e7653e3660a0a27e7e0ff1058b59cf5c530562d1d2fc664137fa20f6016fd403dea5cf1cae7bbb1474d843f7016d26bd3980c6dd217e4150f2a0900 false +check_ring_signature c9131ef362a6de288183666c6f104796b07c931583b257c7966cf4c459a810a2 daf6d447d8ac2340ec8b558f877131c034325086384481d5471cfaff3c661be5 73 248f57f5c463ebe78f14a0843cb1b15799b5898335a87867a478bac6c44955ff f3d7068231d2c4c1a64e1ea43b3ec03525822366425bc3298ba0b781090e4f70 3cef5639d4b1656fc83f13bdc731cd41bf902dcf9d4daa5f7a626ca9254f8de0 8a6046905423889112874d14c780cc88cf8d8ce439b5edf0a2da2224035b8441 c2533abb0f5783e92d751498cffaf72575bcca61eeeab3103fbaf6b763777788 15c7cf4ca88d810cf1f766cf683184e3afa79697f3e345ad834fe3454e46b00a 7f5533a5df52b22e3cd34d565fb3df3b760f16fc4220ef9e2a22a304f8c7f59f 23726b727fd6fe6533e591786c937444bf11ea45b75c18d0568cdd1bd3327fc4 b6888512e3cb1a1521bd6abb7bf8d5f6b3f00cec421664d8e3b1f6b2c9ec731a 3ca688b8da0bf8525c50ba4911bb3218e6921bfeb704279959ada5c2dd8234a4 2dc109011b6cac482c19e14e2351bc54d0d3373e3590208ee777c3228377916e 37775a90869e20f274c0987eddce2159b720a53b290a5a3fa8f769b8ae58ea7c 6591c7b8aae08fb152a7e5e54dd274052c22cf2c4fc5a550b2c1da045dca1f0d 8251387841df1d67ad35ce23a208d4335ae8cb5eab493395ccc8408643220dac dc10c6804401aab90ae2e3ef722deef2703c2c0a74c571ae25c0a864a189aae5 47eb7e9f26a9d7f5d94521da93ea31ee9a90870f8e9a0e11e6310175c7888630 a471f598ac30c8cf0602495c5fc495e4ed98e1ce3612311a71210a2d15a99533 b4408ff8cb1b2aed8e0c077a78bcf03c53b7739c7da78cc4b6495bdf1cde8e33 6e6f9e9de7dccf0533b89a7046fca5dfa5e696282c2857c040ef1cf7f0e889d9 03564fcebba97e90d256d319911ae27b73c28f14d76a22f20a123060abc0a996 242be4994fda1127c418725dc949d2ed0cd683ca5365f751450455f8d427f21b 6e51687a48ca3912e9e22c065c8e0bc50b69dda9b9dd743a3cea02d78585db48 ad153b31996ec37d18d737f0acc44c77b277d7054baea545f0ef974ef79b2854 538da76380fc0396327360536525f586a07fbc28253fb40a81637f441b4c8a6b f749d46e7f74fba5287f5ecc9dd8b3779a5f825e56356a3452355bf6dda0629e c92d82cedd4a24a30ac036e8a4ff469ccae630ef5b6a9aeaf22ac198ff66e1c0 71232eca7dba229bda25bd917c96ff509efea19f35a0bbf90cde5d8653241dfa 74adfdf33bd6560f9a13e7f92200128cb1ac6fc840e0140322c0d33a948d826c f85edf570861f8d68f8cca4052c5fafa2dfff0daffb83ab4c8331c95b9e2d580 a97c1836fdda575b5e69e2ea184de408b46e3d9b30baebd104280a1ba66a176f ebb1249dbe48945a591ee66fb17c9a15f4709d6863b35f38a12a64fecbccced6 116c9feeac9a1d2f6cb5a857088ea0990e4e4f94f6108da5f6b609e5d5f5dced 5c4a872bce17f1948d5b3648d786c9cefedf502c8ca68a925be00188158fc84f 7950a57c9aac3d5f28b6f8e4b2d8054b8f2e7de3cb863f3111653c1841ce0ee7 eaa7234b87c23dcb6fd0b81c213a7528ea9e5f8bed23bccb76c0a37e94be30c9 1af0b5cad5ecaf12adac586d0aaffc50b67011b6473c50023f4f56f43f746f06 4216d54b34e01957944e1cd47330a3bf02f4ddb0b0d15f84c6c61d36c4b1e1ba 834f942d5e493f122590cd7b7c9c5b9533f2132c35aaa7b7c5597092011033cc fd6ebd16dec025785298bdb3afcec5aca19b38c6c53e9a9f203432baa7206e8c 136114f3f8dad334ff8f99ab8ea1b01741286ecceebb97787a1f5f995919a4cb fa8295b3abed22fc6834f6d705c9755d89545f7c200f9d5b59b28e9f77b9915c e2ff28563b5f24f68c79e0a1a0724aef95ab6ee2b803b07eab95d363f97f7438 671ea61dddacf77c2660e7161b39df263ad4fb706c3024e3921b45e4ac462dbf 741cae8dbc27ee497d4473ceaf155fadc59e9a5938d68a9ecd97e459b3e14136 3834878baaceb239ffdc943c9575371671b5ef0d4436c304447a911ec2bd20f6 043bbaa9bd5dd3b996e18eda68eaa80e336054cbff1707045a2b2a8a15b68fff b97d304c40add9810eae38b994c61baf56f263964e95addcea90fbbe05669150 53b30564f0784b211ce46a215a2e560405fd7cceeaa6f362c0f7e731766df6e5 3d94fd0d011de5a5bfac0072b4225c6342a0d04bafcb0f32282f2b670b597a10 5123959d43d017786a09e29b0968917f6100dea222c0717cb9a5621995862948 3bfb6875adb242f71f19de0d182a2df214b2d034649c74ae302139c7679e5925 041dc9e744f26c58383940447e0a745169b19c6abec6b719c86b543de361e5d5 8bc8f3f463ff32682d9d7361abfd143a8af2dee32cc5c52e76c0133637cafbdb fb0b326385e9857b9068408c0aec778b3bc420deec56c3f8f45f0ccc4966abc9 67a19fba1ed26771f115777e5f985547236c7bc917ff527b19f14218ce1569aa 03b59217381018068d92c8e1cd79ac2883661f57380e261b7b3df2f5209827ce b474ec0f4744f6a0007b128a6fdc61862bc4b42915fed58510ebe61ff3d16f0a d0908928d1ce29fa9acaa0d291cd9fcf146410091cb6e4a2cdb1f5c1d8f973ee 1b6ea684d016b8074da9c78775dadc2d9ae36c1a966b0677fb93b290d6a96f6a 7a4ade0cc66c6cd99098eb008bc0e7713fb3cdc48ae2f2a76d1335bf180a1f17 28a816949ad69d2331ec2658370e9dc2bf83487bc9318add58e78618443c3152 7ebb174808e7c00d9796b701f0d61656e06c1950699cb20ac7e2cf0ec20ea8e5 01be11513262e4378bc07857871269e0f4f3e44588e0ac23bb615fc23d944702 0f85925364b7ceb172940f5aade83049b093ddbc2b6d79543cab6ac098fa051f e8423db1841e765839ba5e55b0e520150fcc1f63a139368a2a8676d0960eef6b 5d4dccbb7c501224e3df88c096673821cc6b9fa82c32166bddf7d15be72dd1dc 80b590d31fc40351b7523c43dd9edbfb191d3380a5d48e11d0809dae6c3980f7 75306b49a818ea5f16878845e3400ad87ac2716395e38fca543f3bfb10b8b2a5 5ed8e414df795aeb223344b724a10ad7644ff2839017df30fb5b7a4ebdca27a9 fedc2053769e2d186f3c2434571baeb6f13059d5781a18314825d8c11356c6c1 e266a574f4fa63366d07971433a410d0993cb85b16b6029691fccba0e41edf0c 828d0073972a81e562b5dd726a2259c79fbaacde951104f0b755b1a068ade521 e69f246d9384aaf095f964fed02f5aef42bc74f367642a0b336cd8b78c3ffe6b 279831e7b387a1c4a5b50484ffcbcc1169c77b8304df14950da01769822bbc0a7032eaae0259d930efcf4999d1d79584568161f1d86c30c42c2fa01877928a022d1b22c2913a779fd0c15ba464ba115a3276852c0f40b10e0c41147501e62f0faa17582e3d899fb25e70ece2b28096a3c84f4af018ac616180f9904805a2f30889cf20010ff3f68996b1dd965eba861f793079a86428a3768b9691dcc5b9240880773e2caa5e1c1fc38bcbf4f80773dd81d58adc83011ca18127a125eddbf508f664a2b8debeae5334fefc1d8014a912fd4b91311c2bc9d3c778efac8d8aab016b6f48fdcf6ed4cdf520c0235815a7a54167aa0ef9d4266c5b9d532aac474d03a7414a975258b382dff6a392f9669639e5737cf994c7ffba013cf380747fbd0f838bea4de11054fdc056a468fe52503783ca3703c07e55615ffc229d10600406b4e20bda14dee6f8d6ec93e327933acb0b49501f834b13005687974e869bee0daaa927266de8b629ba98674a5fbb99e96a08d4013117c76d730e83cce3806d0f931628e8924da718afc2da5303aab85c5ccf0ee5be660fabcf6b366c4df2e00e277c4f65c93f657341a3eec92cd6b3050dd7e6e9bb7d7997c44bbbdb359bf005b74da56b58b716d1ddcbfac2c4339a76e00378af79052ea6dd926bf67b36580ad4cfcac955fde1d5f06986d2a57309fbb3725e2c6255e2e1b95db001b1292d02847eeb627549c91023c98d666ef0e1970c01bf13baef9413ad909b3c7006a50a95789cdb69a843b2b2bf07886e8d1b7d18e8661c33c5eabdedec19e25910e6009d3346b404e7c79c5363ea0d35d54523fafd876dea6f45f85a0283eb4d9680047244df3842cf0529f8807fc1eec8363c13d0ce610bd5f3b4ebeb07f607f9c70704a1a701b71555b8038c1865ed9d9e93630c34afea9047d40f4f2f19064c340553fb508ab5dd9ed2a5cd7bd77ea75d24674b8ec3ba161428fb2df3d0b10dc709ef1b2da20b6ac36b5281f0f12d5e1649a6ad87728ed6a235359a1cc2fa49f70a0f12b27a2918556bb4906c24918db0473129cd03a791d63effc1d3881ee142086c16364b88e3f711a81269abd24f4dbf608a26758070a73b646c30da18ffdd0bd3e82aba3b0d8f3d34aef0a8397d9775b891744b3a03cbb0f753879015c98400fff1f6fdd167c03cc929f6cb8bb7461e52119d66d8797a9f96545bb598647f0fee471a1989166703f7f3d8a43cb765f65e438dee4dfa2ecb4436c9e39c29550a67c1459a83ec36eb2be7eb07a5f4769a3f0f580840c92dc466a495a9303fda026b1541b15bc7e1df41114e7b903c105ef8b1f6cf6f508d142b5c0bc505eb730f18f95f34d7f3bd92d44ecf50a931f85278b9db446245caa888608ae1c2bd650da7c34df777b384326c43cbfd18fcda83f2e3e26765e0f572c1d76a1237ce8204263efbe34b64053674500e43e526ad8fc8ef26bb8f24843501499977e4744300e29fc22a92d243f3b7c41749acecbc2b41fdfa36af66dc0d84303d57ae233f01065ee48e4c2ce1ee8872d29b22b0e9c51d393480cd6de2c71dc4a4a233801208fbb2fd7c7abd6bba4c57e869d9c2e0d6cca8a683f0445f23e6e5f8ca2edac60820acca6a3942fa1b1580f86f2e72cb8f55196bb22a0d94569a78de9bd68d4e034a6cfec107111088e1610b48729e8c3c8dbc7f185a44147a7167e6a7689a5f022a68df6f0e62f1fdabac24ffbd7960743563b55b44b7813f0a005b5aeede670e24890c7bbd2888e3a7828cfac872035f04aafe1afdd4ae540c1bd9e4078a29081011c4e575d6186a3f664d3269b746ad0f528a06bc41cf6f9b12e146051e3206ccaad714ee607af6462320621f890cc066408a5308103340f02f723a68f56403a496af901fd75014fffbd78c76decfe6886fab3e1d6ff6c439229e8c56a3d805365346734e73fed855185994d978b637eb9a0c7724168aef1e2332752776000c4fbf13dc378d0b6a9172fd91000bab2d34ae503efe35b3d71227cecc254a19021ba2a1613b60132c47fa7b94417deb17965fc26a9bcdb01f05fb4b77db2f370643bb737bbd07a83e7c65d831e1d4f88c88f064563cccd19ffe532aa32d00d609210e1c316670db668507b6120a85fd86bde55774d593e23cba038f2d7b602203097229cc5ae01076bd255440639990f83faea657c68db13485a29e0f4893dc0576879c980ed88e4276e049956e27dcd498c44ad094988fbf686fbaae3bb58c0cf42af3028a4a3f5e96a74f47789b3b4439ea364d3b7cea219ce8dc93ca0e8406782dcb3422e29a9527537fe03720984f9b393aee5609f2cc2f2efe5bd948a404b20caaf6e2c2075db1234b797926550528850a5a8d77d32150cf18db9fcd1c08eb4a7322b3d6d458834538d313cb56dc75bbcf675d468cf11eee8edbbebbe30adf2bf9427ea08ce761028f854570d628b926d73620b63687ea43fdcf9ffade071c521a1737c5a29f9048c4db66e658ff0db98ae9bc794155a60768c7a494f2054b9019ec36964085f4b2e3adac10b16be8795250d7eb0abb487bbdd749db7f064809fd080da29ad0c510cfbd40555c7f72384a4de141cd658cad07005a4f750a8ae4ddf814a285f46f0c7d1fdde9802e186f8f4d6506d120459449b430700407495f978b4d47c6ca2f2afea61ebe2e89daa323dd237ca5049ffa4db25bcb1f017424a2740d7b4e876b447ec2524c5f4e1352e69ee8c006e6c2f22e10fa5d3201ce2a9d3559583bf9b298d20c8c222091acdb05d2b25abcea5680af85b3e3d004f04db14e3125398c3fcf9eef6039f7612731c6854b22280805c1a4bfd359470aef734d31ba7bf57dca2151eef40b3a3084bee8ecd5abc4654c23591afe35b00e1bcd5bc0c55e69317134c548378eaf9eaf7592bfd1116db81acbf02353a5f50d14c2dccc4899b67f5f32048c2aa91bdd277b3f1f2245f877683f81ce1f92670beba5a5fca1e51946f32a94dcfaed44b8bd1f267c1b0b9f77655e8c2da63cc50265e4baaeb1a65e6ee4234024a8ae4add64ffc064665e014a8ddb5765ce6ff80a15ffa1c069c2a6c7f664a5f519dcd375c8e71234f68840390d5bdb5e8ff557048753bd767c04ee45e2f7f96d3e2daf4abaa3cfff6bf96bbe61a9981d7744130f193d05979e26d26a32c773e418e0854fe13df5788c834b888144c889c83ccb053c92692b114cc85d3908f6ce86aa78aba5ae5a8f084caa900497d48965a749009d7dca30376a03f3b63f65df6fa5c72fe0e7760d1d35d083acd06d520824be08fa1d17b856e89042b6230a8889dc43c66fccbe25538bd187932801173d3d3a0708390f8f3b649fa4e54f520575832f1c00951426e57c52530b5e58a28e7e580589becf7302b6b59a19c1f74faec2faccf98b8055adddcd34edd05ca17ae2d6035962cf8578e123b226c20c202fa1b2535a43843536be340b3b7e5a3236e03405f0802d29d258a023245aef45f7506a9e007ae0df42c1e77ba2710609d331740bb73449b517e562f142d329d8ecc1fc811271de03b9d1224a62ef6eecae049b050654b6dfde278922849e5df1bece13f1f1ce38613ea876a2cfddae80813e540e7f050fa5f77050ea69fb889afbe2c96f0c465208c71ce634970473421ba9d905eb5b5f6ddeb289519283c21a56dc0e0ee79cc509616cbfb6e33a36f2e386100efe26c6a95ef47a794e5f60a9a61be401f72b5f4c9d193781aa4a495abc3fbd0a77cd30c185b900f5b6d401941e29c79d49cbdf89db9c6201512c2ff396c45a0cf39c1de78b5a793a98295fedefa302f3d732f0a40bc9b38b07b1c01c8622a60a019ec9a2a3f5caeb5ccfa06efe0237a4c025c381f23579d733abe8900050bc056360642f76085cd36adbc96c2cb746e00d231f5290ddfa0233d495c015bbea0aaf6637a764a4a323ab496de6c143aa7d56ee4d63f0b7e2fc43bd2a7f6b5b1707293b82e5b07a649fd29c3b8ba00443b15ee7d798ea272c995e95db4ed460470b0dc4b34959fce244425dc5138cd0abb45c823982e7ce98aeea6f1bff439405005dc1afe7892e7d28e7872d3ad72382ea4e42b7714fd5d3b94ad25ecffff47f0420939428946c1aa61bafd3f91ad2057c6c15421c1e9abfecd782a5805a79be07aa6456e70bc9db7369d633ceab60285cc0bc7ce5d615d6dc591435cb9e4a3809353357c2325df3890346b394679581b0c41a50e1e4e76984f29043eec20ae40349bea78823ecd8a215a8bdb6acb0569deda2fe551c87e0f6e012d5929e7a0109ab03dd288418c1a3ecf195df6a7df7ce263f83aff491e37f0baec7d9060b4f0644a58867b4a65b33b82c602f1d63336b2c86deebd5c13d386914f8b03e30b900a78f61f695c06bc56b6b9a3127f1fc2dd7bc3c8ff6e09c4590eb0f995483f203441539495c3ab78c02de5605570ddfab6d83eb5e5b067ba6a2949c979b5fab0585e66975eaea569524d56f96cdffa337098664bf746db7d9413f93b3c547f504d78fb2d5b225e4ad254108402ff77f51cd3927e8f7a7bb3ba07db4ad2a112b09584b3186c947db8f80da7225262fba30572f8eb98b9dd9f15a9a6e06df68d10e3c0f6846834a8bf78a187f4d129e7594459e7d10a2db326f28938cb9b3117d0b1e820a5543f0ed696962193d21af72b8a736ff993c717e3058ff0b4a6d8e2e0d1e358bfaf077f1499eed5fe012a00d895b85cb9efc731cd339f0c5bbe4ffbd0238f5068be456f265d85b7ed85495af0af483a19832b175e53b7c8071e9a95406a96cd618df084b51bb0622c5290517eabe5dd9454c3c19f651abb7735b19b2020a1dac6a137d54b7325337c299509b2cedd92ab8512c716bdd186cdca657cf095073354716da04ad555514e50805e79378c4e2ff270db9aea692d81070e10a08398061b7dc37853e6b40d7c51a41786965985882550c78beb7b623eeaa7ede0b70711cd911961ae2238c949637bd87f08a1d1e6308b5eb98eb7dd740527afd0214f4b053fd74a6720e1d1a511c528a89f68e06d8638d7fd67b0d1abe202e62030d1022878bda3fb8258ae11607a1694391252103a138479513f20dc378097507264c89728ae3100e8cba2f840314a6d77dddb6a4c8620b5c2006c99ac9d0870ce2745bda067b5722028e4df7a6fb5b0451b0469364fe989d06a64896ce5da80f5d03ebe86eeab1134d73f1c5732d8ba9ebc4d69d1bae0d597dc7920d64eb35034c2997564e68be711592e9536ad48b720322dc89011f6dc440c56144fe52de015caa66d4bf72cd8082331b14cc4af23965f73862c1a742bc77655a793d3b5f0f21d845031322ede7d837c5ba9d6dd507b80f835f824346f1b63c1a1735d3c3012ddb1aedc0d5b067cfe31add0273ff628c2957d07168f0da69a959803985ee0676d667bd3a6bb9ebcdab27286798ef5f097a25419ac092b616c69fe659fd6f05584e124f4e111d16967675cf2b20069cfacfcbad3a8ac0fabe23ba606160ab0bad555024557e135a9213e635739cf9ed6737ddb3c05f4579741febbeca32430d35c77229a32e2d63c5b8fe444167e6c2a0ec003f2b7855a137d708c300cace02147406e881540e5d2619be048337178044e6f338fbd3d9c0da5f46fe4efe4504e302a614f363e18f26caa32371e84ea61ab318e8c62cee1db78bfe5dae41ee03bbcdb13766b75647735604463eced30279bffb3c247b96cbd37a523d7ab40807e1bede3ac150661eb6f95768f1bf069a86e0c2e0f4f922b16159c55fc72071028c0f7e56048c933113a16cbdcf37df8259789c55a42d44b0a082b2ddea6c250057c275c06fdefaac7f3c554e6717ccebd28cf106467039b378439398b9621b0f8bdc971760cc600ca1defcff0b0aa25b4a8376e06a6274d3632dc8f54e8af60824db456ff050df2ebbc6096eed1765dd6be7f89f585abdb8c6cf4216dbe6fa0276e0954e80bfb36a53566742792e44bbeef19ba04f6eedaf2c8790c2aad1ab011fa9c7424e7ecfc46392a6eab2bb697edda0da3775e8ef29354cb0bc2a1e630f003dda1fbea7514f4dbc81aa9150dd8e8ac54674b253de49b88346a1ce48250a22a9271eb1a9b30b3b7159d193b85559adf34f671cea65d9d9ae77d86c84f104b89476a5c5bcdd98bd95c0687f835eae6e33240d2c85756026c6d6b4b4b90909167ce67a6f068c92f2dd8eac641e6318aed06072cad33a8bb582e1f74e949906485acb31cc8ced2238ff81e1c8e656c7fbfb0109546cc4817dcd73005489b70e880327b6eb780ac90df1ddc226b0181ca5269d45c90ac7b0a5228920f0a04d0806e6eb7ca7da6942d3cb3c84cd547b5125c48fdbcc939edae244bdacf4e45505c1bf7f13b6b1485f60595511e7f402c80d2cdda3176bfb114d45d2bc725e160b174368ccacf4aa89be64b11ea58f000e85500f8a45156d16ec3cdf8390095603d0cc2b94f82dead40dfae63f4c1e17694328f0597f10ff1e4342ce54b44a61026008dfdcd1ddea9af813c76475dcdab6fa01c89b16498f137c1f6a1fe7436f0f9f7c62b003a29254364e51df1f92500c1507f85b583842fb95d9db15cbd6c90e false +check_ring_signature 6bcb606c5ac5403b6bfb62023c065356995e5c2ff4a930afcff26e43a515c175 dcef3fcfc58660d15a48289b3b3a23af23c8be1e27a8f636db10dbf445ef0035 54 5b554da35299051e3f4e06f6e4e5667dabb070b66ddbb80894658df9c6330b5b 8ec25ae3f8e59ba0d176eb1b77607dfc62c22ac339eea8f9d3262fbe6a06086a 8c6c98ff0c9b2578304ce5430c2a72e212b2891ca8b8243d08ecd2bb68e28706 d4013c9a1a4e341aae62db092815064c2086be5afaf671bc07b9e4284f487e71 a8dc2dd765d1ae781982c2dd6af8a5a48cdf3dceb1e662bba83b9b61f87344f8 4b97710e96359b2b363fbf306fa16b3573207c4c6e7472b38a28feeda459044f e705a63c5f00fdacb0d6d29dcaf32ccbc8775f2082860c924c18423103de0c53 03e610be14f0a07333ac109be72a43cca1be0fc83374b112690b18b6481e9ed1 9750e4449dbd2e6d669d88d2babece72b00892866c83e6eadfea488db52ab6b1 e5ef260b69729bac0e8b01d6f7221ee108430678379733b4a8a519451961d757 0ff43b63c4cc1fea32e9a1273b73597310d9729cadbd6210890b6757af583be4 a5ad86c310cdb300362d2a6e06100a6528b081865f22a4a06f4555a89336580f 92d0f35ebdb08f6596aaafd23f6a7513e61976a5c82e3ccce0b82c33db3e283a f097bf9b687077c96fa792e3bd02144d83f554183af0de1b6e97274133820e30 c20fa0d97294cb921c3f9f60625345e1fa4e619deac39d019620bb948d732458 26d6741c5e566efa04e4b180ecf85915fef6de37541d280a8593a5670e6fffce d33053b3eabf37413a5491a6d29d1977202e91892c10bffb938add246ae8f975 f76dd0a8f3fbfae2755d84dbbe65041739db6e646c11afb35782b59c309d0b68 cb34614c17e31d330db7965213988dc0587dcbb3d121b8b0c0c0d9663104c3e9 3fcbdf117a9b9386e2748b2eb800fb63d8ef6ec7207a7bd0cbc664c64fd79840 7d868347ee651c4b2f9e24a1ae3a0dfe0af4319c7b16b3ba57d623b07d4aa2aa e23cdfd8c4947f0020a54b8dd582bc06c82fb3371a87595350352a693f15b62a 877ac11893e9b775080191fa9c481209f1c6bbfa0d195ad7e81643991f0346d7 e4863bdef362169796ecc33bfaaa118973e7889ed4e10e968de9ef8c652a4eeb 7d578ee6611f35c801375ba70c10ec19caab1d1f98413bb55cc2f06ccb8bb3bb 20b597231f9f543cda4ac7d4fea973ae4ee51f737a1e9cd611342e7d294e818e 28435051919c5565ae4b091fb3b79db8ad21446145d1cb1359f354250f4a5ff6 66fabfcb1ede8106cde845a62a586785db3f3b8348622473a3cd1f845e09f5b2 c5ee959571e3ec344468b797c909d5bbb52891aa6c0d160bb4259ac86d7510ac c2b8931d7ea040e3b2aa117a6ca444e7ac77c47f84cecd9f85a813c243d0de0f e89e0d220c863feaded63e228d7f7489807201545aaf962f1df33c64acfe4362 0d2fc9e20d57fae1f7131919a0c02c395b0ea3a657f9b5433c9cb7122b4986c1 ead50924b3f0ceab6c61f726d3c6b7a979f4f2dfedc1e6b4eb22ba00f2fe8d6a 3442bc16aab06662fc5c203e602cb6c3d18c6a6b59ca851f249315fbeafb89af 7367b1a2a68092ec93c6e4dc6d8e0af7629696b4e0baa179d728b98846a0e124 01e5282fa6afe491f3da626a1b32d76669f5c74a1dac17e706ba24c0a5ea16ba 3e82a436032599d6abb744e9576beac6ad747c0b83edb94b72f9fe2e7940c437 4422709366f010cd64d2b9a1723ce4ab14454d313cb496702fd8d0bc745a548c fcfaede6a5899290f9c7fccee720045ae8b27e3da1575020e2ce2371ee8e22d8 60eaa8e77344ef121aa656cff9401987f671a007439980558fb0398a41424d1a 0bad50c0e38de29401d530cb27e145ab8e8cbf9aa7490bbc3cb6db7b7f4bbbd9 e6869a31e323499ea27de66ba4472f35b469817efbd6bfc1a00f328a198307d4 c7ff4449e3a3683ff8166e261f424754ded50b4803177aa015336c853a786549 357f02eda08709d032534c9f8e2a62040fb431ab1671b0ebfe46b811f5618fc4 6e90b738ea22d975b8188e260a5e593bf863de15e8373742a43d88448ba2a059 ce73782fea8248c6c0ab35f7a5d94e552cba6f4e0bb3489a6dde2036597409ff e5ff1cdf464cfcf30be846f64e5ae3a5f89ba55136a7b1e16e80aafa53e90fe8 cb8ba1b700abbf1a5df426361bc78f96626248e42f99970ffb59bbb01fab2e32 93a3dd5f8d3fec5b2abf2c792a22e2dc0a68ce7761be8ddb6a7e44d8aa638700 ecf45d5bff98e83843c7c8dfc14df1a1c445aa428492d9213512339a1367a4b4 0228a1f20bd731b1f93ccb6eb05eefc408d902d842aedd1d1caf8630b3769760 fbc4081ca49ab1ca662b2f4b94cb803ad06a8a38db3e7cea45b7879c1bcccc57 8f712c75dd03a43f680a445c98065d5c4239a0e5edbe61055ce588e5775dcf40 ecaaa78f256012ae280949e262d5cf2250f92883018eec1012cc98217423558f 85873899094c4e451d6d4a8e7c578b933bfe2a2048e102002fab76fc55193801732af27235eb7828ea90c6de8f45516185a0703a348b68c32bac69751bc7460b030cde43224e03aacf1d26ea6be7e231ab6249b165a2056e0698f1b316fd450ac8036177896c8ab37640f23ab47fbdec4dc4b91e5648b1e044480c42cf653f0b011ea47d3333bad799076c1b0d3a47acabbb3a9c76a1f9109a4622621104f70981b2ae062d190b74190e39a372ca4939088f2524f075b7e7c3a1cab5565ee704a7c7357efd4581a1ff2238708a0956320667a8148f5e23f6c0f3a2a9f5a424070850ded036cc4e847ef5b17dfba5f5753e8b257cb9fd5bcf92529553b9a0130f74e84a6193323cf82f62baa4badf023a6e43043a06c53ef8aca55ede4ad2ea036ef5b87bddb7624ee013a5f5693c9e6669656a7edb66f90d8e1956eb6da1650336bf722fcd525e952249313d797692c4bf782f10d3f24f7cef95a566ed2ed90181160eefa9fadfaf09cadfec5ede3108f1f991871efc20a31e2189a0e7212f0b5aa42b3445d8a7a5521e8b88ea8e10023334c6605153962e6463474ef083520300d15f0820bb0fdea7c6e0d68a761fd130e4a4bc4e7147dde5a51eed245e410b8b2e9ea91979500539d57efff63069ada4971c6b2fb6f546bdc3a1bf1b1198061a37f100898a5662e1a6569c27d0ef917f7e5cbe4ccbc5eae0f31c065793c00194b135df3b181a18412d1eb580c124620f1980d6f3f5d13a73dad4a8326af402c92face0a55136f2dc22f850c5d9c2d0e507b052f38102616d78749591c66b08ffd05520303a8713be312f9fab4673888e7d33b335a666307a4f5767de8176057203380867a3e66056c06f960fdc7b9a5209f2da59875eaddd62d1af44e9b30e602e060d8970c2ede0a91d048655e4edd7e1f2a5a7459648be792a26fd47710ec4aadeccf80583f6863f7313ecf163e106d448074bebf8b6b0384e22b6bc0404297a76aa9ffd874aa987638cee5bab284d9a45529316df53c032529a6858cf0296581c58a2090ece0eee865a67b29dd2256ff701d967df154485abe388d7130205d106fd6a935f99df06469458e192089060e63635fa7f40d0050391f133ed0fbaa571807ff72b46157afdacfa5b59f9b51e04e82762bffe7ff267acc7ff7208cb3a14bb96958862d07b62b97a40fc7cafe3a406b3a16be0dfa07d911ef838037e9dfd4d9e4242dfd365ba297072b159229a0da4d42834e8212c332610f481079d0131695d0bef20e37520806dbef53f418202ec4cbc61853ca561474a19f802701cdfd829ed11652a1cbedc86d0fb19dcf6c817a24504df642a574b9a885609195d0e0d6c36f2a993ee5cb20c2cb631e4e76af28c640e944d16930ae48fd70cecf82f26856c1fef48eed5aa87bc59866658c33eba0ea930e888a5e1754bb60aa9448e6bc3550a75034257a2f09f0299218462a28c9dc174b8870ffbd51e0909df200e3dea1f824e47eca43e8c8d07aa64f530d05d686fd38d4e98bd9adca108beefc30c314b1de45f8c64bacfb4f37af2cab5941ba8b9733a74e427dbe9230429c575f29c3bb42809f0ef0b967fff59f39d2a15d70ba18d1228d0d211d0690cbe0cb805e139df5c3248ff241487e9e36f8575b3533f3493e2c634acb67591042b54a3da182da9b1d8c325de4124520f1ac2a4daf28b4b9bc1c1662c2b1c00064c504644bc040a7b710378c94d27ed8bd725f168e40deb6fd31cbc1534fbfc0ebe2984006f69a41c88327f95e9a83b043696796b6795806124e026729a99720a6d0d3a8a5d271259e685f4509e03c9f248da57e985bb875b8020317d569f1c02706e330304615cc28585cc58f93612e971fb37d999895d39ca8218d70621ca0d6941d6e49fd76a6c0835f672dbd9febfb0515e2b78023fdf84d6e61f84e89601be9a39a8830d54854a7a65729ba5f3cd3c14032de02d778ca0346b216a86400327c17ea26cc5b30ab1a5267fcb4637240fb4ad7af51082c7785fcbe5c8cd410f4853890a4963f61ffcaf3840b89c7c509cba56feee486a20051899633ca3540c27908bf726e622623ba6114c427a8bfc79671c0e19b90bfdfef86c7e3aed8900e072655ac78e1697126ccd8d2a28aea8d5a7a22ca98f644c803a68a1b20ccc07065b51ad24457e42ec0a0cbed34e2da60e7f240ae731972a6b433c15002b0f037d2c3ed9bcd8456335f70c0e95118a0b9aa7e27543d0bbe1fd2babe44cb84b04882aa68c80f5c1d29e932a95f5177a23e0a300710b37d5c80c755f53f017150bf69b3f2eb82515b2f23788e18f44b5e0b8c49d7a487610ed26d90c5d65ce530ff2b1f13eb0cc34e72e143806008c62e99023691065bb53811da8872a138b7406c1f482ff54b4c3f9f59a848225e93d76bae9f103f74f88d095df571ad5c7c707ad3802ad7c54fefb18ece9f1281d38f4c771fc6e896ab888d288cff334bf450747786214c8312a6c75a1641cde397c7b7b8e2e36228d5cc514c1d05dbe5fad07a52a9aee4995479c074818b526ccea7238a49f8e0e7fb37af0cb586ada63c1098327e72632fbb941abc7b1dcf88363ade042d30bd7dd78da61e66695c272ad0fa06052bee61bf7e414cf4f21ca902f0416bffcbb07097fddd33e0df6b99b8c02d79de833f5bbac42fa3face4b312ec66b14f042a19cd10ad155e99e301881a0b2e5d25c5a8fe78ad994bf5e9e63de7ff4fb824aaa3a91a8ef433407e6f348405349e0fa9c622b5dbdf97266ee5c01c4f956a73e7e10af46eba8821a44a6d860368b9b5fc8258fbb66020e5ec999e9f3bf8758f31240b8c4395aa90e4dc1154004f564b9f0ea0bff8482de8c84e16d32ec0df631d67b226f4da05f3cab7758906c89fd612c2df587fd9876d3bd36d5e3436baf87c7e544810ba954240c08f7404b06831e30a3704a60f416d3c671ef56fa356b61163d1b882da5fe5185663b20e9f9eae33b818519d3dee63f6f32f1bd6d78ecf65a4993df977e5914eea75bc02a018d1e49479341af28ed62ac972b5456cd557e9b74e5f3a1f35cbc8028c1406355b41b03122f4b059af443b4683bcf460258be285c8cc596bf465d00b59c70405e72e914b2593e52cbd585d8110942afdc2b37ea0d43a0a1e67febe35e05c05d4868d11d30ff438648423e27a8e7415d27409d2bb162c09ab6dff0a74b73b0088372625bf70d3b9c152e2d7b961adae90ce4528e0f75a000580b212f2ba1002353c3eb0ab0e7052f505d274ae791bde4f8e4b9b1faf31918fa3f06883cfd00ac7c7c3dc2bd496814b640acef41f5421ccef80c0e69fe0f11c37d6fa2f00140a79ffa7ad5bcd5fba27ee2c5c539d076b50e73a4ded5331d873cd45f90df3d9005bd42ee6b4a3afc447b64c5f28eaf15230dfa03481dee21f1777957b4732f30013c24b7c0cf0896e80b44ec520d79e493b1a34d0259842c0f3070685efa34a02ab99c537ccfa95ecad4928aabee08c21bd08fe16e1ad5ccdac94f118949c3809d9bf9bb0f1b092836d62b641028da70a35007c7c3b88320ad9d5a81688c92e0195d6c93edefc8ecf3538ed96bcf628ac2dc56c511ba38fb0074c8ced24f4360aae1b1bc1a564dd7b7ad1ac942be8c937432271903997bb53e12ebea84f31ae099c7fb17f577281e03976e1930c3d23312efcf6105e23adca809bf96105e8220c4d09fdfa77230f8e1c289c7478d34c57022dd387dd5ce36027cb342eb4b38d0b8592264ff6bba997a5ff1635072fb888bb5763c75acb7e79bd24ffdd79789b045b2050bc5dc16b85ad995a803f63f4e93f3dae9faa4b882619196a03ec4dc8016c969b8f9d42abcbfef9521c1a7f9a8c203fe996adb739736fc3eed0bab1bf0a2e0b0de96018460d59fed51b99c78d226ecbffcef62c9e9f0584b284c299ad0c0990119ded941ef79125ed5849d92f1e741468fb8c2be5163c8dcc62383d310f8dadafbaab5965f70d6ae0b8f1ac44774c775f6e175422fa69d16fa1350c2b0bd093950b3d558edc7713e6f42f587b46b973911c032f4b1b1725a42f8e2ac1043004eaddcfe69ee264d88637c197be079f0df399588d5b54734cd0d6f764020634c7665870a1cb3a3956448272e9d39be7cb02219c23adcf28fc3bfd120a0c086723449bb172519f95931d106b53fae73413f01edc97ba3e13b104a86c19bd06419f95a8f6474270ebcc8066e05e09509cffda11b6e9c1a889edf0d0a189380588ba53900aa25e23d486995c44e18b51711daa448939c4ee55ba11768ba47a0e1dbec99bc2f9240c08425f68efbc9d9ae91c5128a32df7f92568f81c9c7c430cb188e9e366b1957bd326f36f4eb4dd07deefd63557516cdb93e11f6e2642990b1a61fd08793745ba04c63096302688aad089916f4d38200d86e24f03909fe507a8561f243037bdadabde6fe33758122c2a2c6b177cd45827484e5ff95e2e7d03f900714aeecb47e14105dc604dc12f8e5a9e39a1a6390ddaf4c61e8f5726ea098e1569af91dc6f777c8025eabe755965b75fe7eb5ebb3ff44df617e0348a880a42325943ce83a5a0ac85b281d3ca2c23006e1ca0b6ae6ff809b6522f49e90d0aee1ce749e77f3fabd486c22249555c72de69a3aa2044d6b65845efb937c0540271cba56e1cc60208d1f7919b21004e05f160eb71642e60619d87bc164b781501753f7ee265822eacbd05f2996a26d8f7e8c766862d02bf84f0f9a76d12eca308cb55f65424020079e53224585a8f7479d7da6c6142de733ede66a95b11dea907e14ac0372d4c1c771cad3e89990d364e85b3ce0411ac81c3f72889c5f7945c073ba6f7e861c6c02018b7e772f46eb718a7d16dce3745b7156c037fcb5c715e08 true +check_ring_signature cd6e25149773a432c78c72d938197f905bde9ec06ecdb7b8c8dcd9a75656b8dd c2cd6788b538e19311440a244128d9a0d60bc1dff5fd21f32b7d68c9b5f9f1eb 2 f4701b22a6d7d00ccb35729b1d3621d4d9ed909b910fed4f2875ab625b77f06a 7519e76fc0a800d8102cda04423e3f9bd74c2bd7d924a8f798726cbf9e66de36 8eb8bc5c07eaae7959179b3d462eaf9dbf8ec830118fb3e190e1aa13499f8e03245909dca9294955c8ec3641c53301aef9273b56b95cc84733a638365de6ec0a3d18b27f5d8edfd61b655392d2b54c9de3fc8a9ffa365f16277b5161a007da0fd6e7dd16d789223ca6d1932fd54d601b09032c66ecb27d82c348b3bb67e42803 true +check_ring_signature b6c80855079612d8966f95baa8149c14df9d08c18b2c8ef09e0744823fe447eb bceb2fb5dbd34e4691209ddeb67aec4de311d25d05fe150c38d0a2991ef01007 2 06fa48d0a22e442ed43f4e3a4532403b982811998cf5efd2c83e05e4273fcebc b8d223e4b57c918f045c63a557db72dc3a0995218e8790e4e76b1c044f2b4d5e dd25f059006eda44697940bdf9e3ba13cb30eb6897906bd5f6b1287df7028e025537d64ac3026b028f95718a864c7ce33c6b05eb326b839c413aac981de907098ee399d9666bfdcb445be369842af3d4f91e057e141382314235a81c5eaf1fc237df9b8253ed644de53df6a41bb42e6488f2792d25636894b9e5d072c4e04601 false +check_ring_signature c84071319124f95f7238b34174a94ccdd5f5e257f7ec1df0e6d7f7fd9e344186 c38db0d61b37ad0cb268b81885b84166156b6559586655d3681cca26cf4163ac 104 3fb1b3fcfeff0578092be2027490fe3745b11186c0bb1ab406f53be1d6f13278 e4cc9f61de84c46cdf4b60ca9bd491c7e3c30139b8c3422b935747bb3e97868a 4a79fb96c24f027819303039f9879d36cdbd749dffde0e233e3d0526dd2582e4 0edc8f90f09629a7306017f82b057205fa0d0d3c02bab95487e825261914b54e 6efdfd12e7fbd197b9d7bb79897e23529c370bcd0a79e43fbbd9423763abde58 7bc2355548a7a2eeae1fd3aa07ebd4a2e2cc28bc1dd2cadec75c2e6816c6344d 0e4e73a47901231b84a3cbd8251041e388c837583bc9de52161910f7c2221280 b7d6cbed1949e664a9637b4720b06fff2163606635c58b773805b8bf5381f717 b53db7a778c37483951c14a306f097f54562526733ef352fb5482415e49e4555 31741c7d9dd6198b8c13367db14ca2a330ec3add294262de7eb3333d600d6a6d 1570a2ff21dc1e8fcccab1855d6ca373bdded00df75d3dfd8fb7c56da5424da7 efcb2ffcc8353c5311e73544441b5bd6d56d6a07ea7fb6abb1e722daaa07285e e103380ea0f9d905de5fb923e15546f00c84266cc30c58b5be1db52c63cd4388 22f8020efb6498fc055c4b0c83353c61b84d2d68c50c3630b776a2021bd9ebf8 e80a5588791c704bd825df47edec3dd64a26bc10f397038ddbd6517e44ea2d99 dd5abecf809077c4545a2ec35577d0d52b5094b01c97e948718bc8201607bffa 4c004c868c75607aa6b6f41de6b6dc70a6ffb87dfbe7deb7fd1ebe6c1bb0fdd9 cf79c9ad76f0d4b214f23c8fa549d321b4fb85329ac6df3f5759e3b76fd63f3f df1ca00a9ab38b75224e95af9c7d0b69a1a7326a8170e97dd726e165af6a43b0 0d54dbea2f303668765d2964a644a068a8bccc8c6f9acb419306aace58d380d9 9df2e5a7bf9d0984eaf56491c0093d2db4eacd07f51a5a4a1d03211438651db5 1e0b716eb636f17b96d1f3c360094f2b0afa577b49ff57b84684d3bea2b10c38 2ba07a2eef5e8b44bdbf21e628dbf037e42336abd10bae5a344f89fd062cf1de 529594e81e4c93d7491d18a2cfaceab75530e8381a2a72cdb170f622c454bdce 13a429a9d79b786a1f280334c85aaf0ffc79b0cb946232f273b752302363b530 5a9947ea12fb445b53bfec09d970246a165c5a737184940ed55ce94ddfb698ca d0d14e423a60bfc2cef8234d465b65538ce8d09b3b77dc62473213b571104753 d8c5eb49d9e22acf6f4a0513b273e5373269d77be9b2395be2e2f541914cade6 9b4e0ef9981afa2acbbcbe6435037a1c31a33ef7b98b860fc1faece9eb478ccb 82c7d6762789e1d895919bafb260e88faeebe1be80b25896558eb17b07b54c89 0195361b165e2d6b45e4474721b487e65d028fbb310ce4843576b8a5173d7bca 0f3efe9bd258941cb00ad4572e64770baf18844fef5ef258dd7c11cb5bbcdb78 7a182fb494a647303419ea8a4c8c9981f6292a2848bc18b2435f5cdd37177d67 09c4b40218a4c675ed702bcc385d699eac270a580a17d9bec0cb3ee8b57d2c16 22fac160c282a7469e76094d351fadfb891c83db5cf599bb53f3b8e938f5943c 3186faaaf1cbfde9e9f65fdfbef25fba841ce1d33f2ad6f7268af5714546207a 1952db5689cc8842510ef83a26f368d6f4661e09c86be55cf70754c04d79703e 077718531aa61d482aa4cebe6430d02c2df47beabe157e6ce1a534a82ee2f023 2d736875b88c098032ec88ae24a74263943120f671e5ded175434bfdbaecaee5 66565aefef3980af6da36c46cd95f467396c1cfa8e20792ddba3d9e527cc4ff1 456f94c2167626338c0bf58cdbd2140ba3427b3bfb1b5be36878b66f25b75ae1 767e19d4eb61b08a9a14f880fb50366624594c51c2f858eb33c087802475c26a 1008b5b1a2c304c11551147bb821ecd3ac88b088158bb3d668b0ab7b553d8752 e8b807d3bf3c45ff3a4a1de93858ff173a6cc267034efc6cc07b7e8330b9889d 3b8b14ab6291ad00d1d0ee26b37631d1f79072c457ce81d6df3256c7ee285a64 9e0f7dc7147c3c673d24af47cabecb76967bd1bf204f6c8d1a4c5e18d59b58a6 bd06c4935589ac1a052e88720002d16131efc4082f427b69d21d273fce441e35 ebf4aad6742a7698e3ca5ac79fb05f7e824af726b7595f18261c6ab1c22484b9 d941b9df25acf9638d494282e12e959efae14fa01fc9043a1742db2b293f28c2 0d6f60e8f2e3585cf35c1adddb3732129e7a3615a2d3c5bbb17cd35e9f2c7c9f f99e38fd937fb1bf044208af12c3594f08a6bcdc1068088b68a52afeb5722a00 ab2be18d23ec515d6cd793850384b0c7abda5c4a9de42b709dbae2b12c3f37b6 fc7c5ec561ea4f4c0042cbd2aa5f912ed9f9773a6ccb77641f4750c5784e165a a751a9c2b659f1df96b8ad198aa0a0cf6d9406c175e34bf2a454cffe88346159 0dfb788d56a07277684682c2357e17d6f1dcd0b9497c9640c72dbff0629d346f c7eacf32986f528c4c43c402c80693e3c234e1c7dde6170b447c4d6c996ca840 0739af8d936c8d58d1bbd1c9c3bf91b88b794d6232a5880188aac98c99d231c1 3728d4aa41967293dad277b23a9f3efd44313330457b9f4d1326a8fae4aeeb68 40f02c9209fb333ded1c3705fb8a85f7e0e21566e8a77b5f36237b0368d568ce 30e707685fafbf702c6ff5c48f7bb30adc5b4c8027326700e90aa62b04256ef5 3cd68d502c56468cb311298847872ad5217c920b42b601644009cfda65d8e710 db288038cb24d736029ca50fb2b691861209613c375180a0858ad3851f0e8007 407b27cecbef6175f7863d2536c7fcfd9219b4205a42f34459d071c5573e0826 da0d16aac1efb879d18b0a73736155949278e488ba787d83ae6796b753fc1ee1 e6bfd1a5d9232c970ec3be03996310854e75fa990d70065218533e875cf33af6 80b7355103999e5982cfd9424a1c1ff5eb053a3a59c8832f16af2d61f727f9e7 488bb7b8ad317fdf957ccaaa43300e4a34b801bd14291299bd557d0762ed1c25 1877257ab202b50e0a2c45704edbecb590d8c4ddeccc62a4edec27c4c5e44c1c 3cbc1e8fca67b22d98a8c8ebba4e98ec0c9c10d35cca7e64fc6732006d13daf0 19851e3afb2322ea78731f74a59311edec529cb30fa70803c7dd5ee97976b6e0 6151a043d02f7c831a56e86a32ba020fa1bf345b4232067e27874132c3d49185 a718775484f99fc55162ed6ee2d32350926c344f81a6b439ff5639a9bd6307fa ac57b3909c78536bb4dc1db8c80fc36bf30395e98900431efd9c29437942df8f 07743a41fa5508baa10b8628ef8b73e9c03e4fff755e8f9c4776f1460cefea34 631eebafe887b7288de12cb0bc21544868da50ec62af5378ae04d642ce16adaf 2d44d0cab69fb64cf4ba82c935d109c92120f21a8ae12129b6c007259ac8b21c b970fe5db5843987deedcec46161f328740d7f7378d9e0c4069e44802c27107c cb2f504f5cecc3863435a1863eac1d2a5df2aa56fd010fafbd74d35ffd12c6df 37a39639d5403a5ce69f00a1eea2eda6a7286d54a7dce04043b0965263e17270 da0b5d06f129480ae1d82b24d85d28ffc514e3bffd05d595c966948ae42c828c 43099151d0956ab078a3add81ff28f1d523755deaa91c7dd5e7a4d08d99477fd 7a3a2affc71c86dd9977a8e123c3c08b04fcc745d070f1ac79cf3f0a39b6bb83 b6c033af32351a5410b2d0a71d7c5ace068802729054e7d36132475a32339590 ffcde6b875c34d222ce465f914f27cfea8fc469fc0da147935b469303e1d0f46 5e6d9563eaa9f8f8b7530eaf95ee0d9600a39599316a244de328925ebf3843b0 ef3de4dc700f3289711ac431b3ed669252cc85a8c9e9bd06bb908a8e24459a9d 3eaf17be8f60daea93328f36293ef2e61ee47fae31c030e60f61fbcd98b4477e 07a4297919224aea5f8fb45f91c4c77f1dbb0c4a5c5602e67536479714a93b4e 2e5ba9e2606717ba0d1afc5f4f50e5f200b1f610b262257818d14f4b4ae547f6 be6ee7323b5083958ddb94abd2e292345d7d577ee3be5e7a02f31e58d7030dec e201875b5fd0fadd04c3302e1407543f42c65822efa387d35708dae20e5f3dd7 cab21727b20dfbdb2971a4536e0e078690a624bdc3fa55913d992a7c28e326e0 9d100b1c8b5e73f7f2d5b989801fd4a613ee545e7b747b5d70408bb6ad360333 96cd5098dee49669046823d8e4ef6c762a992f6074a0b47e96deaade0195d718 4603ae06e3d201960ba8a9e639ef15f2075ae97795e82aaace1986a494233292 ed4bc615ab376cb472108e116bba3f0f2a0fe37b1da1f4559ef947c8d0ce2ccf 1df9458f06d41d1da12d3eb145109329c8f23b44eb6603dc1f72f26b9d1573dd 660fe168c9e6f8d9e3505fa1cfae69302cb44c571a99c88d33deaddd35eb2dca a1c5ec5e005f3c750e49860b0d439b06e71bfa0d6f3df92ab14ab0fe365a79e2 e9613a4c186aac86732d013c28858a6c57951e105e4306b8db10a504b4360ea6 f617b93fc4bfd31eb35da2b7ecb6f1463de90b91a1ccebea20f6e4ce549ff4a9 684f0f1cf4adde4ab71553c7691c5d7212a79d1d863b67e18895c122e043c324 ec81f5c46da8e50243972c20546c9b47a5e4b070e9a1f2b70d812222191d69a6 3ff04b3a18c4c039a2969b4fdb4924df3d8c08b943fa79df3ba06c69f434558c a79cd50341ff8fab4ecca581382c7202d50b0bed9471069397c9d52c3b99790efc58c6011dbaaa0f491bafa59a615858527734d3a1e7cc31fd0df43accd68f0c8cc5654a7d97c0bf19270e87ad303c7fe21323ecb8cf4b1190ba6530a1d09608ad657d48aadda16a4e17d7ebda0d4ca4c69854ed8572ba159e0144b3526438012c4d1543e4a76b1f2420bfd3e96451cb024c4ab3bf0fa89b69e4e9a64e1eef0c215ce8b179d3fc6ab79b906703d9c870b917d1a14bc8ea94a348153182493a02f24895ffabfef71baed51a6272279c0c7a53be00cbee268cbfbd4de99d70810428cfc911479f6c422674506a8d656807295dfd0b1fbe0304a90074006de0530d9851222ab81049709941b5800a3101cdfe517210a330a5fc16e17cd79228ea018413bc54a4d161d36b6f0c7a61277a2ebec3383658aa7eb6f13c567e61bd2706d074a0e4fbac12c4493cd09a179739b48cd24b8fdd3e531423ea9e596342360702486d10a139301eda77cede07f45410d69be78ce87ea5fe9afcb6f4f32c7e0669e00dd63b9b6156032057e187514f310610900201f44af2a79353b8587c0f060e10c5c16598c9292b8b6be663cefa1401be073c58eb79a198638456190b97027ade9d9486b1b06f724e8849c943b70b8c3c97b9eb9d5662ea4117072317f408246f8aec118ef46440a537139c4884d7a9948e1bc35357be284532b831f7950bcc90dedbb3c68c403370b8e619bea7e57e7b205edbc07a295e329f0b936f2b074c055b3e5b20882b053a756e7114a1148f8eaeb09c877a95537e2cdcdf4516015aef165d228bbdd4619a98a2333f323d989fb435f0f08946643d30ffe687690210bdd05845a478c26d67edb06b90df512d02b6413ce20eee898698759e91040a41018ae139011960baea2192b00e39b59bb156662cdbd9d72721176027816704260b16d1a26cda02b4914e352c46c674c5d905bc586c4f6f31c2a67f5592a8074a877d309bb01241454f75c71c73bde4915d9aecd78595edd83c845e5fce9d07c5763ae07b26e78b19fc170ba3d9c445489a8f28ab9b23d61531eb492ec88900c64c7ce26cbd5dcdbec8732bb70034da505416c11527ecfc65279cfa92b2da0cf5761ccc159fe9f52194b16a0a7a62da6cf427d99833d06da79ce972db28c50bf8a42df48f6cca0d18330facc9a02bdebd45767db36de0100397690b6e449f0596668e3d307f80800c0506d8bd7b250a6230838fe4f4ed3c00c396639e26000cdbc386f7b3e1b8c805fee62a86a5cc00d40646d353ee83f5d320ffea7bab3a06baa3a42535de4dad3f6b8ff933ebe30aab9aa69e95b5167f750f5b8a59f083050a864cc753a6f04357a376c2391abb2bfeb451bcb654abddfce6fa1c6299f407f1ba1a331d969afc72ef28d259b56e07b00f83a996503f947165627509a1f00a5a990b0090d031bddfb6601255526bc88c5ddb3c2e7eeba88b696e74b3e49303b1e135be12ebedf818126de61235dd45bd081db38c68826a4bbb852fb60df603d3c6552fa8f5db35cdeab16547842b1e90639f68384087e5bc0be0059588ab02bf2ac44fb9ac39bfffa9a783fd5fb2957d5c4a2f41a936477a7f63dad9b79104523c7ded771476d2496edfbeb9e6563bbe143a71a2ff9ef70bad8f571dee31028df7e0fa401ff2efc3fa22e5b1d25775c275d449f388017b6e8a704afa20f7016f1546c44998451fae94d3f3025f2441855d2f8b3ceb6c1a6d5516fe93543609915bdf647a2a867e0440c387d30a95b251fd8d0cbc48b267e7bd10a5b4cc1203544c5ed04b0242269dbd445c16fcf16589608362e07cb264baa64b48854adc09dec86f90f8e2c60de9a87f2a18277f43918ee92a4030ef045e3185af3305e80aa3e461d1cb896790db408619c02a3e271c62979cade8c6e58944c5ffa5a03b0e427b703951499f3027270b941e500280f49033d639bdb4f11634102eb336b308f4e4d43bc4e82371bf9253cc531f55bf2cca587d5cb6727fa50b1a0df91897068ed3cb67044c38b023ad559eed3beffd393f02213bad19b3cad9f0f224081b0f5c17aaab2578d975e39e49000d715a6736eabf33a831d876ee30d70040e8940a25916ce5a772ebc24fdcf0c338c3b85449fca6bff9e449b9b565d503ac6b31024ccec01e121a02a95f6516ca35bf4a2ba2052e4f7e814cd6bfdf0fcdaae5120db8405419971f813851f17f84f6bf8db7766b206a2c150878239018537a1ccd0aad7f49dfae83347aad629612dc03802caeb1f9382500585aa3f630e326452507e4934f476aabafa024ad79f17528974ac66da7d6ec0820992487149763bcf50f0f04e4480815d30f14c8c3a177eae07f53f3c9c838a22e8cf47f770fbb92430c1fc5d43bd5ed84d54b29c4f983a2b428d124d4b488cf4188e6434192c8e9c20d19c4aea99e50589ee3081556a15c9f0e2269b7378ee35bc9e94277d40db75d01e50412f7684ea371b36ed97294cdded71a5eee4af175c17aea532700978662011d9a325d26c6acd543b407e63573db2fc6bc32ffcbe5b75aa0d37c84fdf28c0df160852f663e91fb9ece9212d65bf3ba6ef469cf1d1aac229c7d54ca180da40bc207c352bf5031e51df5c414cf69a9160f56f4ca9a1ea5727415a075a402cc021ba91b626a2bca69907f6ea922339f736dacf10f8a04d6697e3e885f21b6830852ebb16c67fd755d237b4df17604be3ed63136585ce0116e7949745c7047ef0ee6b963b3989643fc2884cb01685034f629b2f98e2a06a42fdf113586705372040bb1ed3e05d4fe38e825f510f169663eea514e634cb9e84fdaf4445e831b4502866410ee0458ff62a2e5d98dcc866f671159dbb3df90e19a4b5d22956b3c300cf06d24bc49a30ff19a1a0f105a4babb1dbda43b91235890bf713c278eaf7860d7001e369d4d9aa668c99fbebaa4138a71f53e7b3ec2567df7e1376d43ead220e6282ade7a8f5e70c781e98c3afdeab58e7edfac5f0e0308f5835de72ce10b1085c3b9bf69a479c3a2ade124227f331408c233ef3f400b0f6ddc1d44606a88006e60f7598026610395e5cdb7abf3cb3535dedadf51f61ccf591f1517a659efe0cae51db3b7f1d25e48b7a6a64d15776b3f5f7eb6bc4451b20ffff6503821fdf00ae0d27bee189660c0ede5aa412dc2bb79363b9915503624525708d3c6c532a0ed0eb9402ebb57ec1e1ca0c9710652d5cda9889449c45d39ef2c4188546b2c903673a48c747964d2195921d77eea35f7653ed3174adaec1df1535899732a0d20bc7c4454857886301e60ab6bb76ee21f1185a80435fa76de3dd138112848eb80b5c1484b6ff5c63c742a94e0596022f38741872507f8b756c7450e081f2a66c054170f6492a08001988c48c8c17e91a49206be88f9559fdd848023fbb4a4f3e0ba67351bb2710e2395c78d978aab896d32c7e25fa6cb918e7468e8e01542f070dcb478f8ad193235f40bb5a3085caadf2cb69cc35b6846cd49ac3d5aab785260396e1b455a384a388590defa75c2b2f8bcfd4beb77054a0e0ec04757835794f0a356945f004c0a217a7769efe60b6f15f1258b091be51b67c031ffc097ee0e90042c22865e1a7f1159c4af8d61d1db3fd35c25355d698a3eed5d2cac55e297206ec3cf2007b226971e63a3866ff1c3ea3992026e8828cd510d3b30e271477690dc8bf13d2a5c3a81bbf81ebe0d8b7e912be4b5709c72f3511c0831a89c2251002ed2d0bc2f13ad5f871675b973c0a1d7aba55a38a973923a7eff784faefe5990ba05e40b190cf8376c9ed2e26f4810ed39b350da839424a3b99bad14557ac780ba2ce93d1ff2042df998377c143de3ac1fa09ba1813ddba9586f79bb7eb8a670e242200c929d4c7bc8b542b99b71b7fcdb71866c69bcc5c5bd9017bd9997e7f07f9481014fdc2271380ca4d88a88bf411bbfc04f497971faa6c8664084e9371086c42fcd9453e477d400ea2c2a950e39381ab7d51a1fbac19c16999ddfbf3f50a3535e7124370b5d6976f9ff4bd432c53d82e8fdd2e88c39d868f31f39b6c420dd3e683c948b57c485c8e9c18109c093aadcd6201664aa4d3c4beb3180224fe0db7f788938aee7dbdc9ef5cb8506747d542abdd0e37e4a66a338c810d673a460cc7b7af49e10ba9df7cd23f04d243b783b27d0b1235b5a8ea3f2ea914c7be13083cc5ebf53c7aafac1a1992eecd52baacbe06546383bcfb9919c78766c520c1018e85890d6391458f31ded5e9337e269ee50f5376e0aaa37ba4e01814b25f910559d801516d8b0a9eb883016409deb2a8e4e6fae224d9fa12efd0a6ebd1768503a911bfc696f48eec38b03bbd11710cc7517bc204d8a6e4e4c970eb927c18c702fb09036b378c14c5f44e6588f1fbc1ef1f961e35d0372a2ee7ef5e91863c130268d995611fde9365891ffb5c2a18a171580c585eea5d19f30abab16f6ee5cc0e7b2fea29c886751e75034904ef89201c7486c06d14bba420336331a672d6160f635550bbd2ac0bfb2a1b35851bfd02839495d29e050f43b38c867ce1d5b65906949299aa11ff5e1aff78e96527baaa5b04c5580febbc7f6b8d350f5b4471720e503f622a3fab050d4b1e12f157a6eb4755f56acc9902aa8316ced85d92a3e208e2806c45c1eed2adb404bc83913de4855b110e772551cf23201f4d3bf69a1109ddc67a1f692c672de080b0db0ae1d277a583a0206401ae055b45037e3ba6ac0fb6da914ee37708eb79e0f5d3e0f6122092070276baa1b9761f41fd5d412cf0092b763d5e3c533e7d6b7813b22c927399239f0f3ca9de5fc38870b31cc0dbf70bc259a0881a50468524b58ecaa977d6357628ac017cbd6b36f4e4f32848d08801867f586b8b2a0b61034b7860ffc34dd1ae01a1614fa76b9a7384715774012709456c3eea6fa6114bb75b913affe17e7c97a9ebb7a1e8f1befc35dc7e1fc31f0f5e4cde3c9f38008ee7b0b567a1c70e61c913d862f335f1b120d45bcd238b7c068927e7597815dbb66bac4694654443fd131277a1ef7ced20a12dfbd2c7219c00f883d939858bc657b02ecd54a0ce3e4bf5340cff1e819863a04353116a94710bcc5036ea30f39eeb174c674bee5bbedc18d0b13acbb209346fc928c7b2dd33072dae571126662d11a27ad55368500975771a06d8f8f95e99ac13f797bff94d0d6ea993e8ade672b1383cef17066d04f47849a6a5282da0a64510849c31d59e0344b92cea965d2c0620b499953afcd1c50a9d057c1bbd0e5dac80062c59ff0f0caece45394347dc00f2a2b47a1f5c32973c35a372566d933d6a362099ff84bf0851f4bf67ac2020e862d6d79562d52abb37c0f1944248727742c5206a7c047d04b733c23eb440354b24f07fcb5e7eea56f41d94d5656ae8a47ff1b438d87b5f0c62f3140cf241d1327464c3fb0d8e6c837763de1f5cfad7a8810a16ae1f9515078f87870c9052a405cd6e0e5aef5142d28daca6e318dced963810940f4d22b408ddbf88e855ce62ba4d1a0c5040483aa10b3ad42409c5c59db903bfd8c8977c0a30055fdac2334b6a5a8972370f8b1d059e8db2453847a547c5dadeeb7c80c90b377d8cd95c1378e459e423248b2a73bad37e1d01eefc8a46223129624a403d0287378d1da053d594201b66b36e66625e6fa2b79da5fb19fcbe066d08a23b79049b20cd040293841711ed179cb8b8f43fdcf882ac48638cc13c8477879ebd270adae82c2da6916bb1d75cf2fc7861a04c8a73491f4825854ad0294ff6bd049e0e527aa66c92dd3cdee5b6af94251f8b4f9d83ac34f5b8bc401f657e6c458a8c0ed453b03076fec0b20f5c7763c51d515829a96288744d3566d49a887dec7e290e58242cf9fabd7ffb2e2c364b4327936bfbb60644e24847258b78fb7aa6dbe90545047ecfc4ee0d2df48744fa8fff1777dc8f6af5d1e49b2b2042de177be3ce076b6e3258a33ad2190902ccd2f564a005212e5053127e794510acf0ca1af5d20d3c099542aa389864bf05a6cde00571e6d93d06bbb9777d6aa9374c0d5de9ce04ad0b441713f69521e32fc6dbe40370afa8ffc5e216ab1476d5f5e7534492c704423b4e93c2cc926ccb4e0def7bce75b5b02e35c976c6d97875266564ec41b104bead6fb8079a4b92b5978cb4c6ea1e5a70c1d69c079d5ff8db87c92497652f02f93f59d10c6c65d06ec8a7bd303596c7cd78a933ca55fa33dffb9feed219490393e134fc9f42cf51c952cf6140d4adc118840fc379372813e1c9792e2f7dbb019784673bbf49800ea4c28a6d8d78f57fa7c597bc765644819f5a3e8e76a80803a527632af0816a0912524e0ae67e6765a9e80b34610cf9fcb202708ec6f0d406e96e79f809657316316aa56505edae6aae9d64c25b3e5bd325726f8cad66eb0b780a6e88b8f064e5c52d0261fcc456339e948001125a4a775fc87d4ddbcfcd0407bedfccd3f93bc52a284b57270c8732458e64ea2c5e755cba61ef9973abce0c0aff93d48e91a1a9020f10008688a830237a0e38ff953fc7eec706997d9c6c05ddb842f991ed11e77d059bca52f30a8077069506b4f7064bfc853be2fb3d3e03f4cc528a8c1efef00c5e23db6e0baa0c360ae50fc41033c614fd42c9f35d260d270ab8516e93b14161bcf2597f1d56b9a701855b93046ddf203ae7b4b2d5880c496314f619a4d538a4be8f4c9f7b90c557ed430df02edd999866c617d06e1504b6d865d6b019ecb5bca977d792ee12484f592ab1e0032187e8f31ab924456a084c09ccf82954a863da6c053705a8f6d7f9a195acb768f37b8d42a69df2015504bf38e8ff90f3ecb3cf627c2028614120802b15ab1f0ae7e46d4ddd980a78c5013bc08793674f1400618a5b112deeee4984847956d66090fa4ad4da94d7a332079ec97e090e06897c0c16d5ea8953e0387559cb9c7e7ac129fea1e3c2d53ffb079a6b32fea2fb64269c9c836423146a0e446553a6ccf79057c95b8e1cf977cd0a73c205186447c424724be75411c4042c089d53640c865d042f665d1ea1dc590093c14f25d2a15fc925f66f6a99dbc2403000d216afc16d7e6e0a969bb5eac100cf2114c3b94909e19ec1c3324579498dd84bbd6ec6d4ab815da3f76321d1fc0651308bc1c030abf0154dfd6c6a35f62e4d3c2f65e4541e71cd32b24e272c8d06eb01ff6013f74db1b172e93a60ffbc6e6f412f330e4f297f1e034d6f77205e0ffa45e5df0a90d59f5872894fc7da3678aa907ed76a5dcebf2cf36fa48a9ef50c1eb78fb8692d5704e673431435985c7f38203d180d0ac12e94bd812eda2cd503948503fa453f24cdf213e9843e40526b729bd0b5d2da954cb10c81ecbbe94c0f9216e1faa247a317c49edc17aa8fed45d94f0c46dc29476b3c2e014657d3260925ccc723c81a1917ef8be8eda9a2628c356fba1101eb53c07697f32d60abaa089aa70a9561d2ef75a0056ece6cadca8bd3ba621c24861eee11edda2eabd5d909522b0e48d9fb5d8f6c16cd8d213571fd491b852678d33e3579b49b53657bf10590fa295a3e935aa1323526980e915cc880b3cf145e4cf694bbffdeaacf15a7039a98654db0a99795c27a035f475462e3e1d8efcfe51078e4d4684b9081a24f0b3c2d5219dc3b94742e5e742a542b75be8ce407ea5c49a641d129f40ceef7830c50a25a723c631c6d15e19744fba6071187bd9096bba5cd93df5214173edfdf02d8b5353169963c9aaf6b6c6bff6c603f4994578e15aaf700f914d2d1d61bd90a9d8486eb5eca8ec44edbd8544aa043c0b0624a81a546be96d66bdca86342900ab46996d08b1f15128d5c40a7db9f56ee43ea9b9bbb4fc744e45b7d1c69f8530a3a87d27e3e0582b9885d696ec715fc10ed0f310b3d98699f5d8dff8843d09d0689c9656412339bc9913fe68958c93212510ab147a29e8ec8581290733e205309ab7b66dd00b0dc90be75e909090327e93a82b5304a9196dbd97bdde3bca6da0b7524d00f5b1b056f0ab30b654d96e06c033c0a66e405914595b5da05c13e810d1a614991e793d931d7d66e181c99235af756ae0a4caee93c12e9c7a4894db40d4b40da1713427295c60e0bc5c5bcd0bbe509b64b0e12fa6d8f9bb1ce0a2b3d08d29289427d827471b0c72ed90f36e4c00287bd9ccd39c9726699efefdd7c9e0ffbcb753461a5d351b8eb076b69d5202fef8a66993a27e46786b531ab45eeea0b010254f11e74a7c1f93ce1fff8b14cb02898224784c037a7e1b4bc6c247bac08a7e3d9c4b209694fee6d734c6fa8717c605875ec023a8c225105e77b30a93a01d9d60537b6f4c0b9bddb2d12b2171f1d7a868286d53eb1afb942c4c122e9290e8299d84a24cf948ca70462bff30c2f030d2bfcb6a70986bdc6cea2f887a97a056d118d2c4f72ccdf7bd7ec21a1481be7d08f38a1b07c40142aa892a36f37dd0db8cd31680b4e224e3734f08e063179957595caa2bcc9af22baf8c7cec598390baf5321beeac8e6727903c4456713d21b99ed4ba6b292aef90564cb2fe6bb060e29f5c7c724a708b40c8000ae1f635ed6fce2a09e7078ae98b8ace45e88b2870d52fd7264ddedc956e1be7116b9d254023c032f74cf044621e22e91ee0a09e301d58b5229e76c9f4c70f918e0adb358e8bb0b7f0d75fea375175871717a92ea036d2ef268f1c9e859d2560f1ba000f24eb7ef4115ba876632bca97b2ae0b16a0c2600e41828cf7dfa27e661a312f952e56cc16d2595d6eef8e02ee86c64889f0f33f05e89614c3914ded5fd700566718b582a04b8fcdfa287d98747f60c7b73068154cb6a39d1e8fdc2ba7d0cbe3815e2b9b47190a76549ae2cf5c5e5303b29068f4896bdc77731d74aaca40f431b8e97d8d13164796376e76ea9b2868f4c19056dfc434de1da2480db4fe15eb647d5576c194a08b092000305358aa85a79eb0099e2531ff048b7b5a5469b89d38c2a65cc046682393ef8b1eef7ed54e9f92c0c5f14cb9e2ca26197e86512b243bf31263b48bc1741abf85074dd9ff7a852e60d15c92e11549d0696f2050d5ab944c8f0bfe948031d2f8289d104b52cbf10ca0985c1fa0392c7418a948f02d94f7e7f875bd94cffc6933d700c9615cbc20f0a0ef44c9cc894006e8f9062982bf87e1abe02362001676c37c6079a08bebfa08700525c7d1302f5dda8b2e649a981cd6872d5e35828544300f921564e1a93095c06de752be7ceeca5493b5e00b5f2de5cebe79e54a277481bc2703f090e0fdcd1090600f7164f4bc593aae5af10fe2469283a3f4a2ebd727a8630ff9e8c638b2f0a4d7629ef2baa3552e42e6af2dafda39de38b6eecb54f127a849e1d778bb44a06aa0d6692eec96264a7e3d8f94198c6f6502f4847c2ed9a764abd54f93a587a0d true +check_ring_signature f6b1f493e8c8adc6fdfff9245143fc1d590c3d7db672547b41f1d57cdd18330f 6f25337adefbecee657461ce446b7dda007a39e58c4a893bc55c9fac81b05863 6 699d4c03587fbf87d119c1ff0d1e35a2041b34751be315fdbc4f6b882b6b301b 774e0fd17dd9e085f882a3640fd65ee718d93b28628928685dbd3797183f807e 3727ab0a9a3d910aab4ce2219681e70ac2642a3f73bcca22d4d4c6a8b9fe80bb 7086fcd042f219294f49ad974038212fb9f59aa702c8c1555af1cdc4bf0becf9 85b28dc0f47a9bdce6aac10915ec9fd484e5daf62268c1ee37df11a6c445ae2e 41fdb6a0f86e0040d788941c5f9818e2c8479500457658c9fd261f28a0c9ef5b 50f783ee86a20ce319feea4516919e4c5e5c3c508c80ed475b2e9d048458850709414b165a554b7afb8bf4ea52380b147768a1d197104dcd9a8c1aa87b683f046a3235ee1dde62b6615a38eda1620a679dc1ffe6d6f222f18822083cc829d50858787b89eb3b6a01c0bab7e0cfb34768030f9b5a339f27d1abba141b9ac5c6001c21165979965aed26402d8a030bb3ff1f68261f33832c16b932dcf8a575e60392d80cbde2638f3681d3d0a1b2b71ca5ea093ab701d63bb86a98a64637534d0a615a192ea05bbf5cac6049a162cfc02a1af4649fda05e5bbc26bb7c1507b230038dcccea21c7473398bc85efaf7c1c6a57e1527c50c39ed7a22900f5c7e87605a4bfd43a25157fc05cda2ec509190c83b2d84e0f23083bd155d9f857e5626b077c2f2ad698bd3d67b5ebc82f4a15105e6a9b2fc809e0856b6db2686c59c76e014b05e7b47c4ad5fd1c3fd2c4f464d7e9aabbbbfe685caf92d561ec575e256902fae81e461fe006f604a18cbf96716503705868b1055e01f5cea907fcd1cf860f true +check_ring_signature b2ae8c223492c2017998d627a197e2ee0dd93526d0a053e1d76400a5b98f2048 0b4df12a1e630cf57bfae30539cbbc3639cf829685794cb389e7b84bc9d1ea35 4 b6556c259040219cef5ba8496fe584d1a9165af495b54d8907657df9327ba951 6654fa17529cb84968a40ffb35e7e4953820d601e21e5e41b461152dd9d1778f afbc3cda02485418d5625612594b949ad3cfd067e92d6dd6b0dfa767c7288fc1 21e9a83a7d099f12326ca92e3d29cf341092ff5cf070110a8e21e7b9d2041809 4a5b370630dd59adeaa275bff3c3c6e09e737b4a1820dca0105ce67066c0a000dd2d60d7de8aff29fad82848a3cf3c6291e3c72df59dc25de3db678eac36e50d7f09012179781a9cdb076bee03e1cf74b747f5c0f4aab4320b6def4254634b020cf1ece58296e4ad845447de82109be0e7787002b8fb25c024b2281cf6cbc00da87213e6f2e5e4a626591af6fa65494a3f06cac256178edf7cddf77489808f04eba7f2d89ca30d4137b72bac79eafe130d3be9b10d49e409db57d80c8aecd00ac29b14d29e4473de95babb14801a24f5d09d1eaab864faed2ba4fb59c0710b07765058dfb31877748f94e725161a8510d87497d6f427f108849b6571ea9cd308 false +check_ring_signature a3e0f01a87aec00d4c11e688cd1f2fc802e090c32b5dbb5bb75361fc9633c4b7 e1e7f37893bb3e3ffb63dbf74b88522910ee86d1db5a96f803031b6e7f15386a 8 c76f205fc90b693d50ae72d6642d49920ddb49caf0915bc7aee14600eb7995e0 d1384dcd8159a7e65e218a5c8d2cfb23b3da968067f8b36c1b0b3706f10978be 8d4080f36568a0ce38fad0183e0810974da696b2e5d6017242d6ffc0444a900a 1e3b0034a8028509f0883a97d9ff69bba25964e4ea6bb25482f0e0732625369f cb0c506688a523b80a0711bf946e366de3d556d2a297c714b61db6697c85d6d4 8feacdb787d4292b209155bae67384c3afc7060d1e5ee72ea080ce3ddc18a656 ed3447fb2c16a9ab9b7ed4c9e55477ee7c08058b8e5fec27dc1098539dce8e5b a3dfd42f03dce8e930295268ded84873808ccabc5fc1014a9ccccfd43b0e298e ff47be8fa8bb0f6dc0607d7739f97213997f18e724cf749dce76a91c3386830efb0d4178277d842e6974e3ee3e8f4a67938981968be02caa6938343f206728065adc107d04db7dcf49f3e019aea5a4cf178550306ec192ef98c13076cc90cd0c412423a0cad6904681bc77b974552d73397378df9fe2593213f9cc69aec65208560b9c97c636571f00c243853a4f742fec256512f345e7546051752223d13f0e990c38fb458dd70fb0af9cb595f179df222bf6d024f23b0fc8581b226bab3b0552db8df4a93830415a66584f432262afba1a3146cc38efbef602e9a391e3c30a24b2d4c2562a71f0b0f9c0cc8c668c3c95b32ae20e071e5b8eb114aaa4ce0407426f6a05e70928d40d5c9be8e808cbc021829e90fceba6583baffe05ba129c076ce1e787679e65754f4d5622ebddcbc01748cd8db4ea10e29297704ba326c7086a3c380852ee43c9d63cb30f8d324d94ef1fea2772e188b31d30c7e698d33101cac3a9f4b5758fa1ac9824ddc95aa2c0a5099f24d566d0251a1ad7a47dcae70388940ce3437ef63f87efc6c83639377868638ab181cd0cd5fb19cb2fbed44005e1117f7e4503b600b305d1507aab1b190935328afb990b8f59b9e0cb65a5dc0d8686dd92c8056984297f3485c1dc08facb96ee5bef0f1eafc95f50f014d8bf0a12f60c25d2f0d853e77d1aa9198cc9ba5735c0eb6a211e9f0496636b9111dd01 false +check_ring_signature d31b67e7dc0515fdfdc0ff2a98a8ccc539a9615b861ac3a9b44e33d7f162e536 ecc1a4f06c693d57971cd9e7b7270adc0b7d615b56ed34a88b94f28dfdc1abac 26 34ac90eea29b3e9bb81090991455e6fdac83f186a53010b9d9a3305783621d2d 15f65c77c234bff56340570d9c8b8899c0452d83486bdd5fff373e6d832e9e5a 38a1a988a596e32095401ed6f4c92245e271254ed5dc5b32186fb95d4566962f 563ee0094e1bf1fd207d0cf5bce615bb1284cece8175f211816919978eaf04e6 5a7d0a6b49f0b058e18c6bbf46e3af46064eaab59dab1e6d2c5549dd72a4eb3f 564bc5944a83ed6d3b3ef7f68e5ea377949763d4d6e5f0de30f07b16d926363a 5491c7c2f59aa1bff541a43451c5a09ff97473686db2d8d8921cfa1eb8b480c9 0a2816a1dabf8041e01ed51c9df0fed62d725a9a4334fb263e93d9741ca2bd5c 1ffc780c54142c69fdc276ddfaca59a5243dac7ff44ea17ca3b152f4ab9aadc7 87a16cbdb6095e0d238fc8266d00f4e59018bb83816211ac341a173979cadeea 145135949d6c35845d47c4fb10a5bb5241a363c817a8cdb775461384e76df50f 49ce296346be4d275263701b0e79a72cd00e37c9c968839b8aee4b836ed5b67a 00f418c7d352eabb2c1c7e417fc30a2de184578f5223e5e08b273c0ae57c1674 a936f591f35c11776fba42a8e36a0c3834caea9c6855f5834630ef0cc4b70be6 a89b6c750289459706136d6dea1847ea2c3fbbe2dee25556505a0afc6c0ca9ce e7d30ca74a2bb1bc91c3dd0770a6524000a8494ab032f78c16f6fc6e811bae73 81d8eeb70126eafdf43e6da1a6a10afede2d2bfd6d678c908152d161689dc836 c00676b1474a108df433ea0c374dd004c0133688cf7efc2f3b34c70d71841de7 02fdb029e93603582f030b99b32bb2d03ca06ef5c22f1f9d3bd1fd9c0e35fa2b b05461e056f0d290def2925ba964d9ab5b6a1ffc104f242b775ff7428c861372 ff320e86cf223c62d586dde6f247ba51989755d5a2a1a2cbec1b09c6ee342290 561946eb6c7205b4c1712572d965fac16412c6ab547d2ee8f0d553d2bbc42f3f 4812da4f55032249f1ffe5596fd058ae5ed3be5eb2ad56834ead874abf74f34e b4de719368d99c3291e079d57d35aa0a7872a84285a8bbd6ed2b305edbfd5805 5b87f4aa40807647a18a5c26011b69573d2a9c94d4807d2870a7df0b37a6d22d cc0119c5eb561fb8e9f2c3ba64e492e7c5f39850bace9d240367b4f8bbea1f84 3427294f24722a491de9dd4e06109583da1f6cc3a76108d984b13a46a784fd071472dad7a7f551af093e91cd782e170534ea4b2e749b0d61b8d5389b8db69d0f0058efbc84fb9fdf5a7b4d04ffcbc4f603c2f86cb3bf538924934bd9eabf32065530f120e65471b6b243c249ace6795d86e208c66e4151cfcbb1566dbf0dd50e7990cc5bd62c4d1d2c40ae40730eb9ef45c53494aac3e08a41a10d4f830e3007895301b4fb9d40bda0607644488bb6bd4a36993baf9a58bf38f4f20c08e0bb0cb43a90e0c73381e5271946b18ac6008ad370f0d46550c5c61a80e6b0f71fe708ae0dac35d770895090762cc27f95e9dc91ee4ae055456bccbc531bff42c818060741891a0951d0a83294359b307b61c57496e1f74eb81a498c49a050d7c7dd02e68184640900314cd27d1d32c7f2e5f5534af9bf9b03a47db951d2c900ee310674b53574b2e649b8949657fb1a48ef1989359f49b6ad78b0a4afd8e73d49ca0e7d51890307b6315743ed6ecf4ea78bf24fb61c36c6387064604bf11b8668e60102c7c83f6aa8255e78ad81533c15e36dcb31a60d2e18143daf1380e79ade3e0fb701278bca1b044f1a78f136232972e08776d3c88ceb7195d27e9b1a77df0a0b24bb85ec2dd01cf7d1bf23f5cf01b9a8217f0fb6151e8cbbaaf5d2ee5026ac005c53c457741e2605afd34011e3fc1dc9978aa4ebb0443024199cf224276c030bfb96925f1c318727b2d683dc80c5e0daf9972444bebadafe4e5b89839aa7ac0ca0369f6f2003ce4257d621f01c712d443ddccc702976e53a36f2ced851a621056005a02597d69ac3af7edeb34a3e7691c1848d34013ad1f56dc998d2fd8ed40969946f592ca7ec84109ce1820a047e168ae30bac276f3ccf930c872c551d5d0759a76792b9cac906e782f4e3361cc435d132658d813ce3ec1d1b8b0dcb4c1c0da3bb060addd5e994438e7c4e68e13e15c02695165694123cdfb165c70b15fe0ed0a98b77edcfab7eaa8172f65bb493cba29fe2a7955bab30593b86ec6bd67a039f786fa299b6c931be73acb6bc9d64bbec875d5b3bce8e574b7d2b1ebcd09b0ce5b1a29ac06d6ca2a7567dca591282ffde67eed05a0e86f65dd16f5d3ff6cd08884e1cf8cd145c650f6d88eed97e5bd6ec28610b2b441d7a1931854e454d7501f613b338c2ad00133661bbc56b44ba25f3f8bcae2ca5621c4de4e7501ade850106c13c38b5af328a99361845a518fccd63854a9ee1b86d9dfecd1fcc20c17606637c4b7fd72b74539ccbb5b3d99821b5666b41a6f7b220d85f980e04e66da5061b4fa236dd6ecb5d5815182c71f080725ca628d16e5ac8a1be178174e037c300af54238003d0f3bec06e82d07a0536f01e15c7bc99f70d0ea1c99409312c1b08acbdbc537ae37928d1a45bcb261ed1d528705376b7027ef6d48c6abef776dba5465161a4b2aeaf6cb65f92ee97b58d295bceb9fe9cee78b97724a379a4ab9a08a8a3ee5aa0e1653f79f349300dbddf0306369114d4d98ba5f9d16c565b001800c16653785dfef2d14f824385453c2312e6e8b6d7c2c44127a77feb2f54f39d02161ad92df59a8b04b862b43a5061a9d6fbd15be4dfbf9244c8cc4fe25c6e080b6b8ceaf9c5b610194fb2d598263ec916e030ad271a8b470fc1a47da3ebfd3e0c52da293bdef558053f5d093f7ef0750e2cd68e5a286ab5663ee1a22bdb529803a9850ed493d623363cc2b5428f24306c0b22c650b4fd03261c1b0f0bd3151e01824e36a166449aa6002636f773be328f4f1f34723c12c83777b52de8abfb43023f19e807005740ee60b5c13f9111cb3fdebd3c09e6890bae623337b2c102ff044c161804e8a6014fe5c08d6b8a6122f3a03e1838361c6a9ac1733003d56cc307201826596c27f552a1048012568f928f03322ec3e560690fbae17a3ae9042a0cfa56d219157dba8385278d7f011d2b6086989ab2883703c5ce44af8a6650a00f82a68f9ec5dc545225ff14e290615160495f7f402ac232cf180cacceab4c4907f890b2bcb42e99f6261b6db32fa53f77972a3acd39c1913a78ecea8377fcb605e3e8a2d77d71de93efc308016fb4576d00b8c8f2330e1ca461bb565d3ab66e05e2864b150550bdaa39d380444f5dd528b2c5482123f58976d3d49ec018a331089cd4c217de4cb6149ced9a967fac7e1a7aa9037dd8b2587045e100ed563f000618c3a54b3be33ac163106afb0136d76397e3c0ab446e2c3bf21b7293d84ae100fda0f93e419acc74f58ff8f4514e5cf24b40c2e929d18058f980a69509ed630552d609ae233e7776ab5d08a4dbdac33876c7e6782f5a783424f7c4158c504609 false +check_ring_signature 4539a1f50cedaad1b0ea83df68795704eaa4d17f198c3ada6d0e606713b93302 9b04e3b71ff140b3f281acf8221d2746af509e272512a55b4b7a6496f8fcbace 2 ed3bb8e52e83c37220c46de8b51a4dd092b78a7710adb8b1c87f32f9daf152cb f73352b0a4ec371ff9c41f1a3dd50f837db59c0ded91a5d05ba549c2e4719c48 c3d4e7f24160219376edbeb3083febe56208b717dfdb64549de5877f09b50d0caa2e89aa9f316d97cb52d27bae86043d7ec190c75ca5d927133c4e79844ddd0fbb2345166228715c639515db47dc883db5f5545c18d96fc0aa1146b51d059804d3fd0080da36d4b5f20f75b13467dbb8e9d573dc487a71ba5c71b865dc1e1c5a false +check_ring_signature e37ecd6896dd3a829779027586f61590624f7f74fe75dcd1a0a9aabaf520ea6e debd521bd7270118468ef6dfa29982d386114878fa504d6df9c59c3a03c60871 13 8b39e030c80be92b8bfdd2e124cb3dc7790b322ba866fd67d3fc7ecb0b63f2d8 51344416df763fdff320495e93cc7f067ffb20a1e5a7a3c96dccbda27dff10a0 c7ecb7fa700a5bacd767ca4d320adb43bb2d5148c91e0ef3b76155fdec21b88e f1bcb8d94338d05a1cb7021c0d42dd40e1ffbfcec76f9f5a0ead4d4feb927de9 a55e1ff833901e5690651eb816b7f3c4ba5c47d0eee1501ac5bc0c6aa11d17b8 a275256c168c1c562f001edfaf13fffb405f02f4f7f677d895d8b8677b44a933 eb5ea15cebea04ff5cebb118542cda45e994498d0249d15425a97364ba4a3674 1bf76f7579ee39df711497d9761651cc10de966512b2f219d14073432d0a510c be64d2906735b0815235cb21c7724abbd318d9b60613ef8e2036126747a7978f e4e4d4b7a93a19fc89df55127b8328a3e81f0eb52360628b88d461fd458b1ce7 2b24fa5139740d87cf68bd3630d37ee525479cd477992d2df14817b352a20651 60fbce5dc92b73b3ab70b9d64da8bb9b501146b0de5777068028d39e5e2c577f 35ce3ebb79ebc9501a9c5620b4a1bebcf0d8c166211a527a9e629f72980b4def ea183f63cdaee672a97b41c21924b7e4fa84f92e684ce98b809de5b7da09bc0fd620a4561473748eec0dbf87aa70b25bac8718f109a2e7c60e5293332feacb013c154bdbd1c72486875e106b584faa7bc094de1eb09a373c9abb84c534b9b9064611cff52403b3bbc50d8eec03b2acfea37a79bb78c020770321c1936006a403333a7be352768bac62b47a77d7a05991053f7ece46ae58440ae2ed2eae98b40cee6c258fe3c2609b3fa93dc5909ea335e768df6beeb873d542f1ade1b8b3ab0461bcdb5fcc2519777b0b9aca51c1c19f627238a12e67e97e3a7892fdbd246009c0dc2ea660472c545447787310e4ca563248c53017d6c5c9fa7198cce41b5e035fde3878770cd4d427cc03cea7ed64c969b583bb0e14c23f7328bd92aba78d07f754bc99448254f01c7793d82ad1ef57061e8e65816ae6946ad052333a779b096670a2b726abefcb9dffbee5c058394db1f371cdfb0d422f5ca04de173fae10b6a9000504ae7bc58525560afe3a366ab7d2c831fea04e246b22cfc8bd76dac027d70bd89328ccffe8e1efd9f6cef84efbed7aca696f28b875a478b4df825a10bc351b4bc668181c71cba9dc0cbd4965babdb69ecaf66a57a40c341e28e3e28030d2ed83d0a9a1a093e0474ef3da00cf2a4c3bab38b182d517466b9cb64d08305fe8aab95901a60a2703474477987137d360d1e03a843d38e6498493e2bc60104736a17471be8ca487b57b7124d8f1a5a801a0fc3d2bb5230d0c46d8fbbe4280391735397b2739aca9e0241fb821665e808ceda01d53e6c0fc4b34e525b6a9d0fd96313fb9d9c8fbf3544cebb5b92b0b5b2879537f894e61a2658e6d74a1369065dc1ebca0f4775abee3fd42bf4514987cc18a3f7e85d461720519a908af7780e5a475f7fe15149adf66ab0851bb7e8165282827088253004d0febf422946c492d42a34116cb138b28828edeefef63cef62203967629e93749cdd733236b5f90ab7db9909a8698b842f1f250f21a71e1cbeba23e625789cd0dc7d5a0deba09d0287b2c113f07a195fbb90b02bc48bec7c31921c6f571a9a1dd33e9a6f0d6ce50ff994dbfaab00fb3b4cb4697984623971321d1fe95257253e56367f55d95e6002cce52c94ef44c57555177128c89d72451f6c616fb7740b18d296be9a21cb0800 false +check_ring_signature 015e6092cfba50f6f1df671d8e0263d69a64e5f86cdd14a694691853e5eda8fb 22a6d3316d8f95217d7b44ee6a23e20ed25971c2d0432936bf6a1c5f40f91d43 1 911c621253fbfe6ddf64aeb66804ab5e1929220563eabdc149aa8915b9972550 0c66bbd384cd56a7632f080929c93c4b42d1aaaf4d14719a5a23b52fa8f15d00c825164946ca7f13067f4191f64c9af49e052e2c41919ba4d4a7ebcf8523d00f true +check_ring_signature b14f9ec81f65a34a1a01893b8f0f010c0a829a7bd8a1b1b8c949f56ae6893b9e 830efce993afaac1eb1e3a847737e495cda09b6b50dcc99b1f72ca9ac803c0cb 1 8fd8c2d96a76d8ab98d04be6dc71560ca60b310bb21d7a3be9352fa50802f5a4 bcf995b81db40b8e8c25fad506f6af3abee5b42e96b86dc1e6852c8f1dec520f25fddbda7f69e51cceff791903d7c58f3ff8f0a4b1d47c2b2de1dbf472a1bf04 true +check_ring_signature d141b7c3b29a486d8c5b28f40d932f227ec309842a5193d7ffba103b6a1421fc c973249616b81652ba2628ccd7d12dfc3cb15df9b1b0b4a39d2651d22f39c9af 15 a4cf9bc720b8205e60f59d57b4b2e0c54e43f4e541be421d472a71806c6b00b1 69399cd01636dccaf6ff928be925fc6c4953399a44d48cec9a54c0114a3f6f28 0e46d4661a7f1b80e6cc031f7182ec931c8bc75dc9ad258eb86cf9c2e97539f8 a3707bfe3c48c2e48cbe0452b5e3f65fac32d55297d00a4ab02122073080e458 4efef59847e87f8b381dac46d40cd90c358d698e337dc407fd0d806ae62b6fbb 86f06275a9e21efac5ae8e8f7546a89882de561a5a5144032c56c61c1bbe4a9c 6ba4b0a911475f0c0b001ac6771bdca471c0bf6364f85cf0cc78532ffc6edfad a25a457d34f1ccb9f80a7ce3690b7c6e2d79a1ec12347c78ffe5294d19d3a48e 0a2290df61c57cf2fd51a8eb4d2de5c59193015e3cea02457ba783b6486e77a1 b5b75b8fe60cc952a2c7ef9b055875d21fb146147236881e0cd5c6197ef528b7 7a41e06beb55c6ae595ad9ae38441dc440da554cd3c795f40c96c96ed63a748f e1684450930dfee42ba975f9afecb39f49f5be13f4435f9495f40e6ddd600985 5622f02fcc4740f3c10a8c876940e6761ee6516e9c5cc24b7765e37bd2e2ee62 8321154873134316c85a50228ba505e6c5101839a88c9df0cdf768855ecf0766 4da504e8437e93c9a2e3976b2a605434ab4f159b4d7c2bdbb45016d8fa410d39 94ee69327364fffe9f620fd50e50c692ad9f2e7d68c1ec397d887faff4aaa603500ef35e9922e2172b987b721fc1aef7ff0cb0d152e0f5aa8f837bbfc5666f00f62c900d01ae2a2b7196ed2e37f9ccc3707f7958cc533a944d98950e821ca300c1ec79ec5166fb7e240ee7c2e167bd6eba7b1f6a6d0ed3a1a616c9aaa5705e0bacadf5746c73fadff1668d2c649b116c80fbaee2cb918d768f10beb1fbbc7c0fd839253795dbbc17ade80512bb7a95ba2191466ad59e290fa7f5b321ec4c8303c1aa1144999f6a7eda50e463a7c4653f41b95c1201fb0302de390f231fc3940527c070f1a793947bfb7856598a3fcb6ac951ae2f0e5e4bd9885c34f04586cd0cc362ce258ce73e72ee8595085a9079992f0a64046c3d7a258255d5671208790faf97934b6842d642299c6c3f66fe4491e73ca3bba3c873b6cbfd10b1e93cce05b58dc3b55cf28edcb08eeb2b90da74e85056bd2c3a7019e59fce8450e4586e06106f13aa26a4f1680639d16928df62c185919a2ebcc8771153a879572e2d89064e0ffac99698b59249e4ea6904e5cc8ecd0bf9e669af210cc28a0c875e328706c16cbc8c2a466ee75f87ef3e764f646bef5f2b2c4b334e39adaea393ee54080b72894a66ae5f87d3a43f91613866830393262b0fd47f18bc3e5b99914c0c7a06b2b63e12da2f0439e5f1dde4007d6c34c567bc7f15a3e0580490aaa6e95de700d6d10997213f1b6bd4e5e486d236b9f54e081fd7e69d20454972d18187c6b10d9c5dde5f2bd5ee95899205344bd5b42b9b50f0747b772dcf8309f19016c79b0fd9d6882aba8adfada1d66140a29024ed038930bb2c91f31f0ae8f6f7e1598e0ac6c8f5d6ed16fb44dca3be7703485619141b4ce588f15d07641b092c4489d90062cb0e0ec7d72628fc38a2d87aa321c616211baa7be89d1103a4a3f613313a0520f33124497c44d4dc72666ccd540f15c0a759bff1ad07fd70282f7dcca074011dcf45afea2bc5c1be00a143e1648b67b1657667354dc5d096aadde08d0fd7054c56b30c0c6836c52fc6c454ab5d82e97036b94d2bb66843301d95fcd625a203431394c83d4d62e1509a60f4c8e1f2dc24f9c842ec895d20b4e71952da4ebc002405ea5afac727289b95812d5d67f61e96e0a7d4162c2a3d77f65f1320c6b406e8e87f425cbf85f30d7064afc5b54f598346ee1e16002a59512d43ef2eb869010dec6a342b56092ff42c11cfbc8ac196cbd6bf70b8612411d4f69d7a6594350982755c4d0b92df2f88feece8ec1d69ab5f678486dc5ca716b236e155c849530c0e8043c3e06b16c79e79ecfe2af25592dd5a04e91edaf1cfd4df3d2c4e42390b false +check_ring_signature 99a09dcd98185684cdd62d88a2497f34a889aaa4d1c1aa5d3b7bbe2b4051c011 20069f9a37a27e2c0f6b0948fe50b43e9de9ff44b4c8a66f3be480135543e871 8 2c7ab30dda42d5c364eb0166c0cbf6c498e0928d53bc2017c89e0bc637aa99cc fd847e31df02b43172e26db94d5d6902125db0c0b7e200fc23205fdd900ed41b 4a1d699918dd4a30143b208787ed3604180437a338c0d383fff02ed8335932dc 2b36f64bc27cd22496aa9f92997d8be7fde3ebce5c62bc7b35a8d7f41a3e343d 8d79efa896c6c41872a8cc05966bd8c398a461bcb5a914d0de40aab621885244 df97d740dfe8d7ec94d1f575765eb92b0c6195939695eb5cae342b785f2cb660 c3dfad6d86cc77e0c0c7543421d2ba034752c18c712440206cc13e8bb9e573c4 65bf9846f4f9c8a130712a0e1531408c38417323fc4262ec21d01a2cb7e36b6b bc028993d25907c62f083d2a10df79aea772cc87053ad122765470180ad2dd0064cc586ed9927af33e60d6326d62ce54ceee041ee62009e84980477f00f77505a6e8a61e341e6702473c9ec8e83c93ae139e192441f61b61ea0986eafe0f610e4706715d714a35b51e4886b080f9a8c1bdb117f6c61ea0c4ba0ecf1d6764830a451769c440fd25e8cd818cca76c8cf1fce080f07fca13811634f213b8f2e5702a3b74bd0b965722e27971ab8653afc2a07d1ee108e796fd5b078acb3ce137c0152a46d2fbfaab994fdea8da7e8387aab949d05b3ecb57efb1fef4fd90ae8f607694d4f6974f6a69142d7635cf168c2e841fe61f693488c38cf89fd3589c70c03ea6fcb989937fa71e3b6a7caaecc2ce7f9777a6cbd755f79e32b7bf1a1fdc800d83aedd5158920b10383d56b2f2a1b00ae6570ac16a4470ae23c65e8485d2a0b0234751f6be5e2d3ed7849d7cfb3b3bb5b5552b435384c875a8f469ece621806956123163d1bc4313c88f0f8bfa9934f1d01402dc97db84805a7eb33ff0e87013896410002bae2c20e2ea40f121c5e67105cc090a264691221ed1bd0dd2af604951f632ce9c6d61659575f4d1a3a5528dcf9a9d7a39702de7f0b34c00191b60cc733df2b8580895167a5663bbaeac3241c0ff141ef1b53af7aa6881d9e59e90c6efa4147a3e159c3b267694efbabe6047408544399c80f5768f1baf3ef911d02 true +check_ring_signature 65af88e29311e3eda158e22c2b353e16e346b7f91e6cc8f933446dc6bf523c85 ba36ae87a5f944d52f1fdc2b7ef2bfb7c9b410f7c10791f58a7f6b1d6fbd752c 218 f0236e00b411bbd93fbb735516ddcdff4b0c90b60a83a78a67246abd83c4aa5d d9cf86cb8736d0c798d6ef59c27ca8206b6300cf34ecd02590a7ed59bc9f239d 718d671e608fcaf8ab15f11d9a7847dd13c5435981cc937d2597b079914d7183 06a6e6a95722de4158ced87d8449a182dca7ba036041292e2214b1c4cc9818b7 f959b691037b90e03c1a3846e65367234b668367aae8a42eb8c398720de350a4 790bee0d87fedc7c5f8301db4f7d7c679ed352460d6ec2e513672ddfe35384dc b01794177bb6158082719630bdbe78d974b677dd1698bc72e3d5e50ac42f92a9 31c138db4a7962e7fa1299d68e4a7270f87c0bc1ed69f2691a7bbc4ac1925125 f6be71859ee0333c1963174441410348a182bd5540bcd8ead3fe83045193a93c 4b5c68ddcafc65b106f6a84a8f903e48813cbe7fe936467f5bde38b62caf810b cd81ae3af6cf34a7b39f056a9ebcb108d55eb2625313b1a4a931f972c5ff3873 2aad27846d0a23863ca39236bbed121d30430ed1795ab295263ac281f387fa9a b9350decc5c429563c8583dd3cdfaae7b63335d886edc4d3431c7f098b306cbc 520800e1fc6dbdf5810e5faf3b0d19ab289bc780e38995c473ba84538fc6b76b 5c107058d8bc79e49fe47b63671ff5741501da91f20328f5c715cbce0cd8c2bb 86c48f01db584aa07d3c978347b2c79c588d5d1d10033c9fa9c41fea2b4e9ebb 6431980630d6a396afacdd3dde6412d2aa15e60b4abc5a572665e173e09d5c4a 68a5f0c92f187c8c46d1197e2ff5a8e8a3a145257da13a967f9308b203af9e0c db9b7a416088464889ee442998c8e4888dd401817869d4836e68893277e8e3b0 ac1938769c89ab8f3b392198cbd9ded315fcea0d51ed1c27f8e37d174a2ae519 40f37ce3671eb1ffbcfef35dbf23372c2ccbdca5fc5bbdf40798a535fdaed637 392efba070e6cbdd3c11f203de36f81d7485c96920de2167ac43ab06f6ea2475 051815bc2645c7980a68a8bab1b92f76a4a25473c10eed6e80c55eb45ecbc716 515353d94f4d9811450f84d127bd0c60636e3f95d9317b5ce251236cde8e4b9a b689886b8c5207e38169bf4a56d9e1ab3af69f65af094926470fde41170337c9 274960d197aa5026189508856daec5e31f576311d8e01243067a1db7254d6e77 9a5861e137eb709f9819b9c7140ab5a4a6420750f5b2e62731865b51df898341 2eb47f472fe4e04af785f73339eb7ffe397d82e1d056713a15e2c148e41a1e97 703b76e58d509c3043f819ea2505ae52360d929f7b5a12a7f6bf9d64b1cbed6d a498bf4fd9242093bc9abcba913ad5d031c1ea06e616d04374adf809b2526e6e ce263fd9fd5f75a67e790795fffa59590736719d2d6d393e1d5667b2f733102f 37a7414201170daf834a4bb6942789f2cfa01c5d833dd7a24ace78016e11a3fd 0a964b27c1a2b67b4ef867ad16ff844c2170cd2ed409e391206632c5db53432a 06a39c221c13f5dec003c90618ead3df1e0841eeac7190d06f21e9b49e18b4a0 727aa238d614cbbcad6e766e95870b4b4ded840721d1d5636d1f4b60d39f1270 dc67f69143fec54b8bbdeb8943f810c92088949adb24b482d9848755cefeba17 c9339423fd87aceadb6435a05d7cce38c64f086970d8bd206209b831c950e9a5 917cd0d523ee385b36f3e0e57734cef7d8676e38bcd600d3457854ef58e743ae e0685c502818a05693d95728ccd1ee6b2ccf358b29ad304dc2ada9c7356e34d3 898a328229d2574ef41931a41fa5d3435d556b465bfc25e88d315555c7cea729 ddc860ebcf0f8b6e3157fcbf2729e6bf08a244b6be50360089f719b93374e36b b0a48491dc68eaf89d6254dc28be8c591e8aaead7a2ff76a552f6bab281930dd 5c2a0850e791d057fd9fa81adf680c798b93830173d3be1f30df983332129f00 f9ab03fd7551927388c64ff53eaf35db75510f8b7aa8ef13dc9eda3db586047b dbbc99a4774b90cb337a97218ce59372fe60a5a8aa8992662bfe9e80354f66d7 999ca8c579348dd1738c1c51fe6d60778996491be1fd8ae8bca347a8cc4f4a69 e8bf3de474020bf14f48630e75f216b2473ea9591fe9996b20e6dfd819f3a7c2 2e8a3538f7cfdd9168de57d735f35fbddfcd22bbf707c2eb10943437eeac4af1 25e9db4982b3ef12187cc5e7963a0213e5255def6d5faf751b17ac740e76cde2 891dda99ab3ea58d81e037668476c7dedc4ecb9ced829ddd271544a898b04524 ed45740606419dd373c6a22c78b8eb1a317120671dd4b8d9fec5e6cb07fe188a 04b1f68bbf66007c6c9bfaa835eaddd1471c06a42def82e577c5592bb45a56e9 a1af9f7e13ef69005577e2099b2dfe83558c4a1d68e3dc7ff3253749d6d5fda3 554b4894e8f4b7cdd08b81b2f865593903b1e63f7a5400e6608c0b2e183a7640 05705ec2b01fd8bcf49bf553e75374b8c8d49485267c1a8bc83865d4a48b8921 ebcdbed0779a8283c993a3f4b1c692765269710697f7ee57ee3e940382104b5d b0ea1f49941895341bdc03697f955c6d0ea7717a1d289eb5028917c344a0ba3e 1732aff19ccf40978b770afefc06faea0f5fb455470e9660f04214948bed7fc1 c94d7489bbbd95371c1751e992066068679ec6b5de20c89163d9881b323e9359 b257a635195e4b62f27d6b204e71a46d1f54441b9fe3381c93a22b32d925181f 55f111628d0b61a8cd5ba5a39790441dbc0cf3ec9088e185b1a027afe49e2450 fe632ccde48ce64079a7cc28f0cda303109baa41df00433992ebd5aab4451dd6 c382899e8569b5341b13d121c3559f339f35c3ac1a5bd51f14d0b9b403b6c945 0b1f52f9ed1079cf412ff7610add2448bb0d4fca47a82d8edd24cafedfe7b725 12a480adf33c475c49c04c45eaae21710d0a1fd974f3c462c27081e61ec77d24 98511b315af1ccd5835935be13a13d17d96cef2c46f6b7a8e957419cfd7a38de 89bd6895c9e0f3fa96c46f94639e18bd5cee857d997fc667f0bb0d39b710d025 9e91aedb3777623b656d483840940fd352271305bfe77313cd1502158bd4e3b2 2719a01d2de1166b13eea6dec826477278f6752786c2e24345ec7d9d560d9ab6 bb9a4e0fa51585c9ae141e16ff97cee81595e507620134f40da37e5aeb43ddbf f84eda73ae2810b574d6006595d6471c44c6b377a1247e6b22a6bc335a369888 939fa7f5fdc49b3cfee240acbd62fb721446b63962ef24a2b593608503c6d89f 300311ee4c9e7e0910a4474943b2e7e2ee99d1dee5e6790e97028fdcc2ce12bd 23127c008e6650c5a25215d02ddcf88bd228b446d2a35a5c5e8ad2dfc4c79757 b42fade0eb2733c057d0f1f1425028814d7286f0c72cf08063ca0339b255cf6b 7806dd285075a12f66559c55d3d113a6697b2a8e91d932d7e14f59e0906eca6a d1476f2058e0b99fb564fb4bbc7491c758241829a6fa200cc80708a6f4859627 a5f1e75ecd9653973fd307bc2116db19fb035db19d55f5070a965701aafb0c8b 59ed248f242ef579c4eda148c193b0c6a6e7870edc47abbd1b43c58251dab4aa a2dd21cfff06d9d4f34dce864914611a8b678293fb514baa9eb104bf7ae79918 6992f3f59ff785e4b1b54f74a6fff801f1d0c75ec8cf9aa362bf5a1e7488df87 6fe77ca9d1d640691dd7bb84ade145e04e711543af2212cbce17dbf1f42d4502 4f0f309b7e23802b936a88fec766c1f1607b8db71d837316e82bd95e59a3c06c c056d9e9417d9420a9bf5f731c57871d498e57071bc2161d1391cdfd67e935b5 f46f06fe2e532132e9be3e902b24db4983f319ab6eaf85297b65108bc588f9d5 fda47a9027317d6a5ebbdd1dd9de9c2548333b32daf084f1f79d0cd6d5493978 75bda810dc41275c80fd8589d0638ba6a322360bf49365b89cd912d7fafccc17 25ebaf59b6d498301273f7800975b89e4b5312427278423697e2d5b98526359d 57a222a0cd652f39168c484ee6c97be1f98e2b8823d8a042d07abfc80f8f00b7 1e60d0efa9eb760f5a4f08995b0d538904e48d039c15e88714d691482d5159e9 3d952a901eac525bc93e8835f12521577b73850f9337e349d4054a6c38ca0bfa 04cf5be4ff63e929bbf4cc6765cc995ccbc1b82c1032559704d267982d7feac4 ab2dff33d5aac55d639d47dfd9e7d90648f99236b76dfbb6b6b78a7c8641cf62 725dbf8aba03724e5d7822c1cc51836eaaa65904e735dc7fd99a71ab0d66d783 ca28337bfc9f7790f6fe622f5d2415cbc129cce81348004286d440c0b8aa7961 629ef33046341e92a45deb64f537a1f9c727dc3932e211b431386895b446cb96 10c80f5f65752723e7636e0f971a4f899fc91e50e8ec1eb570e9932b465227cb 05edd167e39280b37a8fd0f0e9bfa6e21a2cc0f31c3f3104329497f9fdc51268 8d094526752c0361fee1c1f7400bcec752aac64a96882a3907abd9a52c6986a4 f4e80a8f13b9521c47606973caa5f82ad186e7ae773b72e21adee74055577d52 393e0de80923c34a3d791ad5bc70a89f028280a0ca83a17cb72c17410466fd3b 72d8ccf369af99df54bc69ec571176c3a5d4c31e98306f3dcdfdffa0306a385f e186ceeccc50afd813100a5c4609cc0ff89bb4003ffb29abc4567301cc91ee98 46efe05df86bf4ae85645f3c8110e4a7eca408ad67a6d526aa65e86dbe4e8531 6e3b76eab45fcd876bca5cc4f5b85a3c2ab474fc6d0f176d11864d37ad8f46a1 88215c1427453dc8690a3cd8561c7f84e07ce46f3aab2cdaeb1356f134bd7cf7 8b7f4c177d017890ff454b5e0cd480cffb94d07fa8693bbc4a783a5bd7d9a1b0 6560ae7e4cb0f6b2f647ab457e7af445947122bf561ba8282fab84f8588a9bf8 d01cc061974f2e9b9d5a011c1e205630d3ca105e9395314d201920105e60d2c7 c035e09f8516f0efca30e9a08dc4439e4d90f3ac1737c799e345a532030335f8 62c5a6b818cc3ea24e60cb5871c8f753109af68832deec03ddfb473723dc1f04 afdb6304ee76c498b5f1dec116a649d2206c73fe3fb3951fc1482e6b81433800 ff75f6346982159ed0112107f45fc551df4907c41a61bdfb250c9913f6b85570 2d029132064f0e98687b7dfe0fc336ea8b04b53a67a75645a8001c4d178f1984 b3196ba02b8c2e51d1cb3743449203695c536a891ee3118ff739fdad2bf9c42e 2b26b69b115a0bdece9f6c0d668435a9dec2446dc5612b2c6ce5ff1c2f4ef7c2 ff41598d77e1aa0b8509d015ba15dd3fd56ef0adeea3fd6ccdf59675ed6fadf6 ace20342dd2ee074e6b42aa7430d19d0c2bf68311acf0f45701009a47bdaf3da bba9133c8eeb507485b902bfae5abafd4cf628d37f6ad7d660e65302dc8ea5bf 7347e14299b27ac0b9dd642afd1686fe4439e0a15f480e14a4ffa3d91edddc78 f63879fe18caa8d87edf727287c77d84ef552abfe1656fbc2280fb7dd534d8b1 884013da92c928b9886e27a5549ad006999cbe369f0b0320b7c0ea5498d359f2 7644dfdc87fbf5647f218f0b72539027945e416535f3a425d111ba4f1acd86a3 99f00f91f6f76c8a34fdbbcb190b276daee6e9ce510438137a6ea3a59b785c58 8bf5cab3fab375726bb89505a8876112a18746d4670bf732f9e0f47b03f776d1 a6a315689ab8841a055cab3ba399a1cd7c48d171df7c8db959f546fd62f36351 e0d3222087ddc61c6685b09ccc9b9f64243ffec458e5dd77562703c82e02dcff 9c129d668a360493d80b189217802f36aeba35f0fbef35904b191ac1c15172b8 270fedad99aa24ea506a0d9d869551b8289841c2bbd1f9aecf77269d1b201d4a 93dd39beeac02af08823dbb8e3a31d2d4220d8a78b258122cff93848caa80e90 185e5a2390a9998267fca74573167dcdeacd75098abbc3708c5a19a44b5dfa80 aacc1dc66218bc8c0a12f44750231f6439fedbe7ee5b40039edd8747d274f6ee de8dea7f4e1a0922d9263ad339a950d3cbd34716522094c33487ed3e53ed9d92 ed4b402dc95b8b26596c19ebd3ca7ffeef13a9b710b38cd9634dd68facd1d543 08ff6a06c05a1b024d6d9515205ec6b0711d085e474466b93da48a9f9359e91a 0c33ee0255454f8a4e2493a8827332eb8e68ff7e0ead97f39b57cac61fde30df 4810c28ff5babce9460841eb1a7c5cd4dd7e63f9b58f7615bac5598f5bdcf4da 2e6d71dd36a361a6f3b4538f400bbba84fd2a064b3537e941b3fdbc4f3485fa4 60d9538079477f5bf7e78024b9997d7ee1c2bf551005f3391cbebc62c588d699 0216b86b372df8af86faa7e0b10e88cdae0bf42a2931d0e22853735adb1c0fad 28520502df36cdcf5719fd43940889f431ed368bd3136a394b4bf39fe3181840 9ff89c42cfca0acdc36288a66e9c7bcbea6c4a28a13fab0defb08427cc92efc7 dfd5e32f888d151c5fcf41d01cf2b603b5594820491aad1b76887db2a8be621e d52618da2b55a0d56225a06441ab170d37362ddddd8741d898bfc23d8d8e4259 6d829e1a7f634a12072765e12535c7d9eb2db89ec9c5057495c602f7a86a3bb6 7bff51f2f5857053a514454f83cff09e7a047a59a5f545a24771bd4b9ea78ed5 2a31c246ea873c76b22afd992caa61ebc5eb156f57ce145b8f319d8cc7b50b40 27f4f66105227d4232fe0f0435d1942078b6a81c97716f269a4e145ecc303f15 5afbb35cc42f214dd1e8ae7c565d165ff4f9ab314eb77539c3907583b80cb9b9 ecc3e6b51ab881a76cc2daea37c64d30f38f0aa44d70d3a167204e6fa14668c8 a4e9a8884c3dea66aece37fb7e2fe72938112e883d9db6b4f762784cbb114152 fe26ff991cd412de8a3978040b3adf7be2d9b5e36d9407db341525c9bf2f896a ca907d720c40f2b065607aea894484643e9a837071bb9af1e100d71827f89535 0a50f298a4039aa80afeb07c87032285ecc9b2e3134b7c2b8891be923ccd1f7a 516cf603de724a7cf5e36fd3259b5a655f7c7d662b7f7762a77ec96144b0a447 fec5b384ddd0c1e8ee81d2eab55e65799dee1f4ba8b6697fe7b99977817061f8 36bc3141b269fe98e63aeb4d893396159983896820b97f440b085d58f91d7be3 7547d610005996e7137ced68a0dd210f8354b38bbb7b75d0b9c7dac12a80f9e5 06cd5451f7c2e5222c2901ad56f1a04956d4ab138df1633487adf085f5d0efc8 80c64f4ce99addcc07f576b8d9f762829cb7ed859c5d178c7f7f495c4b0febaa d20cc6ea2c85e99e43ea8265f33ad1dc2b2fe3c40bfafb73efe128b0b709e70d a8e87ab14e2f1bb9fcdb79e21f05d9a53ec17fe8be7ecad06bf1348a15e2e4c6 a805da430ef2a2e036768245cf2b0d347a9c733845aa024a3623adba5b26a333 cc8768bbde104c7eed96e421833f29618050f60ae561061f2a0eb86faceebd04 e12ef24e876f0eec5093a7d2c9c1a8a00ff503c8c133694911b7e3fb1b57730b 2169781a3baa8289838cf6779e8c93ade08486a296aa2c99a126705f077a84a6 981d13dd252dcf2b9f4012e2beaeae1c8f960e2991d851c0770376e0f613774e 76b7bfdd79c26e4f844338672ebe44b55b513290b0e6f47a7b09380b7fa5d633 d88db6683330063500c7fdb1b08f2b3c73fc894afc7d8af8a4252a202e62ead6 be01b377bc290816e9ad1b9f4a811a778a76b2c8dfbd6a0b766ff87e57568144 94ee7234aa0fc4291147999939d001b7bdf2b32338e43b27c7e4a8c61e7290c7 d9c5808a0308708aa253fb70e9aba6d62aa33c8327b862786516f89b7861d20e 56a3f525cc3f01240df6e3abfd2b6922a525441d8808823496c533388e4b20e9 eec419c0c2c2958e6266817dc648c3f494ef85abad5d72a7f5c48d2a2f6c882c 731a11db607ee5b0ce3773be591c100f70d83f2704143d70a01705ecf1189d84 3f0c1eeb85af1304b523dfb5ac16a222d5d1dfb4ab3f5987a7cc8652fe556ac4 3fc98f0c5c48f1826fce53836c3d88cb2dd387293c7269f4d4f50f3f157d8217 543343a615f2abdebb8e068da6934f142fd52dbb24f2db03209510a54aac13a9 7f9f6a0283fd537a9b54522ef36f429267ad86dcc634dbc4c7d31dbaef585201 d8df7224b937d87fc57355c505e571b3675d8a63e0d9aaa87d4b582aba0325fd 12c2b2f6560aa5da18d11aeada73db8b4fef36519cd3a9da823fbf5b5c87c821 343b2ee91544cab1b70d1664c5c8f8a42db648417bbecb2f3ba58610968255f7 0956e949348847f760f4a995caa75f3c5f5b9a6c3048775b1c654ad32681724c a7d6430bfcdc237973885d87a248be31737a99603d2e14793fd6c8ef453b8412 e1b32389d6114f75e93e3e74529b71b432d86b2729e379f12e500b0ef4da195f b9c4e128a106a46dfb46764d91aa7ed45cc75a340e61fa04cc0e8e2bb85fcfbf 5819b99b21d4c3889cbfbb792dbae674591bc0dafb7ed2ce6e3658866b197a61 dd785ea04bf3c6a5d8271f37f4bebe98d7d49cab0edd41470782000540c1e91e b9a71f259dc99abd83b82ba91d771cbc06e1af5e8fbd9f23731e5825bbd017e0 1e7eb1c21e129af60ee5eda3fedbd12577e9b4450e0e604f16620efb6359dd0b 013a6a19d22d3ea4ca67f412858248f6a17a9ed3a5e02de098bd58611583a914 0e8f4b9476665c2e0ca776eb7d7993ce4d362ac1b1c19eb591f14ee799c28ae0 0410f188db7feba02ed68e4ead797c0e6edb7c0d3c04bfec65ad3f979064a0dc 974cd29135bf92e3a90440949ef66effef99c46cfa94902d58a08172be17e2f3 f547abf2a1a0f575096d5bab582fe724660cdb831bc5bca2293fd9eab8e3dddc 6f6a289f93df1700004fea8319b5b4bdf1831bf7ea550db645e395f436e5c22b ebb919e336838fdaa25876f2f54d85abb4bcbe438aace2d8feac78955b13206a 7bcd6b8ddb118c657ad9f2b21ec762ad3900ad4eb6fdd2375783d588ffa806e3 847c7ded2c6b2475d28d1f52616f491f9d9dd1f3e2376ab49cd791d6b13c1bf1 85a2ef503d010e7a400d312234e208abbc40ba3678c3ab5233a220863a96229b 360ee847970a32ab12774dcb0cf85eb0b3c61c88afe838462b473dec5b591f3b f7c4622f349b5a468abdf2ff9f63aed9232d13f8811b89c858dc99f2391bed1f cd09266226718e7608ab6514030a070e94872fdab76ca9b685448af3b73e4feb ade20c5a7cad15ea997fe041c4745a4290fc333dd4624074116a1689aae60943 abdcb9ad79ecf145b96e70660adf45c415943c5435ea32f257c5f681d17e3052 54082efa6c5532739aa082866159f2ff44285bfd33b09141b4c0430ac85b7139 e595b7d6c64169619bc7041c5027f67d3656bee856aba03227eded76b854d20a c127214822287cad72af07970c69996feda970dabbeaedf3c0ebafc99295641b 9f15c19de1808af2ec32681c20f111f603f2522a20f0e228178df88956a9c1f9 d2e48b8a8d4082f5fc2908c34d20389bfa875fd2bdf54f2663eacb015a69fc7b bde11d58567b5e025610b5356ee54b40cf457043f33459239e849cdce63838d6 b138e026fb6c1817be6050753a52a9adbe204d0f62454a43af823f585882fabe 6ea44fcb24556e120b71845425cbfd2c87b27d6cef9221b10e7e9862b03dcc5e 58aece161c35692d8bbed789b49b84d81ad525b86c57f57f875e1c6baf668aca 92016f27a4931ba3e97c8ddeba857c5b30de1bb6e4a6a4ed46ec8186ae13a619 758914040509cf7a9c37f4b9b62f50823cdb7d2ea318a90ab45877dc111ef125 e4c720ff0dfb5f9dbbf4209beea5eaaed4078e62378e8285b2a47f4add8c2750 d541029a00ef0b007e9e2717d31455bae3ab97270627778d1f1319b28ae53c49 131002dd236dafa4961e14e0b24fc243d1ddfa57127fb2e5394d91462bb7e50853a94ace3d8be2b83411fe240ca82a83dca0888d131938a06f898d275c9eda09c214b6394e3440338fc16ebb556bd037b4199fdb5ab3906793fe07fa4e34a601abc222c304560b8de4023b5c0b0799ab60e65b696f245f6f9540c5fa5e0ed3045a07a15e3fa8fbae6112402b6593ce7cce950e53a061610b8ac35ab16186da04ed44a2abdea0d8f33e56a3b8568cd243c097ee994fcc0b1b8ad3247f94bbf107bf2ec950154b20495b0177ccabcc79473975e891463b219221b96894709b810da274a516aabc00ebd50bb2f44bf09b8cb5066dbbb9faa8deb6926915e8611a021b2bbfde9c1560aad048b4e21cb69e0ca99f2c33c11a39a5f49dfd865af44901131499ca8d0c0040268e613aa8a90230c09b470850c8d321a8077c5bb9e1af039bd71e50682849d79748d5ecaca95043c6a82e81a9a3ddcf271ace1195bebd0b531c9329c308ceb8cdaac569945c4d13fb5d263d3b83868a033dd45fb9339005d1fd40b0f75fc8f21beacd39f86a70cd16fb6b0a931c8b9a5563f01daee72d051855d133bbafb6cfbb943f2442608bfa5637f466a9e04481060afaade730ea022e4b22d646c3c147c64708fefa2222ecb0356e0abf837a67ca875cadb4963509520ba9df9d51f04731e7778043028719a6c15fa7534945f242f633feddbb9b084e0faf3304b588c70273c3b507a799706d733522240e2c96cef38d4e65d6c40bb972b565ae0d39e8b2e4302e06197b2c072db73ad3253e0810b5178b0c7fec016229921413dff6d02bd0545fb7ff992495c11f3c02f413ba00e34a3d291721087c9c33f7aed1a9a649f3b23fc42d457c87aca3b6616c61b48800a9112af19c0fce422b5ba4e15f39006f128db86f95b4f55d3b9759c1434bfe552e1e5d614a0beb0332d7f42998877b8cd2df24ca0f47e22ec5c055bfb62e751fdb614f17120dfd7418313f0bb1c95362bbbf326aade7c0b20dc6a1d5440a28cf83056df4ab03dbd19cb49743c859996e9d2493a87bc765fce78296d9a07b53515238f0d0e70f8baaa99b1f64f606a9b19323d480a75c79cbe462beb35d7f12267c533a87110aa5a54fdd034221a79b8a6757035a65d39374312b1e9aab86972e7fe165324907030bdf826d8ea856068e42201f044f9584e9f366eda08bb192cda5aa11b7e200cda74b19ee612f6919c5a25f45380ead3bd529b33fbee77b101c92cc481f0e02f96cee142293d7b3081e2761675b35f51d3d87e1bdb242241dae91d07e2b39046e1c782961ca31dabfff7ec787466aba426050a77cf9c20c896efc9881de48093c67e882da8af3f1d06a89b80ba7ef0aab4df107fb773f3902ed9ed0a1d10201682de3305f08dca3dedabfc047a7b70c456e752786e854def881dd947c5c070c07aa186c0d721e8f0183d46c2863430ee15d86ecab089dd4823e3ab220e498000d9393822210b6ef89ac28c5218c5c9bdfe7e69068006e18788c7f02892f8d0f847e83000dd108e9ccad25591845f5ccdbbbc82007b76a680fb9d51c28577c08df11ecd6a8b20c34afe36dba5b4e994c75fa8201998d06fb4cc5783805b38b015273524587d2000cb0a2c1cc013aef5fe10bdfa43dffeeec439c3b51df557a0ec1587802b82ffc3bb306944665cf712b720c210b0e0b49d57378531ef0c464083951262c37f364f87a0c7576236b5ebe36023033534aa1dfdf291eb0ac4dc707562d0f36d1d5edef25fbaea2b4396dca57173e8e641b35f4608c1349964a6504504f6e86cf159bcc94cded72c12ca1e88371eb16b113c89c1a04cbb367f290041f153caf0626fa6a6cc54a0480c085a09d2533381c5d5bad32b2bb3b4b96640b87caa1e6145e7fdabe39c3e64092b0d7778b1e2049619a598c442f81aec59f0cb81e3bfcd07df723368ecc976b31460e2af6330466f14de04f9aba2ccba2780bdcc0cb8550cc4bcf23b29744372d3cfb38697d5a5fe01e83186edbe66cf32c05949de756f733b020ed52683d8229aa1b60f17bc838ce33f344b9283e9445170f0d8e3472474a41f2ba0d9efe19db1016d3b54cf8353e9e0731ae92c86731400cbe4438ac0959e86d6a5721154ea572b65f4afd4258d0e1c8c614f55e20660402b4aada9934630e162959f50493a1f4ba8baac249f5394d0ed16e149923bb0701be4c17e5f451c493233afa1f81f2872716a94bb12b223fce6c86568072e544026024541e25acad5821ef072cfad13f773907fa8d8a14e30fa684352e69362e078293fc513515e497676801acc1cc2c93ec8532b85134209a57390f1280348f090197056bc07009ba0971ae9c755220e6292a79f2cbd7be07d506dc1c21b56e0c0251c153b16150f158c04265b877fe6d814a41c9bb6c797d849244db2d288e00a9e84bc885af7002a7412238690911a73cbb5e44c067652cf7523d3901a6660ce65bc482672fea5bf7ba3344db78cd07f7060f95b47d5d0b14845830711cf2079bdf5e486b28a94878e8cb2f0bfb7cc9d5242f9fd198857917a8e90331768e0d023b37f04a3a0e070de818948efa2ae19e6f974d889f31116a0a4aaef48c7003f4ef755fd51eab11e6553d32e9178ccb196ac0fe30151bea001a7ee1dec905097a298d1e278fb68e88f4ead3e79598cdd69309191faa5ca8ddbc06a7de7d630eede5f7599b8a034ec51cf746e89b5828fbd2200cfa8b4773a33eff62e9129c0e640d84198b9effd04489363647755c6399604820cb926d500b60e4944943930041d3563530ee40008dff6d323ca548bb9a29af7671976b7f40c6203053ef1804f264799e96b0fb0476cf8bed36c86d916ca1e5536c32ebe5249ec8d42e2f3a0040c09dedd7231d49a6638be948f9bb26cc0be6df0375fe5adfd9c0f2b899af0ccaf2be9cc46a49c12b6c344f2ec4c68257f483ab1cc023a16a2dcb6158f4a20c6a185f34514ce62d17221cc2fcd2973cabcb7df5d8072905b242d3a32f075406aa7b88fa071de53698c166b3bee855d52b761f36ab8cfa9e01ad99dda6a91800e2e83156e0a6e910ca781ec185d418c6baafe031c059c5aca410aeb9d20dd002468f1bda89cf7b6ca00c3a4daa0833de80c70c68022a6b704840df836ba57a07292984b0ac315c3ef3dbd8e1d3f1920324d9b7688fda7b16670c014c1f07ff0879901bb3344687c617490e9dcd3969d4b163cff46b4ad893bee8badf6fd1f700f356fb8b60ec929f6a5ef89eb6da3e856d2a24edf7dc5fc205f43fbcaa643e02b2432e8abe315820b2ad342185c22ff8890563bfa71a05a3a8f9a008b06b9c02797144795dfc9e5589b0bea6ca0ea19f995e1c35c217b9d4e0a82058850d5305c63212611d7791e969d5b399fdb918b822bc904f9d9e8dd03a8d5c280fcda10262ae9a0dd676c93715025ae22b45900eef54823ff881293d4488d813158aab0438741a41a748a41c17486e97a72508755ea48564c6b369a22764c12248042002ecd5f4fbf3da596741087a6d1b7c8ec551cbaa301e432949e92c09fd96b05d0d34c0a2abfc2ba7020ebdcdce590d6384a7e2f18ddc4a12c07afe2e7b4c112c04a9601568d45fe80f425f9684f37e444b3ab41055f47c71a106768ec4d7b28a0e06dedc887c712a9f1d9f94e15b4e6f5c33fbdd550be4f7d5c7f98c629c654009c4191458658b37e0ddbbb247824a005d75e1a8511fa6e096f3f92a548636bd01d04b4b4bfdcf84ba9e580a431bd058fc7ecfc6c36eeabe3896ed139ee2303a011f510af65b7ea7ed4d7bc57341943bee98ce6ecf9dffdf81630afbe4d9854c056540701f3c5fcb8efaf0e11216ab71fd47705c61d0dda1629712c182d1be90091a22771bf6de487a8ee2ce140f86f4bc08ea90b96dd5694f98be1cccab6201095e3b801be9086d8d9f6f4a26dfdc97945d463e61d051df5055c9ac4cc393030d842856c18f3a6f2364212f1f049783415b01907d36d7694b7ef4cf1e039f800b6ed64fc5faac2636acfba57589430b5c8beb4c1da5830a9e21a0065973bc42050a6e3577f65bfe09286aaf10fe73608a3ffaa6791abc4879d171b6a94fd0570708a39bf40bf461a4d772ba0be39583f26f4ae1ded74caf799792ed445257cb0a0e3c4f63f0454245b7810ffa7263e101877e686cb5d81892eda3cf9a89c99b0a0658fd35131736137279535f69d730a38b19849b45fb52ad80ae2beddd7ef101843b97891d3b43547d4c7a8e3dc867191789dee144383328fa22fb00758ea903869bdbdfefe60020c8664e04f167016c529c23a724bf0f52824fab2e07ae2c0332dbc1df884f859ee7fdeb17406657ebe6135bbccdedb81b20fc184ac610b8038a55853590086dc03d94583e32cfd1f8b71bc1efafdac17d0e3a2a1892944b09c6707fab5b63147216ba66bed908ce7d05f7326c4947fd70d59cbc25bb351d051004025c0f0a8011b51aeaadf95e48f3b97080f31bb8b4b3912dea4115ba250dad2e10d8ed16e53db843633107988388b3b44758445807ba95423aeaa9601800a780d71c61a485359557bbe8be9df38b306ab0faf8bbfb8989682e4f2c60bb04e20200ebc59e7f032fa938130770e6cb29f1ba2a29958e8bb65f618e29c0d50273e2fc4a5e9298a179f1564af491778425c743608e93cb837c48e4510abc3c02a077834456d6b3b8bbf642e7606609436a6fdfb69bd4dc57b52fe371814e9f0498f6b255677e5ce9d68ced31a322b93a96b79acfdad8cb8e6cfc632cd7a61908cba7436582e99b009b8c3adef7521c2ec2c15969bb6dca2df9e744f4525f00089b7526c9a25d75b873c2eb0c1128a8489b01129c8119df151dde4994643eda0d4e83de9a51e24b287469ee683752b2b496db5a65158de4dafb41e32b29e8c103577d6059b0629f13b32c39044bbff5a4c7cf5bc8ad70c503b1aa2d875af6510035f2311527f50ed4dff49b50f98eaf6bba6fdaf0e2552b4573de3910c5e4140000c8ea8b01c2470eb300f0a8fe4558ea7aa7815b33052fbf627b2c0604222102f0e465f3f100ec8c022f56298683e3a8a3fb312c8e85631ce3535694ab56c10d6880f8a1b3d43796269d9c4acaa21f85d41948c29723943cac6272d6950f070cf549194d6aa84e57375e4ace2c457b5c5d9174ddb59b24e6e59ac1e6cebecc0a3efd6d57090bd40fda60beaa0392c93dcc4b0a5e0bc1f92e4ebf9159c5a8b4092b0163e9066f4be67f8e27b1d15173cbf820373d34bb960f23778ff4ac944407363d574f31b2c6c76e9a4c3d56541be9ce792efdf1f37907ac3184b2b8457200e1710f5e8ba8eb961327e2506591085dd8172a92fbdeee7b0b77a29290bdb50c5450d7ea196e11f492be17a3aeb328667394459fd0b3a5c25b7f2133f9164704e5dc98f7d2fc56dc661f916ee73828f4dba134935943fac2202a848dd73b8c0b8e3d0a48fa94c1901b147bc3b5d11d05e996c74db83a62c175908bcb52c0a0060d25b9e87662bc84d3a8eb8d3725840fbbce39a00ea303e77e39925a6702010110344aa536dd709f5ed008b46f4e66cbb07b97654c1aed064d28c035a52f5b06dfeba73411f77c2426d5512c2820d194859e806f406c1e82f0e3123da6d7080ad50375039fcd9373e04b54ce7b7d93e8403e4d574b2b27d5b9a5f736f584bb0c481603496f695e89ba1198577c8a22781a577eec9543d69da0168604c96f3c0e1ca188a31ebb743ffba19e99fbc490061c975cf5be2cc69a608a477c51f0b304b0c00ec8e1a879637e2db7235ff23549436a5d2328fc72c5e4865d96e4af8b032c571f01a73b563ac0787e57d7fdf1ff6fdbdcbfe48602991f513b83481d30009a5c95dad444a46962cd098a199207b346099c476e4a83e76ad6efac075aec0074d8571b2b789d9687a509baff193e1fc2bcf9a4055f474c550ca8f0c05a5f0441b5673faeb71c56453b30af9c2c55217f97dba33c5aee803536a9d972d3ed0077fbdb7998190fc3dc5bd41f59f9a7ee51c48aeea712607fc5dab3f1e0fbc40c5e9912087756e17173d94a99cf17925b2b910b871e39ff7b2537f7ae6ce9da06aa6aa9cd9cdd7be0aca6784b551241162f1578482b23f01c78448341e4725c0336eefdfc386f1ef623bb05e0477e48ddb30948cd8611f71909aebefbb25f0e06edf3bf00fdd1012a7e904c0731136a9fbd7d6f74c71c22b1d473762be100420008305280c6bf43d68358b49016e32df46c605f187c1cfb4a3e01a30d53b3e809ffae0be091e0e13382bddfa146977bbeeed1f25d7566a99e2be807c1dd99130329153f8d1bc966209d68c8d8e5f6fc6e7f9f8ef1e4c8bb1012f0a22ebbb8250b74a6cee0140960df7c7226f3ad65d9bd5005dd7875c50df243c72e3a7d164a0041107b7058f75b4b759528e3bf224aaa4640eace123769badb08ba8362454404b977b2f00ce17b7d30bf43efdb9d4dcf5cf69864e54749c01e2d3b1e8ba30e0e5121f372548c673d1d11c1e66d919c9b4927f09b697cdd9d19c6a768a55be6099c019fb46a21586f12d6fefb3f700f0699ba583f861e14869d980f506010ba0c307bacc9c9f0c2cd9784c60437a104209f4eace47edbe5a7cab314a39e1467037cfccb4ea39980eaa40b3ddc5b9988d697911d5989f9ad62f6137dbfcf08df0f306a4c527e2205d6234c4e84ee1ae8bb8fcbe2d85a1ce9b0587e588bb0043b0f36e879c405be18f2129d8efa087eb870ffe42a341352f670f3e753ceb1ec7f064992f6dca19a39247e982408dfc409b9c540e99745c7aebd79f8dbb61d5513016f7210b93144aec5d0b40fe774e6a1a5619c17e70a1172eb3a8b819514bb4f0a3cdc6c84477d2779dc447ad5fd0290607fa0ce6b37363e88b01114b2d10b040250cb785eecaa0b41b8cab376bb3d0eacc2f1b0964b10f92d87c5e43cae3498098f7bfc819e2c07247203af853d77d3ac5ed8a6262a435bfaf398ae2c647b9706723d307d2396cc65818f89a702804a3d7663808d060dd03c547f3711ccab39086806dad7020d71f4ce8c975a1c9a86637ff4889460e22a6ff80b0cdfcfc0c5081abca16dc209d914fd46d8d817b941665c738be69ecbeadbf084eb06a72f5b084bdc8f88390f6118636da60a1471d57787a9173e1015812935f19f66a42d77026af6389ed9faee65c91aebfc5b620586072066f2c8f92a2eea0c8d99bea90101ac4ab1cf01671888d9d305a74b91cdf4c3e479e055bdffab8615ff5a994e3f0e16812ae4aec0e852a95572474d69ade3e620f94a563b833e30eb0b928df6640e9165cf440e669621ba30141c0c71d3017ca6d432ced1e66efc5183dcc02b16046ca376648524ba505e40faddb84b1a1c1861ef49ac14c60a9d4e118d3d8dde0ae7b91c6a194867e8ec914c98b1cbec95061534975c76b94278bd3087778c020dbdc62d3d8a82d7be3843252df99a0f3490ccfd29fb69a805dd2b552d584f860a32112ab47dbeeb660f7caa39126808fb07764859510e53ad4948c4cf2001900d31211858ae8a02e2e0ac2c981aeda10134f9ceada25b3c2170c2880b81ae990aab85e333012813d21190e0dd607a506453e4c4dfffefdf76c621aa6cefd87a06dd68018042b27dc9ae1d9d3cd5a271a56dcc7c011ae46376b7c59d4930566009a7799f012988bec90a00ee44f7bf62e183b6c5aa4f5567cd292f7db36957b5099a771fa4ac4287b7abbd306d5c880ed5e1f1db9a087fd301b1b73ded5336320bd163686b203e672f421cfb33d48225fa561f466fa408231fb06cce3ddf53740aca8282cb84fbd31ef61689f0a8a448019857cd5f4663a640c4217618ee3b9204fb81e536f2608882185836a9be7902464eb8930a1a91ff7678659f804bf55c01505eb463ed30bc958f7ccf039492269ebffa37ab8ed4d1f96966581cd1a8f2044348cf9a9930bfd269f5984b12b2971e180a94641d920284e6aff48d2d323f0fc79c878646a6d781a20b08f6dcd3b4c09f1168779004973bf40cfef15b5d8808d0b05c07ff60d8bffec391746fa3520acc0893a37452edfdac5269e61e8cd6075f30b20927dacdc238362635ba6faf08bdbf80b4cd8318d1f03bc3c6b347ed0942a3baf10671336642e7de438085a2c0a584ee400f68b11fdd4ed1ef7556a70e7f192209e6990f7c55c902ce97e7eacfbf97f644b3242f4217cde707a0829201c60856fbb2edb4b9a8558df9832af7880cbecfd366c0c387ac2a35c69594e00b92032c6a3009442278b61bb73ee847fc9fe01a8070167258d0d3f4b26e81c5006446afc438e93b3f81d6d20da3e9bd68378c0aa70f53751263b1e0e2fdec700d1b0903f0a48298696841a63d5668a66ddf37b879cbba9632b12c29bdff174f0078fd9ddc8550768c787152babaaa59e7f1c7a1ee3785955b8278b22ed38a6e08c62326e9cb2e50d778f95b0ece094a4bc798a243f6f6d15b698f2c57d2dc1f02ea15f7181a47b1455816fa276a8ee79a189da339f72687b985aef1ad901990068cfb6d624143d28df4c7c2f0a99487d698b8f6295f4d0aa72a90e08fd6369c0a9c6de1aa7662e17812250d4bc468648fd09fbf2612237b8681ce6f8c22f35803967ce7a216ac85b471f01cf83e0c5d18f0215703ac46f9973c08afb2c9461208c937c498f3e5a0a86ac89db2c0cfd5124b17e66b506734598a4bda563a421f0e5374903c4429152f3859c4bb05a7461f6bbdd1f53d1bb1b90ed3b8f285322a01c0161dacc9827873a4ff3d10151c375e009cd8e1d4ea275de777dce4b4da4702787fc3cf70064dbe14e0cb7b5755e0ff09e8cdf9b04db62472633c7ac7b7ca0fdf7776f860ef673256822997d6e5f5e7b71a94db091e17a7897e1ce90dee2809968a29c0f74c2c9cf3f5f108b055521167de8508aa3c65aeec36bb227ff7810b4cdbebab7c7dcf04b6fb01bc751a0fbf1f068306a7879bf148dac864071d7e0be5259bbead9ae007225308b515a80e3ceeb3295301cca69096fea1c434013c08b560a6519059e4b376297e8338e5570afadec14a513ab6da9fed3388c3297d061b320f33003f29f014f05e33831170fe778516a5c68370da976c2ca917de3a0feed5a780a1700b9e6e642d62cece0e488cbe1e894b6bfaf8fdb53239be00190838ee2bef9d2d0a865b822f4e7029453810bee88fc0cd9768d0dcef006b2e6c06761244639a7007a971b4ba4197a3c12eba90ae2d3fd08885bd720b9270e14c0eb3bec3e2ae40d3534666a3dedd22aa6ae277817a3bcad2205eb59556d62b4b0731b84dc84e2d9dd2f9dcc9d01546d0a913a62b1be3abe4bb0f097868feff2e0ea5305db6131e30b3d662ca2e10d247d021051c788a5324a7b8cb8fab9617ce08b0ee1f296cde95c41b07397b7fa57bed876c3298ef61e4f7dcf7735f6e38e30a8e0a1170d6a36eb7ca813b33e7cd91877b4024789eb9c79c3797da0442298f000e9db7496033b09ab78af6fa5974f61039b71891c74448e1442cf55aa59c7d0dab301a5b65698de51438cec5138cf6ef4669e6fdb95b79e4d9d630ca3b61ca029799de60d363614b878e3770a7e4aa99100d50da37187ac300bf23e10b399607b348c08e826eb65c86fbe4585e40fb59860c0486b2000eaff355fe0f1701b207791eb7439189feee46c6529ce76020f3d50962a4eb040b5f9a956b9c6dea96054e8df33496ffb3beee88bf13ba3d1409beac1cfceb234035a66c4f1b5b0f4309b5a92b426747987967ab52fe29b213940a56716cd2c2db283c0ee3ae76a2580ea2c5197738977fe58ce3feedb70ee99bd2857b9d00acf3e28e9f16d9297bef0d9b9a2277e2e72355792ca72de782e18f89bcae408dded4b2916f185b749ca703a07b79182f5f08f9ed31b12396338cbafaf679f34c56f9514a23fd4ebffade008bfe3744d237c1a4ac0f40af1822550861919ed82c6a727d2c3f3c696ed2b708dbd763cdf19849b85def7717f196a44ad5940692cf14c98ef4ddfbc4e2aca60a4321b245c8172dd2c3d4f7028301e26927ebd247a19b809f129abdae627a680d99c74682362d9a52e31a3e5b885ebf693601324244ca3dddf1ff7d40c81180000a546af1c474870a5a8b5d52e04c3050d574b9fb5646c72dea8dba892ef321020b5776a6ef9be7ec903b51e98bf0b3d945f4dffffdf20258ce587ab6ae11910804da308ab04690fc2c9363b8de4ccefc85cc6474603a104d07d96c777c5d3307292dc55bc07160d8113a0970cad3518007f384dc923f22542789b4ec89eaea05d1836c2231aaa1d48e68ba13a8e5e4636572f73009c2a04731b7f2e07125ee0da58e58d27b01a4097c9b8a7e5d7b02a853aeb18ff8656a3ed1fb6e7b4dbc2c0c110c77611256f842d91201c002e9d9db8177241065963a0aa2d10e352fa8bc025ce65675920b6938f688f0586cf32d6a81f0b1b8a409ad14f640b52c6966540edc37879c9e7010db5c70a564b5ce6f87b24491c3a900495417c23034d6b2780d558c2e27d2a23ff904efb65763efaaec1ca02fa4804e49f0c8842c66919a830be947ce45f11bf550e82c43b75bada1845fb14fb65702ef9986f3f471df95a104f50e4b17f3c795e34e2e227eb533f104796c5362c1a0edfd614b83f99791060609c3a7fa62356f03293839e3e5de8ebb3454116c2587b01ecec5af32762fa20fa78896a40076c957078375bdcd40a114dbf9b225915e04a10f26dad8110a1e055157ff9b063bb0ed1bde5495e8a6ff52e8e3cc2d0d63833b51d351d67d2c4e0adabbe7024aa30d656b566ef4f21282df1528fe8cdf134fadd69905388899dd02a73c1f9fd4765928e4c44e17229d9becabbf60397e15d195a4a6e7bbc9845401f2a036d07bce9df734a68a5bc253627bdcbe62d2e62e568647f818845231cc03d06fb16f679d24803fea470fbc9a10865a8fd62d10f2d3b554acf56f2e2e430f806c286c164d1f96cd27bb4456c8abc8f78e682f057020cd8a6397456a1cdd032c10edc870be6b90b61e616ddeb9089c66ba645fecfb04f653acdc875abfdf0fd028cb5f56a94935d76425cf9cf00fb182d3910b4b265c4d20faa43e6c2f690f1f2523d2a6d0e52a202fa371dda5c4fe4b33d4187b89552c3467198b7931e90ad9d5d561f049d361e9aa82e6570cd8986d025b31a464263dde8abf21613d7b066fd5b99c9f1c9bab584887b89d7ab5d3a0e484fd9519faca723f8f7430f41d08a580a2c98a910a76d67eea21216319eb9e967be94c28349a03e36733f2bd7b0377430ceff6db8e513f317dd7d149d9deb82a3f85e919dd5b79bae8b980382b0e423ac272c073bf538b2d8b27e9eccfb625e84df8877431588472c85990ed3c0517ac09e39a604469cf9cce92e8f0dcbbab5366c00c9de28057ae60dbb5d1640d46eeebf6405dff818191225808b5e593c327fd3ca8a87c95dbf96ad237e6ce03d7dcfb69bcd4c9f7fb68577fbc902ef33dc54d32c079831e948dddfc1bbc870aacfd8e3dcbd684f0688ffe53d1be797d953840d8bda42abf470b6621c621c40594cfec5d4fc216ffef381fee298e8abac3b10288197d830366f5549ed5b55f08d03b1fbf64115e9999c8dcb2b4198c5142bab94be88ed7ca8280d5459fc5ec088c6b8bfdf03d1a3af113e46089078a8f8dbb020ee617cdef640e0410242ff90f60a0cc8c51653f70cdc8a707b32148041ce3d6283c54c69e6d74bb7ef911230d2db589299c053f519fc6eff348dbff40394101b21e69412f438ccd6301225605a7e642a752fdb1446d3390cb294c0c5abade5ca10f3f79acc2b5d92f4255b407e5d12ee92bc3df7e13d0d3252cc14ceece1f7cec46a75dd31e586c21a614c40cf025a28b148c6382043a756a509b82ac4caf6849178016d4245e3246fca7790f09011a81f8c3298652c830e11521fea1b05fea056289f020870bf35e8ef82403d6cd1165afe3886d636ffc9fa5983241691246305903d6b41e7df214e59941017e38278681e361177f6ee4b762e54e5e9677845130fefd551a32d575a9f93d0e2375686d660357da7ad1fb48270960793fed2111ada2c74923a6d1a185fa820e64ac609d27d9ae22e9f7e8d54b515ba0345507509d6edbe5d3deb3639eb7f20d468e354798e9988198d9291262d13da80cc36c18d22b8c575f2493f544780b0b0e346b604b7054eb2c5d088ef0471a593a6a4fa4bb41737b92d76e7079a86a04316796c172b84e39a4051cbc4273fc77916702cf171fe8a576a2651ac5dc890e51795d00f2e4d0ac02682a58d663a57934b614bab105c937c352863bb47a7202289bbf137360e875084b279aab61e7ffed62d8f2df9e8080795ec08717707d0969daa98f2e54e5aff5102aca6e3549232cba201bb00ee8adabd28b85e702f306d6269fff10e095decfa3449dbc6a5e1dc9d82fd3482a8fa002db3552870af30e8306be5bc3503bc469fc434dbc3c82ed5a133576ae38e93fa8e4564ffffc16041a6b7f6376568630fa465d31d9bc93a84f0780cfde175b4132127a25845d7304c2441c5dda3164da6f0ce4799ccac20c8ccafa78e7f1872ef312185aad58350a3a58303144706dc64b9c285a64a4db7f0f86fd2246ef7cc2aa03c6fa169b8f0bdddd3fe2fc5d910e1db522fde60607e9282f817784b2d904d5f0a0abbeebd70bd6d54b8a93db08c2127a9a3f26af2ab8d2d955cda9a446f1f36285f3c5bec90e383f27d0bc2cde15b03c646f8ab6cd002c9f9ee5c3d8b5ee79376016f967690a56b198cbf84cd50089a6ede96f1b5a67735c5466185575d65176e2631ebb10064f45601cf624905190e64a7bf17731355b8d5ead964d0393969d6f5eeec6ff0b8d130d78653f5170a3eef4c00a8467b82e38fef1b6e977da67875e88405cc50b26eb9db41871b1e5bb4e7b0b3e6d06c6d3a3b8b8b1d5d94a0109846503d81402e7b48e6d983879d0613b27ef638f9752097103d22a36edff44406b308587de02d47d0f2c12adef77fa687875b8514b453ce126512b0d038045b4013c5640df016b5916b203a39b9bc8e9666666f2834dec9bd15f1200b97de0c177f7ac965e00188961cef116ae7f847447dbddff7d42ef27b94fe9c7f2e6166325c08a9bb40f33c456b39548d9c9c2ce6e394eb94d1d83a8e9acc13c3fa52a859fd9a2ecc90ff7abe40981f7ddd154ee729a8953df148f1f4ad1b247d4b390bb29ccf399d509082fe03977fbc680b423438cdf8f52841f1410be14206340426bc19d9cafac0b499d214d5ac44c79bf5325ab0e3ea713253c5b51e4fc7b2678bbf4578614d90a84c2378bf23a386979ee72e15b9109e383e3b9534ce3d1b423f66939b2f54d049646637d7f0317478a3e6a1117dfda62ca10e35614277a8c9a608284df90db02a804c79bee02a62daf6fa914ed39a52981b65ff93d9b471e05d3cbe4e64e810f66a4eb4fff4ac93be07367c45d21c60cc1d5d6f0adcbf3d22ebf0204644a7205a7b06cf3a198d3d94751fd7ee62a213c085b8d017e290b55b748f5215ac404017446ad535af7ea6e515a4a304e65b84a1053c4c5d8883cfea29db784e1225909617b1f85349ffafc13ce506b0ac084dc572a5f205b191dc2e64b0035671d2a0b1b35849277d28d9027445821eaf11d1cc378b82a9bce15d647f1f08a17aefb08512e6b4be4680d06b9fa7f855e89b79304a40db824faf7aed5f2b609eea1290dac02dc3118674618983e666901a881123c498f38af3dfa2c63ebf25b6b83510fc32fcb9b42bc48c3a766bc273e1a917da71c972a519425c9ba2f57d90287c408ce74d804cfc2e0c491edd1c39bae578a2ebea61c1d6ab36eaf101cf34bc3f80aff084e1b6d83d62233ab7a6b00cc986be597984dbf7c4db1ddf2add31664fa081da6e1b99150a81d0b84d1bd3e110b1646133a32aed2c7e1f1421702944e7704f56804bdc04354d94f8857b5a0e7a7974784adba6e2ca4e29233be95934d74062a224ad4335cbfcfa7df75f52285842800f3c4a1e9deabf03d6127b98bee41040751a17839dbd4dc07546f74f2e8b5c63160637e40dfce9df2cd7a2fab0e01094c9e7d5ca0d0bcc6e5a44f44e76de2b68112bc911eb3b3f4578ab2bd885ba70e265200f34c30342cdf199e8fd2560915affdf2a9bacb2aa0fc1268f911ee4e0c18f5c2f9e3f5b68c4aafbd8557e183d7c27460f357cc970f758e02fd887cc805313f907a232089c3ecdf7d7f3fc33f38f5b7a8eacdd21f3cb9e7187f92cfd60056cc0f4e1901b36ade6a7815a304e58eac2cda1f448e4e528b351b0437cd4b0ed973251b6a4aa1b7cfb7108de7dc9a2705cce6541ecb914da67fb5e1b19af908bca376c521024383df6d80af05cc0e4ff8e57073909157969022a2f9ddb2ac041d8f7de17287bbad8b6da324287f6d2a8874d5f08b47cac32b34df996458f20206f59505a4230fbf02a04c401716e6c87107a021c8f129d0d5c169de9f55e90ff945403c86dc2e10f4a145bbd48c12ff1daaf319b0b10abb51beee04839bb1041b14af4300958440551660f0767cb9f75009976b105745c075c5454272f8000c82e99e212979f99f638d0153ae9781de22c973190f167f42538d69303c478202e7c1dc08eb74c12b7464bed87543c5cdebf27bb61b75df16cbcb1878cc07010fa7e66e2c2526b96466f9d034cefecead77042c769694f5aef10d56bbdf4c900eb21b882e92ecc6e48d2f99a213c9971e595fcbfb4ad64dfbc26697e5950107032b4c463dd6b7165272e70b5e5889ce00141fb756d18e6043fe9db2d407919b0bca57198c183d494c6e0b1e2435f29b5a501a57997cf94a872a24c06fc70c38041196b2f5df5a54cf93644ed59f65bec523957518c206f9305076ed95292ca8085fed1840e3244cb9aa93c4914dfb1cad597046272a5fde4f1d55e2370a8c91091ef4157bfc080c8aa03b0ecabb40e6de9b6f81e33d6d1d59971339eb2757ea0b43ed32d0b3b51f80df6f2c166b5812ce36d7b44de3a20d4f7a3320f19d6a56007be4a26e5a7a56f777aa1b6eb46a8cadbc0e84b2821f635bf8a4c82f2e19fa0ff004c93d2c3fa57f45e3c140fb6cad409cd6a07e25edcf35d87623d461a6ec0d16e1cb39d5fe8b9960e93aeb9baccc3647cb0b3306ec3f2171867398fa5e01051ea4c9bbb28753d4da912dcb9bfe532aa66db31d305ce4860ecf62a7137c1e038bfec1f6f3c8dd9098827cd37dcc3b1f1a8f888076b65fe23a8b08b7abb27e08422b53cec401f1039f699326719831a7fd6147545f823b01697c2faab372650437a81456cf8513719c3ed07753fd68fefe31fc4731167835dfb04bf24917ab0db3089ffa3a41af129f4b0ea2f0f08618de85b19b3e2167b0d9de23eb30ad56005736f77edfc019a052c0b6eddbccc0dee37ddb4116254e6770a477d1443e3005586f8c2b3eb5188e33c58c5244988dbef1b948108a1a1c8a23f163e7cb880d0b40c96fc4f79467ff39befbf3b4f5d94de7a5d02bdb0bbf621f62a132c2edf50ce00b7d6053d126b06613d6076b427f613ceefff053ec6022c1702750aff2a400852d8343a973b0476695bdd3fa6374e79f8ad354ea1917054b8a3098861bb30cf9f8bfc7a37af76901af0b5ac50f420eefadef3afabaa10d0f0eafa0cf7aae0fe939ed6c1df659b0c6f18d4b06e760825e718d71878e8fbe752074170bfaae0aa7bcfc0bbea61514d88e127f70ef4c13c60eea99f50888e711fd5e5d8441610eeb894bcb19caa27cb0d5fbb3dad44353e61564caecef1070499eef53ee5dba039a0c50b7bb2b0adef7506b9265917abb1de797ff94fe7930ef9bba63661bf50df1fcf878cd81acd6aa91ea27d4b2b4cfbc263bd919767348c3ba513a176eca08eea16aa5a64239225d96ce4c9dbdfd3c0d2f0945d4e016747fc0774e8b4671072f846bff4d52b52cc310c58f1d1c5d4895190a1871532dc5fdf2c3f428b00f012f3170cdb09240c9965032f17790c6ddf94d9f4bcf4b4de4a25e7448423f5604a4d542da485eccea9ef6a4836f4df3bfe4e099ad90daba784a47027f0d619d04df4c52072fa83fac16753db00661ebcdf358a639a0c0d8e4b6911978d476b9045ee4e5bfa8acfdd59e1666c594e59e99a2bc06514b27d1d83e886177ebfc5a05a28314e3ba517abe7f31630bd8fee6a480ce29f04fa850892d37abd278da780c694ddd407483128df987994e8fd6624dde55f7ce6890f56f5c991bddd9ce600d534b65f6064220e7ad905663adfc5a7bb8fd589f826a90eca5516a69ad8d7c0ca6cbe8a8d1ec432821eac8d97278f57d70b2ba1afa07265b7bffa50a2083180e033c88754fe50cee256fa4ebec7668b8b6b103e40d721d37de84f0b439f864088c11a63e8b98dd767e440a22e91b2c0d12c3dd24bc2777c7e58392b697c7df08312e94c02348e957e603f7f886b840881c7026e29c030c27fcf75f4cf4892707fc523549547229aa535bc986eb27dd05dd38f9419e1f613af9d4baabfd658a0c52af098a6c8f665e2a028a05a687dbe68b97e4cdbab0ba7d5a9707f000afde074eb94eb61e3b2bdfcefc7de92a1c429252e3dd9edf015028cbeb48c52e21556eeac493b2196b001295ea6e4dc6843272c611881a63d59be2e8728b07fa58cb09e74f31147056155681cacab827fdfcd89b76e75fdbd962b141fa932a6738410f91fafd5d7ab614c62d6cf84f9bfaea843b3defeb74a5c9394685be4832efbd0217745f7a5e8482d38aa67f31eafca0457221494538c391335e914929c4e5fb0fd98b61a66f7b3f9ea403bd5dc2081c3e193dd56180cef5fb0c3a778bec66d00470fb219b34e6dbd36e586916f3b5722c33144e2d46c7bd797d8b5d64ad19470fc0692471a0d5becd1e5b83bcba08eb6ea5b179c9594613b2f6a40c2eb8e5c00c8cefed135c99c5aa77fc2b9286878bcb5d59f47e039004b6b621d33e8a76470c8ea124d08f1b7051f10bbda0c699115cbe478c33360adfd8a6890fee40757f05c1cf59c53e8f9642c27cedb48a000bdf72a04175d8bffe1de4a45b397e056b0e4d77c3cb95c76af67b2f8eea5647e1b4c398477ac40893a82bf7c1500b8eaa0d35d8d64472eb8362428c8347bd5e0d3d2bc537175ebfddc21fdd16bf0b17ea0e6e705354f0d47260ef352c1e3a56a1fe706fc1b4d5c17e37cdb34d64a2edca068f0195498c7ebba6e92aa0d602cb6ded870dc556b5253fcdd3e46ab459ee1d0e2a0c535dc62f450544409c4eeb1b6d257df8d0c17e39504cc9ad916344d04e0d25bdbb51c566f2c1d4ff5d3c2585d3ba3514bca272252cf531909bc728e4f509da42d5269def9cb8280f2170072f3a9796c03d956ab1ef00fd3a1cb06632cd0e69caec2a330d7600e8d9b2bbd93896d6c6787b32ed61d421de5464f358b7a9061e0b03c995feaae6bf5f1a10dc0c7bb2e5a79945bd7c088a5cd459e3b389f10d94a501e7b56bc6a6bae7db4749922f99d8737f09da5ab72d143c425fd68ef3064d0f74bedd577460c1794e8f768ce082b66a8db4a45324952632cd12e644c2013dcde3d87c6985a2d20a697ffd68b06e1def7e32a45c70a9869ddc7624cc1f0c00fc674f79e0c14ba760d8f4d8998b93b3a3620db05255ef3dd3db2b7c5a7d0fc1f8d69acc4cd069bea49e540f822819e5ee53eb0ae783a286f25e8d03c1be0027f50fb02c11d8e41be06bdf1f2ebac98fad4688a4be32668f5000b3dbe7f90f6258a2b03908b7b3ec96ed6a2778674f5b57ba997914ec4c9ed181564c93f70d0d5ce0ef135208f80fc32596714ad097376981ef7de8340cabb243857d6bf40285cb59cdc7e638bff0752dee5f4f59f9c0a50c2b1a22d7a485e189a7a6d5640db0f2f485ab74409b39cdf0a8501d288487589835b9a3930bb12fcc62405c2a03135ebb8e50b9ad60f898946d3adbce479d5876b79b336589c3d5892cacd59e0774ef07ace029014833d898ddad47f9461b75c793749cb63d8df8b0b0f447b507665053ad26d9aded426f153278ced4c0704635a0169e25e00af0b6f74420220f9856425127a8d19a17a2e6a665f1518baf1ed8e2ed733cf4a1a49c3699edf20af2555c85201d9b98a82b30263f67460c905c2be6caf098a1aff91d30b5a4ac0e47e176d8e6796d3868b747d728e5a22c569604ade0f98b88a196a91496300802507d14e5ee56e9d203aefa6d09d3f5166747a3dc269281594c87b81362d1920596fd0a63eb5dbb46a2e07a81b81fa85d191141e870366fcc8ae5c4dc099f8a0e2edcb3fd85428cef3420b03dcc9ef5beef5911a3c31e665f7ba550bd9ff79e09c906d981fd059e474ab2d64caef1f082c47f05cabe5fc96808c993d4174b69056f3a07c65f6aeb1daed85d088f12098a9db4a845b8081b22853e88874ac18f09c8f9dd8896955e6404381856eba771d7561892a0125c2385f0e666ad6e569d0eceb70e452c1a66cf97bc120f309902caf4ca1a2080804a948554f2eeb0a55805f06db2097a1eddef2803258ce1fbc2b372b8d76dab32962d5628c062fd5fcc0ce69fd561ea3cb09febeb37bb6e483002d21fb22e430889f31eed50f3a271d70e33bdb11df16c7b4c9ae3b5f0e83706c29264fb8b3769a71141d98bcf67b3ae0c9768f8251c66b1326bdac87a40c1d8b23e50429c42f260a248921c43da83250121be069a9fc56dbcde8ee70734cd62e86dec9d9a8528ed356670794997e95109578f881dc937077009408675ee14cd8528d6bc60641094707981dff4875c650d3b2a9703e98a02420c36e57d93eaa026815f29899d7002de7857dba30da38300593f20e87f8f949417cb78a65064c4b15ee7617f46277ffe681ecf84c42c3801829004f459d4c2f8152ccf6acfb03211a3dc9dd34e063ed2839ca91fad0013088f86a179a4a32279df3df2f5628fc9956a3782e0984183aff1a24c0ac83849060e03632d2b3de021cc130c4c80401a10776188e5066b938c8331d5f040fc5d043205243fd9aba0d47a6f6092b84852a502fe8bb66fee667d455232c8e679e10274bb60b2644256116db07865995d5c2b4758c48ae13a452f3b9f3a4711da550e746f0f3891521cd2504ce8ace72f05712200b1b17c0466eb39debac5ba10760133bf73e516a5efd184ef28a2d57438b3347cc3822dd662a8745d5c0f92e596067a920153a6f03af98e6a097e3f44e50dd077e060e8de44b0f86fa5d3c6d05508a77d54a4e420d9bffe18f94127929161848d63719f6c52837daf986a04482d0a1f66752e57cdeacaaf10541b91f96688cc6be246b154e0e8f4c4e6b52d61ce0b320d6239014158fd22a607efd1fd92f0242d628a882df873837852ab5b1e9608f4554bd6a0bccd6ae9fa3ee276dfce5b8d4cb7717644ed4c4b7d123f7cada20cdebe4db043df8f201303d0cd14548f23e4f58f0d57f89dd687a218854e9ec00ee877fca1ffd371c7ae2766b0c9a74b75b1d5c304106226c8182d6610c69466008ccb944f3f55380f65b0e48a29c01085340850ddefff251dc3ef7e7bac827308927f0407082f806c1f29baf045a857656fa0108b9d9b48dc291eef238a7b8e0d629ef254ded81c1db6136d435a4db7b65c8837f1d70ebdc0a5c7f1ad436bab05d0a4bd68cb1fd74820c4c53c951c714d96c18fd953f10e7225592d3d489b0408 false +check_ring_signature 0f8866364fc1c4bfb0b21f3c6fffe4c0748eb56c8bf206330eadab7109a7b6b9 50e6a82c58194374b064c8c998bcf5d31578b7024d99b86610a4a63b1589038b 4 d7799cee57ef8bf68466bd7c2cb140db9eb789c6ed02faa58f0e64eac936b677 3529ae92a2e8a32cf486a792362147e81050d0a7cc362765bddd81fc5ec150a6 db0a96f0798edbe57822e137f0e134f0466ef0e3a6d27c8039197bce148448e0 f439c050cbff570f5e89f77fda5a824ced0ecfb01291f87fed2b9e999d21da1d 8ce187746dc4633fbabcd8387979a46265d94e469910ec240f7fb03c0e75c508367c9ccc12ead95dfbc1dfc1eb0999bae0db6bd6fd5990e4f86e03bba7d244023a53134a81cd7f335c9fe0345d0b2adb728a99fa19a01317f0e2d38409877c0da7e2eadb03e60098e45b91cd9e10bbc2caaff60396091aed4dc313cccf80120009ea5ff1ecf18520d2f3c10a07f8589552b2b7771eaad6ef2b8bbbded6519f080d19d0c57693abf5327500f7277798aa7abe678af0da366cfb2c2367d6c00e07f87e081ba2c9249f25683cb2f0f988ffa092cfd9bfe2a9f22889f2f2bf43f00a27f0fd04ddbf4ae55cdf47300887e566b399ae743aa50162aa345c9487b11204 true +check_ring_signature 4e45b8907580b0c1f5a32a69719935febb7683e4fb18b772bb07a2a361330968 a7663d241ea961ed72bada060352355abc962e8201b6ab23097bd4292b6a6ecc 3 ee2dceab4caf6fe61254f092db370a9fdf8e0588936dcd06d11f4b4971c21511 ff9a1fc2b962bf5b7d423e4ff87bd47114133bd1adb13bbb7ce6726c68862be3 d1139d670ff5cf5d04916cd3260d1b9a2a1ec0996b8d6a17d10ad8900c714ed0 adca648d75f0fbb7e05720ff850b794a29ed7d08e4988fa130fddb9c38f1cb960cbdabe80a84e94286b551a2dfa99fefbc2e4cf288a45e6215b900a7915eb306aeb37590756c050403bdee5635ef4a75e167306d5dc576f4fb750425b98afa0f8226798b4ef84bf6f990bafdb06fb1bb92fe89e674ba238250203a30dbad710767217c447939cabf199f60c10547e08773fc93b59d07ed4eaeaa1af8e3a6980322b31fd4d7efdd310d8d4ec10f4e26e30b921c84729321f9829cdfa1c601b8d8 false +check_ring_signature 8a3f93b3187317aed70d042d96ea0853fcef5b0466496c75cc80534c0bbbedf7 493eee58fc677508f0b4d8ab0162efb0b5ba39e8cb534ff50af568bc35e234c9 4 445f9fd2b7f6396c1bc73a20e9a3ea8a4ed165c22532264b4b08bc7ee79124f1 5d18c53556b3d28f283a475f33d2e93da7a17938f6ca76931d505436242770e7 b3c90e6b6f37ba0db93e3b5123f28513d57a1a7ded3b4fa113260d80f5825415 2fa8421c6ab03383b2a47a56816830d53fc6aa575b14ad0d02547d644bbe6fa7 5d9a59df7b477f84cbb7953fb6a4f6fff564af82e896d8d7fca2c1c4ebb1120f76216c9055a8b8bcac26f561090521aa5b15539167fd8c220229c61651c6040c19fecc60b2d2ccdb79fff780bb149085eb59ead6c5ddcdd5e95f7eac037da80bb9f8363f122f30dab54434caa5baf2d900577d3906f67b66c912b3e0a02de50f4249157a256263633343a5e1f96c8f94b464cfe2200d1dac93a607b1ade54d081af9e88fb5ba72c2e8a64fbe040984ac4f86ad44cee013a621ef83a102ca4f0a3e2911490c72c951fde1c17b5093ab56b05d3778fe7ac16a86c5de8415d10103dc3d2ee84cf7633fdb67e5f00d0271989547d20b6b17a00b6ba60ec0ccab5108 false +check_ring_signature 15fd77d589a63afd5534072cf7dfd0543980122068dd4f97b987ea1fd2769494 96a4e581a1ace38c0f24563576ede364e76a0fa8b07c1a6ce82c92d642bbf19b 1 9f5076bf551c5a8c4c09e4b0d7fe2fa3437bd4de3aaaa2170479022057db6177 ac58052c657f3eda19900d29b08ca493432e3aca0b31781c7fe09be41e3d8300a0a5d4a9b8e1e07476c4b42d4f95d094cf08f640172d4fb0bb8a70a8eef74708 false +check_ring_signature ff5d4bf362fd7c7b8e304ea5dbeb0c6fc246d6038b38ca1dd5c923ea1379722f b504e3f73d6d60921c702963def87087268a47653e17d2504699e481e84ba597 3 dc30fa284cc7359b1640681dab3e649bab44dbe2d076fe1d1aea74fe4522792d f398895f6524e6518d0c2f1bb1796ac9d2e1ab6c5829590c6962b4dd10545d88 3515dadd1341d3b8fabf395bf776e444385a272540342eb5e59791ef5dfdbbb5 7c5bd09eb764a0352cc6d283aacd20d478f54838701193faf9e9e9f7f34d3f0e6ae60654307defcd865a0d12034feb5ad2a3280663896ba0bd0d60a5b21efb0077c0c4640222edbde24d9b767cf2aaccd6df36577cfa683897ee30e63146a507f6429671682792709cb4e8da790b45d23ac6f249af627e6ccfa8b31625ae740094a315c6886b2e36e22aefa2c8ceec20c59e4379dbcd9d9958b089278656130dce8e2edd0e53d5933b37802d4e69101c1c4a6276b5d0e11541ea0ab27763e90a true +check_ring_signature ab85e4ae82b9010b0d75e034026edb369ae24695c05669c13d77f271a0028988 9dc3094686bc50255ac75f6b8521e0bf140ccb8708a0499c7e2e76fd80848a23 58 54f9d926afd1d15ced7ef9884b186d71582b905b1c50b53e0d897587525be192 2e36ffb278b59fa9e8ab2628413a6c3f53b5a380b8d267da779db1949b55d9a9 cbbae14d10514d9ebaa13be92d8c653f476a3aee01720451fe6025a19bb230d8 1fd3b41d989a6d1ed951ac272bad17a78076eca0ce94b0e28b46eb34067daa8a d17baebae85363c9a993f2ba998d51bbaa3e93fc1775aa29f48238c99fc3d626 527b09d6bc59ca4423fbe45d7f20a526e704f2803bfc1afa13bfceac976cffbb dd70fb40b8cc0899e9c1cd4dc988ca9ff092eee7f9c8ebb806b5f821a739c71f 1b5131337291b5d6d438cc9ffac438b73c56132dce8c57ecb44f41859f9442bc ebf59b752c205629bedd6b092dffb464f9efad8d743b1a89b58c06eca34c015c 1b2ccb63aa8be0710b724ac755db9f473f67416435dab038fcaf3b1738950a06 3cb09842d90a847b4c8f773fd5df97d97ed0c790385285b6cbb12854f8d65eb2 7bcbfb756981d11fa868e368df92dee81b47de171cd97c7f400f9f7139a621be fdd46d6d7588d7c2bb93012c433f2a2a1f1af9861b3521195ba17a4db328b7c1 9bba77dcd366f586f74715186ea43c6937ba784b9cbb660615c1eb75489b92fd 1377b0c268454fa1183d657307a9d7144d7ce4c5c55129c620e8318f953cd05c 8ac84ab40fac4c0a8a0833e4159ce8c37bebe68146a6c46631ca170d278db34e e8dac4fe8d54d8ec4c47f81be98d4a7d07eb6e85d19f96384eeb49c73942d84c c4ef1adb5851de3cee567da4d911af135e344c2477b9b2013c458998438c61c3 4ffd071967aacc24e2a2ddeb92315cd9d1ccc469734cd0935ceed72fb7d4f4be 1eca940eed25df3bbb5c17f34269254da7cd5206227915c842ad2ae837a5565e 92272bac54be7d67407ef647714c45ed93125ee23bc48b3b5a6fdd42e65b205e dd1b1cfa86109a2787c1213ca92729e73e0e25b99fcd660e3bbe8a75241bc3ae 366a6dda48e9efe0fc5c3012399e31cb39be9ddc90e8e6007aeddacd06128f6d cc8658a1622321a66e5cf20ada97f76bef24747a995eccbf7a30cbd09aab9adf b1c03d41441b993e95e872fcd9baab8cfec17905cbbcc1101eb77c5ab1438c2e fa883d7ea5279e74cce63b47edfe66f6eab695b658bca222562ce180bd984baf 17ffb74c31baaacb6fa3b58e7ade81e856c163fefd26a9ef44868a54fb7fb447 d1f6f230647e400ff6617f29b3defd4a4f681a632ad7e0aea745110e66830d6d 2e2b3ff8926aca66ad599097ccced3bfc3d6ca441add9f34da2128e71c8c35e4 3c33075b386c18c6d0da951e64001addecb123afe7fc5ef61530924f0a3e144c 83226e7a7dc217963762b4a361119a5717254d91924cfe136ac39d4454187674 4652d93e16b98b8a073ee5857d2887cc4f73d0904ed5b3be368847de60560a10 79e326a0ca24554e2dbc46e9bcbcb7a000598850b01b5055d6ae133d75a0e00e 72389f28da6328a26236f64c337bc60342692b25acca43633cbb1bb51ddf7efe 46f715689175e4fda8773aede3b34b2ab2512e53980138a6dccf2971ad02fb0e efb36004b0c7c335525ce87035f2f78a28d751d8958e9a0e5280c4f82bf0e99c 86f4b949e3be0277d9b476646392984e0436094ecda65df5a34d7741d113515b a4226a88b7e0d5e6068e7c3b010149dfa7dfdcba0191e5e198cf19ca8bdbf6d4 26ed48910cc904adef059178f76d9cb4ffa83a7d69fe615222699114b492b1a9 71d65e56b939fcfeea8d15230fb7e8676d5494e6a37474f3e46e8735fe73c032 10d3d53661f630cd3f74184ae37e14b1a8d4cd953fe73a118022c3f11e0c86f0 c40923a823f07aeb5ce99b3cae75a3822bdbd0daf65b2b9cd694da439dacbbaa 924af6839ec5409016da4fa9c8daec90ac13b30dc24eca29a5a0ac3c07ca0450 ae1f51854ab0d200c7b723f6776930a08b3ee204517619f4c47b43d9550dae48 b1f994b7fb731e098b578208ccf798fb060ee87d07a7d7b53be25b1a2e69c924 4ad8a4100d86e2078195c09b2befcab1d4b34fa7d67a5d4f94c24db1523e7ed3 a5f8c5d79561127707e894dd9c7f5b9eb2b7cbf85d84a1325f5601a4092461c6 ee58d05ad85a47268790b8bcede8c513f19115dedf16ad8da4d59fd746f3296a 4de656337e64e00b1b3ad1d220a1b111d7735129ea1eefa3288fcfc18de14d02 d9438143ac45d781b75b12d9db4f2ec03ba5302ad1faf0ed1a5f2053785ef44f ce9f6a88eb58f7ef652c45705b55581132a679d3a12239dbeedf203abab2ec90 4f01cd13d2167eb14fd6dbbb1ffd1829320a0eb0c2caae7037b2cf6a09c338c7 55ae3ecd65a607eb029f81a8a3ddabebb030a0f413fb8151bbd6f697706f0ade 8314f87062fc2e6da50820cd69dab955d544e9e91e1e1ddd4fafe0e4831ac583 6c5ce9174660f80dda6267031264cbd98d8f5da9878b528503eb05b07fd6ecea f843dbb8972d83e5cae34068b88f579e3726180c20c260428fdffa726f43b654 de80208fce133a579e577d0d351005e73982508e11d64d1f70ed33cf53a7f3d1 443410a682a1110a2da72e4e077c70bd4f54b76e6ba0c9a65cf04b3ca51062df 4e167085caf3d23624908ee2f73341a2fe9c40c521ba4a3c1e61c6e885dc5e07b83f6128e2b00d2f80f72f91802809840682204c1457d37afc981f55f8a5140d7fe968e61ec9f2ad4a7b0a66df100229eedace9351b87c70b75adf3840d7c10c015e858e728bfcad2e2375d7ea64b8867e8292fe6e971c5f6595ca1967b5dc0fedb70d7bd94089d87fc915718d12f53f4ff723a07c32fc862cacdcfabcfd6a0103edbc75cbab1c51909591c887682dfc0bb5d9d0ee509025386405f4e809e50e4983b1f8b7379f639191aa98357e60f13672bc6c64c83b4370cb5169994a9e023ea69232a1cc3b6ab1bcd48c042208382261f0bf5823e865272fba025706c303e6b9a5b9ee87d414930ffe7a94d6af914c1ca17515770f67471341a813d0120ee30e00f6e286e470f77a72910f0f337d8da2b293924229b02077b4eb38f83f06d875e5a53ca1dbb535788cb8d08429fa1a06bbb152d1b06fddd0b1a6d80be20d1d6c939c4c92494a0a12b4b2c9132a5ade237d44fb6796f9a24879cda34944077af6ed3173aded2e7678197bca8a3a6778a0809a1af2cb8c7012a9e48ce9d804e05d684d021f07c2418e47dbc9154ffb5b240117582c84ed167c532aea0edb048d5f66d38a865469dc10d1619a023d9949ecdc4523f63aac4da3d9546767d304ea441512f0278af0c2ac94feebc88c12ce1d83f0cf27128dd309fa7e7b7ff2037958782ef5f97de65320c36d25d2850bf0710b1331127571e541088f15bda703552e019c6be2b6f7fba0fe781725e6c31b45db7f2823d93f2cdb5dc519e8420a5ff85b7a579cb61ef46871ebc1ab9af9defda4561c0a4fe8ef3aba465ad9870bc37ca04f93e05bc22428681167a50d88bd0b8c4dd724c963b7fff3c2d55069019644724679ae4bdbed8b0418093150fab9d99cac5398182f04ba509f0ddd7204c8492f813c303f366d166747c5e9e06c4f78d981abf1ba1b8c96e41ace5f970c938938dbed35a118bf892c56b99f6cf1b23c8c0a00cedb679f6e7d76bb931e0e0cd0e4242a62d127bba992a71cc36853ffc7eb73649f83398da8d43354d156026a928d0641438715f432b4a82528682deecda5ccf4b8869c70fe804c4bfe74076f993e05eed436d2f066d709a3468d237fd62ca7f02293cbb2bee4a688b3670354a896ca9b02850123aeca4c8ba29125f7010bc12a69cef2d56320f48043d6014451b1e23dea5f6f93fdaf8d06bac4ef12f119e40bb7a48a62fd24ea0a378d03135741aaa015336969410603ee2d135107474b933f86fe1b8fb7455395e4c20b8f3fd8a1c7b14da9b748d6418963633ed260ddd9ef76a43c5cd1f2bcb05d9b04e810a2b9a47c7fb9ceb07cf888a028237499a0347481fa8be900f5e1cd8c50070da658f5446ff8e08ff43b86dd24d2ff27ff1f27e9b490b689f4b985f10f7f0ea6d0a4b9db11a651e783ac9aaaaa12c5b58078b884defbeaf5fc1a5477c77c09c23ed977c76024039d7fd80f801c297ee8c85aa557184172af580e4e93536302461895173cf7331e6ae0b294b3b7a757fcd8210a20fb6a6cc48481718646be0d4639e5dd71e59a67022f203a4fc88150cd48b3adaeaa97f64dbfc995e68e4c0a987a0aabb81a19d01a87b6edb073519c31e537f672e0163d58d16a40a4612602c4b16c42ecd6169963b11700a2e1cf962f66ccb8361916be575eec1cb9745506d5cb42e62c9c598a3a805defaa49b82dde24a2ecbd6e45b469743f96f866ea033933f307bdf9e29bc212cd1f650a64082f8101ad4beab5c743b2f2499e17ce046e28f325c53dfde3cae966f33f83e424a2bec14e15ca1d02142ef4af4994090aa6d90b9210cf426fd1bdc1fecbef73dca08fc8479efba08bf6399e028e545b0c52d259da298f1d21b2a2c5dbcd9994220305344b72d55a6a6e2c2319b57af101bbb5cfcf66ff7744c035e5f3c6638bc6c2159709ef3024996c6528a0a2c8aa0377c30327c08f80615d6d6ef671e42558e262f1d9805a2ecee1743da2ef619b0e893d6a7c617f57adf604193019c293164fe9edd6ad71e6acfc616b593daedd0743f89767405fd9fba01202bda0e1c93e575e382afd5fe01504c6fd8230d13401d2fb475b64a03e667e01d4c44f39f57f97265a0c9da6c8c1c09ab1537a4bf00484e48fdb2367b03500e2da566f790c2a6310ab7c711dcaab35bc4412a6bd2c050839396934f830f26968280b366815323c4ed24333a6c9641eef10f77d5b2a099b350704883fcd5cc9619dbb49e2a3b4d671b83fbfe747a96f779973dc4a030e6a5bbb6d33b4ec9d18d24b480c3fc252491ab38699447cd3d5792b6bb056c9035804aa18fac5a2971754b813452c6f5595ff4bbca7ba98a9abeb5336412434047896a02a8655f95fcf39d5d71d93316cd33724a3b8c18782532b494a6dd7c402ba8d75074674787ba311165738b679349a54d3a583f6ebc2a0762b68a75fce02f2cd90ba7f669479dbc17d559452115eabc36e4b4c68442d45156e413e959f0b453485e7c285ebe993f697d5aa1253d5f69ad609f259e50e51d381b5c8f023075dd3350e7534a493de09adf7d030d4f220ccc14a504777cda0e6229a3d80950db7bee850646cc0b08037ff89d2b616939e9f44854cfcddb58d9a77dd4ae6480397950f6cacfbc48857423feb2829b5e985935aeb455d3ce60541c41e7c313c08884045a0cd25d55399c2f039d0ce4cceb2db3285ffc9fadbe8116b68f77fad0c9c4f508402f2edcfea84af51db91cae678adf094bd93903011ae8d2ef7ea7e0400a8a28e061587099b7490b27421a713720cfc8a7e6fec8f384b9ce09024520fedd48b4e39adc5cdc0546506d5156c3ea9935ee38117cf1928c274ae2ed4a706572151c61a90e17c7c700f37d2d1df07e6a6a151723c5f350fbc61f6141a8f000c0db8345d3229fd1f9f2515be98f7b98c6172dd3401d7e0ab28a4ac81695c0c8ee9bb030e55b9f7deb9736f47c3607f08faf50eb8d71cf89fc525c79d611b064c7c6e2d49a5c9fe788da72929e1d8b9d878d3c57a52442839023476ee590e04ef49ff8dc35e4ec8ebca25b81ffe781e181058286212f33003b0465b9a419801f15344fc56d6cf5f14fd757e76375b8d52c1cd356d891258177546f62d1dec0ca01836f770e9ecc632f65fa0c8940582dad2394c963884c62759c88c0b596402c1e6e257c108d26a762c7909707e6beeb990dfd630641d7bda9dc06313f4b70a2ea188b8720724b2fdc7c2953811eede8aab7e7c8c9249f7b228cc68aa29c50b3275ba534907461ee94d044d404bcd3ac753b4f47104bd8cd7d5901c2afb9e0bc34ace942ad9b5036c22fc67d4f7730b96e2062306e49fd25e1495145dbfea00a51e2cb4f5440784dbc1b15523c361760e128c5f45411aaf1c5d8f67c6ab7203c9a6ac4558a65cb1957df573eb145ad573ab48d216b7da16329a7d6fc7bc02089383128e058804f59c3ba9b27034188815ef546ac8851e8ce785bca60c6266082db7d36715c625845880ac6a11f380c3ce317275521e8dfcc441ce31e48a3d043e64abbc574c5fee64a5ebfd702db3cc666ace4b8ac9df97ed584b077a89b70762a38c206f38d4bad632e934733fd1062be2cc8732460bdd0e2f3ad2f2047d01ab80dcb7ad07fb2c279fc00e7b26cc5ed07002dd800026f31e07c47e9d9a79060dea6a23f05786e6d1e4b9f61c2e622c0fe3bb8e8f94369de8edab3ebf06e108db45d0dc6eaf9760fe1547a4b16f3ab7954e3ddfb444f195565fc007ed70e40ebcfc81ba1376c81f6e0e3d8d9921e6b9979ce1dce31977b6cd4a6d07039f480660a0bd5e608254df61c746a8c5e0dab574eee90f4eae222803dd6f3f11507f050b36f376af153ea1d7cc39b089b1133b6a8e1935a0931023a751d47f5e99b706fd7e1076cce36236191fbfc0e5ba9a1e3bdb41f2f9dcb58c59abdac55bcc0e0dcb378e8fcecfd58fd989ca4964665a62498da672d9be42b78c9c8d540dab0407f96b426215fc339501e56ed7e7348536995330a0cd9eeb7d9ee3b2486bc5310a1ac644853f4f5f74d235bdbac9a2d0e31ef29db9408d6be4dedbc311de6f5204747de3dca684b014c51231843a566248624fea7625331c63836d9755f0301c02207e76cd53964103727b139e5f536146993aa1a1fd3b01cc8541dc563bdbdb0a3bda485327161f699ef3eb7e5539a8ed38480adb9b5276360ed653dff7332b0dc1748dcc21340aaf4d1af7de237329127455deeb9ff4435fddd3d1484abbbc053a63b782ea4227403bfce6503883dbc0b922863f24bc09421cad759631af070d493139a0a3c0e994331659367b339d6ad02b5c3ca4a3a5cdaff676858f84a5046555ea111b834bef1d7f19a4ca4180d74216d3ad2bae5b0688031b694516c80a5390e5dd3df1fe7e659863837bae5bbb97b34e40b7af93238e4bbc81e284300eacfe9ae3fe4e1abe1705824deb137b2fa17802bceb057a82f8a2d944ca9733016aac40e725a28a2afcf2789a89837761a386fe0f3591519ee027bee0a81d60093318ca9dd1b77c2214355840d3e2f44de4e6cf71e48d6d288a807bc173cdda03ba3435ccfaa3de1801f12928eefc4cf861525e173d25a20197684806e7a5230886383a739db0cae9e471ec35db6243b5508188263bdc48ecd5a212212321190f942c45d0b109b3512e2ee9ed51794e9772031f4b5f9077c7eea3b8e847010e0bbf13553cbc074f5e72d0d551bca734b0703e9aa907853470b3e5e215dfb2170f290840f546c9378b7bccf8024daff63893d44bb3a07566e8a6dc6f61426faa085a2138c3fd5df5d65de14a2a0d566746991c129210a48d6c3a596e29568b5302b295cc2ddef185744a8fafa93bd156be865d8ff3c7ed47c89b591f750afa8f05d923d9c8cca5b7d0ee18227a6349a2ace19b10f1abfb708838d9532e65dd2202438d0bab90b4f5a1bdc49fe554476b83a9c8abc2322660ee94e8c92cb3068c02d9ec68e45ce38dcb0cfa053707e0f8058cc87697e2ff858491235b39560b5f0a6ab314fb7c7d6c0585858d908334431cf301f6f99376ee0cf2582bcd0634ab0659a0d99bc20c8cb67a61929fa4bdfa729b21c417e19448b7e4dd074dafd14a09dddcf8eaa3ee3137b14598ff3334c2664be19334e4eeaa9152544e031173f10d5c637daa6f0422db2a08b7ccedd81f90fb05a99ccab7d4b8f0aed696b833b60e true +check_ring_signature c174f5292b1fa20ae4787722a51272a80e32068106e2b1370b208f838b8d247a 9c73e857739d6c3084346690ee9b1c7dd765c2fef46f787e1481c40d57750bd4 121 1a0d5d4977686bcd6441a747049d9eca15587a6b0497e9e41a69b5c4a5985e1a 5106eede725da9beb64b4125d4787d8aebd119fb481b8f1fefc3f35321cf1ccc 4b20741f18ef48e1510aaebff01cd64e5e8ed8ad4ff2906b0c9b012f7918ceb9 35284506ea2343f33d69c62058ec7baa8c4daf64f07304d32cbafbcc93146544 f4c2900c0cec827074537da0c1628de9efb0e401a8c9c10e323d04e2dc418637 47bded615e50fff96055c8fd13ede9e86c4a7641aff9ce4d52b0f7179ea0efbd 6ecb589bb68545ab1da6e16bd77dfffa9dd11d269406060ba0514ecb032ee02d a5f56d172e18122b7ae793c92fc2c9d156266616989e656468f7adbf10cf991a 56d7dd58a0b347ba27cf9e1aa12935835f712673c69ed978867d33f2359070dd 146190e7f8f4eaacb4f5c6b3c222dbc48d7a90cccf0d1c5ba1d0f46b437764e3 3a9b0c006734469d538995869f56ecc17f7eb3f57e146a2157256ea845be9e4a 8bd577e0260350615bd401a07e7fa2db3e8dbd95ccfbb68d7a5a6515c0c83c90 864ef592f79372c2f9d13aa4da50dc8dba345b305a3e29e73d4089008fde04ec 453e6c66493463f496d0ea131c6af51e22ac68f680f8e3640227929c156e2bb2 1cf31aedcbe0e6be8bc99d9c67a0bf30e9fe0b8f240b83c67e546415710d6588 6a6c95b79c21aadfaed25f6d945d42c1162d9452bc1cce2078b37483843bfef9 ad3ead18ca5a2ed622550228e40f784398747b4893596fffebaf395a27893110 78266779c977fbe7445c43f970ac117ff09bda073edcb1dd1470b4ef1e9c95e9 93da6ea0c4c618fb4d143f1376f2c90cd63aa442fab8fb521a5835e1b8eb31b1 ecd5833207ae38d1779f048ebea46ae25bb99f86535b4af89130930c9115878d 150c1358e500bb30dfc55d88990f82ab5e1e4fd447d87ce4046004fabe375fe5 d99f49c60051af2618e868c23ca683329cca8ed2c3b18a3cdc8de3d7c79a272c 8ed6d684531afe6bae22b553e5d33388cc16f66d3304f602b060fa2e1a626e2d daa0b2d6c4e993eb17473c9e1192cbd036f61571a95e218a40a29acfc0951d49 5d14c0898209ba06b022e37d5da89b1295449fc1edd73795cd50c542b2f45342 36031d24834e2329d0250dbc5a259f49b1621fcbddbfed7b71e3f642962861ba bb1c980ed85d3e0fe5f9c6de4fad5d7c954565b59bd59e10b22a772504218f02 2ef68101f34c2c519c5fd947bd96876c876a0b789a920f2b75ab19b3c994e1ed 72bce5722fd77533e0463e7319cdf874d577d6c6f0936d9f255a4ee0133b8f63 211d3696e09f03c1a8ab635161d1e2debd78bb29dd6671e564c772a0bf2764f3 adf9eea5c7c4250ece4582733b6327033c260b35d4d35034b482ebe734c12878 78ad95a9fc374f6a943bf78feece0a8ab3450205f622283e9c88fd76411aebc0 e6ebb574aaa68c55fe62e0143e76834f0f1a1e8043b1fc3f8db5b710ad8cd732 efc7a6e1feb0b9699789e0b71cec34fabe7fd71e43562371e033aa41cd77b6d1 988accab647501b0e1abce359637fbf65ba2a879cf189b46781b6d21e92f89a0 34667c42dfc011a3f5b02db1e97e73612896421c812f9e2a721eb7070b6e0971 f279d4910df936eeeae3169125039c5ad54ddda65bfd8bdce2e260bf49e3d946 2d2ad805076fed0e45bb5dfa58d1d57b9abb6bf9a7e07dfa5cefd1eceac9a98c c8688593f3a995038fc4d9f50d899b9a0451f4f7d14d6eb2be0a45da9aa5150f d4fb68d7f2c266afef2f103aaf54d8b97463744ef329ddf8368956d6f545e190 da855050d663c1d57583d188097b82fb58a623391849ab24c209f7bc08b2a6df 72256e3cf311a8611040ced65bf7ef406258a55a39e43f99a601c00b3283575f c20a0b0c7c063e6d13c9bbe495b56b0f2cdfb76c8bf7ab3b190c5bf05ccee3af 4247ac5c932632937b657e4d4b9433db1882be21ef4c81971ed21171b2968fac 2de14543941ea0050b4351ac6e5626dfb5ebf126a9b71272704f61f6dc21d462 e9d3feb5460ae43578f0085285b4233261a80477b883f594603b4ad124e5ff0d 55b8a54fcccbe6f339eb7543f10023dbdbc34515a464bd17f603e920701e347d 6ddf7dd4ecde66d39d1f1ca633be59e35c6f0d0cdfa5b40571b9933f4f389aad c31e8d39d70cd6a5e53d0c0d3b65f318e25bac2d8b07f1cb84e0c48b218e3b6f f9bfd8c7b58f0aeb2c679665c467a0cd2f5b5e39db21553a2beaee615ee12f28 99946efa16bdf9ba7c9a15be9391f1efbeee9279ae79d45958ec01fc102b6e19 feb333ce11153c260c54c1a1f4994b5525c0b3a1f707d44d00623fc9fe0ce797 fbc7c1933b9c6e0ebe0b9e71bf4f72b422d8cc75abbebeababab1f814cf9c683 af4606083880d289b1828e885fdeba1dcf6aaf5f9eddc6445a6c0d23b4c1aedb 881bbafe14897ee33137852d27de3d4453c71009ca67254be1042f998d6f8dd9 0e7926c71a54db3703992a1360c19135b7ddfc51793019b6eaf50e89c07da74c f2f6cd6b4efe5d820c40426431205d8ebaa22fdd3930012c2561a899f6a80c65 b19e036353bd395708fe7097e51f2e128f0af569f2488ac40c1efae9db9eae34 81a48002cb05261a68373ae8c40522811437f7f241e6f2d73ac7412eb5f371c2 46c8265096afbaad77e2552ca4693f6756b308017d145a1a936d16586d2eba7c fd6c4e247b160006ed2800a80e8eda7efbf4cd4f55a8c75bb236d944c6e1c03f 788f9f59a52083fd7a30bca58ee63a5dc8a2a31706f651beca12ee1c87783dc5 de45b49ee10da0038f33c3f57f0b1c8f5b95cc85a3eb01f67da934d6ccf61ba6 7d89dcada0915e6c83956159ec457e6e33b9e98f3e3d8bd07ea43dfaa8e8d8cd 66d165c4f1253f7f2581c8c69fc9f1ba643acc93b4f496954ba877bb9339b20d 04e4d5c765bf20781604671c4742a19bcfdc33dcd9e2d7bf3f1c13a25595ed96 947d2472c6766765fd2383a49c8d0741eb29ac1b0a6af03b3b0978e16979fd33 53ea9a03d199763cd4ddbf65c5631764070f2b182b9a928b2341f1745462be59 590946ecfd9133f6cc779c415e5c0d166e233d4316ff472330444e81b8699d54 51a31b0dfd507481776ddd262bc6a03a6fbabb406c10692a6fbed1caec8fafc4 a1946cbc7f9691bbce0c1a02c954f73f1a2ced60ca53d78985a575733439ec9e 3e76d62ab960ba8882424b7fe8ec42d672b79a693a4bf9d4a347275cba91bbd4 7908bc3778fc7ceed5a9f55f3cbf7fc43d23e205d40efebdc7125685fba5d6ed b5ff916023da412d14370040b78809315f4a5690be4ba12453e8c410b88a843f 4a286a981fde11e79569e90213668c458956e8e30535ea19dc7e7efa211149ef 503dc16078a72f007bad320a1b9f35648fc9cf570a77f990f09e0fdf2e013fc1 fa9be1cc583f943d82898eb6f31973725c26576831755ff20b5b34baddcbf2b8 daacce988b95dea33d6941b2fbc0bbb81bfb83489774056f9e341f8ae4c8dd0d eb97c66caa761ac963f087b7813a999d17177a454b3d227f611284ebb5980804 75f82051f1f667389ab713bc19be84199d8501cf76b97371fd550419ec57a089 0a8f1687afd8d0361042579cf27965a8d36058f256192ad4701ca3ce3f4c3a65 8148194795704cb8814e154f11266038be83bd1f2c8e5886d2b30a885e333083 cadba844ce59f4462f75e34dca84adb3b2fea4f171ae5a1c8e37467dd4579e5a 816a080b29be6276dfa44334ad8f431c1673e8074b5dbee46ef30d11a2529a4f 17ce336393e866100acf88ccefa5f0adc7e8f5031944603fcf1f40b2f7b0fe57 614144ec67f61176affd21ca14edb5dc29bbcf02a8ae799ab03fc079b4822838 c0acee4880b6b27f317510c50019a2a7aaf8309bcc2ffad13823b7190a92ad75 70e46c1fffbb829e43db3fb5e95114b06d04695411f3699b29c6b1e1f2305eee e7aca4c0ede73780a35d8b02b4276c566cf1d6e6057372f4576ac8f9d4285e8d ffab13f0f2617cfe4dc63e3713431349b23919c62da12d9b130fac8a2127a236 76d74846298d43a77d0cfec8246da9948b0a19e15ce3a68109966daf611f1e10 dc3d13759ea4daace367c2eeb13b5bae800d3754822321acbe8cfd54e2d43858 8be5d48a0772d23a77215ffaa6ef8f3ad343b3607ed4e596ba644b45fc97e733 a0497ed793d3ffa3aab4acd45aaec4e861e3f465dbfb4bf60ef7e41cfcf6f664 9ad794eaa827748513e7de30b36d0777b1ade1a9d063d12f4b13bd9558328c12 17d5811af3d58bce4f56f4130f441cc9449d5541c939faff4366deeff958bc68 b66b972cf98b7fca5a4073862fd325f48570d9a4816770eedf0cd0e6d021a4f8 ab17ec45c62c666314b6f51dd2ba64697514829a3b4c5cbb8df6f0209417182d 842efb7db0a3b4b1b2e35370c26bd31a9f2bdc26cfca8fd30fddd146e25bdb66 4fc267f77899731b531ef48c7bbb438d326c0562b4f07634ae0dfd662acada29 c9b8b684309568c9fbf73f873e5e8fac0006545ada84b637396e74f31ef2b38b 9592fc1eea2f0730a6ea9d08eb8838883b65eb7bfc9ba0e936aa8e85e7ceba67 4a6493690e7d7d450046bf8512392b8986dafb577e0f95067ed6eb0c6ccad8dc a59e3de911c63292ba77eb5d2d896dbd0c5c16143bf890d33d52cd397958e6ee 11824d531dabe7fea4fb029a6d3fa45cba13cff11b3c6845d550818ff3476b3b 4986816d4a9229ac870b53cb0c12c483dc584392c6f924bcca6c06165218db55 86d3f9f51e542afd04229a4eef1a8d6ca6e6f1bf16ddf15ff65694609dd32a4a b1a5595ced7a55d19baeb4555d0c4d3f44864cdad9ce53a4ccdc052b4ad01cbb 21e4df96b7d957263fcc302b1cd62babd775a7395aadeff58fbc7809a8d5c59c 2f837e6cc4d48e982f6021d7c8a8bb6f27e982448b9e5390131542c3c3d1300f f0f168096be50776f89c21b880e8704332701cb66d4a62cada18d5c6a7a2c6b1 2e2fdbd87f9884aa574255e3138aca7b4404e274c77c1e1aca30daceab9f371e a5ff2417b44d3e06fd9639b81cc19c21b43f1c6854bb29efcf1edb2ace2c3621 bf5b333f342715e1f268f3b2fde1e5efc2df0c399e831cf71db819b9f5fbe76a 9bcbcf26244ca1bb961588ddc227c23d2233a6dd52ccbfd90d05e079caf09c1d 5eab377bbe1383ad4f2e6eb2d55a4779bccb15c4688d36643ad62fed5271b27c cfd66295bf33a3d058025d9a3eb9462e459f83c68b0591eea72462682400a400 d47bbb438a5bc978c1f4e864bc78364926898dbaeee1b85f756c36c8c37ef6bf 9e7d9d8dfad27c7d0225330da8074acca5062ba99b35122d4732138c61e82992 3e1c957548a946f3386fdaa011090a0f8040abdb32bd240ab82b5eeee0cd6749 fee96f1f7dba3a3fda9908e0b3b90bb1ae7a175713ae784c4588ded5769bf73d 01d4b6e9cbf4d1fa0f84beb511e24ec14897e0efcad8e4b1d001640de666b40095b07a140ae19ae2cc96b3836bddb84ee9572302cb4cf0a3c64ee8e6e0c08d052a7853e7bafd4820fc69bab60a5fbe49a530a392d9993e564bb308b7056e360648ea78d96ce12a48cdf632d86debd6f69da55439f591528c672900e89f2462088d10ac2264d00ed046d582c607a69718ba983b8c5ff077dae5f88888a329cf067d7b4b12fb65b4d2195fc7976fcd23f5777842d00e881516d2b8b7afc3431f0759c6eb19272a7c71376deeff85ac9f6d95700b02e6f22ca1a4ec941e22a6060d6319f4c5e81b66a90ccc561957447fcc4a54c1643ecddc73187687d83bef5e03f8b75db30d1db67e66925a717971e64f1b9041779df9c5d848711132c742a5093f874c7a3f5d94dd671ba4edd56432847d83ef330e01ca514f39d1402147991332239afcdcdecabe64085e24c40a0dc51020bd16aa7be3964db6fe55bae2cb011bc4f853993f5852e0d8fc003ed5c8eb6ec2a1ff4c51baadb5fc21b13d5bc007ff044b7b619020027359e50adc5f1fc6166fe6cae1545b9f8ff630a158db8e0747906664e47baf953677bc8e1b5599ed916def16e78995399acd57a5ce52e10cc34839dfc81382e87e268570b862e145c9f6744ebbc98a5b911c2430d711d30c634eaa9c7e5858fc1109e8595e3f2aabd10569b617df6ca96575040bf6e61c01ea046087f79371ae5278f6a02483fd7960b2a28c186b33352d82f4bea0a630029325fc061556884ac7bc9769911066d4cd4f42e9b11e66044cd256862cfa2e0958c2d1c701fd66eff66ade14387beb927b7bf007f8c2143bf9e70908e9fc420482bc627bbec6360d026a1e29981992a50a907e90b43a3eb60b1051557992880d24797ce7e7d6f30d1863608fbf2f810d44940d38eb01377fed328edb2273b70c4c05c664cf949c10c391a67dc36ce0662d311a57159261d4788a08cd1d0e560123328cf150e4ec0a2c4b734ca381aac3b04c40151dbeee8ef2856620a644be0dce4b60035ad534a8762412b0358055b9b4c56259a7ba6740f4f63775faea560333d88cd57a6ba24406d2385915b346b5b23df83a365ed06bf46b4fccf652ea0d81440c633fe08911c43a05be23916132914a6f48201416efcc15a04b892b1a0df59d2b194094f498444ef0d07033da83ac656c1095100137061a11dfd4a635094e577502c45329f44a93c6582eb88467e171d980c8ecd25c2e22573c91ce9400d6d59dba33bb4004d821e82298df15f1c7f54364421127a1cc2b39014c67570a2d24ad333e10f1b3cc8c1af22745d851e3fbddeaeda8304142b4fa815cadec09de07e0804ad9de29024e790d99d14074661f9d1ac11cc4416ad9e416c81ed60bcb0a6b4be092b3ff8f030b6cfc8e3d0bc43a81ae57d22e647e6da1619df3ed0c97a779895317bdc85330a55911af96f2e4ea5bc50e3df2d9f5ecad83e4bbf807cdfa93b97f299f0731a3ff4f31c2c68bf0b6cd6863bc6667be3b6d591a7b2e01c972b0c6f2f0a73f60802f35580dceb4ee13e6f28901b76c95e019e5ca04c00ad06da04d336d1a5f143c47b210b87ee880f3b7a2c738c9f2c94c0d64d913b903c81be533e2610034f5a33c71f8d06dfc8ce8067c0228df3a6364cde129fc100445600d59b1b347e77154856995c81a83485d9ed683bf857b517f91f2390ef40ac7217e552d10d7763f6389bbed4b72beb2022c59eee22d4dc226226b35c37c0599202042908788fb5f64dd8319b381f659d3703860371ed1f1c50376813c3f0d44a5157634e0f44ba7fb1d275cc34fb5d3aa49aab59e34f8a6273e02c46a2f0e4df575305774db89c21fe86a64c221c412b31e8186a1101ab61654afbd0f4b03b18489f727804c7a502b300db397083db444cb71a1edb6830b1b87def40f1502ed2f36f1a86d538fec1cbaf5f3f1a28f99cf60706581755f97473f01f3266f001fc0dfb9ffda8f0425a4c32b409d7a5c5e68635613996517e9ff19f76c567d0a986a1f7f78865749d2ffa4e76cdff71ab41923bcd0e3f0e48f8d1628121051032b2f1eee58ea9f93d4ece5fc408232869e07087184438621dd5d8c39fadedd0c0e3f8b9f0d9e438c5ac90e9dee0e793dc311f9345baa7616b5b240ab9970e300f58cfae9e09d3655f35e42ce10b1dd5764e5517a43db8af69c2c9db5806e2601ed4ef0e4af068759ba7a5e85e7223f8dac3e32ced05f77d920d06b32a839ae0494f50d3b511079db144ad1715d62e2a1d7f73a24afe327e05c57c53933baff08f2ba25e0d6c3e1521e2bf17f05e15107c5c7ae34ff3fced5ede5255997c00e00bd11057fc8e622199365cd2fce809bb36ec0ac383dc5a7ca609120c4142cbb02d681ae42e72fa67c0f1bdc90879539d032de226309c2370959d4a5bda4f0e20ffc54f7604188f1cd14400b62731c80e413a660eabc9cb4381ab4d3b898c39801472aa0e75203ae07dec8fdaadc4b62aff83c4838baada5728423d1a504e40907fca70a241d7f2c0348b2e44eff1afba6fc60d67a658276661aa17d64c732ec015db6edd463c68775840cd4753f7822d790cc1645668822080677537cbfa2a80d4734a290fb47849fcd39ab22cf6d7bdee465ad71a2274b4b08784507ffed810a5e1189f210c0fb9ac311bf935b5c62a6d05b8e564c9c0b4149de83b5f6eb19060dd110806b72f9ec88fa4294dc04c5b834ce0c505931e48ddbda42c556a6450a967aef5cd0c1d186b5c1ce865bae0b4f8e704178d3f74764d7d660ef9400480277a1c5a2c513af39c6bd38078e28bbb95cd78fe1b6bd34e5e62072485c6bf20aa29ec9e7e79ad4980389141c3cbf9bf0b5c3953f2f605bb47979bc22e16d260a37b79b276b63e01d0ec31dd1bc47ed05693ff164b6a3df4011b12bd9d4bcaf01aa55d826564a3dcda60abaf6b3f0140bac350bb46a586c0fe420d2eb89695e0d63ce9a2e4457721a7d9a7772bac62f679b17523b102b302b6959e88b5a2a150a1c64e418daeb425aac84287e426e6383dbc8b8b6b8a49bb57ad4abd8757ae50c48069edc7c28a98e6ad68b3a4093f971c31d5f3d6e175734b6ba635fff96ef012e021cd689a9b5d1aef6839ff0b3fd665a7fe71467ec3c763a713e82d62c7f003f2ef78a4c605cc3c4cba139255d55c4f6fe48d4f360d04e84cfacdc4b7ca0067c649baaaf813c97f7583d79303b88aa2698738e5cbd972d4b81e1ef18be2d091b5a5f35868a4e00506f94b24dda0e781f8cbc4867742d5f81f5cb0f6774cd03b6783911ce891670afd2073cdc1aba2fb0990d4e84345567ebbac86af2b1cb00d015b64f105c572d08fede9e64604c614bb43689ebeb5a353098483f483a3009d973ca666060106b57f341ebe370c1191932edf8a8d66b02038c3f33dbca400cf1bddc87d57728bc5b0db71f3f62eb61e5e348c3597a0ad5535e265a4497dd0cabd2600e9a38fb8b33256069d9126c0f46db5837749232a40ff1776db49be00414964f3ea8ec5ba80f2ac74edeed23099eb0ef580464b1f898769950d892ab045cf4f43c53db68bc9c1df0b34061976c420a7b198267b0c914fa1cc23b7cc90a3dd1575495c041089804ab2ecab5591bc5e6b7f3d99c6f9cf37a098a9fe2c0012cd6513b802188276f6d4504da2cbc58f267af7ce07adc56d3cae86e31925f0d7472c8d801d7c8d6509f66cc141a583982ccb0633ef451d4b8b399611dc12d04f3f7d2ef94cf49d0f4c30503029e39cde94ac7f1240a65cc4ea0db92f3f5ea0bc50ca2ec9af555ba66e7e7d7c9bca6959e8f207f2b7e0f62595ab01bb30ed80ff938e2423815c3067f8b2b2216b53d01bb0a618a9d15e37e60375b3e78056c014d2b1c9f324a4ac63a82fb8d254a8bfd30fc7f0ad936fd80fddb946de1c229026d7d7e5d6cba3747dffd3a034bf659898b9fa06c63b72600920c0111ec71630ab103b3018b22d67c472b8b383f537fc5625055629b8b6df4dcaa588d088e520eca7e3f5be7c1633d5d595351ace1a6cc24bdb8a9f6e0a507a1d118e74acd8002dbf4f17918f4810dfac1aead9291846c2ca553428f7cb3a0786ffdf7e8c25a0ddae47570c114a45e84df5662a5b5f14e171d136d00b0005c417e64cd66bda500a98144fca3b90aa26664acdf8e197dc4c370d67bdabf8c921ab18c129c334b06f7076d556f1c829634fcf9666640299ff28d18db24856ee23d495459a504010a803d699103af465d72801452805d298e7adb96da035fee3a9347ad2e5da25705e72307b5eaf873474ffb77f8c652e9e5b84efa4efdf5e552442eec66b6a75a010da560565fb4f50c8b603d8eb802cce1da00dad67de0b7d44d6363d6d7491209b364d8113171336860f7017d07d2c42af0469a9694996d14177e9334ca830d0c4f326b14f582b0e36712446f1dc862f8a061d6ea2c1f5af9f692afcbd85851009c49f22124628ed9e6782365ba524d32dce8fa6505e286e52835a7bbc072130e65b1608526020bb919f2dc38c46ca0384795c498db917de5ed695a540977c708c1003f860dccc6bd6e2aafce5fea4ee20af119ab2b044dce4fdaf46eb2e32405480ad5388edec046db6d81ac4ed859ad9b65eae09818d219cb0db5865a041a043c94be664ecccc7d57bb5a87708b9bb13726f1bd86afab52762f19cb152f5d0a81287b35b2832b73f539f4372e7d95c4c1f3fedd7c31e791f16d3308e51e3005008aae45bf6373ce258f46c30f970a856ffa5a7e44bb05096b4dc75ddaf4a50bb1b26b9050b719f5e71aa4d9d7e9a0fea64eb913430700f6bf39d4058c32ab0ece4993a7eea32b64f573978f26eeb648fb7ba3f62e1f5308b4a123cc955f4607c359227755a57f473fe866937d4fa2fd0c49f18e509809da683817b12f146a0fe8acc507e63255beda126f39eeee5383695e958b681e8b503f3129942eba6906a18b8f1cf93cdcead83761057cce54fa55bbbbc5bc8dc64796c5a9db27bb5609d6ef65ab5c1cd3f097405a597e0b7412c04c33262e8296e00222cc6ca6ed8f0e9e42bdd1c7503d253d810a1e0288aec34e9dc1add8341b893ef5bd16d8dcc70e9f11bcffeacfa87ce10558b6b264088ebde99153cf83866407c2e0e266ad910dbe4403cce1cde16843b43d6122427912ada5f1f5e8e7b8d93c9cc174b7cc0d00fe55862fa07a45a8fc6a25d7169168a774f74abee8310a02f4097d8f01f033057bca2db6b64e24ac4384b71dbe5cd8940e43c5ba0683dae6861ee56fa7ead80356c47962475025dafbd3ca637d3c9f92170ab36cb7e74c3c1d5cc1dbaa16f10564a644b7c204ca619f2c2661ecd07cd3f895db60a3893f826106da0dc1476408bbad6ce38df6c0c32480fefa26185438dac081a091fc17d7e1900a46b5841d08d743b03cbb2d3b3f1f829bff4a96b3ccff2488e300b2a0586c6f485b676ca7059917e7ead3e277c6e19002b11c976fd51f360db89814c9c216d38d2e4e4ebd0056d22059cb282299deb7fd94be66d256c888e473405860f65485a274c3bbf50d056789fe3fa8aa53ac49aa3d4919a65890dea7c205110922f6995d7355c51c06e66fe39fbe22cae3a3037b36b802ebbbaab028809c9504a17807ce5aafcb090801758770ab248264f1e1390a489e83c30bde50b0a5906985ba0932876911120dfeb687bda55099fdaeca12936658f7a6a8dc3c50364514aaeda53b5ce5244d081f61068d256d4e228ea283cb2212b5f94a812a8a0094ce9f6aa4eb524640b10565a12f2748e48ca4071361e68a4614dbbfda23329988392c3700bf997a0e5003c093ba6d71b533ee2c4407103ac1b8941332d55c35b6b9bf3d7697763901fe006154040a6285129322dcbc3dd613ef513f7fc6b523b0b96236b61088608b080aa5b4d391df831010545ae96ec8035f1bf6e4ee0ab10158339b315231fae89c0a64eb293e52c08e955bfed54e05dd0f6b370b1d4633c28f1c9c983ed79d4b0001e613c1fcd85499436aa7dfe1912ad6d56c6a13f2c7fb348ce479872c4800ca07882421679ccc8c00dbcf8735b6546c6a24f143c46f99822de39e0a7327dcb8033a2380b75fbe9a6ae223d6b93ada872a77e9a91a32237f1cb5429f12b67cf20c4fba7de5631b2145838340d6ae36fdec9fccfe0825d2778a0b226b3d3935cc04a82354716f711bf401ef1bdbd990b66c29d8d899807f52eb50721a866322250a2f2a3d4bf2ec49c465798a8fb7d6dcbd62d1865e02e6bca95da098035e06cd049fe63b2ce1ce3c7096b6346fc9097dbe1b5a452f1b1959a7918a1e3ee2b84e09517656b3dcb55b8a78f8749a2991cce411f4387d0743188ca9830636bd22430ccd1023d0e539a1f2add7f67011c486c401f5dca344ca03fa3b17d4ad7161840d0106cc1c162c2035914f5dd86a57e11591dee2d5df666b5131f3e26c7cd31b06d6e3604cfb1c1cced4d0917ba07fe98369dbed66067b88258f1b6e3c78d7e802cd47f119c33ae3578635265abcea5352acafafab973dc273a9368f34f0a4e50359d71bc3d2a4d8c5f142b3e649bb41c3afabcfc6e6c6c43dc51b3d05a5b85a094bd069a123d78d64c352a7af232a5940364df6e0901c79b2662d1e11348f2c036abdce755e296a5c146223e80550cf1745aa072502995c66e660086d3dd7ff0e05d8ded539cb07cf4a31dd4323d5e895b3cfc45eb03da91266e7718476dbff0b19a97f25922141909e78cacdeaf816c7a47d6460a6ac1e3ef4973e9f69373c02ba2da040021d7117d7cb100a3217594779cb38b0465f2bb5d1130d953b870d014cdc60c21b82432eea64a88d56b2b51119eb67ad21254f27b49529dbc92c63085dc7b4c0b4cfc349a33a2851979d89961f84cb92f6aacd13d3d27431970c390df114cc36a43f82b36eeba77553af253571148ee9165aa653f883c7d7eab9b60668a6d61400845c5f8653de933963896c2afe8855e03793c635a06df3814a4e0b47d49162da626bbf9578d3da993c7ee3f6878656888065d991fb7c6db0f2f20f4570ae1b368b64436e66d0ed1ad840d3e36aed55b4d1c9bc4746af4c2d78bc07faceb6af1beb816c9444bcc327fc26431354fa2bcc895b969a5b8762e56ac708c1acfe5e7adad6f8b39b9af633ba0f3bc50ef782810650dd58eeb87ef21b9c015b70c0f8fbbb3ae4b6c01e976055be23b8850cdc532678dc2ea736a83dfa5f03611566cb8422bcf17bdfccd80bf1eb5e2ddc6f40178ac0a1809e1f8eef717e0ee2527ffa64795c47480b2f59a079cc31bc0e93d411cfec3cba868a6177323e0eb05ba131df40258c28692f8b7adfba29e7e6381b1d1771971910af6f904c6b0ca4a6b57dfd94c7ca2ed8bdf40830e96810b301378596e5ec27458a71aa76100d471c9bdaa46f666b68d0956e1c3c83c0b570f58fa1d4d5fa24e0336e58d2fe02cf11beca3c1679b9374bbf14d54b903ae641404422e13aed09fd2d274ab8c104d447819b39814e13150d4a6feac4253763027db7d4f859a66aca9bd6abb4d80d7c3b5f8194d8ba2c62d9d3c00174b5762107a3cb71c4a52d2354beed54baf007daf3f8b0ac7f4dfb3c25d75c993694b86840185a92ddbd6bb566aa6415a9e20218f4c46b8c2d8ae97feba842afe03c7f1234fd50cdc12fda278339f04ec3a2046f74fec7737c5554d449f7e0a1d076a3c83bca80903a17500f3bd0cc2579d10ded339baf12319211824df09a0e754f26744a7d97ce98bc93d1be9df207d9eb05e0674d834778e7678b70de4e4d9a5b3e1222f4ca55409679d671315fc519c60219af3ecffd81a045387bf31951af2c54bacae0f59745c54d606777dfd24d3b067186fee4f062237923649c7bcfc8ad4970aab7834d0f875b7d40495213a3820c821453d3256bb7614caefc3faf20a7865a93bf2d3e69056ecd002993ef33ba043c8a4374d9a5ff93090c5fd97a1db30d5c37aaeda054e5b8bbff6558cb3afd02cae9d782d897c5b60c9e7655b0851af377111b6be0f6a834c591a013fa4b9b0c8e19221bee8614911c2bb7d38e83c415b791f48846fb29a038be9d1ca898860c587679f1399fda83916fa4c9198411b7ee84ad2ee0b652c712a5cb310859d402e1adbcb35e16e237cf2ca8352262eedf0866c66165aa2241444c4e2147725b02987a9f60f0361b97c5d68b68ae2bfa6a159b6a7962f2d43774e4bea41fd0d50ca3d69bbea5efe8382d88b0c4eba5f1347c79586c43e65486581bb624ff2e680f4aaa46a223a33d854fdf8eda5ea825f94da65d908ac52ce8fdeab0c6a149c005bb20ad1a3593471fc89fdc2afc7a4673e04880cd2df0a2c60a200bb94ab5270d784b13197487676eab4aeed1c80ec13158eb134c9e2781f5180694739dc65e0e2384ecb8d1286b1b692d62cd1bded8cd2be724823bef3996776a1e6bda4dd90b747cd7e6c705ea062fc16849b530d47cc8c305aa662eb8fc5fed84ec9fb81707fb2b618cddb2253b4c6358a0a96712b6c30c8ae5dcafc9c349733a3a60a55b0ac96e650a852ed9e4e4cb62e7c08c21359bbea2a17fee978d6ff8aea4c046bb052d012a5579429b7d1ff027bf9bf2ff71d633221d32a665262d58e11bdeb9a807bd15e2bbe95ac9509053de7bf33bc5448698018b823579548189a2808a5a9a05aadc06c1da37ffb3d95098950f3fddbb0c0a5b41f5200cb269a9076b4bd34a0e0b8dd8bbab6fb2d45f887f50be79f23e8225be0c5755cf48d3de32a4f8d18e01b449b082dcc40f2c7c411f0df544b7fae3452d5cf4cc561fa2280da739bace0141ade6b4bc1747296f13f0913095ec85db456925ce6eb9c277ee65c7398c2f09b5d4d10aac333da09ff1c95c9ac0d93a115084e2b86ef3a8bbb834593c2fc509e36ad3172071b7aa1ebf62ddef6383db0ff7af01a16a7ca22ade5557e903f909fea8f7254d0474b3ea19f4533401343c56f3df5376d6c3d19d2f2e1af443670ba61d80f9f98d8a1bfc718384528cdf2b0e8f9af15dc1e18703b37b93d1b96701156954998713400987f6f1618f88cb47ae27bc936d73e42c1c678f47449c33022e5eb7e7222738000589203ce046de59f27556ed226e8e9d19c6b54cb541210b9794d3bf871a2e8d1e091fe326df5df3c26a6fd5cc77a00b20c2ed56cd52080b55a7686a67a83ac5fced8efebc1832138152fbc10ff3e026c836dbf2135bce05a2b35c48e0a53f499d545c73510ff47cd33ffa06e264970428aa4b6854f22d0b460c223688c4a16857635f4b5af8594472a36bb3162b0a78d927f09f77690c0ce65d97bae155a02c3cdd39183d8344c3faed36823888670da271fe316756d400984b9822576b3f44f66d0fada0c9e03ba982dfef094b4758e946bb8a543d3c0557c94dd085b9bfd2b451ff00ffecd3079843230df599415b7e88bf923df4ca0a8881257f5785db41837e0f9f1f4b8c568c7557a1ce580c27606184ec254f51080b1d13dd54d244b380e3d1b248935b12ad697fc2a0bc0552296dd5240b88f505d058d2f0b1f9d5698bcf5d183c62f8d8113c0de57b7b657f4a45fb465ffb1902776e04e587aa9737ee324ef3e65bf7e6f1bc388ec1f390281f4b63abde3d590c098d865c99159cdb28e3bf432219d5e2154ea7ae6b8387521e6f704d6b44570fba24aeed1f614030fea57ebd7d1c0aa57dd5f9634109a31ae0606af27b24ef01377ca678f8dbb019de0ddc5e29633ba10e1c453ad03ecc255e96aa000c73d7025c2b5db75e21ffaaaa81955c7a205dfccf63f573c65a2acbdd4307cc25f25a04ef616938a35eb7d7a7b19fbf06fb1b00af475c2141b0b776fe8310d47486ac02e5a9966769a093a324907da0b06967fb5fc5afe79ab0b3312a3154cc75649d09872e2d21102733ba866ab731ec158f9bd894a48d168cccf601b331ed2b33a90e6694fd8a7d946ae30d15d988d7f9d0940cf670138ca47e0bb51af7a5f0226d01d9564fcb8cd6ad25a4a4d0dcd9318f820e1ffeab674002955726882396b2eb07de5e8e6ae5fc168575cfea6f2a74765cda5b25a34066bc4c1f5e91213c8e9c014fa25848b398c192e97e115a7d5f07f7780ced7823bf53b0a6765e467013f60a7a8a976c8a977e6a6b063f91581f623901ed780e3b103784cf6fcaa9102ace0d98777bbebb41bb7a519a34150a7135586e666fa610d745be8c47c0cf2f63c207aef788fd458718412bf8b3f93bf7ab0793eafa9e86fd78bc17b8693b33021e0dda4e4fd7fde1ed562fb1c568515f84d25fda373895aef9c7a2c2723f2b8a430742258864a7f87be96c66a133fd0b9ce94c5fa15a5b19ae646b534698e6118a0545d71427682b9a39ced10bcc803839af64def08a1f558685aab73611dac4480b7b7745dca8eb7b4d9a5b325689404034e0668444fb3d3ca6beb64dcc71696103697b83abe7e2f70ebc896e4de5ebb98e50ef208482d2d3fc76733fbbe3544c07297d63991e1ddf7834fa75774992e1000288732b22c794bd59ff28c6eea34a0bff080b6356724d9c3c1b2ee6008beba75c8a86cd68159012f0749059de728e066dbe4e9c7eaf24786863dba896edd8259d1fd5e3c5df45d66256f8b699eb4b0907e58b880af2c257bd83abe39cb057a333520961a0238fcad6f6882aa5182a03b3f8c08d93bf2852311cf275a8cebcfd3e10a0991b8bd77a4761b7d6b3b0140253c3bd5319f80b6e98ba569168c85b76c405a22cfd6e5d3e26845fee02ea300d813df7639f66a879161f46ff0bb7a0de2942696ca9e1effa0d427b0904e71f085c1f537a20e3737d2997c2fa351aa08636405224d11fa64b2852c4b9422a9c0814cd9c189c147bbad73c21f03caffd6ff05470c86c0da314714308e5c344dc06a6a0774fd105f0e84df4fc2d1a68bd4b8c4c249f5916dd116346170e76c5d805 false +check_ring_signature 445e2220a8f86ac5dae681a2cefc7c3a166764aed5e33013f6a6b27eb01f1c01 4261c3012e16e6f0a5d89a458b63525c97365a6a95968705aab396a7779739f3 143 6cb95289b05f1b329624d6f17707c87970e8e0db6ac2061c33c75d2aba30499c aa949694fc5f552214879b3b0de12460c1a6114ee6e8bfb1b52552dd87bd2bdd f122bbf68e4fc4864c2e5f2a64b5820966ba201a73259fb2bc950794d6913884 4d63ea4e64f0d14959d9606a938d68832ed1851b6059590a329c987c52c91b2e 6548e14b63d0930b9c25683b0ed38df2f726f4f33708d568c58a5243166c8d27 35c13998e8f53c6a32d187e2903dd1552f62b44741e62e0d37678bb837e01a03 e04f14b3c716605b0887c7435efac8c2778acb8286b38cd4c54fb4e13b7d6b67 1c0757efba26671916ccdd4844f7b00aaf0ad08cd38ced0ffd2502d51968a663 48ab5f7282ace4ed815d2fb07be71528f0e7e17e4a5b2f13547f1ff67d36f0da c114a5d72f7ec7b1063784ffa1582b8548ad20989903cb60bcb97f0234acf9c5 508d21e1aa3f5f23a01c71537170885025b3504859045eb49db56c8c08f7995c d835b6dd771298c8c6c2f3c2c2dddbbdf8fc5ba65a0dbb10837a9089ac44c04e d9f3385a6858f0e067cb7b44523a08b1772b188bc69122b6f14f76c009c943a1 751b4448578393261b8643b3ea1d50529db11bf2837370523d2c4cde915aaf67 bbdbb9aa934e6b8e28a91525f0f866ced0359365c70257f2c5aa6554fed6bc64 64ffbceebae6cf4280405aabb68b4fe687c421f05ab12a693bbe80c412436625 1736605ccf7ec9ac286904b74b6cf73210d2985c88d8e21d63dfd0fb37489e7d a423870d0e9c12761779defc10c6661a6fc91baefe975a1c1b7d8c392207ac2d 04fbfdb8c57e10b323b7c4977ce033ceec1f40f23f4409953445a17b0bc87476 3fb435c5917d0fad9249f8642a16f1361cdfdebe96304174521d71922930b982 bea8f9b95cf0f12984fdc0c6fe8b72a70a04284c35895d48e923fc9fb18e67d5 f80c80d8661e43aa2fdb82115858dfaf52633d3844dd996152b6156249f877cc e751db8645e3d49280ad8aea52bd2a1d82c4528116edb4b382e524ec89cdb61a 8d598d51cf13a8a46f5c5321f60d51060b28882d8a8fe6b9994448ddbe12bade c8360d888ff25a41857f75cae92e16fadb28343d56af1e474973672e0f51365d 859b1515ef8c6d954b73259154fef8e80312a6df6bf553fbd8c1d3bf69ca3572 bb31878aa5062d143f632f095824a94e8290180d2a77296862a2ff1a1bdcf23d e6cba0fe169f0f4549048676a31c5755dc703a511ba3315c83e04d535839f296 75486252443f9568bdfe1dda6d1f11a1a9b744c54cc016969209447328bb9258 34f19fc8ab0559db637f4f5fa446878678b591d65de2a553335568b4f85a3724 7dfb3bace5b9f307c7a2b68f17313b432a78a2603278ce9eb1032c21f57d201c c5836241c6b243bb766601346054d5e6fa6752fa64a58b4363ea1dd5e5baa2d9 47cb872a44bea7843b0fd282a41ebdfa6e73dc9fe26dc789be8b0cd60b391284 1e2a6e53da17215a4999927861232c515e7a4727bc592ef2d1827b0d7f16e73b 35df8b9e7d4987d958271dc9cd80ccdf3510df0ea88d36e63eb1fa974d26164e 5a595a517eeb264a8ae5025330a5ba25d9d944e3642e88ed568cd566b955a926 906fc69f8aa133cf1fde9b069da33ea7d94f57b02c4aa0baa4f35f02a4fe869e 9fd3d7c662bc4377261fbc0711e45f34e6be6e465f296ec47e6d63619b224ab8 dd6955dc5c71a7a9647843749db5b99d18f7d784f8ef2f34e3261c9a6068a538 12cb067f61b8cb5f08d7ea16c8f5ec5e02f8a467a95c40c72b8823c3cbe8e4c3 becd243b7fc472b59273e4f143d726bb23725d915496166466a694fa2fd08f63 2628a1ad7f158c29a84e44c006180e3cb7b2497e5d88173be7a80c0581cda76d 23685ec7baf88318ec2a931c853ae27446d209f01a34caf7004b367ab85aed37 c66c2e9838990e52dd949e2b82c6abe354d2c9a90131b55d65b6a6ec42f17a56 5089ccc39f08f888e7975c72a8eb9df330354ae09e90c0b65d2a76bfc6ab79fe 75ade3897646b420cbb7d4cfce57273bce62a212251bd205f5ba2353e968e4b5 c8f9c9056d748d9dbad0805107fbb212c6df7fd424c6b2c0381615b5537c3f3b 1e22957c4daa7517e1f58730c18745ca4387068bc5df2b7eaf0bd1fcac480321 5ccd05ff041e65383d01ef8457209d839177871b0aa2bbed210987395ee7cb81 cc516ce765e1f7f4f6e9be3d594a0165b3e3a6e8fd17253b8746f0147de900ec 31ddf6c50f51fad842caa31b7dd81df1a46d27f6dfb42d8801f9cb907b855edb 00026d44ae82d77aafdef2db78936fa5d93dd06b8c0b20f2844d415b232d5393 ad175a2a916d27bee9dfebff4702348a8a05390694c5e8d79d7122049833672e 5bd91cb54c05421ecdc035e6c51dfaeaaafae0e48a57b6cb0f63c0eff5925330 7af2d2d62ea8d18860b71515149d361401a15b88e4f2ffaa1ddf5f426c4b2a3b b9ae298a263f367acda6aeabcedb3419da653427b379098bec309d1fea804565 697e467e4e27d958eb6d79537725570ec46584d2a367802a1738f72e26e72eac 2eceb996c3c798d92cc3aa476e163256e8885fe15c16b699bafaef565cf359fd 147ddbd6b7ffcc2cdb1bbf73e74d0cdd82c5c8998c2e2740beadb824c7fbe631 2b7721db2b4122dfa6e8b5f4ae6b9057112b867782b2d9c05c750fa6e060e9c8 768df9463a302b86f0b4a6713030eac1e611d2bddbc807f3d9344b9c21c22e1d 7742ecb2c929ebf84ebd3c83af9d849d2553310b24ddbda13c877f2296525175 878f558d56576c529753564cf400980ed0c5b79e16647ca009f956ba63b634e4 86ffb18e92151d142448b8dcc88f838c46e1d2dc27424057b7c43797a3bc2a81 5767b69e4d2fb6e1d516ed90a3d339072d44f92f1d04c5e8a0abeeeb2a738f43 727c16907589791f276fa5e676d67f3aa5a96b2a5d1e5f2505445fc790584007 c9a420c1d6fa2190237a752fd7587f06b55a0b980c2393606a70c0bd3c68f861 a45b9c3fb22da65929b6f2e69f42c32378dd57b32e43256c1a7b4c143e72a18a b9b3a5386d4f37997cb17eb6290eee0834cebee6c164fe7b1beb71778577a924 4fc8b06a9945f05f73f2eb0ba9f10f68e59aae67d70b43ee1f53caf7dfc25c18 840824b4775d9b314ca3ad6ffc9da15d41278d8864e656296c843266825a4105 968b75e00cbf5c0f5161946e4090af3e678962c0ba7f9605aef2e2afd63ed0d2 ee15a934b7011a6f65d3310375609091c83bd15d97c3f0edcbd740cb28b7c0d5 aa33285a5d5ae0503459d940f67bf1a70df914be0504bcff083bdcb59f53fa28 47745fe6b46bce87e11932f86c731aa780c6813364b76902e5877bf3f6a47c2e 5c6ed9a7d783e78635c0fe7242fd6d4f7bcf31a3e28d23d98f89139300ecfc9b 69cb183f8395405d0d3b80b4a7cd0c1212ba7bb8b96027c0ceda536b1656055d 2df77557644ab0c45fa1f6a0336185fc6f9800bcee8e38518291ab84755d741e c1ba59df73ed086a32018c36244e1d3c63aae744ec611de022544f39725d396b b0c65c6d36cbd7a1979b0232b3922d9baa74839b0e28cd313b65527802f66f09 cc68bc4f9ad1ef54dff7d4de33e2728bf75b099dfbbb42bff9c325adcb2c6e58 23d987aab497032e0116444d955e19ef22de1e5a34f34c1c3f1fb9b2fc9c9fa7 a698288d04f7898a5f8292e816367c23b3daf73265d18d2c7643f8c2306871cf 414224c96653ed84a608a4a5ec72edcf1b03027e161de1723526db2404296ad3 e58a8dea7d14b99b0a472a4d878ddefe2c272a07a0c1a8902077864054e3a74f 37ee5ae1f26f0ae0d44637f0e0e890e78847f4438289b779b306517c54fba77b 0555d226234fbb6fd09e76a04468bbaf627042bd80c5842417690d84a1c2f12e 07bd7c9c08b53c3791b5171e5cfa4be6b7ad47ef3f970b5876b1064ed438978e 3ea5048f64b582acf3a89ae060450ecfa09238397cf55563af99206d2d7c3a81 0ea696cc31032a884c9cc06f49543224ea1bd92f19f5cded7588e2f1487a8b4c d59758584157f4ee67aea1271b206bbee8f583ec7988008a5c3fa38178140c14 4c3b0490879f5a192638ce92f4a1d94168dc48f22991f18a21134bff87b253c0 ddb4bf2803905d84706691e07c19a0ef7d9b4ad4c671695212046fa5222cb2ec 00faf7cc76224253abd3857ef1e5a65cb77ae2d8beb9dd21ea672aaf0c372f08 1e5397c9cbf5583a293c42e6fe3e070f45d0fa6c691f5fd5b025b2e7936cbe7b 9ceb46e22fb3d29ad0041b5908e5a4118ecde1b1f78b7339785859c3476794c1 979c0425042d7a10cb498fbcbd785a05ac718cba194e040d3e19f3742f084c4d 5804aa05e8aaa81d3194237973c86f79d786fd20e248b5876803c0a3b04a88a9 c3b588e9245c1dea78b39feb5b3613dfd99ba55c23f55ca3f676ee5dd7e42fa9 e67bd1d2169ab7616a2fd933f25f7ebd5cc73e3ddbef8fca6369c1e9203d7784 2764d5cb715bbb96439c2fa2298ee2034f7dc23dc4ded3e3d5ee17a155acae64 9593f0a99c53b4e436b7bbb8696d3bed6e2ac761a4d9a8cfc0fd147f61a0410d f038f2312b79794790dfe61033a024ddb5d70dc7a9895c79fbc56d11bb538c1e 2b6cd54c37246bd20d629cefe7dd4433cb04393060844ececd31b91b7e851bc0 857aaeebe2d2a1922fd8ed2d73fed2d2c893c2724449a9d1292a82a696a90f38 4854972690f69e09ffae64b289fc7aadc2f2a2ec0b31eea48d5b31d7d749f824 76ab3893c60cdfb56967678c822f56929420c3fd2465c69b7bed0ed2ad16049f c43e0920a767b4ed4585c96f7b05ecd6afb7368004a3426fb2d66d4b88bf38d7 63f9ee2ff7c328046e84f8fd3b4eb1f1279dde1f48fb5120d6bad8206b9821cc c15b9e0aca27948dfdcc536ca71cddc1dce0ebabf92b753c49e1863b5392f137 bc6d6d006f422791f196b6bd7ad26d73d188a26d144a9556c1e02aced17a1f13 30a3afbad3cd4f426d7054dbd9fa27dd9c69be6ec0ab02d24be276a7dfbcfd76 f2025bfdf8dd950d9280e3d3a96cc8bdf7efde6532fc56adba82a88be122992b e699bb52887c09032e33478464a203a3e2ed6f49b20b11afbe11133669db363d 86d579266783a19d348cc719dd9ccc2cdf80a675cdc57bc2a9ecdb720c80917f 0c1f8287ed033ad71db9738a8e960d98554b2b7fe4a80f0da88e563104c5f135 0bebbe853092b59b8db9074087b6e532ae52995a2fed35d2649236edbdc7b713 b30ded39cf9297c4ecfc3f9e41abf1b8f6954358535877dbc487064e0a3b3920 5a3c50f91ac6466aa752bd5868e5a5d85fa1f393ae6018d7c36d7fcdbba47acb f37b4dcb46aa9b591da0bb685c70b853347a1f97283e8c6934aa19ab1ce7e721 8e87034722bb7b310334a791d478671d722dd349da150c12765b251ef974b67e bcdca4bf92fd31a8c3a64acdc849681d709df69ea0d0d0f3f08080382f8e1272 d316f7a6534863ae1d52e520b7463504edff1c59518eba8c617f75028ef5b562 f8dccbe831e40abfbe73c311fbbe4f5fa050dfc0dbed76cc6f3db3c00c6f1e66 4d47b9d1be1ce0b64fdf4d7e8393929255cb9f8463c6e39370c6cde6cb5a6226 5077b5a491027350669bd6e470b904e4e2fa6224f2a6b9d05c8de4b39b0f2497 cc75a1f882dd18bb5c6e04e29d211d95579c056e723fd5115f1bd83eade20b27 e1057430b8b18e427ee15cb27751a1849d111fac06800da28034594f26f41045 90a8d4b0af2b568d0454186dd6f8857f975765bc9c3b65ca8340baa04e1c2627 f4a62184f39341dd58c939c913953c9c6d7b7f7788cc67d6215f40a840b8eeb1 54d42d49f55713fcc09a9eb0bda694a6c77b997d6d01ad7cda8bff4a95a17481 3f9193df1cfdfb77218b95b4902a1623fcca576d0c97e6459c01fa3b7d03b970 bcc1d57704feecd375187daeb104f24f6f0944549dda9a027691f7d76e32fd1c c99fb299fda39566c26e8ed0246c76d2b036dfa116ad30477ac68cc799054899 b913280abcc3131428ad923fcf749dcb24743d3dd0a0928557e404ce776ed21e 0194f686051779e1492d8a47e54ae41407e987e8e354f5af92516ec09af52493 c952edcdd372e028402d9c906da0f24158b758b5c832e6a4b31bbc1aa8437fa8 df8f7eebceb6164ca60e14be0a8844b22c5c3914ebd0032ef5e93f972db1d1ea 4b0d6f197f13aef6716f9d2f2732219ebc5d49c46f90224f081b00e91b3f6ba4 1d5b796669fda6cf16a628fa508143126fa88e205f45213c2964481c65bb740d bf7685193bd86565e564136694c203f0eb079c0434ce1fa896ae4f142967a462 8d0052c6c3dfb2a7a3d0fba75849384241178cf12f12a5cf2a7a09c392d04d5a c5e6f4bed0062b2ba6bbe18df4e90cc20372b22a9adb85b71db8856f521fbc33 e39a22b0477c61e901b5d8cc62efb44b174f808d5f411e8ba9c877cf8ecda500e04126433fcd0fcab90f08edeae8d49b4294652ea7f3957ffdf84c4b3942b001d7468544642b61589b55e10ac28fea5505333955dce9601332354eb53ce2d301257c5bac1f902af7d1a0b8f4d569bc2c80c1be134ab63f96aa24360cb2f0170d6d54941f7a2ac5397449642cfe97fc8d32c5dc47fc601e10683bcd1c35f3380d64cdbdb23e7a2a11c18017d4e4d07f299affa576e6d1912a9af922446e47550f0d3a83529936998773823e92923ebf70f4edf840c3b5fa846594414cfd935d06830f84910c453795cbc5105f16f8b919c4c1bd4d2c8bb6fd5e39da18d1113e0ae4ad512a116c6defe80eb38355ee5cef065721e7c147ff89627f4d5a9a281702ed486775723725bd3de892360e4cf96854e839fbc835bb1652f8a28cc437650998b1a0fad35aef45e2f20c849e18053814646eb1abb264401102d0e9547ff00ff4aff56d63067c8daf8c32e87307589803722153e13ac43cae4166de7cfeb50af0e6fade5408474ae67c39b0378fb4eb78a287a5c5194f8d790c79b3d983aa0f82ab0dd6e15993183fb8bb4b2b274f126a627b0cd45b286ee85284fda1122b008d3d49188d19d1412fc9e6180fa2427eb17eb41c34363730f8fba218e933080a0683a94e6a1c49a0538310026602b09ec0c8f7c90ce666dcb8facb35e3cb0e0189aa1b20fe491574222e86850849faa51b26c3d3f78086628fc81f0bf899f40ef355a43ff3e48fc5419c725fef6988bac3b2a078befe715f6ce6908e6367880bc4cd9afb4825e9c73237da98ae8070eec0c0ee41024d47a7782daa0195775f02804c5b14aecdfb64327ce51d9959c45be41e01b1c433f9ba3a3a9c93012a360fee2afac62d0852585cc4f520d73726ce7c4bd3b120a7969345ffad7acb34a10c5dc8bbaee63c70ea10e6483046cf7f4d4c2afbc7568df55575e431e3759b1d083289ba1dfad53974a6ab9886d47c368814799e4a3e4228f1d18f1ee27db89007f9bb0b06f609e87e839445e33b38f4bcbc038e0aacb1bdc42eee1bc46b428a02b5345b20d6e3ab5d34b30bd60bb829c66536a74432f34c4abecff302664ed100cbb1292da55704d39b51183b1c1cb46e243d11035d565fee4a6015e1ab4e3c03125a27d229dc9b40acb5b4119f372c3005a34b62585a345cddaf60027cda690df4ebf057a53d2e748cef184ad16981deb2b1eff77d38e736b94fa7d65eeb3404eed39d0743b5bf8a379666a44466c6501a22aedc01b7668000f73b5765a68e08e1d8910c1cc2a82858ebab16d9217a6e707b3ad158ca05e44d62adf88e96930078c2ad814ad28cf780eac05bff4df0fbc8992628d71c6f5cbfdcc28e6d6cf405778f282cc908285efc65c44636b3903ef1a63496cce3fe99c9f91c4a1556bf0edbb6bc49882d551ed374e0f2c6bb50a0be973f60516fdb173ab6fd42d7ab3f0951353ab60d914ac56b936d9f0a5c65a9347c033fbfb09e9f37701d5db1341a048ce3c7461069f911d6fe740b140ca6808612be2e7d0e6788e07a3cebb315790b1f9ab1603bf800981f094c672438ed8571a9d411cc938e99f0ca180d887cd1030438896e08ec0f7c2f8e13696ab3f34f8a04aca67cfe0a50584b5d870a4dda0a5dd14d47cba181b421324cbe5c8bc3bf8341e4c8862a6e57a1680b740305010ad52604e5f7c098d6529eaa4362a54120f823fe13b16b966aa72a15ed67f887023f66dea7a2dd350c17ddc321296a4af6f7a7a0fde1f1a8ab1890e9f412e159056bc1394326b30533632b13fb799682d132f734e00109984edd43e571fb80a00fe864bbf4b8782d8c8f48af1cf3c87355718680f2e8924d513202d01e29f242009502b89d09d4fd01b673eaf6e7eff8f68603cf01992ef4bcd323c7c58a59e20b9a903d1b223b8d96cf2a8ca4d76a70c3dcbc56e6946f3acd0a5bd4ced20b5000faabffcab0f0c7c19928a2c22de5527df77c7b3936a6a824f14bdeaf68901e02a1b2d151b47f294b2fd9a985d4ba23d42bb0c4cb6bdf8414223de658ecd4b5022afa90bbbcb357e96ee5b2b66b147addbeaa1c9de4e6bc016faf33ff72cf080031596ed61a5c1cb00c075d3bc19e3dc8679b813f3ff79c6f0678fa276910730810037cac71b5f4b20961c64a1fb8d84569544a7a2405b47255da4a80a6cd940ec58fa7fb6923288e21619682544028b5372a88d6f35890509cda8fcc152c11060a5628efbfc55f4fe4b004d73115e115fc3034c5cc64e0f7809683162c93b40a8289dad2ace1d12a69b58241959ec8d0bb357a9e0db1433b6e0fb35a3a858902992aa6038c990c9d9ff77778b67230ef0d9adb2d3972ee6b8d311bd43dbb0a0fe8286a89c0db7d589fdf88dbb85bb25c3b5de5fc01655c0e3e7a96fe82506107c0a469ed82ad62a6c2faed2e58dafe870f43c8b2fc85e1f31243561414fcff085e055838824835556542d39796af4e0a2d6fe2642bbbce6145975b540328ab0e105acee865be862eaae8ca5fe4521e89d7a84e3478b832b18a777d7b1c87670df2d024420e9db4022466924a5920609a5f522855a70e19ed59b845865ce5d5006b8924e81c7d8466c4cb5c2b02199ecd4048c16ca9b190f5b90b3a5780b91f08a24d8b9846311c0d6c1b26021ca9fe1e36a61d101602860bc1a1ec026221e3019aa2fbb1f7a86299b174b806d03cf53cf1fd28dded81cef41a566f9e1b10930ced345b56e188ba70cb56354c1e6ecf692cc4e18b1c97d828e4fff84452fd7c0220523ba9b8726c93fb4f33160af2894063179ced7f96b5210cb51ec66cc4520f27cca04dca6b85011943997822bb1aedc666485934cb7c1baa638965ddde1d029e5e96eb20b805a66b2ff5e030a4b08be4c6836b848a84fe0a8609ba5a7bff031ec935b5741942992d1e9281ca34a9852c1fa0f675a4afdab22613f3d732150c14b91ff1e5de0b6f7f93c63e630fac5799703c7ee1f182d1decc867d510146077b0dc92cdf24523534429c3a194787e583834420ab10f1dfeee86c59d77a910a0e1a8482a7e206df5083cc882b69f002d1638234125d6a6051235b1a57d37b03875ec08d8ff55a7eb58439681d04224e3becb1aa18f0dfa39cc0a7039235170d5728a8d0183f94699100e4f4a45e7e5f0f13a7cfa8ebc6b1a4e297438acc790190eded8604496eaf6c26772e11a5c0a7fff9079b600dc66e4b7d63d8b409d70b80373f4fb8a73a394a5ec76217e27ab7aba0e22b9bedf73cbefa02b74bc33c087288160900fc9c7621e1a3811da4a5be964d17388320c4f79f6cc80b86237a01e38cf8b2cff16e048eaf7b0a343bed6b2c534c9c04de87831adb1ac856bb750d9adc79a2f46d429f36d9e61b5d5366ead80109d470bc3be7a2f8e4ccc4444e0590726be35bd1a43db8a7a2a6c6e7cc85bdba6c82512926231ab4cc8c56207b07d1ea02df7042d3895b72a195ab02ab0d8ca6d4ae2692d895f52ccb30ace95a053aca81355738ae747715a08c3bf0b33145b9af097541b7e780eca2721b09250cde22a7494b60242d711efca849cc299592183646461bb67efff38ac775f47b08873ff7be824570c90814ce6bde8652d73f66e8f96cef1b32934f80a5a0950709ba1336383f88a0af6240c0937fb475ab549901fc1e15190e12f45ac9d182800b3c720a72c0d3bdd34400ffbfc334a6bb3db53e70ae260c976b1d1c6c7256180c62079e6f606413b4b3efa7b27e06584f194c1113169076aa8cea5c8617ee900f685e7f2949b4dcd940725dc8f45bb7466ede424afd9d40f961cdbde73b80a40380d16b35b85909c260307d0c79e4f49a29a58f1e2c9d44c1ceef74932f6a830944d804528f8a15dd1a682ae27b67ee975e2ce7d9af29450d60fab150c2c3e403f9d24520a43b61e39aed134059fdad95b1ac681121e07cf5163c9d34488c4e05c77b54c0734c24fe97a238ba7b095206667ddaccd6d98b13d26874a7a2e03801a144d4c0d4409ace117866149cf426ecfd18d5175511a402ccf6e0aa99528208e1e4fa941fdfd6137adfb4d5ce15db0517477f67612054d13765e96007a12c09a376a55cfd2a39c758a7249c394a58fc224870123aad3e5cd081ce4c7a1f1e0645e5dbbbe3f97267c7fc6c03b1e7bc5186cac41f12b7504c9509a3f135ee92057ea9a3008b810387218e40c8dcd8202ecf1ea3bad8d4018f6b4278bd28ec9d0a7d6d93772cd1e60d0fe77cb3097368f5d432a3767e9e4d3a21d1078cc2e6bd0f5b7f18e2cbfab093e0fb2925a37c87115bc45cec9a93451b9309bedbe4f3d20b09f6b354a336b7b820d7519672a2138c56be09c03a12a41f4d90c34184d01509718fe00a13c74700ff71bf61b1424985c3393e3e996cf48547400f131fb4e60a2c6720438805d6dd6fd82db17450adcafd969c43880c4e46a8ab99a5488f65008d5bf27033da89d6a72328c05f1524033ba892d65ecd17b7e3deaef432f34204945a2b216fad03df3327d0d1c416e795a45f3e0de23b8fc7c1f008346a10aa02a457d9c0bd33dc4616bca7708ea91d3f095250b3b0915aa2e6b77b34d0f92806f322f1f2c779a52e2e7527364666dc79f5701467c672c9fb29ac22baf9013f011888ff18aa7d9b98a00998b877869cebc99ece81c64ad95c0850f7271882e90e984d143a7dc1314d9ae7f4750c44118f1d6849f2fee63ff9b9fe6878f7f26b0f640ffa75eaff957cf4709d13dc2ab986201ccecc16edb7248f214695351c8b02c985b705c7768e94b8c9e13907342e418fef1bc197b370d150c420b63f179a06c1c7cb679d6eacd6a30daf1d7686684c9b824246098593bbceb0de380203340bdc62564a6cacb6dac735a0dc441d52781cb695fc70551bc741ca5a2cf0982606f01cbbd2d15493f2ba47a2ea359d7442f2b93845e630ddf6be8431605e02d80090449cdb05985b2f34b97025535f21b589ab0902e253e09b32871e4012c2e509e7875abe8bf8d0e11359d31730ba499ea50740eafc673193d2bb45023eb5690aa9d8ebe9b714567ca683a836bb569fbcbceeb57ff033a9b0708880d21b472e06e63a69ddc3bafde1f35e4993eaeb3d3260af7a36edb99df55bcc0a0229eba00bbd2ac4ff6566684e3607c72456aaed3c55fe2b651442b28f22653f1b267ae706268417f8ee31b28372fe74c6b3b058f1c93f6336367d311ba91a5dcbc3faa5008b3716f68fb78391f449a9997cfdafcef5651ff79ac80d5202b90a5a284f540cc03dd7fd93b58ae642c8158c0102dfc50584451a7d5e723c9d61475330ce310af27b3602c300eb17ecf6375507c26a4fc2e6785ec8a33be6c684f625ec3b590c1e4497bb0ff40c0e1f04e433f71827779e8dca9be9b4f8b0287dc5c3da783a0296c18f6bc41184b35e082fcd36401a5b0508cfadeed07a11fdbc69e6ae6789094120f9f520311978baa7fb93392c9f968ce786d48d800c5cc085d483724ecd025a3b19c7cb1586c05213ae8270e967641af7b2fe1c6c48fb6899bc439f8f5603e277ed89a31e96fe73e7563d6a717d9ca90215424bdb4191044dbb4795642803baae2a1f9975c70a634cbec5db328ca141fdcea46b370ebf193715e5055067047d63c13ea74d02e575b6e28cd58af01632117437ef2c00a3afd5c57e61adc00a97da95e7274d37b3dfb749bf0c100b9f974eea521a20abf0209053e024b4950f9f4de596154ed84998790ad20d29a15f156d4330faf18ba6c3ed1bd6370d200dafe5ab15ee2c06e9ff5a037e3fb84d2373be759cc38cc13de229cee142fdf70be28b7034b812dc662b04e8b64239f4aa648046ff901ddb57ce081cecb539b00fa48303b04d1b71ea4ec7cd0fcf413fd5e35a26f6bd178dbdbce079a9a806060be90d2714cf8ea1016437e108feaf0477c09b8cd80fd0a02066592972158f8509a3cb284ee07abdd8eb767961aae0660730aec05caf6dc6ee3d787c7945518e05f6e883c11bd27ad62aca196cbe3eb14e91f7384d4dd57cf73ca77bbe8f3c0e0e61d7845b4e770029eb7873d718ab35416149eedf9adb97d3c7546dc727e9470fe70587c83569bcb3003379ff6fc0ff1863d9ea755677de416fb3065676ff150c972e11c509669ce4a3bdf20b2f21fb753baedeedbbfc04ba4fbe637ddadd580ddbcc39342ae8992c68c6fd08d2641b22225a675daa57ff0ee96b89f2507c2b0f7925cdf82d1c757527f7197810a1c0bfd5c43219294d621dc4efc0938138620194537fbbe467aa6a064736199f0adc2fecc7d8608f54b212ebfe036c5e83d00319882d30a72f0ead37d0159afae2a72299e5e3951ac24e73469decf74145b90181453dfc0a989887158e456b0981b2575cf95a1280101448886e4815b4d42407b81142d54e4cb7cc68a327d30eeba7f2c2a9cb68025812f1fcbe8f1ff9a59d0a9b8d33176c607eb75e0e7a0a11bcbab2761d542236c9bc1aba2ee0c18bdb560d2e449f8a83b787b4a8bbd24c58f32df707b62730f71805a6d4c34421735ed3098075054b160df3689ecce914077b643e558f10732846396cdb38d54b1d83df0d46704f3c8e01f13717324c1d2de8f53e7c9ce1ea92cde3b23eac92013f79e202d1c5032bd59e879303f73d3016f57454853605304c71ea9b09f86006fa12570b55af96c810760e1f81148d5556e0c6c415c5dc10a148224c0e2fdb0ec4d8800c9148a71124fc80e644268b1df75ce5aa14102ac254eb1cdd1ba13527ae0d6e0d17d39f819013825b0e6833dd2f2578cecb8db84c4c3a8e730292b365e3a4300d94a170fc1c15a2ba8ee7cec503dd7fe1b41f06923deeec6453128bb22ae75706e08563a1366c47d02ac3855e78f9b53d79df0f58e348968dd94f26fa98ff550dec18de99c08f92670f42c5c5764ed21974a52c795e32f9619eb61c04ec6e950480f0bce8575b03aed32feff2d6242db70deab833706e760e9437ab756a2559087652ff63af21eae6a7d82287b2b3b71457f9b068643852479e03ae77d4a4ca0965f5559863c55b60e65b697061ba286c6ee9fa5604e8122ba6f057ef66d7b70c240b1d6b7f70f17e852568ac567fc25322b3f82e54d2e61ec39f15d46da92c0b01dd4b02ceaef2e9364c2d950987188f673ecc9a4cd3485a20f4c641db5e290728a6ac73307496b391d157574843c00ab404d53149b6cd1227e237fade03c6081233e63c867c4e027bdd601bd13aa3ba021f93d7ca2477d56b904693e968890af53633071705708318ee43c4c20e1cee65e5c82342bb55597616065f6868740630d669ef0ebfa23852a7251e119cc6d56c1480110cb5660ec81ffdfa56143700558a8dbb7255e5008d95ddae178ec4be8c453f5515dcc3f31cbf268c471a870507db405c09763c3cf8d7b3b725424770628007fc4fcc0851240093da9569860f7c47afd8a2247837ed6ae0f4cd715d1dca63a118ada034462ac9240ca195ec091c80338460960f32d6a398c674a5c49ad3c825e42904d8211321dec2d82ce60f7341efcd81545ce40ef8ef65a7c96d71b21a7c68b41039c72595cc5b65fdbb0c70853fa0c5833d0f1a6939ee811c47f6a346213cd0810bdccb3c2461a24be70a84d87d73c25bdcfd1788543aabde8fd9a1061efd9530f9dd22587722b0d45c0818c03ee189460b913e3375adc9125d063b19f70ae5972da3543b1c2437bcd806fb6477c2e73a63499e35a7cf08cc3b916365cf2ac9bd15976806868a580a6f0a713216fe98bd21287d98a0343346353bacb48530ab3ebd3e1e2791bc09484800be388cf25a935b25b4fc85a8f1fbf103d1bd3b46a83e981a51df038ce28f6309475623e035543bf283bd48035c6367797393041bf75c3ad40ee7fc8cfe775700c14982d3e1461dc941bb6e7995a7e32433ef78e3644075fb3a6d430cd18e170e725bde47cdf34afb6b1f4cb4a2afeb545a40a9e04986ce6a67e4ccda37b53c0b91d87bd90bdfaa147de86ab378e91d726d76d1c9ce1d31086505e87d5d2899088e732ca81fd58291006c7acae7a9ffc7a850cf420ad17838e13dcc192d60b7085e7a26a21e69644b5af743f067cb84681db72a3244c8d98a3e4c42279ab2f007f66b641b01493da68ea4451aab72f60ced62c6cb565e72925d2b9ca38a61630599e2305d40bea682257f6744ca06b6147d7ec6bb60727ffdbf870bcfc465090ccfb74dd7b8040e2d38660dfa490d601604e517d115ff43ee2327483a83abe207cbceaa09b716536e87125df69504ee83c8aad26772a16ec451241bf82438870d0bc5a1bb5c865eebfee396f03d1d5f930bd5ee77814973fd2ef0a8f9728e0209d8345af9112badd12c3b95f0e393cc30f408cdcdc02391c96e8cff6a6f21f70803900667638474d439369a35e0659f41fd8a52684099d9f02cd4b21bab1db00a034f1dd48d4bccf16d591d778f859dae8e4cb924d0e4a0dc136ed442098e0d082eb7d739abae24d2d83c341f1585c1e1a22b40bc6b66238772c589514106760fe56ae3bc6dfe0330a212024f194ef543a9aaa640228cc1af5ef11943d621cf03d49054be154c43cd7c7d9d3b39282fcd4a8781fa579d0f8b3f608b0e41dad402524fa7af3546cf8e47743329cddd5182f96e004c83034994fa704584f5f6b40450045e9dda5857f438ef49543bb5a440b0fc78959db4245030353263306a63095b94e1956ca522ad1fc4ce026a5327c19afea77d978207362f94ce907e86ce043aea6d72549279d0d946a953aeb95a2d170dc43a8c0da661bb7f75fd466eff00793cf9385e3df82c24e2b73718f83e6da05bf178289edf5201d97e38651c8f07a687265b99c2154fcd2b5f4b76f9cebd915a1a3346b929395fc72dc4cc4b820fc8e07f1a760ba3d93912703f8f39f8da530dce6ee3e98631686b1db25379220433e223368ee9bcd057f0e98dd375b74bf47fbae94cc7cb695031825b531a1d020438b78921ea39e00253a6904346c83ba5e8f86f8e77bd2876a09b591d9f7e07edd00257b59d30e1cc256bbd877f88c3a6fda73c7e10ac10236aa26f5eb06c020865c322c3e11032d4ca1880b3dfad173e815f6175b3609bb47003b0e9aa0c0c2ccc5148d30e0d3c2dcae7e53286ffeaf74a4d32e8082f222e337a0b2e8831068b6ded2374fd2b3c3ce8041fbc1663473e7468e9e0c29b2dfd42f56d05737b04cb9c3dc94338b08093ace8a679cce60722999f477ad01cca8a65f7afe1f7870623ad6540239657cb85833d43cca3481a3d685d6ac07da932684ff7aecf3e080bd8ce977cd71e8774df416928dde0fa73b2c3be3714b08addd9089fd439596502a4c316111e0033451a529fcd33d859058de4d385ae31a6ae6c82df0e87a02a0f23bc636d85540e471ded2a1f0a39785631589371b5e8fbc35fd1dcac09a5340144d89ba785ce124c262038af0c826fcf2ac5067de1b2f5dab1208e135dbc850d9181cc8551a7845446663fb2c98293080ca1a73dcebeddb69c9baf9b09c43a0e26c46d9dddec6d73da9db17da320e865c4de5bd9a505862799208e1fe48c410666a680f0c0879bdda6b0956b8842ba7378a164ac03d3b6f95c0b79bd256c1107bffc3de88625320a298b713c4f7a8b103fead5a0333821381e3d43f6e82be6086fdf95aacc5b41dd692f1729872fd71c11dce0861c93be532c328e9720cdd30326305d829ed7c5343ae8172305d2eaa7deb98033ed0788a7b2530944d23f4f0fccc2479c8029706fcff6bb74719aeda08170a026bea17b36dd2bc5e6007e920f23f54eeb3f12e83d8b413cb90469725493dcf90d5bc55821f6004366a446f105d2c28a779ef0c288be6505c2757f03a186347f8c68cbdd5e859a31b4ec7b250801457d878ee0371cc044a91510350fc8cf1c4bc6ebd40557c56a0b810c364004701b03b3bf2668b01d5bd3d40bf0578aa09ba21b8b3df5c64c5b2f6e5a94b20b067eada335b7ca795bf6d8e6f23dbd3ccf03897a9167ece9b00f0e1e7787760ca1a68b717731815b68504e7e0d1b843f648c273d4965872ba080be33ce05b105c9c0bfbbfe834f29f8cea399aed1628b2b49c35743c9640811fedbaf092cdb0558092ae32ed4b8791621d3a7b022e19d9e91f0cbc26bc38ffada069af45b900a151e2711739063a5e3c9379a416fadb9402d4396b0196f34e91c82c6c89e9e0c3d117b7dc272a9280c68c9bee45d46adc142477d41a9dea8e46064abe7c0a200f04be4346397438a90e12af463e177018e58901a15a01e669021d522d9ba7905898489c713a54e261c76c6ce9a26395c7c370aba1d9155da5cfd16e29e96e1076736999f02d703455b287b5d807a17be36494b66911cd43063b23332546b380208de77e980b9cf06b38e014f087e10799470357042f8b76fc3595348e47eca0c818fc9ba131aa3e8e536e94e63d617171ca83a2ee416fbad92ed717254d08605a74ccf6f388f3b3baca589025352d0002e6dcc53e4eb219d961640a9468a580de0e146058c79749c1b8fb9a15a471a3f175f5c0c7eb4866592e452885292ce069b9ad79f5a721693dafafe160ba380468ef75a94d1d436a020f09c9d5053d40aca6555ee55e8dc901ddb7e1168d0cf8adb231946a894dad8c1c9a753ee08760a73d08520b61ea48f2280faa09f8c9efb91721eb54226a4ab5a9b39681d4fd90dc743eff5caacbfb9c2a0a7c19da0136015ef40bad6f134cfda1c2b053d69ab014e614106a005b96a6d5ec0b11505ffd1869dd278bc78f3338cd1c6f3528d9f0738771e43482b2ba2b3977e09cf34bf9e14997cd4020b9a0eeffe4f72431ea80d5b72efd38f706af95850f6af5b926fa2fef0aac70c91ccc0470e6f7924eaf007b043d7af17430e4bd77cb4754e04a71149e19d0c20fcd58400ccfd945b3118073e096505afce20a2edba9a557721d11bb9e2ea7e14ee14f3bd6f62c24b93e306b8a560a6831440813ca29436b8080b803a49bd5073babe6597fae77e82f2090c4a613adee2198f3e1ead9bf5a63badcea3747f0ae485ed0c86104f5cd3d6f60514c868a5f87c136761a20167267c2d2f1d357ddd1e5548b214665f9ab68d260676b82ae6e236577797b255bb6f3d06086e4711167490e2b6d7d608e2a4a4ae07d30d48a83148d8f89ff2a6d42da0065296049632e0988abd2f4650e1cab52d08db375fec4b6414485687d4af1749216f09673f166910a631f3d02154534c4e074b751f2afa6886d926299d66716cc86809d776ec4a5a98ea865b654f659b600d84a23a9785f18744c71a5cc0e24c46f92e27cd0c0088c7da5be35931fe89670233355a6e15ba5054fe6b5b6a496d4c8f0e52c87cbe1d4de6e520829cd8538c01a02d4a33a75a6d6ba2e5901c394852dd87b303df4f2e82c66516682870db670c2d73f65eca95e299a68a977c883cb172fcd7755b83b4e8b2a16ed54e879b7a02d9686ad5f57de5e61888b73f317c01ec6b330c30dce7d6560a24068e77e6850f80ceabdc4bc6cb0ed32ec706c30631574ad9d23276c32dc788a80aaf68f31605f0a3b96b52e6578e5f951f9e99f42468f4e8712b444fe5b228474f0a33eb78063640bf2e02f112ccc30e5ee2be55526bbd62be70544f90570e1adc9711f0430d22d45d34b9639c5c1a6b5cd953a8f7370e1326b70a2d414afaa5cb642607de06c897e426276cc23570562b62b0d9efff994ff096d5d03cb2141e4341a525a2044db4e2e4af7838fb4f532339437d2a763084b0462b15cc5a013719dab7f80f03e8790d26f9ca2c22414b24f07a6a5c06f21994d5659eefe484a954eb8b682f07e23864a8c33ba6ce490e5d37b04129ea7da86006dabcfeb4b2197d00f2adda0b1074bda9bd068a6684fa1334747baa09049c71887ee6ae4299bf3b226328040b6f71d90b359544a8e9d5aae4148f783865c8bfaaa995a78f7bbe16211c97e20c023cfc6148fa27e7530b1c5d89a5fc4a5396445c4045030b0a6088032eeb7b08405c5baa079c781bf844cae5396b8ab9299129e3c7fcc479eab7ab83271f3e0538a5cf79932808195b0cddd473bb312697322240816c156dfd43109a5508830a98c9fd9b1c71be92cd13beb2fea8e8ef238f362d27684ebd6c6810d1c3167b084a3a2bac20f6b30e300cc15142bfd397f4d267a8033dc395c94873a77c6dcc0ac908365245fdde07ca6f46a6e1c43a091c644dd2c926c4269d58719da777730db574a759e6b1127b9a125e62218f1e9663dde6b57b3c4702e63ad92de3ebea0210fc87825804aaace5af9833ca5612ab8b9bef4a713a6f61693b943a57847701218808045be42493ee898ccf1039e12536c19b8f9f91dd5df2a0303aee5a590349c1da0a3fb8e58cd66fe3a00e13d12eb4e9aaff737ce6deed129718d0898a0d98308bf79fd8d84c757d892c303adcb03dedb25d29c64a12a9162669e6cc2c029a3347993d3bf1816e9795452db011a513bca957d5edb20d2868d66e1a9ca5097a97590d30e32caad585532d4df56248444bc36f201881309fa992a8f547fc0471e2d0c9937e2ca858e2f16124d0ed4cb27b8b861c807886e0ecda41180fe50deafffba7456784eaf12cd1e6e0ea0d6c61b01b3a903fee0046c1c9c31d763b07b4504ef9bede0ccedb7087f8f295b73c924171dc22adc0e39804da418d93de0ee3686a896edf63b7da2137ec29517fd48476e6936895f7233041001d09152308fe6f3d1099217cd0e85eacd2eb028a9f058998d3152255de483917cb46435402556b8bebcee9e9c08558cabbf4c4b93c76ca01af3cee79bad9a5014a77d0620575b7adaf7a16cd1569612f68b5a792dfe7c61004ace6efecc8b7572315d97909912ce539333bea9b167c46da9f4c88a04d23d3a1da0de2afca2b485c5b830d00 false +check_ring_signature 3c01e8d3ac2ac18c9ca819244200299e3230847f97719507281bd79c53eb3271 1298492126cad5c44c072ede459a23470e2dd45cfaaf5bd1fb1b84d85436ad5a 115 e3ef8e31d78a634d294c983b69ad7e0f54c317a60b2e1b5aa6f3c802cc0a3143 34bf2a412b8ac5466170067697174a13864db7dcd0b6443452b86e6931969d8e cdb8d05c6f56daa0f1087efeb53a4b6ea299e908b4a324ef6944dcb8fde1e615 bad622572fe59b38942969fb409d88c244b6cf35133642fd3b420e5ede6640fc 9c888ee91346762ed0a5e7733de93a2d00efe809ea0e2ccc1d7f322ca9c6d9ec 2012f70e9d5e368581fbcde40e89dd26f825514c81d0399255c7c8a66acafd63 c4c6c2ee9e84ba7127eb99ec289372709e06c169cf120c25da62c42e0422e989 f00b7d124986ce14338ddc06d5ec665f8578b6d24fec1767dd19abdd3ef5a727 603a20088017707f5c37a27f078dfadfe53ffbad2e434513e54db2baf57a463d f16a8a9f6640a7468c9fc61ca8cb34e6655f176395720de736c63f62ac25b10a 0b36788801dde9421fde84280c609a13acea57d8029d792a8435cf985488bfbb f46f87b3ac8b375e52893d002f697a29c542dc8751b5c390cf7196a7dbffcad0 022323d1d4c824c4258850c59d108a87033a85c328e0954e099556390ecdb371 5f22a0741e0f7d1537152d9eff9360973a54e8e2f7fd06b98bc2a105dd4b2693 4f0bcb6486d05233296887dc4e642711d8e3cedb4ac9d536a4ee0b0558f328d6 48ffe802eba575aacd0c0f400c05778f9ca68618a5e6c0f6b65a719073cba40f 40c702de7fe000e7249927f49880f4a4fbe365073738fa4a6a16ff20d75cf21e 47d4c876969b0bf337185f4e8e43f86e63de1d263a48fab755345771f102586f f1bf535c016aa286b0a84070c4ff0b8c995c1c85edfd0345aebe4ca0d36a3c6d 62f7bec9a8d89b09c7547fbf2c656fafb3da22fd19973c15ccdae7f513271bb9 464ad58bd3288d5971df63995fc28a2696eb6a07c008bcbfb630af7f33a52434 7493700bd7c37faf83c51423bf3721c92c8c0d60b23febb575656478c78e4822 55b5811253c6d9587f0379ac3479c531b689eb3cb61da5a37cab0c195a004732 fde1044af18bfad4b5430f2001641b6d27c70dcc3220070c7ca6fa8105075340 95e4cf72660accc7cbc0335d6c0a3f52aee61762b8b19a1225b2e4a37cde3ff0 e848cadc4c42e50598db0bf74b9635788ca8fd843fef5a73dc4294e516b3a5a2 1e56cce72ac5c54e1f0fd8f582802c052d516a078b2379eef63bbe382e2b9b79 5ff789000c3c14a2b2759175d83db9a2a45c863a069c93f8fdf9915b5b7816fd 12818f44dde2485a3cede72ae89b2d7899c13150def06eadf23fa421cfdbd68e 151ca75a075c7514e39d5d350d700a322b0b5f84b39ca3425f3eb409d8c383b2 bc0ef11a52ea0ec6e8f22494e41961f15927d8d87b086f36ca51846a7921eccd 13e2ee97c1aed95a66b6c881776948bd7e465eacef44762cb954d5814532a2ab df92b1bb43d8464204cc947311642e19edab22ed364925aea9f49a210a7c030c 11eb4c03dfeb997bddd1d1bcc8a68ef0d41add8d98b0c1ab1666311d0800f103 cb9c8c0f1c3d25d89811034af704ce5033ce98b027ba019c29dc692fadd1a652 4b18ea31f7f7f0a22d96df8ef779b17a39e4a7284831a26efce7fde6b6582395 41a4a1d13ca04ee3cffbd014d2c4f4f86ac0a1c799ad866035eb10574506a0cf fe5ce524825752b55e1f27d5c7f3c73db58a562d777e2c56cd519e1e9f6320e8 9adb8963f0974c99796b5910d76d827d1beff3a2525e64d0409df67c92f1debf cc6b1dc6e32d9c56e2711b06f3050d93d977de124c49e314ed1510935770cce1 11daf43a902c05cfc53285979100b62b1d2161470beb439f2b2a48baaa29c229 8b7b3b936da07a82c06a65581ee4d26a37f219bed835785f72a9776efbdb4bbd fbc8ade7c1757dcd669fef203c8c1ee80bebd4f8a47e9476a7ab81dbf5615620 a2df3e14ede717fb2a9435633bd3d03152bde2dc3f93ffd9c71adc12433d0dc9 548346308f7df9a660520445cd63f91a766c1f5ced567cf39583d4a68655b6be d9d9e417f71e8471202acdd94193f88633cac93ea30fed265d9a5332e9c734f2 d2192cb11c486a1502dd7990d098bad26d21530608e27ce6d91cfd3d2b05f0d1 05f21025ce3ff2c9b518b3fdf03bb6e3d5838dfe1da35a3611971f191d19a488 0c40b2801f3364341dea7d34284b0a880f7043193bb78e0128915803a9aedf84 0d0ccde9b074ff28141dd629b650a7bfed701ee3f1182d2958b43b537131ca7f d0d8b570b665208031c1993ac37c56bf7d9f45b8d1b09cbc789e1908d66deb92 90db5ae2532fc623621ea7ea1487afc7cbed80f179ac1b49fea641ec222ff978 973e2b20d4f905647a717e6d37c745c8237c278afbe6cad513fb4e7ff3b7ab69 b1a61b8d684f0b715192ed06c9c5975efe088377bbdcd70e902f38f7370345f5 3ba0b47584a4c16a078e4aacf5b89e5bdc5c9fd1c6a9bf4b58eec01c0ee227b2 8db3dd14d30d6bb82100c284a5a4739e61873454f69f55172128ce50097e9aaf a3672a5796241af87486908af4f3897f2a322a4f954081008752f317398c9d0c b505e031279c8ce95626d188637544b6789156ef9e8ce20986609fb11b00ff06 e22e702df19a5d1194bd632a2b323386ecba00fa57e18c4333c2cfc71684ad70 5dd47481ef9b874b6188a098f2f4b7fc81825f520e1bb08251d7c8d5315a9c37 2f9eb2b4eea0ffd8fbce206121450fe3500658220386d85e3d2605373bac1673 bb36cc882321c8d01789ee859986c3e942077508a7a7ceeb897559c9c4ea9ca6 52d955b0ffc994f0f9415aff3e69da475c227dc78381150750c8b160e9601297 ba8e6128b30b756d1ec3ce800bea8a1bad1a8ed65fa66c0caa20c22abeb532ae b6717afca3ce2be21de3d3ba3446bcefaba630f071893fd91d2d8bc304baf9c0 7b87f46a5fbbc41b5d804888a44ccf2324f3bbe226bb10e9fef70d497e1f38e9 150928aa9ae0978779d52a9f8daa3395ac32d1a7553a4a6994a7c90a59638bb2 08fa51d0de74feaa3035a35e4bbe933a224ed62da7df03d2d20af282ec575bb1 34e03be064a3495fddfbb90cea8b0d6ee9c23c5293197dc0a20becd2a1297e53 95c69c5e656e2fb71aa6ddcaff4fe2ae6147092633d9aa6c4793707f50875a7b 163cde390591f1bcd912818473cd090b34a0315c5176b223a5e4277d50d16a55 8845569d346843208925f603562ceaa2925fa6c5e909c562ea7a43e936af214e 47b50086e51938203f114014f1880fa5e3076fba26a59f9dae7caa8db9c24de2 06c034234641eb28198cb625bea9eee214026809d370b90e368bc42184c2e5cd 7c48785bcd5db9d1724fe27d42e8831d80db9491af8c886b1bec2f1ad5460080 5c13911d5b4099d42381abfd7b85d30bb8ca33845924a42c83e3cc7c1b8a07b2 c568d067beb7e20b16b792e0069aaac111f8a843fe099b41b8d799a303e3422b 1c241a9c8811004bdb8698d52140a5e3d476182e938372484ac8051aeb7e3cf7 041729ba0a27ae69cfffa03636ffc437a0d0f98d1c91858c9b5577ee8ab43fe5 2bef4f42b61d3da338bb37e8fec1d942f321b90c78d54acd597cf67b87745f13 647fef7160f920bf901d7ab7e64dc681db986de7f14cc8c228206e179d87ca0c fc4a6a7b7239d2f6dad5fe9657dc11abb1a395ff9a70af97be2b1aafc9986dc0 824ec90f7e84a0255efec0cf8856c3e8d6ef090983a151fa0a05b7c569e3f2bc dacfa80595ff523a05df958e3a547cb89a9a97d618c2aff78f0ed8eb7690a545 a6b57b16a28e58e14b2889c22a811ef78eb5ad6196efd5da79fd9c4d1c83032c 2048a44478bf18d2323473054875a75e372a5c983a68d2865c923563cfa346c1 9ac3da3138e9c33595f60b3724d925308718f6d463f3aee14893e7be0462b841 e8bcf3f1fa37206277085d4838c52db868eaa77a6f03736dd781e5ae0421b9ac 70f7435a7d15dc22d18800e2939ad44bcf32e50e02d6c30d4565bf30f2a7b105 e830caf99b79a7a65bc7dce6ea90a6b58f078dbdc8f046740995dfde091081da bc7b65b9c3257eb3ed45e92c46ab4092e328c11a11f458e360746984543ebc29 4f44b66aa0529b99d76639dacb45ed8a14aa77d8937b8eeeabc3fbb965838849 ad175461524a45d27bc3a79c63c9b659bbfce88aaf5c373698cca8a92c069fd9 571d48cb6b6118ca3002ed13886f06b717819e416c36f4914f014c27a7dc23d1 bddb3f7f666f9aa006a9fe7ace6cc5b18b16cb42ff4d7f801f9e1c9e1560b03e 4eb8986e6449fdeef479ed7dcebf0185ed8f9ca40f8326112223a6f028198929 b988fc08e9c0561bde78d5d190c2a8903776c6e8f4a46bceede1b5d0b0f04044 a259032c57252665490459943fcde10e3dd9d2394257692006d1c694372b6afc 8f068c15f01525c6b122d75d0118ff4aede235f45746d800a3f179e55ab7b973 eb6ae0c0fa6d4550b31480a6290f500832e57bea4ad45ff1ed9fe1738389a674 e8aa343302c0bbd203ffa27ed9c7b5154c34f362ae8444d31cb4884cc81308b0 aaff3e188934eb215473b38ca4df86a57709fe3072a05dfb9025cd6aedf6cb08 b32dae36753a4e63c29937d3745d67ed1ae1b7c81488686127e416e62dec6064 a41d7d863e09a0252bc6a85195f3d566e7c50c03dbf00716aba20695012d42e9 578cb9b95ae5035d9f405085993912df3f0d37f0173fc66ad76d54dcfc7aaa6a f260f895ab1f4fc34e12a81cda5abf29577927b28bf381059c201a0d34e727f9 80a21a059873b5b05dd4f59a0971c65570bebad2bcc9b4fedf71052b6e9b9948 b398e1fe9e3b5fb0a993fa656d31dc874e827ed1c08144b0f7c1ef705faef1fa fad384df274c616954681a0dd2aafab57ec01b63c8144d5a47e0968f1c21bc30 75bc52d5eb3350f1b2769a844a373f776bec5b7327beae6642b4bf1a2f1e942b 87e48a4b160a9c5a8423761a4d2c1fe7b0d7775da1ecd0318c7bc3a0ed085ccd 5c99265e22df82a980ee9583c644e09875184320c3a32d91694b2dc092d8ae93 2841f537858f006f2a51139d1193effcaabc134ca5ae667b202c46de5e5bbfce 62e59e2d0ba9592f9684454a8996699b2928df6832054e6df2f9519728254b41 20fe11da0bf9e260c2a130bb7130adc732eb1406479dbc59238aa495e65815f3 d958f1c9ac5d305dcd4b6dd66040d4e0257d85c3869db71b389d735417dee50ca08fb820f887e9e39292bb5075269f573592d891482c793385b9557f906ad00ea10d570af351933a37c83b2c4ae5fa8be70e8aee7cce4d209bc086e2b55d740d5be2ea23b364529ea7e1c9f5db5889218f08c2a7833030a111d2691656efb80dc002e1a46cc8b1dd22540dd5d2422ca528d8baa7ce158cba23feca71f4913b0b14f1f7d11450d0c1129719cf58e116df2a6bb4b276bd4967df932351bb8f8a0da23701cfde24e09342b786060967aa339ce05486f561e983f70d4278d8b5ad049bb8d0112adf6b0671f4d8bc5920d56272c85597ff50fc56c5ee29144f9dc5009541f603471c00936fe85595a09410e177f14678874f7b1f26dfe26ec9a6d30b82cd5e1dba3f3fb21e8876916a60ddc4d5b4c369ff61e0ad1c65b602b0daf00d6974dbef8fdc31c48ef49baed19e60c0209bb4aa12b4a8cf2e2eb9c3a7327a0595558fff7c25db59f71d733a31ca1a5de8797b7b6c21c2833d8c61337cbab903b0bca86c58e6befae9a9ddacef9cadc31ca4e29fb5a42844e49cd4f8be261900d24e68ce53773bfdbe9698d0667cc8d8971a377abf12d5484b67418b5014ac0089e18972bf528147026ec5e3b7507eba3c678cdb1de5b900dc0313218e7f830e296523077ad4874fe56f62565a716e6f3007e448866880cb048d430ce53ac602d6ac08edebb750c8cfd25fd52d806d2584ab666c48a48b9bb6e4bb29c606d10d45afad40f1477cc3001afa9de055f87536100f76847a896304b743e62737a80ba56e52a4691195cd6f824b73a0abfaa595cd3c8710f65dfdebffee6a7b3dac079042a5caa34992751ebeb15cc99fa8ff4324db1d9abf64d7921f9757ec61f30a20dc7f68b352745f0bc25a4be02d21f5725d1dbbf0f69afd0235d5be34b6ef0d233832dbf1947801daaebd62cbfbb907e759c5af7889f545993f48b4c160c50b7443235f2d82f38ca0db917a4023efb06a12fe01a6258f63dc5fcb6d336d1d0b815f7bafa0d0dc1bf414682e7711191aaf65dded248fbe72bba4bda91d9df3096974f9686038e90996ac2b1871a7abd5ff2fd4657d2a1457a51daecbfc64330b62ac3d23e99bfe6369baac796f10f625a2d7871a26e6ff684cac331852c49c0247b6685dc93ec2d812c7309ccf94589cc306743a04f37b3f7274b87bd89ef50b1434255138910cf4dfe6d9931165725d5c84f0550bdcc760e6512ad602c8840dec98c62e02d9c699374f193d3ddb9fb5fa78016e1ebb0ae20fc02a69e6f0cb046ef0de037724aaec41e50123bd76331b48ef459b1ddc9ad2528a0b029eab850a9527a6b2f89349541419898e9a873dd075776714cf331db2b6f9fc74e50475034235f7c38074462d7934fb2c6cd4b9ba6997e73f9fbb1206a574de75d785c30d52944ecb22a9270571757ff14157c325f05918c9ff27092e9694c6b1d142510812514065c08daa66651c8cbd95468c1c30a44c28081c1b6899fc90169e64650686c85756034a58eca6ff71136cc53f8a6e64babf161a71b0de211a07ad4bec0f062568a7ce9c4de1f6d2e8ef3671640eeeda3d54c47a4ba1e42d7b9c2f6a5f0ed5f9269582ce490e71ebf44b2b16099c84b008f5ff1eefce98ea909505f6390448854b0562037bcfe9a1648b4e29629ae3bb03269fddf0c1f1d09f5e1dedd70658b09c8886e3de3ffe5b3c94dbf02beb40ee253f67369f517a345aac3fbb8d0c93c4b9b4b7f0ac0e8aa2b63d9d73ebb7577451d664e25904e402ebed4c9c150c1ee4bf49ee0e25fcfc8626fa49f56e4f60db84a9e15ebad315da3777b2f53006b854d164f6f58d2b7089a7f76f71a4169679cc61e7bdc4bbb70e2db901d20602e1afd57625162fd772e866e4d62653295afab2672c340b28c6e62b9fbb6b470dc560bc4241dbe9af922e2a05f80840e8ef2ed452a9028090f94dc824b709da0309022be1eb415e207d51a4bbc42ecf221f8d8795cc1663df43e89cdd31af970e39e54af4775afedfc556d6ab8f39105fdd0593603b0744134139dd7e6ef6ab0642b9804f1593e53cc0f5e6dd1c7b0f1af91dd7dbfefa7c359fd3048bd1a47b089ebe7d19b36f9739d986246f8b4b3db1fe66c9f1d8f4b2ee680bda109cdd800d6a43b5726ca2214a7ff98cc02d51cb93bd437f80c6b3c1b89b43808088d8d208fe2782cbde443337fa14efa8cc3000a52583a8a96f49ffbc55c789af283fca0482c34d2fabd24267ff60104c8cef2f74772f7914fde8fa287f1a2676b67742090aad09070dfcf319b87e18a31f4b130293718340209593edae8c6f95eec5920a40cd4e64b2861ba01c6979877bc14ec9edbb12248d2f17f8945da8ea9e71da05bab935d561527119aa5dc2e9636b20cd616ada246c6f05e84ba72953bced080c1c42381766f618aeba1e869d89a715ea791aeb27cbd0bc137f39c85cdc3bb501155f1668970fa4cdc157eee016206c3ae5f69ab2ec0e5c7ebaaceb7f51a2bd0af187f1da2a3518135d5f56f50f2593f1977c47829c526126fc8626f8d44f3d066bc1a8dc5de896b1b814c11b52840f4d4fb50203d9a9d82c535e11edc791dc001c308c30161fac237dd04db2cd271782ca77f6e80ad331117b593deefd96140936b2e5e2833c67a81b52d7f8b3c2494813264fd61af76d8b8e11aee110335e0d76bf8c5954dc1e6956a1d4067207d1cc36a7a493aabb8459c68011891ea0eb038a60f9d939a6355c5e63dcf85d23d678cb120e2a68d24ca531a955445eabdd038bfb0f6b8917a90bcee1735c82eb6f4b28ded3d402c8ce0fafbadda42b77d40b043346f2bd19f660f54c3d98cf33ca9b6a61dfa57f36bd834b1410c51bb99f0b1a923e7a38415357fd1e9a72657a871977a14878a28de6aa2bf74a0d9ea6a90c4c0572c04db04007c4ac3293e8e4230fc8834b0d6309cf21a7e5ed5d8fe5d507da98a737d061a2701785bb28a63806069e28b45c2d1a4764c192f78fb973a607ce160ca27291c0776aea1eee5b02fb8dd8cdf7af2e1c693b1b7005435aa9850c38989ecb0edc125a0b489beed44bcad521cf670f92bfe65724ddbd91430b650dab57d0c427486beb29a0e3159e95d623dc109e9d7530eb040886a00e58b04101993093147604218599b773e422c3e314139869b1c5c753db901f4542c4c65f0ba01b23c3180b3103eb1fed7719ebce3be470a7934c2f60758d1b2f443cc6f4074611489a85e85540bef4e24156ef2a7a393a68cefac2a422b50f87a7a4e5e301dc723e20634e153303291d6132c2ad9bda85822e20c278a3833da10f83b1da04eb17c091261bcf9d9b6e7160428e64d027f1fb4f3a13c4fb0a06c6a0e01ce60789cabfa18c5c0225b883dd39120912d7980047cedc6ce50908c9c00b8e39d0033fb9c3ec5b4a9d138c06ac699b72d9e3b76243a6c0b085598087dfe62d56cf0e11d22c7c4fe952d6c8c2bce1e232091283ce6435428fcaef520362b830c3e3052723aade1e879936a807b30184877f51135ba43fd93a1a65b0049f0cd522c0060afbf67b80c4e014d2cb0dfebf5372d77dcd34d8e8b747d36773dc72639c7d09e4844d13ae1b47b8cdd3b05054ccd41c9e15c48706ce231f992a41ce2b15c00bb7196fb0267205ddf957fa9192dc6c4431d08461f37221e174300dcaa0fcc800578009ea4a7b22c1002a71af9685b279aa32305c8b7098f4796ebc6714f3920db60287b4cf400717a67a1aa86a0d2144d390ec6f4c4bc2a81794f821d501980d18ee8a5a077247f1e9aea2fa29469d0c47f1a6489c2b56b189ed421c3235c20dbdd1d953ae5de30c99a84452502393694e60da83d6fb48df523c669617f5ed03a9d2e76af57a8f933f994398ee27fca6040705ea7ae001c8b2707facb8d0450dbe606319dcc672185b03c46ea65cb315d26cab35602a6cb66a21cb8edde9060c39e1ce2fe169c59a3ac34f71713513458a9aa042a89a7b95854cbd684d9ca60daf6144c777bd3efd57d4f8c54b8b9fb53e64f25deebbec47c9054dd6ea164c0172881690397ca071bd47c3d22ad8cbf3dc7002d32860f3d658616d885131940dd8a1305ccd171b5ab942d268c61d0624a0087738263bf115834ef2b2fd6aa70e74b0cd5f59339f59871bf8cf5521d4aef1f1f700c5436d75c3d2de7606a12a02a1f0f3515f0e14da15306ff69029dfad27dad1b06d595202301d174bb478750499da71ae40cc4f0f20554d3a2663ad6e85bf5ed727273749a0b9dd662a0f9201fd74d1e6660e3ca21c0e4a774c1bc9db092172492416ad022c4e976e478e5503a6a65d970f664a728f8dc34c54c3b4914efca7bd2288cfbfbea0935c2480f60e545495eb3aa9d7733d80739317f6db55279094a7b96863030a2d630eabfdfa01ba72f1104be6a93c0e49353aeaeffc8fd7b3ed3422174f729dcfe9c924d1070f4a8c0c8ae9fa0cb77e6bd7064ee3bd764d90d5e600085a0599e2642c54bff20fd839e83adf79125f380fa0449891751d78d5bdba6294b10413843bafb0462c0803ebf98b9898b5daa99f4c5e11931b8cdfe04af7b2d1e4c4f5c2eaeadb21d208c4c2fa695cc2cd9387787be99ef839234227025c72fd60895ff886eaadac830c01cf49cad4d651b6e2536c41b028fe9a9130ca4a1ff9adb7ec230f69f453ae08d703bd4763ed39109c1d29ee167813b2fb563af4e063531540e5b009f9def9053d049b4f04e7d8e4594d57e9b7c94e2413952529c41637d431cf3d95b77c660fc60216668fa657792060c9408e12cdf0bf2a8ab7d2d6395a03d98c8504c1e00effda450f3292916adde31e35c90c981660c1396e97bbfff021d4c3dd8dbbb20d1c9cdd4883786c0f721af743638dcb281bf84521eaeece5d4592c44b5cd5280bde542896132d2aeafb8df33be819e360a386b831a566694c995456b63dec6b05399a142481436ddd9525f00ddf607a6c4b906ed673a694d41e14ddcfc45dd907b3fec16f0892dea848059cb49efe978273a256b80a3d47df3a79719dd2263900e520994d03dae74f6f8683ce623bce04fbec59b9866cb1323085856a23b21101d801f464bdca6246cb9406ddc885e8589285457861a5247796e5aa61b8a05b0f628773abcd75c574e7df7738c41659356ddc246ac8e0a6562abfed0feb13340e71db68a3def0e605593efdbdc5073b4cb14c4fdcad539efcc80fec5f6405f00d4f367c3d0e3cb067421c7034660a702eca28584ee59087dcd194194971a18d0133dbfcf73c1919ae8b233fa1408495e4ad7b20b3b08a7734b32af713ebdc9e0822a3cc39f21f6a4becbd70b233e7bf69b22463c5deec2c8c7d564c56e9badc00a58b992ad4e93233fab9553f16ef3c0e59ceee2849b9cd9da565e7e93ab6420d9eaf6f33c23817d8fe63966de448f0614250cd5e6aeb5da5bdc71ed6117bcd0de944b15ce90ca9a4c0d7ab43c59558d52c98f16ca8bd774920fa876a1db7a5059931a305bde921fa1b5a30f6518e68975c0f676692197c781d14deecf0382c020c2c7a8255690498989ecd9892292485ad4549145d22b33257f0d6f7d157d2000c2ec086b29be49ef4cb90ee474fe7c46b19dce73b1f4dbfc5ed25e93b2dd008edd941254b06b2781063af52e1a7c6d82dc8aba7c72237bf2fb7a80fdcecba010b6ace55103cc687b423e1851d23409826abb93f5391b61f3fc1443ff8740a0fea723663f370e4420d613de0ff235c092de795ccaadafe59927f3bea09f28c06061182befb1ea878a318514177ece94e0e4d86b0d9c56b2448d7779169e1600ffe6b2b58881854c43823d006f93ab9661c98682cb7da11b852ad696b2616ba03519b5ceeb6afcbb80447a34854ec758a97e2ac1a83315c263ca3c9c6c6036a056cb536061e1c618e1064dc7dc73c2c9cc74241ff05bb8ed510fc9fedfb4c7d015316a9bfd19f29f24e89731fc4371c129440e81579371a61943f72781b605a092b31d8d16b366c99e83642871baa8fc7a05d8c59bb2ef47b4357d926918a71023a07ec0da864e22ffef4506e63b34bd0a397162061d7e0183c919d50954b6d0fe901ab22335960266443cd2c3f2aadf7281d38f87208b85cc019006933bc4f06377b416ec99320d7f901d9bda2d8fee84491848cfec58cccd24f3174b51ceb0759fa453c60f9927af64c1f4bde55b14e4d1cbc37c391ef0f0c559e9fac08f3076e1f3b6b6ed1604f27a5ae09a96d2817ad31f212ff2530ba118546fe109b320f225daf0ec9d41132d003b94a1fca3e66edec338f8b30ab260158ff7c68a2ab0b56822a049302cb679df53ccd4fbd1833cf7ee8adcbc686088f5775a4c4affb0f01ada9eb8137ebbce810ed280794cc2eab7b170d0d588062fcbb6f1e1dd4860f1eb0a183e55487cc2d5fc243e43481705797ff29519042fadbf5f01a645ab10686ba37c0433724afe6d181538711e39fedb438a2a70e01af415fa9ccfecf98052e75ff0bb8bf3efcdd4e20142ceed0c3fb865057da3d6c7d0c6d745c4ee05a0f1c7a66b5dbc86ed5032c5431887489519ae534812b97e88258076a3aa72a9d0c1d14dc5259cc38b6f4a2dcb0ed4a24410f3d9e551de05019b36801878967b9020bc7d0eec61368d9e9ae73defe810844377c49c1d1c18e8589d502bbeea0670567285315849788f1169a091086628b7180ece66dcf0a3fcbf95583f49a695308c519740fb511b07d163de3418838c835cadcef9e8f21f430d3ddf72f1437bd09119e706a35cc6f17493204e7c45bb453b04a0e1bb7218260dc4fd7beb4a4ef05377ddf2a642995d0871d1ba7876662e7c488437ff00b503c116a52559839880cebd076f9e27373bade415af6697591d6fae0f4d053fb80d212561f8951186109fb0a4bf2734b130d1de18e81f2bf5319a797dbaa0a778c1d04ee008f8cc93b05a2bd7a9860d57a85d475f53b279b09209889c709212dd1eaaa5db0994a428d0e18820aec2d3a2d26b09cacecce03e4d68ab644357d253169d12a18d85a4b170a0939517f9c28ef3b11a201299f7e7eef40df09a1ff6bfd9b469cdcc6816d840cbae00c1dfc90841bc37dfceb787a8fe262243fa620c60ab32766d3fd0fac030ba15c761e67c925516cea0474bbe9fc53f1e382ceaaa13e3e92ef09c1d8cd9209b6f8d790384e3872547badcd44ccacff8d22f052efcb689e575976ba969e92051071535f489ba406bff0d9771af532e1a3220be52ae0d9e2ab5acc1cef295b033bd7c4ebfea03b693613305130e2e004845bb3f95ddc83b97bdef2064bd66c00e1aebf478dafbd7df8f033e2cff48d3227e6e7c884c5c96cbe2f2988a495bd0e4d7f139f9edaac87656d72c2df9412285ae72a1aa5f7c2d6b27e2813a50700031affd03f8788de30bf9e4eb732654af242b9b5e0e4f3cf6127ff2c8a43ce790fd2e91097dd72700216997bd9890ea63bf9b48bf75d091e3e0612feea4fa5ff0cb1a3093be2a93d3c7bfa172e99c2bad55b1dae3b327ac0789bf6471d624de007e25088af065263d9f4631281732cba02f19eafa800c44f41a58cc590a8fba10f2a5c4ac1f3f31a8160158d7b5f47d24ab69dd76c72e01dafef50a0d079f333089cacb88da984520970b4bca672d533f97685257d1515169938e7be235b2faa086905c397853fc33aed3c60b87fd0e199f50daaada1a22a9693434cd4566b780b0906947c2596035c8db715afede8df45973f4a06694e8ecdda67a8697708a7084238b3bceeb8ba65dd12f43dfab1c207aa8a900f7b809b8aa633395408e01502fb693b3e51178c0cdcce10c7d726679cb15ae232e144337b1ab5205c5337be04e9d72e283df6196370ebb235f02ac903d3fe059caf601add217dd89676d5fa0a07419cb2f8eb5b7017dc088f11b1a6fef30e20f80ff88e31fd2bd6ba0c69220a2909b58f07c3e7a44826d82d0c5f6145a285af7875885d831ced82d5801d780cddcf4d5613b9bab80ac89448d34a81e7ed69d6fe44f98af4b33435484dd1a205ca0995522098d8bd96f5682aec80520fe5bdf3ae28f307acae93588544073c01d6f06cc4eb6ba9857d75378e70fee6a71b654d727b23ed1b8e362a8e2cf20d04f115da1e2a20f8011161aebcbfd81ae8d91e9b4351725d6a63520bd5706e4d0aa5dfaf6d05c11a806676d78d8c79b97b60d6169371836993cb956106819a5501e4fa0af60fb8c6a7529e232db7e6be91c8947bf2087fb9f9bb97144ae43d360597470bc722328fcb43f296ac0c3be5f46f0e51f998bec4f51c2700a205f5a005e26a93be93121faae385cf1337ead5556c3260c5194a5a8992fcde41718d6a0be60f4e8b0db1492a43e90d308bda245fe128242793d3ea8b8e65fafff01b2506bf8dbdfe8dbf030fb8b633f320f9405cf21aa34445caa983183aa5e1f09aea062bb1a5ae2e4ffa311b206c136c13290e495ddcf227ccf0db6bb2c24c8acf0801101ed33fc5c042cef47c63f2b39b8dc63ba071927ae863ec84411735f2a2780183c7b6096ec02b28c909c204aa17e469f5a92a9ada19567e6978578cddc9d403a335c735848c045336ef091f67e30e70138d5b15bf6282e0b7c369d5541a540a6f2699118ebea5e53ab5fc08f7ced8ee8c20e9033927f147de16d5550273160470bd2fe8894427adf4bd8bba1d2e92ee599bc6d29d8b2b73870522fe715ed20468d71726196b89aefa71416dd3fe41b1d2698a45e027bc65fd61c6a8a8773c05a3cfd7136a2ed6618b0273c42ecccfe0c4e0d254e6409bf3f95fd47200cf5f01ce1049e67ad4067921fe4f69e0dacb52f5b3972bd56510f9ed2515492fe0d80ef246929c05c94da36fada97f82e04b2ee4389a3fd688f35b2206b6bf836c110c56f46614942e3493f0c83ad311326c93bb363b03718f078aec6772e33f81b006c2264dd6cd75dc00613fb4d34421e149ff666d8e60d7f2190ef054d7faa15a0df56b6eb15b5a14b6985791f451fd75909e6703afc82daa99f2a55bcea82e7e090bc87aa7ffb30bbcab79224fa7214aaebd6a0b54c2f63d11be30e0e56d29f104b3897724f0da4a819d8d7591ed5026d8f53dd08faa4abaf6c0d42096fc8fd703b67226e0d35b186545e4ff2a7571811f5a5d53c52ab88bd21a86eab1e1ac210adf4a03adecc65206a4823afb921ecca7161cbf61db5b486c0b3fcbb87601e10a5e320ac9a333e08b61ac8583ffc648edd9f920f234a61e98b0ec50275e177606495ab04ec273046c51233504aa0c3d454647ba54cc912e2422f92b91fe7ab702b4cab47ef7e5de5022dcb28bec1bdc65529d47c403fd57584a5c9d0bbbf3210e34e8e5e1cc4c59984a20f461a64f7b71d1ed6ec9e59359e9a6508aaf61cd990fc77d6f0df7cadde50ae0d5d3b064e9bee0b73ad191054bbf272c5d9dca80890c64b777e8b522047229ece0ec20e5c98ae887aca6f806873a6b0c8edfa3df90012ce5dacca30a923f75d0c60c3df35c728bdf64c8ef631adb4a375b7491c44100d108e1fa45b5cd633c693d45fba80cf18e89b20edc9a4072ab8147cd3fd4d7072dbcf7b28a945c0a9682b0eca3bad987fecb3dabd89faf5f1b04ecdd6de0c20979a9aab051f047e3804cfefe6c70a0dc1b84bb7ff0beab527d12f2ed9b59e706b82ebe8fba56471ca823081a639e1e6c51459888329b8f75071b6e68b1c62a009c83658aafa0ee5b8bfc3f4ebf449ca985b497ce052ba88aafdab7ca66b8d50b920ed242f5dfa37e2c8a2ef980a4edb1b60da242a18efcf2a22e7a7a3357e107a7618a3a070020a077db7d9051bad860704103c9fd67acf9c53ef2f677f02e0d8e487b93d800c064860cd8eef3d4148f73164ba8d8e556a63ca9ff4780830c04bdd9013c9d2664a342ff99a3e6ef99082712e5ec3ac9541bae759dbca146c40a84317ae6053fef48126f0ba970bb657e0bd3d090b5972328a06b028f5d519106314dbcd0a0ed110e3f91db16e006ab27bf183b03916d4bf5abc1f4708a6ad10b9ef58724f8b9cdff81523a3d298dfc4b62a534624f53f4159f988cbc4f2d9900c2e695eaa54dcf999afb451942a5291db88aeb279cfe6f37bf75cca0fb02620e452f8b81765c0891eb3c84825a729fcec6781b2d295b0d047b7fb1f8947a3a0e835d43a2a3abd93b2ca9fabaeed7470e5323d513b3a70119e1a90039cc810f06355e0a94662e4f8a57de4ff90f5e2d7317ec00ce5a7935c300cb5f8e161eab0abf961ee7667bfa08c5959652aceafda3d551144bb260eb048d5d269876828c004bd516ecc0849b1910ccf631d5662ca27a36a60473718e53e6a0806489264201d69e7816a464d4a030d4e03744092b7fcb4f6746bdf6bbd0cea941d9500a5a0c true +check_ring_signature f7eea42c757d64aeee3e2ba2888545a2897df324ebdd6a6f2ce74c15437ad871 627e1f292bfb3e2f21e6fe08a280220ba43ea76eb70f8285d9af3a6f2b5a3a6d 64 f6e29e9542c42fa55185de68255ed3705433f453f90d6edc69eeff6e32cd3e2a 6ee010f13917a61e7efc9efa060b480ec71ad2b6302f97f3b8968ae814458751 1b058df4ae7303120d2619265cf5089f941df2ef95af77bfaf4b1353ca74f300 2410651cdb18cadf1681d02c409174f4365ceeb3df9fe47218c58e6c15d36cd0 c3ca95fc5edef1043e685ea53a070b213fd42f372c67be443288cbe1cb204fce 7863f51a2d4cd5bc11fae7c9d673475e2913570d516641198ce0a45468295d4f 627db6d7bf8a11765913f118ec19188da8e8c4db1977e8e12b7f65de20483a12 4a36a7d46b11af2e0fd7314f8f63ad3a62b57217c519b22c0689b7dc15f8cfe2 1d2cdcba3b866f7232fe19709fc99a0634545115def2529c0ca13bf19fa4e64d 5ba5c3891f02c2584131eea470570295fa3a1772b5ac728cafe5664755594108 36dd502a5a7e9748f0d0810eb738b95eb87fe24769b9029b0c52608685ba7c43 f7a9f93f6f465b83c8dedc8c00298cde3564efd8c5d3200ddb7d0bb33676e9ab 9d12c257735e8ac8ec2417b18b810fad463989da4f30fca2cdbff75f4cf9482e a8cca6a17a2926d1e1cb511cbc868c86cf548087210e3cc18d90dbc324c190c0 6966bb75c39d93bb015627162887b5fb8c4e1fea793a46ee6e5a3fc71560bb8f c14da109799a94ecc96bef92f5755b16c53a2318b05d8ddbf9abc33d1b0e8d93 388e6b4bc7f0f28cc1612d15b60c59bb79329276e875988b2d8d56ecb8bb1a12 569ee79372f31947e9799420c3a60d6569b24e5dd3290170f62ac63d2c8c0b19 eba03a672f6a07220a88f00a50efe46a275630f1d670c36180f150dfe224c064 46b7f02f789c4a8acbb4794aa4adc6392ec9d315470885d605ea6e4783280612 1b0789c31ad6210c24d1ef6f9705fdc56051b82b67714a4d71032851c1419154 31bda54adf552dfffbc695d4160f0f96a5aadb8d2bcd07b606871a5188794df7 93d122253d0b2ea24d478a2d7e7ddf4206b0894642042b7a7726a919655be0ee 7a838107dc52787f67bef084799b91c559277217bb72d9b07cd55fc6ea65a3cc b080b87d2a04c81d7f6a3ebd1d216bda7f1c7272c80cfa5aa5f659410ae8f398 ac749feedd23b97ba67219c00da053873904b3fde8098810e7426240e75241b7 70afaa47d877efcafe71ac8643fe43648f24a2879bbd73e47ca008c21bf546c0 4b8220ec14d153f3af8d94ed446bfc6d2a81bd904e85bd7152040ea92e05e7cd eb03ae62833871cac712f7e226d55118a5e763c15c8fb268b9d6cc6ea1ffd8e7 8a6d5fcce58ddc9748dac97450d66f61f0b0ffff9a20b59fb75d144f453a3951 be01e351f3686ee92c76138faebf0aa57d773da58639a5c2e740ace28abbaae9 404f46fc10c37af90b25e1aa0007a159a58c6c693fe2449a804c3d93d746f881 510687a7cba82954fa9365d930984011c49d796288f3b6bad92f2e14c52eb46c aa0ebe02ca72e65151a648284da0bc119ad935690708caf2fcc82960b61714e3 15c568c8437c9520089520870e802457f89050e6f887e07ffce54de9abcef390 24517434c52a73f512716eb6d8e3983383ef434148f8b3e9ba743b1b12005548 8eca4a40c9c0d66beddb9d0fce5fb46734d89d79e1b2f35e9f335195f1bb22a8 84c52efeda870fb07d9e82494e744c7b78f53b7b6ca7dc3956c4be0fb76a744d a1303677eda1bf659e1ef7a1da30e3ad5aa4d3cdf5ae793602f2c97297634ce8 0c986e7f29614eb1a80de38de415b8e135f17dafee4b702798a54b5ae12fdbc2 7274cd5eb066d485ad292401775fe05951680eab6c5bd28e70f31729a1f5887a 98997f25a50feb13e1b64c440ee5735879bff82a14cfb8b1cfbba2b4620fb4d9 3b48cb02f2608bafb477e6b8a7c8a880908fdaa0174d2176a26428c01808ef62 e831b4b58063a191ad0a57b3463abaa5927491c3e6e7d6ba264f2697a092cb84 000643a57d5feb6a08076e37d7c89ef57bbf25cb7e228859a5191c418621b758 f5fb1bb8fb6a88bca43d1181af4df77fdb3530ec791bd38ec4ad8e94347f5a11 fb426835fa2783b38c316a0a723e9e46ed2f94645f6975289e9580d07a0b75ac 55b80010477d23150f3f0e85e3c3a579b36b0bdab3a8b75c87346fb189df0ba2 ab4502cf7a1998b0304b36b975c7f16de2675643662041146bf8ba7134f1fce4 dbd226c89d6d2e07bd77c47897468659b1e385103d3ff1f3d20e3634771940f5 fdb211ac390ef6499d46a1e58c4ca544c216f0856166f99e958c426a2852912a 42647368ac9dcf21d3fca32eb923212e4988f3280b655860e18f1321cea75bb0 e5f673168e30963997462cbe27063ef918c7fd31b11cb63c11adfa07d7c24a12 fcd7309e2e03d0b9a498f53bf666ab710a8de46764deea7b8981d6e090a6b9d0 5d9bcff66d05c40689ccf1015987ea0c0049d7d13ed09b2e4505b88020d597c3 9390dfffc4a34657c465e60bc84810e42e75a1028ee3d7433527dfad202936e4 424f1e7fc22a3cb31c30eed324672e3ab52312b984eaa5702cd8e1d27f323ae4 3bd616dd8d6ee40105e1696ea41879cacd7ce8ea265be9b28ca622e02281ca03 91ce8ba344b0161d7bbbb881354c931bdabfdd320359aadaab599d216199c89c 0221379d849ab822162a6f86aa6e98e73ed64f9cffd7eab97c72566838129b3f 5fdd5a3f96492824e70e19cc62d925622c25cde005b3eda879b190b6f367a7ce e886a56819d2a1ad531062103b500101cd43c44d872a13c0a6b58716a14dd815 e974be0dd96f1cc6da3c365cb2ddf3e29a1f25bc9c96858eb92874ac327fedfb ca47d0b564f996d316948d2723b87682943ddcf215ceb54521a7cafb090dfe37 b3572883bbc5e3ca744a0d4de123a1c64f0186e3702e1257f2a13e249af1670b5075877537c0df46ff37c4ee8cec3f86d0c421aa061d5f3c5b9003b4d6b2f600d214b02cc94cc7cf2003c8a0edae5136b2361436c9e87861f3b864c18c868503f3dd7573a4fa26ae04e1db2988d4925f7770e3512d8a128f8f5829c97454b90c0db66b2d40de017b70d0a189f6c997a49191e76bb50afdf7a77ac347ebbb53043f88765ba0e14ea7b31afb4917159fd54d4924964bc13850f3b8d37253639104d7afd198d2ec064caabc5dc826f55af1a71c7d75845ba121861fac55dc269d06ff59da103a894ccdefa3358a2308d72b3452e7236efbb07fe36f9c284a12da0997c7cb2233cb7c15360b047dbd8dfc067e56209c585535485d81062dc9d62b05a94713c76cbe0febafd26686058843c63478563c269054b753083a9c0089660afb7154b40e90f60339fd18e840a6698e170b679c2128242074d991b355053b078f3c75045a0f4ed6d3cacb4c7ecdc1c3f0ea6eb70b9e80722df8f57bd9b142079ff5cc44329098450c4b2e8a1aafb689bbd2f869f6a90e236dc0cc81270712090eeddb944448aa8953aa316aad37de41bfa39f40548b136dbffff32659cda30e5467adf27b362bf7534786e64d4020e64f604cacc01d30376efa11109724120c0ca8985ec8fe38396417662021fab2befa619d2598601622bbabd42d357d950cae8da258ffb5db998b1b1108dac107af4e4bac2cc96e6f8a6ebc218c0f1a32097aecfc373c55ba0beaa6069ffd95424de968a8ad944a553c70c1ae8c5f079408d133d2ea505bf0c5ac1f76888614caa5f31354b6ae510dfed981a476b8bd8502f80656fd5fff05e55bfff7392c1d6cb4d85e15ee80eb25770fc147d6f6ab630c78afc12da60ef21e45bb7332e42a31bf625e7378f0823fda201cf3569f0f6b097a8bd70f6843776300f459f6686ed7fb324995d0aef3c70bb8ccfc7ef24314000fdbaa465a2df010a07c95a88172498cc7008afc8c0c0c17918a5cf61e266c0b78d4efa1a923c26b342eea5d1e353d13d7e30e3afc9b8854b804017f31e5f80b4ae19ef1915fc3f8f7afed4101e62b361350e2fc1f100ea490eb8f5b56ca1c05334676038ae7e1c6a7ff3cd30d3f2f1b67380f8eae2ba665f1c5d02b0444560fb3a9b812d8d92717ff63c843e20c6db4807162184f81f68b28c566a98236a30eb95dac143077e0af7e57789f3dc0ffcb25de9cf8eaf92e946a2cd1e2b3d019091584f699fbc55c93f34b771d5f1d76a111cf9edcf830e1cd22b0cbb87e233805e084a5519848b2121d6f7109cb30294be0e178901e9f88826eb8b450bad01503bf2d6f38ac933ac0b6b472cbc41e03915aa2e2308ff2c388b76454e91170a908bd01dc0ad47e0335874e6abe0ad71ffe52495ee11e5ac4916fb30a445cc43e03920b8c8e99181dbbdea618ea479503953a7dfac4f605e0ab88d94e3ce65dad0b224de96bc73bd00006604cd6a6932cb927d733ff46c5a82deeaceff6040c28090d6d1b052980aea15a58332865774aaa929cf86d3e4e92f691585be860ee9f0484d2a475b0031802d26badd3711847f7ca244c590e23700b56b3e918bcb6f70a473c8560775f7d538b0181e8b70dddf187785d007febb98bdc71bd0f8ef7ba0fc935e51b91743d74ee72dbc77278f4def20447d1a44165df486785ca9e772e0592410357e71dd1b81413ff2dac9af1c52132dac8147530be39a562b5bea1330a839fbd83ea735c3aa86e863b3d6c33918b0d75b3930f42a3872c629c966423067022d64c4fa90455c3279df1c11c9756c0efe7326334ef6623f7102b215ff60c75ae85e4af50bb247e87fb9cd6d3896537d24cb0205f40e36ae5d47d677f2c007a3ef938766d0b492957003f9c8675b66c99170fb098a42fba61ccde5191f30ff4537f00505ee54d1bf8986909c7603b0f90880f5d987694d955c9fea5a6ba0cbfbce2a716852402b7831ba437c75be5aa237ab9d1a2fba507ea4178c04c7804fc4c1dda1ff828f8bce0b3d5c4844585f1da41c2dd30092d8f0a8aff01ab860f39aeed8915b6e34ff56d52420b6cbc386d7073eb667c214cc23ba2b80da6be0b97a412d0b663adce4bab6b74787c0ad7ee0a1c3456ed4cb0842ec8b2a45f330f0e4bd2653eef049a0028389136a57e6e8dd070db30d9dfafb6d77d6814a0920fd42c8c2a0aebd7f44969751651765318b8f281d658a8ab17e367a7f0a040ac06ee201ca23e0d0fc67fa1c87e4a0bf2af8de7f9634fead66af67999de529b2a0ef2d5c0d79c95cf1abf2b2c8e881037f96654a209d0fe883b19fb3bf320ab12094f425ea043caf8340fe213c1cb900ea06aa79e4888fa2f942a4e822b24977b0d97cd643cab5deed30816808c3524ef23c7e703b2cf39ac084e2213e0e687570218895230c0f193fbaa2bb4e68deaa0cc8255681169697249672d9a913bb5270152edc3c1c55f85bc79e105ff37b01a321325330cdd36dc347f850c42736bbd038f003216c925aaa9382dc469ec216d37d08d153b5145ec1e3a921b20795016095d720b655e667737c8cc0ada25d9b9b178ada66f0338b812d930ce8919d49506bce30d94bd164763f1e4573afb9f2550ad3401800d1ef28c56d5e72ae288a20987c4c81a73c2234a852e4acd1e4a4c341a2f2bf0aa7e8b4b12ec2da5ed9b330e275f29a3a13b585215be87178564c5fd4f17dc53051ab8f944d8662f68b49e0818c319ffb809d60603dc320f85411464dc26c1e7ee60e7a70d120cb8d1eb210158c29621abe1f8880cd93fdf57a5a2c359ffe52a3388c8939fa22b188752e90a191de9f86f4ee18c65de37d3c59b28710f6d9bf5bdfc4b14292175e84259d506888b8a3808145eeb4cefb9dd141bc3eebda6b2a15f05e1e793c91891f710cd0ac61acfb496c83e20b0f157ee9ca7422b3b05a6ae5f0ea1821fe22d691fc9c1065a0ca8d527c120659c8788880377000f2fe8e46a903bb7bce99477791facc20dd04f578b6f39c0a6a20756f1b229a80ff82e5ae37695c307014d8243a0163a06d49b6bf54b7f4ba8cb849a88181780267287a509a19f72a52402a02679eb3700e983d2fd52935469edd120ce78b99032ee3bf54b1e644269db14d008ded95a0b309e5700e6ea60c4b55935401efcde72098ffcad89d1650d873d09c9282f2a07764a013ddb04cfe6cf4ee490cee065cd3972a2ba5a27db470deb6cf566123b04c16b6b44a3b1c29a2224d6c1173e115bf43b9c971405b53a8f33afd108e063006fd16364d0e67145eabe4ec4d6fa6d6a967a7886876c5b7dd42e1c7c8c0db60f53e72c496300c228ca632a0fda88d7df41761e5f8f6748a9986fa243c341af06bfa60e2968ace139746aacf63617f8bf4652be11617354b0d9754e9197c3f30fe2135613e2a7748e5cf2b8d9ff42f55953a61d71a9c553ca75802d60ea3d950a9317d5a1dbc552bcd51a06d4a742f461ad0950a3165b3f588ffec2f1cee0cb02ab05ee1ae065a19ab6000013fb529d0510767cd037a58a22bb965524a6925d0aa0503a6f1fcb4291941232f8d006a5df1b5def3b4f31269812206560d8fa1608369b7d453d0d21e4c3a8e2ed6d44a32b85758057327837c20fd9d2e5905f2c09d04775576d1101288545ebd29518aaaa4698df24e7467035648f85bc1fd02404928010efded15d56e744b59c92f901de39e7ab5606fb3bdf73d903d46f8ae2042e2cb6ba0c469da2756849dedd50ad1d10950c86df85494609fc2da74b55760ab95b3adc72ada23b04babbfefaacd1c9816deb8694fccfe2f597dae4bfb7c205afa059c3e71a23cdb5488b1b0241262441a5828b0c93e2b1b860bfbeb058800c6ea08c4bb5ec9045bc051c1ba40866cb3102b4bd81804c9732ae9accd1a3c20159d76a2e4d5fdbc5c5f41cbea25fa4b547017ad6a8d0a4a2e22542ab695c0d08ac761d2fa8b89eac159e5423eba3921dce6e37f097b0beb92be06ed66a85470fdd163e92aae34993a79b1bf2c7170d2786c98c921a7e2a92626551baa9da77090781364e60e95b62120a5cb530c7d3c3c64ce382714525919fa5c4caa8a9ac08d199f12f00331ec747acc31d9d38dd838a0cfbd84040ce781dde776cfd49460cf280c3b9e39aedde17151ac1398de21d15d26dbc514b609383b8be6e820bd103ee46aa55232c58f3002ef5540dde43bb7f1c4805d4a16d46a4739e64a4d82b0b908237d6ae9b48e17e3d6bfe157be3cd53b9a1dc0d37bc36a953e6caa91af30ffc67987dc48f4cee9387c03b309513fab288bf54923d68d260bdb6fb126b800d726f765b763a2d5661790e69412e9ec9bf7b8537a66c60cc6c02897081e749025b09a916f442e1f7b09471c7703fd35c5b8ebac928a1356d77bd1f5b43e3490ea5d7c9a442ffedb7bea1ae4e8fad33e2e5e8aac8a85c408c842a1ba26f746e04d9dcd0d35d53edd4d9bd4a8ec950c14408075a80ba40088fb30ea4cc03b4ef06bc59dde9aa754d438bd650260651a6c81e42aca196c6257caf3b0b0f8d3726067d1a4e1354dd22b7f8b23df74b2272ffa66f540d083356f6f6aef2fc211f130f000ecb0293a17ec217b56f43d056914a6feae854cfcfcb14d0ae9493bf28600668429e4beec8b57cd0852f286e7d7f3b0cc09a467cc80664adad5ef4d4064602dddbb1ad165bcefaf99ab6c31d5a432a2d32df3c9c9ec1e07aa33face45af10d4bb52c84171cc05c4ff018b61e22a3463655a47813f61be2b8ac3984e246230bd3b2db2b598dc5d627b743fdb09080fb6c4b662412dce103a7c6313225f8da0b92a55c16de33369585d2c75ba9e5cf5f14ca85f4895a5e41993f15743dcafa037ae77577840b8365b0710ddf2c594d46ca19fcdae65a2011532db97a34e67b0afe8a3804a3b55bf59bc0a8b975f361800c169578d3d69702c8ea468b859161078be1584eb26195e1ed3de0341b14e8e8a7f7f5a967ce58682dafe43cff9295003bbc4bd0fc3e9d43505e50c316216d6016d274d73205240d5281f853cfbe860c10f9fa57e13ccd933c68181ace6a8d83c8224d853a305279816d6b4953a07c0f259caefe1b1b4351364208dda66d904e7c9b6849c66cb7a1e70bd3064ec460046f2c71820c4c44c9592117c307f18188a65eb4d145b0f113545ffdc3839bd40a06ba82f611371c1fd83862e39f669b3954caf2dc29a8593f72b28eb6ef148807a9a7b8874edf1605e9a29e1f31850ee4a5d6ec4196f31a27759cae2759619408454014f644156409a81864028be60e1e28f8ab49fba774fede57f2e591286b0cbf5e5756cdbe4f94e7f3ce22548dd05e4e6ae247b2506c9d6678e6516e1ff70f6660047da81d0e0a44bd7bdc6734d62faeeb29f6c33b631086e96a99a41e320cba91f9fe7a3cb0930a67b4b3b756ac29d44e0e46cb792207a0f73f466af1280d6a90380f8402d50f2eef812b563b7d1157ff87b2c62bedcfaa23bd27dca35b05f348ff7fbeaf9ff174dabb9e467a568bf02bd8c52996df672c53708a8838ef06d3d0152cec9d1e27d87c16af9089dc6852504e298497f044358bdcf082343f089ceb68eada73c7e43966fb494d6dd1bdddc1b6ac20987a26b806e347579b970f9b1195e4de62df77aa8602aac023414a2e9b0da265605a2efcf3b84fc5896202b7cd84bfc952893b6900fdea3fcc1dc1f35b6dc5718392b18e9227a67b49d20e33e4125e1c60e4ca5d8c5bb87ffcc01eb54adf0ddf0d4e491e5d559639bf2907 false +check_ring_signature 2085554cd43fa6fee95a69b036e4502aa8b94a8b88e4f416cab78f1a0fb6d2bb 494ab3bd95ccf12dd32df2f14ac0f3699c1355c9a8c5c16f01aa7f454f105b2a 8 5705e61e196dcee33ceccdff2e9d3557397bc3e21e05474c94484f437323e568 3ad8ff4bc37600d8df73d308fb7e37690cc0e6703a85a7c1fe406f5f60092eab 86030312ff655d20c6f9a7cf8316a0a0abb23ff94be2410ebb11e24366265e58 81bd7345cd4abf7982cdb69b3514347a623504bcbcd9af74d0950d51c5a506d7 cab826db0e551e7d33c4d191497e67cbb194109869c8bb2aa3835bae687fb7c2 ddf8d242d3c53859f176c6eb3a8c571b217c9b666ffbf45238bcd6f88afe29ae 08e92db628244049d59774e7f2aa84cfe7898143d32b9efd9a7d20e395c5d1c9 4e6f299796995e02ba42050b8ec82c876249f9c8ae7e76f7b7c19bc8150bcd82 724dd3bf65ba21439b1c4daff7b445049e770a7fca68b70291b788eb0f4fd8059ec4e523a75db87d80dddaad218052cb4156f2918b72bd9fdc5ab22b4b5a180522f666131ff2a70658733d008083181a469a959be83f3836f49cfff7c27686036dbcad87ade8cb8480bcb210fc7549c8684bdcf1673c811453638152b7f2d702eae17f625b177e2a71b0111ec83f1194a15813e5df07e75ac1e71ab28633bf0292798b31361fba12e64d552b25d26d18aac4ff10a3b8baf814d8c4e89a802e05b4d708dfa69b7b40e96e68ca3407537aa31ac67b2e1386a27e30786303de3c055e5cfefe2b566765b7ef76d9a0d4c278cf2fcb1148a46047de2af88a3b8e5a03cc32afb9d1300c40b6b99820628742d630bfbaa02048baee081b527c867aa20db9beff34b8579ac77e7ed0b4ee380e9e2bef10c32bfb72635ce92a73ec70fb0ece615531f9290c66dd9c56f122e1559c10867584208a831835e2812b3b9c0c075614cf82b6d9c8b465f4343cd46c70343b720e8a1329b8cb2f6a5aeed2c9bf0b392dd1c9c3ece18cde5b5ed805e8f9dded30d10150bbdd86a9adf00deeba6502e77557ee8990f4ccdbc6d5f4880c4f8847449a9ef2bfa2721ccc24ae8586a40ace5c764a509cd0ea0b4a963fdb84415f19691e94c013728e3dc748915ddcdc0a0ce1baae57300b5a9081f9743c28e335b9374fb0bd57e63810b1675ac0884f0f false +check_ring_signature 1f6988dacfee0bb7db9b0762c861928f9961339fa7338d07eddde9f8d41956e3 72d4a3b35991a5bfa1824f3b210f0340cc4f533400e63081466dfb495e4e6125 27 f81a70d324075db52c201cdb0a6ce4cbc954afc9a39a0f68da984fe1f1065405 6f9b113fa8077952e4406f9111ddbe0c381d39f153411c82e33489bb2fcdbfbf bdc908352520ece2abd2ea1a20468526e1f81ca940e43252f6ff60ee6b21c401 3a57549841a05f54c83d9e608faa866f74263b72a655cd56ccf762f110cd4523 2810e8355d52b99b32f9b33aab953facfe86a4049926d8cc64c2f8944756f53e 1018c474646c371b0c479500e275f2d7f7b7c989e43dfae4cbd27c5c53897189 91304221fa910d3d1f88367d363518cafd8ed6319055731fb22d4ce99833d800 160c4f5f3726160e164c3c2e284f4e99c34e8905290cf5ce60410b59579505fa 05cebc7f4c09e3a09151eb7ed0737d547bdf886015524d3469940700807d0fc6 660f61ccfe7ceaf89d4deb2730e586ca947d17fb3af4c3be7ab5e9f637d2d6b8 cbde4c6a645c6ecff58cb2b819f193daa28774fd3f4d7ad5b34834d0876411be e7b12ffea9bacd86b54768ecebce7528e03e927f8766587d6742a2f763583d43 0d25f2380d60c846301e1e83c4503ea5e6fab8668e56c63a3eb75f76baaef8ed d3953f264a0ae6361e528796b9f1cbdea6b4193d390ee6a5f3331bc59ac95412 6af511d6326204c83dbac21645ae9e87bd8f98c7d4256a5006582da95db3be67 dc8b5fb30941abbf13834623a5894ae0dc38d97064ebb4966ea4a5e18e6ed074 29f99716e9dcab6cdedcfc14019550db7bd6bc6a08b91c353284775c6b7129bd d14325f9c3bbb3ca1d30ad7d9dd3d17989641496c9a224fe03676e997f018485 4e8d2d762ca557120669f5da26c8a309a53882f13af8d4b2271f9983a7d926c1 c7ac75ac5c125b9f3d3469d347c604ea720d7ff584be0837890cdcf0acddfb6c af40b91a91bea692b48110877d36057d8b367b99c6ec7e53f0392ef79fd4d5d0 8803a3f9056444f96276261e8876ac07e80424ddad3cdaedcfa03bea8d53d008 73b675974b7bd101a92d8f112872f8a2a139a276b1fb6d55104a4b8864c17f5e a3245dbc16377a10b9a7db088070948e2df9dd7b9a0d0f938804030a13b4ece2 e11a3ed526a7821547000dfe68a17e19c6884db51879936ddaab189237ded4e6 e2697e58edf365c8aaae11fc0d6d94a1e124e69f460d6ac7c9fb7f8e8027c01f 4ce911f5f19f7306600d4e7b1a64079ee0eb329fad0c3e2f08bfcc25a321097e c23c180adcf29a74bd8b04432036df8855b2a900145a6adceb2d2fc71a02fa0d9e67e957801703f8e83c08cd765fd01f05cbabe6509d0449c8ce3d1d3169450756886d672fe581113240ece1535ce986738f453ab99dfcc3f7eb2024768533081b6efa66cecb218b7977de072226f9d8abbe03fdc916143edf1ff0b73cef430f6b7b89b8291907c0791ba9a367d4937edb6604752c6e4b60800930312bb0830736eda0027efaf6860d330b22f29121b64210c8578d78519b22e592562c15dd0e764d74d0a40d811a5b2721035c6851cb61525646a22b581a091fe020e2c1410dab9883b29800f02d7595d396722d408ddc80b5244efbc76fb3ac8b93bcd28b026d918b5b883b2776910afff8fcb9c0121e87284dd741aa6f873d9493458446041949c312e32118865951999396d2e52d775d50e83ec7c9d93394046fdc99f30a92b05051bff268dca0d6cc8000e44871427d5e991bd9d88a8e36392e3f6f1e03ed37e3b91cee58e66f95941b51226c74262ec13451ae8dfc0aca36f10c90810bbbc93c49a8bcd2a2f0e7b9811242fe49ab1768c4537d683bb045ace25ac16a08771c980d4cf8a2ed4d72275f613358fc082835589b675d2e500fba42f8152e03760152352bea3d10ab981536aec295b5198e65bab94b98fad414167d96dfc902add5551401b50857ac6a9af68dbae5c8da14f0410432f489e5a17a5a2939ea0f01516a9e80fc572dda27ae7b727c232b261adfce7b03abcab0ff42cf8c1f9d09876333db4e83718725aa290cffb0ea6181beb687c3b2f30ff0e47afdd54d720816c7497328775f9a4088bb973718360961c95fb9f9b2d5c4d7c590478aa6240b0c59735d5cc12b172ec69c7ca0d851054e89a05e64eda87e91bd32598153ab0c007a2dcbf94bc8108092ac207c602d4f78914d788b4bc93a089056116eefed0d988d1d179d9d32b5da68b5e6585663b6682a8d28c2c6bf02abf0d51220766a0f4b0b229e8e04b2acf9ca19e942d0a05c4167cfcbc9bf7160f0e18708c8a9910e4ccb2c7ba0f2df2a8d08fca37e5f8a7daa5ea7b4f82893b837d064271d4982004f98f95d9729b8bdb0328251a7d02fb58c2bb538b12a54f7ce5491ff3ee9ee0564594fbf7b7a10d11bb37d752ed45948f5dc37397d82310ce4602ce298622a042f49d4984d3d8a53ae4fcd612fde3bbf3f610653cbc115d98883bac07fdecb03df8e7e76d1df745de9f4a8e4a6129835a64f98cf4cb026324900ec518aad0006c430eece7952384d11ea0f878520bb6c955980e299442c604ea15072dd3c3b0141e30d711c486a820205650d9e33af26c4ba937d6fee9cb2aeee893e5144fd02cd2aa0a1894698862ea003ae13365d06d97a0e516c2e88554d5c6b3e38ab0906f9561ac9484de45eb772e3fa404e3c88b22e4d413e412868dd0a99c30a39df0e98e15107aedf4f813781760b8b72345832fe61907b88925ecce644e8d311110a7c25e10a30cba96265cd87c1fab573d9aeae14e15d56571da5a85ef29bfd9b0aaada0d9ec8450a0ad45616a37069e9d1e2020d287076f8d64843de8a4fc7d60850080136e81f95fad2a0a5698fc7f8b8d6bdf8381e01e573aad00d8bd91dc2010f5980e5fe38372cf71369fa432ed9e647babe64b6ce4c784282cb805e14c5067f567b625a9036183722ac9acd3d65b9e077e475e406e1bb1a6dbc1ec8bbdc0a1c5a37f3f951b56796181518fb838d67d57d0d02e0562078831b5c0c66183803aca769f70e63b451392b362b427a1637e346412c46b729c3895afcec1cd9d40f24dbcfcf4bc2d204dd0b3dc2e3c1513b6d514f90f51659d1ac03edbec345970540bf14e43b042a1658bad3744ba6a21e8e25bde9b90db24c7018718a6391c100906e949bf5ba6a4cbaaa9e88fd36fb8fb220264ea2190f333d9f127dc6d1aa0c958ad6037f0ca41dc868e32d799e94c917bddee56ef4186761df044eb85b29018044543a04da8394f4d4db75c645a120363c351564abbea9b724957f5e53b40490c563336fcee96ffda3b8015e4434cdee11b1deb754b8ae395280428c8aa20f94ec4357958d60649f76881ae4b2abc129c654c1e23d622940b732d32d858502bbfd7409b9a52462b88fc3457aec252a9926724a4b315b903bf54cad40e45d06b3f1e3dc6ce3ef368b38a1977db2e0812bb45f459524f007d1a1c4e2d94db005b8c2087076b13b21bd65198ce82791ae728ea6ba59cd65ebdfa4bb40ace6fc0fb22a9642482ecbf2db84bebc75670e0e50fe14ef3c3d0f1cdb23cdcc6a4fea040a45d75f9693fd1fc9366b795c596a4259288b19b3d341b305cd90e832bab0078798abe1fa66c46412e15af2e49220ad1f263ca15d0ab855e6ec4ef675bb7306c73dc7efa8ed0dfc7f9df22dbf5357c012a840f2fe2393ef8b736319941ea80c false +check_ring_signature 56ea6786b1920e8a71fd384d7f9116f7f108c642585a567149c78e70ea4724b5 c2917dfd84a93647e9e7aa0daa98ec7e09d09908e9aed0fe588ce7970d945075 40 365df93da98af452645178e6a50629c264685af230c7cecc28c8fa64523c18c4 220c04549970cfe4ddbd1871aa127bbb133ef7cbcb99f7096dcd4573b8eb8c7d 8c961cd40103fa42d0db2785a8c260b0e86adb2e23598dcaaacc4a30d6417436 ac4efa94c94cb649aa4b44fb9a6001e615054530d372c07aeb6d52d83c198e31 208ca0bcc05a40170c3564ee9d2217013b6af7884d89ef3db0d43ee3ab934506 62a7363396d4d0bc9234f7be38524acd476d7ab11f82cf156c7af7e6602ec2b7 edc020d43b5eb1f5f713e141685abdb95b0e7e646da5568c415cae5e869460c4 b46a4f546f01239a91b5fd75d824538039cfe50fab62f01dab04ae287f8378e0 04f0a6a263a0708b9517836359df81ab340bb92ecca11c3755af3845b7408826 503bf8ac6db0d9d68bfde2a876431031a742500cef3c5ba682a97a4ac38326ed 2c9e0d875e19912976f97c038f8aa2040481c6f3bf13019f71f76ff2151aa1c0 c82101eb0e9a25ac102e6b6feccef160bf88fa8bffe1b0d912b8247a870a0f10 83c851c55703c0c23b0e39f13da006943c5a9f19bf264f646a4c0e5d538dc1ee 8b64a9e1c3816c2162fed619e60043639b2ea13c84740f14a9daa97a33cafa70 0a61a3190c90b7744cbaeb069097957f5a3b9c03016d71d75718e1b656e11647 bbe1b248b6bb9dc092d6f2e9207668b224a90da3a4137f86f77587a490d53f60 008504b5deea5c0e00441d32b5c8014a83e7d902b0c104d2ff18f2888ab32b8e e8e52d0c82bd9d744d3d8ffbd3155facc357732c37128b0d19521df871057e6d 4448e78814f582844a7b650daf9b8aba63dbbc10569d5fb02e1e473cab965e11 e705829643edba07751cdafab3b3c7f35208b45219f0c3260103a505818fb5b1 349c5ad570ecfa53c9000809151bf884fd0d6c0d32ed57c8c729a2ca7b224001 a3949e0e86287a96112a45314a88f58fbf4ee59644de943fe7de49fc3706ccb0 3d5d04a1fa404bb7510fdeaa9f18fc229189fd28fd5a370a72086376b64f834c 0236b690a022d020633cb7b93437296b499ad4f2c58256e7897334d346181278 63d1c359a13342cd979970ab8e971de7f1abfac8c6cbec7d6ee7f02f9ab4c0fb 59ca445f5f4008810e0fd11b56195c4654adcdf0eff2227b32b750662b5c4281 1a359a5f0a058182e1d6eccfeb49629109e081bd81061e8ea20fc2eae3386625 8fb072c64c61ce5ae154d9fb181169f5b4fc9be8c4aaa2547a87f1299d12016c 533554bb97190acf20d5a855f4fc8b38db3ac5fc0efe0945be8da697e28d5f20 00a0d663f9ff2341d4299552c522c11a199085cfc718dc6c425c7c278873eb02 2835982a6e5a0eaabc8bd25516e47a2b4a0654b0fafb5f1274f2cbf5962e2386 2d2168c981b9474f1c0d86a5487e34ecb7d5a58549e8d72aaa4449f3bf6139c7 84f51d53f71fdd8009dc265d800ba77f24b24f103abf59165f6441dc58ad2805 40eff5049bda7ea5d9921daeb26724d0b4714f2e2d4aa46627cf2cae7d5726d3 1c51c31ffed028ca251db216b4f72a1d3059daebaeebeac827b0380870c9bbb5 1e5e5866e4bc16483a91ec6e096193a40e5d033f3610558792071062a0c534a9 28886057d538d90614b0ff8b7d017c1de6203f90a97764172821a046d0d11081 11b6ab2dc15bdf4290e0809d80820b6d2fe223a2117dee45e3a61e7f21f5f08b 12087b7a4279fcedeb28e5e014619fe4b68268bf0563ca289f24742d3681c144 1bb8965ea6a6e652d70fe55c372f5a6a748396df28abcedeba6ac1aa3614c618 4066864cbe28f4d96851f359c96d91fea4f44d563f0819c41996aeec9dd0080f107d7b9ae87fa4c558c3cbf31763a5d71f4f893f96657c4b221e738fafefc00a6fd75b2460add63430ab8ab4812316f409e71fd233a31e686b4021e0c4cd360ea49cc786fb05d98f2dd3326f0bffb3f597a9a31c365a0b04216974ea4b1e9f07524c4e9187b533e3eca0b784ac40976e71cdb1953c52c50725ccac030ef847059145cedd715f8f77ded7d55f03fc7aa48ea3cf777fc42745498a147885b9d3075f7c0c5d76e8a9cb1b06ead30ba3931d50ce6e34463cb06858c4d05d2fdb980462ed672dc6ab8eb09e309bde5091b82311b22bd8c325a44312c191ef9a85b309ec21a0f76daea74d1adc8727fff8d9163284f19b059d9cd6b06714ae6682100102bde837fa1d3ff4d333f70aec7795c4dcc5ad8073a9ac5e498f741c85d31a05dcad4b57d33e0b4d119da8a053267f9ced2b1889024ee1fb7b66c3f0810f940c63fd0e1bbf14d990387191e9c97418983653d9cb9374f9b500fea43369011000918ec35829f6a7cc2c8629edd759c8e5d1bd33708b25c1717ba6a751eadb1203eee60b24c95e958bf209bd4249b4e67a6cb75b97bd1a3aaf2bf24401ab37ef057a24afa9edd912eede546d2324c82d5a415d59798a549c7d50b368627408f60e0450852b1c5c3c6d25eccf6115541ae7e7eeda02bdaa629313fef9738d496007f9c4861e686b56ae69fca179eec42ef7d61b9f90331810b4feb608f6d66ee20793a19a7a3392c8df3b312cf58d37622b1fe90102a4d8bb24f6ec6d65706f7504252bed2000b25005928ae496fc54a086d8f6cd15fb42541e8662f9800f7d640a0cebb3865f5d3e26f06cdbc0bb147bb43edf11203312cb35bbcf724093ea880bafe3ace902f99388cf2f7485d48170f17191d01c03c98c29289d6824d701ed0229f6e0a126863743075c6ea230cac2197e9a7318bef2190cfe05d36e9ee8940f99ff8312dd22598adcc5768878d1163f089db8f9ffb35832165bdafd89db72064b3892bf1392f24be829276abf82ca7188750cab09e36ca7eeda64deedd7850ef79cc25282cdee18828c6c0233ecedad4246521ab4d1ff7a3451db306c5d5e0fba2ac2210726b42937cfa76e5258c53b18dd96acc358325180714d9fdab1b5031ddbf3efab372fec1168cf35bffc9e98bd9ad2dffb194f9066b9ef99c3413507f2c807368ab85758b1f32ce47b12f4070e7598cd80b2954e7bcf840d462812027b691a9fa3aff42c3152a0869266c7ca477cae3aee659d6db8c728a5e5d00b0c10e35d70ccf53494c24a37f7230b65358922f69dff21d468638442b48a5be4047799749d17dbd406c3d7fd5f13e4a581c55640dbb3dbf0b68a75a31540eed80de4e3e0a7ba1841be2fc11344e36e87157271507c2a76673e394ff37148d2ff026514919573483693ba6f981c11bc81a550bf0f85d6e4e401de7848eee074f10cdf6f02710a262a9fe83fa0eebe76e118c4989af700fe14e4ee52b13b625c5b06b62743b8fce49943c8eaa17febd5659deb1eb2192a4ee32d3b7430969a23fa05e16b53ba04aa9d9175d06b82109a935675142f9d2e331337d46ec610efbfbe012d99f0df1dcfb7abbf212dbbec0e0d3cb39fc23aa373aae887fd21ea6081db0fc6dd2c0e26bd346045b462723b8038a8ae3b98e8307681110ee24a3d7ae1520527effddbd0139c376f205ae51e91de69c18044aa1c76275c0962833d16696f0d753d078af1591a4254d05a87a63d0d6cf263eb4797ea8338724bd30a0d725308407401aeaa2550f2ae211f4cb8553ab2e519aa198d1d7f6e4daf75e35e896b04e3081d5d664d5584020c59d8afc9edbea79d2c281a611b71e8fabd45a2534a096e33b25992f74b9ab18b852361b539002f94e2ddb865b8f329eba644b9663d08f316590a589093fefc632a41262a6f43316ff71ec5cc43722a3945dcd315a80573a1f3d3f691cf4595638dd885229136a70e99a508bbf32b517a7ac5e3a6090dd40dfaadfeae737bb1f95810ad5ae30bbe58cca7e8d0b038037c3d5a36778504696d840e59dbcd75b2cf02232dffa891cf05b880f8de45126ffda0896c38460dbb6f6040053263b2b898c7d3ddfb3c8b8f65018515206926a2db36744ba845010688587f3b59af7b810ee0944e0126b9cb54e85b268252ae0e8ed41e8445e30a60449881803b4dedaab12575d248305ceba314af62ca5c58af2a7042156a1206228ebb68ed965300aecd719035252d3652d9786b111ffc51928bc65a612cd10fff307e5c90894fa957d831c67104ed472fcb1329a3f3f246d0e2751b9b033602a2567ec20d40980ed5b84c7753c080f133ffe1f4b4ca29d57c75923d5c88060016fc9c35dc3bf0fa5a6d7c266084a7ba0f55ae8c8e8f1498649b16985c2b1c01ff5db3ad8ea52c2793b9bf7e7059bac48eacd2d3083f72cfa2c25716ed076f0c4fcd5fb1efa43b6f7a68b3df0d8131e021b19acbdd01aa18dde11ad4503da707cc05e3960abf489d29feef0928bf22c8cc72c000b036b0711fca222ccec83d0d453da4d34e94a9befc8772292796350a0c1c230b5b38d046b734f8a1726091070a03729943a12f3437cbcc764ac6d87aa539e01f347c5cf179c7dbb9b723d6037324260739304a8fed9f5d484cdda6b6bc87a0fb8907ab9fabc536213578bd09d0953259f64925980520ea8297f0d45cb18776ce2dd8b875533cfe377f874204b7385707659acbe33cf94c4b75a5ed06772a010c691fb621755bb9aef2db9105e6feb05b56251f65acb97c74d32f0c11bb9b79545903f8da6341fe0559a33904349c785b9e0752621b4f54272b5eb9a471fc58d9f93a467601003aff1ebec205076e4f7dc9ee1694493640500f714ea98c27c93eccc6f6877f7b0f5dfa4d510a486dffc097c7d5ae184f12a2d742eaa9c3cb6e798c74e8495c8d2701e3751507cfa18e74614e964856c5782364006ae1c3b052a47896b43c6a3f02fe41231b09d1f9052a8cb26120ffbed2f3f5785717d6940dd48f4294e5ee03d2d06d5b0803cba42c75ec1efe6b91becfa908b4c80e04ae8aa09fed2d5eb0ff5b156202dd03306f23293359cc40736d125feb430a6c2528e3f4d94352a3b070abba928e9d0a2ec75c83b2168157125527deaf1c3dc6d83e8161ae81fa7778676ef3221f880b80e863507d2c766f7d9367996cd211333f77c8fcb640c08205fc934f64167500320f206a96c1b80255dd1cb25f7726bab849273c3eb8909bf465ec58c78f270d5079b9fbff37741fb97b4617989e2617385e10c6ebd159b33dfc303317b0050a71136ea954b9771c38a31da9941cbd6926fc9fd7e4ccd637ec7778d90b3c4e0185faa35ae595885f61bad5be52c29ae43363e3a6532637389bc9571b7c299104713f35c3b53e83fc36f6e188514e45de996a788265890b77a30b9c9d246d390182240d4fb56ed5c923556baa9970993decc5f1d462666262a73a05714a823309efff147e3aa6c3cdc9ba97c73a5aa374e2ce588ddfd750a241da43fda9196401c03010868209221c16769806994908cd3e1027dbe67dc7e67776d894ffb8d20f false +check_ring_signature 7ee3a2cb506bcb4d2972214c312b23231e2b6605777d710836d63b3ff9e87e72 44b8d344ecb772a07b72b43c62302c6d0af2c919bb130c8840ea13ff5944fa39 3 342b57b2474463fb3bbc06941c5d227f600d3ec91a6a1e211ba2b6bcafa50e16 363ec4157ac6268a8fd859a08be6437fbc35ba3c19f98d8bd2f0e7051f126d88 b7716d49262421bd66c174208ce6a1af567c1f63680f78e31aec23cd25717a67 170fda61bd325a34b0ae819dbe93df39dfe44791e65e8adb47b9ebdf840ca607ed46c62f6948a2e780e5c618785272604de7b0ffc454beeab35e2a305790b100044206f358fc04c86d324959d3e18814d35ddf0120ef9de9b8c8c9312d1fbf05e1762b53c3910aaa4ad23e37b1fbfb6497094d5384635bc2fb18e8b49e06830a47db4d0b4220c1e7b09b0377be59bcd4af85b53b45b34f5e32f63b21a36dc90e48d4d92427ec09771c3fa1062d08447d8874a528e0e48c2d7c8eed356e2f1103 true +check_ring_signature 3042567fd2b78b9307c00f6b08dad54a6b721f59c95783bdd534291f153f55bb 4591c46a28fcfe634e1f0408afe00c0071abe2917666ae07e8dd19af17cbf4b4 23 c77aa2c11a77fe2202b8768e6aace6a50ad5052bbf138c5caf32dce80a21d3e2 4a4a38d539c65a8603ec474020c4f861bd43413fc379264be43d5a576dd5f51e 10d987b2b6d1e1a938774b6bee7c897d5accd165089d4f36fa00dab346bc98f3 586fae78eba1f2e9219a5325f429cfdc4eec680f7f87668e481fa213fd532226 d08b6018a59d1765b8b6565dd448203e5b34ac3f62de54059e6e1a96b8da81bb 64051d99eff01d84defd416466f7b81174eede776ca298eeda4c7273eabb5110 1df8cc10d41aa4e07c373151a731e596cc4d9768f47a4429784fd3537356f756 dd7fda05a7024c569f9e8063c32f6826618546fb2353b2d719db4aa694b4ae72 22327aabf27fa605a5d35b0c7be57838267e4b1b4e9542a12bd1530ae85f3bc3 24373afd31347ae325f2d87641e193f9d892a7963bd0094784cfc578af7b48ec 7998f93b6faeb0062f4248571ed50b46bd94502cc59f9f03acb724bae71f8fd9 f93fba8da71ac8aefef4c159f623af35117d2ef1e31ab776d1b33ce9bd8dcd21 8320c8a2acb9362b3f8d4c7208bafa5bb8ad410b4b5131f61bce064a6cd3f21f 4d404c240eef762f21a775b682aec3d98a1dde4a431fc1a20a95b1164e5a35ed fd00a2ede4a48d2d14e65fd022d5ed81b3a291e10baeb8f76f8d863f8498b42d 26088a0b342ccc8d8d872357e90a60083ba0498a63e72b585d3c48c1b4b9fc4c ab48e76e9e66b000e38a131618ba100d43bb879bce14d4067e07e6718b737a44 6aa7e227d3cc4577ed85bd8959e9d2889bae7dd82261b20eea9a6b59014e4eb1 74a08afb79c5ad620002dd22da79a97a527032ebabe76a436a5eb2b4882828c0 7ec201a045e7add33b5d2a5eaaa4e0ddce11a5f56f6d48eca3b9cf8326af9308 60bcf69b53f8719df6f3a0bceab2dee1578b222cc476a60667a695f11f468b0c f40c81684546fc108ea9a2dcbfeaa8be0eabaab4b8c2fa2e42d8574c0da51db8 17172edebbc342d1da9fcbc809f9a1640f2e971d19019caed5b425e7fbe99c36 d26b8edc13ec0edb7a9e56bfe03ebff366d36e51415b732903826478162a73077a903ea5aa857a3cc36549cb7c99618932903163c8e36ef2754757bbb96c5a0e17c8529fd3761bea67d246d9b9311a9fe3fd7027e767b2b2ec5deec870728c098c9f192f22e19d05f47edfa548a2c34568b4b4d461632ee20c5035d29dca250b1304880d6b19d8e6a652c9a13dfe6371c75e88d3b551d6e8e6ba11f28c48a2041114aec8a78e35281e3edf0a482cd53220bbbca39de660702887cc929472a70f997bc4c5a6a99b5158d2b508a1d9927da3ffcf6defecca3344e0130f1a85f508762afb6fba728f733ef3ba08f6540101eeaa9708e6c06dc0434a2484f0c14607d0e1a80ebf1b4a5d8de98ca34bfacf74f505411695a8f47be483771d9dc4e105ed0f26b9f96c33d467d6ebf32f1b3a1e5da71a4dd3d25d8d01fea5e076ac0a0577aff051d07fdf6313c3e6a1682d8e288f64bad1b8851da31ca82662af52530eec0a7079bbd2378db17cece6b034a1645ddf604cf4f594039314201fe9c0210f4b5a2503c5e494bd7894ca4745baedd40e15a492613cd1ea3a7e9e1b70c6d50f14603a5416519d4386cc1661291b019e241d26d560d14cc3fa6692b050ecdc04b432935fc2fa7f6811898e4d5d0ee71214a8237fe6e25445fb96a34a1cf0330fa8c7bc096a3b90fab03b9256ecb8bb03f920455faa0e5cbdbdc598bce6ac000dc6d3a395ba8ab7641ce7f7e3e57fa7d7907daf93e2fcd97ef16e08e6f62f960e91db6e414e9067cc8290bbb3ce9c9735d061277523a607a9f4ccf5860352f505744bf4d7b80f2dcd3307f5e4bd10a33dc0f4c64f4837dc248748c6bbfe734204e6f1f1e092f646fff5e0ee3702e7c6eb9e82838e55f2ceeb5b266049abfbdd021198993c852d914a716a2084a1abdb2ae62a1b59cac33756f98bce580e9bb40eb93c50b2284a5e8e2f47543e27dfb96d7f2623342c39b3fc42107d9f13a28207718d079d3e29f13b850abb3df3149ddd16c2f4e1ac8c979d4a349aac47d9aa02a02559571458e3a1b4162df0d338f4bec6a264b988e180e90001da71117c0307b8ba200a7cb60dfa81a1b0e5df6056a7ba9cef591157462320c5d0800fdcf900d959f105ceeb68ea1c6f5e4278f2619872e81502b6ef1a1c50ae4bfdd166300634890c36f4e004920dbdbc8a2fef07aa80ef39834ab7e7bec82f6d9bee53260135194f18580c1b2d359ca7c93a1a0dd6de36730ef359fd8dde70d79fdd0a2b07cbe29aae05aea7615529496c9a37fa1c2b6cd2eff0f38300b2090707f959140deb0a7acd4ef1ffe05bbc2faf589915658bc2b35b2ed288a9f720f7a89ac0d708927d28276e39cccb61a3153027de29186cee50e9d13c1b12fd2bb53f2f52440f401b022d8d529c00fe37c90dde9187a06890db2470d2eced074ce205a1cd560517de7f150f89a7297db79c4cdbf7c7c7a9750cde5da21e4e0377028b328045087fa0663ff1daf3d4437d5a22d19394dd4ea6f82ffe3ee43f4b80719ce976f30f9e3c7abcd870ea4fcc76c105e7a942f7012d47093b71e321755bc30d0661750bf52d24f65f9b6ae51ab07bbd7cae81bcbb0804d5047fa0cbf55a9d14fe6fb40c46bfdd1b09316deedef95d0587b500d86c8465879cf5c2ed427a3393e7683c0674fd1eff306e2fc9d21b9c63889f9ab881fd79768f525fb59ccb9dd7d501050a4d5d6f87ad3b33bdf3928d8d9a79dbbc61f3e46bfc7eb2801da61770aeecd100d0ce68937cd8b7b41b1d598311dd76a5808b1f7011b08428fe3bbe618892b4067cd75442daaed47e489d1b47cd71b9fea1e99f5b95769b3dfc7f792f815ec401d451b6f3791db7f723d469ccd20e9ce380105ae02168f86c66cdb96026de2c05ea1fb8e0ecc358b6f8cd88ac3288c795406fcd66c0de5b4714d92ce3ac6767050d2938d7ae08038c74b611acdb6f9b91a23d8f2eb1c8d518b51954e9af33260aab715d07afaef9a1c896c50661f03ea86d6262413b4718de42bee4685219f40a195f3e65430694ed186deccf9a249aab7f5833a28824fa600eb315df9e7c610f true +check_ring_signature c5dabad533e6bc3b1841fdcaf7e7d588296b0d0db6f59416818cf695a0db460e b0ffe0af8991d8b2babefb340686622d3cfc5a9ee9aba27d3a1798f43e12abb8 10 334072684584a6451dc92ba1e6b90690a6a3930e52b3207361cf1d7e6d699035 44895d0d4efed2ffef893abaa33797ad44648dd80f848d142c021b10431f890b a24af9510df59ee09c1be2aa88287c61bf5f76160f075dae4f2e4d84b304fa2c 6bbb237a25ac42d141946dcef9cbb3cbfbb0a386851e53a47d231279bb413b68 facbd62d5a10e3cce443fc53913a0c0f082f533786d93d01eb1e9762ce602159 178774e4748366411c2a510a65f507f0948c51d4626628e606e0ca4ef79164b6 d128f6b5a94be357e2c95d20519705007b8c8cc424f806afbcdf2639c45de3b3 8439d86a9bd9fc51c625e086b5d5ccfa519ad330deafdca485de8661b19885e3 1e9b20a028dabe002dffa8b2f73e769241365a1f9c9aadc7bb9ed78319a839a1 c0c0c078046e95bed9fd0aa490e91e8655fa46229b54e788701770b6df5b0307 1076dc5ad6775bef6f95d40342082d56bef2a313ee7ac80e24faefe98305a402d854fc2034ede0ce700afd710bcff0e643560a82ea5fbe4be274531246fe10082782a70cb0aab299069038e7d5d073cd276b4be33da93a9c14609b17fe325d0cb0e8d8487ae6c2adc09ae4425125c032e1473bbf855c7d80b7d8ff98ff4c750051a57a74ec09243369599eddf78e987639863809ed8e2d90e9011a271f9fcd0523dd020790680992e5f08956c723944acadf66810a7e7e92bdae83dd0806ac0875ebb6bbd87803a8cd1684c2588dbd04a8d2ef6fd66b3e3251fc7dd527ead5083bba22e236b9340b13e3503f0406b0742957b9f5c42d6ef8c9d63ec15bcca60c9cd4179d062025c4ad9b41fe4e8522db1d1e6a3179ab514ec3f610ebd9453f04d9024283004134974d7cd108753557fb3908041b63b504e7f5da5f12943d930e9cec85f8d95c11c8584057013c61fbac4fc754d2c8013aae16b1fe3f85de540ee0b8afe661b969b4620189a52709bb3fee56d22ad4d71994d22639099ab5ae067aa0232876b013b1c25f4342420dc815e66dd8dd087c47db187718d9d94fdb0c16f3d808bc6eb8731a84f3b7dc3cebb7e2cdba0fc677c5e876341e20c5b1e30717ebd26748eb585f44cf4b39ce6c832cfa2b78312ff8fdb464c6061da0da1b00c88e4ff7aecea44249b67941dfce720b3cd659086edd846c573ef6ebb5755a0cb9c4e3f49b81ae32ffb238a458e91505e8c85aa1a7c3b05e43a94a1cfbb4390caf492a441d8c1c036054b7139f7885ec426afae0ac8d92542fd8a9e0202a81025a20c5a41be682925ec6558c6c695dd6b0c1dfd558ccc9b801acd2068e7ae2005e04e829fd9d6f13f87f8d6f140cb9640c987d144b935409ad88d9afd0b76102 false +check_ring_signature 7fd048ff02310a39a87e6e9365689723dc022b666770be5968a82f8593ee2999 51cdc64d46f3c3ae8e2b0038abdfd68d861eaed49d451849c62cc4b6b0dfbc49 11 e35515fd084e1beb4de146141666234c196907439b8b36a842d52f36a7aa1a92 c460876dbc954fe3f6cc5e684bb9d7c25def91611039af8a6e4f78ff12c420b1 fdfc63284a6d887f40093d7bffd67eaecd0e1affeba2d2c68ab422279102ed17 12b0be45854d0bfc8e49e20d71e085543fe927cf859ba9c065182a184f648bec 2a1d97c51ed90589b1c44cd259f43902622c0380950a816ae49404ee510a10db 184d5c7f4a65df082ac7b347493c1dbad3903a13b3fc54ed1eef1de4bfdfbd4b 95fa51854c4cf55f0a622c093159dc3f46cdeb6d933963b7d7767737aaccd2fe 0519861554b3841df107bcc5f7074f22cf57211b81eda58b54a38640550c3307 b1ed09774c613e2814661fbee140b0169fd51c2e38e7b004ce148a87679ac0f9 884d98aba3af1b6f27e0009619e54b23b120f5ed192cce2d58e4444002eed5ac c3179382652147000681178f5fcc3358fa41d17ea72d1cc5b5e311fc0c596300 8c5a2824919eef2585e85fd1ce8d5fe050a96b33aa63507e452b5b27912a540f551093ad311a82275bd6be623c0f36523037b7f7856a6512df20ac5db843e7003e2fae894b4194540f8045aade4d55fcaed15d65456cb0c9485b0b2fc9a006089cc47b4e2ab8c62e457d4998fa280ea99b18ee76c4dcd05080871165fae72007dd8b44b0db9423345c53be4b756dcbb752145c5d5c0af3e9202c9be0587fb202721f95b575d126147adf5f0fa10f9bf83fd6b9b881e1da95c9fbbef7ae5f390fa8b1294b3a91046e0c08e0da18cab6fdd4ff0cc5f259180c22dd952321e8bb019f66a5927a2f4ac89b10da1b7a46c2d2a18b116917bf10ff38a68181d0da1801403dd45ef8550954a6ae3e130ca65d6ef0aaa272c24531a2b5941f261892d10d5be6f71f7c892231e83190053efdaad80d20917ce5ec201bea97cdd3c5ecb30e0ba991b95d80afccab579ee37b5c63a45fad1e30a2279c9475adf06227259705374927d685111954cc344ed2754737013481bb03f228cb8cf32e3c0169477204fc5a27e2a5d85b5b1a7cb2732b1106b9d7c53829e1400903ccb19ee566ac3a01876baef1392e3a558317bf4fe7d8f3b34e8534edd299efdd7dbbf3c233e35304960d32ed92aea35e733b186dc51837f9ad3112a7fed8a567cdd173da56834f0975c2b3838a0ece441021b18500b2295776b0706564e90ce6ff8685f516d6de023c896b205fe5b0a9cb32cacd4813a24f55f447796eae4fac147b09a08d8c350f0364293ac21ffbf26b8a0ba2b6a3a62cab5f61fe6f6b12c790d7d0538697cd0c0b182c5ab9ceb79f623dc43f44eaea95909421df1860c173ea3b94b7f8808a00cb0ee5fc4a379777afe0cf76b7720b60c22384235502c8556f77efaa91d0db04207c0d6b83858644b2a178c4a3a51d50184bdbfc1f87a3082ee4c99cb2f349097ff433e55d4052baae76086843ac6a0c85f5868214a5046bed7b979a3d7be502 true +check_ring_signature 7af99bafb31fc68408488b3568cd681f0380f5a7f1f2379469a6096d847ece32 6c57601951ace5430686033fd18b79198149c38906e8d28c9196518422fb4f3a 1 3aa7be0036464aa175b2ee047842bcfa3acbe5cf33027ddb9a61e274bdba6c48 a26685b3555aa1ce7d2f6d13cde69eac4d5185bb45bf38062da836d0766206da7802ba06c453c17a06bf7d46a5f13255d95f9dd920f362e6ba3a7a77c18ed10c false +check_ring_signature 8b908e0baaa463dcdfd87934767b43ba4f605cc47d372880a264e8ef4b852189 64ecb4468b18807015b6c0627e1e116d872745f404b532f431b95a7511b9ff5d 119 ce33eb8b19ce1ac36df0e3091e927a47d8c824837d117bb51d52554683f985e4 db66b83df7b5a7568c10c6868f0eb875fa82be01069618e408102fa33326d6ba 2474c692e6125c8f3f764901df16472e0b7b8c184b19af3ead7d801d1a32f53c 9854bf3ff0068064465334261ca8578e6cb9fe3c804120d5144d9bbcbb8f1888 582c2689cd780a6468290ffc8fa73ea85cecf2b1a34884d2eab58ba529a4a5af 5efcf3ffee6e14eab8954692df1cfbb762d317aedd0b3ad403131b71a48c7c15 c7364cd3ce7f11aea3cf477ae04348221664248052d527f277fae758181914e2 ae6c6c17bc5bc5800004a08fed22fd51d908733d35ec52292c84a8dbc441b63c 4da9168bb0a20df7e50fee4925a068954e2d4a0a975a27eb0b32be35a8103e82 5751aa900a074a75f2a2983fee2bb97082fc6b861670912f08928b132b5066d3 351a7e14be28818273300a19d086efd14bd7788fd9135767574c0112784bc7c7 c8e1e9107ebb1a6581bf49f203b3b8755f1e1e037506d701ef83fd126a43a336 994143a872a41dcff222926568492dc435942143c3859d6a75bfd9ce1f8ba3b9 ca4bd3845a1bd73f6c504a0f16466fcf18834603707d7072a286b25061aee832 b698592d5e82803680c7fa794d95a15e9d9c6cf901e5ef52191532f09de1c8dc ac205dea0f4205a930dc47c21d3fead4a3f56b96b07e685374c3bb6ed5bac9d6 09d4b058c4e67e061c4f90c25df4a519ea604bba441306b05cc8c69bc25329c9 b820da8bb02bf39ec3dbe37fc44e7d5d07bb4d7e121283d56247938d254af5ed 0a410bde2047f983978cd5b99314c37410e586d0a66c2656cccbfcf1530f1c6c 27fd694785ef73566eb05433b791b87ea9dbb9a548ef0c16a66e85526c4df8e5 6e28f2bc86f7cd7f6c0893b108f4104e72d1d8c7bda4ef7adfef6596ef20d28e b273a6bcc750aad380522d01ca8d313a4d24b7565cc2bdf5d57a0fa517733cf2 54b5aa61c581fc596d0368fcad26a6b1da23882b79fa4909ea44bf8e988f6d86 c76dbecda267440e1fcd6535dfcb4b7590767a896743f4d6fb993391ce06eb05 9d370ae53584e57b14ffd2d8b33f2104308dffae643de8461d4717351a1c14c4 580f7e89e2b5e4396cf2544cacbc9d3d72e7e278b1ad33fb3f0311f25778cbfb b2f56742712db5e2fe7c1228f2fc468bbb7a8fb29099d349f7c307695a6796c2 e2342f297de3bc2464a9dd0c66f4223e11158cd2a82259a4a2f827ce3f0d0fb3 258cdc3b646d3df6c6ce7804556a92233a578cca59a91dc689e84e189622e8c1 00906881a81c5a01bd9a07516653d6c621119590397f9e9ad327892ec688c681 b02eb976e40ed0908adde268971bf7ab4b2238a8154d80b6c459b4e32e494605 9658471fafa69731a066a67169dad916f31ed27b7b0e204f42de1f1fd815b4f8 6e19a85efcc2c23df0890d388bda7f6eae8177d78202d534d13013371ff4a9ae a453e8ee73212f8f06b253685e7b609a083e4681fcbbb52663879a300b1e4215 6ec33259ae6f324b37dc536d455eaaeaefeabe473ae667ff5d05599d3968e09c 3aadcfb5006f7b460401a8b91ad56f85b780fa9f50495faaec5b7360c99c1f65 636950b6048432d1e8567e248ce9451fd04c0de83e6283b1aa918057411da64a 15d736c19f871f0af292863abdbf2521c994fab8113ee19c4f321c21b7dfbc72 b7db19743d1fe44ea9b3b035e65544a390315cb5a63c377be390dcea5570b723 13d2c266d2dac189f1e13749c4e5b6c260a6dac361e7b961e0e9f9534c925eba 0fea8468d5561423d2310d24ae70d5432bbe7b38eea805fbb045a8f3d63d80da eb5334c319f463f786e7b2306b7ab235f2bb041b97f153d7a7e0e768ad97aa8f 40dcf6a97e64b5d9a2f34a08ca67835884ee678db140f420a14cb76001a9ce17 a027018ee09af4846ae6db35d3802f3819738cee200551d1e4d884e91807f791 615d239e7f8f2082c619a2f14c20b454f37b6f0f1d97e2b315c17c4a0def22d6 761a5df10b95b8124c71e93d2396c4dbf3b67629c9f2858209034a3432bf02b7 4ef5719c37786bff3f878efe4a55d7c7a69ad7a8aca22b8d71a01f5103c5a10c f09010383609def0bda494805746e55b6629abe3eef91539bb587bec943900a2 8e4568d3d5fa1c6a0d7d43f11fff8875813cb8efb11e0d1e8040d35b4f4fea32 856bbec8977ea7afc12826f86b85ac1af9767337bc17fceecb0a237e989bd07d b409e27483b0d725ab3516c92fca29c596ec9ed1627549469efff8d782747051 43ea2821e00773b44416b75e21d9e160b28c245463521a133633b3b4421749b5 e39e4daefdbe3c38ba28c0dc64be11dddf300a903bb08b70926ecd73630b68ee d35b68849082da176dfa83103037ea20094f00ce2f9fc04180a927ba30a38a48 42f771e563cba3ebaede76164b892419d6ac0b5413a83cbfcfe06c29de97821a 984b7a6682a09ba1cbefbf602f995ec5557b2767d582b9ce76764f21e85e3315 90363870b0a466d15182310a3f0ef49bcf447ecdcaee5e704821b5c0ad82679d cb740ad81673bc43bc99eb850c5594f19ed3f8ad0b2ee8dec7909a163b95eafe bf7fc7de5db3c3c33ac90532d75d75df4f7463874310890a0f196901c6df0b02 9174c993264183a2c143519ff731dbc8787fb9edaee7b88dc65cc958150b6d76 abe33816c6b9a64774ff5cdf564e0826297950daff334c15d423adf6907956ed afb8ced58ec739b9e98b515fed939146f4cb594a0ea6be11f095c40b72bfabda 99b1d1a6c86d9c9370e1b5a028aa21600e8fc29679377dc9ff1d3588f687bd23 2873e4c7f7a7ac8fef56cff77536cf3e14a9d09f8632bfc9408d78ce4a53744d ea457ce86be93c5804053dc76367d5bcdcac211d4cd79f8fc08415242d57c87d 8a60110c5e5a19dd2f54da6d58079eb4a167c497efe951c0049a8bacba2a5258 f98186157f29374cdcbcb22e44fe85d918454aa58784c7b21cf0f6240d926365 e3fe0026282e9fc43f8cd77588470e31a231141d8a678a7c3b1f4a20cf498a58 d55e2dfa334174369b0c622ec81a303bc1f7f056d56623a5bf0cfb7318b02105 e306719c3a86981a2d76e9b75b9665b26b5b9bb0a91787ce39acb83da2ea2c68 19e8a9fcb7b219c8137f45965a9db9ef546bdc51a38fa3029feac0610cfff861 765ce6191bcae61b9d204de669cdd8272b24315effdfec7669b3533abffc3b1f 2de2e83934851287d4610b56cf4ceca1f164d78c3df136323ec464748ab4d54e 516c713d30747fe1b7f5ff685de6c77634a9291415198ac7f8e44713dd253aab a6fc4cd690d134bbb3a3916bbf9a4b2fcf9a91f093b1194ff0f1de4d92a17a2c 70caa5177555e61d65d69d953798c2a90cd28fe802f9772a73308c293344be59 935a5a73861ff3dc558ac02ebdb2bfeb2967a6a40fcd542b269f76138c36de00 23b63347d98c231930039eaa8bc3377188a1c4e0640613d4f946af70acdcc6bc 8a88becb37d43a3f5d0a65f5f5f3da5aa6716144017eb0c9e23b287893595632 7d3aeb4c7909cf45dfa863dc05372c7726f380c8a14d817d292b886d390de3f1 78b16450c4bfb786f15ca2f03511dc1a51c12d49798870cdd75800d844e06520 3ccbad2cc36e55a64cf3dd5d881073445f7e32523a21e43a48e08393b3c32ea5 73957cf61e6efd26c9547fb26316b60c8aecaa3ad2ea4e9e441377ee7f3a476d 4a208fba2989a92b9188b2a01dcb7d75a614cc21880caf09c9d1ef8252bd5cef fd850add920dcf63ac6ba00dc20aa23dcaf187915263b2106880f5dd412f4975 59b67bc6cf9d931c156746bf2dc6136a5822f51c61799b9d1e2bba3c216503e9 5efdc045d46d3977956751f73a6d6ee0b396169b6f63326516e8d2529db80511 f5d2f58877e17b3f915c181abe640d76dea60d4b03f7c9640e4d3729e4adcd8b 0184d77c8400cb2217fe1c8223286f0b560218ba68545491bcb8cf3edcf96854 24ba5f3290c3efc4accfc9cf7bf12b24b6c64bbb810e4d9f4a20936dc5474b6f a96f79abe510b8f8b1743bcd46de65fd6048f9d8f8edab3ce72a5fc9750ea1c0 955fc421a7dcd102b447d96a3f4c7b619cbcf9314234dc924b1789fee700b4e4 1fcecb3ba3aba877282fe80176f323b32adadf462e01ee874aa7e938a0a5c813 8fdc1a196ef432d34ca8015bb520974f839388ee0b0706a34869944fa2657adf 49c4e44394c47faf7a99cb1b68334166cc68427da516ebc6f717406b2ef7973e 4dee0e4fb904d061fabc7442f130bb30a00e15e8c391bbea0fa00536a046114c 3a649d4320e57a162aaa40262735a9f2bbe4d0e7e5496d4cb2729b08cf961d11 0f7e84a110e1d624aeb2b98635154e1408ce6820ca03c0874a294299ea7aed95 4d0a186306bdfcfb132a6f3fdf3996f6e72f13b61854c5d31b42fab31698219b 70fc31db677c6162a4768796ba95adef801cc5374449f847d78c7aeb6e729bdc 4b66f8183ddf0b9a372b0f621fa60095273cd9f842156971e9916c50c86eb454 0690618621f955a139c9731bb32223c937c7c36aa80df0cee61f32475e4b7ee2 335a85f1c107eb137df07c93e19c79ad03e933287eef3d3f7319b27aef8f914c b873b28d52b87808fd3077482aaa8eb2df90360a1a678de248dcde42ed39a309 a333aff157bd32ded800b65054ce16e62fc079165d4251fb6cd2e9d152edf228 e8825ff199fa04a8069107dd016e6f801536d83951b4176c7668038c960e6176 f8553b3af8bd550a7a798d55e2175c6c075a55a33596cdf8057b188b63b40d5c ae9733683e6f3f4bacdab7ab6c134f53afa363feed8599eb432f1ac6362af4e0 546b7427c3086ae0e64986cf5870db14698853af117e094d4cd3b3f8aa04c2ff 9b321d33701e27ad5c62d15cb91b05f16b854ba7854c7e00497442dffd8bfaad 79d8ead308293e4d904b0228c85578d4cb408a12a8522ef3acd66544e4838139 33d90e8864afc3c324287068d3197a7a5409c83738e7b0150d7230e7d416dc8e 862ac682a4cb1332ec002dd5173aee4075bddf3bef6d03e1877d1167ed583143 ebe6dd52233b3924103fa33491ff2479fac38eb71e8350c92fb443a664da44d3 96cff399cd9af5c1a95337008091a616fb0544fa9f79af98d20045257b5d9408 da476c5c5e28306a6b917ffa20b6803a60da5592a4089da7fd0963ebfbeeddad 8bd0b40d6eabc1a969a4987c68e95696c6436ba621e6824cf179a785c7e8f0ef a214cebf964f1c4b25112dd7e19c959c2bc98daf2c96f169010b629e183dccbb 99fe7dea4ea9b756b76e354a66b14fb277433481db202fdb42c06ca5de19ff8f cddb8d0385a46e281ef0fa30e679c47253a039b84a697021ea6c9772acdcc405e0f9646c4387a9197a196fc7b0b1fe4a3df3111bf159f723570fcc00d29011093e16bb92aab766f7458ee9ce4fea45c5387000d35cbcbc095787664a40f45e0ec451f7d0793df474e1a3537ecdc54978109ec8dfe3a687679ac870b027587d04e948deb3e860fdfd24bc166b94c4aed5cb606b8f9d29131c6b6f49a4fe377d04d637a69fdc066fd11b7622536b3f4a234df24f5d56c40c9bf378977cb9e5870bc257ba42532142f1f6aa5116c40d38ddfa31835c2ffa2527656afe9d5352f4006ce1239c8efca62e29b702914da79cabb73619d8d8956ac8fe8524c052c7c50d27d3a5857124312cea2d51a9361d7428aae723612c5c4894ceddc030151382007e236a776ee866eb32d7a323c34a2f9d126a4b4bd0c94da3bfdf91ba05a3b004ddd66e5f0abdaa05c39773efaca6775f9f2437de632671c7eb33971584fa0708750b2cbe0388d68bd4fded48bdf8a6aad8d47289a9dd7100a1d66bfafd6bd80298dee6776d0d3b786b0e84c09eb7ebc9143e6d4597829cfcaa12d1dfa67e640aa2d918ff143b088cd601d31d0a0939c030acdc2d7b9f971e6fba5225f1408a0024e0228bbcc837421e075414ee686f0583cdc7aad036a8a91b0f5f84ace9e107dae00c32f0f1ea2bdd155a2b2f1e9f4d40f4c0cf32d541d3de56f9bbd932ac021ce4cb21b5aca2f58f678533842c54e912c0c524ecd87ced20cb1ed3159a9d08af4197097233b81ece9965959e8641bda6d1549ca5e716aae10db563cc74e3096758aef7fde123050e92fe620961d0962762af1e4e0061cadf5b3124e6262e080076bd36e69ecf4d7f346f18a474f36fff5119c5d16467c992eea039b9d5e70335a65d88924d01875dd4f4563772588b0cb8b052824f3591787e4511a5efbc0cc75f861403f82b0398b2de29ddb2c8d7e7d76e6b99458ad069f15eb93cd24d0a0343a63c357678310c6c013183d38c3ba0a78d02ef094bf8331a3c521012860ff992729ac1dde5a40443d98beb737e1ace1ff964ecb2a0d417049eba62235003e015829d146778081219a1b63e2fbf2b99837b828da35877d25ae4ff0e0aeb0a62e458013b3635aeed69949d46996891f7fd8b3eb94309d5e3fa637a3326ac0546f6b8a15aab2e2c76ffd49bf2f7f4a30ea134eeca12e335b9d6e4e90594d00b9bacbaa83bb4b2438e326a8602077242164cf9720c4f3c8cc561532f4471ba0f536856a50ad0f1c137537f8593c90144dfb64f0fedf014077620e2aa974e3a00f426c4a6de856b5aa6c2b9ba48c51bf92febd31a5dd241844b73c705a1cb1900d830c08b877cc3af9251b0146033752a4b06d3fd73836664b0fbb2f3af7a2508d83dc9d470ff1283480e98c2e754b6f471ed5bfe018ba59534ac1c6ecf7fde017711c87fbae84a392108621b1e742f30dd627ab19c97b05c6c706623d21d710f4fd79dfd5636e811f9461ae2184ca597480a3d4020945ca6bd609dc3335d370bbfda477d0dbcd3962018e4bbbe4a98f07f4f36a61d62b2246b532f60fc2bdb0bd846053545db54d9b5fae0e5a87418a08f5d3e6d7e88176e14fe4de7ce71650da72373366e09dac4dba5a01e973c9058ff2664fd42af124a175298a51b5c4006c0b39b9db2cb42ebe5b4ed6433aabeb01c7c79a73c0e9784ca77570dabd9d90d9ff2edd26c251e2b761d7060f88f4a2fe2395cd48076f400ab885f8a04bb86009bfe15e8eb27cbe1f092714e5829919a4f74c27caa73f7592153ecb12188f90de2a8ced38a95b48eabea43809ccd2b258f503a9a7155de871946ae63f27897013ad9cb2159674e7d855a6374d559e576943ade9816655bb7010c805a9676ce076a3d57ed9158b6b87e15787512421857cb5f688eecf43393a36b4f72bbc7bd792c70db55cd7ab247004fd4c07aa0e56e090831acea86d70a94247fcccbeb320afd356c1148be576b1990badade1a266250f5efcb6c0b9ceda988513a2e6c690ace8763ad9ca8c2b1899e2883e872092a901a4b70e4ff7f92c89017c6150265074e8f1f3bbaebc861b650f35b60490ec3d9a16657523afe4f838bbbf48cadcf0cf6dba0be547a33776c3c6befa8577f6a8ece33a3effc695c8ba586d4f08b125f0d99fe9c457055540757142818e965cbdd006663fbf6fe371c5601605bd0ad08b1dc6f26aefdc060bb9dcdbd514e56e3e4057ae9dc4baa53544c409ed7230508f1b83af220a92febc10693c9617435115183a633d32f3c165de7455a6f131c07369753f055d6ca0166eeb54b9944f7c3d51f8aa785c285a393877693ebf88100d122ec3d0e8282460eb80a1299cd89da65fb18d9b22585367feaec1b8e05280fa0f7d9e0dbc909f2284e23b5971c75746e41a0aee6e302e798a5da70e7f8270465f69c38ceb4215e1fd6bff1818784c9fe003fefbd50c2cdfab18124c1b5d603000d95b59f93b825f265c5b420caea39773cd71f2d4d8a31501c69f85c4d890711bfd65910ca588566ba2c49286789db9c708b5e42903083e9731a6bccd3870fb4a7d0a248f95bcfdf0fd868dceb9b2238035be3f79a6206be1dd1b02900cf0d401b85c59733b7e06e7495dc887791f5d16f121fd9f651194b0b7a143081a609d365c74b55c50d630e08261453f2899cd3c9f09336572a842e406ec70e718a099572b516236acc4ffd305119b7a717aeb05a7a71c9097ceefebb7fea90183e060ac4feb9d2f3130dd045977e6ace8be552ffc70b253c92ef38012daf49015a02435ab9d9bf48236c76fe839cfaffd2e518c5254300144f915aea0971bd139e0acd89eaee995ca10d65ae9c2b87a82a2253d00fabd56927696ab3f5c1fd022109060e0c2efb9b0a8d765b45307d5a95a7258a9b334e527f2f8cfa083f9398fa0d067aeab98397a8a9031329e124cd502b84c3893994b25536fbac08189cee6f09289917fe4a5a87f846628b37c93140cd295ce5b98fca5eb15dd9c5c54ac0bb01737a90d714e8a3f88a84519621bddecae3e26f496ec4f44c47c6fb56ce960600ca61418acdc762949f569671b0c101cad4845ce8b2f641e515e811531c91030b56fee7337bdc81cbdb3713494fbd32d6f4e766191622cd617c64f63228c5710c09d19101a21337a36263547f8d0b0507a63e39510db6c1c6367cfdedd6cc000adafb71c8f29c013ccec41b6a7a361d219cba929a9877aa176b715997bc5a5c09e91d56938a51fcff39f4a40a647319afca8134b3c633f7f08e749b52b0bcf50ba5446a8e9370a3a2f6085c106236a191dcfc0a70bb12c047a319935de58b200a8706b038e97d227196fd8c67edfaad3e603e9cd4890105d7bf8f14e2104ccf0510e9885f1583a778d25d484e9a24cf35db4532ec9439030b878cc1787ade780935ccaa5c73fe2a20dcc61080b73f7dc570f13e5c397ec24f8745120f7aec1507c5d1aa7c66bd7d373d3be0de17e7969f09eda6969d5e22742a93e153f88b1603d547c2cfa81703a7f960ad13fced84cab1d365ef7fbe2f465e6dc82281b3f5046ce8f9c137bbefa934830bd1205038e885b516e81f7bf4cf9f7272da595caa040f9417e80fc0e31e6858fd097ed1df7c44ee43eb7fc2d94400e422fba9ce1d09653cbac56a8b22e3ceb6eebc7c86e5e000517dab37eb397e45a28f890fdf9b023af1d3b5465136ec24b973ef4b0c0a6958bfe07fd8c9abe9e6c233e6d20fca04b0757ebf60703130b1f0ddc345d04bd760db73c5e5e3d160462fb690fc25a90d5599a73c8e9286327fc197fc0224e156c5a082b0314a52790940ec954f34e20454baca398fb996e7a2528dc4a86127eb436c70854acf0af3b0dadf78ce0f3e0896ef7bc585bbd4fe10f48a68688f8851684a977ec6891316664eee3fcd3b380c352b2b28d5d578aec62f7e03de9f387dc602317434b8cb50e2ea65d83c9ce905f8c80fb9791793a99a2e8102b7dadc62ce4c9674296c2c8ccaa5851d6441270f33b093280c627573c602332c021b33d73c96e35a43ebeb5985cfb6dff4a44b0370818a3bef4ca5b2636905f42f2d1fa4a95b69b177ec9eb2ca56ece57b2d340a322d7a97b79a4a00b1a735c465391a97828b1c4b2e168a6e04f3f684cdcc5d0b326b705ca7d08c5aee250d3d4537505e8bec2fa6e3d944cee11835a7a867aa080bf4e02e67295a4250bd9a878a36274263c58d2fe88d385c0eeb964fe522080b97f0586cf6f873c2f0f681039e24e3f5f403c80da3dac0acf1c0d78909795009b6ee8047212243160c4dcc457a56ed72682c57bc0f692dc82ee07f9bdb24a20a232d48b440be8aa6a8d25016579dc541928a88e1d5d9c89e9463a39977f4430196d83efd3cf7468987428795150ffd93b8e825fae36360e6cdd4774b6f7ee203cab5cb89fe57cc304a488eba93ae5ced887a208615f251079312ffd9938ee50ad7170a9f756686d90676923694f3abcba43e3dce8a0ab5b9073b31854afb3f0360f543919a40c734cd34b8e9e3a26cff97713275f5b2a088965e0b8239922f002f34cb1b087e2dd918a649597e61023ec205ee0624048024e492391df5ee6d0a5b03bc27ac5f0036fc8b82bb8c272893d84e686213989ed441868efb5f5f4701868e6234c6ab6bddbb2a1db0f27486aeb6101e867d44e238336124973f40920df3a5ca31a70a8b2ac426510a57b07ff7a184e49fbdde401253009f9c9b23b90e36f2973e0267e9dd97348253bf30794dff610fc9e3ed646e759cb15af987ea0f3ae01b5553cada949f45121c34a051fadfdd3496bd6b96c8ed358953434ef3088f095fb9b3521aa1eff0667e5e1891215b924f3d95a926aef35aec4ce4b91d0dd507a40865411955a7561e5f789f7b676cf6ab4fbc1040e580821272b6b9540ef329595f375d405bda865cc8c34cd16bd2f80192f051ef6d4d36e636f5685f07d3c13c02bc860b433da5af87c7eb0edfaad3f90a3df611db42c6006f03437b05e20a5675f90e2432f779be59c25dec6e2f20fd7d58f2ef17d4ad5711348e720d2511e00017fdc38cacdaa3dcac5ea6ffdcf494e1ff34dba702afdc5968984a0311b0379b04fad4fc2166a882f703266a29fb46c7029d6b7ee0db06dddf002f013b83813693a300e71ae2b77973d58f187c02a3d797cf76e4e67c6f951f6d8b0fabd07b92bd07e1083e669205fb5d763c723b3d05c9e03eec8ca30c10f10022086687c698dbe94d5f90ca1d16a067991ee4e6bf22a475376a03542266a71f570e145da55159cc04eead05dc302c37a332740da4febf3e822780bd65e356e1990ba682420eebedd6aefcbdf3fbd0f1af31aebe39e0cc5f5a41516fcad489c19c027762a31ed57697fdfb708c292e79ed3c82cbe6ac4cb3f5b1a4a76471d08a670c8b82ff16b0b4fe7cbc1c6e9ddb2341af9e5e44712322264ff9e8493ed042a70b94a132177cf1c6a706dcb1f71b0a7cfdcd136b59be188c6ec9a0dfcc4123270e39f61590ee988730d6be87b42c4632a99b2e10b87922e77f79a06074fa742e07453f53fbb834499ccc25dc3ef9db519b6dab43b8e8165f634dbad7697984740038a4cd3be73774ca1d4f416be2fd03972091cf81868c74a4cfcdc3f6a3199704d7f013a803085f9f8d151f779fdab8e0ac2fae8683408c432430e551f881a50fb3bd7c6c76925f1369424abba7405c779e9b7242b83ee10af84ef30f6ea64a091a6b3322e25774ef9aaca611f54833026a83a2a0c816c2cd832b9a160ff703029c8187b2c9841896accdddfa4c4d64970cdf1ae97be1aadf7752cdf1d2bd1301687a281d9493a0ecad5772f66e40125e9eeba1b625e6a00da71a27f8c64dfb0194c6675e4b649414c9e052862a99b7353833f74a44fa6de86916783cd91d0e0d0b3129c4a4f4211e53c30c7e01f79c5f971e9a29cd9f109df2f93f7dc17bc105f01ff1e5ebcd37974d2789ed8032f0afb60e876f62d36a62f8b61520dd520b01657a4e96873ab85a68a7a2808666e8a423e17ada049156f117e6a329d795650a35b0f2018603a1f00943f1fcd9e06c6044df018557ffe0d87a1cd70a9214c004f35d200c0b5aa60375258267af910e66ef8deb25c4620878a44351cfa013a50aa0081b1193921ccf4510a8e024037027498693120d59ae11bae346280f2fe50b549814cc150cadd0da023f380e11b99e17567e50d9eccf5d2c7ee5aef464fe0e87fd56a9d830185a6b89b807e5cf3c13da1a8d6646ce1dc5259e7de9f7d0850a6b3ff43fb2ba00ca925772195ae927e19e8d0330018571b6ef0dc17c3f738e0beaa4f4c5ecac9b3a5e82dc3d7f797bce9615bc7afaa23959624f9a462020700176fae3d988cc3a3f1323906ae4d5074e8149d8109b8e65015f327992f2a33308fc55918822a0b0862fd129b401704cab5f3365375d2349d492f61b7023c02f06abc1380afd01a2e17cb3cde0e0eff2de2280a717a50e2db232ef46b5677f190d990b407e525698bccbc23f4b3aa30369756dec7da27285082aa022d4c1beb60a494e3481f8f9c760f6df875fbdc02663a93b40994d5d476886c76d18909d9a0b5accb6703ea25f7586e32ca13d5b1523bcdd83da53c0142b6e897d795c8e67098bc2d05385b7144a7ea159848365c8a6c703997d270ac9fc147baaec13a6310eefb0b1d998df10ccffe8f07f070351e8c490729ead55f8e44c46e08aa54b970505c0150d12266bc78880890816877bd08e838cf8dafc917084de7771aeb2510854f8f0afa9b9cf79263ae04ad2e7bb4d8661dee8db44d5c42756eebc9d67ba04a11075e8f0eefa4f83b9bbad20c915e57b3d9b977d102ff20d1eb1c1b50f610891c542146e75d2b875fc8d9edf6bbe658988c45f51ed8b699045d6cf670c4a07c199656a5e61ac47b18b6bc7f8bec6850f678ec35d69af6c75a1efbeaa46ad0ccc946efbecf9a0af6995d90633e64cb9a3abfc7e99c38efd47bd1293672906060bab40dccbd62fa8f27f5017d70a278b8ceab98447d6e3573ca98c0af5048206f801c074452cf6d9ea0f4d3d82cda5752dbf68394bebf4053d8db8680127e90cbedcdce0e488f8db9f91efe9e0918960d5fe589bcfa9993fa20c20752b2e06021cad04337eee21afec0a80ee9173ef058384488fc471ba25c84e37c94750690c8ab905e12612c56c06584ffb0d3592d487f2ef6cc4c02cdccb055aecec7fd9088b89ffbb5a4f008a3aad1f514ab086f0688342e3f428bd2c20f1636a138acd020040d64f994210fdfc002aff57891999705ae6c9523aacd818d30068ec08880dadea0f956c323ebb852ff2aea346286ca016e6f21665864e64c319dd21d6a408f73397e5437d08b68b6b2b62430737aa1333233ad29997b73ee181f40e54f6091759cd5ded63a6a02a6e40344cfb8f3c4fb7d70fa3d0c9965073921d64fc8c0dcdc627d3fb69d4a5afaa4b98185a265566d1ba8afdd2c0f64e12c29e10ecf70482465e705a15c17569c027a73ec08a3003225f0641fb9e6eeefe868c10c2b40d2c22338390520d8ff5ebc464b27ecded709abd77dd4fb2ec6884d2d908b5990816d952c62728720ed52aa0a4f96a4d0322253d5921dbc1feb35b7195a78e0b050162eb593c54349aae20cdcd1da8c4eb5867cd69f201522aa1a45bd51d73000040616ddc0cf638d75a2116c9bd4716d30c11e69f061abd0d198a0bb2f77893044c9eab6e790f8b2a001749b76c45ab25a6f350390c7944068c5ae315fe7d98051350b52fbde39b70ea85120f9827f03860239a6698291df0225f4cf6398b7c0c07f69d0021a95721fdc2997f28fb15f17718af5a104b49917b570e518c2cb408dfa5eb627d706dfb4d1ce8b741625d700fd7a89989483c9b434e7175ebadaf032b319f1a4a2ca894b0ddf24d8d30349c19b5c68836f6048b8b440cb4b31cf20ee27c74f8260caa60203cb9b003169009431e1ae358a2d7684b91c2571590f10ffef0da71f81fad183af5969c6cbe92440144df59282a540ad6866421b8375808749ddb843015e68c6de6b624c1c44121a7d016dfafedcec89deea00ba162ee0aeda56c5e99046a81722beeb9b5a8d8b97ab77f7b1159dfd725d20d2aba19120513d1fe691ab379a605c5f779cfee738f07737b910f3a8149a52a1da43834c00ebf96e770ff62a2056f010e5de364f407701be5049ae0c623f6a76670dd879e05a50b6d4bc31e90bfdcaa52b5cb373c0a03e9c3737e055f933349dde9745cad0124082451ceb6442a6bfdab252ba56ea52ec866aa50dfdd282c2001c44d044200c89390b3b7d4967d4660b230a2673e12c14bc7c69eb4e62769ce90d499af6909b6d07f5d39a9877381ee6a40fe216ef7676bf9b8c7a7edb3fa4c78948dede00066049d3907d8e808e49fa23ff6602dd8f9b934e7b647301b99245d3ae5258709ec8932271d2b2bbc362afd0e33351af47bf193d68a3c4228912cbed930ed790e7dde4da1b4eb04a02d0d8c80e9fecfbf53c0565ffd3912c72726ddcf6fff8301d191c08cee1f66efebe17b54847aad3713ff6e0c503cf03514c758c18347e80a473db6318235b092e5ba9d9d3142005b8317a30115fc8b9a6275e9bfdc454603ee40269766ca13495ac21f85f2750245fc2b417d06d0b6eb88741bb0b7cbc8093e5d7ad811d3072a98654f3c741af1304fde97dbeb0fc939ea3136ce3c45c700af6407b511653963fa74bfd50ab67d9cdce100e706d0bb9f3a69f58d92e36303fbaead6c7cfe784999158374b405849d355846dd37389f2e6aadcd2671139a0d891f7b4226e7479bf104c9dbc11c0854d43d32368db37f76f08cc2995600bc0a786c4abcbd1f1d48c7321350b41f8bc7e56b375d778f8700216b3c0f8dac630efcae84b5d23b66b88cac4a8d3435e0f961eb66ddcac3dfd64becf6f800a5490d370e45dd9419b4e2e21dc4158ec04d2d08364a54a2ed6a90c57db20531906b02d1c81e32b41aa62a8b85d3c60105b0238b0047f002b8a22f2f0c5251550d2a06f751465c7e0e6f3bdb728167badf4b7a79905096c73362b24da62b9be46b4201d3dd1420f5228f39a511e4bee3c910f37e67137a7470a170847a189d2c7b0c0407360053490325e17a0dc4144ee5d49e70904fd335a53048eb82aa57fb257202db2ec8fd309c417b5b49d45aa21b251c4ab18202d4803629f259b9e42e976901dd73f8257155280b288fc11cbea9f7755037857bbd0f6ddb3dab928176f6a0042a96e9ef7b94f2a6095a49f54fd36221df0f00f30a3b37112904b69f6641a106416f598fc7b630ef62714ffd281a5964c51febc126a54395329ba549e595670625bf56d0e0551d0ac35d0adad8d5ec241217e2bad1ca3675a46f2e0258d0810280a43e81f7e249d128998c1d98e29776e03afe4386995b80e7ef2dc2d1d7070488ef6220909aa4a091329630e6403f56c2a7e585cf5823c92f33d9d8e514c10a691a09e5a5f1f48c86f91c7da8b7977e3a3c489d9e582e3f3421214dc66366079202e2245b096b42f312fa25cabd27eb7e9ef2a09157f552dc8a75fe3f4363026e61ae0868dcc3277e4b7585c43758aca280f4ae78429eea3d726184539cf0087b2d5b0246e99ad8cd2010789fab1eb1d8ee2a32281360c994c355222230af0c3fa38ebccf39218e238c0d4ff514a1e15be69c21c3ee095152ebd0e0e1359a0c4605e82e541bd8d214970cf94d30b82d9ca9f2633c0bb81c200a548f7dc1490c85d0d81769e09836029df97ea08edbb069dc6aca6d2904bb016aafcc03c5c8010134bfb2c600ec953e6a9c0d34dd81c853e2cc0f6c9e3436c7730b5204161e0f4cddc35e65fb05f289be81b965bbdf3138c0fd3d50b500f83266502383404206e1035d764487e252ea398fcc1421ddc88a1cfc0e732eb6556c37000919b7ba03995e651ff84a051347c1dca96c9e63617f36ff6efea3b81a28a24911df884b08e29ad821eee289cdba6cc473e7e53ba47825755e83150c0272bb2d023e6f2a0a04b8415a76e7e0941353395aaacc5b4becb01ceb492aa0a2145d7d89b87e8d0e67e9f9f5b781eed036bfa605aea7b2a9ec804d81019d74dd92bb33832f5d12080a27d721de9874f72c317fd43960f19f1376fbdc2431f2fc0be65384338c2005ca6225c3a6e4f12e030feb11db7e86cf6f78e0a90cc6c5eed59635f1c925710d383cca5593c163b4169c9ad1fc04efb7d6dcea65484b6c9c9b9337afa71aa30a8b7fd59b2d6b08734e0ab7ad01e96709d789e5439908ff5bcbc31563e693a60faeaacd9d1de2ee514371a92cb57074f231c25a8b8351e63ee1c0c370a3a9580421472d0dc227f6177e9a9c747fb8c4e3a1aa0fbf1fc705273dae716d4f74750e8efe520d4dab4a37640aba83bd568895165cacaa768193a4041db4f63460ea0324e0faf1de18443d4b13b67f0730518a9c6c99eb912656a1f2e4fa5b8c9e380ed45e24817e2f6088538c5f7bfde49181b74290657d7f020e26c51acfabb41e062d0ad6bdd59ad72761b30b63152e1483acb817f874bddd04e4ebc90e254aeb04de72b5aaf876df945217d6e65a25b89e7b2fcf6a6f635c058808755986507c03473255a2d79476940befc5304f36a49ca5190e6c319b0afdb9d6bab7e6812300007ab8f8aed46f512da3df0d440b231afe5bdc9a061463d7f1bb795a1e40ec0e7b7aeb4ca362a19bd3809d7e51d30444bd69f2a49cecf7fb00813aad1ba69104 false +check_ring_signature 1538016ab03ae5ce50b2eeb027a968e5abe076768a8b10a9087efa97090195d5 a41373b2ac52a19ce2a2773d7b73267f68b11b42c15c749bfda37ce8aed7622d 38 faa41da93ce2ca6673e0dc3268d835dac9e8cb3140d0d71b51a8ed153f0587fa da07b6b077d4faea42668e4fe94bce119b390a266f503f5ea5889ec78f99d710 74483fb1fcd82ec5cb843ad75ef9aa58a53e582e23dddbbe9f048daf1df5ca02 2580bf71a00af1ba3a22adabed867a02167d0da3f6b265eb120f8eb3bcde3a54 6847f954f64d5d4e031e3a2e5598348001de2eb92db5c55b13edf271e6bfe2d7 fe1795cd83aed914f3194672f85310d2ba32d915600319a66f0c13dc4fac7a15 349ce6098fd55eaa13f6779b10677ab6bedf47d24c37968b1ec43268ba508a2d dde0e285c3761113456edd39de88c2cdaa5e62668bc630afd5be59ce2591c670 ed601add84f139439e31e12e0ed7cce5640473c7a7763beb6d652852e6a44bb4 845afe781e2c5d908a491e114f9f7c20abb7954574eb3d316a7586f4d666021a 75362d1c429ab0275a800f2ca05f9710bf4d5c4d32d97813eaec71e21782d4ad 0bb56ca9c9ec73ad0c8f691a55199e3008efe4dc6a942f257f78e78624033501 3d2aabe704e00ecd28d56d0de0ee88955d3f4154d02b32a14092f42953c0f267 af5f5436a1ec887de402c56968ce96fd87aa8668bb5d1eaa1a1f237c8c1d5a50 cb9b6843f5c8f594d74b81de6057e2563226ad4230b5baba8bcf98d8ab0a7a95 7144c1fb70d68c787b4bb52f3f91bf8e30ec0c459cd4b63d4e01704cc2f7851f d43bdf5bf82843c995d266f36c5f1fe5586dd6c51a84847e8491c38ec76348e4 18aa02bb82918429237d8a7a5119ad1d64c3a71510a30869006bc65b0cf5454d 28330c4d6d94f4be6066a7c7cbd3349a420d995079bb2a4f92a041529b30e019 58f17d03979f65d5276916bd4683853a5d546f2aa57531dcdd2f2e138640470d 7f816168a824d0d5be6ae60364bad081e66d5c8dc9d9fb7d9a54cc82fabd0b26 1f2ee3f64aeb3c7e0d790d7ea0a054c2d40c80936d253a2e762a58ed9655cb98 8a1fc933682c1c7b5b8f8d16358fdc9c21202986eccb6d9b1472fc4a4d5cc53a fa29eaecb32b5fc434f6ecf1abc093fd5fc88bc3864ec2190af736cf566377a8 af9d6ec2ac00df1c23d9c92b01022cf17b9d9f5b3d3e0117e30b717346ffa852 ab0f922a5b4f6183a0dec0f0c3e9ef6fddcd7afcbb9c61f7fa438ed4e61ee4fe bd4f40bb2798648a2a4650752663b7c094d992f25b10d7877197a77e26798513 d56ef7c321c95cecd76f37ce06d2551cc504a0e43c61155a5d16c99cc7935b5e 38bbcaa0c01dccb46c37e77ac3f26e3fbf8c525e50901fd53171ac4586e954da 7a04b52c37b7cd68fadcc902dc8027e4aaac1682582bd2c59976d391b5428bc9 7463dfa45e04b7161426ca426902b8865e7bb59bfa91d70c6f7fa5baf7446317 fdea58392ec049514951499f4c0f49f98f12ee22dbd904c5e8e603a744dfc6bc 815373cd6b00d7d926f893a55affca90510927510a3cdb0635dc3f297e1cd17d 1ac18a741163977879054bcbb3fd41462ff14b217c325dd7b11c7defa8a3e7e6 2d4e18de4dc18d1b6519d2e23ef17f008326173b162c12d4a7edab4d37527b09 197dfde8c0715161fee62c924c7cb7ca698124bd6ce10eb3a68778c24b663ea4 39f4148add1dc5af67f8e24de1e023c74b2d8e95f1339b75c5ca3dc1324af570 5dfc43cf5cd11dafa2e360b93e4427515145a27c20f0407c2ecb25c60c9c6239 02bcb8f0aa48b7c9f7d5385f0e43ba8db429d4c635db5380b5008e2a778cfd01cc50e1fd03f9e795e932ff1026005e9cec4326c4c4fcaee747cab4d9e70d9401e18301093c82127b0d130774e6d645beb2b45b14a8e1d730c9000f6451441a0d2b1c3687163952d0e492d4596a5a5e9e2a21f2e75b43231675f2d29f74b7000fb1776709d591f26bf09577a74473ed91e8d7de34acc5af8cbe9450151881fe016021ae7329ae847b1a19240b99f4f36daba08d6c4fbe8b851f72487f195df20ce5f463771209430e1d1de02b8d33a861bcafc09855268d873a029de08f48440f75a12fab9dc0d13b52d28c8816c8571bd46e6947a2cd63f5feba2a370051dc0e07693c25c0f442e6cab28f2d0022b003f290dc858f1edf39bd93e6fa885a190ddfa673069bd4a00b090216f8f4a40a4888c14ea8c749ce4ec0a402c1195e32080d4d77e9e9482d6eea9519e044b1c29b706b00e0336f3d9621042261cb188603b52a310f6bdd5b048788a149d8f22d4cf3194d7cc96f00b0015e7b2798dc7000d969dfdb1cd500800329b6a8d2b5ddaead67b4f47b264cd6a96d9537dad48800f3b506cb4e933654dff166a0add903138ab8b11c266eef03669540649c21220884d9f692785736da45baa5599dcfc2b7e62ba18fd2f3f6981b8704de0188c800af6454c3023e08f2efc46e740f79eedb83ad56e0dd4faf49d13a1f749005ff076e3aff684882845aa8478f5f419712924a3f4e8cb95e8fb12210068aefece20acab6b30a53371245fe5ed952d9dc9d3e35387b982cea36aec64d8e086a79340a9be28c828d30839f8f01f0e00feb19d087e8a777f5101ee154243432b0411d0079c20bf9eb1302234ea1373995a7148a286d4a95a36556597e655b8a33fb680efde8c4c3f5ff1499f9c6f9be04c62307dcc92c7f63c1ba8b47748255d9d8170900c088b791d2d6ba89ae585f8a90586b04963e1fa878de46f5f47fc0654eda07ebbb01b4f5a613600372960bd6d59eb70cc17a0efea2741ef513f2031272a5065f914d4285cc0e1e25ae602b066763accfe6e2fd526c877f3ddc521a208c1507fcf2ac68da7c276438ce0d503b2cd2505f0ed81207ad74e39066e75da4b8430aaf2c08bd1c1c339e3b9370b476c8ff75c519b5f58eb08c2024c971b3aee29a02a31d9492f2c689045d540dd016d6a59443261a2951baece3f36a6567d7cd96074fe038ade77be0142497413fe32a60d3bd8e596c62bd3de07e373ad7a737a10520a7c4d72cf22422e50160c9ad40bc1ae227a6bee5c6ef57a6970a759b51fc565246b3e3ee5cc4d32589b26888d742b87d21b40c87f31301249c3cedfa4061016de0b63ba7602703be45790a303556258ebfa2ee29f5fe23af4ff2f5adf2a103f421154660ead194ffd14bd5591adaf8cdb517ac60c2648569c75a0a0a9a320cd3c5923e390d1d62213d9ec46180a33f620083f3181f170d72c7586197f2b805a4c6cde235e31d5206684ad2ea29697c0ac174df84ea8f9d4627c4e6706e770896dcd8c0c08d34cc4d0bb27cf1a1e6e35367cacd27bdeaea02bf99c5c4c97e07b80841da536d1c899263f88d4cac40045d7a7ef2b9aed1a66b0caac898cba50d65d9c798ec9b6b1752094995ba52b53ba8fb001c4aa68981f5f719afc5f88e04f0b33578375bd1e9925313d23f901f9623268a4c30e0d5f858eb7249c16522039ad71c95ad727db9cadb28b1d434933f2785bd8dd805e446eae517f806efe60207ec26a264629147106f5dd1c6e31227a6a11ae47162f695eb8d86e3ef856f0fcd5a14b3264a5eba0ad1ddd0bd08a6b67e244cd6bce7948972d19df28d142f01b95a015a7fdcdb5fab424c951b6b22156bf5b1edd6b2873eb403becf3b3e98037d929f27e91af2e6698eedf91766cbe113850720c76c42a6504174970ab1810b401c3d70d9d0cce2c547d9754cd7e9279c1df2b0831d5481ad5f5ba795a6ba01540792f6fd73ebfade9a6e4e021287ba776c4e26b3b48448b7fb71687fd88d02b27678a3a5a39c1aff13874103e87fd468a5d1a79091b06081fbcca438520c0f692193b94d85037162a1a4ecb377af1c5e486fcf90435ede93e34f6e112a2e006a5469060e68a14222ff6e23fd0d654f809e92509ff3677b77f4ddbd7236650296f17ef1b3dbd63b5cc95d2b8db492068cd0bf63f409191b1f78801ff951e40d86198e2f1379bdf689ee2e75d15b70fb31e1d39d1da4950ef959cdca5d8ebc063d95552ec8e32e61a287daa23fe41a9a18ddae382f9b3a4ca5cced03d21eb80cf7297dac302cc2e1e3511d1f426755ddb863568cad62997ce8f35da751d8b40e8542b4afdd67a78891bf3ddf5f6ce22d53744b88fcadeb0af6b2a0d19c21a3053c236baae555be51999ce82c7fb1897c80082777fddfe38dc2b06b1a6ddd6309574b01fb5c9187a7a9cf008e9aabb6c54860a6984cec58e28122a965f898a60cd7a7b974c41cd47f753728b470101ca44c0973948553a947263bfacb1189950ad99405c145301a5e1f93389178ae51ae8bbfb53715c57f59fbe35225ee7a64003c18055f9916f185267b4f1be291e4c2dfc228fb67de9edd550a07b3c56adc06b9f1cec980b5b4f4d164e198002bb1c1168bdf6df46eaa2aacd0861cb8a24f0f1b0a0032aca9f1b9e87642c6a914414011459aa2c0e11a5a64aae1ca2be1d304d9e471d93cf3025764512767f74187ae970049369b70e37857a85b910622930409a6ca9e4e5d5a12f2afb573823302c57deec22a7921aaae8eff4a029355cd00d31faf5939faccb30b05ac9b913e4a2f497a3182cc0847abe36702ec1e02020c7919347015c5cc9de1264caa052a578afb0e143190e619d0bdf3ab0bc707250c053a12e3dcbf512a78a5b052ddac176673fb4d3aedc8b82f1fc7114bd15c7c01012fa6e0827c83a2fdf3c80d59a92a8c792256e4aa25711442b3d0104e64eb09ff05726271abca78ee1333fd0f8baeb20fd90d0449091911a6daab9dd5925907271b99a10f08a1ba3a57de00126c26c65285145c8191e1f06212b0a7fe20b80228a41268fa65c2a5d7257abdf157a0d7839d0d55d3a5a633a59504d69cabfe0ff327f044aa4e4df6df72912c9036b82875f1bbdccf7dfa832d5ed69f40aea00b99f40fef9ee9246cc3396a578b8a745ffe35af595b7bda8171d93a2469f3c50336e2a03a93662fd61401eca37036fcb09c200cef8843c7d67091bc472dd53a01294a23b0db36f0de013821623c261c8af58c626321d67f25699b62171bcba00c2b256365b16f26bd9922aadf6eb928067c94343b166f4cacb014f61182cbfe09cdb3a90e039aaf3e1b33cf01431d582c11e29144cab59219cefcd589114a030562eb702c1ad2373ed6aa0439b46d98d2efe8de3d5bffd8c7fb1486a48740e507 false +check_ring_signature f8b79a00df10a099b089866da7dfeefcee7d92149fe13efb89a409aa96c2aa1d 7990cd6c4a227fae09e8ec4130c141b0cd45c90d372f54a707cb8ec803347c04 2 4aa9ae63397d32e226b3495eec4ee9953d6e08c3276eb0ce1617cb4c6db159f9 6921bdf3d1a0b30c252c0ce7461e5ca90d0799d0322e339dde9808d647fd83d9 84a2be1902475c1eef0200a932b2db47a293e58bfcb66804ec574ea74eb31d08d464a99d4902762e85011bbf20250ad5666ab0e3462529e8afc97f5706880b0cc971cf9d11a277aa49e91b726cd38ff353944040ce644dba03b3edd580217d0b9d34c4f7c00ecd1ffe541a0ead246862758d3012924fca288566cae61ca19d05 false +check_ring_signature cb7c49ef8bdca951aa6a6ce9e47bd7ccebcd91d022ef6ae8cfafb43ea670eb3a 0b84fc92f1e1b007137f9f3ed1d27b69f36b3b2f9de39b651ba0eb0f6e612934 33 46d115d1b62d5db7f0c40b459b63c66dc03a74fe3651256562c86b246385df13 2df545608fe00d1465f1ec1e59f906178ae21bb402171aa841b46336db4f53bc b3f00178be40490745e45ebe9208052be8c8d9e1f05a264feeebae7725a875fc e261c6964ad602f935258becd58f683bb3f347f691840f7c8b64eac27dc6ba16 d576bff43019621b81de92fab6c88223ca4815461bb075db7ddaa5d75a1c13b5 837a98186a67470462ffd912447e4b28f5c01b3b23ec958e883e39c4de92380b 0edd896d41747b70fbb0a09ede2a5005cee23e5c1f5d9bdd341058628a95f652 cbdfb8db598aaf1bcc0e63cd646c0a6ce7c77d0e282761eef706d6745d5402d0 0a07dbf73f92d743e5dd21a672d29c7e05c4cc464256318df2a456b82010f8e2 51b5d25da1d918a731ff1ddc72fa285272013d896876b396d8ae9d3921fff0c8 2baa0438b818991500a29d951e251493fe656b2730366c99227f7a9da294a3fa 0c41af0ff12571697ff25f8db2abea074d69590c8a3124564aece1eeba14dd92 a407299d40fd584726bde17ed5fcc60cb1c8be95a3b9f5eb7a3b87f416fbd7d2 022c057c69d6b0957f2026c58ef86f85cebf9b531a12265823f83951742643c3 d4c872844c289cbe16512f79e83fa0d3d3c4d52005e637a8638855fda306de5a 842c9c46f61387e781d16210750d2c897f96ffc3a00520c2b8a486225c50b6a5 9b65accb2dd69c244ca6dfe3bbcad53d763ed9805d05646fc17a4dd565a3a3be fad83d9b909d6678174b0d75aa67bbbd14a15206557026646087d0c339681553 64184184dc64d6174ffacddab7a5e25fd66fa5efa3e6158bf55158743a698160 6db3b3c45064b0e1f983a7010a52a93919f8ae8d3377e4cf711e2f88fa7a174f 460fe385d08b9c22c4b9074fda2c7442c76da8dff46c2ff08d8f89b24b607d06 1685c0c29d650e3a6aaf624b194ceff9abedfb9ae2351c6211fe9c9ccdb555f4 47d541e4c2d22fbe9973a892b82ad8bf15241ff71f75a175bd894c5a0760e7cd fa125eaf19cd8949e9d9c62f38a32289e408dfe5726fbc875a98531d0a182027 aeb3115d68242efe7f1f88000d5a2c481cac1999bbfda2d2254e0728fcf1150f 46ed0578b117fef772fe065fda961bdd6ae0f8f8b0a665debd1dc07e73d31a6e 5d8aa46705c5bcd30c5b9dc8ed14dc5d16d7faf87e76bc49b54d5469c43eace5 f12d977cb6a8a13fb589cea15285c1a3c971e98f001c8e1d4501c94876c8b017 2e013168418c0f5287a6cdaa61987bedd3f295e3b7a52e433c476499e47e328a 91114dd4c4cd0499a99cb603ab599426a9e86da664072c8d12fc8f33618ce905 0d307c64e28eb625ce21417391c0c0f3e0a8a3097b72d63c9e6671e49d8db100 6a05afc91117d491b2763055bb46b5131a3f1caef59c1c3c17c76dac7159ed16 639a5239dbd5524e24a8a0db41f66cc9b2ec3e9ef500be5719b4155aae47a374 b1acc1153dbe45c70f265a4757735955539d3e3a4adf1e96e4996ee909da0406a3c233551820e211270a6ed67694bf3dd85321767d6b8c7a2b7743a96604350833197d31b1ad90389e1b66168eb2984f4ac1c9e7b11c6a247e0018cd6ca05302e7e406dede6e34ed8d9c60d54952a98645a27a46b5841fc66935488ee3077a01d394348cee209fc733fe06edbbdeb1c46ae900d9e3e1760da2c8344b13d7540f4a252751f9dc31e7635788473c75d47d3fdeeb64689a4ba40d965153a998dd0ab3fb61214c8b5dc61c288ce73842b04e69b3c38cca294be4f4edf6f25327440e022633bda04273fc396b83eb7d224366ac9cc2151db15ad72509547e4dc0ee033a6e3049398660f642fb7573560942a7e2cd7369817ead81a76bc849f3f0030162e6e960493fb339f40c7a6f03a9aa2069947415bb4e71e46f7aa6ca71b07603ad257514d5d0b709621bc2540613c847ebc11967b69012f03dbf870d58d1280d1e9a77514c5e9cb2ce343c9f47fe1ee70d328a58a7e1b2ac779eea541a42ed0b235c3077b501585090536aa231343e5ab748358112e1a9a5268f520d6d55060a9a6a67c70e0c4c12e7897a87e8be50facad09c898df4bf9aa6d5c0292faf4208d339db4137b2eedbce49e141ac7de5b72dee4142eed8d86047ed1b5c609ff801138c6004c6af2f7094f2de326d07cdcd35b0db48c1aa3b31a3de92da4a62ac0ee583f509353c3e8c5cefcb5856848f3303881f156f0d1fdbd44f565f2ec9da088b646d9d8a981ed420519d81c38068dfa8f83ad9d68ef4f695890d942c3850056e4444dca8a57154b5a25be026ffcf5d66f7621bdf0ac9c2b4d8862a625dee00f6eccd9a364ec967cb4275c6a0db1ca51d36c42e5cb2f2f9745c6c1503b67906b60f269610e403cf760aea0db45467521a68e891510f5457aa7bb4df1b3cf402e983604e9733a47d2be9f01f0bea9beaa774b3192cf4446c5875961695899f014ca1991237456078846de77d98b8cd31566af58ba9b3530fe7dde71b3ccda50b5edcd2b2b13c04689cbdc2faaacb2e6a1d5c5b37ada6f72be07a748811837e0ec62323cb1a8fd46b05174c3c775e42fcf9621ce425020e8d88263d27a9db8b0f7e54ede73eb07f03944090a56dc2ff344a90924749827d46b9e2bab72849700d09c568330b5913d68bc36e94f40f5e6f0756aa6719b4269b18f0ea828e869009a6bed165ba77ae2809ed25e6a4ee09587e0e63412798fb1e58a50524fe2c030ac514e6121a2e32de140516ccc037565823093678bd3bce96a2eeec309fa67409b6eabd15902059eaa18cbf8377c4dad188291c5574635f23ea98efb1c1261207fc463ee6d3300b87ebe4b90da52c520c7a1ac0c4bbb2060f293609581d1acb07879e3f88ddaf52c1a4ab4d168e37009a66b258d9ca7d3dd915c49d36d355520d1ad2a6e1043a551608cd3aa2c1c5d68b3182790f38219f0e32a167b809956207d48d906d7009b897c3c798251b222514ae748d91502190b9db606e3300ced706cc71e6a3499ad0ea271db7b6ebec53b5f7b2df32974760c1d69d23c036b3140c152ed63880c8d005e26c29f3151c0f8356e50d35897fbdb1bf65a9804076f407172149d807c5eb811b4107a97a8a600be9fb2c68f104c4dd541bf2f1fb73310752c79dca10fbab6f9dbed5494d54e3a2dc19f6153cd3ae1686cc7b47dc58da0de8d695bdd79075ceef5068ad8f5be353e01dbab17f26d7ad3f2df4431ad01f0ff15b01d0043e6d02dbb1a86c98f91e3aeb6240de3115ac026301c777d3dcc10ba4fb869210d889f16ab1fc7dfe4705430b9ba3216c27f8104b088539eca17305f77805bc9c40f578baac4f29735959cc8b036f160d05e207c98e3879399cf605e5256772fff5ba3cc4be9965a98fb87a43713ba2d288e5cf8fed33cae6022707543696b83fb3bc3be54497c1bbda9383fdb336b39cf7c846dbc4cfdb8e311b06f5516bbe6cd546b9d5e69bfe8081850f6f37f95113c264bc6ea323e2b2180f058f2b3b891d0df53d602c37ac627f5c34a8ef38569f72b7d4142e2f9c9562eb0786e2d548ece5f3ef6acf090bfb9c7ebdfe1e0ebaa66c89beb123574972cb52048e0f4a5dcc870f93d985022080e7d81529519f8359bc9e0f4a2a36f7baf6eb0ab6c07700f9272d7f56d4d8f3d4cd52ab5208872dcf3937c0b7324c4443a2bd00804b7e2970eae78458d828adda9c81a5218497e26d24a18ab15743ce8f5875086a34d7ceab2af5a12d2d813c2d08eb91d89ef97dcfb88a58b410b13aa0caad0a9fdb20806b163f7469df6933c436dc9ef3716e3af32b7177771951a46deebd05efe10a173ff985fb07164f49d7b37d1570308bb3872a29252267b0dc63f33204dcc15041402977f888fcbfa172e8d59f6d9175203ce1638b682a1d9e9f78b00dcd023f0927ad2c56ca933617509e72b04cc86dbb99e823121d411c00ef652f0ec761fd2bb0261a3511deb39217599d694aa2cca5fa4194cd8b7de453893d6704bafe7f8b0f7e6def239864afdc8bc8b03912d24910d81f679ad544582a96170f766d50a5ad4e4b5eceabea249026a9d0b03d56c0d98077ae6e2c8141a03a2f058b3e951d7ef794fc022e52ede48af1c1546aa27bba24023d1d7efc6f5639690c113d79088771937ee110a3c5a937597c49c9227c33c50a489f695f96c51ba109c0d0a14f99b582109f73b62615c5e06c07e461d0a1f126800c5d6678a9423d05e65ec8598cbb0ee51cc8ba2dd02e5a5f757ce7167d487b3fd54b6b0205463800ea0594539551bbf49df5504ac8089a7a16d6e1c7692a8b7c45bfb464cc21300a92e8f592154961ae012031cb07a467484b92afcd8d62c6a05b830e3a86dfb306716c585e7a6155e0613b51514982fc7a555a153cb7afb7b80237d5f977a8e701a0013948e6939c0cd0d11684689ad5eadda10fce8f9d1005a14f6c04b2927406 true +check_ring_signature 7fa2528e38644e70074de2a7f6059756f1370379efcd847f2ad8abe8600a2e07 01021bb419098d5f8e10603a60cdf0325de5cd05324fdee91afa90d8b09721e1 13 ea7fdb98edf6b2693a724af5d63eaa840b1ccb8671cf83586df3cc051326009d 343650ad2fc39382f80b1bda043925fb80c0919924cb7d5a675c4b14b1f8e1a6 406ea9845555037425da9bb7bd6378880c3d818e6b45300394158ee69fb7db48 e247df67038328313cf448fcb2b1eabb85da210e035235cf0442d6711641cc81 20a2f03b4957f72eb636bbd95d9f3211a4248250d3011bb0a80e21fe522df8d9 7abece2ca024e7c3a002f3924c7d8edb9dd846bdf5d54df60dc4859159f116e1 17c51eff5ff8ec5efc43a889d1ad49887b6f3651fac1e056c5b53323716a13a9 e7a76b326935fab4d85feaee20689e43c6af819943ce9e3d1b72ddfdd0b8ef6a 6293043f062fb6745f5bf4b963261302ed790c4c84a8fdd2d909c58835da8bd4 9d0bf8bc716ddd9553c24333992b70b5f2e19c7210ad4fac9bbad57f1e0a474d 07e171ae972abbb0fa43914d5e515ec70e40030bdfce4771bde801c4765ab899 cb5b6e16e1c9c98efa292b09e79cad6d264e778709e5e2d538bddaa965fe273d 7a85d5bc3fdefdc5ba0ddec57f39dcfcbc8d47879029cd80fc2e601310b83fa0 87a4831af8facd1ec366001aa9c7a7ba7829ea672c09519d9f2491abf0e501065dd92d77397915ac306129e33334c0fe0674f6ccc150e4d8e06a32c38a03cd013ca8c0884ed94742229b16d67d37089deb41243b0514ecf131a08579a9a88e0df1f07c48a5718a45cd5f86282a1d8c6e898b22b10c5209d59df1789e992df50f10a4fa611c51b38c1175187d1f79a234541e3763fa8d245b988bf04e83e7a30ae43ebd665d53da5da21c786b43a6bf3673338c79d6e17c2579841ca3c1c94e05e6e5c3aa5b2708ec4947374f790e7e4ab2adb672eba982178603742714dff30965a4da877e7170c34490aed187d6099b902ed53b7ea9f381a6231fa14c0cdc08687af861ceb4a4b48be2af00833fa53167f1a00a703f8d7c4470f6dc05d29a0c1c038909145eca3898849f94f71ea5b201b1c6b05c69c267f5ee610f4e719b0f9e13041accc79cc013d22350d972b2ba8853ea22b8747b7449a8f53cacd82e00c33c4a767dca23f6b20c48d22dd8ff6147a246611202ccbb4eb967ff65ee0c00ef4826021ab6c8ddab5626bc5f617814f4508343ecad061a507a6ec32247c406e45799b0e930b2c1ec3629551f6edd91652d139b34d82fabd34927e5550fa90ed3f9279dd2aead53eaf23cc4cf23863e5169509f9e70146fb2ba2ef1b1f0f40de08b42c79ffec7bc16c82b4023900d33cffa5048329154ddc2e4ac58a605b10c203fd08a6b0f7e4bec8c3991d12503d841224628b933b60ad97f0b4d61fc650984eb0f35155c6a3f869b238c7c5e1809093f90d07a6b4a06be2191709c81fd00aa73bd131212d0af80dc375719f6afb57f739bb8203816c4b55f4f71f33ee905580b56b6425050488ce6440c24e7ea0b4121763c3f80acf4c846c646ab809a07d7a22953818e9f16265f354e0b871cae79502bbf2e03858e6ca333f241d746040c3af53be61427d746ea05d991ccd0a66f7e04208db806f4fe819d0405b789095540e7309c5c6c28b801ed51595dfc6bce38118f65eca1f1f12ab9a157deda059d0d476249e42e8f1518a84697a75fee7514b270163b223d1827d1e92ed487075daba33c752be77ffe509f23895bc7d4a282ad1c1cc1e321bdb3fc0b8d6d360d26709eaa5216b3c10dd28cd220904f0d2576a7d3f1a4beea786c78ab4d2dc605 true +check_ring_signature d2bcfaed8338e2f5f2d722d5bf4ddf9106666bf1ae0dd6fb1dc45bfd05345081 c82f2f5a5ca76624a7ffd7460f9b54740ea441a0505eecfebbf179453c11f574 3 b52e752364b96368b89ee52f8ba9fb87c8720bd1ea40574ed6802dbea1a37d34 de81e702c0eac21f2accfa4f9e83ea1db499799efc8f03ff54cd23391aeaa480 83b442a8c7329019e2be1a80125989c159533b73a3969ddc32a845ee17f0a415 a5af1ff65e448c7f3c39916c654a279b2ceec4f82ff8fab3d950201d30934808f9a7208a0c186ca4997657c10f673373f78045712c96571ea805fc205192b507bdbd5478ecee0b59213b8456d61bc280fca80235f8739635cdeba3922b96da09d814a5736597397fb08843929317706937e49b1efd240cabcead193eab592609d3984f5b58743e4b42bc912ce8344a441ca7c2cf884800a83bfb010c4e18d406994b76574ca52603985bff02532bc1d6cd06cc013c678357b797469b3989ab0f true +check_ring_signature 3b8d7122a67e8898fe6115c29535b1e9b6bf1baa8f4f272043436c8b3140b6a2 a5774beae71c73e43b4c5b9eef2a99736d25408619ddbda0f2b68ad42de91ad3 1 28088911f04de8334417ec057121173fd32a60e0766c2b2168aa8cd48c5b9f0e 443bfc334c210b6348ba43402994cc5b5ae0973743e911799dff7f1ad9284c0e6b142a5bf95025ebbc836373d8a6f5642755d03b3a76c797481576fe2b922a0d false +check_ring_signature 64f55b6c44fde113d04a82be78e341a3b94b60a86fbba4aff0b88bc631706933 b353433c51d5956bfb55b2e0b3b2f81c288bb0d4dff1ed0058645e3ef136b6b1 102 d3a3a6e2067be822c961da290a623d241c94206da83dfeb98b57d55fc286d74e f7c90aa59fa735e47be4ca76dc1c447eeddb2e4ed9c1a241803d5646f8cd68e0 69be0449580e21c8c8b8d38569f5bcf75e903268c70bb0a425fcdc2aa993a2ce 555734321cd899d9165dc973fd12153f04cfca1ca373ebe95fe422a9088143aa b976ce4e8bb4afa12f4b649c71da7857e2b90a9a50c136363d921eff8cefacf4 f497fe029e96b2b023d93c1c0ee723ab8fd6f4b438bc453fc214173de8b38d24 c634427499256e89944c2b1784b8020b6cf1ab5fc01c27dec4968882845c9ad1 b554b192874e964af78cf2e5adb2f4ebd13f2ab1bc9caa6db86b7179178bc387 35c3ae6c464c977fa21fa97f066fdfb01178ff2488408e8d100dee246afbe56a 204bb31662c0636e417167378224351a74e5c0222456f47eafa03659363afae5 3cac42338ea9af22bff480dd0df08c43d212542a38a9da54a2ee2d43d8ebaec7 47dede1111152aae42c586fdfe27aa7f5a1af935132d267ee4a3f56de3ad874c 356d073db21e7a9912b3dc62225fff941889b312f85f46eed36a4947038535a3 0b10684182af0aecbaeda9f46a88032a7e5e2e5d572ca0dd0cbd46b917aae711 0c163e9dafd837b2a06b243d8f749dd948304667d484431797b7d90e6845a09d 687880cf590e195d6c377611683318cc5a53a1ec7a7161200d177a5ee3c9a1c0 d26a00b8225fc416c29343aae3d559c86c94da94195fdf1820aa4696d91655da 9321206b0914f934d96ad4c97fc1179dbe7d37b276db003a7c9690c9f8199693 f89bad5fb2eb756b588f74f08b5a81d5902f0fe026e496b74f28e41cc8f06d95 2b2eaa40f25dc293d405b5f26cdbbe76305b5fd586572edf620c3d386b8647d9 78c8203ac8bde2ed363c21d5fdad20576c9b8715f0bfb092b0070c54f581a647 4b3576889b9add12037b112c8863253bf78a929151fde4a888bf6a2b8e286197 5f50c7fadb41dd50dcf1fc49bea4b0f2ebac8116abab2f6ca82e81adc3dfda10 eed9974b94f4350ed632b51057507919299933066dd23611406dab199e183576 89819797bd31880b758f56a5a1a5ab6c1b7de363eb21b93348ff06bc02328614 829616d14ac3a04ae1c693d157191a284d70b2135366634061c6af08770cb6b7 bb4ff39f3bd0002e56299776b9b88606e8d0824488edc9eec3b96b23c64b3875 e9d204f364006fed7fe01dba65bc3977fad59d500e114e054c96d5718d767e08 4accdad1c1b79cb6ffc79cc6745fcacc0eb7bf2161ac80f4300bf28305ee6aae 78eba57f9a69302770ebc2300742d27c497d0415a8062c32fcf5854871ae1e92 afc8f93515bc95b1c7232a19702779e71e541908761cfb8cadc47c5181b9077c 0ab9c2b348b5de3edb7203ff1eb12f27cb76e945fc05382d77fa5089358e1109 470a217e66e2a044025785ca2f82f6ebfefe1d5cbb4b12e6ffdbdc770b0ad1c6 bfbb5eff0d78b3656a6276ba7fe39f34ed7a5cc196b852986df025e26dbae5ec ab29f49c534e7d197ecac75fd7ed8a207c495ea2c4451197361717fb1b320fb3 6a1ea210db5d293c4813c715a652e37fa84231a605d9d1ea268f6907441fd785 7946c383faf8f1a503bbbd298a21e1fcc03f37a5a86ba5e2b37534181bbce295 164a98a0418cd0d5a2b918dda8a2eeb3f5e39c90f6c0367685bb7ee4ecedc72e f26a5e0e736fbe6ad63adb755fb686f51bf375e3147838c59771a761d62855ac 3e42a94b481b7e8eb02e0fc2a0e223ab4e899f8a63866b978ac802bf9426f27f f67ff3bf6c403c6479ba25920422d538c6d110345352aacabbe8ffd18bc1092b 96ed159613013daf71676cba7965583b9771a2eb8ad63ebc010b13b4b44eea9f 8a91a636324f8432341aef9d1a230db5c8c29e13fd1ac98da42a5cca0d16ea86 039965e9daba06636a85d7a24831aaa91d1577ae91673a9176ed66dad5e600e7 9874baeef6349e116932fadfbe20677a8cde694d13ad8455b71ae3b9675b6fee 689d24fac2b3f71b15cee3d3be655a3f3a6c962e9ee291d59cb7c3083224b5de 6f2986fc8a69373911f471c0546b2a23f54c3d9733e1cd02b473f93586b5cc3e 42822ecd642b6d77270aa7c75496bac58468740489994b63f5b9cb0a4dd86672 a0ab0b03aa6f6b2c47bba281840b714a2d955202e4703f4c435e72fad5ceef83 db95108d8958c7d1673d9e3aa95ba3c3766da8230bec730250a71e1bef7e9110 f08c0ba2205c71c8ea2a735b38a294809939da96263e5cba01e9a659d058b591 9b1b063eb3c45aff5c6a3337b96b281e6718df0155ea91239439bd8b745c4f6e 87dc3268aaa41ffe0c41de497f5a5bfb3b4a95653111106cfe37226213e7c277 0cf5b397ec6208754e8b3525fb497d7117149d7f028fdd5c473f4833102d66ef efcef94d2723bc1e62a7ed99e5477a5d374e51a9d3eb62e3f28ebcabe035257f cad05b3bae231a3dfacfbcb70759e21ca0a445c16a95f5016c28640fe2d6fb82 3d64d54209481a46140770f2d884708859cb4a9c734e9cdc5a5183be46874f19 54f4eab3508468210e9cfaf448287c260c9d878d58d9f395e7aa2dd09a7e88ed 216235dea9d41a0e4c6c7ce261614819f3b2b733f0c656456220d30cee164698 262e79bc97f123809d26b477984491c3f6f26ef98ebca4878a0d942765d727a4 b078ce20f82e24ef444cdbe62701b69f4e33e530cec5afb3d9b6fe09fe038cd2 ffd68994ee0e728a38a796fedfcc439e177df8f7bd1be77882468dc2770bcdc7 47ed3b08ec7613fb93d23721fc1c928cda85f4a8093319f9036acbdf66f0b056 b26e8e42dbf0e1e2a05778f7971b27c2fcaccf6d15d7f5f8b6437d41796161b2 16b3daf17b9542d8fbad466340b24fe96609ee72c056aea1f508def3e4be10c6 dc90f5a0e912e88877f52d2168d3623d3e146ecff5b5b0302795877988917a43 2106248f3ed49b35d4291577b11a4a74dec52305545b20ba5f84904e832e06b8 01fd92fa4e2c5ca29ae4d43d386d4ee184222133e3115e6a5c330333be310608 fd22ae1861f8aa8ed119a0a177e2e4cb418c9decec4f43c2774627f972d70c19 e87742a4dfab506c7deaee6395a94f55be36f1f9f07e42271125b0a7e6708e3c 5515821e777ccbfa91f39845293217f8b8b26567f8a2648f6a0f663a5b7d792a e29d854f4cbe7b7fbbc9a64a1fb3dbd8d649ae00b7bbeae78e8364e27ff63bed 198ecb4f0bae3f4c2411f18ba6f36fce82edfded54d3ae5a42e0669789a3d4fa 4259e0627de53cba9eb30611f2277cda0a3402802f6dd924408f20b6770f1151 dca5f08948d6f69fbbbb5db10f909b598adce49fd3ecd144f7ecb24f0138cb1c a6c298e20be1a5061f34a6cb31560bf37a024779466f9b2fb82fe0507be55e86 70d5074da26520925652f8be05d7401dbaaacda3cf75714bd6cf3632cc6a552f b36a0850bf38f41d37fbf2cb8d65f5acdd88d573c4d25df17f753c0b324d6e44 8dcd063576f0b6fd68bd7e8c066b29d6d5b32d3e9b8cbe73ca33ca68eacd6a5e d0b37d34de9d2e0021ccf6e501afaa3d77f712bfd152dc3dbc304c3978b061f1 12a659b3e08bd0d11ff469089ba5c2fb29ee0ecb76efb646e263f4879a149ea0 de6db8855a8aaf79782b2e5d5df0aaafc1d071e458c181868ff6e9a6a59304dd 74f98226b632b251b339e31c0f04ea182296c4158c242a8a336207df4fe49e9d 398c37dbbf9764bcd6165bd7673b9c227e17e716a577d4947d48e485bc8e099b 0ad8241df7a44e751083f7dc0570f8ef4c15f33a20aa745a160c0b238962aa90 e02db282e2fceb096c335e62e665aed900fb8e0696f64834ab8d70fcd6e984f2 0da548640b6307993c39edbbdbc24f5a812b46ed545861d688d5bbdb0d07b341 c86d83c2da4897de459bf58dfdb830e16d59ef1dd69bbe2ae36927cfc9c8fd6b 1a2cbba5e1bbbdc7fc95045201c0d4b2c5c2a58e6b9c96ffa6d1283c74902feb a43fea8f0a0f0121fd51bec63f80cceb32094a73c2504ff6c19bf752b4421e3a 9f8adc4d734802e474cb126ff76cd67373aa226651e5a6314aa54b5599a7aa31 31617b1bc1d98a7d01d2dcb8892047d117da55a6b5df0b74d3e01121c0b88b7f 2b090318aceb5bf06f0ec91525b8c809f1f41e017a1415455723148268d199a2 9a07054a4f2fb8b3863a86f3e3087468e4a4ebf013c94dbe740aac541c8903e8 f3eebd9cb1481abb026ff8cbb017fb36949bc88912f9cc8f11365fbd5ad5f5cc 60fcafd9c9b7021c26b361e838a37734005f242be7ae2f62dd8fba7cb733fc00 fa298f3c99df02e9f5235d54fdf429def62d8c7b510b76b9568ddafb40d4a96b fe3c4c968fd7c5cfab4ef8fe544bc75d9585248afec6acfdff38e999dcec834b fa914ba5294fa8ed3c82bf902383863a0a9db4f78ad8b45488496a960a2b3b05 f028a248e98baceb0fb1cb85fb3d00bf38fccb0a043b52dd4c48490861a1bbd2 7913a9b4f9332381bfb43b9a9e2e67629e597825d6147a9117d0ccd72e0699d4 aa0332b516dce860de269f5921f4367e958749026adab343090cca5e2c3f547b b0a91f78125e8a0012ef62b2b22a6d539df8fdeb7764133e9bbe203c9bbcf10e5864624ce42026a4ea7a1210dd467f5cd576638a99d909d618e5a725a4c5630541f5a46c21e5726da0bcd27bb97f47c7fd235e8f4392e1dfdf7117b9bc5dde02a29f3b4dbd7428c512926dc34b3a82b81b90d62faad082d642d1bf5586f06c09e291cae7e0172f6512a1ef12cdcc0f9ec9666977ed4ab142ed8ffba3869f4b047fcfd4e1456d1cfacb84afeb88c27ad26ff059291db6c486710a02de21eaa6051286193ea3df389e056a8b285a155308eb7a1c8124f433497ee4d6374398d104da60941b06357e6993076506f4330b00165d3bbf9b75c194e79ed27af56a17076eca24d21fdc56ddeb1c9cddaad7500afbb2ec1b087c4120ff8d6f49ca09a304ccb2e2a1218f8516841c248466c26da0bec08e55a2e96ff71d32ad875c6365029412bbbbef35c2d1709f293befd9286ee2a9c5531c1211369612e9263ec1f606f820551ff8c498ad0a0c2657d6eb659dba1082ac5939a9266459141a385b850bcaebd26818aed6b580c60a8528cbfc3fa826741dd18ef8392976c86aad697807c4dc92d25d64aa7fa48444db61c1e67aaea0c25b7c73b716faa56878500ac50ee2caf760fcb4638f42be2c08b55954393fefd3758ac0892f1344e887c3b8260d29df192ea3a31fd2b05c2c52ab139dffc963fcbfd79c40f1cf52120bc0fc210ad0205484047513b0e701cc9431a39c64002cb8959820e1ebe3b21d1f77add903d4a626c4e43dda353880d04d6c9bd028b1c5e604d8a275a83c9b1e08db94150c91eb41e77d594d7e154ba0bc3ea27e6df74453d0dfeeb604eb166c57ddf78e02ae374049f758eed1fb3638bee7828821c6863a7aafb112daa7578f80fb06ea0069f936b19c47f925e959c714e0e74ca2111dfcb9c487cb08a40cb4e265a0670682385bebf804f0f86f016044f2e58e45b2a28b4c57273747fcd9e3fad1d38b0f76d8a9d1f033229be33068fb827894c688dffa0e4da9a1db155d62ac00660104f5e3341a92f56dd17bd9f7121850a47caed480bd4c1fa5eef7f3b3c6ba5bbb0d1bb503b653ecaeb61b6ea6b91233142d6e3ce5225743e399b3def9f9c216240fdcf15e3d13eede0d4d075c3eb26eede69bf251c43d6dbbdef657c3578c5ff4064c705b3007a13eb8d7efb4efe8b1bbb1c891d892249350c507225dc1344fc7045f2228bee128cb65d1070c6507d585353f3f1444f4475b293a15a84e97169b0a0b67ad1d17ae23471c0460f58dbec7ed042e353426bca233f29f47de011fd5021bfb50b844ca0609ab27d4905944f3d8334907dd0b288acc5abe97f11d890c0834313c4a11e3b71ec9e8d57fa22a4497b5d1599df9ab41a34cce8e117955ee07e02ba17b5b288b4cb0cae6df350d6757903264d268465451e63bf363784b72013b9a657191ce395bb3bc7575dcb9feba482de02aa469e67f6f15b83aab9cc708203fda72623fcefb13a481f5b47147a93fbcdc8d56912dc379629bf37acfdc0c0535fd5b872041fafce6ae4b8b6d982994291ff35f284ff4838dd7ee3d6ab409f6893ac4886cedd653a61b3539735b9b5e8aa10352f14d2547339a2fffb098066196f775ad774d9b3743af3508acf75c8276ea5d2e5a59e548c8c7a96ec4f305f0ae0212c70b6b3893ac0e4d474ea39045bc1b19051f9ce93c718e61159bd00afb5fe9b1d339604073419a480e750d51e7982dd51cd5720b503f90eabb2df1097909598f196d03dcfb4df78f4d264c2d7b2c6b4def7ab32efbafbfb059c6e107d5dd4053147ea9a5642d3f37109a88b602aba4f16e43b9fcbe7ad9bf007f8c0dde890a716116c04b078ef1d7aa0cf5114b6f8f61848dd45be9fb5b3d9963ab0205dbbb73062a0114caf6517c28c23d45e6b5f74151b2312334d3aa4aa719a50ada25c367754d28e76a97c003445a78ad12809e567ebe5d21a23209c2d5253f0e532cc7c42bed20f48e951483607ed6a19adf2d10b896d6bc5f2e380124736a0d182480202d1a6b2aa66350d2b0f092ee474f5d809905c4e2ce60426e80f318034098a5c9a7e6d27bba02b49ada6aea6ce41d402edda13efdd58442a8a20cb70b2a039f58778c8d76e1deab76534a00e5c452b0c4903a9892d06fabe800359700b035bebdeb3d544056cb1800da9268d3161b704d89eed89cae581b85ff6c370a3fdcd3ca9ea66268897c72e70e27d79152b2070e9346caf60e3ef02c80468e0387f6939e3604afebaaa2c917926c5c73a8e6fe188c3bd0ae10502107087353075174f196d893fe711137b14b44832e08dd3a0ff8a768db075bffa6db595c990d11c539f98420169c04442043d9b6e7c14d5797b0218686c4fc62ede27b4d0308077f5b9a5ac4a355ad51bb3a9cf167c0c575850e3051c401aef5d6ee859cbb0b00814050bb17ad1a3a9b8f5eaaa396b5d46b34a1a71864ed3e7d22467f319600ed923177202c168a2b4d45b46ab4213dfcb11e39bc547a314656af013200e70fc7ff1c8f6567e4e5fe9266349782146d2b912d4b103dc1216cbdadf14bd70400779a645cf8e437e466c2f1543a56a68f1a4986556f17edea22a4cb2e12261f091f015692350cd5e93b7fa5351e65316eaab4df26f107f7d8f09a0a4301bede62cc1ceb74fa8c89c6953b3bf0901582348e6335290072e51cda07dad292fc4d050fcdfee210dcd175e88e6b4e350989b5ec1a35306ae0ce02359589728ef7f40891839d5a8e179d36aa782122d0ba5aa93677c44e7738990a675b9dfcef39be06a8f8412f7bd0dc91ce8bb2870b11954aefa79b5097395d89d4d5bd4e5eae350ef249b58c36b0f3684f57d57f4df522dd9bdaa454ed45c806f78401a12af0e300e493a9acf5d8ba5afe7d1f13a2c83e23ff3e28747f258aee8661a235168b95028a73cdd566795949d60d346f5138f6e18657534653f0557a1dc1439e29d8f2052ee41e76009242679a041d11b7c6e8496f84fcd0d779e959c7cda4775dfdb00f70931f6f53b99b83c9f193c6cbe4f899a12e722a2bf07b53be651362ed417605928109ae261c193b5cb2151df3cfc75ee3b3a0647c4e17fa91d4046abf5f6703016b59d4207b3637143605250725f92e3b120d3cf36f74c47bc4c01b04cd760eec2f5724d5cc2e6d75b492bb9775f5a2064cd02d91b03cefccf1c868e5219d0f2890bdf96d187574fd3c6f480120a271204871a97e7960ea45e6040c9b8f42074ea1a56270788515b08c6905cbd530981917255accf718818546f2cb8f2a6a03c990cc40a2e41142001bea1224967d9b2f096441ed9bce01c9a49ab366a7d6027093ce61312fc3e1fc0ff02bda0067727b4beb9c9b8a5609e2717e2aaf18980f7a725c93cf83c709c5656dd902f078599a05d3b71ad030dbfc325cbb3502cf0fa473d984af0cb4c4be6299de01aaca400dd6bcff2c7e5d161c5fe95221b72f047ae19b7783cf1866394615136772a517bf10bed8c45ad34c11ea70add5ad830a87eb78d8345314091a5a12dbaebfad7f8d7f7870dd6236c684f976c403ae130bdf16b7155a8140cb51dd83c7fa81db266ac0ea44aee81a6db0bbf590d804d406dfdadf78e48254276acb13962c6f0a01edaba8bcc48f77138927e4afe821920d12ff8713fe316e1b1ee3dc1bd6fc8777cca31a22046c5f2a9c24a791506ad008a99f6ea6c2b932e7d214ce16d30fd41c19e30ad458f5115b961d40c9c69c2e071427bce3905307e87d541de50997e769fe143697604a165f48db22f3c4abdb04134295b434f7bec476d4601325b9d8ceffd788010dcfb2d6f9a22045005c3b00ff64a07a0e2a90c2f3203bacb5757364aefc74c77c5e15b3dd8ed0d7ebd0be032309b3e75b23b2ed54a4b335c88abaab310be72f0c135ca2711e7b09e64895087c34273a64ee587a2b3b0a5faaff5c20270d6c7ab24fb7c384f29e9cde56ce07962522542966ed61d6bff5135ab3a74ae3b04b1cd8fa35f9644ee22490e6f6075ccec6114b4f564839cc4bc332fc761f895f58f7919d61b29efcb48ad739d1070766d58abfd9018cb5698b621bfae176d417548993ec2014a95c6527bee7e10ee54b9e0c8b65d8ab833c123fb7cd20c0511b7650815bec30df44ae0cd98c4e04ab1d2abe54170e812065d41014aec3fb921942a55cf6d6bb63061a82a3c003058f2ac1f218c12ba90d49a7563f7d9c7567fe8cbd5247a9a26502dfdea42bb7048e3ac5f61a6488b9f09dbcdb68d0c1b1bac70c8a8d81352f06ce79d855b3190499c0093c3a4f73c23dab4f9d0c2299b662d7a0ce9e8d439f018c76275d0f1d07fd122e638e999138b78cc52cb3240366f43091d9e781e971c74ee0360159c50cfee8250a819d0cf5cec8ef742f91c94adcda76a5230d5e3f692a64b345152d0952e0e4c77cfe79181933ac8737e909270e6c12f6a808fdf88930c99e2f08f6036834d269d8e45693094679fd74668878220d53f881c6943d7c6ba47bcd05950f3116419122b00091020ce17ffaccf4afe0f347f5667747e1c5f4defd8751320cd027164c6b601c00a942dfc69673fab355c30fb0ef94c6a964dbb416f132440491c0264ff05ffa5d5f0d07ca2a26cc5b8376d68e7a2f5d29ed170d67d3771d06d7ee088923ac34ff39adb0a560b7b3dc58e9a227cbf125ef6085843e53cd7b0b072fa04c2cc207d9b1f471567c3791855df00f420a65e05f8cae975a8c9ddc02bfa498232b37b837818c339a61329be05a1a1796e95b8d5e8c18fcc8e7393709ee0538148105009220a7c9555161244e8512e8d584518d140941e48aaf969b0156a1ea88bb0c33111c8244ac12a871d3fba014acd9548218a57af945ef07940d378e7c28350caca3e99772c2a0a85387c749e01206b6dfdfc3a62d6e28ccab0de0be12b1d8182e0dcdf191e9ed9184a82a3ddb21759ab5db08ebbed7fd763408bf014847f9f563ce9244894bc70e7018c29e00781b2784eba6de4c74699f37098570f134eedd2fa5928e1f0c85533797785a1364433d599ee5cd7ee3c0c628029f1f4be050d718b4c14363139eb425c2a0be4e3eedb885b4ea8dd52c02d11b010d069980fae874eab580a50add0d72a8747d48bbc2986ec5b67ff4d6fb2a46080dcf8ad376640d87845b06bf30b04abd03293705d44312c67fa036647981f8059f18f163c9daf8e22b75cfd23800503dd8aa89c03cefdeb342bd522c0e13210761ad432cfee84eabf81672089dc1afa1006171d565c4f28f267b3cc64f9f1b0c8b34cda4d8dc0010002cf52034df7cda900e570c15b62c99bd12240938dafa02a712d53d8ab8bfc9852d89d8af779cda21783eea282b3911dc37386b44dcc10cd224db7f450b2e3efb15b9ea6601ff65ffddb3e406be6643f02df21b4eef4a01d6e89e4f6ae8eb9c79fbaf195d5559d3857ae2673a5875b80efe054edd520902d52f240a8b83867a9ba79e92f959a8cffbd2964308855f8a20e9cdc81e3c5a079647ee863d71d64d9aa7697086806cbf852adee8d087f1d8d99c67dcf1a8c4071c0678840ad54746325911bafd5b92fda2d498046f02600ebe941803a95faf0817f3b8c93fd395005adfde62690f3d2661ffe082733ad0d1e61b46723be61d04cce0f21bdeb30ea1252cd5560056fa25380c930a6e5ba96f3d3a0c243458760c35f9ac06cc3571bb88648e9783dd366e6e43e9d2908d64173ac41458d8b0c7045f36c0e83f524df451f5f627a32407fa44edaa6877ac7b7936f1a7b27f58b30b60f37c69d4e2beee1329d7bdaa8f15cda8dbfeba689b7779c4882e1878f5de081dc9330332af851d3100c2d6add30ab60a52bbf68e762dcca97a4f6a9af20102c0fd9a03a611bf058c7a33f34f1083cbf5110ea287a42041ddfaebf00bc7390c1ffdc7c9792298c53bd8b653cc1383593f73ecbf6ac426ac0152cd81fe5ca80956a361e131513d94210272a05a93fe0a04bb675ec41aaf762e0a7c819dd4f4033ae9a117c3e39c8b422813bc81dfd21958b065535b17dcd63d9e4a8fd828b90b7dec2d80fb611b1859cc3b41505f10e874babfcb58096039f325678e39719e0a3976ffa8e0d43f3a550e4473a5c97fd200bb0a9e3e1ac36579398b4a0ec93500139c72206e67c545eeaeca7bf05c8594f5db48fc42cfc091d64269e78f935c06b69975e8a351d8b2cb68d59ad6ac2df214df6065a79e6c47b8523a0997f2d100d1a37f5b3fd4862ff44d6229079e2f9b558efe060f30875a31edc89ddcbc760358ddc51c732d32ca276672b8eb2cb7632ffaf390de728af2897113741e2ee307543566b226ae7732f7b3dbd18ccf811d6de9e6ea3ae0eb96183c36036219b707d074b179f12c0954234b51faae59ec5c759ab34d1580571cc6ff89db3fef0601b623d57bfeb28bf34a5fcdd20e56039a324040819afb32718d198144b25fd803c94d466fd7f62b34dd079e57d6dd21f5bf0a443b88e7abeb6a6cb745db76e0080b0917243c49aec12dfd587fa6a2907d1bc198d8f2b668fbfe2e26e1052a3d0120ba6571c7e8ff8f3690306f16e30443edc36067c3e79cd06df24b97c008990a292cc9c6ac088462bdda98f3d8e7a9139b1ab9b8c17ef7d8ac930922424c7e0d69e75b3006925db49ac9d09319e891217cf3f622b6c12dd85ed099af9e589a00e3d54f84744ef0020a75d009c004f0040d06254357dca7aec4306900a9ff73016cf9749d1909faf4081012d7262e7429811adc52c5f7a450f75378f2a48e23005559b7af305ececf85b50d6b6fdded3a6259b17497160f22beabd6ade6449c01b7614a9b45cd2340f86555d66f5500c85ef6b7b26d61887383cb05fd5857030f37bf4dc9290f5b57d4f09775b9beaddb43a2d1096328e339e562de49b4e6c802e7101b20fd39e7622b52a134c453c07e176c4288612e8f21d5e80a7c74661903326fbc90fe82f486c58666337a48d0fa8a2a848dd39a16b65cc98b5c8810ee0458ef9c81b3c94b225fb7073456611037fa01ecb11b27281cf8ba8166d330580129bfba743d633893b4f53c6029743aff09c7ea815a879d79f6816e2e7de6760106c1e5505b54d6c9e4ec9980a6dafa084dba4922face30ae8155707891c35e05808fc34b5fe26c22286b2461c9ed79ea397761bd17ce1252ed9f573bfbb325050812015cd9f37a1fdf28fde2b86a04601a7343b407fd73316f2f9910cdae170d83557f1770b9d03b9ce83eec5d8ba2fed9f14233737fc6f66b185e533902f203b42c11424a42a358f28bad588ec8ca12c1a43f83400931342e55e477255d660913c71c96ce34d23771f3a90cbfbc07735a7a8e79ac48286aa76a7c2ddfd9900d26f3bbc4d913da38f6e9ccbf593a90522a384e61f215b9c5307e8a8986adb40f4d5426afcda4f07a4b57ddd8fc3fd75e4eeb1bb9298147d54e0fa9ab155e130cbbc3414f1f54887ebf7e133fb34bdfa482dcae699f1fdc7adfa1168a34ce650bdcea30bd94195af123e2f5786ae9a0f6c8bca92531707f066cdc27ff7dce070772f1ebd02da228f29fe21cd899fd2f4f6067a7859e79fcaee1f673db0457b608878bfbcd03e02c11002b5bf2f846a0c7973da1302e9f52d749b41af5b2e35e0b3ecc183a91e7fe4fd6d57807465b2094a333d378a0b094437bab8a42dc603b0369a0f78b05301da6c4285bc34d0bb884623b875fe740196ef423e7425d528c003653d200814012329415c7c7804c7051267f5e9c7d75988e3cbf3b16823f4c085c340aa72d8f8edf07fa4eee10562356d8c2c18dfe164242b6fac76ef9d944049131cbcc934f66e3420395aef950fbc2d8ec99ded8854610b3dece50c6a3a90cd7fc5a98d2a72946abccf926db8a7909a7fd76d77c9c43f25a9a4a289f58690b38398ac8019471a0f352522d738b1ed6f92b489dd9ec8524e583e36501254a0c93148c91d737d0845bd230bc694ebffa7e7994d5a8e52fdc7e2bcf3eb9ce370d4546434fc397288016edf799fcdfbd225ea9746a6d80639002bb3fd93a1612013c77f0fb87673e57120e03537c950369d686cbe9295c54de027337b44581da0c6d08062f108eed6f0f5db373839997b1eba3eccd8f9bdd01c536c8e5ae92510f92807ce4580aec10d2a0e8ce409dc6487bb48faa9f5e29bc2098cf1ff15b5a0e4f364bcaf874308acd95b92f96311ba8e6d447e56580955bd86149f8b6846308784fc7e32fb077fb6666861ba8c99da2078f773a905283100b220f52ef832f0c93daecb5c45fd33ca316f8988ca662eeffd00b511006395f55861bec60476803ed4c7eb73756f0666b9e6c51d32f9aa69976b58941d51611d34b78da53b6e601cb07a189281f7980f5fa91564302d8dfa069157603bd8161faae36c83cec81069a58e803bbbcef01bdfcf5e4676222f6fa701ed22cda85d6d9b7ba5afbd3140d1d41f067b756c194e4ecf33c893c4bbe5aea38b5002fb4396f184ae16af51f0d2a6ab4faf383a3a9217433829da56f3fc75cedb94e4059ae0e3c6b795790260387909090efaba9a5375033b980a7f7cb6de71661e9f20f7b4687ae74943050015c676cbde301cb67a9f615bb78070e828c322e264442f4c2e82a7be4b84d080fe901e958c24f00fa3d2958c0f345dd7254fb270bdf0281a54b2da9bd756ee2070502fd464ad72b4799d82a1950efba0f19fc45aa770e8b2655ee2ee6430edd05168cb0190fe497fdeaa309c3e126fe75e4aec3fadcc93151d774117489e0560aba3505e1a11b94f91fa43051e31e56a09c5f1ef7f9d340e33a89c8871cc49900de5c5bc9098e42260c2a408b530aa868bacb70ce36f1f643a2ba6c8dd186e30f9b4669760433f86caeaa0ab1e0a092b5a1bbf7bfecfaf5a0ed3e9073d8b2b30606f2e4ce2e8a1cc6303d1e89d4a967ca7777812295451f3cf6c1dd1e1c08de06697eaaa31e93e3b6e6bc483bd7af59230fff79f68f9f454fed6414a9801bfa02c7dbcc86c776aec06818f2fe4d4dda5c756e44373f3c83a8b9b1a8a26266fc0ec764a6466cd9566399e06b39c2bae04fd027985ad48d2d1d846b7d4520f70908c789e937e7337c09d571e1d1b4eccfac7137a0d4f1cdffd8186946182d5d31009812a35b272aa508fb283f8293be414d1e4700d207e5c047d7f8740d58bcf40dad31c8a89862e6ed07063a84d05bce13c08c6028fbc4cbcb7d9bc9c96a772400 false +check_ring_signature c81af042e254596ef9c2e996cf5d6202144f208de0a167843bf8809c1f379c54 97f1bd036fbc3fcb6af05083e97ce2e6e33c80138c55d701d8b7507ac42af29e 2 4ffe6fc1cd50599b9d0ee569139d80a75d30f294e86c09825ddd0ec9cd3f358e 612dcea84a25558d60f457ea008beaf1795638c20d812a2131b4401bb82a312b 694a32f46fb796e8198b4c1aef0c049ca40d8e28410c2781d2f4d4b46c659f0356d650b0e2cfee0b5281e55754e6e151da981e1228d3f6d942d63bd027085e55e363f58269a3aa3ee5b405e0903e6a005030d96eb2cebc960006fa0fcef5e40425188a59f45516303661f3c8e93978d9ba930c36ca253793c4d809f87325390a false +check_ring_signature d067647b8452e14563ff3c7eab8e28208e2b7f1bd650d95c64eeaa724a07f4d5 bfc4bb194ed673cbf59793c9f1a4e71c743713150c8f7c0a4f39f4391faa2709 1 a949cc73f9ef6df33cd82548022d650a66af152b87116751200a7120a4e4824e 5bf42a691a106d590810cdb007d5a265035a4ffda4fcc3b7981d7c46d52c06071251ca9e559357814b10c80ea90fc27c00d7b917dcaf83b8fc95e230d8732105 false +check_ring_signature fddfae9ce3722f7d047fc02578b92e640d7b5205cb31433120f95135beaac58b 32dfbf0f12e6985c688b3cfbda28aaf8719b2b80889262d53253d8049b04260d 1 fffb47a0608d0404ea705bfb79dd594e111114089696070c91e114763f0ad940 4a8b5d439e67fa53098ff47f27ceed40b6f79b7ae273527fdff9d46abbbe8b03ce5bd912eee305832e8b271f5d2f2d04f8917d6ead13cc31b953d6208b1fb701 true +check_ring_signature ce1719d759bdc34d1d280083ecb3b8628b7cc869dd42493fe0a1d300a3c89687 877e5e02f76d1783af3e822d3cdca3adf11cad778697e08c5c6324e721695051 21 3d46a773471269b2d553ee9382484d9c729ff4588af153300771c55f535ed2a6 51c44b5a5ece69fb95d626559e442b5ca5a0c0ca38c2ce7fd7fcd2a9175eaa4e af108e42997dbfabbd9bba6c3d4d058a9492f9ce387064e26d8bdf7bc3def717 caf4eb80ba8064fdd06d1d3eb0dca7f85af4658be79d67049a6a85f6a7c74c47 77d142a04e740d82690c88724564a463088b9f8036c76402db61ab4fca9c6fa9 e7b01817ebf315a37baf4c40e27e9c2b68e730ae6fa2d523438b9ea596f5e268 9617e42ab24b050f91c73b117162364ac066ce55e0c9677423f17f2a52e08a89 472349b11a0c22a24f4d79df2018e35be784c0a1d1283a6b5b14828e5fe95ef5 21dd59484cc22a54621ab3ae5a6461f29be6fc9caa9386e3592fe8079a9d30e2 1191b7c4f6f7b36b2d98dcb89e949c290df371e433069c7298a426008710f639 7b4864f0e9fdc3b5ca552d5afda562082d9c3ed5fb84b0d847371661cd58d7f2 81537aa3c18500679a159f4eb66cf4e09464c22a8e8150f2561ca0ea5037527f ff9e7a442ec71b5e31982cc431262bb10ae4cea05cd4e28a9f0c7e0c98e30816 f02c726808f2c839227abe5ef16244b00c1699d0805b6636a3ff91787195e418 6000d16fa26c0c7e8c95a3cd2585ce512f7599a53f76e4c7cfd2e50a9e5cc648 688515ded5e5ab1b1bb9c6c3bb30f925ba92776126db9b1bdaf141cdfe357f76 55adb83f68932d3d6dce0c1e0a2ab235dec621e32855e6870a86fe09331fe668 f42b0da016becabbdc2f52ded432f8c544279400d68eb5a3bdbac5727af329e1 6e7741df960efbff856e949efbfc666f6133145fec811059f2aa1984961ec61d 5d202173ad3ed450d910bbe7a66eca0a3a74fe1c632b140a3b46824229eac526 9066ac1c71ea989b2b068d2b957a811a173d1722bccdcfa7bc67317a3114d7df e7934f6beeaf3f8f3fba6c6b2991249c4a18f9eedc1335d0b8f92834d18c4f00e2c11301cffc7eb7f2c460c78d0852d618daa0592b40c2b5ce7610f964581708dce051af2ba785f387b7ffa925e16219373e3ae604afb6b63ae41215ab6d810bdf57941db1d24f57e67b7855cf04f8eeff82b657fb814c7bc84b0bb4af822f0bdd83a41e5319201386e5bd0fdf42e9da630437d363da16ef2d90498a8a10c707515a86aa0176494706a7b7a45fd328713d79d2034603404ec30dfb06cd39b90b13bc6648a5784b9e650d14018267d6008ffebb47f774e26b4c2128b89428af0c87e2a53c4314dd94a6d31702724c75b1f185a231061298cd397e1c560bd6f40fe0a69afdc16ef52ceea321a1b3dccec24f71735cfd50508ab114c3da728e290a31818e2322a6edf43370400edc69823e46904cec701f647d5c9806ec7f30790fb87c1d90f5cc49e81dbdf9b7ba1e506c8d2ed435fd1fc9acb80e2c4ebc9ebc0a9f8730725e5ae2f54eb23ca05766b7c83e6a7b2b5d3773dd1e9aabd3eed8f007bfa757781c03802fb63cac9efa047da17c3482968ff17b7582e7079c63962f0efe5826d71b8803fcbe8556bc6b5452b71917348111923ad21fd75f0669b3eb09c417fb39f82a11f5c1552c0a032936979d062bd295b0dbce414bbdee2d8ab90d7d65f21b3eca80bfbcd51345cef0772442f1706d30ea4b89938764a6502a7e0110c8492cfed09392bc45c8ad1dfa57f4d0e725cfa8f99a236b8d520eac7a7906156d2c54676227eb417c0cfaebe06d4b9fe0f6bd3e98d89e2d74fc3e4ece2907b48383c1412025c2c4055afa0a6149628f8947982fd947a2dcc73eeecda2850c6dabb18dc434c360c311fe17f420b129f772ea14715f3eab2a7b93c1069b400be59ebdf6eea306e6f04eaa22cba93fe6e71f1d37ff3ffb45720c36d64a69f40d886416d842260ea25e76e597bb741cdaff8227629117ab79561b0c21013943032b4b396c0a510564281390a10e30295574490f95959432747088352b80fb6d0d23279f39931897fded1d4312ae14b07485b155214b0a79d954675d263fce0807839a565f75d217d7cae3cd37a1706260f522f69825e2221270f0aec97db0460893499e57512bf00764a02a35e50b8129fb6e41c457e0434ce8ecc4bbe626b90925574e60a420118e5e1c264e151c6af3a02f514fdba349329500a566ec7a420c6d5474acf8154131edd8423311fc449e0711697a53039c905324f0dc68c96101a8df1341635f1c32ee6f92bb731b85a20e1dac078420ceb572b25265a3dba303200d0695a1fdeccaa097f86869b81d33e5d6f9901d710d02c65f8a5d88df790d6d54376d536631250d29c1a5d531f9dda53db7abaf450db95d4a8f207f978606e9ad15466e25c3b2fd890cd544c6fcdd3b011bc1ac6ae38e0e35298117ee4d05adbadddd921d00b16ef0c30f8a8153ac6376ec6bcbf72acdfc1c2b0b65c6c902d0966974c9cb05a134a3acbfd0dad12d60f178d2ff7aa2fd61722d85cf8b5c0dfc03dd9614280fe7b25326bae09243c24708322998fcb76a872d65cd7b586902ff5af5b4d0c2a2a90928939bc1a0f654c155366be3cdf14f2c3f0a3bcba3a736aed331463a49f05d737ff9c5bfbc70d2402c5bd4c75c2324fcfbc412256b4406c9b3bb5badd96486e8a1c5e17943fdb7c1d37c0771817bdeca9276933017790c059dea2fe33b9397d74025f8f8a4d0ee8f32fa72a4d2d8e899190b63d6b63d0b1cdce048cdde80234f1dfebcfd16e0842db5a1339d829e647d5dfa85dd3344014608e083b2e32367356ccaf46c5e4d7478d1a7330f520579544a926238b5d30ce99b8d15e9c08bd5167d68d0c484e47014b7f5904d64280b8d465b0dc4f21b09 false +check_ring_signature 6c0ea3325751de12f720739e0a50444e00676761252dad337e7314631e003164 be75a39b87d2b7d488632636339bd980df9c858eaa884f93106ca75c084561b8 1 cf8e486bfc90b8a55c28c1c36a90155fee936952d6a4febd10507bbe2b79c78d a03ad97c8c8d1c177e454bbd556091dc5c89887717a881d272836d56ea6ce706834ac45e144d592747015468c1640c8a3132ab48c0f08b0295f09ed4f7a13b23 false +check_ring_signature f1c4b1b74667cd354105eb5cf59a24fa3cbfd5d87293b9da0b3c17ba36d58e75 9db00293114cf375826a3b4683100235b0a092f2c0dc1e4ee83314550eba270c 2 226b486979453227b28c8709760d48a6a9ef3213072173da47e61a5e154366db e68f39c9f4365a38a8dba8f5231c4ef04d6da2b3af35b039eb911c4e22c97432 12c67a5df3faec190c04b8ad9f5a9a3f672880e6423854682adfa3675cc084fda9a1d356b8468c43e9642dae9d846d07ca3c2fbbe509e9d9e545084b34a2370d55fbd3c9792b83e2a9c404cdc49fb62b2996d67a65cbab4db58ce285e8705dfe2c4ec19e2b008dfbdcc78ec12bc1607138a3b47ac5c00360c4ceac738d3cb406 false +check_ring_signature b63c1de20b8933e4f6aa0da7635a46a7533d0961b2c1688de11a333e72c8b40d 4af075d2aed3b40a1a539e23d85536953f610d22559ddd28b0b9ae7ff2a52078 4 be882225a0df728a1b1a609989cd7a01fb240b9012c229da7ee3e1072c0c406e bd5541ac9f292e818d94338b46ce91c715e68eac7f5b429d1bf2eef46d0ddef8 a5e2ffc68813ed4dba4525b5eb9732e56dc30d66c7cd8fd337b29fbf8db1ac75 03b61d360e199e46793bf4a83be2f17b27b5b1e09cf0ff049caf20f39c3f0ea2 1f9eb804cdb9d315615ac0fdedf0c6330fd325a25d6db096e3c3311bbd0f3405988134217d2ba66860509bb02db0e45ee82505079dfcd7204d8597abbeecea08e7f0bb702dd03d80c350c600b3b0297a14d95f44d56e9c61de1f1583d5701f060dd24e14c462a7abebe375c390dab3384461927679de23061c15d0e32566fe0e1060838998528b113d7561e81b474a0bb1df0df294172a02ae526aa3f3328f02c3d0324ac432cebf468e5c18f77103adfa3e509557b932018d4bd8b740127509860ab0ea01a2632862a2e0e9274ff190a2f01051edf8576f46281dfefd3e310eb32f63228522aa4c4c26996947437bc733b82342d916402beb642c09e892b402 false +check_ring_signature 9f9cc0800bbea75e9ac3352af1b4edc20cd8c9d08c8a401b3cff9821c040e515 89a0e091a5f8c8b9288cbf5d51a89baf57b9a67cd8cfebbe359630273e786879 64 0f1b75a671823f905210ec8c027ae5a9a37ab6627e763ed5e706428577cf1019 073e7d8696d86319a7344309e6c0876c30433302593d19aafc6891c39353eec2 b7e4fd35a99962a135013c42500662f5c1dd19fea97ee76ac7eda1aafb804cc8 17db66377246123e21305ec0f8cfca74a031d8d51da00854e5fdbf12d45a44ae c1980f5757b9a1f37fb0e1dd126d56a76c251077023c9426ab2b81f20f7b11f1 da41f689a032ee10dbef9c5febf357d166f2ca9bd73fd7a561726048ced831ce 64c92d11800869de406f51e552761ad0fa6246417ff54b93fd6ee878361ae3ba 12dc1583077169c22ea7af8bcf55cc8db5e694883a04ce1fe1ad8058d8bc9d17 ef9b715655aa8d736fa8d29b9d6271f0947093488167cacfa85ec4a178de3cf8 8d32b70440f49c522245b6e72892d8109ce9d17ec4239e9cbc8b83729cf15a29 be520fbb4a59810e4c9b3a6aacfc2562ae89ed3459384b97fbf4b7b2ec4ba254 f4d184a35520ee91f6a7eab7bd0c4d3f7f09706d04fee1abd8c81ad3101a5cfd 38d68bc8eeb40c27b525d87a9ee3272dd34ed46169b6888c55ee2c338ce08165 0065722015ce002b4a3cd230bbdd756ad7528be3e106b3eb35d55df62cfabde4 33a036de81f286c8f6c0cfcfdd6957c667f1bb7c631cdbaf7f6ec755a3d212ec b7daa0a50264c9e41c48bc440a4b093a3643627d8c63ea104d9f3ae44ea45bab 1f4967647a8f40e87279505a17216814c0c65fb38a48e7356b4a8602135e0752 aab8b2f843766b4f5372522fa9a006beab0af1c5b4437d742e46296dbed60083 7801dd7bfcd7165fb74f9280714fb96fb09062ab43af876781be5b14eec420ab d728ab7112ce52e207da02b2f6b69add7d61eed21acd5b65e2011b4e4aa9f729 e1ffc85a3bd1266f578c8414e2b3b2301235042093a4052743e01c85649878e0 84ad8a5fb943f0dbd13790c8f8e74c4dd6f8b5cf139b2b4d49a33892f7ee4ae5 62c0ddaa5892dde46223d16f082c9287fa0b64c19775b7cc5a09773fde6b6a13 39e986a50709066cd6dc747daad2bfc0a7e5f5eb5624850e3a2b83c3b7ccd98d 5c549c5e0b1c5a636731887541745e83873ad81064157baf87b4e1c58b2e68f2 0259cca813f8420e5bdff6ee817f7b8c17e73ec20b7cb8877fee4e928276355a 18a1d27910159c5e22c56ae6b9ee3f9da2a2a7093c70b9e26574fb62100c5b33 d86cf6c1f93d14a29507adb0de0ce35870383bc51c6395de85e5fe1a422ca65c 3504e269dfc3969b4db55718c2ce6aed602906fe4fdf53ba1de15802e547de79 a4f27b9c39ddfc602d2048aa56016e51f6fc3aaa48e41c442133922395dade3c 365f6847d17905d5eac92375a1329f1677122db584a97a7d69a96a721c63d92d 3ad8814b8f031aded6f576eba7c10165ef5dd10631b60d1cf6683d8b681c77ca b2b6c824dbfebee9b214556e66a797a7b85b906587861e8d17445f6d3efad7fb b15cf69a32bd3a77f1bd7f940ff4607509d7e14adb785fd1016c251780e56afd e5fc10988545e55d995fc16f48a6b8422086e5b2b7c2790ab8a38f7f6f78789f f538263773a90faee2ac5641c13e74a52549adc9dd7946a98ed8ff1e521e036f f72388544afaa1017b1783a38279e7c18afd78712cb24f85add1a7e6a2ffe2eb bea1f99085cab68a935d0b6096d7191ef29795f0ce8136d4bf29362f0875691f 1eb30d16353e11ba1f1d1daf3d6264bf5da40d67317e7cd9b5ed3871683a6ccf 29975bc0fbb045795430487f962b47425ca21d86a2f97e47d40b24e10129c7db 8066533e7c0f10a9a959dfbfc70899cc3def6c55ee05e629675ab155bcbd7345 5e42541dc698f7e91396de245cf0f4eb2160a8dde322efa30af2636eeea1af2e b958ce90edaa4569fac081bc8d4bbf07a78649078f2fc33852189027aad7fc43 fd6c00d82d8891c89ca678527433fcd0987e2312be31ff3509308e8f749dcbd9 bbcd456385ceaf6e880ccf4d86ddf7c5a4ccadea2a5977bdceaf54f98a527cbe 9ba1b7e1d76e45e56d66f6bb300a259fdf8c440cab35dff6aa19bda589f9f4d9 eaef0120132090f1d72b3104005b8080d7a04b90a62749781ad35aac01204897 b1c1c7cc6ef18b753b0e1b8c3cef93079eda205e391c33e4e2aadd1b5ac5a837 c3f2a36e131d86b2c6eb5a26ec0773dc9fdef771b70d46657b02e051115198f4 bfdf087707a9b9bfbc8f034ba5fb3fec38fb84c70ca01067253ed4c8d7c71b92 6fc0ce3d7dc3b1abf19ddde4ece9079d492e0e52b37a8e1b9ab08cb324b42a31 48831beb93c54c3756c9c9aea85a6a868e2edbc503ff90e121a4c47b22e82618 5535f86ea4951fe4aee5734c53a785bc7b639707f6fbc84c16265ec119ec6fd6 f043d7c0e28578ec9dd19eecc78e6fed315a457360bad3b4a2e9dc82b52cf4e0 76c0866bf2cacc5856469442b6e568ea8ffa9d500af85c33d8ff4b26dcdb9e9d 59cbe4660c965d2e8bfc784b157821044fab0f3e669e175312d5a3ffa5eb091f 8616b86a7feef9b0ad8ce064d26ef3b98bdaad28ca15f30c309853bf618dbca8 8c28c4218afc1ba7672101950cbce3c85115d62e40f613773f0c2b9c86d4b4fd c6eb6e9f2f4f9337e4e646ba38519508817bc3ccb65d39e752223f4318b9fcdd 803bec70814c5e84c3453c480c63aa042605d4900f980876c2d30ca20e921523 58774728c32ef02b2f4714cd9d690a804cb03a872230f0553b785d7629711616 399ca09c838d7528377ca0e6c32beb209d32ea794dabdc6446e0b70d18610d60 b9274c1a8f06c2b43a699f8050ffc93f27eb07684bc959f2e3cf73d25c47bef5 05eb6d8e09661573fe94876390e507d7631adb63f3986088a04067bec00dcb61 bd319bdd87c0b19a239eec6699b003a77e8a87cd4f9b283fc572423af125040a695fde4f49dfd3fb2da06996e46f1380c8858a3824ddf65f18f075b1e24ea40a6fbc73482712ea95050f540553c2226e1e975cd60876fcfb892ba032bb4834048055d58d514d2d1bdec7383072f9f57b906ebcc15d1b9ebe0d71c5d70731dc0c51729f62792899d1b1135d8f27596ce5e09bd3248658e46fe8d9bbbef8763a0bba8517b9136208f559f9cde004a749636242aace7c7c824897ae1e1ccdbd860861f1f0800be4af895a87c1ec4f873d6134b93efb6885a6f55802dfd69323a40c2728779f59d7330abddced7eebb930392fe86680435e52bc7ec38049b8bafa07a246b91891950f9619ea79194a4f79914e26d86c30e3b7d654ef6199cebb6e0e54e30bd17850d0e77316494afac79b8ad47cdfb12ca26a02dd5a1a950b589e01acd9f87d96e57ab93fe991ef63232e7e4aa86fedf385b0d99b6809d9a17e0707ec96341836df1196983980982a18572e7ff110fbfe00dafffd90917e97d91a091e3363283050976428f1bdf50be64e9276e4614705fe221d87bd8f8ccf6ee60294ebc22f76814c12135e4ea353aedf2e2cbaaa463f5c2426676638f4e0dffc0bfb5f663410c08b8c8506c507fecb8d3877383695372fe1d5e8e8a46698772e02534ef751e10387dc5c16070704ddc3d1767e8f63640fe34c133ce69361f58105ae21d5b4288aef9cde5eeb78fbc389575fec098b6a063f7f16c40bcb5afbca09667f219233a95259e2d4fff43c5aebdd4d735fd233866717e17e6cfac5c0420a5c4c2d0c1a93a2653a374090722609089e3a0d6ac37df1bf2dbe6bdf7bfee203eb8f25cabe7b2b65dfa3c453543adabd6de2a1b06c2f4c807a6b62eae9a5eb020b1d1fd1c39ac2438400a2115eafe2bcf1b1635600e1e49f073241cd5a6a260f033d79c0afa4303010a611dfd3302af8b75da7c6a5d8db3aad5e22f36e84300c6a3a94ca85ce5971091eb2aa3223857dfbed6f8bde4e57769538aa9944ad39060cd15055b9f2f21e4f872dcdb209252345a4d174691596e5e53a0d269a37e30ea7294486ffaf30cd88fd03881e92f68fe6f0188bad5182af8cfc908edcb7780efe958ee1f02ec1c55753e507034553f4a0bb5205116b6649657df5a149fb4405c02df21688a27d421ddb42152b924f96b25451e4195b127f940557d2ad778d0e0845975e263ccf841ac59466cb3297b9528c6938aa3bfe03539f91b7c24b050f7f0fd232a0f0d36e360dc3d9d56ecd0f2a460f7dfd53d24c5f9572f77096d70b165c87707b621bd2cd3dd309c562abbd1800b09ab0f37dc85d2b50d2893920046a9bbe46a03c70fef90919e5bb5eed9dbf0945676be8585612d2312b0b93b70312b1fc30e671c42002f9588b19f6b110d6a9d6f7bcc44faf3b395896187e750612d8689347cda913efac24b3b292f8aaffc634f296291b57cd04e3856b9dcc0cd6ac1a295fdc488cadac98910bee6a6cfe7c9294681aba3bc8842eec583050079a01b3c84c52b77aa266e9999266912dee61ee24ba17e46f326b30f51c52ef0a27a380971f83adaeb586a7629de4edb41f357109c2190f4c6b7c6def96d276057d8264d5d77e14b2d4cae335cef7db2417a3f9f7f9290723bd9a2eeba4a9fd045ccbc85080bd783699ff17152cf9cd39606a92fa0a5747a6d65d1ac4363ceb0dd8b5f4d9cd74056668e7cbae1de73b6603f1613ae7297e632e6fa420b4c301093b852365415d6e77a829e5a6ad04d70eed1e1d5b15e15de8ff38ec9429214a0f05ff0e294c03b19f4e578682489c3608c40e141ca47faaac9aa40906750d750c640e5e1d882c6c3340426fb43ce1f55c2993a763779ff034919bc312e995ea07c9451c63fbea41e41c49df9b731374008629fb306154846deeab63f9296f05081c0d055ec7bbcd6e82cb6e6b208bfd3d409fe0558a1b43e4ce2d7212f75599029c0a6adde1098c5af2b0d07d03925de26faa7afd6f72c8e193087f84b0d9a90c232ec44749e378f95bd686babfe0e41cea6501405ee12297046ff37a88217504d0fe7d16619e42fd2071cb3ecc3fc2f5da5057371cc96fbb8d5d2ac74a9db702eb4503e042cf7607b0f93f141e785e575889d00e7218b7466033d710d87f6c0cbe51ee9a9f5ef28ab87ee8c05bea5745b6c083a9858528ba399ef2a21ffcac050fcd6809f6f4a8f79f804606b0e1ea30396b66dcc0766d88739b614d70222b0faa61e15b082e43ff949327fa747005e7eb5d1160bcd4771ce7e2f69a9589940e3acd733a2c06e9e042fec6f754b8f1cb158d005ece62f1bd2bfcc538b8196302b1758d6632f62ab25ef2dffd04186a348611ccb3571b686a901838591115a80fc3c2362d24baa8d1430ed417dc5351a4743b9a539e54b5daea4f8bc45549ca04b3b5499499637dffba61c005e5bf3ddb13ce07e1254179190eef6a08d87e160f2216698f48e6986754d83bf6c68287968c747934e8afeac018c596f29f92e703704a5d0a11e7ba51dc10ef646c129290dc0152bab9ae1dfeda3730baffe52f0cda79dea57d4247c96346b272de33b6cbe4720b863a1fb79e252dbf04d24c5a0102656f41f02f1797dda81ce41b3706cf5614192b20c27b03ec5dfc16e6fe600f194963ea5a8991ae2eb54ab71a35cb3182d04456f86dbe4eee03c3e531456500be9cf6dc5b1203e72179c31437155b75178871927ad6ab55cb32315c4ad6e7001c8e631cc434975714314f97e687202c09b6317e069776ad0bd2bfc1d835250c433c5f82c504ce5d307e8caf02efdcac9f1b0a8e1ae0871e15f65d1d813c7c0c06916678569c56f92d5a43bac2c686fb96be30cece9f7f61e4d095486e39a60d44e5bba64d7884eb688ff4ae1d005cbc3cbcb92046ce06143c1c76b09260e80e9bdd84dd7e7caa07a244b126face21b5fb96c9b39de325c48a42171855a38f04a73069af8b444b4d14ab83772f889e4ee66f34348c9de6604c8a6cfe37b7500b7add2898966d013b2c1021643b32dd317832e00748133004de2137c59b6eb403a0c992279707e905103d25959301b58913d6fdaf7c71adda3f243d7ab9bc700e7846148f3126660544ac5d332bbcebbd3d80fb143df258b6b2fcdd30bb2d79097aace51141a7ae1803c78021826d96a74abfe0c5151cc1bf0db2d8895189520db16f00d7a642dbc7ec26467654785543193ce2f0d38737ff9ece892d512c9509e6759adb69d2d21ddba08ced2575eba48cdbe680594e134504a89723d6e4a4031d854558551a1006b020e44c0ae3bab67a418caf47ddfe367c383d0935942206a33a096d11abe9023bd6873e65cfea20244b5310ed86627785bd043b57e9710d8d1d3d2ea3b6e6ca515b8ee86dc2e6acae18d5ecf22e334085d7185055295e0fa2b276377b365f71a4f92f31f9c2138a2d3e45ec0ffec4221c6de8217f0a2b015e5eff91f6f9f9fd7073966cfb64c59d937bc3340c4f0adc8dff28b21a6d1b080e59af737842c488a88082651735bc7e613ea77e296b9e57ed13863e0d919c01fd17084a9cb86304e1f6454ff841d6b52327eb84d42f2a16dcc2c2e0f4aefe036a77fff897fb328120be3ce6305f8389817045dc164b37d83496c2a14cd0c203090a5d51e8c7b6737ce57e2071cf04868f5796ce973f6e7528b17bd8247b9d0beaf43ccfe519df95d8ef6386df416f5ccc328347e530a093a2abe383ff04650413f191044ee1b4bb3c70e1ffc25f68a767011026bc964995827fa0bacb80b20adaa9ab3ca2e815147945887c65f3d8f24d4dcf118b81944e8f09dfc585ce6e0635e692b4e047904acac2bc8f67e7b44face8c61623880074215541c761352a0e333ab2f0ab1803c95274ca1ec526f024895e92bcf6df474b3b173155ea2e85099238261e9fca23be5faa91461fda57244334f3c49607cd4a9cb6cee26643f607e6c372207b4ed22a6c94653e960fcdf791d96f97f0c1d2fbc3656eddcf12a80b36a482f43719d9461b29519b47e2223a4fbdf15d65c6f9a5c3b777bb43976f0cee861695fd3cea6f8f1940b4795481ba6f0b0b296c0989fd85856e0310809b01b05fd456fada5711f0430158a0aa92d547467ba5cab760b28a3ba66586528e04cb05d1ea209633430ccd3f7552b78bc48e3e1118e9b978022614c7e99e41660621b89706524f6f7c3736d6598a0c013cef4cc2b689e203ce9d6d3bea093e6d09ef91f60418c664c54b41ee5c1bbf32c5c8e0b8b0a235ef064d7fd44e89d89e082fc1981850cab4a84dfc1bfc260dfdb4aed7058e5e76d1931f21ca5c09eb6b066cae3138f7bc5a48a9ce58d1947b5dbc4158173e9afbc966f74332f90b801c091d812074a4be29dfb7e1a621d8a215b3b452640c2b18f0a16647f5bf3c7c6b07fbf6b4ca7820f24711d91428bd6e1e100d560408a249dd905c06f0fb6ef717064e3957d654faa7bd0de0aec5122d942cf2e0c1dbb431ca7279b00d0afaf16c0b72206518d1826de35f22dd6645a278064f5530c323a5d344dc2c1741960b2207ef19ffa14e6eed501f9cf713cdc4c5699578b0137334f83a72ad49c3fb7d21090e7053e153b6ae79b3c8fe194eca78465daecdeeaea5226b0ab485ca6a7fb3096587e43c358a75b8d74f061aae9266f1e1aebe12e657622dc4ea2161d6ecf205ec4183452269114fcd3517bd535fb91fc88d95bf4e1f5d32d862578d5a9f0f0426fb0e4c9f6d3b80ea27f5a744472341dcc5e01facc8eaae456cff090c67ac05b662832c781c4e1aaab610d11bc63290f1bc7dd0a6d2b3a16f0264ac43ddb30a6a933230dde06379cd84b7038b263294490d69c60362bc3c7805b2cc848e7204c1e60d65b5beae45158246a216e68fe2f56da64a348daba75d9a271a8232c5083873f46aeddfaf1ff11fae4c034f39c31bd2dcdbd647992056c163b6c6cc3e06b278fd6bfe0a353a8d09d6b919a41031e9e5a577f671f8f0aeb105f2fdd0cd0b7edf599bf2b9e8916b6503e61fa6c5fb7073fa0c5603aa521f9397f3ce868a0526107c4bf02ba7f4100a010047aa6b533c761a7368fc60a830fd20576651d505fbf984029d518e07862f44eb22a51026b9c6474a88c6a27fd46d209ab6f3f60b27edbba290c3b116aca6b308433c106cca65228447d84b32e65d413f82f6b3074505ac9f3179a78cbae5fc16beb0096d6cb16f64296e384a2d3d8715379f580afa3b5ea218505b6ce5e5bda144c56b6a53758ce642646d11463ee5f23d6a460fbf08749c57bace8b6af79040f9055b533dda5659bfbb69076f746f5544261a0e71cbdfa61e5bd8ac6a7612efef7a10cbb21c32b80e304558fc24be8906fd3d02fff3d1cbf32a3808589c047c30ae3d0a5fa76ef817dc8667beff59380392cc0dcd532d47c22f0e9da365d85831bcb6b7e19ec8f12006011b44fd45f816ffa60ae6ebb3a4804dbbc56c187c81a6fba6e482fc40a7da68d6937e4ca84ec4febe003184c8f0cc2f8b83a83d64aef2ba7a14fe264ed7875ab800c79f40ab8a7b640d2e0a72b7de75ee01adfdd4ebbd2acfdbd2aef7543fc1418b0d6c355c1abb0c0a705bb01dd004c2e402fe7bd03450ab94c5b93e0a77a73fb8766d16ec9d7851032054f414f8e8d816c9f07475e895901734cba62fabd7d61d50b4d5e7005ec80f7f9e8d2df1904183b168f14867eaf1969a6d256c0835685d4286a3f8aae35904fbb1cec926a1e79855f43f19b68aacbb2970682e19239280178b138e9dc31007 false +check_ring_signature a84ec5d571bf07a24699cd109c56bad6276242556746c8cc8046b1f9d59ff492 62b34b2fd4d36de46825840588b56b34b2e2eb51c90383f8604bea8d9f1594f4 1 119f97610f36826b36c147231d8329f86bbba53615a216e35c03dde02f63867e fa504d5b5190274c2af759927c4f061e37932705266dafec25ceb42987727a59b8aa1f3b1ab8729efa7cc5112ea9376cca238881f18fae36c9672547edc03f0e false +check_ring_signature 3dadde56c02b2ed7235bc13c5cab27c349a5af7d2ee81ce05801f25488cd28bc 39c91b91a4e310bc824329029f1f5ad8fc9276fe5d131e38d61c5eb843fee045 4 d44768b800011e2142b922eb2133be6b0dac600d79813d91d4961619331cacb6 5bdff3e43274f4cf3e5bc58ce04b61116571cdb9518332e4352a2850fa8b190a 6f23f15251be7b589cce7c8cbe6507e19334ebb0138be18319576a3935d4257b 30bb6868f326b0b7d144d525c16c32d6f9844b9f7ff6a5dec531eaee6dfe2b6c 7ce81c3d3509e5fe16690ab74f8dc6126d9d8e51f0cb71d24c33694179cecf032fe7e788b6fd585ef45db23b5e97f4152ef777d3ad154a112c9facaf4fa52e060442872fba5c676cf1ade37641c01728b553fac11637b9632dbec03015b00c0ae3d318d1777f88d6b42ab46b8505233551479690728cdcac23c187d01fc69e147f0b270cf354ed48bff544eb032fcd4ab7c0a7c9e82265439980753a58b3f1049b73ca52e4bd74feec2f28858f22d0a4366e3b0ee9f1e932a666d2285c7e5b0ba7cbdd4889ec203dd800c7c92f9a7a1836cbc6582b2b441156d60017f2798b0e5468fe3856dcc7b8aea621f6acbb6f50bbb5f34586caed197c0b209023542603 false +check_ring_signature 9baf1ff079bccb8550847a19e438246ae11457d686ee5d9bb15bb805f763170f 73f8f964dcadd39810562278fe0a03160d5e15ee826aff2722d4c10161fcbb93 1 a96ea7a8ca72a77a2cdb5843cc1fd289b5b041f6b4582520b3c71a9a0b6da729 ee5e14e492a5f0af73dfd29cc78132549c123c659c2806f4bd86a3715ad5e90040f5e3034fc2adb9d4659277ad64c68c1f922e7dbfa8834bb85c7327f62b0607 true +check_ring_signature 39b2a988203e1a1aa3e672edea0a401dc0a63555e6a78b00e4fef1a567d9c8a1 d18b8a8b49b9dceec266f09fe3c7f1032c1562ab78974b8eb06ab72bd497805f 2 740324e2e981342412df3351c21284c3c4304eabbe3db77d03617e2004710e0d 89f87c035cd77be637b5d8cc0cac28e94a240d5fc7005ed725b028471f456ebb 1627256b2f0505e9dbf73c74d2acd32b0ae5b25ab598362348080707d693c408252072b65fb9bb0dc32de68e63a8d1a3832072e0ddda35fe2359d09533c9650093e0cded658f6fab6ba1df7a14b73449667038560933d8ada1d3bc72af4ff403ca895fc8fbfd09e5e1a6171d7c50e9bb70d66b903069b5beb14bae0480504e07 false +check_ring_signature 0e83b06588749b813bbff5b39b3d3f600446a7de62ad85c22deeb2a0f551cde9 a05c4ea322c587a4db5dd56d4573cc6d19b8a73ad7e2d9952ab63d24cfe28f5e 2 860233a558b6393cac891827df7adeb556cb3813a26377e486e167a557e69348 79746ffd405ecdc90aa6d22ef92947ff412ec7f2991dd8567beb68459394aa02 5785496ec627c7f4d3353f255e4c623bc6f2671e7940bfc8257d86bea739e609aef076cf2603e4f243e3810f0587b7e32c31326aa4a1f2b948887d35da0bbe0e7576c5b52967b1f75770102535d92f87c9631471cfdcbd641aa114f25128ac0a74fe1c8646ca0abf8891991e299c3a6fb5fd088fecfe04d9c6fc97e83051560c false +check_ring_signature 2b2ff621efdbe6ce84be37d4b33dda3bd8609b33e458effd26555ae56914fb3a 8beadfc2a217ddd6f59b3a0d08552586da988ec371c65d8c00f0424c5fb46ef8 110 b45084fa0120fc19d2aa87220a939d806e8728007e3ed7a5dba93c5b88f6c7e1 71c7a6c7139ec3571ce6a29470301094309eca5207f84ab5c09e1221dcb0a59e 53005a6e8a6af60c54f04aa960b39db4e8e713dbcce42870adc79b04f0589591 dbab90c732eeb6de0ff2d89137926635268dbf94898794e5ebfcab66c3269062 2e49fc05ce0b77d44ac8ece6990bf7478725bbe933e55e008a86785a0e0f0a80 90c2a85a80acf7424866227143240f7c70823c8ec3a28a27ea625ef420818426 fd9a84a73f2a4cd5ab57810c4e76223ed94c6e60c8878c294ea736b1f600a27b 4f7992b6a113bda6a0dd59f4f1f3b0d84d763645e89fc356fd90a05c3094cd1c ce0af4b840705203889dd59ee9a7895f81f2e219d434c7dac2032930341cec37 b7149299a0f3f8d9a3565e023dfa34776a6024fdffb62cb14b043b77b59d4a93 faef3fe5df962e3caeebf5afc900308d07feb839bebc2c93d17d49de333ee61b d13a0e879ea95eaf223441456dd63daf8b505204234fdfe5f2f7f3ec7189e8ba 233d331817cd1753e1f4220ce21a8bd4fb034ffa7d8d6b7a7250c638fc9b9baf 1e0081a09beabf98c58909cc8bb9ccc20c4def630898573070e09e5bea592d71 b90d4fb0b55ae21f056120775910c3c68545e021f279b6f4bd7b38f118b520cf 51e08824452841818249598fa6899a8d6ec864a332bef69dfef2b4ceda21ad21 e8b1424a7fa361620f55d70852e12421e41859ca55f0e132266532999c27ec23 f09055e7719997d403ac9cd851613254eaba9988d18fd1d4e8dd0a40ff2e628c 6c1521083d5fc7572f37372df070e5fb050b7e4a8e0eff14a43dda3c6fb9112d 3f572869f9166a8917522c41b10417e15536259a8ed00e2b27f4ddb2841ba19f 80ee913cd7bee0ce2b89368b254c4764aa8a280567b2c132a33a469dbd80af78 455e97c627217c2977f755a23dfd0b3e5a518d6a6948676724f192b0a28ef9d1 ce9650d7fe312353237beddb4ea6c5748d63982dbeb4146c44ef6b1ceb54406b 085783af55d19436ace529e12fe0950aacf4b265fafe3efd75d66652f9bfe390 a848c199bdad851143c95fd1d0398fff73b7ae1fc0fbf78e3a3910ca86f989a1 997830f04e0078933805668f55dd4b3befb0b79cbeb416702f3aca8e0699a054 97f42d01f070eeedbd50d9cc7df46b3c0fa11641a8bf227f7e1a239ffe2c22e3 19c7c104960090bd9dfacb332d589d4ee13a3b7b16cd5dbef1f99e8f86f654f7 138f3e3d2fd6dbe1dab9c5d50fc2ca7149030efda188d395e3c8dd5b1293b609 8527bce2ffb8250a54a51586cfa7fa081ea97b56d214644f38493afe22ca6fe2 629e07fcaab08a7978aa1ce8b3fb65a70e8a2d6b241e5ea5ecb2b0ec6979239d 72296a514d67ae88d70e415f707f771910b42b409c68f71f386725c5f9f6dc8a 58f6d801310ffeec56491730c1f5293c47241fd474e75577de09b2ca1b10c608 82041a4f60f435f235ac5e3097e99def713ed6d6aa792612d5ef451a7c4107fd ce015298037ecd15d5605dd2bfac0d0af3255754f64c8cfba759195eb0c2a2d4 8ea5db57608bc9a41b347a81019fc0a0a858c46bee95a2694eb91e9e8281ff4e c436ec4461892efec441fd974a189c014f62fd4d22dfc6eacc4a7f1cbac6cf27 c6a0b9f27eacf98d61210cfdb5cdbe7cbe48bff04ca05db4c0397b8665962798 fd097c5cd268d22320e7bbca5e0da8fa4558248479d88d32190ed54be0fe74f8 e59c6e2df6b7a2139f85292a812296197823a2d4c322a323541c6814ade4d7b5 3d891e77ddaeaef8e29a9dec734e2916d9c8ef5ee7edbb2ed984345a1e044dda cbe5a3a5741634cffc7290055cb99f4d384798d2562534252f5c394898fe6477 7d78d8c179a4c0a55bda28c0d167b22bd80a9436e20082713a47c0ca2208431d 85fdc157648144a8cbcfe70f19395e552277e8bfef0d5a60a44a6fafbc3b38f0 6e27d95624bd384839eb9b631d0de4ef871f85138cb848cd6322eb15bd612868 7afa2311a3b0b15996f99ef791c4c5fd899afb60e11f24bb2e11ac6e9abeaa28 4cc9c0bde82f83a9817417a1aebbf7e349fb15dc37f759a9e805615717e50ec7 ccc9e45ea18d0b9042da59698611bb5806682de3f0f03542df9f5c261bf8a65d 4b10f065b7373396eb2fcfa7895a85d512d13215d4905b9cee8e619fad22a7b3 33e4b56b2c70338a020c040ca0e0d0b71b1c055490d74fccbd39547a0e88d3d7 53fc6e1eff3b0b578b5daa171679d9dd3755e2f9e3eb578cffed934e4c05045c 6bdfba92765350bbefb2230d3a2e5501900f27a76e34130142d7cbd4321e8ee3 c4c1fb5e6a4e1eb0aaafcf955a2e8a1b89bcf0a04f8b5b03050073ed6547f842 7b73ee6efaf37b5d9272cce611a1496b140ccd4e47c9a20b6ae5c7c9f8a32c1c 89e366f4454f2ddcc46021e5fd002f8de8fcdc765c8c5366644b7c6783fe5f3a 9ae2085cc2de80ea19ae52a9bcff09369b4305ab73d7eab0028973afb01efdf8 ec71845913542c91da14a3743de5fae54bfad63cd5e6d240adfaffea49f7f228 6ee37fe28df76d060e174442913f6709573dcc4d38ef25ce512a6c39671ce7c0 a3da7d1d1a9add85fce8ba33e01e3188814a147ff0c9bf33a88c9ab1fb876500 260b022ba023de791c638dcb33feb867670a377ac9ed10b0b2a3c3fa858e192c 7b5abcf790c1932274c94e15e47072725ddb8a9cf9d4e7770e9af3bd68f7f101 2e020c5ebcbaaa1c51bb3716c76018ab5d1192b242822c2ca919cc192fda2a91 b0b72a95829ee168465324a730b7ca0c226b33d338805c942c385dfff253dbba c40a11476a24e714a434b5028c41cc58820d61b9563e8294a542382b145941fc 82c326f8168d058d6f0dddb3db96784021ab06d9a61ae7b4eb1e4f741205645c 7f5543e2b9952d08b304b738328c9493aa4873fb2f33a3923c7be0c222c432d8 ff050f053c28d4cc2d9477c725a4f595ee026e755ba1c38fab47a2c4050a53a6 547caf197cf2808618270e9932d7009758c6a1c24595b544895c96da8e580637 3c9fde7f7f9050d47d7b1be0fa2518e268aef4bbd2493ce7fef0a19966d8bd3b eb25c06e8a006f3c4e33cc1d617b03c4141fac7e102b8ffdd7149bf0fcc5fe64 ea7cbd357b74c0faf5c9ac68162cfb138d23e9023579de5af7417eb75eac131c 463ede26e2cae27f3b73f4b3c4f89bfe3b2cd72f5623928e18ff49eb361e301b 6295ab3bbf629af5ce3d2759396803860efef638400398ab652e84b9f37904ac 226a80074ae256e15d5118b0a156977626447ac22951b59cbd40388432e242a4 26c81ac3a60bfd61790e00ceafc7f96338e3571bd19a85fff892615c76bcb3c8 fa9f6edc721fdc7b852d758a48bade95b1be80bcf17843812d4a74df32ecc273 ca8dd8639492c91e305e95a673d9ba9e4383c7c2b0f9c6c05baedcd26d014c29 353e7c396281a9bb3321fdc52e92700bfcf620752c2b558e8dff952571ca403e 23ec45821cd0945a26098a465b8ec6b850a6ef5a4d7352d32e5ffe69a78c5c7d 837c57eb077aa0f0f89bd89b3daeb71ed74e9a4f5b24b2dcb7e314c70ecff407 1f6e7b9b84515cb476e76723ed51bc59e94f5e6a3019245f9cc94511d9f74d14 91f890e83d409fe8e6624f2f36abb70047263f17d3647a456fc3ec274859f95a 4138f2314257c177297c4c78898542e1e54847a4c352c1ca2637ddd6618a5ff7 a4fa6ae4aa18b9587deb2d95d6504fe799f67036c1df3d7c1fd1e552bbabb96b d429a1c67d741c35e142d7e12a2f302d226c5354e3501db77c440008085f901d 7378be926c3884f8ef89890340e68988925901e76336a96c5a4fac8f90083c0a 83a380e6dfc72179cae9d679fc61bc8720536c1092ba85410ef53f87d32aed10 6b9cfe730272c7962bb3094b25afe119afad685afb3c1bf8170c5c9a3907f9dc 53601e9980a99b2debc0cae2c66063782b28acbe23c5b0a6f9cbcf30f4274aa0 2839274bd0e38b06b6c6eb4b235b10bacdba311bd140aa5b9d4564274b2ce0cb 54023993701eec48d186804fc498ba8da86e5620b8910af1d183e554e059b388 609b6f75872e8d8450203d260a50d4321c30625c0a6a088c1c93a2ac70e95921 2c63db682f948bdcaa6b04b2053d2f7fe28ccaa74574a5bdf184f134e3f00b8d 1d67b395ef941b09d6deb6599eee238eff6581acfd8052e32c8f62d7a3d2e2ec 2b861be57170312e33246baf92058ea34dd60274cece905e44a75bf295307644 0ba97c2b57715092c68dafac2b497142719a60e51f590faab40b9aae4eb5cbdc 9ae24ea5bd30e6345dbe6a378c3825ce5d952f1039e682f58dbfc572a4fdf4f4 d7c8bb1c1c3cf001fead14a03a09276de63402f6ca11543f192d1dfa058363bd da9786c8555a7590c2a5503bfd1c7bb45dbf8901358515de7f9762287e199960 ddc76858fa27452ef551c0daf902747648674e9c1097f0007de927ee0772366a e84486fa64128bb8dbe3cdb36d6e26c3defd8d95f0c5d67bbde6bee8ab46ab34 8ebca8334cfa5d49e33d3ef681b125926c161fef63a9f6a85f0242651aff3eca 9d3db2dac92c17c292f500d1794579ba57e2965421ae3ac4155c75a9c0a4facd 68d1cc83b725b67ed5c77d7e8740e8eb5c494d61b90c5100287ed3a36644ee84 10f6378d7b25a84c59c333b2345f7a8e5da46ed7f1aa5322893032e217f8c62a 8f476ba8c0ea88a370706e02b5019b18f9c17d774d74dea457bde37c3c84aa3a d40b3d467ba95d462477500dcac0affa2fcea47744bacc092ae700d396e67d16 3d65e9ee7f9f194a94da3d0ae31260195540906db2c26af6e6486384f8a4bfe1 2c9b93be8b9e8c6533d90654af17065115e9be37fb0bec0fd675015469a00c86 4387c704ab1566a86081bb4b3ba83ab401c1c981a9610e8b0556029d26f3fcce 4a1f84a5456f23e0d2dbc763dd46f78292c9cdfc0891a4f955034453b19ecd0174c8a75a096dfec8a69ae58241db3c1f77874b69bb55c6007bb86ee0ea35710361a916f61cc4fed23bc79d5df5b50d5a3889c50261afd3a407b418c251b79b03a0120de4b4914ef5295f6fb4e5abc9c73c5c30e367e9f4cff29e10ae6561a90de20a6c9d03eb483b844e088be56b6d04489ee02b243829c95bef0913226223084c5df4feb7e74691f979dc7bf97163bf768fbc2216cecd588fed5146a6bb1104a3ef4f967576361fd6ea73d7d9351bc873f6a5ede77c08213f1adde197cf1a0a91b02f59e55cc7adf84e20a7d04ea5aae8be0b029d971db36b91941df470180fb085ef8fd63ac3c03765599c2e98e702bb31a83bc7c9dc64f83429a2c16949046aec4c67e8dcd97791ea3a25ed7939a6430b52d1f7c912f47cbeecd89296f203829620c3ce736819b798d6d322a2a7055a587c4285120e50d185f913855b8709fa9fa7b2708660ea7a2e6a2dc23e98d491406e29521a777eacd42204b006030bcdbe9173129608e0317e027785fd699686c84cdd472146abaf2bdb5b9be029023fd88a22b378a08f607ee3b47ebf0bba114b3b9f31099e8b024834c096408f05f75e9a50684bcce412e78fdcba6db7bfb1cc65ecb8211e82b73fc0c0ff03ff05a2c2a3fdaf5f75edd6c561ebc06a5640e534b2dbe4f8fe8afedfc1388651af027d1b3a5807e2b2640c66ec5085af18be78a94f30dfa70703d26ac05eb1691009abaa7aa00922a30190d31ce22265b72f149ef7df5882c46ebf8e5619c8087d054ce7e56734c419cf94f486f7cf8b77ce202c2beef8a974e97f2901d5fc8bde05a20577c3411174c955e9e380c0b4804ba4ed0ee84dc629c26070c354b82d9209c570100b532d50adf3411e57031f849bf2f6aa0244be678279aae94a3b31b3059e6a9ccb74bf6f6aff2674e2227e03e11c366ae90c1e9e6eca853bdff27f6c0827edf46bb7962128f20bf9e11810c968b1f3a8ee3d26c41e802d91289a463806c0a599db14616522cc4366f4f806a61525ef78aefdfb22f8615829cd62b31404d78f8ac042bae397b9e8aba56bd8467397acf40112891c23f4bc7baf4cb1ca094be7119f5527bd85dc2f4d772cc0aa946058c431968a250906203ffe99e2e20ec07def37a53c920a22820e8f2bfa277f8214f3dd80c67a8a91f8142d76fbd109db959836f730db3d8be8542b06dc9cd4be442d850c0958888bed7140a224220e0552aff144d7bb62a1d1dae3922cb96ae726d2888a329fed7f3af74b708abc0bbb2df52788a4a5dbd8301e63c768939577caacbb582d6789eca7428de0e3e000e307753299c83d9700e0c8bbea2b42c9cb15a204cf079878304cc945d92307019ef09876b7553684fb8f13919a696dae6e583792ca6449d18364d6e96e9c9c01a05e6202c43e73ad39a988f81fcdfcf1ccfc4698bd51f2146b0e28a62b2aa3050a310b719075faf1fbd578bd22cd6c45787efd2e2ca49a9e099f5e5a45dbc600741a9d16877ab7864c049a4a8ed87549097717bdbdf40c336e5462efdc9c1f024434d85cfb560a81aeecd6d859ea447e9bd92a898172ea42017beff7151f43085525622022fb7af1076a75eeccebef5602ea6292c9301aab2606ff901051630894ce8a9571fcdb03b8f08fae3a191876d5c3271e6520866dd6435dd55260fd0fa86988ae9bccb30cea66936037adeb2bbb0447db892c8f3ea788ede045a05b04e7ac30385b83fc21c99517f6e1aeeabe47feaab9749361fef3c74c2302f01001981376b48b080ecf81b0ca6c35596ae61ed895559439bda3dc662eda55699f0f7d2b6b7049550d522fbc6a6cf5f2413aa56ed4fb4a57771addf20e534b07bf09a1db80c05abafe822a653062e65907417f08cc8eab49caa93629119fdda8f60af0bb39e3f6cd2993844e5ec522b0b43fd469af203dcbed953633ae47e319430514964a0144a883ec360d95d2cd3d9a6fb31d3a803d8bb5af31364beb1d512d07ecd88e33940b724c6fd48c9a9deb5b94030913f7205ca8d965c5a8acd1be2809079dff8680ec5d8123fa3a3636d573dfdf5283a5e3555ff9ffa4e4d2d8b3ac031db5e9f2e46d300c7eeb171f7f5e99d8b0767e1b59325042df2af49f494e26030fd2e469b1050546ea3299d55693d4ec9949d79f28ded7d4490e900e1ace220b5ae2a744bd603b2b95721c96117ad38fe361827f4832d2c08f7e280f8eaa180164c521fb0daec574afda7e312f638819e244403d4bcd7b8f4eb11455c1ae6007000d7f7a4705186865a82f5767ceaa6a52d68545f88b9b047351030e3d48c908bde0f43a80d1f1fbb0ddc20d8340442c1a020d5deef120c29812c097f3953409d1e969caa18af8d28cdccc9ead5423dc46101b964d59f8988d6060b6c75c3e0afd89ff1331d5efb1111b279cbff3f95f1deb1faf0abe0ea460d15466bfe2b2077774243bd91e37846cc594b9ecc8811cab54dce7830276a7a180671d2668f40d547e22ad320a7436330d80c8ae7763ab89a99311f3d0eb9042dbabd2e4fbc9078b3c01942c84a763a150483622798fac7807817cfb26013b290c232639ab18019a201384f370926bacf2865cc5288735b4c18e619a498c7f2d842760d641780a91f7c73e02717b3c4feffa33b0b7c629d04aa54389426a084c563c1d0886e805d8a1f912000ca727ba105d748fde66a9fde0c316fe3af322030d851ad120010e2038041e71d299050e774805a9fc0b83e8c18d87773dfb4d9f894907e6509f07e385ea87fe2b9ca506509e73d9f7efa8fa390d3f25981b4b4aa1c05700c6b002d997b3762dae8d88cddfb4395e2cd86f81e11af7a628b48856c553a998702405506e50886c59d759e21d9c552cc4fca6bae080803fbc6fae27398dacef0b130104a32d1ee045130af9a985bfcdf290ad6ae8e508826be6f570651d21ecdbb40a4018c3875b515b77da2f4aa7403475e4cca80823528e367f4ca34eba5233cc0a1dc358b066eb5c8fe2001d5725fac2b52c2f0a103db56a09b28d92debf2f3e0a257da876c1dfd210d9d4f3a9af729f4282e166cc214fc41334925b4051f6b2063456dc035dd838147611f2e4d5b82267978d123d89c10358a9f584dee665bb0c8394fc59390c5ae5fb54a23c719286bd0f4f952ad88eb4b23981c671e3748d07a7b09ed205a61daa2eeebbbcc19067f4649f8df516be532925caf6e95f06dd09b192a0c288c42027a5715d964d1e7af9c947cfdfbeddaa70ea200a00e860da0d93107bc70e4d48fb817c14503483e8fa2f280e77cb992a3468cc3d43cb66d80ee849850bf56142ca2319f359a86c6f7e983a5daf3e5e9f89c42c90e4a6b03b039f2a79b1fe5420b64740726f4d2c6daaf318df84ae0ec9a84ab06f5d2271950d8cd2a1345d7af83de2e00ec65d8c026e30f5eeade5b74da72dfc9ca40d441506f6adf1bb89a0259dbc5d7dade438c95170dadf44fda8075e60d23e5866bfe90b98f924ad733c7c30f209895f60c5f4ffe76cff5c941960bd44e733a317b99e06383139cdae54807ae41b428bd1765033d55adbfc0063ccf97b485ee23f8a9b0dbb4345ab45fa58b84c106ba74c526e68a322f00cd1dd72abed4d3f924fd83f03c2a35d6d283ef7695c0c6f8b82027aee17a26c944f339a32d1b2017841374a0ecb50834e97339c40758be244001d47b0e3a1107b9d66e375009c7663f8a92c0bb5c26ff75810f2ae66fdcc19294cd52f163cd774ada541bf26035f61a24bbd0a9ee8170ca08eb9d4ba2d09d2a715b40b96455da10025ab6c1ae6300be8acf10d0f0c0073f6813c282972a262dc54b9ac1d08427d166e8addbf091e625b15b2046a9cebfdd94bf8d6d4698e8baa5f3e24bfd160ca2de6b4eff26883b6e2a00603a35b4df402c4a198fd98b22ea7f696a23116d77b9cfa99d60c234683b825b10d2bf2cdd9f0e2a30bae75cb9089b0dde821c5f90b6913e1e2d68def556fda3205c45b5b9a8944f78f87a75da46114dd8c77f38927b43a33095e5d0e98316c2e0a7dafad03b17abe6e389c50998fa0e0fb12ee3fde140cc7a23e5bcf9c1071500acb317466ce01a8651f7463c8d20647e5a124a804e3a97ca1d55087beb1ff330bfa7f397aa6a1715927610a2b1aed02c2e92482128e278d2713886adcb64a140f7e5c57efbbbb68b158c53b8a2beb4f3c0f13cf987e3a1c5632d472a7f1ddfe0de3c53c7f5a538c405d9e2c95cedb1191ff7a6850f8f9715bb794fdfeed59f507e2fd2c41b1bb2919a0e5cdc59b52a250c36976fa3e9811079393bebb3642b401e3938959a8df30075fa0c0c57165fc8b35ec3e239f47014de72b87683de8ba018c056c6c893f00d357cd4467988848b22c91afe60b183b6c37685f04527ea904df967b2c3258313f4f81d8a1813510debe7492eed135faedb7130cba6eeb990b77bd4fe22757a743f4520f013156491391bb3c19cc8475cfe030897e53ecb302f4c97add4200febfe733e658db4cd4741b540666f2e016a6b1ada2d509fb0501ff8578a815a14b96844fb81f7539f2984295c7b31f94178150fb44a8a55d5c0dd3818c38ff0be90eeae8fda61f6143fc76a471d468e45d99d0a046746a3d9509557c1a959c1a92a4ac1ee086ee9aa21a21b43fc9e79e1a35727e4d62094f8a048a0f720cb79e497a9c0f6bcb68938bdd8d7718ca0003047b5ddbf35d34e1fc0f5a28083d31fc47936e7781b55fd4b2524ff4d1cc83f8f0f2b7237211fb0adb068e1bc2831493da9700637209df2251f219e1cd27e70e027a5433b74963400a09ac608fb93e8fcfe0e3ef34d44eff60828a3ca8b3981f399890cbc982848b8e0699070f71d1be6dfd76be59d0c9e9968988ecd22edb0d8df06257d61388bac809d0ba5a2fd3844399413d51a722c300191334c101a2c88eafad47de62dfefaa0595355a363d47961f3cd6129eb1e66455fed5a8957086e34c71dd610dac384d0eddf71b2a10151c8cf82e0bb1b42d9f0b9c478691eb0a4dbc7d79443bfc649804dd0bc7f1a85a5f89f5bba765b6e0d0458127f5f3a4a225d2d5e865741d0a9e02b1000da29bb224eac1546a6cd503576afd700fb0846bfe65ce3d2a6cae5598016a3cf829096fb864ee804a6240ede629d61c5c217b4aa445dd7f07053aa4050f29e9b09f5a4ff94f60d86e165466b61814e959e34ecf10afde6eaa2e316ae702a555bd1951c7c3aab61f846bde8f51916b82cebf3053cc78675dfb2b169f980b3a14615996583a3772f4cba741c8c575a1aa6dcb15a72d41c987aa5b029701035221a595c2f9bf9a91b8ba4fa86414432caed530760eac1015d927309c97170472fa19a0ef285b5d52e4a17685a9165bcd33431bb55fe602eb4ec922f1faa1079688e7e9f20df60ccf4f4877bf742c8f4c0912c8d980c294b4f886d81dbb180cf0f04fd72008cb5928b5544fbc3dc6701488218edc0da1f4c28f36bfbc59b30cfe22a4372f3fa47bd5ded968eaa23d60c9d8a18cc6524fb991122bb275ccc505d4efd67d8deccdee42fb0585d30acd1849833a868f2f73f320332b31d1a69c03f58209d8f472dd3684e32f26c2b9c44f06193c05def9951e93bd6668f2e562041ffde27e88235d17d4e57c0ff9bfa72051e512c7493ffd35a860056c13ac9e06ff463ae3d7d72d41c483df6bc423b668f10788bb618d95e6ce58ed838b873b065685351265c4f52e5633a8151c33840ecf4186249283aa2c7d936b950784fd0e048e8dfd1e67e89fba572c7dcbf0ba04587fafc3f0866bd15f2bac0e7db6730ec60e0e7504a3d71a9e735a6a433b516032ce269872d4a0f7cdd9475d88701208b359f00248926a7b1f61c9a7aaf427a44b7312775742ab44c8fbced073af7a08f9dc76c1e8c8f2b7a65ce33f7bfae7ce02c0065e8fddff242f68939822f366055b274e9468c4032e16eb08b740932c8559111ecbae57779185e56ef963a6490c246efdfcb5e63a255119439caa5c69a7fefb7fc054528751d49c3bff5adbba072363b07fedf6a9e43ae3299245b1bc77550f47a824507dc5545acfe11b3f3101412eb958e3afc56c28918a0beb9258d2cd5c71f93b337a21811fa4706a76a50a296f73579af4467aacc0e50f7194d8b6e34b492e398359a9a3a80c52f06c650dbb4be652c879bc2e9ebb407420baadb188ed707480811bf35c3a4aedea51160b7f224a347c14af0ee23c6f80fe9c0d820a7acc0628a970cdb6893ea419a112030e988336fe40e2a9fe359cc8dfb136c361f7f7341c0989931b34bc8eb50e7709ac98377c17a722f5c26fea14deaf9882c5d14d6b92b098d9e0b7880af987900835f42601ecc64043759628529f466cdea72224326fff5053cd0b9626c52a6c09af937f38acb927b275ba1aa7bb2353a080344b6d5e5f89c28337bcb1be5c00015cfed69f27cdccb927b18d097225466aa8c696fe1f3d01de4a323775c9995205523ecb64302ecef2e6975d2b64ffc521a638d43b6919b323448519851a203b02d2f5bbdba5f68c792d04b81979b9ede268b893f46ce4f58aaf3edc372272b60d3a0d5a91fe6d3f7b3352f5bea51386509d29354cfe414d8fc469f2bb4a7427031fe6559d162966d449eba405bdae98cbe9d4452963a98a5d67f2ad35ade90d05f28b85ccf70bc6445ac512ef2304c82ef8a7037e7e4388c08ae0666d4f743a05aa83d39d77ba5b797a754cea75d63a5bc330894a393e081bc4d5ad640aa8cb0a7c010d5697f40a06feb383ad037ea04608ac7a3224c5cae797c5c64277cf19019ddec976bdad1c9d7163ffcecf64d8781745366086392c29ab641815de0929081612530f3496ef420420efd7d7aa5b71688cf64ff7c1cf6429fbfffc67c92204fe822a505ec0f10cb1944e630bd092b2aa7f5f6f566221fe39f6082c4def8b05ea9600c148319b6840c1d27325e53b61b4c6cc9d8245624868b7234785620208a874ac81b32dea21b58316a3ab5857f63f81ea09218812a69123580ebeb31a0e71d451fd709a0c7d2bcf1fbac3636815189a696eab6d573cd13af585fd7c110eb4297c8e9a458788c65b7dc364ade2d5c13133300b1a7ebf1f6e247810b1250f818ceabbf1cc1cf430d7a60bfd32da0ad972e944a503f1137fc4cb48529e69019f5e6149385a9c6b18a487774b03a0bea621c89c9645b95f991643d1a343040424d14bc4dd23f5b0e5b684b7c853a2c9bea1675747356ace0ee5557ce9de6b0637a45bad7c44e89ef2af803269c1a3689e46eabb274c0d89d9f86471a0f8bb07ec24867751395c5c2f5eafe7611b417b00c7c67c2794248d5858ed2f79e0f40d60db8c8dfb36f7d0767021150dbbba1565f43fce00a493969aedfb238982f500af71068fa8fe7d70b074d7ae2de41bfab70e6f0e4b8d8eb5b07bc154d93de10a83a92bf652f1e9b9b3cece764494188669587c3e2cdc4c1d66d3eda1be03180f17a2c46900eb3016b20d8e4851ec4a37687efd84e1e8700a7db501f4edfc3b0e6ccdc169f749f70aafd17cbc05349224f9ce46f5a35782451c89d02ed492eb066fc4a636eaa09d68ccdfa77f30528f2f6f4f62eb93ec5d29651dd06ca5032301b6c94694ba20a77b955f69ed8f4697ac54d060f9aa9f4f57aa4a8d3a1cdff603dc7b353a4d766b365188f7d811f44b3edfe9c17a8ebd1e2c5296375a2cfbaf0960857de43e325124d2f96981d39e5aaf661283695b38bc265703109fe6e8920cdd68c21e9f19033745ea7285b4e151e887c4981eb72807384efadb5bbe5df80b9af204fd81c2a982658cb286e2cd856bed21b4ca60452f0e8b3ce5cb4824ae0acf1c231b63ee9f5a637ec7f9203e3f2c9c4df0b9f5ed9c4446ddc421f45a0d01f2f85fa659baa852c1408a9a7626ef196f5285e26df56c994608b8d19081b00d0312eb58500a723307fd96cc2c06f25ad76df6ddd2e760daa898207d9b19ea00ad503b951f434e4df3a942b9124ffca3df5b09b58904ec308312cce2c3d79b0e42d2c6b41d13befa7d88baad8ff9c4d215571c3558f374d51d991b4f90e2380acc2325e33b01bbb0965f71d4f8af846823550a380811b3b6d5417d5219d37707e970faada134f1a5164faac31b85220bf9f13e4f55dd47fe7ff0a50ec5648a0e97f81411bc0d906ad3860b1d60ea204fa71c2b5d3a1be07719b19b246083d70fbe78d9272de259dc106b1e89fc136a2081268f26b14f52d8aae7e9c17ebab90b957275c8c3e18cd7f2340d7c7c44b33e3179a50d1631949381545fd6f7d9db06e1731c0107c6a8d6c45fc13aab63093646d2827fba876b9862cf5090945faa08812081a24128404e8699814c93ea8e60545017998c332fdbea1b8b66c42edd0f4e54903837dd412a0166413f7d8be39dd7681b5735e9ea6ea647286b91b655024babd354ad65821150b3d2e0eb4fc0e1d289774694969fe88c5020612787480c9dd3969c9572669fe4595f2fa438f04477dbd58febe2539cc4ac5d4274a62d0c83fe2730de0fda01ad8db365931774a7402b2f7c44667c7d483dde3e43b3b001be8371e4cf216fa9d302e69f38bf9d20ed96a2faeb8a29cdc1e406be93673100c61d2910d411203aa336dbd4ead4030d465ce6d3de85e7f0a706d49aa71e560e5c6c7b9d2b179c3055f3c042ba734612327f320c6f197f0741eda5b75ee78206111d25e8d26348b47cb18ff95ab8a36f955b5e3fbcdf5d1aa163d1bb5de0ce07cae48152de5fc40659af03a0c174f90d4d671d20a1203d8d120674b9aae0d90ca13dc4956d97bf2120e5af6c351c73c8f61316f0def30bf38ba723b7932dca016a1048d0630bc6be023af67114cbf39d110748ca53edce087ae8866bd95e530e05293f2a0e745aa970a7afe438896ab7bcd391471e9153d97f8523fd24c5ae079a5b94ca377f8b4d79b8adf03eedfb0c21d5879235d5dadbf873c2ce8419280b0eb5dc3101b57cc02b1053c6d89212f06ada893ec3b366c8964a0778771de50558a0932757f4c30355116323aba76ef07c88b244b62ad72f701d1f0d67bc94086f8a7996be1ab51ebaa42377816f5ca71535f626fb2b42f5314cfd054ff7aa02f7e35991027b83b56dbc87474e69fddf6ed8526c6a386df897dc909bdb235109d89e4a5300ebc0e8849bac0ed42a074d61208edf57a43b6cdf28cd755d713a0e56278f115c514d3be3e4e6ddf50bcac586d17a47884dc0a7e01c4ff208201c0e2e800b596ede2596256fa5e077e4f81cbd2ba4e8db9b468bb74f7ff7b3c2a60d6ad6c63df2288ec85b88723dd81ba74a0ffa4ad5760b4e7e47231b1ece3609029789491ffdb9f7b5f0c5021ee0dab038292a1224e65578c395a97dea68ae72017ac5a90ea567f344e107a1cfda3b1cf407a89527af8c58b582df9ba9704fb60731bbe8a94cff65c31aa431c61f915a7672fed6087fd6d3fbf79c0ba83da6f40e14857e82d3c8fc079e7557e5b766fd25505f4e87f05487124a78cc0d07b1700c30c21c6ff456355057b8850fa81952287b44a92a8271997702fdfdafdc90420c243ff528e051585ca128bff9312f707ba3dd952e73fd8d6dc4585e78a0c3b80e34abf1c741be2fbcc999eac12234128cf02a17a02e7027e1eb7b74523e63ea0c12f783b94064fcbd5b983e31851996d76471e1e59c104f43ad2df7d72691b500319799507a297bf03770c1ae6ba2cfe7a4b1fdde035be63d057cfd7c4fb47305f42c14085edc313c28384cc2f04847eca5f2bf4b1d131cd40bb29f29c6ec1007f47f9c91d4c28322bcab879941e38c52bd66313b0cfd7a2c170d3f7a084de7065a7a2499fb63f9b5a749fb1b196a065e2f47eed3ca9e28d25d1a795d07b65203c57aa5d52438e4ad07e970e1cbdecce369aa024b4a306de8ee5024a827f59b04 false +check_ring_signature b49397eefca96e79d0111246f8e3e8b1fb05b75a74399cb4ba2740e22be4fc55 9d547341da4617f695a4f699a48f2fbe49a9ff88f30aeea2d4e838d1158f5a54 5 7f796c3226f908092f8615ecda84ac2e084acdc45df8f2c294a192a11e6b9cae 415ba846755fafcbebd4ac6ebe423199edd8583e65f4bb6a59cc04c24adc65f0 1618e7b17c1f7ae5c8af44805174a8126b5b3e4981c178f784b925887883c9b6 c00a52cfbf2eb3958b6c50aad7634fea66c710ce1f4666f362b4a4e2c7ec8403 0ef7663e110f4316976a27571449c8748650647abf95fb917a4ffb562d8bff8f 8567bf565ae857c596ab38c24109fde342e178cd7d696edcd7e8d9ff2a2fd608e043308ad841b72d5af11fd1ae3f21c16303e728e201f4e82f50912503d818094cd3f9a67435c6b674313623fe489c32758c28a7ab7dbf526b4226feeffb7005b7563c5417f4aa7886781b244f3cdd616e1fc966b76ea0fce90f1b56dcdd7c02f161e1b0283fe9dbc13435d5082a1bede3a1bf48358ae19638455a3792daaa0931f7e3b23c30fc971fcf57b51d91bf4bc73daf5f248e1d1b23da0d2ca5c755049d29b7efd3eaa73dca7d3a14253d381755aa178e2baf08d9548775aef50f2d0bca459a8860907d56b6f5fa24df496bd499977c892e4eeb9c556d82155ba7280d0dd7f0bb90b5c4b3a4d4e3de177393f3e1402177c3dfbd579a0375045ee9700094d224d112afe796b7e16d8d3e364c1f9847a05f4822361f645937e0a15dbf05 false +check_ring_signature e6fb788e5c720d35a2bcd228dc251c9da3816f1ec3a3449ed01ce3e9593ab271 217cbd97255faaf5c66eb9abecb99542060803e626d94895dff36cc720979c70 4 acd63f8452f67643f0281e89ae99547ad4e4cf5278223fa591b6b5436dfa43a4 ec61e0d04b05c953ccabfc0148311a8cd1d13d798d42744566ccbe1a1f7c9578 dd2e375100731334fa0b6bc9b2fdb6c1a9ba5319debeca6665e6bdb017d33825 7936f4c47b3b59aa95b5636a0c1acc8e4026488ea06dd177a2da09fb9b1650ae 5c9ce85fa5f3f4cd44818a756782d0241d479a9ed45aeb80de4fe44b22ca3c076017ec09677c9a3042379c6ec9521be268fd743308e32d28cde999ce9a8b2104b3d46eac1fa5f0d6860f9995bd9de03e03b0c6853990507a27bd8a8c5fa93406084643e8ef7647a5720ae1833c964dab17f9503097e156189d112fe9fad4bb0e0b5c088f9db6e9375947f8bf7eca9531cd3e20b7918e9bf8b9532c21369a4d0551f35c38e48acd3fd0e16a0e30d03e424f2f5812767c256e53fe84640af7e8056080bf7392e2aa29b4a2230fb6bbf24e4a1c4553c2f23494a6d7deb2e9d69305700391e1fc03b5123c6953ca302bcffa9fa03f084aa720ab1ba245e0a528be0d false +check_ring_signature 697ff7f5beebf329d7927b1d8397074d051a6f7f6d3663b20a5e153313728255 ba49ccca5ce07109a35bae96665fc0d9bbd954ebd2a39cf00b84078b7b6d868a 6 4da47b0b4c3d2c4bc0ae47f670f821e3194f40675ced0d33e9fba0871ee35504 cd143cc0af80c25bc18d3c6df26477ee7346b98bcf0f0bfdbc95de559299d6ec ca78cd527c6bd32be9b3b9230b9f84ccc9d6bfef8072f143db401e9272e07332 0dcebc8b5a4197784c8b866ea3f004697aa338df021b91948f538c50732e377b 2b3b58f04023e1c6c211597bb3856693a17b1df618c143bf19a2c864c18452ba ef375c5cf4054712d600916a155dc9f26325ec050e1ce566da2e99ae1a4e80e3 d5ab122a81a5e00d3dfd422f5f0a8f5e81969961cb20ffa5d38079f955cbd2082ae38117668a88aa3995ea2ed355b8c72421cf21a7973b94e9579917204ae40535fa52c06530d58f683c2dcd2ed190ec738f30b671e1b734bd2395aaca24600a0bae2b5ecb864c95ca52fbea894418133b0b0b2761c29d629aece45461d7d20c26b952ceaf0e0cf6012b81f92fc1ad331c61461725a451d14ef91e466fc9a406ab86d931ffb68ffb697c5a9e8b627bb39d170ca77e128f0e3dbb126e91f7230c0ba9e3976c25fa4c26978a4c2759bb0f2aaf1c9634962f2eb9dc5d59b570d40233fbb93e3e31c76094578e05999055b53f983f470a3e79043a07f123c9e87407f506bfb9ed42ba308c8ed7d22e5d46d9697b126fb9cce8c5d3e3182f076e770037b8178c462a939f1c3ac71f5423d5cbea65ddca2269f2cd4b7a32acddfdb20b08750d9cc087e1c097d96535a135843ae0082fe107fc3aebdfceed4ba293090e831d914b404ea5a2e598b05ee374fc5e39bcec5fe5fc52acbf63ccf8ac2d540f true +check_ring_signature 43cf8f4f25cf193e721fb64bb5702f010c154582b36fd41496584084ce3810da e4884799f7d4439ba9e6af8411f43d9717b2658b5dd21fdf9ff11434f0f52a07 4 3e9fa071a09c22a4dbdebf5764598d85cdf5c87b341bbbf1d7a7cbd78178740e 150d32ca7746cb5c20b0922ddbd0aa0f5afc170f94d6635060b3bab55fa47071 c157ab96c6013bfa7a7d59df50b7127f2e89d43a4eb314d6858fd4ed19f6796f c7ed7db52200ea239f3d33413df1dea0580d5484217d3f787a621c01b8c1e51b 57405c09a2bad425f416c04efcdede516645a862cde8f14c93c01d2a2d213907394a08cb460ef674cd21527c14aaa14dd7a2226edb1f71b0acdcad8b6356dd000dbf29d2aa43310010fae08d600c36d1514d7e8ef706f33eb271095701249c09ad064dc48a8efee1f74f3eaa274b074958b3bd6d610d7ad71ddd5e206f8881051687ab44df912b7e41976507b19a32a84e778bcefb59103892b6cb2471495b078f94c801e92718649492a6fc10094940e2df277eda975827b59c491c41e975017cbf6237a3bbf11ec82deadb66ab480f4a8336acd897f5b1648d3905f60de803b14b7472462e945ead00bb6a076e09c2c7accb665dd894d489a55260e0bed70f false +check_ring_signature 8785a5f31e910ec5405cab71a27a4d97d09e516bc85d1f3140f35e94fb1d52b1 cf4f45ad597a797b86db60a749d7080ea2b086bdf23bd82307ac0dc11a273a11 17 fb8ea0d7946b31636ea32b1e00987630f1f6624f978e26c617b6d1c43d211f9a d2a757e49914295f60fd8bcbc5eb89352ef156f68cc78f8f80227ceca1acf261 fd5b79beb0a4204a3ade8ea15b8866297fc2662d2c93813f4de2de80f918bada ea641151cca505c525846fe9242e3fd30d4ea5dcd829ba26e566ba84b786c7ce 9b7e29c03d1fd787ae644c5be713a032e744e13dad1a62ca629b25cb3376eda9 2a07c8a7813ae7bde8330a1fc2acb58c9f572608c92ee3bb25e72d5cbbc4d3d0 ade3de81b548d97e66fd1553021433e141cfc81652c02e33c8c94b015b85f36f ac0a6d6dfe892db794c5af5ab162a04890ce0a5eacf4b88e479d014ae085518b 857a97636a82c3deabbcb3f231cdbc3b5dad2b20b070197b41aaec14fc7e95ab 0f1014ab4e91fab8346eebe3db2219de0e62b7dae509b2d68053d54de5952730 ef5d9d69cffc1ecd74c9e2db05159397bbde839a4e5d1a5b11aa6cd942e66022 0766ab026c13eff3c392bfb552e9ffd97aa9b0b21f4bd1a045ff2aad302f2140 e17098ba95c6d49634fd5449466b54d867e4b121340ec989dbe0f85172e808e3 6879127ca00ee583e0b35a65ec0d28a09201b8d14daeab17202bcc20183923b4 357a73c4da84b5aea60739c9848f57278a4084a47e0c7b427ccfb3a4234d642a cd3194895c0ada51b5e04db2a0129df5664facb483e53ea19ab403e87c7b7cdb 7e2fb89b27e8e9182b1898e2d0e91473705ca47fb10ba872d8e36e924077c027 e65d7104e66fffb178cd6bef380afe39087c52a4320a5a5de68455e88e436108b2b7c98615b76d650c2210fd4dd13a85067e47d8b168394e599d65de983bd3007ac3a628705a84d4b4762b51f3230db53ee65994ec38463b9cdd6a042d4c580e71841e68e5c0be5e646406dd493e81b13d6c2ad217581cd79572bbec80b39e06f65f0ecc9a71a6f4e10ca9a8c162f08a60baa934cd53f4a37ff69acbf01edb09cd61afc28923fa895e3a0ef1495be57e337fd0b0cac11bb9f6022afe67c8a3067a9c73866735e3f907a26e1d7c746d57945d0091fb7761e96a8ec097be04d60c41bb9ea408dc4a3402be7f60acad162826c121ed83514d3044e81b39b8d4e008f46be1223ecfd1a262502d07c7d67b71f5048876d9dfc96302fa8a1a47f06503a341db75f1d3d34daf097acc947b03f3a0bd2aa12e0f6a3d447b9be7e3ee2804f073c8df6163751d91ee69657d091bf52153c38ff26c5890b4c515423731fb0c6c52b33b0491ea425824efce7361d2f5e2c3c8b8574de6cc11f0582ebba51c0fa06e241bd7423c1c4d238944e4c8dbc41618ff828d7c60adf5b8cec7d3a6700fef2c62a59e45c53ed81d4d611bf0fc2e7805717e22cb6d2c71c5b699ce4be3051afc76a0980d95b9a47005ffc91b6c60ce8a492e33fa7a4c9583e579e28b3b0d7cbed3678e4c9e3daaa2b2f8ea1a35b2743a982a0f8fbe060456d353238ac3001066f602ccb28e01e0cbaa6465b84fee566bddcbc3b81f63728a841fbd2fbb04d0b58e5cada3baf8f55fdf332a3683f5b8f66a39205abaf815c4c70e17c28a0df62eb172a44d5e1bdf6bf527c0ae7694143ed7bc437908b2393370e47c89cf0ca5ed4b3128263bd363c9ca102b4a4843f25c7242186e2ee964aede1dba87e601fd604e152e80fc23c7d0df40c9dec94189709f5b47d5002bcb004fa620f84c053b3ee08cd13b0414e108e9edd4e65a121022602f505ecf035b67accbeeeba107cf407b920cd45c599c3bef88af4487141a18a374790f9a27876916a93c75480b2ae44b3efd149fe00c8613ff487f2e055fe18dbe1b805e40b9d480ba7b270f03ab53fb4a206bc938bb702a79f9c0048a3582e7646c09697dedc0619058693b0b7ecd05dab5305a299836d5b08b2de50362364cbec7530c33ba411e540922a50abcd4773a5c5b568385e0a68933cf55bd31fc98a0560dd1e854ff5e89f2d23a051e1b46e3a20ff148a7eea1bcee7dac52596018bc2571af1ffef1ef96052b85079decf63f80d9047bff69a095e9f007779b3c50c97bcae4284803e976e4c569030e7b509e8cb9d77b9661ff6f207035fc6dc711511708d99697d6dba5656130063a62816f8640cf9ffbdbbd83be279bb553383bed719435c69c9558454a488e01cd2024bcaa54cff1ce8a8272791f54515eb2679a47fcf282d07147a244f52400eb8e4f81d6dad4d10f254d8be7889139ef4182a8a515697aa2262283b6e61d0680111ae7671c807e0c05b2468b73b0b42553b52986d4e7263df280378aed3c0a false +check_ring_signature 813bc20694a8e98a5bcba421bb667c482c90003397848b49a0f86e2dcb8e5955 359eb903e66d62379009c1d680bfbfaf96b5ccbd81acbd45b13954306c0a4ebe 2 2c4bb7ef52d3f8bdb295570b96b577b20c764480ff52c3d19d9b692a00910b8f 57c1b7fd187c3afdbb5c81b24414f4e1d58baf9a29e592fd305790dc709deef6 b6111f3aad4055d1a1c431096e4528cf24c46b4bcb8e9106a1bb367a50e13604fcb3b846770ba9bdf60b2b48cae2096481ccae71afb2a385fbe8cac1bb94f70df10948e11a26114ab194a28dab926ce0579eca753050835400fad9fb80ff410ffad9f0efd0c60d83e4dc02024ddc6c2f0931726ef059a7dabc81c468846d8d09 true +check_ring_signature 08d8361637d826f8d2fa4eaceb097bebe5f558907200b8b28236e2475a5b84cf 3a9bd67545852a12d7193d6416871cb123be6731b11ee27488ad9f66ec9707d4 2 002926e00f711d29b075c733de87ac695eb043f532d39d127bd1ce9124fc78d7 8319c88bed8f70ab9ac37e733d7f1f9a789975ec3207f741a500dd6e1a4be81e 9fa1a5e41452a777a4aad86c2e3ff540d0cf41bbd87a9565c343d4e330bc5cf19ca2d5282a543017bb567d236f56f506bc46ec243b28bdba35e7b32ce2d32c061181a829c074964196e2c2c475e44594560ba278968c8dff6124abb495daa0361c1935d9b71795cb57e1e21272a33dc769f9ffd5107483cdc5cbb679e1d65401 false +check_ring_signature 80be8c9978521e27b1406df69f9cd5d1e4dca72cda82ff0ad9029ba5afe7bed0 61b819bff7d1e8f4544ac02efb3658b232da66e5ce039d0d1149c1e3c580456c 26 65f071736087f988d2307306f6b4f0a5e604079a1a8aa95b77d38db5b89a2d9b 796551a88aa683db8be2088db68c48ab18bbee36805b7b88cd5e2c3d272dec6c 54450c068b94698993447219453f72d21fde9cbd286c80afa5f70dd30133e73a 1990987e64f84b6c1a48362107c3d4a2c3cf3891a754bdd97fe9c42e86c4ed9c 1b84b6a1248325454dff78ff2cdec6be97c4e726d14d8b1fcda30ccc0b8ae7df 7f777000a53f46f1d85984f6c48f7e531b0e8c0d52dbca119c36581ee6e7439e 5344b76d4b61beb667c92630089ad645964c7ef0417a3185225a6254d57d591f f4c7c9ffc7322bef306f6874b203a51ef3920dedf3deea1afaa495946e71c411 44008da8c2967c77960e3cd50fcd46dcb630f771c70d4c69f035e58cba563933 1417c469cdffdc2edd76d89ea8daa8b06de36096d20cd2426e58a52c9a0e48b9 cf224501a33f4f47e6a4451c5aa6bf929a37df985ed701952a81af3077782a0f adb847ac1b3a09855716658a51370bde031c2561a936e2aaacda37c1dd20d46d f69e0c2f5016ed036e03b349a90f01c084989e726880f8361d5cae84e851a82b 24a7b0ca3878a12482fcb1f60ee6ce4f4cd47597bf9cd22f5fb1e5685d633977 fda6547074719fa51da69a8ff67a9068cdfec083e122ed62f68b61e42b7d5a1e 872724783d86da998b1fd93d4193d0ce19177cc71ddae5d760635fee71d44808 bd14fc9b19fb0919e3e305439e84f499b9a14ffb543475b5350e6a7dc7f6945c 83d67f918e99ae69e8552435d347fe8a6b52cd806819f882526699ac0263c6e7 0c9dc06eabdd085d22769a7fdd52e0fbad04f69e3d9750be3423d9fffd4fe251 ede77348949a17419369c58cbcb991382bc23f5aa36f5f19b2810aeb4e8bc5fd 08186804a3dd028b4f444900cf3e091091ae1d2ca9253167e6f547281c1ec96b c0ee04a6a787ff6ff69175dc0369c441f9c0106b1b9966e9f85b9442c8d6daf1 c7ab0123cd8583eded55a897c7cd36a5b8bea937002c64ec2c1883b0d227b95d 1c21febf5dce40b32bfcbe6327de0a38918fff2d620e6caf51ebdcb9d037f777 78ddf334079af4ddf23230870dc395823e4bce47209f79ce6b4779407fdfb9f6 4fe9e395cd3782d0f4082a9beb8cc0b62970f515ab965b9a3ab95f219e6827d1 0cf397a96133b5b3523669b12041a25589cc51c62c4eebf1b4bb3cbb1d25d30a9a801166015e95e677e714f13d73cfe6a8b759ac25c42ea8375ad93daf545106fbb97abe829cfca5d0b6f1ffcebc47c3ed54e68149fc311099705a7fa869ed0959d12034d2e8a10d5a79025323edf37728cd2a1c8615b927f9f82f4fdca28a0ff95d1630336f83d8d5c4f90bf0fc1fdd5228142248f5eb8ade28c668de9a350a05af69d1985462d7caa46e4dff2446d99f9e3243a4487d84ac772871baf2df015a68d4a9cbc9a07a934b857abe3a6f39f1938e63accbd2c854f6b41a4070320262002985658b6bec0a5280762171f0207b66433a5b052d7922ede5c2846b360b33dafd2b5842071a4284d4ca91e3b0f63eb622505c87fdd0531a8caa30da4a0a0c97d90f206477616a85311934405047abdd978f1d97231f6c8cf2cdcedad204359c07036aab561b41acfc7744527492bb027ce2efbb84afb978848430beef033f034e5812dfbb3a74e500db746b04a26e2ae532f6a68ad9518142bb8ee568042c39223dd0e5df10f393a1a85d911fa8c55928743706ab1a0e0a12731afbaf0a46b5312ea13114b8d5bf943059aadcb08cf9494921996c73c053017a43c44d08b33922d1b719dadef426bb96673c04ebdccffbb3946ea41e7ecb791f732b7a0919a067b974f73fd0845a2597bb0d425182f7a10aea4b194cdef097d45175ea06a2d7e8c76693e935f70727d2eff7846f36845dd41ed15ba2878a902100e95e0eba80a4199133e39539006fa58e7aa4d33a3632eaf21cbba6ebfe188d68df240fa3e9307112af75434f35233d1c4224715ac29361a384a8fda40c7910cb0eb00c2f987b070b1ed8d4412e8e2b3f901c6bed54b4f6b0ad042302f35b50532c7f0502cde0ffe1ec9ded2196aa1be1a861a2efa6ea6cca0dc29cb2cf23234a491b0126e0181348c8f47288fc04fe04ceff4693346e76b753f180770512bbcb4aa10d6bae23f73832677191e1b7beb1db2d494f677569e7ab011b334127f998daaa0afea6cdd1872ac7a02cfd854ae75de27e2aaac9c8b87e920c68ad59dae2ddae0d5ea1c17b22db6c511167011f11581cf7a7a9386c4c51f6a34dd977da134f300c6f2785678d15b4efa4ae16500d176b954eb8383933722d6d3e486258a70f7a0dfa118201a67a65a2d9fdf544de9991d44e21e125a934348cece6479d13f5510871416bcc669962a82b7055d54e58e6db8ae4ebefb5c8cd199cee30ea1665050333c8fd5e21a62ca87558570172e8141e3a3c9800092a40feb25fa576a3424b02462d433801406eef29caf111190def09a851b4264475b7082a6925d7bfdae607930a6d4c8811085209039d54d603150d07ab5978d7df92c319dbda1b752d7d08688f18afd0b365b5d4ece67ca0653ccf42d995cf29c1ed8bec11ceec58e4900b8bfa1c18eaca10c8cf9c1172ece3a40c3bdb316ab5facf3339dd6a9f1a5d25058d991684417aae5d45535bedbb45c1a6e719990d2477cf0ba51fa1e4b8e6ca0621308abbb8c0ca2a8912cb4e99c4c163d13662b8afb88d205b42f51646d22a090eb1c407cc81d03b2fb9cfc2620d61eabe8b40b85258d507ffaec5e568ff3608f5629a0c456d3846d3e53f63fabcd16848391c3de3e7940c88e4fb6f23845a031001bf924ee10cefa06e3971dd261c51f59f0f94f34cbe25d21db23b3004c4026fdc2c4988e939878ea64b0d51e41fe70283a0ae8898af6cd5ac6f362da60d070ed4214a8b2bc2d76b12b1c9003907f4f73070d682ba687678ed33179518b60a1ce706adb2d66663374a263a6dba38ff92e340a3cf65c21fdf8043b6d7f2390e091bc3e467c6e03afa47c41eafeccc05f57498aa22d44bf03470c30a26cf2a085e2537c24ad33c4afe954cba8d3409278f106ebe461e7e5f824a46f50a3a700c4c1ba27e5473d6414fc92108bef7084c0be6dbe2d0de24b2699fc829178bf2094561372dfc27c95fc47abfae39d3a4fd0bedb332d5fd44cd3c49706d751c490b8505146efb2e84f1be968f0a92895dc186478a588b7cd7e081e903e95cc97e01fcd3cce403831ff55d7f70c06e760bd10fa2624dd1d3bf511cec62763165900c45c287e9e03b436a58e2283f49f5877194ff593a08f690dc0843c7201953aa0d18f670bc5ec8d7e4563ed740e0e55bb85c4bb3fb87a6d8aa0e145ef7ab7144034ad959b18d3c6c8b3472d4501c9812e4aad0a295708fea1267d71d67296fd60ca9e7201d234ebb0528a2436136d076eaa9b1285ac3e962fc71463b7d2a647008872fadfe64f0f07935c19912431f89869db9965f2a856239f6a0549f94dd3e0f false +check_ring_signature 8bc878313c563a1ff67a2eaf681038c584d017f17647da5720c6d5796cd5ac83 539a964af77879a9b1c3de3c82e81774e61688270251672d51d282b9ac0be2fa 4 8735cbfba49711cabf731bbb7b3bd9bdd17a709ad9a38b6197e5d9776dffc455 1a24bdeb645f4c2635f6be39599ee77116a68dda092fa9701cb96d86398b4b32 a1f11f4bfc34c110eaae9a3ddcc3d776b4182e05ebdb28d6c9ed99719d5343f9 2d9dc5222df861805562c5151180e78636dc411b0ce6246e8b8ad1425334c956 7b4c7fad88f77cd179fe66e6f2fbd92dd27e148e0ee09c4ea73878c8741c2036c478fd26e8c35d34fc4a8ef81eac53b6ce4e3e784b6ea058940d08343fdb839c175ff0b163f7d8c63810d9097b8e55b952a1770486fea3bc9dfcc07a7755819f7756332ef44a43ffc8b96d82dd0988cb0cfb638887300ca7982b6738374b4e09b2eb41fd0c9a47afebed8f4b6f425fe966f56b847de0a2639d047488bcfc700a63d2e337de33b8c1efcedac8caab833685b605ed458d5a75d5d046d84386b608951b43f3b870d0ce479b85be0ca388d201800336994bebb0bd720d8d5eb0bf03026c4c9e4be422c36fbc2e9f212e343433f099d60f6e17b4cd59e0b0d6066f05 false +check_ring_signature efbd72cff1f51d25724f077d0eb86162ef57d78f7d8dc3b8470b3f1636a87624 0f1cc2e6037f400692b098f15316617ed4a4c2c690647a16ffb120f9d615fe6d 21 cc78f69819bbbf45a10ddc648d8771d336c05cb48db66dc7918bea0b416dea5c 2e09271dbb9218ef14342551db196194cd2b95d4a994a52b66a299e754f4eee2 bf542bfe8a19b0989c0c4564041c3c5dfd96ce5740ff5f5a67ef204c83ffaac0 b397eeb851a68b58b304d6c46f42fe4cec1c023422c695b6f229a94596ac6878 d5b64c1ccd592d6c3bb56c3ffb20da41b2c4b3a9ec5d5e64e9ef06def5f25c85 8dee498ab4bc7288bfc8edd1b7240fcbac8a539eec7947bc541efea8857acca3 051795c823ceb65be31eb215ba36bdfef0a714583c0e450fcbd8199c0a2d884c 449b6ee8d7ef32017f0dcd02d6480c6ed2d572b07fcc4ed88d85762d562d869f c6b6b4e39a9a70efb25bad813d0a7a05bcbe87780121c9f86345659be1a95fad 698a5bfead5dc09fc0f257c123e8a7d3aa3589925c9c5c2ba00438a93ea7558a 9512be60c81a79b382a7e055806873c2ea6c5ea5ff85f7fb938a90dc052d1430 406ace91f17782260a7cc46114c1ff029ed518f449689dad5242741d71edabcd d5b0f3c8514a18e7bf770823d00db50e2af5d4f9f33b59aa2b5d3102aa7dfa2e 7015c8cfed1b7c86830ed9764451923e9b1da576c25fc6e99c76c29ee185a4b7 69518b38dc091afd39b8eef19f7f090fa95a5c1634ae3753d0cd103f20eb65df f27f58c4b76f0481051a441fd6d2af091d37093023a86f5ed50f27440a959fd9 cc87455f3d628348e40d8355d0947d36e44828782a94202c756de054b3828785 a7be1bd17f3aff6af3630ea9d5bb96fd7be90a4e099e90ceb416ad476caffa4e 84902697a21df63c56e3ecf39911f232868d8410967067534acf92acdddb58cf bcd3713c43b6ee6505b3b369828d5c2ea1d95d52645ed7372e7531b023b7cc1d 57dfb6ca53a08eca4d32e728f5325746fd85c05ef58bd8be334abc3014634490 b5e1299d9b07345122d4c3b9b3bdf861432e509c44df5ac4c8c0c9893e37a10bf79b002b83e4d518598e2c3a1f98d72f44cade8cbe5b233afca65db0f81d7e0c766276d8ee392cf8b2e7b97ca719a2e1c39ecba0f95c6cda1c737e63cd08c90fcbd0b782abbfd70f1b6a91ca25ab97e190ce1b8ae68b112c3bd55d4dccfc6b0b299b79750ac2dfabf4401b6e601be8d687992bbf164c3522b1236f7695f75c0d94995cd95660dd9f0bf4f0af12b0a2750e3c33e130de95fdc2a46c5daf7ab40eb0a188a15ff80b65aadcb69f1099716effb662bb3904a573181d411384e7c0066e6ac182c0158155deb4752f2456d8638f4e25702bb6ca86f7a66ddc3faf090f2d8ce9828b65541b9f98b209944220117720638b7f1188842a62d02c87f70d1e5e07aa0e2138b65d0ea8acda19114bb44be415c4c21b0796eaa7f2f397c3dc0eab6afcf601096aac3a14fc166dad6f2b0878bd4a9c4b1fbbc89ebbf7310c3e0ce5dabefbe539458eb897951754d57dc6da1baf281d2c62f4f0d8579914d80d0bf61372e5ac6291e5a023488f3d57d50136755ac834f2ac72b7caf99e72c1a40aed68b0c3a795ae95149d3be05eed1ae3307a18919686bf8147e1b40e799465073e021a71c41abd7ba59639c61c351734917ddb7df9473577d976cbb75247b9026cf2a2b6a2bf582ffee38a3d523789741ad92641c52dfa8e5449d0ba7a434009be54f3d84a8cc53307d15146306252e0c1c5e833446d159d40d0ea4c6409b9097b9632c5d1e344317fe5206c4f68e29817bf4aba1319d0d9f99dd1f0658037071c857aeff7a1ff5a8a5aa8ae797b346fb59fe0cc0f1eb3be9daf468b58dce5069bf5fdeb6ffc73e4e1fc0285544539d0f06747c0c4fe553b32ef225f2d52350810d268d0f87ff3ebc5dee6de89b47b3b6dde52109bedb5c04043bf517c5d5c00dadd842f78abf0af86d5169b941501950d2eeff5fd002913f80bac2de387ff0113921942c870c84489f2d44d7356fe1aeff675ceec2e83a21ba5fc4cd9f11f074d80bbc9f866767453c0e6253c9a0ce92eb79a2c283626e548dd6e8a452a950105812ab0bd7b1bb88505052fae0d2ef50fa6d182d4128fc6c764501fa6aabf0e4c22f39817fbd260f5244075ac5abb0305a1040c4bb36b86a4705e2bf58dbf03c1b7d39c2444bc9522ed46cb25c63c7d3ec40a01758d4098216f60d0bffb4209eed86b407d693a4654da48831fb2a0b8bd70eabf462a52a445da89506d958b039866debd2f790ce7ae8dd5c9e9c4b329dc48c8bcba78e4e07f89b63d37ec450959cd1630c6e6e677499a5b063efc81430949c9c0127e3ae55def010f43650b032db4fb84c151419152d9709055e90f3826edc992933170b2ffc5b46736c0ad0798646be6c15cb3d4c4fb5fb70754537ff59543bdb84215fe24fa14c55e159c0fb8a1e1cec613d6f10ecc136e62a268ba07d914b9557b5547b106c86dfe306c023df493fe0f9284d5fce6adf06d89e74ebf58643ab80e11943dc098081c904b09d553b27e20f58ef6707d20695f54d41d170c3cade5a400457b7d457eccb054056aad5f9ed750b530ebfc71eefae5e2a4c37a67ab52aa4e288685ba3d1e3df501e73d7916397a2a5fac458101cc640ad65b04f67b0526b2fea643c81ba97c730b05ae40924ca1123ec1a2a8bed6a7d7e3ab085b018201697ea15c6466d4dd780b7525d2f3028058a3c96b5829a65d282252638c4c627d77fcaf47f1005714eb02ad301f54d997b6c78aa2efa3d0222af00ecd91940456a91ac7d9e7c2cb15de0c887574d02ad05c6b642ae795e5075b2aa0d4e97e40ffc24c0f4c6b885e7c5d07866e12b55efbaf342649bba5a385846881f0002d354725b858fc2d2daf3e890b false +check_ring_signature 2d6e30c2c5dd4a0e6730f24541fbb2af6364ed98d9234c419660878d61eb9dfb 56427e1e7aadb5358adb49cefea9bd8f933412f66076cc754b71ce2bb2e869ba 2 02b8f89f82756cb07b8a3d64e656fb80c3bb891805d8d145692de63b58a6f89e ce15bcc7ab046d76764c29ad08f6f1c8cc04c71e0c761c4ef2e078317d08ceea 1345cd83d4c6cfba763084fcf70b702e3cf8b22830d9658f267cae64747c9604d5adf8467471b74531faaffdfcf6f4951036bbae0d843971cd66294abda5a10b8384ca8e46967578a6ebf0c1e96b44065e1db3be1bdb14ea31cbec6e6ecf33071f08845754982a313444deee602fc1ebffbd59d37b4fb0c237b2388f84ca6f00 true +check_ring_signature d6272cab256b350250a4052d21c0b898418c15b31609f5b98e33d0ede55cb6e2 ae487c2a4e11651cae43f04e260443667f7e8fd555db8d5dd3512626ceffbf4b 6 a3f449046d9bb489ab6fad0ffafd25291fa93e45024c837928f8c8da238d017a 80b0fdb28284deabf33a58dce0548290a4db1d6556e120b880499dcbbd7c1d6c 1f789f3ac06693ae387ab04f3ecfb40b9c1cd425c50c4ebb3f7ef60ad630b7aa 8ca80560c312d1dac2206ca9baf658b6e1532d778a08860562f1fc2ddfa9e3f2 c20c5fefef58898e12159cde62d2126ca420e540e8b89346391fb943bcece475 559fbb490db3f934f5e78f6ec468a53ad75252cb663cc81f4a78b23c3188afe8 10994a74f84d5f21fc6ac70f727d8a4f990bb8fde7e97594c0be994f1dd12c0ac00c27bcff578fee8ea11e58a16312739e0a2a610bcd3bd62ad6516c4b97f60f11baf4f40ea06c82d94f1b028e4f817d028d2562a275e542ab3bf1ffebf88003dca5bda0570e0dc8a052be19bc36cce3417ba7af45199df689a99ea3ed7f9504990766ce768e413b6a9aa0e74e30c0bbb4ca6bb2989c5cbceedeba4c27a1b605f8181e616dea1d052dbf3a76b65423eab17377eec41ed6f94233ae6674b7ae022064ac882b5e691b8b67b64ab27cf066c8a0e29821f1c94861b960a6e173a6090bbe16bd17a2ef3dd1ba6845990e182f7946bd27e4a37187d242e25ae094baef8225dd81fe20dc3dff8b074f33c762a6baa5baedd6bce916086ea90f381656037b04f95ae81dfac4e80fcec6e07a11eca3a1518bfa6c01b2d662ece0dc861707287de45cb28004b5de1f205d8bc9495ed0adbd6e83f8396cb348ec2c2cf8800a1bac86fd3dad7b7ca2587acf5c15d4e78f2c42d545d92ed885cb2ec3f96d7c02 false +check_ring_signature c94736f051f210c57f266bebb26a6ae112c0f9f020ab7318c6b1595d0734c50d ff2f5341ab7beafaf97a9d83c96b32a3bcb21257fbf70f4af9c1054a4b79b0c3 6 2a4acee7c6b3630f242402fc48bcef51b1e971cf90b14fb858b104fecbd2d812 f5292e1c2cee3b59ed33bc6b0986ded8bec7126d00fb568d24bacf41a70890f5 8284740ae8c1fb0b4247708db417e9b89db9f003afc1cf29bd8215cacb780a46 5de6a62c42e8dbd3cdbd30dbe860221a681cdb64d44ab0dd94151ff4419dc3d6 7a8411c63d873408742404d49ef6b2e44e45bdf34dbfba636bdf4c8b7cbf9e5d dceb7f0e1b40d5dcf86923c465310be56a376aa3c9e37b3f0aa0dfd562654ebd 16d8ca7a85cf2a4bec0d80e5c6ffc9936198f9fb1c218e0770853b573091100144c87c020eb78babf872ab660ba0c55b9fffbcb71a316756989f72a1a5c71202d68ba35ac25e344e9bb943d0603d8adbae56eb941878dc13abd6dab7085c16046b1ec04f97ceae5d0a43d8f77780cc35ba2a39fe2d8ef66663fe515841038c0972fda89a1c54d808f510235d7795aef6124671a2a3dbf5816c0b9dbfdeb6c50fe04f543178489f041ae593f70d20ec7ca20b949e25a7a99f78e2179f3b43c4039818ba855cbe5e27c55d977e1216bf5ea82d6793a112736972430c10dec88501d4a8d757c06a983ad40d5dac9ad45a76fb8a92bcb170dfa7b24500861950c90a7c79a46d9a302ddf5cdea430c9b985c1ee4851ba84c27d83261f9a68323fb605ee101b92289ddc96a78998304ae0aa58ec9422c7f908ced5c55395198ee5220f3d45fa6458d72cc20988f695778db8b2e2d69a1eedc02443ac030b968047db04e37268248034a1fd5fdcdd5c381b54ff12e811a39c7e865e173ebcc537097f0e true +check_ring_signature 730e6404dd9dd624854182d038c72321cb57b126f6322da63b4fae5b6d2a6ffd 0c5d338d6a920ece20cfacdc21cd955cfb02d2bee8b1dd1f3a2bb0f53e3918c2 1 043906df19ccbe39ef16713e40fe99b2459717c18f8d026a4ca013d3c2f2822d e43b14b0793caafac82e5bf1fcc9cd9c28adba6d1bc7e3713ddbc0363beaf4fd372a214393d4cb7dc275d81f3c2992627f8fd02f6b7a35a2d34b9678d85627cd false +check_ring_signature c0ccfa85ca1cc1a38c283811c528cdaf37351786c4a14ea042663cfe5f3c0fb3 0a825ab7c8fa28aa5ca2c97b81739cefe159cea6016822cf99b647db6f25bf9f 109 3b72706435ea79d891d01d648c5c85b4f7cd8974f20f889ad949c4767025c563 5d79f6fe60ec15d20b7d07b7eafa074d9c60fa820ec3bb8ec7d1cdac33f05ef0 569e4cf4a63135b66ffc2dab568ed78e0bd801b8a5c7b1cc7da933dabfbd7a42 1f444372943ae3fa514064a96663a29cebd15f532c9ce4f80150cf7c53554b0e 9453e88e6bd5dc95dff13c739c6fc5b43eb2a0952b8258fde6384176a7938983 3047204478a70968ce9c8f2137cd1d7ce521942cf16fd1071c2d10526807079a c66d3db1c686ec6a85ca7eeba2a70482aeaa2dab8e78979756586f66228850bf 664bf7408d66f1491c7482bd7e171a919141054032ccf12cec7bee4dcbf20392 1cccedb6eebf5be9a64db218c3fb99156248982f21b398b6112a810c32f3a701 1b62142412fa7eb8c0addd695361346e788047e97a2c8eba85d27c6a4635555a d576754d5a01d23eaeb0404f93fdd74eeb3171c343cc1ec5cb9c7d44cf0bf7c0 4c3a856c301e08cc9bf3c43f0ff2cb43a8b63650828e39719846e5a871e06764 81da36f17e18bd121e99017618489749647d8521a14cae5241567bbcaefd479d e603716b3fda885220975422f841ff44e96331e247dfa36d45f936e9d87a34be dc15feb8f38f33369f8d9f86fe045d184c8a73df80e57607aec707682c7905d1 3d2d2d84137188da564388c9e6c29728ae0cf1645d4d75f18a2b637572acd319 83c3b9989a4c977d81d0aef4ad8f15eb31761ce7c6965cf7d07e0a24333dc53f 0ac3e9f9cecc4564c713617fa8a71d150cec2671c9054a0edd636262fda12dc3 dca90ec27a2e98ff1438518b0630e69d21fac15d6697af2b2e28efb989a88291 63a31b5c14a6b9963617c2bd3bb72085eb12f4b2bebc512fb563d2c96c431c31 ad0e594234b86ac5afebc4493178cf9cf3e0f87211de7442b8cdee6d28db3370 3a5c3e44a3284ea772701cab79f8f4115309bb03e2bd0d055a3f210fb549f8e5 251a3468d19dde1232d35e828ae7f1f08b616f9847fe6dd75a5ef0d469dc04b2 ee49524e1e583d73dd58e1c52ce89674ec8f4c7771343ed0b1cfdb9ffeea6fcc 9517d2829cec14b0595c0b08e1b9614540f0aa3402cc9488a3b4deea5c76a344 3bee6fc4f12c49f0bc1c6b510793baab4648007f9d6982839bba7a3e008de884 fdf94b3f2672fc26c3f88f843d637f3e743eba2f24284639e7b5fe0656a0821c 49a673a8fcfabadc354acc3ea82963d54a074565eedaab97faccc6f95ec08533 826d60a0a84e1fc2cc4d60cd905df0f5e1551e537aefdb63530750fe2eeb8945 7d84246cec54c12af568d4cf4808ae1486c87de8416730d58926e2fad07eb15f 37c4cda6c550550cb6582687d64310075898f18a9ea1f313e3eb7a62d99eece6 79c224740b638e924edf1834e7030eb6147ad13d583f63509e037ba47d3b67bd 11620631a6ac0a73eecb4ec5ddc65c0b02b5d43ab253f57a40ada6af25441600 2ba0f97a52aa62edea73c65c037bc7252ef40a0739bd77a791c50b6bc6a392ca fb57c930b9e19228f38f2a1aaafc5c8ed3e9938c8105ded1539d86496e487eaa 6080a20f934e93f3e6d71f249e695c35b8d0133d3de697a8d6ae23da2590162a 3d5eec6252521bae7c11b23402027745c19bbfccc26864f8063acf9cdd193d65 bc753fd4c969f28e937d3105c4ba2034feb8bf503dcd59d328d212fd7895fead b14629a0ceccd3ac130cd9e935aefff367730b139c18d1ebb416b156fb82c183 e0361735f2f0260d3519c561231c596a6bd148508fc3a43948179fe08bf36184 82fe84125d0e6bad694e2dd8b5fcebd2d34643b9264be102f6032d1f2e91dd3f a870482f48181893aab73700e7c7a5329cb6c7ab81f39438a40987ce803905d8 21514aa7cae6d2b1070d812839cf7fbc93b750cdbdef06bc9ec4404cb2d15fff e15deacc54a1404f11de2de692f4856f77bc68035efc4156263e9efd9b6a9ceb 8452bd659a6b6e632a9fa0f29f597dc335bc91d1a6d278c03068c2e672faae21 877f23f60c8b1bf93f3471376df081bf4eb168c005c31efe57e1e50b12dd5777 a7e87579694fdb588d17ce48306ba08ad064c28296cdbb94676e1926a0f5a568 bf6d3b7e26c84ffecf9ab6b5a08104dcd6d4e6a1932bef635b1a620c7e6c9f2e b826a6336a3415dd2621a5f6da7f68fabf79f9bd0d4931075e1ec2d34678fe74 8db02bf0294f3a241d69056bd49b74243f9b50d3f8670d446b320cb12668ed46 555271f9a7c3a02000304517b54967957c68556739c04df0e733ee76f6cca31e 9648f2f13f1e95e0b351fd0fbcef56866ec7a544fc031834e6ab7efc73472765 460802fb153cdbf6a3b1733d86efad54881b562b442828b23d0c898c1101bb29 b91b933fbee853d1bc09d459cfe8a5df58de0bd879b0400c2d8eeb109baec32e bbc487801a071da4aa89e3e91d9f1e2f2fb34de8aad075f8ea2596f47550c8a2 20040a4cb33a867c57212adc715e4da2a2fe44da1c3186067f8faf1a672c80c6 0e0e26c53dbb863e846f6547b602c82069f9dae306f00271e8b237025e3edbd9 aa440da6da6b72b6451dbf4a251146ef89dd9fc15d2c696e05072058b3dbabc8 f03513fe2029880a769d31fc43d0a093e90f5d7eea31634438ceb8638dd4ddf1 e51a700385b199d29c2acf619470d92220bdcf42ac224609dd4aae8f9d360086 ad83de4d3fc0a61c16a0653ff67d93ae734622359820288ae8ac9a0902bb544c 55c2f70030fa07a9f9627b5c35ce62ad50538e4c683d5a66a538111cd9a7c880 04bdf325583cb0ac870c0767e41377a6ed7de05fb00f0ccfe2a28f6b98e0cc78 d919e57b6cce9958763f825663dc6b108fcb7f77fb818499d8a15c59aaa7025a cde27d9785d768ab79b32cc70643fdcc316c281817aa9caa997c1843e939cdfc 9630e5958a90bd95acd24dd174c7f282ee2c624c936e73ee49009787785c91b0 98f01ee617d6bf0fe339615e9bbb066e21b06de2be03188f29dd2dd834f7424f 8d853f857b646c53381bd2090b420c23ee13a34621f99f1c15b9bd9d88099d14 775ba3c62c0af225ec57efb5295a852ffa3016c1126501da79104aef4dbc7b9d ccda695c8fb4ec7af2580416dea3303fe982bfc1721421831ee2182f20438196 37c4d34902962c8ebf2f82491d77292db80f30f326ad289a5789eee5a678e4cf ded8376e2e4f631f5bb649f29d12ea77f717b1fa7e39e37c9b215c5d97157076 569dd61b0af2698bb982adade25538da0404b686091c481819ac6653ab59c467 9a437d99fea73210c8a37fb1c6ec15f1109f9e468ce17554a58b63a9571c2210 c4eead6d3e1f79d6d9da1bc2350ba6f71cab486cebb12d9839b87a791c72dd04 fa63c6af64ee99e0e8e20214cdf8d1de4ec519c9366d6cb45855d88e4bf7b54c 86fbf826746fee908f2799fe83d467b065e871e609300d1cd056386da4c050fc ca5d20063f646e73c08c27b1d3f54663e01b0510ee436aa7910d64338bd4b9e4 992b29de6fe06f2ba3b839372c369cdc8e1069ed1df93eb8f57bba18bb971e28 8413fc887aecc400208302e3df94b7b8b0f4f6870f94c41b52b5e5982300fa5f 279e1bdc8fab7d363bb11cbbb721a7ded9046cd87ca304a2569596af928ce3e6 f2c3b9682cfb38e207d707d2b7e874155c495c08f96c143798addaa25946c647 5308647e8e0a5239eb54c8f5d0e3c5258cf17e6f74eaa0c1387c6ebbdb215327 2a42948ad2f63887c4c251b85677d3138debc4a6f5e4d89512c95cb8eea1977d 22754d883f63ed826918f6ccc5803e579e95d27403b78dcbc7a0a3da5908f311 50c13ac914595c23fbbe461f2348352890b858f75d4858705b247849dd82919a 11b59ff42bd392ede939e1f6d6056b4bc53ee6c946807041c554c48ecff18542 84b88d606428935a0d7e645152f66647588c2b6aeb8c0798e1730d8d67d9c8dc 241fc58a0ceb7fa5209287ce9491f2c10eb1fa0b2c4e822a158582b3eb4f366b de3521994169de7c6d3359cef8f4dfe55a4ab7106e069f6f6927b5bc789babf2 3b838815529b1903db98108da43634ed36b96c0255b99ad0ee2b5cff8b302804 bfbbd759d3cbb09c49e9820736f3e586267408151a4ed1bd164c60ad38de1647 87db1480f5a43e45d11d64136c329f25870a3fe2ac96982a033b1a78cee78d7a eb9ef40d96204c06d2e9a40db7636851aa72a621eb2560e46be6c1e9dc9aaa83 d1f2c6685d9921007825dd4bea766c7b38512f5d76832bd1821e62988aac9d94 77e8ee2d64caafec2ab7525fd4465043c0fae310411938e5a540284019e33da3 981b35ca29d75c3c0e11efe06361c8fd0b981124583f04338e01fc1ab149eebc 33a7fd6f1c38aabee88756c09dcf0fe646686a299f1f06f02198e0837e10f9d2 cfb60d556e93fdc8eaba53f1328bcf955c6fbc098f8202a714119f8b84420119 093e8b172d140084ba369f1e46d48847c90095c92f0bd3b5ddb5bf6d560231c9 e51d3966e7635b24a563bd4988ee242fc12710b49ab5c5c4ff6bf8d511312de9 e615d5328aea9a17f1963b0d150147893af65815597aa8c2ad1e643c62ff1a9c ab6c26460189e98477ef7e8e6bed67432ff4c51b33e4ba2cbab243449f0eb02b 21db55c528dffcaf5c1961d9a92758e7e10e2c850adbe7b2d021b3a2d22c172e 7e7f113a0b3be590935e3c1f8dc0d0955dd297e913ab8a4a06b4b8c9889135db 04a41ae868fae62f220793bc9bbf69c7ce2bd1aeb89273896ec6a5623c9f86aa b810ec57729d74c1e9aa668ad3268353818b42569a2709df3b7b3ede9f37affc bacf3e3ef060253a42944f2b1f290fd2c856966065470157cb8450247ec452c3 246e0f259ddfa40ae47ae7603179961232d1e3695b2de3f0c602d5403b03b73f 429325b547eceb2280d3b3b840dd747571363c37359b99cb2bc4a6077abf7a081c668d365425386d258fc82f3159795fdfe7ed40b26faa9500e5bda5e837910ca7baae963db064c85a24cb079763c37c41ed8eea9eb76aacceaa703fd29b7b68306e4d02dad9473d1ec1d034f396b59d31cf2bf1bde96e62f8fa0b2e1ddc190da18369f537c1ec110f4f3ac8f732d02173c9a11cc37e769ef552eb09c5f88003d246255c5c521e91583cd85f38e9d7e0ff0e1b473245c27cc2feb370b96d40040f9e5328a5a80e84fb0c1e289a22b3e06f7aeb237e8f7b96b9235f2d02bf5303d8a31f21f353a53a7b41c0dbd584da25896273ca1c1fcd046a90429bf126a404ccdc7ac740c7ff709703c9d63098cae26a6ebda3c781bc624fadef22a7e8880ce4c7a3eeaec9a4e6620b8ae8e8f7dfa20c0957644207c5baa6f2a8070daff80a5c1af37227dcf87a89e2c229cc52aeca8faa6cdaa4aebdfd9737dc9169e7ca0f950f6a0089d317fe029bab8a15157375467f5df6a5df7833ebc04159c5a7930401db503a63a8549530ea2b958c42161a478a8559fb5a510f4aa18974ded54702333738fe1b151807317e64bfc10276cb1d0280743045df586eb39877f7ad230319ab39ee0224d19a9478e6b5e868137c858f0c7df1a9ff56852941853c465e0a00083d3880458eaee8f55d60c5b71c72db4eaee5c6824bd8b6fdff4512365605e441d031d760af2c1461dc999022fad48c3997dfb220e241217e6d10c71c650607df8259614c99066cd5f453376e09f02f4e2c6e9a5fcc20cbd9f1031dde570179f54e76c1a3d2f3080572fc93b443beef166b1f7489d4766301584b01723f09a252e4f9c440fb10c9346352f7033053c9b2cd3df8d9caef1b6014a39f8a3c028b54afe375796c48a6ee2ed3cbbc3ff34579421930ad29ab77367f2422083f0a506c22c96cd2970ad14d0cb75706443339dbf93fb05d4f4cef5802cd29d84503bd3290f51c2fea0b657fec99783932768fb951564edc59faae53cb34c051670f988d68de07fc1c1a8f2cedcccc5b1197ce71dccec3862dd77a5cdcff5dd01f0514e3e0ead94258025c5decfbd1a35b0f3913daee377b3a961234c3e337844801cc7a255ed77a8105a0306bd559dac8be9d7f156ec2410ff757fc5376da9140011d627dc7e5802ac3bcf5d7004f27926bbcc71fdf62fe52d3e3ee31109f3e890896690d8460b326c8e24255e5b06268fa935d780b89c63b944132375121a6db02115504a6cd5c0196d20cf006acfe6d5512413fdc55ee1659fdc835440613ad0a208d7ddc1fb568494551af520020b78106d1306289be2cbd084f22a52213670d3cf08287a7efbb31f1052d098f0319f454009b61b21a749d7ea8f459f68a780be1291a91e219e357bca70389bc4756ad5628e8dddfccf774996afd665134fc0c680dd613d1896f61727b234382437b51518049f7986b06fcaa76c1fcb74e65072649c5ea217ae378675d38800d91919e936ef745b882bf1a2e0ed1cdb7bd42034a9c462aaa1b1e10f3d18d48e98c97c454cd5dbee0bce81631a6cf69329e160db8f9b4b5d03aeaa37f75239185248bdc57c9b0a8a9969e7e61377c610ff0a203ca6617e74d8739c715fb2344cfebc16f586ccc83421344f8b320b89280881409f372f523f75a6ec1b85f30cddb1dcedddb2cc3cab9c4fdd88f27584bcf752b0c526b0604a64121a5b72a40e385604688ad0504ef46d040e976f867d8bda96c09514ac34b6f274001e2a2c21bd27f401cf82d3b7d06fe4d28caa2a5726b5e4f0f128647fa2f687001ae1905bb7d5c97c7c081a08184f2bb5357f7dc02441c130f1f1be1a1158c281e5a013db40a602c0f18cb114cb82a6cda0cf823cc99bffb077a9b99a2ed6f1c3411d95747df3d84e106ac47f45129d298317133363e0eda094757d8481d33b2808a09851d19977d1d89f101fa14fb45fd0eedec77ada40d0f4c87bf0c0aa101b9706f5a6292687db20c8b8c2ebfe45f76f02b3b5875b5470d6eb5395765656488323dcfecf1394d709e879bb0a6a3f416ce0f7c46fe43800b9d86ac9b9ecde32fb96d27558d23d85ae35c3448c1de1b0b321c036e1ade62069e1890190f4c8129a1a9b82547959d503704a58795761b67f81a248273e4830cc432c7549e22dad7f356354af97da1386513840226ba31466247428f48f97a0edd3a6c04f2b3f5fe5d1db1c1ce572e4803a40cec325b95d1290e2fef60ee5c0a7b6407615ad6f80be5f9cb0386014309ea130536c31518128096c5d6e3472b0b38a33b0abeeaf3a9953ef633182d79933b71bb998d4eebe6b318d4bfca45bc09cc3b32506f3bac64ab848a08af0514db742e99f7f6024948b72596a8921a840852b4bedae845ca713998a4fd326f9ef9e15db0e2a004b4a02680f790b43b5d0a617693123705031a7566a3a4a82f5e80898b7aae80a85d26cb23a0772d9aaf09fd372a0ca88f2985cbf1fbbb41b926a6eb6cb130eb7a7b726ee4cd73c6e99607215fcbf80da6212b5eaecff20717fe251c7a79828a42a24196a73a3a1bfef2025e53e0cb8de5d9b3150f59ff3a77d89ab6071b27f55b7f62d333b608e0c4ba03b6b16685b0553336968382a81ea634b27528528a44f668a4822b0c143ccbad04632ef0bfa9372426e6e0349ee4753807365f751f1f072461f4431f25d8f9b70ef49ff2ee1b59988474bc1084b534e2cd1c21549375508655180342417f470e000ec44645dd2647f631a3d757906d60eb01a3b42a1ec967832d9a88691e7b1e0d363126712e92eac384635cf2eac664706d53b07dc4f5ea48f0f960a884fa440e17748393538f051699911556d85c0ef3cded23a8a4ffe127476c4b686cc1bc0308838755d145f8736253f64e608da96929767a7abc283a4eeeb9861cd624f306995fc86d811f17490d4cc76a0c2ac084baa4b2f123ab853688e4e86dcd90860f909f7321752a1fdd8924f27c0e032671435a451860cc7a0c29b862984eb13102ad336dd5cf868d7fdd270ac81ebee8cffd011451b7d2c99a21645fa26b41fd06170177c44d79d51740af23d1ed463bed14b01791161ab36bfc7d4e680ec6720b594d4df89f2e29f0239528fbbbd824298d1c394b786da0de6c7561352f8c320d0a8aebaff85fad0336dea14cb998ae29aae8ace957f20bc45658b5bef5881e016087ab60a62ddcb33028071e1538b9e652408aef19331d33f14d9a4a949632073f26b455141b6c068860febf5ca4388f9cc4c8b881bf5caafcbae51726bf81031c419eeaf63955066643af3f0d451388b4eff0fe631657ea6649628e8c4a8e092f8f42bed168fb808f8a8672b8053c4cc4f3746bfa3e677c681fe191aabb6c007546c2b6df04078b3b6f61216ec40d4b6f8cacf0d3f43cc8ef5dc04cb693e80d92bf5a32469d874aa534a9cddde23a7c31c9c79ee0ba39f3a59f59849c85870053de355bc9033df0aac280d03d28d74400e5861acea3d8e1e75ff042f94c160105b012119e1ff2d03db87ff5051005030b135adfd5df5cd4703ceabfecf66c0574b29700a2b82e88d350e4a312ef9cd562613cbe5cc7395b784e3c14d3376b022e1429753506aa0a18729480e09f67e0ab72f3f452884c3fdcda9e41d9c8080043b8b7a99cd65c6cbb566f6e8a0f5f4bc209e8c1165ffe777f6bda91bac93402e31220c95bf42c5e667b5d69e180b724a1e652477ee5affb8a6d0b1a6f98810b957a7271c4aeca89ece63a35beccb211842bd6ef9becb0b6b950257cc0127102755914d2b3f02f8db7264dd2f3ba3c7c527e6eeb3c2c156a5e1a3c6f25fda10039261a2ab9390ba41cb0fb9c8bf275260c9425a18362a73b3476fe27062f1c024e69c522c63a39bef858f02caad82edf280f9e3f37767b7e4b6a8850526117021ed0c6db4ef412901937f6fc8c1fef4d27b386d2b22f70ce9e4dd5424a465a0243effdf88dc9a89f23d7bcbf0933895775243068d9dd1f70fae33210fa52410118ae830acf17f6da8221c241fab2516e5d0ed30426ac5a61d5dd2d4592a1040699b92835515e98d7c634f90d565f6f0a7b0ad3640c539ccff4dc639ee61a21014235ca2e453819a76124d1c7de7fb57dba8892f802d02d5e4fe500a6383b3005693f3123a42cabff09a57954908f6a93e7655f64e7961644dd8eb4ef85bb98094e84e8330e97c9a6854dbdfb7b117d5cc0caab75e6bf5fce76ae103efa2d380f5ec2b9433a0727d0729aee93e9e593df316336301f3b6f7515e65456dc1c430eab74c9413ae297831c6ecaded92056115a198b7b8c73f204e9732c77b3abe80f3444ef218894a2cba999895f7bb1095bdf52f96012b91ba61702606d91ba1c0bc9f27199ec02a210df49c7cadd3a25912175ec34e6aba8270a87b96dc0d652027c2b3ae18c7fb986a203ed4359caed5735d10cd2313f03d5fd2a71e0844406064410da274eebb09a6fb6c75ffdef8af171bac043780a2fd022c952da058a910874d66427bd32afdd06e496a516ea66d7e13ec1f9594edbda69b66bb8b623b30ee5ff531269190244f1407f035a79de947ada93bd91109c2083a9ab726bb83c084685c9212997c7af7325903e75f31a155238601104406aa7d5d0ef1b36891b07d14f845f57f3f10671691c92fe2c76820487b6faf55e5a4fc9f36c1e49ff8d00fda3df03e8a4c564bf72932f842efa57e8c140013ff157b711f0cb12f83a31022353e0b1ad2e4b6b394d031ca72f9eb76803298a5d353485a5afe806f5061e0051c806bb2a6c085c219a182c5bea01fe834e69a9806d47c6efd2a57dd8d03e0fdf9b3e0b253db70e8149fd693bedd675a9f6293f1b26b1c21b8c1cd3fda9d1024252556fcc45f981ba70f3f85e16790a6b19ce07c0d269168f2b41953d39050240a66dfffbefffba27a219c600a5b4c43b2b85a3630fc38fbc7320ae88f33507b6900f49be492db3fc29633b8c0956031e58299274d131c586b7e89e7598df0564d608af93f4452833e4a33a23f5bbac07a2fe2ffb6239ee6fe775c08bbf56082a84d18f558948d771666251a5c64f6adfba36835e8dbe61441118cf199fcb0edb3421236315229a59b941bdf1fb3fbef1c852d958705a7e3bc203d8343ff502a4ef951aa4ce2350ea3daa7131f2f3d1078dd376ffc72c4c6fea70a1fefedb02c1f8c8387e27f69460fe641605955c69e96af89fa7fb29411d135b27439cb8009539cf4fdf5e77544a7253063790717ae3b801523b6349449bcdf8b8a34b820186b2c9b4b4832d5295c696cdf52b3ecfb12d1e1a6aa8e2f99ebae9a7ff049303ff53aaa09c41d030b9c48990fb5d5d414cee148c4fd53a4cd6a19e2fa91b2c02435216b30e3fb8afa9eff2327e5c86e40162cffabca5e5075511fec566d9a90e86abf5bbedf05a5edeb03c071def63daaceb563b646fca0c1f2b83abf97fef06e7d3b5fa837077bafd8a95db7b3bad6bfde0b78f9b416740a423d63cc7487e0fa4cbae97f828854b534cc7d283fcf68bc3d16aa6278b28eb4cf2b69578dcfa0c30547735345b50a32723ad67c3423cd0cc72b5e3732f24b9194b79cf0a6ab40607a5de9e0f44e1521c91fab44c93d8c6da6e3524eb03c58955949a8d44bba50b1979ca1ca37e96bf502e98991289583613a23857fb42240c1e8c131cb6664d0111da2fd970e5ab6d704800ce18844fc5789b05e6938592d694b60eac65ebb805512935c69b74205b3ce16aa9907e6d3efe26679116b08adc3077d748b3c32d00173693e9659a010ab76148871a902a0f409e6b49834a7262d369699c8689ed089c711fe8577f42c68d28a060eb0a9e0a18b0b1b52e998f5289ac9ae977c8290f507f4de73dc838802b52a5430240d1209294d071b184260aa7e60986a0b339036273ef1d3851447ee0f84d2e79f5b3c5bd6581b458d5ef0dab9fb2a31b4d3a058c436775abf61b62cadfc951b3c2befd3321d9d6667b5158629046747e02360c95a6738503c317a05d071f88be567d00506a80f4ca5f5dc1a0bb2c4aed1d22078c9750277fb480dc41a7111a7ae72b25f55f87a2bf0a0cd8fba190cf06c0560b4a78136bfaf0498c1fdf47dac9cd721a71b25e65474dba92bb60df39d27cb70dc91d21a9ab70b75db686b29aba53f11fd3eca97ec77e1ea5a63a84ba2e2c1b0a0cbce9b6a5e954dbd9eb1eda7b7877ce21c12899775cd79862821e19b2ced80b4a7b377eab6c1752f5c3e15ce5b9a4816c22c0b3ac5f46d879fcc6116df6c702ab4412d3c0aaa3949deecc7266befdd8899d93afca0796d42833532764fd8601bbd5f89693401ea65b02b4c281a619e50beb92839692d5ab47e9ce1d8560a6024c0faf76bcae71a2b2c9439d002bb4de1403d9f95d2f506a35f5f38c82253c0fde2131ec3aca4f199966715a161b766d0459c9dfe19a528ab251e006cdc674009f880a91eca23acf8bf08a0d25abe56c9171cd314c0d31f6c8462f38cee96c06ec844f62486ea3ca313e591fb6ebfa9c705a21a7847c996ca543df4f7c18480c83662cf37328e375aedd1d3df30275ebbb383f3fa2624f65b7e1db8727147a0eee3eb1b77c45c3e4611627e9cd478dd5132a3b279c7a7c346f6553f076c6250c1de163c6b419c07a3ca9fba36968601eae9e89c98a442b935c169efb82899c075b4c61baa2a2d3d90c740963aa897562b17a71d81a41925f3fe74f0137ec5f0fd824cff05e64ca88c1353534ec9eb94a5135592c44f8a60a44c7f429685f8607b205dbf886bd674a943adeebe2226e4fd46f9ad006ba263f83382867c5f01f0fa19d638c9d4555a30fe09ea97658abfbc9af9ab3c0c0f32b3d144080cb478e0410c201673cb102a6810de4324f9ed9c559f6bfac0148d7838c080eb37d6cec0d4e194a5671f6af5e513516e7d041e9925dc30f9145aabe44bdb1247c0069a504c93385a4fbaeac55d773d16b3965f53c16e34ecd2cb9eb78093e20e53336d4056a1d7c38786b37478253016862337208492544dcd23fbaa71bf25af63ce9490f1f8ed1a95f40103946a8105b38ec476e8ecfbb455b6631e28f539943dc1d430eb3dade6a5f183b81f6546d6a618ac135ef407365161f58693fcae41c88e2000ada240a70f7eaae33e859771c82031b9dd9d10c154686ee5dccfb4ecd4d58c6058ecdd30386d0d233853d4b6d5d74c7a86cd297558ea8bf4e3633848337780d06f3bdef2d9b81e878b8c598a3df473a415f076787a95c917642eaa76451cb680cc4e22bbd4e02f6480526fbbacbf29f4ff8f33d5b3e3858b2bafeb7b90c4d340a0d3834c3c0c58a837bb757af2c192fa712797902995aba112a880636acdc440a11d6e75abbc2ebf4275b274ef9fa025a9e3550e95165c3f7ec9c94ccdeab28020e03d70d8af861c67135e43234daa0f684c1d6df03167fbc72dd99d919c91e0b6041983e6e4e85c8a4a3ac650f410397fe27b6696075ab8ad7d7df35517fa604c83b83370ec6eb6b371081c97d2360943a8e0832d90ae775e5b07e2b906e9805b300f6ba36b316184d8d6dda8f60df2b082632c1a0543a629ed7b3a92aa1eb06ea177d4f4c0aec54d434181dbe1be0d352c8138f099ca4abffbf1c5e3332dd0e72b26e36aa352bd94f325aa9a96610c62e6c52db31adafad53880ddac585dc068601fac18de579dc34e55101571d0866701d3ce769491d1e3e6d2eb0eeb80a0d6d50ff04fa9355b9897adeb8962ff5865415d9bdeb1e17ba79e7d28db7ae7d08aa5c9565f53562a8bbe2d5b6cb5509ec63a0b90617337586087c3ba15382220ef6d3721846365b89d1f70a50d6b7d6cd3cc076bb095b50051c2bbff668e39c09cf19eae4ff14974e0425709d2e3e4746f4e5ddfb3759b79fa6ac1b27eece4a00c04caa36df2d80e404fc43970a54055b11fb9d6be0bf3cdf327c55db30c8830ab019d25b8732edd30299ab1ad0b2b0a8e9133f4da9eb8253eef731097c23f70b4db1ae2acc421b8f438cc80efefc5abd74d6ea42b9d8c7f5271b71546ac3d6030f56c124c7e70f36a3374fa6b966227f2a77133db81a24094c77262e3aa980011f39050543a854cea4a6667943ae71da8543cd3332b2fcdf1a3c89c63a549e0124ef8163bf736a9abe2c37122308a8a908b3afe0be8e1b63851ecdee26bb4f0c24259c9c5c8d76e99f74ffc1cc10455a3233781535f18e968407fc05287652019e06ab5ae59dd1ed5ffbcc67075ba7585941785a235714fbb6804735e8c42d003f6a6abd5dff1fdb9cac7c1a0a4e4fbc8b9ba39c03674f6d1e70db6dabe3b507ce07a887426b5ca713c61d00d45c62804482f725c87e981580b58399102bd90d495bfa3c00cc03ad051765b5c05aa9d71d9a825ea286c630de0fceb6dc514604c2122391578151f9ad0997b738bb016cfec91a2e4e072879eab4688b1ef36c0c11254cd6800ba6c5105bfa6f0289dd3eb4f767e5a92e1fea8b318f2e5f307700c83ca74d32bb54e2ab42c8319554b4b23d41b3dc3b0b401d52aa01a71d198e0e91c928a66613650812c7b63f364822b790805677f922e8268fc66c3049d7660880aacb670e531c3168c95e7cde9e1daab0c41a13ad6cbe90d8bdc25b8c2ef60855c5038b82a67228c7604217f2b603885cd526617daaeba3ac2e61ca50fc3101081065a2cfee6fcb250f995a2f8c5075bc7762a6c980deee066a52519fba94068b2e836f1a8ebfdd39a5876f5f0e513f3f30fac5fff73ac6376c184a73ac6101b8c7f6eab43ccaebe248c9ebad29f4fb13dcf9181b153f425c48cc7d7b28b70872bae2f725ceba9216954a42b4dede280556f8e52efa7402ca5b0d5a03952b0bd463c8331e981d1ca2904c1b89c4a20875802895ebe5f43cdba5c6b82ab6bd0a9676f4fb2fed1dcc8dcc3db459070debbfc301baa61c2f6c6c230dc4fe702502b72a54b88a914244ca71149a69779bc52ad778a0adcf07f3c8e6c19ea786280d757cdc3f3cef71ea121a7ec7deb15ee4e88b9cc05be71cd61bf2bd9b2d44b0004435a2d34217d121905d9e18dee2ad1f0abd175d186364c61327e405350d6002988a97923f0efd2c8f3da256623e395de8d552ca1b227d44c8a697dc8feba40ce90076061dd3a5bdb15f48e3ab09d623b3e55deee04c10b446318fd9cbad840b5f69907a9e45ccdac67e6c9d85a8c5d234c768e4291bb958f5f6da44cafbcc0f4e1d1ffca7591234d0b5139cfec2c951f9a9cda1b666dd15a47a24e12c67b002b468e4f0a422b1a60fc4cbb26e2fa80b8651a799ae9d383c4eaacbd7bb71ff0798d093d3d4e83dd280e4a7a3baa1285fe495d5cbcc3beabb1c1c8665a7dc270a92f8ab6a1f93ed89bf9bb795dc46440b48c9034061328f70ab4d9fadb6e3a0098be0770a97ff88f7c6707b2609b7babe235b540cbf70be5582f515067ba0800b908ade2811859104479f67d9b4d400a7a23460c897465dcb9854404257238c04169896232959c802bda23553b8fe792b035d5ce1fca489b12c2bc7aad6e31d05112e6dcf27c8544d87285184abc4df918391c0f7c98be9ddb9d675cd88b86c01d5af0fca83b1a4bbcde515bf49256292fca1c1fad331d9c586e07d7a1210070ff8c1223239fb71f1315daab1ee82ace042c9c54fd5fefb227b7dd771beca5004720062b7986f32d9e099c185c0d6ed410aa98a60d6b9d3228c426f12570c3a0a92c8a1b9fd2f00433f6f8517fb29dfd75a9bf9eaa6135e85593dd9815882db01cc264d002b9d56e7ac42adc12d8b25394ef4e3f1e644090ef2f95ecc60f9720519633c44cfbbdf151ffe69a85691e7ebd00eb5bb427e6e398c2a399182963e00 false +check_ring_signature 1f0389e5b4830f6616b3083ba39c872a6de1972bf38bfcb4b87064b0048f280a e1b92e175c8b5be20972cec12e06a8c4c2d442c2c998e582af255b1f5ca4c083 4 78f7fbd2571a5281ec704e53a1ce7dd5e706324591ef6aab7fbd9281b9d83bee aebf76203904a4d6826cb32ac79d4d7e388a94c5b63aa2fa0363093c0e5d6583 b11884d2f301ded368e14f9682c6be234bd7bd064a0375cf05ceb449748cfe63 53be33d106b129f374e934c77121d09ccc19d3027d08a1f43a5d15ea39972303 375e47bd3221eaf5c74bbf537fdcd606f239a2a3b7fdead9c7d2b89a2063cf07fe11790e1c7243f6cfe6151eed0ceb23da889c5d039b916376002fd8c00459019281f7e40e872bc6f8470f7ca13a22d4cb1cdeec6ad6ef8f771d66910bcb0a0322b1b10e0f7f03393672a8b5d7774cb36c5a2c2c7bf0af3daa1c41ada0a4e00be63f7f1a1038ef211a44e8d6eee8b4f985aedd1cd1b1ba937669289c0d42dd047d70557b7635dab44f4c583640a585b732a3ee00488126cb071d1a46a05879e93b636e2dedc32644969a7f07e3358e7698aefea78dfddfb2309d853120e07e014c4de815b27282bc558d55230516c5783def5162fc2f16233e63c8e3eb97cb03 false +check_ring_signature d9e1da079b1e123af5207b222dbb90984ceb12eb226c6c4933b2f2865ba442a1 3af0d642e519b2169e0387a34ef1e10737b91ec9cd973e950beec4483dff47c4 221 6085806bcbb0e69f944cd2ec61b0cf4381da179fe066913800a9ef3b40a357e0 9058eb79055726458d7883fefe0fffd64a02a4bbe3a86e2cca934ce5d051addc a54d0c656acc3bac34a5ea32d6e75ee40181154eaeee3ce2f6c43b9a78130225 0c1728b529bdc2a85bd137549b098e96738f14ca887804759d52809e54a7c01a 77b531ac1014f8e4a1d4d7a0ed42802c818447e7450011e8ad129ac20a5e34cc 5a4c1cffe093a580d6bb2b0165c3ad2aaccc27f09a0612f234838236bc8904d2 45212d503f2746d2469618a0eebf5e69931f603a341d2fd2326de5875debb467 9168d731647c479281534f0f82744776c43d2fd6cd152ccee139e1c3b673ff8c 734cf993a0c71febf808262ec7ec82fd0c5501e154fdd597d1732d788c2318f6 aa7aed56a3e185f447df73f89be09c2db1322a377f96451c708f6a94af62fd29 c671ca2ec128eef7953de25673b88fa3b686ca021c174d79ea34fe11ddce1f49 94374c893b4ebf41778a3924473a6b0e75db6d5822b2b9a7371e0b124de1e84b 1d3e340a7541723575b2c7835cab96380d7f7ccf955ce3ef9a63cadca5da0ac0 3e03b84670031a415a8079779069a8324a6bb78d0ce178da06597cdc1ac0b2a3 e0000c3ae1fe9e5c1385bcf8dbd034446706aa5b04521aee85a6510c5f2225f5 86dfa3060a23521d9cb800f0bc7a9289ca32e1864903deba0ec2dcd30ed980e4 6f5ffc475670c0b536b9c51071be4488807ba149b80f00bf974a5c5354f1982d 7259749881b973762f7467eb6b01c6cd162d0ba71f5a66b4fe70912741a5d956 a5f1eed0f27d83f4f2c285a8c866695e89b8f625f78d3bd75d23b8337243b0bb 30a16f7eea8d0f35dc706a0547a0fd727e9cd3bd53c18ce7931df32143c146c3 b24c513152ffb92bb76a9e4de5972b69d4a19003af101948d84111c9e2d9e9fb 49b38e8fcc639e1ce357bccf674dcc67ee636fff660dc0dcc5bc5e15ed054084 3753a80dd0ee1fba08eb04e3f0dc17a8c46385eb9ef11f2e744a43e016706d02 2ddaca1ef0382f2f90d95c72a04f17486c800b38367af00a862867c64d34645a 19b3cd501ff6db7b0c8bce1499c949645522e1a59f64bb53e78a7df36bdc544f 086bc73434a65a848035de7eb57c6b40402a65c724bd3c0a81116766744861e7 fc0c6cbeabbc97d634a3647e2b38976ee656f48818866c1e81f5cdf0905fcd3d 1e84779ce70165612c1e8d620b5ab7b3887d4e61709d09f5b63a162e4e0ed363 3ffd7994c7e45ff46e2432f868e6f2c1ea95ba29523d4aa1bd273a3bcf2b4afb 831147be9172a2362cebd0e12bbac065ea9bf43083a57e69c00394fa009337e5 a543d41b87d290e73338dc6e530681a399e972d20af69de1dee5b5a537be9024 9a046436552332d29453379da65053a97af74a365dac8c60bf7c5187e4ca03fc 435578c1e0ad600cf3532703fab7009cffc6663a8fd7b1b4975f240260bb3bc6 1bbe40cba376020463e9b0def76710650e01d9b62076bee9ec98dc0584f84eb8 06ba0040f4d3ab82dd28143cd06571a24a814ed0335fe6e34f13597c5397e881 b9011a44d7611f28dbabb1ae43806b70d7fbfffd7caac0c7a85e3c50924f75f9 24bc2acb41eee73e0c6628b512ec5b8a6f2e8102a542dc172b89531ae506678e e2c884bb6648bbac1dd552a9418df2bbd163b907a35bd17a64cec3ddf7c83c38 336484348aa77492258afa78d63c05a2b2b546708db327f0a3a8aff8165a251e 839b86e513a4c00c80024fe097a2279e263789721c26cb15073363d589311cb6 6a3ecaf4620acbddb790c93ffbbdf19855ca7312a949d1298fb2664869f96924 1af8aadd9a07a1dc45bb71c33fa4b21966dc950aa15b3d9656a98d44efad7f56 a8d4e566033ded87dc2a3d9ac32d5cb5abc8008ff2efe264e26f50fbdc9136e7 d93391a4085484d4c71296ce2fe7584e55e170315717abd9a81fea13be53d58d 7b6f64bfe09ca915d0d091556b9f2035698e7439de2c147d2855122a9a374d68 b4c312e811bb1b1b2450553626afe9f419e616c06e6fd102f8c186331f61cce0 6cb05670922c5681bd0a93bd3512bea426fc5120f747adaba6e018ee29c83a7a c0cab78325b887d61aab116f2beaa9aea6ada188d211204ccccb1658b41c75e6 62192e87eceffd4e5057ca80a4f0ac6dce6f000d1630dd4c2cd04c46d44bad65 85d7205eeb537ab3802b016677bb1b61ff46c76dd048b1b1426888ba8824c7eb 81c718ae00b796c035be4a8c0d5f0faa5bbf9f4daf1f638a932d2cb43ede3fbb a1ff7ffdc45caba07695156d0e7e33ca9709fa6a1006db30850b463d88b20ed5 d0c12352bec7f9846e455e993fd2d9b62f9a0f4f921fa52f4cdf263bca3f154c 4214050c03eae91c369abb054f80d67fbd46d9b2acb60a65d3109979733d80de 410d7875af60d0eb7b24258ab6c9674dcdeb3eb498c34a5b27d61cc833091c77 629299c2fe47563bf345c341c6875367d66c8716f2576647a3158ceae6915999 2d909f832399fbae4b6e479f51716a3dd4074c072d8f15316f4944891ac8111d 7e4bec56c0646b587dccfb1049e6e4cf5c3c8a5a134a2ccd0996390e652e73b8 54e8b0eb90ce37b51ac396a9f6ad047541ea116d37a1902b978ae0d6d27125f9 e873e5a03749a8df3fd154c7b567034e042598a7cc0307499a71492e79af7970 7966abf3692454939ad27ec1002d5abb098033283b0585dc2388a9f2b6964fe6 d2be61548852d7d8dfbcca52821f3655206c4f3c079a2f14af03e11aeb9a6b1c ec0f973aba210b36c78565985135bfc4a80fc8712e844bd4942bc32d73499ce5 0917cf5ec7d50bad9eb5bbce5942e9097d29a35a75f984db21bda3ec2841f9fb 948ce04d9c10862d57337d0480a2ad5400c9f2eb5c7256af9a2aa19a2748838a ccfde6dc3302ccd0d3b2a5e162d29d3054938d5e3fac761856e50d49d805ed29 7035057e31a375a9995aa769e8a82a8d7092d6b177b3034f39fc5052e7f77277 7d309443aea1492fbd5c58aa6af87fa83e749ca9a46f84a90c5f04be9d2f927e 5382853b58e7400b19d1bb3f2caf5722bafe4110726a17c2d850e99b793b9791 252f622fcfe3fe301d727caabd19706f84750e04cfcae778be655c1d21b38b97 dc486969d2561e5130160853bae7e308a7c99fb91c409800360c9b66b7c87a10 e93ec6d98686935bad75eddafa748a0b26d521db8a2dbd844ec052daf35050bd bd091e9249f40f5afc3c49281bc222c1ee5fe2909f76a9c05e1f6802c28d0cd4 3e1d6af381b7ec0c7b542c2e69d1be81303bcd4877304261137c1b3bb39559d7 34d54d50ca6fde4da3e67aa2ac35bf2ad54b7990c1d5d902520a3446c59c80b9 b57d2eff238b42d483ca9c4cfcd35e39477bec05253240d025814d44447305f1 103a7d5ca1bd9536d9cf15d952716f2a7a538a0b2d048ffcdf63b17cb3aa128f 0003415557583d62f15cb03959251ec53f49061a5dbce576de93ee35fd8cb06c 58fc1bfb65feba25a37b43c4b9f5c1630cd3563a2f3b0e89c50949d843baf30e a2023bcc9a7eb75c026cbee2ad92f4de6d297fb6b0099b10bc8b804bebba56c4 ffd2a155e4a489567270b32178908e8cbb2a5b7320766dc2a14e16482d9ca227 a3b6fbc5694d43c04742fd75b26cede6b9da299daa540faf741446175c7bafd7 964758aed727be1f2de9e80224f98aa85b5239006e02af1c1554f6a01c921e4b 97ccd66cd1ce2f16907a9c8a7828f9571b99ab3fe8ea862054fecb7efc1df55f 4d3d8d9e4e1311f65fef7f139ee9d8dabcb900525e7ca80a107f88751dbc5a25 0e175e1dadbfbe9e1a0df82f7309d3cf96d8ee92173a38e87eab4a1b0d4620a4 dea7e8ad52e1bfafcfdd36c695d80f32fd1c174ba81d8e27e68f9d23b7f26222 7939b50c210a56332558522088af534e1af1ed1af9046f08212db0494ee58ab2 567134de22f125546a88ecb073ae52f992a1da55a863b122203a90e3273e2fff 6352ab24b6efb9d399391a87a32f4334c5fa8dd9c26714409db2d56b0ca3d6c0 992d599c55a85e591b2a93396170598f899ee2eb8503b256586ea3441ea73e41 4b5d8e1e3a1709c76dd5aa9ed5c36ce6b00ce781780c3d7b2d754cc96eb0e5bf c2404c369fb86764b667a9e855890e610d80e6be5e909240bd5d04f131610ff1 1cf375b0121e5b4052793a52a940355fc55e799a662169a92dd493aaef65a406 2af0994449d78d45618ae166ed6a5014f3939b77f9b58ab1ec9f29a146faddf7 d42c05956975dc4af0e88a2b7e61eb478612f06f04f81a93e37de0b70966bbca a235b8add07f66be80472d13d9774999d89a7be27b34bbb0eede81b2bddee26f 91f4d369dda08e0340a654b446b67bcc73e29d3d5c03ed178916d0181bff996c df4a39b4150679d53a18c907a0679ada25288cdd87fe033f8d8dd0634d4f1c9f 538a5de5824d6bf0073f5a2dc6fb6245753ec762b5eaa9bd39ba1ad0e4657a0d 8bf3792f2252653fd2380e9dbcef8b48e5b025a1759e051c15d05dafccb13b0d 22559e750701fcc1b49016c58eba9c6477418e5777827fbdc2e05655e8812765 4fc7c35182f087e0adc55b42ce557b94613a802b5c6f152943065acc9acf63d6 8596a84a2fe6e3bb4831d5ae7c832c72ceb549ca4aba882e73d90ecddb4bd1e4 39c8486925862e88d9c8f388365a53a40f52d5aa966342b571b951ee6916f923 155c9efdcdcbe78caa57878d6d2e6bfd83940f5ea68e3827a0a8eb58ee41b9b5 6945fcebc8f9f76831152f94dc94a1499af8863bb33f79a579c88ba9d8d0790b b37b442a374aa98b2128fa14285a5a49ee895c8c89b7938e6fd23f073de76d44 e8d2d8693c02b599b3540f8f758cca46729b3890370b7f77ea9734cf14449c36 2568f7a32e0e13e4d8ff21032a0c3f836c3e4989c613c611c3948028f6e541d0 3299ab0a9737f99a838481580b22b54398527b2dd4417d47e8e6a759c64a3a51 7d8d139c55755c4304f2b5d44b679e8616a54bcbd9cc92f5ce1e1cd55f13d112 77f3d80def99f6074a51249bbf073b9d8f8ac3b96a9fdbbe7ce8ec359d722c74 d305dc3edc53f43e7e9247ee79e0ad0351ba7e128b70b632b079037314d5291e 65e7f55343818f271ddb7aa3707585ae6bc1528ecb7c93e5eaa97c955d187a13 046f8d6da02db4e9562587ec9d0d90ac7869374e5bf46be662cc0bd93075d4fc 35de0fa69d179215c9d6d005b66dfd36069a116cbe306f57a8b0854f6a8037f0 2264195a779e112da102d353ca932427c83008f948ab61f2160fdd0e2216feac 09b2f2e2f0953f22dd7838521242b34a170fc135c9844c140fc1b09363d8c513 e66ce0e6880f9f496fbf7fea3419e5d0e8b86924a56a8f0e54dc27f41ec95bb7 c102f52429cd810de14a4a9c175d9fcbeba3e5654ce03bad6648102ed56d19b7 34bab8978ecdc26f13f7b8e32aa20f8fa2ff81855ab500ba78b4b09b663d5d14 053ce3f306790bec67f9d635917308297678dae6665168971a93c44bf37b7a7e 12c3755b03612bcfd1e7c865e474e7cd65342d195138dd7e7909af2802db22a8 6b7d3cb5d90b02a46a774e9f06d06cef98abc41e33542f1be5a5936d78648fa5 d5bad4e277d250a1967ef364f15974404725f3b00e88b3f4b08b3e13b6899cf0 e20467e985b904f286cc01cfe45d996e6503bacf0972cf18174d86d78b6e76e7 eaef4e78356d4c439cce282b0eb401ab9e14648659ae67ac2e3e04f6f45f0994 8d6bc6ee15b48b3155d651cb0cf8f205e593d0b9b07b10749955b1cc49c28780 730e314bf1417dbb2d71808957ac2be84c54a0deafd860c20eafd799821ffab8 254b48593ea767ae5a127f351c297b746e654dd5282ca4a49d7dd3d7e8a36136 136984f5fdc9bf8bce1c176f8b5063f626828fbce4140c97327b6f601753aacb a5d8187d0bc6cde4089f825bd51b6b4803918475d78361032d68cefb973624c5 6c32aa56556f99fbc9c0e41b5cbde1e57e32ed887fcb546a5713b84876e96445 d2e7cf76351768ec0de215b983c97d693b6ca102ca62289fa8a7d36a8a019867 070379d689d50c3905f6f90eb024f6c83cfafeb8ee7f029364685a074290dc31 e3c09363d34010ea9bbc62087f4f78c2b637a2c64cb4e145fe771cbdbff012fd 386e8e8e3555569691f4c4d3eb6124558c32391f9ebd0990c7724ee88c16c790 24e8a5512310f31448f38da375ce62ea85ce7cb03b003819420dd0f6994b7388 c86d287db3d0f74b298b2987414e98b046e07fc2ba2d3964f754df2441ab5409 fc789cc19accc3ae3b19afa0a048f1839d62821561825f4fdf7088d5837745f3 2d2ad6555f72eccbcc7dc2902835d05780229b5284e621b5bc68229e770a59aa e00f72a8861885ccde32e43f05df3275a44f7d71aa448ec80c83f4604baedf1b 714ca1dbf12bf22f3333a46f6fca4cd14e73c6d27ab9ee5b04d4303a01270d2f 0025c801733dd9952ef06978acbec98372677c8c60a426cdb23648cf70eef066 b4c48b7080447f7ad1a8e43caa78192165d7feb64f4350838a5fbaf390985fcb 4696bc855e6eeecba65182700d6a048d7e8fee6c01b33763299afe0076fa0af2 9c7cf77e7ba8000bfc2dc42aecc4a7d7ab58de5763ddc90d3923d5963b072377 7ba0ff4144b56c3f1ce902c58799e40098071331a40633aa72d05fdd0300b14d 8bc8a25762b09a72a2d1c094d7b8273c02dff56152d3946d3137075b165d589e 55e4ab52d6193f63cb89d195ea6178eb57a1ee591e735402786f79963ba6268e 0431f1b1e72ab905e509543a0b95784153edae7a386a13332cf51b0c5c15869e 2a2802901c15f11de1a402d9a0704f875051e5763f7f09bacbb3d517292539c1 373cb6904e274500c033f1edc559c971aa0b7a031aff2b77a70a8eb39824163e dcd4f0b0569f03c46c0847cf4361efef0afd60f20e76c2f436c11ab69eb7b6e4 2855f77c1aba7a072d9aaaaa824c0f722dfe510fa1942b8131f90e362f7f63ea c5687df85ffe61ca279077dae1f6fb43ec5b86783c304612bd0ade1ec5cefd5f ffe8ac954d4b7a76f46e10b1f4841dbb1c6fc5aca41a6437a3dcd2d20dc8cc18 6d0f54e72a00be00fc510e0ce46b95c224f5ab126eebf5757b1a49e29e0deb6c 2f2f5285681c7b390089e4b2a31249580eefbf22ed5e76bdde446da515ac3095 ff483e0c2f5cb1e049e6041f0435a36f9e8a7ca4e14a2465eab511d47a1e991e 7b59de781663dd45113e1e17e4ecfb5073f21cabc33150dfaeba6c07118475e3 22efb3888b3871cd0180f0f13c87c60d22054d53b1e2bb1175512793955a0707 a0ac41a95485942cd4637630f9fbdf2d49b11075d493ee8a2991f4c8acc523cd 6b57b1805d13a8a4bfa5a2a9bf3fee6718cc8396bb00607f44f297aa37452c68 c85c48bd89c6604673ccc9c21fc758d39a89d13d870f5dffe9bf9301b7a7bc7d 658a8446278d23fcf696b4868616464bc96c9eedef4d2935bf3450b672259bcd b3e00413b7f7ec139a92bd55bb5374e9ac947d9924b5a2ec08e194e988af64b0 3c35f5b9f2c838c783027ba0aeb85ca1feb7ebe9beb425b02259614438fb78ff 01433d4a417b3f4308fee44a05fd0a8f6e3e9e42e175b593e445f8efaf672daa 6daddff5d1638a673850c5bb96c59229029f186a47028e742f6f18747f2ab178 b1ac91e26c6b69169709c24a077f727968dd8c7e35845510d81c78ffd55ce58f f895f38e9e444a6b4657b7c557f0f2bade4c9e4d60c5611e066feae31c294d5c 909c7f77e29f11e618db5f509356791a6f457ceda5b4721e51ac27258ac54f05 771a21cb391b871544509ccacca2958509a17bdecf48e0d092c49ab00cce0a61 b47c6de41df7ec914293dc06e78b372c499b4d595ab9176009fd0373f197db6d 0a9e937b303a014ed87e5cce4eb5d05bf33740616eb0a39eae66d3da957aeff1 a75ee08355035ea816aa03a7cd1e090bf3389163652f3fcda504fee787c51da9 f313c88385d393764613079d5a96e9b7be5243626151885ecb846d8a56ea3eae 300eaa5d08c867b09b2b14c3514fb9425f771906cb44ab731764093f7b1cb9af e6a54708c3e052dd95fc9194762a867fe6394c13c6265b30bb44a75d76c5bb06 383247789441804e4512ef65a397c3725a2e2ab63933c62627b67b8ff555deb3 469ed620577455a64a9f010e2cd7271be1d930b0825115c3b070ff2e06e24972 899e93d14b32599918fd581355ba876fec87a1dc8c3e8db2b076ed1f42fc59b9 b135840d5258fa5d69e99540f7e0b7679bbaffed8b384d4d2fc209f1382c9b1e f6c8dd96390b16b819dc394897c551bb491d4f9bee1a3602e1dca89e6cfd965e 0a73c0f67b1fb23152a8ad973e88410f7bed3c21ddbeb7a83435f1179ad4d628 bf8e4e243950841f15866ce5fd94971c96d5e1b08056d480c6c1ca37707d3b63 70d194aabeaa9e4daa7bf275f78cec2e147a8d28783aa6704bec2e093f0b229f 9ae64e955dfd5bd52608d3201ad4af27fdf80c5c003853733d9fd892cde13aec 7de4f910e9a7becc234fcbe76ad57b8bc72bc8f5f64ad881e91d51b572f6ec28 1051b5c1c5512b8f1aa2ee7f9c7f934524a65ddeaf3019d7ebc508057dccfadc ab4fe81f30c006b180539673570dc046fd0b73d8ef9ab86f2e5c0c8fd2649359 3167340920b7a1a7a431c229eb52e07ee25319cb6a5712e659d049fcf0361669 bfbe3ddf250c29b3ceafb871bd2b60a39cbd4b3a210ffed3b5edb909750727b2 94a5618f4abe2b3933db4ce0f88c2c2051de912b1f93c53d286318aad78b6470 1e54c6f42c1bc90910e3c306b6f49d989b0873d3829a41bde1141d9fa2b4f1c9 63cba2584a754d0796a1a38e0801d93de02d8d121da77608a66720c749a02529 9827385da4b39d0438fcabe854f20ae3405227b89e5ec8eaeebd82905e69023d 3693af63776d927891d9d8e4990e2fd800d33c2110431990d76dcb7f364c5835 8a0fc094e42a6ae138a157a068bf29f1d4db077c66abdc1a33cb912e8c8de4c2 1400271d1ede054a75771a416f6fb60f0b4640ba44b7ed6154cf409b784247ea 01eb8f9fac991dea404708095714d013e701f4e9c617eac59ac9b3262601f4ce 0276bb29e7620f674e822699ddaa75ad0a5be70c5a64cf6e2c406922634556c6 6a97c3d0b880bbe4ef2a8ee4c5b866fa038988d46abf79f5bccb354da14d2680 fda4f50857388a7a47c52b98398e85026988b1b3d7732addc818392aabfe2624 6082bbb2424240c8d33b9644f7e38727a9a10b83a951aedb73bc5c8e111c2feb 3e8b9549911b819d8d7520d2592ae3053c23988f5e420da02771a90b5672f596 cb669787fe031fc2e4abaf6b5a0bee9fcfd3610ec8fbbbe07de7a562eeeb7773 2ab08f4b89e2a7f75e491a7b7b213b5f89eadbfd023bac62169d0a26adc0db00 ad2cd3785730b2ba02b7593f831255d0a1cf3ea0efb0c88f24a9e8582b735d03 37ae5e219a48dd7540836f42d2cad441f31e44a8817c4d3774388fe588555a7e 0e3bffdc66e919dbdd9b192ff19b7b3cae204519f380b64ec88d790b2101ede7 0424fcbafc368bff6c1347ea485576b3ded336f4c56bf69f273d839e8dc43ac3 7efe362977f3297c140baf42c5028ccaa5ba4581c551bb0de7e597a369aceb70 bfb99ddc65022db82b0c667fd34027bf3dc5e4b39240a7a145ab465df56cd2ea 37eb015c3f6d22971d62b6a792199daf65b0141a4c90fa7a9fb4958369cad189 51f21547029f323c85e4e50ffa1a74e70e774dd38cb020dbdcf028cd2887af14 8b3d5f3547a0bb8290fb6731fb1f1216de0f4492be1ca5d94ca5f1a04c3654c9 e63253516f8ae981fb90c905df8575cacd006b951bd050b993fbe84c60130919 90a2a29277b4bcd1c16f2c5c44d63a19e390c3af33a9a1d6367ea8834f7d606f 3fd83d6ee75bb5c92dd2e5150a411733b039a85546027f13024a24cea87b3100b60563e042b6f16709386d61b77e4daa8f168329f8065a7a4dc17aa70c8bb50ffc37c45e627f9c6cdb8302004db164c1d9a220a35e75bfc01db5d5f20767f20c11bbcd3b48f4757c70a90c824c42c816fa9703800eabee4a07ce18a5f9ea7305ed91bb40907a9b72c3198f96a36f24b4b0210befe529b5c16b83eb18387137069de97aeeff082246802a0caf36d752d437c0640f15027201c656d23b297d190d0639d1f0d053cb7f5764c8f9273225f9d8ea92edd0fc18636e2a4769c33f870c31cc78b16aa0775e8f294d7ced7fe2f79ae909223c59cc71730be37d023efb0ec68caeafca600d8925c80242018572dae266b9a44c4f8b038e8320340182200313294090f44ebeb001b376a00814c8382f93eb400fa153cca829a3a85c52560b9e1e47c4fcbdae07b785308099faad75fec602158bd8290dc9b345a5556ae80cd9bc87b57b00e92a0f3e16f3e38addf6269a9c654a73adedadf9d8999b4cea0a658a8e35ae5d070500ca7ee3905435a28ef4c015dbf8eeed5c04213e3540f30a34ac574f79b202b36ea098c50ad059d26fce289872d62d8c95fb3e4e4cffc60338fabd62043861d4697ea302a235b0d1e40c143d6aef328fa89b6ec5986262095e920d39604e152ce9e29c84f00fb548b2f2a72d79c912a7ddc5ab00f4c75c056304ef6b438cf170a571b2050a8f758f4ed2613310ec5a6a5525a0b8f02a28043feb2eb9683cc81b9692fccf60302b24faadb00698b97cdba42ebdf08629b508c7b7fb852899ef314a01e276b6d227ad3a49d0bcd0eb89ca9e1da75e151d2f008c4ee91a0800a34cc8ad5c3d41fc98c6909fed01e2e8989ff2f464d1e0ae17079c6504b61da0464d3cccc856d0b71055cd24d8961faee0703e1d9d806a60ec02f0eb81cd15396444252405d57d0cca3513cb5245def965bc25040c6b25213d0facaa427124888b2d6b329f697159daec390fe43fb31bcb4d0733e6e95ae646045c9a27916015a0cc3452d4baa87ab4a260f940df6aa57784b90302c2e5faa40b10041727d4f8c1091be575156eeeddfd43d4bc5ee987c5137f65832a860f3f09264ffac05ea8b74504c21e8c7609b50e42f6513354d3153b6552eb9c0a94820a8e66262d3f641830c66038cc2c8e97fd5ffb78e896825dbf16fd03e2129bde0726cbac9d9a88d8ae13a4df9ef2e9c8d69494b05da76f4a9b60c1a919104fcc0afada87d561efb3d5b53b9ef5253e30387954a63b277e9d77592355e99d257e09c85bfb5934bdebfb3e0415d255a4fce3ee6bfef680e118c6f53883bb2584140ae6392a9ae915c9eaf4569e9a8d11c6e33be222a3d6768f9a285b80361cc7b601ab296fd4cd845350eeb689b60d797f5c38baae8d35b1f9193c926d6cdb6d0d0f9a3c8ecf0b609e588ca6bd792c80499d66e781a403930fba8c088cdfbaaebb0ce282eb6003f61cb4da42d482ff8f8a4fb4f69da1c42e4c309fbdfaecda8d960c7bbfe08725d9535bbd22f6104e7abf1e661b1f365fc130f9d12fe66e97db1a038778a94314e0575fd8412d1a4fb33e90bfd720a48baca333b2aae6b6efcc550c3aba0a6a1d11ec3cd9a592daa8704a96b5879c26a2cb7df718c8aacded9fb20b6520c49911960764cb004c433d12b576a4216d663e54cadb108f53997b2b2b0af20ab1c180682c009f3692663e7941437544e8704106d4e12f8f731770b3b10d1d0285c47cefd494b89bd0dc219dd0da063aadac96ff8cb132444532388ef0027e1a2fb9927be5c47fb5b5223a93db52f56e6d77a193828c3e2dcffbdfe6fc0bdca0138c5a45d4e183d01f6ed20a3b6872cf1df31e5a64a274c1cc7c975d06008f51eac029c71df912057d11bc04845c7893104fe15cb9c88c37ee4bc058910756afa4b69b2458f0e70225758dd403f6fe4fe681ec8c7c5d9b58c7f288731500b2891335de433dedd7f8ff6c3ca27e34484b27b11490768f8e10e1119cd9ca0efb9230ee3cee1be13bb392a5808c45387cfceec6eab9ac03312eda91610cad0ff1044023ca919a21823e05b91f5685734300614000200341912b948a11fdf208744ffeb54c4f84d195c09780454f33535458f78218d9dfb01b14257e51bcf50d63ce3901541d6bd95255b13bd6e20c6eb3bb8f244428469c698e942069808308dd4fe436dca532dee811659b3b5e6e85a7e247cfc93a5e466c5bf82d9168a8036da3b0a48ddd609caf84fd1ca54f157ae65bf0bbaac35049109b9f8749810406278285d874dff9386496a5cc1378788b098e44a3b1058aaef387fcfe16520907a8c9507e5323ba33bda51f1a58eb5d93ca0667fae5b65ab3dbede1c4118e45024402bf83b87d70502a09d02da51cbb226150f039ea498838245192971c06d901e08cadef3038f5a91fa1e7f498866c7efdf7de5f0799610b31df1e79eca46505183cf0c533725896eca364cfe93e4401bc48206bfbde65a8a66913716f731b0f833b66690d1826d4f371e4bb457e3a45771e1f82e92bcf95c6633cdf9aad470003ce8b9716a36b5aa1af44c815567dd68bc7e5c62056dced6fd6999b5b544e0abe2f52c34d7c50cec04bc30405ac60e814c28c243997fb5675fe33cbd3d62001d5256052058f9bbce1e50747bc9eef74078f604ccfd0361f07bcdbd1e5351e0ce1ef33bde619d69c41c5b5291a3653af9724c29f3a7e2962199014316afe18082f60306aeacd5b7d14c01280d32b2b42dca4faaf76384a2af2d659cd2d2302021020060a231b12b042ad2a8f63f36472a1e06fed6cc808ac1fce51af5e8b0b0a666d80939ba08a52c7c69f02012cc7af2d5d57d21be5676c5aca2c066859d801f5d9ab286c1509d60e4db07e500c73ebc7f2a7adba88d2bfafc58a88923f4b09625e92cf674acb6f6fa5d97e4791deaf156f9f921f0d391fb3c3db29fe38bb05c53b97c5a7c415d1f86e17e32687ca9221b81c90b7fd6c4631e20253b6762e096f9431821541d4c89f3c5eb49b96ce1f26ff2172911d20296e5766e8c5df6a0e3bbd0a2f972531d2d4760fca41def26df8914af7cee7a336d038e6f50bb71d012179d54e5e1009158fb6c7100f6c0fdf88960c55ee93d1c006f48b73bd670303b372743b07272de1e914a59adba411cd956c4b94b2e9a67b8cb3c0bb1ec8da01d07a87b97caa9c9bf05b5b7492424a6d6172131fc20cb59921fa5d4e6c85180dcf34a60ffc4e8d0f7cb9d615e8c38477c97a47d1dc282e54dc34fde7ded16401fbe4f4a2ce03c9e36a22a73be3d4cf555a926618783eda376dc7332227e27509a03651fca88a17b51817cff31ac159e1b2ccfd27a525ae6966e95695718c4a040139f6450f233d05c9f567c5547015a58f1570fab627061a6c3e5b7832c9ea0724c748d3a334c72f6c30d1f153c797a8c6a0efad897fdd02b047584aeb73be0842dfaa54d7b0e5123c8ebaea35a93a51c5df03b58ece01d4d7e8a4457478a7089d14ace0a52caf6291450787b3b4073c5eb34170b690fa91aa1d9f3d489be004f95242861437e7fc4acc957a70a95efb91cc977856e407da74ac99aee8b3e50371f96524ef95247579b328915ff4187d85a5d63d08bb05356720bc330663130f3abcd7d5f7240ef7150b55d036b2c010d7bbc8d1116d320df63ac765775d5f01b038119ee32b6f6d9b197f41636c0d027957a35eaaa5ff09d98b0577de881008733f388a44e8d3dea8d52b4e217a219b45f78585e4872c52ede2e553bd203a0a4975131cb267b632b798b49d8f8776f35213ae8d98d862e493549b95761acc07ef0a195c01ac3a5d7408b77368ed7a64819ac05da543824db7dae4bfac185f082dc51696582a5cda5719849ff8bbc0ba198cff65b14e0e62f14e9fde60d75b0db1e3c624e12bebdb9358b7afe342e93c3dbc4b7804dad848a0b797b75fbaf101b7b89bf0e9689fd6f877a29844275f4c4977a7bea38a6daf26844fbcf524d603f41f824f3ba95f75d298c6f3d96c479f798e0effdd4e5fcfcfc172de9b66570c203ebb20db9788fc2e182982ec0a6e22a7034dd96d47fcb5d34d6ee36efd7f062886b379934b7e25b7248ab08959a192b4cf623b16f5832488657b34e022c90534c92e9dc8228e6cda2e4f0d69e1a132ae56cbe86485d8b72b3b3fa37059d708c7020376a2aa5dad53fdfcc0366fa2d14bf4adfbf6d757702b84bfe0a53f6106c50de547498e80ed06d392e2977b43ef90990f59feb7531be5174414e8dd2706d122aa18ecee8a12bcf558d9d5124c679db680ea6a9c4da87bb6d6b2db1b0508628ffe54c8c1b704dc59864a2073b1e50fedb8d1bcbdc08b44a6aebf646a1307d95b2d7932b98088d40bc1ff596c9ed4293d6699da97439f8b0d7e85a1b45606a05abe1469c906fec66155d8bf506dddada07058ca4500d6fc3ce9a46a410e06127696c14d5b89143a1dc76a1ffb44c5ada71205307120e88d66aa42210e810a122b97eb3f66ce9057791af15224aeeb2a0850fdabd5fdb33b136c4902ea390693068e42155033a66aa66af21302784854387c0652c1e5799865d96c63560c0f8c7e42878c12d7cbd03f5bff134bf5d440b5dd1b10613b08aae96f5b66f61902eb7197e5b55316a338967799aed476e15f7a0c20918336e416e5df523591ec038b5747bd5e3e10444157d28e0f37499e1c45152ef22d5ab5de26b78de34cc20a235eeead251127f23186bce1c33a2122062065f692f55067c0f7b9bfb22645035031ccf3096bfb4b5c127ac4ea8eac6f3ae2ac202c89eb416f91723840e8d40cc074986151b77cd4884e5758ddaa7acdd7ece93b8caa2f4d319cbb639e90a40c2f5b74051248d8762fba29e8e79e0157ff7db6a55d70bee68b8b77e75962570a659caff56df32791cfe1658329651f6d17837da1f749da5364a6aa75aff9830120140764f562462dbf464bb9478080eb11bbc6872765d7ed5eb553e9f9b98c03774693605bc20e0c989687b13eb52f9b762943ad46a83ce58089d2bf007f2f0a1b28f82a17f3afc9d2541dab9b6eda94a0e7212234920db37c5463df11af8c09243d0be444eef451d2f74e8f494c8a82d79fa35dd09d99d2fe35d443713c1000ecb5b814b86d76e83dc05552b4477ad9122e0c0df84f33adf95ffcca1eb7940ce2b298905edd962b61db57f5a2234fa35848d5a38deca09dcc4efd281f1f850793061bc3755463b5c10ab95251fa52e07805c62442d8a0fdb978d3d7c967280ebf02a2b849ec94769be78e33045cf9b01c3e73f6554738f0fbc3b985b7cc5e021b5d3f76724e7eaf746bc7922bb66742ec33160f1d38e469740f568ed63c85058961d72fa97afc53412390899c44a20d784ee5c0c17ce15b8dfde1ca0a13d20b8fcc061f8f4627f11c32af3091ad56a50c2aaa2150185b631874279158603303ee7559c34ede7c277fce1bb22c71efb4df2b2c96f3fa07f861a3c7552c827c00db445a20fb146eb566bb8aa81ee5e313892cb0ff60122848ae78f8ac94f7fd0cd3ce3124d8ae8e2eb5ca542d0393b1285399c0d315162ba821681980a14c7b0b9942c4ec1c86c4126a3ad28e7360f5b64db1883a0de621fd7b9b5ccc02da1307a6fc168a81343cb97d2082cd3e01888a03d3e17dd73ea8546c889dc2214246015309859e864f6ea6c03a930bc250579fadb8391a1e655f9ffbe9b7ac02fbca0ec2a4b6c5fa051ddba6c39e0bb365106d554b2b8192149a3e3bb7d5ed58f110062c8e4a5e76f46d3ee283279169d0d082d89bdd92c884901015d1bd6806ed2504c392c39ce3dbe0541d9a5b0cd7364e5946b10ef8628b5abd845060afd352550311e5fd302cd769c4d8dd0bcff4f25bad78a5f36ff3310865288c8ab64e6c410a208504bbfad4b7933f23904980d82743eeeee85199e784998d1fe661d403500b7062f9c2f2672ea21e4838239de5c2024ed4550be3609e8883bd5cce73252708d6144e3a7640e23ba7be8263998c67885c3431d34a6dfe1bf34ddf3c300be601eeb0c88359c6f9f5645ec697e2ff5f975e5592bb58fd5293acf877d7115eaa04af9bc5ff128fe416c33515852cd584b6fa2b1bc91bdc80a122254032a9b8220f30b87ac22d93645eced55a41c8020dd431db57589f33efbe50f6c026d071d10c981477a3e2a66f4be81d7ad35be5f465391d2b04c62312fd3fbf3ba0e920fe0aea3cb7ccfc59043c1f84d2e461fb3a3a76bc2b1ee7489c031a109c2b94abfc0d12525815207b6e3f64dc91317d8b88106ac6c9bea0ebba693fc53e9af5e9f308828835fbfcb705ecd99dd5a0e06fa41d630743dc6edb4914d735e83d40d41b013ee858348b1ec6b3a552bd4fa9afec5050c39fb83a2a8d27dc6427fe49f5fa02b7ca26361da6a1f56be1440722ad94aca8325f54101adfca8fcb1bd2d1dcac0bd9b826ae482d20e21d9c3a1bfc38d694876b28dd7f4133968983970d725bc701ee10da4db00ce88d52122599580ea7499ac9c461f95b320830d80ecaf1596403503f1d0d1e35669fd77e484067226052d2cea8300e272c100c736195c3e89b03423aac877122ac31b9572f5093075d9ffab3afcf46b3fdbb702dccc01e537700455a0013525214409bb4252092b8dd55d35d3ad20db089fefd44e108b30a6e05ac959decd49700c94427ecc4197a057b2b67b525b860c7ddd8725bdecb02a70fdfb68a017b5e4d00c91313cac39393b44b98fae681b265b8798417b53c1c5e0c93380bf4b0fb542c3ba2bb3283e06c79202e5c3d17b080580a299d88098a900f24fe9e29a0e882abbd7471995262d21612e57a6c4e9504fd05af8b77beaa980329145fcdf183490971445f0ddefbb57a71ab4be8de105673dfb66f5cb04cb00b0f6f4723176fb2543a2e49e0e61982bd3a9f21b7b4831ad28473350347feb900e01110245a83e2159038400efe6d158d59e7a54a96dc8471dde41f1a2cb3f602f10ee58fbd68b4ef8546db42ce33f605593106c8c519368c4ca90b5a7776d502e3b1e20bd726e340ca4b99d71ab3621ce2c2a2972cb53975022a32a13b07c90fae7297d1c8dbe87af6f275a0a064f384122cc0fbd22b5027b62f565b89af1a0d1ba7ac05391311b8ef47dd89ddc3497f26da3d2eeacda2a92ddaa77fc292fb01bcddbd27339045cea191bc020ce3884f86050f3b41858b507a7a60d46cbc1a0ff5810137e3874ac1590fd0ddf9df4170463af3e7ec59b0e3d6b444bda208be075aa4df4d9a2a5c6da0f3aef7e7aef582136786d2d646f4bae63f0bb62894700f7e5b8ad2cf2e6af4e57b94897f992288850b7cadd1d3a10e4199b2edbefc700fb108ce93e27cf6799439fc13eee8e8027cfc1d787f71abf59a69500a5b9ab60abb87db165dd0ff173fd93e55396ce83b42af545a5718fa8502ee3688891a3f0a408c2281d87813bbaf4da22b62825473dc21b2b1a744c05ab83ef4c273dfd60a899f30cb6fe160935e2bbb0e9cda24c067fc73bd07fc02c317fd79680c267c014f0848a9da5f6eddbed64746b435ed8c1a46e18d558894fc5583d634e63a220c05e426bc89e227f6c784b32f704af2516850ba5023629b0e37dd419af7df030709357b09271c9fb7981e0ceb42a1bcb3aa4c36540045fe428148fc0da697300757d2a93b028fad687528951c6d6214e20c502fe1fb2db600c24c1cf01041640934ee0401147ccab305e8d6c5a4161899e3c656c51641c657deb2b7af1b31eb0f00c20e44d405f884016da73ec0bc9e0eeccb2e4d56fc47d05f2e81ac5cd1000ea938eebdf065dc0172889287ececd8f5c9ae88cfbd9fa63df2e18246018a6c04b603468c224a8f1b7d4ee4a12fc534ab91e8b273fbe509326cceeee3f8826a00896644680db0284865e5acd1933d0a149a3b5cb3d06cfb5dd0fe3f10f5f8ca03a83719d0f52bfb8512572e9a73246016e5671bf8663fcc3ff0444c2d67dc6e04a4f50fa5adbbed232d8929c4c5df80812ef03c6d3f12b8833ea0b6352b79c1069cdc1d6cf7c4492d5da3d68556d570d8787137e99d63ab1d048821c48d7f4601079d7577b1c307a19d99760392fe849d518192f1604d7341d1bf72f4267a1e0fbdcfdbde4b9f362c1aa812e81d217cc6d1751a6bef82969bed4e3ba1ca4e3c025359693432cb7977b8a9d6b233e485fdb6490cbffac8e1ca516638ea4e01bc0a9ba782c1fec3aa2b0cb035e43da168b6728cd3dacc014073bdf07ad82f0ad106b0691fe6e3cd21c66eead6c02405ccf3acdc7bfa9f7e008ff5ae1ac0c9fe390137ab27563197846a64a993128d46861a7c441be3865e7d9f79cf2b5f5e9cc70cbb48d5f465cfe44e6de77935b2f952960a47df6385366064184b5afc0c3d9f0b5b1004a2e2fceb7a65044dc784efdcfc5b980ecb1d3f2098aef0e3c8d371300593934069ea602b5446eb98a26ffa90ee1b2e84c4b2ef8de2250575c64e64ee04322226311bae0ec9136ca7146c753ce0444ed3a1484ff1a4b041563f8d0831093dc322631d3a010ee78940b921739382625d6e390dee708ed67be70139aea60412013ea89972219bd34875efe4a1eeebb09e68d1e9da5080d34b29f7a84e660ec8ee1301db579cf6f926111a3f2cf6852da0f7a743774a4f2413181897c7530592f79b81fcdc92077f45841d08e1bf8952befaea53cc92a24e245a2d650b890aa8261185aa23c71aec3e78cc047fb608f69c9b86b66c91403a68b1b0ba6b360e75fc2687988e0f074f6627833afa6359f934530679b48b4cb38cb528ab2b6d02c0c64e5a2113c4cf06d9bb3dc70b1daeed84511ad7428487b57ea11de916930c4f205482a0358da55f757d63c1efdaf5c4e74d9ed139921b976cc3ce6ad0e200ab113a5073a81638ef971a83ded8578b77195de14a7d1b6e7dd7eef4b003670a55090c1a9892c82b176f1433a44859bb4284aa6c1757a8710e702f641275e00213199a8664e163705fd43fe4db7fca59b617f830da67f12e5b5486bddb612105ffb53b48ba5364d90f1339a7aa04acf4f58acda5212e9ea0f6acb27c79f5e30dd6d5a6017498e8c3bef657e8a993e71849aa9f29d90d9274010010c794449e048ef6ff2b5a875d5a43812e1db17dd80127106a8c1ef7630a9ab91e09c56fda0e6394c6a7d85e164029a4c306cb1605d80b717002dbe4c4b39921c2bd40381d0f1069dedac2a4e133d64782707737786b509f2f7451b1f7d1e5f332a8b48fcb05168b0af0b4c0d9169be84ef7daa9bc61236951a80d3b8bd2e7b4dc08f6124e0816e19786eef065abb952427d0d8c54c4ac7d1024de6d4a7589da520858a0580008fcf4e8c3ef82a9c7abcf29b25054f6fd02a0c749bad22dc14df4ba13f13b086d291a92e80677e6252bda853bb4a5db7ba083ecfe7dd548c5769a4b5d760507e083dcf58d52509a83fbce17d1a6f872f4ade8ca792480223fed514457bce60afe413d9c787432fd7f51d0effcf7577f5c47124d9fe38278630f0683d69583034a8829d6f499f36748ac3371432305a8621ecbd4412127aaccbe773aab14730c7545caebe51c07c508b1507d6ecac8d87ee2dc124d9038db0439a88144ca760f50c63951e1021d810bda86dad85cc64dd60431b7ab5dfad4658eedc83bbe6e0d4eedf28f7f2c3d1d89158ac59fd7454817df8d522b1c4037818f2d3f426d9a0c93e9357583065c76494cc9428e6220b00ea822a547cbcf1cd4bd77304db7c90d6c03ce83f7d31706c6b0b879fefe3a968bf294c1426228e150bbcd7b258e0606cb9e2f30a27395b118838ab15a41672f6ef5aca441e43fdb7fa225f2ba714c05c1487ca7c004e278a4ae45c3f83b88c0749f285431434567b320a73316a62601d8332ea0e2ced968dffdc62f8e016f0849e6694940e4734eb8153c1751c01c0e036f02710e0d7919fa910d11af7d9e92a1df602fd5d2d27054333a71052f8b08fac325223cf9dd6456662aa46952d8387185bb6bf698edb18314eec59985330289d5ebb63df7e2ced42aaf79bda2fff08896ab3eae18c1979945b92a1969ce0d54c2ae8d8a6d89d579037966575ac76d4e6ec01c7ec82db3d959553043009f03111cbe751e5e5e43f56edc5550ec95f8790a15d85354788917ff13fef0bafc03b20f2261742be3b79b57f9126f51e405e2874f6ce76ad2adcfd9160bc8694204de42cb262311c2e45a76b23d85556a9d768a9641a68fc013359e9f6825e3f10496359fe3bcf030a150d14f29f331f925910d5460261a2d7608d4e526b753340baa777598dfb11293c8f96338d9c23cd27f847d57bb39d19fcc6e02258a600c0772a44480fad6dcf85f2a691ef577a1729b89e400c5e1f4f3dd3b4894508d13034de3f320754b68b45f38125a0476f27d1a41e2fffec27a9d301cd7c49994b70927aaa2000287f53ede731686ebf698816e8eaddff41da7eb0ee87a25e6ca4809376e609f1d891be027386d6d13e6b0af0ab25ee46f3688094680e3f68051260dbc973a483ca00854ab7b8753fca0ce0270df94dd6f2941205849e255eab0b704cb13025244d2ec52bc25dfb7b93c6f7490587014315715a5e46561b1e4f5af04a767e4877a483fae6598157e4ee13d22c05ce7a5e33365d46bd0768c9b72570f4b9bc7a56b822dbb1dee13cb8f1a0b05297aca239e5d5503266477fbc67aae00f2bdb06e26a6e61137dee884c3755e7929276102ea7367ecca9ab21ae507fb0b2c81831fb56990da48b76ec994c2c295f05f3617671c5f633ed6877ec194ce09c7e05b325a9ea6932f748c3d2893aaeaf7f18c65f04cccca840d437b1712900ece868a1ac0c13b77a023a7d71c3c267eff8e9e8a48421fee0ab450c996a04704b4e538ad9c550d482648b1ce4a8ca79a47a63c0bba725bef455ec220ba89670d4476fb52d7f351d81c0e98c77de9e3e91e506f592ba7f9626fa7af9a0cf6f40f313ac104973c69a7839fc5336f1433e88647cb12fc5e00b5f6f5b24a96a9e908b99c706123505dafcc132bb832f9a23cc61ac13c72163564ca11c701ca23fc0907011c20209090114fca19af4a45a411cbb84dbadbeedd292d2e6acd42f9ec0345f4f6686ec48ccd39a837b7c3d9992f75bcf00a436980d9bb6aad9b28fdf90eae4872d08f107d8a35ca474a1fa669bc95eb5819f36921203051f117f74a25027a986f46fe9d446832323e2ee123cdd9eca50db93e467bf7ea8e4316a67516064354268d14ec1b3b2e3828e300681bb3fe80413618c91ccff06a8fa7a3ac190abef87f5dad7a934a20227c608c6aab1f64df9154b513fbdb3f2be2930dc86807c448a6794b335ec7034ffeb687d26b15983232148e6ac7d6e0822c976ee76908343aa2235b0b2c16bd32622e28afd8013a59d629c3699edf90f7636a5a12f30169c4e9772a2266d1f7ab8c8b35d24b4e5ff1b435e33a443e308f3bcab085c80343c32fd3e6be9666c208b1211b834d63038342719bf248b0554e7745f5d41b0da05032b100a5e4321be1ce5c6da5fbd936525bdc41fd1c95175b465050c5ba03245b1415706cce1d71899df9f1beb5d36905a3199093b3780e0df85c7ac2b0040bcbf9af5abab43374917202415d97c4bd8410b6255a87736b7d46f14a0fb3002454f41bf47f35c5e0f5f8640b4a25bce55a3bd9acb714ba78b5e0561dde4f0e011a4f907f0bf2957332da0ba9b9824367846765db9b76eb104c227093510d03f83a56f6002ad88cc91ec88fdd641b921bf4641023a0d6b22cdc5799a2ff7a08a6bc12afb72c402f473a28db8816ce4b464f33fc2ffc8ed780a10d93adee640ae368778a64057565419573353fc4b2e674de71d7a5e9a39ca76c7c5c65968e038c01b722cc88959c62fc064a4ab0523e24b1729ae4c0c85a51c0e1fe12759c0f2a5a800ed458963bcbda3a21959be1ae7c76e9cde048ef7fc9cca9013c952c04ff5041c61b3b775df177ba0e8bdfaaf1304a7d4c7d8444ba1aa85b030253c00cb58dd57700e3187f32512b23ce1ce31a8791df23dfd33cdfb326ac1ad9bf420688cfc09d8f1ffa6164dba8b89ab5339eec2427698d58deea2cceb1aac3c970001907baa1e600bb107bc3372dc43ce2199594dfb232ad4e9aa4f87033a4286b02cf2e79a67a2a64ceb47e0af1b37c38377ae68a584e362acfa5b6d5f69003f6013fa6d8043c10e4a7408c28272b83f87f9d52c5f48dc530b57771aa82bc6d3a0889e57fd7a57dc1780426d21c1a4bc2393801ee4f54ba1bb72502c9e7dd4b850d28feef786d57d86e1e059cd2218f392f319bf9ead3b71e46075af76b6d36ba0cb819ee39bc97e55fe287159037cc0a2bfa148f172ddea7b1caf8ef344e4c030c498f4ae708d4ae9425a7ca938ef519215f3277f120bd35a18cf2569a6e96970f618a3bfbe462ffd0eda6345a114da8284b37ce4898ff885fcab7b9aa77ae5a07f29d8daf453b966f2dd43392f59b3423ee1712c3c0aeb75e804e6f845609b603d4f0dbf31a975336b74408a734f773574c94e4923a4c62fe08522c2d3f24c700e07297fc3fde6a0022e97bb63e1e7adf4e26383321e01f7e0fb3fdb15e6ad50e7db26618cf8c74315f1ea0897540690d7147d71aa1c06d014dea04f5794374074f36db0127146a675c72aceca78c085124f8cdef694ea9ed84f72f52cda562017de6304491118fd236f8de7508370261634267424ff88b60f54cc1eefbbdaf0c20aab5fed48eb6cab29809da6224aae45c434e9a010ef9292696a7a9b648f40c48a9edb0cadd282770643064b7946a6cb40baeb471c196dcebf766bf78701d0b0c37d17325e80324a7e7693a47264d8d25d25257ca0364a969f4fb1868c6890da27f093ae379991a49a731b4a62884b40a1142dd9ac5e5744fe708757476d308f83231f9a87686e8df1e949db581d42fc955ee1ba4089abfe1ca536221f0aa08a26c0705d5743a15071f27cc2725da417b592dc7bfd7e7951f640cb537b7aa01d204e38e85d8b69dc11389ea3144bbf8ad4352514fc3b9cdac497723ded624021408b3bfc985c630b32a6ffdad71a73abab98055af79b4ed52f3822391aa85092f9c1de3ef50be731b1a5118388270ea4068b0a7661be85678d0e66f0c999203ffbb0d3b891cccdc955db311776422fb6ff8b95d74c92b36102bc5a90d2d7f0788a3cc5503c74a6acd8adfa3e4aaa5fdf873c3bed61f407828e40f2588e91408b75876595d1f097d21bb1f0c507630e82c76e6f6eeb2e10126cd15895d5cd004751e5f805a33d54e757cb8c6d567b07b94b91d51db02e9cdab3fd031ff215707d42ffbfba93dc7aa42187b5cde6a52010db52920209bb80e3850712b95ed770b01d053054bf58d3c763fa1e3b64ab371e7615796cf90be2b88a05ad3cc35f207599487bec06bbb2dd0653f208fef3e30e40e639bd9dcb1f894074d16c4d8b40c38c3ce796a0214133d7b8ada6e7c5c0dc86c48bb58150a5de21c1281896833046836883d4ab71534cdc3efd1871ffa494e8cc345adee512cbcc8e8a7aaf9ac0fcc0004c202dc121a7c57dc27a202c69d82163b3e69dd67afafbf3d221fb7a9074c72253610e6a9159d1e50734b8ada0c7842fbfd589d0a83fb23d1652bf1f306a96f5345883ca4d4744f3a60b5be38d5bb5cbc5099338c52378dcc6bdf61c50a3a17098eb2a0d3c8077b1087e505e76936bb4cdf79589d1d3244aac3f9abe20efa6c2c98690dca5413923d6f0e4e9346dc3d0a39e110bd491672995dd480020b81a773e7f790374a0ae32f99d04d906bcfa3bdf78bad10559307be866427160df6ccaae56f5e70fb444c0c969d100d53f482db73e61b2970aa101af12102c9005ebe6d51de676f967ba15782cb7d3f5d36100fee7239c79e92e7709072a1980dabfec739b28db21b8df7300515dcd6d7b3644001ecccd9e182fa5c079e92a108c3b55feaf216e09c2c298167c8c8c998ee60b6f634acd4360838c853a133f20c6cda6f9d147ad24734c27129adbe9e09bacd46c0dc335682de54aad9ea9e7b00ff0e6e693465b0438079e2629b5bd65596590a6c83996d70bdcfd3eed0ff2b0cc8a63428eec1e669dc24e3067f2be08afe1247cfa079c799cb0bdd301f3ddd026678757599849255d6a756b56425e04e2bc4e999f54779ff68d2bcedca3e370d44fa306b0a8772eb110b215ffd367bd6f3b8a4619f1fcb5d2d13c1255b84f103ac8bc18b4b2fb904ea02cf685f34b72cb16b9da0439d7ea6721a0e88681ce5078912f9467b9c03109fb88d4aab2299b83fbcf137131b522ba318e9ad5d894706a8def66dac72a27e768fb215c92aced9fc1fc935f32c3294d62de9607040a60ac361121543a17938347ccf67ab68027be5a7711544617fbeed26576afe1b4908b3f4de45bbbce38f4d659f49198e9ce24dcd95ab89945f6bbf7dc9a9e333410f5ff02da0ac1343d09d31d0e44de031b676a953b8b0e2f75083b9c7299d354c0f71501984aa35d0e942075645dda21d60e102959b05d2b8108a10e22bd97de80b2f1121b308a9b9a09338ee0a252f802123ab0425c01db31d367f6cfbc7d19b0e546185cd954e903ef6537e12e37ace620599e42c138839a2d9721589c5f22b0847c138b5bca4ffaedb57123259431109e8f177111ecffb044deecf3a74450f03879fee47d77bb9694889524c627adde5d5268ae467fa350a7823ceb8d28203066281cb08fe13571603c0ef143394bc00779b11ad8b14bd73a861f5613077ec0aba6f786a73c90b5941cc42e8bda8c62fe607db2eae865e2d8c74757d8b1b6000bfe10337fbd4a145b5455cdfaf746b39d6e3f25d72cf65d392bf6dd30d0748084cdb58d31427382ace25e01e8a69e38e85557cdcb91fdebf8b2ef1678f8d210d0991532e0dae6c6530bfcdfc187619b649f87a3072f1b2c7e8478df29615fe04cd0d09c38de1c578114ab8e1f7007d0057e2bacb527f9679e0fabf4940873a0b5ae27cb1ff2826aaf6efff5af60a90d7c2431244eb2de970108c438ad6a97e0ec52745209094f03e7130c34bb51b6d6f4d70befd00d990152fb732092ff8ae0a063a244ab417b76f92cda23efd03519a818e69a75ffa19b5d02cd49edbf59a0b04c97281b5f1ffba78d3943d73dca2fc2f1b275f3f5a492464f03c4ec5cd3c0b5d7702ea45de0dd16db992bc560c7398078a46e7ecd827100e7a59b1b7919901d766a20fcfbe4a99c0908d9c7cc7d261ea8fd2c56fa2f62b6b3a3fcb9ac56401f4858a8a20d46c4580c3584c1ad924a5c979e61000758c1ea5df7abc79079a032821442009379c590c34b97265d6fe12cb29b71653e70aabd803047ca7724c0b85b534ecd13a94e2120daa8b2797b15d2007cb081e618fa91c1daa8ddf6ff50dbee793e23933548d0a9c308e482d00018a6c026dae7dd467a68d8c8d9608b40e67cf9921dff682d61f0c80b81ff4a182876cb5cfdf06899ad0ea91c8b668f500c01726f0758061413f92bcdca1ec26b8696eaf5b4592f4005d1db2559f76880d3c6f5bcea3e13dd02a913798adf14e9f1f8e45a314be23435e53d7930a2e80024b5b158fa3fcde63b983c03938a3dafbd551da42965f8dab74c7d3bb844bc90eecf929901b59730628b95371fd2f8e235a8303142a30bbcec21e77bc353412043477e14fc2016741e7d44f71f2c7feb2e177eb885152891dbe520e4e7c8d4100f133728960b2942dab40120acb077858759938ffa7eae26760b7657dea43370d96e0f98b0911ecd8fcbcb6afe0b24585d5947b93f55d0e0fda93846dc0644e0796781b7f42f7abd081e8f324f29fe7a1b8755c62b01495039ee63857fe70eb009d68a5f05282b1d6c4f421144879115266ee85084918705be001c58281f31f0529a6e8f40df0b7ce7ab27b94de4642efebc2d5966f18335bf842ec5e4e3abd0464a7b6318eec2575b366528d9df66eb6a8e1154604cb587b84de4a457100cc00c42d7ce6bddb9e251430755d4cf1b4088bef804a0b4aa123cfa9daa078f65903922e822150820e216470ea5bef493075b8028c14315204efb2f4f4a8cbfecd014154b13e177c9b9a254a465624ff98a0f29610f10b73b672b77a98e54e937808f2bae9815f3af33ec826299f0c15d96ccb836c7d4dc1b09a0c0b02b68a046e0a7ec23188a4f89df7358f69390f991a03943d5cb9496a4ff34d5b25e43c0ff60d9551dd23bed60597dfe92f115966c38af5732752484644def6974c767447c701725331b94cf37f8169d4c02b25a1ffe24a3cf10b89857d39dcbd8bed2146100ecd9de92efa10f9d343ba48b37b0a8f251fea9f935d3d14f93c036a9c06e525046766a146057b6a2770a86df34f1c9e80e8fa463b48e6badd7ac0425b25000c0d7d726c49593c18ce4ec3515fc1398d413ec471cd79740840f66b9d1219500c0a665f38815209d464363dc3decddade57400f912391e26e06386d3d24502d2708fbbd2abf38a2a771ab9c3c11d6a5ccd5f3af677ed1c3d953230be007c8b83b0590a8653ae1102799173affa273cc4bf78b3f16e8a9a2cda2784659ba2a0349048f78bd547acbe5ac6e5c924ca73213faa84017dbd2d6718f1e8144ce8c518703a933161bb9e54e4f253eb8124b7ec8031a5a4d2d3c381d8d4c19d325f46e2e08d612a9a2be82189eae40080a8837007da47faae0225ce662bb42a2cc1e1feb067664bf87d53e94e8d48f0846a106bdfa1173040be9f8a8f1dd681d39d54ae60e485c4ef5b1f51dd40391c97352d7ba011668aecbd03c6e1b1df6f7f9bec1290365c7158f3dad5f48f5c9fdb56418f415a55075a6e4da4d2c21ee51ecbf2fd70fa4172d917805facbffc627da276fddb09b02b4b053631ae340b75c3f8a92e800e2a4e2ccbb0b3c4ed97f4ec2805944c797fb49d2e8ba35ed3b93a97f35f6460e12ac3c2dabebf6b6d56ff9a6af0aa7faa4e7b81c812a0fdf0468e61351546703171030e4e3a6adb300bc9b5bfeb4bb84c943401661970c58a527874c47a16f09d713358b9454272cd9e0926a3dc08334001f1facde69e0f9c8c80388d30d4306ab30f47721a3294b52cb8f62f1d1994200f1f847d84e88e3c293f9f70f9d4d0d68f0df254fa704bb3fe91ef20920a3eca6cf0236195f399c770f7e199c9c440438908a788f3c777398978f253abc75884fab73ae43af1fb5afd2e61f4d813f0e3b75a3510cdcc02580d4186c5b61f551699452b4cd2079dc0c34a8c86433ea0d1f674c0c4ff6f639cf9c6e21a79d15db4b378b5a56d5f520cc85ba7b0a880c09684557cefaeb96601d3d2bd57cda82e09a867146115e30cef00e852ba44b0a044a8b75f2a55f9e87e58b933afe182e110c5fe5568d5d9143fc2fbbd4bc53ba0b7e45c5e2e915710431ff4b5f63e549ccf8aadec84a8b9da5455a3a88509af90ac5eef16c115fa33bd18e36e4ba86bccc262b6724923fd65bfceb2a1b73fdd600ce81e2b8b0e1b6b2aa571625d28e68d3dc76ac070a88a538cbccb5e248bf230b46212cf8fa60c826631735b9616938538384814ec06548ee191ea6a82d25aa0250b92a5c01bc2e1cc73f30a12efaf84f291f39c473c077b41f83b1abffa3fc0a5d572226c7f3bbf8e8f81970364f112acb55fa51098bce16dc6346d911e7ab04a9fe7c1c70430f2e23bb7179e3c5d4103855427f814f46c38cfdef4b297ce20c6327f7b102fd5a7810f17f5d4adee9b016e0d79a09cad10d24c943ea21d34e05a3f3ba7541ec691109dfc70b65a10667d5073bcdfddfe2cf59a066a75a92eb01549c1fe1242afc382907bcaf08a93442941d2b5db2a00c9326ea6b9504ba300efb1f95408be8d7c3be0b1cdb19dedc52d21bd8f7cf4e9a6f810a6b7917e3f5059e5eef9f289725e849aaa825c8dcd1e3cd3022fd905cc5d942aa24b66cc4f50d8785df03c5c271ef3b1612b64cb5628c4f997176e5845cb9f16904739a24ac014b48c1a6c7840837d425bc16ca237eea459cc86aa407ab78f2f80d5776cc64008234420541d0169ecb5094744537549f68f93c8c127f42bd7926fe99d2dddb029d57a23fc10a7f2c76e9e1776899608953844a01ce7037064aa9c68fd81ecc0ce843f1e2a582d3700c8b3f1b6bf887e3309f6f4e2b3eb288d5fd68825127e30830590a113b6b3b5f2aa2cf6e19b0d1b80b9fc9a3d60d2bee6a86f0f1d84c170eafbda8da15f2c8c9f3aeef1a06b3df1c9d8cf7034aade68b2d5b2d14d654ed029235988180d86a970ddd8c6d4618506e8626ef2dfc50529c7c1cd239783f6a05a4e27d54b9cc5212059a7c6585fa95c75af893144db1b6d40a8899737103b50e59da59887389192ce74ec658589c327524b9ae5b9ca4c39c7e4f35ac2af6390250cd42e63956359114569df65c8789e91f0f571ab711231cbb49bdf206de7c03788e73c5525334a40b4ea1303ca4092ad3cb77b7b49cb5c8aaabc3753b3a3305e2af328766a11b8a6e3e2f369aa015fe992b46b94b982be9537e657e79d4cf01bcc9af5b15d707d5136e3f801537db7801544a0dea1d69d0307e45f018f0c806868f3e10ab7bc556469cc8d399c6cecc20a708b6a28c575587265136d6a10f08814fcf1fb2b40462af4c27ce1b17b6c3ba6c78814e025b73f421efaaad33700099adaa55baa69bcb3e7c4f24315cf6051401baef0801ed6cde218fc5521c91054be51e2addf5c0847a1b93bfa6044b412e8157272de3e3c38b1a656df32f3c07e65cbdb53ae38ebca69b06ab4cc5b01f405ee4deb1aebfaa4542ca248fb92603172651209f572c793bc30c625c653b2ba11a70b3c3a76cf637ad0b8cf8ffc709cad925ad1a534598d370bcfba16477ced0711f3e5d16ee0f3c157005c15e310099334911bcbbe8890622d41272ff6e6aaca496bffe4b0a464841e44262b3680b9ccf65c27242d1fbe54234cb81db59332d5c2787d7e4d251678c6a8fb65f6102f5d80fc56e08d0e59b631e79b20d75a631dd94a9bbbcdab3f14c4ce3b2ae810d509ce8d1015c870a64a573847e4300f53dc427a54ce4a57f09af7d538a76a60d5159d51db70f72ddbf46546685443a7ab78e892ccece99388a8ec003999b560ae8296f9f44ccf650d14025e508f77041172b6f18d7e4b870d2d05f81d195840f1e32f72507b79d320ee12bb8aa5309882684e73ed9f430597bce4f6cd9788903849e0da822549feb20783b310f155338d1519cc7eb56c4e57a982881ca0df90ed8bb762fbc554bfe0a1b807f63e6391fe6b52e9a5e5fd812798e3b48c3f9ed0a438d5bf1006036352b0357003738e80518e30260aae879e060c7c5d40d68920f32dac881f6ed729cd86b50312851766d3b25e4a6507e266b78db7e8f31c41a00ade27a2547e7d0313c8cf0355d19de3e909d11168a61207dd541250c9d40f702d7772a64e93f1b8186218fe7656ba75cf7d88d29617b7136b7ad050f68d0ff0e98ad9dba60a0d26f5d4acb50fa03c34e1a71d43f043ba112312269e941cdc5074547be6990e39390d77ae3c747e79c589401c0d93214b26b80dee2fa387ffb0440b84244ac9b40f379a02a531a6baf3222d94c3a5285ad5df7d7c59e41ba6703695028b8231638c6614ae03a4de1d949d61ba8d2e69efd585e12299a6866e90ba306a306864458952e4d224c99deab327ec9b90245488359e372007b7fab5d008d7b92edc1e94d4f3f1f1254dc113bc33c425843c7856eacedd0ed573c8cfc07eb85bba3261e9f642d687ce3bf419a69081918b048600042ea6b7272159ec206fcbfb037289426b721e20bbb3bba4a685ea060e1a24d7d24fec40b5781c118077282b8d55f8482305c15562b01b953729ec60392e350018cbfa6572c81bce80834a1c4120986961fd91d88cfb9456bebde917581e2c3fae2a17c74bcd9033e0f3b57ee4b631f5ca58c618a7e0da3b581659467e498686249860733613b47f004 true +check_ring_signature b77c0902b90e04fe6413a69b755a8cfebc02e1b43776c890b42e2caaa748a076 edbbbad8f925581aace0f75094aad1a9a96a1f9256febf7d2f1871f8456fcac0 1 e181502a85400da90272192b78678515a8ecfe060a7fc3f9afedb86b9d2c6144 67e1b80ba2ac602620a0c1c25dadff007560e570cd3e0fad2c14ad62bb82a16055a9309e6b318c9bf612c886964c35c9e09af36dbd9db979ffa149c1133b7f6e false +check_ring_signature b478bb733931be3874ea4a982ce43ebc5958b8a6ae6c2024804d55589d052bbe 6380063429a8dcb91e86615f41ce1019f7ec7a308e45de064460e1e2daa15cfe 4 4c81d3c1ef8c2b8e1d3f283d0c068eae285ff59b6dce16e314cc6edb2d7ab9cb 6418dc81f2383caa868862708c6c2a3123dfbe34ce62d39d770a6de085bcf784 c637a332d30e78da61e1b4d8bbc976eb42f8d2d5fd25069be5afc34f2717c1c5 ef139ded4584cb1691edf6679d7807562a248a8f05f89649eea0418a01c4db9e e33c6ed671dbd331e8a2afcfd9c5fd7a10304349266ab92f359ab7df49208d061c3b5823b8adf07431eaf2a7d58ea5516b2ecde72b708c0e2379476bfc3f5c03d3b3a7f5653737740cf40cacc0cd13111d3efad086d4aeea68086beeee62470caf2e66b0991c13de9ede8824e57985cc39a4a92280afe500b6eaf62d7a9fa309bfdacd3fff82c90ff9307295f17276220f36968fed38041b98f0b6f3244e8802e09c144326940be35fa91b9888d5b4e234416811771a01def06dccbb7d4eaf0f0e5e9634f81d2d2377f166df61ba9709feca9093a1777de161602f0dff04d50df633f388f87d797cf7e90b4032427acacc8d06cca74a7fdb74e127a7b101d705 true +check_ring_signature 667dd4d864ad6a1ef9dca0e8d2286c285d3a892aeb9bd491b5e6f1886cf0d208 757a955c968af5de7fd9a7b3a29f9fb66147e562bb0a2ce95b6e2b3ad0580c4c 1 608d3604a28457cfc6d1c6638904fa4dccbab12220f1690db17c2410fccff08d e7c23fad16b3b58fd3930d1aff304bf6a618ad0420a3ba51254b7486b7c58604c3a24375ab90c2b235b0e428db0fa4031173fab13b1666b34d149a38d59f9508 true +check_ring_signature 24a7209802b11d8bb17071f22cc168ab5bb643439f41c5272f987692c675e919 b529de159753b04b9d32df5c282c45e6d4da7066a35787b8cf36ca27f668865a 1 6b22edf706a89de3ff3d2e8d775b3868659e19a7ff29b98428621902d27641ea 0c7e25b284b0c2cbbda0b76b30e410f15133b4f08e0c37040c4e2046406fb60a1925dd9ff67d331748340e340ef74531157a63f87b6374bd2c1e59d535b87b03 true +check_ring_signature 2030a80e7aef9f5d4eaa1fdd452dd756764f66e0b1f8debc428b92edfbde3e30 e51d8cda7099049ef2a83f6d0d0e9317a693d4c876b444cd6f5ff64a2aa0b741 5 1f93d19a067a84fdde20e7d50b19d0463700ff0b199c0a69f53e348192596375 cbe323c974fc404221e24a9a2186aadebe8d598e56fa098c231e437429ca4172 32936d0b9f4cb1975454c62e4ab34b1ddbb3f464031117b31be7bb66e93fb2ab f93ad1906de3c2d373c5ee257d2e6f6fc2b3db46f6b01589d90f01a4f64f6ebd dba50134c6f46e7cf44a61f95f59e1612063c25531a718264f6e4da648d3db5b 97728646831acaa1f68b9494bfb0cdb32641913225d76f4ee8831d79cd7c750a193073486c17c6a101c39ca17fc5b8f57f8716a4da80fee623c1b9c3628f4e0ee67061a5d8020b081e453e1d1c5342c31be74c1871b232bb511b13747c1f280aad6b89b8a6e083bbc6034fd349e1132c5ade9399a0ad9a7e1974ddb462f228044cfed655c5b3234ccd8c131e7a62ac0173d38a72207447bc9dafd4d2a64c8001d7257676359b31d62a7ede97976e3c2f8da02bdc047b5e8e3b3a2f7590959b02055c41c02dc7cef75c535be7d02aa5f52ebbf00d7a7d8f9c8e121d145a33150f717413322ddaaa73fd01c19383b011faaa760b2ac70932e0e89c83c2b2b531031cc022051596498b9390df7ee755a1746b515d00459644d0e9969ee91073e70b8207d925101711f982b835580bc7e78512f7fb4d575deb4282aa36153f22a90b true +check_ring_signature ec5d8ea9bc2ca27bb1a37bdec6c346d07fd6bf6bba787a09cb21991e5c372278 a2581cd925c2706e7c9662ead378dea0f821c69f24b83eaf1e727af8692a1137 4 05283e644eed352ae376d3ad0f4d5a7c205f223385acd6737ddced2148a84609 2a7fdc0e8f6060eed82b6967dba9baef0453511bee9152f70be2e90de78f0871 f53b4ade7a99ca0e51a6949f5df5afec0932ab1b78892cadd354d41034451b36 4e231396c51197797fd7cfb119306c8eb7a6e063a51a180e5c7bfdb8b3fead27 a696441453f6e2496517eeb03298dd205df7331ad533581b439f428daf7c440ae2a361dab1ab3ab648268fbbd4cd633a01fd724f16373b641661bab707a3d90ae33b5b27d6422c7e7a87cbb4ec69bd655ea4cb72e0eb9ccb91b940529dae110ef081119e41ed67678a22d45baef8ff2cd3499747e6a7550c81a1dbf28a0803061ba19bf12dae3a3829bab4e290018c78e942e97fedd017bdad69e8d54302a20a17e18640042a94dc2454a1e5232c06832c3b5484f50c5bce46b64edba0c61c05f49c80bd22e7278c3d3d492cfba881e5c7576e4d689420d97d44c2313faacd07c6d2c81947fc859446d283d563f7527ce7bd6a391ebfb7137a70cc364cba9909 false +check_ring_signature 7bd3a8f6e6d182ec124ea5236ab15fc5c7ef4fb9b1ae374d4c2de6dfdb14719c 132d5d5df95643524fe235c0a9f4bd9647667e83b84b819fd63e5ea4015d761b 8 985a03bcf75e3783dcad7f67c1cf1d7df5f1e90ceb4c15224f6e90622ee2aff9 b7ea09945419ce960f30e2bc1d21eed8e90fd1acbbec4429247fafcab8f40469 e618631d6e463bf2ff9c4ff8a62ac05a250390ac4c4b2d1760999e6f1b7c8b6f d2d85e8ab40c9505e76cf7db1033d922a5432debaf084a0f1789f6e5647c779c 043bfaa5ef5d654c8d559f1244926ff53497b02f2c18f405b9e8489993c46b7e 61d7d3f3d93176ffd5c4d940e58bed1bc50ad1af47bfb56125712df6749faf21 008cfc50caa10e7798bd72e18898d3d5c5322b99d07f19a9cd3e670d4481f575 7885ccef607869dfda17ede2bc8970bbf98b585de07c72a8ac10b7baf87368d6 d330ff542c0bd5fc5ada15ab28d94a41efe68746c276fc6b620c73595611800c332d5187c6207b54f1574e087da74b62baa34c1659dd5d821a433b1f7ea5140f612c05993b5292aa7929262fd9c387bc3b5bf8fa005a936362dbc46582e987074c4ec26078379dab27cc7c5dabec7d6ee658d29c3032fd56e1df409a5f2b4e051f05020816df9828a80e23b8530bc7f648f3a804296cfc4ba3c5d04bd06f2e03f5a09ecc04682fca9c6680866cf74f940f63f3fe2c47f5fd9b91c17bfe60c107791cb90667089fe320a66e4b3cf74f764c249489a7efeaf2995ceb7c86e5990698e6ab838825a4563d8a027b55985eb065582d4ab6da9281183731a2ca3c09005bce10618aedc4bc94cd6127e98db4ff8cb5140851356f802c815190c8bce00041b751d91c2b14e0c691c3d2ec0e04374080bb584af1db7b8858be460322700ae8723e49971fb9f0114378c3c3751f0dabd5a052fb474a5372263f2cec82550eb4ca5479b19fd043ad31d4e482d47c197a6fd476d7a80f4eae7ad7c44177ff0b55cd8ff6c5ebf6d788820dc00a799c0e931046d4b96dbbaf86bbab61ee086009ef7acdd5e84ff24adcb4da195109a4c3395169db0b5973cfc46651c79f3ac405d8d7e84befb03001bce15886e472e767645cd7d9c92d2a205a80d34256d321047ddecd634f379b989825e69693240670c1103626741fe387b3736334d010d40d false +check_ring_signature b54fc489f2628ee3c2b0c6fce1d32e89ba6123f00f776681a680207c4a442a22 4dc5a3f2b446e3842844c544686bf734089b96348ae17c89a958908b2634628f 1 f32327c4c77c93207570b2336650a12c2db298f90b01c9fb111bc0d7bd27aad3 90a94af88c947d5f1641d5ddb03d212a73c74ffd694df2742a63f9f6d1378604c8e26ae5dcb3e5deba9d46bc5276ddd673c57bc091a2eedbc22bbaa99f7c23c2 false +check_ring_signature 2f0b0eeb5feba19159168b3a9eac46fbd840f4a984bf1b4f310e6dbbd1cc038c 26d04ac980ac37c8b50d4c866b01fc86938def025958654ee60812fbc1fc71c6 1 00c5f42b272d99415f2194b8c7fa5fe7e78fb14cdc19805c065c4287321841ce cf565554f845ddbb1379daf646716e3f04a0b77194ed947ab0792d4554db100bdf5ff0e315d3b079d6c86506a895c35dc6d90e72fd31163b2b7096a2a00baa0f false +check_ring_signature daedbdaa1b2b1f13973e3d7cbd22ec53537858e50c0d116e11cb5a45a9826de6 d251975ee62a3d85ed140da4f16016191b15228d02581a1bac23abdcb3f0cfcd 1 233a9bd071cbd2bf7c3ec966e0b7e618f9a0aa36f1dbc18332eca86367e9ae52 17c9d8f9ce380dfb3038b5d2a4d5dde42da6504c3d7fae17ff6e4dd7b98c400c91cd17fb07e17a66b98d53e134ff74dfe8c6a94f158f286235e36d471c29eb06 false +check_ring_signature 97f45c639b515f818187f78e5b1158210b8c1a09a819084872e5850cec5ffc43 ce827164f8a562e48213a553809cc30a49c4e6720c24883d5807cf6fc9527ff9 1 4f4e15a313e9d2b7e038a457b1190e834f8b526cc23595f2a635a63d99c167b8 1a5a1ce31173a20e92b92bdf5bcf7da8915228d56844952965d781c3ca0a9e0406b443b632d13763b32e52dc52ebdd0df8282340ddabfeda23df9b8a4bd1df08 true +check_ring_signature af005a64effc30292bfeeb33c4a342ed3d4e41f0b9363e9dbd23b8bad0ca55a7 488e00bcb7185d8456d2ed74e23c31382d21cb067777872012508459c4fa3c1a 104 080cdcfa13c86cd1e3774d950a7f6e4db9d71bf2918c23b268b0657e7c2c3267 d9cd4e2990192c0adb2bbd657854fdfe70007d715018249c24d5b37d0f9c64ec dad9b8fb6b30b602d480fdbea7fd683d9b0b8c0474ffc27fcd2386c67a8784bc 268c32020a59170e32ef4718a338fe45fcc922bd3051cb078fcc6cf896b87db8 9a4e9816adf8182b0034f99da19f70f017ac741acae9b74f60b8e96c43aeaf5b 051df0334ee45d13b1e39a057276afe3ed3ed5e6d5a679447cc32c814092224f 05a591efbfe8f92f5be77795db307139606706adce32e608f3f6441ca521099b d94d7d2013ddee10bfde6caa22efbf04ddbe9aef0d86bfbbb0bb6c874b37dfe1 413faf26fc1a4533c0cda1c2b90b5b4b1cfab322948eb7b4d48af01a4f14b3b9 f972a2bd505e79142b942898df7015cd95b6d3f5aee1fd75593489f437b1d324 879c0c047e601a4bba15f03be4be8aeef414fdb8f71596708cccc7721e0672e4 7d10c5d6e8c2d8a6a7f79aab34ac2e0b4facb60f10dba105e1d87ba2d22eeeb7 99a2263afa4ced92f4cfc11d77ab19957d651e1dc8400507ef622d34af7860fc fef48dcda68b04a5becad7609fe7c6e705957bcd376be795e619eb5d7260b78a eb655b13294fef54ac5b647c63bf1cbf3f5735f54349e94347b93d820cf3dd4b 73b2129ce6df03b1e38903fe91cafacd7e33fc564a9d0180aa417c43b924591b 7cc4ec9357bcb3bb504781ed6470ad589a012752d5392812d10902210acbe3d3 bc1a2d723e1443e1f5b4426effa8620e01e531ee2cea260a6cb46a1027ed06fa fdfbbde7771c48c62a09171beb9e3771974d4595eb0625491e0b853896a3adb5 85856030c07e916c6a39d563e605136e02b651bfd039e372161f6d3a0ea5fe0f dab0cac601d55ea0edac5590b474b0b94da7417a6e830af9beeb7b33021c75be 7b14194c11d706bc0881c6d7d9d23e9fd21d1f76e9c1a96371cb62ea5c17d8cd e86342c6924ab402d582f354c44144e67fb7c30ef6265e38d322c371f87ad19d 1669840dd0b489d80c91ee4f2b47996ef10af43a50a382f71d55839a45f5c187 cbfdfa576dff598ec8ded97ed9eefb2210a17fd4720a3f56c9c919e377066044 c34804d19bcf5b5212fefc958a55f4743d2b3a5843e7a8a8f3e0e259dcc74dc2 ca706a794e50e2c9e23463f4b1c8d93c2afe21039123ad443f7c862deb6ae7c0 eee7bc1cdf950e37a1c905600cc5ff50b8a3f9ca7862bf1271db3f4d5a7754d2 b651b4a5cfb767ea15894abaac74c88b993bd4017af931b045e542b63f9ca475 da0eabf143cb33d0102c3ba21fd47075c98534d2eaa4b90cef242eb591709bdf 0f55118e24e47516de6db8f7bb44b0c1d1b805386e75ea6ba5f95be5609c0543 a26c693488f01975a6bb1e0b0e53a049486eb80f1c7b1dd171813cf80190a0bf 9d5f2a9a66eb634679d51ad2a31c33b192c20a23e7cf8eec4c34909a3dcc9221 52c9b6e71124c1eb12b7c0c076c8af3a7007931a2169fe50c4d95e9dd921a819 edc8246013ef6718dae6c4ec1cfb5fb732756085e4d4e6d46b3e0a95a27772c2 a97904b5bb943a3280b0e42a165eeb4778ec3608c29f8228943bb463d5b7c5b3 681f2462ca2d46f0bcc5321576f6aac5724b13c8ce47fe350ed1c6fa14f31bc1 46cdc6e7ac1ac574b51b5595748cd192daff526db91f97fef463d80b7a1287f1 5b27c85f3f8d2109af0e3f2fcef5064bf55a25c9c951dc28edb57833f04f678e 4d35659832036a91be0a360cc4f20b84690f267b4d832f2fa33eea3e292b30c5 df93e88de8e8e909fd315f751f437075ba077bb8125342a594068f06f5ab05e3 910124a5fab2f2c522805db6c1e22d18b38776c94c7a9b3c39155ba7efd09c9d 0d84cbb60feed14771f08b4c1fda9d38ac2fcbfd4bbbe03045dbd95d83567155 3b38875db5c1a42294e42e5522dc4c39549240ed2ec6db52af50a36138540ca4 7beb2eddfd24f68a53201cceacdcdfb96d516c41e6d2baee7fa360cd249ba76b da123edb0191ba2f42dcd16c2c0b3ace021f3966366102b5edbf290b7f1ee4dc 90d7694a5669145a320530fdf847e0e9dd80fe5c2ac540e9b74b0344ed299012 bacbe78f3454791f3a36f2dc46dcbd619e84898ec289239a59fc7d591b551c3a 50c9bef4166e4b95f9129f3ddd598a4aa52d6e4c4ab055b8cb9a956e378625d3 13f30867181299cb04829091356e40dc75707926444dca50182d0aa6a82cd03b 1095f8d269be98729bed0e2ac0396026c4fc82b2b978763e05200484ecbfee22 a3559a30aaafe80d1d47bd52c67571a683c569a038f5896a7d60cd98022242f9 9d50c5e7ac7eb8082a61073bb592f27a1fef369e00b9490e69d1aae477a95d1e 1b5eb3fb5aae1ee59d5d7bbb573077d8eaf86d11cad3eaff13418d4407d62a41 7cb071fb41fe2df7d5070c3591f97f77417513b6b2811aee52faab2ab6bfc80a edac1727f3f8d0f3951f59a707cc44cebf2a8c98161e3cc66f89efe850233933 c8556d372461664716cae1982e5d61221f54ce7c444e30a13cebe884f6cd882a d42307b0278164d4a1dc48b43513f8e17387e582a14ee75a13333c1fa02d05e8 6addd7209d5964e7dc6cbb6cf285a5f7bd6a7ffb5f4dd78910b058455b1fa840 fec7f4b10a26be0783ce798582a8b4ed54d6fa0c2fbdbc0ed226d23db1bc5863 d5202299c74a672a240e1678c29c50ae304a9117a23685d0bab99993c297fae0 d520ad11da5f599c6715aa046165801f548935f5941788d8d2b53b13c354963f 5107941da063bfbf9f8e217179859920825c08322e15742419a40ae0542f12eb 2a3d3c36d651e69f8a11e6861d4d8cf48b14f07f394e14bb2576816c0e74ed8d e2dee68ab1114f2e570d1f5bc88464a8df17d4797dbde857eeba67e75e0032d7 1a7680f60d2ddeb8393a627e2525c64a5a5368af43ed8b13bc409aa07d08fc5d 1acd6e1b14517ccbff2147ba56984721e3f5ed3370130f80708fce53fd367744 a471a80b547fba30c640a19118877fd795f4cf1d1956b3741f411aaf26575e0c 23ad89b4e49be7922e188c950585a7f118a546fbe26a7411570ff7f417342949 6e23b2c6be86b181c4670fdca641f26c7160c1695c442bcf2e983a67b92d7024 f4189582da3968ac8ed08102c8ad9fb9aaa223d143be1f9ff6f9ce468925aaf1 044055addf0c75f0b4931e1422c26f3d67eba28d900ea6075efc75a9c871ac4c b859b351e67ed8a04d642c4db6006450adfc6c9b6fe5976f15d5289b9a121097 6e0521dce18d0c5ad9609c7bd474527c821c77ab0bd1d34d53a05d1209744b72 31ef28a84874693a4c695a1f958365d01bd69f32bc3b67a2aaa44d863bce38cb fb5befb766cd9ca72f0c19144ca18e13f66b0a7e659185382928373cfc760de3 8eb828b6273ed8c643eeede32929fda99e6c0b8a2f2d39a81fbbc6c409428fc4 bc11ef25438f2bf92d8269b0ffcbadb51a536534d7d76777c04c01062e24a13e 5125561f985d22a1ae08f6b2f06b7a9362ea81bebb5ec690c4bf7fd19d206467 42fbe66c06f1c87bef565b51f5573243c50fd068b5d966f7076c74ef3538326e 9e65a2d29ca9753a6b7aa0d721a47cdf0b76a8b00674e3f7726fe532c24ea37a c7a01f281810f7a9f2484b328dc649fd7dc103d62c32e6a9e3d25207ec40efcc b92a2015d09072b1545a89e18e9138395f0064032b432b34e742d82cf03a1171 c6d58936a16ff3ea6d3b77661e383053d343a6b44736abe7b6245ae554c8f6c5 d65110b55cfa3314b16f9128d37949f074155b123643e03b2f8de954a4ab1efa b030f9a078437df4194a10d15f32b34aec97854cee1145d260ddb179b722b419 1fa4c83c2ff31b1f87ab5863cc75ad74ca82d92e66213dc2a3f22658e1ad6d93 67bb44f4ce69509e7ebe75b61d96f8207ad4fc72e2b0346e9f4c20cadec65177 881a620f3b8204a30287705a702a7aa90e9767f2ef1cb2c7ca913fb3c87b18e9 093446bddd1ecb61c2e7d86925425d9fea2c81c5d7c1aa1240afb56fecb414e7 79597e02fee77374b8ddcb32c0c26f0cc139e300c0b879eef4657cccbd4fe0a2 0660d8e213982ccf1eda08946d46c419b4b09f46f1165fc6bb5426af72c3bf3e eabc8299627994523d05e0e940e1385c69e321154ac3d625cc85170a15cd5cca e3fe0104b1e770ba46aab0322d2e7b563d9e5d4dcaa4a39d7db4f3bb1f4ddabc bda925bd335f49445574082507743ed976265b9de59358c6afdbff46b349414d ea17cac904c0c744b7fc6ac64d6a8fccaa71c597a380a82f3cc63be966a94240 94a9e4861a1285e51ef19bfe87279b6bfc184725092c161554da7fdc6a40b9b5 d876e0d5978ecf81fc2c92aa4894e4bfb3ffdb5ff37453c4acd65fdc583f5268 b27eb3c999f2e51bee7099355e2836b7a6c3f1b5863df953d6bc6800929663ce 5158fba66da9817b344b89611dd28d7bd06079499873a73ed9d3228d5afc7700 0b3f05595ff114bb7850632da8803ca4b0b4898c8d7328b34d8fb514ee995cc4 fe72e647df42f42ae8eec468eb638a6676785caebd8c18014d5c248469c45091 f7d3374bf32aa05743361a0931216b9a95cde3de8d89f654aa20207afe503a6a dfe64f6bee682a8296d1b14f25f2f7a20edc47cf06fdc67b43e00e765fbb613d 3454cbcd64e9f7f8dec3761806f47b5129d17221baf0bacf397a7e2615acda04ff668a62d247784d90bf2d3c28922a470e02fb9de134f0cf07a003788f808902c17873f05fc1be23543eccc3070ccfb8919f298745eb286ef9314a44b8ebf30f8efaf7dd0ce66f7a9fd4a0795b9b48152475ca175b1a1d5aceaf4b1400faca0bd38b99db98b0d138eed1e7cf249c74bcfa9a67ad34314d1ec23077882195510ee28e0548eb6e1b6d517babec72fc1596a675926655c90312637885eb984a9b02c6c2b66d6afe4590779315b245c12455cd77d21112fff9c03cf2d6f049957a035064b04c9916c8f53e2a0f480ad92d72419e703e54056bb18e3e9a91455360099d0670abfe16a576476c71d6746e1a58f0a507841c1f7874c62fae97b788ee0a246f2fcae7ac49f8bf3c65dfd1daee9504872f2104f8798ea6f0854010502407dbfea21eb7454ca623afec4793c2a8836d3a2053c3a6440e861ffe78f6d3af06d64e463c65dcb7c41e956558e41a96cc83a21c04e039ee94a2d117652c9c99096c92cfc1b7f5184f2c426587d92a05fc9cb40e635434a4c2ff3bcd435c38fa0b4ad89e44dce6b9355a678a9c7e7a906c9261d9fcd92195868829fbb1094b1307884111f98c422e61d89d3359dc1ed5e2aa27465753fcaa67bb63c0cfbd0bb8017268ecf16f47ea42ac92246b01fc4d67c4b8cace99b94a3d2972663a4b0bd20375161dfd659e8925eac71df4e142592c9c0b8db117c600ce1a3ed2d7677ba005e62fea042ce901206ba4a181cfcf7ac9c31d5f3e5089f5fdd12a754c30c1a009fe8482877aa049a49b1eeb91e7ecbb7265914f311182442784894ab3d1d78b0c5f5c90b18d152482f9faca2d5c400a2f315ccf3da52da9dd21a1ad71154cff074a65a288e6245c31b0a5119508256544d954bb1c36e9f7f4867529d1c9dbae043d3cec8e7083977a32d10b1210024d46927065d7ad1b893289408e4a7386c7084d3915278fcfce59c5333ee61e2be7458a7f0838ad49693ef9e595ae9a7f7708770796269e1a6824163726f5df7bda2f3b54505ef360773a5f755e2e99878505f9f72a9a1289bc41b24d72a8fff29ed2c8b47751d8ea2109e69b48e2641b6503f86d23482b5945d1fbeedb7a16e26e46c4907c90bf0806185bbb7f9043237c0f18881f6ce7e3406a28e30edf6f1cc408aa32cd6631e7bc21dae1eb730b7a70015a482ccbccc337a827dc8568daaa85b45ee4fd6d91e05669cc4808905d782b06aae10f24f2f7a5f683f63ff77edac498bd47cc2a68bce481af0ef7cc40601e0eeb32638a89750f23562cf26a33ef1c2d68b16464f48745aedd1939658c8aae0d192a3e82123add3a3244410061876ba6af208d14f4cf3b963daff34a52c7c10143540c551e7ebb339e4a307ea58f8054d6e57c8b548d8a1f6ad6d283cb2b5707f3277b0acdc9debbfa818e3e6a2147f8060782a098bd0ae6d27e2d7667eddd080580e4a65c65da19f6d4fb63dfbaf62274b3c6db2f82cfea20e36a647f70d10f06852f96b8c1571330052f9e9e6d0733192f06ccf46ac692cd0efd82d83729002a2cc93db41c15dfa9ff0a8efb25aeb37270afe0805c00e4f1406a1c5e32fe06ffbfcd8ec4e9c8cc26f0ac33130fea206e0e2af8caaedda229052d345fcdac08848675f6357c490aebcb23dd5adf114ecfdccddb970c157e80ace9a1e65cd104726dc6c552099f55cfb521ac95f41164a894430c9c4cb88f57c41fc4f2ef9c06f50ac387091544d70ad5c3827331f8ffdcaf5dbd893c612778e14e2189328a0cfc9eddd4088b7f340edc778dbcb0320c8e45f24cc4278bcdbf8600a44fee880aaad40c9e8c3512f9aba77e4fec1e439feb8cb73143c6e6275b0423887c8bdc0e3521e5c0f7733542dc27ced2ca16db5302fbe0bb0b0a85c836276ec8db264f02f0b207f5fc617c223d9ccad333722e35731bdf7912a1b5cb3be115bebef22609fe7da361fe1bd06d31c22ea19c8fc4a36a58157545996aabd896788bf23a520b43fb5fa741340439ba2293ff8542fb2b78bddea87e7621358bdfb288316a92089c927d3464150766360cfed8d1a4f7be560d575cf1be1db7b7e4a823f3441d07d869eab787c5f7b2bd2f13fbe9f92bae823cdf6414072f90a2bd4d19983d480d99bc569421d540a44ca7d399c456f0dc8e9f33113ce951814c7ca9a2f8ee5c010d2009a470a500a3c04c130909b91006683d9aba8190e75a81e0617f83e4b10117a7dfe1ff2f5a1c549a44cfdb1c86ab3cb2ebf663f960d68788c33b7d2c3a0207bef4724d41d3b249feebb92bb41dab3bbc46ad99b8cd402ecd9e538dd68907fca266186e1b8656dce4e2f296f29c5891b3fdaa85f20731d093c3102a6d1503f030b7fafb56f585b9215dbee6411b5be70cfd04260d4ae992b9bc0328d8a4046b258661adb39f602ba774b7a2011170960c2af9a9d9f96808a867358b9b8808b779c130fa3d8827158ad1b2750f30b8a3cbe470c43af41fdc042c4f260e6905a7a97aff5a22e378a9d7b62c70ee1157e652c98ed3eb03606145d37fd8ef1a00208e3031a00d3f3099d969f9938a615cadc1d2ff35652c0f4940f8b6d7ef5a0acfbeee648a7381d40ef27b481ec7419d5fcc430bde441a00772495d20fe7060fb7858f5daeaf8842357730b27afbb00f2e472ef92447d4546bd6e4b488884d0baffe21245f5bc14f4de8821461b4b19fda8a55f3df6ab2c437ef450cd4473703e115ab2f2047d3010aabd173a2202915822eda9c096c88266af3c77ef5a334081cf9eecfe77b51f32f6231f36395353ff45d1c2a0162044f263850cac7f05509a5791381f99dcbacf8d2c0d7d92d8604fb242ffa73831edcd0ee2a07d0765c0e27e644129d15eb8efa4b4b5c78c9f3f07046dc2c44d43f73e923651adea4d40bb434e8e2838c366b28e74782709ec6faaba27757835e7a13c812b12c36ea72068bae6d1a645715cd8a03b7f0967ef39cfafc98afa6315bd4afead100b0fc0806d376307cf9f2e7b95cb2b1ccfa14338c2d58bf0754d46ec9e2595495f5e33c04bebc35394ab6bede2637379a0b27a72a2a7c7a40750ed5a3e9008e59e74ba701b2c4c01024979c24af4c6e3798c612cf4cda7abc36a613cf54a12fc45a7ed40cfe4bb8da58f467012b86be9f70cb754e77b0c6a43fcab3e777933c3d84f7bc000d290750e01afb50ed14005631cce9cae31a1ab41c92dafed7232fe68e7db603e8c4c039e5650b2ad317ade65cdad0d1f2ba91466852a02242504c542b548c0e120652aeddac3558602f510ebdf44379144bbcdc5b464c05b508fc6f0cd131080209133a1652bca04458f899ea21eb301187b81d80d4d782c3a6462b4d9a01033042cee8045cf8ef6e5fd85fc5c1e9d5062736c35249bf156c9e88acdf44c202f9a9c33ffa8e9ff2999733486cbf45aed1f31b42dfce251372406cf476948f0566210ff3cad8e184582a29be630c7e04af3ed38925fa1ad7bc1a2b63317aa400af435ecc215282b3c1ff3532e21a82c576e81e550912f63fcc9e6b55d293950260f00c24a03d47343da6743714177614772badf83094c6c5e697aece4a225d0b6adf03d02aae37b92d9af0ab4bae8f9472446cabdcfbd49bc643019d811ff608049e70b989aa17e65b438dce2e9ccc44ce5270baa0ab4d48c21a93200d96ac042be6207265a3959bbdea73d95f7c9b2191350724bf7e5560c3101cb86dcf200704949ad43bae838168a95cac8a3a15ad3f057764f17ad079a74cfb3e6c5c2c062ef4907c98fe2cdf3957c840dd84fe017833427ad63e42d4af10f451b0159c0abd259a5a9d34f444664b9644fb92a4669b223e366da5d8d07b9b1f20bcf4ab0b51d47687e5dcfef57d06755e60051d042a781094108e5b5ceb5924a650b5910ccd12ca4f17a4cd5d4c6b67c1ad0416248fe3fd31c1807b6032e3fef67fcfb30c0163f966053f2359d14b94ac537839e30c8af14327b4db70af1a97ece2c7510361c56a48dd6d17afd8b086b812aeec28d0dfb156527f10c5b2526026ca26b9097a5416d65fa200cb387db2a21e25f565f5802da701a411be24fa445f2c18c30f67c64f07aa93950f3abed31f7aac955b523e8a2e9a78cf6818d555f37c1b6e04db5f37a4170ba0216d718d416b321a8ff171b219361d55e9b52695085b369d0a920a8064d4290b23fdaafad60e0f4d0e7fc8997b0acea140a87b63bb5ee2750d074f32a8923c4e1e95a8021be4bec4573d416a3e00a860e8f46345ecd5149608bd66d6b3fea5381e962f19aa612afcd136494fcec553b27717313d94356e25069d228a05df4b7fb0072cf38b21986d3bd9c44943264cb3ca60b2446882440e045ad964be1a553efba8a97a68ba7b5160b63b1715baac77c1f0e32f911c45470c28c82757b50a0d0e10f9b9db110bc0f0e6d988cf7febf1dcf6bac392ddf30601ff8fb86f4b37a2f041aade2cd1d8f52be71c3b16fefda5f1421015fb8881b801d985f9ca229793899108399ecd434ae8fee337bb035005b43ae23a35e76d880e09c1b60d93dbdc9d92fda5a4bc622feff9fc605804ab891e0c7bd49e609c7d0223cd566d0ea300443e235fc67497a4a826825459175df821649910375929ff0403833838196aaa0c0de47a0cec2edf002f90a29406a1f89831e4c5e18852fa0142315adcc57239db09e5f6dd7f007acd2e93f326ca64b87524d70543f7f7c607f987efad4d0b93147b0ea77a9541bcdeb88191e1fe165b8a34f1b3c925216f08b7940f7199f57906ceddafe6ef0cfe52a561465ebbd76277ce1e921eddd59202610388b7322b107c12e665e79f92cbb293c45082accd6281ef18fdd746e1dd079227f079109a7ac608020d9b04fd8de50a5c10e088e9be97fe3f9148dbae810ffd5199e58776cffa09b8260bde2e8f3463c5738ac57a360af5030dcb8f9be801a41ff8ed14228ccede3eb388e6a0d397197dddfc7e0e6cc67df5a9e0ad6e5d0f2c74df73d0663c2c23a03a88e5f91cd670dd3bf2940876a31f4437003067ac01d106bbea1979f36969a08e28429d936e541e33a59a2e8537d1b85fa7642074033ec460a00075e735dddb7c58fbc741334053a01e3e7f73e77da845bec42a3604e3144be17ca3d80420b6ee2149b899169fe91ac80797139a98643e6e3f65cd01c70fa71f439be9b583ecb9b61225d3533aad87ab7139d2f6c3e3b4f6019e8d04201d99a10d2eef44ec83f13b448366379f967430cbb9a0682e6f45d0cdad9e0fe96d8089b6fbc7904ab0eb6530fe1a1fd4be430acc5dd31d06bf539a37e46a0742ebbc284005b237376b7d468a1bccca05e1899178d141e71ae2ead67327cd08bd1a6976a87cc141b48f15d5e1df9f9ac40eba8a756547568479c578358cb706b0db2c1c9f56395e74d876f2a9a3b89b564edf2f8f2dacda6d38d38b2557b001fc9346831802d269e5039f696a28f1c90882399e76df539036d520262f052d001409f270739d794badcfa2ebd09949de2d3008a6515dbe3978db387125c4030fb86fe791a51bceec06e067d92d7b0ffaebda318e46ad80142de7e4bca32c2203009865a9914e8c1325688dc3e26a4f4b29d01e0e9efdbecd695de3115b42810b7b50c36e0e8748eed1561540d4d31fc6e63ba5484b2792661bde00a59a7e7801881627d236ccfcd2c9257d951538cd3507b6b9759221d736d5db9d0be99a9c0832a5d5dd32da746495de6c61720a2e60c6d6402649b00fe232a3cafe98e87d00a3b5db698215d9b4db6e4479e41aa577ca735d2b8533905be5b3be6c31087f0cac89f325c467cb57610c61503a9efc59c40306e71208c4a2041321b386838b085f718c80582393c25e8b9618e3453a267f67f8cf3c3fe836c4ebe53d283c8a0e54910f25e665bb06e6605f5a333f68efb948d78fe8d78be7951596c8a66395017618ae62c0e0adbda336d2df9357b701c7cd911e1a1f220eaac70dfd2b787e0f17b9790e643d014a0410f3ba5b903c0bdb70d531e24acfbfda5ce4ce357e800d17705c73eac44a9ad2bfa2f19fbbcb9f3f30c4db6aa61cac8f3d5dea1891880b5a8bf0353bdda9ec4c1401c9f838b9046148acdbfe3bad3b31fb216dbe9a590dda2099730407a016ff6f31bddcad0cc9cbd958e4f925c1df6a9e3f182fd8d50a2c1bcc4daff4b78740cfc3df47fd465ce62198fd34448dfca7faa7cfe241250ff8cdf8585fe6a4602b18d02b7552fb19e64f33753eda4a862c85324c9bde9d034d9cd6308dacd0c4b74f42feaabd06d89ead52a79a1dc14cec98ef7383f5ac0f123477fe8c81d60fee83df07ba2ffb1f16acbc5d75652b0664bdfd4512cb3a0948c6e7373db285b26298effe3fb7cd94c9cf4b6d56a921bbb41056973906520b4baf905725828ecd8282826922e4e4c93f1e95e59774da1b5d09a2d96ca8db07d46cb33f1dc547c651995c7e27181af3ce807f16208de908fac7e62b68d77300b21c9317cce5b9175927644bd66f5ec5266359d0bb9fdc77a2862b384d83890207e831af36e571bbddd3637eaa58277ead9923f801577c13c5e1ffaee0c8340aa801106d138b594198f36e55cca29a5e970385b472b63d8b410ea4c3c6242b03a57a9bb55aec65353369bd36d99e18406b2f6f4072ad5812cc9180070205bc05678bf5ea6c7bb0e716b25f96854369041f7489b4a5a576ca159dfd52d578000b64395d6c9ae5b9d02ce50337a1ec4d73cf244395e5cb9f152247c76ff2d1110391f8fc9a48e57100cbacdf01afb957f5325c65647b37760b203974474f83b00cd37ff5acfcf0c132db2aca58667583e6800eb5b60087974233a4203d32c5cc075d197a0c688583e7d5486670399753a882f20159b7fac88a5566f42659c5f009dcfe7bbf398eac2dd3975005d2b627d8b57f295ae30ec6492499cbbaf18e6c07e7ae4d47153ecc0e52985108067a9b7d17d393fa883457d4b8b2e64743bbca0f00292651b3cf3d005ce66c78f23d7bd41cbbe7f13e035373c570cae1d0fc96035dbbeb138c0876544fdf7866a05bb636b1c684bc6002061ba81b1fdd472f220b5e93489ee113e7f4bda26bbe671d2f9c4a7ae727687dc122388b750e145385079882f06a1cfdf66d52f63ce62a423c21d6c73ebce74a60f543eacafb4cce180cc368f2fec4b286b18b5ddf46f33d3d1492f2e36e3c9a7f50e25699364dfdf704df58f803c1b61d3d190f83b0bf78c0d1090d0cc563b97db18952ff59c6304b00bc0d1e5cd2dfb6216583e3b7335b89cf237505de08cb5108f08e8e30cd3b9b0734e5a5d19aea1460c446199ea225cd7fde106145dbd4d111a33636c3810e3e06ee2f903d9ca7eefcceabefe43d4c9e9f5ddf16cbac59f3a0b16106ebab07560ce08d87edf4cf3fefbda4dbb3144928354dc483948e522f5952fbe4c996106e05c9c86ab8cd62f9240230a248952a46e3f46d66c55c8c793fd791cd994dd72103500de3fe1b95539c47a1549ba3b27b5ab07ab3b5a373ef79bb594ba67e235c0277c599a54032372a3f1e00faddb45a986662b2a601d90155781ed83f8884c60dfdfbce3f30a96e7c3856cf836bd362928308c4e0ee4d59b34df83c0b3486e20bc456577fd7170d0f52bdfcee817e86e8c9af8f66e037dee5a4125287f87507035c7dc3345ae9877d2213f6535923e5944dca55e2a1293b7e1b37610eb4e66d00469e516094d042845e01ccf05a9124132d3896e0b9c546f74015f89984e3ab0a791f355b4cd3a792ccfb4276a2d5240b7625848580dd5afd510549749e92400862b82c7eb0e58db511a6a76398ac4f4349a9dd12460333849fee79718b3f350dfc3ac6194f6a827f7743158a3bf43eb050b2f3be44322247bfad90ee26dcc801e0da3a0b21ea74cdd0b7df85987b89784e62e150641c98d04f4e251a1327aa0a7af94721d3ced0a17045ca3b1a568bfba7f78ce2da2b32db87311f26330faf011e56cf51c8a05e98ae974c9ac0ce3e5f5fb1c9ca8d7d487094efb98d1bbf660757b63f8c32553dd69b0e0894ffa5d68fb80f48059418554ed761ee5142bae009b95e981e33060ea28aaa85a2cd2acc6c50b5faada14193f3670a6cfebcdd3b0139289aa9aa384e9244745cf9e573be8ecb4c2e0f320d3cf97b29ae4e69e4b10820620535ad244b4708d76e55f8e6f86958431f4a40b8cb7f27d9b406ea0924069bdd44b3f3f08d6811b168082eb60e08e32b699d6ea764cd59fb07768e878f0eabb19fef470f7d86bb92c747832a886925f0d3ed34a6e54c5385c28cd513890435ed17acdfb84cd0675112f074871941f47a2ab6521349257de3ce76d901df092aaf57f6515d3a60114051274a0b18122d20f5b4fdff0456ec22c9e7d27bc901b9fef2f611b65064a04a437661a0d870d1099c50a78d6a6d6241782380425709029fc9341d35b749e50fc2d3d7831af3452a4d1d630a834c863d3e2709c7ee08a252812e702cd2d105c883e79bd1cde6c3f9f5e765471810d71bf3b1abf93202e3e03f458b0cdfe0ca6b302ebb3e87171c46522dda50881f29b4b1f49874ca0d791e8b394ad88b20c87f25d9c12a43f0cd2eaeb68adcfb982e534958f428f702002a4f3d8776ddbdb899724a2482a1bbbe59279abde05ad2e4c74f703bf5790884ef222eaf30af77a3fd2ab8f198546e0aaefbb3ff44bf4ffb44f2dcdaafba087b2b4211239605e7ae2597eea54d2622e1c0a7ae3e5d1d40b7d39d1d34d1540c61afd17001ab981d77e4b93dc71ef23cb024fe1ce92fe89b45921f55ef4e320df097e68a45c6c4bc1668bc90b40c1eb62a349f6a7c5654b5c0d934859f37610d397a8e81ac125d9e98d1a2c9f6fd225553c4d2b8b6d5f87c28f99accf133bc0a29cca67fa94a0c9e28a7c314eff9e2d787fe675f2242759fddbe5677a12b5d0473c0283f2f35057fb0e4ce14c6e4bc8811c410d5954207b2430a725ced1c7e0e7f3428288cd7bedcf61f5c4552e3e808f3f181982a337f5ea515166851fd2507c92faddde60358a50ce407406da6c63b6a3e7e8cdf5a8ca1ac3ca7303efc32050380cade3fa4443726d2b27e148e94b86c14ae0949daa1c891c5202e4ec5360f27c5ba5d5f03795f7a7d136f8b459ebf0e23580b2ce55d8e197cdfb60872c302a28cb27f24a08ae52ad80a2fbc8c6a213bb40c4c2bf6a264532352b0fc17270c429a237869632bc465802d69230453c01043d3db8ac61577bb8c256c75ab780f1bd0575104ea2d0b97c133c1f73376f1242974e0236376dd7b00f2ac4dc06b0872cf9e86aec8ac963b00a91bfd14b7ea4c4b2c802e27ce2fab33fadff545df00c696ae068dddc4828ce77169a9df6aafc78057ba2aedc72d7b06685cfc563f08 false +check_ring_signature a54b1140f9758228f47200901171b62d133e09e670f0da2a069c7f16872e4985 91e7b3a2a8a8af1633a2ae82df43a2da67210aee2c602c882d922193de96e0d6 1 1daa773e9f051d9362ac4d2a27dd07880f00d9fe22663b6d2924b4a197e5e6e6 39b02e924afbbfd87a28a7443bf17d21459b6fd1ed295f9f30f1174c99f0db0f262daa015889a1deeb13f85a95096eba218a073e87b9f7acac935f42181ca001 true +check_ring_signature 03cafeb382a7380f6cc428b8bead238292fec414db0afba29947f50594f29bb0 264fe41724e9ea1a0ffa89857ad64b6b008282a18f74b9c781b8fe92e3dadd8a 2 e7dd61c4dfebabb998dad86cd28f11b171bcf43974bca5dfc8af6c5047cecbb6 7173e2d93c67b0fd8c2def9ff4b532d5bd70a1b68accca8cad6bca40be391337 b0387dfac711a61b2b348c9147e8980136377d69302f619a3bc090b438c0050092973c95e81f5bc4adbed9401123bbf561cfba27b65976b57ded527ed1083b06fdf3e45d2efdd334eb123c9dd2a78ccca5870925461841e1a5dfea467a6c5a083368a54f8da29c3c67160dba15b4c6b0c692bbbca39ddd72c3142dbadfada108 false +check_ring_signature c411f6af142c6df5ec373f82fec9b3b68e1caf87fdd15d9bc436dea4fe76fb05 f7fcb9f295aad5261c7c4960a9a33e27a02fa1a0c49ba3b245e85cd991022fc7 152 eb9d58906236f084708bb49fdfd286213b40d1708241581b8757e83a3e684906 8f441bc105ad79582db3dc224847c6ea59bcecd82a69d8ea6f3d46f19785e0e4 afba3a7ad1142b3f38e8bd9275a0d740c04dba7485dbfc75a9c7e41973cc5811 98ba12fde2159457f2d29b28e75df368564643c8f9c335a0bf9c34eea2e791d8 4b219377242e9d49a6500e1361f44e840be12680b212e57ab2431236d877c36d 0c4c48a13bacfb1229a318adfa70cb83eeebdb2abef0b4534ba17efcaf2a5b6d 8c7de156f28d4fcd3e8c714160fa37e286596e0fd084ad441ca60b6794a07763 5e0e013da3459995266d1dce0e393e3045b8658ec0346ea66188251b80525a64 3caae94311442ba405726915b09180b0dd7ce0b5930e4e794c9cac117d4ccbc5 86b79a33319de9ba570e30e94d4d754e2fe1badaa41c8678ca7de8104f23b3de 70920830c0f8900e64bdaa5b548529c0c0bec0c6e4465d9c3921c928aadb8246 b4f4cb14fa3ce9f8594ed694f87cb38dfb15f4e2bc141c869075e9c47fcb264e ddc1f5d7414370d246ff076e28d64ce28c317d567ed8f936d9c0e3d05e4beb4a 23356320f7dfcfd9c500d526a93409021f1447ce69ffb3e7e920e3485a3f7642 244c4d78584528a547a792fb24fde0033b0c482f162a3c6011ce533f3447fa9e e8d0a27961408fb5247e98025976fc89c1358f0567d8b08ccde44cdc8989ca55 263d5c16747c96b29a6695b55f351de580b7b2ae2bd94ed644650665a0a91955 16904eb51078c259774b1fafe49db99cc232b34372c0e4d4590dea83cc61f4b6 1f2b8f4c0467db231cb87e037308e47225a4fee511b492d2feb42374a8bbd632 6500642c89be61593a9b24e031ef57a7f18616e1b1ab551caba4a4d02003cda6 e037d195f4d731dac663af47c83bcb25e9bc0b16dd6ffb8f46bbb9a7557ebe79 7bb799a8751af727caa862a73e9605866ee315a105fce3ac7b7a7acbafae9a0d ff56572ad5e5f5a795aab97def8731d148fe536ac2e17e3050e695acb455fda8 11a807ce8777d71127cc8f94d7f37ddac7490b0c71935e47f79017779fa54d01 bda955fda14d347044c4f84d686aca199e86164f87f973bf4015b0ea05b6e931 c6b79b8d7313792b85252774502b240b1fddcefbe68a0bdae51526022132c272 ca3348f83b64405e311feb10ef1a5dd628e8e8f375532a6cca25fccb192be9bd 1837c8920ecf8928947bba426555cc60ba6db0455d6fa131d0d0ba88129d1681 1fa4a6562a791836e8b3ebcb9ccf95287ad98b27690f2022ce69350999d0787d b00a33f0c1903410cc09b5e1787d8f38a98dccae563753ddedd27456c8c6f75e 3d3941ab689a3a0f22ed8586b72c4be06e80c70f31f599d4068763b0a451a945 0833876821e7646c8793c6d1b412ff7e62791f065e5a9e7eb28a824d5ae426e4 91627d43c6a361c42a699247301b0b4c4e749a88f32abb7d8b779edb9fc15a8f dd3f366d7fc4117270caca38de0c4192b2955b2b9d4f027edaa7fe3586a02232 12c5160fc68bd73de0bb78676550d858b38c796e58b22b08cfa13bee916dca44 09444c0eec584d819b16a35e1627160c6152f04bf9ecd2500e70a8e1f640a486 849eaeaf81b4f6e44a8bf2d35748face6b9c8e67413d354e01d6e7cfa966fefd 14a9b375e10cfb9c25b5eaf414160788e8f49943af8cf9e26bc275d24de43049 de8da19b438bcf1a459d295a8f0d33cd9d33ea344480bbef5014774eff7db26e 6880b0236ff719f6f60404f61324ba02232c19c1d7205690316cf02747ba1c28 6b8fc46cc49f1fe614e0c7de57862fcffa9fde196cdf04c6da2cfc11ba1933bd 928bac53cdff979f230773ab17340bb3071c5eeb81ae5f0fefd309c562cc5f5b 52f80656c9236d2cd79a361b9f07bddf70c465df34ca17d0c4b28e9356268c22 bb672fa8bf9c134bf6c6f734b92819c7c1d5d6525b23727ccca807b78203d4f4 be3942bfeaa151cacd22a1a98540b79273af5abdd15314d3923a8743a00c3bc9 9941ed76186de03816cbc237f9336a1bdaf0f8823147f3fb8c2254e94211438a 59fc684496715dc27ade9b1791a7d5af8d9e7c92b38411bf4c494ef009a51155 0961b6e1b2a22fc0e91d5457c46dad4b0d44fb1c1f61506acc71374ac7bf0934 d04c2a48f9567a9670711213a3fdb04bc4fe228984728fb2c06287e24c13b144 90edf63a0a3b16c88216767618c19918f7a90aaa3fd9c875736f18b6fc2b8b14 ce3851523b573fdcb3a0d4e77232aa88fd65165bd646a3de97b5b611fc5baa51 c4b969c3cdb478692c785caab43669c9e343f8bb7221d710d6b71a82daf9bc6a 01d811b9d4fede179f8a26f5fab8b0f4fb12fa48ce694fad139255e8da885e6d f2ffd9c46e9f5932789a09cbb7e60398f08d5af848a12cd7e25ec0db98ee8ce9 6fa70f28eb6baa62a6fface2083a0cba0c4f155cbb0859fb3dcbfe6cb65f240a c6187082a711d89f03ac8ed3a05c31d7bf6007ed1540de2517a24288b5664363 7f69dd20f05d1c68121b7dab095abc6bf04c48027fce0488fc17e056a19ec532 04e6367fffaa879cf7fe138404de731b16971019dc001aaaee413b132d8a36b6 bdea7ec9e6fceef67b86b923a62f91c869ef06fcba78eb61684d52f3d38e1460 21b84a9d03fa899b1e45596d7c1b1d8f0eef256fdc726f7a7f841eea94b27a1f fc5e2bcb3c0e3d11c068b8d31867ea3ac43c1f9542c335fac64a3957740b125e 7fbb61002e4e38787c252b3b8fd07caf73591e06edca7589032cfb06181c67a5 130697909f1a51b48f9e50eadbd89d244bf3beb14828d6babe41ca03e987a7fc 8f7e248d1952ee61e318292a80a2608ac2d468de4c27eebed8cea349a5e0a9e8 13f2972f856b55dad16ea77dd713ab026c51d1dd39ad7471d5b5fe6c12a8b408 775cc02ae7dc47f007ad49f72644a3222fbf13b107cbe8405707c3067df44c9b f80643574af77d39fbf39a2e89ed1048737744d528922de024be19a8d64721a3 cf52ae349d34a2731b1c7308136626fb12117bdd3c1bc59c0ecbd5ee57f3e467 e575a0331c61f083e0b2c09f144f2543080fd035656b29d779abec53a249c9b3 fed7eae74787dffb6c19b5f0228caebc3a93f20ed04c6edcef9f3dfdffe96d1d f7fa1f03f648e6414ddc2a5121290e8dd6b5d9c91a19f66c0a2c05af7920cc4b 6e460d6ca5c220fa974a04774adce46f0c59ca217c97418343011e011740d19c d49bc07f692f1c2664f554fb1b5c089a9a35edfd6206e5812482a3c9e0fa375e f14b414b6a1e23b3eb69f855dfbc70409525583d166a665f1617426169a960c9 d93f0253b3eec930bdc05c1dc16f0c90175fbe5232488e35df7cb78b57101d2c 895f1da2ee5cfd0d4d63d8ef6ca08b0342cb7ce236146d56af06bc00e015217e 535d8528791394a6dfd06854b632087a5afca6331a13f3c0f8380c9222385273 55cd96873cb2a89a9cdec43d5ca58a17835d63c89eb5b4483d6377329be90c4e b8c84af21f321679198569cffeb84e8765ccb765e7301a9842ed5eaca684c056 6ff779ee2d7c1c7550b5479cc14f0259c1122b5de46a6c719ab5185939ac7c10 0dc2cd4bb5dac86fdac768488f9cd92a7386907c7334a48281d5931b74966171 e70450d74cbef99208ce9246013c79dfb24bd0aaae6f49b1164f3860b24e7d4a de872115afbea43bf586f66b0186eb4b0f159f2e2d1dcf2b44dc6c98d6808042 34b30f5a16c97dfa861897b197b446dbeef8daa3a8c25db5fe3ac7d309853c1e 7ed0d432400f8b732a89f9dc1f283f9aa97fd28b08a4eec2143db96e65461f7d 5e228107f57ae0e6b50e35f516d23cae0f7e51f4e5262bde8bf944b59e43c95f eb0b2fe20e23d9fa8b804f40c11d0b2ef1e812255b43b6f2431c828da57218d6 2a30195d062858a33e865de3891ea42a0879ba8a3888de81e8922c47f7cfc0ae 8a45c644e7093c7a9c2820e2abb4661bfd044595778723a678d9ca3c2bb4766e acdd7ce10b6de49c1b124709d3e1c41d54e67fe60b59c6ff680803fc434aedb8 aacd9efa581dd1a4a3cf15bbf79b07898e14024a01ac258d426091272e6d86d8 c56c5f2bc1dcaf64caf2386c2ffb1db709ee42f6a88ea594ca8f0bef2c675406 1ee2d0c00c9c9f047d224ab4e09d8a197681e580eaeb40c7b1bcd1d52f9a1a5c a6efe1857d63d268e023a48e5e8328730bf4d6a0389897eda017dac90ca8a8c3 122b36388020c7d1c04ca551fb67f126f8a7e8766c5e935c759882e5a01a3c89 d66e0a69f14f29f9b5709d17baf616202352b68cb795ebcc99f807c7cd746997 4e0d860c73a65ff2ff48ca75de4805fb9ecc4603b2789ce8305e38f1db879c4a bc420adfb818631026402ab218e60594a0607ca7a3d645bb8693b1c606e9a159 b4752597c695fa6180532fce32d07900284cbee673801f80fb5fb5aecb082263 49b445b137590576a9389d75d7971929f9d1b12e5e615433868cfe57f7d8658f a5adbe205fafc0496ead3d5ab7054066d6e6a6809d00fbd4cd787dee06e74a82 8c8049fd2d681eb96f7c31747fad7d426e155adb9fd404487d38939caf967291 b87f249ca770746e5aaa7dd0db905d005578f6004db3471dc708c4cdd517b2f0 643474ed3e538e9cfe3312144178453e1592b5590ba2fc22da94de5f4050b995 01b441aea023c5b8d8d95201ee28489c94b6968b93317f1ea866db4a51a5ea92 7f326f9415bfcdc1c38cfdeaa292e4a41bf1dffeb23c22006c1a3cb65886f3af d54aebca45484ab542134660df12fb321fd7a688bdc86669441af8e4a654f73d e019d230ea25b8877dffd106a9a7b4864d0df93ba92712dd16c869ea61637a9b 18e747583326992d6770cfb59ce790641fb1ecf7eb4286d380868ce013bb5af7 905500a60795da6871b20b6627f57fb2b85302a04dc6a4e13c8a3c83fd2443ee 2ca825ffbf26a6290458cacd846417a0dae47dee19db22d16fa35fcf5b4b8cf5 dcd77cdd90df59e89958be4dc5c09d64ae561692f4b1ad7a90366ade3b0dffc9 ef1d12af92a3513288242ef759ae0d6bab5e47ecd2e68d3a10189069304508e7 643f0fa281b411409908f4d1e79a3f0815ee400972281cc6e09edbd5f2c7f6c0 56fca9551c21a8f5912bbabbdf2f41e76b850c91ae27939fb86a6b3ef1e14803 1f9127c85ab6c29b7f9c65e5a6ca076b53195de035460b08d3c4599ebe806a98 f118953f5bd0bf8f00d1efaca2f958d5f7463601997ae43d012cbb2002da50f5 464064fbc6047235ff313d16c68c70deef3f5ea0b1ddb095c138b23a6bf7ac03 72be07beb41b77e805631cbeb4265c86e649b02c930e07f4d10d122d050ccc8e 0f933d5e441c7641e6a252b284fca467c9631fb6b3fd59f2818fe72c3fab11fc 05090b20ad8f68f3eaf90ee95b6705fbb7557e5608fe9c043dcd1b015447c1e6 1f1593b27e4228e569d812f724897a8aba9063aa05a8561438325eeaf9bc2d79 ad3859fb4bd6d689187f5d7e75dec4c9592ec34afdd50f16d9a21ec3d78b7319 9b1bf6ba4751f3806258b0526a046221b4999b4a7c6f4af3ecded2a10ec71f6b 88d85ff91ca6c40fe06eb8a42cae6852f39eb3af816e48c29a758b4d0399d3cc 8265f82b4130b19b950d0302d607cc03a8266526b35e68d6242dcec6942f818c 91386c59a0d7cdc88de735af1532ed5eb9d5fd352627e36573ba96e5b3eda152 55d3984d71b074edc07e569203f01b86a50bb2f935a9b0c1d69ca00aeb28d6a1 7c386c2680fb5014b0222296703497a88898cd740aecb14cf9a74011a9da00b7 cdabc5e3d7bf51ce0298c52d71690892098cda546c0373426ac65f46f7fd4c9b a41198ebccdd9d643cebd7ac411bff93a7eb387cd608033d307486abdb55ef91 57bbdf6143f6e14385a3480b44ecc929c3e9440b27434f8111f8ee580a1645c6 27faf490c66c413318551889f140aff5b000a164b8489b9ed4c063aaeb9a5342 b22d6affe9a743dd957f711e143dfc9f55cc61fa91fb10081882b3c691c6b132 47868ec4ee07e256edda9be0ba45bbaf2f0fcb037a45c9e6ee97a685f6a406ca adb558047e786a4a36318f8437b36deb81e863653791179972160fce6eb080b7 9d78c59d385c384708fce3e54ca7ddad68ff8d534fbfe0596a6fe486c42eba1c aec106d4443fb39bfb92d2ae1062d724e5fc59b1b1dce80164a743b212ba7044 6031c87920be87739ead7ec89327ff07aad7f544c56d30f0c96e06ca5568ade1 9777cffc11f7da1bf85eb83972deaaa1e9289babef661841b6969e5a4bf326b5 659e298a19334332358a1f3ccd33c7fc38da98469a253c26e0228fd68cf0b7bf dcec8237928887c0b1890036b4e25c922e529c80738131768d36574e080eb6a4 8c2d9c2f8ec4207da7287542c6bb99d93ddfaf96b492714e7d61d8d6a2012cc5 2d9fd66b5aa69d166155518c91e1ef9ac50904e44333e619be6aab126cc95f1f 6d2bfbdbcef5c2cc07138fefbf006cfdd6b1ecc4bfcf1cf0cf3ea23f09a71732 8ec6ec9ae4099cd3de91a2375b3f8f27b310821218e777f48e8b2721594c3345 ec083493d5af5cc2826d46e4124d6c34e9cfe49ddd1ad607528fe5215f78a69d 191d5f0258c0a29e2b054f643bd923fdf3c401437f3cac588823e1a2b4c121c1 e7fa4861384abf1ae351c379cf8a4b74b44267790b186790ccde6f0653bfdb8f 4381a9dac1714c3e9b7631d35b594061dc12ea44104b4adcf341bce1a1a2cead 12a50f76f2babd1bb28d27580da2a8e0fbd35821b3b956677756bc9c028acad8 cd8e2fc857e281ae0ddbf9fce577d2594c7d5997ea76667e134b9a5bae2251e9 5cf76cbb715c09b4b0c69c1aaac615bdd5de6f238f57ddede24707097c60150980a0056c7626a66b8cc94cde920782cc8856a094bf77b1f27c453d612d568e031a0d398372b6be67388e173014a39fa397c0833b66b8c2ccc00d7f19ddd5110baf0d26a0159dc6f3b700a4a287b9f3562ecf5a579844c623800e0ca994f59a0dd99981114faf41842e7decc5e18e9de5179429cc9f37bfc28c06bba0833f8d0204ebf61982efbffb3b8ec60dd34f2497ad2a61d1439de7214dc0d91a946bd605e0330acdc1f008b16975b180e2167a765c77ef80d9080fae34fb96ea8020bb0ba4794b72dad3b0d50562b6b0031e599aa79d05ca7549b2515cb19ca67bb1cc0e985e71481910bb43a082d6941c4c33c0d93f9c73069513497f8e4043f075c30cbaf136fbe7a2f3a1ed82fd89df69675e1ff82ce361bb1edd918ee16dd43edc0f64da54d73ddcbda10b6873231ccdd09bb2a73e8faff2214f1abd8c1569a23f07b7fee97f298e52900950985cc8f16e776df25b76af1f74fe05b7f78d9b8ae00bea91a445b6092b97661f9ee47a3e341cce61215a5063c860dcc80274d42ca105b23203cb400f74cbc16e63ebe5339941a649b87d076342ff2e17a78c26ad4e0931abff7ee6b50cd70f36712273678debf3476d42b00753db6d04cfcb0761090a32ee057c29edcb2fbba267eb5c885f74af236b0eb83eaac82b89e0b3418ff00877af31059e94d12e1cd5f00380a391831fc5459eee6a369f5b8f7ed840a0ee02e3c23ce73055565695b3978fbb11d8983e650c605bb53efd156cd5108318670aa6de8d3a6e41343e1dabf9a85288314156d63b6c7e22504873349a5b924fc505de582e3552430e076bd490e3450a362ef8aec07a94d40717baed4a441c894d00972791a1fa5f916dc50b16d402734120d93c54c675639e6c94037b7024369f0dda99de5e571c72b664e163d695b8b02a787cd7b351f20c1c8700abf10aeb02048c7e9d64de169bd18466195987bb3901c30a788d5d6c2e26f287f82f380ea00262f10a263a564da4af087f0416ab4cbaeb0c2906cc867af71e09a580e56d2707fc5c93916df9a73e3e609ec5ec24ea7cbe035f61a8d469a66b4a59aa0d8ace0dfeba22952b90fb2e18cc1cc2b45b9a5855a89ca81fd9c8a48ba56e7a0acdcb026e065ea5a9535651b5ae92299c197873989f0fb058d6cd049ce42070cf215d0b6014114bf298680525ecefc505d02dabebca61b7697d6e12392c790bf3bc0c0b115bf4aad161226b5ca11337689e4361d3edc3c962b7df6da47f0ba1a355120b931e9a6197029751d72dab3412405224d8b5d31830360e2e7929ea351a74b500b10f13e6d6786e6e9d1f57a1bd3ab04fa2c28a91ece6cbcc63ecb2d32ead7b04e8b3cd3403fa03271b80ed5433efc6a31e6c44e28926befc25d322a0e687280a51796a274175dac7983c95e0fbad7467a477adbd884b4b11e23536a883d3f40e2ecb849c3e6c328badf549efa14a2da201047f282b2211c6714468719aecc00eb357e08479bcdade3785c20c14905a6a959445c324efaa8b438b80e42f16a6012896805dead9078856fd8a94e03f76c0e8b76e0ea337f7b18b6bb73153e433058732c8a5dce74c98368a9aa61eda326492d0158b2a452d7785c671de7a038f075e17ba19dfbf1cc21ace42c504d62bfedb62755889130d940abeff233332c900b2926dcf82574ff54ce0f4d39456b7e02d6b3be68f85a38041c02bc6e698f7037c2095763a7caad94452bd33c7271d2ab7db418797b444943cd70750b86828091704fb3725c5f548bac67965d2e23673f565a6591d1915291f6f36faeb9c44028d09faa2eefbe4a2e2c5f94ca4317519df3d4acb2c50d389c2301db4d3a41b04ed5938598ae2d410d2d50e8deb54a92df90a89d7a77ed2962071726df4da9e0662851f7bcb3809d8c474d35916844b2788bd1a98639c3b8ba6ec4f1563180c0981c0ed838139c1c57a40f2a0187fef4a4ee071a76c9025f44ee5eadbd5fe54082adb2f5e3c0aa1098a39709d9618d21298a7516ed1327d69330f0d787bbfdd062cc492d1125a13d435b169998bc017ba4ac7272d9aed8f1632c7e2298c403f07a0d57225c40fecfa0540172bb82c266627a6b776b800af1ef5401d82b8dcc903546095f67328eb7b938f860f86efb48ed8e6f9ca9358fb50fb55fbdcca810a0a39c52b525927699e9632a48ae6c0f23b208d86a0663ccccf0f6675a023f9630bfe10cddbb3846ae350e8aad8607d0ac56311a01d0d1c31baa57a5a411483bb028fdc094671953bf78af4440db0b6c4923cce64f67c55cc689b35dd1c1c617c013f7983810da73d5a5ce141d6b722f02645497b45c9882993aa25e78d2cb63905c59df59bfc57bcc25a31db6d22dd94ab8978e9349732c830ba1f8116aebe970ce1d8a015f9565009f0812a14722d822b0da8b83461402ad164e1de82606cb50276754d11eb9f6c8c1efcb521f43bd627c7a3ad5e59108c9bee31f0b6cbf74b0e2c5580e96615e333cbc676c23b89dad4f875b1dba5df37a60b15d50c85a91b0b50fe91ac299a60931656aa480f1f9fb5c97b752af4f26eb580ef6dccb984e20b3d5ac6775af0b67570f4ee8ea234e86cc135c46071db6cda80a003ed4f761903cf33e8234a8127c981f394e4e50017e6b3f105aa31eb8c721c1dacb81e38e30c77d14ccb67be9b949651fbb0e191bbaefc4160dbda3c8270de2760282519db04ec48af5f01827089846352ecc03e6ab8958ce1b0ba20fb44fe742ba261a18105c8822bdb107e42b51b2772028e6441e8fb7f64daf3f6fc92b2ae6a033a82ce0c033d5086ab761b7367611e7fad124008182f5ecf414d84ec93dce96709900800a6d80688e9bbc8ed9aad7b7e4005ffc9e8e0190bf588a0900921196d9967450f35a324b598bcb1d1b1fd1792916fa89d754b076013101f144b8b6adff2d5990b2d979bf2b65e4f06edb9c4916e8246aa236b1ca7119f5bd7c3e2b17fde21e80b91359310dacb23660798f422125b7a013f88d17aece9634da0ff6fc6bed4bd0a1def2ad653b8967d010cf34fd1a0515186b9ee091a55ef51ee46aa09b034fa0626493218abc9fecc6237cf831904c67fb9519345cefd2bb5669455822b85860bc96eef3b373e3cf63960005426e90da063fcbffc614255445c9cb8718f74190f91321126d8549fd6fe6349671147e8996558977d4d423802eead6290a1c8f90a6da8a1c0402eab2cf0fd77acba4056721b9493a5282c0e023543247229877b071e490c6afb784bb22cf475f16f552960e368063e63e415adac61d85d28feef0d67498a7e9c1358f015906a1d9b41a9efac6dc9c2eb1d8f2f8f883574fccaed02860c54db154e8f768bbe99738214cea908d900e2c8d0821d833f8d2294fae802d9b655df84ed73269fbd0cf2f779aef879a704b0913f9956f2dcd8d33249ca05faea7d0e2c88106f170c523a7b11ca31b7a3857e8aecf66258550b510521db0b4added478ec3b4c6709efcb00a971b7fe44edaca265754bc5e61baa64b696e0867d981925784ccda73bb31cbfa4a278c637408ab3a984f18957eb5eec0d16801860c0613dd652aec4c27c27771a96234d3a2a3230ef36f9de53565357d869c0043e505faabcbed47774dc34361aea171fa1a979d6ad605d260298adf4ab8ad0fb5b8dd9c13d0c81da0c8e425812858773283495c0bb2ceb01442804b3d181a08b6f19d69302caf2b7fdcf898f0a661370c708214bbd081c2ef4e76d9fe557d0b3e51f0ef1d60e2a7c92536ce0b74a13d6792406dd24df9cf01a64e002e68270991c28ae4c6fff6f729ae8fe56c75f02264c96ae1bbb7b77e54c60c4efb33dc0219c5c7f1f8bb586348bbabdc3eea939caf285605e572b8bbaa5aad4a2a4df9036a1cfbc8c787f39b0ed43f5743da21d5adc889b7a94e63495ba50fbd92822009067ee8513c08bb0576cd2e0ad1decf5fcfea32f99344d250f19d6f02f2e23604081a3a8c743263e0ad76228e31bf80f5e52e6c9ab44fc9b04b1ed1a0c036490ec849acb9f1eb0cc4b8c04cfc515949543581c598e0e84f631f270f9851b4fd06b9be2dae54992cb7ba9b18bb51266952579685a77009f2eea560966fad671108f54c2d0a7c671a9ca68547b7dd83f91619b71c35d5c48335b37fcedfacf6c10703fe837ec373068a5e51d4697a696ba772d42d11eeebdee694961276248415074d208be64e25edae310d4195c86d4c549946b6734aedc945d449ed716dcf1003dac9b6b0588bc36d9595f9929b61e4f20240c459aabd3b479589e05e32ca0b03de8df482d65b646fa350937b32603bec10fd3fc0a9aee603e097124ff418110384b0b3578d2881a36209846400b86be81f9fb7294eb329d94336057bbbc8500a1181c5868d9c4c2ad306911ee758559b1309efa063ff575262dfde523e68be0a088518432386f62a38bb64332fcd7126709c7df21b148a69703ecf111a215c0c1b68a537439ec423e803d30f8b12568d55a4f2f2db34af17f00c39e640e51e049320f828c7e96f5485e8e427c1b0bd2ebeb7c5bc2f6336fb3ed0579db92fed0a69b5461a4e6e87a085a918c4ab0af08e10e20ade02ba254f7e5de440e8361909f341fa4fce23b0b8937f47cc801d0e2988524410447babf5c0d7fb2f137c9c073f0894f7bb2c187231831f1e947a3aa6a4daea6d62d0465d06608a05ef0687053253337404e2b17343a77df9951e321c30d62728215481052ec943b292412b0eb5f8af91b98b9db54fbff43b08645a552b1d4f00fe0bcaf8849c3c7a3c8714008ac2787448d8e448c5acea8497a14589be6d7c4da8917b6b9c659d0e0751ee00142705abdcfecd8433b8e3a857923664bee27b17ad51b67f3dd4e46d7eafbd028061f23c24646b0c1fa7d4c75d375909410a024698d296f940da19006d1db20d6d53a98aba8fe94dfd293de09486aadf9deefe3203c806e06bae68a481a0cd05f4761ecb6019817e4357e7d68dfe6254a6a77980a71a65cfa16d0b79de3628094a81dcaa7020fe7c24686bd789d633e7eacca8012ea76c4ace108bd6c14156077c10c73486f797be0783d3a0984db41cee288f946f83f36153f3b4562f388a0ba5c981f729fa02a28bcf854a9c364e9d592274173dd553e0bd9b77e3c40bd6065e4ecc91e6fa8fac1f6f930d3807a7dcf8d9f8f7e4612ec41b66ef1fc73d1203ea8ca9cd8902435ea42e752a5cbc1eb88508fcfaa4b4dd2b96ae07b522ab5d02888c1aa3ff57d73629e4b8674c22850af0d6bc391163251033f0b10b923ffd04645c42f07f1e3f9a19f47f4cd91925fb4d33729860f0d5c4bf4549b32ea60b0c6aa9ab1392f3f2c7a3939c2d49261868ac00ab981d1196378f666cc9306b0e070596909e5d1bbc20113e74bb342bb52bf451f2bf71fbed525b50447196a06c0886d88d5d6a4c0053d362cdc956f8c878a763f12c82a51a36770515283facf109c05a6d3e3e0c6e9920fd9b5f9e610966e332fe248b468fea294f1485bd319a076f875ccb4ad7abcb77dea3720fd2038c4181c0482cd193ba3b6f87944357dd074c2f387da52af13aa2e56f498c2d4a8d8002b42115eca04d6bab015a8d0b3100b26d1720d1e17c237e6295606096eb8c3689dd45dbfaf32e30d24f5cee78db0368763f1575d22a64fbd2fe250762d17cffbf8544498d5ee5e246ae8f8d970b0441250482cbaa569f8807924fddbe2e18c41c117c48cacb792ffc3a77cc14ad0131dc2ab4a8ed3f140dc260e514d9833f8e3c57ce85ffd0fcc114e9886f5d6200a73e94916c4affd650288436f1a849ab6c1fe025284a8e988073e78b5ed78e0e714cb43784e28e1fba6cb38bba2d618a713b502fdde84b8da7ca47d1fcfbce0fe5fa74e20facf1bc8efce097f1cc9665fa552ea83d353dad124cc5c685bfa608c2f5c21d7103569366be73327abe2cb1923fa4a997541ef75625ace6ad55440bf0217d2bc2032cfcc4f7866cd942c8e9d5361b6306b519fc61bc0895b170d107617de8cd532959e905fea3fd2e2dc55c8c3c452f51f4a5dea16f2afae492e80e69487898802d8a8983e87f465f253dd2f0a85131e79af73f16db6d1f02a24c0f3e2953c286ca07ec634a4b06ea517c6a011ed2991d7be7efa4e0bb547230690efa433b356e22237a8ccf8851d85d7910d82889da001d7a30c1124fe904aec307b5aed283143e418b8aa93a4f239189149d87b8398fe70f2f9c5fadf07d339a01819698ab9a2778930e70865b8360a6d928145588bbb10c80c3b71f38dde3e80c86867aeae747e556bb8c332350e1fe34b2f1cb56f8f247c9b1261449a0fc65021a04ac06cb88975f24e7e071dcce4d93ec910790e9aafcf31cbf894b56238d062680dfd806df4ccf2f7feec7a9426c4f4dd4460941901640b9e3fe7ddeaff40f78b21c042bafc3b576e94a4bcf629f42bca878b40434b188aac2be501c406e0e7b092294e7a3d0f26c993793244b4a58ae7b41e0dae5ac01af40bccbab2b1102583aa41b765914bec9bba0ea6419b65b30f2984ec7cb3df70d6410513ef50c05d4bb32b3a90d747d66bf2a53e07a9a4a1749611ccefa2684325180247491b90b0575fc119a2d1db4a9973028a3d01d8963cdbced01cd2024a7f0b846db6e250c1bd6a6d18844351999088d2c1c77fd7ef6693669bbcffb54d3ef3bc1ab77740d85d1306fe8c22f1c79a5b9dec4a4d3c2bfc1a8c569a255bbf6d5b9796380820ee078ec88f381e5e24a4ac94704f7a15fb97108b653de4ce42cd34dfd39ede408adf6c1b7083cb30672f53886945e6823c50c8d51494477d67fc6b6879b2aee0530258d2ee62464479b882fbd9d07eae8abd31ca10f07eb9aa7f167d6e90d6706e1b56411892dd26789a7c233727642656d630de91fc0b28c38c1c23fc772890cf846fd4aa9c59f83a8f74e4de49e72fecc09d2734ca0d6338eed7e6db968e705049ddb2986a7c802282af05f608442e7974d297d4546e419f884f42abd12e80fa0c9548bc8b8ff0beae8da36b48902b258d3d1cbd85b70bedd3b5c22c215470cf007172066d933f7c1f02509316276ac2a5af13c9fe2b7443fdbb2147b3c8f0c4e92bc824ec0ac15d0411a917495a3390ac931dd50735f57fc1afaaaa47dc906bc9110b97ed8963ae0a55bdf22ddb247c4beafccae77c533c49979852560200721084672d830eb2bc623789f9f46b74f9de39532949e3128a31b377cec1ca604d0490540fd443b9ca2614cb8a3389190c6c2dbde29469f6e7bd9ede1db32370cd0a617cc90c10a7a2b230637b361489e764047dcdab9a2fa76ee77e15c714107ac998b1eeab53afb989fce50073204fc9830bc854a5935b240ae3fc74b286b0be2e5c7f7bd29ead9199983e86e52aa29f24b210e6ac4e1fabc63307bd758680004de281f676f8cb6755ce0bb344a5502073004563992b0534df98dd23560a402be69ecc5b2d73dbff138ff22e91e47a6f8a29b683b57e4535101ca782911f308144950944b53b1ae17f69321437b3e481a850030dfa7da827153cab7c934d70926a738d6ba3ad8cc402df1ba83bbc293d0735734b96354370684635f7a722c074b79da43dcd1e4242a980ed8bdc7ed499245fe9a1a97f0031fb6776189634c0f5430ed9fcc3fbe11b31dfd9190e9b2762ba0804e68cc7b8ba4e69a6d7deb7c09cd9593a8a00d4818e47264ec27bed0afdb7dcd2acab5f96231c96c99103df50e031f595d8fc292204e1ee9c8f49433b12f644c9963d77e7a8c4c89a397f56700e0b24259a598c693850953a3114e274a459380f5f57513fd6c5d621e3ab39b0c60012122b0ddc882c45f400653507fc8142e1a248bab438e2bba4b848935d40255f98c8a44371d3bbb4b5ffd48a48f2311d6085ff2d0b1be185ccab7a8ab2f0864f5cec9fb1ce23974c1c00c268482e287d1d5f6c467faddd599fbddb22b7107547cee91b7a766f814b40d0b1d9e1f2003dcb2b9634395501f792343c815a103fae5bd957ade148564fdb4842ad3387e835a86f58a1acdb75d74186ca9e1bb09b6b3fe9a1fb11a1bb64eb0aa0786c8428eda75b625f1b372fa8941724d6370058c99d3953cf4a50fb6044b557068e8a3ee8896aaa31e34b4d1553daf2fd15300c9c569135127b9f540eaeb4ed4e51e8b996cf1ee8e6e82b04dd5cea879bbda0616bfe46fce16b22f893c7847f5c81dea0af5b5ce79393ea791f26890198c6105add1ee09fda4f7ddacebe7eeecf05f3154339c2de31e8135541974372b376409bc1e958ce6e68e6ea5d21f376b1bcefad805389819e837387142aad92084280948cbe5f11d3ed534efebd43dd7c07ceb00acf5c8a43f6a5cb3854f472ef3ac04d79634a8afd9aa853e97484623b4c76894bae3bafead38c5069401d610f2220d2106e7871c94f2ab94e41bbf3b8ec2893a9e3fec92dba8a2f0b13b8a33d28d0c91e121dc31a4a1a8e5566fc5004ae23b11e7fcb021719e67eaaabb821cea0f0ea0bf4acc9f5f2ceccd68fb7025f5cc9335226a30a525a8da4543a30b6e6d8f0da5c6a13e106b4ef250d16d742c93a2db211ec07d849c604ff5a1e4156713c60404e4f283f92b41d56ac37692769fb1714af764fa7b94eda648ebb5be85f91e0fadfdb844198c9418d56bb16fa5b270b49378bf5c40f43bfa65c2cd1c9373a7e6fad519efd954a548fe62fb122ede984fff58301248e30eb7c84a5dc25cc84d080dd46425cd01f4312bcf0956bc9d66d7fe90d51116cebdf0442e4288320aad0654ec94abd965fa8b0d6e5e39bde9a5f2810b8a53f7064969982da4bc132fd607668913450b7b35ca5a705b8ec13f0b54dd8d35b8cf5109cbefc053a54732300d6504c62d589bd50088b744780083706533946ab571b05b9dd9833aa0e31ebc0ad1b584d762c13e24b8d22bd416542114340ae19b80a02f8d33afd0a6e6ffaf076dbcd0d2582ee1d53f8bfab3e68806d324d8e566e5a282963658ab11a5267a0c34a75779e9b16d389a634d6d740b83ae5eb307ce2658fa94f53b6659ba31ee04cdea3e4f14f4cd5543e601e93044f4b5d77ca208b4aae7f14438cb806592860401df2470f6005529a013b1247322f0d432aee736cbbf9cbeb042bcc9856cd40ca93f62d05141da38b0ab8f544bdaae5fb5c3860d67c21eef6e9d2d1cf566410ec441e7653d92aa8a945cbc6fcc19855576017f3945f25d7e23b711e40d13e80f1d3492e746069a5fa2d382f36f459d6a28c38af9ea752c11f19503907d5f880ec80288f723e6c5d0f3dfa7df27e8a63a111bb3636fb6a099619ae06f35f5d0035d066cd10d3d142f3471dec6d40e224b770b9fd43aaf5542a015ff2355364c051581d33559d06875dd16d65e6d97f548d87efbb40299df4dc2a4e82e83ae740f8ea69fda197c4ff4a77e868ae34395312349f3e4ebfaceccbda7ee15aa46170780e951edf354749625867771a2c7c65c7cfc31f9f0fa8ae506eeb4bace14ba04523557ee403ed21c6b45413e3bc32ea9a7e14ffb249dec45d00635bbd35f4f0af4f6c1eefaa760f4ef8480ee0e0f02772b972847a0fe92e99ba7b2ed373575097fc3e9ba27f554cde7478a8e771b6d25e82ce440c3941c721ae878aa40456402b599fa20f734a2de0947e0e18b22ef73e6e7edb1789d024744a1f5c341912003c8e2e7b43470a7a074a3fddb9229fd0f1fb2e239a00c337119a20bc94b1edc06b5aecc935e12c69fc3223f7ad42b62817062d2310b73db51879761bb56518701ea86a7328f630566275f05904e915a92af721e841793fc42e69153a687fcf60461b200c7922aaf50090cacb66b377f03c84a69b016dc52c8e53f235bcdc8300f3db17eeb641c62e9e9690c8f1ceb36955c4ef9357b3088fc80836a2e981ed409dadcb5809296b7a8ba23f37d83c4ada5305bce8b77833009d33cd616a39b820f2f0c9cb9c4f0352e909cdf3d89553eef83d9a5d4fb647869f20970defa83a100edc65e89f427512dda0b64a81285cd9bb83a38521d2ee7022e83c85add146a0400330ce55387cc0f985adb64ed34ffea2a892173cda502aeb64b02f6f4aca7027a4acdc0a3ada9688106623656d06ea654dd3b2d9cf497460d225a4a45eee203581f4f119cb63e97a616dc6efcbfbd1bf2b70b9db396d37646a50a4b366e1f0fcb5b534e66b9d951bc512ae94681c01ceb91930369d9b862472799123f490807a7c77e977f2e1dd015352f0042ec4cb5038bcc0eefed2a67b60d44c752568e0c910538ba03e8411435dbbc222cab9236e34e7d599cac1c1d966d3809911eae0580fe87ad52a10f16505a8dc5130784e6669284ba352b3ce10f079266e1411e08d826e6a7ab9a2b4b603b0e893f7fa0c4db7500e6dda9671b44705865eb8f510e47d8aa315bec1a8d7bcbc1f3f880df35b6c1210f159dc082fbcac34595b8b70d1a8bcd0f586daa7910d78aae21e9a76f8baf1e19cfb795d017ee1407038676026ab556644142ab26d1064ed14e427bf6ac2595190ff66b2a5a33209b7aef1a0fee9406b515eabed061f3aa6af000ca8629c3707a3107d4e9734682bfd75e990f40b13c15a4e9acd027f633683bd8fc3f8415fb9895e17f19c3a1cc8e3e7ff6033eb03ae05181e77d692d3f2a5b57b5f9f59912a9841d2aad4062f0766f867b0fb44ae71e624d61ce7f32c1614fc8075779d37405e4ad4cdb0435d8a74587d5030d0afae8ef9baf504e4324e56dfef696e45a8c3b51938a35e403ef1f7c356e0906796e708c7625498e70602a9d5dc2e7c1161b4acd7d80bdc540d575724c6405bf410d6223d777354807a5f3dc60cb1ad5ed1b3fcaaea917d4f0334c508b600c0b4c6de0d649a32c5f5943e515980aa331e03f5fd13e7631ff9a58d48b635602e4744b8637a69ab8c59d7983bc2f71f9933f89852e3ed131ac77c6c0153d4806f4c58b5e1c7726cc426e55f4979e71a5574055d177b159a7edfb0345e96ab10aa8a70989fc7232327f20c94c7694d9b8ed44bde144ec14a233afc300e9145905960c1a3a16330b13e40c62e676cdd30999731d8106c654ef89bf756dee091307127eb2b0577dd51b7bbe7e05319e68ad25667073cb4137b80a21d5d2fad12b00a5508f7db1b4a776e5570fa188667587a257a79dd735745c974dbf3eb17b84020b00866bd7494f6d585cb6e2cf703c317dee0e8bdb3cc4b36daaefb26fdf8f04e19339f59837a4f1ac5903ca1b7d737fb2f7a5ca2fe15c78cdd564477404910e01bc665395f1340b950f6a7740ac29cf4d60e52db16f748587dde35d82970b0bf65aead60e905061f291ccfcfdc465ca2c8d7397bc68130f87597beef48ae70a02be946624914a5d846c0fd7c7df4397daed8c4b0b9b58910b72233a42c01b0550636151a3f08b7f7548297c99150587a8d01ea575d0ad4bdb750704f240fe0cfa8b7b4c8a99ac6942872dcdd06abd211926ae5505dfef85bf2d415cadfb490aea5f5d2fc57ef0e88b79b2521fb71486c8edde812e5dce68c15f3ac357dc030c7cfecee8513b473d178895c466e10ee687635ca9b722913149e557839ef14906af95345b67b29d25ff5cf2243ed64578c81de940903a392d7245ff452439520d3f0b76c2d5517b0ae6f004e9c3376c240e19e82401c903fdb047ae882ef51a049a178b124d1a2a9ba001ed1d18c171bd3bed149cd6b2e914a4e3ca1430a94905ab32e8890070f15c4c29b527e61fd5c54e6db08b68c6b6562c08fe3ca972920f51a9e421f88a46d8b3fadc994474970d5b098e0f4cf9535a1fb1cb03f7b9dc03174db224bf82473424d5a0971581d040d16e76e541ee1dc712bf2061dd13b9049969d2cde38194412e70772492d9abb8f5ddf8d1603a690ce99433fd39ec3f041f3f8464719d5b46231c3e2213f2e1c220f9b41ba8846817ddc731fa04cb3f0bafab5cf426c1507ffa085044c748deb7f15362debcf8bf56d583b844057cde0d06908c7fc5d9080d44a87dc0a0f8e80eb634aed9817ed1f5120a4458eaaad90e78f03e870d348ad1aba8aac667b7a1a793b6d8b7ad118f8b853d80cbbae62208ba616417f32ce90ec507edd16ce4caa123e07109116b35a4d1851619de89ed0641ab02ca47adf518791e3d602ca510047a8f8d19f74cbf7be2e527c23c72c80fd737baf007c12a27ab5f932e07083ec4ada651298d610e0e24203d9803710506a6dea6a6bad1acfa004c7eca1b725eb0f18457f092826cffcbdb781e8cc12e09717bcc5c16cfadb968911186fe9ec4448d2bcec2a2de2f99e41282704157340a5ed4316f6701fca59dff1c7b5b334f68af0faaa971c37535c7cd909d29bc3300caebaf3d33cbfc2639557a7803638601b8508e982fbf2250c04e9aa97715bd05a2fe9bd7b867880c203a523aa2e5f9b61f723928b1614a64d8528572096f4202e1963bfbf3a045ed1b09d7c024bfa808170999096eea0a5b2c365df056ff890674c526ed9432467ea1440eae687419fc7d92360b24d98701b7737fac004e0a0113eb9948c53bfeb1e8d71f49ec110f97060f3e30ebecb2711353ed571d79e20133a4a70c89135e1f33e094928e8610c64bdefd3fa75acb594bf5692312f1fc0a50c06ca1b1001254596e034d7753c6c51407fe23169ba104f452506003a02f01b212d22f677df0804c52b637b84a3b4ee0f5d445f8bf15087845e9e39bbbb7010c4083a9e52bf907775c6bfd9fb57f4749fafd10a19975d3846dcbe514821f04793e44981a01a8e54b0c71ca726aec96255a2df3c1f4cac2edcfd89810d67a0e35f77a33282e926bc2ed318a27ab8a4891652e85d506ed33741c0549f2983e0c549d0da626918bf07739bc38b32676493fb2005416e3a6079e940cb2954fc20a62461a84c6978f96eca0d6b220fa3b2a79134ce0bdba3ea4be838a8c12f3c10831cfc3c5220269d2d4a6f95dbef19112a36c1d275c937a975c9f78a6374aab072fd25f896eabfd563299205a2199c38bb086540a578c376552a8a4bc4c768004af4b43046787764090b1d416a56be5dd77236315266c5946e663510337de4c0f0c0e5b435bf6674ac0ece405cd097e70c8a775dc60a3c71876618cb0855af50042a26e53c184c019cabc6ed16332865f5a2292ccc07cb5b3fea77e09e6528406ee5d166df38353c8a902644f6428676aa3748c14c8fede6d8d8cf82cce36a505471cd2ca4a15b9ddd513bd39292be61ee6fede01341cb245c08634cd3ef22000c4c6d0e7225832cfed1e911de8f08f255007545dc49d78aad2ece5a6a4e318010fa9b55bab50913106e48bc0d7de935a07680478f74ecd65ebee3447c1684209c98d24e35a45cccff75655c5cc4327d904a71b2c8be1150cb2309e7c8fa7c9023b2d2a29d2702605eb0e9b0f84e1bf83046c72b2fa0ef0d861ed4214bb1575030c04edd6940d233262259d7ff30db0c3aaacb26c18c8d1f47a4ec6849ad171044bd79e47b5e4a42720f6b0df4dd70548853193ad74444629d443e48c76f84c0e235d4186f9ff06555a7b1a9477dffc9f82bfc8f23253dc068ef1124f0add7406e23b6777195c0739b87c2281b44b86e5ab186176281c87a8c2a8a3260c466709aa64948bf9e1e3633c453ed7d47a55c3bc6aaaafbdca432a46df84bc2a37e70a6c5d9e5db20890d73a5a955d64f4775e28cb27aa288e66d48d97c1a4bef3d100 false +check_ring_signature eebd424b83513120e43438a07f303d0f5f409bbefc28b0c9bacad0ce3e9531bb 23dbf62929d1505d986b1c62f44a0d10add5a117a687c854382faaf9798d1271 8 55479ab3e3873c0f5d3b5f976fdb518d757abfa1ae43644090a307553bd64530 841777f1742f0f6862074bf01e7793c09f9cc2a52c8505fb2f2a27779656d490 2d8c7bcc0f3bdde36b73f160064897c5a284cfe8afed200134be1dfddaeed395 d59302ecbb0259370307975d94d9d24656640f77b75b13ec2021939c9c1abc72 0afc24616d0cf04a36b48dd9e16cd3f7edd1964488d7e0633f9b4d2159e97a2c 4311ff3df4dbe9ce405a3aeda5456e897eca61ff250470e355312640fb0f8be1 bbb23f46bfb2738f8f6e635d3ad3e2c83fc84dd80655ac56b637e88165cae098 771a14b00d28e02b90c4c1271dc3ae9a293337ceceaaceab3a66b27709c6142f f662912b5bbc458dbf75c1f013c0543721b2d1f888859445fedbaf7c1bfe9e0482c7298753dc831af506fd816459d780be0351c2a819400c9f02bdcbc3d4b90a566f2a22025962f086006f2b64bd7d7d42b755dd6386d2361449792fbe5b580c1422890f2df41d719bf4a852b7b26b931d13e4d4edc2b5a8d6eb334ab280fd03fb9eca01fc5a038673c5b53b83f0a5e11d7bebb5e7f6f31debe5a0379fa262afa4ef6664b959cdabf74037b1e8d03ae2c925bb624890f421bc3b03085a40a60ad50d4bfede540930acc11ccf07040385d9e474a46d65240c12e0c5173c40e400866a92b58c4cd6d2a3c9b7e97e110d853089b5664112ce4d87e52c96ed71e40c74bbe64643eb7bcaba9f7e28112d6a5d55ef45135adf26aaa2d7cda2ba17a004ec0e9e0b2d201ce6ff175c8f32180d58fa877dc6fc36fdaa49c3010497bcbb001eaaedd63cf8b316e16d6ffd88a496cb8340a2e6d6d06e3e62c8b02f19a1f70b2f6268d0e7624d0a003264b0a6a9f4755c516708ef454245464e25550a85200a3b282e04f0073a09b89644a78c40fa2f25bae6898080a6b8c828e7060e63af055ca1d8d75672b58e8b572b1859013088e9cb0036ac116905e5316f4daf4b62017ddaf971d26e6dcf18189f9ef47e8fd36751da1ee0ced322c598a12ccdfc380b693ba14a0625025cf567f44057db408f327dbf72943fa31040fcb337344cf305 false +check_ring_signature 72395ed086651bf5b2c4d74025b7a44291226c7b504708506f86e5c7791d5024 bf7611d61c1fac15e0e8c79b181b39c88f4b507bcdf25ac1eb264e4cc5374da0 7 b32457d8247d7370b8af715fdbfb527f8238824fb55e35033b87d8bc58ab1cc4 58823fda031cce19c58b070adbafd8729c0f848dc1ddf29917d6cbf57bac8705 a7bc1b25380896672b6a0adbee01d361f6b4a681e5bdeb47a8648e84393a7ecb b67137df343d3f6114c163a5fbd1dd51a359e14a2084af660d7bcda70aa95189 d821eab1e33867a7e7d1d99a67c4ab8346d00cbd840e79b85313cbc880b8789f 3b17ed3a88fe1a418f234b8ff14bb53fb12f951b4f849a282df05a09ef2328c8 eafc96a812d90a11c79f173dde5e1af473c54f13984e23fc44b85ae622465cfc 2b72e29b34ac593e3e9dcb1ca42d196801333c860c13189e6fcd71b14440720ef8eed41c8096643aa1e42f2a833e47be85b3c54db9a681e1cef8c6fc8df0ad057eec10acb34a5c80a32f9d77bb9a5b722dc48674f51e9397ebcd6b714ddbd80b39ea85e24c9a2fe788d6f8d7620d215fd41ee5f3610a1703560ab7b878aec609ddf4169f168204f4c79304d14176e4779ffbd05ae33281e31b04d0c6f92dc408bbb60e08b929003a49e87365895b3b9a3a725909a161db4de5d4976ece48c4054a4d84d57cb0f9ff53abfde01975bf653c96a12b8238fe54afddfd918dd48d0c8ad4b5e6b595e63543f83d80eb4ecfd1b86f1ff69a02e20e97969b3123e1010f151ecb130e88ca366bf54ec0382a57c2505422ab7a68fd2c9c5c1041821554098bb57c934f3bc17d1b2bd794d77a8840f5b05462ab2c379f0cb132c9e1a5c003ec7b6452591dee94bacc51dcf74b4be131efd226cf15d980005041c6479f8a0fb899380b3743d9575d3cfed89a31ddac9d6626af7d9efa9aaabc1bb8ff3a6b00d0f9eea10eb03ebb389ab7b96a173b676c51f55d8011dc13a9a0d458253c1f0ed7e9c8b1871271ec3bc1051f759cd504d86aa55f9949a7277a655864ac770603 true +check_ring_signature dae8760ca41a65678be77fceb4da43a43bd45624db3684b3d1092482457249e4 e9beaa878d3f02562072b92795f11899009c4952f6a6513fed41a74e53f81480 6 4263a64cb5c9e135b40417512fd0c299539d33ad42d99998932b6fa8957dfd59 d8dfffb464cf43234d824f133755532837c848d021c17179d2884c0af0c6a777 91b963dd26d623031d3203c9e2028286872deea2b56fd31496ff71159874efea dac9f8c500dd0c4e36cd3868d47fb2b2396340bfdef4d2c5ab628a2c8c4231d6 6919a81e93edb28ed8e71ccfc25be720387fa031ba1ce5989ed0dbe1ecf2fba4 cc4f35451a75ce595d4486cabcb8a3acfa9550f284335290d698b10f5fd5c3cf 2c8766445c7ca1eddf3b3e5d8f82bc78d8e3354a3ebb3c347ed633ee853f4c0219db9345723a37c1ee73148e7b553bcc26c6e0488c77610f9cf342e020723906bf439112f9071296e5d080c0b51b83cef3d3bf0d906bc1f5a6f48547912dd60961b742bd808b05a1eb59ae1b377cfb986bf686f0b32c281c5136139524363f066110569563292c92333f5dd9a4468aa472692f6718ece6f1fe44f12b88dc5c08fa23e0db1e5aaba875162701be507b7245655a88fc614dc41aa4a4a81b983409ae4d721d94345072c494e834dc9cd1570f867dc3bcd2454bea36fc241ba228e08f7772434eef51311009a6c650f0a5f659ee5f6f24715f83a699f5897b56d402c14fe5f1bdf809d4d08dc31f9590bbfe6163657785af992edc4b348811d88a04a76ce2b569f356ab2d17d81e8140514e0d88196e136e5ddfe30a6ed3be288a077a3595485324f0d6c7f747f11c0aaca24ea20ad6cde3d9bbd7002d5a2e456506a9de3eed94b15588b26a560ec32ce62852276c4b9b5f984cff2c869b17d6ca08 false +check_ring_signature 8d0c59d52e6ba8a8369c83043126a23013a9ed425e262b9590e5421c0ab6cd36 b9c87b102c45480ef8197e23817ed51a76884c7d13886b55f1f14d9478e4384e 118 c5a63dc944a4633decc578ae2d603daaf1d3fab2a8c0e6b9e892183ac158a4aa 6ae391da9dc5b48127f1e479d5468b8b6d5de061b1804946e3b537bf6dd82904 b021188d44fb3e8fdcd62c42d9ea2c3619b3d4377e411d49ba6031b5aa78ab81 fb5d67cfac8bb4aa7dfa760b68f79fd23cdb39c88389b0a3000c0c0903f7ac24 4c5de30d6cc8c13249a0ceb962a5d085252fe96224618bfc85c73609ea06b210 d1fd72ca501b935a40a65efe34f58ec9557fa43f5fe9e763e45243b6acd61f68 53db89ec7d419b2c5bb1094bd5169ab2f9252c004df3a960667b7e9fa1c45bd0 5310ef93f10c179427f3a2c5617c315ba6bdbe378e843fd0e8ab15bbcf6feac4 9d1754636f4923b4e45df73606220a04f13b7a8670569e545bd29919e5270e95 a6c276317f954e2956e2f28205e9f1d1ec67dd91bfc4b4c102e375154a02a6f7 289ff0182c7346e1909193ab5cc40f53a4f4bfa07bc23befc15f3e97421c6ccf 9f9bab224f1a9e04dd27533e0dbe90e5bcc9a5cae482e9d9610183bc181b4120 293a19fbbf6fad7781a0b59e8c95c75c55285021f894e95c962eec41afade3d9 cf4b73d16c1431f659157acb86a02f5347346466bc388366c01ba9f99149c81f 1b11402b70ec832af93248c3bdb293081901b7e9b8eae605e6d6c83d4a7ee869 9297eef742a083587975aca625950e8e2ee8120af06c9beed138a8dabc91b8d3 5f2ab6d63e836e30769a231144c906b9f475964d699349dd020988ed7ad4e27c 115f3dce7a4509b99812a09a78eaa9477276035a9ff1941c7298fb836db4d9f7 f025dc6c09f9df6a18ba27c8cbed018a5c414afc2af07ffeab3d5cf8fa66e1fc 81350b45977fd8fa00e5ac18a87a966b2c175471fc4816f8c25c512de1923c4b 239242b74f1b83da295e81c03463bebe69544c3572479171938bda9f524ab957 254ccb77e9ad10171f1ce527c56cfe6caec91a4aa1066a2ae5fc0c68bd303852 cec28223aac8e4a1cd0e9f7e7c607c35c47635526715ebda916daebcf8921d55 acf255cba74993a337c04bae8afbfee56626e180f245e57ec3e19f3afe062d24 a079fbec965d07ad47d39b25fb105b11790d1c3c0116d4368d8614e85034b919 11e6a0e0dfcca235d1a8ea3ce33761870bc197eb1b574dea3de69fca7bc10f80 b7c1c702db2ebb19c07c8bb726c6f7318574bf36cc1fa645dddc46196cfdf6e8 ef9bb47c0bbffcb9b006e66d505ee85cfe78e54c3f3e456660e4996a0fc0b072 2826d48a39405bdc7f64918ea49945d0e46980886731bde42dc26e0fe70411a5 c758ea61a7f5954a6ea30d2574f473c2be85a4fdba557aad2b8b777317ccdbe2 c7628f566373d2db40263e9831e2654056da0c438314090ccf6f74786cb2f663 ae2ade93a8a4a6c69728a5c560198006e1b57e9a5f00d08ba004a44ba354f740 2aef4d66991c7ec3c6385a9ad45022c926bc286bdb7b322dce625a327d86c153 9e0d0dd1b9b2468059abc1be159b5dbc77b9c6b959f100018981f4da35e2557e b626c67e1e89c1f2e6757968ec8e63551f154e0790a52a14abed260e97f76215 289ff902e428951852200d35a78031bcbf1d22f8ba29d607d18b8007e145a78b ac2545b2cf891dca5401188d993cca03816f27becc006d62f9d3417c3d2fb91b 924ae64c813599dd5a992f65a43cca109ec4920362bff492488172a5d7c06668 c11a054a5806357b7169c49eb7cc5456a23cff2319e55c5a66045745e781d2de 96b7cd184b9c932c039ba9e8d74f4c080404e382e04a23bd2fce1d00620faeb0 6dd8cbfc3058c60b735f3a03ffc16f45501a803b1a47b876e737204be4a400ea 831a8b35e29c445ee2b09ef8055fb089df1906ac100f2e9da86aa99ba79d24a8 391a8a7070a76d4a20cb6d908db08f399adfd01b44a13b84b3509b942c8613bf 2a33bf19575dfb2cba2dc40efb6f830dc20590639d162f497ea5e76b30d927b1 7ee8d096789b0b67db5e26a50c1070088b81ddde49a85aa9ff063517dfcfebd3 961a12ee85671d20474f4e1795951ccb8e12b1c808484cfeb6c29405afac6dcc 93a79f763aa0526757c8f2f0a38ae34d30732e24670d100ceaea833a09b3ba42 eb84be968afc531eb632c05b1d6a063b3dbff55d005a8964a2b42861eb47197c af8ece7a1f08ad52da43bf21970766cae9820eff766e9511327750614d4ef960 d494297cede7783baf7f53a459aa7d9c1d70c59d1a38289e2dda60ab33959bc3 e02a8f3abb78f4c26c3eb3919f0728cfb080acfaafa7cf692553004e830218ae 12161e66852ce26ece57f7028fc9674a3fd399ed90066e70c9c8d2b9f60a7a33 0a120a757f02ee8ff13b02604b9d000a1da8f9ce1b9944215afac78f3abb37c0 44bc6c8829c45c4178ebf0a0b32d15ec411f375f26a8a919d07f3a9c8bf8b2e6 c2ae80443ee7b4358bf542837be0686fb0b736852b2d70b016ca2a180f88496f 26d33e84638d7986eb6dc5358ee9898efb1df5b3fce88f4f65b7f56cded2fffd 047fbe653e09cfb9daeb78152780146f20a34d4d484c620b512620a7c9e5772d c8157f574c1de937b92b8ca3ea8b50943be9c031727a4a27fa5f6893e8415c7d 3b70a5a52dbe0f819a1b39f0c69d6663bddf5e056f91b5d5021ad328eef7f6f4 39962757cc985a333cf63196a7c188516033621552b6feed5ba2fbf98b963b7b 99e61a2b30efdc26c1c47892dd5eabdf57fdabea8c6082ce73b78171a6c72d17 d0e3851ef6ae082b9578833b277d425d6a7f72d7dd8fa9b069b27ba0f5f15b6c af5bc8257a59530bd21655f10ad9a3aeef4b21b802ced7f115e0abd2193fb66e 9eaa1a22a262bd8ec69aabf32dc8dd27c273cf7c2a97c2e34537ab917e4ad402 71792dd7f78b4d3e30e0bb61426aad15b524faf36bf917bfb42c174cf2d1b1e1 696dec9cec4334f661e02e945ec7da9a5e2a14f2158097dee8f84673cccd4111 d9d1e797b28b775b0aae468443216599346de538f8303d5ff408dcac0de679c3 ba09f129dc552fb15d14e14e6002fc569893e60584dae4fbf48385bf30cc5f3a be901c9afa56c04084a8cc16b0d9729f543334693107f0f4f849887a1e853643 5349363de16ce988ba5532719fa01e9fac25d7f653fb8937889813a2df596e59 5c3d3e5532cce450f9d9e89ea9a45e69dc2e60007427670507fd855307200d66 e54c4edc733a9db5dda123f4067ce6f148cd4c4210b535183f6ab56f0c73b3bc e19c6c7364dc4d23d798905360d8e70ecba2b757b838c030c47ae3917198ebb6 dede3092e337008b1134b3afa9ddd8de1971950ffbdb1354570a5518a6a4b76f 8571d9800482fd7ed3440142345968be8e9c1eb5ecc737f46014fc73394f9632 f92a44b5b182bb46c14ce5dc1388127ddb75bf97fd1e773d3b5baf49d56e4d86 8f4be98d9f022efc9c71a951000b6d7e3e1c765765d9adaa5393bd720553bb00 811153f890c56cf6bd582f6677dd0f5f3bbdd14e5aa3ff704e857bb18f0459d5 9b69e1eb3c203a9e2dc48a9322b5e319e80643bc7266cd9ab3727d1016212051 e4796b3a5f603e9691880a04af2f101e1229d8dad8a057b5f147da3b1f637137 909bb5a5b5acc40df0a899a77d02e932a9d5e23f53c1c6b663888a9670dd3b3e 8291d9e9f40cc5c1cc77085227a97aac96da25bc2379eafdb161ca239fd56324 bbce4cd35dbf37e003331eb470fb3110d20df4819852fdbfdca479f46b5cbe35 c8a0bc06487990a0656ad04021bb50d5c7833eaaa3e15e9d2c60a30f8b89cbdd 01225adfe5ad993b6408bd7d90b4df173fee5024004051753f435d8f9b6e272b 452a8a5ede97a042282a69406f78fbb69e49685ad494d59abebadff5fb2e0b24 d87f5de7096977c90106a7c65ab6a4c97be7edfa4995a6698fa5ca9759ccadac 0db01a549a4d965ec0e3b7554b49174b20ee96815ecfc66a18f3577d11a84775 9583d8f852dd8907fc20ae7522e5442fb6601d489798a4dedbe9c3f71b15de3e b44cf7fa6204a451e56db8a5f502ed6f20a627f42a9b74287c9dc99d8c0f60f4 ba5634fce4bbe36a70ade8252565aad33a4b943e01758eb32dd80b5c55033a57 d17c93c435558ac68adb7b7b69be66e2ecb453b9e2a0f698cb0391c301366c4e 7469c7a83c664ce93b300352ba3f11315d74c9a230b52e343a2fd63d287570b5 b75043bda94fd1d7b7887fff47d70a0d44ccc273e6949af3f0a9f5a53ac5031d 26cae182a156c974b979e99beb3eccdad17bf855a2fc2125cc372f045e81fe03 b752425e64e890cf983070d9280a1291b80535784754dc6afefe238b92f9154d 29f062493c1d81937cdc861cbbdef118e646d291719cfed86fb0d45dad027d8f 7930ec509187da900a2c4aa86cebd8b86f98c235548b48f983233c974e63932c 2a0ebd18e4d5f2f982ed6de3b56f56e7899d33bef71c47efdad15aab2f932f0e bd68c9c405f87767af3034b11ccc41d77a9a80d11c3ee5020716f5c0660028f2 1aab650812b7656a543f65a18c68020b2b8a00cfc2d45f56f33bad5704b9aac6 6e4a6ce0b1d44966fdc0fbc43e9c083646c7df27c58d560f837f9f2104fbbd53 5236d1ee38e03fa936a535bc30380a87f6e9d2b596a37877b6394c25e68d0159 df91d457da2a998d5785d0b2bc3f335ca397e0a9e884d89c3abdb4ac90b4e175 09310ee5d2c80233dd7b26015e9c427b9db120d75eaa5e6ca23ac01e7af0e430 99eba03710de96163150918bed19a491c65122339f689585adec88a8a5eaad5c 6d60b677138ff31d89a7f26856a6fe5277b9405586d5dd2023af0814eb654f61 e39cf211c2d1041b6b7112dddff2cf6ca4fa10cb9588c650b817b4c3d9e80c2c 944facc2cfe463129be502ed5f678f25ca63267b892058de07359e347ac5a0a9 ad92da5e33cf04a23af5118916564bc71b0a9fd67e5673f352f93160b4ede745 fa5875dddbe0c61d415a5dfac3251cf4f51c0414fcd26e358b1a2039ea3fff02 a6c669ab6a647f125cc88c0a9b922bd5661455833fa082d2eabd63cc61a2c2e9 9d72c0bcad469ee506d232760dd9cb6228df38b50cab15b7edad20a98c48aaf9 3c29adedfa194fad61bc0d695618411189d2cc53a245882983ef99b05bc3bf7e 919bf2f2043916adf164a1e9a7e9ac5d2f6367af8191a09c074ac7467c579b33 86c1722492b61cbbb8d118a4a90ecc6632eaf48ae50bf57bf2fc257c07afe281 b0de90e74af89d80bf5544c352b3447a620916bb7d2c1f19ca968d78464cdf49 9a882046e0179f39ffdf3341d818dc905dd0e355b633960af4e889e87a4fc0cb 5ada48300cca8baab77d7f9796ec2fdf60fe5eb7d71b45d35215c6ede333900a26e2e90ac748165a4428ecb1fa22281f6b28f3856ddfcd9ab870948e58a0650681a39bd5ab2a51fe3fc454053797124be7ed63bd02f62406ea07724018be5a0121f41a62369b4790872528659eb59d43b6f6088c0a0affd6690474e1781d6000437e2897fe71a27e3816703e9b283390031b64bf1a8604ae1e6326e1d85415028449f66b1bcbecc1e98389b21f32bb05ca0af692e60eb400e2bcd3b377cb91003ac315324e13462d47e193e531272b54da3f7577d24d3976591fa83c3002720a95b574367652aceb39bac3fecb70ec00e0f3a5f5e13126afadd7eb232199a70afe3bc9fa59a81e16c37c294c61e56a03642f48fadf064451fc17a9b573bed00945e883a126bea9c41323857aa8e40fafe3fb056e2d8babe859c06b8cabbad8062f7b960aca36e8b7955cfc95e102f2d876046047b68063bbcd2848f0c37aa80f708d5c06600e4fbb75d95ef7c5286fb960cb3648b9ee97c17659bfa03adc720a42bd8eafbd635aa8e814d308dd577ca7b49aa36ae7d122fc97f31f28610d38058ce8da98400d5da2bacb809c253ef3c26f90dcee5e1bd6cbbe3fe75a62928a0b9c42238bf2efb08b4d960d59bc47cb053f5ff1e2242431af9a1ce2ba48ff7108850c2805d192f963f5cdbd175ffbaecfb2848cf3354dffb2435857e7b5d2e60c88a2761f6f40648d22c9bd99e239bf84f0ab2b25da1cca21dc867bd525c9a900cc6a025afbf06a9e3ae91450c610222367dfb0a96450146913eef620af43060b69a28e14fc3f162840e2f40dd162662fd7ce5a65a06f22317da14c2b10ad3d095a8e422ee3292bd86c3055da1d8efbea5bb4340b0ae676f29abb645cb247450fe2e91e851a2b83c3d231822d52c380eed2ee46659d148381f8ede3404de19d0fe6fe9d76e455f40cf7183ae0511cc4c8ab901d423d86df7ddcb68745e0689a07c65634836bc3af8cf4e6d056af02c239551f0ccd0a4ea18f8a1640bd5194870aa45f2d5d4fb7784787e00dc97bad1ad0c3c02fff572026ac2919332913b4590c5442663660797c755e9221bda078306a05e62dd451b6c042718a89a5eb93330236fafdde9fbebf690f90208ae38400d935039afc44768496fb45fb8c8b6b410f9b53f5c8095127cafcec62de5910476c1e8b778e564ab3deb26787681205bf09de4ecdbd378716d4315cf049992de0cb07c872967d6f87a0a700561ee35c1b0a414bb81e182ff79651f99689975973240b9b76cc9695b55c66bdec7708ed9200c8074fe59b66d8e39d8b48c19841b067cc2462ea24bcba1f242aceb09232f8062cd2c15e2b801891030517b619f51a076357a4ddd08dd2133789b6220a3716014a6836819cd581fca35c9632628247267ea6baf2c0b50fc0ed763f9041c4ab085f1814da28ddcf9e98947e101c0f84b78a175f7be90592fc9ed6e3be22717603fc97bd44f602c3d4bdb94acb61ac3b01a5f8d82dca370b425498f48acb1efd00b2a4df0e495484dcf07ee287fcd4c7b16ccea24792ea14048891358eff04720e21f18a51993044e933c562c651eb79c7684fa1c148fb2a4819b0f29393e41b05c21e0e618269c05f2b0e941fe713642f2b1ffacbf65082779f231f34b3a67b0b4e16e5ccc87cbf3ada95dae72cb3fd8436c95fa0ff639c09786754ac8ca7fc0e35ae270636ec6fbff92009a2691061ba76149240f85574a67e40c9c7af33b902b26f7bc29e48c9ae3fb210bda3429880c1c8f62f878592d4738a9683eccf0c01f2a3d421c07742ed0b2c146fba5aca255814cd5f88c5110dbeb882ac749d820a3be532689884a0af46a07080a3aca23aee6e1b5bbeb9e9ed5d35818b9514bd006d03fa14e2e04dbbd5e3b68d04d23a7aa39d067988748f75c5c8d8e0d06c380871b4de6dfef264a4540e980147cb1b5d3097ac823b47afb8dbfe1a1f51cee8047b3a8567e384aefb533c9e1c42e75da92591d3229d45ad6297f77586034a7400d93e960921dcbdeece9013ce156dff029ed9ac29361bb1c4e84f7c3c57554101fcb1b25d5fc84920883bd5da61592ed0529ed40df08a0aa4e728eabe6c29e40500f6ef4fa963af142be4c2e6158d5c303421858bb9a49bbbe8479bc5713af0099294c498bd613bd387660584b95934556f2510e3a580d433b3fc58dd0003d20d444484581325e1e206c1f8b99f2bf96bc585125296b83fad73e00074a8e6ed012c298619f6571e726b98798e9a54dd02296d6cb1b7274bbb914cfc49ad2d7c00b852c6b743ea4cc41aedf3f93d4558184b42ae37578f82335a2f2d1e429cb10acefeaf892d29cac63aab5ff48370d9b89c7e38ceaafda178cf4e81f49b38cf0c970bb4a3456977a42c133c99fa0acdfc4e048bdfcabda884167319a16e532d0a4a97258ef59b34153e0e89d4fed6f442737d1f875f49cd765386a17a87656b0ad94f6c96fd330f835f2ccb4ab8c91228aa757ae46e7ae52789a2befbcb11ad00bb582b45cea05e8824901dc7c81064fd3d9adfc5aa335f844f9e3811f8109d0b2d73761e9f3bc60452a871aeac3ba50f41c72a145e0f386e171881ac971d670c54940626d8a810a1b2acf5000c4cd31d2d3550e5699bc78c3a496957a5712c002af8057a10fb068527debe9656f9f7a318f1d40ca0ea5e6b7436934b314ffb089d701ebd33b300d056d27389bac9ac8dd22773a03f109feaf225ec9d5b36b30973dd97b2d6c72d6a6439e59d75e71caac24c90c2b486afc0ca244f0c2dfe2e04d501c2f3011684a4489a62f35477da2d85a3b1117a1d3f84d54e6577c0941f06ca24cbd77bce2de898532d2791acfe2de305656ebe20ce8a9a88a94e0749810e1ba0d68009ef8d580fd65c2d1159092f84c3e1bf414976edbf90331079916b0c76e9a178074f66bae07927a54c3405cbb7bf4dd822cbb367f1743c7c24587f067423fbf9a800b235641ad70d9275fd2b97c8968577e9876b724d80a0eec3630ba542f098574be76d4d303a119ae5be27f68092330e5c15bf8e51d44c557d310f78aaaebd3a3c41fead25c64b194fb9403b318521ec4fe03d98e0faee37f5d70624dbc3479d8d4151097c0c21e62b18ff7a357e016a0a21ddbf22549d44ed6e0acb80958288845a57d02ada11a0661102f70c3d2df67d869220b01fbfa6952f0b68623dc3c747c4d5413b7bac0871e8aca5dafeb6e31d5c0014903c6a94337800072e5947ef32eb0e550a6dddf4ba7de59f4fc4a14a027c3a20ae7d7733d90f0f52f446a473261c975b18964760c4d55d695c992ed10e19bd96c25c601e7e7104c93fa2e0085a38bd667e3e16c4ba656c05c05e73a9087fc8ca20f4d99c050c0dace673b0dbaae2d34b9f2ea0dc2aeecef3e236cd4bf4216d1605510ac64ba809ceb108ace5f1fb692cf629e74a5187f1e18d2422578c3d4b062374103771b7027bb1e745eed7aa53dd78ab3f06e50190bd9325e790446f0adcaef00c97043006f2a65f85d52c38cb74b0050206bf94366e6a0766e3174c2467fb632ccf00110344d6bfde9c2b25a85c3ed733e9c8d25b618f19e525394a092031f30552faf10f0bd038835160f0af7e14ed3e356dd97708bef88032eb59917373cb303a82ef05465c0c03c5209e5b89551e16050aebf1033cbf796f2e62759a0f687024a9bb03b28f3403a19fd1f6900df931cf912051141b171669efcf7d82e671e69bbfd2037ab4c97db161ed1fd8df9c6bfbe4b08021df1f8974266f7713ea190b57593d0a9a22e3e5946e2405ba1073a10bd5bc1df82b6440cdeade1f3165b7932c0dd208cca9602135e508bc6996746030197ebb8e515d4122ffe142ea2e0db12da3fd0c8abd327d39a1df63fec89cb128bb76adbfa897bd8b859504930f1468a7664c0fe688462bdf29a03787b20e7c9c48f37b0adffd271b0932a59f0c7aa54d984408de6276a0062fc8616fbde8cf55d3f9b7cb72a3d12e9670f6317d13d1343860001cabbde78c4cd71b66196244e42d6b737ff35966c018ba146eebbbe6232a340fbaa6035974d84d82cb2d92fb82db41a866843b41e2e34ecb91569a2cfedb930175f7539d9559b96190a65a883cfc934f7f24283a552888c5f48f116f34d88c00fa9b419484f10f338a9711561cdc1745231e9fecaa2e717a7c44e559054b5b02f981fc282db843606c5f4f5136d2885f798a95e8d079af94efe8f430b73d9c0737cf6dbc4968c692193c16a2af62aa96c76a496e64c8c534526277039ece6503aaffd580f79ff706de60ac0e97411df933614645c6ec7e61fcee9033c4e6990ba49c4010746d985936f638e84f19d102799f6fb4964bf2ff9e1d02a4f3d55f0371a4c74bbffd1522413335a6b3c5d9a0c776ded2e4dcd15173adde4a06c99e081c55928c80eeb7c38a3338765fac60ddf4179e4679b6bc8b60dc5ecb6dc8de0830479c738b88d88b4bbec30c277c21420c4668ece2d727226c1bd2304034c70cdb0b3dfa2190f040c57f71745d1b5a69765fff9ca7ae650305a484a15ba8600b6df3bdd9593c947233da529a1d85ac48082bd7b9cb14d9fa42bc66b20b8e420648153c539961b83701f14ddda15237d836d7d3af10458d8c05fe9b8d25c8180bcfca35744ea99b3c7c1a9fb51d08d1a501ee46d6a9497a51c6357f9cde99a40c38eb6c990a71ba959c6437116582a8be7535d133e7f51bbe9ab703bc89fca501cb0f1c7f496d3113abafb21197ff5a453c00b4d37c446e56d8aa6872a23d5d02db1af27dcea01fce619be3fe04bd5e3a0111dfbbb9aae828532bf28090ff3901b490b6bf4bce6fbe619ab2e53f3290bc2bad57efd9551a7e024fb117353e9b060987b4755a4cd1cd040acf3e695d33e660c2a93316bfd084747ff6c6a0ce690988723e2ad4c208174f6a1cfec43dd85002a32a3313b4efc4b99b8b552e33380473a4c52866bf59893624d3dea56f9b84ddda0b1e44e801a1bb34324100fe340a841ae37b3fe95d3953ddce9b2276e8ffcf98ebc0b9b2e93a463ad6ab8cb928060ff7bff1c458f79e89414604d2f44b302c2cda0baf3b7fd4fdd1172d5105930536bc864fa5dc2e407bc0472b622fc6ca7ed343edce475de73a9ee0311e2df30758e9d76912b5026c33118debb5d531b08838c34fc3b3b184217f9e30d0a6310c0e66142da76bb39ea0f4c124f773143f29a68b8b6e16bd4311a62c7f46a9a8080c984eaea1cea142e93daaac5fd2bb7040d5b2c94f070cc3c28902974d9a1f04faf7b1b0a611f5f5770d3ae45277f7ab680397b3cf43d16ec1d247a658eda2067da76d3e88bc828cf81d417381986c80d8d4aef2c28876e791aaf9e6fc291c0856115bf6720e811de716eee2851c604846e434ad804cf5100a98adb104b68c0967a63afb1ec364bfef1553778fbcd9553ecbeba7e5a4f69d50de6fc24c19440b83def198632d10ca35e1b66be9185bb50ae8e6c463eaed13a72c75d0249999012f00849491c19e196b15cb55e5ccd10aa3309fe82a2557ef49145cc50b7b3107f8dd686d21797df98fcd921236c58630ad2436971b4ea3911fb1f3899050b60f655452344e1d6dc3166aa8a5738daa8ad5b137723c0fa4b3577d2e883eac8409089d2876891dd043ac2d90473f081c4e1dbc7e6cfa1a2cda83875923e72dad0605a0232b8bc8b123ee997fe7ccea9ed34f946a3cef569e901543ee7bb971ba0bf133290f0af8acc59dabdf598213065bc6b28bf6b89624ac0f4d1aa8476fd30fa57c2b61b1ea3da15c7aa51695f277a4263f6ae89fcef6a6c1fe2125ab068a0246dc6fda85051a94e8b6c9c9e4390eed8d030008c3d6377d0799eaa326813806c52818a68946b89e8d4d409a4e689b11566b300a2bf1b2f30b36784bd155fe0f2bd7bd812a37bcb42e5ad9d20a39df3cca3eccc5e6885245ee487c6b8ade3201bafde45946a0c08d52652f14264d4ae629a38a1e3461a68f08f9866dcb4aa80e5c61209d9303db2c5bfa460fa43ec308247460a2b5e16a1294ddda8a9e6dcb06fed15287e900cc85bc675f284fa5e727079dabaf5151ea1b0ab3743662b1cc0585b8e6f5fd17ec6cd6a5448353f65e4d29ca17abcd3ed6b0c6cbec4b91e2b1010d5de8684e2ad568931831672e0f7608e63b1e1daa8920d8e7c85760a4c5c5076ae180dcd8ff77dbfd841eb44a6f72980c469d6f10ed415619c0d005740c6a0ae68950d1cd773eca870a84b2f8e7f538b511ba7f009c62d7601c9109a836b90eb6cdef9cb07e106b82cd8a418efba4303b78ff43e2a27a1f22dd08c4d76fb80ec600ecb0285e2e7acf166126cb2e83e24998a40a04fe1fffcab794d228bb5206b9fcac931936232db3ee2157d5d897741802dfe0edd7fc2bf97b24a206dba2071a2420515a7d3ed6c34cab3569a14bf37897cd6cd82ed3e87901da1f4b5f1905f1d45e78a385617139ea2d54a51904412016bbd897437c21ff2276ecf3e1770d2d8322c7abd307ae3263f82d0cbd0ce10b19a00c14a6142843b63f9c47786b020fd491db1f52e0971e61c9035dd8e2ab826d5af637f839995c13f8c5423f27095c610071d1dd4daf136946354eb03edc6b8b0ae7bbe3ac4128712376a34426d51b6519ab5d24dd880be2b1a890699eee7b8b4f6e318a4402ae9d0e50afd7e107be660f858d8a6ae4480826678a23af24c374d4d0fee7595330045ec5074d8406aa547c73a248e3d3fb4f699829c99581eb5f63e54d58a5aabba749fa60e6ee0a2d00eba32f699c659949c79bb1baf2ad698114bfdddf2d74e3b8815e2055890945857a9f6de1fcff43d2803e16fbae359cccd601c101b15a6a50b70bc10cbf0589bb7023133ae7f9f10bb49e31742dc16405eff862308d3ac1029deaee3cb00793327a2ccded8500450c2943bb0661a4bee0eb5143d3bb7fad9d8a648d0c8205e4d1e50fdb1e27fe1a84ac368e74673343dc810837e497ca365021102ba91c04f597a0370a12281aef3fe2bd2441cbbb2658d45d9d4c92e54bfa6b47dd2cc90bbbe39a8296757f2dcbd4067befefea19f4865e03db9df14c6275ddd308bd1d0dc15dca3e730b960bf999579e1b32a78f410ea2300958aaf645c2b01c5ce16007dad3077e4dff380ac18be99870dce69823539d3db71450a43f9499d7f785e00625883a341130e26145dc93bfb3893c5808a26ee80375e10200e59668a33e5d002751933e480e28f8ebfb7be53f6c8acee969ae5650f72061f4d94b971f34960cc90208e6f32c13087ccbdeba4009129aa5e905c39670e0eaa0c6d017d259a80787f4ec4e1fe57b22b4ccc0bfec76d9503c34d8ba2b4035c1e08500ae791b650c9d1092d9f1c684d0f9ac2c983452f126c914cc22877dfe45a8939c1151d85e064ba1545cc43a505fc7827f7f00d5d1f82b9275a7db1001f58edb7388a6c52d06a78f552a1dd94ea4787799680192181455f0f3944da8dadad981ccf1961d4b059aa1bf649567ad5775b37c006d8da9b6cf8526ea5ea2e49023eb9d969fc1780b038013d5d8026457db42887cdb61935c3f107ce8a540af79262e1d352c5815068cd2a9bc0951289f0ffe385bf8e58e1b1d55bd6f78db99715a4424d3d070f20b2239f101aaa92fafcb21f8defbc9186201e7e21d00f59fffed5d8a9b8dc57f01b39ca293414b88c0feeca0689298492fe873cba28222c19cd9ce9c5f3e47c8074ae114a39b66025fb6b9026b3b9f4b41190b6acb4d2a69286cb14493238da9050adecc68d53d758d71a848cefad3ae2366c2bf411317d9e3fa622acdefcde80cc9eebb772808d2449c868528705a652737e328e283d46144fbb43b54ab072c0a8bc7f5797d50fc437dbad509566fc3145c76f5e8f0414e1e49d71331740f93087c7d21a889b988675b02568875db242f4ff8e0e995c6411a04a66241a4c9d606f27effb3135e75211098d687b9c1cfeeaddba83dcb2f4dde30d4fd687e55850ffcecb18f0f57f3e0e9ecb6eca377436464147634654308083eb11df9952dd30275ac56dfc947cb600f68ec597be85b3f3e188a70b3d7d003e10e15506c7b50060fbfbbc2119f33f23a1ef44426a55a6d06bb3fcae15d4633e0c341a792d6570c34e2d253fac83abb301baadf6be69b68bccea05ad6de14d7eeb3f9c0dca35e0d4e838e32943ffff41184e681d6f186fa55b11eccab0f4339b753eb8e6373a903aa53b216c3d917b18ebb5b7215acab7d49bba30706294b1c7367c7fbcf20650b253e13264f93559315a5946762a9c196863cb569f82d6bd9b4a5cccb2ef5ea0141ab50a8d846d8151a57ab7c85a10157ba67e231196743ec5e45d4f2707e040b5a49962a7499fa84d9b9911f23b910da8ed5256fcf41202fec7bd43b490a1f047e32b616a1fc11f32a430057e9e7cae8eb0f36b87cd80682b7825597f4f318092735736023af010e133a694568b81f86568c0ec38df2aa25e609d25d6603060ad9f2847e466df751ca09598006cf6f3afe13e8e649dd763d71dfb9e1af8e45057f289b8d45becbf72855b987dfa0dfa17a3e970a5315dd668c73a0196936ae0deed0736eb2e82496cf13753753ae26bd168ccea358c20055c6cb79a8754d060181dcd0e281acd1a4d58da9c4101cc4c97bf970a30523b5a0e22a227137454d09c7c7919e11e0664d9d3812c261e94e714451c4149945735b96b34f7a1b2f130ecc585c00e1e502ca2c9ca67fccd888076e39fbd76d39534c082040bf4ade2f07408f56d4c48c16897f48aacba02ab4d0d897c066ba82dee7e5a3044a66123304438b5dd378e1b84c1296ae55ec572b4b28cdcb5b01e6a383b3029e840894080a02698dffd820eeabd4a2850595c97e1bfca91103a29cccf49e1e7b16a59d41088fad20049917de77d5daf1482d68333f1b60d927a2be4ed5f34fb0873ff67c0ceb1a5dc95181f6cd9effd25b360a3c569ecbaadf745f85e23892ff2e43c6af04da642a414ac16a294409f4a67a32835b00979e20e03346f10928e890a4b70e07c9c71a864a1ec56fe6d38a6b3be4699f4a3517e554f6a1f2b514fe877816fe092e6a9117cb4c9c540370247fe903f45adf96bcfe7d9bbf03f96d72bd46c47103d22ef28721edd417350c8bd3f1da376c063927d60657497438b666e00a5bba0a63fde5b1f0862460342883ad24cee078ebeaecf094850b96c40e5e3f2a009d005eb8c1a50d9334a07bf321b4b49ecab0b3f8da6014a7df3ec104d2d8bd281b0b9ccaac8f2c8b8fa23e3036f72844a1f40016cdd11955e40a486af7e086dc3e05e3304abd93a5c681a1383c740c7acc6ccf5cdc03c98ab0bc4eb137a8399d650f9e8b482fe4d6c024d9a273c0706e24ce51a44cc8a51b1d6981c9cd9e5b00f40860b27ccfc5723bfa46d6f415a4e5fd69bacdf34f9180cb7888cc008edb2c2803b1162ec879b597f2a387fc55fde030a3608d653508d8041af7c1ff15c1c4110ab967b34a7b67801bc08a436de951db704f6c394c5d7474a48aed89150c8e1901a63fd3bf06ec6b2cfc60a80ebf35a150dd90a6db41b33b8f875dc960539f590f91fc07aff2c9585fdaa835babbceb4afd0bada42d3512551c4fc6565b85c2908762d664010a1e36346b8f4256af828d8f71080b7002f3d2b024339aecd44030456b45e583c238c33eeaf5e74b551fe6e8243cd75dd6c1bd4967724bded90ce0a7e794f4d9db2372add9ba16fed73438c104548bfd0d7e28958c88abf58100005b2285327d4d6aab8ec4148ff8402099c0a3aa8c17c7e2330b4bd8e55825c32089cc1dbe6b768e649044e474695f8857c38defe305344db5d587c31c1e2c1ed0ed3f187498b26cacab6536c1056050455631d61d6b382be25c3cd71e30ad1350e96edb3040382a01c737fa0647a4d908157fb8555c8c4172fa5bff7452b8d94056e70522e93620b40c19512ce48371c31b07939d53fb39f0f3851cd65a14129057bf7ae14bee69345c9096197fa5b46079805a5dabc306be8a29f9fdd6f14bc0e2fdca03d1ff33cab56f057fa447c2d7c6951d4415dc42057248e600cd99a5705870971ca7038087159530997c7368e6a99c3acb4dc6f8a2116838c9b40a919030d3ad0a8151db4f5848563327226a20132259f7c8ac1d8316e38326dc26803064fb3b4d0a249dae7c461c876b08af068cc33e15cc1a4e3c91866984ad2ffff05803fdead78a617ce3ccb218c9125b637fdfba6776983d408e4d531cf520f010a537cc264a550de30ee268780af4aa3fd23dc77dd59bd080c7c6a66c0b17e400642e3de440f28bc34bf2db122e373813b9a556da761f489156dace72abb468809b4e8069b91bf981c7055bad833e542e04a80951271915bc1230654df0d8e250e9abf683246120df03c6bbb8403ba33c7b5fcad086c18a66bbb00e62c5780c80c50728eacd1f8763f76260ec1d0e37c0875cdf9097980e16aea2bc3205312570cabe6f64e0bde605e1557dd8b7f247d797763ecaac1ca240ccb8b23f912376c08af2e721ba86ac3b00d2b3f839bd5bece6b4d389996737ba92b0d71990d13980e4b3a4452bcf3fd4134c59e6f2b38dcffbda0e395702ff6f2ce459323f84a670ae683936631a989f3ddce3d5433a30a5aa8420d96d4d21ac6f95faf5575eeac05 false +check_ring_signature 9bbef740dc34e6dfbd84c136e42045809a5410b56d08b8cd0bee3c01bb7ff8fa 06b252006d4ac806de2d37edb31d82d06d77ccd487cca65d7ec468ff2f9bc06a 102 ed328876616ab30c7ff44ac55bc76ed187f0abc8de31c92bc6649b3530f77ce5 97d74a2e1211c1bf1c7b048737eb20f296e70f89661bfbec3b00d5aa1506f2bc 4b6fabd6920555492a898f09f23d9256c0f86674f647360ccae4d56252775131 45d977b0a44ac934c2c888dce36d4ef256abb3f96b051f04d69977198f084293 0694ae689ceb3d74ad8cd17b889f3bc0671de913ea70e9623240ee0edda2d846 24eaba1482ce15ad9627472c4e5d968fa5ae107ddeffbb598857dc4e30b3e046 25c2ced43a4a467e1852b042c7d4cd5d5bf1851cbba0838cbf32f7a681af6e9f 072f6940f7bf1d66a68fdb23974b466146c150a68b5c748b2eb1ec0745ef3c08 d32d62290a4bd9e4ea7769d842d06a1c6f4c99f104b45a7286f75eb751b7076d b223a7e09d4ae545977c69f5b45ccf3c8185ece3c1f1a20c444d6b7da7afbf4d d744a43516ff89dbb54f4118bf51fdc1a8e43aae5e7d188f3cd1ffd74c435528 1210751c8219ab4c8163263d6e0989edb39fca9b15f4f393ecced1f22292b395 b82ba6aa56a5b16711a353db6eb109544c5e16900371e953c62ff4a62fdd8ce8 270224b67be8faa4eaf8289d36823d2ea157d9ad0042ed5d6d6bff9838ec010c 8db2370ca5b7924fe5e1b4807d3e9a3ecc82faf7988ddb50645c3389395873d8 5d67782963e34da97c06867f37e51631e0bedfbbe16aeb4fe519806d44c031c2 0814b6641d5dd73e5718b1f46529212b89a2224acf250f47930b054231ce1465 21e186b2561bd5daa1e5a44a9024a376ddffe6ae8cb465973c742d19e1026a92 757370cf2cbd8a04b5edc91b58adcc8fea6af1e200dbba0f1a4b6b14e96d19fe 52e94622800fe764bfa5a4b71332d3184120b8161ca01828e042f76a63b3fc77 003b7a771b8ba598e72856a959d7160af8a4239d71a81bc291ce029b1a7a2a33 0894b20890a4968632f88088986283bb82c052383b334583085139dbf8dc0de4 474fa968576b7c9434ea2fa502d996f2ea7bd87bbaf7f82fdeee933984a1de6a cf64c0d546187159cfef91135de00fc0059a4d38ab3bc0a866b9dfb1adfa5479 245f0c308520632a3812f272fecf27313c7b1851ca5bde635c2fadc999db09d4 2169306dbf1e1f23cd980907d0256f9fdd78d475a1e9f3951ac15fa0e612fe71 0e0a65f065f1f7d891f35d93ab0099f15af413999fc3b068fdbfb8a918bbd5a8 68e9d65dcc66a6cde3b9c232687874ad18aeeda2c08d61f5f6d4ed5db1e8ee37 acb49dcba199846ad4d1fcdb128acbbcb04724778d027c5ae3989a1ab763635e c093eddb86b7152bcff9247788b5942afcbce24bc1c6c6c533bf72b01e20f883 777b4cffee0edb9ad5f423bbe20ef36469367453afdb688add53b980c0c67753 d865c846ef06a2db83edf9b3589251786e0cc8594025a944976f4bc14b5befe2 afb2e0887e250ba62abf51fc37805491c04ece87d06397841b0fe4ff26ccdb7e 7bb87a7894116e58011c2b241edf4c291fdd00bfd687d2e9c555f6c8468ba459 74f6e52e7be453b69d5506ba9b74912ce16c7f0254831f32a89bf5feced48bb7 add55d12dc742012848d3ee24e33013deead2c8d1d4f9bd627f43122109b1452 b8005b6524af9d52b3e54d640a6e094b6d371c1b739c2a2a657aa1c90f07abe7 6a0cd42f59e3887bf8e799970cea82a6d120a43a3e2310b50acfefeeebaf16a4 b1b27492602fd0e19a66142a2c6094a35dcb82966c69985b478d87404f9794ad f2e32d5b780d5b0a587b0989e2fec30dca1a0b74b94d806ead1638fe066f6e70 76ee4b36790eb027073d653b259a4ee9bc8754b622fc5837fbe4bb2d3b6a2648 b92bd12bb683fb88a7deda3551e7e13850c2f95b1783ce98c122c5da35daaa96 83a2371c92efc47955ee4c68acf151b48ba142fd2ce2fd84de17ea8be9c4f3b1 463f20f97a2f3e9ec3eb55cbf844dc6e28b8d60be12d62f91f55aa190a131504 f0e43d1a6b5b378a9f4b1cd48dfccee90e50fcbe5bce8f6ae82c7a0ea856bec6 e81a343a741e8744ca100ccb108609dd3735378a77ce779fc66a5c4ef8475bcc 5c3f3049015ab02837e7f4b9be48a4d4e45c440ee759cc721f8f2655300accfa 36c402e09b12d076313e6d22c9d65f975ec2010e9f3ad32346efa561a17eb014 fe6bb30edbdebaa2c29679ce751041d1ba4d6fa44b72668fd46fed6454f3d33f 3db775010397439580e8237b5717608dbddf79aab5e35c811ac0e9dbe221bc86 e665f5aa63464795e2dd0c73c8884537454e4866fe51346fe2ef8d5e4a2b4fde 5a80a524a2acbabbee40d55fd353b7cd461c6caa3305c1bda5ea11bc3cbcebdc 295f6ecaea31cfa799ba79249facea544fda7a58f116891a40834066fb6e41ce 8a8a0a300237098763d73a90fe6b868abbbb25222b5d1fab4d08a7b33526158b c72be1225977db324435dcc7f6eb6f6674c2657ca867b585d9ed83cebeb2907b 7331fa16c787f08b72053e261aab7f427d6c219565a3fad365a23473e01dffea e49758de6d4e50ea228f63e34024bf0fdda6dcb4d8982cbaf2b924dcbacc5ad4 cf8dc2a483cc3d392aecb370569c22963d2fc56ed5392c2d89b26288182236a5 422d321e77873446ad95c3c985baeb5eb7ab57ed217d0f0113086c0780441326 5446cd8aab9eb181dc551b100f9a732c1d2389ef940d5393beb40e8c92eb91f8 f17815f64a3125bf460f24357c4806bf3c1306a2d6ab1ccb1227794c2932b34e 7f4d595f368bd8183958a32977c1aaddbd06cd790fffcc1fe187cfee1cc4de3e 44537a5d4ac204f0b2c18f157618bc4fe2bf2170f6bc8f25a421ecbe9d0f999b ae2009b991828fa137a0945265d6ec700b3465c5e310b2f1e5d0c147b75f830b 6fb6e80bee03bd19dd29136ae085bcc3239dbbe257e28a954947969b388667ee a3741c80c8bf4186dea6abdc33fb0311fd13982e67c16fca021fbd3ddf7559f8 06201c6b745f09d0e0c609735501d2342060a3398f693d93363cc979df8d1f57 b6f55cd86ac06bf7149b1c04ae9d694b62a0f147d91ab9e74c489205e4f64a36 5c873bd85dcd34a0b636f971368c6c51b2182ea97d5fad651deeb8458f0f5b88 23d1fd0c771b9e7376e3789ac56b620f4b20f8a7e4d1ecd6037c84049f9e613d c131d4dde27ae4f0b65587efe1909706f12fcd623268ba1ba45ab5bd78d4da1d 69961121d996cf91bd93f013e4a45cdd7b2de1c0c0e35b2113d45463fe5563c2 e61758ff4cf596371723931a45a0e70b275a6a1402242442bcedc8dd011659ce f8250ca8681be2828ff4d9f67c17682d96de5cc8b465fd328a29db34ab1b01b2 163da0706ddf4d39826f619e65c5ef496727d1edd8d136744808ba9189ede4ec cfe2d5b500945a234df06375cfcd8e0be547a23846e94c5c6ee52cacef29e1df c271d28d8c87bcfdeb58868c9a76dc3344116ca799a19db325b23974fbed2180 02c1836e80331d6af903c93c6ebb50629bbf6a29623c09220d149f4ecb90eb61 638ff66d9507727a65a3be862e58785c8ea6eca84d1b1a2e005cd59e6a522d82 b07dad69bd2c4756fb339370d0424377efd3831ec2418a9b77f28f04e8074bbc 697079d4e07c605b6d8dd55110cd1ad8f904da09a7dfbc5213b80420b536be5a dc90915bea65d89457cea2f0f4b87ac04e15ed1fc76cb7ba32a174c24910afd6 e7cd341cab7f97048c70d3dd65967cb2cd2b0f5c6f835ddbb175637c7961088f 2aea1f9111353f7627ca3a3a816d106e68da8224f68de6aeed10661cad82d1af 50500b72827fc41a5d8ec3a26ecbb899f326c9f4ac8becdd8c382e0e3391170c cdbb58dd2c252da60d0ddf0f812660a67b4d938c41c8471194c25dce43676e98 0d8d370f732b17ce0b0736aca0a64a81b65a85628a962abb73e1e9a40c817c3f 8aaa8fa633a9729c5e9fb8685ef04e6375b97101c2e8ced016d0b7f4f64b25cc 3cef6e1d3b10da84af561d59c66fb0867958cc899c925a0b1737f8d1d8162ebf 29a5b1801a820ead7f7e184eb43b4e5c568b2d26fbb7eb748d402afee82992d2 ae6427eedae6c15d2ed0c334f1a3ede079a6a70dd4a2c9bf36629c7528699125 59707f1f2308815ea137e8c06d2308445a8c71ebb2d97b7b36787791c5c26d41 20600d42779d80ec8214ff839df8c4a18793989e6a33ae29f5bf4bdba6377d5c 3f4dde7d5fd7ed81e9e60cfca2adb58a99f0399d1cb392ed9e75cb4eb38f4fc0 edf3259d29efad601241c8583fd66b2a53f7c814648e5c11a21509f468d31e1f 30c0c986f0b46b03d8fe9aa7d7439a3e397308428062ed2dfd54306614f41139 d706ebc75fe86d54b7bc9a8dc86fe788b5bccc88582aefd173e6e3f5c4c13ece d7e8e9db1b21f48e49a9f6cc44b3541b89789cbe9c155734d0d798b1d50de413 9ccd8694e5a8519baa014750c8570fb43b5edfc7361a16aabbd56db2b87da948 d60529eb1d093c0b51fc30954248e905e6d87063a370351680b845ff148a420a 614e194b03da6c807084627370db99ba40cfdd39ab9880f303952891ef3b039f eee6b31861e3f1cc8ae0596fb5d64b61ffd31ab69fa753b4a157f46e17db9c22 32cf75631143500e0340914bc96dd0b8d89433de423fa9a56560d6f16fcf8007e931bbf4bb528d0aabb406ddabcbcb714bb0e6f87e23fecadc7a8b4f08af3c0085a9f190cc43d448912ffedda84573b3cdd9b2e62c46964e5decf838f6fea002536b6a41bb84440c8b44452291a5286096d42398c5a1ce2cfc84b97971019902535c46f9cfd95bba1e55c7c739d6489ab30044d5294e656ac8deef3dfd760d0a50511743877e6e93c815ac25f8aac3c519b8ed17011c0393c01fad0818904005e97bbf290bc6c96a9df8e4d6166d5aa5639d7c231a30a23d942b37a592bd54053c74cb391fd367c8b72e46a061192f03a10fff06494f5038d028c8f54572d500447815125e2852c21a8427aae40223c548a7f107c41d5211a4fe75e62df9dd0fca6e1ba4950403c7acfeefa09b7e31db073927c11940fabf2257065e517a330d210477ef8642962f9284ad0bff22fce65c7fc278dd7884decb5b69c32d09e30104bbf53d590c8cbd5fdfce6afa9b7b742717ddfc8e7ec5c1fd851b2ac42f160fae43b2dd809305cac356fb6d669a1b7e4f31cb590765d16efee29abb307d1a0ab5c453f49a1c819b9b2b4733a11a7c69830cf471a7415eedfdf49d3a3b0da2075d2cfbeec74325e7c1674221f564622e806a061d2859a976a1fa33ac3a0d1e085ec35cd73e1ee0364011eb84297ba9c40d9c5e230e8a15537b946075071c0da2eca44e6d3400c27f142e0c93f04cc71bbeb7ce4147ec4a9fb84ea39ef47bdf0f14d49773ea41bccc7d708fbe3dc9240609a0e81cd30e7caca27a7524202cd300c1dc70fc9ed73b545a4d4b5dc67a2d7433d9b4f4d2b41965d24f969fbf10f00954a083569f0a3308a998d052c28e425e6f5b849f00d8f4d203fc35198b9cba0f46ad3a1ccb88dd0fd5ee1f7abb8b1536f5d7c7fc4cf4a57d21bda5a3fdd06d06ca823d8bdf19a43a4a157524a2500c5736a8a158e4341168dba50dfcafbcd2072f427de6234cb824ae0e0088114fbb6646f8251703cd74b5672fdd618eb9d60b7184f088ee77a3ff34748fc708d5c25141d4fd823e9410c2f064c8469addc9029923174b15f017b5080787de69f370607fbe246313e7553b551dbce2cc884109cca45057ca823ddc28123d0d32f3700f9e248948ec7801315a702e6a4d0d6500c54765fe01338a1786fba76433078e54e1c394ddb9ab97f3ef9d2000a5fc370c5c5d48ecda41fe84bbdfc5ad6e0c959e2cb4b626cede1790716fa0ffb963fa04cab4af60c078b093fb4586597b42c53a14ae3196f21ce60f4c413c2608dcb101df3b08176014bd8cc1dece52b4bbfedb03f36e5f9fb7b2ec90f2058b2128e3053edf9c764df425decd3184c8bcae7bbaa7eeff75ca5d64063db2a1897e25cb01a23f02d3d5f864216504af9cdba60de2820726e6c5c2f311074e0866eab2f402ae1fac1fffeabab6018477ef8847e09bcba1d6863e598cc9c3780fc9a37cda001488518cf0186a4055d3952550a55a17156b1c01af5b6348e457721a0e4584002c5b94e4b558ec699689f95509c11b056a8eed38d601a7284fa26463fe8c9a0b71a1b00100baf75be90e5a4d5dbe5c41132a1bd0368907d06f8ddc885244130c8a987fc7d7898e7a70598b223d66fd5050383679824826dd2bb044cf8deb9200fbaf0a53004bf0d811c3b62fba2e506f53b34ca8293860545d27c1cb77eee1047c077be840206a6b47b603d67e1bd39b327a85177796b20f7064eda0c7e82e0b955e4bc7cf5cccc24f63bdaacea9b825f5b9d3272c390eb0b5118b0f53daa10de3dca8c74dcd49d28db8bb54afb9ca1e5ef9692aa3a5570a8c587e5a50fb8203a340de4ecb26e5e913d0a3e4cee2c4ffefe5a52a5e40098b8bc331c325c0c605de6a6b3f0d460d2e06f4292e4ed0c02919491a239303785b0f23627a02840a059fe0006019310ae4feac83083dcc327a86762f87c42ef4d1acb88c52a32821051797fc7165d1696eb21e9768fe09fd38ff72755165679b3ae1e0bb814a6e11039f8968978b0ac191a5cde35e9613f54adcbf16ea1c027cfc5ae4036f23cc0a02df2e400a9038113576753758898d02c06fde2fec9302400397d8ca6dbdc72401421cf3c7a6d4e7fffb57827b8740c1e458db27fdf51ff506724655299df2f802dd01f3cfa6af36fd5e20799d293f22a0ad6aa978c477cc38a1cf172bcbd4940fa53b86bf51aa509637b7caa9a3b15978ec26ee701b5615e0f71615ea3e83a40bb2a273db4326f486783eccd552f23ed9cc0dabd1e5d8a059bacc9d4eb8cf9a0538389315a2823c99873305aaba62ea58b49bd8c90d53ef9bb7847e70170abb066d7d0adf3afb5fa1a643b318a6d8b5363df746b81acb9602e156777badabbe007023424a8eaae7f338fa0d2b5205d7957564548f64dfb2a53512f0e3293c850b2ec5d0a4e0eebd0bd0e4692cbb1c88e9c3c16ea011ec37378ce2f10da7de8b0d7cee4daa537fe75f99ccce71c50fe67f298dc54c098f3cf32df9334e5476bf018d8337897ec1a3832e4bcd4f6b9ff9c550661694967d5a4aefcaff37777db80dfde016eb3df62ae334e6378e20771d11feb890ab36e35ee03ab8c86a738e0e0fec42c89b291478651266e56076331cbd5e17f483c85204ae9d6716b7e6295b0027717f274307a59b8ec5432fd68b7846ccfe92978aa3c52e52c520b44d43dc038585fcdc7ad95e2397edf55000999a5bfd681ad71cbb720f4b0ffd673f12ca075ca8a79916b946ae613b5d367de9b0ad1c81582f7900e65b46bf0b19066b6f025fd8b1e9dcbacf38614b5eba4b3d60360c3cadbbec8b24c3e91df6163fbf7b0440b326b54889c373bab5ed6cfe26ac2dde6f1e3098e2f2202ca79a569c08ae013b08b0c85953faf3ab9d65359670621c74ff575f0afd2e51e944d696b20da10d795fc211477bf270697c477ce3bd6ec4ebe70945dd02213fe285fd680903190bada9a82ba1501be7a1431261765d0a7cf4a6c134f2c2dac52fc5b330b040bf004d79940342a467368f3dcc4dd512c00f50d89811f7fc8bdbe2dd63871e87830dfd365a8a8b99eb1be8f98efc6b6b8239ded209fbad3634a0cf6538ac2370c80e5764d1d6a0946dbaabe05f650307d46fdf1d1e7b1bf439f21cfd72797ff40b04697cabecaa438323a844adf381e806f3752c19d08ec6c62149a6d0221025e90c5aa60af1570e21b07f6fa7ae52f6c13fb0a250e4c61bb5c69b1efb3557117709cc7b785ea05e9b431a2c5ea7b0941aa7abb4a36f6b4682e02c1884e148d92408f349a9806020dd03dc407be71771eef99df36a849c7dafa14da2c07d8cf4ab0742857c135ac0503f52a5ef8c34fbd0f57131e5c3669bd1b6628e567f28316b0f35180c9d156b5d79db07a8030f5f5408ddc3c80f5c8adb220c0a4798f5d8b60b437e920f9a4254b7d3567aac75d0ee000838525a0dc4364f39ef2a061ffdaa0d09794935cf290b16fbf86666575f869f87739269bde2aed6a2a55fbc7d479a0afaaba6e16dfdd45f63df9a45157a6c06d0079a102e6db30bd1ee0a120b9aab0fff8397ad221e28e75594c0fcb3c61487eef57e3e402fc5d45ec1138ea437aa03633d3877740f5b799732d6776c0aea7facd698407efc3b8a4cade0031fe58102255c4f961340772e8e58c59fa5cc252b4847a8e9840d3b9d518a01580f80f70fe0239fbf261aea46ca863d4f9c33a500e8c3a5aaaa6b6eaeb4fe5d565248320f82ae3e0d282e35ea2ec8e00da130e39afcfc4866bf42a2c48ced54ef1cfa550b0f011792dd6e7ba49d98c293f2facdd533c2e93955c3219b56a1d6a2cf81580f2984344ab6df2d1171d09375999bbd43f93f4c689aafefe0d1888c8f46bead0d664a584736cb903e2912eba71bddec293ca6901f03f4f003ef1d0e8c2da4ca095bbf1756cab144d0027a109acae3450c6745e15ec9c65c5d6699660d24bf280f80f63730e708dbd9bbba05700511fab65453a8e78c59ee412d7e8565bd3d440f32bf2800b0b28aae27a044f78d1b2b1044246e5c5e57e6110bab2d88a3821007aebc392e7ba0f7a5b9f15f449d8a915977808d9efd93661a685e893a7e34f90d2b009bf1a0040f17b6e38f9ae804e277fedd1cc083d7f9ca984b13f048a94103a2ff99d4ca8822a8bd1a97950ca7d6e06fa263d9a51e12ac8a75f888326ee304e8bfa30562be6c6f5bad2be1e9e553227f4e76c07ffda8fc08ab554b388a62089898cbe806bbbdc82c0a6493645133f1913ec75c110600c3c344d95406a350630acc68363a8af7d263b7cc5af16f68663e3c892ed66869f71b1e84c617e3c908ecea564395933818abaae316efb6edd774cf5229339b4f368786b0b7bd2a810fd52f006080a33e78e2469c029c912b4c310780cde1ac77a35235c5cb9ac189020766e7bebc9907934fb02ab7b410451a2ee47dadb33c647eb7ce22edbb596900653ea1898fd7359e6a51456daaa7a97a5084d4135eaf74de8d85d0ce97ca7d091b39480c4c67fc903660b86d6661d0b3f962aaf5690d539904b3598ebd255604d522c6006a0928feea6ec1319cf1adf203c549c25abc85922a6104f96c616601c85752fb463dec99671f5ffd35cb343f94066822a7686bb9edb39a2c620cf30286461d3ac45d5501569c9de7346bc3fd4101cad75ab269e72c3d6ad22d53870654cf773e19d4fefdccb185457ba48f63173c625830ff0bb09d2a32a65a14d7028d0f00e09ffe6234d34d726e0d22a1022fecd2bfcb68dfef2c7321cebebfba074a6bbc9af6f473753cc005c42c32ecab834659c1de93ec84ab03c60a8858c1067279b987d7cee6020b5c9ff4b69c2667c04376d71d0ce49a6aab8ffd03bdfd0aec84f55f49df04dd0ff79fc3086ab9a5e755632778a4d838e6b7cb47473ac350db17484a2dafa52664c2272e3046f08d17a88700eb388edabbdfec2b32cefa06e714adec249da3a9fe81420561f0d3dea71ab23447a98ff2d95a06667c47b7070c6cd62fb0509472f3ee32af66b1694afa9a76e22a6e18d401179ef738368608afe70d8b0641202717fb21201db07b23aa919b3d7fb056755445e02de9c6a30813513c6320cf9d7e30ae727bccab8b031f75d11dbb8fb42e1ddeea422ede310b287e0a41575ebff969f3497dc2e991b8b3a0a3bb812cf934df5df11fc123a70d7f181dca4d5ca24cfd0a98ea431c61ed942e9749e94b1a95a957eb5f4cecbc095f8c7f60a26efce99bb750ad2b7372235d4872d7f3fb8ef99362e28760c7370882b1af8fac12b34e62d08697d407346d9d64c45d484dcaf665a0a77c6a3f3d047fe33d9f40518640e3dbb51a3b50984b082bd54012d36f92d79cbb0bdac15e05df6e84aa94e6eb000b7f144f9a96fc2092f7902ba03eeacf5e863d233a285a0661997f4cf865d46b4cce82d3d21cd32d1fe179156067f4309dd2b6ff1c994a05c1b91b775d64b2249054c7c9397620b49ec0f6aba2b184bb210429fb5d253309e08ad99b1edcecab2de36c4f863af7621e1dc46accce41d2d15e46d00cc1340fa3b4a212afe97116f4d664ecc725781e5343e35390a6a7dfb0561f816bda3002aee0028c532f20a090e395a3ef17de2ad8a2e16d6f44458e9a787fbf7b328c0dcb9c34f2f131a5d87f17bc03c28096b4f63644830090434a62bce3925500b90991d34e4d11ae298d353e965b9f28fa9607636968ac9ef6f13bc5c9b24b4fe0039b74dcfd07b802a417a3c662afd74db50193c8f23161ab5e03ee72c6c451ff0f38f016993ebbdb9175cd9d811c22c088a67b07a075a015c84a763965d638a6039149ba870480581a9da74bd6685d2013cfb7ce1ca3e15bdbd6ae26dfe399b0042b3c473c152f05e6f1f28bb593ec40dd4fe4dcdb0e280b268f10b5527c8e0802cd808e8f48096f68740a7ef76f072081e7bb95991042586d17c0797397fc4b0a951faa1ef95a7e9179ba0f2f8d046f39259f8be3d7c8b98e6fdfeebc1debd409f061387f1c914433ec69209d8848e663270e42304682b82a2fc867c0dcbbf80b0eb7c77418edcea3c66ae0a770bfd9eac718087b28aebe42e7b8e4834294d40719d499164d061f23bce5b218cec0193aa54ac5c1d76cb9fab0b5da9beb5e190543d7d63fc96adf3b009bd663139275b3f51388622c347538bbb35afb428f6302e31d81190cb422f85eb2b4ea297b89f7f9d1713e13393b51d14ec36f130cc2043d66ca1e36605d157e397b2c345401aa86a87fbf662cbceee9a5fd7eddb4130932fa88429a02b91361598119da8742f43a9e7879eb457d2537e9af56e2ff8f0ee15f113b39223f5478ba46a85b88ed9fc8904d746d76c86e906f892f2ac14802d209fb12949e6989f567fa4a8a958d9f04c24b46518df7025c0e21243bf1130c0b426061d3170ae322f1085d07a57658c6feb0f1edeea8e6a903e5cc9e5f4209276215dbfda93aea8296539efb14ab3013b2dda81d2f00ceae482d4113c5f90aa08b44d8756d17fe3efcd4d2b3d8e037308a0ed8915d9329119e1a8663449205ffd367ffd95f098148cbca420f63582cbd9e75a295287b8899d06e5c4472df0b3497f12717dcb42c31b25779a8614f242ca2a6f1c3895bb2308a854e273596052eb6fd494a7cc10b364871097b5016291d1907fb3a48cb862640d218e41b140570e29ca9dfe9b50e6469b4c673ed95c182508846262a08cb870df7b0176da1058e774f1b5592f2fa942f655c29b7df50f009ec44fe197b8eef0e3a3d3d53f6033aa8c7564779e1a1a23f6026bb5cc94d68668e7fc42f63e2b86c8c139428ed0451443de267a7df5f7b92ac3128268219f325429ad47dc73f6bc1c44325ebf80d196993ebda73916846e0f87b2d7a35216f6e00e54e5393f22c5d719af3f1ab00d62d7faa71bbc51eb920fa605f6a669dac0de88e0529aa752780236ab39be300d165f7119357a5b5781eb0073bf8c9819629d83684b802737aa7fe02e0c9d8098c77f847345e0d00b56ba0edd748114f523ad32f200e63160fa1a8988f13980ec4c0c26e685fff6c7a2c486fc93f45efd90ea016c9ed7502124a22e434b12d0d1123ec638f4d3191647ed50288fab3fd88fa6ace73035cf42132df1f49ba620b99cbf610034d83f1924527e162a58d60f055bcc3436d11b7ff45208c120189016d06ecb3daece8f2ea3adece68ba3553661682235de2c0da91f2fa4faa99020469e9e63c1facccad4c55cab6867b4500db3265e6354e90ba69bf43890c2ca00b33edaa47a38a226eb793294ad683fab252472f5e9af46aac36e1a31e8b88e00117573ce84860a0616512761a94d06e332c7bd3fa062c26527c3678ab5a8958015940a00b3edf70ee86814a37be1ad9271f15107623805fc5a31b74f476647b014fc84b2ae51ea956352109763073cf3877b3b40afe2944c8d59495cf3fcf9b076f8fc86dc9c35e283a5e41725fbca43a06eb66742f02dfd8b6d34908346e9c07e6a9e95580c5f5202f08074da9535d9c239f420de562ef6447ad31f047cd090c888e36121d10272de5d3b7f1ccebdc329b33aac93c243ce7cd336d66fe2f1109c9740a8189a5ed11a950ba326ba2edb410321bf117232a6345f4aaf934de6503fbe52d610091b192a0610af4d545a81fb77d3f0eb5a9e911119fed19c05c00017936cd1a337b0341946f65ea89d14767d1d9d04995e0339195e75d70dba87d03051dee61ce94e85767be9f5e592b46985f0da40ae9ca5c2147ee456d324ef80ffcbc419f01705050b5649620e0a0e4a2691974f52b9de0900244809335c6a50135859b8872a451d881768ef90383f5b70841c693516e68dd6db148c0ae69bd07c3bed9bc202c0b8805ab067bd282652329d9c18dbdd857c1a3ff3b75a8bafa03d83e036f87e4f292686dcbdf73ae849485d7866e40821d3f0c5ed505ca197c0eb6ec9161886a563b987cfabf506c8a2f3ad8fec86ac8ec359485fbc48dd81308950aa8481a599c163518473b483a4a38677a07f402f7d4c0fd3d9185e3e3d6008226a88df19851c64a74e0015c052a88c1e6d631242acb30441b3ee589f33509f883ec2e737d5609b5a53cbe1da4ae87b9190566e07b6a85b9bd49f20b62350ace868c0b7bc08a793c762da82b7ccc25ecd8c250dd1b523d6b3e912db1b51c0c900a4fae43d04518eaaaa0bc1c8274cc0142075426e5227e6fca0629b2927c02a1353d255375c45a27cc46d2239806d75766a089057222639bd55cc32ffe2508aabd2ebe16d967e49ac616d4a41a2c08a6f4d0bb07eb5fc2493aa1dd2cf38c094925fd4347142a9adbf814cf8148c982916096c9da25ff05efbe95dd6c57220fd450115b4c3da1a079e226f2f31541add15509bd7487de9d17478815c5b68a0a39b13c3330d62337b88b2812bf5605d1c0ef2f46b94887bb0bb826b87033690d38d6cbc880d9acf4e961e467e272a48115ea8d080521201a110511f7447de50d9e3a18a8b778f613fa950f3dc73831245300da119265ad48797c08e1d0d2bf07621bc06a48de42af823798b74aee79ec6f7b8456d9029ea2e2afac985522b90bd9ff3cb342504120cc75e9d082197f841822228249ac0fe52a8395244eb27001d2d684910f26d9cde2a4a79a2ae19b731fe1248952fa3efc545c5c6fcabf410318f3c454dcc020485a1392fe453e288ef0cd2f9357a9fa1b1cfb7316ce77a8005c26724d22561fefbc521d292a2a01c2d33853b550b0bf4639ab889f6bd49f04f2069e7368b93a9c6e13bfe37b8ff732690f595d4fcc3e90a00376083643740a70eb767530bdea956af04817379f2ba65627eb2f660aeade5c45f19d589c480913d1405e9f78e82ca8699dc6be2d76828d84b2a29fffdda217c18e44e3b9890ac1c3d81e7edd0058393bdd8fc8e23319fb506d8971ed774c52af2c07e56f3a04b0eb04ab3ddf2d20465a930dbd2899c7214add93054ef3d4e61f9f3467e8bf03115b41fb9ec25d1f62d49532e1bdb209981d75570b73f369e77ddd8eca9e8e08f88c22c2f9c2b6014c8658999741e37e1c5de4acdc99eb60cefcc12fc8ccce0bd2c208ed7e7e74fec71759e853d4df408319439f19468c8c73fc340bd572be05371b6b444981d9896cf840ba64e241be3ea6ad996326d19d1c07748356bb510894229e5b1d0be310b19515bcbcdc5b181aa85af1a9501bda20aa98d6fce80e0b false +check_ring_signature 36fc459ed40fd7d6f788457b035ae6341e6a6dbf9b666954b846735c0046479c 26b81bbfac832ddc7900ab19a30e5800c7be1bb149956c7e8940df658e29a3dd 9 951181882755447b38ead74944b9295369fe169814e7b1d854800ac1d1ccf4ee f5bd1b276d89575111d3b46127c705b0eb070461d8774f30bbcf1701b1a75ea6 6a6daf8b7669f98c75dbdbad4ad00819ce25bd0e4c0abc0eba6e200ba72d093e b206bddccb5086c6b8a475b06360ac5c0603c42e91f52259ca7050d34756abfa 5f914d590ad97ccb1f26f970107f4425e303eb1f3123676c0968ca72c7b05a38 f8ded95e722798616330fda9a31ee145222bdf84d9043db02fea4dc0ed24f1f5 21011540221d2529608ef8df5a5872549f8a0f372b3f24bff73e9e0bb932704a 21b32a3c521ace715cac2461ed2721737258f03f6c2081337cddca279f3b02c3 e06236e33fb6c678e797ebd243901095bb06bfcbb5e5ad2ca25e161ca37cbf04 e60137b183c2fa5a87dd88626fc304cc50d461fb84ea323dde1d8f2eaf384c09c44dc947bbb85f9b1ce459d1bfe550522db3b79b1a751e8ab493251f58c2fc0d6602733e944e3e934b7ea148aa626b7932595ba4abae837150037061cac92607750a73dfd9409db67d7cdaefd7fd8b0030c089f84591b2320aa14603454c2f0c5dfeb584bdb0d8e663801517f45773030aa64939a8f3fbe2c20178c84cfef100bcf22d8fa4591d5b2b6bb86825e330a4e60dfe513bfb23a33f94313de2b7ff0dd7095b4c5f84811c7dcfea3f2e00c031ef12cadb52636d00b9ed79e45b019302ed5e74daee855f40af0c8ec7744b1c0d135a5923f3d293878f4fd35306437f07ca15247fac1c29580c71c920b660154a7eb4654f235354a87ca1578fbe6e3c0066be917730ad7c192b10d8b75b3095d28c18577a78815daae899eb500bebe30b32507c448cb5d5c6af7efbeb150449f369c68cbea2058206b34534edde15100841da26c10daedcc215c79b832215cbdd204fe95ec84bf4ffbc1821440532a80aaf0ae6ddc81ec399e8f3f3e8e384ac4eec4319b321956d80668d3e3841fab403721e4cf77885291d8b5581a5bf1d4e52f5b4c2618b0f3b523bb17102d913440bc534b6b65461745369db012f49d4e7279ef8261e52a769cee65e643251b9940296b79d1fa9a2b743448ecef772bdbe5ae64da569a0b374fc02ab3b41b250d50113ac729dd872503ccf1e82c70815abe455cfb288bcb4233156c5ed2f5a7c2723a92dfa5295f9601f590f42889c43bef1382a7f1ca70a7abf0110d3d076cbcd06 false +check_ring_signature bb3686e0bc18c860d051d250ed6b799f0c7b388058e28c6ffcf2c8fd93eab870 0c5a4d356ed42b1fc1f11d7b155cce4a1082275f78326ef41c47935ce0d5fbdc 1 0af5d27568a74a82cf7d6cf1b29b55c5801697388f7dc83dbad8e1d37ddcaf95 974b06e3ccc0b0c4ddd70f213f2d59da110e26b97c3c1a33da43e59aa00e1c079af13f2826c392c993402ec4136aefcbd650b0aff6955d98ee4dc6d5f927a104 false +check_ring_signature 28b2cff6b387538cb35f33b71695287e9094dfb7f9fa1be0c8317f18e99df14d 4a3d3e74d441ba5cdaf9c81cffc063d1cfd67af6819a3d5823f5c96ca670b3af 8 241e01d623ef120d9ee811aee1194d14d5cea314cd2e267e102681734f6f49c4 39090e27e33c6e23f422dfb7c8b2c11b1e0c8499a3b9cb51f453d568a5928ad1 30dfad08430fc815b77aac0e03e4a7e85e8c66df05ec52265dc5b17cd238be47 e835076df32263e9e688dc7c272e8b26c843198ac4083c407f6930987769b1d5 9d6fba6aaeef960e4b60d655efe6c60e168cdb36c701a6f2e7c78d65f917ea71 677ec20527379860fd58ebd0e80d2ccb078cc1f08be00b6b495f4ca4e78fdfef 8a28defb5ee1e2cc1ba27532f3c783b427056a206c2a77158c3240783ec54aa5 1df282607cbb0e1b43d8b786d4da4f15a8ff053f7c21723cc31b9d8313074370 0c747a16d7283635316802568bb2c0de20bdec316f07bb4eac7a71d4b6e2b30ac69fa04e49d5371a20b6a3d113f48e93c6ae0f4ebea069b9e944132530ff2609ab06333847d5168cd63b46ab3d639fa37ed9f700d99c10836b12a98488602a098d34796e30baec80581d572727db713496eb8420a3a4c7e8521f4a9140b4f00fb8e9e5c4184883599ccf80996bf72ea501c91b8f3b6074b04b3ddf79eb72a1083b377ec721b1578643306b55d009cd107952685dc4f58a70c4a9893db133f5049ed92d59c4854d2e5108d53c2371d98e3ce3d1a78a378146f187299b3492ab0cc9ee3dd80b74862c5e6e8dcc884f5138fb8f9e6c82ce7afe98e1f2ed90eeb2062c0187aa158e6271a1a7084e9c283dc355208ab5024833f1d3badf8b1db05600f56388d731441cc027f8e35efe5e499661549ec52ed9a3f55129e5b3533ae70cce70cb37df912e3da1711bf3e960367442ed377d3b4aa01842a41e8029bf600a4675d2fb2cb99b6d82f62c0dc75e4068acc551c65e23bad6a34311d025551708abe14c98dec000297f5a1da2b83edfc708c2cc3d3f41814222219ce156a68c00255dd719c9db8eb336d52203e8c1f3cef13b35d5f7fc92612dc87ba3d60dcc049d9969bf267ba07c7e36aa85b04bd84b995a8479b0bfd324bbb3a1a9bd93d50fca51b633460f97a9ac6cc023efe2cf1f229c036040eec75a3139b0dfca1f0b0b false +check_ring_signature 3e2be877c3b93579dfecba1fa599ee167c7173030b95639dc08326818744857c 8c3e32d7140c661493e85add17b2adc62510178485f74eeb63b7482e3c3cd832 26 cb88e39c97fb78d01ae7bd743d873be4f2e10fe5595dffbca09151f7da298a84 bd1103a9cb656a97e4abca9c096ea608dc8281293bad826a8ec79c72c3d68bd1 ccab1f839e1ab3cbc458c7bf2f4c17166e5c2cc8a610ef31439439f5bd140d17 adee7e8a11c3b7f093a363e3a93b0214e6f845d33ff2208dab3a35883952143a e47da758c58514c99ab9d0637a67b5c258da11b7267fb10436e1eda6ef3c1cf6 8dabab760d49e53d6095221f06e0b509f1d2d057d2e28fef23cbe7a0cfda2311 dc3aca362c7d8c21f662d7b0fdfc2ec0b5c594a5a04dd62f7bb23b3bfdd7f18c ff3a8be45882c7431cc0b78fa0b9640acbbd17380e088af0275e2844e0abcdb7 1ad0b32c8c93fc8c26a3a2f9e626363d84c342c82c4ec52a077787429b8fc65b ee9069eec5de4d9d95f82f3a66bed29d8934f31fbd18f1a2949146043ca0ca07 829cc5e1cd3dd8d3a826599524531e11b49b6a085bf9dde559aad0e1944af7b4 042ec6d192191fc9286667b4bd7a27a78ed316737d56e2546676456bd438eb1e 91ccae805da07145be2509d147e9cbe33576f94e53bf3fa1d5802d1d669aa4fe 76316eb99322b2125a39fbc1212302b5b4205c5eb6fb8b108930797c641fdd9d de00d65d75ab281eaba879db497a6b705fc59ad30c655e207c43c37defc816ac 19ea78b0801b337bf7acfd532c44987b25aaec822149d9ff6990def39a5ffe12 d4d2c1ec165fac8539652bcc2ce9e25a2ce48a58562cb137f438f8b52034ea1c e9d1d458af4339e4a97de75c9e214310bdaed7d9810489714e47301026773e32 cd3fe360d72a4f0c9b5933b2cdd18876fb3116fec4986dd5adfb965b2d26f418 a09d9517c2c030de2811dba1a178e69029de251153389b0a1ed0469781d81b74 96ce4ac8662045dc9a8e4c64196d00546f7f36673131e19ea4b62a1fdcd826e3 1ed547e86042abf327fea0ec963c7048e9877253c5943cc0e60e1a07825b105f 777780c5b5f40b58ca268174f1a15efff0ba05ad299b97185063dbd83279b309 a7cc3575a8099d364bdf8280e2ba3e85c9ae539625172c097fcecaff8284b42f ff9c00f7fc28adfbcacb6680a04fd3f4263a4e131b52e5e2441cb56c48af9985 2f21f52855a91da3879e4487fe2bf399f113c2e13be8536e7fccb9ab28ee0ac8 88346404ea193913c44137e260908fd01bb6b5dd8ed2bdd30de7473276ea630b91fc45d7e021bdcff8859937421f1f87f474fdcb9e77b87e3dd4e12a9f05ca07abba061a8d49b539d04adcf87dc94d465b4acae9685032f41318a081ee1d4902b2121a5ae9a24a9a5ab7c75c79fc114ccd5eec874558a069bcc47a179fa84901a9a750d40a33e751bd013d4bdc810c15955f2c506a4f63cce98e8f125bd325041f8c1409319f5e5a4ab72decd653599c11f00e59ba040ffa1a26e9d5a716c00c9b30675c1d3e5f3bfc744dcb6cbc40c417b0cf3838da5c27620cd3c565c083060b4481647edcee36337886f0632a0808cd94bbb52d1cc761367f42f6ea5fc61f596c476fcd154efd8b0ff6baf656b5996ed5d2ce43ba9bb0b043748fe3d16e08721766db267d55f8db7f8f0007c163646e1e85ecf1b7a0e0da32eb956af8c1084c3b821648f23f34cf9dfe836d5bda8ce6ef21a4ff4cc92dd7a41a18c8d5480d47ccc5337787b767c8890869d59bab46afc6a136302efdab7579b5508e234d043215d787abe368cc1847da036143469797f7170a717295a37e771f43d2eefb0cd0779ed32e14a3b9d555f1bc6c2d74c92da29bb71da2019b9e9cd5750b2b370c188413b4e7e3649cd751e1089fc76a1dc1580c556497393f79a14fca153d0f0bfafcd5ca409a76e34557a349f3f7a43be21486074983dd690f03d41ed9056a0acc04080b2f553fbfdbd52be1d516acdde5ee6b23c786ea767d761a817cdbef07a9430dce4359babed919345ec82808939b49057d6ba94812d93b8a2bcbb911067f4c0223dd7f31dfeb5fcab78ea8f2dd1ae3eefa092ae310ab19d7355a4ea003ddf8cf469682e951b608d8313885576bb6722018244949d9f748b40c39811b05fba4fa95c07702cd7490b68b164e633c2460a77304b4d29e8629d9cf47a70d01b8eaedbad72459d32419359dde8a53ed7f0c8519049ab3210bee4783d7e65d0a20eb5d5a1637cabc5acb2ca914eacee8d8df013dba9afd310c522733c25a42024dfddfbcae6bcf9eb6a346d5b1e09aab824b3cf90272b6cab432d50980e3cd06e3673aeb912f9f3849f043568d2d17dd7b54b1224cd2b0ee627b005086515f8ef8a17042b76bba99ef1e0a0d7db87625173dc43ad7b9d599acf5e39671e12c0988fc0360361a6d866b54e4fe14c52e8174841197d6fb355a25923b727b90850c8a8db3df4a1972918d92677ff24013f17a5d8ae1db5705306c0e1501231b0a02164e331a60a0aa46f8b811eb237cccc8bdbbacfd7492daf2c2f2730b0e04fe0174ba02907d689e40f69343bbe0da8ee6bd970c6d6f34cdaa79b4ec414f3d9b0dc95e78e3516fdee5c335872d6be98425531e6990153ce8dc5babefc4245eb2072b9f52e23408da67f49b9bed6b5fd3ae06fe5e1a4df725f50ce75271522df00a1d456f002751d823d6adc1adf2fb154efe53e4409a7a71a81e677de405bb6008c4591671fdf9c4195aa847ccae92f87143096ea81fd526146e0006e2c0714608bbd914460dc7457578b3121f10d88b9c5587b55f26be85871e018799b8d5a40ed805387ced6301aea43099bbd8e0b210134c5939a60cd572cc7e3bf694a4120fdf25d97f0769cb84a8acced6069fa6177e4bb9cf221f29d438988696730a4a05d99cea71461413b6c5416accbeecb9b8e4a70a78cc5f1a1ae19976246526210fbd69917e17d99834bcbea1855a7609ea538d3ef50a27b06b2465f5cb1d48560cdba0deb7b3b477edbd8403259eac905423dd4386f06af300a70c8ff4d949ce098f9c70c9fd9ff9d53163c1277d7bba3de8abd790aca3dca281dc14f44cf85a09d79c741170837bf62104065f697d02df1ac88a65fee8317fa30b298cb584db02e897650687462ad63803c853cad12df9a4a82739880d6c3db9a0a674aaf68c0f8defb961f5298ff41c379fec415a21d6cc0d2587421080f961c8fb28f460830de828e712f6eedeae08d56f0a83f88296156db0e59e6f7fd02d73249b0af8c20bf84b37fb253cb95ecfdee86d54b8ba078579c4b36f3ae6cbb390c9f480bf5707d594311e0d2e0dc287908641d4bbe47cefd5ec3d9f6a9a15778e80ab1f82e9023f4fc5924149280282ee38b18167e7e5c607887fd34ea930b1e8a886e403970ed072464dd4a85cb7c4526dafc4cee32d2585912dedf60a6b815ab0b1a14a3902225c7c5b50cf58f2e7624b57306421ac14caa3b18281763caf0d35213b575f017b6f3bb5079c5856a873c061a39ce8d6910165f4d4f1c28a1567f2b3a7ee51062fede9bf35241b3693d5ca177da03adeda63b3e234e485b1f495c7909b74810e false +check_ring_signature 31be567e75c50e512726285b89b19d7f0cc27614009a152886d41938c965d5e2 368272e854ffd2e4fd68b88bfc39c25d539e308f67be02ffc55a1e16f9d1c93a 6 40cd2381acc6dd4f5027c74c95b1f922024bfee47f28f6b89c54b395d5357171 244dad7e0fcb23dc41e3bada77d49c94f83b1a641e7350d3870ca8d5c4c0fc53 1a7a0b44758d42a5ac99f28db779fc8ada52b24a7f2f5925ab7a27a6a52bc262 8ff512bc8bb298d603025fb4f9df2addf26ab197e32530275631dffe2c77a6b1 bf92ee1abae3d4f3c8db1682f7704d370b6469847fe51e0025cffddb728af6b8 9a7a0a5065bc69cb88f8dce8990ec94f3e3406a74f8b4485bbc613cf355ae422 247c5cd47233228abaa6e235a6dff0fbbb41b60d713817cd0cf5baba6fe0e60ce3b358b6ca0bee9145767ba7878fcfc439777d30da80bd96320f1333e84ac0083fda38ab24ea6bbdd7ca4a092476ac1b97a9fc4a5312c9fdf2267c44b0fc9c080db8bc25d569253b39f3f27705dcd26cc31b397a762a318b145dd679f3163e0279a40d258a2bed5392aa13b57f5c0aae8a9ce9dd3a39ec66c2f57db57383e60e604b2b0c75db6a06fef3453c7505b0ee8d85ccd53bac458f933857bec1686e05be2b6d1f89ce4ba8d06fba74eb72d5d3a0db39f6c0c28ddb9d466c987503b70850cdee41f23e5c0470f11baef3ec1674426d8d5624d236369d2b576643eee901feea2cb9d4b647193d0d986e9503da149482731184aac6fbf570d5ffcf9a6902875b6f61b72ea4225e1877dda0916bf9b1629c62731a92608ed54cf9ad64170e8abfaf552869274e2e66a3e604fbd725fa4ab32bce31eb82cf2087015692dd0521cf52dcabbfa597f4b06d42ffe7dbcfa33f498a259471c2240d1eeca5353f05 false +check_ring_signature 9a8016e76457523abbb13e8d48fcca2681999a517f89985a2b3f3de57eb95898 e4a191806ac81bebf96b0f30a9c4ecfb176b96a40375ac1a66f6aa75d425b9a2 29 f7e4320d9bb7666b1d05a1e6d8bd7a136d39854e95f69e2edcad48ed40ef6d7c 072cf31c64cfa5cb9c81f7df91de65459cf9cd5b794393bff480b8dbe5958d17 4589689bbe75357bb9f7ffd4bd3ddd5466a44ae78da6d64f0ab5ff3c66060ea7 45ae4ac5187a8588520af11ddc5d0e6fe2313fd7356b8baf2f0634cbbefa14f1 a76add3d1175ce266d3d46f10628fc1c67ef66412f2124505e95dde393507d66 5a41fe51386f8d690471f385d7c2aa891f3f82ddb7fd5cf19538f4edbe7dfadb 74cde06f05dd174b1afbfd7af4014d49efce644a72df135ea57992587cb89183 4907569e2344b1fecc1cccb129c8604a339710f1d0d06e5e0911654d47e1bfd4 54c063a3e36673a360cf49c167ab547cb95226da1aaedec98c7854bc73b5361c b65f346cbff40bf24768f1a68a31f80ce558aee527626b61de4fc013b1b40187 cb648858e824d1cc7f91ceb2132c355ad25a5304302f091485fb81f8ab0c860b e0c4f209af0c9560d184f163303d0df7f255e48000ad287713fb63b1c79f5934 a89dcb591d9f01624e8efee43e7605747056f49999642594911e043abb5a805d 35c4d3cfc33f9504faa3e8ff991bd8c9af31c509f8fe2e4fe957bc4ff89accd7 c0046b430f070ea1d66743efc9adc225a2ef78ecf0d50d33f06b912de0f3fc91 481d856fa5ff33912af8648fb28dfbc206de8391c3cf11665305bbb2c5f1cb9f 8f82af02ebfd54f67c24bb0956750a760da69333f7717354f90a8f09a1e6017e e4fac3c1fb15885e2664310a437085727c23509eee46f0ff43cde2bbac58c8b6 025b3a19300220936c3fc836f7fee94996f323881c42a693fcd732ed1e7a7c62 97097ba2e3ee7140367c4da290ae41baadc6ea2b3040086047c4dbcea7514abc 837d91f99bc2db47ee58ef815cb24519bd1c4614aace5124041f2d98969cef6b 8f3327abdca76d7b227a627eb0d4d5eaa3861cc0b0b862c50c8aa06561d088b8 679b1cb49f790ef0efc163bd6cad5b63669e8acac823054a4a6f9f98f2728b24 632a22c246ba581724e382d8d71104c3ec2b4b5f13557248066b8fa9d8bcda4d ee54339a8a642ee4a2966bbce9f3f49a3fc31ea03e374c07c4109dbf16001ac9 6bf2e1d378b4c7d01962691f1c86d4b05b0c95bdfe474ea72f2d96316799acb8 df509b2185de0db028b5ff404809c25906fc8de75b2639e2ceffc6b69a6c35e4 d6cfcdf9443eaec91789bdaa1cb5ead222b1c7a8f91167667c4cc0e6165f82cf ae4384ac37946ade2db09fb7b9ce36ced306b3700f3309e0fb2811b3a7acd1d5 e7cd137e41b4ab98e9d6dfb9e56e8198052aab9a6152a2085a0b45b404f338083da0646b07f440e5230d13fa0e6ed874473c7f822020609cac397613969b4902f742fae63379bbf813c98e0520e3e9e0e10f40fe6e5c97659809edf061de050e56aacbf4a7c973fd6e7f787b78679e12d008e2132ece2cc488a32a0951544f0b2eeec8e1e8c53bc0d8b7a5e7748009b7f54d28f9d731ec0d60a52bf0216a8f0d3edf606e4ff55f1110a767cc87b1bddaf9ef6c3513e85228a275372b1916b20bc8a02a0c917752120c9d4cb7893b58a3fdf78b84f003fb32ad9ae42147f0e60e9f747cbf52f240316b57f5f73a2696733f8bb906c7ebf9e08f5477a99dfb200fe7d2ae3f85ad6c697d77b4815d536e11fa23cd612e693091e59070e8fa5f4a0e492bc4682bc32955eab6f18a88432db1536bd66ee8b2e7e45eb98ffb2475390de5b53c767e8e05df359ec7837a7b4574b550a5fc3b313baa8e6eb42175d0c503ad125db2f2e111a1533034c61849b1a7d6868dfc1050a1d1625088a6d839ef01dcb79678b1e0322e3f6cac0f139c607676d83347b76c9d0d1011db68d638f00d6464032999bd6fdf5eb70165f14528178b846b18a08849fbd74a2db6e7780901fbcb2251b032b0926a07c663a219d263cd596c679f929294049c7be7af8a9306a28913d3aacf763e69716527615b335f4381e7776cb57852839913022b142a0d0fc29a8ca1c4b7f66b89eba0dc05d5ade9eb1bd91a3ad01893a088faf3f63808e392cc6bec1b2791a486bb35f329ab401f5c6f9d8668f1f84dfaeee6c34c94066c584f23323f255b6959c5f56993d281f01e15faa039b5b0171edc920036a40b1f7c4001f302830e228ab7651588e16c764c3b6561ac50a86ff23795c5e9b002e833ae0e93daa300f8599261d9b0ba12cf7f93f324057900ada295a21785fd08e19e684b8b6dfac22fc5cd476a1b4c1d8156d4a341bbe540cb2cfea5fbc8dd0e41c346fc4f15c4601bd50f8134009ee6ee949b8256e3c26302a5b5c84fa32b02235d7e75cfe37efcd93187203797a3321c92ceae25fdec40b2a10c140a15250671bbd4ecb96de06f979a1fc5b8d80c190cdb8922d6288f898a6bcf53ee403c05149cf8d94bf4f93c17cf33f9141d448c1eb9d34f56098c8e40caf9bc7986c00c9e3054e9e6e83f5d31339cd27aecec874fd3a46c795c860efe0cc86f416dd800d85edf9ed0b2e4b0633c317823f3ef8d959e96aed37a14124c71cf6b3637ab064ed02a9782bceff174a91f0affbe9f43dc4c872f4019ec2cf61b546fd8ff660d54ed65024146505fe4b711033d78d5f4b39bbb15dcf7f918299555beea21fb09be384c973aa18f6640c7a6f658600eee647f75301f443b69a0018fba127e1505492214ee213cd74a3b5d5bc5d36697e53c833bf3c7dda0f6c428ef6d269d1d0373693cfc68d0dd30e6aa1fabede04f385c04dc8227886569467f53bde661f60f5927af8bc70b451d10726f0a33d645203c8f61505e0ebda0768eeb25cf986f059f796ced1d0cee791691713f99306b11ffc0fc157fbf83ecef09a1878f4ea10bcb9e270296161366f9fdd3524655df3d6b4cef8d35b76ab67c2cb23324b1490608684450448516e90a6a55cd75114ba82ca2db45c7f94bd6df5b3bb309958c0a0d4b490a87f4bf68a00c91c128e8a6769ee155ba961d7b645048734e17e1e2055d5fb1ff3b4a79b3a83f09cb321bb8e1eae031d5de74005021400b774333210c65f90a081f44fa15f5bb94407debbbf9614729765ba9ed49b107e391c0252003928f6c1d1482238d3ff5dc192e17ccceeac5df091372a56b5decbc84a387510914dfc940de801dc79b1a72b6e997be1d91374fee34ce8c77c1e0187347407d04bde7e0d6e30c45402cf9ff5107e63bb5c208f16e3776e02b43dbc62eba05c40aa0618ea091fd8eb1a89f36d83c356a7cb31fc09ac0944b9aa26abf8df119c6087d225c65d9c4f982a99d5e609038bbe21e5725db500ae92761b0814dd5ef450773dc93dbbc5945a9f77648d23e3677fec0b03a6a691519eb12a73aebba83ce09256b38076fbcc25e9cce9aedba3e303d96ee644ff087e4d2e757cdfc64419c06edf8ddeef7ce464a81d25083d9e73f5d7781df7def4f8c4cee98e1fcdf965803aafb50a74168986632f65cce0916abcc7bf07d79f64cff997854aa4916fd4900b5a532d0193d88465f900b610cb4436e0904862bfef6ce2b395fb89615413004959be96f666e7d86d6d6c6f5a07dcd371fa59e18078fb9dd66c58609f291f905dfc134f59af012f9a03242ea9d8aada451483f9a801d81febb6f5bd46f8cf00e32c46653eab80303914afbc71763b3b9de9bc0da99c4b53814dc462a030d8d0a8987ca83f31fea94280a40a2a5d4d415e22188f4f1adfe16bb4eb9178661d20789090939fba8b5a831ef88457a71a35eac3a75f4231344d03cf1e192329fe20c607afad35a0f68776e4bb5df63e1cffea0a396b1520b7e10240dbd8fd05a1b055f318807c04f63a1c0559bfaf81b51d5d55fe28214a29449a608b1f130f43b020f3b07ea674d01d96068a47c8f17086105e0a594f2a705ba4481daeba803bc0a false +check_ring_signature 1e09d0318be8c9c60d7d16fc8091602357f26396ff556a6ca360e49944c982bb 9d2bd933f73aa275709b9e1653a63f8f4f5a4876003c00af458c321ed31a7c51 11 ea090f62aa38312c13a434f93449a81d8f8c6c2d8173637286515366941c2b1c b5ca2e092ba14f5f64d25f9fa7aeadd23938d11bd6f2d3f0ec27413c193f7390 8a9e87b8fac5032216cf988b31f7a5bcf7c5b41e9dd09c5b097bf16d988767f0 d85c091df2c9383498b9b8d9222d9fc030462e523777904f04b0d009c62fb582 37c48518cd15f195952271452d55344eaf020893218176f2dcbb2b6c195a1d61 7b722338165f44857385580cdb32547cfa84f7f0c0f2d148c4f0bd65ec56c63f 8ed7f547e38b1cb0441087fdee95418aba34dbe7b1d8985d9f1a942a18428c0e d82398011db8e2c576a60d6b01621fcd72e80d55de520eb24f6741297e121e13 f00c51bcac99015e557b8df9ba39cf5a9b9e9aae103f515eeb1db8471b30ad96 7e15c9f2fd6da4849b1dccedfeba27fee81f68bdb7883ec6126ab79ef8cd4e2e 2b6497112ff895e34a0d5899524aceb56af68812ac1c7539a851cc442e772e72 7837e66488cce69db4651c11fbe9e3f026fa6218aea6fa769de5781abc89070473cb480815e787eb3bb35706337acb113b14e930c47b288461e32334fd4020041472aaa7632ad2a0a4fd45d19595abd4b824fd36495b43f107e42ebb31cc4b032acb6be11982d258d979c55ac64f103be49fa77244b7726c4f2d8ddda7bbba094bf1e9e5bfe98c4375f551af469d25c3aca8b6d8d18aa1c22d8f17f54f56530d58e896f54c13ade6778b3b0ccfb521493ca7b8503f09d1587f8d6ad919c8b60e3d7725a247a654b454c05ffcad5b3ffbbf0c8df27cdca7dfc678c6082eb6b10a1e308797df6a4c3112bfbcab08680e17f7f06ad3a21cc2904cbae375656b9a0c530fa4d49eae53241fd026bf99a12659563ea239d89179c22bd38e5f6b527203a465e7c12748523511cd07d3cd4b6bc4eda6b299a3c299c95dfe94655f576d0a72d5303ddf956be5b1b391897048d68a8c99e093ca54347323923ca2fbf9be02b9e9ee607a12efec888c765edc11786e049557003f703e74058e5d0a08f229056b700d90b6d34a2bd9f05b23dd79c55e39bce7555eabe295d8f2b12916dc790d93784ed6f412c967fc04b76e2a58aea31c307f3ba63b71e35d380da4b5f66f00bea749540902194f10b729db1072d85c2470fdfd25323e1edccb5ea43b62b003b25516abee19a1f1e138f9f396d7f95eef6dc48671902b592aa484f480b661061e390e019da651bf4daa1f58e87b6d039eef87b6cc3a930d1a3f53b45ed31804836aa044cc1eee15e83d475f2dfc0a43c18041379826487ec84163ca5158fe04aa85c93666e63bbb5106efb5be721b5b73cccaa92ab0c64e173d329910e4f9071e687893b5eb89463c065dd5513aa7e808e183a10571c7385314c1c60952580112f1b1b6ad12f04fb432d4370dfc42b9246564c86f5cc073ca1635c45b518f0d22b757ee06585c77844286128ab9c9170189458d4fcc333dc6d9a2a4bde83002 false +check_ring_signature 1fab248e8f82d2a34d66f937baeae8a36f6fc5da0dfc40040869459f275daf92 6a2da17f3d2c980bf074c9347f919082c2aae87411dc0bfa689dc32d50a7d09e 204 8b37890a5d84bc9dfd83489e8b2217c964c35d329a877421c372fc70a4fc4df1 362bcff1278ec9546d3566e7f1379b9aba05a36ac8e0dbd72d4b85dace1453db 2e57de255a1ae227bb83880fc37cf8c247f419672421ce2c1e354d3be88b18a2 28da128b8f0c0e3e59656a9c1dcbfd39d3e08479b6ed291108d95b0865796d5f 7dd0b6862cadd761e06d23475ac7b9f08dfdcabd03437937d1512ac767b79429 c33b6853f4c4112afa39e57bce2891801cca1117d95d98153637def2763f9b93 b1a9d3c633a2484766653ae1de4d3166fdd44a605bd133d2b7d76b86d9242a01 b0961cd6cd4187127f8baec78c865078db91b4c715cfae5711adcc4443b65320 031c49350c14211996023192a78f9dec095604854d23203053a34277a3eed4bd 6bb5a3fb051fd6f0514238374cec6282c08dbc6337b928c14373676f4c874df3 4b8cd76aee5c9b5316395f05392e98002eead3b85346ec2f2b33502e964c5017 57e6257913f704db5e041d3e0a1ac17ec5f8cbb84ffe34fe0917e1504e1edcb8 fa22c128149b312b4306a19ddc7a2e4053ee3dd46658f2b14cf1da39e24b7b20 e6265711560ee8abc332b3770ba36d509495633f4702cb11ac54953e94fc83c1 844f8f9ee0fff362a7f38444dc96ab994f88f2718f2c4d9d7818900839dcf3f4 7b2280dfa6d6f2e065cb2c7907c18b0c4425661f3934a1cc957494854d550b4f 831532cad0e89ff838450de6d7b77cc62298e8a9ad8875e73f4b572e7cb8c435 e65e2c7b0bf5b10eb76148b60f87b99d4315cc500fde08afb9d6a133dd6af936 fd5984bee9491dd741f913f0f33ab1a854b54a57a0c0a0a8f419d5cc81d2e607 22fcae142891a808215241e1a414faabb8fb2ae1c5e8c40b3a83227d2a85ed5e d086bccf20b5c9741b1ade7f50ed4d5b56880682b3c220a545209c4c314b9150 8824b74c23a67ac35eb3c09df9d7d859ac09cf29d45f21fa15a5b656dd04fa55 c3789154221bfb54f17d72c83835075bc0a1b9996b9a26c3152fe1b6ce02562b 1a709d2d1680030f31badc1e95b2af02f8632144e2e5012b628c7939dd1b3707 39e8147e258733d00123ae157364fb2275074f2fea271ccaeedd878e48b74dfe 3e2a80e40dae7ff9f7ec7969896f4863c7096229aea06423611250559a513aae 5ac8e7a9b3bd23f723b2ea79e350ac34d1d727364188bd1775ac860bf06753eb 42bedac8fa8dd3954318c0a59eb32671e194b9389a407199da1a226343402633 647fc08a0c71e4647bf60296820e4f40bacb3ec33223bff514885ee963c753f2 78ad9c16e9a6cda1d4f6c0b262f0e48f5379887917623ffb80df2bb17694e425 69cc23009826ffdc271fa6e821b9191b3b2fa404f6ab7d085313accdcc7246b3 c43c7287ce81abba753f0c0c47d53166d301c56479df48e03b833b3390df30ac 9daeac05848d6dffc62c8f0ce0d4b41d4b8ef84725f619e5f990293b46279921 df4643773f4bd42fa4cac6d770a442422418c42d94c6f416ce075494a1822b36 95d5db8e7fd6219dbddb85e2e6a05ccd1c219ca02c41e829768c37a2eb45191c 5177b7638023719336c9318ef3d8feefa7e6dce9ddaec768a47c5ed488f1bd63 e7d5d11012037035d6c5a956fe8bc44c56dfefdfd92c2df9671614122c224038 6e49212a2fb5d2931e7404b97f873f1e9da23540c16a21e6dfee0c093c7b960d f0a6987a61b2fc10600519ac96e0595231a6e9877e83cd17ed657fba6824b605 425fa8a9eac3809fd9cfd50ed760ecbd573f3f7760274600c19f959ddcb06ba6 8334383ba98e6a4b2eb3931a8ab9c24446d8e7b63d494067a7723fcfa176b93b a11cb8c4e67405eaa1951a98aa0791283c6995217d5d526627e19dfd8b93b883 c92073a934e0db3bdc5596afafd0e0d74a096eb6b8aaae30736023b61ac974a5 66a89c99755d1f2cf2cb2ef2ed371a647847ddfa3df8ff7116ecfd8b12a98a7b 15dab6f244cf10234a91d27b70264dd55a86550a24b26d807a5adf7eff3bacd4 9b40cb4b9fcd3b0d2a4005f50d853506b12e9371556bea98c046de84cda5c770 a06a7354764e8cf8802a900aaf37e403dbbdbf25e3d7c7cce6ad1beafebfa15f 18d9cc2390c420348e92ec9c6923c563ab6d45c84c32a5a2a8f29e09d3fc6066 021f5590c50753a38c5dd488239bcdd9954f3173addc69b664df36d34d76c854 0edd69a56c314ab0e4330f8ad45b0481197df14cbcb123cf6064587308eaae81 b8d6d7fd2305cb91e19b6116f2699b68a8c3a193932bf5cc4b698f369600e834 760d142c18245f319291a75b87fcf2d238184eb9510f57175d440f71f37e849a 3149aa74d75a08cace0ee7e85c33654ee0cc1ab05105ca90d9a97b2cd2721487 ad33a8aab8040913454b7ae264c727a38f3ffe90e553a8749212ce995278f108 524afb5bae81c0845bc6f38d5c515b0808d0259f1a5420a97209f9453e9b5999 855d7a62152d697b460817564125c3ad031b0e56ae74ca27ba487dee95321b2c 4b5a04d72d3578e187bb78f1138454b2a6f6a8b34316cc2d4d2932831dbb754f a66e5aea7486bb769a82c85516112c399532c5a85ce225d5b77cf461823012bd 3e2435e70e509a0d6c189def5378d27679fc2126a992730cc0a23f6f7d12da96 3c8cc6637d5beccd87119b37c1be7a61253c596ce50a25ae9c1c0f8f90916652 802f95aabfad0555c27b97fa31a3660602547a24940d1e6c28d7c2acd8e34b87 65536e49381d63729deec78cfffc40dfe4eade04bc851e2b29eba259ca91f0b7 91fcf9b2931036bb5251d06c664a91809d0c446c888027b5a55f16be1111fdb8 7fdace75b5505677937416ea71e28acf3706580593e55f0c37c2cd43e02b0456 7bc5874d597f3a23941ceebcc938787502255c16a04aee99bf3019e0947f420c 13627fa216648cd3cabebbc8bfe8fa61980edff1c77b44a25112b14d97a3708a 5300b5086f5dc9f65f91868d0420f965f7ea47550bbd9a97c34d6511bd80207c 9ad36e7f3111337889429dd1d411def902d87ac0219a2da8836faac17ab15cc4 38a554fd65adb3196a3e4472308e885179d0560e6c8bac25f316ecbde42ddd17 57e78691be1ca9a955b16725d828b8a9a7ddeb989e1c47aca40becae0e97621c 2372384c69eb5af768c9c29ba885676b101f0f5638062745c3c4cc2e589f2b1a ff7660dcf99c8e34b6b2cb9c7e72c18c5ca8c6a1c528eb18376f2278d1a1e810 5fa13a1ce4f35f2cd77d0cbedaffe63c865765f671934d896aa1f8489f1fe9f3 c47bd97e291d57b36b65f48d53d77314e4076d9cd96fb7a8bd7de04b61096b28 f5ac4d9c8e2b67fd832ad4d498e8a8aea4a40379213e8ab9268d6fd18f79cb8f 6b30d239fc5bda74adba71acc0eba9c6f9ec23b560c72dd3dc5b3252e9103476 0e8200a5c22efd02ca877b0f1fea919bdb72ded3626c618bee00fed258eea2cc b9b2f4da03292ad76eae0f6d345a5122ec491fd33f1202f4c19370dcc124e1f8 61814ba218aa806787c9440b3d55017deb0338cc7563c0ee46b3a53dbc73b080 2a1a08f407be3d97f21f79557af67eb2d658b6050ba59f3ea820f3443d8f6810 af6aea6288b99502a96f67ac148626f135158a1e930557790572b138db2d3540 93d4c7aaa905bf54c5d341c8484e660d5792092851d983475886484cc6ae4b2e e2c2827d148afcfbf2b94fda6f12b48c00e8500b6042116d84c3a24e3faefeee 222811be64978176a5933afd95e3e4fbf199c7f42d0a19f3bb22bbd607725caf 8c02f6ffd54a76d2f85f036cc6abcbbc911f9f4a050f27a2c0bd4d9df1a9373c 02bf0e281b5aec0f0038b067c871794884e31d283b4eb996ce1061e0bd24eead 17e7a3f554bc777b02c241d5163dfd1271016a13fc9c97c8c8f8d5904bc242c9 f6ef5828588187796460d16c76d5efce7ae3817cfc443069b31cae2a3cbddd65 58159ddda770f7971a302f938ab2253af28ab2f57c6cdb346f561310047b6970 63e63bb85f01f06637b671cefa6e5cf73a5f6b33acbe27549d30ed93d291dfd8 d81255e3b52e143a6d22a95082a4ee48cc2492475194d205b145e54dedcd85f5 4bfbde73e6589c8f1ccfd976de8defd62c97d8fbf156b531b2f3f84660be4b8d cd81b526af91f17484355a629f5eba43f3eb4c043fb52c85f310956823dc773d 15b8d837dd472c2f5244d302242b2105c2a5647bc89e68b90bd386faca6f842c bccc65e9df5f962201a52fb86dcdfb2807c55bc5ea2b774eaa532acdad1e408c 4baaa47a893d69e7506c9d35c0d3ebfb9bb0e5e79b4af3cc4d1f0fd47ecaa106 b1819942998c899389da3d7f4c91e129a4e4d3237f12b345b98e2bea49a14a5f 05daf6029cd6debed848249f734e87f605956778ae578c91cbcf65de44426744 c368f5ad0fd0ed4194948611c54cdea7fc4e6feca62bb3cd16b372f0a44d2146 2678ec701048dcec5036c46f0f89caf9d89b49f75c0ab53a100da544b9f69300 31b80cae69a1ee82cc7a1ae5919d9176658186866d28cefef54b3ad21db7622a 21ae1fa6a1a81c700b73d7c4cbb513cb6e7df852f40e11ace2c23d043e707b21 1af6c5a6529c63cc8011b50d9e8b58c73c1d9d1667ac9ce2c078eb4af731aaaf ddc8e2b1be9fbaabac73f97114c0ac45823e2961f016cffae772c360d681b025 133adf4d2680d3308b2c32769e46206c0365f450a3e65d3bf4755305981539dd 4337e6f3c207171a67b311b4ff730e2bfcf9235ee04971c77197dcdd083b3e47 394060f1dcd132c5d867342e85b76b2a337de359e32659faf2113260defa1811 a7093acf89847fbf613a5c2fd116af62df72dc24bfb4b07ab223ac827fac5cfc 19c8e6c50f3027188575d95a48bbe497aba946d09c4262d82ed663375a1e9884 56b11cb34b784a75ad676895544ddfe1972654c5475d6fe6751e57f39114af5c a20f92a9958a91c18649dfc365ecc9009afebf08a74b78f047f940d239ded32d a47bb9c4ad3fdd6c1dc8e1773b31cff8b339ad7ff9e102de4d02fb16851b5e91 d0682415b98d0b2ed18fbca63faacbf4d64ba73d82b5105488778ff4eddb1eac ee45b5ce327f583091b76a45203e0269259b1db98697d9334576223d1397133f 4925c8c47bbbb19a4612af5a6741b91f55b92660d4311a75e96275a30aceff8a 4645b9f9470340b3d583b5dff6ff5f542fc6940025c67d1d713a7a5a76508f94 9697a6561c8c249d7f8dbffafdbf70c135e7500fb719d64ff1470573f73280da 2d6105bd9f06818f009c17a66c70904df534e9ae22f0d69c66f7eff2fb79b999 2e264cbe55d9cb519b672b97c4c8d244063e446ff7687f8b6bfe066f0cc95c48 86f6e28ead95e56d3ba6555dba449c3038fceb67736fd156c8aed5e09d8a67c1 1eee04576472899b977de55fc8bfddec5169a34f3419f3f541b85cf029596711 e991dc2f3670d826e799277bfcb6946ff6e42417d498a4c982e9ed1741fbceca ac3162c598ae9a052a1e59814303c01882facf5d023de95d0888740635f46688 286767fff68b779f562267763fbefd3a992927eb8070f04f794358c84149e6c7 711a440bcf945af5d975364f7cb6ff134acbaeb9d50e5c5b3d6b9fb65af78652 b8ef332960be0f09ad56c715cc2bf87ca0beea9788c89806938c9f6edcad3367 d900d11b6bb3d284e290c887da34f07fdebc56bcf1629c9d99dc1f14efe94f82 00204889817df7f6c684d443b4dacaf38916c4c61747b8c1d5f70ec9fe6ffd90 193c2f7ddd84ac029f269d8c6699eebcf51269c4c0f06be9da2c40369dedef72 de23482d94888406d0999fa1803d00aea696b91db682533f9084d3472bfec7f4 66277d43facbcdc247ea973bcdf4a4169a2e3329201eee5a4db2a2e29cba95a3 45e8419047d1a68ae802555c37800165177d092ac1b4372eeeef8acf8ee07e05 e15692284b3c58cc1fd9252e36d27decf099d2f01ba0f476197ece7d72637a41 3a492c183e91debb684cc20b0d2196a159d874374896b82a97cd7d70fa15961d 24c097b1137439c189c18c36ee04db5a52012138e5f441ea9fc0f8b1fd02fbe9 688fb32ddd0761d3ad6447df541e4a49a3d653662a070ba5a5509231c861b1ba f3fe62b33b26286358bb929691ecf3d86717e9c43ed23f5b2be5f2d9bacf9bff 2924499344a8e87eb0dd6765c3e7c1b16a4808237f529037a747316b7c77e8c0 0c3d5f29b6dfffe0fdf891f40b2e6f28d6aba1a9dddcfce9cd76482bb405e9d1 97c6752c5d03a2caeacb83d7389a09673a6030c53a2a25813e7689339c749a76 0493219df1e5aad3eb6fbf3553d2e4f81bad437bf77823ae84144469a0de685b 2717fa9fb2ffa93f87dd7a4eb204a1648b85d56ade4959f53819b5afef3b5ab4 d824dfded945f0006b6897feeaf9f601f0dc64e64580e83cf356d4ab639f0ccb 9634742280a4715a31dd4a4d8b0498a3cb68e833decfaef80760e396bafa5a74 5cc66a245037f84d0cc1e64e895eadeb436f4a66244d4ac27cfc7da19e9e4058 fcbc8667fe2cfbd16a1b69ad523ed11e6a00e56cda9b0118a77a3c2828d3d610 f65b19c03ec9af05bee7ac63debdceff5d5cec15ec76d3a474949c8fb0de1430 908c8cdc7cc75c551329fdefd31a272e81104c3b231998ae383fe8bbbda1306b cea38cba30d9b3808732d5272d9ccd04ab2968d02ac877188795dc6273658dc0 468dd1240c872787916eb5e6598f4ee17bcba688a5d35b804fd560d112c1698c 3f9fd0b7453896811efa6395f2eaed0d472d482c468d051bca6db763e126d68f 46c28ee03b369a2495056cdf6e0d2a93749efe242c0dab538eb4527a217ae496 b9883dcfbd0f2f071f437bb421074f8a013eeed3a8017bd87bdf3a1f89fd9f11 0ea656bcefe0359da3e0c2ec5dcdc716ea55cfcc216c600f554f8daa8a2b2897 04e634c801bb4b3fd8fba7258af0def9679d157bf575ce00d28f90b42134fc66 3389563580939da36dd1de3b451bebc3c55b454c54587c63083c51e38d5ebb6e 03f05915d1a9c92bc0874b3bae621c7506bf4059e35423198ad90f86a801c843 2b9d28237689c2b23a2ca19716fc68c0a8f52678f1d8a2b06bc18fd66949cef8 02ed3a3fbcf81458e15a566bcb5d824bfd90cb78577a858ffe1ca44cf58bc696 f88b89c64a4a2a3386238ede1bc7141a173bff7ff010738d9602ac2cdff920cb 354c29b3b2c744b1c52b26c073d3397f7ed9756397ceecf0dea5af8901e5ed8d 15b653bc4fb698bbd02f103c6cc12337a1a6ea328b364f2b20e49556862772a8 52e80f6a0b96376356d096dc6318062dc95f6ea5650ac33fd4e33b1f0feb1336 eef48d34c6a0b5b1ea8a8f19964542f4c553803d047382468b32e9011e46337b cdb8819c1bef1c9fc8e958bcddc9d0f6ac3c4e3cf25b3df59673924337f4791e 219f49adc77158cf1cc979c15d462a44226fc9dd5454746227f091e32688c9eb 2b5ccf8779c457819688769d968e7f2608d1c14458b7e62a0db6ae142a1ea1e7 131bbad91767dfd97404520ccfc694667a6057516b82958037f49a9739e67130 13ec1d0eafb6c531c21baa4f698d915a7ac5cbb04804c17bf8da313e9081e2f4 786aad5f8d68e8d1c9c1a1827f42048bedd5d1dcaf762b9f38e79137e1d82051 c9dfb214d017fcc75d889c54aa4a0dd075334428757674c5ab0635bafbaa7b3e 2d2bf8d2db280c6984e512354d0d04b64fccb0bc5b57e4f3229ae699a37705d7 5e46b5badf358062579a939fab4b38d4ade2981615e40cc2ace5410d6cbd2fd7 1f47c70e2da9df612a03f18187e7196893d218690b3bcb80b1aee381821ee24f fd50a3357a823855a9fc1cda003060e772d1d1e41c766d80ce8446e10136c6de 2bedd8313631b9d71ad0b4e722ec04f57f37c14ccb490014bacb020cdf047b93 b22d9516b3f90b47b395d775da4f628fe9223878a697374457902366b3958550 c74bcf956d0d7be4a712d9897e83a26b9cf9de1d4fa7fb82d7f06da6bfefc6d2 826eb36fb28b5cd83c29f8b1afbe6ca15d216a0c841a5ae951f39923ddc7c6e9 478c0302165c55f8acc6af887a5884d31150f41d070d8f2e478eac0bb44d681a 853f572374f8109f40f686e690b11739d9a97a2187d7afb0bc40de473fd88116 80e1f6d502be793e7df7e513985b51b1e663bcc7a9f2addf301af733dc00cf1a e62ed21f7c0c83d42b8d7e4ba3e01f0b1f604f4df29d8a84ac66dcd0046f4e93 6fb2f7fda5ac3a4d2c7a8fed72fccbb733029e24ef67a27acab7c8dcc4a7c269 4981ed940330921a73c90836c0bba62730a475f7acc590848181809a39cf426c c99babd1963ca9dda91b17e48a34a911cf6f55bc618facdd00522442885362f5 a28c5640f3e63bb49eb24525211df9852208c55ecb8b24d44ad23bdc1502c4a3 f054307489e2703fc9f5d3894aa178b583a921f721f232e674c0ff843e619398 0e2582156024d9d5b345eca24d5fb0a804c960f1efebae632a8cdf95743a5938 cb45e258497552ecf072797a7dbf2afbe1d8946e36a6a69876e4c063f602cab2 28451bc502cdb832d7d33b17119f89475748e978d6196757ee256b8e0f445e84 e5b1d33f5a940a35587959a10044b78b2917c45f5e7236a87b8c15cd7df7e192 4547b5cd642edcb72490435f3575bb06eb66ce3abe380ef04debc6cfb4eb8b3e 5d650cbdd9b466b298b500bd31a9e773fe45c57c0d4ccf76b43edfb1287c4f88 f5a88b4ada0afb358728f4d73487b6215922a2738eba8ab45cdf99e778bdd375 170d3387a656dc6915f8d85eb20db740a2c1de30005609441464e78082d92c3a abaa63eaf1f01bc08c18599c80267c4f284bb4ce246d0edfe00442992e034801 c817564f573e2b1cc178ef50d74148f0a1727072b62b0d5c0e54a052f07d774a 05877a4b5143c39d4e0787b4cdb64e54abdf1d07d3c9f8c7138f74eae480e1bf 034e2e90f4c0b0b871c521db2f851700f095f99b04a437ae1e3ce77a3fd76ac5 a9cc7af5d489920fdcb580a6e941184706e2e97ede057164424046259083ee5c 971eae4855b79d94f54b30d7050ee8003f8168fbde4c5c620617fd3bf42a3db6 0d29f0b61f4d0f9262c9b0c26da24df2ad0eb79fdddc2ffb0f7269b73a2f0834 0d12af1ef8eaec5cfdd6a72156bc28cf3a01686e1eb0c47347b70d092660c20c 1136ebc27093d078db31c6a12a6042fb0af8d701e9600344ccf6772acaa8ed083200d22402e0ba0c95cee8e593f7efb91415b99e0b05eafab76119c6fb1dbc0cc4fd7b708ea7b560880709f03cd80eccd8248ff7266181b6e42d2a77ddddba02c87593d815f9c9e6e0a089302407d844e1264d7c33743109528ecbba2c82360bb9bdb2f815279cfd815ab8993bd0807bb5d03c6ab8e8ef3a3ba7364cdf2de80c22f90ae2b181ff71ef90fe67eea022e213e8e47794a5591d515643d0de98c70d7c2e31e36d0a03224214d3f17cb8420135e777f224bd9b649f55a5b5ef4fb50075dbbccd97af4345694fedd5ef0d814d6ea018edf75d2098f9d9aed83d6bb60d25d20ce056b589178f6a1234cea56de8771785fa20fd9c1670ee407d3203fd05c8b059d1ddd4d6c4a4ae53be4e61b80475b21a148f7434c965f506323d806f0523939f07be9a914f2d83ab41637553624fd15571697487ce68f82b7577824c0fd390746eec999eada1203f7da098179173b1c87d70710926749dffcf6d862e090bf2c2dffe9162a3ec63d683f23fa9622aa48d5948f53758b4c4968ccc2aa905182f853c04349eb524071a568a009eea7d15a996183651b58f2b91d83b7317086daba41b9434764d2ca36ccc73b0ab0b1354ad1908fe879cb2f275544ba503090b1ea61f837000e42ec7a8eb54cc84cdf087817fdc03e849091f24ca987a6e099e3af998649ffb8cf1a6baa98ae59aba8378dd3ec6958707128bd317a27d750e1feb21f9075389abfcdba355cfb338043223a6dc1e022891bc2e30bb6a866b0d4a56d4f28ba731f6258cf60e7bb5f822e8740a45fe4654b82a46101c1ea7310b0b17f5067b43ba67d46b8bc870c2c8c185c934ceb9435812d188c1b69d0afb07b597f7c3126e5f4a97f1ef009d1eb80ab7a0be106b210c0a7833bdc284b5600ce1bbdc47986efe8d080f12f24cf2ffd09bbd4cb717d200bdfc016e8dda47c8098adbbd739e53b9f7cebf18805c5a301ecd26848915b5db0eff2e16ccadab68058f2b4522ebf89cb83009c3889b0f61f0a5e2e9fe144f7037877b1cf77779d80d112d758f8ce355c9e8dfd3a665dc968ccd7819b08c9dde2072af41d667bf130126f0a580d5af35cfe013ab1d5e76c1860e5f3daedc34e450537d3c7115e9b8058cba12eb0064e32ce62d0e5b77e9982780636d259bb26cf4240fa42dc471a40daebae446ae54060ccf381599a395f7a43939306403d314ff2810cc6caf63cd035b4f942e1531149c62495a9ea41fb1260c6640fa3afb81a60111b044cb4bf904da61a171c60fab00068e4e344ad647cedc266bb3ffaef98fb721d638c16a660bdd2b684bbe3f5e76888b7952498f0c8262a7b49ad7b6f34a22b5faf8e819580cfd0b74db38a787084f36348c2c9e49e77cb470208119e0337917ee753b295a091c19c8c9484e8c5941508a3d25297240cbeec0aa77ce399cce66ecf579b0aa0966df5de6584d1e3f15d7341a60ecc2f75a134c7f6a537c5e3bd22cac3bebfa05ea199c106670c53daae5b8e574fca6de46f235e69321ece1d59e195fc23ef80f9a012af470de74994d80761002e651955c022c674cbfbf3b032a454364ff410a19506f353d3de5c35cd3bcf8686b79c8c00da25db7e248ba1d31c005c2615400b26a668e55f4b56208ee7c959665028f190dd8f05c8bcbe6214535656305970716faa94acdf677f8116fa21464db50e593e4073d6bdfdad07d9c13b401bb7b0b56ce724f8c3fc7a4032a0ba7e86c2eee7a3820ca5077ed5dae1973c1a0a2fd04e6e0e4395618d31f5dacb72b2b94e5d38d38d33f67aa61ddf592a4db7364e203719d8c57ec5871fb1adfcd9aa6e310d1788d8ad1f39eadb1c6b6eca7a102ce08849926ec8f302cf01beff04dd020cd7c78a6173100357f9a3d592d069d827505d5c6ce49d85959900cf1ddf0f76648e4082b1aaed9be870cc56a049c24191c005d69f5ba69e61ec3804132ce34daa9a00578fb3e73d3c6b1a15558750ad496073e3305f458d680707975f90b6e9d217e98a0e3d1ef2bffdc545f56d13c56b50d250987e106107e630bfac362fc35fdb5e45c25eb295353a36a5ef2acf930e806804d43b66af2694ab4d28e9af4d250dafe36a215f6975d9577b86064d3c5ac0cdc8a87a0adef0b149d753be1bc73301d03d75d283b07bb89672fc006be87460ad44a44c364a8383ba366f5287a312c01fda7e54535922334b75739fc2b4e0903dc1eb9058057c047e6536642c36ca11bb7b78e28de38aa429dab6e3ac825f30a08dc91699c6a1b34b5dfd965f855df02b5b3de7750ff4efc9a10db92d4ed5201b7688e1141b61780ba805faed325ce1a3079543a61b875c9c57a0a520652f3004d45c13e741064bbb018a55ae6b16147ef39ad43c1f99ba79155e0ee778f3c0aec8522dc2fd53afa62cfdac7ad37d7fedcb58752f5d334dd080ed389dacf5a0e64a6b3a9fd4dce30b26f92d7dd07c2c76799788eda20d70541465b566a9b1a05e44ce6ac3e5da430ade26f8dae3b87a8201aa31b99aa5b9306579e26b0735b0de5b69044c7c228912e4fda58063ce0eb4bed883d175162b39023c44029d2b20c5cb0601f69fe2861772295e4b79e2976aeae44bd18f1f7d0e705723954881a02c3875507470846fdf1eadb584fd459a56943cec050a1512de3e3066fa96a9608ecf5d1f4d52ac52fc6e5cd591f9554e06d4c01bc91a62c1cce85d7934273210e125ca51baff99397f72bae8e43a3a8b73910b9379b52bbf3cb1962533db1fc00b40918e6e014fb1af9c4a20a7bf62bb98db8b5aa71ebc59359e9dca070e5cb05872d82ac8b62eccb06dc737c6b081cd75ae3f88999b49bc3ceb7fd5e36ed090297b97a252eb723c99b769bce39f1503424aa2fc377cee0a2d77e451d9ca1e704ef31c93c403720513971dcfc5344f2ccf2b1ff26f6f4789af5d0f2687ef47105c5be0e72da9b81badfd298678ce4a6580be3187c4182a6a99c22432673743c0cf0b540ffccfa122fa55975dc598d1f5e7745a045711ee6178d2326c6a8e6fc059c59af203b7fae35bcc8f8d6835b16bc4964cc7bc70994ad84fd65d379541b0d395a00fc906293f8e503f82622564c098c8c8cdd1edc38ea7252f12df03bca0975eeaed2a15ffbf7408717d2832c157063e82abb56908b6b2fb485552f732a0c6091e98fcd221468ed7a55795b7ec64b491088bcabc3c04432f691e66445e202b3dd60306985e1aa11be33acdbee0c104c8b1d7b7b91c68ca98f0129d244a10412b90748eb5311c630903f3b57ed537bf8ab43147815f39cde79a45461c237085a9075a1236a338033abcd121e67264e368d97026f3e46f926b8ba45cf1b9a0c498c7435a72a094b01dfb6802d9808234a4bbb74d6710adbefffedffb5b6590ac65ce159511cfb27c3da26e214aed5453b2116e89da108ebc5a954bba1f3980e7bdb9257b7fae33142059f9a80b9119f5bf800b57878bb8bee81e1e38f72b90e974fde1bd51643e76c626a2412244eefc98bc958f5d31fd8f248dab4d7bda002a4f31efdc93061cd6f4e72713f6705aeba3ca1fac18f024929bd3b1908fb2a0dbb6e93c557ae56d21845c231570e2924318f1874bcaf6d37daa0031187ec44008b19b72abf28e3820bf081ab6cf898234f722691adbd1d741c35d685fa9e5f02708c8eab609fb89957710657956c2306bf63e24960bebe2a651d16e9870c170afb970a876e153918f7fdacecb2788caf1d8e6289b7147f8453956b7c28ae2f0f1ceddda72cee553bcb4e60b96b73b3127a68cf7ae0aa0431e0838858fbceab0aad6df48f264c28c1b5172675d486749c97d3be2d003d6df9554101b6c94d2e0d8e964d2f3b45e78fec4ade52344838fa780ca4564a47554b33327caaa26e1400ed24092b46871a9327a7e6f37bc226d0196cf7392ce0145818d27405ac6e9902467180e22e5f0d6b8ffba6e43b1f42a95542376e5613f1b792ca43741358300aa6617ea02f1c01dc185297fcf9da6cf9586659241fdb4ba93ceaf807fd9fb90b3f8bcf661c4c3010eaa37578375f7c3ca845e410ce8736b400cd2cd6a8bd0a07cc2a0888a38c026a25376cbe1d478d751aa0602fa003259356a1763f30c349034a0ed6d0c1e4f53da6ac6a3e04fa55f7e5c0e1657949354e10d382a15b7b910e5b68c83ca2278417409adb0d19b704c60bf8eea36df1d793c1c100822225c009dd962aba4704d6b9203667fd3f42c9905edb908edd160f2a98988f0691112d033f31eb182fcd279276ae4f0de3169f9cc964cf0ccf07ba8fdb221bd6e5f37400360e9dc40214a5b6dce8e2cab5eeb68f71e07629eaae3b8f3137ca2119d7ed0fddecaca6f4833075b3d5d7e74e1e9b655b519679c42a557301f71ea3ceb72203719897f786d4518dd96ce7ccbc9d0d64fed7c1d2f1f63834a9d1184a51b20a069ff3d8b5ab1ccdcd6991e697d8504310a7d561a2f46d251f3b65dc4f386f7005064bad0ffd0a1737f84dbebb8f782a604160c092352162a68c6402d69019c70022d8045960530c317df1ac87776cba7db61ccf409c6789eaead865516cd552054096a789839cc5dde78ddd9cd661f11ebf1f3cba217778bf27bee9aeb746660e17e88e0ebdabd62afb0ca09234f9f4826ae40cf1e75b2db50348a7ad9ad46003e7873b65e3607dd9f0a6b694e971c404348ec8974405fff38579874cb97a7106c6310bedf8cfd8eb02d5122ec810901fee53bd20dd8b34d0fde5caf27171a908737d7c48fb79fe6112747e177dacb2c8f5a873ada3fdd43384f008277f5e6a0ebc4d6d385693da0e092da8f5383bb56d2bc55c4f1ef8358c29d1e27e4be1ba0ea7e84da234fb1e5a4566270e7abe49dcf89a44008b4295ec115167b11c2cc00e589d898807c498584e8b3dc7ddf984d804ee2a1d9105c71b214d798c61fedb01f0a744725b0c5beabff30aa7a4e14f93c8f56604a9ee358d49175b866afb770229c58e1c07ccade62430e8f17633a3ec1811d7dbf710d50410c67e7ad5cedc0a333a011a120560b069a51a940c762bedb2b77c0438eddcd0734a0906369e2d061b72606409e36d58590257bb3a2815cd19e9f4045da4eb00c040b1e935f94609000dd3a82fd15c2755e4f89ef824454008a4fc084c3be0543a42170a098cde0f07371a77c49c4d1488ccc38bf910d1cff2a8134798590a73e4bbcf86ff91270b525770f77bf7d3a08b79e7d8db0828dbe5066244827a8a76d8d905bd3ce3a2009f9810d3a1387ec0a3d4ec42bc6bf4998ec5371a35ee03ae47a69aedafa86e08f7a92b653fd6df724e313ecbc46a3547cbe18c818257d55bd8a1fc2cc0b6a805a0df7fb7c438cd87195424f3eb39782e3ee6c9e060cde2cfe803b86488d9fa05efc397880f8e05c5ccfed6a4c1cfa5d28647e1343b8e601b0fabbee087ba95041dbf7bf25a445703adf6615ebcd7bf319dd4e831045a457d379b8300e6f6a20288a0456f8b53030db6eb0cf142c89fc448f0f0ebdf5eb873411687479b1b6a07d5a505dfe4388b8c305c6586528077c2bfc721382fbdae3d132de673b9a64c0fd061097950d00cafd57043d327c91d1e60e2ea4e8622286b5195ad1312227e0ba83b49347281903dcfd6cccb141cae741dfc834c6451c00990108a30eef19c0114e1c67dad2e187b9fce699f137ec778bf6ee4f436be5998926358887a336f0a02726e28cb4dd8221847646cebcd68937eacd94155d86ee261d2821713e569070825472c243483faabc9017f5c7696ff0fa95fa1ce6b627da48fe88916d35d0cd9aa567eefff046c8c622fc43ccc3ce5eb03ed841657350b7826035d8d65f805bdf6a9bdeab808d65d9d5a302facf002a50e03c5b45e8f664a92f8c2c9cd0b02c6b119d990927ce02e808ed574f95f6efae784b565eaec5ea11df0ebf44c7b0413278eba6bc0b9209a94f32f477a9924c228bf9b53b51c1caf6750344eae260a9fab029ab542c0a40247cd092981c8fc58ce518e61fda62204329d5d1c371407d9a09707d6e223969ab9ae3388d75e5203c52fdcdf6f0a539a46397e37e6e50b3805b0cecbfd6582ccad40e3e8ee7b0d4cc804429b44567f71ab599e473bbe06ea426be10500e74a719076c97b70afd5141fd602d3db3d90509fc757b6a55503f0c53907d3db45253d006c1f233d91f101806394c6d9f6474be1599bc2c53401826fc4f831085da0a17201cc092bf14137538573267872cf9b5ac626e54351085be8c182d1a356a66f98710126ac8aaf6d18a4e0fe411cc76387c3e89b65c30f96a5b693f5b7b02bb5927352129824de16c520d044aa1613435c996778818f09558945aa12077b711badef81506d38088ed18c2348550e46e6371e0410a6540422fb69f03318c8d44587057fe87412441b6c634ee043c178ea95ef62fa0c2b0cdabc46b9d51e4ffaf73f1127ab3585877c43a48cf7c8fc87b3bde25b6bc9120a65e8e4806a3be365e169b0414d6cc691eaa16650b8e0af83b9f6e7726d676904fa0867bddff39a106c4f2c43efdab8fd605ebdfecee951bd0ab0aa1247c99c01ac284a202d4859b475a27277b6c554fb935b23336ab727390e885407d7d0b6025cbf812a48e25653c7e490be4befe0c0d707e24129599f45114461fa1921a80a8f0237cfce49f7671b3a1e5d354204b1773836fca6e8fb7b2c5c9aa1cff928040b15312f6c020b758de66243c12fc8cf5a593b9039d2beab1e6a6eee326a3e02606620aa1058fcde13788064872838c1ff68f61a05badb5b9c497e7caed7d50b86291eb7c11512f7a4eafd59430338b36f6642437e6aa8d263d7c1c12eedab08dac823bbda935605357a27823162db08d0e7c76e362d687db95a60c62e00b8058adac34ee2d4a169a980904f0ad12d4cb051a1b5483b34df261ea4674ab4d3063b6590e20605a76300fb3d065c8fd48405938c44380e57b0e6aad28162f2a903c91175a7926c7f38f85167e6f4b537067f6af9b50043c06cd59026fee702690491eb3153790c1cf4cf3553c6e4b42cad3a4a254b58827bdbac3d720c372b2809dd6091f76911195c3a85d5baf4dc267323c6913ab7a8cfca3460c26075c48f07b4e9ba4346e85521209279d3c4eb658e17ae578c3cf4511c20ad2531a1e9350749d519b9a6513a05510a15df8c03adb8097330202375c6fca9eb5e80b40f1e0898e2a8712d63b4e73f2fa898325fb388e2df02e4eb612317f40d7dd2186d06018cfaddfc01167eb3f9a9926e6c7f845225726dfaaeca6ad05dc26ffe63068d0cb04a8b0459d6b2290a463f827b0f2e4cee50f8308c565a58f12dea10c2b24a071970268eb7977f9b4f34f59fc9f7e1fbf3ad6b7d97e9fdacf2b77da6151ef10a70463122f604054c1599ce64986aa9815600d5210f16799e5cd82c1ab74a7d0d03a7308afa4552d222db28705a9e12ce01534cbf79d903ce49d242f6ffb8b80ffa6ea4f4641d72061bef257f4ddc04af856a5ac3a1d13ba14114ac130d940606449e22298c054608dc93b079c0ec5b0ab648d6253c3dcd235317a98e80992e020f72002309d37a53fac6430a079e662be63e545728db12e24846659fe40f690142a8ea4417d8e9a0a666fe67bd7c347a4a313ee1edf389be16003fb5b1df2d039357f68b199e13586e756966271412cb9c12b0b8f7bbe5a4c4e803d9e1c55c061a4884095dc237e55a3e9b5cff6fa809c2a61ce058b5de63dbfe0f5dd659570f0f066c30153da9fefd40030483bd35e3dadc78530f64952edd197db285ce8504bbd014be6a7042f982e4b9f4359c63332bb968acf9047c08163b2efcffc65a0a7f0c0f2686c407e6ba162cbc0b2246d42443da9b11a2628baac8ac1dce57b70bbd2a342868274c7bab27341438106602362ba28efe1662f441692e32dc8700065c7d5fac736761a03631e5230ee9b9b340e855b585bf34cac0aa82912987b10972b827750d886f5fb4008399f1943e1e4b714e069e5edf81759b426dd0601d063bdb69bf40e3ec9d6ba48592462f96cb8673900be9957c2651b7241378f16603f48d0b3514dd98521f3aa12d4a1181502b57743887f0b17b973c711955b46c07abe82d36a45ba2fe0812f9080590150e83026f745d62922f73b0e8df7a25af0e5ba2799676310d670444558709b273c486e5177dee6abe897fdf4cbd37fc740f64c8a7716befab45cda181922a0a01ba1f209787bcb79928aca578eee9e063085e042680af1aa5bf2e2fe2fce9230865721993e6f78135b438390b64ed44de0760ca8fddc48c66d3aaba3d448fda64496b9844f2d87eb83576d8197011dbe80e46c621d4d58fd4c1abbbfcb45aa7ee56990ceb6251059ff7656d15bc4dc8560a3bd45f286602a2a62652ccf016b5cdba4eb274bac797ce8fea70ed9325940307d8bc5d7b0873bc966421e15043441ed8f423980107ffc5a1d658a822c033b5097e6f5045684a04aa97725ab214b511337d49d6b5032485a29a970dbecba80203b160802231c61bb6980f11e8af269b981f83f615a7bf1198ba9a92aeebf5630b2325ac43bb4acab58d26b40788a454c8492c312b2b8cd192fe47cfc376c69d0650b187de5cb8b0cd051ee8ef57f7d0d797cc6c8e167e2c9cc84a5084ef40970a5d4a1a9f9a972b562f9325bd342740f8ced2af74894df90c3475618e64d3870210955600013cf06626f0dbd0b109ea84908ea0c6a5afa317b894f068594bae08ac711fc1b945dc136b87f5810d91e46e4c3a81472dcf8d49ed869f892c68a9054e005ca5108e353d18f3e03e92e2b3d4ce86a84fdf0bb8908ca011cffffe5d05a1bddaec5a4e83f01d002c59dd4c0e487cf0b089a827381912b33d8e201e5200aa39659caab29f4eae51ebce94a45734ce87692e4fa01b5f8c2f7c390badfa0bad0d0eda1b4c93870d1ab46c1c8e60d5ff7cfb9b1a6638c6283e09dd13b01b015145d1a1f27eb17eab31bd961c651344609ff55ed917cc67f4a495ce80917500d498180d7b6a4261f0d824219b48e182fffb2f900349b26d117ddf30208d2c03080b2b1f0bd24ef266a06f133455f172110f0e69bd83320a56c2e48d35260a0f07be8ed258f3e94b5cb7de193e0afcb76d90649275b0aae417ac4f7df8ad9f074ccb4cd851164727b5685ba0b44953db714aee1e230d4a8222d0f39403e96c05c1d8691660d5e49757d7f617e2b0bcc0eb9870915c13a464e6486df779d42d091b866397f8db1d94d5561c4720a0ca2397865756bbb5ab223dd63845d9d5a70a39b467994c06287ce5233e8a417ae7ecf7ffcd187aefb517fd558bdb152ec00c3603aa1d02251ecc95b2034a36423138d4f44548fa7796695a212e3926f3210b2e8ebe1bddeac6e5d2f9423a2514548525ddda5f9c662337b893f95019e44b06681f071a20467d29a7d2f4fd665da74d4167235b05252e4b19a1ff8acecc0b0d50e3e6cc71d817d312eae6b93bdd755f2008991b4c78d0e01f448203f244df07a93b98680343c48c5f9210e5a191b7a9e3e7d64d202ef1f9370a8f9c3e28a10ea6cef02c754cbe772a7556f09246eb172ad0356e87f7bbd59c075a18daa1f9065602655878f175473a191372447071ea8852adae3be8a18c6a278d8637282c0186a83b2ad0ccc8e17c42a6b9c60f553a4e1020dacef45e2e2516c096d5517c0755cb8c67d237796e371a6a790ce1296d4a975c68cc478f2636d5010488b9780017a334c063f26501aabf37f0a733637b7304484ee277b4265b453cc51eb889054b69d5c33fbd0511a7956bfea6c3f2331bdeda52649b3f006923363941787d052888547909afa4e8027d3d0f1e99336c2706953194853e0c30cec6d12ed7f2093f476c0e621ef891ecaf1691745ac1f03aefb56a58ab34dfd20e59366c731600d6bf228abc717f78d5ce36fcc784e307613c43d2224742121282c8310bb67d0990bd36370fd03fefd309bd5545d23abf012535fce1856bd96cd006a370c69a0290b069065455098689a8d5341b9bb242750b7f591b62c24608c746be0d9023022d1d2bcb297f4f8bb109a322dc13455586714936a65210d4a64c43f729496701306c9c3b5fb2b9b8d7abef898c38a6e0bc37520e18a746d222b3f82f284b7e0afe60c3654b4465e79018c1dff0debbbb8e171e161f820d5deabf6b3b0f7a0c0ede30e2f7e12766dcf024e067d78084e7b48a2b120a6b636ef44d18dde64db705435b20ff368155025e311c4fa79ed75217529ba30351bd808c2926cb2624880d7f471972da52689b5c3df14dcbcf3c32b794c2fb708032d98b1bf9f82432f00c0ca4c81d868b1bacdef4165739a6a8c5539548924270d0b3d96d1fcab7a04d05c6356e9343e6798f65638d6eda8c32d87b7960947abb84061765399ac8e1460bf075575515103c484ea90a00006c3736d50b59f64b81eb9b6eecbc6ae8cbf70b07a25cc09f40b2ac1189b16baaa102620ff32deefa5df63d9a468775e03a21034c825d69c200f8a0e0b87d459f06b29916d0bcb19722ab99bace4ed3abeff90e01cdd7fbc3d72930398bc9adec8aeb73e631d2318f1244f5168070196e16d00245d2042f67b60375bbec93c0dfccdd00b90e1927cf8dfe6ea707ad1d675c9e096ea4d916e991410ef7cb11f5304f42cab818170c2b8550ff52e429b0626ecc06ea7e9e3f8cb17312978ef65eb435dd6f582646c7d7e89633e7da75c1d05f470987d6ce71ebc9561cb0edf33b8f784194816e0d5ac7c62b5ab29f0f4d2c4eda03b31ebb74a8bbf8f01c98ebc04350dd18b19d0ae505e53c75c8809bb116efd207c9b3ff6d82f5c08681e51271341984dda7ec54bea0ed154e6aff8303d6ef430c2ebbacd8746116e67830a9247768589754d907b1fcda0c9e486834f411f31d0d54e802daca718c1fabf44d14cde596f01ba5a915614bdc5c80dcc21583739e0a73996471d5bb4e57a0d10037268c064aca86465471628528637881b38d19250ce43d4b374a91ee089c5ad33290a04559030b7cfdd381f2634374f05f717b8405f83daf7f6188076e9b4af913e2adb77171c780da68c1956e11c393529c229a093af4a7934919f72f61165f9079748970083ed51b853acf016471d0ba907fb005e4ed9001557ef166eae37c9aadac231a00423a2ed6dd4fa9a34f0e15587a1903c98361ad37f50586909cc0e524135b7355b1a3ce97a08ffaf84a6c902be4e30c68b81967a652805e09ad90f8fe55f377cb43ff0c64fb51678a21cdba419dc40449df6a61dc8dc574021e48bdfd65c3eee282f0ceae493fcbc67decd946783908d90c0b62dce7ec61c8da072bb55f6f4cc2c4eb7ff460ddd2684c7df33383620aa37bf46ef14cbc660373d2696c05e7bce9f6712930aa3e6c7fc3ea4bfe07b30c98a23583a70422958d5095e9b57350547a58359207a28f2427bfcdb303ed0c044a4e36c18335b35217ac431057c0dcc56c4ca812d3e80eb0d7407df409275a090a2b6c92b085a3ccd9c6d08caa85ff832e83a5b786646c44a44df6c82f1a430d934dbc4b119711734cb297897e2cb7a0f06d5fd61ffd827de29a503bd7162205df14f2c8ea746b25ff0070139becb2603ccf19eb3577fe88f7c0dc419b16540a45b7c1ff0aa987e38d2b727f38f3576721a7d23b5bab5bee3343dc6aa9a3db0025d30ca725f04b5bda5770d85f74ea8b3ae44d26168b77c0c2b33cf91b056a04b178f5beaa529fbee9e9a9c4baf5a4efb76cffd639058c5b845f00b0e2cca7098f7f8e0ea7987da694568510ac50e24f488d68624ddadbfe0a62dae5ba4137096f0eb2eb8c32e38c638ac9583be8ba54a89c4f341a5812fef195a38e4678b00a4427a78615215f60c53bb796ee71e3d243d12fd4afe7891921fa20c05d2cf60ebf5a9a9aa793f4fbe8ef437fe1d255885219e73af0d792d699297349bca75206ba971c7183b10d8db4305e8cc518a1a4c56053c87f7a7cfc4fec7d83b37f2308d499b1715a1166e0aed260676989fca46a95d92668b437d61bf3881f336fa1090e1eb6a9458eeee04a9e1a7897a92295d18793ed8644fd5f301f47c586f27b00b85c0f8490abf77a21430599b2aa17f74fa7ed3a3f41a93998ab7deacdb38505b8efa4fb76b1e9165185c4c7ec6b9834fb42884a3d66500a6af7a94d9d2e950541deeeb7b0e697f67667cb28faed386a352219eafc5daf3c59e72b109d51d804a2eb5f0f6d33c17af57e543e13ed7cd4eb50a906d1b90eae0b5c113275315909d5b372f38353cfcca11851fcf59a29070ea6bc590f7f50878fd97b673677da06611d4c926e7d52d5ecb5ae0819c11ea5bef1b625040f92797408c6a2f6950d0b2a8042991e33c9c8192da946d1d71e62a0715717b5b7ac1188aba6d18a07e70fbfd7d7488870233996d3a3296796db31aeb9b8804804b8fad5e45e416be47d02ea5f20f571dbe0ed3fcd2233f3b05e36af0db0dae3d9f68887ec02b19ed5510a6678efa80b7c795be3f40cf02daad636161dab0f47d6cd3fc18c34fcdd60d80846815708609c75add7926b8d99b9aee428bd4701460dd2aab3cb45b792aa480701976f7a2e0da9e4356d9f3e5a61315953d56b3966ed7a88650107a21661a70e2fbbef0c8946e79a0ba3e4977f43fa9850e2b5c1356c899076336d56d1626b04ca4a20684ae13f5c89759610ec9b350de561696da7ac42f7411e82c50db2000e43cb3ffc5fc019932113908556891459d7c83a26d6515f645754d7c8447c170dda7b1646bffe8a03abd7bdce5cf1892f6ab04a74c846c0aebd1611441dbff70dd11b6bf398b93c7d62ed4c267e1d783f5eb628d54043092edeafbaec56cc1c0142cbc2ecfcb7c087498ebe4443c8101bf0360fd50287996f4907fd0977357b0979f6bc3e4d732d63f0396fa5f3f45591ce30475d9333c50370557af5dc4273021447b943db736f5b73495c0850d4bd017afcfa42659ef5f8621340d5e13f671ce4a1468adfdf3eaa2d5a18db584bc82c11436d859a584a7a0e1bb19c2c116d0a39d93b3ed61951f98f83141562692489c48db73fb7490fb7efe034a49bd55f050ac036aa569278ace7029fc4c20d421d65904ed035e0e00eaa3eb0c39257a8015e2819055150e686e1071ffecd88aae825ddb4215847e05c7889c2c93c6157015ba99eda2531cfe0b098c2d4ddb96b21d3ce7d53fd844bb3395b3502732faa05b3c0b598b3fae61a0cf5080ec39903b880214d7085ec5d24072d74453215bf080d2abe04ffaf26f3b7d2c58c08c2a04658b11e32d2ff9c34394f6d181bce24046333242a9cdeb80a048e7790bbbd7765280d58137ead0750a4e20984b3403f02434d27c9ea7ce82e789128fc7c7ddb6b16a56b21de3f4ef947204f0f65fb460937c468b750c931e745d1a367ab1162079de9f817d8b379815f5263366599700c074fe1339224773e7d5ce4d4be84a3727e2c04e6c53d761a1b63b1111f9ced0930deac656cee51fb8a238bdce7133c34c903a3763b5e4f133b32f742133ada0dbb0e17589ba12dca2ded1b10a1a2a980620585436bfcdb476e0b045b682dc20e49a8460c2c198ed93692fe7d9ac0cc9ff34277a7230c72389b3cd002b1d75504b5c23ee1f584d3a6d05f000b52b688c9090dfe1dce2f4aff3fc6b553584e810989527ba08499939ed7dc0fd8a6967ef0243424e7b8a1afe4113f1285864d0108118fe4f9aad65b75856157700c4e7e67f85bf2fe645b5e8676b6ee5fd4f0420299fa63cb64fea86062468fc4b04617bea950b0e2561a67e0b8f11a8a758a2700f8cbbed295c9d3c70446b9fde89a35895abfed53d15af4e61744a97c8c7b230a2b7ede7d2b87ddf698b74253322556f5dd0b112ca5242ea0ab23daa8be7103061db3f8a8b5e6a5ff775593ded42caaeb497c61f48d663ad3c2b8b4a2869ee60733687070715ef8f0ac9dce714c23e216a834d3bbec153656f96c0559146fc1048e9c37d8e98de5281a3c9ff0cee0d394a1b1816a4893d617d9bf5d713a9d4401b85aee89458fca872ba33626a62da76c9f826e41ae517a89239a2a16efa48d010054d594a3f76db4e592484b962630301bb2eb40f09da892f19b88e2b50e780e25bc256ba86d3825b5c08f05bb2735fc5cd2a0a9f1389cb2a3c2e916b91c3d0ecc8e4adae77414499996f2425b5298e842fa67f8113b72d0dadc60178a2acd01581248a9bb8adf1a1ce9449af9bf716141d1c6b38b097fb3e7cd0fbc97485900a4834b23657298a3e290eef903bf4228bbb9e252ae4ea419ee807603b9b11b0e3b3b6b3d86a3d1bbc9c6108790cc796f6b60a1694ff23e82907238f96023e70edd2b110035aa6c0e6488173a07b07f4e55f9aab3ad1ce89f8f8783d899b1080b3119af175ef8cf3eb9ab79bf503c260a0397baf9c349e0e22e02185872457c0aaf7d5ae29dd79193f2aa14aa2fc5172248d456f75cae702815bd141781cada0e5bef2a3e5c32cff2b33744faa6cf49bb5f664bdca0d2d5bd602af60d2a1c6c0752b6e8ce8b4a5ab9a0114808e02a0be6b6c5d51c542efcdfb8cb0b4b5f0a7703c20a629b0bee4e5267da185aa139acaa1fe0e3cf66e26c819a20b55f3b272e01f8803ac131f66d15a8d0316041ac3e4bbdc349e171a9ee733b089f31323be30715fd769bfc680a6e043e1d786288bd3085d2c9a23456ad3294f5f46e87af8f027029ea350b9a1c2199075b93d5175de1b66fe4f15977f80f4d6d46bef0fbb80603edb0d33b012ce87d36aea6b016451b8ba1e96c38c5c3f603a25a6824eae00afc0a1be0e3dba008c3778c826f8712905c1c2b389f540dcd4b58e1e4b8f15e0ed5baf54e06841376d9028caa9a441867ab97a8adb24f61c436d4731dff97f50d16294fe7ddf8230bee08e7131bcf666860c51de947ed34c9356bd7b88cbb3d05da45046980cf3fa4025c9a1174f6dd35b347181399b226384ef55d9cb7c87608ac5cbd341392a62062736e5d8b8e579406554d7f145df10d9f398f6465ae4b0a5a7bd5ddb7c1ecaef54328bc088e4e24edcf9caddfa667d28e81461a7fdc0502746160ea25353acf074d0caaaccaa2d71fdd650eca74e7f55b4a13b76d3bb4037b719f3bd8b88f02d683c3912bb88af3efbe286aa0a9c4b1ded952e81d71b300845c7a09ed6d9714c22bf5b70d3e276d612b23623285b08fbaf95c4361645c02b19601afd5ce9381e3af68d4b72d723234a5f7a0e5d4ec2ce7d6c79875859402ba44684ab2389dd823916a99ece9b7b957f9399e127909aab330392fa27d1f07e8ec3ae0dc65f8831623c2939a81d2e6b20dd79053f4aa72419a8e0f3d55470e36b4eeaecaa7a1dc7195841ca1dab9ff2489cf4c1a15d8e661162706fe670b033a6d691dc5e0648c378c1f1a20d3f294c6aafd51119467e195d26c6536b9e00b225d89fc6ee518d64bb818e15223ad2c0a0f58b957370c340446256fc92ab30efc37ea5d8c2f529b57fbfd304ba57cd52a2338b62dccd8e5b04c1d36723762038bff77c5ca68ff547d91d82b0faf32d4b256a53180d12d9ada853f88060c6f0ec6f512fd79771869f21dccfa0162e68404691d2f5a91c24e049179bc5b91e107800fc219d8fa402c305c4a39c8b30ee0b0828bde47ecab31fab7c0f586a8cc006e82a7910f521fa62d8842f75d7ae990fee851d62a3062492dac1a9b4973470b4acd59478fcceff17ccbb489b6fdaf7a86ef25960444482aee531342f5928a094bb5200a1181984aab9fc64947e2f972d51a5ffcd4654519eef106bc0b670e08f756e05ccfb64f7d55fc0e9943d9b8e5aeb5aa37014e3ad06340747c96f07b04f733d41d3afea60b0ddb592a78bb97c3255a577a544d1fd7e949d1b18e6ebe0aac7a682426ba180c29a15f8e403c9123927ce28bc0f4adb80dee57ca93066809f1b67b354e21d1959e4aa813eb4deead95db7974583c697fc674b549a543cd0cca78a4035b2ce0dd37538d3ec51704667008268dbd6f0458ec08cacda4e6670c97756c08d754f461020114d7f50e9d2de1daa54435f24683a441a23948a90c0e1ca3bc2a3f843d8aa06574a1ae3b1a121f2f80f405fb23990a9df7d9058994024ce633fd109a2001cb801675f4277736a0e0ba9942a14948adabb90780042a09c1ff092595251abb7f5f31abe90301aaf08b965ad90ecdc428b64d4db59c1d0e85b344a609d9fa0d91939d71dca2bc983a38731cc3eb1154a3d3455787bfe10fa1e4262843ab93a5ad5394284594d69eea0803d81cee7a3f5a5675cb9c7c8b033c86a978cadde29f85ad775fab0bb77290d029d6dee1c83dfa0666834d5682059d1f9db3c2b5d728b371efbb2983b7434eccd9730485697be207aaaa5b4045018e665cc6517183ef06100b33257f109310a5b4740958815be90a23e1cb2b5106832211078a1fffee9b8654ef8e0f13b42a778a41b2de5ace6454ce2eb9f87c010e2d618402310be006144bc8d40c9b462f39ac03976b6443be410304455cb507092c6f8235e81e0291b3160638c460340ee8e14384cc30534a7316d8318e2506ec8620bfbc1f650622f0b9b0830f4996a6968925c158ea0bbc19566f01132709b6d5b07ba0af35719e301e61b43248647ac45700bb3ea54c2baffa4261c94a02e3aa83cee24df01efbc1c1ed6dfc43110197994d5ab9e8c510059d78ae3890063aaa4149f33f37fd7687ff9b2d98ee7947b5f1ea955c65b09af6ec67de93ee0af599d433a54f0d199804e480cccbcdbe50d50e6f7687a7ac7acec08983699706974d105d2495ed6e575e96a584809abce917bc1ce435b912e6e4b42022705a01a4197edde9218245c7c7b16dad3e9d215b83fbbb27490e017fe022cc28fb3f0c519b186e09277bcd978270d5ad950a66b64d6f75b97a88a6296d6fcc31b35f0146cee14c10e3c55c8e0399bf76ea18c79a7ad4ca875ef5bc2bb9e1158d4f960c91da04daef7aaebdc92cd7133f5b12f73ed8e709a840d222012f5951e5e8c501d0fd1730a2313d4942a7344fa3a6bbc2d070cdae4953003ccb096f16c9688a0252ba62d9a6998b8c98fd7b33493dda67556683403ae7b70df84d9958b7989a0e0cbbd7b3048571f4c511be9459be610905730b2672f48f93584f69253d1cb70d1cc013068580ad8da4d72eccae965c13d7ea7eaa231fdcf7debb9a14b12b3607814c81fe334db22b1473d49bbc6773eb45ce0bf3e56729737fa0152319168b04579babf1d8d59317df8295cb980df30e50feea297221ea04c02343913c53dd059994e627fa4d74af86782ccd60ac3b19217e3b620f779f057afb4d3c0d364c044b0b42b36f1bc8823d9a22c9fc1f28f46e364d10b48ac08f1e034604696ba00ebc6295a80db0f46e162e6ae29d04127c1863cb19ec7760c42a8e1fd51366eb021ff9d72b14eb031f9ec4bb421445a65d92a752f461ab23200ea6d645c14d04009df8c947d1967b9ec0bee75cf4fb95c8a901092d92e915ef2d36e0bda4cf97059d2176414e992767ed9020c120d81546b6914ef82cb3c405f5e9a1f6e8a76f0f8ce6f005f66bf9066a2795a3cace013545c2296811d1d6ce7e7348c65fec630c7bb034a2110ee1a13702c55966223aa4b8f8bd3c2d03c1dde1ba28eb059f170147829166aac54a55bc91918fcbe2cafa02db898fae63fd9aebbe8d036edb8202bc34d58a440212115dd566e8fc9cf5126e248fbbb9aeecaa01dde3bc938d8b02fc2970d79d64ff59c70baac1c182636d63f1923760a6ae4045329c5920953404e0113b4bfc951788ba51779687b14982f94ab0e2f7f795cb8473b3a61db55606428c43244426f31ab5c83973f5e96d100e9c5d1734f79ccb1f62cc92a52d7d044d4113d2897576b1a7eacdfdb314861fee9cc431dc9caaa95d9c3d9e48637d0e21a72881a6a61ca66319b565aeea4d34059d4e02906929278fed2e28eae22b036f2232c0cd36a66596e482350d88f0bf4972cb3c7fe705e5ccf62951ec6bfb0afe3973497f39e6fc92806c1953d27e1da58f39fad54003beb9a9fa107807dd0e3e568e83578080d4028d7d0fa13bacc40150deb33d1d4421e4f314dc1f38b50271d0fc194419f6510540e038d0e2d8ff6c3d9d6f979624436c435821a9a2f903758a889763ae7e8f9259f608cec079121e4e1bfb0ba74becc4c92d9fadc8f208c14d6d1e267dbf7a4a0258efe91d1d8dce46a687b1e31c613722853a8c65250469a9d0261873de342cc41280489c8632dbc58aad625590421e674140e530be02282e0e2140c238484fb1951e1b1b0f45ff1d6d69c4e9634f650171d90f6e330f7042ba9a824f9293c1e00ab41b3fc820079fc6626dfb9509bb6a9ea8048870001d715d357b7b99526d292dde8d1d31fdffd8f1a6463177b9188370b7f99fd705 false +check_ring_signature e3e961283f12cf8f05ceaea7792b92ce71e93ebdc2b9e8eb8af891ec7dd58c68 52911065ed731b27369efe7ed777cf7b995dbbe55cc35a98f23c74f51a00eabb 26 8867832f8813e726d342cb9b66c1a64349f634271e3b6b6845abba1d51ee0453 54ca22ef065fb1264e8237f6ea43ed7287985659bb972a247894923533f670bb 736de70d4cc03aa64a790e9cf642ac77bfa94f983a645867133e8cfd3db8756b bb8e4c3a86132f5f1f1131a34f57e1530e194c650f7561f6ce8e34290fe57bca 875beb79a3addc06f094a3b4d1f2851a5804d305266b9772be1612765393d58a 2df6246877960129ca098c732ff478c7c31ce5ecdae4ac3a8078cfd95d3dcac3 2e574775ff76611cabfa54636750e04f7f0ad1ee402adf8835b85d0e0e176f1c 767e4f47f5540a204bb8f0d26567b6a3f5b9a091ec40fdb973337165adb8841e 42c566302207e02bc017582697ecd0b7e055c703c35b793d22b2123f8287e94f 1e66ab2d2ef030da66d21d22ad35b3a4aee2de9a9e59a24b7348203262568e2d eb2bacacd6a483ed1a78a66b43c176bb05e65b34e0bc1922d909cdf50cd1d898 8775bb0daec57eb710c2d5fa19b94ac7821eae4aca3e9b574aabc29c928ed160 0a0f4417a366c752bd03f3ede81801905261c7e7b1f5a95286ba1f225731c4d1 2373fb9fcdc70117173dd75c1b474cc261d841d1161584c307e5dbb3c23c6aac 31b0144fea4ed99e84ea9ddc8fe97b47e7241adb7bca3a59ec3393a008b6edfa bc1941a0c84c20249b66dfeb720a13254c4001df0e0731d8b43407f3ccbd1314 0985b7b36c679c526f6fadf04b7de2cbfdb0d434c145a861b1af430f77968bdd bea38a2f34e9af4f9e4d3b1518d397e51725e34e7d823cc225874144a27d5a0e dde45f68213d1d2fb82c66b715e2972ac6dec4c902759b9c7c298b58512bca07 4e95444beff7ceaaec9c45dff54394ab9e3fe0fa83e4f751715239785464a58b a9e90a70da04a59aebdd8835b1e29e74a53d83256e272570d6a67d838789ca28 975d8effc03f70342f47517c4a2498582c2e13f010ad4d5a5bd17aca1cff1d44 9221a79590d79fd2abd90ac8957e97b570a1d0d035fdc2a9cf905cc26c152d2c ec3b6ae1a6a8975ae99a11d3b9792500a6fe530bf6072b327598dad0a4943801 d2f58ead16fdd9fd14c3af027ec4b3fa952e8750112239811af3bd9fda0cc1a4 6b84845cbd309241c89d20b6a07dd4e7857a3582509df6a6e32c1d7f2363ae18 8761756a16e717ce1497b9d3624774144b75ae9ef4b04cf468b762cb8181750c759ba573a0c812efc92d2addc48ceb7d7aac0204bbfa517ee380c8863a604b06b5c04b0d81ad793ef45613dbdd93df5421f69fc0015de4c22a99e079c29497076e22b8f3f975c18c3822ec2ba82967686ba68718ffb642f90ee9a52253bfa00011ecc9800e97fb41e4e57e454803e7936ceb72b2ace895b937ee98f3a2f37d05b542d2f9cc70954fd47c65b8b5abce3b3a5f69d44b6b103f1208bdabf2746101fc306f7fdac93e889c5f83750008e619e2e91895fc3794b10d15aa55a2b4b80cb293dfc2e89ba64d0b174fc8870edfd4f26e491d5702cdc6c2214e95997b890be19d8692606f28c2323753ef1cdc959b3d3417ea446c5933d44bebd7f4696105e612bd75119982e7eb26f7c606b0cc6cd1b594bef80e43cfc10e8045d6b2370d22212c388d7a27734a0a791e1bb4354e3f6771b2a3a28eec9b8ee9ce3789ef0fa0d6d4b5b53dcd17e4bf3ed09c74b7b19f6c273d360a9304cee572e8f01aea0a6af935a9b8f240be6ac16e431df23a24fa4a8203eeea64c50bf0b01531c6710138a4fd48b243048e40d0a9d6ab58d3825e4a3c7f740fc6597168bf17e9d7dc004c457771fd7bceaaa981a137ba80eeb2cd1fc364be47d0ba574ea20565ad5d00c3018671b3b0ac72ff0cc367a480de5fb7c6d7fe2a6a50c51d8cd1ea9541430cfe0664be5f7309cb8a1a5468da616c17675c58d95fb352df304f2c0f93415a0dc9df38c17fc791c28d2f8364a8817e4165bce28f4b69e94a7cbed277632fcf03a967f57ea4e26496fc06666ffb8e5dbec3438644fa485e9df2f7efb18cbe99011f5832fe97bac4c859745ea8f0661cec323145910a6278227f56a54299f1d60bff5b60fac472c949ee4a8e70304afc529e9ffe34e8a1a24193e5c4c0c208f801daf507484362960b358c047f96f210204fc69243f1229936e040f1e60b6ea30cc0c5d74961bdcbf344b5f1594a315ce6f1cb3b9488ffe784d289804a2c61f8083cd835d6185252389d79bec8a3c75a9e9e3bf477f86b43dd43e56b4c224df00d7950fde577c6542b8f1ef3daeddde3c7b0f497360eaedf7c568ac4f0e3ff3404f7e7717717c26f46ece57f34e498f5f0a7e982db43c8d7dc6e0a2dde88f94d0d824eb6b0f326529847708c77b418bc035a110003299cec58390b6c0e46664d0df206e7adeada9fbc01d8643ba9c85aaa2fcbcf9ef9e2356995add0139c6fe20088bf150b88e6aa3ce969ba70a5a438e00bfc801d1878af248ed588e02e1bd30af6b2c469f3557930ea1014a95ea8bfcf773cd93e9accc24e7aa025346b1b820310397e2e8a5847ef06e3b145b5fc19f47641c687c079ea7ce608ef2bdd4f07031566040870d1e4a75e576c7efad9e9cee10e3e3a5106a69d74023698bd18fd08376256fd75173075286fa3e7a50fd8f165956dfd6990868847fad3d70787ef01004b0f40536571d727ba5d7da3a358e67726fb54f6243c9d941443d2ea3b8d0d09c7e17e136948b5a24739d2490ef1cf0b47a5c4e0117bdc9f9fad86e536dc03b4682fd26b177e8b14fe0c4d7785a6c6c437d700065ffd815967d9fceeb52e04ebdb88ef88fe6fb0af7e3fa56e1176726afbd670326295567ce6af4ba08dcc03280e3363c8a47c9e843b3bcf2af404f608e9542f78b6c54eded7a28e7474c701e02d6567dd88d247cb78dcb0ccb17ff0e7ef784a6812c2e46affa12fa72bb7018f61e1c282d653432d0ba7e1332b9a7b6685756aba0d419ab33d463ed733da07973ba45da8e0328a773477829dd4d8478d13997216d6378cd85bf5fe6f51c30cac56f1c6f36480afc674b30054ccb39f63b1eae1e44b492bce2a401b597c9b09e235c79c973f3942961ef4af66ccd7ebae6fabb72d81a3e4df199b0b0bd01c06ebe888cadb315ae0841bbafebf04f7378c816530d619ac27aef91c93ef9cf80810c718c4a15866c433bbce0c07957694094ba3690f8683538e075f81ee3d5b0bfe2bd5cc6d020fa98b8d61559decfcf2a6a299b9b4185f0c4b6e80dab46cbd004addce07d819486a8f54315bf61e076126f37b112e08b579edfd34f551b4d30520364faa02ba64eb7487bbb88f04195c6f70e0fbe8ebb9c59bc55c99318cbb0a6499d055e2ff85ccec43305e313f61bfd7a529919c6c470ef0cfa937961ff2083b62c8f2e0f19499b918f803d4f5b41ee7a617f8faa8519e0af806044623c10a910713db80ec8d70468ac0a7f59ac5bd524685d212f4b00d338fec83ecc32e05318a3ce1dabfab2e4917386ba542a61879c903f3f4a2a2dfc07dd97d7ab72f0d true +check_ring_signature c0599b4217c4a17802407cba826c01b7251b4504ece77f40bc107b705101804c cca41b3e8fed0a57961722a35e1728145e8ac408db4d50470d9f31c9a83d5466 1 0d45c40bd5479c2be2137f112b262c8368d7c368b240ccc98f3cfa9b38a600f7 0db73e308d6e675e92a959cb508cc120b2f0751ea5b1780d7da6a1b90196ab050cdbf6bcedb3d8e6dacb1dbeabcacd6021bf798e1c2282bae522dc7707c4ed0c true +check_ring_signature 6406eab16406e5707075733bad32f4802c27f5b4eb9245a2e7c7b3bfdd050eb9 bf5db226e8037eaf9628ee912147f0e7004b8254469409bfb1d3fac8885ef815 2 e19bea6208c3de0e05e14befe649ce3811bf3f4ed574e8126d55bd1a3740c3fb 26a4ac393de724e157a14fda4dfc2c4ead1596a58e97a549b336cf8c3d1205e6 cb693e57512389268126a55a4ad0dccda99a476ea5dff5cdb337bf951fde3e68faf0c1772e4abe13e9f2cbedb028da7a3f2805f6b90df1bb455734f8df62f303b7ef671b0f87be3ac1a79e02839eab49398975f407699cb6ca398678bcd7ad04b68e43b255bcf279b15993ea95ab3fc6df03b4c4294614eeca4ffe17dd3ff602 false +check_ring_signature e632d09973a9b1dc13fb5324bdfd90c1b319925f269a3dbb50cecc26a9419eed d871870bb26416511ed6be92aa86481b927ab2d08293ab64850b2982b3442417 256 82b2fe14dc371cdfbf1e9958e4c77e5ba7a6f8c1328800af28fc18805a7ed48b 055d8bfbc7850f9ba1cd676a065d80a175525f0f3970ce174ba6ddabba34e9d5 c61572b29547444ac9d01186eda3408a3589bf2cdc0247dee5d1327fbccdbb62 31f557e1a1681ad532595780557a6100def818a0d06ba56e05d93e2f6f3194ce f0f05dbcbff16751c593dd51f03ad3f7c6e702164816c80aff95c721f5211503 452db49a45896ed6fbf72525aff05e6b2de772eb762bec3aeaf89c7f3ff95808 977cf798014d09ed6d6601b3bfbed35be5c0e70ce3339aba912f31ba276c0110 12ab2e72be7eeeabd7c05167a11afdcf8cbaeb976220cc63efd3b835a474d32c 898ee57544af4bb40fc5c7527622078fc3b6301b2b86d98a36e046e33a92c651 e6179ecf6d149a590945c1d064fec54e4cb45a7709694401c18ba40bed99219f caff47cb38608e055a3348c541837e7abbda784b080fe0004425948541105518 0fa5a9784d4c67c8ec76e247643b659a2609d21141776350049e4b5f51d43a56 048667babaee462cf060b9ad7af67d33b01b59130b194618b99c2ee5b110ab16 8611112612aaa8d0eb19e6bcd07fa4d38a1be687ec0a93edc6913c6aaa945276 8dce84e75d18d2874f05b9e3492838173ac767a15d39cd125fe3dd53d8e0ba67 2fd5444051764941f14d709418c27b34dfab206ec88b0deb9e19373907055f32 4a6c6477829a85b0c3b499b8fba27223f9bc00eb49cfa5927821733a3b44d8ba e30b3a24268edb6efce978198269a332bef1b21b6c189b55771da686c9252bb8 8783e7d984a3bb1883580b024d326fbfab056245f4916109be01ab1852be3f1f 77bbbf4e76ceeb672df1901ee6db8c3442717eaa29ec1023000b955411ea7201 efa851342010d83f6a4329d8b21691460f99fc028e660c155985f094ca841074 d19d533d47488bf249f2a7de67e867147a82f025641a002f9a36e420d1d7f535 200e84ee39b22332fa24dda5ef249610ea09bb1bb775424e5e79e7830cb1dde5 bd2e5a43392bb78d3ab5a900a3b861e6e2095fd38d210f521b417351d51be599 35ce16a5047dbe06485610897a2ad36256d82bbe1bcd5f3dd4bceddf4dc71761 df79fdb70e2decac92800f1febb1f16b55c162826d2df69198ec45c61b0ccfc6 49435153018a5ab0ef4cd32c23779707b2ba3f6fc5a010311177c8cedbe58fc0 9eb8e9409c78dcc7b016f8d5ab9596c89264e7836994bc5b92d31458f3a8d049 a61aa6ded7dd9e45c3824eaa3b3258de7713e6a69d70b909277c844c47adb2ae 7c87bab590559abbeeeebf618ef4771e8a270fd4d1e1a55d4e9c188901b3ea0f df8fc84c2ec441488a495f5981df366f56057a2ba3c1ca485c2c07a7a8302a3c c34694bdad6ef2f0c77529cd67ef1394e34a9fb35e11d9494794047735306a42 262a682649edc2dd6952adb6e108308c226d7e0bb8c12f6f6f4828420c701711 a9006c3ebdfb434669e11995965a4524de100e125472b7b209e7ec66bf2e7c1b 6e49696bb8e21336e70f4b1846e4e9fb3a3c0bf96b5fbbc6ca663671221c8064 1a399f87a51212505912a0dd33ee552738b8533667ef346471f8175358b721ee 253e42e9a0d281f572543b16cc55324e1120d355291aa56f696f4d53a9126988 fd1fe24574503831606bdf32797b1f3da9aa8af15fdb0673c99901c4a6fd84e9 4d119007d902c7e3d5da4fb0c33847e34118088f3633c6d3ae385d77ff35e71b 815904e158ed196790ca25cbe929d47953d12ead5f2af4595f139e196b41ab4c fd1c5393156c11d5da8fe2a424f56f9e454b20ea9e905022054e4a46fb3c970d 808b5c0f0c8562a2d3608dd408eb94998f7217ceacd6a28b7487c4b9c74db286 4f60d704369bf3c5c8b27d93a54d128a4f01323a3f4c29b3940edbe04c34cbf1 095c38757e21269cbcc726de1ff2234ca8bb460c9eeb5708a9df2a1aef169860 b0b8c20582d2505bf6f740fb5dc5933bdd79c04bead45715c8617d2592034553 65504d4a93589d7a89424655ccaa92d73fb9a4a59c57f04027e4bae42e8be7a2 de25154789d7a1a14d2ea31b71290c082ce610f63655e09164077968e5433a98 b48bc691e7514a2157e17197825618949f111edc8dd7d750ba8192d39f182bc3 0e44bd9666fc9794ff11aad465616387051e969bd1d341a536f36c58ead9ac15 d58496ec23b105547897761354aed1dabbc47d266aa1b3fdab707f332886d1cd 5566700a8265173e587c365629fe640bafaf38b9394953ccefa14e065c701914 6836d1409c88ce98b0b6dfcf55fa0eae464bdd93b9031932e21c8aeca549e36c 6f6545ae15d488cfe4bcf140ba3d47c6acb12ac953ab95277575844d8119339e 811f9048f3e597c1007c937b604456d0658be2100c076f53cbcc0c56b3d6bbc7 5fdb3fda0ded5267b0b5e09c635525107cecb2a0fac49f5f16e2e8a1981e3c62 fea78e97b2317d2a8ce7ec439fa2375224eb675f8ecb8f0308c30ce909324885 3ccbc32f4351a3edb0b28e3af62b46f34f9a836ab7283cbb3b5bc9327fe63914 218d869236a87ddf294593f803cc3387e3d6afb6a57d0e61664d0e7b53641c28 440a60321478e8380feadfc4c72d0313cee600fc562f3019fadd4a8de7da1b9a 3f6394c67c55fd8f99936ad4ffa19217f30ec3b86094533282f6c5abdb1166bc 2bf00a9dc9b528cda20b9a01061ffdfa61d245f5b42050582c2039ddc99f5bbb 7463427ace5e2b8ea0b3ea6ef65cd0847efbf5085658900d986b4aa0e3820db5 d2cebbb174b13019bf307b5abeb2169e743623f4bb9e30f7125887e71d97dfa9 8a97597b7786ec633029419a0e35cc8327aecefc1c1d065649e7bbe2101bebaf c8c3b731814dd6bc3743df89d6b9720cc34e9fe4b795355040fced71226e407e d1dd25ba680be42bd2e7c2d94f3f352f5a5cbcfbc82407885765cae1c67a3bb8 16335b7f8914fa33c3084ebe166b8e47e8e6b640534f00eaeacc0bca1ebdec65 eeeb768ab1a93e50ba63541e839ab46c9845debb8afcd54cbfc9abbeaa4e5e0b 641642f8cb69bd49cf6642e88dca5cd25f77974c0af2967464e048c64469cce8 8e57808fae42d72573fd5df4762a1b6b7de9be551367df68b5f069bb6c734cb9 1d293147dc81ce72130a0ed4bf23866cd2e4adbb9dea681fc5d26ac063a581f0 f867715c2b190abc6bce06a3fd2489e658756c67ee7342043f8dd285241e25f4 0466eba09892b73de8b8c85a81fe78910152c062ed0aa7384279365e5efbfe41 b4add880da72eb40fee745d9f14190d3586f0a842298501e4b82103b967f4f39 136a11b55b8737ae977163e6fad57c00029924ce181b58dde098bb7bdd70fdc4 686b86e595be6fef895cfbea78cd65b68d39a2bd5d1d8d358dc20773767de54e f352a6d37036286d913a91407488f304bc0087deed80a32189437eafb5f06711 bb16e0483ec1b355580b3430b5dc6a971842bdc8ea6b0f76dcb4ef4e22ba83af 9b8cda2d609846691805d052f481ba705a334ee86c4c57572e076893372566d1 e4099711cf22ee9a5b9b494ea93b645277d5a86c79830f99bb42486ddebeca91 7afe0214493788b650cd16c84c17ad78cd8ec64626a711548a329a498973579a da9709231e8a7b2c48eb76247e75d895e408712167dc236cb1eb605526b2e801 0d894a9e8bd1f01e4478158aecb15a7276663159d4c21932e6702d75678f0a84 17b732598131ef727678afd8deb63805d532bcbdd41060e9d6e9567e3430a89c fea4772c3bba94a7f384bcead40ed1c86268db27b12377db943a24534a0dd975 865817a10186b821f833ae25043624655592b976a91b1dda91bc98a69533e590 01c668479e9ff306032e7b9f45cce23de97cfc9efb657e3ea9b78fcd17395105 e96ba5ec13f6a7369276edbbafe98057111a8d904543b1bcdc57be5c9dbc74c0 cecae434b891b00be24c934195b473b978bb58649b048155ae7705f10fe56e34 ae538c8bb3d96a33d7a86817858eff10d0b125a682a5b9ce415cafbcae316545 85167747a3021f063b57994bcbba600334596b9c192007efc3d5a1a22bc8f396 0b7870c364750157216432b96837c2f97327739aa70c1741fb7b76cc753069e5 779022d6b7171ae7c4d2f8b6659383bd12fad045d22bc4510c733adf1e8d69f9 9ba8c2aadb086e5dbfff4efa95f433b0fa38f6f71cb7d950b459e52ca686cf84 d26caade220f7daa01cd3929ebf8f7da45e12f465847e457799a42f06c9331e6 881e2f94c31e30f53bffaa52edd4736a81c8f936bd077921914f8f55e1a0b391 287da07d5cc4a775438d2c1f2ddb28c94103d525b226ce9c2b18e39c8b139972 34fe06b7297de39c4c419be0cac2bbd3d7292e16961fa606fd31d745061159c0 c97d230f559d9f10e46071b33641ac1386466913e53cde9f5022c543723e7176 0ac4d4909cc7a33f3e14d2f4254856f3e7017d1dc9de67ff7cff9c9a3e1b2b82 51b4b458fadf6d872f6aa7f00b083ee8eb5301043d30b8f635320a891cb510cc cd028a4796709a343782dd5ccc2db51b294a95365a75aaf5d2ea1c8665782acb 9fe87814945218aaf7804fd36c59618c3eb899f96f370100be6c9e7ec17e44ab 1e4c34579a7617247aaa4c531550d0460591065b28f5886c09169f4c21128b33 5e060676fe69c40e4656acc02c282ea977bc0e24af75a6daac726e4c30e507a5 dc93655d92fdd1634ccf13061732f0e22540066a0f1611a587a3dc6b917d6a57 ced53b882e3bad19887b4825db1dcf77c758b3f189b58de42d01d9c1f4e14e9b e51b6189ab85a26ad2ef016c8f7722deaacddeeb65a89035531b609b6c499592 6c0e19892ba6f280d803fcff15ae7d03ab34a5ec5d727558a50ac2a2b8b8f42c f402be841d39e7e43e83677db980765377bd5059ca306ae6c7ff1a060d397885 34d5b17b08d5d31c3d81838524214b9d6ce3705deaff9fc70f7a32e9fa19a1fa 0f9e358a857588b2f61f234f97c84b5bf13c1fe61c6c009bff169f28fb213974 f1e81db70e93aa25fbffdec555f7491fb3941eaa3f5f5c51a3eb148e32552de1 d2f4b16682edc9422c4a1785959a2d4d9a6a5caf0a4d902123d5b9034d40b998 cf6319313f525fa71b468144e568af262f5d4bc7c3e3d920e0362b53d649e8cb b4ed0e0d93a303244befd4665cd8aa2cff9a62a9e209be5fa60eb338253dfe12 e7ea33d80d19b3b1a31ebee21353d328377a935eacec2e56b95bc8c6973daf78 9a758fd69e441bbd4f5ade7cd347f6133937c0764e88e11207d96bbb541369fb 7a02f461ae8b57278d319eb8bafb52d0aa3ee416850304eb01de560c73847654 6b688aec580d86a877d9e95ff78eeb5cf1a6c638a1d1ed0283ec46c3346951ea b606471540dc538232e4f54154721c40b9137817b6d386baf61f059477d148f2 95993022c8bc7a1ddcf557b2513f8d487daf0ba8c78c0b80f6158048b85655b5 a70dd6736db549a8085960bd7d40670eba09eb5cd9b9a2544c7730117b55654e 3263be6019fd69fe538eb3f5d088f6856640812ec80f26a320a949ee36c2cae6 354c5aace12b45190788b9e3e05b8899389c61a71f39fbe1e380bfc4a41aaa6c 5093c2b4155040828f91bf48bbf8f54baefd53eae6ce68af7792ce6f9cc32773 c0a279ed1d4652701d2df224534683a43492bdbf38ad5c0e698101bcc6433d85 fd413fb092b3630e6d8af39dc2bac49c7ff2622fc1ecb5396b7729d211768796 a98828e7a1e634699d8dbf521f78cb6be1fae1b2d8ec0c8509182d6d924a5d03 911554f8cbcf7f95a07f2483399cff84161f529985b4f1367f68f1822ffb66cd d00b871b50c87713c3d60679696836e6e037f3fb93217a9b8c159388be1f07a0 2e0fcbe3d9f44d32c3f3e7b74f342fe65d716e6293992b452db2846d58aa72ea 320dc1939e0c200cb3c325b402bde954035805de682efbbeaf892713f87fe2db 92481184cabaa69ccf70fcecded1a83148424ee1bf890f8a31d940e882319224 b18583991f2c3255fe36fcbc4c68918b0e77923db705f2e22736a9d5c946e354 4a9315af9b638fc036f9449a37e60b6718ac3ab53697dfbdfa0228b80961eeab 2f531b3c9ffbd60d6b0992d22298ce14fd71fff263e7323a19b73d6ed3202038 152e5c35a77154e779f2f51c73230cedaf0466159b9c4af871190e2e47210c37 409bb4fa30dac590607afb252f163c9c3ef3d342b66a8071e963c6c40aeb7a45 9cef29d5e1ccfe36ba1d2990d81448308a90861870f1082ec8e9e344d27f4138 dd702e9b4f2bc0eafdf0e76f47fbf794aa71ffd15bb87e3d548e528a807cfa38 059503c6c0956764d9d3d95e4e3c26c98798133a72ba177d0c13fc1ec9b33655 b069290ed75f3467f677b3a7e8ac179b410051cf0431ebf371cfbbb29b2f130f c854c540535611806a09ea72b336cadf87bef9f3efb2549635c389447e213496 d30777f7c586c316edd7725281a2580018ab2f942ebf8e86343d1a5394e6d322 c8c4e92ecc922ec0b5d73dec84888c69805c33563418a49f3a830f19b716eada 0105a75a67e72283817532a01c1433c06c898063a0547f37a5bc6e8eec700c67 93bcbb6592ac0258965c9fb921166b5816aa4126de2d33e18dd2f4f900ec6b33 1abee46c7a5bc59f01bbf63ed1cbf5fdadd2babc0404c1220c529d06120943f5 99ddc15d35d218195eb3aa2edf930e3d960ce8f6869e6654edee9676ffffb84b 432a16be770fca5cfbc3a30ee3c7ab9e918bb9e8eb0102f6c94bb418ee9ef605 2a0d8c1e900c4c49bc30ac39f05af2b64bede4cc07921ffd1a126ec9e525bd29 685fe4fde38c421b4bdd1afd9772c7adc1300b815a5e8199b702f7bc7de77e60 6d70491fc73a532d32e4b461e67845fd482e42b0f5a7481c7f7bb4829461b970 e4547d6a6fb31c66724cdfd5c2a989272ea0bf6ed78519bcb10778ebbefe7a55 fdb22ac40e5c2480e3f7931931ee0f0586f5bd1bac62898ba5946c942b673a0a 590c09738c6878d578019835c3ad55ae5867cc3c6093b038b9bc57d5731ab9e8 37ce9d158e69103dd66d53573d62f07173f87644a443378065126097537ad522 3fa505733df652316c1f76f92b8cb72ae050d4d54391e1dd315d11af139dde81 f4c72a53417b338f22e1b476ea0564486dfb543a7b7d2d587d9c1610ea0adbaf 0030abcf3aed8baec720a86293237bfd2fbf3160df17acdc135508e680d1b0da 02dbeb0006533113bea94bab470a35698ae9e1cbfd0c1874129d06a0ce76d4eb dba60086723e8176b8febcaaa3668c17281b132e08e8246171be48e386b35917 c27c4429fbfac92cfda2be2409ced27130f489cdea43066a66292112b95d6b83 69dc36c25e1be8dc3b47d5fb7a5eb0efee00f6cdfed1c572398b4f7804c2567c ee52708e4145475b914b09aef66c1df828d4ea71dbd5ba7f36f02f6539e649d7 2c616c635bc09708b2a861eeaa53415e4c7479b0a99123d4ac712c93aebf4d25 d7dd62774d2330884111c23a412c4237095239c17ce44b2b0406eb244ede36bc e78f4b6ae8df6cd9ce7f4cb3f085b358bc4ec22e02d7fe06f4efa8e9b8e30f03 3204bcf3c1d26146f26cdf08f79fd55f856463ef18b93878ba87b39ef24f4434 f5620a9350aea0eb7e7bb0e827b1b4e5cbfde12225a13cfbf9b2ccaa45e64ebf 66a2cb99f1324db935a5567df65241b9d88ac8e6abc98399b88b502fad4ac2c1 0f6c6eae0bbbb1b172922dc2ed3cd85726bdc632edf3e0a3a61d382b249a9701 01888f3bc3da5362eab31daba2f6ad54caab2c65ab7a1de6faab243e5c7d30d4 c3a695e7a7bc26661fdf35e2a97e05f7390012a668f4a324b95a93f647500dd5 e0a4f4ca2a5ae4ecd544cd89874cd62d1f02d80bd847cd095bbf9960434a7bec d8cb85004617d3af04b5f722e8e033ae9dc46bc311be00f745415ae6d3ae84df 3b44360c9f89168058422a6fbbba138be046e5c9ab10165e8098a897398879cb c60c88134753b79df40051c1c67d48e507e40f2ca9b5c644b24c20c74eb567a5 03693034bc5407152ad44fb129b63125479a86f3dbea67cfaff7ea863c5ba1d1 f203fbf44967198af097d3bfd89feb8a88fa96a8467d94a4cdded7e61ec7f811 e0fe33703ee90e552e171fc06fb256d28ea7da26515393fbbfa376340b511d49 05cf0c1a234bb702f65117cda219db091b147b26f831eacada12ed2324a56848 76918065de0f5d8e2efa177e919d3c70e05b485f66dcef001a740a9c6908d5fe dca17fb9898fdda8a55325c4fc58a01193e01850fd205ee19f64b1c3b8ac9e37 a438e47f01f6e3d5f776f9aaf945834c40b8dc7828f2b934f6479855eda5e800 417c0c0d4a9bc4af86b6ad7468214130564272aa354d15387b4e6867c750b255 0e73af905e2b23be54a763eca1c4227848bef665d424719f1331c3dbce6a1b49 fa93ff0e8dd3a63d579a7d6e42728cfc22cf22e13339dea6cb9cc80edab6dd28 71be10fc3bf0e193001b6713b2d2b4f72e8a30bbf1cdac596eb49fe4ba845b5c 22b9b7ddd42251ce36b0ee774523a6839ae5126e4619d105df08b01697b36b9f 07f9a95ad1db9a090b7853236027b667045af8811c1e0872c5ff2bb4b3997d73 0940dc604ee96bba214bf030caaf349ffdfd6b253254169349b2eb3355ab6cb5 28c6850fa24443c9627fd0cfcb84deff000b2ddbe5368e4275f838dd7d4458ec e1a3897869f71e73b9ef83eb8b2135554c96e2d9f9b9d57fe662d0b89dda27f2 e1b352b9f42ed99a8d71e1a1ca65186e89bc9a012bcce1e97c592640fa90873a 6e798416ee9983fc5cf32f7f0d475faecad6cea1872ccbb0dead575b7c32085f 478888a57e8c97b46545be7eb258c0c7d6a0140f4710db1eae0a9402d133f28f 2d9175f7d453e0e7d97f5454ddbcc1f2a98ca9632942a8155040f178b66c4a37 cac7a3255241d262b7b5c804a257eb8fa4f36741aa8d22c9735c4a0b1d277574 5a8bd25abe0b6416a9fca38803935fb8fa2cebd39d05dd37bf3f5b1e934b9130 46eb5e769e4502ebe0d36bfeee0aeb79eb7512a749785fe33429319e9a2ff206 8714ce4e9e24c468cc7f839f3758c11952219b2f8864622f149281584f4e45fa 73034726ee47f64e586a5e35802a7007154d15b0401ae1c5274941d177a56b69 71b62119304125fdb02af527398d8f9dd517053994496db902d46c2957220ef9 4d8a78a9ab985be37192fec66fdc5d54c9551bcfe622441a73874af8896e4c95 8bc9b8ed331e3c7d5bf7995bb2be9d0df847f93cda729648d0b9c70ea35e38a8 face1a59ec24bd7ba81fb833b6cc82290d4d19ba9eb1a8c6137a1bf76144fab0 b31c35484734195925d38bb002cc6eab03f6229a81797084b51c28da594ba0bc 85bbd1b4a02f7547b61932cee2d3be35d533e928b33fc3b4ef6b0771e8f4a3cb 85577fde78303a8a21b9aeadf42b959a37bc0804660057419803e886226037df 1da75cf04be305b0a41583ce41ee034eafc672dd56c2e1ef71d221d9740ebb24 6f6f141470614f926b3ef8fffa5089ee3a0aad4de941a0ac2b89e3b243c7ddc7 758600e9d2d744433bc080ccb71521242abf8d069f88e535c70c08d828fbc0ae bf360bcdbc4b9d02724b289ddc63d60fe3b8f4bfd555e328635054b966a62c64 d0f53bf846dc2c5c7973647f35a226130ac95f12c83b1f54409ca3f25f7b11c4 8c3e94b9e8e40b826e77fb687e9e06d0c786afc5e3f82354d428a79832e7e6fe 00130bbad21ef2e0a8d138348eee9a9c2a4020d1d69b8805e84f049201c1f691 3f532729e237eac4ecf42cad624bdc4d255d54cd78c89c125e9d8a1260e15ee7 1fe9c2d3ab7a9125ea2ebd792c9abe61a12a4b306687db8678aee382a5512e29 033a08bb53e7b01e52e93d1f14045cfaea8ad93b0768f8a4d0f1a4f5d390097e 9ba00f551be6d70c0f729cc63021595301135c279d4de74fa6ed8e35fd21ebce 1a52ffc2c617d9aaa0cb384d54715407fe2293ca5e6a407ab69e15cd7bfba2cf ff1997574f5bc33c50d84fd500841d82769ba8b74455cba98706910bfa1ce8e1 2c2601f7920c2a6a102cddb5e8c4949eb8f1aaea826c0fbf48befa2541e6b91e 65221d569abdb667bf8ec13cc4fc4ce9c52bb933aa59f179a50c126d33ee2f2e 452e7ee4b6417bce9ccbfa4bbce14241e7239bc803debf924b4fbc58c343d323 fbfbc984a58c096b99be90225bf4e002334ebba8281b761ec15b4fa04d37b46c f21095776a60ecb8116e40b7d47ce6c9493695cb66acc20b3922ec9c6d852fd0 979a849c37d542fc00901717e0f3ba9152a41fcf3889545208e5a4475d848ad8 6411afe3f9e759adae0fd4b1ac66d3a0920cb8526c737094f91f323523d65de0 21ab6814acd76d3a1bef3d75837e6a780d3e2091098b0e528a1daee277a374b8 4ed83b83eebcf3545509557829a97d4aa0dc4e5b9d48f20b7502f2a659b18c38 f58ce93dae912980cf790c2de4394b487211885bfb28ad3a820112b5bd719178 c32cc5c0a465af79d2d8c08a2fa3566696863d21ef440fdc05330c766a54a27e 2f0ac25eb83d1d44a8c02afe2fc7018cebe765338ea2aa8397ef95a1eff28e5f 75ce394edf36a650c281711d13907230ebb52a2069c97b24e406e88516c0d1f4 94cb47c6447af69a75129155fea5f60cd216aa7ce951e4f3d244b4805d075a93 f1439ebaa4d78a58d3c4ddf61503aa7f9c897cac0c706c23b1943f9231d89788 cae30e6342d003c70fab797f585c07f6f7d846eb16ca5a5cd9faa84f8e3079f9 13e135b5511ce7e6047924ae5a9c9e1b271c0bc6fade51085404424024b8f8d1 6eca4b003f66743f123c22d9ae71db24a8d3e3028fcd80bdb525714df41490e5 4d95a7e8102a0ab86005c824d66e681e25ee96be17366966962a3ccebc94daec c2e303016144d9196cb509ba6d65f5e5c0f07007208d83f24eb5d8750c91ffff c658d45daba026dc6cc5876aa23bdbba31ef213352231138ea911c2188e49960 27f0a0dd6114d1959ac290739e4dc5a2b8cd1efb89a57f4ac641e53b84e6ea27 7f9b6b67d92142e241c72968279fd15e1ffa3fbc6a8b32af4fc53da11cc7bac4 db10432d4dcbf7cf7a20dd568ebe46484e5fd7f8a1a7861b564ef905d998896e c0cb50982967f60f490caea2824b0a53174c5a50e7e7c1629e3cb27d21348664 dd6ff252d151788fcc2f06132234ce19212bcf396bafabe7a941571c80113638 e471407fdef930f26ed2c250dd9d8a2df1a970853e80da6bcd43b58d9f5a0943 9bb546a7fc15f89dbb4adb82eaee072478a075dc44da29faaeb8bc1a5febe5be 3230622e9779e353d99396ed02f5e360399c2a4aecdf13b67275dd3fab72971b f69a2f485449c3235728d3f135576fe1688584118ad47bb23d3eedf821985dda b3acbee78bcf7470bf9e22283bf323fd380681d92a5f2dd66f2f66a831fdd3fc d4d5f98d2c90bf9a9822a3a8ad653e340e7a08231681b28b6c8316550d3f8182 6db947fda0c384ca1040424bbe970173ea62b37b88231a3924ca6c5f4912320caa750b73e96d6312bc20dd08fa9a29343185e1a14040f4c40a112079f2cecc07720d47cb145846a4da17021f4513807617725a131428dfab67dc626ed2ebb20424b8e524e82d08fab00b88f28a641a24c494a2a92fa0e43252417e2c95e85c00dcc73248c287b5145a9d8accf96a0fc7084e67b9fc3afeee3d68d90349c1670b51e406efe7660a15e52cf9fb11a64a12f5eed374615f2753959f296a58b14e0e439cee5a8a14ecd029fdef75095bb225975805aa9245875e71ba486d098fbd06a34baa8cb67c21fff72a93302bf65c8f12910e275db2b6bd606fd803f3102e04f60de9ea22ca174f02494aa0144b3ba74bf6f59822073dc05eefe943a6bdb60b9b0b5d353f0a9416c287361b21e2456ad510253406ed02f8e3169d04974edd0634a3a1f8a83991c0d2f8d89dc6feeb38ce129b8ef292004206027f778da719004e681990dac291c8d26555770bb7122aa2502c3a008401108bfd9bc41c5a3c056417323b7f9309b2de358592f71b9fb5da4d20a1e33aedb03de111b11d2f5c015063364edcaac72597d2b1bc6859932a1914163ee75d71d634269115a62538051f66dd4a3cf97a1470c16cc845275ba7e59a720f2ab79448b1e7ae74b9a6f00f603427292c47295d36bc473bfaafb27a255b2cb3303870d032262c8971fe5c03f9fd9322c52c12b3bdb5bae40eb2cdb849c8a31ba53fc1012124375be3fc880d4233a9c578aeef28e6359d90f6cd8c672c415018b73e13eea2593c4a2dcc24001919f0ae87a354de97127e832f42a17d4cc73c16ff5bc16d529f0f338be6670f4362e1c77a896b31994bfd1c1d88312f8ccd8f459e77d52b0285d5e87a76f10b8d2f12db414fe2094b2452b8d8ed721936c78e1dac1e9a4876b52032c9d7f40e24ea1d1f8cca989810e080beeac3cce98ccee48788937c6b31de61152f6ffc064265b78e5b4b68ef80c000c1f12d15351b95732ae0e2ec5bd0d74fc973c3bc0f4551d1a0420203af81caa5d75e3759528cb45fa4cd25e5bd6e03bf3baab716042083625e13c083895291b0aa6a525080df30f7b04ab1df7e85435d76fcba290f7ef66c8bddc4ac9984894eadf4ad3c5d4456624c27edd2835a1d5c2e991f2003cede67b3d300b976c4485f61b69d6a8622e49fed6db4ad986cf47bf63b68490353c3207256fa0406ee23022cdafa86ecb7c4e5db4effca9b10f3d1a3f575fc0d841e8776050da572dc72c0c51f90190509e6e0604da5eb7d905de679c4c40a00d72efd40b956aa2f5828376e772bb5ae96bd9f0bf1e068445c4a5fdc59c2910c764605229b1eb414c36a450608dba18ccff34d0cedbc1391ba1dc3605af1a70f33a1da419041b3e3fab03b774b530a2126b339336dccf14d80c654166b954b0bee3637b93c1558b746dd33aa064c024e327ef1dbda6c4d9cc8a6fcbde9041e06fabe8b0b2a5bf1b43e79a9294abf0b5234cbaaa494fedc18229f2b649cc0480cf6e30b2680dea8400dd01ba22317e06664403e39c2fd549c3ae19fc98cb0a8074098800aea082c5c9f54b073d078043696ee7c046cce0ab2ce020789fb17560ae2a0562b068d3ce8e21ebc8735cab339990e37ebb6c606d19ec3477f637f76037ce3dcdf31ccb6bd38291a7d1ef3b9bfdf335b8163185f7a83a8f40d81420201f69a9202c13dfcf0390757a0dbcb8a54a53aee99de8929f0cfdca3ac1574b90609ec8961243271e0832263c60820b5e171c5e86f72fb8f617da9b1b548862d0b7681c2cba399ce565dfd00f1ccba6b3db718261db6d799fbc2fac6338fe6e9078adef59e1231290481400545f19f772416552e239b1c33e73cf27714e88c06041fedd448ab7b44953f1d3801f35bf6bc49e7349ad796f2c73f42daf10ed0d60f579bd88efad36fab608dcc093dc8b09e1d0871c1d85fdb71dfdf949505e449014b4cbb7ed6e9130e58bae5ad2579bb521849c99d9761f7a0a356a4c8b7498f06c675904208b547a6a16884b5510ec7746f974f93e6d90bf4e020bdd3652ad904f6de2d87a61123d87394a06bad64ca4b32a9bf3f04e99ca71a30f1fa7cc03b0b84af85ae1c80be7c0c771bb4ded3cf5793b73347c7f979431a6a90ee3f465c0b45fe3f4424c1949f0297223d11eb76201f1f0529db0e8dcff096636d652d7909bf8080283575b4c1fa29de0f53f37e799814893cfd900deffc588b4336514d04db3a5941d005f5a7d8323456c76b5f8b690a474c21fff70ab5a89c0492d58a0c8494d902a0ed8dd4b9f661bc45d823bda64ad64487683a3605193eee953cd80686ec1c1d8fe1b74c6d4278e3d4bf1f9668213a8a463aaa3542b8a19f7b81ac018fab64b1990a90fd90d70b9b9dca044617a9c73e24d0cd7ed6e87b26b6e7a90ef6968ae13d6bed3154ab3278e621e8ca12e921f96c09799b3295a21c7fdcac073ff468648c8e033e44da0e1de52be84fb28b144d1c8b936956c2ef44a6af800b6019535ec8363cb5008635cd117eb950b46efab217815c9956e4e30f9d8bdf0a358723ed5239b980d1e599e6bd34dab846002764fc0a8c3fd9ab8d11ff57b80e2fe01ced8c19ca5f0e4409421fc071b922efebcb4b7eb77be06074c424050e010967945248b470e642a3aecbe6ec79e28bfa39b3002bbeba131d876ebf4d7a0a3319a01c3d5844d5c8d60f6641aae5b3d32668fac16edcf752575afb0c4f8007f23830013e5e2cd5a41cace5e5f7968b24daa8e41fdc2db7035c04e4f7543802e6d81903c0dd4470dbe7aa01c92bd185b2f7bf9decc4835b5620992da312fa0b5ddaff6c48477a37844cd9a11fcd90ec9fda0b6b499b1523628570b5a81e3502aa153a85eb6e6cbf9c6afeb8f87fd2500068cc15610125d615db72a162305a03299fd055ce01a85b16562e65007740b3f9c75337c000bd2e4303c3a35eedb80c0b945c6a173f64498595216b382fb370f23d1c8efd0b4258bb5c55314d9c8f0367ae604fbfa63f7b83f80e801e7a9533e3892262323d2c596b61e61432c01b0b3f70735d2b082ee9b18578ade9ffbf8d0964603242a475646963b328a69cf9092238806478b8877c9eff3786e7064ee76055049b5a023e1a168da6af52eff40521f6141c8a41c08721b00e75732f22b2854d39572557422bbf9d220b679d2b0e4e477f413f48b5dae40abe88c6fb2783179f76cd7cfbac1d254f8ccbabec210ca9f1d119073f27dddd8af19aa744d426150ece0906695133b325df88007dd70adb5880ac43442f5d06467049956253684ae299152a75dc17f188edc4e957bb02fb08ff02a7852641f110a5d97653231a3ef15ddf0e6ef3ec1067824abd6a270f6825b2ec465c34a1adcfa976acd2489a230ea79b73b9b55715e912c930472708fb90b00b8a90335579b0bab4e38be850e977fbb213645a9b4f889b5b113e95053980bd876c4027e9edc2314e95935a0965162bb0becdae5ef563316782c1f506097de0f0d3e8a3b8c086d5edf1907f24d89dc8b6c24c24e981bb4d14a8c22a0655704cafea0d14cbe3dc10dc98487a6786bf8dd14e1fceb6f329d3163036cd0dc8ce5f342ebe2c11b3f16d55bafed5e1538687cbff9981fbf7381b6b1380e903e4593f775dc663246eebf2fa3a80113a0970104c2d98f5f431e38565d7ab3b06b3db773bc44d0266461205028e719a211a8f95651a3092eb57b64775d8cbe0093d9fc81bacce99d1aa31ea8200fa032afd0be2457fd1724a4c0cf3fb6b12af00d6c01dc3ad1992f86ad8e458402f0e64ca93eb13db4fd936877dda49f13fa40c9623b7b2bc3cd130c0db4741c20d4bdf14c481b66d6abde1376cfcec8a7ddb04285490c06aa5b0403c9d4089410ac257ffbe4a1eb05db40f00105eeeb3caeb04b9202ceea0791a729d170aef5eb8aec67da8767df529fe829cd203cff31b0008aa3b5ed7a9f2a68761972a66df0ca853cce5c274d71df882166c11a76163ca0ed8357e05dea8ab076e44618b1d93902a117f62e71c7d9b47d37cec0fe6ef820937b3c9dde1b0e1e2152cf8df679a57b99dc0c3364cec35e956cb163c82eee1028c7c106d9513f31d3bc411da6871c1c4f26af77a5944b0424f46404b0857970a09df80dbca009bb4c2f7c603f29dbfc0a68e4ba6384379fecae8cdab982981010a1bb91475ae53a5006cb432448f23f2a6f5b226b11b75580257746654fdf304569be0a70a66ab0d2938202c717cef6bd2677ee94c83621e9d8e77bc5871ec05fdc446e0459e04f5aa42290240d9f0b537d14a98aff2dbc3c7b044009766c404de7f6e4bf9865d4ccc9e6998525e3cb9643286846d4cac887553d0f73210110d45cf907ff916371e0840155ad00fc7ff949a201f3d3a548296148cb2ff8ef10b5d01d5257b46c610858fbec14e01559aa77575ff24fe84904b7a5f011b8db603cd2fb699badddb3ef473b0c6c7027075dbbae23a65367ddb81252328f8c5c3049c9d2eb3864966990a8972fbd8010ad2d40afdc733934a578ab05f900177f90547007440b998412d79dacc2d2695e345618d0b23f5076311c29df32521b4bd04c8ec660eca73d53a61d2d5eb4960902638f6e3657294e0021bf7f1d3aae9580dd58dd289049dcae5d009cced5f2ded0754ecb5569a524732c31e731043f5590b55e9ca1736da83159930161a52921a7a564ca18507e12711f7e24ccc8ca18b09cd45e13fc66df6528cc0fb78563b2e06a5698e42787963a99a204d98aa707c088016018c8ea3a86bc0d926d1450be9a2699e49248cfb796166040c85760c3a0f7faa414fe9098e0ce6f14e8773bdd0d093dfdfae0e70d7bd9adac730104bbb01a430f3c34969b338f88e77e59f8a2b2a8e6038509f236702bc93611a6d29890ba209491e0e577ef8e1209ec1f0f3943b9ebece3b16615757a4f05a73a7d3c20f4c9128fe79e17fa19c8cd13c77f8dcd9cf416439deda4334076bb317641fc408eeba69bdd6b3089140c1ff6caca950eb1471bd91da2882540b4e286a9ba72c0b1865a4ab0063167e454120abee0a99db4abbf03de6b54b177bfbae5640f1b8084db595ad0752b908244007847afc19a7146bdeeea96cb5d2ae777cb52fcb7c0f423f2a21ea8fa2cf0e37d276ff7300f9c1043cabf5389560467bc7dd1eac530d485b84607007ed6c02d2e9ba6fc34c48c1b59dce6088766cbb2f3ba14f7425052a789925ba6d3ae3139cdf202249f8e43157f9f8ca7290ed589c0abb018c49096231dcb4865d0b1ea4f2e3dc9466edcd4222d4076a664bc7358397b5ef45f40db5467e95d6db3015b5dc06e0487c19d92a769eddeee14ef7cbc296f8eb3f2b091be2964b32a32a899ae66e53502edfb9e82d8ee03457d23dbeeef13957002e0c9238ff31386010f5a561a85dc0daf600b894136afe9c863cb45d9a7df905a1082fb6be62ce1333cef91938d69031acc7143a08b0d3a5e50a9a06c19ee5f28e08365b3c5319aed7466548dee80c46901f6f730c7ad7212bd6f3cfb6015888dd0afdb2e7bde70c1164689a83bc7726bd2b6ee5f86a31514c02d854cede00bd8e0bd6ac210d49635305075f111a815cba99f7384c882c1ce2e87d0e992bd6cd730cd8d3b555bf42759969096731c415f95e0f0fdf43a48f0c756e4e792b8d9c7b01525b0717ad389c09b8742dbc802c6a2b5af68c15078bc764ac8b0f4242bd5c03a601213e5b08607c1fa532ee752e1f27162041e7d35f7db3125a93a49e46d10677e012cfcaf349f754e40cb4ca9c42fbb5d87eb49d76230c194b2d1dba7051095c4a504ba519e3e499c88010f4ac23c2e006c20ac302c1d775765fcdcf4b4a0a1bdc3984cf9f04af27f221823389888cfb868add9fc6109d1d69b4ec132b9103520940127d15ac20365e3f715b001f8130f493fa47f375974e71f3969c6fa006a06b88978c7e6c0cce0166aa98a730b7e35e151e629b7fffeb7023f3bc725d0e6f086304df2afe57676cd91a7901ca971e41dcd3965d9902b281592b03a0cd0a100e9a949b5b9e20f4ae6a21044e984f9ea7df05fdc8572e565bd9207c626404a7e08037cdeb2342090fce242918fe6434f2e13dd94f068b89f297168020c50c0e47cedfe4b11ac76fbe1bb2b6f825f9cb88332cbc92f34b04ca2e639c987d078bef9246237da28a96e5fed236640b66033bb4ebeb0fc3a6d0709c8852144c01a28fee7eb4b75340450e5f5186f4684e1337a32ef2699c6fccf25821b4dea80f151d6eb151d3e1a468037ca8aeb8bee208ec8453124df33227a699395dfb310c3ec1d6c7bb1b17c85d6ef7c98f9c65d65d183c63929af08443313032ff0c5e08577267d9a5b4f1f6641321b1b30536b0baf87a1944840150321cee6bd6106403a25bad9f31172da4b364110aa94cbc4a04f8ede3cce5d6a02d5707a29c83960cb14bcc8a2c69bfce39ea6c2276e464347bbc44af27eedd87ec7d8dda8a163f0895611bd81482fc6a701283205fe34a1d0824b0e3634e20a66f057ed010044501c300e4df8247984315a55b25e8fcd061c00c3ffb5dba1a2778ce1c62e4583b0d8bc1c46c12adb75fcbf805e93208e206f51dddb0f2a5c8766184ad196ca4cd03abdc7d989fa08f378cc9729ab9ddce039c445d07ac0f92bc578b72683ddb7e0512dda8acb4f92fc58da63412208b8d9500942d7d2e6bb8ebb197345f09b12d085b1b67041b3f51c3d12d8ad632ab000376dd0f0f94fb6a60a39ea537ac8a910c3d2fe848aa37a67b569e9b277ae290c64761661dc1ed3999ef91c3a386f0a708a14c67a66c1ff426c7e83ebe203c8fd4c31ac299752cafa21635b35f316de6056d9214fea931ce0900cd37b720d41de943e64541ea5bf49e1ce8220eef813a005b01731fc710f2cd6fd81a9e25a17d774f0abeb3717d617913643ed6fa16340ca618e3370c9ac45cf7ffd3d477ce431aea71142a33e93463f87b78154ad52c06e84be1ded9f5f1d2bb94ed2f86d3071c430049a8690bde9461f55c277442650a9ac936a473f0496dc04a1f981cdbfa98b6b3deb3c7450c1b6b0d7fc585cc810abdbe30854e5c215d97a01e6cf66b3da4e5cfbc5523b102c3703a0427c6f7b40fc668d67d0fe4f949718ba778b3dafbfb58c46023b57a4b167689db8c49124b0657d35776897b35038c3f6df9465f00a363dfd60995ecd75c915734bf85de4904609a5dd0e51fba8cbf2f43c790d28a9bc16a1d7621c4ec5451e4a185696b7b0eba4d3e9b5467c23a1cb65915f1d63fa1024a254473c9f561708a653f805af70c39d13f806fbf55c1e3f7566c4ecef8f515e4aad4948a3bb8e544337cea24190ca6bfa2761a1d6216e1921db64b6cdc415e64a86f961f2128a15645ac7d065f07e8485484d19651875ef2a819446dd8c9606cd382f3c3f59bedc569657bf7a30d7e60c63014945a1a954c15ad075b632a27f0e3f5207e8151dc6739d54d62380c812826c23fd265b8bc741ae6343323e2c962c4c7422a1f164511e96f8bbab00d596620324a01f10c95ffd2990a823eabbd714836656a5f7796bb6b2b40bd3c0017f7ecbab6af0aa387f6fbb88fd78278d7f39f8a399b03607f221c7f74a74c073c6d4ad86a89ed954b52d363b0eddc176bf11fa03c69d77f80e2b22b5f5b8d09fef7c5b3640107e39fe3d090924daea6a1eef118fde7e1478085dd1b1618d102a7ef6c43b55c96faf45c2c5285380589abb36579e55ec07dd6b2ae2a9ecfec09c049f533658dcf7fc4a18d7cec016acbb10dea9d59b3edaa3a336e4d0ed458066bb43fb1da0b2b4c077e8b940aa5177948b5a53b352fa53689e26469d36f4b079a02413e8ea55f3de5fff198cf991264f9438d2f2b140e9cd4b464e6a70fb408cd70ddabed1a9b070ca3de0e9bcc04e7348f98200645f751ed793f83dc772b0bcd1d2bf13a5c680b1db87333183e0103c2effed25d7f9587ebed8a1f7bb7ef050154dc7725d140356006277ae8b405397eb3e0f1232108cab62dd08d31306505c882c057e11c04fa2bd3ccec1e7760ad489f7d85ce6d26d5c1f676976a9a0d0ed4b02d0cbf02aad77dff8abd357d0fc48c6fb00992532e7afe78a35e3ce6970f8d7605650ac33945dcd61c723fcac5174e760bdbd0339f7210ed7f755012690e19e8e8f167c80a7ac6ed845abe4e7ab549b8d9ccd593f2da7d352747879f7d03fabf4611c519c06f975294b45744fc5ad6c04f9e2d50df34dce31b79fb0af00c1f2ccbe964b24e9badf8df5990ae4a9527d5fda501155cad9a91f35c1166de07542007b61c847ff5e16506fc30c23be6f308b5a59b26ecc326b50589088daa0ec002dceb2bb6b840cdb71ef781860ab22bbcdb8338f0e06fd13467648e59030bd15c8d908186e2d78302b401c570f51da304bb55290ef2b3bb76919e8f7b440f618e80e316f801b3aaae361cc06ea4d5bece43f17600b3e1999c2efa7b162b00108c14b178f657f73667bcdf5eaf5639a6cce5582a5d856a3c05571871890e0d44b32e9b0fbb2d58be9b8dafcddb413e1e8a011d25e95ca9d2d699ad5f27a100a52cac8702ef3bf18086ffe2a2dbdd144d88271b4b0218e8fe32fa735b6bd80c36f730c4f9a911d49bc2777693aad9bf7c57163c61232731318212ff63610c0e167a1f93e4798e8b180552fa982746dbc31d184bac6653d076d83e1d99c30d0c3edeb0913f582c2cf9e0c53dc47609f5b3c5b0818dd681164d1886b2a6a1a4031fa0136c242045a9e02d43030a5451e2b74d515432b5f41325f847b1136a3b078ad35ad36c06a2be4e3032713a53f1e413f8d1849d30e23c7e7d6f2efd4912003c5b0444e76dc0033ecf0590a8d56682cd44a7d7c7e0889975a005aa16308d0cdcb8f8ad4862393afd3f31f1d626f5f37611c6ce0d4bf68330b316abf2cae1044dd1834c71f954e76c369cc126a7ef9e19db504eaa6e598eaea84958e47a7105db0604246e1018150879b128fc4cf91b42728e21ff7032add075c0c9a2c1e9046a480f7cff722744e47c869035d05f255a09fa0111d285f1490eb0c4fa8cbf02d1b69d428c16dc40101af5b18ed3f311bb975aae3f786cd505eef519a7c4340d7d17d7a0d7df9b09831761bed0e068f27c1af50a7abeb12219a0102cb1d9a40177729e09ea95400b228223cb51933c07973bd6c3e5b6538bc8930d154e769a0d77b4626a90eebf2af17f32c7e8e022547fabf6ca215cd84b89775ec732e5790e8cf6a63a3f5a7bd0a8ea0ccd0e98dedfdcc054360adea643a1a489019e645d07cc4d0bfbe5fd676d11325d9322e77cd79253d06ae965233a2e318de306447d0a725961706fc52295b99a2d31652e97b1c7ccea02ef464d3c76e4a5caf2227d01e9632a55dfe96164e2cecb50d241317d1b0a5f875a9df31493f3918ea970f209308813ddd69aee3cb641096f65776ce430a610446bc4118dea18249cb2cdda08c2b49b08a80e66bcd70dfb22f8d00d1117240a41ff1968d2118a7fcb5457a10ea6a4773f137905ff2337f7e7f80714926de7190fd71198ea45770b8e597a5c0c3bd284e4e4477b8773a391b0d55e4b376acb4a837f458088c1724cb324f10101481a2053cfbbc8b259f58d08c6bedd089c78ccf9732cc2dbcf0cec3b262c660c049e6a6413e9af7f10eea70456a946f6c5f8b61f3724a23f4aff55df1014eb0d042471a8c93a5dd84f763a00aa341cf3ef3c0e13e1abd7d8c6aaa9d21869fd0bc67edf979b16c7ad2054dfdd9eed561d9cf39c64a38a9275253ce240886d4303a37630961eeaf308b238a2c359747c83fb94f86a61410404bee6b76418032d06214547dd0e9b4d3e5a4f2e350cbca6317209c83318fe4324d4055bbfff18d00af44b4162c15423956b3b599cc9c9c37578d961b7f3529c840bcb3c4b95d2c70604b9c5b96c4bbc2cd693407409af496000bf3831715e6dceb5e5161c4b2fda08bfb3b117edfb887956d72cefb41202cb9231265cc1aebdfc5a7a9d250bdea50c2f966f953fe09bdb2e03d79e34ecc8d50a6785b480e1c8f685d9fe113a7a3c079080771b58243c00c2aa5f280f325fb8d663a8fb6e3752cff5f624209d756c0bc6a415ba37c929f11f498a542eeee5a7d1c4ace226ed9d0a0318e14c1c9f40084fb3eab0a7b8ad4e0f03d2d4848e89c7018ea0335930d40bbf9119e5b3baf60173c4e96d5c303d2d146add5b4ced39137ded4bf19aa8dfb291fd3ac3409a5600c5371b8be179506558aa004b1d67da0651cb4944102aabfc8b773443fbf0b203f1f83339f82aa3b0bab37080ad83d1e53e77ed526d5a50bd3c35934616aa7908c1cb81949ef728c24db6cdb260fe22ca321a62e3ea74b8a29fade357ac2c420435805b04adace96e5270ce462001c286e18db428bf16e8f2948e05cb547e5701f4e03e8786f0f6f0f36d190a3ae9083dc223ce07bb8385f85daae9953343fd00f0ea1a973aa9a1a97e2b5efe202e28bb933602e6567a2f03718195dcfb7d390accf68b6ee8fbd1e9e2034e6542d33336681f5c794fa36ca7e58c39671c345d06f7d64795baafbff8e7dc622401107b50ef30f4d6f08f1e135856a8b7c89cf203b58dc75637e987190f3a3ad6d53f42458127ab6de43cb29691fc78419a6d480781b66fed8507c640754cee6486c2d55499c549fa1e5c4c5a2cb57081bd403a0ec9bf87187b88c56e9fc2202506b8320eaaa911d06a7d7b072bf2fe28fb07ca00fe4636c3899eb990b0941c31bb804d5b550a62c5d190491797f73f5a4834b40200672763de0bbd824a504bd8ecd4d9265226cd2fe1bfd8addaca32f6af99b701e0706c3bd5250417da8f4c1a1ae7c337261eee5062cbef1fc49bd24ff8f37d0e49b711c0579f173ee6b47adba360612961879b664cedfb0915de37853a8b4f0b31a9b75e13439386bb429d1208ef3ed669f6149d04f9cf43bfe3ce533593070386353fc4d4f72b58fa93a8c14f8bf83d86df1594dd97d101a3897244b3965300ac5a0091b911fe61249351e81553ae5bcfbeea382d1477270f84ce30e616290ac01438b2c8c4ddb7ec3bbe9cbe61066237405a7d88612b24fbd339075f851208971a47053d5da1d596e4c79010519365d1c31c2de5a75d6c206dd89bd85115062f8a1d538192665482554c388c4ca584b7f64ae4afa750484717b60d986b3b088beec9c58674683be755c71a27c07d0886d15938363990c0757703eae5ed180404d6da877a5208f0268cc4dea4c7df2427dc6eb940cd7bc66ae3537458d5b10c5e575b62aa5c281389b77ed43cbd807af66ccb026b1788d231d60bfa47d9eb05ffb660bd474719e9a717612b11567ff14b3f845303a5d82f4838d9e4e07c3204ff0e6584231664f735ebbfbf73a3aea49800820042384123ca62e19a3ff65b0c1c8bb96c5bfad9fca57dc0109eebb37ce716147cad139b3a1b06b005260c5108924f054253a644a01155c74e8ea6934d67b5b6cec1e3011f8b98a1ecb238fd0122a88623722aca423bb102dd6499305538be8606a3a61f40050c2a484a5b490637d08873ba10023f66346e1980434aef82420cca5cb35b6bced9d1297958d30aaa3aba24435db4dcfb0b89d2c87e19ccbc0ba1cc2a8de9a5efe34b76a09a2b0728f062c9d62a7b05789081dc17686551f6e8488aa1d7f21ebe1a03754a806e0a8f06b7dd20c37c782fda9c9b48deafb980b3ef6fc9a7d7018ae0e93a5efcb40ac8e3dabd4f9c9867c5d4e0a0b639565cf6476667f73a57cda2124ddfd6d81609cb220cfb1ea856290ba49f325a979d60deb21955b404e96fe7196c64831e4b0e45d0d1e5881262fb01cb717bd9d8c8b299b75addbdcd2ff2bea56055ce6d700bdd0d157aeb9ce9fe438f6c28dc5611b4ae99d8c07dc6726f3dc4cfeaef3746099100df9e5e69aa9800d5993d3fba19e777421573082f6d59846b4cd4a7076009900fbfd5320fb5393aa6eece03a1709e117df94b148eef5664893d7e08d6940d926e291a3aea3a0afc0475b0e68a0b20384605e8b04095a8f0207468c7c6d10f33c8d51d749bf792230c51b166241cf8348d0d82bb4ad18fd97771fe9b08db092379b67aba8c589eaa591783d0cbd1d06d307a7d9d3aadaaeb843886a8e5a8098675eb9025a323d123a9e3269e8f21488b8e07da280cbcd33cd2ee8fe78a160db4bca9217d93b29001e8ebeb6809cc723b7785fd40204c80526abba366eb540fbdee7084d001146293ef3a8a72c2f2aa0f9149f4e7b96f02844223767c9d200a0d4c70dd1ccf8c502097f034e9f5c4a4ca8bfe69184847fa69ba09b20031890bd3cdbabcb791dc2be765f7e229032f2aaa47d93b98101c82baffe086abfb6d0e014876be751e1b40937c2098eafef04b59e41b6950d35ce9bce916b548a3d109a453c8aeb7e4e344444f5b8600e7e445491b273090f8736a939240d8323cfa0f7f1a40f949803fee272d1f84408743f28ee43d079868a81db61410d9880a4d0d3f4df1d43388b6b34d324557a05cad922ed9462e374b8b9f2211afb59447b60f3007938e94302a4eed006a7ca9b5e1e17197c95f74b055034e0424570c0e270fbe2881b2a126b0bcd1ee43b21e2b4966536b11fb247f74dc1f5d03dc91064d0d68ddeac8d0f4d8bafa9423ad50c0543d9a1cf03498392c6656a3ee99d3acb1068791b30a752c534ebdce8562c488184323a8edb7d1952db4b97ae6c1c626e205b7ba6bda9627d12c08d8dd488b67dd20655800b1552f04d9b99ceec3d09cde01aa6e9401a95c3df13b46c66774f3916639ec1cfb87531a86312344aad79a5e02577ae181669b2bceb8e17bd1aacaa1dbbcd6689e4f3b442a0c7a94bea1ea2e09adea76d03fd9609ddb48d28c3f113a757f127123595c3fb58b1d5a77076d150702540bca3e91dd95b24930470cb5a08d2d5883376bedf0f07233dbfc05341f06da8981ee5bf6b63246c28404cf06539d04f7e6632e85710430de04fb8dbdde06c0aeddc5ec4b1a0e98d51fe5030db9c8cd065a406eff7f6670a55788013eb701ee6e4f07ad87cd2d942f14e84c17c17893c81ab83c255ad4b76fc530448d6f0460f2aacd0c05c38650af39460ff8196a5f6b0b477d91da7258d5c86c34d2a1074855381d9550268181a7050b16b0ffd34775caab1ace939f6037336bf1cd1b02e398bab6fa5246e620383f98b4ff9c39a87e97d75a1a62413547772f1a2ee20df581badda1824a6cb48e29c5b6d117b961e355d44195a863586473adbe3bdd001d7fe41f35f65f17e15d208458172ecb81e93603d801cb59c96dd27ca3a39b065c706f1f3cf1382d7e48f968279870d17a736688e48aadd46785b3f25c4eb90021be20be782f5b6c2deeda12782065d8e201543c649639772a69ab426fcbe10b86b8ab07c3792d61d8c3813e77df4f5b39de5c56a44722c1f95478c29daa390b6188b6315319457b578219fb505eeca69ca491318af0bf15a1c2ab7590b5490b971ba14e87e94ee915772fc355040d70f1cae0d81ea2f31fa6e68866fae83d07e39a2bde4845b74255f6946ff3f299db76798a50836ca8526ca20252664320098e6f3b15deb46c4533bbdd4f5e640c40a597077fb8791384d3c41fd8d87b8100509bc73cf05efe85d7c1d32dc3ce05e9387289b42519bb93c6f4f3119ad9af0727a972fa2c29422502253815080f0c0bbbfd536a5e8632320c688904ef6cff0b85fd7bf088925fa3ae105e284431282aa09975440227a979f1371ea14c5a5c04b63e04e6b579d0eed046aa880dbadd7349ac8d6c387da07579dfefb3244d2705fa0f0a21632e8d86eb8f499d016bb6da61efb9c7e7879a0ff9913e5db40aac02e0ee4b5583f2e262b2c0e1f05c24e8b1930ed4e04a360e273b49119efe9818025014d64da17244ebb3f8fe63474d4b71ceadabf3ce0107e1daf812924fed8d0ac0df91edec8860b38b476a4a81c55aba58c6a553305129a10e2502c33b14c400572f3a8290a75ec486ecdbb9f81281e763b5ec1fb9276969f5d005def5e11d03539ea0e96b1d9cdb798a8c6b7519df0c1a07b4cc2d0369cd236098bda2e53a07282d6e70e6a6cfee782cc5b4091294e9a743888f5d24a6c9a703e517bcb9c70f252c8c6a141cf9d23ab21cb9abe2a6ddc0bc17ab0a445a5eb0d02836510a7f045985b0c6ad3b002d83f0da447417303d7390e8f206a9532e74bac5f18ea8f70bc36d768ed1b90c9ed6cb2dae3e9610be6e53075d4eeb1c30beafbae269255309fae3def69a6bd3681281313402856ce0b4d8f3b43a9c112b7655a325bc0f9409ab2dd2742ba7fdd8e041361a1131e036ddbd4b94b98da8643ac550e41d6d1e06253e1428abaf5d0af34263f3f77623956eb4df069aeeafb8848e477531e9030bc94c21c0811c1e9c2a779c3c4f6291ff15bd1c0d55c9369bbb0a3cc354979308897f77fb10d6273ae822a2591bc35250b7bfac21cdd7ff8ace24cb7820427a011d7aa2f2f3c5f9d1f36704cef66f5edea58a09e920c2053eefe41f31f556a209a9b904c73cdd0aa129ad1ca2d04189ebee91d1e52eba148d3ec42449f2bbe90f4fafaf4658de9df5d8bdb434266509acdffce63e1d7ea85144e21d153d474d0c77ade540f36f375f67ac3330a42ff8536317bcc36d5a78fb0c391e719bdc0707d6f1d99ddb230f5370764afca838e819cffa4cecbeafc02995f4e3db06794a0b744c5c7172b2b2cfa5bd0a32e9c453d2a7d827131e6896f9676dec771cf3ca00dc3a8df5e2fedc78cfc3bfcda68355564f99f034fb1403d9a0f708240907fa016742304645543cfb333d3e9a649283ac7acb45619292ae079a38cabc4a4c5e0275c40678484f424f4f76d93b9d1d6da907482855e48e3ebcfefa469f2be98b0d3412cdadd235ea297e47c15ddb197449bf0defc9ee832f78decd80205403c70501e652c550393ae316f3f241c70bd3acd0562a720b86b0e5caca4a67f1c04a0bbc4760ce46a2b95d8c925a6bff2126c0b8b6565a15bfcef62b68c5ff0aa5190cabc19b12627620ef6f78e9f400e6049ac24293db17b6949a916debe43165c10044249173670dcec4bf9aacb3adf7d9981c116decb68a1310f1d5b3798a2902063a74105ae45a6e9b5dd853a423e045b60200003476a0adfdbc6453af4ac5f705ce7dff535a026292cb2481b2774ae90f0d05b15a8e1ca731d2bffe7d6550fe09c60f78e8436c2376d528a632939b71f96e0a908f62503b1ed31757789b13390dae0f75f19e2d73abb13d69eebdd0d74d542f25f6188d45e6053eaf6bbcf168099fc68cdae2b8bb5e58784bebf8f2cc7584676651955a0f33a949457b0ef08c0ebc7b3decd1d1d3cf0bfae0182cdb8556383ec8f129155b391330613bf6136e071f3e8d9f62cf4c3c491c882279762b1abb672653910ab3fe8963c6b1aadd2c056f25b525a2408e5044a29822daee37955fd30f9c1dfc8045c5c2d2ebd64c7e082d4b713d431cbce5fa10f7190f2c212a4dc76e6604a5774fac1df14b6af60b0c8c4d6d9a3b64baf8533c83fa070c9c9e01df2230b6000f28b61e83ec00dc250984e60703045a4623ea2af1790dfaaffde5a7731dff92158090a88d3fc9e858064a5b38ffb61c545c117676dc9f01c5691021466f76846d046e55759f51c6ff080b5e8a7a77e386198f104b04dfee10996cb37a9dac3e74204287baef3941430ba679ad6a80d91620e6d6b853b7d0c13d556550b8909a4cd340537c2a562b220e4f3c57a1006eee4c65f6b01be04f888af963d2a063dcb810e177bdb9d733ea02627695fac9e341892e3ae406155199d115a142a077258b7c2ff1d51448368d04b0701aa2a9e8ee5eb5ffa76b853f99fe396025a4db45f541787d87ecff0f760c6f548f6897d4cf054cacaee58288d4c8951da73aaabfff15b93aadb8e67f1d0c8c32749ee2007af9236be6d9cf578e38ec656fd7d70b5daed6d8bbceefb84f0ff29124600db80282b6b3a3bd6edcb1760a846b8032271250fc6a063dede4650b6d76d09f14712169f0be212de3b4992c06b1d7f38d60ff6a5e6df6a00cbc030cdc9569f85d9811c1d8a66a407758875586da5d761a565ac7d193247f92d76304aff9e693bd406edd6c3092583e4262b7ff33df39ca86f5cc2f674e411579630e186f51341a7e37efbad06c9f515aceddbceb1c3e22ffee2297fd2c3a39259b09905c785acfb4a0962175f330d008ba68bf15c3122158ec945f9b677b128d0e0c82200109789528468a772557cf1b3cff6171051cfa1616cdc7074ce753076e06185a4ec5ba7d3579b23f3fa5261dec9d3febf77cb5cb0962d892c6f1c96e9c0a8d1347d2b7c36f87d5fa363bafe16eed4244e49a881651bd4949a7f3a127470f973a599eaf51ed9337d013654e0dffc0b00ac9d3dd653de5e9a939898e78af074a1d4115fd42861d2bd87ccbf8bc8c560bb916214a1e7a5eaacad56048c22d02ffd2e92159d7b2b835e4504ca6d57a7b9975f2809f403b6a1bf7a4aff0d56d0adc79839b9bcea40835abf1492ef94d0c8158fea039a9c067a55f95c6deff0d081796d95df8e93d6a7306cf62ba98aa451377140e4eba1025ceec516d8829fa0c0432da87099b9d93e936bf5e4a5d9833411e06906485079bfedbf2ba213ce502ce5c4b9d96eb0b2825f6dcdcc0e0bbc32ecc4fae5495c22609317bfc6e1e2f09ba2d071aef401b1cfd154794cd10e51a1cdf82d0649bd06cb8d26502ef4c4f08445a4b8e015607619a84794ac6edac684f91638b92c135ca8b12cef4e6f4ab05a74a219de1fe970d041f1b71f8442519b60cb44c6c6ca4890649e46dec6c9e01980539fad33349066f8b50634012dc94be50a0ba6955e7c486445d65a6b5220c0d8bf53e0ebb2e7aada2a65aeae1115c8821b1e1fbba9f954f399aba23b84906c38823e5f1a854b0979217b4f6490694307e4058a9891783c52cd2629951720601278fd3a4405bbabdb375c4afa4d6d211c8b4713d800ca827c61a5ffc9f890fda839be48fb47a7a6907ba2c2a9e6cab17c61d9a7178d435e6ce891b2624820ab9d8bea8bdd2053d4c1d749cb6398321601219b9f8153fe2d33ede4d213c10011cfe9b2a46a73c28c40dca9953d413d09fd47d4e0d910d08b915d5e09bf3d50413c938ebdb882e6687044ece3443637cd082343f1725a14f3520158bd3e89e04568ff863849912b1c33e70ea96c4baf25eb48ffef92c5240c63eff2dc302e00b86758a2d4b0f317f5e7cb4190d79999684b7596c78bcbdf6535debeb51a5740575a352892eb2716056c4d7eb5a39a23bf2c9b2d18641182d0342c8e5f5996e09a0b8b0b76259e179dd9793fe56936a6080c1f423e01daf3dd145740d500b830cee42c426a4fff579467bcd30d9571334e2c754eb486da9152edff537f8e11a0464c181c3df2cc5c5f141cc6220cc805316346bdee3b82134ee9fe6051b9e1f06e265042cf9a86078b815174bbb15fc4911cc422c8f7ed86842843716591c360eb3a5c590599ac263dd4f90320a9eb4386dff0b22c7161c6c57ab04d4fea4060e053ddf841e228f58a9348baedf04df99b1cf8ae0a5dcae12cef1c1bfb87deb0dca7403afa9017078768a6e13515cb54501a91d513c5834c613bc8c9398dc45072e4e4131c8ae703fbc45a24628939cf6b87a979835537e4993161dc45a42150fc9ac009cc016dffd3ba6edc2f178dc393d252cceca776bb6beb941feba32f80e743bb300981a63044a737819aab6fc856dd85e493232d2531ae61605251b93004fb77389c7c1ff3848d006e2fb09fd7f49c3c76aa55122e66152881b240d5c014f63ca519c298e60c6b23ccea40b4917cc347e556d6ffe82eca158d7436a2c0f45ade260860ca1e36bd0c07f3b4daf2d3bff8885e8d40923bb63d5f80396ea00c82d3bc4b9e5aca8a20262dd12f9d2a5740bcc13e64536f25c410b691996790b6aec7a585aa7d56a8ecd85648af9c59fa422ab13395969e63bcd8ee97ed3480ad55f9453c7ef702639276cd0fe2485e711920226ca7a1abbfd8a91f40ed4bb05fd6966b8134ea745c6247af8dec34a8c126fb0f022ad722159b820641dbd2a046e4a3e7ba9bc04d0bc34830a97ee8fa0683fbcf86e2ce0c1a510e84d9ea2c50d94db22a8345f07ff78fcfa41cba1110fe922443569ae065cabf16cfb77eb6704d22218e4878adf9081fbbbdd53c999a4cbd7d9be6b6625ef441ecea7fa4f6e029a63320631f6a0e90310c944404b2c3b6d0e739833077fb1b382f246d7408d050b50088c2db1e16c6ff3a920e42b324282717ec3a098eef2e36f616d4e8a1e0a0677293727fe617761007408783881096a03a3cc8f758358b390e41efb132101c92f399fcfb49b6618986115a1df2d279c3fb5251f5469254913e3749235850c2aa6725fe8244ded709e4e399018d4002a9a76922e3a450331c0453949010b00c48668325505085445d0a3f0d2855263722d1ed6e20fd85387a136387879f30881f119dedabe303c6ad35a1b80612f83eaa32002ee7ba65ccb6420f8a7003c0866ea0caab4a082facfabdc0b676a2c1a987301ada2dc88d464181419200e2f0930247acf2fc0ecf3c0bae7d3ca5f4d8bcaae03b4775e9948653b8a885f4a72050d74e249f3803cb432131cafb32351175074ed6371d390c7e9736a14dc2bb7080ef207f13a8c0673f1baef22121c864584409d8fb91ed3d6c9a93c01f82b59085f4c21e106b5f49b6730898bc8c9befc285e899b80f9f2b4830fc3f33ebe170680677eff63b834caa9747205ada284b87001cd15d997a6e0a05344d156f06205ae78fa58d32f4dfb304f2404a920efbbf65b2c8d9d10a36e266513d942c4560bc21c7d1a18d53dfbfde023a83124e44bdd5c18ba0c558342dc3336e7cdb51e0f939c2db6a07fcae59e82b9e012dc321d4c32d447d4d8d039df0abc7f1fdf2e07574e0cc30abc913be1a5be6fce0f29f5f1d21158b08d08809a61d5ea050d700fa1131b5afd6417cbbf67d4dd6c0a69246f16baee6a4ac336b46230f6b74c060c1a40092f4968c5a804da81707cf1b523e85a34a295122d2fed724b8e1de17102d76f6d7ce64472e71bc3e14d0ff02cebd8995c465ebc62ff1d8bb45ef71fd40136cb110fdeccebb85d807d0c43a853159f416a002ac73887fa4593cc7c81bb011307611dd2101bf83c8cdb058b3436b4f35da96ff4a47fbb2f498b6b6b491b00adff18bdd5ec251dbefdc1803d9efee1519cdedc48f7cd8731c9d19e4bfe760b2807c943aaffba69575ff8cbe653e6d6b82d759e23e7eb3cbc924de47649f00ac5b9f19668960dc57bdd1bb6fd39703159ba1d4a9ec63adc8a63146b2e5dff007a8a7c61d6278ed1d2310e53e14e1314b95fff36ca1b73f2a79ad9e565862d08cf8e8eb70799513966df04f989a9df2a5423f588d1a349c50ddc02843877db05c3c21fb28877c3bca8fdfbb2f795d0252bf428d0b65843a5b8c0abcbe4bb1a021e320c2f30b04c96452dc2739d77eea9923169142e7002b21c9435290b5d9b0098a3c133de311babecd0702611c9f44398ad72357d375c1570023e2be8c23204bf295702ec7ee9dd08fb9a1772b08b48b09d448688fd76fc5e773b3f28f72301903264612214a026383c280ff8fcbbc6d738d8f26d307ca95be3b6bf9259f208947df7cc89ce6d31a522a9066dd56c48b4d7ff3a004dd6a45712476b8d1fe606ca3e01cfde291611980d3845a14911c2af7598f44013d7c4a7e30030a319550720967763433670e10f0a95f0171387fe9eb698b976f59cdb0c61bd221e817f0c490a572c62aca3f4eb348e1ed5485b1d0d2ba484e022b47d1e20ddd9c9cb6a02c3bd2b7dc729a868f91bad4fd6d929b7de99dea2b916c3d7191e7cca8247ee072f93e3856bbb24675edee7c0b4502f8d63397d5814bb39caf9b32ac6199d010200ed7a7152ba36be5c3463904e6d7169b49193ec338504ef242a7097b8f113004bed0c2141a3fd51f0da4bc2e47375f4728d603ebd78d0071f22cf99f714b10d8c91972d21d7a7eba8156704cace1c7be35988a2df0e59fb4af4f36992761c026e9e1fa4715da792a50ec4b7f093ddaa7dcb9137c2d39a5d08f35db9a0d2e307032e8d4a5c6a48bcf8c20d71ab24ddf5c41b783de6577e2f697fc4df73ab6e02ee5939c5f6e36d2c2569fedb18351ddb53453b7f3200e0cf90e018e7e7c86b071985f288bf77d0a63f1f266af1d45f1b09d416c0fd75c16d73768f373d0aff0a8c252ac38e1fcb2fc461f3621ccb1c08e1b4f69dff4e6e64c07500e210be13058809ef1bdd94bddbf9845bd7923c86cfd11c7f0595a6eb37bf2545eaf05268007efab0fb6fa1d26de02d6c193b2a833a083fcc04606c7874ad837ff89d55c001bdf3cd4f4f63154b6baeccb1056beea2887daf2a5b54452a0341c0fd3f53000780d3665ef13fc81d22ca62828f725475363e7572ce1d63b455c5e9fd10c2b60b15b17da089bcf2e0b596494e1d43bd244d75f24c48ccac9d7f70bee7754629027b0cdde85404a70ecff33ec318f69f513cef96f8f935655e3ff5250ed7794b0561099a691c50d059802e8172f61c0c2d3116254b9642c9f1bcf5dfe0a6a0f70c5e9243978b896e9bc58d28c971008fe11c1964331fc28d1566cbafcda3aa3e0cdbc8110da0a453cb6abaef1f7be797c523c14a74e0a0c535c2c61ef51e8c9405a7f40c8fe668103f9bc3d27aece635b5143f37bfc4a9ef8dd7b74c2f38642104ad51b99f2dd7323f41baf4a71fe1255ee6f9ca60b5a195e89b45f0c47bfd090aaa5fbed78f23969fdd1dbcc34c59f4d304c2923aea35362c713f0862fb8c460a2d09bdab108cd3d4abc223a198293d630a8454804b0af523468c00159e5f6b0aab306222198e5380b9a4a853a807c80fd933c76921c903ab9e5032630e79900f975e6f60a7971d59d342ddc17d13e73e3953a5cdddb064d2f7dea0b359d95b056aa18ed2dbcc77fdfa116a4820ad1ed7ad17e14226274de95ec2154b1fa2980e8104a6bbfbbdb95825cb448fedf99f9be29a8a1029df421eb8f84cff4924180aea95dfcbaf28c89f92c2bb2b2fb20c277b67f6031f16174d123b03512b05b908543fa3a751cfcfb21eea48ed604da29d942b5faf48865f73e280bdac18ce8b017f6482ca32b992749b51f8aae5841f9ebc1c68a442cad48e3a10369f47a32c076edc9c44d6c8e5d8e03357b51afdf307e48ba910595ae04f4f93083fd53ee5073189bf99cc5ee02763c0b5b8bd254e080aaf868d47fea612d986b2575ee5490b05301af5892515bb928eabb8fc4046e542e36bd462f422874cdce86df540cf0f26b28b4e1d68dc6826d2f5b644b1befefc3042ee3a89d71b330868a9b29b5908b55a5492f81b70ad4bdb93bd3cdb468de14b7ee4faf9470e7fb66f365da6d10c0ad44a1ac8945d30e4e913a30ea8cedea3eaefada23d22e0737767d7ef8bc3035a0bdc531af795e21b4d5c35c99c266f50af05a54824f1ae779f022632cb3a0601a899b03cd51cf41108212b62b613fa146117e2e0483998ed4c3ea10e9c6d0c5fde20c010b987f2baac09ca68a2655fd1e7d44cd25b2cc5a486d9f9ef48e200bef76b3e4fb2e470cc8842b08f35636cb571928a9c924415c0b9210fcf25d50f6df75ac2a7b7af043090ada5e518f04c9ed2c3fefaddfe0709797506aec20e0a51a4d2c70486d6bd98696bb8f6d6c6173a1e0b1333ae2b9b7a445e84ff100c068a7923d0bdfae6039e086589bf13c553b17953d9707889e56fede2130cd47604e6cd37ff2db0f13e1c2474c80bfa13aee18dc618428d21704ce23841ee2ff20c29d80d5bee5b92e3a628998ba3ea3b4dffd53ea6c90813df46a80a101c798b0ddfc0d7b23920fe752142c5dcfdc4d2be068900a732fb6acce6e77559af1afe08c021a144515725cc4f44e34f9c32648eabf14c17b1354c8aa3f05dba71f0a30d2409493e28f20145231c3b1405adee3245a8f66737fe533ee7a6c6063bd3e20c4724e4a1d10dd8235a9fc6d23dccedec8d59ed418a2d943ccc98dec2f99f3b069107d34207d5651518347f01debd88b05cc7bcb737def6eb7422400781b06c0065a9927c89214bae074a272ad1b027e626cd1a95dd70e05db05d0c836515f90ac9afe9b0bc838b28338ea3960e77102f67216dc3462ddb99f224ab81aeb455006dbcba915feafd341eb98cbdbe86ec242a665e6977e299097c6775ca9f24fe0bcf9501b34e3d7ae2d890d8cdc31fe9181da5d4809d664b353c6e8883a149390ccd9b81624a3c2b65da87d9eb77dca47df3f330eb0be9732e7d313d2f3b46970a762120b13fad7de76a757a082ea723590df31c39f2a70386abba49a5f5f29a0301a8bd9883895ae85a2026d27a387bef9ac46a8078f31a1ccbdc99a16aec23085b1fefd8f6d5d4d8ce9b3c498c3a578167096da993c3fff5c080bf0516f53300954e67beef1cb3f9b990ed4bcdb36d46c875e067ecea1cc7638375b28280f00fac9bd56f9738f4794183c5922c4557cb85ce110731b591dd71b9525e4b02650792bc4071f0063c776104b268cb38aadcaf494f6a5a95293ff2c03bba1fa50401b79bacad934ec4cc0297d2eb0fbdc221bfd13fa2e67d2dd5bf27b3af7885aa0d1eb550c51a08c21981cab8c0c0266f8079af039fa1b87db1b7087798e96cbd09de585d11802067309a33e985cf317bd0f86578a3059da46567141b28d63adc0027cf945547853780c460e8e469a49b2a4720fd4a2d19811a9e0004d38d4d6302e2cf8da33736d4f2f1d15b08be5d7a56ebce0bfa5baf72fcac55349bd157590238c5598496d8839aabf26aaaa968bf43843d7cc4b8b342df151958af19ed460499e580bcaebe3ce6bcda74c1ce0a191a02bbb736d7fe10f84258cb9cd5a54700ac4754a1a351f751b31447d7e7c5b39824e614744e673bbbe12ef3b12bcaea0b095a65bc907f38fce38b5526755ccaba70258623b91ee71720a0c76fe5a6bd0496f97ff3ccf1bdee8090516f72fd629279984757087c87962bb1640f02683f0025431742e2524d3b423407c7c8b3321671655778494f37ba00d8d40818f2fe05 false +check_ring_signature 815b720e30a2b9f4d65311b6bb6d672b5af5a126c25e0e38698ae027c1af2798 699bcaac5591423a57281ead994eaee9cd1534da2a3bb6208436882c2b2d0189 205 46808ec5a227c8d015b4035a813159870b7d3dc651a30cc55faf947783c87723 6fc7eb6344092d826c8d4f9306eaa1824824dd57e4f888fd9f0290151ad2d037 ec00c893df1c87342f7d8308663c4b8f02e94f0561cf71271373f3d74d9bacfe eeb4091e2728060e1dd34c91b4ad7249745c28b0b4d23a39358c3d33e41278a2 e9a873264fafedbca8a2b43c92033140b93be348a10140158a658b966a35e553 287121ad61f3e52b65452fc98a9ef161a078edd7601c3b9f63e07d955ba0cd63 e924117f9e5b59748f3fe2286fd00ea77dfc8ea3d033712251abb92a72a7c551 a7b6fc83eec697f7233c978c57590a53a8d3f6fca9a301216220c55d3f5f8231 e7ae8b4a3cdf61a84d05c39b89ed1353adbf1e684b57147122ed78065648f5a6 1d11cf44cbbdde944fb639c1272f13f404c38af7438866450a4f37d1aea88c94 1d0b64ccf4d6e29dcc2cfa8cdb5ff68ca79a4decb87329547564b1beb8b96af2 7c573af79d3e2644daa342069a423a3cf44583a8ba0eaccb06202e93409dab03 07f16e17c90dd595b2c6a6b6c24128520ae7244a2a507a8db7aaa35c588ad21b 731f811f3682609d44c05a6d50c5ea7133698dd4e9c3b08741fda59925da35a9 3b10538048df911cc9634e8d7f60bdd352f750e00cd7667f81a313091a0781a1 6e4edb573ac145dedfb1766d7a11dbed32ea7aa38950118f5b9b46dd2f255572 7259babad21ff92c73bc1ea7e217833be0ef15ad4de089ac77cd5fdb9cd5f79c c9302f11090cd13ba9eab1c94fa1d78fbf8ace35e679e672c08949b0845204cc bbb2578e973a664aeff6bd56dc1f0224fa819ea7ab027fce0a4d17d6b96cac7a 8fd7e1f1bf0a3ecd5cbbd8ab6a05d2abe1d1b27bcdf947cf4064a7fc248dc15c aa7ceaa4db59bef3d313b2cf80f4195a922dd46d6e22d507fd6db520cacb204e b13dbdf5a067125e9771de8e836cb964e9bcdc32973fa37f9aec158637d54ed9 a9f17f3cfd2d5d871730efedd440b9d287e7f162fe4255b4b5baabc9666b348a 10e22e22d0b469d1925069a07c813f592e994fbb0989e16f61cbf23346dae9fd be02255b84b8c55cd0ed81086b882a8060b92e355056af5b2fcabfc48bf4137a eb0fcf8483a78cc523be573ea8cb68afe8ee2bf3e2b3ea508d9ec67ff547826f 54edeab2e1975fd2a9343480afc98c1bb7834e39ab789eef9eb2f073a107d930 6a7af23a628dee704fdb7189557daaa881738fdf2ecc89f5b072873f7d32c5f1 8b2bc3b90ff93f3977cf5cb66fc2d430a45b95ad19ae0d0031d12c8fc8a481cb 940831c12faada68b135466f8712b84d623a2ff170ace9ea1ba9252605fe487b 5136520fdd364fa57b127f63da13f49fd40035c28c5767884ed5b8d089fac330 7eb62ea81d78c5e4d6b68079dc39be0614595506ac40c9585f44d40825bb0d4b b3d9e482d8077916ed8b87b41a757069183286d14d4b9ce74d9220b6e92ac6f3 fa62b11bca3f541c0f4eb7932c162f03d64a72c3e1b27d50565a7e5f757e9c9f c77302c2f0d2b5d7f738a93ee9a2790885c2ec3e631e1cc3ae0612a4c17b0ec7 5ad961e28f958a5694dba87df743fc9f6a7f8fed873e23cf9f935efe1f4d0855 9c95f178f9ff9c35e3ca19897168c780f114424f4573a5cab7413254d37eca7b df6953518785c3f48c2830167a23ed95f742a366c8bcf68de486b5039ed110af 82076f19801afe3e18cd7a406922524ba1fa5bcf5cfb6074b0c95f93e37fcbac a678b6b65ccf986735971bf27ce1e536dd5f39cb74834daad958d96932a102e0 1d4bb313baba6402f5c66c6e4662efd520c5a983ae7d491e8eee4a1f2c57a1be 2335aed19b0551c7b993dc8eec42634f71ddc31bd68b904f4a0c6518a93f8855 6ed9449172747245aa600c527d8fd9d7287f322afa636bdb5b6cf9915151deb5 4fbd5752f7eb520d5761c3d2c2bfe3a10d92032ab176576969bea00d5b24c513 e816de98070b724769ee3467dd7de60f87d970480eec91b961e1cbadb3a1c109 08fd224503daa065526f8b8bda38f973b4c6146b0ab2e686ecd2044be9fa883d 831e174be4fc84c83ecf9d8fab5822577459591720018447246bf07f48c0fe00 2de4d18b4b19079c8fefca78dfceec1fcae669336d842c0b5d0554d8f81f1a49 92754f30288c4078e0020bee59185dd508d8df664723b01ca03edb43312926dc a0758055c92f5c91dd6047785f7edf4a4159bff2307674e7c7b500c2ba4c21ad f398fb2b38b53288e28078b14ee246b818fdbbf63816eeded3a378a395ca614e 1a31258a9459421a753adf536b87888b1e4c71b9f21d1ab01fcfcddc5c4e6752 e3a4a803637c27003900415317303db3e29fb17f4dae9d48ffb57a0d0ceb7223 e21e8bdc894345225e508d3160f9bc26eca3d8edc6261d40b19a0338a9795673 67bb9474af323d12fc694948521d12c64771795572b8abac82e516c155246e44 255db91b7109966676cc451f1977c6f8a483972a5b24f061138c1394b10a4c74 fc5b0840407057bb31570443d418fe4cec25a236070983d3da3d6872ca56423a 2e5a316fd835809c883e20ba5892a4256a1339e44f25edc32261ca3213e51142 6ce92d6dba2d5b5dc6dc762d38706f6ad60cd40a3bc6875940d3408dcb01055b 171b4ef197ada10672c0df9b9bf0912a67aa7af717f9be19ac6c0d4204f390e7 09d9a1c7c5c1fe55d1f91077eb7f75eed203f3dd4e7cc6059736dbc5e0a2ed51 c8e2d6e39a20a6caaf54d326ebdcf27c7376994e62a111f0a32a635446d32aa1 082e6484ae60a56c5d06e444155776e6dd9fd8ab6bb5c9f5992e0470da68faf7 976f96686e2ff17407bf44ffc73ef7834fe0bd9cdf2089c4023f0ed2cc4d5baf dfcb419baa9122897327b3aea513df8c904fb67dc7c2b9a7eac11d515146bb09 10cf22c6425019233c39b4b96c83e83e7db2a04f23ccabfc3abc5484e17f4756 806d75b7382c8ffa1c4fbcf6283ecef4baabc232126939f95a95b0ee53c76426 1b3aeb03d186182144786831c5e8559bf51cc4f7f68dc8787ca538f090d9c89c dd21462e2887a5d3e0f6d07c01da42e876fb12d5cb301aa4087ae75ee314405b 19f282c85f12b1b84c6696245bec23da9ae81a4870b0554df99d6f0faa949115 5e929336cf37ab216ccc34d60288e5a1aabaa744318ce1c1aea9bb15d070bd45 ce0869f7568ab5f1e81dc096e2953a4402114d29908a4ed54419c68838ba61f6 53ec327528480ec375c874c8442d8c1672d7ddffc251398101ed3f854aea13d4 aae0b5646c5dffc01feb755bdc14e59d4079f249d4d272e56525009af43d57ff dbf338006402b1effe63b7ddaa9d673f4ce74af6879dcadd713abcdc16559718 8fc30b518f31eb6aef026ef5e0a44a1866a00ed55dc048317587a6b8e6f3b397 dd0f3fcbc73a15a13936988ef48be9c85f8fa9cd2cc1f584659cf98f67f334b7 ec7127db6a7b1ac8e202e6ef089bd92218e276160b407eb465ffbc619a3c8e71 ca31c4317bcd8501959569f0c2c0a80fe742cfb20529be53665d0966cc99e507 f4eeda2d12adb875f3c9fd52958dc0379f96fb0c08a057730fd8ea096da4c6ca 80247f453bd529c6131470c196f72631d43a2e313026316d4009ec246c40ad0e dbfc555385782e848bee5cb134b526fdacf2b39a8e326a3ea51c7cc73bbd3b37 a639285115132fb18ba3210159e9035125f242ab49813466eb81b4e2f8ce0fa3 5bf6cc8b5f734feaa623ae61037978ab1bc8047eaa0e687d32571240c7fab80a 9bcf979823e5699230ef19559b8367734dda93ff71f3fca1461a834d37718e2d 3ae86ab0d3d164001ebaf487ef975b4c098bf6b6b3b3adb8293932156fe5a4c9 6db12e3eebff9f5e13fda6ada134ca023058a969c04fe76ec0aae3b0e4f2b037 1f97a4677a536b1b1082d0ca696beab5567bd09b205459ba4ad1961a0aed5078 38dae5a0ce396195993eb6b0d35b80ee8a5670c3f6f724e64530d9d08bddad53 3f3aae23b505f26f30494345e2afee45609cb6b3240c2faf62cbf47644fc27fe 67665cbf9a9dd35a5d76bd4651a0d64a93462c3c022fbb29c40fd27b45eab0e3 f642f57c185a760166d6ccee094ad8d01f70eb15963fa2b9f66540415023795d d8ba51566915b3d8e9a770cd8f2ec3bdedcc91bc0c3a5eaecc5cb6eac22c88bd b327b96649ef01a5b8ce4104f7bec562e1517eb6b6282639ba53ba7c3292c0da 79469b1fb33c93f1f1dc7d91519ebab274636a0c8d9e41e0e08d591fa181dab5 81d2088215fa5aeb35e83b9d3c458a5402d51661464113367bfa8ad8234ac8dc 10211d3287acaf011f7ee9d35e476f1c6e28c3b0dd2752f3287eace7570c95fa 8159be9d75501ab3d994887aa060c53c1c91145ee8158e0335418b62bf675b77 6fb17f7fbf97731187f759a980b92592380ac58bb5019efd40029c79b43b7845 ddc6701e76692f4eb636405954e13304dcdab74028fe32e61e6649419d99bb1d e9eb174c12bfec667be6d7eb9416465d6202f8f489d240a85fd62f3f66f1e532 c9ab488a21672ff99551414ce6953589e804322f5eba918ffb039f960b21aafb f325189b9086f64b8ef89d6fc62674caf3c9b692e6da5d9ba065798bee4c44b8 80d6b8693fca4a5bc6d0e5f44eb92da4de3cbbc16b5e53e448b788f632372b94 711972ac236de0ff0937686d84bb4226ec881baae8c6041394b1fcef67993c31 51723941125cc69315ca3d089596e62b1a77bec34c88ce7e7e8490b80a761c7e 2680fd10a5b284817c6631b0a92e8dfef1bfc1e2607035c92292aaa954c8dc3e 4f45d0e470a0b7ae0904503de80f9dfa5fc73573ac1245ebef069b3b3c6b3eff affbe4c3ba86ea4ce038173732bf9d9eea0bbd3a0b8355ea99f68e00526d79ae b6eff7f006cf5bfdf648f552ef3b32b38e25c7ca6a02b880ad759a0ad7678119 3283ece2b0b31ffd055ebd19f3cd134f988898262e350153c9df0393ba125447 5f8801115090e474d4fe689d23a42677558b0f0ae9bdf4d6dd3b1639c5a919a4 28b436fe6b7369314fdde0fcb93b4580bba512149461da74de1bd0362d3a8eed 2885b485dab965223a90a871e6b83c42dcd8746a33bc65b54164c770b8f914ce 132c69731036180d861fbd4e6516ac31e3f2bec61904740707355b3aae92e2ab b2a1622fe1f85bcda80cc4bfa528bfd6db35d40e8b08390f66a8a279229905ac 3d890f7582c92daf96445bc48de201ead6063ae53317cccd67b69daf280e4545 6b5b6a8bb2c09fca33b101704a905a411f9b81a204303dda7fd8ae8d92537477 5e714ff2efeb7354c4e90f20eb650b6b6fb7ec8b391795108e914db7be098ac3 e23aa1da187c1dc4d12c9360b256460f3b8b9ebbced25ee879c138ee2335369b ab169f46ba4b95fb3ed82555de59aea4558d3987f63b6468671883c559749d04 5d0fa4a41a328b6edcc8a24114e9f257b7c64c69ac8e8bc49945ad149e865575 df978a8c9def283f05562c6f1d7b9d4ecb9ce3965514e882e841399f8c9d8a46 6b6272d457d5a7e9d1380923f962210d58ce5bb6dad5c1f2df81d5fdf1395edd 47a27f4dd543d8dc11e875efc247cab3e5715f66315d0df4fd793cfb4940c0c3 6c22598e7e00b4fafa03204bc4913e5b60f107c84e4e54aec5c3a3ed975d2b75 d844f738209b244a13e31f45c7c0bfcebb55d573c9c8282cf78eb04973a4ebd3 17b492f989a87249c88531372d04717e1d79b7c9667abcf0d4a88fa9ada0fd6d 8323cf1bdec1115033c934c638c9a804723624bbd195f8bf8f454abccf4d594c b862b97f0e446df8009f02171376c2fd5b6deb51f9a22188001de480917fb60a e79be92b2ba2a6d3cad71128a46e119b770fefa8821aa38ebe71f8ad15ea3078 e1f2c6734df6d5e0ee37c152be94590e7bfe85d03cadc555edbe54d4bce9ccbb 3412e05d7d63d57bc81ffb9dbc4fd3691e3b067b81d9f867d4893cf67c836b6b 24857d0b384a3068dd1f032e57fc1e1cfaf9892543a6fa547f546e62b2669d47 2a0ff2e167c8ade44d917a25a552fe3fff99962ef799e740314deafbab5fd1bd 3930420ee3f48099833d8b524f2cff6c03fa399d40246952ce0276e3ee35d00a 9b2f0154232b8125d39bbfab54e3ca5d1a3336715ecd8b877210928fa38cba37 05ca49816fa72e0e7c8885e46e207a1e18e9efe7d6bf4416d2dc6c84d33a5b7d bd7004bc8aee3931de88ac3021b36266a770749ff6741085ca275679a741e4aa f6bb17cabe34b119b383f1a9e1acf236fdc40f8444093334b81a7e14b640a806 a89628f7319ffcc6a9f222d95dde435ff756e2cc4bfee6f0c79c9b67ac4a387b 721867dbeacd2e198f1145d4bb08417f385f454b6bfa0a3f3db7ae23a9dbcddb 4f655d1dc048bc04bfee0d815e951655b2ea9d721048ebb6307ef20c3e631b4d 506903dda715eda5ceadb49254bd5ea12e3c12cfd0e0d17853284eb0cc3dbab8 f6b7dea23931ee8563427087d7b80a9bb131b64a142c6b5ebf9496c58a51f323 14f436ea4e73affcf8d73d145ab9963278500e150ef5f84d397a11b18c96cf7c ea24d2354384f5af79406074c081cb24809eed993ee876aff5b3a2bd2fdb012d 7e9d040b00ef8d48372175d77d7ffeba5e88d421fce38c079ec0793a20c14b05 9972f4f9d905135542a9728d1fe296e7c3746644bd5e5467238804361972d9c2 9c9d1cd62981bbce9ffba90179955d24c71fd082edff0c1d93745c57a6cfb992 a8bfcf0fc417b64b8d7aaa4b856fe0dce5ee21b0293d6dd8c1d236a7a43c113d 35768982866a6644fe2709c8982e4966d2f449c658eac7979f734a93153be986 780f7501b68b209e912e6f2ae30e4eb9280e64e5111760b859a1ab89b694d5df 9cb9827ed9f9e83d49b247e9b8d1da4268c65015959a8918ae00317103553366 0e9c988af13e83e0f6ce98dd23f353538d2d283886fcb8e4c557bb104f6b2919 1b91b5a62c8d47e81309193f70d7e9f2b3a185a75d833b688e79f872eaaf9cfa 0af22fd240cf984cb6a6ad8f9d0f261a5d9399771aac06b99a5dd7f05077a80a ce3ae30e78b356ff9a57293dc2843ea91c70bbb328fc11d90dace9736c716ef4 33722a995cab3ad1aa6578fc3376756f24f8d59226ba5aad364ca37b07fdbbcb a75b09e327bec8dbbd2ffa8c8aecc07289c289509aae955f0787773c23c1885c 8a73f70810e06e1fb198bf9d026503fcbc5158a765211b767865b71332874b93 9f6d4150c879c81dcef532edd0fdf5e810e486c78633f7742c99df2b79a0dc70 52228da290689546d0676bd08f893c6df5ed30e16aebaa516eb82cec84ce1d88 3dc74ae475717f4cd62e890ebb556232fd33661485df45ad74ef0e89d4f4a44c 3e171a1bd80753c1b0d2c234a5b69045cacc07fa419d8396dfa69c11b103a81f 2edf6cb0d1c8272214040c4852e8e10f9eddc99b7b011061b77804584fa6d3f9 8093e314fcd49b6f25219fda55c607bcd8e1f88ed19e9d811742df2e70c284b3 f739bab3a11220704dc7c8795ad63c850a5cf55bb33c72891ee3bfe6afbc15d4 c15baead2a1f83a340d46203ad260e1a87fc20176d0a1c4056ca171de1aeca51 1ac8abbefbc032c20efd3286400fc473e8b21ed2e49078a33583557170e63a84 07611d1f5efe75a4c35e341de964da1118ce76d85782f3f50f3cc4a7ff8d8dcb 99af20545d0264d534abe5a9189459888cd26fdf7058e9417184bad2370d85fa ddc001df4d13a9b1f268252cae70329c17bc4df6c4952d8b5535cdec2e5520bb 549b01037203ebf11027bb31ae0efaff0c3713788bea972c3a9aa31797dcdba4 dcb68560e39f521e31e65e329a291610d2f1f567e65eb8613f5e897e1efcd1c7 ba6ed9680e309eaebfde0e490249bec699a8ba2771ca53d38e219fa551db1f1f 34fb8b2ec25ffa77b9def10a0af009744c7fbc8aefd243f24d3711daba4ee22e 79304b09b19b892810d56d43794bbe3241505cc32358f28b260fac5bb3e11318 64ff1cf0dafa8b89776c63a8d3fc1cfdac00dfcac14f290e14e95d188d4a53ff 644e2bce0ad2ad36d077c262707d81ba6f4e745ad8e03411a66ee7f01fb9e4bd a504227ebaf3d3487e1459359ebf362edb1be9e0aa7594e73deabcd4953eef9d 59edc4d468d712c11cb5b0069b177ecf3530a5df18b887e5a7f0821faefb6d3d d7c93756a89f98aecfda6018d6fd3ad45723fb8b3b647c443b6c9a6bbed81faf 0d32356cf6add403e52c75019e1ea690d0f8f586a8d30b04bcfe28b0515e4343 6d4e5f78c19c7e9e7c54d368a977679bea1db72bbc7fae3e9bc152518800ac2e 2fd2b1631d60971a10847852ddc37ea589e28df71ff6a9b76f5f31d4c1564475 bae0c7f7a10ab733b4753a434e99d3043a28b929cea3839e7ca85772684e36fb df79b5c089dd4a1c712220b3d4f07dbbe239a092e6c7a204901a6cdd481c9108 e58846ee03b5a54146dcac1e238e463757a33b6c3ea3ff2413e6de74c5541f4b 73184497ede96e2219033a1083a12b2318fe828f1c16df9be28ff7a5b1b67617 59774b666277b0f084a4f1c04663217f573993a92e9347fc0772b964e646b100 bae766390553f927e94272af636c4155b4e48a5fe5086871762eabb0cab63cfa 2b9f411f9eb0cbc5deca46950fdec9022815146456939b5145c3234ef9341fa6 060c76002aff88cc25028c05c5dd7152cd7a98536234ffae254af7e7b6edf963 8a003cadeec233b4c01c4cf79e84b951965e88690d42a9533a51d41883b2b335 00f46ba4fc650381ff52a9fae171dfc7d6eaa939ec7dd4307fb7ef1589cc6d5a 2b3f660575a01dab0f93e7e37990bc38d52c44c72f56aec0e09cb3b1dc96319b 1480415350acc1f59c2f4e9f353953f6d11bf3a4c796d2fdc56cf6670b830e08 de3b80528eadff32ce28b3d6ce1477b736db0280e6febaef18669fe05f99757f a40d028042ecf39af7edb0c9602c45eb09e35a2c45b5a02010660ea319672014 4fb1cc969fe786fca42aeb5a7ed34eb5343ee4670432014ea2d6dcb6f45bb971 70c828f79097487d32621addee3444bef675e490a4fb4a9d1c97d51d56f0be66 f2b93d03a8e22b8d8e49f49c6638c38f1b71830b5a4fe80e5e101cb7505ecc3e 449d02c3f52a8f9b2c189c19f39d5b6ec96c73dde281faf766ec1da62d793fd1 674c8fdddbee6f734564dec41da1e7468d89b256e7ff3a4260ceaef85cc76726 0ef7515be01695e4d8e3a55ef113b014abec6fb955414f17e293c252c0eea105485a7921225db68566d2e220f0f629118b0d46fd9d4c7ad1bd813c2a511e230a10e020c50b792e11fba154364f5d59d62fc990de915432811b08cd44e8c43b0b12545bbf0781d57e0270d046df1f1764aefb6a588abf3185b63c8949b8a4170bcfb2b952fcd31d90e6c61cc6a78edbbcb780b6f1060bcbb397030a357d01220b87d29595f64b8348051f645a8ee622bfeca3f148f75689fd622d0c7d9297ad01fba115f4270ede664a6c09bd1d538b58b173aa7f360ef8f125df6172dca266030e57a4f4a61667f6ce8c742a77650a2952cf89201f9e4421346a3b8422ec0a017b025bc173bf2ad697fd2447e0065a588326aab2601846e0c9904619e498760b57e7f597fe736245edb055bbb51ad98d856e8fa88517aaef510a447b7868f30facc85917e7f0eca8efc17044b6605358613d371356af99925300e9fcc8479a0383df691df33bde21cf7975a7626d610607ad5abe3394988a83200fcff9fae60d18a3121a2982bcd6d15e51b9f7944af0f8951c7e37e7b346d4e3000cc9e3e000174cdbfbe2cce9e8fc05159e4b4eb7e483c2fcef35a12eb51b3936ccbde0ec0e5df309801decdabf1acde95a8be92b7b83746df8fda2ddbd1f57e7b388d3580790d1a98b2050429d93c1fc2c18a3a5846187f6b56f99e7e3583f8c642d54480aa26e1d3d5ae44fad2de89d6589ef61f6a94bea22befa55cd18174ba483616d0ea64c7d0f5cf9d417bf32fe7367b11f2c1d06e52e74b1781e0483d31d9b460103a045792e71ac080628c99ac19399ec7262dfce757cb7b76e950245a804afee03e0b8ea882ffc2e8d730490d1420620754259162331c061b868bfd727c5e98f0e41877600204a9076bfa0ba6dbe9cd683482861f97d73a146459cc8ec67f1c606301c3ba95ac9e80b63ed6d1a63ed59366c5e2543a7949a40fc5ca46a1306350adc93557e017f2f09bb42319c937e194e4347b40256bf02f51371ce7d61563203feb69232e914585655caef86aad504c7d04e49eaa2c85021d8097271e4d6d8071e2697303e259db2901ff0931ca7bb02c025dbe403a55d050235b84d94f06a0f48b7b6f79e6ee03810e5dfe8f7016434919c90c7a6ca47da03de1cd2357bc307a7242f987e9d64292bf198d47794c340f9ed1e5a9a619a2dc81b009b309e4e027b0fc86c452f260f2f6ec98ac85528e19fbbeb3c69aa719f4a7d33dabab33800e9bfcf0790f00fa016d66b958380077dadf7762b01e517a9b78a99deff55e60e045322824b1e5085825b2e765666be3fe6979e9c407e62fd5ab4222cba4eb30a078b6a63a99999dfb3b02fea09d795c9e33a8dd71bf63f8e947eea09c61dc405f8aa2b9bd98cc106616fb29b7af9f2638c4c756747870ab2d468cd6d54ac340ab07b1c9ad23921f7b675207b22b0c31b50cc3eb53660062ea6eac9dad361ae0fe791ff8c42304b4c653de4b8d80a9b2f9e7ecc8f19469afa2a3f84d5ba5f2d05f371d563a30e4a81b0a157bf4bf7eb4203978087978c16f3947666878deb950e28634b82bfdc2f50c5d07f3fd8bb7a92cc2d83f4dd2b680bdd57eb7bd6e9800db56ce983aeeeab32877220e1d3d7131bb902217a1a4d4c5c335aad26f770d503304e85d161d5a54fbe7783577f068b14695b956878561da107bab8db83b0040026ebf71b8df7553aef47a94dbdb06f7bb4e9fd0e22dc1a00823e780a8bdd6303a1912c30644e5f3da0f5659ccf05715af89243b31481a5dfc350a797364fb808078b9be7aad704924598559eaa69ef36ab48f1652fdef0375604f1f51bec190fd539c9eace1dd579c43f9561a6571903de1efc52f041a3b1529aa71340762f042bae9bc190a8572d78c05a8d5ac352eabafe85c8ef2938bb55fa6cf5fbfc8e0053f165ee378084b528f21c5c7d9ac49cc058f9573ed43e9ac59ad1654643bb06127d5eff55957297bbb4a0d59adfd85fe3549a7a259f845307ab34508ea80e09a63082f2744283a989eb4ce9d109c55d2b4c94834035af01eccbd9a7e37cad07b882a01f95e630e6447a87132ec1d3cd9a087ce8a58bf3cb93efff26f1df520d426a7db178182a66a40ba3b1abf0fa277ad9a8ec6ae0d3a8812f7b02250ece09618e08d5cdc481e630754ee87a4c60cf20b5267f3d286c651cce208a62377e0e663c474c52088d6b63b2e70ef281c5b0e2d5c2ca0360aade047579426c0acb09e6f130556449ad95a6e13623497fbfc7a70d9b84b5a086d3d6a2d7e1f6e640006a74b2ac9432f1e56055cf5392a2c0fe3e1ed2447ade684e11354ed3b9b5e5044b5226db7472e4c2b570efa1d3d772a029351c276aa2ab07d1cb8d601271d605e475628c8d82a1582f5dc811cc65ff19352e37c70fba804efbd3a0b3f07c1c088ea4c8a8018e53aa7c98448abf348a95c3724db524692bfa57c76f01c418eb00874bbbb398db874a1f6df6f193f9c4b35ddf5818b5bc6a7da13977c626e6960b37f3942f7ff9b6d0d98bb27019f87812285c12811021c2eec82323880422500881ae465f8d10ceb22392cd57992fa8afd87c3a551301bc5d26df47f05687a203f124080937c41318803934ea0ea57f5d10958c4c9a3f907b6799bed75ddf6a089177425189d2bc04ea265f557876cb5339ba8dc7e38bde3478c5470819982a06934fec17fdfabf64cbd450c405c22d4529d864ab26650881b420414e4926680d182c720c1efb504ef69e1609bc98786a6c2cf143a05dc15da744a99c558d9c0d841decf491024de6673614d58cfdaddfb5ff41dc9af691815841d5312ddeea08729ab96e6774feb8553a5f7ddf2c062777a8c306df33aff6007dbdfba26ef705b289060342986538a65bc1ed452005c69baed366d03b9c41c10561e84a0fe4028d92a2305f5e9ecff27e69ecf71b3ded6111a084f9739a45dea35d5e83aa670fab2d8f9f365c582cfe228dcfe46ba6ad8026cf8cb9d4932a430dafc3c037c70108302c325f1f0e91f54bdc9d7da4fafba488cb4f73033efc433c96017fd6400a10965c2d2192d07aa4dbb990adc9a957bbca684dc87a41eb995db1156073630b72f5378d13d5d639a3a9e0986a03bd9de4661a19e090c6839dbee64d6e6dfd0dc0c1299c5bd42bf37673b08664b65788d28f150c0746717db51904e085cd210b8f355196e3a8021566fd8a2f41e7e6ca864784f9bb1b67d94a7f9937b98b6a0cc47635ca249778a12a83a59d2061fcc2b87c19b0d6b7a5fe8d43a60027cf790eff3e20b4aeee59de57e3a019a28a836e21032f0a9227c319b9dd3c09874b4a0958755624252997fb0ab391ede7fea3f1e3592c2cd79e4fe3af9751fad2b7ea0024979732a9fd6ee523e8ac395ccb4c7e3e7277a5e18f9e4b9b7b1e1ceeafea0ffcd02ff8d2be2139ebfdb32624eecb50b1752b0fe70c7775980dc1885fa3da00801281345829981e1407f5b639c4da17957c1654e499fe8a0ea64b9a3843140f56600ae9f92f4d2a3c0c76aa5bc9463f7dfd65a376281349e65c2885aac80b037a3361a6b6db0579c92b7f08a416279c78f782ce0af9ff0eae7d3a3dac360d0a6e8826737d7649eba8994cdab9e6492bce7250a310e4d18d0ebccb271efc690fd4c0a5805d544019fd315a72b8a55cf92e9896abdfad0322d8f03f192991af02170a167e6bfd0aae14c2313204f691ef2bcdd429de87d40ff0352ff7ad268605af7f7bf6c9329fc37903f013ed1634a6ddd72c76a954a646d933be2f349e4401cce1901c1496c4e3e021d46810f818cdfbd0b77e634a1c4e90184766d9ffa601b2043ff779d602346a61a38f6039500111da36d91742cf22625427d2321c1e052a477c1ca4848a139fd5ffbc08a0bafc8a12d0ce484190dd6c5df6566b5efd0950bbc5c6b7de0752c42f5d29fc92433eb0498e41fa32d53e6dd862b9b207ee0c9c99ef523686b440fccf25529b898540ee5c495e592e1a0036af583c788b410dd8904a690bcd86532285c949a5c0e6162daaeee3f59ce0de6d11f20f5701040fab6c72ccce0603fcd954180b3bc603182bbc1c5d40d70d08dfa7a4d623012103678d7577fe2e9eac731dbe3b4ff81ae887f72af75b214c6d256109e53ca5d40607f29a179d4da278d5c4c5289ab9afc7387f23171c7b668b95692e3f16628107c71598368d9e3298deacf38d186e715ebe81033707363fab11af4a2240a0b60fc9dd4f92f037006fdf182df876628fa9dcfa951cda96acce7a1b9ced185f430c8a1697710299243ce08a72cb08168b0eef003b22c6290a101c9e4333800ce60e4e45729942db9017077618d76ec0c0fe0b472a6ddb31a5713b94eac55e78560579661a9c56102823280d4a8bb214ebb8be2d0fbc68f9a4182244d229c2c5a50e9f12d00f18f3dad41b9672e4ec85d8c7d37b24ffa6186cc504ceb7b21535210bafa24e616c38f5323e75e4f2a381bf4d5c515f7a992c093ebfb15e42a558a10d87870be2ff16b7bf4b4408583d026d4e6fc632602db056d47c55385a0d404508796f4b7ac1fcb721a507f8afd9bd6bcc260d2590853e6df881be6ea1c4e88d019c53e652bc73bf6d7c5609d5e0bf5f7a9e777260d176fb1ccc17349e0ae8970a5fb355bdb77af2885dab801dfcc58edebc8e62ffb6f16c342ef3213fa04d7c050fc247543210e9ecd6d31c58ed9f032f62fe0d9e288cf55e7c6cdc7b33136f07f7c17260e4ecf5dae0dbffd235845740eb8777ff6d1dc8c429061a50a35ba20d8f1499be6222f2cc54378102b63f951259631838d4cdb3cd1a8f5ce938238c054a267e5de4a76eb31389088a1fa3b5977f7f0ec616db7ddffda8e6eb54374400a01379ce3203f609d32b361a46f7cc1bef091ff69e4d3ba870e0a1ae768be00d223eda6ed973b2ebeaa667e12fbd7a98d30c9f3173784e632be886057e7cba07136e6baf504d17961d8b216251e5022637af348de86d1b4c70b82beae8c4a10cd63c2b5e825f3db728f832af00368c0d411498b2b45239dcd6a8632ec0702b06b77bc3493bce193220634c60d58e36563fa4037bb4265ae03a048aaf2e27210c61327573b0c744ad5c47f0ef32dccffa886d333fcf1304d127a35490012682096a1fbbf94922bac00de6b5869f417a32ac34eb5ef15cc6853288e0f5c042db0fadcc7a97f7e3089ee2786fe3f783879f4f457a7408b91756f80455f77d2e9408c48c599a5b68afaf27ad4fb55909735bb9f7df56c876efedfe014abc5dad8e02ee0dfaeb66e47ba3839ff9c80ad117b7a16f9c5c1ac0db85e5d345906f43a800461f6e26cc2ecbfb124e854e3fdf811e4af9bc0bf18e1dca909db7ca7edea70ab1dbd45f6ba1fe9e5acaa85ee38d690f4df3cee3768a268fdcd00a4c146e4705583fb3b934224cf17d472f46812ee036bb5f16ec9a046f9d71a760d4732f710982bdf2ffdd903994f82cb00948f2b043bb17b13453f555382c4aa6c05393990f731a5c3518f89ce31034d51dc5dd1c9524a5a20f60b93022172951c87930bc053cdcf5b5163394a5c4b76049498ab08e935fbecd676c9af6bd3bc31221f79f00b2c93c5e3451b6cad98cab4ce6a2ef07df6fbd5ec0ea9ce89724b21ccfd90c025ac4cdd7e9f428fbff790968bb9660ecb4a983dd6d38bfb71587fcdfe0a9a509c8d65726f238aeebc5b0575ad7151944bc6ded431bee3cf52fb30182c520ac0fa5343fe157edb18cadda28e262a92beb9b0176a8ecc43a56179b33a099a78e0379a64940cb8bc16390532e9beaa6156ab013d8073b661d1268872c5e024f790b9c4fc38574523b48747acf455767dd8fb76fe303626dec4891c68bf3a75cf601147698a1a3bcce73b05f35afe36e54cf2eed3f05e79cb82eb974d8d1dbca9e0cd4c018f95f1cf70dc671a7ec3179a6308f5e12f2a53641880e1e29a614623a03931817085aac6772afc47da03dbe9a501f5f384a729dd9855d7ca9ec3dda9a070d42e0530d1efba7782e688837a58a45ad456ed4272022f3ec85b63fce41aa051973ea3bd1bfb52144cc15bf8d5d5cc7922fd5b9a73e775742f9060a76ecb20c38d49bacd4459e0f42aa5673fbab010d1e92cd2e694b5a0bd64024eecf97b30859931f01cbc3282b40e2962b3b296bc1196109530d22f495a926ab803a7f7d09a302dc06106d51c072b65870d468845a2c6287203af7ed2f3719f96532ab3c0e7365cebf2a2c20d09530917821c9a38649c18f966740305c71330a9d7e4fa20ca150d1adde9db0932df577bd3582bc7d385ac6128228ad01ba53224d55118d07bbb4ace14b4e74064f90403f92c8d466449026cca8c686e625592f6d5d656c0a35ca58f01c71d6bb514a1671f78de1082941b014d2c2aeb136b4406f80046f049e608054642e5b80e24c65786ba0cfb816e52eb9bcd84e1debf17a72d5b6cf0af75c27c0314ab70f309ccbe60d8c62d9621e0758c564ffb5e558fc39becaf308009f76e7b7018cb0adf4e3a3f82b56b25590b982f4db4808c5e1774a39e9c5076a53ab7e5bf6dc7db94214a0180804d88be28d06dff67af5a3ce25d565a81d0db1b4379d9e9438dd6d090dc5095e6f82defc22a1da15aade07bcf071593ce30a7566a661d20b4f7b6ad80e4aaf52ac9f9d974e01d55c4c254fa880ddc3d4c000cd0ad8a6138c946713b7efa36e619ee86029f2a4bc9aa3b589a7ee04c0dec70d9dff586d891e0447ac67b82abc2409a9de819ec8760146cc78fb1d7ad3e34e01b2dc6119bf12975fa000bb064a535e0ec33c258692ea49ccac6abc6fe5c6dc016ee789ace417690ee0e8d8c2ade8d23d42522f7962fbe14137422a5045c0ed092ced659f92d61dcf58eca5dc838f45f62e1426dde340d7d4c7ef6f5a9f81360d12203f9a88e4edded13b1b377d4b508308bead0c4d0b01b4242551918dff6a0de523fd406c6e049f63ec95593a56a2339e945f4288b5fe55f67f20763f9d98035700e719ba10a3c48a0bd63a47aed12b3efc40153a56a5b171daf0ce68ba770ed3d42a4fbfff631e0c23114c62f77d9cfb107a7d61fe3ff86b2cef2379110502ce2d75c8a34263ec4a19ba91eac403a25c8c447c993515de45c841eb1960810fcaea68081fa1bf81c3ba03f72c392af2d7a7063e083c787e6e12130682bee004fba4fbe82503e81e3d67ca2589d58ee514eb61e0690d3e1781dc6e2735f6500312c13d58090d88304ceed6a9447c47dfe7bd5768a2e05bd875aa79e540a12d099ef2fcb031e7698fdd06ebeed11029d004e071b2bb6d125fcb6e0be522199f0171f23a17813e438b3153d57af1968c70f0a6d48c8290eabab93881aa2a00da08f779143231631a28b3786d523142fe6cada45fd7feb0635a027974003fca220dd1f34d2cd0597c73087f743816c438d07bae814dfdc2876ef3a1f4f4423f1f0c2379fa4589c50aed62c6e1834e5aea81fe65bcd599185cd2b2f734b543c02305fed6ffaa87192715fb36d92523e024ec9adbb5bd9650cbcdb7fcf98aeb61f204a6df8d632e5491c54472b2418a4f79cc4457be668fb02aa08c324b1763a154019b4b85707fca8419e562dbfdcf6c80b7bd7ac3b5ee17d733014b6f980194050c193f14cc3f00d5af061b8968d1feef30ec52d0dfe0b6178da9b88344075860085b5b57bf3542c910fea12b44c622599cd3e0c98c94b2d15d294375293be5ae03e1d0b51b606a044f3d9fa14cbe2213422c0aed703f54f0af4a9260155b695105ca309d1bc858f5b627fa270c2e859cd9995736c33a6779dab8c732b47f275d064f30c26e1124ee3f68fcdba873108c2d9823ba42b598df3cf93122092f90170e3c4853133946a6a30a0446df35704f1650522d9cd4323785383232912a0603046ac50f224dcec19b1356953e62f61f47442ab35b4344bba563a0fec864853c0d67fa77d967419d258187afce79a777c081e68b7f295801d3b0eb5a77aa0c8b0f4bb2e7e8b3f795ff136bd585806e2ce843d9856c0b274f09b9852f3c4ec03701d0410cf4a3164e68c5aef2d6a914734cdff9d1c6331438ed90ec879c4354eb041f94b942e6262a75093384da774d2a5a34e8eae07cd918507bad4c130f73c00cc201ede0917a3fae4132b97d860b9dd947e073bee118de13bb68bab2bccceb0680320eadaab186cf8ae335288d218f4c5afe921792f33888599115b305ff210cc88dad78499d9f742df62221ae575ba583616eed787646bcf798247bbcfc350d48565e31ff5db19620dfd4e5b2932adb649c8b73c9ed8af7caf0e9a6c4dfbc00b4b0039b0ed82516640f5bc76b89a6e9c58b768ec5f6f1bf9a854bcf02d44902c249f0cf36abe8c095d2d4cace181f332edca36b1007940134bca1df57f030012013f8953b2e4dcf78db418a36fc135016b1a4f0ffc0e4b04c6e795eefecc8054ce3d14f541eaa27e229cf4917c05d6786e78f76cfa3947bf18468420b8ee40609e01074fd5cbfd1aaf650a04fd960366a0aff50639c026aa6c6b3ec0afbc406e92086ee66455589c0ec2ef591f2579d168b1b33dd05b5d5668ff5b23301cd05fc13ece1acec1e0cb54ff1cbadb233407262371d6316920f2995b30370361e00d98dd8dce00aef7c54516d4fe87abedcbd66280e8e5162fdcc164c3bdb0ae803f61a9b721c3d659e6d4bcc14d77bba29840590db2ae3a9ea9bc93dd04b7f650b4df401962e4bb7c624b38b05577b318a3793300da9ce49415c29e2a5cf5a1d0ec506eca6b20b54cbe92762a6380aec38dd36a8e3d54a6d1357b9efb3738e900462e57ed3143ca89dddf809c96637dfeb5c8f39eb81e6cdba2fcd28f7460a52091c99ffcf901a97d7bafb4cf841d00882c9787f395397445d48091619fc959704d1809f518983783d6ba6815e8588b33e4d5cc695d41360646559771d1ba0de074d797c7937456cb5ef2f7a305dd241268835ce2264b78054f77a84d24d05ff000424db39d892dfc91e863792bb2dfe0ebc8af64562b5c7668c2973ca3c6a9e07927610b824f8ad73d241f0d7a660afc31a9191cc206d3820820862abf9681203980a540b9956454d74cf999bd174b6dae84a9d7debd37a114e5648687b610107e7e51034319347c5c516a91bc4e35acc8b07e885dde76a7ae331ef239257280d0c035d78e563def898b3d62bf7a6fc11ae9127078777216493b6d9843da8d3064e7c2d2d828a27402b7e14f558408c03dfc9745ccf2456a39d8a7f38fec1960c70271d89526f10e2d6432925998ff4f90f1f65f4a5f4d8ab7b2031ccdfb2a90074d26d24742ab42451067ef07bb30b96c44e5e6322f5124b8f65b75fd979c70ae0876a882545c25045b43874d9f8f6183c67eb0286b6a1c7671363b2a6900a07a606dd3c49fe0a9e6443cf2948d4b8e29e325fc495e6eabf7c81adf81bc9ef0cca2cf1d4e93a4eac53ac9346676d2b341eff38d7bc0f71bee3bcb2a6e63eb60461fa7a5b5b0fedd2ddb7d0b7ff96f980da7f18f956949b4d96be360617f76004345368df204a93b00eeb472259b153e15abf292b4bfa5819421dbf8852afec0b24899bed787ba8271196339c29204e37e5036957f6c3cd990cb8caee384f8b05d6115f95aeae8b4d9d1f985bfcfcfc75aa5853676139a972dd5d017d4c266c033e4e7aba33b04e60492b17d1723ae83d32e3c58434014ee38e669bdc2d91480939072f3ae538fb425b0de4ec4d6d5d6ecee6b3e43af030ce162a1c83e5935904338a6dbc8250f33e3a0cb9e415556ace59283de3e948d14c9925acd6170b420a8d607b0d4d2d83d8918d6381ccde400a777eac248069e8e4ec88f593ddea170ac7683f32335623f99fdc389ce62d20840e557fca6d98d8a07768f5e9bf04ad073aa3888db329f94c97b51aa13defc505d0bed163b9bffaf3aafb005d7bbf5b058891bb2a52c2828023eb2710d344b44bd64cbf11386ecbda7f2cb5c0f829f304b3078f65122e13f6978b2905b182f4e8e6aa6be17d5c7fadef3028a6f21a0c0e3fc92b9bf0ae215e329321b5029646f1ccfcc92ff3ced3fbf08b5313ce196904c612bae761c14d55771e640ed86924a4d8fb10ef6f84a9f4a0287d5cafb9da0fb04abd4295e38112367d1769dc522ea8a658036cd822635479860bf821801000877c3167be29d956b99a5de22ff721c182685086e6727c517ae1e3bc36e6dd07da9c0e2fcae2e71954e6bb102d34e2af3e5186d107ff2fade2f363e69f2dba076da4e1e5cb538d84fb4ad18a5c419b2e0ae8a141082a02a7d12544c4c215c80f5939a15a9178572f50afcd3a17a671ddbda9a2779a2c65d00c576befa1c020061f25f6f56628e43ed0432f825c7a8324e8a39f9247af5b40270775af4a01d207ef04b8b40a3893ed50d86b2ab4fd43453c0467aee8f1166bddb8d1f8f296a908e333a7b57c14d7fd82f42b9fa670737411f676fd1a5db8462a233e817baf020a2c748632db9499f95c50f6167fa3ae421892433f67ab3f3a4f78ed1d75e2d9055597a9012e63a0069e576b4a03cdd632521a409f6441530dc69d4deb89354b01b433d37c6aee627ec548a8e2cfcd9f3f587d02419ed85088f0803b6d5438ef09b2c2626e59c654bf7c6fb8cd45b2671a46a673ea4127d4b5732321fe41cd500d79996892e07d295649114d1966204d2662fcf406baaa707506671d653741240c4d4d435dd82a46ec3331e8a383b15049339dc390e0bb592d7fd865fe82dc580eed7b466e4bf51a7c938d16ad4210b4894803b94c61ff0512f53778bb5b907b0796e842327bf60c638e667f4303e86d2f637dd99b9510fbb2868ed627d1538c0a8d2623aa771cf33b7779e01d4115c93b690f17cf1d401ee77c427eabcfd0300408642fa2706c876155db516b23efc41fb3c20d125be33f99462baeb4a46f9a0575b4046839cac949e8d7eb3173d2cb1dbfeca3e8126ccea272d69eb4db9c13041e814e41791abf9d82de4db9ea6febdb35fa87a1e700a93175237cf49de1cb046708f8c3c17070e6f85e0691c50afb365e71c462a936d5536dad05a249c47c02f5c918f9b6cf17676f7444bbdf9a9b64517a580049091ed078bc36c0d524440ccb0a68d298d31e070ea34c6f6caba881580112b8de151e18aa221ed84b812b0e8885209b7d62a27c4aed57785375081b8475f0578504ea99584029f0cdfa6f063e36c7192395510c24488ae0c34ac01dd6d1e21e7a858920f9bf56a11ca5cb04e402e3ce830c2e708392d598ec6c56a9c51e11d755568e13989a6281a6c52c02eed7ada11f789d617d3f6606a43fa32de3a550886eb08e39562a34d5d0294b035de177ed2818655ff094e5b698f33023043c5279b92b6ecfb61ab662d125ef000cd0ce450a84a97c3e8b818af5654e5b97b29a66fce57327e3a09a949ac32e0a6c69311ac6a31c0263b4413ea05659d3460da86f5e994d58024bbfe061836c028608bddeac81470cc05db6b7af2d4d3ee4f2bc3ffdba5dbf4f51e1f544e3f408ba1bc1f8b63139646a8c280ed4539909d72a2387062f847a941dcb6f08d2f90aa1588a12eec48050f27fc83bab8eed978f14bac43d44f4995015843064270a04e736258f674382d3faccf7664c64bc22dd5aabdf3184b8cfe3f13c0382cb9b058dcc21efde3ba111f0d1a6fbd2080cd631b525d7c90959412ae232747ff8440336cb110d32b2b935229486b5b025bb8353569f918c53a349b29afe0d68cd9e04b1d2d2173446c5862ac974052a09a970ee30e28bff07cca0ac928c60d4a0640d4db36133e7c27dfe7fba2be3e87640b3a09848e75a1869f77a8c27793bc88b0de35f870db966eb2ecb25bfadd6ff1150ad271e3f045ea799c747eb20d7af7904435e797893fab6c18eb32d580b03c584b2177edfaa08c39f1c377d80a7f5ee0eaea50d85668e510a22ff4bae962f0d651f5352b9b98218172db5c7a9d5a0550c473414e47c040b7a2be24305f91fdc3dcd4b9e2a7d813e589fa7734a3c72af09872c872f035871b80bff941d43d722fd64f2ef9049c2f7a0bf1ec42e520cc30f598a39a42d8ac2fde523ac4330cdc6f43f13b003b54df1722a7a25c4c02b450f1557c6ae1750e80a5f4b08350c71c0bb26a2d9c072179a647f7c16003599150597819b6955575b6bde7200d4e11ee1d86d870a1b5bcf34c494e7261f273e4d07fa3f26b1b0b4976a357db639199600abdd53ff85981dec39bcc4a09bad618606c332a15f8062cba5b14c104f9329010f98404576dcbf6fe9cac6d0123b6500060faf80c05482b4b94d74abc30a4cc36920e933c1c74527bb331382eec501300d622d607846d684ad76f62b431f4294847d2d36712feb503780f5d513aa108d011751cb1b06357e58c5465b45e0db0bd88479be64e66f940462ac65cf4dc83d05c03e7ca2994e131c8751b662d0172f063e90b351dc6da1f077c41d5e486be90f8cae144edfe1e3ef8db10dad0f8556cd73d8effc9ebdeb370b1a1d9f8520c90425e13ec50015a5324181f45a55e075b38e8043c2f4f34ba347f9bee77e229e040a84cfb2c3e33615b4c73a0563ad56eeabfaf51e417a64bf33cebff567ee8104d44c7ce1f0e2b94624ef89f462c6f03b205c3fed7d913fd4050c5f23afa33a041c7a41a92692e6d78856f5c4775cd5d7bf4a0310f23268423bde5909e5f788047b29e6b5069786ff80491e3cb31b7af749bbb1f42ede984fa790dfe457bfa30494a7baeeeb9e6cfaed2921a1666724faaa6ff4149b3c57ea817dfeaca5ba650c0d4d5d097701ebc986b549c1989423e5606551d580948a8e770faeaeb8f48d076c5cf001573b520e83d4d767da77f76da348b0eae822f8a6becdaecd3bdd980a48445654247827a5d75744babd5b787d4f2eb6dac21b346263080bd33ab442099e0cf56b928a01300da91f165dc8fa0ad024d8ee7eb007066f7ed60840a5260f2c8e945a9d194c2f8b68473e979cc322fc37b62202ae2edfbc1d44f1025e6a0c09d14dd959fee1ced0d156a48a82fba5afb536939283ab1714e62c24d4358b0169e49d6ebd972f871f1519fa35951789f4d0ecd354a7f21b00d6487d13096e061d39a3afea7792c20879bea80b50df52aaec9bee507baa60fd2dea8bf9d2c202118187851a32064d216d8b740b4294172778b74431fb8ab5aa3be11cf07d7e05eeb8b420472c188d7c91639c4547a24fb038a2e3566cc18854d4fd9512228d0dfe1337fb442e0fc7636dce613fda8fdb7200337a2dcfdaedae81c17d78ddfc0d8cc8c3212aa2edc6b52f46ce656f225c7500744b0cc1dcefe77011defd15070a224ee0323c3b2995c76eb220bf84b907e119536580846e57a1bf22658d506609ac7c833110cb8b340b4061d50b7d5608d3e6170e049d8e2dd581baef757c75074d79e148516a08ba8acafa172acf5cae2d49db885f5b4fa18a1bf9ce03d75c0eca6321f4bc77d3b41b1096ca4ab27a45e859af5aff3b60b7a5eda92670d1a204e322d7fa2fa71fd034a01792a771a27119778a41c43db0d321610a8f6dd23e028ad299bae988aa9c1e7b66e3313eb53de35672476ab57dee302c8b9537b5de0ef3d943b5fe7ad2b1ba4dfb9db7e05f4042dfe4d377104fb818476661e64dd305802a229d8d6a436a85beaf3c0a8a4cfd999f1bd38f2c6e39ac3321468965740bb6d2a784cfea7ad34bcd548be6566d40c9e7e78fb11bf9eb376c8cdeaf5a440f0df0eb114135b500417ad0eedcb7aa8503358b207bae180c435c10b4f43d270eb5a847e3ed758a735fb7996968fa26f4f70abce8576ae3401cb3f8dc3864a10132ab3a69575ffd84cbb12160146ff624012046536517d2806ead0116627c7c05d80f927564a35b3d5d8184cb997bb8ec67c8e626f6216089390ab9ea90dccc02a49889247837e297d1ec3f3006a1f35f1afd5a6a13075376aba88819ad2b6c0398f8ebdd9b8635e7c2c20d71c27ed4b4d8a3c44240b0599c17c1e291d1659b00662e84e8389f82fff1d8d5f3124cdbabe451972797bce58a8911b78841f29508e5f9f053416b8ed964da2a498618c1543fa7ae49d33e473eec683c834b11e900c67bf6cdce55adb09e26afc14e557fa84e0f19fc0f34b561d2b9086d2a70e10261735f8308e36160a6692c1cf14c77512818091ad28675d38086953af67b5907cecf9367dca4dbcc1b2994081c4b06d301c781911c397705881b2da46fa66604fac2fb4e66140561e45605a6a6ccc549560294e28d83fb1268ecfcdb243b9d0f4ca01c8c935f907edcb9e23663b40bfd563ef1846102c270f7a56cb65cdaa703bbdb40ad71ee93cf5609d7cd1320983926c45863ba06fe18303eb1a92546de04ef67352b3047c6a01785bf6f3de666388b20c35e391d5a67517d5b4b3087350c2f1213cae4192fe6c906d51976950e1f4898d85f6283cdf7de20d5d81cf328005dde943337c0080c10d65bd6ceb5fa9a12017f339c800e2d96a2c33411ad2106de33863a3975688f140566c26e40c46ba5c8abdbf7174e2dfb258f8f77008d093e6eb71bb1832bb8eeaa1cb2e18acd462cb79c7ae8023dca79922b70ee39020f044ba40daa8a8f94ceddad5e07db5340643abc75d525c6328181fc8871724d0e326690648f58ca65c5c6f0a9d10de8feafa51ed8830d9c38d5aca97d3cddd10cae20cf03b1725957aa0ca3e9c05828682f390722c941687c3107d70e4fb9bd0894d546199febf8f71a4fd0f1b90b01de51cbefeef3af8094f01bc030ee0cc90d33409801efc83f04cc1a405cdcff99b7d5080019a925bb8874826e77b52a7b083a20fdbba49cddff7fb8508d6a04fc1d16f3d3b970b558c7c7b5449a47d7ba0bb423ead5aeae34eeee58f8c942352580955bb7a292113f3011bd4573c888e00ef160e9713ff64c30eb2619cd5e3e3fcd94c97840986e6cfce2a58f544451bd0d5a362865c81924e3b5871181befa8dd02ee29348db59087d8a3703b9fbc7800e0fe03c35ae45d50cff07560892e4ebf918e535953e95ef446996e07915126207222f775dadd1b2eff0796d2d57337ea6f413640e0b13a49c55dc44ec374ff5032d181c922fe51273cb9e0ad9206bbc8b40c230f4c7c0306e07bb62ea6ab1400c57d1d310a35ba3c102ba3b00fe79e059189312248a512a1ad76a5f0a2ef6390274ca8842865e0905102a653ee64e7224f889e4016538107b8aaedf2924af230ceb2a6d4559dfb9d166c66cb34e579ef632207f3037f2eb4e5bc8a43e58b2a80ca57af248bb1746c28e8dff283a7d52329f7a824dee21422e833474e9470c2b08400abdd51951e198dce87bc4a54710be1502c68e23364d4dd8f5372101791f03cbbf1acc75c17fbcabc04ff83b3bad6faa99cda30498f6bb3687f3411a88ba056ae80455c9fe54eb011f8c7a0f3c1ec8dbecde56f0ce1d2168649cc3ba134009ee80dde87657988b1b5e79bf511518c4e6a31d8dcf4e721f725d3c22a8785601dc7fdfab262cb4901cda39f3b7e4a30301e5440e95b16b4cb65fbdc661fc7105398cfe96d50673089f688b16a3d6181267d52bdf19d03d1918c6258e4369630835eedeabbfeeb1d791b98c09181ce52c6e8312518a5872cf308dcdfd1783ed0eaaafc2a0f3838fa3964e3459ffeb9964b6d4bfb252dcec6cca9d0592eb12620b590d218ad3300895ef2f3e7a8fa79e90a90b25b861ed8440d2adf60166352a07889c52923ca288ee3fceaa92346548f542bec1b4aa72e77eb7d1aa81ad661a05512683c7456346c7dbafcebd6ff5dc619f6f4fd412258a5f7f24971a37cc6f05068055c995ad65d87039c40e66ca8a56782c3e1de993d77d158b31e5bd35570bcc089bb152ca139485277be9206fab87a6a3b38f1efc477c5cbf2da59c03af0cea080235f9e68b798a54ff051882125337911177eb5172348bf80e2396ba9005efbae5844d466d209b9b9229e288cc6f3f0d935bb6b3d4a327d0662ba11f5809444a349a15f509a1d632d10d4f40c9f58e6b6436acddaa0a5e08dffcb0105e0c1d29d9ac3640d4e8d5903c241b762b7e0a496a6de29262cd6aad2ce2fa044a062ace56adb6a66baecdb494c7aef72a73364649201baf29c5e3e23774b729560c474987f9a45ecdda3072f89e7854772ca0af64d3cd1405c28baa109cdbde7108baae87f91d543ebe5db078f32dfaa00c2b50f5a07a0c197f56e575bbdb8b6f08af9e8445ffb3d7e18e8828a6257d62ae2d057e4c2145cf35c8c116df543fa00a7492602e5a15f2a474a309eb6daa7848fd8256a69227b6abbc4da9800ec4160db612d7b21629fbfefaec8ffe34f681c8666d2856abc30c949ae5c224497e830bd99159e52402766e890a87a0ecf9c9700f8f39ea57c8795f28c10810f70ade09ec341292beb8ba30a2154f68d29032538d02d9efc08eb8857f18f98a76ec34009b9f10f1829c8770ddfa6244b20a1a5b7154ec8de952784cfa22f1c077dcc707210c94516ba2ad7b234e4a5ec4e685aa5f543e92818098b25a5d1a2cb4d6540a60f56c7b821f7ea2cc4432385f6bfc2760a4f8ba3179933e15b664824f9fb603147bac376990f4b664eb806cffa1ec9717fa9806c4fdd76124713e7f5dbad3049da2edcaf7a23596208f866307e81e2e971dccccd4449fdbdff4805f87fca5004b4e62b7d1139f16c1847567b5c8c2d0b21b32cf5b80bf5a81bca20f743e480c5aa49a36b362aa45864c17a6001dfe19d31799fdae6bb11f19ec960160c2e90178c2c3e3e3d87b30a95568fce9abb3a7691e98c0e70212ae756cd2171c6e6003fcbae74873b6d7dc42a6a9ec2a87cdd96ffb9b34e26f11319c9b6e69bbff4201d65cd479ebf5841e7627ef41b41b9d81e2981647f49661d55164695635c607047403cda61ecf33e7fb0e07241e8aefa054f96347eb1fb149e21e8a78b2da2c0383d2da15a69a186ef709ffc74f6c3fd179d167b0e2d484cfc2a7f83945cfa20045eb150c16dccde5a46be5972d7330aae5704fb2f720042c05e5734fb20c9806ca5ed48f772290daf5cdc111b6dba7bd6e4492b31628ec3bec92a9db634b7301fa2a9d3d60a1be219cb40de883b6e162076806e44bbd7b4ef532858462b5aa0af69178813e6980d580558472df4a68903cd49c1a7a46b1051c855a3ae5c2d707b90facbb46a851964d52f054b03f537a90d459dc21417fff0b9509ec0ed7fa0ebd0bee718361aea84101565bcf3d0452bf88e10561ded2e8429470029274b00dc4be1295d099bfab123697975e80a3cec53d57fdb357d83c83db94545ff7930cee5a4960b83f1f88ce0fb863ad53297561ffbf426f577b2306bf105e25bb020e096cdc692fb05f2b470c34eb5b0d63e769f85b0ab0f46152bae08f03b5016f0924e62ad47353a2f10b1f28cd95c9874c209c26958b00e2280ea89595a6ff2a0cf7ccd9708bc70408977a8917331d4c7b20df3fc2a201ba98e38ba37ca7cd6603e65c2fe90eb957471e00732ab1d6b8dcd8ec6769c083f3ea2e54605e5e8cc90cd3f5311fbdfc2bf4eb33c4fc84c0ee0a78d5cb766ceaf36d62d28adf0deeee0c0f4279beefff81f3a66f18bbd3dcfcce38a54224a359506b9698ffa5ad263e000f21e8d4ef39e9e4024f2d8bdc33cf95452535e7fd50cec53c6418982763e709d3c8ac9521bef81c633bed7fe095d917a7f24d2c0024115ffa414e0a430d280879fa8aa387b01ceaf73b56cf44559be91d20f997cfc6e0aff19a202caf45170ecd776bb59b7cbe15915292c2736e91673d688c4b2020c7219b9344590fece301fad07c54840f954f5250cd9ab9b8cc6467f2ddcfa9992a74cc834bb6a8f52907e83465a92f80436cdda7dec6fa7c8c14609450065162cdddcfa55516bcaa9102a0dc6f99c2153426d72157ff8a4b3a7c637530fff224c16cfabe8aa379f8060607020ea565ca31619f3432c0322c36c83a23515294340969daaa1adc0e3dce057119194f2375b611e4c31259fda9b5ea1fac15643abd8abbb147d53178912b0492aadb3da0f02839d2e816d3ff039547c00c3b929e62ef877ee2e6d0c08cc109ea468d481a4039b891beb957a91459524a5f17dcb08b35287c69985e42ef0a066e30eac12587b44e51947834749724c039433c948d2c63326b3c7191e21dc00533d9b706034d71f5e4dcebacf761fe981cddb2c84e29e0a05fe6172efdfbcc05863dcad3b5e5575618ac947eddf27c0805ba995317312b4c5df2cec369a7110f79294fed9512142b5eddd7b3d086655ce552129215ae1f4fb3d88452a1944e0c4789f0b58b7382ff2a8e1d2273be7abeced746161d60d7d07828fbe4b5dc520f84ebcdbe131198a8e3259ac07dc756998f6fccd26080a568e92724a300405504aa8c0064d646be27be3d54deabc8dae4c70f5f922e258440ad6dfc4fc7424708fd2367d1b0b5a713e9c47c9b5be68b8b7fae2296a85836413ba1a735197b8f0813afe5974e10eb7b9f202ded26158b1b77b5bf1f9ed7faeba6a3ae32a0a9220f true +check_ring_signature fe4d275b7e22fac8892987ff326b708727fec04cdfeda325dccbf0c3408d80d7 695a62d87ebee760a3ab216073b22c1e9b6dd687bc82063e42fcd1320e9b3efd 2 8fc07fe73001538686104ef03bd29b3ed1a663ad0e600eae8700f1e53dd42ab5 f91395298c69109676fd8accde2602ac372840c7212e5e25a9cad71f9a5673f1 a08453e0f0964ffa251855c1894197d1f7e0da22f60075ff3c4cc39ff25d9c08feb09aec2b1ff2df52598b3e058a5440e0e290f0595bd885e8c4ba2a2d27dac4758e6dbc8058c1be7e2212db11ac358011090ed876d48d97dae34c1b2cba0b019dcf79c57c6178fd659045820434f15b331daf8bad08a4eb1dc6dad4db6a480e false +check_ring_signature a2c4ec771a4fe1f656defa0137af326562645fbee99b89534651fbc8acd1f472 6797b81a1504c8bc32e66f7274ed342e7c38b134e62669076c00f17c36f69ed2 4 d3f9cb68a4e1dcf515998caf7c30b2921863ae01df30f92226d8d6fe90cd424d 6b58ba2dbd5185b177c57fd3aeb595e2b457ec58ed37d1e8a711b7ed3218c94b 42658b78717ddd35a3447113d52f0c89dce4312dacade9d38182eccca8227be1 4c481857b3ec46028d30d527296b8b973ec3436c28776187703f3b8d23b03ab8 3fcee22d3ca1da871058aff2715f9d2c3ad4cf80104afced6c904f7b1f0f4303b08d860212e1cfbd53544f26ec95ef30151d076e6e778b73463867713cf49a0ddc2cd7741947087a1172d38d9d9dfcd9028ecf6dee8e50f3bc76b6ed1e5fc60cae05bf4c241bfd6da576c7e77d1f3635dd2cfcdf4750f2484c8ffe626c16f002442777850abac4f81c9d4308c389b65571a7fe83c7a1b0c986fb9f66d47263097ad4d320d6cea03ad5dc1d7dbeff94cc1ba82f24bebb8d80ac482cf9702d0d03a4cd95cc72ae25a6e3dabdb7c2fb20231f884d10d28c2b30742b3d2591feb10aeb36ad528f01e1b03782618f21237fb27243c97b019e51e6e825946f2987d906 true +check_ring_signature 36162a9e28401d36b15d3abc515aa3539f3058d5b56d3d7cb07edbf78991df6d 196a3cab36b4ef9b1626e414e9a7da209b962f60d62def6f57e246d18b667d22 1 ee3ce69e015d84190502a5b1e930ba4750ec9f30d2811ae68e0838877b46dc34 6aa322f3f4055f243ec6ca2cadc095ed378cfd253933decd10c00cd13764bdd97dbe4f0650b4b71f2403ccf453acdb4c9854ada094221a87254ef199d98995f8 false +check_ring_signature f10c00c45218063bfc193d4222d3da20ec66d4aab3585865df141880b0d80c82 cafc164c95a31034d5dfeb735c14264b8fa9bfb7db6c42df7de3ce035849cf31 195 308dd80b9d2331c12009596d3fbf4aba89e573d2b241d5f7e5fe8c24499f6f9e 269d04951f3ba69a04fdca45660b768b37741a1af2e81abba1cffb7e5f145bbd 41d57aeebc962cb4266cd7b1d02f474f637d31af92abdd9ef4a5f8beba0a2a8f 515f84c6226eed90f661af0717639ea6b16698d33cf01e6fb1d69b459cd732fe e6180ebd955eb0b2a1d0126312bc692918c2aac6342762399f26b4e700adfaf3 e5cf50d2a8015bff4741d543911bab064a01eeaf0223c702b12f040eb22bc993 fefab43095f3b49642bdb68a7103ae5205b2a0b762f14196bdc976453851b35d c3fd5d42ca8105449fe52a9a1343960b3276a444f4e8e9124e195e9e5616012f e81c1048fd24fe194218c6c8991ba8fc87d28750f582b62344b06d9968541928 f92441ffa308e6b53de74eb1393ddecfa860208648a4f06dfbde9206f55a87fd 416fb359188ff0ef05545607d9ac74738eb6e68c5a0f8dc6ea6adfdd1f207f45 493d2b98ee6c73e82eaf3121f7eb8d31fcc8f1d9cafcdd6a7a3a762c916242e5 92da85af4b9be98e530a70e5e300e753df5fcf0931f9ccd69db976e798fa8063 0ef84e4812af9423ced3e861469a24a4a15fd3c389c040d2cbc18c7b56f6b7b9 7bd489b11fcea29478a3df8a9d80abb437b70176d93cf7f02e305b7d3039ca4f 5bcd49d77e4a827343b0e9c1a4053b22f68dea591182bf3c5d12f6f555578078 38819991bd364133838fb3ee72623e5f1d5ced53a8fc0bbc794e36f719c39f40 a5c72c1d13dbcf59513ae1469def1c085d36ac0bd4dc7c5fe232cda6b381aa1d dbb69dc365cb29394f42a12664d68003199f4e454253f9f826846fed5466d7dd 0b1c8eb032d78fde2ad498df0b4be0f41df90e9371c24f6af180a494d7e213a4 a7981873a6931a6a507552895e49a23f7c0775c354b3daf6caeb360a12afda72 65d1df2e12f10f11daf4ad0c4eec926b6e5ff13cf76da39aebb4a5f11b5a761c e8e22971112e948c9f96214bc9a6dda01d93a5dde00905cffead3a6352494520 8c5f87feac8422310d31505d76d10e20dba245fffb64710c5848c65b794d49ef aa62aad4c79bd9cdbbd64b12d49815b63a64c674ac6df558e7b432bc0965845d 5d1e56bced2d0231cc62f6b7211a5a8f7757de777376449efd7940dfecd308d0 063031f7978e73125b59c003c502b9d384263e2b5deda0e9d6b090ece6760d3e 77905d404215b7844484c21b45b2a5bbe3da83ca1e2d338c42c5d84576600838 2c2757bf7581a1a7e02227711b488ec5229ddfa5b9b0bf54cb6b5648ad6d4636 e1528780175f9e4b9b65de014d83e131fa09a298d9e7d46b6f676242da48e9c6 3523a76271836cf005e613655c9c07f514bc998c180f497ef8ac2d625880e9cb 9081fa61d2ef3659e7d77c89b78257405eb6afc0c65a4e3293c38be990e740e1 71bcdd1e8211157fc057b0cac589575504d10d74f87f31c406a6d756a71652f8 9cb5a2ccefb3151cd6dc12234270093faf27b403e7cc88bcd218340ee78adc28 254a587f7f139f93f26267b871c33644e54d7fdb1ea952b1b399983ed5aff423 1f3c932f5e27869af38085fb2701834c99121c09268004db49df21528a735cf2 274bed621c8df9634ca8e683b127f01d72b4c82dd25d08f2480a53540a133297 bf4831272c05a0a4cf92b895ada39ac12606764f35d45cf4a261ce638091f637 be2661133e770f6fe04b400eac3abfd1e7760aa510440b65c7e0318c5fd0124c 2a2bd9224f31d2a7798472be7af705a19c062cdb646403ad3810e6880c1d7237 7b96db191ed49011cdb73123069145938f913aa50898b2154c772bbde1fb969d 2b541bf562ab0286d885e26cbd474e6c075e7f09bb9e4d865c2413ff0d0e0d94 27df6ebdbf635be8a88ea5547676cec45ea8d658e1ae50e10526d3c012a4b7ae aeb5cf42cc96ccac46f1263504f497130dcb38feab80ae7238a80fc33295400d b46cfbc2c0d6c99e1a668a8112a4d0501fdba93a9b0fdc8c6efe035ea6c6e6d5 17122857afdd09178ce18d4cac5dec583522d1ccc8805cde3ce3520be40d4d5c 84dcbaa814131a8d2a9bfefb7a32cdab93a4052807d6c28f15ecfa30f1bb87b1 a8ac6f0fe8cbf1d1742a3ca0a70773d9c397d972916c192660924b468ea7440a 1067b0fd6bc4dd44ceac2abd1add3527cfe3cd9df8dbf639d4bf7ece4e4d0066 8a82f7f73e0fb27de724ccdbbd0d27203d5e6898d31daf6559798850774e043c 518940526f47bf1c29c2aa260eac0fef3e04d11fff2573cb1b6be88888da70b6 3c91b13320e0163c124729cf8ae56af26841d54f095d19e2a57273a51c27173e f5f64ff1ea43aef16b7ad44df26e52bcdaca2ddf9095d255dad00ced8dd36760 9beaaed9277f2de6aca0d283ccc3df7740aacc26fb074dff054ab1a83fd19d83 3b3eb7c08dde92f18456b3462aaaa5b009700ced4b41c5228ba99f9373ad59b9 f8a57a4615cded872ef5ae54a7df97194d35c238807b37e31ee7763e317af591 25da1b80df4c5e0f9920a95f68d6f293bf039345b4d9ca605b08c351f226d513 ca991b0f9b73359db39a5dafa6387a11ad6fecaa086595bc5a5a3b6915f0c29e d241b1904625466bad977a968ea314c07e50c1cc6cacdb18590953895af84e9d b7eaf5e209408b034ab00a2b82711ddba5c020b7f04e631971e3be2e30f01510 e1dea72f3d78129458a2db4ccbadbdb516040e2f433a3ee70c833b09c684bbaf d177b944a8019674064808b18f3541c610d25d79ea1d37bdceedc73ca5ec3f64 b98ce89d0bcccfbe6590ac85e51f67d2f831647cb791cde2e37f4ef40be857ae 1de1c0eb3e70e1d07a769401d78e91cd0aa452d454e8105a0d1a8cc8571d29d7 f84a1fcdf5479648e56fd0fc2837d2dd24d4c9503052ae638d508a3eedf59b3a 2ef876da761757ecef039cf558962a5969a99c72638e8e3c18850bdf40f4691b f8615038c76e15b30ffbc1ae4286259c59862488056c9db4b6394a9427f3f786 48e1072ecd07a2de81dea140ff2fe30206303a380a4e7c41ce4d98265bf803e3 1a129ec9d787e5ba8fcfe8ba426c9d1ad3525abaaf00cb57b3ce27737ba71c46 9b3d1a9cd35ddfff77d99cca6aff19a6176127e1f79fd104b88c2999e6187991 195e3e146add89539c8f8fee360a7cd59e01c6a007f1da6351df0544e7f1b27f cad39daf2eb3b158a3357bae2c0273e7477723ee3b22ac743f14ba4ee8b3cfb8 c2fd22c4185ea5f956f996cf152523a25d2db3cbaabeeac0c1600667e764619d c169af8fb57ffcd53c9828e197aae0c274b328f2947676f248a27703669c1514 90f9fdc1ef99521aa40f5d7aee8c6843bda7b2f574fa984417ee19b4aba30746 6f228a3ea9c05f11771d2fa58109d7e571cce7e73e598d596825d49702f40897 2d651def7ddb870adb0d9a2f08f7fba3b7072bbd5901a05143a518a6540d16bb e7e2125e5da162a4a8e9d468edaf85c0c31a1b5cdfec88fc77af97bade6aea2d 0a18733031dd8e7ef95bed63621ae6cb88a07dff3e6cdb83c2d9ba040e5c5fdb 179d4373b2ae3f5604eecf1ceab952b698d701d52d4c8972637600bbb50e3053 5f9c61766677fd2619aeb863a8144ef2b770c72309c2ee8925cbc691e19080ce f5763b857a5f4335fd7565310ee50a2d1a2fc3faf20860fb367acf77ae0247a4 c9ff228cf8b41cb1aacc15ce05afbc3a3b1e3e47f6127539e7c09c5d2d67abbd 1ff183e7efcf320df7a77b780062fb1333a030370c53815cafd82526645c4ccf 7adbd0278f3eec93c481ac881960634212569855e6e24e436fd9d0e1fdb64c56 4f8351e2b4b203804c771496938a0f0f36e44d6e7828a737edcbd911e728c2e1 9a274b2a3a7905e083d591f6b686dcf30f13c6287c55a3439b9dbd05b10e114e 3ab8dfd2ca91be37d2ea6db6232fcd06a434b3aea88e6b31e089e367678e2015 2e2185c6e8fa6ce4983f0c3564a287bd18ad64dd27c767452212c2063cd2bd43 89506b5335bdd5d2e6893a2f7f1211391e153d66a8790ca431c8b87a063df812 c1de57f08ba7b4aeac670f3558cef6efbd28a6a82bae327500aaf4d0d0015382 24776b43a67d7d19af3f636d3038df1edd337085cf48eba13bc69c34b46d0ff7 d7a8766c6e0d17a5deb77a6a154f6e2dd41e6d1e84f4c8e082b23e5d40b86c56 01aa1c336a4196e1db192b5c8b88cfede3170ce199c59e3d964aa88d5d9025e7 19b26a1db223f42d460929b03d120a6524404d968ed49b7f198652a38295481b d1cbff1a42e931effe6628a2f89c3a6057fa0f31b0bd9b5cbef3587331979c23 d6f6e71783e52a6b7d0af5944e762e0aeed4c401a0d2fa19aac4a581d086cc29 0ceaf1eeeb97124470bde236084d3e28fd92eb2d232940354405621cc6a0e2dc 964720feb89ddb769dbf663b48d0bb8ec60230afcad7fcb0fba1f63aaa84f715 707e9d93ad8d7c57f533f1caa4c1a20538b3eaaf05620ac34f150960ddac0063 4270a2f9227c936025ce5346cac422117d711255e34f6584d9486a23f7707e03 1e91a47c89bc3270b88f6ff3c6ed89082cbb4603c5621790e0ad6044ea11484c beeca36415fa13fa8e8f2c3763a3fbf42a04568183691dac29f11883f2f95631 3507dfe873245c3a2c80dcee8387df981f5d40e7209a1fb84195899b1e56581c 5b29a28b52aae6b118d18d18585e5f3883bfecaeb080dec1885af6a96e97ca90 5070254d8db187019b4c56114ef55168b940929ac79289922a4ccf75b553ade2 7f3621b7a19dbcf396319e0502bc3ba9db9ae62c8dd944d589eb4b80dc8d4124 40df2e3fca8355e1371c6fc42568d25a0d7ef49ff235222187808c04c23ec9da 177ab7623732a4bf817469af095d42cc7d6a40a61c7ad490f96c08ffa546c66a 1a43460106ef94b068743810fbb6d347f88f9054bc848f3e4c53c9d80e5f50b4 ff7daca11570da384664e8b6bc118697c16abe2413847cd53550a90f37a5c8bb 8397acd7045476ab6007460c07be47f454065064e140c49676ab9b808c7b2a3c f3d6c2198a0fdc444340d73f814a5f904568c8f19ce367b0c4fd02d5a12416c5 dbef59610cd0c57af0d851219d8708e332eb0c88ae0ebc0fae3a73959e7b6258 826be191c07c70c685382e4b0e1a3afd0608455da0cc96a3f1de1e3a375981e4 e0b7cae01ca709d796b9ded5ed762c6fab9b0427e3efdab40b8f481085aafdcc 00c713367924f515b40ea60988a0d74d7cb03842aa04a3ac98a685e20d83c52c 877b9c1162adb6c04d33f86bef624b474db024423875d619e97703104c77e06d 2983e2a9a1e54d7a04709fd80d1436feec5bc105bc262bce92a6af77be694d40 c67d06463807565d159785de87ac873d85d4038a806e95c46705c832230f5356 49e3aff71cb382070962fa839ab019310047f5dc106daffd632fb9417c345363 a3da3e95acda8821423b769f62dcdbe81b7738ffc6b5944b4abc97d56e5e374e db425c003847838c9a8dddc907ca6f19c73444fa3456395aae61251a281d88db 262854d49b9cd9d084e574f705227111569fdf21bdaca82ac27274fd26b92b21 7e34b2462d05605dfebc5f698ec91312664f018ee32db9b0a82452d988990d8e c3bfd749dbea8dd4c3cdba91745afa4f645ef1c2ce3a0c47bac3d75521ffe60d 04fdc935f051e74e3460fdd7797e532540b4a4bcd50bf57bc17cef2c3e805505 0b2c34fea4fe2456528cb6366291671854672fae4427753cb3252b97a432d992 28c6095eb8503f7bdab125698873f01de391a8ff7314d6795efefe2f069d7c05 4556f71849cd16dc31657e53df4349fc764cba6c0f07e89f3016c1aa8159fade 9a64027ba441437c6f26e6ce6d8b70667869abdcfe2d8bb6e4a0ac1599c33b03 de2381525fe76e45cb8d25fd1e9a1969e393bd0259350060af4e9d31aa40575c b0dd2d5af56404c3447611810f7fb76109f644f1712f370da75eec0a0ec40707 fcfe390957023bbba2f5fb50c40f4dbb19161c7dbd774954e5a6db0ae70f78b3 fdd978a52a369fa1c3c775523eca903e5d8377c53ba64def54390f7379becc1b 896f908a8b9d14556a82c8e14d87e91203a46fa1c09ee690573011a42e906ade 1b421a666565a4bbe124850c7a4b4749fe03db41f7a204615ddbdf06e411b7f9 852fb8b6e6c0898d58ab846fb9eb267581a2eb0d1241a904e96ac632a06c181f b5f52f1377ceb2a43eb926d506d42bdd9186f143b9e89797ac43b682e96c7f3e 2995bd479c178f1be629746c3a40f680902181bbdde5fa1749fe8033501f15f6 51b8dceeae76f109dcfcf8eac139e8b0bce151cb1d471f92b4a5a0460a1fc434 b2549c229b3d17bf6e819ff116d04042897446c784604065f00976d0675171df 85ca26623f958120854b49561b4e43d46d8e196b48f1b0134bb15c4560d7e2ae 77054465a96c32898bf6cf8b1c0928470564ac1f01764b7f129d0dca5c210f38 347ebdb50849d5ff765fc7a8d7ebdaadb48fdd6198695e58576cae3165653b79 55f741582d53ad034d62e871c83fcd7d1d7c388ae8772152055871baa4f5df55 1a94c340f2e556a8d4eec1b7bd03369ce16ebaed198c9a77d29b1efcfe030cfb b8163546ab34de618d3bf873a15cd467080fe356cd6aedb0c48aa5aa165ee9d0 a6b30039521dd45b77853c838c5b306ef725be76e5fb6de1b6717392d4f24714 6ed0fa9a34d2b4a5b69466544ce313549772305653cf04b65bd7bdc858f51f99 d5c7c82736263c5c048b092b7809352933d7f8c0e24405091b3c281521305898 3267fcadb52f233d5e2f72c51024b7f534212bcd0faafaaa36479b8dece03445 fcbdad62b65fa72769ff09116ccacf38aaa5a3ac6d7b738d1e1cea70a170e978 1e0ef6f8eb29acc817ca4d711428ac5ae45f340b373b395b996fe3b13fed6258 22eab9a8926b9c0e8c4dc80debef023423a863a8f1f5b22c82e4985b0185d5bf 2b08c59fb40af5a900025c53617eecc229bc9be23c89866689b8b975ff77c2fa 0fe835c7cf0271867cc3e0f971cd2917c32f6e2e338ed2b6bdc0b28603a54624 3e8b2321871ca4491039531b2b3f7fe4d35c8ae27965ffe59d2a2a2cf9f18fe6 587c8599055f8dd92ecf63d26080a5d30d44d136a12971e9f80513bab9acbce2 d59f54f9801375355f2d0224c856c6c3a36c6b834e9b53604e22e6e4f4cb5da3 b2971050b24f4799deb216806422bff22b4e195542875a4f4ba2c24736b4a866 41b41a5e50019c3678618ada30669521e3c759b3bd8cb756e201fa48102cc3ed 5b2e7d0fd31c02b3e39bab236fa32661d73a0e2130147bf9fcb66e95287369ae 081419e4a38f8da1d08807395ac1c1cd339c8aae4d814ad99570e55283906338 fcbf6f419b44bc4e1b0f9bbfc39be13fe87b2e78922c4d820c8be3b2fa6506be 76ea0e5e7625cae18dac86a6aa4c72811799006d043d18a47bae60cbf468c62f c931524e1fead041f3d721bd89a7b07d6fa0aa01a2f392ff05b2081aa26d52d3 e68527bc8f4589a2493455b998eb06e65eaf6e5ff3dbe853b4362a1fc08daed8 d3e38c410d41301eec533265b7190b2cffed9d1564346fca042c0293b1e55850 f87b044acd74abce12574df9ce7d696613560206bb76b91359390672a613af45 ae150bc498781e1eb372bf85e01ea38be5d0436fface5ce7bb8d5b319247a2a3 6da6307d2675a1900e1d618bc2450bdeab5af4bba099fcbad3477e5e511b6d7b d14067a84c81f13874d8ebe0d002e06956ea8142dd7ef5cf4fa6af4110005a2f aebdf9818298f08cac1eafb3acaff0817b668883594e9d407f644485c8f8142c 273a3523ed6208325995a5ec44c278b19ff02cc6ae4020978cf0997a3439189b 9c62ccdc36303bec55046205c11b28b1ea9ec5cbb986915a33d9318ff1759179 8356c68b110360760f61a94f5a1fda6b18a7cefa893e9d7d907442a5bec0e403 cd8faa22f8b44449aa4cf26208aae810fe949444772e90c9c52a057fbe5f973c dc0c8e7758b18de926984a7f95903122217bd499518bf85fe9eae2fe0ef413d3 d80b4d9bd301b30cf977dbd4c7ae624000821cd92320db2c0ab7c535667a4cef 07fffef8e4ecac24cecf4830269bd680293ddd715fa28cca5679b9c229b27a61 d6b03c9b56f207ec85d80af806670639d86f1c9b615555f9ab1e882a79ca59cd 310d9517ecd79e7dc218bbe0d04218df17c7b5affe58f85a4f5d60a0e3ccb8a0 ce4ae9276f6898087a89331c085794c482b90fdae3ea0a53cb68eedcffd9d39f d5fcfd49995f8883e4699ab91ad3f1988fe68ee3172ccbec6f5fce620aaf0e24 5ad638db6dadb6432400e7e829de612d42af5176a33df423964cb33aa9399ad6 d862723428a3be33164cbfd459d2fe520cb20af00acf83ed46305528a4d2af87 79c705809f7867aeca53ecb8e1c1337c396a30d0f26dfe9a60d7e2afa5bb4bd3 af5dcc23aea91d620d313f14d6cf8d8a8a5407390ce5129e84a85b9de952d970 77042635a92bcf09b31347d97cf81a02bcbae77d793cff403eef073580e12ff0 8e1cff4ab6f6a9dbeb11dfb30790b9c3617d533e98af602a6db376395d560870 511a3403b8453de9c36f2eb376054aea024c7b8646770c54583c0525d2b628b1 df6abaf07d669a0288a5a592440576ca7c5625722d9afd48511469d8730fda64 4dae5ab284382bfe7ffb5e98204acaaac5f2535b738e6038b29d63c0e1d54e64 f61184af38b054253cb406475333f8ac59df5d170eafb717dc88775827b146ef 7cd57c08e0857013bfd8e185c5af30414b892faff8893edbf6cb52e8f1f15701e3fc994a7312a59ffb6739a79ac29d9dbe39a15d251d32e9071335f6b3b1f200a9bcb01dfc4549da8e841f6dc6953ce69190ca4e3287a46f89f08b2d3172de0a7b6e6af8dcede1accb446fb5f5ac8e5b7c5c69b91d84df74d3bdb6144f599f0cf5435754fbe8f5f1ebde202b654ee8e113fa2350a8b89b100953e7972579350c8c3e33381a66541e7f4634ccc3af9e8dbb4afba3d7266a4a298a6fc2620ca301f42a22dd34dfcb98a4fd20c8e9aa3262f88c31e1aa7c58863dfafb5670de770a21ca55cb8aaacfd7747d83f7f2e8a3905795dd1c63232662235e165852236b0cccfc49ca6d54c88f19fbaf6471ca485642450a50e5408b832069d8bec9217a0e2890ea522e7d253e6bc545ad0039d8409970f90e47926745c36bb45c2cb88e06c3d6b31ed8ec10a5576aa2b273f06197684078736dd09be2ce2c71c6539f920c80464abbd37295b8b452147b887b721502795fa154fe9bb8763dec42d8a488004dc776c3af317e8b4c614d7e63580ae18f57132a9c566199795e12a478078105f341a0be2cc5967d3881c34772d9304645f25ba8a5c56a2fdb7c7625994c5507a032ca58b717a8a745c3fe9c35050573248967f092e1cac68bfd5fc24f5ed80d4e1dcf99633ff5aacbfcc754240216f3c9f5e4e3a9b0147eed524560ad27da0925cf2ecc27e992353917545f0e711688b31214998d4e3ce0452a1b0ed7775502bfc90b68951063bd20e2492f7c7084012dbe115e11a5876de253f3233c1ebe0b3d5362b16edcb1fd3d871aadf56810f8445ef4cec14a6347337eaca6c2a303080376be971b3c7a7f89f70ed53489222acf01df8ebb62f9f92ee340fdea0aec0eb350c672fb3d7100a596f19ff37fcca067522e929f27173e34263324a01afe0d489fcc09fc6392c6a6bef0d823133b0bc4ce53cb35a1ad34fed7371519598c0f51fd54310818ca2f2614202d54054df4492091b5408bee8b6e31fa05239c790321838da7ae1de7497cc590e5b7bbb8aad6612c878760719ff79fa6ea954287017b0adb700570951b82d9cb3b98f9a9ca53fd4efe48af850b344bb6d600189704fa3759d125a8c84ae9d81b129abd084c0a9d3bc30131b9dda8d6624622a85f013df0aff402f9c6065e999ceb99669cbe196b4c2f5bfae3f715ca92505fb1a502d28512e7c79772fbc99eddfc4566643a4fd9f68be803c4d68f87b0035fbe9b0d9dd72e813716d8233addb74c2da794ffa442c6cd321789ae1cacb662de90ef0d5de16e50b9ae9702b0fb9bc310afdd4b94b82560dca24e397205440a83a24508cc3988f65184e58b041cd36c59dcd294d7240d4cac6631ec007e7138653bd5094c62a0a3cb491091cc9285f80c38199faeec22d8cf2ce4d5b058eadcb96ec902739a714c22f9ac87c702c117d815ff5a1d256c7eb0c8db4bd7ebdf71435a2c0d5358b35ad3c69c5558278a9fca218660ef637ce1cc760c832450a1d49dcedd0d9ab3665241e25cea338bfee0793c85548763e3927950830f43d97f089526e8037bcc253307092ec77d90cddef3d734f04ddc1f35ff6989e695960eedd7c35900493a82c79b7331ac62f9b3b0f6129ec3c703a5af91d8ccb624decad400773c0414cd4decf801307c55c6e8f655fd5f6fd2c9d9447444df41a00730361d1c0d00158f07b79d98fa4891a07057490b3d1904dfd3d65bdcc2710a4a995bfc3c640ea9dd502f7c47ba5b3be28a31b81f0e1a55626703841928b462051b1b32ac3e0e42210518e9397a9d4f78beb7279962ac7f3602ee365af3f768b9cfe975c8a10ac582d222d11902bf6ee03441ffaa87864bbeaafe4a772ee0cf328a461696fb001776aa1bf87b413de64b5a3f2d72d32647ced348fe82670e1bb4cf1020c4ba066438b279c79201f72ffe302fd1bf2834293654fb71725acf23031b6b35abd707b0a1bd9d3d67163811529694b07574847500358eef0c7d043ec6993798aa6709a6cec3e157231676e506204c1567ec711bab11312371c016ee9c821dec0716048a69a1bb2506e79a71a05625faf6ba924918668f0cf1714bc4fb8405429baa02f59a739978eb00c98c6cb63ec2ace143ee292afdcaa7c4edfc3d9441016ab20722e6b1d9a139864fad6fcce2c044ecd69c2918ad212e027acd96f7d4e8d8490037a82d2db9496262945e8a8451f0d24d26962c65623b862e334b67c8f224bc082388b30cce2ea27d5ccfdab29f15592b659cb7f2357250d08d9dca5d6bec500140a568d8292997389cfb6a8f98623215ac74360c1dec91aacf25069767f724075d0215fedfbd2c6921e4939946c17f99001d2337a1c34a5d6dffbde6bc93800287596e42db39a2c5fa537ee09ec908e7c1c07f3a483aed9f7bb77836bf8c06057346307d8f08239232595d778e250fffe3e24bff36de4d96f2dcbea995171b0562e233cecff5ef9e272634d584a2082e2ac0d7642bc0cc8404faa0dfdb5aac0892343563d99c1a3fc069b17d29c87ceffcfdc71e13b809aa014a3d7a96357302893992602807c7b8794df2651c95325e2b1fd897c92ded856ddc4530b33c8607a5545d687d540f0ab83a66abd37bac69d929744f872e7f3f6657b7f401f6f5027656238ec597e75c6fe3b1dc5fce1ea856ab41f3893f78e67685adf04541b200e35a065c7bbd063e287ca41cb70056d5a8fd99e4ff1e8f43fe44d93e2a68130bd6c375350ed63cdab9f80f4d1cd1c713bc7bf10b566d3dec60932f5a3b0fd008a526a7218f190911258033d19fc6da7516fad2afed3d32015c22ea1cd05b3d07cd977222973d06333be7dfa6be673b34e9c664a17825bccfe231805083038b00fd68cacea07fefeb907de204af93f864ab84c88b4bc613a6a89b71f6d233800e6369ead568c279b1449635edd83660b7ee1d4071ad28e944fa43c1955ee7610b89b1845f7539c67cbc8dfdd7c5c7dc25b11ea086c4c63fe910c5e34cb0ca4804f3586da6bb24518dd095006bd0fd1c7b05ba50a53d3bc2af83745b62bef4cb0349dd1537f0273c93a4f75998252aeb31ec359bd21c7965ef86c68455154f8b08c7b28c48a585117d52e5af65419ccb5276d03c027606e52a2275557b9da4150bd6f12d2c929690fcd9996eb353c8d07d36fe9a76fc8d02f278c2db900bbd1709e5784999463070152dec909940230bf026e1a4531cbf036c9b7d4b2fcad7970daa22541419789489ef4627d4cbbecae0e11ec18442c3f1879bd55fa44b1bc20bf831ad0228c1b0cd03b4881e506b3ffd464135f4a9321131b5614684825fee063276163a2c8a579e4628c67a4acaee08b77a2b161bf3ac787e3dd5d17cab050aa86048a08e51da26d051a48224349179ae08f3f9b60e468aa6ec67309f78c30c2cd24de9d9dd61842ff8392346f691e844f0e62d0ae9ec650013e8ecabccc102351c7018d502b8ace47875f8bdf716538def176e1354109d4a058abb8c6c2c0e0c8a94ba752d74adf140546b53257a13d07b19438e6a275d6a8b265b7dc7110bcb1897763854b2fb56941ee7988004a2b8a883b074df8e60265d790c896d7b0172101aa50867a603ceb5226aea1d7dcc9441bcecd7b9dc807447a5467ad45503a24712574d90a32cb150a0d6a580601628288acf39df25adad6ec8bfada83f040454eed001485efd8c3a0c51e414bc63e08c6caf834a2897db9d882d0a51d00753da3f194f5c86e66e3bbf2de22aa66ce1c3a80c0d60508ec94f366e218eea0e01e0e5c08297e7b32f9d716e57270320dcafd7042ef28055902700f8c64c7e09a774cb0db143faf27ba23a37e69b3fb7e14374cfe67b0fc7e9fa349b82c25608bf5f17f65735587c39ca4d90fbd1325d5779ed928d052d74dfd7ca22a98f610dc535a76507201582015e5baece383c15960ae81f2863d3639261651ec6bb9b01b54096d5910037ea2f816641769981c6ebd37ef75063c9272bfd32427d716c0ffe8f1f2d1d311258e33be312078c7ded7a6010265f7b24d41f6d6386aadc4e04ee4424f7fb478a3907ade2b88b86b7dd6a77df993975d1a40ff323aece43040f0b2404a5108a1be72e9fae3d569529b9f16bf7209001c40dba3467e602ddeb0aa67f82f63890522dd54a4793db8c88b3d345aebe51d7a919036970b9ef104100f16ad2decc6643df3936868c842171d4a2dda3cdff2861030f5ccd186bdff40bc8122ead68bbf9c60744f9a57609c29d960fd85b35dc9ce23d4367afc0f8260b9b0855e3637aab40bea8b8d422216c18a31abdd41cdc1bfc8bfa9657bd79160af92fb2f57b31efcc2d962637b50af0f1b818f1690237ab79ec87514d08cf2500900c860e233c47a15285dac494119a4fa109d871f75771170ffad71a0ba46d0d6405c8da66e84e4168f1ef094df3f3f1238eae6d0df61e663412e616dd95b902a9ee9d72ba27602e9e2d36d059bb77f79420783629c742cc6042f1c4142d7a0103e274ae0dd384e238ef5d249a9d32c32258c37b70caf30f5d8ddc54ebcdb805af01a51f7c1832e8207c70f982493259759effd4678d2138ab871e497ae6ce073231f6108d4285765b091eef851c4d81843602caf83f91fc1fe98b9858cb600bc76300a19bc012892f2f037299fbe1aeefe4a40b0456c08a9a7467fa7469d30f1ea19f5d1a0f0eaa801780807ef52ba99240f8af60c80ca0425343fc7b8d0404382e27e723a80701527e8bfd5ba0366325c59a94e39258279150a59a49d91404beb8fefefd88c8c2d2735130b2dfb876ca50008541eed2a92618454ecf13e906346c7925f779a9056913c07df756af6aa10c28e41173af01b88e54cd785af400c5bfdf962cca19ba5775ba5d6933fd27c94305c21a5e2dae9b4ff540a4374f040ee0700a3f287543459c90549f0be8c34a179e6914e2148134b8e9ab591e290ee6dff3f1891ebcfb73a0aea7547ae3cdec7019e6a7973dce29cd793d74828d0d685efda6329b3beadf5057dece041b2ef0c40422125f688e0f4097d5d81de6095f699fb64016c017a94c8746a420732d3e48bb87537969277f5d65e8f53a0a0692de1bd94d1427eaec1ef740b58c3c5dbc7597c2f01d2fb864bab3b5bf69c20ae0e061fff2382732f8d43f18aa065086fcab99cf59225d381c5df0728bb68700918cbdfa4f3869336181b10f223ec8c25686774e37414d6fd5fbc5a9b8a52c07da5109070aa0b556a3694dbc87028aaa2860ad7f3b820abdbfb00bc23d2f580488527bdb90607de0f639473ff4cebb535cccfc664a57f728269900e0d66af20fbfe5e6078f1303b97fd12a11f73393bf2ebb5475946c400ad04735578364bc06ecc44b94711a150cffa1e7aa4ef0288232e4e9a5ee284553d1d4f74802b3ec05a03779cddb582aad51793477e886e1a6235d29a052d3d360d947391c78f19907e699fb47476f69025a61063f9d7258daaea51008f3861c13f4f23864ae961f03da742fc03efd0341a8fc063c03a0306e2e97ae5715ce4d9507e3117234ec6909230d8420fbbe1b118bde00de12e591b049f4a09a4cc4b098402dd01a97578b01d59fde8508ba0507a466eb2536af92a6ef082b6e17850020b4e252cc9610e801c9378597c92d2a325a9511ba5e62cd48867c2f6f22208194afa6eb2a77ddc606629f637fb80399cd2732586e76da75ae721cb2c2a7a59ed3f7ea7eb5511d500898e27950721e643a5057edee1038abc7c244fae4936d4e5c5052f39427646607ae414c74de2e56ca09bf0d357f7b0443466b564744a44add3a2954d1d879b209c126dde24e1f236806eea609b3bddf1d9548c28437479c9dda4392c87726ef0a15082e18f27cec85d2ebe0780a42b4cd2f2423d558f5925042d4c18f927fc705c565ceb67ba380c55cd30aba517028845c97a5315418ed637ac6d90633007d066b8c8ecb4b8b5fa3bd3587943cd09e547a46ae1077c8be9321d45e475fbb730ce9bea601aacc16cfc7fdad6554b9ad6e1f1bc34e8056b7e07e2b3ac4f539bb02e87e657554c7dd38b5b3258f1529f622ef182653a1786004c294febb311aa3023a8a8ae9482c0b46d56c1e0424c4ff3ca83c89a511093f2c9de701fddb61c30e36ed8e7ac5c66544d779bd85f87337e5ce55b286bb38b68caca4d64b270a510dc56cc32fb8be9596685d9997a0f871b3bcf028caf0e2037de0ccd279a418030613b8603ef65753a08eace88a0460d8379575ca839f7f31b7e2efdfdd4d7ed306fe75c7e260ab6af104bd990bde639a3b1e8f1ed9191a82292a10b6fa8c58450bf9d75e5c2c7b46bcf778feb85796bd2bd2de98da3d1e13384f6593dace26a10dc5e869eb5e1ebc1ca8b45672c9247a5784a87f37412efef9287c9edfc64fe4064a610650dd0ca3fc777979310747ae12357673d598565f4fcad9ccf821af8606f24eec23850efc347542137a80c7144059186539fdf8f6e1ba7584e5dbcbf90f90cc5e71eb99612ec224c3b4d0309033fb966524d46c5d4cbceee6dee4fa0802b361075b87360925197c77315420547483fff00d74955d07fbefd9d5861a770762760f3767f04ea8306c51401a59d471a019b84b28583459d61918cd499abf0a4f09531845b3a18328fceffddfba94c4ff1bb4092e8cdc4db45a5c703d602409d482eeb86cc9d2fd7b0c238b9002df011f6752a5725f4d6eb0781c7b8e3de00bdfd43a34d6b80233b2dc79eea9d20a62ed0a19f9b91fea44297aa07065a0b40ddb76cd0f471bb6a38ea9f1024f41d7a9eaa2922e4217fe2f5e2d1e1b5b6b4d086c59e155fd07e2d31fdd63e5a11bdf60db454d8a23c799abef0cf76e958bca0eef8118671afb756582e046d282cafad789c4e7e637846ffb02de6e3532eb620084992c348ae27ee5fffc54f289e559cb993f599ee89fd3b292bfb8e17e132f03d928ca1aa1fd77834b5af3d324a119e0d62b1accee623f2307bf2c5f049cdc06248926f0277146d2c8c766292bd7e0b811e193c4d9572a204aace8602270aa0cf90fe8bff78b19e2c12c5d947528bfc7c52c7bfa7819721900284294ed915800c54d0f42a62445a1b12914f570e06bc61efc5abe01cca24ebf8e9df16b907f0514960a7427d05906650a16adc5a2e40464a90833d9ab685cc4586c5c08823204c22a92c64f78e1cc1819fb87e3103fc7520005c353e663478351aa25116f72019f22ef9e687456eea9ff6a0e69a7ca7d3b75f7761f7d2f48115794b00b7146071de6203cf00d9eb2f62c447e256ffb53e5446532f5a0c1a0f8b21b0837e6c9048ec54fe3a31e9867d6bf3d930d32e91bb51c896efd962a57a95ea18f6c109b015ef81c9a1ab89af622802712ed62a1fe64dcab614a8842aa6b58bf6a795dcf01c90e1884a44349c5e6039323c2354fc84e7f4678a8cdf9e16c9b06cf0929470e778a8661b51fadfaf9095742ca863dc7fda83bb1a1d0dd4f66d5c729092fbd0bd6e8695bfbfbebae817de12bcda3dad6a0fa9e26510a64d66733b3196468d106bae06d42a689756e7b33426df876add7a93aa100ca076892185a071e0c2ba1023fba203311aa44a543a9a54354194a56bac5274c24185d5c864d9d26f550e10ec6b321d2a5bef3c4256df894c47353352631228af899da43fe0d5c0793a7530f8adb2c212de372886191cb5dcbb08c0d8d52b75435d0467880a1bad2594ab400a2842e62858640db4b5683ed603ee39ed52bc102b645d033ab6eea389154e50834c3da1ae7462c4a978854cc778d8cc3c57b6a22aeb0e4eb2cc71026be6f5f0f88ff14cf792d9a578c5963a835495e0a3ba994119a172d81b71c90461a95e902c8f72ca64fb8c60dd580ed3346b6ebdb4cf091b0a403197f63158ae9dbb704060e6f2b706f4567d86aa1c1df66c6787c0e9ce3f78b55ec41be0d47369b697d02eebe577a0a38fc2eecab760ef13aafecdbabaad1711178154e2145a296c48601d3f42377aab7b1b1b1a5030b6ffd4a42c8fd85a9df51ae504250cda6d29a6e0ef78d3b6b53a95f1ce19d32b8f29daf61d28394803ccafc7017b3441828343d08073ef79815a328345d5b349f47c454a2bdcf068ff4e03feb3c2439a65958350c1c0f81fc95d6bd7352831e6db3db5f6b74b67a9f70df295e056bc124d3b34508ffe68f2dd7a1f27c2c4a069d74339ac8ca7e75ef66e9fc613e21fec46b828b02462f465a735e2cc4ae75a4031acbbeb2039c860d1444dc4c890e2ced44e88104a9671e05f8e930411b24d8ca5f7d97f18766cab57fc95e4983706ee6e3bafe0feea59ed629975f2f75070ce376cc6d8b269c309ca5c17d4953959231dff60f0ff8ef358585833b683a858f584bae465bfc7f1d77e4983a2b7ae8a48b87dbe90f639642b98d49652182e96d48303002684cd077cff35c38ac834a7891faf076064ddf9076cb578d0ba0244dd359b9c84042daee952aef3ae092d8086e0a058501f9a0593cec2f2da3dc1dcb9a31d029986b7d048a151b3427752837f2b45e4c04a32d742bce6167471468b4ecd449ff0a4473659595f6c965f95361ad2b419909adc3b50719e9e29a305ef06914eabbf1cc796d230a2e14a781b7d87a3a145a0d759b603cf1a85f812aaa5e5bed07d24c6905f232ff0e3ca056a6950b9d128d081eb0ac99bd40c1dec8762b775dd372798c542b9978e78bea8f9967e4ffb0a20f43d1007dd6e75b0779e8cb8c26c64c6b6ca0e8ef8bb80c4cd5ff3a8624d6ae0152ad3f3fe9b78ea266840e386d0784b8e225bf573c53aacecfa40ff2caafdf03088ea3b1c699cc76d1e55c881c60c5aaee9034705b81f507e5efee2a404dee0cd597bf40cea47f2dcf2d4e1c984e03b8882ea27bb59bd1a27f3465ee4bff64016d06357e224835138a8f5d0cc86eaac6437516955ebfb381ccafd82a75d2ee0c967f14bb2e6da9489229c9515a248a0ddfb71a37033dc2b2c68f658f245ff60c77bc88ce23097db9ad8005a797799c1e1e03f9a7c3b225300009a9f49dfc570d8b7d2975e4921ec398876e004fba6e7ff78e1f233f80d15b1473a927de2f3c0b3ed372fbf294bee16b8bdc1e07736e6be72f883f39592633b0054ad97532950fd85fce9f9fc6e32abf53eb52163a829f3ef623656d0d38d27dc2b348da59ab0be71d7dc757b874507b557f6c5926eadb62a14dfda6cfe49196fdd37fadb8b700e3535c9165d82892c7419171f240565b00d6f39de7a8ab03574378d209c45f0586e774edf0e8df9db796f5f44babe49b16c3c29660ec3276bdb2ac70bea16405bd1ef69ed4c615026c40ecf4fe1a6ade6efdfcfbcda1deabe11cf42a30644e0546e9cbf4098b4341803acb0b75593baf469a77c76acbdb14ebd93b0fc819e10390b4802ed52e19b362b6dbb6f33fc22401e7a8a199db07fc5b3d5a69146e540cb3dba59dd79c50d894ecdd7e42e2e46200268bc307846ff6a8cee364b679df0f5bbdfef1dd756d959b1a2c33958bd0a027ca5efa44ba760b3c1eb370ad791800c8b5de7eb43976428d6ef49fca5e91ff8d3d1863361f1a0e7c06501e0ef6110bf0d5c7fef53378edc1c47c6a39a18d570591706fefddb60c1a44ba67bd5a130fddc1351ecedca8e39d0cfe598180736491cd67ac9c80bd2b68c7bb87b88a8a0a75611d5f6ebf86ff81c6677c993a4cb89ab19f3f7509b21e864e0f8be2353d03c332a1d362c00085165488a927cec821e5bbda6f3182c5ff86388e45a91f390bb666aefc4a1887a19bf53cd52b76a6c1b5f8b0e3cdb702b20d485dd49de06107159fafee708680324d70d1d0882d2f166854d73587761453358cce4a74f61b06580931ea25466fd9700583a7741ca976976a4f93933d86258c36e807377ac2028b601d9b6bffb6f99bbee4f6601c4fa52fc75f0c2352503d2ff55b720531e10588a4042f56845f68c3b879725bd9c52bbefcf8aa88bc0b7dfdfe35085a0bda01b58072241b959649b6284db3d87675d411c2e6dc3c59167b7ce78d1f2ec5f0064ae0bc31230f1c3e57439d75488aa7e0a6bf38dcaaabbd8d3e93026c39e41e058519cdffe5eb857423ee1e16b95be87620319443cd745fcd81feed9ea94ed4028c1b2df810d71ed80ddf67f3a36df238c0565b7088b49fe449a4beb5b2af68033d3ec4bd14cc683a3f5ebbb95fcbaf89df82c98b6b01aab8240c9b82d201c5091fae2abc5c2f1207fe3c7da205770f9f61a37c7cd103922c377f6661c56c730b2a66cd9e1bec0f59e667e2e1ac868be92646f3b5fa12320a5997dec5ea433c08a0d0e7d60997fd8b8881289f3593ee27530f801dd7801a734401c1f0a4b59601ca05ec4cd5cd46d3e9a8f324a4496c1c9e10d8e263c748b4e0a1391bb4adcc0b55c338e6968a017f345b23fb92887596dea2b23e16dd7f995b9a28492b4f99064df57ef2d88a5ced37fc225b9ef91c33a901bb98de626e0d9b4a5f38dc14e507ecd4e673052d4df1d8ffe72559cde91ef399cfe89619b98cab194436304f5b06310ef9927d976a6bd621852870b39266bef37077361c11990c1664d19dfb370693b206262acd8953763f14da238f8673ca658165b7171493855f956b27bb0600f2c2e479ad080ec6794d3205d8818f805dc9d5c0e22c09c9e3b8344aab29c2037b7dd1e308a80b8e9024b63b6726987d826e15f73918aee4fcabed8faf166e09c3ac33b134932e5b29235e947af47c73f754619f0aac605d548f0ed47994d60038b5570c85b263a592fbceeca5d4b11df867f59e47d8b54577007565b08dda0f658f2b6bb551f399a4334215a57467a06031f33f42733f2bc7549d982104ba0fde2454b0d62dc47cea983ad61df5742f86463cdb03a1bce8d3bb776e686cee076a8b0a56edc13b5d9bf33d237647541258e0a9df1d8efee2f119e5b728c55e0b84fb81a8905efbd65c9bd59ddd68694e6502e37d1cbbe3753dfc3b13f99ddc08520ecd06fb08d4ef0024eae007555ace9f5ae383e01f65d9159301cab88b8d0cb1be2f569a03d6219f921fb22f401ee93c66518acf36d1111c7d708ff07870070fe403298be1cc1358b7befe7edd11bd560a5bcfe4fe74583dd5f425403919040d39a9f51b81594f3d5e7148e4902e289f3c851d48442e232186f2a787cd3501e83b354a6a356bcde87a8a60c8e629033f594c8746d7f3348909d9cc644c9900c3fe6441ed665f7697e69859e4643721d5789c809a8f29de51bc00713763db03e1e34573c9a45da87cbe7f5c9de181da1844d3a05d31ebe1868e5a5e8d46f806789716e4b332ee2eb4aadbf9390f025ae132e27ea11b14b89414e1e37e95210240aeafb54178b3c0e7242b8d574137464d38861ad6428c5520b8ac1262c4f906ed43b13cc77ae0f6c22b193d1d6780a9520fc5d4db7d3341cc430de240beab0b89017f4220a49c14571378fc562a1c1a72e0ac41e8f26a0c92fb2f93b6169609ee4f170665d611ae5b912d1752a86621c7602242fc6ffd17fdb0da0983ac450407dcdd02c3d66520a2e84ae679cb358a16587c49de0aac646c3a0c1f0dd5b40b495d85c8c5fe1b8f54cea0a221ee333af747dcebbf54dbf57a5e8aaa5c1c390fc7dd7154451636d92be61ebbe4bfe0a34bf2259789d27596a18846b193738b04bd6269e3b735c153530c0fffb6bcee4b3ddffa547a768784d3f5531b3892440b0e777103934adb9d64cda2e7b5cd212f0739d5fd4736cca6902e68f3e3a8390098ec5ca36c3f524196f7a2564e02b1d0fc9536f6089801aa2407368e7fc34a03be736b0fb0978b2547985308538e614933f378af9c673d96b9b635f9d8a3320066b5d2f51d7cd4779402c290cf65111739d709f60c56236d691ec062ac43e1024f8af9d22c0e2db18eb2eae9e56c81e246954f8882b6d6bb532956099e549a0dd34185c37d631e1be50d43af3185475a77ec5e8bb2e630551f6efcbca8c0a706470b2453bd5d141aebd0fac706974c26be46ffeee6960b76a2fa31a957ebc1080132c510508df9c4adb21ac4d8ce63553bea748b5bec47d9f31ab73f342eed00accef81b44b90b12acb8b6c3365f070ad61e1f6eaff76af7744ed7b38802e0092e1c14cc20eb58df921813004926c845894c6fb0616c476f107318d1d838cd074b59b3534fbe6d5130605c7ea5d8e2e2a39f119717b140adc79c8184e63f6609d00a9d6b601384ab1e6cdbe30d3542d3b18e80e04482bc16fe302e2ff2185407a43c6409e6ad403af836cc2c22443aef11e73db63ebb0a78ac318cec7725b60b9dc52be9df6f25cdaa5d3e7515208eff72bd17703840c7e1c34bb59d31d19701af2501bed5c440d8d0969f87c986613ab8a238c15dd58e49af0c29aca6395e0f30e19d22cde89ada609078b01d9e681b5096ed905d8caba4ec6003e714e3c00f646fc49ccaa32b96a71e0bd9897ab5e6177a4d60856bd3e64e4d8b2f6e577c0a09f694bda2ead9fe73a38077aef24e11943cec845ee8e3849834523a7369d40e4d186919181e4b119c45f6380cddcd8809ec9bcad55e5110e9a00623961d860ee43b03c511445651e4a923bce69c439218065dcffa741791078a121e004a270f73895d9d08898576890a16d4e1e7c0b49689b45b7d6c5a9de9ecf970cd00410287297b346888a3f89f3f00dc8ab4ebd5948380118b53bfb8feb32631203dab08a52b148aa0c0df36c7ebcd662bdbafc1e8b890701ff5ec7c30ef4a014803dd0dfcc16e99c8dded41853f013ade0e9cca7416ea7505118b09351dd71752024602c2364a5808448ba49d1648ca74dfbfff999758f573617921bc5a7c7b9f5d6f04c353e1aff993054b1501f1a73f4c0fe1540afdf7c959ba7739ff734308a8e103893d9fffa4a41681e4a85154b81a6f82bd84942b6ca65cf69f31fda6745cfe00a5c2c831400e0844d19a90ed57529dc458e513df08bf42f0978fd9589133400e10e38dbac1eb23c78708af089ebff4f2eeea33c05aec164c15556f9d3d743a017e301d88d9691599f1293c894357d8d7bad0b393e54a9434a5482cd26f14850e33ef433f486c4ad06f862fb99af68e8edbc18ca287aa12f7e301f7c77ec02a035cdbcfda8154ab4aa1b0a7d80f926dc97309cc065e4398fb91d9caffabf3f9086f2a1b9ff468405a815b9ae8028e94bcd2c3b2915ad9587d35a0bdd0b2ee0b0c3b7cbcfe276c1bfa002af295c3eb4adddc2a7c0a8018598f42ad300c9db540095be6a4f5f6db43787aa5dea4455a888b83fd085e41e8aad4349bc4ac4f31860d24b58e3f021b5ebf3c2a40cb9ece861d9306c4bd696d033f21246ea61ba9da0a20e90fd678a8c88bc8ed19df3674815ef60f58d8b81e69ec8542f45b91ccfe0e1011c13614d0ecdf3d2f2c0a518ed1d643f90303d74adf10f7414346a73edc0b6c4e5288dc20c12485b8ba8ad41ef2ea3afee2cdbc3b6292fcfd209e9e1e5c05928e241e22bd83b979a42e7eba09175e201bbfead20d3f35f362c40f9fe9330ad9ab16730756453d1643da1eb9e604613d14de588f4830e49279ef60b8124306cfbcbd8d1c879373b477efe3aeb4540770fc6c736e50a36da3c5f5a543c4210f62283fe686106ae4cd3c175580705065cc8082e16f1f918de493af5ddd774f08ff3bb4f9e025da5ac73db749a90df8a66bdfec022494c3ecf7fe24d149b2280e6c1ff8e2bc47351c47d333f5cbd9e2753d5d4ecadc1d6c28edcd37686e55d10bf407cfae5405f1b9111738348e72cb46deff38922683fc6fc5086d8378db4b0fa421af5a15935b5de2eebed3662063fb6e978b9f767080a126cd29d1bab2b60a83d958794084828477fbe17413df480b761f61aa625000eabb8e52c9861d6700355f8b0df4e3cb0d7da481021aaa3be08cf860a6a03014120ca02382786de907507fcd15fd02c97611e532d3b5da2d73ab86af93222c6df0f81426370efb1607ad61ab1585f70525e29ce65acd67251ee7a6f0e87acecca27393c1c7f4ae2a02e8bb266e15fde5717dc64af2c3c44fe42c8f22a9893815244fbeb35279291901c8b75cb61f8185f4bd0511748e495633ba8567dc9ab9ff5e977d83f83a208a066949e29e929b2c0fcfff36fd105880dadd77df6b77b86f96455941ea7341f30ed8fe99facffc1148f5e9bd0b100a47bbb214c898da0857974351a7c52b8e13082bcc686848647659e17dc45be36884b6d6f8e9e34e67c57ef1556ceea74d2507485512f2d822825aabb0ecbc4c9e3d067995070fe08a09c8da5aa6b441fbc7082137449a73afd8d06fc89aaf3e348d399c995a5c53cc72cd2e4cf1e390dc6c02e3488ac565b7ab390e3f4f1cfe84959148261572f008a5468230c7b7f2cd230066b0225d2932aaac72796f1495dd76f38274aa10a0f71b438bc8957d598d02044b76a94f911eae0eae5677721b91a77b2e953e9ff0e867061b8af4635c3633004c7cc2ad0c0cd52ab5db1122ba7c45c54387ae6fb75a303048f3d07db281c80a1c9df56732c4336ddb79b9233218825569c76e16ca62a798e8980cdc9ef04b0507e62b494187780cfa213c965fc205be39072a1896c1e51070d183e1247be6095b46e501052839949df1fbd663c77a65bdd71564ff59cad25825c9c9680ac0057e0aaa8b73b7c0b082de7c479dfbfce1bb0fa6231dad2fede43197a419cfdd09e4550edc5fbd6771003c9a2b5d2820d82789ecce37b8a4bba7e797773f87d70307709d1b5652eae818ad5894a58b84dee9aedad422192781b54adaa6bdaefd0b4d1350230b611143634e2b6119ddaadcc870a5952ac95832bcb88f65fd8f690c6c0ae696fdf4bbf9da0bda9af8e7636deab34b8d78134acf7782ae7327489105fa849f2d3bb8527cae5fe31e10a039c0950e8aee385aba58ce21c10c8d637606788c165b268982c4ba6c5379f880231917bf69466a1cfa8619a1520f6925af050278a45b13830ea2ea3367389543d519ea43d5ab7f92bad7b1cbde1cedcd43001c9e5153890502fa1c4774f80bb602bcc17a3151a5b421b5cc95f0db5e00300bea59b35e4a5535181677b0f84952e829c86fd65faaf6f6e3bd6d4f504fbe010a1bdb3db6f4fb46f8ea5c9016e4479c466e710e8e5a9c1853c9dd3559b71ee304842f7b5783338d7febf9172b93c39c6c692cee5ec5696639e56b66aa96c03909fcd36afb7bc25ca482646cbf7daa290a9d68a2e21ecac33bb8678ecd34f75305fb24bede128bacf67556043539f520d773351e57d72004ba218cb2d347448203516f6292022e23cbaccb0e9de1e781e8db621a89e2f85417d3ed5dc575f57d023e4659273b08dfa31b92f874a50a22d3a278c0f3c51f9f7c7a79294d6806b3092e707079cf415faec287853f732a8bab31e822adf2e3f2c4e4f5bcc9175de70715b81237416fd4d4c715a67f6c6225f822061fc466f8bc920eaf1b9a2e930103b2b7a744c48c3c857ee1e03f964d912e5f5fc8a7b32b0070ee033b86b2877305edc7e1a3e5d62c48fd1465daa60c25b20825e294df525bfc4db3edbb7476df054c0cf9c9af2222753a57f495921cbd3f220d0af2c6e25f1451f81a9b351256075f57baf1ac38284bd0c1a15a7659229aad94e350921973d57012eccf7a66310a5e3dfe6f0b7c118edcb6b3c5c307352c95d8dc1c4e11c8d28ebaa50c2d5a400d5d77e0456e59b26770140a590cb6c9d81b24a1d07f44188ac9bf79561253a70bacef16ecfd3bd42d169d054901c6a1f22435fabd6568e7522dafb99b2f0d1c7a208b76321456ee2310b09a0d2dd7ffab9a49711d5417f626a88be42616800300b5500275b99f02622bdc41c5e582a0bd2790c181975294b0b26cd20104e6e7040f16d613e2f03ef51778a0a61ef2ff5061a5b743ee4bedb480ce55fa7f29d30fde3755ee6d32511496bcabff06c5c36a0bea5d4bbafd8bdd3ceef97c7f7447052510fc5d513fcf15a54459bbf9406be67afa664b29654b8b871e56fa7d0fc001b330734d8cfdb585bde1868a7dd7bedd3bc3088e605228a9a650036da5400d02fd1409cda8f1cba153cd9e4b40108332fbf6a2b3e4183acfd53c16affb4fdf0f0d41db392aadf463f9827fbb79576bc214da99e6a38ec75d997ca7fb5c2ac70a7f7449a7bb1d5819a464e0239b3a09bf2134e2d16b828e074a538012e8517603243196c4fbc3c6d1c7e06afdbff1adac90ee81e23e48125730f630a62cfab00f56042cf08d5c083e6721de490965071adfac1b673a3297a99758ce84fc31e90a61721c856df3dad3175d31e0f5916fd8c07621553456e213c3300d10faff7e0a4c15a2808f32842346fd44730fe73269544e151fcd73aafd236c74ac3263ba0466ba5a9513f9d58644fed2aeed88a2b65883a3130ec473fb7c1af38742d34e0c65890750d7203672fa714f3a4a0ad80bdf599e310a1db86e88c39ed98457cd0cec177a3b14ab4ded8d5081d0fd4266941bc16adc17a254c0cfdd8799c11a9a0b55321a8876b947e0e8efd78bf3a5f81f56e9043f8858096626a7af35c26aa50a3f3d252b6c6d238393ca02a12734ac37e204398e7b626ec64c5da4d03293550e7a3507e9e40246aec394269554c2aaf636d6717842c7662aa6e13e222e804801237e54074f7a62b75c6da5cc72f277bcc9e5268d4be7c59f9df4f5124137110933c32e20da5762a870d1c04bb8ca0d37c1217025e51dacf6adec7dd7b35dd705b85005078d50fd21d8098445e3f47e053c6b606a5e772161b6da9098495d5d04f3864f5b4729059126f719c702b32df40d987b880c1340ddf047536bba61fb07933d56e2a3335a60bb246d21b8b567610eee3d88896f20f5483992562c2bce083122428cc3c0bc51662886253240ca2cd17a99d544da627882b7a79a49cf830915c196e368193824b57867db0450845b40af9d30f112d3697ebc56c56474ff023b7b677a52be520f2db0cd810a41d5ce662252761b8d4d3939c7f7c947f0210a4ac349ddf85b1afdd3e77421e802d2d34bd57ad4bca205aa35b804e933d6960e2e94b4cf74004f3586f09b9e7f94860bcb0b40e5caa34e5ffefa776dcbb9ef0ebb1d8a9f17a393f97c8f87b26971dd5b3aedd4e710006e0c6e9c08922cc93f0d9ba05bf7ee7593a325eaaaaeb16bed025cd3029851edc374b37717a07ead540576be96f61fea02159924b23cfb299f8ba2ea3c3ce0b44bee9c98c0d2f668840baaf282cdee753de23be2a9d5ac69c8edd70441ca41c6c9efaaae3b39620be102dab6138eeef00ff1a783eda94588d81f56df03abb1bbf283fb6f7325418d900b55afc2d862fcdaa5b42b78cf1f2b584a2878e79d4503d86c752b8b132a176e0f10d68886c9d82d61e1dc757ce3a85c8c19cd5273640d48aa24ddd2bdf2b1d70726b8ddb046e182ec0240729d08f23a23133f0f717875b32d027fb09057c7230b2d83972bd63e3a54785949d4c47e1720e2223e41ab8d8bb27e8e7a144cc9e80538303d2ff736a3c5014e050e5d75d4f7d4758f5a3a5167d680a9a2616e219106981931f7ffe90ea72c1daa9b704445096b3a3d876215e19847e9e5df48d383027dcf9b6c59f7e7622ad5f22f791b4990c64758a100dc50ad96a6c5a6d4558c03 false +check_ring_signature 082a55763568afc90f9d8bd4f8d50cc39a331f26f65bd84597f987b9ee8f95fe 27bf3ef33180b18c77b1d6408e78fd127c5e70cde0ae746c937a1bcbe935a254 1 b72d5c7baaed681786fe8d8e6deb4bdc7e7b02c7f80c7c3f3a11c837b175d90c 8a6cf4a4cd02d45190ff7063386ad1b2f6fcb450383eedaa2337bb0a7b966d0912989cde25e3a5fb242aa99ea03216211ccdbbaa3ae9a1464ab660629208c007 true +check_ring_signature d7272d30ec2b65df049249d7c36fd598ee3c4af632217e1e6170e3933e1fcf28 5a53122a73b9a381fe9f4acb9f2415c543e6c9af273a5a200e69247c1e6994d3 3 14e167fd1fd859d90da2dee4f025e4f63f03f25b0142c8584ae1894e110fb623 def07039e055b0594af0de0a142edffafd4b4e31ef322434532d4d9b971bea93 494e30d6eb6012c56acbdf75d2f82f72bd962d8c6e670f8b5e5cccc02d66a733 96337d40977718e20b30b01608c956803886975d48d07e73d6c4f7e94a93040a1ef4b86f30d700eeba07fe065427a2dee97bbb0528a90cae0a4051c311863b094abb4b7ae7e246cc28c854a9b976b4fd0f94825548490d2d84039477f2b202c4141f579199d77a79646193f4a630d6c8bf9d176e1578798dc70ce91a4cbb0b0be27e135cda03e86631ca590cb6c85e86833b250ec4b2e6ef59b16cf38692af0866d412b63a35fbada78b9d6e8a4e6a973d67d271b68929356ae66bf23bba6001 false +check_ring_signature e7b77828eb3b0b383d268f963107d00a9d0d4e0765a0f1210b026367eda1f21b b265637b07d0cac514dc12daa2420eb784ad135712db95356a696c4150e54861 2 e2c5ba7fd9c3b480409761a038cb0699a208c6d4fe2c73f0cf2aaf29bba6dc68 0bc84aafc50b9e488f0d6d2f4c6d6194132bb40b3b962cb7d65e5779ec7ebc33 5dd644947c1f1934038a618a64ef97a705f3a2ce5017eadfea0d53c7061e2407525397b80d16881a6bff8bbd34acd5c4d0968c9542a4737e53df19cc512cf80a1e3a8b15922bf046d324ea803400479cd2b6517b32fe9706b5c6adfd144fab0e68184d3debd2ebf99d558b83a450a665ff9b8b7cdcf1f2a12d5f3041e8dafa00 true +check_ring_signature 57fb189868a09b9bd1b77cac8c098e31d1556184914d4320fed39bec81a7f40e 521f50503b59060b533f44b22c485f65aab9d2e9ead81e425e9d216f6d33730e 99 0302d095ae5ddc51fbe0c6267f5e67a53bbb838a535516ced040d52cfcb4c960 ed26b04aad0fb9f3074609b80a08c739d0b75ac3b4f2fc9bfc3ae275ae515cdc deee49d976980c2030b4dbed78ee8e5a3045c5860cbf18d3b75fe677368e6ed6 cf1533f7112ca2e9a84958761d9b86cf9fe9d83156550e77ec1d0c8a67b9fd51 6eb53639455cf5dd4e9a41d210ebb97c5cafcf50c1b5f04169fd5a44458ddde5 f1e4f62f74e1a0764e7441e465521755e808acf29b72c958bcd4d1aa5103055e 11930582d56b2bda5f7870a69e5bfac67f9c961086b71be8c276d1ba9cb6bab2 ef4cb0d6a722447e9ff32da845905d99bb5a152394204826db183954173fdf7b 7e0db620c425edb09aa3caed87664245daaaad40c21c8a40bdead9e556882428 32460538e10ca07e04573aa31d444eee96ebc495f813a48ae3bbf94aab5d77f4 a4cf4b9734f60d30f4fd82943a09fcd629331dd003fa6fce11777eaf98c14dbe b3451a6b9202ecac71ea776f343ed29283b9eed0dcaa3af3e4692d6bc0ae21c4 a3df2c579560315d142d482a7b4635e3ad861ad868d9c5ea14bf532a1481c3ce 7eef930599bbb767c25deb4babdc6c2a3183853b9928a6b551f5eb9db013445e 82667ae80d7753e073fdc05183175905dd4d154444fbaac55ff22fc93374e858 0ff885dbd775db4d0d2c4c68cd4ea619089723bd31d5deb0958577f19ada6711 bfa6d258856046b0999c465f252a1e3a577cf0af293a4d41e5f0cc2c64fbc360 80b74312d1b6a92c3eec615838d27c37cc87a03657162488fe5cfbc3e4152e3f 4de017331f52b08b42b6b3a61804cd8d9732cf483e89db9ba15434454f380d7c 49c160a028ca6081e25ad843d14ac984ea2a52c86ef955323279ca8352aefd56 95748c8b7d44a7ff57fbde43ec81a5b52692e2c7c705650c15166b68e8ad1a79 7242a9deadaa3a703b3fd7f72636ebb3fc0a09b463e15640ed3e515a58a1d02a 7d73dfd7185a8d935f809a0b8b564732cff7d26f20ce80d819433791176f92b1 db7c11ba78eeba986f575fc4c32cc59bf6edaa76e2abaeb286eec5b679b692c6 0b336f5e13d21cca13886e8ed8678e3492929d90d52f6e3bbe983ef61303cc5d 71bfe648bd58feb3780af58620c47aed817f7f4665f4d015ea4ae50ddfc3b765 8cdb9ad30b6d8f18c0b8211b589957b2106d748422b955337f63d3a37b1c07c1 b91d96c0f7bb0fd5294a11c86b3df1de8365abef70af239284b01bb5bdb4235f 9561ae39c9dc502e2b23cf51db5dcdeba859cd6ed962e21821418c3665dc5a8b d0e2786b1f85b7b8e65cc107e21358d48d33b252ddee064e17473c39bc308318 23ed191863d0e97614ca4c5758684c21171539112cb22f22dabaac5726cb5978 8104310611aa3937908cab7c24c4a3a5afbf05f44408993abcc55fb704004e97 4c0007083bda795e6ccf03cc5117f2f5d4bd517b638704345b9105548d13bc82 80c6e5fe4ffc82484c8247af7bda97d6c5334bc8b0077bcf6bb827ae2c6dc0be 0b8d38c1561eefdf81644b4d5dc58e0139c0ce656714ad27ee100048037be11f f27d214daaa05850b7def628f91fd68225c6728495d4f9217070fd37aa601fb1 979a141b45382cb665052d0fdc44df0f482d138459baffeb0fdddeb0b12e8794 c807d7d4df00d65630988a04a756d0556e1b91beecee3a2ca8c178cbcfefd241 845fbba3b5ed4e0a67248e0e0c6f587d1d386bdbed3e5f5983ee599b7cf224f2 a7bc83265b72be8cb731536e5d61c378ae66b9d86792fa9e41db14ecfcb53598 761ee3f7ee023bc11c57ad47fdc9137fc18cf2d09da1c2c1e8e4bf955f5e5173 9f32c63aebe19c00d580d0a094ca7397d7f4d6e8ad1208ffb10e8432f595e949 71c2e03ef57d7c11f02587282d4cb0e53522c14a68f6ec2163bcef6551d6a8a5 90b7a4e6ca510f7bad53867940235fde959ddb210db15573a3accb4ca0f7f599 a24d5baab0eb0d96a7ea1c83ab86e2954d5559ce5d030976405d488e527e43fe 1c1cbcb85036aec517f04293fd9238333c4779768a7ca534d2c11cbf3f720f79 f0d136d57a17a017e97316eb24cec1f86a8d429e832d6d2d784c54c7b9e9db71 035fdccc32adaccaff95eb5df398f78cddccf7f3e7d5fa5375f6896d3547541d a14cbbd78a60823e44b6060aabcd266099a92019013c0d3c1b1c046d04706697 e55315a3b006028d66675317bf63e95865983c0dd2904a24baa6aade55e393ea ec917d63f44a5cf0ea0ce7fc5f6223bd4496eec7d2043c8aee0a9a81ae54695b f72c11e47c58ac7a510693a50dc94063e402cb3118aa74387f388aa84319ca2a e8e12a7402697cf8147d36e636af91ff51781c4f34a0218323129fea884f40b0 5c8087313cb76ad6856945fc2fb0c03d9696433965e1df8294cfa281ba9dfecc cd23ee679c664e2c03c5a6a01a2dcc2c65a2ca969b814fc64c5dcf1018e718e0 8b217bddd19a939d52b7d4ea3edec0569ed3f668dbb54f2c68a57d66717247ac 4f80fc7f4c6e6551ab8850b1a20d8f1e1d3b7bce6ce56288810beb79d1a6b116 51cc9de099ec87e06744e2bddf90529631261c22b8164b2b43d08c5a358dcd89 13e12b50b1822ca50a8ca2863f2db7ebce148eea4e0256cafdc24815529ca8bb 0073fcf3da5507064930b22959c29c411dbc61d1122f854d6ade858c82630d00 4509cf327ce16e0379e035f33f8455447af08b5b9d887647a8f521eb7487f3a0 d5821d90490141e025f09d91c5b4c28c31c5d9e7c453aa0a4b485f93cc6d748b 8b49db7b4ce3b801394ce416495eaba18d8c292c02d35dc57a45a06bc3fbb15c abc7c132ce3b246481076ef6f3ca29058898954d50ad8afaa4007357d12ec520 5a7d3243d81a7a3712822c3eadf10a2dccf78d86ddc96e0a6f0196a2290203af 2523dd7835f44d5d140b3120ce73c9904343584922d8e009b2977c85f32e913f 8d37e302bc8c4383c543730932fe9a1422295d96ce856a2e717ef9d4a018ceff bfb8a40d4c85139d28aa5d1478bb133c0934e4752e3bbb9aad3f6f41c0e690bc f491eaa77adbbb122a96f3bc3427bd0f08b2154f4bd134485f152474d21827f0 78b72b289f4ab22a082f03973755d4bf8449113855662be83150b02740f02663 21439fb9a344dffb88cafca2959e77fc5bb472049acb03bc7cb696a04a91ddf9 eb7d74148ba83d854163a91c8ed5c6669fce8e6f16a556255885474e9283caa0 92f2c52aef5639a3e7b4055a24f732db84bafd221f0ba013e16b4a44db7ddb14 a8d4d5494b6c363947c36f829f27ead9e3664d44528659aec3a78a4d440d2a8e 46d76f48f754606389fde3c19e5f3e8df9d9a1395819a93c61fc3dd0b902d02d 321eb6e99702fd2d7213c53418ab2f2d9c257ca64a5bf21e07aca5d477c3f284 ad115e80e0e32a40af6e425e78cc9bac7ee9bd2b0835dfd6a972803018890605 a41979dc62eb3416d70b43c0bd4aa6febef0391c958227a5323c6f9ee19a8992 778c4fee1ae52089e2bf40e84473d2da23efbbe69fa7d8d1cd44e7549d14f1c2 234ba7a28e04dc4b1771691d492bf8c04b02dc7743333e6050ea198252fe0828 c7fcf2d2729fbfc2497afa913d671730d95a8d4585a1c504f7e7d6a4233b3ae3 f63b3864c52b7afa35b8dd417bd90ebb2f4eae247ff90d735a5476dcb23c7f84 4ab290b0c01a6acea54e3ae0ec302928013cadaad7eb4b55358538930d5c99a2 e1050eaece0f98ea1b8c4724d8495cba5939485c6953f0b6b86b1edfb08aaa43 32d486b5871c9d098f20d912cec3fe1a22396e2f32b81c211398bc9bb001897a 8d9f21c143d6a606e73f005eecffd2a3a662fff41aba0cc2420d19a7f8951a85 90fb3f369db8eae8320b748d3232acddb7b84798822bb1fc370d529f401bc705 1f0bb043b1dbd209e14d7ed20778480aa0f42762068097d7b8c313dcbc8ce527 67cc7aca87b762ce85d338151a419fb4eb594f159da724d0faa0216edf31555c a3098fe2308ab3c181eec8c6c4aa58785a803e855dd350578f4bde564d448c75 8c3301255374e7d37b06c393384077f80b3faff97592c0e020d31e740b923722 cdbe25f1391ff7125660403c633ee4bfaa0bf2ff87a20efe8962706ad1bae078 4e4c542e8b63ff764008a16e86119b3b4ff153b0595e07a2e22417792c09e815 39c69ef6612618dd5f5bca8923bd938dfe2f2ad29c5e259345e3e2b131a949d5 768bb732387ba6f389a7288992ad497a990e2d4b531eb743984c8cf4f8530682 5226a7532165a19a728b00261a4df3b3de15ddb9c097822d93dc2a5c5d89353c 877069645d5261ceb1a65c856a25df29e9332b15cbcc4ebc0502bdf2e9faa434 7eec25b616395ff181d0a3eb48ff96b90abf02439cfa0fcfda003d94930e322b 235872aed1e323bfa3e5d3b0b0be1fea4b614c61a14ae01c63208a787217e90b 0d73d900f3342f7e095ebe8693a5fbbadfd4cbab60a8b7e7eb77f7c8e971ba012c5e3249af90cd58b335bf1e167b878754182256cc0ad8f56817ff62dec918001a3d593037c61cd831fa00517788e201df361042ca6cfca47065c4a62cd13d04925dba1de8db9fd4729e46ea3f524684e73c56569f07ac94834f72dcbb91980a9f16842e34e9ae7d0a1d53c8f19c8a2dba2e08294c9ffb83c40f758ece9d0105e814ad2b6b2bfae8700933a90d10e7069d0ddcecfc0526b8fa16945761206d0660d15916704a36c9cdfc7f3af68dbcf869d0b850fb73b3dc5be8c147c9515f08b06f657bd706bba477966eedd44a92cb511ea0b49be65e389beb3c305553740d5cdcb1e277bd382c9a80397e092ee880ff498c83dd0776be98707ef8bde7504acc1fedd30700f95404de22d4c31c08a50cc36d11180852c8174bf0acf93c7c01a64a5eb7555a9c626d49071a50e76755fe5cac84ed7d83bbd3a96ced929fa409f57d6d48ca31a82eb23bf2e3dd7d52afc2f1bb705daf45e3420e09c4d78103095d5a12c9d4680cee625e9df0e3a6bfcb1682ac4ff7f9134b1a0d897f0b5efb0ffe7920762b5fc4d208e716814a823450ec1c5f489096d561f3b1784be55b9000f190e5cb5ba14e5e300840492cffa66b19d6e9419f7a2c2543ad8ab70438b00e29acad3981d3ae98460b7328c631f7b6f5f0abd163dd6c0d5a7c404984a67f0d2a93b83e23246870d7f95cd2801dac90344b571e9699a483c9ea4068f1a94705e740d3df1a3ce50467a00eb611edf9019cf1dbbaded905392ffaa22cd7ffb6010fa6c19b09e80b140516f1d89ca4df1b1362820fa65a32aa7bfe1db6440f260b72fc1512312150c0a3395c882858d3cf7c8982c74663829e8c2e6e5c18cafe01c1d242fa12be599deb7af747efece2ef2ee13bb2a1bbd653bd38a0cb7f5b0400746bcbe05e10800cbf91a7eb879f2af0110ab0f3dbda1ebba9b547ed3006f10e9f4675d56a9589ef37de74d53cb4bb583f31bf009f8d1b65c13acc1592262604082b6fdefc0fa1d421a2c878be4bd5f2ac6559083e89662e5cfbf9f795ebdd0079b091abb8b145f9a12e92acf717cde58a9685d4e39ea6822e176e282d13510aadb36020e6fecd165995a64f4c53666ea06fa7b04215857bf438559bdd3f5305e0429a72d3675ba80174de90f07bdb43da13081274cf25cfdd8ef0535cce1e09f4ae9b99145aba35a0eb4b23b4dfc9bdbd81b833ccad2361f98dbe3549a86e0576e038f49bf7b8c9a05ec93f75a51cc2e4bc4a833c2da393e8528180752cec0bdd1a17275d631cfebb68eacb3d73e6afbc6cc4d9ded0e603ca4bbe2563c50b01d399722c3ae02fc6c7cf4eefdbcd0eba3111f9366f1922fda4588f304766120d00e57c5d0a08617ef29a459f5eea2d3839a01ae72d38e8bb094bd2f0eaa77d0eb1237f88eb81de48f137d3fa365736f36bc8a951977f9ea8289687ec3347070c51ae576f1475886a9d98c4ad771091863fe46411f918af5a0c75eb5bc730570c7729a65e58d118f7d7d439a9e3574dbad3a1cd7ad7e5b939d5473502cab46c04e318a8c59cf3080b5537e1db6d4f06593de6ac1192d63d4e2e13d8a67bac4a09e0b0cd8aecacf9eed2b09cff69abcb2582ce2ccef69abaac033c514d9b1406017c7bf5669179f76e033e0e36f3a74d9a6e16367b9c3934796167e3ccae5f0b079414fec6d38041353e1670f66e0850ec713adb6ae9115614e3ecf49fac9ff405c9e87d03605f5a1096e1b95753bb0b8fe14e4d9ff074f1790911ed0167a85305901b3d3b4704f137e3274152f70827d6b82d65e2a9c6cfeb7197fc598b92ca0e89c6beff2051e5f63e763f387dfb8f7a8fa8306f0f5766e3566ec9d60bd2100e4245989f5972f75dbc2c6dac0f299928015aabd9be466c31cb14b09f538fba02c2e0231d0927f3811c02fdcd500bd93abdd6818f008d4ad5e1b5b9fb85fdcb0d5307fbf6f49b5b2e631a1412821bddd6964c55f39f9313bd1bc079f54506870dbc868a3a4a8806bccab1ea7da977f8b4ac93ea85de26529843c9130a6ceb8e009e9987e2e6e13bb8d3fac691f2283c3fd0fd74ad05cd5825c6540d2c6985190b2ed5fb1df0087979b3439f6d2193410c9516aaa3bedcfb39dc50b1dbb8aa9f04486ee93731927771cf2c442cd3a0ebe52e7d54fee1c33de7bcbc7b3e04a4030f039c4f8a2df66dbca09f9b928eab437af69d758d79bc3567a28f57b4d049580f322616e12e0a9865bf91550f3775be222ba230ba075111574e86988e5e31cb0b865fdeb31a9244fb26bbd7e86ec2eb98de4bd078bc2e4f3821b57c609273c80ab42f0177580ac1e9274890306c0527bf4fa834c41a17a736cd68749dd418700821122ab39c0adb796f909c096e21ce9a4d88b7be1edc7e23cc50ca5a9cc77909aad276402710fbdce5cb169b874e2c7923ea52a1955f13e2059eb219573fab0541986ad6a4c86e3e775dd6e6fdb0440ab254aa26ab64b2032fd8ec36b0991805a64b778e5492007351cea0f8c1224caf006e0fc378f627132141a661d59ba80073ef3c9a020679952e1a6aed5fb5d5b4b2956fbc1a8a625edd796ae2218bd50536ba4ddfa2282c0d0199ff07dd5abe184b303125acccac9c93bfd74cbba6620b7930c089df6a00d1fd3835b89b6c21ddcea0054f9880dddf78a2df1eef7b6a0712f759c12da821ace40f63dcd712022357ee5c5841505fe07bdcb4f3cb606c07da2240d47bc20f5f7567bb47ad74548c22a045028f6b239f014bd54880cc2600773d99c7c1c22b7f366de564656556b06d580ba800c461c7ca6e7964b8173e054919a48beae565d8f4772c70be43960346033f6239db5330ade100fee4fb6d0d8b09c66161fb73e0717164d1c94bded3f6ce6853e4ac1f03432a418937c71c0f2529d430ddbd61c2afd07f4d7c24894242d1a9d1ed5a1f8118f5715fd2dd380f3db3fbc505fcb5d7762ae9cbf063e8152f95e896f1acdc0b1fb5c7fc8ea1af050db22d9c9e2a3ec560f721dd0178ab74866d221d4f473003b9057e26d81afc0fa18eea0a0aa8eec79253864da97ccbd841d04643ba47af712bfbb25778ffb00e35f0026644b77518a3ffc4ab45c7568f6743230d4600927ff5511bda67eb910793cd84d0ae7b517787e7bd00d7f37d83e91b4e898407914d379509cc5065d0080dc25577dfe911e7acad7d475f62efb74f9f53a5639bacef1533821bb4b40d0a803391af7aa7fc681b257bf787f0db299798eb78208c0304ff0bd951017e2000eee874fd3debaf54a784ca4939155baed2d21e6483a8e525add8833e37dd2bb4161559d486c3d92ed94aa8dabca44b64f002642b6d863fca30aebce0ac8904020d0d5be69ac39c424de38d8525d76d798879683d29374154612bf005be79c5002f93ba401501c537531aa905610959615fe58283a5dc0aa794cfcba0dfaef30c7bae6d80359ce041347b680239967407fe3316832b82e09a3f6e84ca6a01a7062f332917a9aafc79f700f44f2d41b6d0c5d6d09b2074fc684f309ed547578507e2845c0df737176bd7421eb421c3fd6a2f8a249f4b053c41edf10025c484750a9b9745ab036ebadbafce42f6704ec8e299fb2c9280dbdea9d14239a9874d4f0b8e2c8836fb37a5932c80b116f8fa74c46d605dedfb3406ece1fe143f8ed2f004a983e3ca65ed28bf0a6508bf1ae4278026ebcfd1326a35a23dd39a782421bc0c922ce81ed39a2001d311c051e7bbbca2ff6bb9b8c60f5c95652cd15b79f82b0c1203e88abae07e93527c4138bf80c3e88c7858d92f0c7e3255168fd8b1af3e08b53e646e2be342f69eba59601d8e6469370ae2dc80fdf8265237a541f2b1a6092c54b9780a353d7d94fdc0f4221c6d7dbee164adb31dceb3eeebe0acdabd1a0be2afec69739f355944af8405b1b76e374c7c54d14caa7378729ff9cdcc546803aafe88c236cb52a212b6ef81fcb83f0e8ae5da4b64cccaad7804ee2a7ede7a079be51dbe5a2850b00a50670fbdeac050e3b0c3167758b94ee03509ff1839b102270f429c9be7e856bc9c7546b73e54f1630678c682a3c6e933d718fcfcc196008d31c9c0a7ced141eef1d0d3a799cab4d9eaf5a24c7e2bf753ead2891fe14302d9db7211d43a54bc59446b6d5898b8b309e643a8f6e07f42031d6abe1dcfb306f5c6cd59a417e3c1f2d62f5a34d4919d1427cce569ad7162613f7a6ea8573d0ac38419385d656713d69680f017f3501603a4c7d2b253bfa53d2ff6f21a0abe061538d48fec18a0045ae38aabe8771bcbf1086ead13f500e40be27e76fea6ea0166bfd0fe36d51ce33d45ea272c621d66af534ecadbbfc17457106a3db3fe6f0937a78656a1e68860dd8e5a84c7d5e0762d36b852382911afa51136c7c5a5eb071eb7ef741673a81611c24ebff6f88679acc966831c8dc2a5cc67b6089860120992bbc3942912fa012033a4980bdc3d1df869f8248aadabeba2007b61ed5f7408c5e2f8a3d6fcaf189abc1f0f8d91f62f569f0d3ded0898085ee034d593573e0725da8e14c92b58b6086d2e3fc5d2f7e7b0f88ca03aea085bedc4e50536e040066e4bef944812ef94959b89373fe7f8c0a7ca27b7c03646354c8b79bc273c0b0dfa6f2f762fe41fa8781adf6bef09c777f37b8bd9906b52daecde3b7f95ab7e0440b94e1e670da0f1af1d59f10aee036c03f0ad65ebb77bd4308e203d52e06a08aa70c2287cccd122427bcb4ea114c26d2fd8a40bbb64c7133447a6ea53df0000da07e2c529440b06edb63c1a95061b46d3300c22bf8e3202d45c786cae636d0a10388317cdb8eec73a4084827c8b62f68620540beda13e1c0e0de9ff4535cd06083ebc3e47875871954034dfef2ce555377e007402a0760fce9770b3b75e88097397c09e174f5f2f4c5eb8776046f3683d0fd12be4fc156b5b82b441a0ca990aad66f475cd2c30e4e0482c1eee0103a1a73253b27af53aa5c144de162c759de5fe233d07491efdb4ffdb31e94ce95c09665b2feff788b3dd9adf54bd052c1707c9c53499d845df844f998f26206b851f6f5df6c2e131db3cf50ac25a7512960d8aba6054a0b0d19ac6aae9e2755e3fa9f21cf955b93106e547513ab705de600aecbc5a1c07c34d873b4f1964157da29ed75f39271a7c0f98a7dbdf7e1b39130f41111f4f81cbd7c65efe95da8732dc76f381aa2e304a3712d6facc2c0195400c4bdfcca8b7ba36e2791c2881a546939e0dbeea5973c3ec6cd254fff499a0a902c432caaaff7bbb53f7970edf97e79f49fa739a9b44aa6f7cce322e9929f78101f893a7599b2621fe7bc0f51bb0d67c67ce89bc88330a57f12d732fc330f2fa080894e0aa4c3c61470b8a23f6f5642ac58765cca59ca5de99fb7587091517560351db251e649b9163b8f099fae9aa073daa1da9afbfae8445e8bceb0fa7fa760542f4e237c24e3029a11e249de363dc49d72ea22870ed65ec53dd49e5e51bc008b317e4ce363117698e807e84af69044e782f9f87254e9c46d282f43ca5c0fb05df8237ce70cfee61d3f7500a223ec6dadf048d26b1294ca6a3448500a248530ec5b2872d3948e14696994e99b33e4bc23084958bdc01bcb8f1fb06c648cc71056857b5667cbd31718e758275c26a20e3ff18c341c91c4a6952db80aacbc97604142b4ca955f59bc5175b15ffdbd3253b528b853c90a2481436e040b54a4ce2038cb871beb3d10e334095646aef569f3331a2c350b4cec2cbdcf208fcac3cb601c1869f3e3b7fbb8590aec9cf48d108cb42070642e7039a240087ed719aa45605aa7632836f2fc486d805c41b24f92f1507a017d1cc6b95c3fe64f91ca6b15b0cb2816b80181730a3ac1af4056729c5563fcdbda213d85bec6736551a968ded0438e47d0ab11eeb66d7636075bf99a01cc12a96c3513dfadbb6d431fd5cbc650ef1cd23a59e2cf8b2d69fe851906f6ec50d3fd784b12936ab4e3c8165b1b1600465867e13a6b62ee80e3ac1bc606a1ca61545d3a39ed06e1dfdc484aa302130060d4f1b1ddb4152b3f4b31a1ca5195e2dfb5620b7fd50fc29bd9cb2074241c90dca514c74d1c7586f740d04d2f7e73e0da7878c471d5acc41bf46d0b016a7c605c9ac9213d8ffbdce0a591296a66c6e1f3b3a0ba4bde3577df2c6366a7b457b0eee5e169361a2351f41057e0023794a7b09796800ea8dc81cb19476c226013404a99879ef1c2c89dd5b7f5f125236b4072fa9471f4e0daa42f5df91ebba29b20c07b0872077656c117381a74bd44b1deae5fc7f3d18e58716fb9dd825fc05930df99462bf7136008a016aa4c31d6bf5a461cbe0260259d530fa968f80337a6a01c739312e385242ec2f5ed53cf57fa24689462f6e86a59d4d69b05fc3b29263075cecc939e0170594d144f086b9648c2f017640e70ba320b2c772d8aeb1d374021f3cce86872834bf1a07ff33fa5851b132874788efc61eb2ff4570c024112c0e09e06dac2ff2d6fb2d85b512c3dc9d0e3e6c5d408b13a61364a85830dccde90291c92771dfd7f1c14f6c236150e1fb02a6eb8c94a3490e31d31a35709879c30bcc781d82781f2e0ad4e831a7a4e5464934512d0bf9f173ffd8a372d0fb024600d9925932f776ca8d74d132eefd2c5c5d679dc0386a6444edaf38eb1c2aff94089130363ced7c2183371558d2121581a75d75e7311d5b8e0c8acabfde085d220321cc8555bf328d53f709559380c13b8a3fb8651c8850d56899edde6fdaee770e6e8d266c5a2131115114aca76d3839c0c7d0fde6180e7c2245ac364030e8ec09194f7122c5951c66a6459d488b1b748cb7aa9b44bf3c12f1e2c93605047f65076e7e6363cb0a4c22779463c0a1f31b4516be0d765b6bedbca491a824ed1f470b0f6010759f1ef75fd17a2c459b8a979b0686efa159a9e99b3c007c253fd5f20a4e11430eada85238bc5f0bf572bb13fb4187d20ce5507dbb0c8904486ead3507283d9439a2c70ad0a1276cf1490ca25678c0f24dad2191f58f66c034d0278c03672a5b48616130d4cfa70171731b4ddaed50ec99dc199dc7607400e9680d9b009fc0686c443ac1bae0e6805d904eef32483e4c0f48ec2a6bf79f409c609a39005f1e1936895587e41c8943e94852a85041664f32d6cbcbd380d3f992190db5010b71c749a2967481d051f41fe9cb94224674205a2b0138b554bd6fb4f7934b08b9ce1af0a20dbc80c00c74437f7abac01d09a8fcd07c5133e1251d9b69f87405b8a4f675065a67ad32425a7ce1acb13a3b1d69238148d6c1ce1ab8acb2e0af0fdd41fdc9f17828b9c1a40a8295f9990f3aeab5e297d717e5cff74e673175be00a52bde94f1b18a1fe5a929f3d027afd0789210943c75f8b724e04b251a0fa401580e8c179a1456d552ce831fbe75ac1286fbf54489642823b7e15359518fb00c51fe9f65b3ab20a2e8685935489cec6112f0faaa76bd6cee29ff4ec9fb06f80049d9c24a0adb08d69328448b61dc5157a633480c9b307b0aef8b56c7c2b6200a6cd204c7c162cadc4129348a42428865c48c3275f2102212160c358b4191e50907440ed410cb286d08c0a862ca76bfc5918fbe547b378ba9f480e2e058b3a60bf54aae6b03c779a73d448131d60104b1c3109f431e77926f22d55c5cd0bae00d33965709ffa42bfb8d34caee902909e203f19022a31929595d545c248d40a6087bb3c69a8af7ff26d1a8cb4703587ed9c512eb68645ffb14647151a2a02392038026ff9bb779067aa32b7c5d08a07dea750a480a9d593b88b7097d8965151c00f74dbfb0ccd0e47d27d893f6060b58039840348d83da225abbffa168eb76540f664053d3b817e34caf099534c408c9548301eb8a9cd7742a2eec30dfb88ddd0165057c2173344384f3d4c7b1d0a24e9ffc08a76c78db039db74b151a4a102c01d3c6265f02f89e4177beb68567e983b11f4b338bc209ebb60f9a9d1a64cf4907c2040948b04fe7debb56362eb690d7f0115fc6b1888834e923aa6578a8d6cf0979159a5176f68602258ec7e4887b7fa1cdb12c76e747b0b2e13dba65c8501905439655b4a4a1e27e781c362ec1d21a49b3c8a1f60651f65be4ca94f7ba3323080c9e6fe80fedae451f5baf8f80a5676475587dbe37291b1f5442a8301128cb0ff016d87a7cff7335f33efa7fccd3b7b96c14e50d68f148946ca91deb17504805f2248d634afea08e9b53db69c952d9f4070f09b98d9cc2c3d5ca67ba8890110473ff687c73f80ff947811b5a064f0e075bb11c2f8d06556ac7f1732f69852f00095dc5242fb3ed604ed7e53b22c72e1c9214e871b5bb0b0be458d9645ed2d8680629017749a136187319a002babbb6aa8302060b8d9b444aa0c88b841328c20ee6bccd4804e986fb1a0b0a75c7c0873f848a62e00245a0c5a1c234634dd08d0c0d7b125066833383ed42000e03243db139ad91f7a095e951dd0680d33fc64d0b5e8076143b5fd86865856e5499bd066b6ccf3e79a1a98f8798114aaddc57160603dc13563f1118ec55169a9f4776af598f4a1919be4fbfd2cae404a22b6930045088ded7cc0209a6746eea7bd397bc2eb1424946e12d02b8233cf25b04fd530379a8ca57e9805a54acd859360e1cbc987632a8dcbab13039f82089d7034a40039236c3e80862dcd29540c99f0e883ef8d0802fb39a74f60e910b74d46948bb0ef0ffaa56d76422e61cdea6a649191f09a03806a00acf3bdaff01cb4cc0411701ae5dc323f9c57f0fee203cfde717b3a13d853d9966add87e951c8674ff0ef30e96fa5b00d47c23202ba3b1193f124cac394957d032875e4e26bbcdc050a2bb06d161b260079330cfe6f6424c98f4a4b651cb5c734dc1a210e545d25c2f64b60cee166cb7bb9073e6ecd9be81ca9d896a3145360d7e019aa9462aa0822279a90f false +check_ring_signature ad2dfa27c7b52ef5206e7c9b3fc8cdd681fe7d50f40cda7bd9bc56633a63da86 c648dc58239fd3c4eb97bab7d46fd10fc005218284276c33ebce03b1dc0823cf 1 a9f40e0e40bfe4bd489fc04cc459474a3dca6b3ef80eb1246db7f0a4c20d4ed7 1a33a0e9cf54ed3c8d632617b2c8cf345fc669bb6093932f1b1ed50db9bdbf068bb72c16fb3183b00ab4706fd28a3d742a1a1afbd3c4225fbaa5d7f403eaf566 false +check_ring_signature 257065e3f558f6e1b8ce567b6659bbf5958a9991763367fc0232c96d64566ceb 68079cb48c30bad04bf78ac2501c8e47cf2ca88d16beb42fb7e2897be83e0a1c 1 a781e5461f30a89f8ab9394e6d0953b9b2b9489954c216dc86454028f20aa6c7 4ee8a844f0b6d6e359f7fb155fb9e1f35030acc7c010ecf34d3c3ab70b7712016b5b6a8be995b2bfaf3a4327e942c76f3a10635a88b272b5783b7e6e55a5c616 false +check_ring_signature 283452dc87a21969134cdd73a97222f137c602950729a01f673a2da6c03efb47 e3611260ceb09969c80ab635cd694390020518f02398c409710573a195280423 112 da5373c45d0c721bc50bb24c581d4a59ed724d05bd704616f5209449922ee343 38b430c62953ba51fe2dfe40f96bf36a78da9d1ab5ebaeda8cf3e9a6c2f87a03 e29cfa5f2cc55f417ff2ccbc3c808fc711057aec6f891eaee9bdf875ff929b26 c91769035a4991d454bd2367d705be388a605ac6dede6b194d32151366e02024 e0583e362aabe798f761898e6ae7f61eaf2305456d5d88d43f15ac427d65f0b2 e1e027155c6bf992ffb3b2592adf5fce1eecec3b3192c8a1eaf7b000562976ce 61b7720f71d907467d2b38a66a3bbd897d148bb030eebf693a6c6aff09152e44 b33b518c2a36a661452f1e0e34c638c8d6a178fdd54f48e7721bd1627085146c acce8ec98d85a45c376b5966e0eb65248b7560f496c7f2cbab350afe7bc191ee f30aa7fdb55fae87bb1965c7e31c0103a273019d61c2d2006f96149b7c70b4d5 625063b4ef5138fd9adf89a4e8632f0cfb2c90d43fd4f9700b74e6c036c7c867 3c78847548e04e61cd6cc0abde7c493de5f26dfda8f54b96ebe85eda8be046b2 e95e16ddcf1662b09c713e4361569e88389845ab78d276b190a6105fa0eac147 3ad769f289601b95795c289522f8da182a33ebcec6012fab58603fb7eb2f7f70 bb50b3a316da981ccd90a72345b0d753922b89ec93f4f3fe68ca772d76227578 eb892548da6d9f08211bf213944b4e61184629facae4ffb3f949839c3e0230be acf544643c0ab99d8871b1d9c5cec0e756bdce124406395d4c4f6f1467dd92e5 608c70971c9a44d937e28180a38511b296477a9b59f029328d72990f4d0212d5 2d5ebe26c26947e0a38a17c7d7fa310b3de724800bac4ca062b20009c8a086a1 007246a99965312926fac1bc7c6b46fbb83b2cd687934fd9e84814a8ba9731ca 935ce6c124f1b96b9acecf14d60d5114249de5fcb2e6e60d091362b3909d31e9 9e3da883fba593e39d4a0b72745da8780fba5f022861d9839f86d07f2c0c2b6a 63fb8451ac3fcf76758cbe91f4a0eddba54cee0f75e7c1a10c879c9bd2e3ee6f b59e4a244dc225ac050b35e52d33309de4c1fb5975324a756e0257971ff68818 e071d30ce1bdb2c7250f4ce6a17df9254935818578f45f28b7cbf389c8efd1a6 4a8c336c736f400b866a838a92d8dc6f657ecfc022bfd13b5d8ca185b0f84368 b1d70897fb03a0d9ed986d50ce3674dcfcb8bd34e361f8ef7dcc13044b0282f7 0f8c1b4338365a58a519675f729f4596e07acb7f9bd423d956ccd8c0cd447482 2da68005492eaf73e03bb8da52c9c00691e55992478b5a8343b59717cd698639 6651a759ab88d94cf5dea9eeff4231767c50d7caf1957bc90dd136acd07b6eea f421b01a49f529bf81624be72f20018721556002497cd844dc9c0582d46fd16b 16c885a676016da4b1030b3b390a2e4a437f8e690be37abb6fbb2e000da1e929 811a3c0f63e11e3c69d3836964eb235d63783bc3fa5b7d0b2440e32413f86d45 41b036668dffbcfea453bc1248adafc6373a25d0869f37aea2915f52c325cbeb 38ca168d5f0b521ae54a26d031cc3bfc6c480125e12fc292e990020521a272fd 03e6e40e69829c0966a426995f02b1c00b19007128a459e3d8cae6ce1069c695 ffdd39203cda580c96b75a4163d78bd7c4edf894b5e6b5c9cecb96593506dbac 57bc6b8fac4871b6dd9fe8f3ec41598f4d81d88f61471e1c305e8f0d003f1980 bf51d3c81aaea91a827a9e282581f06825d3a40d1c1199d4fdf6a209aee49ba0 65da2942d2e7d81fba9368b0bc38cc609f2cf4a8c90aac0afbfc8ecffc5f63f5 a281eea53726a101d0dbf2f2fff83836d720fbe04e6be92868e6e88da94c3e3a 3199feac211e836afc93a518145a09135b2429c4b3f7f4b5707acf7b1a9e263d ec6b47b4d8bcca62541ce10ba9ac65d4569477176309d50e3b0faff468417d40 8140bef6738dfdf6c47c8650aceb01ac5d68d21b15aaaf003907149cebae2503 679bfcba4c53d837b5668372f0346e2226672d6d9f5366331d5acb832b42e74a 6d72a6b5f6e42d642f2294d7b7c02a5f2d51760f10b09ac3eb37bc4f13c817df 54d587f61b17517573e4e33e9ec702ee14c7281672ba0e12ca3238669a69e8b6 d5053f59be92e995c76905a7555837dbac7d9071470ff73f6a8f6d1efee24c83 05aef0757b6763f70303e3ccbe739df016a466ae573b94bac20d5ecb8e63031b d8983b6ac995b6d80cf66a7218300f686fe4ea4fff59d5d8b54c6260b3749cb9 960cf2f96f33976cf294afb6942cf4c775c684b08700c871835f3925ee96fb04 d93cd90b145608863216086cd948dba1eb1d2365256312dc69ebb620154afe1f 350d3f6f024ee28d1444618c3a7ef33eca4dfb12d850d81ba8e63c5640d3db8e a58d5982e7995e85ca38a1ed945d2a28c78a7472b399646051bf2824f35c5e0d 5d4a5f8ee88e6d885f86990f42364684813bb7bb24d6edd24425d4c984aae23a a6d712ba3f94742a01a500085010dbc5b4f5f1f35aa50a59b655308ad46943df 72d79ef512d3d0f57a6ca251ed3a8ff0dc40a5b80383e6fabe244224e1dc7599 d02cd315bddcba15b545e7a63292a10dd879443a129135d9010cd2c678bfc805 2eb26f5dd19d2a592091549eaec3d896d0f3e9a72ae24543d4539c520f5ede96 5038ab4bc968130b2553ca4d467d47ef4cf46eace8f38d36b6c3bbc19bad9028 ac6b7790a2232cc3af29dbb147f5438cc3d1be3e4672c69300629e6e16036c9c 60f1aabbca417d2e5cc7e0d71da794cb6fb2557350f800bab6d689fb8a9c3b5b d6bf07ebdcccbdd7d767cb71cb51d37b43386ee7c9b80c605055f3c5f80a4185 27f9eea19864cb048f6467be3f4d729d2b302ad8fb75448b0a01e5142e3fc9bf 0e1788d83f8ef39be196b6ccbfcc90a71177919670bd71deccdf31a1151c529c 09b17fafb7977e0189788614208203b895a0bdaf844f3b532074a1876ac3ee98 c382297c478b87bb999d325110fff19529fef3d3210a7d06e6659d5993f1c662 da6e2a428a4797e96264bb7f1822907572fd0a82e8a62e0f9425cef12ebffa07 69ea0240f5c88f255aa5c3c8092bd37b419fc0e450cf3edc8f14b2444ac0b014 e0b45f06ec46ec8e5008987b6711cb2785327b35bb4e8672e1a26badc8dc9acd 8ef96de8a03526526805c5f65f36d48f235b025df9b449729a13b7906aeeffe9 2d27d89cd6221f294897f1a09b9decdf391ed3505d2b734bbbd5cfdc24f3b1db a934c0855047a0bbf9cbcf568cd022efbb91a4949ed63c509d7b96089fd2a516 c331cce26666ad3e0b5800747d57a8b8fa41da3ede6be435278ac3aa011850d2 2a6b7ab6e2b1b1739bbcf24ddb754b82e044ea09e64e47458a541cae3352985d 0edfa7ca6b20a2ee23e4e7108b4da55870dd3208c5cef36a05388d5206876dcb 9384e86d3b4c140f60e56922a6a55c8ca9536dff07535692e5e559928e2eaf3f 3679a08f9a5c1f635347722c855c18b663e307d793a3f4881d7b60b071f8e6ec 0ec9841ce5ff3e051c510d037906468dea60e2c43a3f6649335f42eaf5d81614 bab6c56d280344b5ace9f8a0aa035889506138af4080b42ee3925974ae5617c6 c783673bd64596d531da0f143054c88ed0e4a0ef64b672c162ab2f182cdce360 66ea53e126334334838460303e5a2d5de64a54e3be739911f6b2d21efa833d29 2572595493f444e4ca3910e6058b798bf17cbdbbcbf2dd883a8504ff8145c824 f81a71440b7c3fc754f014f09d307d3fea34f42630000e8a58c8cb3e683d76be 48f72a08a3eadf36bbf9eb68f29d399260948bbdcc820319d3da2b1a998ec4b6 ac766c4b518aa40b812c1f980c2251646cc5eb23f1d3f5b9a629f69fbbf5ba93 83fd1590213f11e64f9b856dd777214a2ab1c4cd3c0eff60b02097f07b43235b a95178f17973aa9695b26f9f1106357691979cb04925526924d49e9529563323 cf4b63324c71a9ff1d1be83670df3ed4ac908f8143b227acdb257b1770ca3e65 c758efc8f0227c37059964d5d7d9b62e91d2d12e54d55a0d8261be7bafcc2571 1594d13c5b911a85346fd760079bf5f72172020d1a9a90b1e2d0a7889828f9ff 43f0ff894761c4fe1c4996b580962a0bdfd09d198f00615f58b2f81fab2df059 19d1796b811eff2df8e599bfc5e6fd7dac23b92b7f2d3643413ea9b38fb7272e 20fe831fe8f08d90db22f5ba1de3cf8db28810a348815da2de1e8633fc5c0cd3 1aa2ac4b61f7964912c5578710a88bfb51a5a658cbd69df1e0b9efa5525651ed f0326046c3cbb21d0673d75d8d09febc09aa6f9a147dac88525b50655a2777ed ba5cec3b57161505aa38f78d079d43bb4555a74c8fe705d2cd58cbdb58c6cedb af6ffe867b6779b105838467910acfd8ab1ca3e1c4d23468d59d12a9c0cc6124 b289cb0dc63b2e5f93141246281f847f406d047dabe4901b5491a6da3905af74 e6bd2d9863e55f89494ed83e7e7ba93a7aa46ae933db6d0cd3dc2effcaed0991 7ab159a0c63a3c65c9d34033fb45e3707105be60da847a6269e7638e5b453315 d580ab925d42515994e5383eea786616dfa36f68e24c52d38f90b6bfc8ff765a 3d4c7552f6dcc45101e3a34d6e6f2f5b7b4ca78044cfc8173658d456d2e99ee1 e8a8c06cc3e381470350624556b02036a50c5fad46a20c56783ff5d5f39c76cc cddb0775a122ea0b96a4a2f91d78506006eb3c36f164da705af929d5fab817f1 feacd11c8b657b954374d46dbcb22bbd50f248075725ddc180c0328cfe65ddaf d6e58f3cc1c0817871f99875c9ac3e404e465612c30103eaeaa5acbd6449b504 a5a806097f23b7c45a215d8dd9f4f62fbc4cbedab95cf47742d9833daa88f0e5 1cb3dcf2758900eeabd2b1536006c569efcc451756ebe6b03e13a5d164d9db0f d411c09c01f38151bb6192f11f4981c119b7d68f8e754947c9c92a81199deb8c c9b0e41accc74918f9450db9e0a4bf4fe4fa70edc5d58434111bb100e0a12207 95b3146bc5dec88d06339471b71cc2dcf2674daed610d057ed7be919500d4c2b fefc887a11597c1fb2ad6216c0bddcbb6d3837e035efd574bb247bbdd101b700b45ab50ec4be72741b002a0f5d15f596c2d926daae8ea48293fdd9aca2fb3b0be125d11ea19521cf3a6da948d2d0a54c6e3b7d29b91f2abc165c2793cf8a5307c130923d1e6f004f8913f9c5113122e1a09362d092633da7e8cb207af1cd56008378a4288aa7801f1e3da7c88b4b40cf41b57551bf8fee3b1282e49dd6ddb208036f3235a5931f14501e50173c3e63327396ae3d0e7b59bb7b62b924c33b9c06d0083d00cd010d8170ecae849765b44c127d5a2db1169da9ecd3b06a2633980713b5a5676ad9953105d5de0a56944cf773451aba6fcd8c5b6fcd8c300ff44a03b2df9aee064810d4cb0d09c495edbdfbcccab2e606c9e2d3103d8c8294d06b0fd97d0ab83b8db289746787c9120d5c4674d03ec8f59f6c52eb496fac143b310a1ff10b48acaa7f34f565215ee2c3cd52390bec63743b9ca3238461665041d6085f1804ee5b9924dc9b824072e0564af1a3630b42102f449f8adcda2c6a13ec07f90f830ea058b4be11b9a57d9e4362a511beb9e34b9813ed2a08ace90020360057733d321ea109716866421c897985bd77f76d732c8433f9243f61b3d1861c0fbd74b179de683d13d2217fa716fd91de84381c6747ec620260657d537848ae089321a8534ffd53fed575b12a82e7474a5aa7db47c1444080652b34b4ec46a40c0ce5198cd7961142da4cdb59fb6cfb860b84ee3d3752ad9ed74fedf30e33b90ac04705be15a3b1f58f9446652b33285ef522912f955c4600fa2a6cd9b62615009713ba56fb5a65e14ca7a9985112ab29302862b40e501484ea7906274ed034083d0dca835ea703ea591fe732e0c656a3a20489965a98adc6d600145c58a12b089affed1eefc6468ed5d2cc54d7da783909e4026700da0a96a9fac51dbbc67e0370c4e50cac8b7ce4b57ce79dbf0e8afd91ae97477f86e0d28f5d690774ac430629d127e0a946f2fd2a781c405bb35cceaf499b15b7f71d1f0a7b817df9257f02b851ca8a2a7e96d1391d1f6f676cde2d787a54e1821fed34fd16272a7bf7b706d8cead35afa211e22861ee630353ef2f2ca1fa116ea0d94c9875ce9e67fe5205be48bdb180e3a6a6e666676c70c082c6265060ba73e884b7f90e3c35d1bb50092a4b9eac12ecf7a2d63569e8fe7c88b962f5b981f834fba76f647399fb1c7e0aedd766f3b0d7fa94f28c45cfbd73ff8e65d4303cf64b192534da3c0c243b8e08aa0c77b433c633f9b7e0282681b1e6071b0e7773f6251d478d66cd3f4b2a270b9d21ba085d1a254e57cda1c0c9e5429aa1e36049858f0bf9b82153c78133fd08561099a2753f39ed9b2a42a8cc5717e199a37e6a997d0081f0fef7e2eb9284030b4df052a89dd042295eb3e616a29e95ca89639ab7ec0581d1e5091b20c0890725ae5f755e7f9c9d4420c3385684583945b8f4cd14f05d412a487b88eaf64a0f097e0d67175e28c075ac6bebc25c024be02d4e657c72e71a550798774313f90f05ccf7ee7c56ecf91168a2ffe3b8ee441d7b253279523cb85d636eb13b7dcf02de45c9863f4d393dbc87b62ee9a9a15a0ed2fb49e8e90a01fc32525d045fdc080c13a021a862aa5d5c3913fd7c0e41219d04cf53ac5221c936af4ec03c668b0c1ecaff867a68cb534fd0b1b142d4af9bfc42865064401ae0bfb842fd0ff6fd05de0ab14a7318cc901c9f546eb12a3dadf853c617e7849b69722c018098ddc002617d2dd9e6a08779fab11756930905b6c2895dfac9834d3e02b3f60190a7840a7f9042e5c25f412f4ee8fb2ef2761874ed3bb1bdfda57f01a6f0c506ee560d0aac64f5a1d782ea135a9ea1ce0f23b5ccc5f7262b5d215f7f5456fffe7adfcf08c643f258394e61cb6d93836cacafc4b919dca4d6da9b0b505bddf5ab7bf3210e940c1ed7314a19559bb7ac5783c8937f05f793d0413175cbc96d782ca517a50af3148305bd5b139566bb6cfafcc2e62f8b912e3f8c95623e0282b80583714805b4d4e7650f522ff8643b5c647f40a380739b3327b1f053b7df2a3e350e70dd0a29b84b52a4fa50136fe99f3821231362051d0a5da69884b2eb52da3e4f7b3d0e02da8ffdf5640fc4c77034472a7ba6a77bf3b437a78540ad676f0811dcd3fb0683f6e50a0d8e1a53dbf93f270a68940411bf78e0a4955d66fb09c4826d3b690376f05ed2d8f4a08609e339e2b4a8f04287b77a7bdd81f05ce67ba98063cd840ae51c0232db665c2cccd10e1142790252b9c6533f7b1a88c440b8e06aefbb270be5c232810ffb554c49b35786db75c758f962c8a40d66ab6f374c104b8a1e83021f07f3ea96f4749f67d26627f9f8556e6c7d7177c35cc581321039b883c0f40701038b8909364100ee9510241dc8d81aa11d536f435dafbf1f521e7244d54308d4fbc84fc4da7d3b94c7185596d71e50ae06e8f2f4a8b26b456a2b18f8df6e0adee77b3bba766d6f71de792b50dac8d17190ef7b9900d366f398a159bc00720f68de86c69b91ed9ffed02b7180e2fcfcb824f7a35d9392f1a5512e5055af14050f0bc6bd2a0400fd8bd5e85a35a07a1b71a003baae7d55d4f2619c1a58df9c08cbe58b3a0892918542650a142b8c6ac6cdc7c4e010726267a4e74157bcab0e0276c62d01f406aa4e41d175bfb17644734f8f42411e421059fc57378aa919f30b399ba710503766a10ad3441153926cf7521c515095488325ff4a2d76452b6c0538d5f5a55cb555e6a9706410b57be90059ea1c7d387f12b7f267151d545f61050942d19d5183cfce84f8a56246e062b38653d94d4e82563a5b470844b0992102e0799736008dfe11196cbdd92d81df70d531eaa8df966119ede58decccc4b9014e60ef2f1ade66a6fa65d10627f3e47f6f1d90a4e84448d7eed230c32407860524b576efde4a5b0867b269bcdbc2ddf65403156dbd8bb347ae96227fd1566d030c207142b1450cc5b4daf8f22e4614983b34725d37fc304ceefc922edca761058e0c9f7623dac1c32093eb60bc625a43a4fea13e37d26d0bc6cd2eb804dbab04bb7df5ce8300a0b119bab5ec882b17fa5d7073234063262239626ae46b0069037932488ac7533ec70dc6a63b243175c5fd57e1f6b94439efb5bcdad16f09d502521e20215714fbbf2cc854ee573e36741d2be1cf76863054d42899fb1815f302afba1b8afcc7682558e6345ba6d7089529b91b9f903671c40a592b074ed8c90c2d075bacd2236bc618419a7ebc6321878b06e83cc960147969c8abcd0fdea1061324147f487c4856b361148202bf3790a3e760c183309ea71f6276d4d16f490777e472547b7b2d48223745dac95d71358293a8dfbd0757eeda0498127906d50d664df58189b5166ac2274efd91d1ae8c45a3406674cb65420549aee6d48b6f08904fae33a401623dec7c65bf06b5c3f4148e5148df652d6a4f764d3f48bffd067e727cbc8e771bcd8d5482b98d206cdfce5a38dfbeca2342f9d9d8dbcc75fb0fca7a8d72de041624aec9b4246a85829afc8b606fea7a6148395744e1b76236043803175c36d4516677b9f248f344111738480209863497c7061e2472fa044c0c3b4b954bb72215088f26ed8ab39bbd8112127530b147406b8388316b29a82607e3e888c7f8d05382e8d4a8357002ff3ffb6ba7640cb25bb0d5cf1aca7df4a90ed3241ce8867668de3bc0ed52557c6bca884efa8bb1de521ab3560fa4b6af26005617e4ab0494f14c43eefe27da835596688d4997147aee0ea527a47acb214305314f1850a481cdd2e17a0ec18217f1ae03b60e4568d2c175a4b147af2123df060148de61b253c8d3ee062e31b0ad21e85b8a89036698e28c89e871dd42c33a0ab762b076535a37973b11096eff3d3886380bd3cae5f715648374bb45aef6fd0ecfdb7e625816c4c2b0148935d9956db43be1493bd4264efceab850471bfb300ca64caf0d319c0acb9335244c154ccab7e45b02f81798335ea5df0d09a16e89044bbca95ac11088957559fee83012bc9916ef144dd497f82dd449ac7c0a18390ec4d869c85ae212c902edb5fb55ffe849c61c9da8318a3423bd5b923dfade1e03e89c5e77d67d77e9112c341b43f8a51ec4f02e5f9540bbaeae374090a520f9000cd242de9cb0269f2e6769e4f4a6df9b5df8cdb2b165b6f6274c24733582b40e1f436e1a00b103773e1bbee738c0e60d66686e5d6584f6869cc2f7e7b5fbfa0520b4158eb5ae54536f8dce67316fe74909995d84609426bc7fdfd487e241ff0e0f08850093263d704fd67bdbf361ff03f498e775e47db574130c32a02aae2100b772c33a3689b85007cfe0738a7e8be8d02ede30a03b6073bb9d8d94e09f7d01a3b5bc0800f3f1549fd00284047b39ec34df5e6af0424ce69f6a1dbf19b58e03907e00d6f7b96f35ab82d0286972c884452e45184e459700c92abe96b194fc084d92a87540958490b614b03f1ad58475b96ea924f6292880860312c48d7ea80bd69c591ef0a88f41258955f250804236660bbc86e939414203ee591bd916fc0bbd8033532452265c715b643fb03697ba7bf15590be5fc79be3ad7b85df492b0cf4d72df398e17aeccbdeaeaf526cfa0b05e05392144064e742d389e107ebfc0bf296b276e1fb6ea43e7bcf13cd5b23c28178518c03492d09cf990252044ac60a0aac463b609f3597d4f285a1ffc01cbbafb91c8114ce56190a58aa3a4b88c4077f7a2a5345bcf842ecaad030515cbf1a0938a676302eac8671e550342115380ac334ff73ec1209b6f410bbed68bd69aac1e8a4d732a97f8a44dbdd6330408c0da522287af0e2c13aecfb2e4bbe09be7b19fac8d932d33a067431e2aaf6e3e604eaa1364d57a5bf85df8450b653827465489c580bdf8b7ea4ef1294667b3f500760d4eb36bab876fa491e11b0809e8386e7bd65e021f67225c9e1dfe8f0abf8072fb5a48c2f38978fea6a389204833ae204bba2dfc0866cdf4a9e862c5dc7d207d44bb2897112882b0feb616fac9e4e0647fcfd01719dbb288e2b9380d853bf096facc2881781a1224b25a822fd4b7adc3ba50d9cc0c17c39c686cc49821a8e0b845ad12c27a7c71c4a4953760a096c75a5c04444efc0c9418a819a33492292067c46cd623ba8ec443943010025ecb877ef632f7b6cbfc0a12c16b9cb3a9773065223f275a58f8a56fdbeef2834cc067c4e74fcd883106fb424292cb6470072017ee1f0df66954482dffdc522f1680e765328b853b688ae2f08b52773afa05103fe717862040f211bc891f4102f8939004fb0292b3e113b14af40e4c7fe66c201d80bb5eee0d020d62d6de1f29ba1c5201b00111d7c942b2a2ad24cd752f45a0f7e6c233a3158af1e0ffab866c1d0fb3b271d17a33de0a7e470f4b5a278602d0eb0ff7f6485407aa99b498751cc2519f009af51ebbc1437e05d2956accb747300ef8457df857be445c5381ec2e68884a232f8d78ede84f6e4cdb72140b73de4051a88fe34e4d517e8763356fa6664d205d3c19dfc00fb559ac745ff425f6a1c04babfd2fa5d644137e3f46ef8f823b544aa611c5446457d43c429b49a324ac90af9d416b8ad1fb8d4a8a6657a8134b56dba00f3881e86c6534cf5e4a7147d49072e2813a1abfea682553471b8cbd1f403f2bafec5e7fed1276659b778b09ded02df7876b84e2667373da091cff65dfbf6ba74911beec773a7ee9950c19aa4a30f7f4ac71954bcaf83d0edae600403df9c0678f1cbfe52a0b7520ffec3e3743d0dbffc3798bbdd122f7c85175bd62278d938183b51625ad0bba575b017efcc180fdd1f96654b923dfcf7123ebdc16f802dd2f4f95d0c2b78cdc9cc518c55b0a80fb423536331ca5a054514704946fd0bf32a42c37dab9aed1adf688a418bb6ab0ed92149cedb9c43f7c52c45d71ce1874d6704a6a3216934739f105e22fb1f1b07e21173fa6efe1ee385e21b4ef624fcc291000b200b51c316b65230229aeadd03de2b90895d5e6c50a82a32bc89fb5d0ffddd27aa7f340de73062e73249a5330c7bc0ba7343af53bd449848e294d679d807ad30abb168267943a11ebc809d6a036756ac588122ccff44522f21b2b15a389175263d5eb678e95f1583b90889e007034054e0f2776a27f51bfb596128e29ddce4430239f89bb0681a9fc9d356790f824579e5d0e96f447ff46022671e123b498ad964e2e4c4412ec2873cabf47c09b47973fef9af48b33963102788cca9d1e705d0f174dce554def3e6b6431cc10d94125ddc88a32554dfbb6a1d3bc18d3465d463bd585b47ad9d18175c64b89206b25e8c894552808cf753573b85c4b7c875a8598ca5bc8c69788f71b2a6076406bcdcfb65949aa28b6e1f5a8d36166661ca02501d880e0b7e5a70e17298998d05dcbda35c78f992a24defe241c897519449379810481c03ca0fe2f5598f17fd04845cfee2dd428425eb41aba3f1b53bacb95d4a163d477c2d081e8cc19e1b9203272e8a1188a65764dcd4a6c268ffb6bc2f91d56b25ddbd6017646952e73c6e06772a370a3914a2bfbae673e6fb76dd2f97255998ee2be81fc1d713adb67f0a00b24474ae3882751e4d250ea14a30564c185a50e321c386ebeabb5e0f0811dd0e9f43369648cf160642144500c17ccba9ea81d1fd04b01254addd7beb277b9e009673e13698e5c24b923012aa909dc3323d0f0361e3a548fa319f6463bd1c90021322aae72f69be16c3215ab60fd085d11940fe4b33af4c2ac45007fbb1603d01985e772350d4a5d1cdf57315a8e88c3df81ab6dd3f8f3a7c29cb9e306ae0eb0c4f4c8f6625d4d880e41c6861f4d58ea662efcc92dda3772777b26c9eb878dc0b6bf1c3e73b46076b551891ce5800bb6b6b228206a63718f0d408129b16addf0a66c99ac99b0fd31d24776afe29eca04645331a7eeb5b6c274cf33d74eb494e0b6ed758c36f83b08dccd84c5e3d047a7bab2d1db0d073453da10568bf7b1c0405993f50d2c8a2dbbbaa2d926b915429ba10d2660def0ef812d3347bf3442e210aa6fe527d14bab479d7ba20b0164abef84e6ba04ee5ea2ce8d0a1f76f19ea470d7a374605d23556940f64fe72fbb6d44fe68fe724c2f5109ec2e0477672f9f70dd65f53f95fbe8109f5a2095b7c6a3d2e2af65544e76c8c210e3dc10d450ce806a5b9f4677f606a12b4167b22a386e5828e52251641fe18305855049b32c13a03725c628dd8e713d09a07f095fc4442434cce38357ce6cd5d76243bf90ed1ef0befb70c4197ba757d5d665b13145534cba1ba00c90cc27df692864252c84b820dc9958a617dae089c5322a5ca1894d56d28b1a234e1ce6b48c65334c307ed390f4b54458ecdfef345020f3764d0d6f17443c912d86c061061a1d260b946346e093f544d36d54d3e52f9e615023a12daa87b3cb687f59975e3b30be3763351820ed3fe77732f9b985d5d092e900c5987faeffec5d1850f02de670f795a71e7430f460dc706a24d2d11441d24b59445bf9339a93b497e7c6772cbde29e5f4b0c709233022bfca095e4958450104168ae3f9a1c9b20370f24c71bdc8c02a0523dc0723da477292d72ec46a05734d749503c23edca79c8f9309645359d112e9c11e02e1b3278be2cdc6435b882bbc26e381f92a5227d2552e144eac83ec93802d940b02de41e847a904cdd57e77f659225739ecadca613cbf208aa5aa38dbceaffc0095babd8c2ff06cd42c1cd4365a2d44090d2b61b37b292c2734a3fd3111bce6051ba352eac3d9f35e59b0d6dff1349535a804bb25bb74cf2e25a9da952dbe1205101553e26704cc37ec51f0b3934554bb583d34a9a8fa1235014d385ec6469702fc4b938cf782b5fa2a92ea096596dc6c28be4795ad4fa07363f0b5bdd3ea1d0cadfdb5e98396d1fd3eee09007b29dcc536bd249c7e43bc5222f70826f4e51106e6b6b046b43d2e03911e0874504d35dc13886b3341dff4b7d86b3b7d04d4a9069474d5bce2aba16f5e3a8b4897cd3192027ca0462132b93dbf558ec1cbaa650bfe58b8e48b659d1f70f71518046f61ee7d60d80bbc61588d613569a7db6f4201c066eeb67abbfc374a0182919531ef490640da0f662570009cce19829dfb5608428e20a05edc18c6529b83c860578d986c5e06d1155ede7ef97e5b2fa0a50c04cb1f8102c90bc607240a9b35e2f53163da2389e71199bfefc26bf060ed76d00bc7337bb84671c512f8bc9f958df536ea5a4f4125a1aed0366ac6e2f22ce50702c2d7ad20098b60ab5d29cc2248b0eb3876b332eddea89d09ac18a909d9e3080f6d869e5c03e053019c31ca7d423e6bd0080e07bdc74b6e8783c5372f514f2d0fca0de40842d29ac88a7428f855dbb1ad5fed7b33e7e4738ca33331f53d0157082b78c5ed31cdb8df07477a09348fd2d8bdfbac39f1166b78466f2e22ec37d3071ea5be43a2b8f40b5720edc5e27dbf8f652590cf837d1d1943a513f4dc5e1a058a453660c90eb8dd56b9a05b949bc6ed78c806b218ed075051096e9d73efd905b6e909235e9987aa4bae2fed033b95d2466bfe19600795fb77d01dfd82e4180c9146f9d644c2a53c0eaf9bc83d16d716e1826efba465e6642b0c68a319836e08d0e2be38891b25b579d6d1b36cabf91b35f3fb90b2a57a790403e1b591b8350a77d5850ddb468700868681ab1278d70461c9a2e1a356e20aeda97441b56358012bc9fdcdb198ec8968182db16b9fe32d54ebe770e2631d7eef212805f9209d055ef9b37c6a1442b293d74f6e9efdd6e31aa27f77e257b4b7c3b4f526e4b67c0f9ac7b5c67f906acbf14c375b718596a32fdabd063e3d37ed2c5938d122edbd0fad48d1a790ca2ed64f23ec3b16fd839950298c358d3fe4aa95a60910a22a6309720b7d5cea29403911a56bd3afb670591325a7652720277e084cea63d9d9540ebe13147d5d17921a92bcf94ec0b8e05538a1ff466505e8d1f8b6fb50df853f0f235385f26c33218bbdf1aa71ead100e82a8113b995a652e23787300da3e512025706b85bac42b1596cad28b0ccad5f3dfdf614c8015eaf576e45c820440cd3016fce13d9bf3bbdc1286c259525746ca3497fcd0bee84919a318e0cdfa9130505d44dabea3c925c56e0a2254cc709f3e9e042151335bb836d2c2b06e3172167055160fcd613df419b30354d6c87baa2f572d6086071618a2004ceecbb9adb8c09b086ac819561285754ba3ec4f8170cb213a95254509b75ec31d66968fdb51806befad8bf64cb3067ece2b85d573e0246f3a28856e67a781b024032936f4b07023c631f6c7cf77cbbd974d44303e51bbd61011813b9be765b3ef102309cc1d401cc8a4080312868a21d54b2eb19728abf8a850f99c988237cb032b41bf1a99d0ba1520a0d60abfa0b772d472b6375a2bcd62919356ca1cd67fc4898da26bbe00377f537ceb2e53c1488e7795095a19b5dbc874dcf062f1a6353fa5f490711e7051df2242d0eb32b075bd3f4a1e5d06ab04c535eebc439167458d05375fbf06604e1130166d5f320e9398537d837460ed520c7d6bf37d9a09d682d5edf89407701cc2f9f1b3053b21933b5ee1fe78f0177c04a14e89c020c9fdaaef85fc02d760ed81e821b9cd38e58f2110ac410482afee27f32b15f890a664f1570817e039704f76b86e5cecf773656b1957bdb28d97efff4949532e232eca97ba822d9097e0e330bf8d8f20073995c4da29600cce67d265eb62303966ade6127eefacd8422082621afd8b3a094b62ff6c81ca8fd9f314dc25e82e0ff829bcc45c5e04475ef0cb9809c49956be307134674955a17937c14f2e61b7ab53e7c45375057e42c1b03bc962d34eecef4333000337bdc4bc4f66bbaeda1aeb339900c8a2eabc901ae0f145e608c29d43f213818595e1418475dfa3581f001e0e55d922d317923676800d762569bf0c0b72e16f9cffd66946fab1bf3bc42661ced3533d563eff656430ce050dc99211964510e2c511a0c224db7b7b040f4469cb1c644dfbe556fc4ec000619248e7180cfb032375cd0cb02cfff0644ace04d0c7af352e5ce9c848f7b02e438321676c9f3cb880520647cffcfcdc6467a1c2064f357c00d124e2b4fc405 false +check_ring_signature 2fa629074bb1f14e5fce12f8c023fc899216d01128d751c10aa8e1262553c649 ff73db62c27f38eb9c17ed162783ebb00440f5c6db35a2bbf6d7794eb91a90a3 240 c9e0d06f04c12301688130f9c28eeb6713ed5b735ae6f29aedb15b4e7540aded 7cd38264bbf17351ac4059aa2024804d0681c00838fee58c35527f66027c43af 53155fc30af71eddd4516b4b9b7edaae88bbd2f8ad4ad090ac6032c9058a53cf 4269be4efe4b4a8e18da62f01cef35f5d504c4cf4a9dbeded3ee75e6cfc62298 51317e5c2a771658c19ecc34b7635e04facd91c7d880996b48358f19400db7cb 690c5c34f9bc4944b7507b1854861bff4870910b6c0ea674596e17f79fd69fe1 10e3a3cc9232f2ec2d0ed2f7dd734265ccc8313ffbee4c7a97806b23500829b6 e0bdbb72b276ca4a7734e018e4bfc3fd5774640428733350456343e59deeb637 d892c06090435d92a72a0fa3705cfa2dc4595ec9796e915bce6d99ecdb2610d9 0ba0a53cbe7ab60d6455149f48679490486153745a1f99644efc5508f46b73d5 53f0df6e35fcca63d4a52d051320cf58da7fb66ac1328399efe1bff49af84fc6 d70d1555ee4d35ce1d45091e6199567a18e2acf736b49a72dfb61937f331b306 ef65927f5ef3d625d2e09acc1e3aca15324e3bb71b4bc263bfe20b0089c6eaee ad9954cfee995872ba1fc47245e94866c237a0067023295cd6270301130c6724 10c66f109720bb657039c8adfc628a3bab61231393b71385f4c01b84772753ee 327afe4e12c8e5e74e328674d78412d74d905b727aa5ad0eb264410bcecb0504 d9c466e75f80e3eb2c8a45c8637e28e2ff1ad4e7c4cbe46956aa6134fec09910 5c08c97cf8f0c2566a3021898e7ff2af59cc5edbab4d93e9edaa57a97fc6e5bc b5c3237e57e7aa2404f23bffbd6955e53a3d7b533b74cffb8e33194e07996402 4f0c50efe501331caf371266919035bb0a198aa4178d98713aef8ba821573d8a 22a785d3b9ce32bdd155f1e73e9f4799c7058229e55442904e4bebfd24960fe7 056ffcde9fc12869a6d06e6f03014b3300a6dd020eb193a9a11361b9dc3e7791 78adfeb60fc6f6fec361acdeb2026827948d7adc43b5d13ecb7ca261ea852d9f a7359f1272b4266d79dd59355d376dc5d63baaa09ee0d7083e4aa36527ccd06e 2abd85fe5355ca9c5de693a09930b04f01c122487ba014da153f00e381c8c3dd 3fde178d050c62be819509a06aee0a62d62841e0dc3b5c31a2b736157ef60338 577d73d20839d9e12952c0e9283f84add640e4b965054ad1ae67c395aa133d03 c4e208ed4e3706ee627049a017dd9cb52a7c6d8c2ce893961b83a2533346a4f4 c92e06ab67b1caaa6b8db17e525db2d23a40607bf2a7da667b0eee528b3f8976 3eae87ca33881495601a2c51a616a67ddf8b945ddea4e59fa361758358469b3a 043238319d2ec94644520d284e7ccdac2a1387ec4e39c298b4f4f59c62cc9baa 9a61b6ec8582b7da82d4e9bd6875a5ff89193e98d45725eda527b3f1c518d81f ee6faf6ff33727bdf13c1583450f4f840fc645856171bc0a19250e5bd0659595 1a53187606769568c2f47c2e00eca791d2995084bfef60ff5c9980bfbfa352ee 247cbbcd38552499a1bf16dfbc3b0f31a0a7a2c5ce97cf2dc5752e78b860512a bf9705cdb6284a432573683c70272c26ab185c776e52f428aa36527e3c8f6f31 5787f78fc14fa1ef32903bd1361349dfe7c6fc5e320371768b0001813204e579 4b46dc0652957c9a4f4ed8839ad2426f9e1dbe0ad573c53c8c7abcab575d2ccb a8547ff95f24f103f36eff17896f3245a9b612da0dcbb871db38ed07c4f6e1f9 3ee4c18383acac3d58d4c52f2430c5c99d2cddcc4d75561a3c7f10b5a853ed14 b23ccae6345cd697948b9b75a9d62d016371415de0263a08e405dc8fcc82e0af 4f5611c0a445d719bbd91eba92b924ad040d78e9a5e81c58d9f54dafb68d87ee 8cb7c128eff7f530cc4a38ad806ab9e1739c01561502fe8aed329cb1923ccfd4 02e46cae510c4916573e1769c23f05b94d3c44861a3a3c64b410f79af92c723c fcc9dbeff4aa9b5f8a65f16ed5c2bb6f9522219ad2c413351f3e44b6f6a480a6 cff86ff40847fe3c9e81854052271b11f1133a4ba046fc783036a0cb31104372 f0d60546fbc313210e854137979614f18281aa28e3a6f10472144423d8a80c35 b4d1477dbbc53e68d59629149c2f09c60f34ec038851eb023d7561ebeeb97b97 be55c1860b5f3a06b3a40a8523f010775ffde195bdf81c4622294a8056867011 9fad992a028a2fba2b0349d51c83976150589c613cc40f9be1ab9b7610a747e7 95ae275b62f743cc2892a6bdbf160e3a739ab18af8170cc84749b77a5c678f80 fef2dfafd326eeec8f37e40d8299578a08f7e2f68226db76f14c89c04a3cac6f 7f4afd5e01742721564b91b00ecb1160700946fb270c18f53b695bbab4d0de1e a4304d3030900aa44a28040e243142a9f6a4538fafdc9ffd96654e8347a32f3d 2a1ac1c093e0e0819552d08e04db82841924fe218f69c9df26dca0d724913b27 458ba3b572ff47587944bc2562ce0322a77d151a2d58b35cd97f4f6b5dd05bd3 7656edef2aa4a352a2ec82e55a10822b4c97516ee71117411dc47ce85181d240 ef3a35300bbac927bc2af8b4cc1976bd43af57bc9e8c522299912a93c02398b5 4f5b30783228c4d25147e0326af267555b80400f3925af7dfd50176c6a923602 f4c37c569b593c7daba4f0cdb29d974ba33f10e8953b8f82e2b8abc586a2dfb0 c4b050b3f37a2134d5d692664fcd0d66e4e70dd861093fe8029e85c531b20149 8621b43ce1af45f0883fc0110bc0eb068a69dfdad92fbef7fb049752a456c6f4 e44b15ce1d17c661d3141c248034b40258635766f0950ba7b3d7ab7845c91a96 49f7ddb243f2fec4a72b757d91d15382f149da38caa2ee1276c46843c73cddce 5314ea7a5b4451119c8ebf2b9c2d59013c75551c9e97ed3938791bdbd9eceabf c285f539fdffe4fdd44807bc16daf333522ef724dd7cabda8ab9f0159fbaeeeb 46df69ca0b96480bf9a6db3f72f140b348072286c5a1ad2116203dff3b547417 5eb4d5e13f14323731667a6230c6da5dbefcf374a6935313032a47eea38cb2cc 5ec86766aa94f6ae5b5ab917b86dcb34321d2b9b8426fbbafdfae946154db4c6 06a18bc2c1e81d0d901127771e9b714d923708cbce786c8a5437a947af8c209a e5437ec36c7e8d53d0c84656897fd4de49fd446b328c64700d734284b4afd7fd de44609cdc411d7dfb8cc80ad6ad50f0988c615739eea87c4b1f90a25ac7e076 a2f566658874517c8716788bbefd5872d0002510d2d165b71216782ef9b5c55b e983cdd682b4c9a56a14f1c0dca29190167735483356e5d30232eecba4d45b93 2c0bc3b78632991c3cad5c21213f020e87106e88a1ba5dbf6c715e491ebd2f9b 8cc896da5a27579c4d67b9a811e40ba3e93e5c3640b3ba58138a7b418b649fba 60ca859038c4cb899402bca5f3317adf5d2136f4d827f8431ca356f996d965f7 8e7e218cb3e91a49efab245f3ada7adb7b4689f1ad1e4f69fdd88b2ca713de0e 1ec3b4740814e0a54afacbd9aa1ee6c0121d1fa0b44e1c7b380ed2b41e8daaa9 2b564e6e58b09508e0941a6232b28aa295f1ce02be36e4ffbb252a833965175b 54eb1cdcacf257a679b529658c27afcc9f9242a7262547b04b02ef40b9c1a0c6 da6ef111b30774a661765e8d63fd0503bc14c21b63bdc0f0d3094668c7e4559f c31097db881e6801a891f31ed619ead0a7b883e3a3ff139d893fbcc4897d6ce2 9616aa86704360354b770af7a43cd21257d7690c339df6848c6c6e3ee4906765 ccc2f81ed4365553b58adf3eff52e265830b5c1ddb0c0f7656060cba8ee5d45d 34a39c77a008cac5f604c3e0a21530e7a00e32758918d38c6c46cdf75cef7b26 201c8543b6e57946795c79137082ef04e15f2a7e0dd635a504dbef42af58586e cb2542f862cf796f35782475380825b673b597b751685716e5cc32b19f7e6e23 117838ea2db78fd698c3b5dbfeeac4ab2f0136c3d3805307eb3e6546b345ad51 fcc4010a7226833c78bf2b21797de2053d845b7125eb1ce6a5113a46bd55291f 5d07d546f58c09dfb5b61336bf4677e8fc857f520517bbba17e76b30e2b5296b f24b1d2e87a58d033e54734d70537a3a867f872bfe6671d3c922c0ff2d38cd4f 793a1fddf478b2146463648d84845003d71fd62b8aac75f6ccafb8546de6f18a cbb8f949f7d779aae52ed4017a4a5649dda551f5e3e46187b911dd367d629039 fda502ba2e4d035ee939626c42b20e5bd2a5fc4e3fe2cd5c4144936cca231cf1 900f0961b1748f8a83771af6b68ec97111a520b553353ee44b0b8866f1e73d01 e5a4f41f64c549f1391ac216755dd7393ef62e2a4284bc3a03dbf40a04ecc738 c6bee2dc37e0b6303aa2157af2d4f7e49f1f4c5c272021aa31b0043cb2621824 d05eb4d77a59006a450a765bd2463093faeb024c2498237a3d551f170227f9a7 1c4de575c6fa6d32b4ed12a1b81bef26f175069e70265f11e33c1366a5ec6468 df740b8c83107c0b33788bbb798c0908c80228777cdcf93335eb502d245094bd 54eb9ac0b92a56586ad6f206ca6d98162656edce2fa80fb7605e6232dfc4cfcc 2ff40ed50834fb6250c2ea4015fb4d87fdaeed694deda4abf07d573ec39ceed5 fae9c49ac8f0c7c394292e4bf38fb5785436d2d795cd44003756a97e883e7f65 731aa7d26979eb96bd134a6e3e3f7c307258326fd35d3fc8e6dd98549ef2d786 684bae1d9e141c2b6340ac11565fb5223fba7a512db21e021be752339cb7f2d7 e073c48593cbc26071148e58390395b78f0cec9031fee2359d97273036bdafcd 5e1afd7ab7dc93c3a4d24fe134c02f02fe2bd190b9c7e49f3e184dabfa8e5f7f 3583cc3e81964b315ac0ac16048f9c41a16c00d4eb2e8a1bd9de77f98df35a15 4cccae799a9e4650e6590d458cbaaed613a6351e2c37d8c32a5443bfa4ee07aa 008703910272925a5bb37d08547ab29f53a947dc06fd8bcc3ef9c23bf1ae5ebe dd01ce9c1504850d910e477b64419883a1f6f299be7e89dd078fe8dacb036e3d 372ee8b282ef98c4b95dc71c68d0069a19ebd4d3290f2c1aebe70ea40cffb4ff d10c766ffc551407c2d4a337fca3917da6e4356655a6f3af54d5a3bc4b786208 afc82cf25056d52f9f8723dd7b5a0886afcb6b1bbdb1fb48f806811cfe798ef4 6bb9e3a2ed8be2f426e6b5b84d26f8c221bd64ef83f1f61736537dfcbc7bfd6b 516c46397ef4dd0b9070dd5f7b4d8400bd23e49be3188520eaac51eab72c4b65 537fe26a91264e86dc94d0a69c9be9a1c19048e6df7434d7c56660d1ea0ac0ab a64874f8739ff1ffba38e4f235df30072cf614355e6461a2f411d79ce568093b 1fa18305a20bf4ad2fed7c169870b842a6a84c58a7ac5a33deaf463e09204c77 21890cfd6a8369d42d128e410c7b4087743470e65c67b521cbae9e2780a44c89 45819d7fbbca7f37d63ce1bc6fb990f71a8bcf835268cc6c699fe3123f840c64 ff0a2f143bbd8051a15cdd83ea3dbf17605cd976447886f6d9a999030d7b935a e9156a40e05beb80d8f8bfa8c1e65b7bbe8449d10bc7ce9c587c9c6950579ec7 2402acec976a078e20618297b81f61a3b49ce9fd655da8fd018c03c0059d657c 1258954c03a6d2a8d5a005a6eda922e27eebe4c6dfb8305e5f1126ea74fa97db 8c10a147637fa771f897da16e8aa8e643a18f7b8d1f04bd643e84d5dbad01e23 cf0ce11b9573ca49d480986faaaed48ad59847e46371b66c2c02ad21c18954c9 bf2fcdd9d47f8a36f1543bba59ed5513c10c1410d7f064934d9901524e13d62c 00f0fa62c36d53247baae90447fa5868978244067eafab6de401f48fd0ae7a78 153b2a86dd3c3411e4487e2453c63ae4aaf2e8ee069f68adae1c629e135de595 2b931fb89b9b3c5b8110757a91a79758df975a5d74e6a75e7b2a55c02db62605 e184cd37a27a9ece4df04285947629c2aaf3e017d73e8cac45433720af6d0612 385838b6ce7ce3d4fc6d1a3498560744e664006b4373d791ffdb86642827e92b 28c65fd23296eda5659f4559a408894b29c19b1d0eea3846236fa78ca50d9641 2a4f6d5932c1fb22082659f4289dd9d30935a714833a66d69415829401a630fc 942e91462e6cf9946427d8344d0e037194665e38b700c9ae0c94bace8773bf84 0f82145af193ee1a459297d52aed132d668af8ff436059094e05d4cf4b8cff2b 527d6a31d3ffc100538b49d87cb5fe08856b3cab2da4b22031419412dd947fa0 48fb7dc25b54ce37227d7c5893f23c7616fe8bacb0d854256213cca4f2dd07ab ad8bbdc0c66f076405f79a53fcd50919142e41f90723b8b71ac1c3142ee130a8 1e2431969600b5b9597a5f7ad4d940cde10223985b9fa6579fd69d4547338b9b 64ef5aac29f4cef8e606676ddf413936b4ea75d276a6d462afeef4b0594d63ed 2c99ae1b86d294945fb9965ea323925349ba1b95ab0421294c575fd4cc0255f5 f2d9e680ca221dda73402a1064881dd91121a3aa3060cf2ce0deb5809c173fb7 6e1d59d01374c22508f117f02a6daaac29898257222b3cdea0ff987b74b3e84d 4a2d549be4c7e76b8f7777f2a653fa675924267d7fbe5cfd4955d04984f58576 4291e325e7bdcfcaf2b3406f832e0e2a09a103377a43368a9781748eeffb7b42 eb92258e28d47933ffdd8dce2c570f645edc460199bc4fcc0e6803b982001bf4 84552b222948c102cd171f28f5da6b53235ff402704e4805363ceeac5a847dc6 964f56f9a5148281b21d50197c8ef7b7512e43e8f29b6586343ca287e1d50b1d d5e5bd9f2f80489ef44a33bb9e8c7924d6b75a602c3971e3398318c33bc5f2f0 3958802c6c0da75422c8593c2f947979656cfda4f5c54d8eb25aff2baa066b61 d2dc465902ea82704ab6d5b1253f0f421adce920b7d4ccdf51fd71ddc4236db8 cb8cdd1eb44331fc01c7c31472554b75d2dfe82461c1405f9addc3473ebb92eb 30ae198a226935659cf532c5d6d9a6f21b0fc594038330f201a7376785383244 c0ea82f1e06895d587de38283d78b98dd97c53ee6aa1c2a3b80143c59832916c d44e0709b98ec1bd96da3e4410cab6ba3559bbeac0e23131b57b48fada8b7e92 781675c8a06a839a8acf7c9e3800a6f7e0045ac7eaef92e10ba0aef4411e8607 10d508eaa5044d99c628b2b9b932eb56ae720003309d3fbc9331e40234e70cfd 66102c3f3d45ca46e89cbb312e98106e6dc1e4d7171fe72eca94077a22ff3962 7d58b9b6ae07353572661df3b056f93fa9570610d8304ff20d75fcae2c64f485 30d29c100ab86d49540f6ac644ad9c9dec28766e5ebbaee3e80a2c6fa60898df 22ade5d6c48dc89ae4861baf202a826adb0bc75446670a659f7e3153bfb451b4 2e2715135630ca4238b04da69d367d923f58922e9230553fb3062f23d4f5b0a5 1c23a3f76f301eaaffe7119fb34706dcb3be7319d239ff434856bf448bc261bc 226f0b56902327277dd56fbad4986a044055aa8f18ed4104ecd8072cd9d36f91 98c16815667645132dbdccf92f312ebac62becadeaf687370919fd316b7e1cf4 168b5edf4088f3e27ab8da78cffc237b407dda0171df776a75c2ca483b876df0 73c8172c91eea097d41d67459095d7a0f982b842d0051109d830a66840a2b388 4b01434e6982dd7256fcab78910549b9cfd0a7d0ccfc4445edd519f4f3888c42 89e8d381ac83dc5caed07539fa4b98f196eee10dcf857d7414013632d33b7a49 a986c2e9904a404aec8dc29d363eafa5a7c44c8aab4f032f5cd16d1c7f788eab 57d9947473c31dac70d1fef555775e711641352b5817da9687df20b8de023476 00286c9288f7fc8a8d24255a2c7a26b8ee51eab26265107950751372597e4389 147c53b3c25bc1f8e2d055fcc6f3bd60db5c09674b939fcd412df60fb03225ad 96574ccc8ebecad534b3b183b62cdf1d0ca86c427f4d7fc2553815921a6d3478 ca467695bf5bdad4ea570a768452751d816c1e5520e658f1941d297c38a0b165 689646de7b86b4406ddc74cc77f9b4ae3b7a15bc846db37410311fbd29da6da4 921aa9baeffcf502ff1db05ca1da3182bf768e73d0be64609b19020748a514d3 240249aa5bc8b6a8cc0fbd79dd1caf94f78ed7dd634af73cae6455fa72195221 41ce2e93b2f7a6072b02865e34111682ebc8ba183b90c8d93d50fed2171a2e89 704b24c05f6dd0323523ad826d09f29d3b8508e3e83140bdebb3659d7c2eb36b e57c790d317cd4da2900bfc026c596dbc258fcecc77b30c10e338084b4273fb2 cc10410d46f65f17d574c7080ab8c16272b65ba2b496a43f8b0d33942f1b55a0 a08481e35eed53c1634efdab103b90fb6d82fe4cd2706d868252aab140e6f14d 47d0f1137efe4a0409847caf3859e8b777683bda2226fbae53efcec70314b7c0 f844e16df51bb820131d095fc45f450897b0c8ef57c00646243861d0bb1cca5b fed8e49d9abef06f44e910d27d0187ad6449291d04ef040ec97c52180fb43236 df84c364e9e9ad1ab225b4621361f979d240168cd55dfdbfdc35ccdae73508a9 630be78e9079973a5f9e54af5c95cef3145ecdb84b5b952442848236679d0039 40af60a7745868b7f6388ec6a586bcb2cfb07831258e2c424f241116c23f4877 231e146afe26b79c73704571a85eaa18195c8abf9e38d1f755b75367cd7d479e b54ff0e6959b2c55b4c94c30022eed9291c0a35b724f51cfe42d5afc1d01347a e1b3ec3f604e5c2801c729ea90a8b783372316c587e4de48b87668fedebc613e ff11173e7f85b3bea412a346c774177b832934ef8be3dc28e295fd1c2955ba05 07c02742614f273aabcbeb6d5c2c2b51be7a1d7f9fd3fabb1444b39c44773571 6c0b814607cbd807772f2b95c4c609ff3e5baaa93385376f00662f048c70559d 93d746523d827ba201aa7721967c0f2fa7a66d1d63a67e2259d4d15c403c9857 3d733f2ff2014102835580f8cec7f9ab880d7749d25141495b554c62b7ae7a51 51c89bbb04ca594d6e5948b5c6ab7673d4a5832c061d7a5174928946bc4be9d4 1c1665907d569d7cc3cf704c06820ba69f7427dd6903175ce37d4d4e3a415bac 4d49b68fe2e75fb27dd12dac5857810bc9c6e08674fd6803e687ee0eb8f14725 9c699fb4ba82f4ef6446a1c3ac08faf5f48eeae5e4cc9b8c419b9a1012ec98e9 ce187041ad62206a7696e8081b8387ad1bb0456fdbafd8dda57a0644ba7a4d93 3b4d61387e8f24708218aa8fd234a315e2026eaeb3fa09ee97f9d68f1f4d4ede 445de851db84e2520ebb2462a18e83a202fc3eb8476c74abc198742eb55257e4 148d111dac2b5aee9f1e0102ee7021e3107ea12d605d60337cd55ebfa23ad8a9 bb464f0c1b39e9ae5ac5b3eb28dedb88e921b9200195067aba9d5308827a844f e83b01c85ee8aeba47a440d0311afce3d71512fb163e570da162ea4d7fcc171a 5a96b1565794c048b85cec16ee424f3e144ffd5484b9c0eb53d0560ff29cd7a1 ce90cc2d8eddb6faaa8e14969ce0f06927dd6425fd669347740ec5057f4545e6 f369ba39c7c8b952de03a06f9e94ec15cec157e9920d68b91f9ec7bc070e22ff d858068517defaa3ebf3ef2c7c0f98e2afe138b5378aa22520ea7e40effcb17d 11f109fa457776a7871796b37f454e841f9d6754f24aac956f33754a51a48aa3 f18c9387fc1fc06ead47e2a9862e268c2fd50bb08a2e0e112fbe144b7594db52 a23a5fb14f22a4c40b413a9b913d8d9369b89bfa610bec79a1a0be3eff9e8219 cc761f3cf010790720a9ddbf3685f64d38e18db46466b54a68bec868982afa4f 6868a2531dab9732f5db3cf8b8753c19aa49436bc151258c62992e466ae06c2a 8a7904ac12ace5495273f9b62d5439bfe999b3805e0653cfe901f224129b6d56 15af2fb033cf8a5ba7028fc56e7e2f4fc15c501391e163404552f7002a8749b0 35c62e2932d1d3406b63144fcf99940bd90b2568ea1c0552f41910cfaebaac30 4b648fc8ae224258f2039ceef48d78addbe3cb1e9584e1364d45e996ea36ca59 93e1a1843cde0719adec40a140c263be9ce8c3b98dfc340935a5a92cdf08b744 8c065ec5225767031e38bc2ed2c825515f992dec47c2e8a5da3a26a510ec6f78 3958eb23bd7a78ebfba4bd6decfa3a0c58222534ee4f32d50887c317014b7fc5 1c9e35033f1035a96cf48c4e20cc6161865d9fed19cb17bc75136c987f757e63 f9a841447d3c4be80ff778b2b9c9971b222c3f32c9b89cc6b106dae4256fd9e0 58d592a58f4a5a57f7475ed9b63fa1157d4c8d74aa18cf1c94ee6b47bf4655f2 e8bfd5565fc96b61cd2a7f982156a43bc724ed2a4ef327e25753428cf5e3bda1 c0959391c6b7ea124211dc34dafe5a5004bf7f0186c67f5817d305da35cb50ee 1bc5494ee9c926c186ee9e682bab8ef0af8c6adeee319db5f7e5b24b5e27301d 83bca24a6d4a2fb5183ade45d9f40cb1d6400815c175dc3acd3e5528fd6ccb6c 7328427e6e4984cc44aa291594fc8fa006c0e47809ff481bfc3275bd23ca66e1 6de86d57f8fa90d18bd6a80b83fbd0eb7a6291e1d893170eabd5a92580b5c811 29caea53cd88ba594ecd5aa475b702c7513bea8cc932350c9ed518a63335fe28 5dfc1ee73710b4f53884e41dd5f9ee067a0687fc966941564f9ec09c3152ddba fa0d6301ccce950fd7e406780aa71c4cfd98264c6653771d903e192369b8003a 5b221b006050c7ec23e3a19764cd2bc838639582424bbbfef28b09b68948bcf8 fd7a95da517721081f6b80b75a4c389483da74b322781cdc7fd66ce6cbd78f64 d2f63462a0c3a90226bf9484b3a08a0451568046ee4d0662d7f7e63fcef72e0a9707e04733f2f92a92b5e333f775c1868f8df7402a80e95d44cbd2ebffe01f08235ecf6040a5f10a433f7f48dee43f7e39c1ad609c7429f1f9928a9255b41a05e13ce62699d564cf8b048e15402f456d117690316d4a19e12d523f00ebdbfe0571795bbd4ea69bee58f7de365dc34d6cfd3df6a165f6808d4b185322cec4010498c3f9f1a9a3962c1eb9d872154a7285b20e411cfac949f7730f9a6033cae300ca496b752f4c3b874e49eac1fdb5573fd88dc61905b36be6187aab2c4689a3044a0c2c499037db9a689798cacaf8844b9704b36ca9c1338874ba5dd06d56460395eae65f14e065a80dafea9f72e92974153f1bfe4d44f3f5e652bf1193e7a101d4590dc27615a412b786614d022decd27fa1331383a066ec3ddbfffb8b7dec0f3d794a2bd9615e607fb8e858eabe044c7d88aa76816df607c6695a80faa250073bbf29657f12282d30cb955714995ba81f9b4d88784679371dae224bacb56701fe166b32ea62e1dd5287fa94f92e177becf75f0b0013d89bd576f737bd953a0dc157939a3258b3e5da6719eff6b019663694453938ec301a859a2b7420a04d05ebad890a16da292c65bbd2aa296590731c5feaf173543bcdfb44a09e8b13f709d8d96824643633e1905bb916e8064cce013e6b24c55b5ce21fe3bd720c6ab00e7b499a6008b50d2083602f3acf994db9615d6237ef9aed7bbc5e6ec695a1f700e9540c2d9e592d163b6ed382d0cb57c54945c6f040340033db9ab0e493154d0e31bc560365d238c92939a29c95c7f7359a78ba013bdcba4f09283360b11bfa004c82f7f6e7839f218ff812f099bffb6bdcbc871317371f635501d06fbbd8ef0a0cd8f3a5eadc3e2742fae7f39655921e66cfc710c1033b3e2bdfbc30dde6a003f8affe0fdc194d95c4ad005478992c8ba6c1886fabf8f90761eb456b37c19f05c78c3411d0e498b103f3865f379872c85989544db06b526a0aef02a0c9e180024b8438e04b2ea3f652b7c95f2d3ecdf7eb084299ec04742de5180b01fd37bb0933bac0f493588f3283efac4ffd84e6bf32765fe139f918a47956bc72eae99700760a710587c5e88d161e2ef82753153ca6b785b05522c900480990a960112f05fec2ba31d3f4227e18ece794b7c092b58f98cc999a3bb34a13803378339c35038d774090fdbbcd161b8fb67dee0d1c5dadf7b24963a93f7db24c274091afd503dc5269bb6246a6df4a09c4b0ffd2ca3b859766cef2687f86cff67169a2074d0bb01bf70fc0435fc9aa3ac452d80199dc92ae90c54be2d6cfcda9c8f309c052074dc2a8d811cd2bf13087d62d4a35adc4651422447fcdc6fb758d58fdcaabe005fd69a978d086a459fc42bfacfb65707359131ab751619f215267ceaac5ef780d37475645c7bea85916c2e556b2ab024d3db6c38693cb2ea4cd280db7cd3f7d0f1b73f4ab5ab98fb4e08a277d300a189671d0ab2015c3f6c920433f46b1a02706d1e2ed56b0e3885d5268b2f643646426458eb6ee54c29bdc769cf72213751801b7bb7276670d3148612e7cba1893dc85739dc5683204c54e5dc1af9d574d0e0d0e19d7bbb4860ac945b1b0c8098d9832dd804d7fd68da1f905787e596df9060e288f754ddb3516ab06eac7a63de6b2796f0f8f9932b7d0b9e84c518beb7d06047a18b9b6fbdd2622d365ebe71274150e51daad41d64bd6eb831ffa03720c9602b3f077be2979ebda158d826c24933081a729e9b64371af80d026836711b423021d7ba12d2d69ea62e73bd9e1935d7d784dfe9f6afe1f20fc8ef048700ebb47047db4306fc75b550ccc1d2848679839aa79c03097e3718be4ef8a6626b58d4a08b0e4fa535d21028040d63f504a42161fdabaf49b453bddfec17e50e6bb143c0a5a23903de14b1854a8f15099a053ffe590cb6f2061e29d42eb367face0faf30d3be1ffe0b9f55367bffb93df5f67e1bc6489621c2e8522873080c8a67dfa180ccf03d28ad23e26d77fcbdff7029162e22ecb3a2eb7ce5751c1f0902052579b023633e2ed58db0f97680e5704cf593e65e953532294a561ed5ba24d3868f0410a5e4c87b85df2b7c165f2476978d7413dc780c1f002a73b995c97946699bb1c03cbd85ca321715644a264bcc33645824a633e7d321545467cf0b5792939925304d152b87239552acb74ed9466244c0cb8d4f0d5fa77b01aa6723ee7a92141f202a11293bcd597c2365de6f3cdc53c7427c20672f104a1fc43952668f8b1616f0e46a46e3e706fdb6f5594275f6b21dac2996dcbfc643527a7f96d33daf0a7d601e9f747eb258ccfef21d5c2a424884e14dcfe9d1a4b1d8a0356631ea456e72b008f31d49fe98335707c437cc9063b15151266c495da3096df49896c66854bb70ad7e57269c8edcd40e4bd425dae3b245cd5ad75ab9dd4f4e58fb11396b085fc06e08bf7a997e7fe937623a5a4a1f766f276b2fcdfde6c887c80102fab6fc48b09bec8177a46b49c66f91dac10dcf41411222c89c5acbd7b4d3f70c06955283d02a4bd63d167649aafcda2463e7bbe9def0505998dba93a037c38060cc2ce28c00a2cc633f7784cf7ea0dbc2748c9d6367e0b5a4fc109b9ee5c37c675455c3910bca5a19fad1c5cafa3f197ed278c6b73c326473a896534d0e0d638fc5206dd70fc209cf639bdbea6a438fd02cefc524ce471c4e0f1c7d72632978f71b3208a00d3cbdbd12dbbb396608772af217a4cf3cee987925234554edd385ebe183bab2026bd986f0d288121a8ca979a27bac79177d82225263338382847496516478690c4bf92194cda60ed6ef6c7862e5de505f17f48d5705c43c74562645f3ee5e80057361fe9ce8cc424dc6febd16f69c9d58aaa284cf5d38f2b686c48cb7271b870ba851ec6ce2b5ac6ee7b2ef49dd9ef2052eb605c92f0aba9ff15fad25831baa06dabce3e06391bb66d0f5d6b6c6acdf96ceeef714160dd116060a446ed87a24032888f81b531b1086d8b5f3f954a3164bf57ff7684094b944e168639d51af980d5766a6ef88314834d8255f2308d3adf77fddf06010646e2c2b8d9efce58d79074c9cb7fcaf84a167e49ba7b6e26a3323b5d7607e0a502dced1082216b0fc6a06e2d2d2e5ca5c1cda83e0687c6033c77ac475ea8e5434302e58caae068d396707724daccb054fff8ae76b747cce35a25982ede640ab93c6b326a435359a9ae80fbf62b8ea14b77a463d1982ac68253f55717c1f8be32e15993b85ccad114bcc0e9da17f2f4e987d21ce2abc7d12898f26d599096b145584b88e424d1843f27e05730e8df02a0d47b2e848c7b0f865f6493041dd07e1bb58ec2f16a2fd01d1e60ad23d9f0fb1eacd57d080df016d25acdb953df242404da0a6a4d039129a6f2b039bfb96394a0bc87341c796c319b766c90ed31284fdb2dca6180d195f2a79ed01a0e76ff185529393ee75fbb36f81a3f25111e3e939c8c7fdc9c2004e845a0e0fadf77ac8bc084375caaed0a1d82856aa638414dd8c0e02a7427c2a09f4ee3a0627a96eeea1a7028d91264bf54bb19c7022eafd603b61305e3f4f2337636bfe017f3deb8424dc648454cdf40e87bd14615b7a4376375039602b2b9e67d817220c6bed5bed94d3cbd4cddd358b27859e513f6b57e80009ef1ed43610d82c4f8e064a081c76abbdfc7be009a56d05d097dbef270b2b133e8c4360285ad35a29a10091763c47cf4958cc51293ee19383a1e589c147f1bc1a6528cb0efd6f4a02b30bd96d760fac257ecd086134137b79ea64363e9363639b74866ffe669104b13806374c1a57f764450a7f60b90661c3f2f8bc3f535565718e9b3dafa9c9698c8b01e27520cce575a635e1ec4d16bc2c00924e276dfd6a528e973c25ae2cb81c7c0f7630e73aacea34a52083bc3e85d41414d3f9998fa3d3226dd517141664d09d0c2b0a6816507eff08e9f9bdf59e1e1a88246eb8b1255be740bad5784dde5e630ee2173f79cc4dc1248f702283ec4cc5fb36f172586c95260ae484fff0f0a8430d426441a74b659e0765f9ea49953799e9ae561b74b90dc13f52d5b8217e300e0ba79bd86740f72289aeabe768d5b2401f311f5048a1c0b44798feecaab9a79308585fe4732d01bb66733d915a45337e11dce78373c391c1e3426cf5379184150cc4eb6b3e8e57e04dcaf87d3b40e1899f4efe7290501fc883354a3a2e4ba02006c930b39c77cf6469589ccb85adb1cddbd8dd33744691fef2a567f22ab00fc40c108fe89c0067ab00c8a0c13f3731fd500f0d98fd895492bd35185a81299f34089b2d6624a2ccd224b9c4de149a27e6322a0fe9e76846b24ef141308f4eb14f0b4183d199895598ba8307ec3ea0ccf7a1480b74218ace3c76f3fba448fbeb4c0243b4477fbc16124f02b72a97bcae818d6f693981289ac9fac89f82b055c93a009beb882fac4efd26af85e5698d65c789baccc3e0b7f381e090752a739a970c04dd98e306e2cb87a1b92507dcac6b95a69160637372a5f61769fc956dbaa8cb0fbd815f91ec3d0926b250f99238064f566fed62c8773893aad140ed2d3d6820085eb513f0f87d9c93b5d06178face55f8368474b61cfb9b8a7806622b43b35a0abbb7d116f4e6ad4875a2807564ae1c011760ffff49b2d43447feb303f80a580fc4eb148de710e4d3c0657e21897d4320ccc6718f6444a51a9f04402bb2667805b2b345f4ee431098bab1517184d143149ea541cd020bf74e478ccc01f95a230b9de073bc1fd50cd5a0b7a344d691252871c5c5bd503f2f671a6ea26b645cdd0ed4d17c77fc35cbe1f09fd44c6650b620fb1d42c3d056c6f98e9f458ec874930d4c3350b669311b7599f71662755a9d567279a92d1fb7eede8a6ea7c60c2953019ab9695280e7d3deb9861849293a0284613eb041c2dd042559412250665a6608ced16364237efd751b633ab597aaded11a182e4d7e0fafbb798ac1d908b2e4056fbb85bbb44eb395109d750979b204decf13d9e2702aefde898cf24b8cc57b096034ebcae33c191d2d081aa1789aeaf48e7ba3d8c17e39383b1d2cd39c137d0861d77fbf2ff8a28127ae1799ff0fa195c47668f1657941cdba7a00f05ac663010ed186e5c5c8af5052e01c3c56211580119ebe20b62d01a05ff91117058bf2043f9a66356ad3cf47fb1193025d8be29489fd2b983df56cf755ef8521b6b5d30fa167564f5b3546001c82697e338d6001d1c68128264614ca0c8804ee3f826c0dd7c507a98657c926ee0424169d4cb3fc95a19e8aa5193a57fe5274f8834be40c785044e9e293744811922b536390517a0acda557405c4f513b739f5202e7e6090afca9bfd28f451c5e6b1df9675e638407274e98423a391a783df3fb244e460ca3a3a83211fae247363b1e89797679936c980033c2e2a8e97a551ef5f63f930dbe5245961bae9502f224484eb872c1d0ef82b9b6e1edc1ac7f63e267f51ebe082e1ce2557bd61ded281db2a066851c534258f19f671b788d4db3b82b169cc804e65bb47bd5c424cdd3868964913bb160bdc6c22e5f99ce06fe1ce30f64339a0205ce781f40f503bf8225631d45b9728f652e01d7448d2ae4f832cf1950646608718bd4e314773cc0a97337cca50b8b6c027f91f715b4df7761084caface8275f7c98512501148db24bb07f203dcb376afe6871dc457b078f6a1b956dfc3b2900a4feaba0870cfcf1cea9e275e4be7fa02e68b2a5ae6291eb6954ffb384e2bd05897d7a11b9a3e76c41a0ec06908aea2049c823faeec0077583a2b639f864a703c2863f222590496de4a851928700d2d9a314cbacb78fc42917bb39e28d95b30646cf80bbefaa076d15b693a6260bb42e8f0e0a94e533b8aed2fde9eea6608d047da388d0c3746b0b9fa98967c1b73b6407cc9bdea32fc43ef16f9bf2761e7c08707f597a279eb3d21e83f720f307217e7c1cd5982d9f345818c7bfe3d9b7790041dbc6d07e829b5f8bede4469e797e70861a89d58c413c74a634d986a6f64c087974f269a6995aa1d9e6cf25059c349838ddc6f650031aa9f4a1f4e1cac6a702606fd44fba33b6e50157affc08d63d1a50c496dd40442aa09ecd010b2859890e6eb1d1baeb4645f93a026202463f3b160a8d00755457c7821e870564ff1d58004eb9878841e91a78e730dc38d2a07181edb796e3e8990b7c9e1835115ff06a0dd7855ba758ad04156fcde3916b90f80e9019200d591a650d0d2bd9ba1db499019fc89c7f6c8cf55ed8f65f55d3693e8007eaaf95c6ac5883ddc101d501483e092209534cb99cf7a1b07aeb2ce3c0dffc2e46dd22276d99c1d88f473dc5c9820d4887ef152025e73e2d8269911304f83dd101ed3a8208673eb1bb6f991765c80f847050631c7def8af1fecda67166e07e8008662403d6a5b9d4e8c9321b17eb0b7c0d12be6adb5807c12e7b10ebbf2e85604c4bbbb0cf58bcba50082727f6f70acf1c6fdec6f8918bb3c67e112869e15f194a524d4d8aff6d64e476b41c33970fc7733dde60585d226fd67c4632184ef60117d2dccccdad7fced8c9909c568f026ce4c6e61d7e790b4daeb64501bd7e5fe17cb8d26a8d8e8d891790773d926f0878a043d562ef1cd216948c5691c8775e85da16e777f70119c58ecdf220f9c20ddd1960236fb42f4e839d756c2bcae3005a9b31edce21edcd7bc85b8ecfc23f029e15073c5d28fe3f2642dc437446a8cefa8f9499546627dbdc4496cc5c02730d3e219176ddd75edc9ec0ee39480f94494c753d6b946baa1ef2672e2f33bfe104522c582fc07b55726f1c2d545461cbd324944ca700e5c902328ef1f7d51d020793d2da76c6b56112361de5a1606cb26e831e06bd6a4c2819d4a5b3d6e0ad4a085151828ffd34d23954859884afa27b0a2428338f20245862a9193f1150c7200b1066be88803c941faaf215e93064f7e8efdd592d48e8712cfe559ac3df796c0798db0a59ba31231207b817280549255b5172df4c31fef7f82e1ed1c10b080701cdcef998c19167d2f1f202751ff5bb58f28d7a6ea1b47e6e6400fa11d699b50433c16f144ac63ec18aeb2d9adce02124a1aee2be3bb06de1ad58a3cf2acf430d533e0beee4022be698228ba552bbc4fd9a7a6dd59d42fa1d6ed2340664569c09663f4d5dd857f58933c1dd3fac42ab5207938193bba4901a9598821a3feb3e0924bd9d3bfb821b18e19bb44d0fe751872b897641e6b9a5d9192c832a9ca5a008c582296901aad709aa34a9d3636324ae7fa9556ee37d03d611553972a0d204083ffff3150b0d0ac70d0238c30779815e77f209ae240c80e6f2ef0762bd1dbb0422fb7eedafe2497f3aecb53517eae5438bcfaa1811075d6910ef0a6eaea8bb0e868b8033512f530e286a1c40f250090968e77ea8dc79ec26ed2cb18558c87c028581b8ca9df8779b1317515d8555faab19b368e91fb1f2b2bbbeb0ef2e121b07d875019015c97757779f33edf922fb2e41afe5fa8af1c85aa0de2ddaac808b0586de5dcf0e7240d250dd52c7e09720a7d644b67bb95dd341b71ce3444fecb00b4d03bbbd1ddbacfe05ebabee9c86c53bb19824b6a647de4c6568e0f12db75905ede93d12544efa53d56f849ee5ac5dbdf1b2cce0a00c803fe0e2cd293e177a001c9f33c928b94bbf9d2f32de8ee5955c6b384593bcfd9f8e22449587dd916c095fe00a0827bb6d7d026bbede80654ae8f962c7835d9204bdf0ea47eec7aef005491d7c34c507008ff8cb209383c3b180d57c5736e348141033a5cb6cc0d3cf04887b35949f1c414dac046cfedad25c8bad8a088db1b90b00d1279c587ee78c0f14cf40d38dfb8aaffaf24be8efcd6fdeed1d40871766c3b6b727b0cd2abeb803a7946a8fc10cf13256d902970e104f2641a0954b138c5001a1989f323e1ace0fdbfe605f9396249954d19fe246ff9745da681f17c6ca5af48a853d6677fcf10db463ebc4c0829fcfdbb697a6c47f959532e08dbf1238250900ef642407f0d70234f4721a940fa3747630d756e4deba906f49bca9c851ef264db79ab25381db0af6d4d9d5afa9cac6858507a3baf9716b38cd49dab304d4bd547c705fdb85430892ee0a977d4489a117435c1a01166cba84ae109a7e710a8a695ecdfc7f09610480905c3d90a81b6d653f4dc0289adff47d4e20cacc0ceed7e31aefa8954faf08b61ab6b50d13c065c62f4ead4b4b0569ec24775394b9cc48630e89dc41064d0b02e8523d1c82655c1d590230437ec40c4b855bab1e404ebcc7e76a59a89f740682bce7d6525bf027fe5acf1acfdb4962e441dafbb6453a87200080fc3f356d0e75fac901a0bd539c03c543fbb309f10e284c8cdfeb261f5c8068bc6710fa660be3f27ce2b070a735cd9542ab221cdfc4880460ec8a97d723a33723c558e9690f087caa7aa1c9755d1548271344db26bb04bcdfc7718259fc692026f8fce5e80e78dd9bf5413f13d0f60d0c0be94153d1ec4087258c15a2dc9fa3f6885720090d928f84fc95c47ef8d351f9b25039a76df928ae7a2be5b33e7775ded9340ec70fd0dfcf04543eaa349f978e95c4e2a6b5e273d5794f43114a3ca878a50e4ca50fd312b371dfb82b6c5f1f3db01540012e4597777798212c8cd8a7466d6065b50b66cc60e01391ddd622baad9f36e5e58f1646aebf3048184d9e4fd0e8341f070461998e695df573d9683fe6b36a624e97b2b2ea3e8bdba1872d234dec04c0300112ab2e62b45b9bde45bba92d9f0b8b4ce594387ded55e335e5daf4c6ea76f10fcd9061b116f23355c0e11fac3b77e879d87c52a66817a831caa6f0b1642f31eff8c47a4e515874f0e41183b62cde3f41bb4a5e6520227652137bac703feb790b92805a509ee913a15f3f1c1c39dcc85ccbd213463de7b1a4bd3de0ecf9a3f402029e572abf71993a7b8c043a829d437047bfade1622f980f8384d81965c0a90084e54bbd4cbc333617f75496c13998c5a576b39473ea908a3199e6bdeb2d510638989038cb0b70c2ac8711e6d9dbb078484238fbcbc9d1bd89185d424b3bcd026c74f6c80640278b8c68cc3da9d75cfc3f8fc862f7cdaf00892fa7bc10eada0c69cf33b3c7764291d63880b3d19b9f73b3c42215613d6311b27ccf361a800f097630c854bd20f83889321d561498337f6d9e8e5e5862b45900f3d54dbd874100f353ef7c475a18c940be9381fbada1a01a2bc2a5f975d316f832b0c4d7d47809a9471bad9d0f31892e41ac32f53177320d2daad33bd047b2bbe97e6480d4ce04fe97969722c725ab791f26157f4411eef97981545aae0114e03f12761710250146d68b3041ea2c0a009a2fd8a94dbdcb544033985e125f03aab3d811387d7d0764eb5ac4b8a534425b4bc0190cde7cae9eb657035c4dd4f27b955520e91c9c057c4c10effa72e773f71fd1ee0deab96a55199f4321dcb3b0cca9e7acf7a599063e0e63e695a691363c6130f894368a9c34a12cc11cd8b23b3718a4d547b4bd06bd2164e4f4965d125ee437605a72bbaf3f12a22cc7b685dbd64a943929e7920ae324d491450edcea79753bb06e23c2f002f72678376d894701060b2ea9ea4c0e4306042a049196f82e9ebd5a4d00d595b9e1ee796142a004b5e770c80ff1b8038d4473353618a74217e0a1db6ee087dd525cbdd36029246d293740be304889093e8c509e8c828dbcef8d1660ff6b688dab49a9284b76af85438ddf37d413bd0e2138e5c51327d4f4a86abcf7bda42d078f4efb7fcb594b659a8785ff4cf36708e66121437d1a5eac7280a9799976f67b72bab18ca868f628f3c6f07acdc1a204da770261af6b6fa17e80ca45c2911d04ebc2f7646474d0ef994cd123d1fa3309e5da3dfc8a1401dcd727a14697af151ad96dec57d533be6583d4f271645fe8049994780a63da9352ad071113bb25c5d58380cfe56d9ce22d6a00790ed99eae0a35ea2d1f7617a0ae4a74e8bc67593739aba28b0782db3ab998ff2fa5a11bb003ceff59e470d82b74c6951e08f9e28ae33182f21af0d0829df681218c2843200b4e4c557e4e9ae6ae4dcae5015e2dfbd3b8d4800369293d1bceab2a746e8eb00f924bf4c807ba445d235869cd123bd2dd38471922e12d505d26dc8ba2cddbef07d9b6bb220acf325a38dc42c7b9d336e7bf9ee7ed7a0d789b2aceb9721075c3059ccedb593edb8d61c1e2c3c00a8cb37382d9d72558105c8731beb247d75de90feda5ae34e478da35616db7d93da2d10c3361da2b1c81e05712776f72d52aac043c18f9ed78bf5b618a822c7f69a98eadc937a92d3eb282e06f0942bf2d8af4073bd99505a198d27afc114d40bd022e87dc6a66b50deb823ab18e9dd9943ca90fc481f25e6310fdc95b35ccaa00b6356acb21a10ed18325c922f365481bfd4a0ba940d8507871e9766a018ee91dfbbadfcde408e94c6715a68aae24e75e754301b8902bfcbf5e8ec5e6d753b84302d761b9d56a315de9b5818b5d184f54fa380f9f378b2e18218a45bcb0c1cecea7fddc875390498d6cd9195490ae0048008f0fa9117bfd02a96a0a8576407506b0c27a23e084186a497a9dad98022c550c9b08bc1f57b7c38e619fa4af4937731cd8bef4709406ea7e5893a9cb38535253f300e16a0a3a42659151848a19a2d0fe0f4f783b15bd7b6452b0d23f035f233cab02b3297895565149d0dd0dd0b5ecc0aa2032e88d8dbfd6f8c00076596c13615902cf47eb033af864391950900a23ba83d25b185d77b4daa0fb785b744be7447600dec8d737e7295483a4ad2c4197db8fa036b46f5f1e5d61f491516f7deab4910142ef954c66de12337938b3173e29ba5b3a77cb500046f5cf1126b39662097d0e4a045b3d84ecc021016bd96ff9d30e3af864732b668bb7a0208cbe43d3672702ea47ea3334a25cf2979c6af16e0bcb15d3f334d4cf0a2cc96e0d4c9a4aea0d02cea0023f2483ad33861e4cb5e42eac3c2bdc56805b4a469c171071994d2f1a05a85b4e8e3044aacd9d7fe9cf6cd314a74cb57ad604323b71a03233713a89870f7fd495e6da45ed37829432510850e0b177e0012d4dc7f19ca0466807423a920c7912ae211703257bc81d1b76ef2ce83e7d27034733584b6126b4a9d0830422023fd441bd0e71cfbb4645fcc572baae75c674872ec90d674aa3a26feeac6b870ee47f2252043272516477f7c075dfe04e04b4b60f2888eaa11d7bed584aa10d0379ca4bffc40733dc8a5686020eb691cf6bb8f7b1e4316637aadaf17a943a040cb3c18e07e471c90d2e29008e16470160e1e8f169c4773f9fa171a06973d51f07ac7f5b82b66f466dc594068353befc3263ec9c7ebd8d4b77685693601aabf2008d9ce6274514a8340c48ea2a26c565561f5c099f756a027de280ca944eb1200057e0825323013d890134bd1b7835bfacef905b3787686f1ca57fd166b95d3c095fcecde719eb97417a1e59f0eebe3350f6de5aa3f1097c8fc4fb7ce25e3b5c000caf34a9e6d15b76cd950d762be446d846e3c56de2d6cfda4f219a3044e6ae08d503a57d5a19fde986bd66ecdfc475369624d78e25e3b5a3430c916b240ceb00cd394e31fc7d41d1ccfbcd8d559efa58b0592d0b9e4f4be60d434f7d3704120e818be396f0bcdc54855a7b3a63a51329c89f5b80ecbdd4a597363e1e03be190c438a4dada29dfc5883e7b232dd704d7075020a5ec0275041744b0946aee68105fa92525bcc883b27bb8d6118c069d439f17917777181e05d4d2df6c9842e460b08ea2ab7621744f818f86ee200b4a29b176f5f7145e612db1bc9243c74476b05a373a280daaf32643fe001837a9982cd0a8b7287980eada0fd2d95b072b13903bb78fe5aed7f42bcced0e8fb210d4943e3bfd4dafe956e9590ff0978fd58510204041716917187dea45f587f70ceb4ecfeceef158d0574a751b45eeab7ac2200d302f960d835dbc91bc54bf4221941091d3a77cd5a2c555363ecbafa3374730f0a6107c7da652bfc0566b0c803e20bdc694b20ba803cc5607ea590752c42490004005ef46588b04d1ceb462b1f36df903ac30bd7cc88da2238b276202c995402ecb16b8a8d481827555cbac0f9d43b3c419c39482defdb2a6a29f30af91e3800d3b4c105f043ac109fbfb4e4d32cb9bc10aa7ca3c245a75dfa4445f2a1496c054bd6c5a52885b29b021e9b838c23e9bee1dee9dbe7a7a4c003b0c620892d960f7e566ca9f6bf0fc145a95ee1b64143505f118b21671fb936cee6d5fb5f1801c89804534775916c7639c091bdaa4172a231f99434e37379ab5eb1cd920b3a1b090d6787eebf5ca181f8c9db95d9b96c8fae88731a8635a4863e73f5d3b2cdd909c5d00258b9a6e39f736d4ce3e97e0042aed19ac377a1aaa3ac55dd5be075c803ae019e58732e226201d877edc01da401c7ff93ebca29868673569799220ba0075c41cbb4ff2cef14d8ff6802151f88966a93e6c27224e4b4c16a61468919d406d2c24fd6b30afbdf13645c888bdc62db524a0a7be7a772988ba4bea282fb3b0ad47c9b4e09d1d06ffdcf1ce642ffe4eaba0d0ae81fc9d0992de8e84b494c340ee5505f30f90b0d21a931e419088df3d67a1a081a1757d947fcd063dc75015b0291915bc2b07e341b28b2a83baf906a309abdb8a74d74c65255fca490bd4d890a163cc4133337608d15522818de8b0ce1a17bab4f36ed003c54e5af442e353809a414b6cf8f8ac3c4e7877185ab8bad3f4a15a16194414022b94fca124c35e20ae36ae9a6d229756f13445fec620486398aa4b940c777ca2bbc07a9c30145db05d61610124fa5f5e8b6738e345e8570a941bf93b183352c8661c64009b16bcf0c6f563f626aadd4a3039ad8fdb98fb68591b5fddaa5ed6576a28a99519170d008d110d2725bbf5ebc6aad015f0e4714fcea5d2d792ae16a172ebb91b3c5bf010c86bf72f305d96e7b3b1fbd262163194234111c5d174e31540bb1af8afd6707024b97ba3a087726374ba5590bd97b63caff2ac6051180702f6b704be922657c01296f84f2d8952f9621a25ea9cc80d2ae89e45ecc4f818e6eba2061348b28a605d3cb360661aa32e98e313d68e28b6dd272cadbcb8cb2783c52e6d9a449f96209e50a11dfd808b16282b617f747d90124b92b2cd9701e286231b518d7f69aed0c57e13b8f3cdad104b3dd566d3098c5c382542227a057ab95a72305a85a3cbd01c7815cbd09be1bf4a8a28db501ef94e0c454bdf48581010b2c0adfc04d35d501068f337dc023527728d9213f02a44fff3012c5ce99aee13a51c770e920b51b0c342694cdbf5760c41ac517f2f18447988a66412c41a7311bc2f2fa79cdd2080b05d751a1866b84fa506b34c2b29e6d0d5a35960675b83db04c3b6536cb260c0089482e960535896b0dd78c31fe5af0fd557cad8eeec150b55b186a4d63e26f0a85291d87327200adf7be01da9400624ec151fc3f61e34b4f2b0c6ff4acc73e02f317cbed917ca6642dfae76a4f969ee88b20de91c0ea24641611214173a4550bd33f05ba3874b42dd2e1720874b7f6ca088341627a78eb8442f66f72afa02c0661ed7a6f8803ced85d48df66ae09b371877e6b6fe0b2f515415655c3a34b430a225f9bfb2f60f5aca17bab5f8acede013cce9634f5cecdd3914999234d9a3d00cf97caed418fc3d00d9887fcf35934907e9a98f327a87713d3d40c25e31baf0842668577b5d59214738eaed6484f0bc68131ff7c4004455382fe308bcb65300ea3cac55ad936847dd88d320d15e1854210d864a64b92756f79bc1aa7e7f805053e5aca70ea07fc0cfba0bcb3494b53a3fe92b6468e024c4b58473257a4af920f5eeda6bf4f90b63474c376f1089201c9b2d5046dbee4529c5af123af2a92760dd39cbbd7235e7f0eb947c42958ebd7f468b9035283c601c902a323c4b70bd50032150740b10f0df83cbf2033b16d4a00280e5ec5afc5ad9d1427c7c4e79de20f6da5affedf4e74f6137b88e14a7633e1b93258ba52722b12ef87e69cdf2a650dccf9eef3abb90e5aa155c237e835dcfee0fb0d4c40624eb8785fdbe633c60f025f0c5a9d972547d4c0f0b84d841b57377c4204a67dc42dfbe24a3eac52bba906c82c5d7e77f9bb28689f3893c3c6359e8b884793e18c66e5e6d22831955ec40b73976f75082573794eeb841e6f89f051f0f865c23753663ae111faf44c360508e9799048fd999a44969069d34c4f2ab16413e3edb13d56d79d2d60127083590e69a19953a5722b679546de38bc974dcda56c8c3bfd21ac577041f422b7ae740fb94424c91627b52042f06f9f5fa867a610e0281b114c6f319772363a33363605db936786bc30a55eef60b03e18ffa95374345d98d0780bae0e41c3ed78d4de017bb39f7e75a2021329e88105557ce5b5e06dc8df074b1f8d40c0882ec249b004948a5d4da107b5c379a998906d17a7d05a19af06ab759106539c983e572a670c4333d67d6489394688357d0eaeccccf3c0dd1e791c4e2e159f04c8e2f0e36801e53125c32ef7bd9332421318528f83bfd9db9496445d2cb54b1657579c3aea06b8216b3cc8e36b8e98e9d46a625bcef862822780b9c0ffb410618d66ee2c080d9af34721ac5a63f3a2bdef061a13101b3cbcfdfa6ed28c72004608eca260280fe34b6c555cf73049927ddc130ef1dc025907dc55ff739c4a72d1b16a5b0e500acafc522160a843af2a407d1838fb9751aeae827f09f875ab930bf9124803c9059ed5a8500ca585393b20611a2f6c7f730110f8737a2fa79e9c401c65dbedcd0cd337bd2651e432bcd376b317d6675654fdcff4049726dccee0d3aac5060ebd0256517be2eacb022e9e7c858865a5807318e9b9c759969ec42697428871e17106d9cf3f001897082bbd8edbc8a84cd14a2d9006e1d29bbe87d441bf2bc1cb1d0255d33155ceec802fd8f27ecf84feb40073565f746e4703d2dd15f459d8fa300f424dc24f975836622d3db1218633662888b328783496a8df7d5fbd81737e0b0f5e5c96138411bc865d4facea7196a3b31d529c1085ef3ecf8ed4e6d7ef01d90954604f60a73f49061b9f4e2a0dd37a6f1c867a61291db06d910c0d32ca65c706fc718a9f1be4d4c94db8b2e6bc5c20840a68040d8c4b1f0c6d1e49afafb87a031f85d94d89831ed54721044bf84b2915e79c5a9c7271cbfedac141dc5d14e500ce774bb2edf5b3bdc67c1309766c15355b78b8c332ace6d48bfa52975570dc0a3d632065cd8bd52aa7187bb16751dbae992cb0092e5584eb50805054e8ccad0a47816b4f1d6ce4f4df1d2d69d92bcd42f1c37a00eabf8c7afcb5a908125213037bf337af49c42a5a28470f61cec2086f1c9fa8b1b34c92c0b286dddd9c64550dae7bab926170dcb521949c25516013dd31bb93cdc1a39b48797d81bc7f11300c7f8427175ac517142e19d0d95a8895374f4608d63183978dab5d58f06bac010a3e0709467a1465fe9001ac1d1f0ab78197496ec31bf5ccba4f913e59142c9f05d227b54f5f44bf38029bba624833558e7bb33a7c7bde69ca8ebb7e30534338074992f340f86a33d9ca805827ef784b4f23d4c4012ed7d3197a9a42d9403e5d0fce629ec5921f63f57c5e7dd6eb9fc985b1b4b51029b132a5e9dc286bc6a6df0696192af1b4f85ddbfb9fb6efb0fc310178bd7ce7217cff29c9dbdbc0e60a5205444620b39c7e50bdb056b1539a8100fc95ea35c976b6c555d98ae8004af997040271d81453dc46f5b3a35f54a2c650306470227f35e71ca65bc8a00b8c5fd704141f153e4868d29ee0684d9f47db59dfff5ed839d67cf046095c44410d31ce03f1b4f32514466a584e6626b36f1f7cf7b4287dd203a64ee2bea75ca060c5560008e38017bb7e736be2e6cb249a0bccd26689d833cd2badda10731ea94d161007c7e4224da05c8a9ce9e7dd2fce56b597003043264c3d0b453f40de0663628c0665be823c3e36191230babcc817529751d911e8639553011740324d1b8f319201b63bb5d208359ac0a315f674797f1ed67c9cba690980f494c66e882680f65809237db36c2fa9bb6c4bd20dba12c17c8532543e8e6dda0ac82a20d25376270c08ac6d54cae11c539a1e92759e3d39654eb610eca8007b23c1ce46d8b569f15402471ddfd3ba25668ea91266b953b1476af5c06ff4b8046ee473ed2704c5ecfc02f3ad53caba8c39a4c4a0f3d87e85e1342699d70ef0b4951c0bf0f6d0c31aaa02d9ea4609cf5a56474f94437b520ac2ddafd999c4c35c129fbddc53dac95e7106f14812ba475d04bcf993e72465e3e184302fc8747142a3c340ab7981bacdfb0810fd8814ef25cb245e3dae4c3ba0c33f3a547a04967349fae9192f4c1c979b0898232883c2fc0ddb287b6a169e7b6d813bce146ab89ee939adb8e0a7f87b2009f7a34b310fe405a93aa8bb1c78be270651da33b36fdfbcbb9ff0330c4e81e2032c8c202cff3a1ea0869bd098977c01b968abf63fdef36f3cf882b578a5513c036d84ec25457d7bf3771c12e8e47ea296ac62c589cb8978efbc74fc61c0af2402711edb0a97733ba4ddad9b315e6287b4278e691c3fee3039ff2d43e66ccf540626e15a738c1a8bbca581298244bafd4754e20f7c8e85022156fff497274eaa02c8f1261d591012cd51855517b700f387ed87332b4b2d6fa99eee96c5644b9306db57721dab0293286f9f19493d382b998044b3290c1829bd070954843ee2300e5e5c7d7fa9b822247d75eb6c8aaac6ef6c27a1ff97834b9d84b0dede2a2476044f9160e3076a5f4d7fe6946dd971b5ff13b8be664870722dcf32989d6a37de0a52fb88e1b1c711d88618ca55bd07aa9a381889c52c9a3fe006b2484faf53270d271e668998f2194a6d8b42aebb7bbb2717743471ff6b4eeeabe578548c1ffe092e03f680dc64d2edba99eebfd2f12c7a14a76e036f5d6878c966f05e4ccf230ba91c673365512eb24ddcdb255de4856f8bb727a8a50dc593060ee0b4150fce0de3bb57fff9136cd528ddcfe378c0162b09f750847f6f3d0117e68040fbbe8e031170f0c935f45be63b0834209ed72f776299fc0ef629e4af8a5b275886e1d30deae38ac13100c12c6d6d667d41bb7f1f4003450f36939ee05753c1910aa0290d16e2b864007d304af85ccaa5d03fe8cd9d8ec3b8f0b367b6ceacbabb18079b03b66c507997a4ed92176332fd5f73317da02d135b2406f30a17378f6ceba549067f10ed60d0b87574375fba0ad54d514e4aaa960087d286be6c4c813533c1800efd9d166da4911af110f5af854a8ca77a0aa206c320e413747d2b7979e27da50dd94e69892939bd157138c8a2f770dab7d4d7ee10d0c5ce6978f9b15b76d4740d83ef5f255454733f8a4f3d6cfe4ba98d06f65acbce6c3f74af555c743e9f480faafb3bf0b015f3f5175906cde631d234366e5ae0dab51194f3f5bead681be80016ad9c6dc4c6e96e9180e0fd791e21b94d285d40ca9b14d9289a3fbd882d1f06ae83c2460ec9aa86c3593447911d0dafcee5b43cdf46feef728e35ecc924b807516feb0bb23c530fafb56583fe466d381c62c3530096189904b9c4148803d90ed3ce9f11846681450e781e2d837192bdf7c030417f6c0a4ecd443202a99d3603d8ae8bf45e2630b6cebd89889830d4ec322b3876c4fc47d66da3f0815e0b04032b851a5df21b11dea1cf979439255527ab81362dfda13e23408125aec0b15f00ee32bb77ec9710ada0730caf78c6a0fda497412248bb8315a19280f7816cf105e08f05208fbafbea9982eb843012af1cb146a1eda4114b7372111b68b91fec0843d33fe3ebfb7b6d42cce348e6b0c1646fab579fdfe921c0fe47520bf24e720b6a4a32c3355ac291152d804444a8d8b624d72b84cbdeec1da678401c4c384504e7e5635d748136b71e42470ba9d1c4bbdbcac8450f922d785289a93e7a90c50a8ea41a56b3d4ce109de46d4874a4dcfbeee15404ea044f3854d2eeca8d873c0454454a9afe0bdf97c8fab1b27bac18b4d7f838c2ab37db41da5e989fdc0e730fc1c487722fdf39c8d8edce3f3d4c8d85971d948b500f37ecdb82b2e20f67770e88668b4dd8cc041aa1b8099659aa8a524f813f19675712d53506fbbf75bfa20c2b89a585f33dc1e76675f6e2c0ed7be134d05854ae99dfd4147aff534141290f3723490fb26fdab918c5e9e97a9e3bd249caa03238bd260d032e97f30041fa0af83994a535b57cd6e62de764c2ad9a9548ed30f05e6a0ac875c27b247a1dac08e58c990da627657ecb9b3fb3a91e687ac0e12832ee206f0921c4243f4b344d00984289774f93302e258895a186e98d0df5b8043c46eb7c630d6b963beef2910dfa29d96bf6236794857533c9c0b9035d41de3dcbcbc1c5dad2acfa3414e8830e878c09f3d24515126243bbfd7ad2e9d22b48c8979e8090480733ad0a49455c0bc1c311b5c5d1cb8db31c1f64f5734d81bd270293b0258360b30a7caace811407430dfc33e3ff64c0500d4580dd485e41ced04194790bc814723ae234dcfd8c031c320932b6196b372f3d3b2c5ea6ed71f025775ee11595e9ce85ee72d039360c63377d21665052d6335287ab167d5564203acc58457c7f9b10f2ca114726ef0b68a4c401848489d22e63354f67e4438cc2468d3d4f35f8c00e412bdae4a81f07b1f27119a14e769adad01c2e8c49ca15dd2d5bbfbaf7eb041102224661e23700439e0ef15258826e009faaeeff324bb569caece38953d824b54afa75f555ea0ad28d952914432260a0f2b0d7f5b33fc748363eeb1272ac82b6e74411a41e8901efa933b4f75d8a879b360ada0035fde6ca218a4ade6b3a54da3709f764260f064f85efca94890dfddbefab2407abc573d44ef88febc45427b6c408797391d3052c5647ad0ab24f5720e82175f581f7cd74e154a289f7440ff0315689b28b9e050c29fd395c7c1b792c3d05272d09f8769036b719a2b036f9abbd4cc4366baf02eb2f65bea92ed2d0f4042ed5d5442f7863af73c38a3cc208a5ed921275b42f093c20f0e1807c7b9c1cc72fb485da038a8212c6a3b49b97e525b46c671a3666059f57ceea7c7d2889fe857b902de5e705806c1ee840929b0c9333a4617302d307af221d9c22fae7b5fb057f4841a5e6d5bdecd8ad8ebcd4cb781605e798282d017ce7f1021ca9c7e50d4613958541b984877c70cdf71164ee92c027a79141720c6014ce60a61b9e1af3b26df4b1fc7c0d6549d6d55789af8b8837d544947685004e364865fdc071a2aa771524adca644c8b2b34329258bcbc1897ab6b1f19b5007264cf41a576d956f687108b741398c99c4515436822b7644dd57965b7a9f802fe812ae6141cc90c7a5282f71b3a45b2bf4caa779a2c34796dd91ff71944fe0aef32a9f58a08b633bc82991de1beb76b765f00ddde97d8dfc416aa2ac90bf5080a9f85768f7a01745abdf8aa38bfaee1f56b49bee169426660158eda5069c60cf9c1ccafa1b4b9b9a27f9a1675febce24ecef0aa87262b921001ae8af0492b0fbd8919a223eba1b653a18e45a8e7c20bc41aee7241a0d64807cc05d93e75c10fea85828e7e55d12ed8c726af4fdbb15b02a155d992225a2b25cf6741a286fe0f505c1bb0fdb122dc5317e92098b506490cd2cd9b88a00447650b8583f3fbc50868f8676856239742aa43fd585d38e3683d3c8895a6a1479431d8e277f9b3860643408c5ddb18c2b3393da63360b0cf4b3ecba14a084ac03dadf33f6535dad70ecd64b3acc5c5eaa95a4a87aba6e718b031b1dd07e97930ddcf1f578459c71f09422213453977a616c0c1e237bb35da7e75eeb58f961cb8199ce4620a9765fa0034e01dfbd2435197e537e3f86d135046211dccd865946c5cb5f56fa810028800148849fef9e405865079243aa006cefe0a0d99e0f5937b683f7a58e3719a3004c9647991573b6f39bc1240363bc5bfac1e770dcbc3d0b4aa23f11893a738ef08b9d9d245ef214658b9137b76fec40198d44d59a6c238f75adefec47a5786570e39a2a4911f07adcf48b9c0020a76a1d7afc88d6858fe1dd051b259ac7c60630e36158a89b365da27833e372683fce0a61bab82f10042f2ccb246350a10d5af09a2e10799d465b58a1edb833f6b6d2b4f7c413227d67f05821d5c340e50834403576ba936b48836e1cc74b15b01dfe59a359600243cb08ecb3371b61e33ae72019dffb9ce44b5fe300029e1aad20cdadde9289979c70c2730d1220d256749fb034c24a589e30e5ae9f94b724dddb644617da22ff9d0464ce9082b07976460520851178dd2fb5915d2038b50f5a4c02de4cf3cdefdebc134e506030eec491b4e0686f7ecbc64cdf309a2958ee1b2ec8239547eed639431cd822a5c627ed1a1e00ab3c2c17e9936b4ba87f7dc31becce89d4fe2463a74a87d134aa067d2868f8200e9120b75fa3325fd00de481733217bd434d005a6ebd4663ff1c75abaedda3c0eaf93c7fdd83da23a323d677a4e524e87194fed894b32203c2bab61f68ac18f00ea1412fcbcac88e2c24e63ad215d004ebefd97254db80ed7dfcf1bde49b57b069a9873c9ab9090b235b4a88a8cc6cbcb174e968b019a1dff1b7589576148e40d00032815236d037bf40fd245ea49cad1001340fdea8c27705f206cbbbaa64704cab4d011334f7f1e3b6a765a55c2511707622cdf32d1426705f6a70797cc9b0205b077c4cb21f48adbde5b63b915ad8b0c0eebd58b46878624e2ec088d44440b4c7eccd858b02439c07de75979226b69dfbe31ea370b2f1672a96b2b9fc7b4020270b0c2d7f6d1ddc2367e09d33b7c32242cae5e0d476602cc858f8501454808a8d19d8f252988a87dd87c2f51b2e40d1b98f8d1a1071b37e6afba5b36cda800c338f18990f7edefdae0f453d331465500ba0bd56565a6223099272ac0e60a00dc9767ad7f8fc185fd7329c2e25f076895b7f62d28ac35b22871cc9b3b826d03837af5908765a83c2343a34dbef94a4ca937584d461d6cdadd661fafec6077067fe649c8ae8e570b53232a7edf0acbb2298fb02b9f1bcad4f8a51b70df73ab089c33f445012fb656b3e7bae9d7ba9a2f273f57093a65d79fd8a8bab9c9f9070c6823a6b41ea391bfccaff7c6260b74a47736cc2878a257cc318e6c5b73ba5a02830d6001d5e0002b50f8be7db813a7b6178789342f867bb671041f37c1fe110f7b948566c1bb76a2b90131d1c7987df22a7268d4cbd8aa17f151b9bfdca8fb016379f84e2fe2a350f085dffb443878d94a31e93426f63258a9429aa2a311fb0c728a56521dfd0ea84e33669f399ccc3cb7f08a5c42274c132fe6a97078565c01d021fb2bcb0500874ce3ade6a3e039b256b38c92af6825505957f97c429dda0a3cb53a942e2b191dd0769f265ee680a12d51523c6c33a5eddebfc72a88f63c04c236c992a209fc3fafc9afbe50f9c25ebda6812f77c2cc2b73cdb5c88a89750393feff7369cf143817df74f0cbba8fbeae5ef6eeae31c63159c9537b3046d30c60e588db0c8efc0e35513f0de1d5b2a529b4db9ab19ff439f2986439d541b305372233b5414c48fe6994b3f9ff0d1d45587b9bda12bccfe5fa1e408c06151206c291ff3163f30a929ac12217f9a08b72ce390244be12751e45809020d3046c06 false +check_ring_signature 71249ba6d99574ef1610a673e0f5a4c6c127785f7d7e350e4483b0ab26108d1a 602eac194999239944d93afe880ff03133d797a606954f33753c3a8a9e2f3345 1 fa5dc41402dc68744efea980bec33d9f25f6b05ae5ba9d004018aaedd24936b5 0cf8bfb55230074bb22d2c6390c62cdbc9dc1b9d02a4d881a0835a6d53e690e30c16f562ffcb74fab7a69da99784ef8cf7d8336c01b0f3c99c1bc01887d680c4 false +check_ring_signature f70701ebf72ffc76edeed7821f54f12952cd056e62b0fc23e8e13b1acd51b444 2d0da49e93d5d3b8ccdd8ee35d8b76cff082a70d88d800666dbe065dee88ed6a 7 b8155c1fa36deedc015d0ea6fb32724854e346a9478eaca3340187cd1efc2966 00f6ebf9d144efb21c852053e98f392c0e483dd453313f834558f996e5eeb5ee 417608038d13f03d22fde9df3f8c2f3dad99dbd73f4c55587cb067c7a49021c2 810d402b07ebcef5095002ce2ff9b4e7ddda8eedd13e4eb521ffbfe377ab6fd0 68e51105a53ee6a712a9ef9c72fd8edec9d08bbe75264d6bb0d4347198b9581f beccf6da0ae954c7c1c7734f9a6529b5fa6eba76f2a76ee523eff025b4d2c5af 3043790b7cd9c2ea130f1cc6ef600ec93910b7a9f22a24eb06b8419f20225225 00ee45e80c21be6d6ec401145b4085371d107b52fa1d56f55766708c114fbf05950c61849a245aff9e65acf3f3a6dfdee3c1da5fec1cc36c0f33389c8b7a890618b30e28d133b19706a88e999b3f27487ce70879c6127276875511ee36ab710c85134f7e44ee54ebc2376ef9913060a68fa7275f10f9977b62fe1163f9fa0d06c8e118ea9da55bdf304c603702caf51376bbe720eb9e863391d6e4b53ad9e90a832c9f2966cd6a8ad6438ca9167d808cd3e1464c210e94fb21771c3823ada806c564cab1693ecff72fcd3055cc6f4aab8b11720967440870141f8b73cc0104008df1b6b129fb4ce2afd1ef94634860f6fc97fcdbc225e2b59326f55d7a4e5904f7f2bc9c977c199b08953a412c882139bc82933358f7a29c95df512623550901d03834f7f1de82d6843d60dde40433273048ed39dab5ff6276a5d98dbe449e055e141ef46aee3a26922e3af656a7391a59c0c8fe412b95ba25c72645c045460019dea359b79d753707ce6dd0ae68a45ef7bcc84e6ed8c6deb98b79c2db0e030ed625d3b4a28e48ae0e2bd78f0886ccc9b1338cc2945259d4bd5c205866ec72009cd4834242fd2815eeca98b9a0347f7e01c85db49cf26e806ae64371e743d203 true +check_ring_signature 2a3a4f1dcd5b348b1690f82df505e5387f7e48808ef3cba3e93d36114f38dfac ab59a4655602bf2270dd01d2220bc90251470cc8a43d989f05176be7dbf95ed6 58 d9e64d2e9dee5e03e87dd547628316f6e8f85a8077f5d0e384596ec4703a56c2 0684255a2deb00248346c1c251a10a1e52d5a33a8988f22a10f922cb874aa65d 7646335b65b46d1d859e418150ebd60976af2e8ff11efa1ef551b8c2a9f34ae9 41fa44bb34c81ee057cc4f83cb4e269c65dbc58a01b53a9389a977e72793a332 db5f345f6bb82fab064bdc731cb8e09e25d1bfbf6c396c83834b3bab884a2f1d a7bdc7e666a9527037f3954fa5a19e09ac5ef9c84a3c081492c14a2ab480961d 4d52fd7f2c0ce0d1fc23de6338d510a3b7a7958f07170c4d6a9405040c71fd15 ca96fd4d479c08baa8995735c5744b9acfc911c894bcf8301d071a8e1ac9945b fa4a92384106e83342be93a8926fb22d04416ffafdb70f3d754e5acd62b0e44e 9be39590e492de6c2c4f3427faa7040ca5698496245bd6b5654203d1ac12f63b 185957feb18d3c8c8571f146306d965ea42130b0d6f7ea1b155caf6b96995ab9 86a358b0a5cb2da1bb0f6211ad125fa6bb32a79ab5f9926007be563375819b06 833731b78352cab83de52cbcc05dc55511ac982f803e2273cd8cc36a7ea67e1e dc4cb1766c7643b78b13a6156bdb8d8d920e241a3d98f9f176a994f36ba62f97 48579deb96e0093685fd925e546288967147df025ef8eba05dfacff485c575ce 4d83e507cd2364130c0219130d2c3d3f36d46c2592675206f53080e2e798e29a 61e1c3274fff3c38b3df33de029ed4d8241413eb1c7ff95ba364154ed4e020ec ddb7064ef9d08b2f352c0ffe14a3be3b79fd2e12d0d990847a369fd26d32a740 b9aa337df0355a9cf024376dbd6045821e7c1413fbdfd4e7d2242bdca587d266 9f5fa6e4e806b5f259b4e4d981b85fcd8ea3ad3b2a5e01e36fc8b1c37b9d077d 5a4286a71533b38246ffeb013641d7e9949a5f67fb5abb4a6cb859721a22adfc 7845703276bef7200a5f55dc0b173bdabd9c63ba8cca02d1a1a84e3a027b4d3b b3dae467a66a618326ee1af9f0fd2a0c2fb1fa15587e4a00d018f5d4ac6d5e98 568c3a816506199d1bbbb19cd6dd6c81247e3ec83756e35204fe8313d7e6ed14 69e96a78831c7a7affd08f324728ffc54af584329a0a4ea7742f41c097700c5d e39cdf073aa3a158a4bb9c0cba045678b1b730c7154ec6b7dfc1f58eae304557 ee09f7766f4c59c8af20daecc7e981a983a7b10f35cedde36151b828db69d87f bbc2d151271ef1035df64557bef8f54b68dd1ddff0eb540f1486b9b04c447ab1 8fc7fdec7f4a6cfe1766f6e2f361b572426933666ff911ad51daddb627fc7735 437087848a0d8142648aadb4cafeec1fa25606469c05f35bf6241b07dc459078 dd2d6b8c1f8e7b37b925fdd4e8d419cd90dc1927692c0df320224d1b99632f17 f3e27210cd605a9bcee4004eab93a1c42d95fb55dac457e9390f93953775f5d5 3cf1a2fad46bb87e0909bc363654e00c270e84daec98dc371a376fea04179c1b 4c701673b75c250d5d741e73e352f8eee1b41274d68e487d428ddd2d6f53c2d0 cd0f083c0dcd88ec3cc3d26facec91903d2c91c3e8b348fffd1265f5b77d26aa 99275ab3745810dae0be469aff8ffbd35a9a248f28ca4ab91dc0d3898922f7b2 29a1b9dabbb46988b3536eb8c570e1ece2cb4f556497d8c711d83e20dccbdf4a 03447ce6a3427d8cb994703fa2304e5c2406979857e6011ad18817bb3185fb1f 61d2d89e089f3794e719bcd111432d2b98cc6f1362a163b9f4a91a6e8292532e 2082d3dcd3307832f47f4316c3b489f3b3c4857785e5cf4b9b87c45fc34e5233 8a89f1a5bc5dd66a56a9be983424297e62e64b262541bdcc30301b8d52d065fa 9b92c6cb5fd97172c62b320f2efb9c014d7d68e3f0a07e1fea8ba61fe92f5681 ae1ae1ed10ca24c6a21fbabbf86d3d817083c3720a0a49b870efe392c52ac9c3 cd7e80ded0936d01d91aaa5080745b0db55e45e3259078ab5b3542944dcc9682 77ecb9bb9302e675f93d756c57b2ae9969286d77c8a1f4caa4485ed740e0724b 907f7896b5ae285c944c214cc93678c83e9a9e819c786fcca4343caf8c48eba5 238fa5a99b7c812de433a63da772a9453ecc4a30cc7a1e376789857eba4ef036 cb8d0aa620617f2a7b01a8674054093f4b4ecf314532904e104e8ca3eebec03b 3ff4122ccd33aa8e96f17c40cf8060038e723cd3c351b82f2682cb6575850977 111a42c4c1f07e24f0db30150bf4a8892656f20e0688fdc11b7802b9f09a46eb feddcdbf58f40ea2f21d5ba0b3c594dc16a24f7d3279f06f3492e5a1c23fa944 99f7df9e342cc086923524501dda8314aa67ee1d91618dfac9751979c127ae5b 95733d4864470983f2049f189c9450c4b9769fb738296d7ba7e1d87eb91fa2c3 ab18f707b7de836874a9c0d47b0a082543bd84d9949f1ccf782588de7ce1bcee ea43fe5f1b59cc249bc60149772b9922691b54c122bc77cd5f4f2baf849b23f1 c2beb384fe7e51fa843c84418f33f9936297a91531e872ec2f8fc5a7711aaef3 d0892a884d2fa7656855810ee00245e529d524cf3f06d50a4bcda98f6591e32f a15473f058f1e7074b6655a97cac65e186cdcce3e5bf1992f583847d623c11a5 043d237dbc8d81d0e99f6d808d9960f13f7a5176b66db5d977351d36734ee90d3e65711343b2dc5da922407db11c8001bd4e068bb894b1e5dc5073d1ba069a0660c659175fd8af8d8ad5104194b1e51cf31aae4a4f5a85d02c8c094869449008b735eac652b41cb0857538349103136241cfec37dfe8de353677aea2e2260505c119acfd4095001848e401b23d29ad7bbc71bc5f5d807bc38ccaf94f33c68b01a9de399c90fe30f7dd4ff5b268f8c0229846ae2aa2e6c63cc963037e12873b03dc715c1484d35e80011b996909b62419480c45d3330c47143623468422840200b7ac6cafd0e80ae33308156d51603ee270883148b9ad0f93a7cc288d2bdf7606845cc85c06ae1c66c5a500c721bf6a0532f264d3fb88fae7d74829cca7a0fa00e6d9c917587dad05227bab71ac4daaca2ebdf368a3da6fcd3b4ba7f6cfdf13058b6d9b1bcf7149b6412c1c8138465c927d97d51fadb83b69862615dfea9502026155b64b2bd6222e0f4a6a6509f68d3ca75a6596f7b22f0e9706cffa1b5f210d10eac80e29115ccac26d7c7e011ab2663ed747f10b8bb1be36929c8bcb68c00c3b1584c80a7e3e77ba6e5d7ebe3485ddd88b5df52c1c03ba70554f7347c40b0a3a4469907684a4b16c902c1b44c8785e342043f0d9d71ea23da203265a2e4b0911b5e1d2fd0d84d154efc679d3ee0afd8594e83dd32c6efd67b07db47ae3620c41a02aa8657b391ef0fad876787b9791b91e1c8b9d75cd0db5494e2c21f6c6043bfe0a896a804021c9d90f9fa232a63e1b02f6dad325c1fbd93e861cdbc2090b4f2786c8b06e8892ed70b9d7abc4122bd2aec277428d0e68fe431dcefdd29103b4cddacf2bc1b02ac76e9c1e57599efdfd22f672b7ff457348a23c27d5b44107c6bd1b258143d2792e7655ed690eba4a132e10e88b318c5cac20dfc2c2ca600bba7c27a395fb838dbc0a25976d6f00509901eb963c28f47ee549a84a6b17e90e88ce7bcf19b4c5d23fb86088ae3cbfa5b71ef1e50191dabe9deada2743e4ea08a7d03ba352ba85f550c50668abae8678dfa233b31d28e103224075682a056ccb7d190fba434d4442cf0629211bc4440f71671c58c939d37037f711dad249a40fffa16ff6fc90490f359cf9ea78fb192f66a0556593430111d77f3f195a18900a5bb6527cb756df9b69e78175be71f935281bb7db5a3cc82c53f0baa09c86fd0a281d57def3470fe3fb36fa00e96dd4fefc92e90ce9487df89014f31366845f0b55b66662dd769de5c6adc4c90df426234c260f0048c583bd7266ecf68ea33801b18172c7d1c708dc7ea244de65a96ebf8d540cc1c6cd38e88954cae4876ab00863364c69c342efb67bb4a8541f9dc542cfb924c12b6d5c57eea53dd90cccb30516d4fcfe7903a7b3d46bea172d0404280084c09297b297e349a80616c2387003c67c25719b07400b1917a76eb2f4738d85d282ff3b3ad5d6534df04c88aa2a0f84e5b5770ad9a638fcaa004d44dab1bb5fe18c7607c1625ee977128b0537660a03a08abb5753adbfc9ff9df97f723a51bd43924ba56d304e498508a05f121d0e371978a6ed062a094be22b5bc9068aa76b0540c42f763bb987c2bcc9bda9340dda94a0b129db01c39fee60226b131e7cf24d1ec0e8e1eba9e2b2f5eb130f280f7d067b912ce4af15efe80a1b0ccba99ced63e5a657e5056f90ae0c3a9674f40616e83a2febe567b1f7e7c0c365ea56977c57f6f6925ae6131e72f41cba9f8c01fdc807921626cbe11633226cc5623fe71d423479b9c102f859d2d57d70e9750834ce0f15aafc0c51a397ffe5db6660b5649566478ad982e58387058ed8710701b1929817789ea21881c8046e558d6af954f5e74d6695ec75306ab1848d35f70d76bf42ed89fe0a56e07ff23f67ddad9ba19fc75e52864e7442eb639dc87942050a681c17c04036bc7927155ebc04c2902c1715b311358a5b54b8540ebd72e60c27cb1d9282c983023c47490756fdeb071c69a27c4a1f9e73d831cdf766a2120be4ce488b55fe3e2c658fd381d1ba7bc44df1b9ad68d13345efbec9aea8c3440674a573ade96660578f81e5723fde561f08104b9c1bbea4f8b06aec1b2f7b8b03dd4ac158e3e5f07934c57a8d5d2e83fc01688da5c1cba9b7e45b84cc825a35039d0cf536e2dc1b2e0784d79c4e707dcc2043f51084c4d56cdde7ab01c632f60116db42ed6fe045be8aede8c5ce33bf6ef9f11f2b1624fe4105e7f27814f10e082e9204f99f250b8e04bc634188997991958d2d5674e0b2ba3c2fbfd0942e1d0be451caeefae70599d8897cda5ade965f0b359d5bbacb7463c1bd1ad7d3c00d02319f3da0f414db91531a3b77d4b37300951387dff119ccda5be2596421ccb20a3f1348d37b117561dd214c94103c6e7338f59bc94ea319f56588dd50f1ea7a0ae4cd9a01691a9da4127b747c5d9e3535809af0ba5d61ae26a6811c89f6b2ad0b3d88e677dcb1b7838431b1444e02bdc55900de39478c0379eef76259b9642e053a83477c2c3be6905fd23d08c606491787b3b47087101bfec4d86f953bc7170c59a230bc1742ed01a2298853b4b6c7c17a3903abaec22c5de8a7260df3dc5404debd00ccb263b23fab06bb430e09b610c6fd4464f020712c79bd883b23fd4b09ea9a05f13a802ff56ad9c79f61d5593b8577e5127f446a9d977216417cf36c0558e96078015ea8aabde30965e6743d429dd9f4f6e4acbe340beee8b3f9de6a046d32d05cc7cc310459f18aad29bdc6aed9ea52b6373de10904c6277627b3d70044f327b28ac2bc9797abbc6d067e7447d74d789190deac169410a4b71b83f80c203b9e241f28c6ef70de6bd9b7e61e2468e09c98752b3c0d6725f7478125500d09cf928923589b21781cc942ac4c60ef841e2a921c696f36bc82336f54ba970009d0cb2109508df0d492e4ca4382f56b4f3e0324d1ea1088677566b46a97ac04ad15e3a2536ad96c3d0196d8ef06715d3d2abacde4bf6c72a286bf632cff1b01247038e36c08fc11b1325a9207628525d5d531823030c2f18d28834371ffa600dbfba4268341e4e5c73e340676f9585ac884f61cc9ad0b7c7b77c59b5bde4808848632de3c45edc70352b99edf4f473b7dbd433c3c941a4a0d100660c7bf5802c2e75faff55adce0c4f3137cb3bc581b7b53ba6730e35355c04ec6e3dd88850f91197513694cd4d33aaf3f3c04e852101971dbcae8c4e6d94a18befc9b155b03f05a12b9d9046cb346b2a526e49b1ca753b6e4b2297fce9301182e7835c72409cab5d98eff034d776c92b3d0f432d02cf56b9958db3ed339979837d04700fa0f19d080e36c2218234f1e431a4906caa1efad11bd47484d7d7f6387a8b8954c028bda5257a499c58fa0ffb3e81b77b062f92396454619ef45eaa78841bb4662099a54c3ba9e604d7f57db2afb6522c3ab736c43ec41d2ed2e6463ee5a5b726404992a453212633abcfce43b03e9ca8c06513fc403cd5521bedcce9aea5924e30c505653aa96def5d05b038fb03cb8f7a8f495cc084f12d7cc43306480df0cb60fb8cadbf941f16e5c48df4b417f91d27e8d67678c22811b58421e457fe53794023be308acc82e63bf62b0db313c80467f88aa1f303d8d915fd0c9d0584b7e6509963ed0829125ab36edf1bf2a9771a58db9f34099a6cdac7310dfc93c09068f015b62cddd4aa3c47da2e7f2db8abac2b6a9edd874f4e04ac992a2685bd878af0ef4d3adcfdce67127a854b1443309a1aef0a8e55d93d9651e87f534f54a032d011b3dd249c1331b526849f7bf27574595bdb8578d16c2368c4bf0bb0e93c3670433baafcefd9d9f12ae198d3f321a0750356c61686e7758ae52cf3f05a1d86a0c9cb28d94e60d0babfe1383e3ac6a77e3a5ec1ba65586eff6f1d92bee5fa9120c03b51df4e47522fa3a3fc5840a8a602d0b5f565574fa05f2c0af9752a5a9c10178da9204859628b09116993cd0adb82677f144e9623bc6ae4b5464b5fa26ef0e5a1c8654aba56812a216c02c8131f0b9c8563eca0784588b610b90ca37cdc30b4fbcbed2b8d6bacca79d683f646a392a8787050cda3dff900371eab904d4e404766019dec890d33d7f30e6327e64bc4085cc02decb13288755566b78ce0a700722d6e61f09b025b9de37338d5109d01350cba42d8afc60724f1523b7674395046a0cd5c41477fe8d876b8eff0f02094a115908ff6e14541a4b4ae78add450c0121e7004117965b4f0e73d9a258afef61f6f1f7766a1797eb4a071c374f671101243cc59c3655167f998189b29d4eee619d86e4f2d37c0f4848ac891e724d5e0419d4d0d4f6ba79561a4d05d730fd80ebcdc8546d321846edadc88cd08778d8019bbe723444aed93f3f9b1482b5091b264cfe7d18ac15dc062f8e74d8e3977b0babfdab30cda2f99c59984a6d748fef589a283ac2935e20f54573abbfe6a07702ab0c2c6847d0bb98dfaa826175207309bf7378708fff0ae5c2e7d43a6ab3bf0188b9536c81db9bc127a0a370d45df60d241c855e61ff0bd21b184a3355d1ec0160e6ccac421159d4197852b245e89d32587590406d65dbf6474945a2ba8ab4018f2863661e87cb7f5959f39de2172c7baf896ff3e8222f979e102d720b063405bb4d36054053cc94953472a34d6e0f755c7251ecad896136fb28c717328ce70be7f439a7e63f2a40970b828cf5c0b33b504c2053ac7fa3f945ff6d6585d72d0c6e45798cee4125d26308bea88c2b20ab0e95bacdbc1857a891c66bf7b874c9004de1741d4cfffadf2c8ca2738a3c91214451cd15da2f04eb0c9ddfb51ab103040528ba1e8825dc9d7b298d34c02c5de7a7a470392fc9c3b1bac7ec5d51c53e079c4f30e56fe9648ff861d5c436b4150992391b41e8ca8a309c1307bedcbed408852c3840ccd0a02a9b0fbb73182c4b7b5380fd5349d59bef9ed1d6961a3fe102e175751db77dc786fd411cb747205f74e63ff51b89775e59ea3b2a00ddd3de0de588737095c3f39f552c60b54004f48de332f32f64e99890b2930bbb5c78130c143c8079848f999439861efc8bbcba436bf3c72c67eb16ea5cedb39cf116380eceed2f0f5734db4f91263bfa1a720736f7b7e0ed370638f7ae28c8ba5e25f9075e9e647b068140f5c73bcd5fb2203bb124bc5c1be11f9c6b58d6ac834f679a0bbe915bc7957c7ce0caa53089ab5b7e889147a254da791c715ae8a36f75fb0e0e false +check_ring_signature 6902c12558d791f5eb43035663fefc1c3f2f97313ef68631d1ce7ab54059449d e08b82c7b72a3a105af029bb6aa25c77fc28ad2bff3016eaf56309533a6936fe 88 d771b2d17882e397febd581c0702574d5d9c1ed525bd622c2eac23eaaefb9b3e 4426bf4fb3bb61a12a2d3e664668246551ad29b0e42b9eb4f7452008e9415f0c 8e56920eebd5f732cb08b52a0bfebaf45e330573f6bdea6cd499709bc709b18e 917a76a5ab018ae8380ab6c1226189e0bd80a4258ec7c68e26470b30c57a2acf c3fb91978aed8f8a606847e4f3563ca3ef190d5c162e9cc73794a26b373322cd 7fbd9432bc40a86e08fe8ea44368372ba05ba69b80237ad8c49cb930b7b394b9 eea30230924925ca11a6c2a5ecdab374e4637f4219038ad7415ed20cd20179a1 65f28bc945096585e714afd01fe0ede683dc3f3b45eb83dc40f6933a4728d7ec 4ad3190293b1b8f0f2a2777421b5865fa2df3f356520c5d5f189f894ac0c7104 1630c8b9dfe46d6015cab3181507ce2b94b5266004fa62fb564b6c86066f8157 6e01c11439aa0ea00b933eb4c02dfc987f8e86cb5a957c38979d9f1bfff7d458 7b580d32409220616eaaa8d64f6f8be036576ee17bcf6eeb11536ed6e9d77258 d74d7101def9145bc64fcc3f18e1dfc9b160efa1461c49120842c00fd79abec3 41e65accc12a1bb4c04a4166cfb57b43a54c2b19a14430c200f9057a09e90c54 39852880d05b23d7002a54914395b818f2ba9fa462bb2c68147256ee337b1aae 1d4c033d7d6482dd42ffa36782732e20849b84cf6b73b6527e49fe84ddbb2bc5 80ccd5ec78231772f436c36e2532f1317f02445d84e002b90df90aa54489cfe2 2fb76f0aa7828478705e8887f0fe16728b983cc25ba9a0ed5ff2f6f1899d3af1 ba2f838dabe8fa826e93a60d611ea6cf901c580a78686a9de16a03a235e2de08 8cbd31b3beef4539169ac3c8a7a815ba89f3eba4de118591305919ead9b7959a 02cf3cc01a35652e632d76cdb8e4bc7ddcde83acafd86d1175ae4c63f98afc0a 39162e83fddb9f4104c64a00e7b728b7441cbcbbe7af92e110e172690c6e9e2d a6431bd3dbbec4395133efb23abcabc1e67f1a2486958fb370bc799f30e0c1e6 12c11ea1ddc47a39989ca2d0f0a8d6cdac96e347e7e2e6c5c650ba0358f3e05f d44c65b05a7f0a44c4082f635513ea5e82be0d9b6ce24e45b3a0b2a200417f5c 3337c2e52753c516e9271e47d552f8b566f1c3dbc6948c208361ef5cf316f455 43e880753f0b7d25651f12957b8ed9072782c054465beb1f970d5329bef9def4 f88be5f3d81b603aae76ea0e460a6bb4765451c4edde4bc7313332e3628e7f22 74440d6d2830e6798f389c31c0bc9b492a7fbe5f3d0534fa7bd953d21bd8899d 4fe3bc25e30e4d25d1af8bfc333a2ee7f87f27b01e4d53d7217c178a8be2ae83 fafce38a5a14ae5d7335dd7c97210134e4210026452f9bbf2ae6ae529ddc8e50 da135aa8f3e7f665366605ddd0df3c134256f5f89d94680d72edc008bb096024 a109045be4b55a23245263c4605a5b86e0e5d91f4e06273e90685b7dd2ea84d1 946f888233a626c3c9ee81381e8cef12d397ff972ba6294b48554755d70fb47d adb081141e01e4f186cba083ea4294874bd45284e68895ff29e3c41e0f8c2278 32efe445c48e775ac602480d57c1b2dc549dcb5e736ee7f75a7f235982383aba f2fe88389f14355ab40a6fc399699d7beae6a00c5f52defeaa5a9d0b9ad5f226 e36cc60f24851f52de87f13fbf57680d0fb46e9bc951da0fa567f13e2f1f6dce b01210f0dfc4bc0307c19133ec9f8d2aba46651f5456b2e2b1cea833a05374f9 9c734099ed18e36a03e2f480a959bb555090a821544d54350c60091d631ccf5b 952745b483bc955162db0c3a9882692413738b54060b284df3eba63921a41a2d 8b68bf9efb2cb3709c23ffce9863a3ad5e097bf71369cc949e60e92b88f9dd23 73bbed395c5362574db345f9dee2497fc4b9d0b9ad63a9f31533784ff3f43fd5 19f0882c2f6e9949f6ffdb9c15ece2e65eb7525948604d0150eabcfb79b644aa 80d6b8d138797367c18676b6892d1a12ff08d2e50a14abd5dbd5154acad17fa4 a909cab1d1240f4838a0942f048459faaaf927fded7b2a7408a27f07d18d4fbc 9643334c81c774c9799ee94c2d04d7fc6b93d868c7359a33175deeb44173cb48 56db02d96fdb3d73630f18ab91659bdb084718d195593f969142aa79d975b533 ab7f8e8940daf025a69b908c689158f54c652108c1928dc3c49e92f9d605c20a 1bbc3ccc0cc366cd0aa756fcc00fad8e688e4bdc0f4d58c996de20fbb75b43a9 d1e03bde4b6e3b7ffb071a93e325b3117c5c95447afaccb9a07a83b0a4d6c05c ed1048f598be04a3d8c8f43d61743ffdd29d638b876f320ce93e78d0c0846674 c09b41faa2654245f17e4242edb20903737f0ddea6b2e53bb9c47bcd6a871f84 f254226385258439343935e3095786e12f1ee2705c972fd412f34fedb99d5199 6c049d4daa84f702d5f8986b6c2f4578c6fc21be771e8f32b00d1455492a4616 f7cb595c3c0a979d205b5dc01e11bf8b839bf22ee5298eb797eb87d71d0d34c9 cece8fcdc5e72968d1d78d0f2005bf3477e99cfac74de396e92b0d7f8448b7fa 5c62de1c5f89866beebecda975f3c43f04f4023ee99e6d64221ae647bdbac9f3 5798894008490ed3a478be95ca85b7e3a4a45f8e50fa0fcdc54e702370c9db1a e4c0797c9e7abc3617bb81c2ae9351433638dc8b5c386a36f4522630d115a72e 13a1db277871009c8968806141e1f03d86291fac69ef7906a46ff05e83206264 eb327fb81caac0e8856f0d9fa6765e9c6b45d23b6ac51b66bcd3d8d0633a563e 12bc1da175d6be1772c814a6fe5a6d03663f900cfd4226b26ff6677142430720 f4d7332a99a8018af49eb178f06635d6d96b4365a931d407aeaa14889383eaec 323c7725f537cd550ccc66b2717b1268f637702c3fe1deea3c86c0bb4df01f02 9acc5105c3adc984559b52c0b45777e17112406d6daf577209de6910910408ca 46524bb6ab52be8f20ad62e26189c8f0b021bfc1aa0f91d3cb7aac643cf89217 c7bff5ecfb62a91073ebf1e95decc3374807fb4a3753b9c2a310f0e0a4e6b1a7 5dbd4ec453d98ca21793342498f4847e245153281fc1180e2d1a6224410dab28 22715ab811563cc0f10032b2a2e7ddab5616deb275a2c3b9a8c3795863d37780 6b16ffd278d72ed654f4f8bcd88b358bd527bd8a18e98798ca2d8a61232d9c1f 16cb140d4baad926e542a06695cca385294e1fc38d30bd8b8b11bdcbf5f250dd c09bdd233ae14b412e1d27c47b4331e036d7f5803969cd0186fbee7db4c03409 ef400d26386555ead2d1d11c771d4bfe61e1a4dd8d8efbc256d33a817472d4ff e2e3dd796d91a57b0f52bbb915960ecad4b937759389cfa32d266b2044b244cd 61e44247f842fc62a166f18ccd5b1875b4df7280610226528121a808d9fc16e7 2f48ccacb5966921c2b404cded993ca0311c0e03cfb27ff60556da06e201ab12 f8308f6dbba19b6bbd03ea31d94603a76d8e6203ab5b2fa275e8cb4e174bd737 1c8570ff83550b7d47cea6ed4d80783c2fa4ffb0d3a7cd8c2e6d8a70136c4ede b1608e74ea47930ababd13f28a1be1a4fcf54753cd19f75f495f5025aad734b9 7396dab94c6f76c2711682f0eb365f07a33430626dbf9fa653c76b6996276c4f 22783fdddcfcae9e745590c17b46ac1979809ee09fff805d68aa24fdd5893c69 cb7daab76182699fe7f2b1d91c4db3c0160f788c0982d3cd8027057c49ce2c27 8015f947c60ef6e1e8e2fc1efbb1660f1945081be9c15d5d42496ea689f16921 091d4c4bc63dc23dc5f36431f832388dfbc8001ec391672b5c1ed9a99bacd523 a44ac68987f8ec91b8e2a1de3ac4488b3733d9733533c7dc07d4c784edc76ba4 b74222cec432056a6accc3b37b08803e6e8d179cd0306acee4a6ed38fc606c54 0bd01c79fe033e408a9204378b350aa75b0f05a63daafe056b70e3982ffdab31 f4c2c0d67ef28f471be5ce0fa9ffeb1d94512195be95361fb453ce94530f120acf6bc607de67b2c54a88feff6b9116f99be928b415f0682b773822a724250e0fd8c05d9545e9ae96b01934a6b4703f3770f26436aa322c159148afd74dc8b1083bdb13510fead3d86e9b3c30be4296eb15ce62ce0b46e2d0cddacfdedce5bf0179f67ea1caf04baa0f5e2126983b0d7fafe2423659471541bf06022a38002700cf45893454bda1559ea12739d7379dfe9db19b2fb4759166545200ce891d3a0aeb68014194978d9d4ff2f3de78808900f55ab56ac78f40cf68c4113b9212c206cf53cd9c21871c2cdb2780626a53d66646ebdca601ba5341b72653f062176b01ffc37a7d99becfcb2c866d589a512f6f55ad8f0fb812ecfafeab4cdadb5e170d91a1f85a96e37b5763529981ecdf8edba96acbbfc44cbb86c6125d640718f60ff38cc33b3fa6b8889382381d8d07766952a6095e55c6263b316f8987d146a50a0a691b2521ca69c1b796b5383c12360f25cfceb72f962c2989cf5603e1a994073980fdc330324bd542eb9fc97b5fb61ed1db709399f03d9a4d4633c871ddfa08829e5135d63b478213dd1c277af6a455d659255e13b5e1dcf53d62925594600a2fa62ebe31bfce2f37bb42c153a9ca6382658347237852056ab54304ff78f80c6e7c245a1b06e3c10cfa7473d34c51fd40c0595a86d37b9aa2f405d38d3dff05a0182f8ee4bd58a00909a22895b773e0845864f2f7e3351939aa97fc53277a04a08b420314dae7503214390deca3bed0d7127dc55a20922de8482f50c2840104eacc0f089256d7a114853d21db1642b007c016a3ee26b5fff5661d5031163109af1947b3196f8a1a56a98278d3661c7d560f88b20d8b932048951fb99e8a0c079464e38b50f978647a7acd3bef19127c27cef4f4af9c34f3a8ad722e62ec1701b725c7b37bd17056a3eed45c1f4962d6a8bebc778c4f1064a87e227dc28d86075fd64c8a8bf0a9c11218c4eccb18c17263c61d03e8f0e97a51aa437bdf7a010b29dce2ce4c1ff793eb2aa7895487c1b943d0bf77df7178e8feffacc780e1b80266dc711c890494f7de72af557f1d53c0ca49a2d61f4053c0e504cf697a8489006328f1690c80377a924df53d71f80e3853dc4aca576b87aeb5d0f39b64b36e0a02dc9f5f9dbfd8b95741712e1a3589d68c5f495bffa2e371eaaa56eba3b03308d6faa6cb9f2235f1ba7d45a116314f435986a9cfed02df9ea8d112b438624802cb513d8848315af34219bded1883dc82c28db45064416b7b4eeef28665eb8a0172b307d6a5696eb4092912448b8a2c468ab184a1c08e322db3d0db4affa11605c956b4bd9540b9de9676b13e9b3430107fe4908fc29a6e520074fe998a37810b46117122a7849e98ff50b11e603d6a99f59cc242ec9e0dbf7bc0ded90cbc8c0c6db37e03107a1354f6be6f1bc97b53104edc61be013a0e4ca092a73a5746d608b64da25b6780d62cf57030eb388e075d19e92cef8f06a70e30e9ba0af5d3a00bdc3bdce63daa7a572e2b2e6f891649efe8587312e25c95fd07f7fd759ae7e30ec71d1403bd3b7d39be92132a43681a7c3a9ec440201970c5b0583222ba7f3c00e908d68df2ae8da6a5a2469c53a198a078ce7358647f744d9808ef45bd02e200b06a588ed3547c8576e1c21d0fb37a70dc518f40ce9fe975798ddfe37ea1bd07dde723789c17bfcf0ecf0a5a3b167dbb46da5f9955a4257f75f9834b40c0ba051b64333e85ca554a63c35dcf20481c2b3999206f3837affe87c3a27177ba370cc427a8347b568f231c190e52dad3b007431018d8289bb05332581af49a300b0426860447cbfbb9160d6e759ffa7a26fd8305c9effda33258ce4531b103408609bc459a5344565fc9921f2ed719e72e4deac4eeb96867de58261a25a0130bbd0f20df146cbdba42695c944986c3847c31936e40d91284aad1d91f75a7dc3d630ff0a0fbfd81ec219d2c653393d10a4489436688e4247c3b69c83c5a968e59d501b74064f41541c072dc0a6d66979c5f68e285d51c57fdeea8b19ccfb40d006c09ff144f4b555e18a9cde73b56589ac2adcb20a07ec5773d1d7e0c5f012e05210422dd0ca1a1af7fa5cd13812e10b352871072055adda27ff06278b14a090659076fb1d006e71e61f55acc414d8a25a43f74fed98b7a7205c34f5ec2d1fee905064ab8c9d0bf52048e1f831852bc20d3999b079b71145d51cc3811242518bfaa04b9a1a937699eb217bcb28d6dfdd6b5fee4afa48a596a4034a99de00d05eace0d3704abc9ad65e0da1527dd62e704b16b4935eb3cbcd0080080309fbf801c560d88e5aa830e462287b191947123df7e4bda48449e72d56f737c359bfc6fc32d0cf1f1980087b9de00ceef249b761975dfffa09b240e9bb5bb900ce4cf9eaa3107d298261e951594fdcec863f472aa852c6e0ba8c08f75ab501bfd432512a6020b3fdb4e89068918e2fc5eb22358fe83a5f49352b954f1b3f7c0b6f6dcfb40c20186e8c19bde72a61cd05b98f89c5ce3758ac35eac5fc9c90e5b42004b6f661b0ef80f0a1c4c5ab4b2deb771bb510fc7f0d57941f6789718d63cccc7a1f7cefc8243fa2416cfd1717b063e88a5ec7e045c9b2473f038c74aea22ec88d6c046970bb599cbed725da2eeaa7e463cf00609869cf63886484d07307b7a3c92faa2e60d74a5ff1f1241685fe8536d0a4a88c2bec9fdbcdb6f86594f92b5c3bff16f180e232111959969ce57f951c9db6d327073288b2443b71da4977e4096d2d4b15c0cde9787b421a376dc57651462c1b9198079cc1588b6a1904ff8edb1311869ee00dfe3674a917d2a97bfa08a84d45a43c79eda42ce36349d0c3f6d428a4e55510006b1f112a23c4920698a257f25efa018ebcc68481f5c2f2c8d229c0062af5307dc2c85b45b6f3b4237b9cb17e772334ec3fa421589e5b63d1e42c4ef4fbb3a0cd3ad82b280346d405f0e0e794a12b298896ebf742ed10b993c53d94e566dd6009d59baa4b0e2b29c97840b2a43edbbc2ff6b27349cc74e32966845ffac5f280e1f2769e432c596316fd972a8cefa02a313597109c3ebff30e2b602b827111a06960f7ffc5cc4b01c387099fd2e6c32524abb26abb61884e74db9272dbe152f00d0ace7dc244e3db3dff1133127764178c181da87d8ecafb1f1b588a845fd85032d2233e95b19a5a0594a56dd64590761cadf966782ff9c472715d36de92c27068d82e56b44e0b7bd99d75463e32567f3c3f4ae403aac43d1e0fa456cb8004c0280b74b9e961cc905b44f3a0547ec0d0b3a5e9d476496a14fda386b11d720a60e9fbf4cb61c205fffa95caf6b4f22d7b8fc4faaa1329c7d16f2fbf03d32dd4e03e0b84dd78f0d9ea31583c49273348413f818b4d53f087799b17c372b728fbc0dbf007172a94838b3739a159936cac10e6629ada85b0c74f66d1606967103d30a4142a26804018da787ecaa08ec24ea4ebb3dae4ba9663bb218f5f3f1f9debe0d830a08d1a538f21cd1de5705512add40b8c6821c84b8b830419b3e084f3c950302d44dd3f42dc32d9bb26894c5a10deec366b5b4ebc8778d8a45d5bb7cda7200ac334580a89303e67fcdf8afef776e1cf57b45646f13a2f2b8b059856999f30bc4052ab2d9bb0334d453a0bebf5a4f26c745ccdb9ba8f01aed48cf5e69e420034615acee5062ce96b8e1443769424893ab422222f668c12d67eca14cc35def04a63ae13d004ac5940bb23c44b894a4eca34e8fd494a886400127be675bf10f0e46f40baa302865b579d14270bf176e67224c047193f134cfa62d00cc3ffa78016b970bbe4a1e67c444697bfae8ace332c922abbf5581b1667c6d165ef13fd202906a4e1d250bb0bfcbec1e2601228d602fe2f6746592b5ac8e3b02c37fdf8c0ae8d13c1d883edd27449ed38ae1bdede0f4f27c017475b53b004cf07026c44606e15216bfa04bcc9021fdc995cacf589f99ddcb3e70ee72515d307474ba145107f6cf0c30cb17a0ed2af03ae5f3aef5f9f270afc7ecaac21772b6f76b6db8bd06957fd6eec6a79bbbafbf462c4d835cf063ebcaa7609db6878997536a5b48880da521de674a2d17b03649621b070d7754131ae35aeff5a7b5d45ea33edda4de0b7f5f873352fb53bb3b515bf2024809082875eab7be60b7756b7dc9a530246f0ead66ad13ade72f10f1fa70ec1dca23699db66233bec5ed397f94a50699732a096ef62d9ced3710e461200ddb8320e3deee0c88945f3a6798c8d6d46dcb18830c5fb7b74a7cd8e8463fdbd3550d5fe87ba2f3834ca4e26dbc05878315aa19be0c89e579d407cbf10356e86a1f144442020f8baee9441677f37938bf601f6e47036891caf84fde35607724a623375d1427f296f8940cafc6e15221b34da9828503c2395e2af76dff1f869db7957bcba7b25ba650518564ef7718f33de91b891b051bd979c00ab47814769c6fa17e2bc0b69556d89da7d9ce6b8291265e19ea6f0011d859eb5ff30176470cdfe045abdb2d9e799a816e3ac993dd2d12bff6722a09d9af7b1d83f9487120b294c299125c76afca3f6ee9563e67a9e5349190e5210db280bc81133647b88a61b228b42352ca0aedb8bf5a9575f094300e522c3dc1095a84152e1b4f38ba9a3649e6157c737c471460a9b82853821c01252d62b05f079522c7f614d651ac251c413ed18a1ab1f72274f46a424dd57b7d073ff57c620ec09915a9d9f76a758867661c9a141bf2113d5cb67f68733435f3239dd8d60c05b9cacf09b146c876a5e6e9c13f54d3bba4b813cae03b9d2c00a77b5d3e95870e55f92dcc788d17abd8a5a4f70b690c618a79da84b0d5b27e2dcf0cd9f480360597393dee36803cbdf972e51a2632fb869f683186cb35b69576916b931d8d180d0bfb9c5aceef11ace717f2c11dc5ccbe8bca1cbf965ae8390a91325adeca3a0cff5b0aaab39d1936f73559aeffec83feb29aaf16ed26659d3a15b942c938f90778f62584654cb3762c51d72d62571349ab57d4974dfa81cc445f89a67eb18905d2c8e5652725ec843037c1cac2eb199faa8d4171699881435fe8b5e4477d6b0d378cd30b2fd1487c502c977e5e9f6b7f317c66e533ab39329f1f35da75e9c005140623a29d52df6a9185cb5df9272269d8be80af09b0086b9aa056e9b3dfba0848918dbeb064b67d18b7b20cb652377cb6b835bcdc61724bf0fd5d64eb4aee029152ffaa58af4555cc92f40ab3ecbf29435f3d2ba9f041aa0624c412470dc40df3095ec013193c1b8fcbb43af7776afac88895ced42a00487e07af2142e12c02de7cc9602655d7a7717fe91f70475d3594ad272ad8bf8508470ceeac772d91063f2fb8019cc12ee187ac614911534d4b0db11646ee3b175afd05c9660fe7490b18b51f0ba3cc484b64d3943dba00b738cbb2158e6bdb7e2b62fbaa424c2b1f0119524c1e6e41ef390658b1267f5655d308e8abcf66f1c9f2b2b2df18bdfc9b0c510fbbd558638c9a03e3d42416379843ea37a8b643ada75836d6b0b33090270bbeb86488171d2f1eb184cb76df99c8c6df8851546b79035bac6b5969e5f74402d0dad195ad45240bed9f83d2ac0b736b0fbfa3f13f112597c99a02a9b1da0802c8bc9510bd649e956143d97e1dbf9be2a3cdb663721eea6a28ddd53ac864a30bec7318f5f5dd9b176addcda2e8605da01d2e1e9d11182cf33c9d2bdd40afbd01eafcecb956fbc054023aa3dfd1754c395c43ae105aea6fa0dd0ba6b39b54060e864a67f9bc59ca42468cb926ca48320c958be1454ca5fd9ffe5dd1c30e8db90d207295de988f13988a3c49178be73ffa53cb7c3a7d42e187aeb42b0deca9bf0b6f650f5293b8ec1e353f6bc49d12d7a22be32f0c08332909bba355c24ab8e40e0b7d989b26ee0a4f673c495f4b02f7d7e99f51000f9bbc486245b29d9d3ccd0d47cea8ef06410fde76d52f3a6220132c73f33cac5e23935fa9ac32c351f6470c5fe162f4b679174cb6dd9f1e6f6680f81f86dac84d758ceeafd8c4c4789e66079a5e5b5979bdcb9c889817ccd5fc74b83b9912f1722f746a7aa3f53af8f4ed056e10a6eb9c0bc40657e49aced72e9c8471e9eeba60af5fac55fb9679431f650482062f413fbe3c26a71c5f1601e3f8a6214387ad244e22f63f419980af714e09d856ada170508bd6e6e56529a562f4253dac8f296b41d143fb4790dcdda5c401da82d39925316a2a0789b4b39157aca0b012bd30f2aa6c683174093ab23ffd09058ae571d5a6ad9a13008731710ad6ee14b770ee8c021b52f0cf83f688c850023fa6d2d40c607c724b55a69d0c3832a964b81aeab3f7521a4424c757ceb12706fddf918d1133d290e3161e3ea0ff31b901b01e3088e8e2075c311dca4fb2410b51a003379df9140c070ffb6a048cb0e0cad03b14db4f96ba0ef11a4bcc2a9a031177aaec213899dfc2a6c964be4af9dc4c989f2198d33d3ab3159c728d0ed507dabdeda9bf00ce42cb2a650d464bd91ca0867be7c987f2b1e40887efba68dc091e86cfb3f3eb598bf43b56268f27d2d8733c5f7e541b2ab2af250f26e423c906d24a7521779e17c2f5dd6f92608b2c51611dab780bc7525e65964fa3c95e0b037da7b44a2507af908c97f78858532d0a2cb8d72dbbac094101f25e6f5c6ba70e9050c482e8d24d555adf920e2e81744299280cb9184d817ae8756035e3098a028b7d57904d9418c4c155456599fbe14a1ffcf122845b347f7598796646a2ce07353846be6a3edf4ea2f1a64eeea8e01f9839c8ca6efe8260d4dea7dfe89c6f07c6782cbe67d4bf8a054a850717de9bbb5efb24924b5abe4901c09e43ea11380bb9f300c1129cc183d7adfc225ce2be319648fb201cb8deda4884b0ae4870d50998dd24eefd357279226a021d26d362277fce5aa7778ca2c10e300e5571802202245e837a0eeea96efb0e5d7d7e442ab633026366614e10ad8f579f9e0686720b267a2eb8db549f3369cfdc561a254eaf199293b62145a1fbb1d974118fff17039da0e11b2b354450ca5241e1c8eca45074884fd040ffd375d778c7d65654f7043ac557cac23b3194c4aefdc85239be4aadb6c147c2c07d30287135ce4fd7a103375f52bf166ddeab2dfd7dc9bff985019a0e72215e06275e2288b9a659e34d051ce073ade1b68846431633082566eabd9f29cc1b68ae2130eb7642c6278b1d018db96ed590126899ec6add7d611f39c200426fcdd9463f68615c94db6293dd0df7a8f4bd413c9cbe05fd79f6fb805c701040091ce95daee6e6a40278a45813060cc99201a0785886000ccb0e10caa0086feb6d509f3e27d5ceb680e2c7741505f97c8ebecdfb1156695433cc420d7f018d7d6f9d6b75a8094ec70e8da564220d57976204dfbdb6d7c4dfdebcda05f0b397ecfe5b925fec0921d375e38a7f0a051c15f248f368acc259837478fc0bdae8e973e34f64892dd17bc1fa8262d4c109c020227426dfb68cb542133d907c2cdad24c107cb75596a64a1ed4347fca4001fe9dfef091502890b51db4ab8950ad2130b7a9fa11c2b33c173f17fb4e569e0c95dce0249d331d8f7ca3fc0c253449f1f9e586b8b292080a9ca8e28c20a4550e7458997c4c977d7c01bbb765bfb4f89f24308c55bbe9308d4d7943b9e687ef081230d21bc221ea3ea6c20fdc3a86db80574edbec2904784e0a6f1bd61d1b6e0d46ba9705c576977fb556c4fe6861f9d799f4bb1a3a61e6e262d22a0cb59152069abba7c067004b5d878c8a15fe0ea15d1b25c37e9a742cf46156e85d0cb1fe0dd59f70d5d577eb766be9a6bf2c52a16ba7ac7ef25132319983a8d123dc589d0e381054523fa6539fa09c8d45bc3a7acf5fb51270f78b258b5311940da1c1f700470cd7b9da6f3a19ee5c3f39fbd9cc36eb0204e5db83fdc0e77091b13adfe202 false +check_ring_signature 30313578abfde4154539231fef9791830db80d51f16431cca33810a31d9c2bf6 e1478df8ac20b0461caa3011b08b5f56a49a7b30d93eb9a773c8b46120d27cd1 1 57c7b188db7985509b81f90e0f44a13aa8d44f81a790ebe5588721aff9fcffef f91b1224761191bf332d49ff3c447a98ba0a204f7ba6bd9837b819ebfdc8b00197c547f6d45a86525aae4c61ba45047eafb867c9e6a024a01f0fe294f8dd2703 true +check_ring_signature 0dd14506bf1f9ebbb77d088b6e982fe6f46d75b081ea260aba0669ae182a8d94 47587768a82a85f7e925b07505dc0d5928dfbe352c9cab80c75ec0246b842b02 54 a165c9bf5944f65ca3147fa4901f629f5a37e80ea2ffdc40ca4d0136ae50bc8d 7c838eb286c9442ebc06add225946f6a55094f24acac3e8ce4a8b22081f77641 b109ca1956c4ebb3615f417fa43cb9a057b33b995b18bdc32eb5695babcbef8e c6a4d53e81c8030dcc7a8ffb403399e27dcdf79449e4e0dd4846c0eb949796ae d82453e3b8fbf07e2aa1bfe35fae22ba6a5adfb8752e67cb928f24cd484a4002 7071f24d0ca198b95df81ada60231b97d892d5fb1ac36726b69d0fb25e3117ab 625ea78ca1c13ae255986f8f9931e65ee6f92f4906a255ae0af510fca9aecd96 05baa7dc35fdc42e12a56e09b9a97091ed76c0d18cfe716b01ac4aa5927f3c14 b27c7929f67c2e8335222da7ce96e6caa1753f88881714a2fd17a8c61597b6d7 348f5c36155ccc324a08eb1067ef191f1ecf77e1f6672970a785648975ac5997 2f28eb5f3c5e9895d600d722fbb5a9c003de950089eb7a6e150958fabb4f3667 db3643e613d05d48321379bb74958fc036264be265d66e6c497ee676b4b7a5eb 569f130fd9a137da6a063c04a35e86371bd54d43ced1d66036768990d44e80a9 22514722e2d8bc5d7b1903271f2295d364a3cae68fcbf5ee77553ade10d73039 b181181dbb3ab71147ef3634491027cc4de65ee8af46b4cba3f91ab6ff7ee92f ab6673bde63691cdf566e2b9aafce2afa6139b98d3237ca25be9d0ef5f008cb2 12c7e930e7b7f5fcc0dc56b8de7a95aadb34a2e4a0cf912d4b6e0ffd57e39ec6 a63e1c8b12ad0cba87365e1825b8c67bb65d67b9ed9d03ba956d5c78e1a94695 da7136cd99bf8dc0d68fbdec90a9b7d3164a3b66d8b93ef77530a261f78644cf 12f4acd0c0903fae93bfd9cfde6a156794d5ae8c90a4e136927a8d4401f137d1 adc207272959f0c74ae77317b36a008e52bb5529aef0fd0c36c43e41d949f4cb 40660055a8f5c933ced5bee94e1b60c5514833fb8ffb8b12069de803db074385 6eed18bd0b99f86bb8a400e3fe3b529c4744f9e23bd1c0b4a3c46ff69872b82e 30e93b59155c01d6f874dc5bfc392fdf639746939b918f00f35c39cad848c125 451e14a6083ed1352df1155b7b851a9dd3651f3e160b4c61bad26f2df298d7ad 799ce8dbe6f26198ea7e949b58bf6170e09fe426285838e4dd8a848f5d715c28 f7128962eff1adbbc48137d22f70dc4ff625adefc291f15f48f26926b36af231 6794927b185bb8ad6164dde575321a6a8639875369523006f2710e26a9eeb0fb c1837866838dbfc5cffba33f121bdaf370ce243a6ee7c7b93d60b8755268c277 d6bc0d9efc12a1993f886478002e8634e6f250cf718f01a49a21b37fd769bcdd 0d95ddedf4dffe0fc631d876fca49b17c7efbc54c4b5e8735981cf5641c6d73c 9c6556e1b983b6fe6d4f22955db24b8862cbbb83e05b232b5ab1bdc601ad4977 3154e34894192a41d0648f68fab8dfa57de53968fb12d16519db94b4bf3d69a6 7640308e82d881be6120d8c0e1d30c061df93c84990ae024048d3770fe7689d7 8d1dab046a4686f8c2e61631f0e93ca9f3946e238b24320dcb17f2abdbe44393 d44c64e4c58fa719bf6a63c3ffbbc68528a83fc0ea8ee2512e466bbfa413ffa0 60e208c806a280c073e46304edfff6fa114c16a2443533551cc66d01a9fdc8e3 a1f7f393fafa894217bb487551dde692694a6f27c6172e2c72f75a6ff1129593 41d6cc17d2d79d338b35cd62d3c00eadef3ede43af26f6661dda6360101b0f7d d1837143319634e3cd67fd509fcfb6091f22318439693f9b616c952b41af1826 06c8cc48da1dcf7033daff7e86d449a88588e0c36a950fc2c6b8324fe5cb0b76 010a1773aba35e9ff4dc10e9a5843ad7f93c8793b17ef79e355624b275884f83 bb6873f8423348eccac9d5d68c15d9a92b380b1ce13afeaad2f25f6611c15ade 013a565a5b757035a8744eedb195a2d9aa063bdadc83e5e573d18e65f91e63f5 77996e80f58cdf840bcba46e5b42ad41d88325d0e2ff7b914a68a45d0f47f6c8 8c696e90217e9746d0f32e28cd88969a77f2cb296af98793131a91c4f7c5a707 8785a12ef842b5978a0ba8602a39428cc08867548c79dbced0e15cecf085569a 95b81615c1044d2db9817cb28018372afe26e3f21fb3cbcf7921bdd12fbb1ea0 ee00f1dbed3ff0f25cac380a84716cdfbf10d13d4db5b49c1b38e3b33717b1dd 6056629fb59c8603e81d4e18ad34dafa6b6607e9742cc8e73afef72ba7fa1541 8a9b9648c76c932493e655ca08987063eab02347cb80958856e3d623cbc6233a 290ec9be1faf2b25a8457afb9602da438ccf2b2d0b83e2fc2d06f23bfbd1f209 64732bfed3e1222b36f1687a6c831899d1c5e85a6c11ef6285cae64971408e81 71a0a7e8564ed5948e3ef804f57dcb2a5f4e7401aadbabec88b07f940d5e6600 2838d6073782c8f6195eae434acc161318897b7659ab0b4e911f44d76ea38901d2d76d752291fa1ddc63b81d87f8f3881d22aefdae62c9761dd6e693225b590d240b70951524aa67a38c59b85066f740cae67cb2356305f5b55a4ba9f5369f0a0868ddf82e626c7ea9fa4896f58039edea1dcca60b30b72f4e024bc48513050381863284d191f73a9bcbce6a60728964541511ba364c00d6798ba645c504660c41ba28117d15d771c081ae3a1865e9e00588baea2f634ab23bf652102506800176087112661a0aa543988104146f47210f3cb17b986e86f382be2917b27bf60244c050611736fe9a5fff2d039233c9240cc221ffac9f8674f4e762507a8d080addaa8f254fa378cdd820e3a6b5e104d8f50c5ea185d5f8791b01180200871503e358f6ef441ce4e906026eed6e6f5b9bc554dcb605497960fe9a710551d7c90f61ecf9738a51c42e295adceb23573921898c65181e40f68653c7539dd7f4f10460dfbda5c29df1587bc6fad4967def35b01f90adf71c0cd5560f77ca220cd706aefc6d775bd3600b0534ca90af2984003dec5666c6080f2594b3b88e7b31170e361db206d4373624b66308c68ba4ab89c706f77786420a2fd54f47ec82af000c6e6e42d880e1b646da465a4839820af192ebbf01bfce5e5407a1fa37f597080f493fa5737d89daaaa7fbd2ab0ca12597dae579e82237c9b86b3ef3e10a65a00d3dc9606cda9e5a4ea124121a98012d390bc65239b23fbec9f3907d96e1298d0a778aea5d22bf213698bd0f0ab235f090eeea2bd7874fe1cd312795880ffdde0062615aa745e8da734865b341dcadf8e958e3948c558538d24b293c4f6f27f903f34d1e2330e6ce89ea50c6fc8ffe98c50a6b558ba3a8bb2bfda8753a927a3607265f55da76a243ed30bc0b30bf4a72ab604a41acf7001930e7d98225953d0c0462b69d09e59b4d61c8416fdd8e72aa439f255bbcacbd71d5046d5e351560890637fe2a8cfc6401a9c2593b783384c530fd3ecc4e0c644020c97df99e8429c402c0d1789cb9fba629f1bab7a286703e4f2f55688cb54ca0b827dc2c1e000d1c0a064d70eb4d6bd1ec873be32ba9768c9f1698cf3f2293367378c041aa74ecf10a242ccb74f18dbed8be54fbf6587ee37f66a72d574723f4ea846d7fddb8bd110050db66bddabff6adb0097cf474360e582c5556816323ac4d2d32d0be3ec32c0a783b2e188b3936c5206aa357b99e70e327999f092890cad300762915ad65020d6433433653918a11f057a25066a346f333622fcf5319b2e31a1ad9f00256d20c12612bafcc028f97cdde29b1a5cc76c884c71a301c5f0980deef11652c12130d31609453f862c0549843feb843ecc280f62bc26c6e595389b8f6bbae7119bb064382509d2748f1379b26507db11fa6daff3e6d6330f033f48f707042b0d55505c223e89a4b72c928dbc2d65847371ac0b29eb0e48343e3e5ab5b33572e53e307a41a90669610b63bc42b9a34c415af94fc3870451b9969150fe4b326a047d50ec77fb27bb1918edec56a858b5493ac37c8c4ba1081ab50ec6ad0b571b71fb105a3717ffa9b2c124ed341ef8ff9a2d3e4f200692931170bbaa2c6e47a59ee1c04fee8d43de483503122500d019ceb5588f40251f129e73a4accda9f91f01be108c352be82024f1d3f4ab9e343d98be8cd2915733cd68b04e7ac555fc05dc98606cc9345780b54a451a52e08307cf4995695bd6a05caf921af7dcb2ef2d63586030617426530778921e12e6604d92bf26c42ec38f68b2d30dd9f0a0e162623bc0443262d60c8b17e821423fecb50f8f6ec275f49a4e0397ef018586d4daa869f0fd51f2e4a8578d988fdd78ab7b2aa5e8bde2cc2d7a54219075d037c4242b0660e0e5a76f090899a6961145c4b32c0427301a7f00610e2c776fe8cd59acf2d8905b17f0d20558c3f115a5dfb7cffaba34e9b6fffc0641b3dec3874d2c53c4a4a0ff710d0c07f0456945d7b67c46db530bbcdd7c4ad149672d4daf43c22f9f76d043ba2a404492794a63e4b0118d52bd5b73d57802255859def306c9fe17a25d70bacd81b986661f8dda2a68bbe8969f0a4b7098bb531e937c087189bb1eb20bb09372afb1f401c01b69dfab82a1687656348d49926894da882ebfe33744f1f03050bbdc4a4522daefd9336c4ddbd06aeb91d44f96be96c315d940bc7766818fb083c9dc10209c610eaf4209a796a09b4f365468cca9bf6b6535cc083c73767e20c376527b38af8abdf1615e01efd7d52626bd0d1356b27b6efbac0dc780d8803048772507e412d971780e0d485f166509ed531e1edc45142f51ba6f783e10c71084fb4698eedb1c094d4b98fa036e469f45797fa09de8b8b9ab913d0aa8867f20cf0f827d96614931b1e8fce80f5f3ffe3da95dd34fb774589cc44361aeceb960fd533b87ec3775ec569240f430e65f7cd0e7d7017b6705f61d4403eda7531660863387c015f7f269587e7971eccbb54a0f07cc071f93da30f7b2dfaee43b9c808fcd3eead9497ed809264af6dcb91bd7a8fca79679f4c2f3c12c5b847bad56c0b946dfe948b8d4522149987f2cff5cf7edc41437376af86bcf0844cc8616c3007f104d47e3773899a5fbc0b6d84163f6f5acb9da52cec5e8f1b13e54a36bdca095d2ee7da0792160841ab084cc05627fbf11a3e69d8925c7d5923dc9482f98b07c475b1e393fa7f7fbb6ade376e3368631473de3978b1573cc673db66c66e5f0893356b3bf480e7b2e0072aee3ca328d92ae4ffc1a15f1032065ac43fec44a9004fdeea2d576d04acca0835572a6ae29e71f10a542c51832921cdff5cae053e07d1630c0c021c38bded3a3b8628e9b3768e248298bba91175c334ac84931f020fec99dbfd1833542dd06ccd1d0bafb6b902b23d4a14a9e077a6333adb08c6f709068eb9b036fae7eb6c849238b4dedd0bf237243b28aa8d5070b84fb1c4b73908c2848c08cf4329a3445688b3ed259cfeed04b8d74867fd98f10830f646cb1804e1d6512c03ee3f9c559b8a176e1e2c24513522171af945b23c74541d98f3460a66746a0c8f22f88160b0628cc868ab287e8d4eeef57adfe5b07b59140872c00bdb99c8443aa01b452fad7117126ffc3d90ff1acea670cbfc06033f9e9f9a6305065cce120a4f7a1c9e218c33f798125a2aeb37b5c45d2fe173b5e4c4450b710d73aeb1fb154c5f243f3f9f4d4dc1149aea6b27b9a05c2e769fc1216988ff130d3bd5d518b3a4a0670856d9984490455f6e42e2b49eb440b0a3995a78dc8163021bba8dda23f262ddf0681eee1006052a57258e634327e135a3eadca2e99da806f5a062dc1506d6b5d246dec98e8eb7f99cc94841be5baeac061eb8626ff76f01594db53aa17cca1ca87bdc7278b599233eed9ca3a652a9fde86816dcf9d0f10caaeece898c0819db51b56e09b3bc1087d895aa041d0b1a9a21eaa1c740a5b708208a42d18607ca3cde1983d9d3beaa10e4fd5609d6053ed0197dae0420e2e103b03e4f50df10fc3b7e6ce43743fcdf409a260407146e0152db78aab2867c600937a77ac60ebfff5d9cfa972277a7ede295d9ce2f16d6f95f256bf3def4e5a0025abce572ce9846513299e8b046dd9f4617e0b6e9dbb813dd77625eb064c67306d8d0e6aaa93009e693410d9d21402c363cf0b58549f196781ec50153f04d0301af2c57a123809c5c18f4625c5e6199dfbd42cb1a5166dfae442c10d7df580d0e58da681e51776fef3c81543477c8f5b56b79314e854d8f912062f35a22cf860aa95f9a018b7b8ff253f620cc32dabe99cb8300ca88d9e54a86cb37c329caa505934ecc7c17f0c3d72bf6d72596b5f56b76b33b3b9d53e087a360439946032701f23bee13508c235a2813efb3e733df4751f36ba7a69ffe6cd12f302b0ecd7f0c56aa824dde54186ecf8ee183a4ee1b45ec5c075db558f057be2edddfb3a64a01c7625eb5a2a45e974afc8ba03c6fdf5afed0aea9a06209084388bfb3a8f1ff0be3f2d78c5b0fe77ab0b44f14ac4fd54edc6ca19225a72d0f059ba3c26cc39104e258e9e30c1bebf48fcd49dcf0762b67582fb4131a2d3d38b00ecd6fb6890a028bc675d4ee56555a8f3dd68f88c376a539c47e3bf8123d4135df769ea7a87a0abcf94428b73b19cf209f3d3d2ca9be230a8d6d3258ea5514d559da199a0a2707a64f03d30558f2927fd39f21db25ba68f6388f22bc1c4714f107e6767b6e7202abff530838c56791dc0d9e6284d4350f58a0d5af1af66f5c11c3db0628765d016bf99f7c5634072899117f5b9b1e554fb857f2be99360cdea900c64e91945905b62b4c7d621cd1bace1188d528d970303ca0b9cd2223f946f107e75a92156605fde9730048c665f543591592376a97a3b5541a0db2841440abfc62e33df80ca5228310f8c8714dfa5a976bf45e201a803031a2f95620a2a2215754ca5a164404a4f5a0a5c814a09da03c307ad2b4ad404bda2e596d462e94c4beb6997fb4a305737c7c1df7b03e92587250c4947a8a7d3098688ce85fe00f64ec8edd2cc5dc0cea089aa9a0b279dff6dcaa33e31f84cbf694794623f593abdc7dc29f0cd2120ae20fca9845ff32d509072399dea751b5c8a7d6641029a4e18959069678e6ea05af59939f9d09fb1f275dc5f5b8d2ddbbc299f4c3dc6f49a0480ac7388524b80b822d07be86218f59c416ed50e9b42904c153aa2f3b2fc2dd80b7399a490e3d031b25e1cf00041424467c0f89b329c584892ac602451c5807723372472ce6f50ed7582bc18aeb1c27d58ab10e703572e06e9c3a084ab6e604d30095cf57622c0503f79aa4278be5addceb2ac2acf8d2d55bffd274b788577797a902e26d493302 false +check_ring_signature 186bdeb4a35b5f02f98f4bfb09635404dba35afef276c7648f822d7e258d9dad 4f2e51b834ede667e1760d8685f2bca23964a7f5daaa72dc023580fa88025b81 41 7947432bcda1d6bc23a83da6f0273f48f32f7035f7b489e449288a234dcdd489 a120ca8804bf936bb84bfe65583f3c43d6f0b2577c43ef6ad5446198896bf89c 8348e044f58a2e436fe38f93b1786fa1ad0076e57893b023193ed64d314bbc7f 167511ce389c69a8216bf534008ce41d1215e3dbbfc40d35270f455997b5cd7b b0882fa55351f4ebdb386cd4d594f8c602879e528cb461ec5448d2ecfd991bc1 9ff68fc1716ef975bf758eaf53444b55e90a33299452d295489b0443d8c02fef e46be77fcd3dc159773d572887cadf259f9e72526bdf262d04e930e44289f265 4f99948d93da1c9db876380d0057bc80f48405a0b8c0e2f5d35418c81319f768 b0d769ce92992af1ebbdb585e0d35822cd7b8627bc68f900e7f6eec969a7b1c4 88a95722ba9a6eef3bccd5b928943b750202def504c92912da65c49f158150b4 ff045f5a789d11cba702b51d0775a793bd945691dcdfd6560e0343b31a2844c6 ac0cd2b24fe4a9b44a6e7d7224e2e338bbebafd41944d44b114fa7dd23774225 02a9ee03f15da9eec98b6f574e5ca402b3ac59ad0296a2b730e720108b6abbfd 07d1f494dd31ed5d37fcd314b0dcee040d75d11512f8c496069b2e57613d417c 98555b96e05b000aeb2d797a5be38992eef987f9f6d42113a2284b80af548b91 e9f8fed2a55ba47459a4b0d15c7845117b603909954ea5bcd4dc9887dec63fb0 1d7eb320d0a14e0f92dd5b247fdc6b63cd6712e4bdeebc0d30ac98aef7fe8cd1 1383e00554fafe2ad716e5e92a6b6b413bf8ee9c6b6e0967bfc7af87a53944d4 9a50d44082b9c6b05ce6f5af299c0b6bffbac1ae6b50c4ed7e4fad529c4fcf98 1efc4e63ea9d5531f64c48688a4cfc65f2ef21b947b50bad88b4672485477126 0a49356ac6aa192a581174f2afd91d35e5316c1b5cf8180991d95210d52172b7 3aed4b3b4e76534dd4a7a210d2ebb541df63da574d5c9ad56bc941ed927835f5 e2c7b99668fd12c43c77bcd3c3b6c18081a8d63f57a66d786b235f3f2f2d305a cd1ad1301a3fbeb34e6b60581f39018cf2dab1899c75c1175b95bf813e16680e 0f1bba92fd2571f8a85e170d0346150fc1a96f061dc0afcf58e2d606d253671f a8db83b6f85e1e674a4bce88125f594ba97453c9a4a1bb1824c875c81e1c892c beaf164378ee6d3d23d2c48386985a8d56460bf67bca60d0aafecde714fd05ca 73cd835dde286d7f3836d1dd08da374ef3e8f0376402d755f2f3d1da37124835 d7e487be6b1c88d21a2a7ce850e453bae3ded630437c443aa3a1d2f173fc3a19 1b29d42f45291beb77b13baa0cc0132fe93626be31894a7b5779dc958e697970 11b534d520858652f198b962a53962ce34090d5ddaccd4bbb2996e4f069f0026 aba2755623279dc67286074240f8cd10c33a13e5bcb48393cfcb58f7c6a0f58d 876784f636315ccd4bf718c2a137ae5b9381e5ad8ee6df3e2aa5edcf4d9b60aa c4c6ae76582616972ab2b5c5fe11df475aba94e73ec4098c538331029a9474b1 928ee0b00eba52fd56128bad01244d4ee532bcbda133bea3aefd28d918244063 92804cbbf41d4c7640066ee1456e2dab6b296ddd26a56ab37dcf52c6f7c904db dbd8c112c58806253203c4b5e65ec52a8807d45b5b547ee9853693ff5ed881ed 9b777e5f048a5a4a68a610fc64b3b370a4960986cbc5ae08b320ec2d60a71dc0 ee33d4f26e1fc296240a89512a99e46a69f726349c5a5c293852325d4c7fde84 1167bad3c7dd8f294f50eba8b2cd5f6553c29c8feee5f711d5b6f4ac0beac000 da69b99647b55f7d6420b7797ec1b4a3e7f3e36fc29e75d56d29385d678048e5 5d14c2dd81b313d720ad54b2d59c5c121bb61128ca2b8ce593df3e5223af170736b3b19b9ee3b259ed0d7e574814a9b45685232498b3767d136c53b4ff57ca0caf645759f18224f8a6661f78448ec9cdc9ff8d33d6878a899239a46347f115070832de4f58f2cadae5ef56472369c17c8391993187c05a6e2406f083aec3d2037713622f3ada4438ef0b86ac4a05ee7ee2c69b176385547ea8385d3d3215c703e64ef187178cfd3bc3099c9b418b82ab57383a0250b018329fde447df5002301fa13cdb85f9de7b08c8faf96ef743eed4b2e735d08fef5769bb2ad6abf09ea0b46195d1fc3d161440ad2ae2e9af6e564979c4e9ebb6434404d7ff31ab5bd470f7027e0dab600f0b1b6244085959c7e3a03c88cefa6fdd51bf321aed0865dd10dcf56a4e49c3f7079c6a12d02fe3a39f42b3c28f3d67bbd5d47a8e0efec9e8a058e8d551d861eea6de66535729b4a93eb0cebcf95320de4121cdd034a8fc0a90bdbaa42296340f68c44787e799a2394f710a7843a042b696edeb58ae8fe130c018e022487846e78cec24f8b55d8eb3ae567888b025ef8a8d0f467811d7d499c093f50580d88cad6f99088bd26818cd1e9c46475a3253c309519a3904f9c828802a0dfda24777864493873fba35f3d8c02b56fe6a13992e09cdb05cf01a16458087cb81d93b2831d43d378b0f4440b18946ef3654e0217618ca5c25ebb9da9b30fb995866e7d90297daf98b1a952156dbcc174af9a958b5ac8cf5d28c166bdce0421852500ba8b0072d22b44fcbdbcf86f834ed97be099858ac5a84fde9d327c080e90e0e5732406b5a9a4c1d8066684337fafafbd682dd572c4c62736124c500a5ab3ccc414782d6ffeda354c48df47209ad192e23f8e0bd1069845ee209e88004835ce4607ec0b960ed7a49fac83acc8bc8245cacedfd2bb076ab8562756d207493a31ce284e302e054cf5fcdcbff02e7de599a274400252fc50c389cd257b0cc3248a705792663d083e81e170f3ee1df26bcdca3dfb328eabb21737b84c87005da8e0ae272c0808ac0ccfa5a8653d808d95a6c9271c49ba26f78990e2fb4b06cb50a1c17513cb50c5c3ed9425ea3b88d4a9cccf4e8a598fbb316a1128df160afe1f52e9b0caff5045815e1b86c640a53cba36328028bf2f5f1b16cec7ea750c52aa896219f280aebf6d31bc77279b571b7545142c49f11854251e7f4ebc280b9a38aedcb460ddb49f67cb381115c70b7bc214bd61b6e2da6945ca7b42937d0eb66965d573ce943958581469d2f85ad1186daf19926e12bcfd5e21fdb3ec080ebf78519bfef7ad00d770d1ecb4990a441fd016107a548132e26593fe9d7f7502f28cd71e5c2ffb1b4408c656886379a28e904fbd99668a2850a3b67f6269940463e8cf688111df2349b673a4ecdb140a5fbe46d768482a24c9dd715cda731803328fc594139cf2ac1754eb5175ebca560609baac319a724cef9c682c345fd70ccc8b40197e489115889775faae4c0cd5f7cf5cf62b2d79455798a07a84dbfb0ef9918b81501e47f89aedd51f0d93b36392611e86f28cf3cfd963904331b2230d33efc4a68d47e16d88deb05d68f35cb22aa809bb2c5bd1c30c03cfbe74664504632d320cccc8afa540185c3f25372ba8629c016488d524df8eee80fb7b037200a83d7294f2fee60158f1f6789750bba410bb9e0e1324bfa104410df10ea3b00688d0c24e812623daf041879930c8a368f64f92fc286cf1b763a52a9ab3d89e04cc2b958f4c13d3f7b78685fd3ab2e4ef1a1ad90df68206be4880022fe92ce7078e2bc6789d56187a970e44050b72cbbafce7c128e7895d2ba22c428880b30c0bf7e7b6aca57630b91abeb12b5650b5ee0232f70083ded9731841a4742c97450f00ad838d0d34d91657de2f56be6989cec35ac3a549459ee9fdee50e4a11f310d847f69a40673a860f37fa0d3ff82c80eb70ec21bb84237f3939239670c4b3206697cb25cdafc64b2a82c174ab464206d8e77079e1a2af24d423b8d45131312061fe3bfc890a9b1f8153facaed13ec8c2921bd0c602b61e468aa1ab26d5b9e50ccd1b0bf230e02ca251838531468eb2b6c5730bc94052181d8e43227d899915031c5b964943700face699237b290537087b07abc44a2bf2483c1afce512463304b61cf941a9be56375c1d9ca449a4465acdd4d7a81cd78d9bdb68d341754ed10a67c1cdfbdd6385aad810fa650c34ef428a467b6f7740b0241c3863b61d39230dd435fbb7bb72ad67f17b9860778df1edfaa5c5a17d2b10894aee045dfa75370cd1a325ca26a58d926a8c00712c5f8484aac191acb26e6eb3bc7b278fdfebd00ecee0e24136501b46377f5e60585834aa1f240a240ff009609e54415ead45270ca3f28c2725cccfe1db47d67c70a916f80d147120f9626fcc08021b3005a41204ebb8268e22511e364680099c66f6639ede2991417c8eb6269878e6fe3ea72109a69bb54e378f48e732dbfba9662bbb2df2c17f047007ac4cdb692587947e1400a9bdf05d3053cfd997b107f3d8e658e95d65629a5cd7cf84888abc23b838d306efb393700465dabc673a5c6c9d1a8dbdbea7bec275a0f3437af7155b8dcff90b4ce03366edda1db3d6bea2df418d08cdf5caf023e2ccba6a90762d628548ca0f0afe8313b2e8fe16e71af673961d113f7c3065267a5551cd36eef152c8330e0fb58943bf9f53a8d1ba5a14ff059e94768402eb725309ff51f22cebee2509f606e243b0dfef6ffa8f7bca822de18c6afa7c63d7feb68399adce25da3a461dbd0d458c503de07f8b1a96a1d9a4c75f2a795896088dd6b6a6f664ea511ab3dbbb05477378387ff82cf18459f97517afbfe219d51ebe60ff901f3164841b2b31900ec17c4589144c0875c9c215cf480243bd1e645d257a1117d5be5008ad340f3f0d625d66823c0671f7c51d7e1eafe4a5282046653fadf9359a083e127c591efb0a2c1087d63aa3767633ffdeb48977a2640b0336f2c1764a86278b1829274d6e01d3f488865955ff863e0f5d9fa44ce8dc8c80628025b3b97136383cd62689930214cd9c37c110a6b32ea4909864c1b02480d1f964eb52097bcc7d795c634c800519e3832e13b008371112d75b2071ab0d7e6d9313ee8c0d2f25e1ddcb0f7d2102ee286f6020c6a083faca6e32092ad37d3d822c057abc7f2392c19e23b9c3280a26ca2c3365d4609477b5de33e978a0adb163238814a08eb55d0977704501930fb35a11718e3fb5dce6a7986fa12ba7a61b3ed8400b957ed8574471cbdd6d1f08c3542bc18b56ca4fd96873e3215a63570854e6cec0f5943a1e6eee6a43b94a08f00ba0780221f6a1bfbe3f9cedabe0d9e285002a980eab7e4bf82859cf519e028d03121a3c99dd24fbc1328389e14476973209f48551c46b886472e2ef1ea507986748f61464f88903ab55cd9078d0f40099d502fcf4df144fcb3ac8c9954808eef81484e954d0023456d6a912602efd326839aa15f58aecb0d2d2645165200a99d73a40dfe33e0a77401cd573534ee3b8ac5c746dc52e90b8e111155c6a040d7b28a5c5c23ff5aa4c017723f67b88389483a884d8818fa1fca2141268e3550c2a4ed3e72bfb0a8c05a7e1d2042c1f4cdf81432cdbb165e7261c238020ea34068c991095b542bf8b5a01d2a9e5e78460ff87bbc940889968705c6a90311a8209 true +check_ring_signature 990fe002654ff80bf37f4532b0a4dcfd2aed520c2e67c735ae91fd8c4aa26249 2bff7966e5cef8770d0f01b5aa7a2ba52cccfe0a0082dcd502bc6d01fee70c75 48 fd6c78c13fd19562d92ad9654740539f87e724d2f17427ea0a0bb88e198563ca e7dd644246981a880fbb3f16e657b9837cb8132204982b79495b180220a8ca0a 94abfd919dbcc1fb63d493ba58e177d21d57a3ebff3077402a11f5811fd1fee1 7bf135999f087e964864b39972fa74223e4f9449b55962062af185b3918af66f eb12cea7a522afe9f834c58c87683401cb8d116785c86a94bff49ac61cb0c8fa 5259b18220b3db171d0c84a3ca8fa3cc1fb706e3298a89eb3fc9ef687f9731bf add010da2d85f5d88771aac3bd15690c97417123eecb12f34c2366b7941af9e1 e5c395c4baff5f7f7ad06fe4d4ce80e4bc4a6e876d8133d911862e7732e70179 bb2b5ad17409331d529e70b0d4331370d846bf8d091e15e3bd0d35369bbbc1f3 15f21a3e4b47d9fe9a49650e852f11367117070236a0ee9af1b9e5069f11e1ba 12cbd5a2afbebe188e7926c7d9dcde51c76a515445b9b82089672254712cab6c 59165c3a0f3a291ac3e299ddb4bf15e4fc969bb29c19e2fad3f0a821e18e9f44 afc8a13ade0dcad440b7a61e97c8ab54487e92fd30738cbbb732d274d059c4b8 8d4f048425071b9b13b89570b8c5c189edcc235d71f67b4c9a9f377e671a9b86 c75b4d7e07f9478b8f83de410a5520539f618299be6f2ed7fdee81e2cc2a42ef a7b1d3da6c4b1aaa859859f65f61c19d4499a0b54c17857d84f56d69c51082dd fb5550352a314bfb2852f5234ae26d0b998db1d2edbf69ad2b23fb6ec95281f9 e2a2e5fe6bae4bf0a72c56a7079e04155dcffbfbfdcb583b32efe4f72e0e716a 7fb2d7537129b1569b9315de797b09cc494105f8c0b8192c9153b8c8ff3996eb 37b130fda520fa47cf6dd3c6ae6e00d2d5a8b545692cb8b6d7704c25b9d36afb 9f3c18622cc4b1791dcd10766d036b0b740717a72e3a6a93d76c3568e868e571 8856a9df7c18339353b7bd854fc8ce2c55e4d054b04ede9c319857cff1ad1354 5f11357e08d24551e893c5b0d34c97118396685c53395df2db732d5af0c3da1b 276be1a5ad3dc79ba6600be2fa8c405753d25110df4ce9283a823fc9fc370b0f 6ce52e88f59df7f1f179aaa95519b1b0c2f84adc780d4865562bd3c81b3ebe26 26bf3d9bb5a240c16fd8068cee3d742d268f0c54f6945fbfc43528714602e02a e8e551e920c01f93255c345f850984ec04d4d55dc84553f9733511176fa9ca49 03598c6f908772f61f9d93b47a8093cb0a9c990ea495cbe18f2087baf9cb06e8 61fb8099aafb9d741f7f232620111dbcb3f3797faf8c1598b0719a4621946465 1f28d6da8c8ff8e7229d510163c87a4bd3b041b20166c01d85f96e8461baceec 6100b0b648128573a762836783d71500bdc3a46bd4e57d9e416cd6884fb1b5c7 c158983eefd492a166cfffea0902795bf1c852742f7c4092d2b9cbf31ac86221 c4b58a48c0102f4b149bba6399627987cfa79b8dcc624bc833025a46b1bfa62c ac1c7e6a48343303b4d7c378e6c68812aa0d1918eec46d68218a232cb8a39ef9 f217dd80317c8e9e40360f4379b4b85b263c0ad76f70e5feb2f4e652d842cba6 1d6ca6dc0e933f777a96c1aadf76d7f34ed8d6f216663296e361e4559918499d 3ba6c54d6e3f3b769b283dae928ec872258d1caf0d8a0f232f6c7979c53b1d05 821d812197f5a06d8729e1fa9dbf148da6df0c30c415d743c5b245798a39774a 2813c0c19d12983cf3cd4ecd69a2db9dba4d632d97ad0c56d01d76abce6b5289 c0294f8861c6543880e579d23b6933cbea815994226b802b0bd26038116f6fb8 902bd23b5358144e889358638042bb52c7ae18b74197fca07e3f652527d90725 d8ee877fde0940f656312a963cb3fdaf5e976ad7487a9d56deab707d86aaa67b 949aa18ff1247bc6299d11083d7718a7d0b2394e6cea490ba02db7001127dc74 b23bd1072eb94336a7f2f9e0dd04fa2b38ea78e3309bc8ba7a54b3d940c027b4 aa0b720be59faef6dacb14ac501a84bf5a031ee05937539f049bd2fe9a98efa7 f420cb0eca42a629c40048a83656fa07f59312ea355bcf6084a8804fa213a590 04bec3f626695f8caf808cd522cd41b9e18d32e0e8bd88c2cb978d90e902fe1b 46500da98453901f97474a2391f218d70ee626684060ba9c1f791920cfd392f2 8b9a5de9aba0ad9d0805fa83946ff330efc292062c05cd2651507c284177bb045e752aba2753b2ff9a26573dbb32cd0d67eb798cdf9b7cc900445645d413f6083d5b6f0654c0eeb8de0b3e37350367e3c2ad43bbb2e9872b3c773f5c004ef704a348626673c3a323c7c51f337988fafc719e0359ad539a33fe0c402c78b58e02920267eb4a6adfa792f5fd14ccff290b12d14ddab57e9b074ead8ada654d0200b04a9937f9ed7807ff542000aeaa2dc9b3ce3ae7f4df39519132efde5baed90c7a4a5dfaf93385907490cef9d4161af99f9a6c84fb911f5093a69c79b66e3d042d2f84d2d0fea4f60d70c146d41d3a3150fb2dd51f3d1a7824f5facaddc0150e44091b13ae7af30e6b59ff18f36d037c4a9dc082aa3599e29a7c04927d5e480d921600b2d3731f55cf0fd327276674e8f1d37139b17b801ca4da46fbc881440733d46221cbd32db5c391227a3fac739d13ec3c7fdf803223ab9376cecbd2b10944952a88a581089c225aca6a2d43b87b183cbdb76dafa6a07b735424af8b6203e75aab059d11f76104a771f53f539b74b288faccc3dc3c316059a6c1447b3205224163ea7cf68f14b3a9ac8a10c851252dc3fe62fd105529dbddbdf937b9ae0ee5f36043246f6a43e6ac67005a917c956294e7c2b9a83caaf3ccba1a01dbe2096139efcae019e377a8e301af754499d2fd803bafbb0db56a2ee336c5734b9f02c6b6dbfa65ed99aa1093a20d0ed03fdcfb21069808611adb1d85921958251302928719d30308f363e16c55e4892b165b1eb0dc78963fb11a8845444a4d02bd037f85481128601e5617a1317aae11edbb9ebbc712cd9a6911b3fed20d755ea4049a9c251e7216e11647e652b5aed9f5fe7b72425fbe4acb352ddea40a838519086f8e58cf8cb52860d552b2359c404f0ae6dfb6fb575cb95e4cfa975f55fb870958fbf12f7cd5f16c89c23d441ea702ca18e3b0a83679b83422f4d07140758f0fe820b94e02a5c72ff181d8573ce5ee929a56eea943b6f7decf6edb4a4479e40045f4dad6a547467715dafd5d83dbe1384ce762cca4dfb1cdf42b3016eafae201c5a8f2cbbc68d3c8a4e321ca202c2a4f166142bcffdd2c1f278302272f2f8e00c3568e6297fc6c1548a05640ef3e2a0d194dca42d6becfc6b5307153a0b4020fa9692d10cef6274e6dd8d75dc4a0bd57c6142bead2d6b6a29f853c9351d6ef0f0d7c9c1f5f6b33930cbc3e3185b455e76b1c70b91da2b8c1b59903fa7ee8c40bb6decfc19ec7826728a457731528e459659aad30e5a09f819d73dcbcfce3780053fa3f71b77698d6840daf2dfe389fa487d0d2b60edb943c1b94429b61c18507b13dadb8947ab76a4f8c5e20309a2d0f6ab0743d2585c5df69def84b821f4f03848957d985cc1c69beb081f9b36f5f43b7e93e808d36a2d23c42a3e52b67e80d49c898a29bf3d21b357f277e39bb3552a55456389ca7c70678f8d290c47c74077dd629cd97b4b3626757ea1f2a611d5debc19de32d66234b2a075f972ef4570cc3515a54efddd1c589a45b61f82ec8cd7ba9a85c785d5577bdf0ddb9cd3daa0dc6cd598a7a573e86244c05988d66df03947ad31dcd2a41085ab98e126563c30da541943e6d855bffbe7d31f12c6f18d2ed397a31ecad28f6f29b00f06707380b39141cdb8507da5ab622997007e6c35fe07f775ba2cfd956b0051cca20fa3a0d08b8395dfb37ba154e9828814bf340aa4d7cb0bf6bf6a0ce5d3e4b255258a30a27476bf63be726bcbbd2c3227eeed0d69bbdaa833c56622dffb071a9e365450bc3ecde556c753a93152dd87c3f665b1d633f2b92aea0ade270806b834d4ae60545f42abaeffe92bbd56064cd06b3d0a321adfbe36983e33a294b94cc9f53e60e2f3d5425642df079d95e5c8d0a213c75baf7a93521506c878762dde35879e300d2fb1d0c07840ef0b3e8fa43373085fa499a41d13256adc4f6aa6271c79522074244f7745c83f5367f0f280eb2f4110d1c4e6ae4b451ee069dc1057c50815b0d677536a8d8425294e583d45ee6fa2e73844727a4ee52f173886d68751195cb063aed4fab1e8ed4d0a540a9d067c70b7bdf9a52a670bee684e1957cf7a8ae32086939b335cfb240af407fffa2088ab4c5eb1e94c975ccd0989a36f14f85d4bf0633600c7dc803883c79212aaa9f87a7a386f6aa4327f27ab3e0d681d29786cc0031c05daa79913e0190e5119d2ec33d9c1dff57ef2f2e9f61cf50158d6571620fc378ce17e76c38ce2a925c0cb7c801afabd8536a22cc4ea169f396d88433350a98df8a393892b4e372bc03167a7a33728dcf4aaf4712f7977821b1b4ae6daa02a6ff3af55d9e3545f3dad35dbc273a3c39fa01e787f69fb87324fa7bf65b7a059241ec5d4597303c85fefcc1fa0cb919b49e23353ba75d0adff79d6f125c6f0e97333bd4bd8c5f88e1d95a985cb892342804a519ec087fd20b87fd42f5c02bb6898291842b4c96db8d445cd89b6f2413cfb9103721c8556b2f069d96ff8db60aea2be126523bdfaf6a4edef7ddc71c934c5b8c7b0b4c9337fb84845d1fdd7e0c6ca1e6e7d8fcccba0c5a86055f856357676a8fa731422f1d4493553d50e64408409365061c7841193f87ca8bf075bc9447180cf0c692430a515086b5719c2302acb283af5fd438b12b0842414633861ca037ce40c134e1327d556ae11b38a7001f33ef8c1e6b71aabc99e2c4e977e6da12f599a10e2868875907f8e1f370a40e072dbcc9614419722ad8f16cd1b0c605f5c5f928142ea107d146f027d4dd0303ae6488d7f22217c529ed24f814ecbebf281862a0fdf9e125cab29b981e9b1b0c9830362faf618508cdd0c79ab7313b5a2f00986dc7e711cd2d065c078323510690441a97a3d099ff8c31b3dddb892d18b983e1c419b96ead64b6d87d5585b008525104e8c3424e1f23f24b0fcaad240a1e0c1ba8cfc1916cbbdf29a4ec618c0ca4ee51679337aa0f4ac1f4b2e4cb4242cb7c244c6deba95e58c773d584098b0575579c9eb28fe559cd1682fb67675faf9b2226f687c879b41dd03cbf1ee0740f495cbcb5d90c976d8436f4dc1373d5e22b65dfc81fa88c4401406651438b5d071c9471b2133e2005ae8767785d59e36304bb0515b3187d7d303bed28a1744a0a82f3004e8f1e2ab20ac4471aaf14b77fa21f755ca096e0ef627f85870af5090e306da22281f9c880f8d145a03fef0c027f1f090c1b0e18aa57c51784bad3f90bc69d328d26cf722730306f00dce9dd3bf9feda4ee41f6b62cd1e5ece0bef370ab414ef3027fd99e5eadc80d67a5815d709d7b89df4b5aefc5e1375d6a672dd0f6447aefb5e9c7d132898749ebd4fa0a45732f0bb0828688f0106b9771780be040d857e3ccde0ea4578cdffcba24da763c4c59b8a55fa27a7f7eaccf43259b504db6b218d353e00275d1f0556d605857a7442b07b287fb7c4e996ca492133e203a28a91eaf37f304aefee006daaf868c67bc51500bc0610878530774a3acd6d03021ba04af382603aa9bc8410ad1606bbd67db3c6677588d24a57c07d42990306683c04fd801048913eb99fab0ccc983a98da4383646ad9e25de7ede6d877d10ee23851789316b0380657096eb03892affc550f21eb3f507beb2bedd29282150f733114cc040534d0a31786e528cacfb33306f32b96f74fd8fcaed5f0f0f3c8027120375b8e867175132f04c4e6416cec8081260170ee89f5de43b36a5a606d010338dddc781ea49b46d819365edf7a0cfa6bc7fd644c0086fc6702f0d3d9d706c644ed8a30694b6c3092bb789a0750a7fb71e6c967748836b0b1483ed56e9c02c75f57d309b8638058675314d1345def4ca3bc31273cd350f962ec9ff1769707bddc056079240e390e713178f75a5b76f49ac797ddf397c0b65ec7f51c1e7c0fe24f85cf114784a5928cfc4bd68ac22011ecfbbb4b55e60c1c1b1820922b760b351ee62e84f6e8f81253a05b30179e525196c138928e43834e9140e3112aa906ef068788042baca3fcc5a5769e4f012bce717ccdcee7d3e78eca5cc27f7be0097c2b54dc7cfac1d12658bef921906a0e81e76043864fc6786460fa3277c8ee0a024535f6b6dd595fd7c17bb67758eca3a78c3d2b2c5dfcb7e7d32c1248ba1500f2abd65cf50f14a43eafcbfa3dedccdc5deb9e0818747bfc7d5d97abc1cc1601f25e51a231b59157e617f81e56e2e471241f72917a1b05812dd983fa6b0d700889a6f9fe4473890054f6d1b45944d46130ad78dabd0b22c622f37aee8439840680d01b186df1ce301dc727a435f150c00a661d888fdd4b53dea4849b73681902 false +check_ring_signature faf02af155e95d05d236ca928674e083959ebe8b54806e03b9e491b0cf3d4c16 25301c185c6ce9f88099c788a286c4572b2ebbefdf4e900e37a10f1c369355ca 1 02c4a482bfa5db75ea1fe8470d0d46047bc3d070635c2a3aa3ef092d30fecbbf c26effeadeb787d540f2666de53f84273f2390939cad46ce5169f9abf2fa410a732beb9516f7854eae8a7675df355d3ee3d3bdec1fc89b5959da2e9a30da2506 true +check_ring_signature 5ecc39c73aee026178902fd247c3c33a6b489d1f69adf7d6d932dbe0fe4ca160 a78c01745da1d9239ea4af5bf5e6827fb1a06dc4bf440535d98b073c2a39de23 30 ab3fae6caac2f0c0999dee28985054b30e92f7f686f8496209b8a2cfdf1fecad 56e9f95bcfc3aa8c92ab89cefb37714d84ee726f951e7989aad3bb3cd7c86383 099d02878d8e999f4d24ebd53d99f1fd4ae26060e8ca42e175ae19d3c1b0566c 86d1353152c2f0d300a91271af39eb7f946b80e8c0c95bc0ca6eb9cf7e1e6fbf 8f529ef1b0bc75a1a8e104070eeea5073040c7e8ec140e87e9f3657bed30c616 02c10c91b55d2fd3dc3ff10eae543a89c341521a76eb312acccdba67eec9b65c 926bf66abee0153a19241e6f6d9123b7d0e1dda2149976f9bd9ec91feb32058d b096c136f42781b29d49b65906e229c92325cb212e1c0a6c6e673b1434ef0eac 4fc3459450ada46c8b0c32baa67e76320df7b1fa8eb39e2177a5b83a4eae18f9 6c7f928c968a2d417684fd8a55c3485a1ad479f9f68538a80e50812b7df80e6a e100c80c90b6e05b5dfe0ddac36407393b8a3cf09b4bcc94bf9fd1d057c0f628 cf2adf0b942a8844857c7acf2d64c229ce1300d63f5b8e940af18710754829c0 a2efc32a4b66faa304215c8b604b1f7985c07869d41931b152a781b34562e395 28a983f55cf7dcc489f9f157eaf8dfe031b50abbae96a2e4908afeec81df5cf6 939130cf980275463a6e7664a653b27bb7eba65507228b6a768a2126940582d5 f0bf442c715143ac9bf0843156411c702a6d16b919436fef8bd4e84d97d1dd3d d66c0b48b50d48a60ffdc472f5a12ca1a10d336c6c0b141e52bf3f6c72971ae7 ca62c7f738a6008278d153ac6a539543ed97bc9d92bbd7ed4c1c2c0678f46c64 3cb3ce0263ba64de0085396d4fc855232abfd7d770f0dba4ac598bdcabf05299 6fe7e1d26abab2de23940a1b8855a738d0b346623ea8ae14998dc36d6cfb92a0 20799f9fd10e7c4e56842b563acd48540cabb86b2384df0d7ab89c2f5dc065f4 44b11ac38279c96bf1538459717cb1f9d89b6a2ebe3be18e822f77914f70a683 3b69a9a4cb048f64ea02b21a479d2dc93d2b960aa6d77fc6dd8036edb15d3d27 b80a8947b36c838416878c1a9186ccf120ab42520d446b4bf68b3a02bd366a9b b7b0b7e91658bc057dec5b6df42d8fe690b28b5435d77f72735496711a841192 573bdebeb575d76cd3c2e9dfc100b47892691d5806ac4bc2e43e74bbe4be7891 f6452df2556568b380aefd9ca2e70f8aebe07c0d35ccdc7a0c10fd0b10c6d3ab de4cad90cb57df3a5b398830257141e6a34c159b01e83d2100d1092722fa8885 0cfa0fa3161af06b916d615ffbc744e2099a82e40ca8a7c336e12f391ce4e531 85f7b64fc4a6478e1a35c0ab5038a07dd8bc69348595c374832c442695d4a263 0970e98a41f37b66e08c90bc2f56ff2d22cbcf934f8fe85969f2589c47b2a60c1f6dde2acf48f6e4ea724b08c11eee65120aaab83b7275459a396e34f12bef02795d1654efdfd3aae5c4fd80bca19a60e47beb2c500e5a7990fa32637b1099069559cd256179055d9ab92ded18e88db66d53e2858e59d491701f48132d05e905309a5a17f01167b8c6019fd8c0b9c169f163bb935fd7cd14efb8bdffdf70d001f54ad4151f062f34eeed8ebb46637f3d7cb0dfa1c117bb77c477590c7916f80a3f9647884d5b15a0dcf4135d86e787101d7ff47310bbad2a8b8058f180cb3a03945b97663334e074999cea1622fd6b2dea2fe6e11580596f0cbd50ed75a41d079fa074d0bb94a989258d3a9e631ea38bd19710aee758eeb857339acbfbd7dd06d48c7746a5475d58328513694fe4c1fee73d17255a4a1e6b252d374d732edf05e92513d6d70c4a8b5caeca1e1f384d889a68010d50e0e5d69abe2c6d0ec45b06f70e9a2dab6728ba3d30ee2c6a875ba989647eb1dbbac0c6e71319a5c719c5063a55f6eaead2b3535e01a0270767acdc4bcb97a5178feade1497718856d3560e01a38ec573b7e197581573b8506150baa31cbb840d3e6bff8c444ad98c62bb022cc5959c01a412a9b25d320c60f488224a8a853d0c53c758b4a705860d8e2a0ae5bcd81bead513d4b41711bcd2ddd35342c28470f35c546690870d1583f6e90199e4d7d4f6bda7813c759cd201f99e01f9df4e369e04d5aa87ab4e1409cf3d0f185d21ae29a77125dd2790e39339a3dacf30966423112a0be8b7e35157c60f0c90e469519233772ed8bd956eb9ca6460435ad34def5abffdecdd781168f54c0148ed30f41cb17628cdf4911723967dc94b7960aecd82b9266b0a81d06130c80a3c1ec8443de8befd611f3ea6bbd8182840a683b8685b0dd2dade87a60af5b6057e6ee62944b7a3fdf07dd68f06854ff0a87f6298973e01e31c8165ab17fc6207d30edcfcaf8bf0cdbc624fdd8f598c7ec6da867dd636f47927b1b9f516a665051b989e9b10ff72f9fdfc5be63163c6a7c58233b9de496ac065e392071f3ff3035af89e5e6f0213e04f31bf9415b2d39c7f3e1f7ee1e40c8d9e6e12c28535550a0f70c799539f2f5a68b8767584ed1f15dcc0113e3c9496ffe174f5dbd6b4a70e57676e9a0da51e6c78fc03ca82bf2c401808deeb4d799c22d7050aab740cbd064663de2fda90ddcc85eb3127326be4ff81c7aa26ca98b50bb474c212d4271c0252f84f9f6124db7111bc14bc561b8840a00eee2c5d7b0578a11319ac32140601f0220a14c3090e2aaa3cfefd76c92053a119694138d2adb15de6ee857ae5110ddf54358bc54fc5af6c83e5fd0c0e28c6d7cc4b391c37216b8a03c7559df20a0f1c6d850054f3818d616d1a340def568034a1d8ea29e2c48050d8ffca18122c02f0481e0dd1a3730bd8817bc5edb064a66d420e8ead52e300ef370748d6a78c033eac3ad7f5a68fcb478513fb90b350a7ee563ae16a36601f0a1c4ffa14341f0d53329829ecbae585fda8a4c01d0712b624ef2461184ae8a70d2ed4115db536029874448f977a857794a175e72689a2316d0735056041802dca2a1b93b3b3f8020d7428c640fb59babf18cdef5395404d9bc99fd4fbc115dea2a5cbd512240c097dd2e6dc8d1d731f0e42f66922d50dc60b40701db54784a1969765748115fb0cb0fee03a637297a21d395b323defbe08bc08381bb09a39c6f3601bbd74be1305811900df8477f6010c338224fef560edb90b49592f1af26b660eb369a7364a0ff307fbc05f49d872022c85de3f1c387c622d4db2a0d29c287f2fce95fbf489a0cc92b87b6a2d754cc35e28977623b963ddfdc349118e47aee55fc4dc50bf870c21dcdbee1dc7f328bc7cebdb6c3ff7f9daa9391acbcf2d9ac253048776c6230293b6003725b9aa4d19870a3dabd3a6e30299d011a69a01eb1730b8fb7e5e1f0f774b5adbf3bca911de9f204ff79d11364e0ff12d17b81fcc54771286a557670ff8a4647fee8ca1ff16a71013a3c7097d701d9856f8cf82353f74d71abe559300d20809611d8ca49e27b6dc72695167807cb9b5cb66cab4cc9d61dcd83ba31c0a3bd6275f10956d7fbf6826483dc59724bb93746454178427a9b274b6d1bbb0009c8ac0b9a7b18b328f28346482a821b8df6112a7e2978d5c26fd81fe0dfc7d049d44062e45de21e1c62ce2ee79275d3a246dee75e630d1ef6570befd533f70026f7a16499211a09c672777aa37297a38aefc485d167d03d9cae9a70d64ffce059a7870c972f1fa05a465353f3331a807d674e3a3180f25e04822752194c20c00ecf5de078b4c4b852a3d71b44ebe29ce2b55f23089523939bbbb80baef7eea02bc34eab7e3cfd5bac1142497ecd2ba30e28ab7750b86477de94226cf247c1702327718612484e6f961f6d31ecee0372525cf7847b938a02b1a24928167fb8e058aaffa747f9af7f1f6784e8cfdf6bed6c8f3a01e5314ce701cdb61494c9f910d417c72a6ebd0da0d5cd1b48f30b4b97fb8e7284f6eabf9ff0c82a0c3cdbf180109ccb138d65c94b70dc899a80be3aeac56bc8717a302c4a8b735bc432344da09c8fe69c255f1954380f353e11daef9808ca6584a7ee9edb8cbe3718bcd0c6404d8348cf5ed71af43f429c26c73832c5a8ae5ca4f70e47ec49fdef97112904704 false +check_ring_signature 2fe060ab9b9691629fd782e2d34021f50b86f4281f17657281b646d0f2593558 496927ea1f2d3cbf333694db6213887cc7adf0dbd858d0a25e90e6f9b58fa8e1 54 1c106574184a092e051893c2345ef3efffe2103e41305aeb28d3dd9f604685f6 2db47317affe00fc66384a9e1c32ed6ea849e339d424b064b5ec9f828d7446c9 7cf1f4509858d29f658dbf159a0bb3ee700daf7184a4dd094f88585f5173a513 d53f54df71ac95bed3961bb969da9b8becf9c107576956a05bb6ce29805d3afa 5c12cc727645434b4a54ab667278655791eb6209d7811067691d213dabea58d5 7400b6ba646b3a76bfe331a52f14471c610a6cdc59723dc069def6b650c5f752 b96ac9e49f3d495e2f768edb0663c4af03aec4499194206e874042182260d29e fbed79e8d4919e47530cff7297c6e4b8a2268a217d0542fd4fc579d521e7d539 6c6ae08ca5a7dc9de081c7dd43a2a373eab0d3ed6b11b9757cf6a89fcf49f829 87e53d0514dab0b1a44f15f1db6dbcb74ad4b81a30dfc6217b85845b573857fe ab7c77e49702f2e751beb50e3f7e79a7879b262f7ba425d0b876b95814e9d90f 6c61448186b8e7be1834d34a8e21a906aac7654ef46b5a388fdbf047c07e6f51 a4cba3c157efe24d386a0b4d4d9aefa6b88fe953191e322e5be7a1129d9c494d 0d1bcda6d9f36b008baaa18bee2a6fee4ade2c8b9f45ff38d5568a5d96d49911 b9c05fd17bd7b4fc8a8e8a7b2eef5424e5c3da5db75d9b44b7dcc64c5e5c6ebe 1b0bc7df82e6763be18617f26dd149e8e6a471cd82010accbf4eae77a7672038 054afc3e0c7dbba6bf870e9b9a1f0c42b0fce4fe9514b2a04c2234fc71e55002 debb045aa90b835463149dd807f16af22724f0d8aadeae78ef4e61a5b7a87a98 c8a369dac1fa6a37de81efb8537fa5801a2dfc24b4adb0529b21ad019d922f77 9cd2950731baff7918b27820d31279d1f8dab851867f70f4074db23efc2aa186 aa6561da965f8c375478439496eeb3dfe16bbc2adce280b6c8802f3a35240561 eb978eb1bdab0f1a2b1593fdbb44eb4c43d30937ab1ec85444b74172950bc045 a06814c24f6220ecef52ac7a37c1c26866fda82af52cd1dd269388582a0335e6 81c2510a9af6299bf9fb1cd3f2c5134439b89411b1f9bd2b02a6901363bb6d9d b2a15435089c72b970d85f9cf903e57d6099d0189883ba396f285d45aaedab89 b632bc4d66c9ccc556a8aabde3dc67a241bcc9e86d4f2e4983a825a323bc0108 de7a5563555ed4bdc3999dcf3b35615d14047411646e8959188cb9e78787a8ab 7f6cf9599d28a4109dd8af63a7df3b4a591264397b4fa4985c63fd137805d518 23268063ce79faccb38339c19b54d61919e2900272a95fec91391d61a7f32602 e7a7e3715317a97fabd6c374ed3e910fe504d63d0e6c8819e30400566cfa1d79 037c694ba735493e116906a7cc8b049cfee8a49b2a6186d583e32a8d22ab2588 8425aa5979a734f18a1506d85703ef1a81387ef88fe1e36484e3a2b9e2e79a9a 9f3f65ac4717989aef23e84bd9bd27ecf5f25d94d4ea91222d0927600becd6aa c571bfb3c904918f24180c24cdb58893ce179587902fd3fe04a47c7313f97447 71217babdb23b879e0c559f62380b88dddf709f54ad78ef9f7cc0afd4507252e cb1acaeb5b7d0c3a968c942feb38439f01dd08777bccfced5ab2dd24307ba773 b58193c92f5d4c4522fa86429340b0131922a2e1cc4ae1c4771cd1c354141f5d 19f42214b33b200478ab280b856a75d750cde159f3cb2be33750ad8d03e55873 fa85f0fff4cb1a5086e66a26445bb0c1869bf2d5d657d84acea22982a3f1f486 d8cb4e3ce179479e250d5f69a131a9061012fd31518ace3086118c70d59b60b8 b0153b9237f719917e7df2a61546093757df37eba9dd9e5d51fa8b9407287c6b ae88085aae8e11368c9ad60e989511a3eb4d983c070563859d78077b4bbdd5f3 1ab5951999ab032c92acf17eff2a428e446c4212c2d7e71adc3f467f1c33e210 9ad29e99beb444f6193d04e46341fff6925ee5c862cc61233f184ebde90f4537 f19ea28b73c8b43bb4b7c1f4cbfcc56fc42fa07cd7738cfc9321b4bfb47c0438 653168d154e8aaaba829a276b1391180e0f032f71f395799c8683a864c1f8256 102567a75d5df1ea1b93139c30fed70e37e5b3c417b775beaffb98a131956d7a bd41e581332ae8cd4b246ba97433c7b0be70e9b5126e6031d631951d2adc5ffd dea84ede8f69dbff308ef43ea8dcdcee70f612ac8ddfcf65b4d8b8feac647d7b 6d9148f9754c06dc039dd4a6b3e77c571c8e84ad158a11e991dff6f5c29a6826 41ff9af19e2838f79d753f43feedcf84e8af92f9a2bdb2cb74ae1cc832feee60 59481f1b14b9ed88568dc46e7d239c551b2729f0dc29a08ebafb435ed28a309d aa7bd629bd430606a25e6d407cff2d55d11e384e4aeccb84f2e428a45e55ecaf 14766517a9305effd5511c2b888ca98eee542b987490e769cbc90f6794bd8727 d2318bd7e69494a27f881e63d2ef52c0c21bdedc33c89d971828edd0ff68a4051932731241f4e8db628f8a97e1439faa742d093e0186b85fc8a1b247888c280129cd192193e0afbcf6e1751d099912e07d31e9bddfedc5868a9b2ded98fd87091fce27b2781bbb3a80fe1d765c700a20504ad397f42d2bcb1cf0357a7ec4a609abb361c20824136f76c886a55a5f150fd2010ce2cfcd9c50b6b8dcb92d2b8b0e1ff6c9b26a73d97d09211c4db9d378f4cb2e01fd744a5da0290d20e6f138750b0a6cb237f93570ccfbed3d5781a4af111250ef8169afb655a151bd5e45a9ee020320f34e95480f647f5dc8fdba690d28e27f429214b4d89d7df435cba220bf0382eebdfbe6aee8ddb2b02fab080c5f3884c6b777ab3248ce223c1ef257734b0b612ae5acbd0d331da3279ba7561acfa8f1219fbf5237d6b592eff81f399c97077ef8a2893decc8a0042d829addf16f16567a709d87dec22c397e4e6c03817d048bc99092ba2b59ceb34c70e0591819e953d6c43082f51c2b66bba180f7315c05596f902590f8d1a419648cfeee1607be4edccfa7f878f9713790b9bf3b6e1e0b57ad71a2cad8df5e0e32c808bca739b8cc76c0d4300f8ab1c13ce53171c362014d0c9f61c9d1eaf3fb2adea6868b2a64117bc51d450ae7ee2bc488a66f43410ef2287576c192bfa8da133faba85c070a98d171c6c0bf3dfdd1ceb1e65f92b707a8f0760d2645f0158db3185b5b2a6079a7d37624aa9fb4b49ec0e5eae24e0304d9261f617468c11007bb6a0e80950a566cd0fff24d39b7e29acd6b6c9e9de40118edea577d341b0584e69bbe4d527006a580c03dd8e723cbd60636d6d74e4306ccc271edf55501770c4b1dfaf55823ea828f2a86fdc6a605eb353349269a600e47e6f2fde87478d0e101307d43767515f5b1ad40e89bd2c07fd3e980e471810a82216c0d23028e315e1b7ac161c155e00a221beb33d6ffc4fafc966dc8d82a0ea434d2dc83dbdb39a7ccbe034aca3c8db54e9187f8dd32f486c30e9d3fa98801b01f711968841af4eb70c20936062974a1225a8951830082a800deb44381b40655c8b10d041728abd3bc959a94f659eb84f4261e7b830a7ff17bc8c07902da075cc04bccaafa302ea3ff610a02cb1b27dddd3a3dbb7b5ccb2a6a651368850a04197cb7b9740425a1fd9ed99360facd525c15213fe74d509b695a7beebc56e60e8edcc74f2913836fcbbb4f145e0c10944eecd81d1136f46199b54ec3621c2c036c5c39615354d848c960106549be56b473752bbcb75bb5d8e70ccff4050aa00789f464cf91066234da83d881b83134aba1b00c550b53c547ae7b33ebb56b910b7425ae135c76e6b7e8520face916772af2361afb080141a2d8a3d9ef99c7ac0265e40e58d9a846ca36726bc3489b6a6a19b771177935e917c94c968a4e0b900edc79e08a7d8b7ee45f94995a10aa2efac7d00c42549e12b30f97e583a832a90c261e287129348bb91166b9b0e79956b38f1d7c2af4480f4d1b3ca4a27b6992095a867aa36c6928a6a21855f7fdc2add680b3f545a3908c6dab56beff3e47b702b20c37eee467917f172a8771d7a179c54dcd40a16a312058d89d3f2ad759cf0fccec5bf2d0b1309a58264dd14b4962e0442a9f95fedd59c1d4127d423448780ae537d4bbdde52cc3f6d541405aa6a27115ee6da1481f1b2d4b846aa895aeec07259b89810f96d01903a326333a1f3d1db6a5f8cff76460abff35821703d3a40304a33d78b73a5d0d2dc8e318b1185ef7d648fe607c67038ea0f38eb7992af10d352dbac6a6209053f93acbeb97e884e3614fd95231f2116aac5c52c9863f8e08dcaf69f7322f31609604e460b1f48204f217c19159a226d14e095b59baa25f07cbe900cd54addc4de0491ae6f284b1161458e79d2ee4bd156a8ea1b669104606639a5da13a19bab39b04eb2381fddcaca51fa9299a38ad180dcc18d285e4a70f1c05aaa538cf453f27cc653dbf55f64334039fcdf18b947053e825960ee2b60928ae5ef6a301bc12c2bda39cdbd4adde0649555959392006dfc18e2b9173610037d35e87353ff20bfb0e4dd2b8d4610c5724cbabf248e0787ad4785bd08b00037b598be6559f89529f65f980f15649588fc061e7d3ebc150f08f9b6253f4f40ad6d3964590b6b5cb5dccba47a4a5eaee0d1bcc404d628c0d632d340b2482d505f84862ae7a32b13ed9a7fee68f295f47cc45f8b5681142717f6f2903511ca105b24f2489a4df4b3adf2e3925623b67c7ecf6ef6ec545f4c8e2a496eaa689a30942a45813707a6ae7caf790c058c898885df69d9e6b68c3d277baac2d444b7a0d50230449c9c34dbe534e53096cc813f1b8cec2d5717f8abbaf8ca189267629074564be5666d6de49592c31174f95705749b55658694ea71a0cc989c7b5200a0b7702ed9fa9ba9341efd0575c6e05187e1e8c976dd5739e65b10443286a7ec001b97b5923e30a4518e833381bb68dad4e1d69e3fec49888762686eb0793f50e089f8f2f803c2d917b100c87208bc4be0b812ae2089014fb9c70efca54a819d50082fd35111d617ec0020fb0605ee0899a8ea8874b7cef1e015c0cc6bee1832b0755bdfe819a0e2c0fcda3404358d19e9790712ca6cd8469ff4cd6ace1ebfbc90faeb78576e9711be8e8a02024a6e1fcecf211b538e26263f83e8509146e63e6081fdccc4a279f59989e033fb86375fa59d5441043073b7e62f976ee735795c3010dc87e3848a103b802d4260d1527cf44a91579433d121451fa85f7e881933d0a1252e166bcaec75c095bde786b8cc913b1abbf3fa02c4c03c2720fc5481c8d0b3b6ff036d1c72639e728b1cd0518cb6205533b75c636ecea0123879f4cdbb80ca2109eef0032163331ca5100bdf265a4ab82c13f831393c5e56c8647cff8bb0d540651bf1aba46ad503893dd8f8b2f3c7fcf7d0d02a390412ae2d308dcbe1e0452314080460f6127f045ee61f9768dc9447429c43a94b11cb2abbd58f7d794044da3a65f5f91354902867ab03131b75407a3ecc836eea6e662b1f74d1812b10bd53bcc9581498de418ae9c26abc62c25b0fbb5292eff711a8366bd6bf2761604a5742bae0117584b28622f0b8c0a5e481984939c819d3073c4aab9af23840c060a5979aa723f446f18f378c575a93c20238cf0185ebb6d9616fd6a4e0268d2070e531de3b128708cc8d31ef25560563f7eb0514437a07a6c1578018871009004a5107486538cd8278be114fd49db6688b2c57ba15419f69a70cefdbc848331050bd82a5475ed0b31493ded8bc0c1401088a4278f8bbf0999ebc8a586738d4f06014797d22d6bf0d238cc0c5794d69f1526db3bc8bee4cf3313a00252c603a90162aa215192a242288e4f4b136ace5d24323855c95beb43f4250b166fb0176703e50ea36fcab4aad0ddf50464b47987fb09793318bbf8f8e4e0c15ffefab48f0618a9123cefcdf2db07e813b1cf7c4ba456eed6df2f9443e0462e0f74b5b30f054df73eb78af147b02fab49b53f795f03f4d59586204d463ee961760ea3f101003fbfc1a88b1ecd45773cad382e15caa5380f1a7ea4e5114b1d07f4e3e85a140d4a9f2f3edf99519238bd913bb95799500b2d1f95b496909da45a354d4a7df80758a18fe1c6b70f4ad7c2f59ab1bf67986512eb7f4e93b3b34cbba39fe98e1c0371555016463cf9c5f7b0d8f1bf22d10270c025b08f1749d6fdbea89877904706c2fed5779428ddef7a8463d9c4390ff54637d157ead93de39b105d37030dde02418db4f2efb1934076f2a1c826711557f88b75b3c700500752a50e8ac3758d0a66bbe3fc245bc49b7a6c8705ceb0389de7f9f19f463dc1e944718758f063d80bb075a3d92dca7d5440192b8957b5dc13da7e8f86a8861962abe57a30d328f10f4dceac7ea8a3b56fe3c2749198da3f47529b0e3e96f5c0f34292cfb0cb93b207ddbc12e3187de3e68d68e2ddd0e311ac34ea14aa1866fffd5f50fbaf1f624106f316f2c591800e16274b0792bb785a5ef9681cb7953afe174ed7dfd50e0e44080473084d84c8b5b109e0a74eb1f80fc510ac377ad53aedd1ec46dbfe0543f2044e49fa10f052d198af791a2f6c27032dd1e4686fdbb4641bb13b67994ae2f602aa5bfde7d586c5aee91ea01840fa6684fdee36616fdbc0339e8bd1c32104b509a229358d48f7590f332a698900c645b37d6397d4d5968f8d63b4d43567033706659997dc18cad0b4347b89cf4b185ab829aa4feccf0eec2cf4c3fa71f0a6040c30f21828c8f9cb53b1725b31764c5a6a8f8e79d896ad64b1459dcab3f7c71d0529d8799622706e3d04a12b65d1e16419f606bc9531ee1a79a0e241cae67bf20f12b06e1c5ef9ceb7cae0d24edabdd8a9bfdc7d895ba27e697f6471caf11a1600dcd55f90bb13d8b0aba89bb392563524cdb3845a3d2d51e01b716cd4b1714b01754ae2edb081b094ebeeb8fb985be26d28090f9884b6737fb78166b6700a390ee8db8b2cde5a70b942726f0e1d7aa810c259a4cf505319291f0180a894b12a0c65634cf8148e169cfb076ea90998ce9666446d069759f749f787e586756e320083985f5f364cd8b4654505bffd38d2bb148ee1460da2adba0c7f43008419660624eda03337e025b7dfe31a3b70a4491e5f3b37dabd25343f7fa956820e8796044bb641da5305ed11f85bcd9d9f0a1dab717214d19d3334f77b997dc2ab628202d8414a4b41b12cef5dee15c680d1a507e4ea1010e2aa3fa37bc06dfa93531c05821581b777277b5d94aaf9585e5d69ab06f828680abd0a79757da363a0ff530fc196835ac08d1f60ec3caa7cdaeab1b036a04c729c9cf6f6bcce5126c799b204 true +check_ring_signature b4bdf7582d11e32d7846164361c612b16c1c0e674c385be120f66007a615b04f f170d3274a94018ce1e197fbea67b8962629d265ba1bad0899d4731a0cb93fe4 80 683724c8dcc125e750b940fc42c1a3eb15fe5c03af34efbcd9a146a6d20718de eaf7f69f6dddc4b9087bce0dc8c08e7659bff47903a88aafe40338956de05bf8 b48da08f680f4187b7dcc38937e5e1448b13059b7cb74214883daa881edf215e 7b9ff29039984db372bafb14864098f30273b4a0db9446ef7029dd9865135449 381fc40bc81a62e99b9c9d4eb5cf24b9c3682a087cb223005cf93180dbab4a36 fa39d62c822e6c0de80911dd63c7536633ae573eb5551aba21d7790ac695b4c3 3d3fbbc8165058286a6ee82b0c0c426f5ca13ea0b1c722e31a3a2dbc324c77da 55c721aa72b287095eda163084bfe1af588d446de8ea8e41f782cb290c2cd49d 2bbb27ac175eb539e92485cf8b582145c2344d0e5977aef4a381c6740025f248 fe20fa0abe0068c6ef76bc8f36512a2a448d3be4388a9a93fca294bb15302f0e 1ce00d7bd90f8c50a9b283d3b52863dee840b9cb6877a0da41bf60f181e5b4a8 b21569694491f768a80e88eb49ababbc24a421bfe6c8af1e0997407359c8d820 541537afab6bed3c21aef7a21191ded5daefbcdfe68d4f9c395de22645a9f3da 350c0b1f46266493db44b68d6dc053020b1196df81a090918c005f270d32653d d96472ab2bf0b33e00f8c27dcff8ffbd47e8e6788f8ed8ad36f0d7309cabb4ad 0f8f068a58c5852599a89f607d290dacda87c44e8aec27beb3e08d469219223f 6f98594fc2c16f5b6b35c10a7a0da879b1b46574fc535e1a421d4c76c25b66bf 8bfb516799a9c42ff8018b15dbbfa249738f2d52f0c33eae30eebfead5580926 21c1c666ef49f73cd125af0ac53cd91c9f8c94901b43a1dc09a4f42901238452 3337b3ac90c1f0e5919edf0b0a0862c705c5b0adf19b61461de7ef63c0cf33c5 98ed26adef0fc182cdc4d0ba38e439889a7e9b2a7af20b9f817fac92f226bcde a700e020285bfb2dcc4aa0e008587f8a526668c137a0fe954894ef62472e6df0 067d280765f1d0e23c60cc13d8c4c835a76554bae9403680a003992235c3369a 07666341ffd8c47dbe98a4ded7e5e3a3342c6092c1125dd9aed283522ae28440 1a61d321e0753572295b6540b7d0d05a5808a621dd5ded203456ae1de2bab80e 0525d4b1af7141b2bf2b95461f2194fea38f5449bec2a2e0910cfa19a73a7083 17e8e9abb2965f4aa2d0f983b42bad1a3e3d53c87911a1e653a44d59129790bb e886ec7c38adde57899caa2ec6e66cf74b3ca4a2fcfb5ac214ad4dbfd97efb7b c3fd2e3a151b698716e5996c842415b26a72985feaf10145b439bd3cad1be4d3 acee6893c447bb4ac7fdc4b3ad45503630195a6a3b3c42017d843a600a6c4630 f816f0ad7d66eefa2c1f4dc36dd01f3519ee3816f4456a4d053f659f9de34b75 e49c66f4904edccba912f525bf1186fdcef35cfecaef8da878fb840d1c5fd711 94564019fc5abd5f0b70f3f352841b6ab9f7d81a5ce4198c0270ed502b8a94f1 493c6f93e1c3a10d10b038fd4df2dbecab7e09305311508e39027a0b71237f98 de317e163604092f8b6d6a5955dae815a6cd36e52bb43559091cc13f3639e982 3fc1b2f1fb9982c7967909ac6d53dc93265ce5278b963f6fbed2467a5bf0f217 04607b4d5ce837c13fa9207a9c0d9eadfabe22a3a818bd8713d3333150cac60a ee99ee43fc479d8114edaca045ce128b373efc4e11b03e1e17867ee6df20a02f 88ccd4d9f205054255d13b88274c0e3a471f10da837e3eb260f4d16955d03807 17b276a97697e0ecc6120675f0b3709dba51e4346bc2d48659123ba30ee91c77 62c967ba5efe260232748d98237ea61b1866374c6aabdb6215277540cc0702f9 b72607224e4026d00b0fc72325d1138f2cea845ea4d443c87c7da16d2ca6474c 405b492a9ea4a7e27843bb988a13cd73c116cd0b8e2e49c85de23367c0029e2d cc1b57560b20ba88fa80f3ae858e41eb94d022bfd7cae802af8989f4af753036 9ccd6c87c6d38d11012fe15ad533370bc11f3b58cfc83d648bc6705ac634819a b0a348591e1ed9c14a5ab74b835e94f04fafb9dbc0dc8305c474472f5ecb5e3e 833c7e37b374c5e2b55e7199e3679a5eec9b95c14dde3aec8d549578f18c406a 11aa168f8c8267127b7c2c5ddfcaa07c203c0c447b98e752d95ba74ad974d684 578e1cc7b89656762a399e26f6ac2b12f46597d81211d7f7567be033bb8cb09d 569b11dfe8a05e1274dd23f8d222ff8294eb863901d6fd0895fe802ef5bb543f d228236eab122c43f574c3550a0f4efd7942a91d4120f5c0e7f8d2641c753eab 29c4b95f755bacb357324297216bca556b7d130f476e5ba3bfd3b65c9e550de6 8af1df8e2ef9b761e1ebad8b0247f6ee1fdc87990f599c5ae9f8a406dd737b12 cc4ddc89aa086f58b60966d4990e2203d3779273f8d4c0f41e7a7a4c45a66a17 3f26a5071579ee03fa1f4e81097822f78a042bd74c3205e91c4fdaa13356d623 d424987564a2cbbdd0f6bf23201a8fa9def58acb53dfa0c06ab81210ac13ebd7 7eb1c8ff48ebfc0d7c4cd65916ae47f434d811fed7a240f75b87c4695e5d853b 205e2ec2b850f51a64f6caa94a64a2b50878fa003009f867255441ff5d1248df 1be4ead9aa7cb24c3cc4d1706be9f1a87ec7ecfb7004d605236479cca8de5132 30e9ee481a28eb727d33ffe17c7a634233f9f00dcf0b82fa0b712fdfe497c413 f60ee426db68ad21cf2177a49e833ff7eb7894f594c930f1c56e9b2213a3741c b7ebfd2aa74a2e53beb963d04609dd5359c1762f18c7e4df33c8a2f3679c1a16 220c49b6d6e83bcaf16732a3cc5ca714347d99561121e67940f417234f1ca891 31d2bd90b20c7c16a76f9f0f303d1507cf7151522f18138fb8817c6587f62e3c e7d01bcb7376b1335bb19748227adcd3f33a241ffa9d08ac9177af669f80a404 168f6e843fc160ed84bb491c703dc2d01c822acdcdf9dcfba60a814b2067839e 1758a9deab4bacbea0927cd154fad5bef771191c2d333b07dc7c7ce350f36350 eb2683a981b7e2d076840b6ecf74417b464b96a5b8375d48bbb95631c2651544 590ac358092d8bfa3ecefa694edc05a21427c046ada74fb0be5d5ac1ba4fdbcd cfe6f51f58ab2523f8d3a0a62af2557eaf4a6d4a45b055a9f0236b4956a2998c 4eec063fdfef686e319696168e9eaaa21e7487bc2dc5335cdd48fa09d1f33a60 ec02550a64ee86b4d8631be1999a525a8a02126f3b7b0d4790be4ef7ffd1711e 1c12a386f74d1d22c5c84013c9b594f74b885758404b47240642935e50c4d094 b54a7ec57245905f7a8a6888967068005c03a5c658c319bdb7f869270a5c7399 d5bbbaf433b0ac97d0edc0301117e50ad7c4357fd1bdd8eff63e2c5bce3c0bfa 60eb2411fe5ae5ca36881d03f0579e8ff39cc77daee24138f50e5d5b90b7f4af 81ec6bd568db9b2e8e179cf009c3406f8449fa22527c9c5372c7b859d4f2edf8 f3d087507fbb393f178e9acc5c380c20460437092f8144c066eaecca47363517 2a9b28b176f98d8694b892a5b46b38ed245c774945cb3cc1c3157b27632fbfb4 a1d0d8fc22b776fe21aee666b92613394f3028c948bed5b82a63038a4a9f5fdb 5995e1fc9391c6c067716dbae606a012b722a6a5ef5c56453689b363f678d902f795fc482e26a96dfaf78136c443c09f3c05e530c8f3c845fe58c116723feb05d49f8e7bb02993150f4bcba4e942202dc96cf7bada5ff57c7f832b93679e6b0dd71a94407c71d74e2dc8964d16f85d56e74a695b33cc5ba37fffbf16cd200003362fcd66bacac167d35d740c234a3126b135c7217d56a095a4c9534af8ead10860fd4a5efc801f2a287941f60e541a8cc8f2cab99c59a35c436368cedd8c0c0e242e37cc476f05da9cfb50c1ef6dcf1ecfc6eeaa415339322c84f40ec9bf5f076bdf9c8e68586add9a12e64f5fe0e8b2a7005584b0bb538486b1c748411084053043a12d7e97b6cbd01f6bf164057fac5787ebe0d8363db30e2806940283030230116a0e6fcd5d2efbb7cd42eac40d1e9d2a2b5d64fe4bb4e8b575b6315d310713c879595388f0528586cbc8a527c170f17cae77945c0fc75b31b832123cfe010027fb5b9f3ec413cc1a19dc6966f5c8285119d7f686c7cb646b9cc0d88e9b0d2765f2dff7de1d0a4532fc5afe1d542cd4fc9337426bda1fdcce413741049c0b1bb9c170bbc07951a8bc7863cc9a118eb56708c51957b2c4abc5b8256fde400f766717c2ac7711fd419f2229814128cc65ad63f3639ffc942bd5cacc3a1aed05ef0dda795dbbf3eb0fbb7102dbd57e0c85b33bcda9ad13ea8df4400a1550750d86ca99cb33a36202015f1dfecc954dca916192d892e7d94787b7eef3201faf0814f3dafe90e6b936e4efd34f3c91950358ae69c6775392b12970435fb488e60beea52bf08e1bbf2a58ba331a8fa0077185ff36e413a359068200d38b31b121005b2d0271f54e7c52d3e2ed2980998dd50f3243f47cf221414414787fa858da0654c3ddd96b7fc700f5f44329d94f37f78828d7dd5b551276d6249cb781fcea0889424c96b2ed67a1d40bd8d0e40195a369d7812d1336583ed7e93240976aa1050eae552607a4fe138b1e00cd7b30053327de497aac9be8d962156d053cb7ac0099c458bc2ac1916189d93cdb963bc10e4239ca19efeee7deff4dd8c11a725404ae946b86095d3dcad0873a9da4aa14d84bedafa350dd8b87bc1c5f0451acd0030f81d96a11ef9d5fed4a994e001e573b44d97a5b2c271b18647d1986299c610c2b0dcdb72d9871fcb56e3a5c8e108f99ba4c7e511b638ef1e70c7fa00953b101e866960ec48feb4ebeb4a13624ed00288bd0329314852e07870fd35708e6940708f14cd2fb040ea6b6acd259a91178ac426d5c3ed87faec5a2ca722d3bcf9e0364696648396e26e1d9a6b760384ead81ce399692cb2ef7e45ddbe86d20c2300105b5ededa21bb05d76837fa6cbf851a3b988dbc845326a38680170a8f2b3f3040cb91ae54152dbbdb97382b832eb2088ace757a7649aec5c92e133c9168ccb01575cd3ef5bf501fd21d777902e20a062c555ab7623844721355a1c7efdf48f061e976a8c6e7f14ffbfb08f524de282c60337a2df942dbe70d9a06964d68cbf0b3fe98aebbf5b838674f97b51b8425e414dda700eab8730cf439198dabfaeb1007efa3cdc916428378c09bcd1ef6870e883478e5dc5170313f091a43796ec7101366370512db5d57a71726242beb7b46e7f9e3a630ee37e5a4eb12ddfe55bf80eaadfc5832990b9b2dada68909b38a3f25c4aafa74a584fe9009a4dbdaeebc804a0bac8149e37615ee13a381b0697bfe00805f8bbfdbec7438fbbbbeff7332a013de1d65018e026dc45a38ddb898cd3c4b0b704579bab26517320c4e5cea85b023c4523a0629817c1afdd01ddbfcbcd7588a7efacf9635fe425ce0dfce14f2d0a85d4ed96c3a5080dedec738ed0072d980ad084cb4ce0d6a6ccb1af65dd05f706a11b277b2fff4de6bb4d995e35fb88d47cedd096564e7a2afbdd220529159a038d5af5a65fd240c306b2ea005962c74205953595593b8091d2cd07bfe4781a0fb2abf33e41412283f20038db29a1fd6bb0171c9927d8e36a9bfb53c63849800b8320f51c5c32e12374250011b0ad4b4cd878da35704fca6380fe4ea75f96da0469449a24c74127e42ce6940d639fdcf47e844e53def0491aa7d7d6637ee6d50e8e627050af7011d962d4bb8aa468039894c82dc1776ae20faf10f5a5b64bba08acd1861a1f5e832a8755cd81abd0bbde6021028615339f7a1bf784886b57ad078f642918e16e6f4bf279933a4d97606adffa7fabc05059e145c681a2ef46bd0653ed9fd520aa5aa27a0964568d4d474abc8bd45d9277407e0c1f8f0101321a04482b8ab21994c0f3ad41788cf3cae9c0c6dcfe7f18667ccfe3c0ec964217850c5cc82fad71f14a05d1a34b8014898abe828519e4d1354ac8747d5b74fda7220b71b570d58eae14c6fd6ebb677dbbc2eb2f76e38312ee3c0d3d9da7eb2785c40061f2fbb5a57be898c7083f81408762d67288cdaab6294eb524c107ac64424003d2855ef9e914142307a267a153f37324643f9ba113ceefccbfc023a0ebd0390d3aaee4456e2b5abf12b4f14dce8d0f5b524fb6d5792701a926760d565af2030ae7d7722ed6f59ac24830fe9f506d6673f073e110ed9c79f654447463beb31e05928bf2c62779f7e11375829c0f3ae073698ef68f4caad16f266f0590ea0e200ab60acd83109f0ea8cc0a20d68de33fc388b12d79537e84e845a2338de059010a2ccb0bafdb2d0edfb5eee1f3463332e9a8826a73d5f63be9c2d1350691240f0ec4bcfa4c084920bcac06c0cf672d2a1f7791ae44e4f85892991f0eb1b07db40d54edd1c1617db67c88c780883128c82cc6fb705f6e6f6d1f14c2c9399fa95e0408a7b9e910e7f2599fcffe9564927554efad3aca15f6f815988b9aab8e8fe2046b33f1add56a06c710b883b8c18f6c118512dc00254ab24c5e0b233e3a952302090b79c21bb0310e5ba6db3e55f724e5a8895d70b84bd384eee959fb293ff401e1ed12807fa83d43af592beee7114a124e25f9a9063678c61f4934a8cf39a5071413e7ea82c45ad642014bb1c9cbbce516739c312e1a9b91911054c5423eae060eee99da5d69e0fa0aaf48129cd62bdfa652d7d75c0cb8c265eb040aa974110c23aafe5699d9567e1887de59e65f59accd45df334fd5cf30f688612d6ba9a2030545c693665a9846a3a0bff230bce0de74c2c5147af7d3045f3c3b22b24d0d054056ecd8588ed6472cd2545190eb6a19ba9701bbd8ef1831c7d714094bb9ce0eafa6c8060e8bf8ae73996ae2f71d7b7118f20dfe736de474ef37d97447d6ec075030d006a2472bc8f9d12907d58f5b38dfb72f6a8090e487cbcf11e8f992e008c2a9f489657a025002f8a305024cc2476b0e3b64829229f43a90f3f15d47480519db2a535e33e09c92f9270e1268ccceb1f4a90ac8b2ea464d6bbae03274ff06580d19383f55c7ae620a989591526d423b9b8a9600826cf88fb7f2310c32dd0ec2ba7f88cf5065dd1dca7d28eec9469a1ed4f0c691d1df81db6e0d5b148d9801244fe3f586a209272a6069ab337ccdb7bf48d81790843d588c199d51e2834109fff47cd2a210f425997293444a0dc91812c73df2feca072b9e0aae8c48f05d0a05b9c07c410c8a3628b1a95d5b5439b2eec5cf230a5d71ea00431be85835f302b7b662fc09a1b39e1c3a2e2e9721ae83ed75f060c95121742e20a5527fc08a0f70c97c500f360e1ba2d370b5655b691bfbd1cf867281d8a310b7db813e2bd10aeeac811f58e973e1b20ec50400219e4575505495837c985138637af0d41db902cf021df294f918c50c8d0a677acc46bb1f325063f385ed6a827a27a2b71789052fe4a3c7e3bdf22f46808cf331ca9347b5e3eec2f223a06a65366485364b270a5404ae8e29855f840ccc61a49db8d5d9066a976710852b4eb2600b48e3b16601a85c122535166e93a991c294fd1dd79d90e7ef1bfb347703cce83c70fbeb9a093abe462d7535ddd9ea02f601a1444cabfddd9fcc74b2fe4e3bd3a70d50257d000e77ef4fa533725bfa1bc298d47fe712516fdae670f8e42f9d6115e8dca503092ce0d079c3331495a7c24b4bd942c04a27a4466ad3d30249b2db211fd22c280f5b7d6cd229e2cf325061da737fd06c7d56f8dcc3c96a2124c8557bc666d2690ef488bb1ac43ccf208613719a5e5bd82bf061cd74ee116003baea5afc94de2a0084dac597ea4ff214896a5f8779fa9c1369d992996d51db8203311ee5590d9d015a8d721f737a1971c4bda564d131f35b2c85f947f2d2504e6504e14ad78adc0b84fcd1f995493b7587fc8eaa1ae0ee93cb027f53b19b69d13e10ae3f942d530ede9457587906ae768a5e39b6cf51535f83ec68027cbe05a39fe3644542383704eb58c774a32e383a5528b1068e55b994d1482bde0e17b846f1ee69aaa370780dc5554e91f53b55161d90efbe8f985f62c01aba02c5b53d4a5ee2a8422388c702c7e0185dffac836731bd94809d7bbc0712d8857f530792a4a6e4f8df3ddfc6021d573e4da27acde45bc43a74f3b0f72608805a37f60a74b6658411c7a25d8a07e98807ef05e1cfa10539f5baced15c457972339674ea4b85ff7b4cab078bc60a1a5a138fab3e8e02c9bf53938ba8f29e200c783aebecb8fe631a254d7cf738086b14f032a5d1df7834c9a41dc473a188e8093f6f0cac010bafc80aefaf36fe005c0044b1d0bd4677ff486fd039ae9f8a6441c88aacf9fc2b7bd8b03febde0d01cc05c3da1a8a5bdbad1c4dcb1d46714cf8e31f8d9fa6936727b554132932ef0030436a72d2e78bdd746485383462b39c1091fe064a3bf55ba8ec9714c594000bacf0f0529240788751ada058c1c593b85695df49a2a65fc402cf386f32ae760391c84bdc94315ee206b522223ce45c5ccc923b19897c7cd79b6d2e629f34f40dd3fa9aa42cdfd7cb66bdf54fe9c4a91c329b28a9db3334667a7f7b8660d5bb0f6b15cee970ad85ee0eadb6f014daba40184413ee375f524ecf89c277ddc8990cd12bfe94af8766f2f55253b5a54af5f6ec2d4b10acaaa958111e02e8f5844f0a7a482374a7f2d0d3025a96cd75d11792684b2fc694bed279d7f62367a538150b1debf79b2222113487a8c221679de336a8d307f346700c8eb1c4963d66742f0ad231728a05932534188928236f22c0155e48d6103b015536186622b5463c77054e75231f158a1260035f515a004ec241ddc23a0f0c4bee5d838f2b423e81d106b006230b71d146812523310ff6896be72662dec10f9f812646a278fbbf676403a157b26c7b3769756c535280b51e71bffbfc6a235d3c0fe865283ba687594103908a2dc2190b923db2baa5911099792138a83ad69fbe33b259d286de962c6501603a76bdcbb807dbf9dab7715dc342ff38a9956e6546485313fb13c29a481e0c48f390dfca6086ee8cbb747b2f27fad71f6229fe768ec8bafa6de7dd021b460edd1fbee6b76b18651b242a374c735385cc01fe7070a0eb1c452c0884d587260568ba3ccfffeaef11d5a2a88b531611497175039aaa9bc47298893ca0fc72ad0db132663aa37d608b1b54caed3552946d6e4317d4a56fc0723a26fdc41862690786bc5454858647039ca4d318bab1a22d69169f75394f1d38ffafe5d21ebab704d30478bd17383f82aebb60c40b0842738f977e92f73add472e36cc370296a90af9e602ce5f049501667a4ab2bdf8fd1d65a0b111051b3c3f7f190aa9c38b6100fe8ed80422c4d445492f2ad2a6bcd3f4016eecd7d56b9dd7f8b6506cb0cdea00ef01a3b12b3cbb2427c25320e69e2e332f72c2697c518f52ae3742a1406b720e6b0450c8329378b0c9e7c0c532a68ec0fc224aeccf806898deba65f68e212e0419a8aed4ededd2a212812749866d014f9846f195a80213f0b1dd2698e96a3d022d6f2a3a344cd16b54cd11b6bf67725ad3871654e55f864a5ac27d21b7567e03f322e7dddf49c807d2bc746aa0841b8c4b8d735c72fa1b6da369504442a7790cf72359e9449f5e0d133de37ac9c4611648efac96c8a3455b15142c1c4b63370b369513526a870f394b37f204a196870e340d114a8b5a693796f49a8cce8d8e0475934c55138e0743096e10232bd14220017efb10b02fcc9054d135d29fa07108a000bb3239408bdc9ec29317d249f05d71df60cbc4a60c4373f2f62c5fd61701f87d88097b6d17f1ccb31e8befeb46b0559f1e9f893e49632c5eddeda841b605a89f7ace92a0eb53546471799fd47a21650e1b438c18c1b45526a746dfae2301eb4f71171f86f3c42c19ad1220a85c18d795f195a200df4718a7fcce781c1703f6b8f42815fd3ef0aa19e7c76bc5273dce79e7a3b19777a63c609c5b9c1cab0a1d2b4a26d9065979270ae9894b8d35850bb80820e5bb9b9e3913d06964b0c209551d16d73b55efee84f4dad661f20fd0e1bdda37e90af6ebc7a924617993bd05b8b666d78289779533c20e00b70a1fbccd023399334332c2ac7fb3438167da0eb95b4bbc8d8fc7e2eccbb2143091703bc36271f5a4990bc4cebf9c19bcfe230287354ca99dc2d2914f0d16613c7f7d79575ee4b600090e7ad1bdd3e4f6b68802a5e4a7e5b5b9fa4f91fbf6e83c0238f0c7313bbf88ab81253ee021d9b03cb302f8ccb7223ca9d4fa971050a3dc56291c50e4d9ea19ff3dd961ee7072c498aa0c48499a19ac088e0c43576695be424e406a06fd770597bc3191a25603801de102d5adfe317d50338ef68ce4a02d708ec1cf52062322b4c7c0ad89a314fce42f0788e9ac051f6224b5cc7f72bb242deab63a38bddf1d331346a147e09e374d40068fa52b9a217ffd444fd88133e1cdd464712d1d4c3170616ea4156c2f0b74500bdcff45e842fbe3b612e1d7483d2a6bd5aed16a67e4e95f8a9aebc5e2ce33b90a2d1104c500cc8bc1b0509846921a57e084ee3587bceb1bf1c843468661cc6f0cb0b7e82071f6a98a4ec541db2f269d605902ce55afbe807f617d3ce472e7640602c73e9411117231d253fd4f281bb97da1670101c526fb9cc59f5b61881e5c02879962f33f873dba7abb720fe944774d378f70c2f0b583f61794a38195e3e1095116b433d5d7a0ab1bd8f4a916053b18ce0688915f46cf85e4a39fdd5d023f0bb1d28fd744a7fdd09b70e19f6fd3701a641098fe0c9da55fe26a6c9d12bd3400e3440c0d85419dae6e4900bd3034d07cb15d80302abf5bb357f8a29c93c53108 false +check_ring_signature 893401f381a2a92c8811912dc9c70464d91fa69fba0c252a2285426769362a0f 594600bb59fd049dd7147b2a06668d113bfcbdfdc29d7a51cb070250e1419b36 33 9c1255fa9f2c5150e43204a7e3c7966834b02f6edcc81a928173685f6fcaa1fa 077d24e33ac5e6940561ae530f8d28dcd626dc994cb336cf6b60c5c64b838e6e 3c044af47453ce611d4bcaba887515976031a7917ee476bd00a3252fae5ae50d e886780f4dcceff072d2ccf083d09e5775699f4da091caa839ef01a23c6bf0f7 fca6ab416ee873a65b5b7c8761fcef9a9b0d024766b65cd7bef75ccf1cdb6678 715288ed60f3b920b939d2eb419e145692b67fce10a61c5886a0276b9e722fd5 d8efd2f7ad5340ec0ab6e0c21e6969dd0df83f575ec42f65ccc86c4785612513 1094e156577939c224c31813ce0f19bdc26f410560ba37a06e5984e07e3ba77c 8efeb4c701aab03f5aaf7ea0ce2da97019d7c63e75b883fbaa7275ebbb8a1468 7ca51889b9a8a971eb835c6e11bbdc9831b8ff8d30444381b80dc037e2107ef6 cef5d5f07e41f46bd954c83008ecc6b7d32574940cd6df312c36baf04f558db6 dabd7122a438ee8f8c47276809045f36a99b823895d6faeaf4b88849034d955b 9e8b0b82fa6daf3adc0e259bb410b62f798cdc16db33043614192faf2952e012 5267f53b87a5c8338ff2c9989035a6553cf07072a50a94b2dc36243ed02a58a1 d6ae74f033a76c46d4d40555b211f552a21935362cdf88c0e985f429e19b3353 afe6218c2d39c5f617f0850026df86bf42b767fc06bc1b8e9c54721e4bac8642 68e7156c2eb67b8bc2c1c1e034b86a3fb6014f5bf97450bfc1f57ccec46dd7ba 64ceb3763addab10aa178bb1ab4a7156507ebce0fcb33aaf541635d1f90a3771 23cd122ff7c8ea5e385a2e0f1bff9ac29d897f2145281be95d709d855e4c35a0 109aba675d35561c4220b3a96b8ea36f85b2249ba54ae054b8835cd37eb71cd5 9febd8d5d61fa21718f082687df24be8263e3ca8a2299d2e085928c40d0f1bf1 da391a2c032e484cd7bc40369540742f38b482af3fe4e4bbc946e2bdfea13bd9 7855529f98068aae5e276f38a1029c3546664ca1697829d996322ff73859afc0 5ffc72fff7dae999d506f9732f6b9408a91284dd740c6f0dbcde0edf9b1c87a9 5c00687fe5230c9fd3be3d54dd1f031f1edc8d95bd7eda75030ab57703c52226 af37aca2066a1b5d6a075b4e96fddf2d02e3dc5a3efd0a0ec347697bb208a9c9 22b6a873b8896f7218fb79e21dedcd9ab6890650d37dcaba98eb3710ae973741 5f42c3c87415169d770aa54d06f6f82393324709a288db3dcd728e7f0749debd 52240304c74343a8aab4dac93b3212d45931edea24569a030959c20b108df090 aed87e7076efe855dd9b8dca9b1f8f9d8668705b1ae835ee6d6c20ec735de9ef 11bf60cdad47a1b62aefeb093300b0d7bf6ac2575b601daf0d8b6964ee7526be 3db7bd6f18ebf48f69740177d1d73a31c993b55a75ecaa79b7f49309ebbbf690 55fa8322eaffc59a7bc319765debd3f1cd35e73342e8480387cb7466415e22c8 af0762b6c5faea5c439eb16ddf08054e10973d67d358e040fa80989145eac7087c425ea5fb953f97108216faf202f660e4c9f36a0c181f75e78c2b033c4e5e0dc27e59804ddb0eca350c0d2d1687bc7849a18dd66d15b1ff881fff8354f63202deadd869124574f6dc69f2892065fee2dce7fb733e39525c99d1f3daf0250f07ce6c5ab8aec544c70b76aebb6273dc79f266b19f28bce73e99f652b2a4437d0a11898696850371fad2cbf53b724c1015e9c469da8853a8fe95f2cbec91a3b103368a67863f42873b76903ce26a7be4a9e877498748e67399a6b9abf489f8ea02adeead1f9f987b548cbfd7405d89ffd4796e099c3c87d995c2f8ccdc3f0ee30d0283b5cc30b1627b95ddd64e7fd3631ab036e4a2c8545c8aaad1d4cabd050305de1ab7bcaa5365af3498a0266458d1d5f9e0ef80d13b8b119121d89b2e85b50afa70ac55162eceb3f1390075c8a558dacb06600ccca3db33431ae2c6e325d009148ed265f089121baa993858de09be42db0276b435c54bc90572cf38f1a3110cc56d75408ff54bce3b1e5f4026e3c191714c64bb1159a84a839f5143b7297f03ed75c05b206ae0a42f7ccb869be1a805353339b0c699e1fb844a4f1c046ade04ed133f8e206f504b0534e1bf10be5d731f78ae2cd27b48a799fc3b1fd6e4df0a70adfcc138cdf40e00e035285cc9e9a3da9495d622a7c6e46f3339585634220af39021b1df78a1cfd1a7437ae2a8abae9639ca2ec43dde17c468a96480a912068931609ff865ba60bf7ff9b9151efb12bb8417659609a5d198fffa352285e00947ac82b9b569187f77a4a7fd5ece18492524b03cea99d4cf5776999c90debd0df2fdaa18de938f7b8c3755d64f7e36e8e0f1535a06ec2e8834942e87a9546b0d52a87328d18f1479ae12ec69281bef506c4c0237127befbf6f06fcc81beb460ed05925b2bfc4ce86ba67c51bc3a55a0c3941f73d30da07d4f6d831286e941204e50475ee69fbf5f636a81d4f8e940e35c29e77dfc4005c7d6f058d82940132083119e4906bd214c25b58a6e1bd04dbea3c3af4e252b91414383bdeba6b279207184452646c4ae43785d453173eaa312adb348f9cefb1ccc371910d511513c70f74fae30cf186aa401493194c50a32150b6e4d93257bb4c225c45ce830cc02a00943dfb88d80486aedf7178de40bfae759349e90f657114a46f1f7f8bf471cd084868cf6235c573d71a490ae6da639cfb195648675ff04e185a385a88fc016304f328bc5abad188dc1d5cfd8f87d2a133434dec61cc95547ce734bf2c19c66f084281dd2a47acd1d04f0eb087a983c120c3a7c93b08aff94c41728b2170bdcb033e6fac6a838c8518d362a204cc8070d6a4816d981ffd0e6e1096e6eebf31c60432e047c53abfe1a755c90420d69919693cdefb2c452eecd0ed5cba5a72a880072ef54d1a13529febafee2bdb7c29de678bdc8f1686c66b5b8c384c3b6026350d082f6596c4b1a32948f1a2267ce9ccb484066669e1b035ab3b75fb470da649005e780843a334f8c0b11d387ba6a2f0e8ac899301aafb150be6cae6868814550b4d9acdc3fa891402a16906e54745b6ed8586692c1da528a1033b4658b04f7100980800536e8b37ed47964c5237bd9bc5042b64aafc14988e32452d245a989308ea0463a5c80839d67f982709227866ecf621d764cc8ab78645eb1e94c362940d75b13489fa87fb95b9daf361f0f2bcff80dce2ec2a0a0d38c781c7106d20e3036f52901712a618dcff7685b15436d48bcfcd9190f3a177bcd0063e661a3461060b891ece8be80e42015ffd3a6b759a9e8d364814a87e3977c053f37bb5a8ef0ffd62c0cf270b785501b893b1e5c0fb8014d77b48205111aa8d6cef47d175430b84a410a68eeb6bab9d50c02cd0947d066ba733bfb47ffd3dbd3a6f5c0535e70f58293978d2ef4808a774f205e264853c329c0863cd3b1be564e482e396739100b97588247060571ed00dd714dca4e5d89a52eb4041cda33d37766d3fce8ed3039e5d87fe0ed9257ed1fb97d2c9b237f2f38ceb25e9a015df79bd802fb021770491158b23a3769ff012adbb4a0a4b170fc31e89f8fccc7161d8f5ef39e7796309a0066541ea410b1fb851fa52185eb0d5b5eae18b2524df7e3914a031d4edb101d2a964f75f0154e67e526e325a1f28457b61a832df707cddd32ddbe7ae851b0ba166b2b7e20ff26ca226f9efc6d17744a32533063e6a4fd1edf996f161898a0bdc63d3b31ff6e15cbf26d6c5c8598828fea680f48add142cf8ed55bf7d0b2b00d892a1fc72eeb044227c2e1251430dc84c6638ff43782e0212467baba4786201f9f6fca542c483bf066110992800669a706f653f19919880e570717b46273f0c3517de773f99ddf14a47071d5ba5e44f5bff0cda5c0f603af8874cfbd9919d0df23576c45f658564b63031aaa73f91d74b7494defc123d6c0ffa5e47cc06290a3cdc07d7a29baa152faf3c4d332bf5afca116ec52e90141cb23d1d762b4ba60b9822f5ad41c6cff529ebef210aead46a95dd14a19999a438456f3b2e59f0b8002521e8bd14f71f07f713761c43030ef176498e15aa32ae95a77bb702461fd006a361c80b95ba7ce586ec43775c9b9f8516454ea0d32f8a4576c5f897f3624c0cf93dbe045c37eec0cc9dfef99c1f6c1fc5be0b8034700fc8bfa195adc660960935258609f849a52127d5d4bfd39b93bcba216037e859f2a16e2b2ca30c9e1706acda9def4b5f7591e3fa789aba584edd932279163a1d40b92bf3aa5a4c7ba606df63c15c4f2b47f34a6e8f38f0a032d75e3c566c21d467603035a9b88605690a0427b43509a02ba8d36171aabf2f327843c2392a316cf56c6bbf376f091fc70715cb47497daaf7abd30bf7014a950240d70f4631512e5bd16f929ae4b56bae05232d0dbabd6f92b045a90d9b70d7ff15ee7b246840127448971f5a2ce177320e false +check_ring_signature 22daacbf04e9b17df8422115b6c0e6d504ae70e7235270df2b1d0a29269e56b3 4ee4ba70a0a0f329f3b0123e92da0075af6f736515607bbab538ff9312f9d839 3 aa5fa4b57ddf5edacca3f0b4321f84c57c3c7334d308e2e21392b751c4e6753a f9fc3b2f7e0770ce370be557ce86ac857cc485ffd8ac60d022adf92ac654d075 6746bfe3be3e06cadf27d3a2bed44f565d5a066d47b42600e1e2f1ae491bba88 7ef34eaa322ec5cb9ed529a7e86b73a1da1774fb2463dc15dea0bf7eb173c40b44ad1d0068a3be34cf055c1f5ec96e0a32ceb382514f2c1842ae71a274cacf0ccd9b202367ef87f644d92fc046d5d808cade6e5ef8848dd1fa1dbdf110387c039b1ed651577bdfb35816b496cf35ce44208abf47d21a11deb923e29a6929220c5b3b92fcdb66f3bd7da6f5fdd9c6b0db7a9790fba5a588e1d4e9a8d4999a4d039c49fc6c6982a88b5dd31cff01d0b4426fb95abff805809abcd816fb55e73367 false +check_ring_signature b5b797bfbe7c90a9ccb032b7255b6bc36bbe87058fb3eb8e4dc3714cb020b670 f8102b7b8611c4a31c176e8ab6e26610bca34a2a62e23373a3d4fa9f1f0d5bed 119 b32fa01d8145540b04241964aa53cd2c53143f5e5e75d0fa5d77a6ce13e10078 bf195a6959a2b71f4108fbc0fd057f74ae19eda9d047a7e9f7f252e734c203e2 76a546f83874456c63b6c1b95e213e9ff89c428c032a3647de7046302a26a987 747b0ccbe4de6cd77630c3ac77fb2e06aa846a8ad6b3c73de27fdf6c991d956d 6cf0dac774077879889d2707ac551441e262188a6189ec5923b201dd81f4732c 12309ded6cbdc43285fe66cb8080b94343e9a02b9c8e6b3aac72af0db05e5984 2ca7a59f2230ef089708c40b36a986f211217a3cbafefde8bd1c59daf2e08eca 10cdfcb367118a0dcc7b4319bf362082095282146afb79f3cade37fa46f6e6b6 c3fa288f671fcb727c82075c8efb20b21d32f19115f03727afaed8faf155a972 90d1f1eccf46e7dfa2586f79f03e65484fd94b405f2e5d70fe1b996753c304a9 3152230e00c51427cdcd236bd3fdee07139c0314d1d19b307968dbecec9455e4 fe81046fea47903f8e00af4abde778121d0f4d4cca01c161b010d0a94941ff2a 18282c1a33ecfcb9d01ea5323c5277fbf10017d8cf7c8f8635ca2294efde2dbe 29a8b759aea07c43e8e42d13ff7ca5f905a936ccf30c9477070c00634b037be2 008c10ff6b26825254fb0d3fde151717f32e212158cde089cc644c5fb65c9fd7 4eb451e03f33f16b8262dfa61a572206607b6175dc95505918b9ee20c8f05e1a 6f89f003e8b791ba23c8d373f10470cddfb7f87e9398bdb28306f3d846f81b29 a05592f62ced6e558f94dda6ee84b2966d64e969292382014a40a7590af3fa7e 6b4ec50cbd16e3d80439b3f5ea0301259ee667e442cf310bf8e0b42deba53203 19f12b2ffa4f93a01fdcad0bfac8eb05e23858b195bdc9bdf0953d70e33bfe1d ca31eec997bc6b366ed4fa33cbfa15dcc437a1c963d0173354fd4d18e2db0f8c 2756aa95a02a3bbdd244be82a54c2f5df310336cdc1a3bd0105dad73c76c8b93 b5617608b2daca154b0a96e2e07ea208a913e8124b9642b5fa2f5cc0ecb6ee5d 49ac1693e50c6a1ae6e43a1b8bd0f16c649df9b43ad3df0a8f8f2624def31a83 d149e39c2cb2dca5875a5069498bb2935a8384018aa104c2a9045a5b81928aec 4802a1a949d417a77cab9f535b917cc20914259a5b2c26fd30e8f6d867f1cf26 65d8fe57d3f493abfb081b605248b2f08d6382e0bb7482024be03411e7303429 ab019755439d31c9db90ea2bead70323e183ee330968ddf531a88cc84a236580 3be763941c403153889a44977070500eb0fb7ea5c0b1715a50c8236b74135307 1ce63ee12ef3fdd72c4ba853d27277233ba2a1d54bf8c5ec6b4d9f0a56bc2a99 1deb337036fa524d4ccd82346f8473c9a0dd1f978a072b35060c2d91f4faa5c9 7b2e5e938aebec10dceb12b0ba7809f79da1b3bab2ebc21b98fa0ba597931f08 95cbb5c5dceeb85cbaf231d2b2542475ee8cb1054ad68a6248b3c96542afa7d9 aea12f1ef6edd9d9dc8f70062410bfd6605b675b115583d4ea87c2cc9b7d5acc 9f0e2f0c764426477df643332b943ae8949c3555b1fa13122226f22c2895f23d 00e84a0dc59569adc26eb7f494117cff7e13ec72db12a348978f402c5a93d1a5 1c31f0b39cf5220ae438dabfbdc7eb221bafae4435b3bab0e581486c1fe437b1 7ffd18d2df371dcfa52fecd9c2782b75899b2487f7206e0e7865a7364ac3f08a f3ea1e8ccac608b4b2a16976d0a8f3cf88f81858405e4358fcdcdb1193fcd02c 7fadfd4ceb3e07a617ff2b2528265bab9c8c7b4993d8b817edd2963112b39175 2c1a47af7810588e3dec50d730dfd834c7eba6b017874f2f7c11353f6e673297 fe3aa178c1219452c077da59d3cd160dc4505c6d069821c27d0207acaf8ec1d4 acb799900fd506ffe50b89104850838b618ca0046bbf0461066e5328a13b8ebe a71dbbf90b9cfb4dd8f384018cfd702781b4d90f6776370176e8952ddf62c613 de587c22185d4c3fb183e0dc281715869ca2bb2abec85bc410935a2ef01d69cb 4a72a8de220fe9450d82c5c4700933b7ea0600dd659a66f9a90354ea8904f6f1 7e61a20ad6525804dc3dd0eaadcf78aa1af1473813a7be767ecdf0de718aab7d dd3f1916cd32aa24c69837400c2da0a552923cb5aaa41df14909a40e7e7584a2 0c959ec3961ba012562c9f22589fd24186fc27e0bf4e63e7cdbdaaa54217fcf3 a07c29e0c7a95a54023a0fc01d14d1afe3c71435bd615953dfb239cfd72a4552 002124a0b0a009f0c5dfee922b02253b908ae3f3de7f21626f0c8fe07a1cde4b 4715c8fb9e93c08e2e3bd2adf4a1ba8b0021f82eb55e11d2b3b4d6a6921ddf5e bb4918eef220776f01ea1c11612b7d3cbc471e31886361a0fd92dc7ef6901133 6d9c842c8896568acaee2cf009c2f9935fda652acc29f172b30444f1e16fc47a d53183d8c736ed55666a4d9924eb766ee454b48740afeed733591b8715030320 05426b18a20b3d6cd8e2bef2c50b4077606a8f13544af49bee01503ef33c0250 381b103150a7db786b6beb70991f58587ac03567508b5efa4c900acb16259899 bb6e73cb683e1ea9ecf591b60c2450cb06f5693511b83dab4c53d7ba321512df 64ac6cdf5d45968276d3d1204f80f4a93cb662af39fce068d9a9caa633011829 769c72f403a93b3461f9db0aa3cc82cbdcb42d99c2d70b8391eff877db50de98 97d1cd1306802e4575a449124cf15580e56b7b72e9b8072d5ed675e4dbf088a4 6f3f301fc9d89826e71dab286e5d64c5e7f4d30866c2b5a0cfdf8b33a046cf49 b4ff76fd38188995954a60d9e42df599f981f51a9f34382f69c6cd26a7bebe8e ae35d45fcd71237ca6bec3def34c4af716f87c1a7b81633bb0253ecfac073c34 71c6e7147050b98e8acb2b7c162793529b64f5eae00034e6a9d4271ae548ea6a d0c67363258975e99e676a4109c88744a9204588476f862c5920f258df7338d0 b66c1078a0b8fb0f3fbe82c28169889e4e5d655f2b103a838cc8867acd452fa8 04718ec4b7911831f606585024cb664333350b0adeeae0f24793bfac5360554e 0814e49a94d50fb4ab0a6cd8634c078ce08498621f476b3f5c3ab65be59024d9 6ddf0a6587af0adcb56b682352296b5c5da29787aaf2cb6c3c866db3df2f4126 bd3be488ecbeabc207dbcabcc16956c0cfc493674ed8967baa7b7d398b1d8fa4 2565d0a9810ae6ab25e01de39b5890b95af87c99be6aa2a6c314b574a6b5d055 699fd7aacb0a506ec70970bda23d4b858efd0c63aa6afd22851a0596df142ff2 492e6484a1e8b2b3bff4b2f880addf31ec82abc2f32f353145bd6299a08d8ce8 17f4e0e8344313d601814d6e78bde42b8c48dfa868ad948279869c39691d1583 b40bcb0090c58ef14e465b489dfe2cfef01a8191a6199a8c5195831fe133a2de c6a8b43bf13fc341dd93e7e3e0b495c733558470cfbf2954a240dd66705fd202 a1f43e99103c9b12216397c56937cb4d85d87b27cba4a104fa49c0d6179b2dd0 4ff7d632e3a37ec89dd9f40cb68c3a392e9fda05bacd9b5f63d0b70229be6d2b 685bb33933401f58c7ac0469ce0b6798194ca4e373dccb626b00cb3a12b3a880 b66a8357c83365270b7a69a84d29ad4263970c50c01186998c6a9d58403493cc 4f84c9a4b7e52a6b1acbd9f4b2bd1a202e966a07b03d6f88429e3319ef79158b 65d8f497a7437f3f5018e5bc3b3ce90ea9097f6f57054c50dd2e054a2552f700 eb50358d224680ae1bff5e7be4f61ac243137baafecf526f63af6b21c5479492 063472c7a0160f8da96a45b8d91edbd2b19b9f41549a323d72bac70d7512a88e 774394cd6ab95fc60244a4127c16344d0f82fb53a02da8cec15209fa917540a8 62c1deda73fa522195bf823aac2245afd11ac51c02fc84efccce2e98b1fcbe71 9a79c95d43a58e86ee9dd83ea6a9c0e12316b2cf552771bb24cfad7dd8b9e1e1 27bcd7d27e20b7d84b8fe6aeb3150420b5c9f64e806076ae5199d18624b7222e 8ec67eaecf5db91028f2b36367475c2dc4cedbdfe7307397360e800ea01eb83a 46edfe3038afe1b1584796df0fd95fa645e5e0391750e16b252a7c5bca6bc291 e0becb809795f6247804316817b95fef4d1812534f0eb20e653e150028aa6f3c 32094552de9294345ded2b41423c7ce6b1e0eb2b4d4947775ede206b3e133017 c1858059afe323d73b4d785851b9e1a677b50ed720524a336266467a19f04c3b d33e395a54ebc87a88056da5a8b751c2bfa23f5124e9f390df37b72de7acbc30 b5138172d5ad46bc57a6b2c2020d7280a5ca3c9430e11dccd41782f1452b4b34 fcc018bf2f86424ddb036e3e278c909bf1192bc853a0160e49e5685194edd78d c7fd2f97fd0e1a6917a2938432ed66b425bec3f7b1c0e4f5c149644625894421 92cf7ac8f37466c30c4c52f8a0d5912ee9a384ee8cbef6dae99299f0f3f89c66 35bd468ea8a613a378d3475a78a51a6e4253b45bed4652ccc0d500ec95e558ef 5285fdd2497ad4810bbe8a695a228e9f13bb0953b7d455318dc8ac372f6f0e70 07bd111c81e6627fa6c3f76d0b6b9a3bd94341545a01205a888f2146974fdcf5 b89756d200fa4c65986f682e9d215fa1ff532598f17bb17f3d387a4f5867eae9 0d3c4954845eff8f1c8f723e79f459a1e1078dec67a435928c50ce3d8f90ed5f 9814f936eb36a9e420c033b5a3abe74434afc62a92f78002693159364f1a78b2 a02407caafe8c770042043566241a903852f72afc8e648f304b13ea1184a1233 a096dafdd59d3145fd4b340852ca52c077efa4030b489fa8841cf39e688b5a70 1f57b6bcb868a6e162a0228fcdb0286c305746425aed312764754244fabaa8df 10fd4a4bb079812211152c6f2f981c9cb678bc07c7aaed64e306879a97b2829f f4718745d9202cda41fcaf14c399e44c24b4450a4ee4b149d43bcfead204c446 2e638c3d923a0e9d32a7380687bfdf803cd72f39291335b67d2b67fb130c5d15 bee4982ca1f6954f9c0c9491e3a78852d23c4ac88312e740e724fa3b4c431abc 34b6651fdff863acf35408eb70a60aa400ac3e71c1df14eee252f316f45e9bf2 312cc61fea7bc74a95f3d7fb059c2a55a5989cdf66c48c6e3b7a0059930ba086 5d04d02dc3835ac075e835cc39e8b6087fd711ceba49b617c42d25d53dbed38b c3485ba4774f60bdf19e333ec7d3d2d3e8742441a34cc4607053236ba90dcffa 310746408f1b99339ac54744bb3cf4ca7d951f1fec573fd13ba199c503cc7148 a1d74f83b467265a4a6fd40497a32a24e9f298d2107c9806dc8a592881119757 c6997aedf3a012d5141be4810afbb244f35213b60ab17cdc5ca95bc42e100204 20eee7a5a75ab92bf2687dfe4370bc5d043fd9669c0bbf440a0c21e77b98800c0a2c2238547d66ddc6365c7d804deb9b8abc24bdd589dd562671cf1ddf4c680779ac3ddaea8a53f14c758d35317e910d5cb41cbc1af838211c08dcb6d12ebf09accc3bfa6cc6278e3172c59081c3008233b82f2bdfd46514ed9854780aa3030b6ecb2766251c683ea92fd52e8dad9f4211252b699eb8eef483216418474a550e86080ba98aae3adfb2a506a94077dd9e64751d58445595f15a7db2a610d28a0762fe26f49a88817394a2ac24f62f4705863a2c01e8e5ed3d37cc3c1e328cfa0340822e124091dc3fbb7d50e206264eb33065a305bee99387eb609e53db5b130a17cbed7b456eedd7fb91dd5be37c20f4aafa91a02850aa29ed8098fa3da8890df08286bb7dbdc00ad8b8863d430e2544c556f564dfaed31413c321a13a90600e2f5d78621731650629b2675461cc964a2e15f62705d524dc1542365dc412fc06bf6870cae34240edc3afb5854eb68db77adc0cb7ab3686178460bd42adf2f50b3459546fa78a494bd2bb5f225b4e47d88e73b5f7236156db1a86d21d6cbaaf06ad21b1a3d6f7ffbdef0306715763fe152e0c9edc07f2b7ef5c727ba2039644069358af3741374b250d3a36c1c255596305b86ab06494923f6c27e3395851d0046434a9fbb2975105adab3d9840d35e2e1560a62524d99d6175ef6437349bb30b61884526adb60498277c329270e21be29d6e3e3eb157baa22e8937ea4ff2930107d3db800296a1ac67f220af9e1f3bf88d9d92b070420df3ac45714217e23b0a32b4a6ea581430ce4b7dc18920c960c9efa9fd95f0dbbe1b108d1c6a8627e804958fd7f513ebd13337443c04bea936b97781ea5bc489ceac83adf65718b2d0019bf12e26b1cb2f6b4e37675909bcbebf25b43841f74a7e4739658066ab37e002e31af7a0c0df6f92e2f4ce162e259cf1e702d15ab405f21317df62cab2568f03e3c8ed3b44b515c260e36fa5a7607f1820537693c2e2097d3123ce3437316407a0f27ae9a3c0a04ecf30a90a15b706b2cab38c4bf35c04608769829139c7f007e702a2861c526a72c17b74a99a645efbe72c90a22c336fe7148f7f90f481fe0ac627f9d4e660cf2038dd11c3a869928e16a3c19b40ee41345b540ac2d803390ac5099190e9fd1e4b145439473bbf5098c9f4fb36852f671c667994a553b20f0f2639e864ffcef0f891314a5708365c98b7ee0dbb6f7483e06fc3b414cd7449072ce7054df3541d51d591a137767ec482677382afefb4e6255d4b800a9b0b3c0255e034e317d8f3cae6fc4b651d32b175e7c2eb08701f4a870404e97c2571df07e1db390723d1598ad0e252335ba9977a684097b9343a9a3d78bb607b53785501a1636c34a1f4807369a8f25559a30b38c82bd1bec4fcef231a931e8ef9d551011aa57f7fdfc631eeaeb28761cd3cc3c8e96b71fc4ad889740acf829275578800ea6ece3bf4dd47e9faefb34a1240b4b92543c6c0a47ebd2af5f61e5ca8d4e300c7efe7338c15ad32f76d1aed124559631cfe692e742f7bbb3d9504ccae7b8b00ee3a54d5e6a6ea1f2cd28549de83592aca4da679dfba2a63ca4e8390e4b3010d4a9c65e4e0300a9d78f5ab19964c70ec91085cfe4388594819ada2197ab54e08ac77be361d2a91e67a10fb05f553f7904b2d48b6745886ec45b2de13775acf028464e6977a9837f7e38b86fae756970339622ae11c324c5420fc0d2cf4e0f9094ac4c7b08f2d51536a6a804acaef953e6e8286bc080bbe0b4b47487279210801e610c87abff32503d2aaa950e3dfed43fc14b93d9107691902fb4c69419a0b09e912df924d200da2aa04430d3b87d1095910e2d3de47c3c07bd76d03c194ca0a3f37fe3b2814d4e74c0bc086c41939105db323297c83c8a75666fc693eb2520aecdab5327b0cc7d6b509333b374c97cc77858b234ab348359672dc4ad4e2ed0cf51780b2461d63397c6fa287ed547a6c9a217bd0f10425058b3be0d8bd7d60091ae504be6dc35cfaecd4dd5fddee20db3b5a070a1acac4fa9b05190dd8f99b040f7ea84ea8b535219ad3fe76315f4f1a5ae39d69771bab92e9afe0424e84e209129297d2346e46c7802a64a91c7bc03d266ac6a38582f76a42230ef6cabc8b05d2cdac0b87cb61fdcdd07c0e7d259434a6a3bdfb110d3b6327cce79d6466170878db10d005dd5fcce85d206acf9fd06e8ac0217e809efb7b0fe96741c127e1008c9a7647b202e58c4f869004161ca4635033d25cf5c0c1fcc9dfce7e801eec044b32906f3f597eda9e98a17003efedce99f2ef7490a7d1b4e97a786e65cf9f03d9a921b1c8a0005cc0eff377cc42a2df802329bf045cc99698d4f9407b8c2b09d216ecac659268abfe640b8a17b19ff06b6927e0d9f11879f7bab2ed438ba608526cad0feae634522b5bab5b716f7d96bd6c33585c0ecf1141d80b36f39aaf075ab58b32508457d9d361df42cca298a1123be4ea3e179699e1829fdde855a90ad3b260b5926158b33c659eab4ca230ab6e3765188a695c64bdc568f6934faa0438a6343861813c57174f3bc5825638669c0c3311134110f794cf87deb0a6fb0736a4a69848fc32841905fbd883b05dc246c053039cef764b6d0297677e90be021ab3585e071e777ea3ac9c208376150943ab616c82dc4a14ddcbb8167ad60d0044f14433a35edd1aa07c5a3246f10302a7cee7f934a6b24b049cdf45e108e400c5bbcff21c1622dc9bcc017e2de58cfa32f2f344c9e788dcdf665c47600f8200df3b8b7365d504d966eedb892dd3baeb186b1779c78e961042e1b5d59431140e9451dea159393130eeace36a93417c6e554aabb2e8f53b5362d502dfc2ae700232243c3dbdeef0e59a13da7907e58094b90803492085726652fa89b7ad2f530e06ef47f8ac1afa030f5d7e2a9b9d60e54e3c977932fd3f7628ac579567879a0305b740802004fc0961dcc25335e7d7996b4f774d6680947bc219dbfe701e5c0dadd570e2de1eda4eeb41295c994e933f1cbfa39d8f5634949dd7644d5dbb3e04d37c7b1125e7d7144293835aa585734b652d51030633efdaa05052d1e20a8203efe44383dae5185963c0280bf272346816ca367899b3dd4b73eb156699c0220b6400dee1307d804c5bc2ca915ac506129e95b6097f84f541a1b414fff12426013620c95d9e83b648c152819cea547308d72c259bf85272ba73a779477af31803eae01293a489c03e300eb055b5f2be323e7535f72e19605fc9ae2d2880aca40ecc03ab1e57f328f526502d11e686c21b0d9793bc23b00152469d58972923540889ccb7befb1d10a6da0d0f3afcd93545b51ab5f5fa7b80c58e79e31be1c35f02c7a5f372f5a67b98b8094bdabb976210de9e4bc36e109062d0b68433077eec0a01e4dc5d2cb8f75f8df0d11b6b8a71a7b12b968267acca9a1d0830cb1ba8a3018e9f58c84fdef9dd93ed15a5c59d766a9f93627d1ae0c9469025e2514ea917035d1f0918425f26a5035d0cb86d7fee1b27b9d8224f1932566e2eb711c35b1600a68014a523513dd27aac7c3146cb93dbdb31bdc885d36c55b6b5d56dd004660a95aab3e9f1a11cabf6dd6ef8a6896d11b26ae3250bff1b3464d440b231d2d10dbbad2459c27de8dbbafd3d4f3c00121cf629596ad889e1e13611c3b77d58260108813dd095170af94e8ac8aa1fa200f8c6fbffadd882b50786f3c4f7c33b5a065a2e369591e1278c59d6f47f25eb0e98713d48bea868c88676e3f0ca77e9820609cb4565fe42696c67a9c2b96ffbc9146dce93afa9a726d604ad44bd774b560bc2a78044ebdd80afd0e6ebc411031505a7d40e7713c5cb482a1b33c0f1849001719a4761badaff31cdd32c795a7c7a0e8d7124a5ceddaa954b1206b012ce4f0275e449fa0450164c0e42ec021d3ac199e7488e779c43994868b73a9795d2300d81d6693cbe10ff2d4482a723efb3b05b47166454e7aadfa01eb9fad95748f90e2a850acacfdac987c414e129de984e6e7485be3baa7a11cc0067299b1b97d10f1c2c7e3855c8db43f4eb2b011374a22d5effb040ef20d77fcabde46620d5e60e429360d71bc215bf1fd0c5792080b374ff5be3d6ae6044ad03fb320b55327708d84b8984ec5fbe750674f4b75097d05cbd3f3d3488153d1d5b87c250a91e96026de457926d1b3e19f72e2ed5f6f3a73d15ef3640263336a68404257c66db850691f4761716fda0daede8f12ea1396a9eb74cd120d379be97efcbf7f05841f70d52d7bc0381ee5ec4118d2cedf6b8ab020ba25753b44f4e8938a7e58ee79cad0da4bae0981aff69f68af42f2a837ef28611c5ca2dea42f384541eba1500c0d80e1b80e5f03c07d0c1caab842cb7ad9d576e2875e54dee8a58c382377f2d39f60c8f3f063785aaa466bf5f2cf0bc55fc757f2fd4a20855ea5904219a798e366e03b985f95399eb205288d55864a9ca9ce583cdf495f78be89c68b144d555db9a091e38aa27567b71e30d185c12a395e8c8b914d7a827684bf81092709ae6d585041aabc89c570c4ff2acaa6598b41fe34b213081c114de00f65b18d161558ebb0cedc2032a5f94a951f5a910daa0329105b8ceccdf7300e656d6a0769bbb7164010a710b18072e8689eb46ce25cdb2e4796a28ceac7e17728b42ceffaa8fd9e6057b7dd18dc5c615ac371c6a522fcef54524ecb70e485694484e9addfe57c15804324bc139087c4c3ea7d3c232b35fc1f99545dda394577400eb480ba92513a5029007d9faf3a98d32417e60875a988078807b49014bb0a1231c3f43e9f919850e024294823112ecdf1a9204e814c2dcfdab889c00a955622c725549ee8e45e80c988771b26513e0c0ab041ecbf1de2497b053213f270607e3638cabd538a3b3038a464d858f45ab76a7387a53a649a386111550dccc76ecaade3354565831820c398a9906ce7b9ff2850ebd23b8a76b6799c5c5bda51f143603c7a0538d3e19087dd2d633adcfc8f6d991268841d858c1bb8f72b032371a4aeb974914e564650c93c66bde4b348cb935aa2ebbdfc7a5fe7ad2d72077f8d9119f9bb8b0c490500a09b47968e948e1f50273a701d95b7e2f0dacb34e532869ef2c3d576023a22d0f41933dc6e062efa890efa36c2c4c4687f699ae3784dc6986e9a13883bb823f00a00709a9a5ff7e75ee3d342d487af1326a56f6b2f5237d5a5c05fc0f20c1190a1a499c696a2bac2672895486632ce4ee02b341f71bafdf3e3370771e98c9900582b88f2807986b5b3173a7de57372bf2cc23497a824b357acd6682bc920b980245e1ea564cba73a611b616f65e1a5701b7086f35926ceba374908e7caac9d80f86330ed2700e99dd8630bc0381ecf5ab29cd7fa268a1a8ae0d4723749fb164084cc996f527228c736556244302b72a97f8bdd8c5c18b33b666b5b91456a98e0ca17b1cbc0cd5062ded99b4d91c901564ff69227fb217ec003e692426aa8e9f0ae02b0f0bcb1d5ff4a808b37ca4ad05786af4d09be2209dca7623a68d613e450a75b51ca3ea0bbaa56ced307cfc400d724d5e9ce3241ea41f0be2559674571603f83a2e847fe5c0349e4658f9cbbb5f9276e0c69a0705f4c59ddfe38014c21402509b332ae899564be9b6d57c5c3cbc1c1caff4314bde4ad4e72f3cbc1d6ebf0d044f3b1889a54c156a38d3010db86f91804d87f8b9c1627670b6a006a55fa105b5f13e43041d807a8f8451e5f8b0a2991b24890160f1ea7353f79f527772ef08c163bbb9b37d049ea1c236aac84a077f4389839b030bb815f36b514f081a650e5d35c155c9951f881fa883f289d19b553dcaae5070f2b42dd1578b5e66271b0185b56a2ced67d432cf0e198ab0aaa30124b8ad68978632b89440c29a80278d0151eb149f586a503ff7c514d4aab78dfae1a09b04819c5b911eb6bec73e22d30565d6ed7e5e8d7480814015a30c990a073be4c5eafafab5f1669dcc402152c202c926229b2cd5332a2d6491a13dbc493c456b52a33beae5d857567e38c2a2d6093113ae1be9afd481b3c802236f67f1a1060432a9d4fae47e1921307ca57f4708ecc81c236ad34e7eb4bfaa15f98746a1925d88ee754939dc44dfa443b935db0a29534fca8b0c3cc83859c6837ef851b9eff24f43a9e18f37dc663620fcc384037959b5e46250069611ca897b75356440040c4deaf0da1a8d15bb72458964b508c1336323594f271baf5bb687592a20c9afe56db4c83d6d650100b4ee3e246a0eadc35f1b75d1e7a03cf2d49ea4732f3cc4cf6d8f129d3e8b7d02a0090c6fc803c20473a33446200d58eca4e4bad60dfd5cc4d96360e5158db5bb0e628ffb2d0ee7e17fb6138b5ee0667bfb786c399f8c0c5f86b1dd85debf85f4fdc9c697d70f2a28d64e98c1507b234ed3ed00b7d754423486b00b2e8444f7e6de63a0472d0d7fb0744c03754c271f419a10ac7d00ebf5f2e66486a409cfe60baa1348862304c641fceeb0ea945a4f9992fc63506f32519c9aad9c25df9d2a8f8e0934640f0489260d7fcd7bb2260ffb8217f0c36b4d693aa388323478c8c9765acc74809e0835720ccd781357846e7ebf3b994e7f9fad32335ee625fffa66552c6968a2770f70168cf6d1fd46637f1f5673b5384369a63279f6e4b7a62c8241cb3d71b55c0d96f31134d3251cf59c7f33f4c768606594a87f2b7bdaccaad6748ce031b96c01166a457cd32f02784f0735c75abaef1c18af9eb8aa09724628d30e856843560b3dc9805e20cb984cd9f9b969eb11413bd9f386cc6ae4e741b5b303da5f61fe0e5181acd96362643f4ba153e8c7a026e217d09b78ee74ff33a685c6e4af91590f5ac48df49a0926dd210e891ac37c13fd4c92029da05968958da28997cfa53801a385f2c879f6629bda69779e174a246413b056436017df1c62fc82fb0149b100bc207f9fa0d36072d8e926a42f0b847f73aafb64edc600724ab1ee32befbec0ce5359ed469ae919c8a5347c85c1cfd17f8a14d73b3c9c45843dd1817a69f7d06e4b83deb79538262f2931318969e797120314e97aab97f2886663ce0a072f001734eb9747f53f42d17c4d92ebfc7ad24f92ecc28ce46b13dde798795e59e9d0f1bb8edc18d76e57c072bb36ff30b294d36c33fe40c3850ead92e381adf41cf02a2dafbba1b10eda4ce7817bf9cfb33c269ada9a78c494e27a1e2fe44ac3d57060ac8aa7af93d935ab619c37d060c41ad9d7e04b3c9c986d1855644c865e5520c4cd9f97aa5dbc83e392922db868aaa392dbeef4c323759ae7e78e7652eb94d02f9cdb792e13c1f67e7e0a5cbf9b97273923a17b47bad9c232eebed826892bf0278f94ae25f41659d1be2f6566c9652e5741f9e3b3b84f9624f423b07020bf102250c20215ece0a395e4c5374668f06aa86ca6576a3efdbef4fd2252f208c470632087378cb7c0c750e3376236b5275cc85389a01261e7dd9878f90df2d03a5019fc93cdcb34884ba212843583379ff2a39aa7b0a52f31ba5a26fad4d755a5e071aa1655342dc12d9cc746cf611c01902c93df717bb3fb46e7ffa32052c115a0c9c0cbceddb7f9f1a6125940dfa14d4d7ef762949655be5354be8a79217499e0d64a21a149f3f7eb4541a4dacef0f439529c16b9d2ecd0651a266c3a387ff180a233dfc946428f2ceb0fbe8ed4aa129519265a58da156178d371979b1d48aac07a5aad82c925ce6dca44d8ffe9471838f2b21c966dc80511739ee9ab1fff3e403b038c227bac3eab1f7b161883e57cbb9259972ebdffcc0c454bdb10e512d3409d7b4b9e9afdf12b04cf1859620a302c248746448f3fbfde79bca5bcfce7e8b0ede64ebc1195e74bc92603df5a9673d142fa920de6d1ebc6fbfb3f0161bc79b0c1c1f538b968f20620c252fb70da69277b2248b38524e279bb05ea21a216cfa00589f681d383031f5ae113f716b0646afba4fcf439b2487ee978638b97c25e00737cd896977b1d47aad7cf38d6539b8c538d96c87a91891eaaa17e908c6208d06ee88e08d8640055ce3960587a50e673034966d509f817402b37400b7c3399d0e68c3b953c91fc5cb9167634953025136d9ae6e6ad5266d6c6afb3efe7eb2e706c0810a60ef8144f2d12a875c7cce9914a93c77a596fd618cb058f1e50ddbd40164b734101ffa7b9b2349dcb92341c0dcd43046812bdf77b6be9c77734ebcf7041aead0fcfa57f8975db42207a6c66b9846637e9ba99df6aed0bea13d959a4805e920376924956c28eaaa2982ff8992aa57c93457ab3c6dfcf6fdd6743d22ed04880fc1d69965dd16ce8f0c0f62db084b2a67ea1f5293f705195c0b12a9a13a0e3de873c1239f2226a49af95a3d6c39dfb5c97fb278d93d2779480d0dbef1710c3a8a6efe8926cf49a1af75e21505cc4e4ed2b31d7bcc73bab377dcb43a7073092485de61b8504df51a0d302117f6658b61fc931132fef170567bb9011eec830228ff1154d44c50df45351b6bc8681dff618dcdf6b98b1caba3c6f6231075f0045268c398e97e41a03ca4732af2c99025c0ae6973c718ab11362dd4a274429f0928e22b9971d6fb851990ef0d57fc9fb8756f0c9a5d4fe1f23df969cf0111900c5de047b45ddebf4b07d45d34451fba43d92a015f83b3f08eb95bfca4c231ae0535de8bfa5478e9f72f7e192fd59d227df85bc639d087d0bd895a600680cc0c0c47c33e933a3a51c276467813fd7d56b437a0122f2ae6d57d277c1034825924037e2ab07ba9bdff38b93b3dfe90c33e39222f5027a043ae91ccbfb8bd602ddf075215c0015e3424543d37c991c3e2142892fa2e45fa0640a961a369f816a440059b57d3666fe20d1a8dc2395cc2c1573001e142f757cfcf831b7ab34045b3c803f24380de64ea7fe0c624f2cddb8afbb898dd8ef906b5b718584a2d8d2efcc207d45df2c7a7c3bda3fc16a80ab0833bbc62dee12490e0a00be7cc3b47e0a0f105fadb8424e193295916124575b920390b3ae29b0514992f35a749b0f70f10710254dc8df584ccfbd720673025d21416ea3e1ffb4fab3e2607eaea79fbb99c8a06f10f76874dbde793fb4b4ce2506314b1fcd118e6a1b96823ddeb24fd2797540868ba0b99bf8db7f0b151fb5b779465617a95dbb097b388e1663b898a9eb070081935484227f89e149826abc0d72ec733c155c2815097d10a3bba82eb1769be0b8a95041f5b9a5079ac46739aea0a59b391e650b5c506af66a1b051df5cfbb400a5d50e8736d0c758fd7e4110c9511ad2673b3299bba843834a2f3302d576170d226b608367a49bd66677f5bdefc011f8098e6aaec33783e6469769cc341f3005beaab2d2fc9938bfb9f393c07c0bf1d53fe403309061a434d5243717ac2b420f6097434071c5f2c7f4b15c31beec7d259d0eab28d77a5c7bc18a99686f1df70bb7dc190f7494cfd732b4f0d89408084a0f38e66d56803e3d3967964ba04b0e0575799343612067fc7141af68fe6505f75a46f5591cadac178e47f56b3aefac0de3e7ecb45a4a91ca7d4b568009576993f4dcc4b483f65e916bcd05148668650e95606a512ea61bbf323ac69075b91beb6106453926d69442ab6535104911540d9303c89fbdd769c72a00ba246985508cefe5c17fdb888d8e3661420afd5dba07eb9600fbff857fb88afa4e65d048a55c8cde2d32e34144bc048c0280bb65f407b4050308f413934d483df434975e0a5853419970bff935fbb6a0c889e4d8ef04f295a76336ce724703d747aba1429dcddc40eb9451b0fa399f537383b5101805a3de109ba9a52fd03afeb47de330392dc704161e29ad3d8a52bff6279dceb80f0e25be18803c047a58b260ec98f5066d552644c9754b480a48acc210bb3a3303ac148e9d8519d141b36cac509a9751b0291af10b52ed066680a320129bd3db0004347f861e9830db9d5db16580696396b9cc6e0b257898a0608adf935ac7ea079dfc8636455bc0d5a86eda0c62923a82c6627df94582bdcdf7d61b7c9d94ec0ede5ba01591b0eec9c6117f9d2b30cee1901a68943dd0d6cc2ebead94d4134b02acc1c6d5e5426f893b4c4fad16288c277e64a8a8ca1fc86d0d9c039445078f0799c926b5060f0b125720574522dbedce63a0e96e6b67a6c3692f5d841ddbe907b37f02b69d81d24ebdf4128ba77f163ef1a71108d5db9cd49df51a433b3063072c4c92666e11525ef95c7487084d8158bc9648bae1c2fcf2785dc28f307c9b0f8762ae25c090f1de012e9ba55b21c0a9f0a4ce8f8376d80c44189738286a000aee1b215627d37536c8bc3a5a75f45788a40c03b408d037b8a58b923950fdcf0e2d887ac3124ca2ef531179932e7c0d22c8f9fbaf6d58d146d59b8c577721c40914c01e7d20e01c1657244437e94c6d25ae91ac09c3ac51d5d7266e3f3f1a130e58d24eb7368237eef647b03bdaff73985eae4deff5404f4ecb076033f386030d8f493b54cfce61e4d89fb0978eb02995ced0c7bacef6285af1665d59a50d7a0d7406ec65016011ab45e0ce6603c1432a20816f07f3655bf6169325d69bac8a05cae84b9488986798329ec9f3004167fa15f5d92c2c6c780d3243c56125c07d0990ab963bc5092dd76bf186702b9675a4c5119a4fe28b1a696cc54e7bfba7d309a3cc38dfc0a6ea12d413c92ed3ad9f1b602b50635cac0d95e6297ad9ca1a1b0339230c95c17610a42f36574a79f33ffbe1c95a138bdd694d3e9dc9c09ea73a08 false +check_ring_signature 324e7a925a0135949e922fe86d1877afc0783b494bf5a36d51298b74f35816d4 5060f42665ab04ade6a041f25598b21975786d0b2ad6e32bb8a08cc020a42c42 2 be4fdc09b4a9186aa108ca25fe6fadd2b0dcda89f198b2d42a8b848cc3e6b12e 0abad254f0de4508fe448a12456cec8e1d7bc7257035a5aeba839b3cd13eeee5 aec907bcf39b87796c7971571614196bb9ef1662c0b1ce0a672aac143d6c1d062e5f281284ed1bd6939eb0f13c903c9cf584dba073d54b00f3cf9be84f225b01e85224c204073202661718cd011192c28ea791ef1b4d6880521723f63a4cfd04e58ca9d8e0209c61edac61c3fdeb9c54125069ade48fc279651dec2a44f06f0c true +check_ring_signature 990066fb15d76c6457efb3ede68c461266912132dfede8bd5377c95b83b463e3 c939aa86013b6000624a2bb1130f16766d871955f0455a07c869cbde54127f6e 1 a38b3becd44f64886aa2201a8969a5ff22ad26e1051d51cc8455dd701a1ce650 03dae71edd1d7b71bc674e845f2be9594f89cf6e2cb93f040c61272b572afb014816dcc0345c819f79f7c9f16f8fe72d2fa4d829705adbd5bec4163730eedc0f false +check_ring_signature 00bc2c28d7f29a90298aa4fc7ac6587ff1befa333d9cd5d9b973b3e22b91cfba 79c3c523eddffb8ff68cd68a6e6fb357cb7e14936fe91272312682493e7bb4c3 5 e016aed8826e76d607070499c6117d4f5db1c550e950dc89a395111e55d7aa08 978a3cd7964b8e66f2ce7dae8e850331bcc7a9d88e666a7b00585bef992ac952 c5838743fa341ae9771cc0ce8e459fecbaaece0c4df9077dbb2b26a3f7faa001 25ad5e35bff3787b133fe3d11784e6e28b8b328345743626f5a0a938efba3280 b47543e2dba5276c4e7a4d6b073c2943d73fa7df5bd543d25e254e2d4b80f2d5 5c636c192e4c7bee18f3b1d2c520a2503e1016dfc9fc986f33cc18619175140ec7cf1960265292b08090d3701273107e378ae86066997c176d2e426cdf7f2b0276259734c421ea52d9f6ac3ddc9891f8afbf7c86f6b6016355cf343e2344310bcbef66e5116d6f9bcba185891341cc816d2fc34212363ef13edb51c89074460ce9b89e3070559c8dd7079dc08c9fdb995a7c8fd4134d1f0e3cb53404e3d4910d34e9e2bafff77b1b5822235015d32a69dc1c2caf3f0e47fe05992653b37a390f097b0fdf2c964f2bb5b021411cf3b2f71f85f95c73cfa19e5930c67734368104f648f20dbe3ed2a01e7334f8d3e5bc1cd8bd805fa77679f61794e33a56bf8c053a9507a121eb5f951a57c2fa25fd9c644bf4675aa254cd279f76c08ad7b96a046f9886cd9ff9e594eb9ba3742a9b575df55136f08466f34a95059618b4bc1901 true +check_ring_signature 95eab55ae70495fc0a4d186c92cd6191ce49af62252768fb4d6bb3289e33c556 552e68c75155bc4d6fe64799d4848f402065791b33cae2f78fdf57529128e1dc 1 e03752264adacf9faa438f2322cb5322b3fab8af7cd5d81575e8e9b2d01300a5 8e75baf876eb74adc442c5f5eae5e7fac374ac9e51c2a8c1aaff6bbfde29dc3042ad713cfc6d1dea409f8f136423e81147760b9c2c8ff3f18201b4e446faadfc false +check_ring_signature a3d029f0eb7b7767e59790776ef4214ca776444d934970d74ef9574f30963925 c6773d53906a34191cb3c990ace6d9c9d7f92d78ddb31c8415f5a5f58e0d0453 3 317cb4504d5ee17b4b3786c1318f620fdb85410e4646e1a3605e9485a408f315 264eedbcac5240f83246d464366e172cc55c381c57c51dc1f221ac6a539ca332 01127f9665b2029d1cde349ba90e8e93c08777b1d0b1febb691d0f31e6dc7586 5f4cceb87adf6d2b0e3d05aa8b8317fa2d050d110e971c4cf545753dad5411617c3d05377b7d1a323b1f7c835e4ccf402d7dc9371da5a7a89a0747599131680e72b8fdecd1fb2a1a4188d331835f8758a17c7016b092444df8ba5ce9836f0409c3ab966da1385bf651d00310263acce16521387adc641867fa6f3253dc2ad100389e3a92e5ac1de862a7d52f6a846f61f9d02087f1dcd83a4eaa3407a0cf1104e44ead73800c6358eed230bad0477ba42454d8d523267743ee945709c488b702 false +check_ring_signature e5d26d01386258fc981919403ee637ce723a3b2ff0357d1483fc5a22b146c581 f348c08b139da3d283100cdf746e3f2790baa72c9f1f2aff29f00d1ed5943566 33 4e215b4b3f9a18a159d56deffb0efbfb0a20e229cf33fd7fef58b7274e77b703 c8a8fcc17fd9fa049e508277a37ef1aa0a18365a732061f8d99b602d12cc636e 3179df3b8e037c5c228a382d885bdd996bb77427928009086baacbe0efa84a2c ed0f89c153871bfb540b128dd17c88f8d567cb52fa43eaf2f22d514c82a4e0e6 1c5e7375f94446ff0544713e214179da787b20e430fcb3c585f74814b1a467c0 7643694e2c551be085147cae91ce2a9997c082ddcb8a9c2e9458c0bd9ca26d98 4a3dcf6b55e1e3394df803c59d306a1ceb38be53006400c40088986fd70f3db3 20f4b49cb618be3bc80ca3b5ef931e19ba74c7ba5d0ccf786b14152dec242cc8 67a3be63329a1116137eb892732432ceee724311b7d7cf9d36c572a4436be5fd 8e7fdc2cf5668e867c6e4d299d5a9fdab6e370c87f0a4ec14b67e45e40574487 a33e77e12d3bee1c671f8e9c8f44567cec863fd287a83e439f18845f56efeea4 ed6485deb6e249ece3cd12a99b5643213f89596a52baea470839d77ed27d8eff 584cb757d924e19a2e13f76c41b81c81046aa8c09f2bd43be5fa688e4569bc17 ef6c4fb456d697bcb20cdcdea0c4f5861834744419157bcab8fcbaf179e17c0d 0ea564d99e130e92c06a28cce268a9346002115f13e3d78b6a832dab114d8626 8e5b5af4502f951663f6afc69e0e07e4278f85b3fbf7cca3c897e97034d9ac93 8159d2b8dc61077f2dae716b34002e73772f10bea825f3c5d71cff7b52009fbe 181692a46f177f7800fb031dda7a6c655b372ae97eb0cccf194616d51affcc6d 1483e7412218693d49af19659bb2686232d48a4c594f8a2d63d1642b6859057a 578d2353959599d4d706e39edd87cfb521ef677dc0af33722e3f5f6b6f689770 908c5a23ea5e5e8312966b4ae9bd200d3e3249db21c194abb44f678b33d116a9 2ff49e0e96b01c392b7f65281110c8da727f7725d9af10d69882fa0ac79c452c 7f4e38bae87cfc953db7f9679e0fd5942f14ab60c15df7891ec7464d723d7867 021562b725890ee7d9ba601939dc20a6fca0ee02faad3f23f066725fb3c6dd05 319f21834a9616800a1ee3e1388b7e05a140e315ca901094a9604c1b63b92176 a07c9ec84bab03fe3873afa2445e66fa10fdff3e8c4094c374a7caa4d7a7228f 515eda5198f3f585230fe644930b0bd272255c04b749f8c74adc17a8bba97413 d7325bb846ef9dc904561a7ca931c3c5ffbb8445360a055295cd7ea7bcbb03e6 36542cc6f24d48516f06c9a06f3de44ec13c880d05ebd6de10ec98684ae27cb1 8c1f570ffe158b9f72224b4522db07c9020465953ffd20e68fa3422dd4c133c5 97d3a29cce182911be58e8258055fcaac059ac9d7161030e003d3d04886cb8a4 b5d17308fa8b6116d2f7fe3ec39b3f6e42027d7e2a48f4691bcb47f2d3094819 02556f7c6afb47ecb933715b899847fdb97417f9f7b6793e9c8974d96f312285 11e1b96d62a3d226df724ee1fb91cf8fe2b414c131f5db9474bd140000d81703b77c10b9dd3278dfb9d526459e89be482137992fe86e8cd1227dea4b5fb3b70cec4abe9b16478c0c51fdde02dc0452296a4aef8e0d1608cdb6274eb6d8e8c907c6e933ec2d92ef0051618853198907f578aee1347f8530cf8543eeefd1fd4c0317714acb026fef0ea7325ed610714241bb4385c9ccfa2b0f57ffd4c1c6ccb00a89bff4394abc39d44e3dcba3d5ea7a1a3400e87d1e5f12b81aa952906f4c1e0057745379e285758c0886184df868f97045cd9f6934398a219e642b25868de80a4a40c9ac804221da28c8b788621e620e69e800247413e87dbb4fa56be472ea0a2d61550dafe565db75a7619ce976034a167c8c0d16fa1c03fca64d9208ec37046a255a4bda489eeb886bfd249b3f285fa5117b8d5e4603e2ca0c1f1eabf0d201ba02bd71a93c62e16b43bd99c1f3f10f968b9c04f171a3a2625947593d81cb055c0236879e152084c2b2e4ad23129b99a36e11b2566fc458d66da9644d0a3109b61edad10fcfe100cb536cf113ff373eb105733ad3469ee6bf1d8404dbbd2a01320f8984b4863a3c511f53a779f2ddfc63d2dfbfc21653c5bc285a74fdcd160c79e53c0319f132ccb660705bbd859b2ec048caeb422366ae0908686c8cab3705c58d5f69fb08fb03d87c607e15584560731f8efb53b565640827d2b14289980baa608c75e0f7792d775e755aee52071469166057b0e97b77983271968236d501f9db51e45558ef647d248b6f23067ff1a8d75a0452e9d3b6b3f7f2ea69ceaa084bedc398c72dd478905e4f163e20f1046c71e4526a6b855d1d6042ecf85ae70f8779d4203f5cacf61a7451f2e8cd28e225dd6bd3cc0972608f67ffcfa2973206f3603cfa7bae5fdd29a367d6af95ee92c307778b31af1fdf5f09557d5da6aa0cf95117f889c3fc170b4c9d1c74a2d490e72732f48217612b19e6bb92679f7427c81435b2626baa6cf342dac99e74b5aac108374717ead97be26f7a23703883012f8ddc6ee5c7165b649c754985cddd0d1357b106cfd2453bf51d04b31a531500d043d743d99be5307eafa136ce2720936710693293506b7cbb16cbd246698e0e5a22dc352353fada97412e49c09e4bcec6b03ca910323e10cc27da78ecdbe109b21bd9b03f9465b01663ba04ce90eac626d785ef3fb00dfe66e6144ed98d4e0052d52767de163c28fce75f5f575634eeff680efc43cec5efe32a952be0af5c07ef4cf5ef2de6e735265dbacce17f7e2c9e673aff5d0a65b577ed70c225212e06e2c6ed62f2130e48095df11f7e35f6f6bc087dc61d0a757fc43423a9de77ba04cc485ffd440360846db91bfca317b8cf1d51d41f8557985345999f126884f8001c0bada616c180d745c3c9f87329b3bbdddc8577dc04d0e316e58c44e845550232ee3886c23ad826b4285ef038e14edb99fd41661a11011ad6e1df1e36147607e9109962469e3076c742c269af0c604bb1f0854b96cdef11be675115ec3a77057fbdcc1db35f5c78884ce24d8c0127ac4f451779ee33d0fac3a70e825c78db001c6f6637ace02f87110833e16e8f627434cb83efb23824ee7074980e9cf40d0b371b83f9c30d6f1e27b252f700e33d8fca36609a9094e076c2144c0ef849b30fa9939bb35feaa23d977a05b32b2a8916fa665228d9dc29d6ce35b0b975b9ed085b801c55fec34aaed8af34207730e362c6f9f89cfc2fb40e63602acb9ff9bf096605007610e2d0ba61400a79ef18285f79581487abd1a321eba317ad25a35d02afca672aed4a3798ef70a1692e86cf08d955c5a37d19bc1da09b0d186bb7a003266a0d5fc0a07511f0c31e92a43e0d435e1ae52854d65a9d9292711dc5fe2f0a9d95c24641f60a3f9a4299488e31ceb4772d99080e71f2ba7c28fdcf63b749003fe030b94196642422aee9dfffd31a67f72af1bb78e6aeec6976929ae3894f046b9f4b7b6c11e973fe62a3571c4564c8d806e15d8a99b6d47d621311ce86060e77710447f3469d7b5df6036ed045d3e5d7804f79dac3e51fb7ceebcf6ba40d09ff1db3b308438d0f2b89bafaed3633720806fa1635144a4b93ff07bd79467203e139362263bf01a1f8679dec730440ca8d928e3d99fca7616428623fc814c0013378aca5f1f6742f42a537727ea87fd0e0d995f4cef7a829e4b06499c126c20f15349b9c0d51db1a3974b8eedc3cbf7cbc96d1a5d8e44e2a9b47dbad9d1eed03d27258f9a5f77d0cf178a8ba25841bd4c9d6c1bb58c15c7400009dab6341d30c5abfae8e9033701ee9719e8336f4743efe99b86ea52faa9fe0dea9a3f240ad07601020e8a1aebb2ac2473b4e4ad39dd70aad11c4ff78ebc847e5d8a577e5d1039392003c52d37a332506a78cdc64dcb1305f987a7b94d00fce3ea6ddb978620039d7931acb605fc35b1d47cd45c9cd2a49fb6ae8f2e68666db03068670b8a606fd665e363eb8067f1ea8ad2274626297a17d583f8f9dd413fa7a4d02dfa6c90b4609ac9d85db8f9805dab3cb499495b01a7203050aaae3f0effe5f1cda3cf60caf640fc8ce0ebb93b155503906038dacdadf487b7274fc9ab9260a176a29ed0119d968ef4d4eb9ae0c69d5f06b117aa2bf82b93dccbd1bf92f7e92dbbb11ae0f1e89bd666450c72ec96e0aa21efa45f5bf9a9c7e0797b47bff3c0f38626904024cf731a2786ea66743dfa5ad36a33db1fe2a86708366a97687725f1ec5548b0711a2e008fb06f16feb5f5a04465e02ebdc5fc746574c69eef800f18f19829a05862e2c1ad6f5149aff143dd9292b733d694c3230f42d69b759a162a341cf32093e5d805016763f430a314c79faca9e5d53c6181f0a8744216f2a976c7d0b8608e7dc8990415ce0942565c827daade31588b4f8f779d48fecd989ca350464d806a929ee9e0c58cdd909f3c284b3535f8ee62dc44233bd7201e17739582a111b0c false +check_ring_signature bc7b749e2dd7ba29dcc211a9b0237c8aba23cb8037934f4a284a1ecf0c08faa1 75e2ac8878708fd5f71d12c593012a1003af662d1701139729f24f17e4d89560 6 5b0bef1a8272672fa79b2b8fd69e784464685fb87f3ba3d5fc2f2457dd60b773 de55f2f5b6b19be6a0acdda4cde0ef740313d5964dfce2162456164101a1343f af39b738844e3d0321f710a8837a995ab41b4eb68f58c375917140a313da40e6 a5c42cf235d07112f8f4ced05ffaf8bcb563857b3e40be9ddd3aff90f0ef4ca6 a1527da8505ee003faeed12b049d3f238366c7d7a9238c3ee4861a9fed80dfce b6b6c161cf384963bc79bd36d15c3ee0ffd9baabadd25365c4542da6c1ba5d17 1e3465b78bd1f4cf80265fecc3573d949d7ec4099fb8e983c9af2f424805950db6867d69ce1357c4bafc56a2fbb8d1c21f577da0afabf625dc2843ab6129b40a96859409036de9f3578d0294711bcfb09782f59f96c33d3a698129bdc5a9ba0d7d1846d8cb39dbc18e76f95a36578f9f4e171005e65f13d55cb33d981e9dbf03c2b390cb79855d9d032b1c65c65fd239be7352e175c758a291610148d4f4cd06634fbf752b20a888b45c1b1cb439194f37374952016b7fe3352c7facf7c0ab08916227d16934e541aae5397a35dad6623a0f0517a218ebae7624c3cd006c0201cb097ffb82def3ff6a7852a9596a62d37baf9be532fa550e5d7c2d743905ce0d888a5c62bb2093ca97aa3adb60b33a8be4c5bcab4ea557acd44dcc903139d20b53c780a6c2a1404263f75af4d75f90cd53fc7e87a1c70aeebb3d071157c10906d94e930877aef9ce796a7f3836ae9f8184aabb77725ab817d55f3ee8e60f2709f3b49d9d1404e307bc530e24704da1010bd8f8211eb5ec4f90fd593dc6d0310a false +check_ring_signature ed7743168f95cfd2f09fa9027e18c17db355707dc60af62f75be4583691ff0aa 01ddf0e52f6b0fed030671000a4180122c0f29bab90a13b6141b712dfd7c193d 3 cbb7a8ffd0a3d4fcf8b80ce142ab77e9e51dacb6604390d470f224a224ef9b01 14092684d3d53d986b68af938c478b1b9dd98dec7827bcaef1b7c302c64402b8 b4ab51c61245e6ce6039b4cdfaa0a906912c64e4f8d42bd7cf7da29ceceb28ad 6d68ca33688d3900d0b43b09b337da7b700039193f7d7de1369e50617de732017e6202ed8a28f7a9707496529d6331e58fba887ed5b5d92a171603213d261a033be7ed524f014c6c51e5826b9ce1560e48c84adeb36c3a55c8b636c9635c1e0bbccdf2f53d711f4be342c028b3017df94aac3a964423290b2af194856418b9030a8fcd844266815b52edd0663344fe93fe3c5e034fa5b6e24cdd9c143f9e18086c2338cabb41d42ccca0fac149b6bfe942eac4ac7ae7453b4824deafbdba9e0e false +check_ring_signature 407764a3ca76206d8b02138cc82c94b418b89373f068ea7ba82045ef4f294b9e 0c989a4f3e6de7945fe7d3c2a9ffcae30c3dd3ad64b9fafb2ca6d16528463291 2 18959256ee80a493fbbe352b1f66f326878af9fa8654b0915dd3c24d5670ca37 28f70d7f5c71b4a04f41baede755f480a5ecc62fa463a6943841b021ef03992b f9c3a2249d9ff5f412977afb6c503e67fcf8cde8680fd92b5adfc70a8e355ae0088ec6b5d80cf817b199e43e36007909c83ca4d980fbb136e22ec83b5dd15104d70cfe9ed262acd489f8a7b992357864e9a7fb26504bc5ffef050ec51931454fae5db2d03cad0e7dba07cb8fd93d4375ef7a49af49313feb8f76432f540bfd00 false +check_ring_signature fd1b9462f34a867c761a9fd212f1471aba3b251b2e2694dbe0be5c60f2618072 350c3db97b5988489ce4ec8c670f31105e49df7036128c8a8a2ca78fd893eab7 66 5f9bce32d221d332ca139da2f6669c9cf80f8a8a73768d743a73d870f6901cee 0b874fa98b7e53f879fa9a871d7f01efbb7db749059b698b73120bfe3bcb836c 0442e1e86e997ac78e4ebd23a36f082075a4e9f4c05a5e30d6b5c5772b04b121 d145bed7eddf541491cb9d111431f01dfeec5c07d77fd477b5f166e977c65852 49c06809f459d54f9110f0de70f1870637ea033ede8c56479e8f813ea1e20161 b189e88b62275308765bb598f42a9acf4342ead28587448a31b2367e7dfa2d00 e62c0b80ddd4a4f7cd34b5a2af85d032b12f6db6a22452e553f0bd5dfbc64a85 311cfcccdf369a668b2b704e0dfb909363fb61f5397dbb278387657a0ce49c8c bf33b461abd5466e36b006c6474320479f0c47ad588629c33f05c1e93b627ca3 85d58574bd1defcd01d1171b483a130fa6f96432a6cdcea838408dca77ccd75b 8f116f153a428f29575d5479c71c7e82cde177caeb9bab7dec1950b53548602d deb14182835806e8175e0cd3fb06bd00388432273b55fe63e72c0c670cf05ceb 8e2e4f1c7220bf862e6751ced192cc8e5b1af5fcf5d8a9a2eb5765d3bc668c54 a7cee0481c4dbed64baa68613d5d3740cd5ad6c84faf358f26907c4b19dff82b a4295b107a916aff86a3d8744be6898344bd9602bb9e8653712196f8020d6bb4 08e3b966bd2199b5866b98343650b300714c32b5d024992a76a888bbb3db414a 1a255eda8f925300509768371d869c4e69980fcaf9875b36964fcea763e44b04 1c7290d0275e02493faff75f9a8cf87c598f4cadcc3d328f626155ed48e75400 5d6fb729174bdc30c2a0a57aadf29942ac2eaf3ac01d86f21940a4b5d43722ff 605b916bb5d99395135be5969ca6bb15d827b0c16804a7e69df3084d408664e9 86a061bae5e04a88ea849b24a5c8e6979d85ef18d179fc52d272ca82e986e10c 839c1885f5f4317f6c35fe54e7a4ca058134eceb42c052096feef1efe5f8127b 91d04bf2c4a40a22b3c44d1293769e2ad8a7883e77654f33b159d6fa82ca4da6 a28fc715ac005269b594044c0e4900ea2d13bfcb99479f6f5d7cffb0b11efa51 c1cc371ca6f00fc9ae339f594c86f6d1f2e47aac85fce40772f531c90f1391a0 a654bf73435213ce367300818d4f77a1bb54a620ebbba40fcbb2869d64f0abb9 8583e47ca3cd837d0781ddf4d824f4d33a2ffcfc3c342316a53909ebadc25390 2609e17a1c127825c7f10d07e597ac60660e4612c4b6b3ea724ad5d89945d72c 9d214c2b03603a65289751797361e2d0b49f7bc4dea4bd6417d18ff81d59ccd5 ab1851d9030abd88538fbba65a095b0204a8b87f0144dc3d0d34c2a90a127284 c5b839178cccd7c6c04fafea91b38ab477d9fb0c1383616f6de9a45f69bb7093 e263e84116c6be8cef24a8025b1da165c4a8ae957e918bebaea2fdb47e963360 d8c29cd4d28939679c3d7d32d4d26c5298301e3d0834a83ddbd702b647d444cf 8ac39c8299b448969295d57bcaa016778ba31f599d205183e44e39baf4f8713b 6e1004d70fde36bff53b9f5bf7c04cb76040550bb77099fcf86644e65b1ca36f ea4607485ed1cf5464db99df00cabd89cce90b7f0be184dd4cf38e49057a74b8 50dfe463a59e53a8ae838913333757dd5e2b291fb882557bb88332b8b01e0784 3a2055c532db8a4672f4811786c563877ea8b28db7fc11c6df155cff99b57f2e 7d1d64e336b9fab12ffdfd654627510f7062e57d76315fb33a93e8483026fea3 1b5627f43e83e7bfe7d464efe36dc9d47d14e7604c780d0cca9cc0c343bc83c4 3f2e5d4ef389a872304477c62ef894d27fe128b02f516b180712ce0aa3f4a359 a08e7750ea6b6e8e076b60c5c30e9db5c52d9d3609bf948490197acb04c4161d f45b8b248ad1b3c11db388fca3732c023ef1c8dcb34133ff49fa21a65c85fba8 b2c833449b8aeb9ba5ee7eac88afc56a35df70777644802ce343d03232302b23 b230aa1695356ce6ff2c7ce19ad1b1caa638f97ede3a858de8dcf41ad8070f77 f30534ea078367a8286b162f033b3066f34129d2f0532f7d8cf53c96b13cf81e 4fe23196c2cb46d131fd6bb45658a414c447c249435b1e4f1d5679bffd1e9570 818f9994a76ffd1e91f6ce5c360d922e2326b9ca915cd6547d37e50fe2540bd5 2ca1ae1c9457ef42d184f7ed0e0cd5a34de03c68f06874720b426d50c25bab4c 74417597e106b16dcb557858cc06df80bd07ef19cc7d500ada8fa0fcc4d02e1a cee158e9cfee71d2796b4f058c4bca5a89c15b290dcd6fe5b7ede1e76d507621 73b120141c29fd1a5db43ece454c96d57c43b9e8d883206637da51d556fc7292 dd657538dc311ed598e60b8ddf1a5f0fe4310955c4f3fba7ca4e0ab8d220f305 f51037e3658c516d5803c40b74b1935a25ad6a9ecd8851cc84640ea1c92ca7bf 7ae3d78b47fc5325bf4ec4e8246c741c6053cff23af0e6bbdcaf36abf48e2a24 df08df01cd7d2db839490e2822bff23ba42482e2a125015da5bf864dc73f2f95 c00744be33b0b4cc20e669a5568b02fa2b41494338bd1fc869ca9f908ce03365 b0a696f657a596c93a3d8ed56380875df4c19dad0facf19c4cf404b713c64619 497f3a7b75bbb3957806a7f0f34b00bae426f762ee6a63206a997ebfebbb948a a362f9ea7e5dcd18b473ac40267c6b2a88776dfcd111ac57f0ebf9dbf914a533 a61942c8c00eae8b91c6e2ad9d7e857d3027135a793354fd05b368873811ef1c 861a758460d58c3f1a40407966c7f23b104eaaf73b04dd3017ad65588865350c 59c8d5ce7526d0126f98bf6a78e616c4975952ca790230f1be58c4db50ab92aa bbf61d477a87aa3912f2cd03f96e2d1776ae67093f080f0e88ddf5dfca0aa627 dc4e19238089c0ac3f8d2ae8dc3157db9884c85998b81265ea42cf8fbfd04f9f ed0f78c762e8e0e23289fb08f141b1f42e9767741d09b8de0a4ea3381f992920 e881e00ef7eb2a9250461701eeeb9ce674860cc512d573e13b008187068f9501a3d7ea34a758a0a024df8375f5924d3d8b6d580d1ff76a7c584499e35e480301cce03b0962800005527efa7b5821e6a58cc36a61c1c45f6f9361761d40b12509fb62b338fcc6b863264cda9aeb217b9f669ced96ec33df244bda8d969938df0426b049291097b9cb165c67890bf8e593e7c96bdeb47e7cce63de151759df8e02f4a89264682bec2e1c2aabc112f797b30f69fe086ba53d6d0d107e0391efe40f1284d57d67fb1424a51cc9c07f140731c457ea3afd5185e149dfffb1747b44010e5ac548307d38a286c4fa05d5d7a4a4e96383b1291e3f04bf2b902bb3a7480a5d06015c4be08c182c63447053695252738db19e93c97764af473b69bee170018a08acdb78643200cfc29ef859447db5a5bdea5dea8912f1cb1c0d37373d6802b02125f6c330550f5736a02f32b0a8e0ce33fa54a268adb687e1e1ade32acd0b795bb06fcd82a3f0828e82960406f8b3ec1c92d31183638c0d954f14974aa00a97c5e53c8c8becdb6be81ff2f0ee6273f55cd3f2bb639400abfd499092a3900a477cd0fa93f1dda2d63667b4fb74eb9c9cbbbf09b0fba85465af2e604d287806af3140b9ae037d0942ebcdf0ea7a94e051557f598d6de60857efdab82ba7fd02ceb7b29b34bee4818a5d9fe2c6fdc5b21a08cb9d0e634185b0f07cc135da3201293a2d5e1f9e4de469948fa32151443be1f9a3a4bff7b34be02841893a4a8b0c255c19a13eeb0280d360566c26523f4cbef6fe844df41f77d5b413a55be1180fbb73c7df119b8768d8621b867bf65cd5c369808bf5f92d18dbfbf1dd45b158029b66e9808fc6882a54e191d9ffbcca2f481c7ead41047ffdb3e8a42f18a4550232a5f0bc84cfa362855c4ded37a4cb2b9eb5c5342079eb48075f3db0d2a1780c2cad0c4d2fd41b651491da9c6e6439c8e1b4740873612214b103575b8a4c5a0e9484e38808f517f151871dbc5c73f8b3d90523a29357e311c782c34c5bfa620acc4f05c541bc35d9833cbc8fc07536614043e707586a32c43db996a23a39450723648f6a5db1c568b92b5a169d1de87d62bfa64ec4fdece3c474e4bd6e80860fdcc94c28b0474166de1277b93dc0b93316f5959ec79761eebf86da9b72344c07219b7ddea7b40e8b53b1d8003b2aca98b68df2f64b2249e9bee5117c8089880a52c3bd958be8e386b1b4f80943f5a54b51ca2db487a4050bf78d972289d84f07db32d216b99a47f74fc948a5e730537bf33548c7923302d3bc59c6832773d405d294e567a71533e1d14d13c22a5660281d35a7d86e68a8798e034c228b9cc102315b278126b743cdfb8e8d409a1d7593adc367dd97b6487c38c0a8614b39340222674dd9544212e0669eba9649422298b897c8837f6244d9bb1f41e3b534840298fe1f70ad626e76ba572a26b6ab9021af54f72371ec4c8fdc5c1963cf98d809bb2aba95be0b0e558410343bdfcaa1a7a9aec404b3c8692c02e13f8754b1ad0bcc4f80db5f409e69245c1a839a05e63ba1d62d2b3cf379be41cad3d0420e07030d682254eaaf4d2888884308fca5b07a0d4170683b16ac51608a5c392f1775038080c401b38bc5fcb9d36a84ab7cc3e3e01a5340f243d1f9a3649b2c45227d0e976c3f23627ddefca63333923cfad79dbcd4b67ee57fd8f4bbd77dc084cc6b0ea1673464f9dbf23a38e8e16b365879b88fba18622b3d647ec43f08a122f98b042eb983f47582a21d2c0556a0f8ad96c5eb3a0f09fa29e668d72b15b98f55af0b5b1f940337ba5c3cb330fba64243a0889a47e24e64f1c07d0478e6939b29f80d3d413ce388bed5c5aab746231f7bf707296f66c3ce2b07b966f82f275950fd05aa7aaaa40f5753e0bc521511688ed7e8bdb62ed51008f664f7937c65bb4cbf01be31a4e420185b47f6bb778833f66eda4b6b95057a32bb6f892993ab0cf6e50a08505da861c914ac30ee5eeb5f22fe62cf4e054681c9e56a534f7c5777b5dd0952d5c172871338f344f892a380c902940d2cf1090ffe17a5a19ea31f40c3cb09fa9f506cdfeb871098670ecdf7f51fdc4cdf089dff7377d2cfbe44408e12a00f2915e20c51a4fc87604a38d1f718fe3520158773afbd17311a3e9e4c75dbe700066089acada38d0e8f361bec66733d7fb78349ef982287131ebac26657b62007654ce7b7ba965351bb1a8ded95268a80ba3465f3e666645873cb336c57a32e0eeb096e558d16fdd6a690c5286a8e4fe463a560ed2ad47c098525dfaa66a5890d2d1473702c2d29725be0eead20d50e90f14f41c443ffb2f0a7f8a19bf1bb700abbe6f1a303aa3947f618e6ae78628670464000a2dcc6d67b905216c2e7b9580f85445ce22a06594a2a4c6e728bd1c132015ed5e3cde0d29672934239b47e860de3d40184548a24b37590802aff5652fc490149a70b2f2aa7fdae7826caf7d7001835537cf9fedea830e8b34af2ae926e8d6e46ea36117ff1f310adad51e0cf0e0f39871d1a71220ca30d5432c220576604709ae5b164f576c36f186d1269d4054ac2ed56b682538efcc383a21ce3f8013d7702e66b61fed1722054e708704203c4ea4a526fee0a11477b0d8e27fbd60cea54cd420d5fa69e153a7274daa92b0f14976ccaec18636cbe98654e889658c2cdddbcab21de6ec06143ae1115ded50b82e72a58e6954be97e71b34373d6fd0b3f715a27e9190bad1cd3f4601868c106cd6f650720b85779a9f873dd35ed0efa8493886c886cc09fae57dda90b9788077c4d2ffe50046ef04cee02f692221ba6b5f495b14f0cae66b3303cc8b4fc75053228c3c680ab8c0dea74f4d04cd176df2311cece87a08dbc72642bf12848130fa3afb8d83a98b6d04a8cc0aba48896c0b47ab8e7cf2dd49c90af35cee79b1705bbe2d371b9f0525ad35d368536c16f8a5fd81583eb6f5fbc88eed69de38e6703fbc1e623209d42fed40ff62954c132b8f5eb9a41cd26d6dd676f716796da440fbf184d522c5259b5937609f75c1611a406e781e78beac535a4a1d6eebddf5400ec73933ba627edddedcff7cfee971984b2fc3508d0c4434be723d1af35b74c0a8a4e6293f9918d01d05fcce9f1dc725afed2bdb58f088121c6b5c265b96b6901cab40267347b437dca2eb6e673e489238497e63a3f6d7c9bf9ceca2cf82e9a022a7b636ee943c5673121218ac0e3ca77116b5e33e2bb106e595ac7b4fcf1e8012d2c93d569b94b88d7064a64411227f5607feed4d780bbffabfcde637f8cbc03fad30bff713e453288c904ddff21d3780b73afbca7e255223340b3390ed42a03dffb6bee2a4082df30d8c85b342810c1d64c2762f49471956584a2c70322500bccbb053455e8a3ec658533f5699a8ee19e997be3a984a4282b3041408d04170ad7e011930360139e78afa6ec6d679f5c284981c737521a97bafc8f11c2fef102a7d030914266336507547cf5154e08e490ff9df0d0a455fe7fcce8cc88cc260c0241ab2c62c1e5fe2aed2ea8f32938ce5ae799585d8c8e105408c93daf4e540fe33f361657419085391f74215451680c97801d891e71b7c61b1bfd924470840a14c1c47df1947c06dc39f9fdccabd7d1b8d2f9710dda0e542d9ef2bab97f760e58ab55ff8fc0b00e9f085f10a76dce74ea33654a5ebea835f9d6c9c1dc0ed1021efd8dd773ebc01060e2bf44cfa9e8b4d6d88a2115182fe98b0cccec787515082e6278ece9ffcd7928f0f39ed60ec159f92aa5756eb76b86511a81f64e363a0872312c1edb113d66c18a4ca71a437744b1dd484bbcfdcbc2139da021249b1a00d1f090bfeac0ba2d16491e08fd0432c8239db7dc04ff0df6afea1903cabd360864c2d8b7ad1517e03351fcd7e8f251499b7c7e1abf4d6204c96ae7359766680ce0752ae98abe51e85294efc39ba055215b2db129b3a301b38447189a2b4dcd06b37e80ef8354218212ca9bd6a292bd3decce0839f9a88fdeaee368436b747102dd710eb2c4718a066dae7fc1c62afad263524910bd72bb8d7ae237dff998eb0a6c869b2e9a5d15644b176d9f87731eddea31a3772958b79374c661e1ebb68503dfa5c227c308c71f615502fdd502b72a6d45b7bc3d492de324dd878b31db5d0b1d7d18569aaeee5bc5dcde779dc5c45b8de2b8dc0ab5b3a48390248d88e5e506fbfb295d0fbc42f7668920dda07d2722a34708c9c856074fcff1026f0151800752b5cfabf833723c01c805774c1ad401765b148afb0f55fa4b91dc356d54dc0208d11abf65c0481f53d900741f47d2241c8cdbac3d7b0903442e932940c1230cf9c2dadcba43258c59e1e39106c5918dbba92e9715cfecaecb85410188ec7a0f2e8438b9fa43b56195a6edfb7105ad79cba71dc78da397e7754112eae7d39208031ddc7d73da549004309475a226e06398f0c29cede08552ba647d46e74761062a350b459b356f021acd323bef6b22a49aeba9eed69a4254589dcd21d4d4dd07af8d01bba264824d811ddb7c05fe32f040e421bb421079b88d77e8e494d7c104040e51bfc0d26630f12bb74b4e85c511bc2cd7701dd61d67cd82bbf3a8c0e604aa8bd82ff4ae816a92580f80b12369e4cd77f6942080686190031068c2fd6e0f108825911a5c74d06ebd92f75559ca4b3d4bfd3f5eccca2aec3390a80c72820920aeb22e50973a30996a4d80ef829c8bdbf270e1977e4ebfa95fab7c37411706eefcde5528b79c9d711d564aff0e7c8c846fb815c27601a8c4286ee5d0476003546bd8d93414e2652bcd14c5161fa50267a808089bd1513b9c12748dd2a341064e383286599171dc589af9fc0b5360305f977a1db31c8d39bc9915a21e56800d98b4c469ce92e6647bb537786cef36f91e7402cae2ddd4597a543425b6c1110a33968d1a379d0d8e491877c23264abd5109e88314ae865e3552c68a52d32000d5d68e73d39b80d8fae876ec0b35e6c97f0f8dcd9efecfad99bfea2069be96d089b1253d7a66d5a8395cb304993ede653ab83c9efc11d9cae77dce7e6a7d91c042005d3fe89c6776d6493890cc67ce76fbd78c43977d69533116e82fb049d4e0fec8d687ae2c2823310a369188ae8d67214d5e7481226771470314019167bcd0025c5b8e96dbe9e1639cf6580aa6d2e69d7498f3b2c1890de78bce1243f69ba03c6816b863dbeb8a6336135e0fdae0483c04633859ff9aec22ead5e87c05ae2062edf1ca00b5617823825b277a51a1b079b0777af03f8105a13cf9b1c781abe093bfaba1f46b9bb50a9ee731f0805557e4a7659bb5a976755ef006e269ba3b10203f635422eeb080de0abf12a28ae0da817c8441cd22988555824566798d81d02c0f81d64c81401db8246c0c808eb09835a9f8ce48b3e5486576a8953ba9ba905881e062e37a0b03aff53e73141cf563fc79d296bc9a98ca2ecaefaefaab2e90bdf5bd8a825ce706099a62cec5ee86f294b57e0fa049366b499918f1b1074ea0bbffc88dafd79947050496ee8b6306671a6b86eff56c3c83677e8a02ecd0fff023c3c8b00f39c7e142fc3fc02437c362b2a8c02364a1eea9455c26ec59a3c3d0765efd5911bc406486c2ecb76e0fafdfad3a23430c21f02f4f2f7a6d2515b66073995550bac3af3ac4a17ea93f842dbf767219cf457dc0798c926eb912499530250a10e259024680569bd03b2916627fa2671a66ef6bab3930356cb6eaf6d3b0bc9308ffb78b2f0b80c27c0908dee0445ca27d36baa2d9d3dda07c56e9b77af01d23fa995b424a0f9e632e8761d40e4b0fe889ccf753e1034b2c3dd3a7051120156167066a425f4422d5ea4a2b7902dfa9e9cab75cbb27f273adc1d3a1ea7c90405809d1939aa25543648925b2ef7a324658a00c5bd1b7713e962d14274d81407daa539864765ca9338ba2b95afdaef3c8430298c282e6fb7ab2be9320d275301 false +check_ring_signature 69bc79bf75397af925eaf399a725aa9162568c3b04c1a33a9d0fe21bdfb8e1d7 bcf4c32494b88f7e4a6d98281ff7f27e3ba938626a9e62b112cdd08889b08186 9 dfed0c50f2ebf04e16507f352712e8ac3adc06d76decb2dc8cc01aba10f9a0f1 ce346fcbac8e98ba932ca6cf138b852c23c2866659f333dc22d59670e952177a e5159a2485743c8b4471d385a8ffc16a4e14c3543ca784963ca599feb1ef9a64 3ac7f6c2f70d651d7d64eab39297a1399c6717be4702403b5ee4178149811119 f1ea01d230607f922c22214db54cae5a31cfea98f95f2aa481781ada603b9eb3 de49116d4e98f26def1420112f0821de3ffea5751197f7d46daefcf94b38bf6b c46718168272470dc07c6617a561c12a242f76a086bade96723e5f5ed128d5c7 e84148cccc7bc005501f848b2f331272e2dce2137e0dabd724dfc457a6219a76 8d2a7701a6df5ddd7e46223e029d74f1ae252dd080e7e68cdb7c81002398a967 e3d4267cf2701cb410daa0ee9805edcf7a472e7e591bfde46b5c69d9463c7407e923355fe44d5573eb50d57adbc2802f0e4c173d3558d0a9efa4071fe4db0504bc5f02ee6c1fbe3eba7ef393db28aad15856583f1dbc5f21bc89f140574efe0a7b94fe0cd8ce1be4f531e1d86b58c4163ca8c7525fb9637453d6a9a0b0210f03afb65d172cdd8441985e4d1c4e54db71e5fb3b569b02ce6500628e49f2010c02f5b1bd2cc2f48f0500d79bb715f470bc807753112f3b67d3dc60ccff675608054e0ef4c029ff072c4177962d0505091e75e87de7e734cb6a4d3670df2a3b15046c2147d8ce5fd2dff6ff7f24b028eb70a548a25da53545d98342553cafd1f8f06de4d06cf0732761057df5232ff53c5a649042caf9a5ba2ba6997ad4d32305007a094ea0f408e803a3538f6e297674917bd69d0c7fd9f0979463700498de9d05efa659859629c5d278abbc9c8efb0b31c1baf5c32926ca84e378b92ffad42303c5014ebbedf9e56ed3d9bc848c18dc6d814ebed6b73bf27a7ddd42a0754eb01f5128979613c45ec4614aaf7d4650fef0a82bf9e47ff9e96d1b6bd30d9e08260411c2d33a8d9635138aba2e67ac00ac2d96652e01515ae8daf485f3537bc4f2065b17a66556ac5f26b0eaa6f690ec74aab47d80d9d3d7c16c5d39660f924ac2017f4ce12ed14e7a7051fb1c80a16f9639cf9f2b46f6717d0672e843d4eae36e09d8761210ca8cba1c203ca90aec8a9f5bea9f972166e053d9bf424bf2c3e32f05fe8d1f482f958d17591a23e9fcda64fd4a6ee890e90e07f90b5f6eaf49a1fc05 false +check_ring_signature 78a16b1ea11d249357d87a0e3f8dec3e3901f93b48840afa4a26e72f80ef67d6 4cfe06997d7def1f5c265254a6ad4c4d3a2673748378bf8e440489eb4805e4ba 15 ec4efeb7ef8b1e069113b40f90173eecd41168f9a73ea05deb22ddc721c2b805 ec7ae36b977add1fae5d8c1d810c6eca718beda38931a5ec95b19ec25ca3d587 5b14ef2f865e4b4f2e7ab034dda1c6503b38530c0f116129bbd1bfdcfddfd589 ba2f921b5737bf721d319f20d5d24be5eadb21527c1ac667d9bee59cbee48a32 307dac121cf7fed951b824ba2edda46fb210999ea0101e868f585975fbd20703 b67a42b38965458c76bcf493439e7a1fbb82c792fd1a67eff53c2bab89bc6559 afcfbcd613cc6ac0cec68fbec4b2cc151ad0cc579364a96124e5e2a583185acd 6c518e97f94faab4f98a6210a32fe75e7b7eeb6f36a89e7c3622ff6b06788249 1f9c4adef74d8c9cd56eeffbeb12b0f8040549607a3e936098df07112f08b956 09483023c2864bcd431080d7bc71596eaa7812522a41228bc7af1dfc96bdcf10 f28153bdea272b09043fc7704ca6795677c9e691a6ef4e3537bc0582a8b8b423 49bcc72a0c715367f6356ba22aff7497f0990be0e527c2d97dc58dd567268d09 a24435975375630cb3defe960beb6bd7f62f3254005a2a3ad9efd137fa38bc65 a50854f47180e2b82a8571c21cadb3a2ca1cdd47abe287bdfda25a3150a0fedc 708c6f5dcac59daf3ecde02f8de6e3d691c74c3db84863495fbc43dded8f7e21 d9d37556b1fa2664ba47e2f57353bda66561283d0b91c6ee3ec310878444ca0f9df1dedc1f35a977037c1c5f82586f4e61104dfc5a8c0294fb1e46eeaf25b3010ca9bd70b22305ddb249861ad5419cf89cc486fb6e0533e307d717c39a599f05fbe443a4d2e8b67737fcac5541ff3db324be5c985188a7da7c4c1a9c50176f0485523523a2f80366fff486b1d6da0da0ad7b694adbb8cee168fe0659b2cbb0023bb5983751f809812dae4ee5835fcf3e3cb70a79fb8180bdbd9eb3688cce5b06217b84facdbd6605938e1d9c7f2b390bff03c64312c04b0a8cb56267fa1d6809d0c5b224784bef8897e56d37fe315e0e9caf35dfab20317f9cff2f057c3410001b9cadb2fd32c556c84d01e2405e00870bc696262a6bf32a1424c794babcbb03d584ed886f7a2b5b882b37556dc4af248827f02e0e75696ddd5e0954718ed70aee81cc5d69465ba7e710fc6f2935e5c94fab766fccd5fd5af0dbc6caef10230d87812c7b3e3f8803f3bdb1ad20435b23a6091dd6ea8fa9ab169a947719a0780aaa00e0b6350a11c49f05054d5478b74cb471608a03312138ef43a5b43d551f0ce09735b437acd5d3f7747a61798ffa9ea34f914c28dfe5f22e3b2c9d37caf00ee1caf87902dad74009d34061a7b7a726451bf17e5a674f45487b26cdd29c6f059cedcca6404e58dbee94ac30ed3f8fafce1532262de6220cff3ae5738944ae0e2967fa72df37b125dc3aad604e63099e15351424ca5900a9e94a82424cba910d428e5200b52714532f43b7bf208b422130e35b2fc6bdb5fa0e9868fcd2f7640aaafc27190913e650c859302d509e579447b15ca7a5cc02bce396d5a6dd63740e95a58c222f2504ac83a6bdf28453a718d9bf6ec7987430ad40a836f717e70d017e4272c4d7abd1458b28535a29ef8c804bcab283ab106a7279316b34d9f54b093dc4d5fcbc329f8a6c764e211ac837914c706dc03a1b69b5d03a1273fd55fa0abffcb6d2f1880df7d05011545bcd0d43a0eaadcd13465657e2233fbff73c48073f0352820b042a70b4eb560ceca39e9379111bd7ac00d270ae0a176d4673d40aba1124514713efbd84aaa9fbe6c9c1ce147928f3729af1d3d7e58a8d224a900e51eb52b961731fbc2039a11dade9c023cdae94eff53d2627a5ff69f2254c280486c9bdb8ff5bc5056b1407e2daf73e45e43e5727be699115120cef3050c9e10368baea084c4923de2865890f1e0ce31ae4fd0753c8ce41ba7f481a94b2b78705cae89cedb08bba720f9b3c449eecfaf5676f43dfe89a41551e16c25aa81d2b05e08f7ccfaec221a2b6809e124425d3f311435ffc6b35d0be68ed583ab46d6207 false +check_ring_signature 11177ba8407b901946b12ea4555ead8fe4eb1d342f588bf5c17a952f1f1c4128 ee1f378edf33a81dba983365cb49fb18bd3bd8f2e356bf5071ecb19dc81c90cc 2 8d724d84c464417e15f17144a07617174ae1cb8927391fee91b505a652b7e5cb 5332a7af8b1d7c0ab697ce1d97a7e5ed571260e522016639c6d6c977576fb95a 8af6d0af8702fd77148a2dd3f2e7298ab386610c17553d99b385705cecdf9a03353d3896893b3b7055a778dc4e8c1360088f4a9b6090340dde572be32793b50b7e43bfce7fa9d2f42a002567002bbf7bef78d74244981ab713938a501617e407306dddea7e03d60a0f2ff3d2af1e2f6556a3b8067b2b5013a0cb4c06a9af4207 false +check_ring_signature f7fd74f94565a05c2e9615f5e8e698f332322b9cc8cc1223c601c936d08cd601 6ce7aed448a55d480b7c013d002406dcb68e80ccec30dca99001e5df63f1a2b4 15 7d5a273af5188869d20ab8c30aecaf4dec9416bf4256f1220857ec07d479bee6 bf56a085e9261801e30b2ac373b0c48b917510f2bb3b5042698e6ed4e67fc6aa e3c7eb717fd9d113afb3292f0bfaf3986ff58ba3033f502e9f71a90c0313cb28 d229095d3b4071aa984a6f00881c6a7c431dc8af530d13db4dd53f439ca06116 37e4680eec63a28ed7b9efd745b49291996b063bbc8bc35f2b944f0f3813449a bc4b16ed6e06fb6dbd2da32a5e118224a95d29f2264275b3d307252a2edf5713 2ace1c52eff7dcb0d91d9000b282e6a63140a3dccf421a9531c464db3c3a78f9 b3ad9daef20e820ca19144cb460b61601b90a864c321a915d8cb5fc5911a0660 ef5a28ca426a603e5cd76dd2c36a5dbd476d4598802c4be487fe575fee63b3c6 fcb96b687fb2584b69133cc83d2208a4849e46e0a634bec2fc583c4a970acf01 891b2fc5fa36a73977e4f092b196d789d5e1aa33d5d4675ece2e35f27b5dcf14 75df95fc3f8617d0235cc5e7f3694b02f45762d72bad28b88599fc63a2ff79b5 7938f83004a9abc9dd7b232e9feac4d592774da1f2f8a89ffb40e9a6fab0883a 20b5370f073b37e91e6d02664d7dce7c69f66460968e7ec3c1ab0171fa68c278 187e844f415806468b377a08e85526dd522f006eee27a19c1f537f5910581796 221aa17d47187da29d62d76138837f9a3abb1b269e2b09d63e61e78e1dad2ae74bb309e368eba6d0769732b63482fa76db898e4718f9c54f31493d2f61235c04a6eb8f482879c9ba60e9c22441ec14902b84a0f1341e37a3acb9f8d91b37370dc14c4c2fdf91c5e7d6f27b3167d38a39f5f8d78d9d53f1911b797dceeb757c0b17c6d17690a0b9d376a744c5dd1995caa7b135d764c534b549806a4133203406870efa7bc0f48275619c58859572112bbd24b94f998313fad971b0d9548d860f5b979c9a04c8e18550be97c480a0763d6df773295fc598219d44617832f50804c39cf1f4095ce40c5b36ef35c40d139de26914e4b84a6dd958cd574d750ee60156fc4d8763736e9cfda05810c5d62acec820dec63447af28ac9d62e20a8ffb0ca683bcf99b1566fa6484937c8d65e6e3731e62ef594c7babf4e5855d50202c0534d342013d88d78a58c14c0a2505831d9c72e617858a8deb9c89327326d1bf0517fd3cf5a9b4fa8c1d9ad95de6f623efafe1269ea3ca083a801006434c594909d89d269b9077e82ed38ca008af2d7e4cd4a2edbf7a45cddb3551e05816fc2107281972631be510d6d33f015859e8b05807880857b19602e46767b6614228aa0acb2926891f00e2f6cc35e0d61ec981768ca907feffd4884bc3df6c37f08ef5085882076f3bda9719b629f5c45a4b10dec281e653ff4e84dd3a3cf55b1de5800d1cbf86ce664e0139f61c2aeba3374c0c4e5cd7cf5d262316c6642b6dd85b2f05461a8d12684a4b61ef6a04ced8c9e4a29220e0e6102dad70290ecd3b554875015c38f9fd5dc33ee94ed0bca0eb0c755631dea0be9fa19472bee4d56c340bdc09c84abc7a67cc07f0162b26ec31f8c94a8f0e02e1e3be4d6a7b07976055a0090a5e7f0ec430a07a1696182d86025563d794fec02497647465a5021f1868fd9e09be566247fd932d1d63f8d115c76ca52a6387df40fa24ec310c5afec0e02c3d0100b324a046e156d20286c8c4c569dec023eae617a2665408c975ba1ad2c6120a3128947a4bc7405c244814794fae8a9ffe8ea6691a2a04a47bb59fdd9e0dfb01d7af20250495097882fdb05aa47e79ad67b6eadd57e21ee3b317e7a8bf369e93ef7cde4479368c59a0da8375dc0defdad6d4ba659d76e72dd6d639e0cd1b2e07d991f114508cdd7069e8c95ca017365344a69e15a1f901ac9cd4a001a7db840e5cbe2072e8df008ab58ca705aa66e17e2aad3db761cdea5d593528b985e7b603368c5b0e19dd22e5676d915495582f6f844cc6c0ec121e483f11f1a37f22130765f2262ad7137c525c199f9c92a9653696a35ce4f5a85f7356cf45bbcbe49c09 false +check_ring_signature 7515e4f67590e701ec716d459fb9da7d1a643a258923a59e6b373e9c38cf15c2 a366182d280c49d70e99f689990f9fbfd7ff355951b6eed6cd838d3acab1b5c5 15 cd1dcd89479454e0de47c7481bf589adbffd7a77c22ad60acdb3939b71ff129c 2eec9abd2ec9143792772073e45355eac712a573e1d51af53fd4cd58bc00b39c 74c3ba2d2bd066b56a62a1eaf3380518eb97d232588c68cf87fdcdb64d612e69 2b8ff545f2f23b891318b1fc0acd48af702238a813e74f868be2478c6e92b469 e83f703887d396f114a5e0877aecba1ac7f725a7b170af5b74f51dcbfcdb6f40 e58c787d4ff2edd4b1b4718bf4e44604aab73c0f77c433a010c1a93ac65124e3 cb6fdc81e92a068965e3270cd90ea45c2a9ab8aee37ae950bc3c8e0aa08a16ae ecdddab98c937b08259955deafad9fd0184e7d93b13037b825487a29cae5fb1c f6e5d7aeae9e69b35fca8c37a9f25368b6c6ef88118374db1ec8e94372f96157 98a2c512bdd41e8adddd9dad2324c3896230bf0499799cae279399bf4bef9253 384e32ebb58b0c1f1fe54dffad46c85d2ad8c5e0eb03ad7cda06c802e10bf751 3db5f4e77f456b54cb851f2df51dd44fb261d491b19e09d672494fa977b3f64d 38a172bbc525ead1a480288c742cd9cace2b2cdb245aa053d415eb003e6a8950 e6ece4672ead0d22ec09d8ab61510dba4175ca557302fcd57d26698f2c378976 c8c17b4e07b3f0800589d4d47f4a343b9663d0a06e6d6bfde98e2583c9f2042b dd897ac1c62a400dfede9dd7dba3fd0d53873db234777664f6f176cd21c63d0465ac9c0e41f3ce13327d7c58505736673f224c922e32a546c6d178d8fcf3c10b869fe7c9eb6d3920d8717cae127a2e9489687ae818e98d8347512b6c44b588076ce672c18b0f0f8e47cd004329243e66cadbbc0cd137920ceae44580eb83a9079691651b567651917074ab0beebad41d9e45fcafc5192d3754c6145186192908baf0e046edb372bd31b6ec927a7934e6902237f244b0006c00a85484edd5c907a1ebe2a5c5fbbe0461dd71135df1970bb7d535364d387249baddb45357c32a02b64cc948d96de17f43bf75c27e08ec5d524d05a278f47cca72e8b0821848ec0699793008730d7abcc77ee67cec484ea8090b025f1090d6d5b2d36b42f81db7096dbb7480e64dcc12c8e470f7c42a55c11025c3925615d256ea5ab7b552a6ca03dec0c34f6cb66e858449f2c92fdedc1144c434fedc37084b9f81dcb994535503c0d5ea3e45411f9d91bc43af65183194883b93cc195fdf5ffe62e2b7363c0a00d52e822408ced26bec9ff782d7bebed5a8e10a24d9295cca4a20a17deacc6d02aa8a248171ff93b42c2ae943139e76bce45b6c13ac3899088d239ba7a3f0ab01ed3802c8ef7fff6b2a086c9e16f788d1674e493009aa3002758d208483359f088add42c6db32f02ec6fc41e2df627ab6200cc1f02708e64cd70d616a16415d0a43329653ca569d7d3afdab01e9602832d2dc1834ec67252d0a2a2e8dacc5ba0a30093b1fa921214cc56ef968844fa78c8b0d491943c3b9c76e1cf8e5702437058343f060537628a5f9d5614df3c7b5235446cd7bfde1e1adeeeecdc688df1f0778ffeb94faa608efa314180f1a1a8632b393bba645dc5adc48245d0c4397fb088e4b7564ddee19cb731c6f708af4c3189cffbc93bc7c4ef2e082394b6d371c0a01b1c2e672fdfce4df878e18a53a0f6ce626eb4e0c09f95025a6880900996e059a70d560faf39f2a947063ff28b61945a5954cf11439eade8fc731400e48480ef57c0e641b554fa67b185051c2104083fb7e8b1808cbb5636126404005fbc205aa08e45676fb373daca06a89534a0771e33be5297e0a5dbcdccde6bcd7ae5d05aa0169fc6a16c30dfb0bcee06b7b15a98ef4e87b814d869cf99a0ab5665dd70b59ae347899704211301c16c05d73fb6294f8c8d731e6378a09e517edf7d6a604199d9dd97c6930bae9d9bf09fc9be551e508f464bbe4329b7c69b8284f515a008a7b1c3107b4fbef940b52fcaada372f064c575ea1725539b481e41202a4920b45e0f88b11c0d3e3a096b6d862942f073383e841cce396e1a5e6cd59c33cc805 true +check_ring_signature bf8ef5f3832b3099ed47218b5a609b6bc6bb5684129eaf2b24eab577b744084b a3aac2ece312ea9854efb438c82b51183b28756a6fd1c46db2d1a688e35bbf6d 2 c73ba7609c29a744b27465e2c1026df7f4fd49db82e14830b23eaccc50527c63 1af3b2fb8e4848760decd7123514ecfd0a3ce63ea2c79fff59ae0b9ae5ac6ed4 ef32d807312bbf0e840355d85bed4369f0614a9bfb3197d2df06fb312911e10a11a2e39f673809a8745c314a104cfa1d93851354dc5e0f65f80e6701c467a2002bd3e3cab2cf5ea65037edc4fa0ff8310fda2361e3e9c0e1482461f92ddbae00aef87fdd767b0b0d212028d2b77a79ad4fb7f0267d156c9793724fdcc5c7a403 false +check_ring_signature 14920743cb2a334b2233ba32885b8087e1ed09e95e9662fc8fa802dac8b5c3c7 d011311f80538c5e3341235c52f01a6567a59f793768155c019e58da32a12ab7 24 9c4f0e8b87fd66f52ba7839c87bfc54e27cc8ffd24ecf0f7e47c1bc35a99ac93 3238306c10023d27b6cf436fab13a008dab91675eab26233ef13035d1f3bbed5 b71f435a6dd72761956293eefd8751b273c0b751022f4d9ddf94e85199b85c30 d2d5e6fc0969b04d2e1b3e608813ca0882406dbce9dfca054c0e8bc561b7466b ee5476e767386654373b6831a8481e2404aef4145f104717a4999f99fcf6fe7e cf32b51ee5587f20b7ab8a1b0ce5726617276bb9336cbae6c27ed4aed40fa650 d75237d69f52abd7e55c2c89300a6d8d9a90c7403802fe18ebfc22e7a648e506 173f493e81b81d99755a7ae9c74cac6aa12f354def0593280c78151f0799ba0a c9e35d322b340b77ab4318aaf09f8c714fc49c54a19669ef9c63cc25eefced52 232cb0f50a30af734515eae0cda5fe9cbe2e5b38acc9ba8ed6633b5810a79902 6f148df8c571fd0d2a201b1c15d051ebc19e40c5e21cc3b5b6d61b57bebc7c9a 4adfaeae325d47610c33f96452dca8e64b8c1ec64608ec3567e3cca4a61db045 2ce9fcf76007a1e71d9f31fa102779cbe06480cb8cbaec8266bfade4447096d7 21d73f51b873f3ecce42f74891d7bff57693b46e97e74773687d8d20eee6c82c d9ed1e96c51f64fa444adc8dda84393b6addc66f948d27cc72d81b8ea58920e0 de82e30abee29b8dee84d2c252f9629809657b07e60ea5eaba126e84357a60c4 8b211f7b7ca23cdf7ea2c16e577ce0bcf0cba05bb17864b51e2d2f324a04f83a 5feb1d3f0dcab556743db6227214f666f9ef642ab149c589edd588a7bba5d54e f061b5f7dc16c5cdf7f7beaa134c070a933bb7405cecaff2359d551043acd747 24268d028d085e0da9462034aa0049fba7b92bf9fb2d42cfef84a896c4ab550f d0de804cae90f3eadebee2b9b9acbb734507ed993aa898ac27d09e43724f4add 21afbad67bab621b2d41d8e59931bf09290447c060ef8c10144d7b234657e217 f9ed64c4ca9879beb4ea4ae675a24c14e5642446153284de5ca2891319cd6ead 0878be8ee4f0a2d82e85b2e93b49a9f44a177c8accd1ebc9336610673a708bed ab1a5538bb75bcb74d35da83895358d9b208758082604e3671aac5f81ec5210832ea6f5eb1d1fdb6205bbdb872de46f6f6c4d05c813b16fc609d1c88d5fcc20e81394df92be75a372deefd70fa9cd406acf85ea5303072844833243168e1010cdff1ddad124740bf5d61977f02cd2cd700be4eb12cc4807a8b05cf04323003013a4bbc35588759ed2ed0ba3f7af80a7e3dc2eb6275d431ddd1568a83c2884e06bc0ad2e34c8e722ebade6a167292ab262b2f3b11929d522e2eea2c1a23798101bc8bd3d65d28f2799d8e3836546d5cd8ba494ea3286ff2a71e535912c6de5608604737f9a8a49d1af3ea0a1236fe3580112f625ae0068e96335d4071bfcf780404699ba6d6ce4970fad89122dfd59fcaf93d66dbe90a56123422b26db7bafd06524dd4384ec382cb525415194b4c269a81b75d700c9aa78800ec61c15594a80f82d9b8bf35afa31e3ed528f947a86f7b4991506593b0cb2e2a4819ab3042700a230be7d99211a5450767a0c8ae3ecf236035847ed9aead4174a9c4f87a06ca0c6e4d9c68c963752bf14b49b0cc5e103805d3ebf1867460ed3efc79dc81b9f10878988073f28a08f558799f0ed41c4e68611046e43d9d7fba48123bc2a80a4c0f08e3407ee7e5d64b625a8c7e6f13cac7a52a6b680a13b55491b2dc41c932cb0e1ffa6de5c1a016704fc9c49b6eeb02d5bb51b65a138237d09ad5301934c46200464721690484801d580848660db26fc8cf52ad8f7d70f90bead6ef282e0856095bbc5e442c5257b0fa839833cecd2509155ecde1dd878845fc3b28adf3234505c0d871eaf196a97b7016148385d7a919a1bfd2ec8239dfdcf2f386468ef6fe060a5621fecaa38382bb1876901a3bf7ce7cf2a3ef681afd7cdf69221e2b39880ddebbac12e6518274068df51fa49bc5e4532e750eac8094df2c46ab5ed4ef86064ef76dc308e61ed4913650398545afe5362b62c1d9cc8c3ce35bc761dd11c40ef2d0d71e00bb89554234e1c582330efb3e5fe3dd3623b50e4b227d543beb2f078c2b2f88a481b7f12998aa6b24ffe22b1697bc332a1693b1f2d38302af291e056cd36b70b4af644bbdd796853b96df88978f216e0544b59c1a5cc66bfb50980e1bd64a7a9a1956f2fe82c2dfc105397bc2e2df8ff182d5f1c93a468cbb78fa00dba57aa36bf8bff888394126b108760a6276f86674f851c73ef69217fa6e6e0ec82df9e9e032666052668b2e28d6abff06558c4364d406671a7610ec7f771d011b78abd0a3f2b3ab2aaf40637388cdf680fa86a88765c2b7972b77734df13e052317ff62137d4acf038b2898dfc741ba073ab7c57a23f51b5c5e7959d1c8bc0d73fa92e40cfe82da39eb191b36e8588ddef4e363ebf819c3b1d91004c70c4d0e748af2e92227c75538cc31bc91e39642096ef101117efd601ea5f4d470bef101a019b7ce7a7ceb683bd102d6e6321a74d87be8b836320e9294b2e437c69f6409fe2a692f088b4518503637039d0c30eff725579efffa7f83b057f5a9cba1fb0ffee4a124440df36abfa1aca2d3d8b34bcd577de43ac5311062abcb8d3cda790365d4532f97a27d2ba43406b790a049cd6e572266b49abaed11f8b14c60e4a50ab163f9eaa419fb9cd28ca33056a67d7eb800f87f36fb569b17a6f3cb13f7bb0cc4a852d18423fd4b83832276766ae65467c4ff43ea340b9701f730d69d520c0f24bd7eb83b7859446d31fa641d588f9538b553bd79b67d56abfc57f2ff957b01eb297edc669debaf31d9308fa59381fcbc67ec582efd72899cc4a84c3f777d0ee232d909aa18e4a45dd9b3331fc1c68b916b517bf0c6bc827bf626e272f5160032cfbff3a505111acc9b58fb0e98dead4568e4c0f31a59ebc2a5d3fb0c52b10ddcf05f034bb1b4d9d498b83a7e98b7fb70219014a252d0cd630b1029996bee075a0cf99d140e7502c9bd71374a3a703e4425fb1e0fc74ce219d3ef58d73f5501a9a2471babeb65939fd0b38f8a7f567cc86c07228c1b6dcaf7adcc816e92bb000b7ad8ab956500d74d24322859ce0ef7f4e7132b45979a1f6662742da7a3b902ae2cdbf4b784ceb4fdf22a544fbea97e0a28d77075e822a8553dd6491d252c0c483aa723a17de5817489d5d52c9eab4b6a9e710d8ba94b1399b2771f09c43f0c false +check_ring_signature 80c9f4a067cfb522e5259fe6dec61865266527770850f89762e66c2a3e228bbc 9d142637676aa0e721ca6d9a032739a3f5a96c7fb2b2e6678b60d625a4069685 100 13072b91105973b65e17d5992901f72dddf84c594613718b633125ab100ab912 1fef13d8ef847ee9b1803a0a9c68f330eeb2975ac45fa42faf132f3c4e95f2b4 14d042d1dfe9b208a010854778a0303914afebae60f4f928be2373c3082da408 bfd457847471174f545caaa1624c6a1f9ec6d3f7471a867f4e7e4d58801a9aec db40b68afb611af88673ab1bf424d842824867109fb14e98da204c797bf3dbb0 88ef98f3ed23adc73518ae92c5bf7288a82ce31b42844044235c2c9510d9525d 10c1e54a43b8be9209b31bd7199ae5004ae16a5ca8219e07bea959eef80cb939 454953bb4523126bfe9802950345716c0a3c6534c9229a65518d310c95a923f7 cd9079096adc49f91d4e71188d7cfcf222578e0c9d1eef51c35518f6f3b23d44 8a07b002ae3b5b6501c4ef6c4166425306f30f748a3d8e48ac2a7aa43f428a96 1392b5d20a0e5ac9f5080743ef41c13ac485ea42eafa1c332993998c446db073 9fac2a1e5793bf8539d058dbae4c6a81301881b14f9a6efdb841c4ff6f1a5819 dfd9b9c3ed3e87b81b290c4f80245fabd44aeb64ced13e000cb4fa2c6363708a 57d9e6db1fd23d59b3f6c896b034ed99ef4582641d9f87551e9d627c4ce98348 7f633ca6748ca81a3368ce0edec034003bb1616db3ffe918e7c932d90ef000ea b253fbe166214c824e07dc0f4ab66e2b0686c348ef623bdde789b47a5ece8698 549650ad4e8499a664c3f0fcfe6dbe9129e179dc8ac5de4c09dab429f6efad1e d87c69f1d3caf6becd078fa63bbde889b9ebfd62e82b3d10bc81ef043e8c3706 c0d3ee9be55f038fac146b226dc19fa7c535f1c9051639e179be322d59dfc621 f1f005085f16a7bc39c79d551bebfe4436e7efcf8b00208f9b6105b3a1f81cab 7466f95440fa4d57ecc6326174de252bd9640c9a2e40bcadca8b145d8e08776d 934056d115268d652f5026e7db449dc9c6abece60307f0a451aaadbb96cc6e99 8fb216ecf04eb46d66860f310f57d158325516a313d50936b525ecdf68f7fa63 0854b5f5f91175fa8e3d0447da6ed07c9ce801d8eeadd976335965e5d7757930 0c6e9b360038de5c2c82b3d3f7fdc340bd1126c3d2a4b97c27577059c38637e5 7ff0ef353a751ce64f4a7eba0f98c145803b34fcfd83ac523e616cbe2aeee92f 973aa590a12475542efbc2709924da5b59b9a0866cb312bb9d07d7d95ee0a044 7d2dd1de4b09282de8a919d93284c91b49f7b154c2370494d2166301fc76f6bd 8a34d2dfdb38b2ede594f9e65b43dabfaf83d211c0fdfaaafaf926d3ebf1d021 676034dae4d52808fae6b3a0d1b9d2f752b279a7eaa04b34386f1f0ef235da33 347b649f1602ce99dd93f3aec82983b81b474dc52bfe03aa7d5b0b940aecdf9b 168651e6da2e40bf3737d3a405785f3bd867d03d847403db396fca15cbfc49a4 76d90d3ad80891de808d02500f316f59c70a1231327fca69f84278aaeb29d193 a6051f5acdc7303b54e5b13a6dffff514cf6867079749c0d80251081df37462d b3dfc849a640bc84e013bf0d6e922ae6cf0cbbfb63020c54201f1310f23783f4 4cf89d19a7e23d14f6e47ca7faa46b1e998e5bfd7cb6ea230afc0600fc3ed581 93eb942a27c7280065a5a4defb72fca4a01ddaf27a5489bd939c67b3bbf3ad9f 7dea324b92c084cb383ab500cc1746aeebe198e190ef4263f5b9c97746d8a2e7 829664469b7dd9e3cf913cb856cfec9c326d90671dc774c693495ee8c1169e08 0345a63c12fa17a087a0ce2560d577b86527b9677234b65d542a9dfa790fa6e6 56c343caf86013e0e2731d01e2753e6b975114cdbeefaa97048728b6c1007e27 035f3dd7e1c7a74751c2f7df4203c0164eebed2eb75f177d9f5384f9eba54084 8315cd6ef0a7522c4409cd64d48f7f9b644ede0925fc114e51723658a43283e2 46bb8268f3804d8b3cc33ea0bf20c66da80b16726d17dc296fbd77010d7d0080 c23890887e010a6a719612c6dde5bac22d59816f77f4489df7c8c526ca2914f4 a903510b0164f1c27f05081abd4538f91b8159b0c6ed5907657987f01addba49 9ba322e98ba21c7627ac915da780471a27084d998afc35b92bc5e55ef4ac7fa3 275b3a7db1d66319faa1cd7698de2b7003ecf257f39c0d1cd1c33631fb818de7 f1e8d12b1a517f9cbeb4dc373edf2bedd7ff674e7aa1109c4342c990e5f9ff7d f88eac43ea5dfc7da41129f4ea1e4526603facdf041c9b2ef260c4dfd459436b fdf8b7443b35a6cde995895e5ae2995725b6a54db3a47baafdeb2d48746767b0 6beb42d99973335b048ead04517199a79275eff4676ee7095be62af228344fd5 c98c9adf6dc376d6d7167f653ec943965f64a7ed91522cd87ae88f03c3f9f105 69b9119ca9437b733f31f18dd2ed413d94eaddad370ef01ad403ba77393a16e8 083a1cbf067a781ea5641bca0d395b61b6f05ead92ca5f328b5af392f84705fb 1f67ea6c637933c68394bfc5b2f465ba873634c7742d62a22a3e7d32d05bf7f5 afbb4e6bd95518b45ae2e64700729088cee07c38c5908d7a1102de3b91c99b83 d2dcbd869a151af00b16a8ea1c29c3d37b5956404aaade1fa962f681224f80f6 9fed637d41a4b39e11ade1da1b92df2415963e9d2fe056a1cad577b32f960c53 7e307dfe247134e395f3f0ee92533b16594d60b01200b427719c977b26ccd726 3da853f0a9977fae19adb8695e44549c7f121f7c25a33df7b453170632ea0c35 97acd6bdb3dd461b5517b4fa95d0b7537af7039f4c6ae4f9bf28e5d1b7440e24 fe38ffbc2ba03b145ec7a01cebb1cfa09913c9ed08ad2af86736343a6de76982 adef4a75c381e5712164a74dc0b86b59348fae4e7fa577548639d7dd3dfbc3e8 5ea9f09831502b48fbea84bea70a4ea6bc54b021460c07aae2bfce5c23273cc0 171b379ff68d1eed9fab4ee4c3be210e6888347878ccdace19afd98ef40ec9fb f733caf9be3db77e4d8a05e032798ae8247f5009ccc4f02c2359e25e63bace03 a4d24c801529a9c64308911b26cf81727deb9f3e0093714c7a8861dc72bbffda fa47f0d39dd4a73229879ab4043deb708e58b4c9d01508723b356ba2c9c80b66 80873ddaba3d9b9e614e1608b5e1a58616c44acd5467fe819758eb0fc0f669a4 c7c49d80933e8056bc8f46d4bf84ac2db51faaae5add0a720c635991c71d9925 cf610e8ce43be4580dcfec64fc5abf12cda248023e47abeef6cffcc7286ac286 74489faacba0ead8f6ae5e32b40dd159623f61055a96699d6e189d305ace500a e7d319c0865d8fbf9f0cc4e79f63e23a38863c86bedc1244d68bdd5ff547c5b4 f74d05a0f8734c109c0019b1d0664d000d26b2d68f99e6047cc11b566bfdcf60 096c119f12c9654651586527c316ee215bf8dffb0a0c2b040a78b4af4eeed9af f64c262eb953cc514f7891c4c1b4a191f179f961710c01611855b16b69bc6f6f d879c7cfbd83093d89e2766d9d29dae11ac9a4f1bd77ad06cfa532a8da3ef68e 3019d420a18dfa67b04c0f24534c84632a2df9cace0e8c3169e8e1226e274d3c d52fa4cbd2f2ec7c849b4cca40b2939f4ccdb839abfa95a75d054f44bf2d9bdd f90abe7aed3cb3507d44f9cbc8bfa55e910543b057e872a5c598b5c50c8c7aee a67ceaf95533868c4df35ac5d9ccb5ccca89e5deb97c4ec31fc2769c3b7f99b3 ab32ef226524021f0c97f6d0980c763c5f90b827e290459b2d682a1665c210c6 4676e739094237f6eca938d2c99da87654a23ef8534ac5e2488f822978563ec9 1d89c48c47b054d389bede38e0641f8733ecc3ac4242ab23cf0ab3258c12eab3 a1a82421859424e339453a48be132710ad0cc8c28aad9f5bbe28879a12262455 e63908281d58cfa5e31b0f7c7816b77fd2ed386dfc44faf08a390a1bfd6dea59 4e21ae0c033839c094adb70084514fc47e48ff7ddf66c8283deb92a351aa0111 2befae6ca618d7c80d0c4fd521a25ca4573b9706f86662f26250ca270ded7949 33356830ab403ee709ebe7601c738cea9787186d94a0585ee93cf64b1747523c e900773bc049c6298a1579db30ce7c06afebb05a6130663004666465bd768075 6f8164a32e778d5b2e74a7537bb364cdf1f5cf10e5ef64e876f18309317f917d cf228dc4d9994f8113aa3f124f7ce749b4fa98f780bfeea1b5314e2d26e9c505 c73c83dada84e01a80ecf13d23f6c535e277f6be0effeb9ff22dd928dad472f6 37e5f5657e3b1506fecf77b4565e1124854e0edd1dd858df45470fd064e29835 9310583cd7fda737aacf3e255ae39ad6f1830fc8bcd147d48abc8ed900af4a7d b89926c52cc01454a356daa348bb3d2c68527db693c963fa51c57e581aa5baae 01dcdf9bd5e691c229eff1b12250cc57e324967e580c2f3815f458b5beae9b70 eab1dbbe1a2ae555f09ec9152e00a0ad382c89b4ab2b6e7da64773e34c349144 66d8c506469e3229a1cf56283ca058f4de32ba41851182b0051964e6f884347b 88a86247854022f69dee09410a04e910b23960e6579f9234f4144c37e814c00ed4a08f6095da2c5ab661b56605df6597c2dd7be0b51298b4132134bebb80dc03ff40be2782ddd810ee6d8a01d956be6aae42b5f4c000e1992a0f387304bff9005eac2513f9c1def4d84defcc0851291397971077cb50e5aa84fb7354fe88fb0230d1a72bca18a260998214306f2b7cf582b4a20555c149cfaffdfd2e1531b605ddf916808074efd92252c5e5d28ea5333fefc87c32e500daf392fb2c839bf403f9d36a4c85d8778b02c3274026104a4c0f3817d8aa40b54fc65b4c9163d1ac06665032da21ffd054a88f5a3c4062fca27a7f0d2692472276cc0960698d79de04ce3391c68d9bfaa0f75e099fb15db3b5741e1fc8cfe2dd26dd40b2b01dabec0d42f70ca3347c96962505bb7c5771c44bb1d68ed42df03225b77e9431b0308b0d5a0f21fb206fd2677bdfab7bbec7b7996226de7f67b87ebe4bbd68b41e98170f5733f534e3edb7dd7418c5a83924616f1c6ba662a90b40058b6d0c822eeb01011bb026cef65b3d0f1a28726006a1f7e708f5f51307363fa785c4ef3f24f3d8048d820ee3f5a9d319cb53ea0c6c217527bfb84c549135b832aac61761185d7509e7fbf24e43d10d5f93a86a04469616c75b0db05c56bfef823c566ba932196104e1b52d7ec2713ab0779be49fec55d35848b734e624b57a2f549b9124f43b680ac44db0a161045ca655966b82cd823cbebed8287be4ce486450324d7993992108fa8da8b86c6ad0b243208a40a8b918421adcef491dad2ee62ccbbddd92da6e04336116f81ad8775087f6ee241cc4cd27d0d9b95bf8c1b0a8d746fd657bc17604ddac7e3873cf4b6bddfa73f0b6274a51addddd75c58dce7dec9a4c3cd012c40d2fe18649775a9fb09929a4924a2607f70fc65f53bfd1a3d8ae3bd1aeae212d0a084cb3006d91ce5089e8b77d946ef164b20e2080db0df0783d3763e23f23980e3bc17e70495fc3548b49b8c237a10ef3733619d96068fb566e12b29bfa3e690bbb4f8ee5e2ae3b66ed7a9392633bce2c8744dd9aa2cfb3adba4889264f718602675ae25fcdeab17985c3443066bc5afefcd98e816f7fa479702a219d9e6631061cb359cd3debd43fb50fe779fa1083ff9cc7268b7c0fe3742ab6838b3f77260be2368fb0e60dbc165ac25b3a49f21095397fabfbb2205af6a20add5616a55d0dc0ed96530ebdd5b3e21c68b8325f58183b198edfbed2217a9320118901b3c808ee476c1d0717632120ee87a2277a9dfa700e63aca23239afe8fab199ea9c1902db51b39fce2b5d65d434bd610f114691b0b06d044881016bb45ef058344efb0eabcf235b9171eddc4c02309866434040c31620fbc0c8d7cf39386ee5f708f304d0f3a2837582ae834acc9dc83d7e2d5baa3da06146ef43a92271df4a2810a401e9daaf58ad05e0b892a230d2c8fce98840f25b7b6a1092f7f1aec3899190380930f2926bdacce5e58cf52cee61ae55a8770576560e1e70b2ab3db0a4eed77904ed197096489aedaa12a64d66c16cbd11e3476b6440cd7d2dc5d13e99a9b0de027812d32223b921dcbde548816cac4c820eda0dda18f212b7e4e7789c302cd10820d0349f621a394fa647853f3aee9c9672fa598caaca68438c08ec33fa3d6309517868f3cc9063407cce58f227169baf6e9436cabf51535a8459b9d4a99f3c05475e8a72aee28a124f3ad925234d37837b672215f20dead33aacafe533e7c8013861cba00faad5172cec87dcd29a536a63dcc5a2d0d79164b2375d04f53e46079e0261d4f5c5ad7443e13203cac446d2fa0e68cb08536fe89e1dfb7081bf3c02c5b1595233e8abf1ad47386ae3e51a4e3d55aaac1f7b73f5e7892bea4df5d60ba0cb3ae1c6477e43745bfec2685c2bc918d75bca2ed983eb340b768fb0b534065c2cce471cfa6346d08698d9749b8b47c84cd9578992b0c7e35a1894e726130ba141b56bda043729ffd276de57592303e749f6d24ccf2276222b111cccf6e209f09c7d8d65a43ff3d06aac6d9e01c54b09e653190caea2849eb9546797ab4b0d4e1b8237dcd6aafdff36b0fe646e4bfd4accea517435524cee75f0b03ef96a06bd481734178fbcad44066d4d1e0b2a9ac118448c0d214e74886178c7557041066bd2ac41077578122051c76468edca064aac95d3a019fa70ed086932d5152b0cda314abe7c008f9d731f3e73666300111076c38a99f86c8e8df4a043a1eae60a28aa2bd4311b48de49b7c4bbd67fee41a4be0e3c1b51371e29f43b89e6aac208af705e265c854f149af18210419a8a3dabcd2a248d2338cb5414c71ef963583bdf9ae7589e388eac3032352f10d623f768800de3920d05b2ab02a7da0445a105c100a0a4a9e1951948a9ebcfd316f54109a45b4d012b69099f67704843ff0a0ee809980ba997dccfe40a76ea3e5c3ac237d4dbf1a46b20bf85268b762dbe010f721f83d06cca8e06cdb5af9930df4d0b91974f3b0fb07872e0d47f0126096009df2af36736e553e4be100908144fa7419c7c0371b0a6c8a1e9ebef04f57054056c306af98066b066f7b9a2c77448c2ecce9a7eab53ac601b39939c0b47e7b009b6c9842fd2559eff07cd300e191900543fcf79d56265e9174569e2b4650c1909542d6ce7d0f78ea15e9e1a0fdb14e223b7c29de992746a42db0f5b0913c986049cb31c87070842e221d0f31f51408228269271e9cc1ef2f6cfeeed3194fb21075411f8142f8a43123a4da96096319a0ee15a07af8f916f768fcd98355f41b40ee438c3cc6dcd973c31514527386c1966003464cf5b7686e4da9288781a5b1808be75fe8f96e027c21979803361ec2b3113192cd29118bf30361947c7329b1e094e0b7a41e5d9d7ea31797369339284ed02cc389ead396f6806e8e99e8c51630d294fbf0251566157ece929aac8959c1bc91d6dc981364a19e3d7d88508f16d08dffd1c63dd6e08bf85edccea9eee1c1df7a460f616037fc0b78d9a609d1b3c0e2e506c32a6ac03c08299f5db6a86f8e46eca75396a5990fb2f15b8bdb961650a74b58451b5020d7ac512e2b2cab58ee43a9586cd91b0c122d6be655e8a3d080bf9c7c0762d4f6e3e0bef355735a94721e22cc0876dbd3be7e5b76c8747d62d0b04860df0479926c16098fef11eefa99b354b8208654c986ecc424b316c04250a5bd4da807682d791c52206bc38d4bf59268d39cf4f5b3cb2eefbe12a02ac160f92e451d312235a9566e991ebc72d7691406aa8697a37468566c80c5f66ea530dba1e53f4c08819d6b2227c2b775a8247491b356026e77a98e1cc24c030757f0a26627a63cab72998376970aaae9ca2e4f38e26ba923580a18cb00f53d00c060527299c1102ef1c349c6caa3dc494cab3e9b61ec788c5f4a85346f161fb660f09b3cba85d47319a98563343e1c62bef6a9ae478d205c644f1c6fc625fbb8fbf0ccea5bacc10ce880e24ceeb093443e826d54af1d2bd5ee3397acad8c4c4b86903dd9e1b675f3f322c7cbc5e7e30c9104625f3a8fff41940869927bf6c36eb450ee2ddb9d2deed5a7b513916f16af893f34ad92d9fcb6542fa2ec8ae585b32a906fb628b425f39f3a502ebb910c632f369ffc6f6eeba967606eaf101873eaad80f042fc65405145c383e838d679adf38e0b226f219118c15e6dfe884cd67e7c20c76eabfb8faf17cc867e80539464375d8ec2fd4faa92e7f14cb3a56cd04651f0beb9d4583b5816fb32a40270253c5a1bbc6ed1c5233270a8f975e79541631e60861a38538289fe9155479166c2903582b58c591e5fb86fd0ec5c1c7796620bf024928f7d386eaff7baa783ae44bea71297695c80e7f84c39f9d132dc807a8190b24485f8217ac751ad6111f07139b7f1e8e865ba6e7b461f4cfa2ee2c0bd03905cac85b7c9873f1f5f338d333136459a53f2b8c122e4629349f5b5d01f6cf670d810f45cdfd9ddde89b315289200b61b6631db2f1bdeae8c453b843b34e02810cff5230cd84d632524b0e8f3e5a8a16322249bbedafedc203eb14bc4f77095602730e3e15e3e7bb30cde71bc4e375fab98b8c95d4c4cac864a5d69a1c440bde0b581921a61d7dd802dd8e2b6abbd591e69b3b6e518f8b0e401ffad8d05921de0d08edd54a441e6fb958ed47b0b9115138571817340c8dba11a4424016424bbb0f1843840156ea6d14e25be6e4e83012e3ce575236642ebea6d176769aeaa1ba0e957bec42b0d6226dc9eb2c89612ed2cd5c6384ffa51d74236e21e35f0bc6ba056982bf4c59454fe5afe1ad4a5c123759481249c55d383f283e7fcac8cd38ab02fe59ab242ad935286ecdd5c0b3eb685d6a18417b32838e1ef483a58f8b27e40d15a4f0a4019f521bd42c938ebbc079b3a851692ab32b913cd50b20e7689f610d63b9dd516e3b932d48597fe2a4983df200706596b6a3a1dce0ea95d1e2574a080f7633f7b50521fe992df10174e0886a73f8ddcc7bf98140a5efff70c8ed400ffd66301e0c59aa4d5f48600996e6474a32e9dcacd1b32ef774f2e819831d47061dfe174bf04ce0960340ebdac37b07475a29a69ae47f316afbf9ecf1dce7360e7bf024595d3e8cf3ce95ebadb75a01d9c63bd5247a0afe330f0af3b408d12d09247667303410e1c62bd56895f22a63e42921dc0e51a593160723eaff737fb60b0573f7a836b4f63453dfbec65f534f26e5d60921144e81c59b65918b1f80b308615cfcffa166559be8782e22fef479f28b6ffadc06b4333257ca6a263830fc0e4fac03208cb251608a41ea3262298da4e2e6ccbd49f6fb28b0c2aee245ceeb00bf75155df131fb48a1183fcc592b3d6057ad72f2e2c68741e255912c49cec606df33a625013f183248b15705a4554f7418d8fbcb8fe84bb00e9aa56d5034f60308f6fdaf147c004341a69b79b1447b25b1474b7cfef1e62b616cd5380097bf0c54252433cb2ab14bb436e70d7f9d6fd4867ed7345db19075207b904f0e930b0b3aa51b0569465c6b1590f0a02c50659475f95070773234f6aa28778780f71e0a0cde80befb5011086d68e58041bc9130a848ea096357ac002e7b2e00cf347d06a464dab76e3194462183d3254a29290d6d8ee60ae7767c7027f9e9ac98a5bb07a31a6b1f76c9f55952f4035eff0839aff27f4440ba0882a279bab1608ff3b8054b888fbe3140f23b53be1fa5236317166c2c7ca6b599547543bcf0e4faf6300fdd4d323cbf0397eb07680c763dc7ba0036800a8f78cd6195aef80c5bfa890904809c29185b8f4feb6a1573cdaaa49ba4efcf7ed3219d26f889d694a8283aea01df36a4e1c52c0797a4409d101b85bd25de5f3a3612e7c926e0ae5efccd9144078f9694de3bb2627842b843efa3d1ef17e21221879c8feeeda233221bf7c5420cb7a530591935bfb6aec79a1fcde3c127cc6ca5de5a76589cd9c6298c1ef3fa081a08ddc1dd5370a1929dcfc825cca0e7bb7d242a073764c3d4a1d149242902088c36b4be69efbf9e2848e15ca2011f32135b76708091db83b06746630b6a960c39349496e057ecb4dc638d772d4923139383ef990f9e59bd51ef726a22bb2508f1a7a44609e606a89099fab7a20aca8633b7dcad03245ec0860870e4bb13760b4f821f546b7b57661986c2c568b54c0067e71a758e272311631c42573c951c07acf9fa6e20806f02a7b278eeb2f784a5cfdfaff86e2677725908c217acb5140e7e53b3a4d28d63ad141e8306d937e1945c888d541caabe9caf87f288940fd1084ba423bfa55e73a30f8eddd2ac268e8145e447e5906309d17aab73955265aa0c2441bf705bb379ff99466e7921be45c22f6c0a1fcc664846adf0356a24d9440a02aeca46c31db9b7aa6553e2195192bd6217d154096f0850f2cd6150a0b1ea03d9d975f96af63b3c28f4620a345cee9101089cb2f5dbce990f9171513f214b02584e62af1b84bacd3a553147569040a9ac8d9a0a2874fabd34e7e424ec3a2d0db773121e76d7dc5f0b21f2269ed401a17b2a71020cb5e1e4e43b1c07348c34039a060b28efd946474de509638c2604e6f8a7ba28b3c863210d0195e740c28a0d0cb519df7d4d2823e27e18e5d212698d7a2e103cd727795f181e8bbd4523c90e73d6aea650a29bfb8a9a88199999f1cb2b44f682daa53ee4096d466bf3280e0c9ac6ceb0540a9045eaee9ea2cf0d6f3ef75b91cdcfb35d52cfb1e8a37d1ef00453dc7c39bf6c92d59fcbd88fb43180d125bc8d26090ef899d4f1743ff8a40e03447d6b1bb37680c0db4a17f0a4f5867c0a84dddb7143ada28ee6621592aba90bb921babc7381e7f3208d14d9ca9bd430c6b9d5f6a5b898a45e579da92ca8c9053c052f5752584bbfe88411042027a327c7e276584481e68e5c365c0d0d0c240b7d25e791015a7e4d81d591ab8b16ccee3ec363bcfad3182a4856fe826d5a7408d5127e7f568109bd0a646050a831b7bfb301a2eb6126058689d3914055168704bb860b2613810931eafa17058d9ace05f2b0413f5cd8217bc4b90ad70763ec01dadd0ddc8d2cecaf594edc928dbe0c2772eb0f7b15406cc490097fcad17934007901b86daf7145c0db4f1fe610a276989c2b2070af2299d2a359b103800c51080b2fa9bb8a6f85177b04661e8bef72c73e24881f9effbabe39f1af4ebf6355060a5396e51217761a063a60815e57e963540afb93b40da7846ed39869d30cdf056e0be25b065b6d1e6d5dc8895f0dc9ad6e86b24924bceeb30e200c93ba44100e56219bea55f7cbce53dd9b8b16d9213d49f0db942378f466d2c144ce52678b0f0133506ecde553bdef2021db0c33f5556e204f816c4377b9c0b4b129bb132309ddc6b19ca8864816f405e592eb31534c9bed347fb2e1afeeb968b07e32f84106cede7a1f6b364bbdcfad24ea1a9c9f7deb717b80df40e8b7151e333b49ece405e100df3fb701a210d5c4af3fe1dad80b7c6620b3d1b811e4ac75a86ac2d7150c1b8794a4041a68814fda0ce0c0d77c5ef6ac388f5499b5bb8738179eccdaff0b2979ef7e7d2da5ed20ce298244ec29cde21db09b2d4c966527a649e307659108cfc80bbddf36214c05d82f6fe2ecb3879b9404f9b3c531ac59f1f6769d35b90d1621613864a341b4ba1e0d89b5df7bd0a69f1a2225a3d0cfdc4f478dad9ecf0637ab38ec7f9d68c6f3d7315b80e7cf2a8c146c73d7e974013894c29ba422a80a42d2db9b00f19d052bae235efa27492ede2f028a8bfa2d0f31fba50d52ee530349ef34ecbcf732743f95fbef4461bc3bd2ace6097331b3ef763c2dfbd322060f480c6c22b63512c3ba7c4b59126abe18d0775fe0fe56d24a2b3f7a3487187f0a08fe68ea7de512b7e134145b2b572f4b6cee17c05ad1652c07388a2aa8b7fa0e460aaab5c665914209255e02fb63b919a1a0b2814ee8320283e32e8c8cf9a303a2426392333d537b01517827afbda5baf01128f19756a2aa76946276b1d5ce01c7ef04971675d8067d7b0c9b2f9d0cf7f86085a3025d01daebf834abf57de90c523c939b6d47b69ebacfe6faa9f9bf3a302f6ff100c0351600126ff5b3f91602549964cf2a248908d1e9a119c973954833c8a9f730a32a5c67d018a0aa52be0b3f91f116e974042629183570d97245be8ea3222b648eea8881145fd85fb2a606ce16d9e94ad6aa9080b8aa2fb1078f94588acf6852dc6c1a2a884789db358b0de3d5c79a249c4a06aba6e7aa18673225c318da8d9850aa095848952d17f7ef0d995ccf3bd73eb6b37063de0296c07c84d6af6e7cada2370e4ceea69112323b05af0a53274eff6582c93e8c198d96cfb885aef41a3e84d39b584e85089cff4e0cd2b54e6e79f7b2fbefb952cd8cccc3f21f5ed5c650e690987b6bc7fabb459c013a44dcca42a8995a7a4c253e5e07156be04f736d30d19d640d1d786ad50ba80fe0a3bd8d493e7b4b43d929f4a7a7fc2fb6a2b9dd8aecfe0679f2e6426cbc14041a84802639e0ccdf1a2d7e6d43bd4af4a81f049f8724cf8ef1a922d4c2db64017624be73296ccb3bedab252ef531edb53f12d59a7e9e34e56ed141a362c449072cb503125981599327ac7d5eab6619df0ebfb40ce191f58291d97c453e046f0ef702b413e00d50142901a1406d914445920b337f117bc94e1f89009b42a8da0dc964c674317fd34d7acdbc98fce6486ea0c13646a1826a0ba77c49c4dd4a400831b441f70e3f10f42ccc8b3cab9f4a4e6dca3a320e6e664d20b41f329809100077a2f82025a0fb02abc5ff0054cb89526ec4aebc79bce9bc794d3cf51b04920ef51386a4a7d3cce60522de0e8c548f6e3ea28fb08edc6ff5f9c7920747b05700a4d55393b9914638d66310cd20df7919a72b58a9103a7e664745aab3f0ebde0f02a56a4bab6da0bcbabf4f10df7802f4b66abdcf79b59c5532511c1554f3a3067400ea96e5a232f343517a7aa5fbb6e88add265f0a944c8e92d7a4c035dd07019a3e2b2b41fa88255bc236a667b32a9d0274054900eec7961272e99a3aa772069ccfa27c987d95aaa8ac452c0a847413284d25a3f33f3325126657a0d9b0c808527afa33f485f37bb432511979e0c0731a26d863c5a603b7ac0bf4ec886a360c1cb266aa7144565067106682e26f22b7e0db6f464b7d2e1dafc098140dfbdb0dc86570a918dd6cd36ae8fe42a8ce4d3310dce6801e4df31e01fd7c9af577a603d4c38d64b9f2c5ab6746c9a25c4f7e79560630f7c776c51b116be9712a3a77030adb2a326536884ee5a7e10bf814d467bcf447517efca6818ae5343f10a5b806e5d2a528fa331d9cdf322cff9023173d0eb5bdf0eceba5d6559a1be80e5bd302677f37b055b75d0b5f771ead3cdc6ace281aee3eab0ad4b6cad1a5734f9958051875e6962c4f22c79bd8beffc908c549efcb43c0f50871b183752f6ee607bb0b79dcb0762d473d3a3bf0e7329d283db8771b545387d59e2c7b17e23435908d098c8bf4dfc15fff8b3cb0d975bdfd3dd9ae6a1c8acd2a63c0a4843787a8c0120e false +check_ring_signature 4bd5d1a4069a4cd4c9c23275afe54dfbc9c7c6363b67dffc5116365501ac8b2a 496c6b699d35fe77533dfaf66021383408d5a86933e1ba802aaae3d60f20dcae 1 8469b0ba9c132de04dace7b86203b18c235203d97c5fab721de267abb08e8bb6 2131d35649e35d49bb592ba7847f9863217e78c46f4411e2d13d25330c967e077049dae66c738fa814d85165a9361b1ddef153e050b186d8e942796fa7122203 true +check_ring_signature abe990a64987a9351ab9f5e634453cda7c18cb601bc557ec444aae00974519c1 6207fec918a02039841354a0ef20b9e887481d6bfc7152992929b03dbbed5293 64 1e693812fb97e15e665ef4718a55c9b533a0ef2a8614d8b8ce0621455dd665c9 c51b423ee4814ad4b3172501d9ad2b331c2fd9374319120aa8b48905a00976df c918981adbfc6f5f709e314c46ed0f0203c5cd34206460d348a222ba60272fc4 579fc7cfb77886d2912f469387a60fa4145d318c43f9a212a74580e4a51be910 2bf21a51b034606b720bb538239959cb853d19b53bc8df967369f6c3004d55b7 faa6966f6d57a98ee371e402b83933178c4a881af8a8a6d0d8a16706986923e7 16889a61a2b477af9018a66d6ecb9677f6a32bb766f9a80ba64455c044688382 abc5e425f926fc84b3ee73dd4ed892dad077fae0d807b4b37556ef7586f6cb7a 54d2b7ff0142f4d6d2c3b9c524e8676acfbf5388fd8c3de7e43ae72c79428c54 c92ee2171d04609846f62e17cff8a7bba1534fb8c030f1abd43c9741ee68e9d6 62ed7773e6f19ebb5881de1352f31ce068b04eba66371843a63907632d3dd19f 22f8b30f452dd2a735a1a58ccafb60441151fd3abef00f5502e364e5b82cb570 fa76124d9979b9750902e999c993206f8133421b0698da91ea5c075e78d25c1a e1dafb228cbb183374ca8cb49b45ea1262cd1c7c62c19a3c31f0f086b30f13f8 8a1a75cca8d05117003ca805ef894b32d7f077bd7a25bfa1d42b66024e5109bd fb16ddb846654913f5634d6287c90b8b54b2dc2e42d7d7e98f6d9106fdeda008 d73f626b2d92d6f202f52dd3b404918994994b0d58420c816b3d38c189c4b709 4c0db97ba98dc75a3a067b55d433d91efdd398c1552dd653061008924bf21634 b38dfe10c627131e9247ae0aade309c53dd261e206a3cbc72ea3bcebd40227d3 25ec77f20d97319f7794b599e074f7a7ef30005db21ff63b31a10b29e31c4b6f 634957ce765d5fa26d704db18410ba7f121c43b28ceca8c756aebed50220af1c 1cf82854bacaa3b9b0ec42c9d7dfb925f2cc70ba8ef5615878d3d3554cc2019a 6571db7f15c06f9c14dc36d7fa46cd7e74358cfba124084c66c3863eb6869e5e 5ae60d8764e74ddd7630fb5ee84c9ae5e2993873849566aa2691259f37e4bf67 b7dbddd903464b39225e6f90343ab23ccaa6907361730744bcb4f508fbd65e1a 12bb59ee025cac298482ccea09208ee147070fc8c43e841064d7693c1a61d911 cd489451d831d134d6db240184168c6d7d5ab3df183c2c23f16dbf6962259a72 82d45ab20d3f3e57473b03e3973c9c3496f1a9e22aaae220565ac3d9667131d2 71a1b21963550d0743a553017fc4a47d00bd8d4518c35b7ce0519b4ee998cbc4 d1deb6761af3f05ad30215416d423029b574c9ef3ce479fb97fa5f1955cce88c 2e3870b3ce621344903c73349e6b466a058a94f9db2f88b46a23b9f48971e685 6e9d8ed68dba14f68127a03c0f60d3e013218c70b697b90a36eea67ecc6ce041 18bf95b7ed67198dd65bd31610b0b0f7604c64e2f2bae15769f6117f0ac42465 1c3f4de2dbae6bc1908bf7459e928d8412e5dd11700d604c0b2e041235cb0911 06566f53ee8d8ad009a2bdda1a029c5a4f3f7ae00b9998f9184d041984a9f4b8 5724b02ece1057dc092123cdfd032d1cd8d55f7a6f3be0ccaf64ad11e3be2286 375efc0e9dab63297389b3593b49191753761874346380fdc6dbd03ec28f7c50 5708ae2c684a8e4c619bd1ad57c41fd96f34bcd3544bbd491c4d2d8379a199af edf7b66326ac2396540801b987ec2592ae99579045edcdcee733ebe183fb90c9 1ee55428ee3e7f23a0224524c9b59a388918a6b36a5b64ce6f34021860de831b 3786a701c1461cb8f050d65a0d080229bcfad7576479173f6e6437ec2ee18aa0 62244939f88eb3e4842607fae8ded2454e152cf136cc116eadf4dff59ee247b5 884f5b037b45fb3399ed57fe3bb6fa47242983d47d00ff62b443cfe12ca5f47c 7f0401921ebb99f9f30fdc30af31d78b5649ba5e6516507c36128fa3d6b58382 ebf0da172233686308fbe1665067dcbc3561b1923ecaa245a52e66fcd11409b4 98047356fe072f3dbd42e6fce0435dc04c7ff09e71c14ac1cadff521549476bd 3967d622ed3f099bedf03085bf95f04176aa7c91bd5466dd2706a142eecd3461 6c549e20aad31bdfe119ca1bb712d7247e7832b9eada314387e675ecd4ab7407 8f858ffb6373f46ba56593f09de34cd1bb2ea0b35cbd02ec3c54e9321a9df885 fe19de90247ca728de8769812f762635d12f571488c95a9337956bf7b06a869f b01e9458b3a54d29a08e0046103f23d04df9471e396361cb43f7ccb676cc3c1f c04ce25028b37d9a88b30dbf73b0122d55ff914b5781783f35154984c5f808d2 61c6cf66c7b43c71c646cf371f9b286ddae34d614d6c1287594be7f8f19bc87a 740f31f83d4ff4e6a9824ba0ae746c6176dbce84759aa29197da69ee1ed597ec 6ab81369ab73774ba0538fd5ed5b2e8bad84ab32b8100a790eedd9b7af1d8c3c c750c158bc454a878138dadeee294f7a246b65a428510ac81c852b0a357dacb0 ec71a7cec868354a50d8800f47776a2253792d753b23ae9a0f500411340ac7df 49d1a7eb47c06e321484a1e5017acad4c8fe27f7d669b29533e324d7fb074b16 f368dd8f879405316621380f241e8c1fa902f407a809fe8397789a0c3c3f8aea 6cd3da8b9d3bb9d2c1bbfac6fd3f0ec3d14ebdea669d12c026e01cd0f70bfa5e a096b1266c98771c59000a5f84b7352f2932b21be2c8acd7f7cf400f70ccbbc6 b9b3fe747a1714a5d4788f250e64d54b11bcc58537692cf0a8545990e20571df 32aec9b1631aab5140cc726b479c0388cbb9252c069b6ad346bb02f09dfe7438 f3c6824001c027c87aea4f43ae990b893d00cb082fbb0914948dbcff8e0fc6ce a71a67b6675cd720d54af1143a63b090154e006b8d45469fce3c070f44b7ce0343c7ffa22e8d6f8060db065db90921f59322a888e460b5428195d40e72dd6a073ed5f110bad65844d82a2e7554a2cfa5ade5543143033210747291eca062d70ba02a1f775f43bbf5b11328b06ffd3b5b785cafcdd52b79df3a468b7a17a0be03a23b6d6aaef93a11e6af9e637f114aacf45a78dcd794291f23f440de7bbc440365e1f560d6a1d7377007d7a09b8424bd7f514bcbb5d4c8a1daa2038836f1e00ce1ef804a3039e6b059f493907d7e8c8d3dd83fb0d3b9a76e380076e95581e652f57449432109023f138c26a88e1c7ba0246658f9867e146967dbbd292e259a0db43b8ae52403b583f3389d56117a35c3bde885a4e52adeef4fbe22040ccc7f0da454eee97029c082528ac53b2b98d5fbf75c001c9f5c8bb0837d9bbee2b1d60cd16cb675dff0e04c87128da7acdd4da67657b595435d0dc6582a935796d8970b376a496a1c2b42c426af784d7e648cc4640f0a8274b1f280dd4903266aa8dd0708b3f1c6a5f31135ad08c97e63c778a4d37e83a0bd9f86d4c2a9258e04fb2b01d038f7d3f7b251c1fe38d50be447b70c8899aa26b50e4752236e1d5f6006eb006f7e29707fb386a6e9ea52e28384760b923b895003992f1e11f788bb97672f0120de23d86c33557e1a3ad603d8ab4d1dfed0e30ae14a212a3ad8a1445719b808794d11e7247028c28c243f72e4e7bc8d0028a1583c34ac7b5bc470ee693aec0c781089d49bc5df03946ec9be7303654285c599cca0aff29b43a31f4ed1a85904b5b357f049d55ae3f412705e7d218293d12a7063ea3efaabebeb9bb2d49ae2049c70ba0493a2386adebc29cf5c7b4d8a0081f6697c1078aa5d4c9d726022a3082fd08a9591d2b79cefe9db3ca21413acd7e733f56947248a690a7d70cecc01000bfb160538e6217f75ccb7401acac971e8e475fdc095d4cf61943030a35d9300cb5a0beff9293a5e6d3749ec6d952a13b2aa2cfc8de8e388700721a559fcf30d16b97dbb30886765c1a5f46caf355ee778c747194f825116ec8b0bb511a53f042914e5801275179971a1ad274978bb8fe17d65ae7b3fe75c4ae1d0b541860b0a20965fabf809dd545c6f9840611b373961790be40ca00b0b639674f1deb23107e18d7955146b26d7ead08fb9352260e1a3150196833e04391d3ce6aab217db0c31a6ac9ffd888c200f1e7f38a35055a4f691f44572cad2ef28f4083090767f0dbc570b0a628cfc25c4f03ac5dd022e9de9838edd873ee35749e4131a39775c0d08556df1ff285b8653a74af4850120807f7daea7edd2a6ac7c45f6f6d6f0500b69434685fc184da62db1e97a2aa030d93191d6cb6bb5c7598916e51ccbfee60df3105a83f46843e7e2e6438046536d620dadd5126a407566cbf496a57b30350d52ac74800111d55964c3002da5ac22b5b3f981d055044294e2c670a54f671d0df56802bbb27f6ab88c76a4ab5a0a3403eb7b83d73aee0ab3493fe8240fc0b50d60a5482c1d5c53f4a4608e85e8032fe61bdd289aa803e805f0b04f756684370574000f30c19683bd0ae69ccbfe2526af00196f70920d72c8cc71fe20a8a3e603d1c5adc87a21600233e924f385c8ee9a3cb119251fb322c671167a3c870a680e12df65a579c0b1f235762a86ff03ad58fcaad03cc7479fc3a3341044c984030a4bb52ceb0182c985263ad81abd22e3115aa999c064ddab46709bdd5a92a84d0ecdf4f19ca50527c29440422c8086b6ac00df4935d9d9b72c250b616f0896b80b41fe370371ecb29e0101b05c0efb08691d1e7772aeda4a1ee8a023906da9610ab662afe6db21770cd9b20dc12d1c2abc71b6802e8f53ad17ddb5283f1604ab0ac0c2d574670be4298f81cea5a7650625ecbe5aeedffc24079ec2555dc022ab07b728d1e122f189e8903985464660623a81be5b54aee72502f9dfb4e21895be076be6fa7c87cb6aba27df32344e2135dd67e041e3aa77f5b73b6e065bc8294a039d58f74c496390e1567ff4114d55c5a253d3cd73a23459822e71b18ca3a95b0118575b15e137509839165300dc89587a0c02a98f27e45660fe0ad86666ea8709e363542b21222d510d7eae0040b667838cffbffc78efb4c9cb2d34ba9f5c9a000e2cd8d1e6b6d85db88818f879d387611bf138873838fe65fb2afbea45ce7609a2b831510bed68d109a04e175677751e85b5ec95cd982526c147d4d7d7091b0bad3e08534fcc21fc66726f370b148b4919a5b3d59b9e5979da92b05ac851910efe0c8363261993589975032839ffe1da1d448596055de5612a83270efb641808af177ee3584682f6b7d094b4fb9350e586ef42918649ddf4d9e27cfa9f8c9a08039b48beb1cb4b9c3980cbe6a99b2f0c96085b6c99d4d534a17aba7fdfe9f60066be45360dbcca6700826b907bca82d4263f50e22ea56b6163fab1411a10e8062987def6ef19d0db3090c6db93217f1a207c9df7dd78f82f9fd566b179ba7101b9fa450387c67d0cdae8d854d54c63f0c169c5d3127d6b4094d4c3e794d32c08659095a997ae0e1039dfc952fbc2d20c084007cb372a9ff64e2c800a3f1b7005b28647987d32904c5b0818aeb38428c247bdf54a877213ebf7548be5ebfde601c69327e99f3cc491300a1d728e65f99f5b7e5fd280524db2e7c7e8e418599608e2948289946ada5fe85c2630a6c720ee85a9f4b6f902d1704e2b1b9ee9c8370111ba31f033a88ae9d96b244c8021a1e38f4dd284dcfc2b1ac4f0a83933c1c307bcc55f65959a8fc039d99eff733e1bd0ad15e49c8a4e9ba8c0f39d06a436fb004ae003177c3a39ce02d76fe8bb0b6be8ed4b368c4e3ddcf74023cc103a9e280bee56cec12f16d77a963898de5772e5944362583727793a70b247f25753422f07120ffc32fadf4a04198a402f56afa42f5d6cf5f6f1569627ded0b369fe5d820c478b05ee4e7578fb4ace25beaa9bcd5020473dd0eee42a36d3d9901c9f5fb701430bb1da29f944f4ba0e878b78ec4062ec61af76ca3d41aebcc9a7129f99240511c23464d287c330715ea6f5540abebe98de33e2bdde6dd914acf872f2ed5301e4a215c896e4f45c179ed36e79da12d14e2f92f4e223db0cf0e454f5f0bdc103d615aadd9359b75d62cb6448397f03d1514a99f70da2d27e981faffe03b25c0e3aa5112852e14665038aa1aa05b848779f20f9db960bfd54ed3e0d0870bf910e0495016ff540e5b81c5205bc383c9275060a22c06660fbe077c2a278ecca6c058fcf7af8efeca12735f277f09985c7cdb6ecfebca1300ff47866809f1c0141018d58eb41e3e7597e277c8e2e186ca77cc897ab7c5e950b9e7da515eea3de5b02b9680ffd68ec8021a5bd860a2a2b2421eef57a3c2beb56dc981f92d102c6150b686cbf6ce0b78c672ecc57930a37ceca7aab67b46c0d2df72659b22d8ebb840e507231bf3e3fdc8219239ea92ce4d275bd8302ffc82282fad76d409b219ed00d118bf434e59d241eb7c0060e6a07218ab111026e139472cebc5825d162f946092bc6f7846aca9d70f2818de5782c77f29a9991bc9bfb9c0c02253ddc445f1a04e164066183b07c6d101508b8c392c410d2fd4d7f116fc9d87135e40a522e1201f829d6a961c3182a4e8fb5b498cb95e1189ea989a5e88a48fb5fdd21f3008e023e48d6f8a797918da6251fe48e29dfe9f01b055015a8725c756ebdbd31545c0bedb122215426f3fcc0fee68854a1e914e4a43654f3b880eae7cec74ec14ee506d6939459b1011364e825bc648a5db8b659d8b16360f797445ec1357f4399850c247e40390388a45cbb9adc4188b3e65f2ef47c11fef7902d4ce8247bf9c177074d9455fad27579d8d3beb2cd6cf0b9856943914263a6a87dbf0728acdccf2b020a82ef60bbdf9e451952211a426c1ffecedecf1fd738e4bbd84ddfe0be1203037380439f0156ac1dd4af40d2c81c0bea545d675e505733611b4adf442bf16b0c5ac0d681ff2f1fb25dc4b2af5f53b72abc54beac84c14d696a00246fa24bda0f9ba86b7fa70b0765653ea71de56761b6c289a8b486cdd507cc6b45b4dc962907fdd93a7bf050abb3941135961479b26b065ffa5cfc6b86c74d8d48bb01ca14040f28ce803f17e4a0bf39bda9c5fdc29724956ac6687e787189dfa568e0e3860e28b5272dd15ec43983f43bcc383985304ad04a9564e3e2c2914f0e4cd4a6020805c7d22157628278265f3fcc298d6d332c772419cbbc6cab8cdee7ba0458e40b77bee389a091412cf4b3618757b105f45235bfa323c71e2c183a65642956b90744432b74eb9ac9cf01e1a854213342eb4acea325d11308b493fb0f9db7712d0fc88b5ec81b84e60eaf18a544b98d26572241348e411604e0dc094ace8951820604af52cf17186bf70a77ec0219dac09c4dbb89a531a0392deb9dc944d7e83609b0398542a4f66ab944a53d6df2da442f92a0f52ccf9f359117cbfa447cb237078a5fbe397436946916a406d91a8d814fc196047a68590a8ab265d34b67af3006ebaee4a0912df0d1e6925d0b2cc7346622b2f30e75b09615c9b4eaa98208f40eb419143dde0e66eeb50d9eda255cf465eb2a5bd8480aed03ca75fd6d4ac089015b1873785d0a4f3104e3d55a9f62040ac3bd2f90ed974a6e8f23a4b0eb78b70a261688171cdfb52acaffceb63e6f47db60a26381221d41521da4493668dbb208e4a25db2d93519bf9074cd8d4cd55f1bbd147d78df208d0f5d68c4b44075760583a1d9304cd6d188eaee7909fdf08c80641efd4ecb6f98d2cedae6b4a2d07d0d78f6fbc7ec4ec21f7b063c8579ce65056e4f61fd184c858cac787256cc7015092903dc179e19e66f39d9a419af28082afc667e9dc39580909b2fca9c4755b50fd2596ac809ebd2bfefc3779c504b92310f53645e38c5db8abd594b72cb6f590a2194f5ce20e7f7ca6b347e9c5393bab17dc25fc3639ed30746ac00a93f193809ab913621e66908470311a272dc4a1e10ce4496e4023205750cd5c3b61418b603e4e3996c39b49835cdcf6935432b460b35ca69c91728e88ce304a8166572c5003bfe9cdc517dbecce24ceefddb20dd3a5d9de42a828c8e193f502fbabf577c0a22b2eb7db18610bb5f053cfe8449363d0bab6145e3ef5c621ce0c223a8619f0cfeaa6ccaac7de309dfe174ee435b408cc65b95e3fa075f9cb01998643ad670049d5a684399d50856e8a0ca63f5f2b52ac0f42aab2190cbf34c35363705719500fafe710c0553a31e457530475a89dfea85b826b53b57cda8ddc51e8081bef802b6e59a1707379928b4129841fe6766a7a1f838696ec7db50825a0194cf44460c0cbb2062d58a84ced905f57277d1a7a988a6205b7a38b6ee480ac08214f4fd05beddcc0295becf2c47dabcf3badddf231bcd66b9a35cb9b301f6bf6f07c4390c50c0c94aa1f2429ee7d3afcd7ca7a37392536c3e14d1d153cbaaaeaa15df5508cf65a052c801e770da758c3f86fd59736ab3247359d4775eb212c5466b36b402d97d9a0cc794590a926d21c70c9f996208e560a9dea83db725fc3130af0d8604f445f139507921b8a06c622c4d70bff4bef48773737e9ddc1372c92552dc440c4ae2f1c98156e753d3c30ea5ec46932de0bc205c409e3198fef43e7c19e8fc0df2013ff5276809e4ae448ae9024629fd6355664645b9dc5aadfacc402a7b050a369a581ff73967a4dccd13ba3deb2b62eb7bf2438ffcf8e5683bc4ec6188d60a false +check_ring_signature 92f49eb66e8db11dea07dae79fb8eb083f8df5fdd458e6003bf6b8840410e8c9 3e870f96cefbfbeaf0082560fda26b3474ded326b30b552ed450e344974fc3de 1 804a0c9d77f4e16f096630063a684033b89ad267dda1c99248f3277937a5d4dc b6cd4880af952b04599da28b78610c01a7b7635c437d4e91e0ca6023d44a56087c52e7e651beccacd53026b21e7c692f6763566187ea67cc34dfcc90ab036a0a false +check_ring_signature 483426a1185dc1941130593b12e5c69cf938545dd5d5ca7d0ed3d8989ec97d66 b49789de1c05d5f09c5ca5a2ddde5c2b591eaf448d676e03f5db946fd780cd72 1 902d275ab0557709e1af2896727735b800a0e3f8091d1a09a7028242084918fa 6597215c01ed63aa3e8d205acdbe026a678a2366329ba762f9344b3343c36307d6b7aea0388daa6104754d2ab2db526e28b85ffba34d436324803238e7cd8fcc false +check_ring_signature e1c5ed746e067711e85ddc38c3d7cbfa0df7dab3dced0e270d63fb0310f83e6c 91c0e709e263c0713aaba24613a95efca6c9bfe9dc8e4b26d6331f20b8f8e4d2 6 510d623535a3e5a0b853b934d6f577bfe36bf03ed26bf78a0a3da7cc8624ffc1 77615a7539c3ecc8cfbeae6bb53ad5474eac1cb306206e7ff06c1c425850dac8 f5942569bab15e20609aba3f88fcfb141aebb5abb71a640593464b7dbc1f7f52 3a592f40face800e7bc03ab9cf840e1088b8dbbec461a1a9fe72fddcb0a2aa09 36314faa4e6bc95ac4a9fe064cf1e729f62669dfe7db2c56a021796bb93c2f29 677e195525bc12964e3cc9e8007121ed1cfd693c5c20c85b7a781845ae3adc54 f90641ef5c3ff58d5658938af0c88576c78dd964ffde5593899d046a886f1e051da95e7564b4675dbf60e13c38c7cccf6bf6a960cb14908de1c3aee8b510b20260232e92e8432324755ca610790f73ac9af966d48b0b27117c8e60fd923cff0a66e0f03b61e712b888da408a940839e3c02aea575946a98344570ac7205a5004f64f7084b9dcbb9b66f0d3812c61947ed47508bb186cee91f6274ebc936d8e069e685d8e96aa0d927aeb95ccf0a93fd5df2973275486a52ddb7b065dc299e300cec18505312d82eba8e45e963e6ee8b3de7dc6703d67117a006b35b3fc5dd10f13053af4aeb3cfd5c68559e60df4380854ffeaf4b7b1d78eac676b1c17c66d03b430bfa3d26b2c8c4a1fdf25b4be8d206c565468d641f56056af4325774a42078951bf924cd98a52fbd73d91e3ca7ee7f1d7ed17df3cfed0c2b7ef8c1829320299e7bece7b9bb7f45574ae7bd2b7437b1a8f19c29a9362cbf8dfa9d3443e3701e7c13f794bd2f0b99d8ce911f2b8e33505f2990cf83c448484985105d96adf0a false +check_ring_signature 2e0c9317e424ee99f9a4e75fb2de9ff18c48543cd85737afb35b73ca04401ca4 61199bbe7c8c28bd233adfc7694b9f9bf6cc4a0dc22ec60045b633e0d6123c9d 2 6bc30ff28c51f192c1c4ee4191a369d52032087df1e112d1165488b9916a01ba 284d9d304ed474d524080579bbc640a0d18070dfbc79d0f06ba72bc6ef6d84d3 98fa265e902a2debeda22e158d51ffad502639d96bf92a1f8be75510bbef1b0eadb7e8e7dfa008a92fa83bb2055a4b885128037c1fb6058345c7c87620ee8220c0df6d62cf460f362b8916f2b61e764cc08502145d092ba9a14f40817786851d56e36be39bb19c8d5d43b3faa72e7a0d071ce6488e0f231c5ed4dbf650a49a00 false +check_ring_signature e536fd6198b60bd1bfec2d90d7d10a9af9d8f7464c83c413d53ad2d1cac3a263 c9fa5d02faac5113db3e2a1ce430559d04c19fea5a348093cd9ecc0a107fa309 4 c2e53f27b9d97bfee5eee43ac9bf9d828b955ac5b22a36692cfa1f1d08f7a418 296cedc4c8acfd8bbcbcfb372b3d2f1ddfd69ab3cbd331eed0d6f78a4e778c78 abc1f3ecadad83fc063b9f152c5c601cf75252953c285ff301385efd8fb91dda eaf631d9143f1c2dda625abd6bb53aa62333781bd81a179a1c86655c94d52052 45469b9127d6dfb16a7e169d92d7d085cf90978f3866ff63d68625f486d3520290839610f4cf066a4f9a0dfd072966dd5a4122372728aa722e25bd0b43e17208502b843ac95c9f9902fe8e821d8da986d85d5c2fa258472ec0b603400b880c0d2b242dfaa689130baf3843b2037527ed8930d86e6281a539ccc5c1100765ce04dbd85575bf80ecd1f10f12d260f9deb9d7bacf92343811a728eac917ee2f76774ec22cecadbc7ebe6cc3c55c2b808434a450992a1fd4b6907e0b49b9c2293e0d82614744b974594186345cfea6a717474a18aba3dcc431e21a59b2a9ace5f10e73317749ab6f5075fbfa611c4f10364ede6b4cc525c800369c5493cb35eb3a00 false +check_ring_signature bf7619a2528c8ecc5389da6c422a420969b60f08b06065ca7e2cec44156a21d8 832c61db9b0582e10c0df415ce933787b66c2a748b269021e593d6a0aac7d1e4 102 3181abaad54f99dee70f4b5cf847d8faf1f36493df4f183f45d1ece6b3b68364 6df980a08a117b2bd64b4a9a27c80301a81799ec68b4ba3d2e95370d3d9e161d 7ea5c935cd72de9fe133112c7de9ac9788b85249adf5f47f9c33a75356e6d298 3da1f294e88d0960154a0330d994717fd9aa4dae8d32f96a28f85bb232637018 398df328597db7a572015b2c5488a2ce9dcd9fa8d28ce9a415ecd9e525c9d036 3c4c21a18585c2f5acc813301dc2cbd18ff2bf4ca9552eed190f96462dd0a716 0141b22b65826728dcb760cc2d2454b4798b40dda0569c75fffc92ffcbd46f82 a9771ce95484ef16d8fc1200a22adcfdbb44b12285e4f20769729081b48b6133 2dfb48b7baffeeeacfab022ec10db9927ee329b06bb4214255c64ef942d0d205 6af0abc75b56f6225220102a384489f4b3f09071c76379e44ac465351bcfc2bc c9afde4da2e26e0bb37c8989ae3ebf83e25909673814b6dc129824dd4bcccc4a 5692251130b3a3fb163849980af047a04651d3670dbbe7fcdb46a936f25edeee 4704f86242f9f1ec1973f4ec52245665a7c6fa6c558eb9d354dad1787a5363bd 7e00082d085270fee35cf9e7822de4fdc42a2a26abe13b06f8942383614cb5a5 7f1df10a7820c057a4c2af4d5aaa751df5d99f80f4c8ff7189f89d11fbc7f271 830548adbfddc4d1690a288cc1051ba0e01a09e1fb60e0e1385bcb783e43ccd2 1f4a1cdc2c6b749156cc5137c1f1ff9750e0c42813459df4a2d4e7fd5454f2bf 3603a5caef01b7457f897c3f7e8e5e2215d60549242c5ca854865ec241757787 405ebda4cd30a835eeed21cea14e78c931e6e0574a41722f8c1a57ad3cfd75a9 656896327308a7b53eebd8c90cccc254131e6fd6ba891569d18e3f77d6c714c0 0033c579a0eab364dd8ea813afc7b5328e5474bd88ef00488c791c3853c004ce 5d69453fe63552defe0d4539d4bd40242a5763eeb9114241a94200e716087afd af68c675102ea082aa3b3c784d44f0a03cd95ffded496b5328024d3d5b95e5c5 bf96fb561a2c3564c11d2b7e979a62e202824c5bdea0d1be29c6b14ac4cc0a48 07c676f0599c63bcd07baecea8f95d7f35bba7f1f73bb640e5a3d6ef096f151f 1d3c739d423719b76c43ed711a65fe4d97454cad0d228af0b9afd1b357d35056 cb0dedbeacf0a7541e55e84a6aa9b10116deb50b5b4b3bf395c9c9025b641e49 714490aa97c4eb83e2b632d0bbf56fe1f1d64ee0b5f284b6fc56f9d011e29a64 f4fdec4d8bb0f70749a2252cf1a18ada84d5cb4f5363be47af4a9c070758a016 895f80915e8b0c73da324e42225a386a945e894c169e4b40cb440893dc26ef25 178878f4a9892cbc1bd343e08f91ceb942df6992e56d76ddfc9bc59cbb7796bf ecd45bde84d99120c3c66e63c04ff98f4c2aafcac575ea487ef78b1b4f9acfe3 b07e66a44d39d7e8451dd02535a7e1d22f0c0a7bcf5520ffbdf0bce8bf028934 cb8dbaaa2d482855b4663d2988cce8caca4ba85926cbb909fc573f133296d64e 0cc49fd061e8286793a0a1774c5b9499dfa9b26c65dcd835647122936959796d 39a959cb540d8a54ea6ac3491dff18beda624bd21d334060400392087f9400df b0880ff5c10e85a5ef17ef90654d24cd65818d94f42752a8d8759a492f0c3fd5 8375afc097b79bc4ba85b1adc5b8444dfdf935c527abec75227450400a07cb05 2638231e682b1ce890c5e83401c9e4d747a43131b88a2918ba9ecb75421de4f5 4fd7b5c91763ea0f9df65ab9e4d1fbd147a424236b6658bf29a53065d917ade7 e95f6e7377a137276daa7f61c23f575b7075163a1e26e17010e87440fe9d4e68 7aced5ad63bd042470b27cd772c9d83a50996dda643d0617933650c056048dbf c0d217e78379e3d1bb0df0c8731b0d5d581169b01c34d296a12e5a827dadb1c6 5ce6a8fb4c73fc41e465b52761bed3fa6f20457821fa71fcc85fee2fcaacca59 5c21fc07d9f31da7c936272ba5f15cb4bc31d1ce5d6f94858f1a07f05c793ec5 e8ab38c0c3d0246d3f395a7beee64792a6dc7310105885c28a813c7f0ccc1d71 c29e78a4eae72243249b7a05373ba60f82a29cce2271e9433d711c5bbba65228 828c7cc993e6ec3821187c57adf5adac7c6cc8f1fdfc5b7770697b78f7799068 e9bb773209b35f5d6e17c5fac021627ffe6a1ccd65775f7a2e6b6e3a66de277f 3889de9eb7309e961d0e41ec4db6efbb4d2b2f56ee70618f3b0f83f0427304a8 dd7d27a89d08e0e929e888188cecb119151672a2d0307556ce0cf3d50cdfb792 6dbd64c93832ed455911688a5e88d54041c8503393666fdd67f0f56ab67d3dd9 23ba1581170841dca8f1b9747ec0e7ad79d82d41aca89a4f957376f2ede3c8e0 0d08e18398d0551394e967fecda71210d2b81e910bd29eabf94b92f92dabfaea 0ae61bb73656a877a1f8cb9af3f71cef301cb2d05aa819b41afe86d314122189 3d8dea708149a569a749e67021e07262c1c9203943b4797711962ea0a8b0e578 cf856101f13dd501f6638a398599b7c6fdcb08599d108c34121978a72b0f3f83 657e6d06d181b211dfea8415c0273d101cd92d980f1542489838a0c9066669d7 9e1b67f698f8ce96931afc12af12b2446ceeeddd6a0a6607862f9b834c64984e f11e56cef7340259e59423496271ddd78f385feb770413a6e1099c30883d944c d95a766f795b753ebedf1c537fc213e4d6153cb97ea4473306e939b0885deabe 2555ae45f766f7b26389d658493d0eeea80e5fde0de817a06a0afea0407b59fd 48b246717d8e94f084ef07ad1aee130a979c09ab06f4577467bbab2ff9d219ac f3ec89208ac62f22499876ac8bf7e032e3ebfde0397c770ded170efe442c686b 30c651fe2540780028a988228a8e7276c382a6da2de349a4867d6c092ba13de1 b11d2104ad54cf25f2e93323d6ff70a644280f4c114bb2b00018fc5f19954319 041791c893573133460bd0a5f284fff2cfc796ceaf312aae379112f11eabdda6 794e8876cd3d270f0191500a5b3f34dbeff99ccf44dfd4c1d3510799db17cf83 197ff4f46447c48eca397b5d4568f6271ad696aa1e5d971de3b830c361fb2ad2 f33041f37905fdd440d038484eed504cb182519a136730a2b8b2d37f3c1f37f0 987318f84fb6a4b7c8b5fe720c0882b9eb0b2beb87aa23a0684a2155560d64e1 486bada3e31de73da0dde780cc9c82829f2541c92287a458fe540537ed8abfd1 bf0b9fcbf37b1e6e1793d473e1a54c0660a2cda16acdac49f3355ed65f79f0c2 534ea2143bf3f3049042e80ec7b6e4bd2506e4538fd0086591b01bd86f1151bf 5c4d4ffb59cba3997b4236c22badc0c4bb28421447e0afdd11c1787d8a15d71a 68f939a9abd3e0b40053318b0c4cddbed8a2ee33ccf4652bf43a64fc63583d92 b38d926bbeb0b42fa57de22e3fefa0a8c762ed0ccc5bcdd5932fc91f68136639 4881d89828e0a2541004d4aac9a67f9ad033320258374669c47e8a6a9c108ef5 5b40e79e97f0370d0eb0c02c90060d03b605ba667cd20f8242e73d5c6494a546 8b1a7ebb9efefefa7549aa879ea1fd2d276fa1286953712a775b62e55faa3ad4 8ed488689eb102b3863fbdab8fd9188307f3d599de3a99950f30eac887ede042 b8faca9b0177b3eab0491582eef45d3d1b6ce6c658f9da32c98b92759a6b74dd e7e9617626a0595a77394782c0d989700ae52bf913a583f8f70ff763f6adcf74 de1c05502486105a543722c8e3c1f2447382eb898af2cddeff1cf1ca13e16106 9a918071810ed8ef2909dc79654aca9e0753cf8ec9caf8b910067c258520a1b1 313c33e09bf73efc39922ef3d7c4b12674c1efc272c66dcb9fdab4bd7891d9b4 858d630d9fca3ead28f18752bc6985ea42ed61b4ad755b90d1bd5116865ff181 e4aa5784b1dd115351b720a2c7bb636ff0e76285f2916675a012f091526db177 f18508757c7e23e6751d962c0b973f1ca78f1e4622d89ad0d5254fdfdfc17450 a26e58557b85739d574d0ef9c1bcf7bb3965eacfd6e70362f9bc6db3a028151c 3a937a456080ba49d3f8a1afa3b7526719b9b1af2d6a9a724c59a50375ffaad8 d2986da4158f9b559ea9f8b5ccdc83235c6442fda56b6833cc09534073b3c471 693fd09a457d7a3d8171feca8063ff9d1fd58021bbe396723b5ce8469c08f1ca 3491c53392d62020768636f8c8d95ae710fd8172453b2bc96dd0de44ccd3f235 61f3212a487f10bbd6d3cbedf06cbc7badc1d04224b3ea2cbe5f7915c7e00629 000a92cea581843413529aa6a8e3e0e3012aa554d74c88371416d7025aa33c07 59a6eebef7432e92f32870bbe8757d1ea5913ccf0908314070e621591dbf9e65 2fd3a32cc38c0378802b72eb3e012bfd567db57d8a7575234a25c3c2aa8d49d1 6bc13669dae07c822709b6487a3605c50e36b07002890ea5b7b283620eff5b30 293289e6ea5289bc99db9f38174baeba8b2c20b0bcac143afc8a97370c773665 52b16967b2562b57a7a6f140fbfea732f030ddfe7dbf98068099795274b90b8c 4adb7530b751116fed35bb4c6b346db674a0b4d7d07ac5fffa85c2a9b942aa6b 34f072df78f848db1a6394fdd5887bdfbff8f0e6e963af0ce724c32dbdb7aa0542d7b60dfb7b86e8a53476fd8fc6c01859af7a864ba7c3b55113b08ade79240ff8335a4dd6743234c6881f733259dfddff8752fa3b7e322104d3364fcd3c740304b7a7bd9326063fdbe332bd7e52178d0eefe55639e333d60c7d0c01bdea0d06c60a0e39256913467700dae9aed0e99756336ba5ea8227474eb60939c4b152001ab4325911f8d9609dfcc2c495b1007cdd107e81814bb87edf4354f8cc7b1a064055a151b7120828bab9e7e49ac3d9669f89b0fcbb4601235a77050d69a30708a9e3b6aca972b4ccc8f28faac4bc0fffb542e1bab63d04c5c599927e1f142d08867abb0ec55efd501e2ec4566959ac39588df18d53eca3894cbe22f8a192e5024d8c3ce2929c219ae709c1c7f3fdda5a666bbf28a0237ba83ef884a1567fbf0db8164a438f6481ed1dda265c9a15c380e0cc4eacc3d94e75f6477c4e67a29d02ceef26461a63d7a82488a8a533afe4f4a5a18cc88c9218b268fa012bfa50a304e092aee75b09cd4a25328ab60e1a65cfc16aa1397e6241e519f93cf18bcc3003bcc7076f056dc8cdf531197d252f855a9a0251deaa4df81e980bd8f31d3e6707174ead1f08d58a5b3ecfa7631980dc1105e33337c0f2b71be23ef7a44e58440ba280f5aa14b7c7d7f92ac13acc699849ca6c9e61d3b2c13202aba0a394868309cf852a23cd6ca25051af961b413e6ffaecc952fafc19b157f447ed7aefaaee0ab414c02f0496498c8d91d4264f0240d8cf45bb7fe3f493a90e3616dab2ba800cc79441bd32b22e95517ccbfc5c8cefc80e8875d9b95a3fe59b1113a4402ee308488d839212aefa8aec7b56f0ab6f8ea58c5a8aa83c3a7799930da137a89da006578ba1731721790a5eca1e0d184a3dddcf31eb024d8289e23531398866c31b020343795f7d62f51ff7ce0d936a1ae76c25a40f02b4cbed92156b6d8e1b447403dc84f21e74d28e025362f0c4e51249ce70fd1de9e311f8ed431da3e1579f1a0c17c7813bb9028ca596ac576c259cab17e00ec375ce202bbe9492e2d3d4bd2202ee6d389262d5d88a332dc915d1dbba07fe50ad51c5b849959f375bf38333f9052ce7e5bfc329b3625708fed351903de17daf71a37253ec71f5c85172359c4b0ee7e9e7c75deeaab1ce6f9a863cd55e8e81dfd4f47f63611de8f208028c2fbc0b14192bd4664d05e879a3dfd56acb4be51ce83460d4eab923f909531d63b4bc0e7a1913aee6d98519dd878569d06cbf303b94cae821b2d57719edcf4fa956540501b472301244e94e4442ce28633a9bc2cc39cb01f08dbed730d9d5afcf2100068c13d05eb0eed77dea283ec48954557459ff9d9d20e343c29c83ad232b7efc08076f2087f30a15080bcfcb027e6db289d009804d7aab24a1bcdb21ec4060ee06997acdb6486483e3aa96174525f14feefb13ba686377d8c0b46078530bc0920f6767c399d424c89e2b8ee5180c7727de5dd699d2312f697799f3b363ca4c0708514612c6aa798c17a204a6f1bd231ef74b827656d6620408597a0897512df0032aeff3e5f0370b619043f597e8817c365dfcb034bb673ec48812c395793eb4033893fb6a638d674026fa07258a7ffba0bf2d92db94253fb812cb179ef5ae8805d1d454ea16a45d9d3d4163cdb04762b1f7f2e46c2197b2ce4df12cb34ab15802b40589365a520dea9e4058d1f2eccbafc046a8cd47dc2ba481a4232fb7cd3c0abbdfa3062071018f93e61a8965cf42a9bc993e6750164ae73dd2c2dd2296f9077112d1b5741279ab89cb085f03e1c2410b8d0858da9a49f4da754f673fa2fe09ca2d819cfc21cc23502a203b683154b05bd97c96cb3b92f0bbbfba6dd03dbe0cfec76c70de312881429a66ad22800301133436bcc362db7bc815399eaeb7360a3b2eb48ca2c65fca73398650ae9b60f13d6153df1d9c33bcea9c3e623c322a0f1419162fb93644ee9fd48be107332d701834cd980862053cc919adcb2548090b14143baa0e613b9693bf85683e26c697063fd93f0e7eb2d0c22151f9921dfa09d66958265dcb2e0e07de42cfe7d9c73b5e1167b414fea150ffc68127a9ae060c581e3e7a8f8b795a630e8d7f8b039525f339a436a0dce834a6cd8d302066b20e2070c954b0b85fb727643424ed119eef93675697167e4012f6d63bbdee2c550208c01792b6add6263f7d82798b549e4a4cdbeb81e101e3c78c9c623134741a067e89b71cc738429c8473468dc105fb8655f1299ff7e34dcaf6db5b42e2efe303b4d0606521898bab8895612442abe517858b404c25c9b8b25fff6de4d9e55e09d042e628f0ca75a85f8567952dc2b5d0aacb1b6bdbabf44ef7951b614556160c2a6cfe21fde85d2da0dc4d100a9f6b469a948ce7c1d6cd365571f9a5b8fe9601b3152e59912e72abdd201cd397cb56aa1fe11c6f0036fa768805466022917c0ec802c833b41b90d3ca51944a31ba958892b820e8adfd2c798140bb21539b1400620c22c525b0549f108a60a8214c557eb2f26f9a6c8e2b61e1b50d6b7ed3c30c59f627de7b56ccd4f4f6f1abf1f58bf6a9d81b36d81e9fa6d2895817186b0c04f607a59ba6f9d7fefdbcb65c96b65e3d41c774f434a7210de9aa6333eb3ab40cebf8b783dc530acf1af9124fbdeed2ec0ac6f2e773cc7435d78d3655f2443f0a0bb3ae5bcbd1ceb5b9e60501e54ba0f3b9662a985451f44d053b5d68ea8d7c0d028a5dafefd8ddb785696ef24bdc7236f84867e81277c98fd36c336c0c811c07ffeba5bad9125aab99cc22677da7d8a3c166a6cea4baa519577cc8d077f56905f6f733dc83b25306bde895970b4ead0244036ecba2b93901fb0e48f1b1f9cf0918cfffcdcacbcf2730496c6f6311cd7488af9890af708574226aca85f1828c0e92c37c798942c6ad36defea43e43b569a278c57c32981db4e9e7db9f5029b202bd62ddbe6d51b942b9e196da17fa3d815185efa42a6589c65f65a53d5d24d00d79fd2e9347598cc6ce7a56270ab6fb4392303f94976adbc279506a1e7406020d200e20fe54fa28ecdff7e7f8e91050d50a995b72d1662cd90060de822c2500012af88940360b63cdc15f7c3c318c783f78e5e93082cb096308050f24a6235201c27fd3f8eab23c85c4e97a9da4ab666d19c534efa8415a41083b48ca22d136062ee10f05e4ef7dd40b6f5e7c30823e984e644f6ef7e0bfe6e00b0afa9c7e270c6b8446cf38f41b958b9bb6fa6b22a1dac04dd204b07182615a4e6cbbbdf2be09728d7d3ad1db16555906b98a4666bb0e2c7182f6d2518bc5a53e5c18ed83cb060f8c092ff2f42d985f7786d51faa6d5679059b4e76a0856cb6354a43b322720e97b3ba8e7f34ececdfd6af470328347283e12faf7aa0d4afc726a7d76cba30074fda1c15c16897ec874d31cc097681a57a675c6f9644bd477aa4469bac3efc09c4b6592a6c23e78c98a699e190ee085485d0e41d3d76ccfa7b76bae6a399f501c3863291fffdaf7d1bc7e7d5559685f76f63c48ead20e2b8f6d1df9cf9ac12083dad5fc1261c24452c95dcc40f34fe93a9f90386b8db2aec760542e5a0020f0641366febce425f6a225d6e4bebec67388adaa32abaca3c2c333674dcc5fdda0192ec59126945d5012966234acaaaccb2b2e83a5e27af8d6ac550c40db5b91f058a74d4239a2274ee257b8ec0c9bc1e10f5ed2c331eccca49b77a1825176bd60a2021cd5220e665e6ecc00554b1174b8b8e82eb5e3595a16c60e85aeb170570094d03b50aeda53f81c59a88cb26b9951539590b33f8a005d93bc94cda531a2c058ef4a6984a73c005f9187a786b62c918d3b186842cd71813eb22b63bca9b590877964a8d6b0f3b4cbb5997e74d33e6c7b7d6bb647d38442b50b29f7d9e56170e7a0b6a90dc8982b124ecac6ab769f113dcfe3156265c7a8faf7ef65d81201204cad52043ba2287bcf56e1ed117b58a884439d6b4d1f05c6dda647bf7579861077dbedc74833b4cfcc01397568af335b86faebd1023ac46edbf5355df9b3f74089c8a0d45bb96b9a4182182783c16f2ef3067f198197b4cb03ac1dd5b6f65820f31c7e22df75f4699001a70b457645b280e175c02fad28ab54577ca281727730ee30fbff3c0a8ac631cb2d1b39dcdc4d35d759de92caba76a7f2c4525218ca10ff835078a686c11d06fed230b93393a5ee59b1cf4af24a8756ecee58fe01ac3081d97c228faeb6dd2b393105a2b882c9d6ce6884b790e1fbba774e4460ef0450b9477570470bd5f95117120723138692a566a2fca0ab021ef3dfc3ae38ce6b0054d2352a70282a1034e3bb11395403a977699f580a2251bb19c9dd463899ae00756b778bb661d2336fffe2e2ad077425d06c38de7d5079c781f2d53329bf022081b39e055b33d55112fe0e73930b47c9eed93100cbc03ef118d375502bd60d80fbff5d109b988bf0c5fbdd348fe54e6bdc1d3b4f0e58fc8011203e72489864f0bfce79baa9c11ef0bddd433290cfd951a299c95f76a2f3139cab016af9f72d10a0671af522206e31c44e1988b323037ac5873ea96357959276c2c235344312b0acc2af5574839119abd1302eee12838bf5406bba39cdc573134a64232534e4100e6ded9a78d95064ec976e3fcfd620992f0ec315f7469e94c307702fe6c5cd40c81252c9169f7f9355cc134fffd54105396bde30f998437a8779f86e541e55e085342c595f169e5bebeeb4f92645fe343a3e35c83a0c8e103d75f264eddd47f0f1b8541b556246adcacb251cf02a148e8876d907676fb03c7540dfdc39bbc1b080412e62f28ada516404a5d2b4a4da3386bfb8c49b31fa24ba27197db5c27f10e726362cb75d3121e863b25ba347921b9183ea1e1be603f58de8bdd4e9bce430a1ba61161ae9357a5e2d41c5bd8a99928cd0360d313894f0b8f09e9b977ccc805a5c7e73adce707f0cc00334a269ec1c566b666969a71cf8e7f8876da13b6d5027aaba19deeee73789d16507aba0b562ce29b2d3d75ec47f03bd31ad6ea054902159e7f10c768084955b68ff0a5a4729bb7c923335d98d88e5a06c9496f5adc0ea4a1855e2ff304202b4719954e2ee18f44daa411edbf9059b263a5317450150a2e2a5fbec84c4813a5e9e0638fc5acad5f1a7255e0ebc1e43cc6414bf1bdc6072937c9567490e1f8712e2396923ebe92b36da1a01e58aeb1759541149b321006e2d34faf5b64d0297caded0d8dae621c69271465eee62a17ee9972dce8d1ed09ce5a88285f63768f91088b0ec3a94af034ee3c6b757f33a932567b9d26090a0ba5147c97f6c419a20c86f09050850e7649f54db9f9b42155ca0bef01fdb5cb0e610e327c7af040a64a50427419f59e45381a451fc3821cd3b234233959aa870b870743f598e23bc5a8394d378a53bf4903fb831422a4fe418c18113444065f00be541eb5d54e5a885634f8c89157ada26f9e61cc65e1c4fd32f50c5665e66b0d2411a46becddd6685775399025681532e55f18f074e49db8f34b5423f4c0090d9c51620b33e7b0ddd55463d156c594ee60d51716b42d116a4ae9cb1b2c62af03521a96458a272e3ff692086e869edfc29497c86634770987e04eebe2ec51a002f8b572ce112c0605f38d05375a865c818353acc2d529c82e508e91309d858c070783d8379ece355dbdaae9ed9c2bc9838a0598af5b342d6d156c50bb8beef8010073fd5d86acf36677c5d91ddf7ce30f51c59ee678694d34373cc465b55e590353bba113e6e4c9861573e4318e400282a5389cdb951a83f61a34b0d35cb0f5010476d7502f99feeffca541476cb1d6ce8a0ddd6f9baae0a1ac3b1e648621ac0782767ecc556b1de64a9ccdd437f1b1a972f57a0e3389d61eecdb66b8f53a4a061bc1682c70e9deae1c075bf4badcde745a27a4c07d2adeb903940e9766ef0d0f90e9abd6057dae37b9ab79d5d40e8c10adad1b8f395dab4c63bffd5c7bf3bc0d9198ad8c88e0a906bb34eb35cf1fc9266f143bac70b738c68be6e95d317619020b70ce943ab0e4b2ab3f08bcd07a695c24170a8f5a93a3f61e142b75c3cfd40ffb618e763ae592c2c77357824aed9729d9f70fc01f0d8ff349896d504cb206087b174a712b98080fc22ef8fd3a00c87f7adece720a1783fcb8a80c9de2bb7f0e303e87b9809cc9d8091f8b1e21deddb721194c6566aecf290c0a954c6759ca0ec39be79ec68ac6c36858bbd5ef659429629f1828bf9a25e07f18163051e3660dabe79599a4964837ee03250b6ee1a82065ce6958a801463d338e74a0492018079843d693e8d4cf50f948b6c4035e915e502fe0249addb7669e1b75cce0231e089306657f5c8349a9fac7608eea0c65466ba801c21003af78487d789d02cdf407fafdfa363dfe6fff2fca80eef297a85116f2e0270a85db3321fc502270a82a077717a47e961fcdc241aaedb95247288925fc140467010984fedde641fbdd230351bc925431dc6b9ed49434c6897355a4f5b7c8f2c897d7dc720d8b1f485a7b0cc0225d095f30ec35a219519ebd958b215b29cf147137aaa74516a4e767a65300a43ba70239c4c21255409f68506a37ab075cca2fc94efa6b0272a4fb4b6b75070db9d4e775c1101f11a1e8c8565f9dc1097fd93c309a23236d3d03045c2ea00db15f4fee035032298e841fdda182f571a23669ad4f00277bd01196a0ffc09602298e5050da66f7116fc051bf9e01cca4db55f59681f1cb9ab5116a97bbbd6a0f1c37415139b8a17c473f0ae3c745c2be97a1cab1560219c7cffdc999df2bcd0b5e112851349139900dbb45836814363fa9287ac756b62bd40d5772d53489be0cfed7f0f6a257e460c3dd8eb62a6fb7fbdc0b1a9d51f2f066f2deb9b95491a00957fc61c278cda9df956922f800cfe4c409fe1b4e10daf52d5f3c17f597db030342d2280020840fc8c12369478503911c5a846b0a1de24ac6b2dbd839bb75490ee2f54ee6fabdb92c2e4d4212bb8aecae12e304ad9f81c9532116e176eabb660ae865267312e486205f5aa404b257630ee2708551ac51e91762bd52d85c7a87020488bce10ca50c9735a4fa88ca0c685d8c8426cc2b8bbc8d38b62447a24db00c38a2cc9d5914614e333ed138aac71e2bd1fb09e247ee91a686f522465abf460c6cc7664643585b7450fe1dbe8dab5c61b1c6a422f68a17f091e930df3508b1001c20faabd1edebfa6d2680a8574dbbdec9195656ab599b963bd276d3b00f500e72a4b8db14f78f957c6a10179c682b5246c3a6c59763e4717e914ded86ded60fdf059d7ab8aa84d5301c8abe5a7b7e0867d31375aecb55f8b29b38e4697d8201d9678ea4359bdc7259491b1902388c5979eb85da7cc270b02c5a80baebc0f106b3d0a1427a1a82e44fdeb00ce6586f150809aaf2ff4145750a59f0c95945720b8343c5f8d00dae4deee8700eeb91c09e73947b556657796fd1f54b0b1ab78505cf32eab8f6a5aaf1d3d1e9a80a174a6d007c5d71b35e2e098796a170576d88041719f82a1eacf568e4fdd1be6ab4d97e6244808992f04fc1bb61e09976ec370d54d329be341c0df0de40093bea9bfb35e3656f7907f650f3b08433b6ce409609893cc9b5e763bca48b9a6cbbe0e8d3ef55e7afd2978bea07fe59b58e4ede6f09c236ee99ad260c04e76b35f184f0af44bb05d50e98d8563d3b5ace8e2365950e0e76b290bc0bd252db635f00120383627070a3376baad30393b73e2209353703e9d009b247c99f1f12cd9047e22d1b817c93f4f2e3bfcc5a8aca4a1b29792e0330409c556fef94da3f0177b29cdf12fc049a9fff8d5a573ad35dac3b95f2da06f7f5142f4e1a5144d55f0e134e5abec838a1636cb320a87904187e4de83f8d059f2c6b02af87acd6c5dcfe8428e2fd4f7521b5c167b0b1a1ad5a4f671869ae02a05b97fcffb0335e9be6e193e3957928a425fd144adb58cb0a445e29b8cd0e083ee2f41e746c2360099e4bf041932916a90c4b6ba9b2bc5edeb5def43fdd3209e614f4e3465a46f3793c735819674d45e6ee8187f4bca436014ad53bd51d3a0afdd5a15765e4b51b9661252590b924919a869f54d76f711880e1ca9ce344dd051e4ad25d9ab54f14cd2f85d121f7d7179d4af1c9e3b82c111a88544a915d2006b345ef5ad947aa794ce36fe3aedafd45553588ec1bbeb48986900e91de13fd02e22c2547f5180327a6358d0a5f9d1c4fbe568720c8b0b6673dd87bb5f8c68a04aaafcbb53aea254954858bc4940710b91bb7f31b5d58143d2b8c022cd4291e0e1e11bb592b9f183c2e694c9d57c5082d29ed151f275d55df51391fc30a805b078cb42656fea87ba85398f1f673c49b227e0205e3b3ca2f8372efa660785ad20b299c3b5ab2b3ba8bc994186ad8b0d21fadd3b9e140f433663e805d3d195b770cd7c677341eec8f4435467ea1485f6233bbbb4d776557d2b18657b8e5c9b83b02657fe9939ac33388a704bfe1d4e39c181b4b1205bd313addfcc7c960c759a80e088f7092433662401a2682d46fef8c305eef186971597c39311b3acbf2f92606f86c0c164d7f35efe30b3d0edd26dcb240a615fbe6fcf29f8a5980e660099b0f187ed4025561e32a0c24dc8d27594dd5478d4bb0f77be8572443cb78c9072c0215728286b681f97fec18580ce47e5dbac347bffbcca4ca7986b0a359bf1eb401a987d54969fe3e4722d30630f817f10e7a6f7e5c31baae7ed9373489d885730a1181e18b30a2a669beac1d88c850fee5f4a987556c91badd32de8a6bef7d7103e675b099cdeb14c2e69407752a1b6e024cc701346f667c0b259ece41967fff058c23cff7f799cb0a8ccfc3b69f86c0cc7fddf564db0fea445fc275a6fe6c680264d0c21d66f5c0fe65b4c36be5c8e5551ec5d12b944b95b925e00ccf9401d402a70e7c8085b31f27f8c4cf74038229f44740b4a1bd02ebde6431dfa57a76700105520d7d43c3a9b871b6490d153dbd30dd54eeef1e4f627d7a87a0fae3747a0554af873626a35c96376f4ea36bc220b40b51ec344892905c2265675c38ef8b0079e4a740968af74dcf9202e1888c65e9262907d3fcba17ba4b3358c18cbecd07e9cb7eb135573b92005a17bcb8036cb261755be8e89118370433670ce6f29a07657dd5c5747267638b7097882b5e5f447c2371bc31a6dd41d7cb7c4dc0e42c00 true +check_ring_signature d4a3b483c5771a8e5d71b250992eeca68feb81a23f7eb2e27bc9e0c45c3eaf64 7d973c175b36fbf33e4beb3ca2018b3daf21c64b4cc96cf7d169a3ceb4f2f20c 1 bdfe8cfb7c43c072b384c52f31e7ed73a3346ef386454adf153cf5f84dc3d38c f06cb1fe41639f8469819417b6fba59bf9111a008ac3e307d73026642e1ae90c7b5fa35a37f433a083a6e921804afa1dd64838a3d91eff3cc7bc0055d562de0f false +check_ring_signature d3fa7424203b36987c88228a6f9abb6f9651a6c15b695efad2e25da9df9ceb35 a6a8704c290f709ec07cd3056b0528bbe0c560dcc970d30a309fb3917a42c598 1 b46c927b4826465b86ed1d0279486632fb64842f1390fa95392a7402c602205d d8cec3c4da2094ba6b22784321f99c1297ec924eeffbfedebded7d4ddb83defb50d05814a5e30529d1ccdb50d754f11d67eadc6ae70feb2d5888a15003c2ffbb false +check_ring_signature 262d1521b17f1a3148019c55f2cca10026e61725ef7d06d841c1c7e3793da254 53f1fc68f5f6ec829f6c94d9d1e2137a647ae4cf24b9a8a1a759fc67dddfd776 2 11804b37807cf8907c257bfd5648b3186c1b1b995cd779a34218402c4a1acf4b d57b832f3bfa17334796e53fb24833568a29871ece1c8def5cc3058ac502a376 b092b81a51e677168898d2baa7dbdffa1690c48a00e89f089cf07aec963f7909699822a0424f2c2d365b6153e1fee18f4ca1a73096e1c4ad5dcb2c0147b24b05d4ed00f20a89bb7eb004d3eaf3be9b3b796228b74b84264368da67201041460905f1c04c180441283725188b91d32b54c129d0bd0668013816b5147e26a59402 false +check_ring_signature 09da123710135f92eb76f4f4243850f1ff60e87d1408b7bd1665d21040fd29cc f79e8b92670bc37d0044164a77a80e11cf4a821728a2a6f396057c5dab6f6b27 6 03bf8e398c4ccb762c88c5fcf6ed6c698bf69684184f8842b747decda2df43e8 1215aa0c980fa9fd8d98cfd020532e7a36af834b1a10b8daf2d47d308c000091 d772fdbd9b0d8e371b2f5b04fcb0752323f3a57da39d17b18108f4269b0ddb4f 98d85954fcb40754da72f8fa719e2014c370b656a357700518004f675b0e20bb fda67c2705d121df0ef8f3244a8e61520f1515d98b9b873728f886f913aa0936 2e6d6a5cdb2f084a27b9503abff1e272b87b1f6dfdf61d0b604cbc30551beba4 7f89543caa9aab8d1734b04f44423231ea9cd16a223f561c06a206c1e1174f014581d5c246d877ad2ec1ab4be3167bf9677419b39bff563b22760f7e209e1a02a827456c9ec832106753f18d0849ebc0fa792d5f0976179ddd7661722cd73481ec8fb1db235a743bd9012768cec561683a513c4af8cf39989d156609f2107c0c70078ea78ddc8557b47e4cb274b261b9c26b3cb6653addfc05ff737f2cd90c03e8d385c2d10545b2b3d0f830fd1e3761e8b39438cd96d6c33c56abed1791920e0945b91ee251154ff607c0659e86c50daa801f4f55546854746c8dc94360759c3e6bf11a59c9d6586ed738c8f1aabf674f01d6efa5c487041f5fcc246ac9110c23dc42d15b2124a9855caa89d653bb9ca1692ca4b62a4d4ad40f42fb1433030f97508993011c8a7fa5160cfa82c6b5cf053896b7dfc4ee0118bb6de0e29e3c0d37c695f5cdff9072300a4238866bd74bb144f0c8f4413e3e7149f021bc3ba60ff773181c7d535f206a8db59965775527c8b6733c1ecda56887c494b56f77c302 false +check_ring_signature 9b4801236b6e25757476b812361b77b335dd77534a1082704c72e1a5cfd3ca1c 1e6c19f234915e182123dd60ffd9ad48a3dc791320063eb8e2c94cdec8bcc7a8 2 faa5163540a19394c6c44dab17a25f7e5a8486d651ec6c28ffa784d34af3dd89 ef4729609ea3f93d198771ad6cebc777dd24dadbe59727d65fb4625a4a19a41b bba34f92d0f817a051d81e208fef4093d7a81e9d196ef2caf1d9634bffdd99c43726e07dec89a86bad1970295efd7c4abb7e133f00713e90ac4f2dc903a7a630b05a670c268714f19478bd396b861355eaa950307a4ddcb23856f6fa5b95880732a466687cb5ef488487b72bff998b1b586ae1e81382715b4d0e458d51c10900 false +check_ring_signature 0daeb14ef83ee974ab75ff4ce1dc72bf8d077bd50a4ac330b1cd5de36ed52cbc b67426a3bd777a60e8b3d0511a546a6a147e9969a0a88f57b8d35e94fceb01e5 11 cb3137674333e8feef9ab4e339993974394f0a52ea116d1642654bd783a4f40a 7e9cdd043db45d79f537a89e2f2353d2534e2021690f62bd349acbb21349231e e8f264112f76c22127c88a2d76af7e636e6444c07e6744d5c0c4b27b37b56cbe 5232a50583c54679b0faf1321cbe27ec6cd46176947dcb94f39217d8effec521 c09847ef497494d61c2632d0dc2eaceb3a7838cd7edf617d061742c81ea03b30 44a1a9b19afc7c9484dfb3af284ac39894728ca3c7c3d0e8f682fffeb1e2ffa0 a90770c5e750586e31950c44887738442baa622dcb02503bb0d498981c541a91 cdaa68443e519c8848b8f5794d470f7a84122b8533a70b8f52c374e276334690 fb69bedf725946c209aad09ea2d09edf057bcc2f5c80563cf135c09364aa3a4c 00db0648d124166c32a21a895742fd91a6249059b3a9b9efc7aa9fa971c35924 b5f2b4a40d49d5395a871b5e2fcab9dc77c6ad624dbef65686f4f697d5f86f94 52ad9edd66f7dd938480fe9ef979cef950ba07f72eb78f5c2bb8841a8ab692071188cb577ad9a5026829ccaf288c056b8c42b32d4cb293f515de966f8d7a5608c6af2f921d16026c489c887a9a43cb5db07c160f50ac877ce26045adcdc1eb3d0093924ed808cd08c8129ac0a1391d6dd6b6ce31959ec9a1052aa5c8a9b4d80f894a5443bedef5b65d2a909d328f42702c6c26ab869a16acc911f24293906702797db80a8430e94db94e3ed48d4bcd3049251fb7207213657703a2527870c502e1a61e7087e50a47a5149f0617fc66f561f2fe7b4f486ece9ff1819c6647d0021a65029f9c2e53a6d2cb3f0405e942a659fd294cde5c8c9bddb61ad309f946077b5a342c3b92a011a243dd887e17c96542e158719964d46345686af2b8684c0c084de1661cdc44c1ba28a83fd20bcaafb98737a1b1895f56a399ee150521e4035a2fa81c99983c6d94bc3066c1b56e7fb57028b4016708cd3f1209542cb0ee089ca1fe72f65d804054c5668339b28f9cfe80af53688639b63a993153fac85f096fff53d760f9aeeac05a9c5531d4c33199b169814fef859a8638c0f3835ec408f1527202f0aa0b13e678df6fdae95509de4ddd32829740903a5fd5c813b2e00e312d321e00dc6eaaa3ff8a08157afbdc6dd46a0ae5e85169c68255713152e509e28aeb67e3b767b65a92745c3dca633dfeac0c050c5514f1efa5e031fd1f4201a7c175bcada3284b07ef3d19952aa158987495cfb67ae63e97cf1e8c2fb30f0a2121b40ee4db7d94df9b2590a42097eebff1700a6cb229fb148c0c2f06d4c40bdcfc54e446c57ab1a9de82169f1567eea8294d04d189eefa20775520c07d480424f084f40174e69d2190bdc3d745d98994bd577b05b34a9ef734af835a2a390079e001d127f1a00e5d81f3da1d7937a5c3ff30ef0be2064b5c89ab3d09216e0ee2107e41a9697416fc4718b797c612c642eccb84e76fd7eae296a680a4ddd706 false +check_ring_signature 3229b2cf183801788d58c2141b55409220b52a0f9cdef1549ebcbd4efe5e54f5 732ea34d412d52e72926c292f419ae004889db5abf1b056284132f28ce57ce7c 2 37530a175e9143674be0a567553a937374e6824f118b9821e295037db9c7435b d10e1d4d3a453dd1fbc0970a8b8c97ec78ded4758e08bdcd310837b368363d00 304541e76fe26857df689418917416c2d63501b78016ce792b9ed5cff248270355fd7e0cedf40656080e83ef84fe26c4958a595938c7135eabc22afbd78e860a90cc2aeac86fa1bbbf4d7f371bc41a5d24c2de9a4c0849b5b2eb72aec139bd0500cc73cc600fc812e3bde678c3026718cfd0807d4332352b625916f63d144d0f true +check_ring_signature 1bc2dcc86244059145bd965310c37082b8a71577f00747dfff5a5cf8ae1ddcc7 5943e642b78f8358c67363c7f373ab21544ef1e231ae5707898650e7b1cda4e7 3 c59b867b54398714d0ec00ee5b49081b794210509913ecf56d021558d01e82ed 8718ca26ff006acb7b9ede9c292c13e7bd27ec37229cb078e2959f430e44ce54 2a2e1002f6723fee746e9cff420686a1b3371b76d8ed596f48b5295c7be4bdb5 ebefebcb8da00e34db579be6e4dc19f9431164b8d87146774309035ee0c9cc07a6db74f0393e91dca32fdb0931c49bc67621cb1d513aac8fff13c99187f1e104b36a0889098643e82f7405289555a3685a69e4a3a3a8e0d4610f90d63aa3c90cd76a84bb50bf73d28e49a5b9c09bfa473e3d17d339326afc1091d229c1ccb400389d05cc128b8f6f3bc37ab6847a44463c9bc002cdabe6e09cf48f3fd7c1af00e14b8e97de8a090260cc785dc9965f385e889d15f5f7c4a1f9b002958a42640c false +check_ring_signature 2459661a1c244704d7cf19b2bc161b1129916858693315d2b27a55538d88c527 47c310b1b0faace0419517670a2c920fe82d070e754e3ad95fb988ff71662320 1 59276876e86680e7157c345b589c8e5e96fddee1f75c8da23c66578dc2fba802 a3583f91e67ac2256352d91f033c255bf1ef39a25225c7293d5aa75002d3080c1c16ce47c5fe6466483e4e7b279fb66c7e8db5be3e86573692a64128c62f2f0e true +check_ring_signature bc15a04cec0deacf0edc5688c7030086b33e9592623845f09edc53f3c3d7721e 408ef2ac6d4b0f393f855d1d431e0553f55fedb90d82872926e4bd2947e95617 8 baeb3d30947dfc5482a5ff511fe83e37d96c099ae03b683e8a70904cbbcc3299 05808a0169bd8b5a1c82c0134910e18c607339764cf60281e6cbdf8378463931 4a70017196497bad647e2abd096e5b9b13bb0a1e3ae62f3dd9098099ed83821e 53c8bb66889ef68827f6a401df487c5fd1723417ea4bcdb3f983398b004a59f1 3171a9e1d8cb1bc126572f09decb387a4f54e30587d651643f7f6e207dc47124 88a6e42d8d049fc0b932dbc03d17daabc4444a20136f9a81e5373f732c7aca9b 60712be697e81da16e1cbf23f88fc6138ef432a5b938f3d698b0aeab2e6e7e17 338cf131241dd35692d6260cda7db8517081f479ff1870ca9cec65c002d16643 f6158834062fd270b878fe6aa4ba009fa4972676ae5cdf3fc6039255edddb708e6a1bf0ce9bc8e947d2fc52b64cbfad603bf5c27370aa417113022bde648c50cb9187650ce7545f2b31094237930d9425c9c28a33e9d16cb92c1cdf26497a805b47395cda85a13cfdfba6762888d6589a5bdcf876e4761cbea58eee1aac5350c890616573f431155e2131fd2441dfecca403c13e064063e1a9fdd5794d3a1a040e276a74698b5caffa41c4b3621a6058b0dcca19901b494a1695edffcf9ab209442c766e00941eb160cd0743fe446b476da93f37c388cd1faca0aa7d1c604f099b9fd6f6a30ea1d1294d1116249ed112d5f7a10561d94c8b4e20ba4bec2fab016b7a633f5feedb464056ceb156f117f8febd633a4204310fd9f1b5a82d3ef100b3af210a62e53f75b105d5ed4a84e084d5855b40a81501177fefc86577185d0ff9cb3bd9e75cff7199ea31153ad0b23686e6e07d54488918dfd5068de0cbb802516acc66a48bcc8468caa8d17f444cf7c28a995641517ad1c2bdd5c7d8ac660927f212c7d9c67ba40a3360fbda34c07c30002adb224d322475ef4c53181b370d54d3d0b9095a91821b9cf45b71a70608d4650e6801e39a8122f18346f758e1051af23385cbb3b7e3279c78c53366dfefef1ce558ff09573065f3115f6d5a8f001b540ebd71ee68323813f1e3e02569eaaeaef6f60e06c4a03c0e6f0c02785103 false +check_ring_signature 248dac29fedc44a53f6db8791910449f27a0e4dd52f9c4801676a852327b922b 1364838c4ecb4e92637cf12a83d84af6db352564205e3af7ca3f3ff158bc1174 2 d0bc4690f4fb274c4fc876f0e73c58798deb8ddb37e2b2513229663bc8b2f420 18588ec0a5a7e0548fd0c2ca8344e1bf88ff39cab765aee2aade7799fe8fda0e dfa4420f366133d0a7719f94e5eb7718bd405a7f32c281f19a445c9095b3cb0c2e7cf3ede1bb78f9417b8317df52753e78572e160221b164ca6542cdc995fa009832de6f23c6072a0367dd1829d54af56237118c5721187b597f4e79dd80ea0083fb1e9aa881810e865d2c7c665c9cab493a87c6544352a65ca4289340bc720f true +check_ring_signature f95e5baf6a08024b65f014fa217607397bab05535809805bec85f8bab3658366 162a0b9d63b50ebaf095b998f93a7328a91ca272fff07ec6ff62399d9e1502fc 31 71db57ec0610e5ac014200ec8ec6f4a75b34cfe6720261aac1e6bc790d8f35f4 6a9d97f151bf663f1b7ed094af6b0f1a87641e429b8ada1d804e847a81d71467 f2de23d7ea7fba0282d0f141c19cc94befa4d6b02f450ba268493ebd8dab01e3 13b5eab13f677e9f8d4451603b1548af3b51c0dc730f6cde3a53f2dae5eb2c3c a984f9421f6abc7162dca2d2ad25e0abd8c7103179c522ba828d12794007b30e 10d99db94c7710d1ede4c0ddcf2643d9e97c84c176da47c929caf4000bdfebfe 37592df9bc24aaee411962258cbbfb7856b64a571adead6e1b26f65b34305f92 7a7c077bd4fb8d0a2d255623b3b7469fb9db7aaa11b4cb207a1e3d9484a81d43 953c5062882d7c6cf4cd32d2d8aecc8b11fa8fef1ba79e2d50fe187e37f87504 bba5c785dd44294bb94ef1f06a3d9c6423fb7a5de1a9aa39393131451bec40d5 e50bfd83a4671bf4a014266b476baa9de370c047fb730484925416928d1fa3eb c323c2f63936aa3efd6695059c38d39bf924112602760c6f49f4ecf051163f42 badade1f0e03a00481e95b9d930334096cace64654dd3cbbe0bd3b1b5f8fb9b8 53da68e08fb6cf21df269b52e1cffc6edd96780b5d663527ef92c7c809435787 f7a8f5149e0b338556d4ede6a8dd276a0a8bb9aedf5296b14334a1c8cdb03470 8abaea5296221f493388682c578b0be4873c62b35fc437b22b129c6614c26191 c32ff3a2c016c5a95b11cca1b05f29d555b781f3277ec237907c34e4f12fded7 adc283e84fb187b510ec520d2375131e6eedbb063804f3cd42c83001552396e7 954db0cb9fc7f9c36028051dad039b4420d6b2ef531b6fba92a95f6b69192d9a d6db2854a901a86ec479eea3818badf3764bf27e9053ee8addeb57f114712409 a1c5cbbadc9436139d45bafbb483e203f3e6a74c2d96e467bcf604a6007abd56 6fe5be5908338f71d73feef7e60bca4e3b13429c5d2038a1630fc780e171706f f1a1bf5791ac8779d3429291b7bc7108379ed60eae46d5ed0da8ba80d08376f3 1b15a1dee68c5f985d2a7a412469178ecbcdff445775af1f17d17be5f1f6f56a 461d8e06ae1e1f00229d9de0354585267555bebd92d70a21e78de227ba3a0c90 d83826f4dc366b1525f8d51b63679ee937f80acdbe1f1b8560f1235b21f30b4d fa6b21e71dd3daf9a3f9c1a97712c1852de953ce8cef228dc0d111908d5bd344 2cb04f0d0fa7b0dbfabcddd90ad2c1da722fed08f39609f1e077d2aa4d1b215d 62ae94be62c7bd53035cd53e98f9186c94ba826dd01ca5fd50d8f70fa1eaeb00 7ed85b9955cb777e1152e36530229a9be8b1a214f69455611fd5bea07a158a0e 578c3a7665933e305f594bdd8bca5e6a84a0808835893a1c49bc276efd29e054 a34a2c2467faad2d1f08056690a8bb1eef91d33c3ca15cb270fd6b7af4dc050cd3315c45c57c569dde3babacd71d7ac37098d370ea53585782a0be718839ca01276e95b6dd238f10fae0ac09fabdfcbe0558790306b2341175e0f61fc565070dbfe3a58e171a90d3016cc03f152e1e3eafdfb68ca43bdd5d326747cfa58e9f08de88f1b417d4ef5682031bc62971b6bdd48459f2144517479afbd1bf5590a104d638eeaed45b923a1a2fa1efc9055480356f494526947f99223413be6b2cf40c8077573c67bcfe0d73ce3881ffc18bba54a227f46c5d4a76ede40dcb1b0cdd06eddc06edc84addd509d0a47e67be0e3a30a1ae78eac34196406674ea758b8909fb7f7a68460b4273c3a31542c63b485a16bd82abb01bf7d8f9725067d1cad70434113a087137a5e294376a3420d9acecf40b3108f784bcc8778d558bd3519809204f046c4c3970cae4c6c2a0dd66ae661f1f38196cbb39a6abd3a478b9fd770cee2d926f839600af1f1b2b30db4b749b90f67d3df08eeeb437631ef8e2512c0511fbb451680dcf5940a4331fa1b78f34ddb3491917dd0bc19fdeb84ba8045a087f0b157aff8ff9a00ec73bfbc308c322b9c42306a8b1c9284442fd1887613808fb830181a992c3d9f0073f1917d8ed9fc9998ae041d06ae1f5c557657200ea033bdfdf42aa3be166d7ae14936dae3456e58433dde6a9ec386f0195da9883800bb75b46dd79439386d2bce031eb600a8409763700f89be341250564adc2420500a2b54c6e3e4ee2bf28645ce251f1574fc4172639735af4e03fb5d08a02181a02e74f8e94187075ece050ba8291bf5c338e8bd6dcedbbbabdfb0b713878e0fa0cbc02860930837b1a3f3b69d3d37fa256d3c18d19f37c7a4150adad521d17c803e1c47f54d9fdc6f089b9f223f714ce2bd89d1d249038999896b358fc00f057080a85b962c4c8a0d1609ef1d0a099c8334e3b24ec018748dd050ab087b2ad230ba835a864e4134ab6ec965ab236306164f4e2fb2ecc9c910822bd26e1efe1ae0160b651cdf85b5e0ebd51e74cc5322c94750e9f2609fdc3e71660450b55da140ed51af968ab1491882f0a6465047e49c313c63663012e2ef8257bf9771176bb0d06dd1efca3a8eb9ae27d726fb00fb6605b215b5b2021c34bf8cb314bfc41a301118e41cd96a7e575de43b91869ced73a5c812b743642aa3b7aefa1e83052400a509c582fa4d0d6c8b4028f883ea919883ccfc38a83bafa6d3e000b65e8d7a80f92717cecd4e4ab7438604cac9f32cf04d463d56a7b3759b5b4374b426cf778010bed6411fe97337593c1a45ee5a5275dca7785c087cf430ef800d5193ec8d700901fe8ae987d91b3197a405dbeb155060fd2a7b5da667e0e3b658589148252077a9394bf94f907640ff49b4ca59ddc1100f7d82ad72388b0c2d5a9d137369e0b39d7df9258fb480f07f3b676ed1f07af49cbf5d47271486dbf789c28c6f3810f27ab288e9d2fef1b21792760a696842e244123f5681d266571b25cfc71960a0c9385739a09788a93b379d5cdef4dd0ffbf424b59fa78ae3bffaf143b9917480a0cad3bd83ab1f39ed8587e1c8b50e92af259a58cb85fae23afd0be256456120857c2aa85d7b424ef7ee7fcfcb9b9cc58e9d3aea8f4a85992f772f475473c1409f3ab75d104c1e6bbe086b36c034a1f89ce65d1f6b0c57f3eae3505e0dc90c0045b3bc80e348fb16df92edf2d9e076080686ed43a3484df05dacf52851fcbb108759b41ac0683aac7548fb941bb6d4ca583906ff599dbec17258ff28c506dfd0e864f234625e564d06f008968ccc876085ce3c30fea740c9e2ab581ef3bb61f0156cb9a35628286856213361e05d93d9b5acaf6b7daf725077f062fb00be83defbda5e34b5933f0a8b5b6785dd878665cd726ab332f2c1ed2332266d6932de90337d3ada0cc94327962d4b7dc26c0c6a25c5521f7880af9e0731f5be67bc1bf03fb5d2ca5145e0efef5d127150affe4bd1064eff3339500c32870adf070765903725736f56935a9016dfc39fb8eed28ee9725dcba48165e9009c1b6d3c4da7203817d68d0cc2c84b9f84da8f00695340b17190f7140338a1c2c70a6936b73f605b0502172e1c92ad6866d3376d78863f253bef2587ffeaaf772b51f2c5ecb1508202c6b1ea475f846c903d4d90a22adfd4b8a5dc6c90441f3092d8f0e9a8781090308f8b03db01595a1ee830747471aabe655c47e084f83a5c1a3138fbc8cc00a7067d9bd0e9155ac6a8bc1b836e5cd32adc05753f0e422c150e3d613a800640e4d69549cf1312752efc14c0653c706e06fd15c63be066d9570b82ca1d4af9f0e257ed09f243dd44e6d66f4e8d2373e6b1fd5ec75964876dc0c7ed66b7099120291141376d2cda305f47c6a2db66e8a823005251d06c62891a4d4e650db2d750046c520cbd1ea887dcf97d45cd03742ad937c6c08a60910202a2db737ce80150f8a86b26d65836d41e13c0886376ec345dd4572828c913663738ba3fbb1bd5603836d6fa23bca0d5536009492d2b61d662fb7c8bbf3a2be69eb0fbc36b14bb70c3f072d0b19fe79a5b46564ba33cbd2bb95649be2b713565898ce74543545480ba913123299cd6c1fb3d50323312d64a14cd96f6d50ad7cca715c47817a65b306deb944b9b344533ebb5631cfe8459d9a4581d2ee8153d3bb6864a2caa35aa10243c3c677d53e64070f0946eed24d2866a48d5408bdfd6345a83b591357cf63018ad7cd53947f7ea90937fc89a4a116e85d927acbff42dc3ce37d0a3be0acf70d false +check_ring_signature d0b2b74fc78c1654cfa6d1a1c21ada1a79ebca4d23ffda2a64bba3d860a41bb1 7d4223be483a6818f5dea16f4bc6d5869e0eb7ce4d2b39d5010f2427442e38dc 1 a8692ccf67dc9b85ea32cc7e0cde2d1d5855b641ff13902a84cc9355adaa5a82 2155ce9d8742a42b8c5ad7ddb3e2257a0ddd8c5af64fce94f05a5d10da63c7c01d481ea0a011bda160257a9400702cadd3a4bfa6a0f2bd2c1b7992eb4219200c false +check_ring_signature 0b8802c0ab57358d72f08c206bd75d3dbd6f818e2a3d2d5d6690aabe043f8688 77b948673ed7a7d5ef928b24d0ed211c855d7371836e3d71da6678686d006a99 17 b57940c3a9de4c6bf8ba42e6c315fc4d608fac710a7dfc01a53fc6bd5e7196f6 b11db2a06c3204a2ef49ef85928d0970d3fd5d66568cbffa4333c4d6cd879fd1 051f5044654c9e544d7d655c749caea41c5b8bebd1633bda7fbbe0f293d42539 731198c0262e10921142d97e6a1a8f76ffc1eb3a6d85398e655100a0492c40c7 174780a93174a5b3a63eabec3154a2a3d13a20073a64643c3990210682fdfa80 ed0515ebfff1601fe5c34018832b4a80f4990aa60d3d973ccc3342395e7f776d 6cac1904d89cb5a85bc6fddbb6cfc6cd018ffe80889efabca22da7a36c40b22f 68d2947feb0ada88d3c678a8eb0723d62684408a0141c87715888935bb8ac456 c786195a8bc591ac0d15e6af8f45d08b9f3e907d2e6d9bb47a7cd68db53591cc a5aa135b1eb56f06bc3d66c1f4192ba1a2a8c524ab60bbdd50b0ec5e9000ab6d c946a32214d53dd21d55dec4796dbfa7f5de8391572f563485ffcf0cf8646bf7 3672245dad57e7289533f4ccdaeab8dfb8290520a7f14c36c60330044a3986b3 0ef9b834dc6ee7e50bb30101d7be4d58d9cee6c6c57fd96dea43256c2f2a666d 28e63d0d3fc79533406e1ae9002afd40e340c7e81d4ef28a1af0a0dbfba2b6bf 0aa91ea183b8bc483dc3cdeaf807d098234cb29c812308aef94fffa2d46ed368 73ceee34a8255dc126ff62083593a7f47a77ae6e4cbc4daf7eff755a090cff58 e5ca539c8f55d7a67ce23721531740d1eacd525044d182ea3d25590e69324053 661fc20d1cfc032741f2fa4942652a4f9eee7251d4d308547fcbe8fd6b7b2e05dbbbf8d9ab7b844b0c9de0faffe4c008dc1f436249f3a56a98c7a8fd1b85110d6dd58f0be813c42011f395b0e6ca6180b64cb5078eafa356d31aaa632b247001e63ca14b41ae1e8dfff9759cbdc89fced20fa701d3f6f68336582b69a4a50b035980129f29bb044d77d59c4a8d6953a6360415319a31d812ff081c276bec750d3cd67f4fc235b87d861cb4205123e03f56ac2b2e519e130d130fb719037da90373dbaf6a362d95d375aa16cce6c3cba0ca2be1ff80e256aa837c8290ed146004af2922aa9f8003f7181bda234cce1d8b6ca0a2cb4abd6475a7d9d64a6e4ff307990e8e4a54d532e2b7126e648331ebc916881f8967cfd36a67c22f5bea34880debdfe7bfbf32521a9c16b1374111eaa6244337306f8f8043feafcc6076b4590f5ead4e400f54cb22555b1aca9a5954b43b7cbe7a0b925ef5e7801125ad13de0a2d5b9137843fb725d56d4c08c19bd70e8294409fbb859712534029387e3678080fb1552e8eee25b1d5516c933a19f5a691d055b59d78238e3cb1796967550b0091e87dc5c565f5644750be3a55fd903635d704fd1ae9b046a49f78d29f5fea0994e0489acd0f065220fbd02bf845b4b7d000cfb7f06162ad5f40f7c88ec8fe075c57ffe22d7131a48d307432af31286a9da47bf3037a52f88a77e882d054d5001d4bb5b7ed029b9c133ec5a90dd58b0cbfc155746a3e5a7ef942992bf66b46052967d48f2e9e4ab68e9fbdce806078c45971d5704b3546db3d2274a5d91ec907a0d1e8c15fef89573fdf97148cdc7c90b7ff77b4c33906cc6ce8ed2843b63e03b2233ecd01548c167a027996040798d664717219bf5767859304e5c12d5eee005567f7015cea4a718c7512476440037b473d7cfd2b7e8d8f2d0cb239a48edf06944246c2cf659c33783eac1f26bf9788df5f824b6c6a3e9e910986b1ea03ef0f7f3855763566190121255d5e35f2a8c1eddec3bb15466925ab0c358ef088bc046c85862496ea5b7facc85381a7117171322caf917ce3546bc09caca945ac45053083fdcc8e5ec8857f03d8fa3ae2931aadde4e4e84cf222f8169fd7744194000c8203ca71a9222c2c284979045364efc4627b1224da14a7a38a4a63f86e1f30bf7e314effefb4b75384c833885415c6f9175fed98ea78d7f0d99fc5e8aec65062440e4945dd802a470ac3e4e038a2eb9bc5e20ee6d14d1e02851855d9c452501ff3a9042d056d4bde35dc2a52b2e680d909a7ed70c122f7018d7b6924162ea06f53a5b37162993e582de637ef58dd6a4cb595b30dd627dea0cc800d0a30d93029998e16b8fd962c0e43bd196009c997c9b388877958f26c2226884e512dfdf05dc8c6d133a5a630fac33a60c6be223766603a3981250c3adabc01098a2a05a0804fc8009b31e8c2be99dd07222fdad3e0ef595431e360df5112dc10ab7aa8e0c1911195f7a77de35863b15cf16a938c2565d3c0345caaa8e5fb312e7ca88a30c true +check_ring_signature fe42443fba2113d2cae22ba022dff69d8b2a22bdb3988a89000f6f37731dcc2f bb5a43f53fb22f917e41ae9fb4362d002a8e0b0bf047379223eb98011c1a8778 1 7d1fcefcc2eef6321dfb2824b6a3be42e5830e69b9bc67afff8fe5974395f782 0bd78de5982403ffc0cd28610f28794f2dc927fc648a833e4618ba53070b9c0dd77a09bece95d7ca8f5b1ed6726a61b88d8f77d360cf3342ff139d210091d70a false +check_ring_signature 69f06b32d65017af9297789b81aa58e25178e22748e29d93507b15c9e9728c5d d2771c37351783569ca4f644b17ef475e531f7b48f1fcd6c05bc03de03a50fe3 3 8ee4d5d0c9207a212f4f917e62dd846b38ba1cc9a6d4de82d5afc3e394a94a34 741eadaba0fc310486653128af443772ee69b34de219eae4fe34eb60e6031387 485ca9633a49691954c1e8f62ea4d58290fd2da31465648503c5034e42a96adf 76a414d5f8fc64181032649c1fb1a1003f4bf93744eeeaeafb5a55c4934d280211c9191b5ff47b929095e4f3d279a899304b55d14cc6331e5161a3b1a8f53707384dba36da8424ab21ac90fb699d796c6f3e6c978e8785cef074d2918211880bdea26b626f07b8c26607fc4714674828eb5d4c53b55603a8b86581f082b8b6008a61e6ed4bf2b903255b34e5a4fbe766475c2ed633a638cc236443e3ed06cc038d4f4af2fc3601ad957d267cf236370bbf3e6f255a7fba6414ed97479eb93208 true +check_ring_signature f26dfc47c86f84d71f1389644af4a5add42bf97660f3114ff93900813ae0eaec f0c051667b09380a7d8f23a0967c2ce0a4536550f0a2a3a8bc49d732b75fbbbd 1 288b180818b0641dee6d35315674f77d93fd121fb30271e099eca2f3134c3c4c 434fdd1cfbfb9ac40c7b9886829c4945e18b9362cd0f1479903528ec0a18450c5874693ff2375d01a6a178de42a3d710f969f662973c487a6fd08ef9eb81715f false +check_ring_signature 6c64b20dc86b3a0a2f6d135a56317ce793866157d2ad1fd3f9e8916a2ab7a588 c256c20c76c272816dc36df1135e4e9b2fadb541f566f3a6de01923a1d141434 1 38d112f6e2fb0d72c16875c698d2d17ecb5921cd475771f2d95c8d4d3bbefe1d a94581187f00fdd0a2e69cbec3a1222cf44d899cafb3bf88ace8de1b773b45994458118bcc93e73f1afb7a051ad921bbf2bd284b91f4c00985422a7bf273ba69 false +check_ring_signature 1ad70a621e382d52bdecbed52b753de51867eab9f222bb9d2d3fc4e2127db328 f1b2cc287b30d9b4d45b282b379ad8d19dbb6cfc38cf2828a330d0ac6cd9ae95 3 f4784bb4d4d3a33dc6b52a11070b9789ab6f298ab02443b92a17c6a6013a4d9a 5d7ca82b7e86735b145cabb02d219dc11a1181157176d47c301f4669ac9dc3a4 c124fc60a6f13f7049742254763c30c2f6148a3424c5d868498950c064288df8 b7d9a1de6a3aa9f67968c06fb7aa904705ee8f4ca13a493e39679f8bc75b100ed4c6b930e8d9ec2a27755ca8a1e21f9db6a5af3b724607ac343e147d2bc90f0f9d7821fd76197a73792da751ac9adba445b32fbb3970f96d30e5f7b5a914b806037448f73f70c39e8b2d048b97c4bac91deab973d3082a013e599234c9b6d700b378b6eceeac6c02fae07bb7699ac78daa2737c916acb9c7474e9169bf058b05256f7330f2afd7f5c0b4eab011c5e7ba65d299b52f75623351e4d11b67e1bb00 true +check_ring_signature 0922a331552fbeab8d9d06e1e1975263f3be39a425b96b110a74901519cc78a4 29d7ddb88d41e4ec3bb3f285c51c17c68e6e2351ba4387d00b07cf07001afae4 1 8338c9998da0a9fd57a4027cfa56d9d0330363ac1e5f6cba1f5196a1e9486100 54ae509e701bd133a0bc882758c0bf39b06d01c540ebc13fa0f4fca29cbfe700a81a30fd20cb1ae4e22f38d17575e899096b2d12d938c8bd2fab69bceb682c0f false +check_ring_signature d76b07451512784c270ed0b0590af44c0d3687f4fd1f8975c5599bfb4e0e9333 504fef7ee250b524f60b66ce14a48cf263b0fc0fe028da409ab1dae78e8330ee 115 998b2cd72891ce2683420cf189db025d951121a464099483a3c96f7e60d16517 20880d3c70f8d0146f0b199fc1d5b94de5e7a2c3d2434806fb516ba9135bec67 4b632df02c5b80312f5bac0c49ccb4e9b2d704870426563afdc4cb16984e1dc9 87a27c8ee02ba36b9166a47136e4fbaf0e4681e8c26ad95981ccffe9c32fadf0 005b0e970fbaeaf521c3084cd7980525d74fe31147718bd05759a402120e4cf6 7fd773fa2908546f6902df5a3e382cb281ef7c2ff8bc458ad5cc597fbbddd100 d95c0f19130273c2d5ce1b6527e9b0f7bf94ba493671ee014b8fd9d2d29a8fc4 b4b474d7bb361e12cb1e50a8cc829323efe2bd1a202472a812bc31a0aabf9c1a 799cdcfc812c88697f95b47c176db05219c0a5c22294fa8d80bff5cee8a9a7bc 7c86f5a7a0467e4132ae4a5b13b698561b4eb27b567e232bb7ff22aa6877123c a1e0b295e29eb6f0d5210f58968acd636df227695598f952762c06c49de3a09f f021e39c29ca916289838a5defb605f14e667ac6fd42ea80a79db01e82263274 95b8ae2c60e9e99357138ee161f740dbe08a26678555e0fcb9626e0769cc3e45 07c5ddb7d3505541a83318a7a4aef6ab267bb481edc65f87176545df034516fc 61396829602bfaee28edb612c54730860ad3c8140224842a2895a3bc95af3515 3e30c29da5747d5ba45973bab02879daff5a102a78fdd8d3446c263b0db56b2c 144f9a990dd11f8b7681e8695e2b86fdabe913e58a3df580fb311e7b20e82ef4 fe689baa8a2dda7a9dc8d79fe3d909ee9ce2ee4370fb2b81f2074e014e13a937 7616d08a7b88232a5a2315e819998b3a771c1e8905f3d6ef0acea7a40de5c46c 07296c70bd6e3dcbb761989b2b187ad8af3ca81d7dc4d5b8d62b1402aec5994f 48fbb68e542801bf966f9ae8003f485ca46a5b2b9e6a5e20262bcafe411a8a84 a149eca4ec575c953c385f623f31bfea9832268ebb1881424bbbf8e4f97523eb b423ada5e9c84d357c897e5318e473c6d12132add9108da814f883e82eb54ebe cfd6ce3fe0d5e6939ac1a65b64211c47f3d001cb9895e7f328f3f23bbf762e81 e323ee7482a44c0c51525ca649c43706d61a1449a226c4b9b8c4da032915f79f f723ac1bcc41331ea7f5d775c2de3e49abd547db6416fcb8a30980600dd5db25 24d1b357cd2c89a6c85ce6a84219dc35a1bdd31df2367ddd7d0a876b2d89216c 8d9e292e415824e62ca9d1b3d215a398f473421d9a7e2434d008f757b385b6c5 8f8ef4f948750d25f64f0e288c692a0dd2f2bf6cecc5bc05261e0f2c84a327ff e9305d8882a74104b9b4d6a4e36fad1b94fd38a05f0bd6277a33815d89d33f9f 4e26e1ef0f15979a69c293209271e4f419d3ed68c8a95cff3f9691113c08466d 82d5a64aba03b6c8762f5d3f4b280a68b69c2760eb711623927bb6b24d56da43 ea3c3f053e9cf665f84e8388fd88b256534ab73ecd3bfc9163b05ec99be7468a 83b44ec2e8fa6d00d98b5c0ae128497429f6fb06d0d6d4c4f0834d3dc762a396 66d471d7e10fa8d57cf976d0867999ea456008c365c8557835f0647d04da8559 d1d0be1fa4bcc8f9fbbc6cc223b433e11e88b48e3dc33e4b144632c803151bbb 3f34c1d24cfd9c7257b58b64dc196ba93a341707ab6a8884be53eec5a0c11db5 2a5f4f3e15fe4c34a104650d0be77ad265acec0741c475ef1ab24dfcba2d6b9b fe6a93877c821bb10ee713dd8b295c722ea7b300570f813b481b0133c77dbaec 5f2c09534d9cdd47f63a3decbab81292bee751d108bf05cc81062701b067d96f 44679a406d89059fa284eff725bcc68b35e0601441db8bc7a2bd7b88fe600bc6 e30eb92ac244a062a993f84f8035de0f149fdd196dfb422b3a0aeb1d44dfb50b 5fe16faebb1a059fac9a5de0e9cf6807c356a4815fe757586309ab94fe1335b9 0e2af9160146ba91214bc15947e6081f97cf98007421cf8cdd9803dcb023f823 480c3fabbf983b71c4ff693c80b3b4c8e1b63f24ed76cbd39f1e77d2090fd9fc 1433fc85db1f91b6425907855a14da59571242b9e23bb5443d1a4127304e0ad1 5e3242327456bb66da631321479cf5534cc95b1a836b24aacd8b6cb5ab642441 1eb37704ea00706e3a185f4108610aa2cc372390cc7ebb8a94a1ebb2ff925dc8 853fe32dd779677d045d830c058151dbe85c45437a5cac61474746cb7b5c4b31 19d61d88baf7436f4f50463b2b32dc27a623bdc202fa2c9ed39f4728cda039b6 9cf4e8a2f6748be6bc0af7daa1de31afcc8dab246148c9d4b065470de0b34292 2a9cb1a173eb8f45e76d12f6d9ef9622f65957cfb2562f1ca9e2f118d4e116d9 80b911d9140187bbec4640f0685d6d34d7f9e7b71c70024b7fdd8d693af814ef 1542d915c44270d6135ec01bbdca44d6566213faa85989977ca30c5b86305202 8fbb5856b441e470e27d50edb501b2ec9e966fe7f7f57cc5aade4ee9f71c5800 1837b84b20687529072e35ad69e64212a2f3c30be6b6beb81f346dc58ef1f697 384e034c65ef606f6bda6da33ec4387d386825fdffc221af368d3f114e752e50 12743fcf5e219ddbe658d367d9d5774d4beeac0e01dec8c05a8e6eb907e21fd1 4f92ba3fd6aabd8fad4f90fdf73943ea819539fe1b6b055204e15b288dbeca62 b80d0776468cc27b1b3734f54235702f271bade4bb506f15595f489de78972b3 9662a19e192a653f5e31f9cdb787638a0b0ba0c2415194454427183530f18944 55bb328d7a780002bae3e4cae47b75d8dd55fbfe2a861f791f991d9ad1406007 c36e99d1677c07c11a3e0780d4b9ba0a1cb5a6b43f7f4a0e932b5cabcc0625db 7f5cbeac9ec09c3737ac82a7a341bebda9988acdf0ca4205c0b5d3c0910fcdc5 6773957ae7760be105ce42cee34fd443ddc1b708aa18eb2f7a3f7e6bfd1aa521 1f58355a172d1f39c5e409c7aaed092c956c4d957ea0b48b079551f23ff3642e 96eb6c18195aa44a2f082f926e979a1f87340235192ee0c17ca38202ff0d690d cf51999be6ec0ccc692a7e4a6278687a7f90e35214f6fa37e50d62e6ffe43ab7 48ce3ea159943d6a8fdcb2f960707239f3ee21efa96b6847e44728622988b7e4 ceee52dbf2c2d0d62fa88c581baaae85e336bba9a46754a04d373fc1da734d1a bb6d36bd441e1aeb6762d9c1f4b7dcc3a8484a4102e981c00189edb4abd61973 39af4488d7dc77630e7d2c9e5fae13392a7520fe7cd4144a9c2ea575e27766c6 2a8c84e15da6b6f375e0265193c58dffd4e76a90bbb3131397031b583189b0c9 f4b60514fa6ef1a22262932c139e7bcca1a2d9112e78edefd63ac1d32e29726a abc390f7ee1a943ebd891bca9ba21d8457098d1dddc5bbcf2957bdad0b66809f 993af7c4c0181747fb3404c4cda99478ef9f472e6e673733ae93edb1a22bee6a 9f5db7a2e1e887c24011a13dddf8eafb357d301783e1f583d12bbcf1587197b6 fbd4ffd3122ffffeb83aabeec6455ba9e22747549f29af170b0100b6f78cb774 4c1db9edf9a11ac29f60108ef720f36b0c8f1fded3ff4652738fc63bfc053ff3 c5945c181b18957773868671189654ba3fca84af1923918b4c2ef05cad3dfaf3 fecfe7e5cbbfb98afbe2691af18351f92ad993d58cbc92e7f4f199576bb6ec4c 064b73a700b6a5601896ee01b5a37811a84ca672284d9e87638ed09c3c62cdd4 97c12a13feb440b4570f54d2c33c78d7a8cdf313d29f5880af532434d1f7cd40 8ac8fba13629c6a9006271b0b87de9c9b52e66124e06088984e439842b23b06c 9503a9126614429fea21da7976a002c4148c6fbec409cd5a05c51471785c3713 d39d2dc06ecd2d2495da60208735f05b8343c2da6d8c751c2ee959a26c8be931 e862e7f0ef17e0960b249b2a56a5ba77cd9abc546a5a4195d311200e8f36fb61 a31fc6c1150d144744c6721dfdc8c979d64ebe1d689f006221504c33e7ca6aa7 6c5db2c677340e3a050553b515fbe4a9c0ca16133f14b651cbeeb5002c89544b fa45f63924069a0ce49e99123c42bba198e0a902d65a793c6b9ffbc9d60b1af8 38716506628e686b7479631a080dd387267024ee3f40546c1ecb685734cf4082 23583217836cb98142ca656bd38648b46941534b56e3ba4c5685c6839b87b2d7 cdfa2b033cc30b3450c9b4603646dc7a239b9f2dbb411258b96985aef26b411b 3a3568d3d92ca849b90b535c20adc6bb6b8e7e5021a12dafadf8297b63610d4c 196f45052aa92f1b4722176d13f297d9d0647ad076823c024f3c5562bffac63c 3b1be8d87423c0b853e9baec95dedb528f8bf776852d0bb99fc3d9efc5553fbc 159d5b1b21abca02252f0e8ff43c82cd92e34d08e943017b1ccc3231ab4c157f b8c038a41b577221dca1561229af67abecbd84e6f2e3f54027c96d1d862d798c fbc0391ce4aa9aac8b9f5df2e3b65c809868df2cef18232d5e6b0193b9942e76 7160fb9e1e683791521415da5357fd8a66fac93f9ce42b50d2aa831e322a8552 e3a56e7b14387f0db47f661a7620ef8f85bae1dbb8144ecd08084642be57e752 d676c25f3d9b4b56a8d16ea40c330966bd9ed58c57ddd87ebcd149940ae633c9 02df690e339fd2f4a5d864cdd57490079fb992e44e7cc9910e57093f5bf5bcd4 15fb6b1b7809f32f6945d78bc1cd65890eac33d14504158a760c34c1c051b33a 6ec97e342db1c5fd0bd92f3777eb99856c79a33e35f8795976fe231ae7f75e73 090ae7d297128584cceb5c86c94a80a040d5578fb29cfb1cd13b60ec695eb79f 97cac9786042f3022792d165bcd7be1ec4bec56f442b90f940236ad3c70726e1 2f3c015356f0cad39319d484c50b84983930201f0ecf3a1c818b940569802ec7 2987525e6f2c9e255db76c2445d30196f33b85eae586818a25274e82d4f4012d aedd237a7ecd6744378033bf577cdb8a704f87ca9881887be1ef6663042eb700 69a419bd7cabafd00d973629781cdd104310146971e39ee34b69ceba66fbdd8b 80987ce42c38865f6de95ad8350615481738b67f8fdf11a9fcaa3ea6d82e6172 56435bd3d25163bd8ea4a1231db0864c8487e1a8ed95edccc74ebe3d397259a9 2f4cc593e91d06bcba61309051bc0291a620b195cadeb8e51c6bfad884b4f802 edbb737bd8e2aa6b9f5eedcbf1ab179085c287a609e085052bee19a5ef74a2bc f7d2b2d15801c8df6c2196207a6e7c68dbaf136d0a6d7992773b497cc3a9d70a41dc05a93a6c66006586f00675681958a611f85a50af7eeba25243f66d5c150ff1a13f294e561fa92da848264fd4d994cc0870aab96e79254726b2e8a41f8c085110721f6fb358bc67cf1687a6c720780b6b8777f13f113cc97c75c5dcecae038c0e3bd20b49fc04605a1c4fdb680e8e7e6d3129cf634d495872af98e7b20b0e15b1ed0cfa681ebd75cd0df9c51286661ba7f2a1c19fded9e612c186ded5780e52c3c4d608f5b78ef823e8b5052756076d93e2f9cc561a54651fd729af843608bbc00237106a409f6c976afd8c2c028485750c9572f29710ad77eee8f85b440f7511007c1977291b54095a7677e69a9550574949b8901eb83aabece20ec3730b3a1eda64cc38740049fb90670ce98c35add7d2e975136e6d5d320a3fcd8d8c034d085247c264edfc5ada44ca6fb013a7aa566d260dd3fe5b64dd5b5da9f0ca029f2c39393b05c6c0391247fdff587d68cf5f7447c7fd9c9839ea7e16a20bfc075cd9b9dc863aa1c9e4548cdd8d195ee2e9cae054736b4e2c66c66a7158ea0e0ebfedf3c6b8814d135c8820c8884be5c27b400caf72f3f502bbc1edff5a74b40231fc530856d2bf815b37b166b57c1ddf55ef796e2a07ab4ccc271dc590386602270998c81b34ca4affa76720de0dd4bac8b97c1c681f1f06519337e1faa64f0d2e17662556577b939718069867e3296927ee80f078ef09a2776e44bb8a50ac0f6ef5d28de0af457f4dc878b07921f7b4f2964825289a5d745e9046f1dbaf680186361affd8fbc1a4bf298a8ac1635b46d0b970426a86c5ffca3af6b3f87f620b95e57c8e56120cf6ce0dec2ff92b5a37d059e2a276779d098eeb13dd5d62ba0556ebeabb1877091840b9b1f1657c9be4817a98a297831774f1117109c663c00cc4dee5d08d5e11485caec565af11a4c42d1d8ca7c3540a365c2a37943fb19301fd943193f3e165dbc75158325717ed92a4ac68f0cfcb8745b1b5d9dc10c53b0765f756437ead1c17eaff5d672c4685b059189686350648fad8e9a8adcd0f1100abbf8b8c396d54552b81d3b81a35889a52ea9e64db90705179389a179ce5b508395ae41d91db0191b5aae20880b4f08ce82cf2e020e7fb6f3858ac6c04f85209cfd402f85db080cb81fd832a2e69c54b0547c3930e7c89d23ad04df1b0ffab00d866084d00f5b4d4d43b73ad602c1e2b47023c218e8708c9e17116e0f9da93077692d27fd90df80ebcd564f4b2d24f9395b47ecf3e3cd6e63201494626b5d90bf2d4edd3fdf9ffbae4446c666610f74ee29f441efff3c5d307715cc26b655a0a616b12965fa296a667ce88cbecce5b3f3a361a90b456dcb50ff6de30850bf80c940ef40cf04d9ed97767793ca63bbd0754250273552febc6b17b8f3a81c4270abc6bd5ca5d22f2c85875d3085d5e0ed9aa96b8391be6e35fa6191b18978c32012587096a8ecd382a82e3be4cb6c9b60ab007a3bf0c23b630480ef802b20596086dbcde0b2fa9c3f1fc9f9a1c1f1e66501346bb72519387bd5b0d752320323d06a6ab07bcdacf7c8532c77c4d16cb2a0aa940a475c1836fc4a59b3ad91b00a704538b5881cfd22e8b31aed7b85ed85383ef85731be0cdb478659dde1f72ceca0b417979b896af293b89927666453cc35e155fd23e3d7a47a3f4dfc9d44d30360aff27268d961d5a32e4a56e5cc61a3237457f6f81d00b87b0d2690896a27cd90befe5b3ff71b52b477e8a6c2704174ec5a7175a55e1ecc8e1b1c60d4e21f7590587ca9754ed10a9fb762cad28579f0af53644075b38dc8ea9e49476aec9f78e0327db9f859b7c83820c1c8156924d0228027d1c44d6f94d4b84ee95f490b081089bf58e13a08f4dea988b43f858d67cd8ce43697d1c873bf62850aa171ec88f0b97d28664298a4571afe5244613f950a60250579820c8b5a17186931186d6020f13dc2e55a6dc39f055cf41a70fb0f264a91b406330aed76642769ca4b4937a09aca53c6a0bee6da13e2df25c139e8525079c98ea3a3f93a103ea502f2f5b47037983f2fa4f81559145c9696a40444a190265206b38dcab62cd10c7cb3c60df0224cae3d429bde9f0d27ddc1c2f4ad06c750524ff6e5c05b664c0992da8885e00e27ed7e9241a33f5856c65371f4cbfe4fd17147fbe3b1d487965b997284e28095d418303ef91feda5a8b151499239d4a9d1cea0c9c020ef42521dfe475db6c041cf97e523e8fda17fe69466185ab1a176987cb344a4e9abf151e6457e07f53055b71a74bb465a12bf13536ea90eb135363edb6b5e18e90f37e2e92290d77b5017d64ad354e1b3e06b85278b8d22bb60d587607d6ea5e81542769c9d3be53320ff9148476cddb7a590db279b111a552817630d5ec461ee0482a58e6f9982bf803986e62e2368c6c786d03463d2afd38a847f729a14be69e0ee0f09ee00de970069a4c6484205d36d31865aa484207429bca516992b49809ce89377867c50dad08b8947f56ff3f724483b79702359c12ef2f5f916a1717b8abbf5b33a25300310ac43223eca9aa00e54d751333df4648ab768e4274dcbc9193ad99ae11bc3b82016b41c7c7b8de3ccb9019481d0dae509ee569f4132afbe3d5187fe405a9cadf0d8308fa8fedaf1b20984b9d63421a9e9d1d0795d32978606da5e5bdd49f7d2500cf15060fa62849f8373e2570702141565338f5bb179525e196287dcba392eb0d51a2e5937f656c1b5b7b0fd1fbda6254991a5386db3532d1d28afd2c17e1810b424d349feb66731dc2cee5dba4aea5fa8f68f7681b6f0a28aefeb653df16800935c22d689f0877c406f3e626fcc0193a51312248dd2323a9afa48bb9198fc50470dccd727498f36a1f1967aae41d792dc99ff45ef5ed8efd97fe2d0e7fdb7e0a2ca6902214c833d6036da16f366376ca191acd1ca08cf49f0437b86729a78d003432b9eb3f58342f3d01ce893c8104d89851718cde1f987d28eb4dea35dfdd0e7d8e4ec675ce2423ce938518d96776237a231fe0973da0cb10fd5e65664f98078e9727745042ab7972526843abd96b1a606ac7e4d641e48b775e5fef4563ef00c89d7e195c1af66b1312af5ade161b5cc80079764cfb470537e23f4256c494071d93cdccfde566bead33817367a64f6fc34da74bd40929bc57bea9d43070a50b1bd8cb67c38d229fd19b20636526986d45925b66a5695b1bb97fb6d221ee920a6d3b36cbe4fc60ffefb589226786d7e976671e3993bff898ecaac4e12a40d20474b6909ad7df7214167c4c4ef0478998f1c67812f9d73a6aa2bf97970487140b22d3c0f155487d184eb52cc27eea9b9e1272bdf00b09dbde157c0ecbeb97b80e392d4cfc89ba1e65a5cacc63841d24a60ea8e2f4ae427ea77476fd5b223ea708b792b88c5e827bae3cab3eb7a561f27888886ccf2f1935812a5f32b3476e5f023b70d295f5217adfdd64d42c759c4af0afb85215282271a388c1d0f3c075cb07cd3d8cb8609e5f9675b07ea36c8925cadb36261177209eecad8abc2e2c5e0d06d4effdc3a2f263e276dec2be3a0e42bf7c0237818428418bc42e9240ad8cf90409926897de1c6bf7ec7102af27b02797777828f5afb8533da16735436399e50d7b7e79d4164ca21d1682cc267b43b83b0946243375ad3dbfb918050ed700b302bc0d4a09f0256d9f94c2010df5382b34861230c3a19c03f298ff575410cb3b018a07aec33bb0e38f1fdd411c33b6cdba92027520b3a80fdb5bdf31d94a8624067e3f908f5524080668b78e598e7f78fa385eeb5e5ed8b07061797736ca5c5b0e0a9c465210a9d48a669c9c07ccfe7c3c8b19eca14aefa90540e9a1b1e2faea05d38a6b499fee87d3f6025fab4353e330ca399b0785e1fd0d9b0afa4169837d07e887527357219a2c6f7bc865fc779478911574c29e07222915cb877c718d160c6abc9facf3e756f2823859ff28ac6a9dbaf4f6a045cada59e48da7f90c681a057e9d9ee41bc927cc423e9c6e99ea67ddecff5f2ba7c99e7c56e85070849a4402056030055f95e238509b0d88142f230fd738925c16362c7a7cfe0d08cc3c32090a5e54d1c5e4c9bba3d8612c811452d411be03e9910ce9e7aea6b34547028c02a803fdad222a2f82c32f6596b03665b714f1c7d469f390435631156cdc8e6303d1e1f58f6764fe4e5ce0bc09f1a22d1bda1b870c8c5b9e49b76b4fc8ff9dcd0debe3472c43dd1bd502a819627d1d82681f48ad2710f46540cf9687316d4ee20d1c6c0027e46fb81fa38dc65b5e55b13061007c69530edeffd3029aee101f370e15e212f10849357ef576a6e90973e2c5541c41db7bb3711028e3a82d6b378c048a63eca9f89b483e607c4667b3c7e84b81f6608145e25ffbed676106271aef060989d66b857497c5fb28531cb1926ad4d50664f2ddb72ef01188c23e6cceea031242a53697e9e1e213b44646f9b4f4c842a3ee83b24952c3f427f8bea8753e0326f9b81095a63070dcd23fe431b0dc23588d21397ac1de87b8a818a301471005d406d4c479d4e2946f98b1b62c95f81b0beac5cfd66462d6d91958e82f51f404f6513d0852f7a51d00ff2f02f9f78cda30ffeb998b0b5b1699593d4a381eee0c433d12979ecb608628830817d7e58fc9cb6c13a05efa6283590203f198a3c400f587382e2412d407e41af2133c794e71e7bd4acc27ec5126f3149649f14d7e03e2be753ab604abe5477bddf298673711ff30925ec3698598e7ba01d96920c10640f2597df9a156c7b267907153f59dd80ae5028f37da05678f2abc9b0743e308d9e26497e39c07a30ea64076a13ed690af39efa14360205eaf0e5dc26415850ccc114100cb1783685f47ef9af04db42067f8d46ab5c846ee19c121bab53da90ef9bae90ad4251a534a099a2154c19810f622d8190f614c9809437bc14038780d4d31c80d2a49084bb8c3e87bbe27747cffdc728849ee9c63e5aaf83e5bcfbf0d12acf0684cc22b21e199ae542be9c6b0eb100a2dd504c325fe06215162add102968d0a256ad90b849b1504936b49d9b3be5e12221e29f400aea0670eb65cfc09afb7864b84daadbd7b1651c556e9588227f8aff51c6ff7ebfbaba01cbe71dc03d7018dd3f59e3d11f835b06acf96bfbe25ef26e99c6a2be64cba8bc576d868038812674afec587cf91c6ef612bb5a09c4be0a90c6162dedcbaa3d08cbc739600efc60e071188a522d83fc0e6465efba3eb77a99239b579bbdb42379e0f37080848d87ce4404fc1963198551bcc784467d7e6ab47d59ce297dd574bd9e35bbf073232c9de2784142f89f1266532fa23b880a4ecf20943d4827623ec1cf396fc0461413b8520947b83d6f86053f3402f319f00d0feb8ccf1a59b59d36db700a804f57a4b9a13164743308441142a5631d20e80172a764dab460f70f14ddef15f05fe9690a5a3f32d4ebcac7ee9468e0fb481ea83f797127dbbc5b1f1efb632410bcd584bb8afe2adeaed1ee72ddd03191cef120662957ac5832a2a2c64d1e9b5004c37ce597922f5f97cb8682c05506c792160c448e3d040818db14dbd7ffc2e01ac28440a0f272e87456392d59e910854692bf202785a150316fffb52847b5206d751f652ef4fba60bd60e6b53122f861b2c6fbb33d89b5121cc23dec38a65506cb98247fae83fda697fef9b34aaf76ac0ccccc88e90d8d8a12b06f0cbc68c002db17c8c162d1f43295435c67a1fbcd1c8ab325252e71d01dc7ea769548a47902ee28baf4ae6d135d8c1c39fc76eb6153185fa2233315538a55b3dc4e3f7e670ef7ff7a3223045c72ec97d809717d5de0039c45d160514ac39b16289820714c0cf6a18e11321e2a7c9ef5a44568ec9f5ba9eedb5aab94b3ee71e76c04ca60c706d95d16d7d3af619d39f1cd6805f0fe7bf9f36697ab8f1e51a84c02e2d623540da20cf34f794cf35a126b96f8f305ac9955883c6842a152714ed35402738ea70950b377a3eeee6b4a8ff2d2d92e87787f7bfd82267896ba3614ea386c805df10bc3c1651ac303a86f92d88e34da1cc9a2396f45f533162e6adc7b7e677ae47508976795959866683ea6a7597ae5c7b171dc4034904fbb1afb8a26b6a69557860755bad92f340a871170639704feed14c82d1678c10fd6b95a17d3d46399db1b0655ad4ddf19c1eaa4636e3500434e8f1ff073bf9f3ac3215f618d79e65ce7b3049ffbac944157cc55f9bdb008ce028bbd916185a39d8f0d6f5d76c591adf5dc0b188ce72fffc31ec49990ada72bc953af329b3030149182dbcee3a20b0c0d9d92e226e0733b34a573f78f96ca2b07fbd1ffc7bab14528ead6116c0229f3f6dd062c7d9295b25b7acd4a97e63f1cbaeeddc08503eddeed2d7e2a807e0a4ddaba00dfc89d587a868bae42b6e0a37eab3b444079c334c59c268240eefd035afdb60551fb1c81ab39229e7ff1fe1e8269aeafbbffffcb1599716e6a888f28adbfb3001c4545915433515b5459c71c4d605e4f6a5021098d3332cbd23528a69f6d7a0b3de38e3301b822d59a6c9b6f0cb91b055185e17d8765567f578ca50faccf6b01c62a773dd33039ad1709640b91a8252590161fdad42d154cf0a52e3609089b00286e918bfcb8c43da11e2aae289811de7467f55ef7ae632546bf6f25df10ce09d8c0fd3de8b5f8a068529d1ef9d7b8eb36f975e2c1382691c831f7f0db5c9000404496c50efbaf53ca3c1b30f6f2ba73b5010fb0ac83897e8e5487b5e1f4670512dcfd91d6a00a6ecd80a333cdcc0ceb178f7201bf36f2e4d55194f3f11e950107a0ef92920260c4d2b4971401fd04386b0431b2f91f4fe77d61052735647c0af6c01b29b2b5740e3d08ada7a43b4a2dc3299354655ac0a397e5d87a1497260338da72cbe170148ba4d18582dca9c107d206b787b461026a57ff02c3ff85f50c3ae2b64cd3e9a6abfa60037117fb14358029a515e3965f579cb54e02c11328080f195109ac2f453036b10b08e145df1641c3a965146bb6a9520bbae1431b6304ecf62b27d389a2eb69fc98db15f0ef590493c55e06ec357b5735a43285564803700450d32c9260527eeacfe7d9e5b9d77a981454de506d45195281ffd922e208e8f9e489228b02481b37ba95b03f413a82b7008ae586d8efd2ce7eac8b9bfd00ca641f454d3df438054c4653b9e9453f0676a4df362de289cf56b26e7a6bea0d6e0c41610eaecc0baa8484bcc2ff31be3eed6acc424dafaaa5f3f46d1a13770459c06caba56c5ecc350b946cec48ceec8be6ad205487d95e3cc85ddee80865035c238100ec5c728fa509c3d19e71be75f91dafbfec230af8fe4970a2ad6ea800e105d5adca4f386c7a592f5dd1563dffddaf538c298ae3c111ae747e07584606097d8da361a83251d939728aebd7227baa50d8eb33dd04f9f22ff11e019ca40d40760f12a9855b5a455add45971350027ad8c6dff167aadcb120f5a3c4e52e0b83ee6d3be23dda301aad06c75967ea56f9b08f042bac2cd6be20417a0f655a02f809f1d830486d51cdb773d8ca4e774c0322e6da98697c6ad453f324d0f05d05e2bf81b916be815a8f50a5bd0c0dc16f7a98fd5b4485b38c00629a61ea1ecc04eee947cf9c73124832c965af82c667f8cd3f7140a8c1922358c6acbcb32e4407b549df55a2eb742bc33ba9088053bb58c139b18a42e8fe628dd04c7037410a0b14145af5238bbf308f2ad319c87da3cb612d94557a591e257689155e5ef7430d20ad4afe3a30c7104d41e0159e72f664da48a75883b42eaae3b8a5208c3fef062019321969cd06b85ad02ace05b01317712f7a6713cd31802ad0fdee4034b9092f9a691e298b9c335e14ad913728fa7af25cb19f74e47524f9aec42b787e780e910dadbcb2b13db1491a3f47358498a94bd04bcc7614129dde5d62e42cf8a00a5168af04c1f08d62d0899fc5f4f111e73952795352ee26d3adadee07fdba7c0df55c8b3eb5a02897787fbc889d3687f1f18c66d212177191c382e1efabc7fb0e92b004c204d16224c3750d2603771480277618477bd05e6ee9486b58c4a8260fd53953b85422867c524430dc0dbce7cd2a7684a8330c7d1fa2c71870490a270090c1bdbed434a7cc5fc6d28d04b3fa590722e66e2a1ba2eba3c20ce894de460a47843bb24f7a98de340433249b96227fde4de9fcd67389373e85b6dfeda93f08a2c69925eeb5e9b643006f95ef8142f5cee709af69edf78214730935dab1940df16fac5f95c045b37e68cd822707accd7dc11c03fd609e041b9ad475440f730be590bc0e77e29a29d3b172ef746e37e2944da687e4c9b242c16236c2e8371a08b8042161a1ba3962e383f92ee97430735d5819e094d844705ca583d1f9e266097c9afa817f0ace39ba5656f5084b957b98e69368a5d7b842e6459597b30c4203822bd303f6b0afb26aea39f2239c4dd9dfdc1f70186d9fd07afb4ad0d295be00ca6bcb3f47d4fa1b6c032b5bcc4895f67c659914ddbc65fff2396d377857030acdace521c3420b40d6691394f8ed1fc292fc0f47855842e845e7455e8861d906fd6171d7aea5cf0d8252e6ac7ec8f4fb438ff14c9cb819200bc65546911f60096024338182890142b4171e9f2347871f47cb4f36953744757d220dddb1ee680c5a90134f28643f78b568e37c1e44aa2ee1843ee5654c5f67017edfde9bb96e0dbc2c1376feed25899a98b4499a69b9aae933b585f76924ac2429fc8c4454e10a551c9ffc64d71051ad4951cc99363785e43a847b3e3ae681a1f79f9a3a5ad4059c803ce33d01a2f781dfbcdc3a655c5b512280c5d4c29dd0c415c7dd2471c408c18fb1b051c66e9f055ee64a8baf1afb503834f521672896686a727369dcbb0c9fe8fbf5dfb39dd3dd3d44909facc7aecc9d67cc221fe4033516a52b933938039d0474041601d82b8e7e086323272a8eed1641c42f49deb51a1b22a6ca1dea0fe3d2c01460b2eb7d9a0536bec976e43de511cd260756a1e0ae155121d00eb7086dbac5fa2153bf2d565d9d9a9dbe32b4fea58e4fb2045117b1c626224f54b30e08ade24e629c1ffa2bbfc2626bcc422a0475429ab931380c5cc04283a70ada098c085c0183dcae0c239ce76876c0567b39499e145719a4e50658773632c74d02550b6e18131a4b5843dfe76203e6023d7d806a14b99615d17ed256279cebb406374bcdc8ef29feb3858a2a708f17a89b2dff074822e3a9e9f666c0aeb05efb00ec43a46e981609dd29675ff034bd46ecdbc93668c62f9d1016df53080aaee90efaa336ca0e6e4f55637fa1468fa6e083700ab4a72c78a7690219819c29fac702683a04dcbbc4a840d91367e3d9a469e1e80a08dfd39672612043ae8f54c95e0bcf7f009ee773fb2f5f96e7b831801c9a244ebdfaec1b94b28bf2bcc9b6633b09b7b5e8c26ec6f377ffda99a54aa99124f0edb96fef68447c311fbb304e42df02cd9cd6af4a398f38278615ef2de613e7e210918d96635964135de624f19b1a0caeba18488d272126e229b6a135299d97b3460ec2475dce55e93e116391afc408232f6ed109d9ad3e3296e6e7d3a5f20035dc47bd032a0be0bfdea2c96432a9050f94b4d25b5baaaf106acfd33a27237d4b0e835d23083953c99a944ac6a01806757bd9cc2a5faa29e1efa454b83beddd01fdfcf359a4978725262706ec19e506a254def95ec28dd2a82618e3439027f465b4e39df28b4519ab0e79a020a9830a5f196634912983580dff9c0f07fa833f20d03adb74da7922cbf45188bcbf84038fa28043cceebf9597b436e566e57dd2944a948ff26e1c4a10474bdcd41aa19d638b59aae64204e4b336aa25006203d8cf2cecf90bf71b6671201deea543eb03a31300d15acc0516d57ccd06fc0f6979461b1ff8fd8c63a5ea0dca289198000fb1b79614055eaa6dcc28658b63f3e777c295499a9e2531200fa1aae88c3240060b50376233ff74ec8b4380081c1e307329bf6fbdd76b162ab2329338527add05c2c900739ee1f4e0036ef0e3b3795abc0cd2b96a9e28284635c4fb7c9e7149016b3dcdf2002566465f65e5a7c65212c2e370840772330e02651db82a655ca505fec74faa97d9f9831b3f7f98fb68b54b767e7c4f83b37d8c8abaf9bb71c43907ff50fd262b57786d3ace9403077d0f52c55cc67c3fdef142bc13f942bfb82d019cefe94ece1a67a627858fa41fc23ccb5d2502eddaade0ad15f41ab776d8b8065e090693c108dd5766617f5be559c7af1241acad48fe3f9f65491a1280a69e0f7f2858b8b373bde4ae81186e572f914e259460b51c156a819e000c9350b58f0f5bc16a108bf4fd7621dab6f3d467adfc4ba6977dd7d09669d26b018cabe5830f false +check_ring_signature c1e0b929a636268f06ec4846dc36eed9cc8542634582158420db366222e112c2 df24217f3039fb1871f448693b35255df5c3b678179a6f6b822d6da0efd30a35 18 2c71fb02d2eebc4055e2959a0f6a053a4d74c1899d2aa10ee3c0203ba9269a18 288737014c193e6b0a39bc3f8c7fa736f2b83742b5cf5db22325f7eb8318e3f2 927c1448597f6619a01042ee1b7ea723d49780c6fa57540e8af24b84c2c35fff a883dc245ccbd8d58677db5dd5b73b66c8f76885686bda8a5abaa8419210eb70 4e0f2bb8d58407c1de9e6f74fd2f181db9cf9a2f99b5e3ae35f2e7d1117f722e 771b93353b6947d843cf51e910ca71bd1b28251606c9593f0fa7cb4937718b6f dd1834addb1c4cc2b9c36aeadac99145c580929125b38aa6f36fcd96bd3fc7b4 76070dc91d43e0a81e84e0d6b26c91759f0b12a36aede6ef42f28c5e31fb99f4 967d6e226295b7af7521bb442ae0a3691b3a96a04170af8e5027dd34c19b2030 623535d7ec87866f36e31e91b92af52eab96212bc61b9e212d91674a1a19c2ed 0310253628d9b5932a81839b367905aaedd8855588e1e0883ddb58081468a238 d81edb5193b0fda25bffc766e3ae3d6d40ef768ad59828cbbd7733b0dd1fdd26 caf804baf66f581bcec07e4d76929c3c291c4f0343165f762d654d839209d3bd 8fff9f82b1229076c55db3aad644adaa00d33b4c57d069b1a68f10abc6a4af8f cb67a6f87a26e21db8fb700f456924e6efbffcee208577d5bf5cd6c736253ec1 1fc7bcf1edf1e6b568f3f0a7ba8807b29318d3b4d0f74f2817bf9486f18dec79 2f00dbf8f422bfa085b67118e4ec0b549095b08dd00de440a5bde67e42fb4f9b 1416b2c750bf76ce7bd162641409c9b5fe48ca04738dbcee2c3071b225edc5be ad01958db82f9f5492614436e2ca43a80e278e2486940a5d637e9ed844f3340aa7403e1cf9871bc7d2e0d7f9f8da6882ee0f6d8085d052e7a6e7bdb4040ab10b766f75dd837afa140d7b2aa612e96be34dd11ad3f53a07140982dfee95205f056bd3ae9d4f846a613de90fcb5bba9be16c89eccb218449e6ce8e1d9a36877b0c98ed9d8ece01fffbec91afdcb5505fe9c8e1c1611de97ac09d959598d988eb003364d97970755cba1bc76920830ef20523a58d7bc049d870b516645a191dc708ecae06725a6cfbbff4fad6ce928dde5bd49afa988390aac11dfe099b3fbdae0207d3d1af585c8311cb5b2f0ebcbab20d5eb2330314c45bdf092c0e37f043a50775e3dc868870e5e5689518e8605016f502c2cfecdfce703f38406b984bd1bf0354be7dd23ee2f5ffce656995c95e97333fe2c8e7dc147a69ee81ab14658b0d020dc2d87a6c963d1a3dc3269f232878b06f9698755e123aa5de71dc5b14ceb307edf09dbecb933e215f0db2ed8e84038812e864595c7b3669aafea1b9efc68d0cc418370fbfc285c4f589b54e82dbf9516707d6c2150565a9e1a097948664db0e2f9e0775a16ef01f8a76975fec0171545cfc9fe9793ea4c73748ada6fd111f03717f806c713d3f599293629eb9285ebf83d13c14c3a1f658b79fee740107700f382c47b6b872b2eb0466e594ab9e5198c95cc16124594c4304dcc775df4035001142209d837f9efa2a1a5223c384f7a6aabf50dc2d8480b9e8b6141aa3085509a022edb0280dbd398b6edb55c5183cd92328ea212ca8e2e042720de526084705583c3b46b9cac268e913a70af1d86a741a84a4b52fdcd982f38a05458a691e0876f2107450b7baf580cab1fa9a20fda6aa26c8030f381082f1bb3b9c8ee27802cff8637729c064620769aa86a7d06255572f0a5ec27f9d28711d392d4d33520dc8d1165f0d47fa082cb41a133274a6f3b77b195bfbc5ce5c1909696b9e232006d4e0e83919f0375ba941bab5e6e3b31f5dc3886ea08d92bb150da502eb345f0739fbca62f226e174b98f5d0fe49111fa83f47dc51c85c719a253af0b49f47f0fdf7bf52c18059e129d0c0d42432563806ce8d5297e0278f8bbffb28c804cee0f10cd464645347e7d397693abaf5b6f7666de9d4945ecbbd86ab9582282acac0768d97cfc32e98b53f4e43dd885c68c64bbaa84de2a46b1635b20246c7ca6c1052f0455cf03ce849f9fe7c3236eadb1b8521db01b60ea64cd93948d4b86991107aff2303e6b92047ffdf60db102e458708227658d7131f045f97c9def5fa45603f64981f2f35bbf6550726439a930c3789480f346f2e90fff82bfaf6ba9e9da048ccd7a914dce0ef0de78b4ee7dc00b50351f26c45da3a40232287fbb7463a20fce07e4458c45b7ab4ea1c435a75f9a95e633f2bbc875a67205d4986acd68bf0cc67b22fe788e3e2b199c2fa5d929ce340d33cec985211b94498dfc70c57f940062d1ee27050755b68d13ad3d1c3bb9a811e691c152a7379d8d432eecf67f42016adfb9467b3da56add0167dfef467c8c6a5dc3194bf4d950ab6728c1c9061a0e1d9e603edaad4b71909b9521af30f994f8f2d783c9ca6d0b9785290a3f409e09 false +check_ring_signature 732795e6583065f9b90d3a7ea800f57cbf697863e14b4db0f4b50dc2a4d857b4 a1c95c9020657817fca5e64e7d7d9c60f6f50ce87a9443528fe03e3e62083d64 2 11a2ab0259baa75c2883085f3ee8dc6ce3d1851f744261dccb2867a6e0456a00 e561e9c376633d6bf946761db70fe9aef59ec653c13cc2f99e0cce1df11139e0 6a24d45c34199325ab93eb48c775506c2a745d67e782b54ef1710de50e569f03d3dd1336f526056cadf3ae03123fbece9f879e917a4ff913dbad07f03cd112e9e553b88a25095247e3c60d1091f6c4a234992d47b950f392987f583d36d7b40053a9f055c6194843fdf2c6c6b743b8917b1c3d1f53044f9959b28054a85af903 false +check_ring_signature a0d2a073d5c39538ed4f783cc654a833a6ea73dbc34aea5230ba907f36e59a00 546df5014de0095918b85f72e18a880928ebf995ceafe5176f61a2c6710e0d11 4 2abbc04bd231ea2bd90d1316e790c7976b859bbf6f7ed38dc4930556e7961eca 5a8ad12609bea57672692f6b7b57b47b6ab973ce6dcd30999d0877cf63b86dfa 0beb8d45005c5315b89d12b69cb5123383a5b56e9fd46012944a978705be3924 b17e6771904d755a020a52f903da1e6e45eeb1146f6f3cf35b9d6366e724c1f6 4b0b373dd51791c19f018a66f21db95bbf1a5e742702bce60d5b3d786b2f400d319ec6e49ee8f17732b4dce43ec0a132260dcd1c80726c123c8cad0f4cf63007d17e19929ccfd81e24be93f4e120265c0efa25b22f8a90bae5827acd67fb4c00f429108577b824c0f1b88b949093b976bc39ae11331e24ef0d5305026182ab0b1bcf831eb8a156aceae1c703ca320fcbb3a482e4f46c2df5008972dc94e44a004ab18c45a64bec2018d8ee9c2f2ba8c46b9b589838555186ec07b0c669536302ca9e2a3c544a2e19ff47837cb69249ae44a0e5ef9df8a354e073127d8c72d609b9129eda0306ba41983144ba3ce6606ca041abdbf63bee37754d7708d0a7ca00 true +check_ring_signature aff5536d501c7abc58c11457e077d85f8fe9d2449c610938227553be8a2fbb47 34d1f5117245e69f6158dbe16266720096b03dea913a32997c9bf6a0e9d93a82 6 17877eaf0002a82e36c7e996c885465741f1c1f43afe6964d288b23acaed3cf0 2694d8d69a51c43bc9b65a74aa2b8525f065a30ba2c8098e59993671f028751f d88c59eedab6b8baeff2e4c7141088bd23860ce1851634970fe6d4ff871bd60c c9333a15972710b9b4fcedf63f56859a55f346a7013feaba277c592f7407095b 942c80c437dfcaeb59e95faa21bb82e50346d773b6f23ce98bc61df0e959ef0f 7a3880a2d9b7a4a45cc6cf10954e605cca5abe9e0611e211ef36f2d2521ce120 21f51b9bb38343268d0e4c4367cf2a376fe7cb3d3bc3b435001335eadd42da0b7d2e49851bf46af25ff8468bf483d094be89a412e1769708a985f72a7540080550e0c9cdd0292deaf4ebad7e611323f4720ba9bbeeca280511c1c0185cbdd40f913483cae5ffc92455f60790330aed2e7278a5a835935bde15d8e7ff75d386071d60dc6ecb4cd7c7f41334d8ec79056f8c76e3e7c52917046a4df40db49fac0c8ed7ffd372a4aede0469a4914ce15d96b293b0cceb6d7404c33c245cc3abf6057533965ea1edcf5fee711256022e18c1b8a042da9dacaff5d5da64bcf75954051b355f7324d0f048eeb2f28c923d323e0a1e3da9eef99956f033a097dc976c004d50fd66d4c52d250fd3b91d5bcc52ffbdb230d57fd15a3a727cb2ace9a2b00f4ec3e3bb655b9237b572245cf02d9825ab153a8389b632dc491d7bf9de65040ff349fff14024719f8d563f3ddce2d598f3d2f98743cae1f9e5833d61a6310307aa5e658b342e16397cbdd2e5bc06aab5f9c6aa6990a0b927f585036940151e0b true +check_ring_signature 00e23e46f547118888263623c2331220707e7e1bf07111cc330b03e1db34d361 6b8ee84f9dc67ebb22ffdde76690d43d98d64d450549ca0100486674bf215bac 180 7129489089685987a39c22eaaaa283d0be81e6b963e8461f4703727bc29241be 579b84a78778cbc6f512efd5d70d1f7fed37008043dc17c183f8653e34325485 dd5cea807e451298a874682bed90822cc5666cce7adefdcaa7c8fe4d81b45bc2 aef3b28ab54e93614f004700c62c61812cd3c0f76d96350c1ea3005c698105ec f08103dbc065666993fbea1434b4d1b6066ce6382ff8fdd8c63ba1c6eb87f9d1 de87b74d3b15c5a210f5b8088f6d312142fccfc78782200b8bf6deb11ba7d8fb d42dc3897b334c990f727d70022bbbac21af101d4793bc7184d26304ce82d520 384998c0ca1c12731f197236c325ecc4a5ad7f2d6cccab4b7e04ed57d479c401 a645c9046258ef6bd76cf6940cb315ffe83e949f1c7a1d2eae6fd42777d69a9f 3d35bf6f7dd073d3d83d567fe78587f9b02f016e250eb944d9fbbce96e0f6c18 f03567419c1a1d9b3ede42f616707262bf922d5a0f59736e504bd5805095f243 658c1aa3a0ca74361d17ce1e477fecda9718639189e3e6e29afacaf391e8df17 91a097a69e12fddb09c67e240f1e3fd5e3f8669067f8d98e4f532b2259347ef4 dbf4799a20df6fc858cdac867462abc09d6f0d2f3dac937582d0be0daf9fe3db 58ff90417575b969d2c3aa42235c741968dbcb92e2e8798936a3efbe215bd539 8dda3d81e57e51c5647ba5abc265a786588a77ee20c6c9e1bea134be10dc05ef fd2b7c7293aac768d73f34734bc9824f901f58eb40e4f514d19a67880ef7afd4 e4987eeaf64137d721c7f29c525668a28630176fa1a9e25d8a5309a6d365f558 85ddffb89b5987ab396b6fd063934ec660fec95e33bab9ec1a213dcefc78a543 2cf58be58c4457b153c480a544e47d1c42fb5825fc85f2ea97740584f76edfda ee8fa2aa739780c2e033c4a9727c59999201e0b854d3ece9342c43e974a26802 8cabd58d599dbf451315d527e110d4c04f1910e6e50524a9ac16780155718fe8 b48b439ceb235ec6663a35b32123a4a0a343fb8735828038c83d833570c21651 9124181bfdb9afa25351bb47d3b1711c40b02d78dab41a9c1fa892106268b8e9 93f1c512d4b7441b96f1d66b27df8ae4ff1efe2cba1624a01de71f347c291c47 fdd2f89d77865811d1cbb9debdc5fddb80632b7e9158f771be23c09156cd3dd2 694e297a7fec5262372fc13ccf346c298ddfb01afaa78a1b549ec89abe60af70 9f21e8f2f356c0aad4aa9837f9bb0d806a3f53710f9cc90d194725c06f0cdd39 0f039fe236710660cdd87b61bcecbb94dabd7385ac1464b5e3fbe83edfd84ec8 aaa53f75229fabad55de23b31bcc60232278e188f7476f9cdcac750b01a18fd3 1f462a12be1caa71b3dfb284b0b9d474ff97b77b2c442b416acdb21088d0b3cb c6f2d8c2e39e7bc570685c5e6dc2d6a8e12164f57dbf283efd90c61774153828 233d870d1dc7067caf493c9ab74926a36daa14aaacd99b7495292cd60d8f8b5d 380271a3c505092f2fb409daf19418d6a18e4fd533d2d9bc7891279595bf5039 cb6cc5e44f991885d0b84a7b0d5dd32cf428c51a0846a7c7fd3d246fa1a6f4ab 8ae86f628792d69f142ac2bdab247cd8daf3b1d3d6a5ccf46561ea2e1b454e1c 16740bcec8a2015c9dc2bd8cfd5ed22b90910b83b9c13f0cb4e2e5db9f4057bc cbf620f46a68cbfb6a847d9c22280ce68d8c07e6066a38f429adf2edfcdb4759 7cedc0a05ae9a08f2fe27b6d9b4f2cc4fe450f76dd108e9cbaffb4db10b560dc 1530e3aa486f7e12b59d8c7d32e5762d1f04b4b1d2e9662059a7fb35e88d44d7 fc5e69dcfb38970a57003dea56e007fccc94d55f297f76682d3b8bbdd03af0d7 4416f9d9aece933e4329315546e6b623c011889925b0b6e0b076888c53dbdbbf 96343556f2be12feb4a6819f800aac57212702fe09f0946da8e2fdaacf5b39ee b1b4bca843911ed6629b8b6de14ddcba427e2245281ba4cefad3972fbc7e6ef8 b19e515350078061874864ceb35a4a3349c1d7f1722d972c640d1e7175cf0d4e c0dcb1fa81a138f087a5903e8473822e05a36caf4ad4edd9127324db7657f2fd ff578267fac5cc5a58b1e138c29cee45779286d4ec41dd57c467202aee2f5552 e999dd8c35193e651c8591e6d164a095b16bfb92b1bb6f7bf58dfda40b3592a4 eb176291b7c37a163cc2e172f6f4db6b7f97fe58423d0a3a6e7b93adfd8d0c76 967bf0084cce6eaac94780b6fb893efcd50a44220484d012ac36083040a8ba87 4e37de03f52a21990d2671134fe7646fc1804007fa4699852b9d747ca9f1d9d4 47b19cdf3eef2426b523da471b8bd2e72967a96f0b8374a40bad1e06231c7054 43e5f053c12a5faf51b4a054dacfa6c909ca99f0b25438fadd99dd283eb4deea 77a2d6a7a8a5898bf212b4e9a1fc8b22394449ad942feaa59bab2568477e13e5 bc5ee669510b0cc2fe39489475936461052b4d248544a33afc0fa7af42cc10b9 c97567d5d38d11090ed05d750150b0f727f8a4625cf50ca85d2fc0d4155ebc8b 2e8e8291fb691b2e717a5a342bcebfbce0a74419a47049a9bf17807423950071 5c67fb07a600c1a647d6f52925740de534c3c9742c9ebd0a77c22b9edb104311 75c0865b6c39fa11baeb871647d5002be78f367dcc07de86b901f9ec6f5a4378 7311f3096672d84a2ec867a232a988bc89c324c5a394c85719d1eb2e3fcc1a49 29eb8dd403e0c33991bcd0ef80a9b02c274e5c7652db0fa4aa286bb19a5a5c47 7dbcfaa62d58f6be4f88c3c691b5890f1bb260b6795bdfd4dbfbc1cbcec3b841 1a99629a8cb86a109b3c2a9afb51d52b629025c2e1bf7ec0624cb1f1cbe4c853 3232ab1309b32d4c9f9f487eeca2d026e78e95480324c1cad980264f6d0f601c 5d106b7c998fbbf7fd0b3ccbae897205fce06d08eaa3fc2af3721db05f6333fc dc74910e4adc3f85d13f884a7b44943ba3d1b8acc65321f271f197bcb5e29554 2e7012643edc858b5aab0627c0ffd83f82ab931d49b4f62877b56bdd4615926b 7e3562d1d9f326c139cc6b0d8a07291b81ee7ab0cf80fecae020c0fbc5506034 4af97537b137059f7da5a093f7e2d19824d8d372c6ea0f78b2d5fd36559e91f4 3f8b97342316e56587ca0335bf313ff5b7c2d8287e91a9d86271ccab2cf74e4f 7abea9c7d1a15f5c3072d35d17be3410656190381c2dba1737f2c7a2f7b8bb55 263656d756223a9a20c7d19f41cbeeb85885b7c3e20270606a76f810cccfd27d 7db2b4629b7264f28a7a3593f9717c2f8a56b1a898ab1a9c4a34365f0d019502 1fc2fb286f4b64bebe53a94d6c41c289ba2ad4c8d47ff658c99989626efae975 5e5607c081e220bf14b3ed4af0a8298e11f64246b2944b1f280dce6274216390 86f2646e02e430dadfae1282cb18bb5cc109ac19e8d7ee1916a877826335dcc3 41fc1d5aba27e39b622df8e3677ef6e4bd6265ae565a20aa7d3cc373317dee9d 8bab8773ef603ccd768c931f4fccd2d346fbb1f2b26aab0ca928735f71b8b227 6fdb5269157a6eb2dbfe14064a420f55315b7ef0faba0b21289f6b5c538a2cbd 45c22343b0c46053ac090bc8d4b82e89c7eb92724f6ec77562b3807b065d7340 7c24a3498896ecb8c7638e4da77e8aebf2a72d72ce804172e3fbaba4b54f2f3b 611e2a1c5e0765cf44e85f7388cab0d7b6541cb2fd59557161724a6bbbe6cbba e374fbb188a82bfc510f1d5c1dc08ee9a38cde33ddf3c13f9bdc771fc7c679ca 25b8e6a3d893e1e5767da54bc7d45186a7b5b77f9c3f78f7d22a5f1fa7ec8d57 64332442175fc13e45707b0df7b778b20944219809f0f2901d3a07b91534980a 44548dc11fbb23844bbe6059b2340b585b656dbe08ca1cf06392952692875fa8 42ab9165b2acfcf20b060c49a20ebc89832af935e7b8aee1e70ce587d4ba89a4 e95875710318727c9cc1fd726396800c61f7c136ce6c27d604237f04c77592f2 0646eaa6bc70bcc257f597b7360e57271538f3a298c8b02058715a40842f7866 9e53b665e7e8b32f60cec7e2953efd826dbfb494856ec237a5f7e32127e03210 24c7166270edee0264ce598aa7ef45c944dcc1de23a704e3c1442f2cccd1b462 80cde51b1fe1d39e111aa738a3770a186a14e0e0bca96e70685b032433a2f314 45378eedce9e641e513556f49a28b41f56e2344bf0f1b1521604672e5b7135af 2e5f45aa216da651abd966f0e5105eda7bace8bb4d8466c4f6294fd7ad9ffb94 98a3f48ef7c31f25118cc2f58de1e7c22224d1947e1bc6d9461285124ca6790d dbcd69b77f309525d17b29b115ac465487ccd92eaec64638169510c2eeb0f132 d2223534e0b6159688f31eb6a36f2394bfd61ba2f0401b34d983d1c2f00d470a e169782ac30f62fb9715b3bd0acca40ddb385344f46eceeb2868775490155845 0afbeb40b575533dcf02d75204d049b840121f1eae4a8b8de78b5990ff84330f 51b36f88c25ac1fb569faacc335122008850a5d546a2b1fb89a5b7aac5d5229b bf5f18bd98a902bd500cf3646cea81149706c193172a2a84abb250ae8160611a 8471e75b11d00646265f8015fbc1b8bcf653d40d0a95503184732851dd157d41 c61e3b5ecf50a26158f791adcd8a7a4ba48184c2baa623f851114ea9923676f3 e7808b8176f2c36086f51c604b3c2ecd295aab7ee09889678d237ab0ac7d258a b00b1ffce23220b15c6fb2e27d8bbe759e3971dc47070695e6a0c000d5db4a35 96a021bc924a644c89bdbc65c5565d8f3eecbf81d3c0867e449832262d99b222 d725b7cf9823164a9803f554caf253aacd0d625088aee8fe90e7d26718058a8b 55812672d8b26fe842f2975fa1edb7bd648f3b730c62a44c6c6c4c0b794c54cc beffbe2c645b6fedc2353726cc157f64a7213449f62aee3af5ac3ab75135c5e7 2f3630120baf00744f9c81219476fa5f25d0cc4669955669e92a1d9dd160b602 378ae59efc916909aa96773121a780eda31a3f80088f9ef2722c094e7a8a1423 eb6414ee327abb8c62b4b824d8f742faae9b4f4c4cd0b9cd57e4cb7a6457a858 61649201ef0bf794829a1f7e9bd66e6367e5596874e41bfc047136c15cf3a27d d8e39cb5e63ce1530acf96a34856b25d3a375ff1ef793a74c4284eec57b8e259 cf75219dedb8756464407072e1b583024c3205e4f8b69ffb1eff5b2fff34f35c e8302a2f58ce172022c8ac2d59b44a21bed20a7f855a7d24af6ef93c781e4fff f3b24fa3c83a643e9e5f7cd650d73210cba6e48d6682e6b79bd6c3dcfeb8f1b6 b5278c27214046a8db637f5a8f81dcd90ad697e156ba5f84c16dce9ddd3849bc b7f21555ffb3b04716b859f114032423a9c6b2c662ab12816113fec85d725d2c 2e35520322f96acd97ba9563079cd602623d7ac2f317933c94ff821e9aefaece 3ffee77c1e3dbb5ce760c0d206cd7f09f250f39047008cc3aeec9b1c98c88b29 a75b3d40a028766fe6703123df140151ae67ecdef4675164a5c9f8301aa7699f 21a23b7077873d7e5957ebb02847534260bbb530764a83390d25d705379a30d0 5f14a5eb9c98d2fdb052be5187008bfcae2d3f0f2b2e291ed51b9fac2f250176 2e61dd65b6a39282929bc613c90784927b1de54594de97c61c3901a8ef8289a9 8e0c055dd8605ce91cbbfe448d646334308ce1fe0f904fddf3ff143533b67b3e 1eb17ac6aa56f038283560dcfb463047f2d63cad67f614851cfd3d046b7d1fdf 6f889d251ff96eaca3fc36c828913370edad3acf3a76c08a39b09c53c4649837 766b7164a8706decbd1c9a0fca8c0456e3276bbbed571f3903648caab19fd9ab 21ed5395468da260db5b7334268f8607d06e6b1af3945dbf4b06fac23522a5ab 1e2d9cd485f673b5eb3a35fca9715475c33252f76a28dff24d03b6c0832347f0 7198fcbabfbe0e8fb8c0eaa4b20527e40c5a1803edebea20f0d7f1be49e3a443 b9812791616c1e4655688508fbebce345668a903ddbb37b2c6532e9abe0806c6 f564ab682698bf4c844e3685139b924adbeea8700e57fc2e57885c70dd73db69 a5a5d33032b17ca93877d208e5cb930ae4c93cbd8657e1e4822ee4a157f00735 64e0fe04cdd2374dc760deda922530192548e6048b4654b77c75f67fde23beb0 aba63cc3d9691f3736f36739184f9ac8a981196b961f9e10f48ceec842fa4771 08e8fa2d0bd3595a61a08a7e6aca605eddf4d7a3d8a0ee1f1442c67351cbf204 45995f84487530e018db972597d7c8a976e099f0d445cfb7ff06cff5afbec3ee 6bc1da0705efacbba554f7ac5f39a54eb6445c38c45bb2fc1c5219731a8921ff 469d457e3b4d5b02a0897b26c19f20c949607f2c7e35571fa82064c7a1c743cc 527610ec2362ba637696a9999349d554614c011a85cc841610c9b78ba0035729 bf099695ab9b2858c6e08ffef87caeb04c4bdafc1f3f105ee82553a6feb1d979 5a0d3ad76e0014537977cefe4d04943ed17082718589120b5c46c0f0f3ce9461 14ed4b1eccf8d2be10668dcedef92d6b50391704d5c7927f334df6bdc961494e 6447739dadb36dff6c0955da2e6a70084774a5046266c680162212ee84f04196 c249b87cf3725d56d72119e1336d71db7a4ae94e0c4c1fc8b3c447d221d0027c df68f7c6f7c8139a2abdfa3e40f6fd55c80d4bbcae38a6925ed5e70ae53d1bb1 c5f231ce6b80fb022392a8ac7f3e15ad454664f9615969a8c0653fbce5579330 fa05dd21d0ada75978c18d56ca896179c34ce4bebc6cd476171c1cbf07812686 a5898d99b96f23230d9b19b1cf3b0a545ca33db85f5b8423590a8ffa1c8bdbbd 129898f6ed9c70d1c71fd168920fe083a2388d66e3aa3528e538397d8ab1128a 49ace0b22ae87aeea43bd25acecd07d1913ab24d1db718c6c05ef5bd4caaa085 32d97e877495d10f1c31b29e179d70f4adc419d220e0c4cc74ef3c0f4974f812 eaf75798f1d522265d9c6ef47ef8e9974118dac5b1e3b095afd14c0b3d5fc6c0 05f7de5215eeca98047ac767b717d7764e2499bcc148b02e31fed071d9064632 3c16b005d7a0f8078f01cd05bc431348ffd9c1b408de5355f10be8828731da99 5a1d2bdc0476ebf93fbd4fee92082d38091cf0f952e23e3190aad11d96fda6fb 9c2ed3dcebdf7f186d88de893ac9fa601df02b0c76c4e17c1f8c0518c807ab30 24478e4e8f7d59cddfdc9a668cc2669a0e7cc86c4c683758995d057de8bb009b 93308bb6e7defeedc5651a0c21f006f0018eaceb22d31faf1663f67adce75484 2654d33c0bb16c96bc011eb92d1149bafc469869e7f1165e97601fc9ff05da35 4a974ba5885bf3c080f07a00d39e4e792b449b102ec9bfb4243c15df1eb3af1c e7a3b1379617f5c1fbb805998db89c8a87ba88d7e0d248dac398f8b856df0f0e e0b1e3bd067ccdda5a8f58e8b093e938930a8b4b7244c68fdc78be7f8e3ce44c 7879efee4fbcd0b39b3a2891cd1fb9be51b0943d48359140072cf7807687f3c6 6a232da90e52118ecafa88b474181342f8992791b2a8b479f43a8f641e259c31 f47919a15ac27f54e8188adc0176b058fa23fe646afc0635b06d3825c9390bd9 c8e5e4cfcacaadfc7e42dd0ac0cb6718348709eabbf1e3e189f715b13efce419 5b8d6dc8894d3ad0ff7880c3b15714e3b26e55a8d753716a7faa34fb98708dc5 af5ca22df20fe7969aebcf13af365ccb36c8b26ba6395e8ad7e5c9a750d4358b 65c0c589b70011c7ccc1cca1ffbc1092c57ac9b9fdedd1abc1198dc284fc64dc c83734adf0f049ed0598f3c711c9b7907c59e347570588d4fae7003c0cca0606 75a69d263e01418c75d1929ff7132f3b595a807b1dabc1d227cc84b581e4cea2 bd502de58b1e2d45d235d6ca82721c9efe2842a1b6eaa176cde84d8b7349d4ae a68f1e9cd9639ecd5e8ac7d55d67ae13c8d2cd2c042f1531a5b9b6df5b3effc7 7f058b9be1bd6a4bfa5a78d0f86614265ea5174279988ab3620ed9e5ad35e957 7bb3d92b6b8afdd05fb0e2f0c4a3d74aac6bccb1ed581575e556bf8a24d735f2 6e7b640d033794a4e0ee0626bbb512b76081955eaf2d9b11ab51268ef1373a75 0e0d56ed29ec228c3e4d59d2295817955fc1fa551914a7b0f1b8db9fe4660721 dd2cb0a9d8b7348ab99fed3138c9672e48a6518e73322bf069578493d6bc80052ac4e52534294ada2507147c0403a2684aa5cbc41d3573656e485d8318a6640f251ed8218c73b2ecbe52421e364cbbe690a4f59bc8529e90fc6bc6ef55f36c0ebe62fc87fbdd17efe9d789eb013e332929a0da0fe8f214c09181497e03de7008848aaa1474627f8481be93237a376f19eb69bef5ac86fe5543cf39fac1469f0039daae62fcc0567f0b83de6960c60e33bcf04fd6571b9143d7b4b98ef3a71c01b1928357d083295ac7f462a07933acc2f9a942acf9af718c5c4aaca60df40a0ad84d34e7d2310c6147903c951129c8b288110b57bea362157d3290cb5dab7000605ab0e0f06be429581d3c1c222de76ce00e436f024258b51f0ff975a9c8bc04ac9e813f251370a42af76edd3cf031b51e9bd89fcb92b7a0678ae4afb1e67c0088e58926833e7c29a779d9ef79efaa2e41af089b5e467fd1e4c8490c7e2524017a9e0147c4f01a2042c4085978d91a573bd124899bb1250af7b02fa239f84800a1c5c734e689f10953466c48f7c9013fd96c1e7217f7f4c854a0bc761d657204c7030b5b7283d9ea9b4acf633078f975a939b5b7875d522f65c184235224c202dcfa7baa3f192966e88cb447a07352808f5e326969a50229d9a082c5f81a010454fcfb9f06d17a83f24d3df9b557b802a6671461caec71ebe10d7caa0df63f091fe987f404e2f9a8546066cbd590eb4866050a6c75dc450c8ead81ef7f4d150a04fcbc86a02de0fe3f590a08988ee4f84f75d7914e4377e7405854dad146e10adfb36c87210913a837b8df89536c4d02ac7ecd40a40d631899ff5b83b8937a0d3b342599ffcae4f8c5ceab7c2b9fef3f4e33d9a9e50aa1105cf760af6803fe027064db341508e23bc5045cf4676f0ea77b6d86292954e46b31dc0f725e83e2093e5288cbe0636cc21e7b3d33b553b4b36a8963d8f566652f2274cf97057e300bd1e5e44ed42b869fb9fc317ed17f5a88f65bd9b774aae70cdac2bde8e4ebfd08b1d6824a066fce8fa67be13165219c4295fb7897a7f8e0806700e0e03e5b45063771ea1f848b78a7a0f38671fb8d3909546a4a89043cfe1dc43d77a0c8eec402b8b77f3034fb6f494ca0548eafc03dc69b02735950c75ffe44bca5570e6ea50e2a5904c208feac24f45aca803e3231ea68eb1dfec6e26c1df7bb275d4f05450d394a5674101a6030102ee8e8417fa8d4e8c347fbc7740cd667a059274f91070b39455c863b7f35730d7117c21f843e2791f439e3b5e2260b60bda5548484890263f84c02a74a3228aa24a24493253cdef6343b007bc8910b1f29ee39cc4ca10f2f85c09dab0b3e03faf46d89a361d56f1406ebff3e8c4bed888dc3784f96d20e425fd54100a98a067b8a7c891b53273ca38301d2d7275f36d24b1a7e4277df0f743df4c7d79ffaeb56998d4a0dd69c3afdf8262ed659372e34994eab49a1470078a4e364880e6c4a6f540844ddfac49bb1d93efe58f2479ed14e3d364bdf2404a6cff747f4b8f128dc4c797315080e358e29ec8bd364e7955ed1732b691dc4008c714b920177c11691388e814c766d7892c9fe7dc2442bfe3f9aedb0abeed8089c2db64d39cefaa9f7ede5f629bc8ea3657295b5bbb9e006e9b2bf9b488fef04c5736f1f1a45123cbbbd2ef47fbf8f912b0285727fa57292c6aa743e2f44e40ba66e43e8a81f0fba042a7dc62a32e5f6b35235bd6d6343172deb04e60e30690c18234a87397072bd8bfde411bb35f73909e9036be59590f27abc5a4bb714fb0beba9f525fc1970b9cc878e7e68d0f812272cc60845da051eb0f970833eee100cb59b1c320856451e0c86511f15c99ed3d7784f80b0aa37326a06618fc4fd100142bca52ac85c53237d96e0f235c33cbdb9e0262ffd6ecae2ef382affb3de680d2d2aac8210c33019c7656f41bbf8a8611fbc5bd2b6498984071b0addf4d7e5017e9cface6ccc17a21fa0ed3c33bdee5f135be97b1c3ef926fe09002199d7f404e4621621b5d9b5370159fa0c592d1621ec9fe9a0908927046c493d5dd281a70c5b5547fff2fc2e25a18b394ab9b776f8789ba72722c827eace263ca80a890c0e3e9f64bc7040609944b253ab48e69bbddb7b9b774bdab2f3aad5bcd9a055760e5e846b8c332632fa3f6e7319e45db18bc120231380b05a428c8b797bb4fbf5005acd181e42c315d0650d10af96fd9644944f97c6db2f0e71f7364834f2a957041bc3e96e48b3f82afc916cc015ed65671bf911b3dc934f7bdf6e38de866ef00de4e911ef8f3cf5acb58fc1232ba529bb9a30077edcaa4411378750359c7c940e0d2b05deab1dd6aacaef5477ec79dff076b689e6cdc8adda387081f301f20d0149e7a7687faee0b8aa9461d133fd151a56d2dba4ab453fb7403b5ec63b1b1003c9a56bcea12c134016df2f7c919a5c5b86cb91454767b38fbda0626da15adf03770761b18cf5d02d541ece3d4b9d49b16fa395f8d5efca198dd4b9cb6f817807ec79df383abe5c65f5877f18de9c163732a94a179349c0f7524bb4db9a62270fcefa49ec694232e946420819470a0052cd37a5cf9798b7344101b045e2d517033026c46d80c8d8dacc63147c8a353696782266feb22f09249f3768a8684ed60da2fd72dd912e187f0e31224931b71018849bb027868a12fb051f385c861cc40a2dd6ae1633fde42e1d34c5f3d0fec9c179c7aa66df59ad8d3364ec5113c2f305547e12b6ff9e48b6cdce1bac63db5200b9d6f46ba9b7ca47283f6abf9207ef0d6d5decfc0b0c839058a4130ec8c95706e208d4e877c655c3902de903eceeb30b53299426493269d97662cc70568a22b3b08ef1a2feabe73a3024907b4faf5d0293275cb48ceed1ee317a288457de625447cd34113ca3cd7ff13ce30b7a120c0e8dbaf58e846081ba6feccf5b80da1eaf974dd99ae21b1fadc2f2aa3cdae36807601de2a14a62030565513917bace9418c037ce6bb7afcc423e445da138c7b1066115ea6a4a65c1fa8772ff06cb70967af53fe8e56a37c380324f9987881c1b0c282cf9ecbf7694f4ba458ff82f95f052d4b0e513795ddec357acae54f192dc07452c0695ea8852d26f9a96c280cd2853d257f97be327d72cf007564d97c3de0cc1251857e52334646f833a2e9437516a06927f4acd848a634087b6b2ef730900a43059cddf7e3770251ca621be07b6d577ce0df39ad7b95c1862d40a1af91107a779f1af5dc8c61dbd6e05c50c0799467fda931cd216b9f7a9822e552444890314847b54c177079644210e219258344c71e102fb5b94926ef03c2e8a2a0eb40ee52dbaa9353ee15fb8012988c075c97db0f9a5cd38926b07232b6b8e44d6f10b23493c99d1bb2b65e2a6d1a87b6e73bd9435756d1ee802caa7251566dfff960e119ab003154aef0f1586c7eee875483e40dd3436dc150caea47cef7cbb539c021f8ae3af5d48930303db0e75181232e9c1e27d221619edf51ac208ca05a6a5008706f39095bf15c676f3c52844bf266b0f35d36431ee9b47255ad4ad9a28760bc0f074ce6e7c53831d9169e7124d6f53ef19447a4975640849a69f36ac4dfa004dc15a94e3350dee2e7fb7e1e62b296cd6e2af1951b2bb065741458a6c185b02a72561c078fa1adf1ede3ee66a072b1e2276b62e804df3256234978fcf106f0cbe432c9161274c1d36693aaf49446de70d6dba365d2bebb98ee8aa05a7f66f0762fa7f888b4e062d1f5525eee2adfbd5496e0d372a13eb7e7207d03310fa280d4b38894c227259ec9cf6b28d91d3b14aa771c091d91b2866f66baa57eb5c2005fa0aa861db40f592ce47f9729a1f19b336bb3fd7108b1dc3768676c75d406403f5b6974dd59e5ebefb8e9b70c15dfee2c097b695616311ce7939784ef936120783d8728bcf51b99213359ce9d2ec6c1f4750578bec55cbf4283f5df560e4140306dacad8b11e4e1861ef6688babcf15e63cc5bf7b135ba1a128f54cfb8a18c0d0227f137c1ce69d8991bfe75570b70d90dd897551ff6d2f1608414a73def07072989ba2a05120b15c34800026c474b502902ac384662261ecf20e58ae1c70f0e1ab8840c90ebd4e7d9934735199f2e4994e529cc0d497538505eddcff7d31e043b1b4d26df2b988f27637216ba503cf83b19985a6bec3ea5acdc34dc1001dc0a849a7f4ba5825d88ae62cd832ecbf12a04a1817e4c812311e84702e63e5ceb083533f08037dae8d3145b7b312ce838a371060d00f1be9a711aca44d9342f0a0fb2a5cd8284c9de625eb7ee41c0dfb2953c506206f1cfeeba2f91b033d88db7009b9edfe9ac25b1cca6205d8b30ffcc05b696ce3228239a7547e606da961928080e0bdb305e0fc70e54974ee0cec6da99dabe13de617364e050ff58751a73ae0819f1478cb4ee7a4a30ff0d40a80b0210f246d2b488f8d939b0304e37e586b40a9396a9383fa9ec94808afb341f07883e5886f9c5e25faad91a0f517d71697604468ecc1fcce0463ab9a5896a49143f97c61ccbb739073b95ec7b14c2deb4fa0d39f7b2334dafcd059279871a4fbfb1e9a75a8f042fc9010d4e1e6e80ad7fde0e55befbb4199b63b7b3332cd2d9103e564c4e73037ad25aa2bdbff16b55276a0a7fd659fbfb95afe926b2b13005eb0f5b70c02eaebd1f4585025d53c22c2a840585a4014465d7a87b8a5e5aee5d2a1a0db001c9bb0242cd6e8edc0bdc6cc7d008d1a12450b0fdce52889f7d2247ab82fd245f6721154d3d5b3f14a067fd5f280e779dd2b10955114c4f8eea07fc6a9b34ea894d817f27e18e2a6a4a17027111060dc56e422e2e004ad8ca383068ee89743806f20f1d29e631d82baccfad31c400f401fbddb1736369f5c4fc7afaefe6790a5cfa20d3ca955282f9a4dda05d09065e4c1adb25205fb9b6673c396f28d86e88a2a8c113b2db1ac7d736886cc9fa035160f2746e4296e4cf068821cb246ac8d146fd8b821e582de2c4b60bb083b204975878901ae5baacc313b62ef708b68ba8d7758f4b84aee3994892857f92dc07a00f40ebdc1eb1705ad891da1257f98b67a43247377421ea1e1dc76db4f94d0509b232a1a45d4866d83de7cc7d1f26b7814bd8d65c7464293ed9bff163ae900b748d96175412c3a13dd818094f659aedd3dfdf1cb8b2958aa7ad142c2e142b0a65a63dec8ab75fbc5463ae4a3cb247a67ff80c6b73ced3b2bef79354a8ae8803a5808467943f2081a49517d9347c237798fce86e33a3c2383cd8f3fb698da701495168176d46cdc05566e622449f68b771d6036c6477a8d86e9ee1f1cc8bf508f412999a19c5f9dd9a25e75fdbe8e2d70892ee557bc6f58e596b8e145a6e720b89eb77c2ac776a62454767ef29f2494059d0508d8c39a46e079d96582291720d5ac9eb53a4cede2e550040dee87930f9613c553c28b55dc69a08cc9361d0d806daa468c1e7dbc1e83232012b88474a59f98584295c7423bd6103da0f1ad61f08b2a05bd740e4f1c2f0a05a1507e3243df91d6191317872e0eedbbe200b999e01becf263e49c1edae54b85062866619a2956805c005b1d279d74af872bf535e0dc4305478dcfbf4a7786346a50cd09ebb7c56911d419ac8912cb279f9c1070c0148f0ff18296f975ffc73426100a09332bd200b86702308147474ea2f0387770e8b1e75f7abb0432adc41174630e6dadf6022e40f37a5551934d6736aab2b7906bbe2dd67f15abeac0d295c71f8a43646aed7b971402adbd257e7974e66453501f5181dd0549fb20efed57aecedb34f83868afa14558bffbdc7b29097be5ab2044949b0ec026c7f1040d5358a4e9ecb3e8d1e8c310399f19fa0e34c5f0f523009f7e7437a379b8e243735ba036a9c474539844504eaed2652d64314c421091b0472c8f5da5eabcd70e2a7e1b0d455995f57511586e1033c6cc942fb3100632b0966203ca09ae241c81008b0606400ac8545353ef323704eb14cf9c1944d0fe00775ee1e08bb312670dabc22db9e209e9d70185d511eaf00cf6e0f128a50d2a90913eaae6220ae596534ca1a4b59d80e8e745b9d2d74e66c45ee6b1feb8c92d8050a52008b184051fc427ccd36cd42e08664628fcf756eccc86cfecdc84ca79b011770bb652c93b0713fe69a11eb3400a6a315b8c7d9733f22a406f539b519aa00047fef396af95ad76f11df93fe1fbd0b85c11f35fc622141fa95e03d49da240d60184cca2cb0e4bc05c1409725f0909cbdffb9ffefd2c5f2cfe032eb3a0f020ceea7e70323151d7c19150c736a133c6e71632c710319d905e1d9464c6a0501034ecf8071a8f62bcd3611a16f396e3078ecddbf786b3bc481ea3c0954d64e8408472d8d1e69aa2c8f2adb57d71b20a5147190b7953eae610f3788cabcdbd2e504fadba1590094e10fe6ef5d55b1f4c9015d49557500a5a2b00c4f07fce229190ddceed2a4f06da9d099a1c62264ff59d408261d55cc92895feb3f2bd9d077bb0d2e6b4ec2637c0915c4acdee80d43993b9cffe325db511d7644d59cfb2690fd0fc09cfe329266284288dff38160628469838224b363ae75ff00ddf705ca2f640834b48a49d8df7a31ecd68e8a9467ee2e551e2326ad5cfe8e90205a614055dd04b7f7b2441b4cfde13e124c1d820af10d7df69d924cf346024cfea768537a300df6ad46cdb0458ea23dd39056a9b0c11567807644d2c32e3dae7876f047315a01db6e63d5a8876d0944ae9692632e05cd5ad7f865de7b9b4d74d4d0d5e970f7060f23de5ee8e8d320e6a708aa0861960a48e2f57243499c6459f9256810ef5202c9ab87d1522a1181d4a1de9fb9a2767daf5b1b4eb45da5879616067fd16b7b0e12ebbed4c90faa7c285ced07802383114df03ae93ac73b5469739b2ec1d600023d63b08ea82cbe1d3094a60c02ea5cc2ee04a277f070bc6ae27feb522c5e19061c86ba6af1f5142e800117cf8c37298517f7e2d16cc06e4738ad0f5a49a411004e27d683d16ac8cfb225f0cfac97108a5cfd8d431d14d46ece23153793d2f90b97323298951588a299acb18cdd3118df6aaf2b702252a5fc2f044b43e66b5402168be6b499e739f0fff4f2c2a51667e4bccbdb11835db83b08a7b987806aac0b244ff519fda960b07af7f142a0392a3f23dc3f2b03a956f46b42b84c7d235204301f2c630c7b121245d1174870cb0d7bedd514375d78233c7b58cf19bcf92e03f44b4bfd2a9624aa2a540ef2e6047995d93ef589b14c07755810f28759b1500166361de91105f0e3a7e71d69e3e8df59a08347aedc6115b1917611e3e1d59908194df646d963a776203ade6d0e4f5509819257528f55e15e70ffcd3a4c6ef100f595bc9b729933a8034d28c232f3ebec0be225a167fd22a2fc97b0cc4277280dff030d60eb2973068e08b5ba6b7b82f091b0a12fa5f4fce3be930ae613b3a408ceb92794962d6ce7a1ae73c7c8c9333022780371eeefe1827baf774c1050fc061bf31b63499bacf498ccbd0b5aa69023ec96ded5e87446e2bb1f54dfc7c38d09ac05a687acfa4208bab9ff0683c9c8b162728b46c654c0789451c6ae2e45c208141c0636b5f773820577d85e7bf695681d38c3199c6050e363d3bcb9d96cae0252efc2643df2ded3928a974c5e1c7329e649a3c67066e08c278044c6f36aed053fef3fcbd4d779619a6f84e129f9e9fc0c93f00ea96fe225068197c1fbc84f0a74f33eb85428a67f03c7729a7c5f6e133db9e231423ded674547d822246e1f0759eb1d3515201c51d4732f7711f8c3f65c3bd37da0d88a2a6db243f7c9754e080930108e327f73fddcfd4ea3e7fc461187c5b8d5510183dae7e510fe1a147704586ba013cba206a986eff26974b8d6faca59871a3b32073233d0129b7dc737099697e0a1d55f3371889802db63bc2dd52dc78bedce273c232ba71c7dc981f107f23430075989b98606dd63d889aa54976c3a87e8a859f9ac5bd01850dd6d8901d889285e127915a21511694f3a6de15c73146fd76b236d957bdca32a628e960f6a6599d4dbdbdc5692b81510afdaf15cf6faf0ab625364c3223cd35e72c056085dae247016aa44d3f9eb854c2b83e5f0b4857565bff9e61bc9e4e76a80d81100c06e0619f96e15f7a9e008a2cbb9e6a43472d1caedc995f8bddb6fa49d86f4072f9de64a4f21f9bdbf68583850bf725f1378c8ae146598edcb94d4d9b51a4c054aa3559a4c25caeb48dac407d38327f3a3f3da044333e8c33c21a183d9d6f50ccd2d33f0a54d77e5f24da06abb6e3d3dc16ddce4616b190cf508cd79db0db30f32325683bca8a5bb2e9c36c4f4bbc2a6736203003e7a43864cddb6887cdad101606c8fcb7035709ad1be36033a2fbc1bfb7cf7975e70e1e2cfd10c405497800161f916d7e2e71b93f90466ada19282ac96ca9b0b901f31319f4485ab7f5eef039cb4d99d98382ce32abff32a526a45b9ffb09091d43764777717ea4d9e1d0f047c322dac695892151d444aad78a15ab74c37df8239a6b2222b7c9c8cacc0290e95edaade0ee3e42473a9d2a5fb051e73014bec1d7ea42ce4e25d1cfe6af1e402915e923671819d5476f5b7387204fdc5c5cbcfb03d1026a1b564f5d3b7f6290b43d2aeec1bc89706302f0a836315256a835bc012463e0b004d69d5f0ad921003170cb281cdf4f83aa0ad3b102c3274ca77f32957b9ae04f905ec4f0ea2897009ea74587593f6d6cdbacc46c0ce3d8578c584fdcb1ee24d23613557b382e1b100ed57fe87e65dc14f245d6bf8fd2c2c7bb23e1dbde8589403b8e38156000e45079dffbebd49dab5d89549fc3600f330df9b0e6f691c32dbdfead1dd4f6494ac0714d4d34a8e4d5185fe779ba63af14999defdb15b1770e57823852ae4f2dc670229ed318c2db974ddfc2621b619fd0802f1df2cee890dc51f6254dfaf34e1f508a30c1254ab26f1e59136eb315af2bf3f78892749bf0968c847f4ecad41cf02098ea7949c25b656ef10ed9a168081c28e0d0dd52011091bf1fa20db9a38fd3e0ff167be44134111370f485959325585adae5ee1c6c949cf7eac9dd161d1275b0a314421278ca2cd451cec833a69e450b334b7291a9ee38a75ec5809f94c22790a89620dbc2e53a26b6a20d0c677955d7f1b909b0fcd856ed679c7128727bdc80f37946ddaa78d6185c0f2dace1a4589e25e7e77e4cd645bd0197ae745d901940572de8651b1bf09b23a6a1be9b5bdd60536912fd78454b3c8b771347fe0ae2303256d90e8b605a17605ce54c56bc2bf0ec5eb95b26cbaabac76babafab1379c0144699570ab4af2bbf76b6d7eeeec94ff7ed92a0f7e20ceed7d89b59e4126c20592962757c40f938255f65080dc01f05cc695563ac799cce2d1eb13e640c96a0c806b8d3f3b5c7b12791cab44d67ac82497da67cc239d27ad1216ff95b16c0b05b6cd55e31d8507d068227edf78793e45dd364ba0d281dff0ba181358769df1029d848b47234f967d3e6996e1057b0719544859dac2e9756b5220d6a2421c090d5833f7330eb8d712d1aec504276f51f83b2f95c6b2a905cf893fe16f445c050adc6843ef079a70eeed0ebd84ac19c256efeaa133c4b2c782c113465e0d98520ad4c2bddc662a0c500bc9b1292e3ed2c777001b5f56428d9a41d9e0c98bc1020d9abcf6cd0b7516865b8ba143e852b87e4fafe18f7b6befc942cd5a232749150835c36a358add5d616509d9bcc55d91a1cf633e4ab8350822acfca91a9b415f08a0b5d354a05104bff5f5329043b6b89e5381402d5c519c557060ffb7f27c2d020ea29e220e09de71dfa7159a7b36b75814ccd301e3ea5310c10ee0974f3ce30148fa22dfc9e73ad561dcadac7c73b092692ffd097d0b802d745f6e39d3e36d0c03f6ff6bbf0c6a084c61a260a2b3577919fb496bf7633bac3ff6450a84e1e701014dca9478affff6486234c5629088e2d4ec9c39bf97fc971d099a1231fb7f0f7acbad3b88be64723774885e4f8c0f871534cd5ad6a74e4096d758fa99d58d00e5d3b09e8d0f825bce84b355e50d8555c2b339c313240fefe26befa2df5c7f0795ff2f0d03d5d920d3968ff6e4ca3de95da2269553c8ad4412af20bb435d5b0e548ea751882a90c160b98e960549f4ed338beecfb6a1edf87332abe8830ab004e73370f53298e995eb1f502253355fa78cd89b7f5896528b2615d4743359bc08353872b6612302951e60342b4fd12e363e0cb59ed4e08d3e5f545e5d8dd42407edab5ce01272872f9b979b92cbff2e5e4f5aa8a617635ea5e7c7ed24c841fc0cf678b6e54b051905780066c0e301f890ab03b809a1a0a918c51f6e0eb0d9e60d3d1ed9fb2d4e478480a4554528e9070fd7acb36781c96121205d971dac18c406cd40245b3ab8dea3fb3906b26edd3a920db7b4d80884e40e62127e10cf3320070b9c5433e321bfd74357f0ef7b2bad9b675d5389ffda501caf013989b42fa00d986a968076ae544f92a6a6482befd5f3936a3d746cb0eddf1740dbdf34ede7e9963197cf0495f7e2f4cd6a054eb4c7328d94cb5f9772dcda9ecce5edaaf5340a73502a10d13dd3381b9bb1de754ee3b5c8418c031fe3de1fb1f7567d1b975a0ce313d5235f9fd3993d19e115887070586e20e0aef565db05d79fa399d62d5800df5c9f66403cd4675e8a61e747148ac507ece51bfeb15a95831ad6961a426c085e8a48ceddd37c90c7c13b6a5340bd09284200634906067410313c47a8758e01fa19e0fa59d5a421b4b6eff97c292cb40c2c004459f5e2e829d3ee2af086ac0dcc7332815ff98c41aea5cadb2e67e64e7a6ad52e7d6dcd6ffefe9041da05ce0f2d9f62580a04010ab834b9d43b5844aae79fcf84f3f9c793a79b94a33a051604107e178fb683882fc307489b29a8344a03b237c1442cca958ae0f093229f1505f7e92208754424f356cf42afe8b5cf697d42f4c8a30cb33ace149be3b168620c2466d353d46acbaaaba5bce3c707233d2416453f248fceccf5fa2f57c32b6301ff3d5d4c71d6c73c3f7ae6a08e8a7b09b47c2e4da50906247deae5024a117703805ec4c2e16ff4b332f8548977fd73b560f77bfe9dba3434a93df7a4f7643f0fc0eae6a99b6e08fec5c0346df01aea04c672386cd2ed5aed135a20399ce5320bfd783eb51ee8a60ee171e757276771a910e9c6ca7bf3c77778f02466c662f809ed1002d45c88bded2a2806c592dbf623a40f8462aedd720ba247865419a60200a5ad6733680a016afdabc4254eb6497d834724e327ddcaba4f8b6f540ff5c002039916249c52cf05e2c92c66a64e3523f55c77dbc1ebb9f448f0026ba6124c0c5ce5617b713f286292382f889a0e5786430dc35b4059608b76914bb16b3f400e530feb0300422dc178898f267451d63859408e69918672a88eec6614cd51480eb5d84a6773a680a7327039e21f5c9ac60a85b01584148574776a6d6ac72174036bf0caa3deb11c13c40b5173ea8298d03f4cf00346e7d3eb458eea3f59c358032f9fdec403881d8ec51ac4dfa2a8a5953cbfba52632fbf39174a5e685015940b11906807c0e7e902674ed57b9e97d30e2350afd121167139289ba45f435bd30a8d807909ac0c832c27ea0ba0b335e3fb31e4df2b9f008de5f55eacdd0ac36001e198e3c3fe799aed8d310a70542681589f33269e7fc0a8a6ba3fed31c22e280ac18fcf39830b1f269daa31e3b27ffcae6ac8c7b1a2fc1d5b8959f8fda1862e0a1b621eb1220f9f10c406feffe17e20f2d8c00860ae4eaff12bd16efab653ad061933b33701471fd839a377a3b120fb208e19df4dcb12f8d49fe1507ec50a5b05110ba72314a4eb9a2d82b64eaa980993d1ba8a560b38e92b0f30e2affd9a520ddb142bda20a5a1a60e8a23355fb17831bd98847dbdc4663220210ce58aa9670fb7b575889398fe56f064a088a428c015537b6ee27da3e81b6f27238213a913040707f3622d508c58cc50bfcd903733b30a5963b3077a402a3041e82b5f7927024d990bd269867df9fcda8d21e62cf9019513f41ab3ec8ba89479b11c956e810f1f6807ce176cc13c41bae6263aa083885e3003cf96e0ed72fd2eb90e7fee920d4056bf8267f6ca685981106d5b1ae3acc194b6f6a3ef69866dd1d81f5658da0b2349acff909ecc0c1ba1b567d141304f1a7765d809c77265e47700e5045b120ef0b16ee85f6279e631821d8106ade1e88ee4d7c6b8a3780ebdb09b5709466303c3661b053094fcb3de8cf9fbc00b61da7e970c7188dcea480dd016a32dc53003094e1a0971afcaf59a6e973234aa9384ce6ced84bbc0540ffcae1afa7ebe5e0b404a33dc2cddc006297e828c938d4627668a4c3ef18f8a844d2499395e948f0ef47e52ce875f2f918fb6a634ab4468054669a49f11f5fc12eff03cd3c6b92c03121e2630f44315a0065fbd1061fca4f48ee2df76ec5a5c7e2447b730fdae4806b0a59547e4813166e6a55fd15a7078af9d02921a3205fdd64d3ab6c3c57e560618f72dc22e2ce2c028cc1529eea6895ac1b6e223a7efb6f4caf0e5f5e819b00ac1de25beb90692e24cab5bfe0b14a8a5be0dea0908578589c066e99509a5f3057a50b2dccda18e7018f5c458688a32c6d9d5fb7f7310172556d1470b06d1a90907e3fe489192a5105bd81fde68f9d490b2616bef0c71204cfb33d6bbac782002243ef0131e2b9b5c4bff7359b35d7c6d0bc0b5eae71cbb7528d77409db495702471758a398f6f0a7237fb76c0fb35a2a26c9178f64287e18b0e8f230a8e08a02310de496252604b6c1f84f367df23292c378f6555cc29c1ad244d719f60c5b043a3948c869cb705b83012cd430ec1363b4da73ff9cbb3e34709a02eca0aa9601b7d73f461dd8ca8d643101ddc256b580e1c2a47ef7c3ad24a581b3590ef9fc01aee485924e91b84fc6c53b2c5702f476c0ba773041360945cce98557c4217d0f41e90dd596398a04c60569f0089c9ec4ff8b261013429f601b92255cd95e9f0125d19380d8c0b692b2131e64e22f7ca43bb6ba40db7e37af96f8449fe20f1205f9688a89318b9c64937114ec31169291880412be87e13b65523657d2bd9b1a0beaa2ad8a64e54a727febc4b70a5c68300c1e980a225aa58cd4cdc584c40db60da4e8543cf5b49586e60e6b4a9ee937646b9a89c4e78a7fcf3a8af9efe8b4cb0725cf46b0e066f737db2f6d8c9971cab04fa425a9a1e240f7102320c942b8fa07f0cdc506a7f8a2867d3ee128a03201fe3fd89873e334571ffb848517e273af0b26575018e86d815f1d964f68616204905036665ab8c52a6ce2e71669f24a3b03662e496c3175f00e884130c548bb8b9a5858d169662f952b7a86bbaa89b482004ce6d14cdce790f57723b58c460a215490ea013414a900adf671202cfea1a90e7d4b1c13b42fd3ca5f9b32d0fb9bc272fdb0cead1d8bf5380c15609a9cfe7105934093fcac01721993e07c8fc70bd596677a57a2e8b17b80b6732c414b711a0cef27683c66029ad885606eecac4815d1ef4dcd5dd14181800ee55a5521684f0a06323f49042c3f66b14833a7511eb8408de9628a09d0da50af2a4d03dc6b680de0f9d724302085002a04138667220925f6c2a70f3aec70242227e41f810e220ba8cddea27592341b02a9660b72a1352f633815758771cdca64f8a813997a7900411909bb5dcb8a6ab3f0e4b0cb00dad8f01d5b64d196f923ab9328f5d6ca440a7461afbafd894ede113e22a6cdb57d17ddb33d9ff708c53e145f33fed840760a462a3894e76cebde3c4699e9550dde3df7955d06a840da97680070ba112e9c04bcf5d19a34d1e47c7dc50693b45249719841361a4f841f2cbb0976566ad33604d67900be5c3d73866b8c7b0cd427796ffa092bb3833b006ca4a6e2eb0e6dec048afb11f0a11fa96b40d337f55f0ec74877afb40c8c10ca6a122c175b20915606e0d5f158939e323c6e363c61fbbddb0dd8308760e298bb7160e0c0ae40b6f10dcd090a02d80bed3ae1cff5b1af1c1533a53294882c23d4fa1c5c534f1d2adb04244a53bac4a544b7c5497516cdc37d64f1408f2d39c9c564062a565428b6000838bccf00caddc0bc32e56514ee842058ecf35dcd8322f68bcfae8cf3b16ad30b90a08d68bcd2a91c9d2e4295671b862874ca68978950717fa4624abc36fd6508a64f48d05d231cfde493c61d1461b541ad31aac03baa4293571398dc0d137f0f6b7c948eb34ac2097b048e38c99f2572498c606e910e6235a19a305f3443130b3606cb7f3792f08a92266b22a0fdcfabd96a476fae1cadc2d42c9e1a0b3585060691dac493b86e817de1d1bee22f8e17a30fe28426f43be7fb06a587fced190d5a0a46bcc781cea10c9ae086f04e0ba5d7ed90681d1b3dbf8e2a4a7a9b4f39047a1fb45aa815aa278916bad8230ba6337a02cf00839c7cc9da603e38edc4420bfb728c356d3d559916b19e4871dc7a089fd23c60099006b7dece975280c3fe0fd0e9245db95cc211a300840e11bcbd027476a6f2d93e001a3eea3ca8eb473b0ffe00bdca6b6af15531848b916c8f2f6885686c14f4ffc92f1b7b8c966eaa580844895be7bbca5470253630f9dadfe8a6e32fab3274ec19402f64d4ba18defa076d96c5770471cc3c3cad5bc484c8b2fffb24faa0377dc06f17ae074ab8231e0198c376d71aaf201555362f4df6cbd31b52452c0b63b628b63d48dcfb7ff1920b257ae575720b6c12e00c81e7de784a726a62f20d147c6e89b9a59a12b1f5c902732e7cd651208af5826cc4bbe71583c333be8b2e6478b8a9d84fedc19b2af9078c7f411ed47bf3ea0df62209014aa0e486aaf6fec669e6473bb3bb9259c89b07c820a7d69f6bd04ce4cc667e8d9099c230bfa959b1524fb357ad5b56a80299005deb33bc449f47690c5fecc3dba7d7e3cfcfd8afabd5bb6961865625e990af0023486dc0ea736d0419389b2b74498810691f8f93052ad1b2945a2bb3a80182036e5f82e9ef411325f0bb0f374b1c2730d98c0e5b6d4b04245cb442ac2bb2120813b0219ecab9961f3516cc91129c039bb825baafa7ff1d5f220d1855af8dd30802d78318f561acb078fb1cb158354454766e6dd87adc155b0fde5ec676d3f0006e5a0ced04b2e9a3fc06c2337a38ed60dadc8d0f2a382cf953d55cf6e58368089858affb008a9b34f5426bace0bb1898586250b3980c82539a3b54487fe90e075582540ed970cb860a4251c8396e11965738b1dca8ddb1df3a0ac1062d8c3d0c870f347561b9b81048cb6735da07ab906c52f27e18630d6823fb73a87cb3c30d7cf41b67f95aa572dcff50380259e9ca0edc3024c484edbdfda611149e246c0cae6cdbe7c46c9af4c1826f6206587354ef21866f7835fa5ef2be048c1b539209a69dfd57de8b2030569265dee8c57608a94177f8b7d274b420e99de174222503ab9faa9f9ba3d7fe10d4c079c9823789515e24b93d6f60f2ec1ccff675721702756e5b8a0da9d4d70bf70e76dc5966ba4ed660a4f9f26b91702e2c97f644940c0bbcc68fc3ab8feea38c2502a32c3cbf6102c03ed43353a012f67b7a8931e40acdf6e10f37deb1d8df0e3883103e45ee3c8444c7018c1b4f408ebb3e272af00296c5a1d70d12d3f55a2a2f2dbea61e767c5a8d2e1a6cdd59db719d19e3741707792df18f5d12ce97a5344a8b6dca4cbf3f628894005a0f7070e8971fc8fc0d05680ea17ef9d1e2c67115a6c8d3a9afea056b245f885817de37e73484f68efc0ecea8f8fd6048d980ddb4fc8ba68561a89948a88f00832e446be4692320c5c209eb6373cc6a3bb69d78ccb1d9f932d364d71a2125a6a8c2ae82e3bcf9861b1a0db03c683c1299bc4a467cb458d2bfe6bf7ccb9036e637b93c2b3bd1f47a5596031db29d68aaebb954b1b8803c377f940e7809683c0168d5d94edcd5302e7f650866d5b639efe1f824eaa472ffd8f3922bc557ed2f1903a136bcd5c72a7aef2a08db5862e50bb9c7dcca3dc5ba96f613204f52f18190878896b480b9fcec384206db56fb379881313752c49be6088e43dffcd5e3469c4d8d21f429776c6eb87901c16a69ab0ef7ad1494bcf4632181f686cd42bc241dc0f5b9c5999510e268530c7f7063344dbf8decc7b6b86971d39043b2c1cb98b2621abd51bcdb9d7e020004dc015cfa575ba8916e786fc7d705bb88d27c5e1fb34cc991a57e4ed1b6b5e00694eba7ad7368abe9d6ddef697a6319c3da4a061f4bc8fc3934008f668d569607 false +check_ring_signature 1017d5a20b389f80cac29791db037b41812aed7e9646f0e760d64c1772c531e8 1b1d74234f95cc50fb55547a487a2cf7fbc718c9bd004a782c73e01eb8800f05 2 b0fbfb9b5385bedc74ed84ca687a75eda88d714a9add231c556682e8985e4bd3 abab0d8849efc78f2a293807a16563cdde1c11814c21bb661289c225648f5334 4a3c6c588840d94bf8c387bae32b4fc44976346adb0f09c9b58b3cd84cb5f70c428d606c5e0267a321b579914165b8fb3a5f559470f58c080e62b7e891442306c8899cfd050e753924c92293e70206260da8f97ba6e69957df7a474c56459705cd4e1e1bf8c9426020d6a52d86cb87ef862a0f039f24d5ad16484d8cf0a34337 false +check_ring_signature c44c4fb3c043f3f18868317822828dc5de5dfb1906821ffc743c668d91f852ad 0f67865f4a4fd5e557156344138a7b031f91d9dc1494e35871376e1e9d49b0e7 6 2fcc2874de6f9198703744b9df05310bf063fedb8e9175f3ac9509ade68c862e 6124967d83e3d465eefbaf75def9549072e0f770d1ac8b580b29a7a1d5a88479 4469b71004fed3870945abb2175fb1f2f0a041473188c220379da25818399763 7988a83c3b275bf5ba4819b0c8249a59755b196928a84158a33b7be59ac13dd8 87c4a61383d50b09af1a2610871a1d67c6c958729399a2bf24b1b8cf7a6ff186 a01607d0ee7413816f72d0942ba75dfb29e00f721f32b8c3bb730659ff4fc1e2 88532098920b69aceec71efdb7d7c0f8e1acbc4f6e393ff0b2120ac2863f43682c95c48aa783ea91c97277ce659d783c77cc1713986c532a203baeca7c52e00a35bfa63684dec13b701afc3d7ba460c721f3170b7ff94fc90c6338356e219daf69cfbd769fe7e77ecdf72616a3bfdd4af3eceb0a352492713ff75bb39305f900d88ca41c185fa75bce29d78a265a6de68d0e258bb55a504456ba310a45459b14cd2425b5c6984c20d8f53d0166d54da53c908c4af19167af2613522967f27b05d4d3bdcf7351e44a55699d0cda25d33d4b656cdd961bd1042787f764a1f2980acb43f94c94725fb7b9054aefc359e196c49c8def274b9433d0e04d1dd2c63d02a7abb802ad8f77e7db11ec047f89c5c6e76417799d532b00986125fc03041403c67a7f753a127cac94ad0f88dc8c9a46f2f68c0c3475474238d143875daaba0452431d114706830ce150e942cd04a21aab68bde52ec4eaee4269052a4efe5dfdc2e7e1e50405dcd309bd0cda7433f5e714e36bdf93e4190129fdf5dda688930c false +check_ring_signature 12915085ca99160b2640c9b707e816263a9ea9aabacee0c0175acc987808a4f4 75df79d852e96550b723d42b98daa39828eb99b1e2d0b2ee9ab624bfafd385ce 99 18836c3a7246929abbb43dbae0d7d99edcc67545a9dc645cea4fd934fc85a2da 78ef55b5887975972f218315de3adc330e66a5564b827faa205f9e89000fb178 c498db16210b727f429bbacde9ee06a5215ef6c98ed0d21f02953687767be556 5f36d4cfb1ec867c11c922190964035478fce76798908271a52ef422268fb570 94354f6e54511e693f27a97ea5a3064dfaa9e690bf9cbcf15ac308a46da3ff43 a53cf72f27858883fd9d904f28a9ca30e0e263aae02314138afc1efc2389b999 7897dd8b6e6a7e0daffab101627d07e66635c4e331284046627888e139684879 c1953ab86415de8941673bd58b92d4a5ed273f422ee50f676fc37b099a411f5e 639f5cd3debef68e9179fd3dc5a5b6bf4ba78390e6268a73da4b10929f950a1e 9cd43b29a9e7da9e834d90d381a5abf1c7f009ec40dea1e8eab6c296d2e3e7ba 57040da2caa25a2859da07190c8688304e616d0adde92b5f4b3630092bb3cb45 7b9957a1a0b1d9aa0c69abbf41da6572be8ef81be860c67bc66c50905055540e 19344dde71ecf93abc369aded5d82ee3276a12c123ae7bb34355767b7dff0792 5d264a96f8fce57d6e4236e70fbeadf0dc163e71f61c7a1c9d774b35e7ed4ff1 55a9210326f6872d2a9297da1fddb87570377a99280cd978c3ca443949c8dbc5 ff918f777f834838c3e026fd60b9494ce3a5a7417d5c14a6ed571b2948e121f8 f7521e3f0504efbad7367f43cd44286b980fc8d90ce41af1c7029968869b1039 ce37974ac155dd555e5dc998e5e72629f9dbe573c2536bdcb6860a446e79a07f f5b5f6d4887119b21234c0ec9bebe54fbb5f002547a374424cc7468221f75238 70978ac42385a34c5fccb9cccbaffd5d8b5a693f3ce12ea465e305ed42ec2a3e d90e6732b18f29de26afc3f659d27562161210e82371986d6dafa2ba7a14a35f 4123552bcbc60a1cdea3f0d3bc205d9939cf8037f9b2dbe0c9465dc57e304812 372f9bc9f404ff3322061d181def5fce296e8e538486feab3dcbaf55b1dd5ab1 74ec04e1fe7ff544ed1931ee798e02ad58fae43258f78a27dc5413c2cf904548 0f217e5c0bde86097fd9a11355cc73a1a49cd028485e98af47d4aa6cb2862564 a55be7982703e4ec4a0a5efb74de06ebbb7ce761722ffcd0951f2e41e1a33f2d 9514362f71b5de02be7a943421b05309ff76482f2c01c904420257faa7385544 34b5e03b818077cc292829101d40680d478297259e118c10bd31ab3b8072183f 566e32c8203bd6dfdffbb1611769bfb3b653be8a312c725b89143708771d6386 864be10fe247349505ecfddaa830175346dd6f664a0bb066a2b9443154e0e220 c008a01bd438d437be1b12df1efdcec7f8c3cb6b4fef17ab3b0088f27460b8cb 84d2d65974a7902176ff901d0ee88c00e97b403e67ae38836df353a364c2c90b fa9dc69d0cdd47e444a60158f35565a929284c829324c3cc350e83d418feec83 abb495b37f165c1ecb5d65f3922abb78cbb03740a2a89200c5d73e8ef48a312f 08e4a297daeeced115a9d7bf4cc0b6c8b0097ee5b137f84c198811629b7323e4 7dcd32955366b60b391a635aff7c3393dd0f8eaee1e86f9b194962f33f5d8789 88f6e82a88b5902dd209d4470e75122521b059bb7d365120ebade6951fbe84dc ce1c25f76d129a96f27a2bd1afe8a9f5ea73739d11ad8fbfa36fd4745fa6eb11 435ee49c5ba2788f796817a01333dc0653ec2196e260cca11fac89fd28736695 8793a976b5833c51f55ab3574b4baeef71991447666c1fceb2dc99a0891f7e65 9a1041bdf21f5192371a9792be8942e55bf528eb9275803b5aa5f45897ed912b ff30d7dd897e3dc42725f0c859123105a84203cf07c6a30dfcee61dad2acbe73 5b8dd63a3e9bbf89f61ae2c1b1c4856c51bb02c8ec987b3f62e0d812e53a7304 2197c95ce883ae61b0686cf7cc273ba8e97b4b94b6abe44e7f7c5ba4aa0d09cb 3808d1c54d97a76ccc7781ecc759499a0aa9ada3d8113082dd0f42511b86feac 0263431a99bffcf3a6c07080980efe4f04e9ea5966d99732cf396f74ed73d991 29614561fbbb0f36e9e9cb9ee7b26209365765db15688c9844a44487e6610b0e 6f998749cf1e7dcb66bed91d1deb0c1d70bd50feb096d78d20e1f417b14ad79d 0acf576e496a176f78215623c4758f59ce5af55bea715727a65c88062fe4b6f5 b5a1db12244a8369c8df5827805b3ab5e259865c752627731e5cd234e315df0d a9071afebc0b414e6f0de7a74b8663ae4ad06fb8b26485422c19a98352f12ebf 60ab0572751f7a9d1f9f8200fc864597435a8b2567e774024e272efcb3e92da4 7b97db943475e8c21b2cdf32405a72456f350452c27ac064efe5d36a522c436f 2fb968816deaec4f8d16354b60ee9f0528ddb45f24d5f8ed57509e2493143607 95219f865ad05e8c4771a3e2cb0d3f2643405c86a6af3edfffd28ccf3af091e4 7c7917acab5c8982f89d543cbbce6ff5ab4e5a84d81b9575835f265c783e87df b2aeaf44996f5e621dca58d8556e2e0233e2325f6096a7815a95739d3512fb34 1fa62aaadb3717d703d7dd2bb9632caab3dd69569c8baa20d4f9d61dcc2d9262 57daf531f1f5419c97bbf58d25c1eeac35cecad24be49c7ba0f7117c3b48420a 121e8e7f1540ace3ad67238d495bd502bd0f5b9598b1e22e9b34b3a33894acb6 267f145177887570aadc347473a400e0eee1c95d60e5e23bb41b1d88d058fbbe 023237098422ae9b68ca0e6b5a9a4708d723ed242cee6c575b35b3a3037c91cd af556086ef39ee73b2dc5d04bedba9eb4cc4ba40a11715a8d3c64a544c65c500 34885d4da997c7f14a002eed36642152d25a01ebd313165260740169c80b50c9 e93f916e45cc09c1d03e1827f911342a9d1d13666d6bde3c11ee72d973fbfdb6 4a30b945b32ca830622e75bf305d6df07aaef639ec2b58301453f384b3a3e2b2 df160a1ed19f7327f6ffe7bab82bea55f79ab80dd71c36e16ef342fdbf4a1b03 e09670c1c89b5f30dc02c1b6620b21c4adc760db23e912f6880015d744a03f55 49f48324143c42d8f6f4678f36a252f92373adf2a80dc3b27033b00050cc170c 3223e711fa8e4391019d56ca25ca5f93c75aedcebaeb08525e3990dc3b53060d 4a1e5c469d842b4c2aee317f7a53ed1f11e06c4ea03d130939128e26207004e3 9e40b53be005e5f7e2247b601ac7692746218fd1694e291fc0f620cfe1af52f4 aa05e729fb3f25364720164bd8a237e38377d6ba69fc8615aa1db3efb096b504 1a95f5ceb05d03dda3bf1dad145c2bcaed23ce18cabc8628c6e4b0cade18b984 469241c97b5c1bc79183daf6547f699a40a4030a0237324747080ef0990e8e0c 536d7461041ffebf7a4d5ca1e12ab7d0939166aacd86d8a4e929eaa93ff91cab dbf995e5386c4e96a5162cc6478131018871fe9c38ea2c2816da386b4abdcf09 c60a415e9716deb6b72d2f108926867f7a79d47061ebfaefdc3f535f630b0229 d7269ad86b3fd743d20aca4fddaab9c1b4dde29e5c9fde75e39977fc1859cd62 c6f5ae1c941ee0cce5dfeb2fdc43f54b8a3057282656fa1aefd989d2fa03c711 99ab44cdaf4c3fa2dad565641e6964f25ee80ecc9eb43d981f9eb4a1edc2f466 8ae3d1132c23d3609a623775e5f0eea3586ebffb91fd2150d407c423f3903cba eceeae49ac6bf857bf496af5ead7e727414da7dfea33646551fc4046195cebe5 eab33fc86927cd6eb69fb33952f3f6fbc1f9287da8e39b0c7cd73843d923df86 25381b4769f9370117aeab6580e3bafddc8a437466121e38ad4849f6bd07f240 cc58ded3ec50f78b91e599186d90b3311c2af96afc29b9c689decf450cf02d75 e29ceaa77d59a9c37e21a21a1752da384d5811984a6449779b96f9d7cc8708aa f8e07221b59fee839ff126d347940d0c433a4722e956da807c9f83b65766ca0f a7fee91cc39ef9c548fa869c64e2351efad0355ee040666b57a8133530d8b528 471cea366dcedf0c0a6e096a33f1b335eda8ed85b1929923d4fa614c6d1576e2 53b753684b63097d92c10bed4d1634e632c7b86f2fbfae3beafc8a0c92262f8c d1c578c11a290eaa6b0ee98be52c006eae41439b14713c349d7003b695d83151 2d3a6688e3f2fa56e74b0cd5fa0ac934f7270ee03eea10ba5f06d28ce65423d2 39e54549ede9f96627c62bd2b43f90af293cef0871b2cf9eb851457bb70d4bc3 440624a91078a6c394e62939c452cd2a7c1a2f379e9a9594ff68eec40ca2caa7 cf4f6bc74ba8c88093418af009d77f22ad2b05e9abf038279dfa2eea9ce2e66b d1eaf497a2b9c73fd5786147f6a46fe3e5ab3af7ba7b0a6ce06572f827e2554d 0fec43d8920d796d5ac9acf38ded6198805798020c24b4e57bf70c4cbdc9e878 08577a682389b1d4c3e7540a7e569733e2ea6b25de09972f0e210ed1a3b9f975 5e26d2092a0ef632d2b070793cb99111e5e05a74ecb1b06aa7ed577cb63ace02089706d7d4fa9ed3fe36efa4af50c630809b3a4492e622fe481df4ccef273205913b5a2a177fefe8134ac593e2aff423616506a763c8e410486af87e17867f0b0cf2107fb2aebaeb371badb490004c390edd7b0772fa5e67026f0601377fe901edf9df5c84898ba66fedcebcc8ed092c60964759a4739cb04ccafad07211f00340fad338fd586591edc0648956ded5c6c865389a688740d01ce655525964410dc617bf83aae0e71f78d8543d307ef4072425cd4f94948a3274d335a9d769e1007a6fe3b4ac2d9ccbcc78af03e8ef5fbbad4b1a081034f67ac78ca33805c5ea0d037d0a7c981da35275521f401d56b5b386b594a24993ccba534ef131662d280cda70327c344d83aa995098f288c65a1d12e9d9471679f8d94bad01fc5d8b200a0a51d1e7d02c000fddf30ea515da08da0e35f3b3278748fc6f69fc7ba56b4409700a62a2244a6a28e448c2aca6256574efb65924b7037b51a3b657cb0782d60f0d3f18bbccfd7e25799024b1edc11ded40cb488bf901a92ab448d8c990d88201606f90f925be7a9ef5a415fa253de6a2a45c16d7a6d7831a992b4e633b01d30b4810890f893eb204463fe0b9571883afeac8b01fb90af9cefa202ff9cd2e4000ebae189b335d7caad22b7db1458385f9583d079f5cb258faece9b582ab7d7101a52679e287427dc49e6bd6b14ae65f564a598e2b491ab399cd237e9211f4f70e4a4f6b95d5e081be0f520bff752395e850ce999402d6f6dffffd50fa31f5280bdc6803a71d43227527a4d34da7d6311496b5458089e331bcc0bb538d58629a059bd1105762060a24663f2756a75214bd38f6d959d89ad8306dd0573884e7fe0e1eebde1735ca33e9ce0a58d7c657b2387e361f6ae82bf57476f6bdac3c97a906e66d289cd6016b8a758b12394ae8d2fb7a7778fba70d0850e1bae916c25b750d629e47239f3fbbe0904976371dc70b02f9a05f39b03da211573a8455e21b5f0a455464afebd78c8176fe284a3591f3301d3d15976f0e3d24b52f373f1844b3022cd6e09f186da0ab02c3749035ff73ff7cafdaff032bc0b6bea26e46babec5087a3bbdce0bd0c7690a8cf596c2c7a0c145b8c3c3a39b57a876f91d2a27606205d8effda0a84258ddbe1bc3ef435aca775a8e45ffec7544c4dcdb1d3758572603d391f86b818f870629d03ac36e5fa01665b015994c6ebe59b642430ef987f10d826157082ab89f5b26fd122a0ffbdc8b9811006258488fa0a297d7451b6eb40a40cb3173c7e80276db630b233e1da0d13ec1f0412100689b874a362b57a1e7059c48c15ffc2ec56c5c0c796e77c5a0556b88979ed3021104179eb64f337cfa023bce1f2f243f77fd25a6e2ffcd1478fdbb0e6450e2694abd014335c026bf3e0dd8a8c67b5d50d550d96d7583194fb682b9ad1577323a69f22329434a2b08670ea237e7bc6f795fd6a8d80c76ba8e21d30ec08cbead4890a3bd2860727d21850e36fdc4eee9add97a0a140e9a9c8b02d8a3708a53bdef37202dfb259e1c3e0f0678ab7499b68e6c08aa049c3748021c4a0fb3f5c5bce730820920aa9d5c07270a1935a0130add15cc6671b1fa8f061508274ca6bf8f564c9a02bea8f9db14f30f9c14ddc6459b8d19fa2736267154b7412089ffacc51481c6f86b18998a88370ee590b1c554ececad0eb7ee0d556c396aad6158561fe71bcddcc1d7b5449fe803380f30e72ec3fd14add4ea0a279859478dc064c8aefa8aacc94927b65798ce07ed941fbd503f3e5e089d74f7992ed71873e7452bec20f01333fad2ec744a93083acf1da1017936c6fcf6c3219ec3d7c7496eb1f956bb662e1e47341db27914004d3042c61272fad3e53a0e1b5cb533da7d6a3bd23954e6b1a41fa391b13bec0129d575073ab36aaf953682d2ab0271464300450a8d9de43e661f28dc80d02d014f8d3decf5e7128d72ea26a2de89b49862369d56fda61a59baaff716cf610a0c0ea8f2343442e735431269b106b876377e21da0af9fdffd825e24f46d402bb07f53a12bf11e96b19e21a3d55634f3a417e8d56fb018f0be230cdfb9daf3349064b8c1bb8670bfe5e25d7d49f67f4ec37ce651395c7119db3041e57cdf66dcc0c59beccb879c3f6a7956733082a56c6ea56da91fad5a69066297ffa375965b909560d7812ac0d98f5848389a28a41aec63d9920bcc9c98972ae5fe4c5c27eb6067064b34587f2fd5f13c8d5056343085417fbe75c489e989ff30cf7fe49d1cf09827831b78bae50458e39c6a4803e77e3d397d80e452c018a461a3feea957bc0e769333adeab566ecae6ea04d9cc5cdb9080d969e1935c6a8ea7005704f356703b86e33d3f8df63f620e6151a069308f08c5b58c66bf106d3fe0888fdc3c389019b109435e6547e8afceff5b720b27b8d211019dd7e19163aaafe96db83e6f70783622f02dd554b178472fc4b974611711c9120b971277fa41908ebfee6f0cc01b5336db17ee023976cef971975a292a30b5e2623c0e70bb119ae9f435dd5220085d991d234da0efc8259e6b21b876bf7316bbcbfb34dc3cf56da4797fc54450bf89d875c8c82f12f6fb76b91e1f1e4c50225d172b562245efe054fa252ce410dad9cefade5acb2901e703ff685fc30e6a0651b805d6a5ef9bce15893d9fc610ae503256483c06a3f24d3d5986331729323621161d0310fb8057f794aa0d84304962777b88af2802fbfa0cf8adc610403bad5987a903068ea4a7dec353c581706328b2cf1da571b458a9cc53b07705b538fb27fe6f48557eec3f8d057d0fda0fca928a9cb65572919be38914b494de7cbd890b7467b820ab137612289f270bd07e07967e4bf339ba02e6c5001b374d75c43329f4aac880b6bc1835d465976e408b5ec694ebfdbb8c85f449671c71399a36af591aaaebdba399a653105c1abe309a91e4ffa39f7afbd532787c35fa24a8fa2a61872715bd52db7e1b4f7c5c4a506c7cd31e11d47678c6234c47546e711a88eb7795f983180b224b683be3214cc0709efc6bbe95c1471337665675b47f2a9fb064c4f4e3bd036ba4550c56e48780f433dc65c315cd848e42f5cf48fa579f2eb38630e8bc28ce9abffc49b9c95ee0079ddb0d8276124cc1125f2afd5fdefac302a967403e365472fa582d6d4ea1b0ece800a75fa0ca05e124954077fb144f1d247d3274460cf2b829fb0bb5a991901911f2c90add648d644a2f44833e5589ddbe7d8b05dd8f7de2b64215d85bf7c059dbdd955f63f732ad85a0d3d659624c277b022d6442dd6ee2983a8e16f11af0d413c47e083a7f4d332ac3ae9de7e3125d553e732655964e1720a9bbbe1331402dea7299691b554f2118432739ed737d46ebcc41fd2d8ba5f3ffda2003ecd27033acf7cc8dadb52f7b06e8e77a7e2d5afb8b958bdcf52c86d5d19f84041c09409294d07323dd27f191c5cf67e0f72113e6de19cdac7e8db0b2b8c1afd6645770ee513e49db237c53bba1c527295f73311ab3ba13e5dce0548401f27a271c8b20c6c54f056bfebff3163a3e40056478e1ae4446cd82a366c398dbb03a6c77da00f559abe2c6753b399acb8463725fc09e4bed3ba80d32d81c83589dda721a8e907ff64ff2294a0ae898a06fd5e7a251a1ec3e62928271aa6cee4274026161fb10f9519946a439b1ed8d3efb51c99e5564d7b1e00064001869775af33186f2c380663276543fe06fde13843b7bfd222659b34cfb47f8ec36611c31947904d9b8605cb5145d999edac12c4d76af54b4c15739c173a56f64e368090a5c3a1b4242802383ce7e535630377d20c43ca33b24387adf524bd044b1563cee93595b410470554d04829c4f964fb863e9f16156232dd666afa9f3a1f73bda13d0e60ffc5d20101c6478b69fbafbfe67dc72ab7d47381670324d16e17b63788dd744007d8dd0a33fc87952fce263316f02ab68a745a8efa93a00f48a040be885e2af23d1d55071590e3db46982055798f39eb4745a65157ce7f794415b24ec4a4fdd61ba54c02c1151d041d10fa754ba8bd8693b6e841b3ffc7295afb2abd94cf3e4321cfe00698b293279cd5f15b7a5e353d6a01ad06ccc1322fecf116e29992b02e47ca7c0b651fda6d547cf833c9ab8fcad1feb7f299b42590329747d2206bd7c43a9a8107dd73af574943f9e91d2d55ddbe30ad3e80be7fcafa37078803a9fdefb39f1d022033345d23f1e181ddb733d55c32dc2cc669a449f9d0af41681115004a47e40a7db69bd8749edeff483b06ae34a1c1e330329fde1f09d4c3f79bc9bd4e9cdf01d0f8647675ba7fa9776f246b615ba009dbe84bc1d168477e4e76fbf8cdd8620447373c827650a897e26a35650c6a5f9644d77e1e12e354bd8680ce31e42dd60fc9141f38243647a79205b02e1311292b30de740f4804eb34456a7ed00846fe0bd1a18217a2648070796334b33d079a2a71125cc359ff04366ace003b66949b06f92bff5736a2f8a0d36411efd029b59dc281bf36fe388c8927a5db5838ff8b067743f1c4f4ad8567ac832fbd9ddcf174beb273a800e2ec87263f108b3669000ea270c7f2feee9f6a8a5b48fbf7d6daea7f2abb2fd87086953238c6bdde5889002e3476c5b9176207e85d1a18aa692fe2f9769d4f64c03305e2c0be52107538051fb517c723d0ed773a8ca09fce5c06d9f49a36afc30bf5fa108252454448fa0cae63f5988d9f0a160367651d93dfc1029f79663a8ce6354c2116eb73ed00570940de3c9c32805ae574cf3238b14625988d7f735d36de3834f992ff86015a0900ffde684e23a7ef46cf434ba6c8a8cbcc057aa50e41c68d66e965d8a7b4786d0a536707a4f998e9d5057b63da18057728d697aa6204b93945c0430808a8d4bc080f8bf6a17c4344196d66ed4d70c2faeface2a0b828cc8fcd3c819e433c1850040372b6de8d32812177dccc046b15e2eb2bc72845abddfd00e6fc638ed744430147b94f60aacca6bfd47a8fd45cd275fa1d2d1289d58e75e7957fc0be88da2f054ed355c0a6c36c520f48ce1def99816b5527980d016ee2cb866e23bca8c16e0ca86dbcbaf3ffb15c64429fd7e22542c1a93766d89f4b3d3bedc0f1ffd2b5e202d11fefccd6aeebb6d5629613b5ac6570669d2446338febe53c412878d8c204087be9cd7101d08c28873e9279e33cf333ba0cbaf36e68f5e46bed3933be6aa508765252e4e238aa6c1a8caafd5d94402b70716d985d1630fd83060dd11012eb02042ba41452ba3b6ba192937ab9a9f1a22dbf4b3cabaf91c140ff614c0078fe0356bacc68e34cfccbc439e67b7fcc6e1b6b479b009f735f2a632d24dc4c289209cb15ead584ab45e646b8f4a82148cf130aa3318bfa6e9f743bd9a33c2771ca0527b1b05700908777d64999eab7577d4f65ec23a9d201a46154e9106df648c30a4430bfd5ca8795f0126852eb59e0a1e5ac8b50b88b2509c01bd36cca4ce5b806077bbfe83f030e8bed8312b254880949dcaad904333316beddf8411bd0cc8d012e24139efe898ec74a9b8af9269a23032244e459def090cb3a611810e18e94068db44bdee221ace6785ade40967e79c669acfa95b9e3e49eac846dba48281c066cc5b9985dc4eddb6a0e9b29774b559451487a48d473065db8bc4d09df5c2502facfc353bef07f1000586f51211da06d981c39a682853ac7d6fd6c52e03f050a6bedae935f4e52d2510137375d9d90476b9e4a8419c5def5208a71337b35ed084a372d7966a4a1f3b0cde049446f934d1a0c887c45e342c950889d1008d800011872e28cfebc5bf49e421d6cb5cfdfd798e0b7d9b513278540d8f585f2d26e028c400665c48d71cbf95d11fe41a28370d3466b3333045cd12d4f1697d332040d9ae4296a28a0cae9c96298727e2be1873729d10e6e38982b830f5cc08aee42062c02fc63563f2770b420d090c8621f7fc9d881427e1c15c8a26cad2c01706008b1a663dfa64988778434c8d71230749ed7ebb6f07120eedd06a8fcb1171dd30112dea4ddf769d181083001145aaae211bd1caeb6d658d726632584716dc41b0aee252d3af2ced89dada609479d46470b085141df007863fe0834bc3c19cf8802d56c02e362676dfa00dbe5908eac4c7ef314f4442df2f0ba2241b75f8212130915d9312428f7d9dfb40454ba85158206d63b5e8ae79404c14ea3a4888577060bb953ebd4c28292cd37e3f98b089374d13e5589b5b9abadab6a61a17296acb506ebd6552ca6f309529cd1f2a6f471385b9fc15794c877e9fbc8de73a84d935d017b9ab9e637fab91387b83a18fb49ff6f705ed162dee4b7337236bea4269c17000874cfb7e14a024ebf06ceb25131e562fb3284ad172ad3efbb31470ef87f850b4787f2c69970a450243a5288a8aa2fcc196879557464cc10767b2b341645190390147954b963cd8bba1383d6ff71166a85cd5a2587d63ad4e00f3894cf367ca2f528b1eb6ca4fa56174e546229317fde5f2fd0bd80b8b7197570b215858108046449d2544fe1529533f9cab33b0bcffa3bec35f7ccbcea868ab71a4e1b8e0a0b70ad5d32ea2a816456a14de68af00040519430c7cdb7da56b52329b76da0760c440bc755af28c9f826a74afed144fd07cdb0f209ceffd238db080a90b52db4050104ec2084ee3d66b2cd04e13e239784f46fd48dbdcc0eb35fdb3185d73d5c04ca9429a8e54ac3c5b3d235ce7483f872f1010d22c4e93c556e695e8c5950550c1097652ff736274565c18608162455a939f0e25be3c7027d54df25ade593bd04fb9989285e70a95725ad0ac73aa959ce3c3501d49edba985607cb5e06eff1f073b8950921fb8705aebbb7e4616a5dfe75a61bf1a6f6a40155247af88f05eb8030788d802ac449a6a943d083cdd10a3d212ae6ecbf8f63478bf078caea8e34d091dd0c9b542221ae5aa1e031f38e4e4a69bf89279f39789e799267958b8507a00e8fa31f24971aeb35c032fd6bcca2d6cc826b196445888e54eaf943e8c45e309bb00a6a5f5d7163b589272ebdfa52fb4e90efa94ea1430e0e749f36127a6bd0f5f7c0a33af6795e959384a835e5f6c141405e96f56913edd1688b30a0d922300c6eb36bc40cdc3d5f5f0bfa1699ab26ce561e3f9fe4b72315b608bd515ef9d085ce142baa2b0829df9b3766cd2112a54e59c38e306c272a61475f9a286675a054150600cc1091df7c4ebc8dfd832954d21e76bdff7bf9a42635b8766f42d7b0b9694f81e64cf146cbcd233ae91841d315448f0e9e9be50da1e7a353accec390460abfa8d3f73655b3cff49ab429f3996643f5af7b8dc56805b58a67d8a22580347c363f6ff9baed409bdfd2f91722f66288ebc7d92bfd9ddbfe8ed01f8b3630b7414b7623795a3490ae860a83a5147af7aa9ef0fc94d3a3afce19af39457420542406a0a6b5f910c7adb94cdcd6914175da782211897b96f0b13e630f9e09a0606b70e2851bbc6d96e2159530adb56728e833f69f1c0e3f6809f9d0d2164aa099839b800f1d3039727b295e5cec37f8f8f6341178c9513b0a119ec72c750c60dd40a389cd51a1a0f9421f08bf75d0e4256ffe78e3790a2ff2b9bb1288894af0655aa98f14274609cf973d7bfabcc0d91b5b3117c3675e5c810b61c284a22da072eae5fea7f485602f6ca5cc42294a43a87ac515c2193181d91d04d4e54e4b900154de5c661c7f2f98d97713871397a416aed126e576321c110a6e5ec08fbd7045477db06bf1edcf97a4ce84ee2d71adcc56ee657c973a54f4ec6de6bc055c308b2460f9a3d7cce965e041a22c54bcd2e42640e0be6264e34c953519ed7d6b303606fd1bcc68b8cf789c4db6b6dd3aa0cf242c15d8856cd2bb93fbfaf4e142d07b736278c5f83ec8e2484118183dac3a19a4ab76482b8145c295b320f8e22d10c6346b745ad548ac87d4f7a20767bf3d13fc4bad37d28fcf8924a9222680f690a920c70d583b018e615205b764b0267b03f14aa16a6054cb42eec86b506ea060f3ead47828702b8dd4c020ecede69c5cbe567bf435965d0e8f21eef40e9a1fe00d889d467077eff97f0da40e2ff2cd7f024f67ce187341628f8f934a8c9fc0104b8ac50644e6cff59bc3ba95fbb2bc7b38a89b09b9dd52600bc6bfdf72a6c4e02ca0b705b9fbb6e8ec7fa31d74ac5056f404549bf052027c7252037eb93de1d0c3b541a87ecea65eb9efbb7152f39ffba4a8c5941536554f04cbc2064acf9fa07c0463dc91db81e259010d63212676ffb9dd3f190d2c03d1f96f51dd430a715024674db72781cd0fbe4043a3ff910798f5197f9557b76bf3baab2afcd7df3e90d6c855cfbc412bbadef4725df73eb84506e42fbe23dd1bbfc7ba9fa4c7db007019f9fed1d197c5dd6c947807d64a591516c3dcf298ff99f83883fbba218abf80ec58dc51a55a768fbf203bf46b71f36518159642debc50a2153879d09147c470623ac4fe2818ede4568453e644e005bcb6cf4094759f7762351c61bd7b2adc605df301b2f2aafe3b469e2ed7974812fa4275484c8dd4f0b4415b3f5fbff81ff086cb6acc7f26b1fef47fed42fa58e10d8a12233e2426d4cb668fb5921587db80d84d0b6c62f4942fa043ea33b29709786a9de1a5142701ac6dac054818f10da0e73b4533f008508cf057fbecc730692f8b7b9525f8904324854165956957c71060edc17d93795968da08a4d592fe0bbe24f100b620ac0af7b26888b1ce8fdc307ce311d7c8d3b835ea5bfc6f081ae29379306a3e6d7f22f5b174d653cc5099c0f117a4be5e1fcecd75443a3051dcab6d04604139ea0d1e59ab160471ba6ddf0076649988ef1de7b8277cad6e7f9ad09d5500411f44d8693a71facb4f4ca79ce02bfa8e09322cacb3ab27125cef1b0c29460d6d427a235a6503d0f72358f2d9c05 false +check_ring_signature 7b394f666dc3468d062bc79fb3f29f607ce7f9914f868940656fedf174ddc51d c0c0b13276dbbdd1b71868acf95fbd1494a51f813f916d764ea3ce8af422d7d0 93 658a1a2e6187ced7ce2b09b069303e792e95c701178f1f934113fcd3a768270f 63f33f7b3a23e3df486111006fdc3515372c9384e34e1264b6e9d345a71d7ec0 04d46c196a0851fa874860d144fc357daef8376f29dc7b53be82a0ff89588e46 3ee83ee0d14072f99e5c6d53a2913f86650a3a19968af89bb297c37622b17c34 bf5de7b2a29a9acad48d9e7636e7c608af94c96d1e03a492fe7389ed4d18b832 4181250bc43f10ebde30a08920c621c5aa35d9897c7c206482f6156771b0d51b 019bdc18a3668091836c9778f1196032e60957c4e2f0f672473d00b82d63a09a b158406a54f25dbcfadc4393a48bdf240ae67dfec27ecb5fb4e560199652e719 3530d72b2ee822ada89d19f4c3692479c8b4cd7eaedf0e12d3612823092b0510 035d3a47c2b20916c4d307c54b5ae98e17d48b5900137d52228da024fdffcca9 428e563c106e056c44ec2045476b34a7f2d437a9790f055873dccbb4df6b6ab6 1562d36e6dc58cfe4fa7bd9fd498cf4c9a14829a35d90e80d729327a84982f68 1f3c9511bbc76e34c959833a80bde5e7590caf8a04f299dbb55c4a667940145d 00f8d9d8c16fcea699e958d1f713a5994d287c3ac28aa36cfc4defed6e392734 673cb3e9cce813981afda2c5283e162522b848ddeb6842ee7e38667dbe51e401 7dfdd81f37bc39acc577e1affcb88e221e932d111c6520e8533285c32bce53dd a556752fd37abaeef70c793a727a2b67516ab5ed10e2b6d651e6a9820864873c f285cfb2b5312011b5087ceadc78493b1bd010d091328018d21f9a20b46258a9 f0f144741a85130e6a0a5a365555dfa7f271ac5127e1fbf72bf12fcaa3768e02 608bc2865d58ba85284302681607a248e6b6abf6cc70d1927f21d66f8d9ef27e 6c589f308ac6c01c9fd02911704047fe227a6bda4cdf5cde440ab4ec384186c9 c0b0648e77f51b995026e717947002c146ddc2423400db6d2ca129eb8b240ad3 ea0d2ae1bddd27c415bd10d2884415f7cd9fa83e7fb08c0f4b75b778abdaada7 5e852f2477bc631f8c05ad58ae5f5c37a347ef721822cd3d148ef291a2d1172a b71aa52e4680688181abefc92a23f803723d4666ed7143fb0f245b760c60c3b9 758a70b2afbb0a1c87b5ae10152b2afc6f1662fe5bd4327e03eb1919064dffc0 b76eddc64bb08b80ff89c5995ca2adabd3dedfa2368e581a313ee59f54f93d5c cffb88b1360420a576973f11cd00316856e2bfc9d8f109097091dbebe8c32374 d7f3a4ea341a01f08504a56d0608754573b6e54e9e9a8aa14c193a8b7921b47b 9f77d57fcdece45a5f17ecb5c3ebb2db343d6ec9cbf4d3c4208b4d13ffb88d4f 9d1212a557224e041d3a449205f24601ce3d70c79621b1ef76c05fca8c8d8664 9b1375ba60816c63f00a0189241f1b6840535503c0a417acbad75b74b8781029 e4755ddcdd1275ec6751302e6078b4848fc88008053f9031dd34e582ffbfcea0 e4020f57a8db6c123ac94bbdb0c4b1d83640f25ea2c963d47663f0c7e26221e2 e3d02de2ca5cd08896c63a72e779f2335a421da3130df31d825340380f6f70a9 6351b2711c92e1f96ee94505b4a06c9a853cb9af7890bfa21b4d8c4d5709c486 6490fd121b81ca403013a33c049d255b429e0372112aec3f2149292543229977 513b9a973a58d2f068d09af11f9eaed41c4ab972b0fcc4f7a17d9cf70d14003a c037235d5cafcb002e4527ded54bed12f03d113068e76bb7834e6a8f4e0f20bc e5f6c3b7aad36250796609e94d0f640c8a3f9d5980e86509400777bdc5d5a48f 04688e1f9aa37a404cc2eff56087072b7a8a4564fbaa7e73639fbb142f217649 ed16f3ad6c5e9472367c659ef7fc1411b8ac6169650cea8bd5cbcbd1f3547002 366a0776df3e40bce862d737c33058abbc7debff8ac7c9ef2d4bc748a05d8054 9e216a8fc44e7cc0caca6ebd41e8a6da7f4c731a14051655df1ba3887942df4f 02989a0fe01a6ba86f7f425f9bab5419c5b449831e505c09cbeedc0e215091a2 a3ea5c9a3b6da9ecbb75d6674e0f307fc0b5c8819ded12c6b28491e8b41b3553 b3b838f9d9c8a1e32c52996730b43d36249e19010a04879b60f4ecd553bd2a88 fa469aea280bf1ab710744a9a14d6e6460927b9c6d5b46e0a690e5f3910ffacb 2d0c04feee26b7ccf1db59fd2fbc1fc151e7d3ffb21e6e6d5254fcd34f69a09b d62ffcc5c6057c46a452bbfe1101738c4da8d517f32cdf38d8cf9e7f1787ba0d 7cd40bfc0793a8d461cb3c91a23f14036773537125f0e91228e63daae4193a5f ae655c23657d00fe89349ab5e9f9df0926d7f0eba8892d715246974e81b2a9c2 b2fb68cd54c307084a8a98ecfdc3958fd5c06ab83b68cdbcadf278dee3d19a0d e847724a3948785cc1968aacaf75be1fce5046d28719a56c0f5fa33510c14d18 239482c4db448a66723f55f3e65f281a9f84b338d8fd2b70cf6e8daa35725dd7 ff79690391346ae1cfdb50d58e6a0b79eaaae4abdea9780d3f7e8626b0f9e191 698e177f609a9ebe38d720f0eb15dd4e5830acc60835d3c0364600452b5c5f21 e976cfd5778f45bbd4ba6f0fcfe513cc8599e452388f987dcdd2527c851e871a 1e6ab72b27e63bed99978ac405a6691ca8f1fc982fa64434843c9726b3f05d1c 26ac97c8d5b73338d5c4b958019d53080b5a5f7259bcbf6ca92c7b0e9cb2a1f4 4f323e5570a2db75be893b6065b267709cb4845b01bb4be818e1c194be9936dd fac56842fd43b618d369585a72f405aa45e1991a7d54d4c4b7e30f1adf37f4e7 9fc080eae5e1291453454a098aaf229bbe38681c6993284392adf774464e9cef d44dc2f762606421505ccfb2a033bac191917d1d1fe46d5cb1efa7d620f0142a 2344dae7759d49612c6ba4244f1a6d29f018c2cb9ca7c486f99dec75b2df17bd 4f3693d32b19780e16aea284866e11e2d57993bbc6a52cfb0de24aea011129a1 ad75e9ca21bd1d85fcc2be73df129ea54e3bca43398cf2b06abf467f43e80d73 731448e0f310c52d2f23965cde9d8a0ba569befed24b08f6e76312c41193e692 7dee8a5645346e412a31a353b44fd5f9cf773dd88f3f5177d1c906875d72211b 395a2429fdc440087ba8ab5b68dbf8a3e3ecd503510e1b8eba9da6740dbbd772 16abed3fa0bebb8a984619a74d48c60b3f311d25a8012a969d8300c5ed3fe78d 5bfbee7dedfc77c3d71d7c8b558f39b34182f7672e38a77b6131dae4d48c1cae 72f527fd963bc8dee0ef2a329144a4bf0a07dca0ecf2e9f313affa98be56131a fc007bf10775a276aad7609c73d06621e3392fbdea7cf5cf25e933a6a03c1e16 cdd63aec6bf340ee8e2d59023e7f831e46ca8359dd0d7f62de20ccc2fd23aad7 c989f22d98e37638425183a7acfcfef40e4f7f73c1b5722d0e1df96072efd0f9 4ea5e9075d290f90e9d2a4d2a60970e00084f6a32987e7b4d82f93be3ea33e9c 839be52ebfa2c5c269e8d609da0ce61bccbb77f4b185682a10170ec69490e067 fd3f59e94eebcfa443e59b393009a41a698ebd6ba31d8c981451e18548da6088 b21336aeed539c0774d94efa636b934e0c8744bd570f6ee97e17a8697c23cc15 49bba71f1b3c3a8ad95003e68a0d9bd8d63d8625b179f0135cef524aec49c811 1ef8d334350e2ac96de3ca989c32f4e861863a350544c88e1ebea4a89bd5d450 a812f04520138097cecefa1631a0bd86c636cc241ef2ca16f18cd89f12badd18 da47dbaa682738157f692eaf0c844cb6241b355020db502603303fd68ef83094 5bfa870ce5af7866bf10de7ee1b99cf8e911efcdb121eaf82ff0e6f383e80ad9 2e2b1c8e1a4e28aa07f595e070d78d41f1be73121f6f522c8992bd440f5d7ff0 66aa1b41222c58c79372339afbeadcf1e5986cf7f691a65aa9d455110ed516eb b17f458e54f8a627de4d9bdedab75802d588e439a006a0880f0a732cc8ba3120 b09dc46e0a22f7bc4c2b417cf1495657558acf10fd04e7d80c23d33d71ccbd03 6b308eb94dfd1c547ec9bc4634c88771b1bbfd0de9db03cfdb2667c9fc7d673e 58309275178a5aaf17c151497baa6863ef5189d8e07cdbffb33a923661f6d73b 579acce940a44a29fe9113172ef23cea0529e8c30ba4b8f4fffebabdeeee6e1a 4e5c44e82d1eedda3c6aeac8df07b73103df5c1e4b85adeb06538c9bd7952b4d 076d2dc2b865abe785ca6c942a06fbbfc1fb39769d4e8221d794ffa5172ce106d4195a9fea5a9eadde67f2a6cd2c94511338b1d05dbb809d5a387bcae99cc300de39d9c5174aae4d6417bd4570e50dadd040914195cb25dcdf33d56467f9a2048f3b410580feda474d2cf337a8e542e979984096071fa04998fbaf06ac11ae078320c4f14009b3aa51b5faf440f4bbeadda94e43a11ad2b7f07d924e5fe604079b504591f91fdbdccc26bbad009715c54e23b229505fb392c88dc4c003d8470f8afa522bac4cc37d50a849910762e963342276e7563e8a24e3dd475bf5e6b503f118f9b9a5632fc70a9bd8dbfd86e922a6a36c9bbf04abea00d404b6476b170d59f2e384d74ff7d029778f881b72e5b1baba4afbd4a6dcb1596ea094b549f205796e2a7dc43505336fd3c3b6f23c60d66e4af7231094cc64250037c525c10b0b0d7b0fdfd4937243649a0a17367da92801049186f22d74aed1d4eda0a8b4c201e55ca007910ed6b9656e4d4ae769077cc7addcb2d607935fbe2cf9d13490530054a911815b27f1b1a2df7180a667954f9dc5ebf14dbfe0349304aaba8d6af4027235b4c1d137e055feff8e2223eca756712e40d79fc7f2147e6922afeebb8a01474346baea93b0b322c3a27a15dd84a78ca0fe658aed557d653705ae0eff5301b4b1b5df979252574108d8d9e79995012767b000642dcb943bfa2d251bfbb302f65425a46bda0e043c2e8b68219f74725b257c3b981ca5bbf89d1edbaada4d0c3892e3a75ad217d620de92f39d3641d97ec9af56f8fba1ff6055cc5efc6c1a0fa0412d5d27c80e20f1a12c3e8f1c2dfebc077c464a672b568a489b4f2cec620c056de899ef8cb3ff756a654183d4cc5e18d4e7cf682f5312c960683706c57b0a0c0794a0887d466b3e9c18fbdbbbc31b8a99cb17beb327db614033fac37a9b0dbbcc4edf6963f94c65909675c1d5cb9c64b1d805673ed4f2d17512e62e1ed30abdbdb7d57c7c5395849a0a3ee585d21b57674b25cfbd7978b59df09e965eca02a827ff0f337e6304fce78099c74170b87d588c2dcaacbc0764ccb43e3e96ac0b833c50a195bbf84e6fc9f66d9409370937fceca2b52ce83f6386c2a8a6694e0fad51d92051808abd4a5855ea32bf727470e574c198e9857129e51e994c286f04f9e206988fabca851667a23e6cee9200381c1ad11524619b708bacb830f494032913449f8a0826b38414b3428f5412b754898a4b3e3b5ab5b4b37483013a310572bc867647cd07134c11849f8fceaee9f45a994afeeb50d187c0cf5da0afaf02894e0cb40150905fd9867ebce02d8d660d498fccd0a4f0536d6675b8656cf00f5c191b96e074ecbbeed0ca69458ac857d7200c23952d6ff0410198394cb90605b4ebed50abed21ac4cbe7418e2f0221f8382d7ae7369935362d0c1be36984001442889156dfdd0def0744f2a0d9f3458b894ecf7e2aa2017b71e42fb8367d809c762c49acd60d12ea686d56a99f3fb8131b9684ec0675f167a5cd2de8ca5400b55e70c98a5d58a5258d7e24e9722ca1bffb55434cd2306e4e4227ca6f7247205d2c7ee0d8d3b47b6806e4b8ebd0ca4efec843243252ca9ac04dbf1e5e166fd0035ba80d797d926b041bbed65434d619f73c9b5a0d734efe2f15ae8b6fe0dc401fe69fcd4ad691844eed8bd585353e9c8a64424eddb7161b9b5785ce97a2ba50d9ba9bfe9f4152f1e884a8877d01164f8d4a201cb4f083c30bf7862d23d68400a3c8df53b28784fa31bd6fa8b16596b6d11f9cd85f3dcb22569a8baffd30e6a0ee702f117c5aee5cd75bba7789195eeaebba647f7fad4164c4769468c2105180ad0bd5c10e32e64184c27a1ae74fb2dc26dde05e02655f4db01b6256eabadc203f54d78f9c5b20814ecc71673ca293aad521967bec4b20069069ae91b21778a0174add58b6d9081b92a898597826ed9c87c450cf5a11de12324a2f89969783a030d65e6566acdb84980a71ce2d25095dcb519b3aa09e49164b4a5a9efb655e1035ecc6dd7fb12232018f8437c35b6a75ee653b6b6c19ac041a63ae45af31ab607cde21bbaae4c9629e99bcddbc6d51b5fb615907cf7ef854e89ad1fafeeb1f50f5d509ab2c27cf601e85e4dc09d1cefa399cac4eb433787fb3fc4f493ffe0f80fefed3e2b70c8466df94c1e962eae1055a5d1c52490d83a41451400035d81470787de2a340131fb0c2bb0c31fa9e123af8f8864374258c82d48c6d0dbf48d5b0226678ffa570b5b900dd5bff67c6a12d7257ed8e471367477c6c09fa17e1d350594d541c843d3b85311a596e6f7e9d7e85e54bc1ebc50f60d6a4624dfbe763a02f46e42e9a8449a8c67a60d7aea4bf5515443bdb6397f3c17e17ea59a7d4bc40d5d803c1f26f19be869cb7771861036b35cc2279fce118d65c7b366504ee5490384248630084d61897707b8d7f0ba76a9d11695a123c20029ec88aac47348d107f362ee634d1d8fde64412e082e02a097cace4523eb988e5116933b3a6094900961d5027386275f9aaa1e4d343b1e6bc62510151a567a6e7b6062a58a0ce5d7061edba0246ba20a46e9ed31ccaec25a8b4b366abe0f5c8ba0937935108433f60804583c37fabf4a4011d09b07b4b964e2ee263d40a4b42b8c2fea5fe10591e40321b01800cdee8f7d51d72154423d64f04e64cf645b8087f400122a26c821fe010fece63d131b24ee9dc824276bf9cb9379783150def081d1c2cd63784991530452d14a4dc1ba7a782fc3fbf068a26ce9c37c2fa2c30cc5f0bb95345e47e2990dc5d610a1895ea2a93ae9ddb8319316f79df4f3c34ae97ea9cfc50c6d684e9f0b6d2d47d1fea8ee30719bd577c2dea06f5ce4a5f7dc0a58fd9042abba8d729c0cb0f40e0428866994d71eaacd70890eb4603b88265aed7aca98d995be911cae074b02df7139d817b3c8781d4e04d121b6e26611d8576022653dfff546b6941d0bbeb462e8cb12b25c73bd22f7eef2f1bea25680b0995a0f072eba73dbcbf3d802892f4e5c9e4d3e26f09dff797b11c8e2c7e29e43664b8c7be87a3c5f48573f0487bc5cf50c24b78e701cf185e8c97721f7a45522b60cb6a7ca38dc83a09b0308924ed1e7ed3b5f14803199cbc085d026ffc77ef2f6e0ba771500ddd3f1c5f601162050e943fb8f0c30ef93da04c14af771da13b27d9218b6f31b314bd2c8820ab4b6358ca85ddd6c6e612fcbc33150400ada8b203019bfe6e75ec8387c336d0a552fd764a78de0641f27f01db258f08b3b360af227a080ccff85c75089f4ae075454dd0afeb32384e753bfd7cd8be0d45194fd515e3a20ac40d16f128e8a100b70b17867e1b88d8c8b76e539a538b50b1fb4715041e16bbe67fdf97d64d55c0aeddbf3218128bfa09c6e1e0361118baddfd586563fca227867cd297a140bda0de26071bde1e8f2ad9a43afa0b89c0065bde59c431c0d23f9e5a63f24768ed008fc4f2afaed710e3345b6135b015dc006d4f45d97c40e4f409adbc3dd1c317a0cef541caf1ff88a3c27f1cef509871177b7a37a4bc39343c2ca428493158f4c0f4a376b2e8a46ef7fc0ca117b463ebaa7cb011e5d60fe263a358404d688fdc204cb09e0ed9a8d3dbe460bdb506cfdaaec35972043b97d2783f74a75151b9a290e6053598e2ecfea5fb39b55de8e568174315c6c81ee626e8d17a80316d0bf9a0304746984ed8c3a3ca506726430f95a8e19df43841b5f973b8b108d3514b1200ca36916d782f0d1b286499a49e878072063391061bd847dfddcd490d56ab6f40b2875dea9a056e5702f8eda07b99abb562989c10bf8983f1c620b5da0551b0d0da3bf23de4643f9f627169dd9b438ad72812582e3b477b3bf7452c78637150c053bc76593f84a636bb02b7640e255daef3c96ce481579be76c6c0ffd244e1cb0ac5e0b578c2a2294604803dd548b9b6e2004aaee045b504564644ab7816cfac0c3332f8db7dac5f66f77a463d75cd71ffcd3921d83a9c39f8fa37de66efd9de0ba9a2722ab95f10ba88270d7069510c09c1c1534e733da7b1770ec2f7d82b9a0c5472d5dba5e8ab568e2160122110e038708eca8791ef08b602cffad122b01e0644c0b0f8c1c5fa763045b835a0f9bc40313245dd3c4440befea5bd578e26090c9ca4475f96f583a29d808a05b18e267e8ba73dba546546233868a8ce9bad3e0e2805947ffbf509401c02dcf0cf447d8a85961f6abfaf21a596f01dc6717349007ba0da0977017416bc67b5e682942c3d1082b6be9af82aac1bf84254e1ff110b96b9341aaaca1aa969d1363596d97df6f7f1ca59b17e06ee6fd19fb2efcb690f10ce4b7633e29371f7b412a0e382fa5204dd2690cd38b78363baba16d4d6060101bb43391d6cb584c77c769599f08ecaed4bb86fdfb35a72479698d5237d8303d9259a5ec21d87e27d5b09ed1baebe2ed4735595c1a156c126a9d9bb9f077704847edd0586dde3a49ce79f88b0bd56371b981fc8cfe8b99a461e9905e032b7010d5d535550a976862339304df745c07ff4cac590b538c52a1ba7fa9d20d0ac012867eda5157b9e8f7b804904b0b73cb990065e9d9fb3b8c0d421359e5f31980f5f3230afec937a205ab235f4b56f1614a913501015cdd598aa66a7e240cdf00b35fa0116b8066201df2dd4c1207a6b645588a9ab93489f2771fda9e64e5c9f09de226b78280cee965c3140052b2aacad35751413e6ee3af91d594df6ab1d8707591e691505a4e4fb3a700de0d9873430fc9fd13538cd515e4de584f6f0138f0ae39b168364d6dc3d7e4874f35b7427f6f23caaa47b8496be9c531b92d72426069ce8386b339e188b0273b733b5bb08245070ae25c8488b0c0ec4da5de7b93d0f11c0b80842902171aa88888158073f9e013c1ebb609bcba64436f436fe923a0822950db58e1329e279826f2aea6bb29184fb84aeae83874ab8fcc489b1aba50f4b2b060286776301a61d4aa9d5e3597dfa4ef5103145fe602ebf8cae0a96170cacaa5712361e32ef6bc1fbde264f940a225d9b82b40b350fa890e55520fad4022080d9f43df5ebf35796e65ea2e6f2fb30db869c4494f5549183fa61eee0d906fdc65c1f0006cf01905cbc3447029fc3d52d8fa29f59fa2af3bab964dff2b90a9a277523bab3b49ac058e995dd02d6e2d0c753fbae9cb289162673e7522a5a0aa303a08b96a6acd506e20b8c51f3ef45f6297a2267e2349b031c3ef5a81e410672a647eecb9971cb6bb5e93351d9f25ee0b8d08c94b427cfa7422dfa9cf382087ae87ba2ec8e19fcdb0e7541b9fce0b88b2a5d0cbc32b6029c34c23d7105e80e80b7b38747015e5ecf6c0c95de67d1e1477239edf4d0e43df0483bc46ec23009eef5e436101c4f924768708e718ffd722d56650c10c770375ce821d3a1d2210fd57ab222c867dd339cff720f871e7fa097a19208b46431a7e6f174618fb2760ff6371b3fca79b8622e76604969685aa6121b213aaf56a0a49cd236e852896b0065067b1600a5d8ae87cc5e579395f26dec1aaceb6d34e676309f61c471ba1d0d9beef7283e8f3ab9ab806f47c57aa2a2533868f1bd7b1fa502e9fbecd3ec6d0ea9e649430666dc9e32684c3d1662e1812a8ae0419edfdf2842d8708a3e80e10ffcbd4de27a3b6f6d3707f54c3acb83a9ea8eb716fefe872ab8b251ac94959d080c8f5d8e3f5393d96291d50530afbf1e6a56f4b6dab807f4fc695567515500028b51e5296b71e05d84e1977af62105732074f9bfe4acae7f2217f413df1a530b1f587ca63c3ed47a6aeb053cdcbf7dc214abe46b8f535caba261e7bbfae80b064f45881066ba0fea5cdb4489777c29a6c79c739d749b949f520a7983fe57c5062528743b1a218e60fd5067cf7b7cee02f95cee26fee7429a86799223a11ca803c8a6a248cb21298da0b4c2546d3cbc7d46f0b5f2038939932a9e05450d917a0f0e5da9353f149a265e8a3fd182b00816911ac4a86bdb70f9140f9b4cfece3800b652e2eea57a62103b7929eac0f925009a75e925299b9d83ade0b40bc448ec0688712a803921a743f5e92f534b388055b1e17e75371cf54f1964d2793db50306702d7087496c0022ecabcd4556c718dc3905d82c0cf60604632593cce3c4cc07f8128dc2cf7036fa1eef957b2d45678b823d45401d78c0c7f91dddcd984efb0aab5578028b5ce5e472925fd03291151cfaa3f56f87ccf050fa20aa23ea1de70537a9a600032abecfc20601d736e5c4f2a6c2ffd068e70b47145d3bfd4cc0b405879664519d32d5aff6df6816a39403dc0127ce7cbb17d25e14fa77dc7a70420c8590de1ce1f69d76dd85708bc70b844f37b5e7551445fdccbd9a2427ead69205593eed4a3e23e6d864eb917969edcfc66db8c9472fab208e96fcbc56d4584f02e040e96c94b90b7f4633244965675c8c2b505ed7cc39c636d62cbd3e931232052c2b318c510d918f376951ae4e566b37686b7ef443153a9cd9cac6a408a9af0180a4518b18eb39abcef0cd9ae6022728976ed1efc01e1d039fb2916210a93204da9f72bb6c5da586cbc94801f81815a65a930a5e165ffd5e551a605603fb880e4f76b0c4046f4165ba3afcad13ac1f2878c60f2240b470af1b25100ee6781204c3bc16fb07b08bc2017844871b5cef5037ba5ef85bfc17c25f0741bb337c1706099838849a6b0fb78f32f15872362c30a9c64aea63f250ea004eb76f7ab7b303c0fecee84943156a8001288fb13715bcd35fdcb546f5eec8241691622f1f000dd91bc8fde56bc9b66b3f64fc574adfa7b26bc2551c2319efaf5086581e11190b9a16ddc16fcb4520d111e91a675aae7e256c96c7c63dfb0e85abd0e48007f80e77f0fcf55eb2fabd04f8a711092f3fc8e09be3a2ed3c4e0a95b73b0ac0560f04749995343239d3703f3095d89bfbe046be57e21ee3ad1c2ee26c25a0db16ee0b8f1da7355509866c72ae8d89c1874cee8235d7e6d307c0a9b2c8acda2a8fe40b7c99d240962852c2d767909d924c3b3a31113c88f378392a03b8b46cb4f1ef06e891b19fc57ab0a3b19d48b6f3a50296594c5648e0e026bf1049745399f97a09cb1dd59b403e365fc0757702211aa66eb710750868c071d1abb28417862c3a06c1b3843c1e5de183bccb09c61e572555400b26843035af776de828c2301433044adeda45fc83afe17a73e219e5b72129562a48ba6b729180970f5b47a4cc5205fb7abce49bf02c93feb8091df4ebe910bcba02e565352896d8e2032972b85307bdf4be1b5a33b9daa523be29bbbcb814264d9b14d4d7c7ef8c98664774f24b0e8f48a8e07a20e29e19e053b923c769bac272bfdff887c1a5400f600cb0da950aeaefc4d7d7b277c7f6ab621a4ee85b095781cbda7bb39b4ae6a847cd389fd2037605b4767ce17a12685fecb6c72f36e32a6f6b3c46725236172efb4db1c04305f5ebef740c34657f6a5854cd4f3aac5b0c8424ddecedd20153c368b4729f62035ac81d00474d6dd3af1784e6eb9e76b747cf75994385852908fb3716d0a1f1018cf4d4e75a337ffdff0e207b6b9196afe09fc3203e38592bacf89559f303eb073b9792b9438b90bbe2a8c6fcac4c1551a406d3f2f334a83ce701ec97e0e238007b682d1f39e57c926bda7bb25612df59bebfdc441b76d71c970b7878c9d5170d951becdc975deefdb52d87f8f2b6baa385b9180b8128ef66c8f7d939cf2e7b0eea3a97dea9d828b9f5a7842771322e7f2569b0850321802732017c2bd4785c001608e762dabc7b4ab2dea171ce08cc5997bb490d013c2ad3c5947590d7e9cc04980dc1b84c4bb286910109ed642caa31d13408d428d3cef8b16d9024d034de00fb13ef77feff2d84c66ddb0689714dc3f3fc9a66dcaa5b14faebfa1d4a947a01fd3ad84c5c81c043800282d9cf0d84510918ad2a6682304813c1bca030cfd405d950bdd9b694ef040b0cc6f20954dc9d31e0e650d68daf4606c26eb7208b5f064656b6abc13fec6dab84524a7029a18c9800f13de2d12ebdce1c8f6fe17bf4054d172e8877cea0bb9e24c7095a539b5ad87de74a06756286e498ce998491f40ed8a90250b9b1e042c7e5e67f67793ba6b997d5916aba9c8222e51d987cd7cc03b6510a0c9468b5f9a013cf5b1086cb5ff5a96ee1b6ca166e7fb7d63ce868690854c5ee0ef8d57582bd94dbb9f55cd6861464a26913586153103cbe8f02c14303f76d2c47782531ebdd7997a67ac29e27c022a875700808b5d175e70ec4b5450a00846d3e754d1947f744a73e2afb4933d3c44b537f76c80065d2d1df37234d023ff2149d356a6fb2d337a1ab5ff79bd2b52e632c1975ca9fa10116bbee672c014a07270e6a285b6416ccd5dac884d6d00a855188e5f2e3e90f861d7d7b31c103 true +check_ring_signature 5b66c1833f16bbcb4df5e318fa4a03560872b6a471a0cc9ba500f291830d8d1c 72b996c9a5bc9504efeaab5c6a7ebf462c63e82a247b242d2c88c4fe1949c548 31 dbb66d0fa52e050a107f4fd1cf2ac927f688ad996bea5db1c5d0dc50e8392c97 a50ba6b711b691c5e058294491cda01733a95e6b258f4002e82686c6a510f6aa 51cdc0f7569d025db00362b9e73492ce6b4d805ed638646ddcfbc419cc699d1c 4280269b9bc3f77da39ed483730d94df2e8b9068df187c636a4da0b7694318a3 13c89456c6eee2a1a3a47444c071a9ff447fa46cb3fd321ebb36db5f8278006f 68829c5d9b55b665a2cde111fd944a6d919161c1862b4208bad8d7df67d67bc2 98132a3db2894fae3643d8ebb3a9bf142ef0e1ef5dcf54149612491696f04017 34ce813a221c349a6733608e932875bfc09390602644aecf5aaa5fe9178603ef f3066754d69a5cd71aba1f869a8b2baa926dc9b8e055bbcc9b62601ba52f9749 1b92d8043269a5f78edfaeffbea587f0df7090403b56bd72ebc920fc3361f7c4 bbc9b7d9860f0fa797fa97207cca769966e85a640bbbc52feeb808f328790239 0db01c4387577a9254bca94f67558f536f15ad4a8196851d1d3520f6797cc50c e9824792478eb72115d49e55c1a29d576b330374988f80765c16f349cc137184 498c3aad61bc810ab9b4bdffdf6d96af87e2ad63983b8ad022b6dce298f4d6fa 5bb0709e9d8c8084126b585e021be3076df757e25fe1c7f429c785b74c9eb18f 340f82e508cb4c560d99e905e0505b7be23c73d9efef095d52c53085a20b1361 2292b401cc91f30d720906fe3c50316b68c6718225b3c2cc22c0355b9bff538e e1315a9554e3fc59ccaa75699ff6828f424c4306dfbc8e21929c2e9de740b86c 548a9c299d9f368de36a783741a8da5d1a53c59cf1cc8007b05f34cdfcabf386 e16a4102eab3a68625f97e162dd6c6340614fc67bea2e8053c9d28d1f113db77 d7c25c598dc3a0c7ab178f0baf22785473475dfe19a366e57cfe9bfbd6d63c93 7cb12ea8f4363bbff6a0ac73284836f5a35f72f3320b76bc5e76639d9f357956 8a999d5be882673110ff2a97dc73f97d7ed489f0f174b3f51c3e5c737fbb5b17 18a057a39c382882722097b49866751947b78a83e149d2af384d69568b67069c 945cd873b8e2ba79536e98865453ac2dc8e4808d9e3f8779db0175e9914052cc d13250a7c63a66bea6636953611fe6e368c3ba9e7cc98eb1ebe7d5642fa3af4d 1ce744f6c7a2e5fbb1696b1557f8f80bb7466190bee5b6e95b9ad6c52df0f25f 10db3124659c2ffef00dfc4151072b8b25c1c61971dd6455b6d9d339de544766 5d2fccfe600a58238c65f98dcc0690fa7809395c24caed4f9b88b4e41c22dbd8 76186aebff3e381c49088a2c5c3240c163607f57fb06ca2d789cf7e781fbc6f4 0b768885d3759e97116468b3fb0dd76c0fe24360deacd7b137112b896361eb31 a95b4aced20ea18e1f454e7197457fb35e0b5ab570bc23ffbfbb8df24a74c607b63bf009f8d973684977e175891f4d8e2f9172171cf1233e1059e2ba32bc390849df0f85cfd0f76d1542a9cdbad38a4b6a90c17ec0b4dcd052ffb9138041b903801350b430352a050f5aa9bf8125c4ef297cf7c96cc1d126df60c2a90a2c1c044ebfdc9fd4818b4a21b73c651c203d7762e3100152be307d2fe1c5284d4d3a0fdfeebbf81400b857c50c186e5160d3edb470ce36ee471f5b9634dc4caa22db0020db2da2f6a8b81ffd1b95e107443afd07e5f439d3e7c6dc3e141dca3c5de201707b3fb1f375e816939f752d570c71f81f83c92dc7b2d2f723fec39ae352f3045f35c6f753b3fc40e915f55131b4519351e8838164863a29208a474f498b53097dd8914961a4ca38f2715f162e8912ff8fc2c00a13ca64daae9b7ff4fcbb2b0f92fd876de2c14a3c939e532ca4c3ad099321c8a8b202ac86e5af095e10c9df08d9463da930b834e61dfb8a74da62b001a89e690ce613ffbe1cb49433da83270aa5f5d37858f19eddf6056686f897703c07c1672debbf93d4be9304dd79981e0cdb09a928e5e4641d2e84bffe8552d822ab91359e35474f144e37197b7560310db50ff9d22e212510a620cdbe81b3332a1816cc6034528d4ee0f74ed2f4118e08993d306ed82d67ba02cc9d934288819b95bedd4e56c1ef52aea0c0dd12f1190ea801f0bb22f0e6e1f0a0961e97d3c636fed3aa171ec1b3cf7180401ef75ed006d87075ae0e3463d47cd3864300e8c7994831aceff1300f8cf530368e5662130125e4f62d887aee7f94489d2252ec823e604c6024f18e135a848e48aa2ec43907c64033a1cde887d2536324499bb5654727a91704d5250e1ee4f8dd6f0c464c04c59717913add44c8caeea1d955ed82bfcc51cb19b14c427116da9e69a0088f0d1f4ff7a4aedef5a4de1c5946d52a9e7f2079d93f5cde3a1662c7f2b02ef83b04a3214d0a905a6edf636eb82b78bcb3dfb24550239b4d8e1c1121ea1dc88fe0054734e9e464710e83edb1133e0b0134a33b784523f14b98e34cf2a32534e29209275b90f0923e7e7278995e11e63656d254acfe6bab4bf54f8caedc7329bb6204f6ab4f3ecdc7682deab02825f1a3a0301e44df37a75b65e779cf5529aa0dfb095e94ae664e535c6533902e4c576f462a18c6e1b798eca904863d8bdefba27306e387fca8e4f465a129bb50c6151f39fc3301c34da340b2327ba05b65a6414b0c47991c2bb277b4bf9bdceaafd64aafb99d252cc160bdf8adb3642febaed9d80229febeccb3100e2379c6d37ed2fcc28dc12366ca49f2414015f913cddd2f960537e234321e4fd09af5f2ea2eb8df6b5cb808635fcc4d9e37e49cde00cca8520735e6d4e6ff0873b170c64db62655921f85a53d68b170cc454a37056bf4c0ad02bca9199fa3752b1ff57b7e862a434f857759a61884b0d0390b2e138c0cf6a406d1c03789bb0af83f598634f408ec6ee90c7b59d1db887fee01487fedb910ba00de42ff697ede44a153f7d1110774cdb85c9eec5245eabc642f42857b8656e40b74c9a394464223e73c041e77f36b31f38163b6c1f3cf1f2fd438ba63cb9f230776344c61846adb6dd3078d3b6b685a0c55fabdcf8e0733071ca4abbf1cf0b80f88fc64da9243ee981a8a73505eac81961ea7115cc9a597ec87fa96ae1504900b8bc85e475a2ed3bd5654b930f1326241d4cf3f6030bfce4cf04f7d527fd299038356b99c1857e538970e1253cf48cdacbeece0ab8c37b427d452ba3b0d82e6090a1416bba6b8b0713ba5e9eb08027fdd1c4c41f391da3c06f7d8ed4ca7b2a008634b71fd3245755deeffb4611f255efaabe1b1d7fa2d1db301dedfe2c869fb008608b796647126505055949f0ec902c584e0d33162dc63a1ebca344a3a1d73028c59a2d66cafe51a441005177dcdaa9d583b44443e7a353b1a524c1e50bc750dc35d4f2833cf6b84b3d85f0f9c05b31c699dff07c9514b9fd52fe5551763190e93af89ad5898065f81d74753ec8970572cdd30c180d883c62a8980e723d2470b28e70c7eccd9888989a21f6e2b6ddff074e7126c39cb719920d909626a6d7d0286c2238d8dc2d28b8172262cbfb4f3d4898c30c7a54c4aa3dfe73db7f820690005cd5b74f3b3da14cdb7ea0fae9b18b076c434a143fc63751586db20532ddb015d9879c3a202bda8437e9bf79841c845e9002bd1029490c04ce521910f9c470800d0bfbe7d4c80ce0e57acd0ccf57f4e2d328b11b88f53dc3a3c36b9c40f310c14388c66fa67dcfb287835c55d6352aa55db9d3e2136c4ba3efe41b46f9613001f00715115a53b06cb753c5d51461fdd9061924db8340e9004e79ee69137670742ad0ea77d1e61341c2d7493b98d000c6b8a10f8bc60bd87a1f4d007cbc1820dc5eb0f0125de033202a99aec78b562c921dba997036b9de68cac67c8dc886a0d1f7482c3a0a77a36336f9b6b2d16295d4c957ac53e30d8e61b36bf69fa297807ebd8048136fe8e40827d0312e7c168512e1e72125683ac85efffb7d5dc2e00019f7265f17520ef2e6e46bdda7dbd2761df02a3f801ca6a892b8717bee37a28071d1be6e9da8d6c6568aeb1c3308258c03f6e391f2c0e75f4e2d24880bb8ef205d1e7d4aaa809aec8fa67cc0e7be5adcb7c76f3447798d78064394ddbec004f05711110f457370f3d43cfe08964bb3918aaf151a3f7046251652a3cab792d0a0f7028fc02db07ad170ab58b9010c4127f3a91a02081b38ff4813b4b8257a5470b true +check_ring_signature 932a1578ae3d236c67eb54a69e50d96c87d23602da161c0690172e177059a1cd 0d9fe738c79130c06cbcd6e3389563fb46f08c5773ea03dbc69668cc3724c8bd 9 b968fe561a86be37bb0de8f5dbadc0b7ec8a799396858dd03de6bb3eb60faf24 149f97d55d089426e249085f10cbd7a03ac9137e831f6a88c88cc324eb4e1209 e89b23d293e206306245df38ed595934f44bc9653003329f9b393e6f073fcb6b 11e4efdb80b1f5eb9395da7667e3e7882e0024b7d9808518e6685571375b914f f5de2c39a47cc163ac4b9ac0fccd7012fce323c5553a36dde5c77f1841c4386c 8a21e41dbd92f9f9450069663cdbbfc604bda93310a9d82b511027b02847ca28 a4486eef2640acd0262397a2e7472742d5afa9c9c49479729ebfc464421c6d0d 6559722ca01ffd16b917ba56c7c271d47681a0b16782eb034d305187e13ce8fb 28f574f2e6c8ff4e6bdec1aaf37b40c809ac367382ae0d3f0b380d3019c45141 4fe108e3110de164404f5cbff0f0c09074da50fa1f019e40cc7044a13efc2200c40ca23f266b8d795a4000816ac18694bf503b3f88a7b2f0c230ca5536972404ce22c8e432bbba189741b3e916562e9866799a96d53490ab47c7a4a70dc33205b8e7952aa41cb52869334a4e0ea1b2baaa927b65f745968f103f00b48cea480476d8db4d73aab50e8072f224983de59b6093708587722501d1cb2f3056f86feb88332707b923ee466e04eb658cf702f9d58df584882a61a0a85948862a8b770bd18c23b20502e370fb9c9f4675127a8bbf5172d4e3523cf3675aca5e45db9702438d050e2b71797d42d2c30f1ec2b365b5cee7fecdbd5ef32a87a12b65bf9f0715f27e8eeb091aee5897b155e81a8dade88ecc2e2782957de0af85ad0117ea004096a41e7813a7dfde198018cb2a5736ddc80cf6cc0d6b324c303addbb343803219ea923fb01eef72583c5cba4dc447c554aa897396a1aea14261c374ed96e0fe08d5e0a40b61832567b5b0c91c65df05d1b7b39d295217b6ad91b863d397300e94ea9c8fc2f6c5e8420819f9bd2d03edeaa3fb386f5de30349f94ac70bdd40ab2b3f763a93f29435a64ae8d65d616c2022e989463d7f3aba9102d887338cb06df82096c3d581340f64e2099b681b96186c91a0c22dd91ff13b8980806d961033e1267071ae9c9da415db6fb3ac73f63db0d26f14e753d20cdfc89ca980a80b2bd72bfb45411f89a07bd560c13fc22ff17219fe8db5d581c0be107d44a7dc50798a9b471d802780ea9253780b78a3733fc803594b4ad66b2d299f59f86b59104 false +check_ring_signature b9282c33011f62630fa7f9010135d44d5aaf7d0e9484745edae0b812ca504093 a102f6f2cb843bfd7495761a0819c6381a4853ca3c085b912f709b43d15044a0 7 b1da270e0e580fa2f739e698a6ff930f1e09a08ba7f96f7781d70c4f9398feff 2f05b694ad8f36daf1c0c432a812078134ad8c665741086e6b2cb910b9b7b74d 7a0d62fb31cc1e7298b080c9f6e991d89dd562f0779af3e9f4e8abdd8d76cc30 001ef0fd6485c975c2152fa39ed2028d48e2c2ad9585cc509de0a69a866e0202 f02d549a15f5ecf77599e88b6a3740e2220b9d4827f5ccb5b3fdeba0a45a1b52 89013abf6ec36ac4f15339457533b9c753b7f016d45a15e2a528a0c5fd0feb4b c1c5483508ba5174108dad5fb478194a5d6555cb8c1d79f69d0ae86a5fb2361d bce3589c38129a84ae3cfd76a3b67186aab6e99715a094ac6dd30cc39fcd1408599e8d744ef5b44b36ec357a70850810a0f80e326c8e2e3c73e50ce1180c500f5351f974aad6fe825ecaeeb19f44015e465fbead84ab1db4cf7075eb12e6130fa50bf8dd0b2bcb93cb2601cb80fc52a8b2cd5d61de66876179605d4fdd57470aef9638800c30d5edd6d0f33acfd207a02205055b6d9cfd09cbc137aa3470e10b491975daf5335f26fd64562f820fce26e6149e3f6672e718a296e195c2407005a2b95463c31b44ebf97f2db0aa5c2f782ef7ff336b93c64b5bee2a47075d78041d0a8442dc9edec832ae5208b1228767078b7eeb9b937bc068c8263f091cd700201522dff7f80ad475a12440727c16c14af798dc601a0b46667688a4f84ac30666c79b661810c6d021ebf688b6ef3d6b39d2b1c15d3ca49ed5e056fdee5b600181a886e7b6bc333c165ea3eeb9dc155544def9591a729be43ac03505627a5a037669e681cb7bd1d0f1de5eba21fd0969b26c304e12398755714f482a86f0ca006a8f84594663ebb27e33a45aa220ff96d5aa8cc20deaed83bfd179688874770a69c8451fe0b6b91c561891001ceccaffb44adea69a4d79c91e263e601227ac0c true +check_ring_signature 07f22157dc661db19b0eaa00c22b93d3d36f035ecf8c4a3b591b7aa5218b3a83 2408cef2ad10a2f36506138af04646b639059b662198557a694f3be89f443621 2 9f220257fcebaf5e4b96e5c68264a46f6682f77a70a63a87e1f9e847d67576f8 535761e53d976dbb5fd3ffa96ccb539aeae5427507c989013ed2f58b6461c42a 22c3d69e666ed3c22b2da209f8a1c1e14954756a40b1181ae433aea11300cf0918fc71100532186556f2e24d6ac04563f9c1c04031dcede01848ccd684b7670689eac17db61df34aa5326b46e4828cd9607b91dbdef8e8ab101e62322c14ba0385d6680d7474bf9a5e431ee5f0f993acd7e2384ce06bfe17591649382b39310d false +check_ring_signature 26d102c2b866054256c28f19bcb4464e3c86bd020d623f18e9ccbd297019ad8e 841fc3ef7e5d7d43fe935482bed5d4857578110acfac53efeae5d91be4969c6f 31 a291b4bb4262f291bdcb352d4573bf14722faf9b8874554fbb5eeb53826975ac 696b6366e56aa4a2dd5be9ae79f0764cccf3bcc702d31b8a71f2554c3706d939 c45883e6c430e60145a68ddf4add17b4088b15ca709104d9593d29efca0cd792 73521ca7c74fd172c1bddc127889e0714126d86a384995fc90c3807845cb0ff0 11d0e7007b35051608ec417192026ba3b2d85daaefd210c4f6249a5700e9d268 a6c14f3c36b805272c56619513411858ec8b1bc033fc83239d0d48e0f106d85a c137863c8f1468ab8097e30266b3039312c5eb55fe90cd6cd5ca1427685a7fa8 0a8171350ec35261317f9da5e922b090f842828ba1a94bd13f5b309704479856 43789835cbe674dccae8becf2f20b9dd5e9af59954857fcf040e5b6065d772dd 6f0ca3fad4c4890f272bcdb75b9836e06f0d1db087541b12bbeb68822d93f0e8 558e2f5a302778b3950b18f69810713776c4210c5e275cba1c4b3d20b80d7cc4 78c3de4f5e7e377e70f1df8ca2ec4e49ade53ab6383147d22e675caaec50157e 2924fd139a9447d7c437d81cd37d5212e11c9ae374d9cd0c8bb95acf6b06fe6f 3762d058d1057da44392f59c38ef7396a1ea193018c952f2fd46f0af212b6503 9fc1dbab823af6a19ad0d08b18aeff3cd98b003afb7dfe5cfa82c227fd75c39a 3020d0d508911a4db5ba76dd780af1a29de47a88e0bdc1cf5e7fee7e260af7c7 59e051d87fe3b0faea77949419ef6870c36554a966630a74428fa5ac32d43ea5 13fab9daaf3377ad4e8c5344ef786e12fe5170a53ee1702ffe8af1f3c83cc5a4 6c00eee221f6cbb64a9f6acc783ddd8a5b29dc533f5efa5579a9c5fe4be99fb2 6e29b5fc84b9a39c8d05c81a98326f16fc413860c48c587e9c3c4d8fb2d60035 f3805954cec27f90176e5d9c81bbee9f7abeb8d05d81aef8045adb6fd3bf2082 6b73039b8283a6581dd0354d73002063c5f0bf99081e6416b14a3c584cae4c87 f70c3a3b32dbe1fe5e6e5b3057fff827dc90be8f5e99dbe625a5cb6265f654b8 9442cb9935391f253a550e85e38bc6c6c2215b11a04033ed3c609174cf796256 a0db8aa0049f0b98bffb6bb37e2727003d288d35c1f6ce4f2930af16299da520 3266f3934012f7f3b1464e3aecaf3a7933e9da9879cdfe8ccf3b3c2e98bbe1bf 934ca236023872c0a9e2583208daf40e7ab0dd9923816b83f1e8bbeb1d63de31 736dc7f3464aa8fe78ea9adca01ea42853ef666c06ac730dad80908bc792331a 23777507ad1f8262470e4cd65dd00a9ed027d5f0cd16e814d5315146c6e90dec 01c6d9be8354d1758af482064e02e0fdf207ce4572ef27951a8c8fdbd5f0b082 9b63bd3262a81c4ef55f4cbfcf80422f32793c8b231d896245069f234364df70 36ebdd8aeafaa6411550b51eb8d8eab8fd623ca32525d8f1e98158253ec7e105a5282eee3d152ec6fa7b4c7efeb3a8af3bb370b8fe67e845b9a48aa101926b0f5a9fd6f6694670a2c48a708dd2c1a28697082f7a230bc1f02f23a6e14f809a0a7d2ecb2784188f68545e8c352fa952b184a51a9c7cff9f9e182cf1e558dec0042e8251f54142cdac2d0d1381630d833ec95ac50820c1faf91b46ee47d4e9e3053957256c3810872655b8af1ad82bcce0fcb06d8f0e582314d166f4df2b74d50204b5c94c76a1c9081e94b6efcee26d9f7cfc3b35e8882b405976952526fdee0422a475ca4d7ff33c6a91076f720550fb1b1e95e23b2b485881fe6025b4921d08125a025c2f72c39b81288cfbca910e4673a2e3f5d93413c9cf8ee562b0684f0072338c78511fc20789a5f52842b3d0c11251cf7d41c88c4d8a5afe4985195602dea049dd6a1264cb0f099444603857a875ade112d7e90402c4d3909041a4ae0dc7a861787c86e121a8af42824dc835a5219996ea686e42e8f983fa34a833060805c4200a18d7bc27df9429d386eeaf1ae4e6c130bcc23d17e21dd8b49f39e608b0bfc95394888dc81d6d45a47922f4ed6a7bb6849f7ef4c6a02185ada5087e0fe824ae51d79e1611634ce519133b662de6736c5009aa025834449c19f0541b0e6b102231bb40939276cf1c83a2968299b04b779dbbc8892a77b01f4706791a0edbb96a15a442956daf814b21551a3c9729d0c2b208e57fbd73a80ea12e7b1b0450c665e3d9510f9ab4a621605a5011dca359288787e682524407818566c56b017ef045c42203dd47611118c4901fc6b9da335cf721b13bd4a234bb14be66ce07506e47001203a2f1ab17ff8ee9e6cec628ddf7eb0e2f9d94501a85c84fa3b4087c216519fcc208bba8b05d02ef16fee94daf400d87bf5ea415c4a43292bb300569c81fa53a99446d7702ba326c567ada21899f5372aba1c928d1ff478568f20c002c8fd5b6a76d27b57c86a1a7c26b3c4c6edaa0f5ab7ca89015cce67372d7063f74fd2066d74731a89505d23c8c56ead638a325bd7843abb1d6728762e82802aa346e8eb52496f08d5b812d1673285ec1fda8e98b32806a7fe564def7c14106e6e7e0500a4b2cb3c36a0096b735500909ee94f9d6aeb89e87167d897df85a0f235df3c2313dac1ae85c71ac4e1e7b0d0ddb24b9ff828a8076d278e2229fef0bd02f4dc0129b8976a7f080f2c5309ea936447e9abe5ba15892ac781af866a201b76c443ecf49e253fbc83718c56ebaec16782e3b5572a5f6344794e37b08290d49279d8fe08fa64f16eb1ae0e7ca4a553de019bc7a627d7043a7a19e5c39b5084c5c956f985e73056084b1254027f51bf6cdd02804f22fd8868f29255848f90e4dfa8323ef5459762da5048d65403ec0a5b5d4e786e2bd65c7e00eaee5fa40017bbf6d65db37d87a8000f114ae45fd1b22f1815e46c117be3f743c94eaeea3083ad12ba3876dc96bd4fde63e3a1cb0132b4326ff3477a11cb7e8777eb3fce500fd60d49756e30b630152aa2383570a4fc48d1f797b6a6973ebb1a0d867a649024df167f4b8491d99ea9b0b4302cc28ab459ceddebbb9aecd6fcb18ec9decee0e59cbe0f97c2803d1f126438d67460a8bb255c975e9646d110537880527f7920a4d409d4a3b15e82e81a4a5fd1bd3a32bd523c62817df365853618dd8fe486a0e3b6b0a74081f08f44da73817a461368c50ad08b8961cc2c6cc6d561ea72e060ce571779931c10968bdcade1fe97b7f0b4ebfdfd07f9171f8c4b0a8864d208d0aa1731af7b44222818dd83bf9697b3c3ea84f8343a081414cbc66d4b42435760805a7be105b1839047c9478e6041fa68d5207a47cebb7afd6484e917b6ad37b07975af29cab7eccd809c94df7633ed1d05617369b801b9d68773f6558a0b8440bcfcea5c53b1a4e40ae834220d611d2629e6dba4a65242db8ed53fd72b8819d0e9ca117ef8ccb3ae68dcd978e08960251f2c0310d5cb880eddbd1c0c21a8ea306cc616f0b433ff1a3ade23da81c318f52810f2258460b88eb5bd28fe76b17a400e17c2a124287730e2ecd52b5c6e02b03bf1eb23db9aa7e8e5c0185b0d0c7d10faa489664db67a4cfbfb5316567e445a74d2869900d645fee95a520da6542020915c140c95eda2f174c7b0998155e7f2a6ad74efee4a851f1fa194848063bf7087a42b9362c342abe162cba012774b480ce3d56676981374ad87ebd9f5f671f092d1ce71d9ac2fe31ab53053892c3f371274546ab5de8d95246a54e3aedb35c03dbaac2f63895ff180157ae40b99ced659ba097a41ea5656ee4e9c21ae11ade09b014c4ee6371043d3eabaa24b0a5e3ed666740664e15532bc52946512fb57207017a2d16cc6cce9e08cc64420d7f73b6f02cead76edf5f5e33460ba22c8794002d4b20ed61c3d95d420be7bb02b5a94a33bc810cbdb115985155696839dce80534f0aa573ea07fb2dc5a4b115c92fbf2a1dbbc1b1f8eb0c53dba5d6c09a07b012f20dc90f7c5ed5f2590dff4edd897fdeb38e79c56e010c0651d01c693d7d60b5ba189080f4a68b2808024325d89e600509594414134744e50de9a3ebffe7b0d48a2b793241e9f938e78aa08e0d54e7a3e3901b1bb6d0017b617f19ba15ddc095d619ebd2e96ae7b307e9067f00658f2113e0006635d9da9e8ffc4ce583d3f0362381812bbfe68dd4fc8c929d9a7573172ef6553223d8e3b057565a6ac7cab0a31c76afe5fca1bed8c8c64b9253502264350dcf87dd025fa9960df84eb60d10e true +check_ring_signature 4840e12519fbd23ca6af6c1e40e608e9e4c72ce84b1ea133c7353ed5945a8da2 e0a0f19d09f682e715c23cb22229c2e8a17a70b11e2bcac6664f68985049ce60 24 b8b8d5ad75e122a659198c26f7d38b5c5befc82341e0c5a0a44d16622b2fee7b 2204c06074250024f17d8eeec132d6aaa9c34642fdda255ee5408f590065addf c0d169a787a27a415aafb34d7627f2d5b9850513e4659db8097bd9557f2bcda1 a626c05b120c3957d0b520c9c052123ba14e4e1fc58ffd09d26f27aba0bb3909 938624d3e5af7bf0378900d7a96feb80bae0cb0ab9abf700663311cc90efa27a 607a2651b931f4755129c8713e0026b10f7a4ceca9818f2395aadc853509bfa5 cdb61907acfba636bde29a66a704c9ae72aa6dae7e9e967bb3fb0beae44ab35e 3ffe6d107578a5c6f2ccdf8f997833ec3c53ceabd91e5267144d6cb26f98b9ef 50b854bf4621b89f9d9e46f5c479f0cfd2ca60054a404c35bf75f6709dbfeaab 3a658e9336cbc6bee97e4ad6e2c14ef6bd226a0ec26b05bfec28da383b405e06 9758aea626dbd6ead14d158a2286313f62e5ab2c24d529aa92a24207e8576d01 632101c9cfc308bf0971c6494edd72280a1fafba4e7d69bf42ca83b9431907e2 565b2113d2a4763d0b32bb614cda343720ccb3d9bf7f5f3b3112dc39471d72d1 a22231dda98438df8e3d1c2fd99601e0f0949eb05fa73938e44d76f6a7af3ead 04f193a5d643f0c16a04ecf8a34c275ae8c95b23ee29e5e274cc0d9cbaf2081c a0e83150e726d446e5b9d7386bf67689c1dcf59af6da9733f8d28388c62c5dc4 60104f7bed89f67017524447c49bf1eaae2152bd661e55e2cae047a90135a8b3 2357a6ab794e8a297c760b1c4ab1dcaa6f5793e394482829608294db4e51ad22 bf66708fa7076d8a2e6c8ad5d0e6059f2fbd6bb57b2210f01e047032a38ca6ae 5f4a87a346056c8142341436cfc7c458f16a71e8e619e8f94c7f43076d3d36d0 aaca6f01f93b0acdbf49b2da33d67a84e989a9594f0a5cf3590d89bea48d1a70 b1c2134db1a4a0e9062c937321933f7af85a95b87a3dfa118bfd4f6aa532ae43 47344bcdbd76ff72c98e92f058a35356a22553c1d56fbbc9a7749fc17dc37daf b06226b33345a1966e25f90d25da80c47d7c299ac3058c459082ea3c274d9650 6b866284ea6aed8c9edcbffd92066620f1119b473281851d25e1914b6283f1049e7964f5f31f7fe555cfed1bb624f54c60f92f77896b819aaf66d456475e7002c1702fa6dc506588ec3bcc1eff3385e05f5951e04b8231c986d6ff2be69c6f079420e234ae0ed1aa7fb51681862ecc37646ec034a88655459a793a0e560eee0091337e8e71ff1f55f83128961a559d696d55ceb299d3c15c101a9fba93bf2d0f8fe085c0f430dea2472e6926719e1c1a9171f4e7ec88689dc8349532cfc8ce07e6133977b2b35ccc3ba1a5852084392ac48321948d74380517733f382ea8090554883febca4a674959c121c1807cb715954e174d384fbdc14abac51abef3ab0fc064e64101c5f00b29d4dd1e2746b97c55d5b52269406323cf1018dee803440da5e3919922f4e33d0b9e9964d66cc248eb368927c6e11589403d8372b0d7500d847269af0112651933aad52bd1c48d20d0d56bf9b6675136a7afa6a0d9703d054705728b46f2670c2f5633ac7ec186cb31253ffa039215ab12fa07a964b0140f8420c14f894ef07add298123cd21f64d7062fb736d025c02c5126859c58abb0f9705103f1733075e5ad32febb0d8b45f8dbe9e4dfcced544f455c3ecf645960fe95fcc5e7232f9f59f5011c41da6e7b4e1aafc516aec27e5a7ef0a7a5e78d908570289ced132ed775b664a3d4c922a97adbdebb39ddf500885bf3a64d1fd3e00266b2da83d5c2806a48d96b25d4d3dbdc7fe99e5010746a5895ad02aa1a91f0031ee91aca2fac436e6f73219ced63ed84e3a20907c60315b1a4f1ac21dd93c011394b819525b4a86a36751ec5e3cafa60683a397f603f1d9a5a1d0484d48da08a9cfbd3b15170aaadedbdfc9bdaecebdb1ffa8b0726acc731e75084ac130d70c711adea5103290f1002019eb132b2252f33a51fe4d19cdc97d36276cbe6e0106ad78ab553d5f876dda9e062160374ddf9c256fde6111cd998f547d914a301d02ec0b7bb3ad7aa976d7e98748f02b4fd398c543ee27eb2ff7c45bf10535206c030f3b5990d2034b111b3f114482c03de6b429a9f8b75945fdea9ea9a2c5dffd02a1728f33cf9e29b38c2a6bdebba6ecb8a00c667a040b1dfe3ce32a11afa45b08fbb2783cf19d7b9ba311de4fe35ee92a11daac4cc6b0999df70b182473fa840b23d04e3c7efa7f5ab25531cb5980dc3c070c981be8e56ab1072aa261cee6c80232729003b1896858b3f81a1d6f319e4b8351a6b5f2655d34bec7e26885d12f0341fc118fa94d21077801b4678d219907c562e4365f16e30d8e11998b827bc1029c2f54102593661b5cb45e3a06013f060bb0c687608140d8ab72743435060700f0477f66bfb6836c0875a0c81aa0b0f373ba2f2010a61f0ac6c8daa3a78ee307edc788dc149195dfbc4c8ce30ab5bceb3511d4c8472a3328ebeb60243a6df20428a1ff1969e168ca5b2db7ec772b3ce1ede0191232e0357595be1823f881db04603958786fdfde0b0510e6332d4d6fcce8db58b97c4f9aa712a6f07f17bdec01be5b58d179f4db17a996ff9a1961f4887168c88a3e934174281e6859ecbc8a02db30928144a3da0a76e93713b3787192f74073128d8ff89bfe3736f04f4e3e076b19f2b67acdf2bdf43e9be7c31051ac4ed9e18dfcfc48eab4677b9738a3c2047475c510a68870c9ebc899523063ecea991114c3d6a4460a2e4eebb08f0c260931453f5e2ef89786bb57b785389fef74805266289f619bb5135bcfeb985e500a886ab55cd53bd929c6c04cba904d270e1d372fede23ff09c303e955e542dba095f00caf504725c55e36845ce63a0b0eca93bef8aa8514c9c4bfd54f2f47816012aa09b6b8f1ef1ce0709bc1c31a91934ba2f616c4e24b63b78d099211adbff0d75c7d01c8e67659aabe0acc5bcbfb7533c26f88adf43781790db2c07d4ebfd0d90e50aef5b47ee45673334068476885b2ed02daec653738bef6c6079cc89d0019cafcba77b5dd83cdcf23fd87b3f8f5454b06d81d21baf97ae4d969322f3db07954659ad66060d1309f8f395eacef793bb718b417cfb1abada21aa6789d21808a9e4333dc7340d48cc13dbc71edcedec1e28538b09d4d8a1103fe0b592668607942bd54ad2608b3b2554e63241f6c58da2f0dc68fc56c02901126aa98696c403 true +check_ring_signature 38bf470075d2d51e16fc9b54ea46ee3a17d809e814a2d8a5fa29badf20fc107f 04826febd956e191b479b8555bf515943260e96013a413003ebba88c7c7cb1fe 1 b657623c31acd6e261fa968a26740e1caf61db487ec1b53e080499e69c4dc648 55072f08df49940efd81fbab394a35afcb8fb00669027cd6320b177227bdf601f4eefa0299fe28b0b472cf5ea9b0c317aeda829c2d3f11bdfb2c43f4c0785f0a false +check_ring_signature 18402d1771ce4a688ca8270dddc78d1e5f5a6ec7d711ca76fa50fd9af64ae27b e3ba489bce77c787ea576fdadc7f4212b9a8f356ab0ccc578b084d9cabcdd72c 4 5a5c6dc07ec7f5ee4345a65796d2460f470d0946744300506614b3d2a11e01d3 6630635e5174ef4bd21f69b357508085439b2707c376885ab47d7ff4f98f055e 0c9482c9f27ce2a6d4539fd3a634cadfb87d7b7e9d3b1fdb50c89bdc47659883 fd737456e8f993c258b22365c520f902f6af5364edc8352d7bb2b0dc825cabe6 1d31a79e7ba49ad8a7b93462bfa0be8d4df879686912c875d71a2b5333cee00e86406d08518b30f2de244bafcb330b1396a502a5017f5ec605c74b968999185bb7a450db696ef2a833db21a1ff53105d356248730436c77d40a11849833394079f9019f8b671fc510c5cf5f1df11ef5946ba9e34fa1c36d1d7e7071ec110bd0277c9d541932b2246ab78029a578f578319087d168169c5b0adc37a00b50c270fc6b8c9cc61c534c736822b7de933cfe3e71c45b35c24003dcbd66dbe67b0b20446c5fb7944859a90d83be92de69eb78d54a742a6a7e6d98ac5fd412d683b6d1225d0cd5e79433938c9535616f86b6e48b789963e577c7902ce0b3a00b7d9890e false +check_ring_signature d5ef953c6344319969dcb506f2014814901f21d753f9daf4ccc06ce23e68540d f8d8d8177981fbbc68908f211cbc0c1d83a5346d277ef492b40a023f36392088 40 56611153e4d9bf6364c6ef0e7c1114e5d870729a74fe361de6a3950fa56123a7 e8056ff7c0892d8f9ea8a39a927113e48a73adae53a30de179c7c5f655573545 17957c1ef4d9668337e7ca58b2e76544dc9548a4e87136dd7f5fa974c13342b2 4308dfa7e2fae53bbedd978fd60d5cfffff16fc4eb1d8a0052c8f085a3202b43 54343b159313069d4fbaeec8894f314e6fd25dc501beb87e903507b0482e9337 b53c60f59000af6fac40508f6c303b80a45ae6b28bd5c88bf0cd82843f0e4f08 a8c10b7cf89b823912406dadca01ed2690e38df0350636805d7d258023d8e3fb 876bc48e778a793d5fc0f9e3300844f9d21b82b2746cc3ac46523a6bcedd5c59 257758151caac682e65833e369ea2277eff976d37d4fdca0131bc402fa2ba0db 9ea40bd7dbde90e9ba8d513adb10fc980456cffcf7654411f7c3ef66531c88ff 94b3fee97a0dfb4bf5d111fe0858263632b78becf0f7b6ced9f6a12fd0515805 12c518ba717b843f66d95789644f558f03fa2042cd5f5beeb8cf05c70a2d9593 cfb49312a07a3519efba69e5cd4e21d3ffce67db5ef05f2227e7555171dd54cd 3d242c6e4920251f47d76824f6bb6a04acf727cdcc3fbd5229251a8c4916f0b6 b3dd0ac5ac85a4a045573634f86608205c84a65995f8f9e31f5c7e15982a4ae3 f2393b33b5e93d1cb8fa280587caf58d52f0c98c6fa333801f1377ab5122fe27 46f579d83bbc923ebcfc206f669bd144047f2bab4673b36dc5438dec22cbf9ad 05291445c5b291a445fefb3d3f41d593a3b79c56f969a3a74f0d1797688b770b 5f381613218c474bb04091f8e8c2deeafa016d226d5f22a9161631df3005b705 316d51c2c86768bd71587d837ebad8f747285e2267a3023e657249b2e835ac72 93194687e21347dbddbf37b02680553e2e5a301683fa7bbdf30a74959dab42d1 5cfa43fb87cb9c7ee3f381872486fcbfbeb996ce7d2b06dd0417e2e0c413d237 232e171dea3651b0163e3a1a9137d867fb8a7285de3e1f48404f57fbda917cb7 fbff6191db41f934c37f075decf6696a8fe61724e04700c88ccbd2cf830dd903 dee95afda1b1603f25a022310c92570a9b464be4522080564b39cf6297e6be54 043317487f7d5510390b7cc8309a29aa0052316bde1b3856e5f20a2f1e69b177 197359137170c7612c7fc72ea868f0beaa08ad032e0721db7d91ea31a01f21a4 b60aa177a4d60221c747f63f8ae96ed9d6f0d904e8ef9ce2c18d9160529df8c3 7e9084b76062b0aaffaf3073a0237013e5a2bec7b67393e63fbcf89f9977750e 83ebcbfad0a77e37bee4a1951f8e71aa554ee19f8ec132da96d9b5825b64e523 1dd480b188ea6a8bda8b6a830a38f6fc5dd80448136fcdbfe9e70cf2dceb0cb0 75f4e4bde7a0a1aef55bb1e91111b8a354402306e187682082280542470002b3 9459c4d53ba3f1a80a160040bbaed990ad2d3c1b1bda2888a35daa4508207089 ff1d1dd2537b91ef5ca87f3ebac64d8572d02bcc5460de99d088fa4467346ed1 60fd135bbed2192cdb2e41978c4dab7456effce809c14b645057c6a920828bd0 e14055b6f9cb9baa35144fc0adb3d6ac8c7d196526dbefb2d8f64ee6ac049250 6a3c5b99f529893a206f19f289de841b37f8092e102127df8d06556b51a4510d 4dbd2dca649b3770046cea9ec3c19033388642c029f0b0d14d0980ef610c6d7a d956dc57b9e62f5c73da280a3eef5d2613cdde8659365d64e5c04f838bd99f9b a1fccc57c11c33dd71ab62e8a1834d13e026ef9905cff5f94d18f3a7706cdb47 7b06c99734cd0f7cf9288e19fdf69653977dc8747e9565fbaaaab83ffcb9f609a167b13f2be131bdcd1e26f5e66320b1a9574e6dc6553f28b693ce015ee740035cd34dac2e7dcf777107dabcdba68a07eaef6fc78e3c22be7b55a0b26d88c90ee197115c3dc860e4a8f8cc3a934296d734e6af572e8964d91834f6f58d5c3d01eeb085cc7a879f84069faf31b545a9ce9bcbf298d99c440681baef8683111f0049ef2033794e740434c000f9ddad3ecb1be2aecf65213d68cfc03c8c52c01406d34916e05f255988a8f13f8aabba41c5260b07e8f0bbcae2009bd16e4b737809ad98ffe8e48e41423558b3b8c9c741f55af64e3420014a0684ecccb50ab06c0f8dc45a6f93c98e25f9db64f703407bff9722b64e23dd157afd176424232b2e0f3a50605e964bfbfbf8a99b3d21d604ff89c3781d0730082cd71142546623f800a73c1ca832a01f551944eb0e108594734282749be961765bc49e6035b5ba140c0a994dff14dd6191f8b4163d3fe36d404d6dea91356cdde72ac942f7c0247607f250b47e1523738433ab1ec41c7d91301993fcd39e718c4df0075bbaab53a10254837a1c10b36a7131c96fbe9677c8dbf819a85706cb90b61d5375f53ccb2a0ed900b4aeb536b266e2b8ed10ee8396f4ed3df82c4fa0d78f48d7ae84e8f88d04da4f53d540872ab6e7f6c36157059318ed2451da79780538ef8e555369a5040b6a62fe23015ea98ec54ead466ce2bf92ca9e2dba3e4c7f48a77f979a8dcef60714f1d9f88676aca6c6cdf992127e2402e3031ca106597d1243f4e9c8fc6a9e0af5533332174216d98fb0611a7c0c115409ddada5b55180d7fdb27f7b305e1b0834c7841b3f74c24c032d5d313cb30acae9a450fa8b828f1fa03e2e310093b3004482e5a2d0dca13fa77f07f9f242aa53f662007aa0ac01959fe5d397b01d0d0bdf003892490183ceb59019ddd95820c08f1f75793fe8fbfd49b98d3447619c0e53739141362c8e4004a7d666af2a3a2df8cab160b7e3d7309bae60ed96a3820cd5eb5a08a21d9ce595ee88a9b28a4381c3a07bb3c23ec290326a6b2d11df4d063a8552a1b44051e9d465143b927471194b6667217b3c5fc5cb9fda2fcce1c00b9c371a8279d16aaff06b9d6abef43db61e6817c01f73ca8eaa154a95941d07023efc597c3abbb3d4711ed8ecda34866e3fb10e8361e40119c0d2f6ad035dc703cb8d1ba5716817136d4e51745a6ccae02a90a308b38e3110969c882c6d703a0d090fd9596b43f5ad09b36c6b530826f52bef0253747923056bf18403ad7f7b0af2fd8e074102bcdfce90500328a7cbfe6ec2c7537afa699ca85a207babfd2705c6ab2cb3a5ffee7bea5f285b45e00768c57fa038bec95c5fe71dec23a1e38c0a0cedead1b575f9e791e2ab205bdf13e5b18e704b2d12c479e07eb06e7fb0620ec401f1804f1cace66cbf1e7400bd1c34538e20a15060de618db5b791bb777e08e392ef0bedcf457dc83166f2d3f2d2e8f563d10d2052ac1bab28c32b135a270bd28cb684e5360fc88cf8d0bb40e860809e67bfbb7de7687926fd98d7af7edd0961848f987cf65fff7fc3bcee4dc801f11d4bb37a17c2348336d69db35f4639082a068538fce4e8a772003e5fd432900077ac53cc75eece27828e7e5fd6ae910a1a235d593269aebea6b043fcb9666549c4d3d35f1d7a79e99b4684b31d060802737d98b8fe75bfa71a0913b6f04a01ca5f54cfd141ea9abfede65e4002e9170ea536752aa0276f2e98638d7c0e20b88bcf1c6ab545050c05ed8658e84df4d60556e352396ae86b70ba852db766c1e1135895b3b7356ed7ea1c9b38cef8b539010c21fd799c2cc9527baada939e04711636e94025c9f9ff75694bd507f75d03027655f7f896139068a8c5751833f0233afe3a68a32920db473db65c88468cec0545fc048bb51b33c817aa06b7731d6f9127b13a7d1fd1b773029d12cbee6c1e032c53882c131cf250d140a4ed2d1d5be49a1fa2d167b0c927923482aaafbd4b0077b5ba915d9835962c31f65d98ed8f308c6f8fde45a2eb0f269facecb6f98d01db5d8de6adda9bf495d929df8481317223b55c3cf8816e4d5520b2343d688004fe34bc226600ae2d09b4e1bb0860d13ff3468830b8904704055c1baf67c6b30560c4b7128c6c4c033b11f81364b2edbab13829ced4c0bc128b622b7c9ff23009c1a6d1000853f6642563ce1f9bd9be5cf27c3d272c603feca9a7dc893f2e1e019b0d6f3986ee7dcb72062c689c7ccb7619909b1f0e47149d1ef9620589be2a03e1737392c3add97d977848fd2d87dc5064e4b53a963d0fc495b011aeb619dc0e096a5d4ce1adf0c05ee3f8f7559f05c43e7701b42dc9521cb6f925542df0d706459c085f55136d8471920dfb9fc0ce8186da913827a5f95af713abed26fdda049bf3cc4dd5ba0efedd813261ee78c3dd11b6cd7763ae0b57ff007f9d772faf0301c6d24f7e714cac33d3cb586c9f7757632e6e52ee2e5eaf0a64c8e83650900ccc30a96f6423b1892eb126094b1689d65d798d11268536d9a3d824df2cf94607edf50e6b1dd476897e187bc653dda7171e95bb7888eb9ad9377aa0112318ed0c333cf7154d039911e484003d13829aea583ccd5751c065e42c78be0b53bf4b0c50753f84419b38f6b0d025ecf0234456f87a0ac8a247df36a8f60103f9f37d0579a59e3b3b36a37b0a534463f099b09299097e90fd90ac75109b1a523c953e02e00b3cdb31f07643fc32d88fa47510ffe9954f3e975787e94c772742e26e55003fc150d72dc2b19c10bd957ed8229a4327d1bd9165cc862226ee7a1875e6900759496dbfcdb2cb138fa3ebe52c1868c3b820590a38aa29c0f101955ce7d3b1063ff9c74c1e1c120ec7a15fc51af0ac2396458f447089e8c7b7cf17fe7ce0ba0ec71469aa05cda91469f7498b098cc2ba714be743b1d913f7a59e76d26177070d692d4a6415e0490218f5339d8cb961ca0c55a436ae5fe0062489210b5b8ba609741651c5cb5722fbb1c306cfe53e39d786960420d42289c62dc87911ae0cfa0c0811da80b4d504784243517d9fec06aed621e0f7d4969845d50c7fecb9b4050e15c3da1ea1dba6da5edd3ab5cae9632f9e0791487f57e6461fad08c850f3c70e8999af75864b885496c9380bfc67bfd9b2d530e558200b96695c4a47c2f5250bb319d4da3f1531645c989d9fef67264499995624200182fa427d576d5c6b860a951a8cc9fcf2a832c0c10d3ff5c3ef4f56f1ac0a8b62f32d3e115b3203b8ab0ce1e7747dfc1fe4e996c9a7424aaeebfc50bec01797b488672267e069d21e69065653e135409fe663f9139d20781037e1a0f048cb68f65c13a009781751f82f061a1d0c6caee444eec446fc0f868fd65b27582e63938883b5264355b989069a02d7aae2c40fd2889da5124ec336088eaa8f9fa97ddbc6db23ba1d9ebd73220306e161cb3f287adf0ab658acff318fef44693586285136ba90e157ffb41606ef06746c729ac194232d3d170bad84a0b0b647a026876c6804b3d102c1332bdff4029474c7ab29a62bb1c6734b56203886c9c790e3f62967a160ed61ccdd6ae42502 false +check_ring_signature 854d8896cc61e228e2d395b33d106b88ea8f89a186881947ee961960640d2d35 77cc96e1a69f90cebc8bf9ffd7858a9b68cd8879de18d5eb6c42bdab38e58cb8 2 cff2ca57e8a5df72453d5369c48796f319ebde98d065d8fb3bfd61c1d07c368d 875b65072535e383ccc1a8ad27089cdb74ecfaba7e5ce2c1286316f4abf29283 f53b06270af3693c9715da5631b9d9cdeda0cf20665b1553240be9c366d9700ad9deaf1f6f3157e4132f2101d7380f1be4e864e31e9c7f5221c24c7fb8f37502dff7d22473e1c8ec4ccaf56d5965fda0b45f29e141bffe47f77521bb44fd3ed9bbf0d82b0ba54093902e77af9339d865aeb258c8c8becb39ba701d74a1f60108 false +check_ring_signature 3c7435cc634d5de2616bff32a9395e92f2157efb1a8c73acc1a6297724d9f10a 393e5298c828212b4fa9889637f5c205312a5adaaff20fe5c02e53c210fb22ff 1 3d216b85742b8033afadeb440d67ce32224de93fb150e711d3f86075dcefff97 269940f13ee17da51dc55a0e7591128fb264f8df98f2899d88c2d4ff95e55e9050e23d234dbd2090bf3ca1876d69ea4db09c5392ae0964ff9376ae54ec412f87 false +check_ring_signature 6317eb748436429ead60990afef78590426405542a77b3242f6e175cf601f7fd 5fed74ed4cbc86f99e97f9872136aa875d7e97cb37aa41aaaa100e3c67003dba 30 6025b853084d96b2b8271e595c6b4470ba0a760c72ccbdfa0de82f013800a828 cc99011659b6175e5c5a351837f62eff98844ffb73d5e9fae3c60a51feffd330 b4d2445990dac32db342c7583b2e45a61c77bea5c76958ab733f84946c853e84 984e1c6119358599f79a5e326f0d81980778fa86e5355527558bed971e55bf8f 074da3b57f2dc905a031bd588d0cf15ceaadf3605ef2a84c27e83e55bb4c4687 795b9986a412ca986982d001097dbd4ce350aa22257936b1cc35eb46915c488a 2d93861a7d3b9d6924c3dec621c054f085474325a006004e5e4dc8a934f2c0c7 3d2757786de7d54afe37bc6ec83af9f24cd89cb99eeeb4dfa2dae19bad1a13d1 31938c485d14f42ee747db0c6e2b15370a6a0c7ad51882bd0fe4dad5544781f2 c0b7a5d203ad5f028406f93ecf873c7e7abde4461875eabe3e9b7ea1e7330c29 f408dd9b0c86c12459511a8dcfe361fde54572ecd38f49d1f31c3bc3674682ba c783f429c88d915fc7061baabb43a84cd7840751fb3cf3ae0e2db0e9ec056144 00b36f07f616e944bd6d4281be7ecba2f20d5b0c8fb647f5d88210efaba8238b c27a56bfe4bcdd33687fcb6242782eb3b01622cca55efe182b87a27312f06f26 1d62fbbc2580f2dc1686a1bf582e465e119f98a25dc3e51f25ba8604c95d87c1 709ee1b08710d483803f011fba4b6a75ddb55a4f282e9012d6759efbb77da0bb 0749f04516cf245b640c220eaf5e01a21104c7fd02b1881e0bfeb050ba234a37 20d523d7ad07937f9325199f5f3bf47f7ba770b91eede12b52923a11ce6e1668 420b01ef09f62aff38c90e27daa2a9bea62a538749662892911047f9a5ec8240 db4a02dae4ff140dff09755e1fc1e5579c6c75500f320c84937eaf249ee15adc c5b63e2c9f48e1e2f55ac150e42263a7026adc7c10faf9f2334feaed2a54fcde 4f16fa569b7432cbf2401fa97dfef7a28790287e75193cef7798208bee82adcd 6aeb68bb1515f5a93e286e72d1e7afd40fe5809e65eb6146de49252a310a595e 00620e6c5f2aaa23472f9c6da5afc0dc2a3c5c907aee44df1339b37293f12c94 5b78becf61d1fb4047065c70fad54305cc92ec5e5dc87dc2a254b52daeb90bcf e9aae6f5a8842e8c58af451f8130b37307dc1ebbad38d9df467dd6bd6730c42e c3691216664113f8c986f928b7686c1827c72d6d442a983386b3f38e5c737c85 60fbbfa3e574f4cfbee83fc5c951f4040ee70a48e4915deae249cb2937a238c3 bf9c544ba9b276da5e186bf6eeeb767548eca447122ebd5a5ab5ac2bdafb670f d8c35096e7628e219b6c46f2592308b22f9ee5298899acb3fa8382abe4399f4d 1e64fbca5475101ee7abf629d5ec4734f55c150082d4af0874f277527866b70501725667fe61300ed87685516bdfd6642f498fce4fdccf21f5d619a4c478730b379b6e28c96af1b5b51bd44ffeecc9778c9ccdd67717b6e644c73d64de18b70a3c0d09448243b9d251330bab7e3c31a8ad7ae56aeb035257a62afed171ce20031658d4f7cdf8ff328366bebc3b5e97290493877733694605302357cf946450065b59406144ad8e263e56f73f0426bf5e32f42eb4eac00129ea37a02412322f0773fc98cae41e0fe31c30a1addd98fa8d8da6fac8c8e356d44ab0e989c81e8707098be13ee7cd592fb11859d12b651eb3feeaeea42aee4f339d214a220cb4f20649c4c32613c7539d7a62d28d733cd788e0b0a8130003ca4e5c03badc0da0fc0b1f207bab367717d9b1bc29d6332f7686000cfac3ec665a415e9958c110722e03ccdc0ae1f2642aaa0962a293a3dcda284b5e1995942695667a06ee2913a17e08c96f58fdecb5bf815e831afcb458b6d8cbad21ffbec3e105b2ba16541539b5013db44a51584336cf2aa4ba3e239e187e79403b5da6c1233b8f49af9d7c464c05b9cfc9a5c46a89f7a0ee167debe83218d3f212b29bedf6905bcc499dd6edb2077382585adc9cbebcfa91773f4cd9801e22705b545f36edcc2f36aef8263cfb0f3157dd92fd13a2f3b0d1e04617f0364eba5d62b83cc1897683b041e5224f790a8f36b1c055bb1a561c36fc6d7ba45edc75b25a05651a5d4eabd5a0cc1115f50b28a6a7ddf50fdc040aa841d6a796710358294f47cce8a3b3343e270a3f5f7200a730b521977a906509f392ef6dc452c0af2fd3418586551f8329de7be9bc8600040b035eea054688683c5c540d0e9d7849ef5f91e21957a9744f63e46186fd09e284d450a0e66ddcb917adb8ea103a72e8b3992b64146f5b5ef6673976fd42000820b99009c9979f711109f64ad2b84ca3f2e5cb3c90c286b36ccce73659920852f7d2bbf09aee73b788e7f2116ca722d84a6db37a9bbbf8dc255b14b772d20e2cad0a4c4bd7af3f9e4f554ff5f64ced14976898ee50021215880f441238b50c84abb3f7bfc29acaf22a757189ed44f6ae72eccd454e9e3fede813dc9ef5f80b520ed3bebb4c845369817f23540292c560477c8690679bd3b60f78a05e26e50425424be3cfe4efde7ebaf65bcae1b4fa13e3a51a3cc512161e6424f1cb661c0edd468db7f92599619f1a158af2cab45ef3751076dad67e2297ad82659a6ef104849d70d2e70cf7f0477f7eb95f5d52b69e6a7e24000ad25f630724b5c590e909cf9fe698a6d383ec6a0560b0d72ac19025f4f62a5bc147873b5fa815c1500105b5b29b52471105e20c2747769196dddaf0c65d60f40db7841101d124c0d8b406f5b3fb6ad49dfd37f35cabb72a8963aad1c8773fc876696859e381a34957f10341721c828942d86cb9875cd9fcd21d2acf2b71d11664e2170e79f5b61db8490013739f2ab5a775fb3302a99810ca626bd33165921a240093cf1953d94855e7093b3f88f504438bd3e35c1a1f7c99bb00e3031290a12d53fad340b8ffe8003c071c63319e2d047f607d7613b86b7675f0cda29daf25eb11c6da681248d97b5b0e721f68f645bb752d4dd2d90e7c48c08f2bef9d4f8d097ecac54746c823023a018334bdb4a762e00613b1caa035ae797c7e09b627ebec96c36a1389b5450bf509e940cb928532b7cb58a9a6882d1c427b748ef9f6a942e360102a8d5db4178e028e2a17fbe2fbde69c78b2e74c8125a42b26137ff20cd1a113f48a02b9085610797949293a2d4535ce2728afcae7563a18e57ebd08acb9c18152df22f7a191b0e71e6db3e85f0627da2378e0b1e6425c279cf0d57ea60634965dc923aab5d4e0aed2da6c19d8a6564a350ad986f1757826af7091b10d9eedb843bda48d1dca3071cb974e1d40edd85851b42749cb21a8369725ac61bad8ac1de65c7b174bffd0ae239449c851f4a682d0a8e618fe8d8f5d39a5ca618110d7611342155e50a2906abd29839b19cf02820160122759f69e4a34ea5f982bdf8512c5cf3952e8c60004bffdd9980f13bba50232fce3372eee728d26cca57b020bc72d6285dba28aa0d14d0eedff3976a4d5a88ff050751b3a69d196aa87b0843dcc9fa97e6546a86026134506df426535b4dccc365633997c95e0bf029ebd8bf2d56a9ee96457a2c022f438812230708c6803399472ad8cf1e83075da5067063bbe205df82eb624a0bc8ecd92b1f96ac9c1d1c6eb2905723faa559358f98a86ccad158e9f3d030f607ec5807f8f6f8ccec2e1064699209083b9f86b06131e97111eafc0c703dd24b0ea754e19688915ea350c644f6af04bd2b55e8123ff5ad3f8bb726874c2144e0093834ce83a0c1bf3a2a0f5bd878b0196c6692a2562175ccebab74cdbc9b735b04d381cdbbc2f93829486b5fd5fcd18ed48d94a7c4d4d6695db1ddfaaebb3d2b0e61a4cdc41bc46870868d03e5bceb8b9e658ca48836882a098f2bda50a788fb0eefe5dcedae5ae6fc1bebad827a07cb9339543570f61c5383cc01d52e9ff6bf0dd6c010e7f44b2a055c36c54356126039c9b69fe25ca1600fd6f8a580bb7ba70f02de9fb323e7aa7a48c30cccfccb6df1640aed2e09e3a65fa14cb74d248c200bb2985456cc2f0d1f16c1d6cb08b01b1cb2c71c7f1e9eb26c1cef04d91bce7104 true +check_ring_signature 7c84e4b5cdb118c9c9f3268479c19a90e9a2e4523b9c909888a64df0ce0ea52f f9e0bf9d25e3103096b3e6ba6c4131bee5a3f6eb0fd75263be886113ebb9265d 1 5151f7283a163ef3aaf2a4aa03daaec6d69f1afd1e5ce42d7490833766f65c90 466f9a22669873bb19b5ddbeec1117eb898bd9ee2fe3d96b2ecbf6a0a6db5c04aad7984f1800ccc9e062e209c20ed61f26de01dda1f1e451494dddcf46acbe0e false +check_ring_signature 6371c0693e75e57781e9ab76345fa549ec30ccf277feec630e7d38ebdae4d289 38769a98519d44736e9fda288ce05799b19f83b938d2abc58ba22e66d42b8ff9 1 b025866663cef9329d42bb4d34dce82296bdc5ab66fcd5811135f5cb5088c167 55bbb7491a542f7659a39e3aa1ac591acb5fabf2003465bd4578d772e6aa3006da6665adcb035ac1a53e1ff289873453cc535380afb97b3867d829465f0e1307 false +check_ring_signature 7d4b05c9900835d8840bae97bf2dba69c29d7f4ff8df1670b2aca1958197a702 7445eb2beef85d0a99317cba74dd21f8048b4e71e9559b26fc5bbc5e871e5c37 1 9b7ed433a9233d720463752c86a0c688ef1f4e15b153a8a33ca2933b2c210865 caf91949d8b9ce2271b25110617996df006719697e36229cee4a50e1dee14a001b766cdad97ddecf98c64c46cebed20960a006f77eb35142c25493e1a3fde30d true +check_ring_signature a8743cf05ef70a76d14af5cbafe709cf2bd16e44bed422cbd28b586839ad72a4 d49c3abdf6f5eff2b0c1cc678fe0d5005f19dab480d78d8a823ced01a5a6e393 31 9d69c1aa77ff8477575bca1df15f0302a076f961b3c0688ca0bbcf9f832fd21e 4d03b9086b98df857f5560a9481acc38fea84263182c21931e4db293941dd669 af82a0f831748a74c69e64db4db8849484e81ccab4151110d4c05e826f543396 35b1f3e3311ae8aea1c3f127c19bd2e54a46c2abc18e014f7eff6c1e2beacbdf 7261b6c9d89d6ed0ffbc9b5f6b5b3119291e23b8fc63d64ffc4778032c169354 65a431e7fcade51a4c49bad750124b0aa9dc9515b750f49f723c31f9c93cc1ad 40f175e5ad35f3b9ccb7bf851e6a42e8ea1919a8806b936463d6d796b29a0a44 92c68f30fa80d3ac102cabf805e5e70bab511ebbe08e59a321dee9c0f049f0b9 fb73ca367073e3b604c391e5f5be20be69a17b939abddfc31cbacec6b20a8efe 305fa303e4a6c5e077f502a160d6a9fcd2fa26f416572f973488ba71f6c2c3b9 994cf13e01acfa5997fb8c36a06867ab89720809e3adf6971f1be14e6cd43e8f e08b35cc520e3db5160d7c0238acb73a3f980e82b0d26adf4d8b97be5b893017 d87dd7a12b3abb9943f69976197907ccc4da513d7411062e7b3132087f699c0a 1bb9a010bd4defe398ae8b29d9b404f244ce4f81962d4e0979003c0e8a670dc4 98086c572da951503e09b563611d1e933623f7d94eff3e275455c5244dc59189 a3bfe9c636959b8660043c497d7451dfbd2b9fe5cb1d65b09a2531d94aed6eb8 b0198d13f2e785abae04d3e5eff44da3ab9e45453ed394d7c98d03341a00e359 9eb50828d836d45cf5a4edbaf2d11782be8b68b6cb0c5289462286c9e5a8c886 61cc882eacc3fd7e5d77b6c59830f50663b575b292977af19ab5e40fddbd5b20 fb176e6c72a2f7954dd248877be26fa603a4797d6139d2fc808151c34026a5e7 795cf85343a45bab68e4755e6ea08389d83dbdb1efd34e311ca391c30c170af6 97a0a691c0ddf3a79e17354994fe92818f1101ddec88ea2dddd8b07ff32fd8ea cbacc0d708a5a514dc965aa6e5c9a63061cbada15a2e225439220080d44674e0 cd218a764333cb829f133e840ce9c99012057851ebbbb9b52ef2c25bdec108d5 71c3df1c8e6a6431bcf68d1a29748e68c754bba2675d82d2a19958c100c80953 a1b692c65ab8c04402c4e444b35d9ac7bff0ea458ac1395efebe06ca40be4274 b4c89340cb14675e28f53ed65cd1ad83ea8831f67d7174f96dc8ff4f65ef0391 b5ff2fba21376655666f9bf15290ac9c1af9ca7110707e48c77b38ec65ea93ee bd5bd17323a70961001b968d7abb3be1939354d54f357c36f86acd0ac0a14da3 9443a266ac41798dd50d00d574dece42c02c58e8cec1266bb868acb562aa786d 18328f00c40fa2850757b0e636e5ad8ba5be309568d143a7f519f7c94b378a45 e8f7d97803b3e4e4ed26ccd7c7d53661104760638f5f31061f2cee98eb32fa03e5448cd8a1897d5b2658d920584beaf01ca7640576eabd5a048ef6daf0d5d60289b4e9a3f118f5cec14ba3b857253fc9b5d5d4bb6e97f332735ec418b7acf604ceb17eadd0fa94685546bcae93bef77b93f71763758e02d92420b4635f2ab9004e6a9973aacfd0e70f720145dbab7524097e966c8318e1282e2f944eefeb2d06517166daa7a2f64bf19975c8e2ca613eba2b9eb4be4fc3e613b46ebd4369a0072f6b2dd7eeecff4ef816148d73f0969b6b2dea6ec865bc7fb34e08ec847f7909c1ad132b2049c70eacb2cbb508c775c5c4d136289f4fcc706bcd749124ec090d5c31b66fcf6f7809f28022bd26a98d107a66d769d2e240886e3d5059ab044902af097f28357c8f2c63d9fb4664d5f6a034e873fa098b44eb15d494e89bf1c70c34c5967b064dce74e02893268aa27dc4eff4bcc4322da9b1facf288c0cfa6306cd91410ccc20af30328ec7128b388448bf65ddff6ed5985e770b253377d5030b8cc4bc42f671777c32529f1bfd23cae38daea11a17238ea10acbd9dcad5a0a03ba5480d4bb0c0e3b26da72de7e03f64c1ebc225cac2cd1e8c5d4b4b569f5a80ac793a090c5b40c7b34b94984863d97fbefcd096967ed8a59665b18afb2b1cd01fe6b3ecd530852b25a5cedc70da5ba45c54d20eb85b04d22bfcaadde30eb2b045c0d896a4de608b0f4538e9f5e9c6f29e2763b51378d3bb3167b39ccb917af0708ab4ce8f599c876d2421b4b779692e4379ee0bbd2e11cd61382a67a15c49f05d32777b84575e0d9e206a8468411404764a1dbdc05bfa46ebf3e0d4b46d4c10322c491aca6b7a4dad2917fbf25fe8c846961f6df9b4adee3621e133e91fe0b0ef0ccdea9e57aaf1f3cd121c53f319eb028bc8160252a3e2346d7fa08044805080255df6de4a525f62c84d23cfeb4d08a69881d6a7df4d275e505c244c20881083e4e4583f90acf51fd00c4648bcfe1220af085d8fbcf1db46eb8b7640b95440bcdd7bc99d84bbb4fe56c267c99f2b463e2403af10255786f321205ea38b0940e2c4133ab101843c6672079f38b8bce55039c42a83a8b11f126af5637d04ce40eec1bdbda795a206c6e24db4413aa7ebe4b76b06238a0aadc925c0ffb8ba57e0efb57b681ef619a3ad0dc53e5cd43807e05cf57f596647939dba685872c66680fb33924584fc37f4990be59fd17b26547a644d501510380abd109b6d1758d430a40cd91c67177b71482e083d8fb993477594580b8f7f86a1b89fa09eb8fed4f0950a838d953a3f980380db447a4fa78892aeb7bbaf46082302244d76fbd654906addd36d9c4fdadb034ad6d922e41431b63e8d11763d5e533946662374485540942fb88bfcdc6fbc8147704537ce2136ee5226f850a62257f397aa7e309834a0463b6d6289984bd082920f8113e3776c48351390f310f24b682375f1d987b2e0e677dbc5ab708d569517a2096d96254113ecced57406f6540072d9765a315820e30b259205b20aff6ab7dee35dca3a671bbd6214d470fd3fbfa2d79ea0798dc0cc67d000051952260805fcef7f7707941c5ab056ab8d478ef550909dd5e9be207b9c85faaa8238fb2dd2ed100c60f2c575cb62e1fbdbc13ea95ac29dad2786b0a1d19d3c3cbf171a7bedde2b7ee116dde4451ea3c49f9a51ef11b7a44bb23e40855f8be7755fb4fff76864fc6dfc4ef5f8fd2ede10d3f243ab524747f0ce0e90361086b7f2c602928c7ac48ce169a2af7f099164da9bb012cb78efc96b1619f0150e88286d2c4c52fec8810e7f71dd6e244f05cf789636ebdbc901c16e9c2a204ba785b60b2aaf63b83dbd5c9a5610e52e60e938441476918893b88faaa246a02ad6f2d2806baaa5255dddf256993f613e8ea2a775ed645ffe1a17416edb32a04474f3f827a709090df0bd8d7f7371e7ea6147b5af6b0f81561bc288658c9ca0d85bc64a9690729d8e9f2f9e8956e2a8251c77ed69f29acf6fb2fbcd715511e042f3384c4da38d0cec211dc01b9d5498902a3836c2071b67755cd8622fcebc90031f3393ba91d71f19b7d310a0a1c9170ce3825d35a8df7d486fe5a6ceba8730a7963606187f17e179a6c381a6ff58e33b301339cc80bfd727445d081d8f228071fd1d4f37290a57d51ac3bb653b30080190dd3f562030ddd186ebc23711ff20142bdb1ab994ff0391f8876e1eb3b5d651a3b17440f5873009ad14d61b079310ffcdb1a507a57bc4eb2f08de471ff8fdd24ba41f231e219f6e61e52e8e078fa0bc301fee0fb7bfb9c438b6a0b5a4233b27bb18b799bd47529bc7d2516995a7a0dd875e601d039fe2aba85a80448612e1d2793bcf6348b2c4114765803ec7781048810568841d171955d18284059ff7657d04cadfd85945015680745cb4c07230eca802f7dbfc2dff519136784fc07871ac4a651857bb3c7553937162b6ca03809a73126a51e10fe77e5c48f54bb66d8f00e7ac8d7570f8e71595f0babef66460549d26cd9703b505716b4bca43a95ad7bec2ddac6a9cb9b9d899292c5aec12904a71d473df422f3759903a93409157a49b916cd86afa7c27488ac0c99dc94850b0aacd6b2214da83290e6aba4f00bb928d97c81c46ad74895c0de4b80a91d330eddefc8663b1a5c8ed451ad2bd06786707adcf994b8654b378aa1ac95200e270d304745eeb1cee737446284804eb6ef9d1aaab98f595243cc142483254653630b189637fa5eb9d450846671c9a12dd554fee094948e60fdc0f5aa28445eb6d10d false +check_ring_signature 2305d0dd49cb68dec7f941e5d9a4799b7dbc9701b87319ef207bcb229ab9eea3 6a48fa29124d06c4e13d2704d01f97ff4e13e8dcc2f6d2a8214d1e0d0271e97a 10 20b1369dea6ba205f694bed41c79757231f9d106b91b913fe94aabcc285b513d 3884c1bc1e7702242db2921dde6b387fe62c3863fed2b6a346cdbd44ed846d80 229b365f72189e0e5f7d8598f77045f7b6dcd2ae737b452f05ca539c6ab1b209 bffc8676333d72ac82e8b54faa319c3abc31216f78fc24a937f83975f53a5f38 e9348edf3a551dec27161cb638d33daa199908140072cc6bce225f09ba10a5b8 17208dbb3897879a7ea53ec1763dd713dbef22c662f21def0454178a60078710 a6b0c80d47997936440b7ad2801d6b3ffe82c9bdcfc4b8117b0f27563544dce8 383dd58352b73e3697c0922c168685de42100925e75fb0a16558644ec8e50ffb a6c2512d3d8b50868d1fa4713f7b104395082ba5d436e48667bac694810f328f 834dce6ef2a12a961424c224dbf355725079557000e621568950452654649563 4b347f8cd86605b857577f961fdd5a211468f00c1ed3911d8a9e937fe6f3e80de7f6a72529cdb381fea87c112cbafd0b8d0dcf7ce559633baee0fcc1c650bc0ab7da6497caf0e579a8df642b03c8cd55959843ea508b136046422a827c4433035b797d8add1b0794b03ff297028bf8702299ee1f015711a157c37b02455dec07a2adb35c96f359dc4edb1919a48dd53f916c529442ea805fa30ca1b958ff750e2b4ec53a3fd20c4f820391c387c3e7f3be327367ef1dd6193370af83614995034d0d50a22b07f19c1bda884fa13929b18365d1827b3a760699acc47990e6f50f133021977568ce6a82dfc80c9dba9b395a3294711f9cb79fee8f198f85519e039de52d6b2679902501aad292a89e267926b544ee43f6bf67b96eeb7211dbb80170a8e375e558d7b7f7df19671e778ec4803b91f0dffc01233a4add972c123e06fe41e19ec22157b03bcf12625035068fff7dd9baaf0ead78608c26927b1897016c7d9c768cdc62a742e88bccd751f2eb32143cc775a8b21892c0a9426e3bb001567aa3dd65dbe9e87fd082ddd72448e0825bd024991d2366a2279e08184fa20284027b1dc6b6a973dc0a26f5442393d92de5afe7a543301ad7814efec164690918818553eb781fede7279682feba1fc39aad4fa0088781720941f4231751020041d3413bb45c0aa835f1baf396c6341afa57653b29a628e85900a4e09e822e0be2b401af27be19bf712ccd1216a3c8d9077fe00779996ab03060c60105d2790219c9b4d3713ba315fe385e69c13ee8c0cb909d4d4553ef39b837613e3528cb011134acb86bc812bd8b623844d1a0f136cf8eb1caf2bf9c37d5c37999f5ac350eff40fe2d19086c079c5f6387381c26bb53f0664cdc647b8eb79d63aa2800750d true +check_ring_signature 2545340d33177e10902d0eba64399c61f06070362241c63ea323aaf19bae1286 915b7f70ce3912261aaf1151b9a0708346729c7720871c5882c474793804260d 16 eee37023f216e0ddd335affee8f7e5573e8dc696048986881e565bd586019fe5 317cac59d9b1a329413ee9eb0750efc58fd880b0425823f96adfb7c21a312588 63040756f339c22bfdfb3b3da1880cebf6a30aac5fcb4fcd95427a2e53eefa03 fe0462027eb86701ea1f941f48fb3ec21506793f7eaef2b2b07bd8f3841a7f74 18f968a1056c83fe7e67368b5bb9b76fae8703d6c747e9c0b2123888688d4fd7 b0f7477a8880368271bbe8e8a26730272d409c787ee0a0a370aa8cca1a4d0822 102fb72266de67eb771a4bd3744992a86db1dcd48cf71dcf5f152665d976f872 035f79e7b74eb2cc5511e6f711adfa89ad9b1701cf378d3f5ab61e8f098729ea 7dff01f30428d8b6725d713bead2d2379820125e571b4c856389f70e60781ebd d2a60d4561926df13cfb180be874482f7bf11bfadc55f5bdce4eb5ec4a7f85b8 75c5aab4fa0624ebb90f2bc708c08667599cfddd6b38538cb0327d4085c7d633 ec1d3e6bcda7015a9843467310ef624c8053e43c1cda6ebde7081484668de2a1 84cb586a5d28488adf9339ae79ef3a777589e5fc2a712ff53ae4967eab07a277 da9d43629c95e0eb4a6b5aa47b7eb8f39963a5186a769477a64673d8be61e60a a1c27e6489f566e3534ee72cd088fcdf51ae14429fd2a3b8728205edef251238 130f7a3345c96747073eb7f8324c9ed19cfe159d6508738e37c3b67b630b1e8a 250c7e721bb3475220823bc47f06c6af7f06aa22b4920ccf2796e96d7f17d20a2a5ee12e5cb7899642c86e9fd0f5f2c533766b58f31d82c537ed878108117307ff221614e31d3f175d1d4032e839525b57e46ab91596679b37c9c48f0f2fff03768f15bf95b5e6f98eebf1a3c4cf62758df3d649ea7dc15f8d6e41fc6adb2d04431bb31a0344589b7cbce18769f157b05ec1f9a8846582fa017ea923528fc501cc181ad3e5894d410d18345247ff4b8a36b0090b011202576426b143a3806900ab29ff11874e056fe6ce012fd4940db1db8df87b588505795cfb52699b20a40234952fd05b68c46ac4e8cf49d441a31bb2e3abcb0e7559ac5373f47cad6b420c05082608bd33e9d6cf08f8edb01cc5ab77ef4e43c341d9239af8f87709748b0f745466d5bda97db80dff3b67cedf5cf43d7e98baffaaf1e21cfa4248cf9cb90eb314ce61467b470a62c0478dd4e9523be268edc41fc1b6b34b418f90c27b8c0df09e665da11e4102a6a9d6652b0679f9e776b07600ef1fc0b4805bb5e01d3b0d590f4cc041f6f5415cb745d57c62b0d14beaef5a5a04ce1182e7cab41c6900025caa75afa1ddea2d270463dedf308f41181ea367b663f52234767364991741041b7627fefcc04953c43e3276232bd18b04a7974ab5a46ac190f26e5dcde0960ab32af57c59222865358de525920628e6284fa78312cbb80a808d8b23a460f60016a2dd95ce1651a485740dfa4b8ef6673a6de80680882552f99342e00f3e0604657cc35bbf907cec116b4184376d342d8fdce322812f8ac265e8ebb61256f00065d89ba02549dcdbc59de0173fc71d98ed6b207dde6d784d20db70a13314f9011ee1c74d8d8bc6aace45216d86b831c2ac0b8c8d435d70cab98e6b239a147b04f1492ea2b19c6b870def07240a3cd71815ad9de365c35da64fe2d404f6969b0c30cc123392435a6ac1849622201dd54e6a552d7fa64894526c30c2321d24960d076f6c0253e36b70e5df0c5f4e26cbc085130666bbfcc1e067cb0cf8f18e260944718cde24cc2db69fa6d55cca1511b52c21c90ba91834930d4691294630350bfc2b4613416229520f37babf1289d5d79ad752d59d99203a987b861bd1a5e10943b7837df6f6dba258385591ea9688b80e3eafb901b70e6bccd52d79c0b30807af4e3972866ef4f883b7f7478a640887cc36e4bd64881f0d1b0ec747d3d3450aa082732fdfbd5c6a138de0f02d7f16122630402fefbf818508b191f739a241033c0f9747431bbe1526d03e0396ace0bd5194862b9cc75b8299179215908a8000070827a9348643cec1009e2ab91f26cf256bff5144522981ea5845e375dcb20d56541cc46f93172ffc3d09b7f9f52c64e0566ccbd43d4a4f7d46a31e81d568e93de48114807b198048a5d54388ba3f7b1df93ea750fe06677aad83c597af280b false +check_ring_signature b303393b5a3a402fddbd0783512a770d32a82de41d9f5eb42bffe4038ec921dd fb16787f11dd07724a014879e881728ed202edb32044d8e85ff13e0f2233848e 6 97d7a7bf1cc644b4ac7152cc37f3f04f086fdc83c59ecbb4a559334fcf0413c6 496ad8880639d0d5285e1201dfb42fdbf37902917c43c5c3442ed6708a0859f7 2bc7647b4d092ebeb560af6ada29167f2c62cb593924115eade8739314307593 45210f96925c3703512a9abee119cbfc9fbdf0762fd4c18316b38d4b22b2dbe2 6c91d4d9dfaaeb24748b3f7e989618a59e9ff9f1c74594f0d289566e6f7df7f7 95cc25e96ba0778eb98cf0023a153ed62ec4450cb057020998fb402b88bb2c3a 312a63f71f0a597f84eb7ede036309014a94a88320b52ffd29ee9c258be5500ff380e8282dfc3d63e31feecc329c7dd7f8d833d87949cceaa67def19d25de209155e9087476fd09f429c51ee1bef5566472543440058718b6a76a726639f4d08c8fdc981483d5b7361ebee707ad51fdf4f5c9401ad522d00ca4837bb8e78520ef52322d211cd97b3310b989249e15f8f7f6f28eecd7626fc1c42b14265a8c10ee71d06a6d68066f5f5d7fe1bcbda14d6820b1b30f34d2b0cd49c1df853216c04d80b2fb4d8947d88bcff4fa3c81d87f3b11e59efc58d47b7b9893c93710b5f059be81dac908b42b64289b7c7da07fa8c7a21b3c36f960cbe63af0ab3e99cf004d3065f580c0c268ae5a4c6ba801146661d586acde13a8354d838e8c2b4e6ff0856995e7cda0dfc10803e1f746f4c8094b9e86a8865fab721a9b69ed24a42cd0cfaf17d89456b928df695fc9190de6f4461afdd42823d7dc6e596447300ba43756ca4f23979b560eca9e9c032859c6cf3017e89e7c2c252d9541f8b0991043a0f false +check_ring_signature 68b4657590f6ae3cbd4f8fcdf9d81c3624d10ca757d900c18ba71d6941df57ca 97d3b43c797ee576b5cadec858c1ee73126a7264c2cc9fd550e2cca2301cd5a4 2 a5ae1422b9cec69a37b52ce581a8b7a9ee5866fc5501233ca19d60c7cf931345 d74af191e63c39f647cd4806dd1dc6f0cd1073f4c2161758d567c33a906edfa0 aa262c115ab8d4017dd4b913ce8d69ce0ae99714ea556ebaecac3db0cd2ec0ad74962336ada74ef05b8844f0e41d8bb1a27f31d7fa691deb1a94746b1ea9ce06675a11c859e47a11e77ca6b49aa7a5d75330d9695eefbd5d2e6b59a412e0490614584acafb254b99e3a53abec1cea542d3678629a231f3137053803a7ab29008 false +check_ring_signature edc3463e50abf5a661b26186cc3c853428e8b137869ae3bc5b2595c43b79c94a 22edfbe7315b7afbf2cc68202a5926d421945bec4391b10c5452a093c398d50d 2 26a5443031d6d73d04dbba1ca11cadf167228f46fd6932a31833b203c82d82f2 314eee3df58870534c5abaf963701feb96a253779bfe92fca636a44989f4ebc5 6b00e0c51f4272c2bf177411956d781949d786dc2b7f9e83e35eea1d3a5596047ab7dc77591de5d08c408f2a098451272b5f8a0f9670898922396e8b55bb2b00a277770262467f8347a17a67b1abd750f3dcfa3fff9d3121648b692aef91eb0f632dca465ff1b1a845b441bbaaea06aabe0af84ad09c49f518d5adfb747b5f03 false +check_ring_signature 135b3b7c7dd574afdada80b862a9bc26043188a8fe3b3bc13605422ef9f82c1b ec3a5482af87bc066a7ef0bb6c8b85d142d504a322ff04a92e17dcaf9c9449e8 94 62b2ea4f87f7c42bb4d6c15c1fc99b5134eba1ab7c17fb8ee85f7469adefec3e c6f54764a293d158fe77a603222a8fb17d54f38682ffa63f3d1535ab080ae21e c16533d414bec2eef39a3c44c6b84f8a9ac258f742b9176f25f32df989a08b51 52454c635efd83a6c5d328a4c315aefb7beb28b5440f054264b2cb3faa59a0ba ecc9e5d39248ae758b7bb7696eb9999ec842a021577a6930f05faafc2391a386 19a6833a8459a6652688e3966f63acb7c893b0ed284c12a9614b3ee219493dfd a656f0012bb9233b1ec246b12df93630d746e5dc47ae74d158cd43957d8ba1e8 afe5efe4717f7d8670e49c7758a4c25c02e79cc6cdf0f1250388ae3a2a7b0fb5 5a38b6cf0700bd3da5d12199e26d14384130da97c7342d0ea2b070549967c589 293d9a2f096af5d7b05122aafe2782256fe2ee2adac8df0c167d14701216585d bc2366173698a3b532ccf53f1e7ea94484f3d6e99eedf7613c8dc4ea174c6b25 711271023cb1ba22664b06bdb1abb1d4e6382deb725c954664a6b7c740adff53 73bb89b291a05a8e54c632066c900eac655fe6e7567488165a2096d46a090d75 5169b7c57e4bad69839e07d91ce4fa6ad642a95d1d7d9d552a914ee197cf3380 4610edd8b5987b64b0404460922941f03b06a6120f671c4cef4f2b0aafc6abfa 2a5341d326fb59483b7249d6f72021ba056cc7969dfb3366d3df11fbddfcef57 4657ae40696f1b710fce994d4a78c7ad5bd3fbaa8390bb19d5ae524a54fea134 a3d4731254a102372362906bbee0d5d6a1b2f3ebc16bf812f26e4362f1a752d1 0d88a3c5a06e0f7775720415d801d99aa8d310ed26d75a8d9ebdb24e844957da f1e96c260a80c465015d0ee5d1988b6ba65829b74ef4d7c3d507ba6fc8cd7a63 ee5110502625c0600ee52a308eb47791f040bb8eb52ab7ee03fb4ebb8472e34f e4b930fd3caa3ce23c33be36aeccd8f5970dea5c5e71eb948441a1115d31576f 69e14a0070051ae027aec629beb9e214132af6d5a55d3cfb9b95706a253f92a1 c1e769ab548e8c9d23eabb53d63de73e17cef85f61e40ab9c8bc450c53dd8861 66dcb485335fe1dd5673994951b09b1487a5b3789892b02361f60e06e9302edb 79e484343ee14a8cbf2defe245ae4fe37bd31529e0f096f5ac78d2b5b2429ede 31bdbf34ca071e9fa870e1f4dc4c83d5fa38cca985cc667d8a965741caf3c881 a5d7725805f946c1ca09f555444966cd0ab719ceffe9d96f3421780159de7478 36081493777e6b6d354b6ae40a93372178d4af2654b48b901d22f3bd0dc496ac a8a9aeeda0beb25d6327a78287e916017202045af20e66fb54ba3ec9131d3dca 1914815ad348a2241cece7d07416580f8b3d6ef0d3b9378efdb181711f6ebec3 42fc19967296d873f5f2cc43dbfc4b93ffa5d16e1c57e1fd4e536e69b503efa9 f15721b8b41f1a7e5ef353ccb9616617652e397d2f737e20d502fa91c1d585ed 6e6b5ef5ecd1bfb5e59648ca25dfb4a490388e369533d5d084a33a15f5792953 6873db9728f7afa498b87c33ee81389ad5344ed8417be0e59c4d2d451b6f0a1d c1a2172940094aae74959fe9e6b15c10fa600807204a3b589a3bc83246a19b68 9f98bd5514c997278d7b25f4dbbd82cd991f9c4b9567b38dd8425c44f66f3d7a 3e853923a86ccb2b50dcd7feb48c3d6eaa7dacec7849637ea5830d6dc556da17 f85c6d89e908e228bdfd3ca272db18f65c38aec0b6fab09b0e0ba841d575cb31 2e2deaa9a1e6485b21c9cb3819cd01d0b7b7e5d28ae989bedf28b371de518aa0 ac3a2fd7547129ff67aeef353579fbaa8545ad3528bced89a8971aa68de1e9b7 14fa7964e17eae373781798a07be915b65bcacd2c21542e10478906f0bd3a49e 596d8b497ea8a862944313da9fa45e200a171f7814685e2cf0792f661e243cc5 5f1b69cbf08fc7d57508025f76fa68f178cdca9fe83ceed93d9d747bb6a8383c fb9c9dab0e3baa534a2aee563759bb2acf47c4cf845de7408665be28d326836d 7af6d70f8e84353960510416078bed23acc1976012815dd02500d78aa79fa7be c741cbfadc38514d05b82e818f961803559aea2f9290eb841f95509497e17bb0 64df765c118b0ab258c8a2424b06065485c9bb3161cafc7084bb4b3fc42f28f1 219d6eaf35523f236943bbdd3188dfccdcc5cfe90435dc798a4f2f79ab2f15bd 02da8afeccd719c55bf6bb71bdc7b7dbaa66d13fe7c1dc61c0befb68acc906cf 361c0d5ac5d2b18ec086928f05993c76141074f6a680c331cf8521761c0a159f c802fab10a8960c1f2f27c326d29390545f95513cd4f68712d7b04278d23b909 b8241cfcd38ec4de5473059eafe99e17ca3c971bed38c61f004658fa4036bce9 abcdda3e193c06d3fb71d8877feb1a00298d8b6715e109aaa638a8577ec69a1b a1588cccd79d26338c482a197eae3c73b64071e5592403467accd6e8bb90fcb2 193b5da2bb1db48f823abaaed6a4c4ea1c97aa7d52eddb1ffc2606725a8fc743 1a53fc8f6b130a59f69c26e1793271a02ca52f07aa250684cd9c8a895e969e97 293c8120828761615d731a41e9d837f456439f9bc4b0baf50cee3ee582043dd0 27602b6dc5cb7819460fab04593b42a149a10d8c829659774d9dbf0ef39fb1eb 61f470e65755d53312a9c73276e47b5cab347db123d82126fa2dd14f85915593 2a4b06939287e5247872fb023882acb001fe94f048bf75f4e81f0bea45bdcae9 25e46489cfe957ce4d178f257f97adf9e53909d644e7c22663888d597c9fefff c5506756d5353e4c9f30aaa1229fe37e933385c6624b2c3814eec609183b05fa 0806236545237c849da4b207f651a9cd522ef4f6de1a3c48cad8a24c1c0ca914 29baee21fa44636a322d67901d00e699ed7500ea7a99a847e2b4cefb4f7a93cf a53e6a294d757caf46e2df0bc74b0114e5a22e220f2cd6151173950ea104d65d 339dc30f85eb445c8f72a3f2f3c46ba162c5861dd3c2e4f605390a83d8c0ac04 33d232200bc96ac858fd1081d15d4e0a103706aa827d3022a23f671784e0172f d3848909e093f45feab8e8aca63fa30c62cb08da457f2b2ea2dc7dc643589d7a 15e47aa27443560718b6d863a192a958e74648b07edcd279f9425fab9ddbade3 9c6ad1a7b2ea68bc6067d3d8d91a3b9316455977279a79b195b359f5fc9b11a7 c03efbcc865bdacdde2154ed9492a215d0142a7d6c891698d8448d3adf79a340 37f4d2f142b7bf478d6a79945f2b86c9cc6a77cbb7d34ac88bb7e27577704e3d 09b04e8a33a1ee5ca8251346e23fa83dee4c892f2c71cb77f754baaead34f74d 3bd9f4f987f48dcfd9febc6d1c83f696059330ec7958838e498d7a7915b246e8 43e8bdf091a498529b327b4c31ea57391d6e3701b46a3edaa1f563b6a9f86359 5cafe46a5ed6c00d21934fe61bde5f394eec81ac53c3ad8c690b60cce2ed95ee 1d44c61c23215cdd75666a84c785272e3191c1a194fefcd4980a453038e7bd80 07101dd2239ff95f5db17febb7a7fda88d2df2d00a4c792fd6014bbe060945a0 96f3877b8673dcf0f4cbd5d5ffdf9bcc1926ea0bd51d79efcc6dd92f753bed82 ac388815335c625df725a43979be028e7156254d74612e31358eb6e6efa19c24 04af508f2deea8bbfd3426fd52ec87dab84f164fe442e2a0d134747bef78d4b0 48b585af7d381e494393ab97f286a9c804d9ca63b12e461b91c301bb1d11b1c5 879db2b3a671041244d3fe2873c1af73aba858f5ce0c54b7742b816b48134c57 4222b18008ebcee2bc0acf301a579a01108d6a77071c4bcee1fa0d138579958a 784161cf35ddd99af2922aa13c15726de081cb2da5431c123c1fabd7c8b7e317 7a9d3d870128d1ac64abe544754422b4bc89048c339610f0a832dbb15d44eda4 728954fcc717b1db0faf35cd0f18582c4ebb03521ea37e1fc8b213b86c6dcfa1 48f96a0c972e775278e23beed7d021279bc88c69298977b3f479b2f49cbf1d12 2543b8a3c3141ed1eead916fb39903da73c69dd3b5458de68010ebd0545294e6 2a8d7408e74ea7bdcf577e0320810c699e2c4b7257b0a4c28a5a362c3b27994f 8fa6d3d90f6ce73f44c9cc508029ee828ffab16b914433f8f9c516d2471345c9 19c2805b3efc13c80b61d530a0a9ab49e14e3c0990a4db83ca3013c499785a8c 708467b21946b5b3e5e135aa1449e9fd11178c874737a9cfd158ec543f32a750 4d6dcd5355117127a255c1bbb34eb6d69f701279aaec1237a771e4a294ef0b03562b96c719df9d225617f60e156f985af630cef02c09c7a3048ea3401f35d90c3df338732c1597a04cc2ea0a501a600607347b26c1cc03874f73b6ce34f679007361eee79455ab958a1c335ef9912ace77ed3bb5fc0db8ccaa9a359b0689c606f08b17831be7e4be97db62f64da2e5cda19fc621fe0109645e7804feab46f500d536fb6a6f1f57d763ab202963179d2c67455aacc0bec87e91338fd680eeee00979100f2d66c7fd61ad269c31acbc4fb27b7892dc07a099c94465e1d726218051cc564e6d0bcc46a22484ab87c9f5bb61417b0feee340d4d7ee88219a2892400713e4e279e41fc6bfe258f8f47720035e3d96321303afeaf48a2621f9bdc030809d97abcd647cf5abfe09591b2991bec16b456b0fd74fd02dbd65710055ae406e4279e14e63189b3c9295fea158a127aac4d5076c101d0287d2d51ced1176a07844460c64200315ce933a45e63577b14c14cec96cd62dbfc2eeaea229c8b200adcf47a4c51cbf29455a90d9bde24073d7904783b895669284c905db20420290d0a274912f43fb4e131931b175f76498e88180fcf4d09513d761d3f9b92de090646840a2dea6ce767c86964d48715e56a33a2e208067617631c1a5be3c812330cd3439d0fa805f665e981edb334de9d613617a27048e735d7db82661d0d29410853fbca58a547ce07100e053eb84465e8defcf7c57068955666d2158ab6a49d0bca2dc84e1026ef4d564d8011878b9e3e84e4b272ddb5fa9d76abe09e55a3aa0e1e51e9bb22128613125389b4a3ed05862e19f78ad38dfec6ec9d2808ba3aae0a767d6d10c0865c067f366939a77b4f8bbf95e45c358b0c79b4715e66ebfdbd063710342f2d060bdc5ec2217e920b43cc7be38212671ec0589a2310a85176050b7f4939f0ef9a5fdfd60650ca9f9d62e10816f0ed8428d8d1a5a3e8c9b789f70060077646eab5592c22453d4fd1302b484018ba9ac128581c15bf5b7effdd340a99bb514df9ea9ac965634bd550e59e254f88b9e043280f871dd251026c387f066fd2491c71e9e21e0ce13c1a58c0e8174b9865801a882c816fc839bb51be180a152fe78cf169e0897db5433bcbd83b527466cb80eb44d5cbd8fe1a79c60f2b0ca06ee68bc186770cede6c6740d6b7d54c33fe687b1c4b30e924b54d9f5384d063a8b212ad529be72e0cef1fbe57b5abf27263efa1e6b12fbfbd71e7ca428db072cda940e06781318b396627440a2650e776aa3d29f04dadc6d75297a0629ca038f456598b354d7bd7d86a12f8287a9678745bb1165a1ea6d80fd41843504a4034064c6e83514970dae803d3b8634adac63aedd62c30114914d2cd7a020c8290b37e8d22c44acef1184ad510c2574a20ef59ce28eb609ef7d8c78f89b42643c049ebf608b44997a6fadac3d07e9b36ee4d77e383598f8db312db5dffaa7376d0a4ada2f81d0d9aaa89293fe4e918d57b27255142ce434b5655df9219e0038230da037f7f776b98f7fc61043bed640d7c009ffe6814a3d038aa3d794859452d905cfa3e993b6d3a4f2589939f2388bb7c1a455f40e49ca5030eef599ad2709200a8df14fa4bc9d99fe9280981ba9f0c8f28354ee9d8cb376046a9f93f32c689403bf5130f285be92bdf6fa033a36e4b92852e5677f22e76d40176751d76cea3308a7068cbcf3573f825a60b36b37385b6ad1ce1230a7b80de3d782bfec9eaf67002c6bc36b53e542eb50ac47961a9b45ee7db12ad36d273bf7aa311bcab384a50ea7f303e0870b5ef3b10ef4e59279e8244a422403e1172cfed8a5c7c7c3e3fe03e05b1ecd39f75e3c48bdf6847366f830a2b79ae460aeafa067b2397af3fe860f8072c6e3f1a27034314f514ba2156540df7c0f49c0b64bf88fa70e242324f3009e434815be8a01cce8a8221670e3af5b276282db8a4a6ebdacd04aa77d9b8a054f7a3b05780c5f63f978f6e0886a58f548f5c17a9bd0baa86fe165adf2bf300ff7f7e2775af81c3a4647475adbc8d50262d97e643cc5074068d1a9ec5f6a090611535454e62a72e9dbe664b024cd1dc80444c4ed3a30612c0f0c8fd644f9160506bd94a8049ce29186e9f801e3dc00c6f032da92281099392b506662a2b0930ed07b9fdd17ec66f1c9c8ed51e6088bfb8e97d1ab42e4de48568d7da1d8af490a1a848f05a54a0eff23e3faed1573c4d4975d68c163ca58245e00e25f532f05035fab857e8e3c65aedd171d51380672f5939a65e26fe3941935f86d7e5f7bfa0928e144081c26d5a78793f1f2a1eb999fa9c379abd160d53e6cfe0c375b38a906fa503c04ad8045e353f95f5c83c97e4fee0434c9d25530ad55bb586fd2844f0522b508d493ed2f680614c23a33b06c49eb99a04fd32d410286f47e385c6665037a963efd08aad73dca922b9f3ccc55711bbaf6d249f9bcc35efb9e064ac0890b22a803ae8a7b1ae61147cd8a727afef56b40dc07e5a38bcdaf5d7adcaa5e9602315b3df48d4307d21b44bccafe5d9070297b61547083bcc71c2be7374db70405fd7da9738e7512de2536236b71e61309bde8eb5cc37bb20d461c63dfae388a0fdbb2439a1629d57480d16c954c515e620ef34ddf765203161a7958d6e48c610f6847eb0478a7cb13efd400a168662de0669208059720b348c7453ef518e8760ad7081914bd948d27188f1efe20cca32f354cd9ac333e6f163ff8d7499c0e6107bf90a0832a535b9d97891e5f9ef23cf166d5ef67fdbb2878260250c6533bf4033a591d4965fac6afd74ca6507ca6a52c73ac97b8e674b94a794fa071f1a2b7097d66f71427b33a0ef011377ccae3b07afff5772b717c8af83ab924093f70980a193afffecd176edbdb26e8b245d2c0fdf7641c1718e4d3619249aa1ca9b5be079b29a20cee5820fe9059f7ae58746da755a4d2ab7b4d94b446a0c650d4d883046b94cf79abdeee6b4fcd5932c05ebe95174074133b055e4b03d37d2e7fad21048865915ab7a49a8427e074abd432d5b09e3c765b2579f4015032592cafd3fd08489a8ca85c0be93e86e7717585592430cdc4cbc0aa13698ba0882ddc1e882a03c089085067b77d969e5295db91c24e4f24ad10071189f8be3174d3361639f703bcc1d0532da633af950a5773dc44c01223eb07aa3bab7f08111c272d589ea507a532571149ff01b4e9eef9be65393c18aa2f72609a99dff0763454be551fd208130b3b79b143c17787facd15c7c67caecc99b99e2a9bf324a2b0dd6b2e699607543fb7e034fb943b5e4af83fe23c15563eb11ef0deeeb79d7f54e36413eade02c905b5aab09e0b569e3f0a0944b8b059a6952ffe9be216a96941604ae9f06d0adfeac54d1cf7449881696ba098abe6935f29fa08a8705f27307e018ba8042b00e25b7fdbfbf0d76f8e42979649210ee24961eb27c0a407719c1da6a91027c60ba77832eae72271c8a90ccac3bfff324d250fc20f21c3227343c797a20977110150ff2529e00ed60fee90125b133564c1977e537609372e3bc16e18b4fb0a4e0cfa59c70d0f0906ae771590d9d36a344529f8d1dc4bfbfd03af0c4f4242f6490fddada568578ba4c0d006338717bec5139c7b37ec4f9962b34cc82e1c9884d10796d8d4bed2378d9fc646a11c38bf6f254fb7d75ada16bc6680fe0e9d746bd106aacd1c430c89aad5cbf134bf8e21f2fe3ea9833783e063ba65d8d5d0d9e2bb05b0017f416bbded96f39b9de905f7c59c62f976649b5e2faf161f6d6817312f04eb7ce578779323457e21401adfda9185d66acf81694e4da535601561a03e5a0fc41ca03c2e4cd8b1ebf437547ea78f944e99ad5fb7860cecc9b08d5da38af7030c8de78343b995f1bedcaf576f63823f8a284396751fb8405f9b0e1c3cf0f10813309d9417e8ea4c5ede02e5c92b80d9e82951a082508d479ec6c6888b69d70dc84a6e2b03782b770d55dc610f8a586e1ec6f470c392685413ba57dd08acfd0f2e4f51e0e8d8ea05c49c3a0bc1d3e5dffeb6bf8d88e4cfbdbbbe4095a4ca2f0a609b5dc4627cdbd2b75564908ad18430e955be7bafc42df715a4dcf1ae102806a2d5c90d936d528355348cc7f9619947bcad732e4b7156912e5d6790dabd15052c75588fc1f84c56ef7b24ac5bb862924f1a8867f0593fff3c6460f2b7755809b3fa9ee6a78d11bbcb69cbeb55a18931bc289f73e2bae66e02707e2046f0ad0e448ad7ed788c2769b11040f4a2075f68cece1fde8385f112a137fc263f523a0c238c9f8c683736cf275dbe0fb0a13c83993ce111f689f8cf753e77fe502f890695cf56422005047f8c2b0413988dfc6ca1a4dde5002479b152249f847f3d2c040003ca68eb8ce257a797a3a39e444fced11753caa828dd5fb4e45ec7aa8ba9022ee206f596723137a2182160805d82dc39dc68c1d949c3590615b759e77948008f8c36c0033211a7676cd006c753f636a29dc246b265e935c47360fd32c64a05395d72e2b4f3595ab4c3ea2e2b1df1edc33189eec2fcdabbe7713a94f571960c11e3c5eb59545cf153e3c92f2a635ad31da7652323881dbe0f0a2580f3393009069a32617202b6fe0942d5a204e9e05ef2023ce7cd579548b173cb8a0789fa0e17572eff7fbf6d752b43c1aec97a9621359910338e03a18c1a13d4a870b7910619cbedd3031e6919560a5bf90afcba25f9c312fc0e0a6a81ec49ef758df79f08dd24813832c86f67e14ff327834181d0819a21b6032fcc7839a34118eb595d0496f9f2e344a834910d1bdccf64b65812627fbf9eee4a66c275834240dc132c0aaefd01d654c694e6c983dc0f9c8e44b40abc6b864e6278aefeb56088446d7c0ee6f672f354f2b77d3d95369c34da6eb10a8803f97f6bcad06a3aba914801e60570bbaa32d42dcd8540a438390c291b40e02ec38de13a2c1603b2cbb81b83260b2e1feb57770c8d3a264ce3b2e585afe67102b511c0dc590e4833b264d5375f0171a3322ce2d61e44d3eff916cfa40ce0c8325446d548a63c5366f1140424e8041087f32d56b8f3818ebed9a9a28b14d42ac7ce928c365f49e93bf22aec13cb068b94d7732967b7a63cd3b068599b278f210ce55eb2f36dd424ddbbeda560610b7f78c6935bdd3f668bdd7724a39e879ed55a89167825fc9150b8aaec46311e0c5e89ee237c06ceac8a615827022c826d7d693802340f65e82b011a0920e4c10c707d1c8bd6d12a54aa5460edfa53fc019bf778d447477f11dd2186dddc3cff06368d81739d9d4b1e2da87e52c9114dc86b3d69c36c259b79993aecd9c6a5b90b92c5e426243813f5773fb0afe726f45c608aba3aaf900b2eaefbd12e5c9cfa0b7aa4969234f9563a6084385f36257db1b87168991e2ff206908fa591fb92e706ce7c5b75ffd9ebb600ddfc4852c57b62e0a51d438aff96f21821e93d792e11034512e719773c4f5dc0b867135e04daee9e46fe8e30956df66aff3c0ee8777e045fccd30989dd5fdcf804a7344af80abf937f9251344ed106d0fe3cba8f0115069a3a3756e9bf9a9f7283334391558b10a6098b9a755a86cafa40b3d3a5086603e2d06b0a34781946d7cbd48861926aa8aae028c0cbe2101d7ec2dc029fe3790854990152d040061ce5e3221db20f10628cd07c08ab596f1ae9cf861d1ae3ba0554a23d3eb2107b4304be7ec4bdc9f65099dcd259756a15522aa8d3427e767106b1ffe44fe7e89cc38ada44277fdf9d8a978460f3480d631ec4beb40d5a4cd407bee2bea17e02d78f7d4bf631ad8f1eccb52723f3dd6f0f0855bec998ea7da00b213fcec723f01714d4900bc67d2820862f1c3fb02c2e940bb260fd11138334063532e9e3926d22dd28fdb575e69149fa97cf31aed077df633e2adf15d90f030f466dc53c57668119849e4d95ec70a632a288b5afc481a0936f93e43a0a3d1b07fd1e470336a1a7aa5c0ea861804ff4b39aad60c3d876e909c1ec54a7d2b55605d4aa5eb3a36f961f976dbd82041fa9a51527a20e14a952d3c2f43df9f9ffa00d4ecba2edee465309b202ff1eb8c274864bb1f7b980381b59a549e6de04c92802a398261a0ee184a64614299130a019944be16b38c831fcb63044c0d148c2ce05fbd8d12b23c2dc8945e6a46effd9c25b2872281a5cbabb4d1ab2e02a4728560a1a4c57cae220286a033c710a8f8c224c6eddc22ec12c682802d72ec3cb910f010b631a0bd4add10901925fadd40ce8937d9e71acd682a090c51a4195df92da08bae42339e0c963a2418650d876a2f4861878b77ad961fc66fac36e096a5d0a059745494cd21a3bef6f9dffcdbffc83660290338b708ca6eb026d4ef3a168a301f2558d7694e7868ee031d3e5eff75068fbfc48deec4828f9422300591e67b20edab5a8654786ead6cc3f90460bd81189a8722eba0060e0e1854f1ec072d35f03f5d29a99811ff0fcab2ff79e04badb6022de56cbfa0bcc4f349e9f413e8a900d94775b6771098c32b0e1a5301d303aacae64636d05135dfca460f8a035a5fb0bf5bc5412e2bd940cfb3d53f7836e349b79d2bf5a7a19fe9a367dd38c29101c093c32a8a087ec4d27f66e1e58e2aa016478099cc02ebaaa08727c56ae5b85d50d05456067f2e9a73eac7b74b46af3e7b621a3d178de97929e93ef78f54a41270222fa6209422677e0059622de39d56def2408eb0f2233b056a4e9f03e3a053a0cb63efcedf6929055d4a2abf927605d95f08994bbf7b9909c40dcd2efc3f33b0572e8b5b05afc6d9f8d7eab6c2df63d5897d5fb3a3af44ccf923b1bf2d3c4df0706b47bf4d3360d3a3d41bc8362a1dd63e26c043b309a448c6566659a792751059666827416efc7d8cc6780d7264be7bae6d955783b4bb2eaa894239ea09f0b07d9fbcf88b87b1ebd8f4ea6d7f5f9e1ea319c17a4b7b677c9cb49766f46e6e703c7f3500053d1a2c1f921d02e2c68760b77686d8ca8a53aede452fc3724d30508fc8ce7d119bd7bf48fd9cd8e4995e26289da51f490dfee9d674dd6934ef4e5013bc0389feb729a4e030f9be125452cd8937d9a2a173b93b6996c561bc5613f0ac3eb16587bb8fa0a79c8452169a94d2f1a531dc04b521307b639c1cf4fc2b40926a0469df27ac91bf2eef5b8c0f34b620b535d8490497139aa505cd78fd9bb0bf7e888ca60e5073cc25b159d41eaa4d8e5e74c8d1b712701b91c67a24cde170c38abb5b4ea4e8c533afe1e513ab525677cda6ab9d9d39bb7a7423aef548bb50f451306f8744b3c46a4e9f93999766af5850740582df1122a52d2b4830ffc5d03be09c003103c4cf6dbfed1fc2785e6fb0d1f134adc0a0c270cf93b33d991540290553eebe9e7b2a831dbc0a7231f7db3f8e85f9163484d228ae3cf8de3613000fdb604e3e96c538938cced2050cff15ae8d4ca794558ef21e3a4170bdec8e005e8e01f186fd7f5f2179c80dcae7d371087cd975d0644f872d9a105194b166a03981601c858c3441316cc62753b2eff1c241a686ce7f32e4a64e8f1c31c72330e4ca85e1e08a935597676dfd7cc0adbd882e78a60821d8005aff0317ed826f3056dfa4cea5f02d9d875cf4c51dae1078aae1be46bf7de46397c9047aa3ffead013fad23442d297e6d623b29958d209352acf386aca8d40ca66f001018a42c010a049d843a0edf78fceab5720ae5f5c9c74cb6ffbdccd4af5aa3f6ebae074e1d0a6edc44ec2145ad1f9bb0e38a591488a24a7363f0f5a04d3db5d47826e150aa0f02de20c137f3be93a8fa2ee64ebf7e4f722b913908f2c5a3fe6a6546097cd90e0c85dd0374c66b6c30d3fc19b3f00a93729a7b716bf84ebfb70816d15a39dc0f40e63ea4c265b5b66072990e3eeb921cd7b20dc46ba2271a6d5ac17b6a6b450fe95c4d2d44ee4290d6e2c59f2df5a7e2ee7d727a8293a13ec187c5f77e9ef90c75659f05de8654ccb6fb53420c4749b3dc8ade52e5e89cc8ecaed40b8b2a73007ab4292ac04ca7968d957974f802367ef3a2c4ae21c7bceb1fcbfffd9e82410767d6019f0cd2d2778db359ac8d74dd950cbedeb7d7ce66af9205bc12ea21b40f8b656693fe93d6147093e668a6918a5e7b468b86c681b50e9529b751f9425f0ba85e3217e4dabf38c56d8d9ad48bfa2b7fd40ee3f16ad6153cdb60e35986570c9c3db17974834f7fbae445fe3d3a06618fb7c43c8f4e165ec340ba0d2cb43f00359ed029cc1075e18cca56916129166e9e988019373b8c1396024477e689130520a7096a0e08c21dc707644ce18ebcf16af6a21beaec9d92a490c397f040150e2861175e5cce9c2d2bead09766812012527fb1e455639bab19c1bed18aa43108c6f2fe4223d9d89048fbca7852af8f993f86381bc97cf2d2af36de703ff3c10c3bbdcc9b86bdc3383d07ede017119a83b69ef4c5b14398a2b440bcca32ac10026907e5739c5778f0a476349cb7459202d6f1c9bd58a23420d0ac5d1601b9c007 false +check_ring_signature 6c762b218869b9a635c399089e7263b740b9034ed92ad738bf4dd070acc56d90 4c278d176485b85e41f1a80dd3538a59bb3e73be32d5f4d40299ba28dbbea156 64 35469167d00856b3ec79a28397cf0fbc628ec807b117147ede40ba0990c0a126 2295fba51b22d143af4e9dae046d76155db103ab8e56c333c4a710d55047b6a9 9430ad05ac7078bd979713388a1263631a2e6b0a554a78cf9a61977c1ede73b0 280daf51fb744e2358540050c21ebc587302d12f8fa47fdbecaf9c0d43fe057b ec2a601a41dff70e4623339108eb6e6a5e5c5b7770146572a5edea6577509724 bebc8336cef88321cd19f254d173e2e58890cf989297a1d8c061f988f2c15482 c5f84d2151c480b51176ddf88403d4a31a80cf077c741c3451f6913d797e2652 62c2991e59a7d1e322d57cf1373b545a5b948a62ba2c56d14186d5fc5cee9325 ba3f3e5075da63136840c76bfe67db67fae1d65688f18ab1fcc48eb45056f7a4 dce0302436665e1946b140a9c29e2c47108348e99727e4fb6122c3121eca50b5 7ff743ce65bf2d88c0d6caeb64cd49e6af37690b0db0f4454a37f1ecf37cb641 a91716b7225f3ce115f313f0b0cc2d7c8cb51efc97c3228be51b127e1cfd2d09 c3b9caae18c4cf7f4842318238c5fa89582ed26a3125f3f590e7954ddd6f2369 0b25fb2807fef265c864d75d2046c12823df9af3bd602dd87d54d88d01b65a14 117bb8404e06d4944e4dda7a0b5e223324ed5d4f7d76a09133ad307a6989b5ad 5a66c9d3ffe0ef71f16507da1baf12365909cbeb539f5319b351999a8482800c 6c8f6a93cccd89eac35d97c902d63194ef83fff5137d26d53673c96e29de30f4 7cac60fbf90bf19d6521aba0f30ee30a9d6d0c681bd74695c9cda144b281e2bf a3adf5280002d1cbed14d2a49ec4655ddd02e1c719cc504c9f305d939f5aa452 8ee573df2881a1600d7e3cb25980871e32d836de74f2c914c8154e90152c7a9c f804695fc325b9906228763cf23ee50b87ac76e576effc588890227f1714f604 5ce68ce690a96666e7df6538816e24adae4acbe9b19b478e4313512c9c1a6414 12b07db7a02f0063ec194df628e15cf940cef1ce35ab6a339cf340614c7fbbc3 2d5963f9cd9756da4e43d5c07cd36d2ceef45c1e6380f7633b03d011a17c52eb b7b55deef3148879dfc6c90be08076fec8a239a22c1da1b2c2c1ae9f2e3df69f 11525f3c21315d5d7443df44105e83f1ec484ebb91fba58adfde4f908a0f1512 42141b9828924fcf2d55b4561ec51a75bdf2712a1ff960dc5fa64396034ce1b2 02b1886481148fdb2be90b85ae6adc8dd3d6de3789dbd8990755a73f6b5554e9 8947e93534d09c934f84f8694eabe4c03e2c69545006874aea12897f7c2b15dc a75c98346e57cfa2b69821970257bcb2ed67b665dd256f32d534ecd8c0876df3 51ca9faa7cc1273de995ea43d4046844443a06bdbaa29fea950e457308c77d2c c3c8da9f1a170a7ffb4ac5d9e6209a096ce46e5b6fce76149661861178c3cd2e cc532f8c3f4554fe2b6d10f80c6a247717e75ce803323772bdf2b9ba7d77ab31 410bcf1490890da494715c8b4507633e9d1da434e9924e6e8718ad78096738fc 4edd0bb4a5103f0597854116327b0a3eea8e328be22f3fa7e9a7519aaf47efd7 82b24afe6d522bb8bb93df05c17f055734196e868048f0f5006c1b031fb9a327 7a0ace7b48d7a4432f4f0c3f32d65875890ba5c95909fbf3dd3526f953429d37 964d2c499e5ad0d04be3e502b56a9161a461616141e9183ddbd76e0230e94f68 85a343b59b2c7d305c5d1bff1527654147ef31961668b9c7835953c5e17095d9 8460231dba0a8dc75178151939307a52b8cf7e9e2216f49e94d067dd40151044 eb68f750b85880c03f2ca7a9c177db3004c9432a021735978633f59becf104d9 9e66a013c1df4c32bae8e3fe65d56de31a58c6fbeb44a86ca41264f4c7d1cbee 23402b57d2c6f91aa31188583528f1555687156301f17bb7f04a46cf22eb22df be92a3db2e67ebda884a880eb3af37ea2ff2ff0f2eced4cce4247385430fc245 281028b5588a40951dfdd075ecbf0414c5cde4db3e34c00ef7f2fdc62adba16d 81087ba2c3bfbf8e051cd8472be175c60686aebe66247bdc0ac70f2c12d9484b db8598c22ae1c0a0f91fbee027a5cf38ed05333a8245e87cf2066fd54339e479 0f4437fb7e4ca05450784b8752f3206e91eb71f5e52164e13badd73b6765df33 48c3d7c2e6e6304c8a22cc3983d437107693617075cf95f98b9c0e72fc2b9446 fb47a4df2ba7bbade52aed27d3162eda97c2aed884c9c128b467e9d3df3016b7 42d0a6190a609a9f32ccfe24ce49147a6993ff290e4439944681bde236cedc8b eb5490cd8b64d4f6f7ed2fdd8d7f00c007db02182488bcbed864218cf8094b63 d5c94409b490b7d21880cb41aed2c9c4f6b39332dee99eab9152e7f1f36883cb 34cb43f316d53ebdf5fe1c98bb043117b55dc2835f781b54a243f9ad1e757a9b d41342c9276cec743234478f7c3555223327ac88abc79e152d75e86b2aac31e0 dc1d91b0917eeffa1204cbe9e10402acd99c0be7a035ca9fdc02f1fa7dad1f76 9f8809417b3fe9f177505adf3eb1fd7ca247fc8e29f5c88c5cb335ab6803cdd6 74f73e46ef1c1a37f61b4c1836cba78e0ad6266ab6d3580b96aeac2676563511 79b663c024870acb3628aaf6fba00ed33a8798ebced71fa5dba2541b98c0960a 5adfc583870b79853e2ebb5e13908e63dbf2636dcfbd25b12df02711060c1445 f772586a6e6e66e64bf0c7763e80f81fb520998121a92083bc445885f4d22424 7fa2513256dee8b105a6fa9de5ad2a60d9c0f128490cbb461175cb1bcbe2f606 4c501703ff62b89150c986136dd25263e32f1c4b7cd341f06aeade331111de1e 29d0ceeb169a472c1b06bcff12970fb7c1a926b2f04729e2c8c59c7985487946 11c637a2a5068fd86671fd4610c0d0f2bba50261241e22c11b95555496700e00e1a0e37464295309afacd980253afbeb08d4dc6f97375e8e9baf7eb29595de065a496ff6ecf2e7014113b01b52b14927679b54757a3b0c5c5d9f7a67269fcf09f9ecc649f006547c1f7234087cc922c8d2facde0908f8ef48abd1009c92d26091c621d3e9a1a69f3f7fc2ce49337ce7aa7ae8decf8ea72070dfd5358d764770fa48a4ded93894d2527d2381f5fb8ae2d3b7489ac77d2732ed7de7767ca88ab0cadc859e985811f5f367dc2b01287dcf889b50686c97d5a5f3f6642d817432b02364c36a5eb1c8dc9728b32a4095b43bf0c76359bec53364ca467e5620332160bd1b038b76d9d218bd629167250834ff2e9d06dcdc5d20732d3301d7494de3405963583da8d79ae51dc4a5a8ed1774149fe00a1f974c8abaf0725cd4ebdf7ae0d56343ab1b05ac9795d9660e2a78fbe342c2985468bead3156b899c4989f354042e0184dc3feece3355478c73b72689f2a3464900bc926069faa7d870342ed40782e9c77d114fa5b2a9b964f3a61a92149cd1721219de3c91c9b626a7b26c1f00aeeb0df93d89b05a18b5de4b2dfb9a657377b1bacd1b1412b3b4dd8b1c9e8b0636d5dd2794d6b75ab1569cdfa188dcbb03210a42b2b78bf4d722c798b9b2310198f1555923cb111a5c037b91a3c4ab529b60efaa7754b69b49cdac5ae39a150ddc6220f8d086719fa38e6f3fe38079a9ca9ec15222f94e7654818d94fa2c100cdfe51ebaaffb90230cb7f3931863c4b50d790d392feb2bb64c94474de8c2930d291239c8c2f7c48d0495fd681405db2e41720bc8a39aae99c3d545685622dc0c894afc8eeaa93ffce4586e09d26f63bf4595c3b9f0797a72cac53358f6c99800e3dcac131f0f8b2e2a6a6ed09d51630cd185ac6d584c3d7d0019b9d0af1a600827d98c4df5a1ecf0dc3446299a125b215a5dd29fcb8827baa200a16cf5818e026deb64439d20f1e44530acb684984d9c812830113278708e5ed81e653d62bf0a61725439a695a05372d9cf8142e435cc75e00d2edc886fbb73efd37909f7b60b409217e1ab5beea34cd79f545e28874fa3df8df13c566bbda7d8643a9563d90ed68535cda50d810ee14bf4b83a264e943e9a60fb127fc9e59bebdc0c95913f0c366eac1f697a42c59ef0f655498813d344e28d8f54c41e86a144e40695d0970c36f4b37e8c730681c2ebce9c3b3e4091856d9dc57053c9106c6bdcd08ade230a2940878076b31190aa5484285695f71718b6ab8d1bccc27e688077f1cf68260adfe0dc66b692b0bb754547e30adcae5723d5810c272fa7545497c8825072db00ef57aea21e16d780b301390abb5dca2547222de54e1bb24657119353b4436b003670a2642195eaf4cba043746e1813443a87373de30301cb2d2af161afdc24060fb911ba07a35cf93b654d44028008d64e4d11ffdf4183584ac2d3abfe52b00a074ec65789cc375c93647eedfa4c5be017661f418852ef9b5d4d89dc06c5ce08037a778b84ce1a1e3f912c0115ebeba95f18208abc1d9669765f4d07728e0d0bd5c73986123a0efe1c48e48db10ca3234ea57abb8bb52bae48f48b9d0e458c02bfd0b32c7a6285cc8d2d8b5eb59d014ddce6ddbf413277b84acb3998629aa90d08e916d7f842506af1e177b14a734de7bc53a9f08eaee4ddf2fa4af43293210036a8811b348b09fc1763df7888abb2a1b9e33cdf41c2b5c616c7ea9bf6a2f00153fd75fb5585bf6281e9067987661a1cfd8ee196f33433dba275da8f7d4ce60d60c5dcf614dfea050eacce5ee655c067fed5c2ce0f9e65c7090f48c36d3c6004cd1ce6790432c6354abc48abdc63cb68ef4c7d059d574ed9b5382405fb735505474858da94e9564c03bc87fc7c19a3cbad7b84b8e2b74164be72331b2e584f027de67552d4f4a9f528cd17b7decd7060af7b3652f8496fcc061ee4d809a832018efa3bbc1ffedc98611e99e8ac71147e5ae4db47c9fc2868ac28163551dbf502405470dfc880f8e73a2469e624911c37255ee33c130c68b01ca548f7014627094e3146d348c124a4f87426147ec173380678bea50d10d34d3477b94d71340e04c329a37ba5485e314d4a6dc944d98dce33001c23ddd7ac1caed5a2db00076908b7c1221ae33137bfc803b330c071ea8b99700e413501831cf2095d031c1fa40af24e1318997ab6b49550e2283e4c3253d78a54460271abd9d0a8c5abfd159709e81ae5c7711a4ffe6a17d0294b0f96cb4f2487c5a5fae6f22976dfdb62ef890fb8d9c94ac73ec1518968f181569230625a5346f1d2329b2215eb3aa24436ec07a97ebf2d5e903f089e6d8a336dcac64f0d7c35155179016f88dfdd30e2069b023ae6e2fa3a11f922d39ef0f048a0d9ffb2694689adc36f208a6314bc6febc20852412ca629f0c5405726e1b61c05de914479e8cdb4542d1b53b338d19de205088583ad0e5b9f4c8a8c0171577413b9b655e34d8140c349a2e0bd05dcfcf1120362b7147a138a5228406187fcd0bdfeb579bca829fea74e76fd9339174f28a90cec0c3a718c15890af8fc66b0ae028441fd5e21567c24e6510371616cbb84a70cd2727da6b9e930c262c5c0f511746e7193080ba34421e941af640bea5f56070f47b272fa508d228a20d20de7ccce3f9b64dd68fac18ad3d43ca6bd62c1f4130bd4688aafb3c9e4bec1b22fdf0c2fc1bf739572169ca85573540048afb91fe9005e117ec3f0da55eb57598b00740165fda6381e28e351c67a4071a9c932ab7c0de8e2965279295ecb7bab089af9535370b1490ab24651692f7c54dbd801d7c80987df9a3c8ae998a6f265eb3f0e5ece9ac2d4ab11b481bd63f1956c5376d6160428fb465f8616641f4a6f44511ad81b5e816e0f144e8be2f0f4e14862cea0d40a48614de1a62c51303c0caea387c417803a4eaf1c6ffbc2d9067b0d6c02d35e031038f2bb2ece5d385f2ff8c47234eb9db87754ccb777e43c4533572794789b0111d31a66d0475933be64e760292a6e4c3355381ccdc5c93887bb7ebb171e4e0b2dc31e87bace7c861843668f32e1b224d6528253057750fce27c3d7291d5d20f0ddceacc768c290e39c728058d4087dbd818ae6c41f55e89fa6bc83846b16e0fbe31417b2630420ecd5d7130cbfdae16745f8947abfa4f3de9a070b2ce043b0c512579721dd5475ec26197f1aee7cd6447a1e2aeba992a62214591600554f40c41a0e7642977e05b8298c742356764f7e015e4ac90ad12504f34ba22b0b51a02cff6560c39d0294ad0a73f238a5f65ee5a62b0c309993db29e9af428e926d507dab09db4ef0ea12aa5edb38a287850cb5daf52ba714068d6719fe3748b1b5806c4ca1330c607621fea9a07a9af1f3c2374b1e418dfb0dd6c9375b7d683de2c0a4f8ca6e5b7a4af2085d73de91693e416989f04bb0a935ed57a9114895e0f890082afeeed0683127d5acf0d8c0e9477ab40903ccf3f3e3f3aeea7f6739f578c05bf0b15ab8147a8181b2d754d6c9dc730be32939b5377832060665ae7e6aacd0acb838dc08537c8b3a310f75c78493765a50074ac5816f35df3e28522544a860fe245a0b1e651d4392a5a4075d970cc58e49d7a12c1f87296d51cb0ac41f28e0b695e951ab094c4b43d6771ff414528eea73f03d6abd69ea38df6da8a8a49460d3a18a0cf6f98fe1f2feceb4486db246ee8b4c582fbbcf32a45286cdab2e3820d8266bcf029ec5108acc63c1a5c6d34ba2d277ecd3c8d42b145f2afc66217a202c30f03fbe958c563291822cde20ccd6736187948024be910145f9fd308905201f2b18d41f38c08ebce3e5bc3189b71fb831d0a8da54cc5b1ff4775521d27540b1e729e7d47f5d0d5187d0fa836be55af37807feaf0de0a56ba7ed36688598d0bfa1337ea1cfa4fb0f3be4d499bc6b00719a527c472401b774cd7382fb989d40070cbf979bfe2de90a6a985257f4c90a60efea7745fcebe22899bc9cf53561d0df4aec2c4db2b899f4b09c45bfe6b6b70adfa88eaa84c5b647ecc2e78bacbba0b2ec71d4fe3d26311a36db443818df0d0fe8c1c864a6bc02a6dfd27cbf847760917b2339d6eac79d068f97dda6a350cf5f053f12886afceea328a18fc52398f0420e12a45fea21aa18246594a4c111afd60f15ebe05d036165948d2be1dcaea02cf4cb1f8b7bc0c76bdee1c9aa6987f3ba0bc5938f5a20d077d1fc45f840d000f75846b3aa7f09219047a145f192e79a46d54b29b73400ceb024a8897e5705a06cfb567d8f8ab29ef59e3627b74404206fdc8feec7e9bbc7a0f383252e0d8f801e641a47bf2553074b576518cc7b557a762573f79ffadff868dfb214a60cd060ea4ebbb19399cbaf919861f81228edbfbce38cab18e255dacad86320b2ec31a07cc2a579d97e879f739c5a23c79d5ad740ee8fef02b5e8437eea40b32e29abc083a5dcbccbb0f5f47a63440d1f0c2ac1f396ec7358a2db670ced5bf627e0d520c14ab84eb00ec785c985edc59963031969b0276ec701212eff9b6317ffe72fd03e297223d004e3419bc298a09fc4ce45b55ac5dd855e42eb334ec3527bfa11307720867d63341c84efbba2185ddecf740b95aba3b04d8de67ce5dfc808229040a078a4ee82e3e201b033729d437384de0d0ee75f45d903d9787013236eb1c3902bd52e9ccbdc64362ca97fb37a15e6b4d6a00652f65a294c280ed624392072f001d607b05c694bf9939e251187cb7928c939d834dc6719739ab3d48389e6c420c4a26d47ed00a383d3a2973e5b5675fff04ea5b87fbe98bc6bb5c39d135327d325b1a150d6e5cd7fdf5d4b3bce88d2277e2ed1bbbeba3807f3dd8f02dd6e3b90ebee597c9f8b248f3207ba476795b2b2857c10876e2b79f0e2458d2f5553e7b0fab5271861d919c0a957b23aa48e2ecdd7e9435042a0d9883cbc180148b8e1204a0df0814905f7c6ffde50f2fafb819860de314bb67279bd038c6e40fb559dc0cb8b3e4d053e53766c7387f8457844c02ea78c86276bf1fb0ae355ae58a45a8013a62e045ce11a367ae7c7a8d30f4119b81093370a5995b6e9e7e0c642e2fe307e875e2e88f3cf28ceab57b37afe84c9dd19ca2998e323ca2a22c3ce43476940ccc3d5597ed31b3b185e4db2762cebbb7e65faaf42fc74a946f4df04b3962e20e103a3318053e6fada4d83c2a9d80ed70408a7c951a56b496a54d20fe590e0906c6d67fb2ffaf507e88a1e0c761d6c71d259e6e0486abeb1bbc68617575528109351af848122352ffbb0f87d2ac9d76c2cff89a6e2d9383783092d7f82b27240ff286f1d82900cb758c84ce6c9eca798540bb3516e02afd1538c86fd97aa2ca0b63a103b9e71ed3c06643de6f7f3e2a65e8da2668b5a271e42519052922346f089693ad3c0e68a4a62d360f43febe01865e2a410b45ba2c97fbfe9f09ea5763064a869f91627d2fda25677771150c5ce0b3eb8881b36d8f84e981c464de4d2b0c08f489ce817acec5dfa09b1c27d2b86a2360c15eedd805d2339e38db5e27640fad496c6c820353d0fbe7fa4279c62aafc8c15c49741428f8017f4b5fcec258027e5e032d961ee7e5e913ba52c8491ca6d071314479cd1ebdee12e609592691094e3597bba9ff942ab2b9bb7a3f65ae44d69dec78d269dd4a429bd20035acd602f16ee8318f81d37277260a8aa15af1f0624ea3821ab9deeeaa6e9c8f339bd90638487444bb3c6d17702fd2e352c453208a1007337aa4e6c62b3f4817de38bc0e false +check_ring_signature b1a9b777b39de265f1cc37b1ea7095f596bc43cd50c2345510cdde8fe935c2a4 287e0d75ef68de4ccbb593b87d7a3815871bd13d77f8851f70149f96b2887123 4 1a1f8dedc70a42fbc84363e8ea5fd1616495351683cd323e4ac9f9fa37ddc1f2 48d9e4b8a730b6979237e121ae1e543fcd6ee7b9bf2a3e09432433ea9593838d ba50ef85edc8273c8b1f40402dedef49854c4d1280e08cbb3a06eb598df204ad 10d818982bfdbd55c33cb9dc9de7c2155db62e3811dbe2a8e06172322d78442d 24efd4eac6caa160ed7e89a71f3505ade2e11d198c05e83a42a7b786a7c00607f46eb7874e3696bdabde8c8cc6be1e4afff6c2115f0993795d4d9a46d96d6e0d9df53b18907e2b21770bd477e63aa1f217acf19410246fd145c5b7a70f2ad50ec7b87ec978798f692ee02546d7066278d35b21b0fb4ff076afbd1bc17fc9669558ff3f5b46ab4f0e98e36cdcf2b04a6c4691324f991be05927d656b03397cb0c189ace0b192168646d6ecbbf199b706434e39a973016826fbd78dcfb15a6320b0368cef87a46b75b5b3a62e840ab6891c0fe415edcd1fd1a6e8eb45c2fc9930ab485c619942c98cf6f96ad6eeed98713c48ad8c092a4090aff0c6d7b7c569105 false +check_ring_signature d1541bb4eb47e367b34b44c6c714547cde8e713509e7909577482791b4bec03d bb375dd97e374d0e6e523803dc59aeda5ff0639cbe21c8c8a3f99fca9f92ebd5 16 9533eb98dfc25c091b9ca26f5de1e6a9d8b00789bccf5aca368caeab398b2f15 47d31131ee27362b29ca28a99e017290519fbe35f5b519deb897b620e0387457 7851ec28d7d01b3192b79d7bcbb8eff45527c76db839c7f6e3de345376bc1579 ec4af80dade1954061159c034d079ec4536edd4517ebd81b0499a57ddd695903 dc0cd0aed822411c41f947cb30fe0fa1f269e59818104914027e0864a3a0eeca 8de5e640acb7c2a4cfaeab0069c7cfc33fdce6b00d020ccad9595fdb2ebd4de8 4b769861ed9d57c2797639bc050d995f728eb507a4b6b89350b4ad4ffba015cb 9b98d7df5dceea1cef57baf60f7fa76eeec8968cc57f95d6b61566aa92cebd9f 55ec0756874d798b4f7940cc30e6852eb1a06caf51f24cefdc7515b57d1e56e8 ce493e4b3ea6b4235d57589b37621cfad5b45cff25d15f01cf149c7dff24255d b0af5270aa50ffeb0244f2da9f68ff283683dd1d9bca26bebce52e9c45587c75 f40bc62d2ebe80510ebf14a45764a688ae73719979fb19c04f17a90d0c249ebc d768c7b804d3526f1ac7c4e12084d721dce82f9b740d6fe7e4e4291ad00e16da 56064ab07f833886318a59170f2ab56b117577173ad22cd03ecb3b1cb95dc0c4 1265112203f6964ed03117d1afc02a491e20d3b0525070f1f50e010b7460480c 357e24729dbc79fcc488e00e2e8c7575750ad7d903898075934b161874af4ac2 13f61789352413bafadec122d232f5b594d4ac4fcab8724a8ca217be4032860e1e9a4fc2086a350d2552c7c2c68a43d1b1c1978c58624ec247035c428c850901dfa5eef6eaf1a95ded70acc7ae746dfb94cd9964a49a123f9821c6fe2aa3560e2461a70e05ffbcf6d6c0b2a28961899483b99f6b60b52d63715f9d93f38104009388bd6b361506a18c152b0490baba5e0c5e2d30581c13eee89e1e80849b9f09ee2eb66d173826bc2ac42b97366c0f28eedfd9e49b6841257ecef080e9dc440c4ef1b10e3b12866e1f4fceb4addb9151582b17f3fa132dbf98aaad1ed3c3a6060ee8e0c6d98eb6d885eb4b76ae0ac2f223c8ccd4c38d753514dfcd07d869c50fc8731c4f2222c23460588dbe06e9d13156e3c817a1b1078487e3b47f322e520e2eeb27a276d660356a23fa7ab0c45e0f01265d84e0939f5e5e2831bcf613de0421d70ce2ff15d8b9c0f6b82eb19d82dc3773c98dd95d168abe87f0127422a8082020414ca867ff30ac1c951a2aad712857491901b4d4348e25f08612667c7f02c86b8c281c8b8f6770467cc2d3280ef8cf4fd9843bd165260f6bd834333ff40921c0737e0c7b13d17fa0355ab62f97376ceabced9c2396bafb6b74f08f20b602320e591a54f340d8b6d68ed953a677da99df1bfe2f27e0170bf17a6c310e630bf8aea3c7a7999910b7ce3903b8604c98eb7af2695262d89a3bc61f97945c5901864f07a5fcc642483cf49ce1ee6142ebf577ca76f37d1fc04d2f3fab13b244026cd1192ea74a6a7c6fdf9cb2272f19fc942d7dcc897453816ed77d9a5b3cdc0ff1d80f32f97b9b524fa2e7594c27ed3d67162aea6ecb7281b1ee5fb0dff7c70cfa51583d787ed4d3cf0265c4ff56d3041149372c31f2783a5d7fbe7f5c66da07b890d43cf5077b7b129454b2a5f75454b7a17842e1d60f2d53cb3b4173382b0a36b61ed624c03433ff9d2a0554182de87a9e17f9c5cf92e2c1dd55941b0f3a0fa6fdda9f15e2e89c12119bfd7eb82abe08a17011c60f1ea89da88344b260b2070df2eaedb27ecb85b96cd05416d37f87017e4a3e520ad51fdfadc9d2d071770ec8cb82731ffd62a98f41cd1fa7157ae2711c37de20b29085c968aea19578080696fb6d57cbcf5ef129835d6bbbaffc081e442af447c76334775dd2ed673efc0d57344c5085a237b370130dc99c4a5cabb806d9b4b86a939b822eeba24211c5028e7bcfafe4d8f327df35a85fe499310fc4f7d9e721b3d7a75c2c1a5830a5240170bc0f559d1c79831a2512fb98e7ff34d3d401e59267014d26192d367f993c06f2bc12c76c159556d6d808669e3702ecdf46a5390e3dcf77c96caeb31c396901fce7bbc82ab77668ad49bcc12e18e6f658ae41e98787ffbe2b3beb3ef3c5800b986cce492def1f0a6fbabc8ac38c398045145788c1bc37cb5c375f7c80d3560d false +check_ring_signature ad35d7cf75cbaa24dfa79d9a952bb5b52c08f07cc461ac833f134eea4ddfcc41 9d6649f8cd532c5f93cc497e35a06ad9d75ed0ef4d35835630e1d9c2af27c3df 7 ad6f40048f651433f8022b7b10cf627c11c56ae88d446d221c7294ed8a2b0c10 8dc435c39b40f89b22ad8571d4313ccefa1afb82bfea8aec2e3a2c77ebed2ed0 8c773875c152155f03b606534d4e4ea80bebb90ccd60f7c4ef01a88e1f8b5f32 0331bc0608cb203bc98302b512de36ac9a152defa0f5c4b2a86babd81fd23b89 3653edbb29b4124fa5260787f6c01da59607ff21ed0cc17bba7484fe1d9e8b2f fcbdaf5cc4d77b589649066ac4977244eb4a6491c5886dc0aaf95daf09565ce2 027859d2c2a10f2ec11c405edde6f397fd1db865f7adfe3161e824c8a83918fe 7273e25a9495dc77a32909a855113bedfd94e2a514de52c80522dccd603fb70cfa013cdba81eacbf3abce14d643eadecf2b789abd63853b4ae514d8f3c6d6001f658e4694b871069dccc2f178781ec62fafb4fb6e592b9936c9aaa8594f56604f0816ff9c7b949824d4070e38cf79b1f281a59b822706daa07154d91fe0f9a019127b85a2cea3bc4832b95e28f9c589b8df6434b37d320e4f2f53fa09108c20e799834e4cedac9a9179baf9b90ec899bf47372e228313c87a1e43d26bf201c0fe7834de9f09661c3a8c9e1b3fefdcfedfa28e56ca5a288c6e1d848f660a7720d656c115ab5c9373d77bb9ba3fc68639bd98abddbe416c74e8395c2b89eb90401fb4017480d90563ccbe4809752d38ed9b645de15d6ac0890474b774c8457000e3e7917ce7fea668f7b176150a180cde40a50fb7c98f0b64da37abea602174f04f6425b326166f40b79ce984b1af148e004b775e394f821ad743b17c31262b3017be3a3f9bf97740316948d97ec4a4bb1e3fc17dc9e9ba3c8ec31c5149ea07504d922dcb6b1a739ccd64fadee2aea4316db173c0b58248197ba331c930a75bd060646a3d9a8641dbc608420b7637fe09e6b7713c5af5a82ce8b25141aee65ef0a false +check_ring_signature c3f850a7c193faea73fb2c4f25e5769e160fb851051a52e2e8832cfde3e327d9 220e91ad80499f56ff0ecdfa65f614fe4bb807a08de092040f02098488300594 15 263b7e81526d1f48b363d68aed8f2f734009ccee80ac1e8bd5b859d2fbc37b89 688b90d515112359c9630517c15bf437dbd24d3bf631fe6c03f4ee62c06dca7a f696557a46247408f908ac78ed1607d7321caa8ecb562261de1527540bb7ddc1 0b05c011d8cb8854f6835fdf6e7143070f1dc2070b06347c4325ee8b21137c61 0057ee9eaa4c0aa4a1ce67daf5675e214e79c5ee940ce52b1ab25229113cb76c 4d7e122df2b9fc9159afec7f972a1f4b453a937654d7e039695b843147de9a77 ba2782c327fe94b74c720bded6e59b7f4650c101e9b949dd4d779cbe1575f3ca 1ca7644628fe1f3b0c5b558b2667f4f5d946b29a7dd395ed80240dbe60092781 fa857ce15f16850c0b40d62290baa3adc124852dd29eddfac568c63ffa37e1ba 64c8a33139164f857f713f7fd176e22d0b99c4625585737b6d8eff86405258e3 4a9f3a31bfa30006d4855c4bbf14781c2ab2dac478df0207c52a6864519cc174 fa9d766ea135af001a7e8e2eb1e2d80b08dc11f563d299b35c3eac6ccc7995dd 1e2a6e6882106330d6de3e4a03006977cd00ff9bf550710d2744b5b97e0efea3 eeca1916296ae8675a25247ed803868435bb1f70ab1970f16d1dc886fe18f40c 5a249522e6adcd596c0c87da16d85badb534ef9309275af9a92704ca53eeedad b0d4674eb2147564863f389ff7d7cbc855360d03dcd11ae8a3dc52880e87020a05d8207f09a0ec822705fc15dc4caf21d44603c66fcd531ca6a5f5543e8c250fa5d19b1d5b29f58d4f5be6f8e7ac570fb40868074d52d33c487a11a0ee04cf0fb725b64de695eea58c34c8e93746d3204eb532cdf0bf79fad60ddad41942080b13fb1f83b423fddf37cd77c6ec78fd7b2ee44d53b79defa89e48feb9dbb69d08d48910c2f7e1a5a62952348857663b3cbbd28800931fea20b1fe5ad82b10730f820753e292e4acf7942d53a3e327b38216d58171d92d44ba5c8e269ecee0840479edaf1cd88896cd939b6d85b00e4af88d808ea7a486be9cdf0d9df3c4d5a706f3d23bed7ec47dfd22b7c469e391d5a496453ddbbdb6a974281fe8cc76dc810e8e3082a74f71c59067ba50fb9c4afa32d15b06880d0559b09040ab4ebde8af0d7ce1625f5b8f45ce29929ab493bbb255f3615b2ba9f502b5c5b7d4378b27200399e52caf2e98294793e7789d4d2b63c713467b63600398ee51a238fb5881d301b6728e5b1f5de90b858d6ba8cc4e352b2e009e32460458f0ac74f1a90f0aca07e58daa4e10e4b319976b43536712e2fd74f0215502f40f9b494e7189f957240c86e40316f22fde91b4216dbf05f7c7780bb9ecbf2bcec8727e993493023a4907fd1ed379e7f030395657242b96739d7588625b440efe107ed66b288fe85a7e02a3bff7a975604eff1a00d5fdfa9e79724cbfec5de28bbfd92fb18b56b68a0a0c980838e0f20f3b5835c0f1bccb6ad6cb74704be62a15b6317abaaaf6b438f7088d4c93103540b1b05709ec666c337dfe68ab70e8f7785226f11897994173d407a01fc1ec0d2fd4e40161d266b70b7636f888fee5fbc35cf60a70fbdfcb5bc806316bfaadb21dd3921cbbe9bc3982ee9956284d6c939ca2ac8fda2701c3dce409e0b380e5a228342e8745ba675f0c399a565106c283e8ed0c2c3112eb744ecf0f71f6f53a8ba9bc428861eeddd97a77a57e0420dcc425a2b42f56da810e1d0f05b41a042af508ac2d54ea8f45fa43f2a2a2a2a4568f9923b5696dcf91ece62c0ed439880818e7f34fe58508d9592ce06920b48d7373b820049e3353bc7fe4010cb8a4f1c3c8e307f609bf4ebbf7d197600070502cf6ef4eedb94ae42a431e8005c2798b5d053a957d1578268c6fb6da85b74f392b73c10882e3727a22c7953404726c68431811d92ec1940cc3081cd2362db1f726a03f5cabf774fade49799f0f0f2f56e2cd669d894a9e648ffc28607cae269ca67cf6864207bef4016ce45b03bbfe6aaab18369620c87e26ecce4be943705f2ca02b652d4dcb1d141005a640d true +check_ring_signature 738b03297da0f41fda190428ba36b77a89836f43b38fff20518a8f2634340686 7aa1ee7860fd428e421ceeb1cd32b14daac7913d754beec9006b1ce896753d92 3 ca2b4fd8b31cdda789643febeed7fb973b0afc84fa9ffc6113d521be15b9ca0a 5eb395d9061e547e1d87e7f8a52fdf7c14edb2e75efe5ced5197e13b7f00e6ec 1459fbf0074374e462c3b1e81d30ac1b750c064e77b6e058f4d873577542ca27 6549465d50717237d8384195c8f38080f70bae095774d191b64fd6fe42622b003b2ffb65d35af44e6889baaa588f253f70fb1c63138d1c5882f9e6eed3c2170baf6095ec22318aaeb3134b9fb633b55d5273dd8935b46fdcd2eb3c907b064a0d67f784f6a0967992e81ac2859c9af357104a857f5eb39ef733ff671546c039047d821330bd096518f2c6269ff953f8efad16f0ff9b799b26949dd1fb76539108430158f7a213c7b0114fd6ee9c488f956596556f5149a6ff2053de166f916008 true +check_ring_signature f93c9297bf140b8d1a24533962bf833fd2ea8fa1656709769aef7f0b6e73d32c acff3fb62d563d030bb4d98698c352572ffc7086f0773fbfbc7fc5ac841e2e58 3 b14564ecbcbb04b3cf77de807cc7c5599317838c41f5b118eb31c4dd9b28f17e ccf88117359ffecf0b2ae13890e1caa26ae28ab05d111a069f44461f7aa886d4 93be32d917eb2eaee7fa2797ea7215616908bb394b42806038fbee0d4f5dcf7f c467b1879243ab125596174f364f00bd59d7eda49e704d02e67b0837c35018064eaa25970a82693444a4d27ffd89113c132f3fb0f660a9637a1425896ddf9d0a577609e8e7d7c93496753c96dbbe731c6178b8ca057a0af79900bee608285c051f3bcc7ca1d5aa40f49e2de343bb2912e019d5dbb59553c2ae06b99c909837032cc90c8dfc3add665fe0469682409dd29838626ae569126ed32d82dc1511524da2a234ceda86e59635211aadd737d0c0fcc4172f68422baa7a0051e32eedc203 false +check_ring_signature 4b804dad40a3329b989617a64890a8bfacd8ca46963410e7c57eec5fb413a37c e456e1e64aabb85cf30698925872bf3c00b64fef6b86276e694fc4f94e2f795b 1 a5dadd2a36f49d667496e0784ff60c76bc3078477f721517e6b9f3be738f89e1 5812bfe885812a2ac4e02a02abc9f46b8ab21bbff48bae922d88642a78887007807f7466191bb6c2b9174480bde3169a34c4baf52268da885536b8db88e05a00 true +check_ring_signature 9fdc16ea81cc4f10a2dcb5a8945e269a8aabffa60910e5e7c0a3f50c632da605 b35e644d3b7bd977d2e0dd1d7884265682a30a5502cc5961dbfc02a68a40f951 21 afacc713f5eb69b47392948aba4a0997dc8ad31470d1ce39f99d709ed78f9c39 5ae483e8d3a25d56757a30bbce59e65e09df0ac92e750af760213a5197ec21ed 2aedd4ecf0abfc0bc6a872e23e5806b5efc25005d350750ef2d0e4587e243ac0 b693ef8cbeba7e041eeb84808d6eeded25e71c621e5737ae2d08cc15587a82a9 141e6daf9f74902ebd5828d43a37a7a52da39e10942bbefe81b2590ff347e531 600b9bc90b69f6639f0c17e750e5903152c14f2d89df9c48ef64404f48924478 cf629b9fe76fbb7ebc444aeb82db2fb9091813363c0462afe7fc5d5a15659fdd 836c6068921c73490077b4bd7963a4acdf24344223558297572e491bbd8ab9fd 47c6b7478d6a9bf1615a774daad6ce7c25d86cb02a371e10954af56984ef3fa9 83c7e868c5df48fdfed9ff2535bed8b7f8199834a2760e66ca9abdab3a5e1005 c13820b9b6a6d05b682dfda60f9134e04a3dcdcb87af124247b4b062b7895067 a7ecc388a4963b760de718cb392825ce098e09b9cf2eed2e9e6ce41277e46e59 bbb9cb08416a0dda987e95e0b04b48bc83bb0fbb0b27488440c71ce197c9d8ba 03ca84a40b0c89f855e43ecdf8da5d62ae195a90806c83cc1f743bf2c496c467 710860197bd60553a72f66cc60efa88703314e5bd3ed9dd370a10e30895e15e2 a7daa52cd0c9089a0f9ebe93ba49a1d96c18c93e2a7f05551b17132c388ef559 79f574b8f368b6b935e46be4a521d82fd1da90b11e0b8d6443d7e9e09e8ee044 0baceb25fb24ccc1bbf68cd7e26842df2af25405ead44afd0e92a23f3a376cb3 c57416f181cf5d3a08fc66663679c54705744ac523266bd17ebb91d058ab21ec 1a4440a45b0a983a7c7311da7f2b453bd36dfa7c56c7e5de071f9ea490d3465b 2d82f2cf500a11c419f2b8ab4654438fa6ef05d1b888fa2e5459ccb1af665b2e 986ec0133df716b1306f0dede2db49c10294ec89da3904b068fde7e4795de30f1b3df21689fc0dc97a43f2366cca658ea168f82c29c849914b6c591a4f4bfc0f74fa6c1802f0d688ff16337afd46f04089deb818de15740d019eecfe8876340a2c493ebce9ff58119dee5360f9311c0198b4e0effe5effa5ec6457c01685eb0156899d5e10be6fe6255ba3de9edfef727b45d66d8d7780db5b5f903de65c7c0a08490bd494e32941cf2961f8c03b2d2c35791ba42f3852515d52a1dfdd0612716067c75419a0446940017b1daf2c28714ae34e4c2bd6d9b1ee93af79bc686c0fdd7566f6157e9ed56065d81e2643a8d7dd9a07c91fe08712d1599f032f1662003fd4e54a57014c768d2e459a243539bf6a6601a231d2804957dc7c9362896906f46bf33f9ca8dbe9f3281fa4e761536250565ebaa2d7f1b24bf7ca0012d9860c05d641165ff2044958dc5b760b45a4efb26781aceaa83bdfc7f78cfb43c2b307fda6e6417f29ca5c3a659dcd2365b55cef4f0ef60ec019a022928b9aff2f3f098c9fd3efa37cf824da61e9260239978948d9eac41ccc665e7331db4b22a57806cb90512a5e09b1eb40d6fb19471b654a73e51cba7ae01d74d6ab92f62ef0f303306396b31c6124796b91ef5fc7860fc3422889c252426bde66649fac13d5b700b0271b630010c1a0735019ffeb8f21b078560246d815e42502835e523041ed0189020583d4bb4c27560b5231a3d52561add00e60fc122e1cd2032a29f47c2206d427f4e5e7abc1d9861414790fdb517c2a3681df7cb1120de9d166ce8ad49805dedd59a8f79a92780c6690192faa97e1c8f5cc2a1d0cd44f50d527b9f52b3f03cfc5c7c610ac5bdd075dc5321393ceaa5e53a28c9db4d4daba7873dbd8aef30defa9349171c35fe97ddf1f2c625005b62b585c9f4a241358da25df6f02217903576e668650eb3747dacf4a70eb9108e0ede01e24743d87915db00dfcc496cf0212c2b0d00815ab91f0aaaefa88a9d46640984d6b165c1703602c47ed3f4f7f0cf79e226a0e47288fd95901bfa1487de6607d56f46dbbee33c701da59b84a3105298c737f0d056021e0db53ae375f7ee56d9318cc68fa9c9e56d9e7d898c0a10438ee8cce27364971d215b63c715c935846d6be834275f770ede667220e4c8601f57080e45ac16c391e805c10486a38438ddffa5491479d2f7f340533d711eb597ac47588fb62d890132d680ab3cdcd4a3f4fd315ba5dca77b33c1d92ceddcf0ba2081c61aed956c2029e7f66392f00413e7bd8909c4df492e036689bd42be90af57a914cc48f294354c0bf677b4fbaede639d467936b0ee73e66e3642fa1620cfabf2cad691ae0729056ebd1b34b07e314ea4f167b2224ed2e007ee9a93f5f06a0803633134586008e10dbe3371fdd8701fab493c620f6838ee9229717decd0ced162ec240af88ba52cbea8b9dfcb5b54f16185ec631554167f17c62d552fd05b8ec666b450817f037d8c11fb30ae73fe32c5764b5338a7294a1b410b7e6500b57dc582e63dcf5b77b5b7f02daa55cf9efb6be67b64dd2783962db3f7cc9e60c3a2b6386a310190c9b764e8e7e5d4e55487c6783ad6885fbcb58d08e726590022fdd59fc86121c667fca3e121f80ae91e421ef68660d4496d9d685e1cc73a30848d0291526490b3790b6d12782a3589c53913f360ac4182295b87f18b10f0402145d8f3493acd1879d8c2e220e158a7fe79d33bd36c0859c24528f915fa61a0736da460c3d9ee4b4ad5eb140e2609bd6129036b4a52611f3539e58518b8c9206fcfab35a587457e8b4bd5457a4883c32613f64288d37ad4d912fbad90f4c7f0e9fedbded392cf2110779e3c5c6a7c4acc288a1a83ca2ef4240c927060c98bc00 false +check_ring_signature c782ebf6f34e11db644adcfe4acfbeb4e27789977a171337d71aa7d945f71d16 84059266d7aa6c601afe8d4499d228ded90c127106a85b53dfd5937fca5c683b 130 2f06f05287744087c110f819dea589da03ee2ca00d4ecf52c1d1a666d3dc0840 7109438679526ad2732223b431155eda177ba97f4b995046487a15a2c07a9ac2 5eba7d79e92deec31d114b115b9b38d8f6d2f86704fad2885e9a55705abd3d3b 39e1f5c2bd469f71c2e6caaa4d01d9b138f77da574552585f275612f79e46ec3 48d81899ed728af093595f2abb1a9f1c27050d7e2b2a34ca4104e9aa4f84fe9a 8e062b2a869bf5083d9393c7a7b3d5c05eb55c89520687a4824944888ec86501 7542f2003ce52a5641ed590cc1fa2edee17f24728445da487e1f1f365db51aeb 13f66cfc5d5af143700872e844a0f8fdd0c8e7b87cf971c0147c15f4da610593 1f889e0fbea8e86472460ecd3a9f976445edb4055638c485daa53659216c61e1 542693a302af0cbc18870f33dcba3a566f39cc6964cb6c13904113e2fbdf5b9b d9d420a5bc61cf14f38dabca80f6be2d40343876c3d58c5f5353c9cf7ba3147f 7fc9136cafe91ba3a84545f1f47c0f335972aeca9a1c796c52ea4c37a2f4ea47 f3f3659548ae90ada71a958d5f77a2978e705398d610647b254dca443cb3e7aa 7529c2850bc788edbacadfeadb07c00ea42641bc80aa7e79b8904c0524247aaa 70abb02b29bec786d50eaa70cb580e3a36068368ddc00a1d46c8ef1b747e3662 f0be39408e91ec06367f888ec31ab0320c9ea96b654c0580bd68f1178b3a4f3b 9ee29de4ed5a64da262b30a5c5bfe9087535102419e6da6a800b432ba2c058ae 338669161b7a6ee1c881a05332eb24c1194804efffbd55c56a99aff74a832009 bbd6edb9a48739c6c39acb61f6bd56d461944234d975e29090c1b593541444d9 a21204e47abd177fcfb338d996c684bbaec3fb1d49a54334364b85937cb74dd2 53b4ab7d0dbb591ef66e95ca140484e3bd51f4feab2be39404acb1a82e9fa35a fc3440ea946dbbc442ff4fd1e63a7ba8ae9a8b6c593b6f0c7c026bae6dfb75e2 33527e415f85797c9190ae653ea42dc276a453bf372e557d268defa1cb95acbb 21d8368c3ca8da66b241c6c9835026542a781310409a6e0816435d1fbbf46c3d 949f0ff5c3badb2cb3cce4d4fdc95e6c681906de76565ea1cb60cd05aad8b601 a0a7f47138b2ae1357e5777ab3935d55fffed585a60416e6ab1d7f94b9157889 3660282869126d28656cffbe46a26559c4ef6d08c97736358541120f42159831 ad3d79a6b6aab6c2c5b3afaf2f4f466fc5a2a582fbed6aa7cab0f3fd11e2cf46 42a440862e68695e482f25b72b98d3c671d6f58b10b7971a75074e26e94073b8 79e35e889d13b5a0130e8f0c2e19ed32826b7f5ec4af9576e44c3efaef1138ba b384abf9bccb64e01be45bc6236758925717300eef4a50f6c506421afb4bc1b7 6d43055f445fa8777c8226872abc2a3a62a5283bf06b6dd024059b180001dab6 b9ea0c4f9503a7d4843f07cd07e8ce7ff63cf584ffa0932c354f8daf46f17dce 3626c988956990ef6be17d21345a7f3330f9a405aa8cecb1a31ca62d9e9d1e91 08265aaeab3a8ac01b5c158cc1881b6551b54c5d7ccd5fe3159f667176cc2db2 db8cfbddfcc7b2cf481cf3c61b7bdc23e6b7e8e8bad640f7cc38ac94fd9a6c76 5d7ac6ee8d33b619ff4761fd8db620cb3e171d5088e56c2f9b611b22f3c9b5ac bf571f3059546e8cf2ad0bdf85d79db53a09e94f5e15e0e7d72753621c17b1c5 a4107d4ae8d9231176001182017b54c82bbd76db3989b06fa6b01351a920d136 aabfe1493640fbfe8b6acf0f26ccd8eb59e89241a5cf31314ff9b63e02b74146 1bbc1e6d0ff9bd2df809bfb4fadf5e7d16358190b43596261fed98a3ed0acb38 6dedfb57f71077b1e6ab00eff602eae98f246ccf54a50a2c34a5c3c92d7ca05d 27c012dbdc80bf36fb232b191c44e0f02fd623bdabafc8df16395b22a4114223 a0e1d7ed2db0bd883eca384c01484864b5a3c1a34a2658490ab7fb02c7e7141b 182d57cd04de636b39fe9244896897d600bcc8ae6e4e0f42a51488917f0a1686 947e7eb346b26923e626f9caad156e23561eda9a7f03b571c15af89862d53c9c a241c3056db40fe1fcbd6656a396219b3ed319f7cde812db032878c56de66abf 85ace815702775a42e6d1470329cbe27333abb460e9e9cf3f8bf549b105fb5cb f0704e484f07c1340cf572bcc326e778332078466849afc7ed571938173ed728 eff9f423cf2575626254a88c42e6c0cefb3ffd7bd324e372e249b1de751d8bf7 35bca2226ffefe568f0f4447a329de7f5475bf291ad6759f140d2cdb8fb400d6 b63482e008177c4c26736d164dbc166b23dc4db43c5640eaeb0d37d290c8e6bb 8aa6ecfcff97f94c7539fea470b497fbdc42c344ea4c9c2d833f56d5865aa228 afcdc12ec1f0a0c11c9764b90591c047a92cdda8afdd5ca3e2b10f72c9e52b32 7cfbe216ecdeaf80ce6fc9f9147f457c58378fcd7630b056076f56e324fbba94 5c83807c1941f2974d53e975d437aa2182de34d03e544b0a892eaa87c86795b1 724e8c4c54d62d51d0ec284af35077bb1a3c83ebf5aba4f99fcf1e389365c469 4a961652245d49e0e55f3c8346d2102a17e5c46e8bef32605f7f35b487f9d5d4 e88c33b854530cc7ae3ae943e52add26d20040e1c07fa476cbd8306d17d5aa0e 9b0ef790e47852f1807a898110fab037c74d24cfc388e5946712bca64acc5af4 396118216c48a90bd4e1434ba9b3d61a6f343cfea4beaf47dcf3e6ff0350378e 820d6add6b3f401e96e2f5fde6cdbbf400580cbad5c787553d11e1fd0ba733a0 6616cbc927831578fb314c72b9f79a6dea05eac6a9c209022780e31ca8fff711 e4bdb23544d119eaa757d9717e11221a3fafc21f9016e82fce829ad45dd159a8 29bf5cfa0f867f633a8880de887e29e1a4a9d0011ebecc53abc390a9a6befac9 89e251f6edb21dd2901a1e0cf336c01e6b9a353c26c596772cb5a1589100fee1 0c494b62fd3fdd58519d75d0d96188d5b57975e5d0a6b470f8776501cc32f959 2ef800c753b24a3d8c1603b172c11f2926cadc5608160321fd681c0d3bcb2567 58ef776d34b662acc6d630c7c904aa8c8f98ae2e898d2ca5fc12c97d135c242f 6fb79a476d2c8897618bbb6e0d303da273dd3b9fac57f7775cfbd5db83721a50 a1080c786c43db901ccaee590aad1352e80d4989a72bf002ce6089c702a5141b 85791ff3e88cfd5b9de9885715b158cf41781a8f5d98febcd78d4d5144513ae4 775bef3c9c581e599808291f1ebfc0962da12e0344afc903780f13c4736db4c6 d7db0270dd7570e96fa07101d12437d790ae643f001e6eb00576de4281df7b59 bd305589c5129f107cb1ae88666785cce9fca22e6eec7dfcb42907c112d0ab1d cea97481a38c79ebeddb61fa9d8ca624ab3ad37c5706553ca8f6975300026788 f89090d461ba8cb409355c911906b931f744126acd77848eff4d48ad1da94a89 6b7bd0fb5f3e771a4c0e32895987a05c2836fdb01fca6aabd0d1a287913159cb 29df215f50d952f03b21aea3fd812a73ef76e4138e873734af3ef0e74346ecbe fee08d45fa3e1664a67af3d180ff997f099c6df6333d69dc994a4e995fd154c1 a27b573efbe6542e5fbd22e774cf6f0e59cb9cf6a20ae039b477ab0b9ea814b3 67d0edd6739ed26bd508e23a30ce8ccf8f073aab380a32478a35e209f83d3b0e 0b85e36eb236de99184e142f1f65876afbfe90656a3f1d4be351886542cdbb60 437c4d0073e609dfb7cefb4630ef710818a6f8daa1a6d048a48044879dac460f 6c8d8ba3a7fc3e31704ab60cc1fdb87cb82724d32ebf57df6d5c0814a30e76a2 e20fc03798c23e403fcac770fe3bcf2188ef6405b4303dc3b9ad99f130061b32 a3a7301dbd88a76d568cf9c8182ceaf5f52be495dce4ab3523287d0da910a705 24c678b2f09aeda9e364153da9a10edf155d95a37eb67e5226bf430302df777f 534b66de756d95c123fd03d249813f81c38fccc2e25177f3da93d3e1ac22d80a 6d40d1c799c08566f812a26934981418484e3be70fba8d66f58b8d3ff46c151d 31d046a75003f32407e3b2bde8576eed665c4d80c84d73875e14a750655eaa7d 24c9d793bee3f22b58ec6107acc124b4bfdf4751af311efa58d1b95a120b1589 bc16e23a2e139bff16b80770f3df9966c8715188c453d6fb0e883717399ddf46 2a4285b81542c5bd5a8ac8a0530f4238606ef091bf407e25d284ebb6d671ba8e 7ac9fd63d84eaef7ee55054d2f7fca64f96c5c8796e7529594157ee41c29f10d 7ef991550ad83d0362e741c0e3c3dc935b3af5a543bb5e3443e1882ad638ebf6 34a2996c7933ed89fbcb2e62ba635773fa960ab038ed4a87604738c833dc6363 1235c49c8edfd471f1bd01c45182bcd40f2c28f79b6da0ca83c2a3100561ef53 8c6747feccd56ee284016c0070b938bd70199ac1007149412d5e925b4d0338c7 3edf73c658ee2229b69e5d4e31e00ab4d7fae54c86d785cc3cbf823b6e7c32a8 345b00202512f61070510aa59df9467ab900b846e5c3d6112e22963137471f19 1a098b64d43083de4b1f756a36e10691d7c2d533a966f0d720b09352b137e87a fe624f59d236ac7f56ea2073c949b1cea3674d932638409b9604cbcca1e61757 50aadabdcbefefb8fcaf62023943a05cd64dc0755f5dcdc7bc9ef8aa39d8e6df 2d38116163b87f8dc9b36c6cc5dcb0175ab1361ab61d8bba6b2a2595379c4a4d f1da787094528f52ff01c4c0218d7ab32321a5dd67c9758c8424d9ca9ff7f6eb 43c6cdd2714091c741d4bf6da79e07d189e897fef676e65b6f9ef65780580799 f9872654875c0fbd73698afa5690326537d2bd499cd18ca284af03870ed0916f c6a1e1a80a1cbd35c097895b472d7528f151219c533fe006cc348be9707821fa 1797db44ef7e4974ab847afcecb58348de4d117288b82e8552ccb77e54b35cc5 386acf89210aebc6390c2856709ff6ab1c3ac0e6f69e942cd2771fdf77012d35 b6b0bb9a69d00a9ce1b279c723679cb14e7b690c666f53ed4087214428fb16d4 ad84673d9a1861d5af5585f892e5a183b38a4e553995848f079394c40e89a484 405933d2fcd30e3c6becb2af63938fd1619706836e21aa178bf341615496e0f9 8a83c0534f29911a2ad4b1725d1833fd83669bcbeef76e964b3f10adc5f0ca4a f7d28eb4e0504cceca15af5db956bd4fd737d365dc8e2ac3123766d5f19ec130 7acc7bd467640deba82c8e3a549817fa8979636f8ba5140c252ee9fa461a5b7f 44501a6c995dddc7ad46f8455b3393a2972418e467cdcbb168e05b78a6676611 d79b36b3cf7f9b14d9f772284ecc03c60bf8da266e34f00e99db2a7e1e482926 92213109126dad1114cc6646a0336ccaebadc3dcec5fbc75742ba40d11c0752b b7615a5c37301c1cd28c740d4b6ae62b0b8f17c6cd936c7cb165a1e9b8420962 ad763b5258e86239348a1fe1625e9546b6508bdf2464198ba22b5838fa9a6f42 403c71b0dd1a1329ac9a9d01ba0987dd934008fc7e6bf939585d69e9a46b9e41 0587c51ca9f7e5f16ba29c438c3f0a1d3ece2a4a892adfdefa98bdbe8ce244a3 7aa8b9ac40393a98c1cecd03308a328fb781886cefafda9013e8e93e93a0c1c6 01cf67619adbb876c782a15dc5dc4dd91068480012197d8b611a052f8ea3439b 0c870ae502129236ba4c78a75e75da95cee758efdc8afdd415e2188f0176f210 c9ddc4d9630d4472dc4c582c94f9621c4a6b67abcce56a0b0f22836686ec3147 415543471ea3e5386727cf165e7cbed60180d02d7dfe9c99b040b743edfe1e0b a27b0b868aff9049ac7b8b3611d7c56b5e3c268592433503f914b405bdd3ea18 88ea3e7a374eab2c31cfe0c27f93f2cc38ac58fad9344a22bf14feea094caa0fa505d2a15146f64d9845c20adf57c9b5552242a749e87d52b40a58d4f97e44027c2baa9cf9e847617c773c8c254fa585243ac30b6beeeeb3106ab43360b24a0449fae5997c607470438c28f84ba4b0b492ae3e273b0b7aaacce406aeb2728000d26a4e53ffae308148e87b3b3a81e921035d2f3ed5052ab47afc5d6d1b82ef054d647a96b7b0ec4667398bda7c4c14f3419de7d0b2ab6e394b8cdca6c9bd520d5d69d4efb0e00a5e40e93ff52c120ff87c1ca28c4c7099644e2033b230a38105480e90afa4213ef3d25d25340ff462036c2df9bcb4efa325b3ef5a80dee1bb068c768b94ee1f21eaeee73ced5edb224fa3ecd67d6a676720d4472bfec4772d0bc56c7a97b186e6eb0e31da130017d314569cc21f171b746b2ef31375705b6e029952acd57a6a36eea32528857febd27ed6217d284c7de71a6dec527544744f05df27453e04101cac75bf69f0d13ee9d888aa57040c5b513fab4ed7a38fe44804082e86e9a80eac765495241587ad42cef3fb18403bc41ebed2abf76c4cf8770428732a344a8aaaf0c17721340ad405274105758375511f02df80d4420b936704a94a256c063aadeadc827804a496f6a4a88b5b07f4fbe1e54be414a17b49cd0d865e1662a27dc43757b3d49c893e945702d8aa2a22078fce8f009e207756e10542038972810c68466e2867f14015d6572a6d399a55c39695946561278b2a7f06e81a25160c7c4b8ba37c907db9cd2ee4031c769bae8abf269178ec746bab5e0205970b883d575447e75b638968906869405737b15d71ccc819b25b147da2fd0860044bd58c6b8f94705bfb1e005cbd16788375f5fb6f8eb3eefdfbc3705ddc0feeebefbc2bd47b38537aff7aaf28647bf0162dff15fcb7c7f65b1db2b455090122d5958d2e4595edca3f15111e6ec5acc2e3197cab4376a219365fb399c99c0ed90e425f99d85ce9821787f2a2073dced800fb53148625e58c652cf68c8f760d1feb8937fd219ca4151a441eaaa827daf39f4a2267f933baaa6d1eb52453ed09d123a578f0ab76ea9d671ea2de26382843f652e11f3103a933689d9c8666520126632a62e672fdc736a3c04ecfd72ee63b7a4d0be81bd4947dd68a5e8a5b0b06ba410f9be73ecf1a1f53a961e7f84244530d6fcf4cc2d11eaace886fc51e0808840d50fd162b2c3413257358835ef515ad75c7c2a9478200479c38803fee8f07ccf7e3295879ff6d56252aac3ac7ea1325f827335d9c73eefb398954deae310a1cdd36dbafbe09530747c32d23a04a3ffb127d14f7f1eb5bed1226b116c311057844cb8e18a0c374d36f9d31f76713c38192382494a3168ffa2e0bd85c62f005b2252169a9698ef33767ec8a1cbb80bb349680e18e55474fe64633e5b48df807be7cd45814c556181631f6a3252f6dc208036106ecccadc8f0f20e1063d6d40c2ce7ef7df04fad728be11b19814b44a25dc5298394d9fa0afeddf08841d978031156e86f42f8a7f35158ba162c4cd614d5ba34938bb386f0a3aa050df6f14906992d10c29157d241ff2d005037495383112885d505aceafeaf216f3eace1a40a94b67c20140718159cb5d4fae202a7465f1dcb5220cbc16c0acc253e13e7b40c20b85927a32f5db1398b14e7a2fe1b49b3dda074cd2bf66800e88f4d56fcfc0dc18216a595d00b646e255145eac2ea923191914d995c038465ea30ea8ddc1e0548be290726582dc64df82200e29e3c891aa687668013897dbf69fff023ed0106d80f0c7fbaf35600a553b3573b622319f34ccb7fe2a6eeef1b92b6e992cd060e8c98ca0b779301fb9f7dcf7d3e6886b607290d218116429b30162fc08b34280e669592d2e8d5188fd25b20b33895c7efbf5c622c63a21ae2648f2977c083a907dae3f860e53063109057f8faeb6fe99c63aa9eaafa2ed85bfd638d39f80b160eaa2b7914401d420d340c307815f4df3b7f21a5e96f85c2a38f97a1eb8285160c423b04a1c48f22e8337b4171f1e25ec57f3b01bb0a09b0e9240e629202ae2f07edfe98cfa4e497eab1bc8872e3d8f66869396b3c88757c473255393210199a0697081b39e478ff9f42ee5bda14ec40e7f691ff6c465e4826ab5fab1c1de289040bf58b8a601cc5ea42825f69fe5940128b2fac1e31e470bbb4f23d5a6501060657c8ed63b20d325d4c02766bb4ec9b309a040e03b0cef5c0f2f610bed006ea039eee44b205896de765302c3d230e99331d5e089e732c57f9b3cc0eb46f211608ab31d2e5aee5f844a6c82fc2828c7a6c603d0113166541ecb9425bb85194970d132582cc97de9d7b6434e9a923a6f698668c3483fe872d6b77ea30023e93120138b80dcc98311aaa0c46e29771b4ed5454752bde81a552f7a9fc9d87e2963405d28258dacf9af99c8be7a070f75af622e4e64d34e220868a30365695d941e505da22fde9c702f47dbb8c66c7b1a25aef1b3fd780f0898a9bd046770b91848c07cd7b3c279ead9462c8a9bcc70d05723264a5bbea91b3d9fc888c7aa1bcca8005b6e7e4faab37852e1321e8c4bbc4a8c2ce913360e277ec6bcdb0f766d11e79021074298337f67250fee12b08f5ad353508761e29b3851549ddd1a1b70752910135f1d572ab430564e25fb85ac99a7f7d21cc3d03339f1c87ba265e0cc029e009a24952d963fcf57d88069a95bd58547dbe7d1720b51fa41a45995334d988c70a471d797e80958f34d236bed9a0e86132276fef92fe263b263f06a0e3c09e5d013bff89267df053009de895f210e4d8a346a13db77ae244afd2af4da21491290899867c13a174f611c74b6c4151abec74b4c4b5ff9557c56142170dd41306cc010cf3fa086dab1d75ffc255fd7bd8cc6959f1cb572937f0ff6d754d0a81945201d317173409e786a30a2892d9f2fbed94b10c120feafc70ae8dd4f0f9a183060f3a7726bd3641707e3bf55ae1ad662cb3dc2792fdaeea310c2cda2b9932eaaf075a98ed1826c9ed9c9c548701719fa21c6712ddf40fed0af419433f03541f7f06e105291a65bdc69a0c53a5d5d56ba60022851e8df5794f166659cd9ec862f50b103ab2672ad8c52eed0794d2ec32ecd2c00d8252c47f9c410075ceab859cc2034d587c02bd18f254c900a213feae3188ecd08a9d91d72d142fa2522499cbfe02622d8bbcc2907fc07ed489b115c8cdadbba916f539a747b8d885ff1a938199039acbdf99388a1b5c43ec82f5a76176436506e45a7c461b608a102bf4882a3108c9666d2df3de5b75e932c3d073d94e9cb122483be886a0c63b7a40fe21878c04a6be7d01478e7f32971184eaa4561a0b89db159cc5ac76c186d33c80a19e5b05c56e11d546db04bac8b0a02c06200becf391721b24ce4590671bc5f990bac30753bc4d1840efe2d24ad350b932d72eb30833f3bfccb902e8be703f1de0d5d7094a51a49b06645b7cdcd07d589836642cb5c0b8de18e23988978cbadff392fe0b91d9e14f907005472ed940b7c2380da9e4adad6dcfdddb542b534ea5f184c908d6191e80844696348d3cad0e11d8a0ec49ef589e34dda1691f3db907cc6d620d6a46c6d41f141111719ac8fa211b5df4594b4accb2831a30a9064e8ff60a01099ca2b45021120fe27b28c2b83bcab2590bb09874f57f2040cc9a7e515d15610fde49cf43ca1e52eeca350f85ca1f750c4b774d2bd182de1b32aace446ed3a6098332fc2c100c6f17b9bb0df74281f03c17d70f18843ed47c8c8955e8fce9460d485b7808eb16545cda7ec7411c0a40bbd93ca726989db89871c517441851c90d0e9bf85c4d6a1b468038d105b09fa66f61098dcdb751fa37fddeb55798023e0e6aedbf8fa5700040a0d5aea74783194cd585c39cf514b2fa75832dfdc94bbd01600e59c27828c38ce9857f6f86d8a9418ce0924e6efa6a0816b025b757122b099a8a81b0e8fa977fb74d249d1b71b64b2266d55e42f4db36235d12e0f12ca6070212b05aed0411962502c31e7d1b57826007d757c2aab27849b544fa06d7cd0b85683c732aa075105066f07e79aa513f5ea0cb9dc28a8acf936f213b4943ad0cdd6caed0539daaf29b302a83eda3dd78d4556cd96ebbe9447cc1e1c6c6cbcc0107916bfc2d3cd12a11e27526a77bb0c3298665d769ce7a099f7317a5df257f07f3f9ce333ba58fe31358a9386917a914ed7c51583feb5eb1859fbfa7996bf90c8e193048ddbe12cfb57295568d8ed28f9303f392cdfb621b260793769cc357037b567d7c0c0b41be1172ba9ae3b3e6fadc6493a698d2100346742758ff7b7d0dacefa15ba8c91b79d7dc9557f7143a1ed6550ac3f9aefbbaedd1b4af2802d60da613402ec9de7a42369534f31227de9f53ed01c675e412408b69122434b7ff0ff2c220d933ea7bad6906484b2ea61d288160c390c3629d5f17e1224e1ac222021ceaf1834d167b017f9971037d2b9663c0a76585292b49443fd22c9a8025f40483585c55817cd6d0be1e2cb14e95bac13f2932000152d97c7bd236153d81940b26ba2cffaffa1b9a98347e474ce8ee0e5775d1493ffe3a46b0a98a609c0eea05e8ef12e618d9fdebb538e28e5efb47993e23660ec53102d288eed28925b26d0f023c3b960a65e3d06a6a347378e1e692d8b4e1197d79821bc0d7e908a333d70d499dc3d106152411abea03bedda03148c8cb57b3010f490b1e6ef8e92b90cf0c2fc9ceca107f5a5c26853ee6749792312878dfc6fa2c7047a5abc2cbe6579d0408713f6a3f3989976703a11bbf2f897f592f149c1959523c6fdc55b2e582630b8a3c7332c65023d72ab4a458bd6f358486f85008eda85e39ef633b756e2c890aadf894379ce15de25b809a2ee7f7cf0dd17fc6354b98dade1cf5d40c4c87d60d917833abba6ce0f5714c719cf224316c375eac9fc4d18e7aef28ddd9be970005a14a311a516eb276cfbc7173c074289f26579d8484687f16aeecf5d95492e507371622161c91d9a8a1e2f605fff996a9ae3470888945f2d71e86561f54ce9d0153778eaa13b2c1eb4b9f0ba2368142443cb25aebd04bff16537de83a6a8b3d098c9cd05bac5fed8ae28be5a331b0d05466651b4bcc829e71dc8b1034e829480fb0bf981bcb82b9fec206ed954a119e44c963a2a4bd893a73f488f4f8618c74077adcb9334886e61713740295668a754a1e690619c6c39e94343f842221d6b50d2182639efac642a4824b49b76b3bdb1e253eab5cbd6f9027e0d8dd9b6fe88c09c3b2a1e15ba751ed9da76821e3aa6b335319856476f4c2efd98b0b45761c3f02fd7b470a9f1c7e6604d5c982abdee2a92ae98a99438719167f107c5c03e00c0fe572a93f916cb6d2a241484bc9f84f3261483bc8d9a77de6e60536d6b7e92b0b36737706e5b61035cefa49ac75524704866dfe27ff4ce4e791e9274ea436a100bd486f6f99388198ad394c6a65991c73c843e9215aa6b4aa7b9de8a4e0c2ac0e9db0bdda4ba331ab481d215e7f7084407dc5a5bc108262b2b3f3788f98442c0ea7c0829e8735d713b11c7e9e9978b912c9d0d78d38b1856c1cc944edaafc4804d14480c1c7d10393017955debb544325c7cf4016e1a00ce6f048c95a2ad2af0d1d1601a078b55690c34b1c2b41f47c1b5a3e586416bc3e8c82c683948624d70e3709769987a67d8ac1c564a03b6cc4308c0b9a2a73453ce45e9b0f3dd6e1f60cde5faaa75e83b7022d41428d5593af2a47a7a259620f08fb6e874e97f9c6d901df3f494f5f50864bfc14e54b6efddf50ab2af8cf39f98a4b79b5453b8052170ea0bab1f6ec79773d5518b837a89974c402f519d88716eb7ba0d2bf2675de590ad6e215c1110d848bb1f630785142eca04d96767930f58726e13df85a1fd21d093e2d1e3318aba85275a72295f64ebdf972837ee9c364c89986dbba77e721bf0b4f215101deb355c52d59f5943c070b13ff849ae989b876859a1301c3524d2c0ba2bdb3fbc9cc4fb683e70d4cdbbec4da317961713670d5f128996b0c31f98f0a7f2cdc15363f15d915a79662b2a2e8e6b507754c15bef197dad13ae70c0c820312051d1eae7655fcbcba64d17659e1a1ba06892797dbd2f0047493f3efb20e0ec0dbf1dbbde85b2bac4e40f747207a03a13773066b0bcea4a3d27c42dde8900b1efdf518d0486e76d5774d0bf3d63f8770e30f4c844dfbe3734b819375fd790567f134e47bbb32151ec614dad3989b8034cd06e928128a98d877d302586434022490c9b86e42dd562bc660a356178043a7a8cbaa1d591b3fcd46d3552bfeb20243a998a3f4d7a37e9c876c5f945b05fe54e964c7a784d80a53646eb7596c5101d3fc96ee8ca701a9e7c6e5c6e99d9f799be5d751a09d14f6546d821336b9d40ca3e5bc0d3e353cf64acf4ada30952f8656ede2ffe6faeed2c67e61733317450137f9b7ef6bb1a727025bd98e265e4ade661533221dbcc252975e6dc7a21084012f9a1f9dacf2f204da1b51e171073909e88ef0b3c6aed6759192ad09e3be5d0e8450666c3c438c2dbadee07a1b7c0e6e8c1570ca5a1553ff9a2e3bbe5e917e01e03d69343058ce7405ae19dd59d8e2818da435ea36e9552c95dd9d79f04bd1034d6f4d21b9cff42bc133db9f1df55eddb695b3d0c4576f82cc2bf16c5e1b3408b0a411b646d0b66c4065ba1e8e278751330ff74372544f6d0918b02be95171052ae6c75270681670500f23b789ebb47f678674e60484faeb69f0db2273cc0a078cdb6d1a6236ac6373dd99c8ec9482a91f6b06c805ddfde60f99614b29e87f0b2e527e3a33b8fa2050bd28674af0dd0a6185dafff4426a5255b09acc5e0dbc057e7a86e68f2ff5832728bdd9a09133ef067a447c2054eea3eb8caf411f7ce20f735b59a7fe9257d0659b757e71ee7e209d8419d8b4d2d9803d143dec4b06b3007cdf11a31505a2a4aa34cb10e2cf99b3f823b59f64a92536393e1a9c53c4f206b2e7cefafda05c11c15840b055c0c6c41ff3985f39c860e618af84a00798210b02f9394d773cfe4415d1cfbe6b3214dfae02cda44a86a5b3f0ab6cf44b87080044496f593263213922b51d205038e72689c345c92234155d2602309602b12a09d5640e176d4fb814c86f7c036b9cc72e6d18f95492b5dde32cc7c0e38d38240721bd8d2868e84baba5a466e9e5c80608bdcabda60599bb27f24578f2621bcd01694165c177543596996af5f0d4df47e52e30cbcf2d85f3c32cb62274f2c665094b7fa8aa42f425a33499c9f8dcffcedf3a756ff0a39ee8f69bcbca614951aa05c15a16d4e8898ae61d920b81b3d3ad0c85840e25336a24567a67e87402f168099c16f7ae43c8a932a3c30b749da8575d3789f6efdf5e07adb4d5e24afa4ff202678ecf5af88ea9f71bbf7869013e8bca452e39d32d89862a0d127336265f4c0256e550fddf8331b64351b134a5fafa1ce9235a2929d55db6bc6c1f4d34ad5d060973d1ebb2adfaa8bbea00b6d96b9783b3650fe367346b3eee6b8484cc3ed8032f4b296d8d428b5d7cc619c878f03416424532995a47f62710b18b7f2597f903bb6a8248bc0161375d036b11975ada7895cbac7c12b9eff41c21845e3319ed03a031926f165f30fa8148f875f81d6def0bff9b01413d58cc99aeadcbff3afe06b8f200a5f0b0e79ce322ba5c3eb12df024760a9583aad7465c824c97edae580ff349d555a6f072d2097c6fb543426b233ba0d261a3d42fc4a65af5fc311af80b38ec5d0422a1f24f68eff36b622235b57c7bb0c9b8f6b65e89339d15a4464d07f49ec1f5054245744be6ec842a7116b97989600d251544c643ec637394df870a0b7ba2885f9cf1badf939a1f228fd117f1ac01e8656f235e790a93602c2a34004824cb72c9c40e11d1ce7d8c00f4d5535873334b93df0d9207f9c9da9020920879317424b7cda27e1cb8410108c3f79a6981047d0635fea0cb049cee403a0e03a594376f4c469b867d73a0bd0d9492a8f25633a5830bd43e7ed9e280eaa1a10652451db3ad328400bb40e1de8461c9029ea066528c371e8382457158476a3e043c8c20e74aabcf7923b37b6ddc31a3f8ab4e1dff2dc23562dd47c2e5b6c3fe05ab7146e9e77736f775119cb572ebc9652415c088d892aeb43c76e0fb318c020ead95cbcb0e585ef5219df0bf81135dcb74c1f51b6dd68b872e694b0af0d8d408abf9b59d6bce69a8df76b87490e2dbc57d2da377276b8609c4cca5b7f30a6f040ad9ebcdcd49d73eb1b91402acc20ceec8cd2d5b9a319e2937c5a8c7838b880af0566fe7b3384ad91be83bc311d4ba5a6073fe2459c641685038fcdb421818018ec448c22ab41f6f29b9ed724aa3dd0f4b45db76292d8dde17d0f0871cd4d20807966ecbc578e70b67a9a3502c5a233025a6ac5c5e07d43cba0ec3e85dfbae0b8b5c54a66cdfb717a2b993f4f41ad2783c9d8a6997162bf0d58c50f22283f309eb0007985e1cb75db4c635e6e7963745fd84da74d245406a462baa33dd483102655d460bdf7b381fcaf77630f0163a850935f373bbb4c0270f8094b2e16abc074e13ad666d2cbf0b3872b7f636e5210867bd270babbc8f9c5ed53dbf461a310a4a9eafd117eba333aaf88024b603720049dae2d4f641eb3b25ab037ddcafa00913191c62bc7ddf5f65c46544d812b740067afaf7166c222f55169e9199cbd803949bcf5d9d448c3e4dca5116b4a9f41c8f8d4e6f8415c3c8550f2531f3638808dcd2b51e7fdc60edc9fbdd8733a8132a9d645a59ad6204221caff961f0b62500b92ccedf67f40cf03379f2792d952f57cdda8c8adff684a95635bc232dd9180d7c28b8bc987cd1eec0dd5484f92477b82fc33e4320532478f3dec606ce96e80bb18ec4315c6594586b5adbaa371feb9e81d951a249ebe16fd464af14e06f0409737f35156b85289e3969592653fd3d318e013c0675050ce3445e8bf31dbf980fc0fdd086ab385c2395b3d149704d55dd5fc2ae3fdbc03d4406caeb8265de480c0e44b473f3fd324909b6b193917a44883bd36f0a0bb0b6ba7ff41bc41e89e907335fd95e083a7e271476924334c8e0466de2f5aa6c988ebcaea4ddc4fd509801ed2e67d5ecf04813c7f6fd1137e23540ac60b76b98b8982d3c2719ca4a085e0783ce54ff633f68a9b0381cc695f5c40d95f6a0a6f575478ab205314e344f450c02aab52f51c7479c7811098cea8299cabc8a8565ca4d1fb02268b517872b2d088e096bbd43a31464c60ad915484253a00bc7af96a41aadf8c970da171d4e5c02c475b20c340f786ab15f0b010ccca546c35f886a166a18f760c62c4c56544b0b8e890240fb3ee5ba9bc4a3958aa8426d2470a839639c47e43b3722360b8396047d513d05f8770262abebc8866a48eefe958216d8aac11ffb97536642248e7305e170590b89ce868dc94ab2dca8781c45caeeb0b2152af272a46513d0fb2b9c0d7fedc8ab2a1647bd7316d89bb916b5286861af567469aa51c2aff9dfa9d66d0cadcd600509a4f0edc80143765a31fa8d3f759afdc619703fde8fedd0c42a0c07615afe33fae371a36b753c6cf9bc3a0154acb4a1580259cce1b267bc00cc53085f4727f5f9e4b9c1241f90ae5394d7cd5913fd73b469a15828324c4a19386e02df3d0543720b9056b6a2ca0e6a5bd3cb3c571dc46238ce964331536f8b6c0a0c184c0f596e4f06ef9c591e04401a80a905b235e6f620f9db7c69510c18b11904b06723ce0ac1bd6b95932c89014caa8d8e820376207f7d734947bfa29fe3fb059765e199e37172167d722c64aa41d94aa6f186407bf009c3ead6bb4a4a86650e574a49c7b9927e2c828326d4bc7a4a4e9eea44c3058aa55856367f23f20002049e5b44cdc9fe194bc85b091f41af9a329fe849262790f31031dea458d7315509cdc59ea78dc964b14a54d1d9710e9c4926f805f3552e4a4dd1fbc0f5a1968e040c2677448f1da1efa3109bd99eac480591846f07cdc94e6053ec4e460243390581c412e8f1ce61e120168d686010625122c8b056f91482df72d1f22526fd5d0fcf006ac84ee4651f49ac008df27722e2a233bb0d84fbf2c926cc972c79e3d80b984eced18a76a434f8afc3e2116b1802acec478399abee5d32d6f289b1eb2802a057c3ccb05709ebc8109c38e96b5d5f1a42bef320be72ddc5cab6c2202d2d08edc81fc114cea4581ed1794998c47a1ca966b2726bca3f9144ff84c58e0e2002e1a24f5f23bb80e57b35cb8e54a20a6d0d877f3e998103535100833a037eb806d7e78946b1ed5520239d4db9f2df9701326105b97819859086c8b0c0a357b40199084c9aaa5ef6800cc970491aff67037373891c68a6118b8ec4b85c1ae8fb086de59cb343639ef56d68e1e8247b3ac0cbfa3732143fd581c1e1f081c101ba0418a1e8ce18195e3ff58d3d6768689936e9663de7d255e3449b98cf87f781340e0984cb35a296023879a02dc447674d9128a6aa290b5b59ba46d7239c01a88a051537cabea5d565ef2fd665060b2a1d4a4a2b8ed4cbafb160580c04e79f3a6e0196abb0458929cbfe39c19cfaeae06e3d6aa926bc2f773953e5a864e125eeb205f15e25359fc2bc38067d3e347352ee1a61e5d826d476788ea342d2537618480f2950faa492bdb5c9d39ac8f9d0d7a0810e65c880357fc410c0171c23b6ff33095c0285172e90ee271fdba755c15904e20b0fa18c77219d922b0a638eab2dff06be887c89d3eae1fdc9551b0089d5e2d4a225f5cc699c12bb303f845622b96b02e9deb295b1cf53e9454b3bdf59eb2bd4110b5b325e045afc0366d4ce3dd23805755e8fca362e9e7c3e22dce1ea905da5e6e4d736f72d00e5416048412a28ce05a026295ddaaafaf3dc23b9bd7671ebde13f577f3f3dd3a0bb02c377e30c17b0ce9866b51d4fd2ade43f189414faadcdd4f70400a5d64ab716bc03dabb03a400663477a4951cea3e5cb166daaa8cfbd0e4787b741a0d3d18a8b3507c381abde0aa47fbab2d4e1f751145ebde2cc0b5f3b00e46d8173bf83715dac97510adfeb03494108c4b4df00d6c17f2699a82b9f7e4f7430693aba0adfb56ff0b03ad19309679b9598b48217129fc8b0b42d98e6051bdcd60ff5ae59bff6a9ddbde541dd013dd9e5512a8a5c803a53c180518daf0fe21f957d4962114e267362aac186840e4da02098017e7f6e92b9603447b31f19bfcd431533ee8a79be75011f98f3bf079b797d1c5e1db798b57f90898d83f37b85568a4a60a6a300c23be9537cc45b01638360bd5d984f40a7ebe076111463bada60518ccf3054797fc347ed26949104b95e61c0ae970d99131bf7e006e06357a8bf853762d7ca651c45593ae353e00b0db50f14997b6e51ac9155b8eae62d906f45beb3e5d7a86f5872a7c5636924092447810fef8f30372450c7a2dec3f8189a90eb7433974820137b7ee953a2480afa5fef00995dd68d914be35f9cf4f7480ab045615f6e8b5ec798e98815afe30640729d372e67d29bf41d9874b0a5d4d60635d57c4284bdc8959c448f82ac8a0599c54318c0ddc82e85f0c1fb99f9b5f8d31eb4ca304998512305e6566e0b0409f6dc2b66164977391e300fbfe2a610f21896c6f1fdb96daa307ffe3df31c42026f15d01baa992c6ee5c4e2095117b8e3eb9d913c317d0ee38b8fbfbc96b4a803b665f75c6d6245cc0c1a1a16d8d027d55a54e3c179022184141595f412234b06 true +check_ring_signature bedf8da3b5f1dcc46e7f1d5de39335f1ccc1aafbffd1144a1a2edf52317b2bb3 1d4fd17d4d948f4111a0ad0c5f955fd2d08ea1a33dfb94b4213af9d56fdab48f 78 92f60fa7dd1bbb8e70da6c47fc7dd3a4f529cf6c1538f0f0f259b044ebd38f6e b2443aad1712e49dce98f46ab0ee7167bcdcf8a0445714819142c41b61a801c2 c523dd938d2dffbc3f86ad12ba77a4a1d883b1a3da267ff90fd02a9d2e1d7f06 e290067c334d152ed8308199eef5f13067f9349ac19688b3f5e44c0d5d545870 0290941bb76aa7e61fef1814896f4a8cc91b03721cf8f03ae5cd9541534339c3 31ac6967d4d95bf8528897208b03da88caf02b69bfde698af384195eb4feac60 b680c121be31d62ea86f29df56b64b2bf93072f573bfa10cccd620dda2cc8153 15fd881ad2091099fbb928a8f5827646b322b2f2d8dbdc3a427d5dc89daec066 57d62d27e94ce0abe1722f7ac05540c077be251d6ed8c5a13da5b485b97ec778 5e485d56e9f26c92cb0689b8b40ab69a5d2cd3eaa58295ef6f8e51cba57eb9e4 ab8f9ee09932a46c263fd0770db23782576966105729798630362f423c35456f 8f07ada38ca39657a12a9a0ec01faa78a185f2583abd39e3d9e45da9da3b9714 04cc4c15279b93b31ea39919e3a7dfba04084b5eada8a0213d8ad55e48756ef1 1f14a82d0d99777f983ec7cd2cd402a75553711ca9ab765693db808056713761 168dd80e70dfb15cc6aee345e008face567d33a29a316973b66825320530b890 14761edfd72f4a776975ac1a63add7c701379403d8beb8f7fe88689916711aef 077a81529e097e2b3508824c5c1f16d6e7405c185c0bbe428ac44324e5dbfeb8 519ce85faf360b33c11fae6dbb5d58128d5f31efdba38a57a593d913b989c20c 1dca8370656880e682376f5305d6b604dec8070fa2eef318ea92f6c1c8041114 f1ed27d91984a3288a29e213b9d7ab425c3c208b3e10396b0e7e7663480be6b0 d6191fc910c5c2b8fd335e024d9ed25abd8348f91707e78aac4274a460d2dce9 56b692ec64ff8b35f83c7742db9024e7311e5d36d351dc438d02a1ac330957d2 09aba48814843c4817f3cf1cd104c6707b8153e8bc5487764bdfc44a66190b50 8b323002a8d426028aa4c58025069aa0372c197c46aa1233a205decab11952a4 d48fc06ea0f259f345781ad1d017ac9c8409f31f761d9fd091fc330973cc249e dea7a3f2d99ae1f8490a79787bf21c7030ae0f2032d6a3e5ac1b72d066bab2f6 a724280eedeff8d2d60483d8c75eb78717e302fcc4a0b998cb954903c4541e5d b42d8006e1ef703ae80d53304ca005851b3fedc93304249845908dda317a435c bf6d0b7cde5e175f6d4c98542ec211984c327521be8f4cba3bb3680c4b1b3e23 696a155eee8e08a006b06519044a49abf7e3545a746cd9510deb174972d5124a 8c9cb5d56d7527404629feeb4989e798ce85a4649cabab44a10fcb51115349da 724dc7504e2437f1c65f886a4ed2a525fbd4796e20b95fe8c7eeac37c1343219 f3da2011e2f34e3d58d7fcb4eea22052963ad339ee75862aa9f0f6b814c3e4df 66a68f6b4afc49edb499ca2c2e6f5f79c93178eba545f8419cf32389bc1701ff 2edda39d603e442e43baafc5a7d2589b43228448536dc5c37022e5537d77702b 19103afa4aceb846249099f7e077d3bde869eb0fd6ed01916a2ca30e1e3ce020 b0ed0b819bd3c392938bbf722cc02b9b2a5acdfc5f6c8bc57a4b52041ae13a1e 5c5c7631d7d011b6191514832ba05e94f582086481aa3045f261d1a268ef5164 08f25ad2b5ee263b302ab31e3c03a857cba1c58c6a1336abe3d72dc744a1ebc1 5cdc4b94c77ac294cfa671f69e457aaaf0acb2f05378b85f6151afa3384b7db8 4f51aced70fc408f4a322bc738c7a429998d8d6dcd267f9a7b52d51572925f40 b6e37c07d8a9ebefa2b2099c8ba934fbb7eef243645937e0e4cad21f2c5f2db6 858ca0089eab55e6ebeaeb08c0e9c0e2efef93401690bd7fc5aeb4da26cea2df 37d3b487eb065db679b909644f4a04124a348e9ae81daaa672a50ed8afeda1dd a561b5481554b0c4be0e2ba8cbaa1024e6e957a37b495f830d22fcb43ffeb58a 1120a89357c68318e9aa5abacd22f273eaeda88e07d8550e1d576b9b38d3dc7b f031557d3387ddeb5c53276d13e21140657693465b7e2c26c9f65ffa38e20c2e 101a23ab01244e39d36309f6318fac7fbde18669763128951d83be1b1b7b7766 087acd0d71eee939c1ed210fc79ae92672b3359b77395f91a7c4ce18f21ec4e0 c7068707d8b646050920042434b7cd358644c035984520f4ffd6c70bc816ae86 f66bbafe4c95f9fc448b618b266632486774e7ea412c297f9525d85fabe13b8e 941038f2a4af0f289515db22107dbf78d613619dfda4c91c1aa5e54502d04037 a0574b28cc71d8a4c561ed5f32825168c40c136b99eb17f75fd2dfeec3c93868 28407516de438e32d713f9618e09050b075a5f5ee37b785c328e7ef9be9394a3 52ceb970133dafae46e457b488f3bd986496b170d7408f59d54742d68eaf1d91 499578821be5ab57af28827c2bbeca2ba452cafb849eeee43f3189772a15293b 284b617db6d15fd8cd47e7ee22ed136887b1dfd79cba80671b22e068ef35afaa 68eaa7530d673386ec4fc2c1729666b993ecacca1c14c243aed5c51fc1ab0ac3 6efcef8e0b9d587e887b15db994e2d49bd7a56b02876b5abbf7e1044d934703b ebfb762a284027bc0fc83c9f25a20296d39b6ef195da91d5678d7e346ce5a68a dd0ad3df51bc028c37584e23e6bfb74a1a89f5a30bf6c91b77b7cfa5382e6e3b 17d1a394d2f1e20a5b3e4b15bf30c080456b2f79e3e7b64ae3dbf5971fd2ed49 0597e5036200ce231eb19ab2570d64a9eb1186a03d5d9e6dceb3a37a99764262 7b7108d6a9c55ad61fc557ea42f98ffaef80b0734eaa8f2f9356911b6e2a7067 c85e65dd00cb88361faa171a0616e9eb0b88d4e8e44bc7fdd0095fc26f767c17 0c3a88723fa3449856738eeca1d541aa1b6e841cc5f72c8ac8c213a60c4667b5 412a10c309c4638f9009b68e545c19afc6dcb6eec001f873d1aee39fdee5ab60 7a13bf6cdb0229de9ffa5d4549a02560807001eb32bcc7da6bc1f8d7024602d2 ea1817753d764a2a6c5a5f66ba49a937938dcc1e2b6c33ee56da817fb84d64af 291dbea14851a6202b20be02c008caa2a40850f96b715206d62b7d6391ac2796 c03ef25730f1672f626cd66d2eda524f2bd7434f006a31fd764fb5a564693a38 2f94b316836a5d8533d3562bd75693f214ae26c5adfba5733bd6eb6d3231b0eb f1583828073850be803182fb7cb2d29e8cf594d3089dce4ffb4359971fd24362 18164fb5473d8360e24497c7011a2f137a275fd9af1d7eb1123beddc27b00f1d 0585c053f1e18f5064d9f7508be2e557bcaf5bd81e941ccf6180f215733bb4b7 9668dbdfdb5133d48bcc939993f39b546af2507087bbd5348adf30c7b35b044a 81ea6eb29f48bce1dbf7fb0d3a751e00b46c8eb3f679c7a122d835f64e9af13b b9c39692d27f2186e60e3f85a218c98c2edcf0f3f3330aee3370bf4b5e8df991 acb239c5c007da595594cd26be09dae6d1cd068860a93f0114e6ddd18f58ae0f519e77f93d4736dc0f672bf45d7e348b60da403fb0de1fd53becede4cde19b0c511e51788353932363baa14419d41c310052a6a417896b460df95c3a13b54508826245419b509c645dd2da6846347449d0ea474bfd070f25372bc08637c0878b506a0e4bcce0908f381d16339df70681757aea7094918a4dfde5ab1665b58c0aaae37415cf19d097db90859efea8b438ac315a271faba89066289a9ec81abf078c806c75d8d3c4db6438f3f3afbea60c2ff288ba43d2da151bdd12f28324c20341c6d9c5edf1de497e1a5b7d8ebbde28e35f60a1e85ac81741cd5f8332201b08832813399bbe6a90cd68c99c65a4177f8066e4f541725f1683601f965917660066b7021b4398c319743540a30e4288e19a7f428187f3e44f63160911fb90360c5a527cf0cb255e3ebe174241d164f0add8bd5d8b77581f0d2276c824ab4e620f2eda514ad0f4d6bef8416ed2219d2e0f688d31ee442b3f4c4b2bf67a19edfb01c9796e8f6f3f450c58bae47a34bbb41c7191c85a8c75d780e770bc3f624b850f775aed199bb4f1a69d9c068361876c33605ab53a8f69e6cb2b666ef8e39cef05688f91c789243bbaa526f932960dadc7d968a1de78a9b85d57a4fc4c8abe570fbe4a2f150285488c23ea19ff2629ea4274cab5ca0cd8f5ec335dfe4493ef9d029ad695c209c2e21f801e93ae695ba1b63de07adbbd88fd5c4758bbcc0b13990a81f7065a30b2eb76b522d92720c5a3e528aa8b27038d694a98f8a9b43c8acc0f9d5a1c4f90c967b37dfaae3b8401456d96827813bc8048e5312f18378af56c0d95ab6fcc4c0434b7349b4a66e6e1cb78a5c94c5f87c31c0cecdd2393895c9c0f55c1815e766b7371be443b5d3f6ff0da714a7344c964cb574ea31b1229985d09d2235726187f0a0d0a9f2d02293f530a2ef3ce167c763b8d8897b49038ec84054273ad77a82fdfe5fc1cb8f54bfb4bdebee3b56052f9f25b33695b854ed6e60ae548a294eef48be838090912d1d89a22c8424d7ac1745e716161b1c0f38765070decae18c986954cbe9a9b95aa29024c7fe4e570d7b17ceb3ed12c555437a904fd8db0050443019dac019e8cde2f0546d6c254c90ee67b23e508aac7a8e5b5051572683c31ccd4e1247909f2bf48604467ca6ad13477d7ba27c09b006bef55048d4ca9a320caeeafc241ed29cf9bbf85ec47a3aebb93969e36ca7ff4b53cce0234de5379c24dcbef602893b67c946eff83dee4a7895ea9580f9346fa32704c0288e556ef0b08bd843f236ae2ac6b0b8f7a3495f574b908baa8b9602fc2268e0524cbe9be3c9412070c2819b755d7b9588d27d171e31420341973364265b9a60abace88e8c3ec49f2caf0d1e36cf68b14951854a7753ed8c64e5e2c1245007004abf52818ab6e96b67359e8b18123111103004e632ad2cceb2b57a92883e3f70aad9ec8f2f1ef1372c631478e93f9fc33904052d549658f76f22f26709d91e2088e31a454452c092f92f3cb44742d579cab2503ef49365e745ca400bf02da2003ed92de93f3fe8b3b5490af394945a0addfa1bf14a16665e494bb940c47a9d406b37c649cfabb4ef27030f0f65c5502814606a932759f408dd1883db6565b5b0d564170dcadeb2e1f3de53d94c0b1b1debf187aec17bf77aee98f0c939c259b07e383be8022025bafff0559c02b3e47b4f4b3e95ec2e79d9e635f777dce295d03aee637231e1c05f15f4582fcafb82ee5ccf06e3643289b73097741f85d0dcb043a8d26b648e0c07432e9c10a1aa871806bb42e7af4dc0a26608af725e6c4dc0d3b22b0d8249d169b4bdfc938fe4d80afcd863b09ef0e2829c42e560b6428de0941bcd89ba725a61939779d16536e4f421f77a4698577bbb7b5f0c27266701d0d5ec338fc31ca41aae3b72c80e0f13a988cd27d3d8a806411b883051d92d8380e15700b5ecd14a196ca39a333e77b1936434f93f61a6ccdb9e8c91f78e3cdbc07ab236e62ec33d6b7dbbcaad014bd0c5bdba7611d245489b8c0e185f494ef4c07b3024859d2d05fa28e917cf0f97a9c4eeeb999f79459dfb90becf8074fd27b01fcf9b36e978e08ac646f9c2bdd0f97919dcaad5d0edf10d91f32454935c69202330de413fd177621f9053f0d65f13510317cdfd2c1fd2d0f097b183203e6060e27119b057db837a8671cadde47590c87fa7f4a66b9f50d11a7a8d921cfd70f03c3ab4309cd6de9e6761af5e56096a9292215814c09f8d39bee76b5320528e506acf1262652b6b328af3cde6171395b0d008b230221770b7bf8feac02dd45780ae8073a07c3025a64f8beeac94f1b07d431614509196408ab8d8e3a2754f71800bff7313701c94c6881d42db94b9b76a56acdcd7e87ba0bf1f1e5d6d59523700c26516ddd10c3ef68c80bb93a0b692e80b837cb9230e522eae9de340845afba02af2ee9808003488b2f50c5e1e8f00de5c0b9400e938121a7e77c677de93a770c964bdac8ec398ee557760cafbbb143845b698bbdbf243b0ef15b30932ff383061eb9717e3312cf684abea9699082f92cb4e411e0bb2ad4c40c6ec6a3bfdddb06937129e4af8a7179f34ca42c25069ec3bbaddd31cf432242829591ca954d210514482ca351eabf28eb8576d4b03c358f83d944362ba3bf8a3ebee38a68f96a0f95337481bb5bb6451b6f0ec2cd6f31d8e0b49c9e3584644840a825166f5e97074cd9af644f9c46012994d4be6bb73489427e4172ebd2670b219a964241cb170617ec20c68b6054377d7c8836d2319304e3f8526aa86d75f75e93dbdeedb8960a788cbe1bcc3269c2c34f4afa626077044b5be1ad41bf143767b3a6e3f469ba05d6995cd14592cfbc0bc08fac8eb3f881e7e0f9d9445f1d0eacaa3df2c78ff4011c68dca3ca624af5c57bf29a3015ca11b40f3ae6ea4684cd89b48f3374abf10d3f5328632c2f39e17cb56d2b29b9f2a4ae82e224696df5107b846a2b4a511d0258d3b6aca2136b1f663f4ac1b014634878c9fe3b9c6fad36554607bfb7dfd20ac6a262d80d4c1d28fd2f8667feee0e8170c319682a7fd0cf222673e63785f30ca5ba5e0d6ff7ee50bbb46346ef3247c993b0199780e2523e58d4c56866ec1000ad3464f98f86c8bbabcbb06389a4dbe8704d039302761d849b956d471d60ab023af4c0670e5046652b994b3d7ea9dfd5405b1adcca77b3000db864d49cc15905c93298347563a781ee3f4f1209a718582d9aad43d54f01786c64f83e1b6b1a09d4d15c088427885d7fd2b35de80fb600ec03027fb690d96a0bd84998fe98340c2cb18187453df03b3a330025af18a0e6576a2afa996f9781377366fbd81b0f0e4478fc16a5862f207d27ae01f6419285db66becd71e4c5b92ac1847201e3980a9349f15918b080f83d46747f55df092f7166f60d8eb75dda91ffaca62acd510e686f45c8062eec829e06ea71cd00f70a57b003bf307f9ea1400d6fa9fec6c100d463221ddccb169e51f4f4069e3a0df0a5c31b0de40168af8bca07b3ecf3e70d50dfd55284c706b1d25ea8f38c35e246edbd42d372d4afb6bb2b77133061e000c30ec9f0ce00c76d1a38ba35e9c941cbe48a079e73c3bd95c4b478718076e50166769fdb0fec84637a46b39a16b5ec15d7d518fcad449e6fa4e074f1768e410a68dcaae0c295a1c9de67d9cdc915eb4cee34f9a64d980a8c572bea278dfd9c04fc964c795ef4751debb5c544402c7d39b0937345bb7057fb227219a33283ec041d64c18d1a8e2495b003097946bbc7d47eca4ea4d708ef9331dde3c13e5ab50a71367b26fdd38b0fbd5db0ca80db8a82c6950cf1322f9b12858fecd6730f0809d7730e97a26b35865ba2def299fa462847c141676cb44665e374627e4b5753019ac34344e30346ca338348b31408dc239bd79650f230247e70f9f750122990012bebcaed62b884013a386a8e531b32c4e938dca412575e78b5b80e22d7552a022b9670622ca60421acc2ba184c4a7a9c51bae263dd0597c4c27c89fd834efd039a529392500c6e0add53044b13ff2bfb614b3f109d06918ee5e1e9b18bdeeb0e1c9d289a81aff821b50147c96c2f66d755e1a9c24fa99b3d5909bedf9e241b0d376e92ed98f6f62a00220d51b3980dba3c14b248520c07ce46fbfe3a9e836d0159b05924bbcc576953f52564aa2af219aeb8b11806fe3e537e1ef1dcd5aa200807ad589b3e95bace39cb13a70ba63fd4fbeb0bbd88b981398a51a7e817e29f0663f2a342dee88758730fd6531a280cbabb2a2c074254a71c8162d5948ddf330611a5f21f847bbf8276c4b2c3bebd2f316074e55ff50ef7e607d8325bbd1e1a0e8c50278dd346307e204c210a94e09bd0c2c2cf23bf3eb34abc4f25e2928c7d0f472c92757773a2ea84801b536da43660c94cbbd340322485cf7ff5b418d0850356101c73d9c988b438e43adc012230338f7965f571f56dd7255ea56f5816c2042c53b86f78cd0c97da4eb2bbc3b58ac37591690998ad4b19a16c22e54e3a0904ea2f2ff172e0b62e999f240fff10d2ad02951419951461f44bff91ab58416e0f2050df4cc4b599682c8acf3346c8c6d2f293cdd35eff4240ef590e1479aa4905c00ebf8b4b001deb370888d58268d844f7041289157bd7cf96f8c2fbd9ff780a6e508a3d5181ae280133198ef4cd8fa1abd6dd4d651830af83862c29f63b0909274daf432ac961137fc6c5937cdd26dcaedd7e509d2e0820f5911c99bfc64d01b760f0f97d4aaa32f64978734bc879c662e4c1aa11c7deabe028a1684579c905b07f230e1cd7b9e8fb533b87a671436312cd466b9279e5913a689c3673368a0734caae443d308a41ad37a97462ef766ecfd5a3b8a6940ffd349ce01892525806e0bd712d6a39cccc1d45124fdb83964598d763462f3a3e164cbaaed4c7b9d701fec9b122319ff51199d035fe4c1a155e051c2e24e6b26e68607011308a8e5604561dc6059a5dd736b4c013508faefb5b34ccf6eab8bbd03d933c79a8c43317079ac87ea6e76d7ba0fa9ced5075f307c5d7c16a3c67cb8cecbca13af40dac4401124f2c1f5484bffe704571564ec76bcbb2349523852e708536d3ab3dd2fc780a88b182f0b0924f71d5587087bd9a7fcc3c66870fabc3883c2afe7020f88c4f0346ae39a1be50c571396b075588d11339e2581da4063d52b4e5446746823a9c02b73d87edffcd32825dbe300c86bb875f9be91ff5b1d4c26bbb2178a28ca8d905b3c8c75fb032e36d6918e1ad13f24fd1d3a01e7d3d4a83c502fb95ff44a6590884eeb4989e3f553e1ab19fe6a4ea7c5509afd5c429b232a82c1a95dce34c260ebf2424748bea2b4102fed49fd35a99550dca96bdccf1625bd2a471ddbbc95600253b9dae831a4a90373587ae4543da94d43ed71b9f5d89cafb2699e2787d910bcc6ed85f227e46116273174b508cc1958cc72de790a337f1c62ed83a2ce8150a8d3d3317bf398e97762763ffa0ca9f3b76609d6b6ecbaf47f49125742b3ee803dfc7ea795ea142c98001391dcef1cf7dfe4a4a6ecbeb8462072af0f78988a30d02b52a416d28f18a229cfde8e1aa6d3b0ce6db7f288bcc7884dacbe2c5e78402f1377186b633727e0e4fb153f0770004c3cf65c208939fa7d4bc355dfd3f700b6fde8390bdd13c46974b4cb90e01cb22aaea12913c873318b7f1a09e5f496100b4a36f5fdfc6eb321ba57bcda4866c660439d62d8063627a81991d7dac3cd40f29d28f9f46a145c0165bdf576dc70042e782420ae096e4099155fc94baad920945198a78143be5b440c055366f876ab434967cf5f79757c8068679c194424e0f991743e78aa7d2fef830ba5fe4b35422ed989ed29d51900b19fbe79ec1303005e2fbc9903b163ddc4058ba83aaf2752adeaf9c01721efb49f61755e0041ea007afdf7dd646ce8d3a45d4d6e675a5e5734cc68c7bffc514bac4874ce7e811600bdb200f0464f13a016cccf083d9332afdf3976c320473dd8e22057065919fec0beba52f7d71a4031c3fa9d6f8501fa503af3f6f97bef4c36d6d4f52afa0903802c8ad6a1102c10b13fa573e4855f5b28f1600d823e1b4c85f772901498a4bf708f5ab63a373158e8426d820f24fc02ab611f23052bd85e615913439ac0897360b6415417f6358f40338c185f315643429bf7ad912d8d33ae119ab73e3046c7e0b529d01ffb0875aa98a264cf805238fb2484de60e9aac4a83f2e40c9851dbd10f23b9b48e0e31ff41e6643c4095f50377452874da61185a0c46bff15e79e6130858366cdbb946b7a2d473b90fb8ba2189010875bc072856d7d2335c819fdbd605bdb5bbe8d48fb4b49101e22456cb53575369093d9eda6ca558534199a6d9fa06bb7f2179c1219c48b1d51a94f9b0abbe954dd2ee46c48632a4726942cd70c70e289603af7518c60848f52743790d91e4a366e892d3f7e7b5929f60b59acbaf00e9b9c96dffa1493ef352368a5c62dc74df9dd96ee0dbe881ea137893cdfc4e0bb17c47cb20c61f11dba9f9bbe03fad87d5b7db3924f825ad1045a048d0928f09f42ed0a22a52b017403c442c864f141728a2241d92946d052a9c5e1da851270363174db3af5c86e16697e970db5b9f377e86ca0ab81f9764036e9f91da6d760fcc80a7880e261dc491476768953ff582c2877ccbffbe2c4102243ad9548d1c0a0ae469053850a50c0c955d133d0aa0ea1f73f3fc110d37eef4b9354353e8490a4cc3559dae193f1ebecdaed305a21de250ba4ae21b9b942e89eec5904b9a2c097a9b0191712ca0b26106ab32787d8d1f11956c63b4683759497e1e0f7dcd7d0364aff7f6325eafd6c234384b06bc6c6b9ff6ed19630c91d8db0ddd29f74d9a08f8643a17d64005a14ae1f769bd3a7b6cb9cf236e422b4518d1f21b7a12525b0a839c403310f87b5689ad0d388f03455504cb2040d73c370343376f0983153e04583fcfcf8a6f57bfe5b1b66c98d6ee3dae64a6f3e71d30a2b77dc71f11a0ea0a false +check_ring_signature c090430e097fe351f26ad9145cdfa365cf4366f92696a5dc3bf9ffd230d1bfdd e6a99a9e418862123323015be1eb309376608dbe4668e22ee3cca6edf5b1cc56 93 58e9ca491322f4cb2bccdb57fc7ddc69ca1da1a425c6389b345a317c21a9c4af 000de10fbb87a7969c711f9706f9ed8a3c1451e44bbd26bf099e28ed28aa3c4a 0d28c672976f70226180dd5617a69da6c0604cb1900df8ef9c7307e2cb9e545d ab7a3e989e55931d0c9d7ee14243cfec567ff995152c652325d7863c7e68435d 7c2d0fc71a36c55331d33a23179387af919336eb6518ec0b6780a1afaf97de50 2536136c9bbfb8923ec417b1e35867186c8fe154a97ce93fded3a14b69c7cd34 e2f3db6f2a5b82260290d6e5d1e603abc077e2438929ee1df378a3decc6bae50 8f23c72db8102f6ef883dcd383673df0a22fbe78e8b18226daeb19a67a381bb7 d9449d52a97f7f2d0c210949e0190937cca30840d9960159343ce1bdb61a3346 371d5b415efad927bf9aae46a32c6a7133767a914fe72a35ed426693d5d79f0c 798e7d97834d9934d6a025050183f9a3acc19dc46cdfbd873b0c3d8a4e8b4a78 4774f700becca8224a06d41641fb5c8b25346d3ced234b752f50a7ba84ce54cd ed33f93b9bdc70ae9e10192ac18814e0b1adb8c75f240ee91f5805fbe22cc418 964597085ef88bf341fd2a3495f6707d57ba91b1c167f41de65910f3f6faef8c 2a4014fa5fc000ab0e362f12e937d881895402a30b201647f093a04239b892de 39f58d4211f345edb8961c75f270ba3c1cbb25688f0f1da3731e6225fe9488da 8bdf9ab1e865a4cf1cc8315ecef8bcf779f35542d2bef13408949347b9156c89 ff430a381f90507af9018879106299b952644e4d1df2aa70796c8e1bb68f8b94 e4f3004f640d5cc77bc1686ee359658d13c917f9dd8c3282ebdcda7e79d2d53c f0a498823212bfca85b3be32e63ec7c18b7137bd25cf018eb397c380e1d93656 752098df6d9a178f8d89626c856d87e7f83bf4575248b1fffd39481f7172e861 6ace1628385fab21522b7af864892a956833c3379b8d19411abe12c1045cf1b5 202a84f9e68aa30569c9dfe9958109b479a98b5259a87a0e44411d96326b2aca a4a30b9fca74d65fd0c67c37019457cb45d259f8c09d46b198157fda4699f3fe 8d764030713f90cf69657f5b52985678e51367915bfc0b7475979bb512fc7ebf df4ce264cbac4e38b1c65b1f7e8e3349e4848f037e0456b22bdff89fe9c527bc 75bab3a4626d381c40e5ea824c25b9bba7fa69e01f36c12ba618c50006134413 a8ce18b387c8cac6fe0f0a16ff5e98bf97758e9cabae69f4cb2179aa0beda406 ff98cef044d1facbe9680380e4dc8183a20b779e156114d6b89ef8a90657c813 fcdb10c139cd4393f695f27ac3398735c3d0aafcea6e006ff9741d0838ca4908 f6e562cca677f98c0dc19d1aa9446b63da88c9b919437112dfc0487c36724c9f a0ff47779288e455bb40500f04fb7ffc384cb75ea16d8f5484faa031c2ad7c0d c3208fd7145d104a95ba9c3da142796b82a88c9c6e446382a5a0fcbd22fefb5c ec687d174dc5083268b77ae445e363f74f7a23652be11077246b32b77d9cd6be eb7f27d913319064d55146829654d18fb8db01139c7ab114437c90c866de2126 8caaadca5b842a11c0e903c0e5719e6f0dc194f7f055f7eaa10fcaa0fc9ac13e 45e8629189fca96a24d6663a89a952be010dfe7a93cc95e2ad3de00fbee1337a 6c84faef03ab995907e22cc28f964d1af49fb6669d5170c15ad2d48c47ef9ac8 a2210956786d3f30c5742072dcee9d99a093f3a52ba04125841583b4804ecd03 992207e47e38b863d80df90230a0437462770fc7689bc24324e29a4500467cbb 9fd900a3c0c0c728e9fe5d32fb99bc43918200a78008aa5e46e6302361e31558 98fd974705502b4f4d678289974ce57dc3c01b4c051381da077c3ae85b409fcf fc2b572abce412dc66711f5fae4dd19e64ffcb88f27b0dffd547cf1f146398a0 66f0e555216ead374957d2cd5e2c851bc719dbd0855952d24370d5b1f96356c6 b254b4a13a50cad88242438b76e02e482de466b0efc7724c40fa012f17a94a18 bf8f10b32fb2f1361f848cdac2055dc6b8d37b0c743d9fa76c694321e6f53cad ea1703480e151b72835ccbfe9778696c8b4e8be3c8ac4e38764f41d5078a36b1 286fef54f338dc0a01e7fc3aaecf42cc9275d3957d062844617c3c3cdd1f5989 e9c2b9b6e0d8df5d7df0d1966f523fa154a9a27939ef885aa32802705940bc76 8c4e5df082983644030dfc4d13695ffdcf1eac3e2c959b6d9a5802c529a95a1d ddecff53d04b05124045191220665b59ac141f80684296951754ae7e46197309 f6c522f461d654f9e71afd30843278582ada1b71c10c18ebebef96af372e2a53 59c5596d7e9ba1bf1784043db03df63a839818c03f4765301fa42eb328f32864 67903ef5519fb7dbc9066c64151f26c25bbb58c8e82791e9a2ef17d48d2c6f7a 3ef1bfe01c97a7f5668ec530527183d6af4ccb92c0223d1403d186c571a31b3b 70ec5d6d97df8f9eb2b1eb76609238b64619d40392ab82266261d58f08127f05 1e08135a4bce679f79ac813a3e1d0809b9205c91ac64fa9842c69062ccef61fe 7442a4b391c5f0b17c4a906b0dfc34bd86dbf446758711bae5e50ca076012171 d5a5de638dc8d9a2ed053b247b9a96ede22de7820ab55ddfe294945f81353d70 10a52e9325ceae223f959318b7bef87486c6c8c8974d227d1c27fab0f5806fd4 eccf477b96eb3dcd52d91ce3d425faddf8db70a12ff7c6c168ad26c4ce37600a 7236119804924af2f19088ec835bf1aceefad2df3e678a41e052190e11aa2ea0 a88cb769163141e7eb81d0dbc033cd4d5616f7fc66950dcf35a96d6344355b7b f8a18d08538ca83ccd656c1871bee4a1eaeee40bbf92ff0ded52eace9896d7e0 611b77bd3fcfd52148eecd86ce8eecc437f0c3c2c057b84ebcf0e5353232a459 f2018befe29182b2a528721070140853b602b1ec8867a5e786b106ded1e19ac1 bf33153faf4af8ef4895140c75c5f8af4213af3b244c7b31485563d029162fb1 b5a563cc34267514cb48999b2fd93fb580fd95386059cb466625445311ff2aee 2c909fbb35888bf716ba825d0080f6fd3c47aa8cdad778d0b34b25a87e9b6eff b2341439565e7133571ca5df3f65816bb3bd8ba964450c6f4608bd4e912b3a65 fce69e0db7953bf617095532f8bd4629e201625368a37c2288a0383eb82b4b5b 656afe6046daf711811bc31f304a9f4ce98eefa51424d2d59fb4e37a8531b529 363812f441aba85386b952fbfdae73b65d53eb435039d8a81e9dcea0d13f3934 9a90a1bbe8871dbc828f6010ca048da46f06613b459229d413aaff7d376ba925 f1d43931f992baf4fc6e5d11e7cf0cd7aa5f13e7a8815f4cdfb8406879e3c954 943560e6742620e4cfb999f698aaf89db16a2bf718588f2ddfec884ea8807a10 946bc9d991b41a28b3ca21de68c6e0099b2939de63a658a243b5915b0f93b6e7 fdf9145b5b77677422794ca66ef39accf3d105d8515b48cb5cdee410e4aca360 8baa5d9ebedd6551cb91ad1dc25c6ad97ac4ee77ffb170c3329f1db95ed356c4 ffd3800f8223bfca2e15e4dcdece9802e9321aafdeef26d0907de27d4604306e f9719e2704a476a8b75a07180374adcf7aaecbd280569019430092c25a0d6296 9ced966fdd7797382e0cb1204e180addf34b5721137349e1382d2737b3e82ac4 75e4ed0cbfbe57836aa30188eb85e33022fb6c697876a0a1d79db2d4651ba23a 24f54c881735513562a0183a27fc885e2395f45edc416c129c7f3aed64bbbe91 64ccf38e1a0319ee7d4733f2f10ffdfd0f94335259f843e633a8fde53828e75f d801346af2eec816173fc4ecaf30329b3f864884345c1469a45f966cdaf72699 8b964709d3b199de9426f641082c6af267dc2607e1a41127ee15480998ebc83f 0343c3b23ead24802faf8ae601ea656756a79271b6f8de6bcbfc14ccd2e212ea 79cd64fb99075beedc00150d341e4993c0cad9c7d9c1b4f9d509848ce9e8be19 d0ee417438f9c157bf024910bf45fabfaedeb75f62deb566ffbea0ebbe6b4ea3 2525e5c2fa31c9e7845acdd51ce15feaed9c2fcac50f0a8e3ff3594755861aae 73cdaebca40d57e8b5b8eb73051451212998e2860e8013da85be0ec48b7049b2 ad9cab0a211c8f923ce3f3f30d419cd793ce570008dbfe4107724a3dd814324a f05d5a5f12994001759ca621c3e2b4454d6465e88a790c22b9411d0248f3ef0f291951a76c7897acfbbee9b71eea0c51926b16d6670a60f8839d0fa20fb4160a285552a24c45802882b7d142f93a3cd97bd52846719289c97d3f0ff05e57bb022474f76671c89e2b3bbf55906e6b26734946eb16bc074995fccfbce238894408ec0b8ba7e887399ac3fe046933d0b7ad9402fbe0ccc0e83e119542432b72d00bf2ed536002f0ff881eeb45ea910848d3924e2c6fa2886233839562ce9a69d40ca2c4b9cf7db3d4eaa2061ba644b427fb92e2c67d3a61a69152d984d57fa23b07711be35334466b6d3567cd4f5d089def188732b4d11724252aa2518bf1f42c01c6a99bb9ab1d42e177d01ea09f12b69850c9cf2e842236827760a42489530b0fb33bdac7a0e16df0e4cd4cd694be9ce12c5b3d6e80e2b954731140a223c0c90a08276b7009075b1791a8c2aee2a089c0987b364a4cd2d8e1588d85fd611d90049ebc03c2c9c0cfd6f6ebd14b3dd4e2841a302ffed9c50fb0002eaecf643b2f05a1c4186a4b4ece0e934e1600cccccc65e1232cfc99ca7964e3deec02e8f46b07d4b78767b1eba258f4c3b5c4cbbbadbb0ac8a0664c44ea190364b592c249e30fd44b79cc47636772c16ad34e3367ad277cec768c4a3a03b8097e5b1c05e47a0c26e4c444e68d856f424cc8620bf421a363a98cbf261d08d5a07c7aeb94f58e0481436087055edc0c86b38a81cdb959761fd89085f79979523b0d89c781972701bf7da316e9853dbe1df322c01f2353bec9383ee52739ed5afd161bbf03c20f080b567254209bcc60a38378128632d78c39bb2288be64824beeb9c4822e99020f7c447742664b016cd3cea6a08fc13e26e20a1cea1ee1c7d8fae7f2c997d7e60ccee7dc88f0a731179956cee6217779ffe3b5e19a319b83e88e2afb3fd4d93e0cee229b15e0ab2ea8353e85ec2a76d2e1beeef012f58d8aa04bb95c3485588c00cca72c7129c1cd493f7f625998e245132dd1ad626b117e55b4854e69335a26060ee5bccf7303a1a497f20da32a7abddb7f002f94de19527e5911de3003bd72031ade483f8e663a71fac92e808573e61a49ae576763ec8a939406f5884bb2c202e35f93220de2fd9d49908691d87b5ca889e763e741989eef30ac224688202f0c0411194f9642e57cac996dd40fbac45401644f02d7e09c6a592079de76ae600ab94364faf561773df23a9966689a41c94ab85d2010cb0824201651e9a70ec10633a86de11cc97f3369fa6972f72965c421ce71ad3651ec235388b2473ab17303975830315c67d38ad048f94bd426b09e90de3c08db1cc14782ae8518d33a8401f0c4effe0c69c2b5c75a4189901ef5ed0ea6f912250a8a3d063eb465510e840f963ba5448e4c732da5e76ae29b38ffda3ed81841d34ff6791a51e268f9ce1b05d1433de0676f4c5cd3ef8fb17470da8b0c12526d1f8bb9bc05e1d1c1f2cf5c0e0bc37aae029b533c35c9b49a4695e9098a4c11b052155805be7b50860309010316267dbcf1c36184a8619af79338514897d63dc3d6a161486542e8e588a3e103ecfaef142357e13ee7a9033dd486ef646d47e54ea2416c1aaf57dd4b7b3bfc0ca68b28a4a93f9b32f73681484db305e09b5620358bab760bb9e8b84fb984940fb2d2c868e20e6e19465132732d53eeeab83110be8be8de3952604b4ea03bc70b3f91d4cd8863fdd2d26b243684d76348cc48794d04795d528ee5a811b25bb2078b6b9d6254db0bacf7dfc687533b8bc04aeb59ec2393f598cde438f280fb5b0c32e9cf574fdb586ab2704ed2a988a347a4be5c7832b147e9a286893308cd06081950cab143d8ad69170c77f8782989c46a0a6ac56fe590f0e9b20edf0238380214f18ccb364cd6848dce265b191b71f35c9fa11285993f471c3346413b5d560468ab62bd8875e0969e9262b8c40b5a7e6e9c03b811c39c6e1cf8e0072379e30a5f0c0d2e85e530148f3104608cb582208b1a0f639798b4f0f6b64c531c1d59005a72ec2f126024ef2af550f3a5deb6fe221be17057153776eb1d69f2b2428f080ce4f87f56de61c3997751e356f8175ec57d71f666e07503ec6d6eeef446f00a042f1dd79b42175b620f009c65089ec0d39e56ce5acdfb7b342b4d1f3eb203064ae374836dcc30b66e54989a35a66a1d77bd207e7273938068a4dae019931402b6437e35a9fec7dc44a4903030c62adf0b086855d1d5ca35609bb089cb8f88033809d7ac4162fc54592535a488790a90547436acc6a3f86fa244a0af08da090de623cfcf50bd54093999a462e0ccbe71b7ba65e9d7d567e2fb1fac79cb4a4a04e829ac0b3b532cb74acd169792e0ddb625a2c2ceabf26dce07b42ad457cbd501033feb5e406e14c6bd9eccb8c3267d07b86f8cb22fdf14bfdc73bf19dbe3ad0449ea6c03a58ff67df328ebdc448b54dd225b441291b74c465dc407ee10951f0d57efe42f3dd5d29e14543110d7790ab05338781cc5cc860926da88553370110969c30a40aae54159a5bd8bcd2533d30cef8b3827bf68873256d2dd51edddd0041cd4db12acbd21a1a0f2a193314df4166989bf4f5be27baae36b7f3109db670c2adde55282e3bba1d35ab46b0770fbd7da0aa60cf449a6564b3bfa0898872c0d7425b59278ebe0e51b775c7d41e03b6deb3fc76233b475399e05d1cd4b920204e1e27e6718075ea5c4db3e94f45702dc24e8aaf2258b913d4ea3bb78832f0e07986db22ba12b6245ed016598f50aedd5a0f886b30b9e15d7df6c267f8055a409ab9458c339b12e59d4cff2e89e79a31513d0bae1740fa2c91add7f36a672d50ee44a1479784161cdf9e7a733a4bd6f04d1a3ed9250aad9cacccc844d16ba200a1830eed5c267334910a9f4c4d0ad032a54776ec81ba3c5e0f7e2ef6f7e0b0b0bafa5e48a678f463af47533e4c82937ac7d309929cc760589920bc4ccdb80040d1cd7a3f0d5d7e94d7271fbd8e592d0e6359d41b8abe8221d7a29235ac5f2b00ac7b1f5a5b4a5413e4f7f47bbefb79fd13e84211f88151317e5635a7cdba9aaa3e6009ad06c2b6f10ce0508b58ef7634b9b7d15648152523611e3e41b9f87ad078fe25787a650c520c10d1eaa7f0440cbda9ef9f188d7b6f67533a3cb8cd97b016e852e25e24214060b1fa2cf21464870a3b9e5bce8bea17b22425900f955b705ef6235b66578216f815d3e0277667d62e6a51500ee960f4c757e613670836a0077489ec5ca365a5382f29d1080f06b2ca0b22f7a98ab647f5ac3d0fb0333350c5bc634910db5a3a28dbef39b5f892efca6a2bd7ee849d169f46b2e3c67cf5201b76aed7658285dddd147124685726d017aa1ea7c03d245499ee0d278f4a0f50e202d3789e736eb2f62c3ab8548b1c5878421991cba8e2bcd529aca8574159b00908ef255a30c0bc98575856aabe5dd48e99cc2af80507b4cdf0492dd203fdc04663dfd9fd90d70394cd8346ad3c1896ed2221ddab6feb2a1f4beabe763a43a076c463717ad870e56f8f2537347dab378d59528899af45202796b50105451330d3f5a81a888963a478d2b0029ebeb1d49f4ee15e8498aab9dae529639921c0a09e11ce5f9a4fd9281fdd1eaa0d7ed89a534720a732e77521ddf19d5bca156ea0a1d3b86e1ab1f3fb53420a15178e75ddb21cb6d7ff0a2bb8ec2bc844728a41f091a8cbaea32eb9fd82413cb1f215aef5d12a5807a0208696f0d8425df98c59e094c49224b8d525b7d2b8e43d199ad54144af205d919aa69f53b63a8bedd2cf90a6427ad0b7e7121bb7f21fac32eee5aee6c8f6467026a491174db57eb3e768505047814d01b6e0092a3d1029abb1ee688d2570de0b526ad03ba7b7df7e4658807dd9bf8357e11c4e6bf1b43030faf49814066990017514f7de3376d0be470880c4ef831f354e952fd2d08916eaaf580200f6db4dc78b6d7d0e875f500aed4c60e04262b5b26b8473439fbb1ed55c3643556b890293df482fb7f69117c34bf49075dad156dc5951741333bfaa6d8d9564955c54ef44dee047183ac697ac036b40da57bb680a77f8c1091e214d140aea31312061ffafbee639883455c3c3c25e806672b3e754b0ed274b0e112f0c0d924f59e59c351e6728e97c864231872979107420b44e67c378eda98bbb8de4728a2a21a858ebee0218e615221f3202f970209113e64b8eb8b164bc417e0c35ece5a187ff3d0545535cb67d8fc68459a062a0abacc58a235a7502fdbea523dc8e7ebd16a93dfb11764f3db7f94dd1bc97886083083408325e771adbfba2519da6c907cfa2845088df56f2b56226d350dde67019883271f2584eca392f788bb1a37c07dc0eaf5053758e3de8114672502d3d8044f44c0e4845119c300642ad9bf71976b0ca211a188390ee76da743063c6f3e0f7e64c3f3fe9c3bc28befcb3dd697653b2c108c179b12d2003d6ab5230cd3300eeda9715d4915b47a0ecb6a9f3c24426db6a353c3c275369489a3462d0773e20e00831f521781a7e9808c0b2c27ae728dbceb31bd114e6ca2976dfe18b02c200ab37cb68f18e3246ff72c882eebdf8f1a7459eb8ef87f99ce5e0db408b85ef00f663f2636d72b9b628e8fdf5b96340c04ff8b7efa19c3770b8b2091e351026300fc96463e2bf5fc27ae4d36d1e0925bf5fd53c8bdb08525099587edc1db7d9401abd30547f3382dd7ba3845f4596351740ef88ba39a37e7c70dd5a774e52bf704f2600f2b17f42f945572f94c699fd8b209f81ef691b95e24ed08af630c855b01c97928914df82bc9ccf5920dc567ae0e21745ca2a5d39797dc6d9fa005eb530892fc5f469c313175345f5bddc43c842ad023d634e86d51556706261fa610bd03147865a7cdbfdff4b59a77b9b358aecda39f9f14c36bc8ee2038270181cfa2061ddad976400b9c6b671dd11b5599434d0d4f4f33283665d7441138bc1a4aa40b613533d88d70f9b8905d6715507f59b2072a3f4d90c210d53d4a732dbb3cf9029be85278520943b8c36b26b95ed4ef8c04114b3e74c39d09a936ec692210dd06f310737470d1dfb839f4cf9ad6adf5b6f438bdb91abc73a8f6e7ec6419bc90069ec27f611262c5900907d895cddfd6d945e9c265e8b130c0c44ec1ae1031b1009d082eb983b8faa12ff32a644de4de3e471f9406ab8bd3a95f00f60b09514d0d5ebe586209a7e892635eeee7b6374b809b89705a94b7d4b97c60728a294655049a3f8553815424b73c57a81aa088865191b9dd54f00f6d021b6370111a6ab00326093ebc258c81cf7335371e5321837ce2c037a50c81b11893bd0a0351ec700d255971de464d11c30ce5690dbfc26fc672360871853ea8c7e5f993fa98da6f0af4afabaf6207cdacf56ed74527f849f5df697d10064bfe18fbf3e2e7f5cd09071244638dea6db5ae518dee7cb23ccd2020567f49428c34d995e4f4ffb27317025c640a3a4224a871ba3bda3dac30dd0b14b940b8f6f1200e6e0d857c94b0740f44f293dca1661a24f5fff0ab516463abd91c3a3566f1ae7cfa2a80089a108a064ae7fc20f3d19ad86d618dbcc1ee928503f343db4fc872c4a7d198ad390862009a02a40eeaa47380b6f33202373f70c381ae6afda081cb5bdfb38bddfac4860a6d620f7fd6a8d10575999ea7cdf0c7bf312ccf8d803e94dbdcf90de7f36d460c48169f5ee8a25b91d9e24e39b678d247b18e444a4bd85f8205b2ed9ad0d439009dc617e13973c2f4d411e59682fc3fd3fd63cc326b21a80327c460bdc6aeff02df58cece14ff5f6f0c12df7399f0d777d60cae4c9c2d9e158faf46319477b00f7ac13c23678f832e18f22fe139a58c6d65c1a80186bb4d0ee76d8ee51c810c028aa5fd8801084e7fc2ab2927e9a674c0cbb8b933ba4e2c6e82cb0661b5818208b8786a6cd5f1f6e0fa089db108065f3fa6d5a808fd07896924ba5a0ac0f3df03b5b9b1d7211eaa481f267baf355f9d9afbbbbb5dd9712a7b0785358115440008b888b291e1ff7cc748266ce9bde76158eca541d989690e20a150a3b43301950c58d1ddcae278179a97d1f5747c7190233df0ac02b3f01b002eac8da932aad50781b140cedd7859308df73d5b93f5ac4dc791bdf3239493dcc5ef7d54fb01630ac1083e9dbc61f97d0adeb3d775a7c6f9431323d2562862000da354d18375af0ea18edd97180560fd134e7bea0023bd29ccbc660bd256f8ba9c39d4cbe87bf50520f05bbce115ae1cea89e1d58a5587396938aa7db48ccfbb24ebc633085cd106ae2dbd6f6f52a9faac6a2a26a287426d97ba292619e6dabb964f28868b791f003346f7b5c1237093e4c3b5a4de75f90f0225a447194a0d64cc8be82148844b09be66a9b064e2b0694f9aebf555c42e0804459c814c4eac245f18d8afafbf5c0c53202f46fd7f4d258f762d8a781c474a0a9691ac34ad6ad86374900a4c67a90e818634e0314277c69e718304a1efb9e71109ac12610ca67a04c3af5b8e2cd80efff884022c08e7297c1fb443e900d2c88d120cfcaa3c65f672d121833e51da06ec8ee7fec700be25aa597974d96d38f8bcf78e89d5050d275fa6314308d76b09f095c895140870389134a00dad50dfa23601c70bcc9280e294bf08fe74278b0fcb49b8687bea4e3013aabde8d9e7965d84c42c69142489239f4c68c7ab907c0b0c6eef2e92687f216f641d5b622cbacb0ee36b3e722cbd76cb26f6cce5098e03993bd2295ae5bf5a4328c1bec9423226e7ed4296435db9bffd2dbc25ad59d20a0fefab5f3adb0d7d311b3499e1545dab9c1e246c94e83dfeb0b21d08e24feb0bc334c8b04f44e0a5c6d4e7f0a74aa255262444c362cc94ba446b7bcad7a4ba0b4c1b52616d9ba9a4d82a1de723a0c5fb321b1e8b8b0d1287a33775a8adca80019477eea316b0beb880a90c943f7a6cdd7db4dacab2160f3a23eb0ec2041d460c7c4cd6f5aacc380936d96fdc1c61f690fb1134bc60fa30dedcb241f15df6860a0844d6d26178f0b92da6e9a3498d95b18a16e9a8d3f1948a685850542a7afa058d5845e9d6ea9c08c58cb34ddf0b4ab0bbf1a9ad5773a2e443a2b965088fb402f85f4f729d5eddf28b2d9d7c82f0f5734fdf26d9ae1815f40a1ec7f67f1bad031036add3e7cb17fd3ae7c170dd0ecd5b7834329eef74230ecf65294dad30980f4d8e56ee379c82c7aca56789eba2c39753c9cc49cff147964a8f3e19efa0ae0c131b4d62a538813187a9dc534347ed9f13ab1f204d61357f1dc38e6e2d8fcc0567de1d10d1210f2d96bd26727161b7c540c6086421f08bb390aec95a4d664a08603ff841ec061513de1b619b4317f81faf97a95263143308bbae86efc4174d056322a7b6ab159dcddb8fd7c22635ef7708fa88adaa69a9e8dfa7a1ef94d66a07d28f548b365b09f768f1dfc4daf9297ccf59d61f67a8c68245abb3641f92010913df0b0b21158e8ea7b07eec5ad46b56b5c41d0e7e37660df22205f74e4197081411b4da4e2c6ab14679538920deaf34bfa0c3df7d1828bb4b8c8293a13a6c0e447884252b861bb3c81656bbc5b5cacf652527b76eb9d7ec4bb64ff14c19920098cdb8b459879acb312e1ce2046890f3454f4d5af015e00bd83beee4d1dce609c5ee6c2e53224dd33160785b7392a500250f14bf383033a88b30459827c1a90ae59c44866de6ea5d00119c301980053dfcca7bbae8f5913c98531207ecbfb403a5148e16016c81cfa81d264161fa080a7e7d7ae240dee48c4ce9c1ce00170705281bcd196bcf30ec946280c7547e28d7d522b0c31574946a87f86d2b62f43c086c1df6faf6465b3e7bfc60de5560bb3ad92ace9e7ca93444a89c39c68d8c1d027f5745fb6601c29a440c5a665eb57fe3b814a5e587035b21270db49df60e9903476fde0278f977811fb590445ce49cc4afdae859ee97b9bc93e08649b0270e0429223bfbc35bf25f3a0a1edfe158b64d20ee7f91174843a0b4adf07b31e486013f3f172155b4d1d681be697553a1295cc74e0dede9f30b45fd3e870d70467a0cc1c777767faa9010816e0928eb41df135cee1da0e952d0ef9dcea750a429e803023b931e51d869632ab5ce9e17ecaddacfb0b9786f2e3aa3abe9efbc8ca086026c2c81db2003a9498bfb39fb659541786df456d6834ab7cfdd985b6cb8e8ea01ac2f332e33743e74269db18dd75c47dccd1854974e6dbde5d2a8a901720466062160e8937f96358a34b1b6c5f428e1d7d88068694120cb04528317a9b553db0d8ca2854445521d846048ce069192ade127d06a38e4cb17faa0c394fb848a050a177515a123ab0416dff4248887e248ad515a8106b59f9388e4ac7f449ba24c05a9e4713b0081922b9c32ab607d077a8d4855d913a0430181af74c9fe0d029a0c false +check_ring_signature 74806849107ab796246119a358b7facdaf2e9ed11f37322701195e19766ab7fd 2a95b3e2826c43b97af546d71b20a84785c6946f01220150d91915f87b9011a6 58 641649760c9023e80a7526540b536c49dafbabb957458a5c3efc08ae17955658 f33f84b32d1d448b43b1173ace73e0b73040a818c4079e126f504b0cab843885 f9483f99451818533308f61ac13af461e126f53fc02fcfd4a206acde3dc537ca 9b8ccb548687a4a8ad46219a31fb2287a928c5abdd266988a3f54f9b081c3177 5f432c685eb3a61c3ad22b4455f4d1e5c9743e5649fb3876d8907bf33854955e 17d1471e1046c950dbeda59d0b84e43bfa9e3e039ef9e8b690940199fc98a38b 334ae96fd28daf400a58e136ccc0a3b1078918307587c23d99fa4660605871d5 9930e45834f7bc3f1afe35aa300f43f924e51008820c03f262ec680afca3cb51 10f3cf5ad7d8fbdd5d7db9fed7766f72725360e7b6e51b7b8d235b8af4619d9e 078b56181c5683199d1154d316a31fd533fd3e4363cf90c980b9a8ba93471065 071b1b2763c25c7ac77814d4f2b104dc54ae0340b10758729a553230135cfa29 b4762093092f32168228f9af60f09fa34a1c24b52c74390f64f0ba0a0541febb cd4b9cbe7d7703d97136bc79e5de63e0066d8364de154ab57eafd91131a88797 11cb10cd89abb263935a61eaba4d1985fcb8135eafba75552f85faaf118c68db ac66ead4ce273c0529a4e5a900493e1d842aefd0de52e30376591a2d856a963a 755b335a68953ca018cced152106c1d1dd1e17c66d93ab22aca7df088f520155 dc7d16814a01cab355a8cd43b51157eba2cc743b95ffe0462f31925886961ba9 d2ab85831e45c5c535e3d88bc1247d3f5064e40ab50a96ccb29f4ee62ceadc18 a0f8070a8122da73dd12db7584021f16e18c73c0865371a67a40fb7a5029c78f 170cb040462b9aa23517f59a819eaf107224b1cdbff78211728b51da770b63e5 949cfdddf0f1c026499e4db700fc04dfc66f299386a7e0ec76e2a6eae1050f0e b51d85caf77f2546a320c7320a47d6b34e1574e5c89fe51d5d74fbdef608a664 ccaa3046d6d5de6c1ed3007224f7f2cd548274687304b4fec304940a47b21348 d7a5c395887ea0bcb760867aa99b155aa09d4c1a56af0185e2c132214cd484bc d5a16fead788cb2cff96fb0663e895eb42bcaf6f79dd6a5e1054282b505a00aa 6057e218051596d45d090cd282c46cbd7e4f31e2902853836cc5df76b1888dde b0311eb6e52def273b0026b613fa35cc9732a245512e59b7744db22949740069 f660cf1cca9e89deb2601dd4b0bd750e75d74391b37b2aa19fc751661d482ad4 46ca9a25c98881fb52887cba4930c296d97383aa66538c647cb1d32468004a07 198bcf469a8ba8f27dcb37dbb6cb24e7c924b2b39a826aa2b8bd165007a3550b e6db3dbe9f10f67b18a7579176ece8e565fd388c3e7e5880fd2d2fcf0563233f aa0d86d7e32ae9da6a871eb452a63d4bd22c5df47120d7df6a56caf328557784 d67fccbc6292a09540e12c82b753d32e2d7ee4bdea9bf77c8cfa40d8abac5a3f 2fbbfefae04a21483a346c7646163efe20c22144606b95616ec8d3e3c51d3798 ea538154bf4ca5ede647a925a85fbc07e743c81cab9371857dbe2a360a99af07 04b541317f0c0075d95b3765dfa086e5274a55f9b0f3c9bfff11ed60b5c65539 4bae743929180885f161048c6a7ec7108cd52428532ae7c2a9449b0bea524ed9 1148b3edf3b8924e48ea18d2f7bda0b0fa333e4c34735bd16d7585eec45c2703 4e3cc5d23ca5c7ee5fc662be3b79365430cc5e292807bb187921c0226ff1f68d 543fada25949cbc8d13cdedb3040fa76c2d7bb33ca11b9631e3eab9156193395 5ae1caaede62d21a9a188e33b1203332aefc6e0fed9f45e136c3cdb23f300384 f17162855269070adc6f29072501ca8944e73a4e44c5951b8c5e6bfa278ab15e 10f02c8bd56fcef167d181523292465d0a046855480ca9833fc8a6e5c211917e e18a70c1b46c60a13da4ac9dc74b2a938a1f07260ccc9cb9a1ecc800f9d7e66f 4a6c46f36d6cb5c49cae0e8c09a0f94a5266dddab8edfe770c4b21147710734b d30b584cbe1f6ff19c01836bcf0e31615c6ff5e2725e66657b4dc225133e724e b95a38403a3f70f2f7555c718511d785f218d06dff72e646c4e0b3c316cca4df 137c78303fa04dc53c2550e402e6d75495eb7c8d2cc8ca222acbca09b91d38c0 affa10aaba94a3035632a084cd5e0a0fc9cf21efd17401fa24184098e1589ac8 b0e9c4c3dc27984b605e0d994afb2aca8c95f8aca7a300aa1054f9efe81caca6 cb0d3151465122e90db624e89d1efac1388666f2f3ccd3b2725ecf70f9d4a023 26d7cff01a94120be9e65d9e7a34ab6e95357f3a7952c29677f7d84459c97a57 24f4f7879fde89042bcbae46028aa91a449b98ff08f5e060e3e31ac806919539 f698a07a35e9f98156746092b2f7604f3b7dd083d67d74b90ee094603279a1d0 294256273eafca74c74555130edb4247d5d71c13c61c6fb38122df62c38151ff edb19013ce53040966886f789378ce982ff76698e344ba0199b72dc2b52ec30a 859e853b1813febfb8d81e90a4c5327a61602e94882bcf614c047c03a8afa76f 685b68dd21bd0bf13d7b5805c842ec23e655ee7394702909be8c8768d2a01e37 d5d7b2e72ab274922895413ea9810e9339424e317d35f323704f2dec3a358b09a5be9bfa237d6941074017b0365221caeaaed506475eaf6b16f66842f3e1130bd0ea398eee7eade7268d0a49da8a77f41836fd26a9762c68376a6aed59025f0d5b0e5284dcd5dcaba1590759e4663dd794f90d3e89e8746c5484c113bb149700d99018e3d613deabcf184e4411f6e083bc249aedc5cb2d32a4cbaf7e76610d0f3c5bd4a234a813bed9461193b96d17bd86bd3e20a4bc69cebf63c957af1ac60c6d3d2c4f6ac11c5e0006765b56cf28dcb8ee1c40a6c08e4f03375dcf9663f00450b46afd92ff0340bd8ff8d4f9d60ee8e9149ffc94c44a77178e53366f337802241fe100557a6a8263b8f286436f29978227c915710f21041bb0bfebd98fa906c0d9af3ed2f4332cb58520b57350264fc8ef6a96790e5c7bd572b57caeffea01968d9ccd7430c257621c5f63463c7ca1d9e24de1f17372c39500ee20c755430e811345a636370b9d9432b576a7f1ecb18099c4a6528f1e8c59ad27208064ba03886873bff0d50c04fad5840dd6658c721360634917db3ffe9f3f93e37ebed70a671c13184262c4485a8049dd278e45a9e0650da0b1e14fed5a0109d888e694091b9e9182d884840fb408f411cfd5010e6b164790620683e89b054fd6eed9ae0c38f1e8a179af598e55f923d7d62e170e6f0669830266d632dce5c2cbb78fda0f4247e8121519ebaaa3ad06c9e7aeccb1d871af2c68758274cdee5bbeed1cf10b88b7a281faa45e5226c2036b0df87768b1c91e4bf861bfe3ed2c9fa3a0aa9a0babedf2b7d63c7c773ccd980fcca8c83dbb6f117ce1c0bbbd766e90524ebad703cf2db5a67115e3b22c4355561e1bd13b125769cb424279294a61d3e4c8346d0a29037c576c681c2a513ea0a274138617802f536304800a2d50022359ff8b94032c315fe7793605f3b61b20d0aaebf98b413f06147f52c24ab46caaab334fea02b4e2f27a74c4c666df187df982eb12c7daedae2079392578d0123459f4df0009fd7826b8d967ca4e9ff29e50b3e1ee8008cd5f49dc578103b32c9256d5c71c0fffe8baf177673ab5bce0dde438b69f6c5e0dfbb0f4e02f1438559fd952202600e189efc21826834b737c401d604fa39ac554543397ecc02716c0d3f5d340ea096e459e7ea8efeb9802a104655bbb2ad0861a8633e94fe154dab56d3a5fdcc40f725171144c1b9cd8efe0acd12e9deb119a531bffb37ea58edae5c18e0830a30e0461a5a6ed34ffd386243f2dd1dd7ba96122c47802c552a97a948011ea97f8019435ff9071e9f07c4678e5b8c689288f97473cfcfc1b6989a90cc15aeb5f360a1f0e2eada316d2fc6c3fea6cde564802241c99faf3fd034aeabcc85a9bd9360798a445295f0dc5988e05b002de65e75d954bc8525ccac152236c7aaa688fcb04975d2906d6dc6679994538c97898d060d5095cf6a950d42cb48086d225985b099469c4216806dbaa265e2b522679527625685b0fddca0c2424cccbe9996f040c19df227dea5b54659151e62d786dc69208e83aa02713c85dd1cbcdd7f0b55505061bef36b9b06998abc9ceb7e75335d715ddf5a5d0f1e755a00fd033fe0f8b0b92e5d4ac05b69153712b3a91c408a03581b1d87bb34919c0da19d677791bf5068566e6ffc3a69c96dd83931011871caf68eea796ed58f7c3a5a2e78f9b7ec4080e4d3fdfa78e3823b03ddb5d70366c3b0ee5e2d9ec8247a4e189efad57e2710f89a38f7bc8ae1bf569a923510ca4a94175747c95b792589d6367cea52116620567d43bb45f0d6fb3c0307a8bf0e1740ed32ac856e6217c5db0a4b0df8b1fc102c3fadf11a7c508f5b3039c87a33d07563c8d40b7348cafb0c008472812409b0aeb66857986d19e4effc8e38051880b4f9e628d960aac73bb21ea73cb076532059c3c3a2a72d5104aecb53c34849d2dc375c1a4b3e7ce11efaacb8895460ee10b95632c3e6b81e7c4173a30583a5b9a9edde55cf0aa6d4061571f438e519fef07c76e91a309f2dd4265ee402d2d5696753197f9ec6caa4b0a7b37298861a75a01142e7277fddb2d3ab703913fe37498ea230079dcb3a772f663e82e9df9563f004fbcc91b9954003ba98942da6aab6c44d1fbcd22f325d6e02955ee9be0c770083ffae577207d566a0166fd718b3a01f8e200a261bb5b88f3c19b7b69d2fd82090b72bdeeb5392a957fc281dda86d41dbdb79de9144c671e3d38cba90de87f90b7dcd556526ae05dc4ff311d4f629ba62cbaf55195724192733ed8eed9dc0e90da059626a7e088943694e8c208f232be4582c8662e50db45388e901b8a23ffa0f3dae1f5dbe0fb6cbe4dd590ae42a6834e554089fddf5b5a20c3fa9677847590b9994164103d055bfcdaafe9902c72f57154f97c76e76954b91ed0c7fdc377b009f375bc1b8500d64d758f48a357c71ac1ea0b11ea8c649087fbd3a5253394d032830f6cec789dc9c8bc0efb989fc303d6922127cd55606777aa5d7fe70c9430d0de61f98c796a264b032017552441cf5c52a112d5b9eef44dc6fd4cdaa9a5402c5fe1b8d3f9bc366b70903474b31c7760a9a0d3d13773db32af1f71495330609c6241df7f4020a31758ab480b0f48a018ca11cf9a75d22cbccf268988eb29e0bfe1facd3e5ddf46d10b5ff807fddc280d6b228486808e824abf60ca25426c404b1c2cb61a03e20ec72e00556fcb3a17fb060c7492960a2ba0226c7b2fd80220f6e3c99fd72b3ed5a41a7ad87f146a9878e2e122624af6d8c9b2ab806392cb10792f72cf95a5e9275c3359f7dca38af59bd3151434af5be51db63eb3729df880cb4529891b2f270675193bcf0ecad56640788e780d0aff6227c5b5b2d2c7fc70a55e29b13ccd552fb47995d4397e601b03db515d5fa04d5a962e7d71f0cc85b033b04ef4ab441065ba9f30acc260395dd2a8c55c2e929882decf8b3a9bbacb207056f173e52d068ec6ae6c8b1eee3079da7cf11dee39aaafea9f0700858f5d60a92b870f18d49d3406d401c88a2cb486ffbb1315bbf5402743be15bead3fc4103f9ea5ba9eef7271aaac97a0ac121fd551b86f87c1d1e9ae3ca43745d9f122006c196d6a672d578a74e7f4b0ce024f15fba7e359476bfac46e3b951fd5c08260e1acc66c6f4c51fca078c55dc8516b20f4bbf00e9c8ace8b4cd4d49d23511110b820b29661670fc3fd091af746ebcacf6c7b151c2767a9af475779bbe259d920d054f21546c193e509561e74957b8ef00057c4ff988829a249fdca631d0880d0da710bc25a0b55bfdc3ec96297a0833bdf8795ce491e36383123ec5dd6f89a500cbb15aebaf988279355b75c93663a90ada3915a480a21cc178e9057fb294bd0ad7deacd91112d147063f9bb529ee0482200d30591ea3006368a2df16a54aa00432a8c7e73885621947700d03539d54585f3891f515143c42eb7f8af3832a3902c0c3d72b59ab7c1cf31512699621258040af230f7a5cc87818f8ecde42e432013c1dc6ab2645372a3de4506cf3eef7df3cd1133730ad6afdb9095a03cd67360bea47b7e5a01d9b45939552abc15ec0cd9dd2e65488956d0ff7e9f0ae1890220e8c0e10627b8000683e81a22dfe8fdf2de58e255ad8babbd684e19d3120b8be0c527e729b6b78ad4761c73d456d3152c6c1d64e9243da16d67522fa8a9aa51801f99cb14589d110dcfaa1acb4758f551958c34a8a201e69eed2eaed6a0879770d9c53e06d060b998173f7d5934f21d96bfe92ba96f3290191eb78775ae1e6ef00010f60f8efb995d2cb9a807c18be3bc6ddb05beba1e4df51e655ebf1b0be76022a6fe928673e5a97c3dbc64f25999a412a0e9837c650c379173a99e6301e4e09af77f1c3e379f2aa7993bae0d18c7e452f6d0fe2b637a20b31644a4dc415db06ef1767049fe5be29fcf3f75ee6d8337a2f67ac6406c1721eec84ae16192d2d0890ff8059bd9c96b50cb90f0448327bfd68ee67030616adb0d9441dd905f2e103a9871dc0d9831237aa90e900de674fb114a1e0926eaa4b29127039a130c1680d170e0973cf1431acbd1f6547f88a73b46ff802db8519de23b42790b7fe48540356fa90ba6feb7cc9e0ce460a1c4c61f6c5083039a87a80ee3a4ead1c91cc3c0949be424d636ea6fabaf9e362ad395c6820d65821046b5004878f59a631842505fc414b0aaf4d21530e81208c298ea59456331a32b93eb43d1568ac21d8c220072cedd65dbe3f9c3855b91dc13526ca8f7d18cb555295d4228789262a9f0e1909ba83219f6b33e7647723c134d4e00a5134169d23d2437b3308f02df71fd7740de778107bb99532e5b855cee27e346cfff27638b86bb7f7690bc14e72a2b6b2041f6f87fc1803b6ae0d32b78f888aa27863d23da05f92bb353eb65206ec4070034f3a54e333f41fb409d2d43c06b338f1877fe35f1afc054afc7e5e6da20a7a01550e6d2b38e8beb9c29215e918b8a9112725ca5d2f2858fac375e830dcf47502f1e2a2a119c1390f4d3ac8f6c312caeae2729295ed02e07a66e9fb0c87e4400d24e9605974a93ed670cbaef324d895be2d6a0a33d67fd23c8182632fa6300e0b024e8d64d7454c91489bfc6bf919cde2ef9da3a773df088c935df9696619050bbca78791158728cc70a3fe29923ec6e9af4fa0f13b015612a65be3f6c8e4b40bd8748d57dff614044aa2755e658e3df2ff98a2e439e1b3cd3f480a1b5cde3e08b9ebf2a7ef25a194b0b40387628af76462e58b7dff0e935e0d80ba1b6845600f2f8314bdf942e06eb49650abec8f2d92e9fd6a6d947080d115390c5a551ad60f746021aa875cbe9ff010183b70ea183a9248c4c4b3e84cebcf7f939fd4a6720434e4f4e04d2724be4f2fec5d8d0f80339a63acf31335534779b87fcc070fed0a195b93fea4f7dfc44bfcc6cb053c0637601f665b71980579233c22fe579f7c0050c623db05f27fd15f17f4235b7c8eb6507aaf8232c9d47b63f14edf19904b0665e1392b331db040a2e727eabc4d6ef50e39eb4a80d81a1f9a15c0e2e5149001e56d7489892c3ec3d4782d13d8753f0ef18321ee53343f06c43afe2aa7d0a40d3564b552ba24c4d09a9268eb29cf7403fdfd277f48d8a09d90d7ed342daee5070c660b3a9a35f4964f37f87b256dc3804bb3fd6cd1ad9ed1472588be90b8120cb4bcf0391cce05d5e6d3fc985bec1427c0ce38681b373beb1b9f708550dcf70a true +check_ring_signature 9f2a72b8bf565df65798a530d73f152a55487bd791b61ca394ec121baf3d35be cd6b98641f4b4aa76ea3a013019d362a12e909632f9854a2c0f4b45267ccdfa1 45 36d51ab6753cade62e65f65563e208c32394e294a331820eaeb42326c1a45175 2741305da0637a5f1f55302a44e0446df86738e67d78b951111489f5d5420565 407f8adca5392d83d92315b348fe416556ed8e29633eb8eb886443cd47bd21c4 87aa8d8fd74ea8124ac3d3d6bfddb873c22649b267bb1be63817aa1f02f9f1ce 3d261fa05afce364a7026ea8c766bca6cd9876e1278e55a9b68663e32b51aea4 ff8a7566b6c81b11910a7b930e311078cec4aede3c0f342e843b6859bb8d0114 5754e283282722b4d3260d6c1ee081b2204881bd5c2118e679e56e9c87acc7d4 98faa9c1dc8477c811277e7581230efbe67132c49065dc66f356af690d48ae4b 54648223c067cf3a212080744dd8b04eea7c6e3af8b6cc0d1c1da93989e800e1 94bcc968d644e507812885c2d7b00a51258a767342c23b68efc0cb98fe3d4935 244bc7a1a13a01bd4ca22083331d1f105d0ee7df01da9abeef0160b60699445b 6b2ad832c05e7bc4f2a362cee226e6f426f6a5cd52e2847b61143d159799453c 31691afd2e7d675a114101406acb079559e26576d985d4bf76a92a9c83d5f601 7d1005df10a726617cad6c8263009301a3de4c206a74fa849fe7d8370f11f142 638f17d6d88079bb861b80a0f386ba769f15c3522c8c057b2af570800e9cbf81 607271ff905372934f47b4a391f582547597c9abe7a7d6fae262db386853d7d4 142df1e3e653d9f9003e8cc1301ef81bd76292f2890dd2f0048a2f565d2b8ba7 f29f76447caf66db09151031628d6c31300f8753dce73ae5e22a9f44533bf1cf fe263e6346a0750d646814e289dfdadca507023d8ce880f6171ccccc44f1cbf6 23c168496afdabef4276f7e1182df23a55f60f31dcc04deaacd6b32f4db2cdfa c4494cca0359db6314036cb0602daeed8c3b83d4f4f491da30e5c2a75308a517 71bc2475911ce366d719cea042f447b0003c5880cc97a22b2415a9ddfce1d6e9 31da11a5113a4f741015cdaad805672fbc195e08602f1520115bbdd2903190b0 091c45f527ca9df26de1434ceac848021482d610d74799a4413070eb21cc1b15 807ac5ce009bfdb9f1a174e85f33a669fff8641dfae779feec416f49c9e1b3dc 93ff79bdf136714f92b51715390a916aa4d4020269449050bde185ad83bdd3f2 e6c79b12418b0bbe5347c6f080366c0a6934ac269e6201559ac6a9cbc4370239 5a02ad8f116a5dc84bb93d30ad46f47d610e9e3b96e4be2c8f4b1b2044e27f3c 4dfb8b2309d1d42ce9178a90bac81774a5854044ec77e3e689f2245cea9cca4e 6a9f37524d0d5c10d7869266efd2a582a44413e29c7bcce67b5e695bc099632f eb7ea87d11c88860dae9e8fbb4abde3a1e6a51c4ccd4973ef74feeaa65619478 fa2f3471ac4ec915ab3ede7626cd66379ca7d283ce435af3563479a06e7399c3 112eeb610a18399e0870c6145b5b0435c3aa02bf80f65b9d0386811056f3c4bb e163c33718643d60a9a62ab2e08e6f81ba1dd0d8e43dea72d4090211652e20cb 699341b5a839fe3038e520991cb3f69248ba8e7669caae240b8880d8bce8bb45 b4fff2805ebb10a1a73eed8b968fb98e348f2b5ebc0fc0be8d7ea4d5df065dc3 6e443a375c061d192f4192848e9518729a3dd016e0815e2428c461bbb78d9038 af836e6c9905b9e85b90333ea97a0154c3f47a5e45c933584c9bad797bd5667b 811d17181d77412437b257caba547099bc12bba83fe77334e5a755e35d916e41 04af8c0ac0cb193d66159868da6b7f2b08e59973b6d84dde60b42c7e994e2e05 50a38504fb9fd405254af8ba4242d7a9b1f94568981256d817e1f10548c17838 c678f13751dd8623dd7ebedebbea82b86770173faf7b09522ef13804025d978d fd75b17dbceacae8762576b9dbad599659c467181340935f9eb0532d519e2135 99d6c39d712096ca298afee05486485b2bb944de8df300bf3b62ad846dd3ee7d 17997c68150ce32d0c20ac03fe3970b80a50be60d6135819fb1326c4d380da49 abd06de008850f314d979820ee0a7f34b2fa6eb34879be9e1a322b69ba36ca0db13ec34597ec1ef8fbdedaa02da821929ace374961d8a386b443e2990c4a6c0d9ebadb4d2b3dded8ebf5fe54de921b1f1b32948b4739bc11e470f75a5a173f02eae259a9b6b946dc02e3497f68f5090813591ec30319ca0273f2aa1608f04a047e486c707eb613417fb16c54c204c03206dc24cb01a36892043c747e5229ac008ae45bb5bde9eb7e8fdaddc6cc61a2fc02ad0f66b1a68329764992bcd766cb0ce82851bc06e1be3ff9ea9d3bd7dcfe7794d6f1e590234e71106d5984a9c89f011ec4ca93b4a83645e0fa91520448a4a46f2c0ec7cd86e9acb0eb5700c139590647e3b4e5c29592d91dcd1683c66bca17f7130f3c1fa5ed06a6eeea1fec4e0e09f3dc61ef2dfef2c00588aedadf1142436b2f6d3aefb85c2438b75982db2bd10f1fa742f17e9bc4a51bbfeff0733970bf19f86350bf08edd4b6d5a9f961a4b905776b93ebde9000c0beed8db6087a95524e3991e26d180333860b411869fe77031d4e2979c78e5422162a4fd2066c859d90d4e8fb8fb8e895f045c22e1cf5d90d1d410210452600fabcb38433c7887d9567dcddfc5541f0149ef14863bff20601d0a08db62df6c0112fdf55a18aaabced8d254a21d5c91d090df07e0244055e08ff3208a5c3c9d69a8d9064a1f29f7422e5b15f6de3b158159835ec4d0dc1d1039073630db0a92910fc2b7d46187c40253423d7f7642296ced5fbfaa5e9f9110613ca13313c2818dd42b5455976faafb60859ad05b005c5fa7321611c0e842b02a715cd008d82f00cf976a9750ffb8a908e6ad473ce736c983b47f6911a33fb0e8822beabc15d96b2dc08aa7bdd17edb553aee6199ef48e55a718269a33c9d603a4714778f343387127f8f592ca078d97551a3c6246f497a6153df0aa890a730fb8e5f7f2d69a2c24b5e26b93d455d6b89d028d7c747bf48594ad5eeed94e850918d71ef09eee31eb63b06bf81bc3462ef4e660248cc75dd5e15ce45e59d0dd0fe7edc1ba6de50b20bac46b4fe357fd796b194403f610ac16eda3484e95c4650754af69498de8908a0bf0095bea854c1caa5c20b5263fa3845ccc0fc2551caf0d4d992d8c0734bfcef3335e69c375a44bcc0e23315f59acdc40dedcdda478230c17f6e5f99fef98f1734344ab41086f2df39b8fca073dbf5d09970f81a6af450a2a85c081e0b3f16d27ead4827ba3fef2d99d4d380423be6388a7fec55a3bc604ef97c1e5d0ad585391f5c1672d70694be09c6e7b2a2b2b37d40af255ab343d05e3fbe3af8be9abe367c286f9583e558b504874fa6bbfc157739da6d017e8ea031beee45e61d113b4d3a94dcdcc8b3a05e9f8bfa9d2eeb81d5fc46d754e718f02403766478aa65aba504c48bb66bbfabb6713b476ff0266b5a528bdac705df001e27fef351bebb94b1bdb0d81019991809a297e8d319647bcbb039820121f9504301308f19d60db4d5704ffc23a607bc3ec15fbef26af471b806d6e9d733fcf06f28c459e813d29fbb353995b1a9ef7562b838417e5fafcb1925559e0812aac094d2364a6bec93abee8dffd173bac0225c53fe3ae786860783699ea83417afd0a04ad215cb7477979913b23ff6340e4afad675928512efa2665849cbf344d5002276e3b0015ed8abc70722f837eb6fee88e302e96867aa1adab26a94e5af0dd0a4b01c9904756d6392e6931ae1bbf426ebfbf482b8de92060a6bce31175df770297049344d6aebe5bbab0daccbc420649cc2e41d8a5e563882e0cfb328b836e01ba1e589b43a7a031c865cf740ba05c20ad6e7bda4533eef95501ef70378ba809f93a2f98323a233135107211528770a6a23892841763baeb75baab8b624f2608f24bdc178b4c039a4e54831e4acc76985ae12708347ca988d0c481a6f16257059b2fde1b8f8481e173b17efa4d32ed0b7801b7e63ae2e7e8bd4dc2cb5a34ba03bb0e372260c3560b85761b3262aee403fee9832838a85dfdd798dcf5294e440a39574940240c1d4ef674d7e6e7cdec74898231ad16809fd7e6d976ca70869f05593983376ea032298c2e3587e4341996c512eb2dc32d75604057b2a079fc2f08035224f3717d67f37e83322c8af005ff27812d5850b4a2040e4a8be7524f19060d8d65868f28401746726cd71cf1749a16c4dd19aa55483a05742d829b953b06275f91e4e784dc914b227f1123ab95616f6853a6a05b642d20128dc557126a0b0caf13257a0971f374f35c30d7d278214a14edb5dabfd936ce9dc1ef53c5a9042b3e09f3c7bdae84b2fb46dfe92a4508ef5166277478ff36d2c17a7c63c2b20a7b8e765774522d0dfab13072cb1cf048f00d4ccf3ed50df9526ef1554aafc701cd6f482f4ea02b8538f2f0fd475e07f9d1ae59df8d4a7445d2449e1901639e08ea8dd3cf6ad6d3dca126d4c2862154be7fa8cbb62c7c9af1950b251e729ebd0e1cc36a4666e0a38bfd010893c2798eacb6b3da9f5fa9459d288456adce18ce0fb74530a8c2aa2bd4cc8706b86ad670cd83771305b2bc028729da5a0772d4a309acd07f5ef4f3556356b84ed747e2c394f08eed45551d453a9c42d478cc31ce0f7afbdd794a517359d1d079abc09922fd0d806fdf7080fbdacbdbeb66b6007a086b89e0883b0642f34f37795511a61e8698ccf6ef7020ad0305de0971eccc19010d00b56989bfb7383c66e3e7efafd8cf695cf47905d0edf3b64d6c33e27cb20830573d37cb04534e1e6e9ed5dc3a3d50c370280bba9212825c4725b6e939d701978bdd620d959e3ebb71bd5fabc57fbc2f44e1c45f8f67f1b94ceccc2d1d5d0634267422816733c70281c21dc0fe32cb6f29c8c892cf0a4e22b5ab4e77c1ca079de6dfb693b4e28ef48f239386ad7c615eae47203bce2f4a359a2ff92700160ce2ab83cb010f5559bd80bcda4ea7ddd1a730a9f07580053ce08fcbf168668603c717f0e79081109d8686813642947693c0178ece1491db50fb4d03331b20490714d774a783f94af2511c2aeef6b16245cec64bcee7c8130436524d0dac599f02af481ff6bc25037af4a2eecf5f04d9eecc4aa1a6d2b12d57355a9dce75e8b80027486ff5ee4905a9beb96258fa770f3572f4ac1babe0649e1bbeb447dabce10dec11c7a5f79b7b9d6bf886ee988a9d7c4d2a7c4237352371e3670ecd6fae0400e24c805645733dee3a8d66a83b31fbc8c897e0bbf9fa920cef7d1a29b7695c0c3770543d4a10fdfbaff57160270488e7c3426fb0642f2a0a71647d3d41790a06f74284107b07272898c7bf83ee3d91f081ca38048b9466c65daf9dcd9a45440207f840b4ba49b797ef021cc77676093342a2a9f6d3fe93ffe1045dd90b350607c2649a86d38500512502d5dcf12f8d51bfb1f35324f6d0e3f08a78e3b1f6310b96414c7e3378467e8608d506bb692b13bd60c3b20e1232701bb107edbb0a6306c941afec09d9ede144b3b9fa429eb6275a9baf332c4ea67ad164daf8ad989b020aaa10291febd8a87cef76a82c0201855f7a25bc0d71881fc9ada67dfdeaaf0f47b1f85f7ff9e7e3a377169e6ee565ad43bd3b1715e1bc28b7e690170cab6c012f68477ba64da522791f2f4b21fae202aaf5718b9aa5c7f107eafc41e811cd0b9d1688db45e9f239d3ac80b470f6f3c351a56c90f7cfaedb7fc7312937cc480364e56b08a9b819435a85f4c820c07a79d3d1209fb68b82f4c42998e7451681054c8836e3e728903c3b145d29b9268675ac4e4ab65c6191cb6238a27a44484804771ff659485c9b5468794fbf89aa47ad19126840517e4b82af2d71dc9064ea0cdf2b4f7321103a176b00ea27b0f7a413cff2af02c4fb3a78808554c35f0774042dfbee88fac631efcd150a047e768ee5989f8ba0a6ae79efc4099b442decd10f1dc2d1f79ec053f78942351da2ed6cc145479d437205bba6cfe15a735c27900c532d7305d761265183147740acc4aeb69624c97406d303278ed8ac4d7c2e7e04fcf0ddd2932b55ce71a1f3c0f36ac8e3c2b97214877ebc02c3948053e62e6805 true +check_ring_signature 566b2084d0c80e398d83252e2e280936d3eb3774c47e2c839138ee7cf27ac985 99da09f476e187ef2eba4e318f22a14d176ffb4d9c65df83016674076a858771 1 c3a171c4431feadb0473f3c650275cb3fef66b0137b2d766617da4581006981e 1da3351acba0088da3042a0a73c35ac529dccf49c1e103c0d0a3e35e3ede1e02da370363d712a1a81ab9e2417ef0746d57803cc6429039b7a21ecee3835ca45d false +check_ring_signature 3535cde0d839d324ffca75f3d6032f31bedad305788200d1216022ace9a91b59 290a4990e62c8e02956ba3b5496d980ec373a8f7eefd83cbdcfd9a69f3063d23 95 ba6140d433a1fea279aad72a4cc27e3bd8376afa53fb9ad12f3d1b16fee516a5 d6ab88656db44735e56ed0057668b22008a45e2f73d6d470136531309c03d92b ac266f520bb84cb4ece3343689a1a69fed7ae06f94a4c53280a0561c84079aa9 96f94b82a66d2ec14afe578be60f3f1d8fe831bc7b1570ca9404cb6255f0b650 2a09bdcc82a529b3b88a4c09f8afa175503a557cc260ebce9ca85792fc9a7e06 6d8391926285182b3862cbad713d838931a7c02e6a85d18c0844b135427697bc 7850f6220fabe716138eb40c9991cfd86be25726d1c7c0c346e79c601bffcbeb 74d25c90ff7dd21a19df67cb18a97a779addcba386d32e402ae82174c69ef268 ae1bbbc8e02c279fce70da79c30b682ced806d8be7aa871adbd66f34588b7b8d 8d3fb14d8cec408b0bc0e9385d1fad3db8fdd18fd0707378380290e8a6d9415c c1b6768c51e593377cb3741b05ebb38e5dfa2143eeb02a70fdd103c2321f2085 6204633736b38657c2c7c1b2ca5a389b21940b587bf0b24696ca943971160834 3b904078052c4e10651d1968b690ae399ad939a5f92125b517fe0e7677c7a92b aa9af141a08f437ee55d6095965a88c58922423bfdaa4c996699d635396a48f7 2614a8121a53677002ebef1d2ae45d787dea0c78bdcc4eed72efe79e228e172b b77084c4c9314f939248393a382ba554b8ce754618702d4e2cac6d1e93ae4549 608fd5db8863f67212e1cb0dc4dcf067a6a890cf8d2a424460314d725d74b089 33e0556327d131b5aeb724b5a88f0f7d82df6254d9d6b6f2e4024d1f5b413411 28ea4ee27b242b4c6dff60142bf9ad69df2b888d25c541392bed6a15a11604e2 30112e26ca564a6ced8eb977ea6dd940f758d772f6c5b5c6e52c01724b04b47d 18eb6c634bf3320677e6a1e5c8b6071990f55ffa4ba34e4f9225b1e25debaf43 71dfa2875ce082a4cfb15d529aeecba403861dcafc20c5d725ce9f3542361bba 4c11714799e84f1e880c4f7e2e9514e487be732abb750105db4040ed1aba32c4 ddeb84ed38748ca5b17d5d2a1a03e647a68c5324a2b316f22c737292a4e51385 4796b00daf41a37ed7d1bb2436f26b8c1183e1d00f179a5502d6e728ec4d1851 ddf909e5dd159989bfae8c7bc68225a4c9a4bdf341564769c949da71a87fb28a 0a6018e40456c112b718f105a7f1074d65dae9b758cf0f6e7925e3df26119908 f3096993ee07f8dd737a58168e34e2491faecc75584095b5028e858d9305e598 00b7103d49f8b7c50e041423b50e91627cad548b26e5c42dd161d1f94ba520f3 d2c6b7c92dec45326afc2417a5393cabd8ea184e041f43bb5d231b5ab398d30a 3d14b162130e37b7626d32b19e7be132ab42f01d9487218ba22365f7d2982000 f2b10cf9f7d66d2f732f084cb52262a33e979dfb3399a1ca2f9d45f0c1f67dec 2bac55f32bff0da7b1ad2f70e842097e701d6a7b313682aa8e810af24f55f313 2fac87714eeb5197d7f3eab3c75b00aee8c6ce41d0e1631f731673f0a1ca1e2b a7d9b7f352e0448cb0cdc5442af81f7903e004837e05b3584a231e86d776353a 6ece3e19771e44a75c5b4adf6da41f371be41529c70fa1467627f8f82f1e4b74 05c32e244d29479148faa74b86c3cb237e6c98ca985dc90a7118edc0f092cf7d 1563b502c4afcb1529172a4328593adfbdeaf24bf15b02c6ef853f02d3df65ce 777baa178e66c904dfcbb17ff4d34db7b91d7edc60e5e151b31dfa00c647fb98 fcf3ac90f88bed0cc4d70418b3469eecb6c08e54f70cb671f210e775f40cb361 8798832bca20c7ea15eb92d1e826a52fee5a8fee98d2340c9727e97fe423b1c6 d42024f4a566fd27af8c80833504715acf55926406b43780a04fba446f2e25e3 9177225a73ea6d1f9244802b48ebf36a480a4c8225a610662d3a3e3cf3ff8c0f cd9554652d2c68b1113563d8604c52963e2a24485646550a27060488664cf1de d039eeea1a0e5aede25dd08085ebd21b71c90ff12c7af1874c93661add380137 7053032365755f3508aa119f0e5070069a82cd23dbc65164923187132213a511 7c046434829390714f83522c5ba41b6b70ba113d9285f6abddf5e8d7eba5bdbd 8797468da9f18d59181d4a5e10f602f162760701a89a92a9f2b04f778faf0988 5474f95d053983cbb27f58cc86f5bad5d85531b9ddb50f9d95cf4e1f24e4815e 9af9d464ee4a968f794b095e1bb0bead3989e0f92dc8f14d3663f1ef890786b6 b6309a8ddda4ea66c53b2991571cbbaa0dcb8c621300283ce28744380a9b1546 86a2b41e268e51f2fec4d119d705c875bd9d6fca2b9055866d69a9633c0c05b9 ade5976644877858bc9e474e4305b0c48ddc524f842bba20c62a44f0d1c917c7 96a7b7d88081a56d5bf1860195d5b258ec5c513ab3d6b082049bc6147682c266 ede760ea35111b900f74faa1bc62803538b267359a6fae00bc4598bcce44166c ede9f5e292f06488745c1682107e1d7f67db1cd11e38f8c98a59eba6151227f2 405a5f55904be0d3b3b8797293afcbd204715aa5f7ecb58c532c87ed01f5cef2 013823e919230164a98cc017a0bb8b022be7e0ea39515b8d0a16999d5d2d6da4 e89ffb4b5d9c8f8423c04b2c9563fbcc269a050647b32b7565fd9beb1523fffb 65eb4e74ca64d7850b6ff90e8f2ff434f755331e42aaa8e14a21220f62557dbb 20607854d8917d137b87402f67931ec2fe2431764fc1c6c7b33f387eab369896 3f4c0889801c5ce7e01a1d4cf905740a9abed4cda3aa453e008e8f6b834d4e90 9b6095a7c84fd5812296292bcd6588e67a055d339a8c30fe41bff26801eab847 e646e769bed7bacb3811db67baf14f52d43f5a2fd239895fd9f2a0a72403ea24 50aa8a6a80b93d341ed754d9aef695ecd4f345c876bfdd542db4181167d7135e f887812f0d8b2aa8fc7fcb4b8144eb74ca94232d9c471ef284cec98b0185eab7 4fadcccb417562c3d9dfed806fb912d5c4b5d8bfb00b68ddf22fd02b491ffb91 b86fb01fd553ae7c68fb6f53209afaade146fd37c7e308cf582590d754b7a3c2 e963bbf4c88a1305bb82b80bdd61c23f4bf1f9cdf0bd8def5be16f40a35ae76a 6a08d5954196d9092886748b0adbf8e9a7b9392c90790b4f067cf0c8d1997cc5 6d1be1b0eb5b821a8e7f1dd1aae105dc4d3d627f7de5db74c233bb92d69a17c7 a9e4005e8783efd56cd1d23a6cd0d40b635e42a2ae7ef66c6c73d3801527c96f 391bd3af31f035842c79c76e8b540316f3e8da95a7f6183fb5ff26601295f6ac 7e89d7e58c8bb84c47314e1354e0aa23379655353fbc4ee6235c3be9e3ce21d9 c7f750b4172c5389068031700eeb9736fba829f601ee8bee9d0a29a085fbbee0 619320ed33c913ace025d265de73e361cf70a27ca1db5e1b21febe4b164b155d c1246cc4fbdd07655c252c0e9a7726cbf1db6610f31c8cdb45289830ce696d9d 97044be589179494bbcca0f5e1a2a05c018379f161c005130ec8245185b3a6ba a753d4fed9f8288e4b081a3b0731cc42a0b418f5b6ef1b9b82cde99704766bc2 f681bfbf69e6f0d9a382665f127c0c1ed6f0c7dd26c994456602e642204ede5d 22d5d514653c7b040329fbca6a7559a85a010d65914c079ee0477bbf8a05e76d 308fe4f9eb8bd5e5331392bcd5b558724784c85a516031a9e8868d4d6a8ca1bd 2bd521a4ff5056518e0c50ae6626522c9b0a11f7c8f3f78caeae644c68b52def a5d9962f5ec57a25b0b40c52ef3ad5d78c8f456ef539c0699841aa3cf70ad9bc a5599d169fde852b426efe74195c70def4baf64129fd18c9711a89288fef597d ae6131e291c826cdfb93b5725e43623afd4d10d1530b7af467de13b206601422 7e49457b8b1de2efa6c52bb608372d014772c039c4259e284f043cfe00d4c1f1 4005b3e2a20cac48d8b1007da4503e024e9ba4ef1e44be18108679468794f99a 98605fbbec9607439bf7d3c4e0b95489df6a6a7bb84db1ad1b38fb56b24cd46a 2459330751b372cd25d04b4eb54e1bf5a960955ce60e8b21373e5126d8fc0492 f056682afadec616e3db879f85d452bdd453d5401da457dafb914c5f8391a7c2 ad189bcac3d4e3a7c04ac7e35b9dfee9db168f5de43e3b2199bb4ce63431d099 7bba5ff4bb64a9a7798f8e4df53ab032e8e8d4ff534de52d77ecfe5b601ca3d6 d3a15d1e338177b1ca927a882718710f21fe49609c48cee5462881f2178cf86f 5a311d5770946bb3982357ff620bf89faa1e8885d70445b9d913787092ca5526 0551a0cb674c1e031655f4b7e3b32a2098b57647536e823b1fb1c3f73b61590d3fc363164272233e8bc1e269d996a49cf74f8dd1c80013a4eff100a90582a3089a16ec7aee080d8093879afe9b39073494a7727baeade64994b7b9d46f029c083b9336539285fedc2051fc047e43051b5feea327f2778a901634938d193bb403ebe6d25df0e45359b9c20baf89749b4d459c184643fe2506f0903f1309b1a604a9a35e9f81d3bd2bc152d8b6bda4266f24a281a712114b7849b09808d7a85d05133d550904311c65b98134d0c5179132edb84d76e96ca4c23c88916d4f811e03f81d478f550d22a60e49778b4feb2feee2d502ecf1c8332a1d97ab60b360fd0475a34bc1945fd08f4b6de083bac1a87ba3e86c9db77a9282c3b62b1f07c9d103b81257ef21e0e4b5f70f5e3e5e34f1cca6a5086ccbe0cfefbea236e3b3c7f503812e11190f3204f698834b1a8f997c02b587b60005961f123d46fa485b905d0bd72a0e0b0d65635d7464ae4b8cd6e63782e024ea589281e183211c1ae8c6e30fe7fe97edf40a154dbba791c1b38cbf4efcdd87f83705780d04ff61fe7de8ea02cde3e12c5b4ac1a72c3154e71fee0b0a80db5e01ea3b3ed85047940975f749048f2e0eaff9d1aee547d8d67fb1577aa028e63d6b7f18ab6981da67752f3c21005a4e1539d002bc1f3b535ea5f77ea711685b94c7bec9a2cbc6a7245dd526db0b90e102d680fac78e52beefa51cd61bb40835147a1afa9194921e6f4b9b8ffc0758c1da504a0d615d9e6554615a04003c36cd71c9abb2cacb95787ac1f9369c0411e7acb4656bb11bc8b4ad8c40c3155f41ba1c4f677d73fd842678dd7cfa47089983e88bae7f1a53d35ff1fd47c90445022cacfa77924d077bdd61af01e94709e41d6f6c3ebf3e249f77e4c12664549df09fb641c019c07069edd2924ec86a01d7d37c20e715ce41e85f85cc9db423b21e682663d26757ca77a73072c07ddf096cf4173c25fffe1775a0ed4e92706c35677d747dc1630d514943ecd9a81c5704e9f30c2906a561dabca7f57814c309d683994eaf9829d034d5559c8fedb94b05e6ae2a602dbc9627b743b9dd62b23238b7daa00e6dec463225608422cef7ba053bab7e0a91bfec7b11120cafea429739e984c168b069ee1897d431c3b71e53058fb66b34a42941ab842e43db47c1d3c85fb17f6d37ae2d0c12f42ee26441d404a0476e91977d1449d3ed082a534c30cc91354d0c668e7113caf38d24e696fd054a42ff6d86a3db227b781a2271ebab9198ef297e040d500209720df0ac9aa6056a2f4b58a783101c2dce0d9432cfc8705e03fbf79dee7e9441c08f9e70a2430028a928577af219bdae52bb5f5bf826d05a586eb884d5509bcebf63a2290c8c07d3d72a781dba1376a52cc1c1fbbc3678985686034425519d7e47e74bcdff7005191feec89fc4e7324a4ca0d5e12528f348d7cfd470593d7bc64c3a1d38c52a03baab613f9dc9d1e74ef73292521216d33367ff1090d50e348cece26afbf6110aa83f171471e5b4f63c96d160d2c1be8ad8c2e38f54d4515e62d9aec01b8e6f06c0a4483245695b795668d234c29dab14961a95b8b945f6342e253e41ebc8b904a54bfbdd649683144316efae11b971f267d72626c3521df05260736f629d5c0c7c33de183375e2c326de670bed8148357ee9509430ccc82a4bbbf730f9830603fc34d05454f8f98758ea1706fb07bb8d222d020ae6dfed59efea7b3753f10e04c38ea9e6c6cd98b64f5de662e205789f4c68bc418f9368cdc4279589437f4a0725beca1f572dbd1c6e35bc8825fd81aeaf8df2f26bef0312c6ba17607191160766fd85ef0fc6f5235a4cc209cdbfb194eb0860d7b476209689b6b2e14206e10ab3d1b0ae0a58bdc5861ba8e28a4921573493f405813bcf101e713f71df40620b886fd5edf9ba7c01efc2d0ea14e364a794e897211202950397c6f6f2f1eca902e5f6cb7f072b8ca3e5b5df55bb4428e2a191e2b6978339992e3a38f14cb52409853a82a2b5437c5b9d0c4ce92ca485d983cfdb7154b37a78c404da52d9797004fcdd4c866919bb3b8b5fc8584b08924ff831be448c32fdd19cc797cd7fcfe90b0409f730472c30ea625d4ff11c3d2af50ebd38ed6fdfaee15ec2cd7a717fc00bbca06be39f4e979ccd1b5c127f4c797a6afdb23b1930e0cb87062542f73e5b0b77b6113d21b0fe15fc7d14b997df1be7129cec6a274f35a347f2b59e13317b035b2785ed4c03a030682b2d8d32bb3829fedb5d014491254eac5ad361e61da6029d38ed8eabebb141645d8cdeb1fc86fb5ca28fd3a2c533bdb9a5c3b2e874e20df22b24a559f7ca4a9fa406527d017e0968067eb1efbb7c5ea1af7c4ad517d40ddecf1f79b6565acc69099b0ad8c262d1ca8bee9cee3db0636f42c6229ecd8e004bff2d29216ccad81d715bce3027278d7485181c3c03f2966292c3861092ff0bd260026c8a2e18d7757855482027b9ca1e1d1dc1402a14932fe8e8bcee7e040d934b4080825c5ab2b9c4da230061df4ded8f84b8172d9a0e51a173571afacb035f8b3fd496b50e87249002b3288214407e85e3b46728620a61659dd6fe5efc080638ce322574b5d2230578663a20b9a18dee0b0395400b4167fdd8fb15b55b061dec5d0daf52d6c5579ea25b6e524baf5054f6061d721df9b16f1b5cc5d2280d9b5e323ab04eb7a2761e758de6acbbf42f3109935f5745acfe730e46f1b0e90730b550c1885231451a1a1e5ad27cb7d013e4b65e8c6596f08dd47c63b699f30e5734d1ab49500ff5adc8a96e8eed54d175ff8cb4946663c01f2db92adad023045754f9f11c48d5350b8c992ed7e5359069f6fe58723c5bdd2db7200218e6190cbb0e89312af4ee9c60c1ce5e87396d66d35918719011b5c0d87373257816050d35a5604cf9df0d2a5547582ed32c6aee06de3115985f12c2043b1d105bed080f9be7e2bd424fd60a2fbebc008331db51808d53dd9bb8bb2c0c7591bd79bc1a09785c50e326d0cea5fc1ac8691ca39ee957ef440e2cdcfe5169ead6516d0003092104a07ab24a4ac1a5f2a5f009ee7c11ecdb135ba5a8d33d18c635fcf1585506d30700222b1125bfd5bcf57fd205f743937e3dc8ca5c6e5920d8883cd534ff0f89a58b65d39baf87bf3a7af712365d0fd23f7c2d8d3c2fa8de37d60c7a2db5024daf799e88a17d2c71eefa7660180a3c4ab1b4ff9d7ec5f263ef87395c82360f2bd45f89839e637ed3cc7072acee31d890c8fda0eeae303d7193a1d4bbcf51028696f3af95048da23bf8e827d64498a5f6e2c47202a1bea61168b2a5109cc10a3d440fb63aa2387382961454b8f8718c64e9c4374d3fd7d924a75d53ba6b450c2772e366783e5c7760063c527b3ccc463da5d405f6645f99217806e5ea7ecf03ed9cdf37ee6b8d321a56412ee36478eab12d0ffb14213b44f5dd0dea38b6e50a7749f90afb27c54846448ebdf212bbcfb8f27dcca82c01c6ee9afd52ca64e0015ea7ab94b765236156ed85c24e14c5494514be81f07d8ae5b08cd6ea1821e2016aa6da29c8029ed32c7aa9e2ab537b7385b423bce2c13c36377bffc28f7f5800084bbf305080dbe647aaaace48d1dca62f431f987085eca0cdd5115e6148c10c3e7b9880b5735566760c83568ac553184e85ae365a92c44d68f3cca1e7611c0756ecba30544ef93f00633d3c03259bfbdebe4903e55a7356dc69492601036f03e42fb782e76748804d1bc088b8f9970db1bd7ccf67ded5bbd94858debe4a6006efc0b1f2a43da758193853e999049c17ef69c3bed6b98ca189beb027b9fe150a7f69d4f2798eedaea6159eb16bcd9255ca501514cfffdeebcde72c065f45d00147567e0b6816adf02d3771dbdb2bec947b1191fd49400a5fc548b0294f74440f57cd9f0c2090adea9abe2dc5191cf6dc1f0a9c6bdc1d98bfc61b0ec2b8da500692e273262e35e4beb4b0e952cb065b0ad7667491103654fda436fd9450880600d1654e8db968f4627509cf21ac8cefc0016bbd9b23102ef0a58ba716f1551a03fe8ce3ae47ec6767a72a390632e38eff5decd7f232d3accadc8d84d8cbe9530dbd7badb1d8c7e748dd7e977033ddaae6ad95cab32b4fbcf0b67567142a52f80e22815bfc933febeb388f8b6af2a63f1beb63b2276bddf435b27a2ddc7fa1620176a59305d0542b4fe772cb7b4951dbeecd94639a802632422cf7a298929b75079ef2113171dc7f354d33e7fff02a1b73f242934275ea2f7250030c430fb6b4018bec33f6407712b32bcd13e8fdf90b61646a7507f133616c58ddef0ffd05cc04842ae031d0c8a114285ccc3411288f1e77150436321db5bdeefccfd75b15c70605f88f4885d25c1f9dc9863dcdf9da23fbf330733b3b7c2f80c9e603acd53503622c0b1f8c467fe0d78702e427d260ab7a4d12245723901716e1cdeddf332c0568511c53c78357fbae535ca4909d88f52adc9e36dc83aa99506a3293a124840f6979300477223445d1fba336efdf2ca54f24c8271291c54d5340f351fa38a00a6f72000376c9f345f2eadfb2389acf5ecf4542fd56ef381f0817b899ac9734070dab030712bd29cdbe91fe08544054993b54f4e534551452af9f09279550240382bd8901a6f129a4423cdc3353e6b2c10598e5b1cfa4def135c6cc287b4ce603d0ada9bb78aff897d1a0e0d1c59135e79acae81651ca3becc763a016138788054d5e492390bcd511ce5f68fe382ea594aaad2020f7714992f6e272709973b20e9752fc5061a4f38ff5a388e62689265a5b7a42ffb156130b0620fee01667f200d5e301b4217fdb9ad8160aec64bddc876149755c214b018ca5b1177f9188350d3269809f9c08d9afc423296f87a4c5f4df4da0b34d3fff6c515718596a85810ec75db77e6d7a6de30b99a64425ed2d059922ef22fc5606fd180c48e91db8b9094074d154d689cba052d480992f454974f56d4b9b859da804884b146e91be3c0de424c6fbb2567cc8925fe8c8a64c5b2685f735860a4fc9e7ba63539a8adf9d03b5107eb91e08d4807fdbb21189bd5e5344bc08c61f824bd83b408b31af3be50cd49ba827c3f5e75cc90c6c14acb1cc90aa271afa6be56a798a03208728070f0ca3f92dcc8d32fc7c2be437c06a220c4278650475b87431f313cb692ebd5a770284b6737808f9b7448fafdec622bbff5c2093c2c3f5b183f65530a55c481b2d051a6ecc31ab95833ac2caf7c5f2748231ea69139058128f91d7c047cea7caa305943b6dd4b1ab9548bef2226863e60a72da18f9528e76bd735e4d3e1f005f670efea653c882ad8d001317f98da2d3ff01bd42ad51cb155714282f784589ea1407d1a1675a588f5b54a2f17b218cff088d96ac44506850dd4fb44900053fd8ae0fe895dc89e11507580646516249660df702fc66c6f05842bebfefbda7e99142033500258b77295edd86a5da41a599901a46e05089b83993a283d44697f5d6e009dea8bfe7c39778b1b0f87264eb32f69b37687874105863e9e41217f6548c220b7cb0be08f82a3237323c3b463286ed118b8420660f86e7f5271ecbc58ad3810bd4aac16f779187a92fab427430db0b136abf771584efbe1aa75dba1213434a0874ce766b5f8243331273a0790826108b4aae14d84660dc83323605934825290140cf93250572c4a1302eea8fae55aa8f14d71fc38931fff43649eb4bf89f54061539c4c7b30bc5c0178fffc617c44f0ce0daf36322e3b37c460121d0a2de860e5e322c3b824b6e7fa5c8827b725f79a462074d2635334ffdefa4200bff291d0d85f7ebdc938350909e5911585151f0c7d7caea7c8cdb91c9a90b6efb9a84ea0b9a517a5f18436307c825a34a52ff46d3332323b9c4d24300d5d3de98d4382a0b4dbb4322127fc2d3a539ce5fc5387109ac80996fba641b797a9ebdb51084b404621f640be0cf2e49c5c3274368b5b8cbf66cc023dc8f956cfcf6048b6bbdd606caba8595c80f8e94f57ae123b3bd8d43e7f989a87ef72dfacdd303a71aa85d05cd87a3ec1247303cb1457db7fb372587409288d3706b805b73ecb369c43d920089d5d96e7757d59a9ae6f728e7b8713a0a23b9fcd1d3e4d4accdc007ed473a0117e25cdfddaa5e1ec50b146d7209e25f931705545d5dd16b0e2810581807a9013e7dfca5225b5f48e9adf41650a9c8e65b5773d0254e27df96d12456c3cf8801940e9ac1e798f3371a2d0e1c7968935a1cb69426308758bddc83586d2f0145019b381d87201fbfc7efaa4b935480896abbf3448f0c702c4d5d042081fe0d9c004f98866fe8610ad4cd3dfb357806441ddd120896cd35a9cde5e36096c3339208ec304954d816928abce408843acfa9ca01752f822154951a511769db52a5100c0b64bdff0289cc965d5443301624db956497f3032c361241e96b8f2aaa89ae05d3e17bf2a26629a839c4f050cc04cfd4d028396eec3278da45871f19c044fd010b2b3081577b1a8841098e29b10b45be8fb4b28be0ad8d7032abfc5126b9c50f207b237993f2dfcd8a7032f85599326f4170ef2b9ff6708ed963c3addd44ca08ca86dbd12877a01ce0fbb57c373897115901bb688137125c7d9ed77c10ce770f8577d7511b48136cd0e5d53ee3772db66999bc55a546cca93157ff191a02250dc984c1679f67713e1d20973f7f08491c258613e3ad604d31f133d6a2ac64660f21338b7ef3cbf68fd874545698150ee1a271bc410a7dc6fd0951601e2c80fc037e1e0b63463977de12025a557c19f1718413c490799c947b99776b93c979ec0b7ebf8f896192e9c7bc503131b5e7627f20c11a150dd30c252118cba11399340500766b1574b475c721d75a3c8f2f8ce65737decb86314a4a15497824ce67a70c60fb9e6fe4d0e488aa59fb4145683908a6054e43c73103e5b9bc19e135cd610906ded83730016b9bf0d37c7133105cf5fc4e0373ca7518d6f5520523813c78065b73ecb0e62278243480d95a4af016703e8781fca39b03fecdcbd662f1bcd70a890c24ceea0be44c5221730143fb133887649a8c8579098e4bd606a9d6c2de08c4fb1583d370016f1e9e8c3e84ee45c110c9673721f60b2481f804d1249e5c050c8be12113f9d69224dee35182a60d8e4aa7077194602f9583c04528a5fc81069832b44fee94d6382c5121cfa9ddd9b1fb291b6e1b4327f74ff4ef50f50c9b0d5a5ef6ec1ff749c8c64ba5bf692f6ac4be804f77d359ea95ff0ca10c7d5de40cfe5b1d9192d3fecdfbc1a6be418b936e89103513eeea205980c58b1b89c2d7032906ec75cf58fb65d28c96f3cb44a518c0c6920019e6ec56e8e4eb683ece49031bf108ff44edf0fd196bfe46780f2d5dee392367fc5bdb0c7e9986df1a25d209ee266ce9a40e67bbe2df6113ad7fc300d8f860b6bdc7ed8bf28606aa990b370a31bc76a85f71703b81aa6b0b17a4ab3f7f9c899242d26989e1f709964e8f2906080324e65eb860168ca54b889d30a5428ba843fe25129d70bb24ba07e5d9180745f98544b78cf8b8e1312fc1e16f6e1d49cd47e5e27ee73df32bb049cdeef002aecb4c2a1981452901df98816f7d8524c4d2566a53009d14371fd85b7983e60baea2941f9be8ba186f9a2b52ca9dcc40fcfc5c7d26d5f04a4fc1f90722d3e30d260627b703baf6ae5814e64040dd5d3ef2361db321af7d34953491f80cff9d0a95e083eeb8ce9d050510032642aec8e14e9a377125bee4c0153b88b611d2c306a3525cc1e4f1b7d8d4a6fa2de94a22cba4e6e5d33f1ef2ca7d1a573e5d65e402309b4393b7bc109373c63714771aca2d437fa6d7f006cc77b24531ab9cf06408ff1f88161d9c83b968c642df7905e2fc42f3414cfa6895110dc0de80adb1b90951946834928c61aa4e0506cca72e0c86f283f6c2e8d5d5e127c4afd34350fd09b25d625a34552b4b21beeaff566fe00a3361ca9f2c423b11d62a5a541fe1da040e0c5fd17e393fef7d4051f7cde7a6580964f22fe776b15f27b01237691fab0e7b3826c6bf4e10a1cb7870b40b03cd58cbe03e712b52341190740564a4107a08f0a82039f37ed1db17f21fda54f60903bb74de83b4c897057ca555155dfaf908b446ff92a543c557fc4d73b5e93aa0d38849e4bf255f8f16d0bc0deb5e6af1066000bf6bb947b81812fbe0d39d4d11170ef57fb900e35258608d2e49a89c64089a2178f3af1538cee328a67b770d24bc41677c844ff0ceb1ec6046d62f97ab09d63d81947afdf44d32307dd64b66f6a1807c8be1f1447f5b8647261fd37e70003bd042f91efb00634bb775ea59f7b47f72d230cef31a8e70185d9ddf33c7c70b714a282b95f2527b66b5028ac5fda34114070eded3d573d629b0cb3e9de6fd0c726be029ef5656f842e74d00fd9782ed0afaef32200e017ff159c252a85c5a0c03dad1887304023bfcb32da3165ab5106a5c394afa5b6e0cab24be37f7622009396dc55991cf2fa66bbbae78962592f75ea7e23f24ede7cdc237dd290b9f190521043b42d8cde939da366fdf4ab7119a9064e9d9229c849a913bb98e8c20ab01 true +check_ring_signature d694280cf9141d374e630abea21be26679aa7be16ac9a87fa3cfd6e356db83b4 1d2bba0f93b85a413b09044722c0e173d801d38d9ac99a8933f3660c273c279e 1 f91aafdf1a0a1932ea5508a49e1a5213683df0f37887e74bdf5d45baea57f200 5de6be5036b8d82133f05eff3fc708fcf39833139f5114085c04ac1a81c6dc9ff5fd7f5cf8ea086011edd34d792fd52e15c03740d69498f6e1c3c4bb218259f5 false +check_ring_signature 25552ca4ce4e6650909a7e70c9f4f8fb6d71d8350fba3523aadc0f4579540efa c444a127f3b6c1f20f411a0f273c0cfc12cdb116f9f7db13fa32579ae7868ed4 1 b522d6d817d992f3d973fcb990f1584bc4867aba11efc7b71fa69d91d582c9b4 af880951bb206768398165c6cc6dfd3bffcc1f4a9d69b81e92c7504ee21fac95be1278f34e1264af83034ac3f791f963fbbe328676e7f4f9004b7926775b5909 false +check_ring_signature d9d67a0f86f35c0dc76aa36705afe7365325e879e47cc1b05cf0507e7d69bc88 c21bff03c25c574487326feb11d946bcc1d949b5fd04730f7d6fcb971d9ad96c 27 6269bb99d786ceb2f86e171bd94fc75a2487b3e58c1e4d0287868723e6d7a978 2c24a2006df44dbd1e01242ac893d46a51ed8540f36c5d9dcdacc8123d4610d4 b30dbfd320e3548246ff1e13d4b079379eba9cfbb54800e5f3e504bc0d4ff617 548da1db2e5c47a5ff1d76528b1f94de5f17d6345ea6844715a6c705146548ef 951be3d245b047ced036fb3a0554ffb8ed1512c941394efbd09ebae6b56b1861 4d2f8de4b7a3dbd6b1558302d45f0bcac49b0c34dd6bf167c1d87c6376494524 213f01fabbb6fbbe7fa34207528c8b04eb785c3a5ace3501dfa0cc25da62e993 14ea64f316c4fbe04f8916ad01bf0ffa824d16b3978db7dc0c592c14a03b6e46 1ff183252f1a11efc8281e6555dcd2275cebc5dc32ca9716ebaa4b456dd3aaf4 6d501f3810b143ffd2e8a7a8e48d976fa577006ce6137676a17f075001e88252 0c4f47d47997c44188051e5db72cbdd7f5dd4e8e96bc8145fd48a884c2db38ea 46c90af5a259b4ecb1e4c65abfeb04f5a572ac636302c8caee22a8fd89fcc131 5d0689b5fbf35824d4edc09a869454568c51544d3d98d3d16cb73bd1700ed1f2 c9884c11bba6a569333e60b9073acb2162f76ed9b99db7adbd252aff164bfb46 9d5bedddace00c3b03a36d8402583a2b257327e22ff8045ab5bb61db690ea3c9 b19d3134096ff1ce5e4eff1b3700607d5f648e9e4f76e6c85fd06f935c9c0dc7 9daa712e84abea17542e18482a7fc8de6b39b410f37fd6f3e835a4f7a9b29c5a 5ea0d8b4fef7da7ee0e77dd21bb0086f7fa08a685b68e0d3f1af49ce3a0f731a d2d82d5173fcd2d19718f945e50e1577d4bd561d5a1a800dfd6f0faf9920530e 35c59d58d629c873b6f3f038a4ab3f70cec476a297d85bff247f4eb821cb86a9 e70babf6222e6f44127b8644ae74d9caf8f1c0db54e284bbae7288c2ea983d21 a5463cfb0cf507b8dfa0895fd9b861f6a402c76dd1fa4a5575495b65da5ec914 339542481cf2eb6858c9b44908d7a2783a3564eb3f163e46bbaf2c950ee1aa9a 93594ced2737dae707612857340eb03178154342c6dfc24d07afd6ee243381ab dd15392012253de69bb51ec6e9fc23e9737615de64e5436f08726abd20c7c2bb 55720bdf8f9dfb2bf6a3880b9ac98d3a0a764e2cd193024dbddf77e6e47e031d 7aa449ba54416760195538961dd08b34be94b7380b9e515964cded9aad7771c7 a18b2e93cf609a085505eef92b4f37cb0d1609ea1a0a8f47d05405fa58c879025739b4794e717751f6be3da8f0e963240dc5e18ad8de94b6be8c2b685e64b0059eb1c00d2012a69f1a346c1b3ea62495fc84d32ef43106480e41ebd16ee4dd07eca6e6f7f0136de38d0f8d6cfe9c7b465ae4a9029972870e21d7872c9e482f0b5f70bf639fff5756f6b8a9175169bad48b5a082d2d3ff82275c716a13399fb0b2384f9822a887195bda8d5113dd34759ef382aef0634c4ce66d273de18bbac01991f3981c4e08d7154582f107610fbe2ace96747e9beb99bf8990be7ddc06e0795e20e8e27f5bba5b73772c57687c901b081eac270bada52a47c1c3bfbd21805735c22aa035ab8285b13431cf313873719fe11b8713d9828ec0df0f1b5ad250e05a9bfd6ca2eccb3a168d0dfb78cfbb79b36cb4d2f8654bcdbb2900b5f58210fd9fa47064da25e3d46101010dab56fc94eb614ee670a29321f351386d9788001434980dc929a503ed4e0a9ee032e9e870cb8038c969761c44cae07b1ccb62407a878c74132d08f92657cdc5f3dd06e946055f31a8d88f4113be1c2d844411b023a406807ee1fdbc6cf663727f4489ea9e130f912b8124d8f9c8d819a59088a0755bec524f58039d80e47870b57390b3b3159311226c012f566559a4ac5ea600c68a1e8b9096447edca5c914aa1df2c0803dfff45cf2dfbe8c68b88d9e02bee067ab25effc2ab6f90aa0f44a109e73d25dcdcc07a3aadc1f24caf612a9d26960760dacb4761176aa0f8bd29200775363960a5c3ad5b78965e446f0ddc2cb55608a06938d5b6e2a8c66115354bd0a988aeb76dacb9744fc3c337485415cb169b07fef745e5891e67254005ef7f6771059f703adc341b2db0307749c487e41d780615434c62e2fd93f5cb84b896a4b0a677e7492e48cb6ed9b13eb9a15fc616d20c59bf4dd91c7cb7e6d74172e60beaede0a2357e7cfdb6f31174a1a6763a07b70faecdda8d1952e0b8abc0eb8b2632454e91c46776ab3f9f926a8854f3cdfd56026c9208df387d5c39a93572c7589a276bded515f26f2582c8d97e682c0d4823051ab14522fff722f4275c07425fedc637962db8c87ffa16454f7132ca18b1c204c30dc569d469608111e7c3ddc0c466a6d04d284e9dd78bd9f79698444e5a2e0e8a9851dffc9cf4d3a240bc5bc62b99b3d725697adc8eb255d492a2b0b933840a250480287e97398229f5e7a2ebb3de287648a922c343d9c6bc547e87351bc1068e4e5f394180c7654ee97118729821ee394093c8c808b8cef2d6b39f1861c30aa0825c466d588cf84b7c3389d92f8a09bb7bb03483380835a65cb5cdf137fe0eb5d0d83f65ba28b6b3061dd91dcc730448622f34b43c02c8e0fd08929bd22d04a41804b98e8b9e9cd934c5d6adedb9ca23dc7b7ce4590e11f1dc99e44faaa500842aaf9e9fd908714681cc5ad0813fa793c5b3be770b08977fa8d5e37434ed01c67435bc458ad54765c9566b6b328d17737b1d050c8679df39cf22eb113879075c9b8f2162e9c1f24ca8fea5399f684251f12dbe2b177e3305303c21d31e250c423f94040ade555b5f7769751fdcddd02f4f5b8267c64e136b9010b8284fc90b2d929dcebb61da4ef4bf272d4a4474877c7cb68993fb9456e448c750ddf41d065371ef6e572ba18d19db1df48cd3cbeaa7fffb999126c38f68ccf47c1da03105c181f6f6b44ae07de08a00e68c9286bdd3bf5b99d7d47666835bb99813399600e418928d42ae25d2c5e75a983767a1c10b3d6177f84417d5e16ba0acd52b810b796d5c6510697d036cbeb593ea002cedd86a02d44e132cedeb50f922e5f6670248f8f29c38b5c2fe24fa36edb6aa15129838b58acd5a9b29c4a061ee438df6096b72d6a80bf10ace4e7b2a5cf482f19cc2da318c8632f55ebab8f8c4eef2df0e0fd97ab70e4764f3f3aa76fc9840bfac520e01fa137d1c4cd68b41e474271f06e73467bba8cbe731ccd123b4d357c3f93448e35761fbc8171a70308f5dcbb70afa965577cdd0f933c7101bd8ab852574c90888b2df9c65d42c3f223f86c6c6010630d4fec0ffe6a7163de97bfbb4bfa1d7bc4e303ef4824830c84ebd3657bf0369b6c266c6d36d81e84355f3615b66b31affbb05bfb2e31a75834a18a15d2b061c7285c9474b9d4c85005cbf2f20e989713378ae5d3dde8a1335e0216de7f8092c624eb2772054111777fbec8d3f5ca0cbd38c7b9a83105890f386b3c2282905d8efc21e5c7ede65c5c735ddb02fbed1b128a496808590159fe86a1a286dac08b51e535cdf6c129d9b25a197ece0316735eac1f23d419be7b4fb8cef9e30de0f9f2023ce195c63fdf31debc97f2e05088134b11e0d5c3b5027425e53c7a3cc0dca7e38d1bc1aa93d85e37871cd58d2e6cc8c2971c85c02e4aa0cee078648c201 false +check_ring_signature e150bf1b55bb4bede4e0b2ae49f3d4553632ea5427f42ab42ffe6a3d9c8b56a2 f5225481c4719d6fa9caa10fd05f16f2be6a519689adc76adf7e75298155ff54 2 9697e1accdd1bed3ad19b5ca96f2c9d00e93d8c3fa9d5107d33fca49ba75bb9f 8438379e700bea29dd6f74752d97109da666afd6bca72cbb8668382400ba9264 5ccc225d3e4fba35d159cb5001841c9d7e5fe296ee9543c51260c98b92e7c50b2934745afdc4a87560cf2a53df3569a390e6b145461d73abab67b5e65a4d8c07871da9f20ba4b934c53d493409d3b17aca3fbe797b3053c5cb834f71cd8951057162bc722cc4d0c8cbacee6fa75ab65144700ba94e1724b271e770a6e532110f false +check_ring_signature a44d548bb98bed1f38ceffe56255c0828e25b3b984faae9b9ce5a32f70baae78 8339c1ab7e88f84c8d670f066de62059584d71be7cc558506c48ba9dd814792e 5 8d7b3a54ca716bc3613cd323aed268df8a73407c596cc25d33620b4ac0b9403a 47f10a7539d91cbb5ce587d40983067e527db7b1e242062224ae87d3a108e258 a5500539678aa21e2f34cdf049a15b09bf6093adecdc4cea1349d107e3a6fda6 dd52c863a418e8a57f80aa41a3561883125a9e3144952f5411d05f173f1af888 61b6abf7a3099c1ddb6c582e39a7b5da67c575c5a990ddc0cc1a2a6d4fdccb72 9fdc53fc858605eed204e8951c66aea4813c97a1183f1175f411a6131d2e640b74267dd59d446c6ffcc3996430f135cbdaa0e50933a01c03f2cca447a963e007f60a8e88c2370eed1949446db146aa9b0281e1e956f26b56c648603559425606c909412fd00d61e1f78d7af6afce108813adf90c26051b9c6395165d0a2d750eec4a5e23adef53d1ad86a75e5980d23f86616addd2d05233d6e9eafa6b0005b87670d809724dec99f0e65dd5d993ab490179457aeec0688ffd7726984b788b097cf53329629c6073de0d8af1301216820076dbd26f2a82901129358c14e6e40d5f7cbbc3aa6e2d12c570d0e9d9842da2ff6486026dada539301c7946ef5580010c391cd917ce29f62bad644c2f6909fa6de7aabda8a6e6ed23bc48b693fb5d0073832b9e34da6b568cd06783fe370f78e419422501196c0f13ce6a8afb9e1b0e false +check_ring_signature 0b0972d09222e36ab9511e186115fd533317908fe01a49749be7c77c4b291cac 3b94824f7196d1a6b550931d23d0403269ed63a4b3fac3bb221d7d10798bee51 43 c2ed84e2149538e412030480f804697189e7a5dbedd4c973012dc4de688dc6a4 10a9a1c042414658efaa9ff903961a93252459935b3584ec60e5e3e89f5d8d27 1d10ce574bce3a3979a7eff62779e497403945b2a18944f89ebaaebf3bb9088e 4eb80e8920b65b97a0d0f105622e62ee802778c0d2637e3c8e3c52cc51317116 f9619b9db5c18491c190cbbea50182e8e8975811df8f7000c3fe4d7af4f2f85b 5129707e8820f82652a47cfcca598fcd8d9a89d210fac45f6f8ce6901b45dc9b 081bcf3401f2461c0bc7ceb9dc3c22502bf4c10e344d82ed9baf839d1e344c63 0964b3134df79fb52e15816f4d6dc8311b449d9b2c18103d9820f9be80ef7d5d 057a4c1acb8cf6ddb69c05f84cbca0effd1d2d6b3f56b5ffbdca9f10de0049fe a2832a8ef569caab8bf9fa6f283dfb8a5cd78acf99558051034de51597a9a778 9f6acfc4c1d6fd0a9a29c778a88e7cbda51decb21485e8b928d54c8c6172bdc6 a62ffe092e270d20064e576106239571469e7eacc41f0165d93b90ca9c0205ac d1ae39adc63c958b625e655bfd7245d2f1ee838a6f225439f7ae74b30ee9fbb0 a35fbbe9ab043e8611482a971baccd72067173821bcb421136ab9dbfc312f46f 7aaad044e269a6f5cfe729c226213b91350a82b0bd5e3f249f0a2be9d3a91211 b96ccf994e4a7a4f0d7c3904a1ea27baa981588d1ba620aeacee95ca9ad80b95 e0a715aef126134d6bb4ec4463150f31c33cfb74d7aa5d4f06ba96b96d5329d7 0cc24264ff28226f488c9ba79257863b70b79ce8e2aa8d70d9b55d80ac446697 bc890f6b80b05d7645e0af556d60fc9095555b6f7aeccbd0a1af237e30bf6996 27ad4c7304e8f70b484a1f1860c1da4ee1490dfea5f8416fc3c5f00b8a26227c ed84d4f57538d86694eb9e965a9432944d73af01cb1609299eaf3ac1632b64f1 694c424e739de44a486d9ad8c1f48bf053323122cc16d0bbc2f43f50385f0841 56b3d1d31d3e3d02994588a5b4f64ff0d42f57f3085a6d79cd3aa587325e621f 532f5fe89b553987fd846b068380a3252889bb925c7dbf962debc1988f837cfe 8365bf5f3aa8205751329123b98e2d9884c821a85ac09c85aba8e91ff48e1ffa 6bba41399d77997b5f883d9a62aa0020a13d0c2630b8ac06b5e43bba432fa272 90b43e6921bf3334ecbb44afb7cdbf8deb7476f189b5da17b1a288628c3b3115 a8c5f31d87ed4ec3b1ef178e73113b69387a0625957af28ec4d4fe3a1280c4ed 81466c2af33255e3b44bfdd4417975d0a3471e6173c8c861c67c939664460ae1 abc24bcba5fa292f3c99ab78c52ef5f6fcd8680e46fcbe22716a0cb776ddc3bd eb5e6771b758144eed2bbbbc93195f53ea09e14e989937c6df6e37c154e7ae23 877f4283023ba0d4e27a86e2e1897edd269342d38ad80a4625247259e17c08c9 8f24b04bed8466a0887d0b6c3a9367e696795c97ed1442e0e32c9a755574a8c3 615d5318c46b99a3eaf48b5b449dc4547939e6f2be178ad6bed0a07a98be72a6 9b8374e61dcd0381143153e65c83437042fa822af14d8befce86b8c52ca2d8e1 230f2a5a327f22ab644cb6010cc95af63e1721271c63d71c20c71a601d541b04 e15e2ce8f7bb2eb3d292e3a4b1be22aae9e7030a40d48653de5cbba807a5a586 f33761d0986f27b72847dcb09634d758c6e4fe8433f2ecac1475562079fd7d9b b2a398a1c2fd3766df7eeb33506191996d8f01b39ba39b71d07ef7f0eba20b2d 4aadec7983860ee1942ee766b2877d652f5789917faa2274b324c3ac0b748ea2 83f52680cfed9bc27403efe40a38116c40188ee7a2a7b81e7fffbe6dcfc843d6 e414d5cb004296981d5d515143c3f894bc9636cbc40b020a5bf703312923235e 01ed698aaee52ddc448eb1735de127ce787eb4ecccf6dded7280d629f17237a4 22cc06e357eb9e578db32fd8a4444d22c9d16a3606eb6639449bd9efa57ba300cea858f32f7db86b5872a3a63f4a282dfc61d4d9d216e09e57d609d872a90507d9ae246e8dcfb59d8276a7aff1f97b56676ec9bd4500aa947766387c544c9835571db9fa7aae41ea0d24a035bc3f2d8322a5f51055b0439726352fd46dfaca01bc4febff11c8469e0d8b2c8b6da8116d734ead270c3acb28064de045a1f6d40aefa2bfefd6b5371683d0557b2f0aa23cc5a84948b38643c2f49a965d3137c90c887adfa458f9cf3cd10c8fa54fe0321593d4d286c7d402689d2291f327c80600e1a2261130991615a0cc4f547af2b5f12843a1b233a947c446760ac388b8ee0c8af4b2bcb8d5442646b765399534bf2a4fa900d161f54e1dabdbe4e2513e6a054a87413993da6a53a047d9d5f6a8d712636755a4b4a1b16b7beacc2e00f2d9023ee2f8cf60de99c238dab85b0fe26c4d8cf7954da92a0b305c3e2937493a96c47156a32691a190f018840aa7fd64301161c42b6039260834c23ff40d249f8f0eb18a03c57daa9b9776dd3d598826d24f9602ae11ab162c6dd552820acb2a1f08d5c1d8f0f8c282b3298fa2eb535c1042e8db03ceb09751b02672be80ff866006ae3448a49fb68bb13aed0fb681ae33974b42cc61850feef32995d4c734415906931cbe31f2cbf94dfe0d7ac190113c2cb074d47ff9c0e6ceab7d6c0bba8f900143fd45f3bd9537b5baf04cb752c11867bc26e2b54c37d2dff2271b476142880aa1a4cf7f9603b07d90fb1bf6c8fae430ec88e625cf0e73380ffe052d9ed18509fda2fec0acd1309ca4d4c074f2acea692a4e87cddd650229048bd1bc06308c08720b89b43bcf3c4ee7ebc9f3ce07edb16a460d67748d24831542aa0af4edc807fdf2839bae329c9cced0dee6fe1fc71bdb8db517741d8f4a81d6d53eb561f10f975c2b1ffb426d0bf3e1a50d8b940ed22a29025122b2b9351f75e0f14107ef0247668a8bfeec985adcbf8dc49a480d293808782bdbfccc1d264a2cdd6a0ef2049a90f4d72e48ee6f5d7dafac0f5ee1d49c2715dc5dc26bb4e238c58436136a0c048d8747cf51697034502068689c5d38e03c250a10a5d8871d833cf2a8c5b809634e9cd7dab1d187dda68bcffef727df3c762aff81036195a3d5933ead22c4067589da4c97b4aaa67917d1438c5347c82edf54287ee77c4a2c758d8282711001d63702bc3342d459986cf40a54a3fb48b89f388d53175ec62a864244c63ac104f359cb502dc2c26bdd7abebb3f1f833cd9ffe8bb338ceb67abce4a0ba15e160eb770d37397333f417873a75941a119a8b845dd654a447a788145a7fca4f2a20b79404dccf622f7ed84dc007b4cafb4530203e16d1200baaa950412f5ab31a3021f5e9a9f06f7b1e9efa60593d83b2026b2dbff5c8a88a14f6b729bd2ffe1150296f58f7bc61f645c5caf8b258485124583f45a58eb2ade49831866bbb632430c15d8d43ee0e5ba62450442fb3d67fccaf5d4dd0fcf0e96eb00aedfd8ce5a5c0d90794b8202a8e066a1580ccda51afdab8ec4c9be76a798da29e7571d26d6ab06dcc444eacf5f5930bb9a1f1e383e0206ef4fae6f5dff63de719c94ac6d142f0be87ad0f27c7bfde3598b962aacdddf4f6ac66da3b680ce1777c455f0502f8b012ada103cefc002abb23a79a386d43f4c036813510d8be7e7355e316ca7aa370ad6ac6e92ee61f89c738a7b4533940eb60e49134fa81edca0315778bc72926d0dafdca3000d826fb246442a150ca274b54993cb740f4a6483563f890a5d8c750b53e8ec3c7e34393c5a1e6befb83d33bca6eea6e417073cda55f23f2246392801128d6c26017d35cf44533f7cbe84d53d490cf238f059d7be59ceea8ddbe6b7065fffd4637bff8ece521037437574b93427bf6325e89adfe12fd633099df2b000592bddedf917b8886dcbb239227e5df4f9d7db8ec57b2d1a21d55f61d83a5f013f112f067a58d9b4b2d0d2a42a27bac4dd97c4b9b02d2684e578388a3b729b0b447809e9a2a3db08d6ef41bb84d2ba172d51f6a2dadb2b37b52983bf5ebf3c02e557dd465eed5ab2d5f1d3b343ce576c2b80d80fb0234e7969982417e70ee00d6588139a6cc408aa62f6b894d8aa26871ba98e0f7e6c6602b2d782a428c6b4009f1e86ee247cae04ec39040ee6c07164e85118b6f2b4b78a17c90b49dd50720fc0236f8b044714ad86141b12d3df060e3a2099693d9a63f33f40b0b2c306ee09a8913a5708719923951e491ee71f99607d0e0c5ee5f282a4b558fc172a9e210a6c8d07e411550ad0d133c2719faa6bd8c0d046927b0f88770b1001d37beb76032081f87867c296a9ee845099546575274d6014a47c416e9f4e89f74f4802c802a1d1d58a0e55724d554e1935af4100e162e3ea16720fd6bde0eea4656265d20582c07780a077b7e5fb84c83db960842733490681c046e4980bec2c69e28f48913586e2e37eb0540aa0e58857cc382e4266e844752eff2cbc80880f8cce7c6805bfaaf7abc7debf403c0e3ed3152a5eab285403a6f86361021fefef3012f9de083d9e6baac3f0357a96b147336b0b5dff8e8cae24fd76026716415dc2f11f890dece299394a8759029eaf44d177907567222c7ccf1ec50e57e3d6a29b13ba5d0ab34d3cce6addb8a953a20b6697848378b38b15b7a38645177010648598182c09130cdf8d1c2ef013497074551f459fcb0c931e9577143507f3187fdcd5f340096fc350d8ccea233d19114ec78bb407c37e3561bdad06267cd754b0b9c779da0e1aeaa3715f68fe3eae7d43f0c4752d86f09cbfca28ec1b7b709ac01c85856b0b976069f6263aa74bdd5b8abae4e32fdf2fb67dc5864f5006c5c209a82c980e0f92eff27830416f4ce7202f87934ea6036f1ec0aa5e33844602665971363b620fa2a9adc30733497e423431582e77d654e46c67aaaba74cdf6b780486a86a7a0c49aabb5e2aac1afba8db0f9ee154e636dc9f486263a0420d36f9ff6d793051016e4e05872e0ac20b8e65ae1c4f9dda4b779e56346a69ee80c4eb659571867a15c217df20034fc07be723064c48b95269c385467181ff43b16d4b94d3847d6702c74e81829b987b6b0d0e118a3d5e47c5e8a8e6bbaec08d52b9440c89f9a182f8a3b6b8070f6e09c85a57e169345f3a4747b42ff9b6e331edffb9eea34722cc0e988e4f9b12674c358b6f94a836c07c552607405cdff7dc8ea90969a24265a301bb89ba74e42595c0f56665401caf3af4598a0ac888c5261cd4b4b8dd3ad58b01230274f27e8796c595e63ea89cf424d035990ce96e2e94b9ef1a57f5ac4fcd031dc48817038a6ed96448c843678156512362cdf8dbdf587f3ef0c5268d464f0b5a7cbb078799200c183a9e28b24abf49452650dac4ea661b907278f9fad44d0fcb40b5691b203810d0bad62769d1fe1802104d3e83ed01bc2953faafdbb58b012c68b1d84e0779f94b6d59200cbee4a7eb225d01ba7c8a10a6712db0143c280ebe6ca0879cf54805e378047c76c50ef54b48673c276732d0775b28c0ab68a301d2f3eeb5df0f5f2824f587aa16b527d419f347031eeee193a8dce350109f0b011952df032ead55607b903c215f1faa33d5c38c3422b93d60b2457b2072bb2102915853117a29e0b75b790c1e5c0335cfa9560de4bd799c51d51c1fbb78238f07e864407fc3255c625bdc973986a36dd3068196a289aeb02addfacb2e0db9b80222269cca971dc8f2d89b4224d82fb9d070cdd2fc078d20e095a9649493800750cfc9f2f9acb6730799d023943d9c85e74c5bc49a8340afdd01cd37adcb921205653e7dd87bfcc2d7a99d47864550cdc082f0a140604d358008bf0585a042ff07 false +check_ring_signature 1de922652979d0a17a893ee08da210da38d12ac8f38c21651c2dea0325a31ffb 5344d477797a70edc8afe649935ae231cb337f4fae668d0cea7d31d7d733ec52 42 efc6a76ab911c37f55b445d503d24a7517ff9bbb01c2e27386afeb85d0eed0ca 1e69adabf246d79914706895aecf5075fc9011865fb4fdb93594d15d3c3dcf8f 502f7b1ec0dbce2e379419ed188652a8ded12327b5b1afa41b3a3971633fb5eb 3472997735e2521d7ea02f6c31c824747d9ef1d15652371ccf8c5e75d23077ab 692308578c4c3e6187858185d53ecbd74f5272f85f24118ae377ac75a4a70ab8 29e882f72cc88ddc1c2b619494385d91fabccd6369349a609e4672fd11af75e3 88ee211f83896464c7c50c1c2f5392d221b8ae9a4bc6249b7dc8bd34bee936f5 88cbbc0a2dfa10aa214b6a1310820220c9a97a39e139d0879181fd5ec91ac6d9 a026b6c63453813dd8dc8f5f763bf9ae83e3b0c63015da7727df929fb5c2e07e 2f6c9b74fa30d3d07492de0217a26e2008121e70f61144323622cd6b497b40ca ef502e42251e687b7422cc1f12abf702f860152e8b370e26691e78b5e08e0a4f 21129bb70002b06d052424e4ee5c60566c32e32fb33455516a19ad4682d24c7b 8928608d563eb53c962fdc8d5cd8602e4041d11f3445a0d0f911e8bf9c8a2482 ee1de0eef59ea79cda5df00ebbca2563a6d1c2a05e1180523f3046bc8388ace9 2407a9c241f986ff42f2f671b5ac46626353bbdb00d59ad164be623a06a8edbf e456908f1b13b2ca7800f982acfc01ece3d3bdb84b07effb621fc89c7ee97209 7d5cd6b2855b548cb0845946aef419ee9c16d9855b7f7c4ad74ff36bee388686 06600d20b5577f74321786c41afd383e2b1bf98ed96f8a5a120816c895c550ca 024ec351bdd318a80d00d91ba42fba459e3f7a7edd948b3b9554cfe58d2a1228 4e2b7a21eb0cd586997b55c89fb82faef5867181ce83805a8b3526938c263443 471c99b34f7e42c396ae3680652f62699a292501c34089006f0ebaeeb07a37e9 3aac0dcba5bd8930f6db2426f088fea90fdc39c197fd20da32f0908acc5e1b39 64648a7ae0b9c7ce947d77735759805c278760a96055c30eca297467af127001 ab53df496c9723e45f6cf2edbb841d69fb04a1aec6a3fe99bb224d40165e8867 df928ff2c13af3a1d5532a2da225f87ef376d9f0c2b7c44f37c6e9688c160673 056b6737d05c38d2d6abee74b532b4c483d539363618aff380ee335e9c0d884b 26450f2f3035bd3a066874304781cc48ad28ff57298df2cc7c623d4f41f4f4d4 329cf49a250e6f7e9ddf46463ded34199fa6acaab65a5413e250fd7a2a5e1ff7 2440654030361d89ef79f6700a7f9f5e26e30a64d67d9a9387ac96cca91143ee 2ba5ba81c29ae1bcc7e02cdceedb6275d330fece9eaa36dbf01afa726058c142 06acd4e5b1a442ff78bb1338ad5ad725d4eb7b29bbc90ea3655e625a83318bba 1389b5a51babdb5e971c6e863bf3bc137064e1aad29bd368d739d855704f5235 662be8c6396f10a7c172f4bdbf86efd02a8fce246cb6201117bd58187c71e547 e59796b762cfaa4907fc8bc7964d078b115e0623ace2d9dc96c38885ea455a01 0d9bbc67bc41d6944a26cac015b174426b19cc691e4abac9fce95aa2ff4e1582 4caba6cd2a486f9b1f2f4a956fd012784a087d781b75e7fee3d08258983f8070 f58f86c070e07776f73a93b37223c9db1fd54a2dd2bb3a8a6041994030905f71 4c8b81c260e46b4f2682982ae172d2bde2ff69ef95419d00ec5b2a177fa9c020 5fdf2635e0aa9f3a5eb1f9350b4fac4d28c6a57366a6686496bb6bdb8dba4f78 d008c474a6f822a5fdb580e9da190d9bd25a79dce9c45eeb508d85b25865a9ad 5581cefcc93e8a506cd57f317c93f7a584d565d37158cee6c2f338c9bf6eef80 8977a802ca7edad9a744570aaa100fff1f423a62a3cc0029bfd06c9947c5d89f 9aadd4c7cbbeaf6a9b8031ba9c81e987ddcd62b2b4ba668321af1f892f2f8805c248eebc852fcd099aa75de12401b84e339d934551f14d4f29c7d77aec9430093d99caea98ffa908ca3fbcbd641533bf67143a1407ca59a01b92143eb547ff00358cf5208c957f50bbf9b302afba42ab09715cded8ab4869a003e3f7dfe47600951c229e5901f47c1fb7a19e52fbb7279d405d2a7172be3dd6d808fdeb275e0c5d49c742933bc4f790d200b026220b8b7c6851244e0221436bf40f7e9b78db0d89ce35cdb8cafa0c81d6f8fbb3d11c3cf6b07aae69c0148cbe2c312c16e3c407e6d6b4ba0f266729d27879c903077df817b6186b85299de984880df5b707c20270ed8f6306387e1c787d76fc4ccfe6e890d9e62a070f85482ab7679594dcc9010ed9c8046c03bcabbcbd111c8afa511239c275815e20b559d51b3e20739d8a084650379b9810b0c00ab0e25670e16a1bbb8dbd8edb6a19a6081718f42888a10a802b5d3cd2e8976dd8eb7d28be35550372541434b5943dfae57c8ea407b8fd05df069497f22c894c496698a441387dde6c1c38cd32bb5886874e9852cc6fea0dc1e428e493d203eca5954f94ab3fabb469bc06e6113dbeb0650dcf6178affc0adde4e7cd433d8b13e55d2e390bac1999dcea9d7b9ae684937c4099ab0149680e1bcc6145518587897e1bf0b390ac07504979378d32bc8c9b9e22c0b2d8333c0771b16988910d2b622a98060e2b1f81621f81a2beb89473abc9a00de7eb048708ecc8cdefad25f2fef4ed1937f773982538b96d7dae6b69e347e619cd1a2cff09ce8fa8e53ceb9ee2bbb9ddef0765aeaca8795337e89c6c642953af093f22ed09a7d7a11318820fbb223aa9e87416942c82a047555518cbaac3bc0448002acd0fe06eb68e0dd71fc4ad66d459ecb5f4b2358f644e42d1f7902d61ed26322fd7073b34189a1eac15621f68fc013aa7360e0e770c1703553e56f107a7f9d855f40558f7fdd596f84d762ce77257fc591eeb6148e0b8f1609ff05528632c332670055366289d940231dc10b42f52925a1d96a3fbbd9b64b08c71820b2290b3bb8400bf95d7a760c551004f5bb77be3d5e7c8072f65e7ff1258cb0d5f7f6184d27b0b40d4299efdaae79cbac8f2e6e5c61f7366f6186c813c4a78a1dc2cd12ba19c0c770eb6d0d55f6141f78651e71d56d452efb33612fc9afffa3545d114347a8a058e2c7ccf57173c94b3e650a95220bdc212a0030ee7f7570b3dede215183de70a9b025c2f11a57e471abf35c2363696163b1e9f67ce29225bc5107b8a1217220284d8a1b544fda5291a42c2efc71a32705b1c0ec54d66ae883564f89a5ef80d0ed3b2733e25c0d705ad73e709ba77df7a7e866b1fcb195b239a31ce88f40916023e72bfd961d5c4f9ccf272cd15e37e8ebc61042a3f6f0f7e53bdb29591f6780f6584a904532463856e367bce2b25298a7ca9e7c3ad28b0dfdcc244afbe975408a15fd692733973cc3dd0b682e66d12f80dc0bd2aeb0964252987fca2d7d5290db5940a4588cebd472ac324c5cc4d6ca80f22b525c3a2ca373ed44ab86001a60938258253df8dfe7f64dd548f863ca770f1f335a0a12bc727ec3e5036fc9aa5005d5f71516b159451e7227d1d8d7b35698dd7553cc489bc0e60c5a01426296c014d4ec7193dad3145513c14764c8d6ced9f5274b8aea53855cc8e88a1608ba006b59eb30c25cd75dc86dcfe615f9e818e71f54d15d8579e58d8a2be419cf3060696c5671253cb9d11be0bc8a511dc0218f06f894d32c3c77539f4b465352a4306527dd75d386e241cc842edabc521dca4f3fe8894ac36884f23f31bcadbcafd02744293f8cbdecf7c4a5b99a174715fb2cba9058e40f2c658139ce8e273f59201f1706702b895cbbb1d9e8903561ab81f67549545bf16d3d4ed76d9c0284cc101de99c9917bbccfa64dcc711aed3f79d2f62b864109f8a6fbf66a8f934df70a0bc31f2c48c4a65d82a7e338ee42ee1bb630104d9529f01a454f2cd4bc1269d2069370030c46afc38c9370f3f2f7aa1127151c6c36b1b92089ad2cedcdd30d3e0594e018a8bb65d95d21fbd33b134143ef3e5edc6bf4207e44357dde295c248308ebcea162aac307ddbc584ec87290c93bcf1c191d71672dc70fa7295f7e6fca01c1717672b93ade2ea82a36c21ad242830ff6e22920f02b57714c6a676069d908f095fb1f559cff289dd8b24423bf444282fdf1ee76aefd077a503cf178337301aed762377a62ade9c0c5a8960d49193dd8d6b0955b95cedbb5c4c24a0f40c00213efc441eb446eda29682f6190791965790e0934187b876a855d4da52cbdf6044974b86f4bc20d2992cf5aeac515e01434d2b4128244e343849d3355d888530812471f19ad4a10fb42ae875ddfc6b4f43ae51cfcd53f64cc640efe96c08a050819441be243a4d3bce75ca63142aee0a67d4dac779118b02151f78ed1e39b150e8b5d8a9f83bf0883dfc3a53c961bf95e67a1a0a87c91de6b2fe400490ee24c0611c63a4e803a1b13f824dc66d6878eb4aca5efbe97b8cfa9daafec41c62af9003423221b9f0e5ea9548765f27ac5da8c32cd52d462bb582af0de46751090940b8038eb84e5620fcecea7a4a4073f2195305b073e8b1e2ef5ab24dc930f09c703cd119385c9837a2af743bbb8bc2da765186d2046780faa464730e24bfce4e4068b06a2f7b625dd599daf82ca2d044ae8d9a9985e32274b79c4e10c96a393100f7b20337c9c6db1a001fa60df236aa8cecba8f45f1633ff69c48eff0c92217a0d71d679e558a76edc80d2d1996c309f25b8b323a92d4f6e3fc403e256cad5cca0ab56d5f7d565cc250f06c21e2ad64de3c703852247f045ee5f17d1f9f811bf08e7cea113fa79b9310335df272b480b579e604ee2cbbb334adac9f9bb5e24da0ffe35d5787d1610efd6dcc976dc409c47778c6f12a77629d85415fe9f0e02b109ddfdfe52a513ad09e7776a3c429684612fcd918d65b910803af78c404d6b7304d81d953316bd5f1993c70639724d3c21c133fc72c16ef0271e0f4b01397cae05d25bef9ea7e0967dfcad1fc44de4af0a4669004a30093c66508fb0a1fb057b03313ab12548f3df060fd21afbe6bf612ebc5e4bfd1f2fc6f2f9afb3f1137bd4014128ffb90c29a7938601c5407fce970bd7c5ac8abe56f9f095097dfc1f4d7304d5c846b74ce3f4ff23244b8a7fb8cab30a75bf449eea4d759d41682d2924b008b098261689ba3dff81e263d34f9fd8cabda7768b2dcfa809623b3eea46935d0ae52dcdb31cdfa690980dcc24775accaf617007a90774ff2f8627ff413d875e00544229aafab520f9a4c217ff7b4f0f6499bf16ba57d7302a07d133fd519bcb0952e061777e5919c8f5f548103a5852ae12bd9a2a9a1c5b9b1e85029369c00a0e36081377baae50390d63899b17272e68cfe0329cf472dd7eb5b013e4bf6b3f0c22b39fa29d003fd0a2ca0a81b04dbc5ec67a0c2c7178959334e09a6ae45dc00c2a8af6caa0c02a0111eae78f14e80d2e0f0e9897657b1e5e3353803849b1290ca44a1b3cd180387699924213732d050fea453b76408e9f62c58a07092467b10d1def773f71c4bb8cfae091af7efa185b0f5641ebd74ce870dcefaaa6981e870d4eddb74fc37a6c208f5adf3c117f099f73c4f08411d1ab1b6ddb6a090f06da0a2bc7df0cd9f1931bb2112171537d61bae8fa81cc46471567a17347a110d2d80aae895657f162030712562ca71c371f21918fcbc84e05d8340d043b3aef65bb03 false +check_ring_signature e88a20ecd8a7f8366298af0e80fc6deff9143a55dd669e6827fcc8adf69fc78f 99ff4f692577117bb6959b30b4fa623b00f100040329ff3301922e9cb14a7367 1 709bd0e814a7dd47a19a896d78b1b0630a9ba717777dce5db221a5c3bab621eb e17c78ce94a51b9286c2d5742afb0da707c7b8250b1a05857b3465191166e8012843d1e087fde7e9742eb977703305b0e01365b4c9656dd814cdfd142cd22089 false +check_ring_signature 9fa11768376ab38d4a342365a5a9e27daacc4c0ce953f7d5069bbb6a2654b2ce fa92f268c1531195b3937591219a8cc9c68f2cdf4041013593ff4e01d8526280 2 799fabae380a3c1f0c980ce2912ac4104910a7cfb212cc5f5f67cfdd7323d5f8 c4ad4ff3371bfc2af2ec29ff148f8e46cc770aff92b8cd1bb2d67c4e7843d5dc dabdeef95d22b2105d0750cfb401deac8f5c421e6ec6bbd2addcb4bbc7bef4068c97920368ce9bc414bf3fcd740e6df528d807d7fa328ebe9254b0138a4c53073dad77db6e5a15507d17b3f1de8596d9bc52c6b6c1f9010ca9b41a927ba503079b7ed3575c5f486d8a78490a885e601a8dbe4d4c7554b9cb08379a3822256a0a false +check_ring_signature 7dd074b0c69088ba834846d3401ab980d7cd2f96df3d2d44ff77cc183b213a54 c0846b085af44ac13848a120fc0357a134a9d972443e83c8eed97439e1a7792c 1 a94b0291af270552d5d7ff3e590e78dbeb41be0b94c6f1cf932e404eeb0acb63 33c560626bacba885edca7fc477610bc725c291a3bc205c747d2dcead4d1b503915a6d2c37f30279c0b07246e2ca217cff3cfcdebc9ec42d33f0e7ef96fe4b01 true +check_ring_signature 1f1bd0794c920f29f26f247e09673084a2de7bd185e8cc014145b79d09898cc4 6e3f7017d89e8099d48385e0c0be42d9e7500817da7cf46648801c09f1d17326 56 ccc44511d735fa18a16198cdc520a20084f3675b8eae2846e121c5988cdd9c9e 7e04f8e40b3f45d52ec2bec8424530c8a8bef4229a9bd9ba99098716cc6151ec a58e52534dc944d127c4128fe0b65dddfd32d20ef625cc5a7e9255067d44b731 0f75bb23bc75b5f71600b0482ef3b73540d8e162829b2beb8abb63d1d76046d9 03d625b4df843b50309f374182290fd200a48e2a84bcfb7ba2100abe01e5d208 e1b289c0b07382c8cc5c7e9efe28eb4e2fa4c09fc4716e9361b8566ee4d6ef83 ade995ff64a980f2db17c49b022719e283e4752149593fc4db506372eaceacab 193df10d1533419e41798abc019889eb17437f8f80dddcf2e429800e4e687001 78d845c18bead6f40c58f73612379592d03da2439ea0e7ed5344b248ee421876 70124b2f28b9b3568b912004337cb95ee377f5a9468dbea1c891c27f94575c86 7a3ad2b412576a454fa2a5acfecb1f007239842bdd1c5a1cae6d2264176d2bf8 2b7d56b0a33fbc56742d6573e38eee10b6ac5311221f9f5ffc87fa8b799fe7b5 bffbf7732296f1e1ab3496b524c97fb52227368129da76a3c0d3fd4c32ce2d0b 8a78a45fad6db6579be3c5554a98d3f0f316cff6636140391889983b24cb4f89 a0d413e7ae166821b289567997ddc13f3afbcc943e1ac2eed3d4414c73d89e96 c1a5a45de83612da599c5b1c02abdf44b6a30f5a4409c92f75c66c8690527def 9a547d05eb1a9291c312e049f0215100486513c618ee290b45f64a3c54b6bd8c 1dff4651941b16c74548b0c012e317baa3b91835f491ab134a403dd0020bb2c1 6652970eaed403fa9d3f7264476e2fc4b14a6f3605cf78656a7e46ce4db7e4f1 6838e717b1e0094f9da1c90e1ca717097cf9f42d6d164ba47e4c94d6c6a1c3f0 22e32245fd0d1cd3021eca07bd33dc9fb5d12fbb201481b430fcaa4fdf2cc5bd 26efaf09c7b346e267b7a26fa94d890e74d756a96478bf955d66c3e194a6ac8b 9a5c25ba4f49906413ab3472fb726c0f901dfdd4629988a8563d64e1f60cb384 53f74332b6756f71dc7e257488936c08ed389368e48a5695e479627643aa2989 5c987ec52ac9413b7b7abc911e54436869b55aee375b7f26a8fa33365c6fb2c0 218fa2bf231f153f6222460a8cb4ab907ad637bdd0088ed4424f3843fbe09d9b 9fd045c0d4e88d14ef8f9a989a4dab636bcb83fa2b13963d49e0986d01e985e9 5ddeff62f73064ce44bd8e3d878189f7a4147eff086c5b05f6e1b14bd54a8631 cee387a07722f99f4d73d3c21db9a199a1372cdf759cc12b3b3fc13b82aa1c66 714b17fcf76a9257df2d000bcac013701deb401734e6fc2324742cdf46bf1ba0 eeb11df3f03f3965f521a0c36bb68c739b70bb9560f05e736822397be1f7127e 17c975c4d391f1a8285be009e2c343fe0edd9e982e3e8ffa0773bf45a455f946 66a135f9345a64443546575fcce3bad082abc35237741744a287872a4827d75f 32d478d305c165cf62fef8e0bab461dd5c6bae8591ae5c8e409a170d06b1d6b3 853f025716fc04f6de615211ca9131fc6fd14563c77dd5e304091a4c7097eee7 64e2de414831f1366279db741405a9178b6b4caa176d99cc33af2021cfd97e82 962d10e172cf723e89aef5e545f7c7fe276b7405ce9a63e320bfc74bf6f6cb4e b5e98b9076f82b3c2f3a41ffb17b4dba2529aa654cc8bcc6308e77342573f888 23a4034e85875f8abd13c2bc2096a1178aa77ebd551ac09f7cd177882311639f 475928ecad0577612fe4a957368aefafb8fd2a8728f30047aa92f56cfc0062bd bba904b73467ff6360f293bd65bbe9717a95c265320bdc6a74cd85a48f774431 4a060c4a3882ca0f9090e0e0c4259ec7a4a33aa84652b6a4ce5279a861a633d2 fb2eb5bdcdd9e0153aa4d8802c203fc8fdd20ccc1c78762d4771088e937db8df dda66a36e200bdc149d52dc4ddc651cdfbde258d38e87676ea16ea8b868b6425 eb2b37c90548da0ba3d5394284fc38ffa568be0c30589859a61edb3782cec5f6 a540b27a9bf9d29275ff6a30325cb52e314ed6eade05b75bfbf83cde4a1c57a5 553cf1832e6a1d965f833ccc0b78e0e3dcfac6878e027bb8f44dd63674a270df 333ba92826bfaa273ab773a7a66221b4e8d0bd284ffa690fa3ebf8eff580b9c3 27b42a861553531775dce912b920eaed1c9ed3b298ab5147e6bf1c8e96e47108 795af6074535ef523314d3686dee591c1f6d7394f142c375d20655461d30f575 96377ebebb8266ad66ce1955dc7ad1041f67afe8f8731224468b08ff34cb6563 ea486cb18b0c913e4d2d31e2ad3dd86a9e67a466ceb9acd28f50e5af72b9bddb 5c00ba3ccc8e601862e17d8785c62225d79a21ec1b7270e7731971b98d010de1 27c0e56acbb7413b7e7d8ec60f7b4ee496f55d2ab0a34d17f4ff829a5365c8b7 1e4f9c41b58e410ae55013c1a3b11534f632b7d73c395b6c217ab1c06719c4ca 2c5cd773f002627289a6d61a1837fac24f95dde0cbbb627aa1efb2e9171eefd6 44cf6a75ae7dd63db5048862631f8ebe4e93817cf95ce6e43dfee710ed70170e502d3ead1f3c38d63c911f379edda8266a175937d36926c77d9824f8b0c0300f74944a434a344917bf4f9015dc0bb965a026be4a14ce2b7ec47e2adbeadf7202112811ab0326475c79ac1a5ee59d58bb6a4972d4c01e3c69937fbee159b7ee0abcc135c0624dbcdc04c81b47b3b5a7292dfb6ebd48bdcf4fbf5fa10cc2bb9f0c899d194f50102dbfcd7f540823c47b53fbab8bef27451aa4b9c3211966f2e40a3e63df2a798fe2141271ee6f8d35b5a5df71b77dec7d88f7fedff120f431830d18cb556225bc448c8bc57d9d6b8cef8a31480bba1dc28ee882f22eb25766eb05eecbeb8a94badcab5a1ccb564beb4e071f78a1a192ac054b794b81c7f61fef0fb508e690b383f40f82d90127f5188a2b1dd2d8bc2f5cc062482359b46572ca0bcd4bd6a543a77fb7b055b2da85938a577821c0eef3d8bce019c89340ee5f2500a9a85df18fe08a5518efb64cf19d4dfefc3dd8da23a819cdfa18cddf0f0b3809da656ff2544708553e583acde1d1cc907153e101a844d7fb139ccb426fe0bb0661480b7dbafe79eb3208bee4d3f70bca199189c7c09ab0b2d92872686bfe7b025738d10cb7ea4a64c9b8e68912600f043fcd1ac95630162dfee5c84238ce5904c426941db66dab969e6470f657b3ccd5c843b7cbc71a3695f07e7d98d2c28c0293e1e35d3a5fe71d237dff296ec08fc598abe328d77b29245d1252ebcf9f5e08e723558109bce1de44ce4409ad449d664fd05e3cbec62faf3955abb9a91b6a05a08aeb9dc4dab57d8e3673401a8ca795f7b29f76a084afc11b862082131d9d060be3f74937fa7a74c41530a5c18152222e3d8cf68d0fcc333b9d3dbaccbd0309664f5ba16dc12fd9aca04d0cf3d11e434ea31f9f26d83de1b672056351c2aa0952631399d8197503d11962ef39ac5f8164ccc238fa1ae1670aa3a4b571119f05fa890ac317f5e4be1ac8c02692b860d4bd1ad2e3dbed96968dbc2ddc65fdcf0ff6257272e6aa4be488f5d1534a6e1ebe56588d563e08b3e2966336e36219fb0cca6d09376a53bafc1c63963dee93c6cb8457d6d681e42facd3f13a796b5846018d5e19af188f04f42046056069d88fecc9cf08914df83d61a1a89236c202640b438dc051d726ef7436b60f7d9a1a7f7333dbe83ef630b2554dc129824f6cf00ab830af498bd33ca0b28d85d77d8a26bb925e4d11f8493ca05d20f31f8252480202f0a146950d6439f9801fdc33aaf6b945fb241d3e5315eb78568be849b4bf0f38187db32d9253e0d79c06d6258015cdba19b130240c042904355b446cdcf9055393c45a0c1e6a15c81b4405ec5b39ac700580513370196de99c4bfee98c660d9490ad497e115a035065571319961a9df657eefe2341a61add7f34a45e66e40c7cc6746968730fbc5f92c4ba7ebd66c794abb28c5398565996c9562ab1e5770bf3ee8f225ecebcd1fdeda575c3acaae33d3c2d49b31e4dcc2dfe04e38ab81f063b5d810dfcd850c18344d91147364880c0ef8da4e3b14f61ccab6edd59ff260ecde0d6ffbce3b9fc17788f3cf3ddfc8ba3464a86e3911ce92264e2552bc1780a4060a918ff1439599dc44f84faf76e75d28bb6ea79c77f1d44ae6a17d31c2503742b0081f857288b54b33f4ae95f6d0afc9b217a932c11874bb729251889ef011ec58118f31daf67c4af0f1eabecfcfa4470820449543516d6dce6646322a2061aa44394edd851505f2df706a4fd35b1c1bc5362e50bd04c167726e015c9a20471d2fa564b979ceb21142672e1bb093717abe6114fd120cb96f3789433c4670e5a75ee9125bcfcd2d882d64ca9f77d9320b9d8141c5dd56efdd3eb4e82e0d8096f1618ef5ed0a54e74349921d46ece2e683d2df66d3466e64702a1383df1ea0d59840bd11d988b761c633ed1c5414d949fcf466c519396dd3d2fb60bc776c3097301263f229d8618aa7608f6c26db442c27c39334206b5f7f61a5a927a468a0acfb6a3f5b48d2e743ac19b47010e4607d8b3dee04803f345d2f49e5fc644820670eec5fdd8d72fba1e9dd774cd9dd80ef3f4c24c80764f0358c360b1c8d08d0141413266023e1dfe111399112a3ba0643e5ea32aca76bab1f0f06b5f521752061c0c7eaf857dcd7f34c1f03c3260293579d3b8121d2f56fa4d919a2e864e7b061bf7ab415cda8cb050d036421f2bece0adb41cbab39fe661b4c1690b5a96ab03cad50efb70cff746e6df2db033e781364607868eb0c4b8d86d2b7c3f78793f076fd5809f24d3f141af00f633a650a6418f1b15d3e24e3804e28598a637b2c604c004b579c453bf0e71b37f8501054eb296c82fbcd8970de89326a5e15e619d0ecf3d6aa5cdc685868a90c0ea2970eb4f918221aca91108e8704bfceed087e2046ae778f3e86f6884980d1e4e9b8855ed5a7372547d3ed5af5c6705781c1c7c0f891f75ccc021be4064dae137a52fd7e3cf5c28721008dc54f3cfe6cdaaa9ff0d143f07030f20cf549a12983eebeafcf58cbc021f4bfb4ed8b177a6b3f723d8078cdbd975121def0a0eaa4a17197ff5f517568e60a07cc7e55e5ec3808f76e10671526d3111652db9ab6ecb490739388594bb6cf9de0a63bff4cbc4c271104603e45e3ec936d509951d6d8f086d362f7ae208faae28e159ad000f879a958c60099fbe2ef5e86c5d62152d0e20ef1b4d601645a416a0343fc2188db83673321f0096891a2ae321bd59224fc3fb007b02af330c0ff2f5a1fd2682144dcbb23f9d02f826924507d810e6836bf0f5d6655db023ea76d39d205130277bece5a91cc70ed6449a5b055b666501389140e63944005ede74b3b4326599e6e8bfc849502707a626c8bc19501c54263034e32dc55b79c8c2b7b3df4252bbc20bde99a37a950d688229d8d6a2b02196b69dba253490f069cfcec651cd78c006851e356e47cb0dd7c5dfebc631f6bc1323b884a6ae83a52b975617b58a8a2ba99c7554acbd85053ab6ab06773d3e1c11002e54c587d93c467f8c8e13a56c8fe93f367b45ec110580a528f9dc4e3bd5180490fd98bc21d035743e3fa224cafb4227725d92f4360bfb29a44dba2467bd73dc7a49ef046f7e70b7343a4e27f37ec1ecdc4c60e8ce031cad36ce68716e69dac1ff98954acacd64799ba388478de86cf55ea8e6118a0f75790103e423735fbc6ef37a82f042d6dc599e7fd6d1a2bc7ba8ae11cecbbe08acdea47d2348b572650db17a02713ad8b034e1b11b1a23701b1c2ec0065de10f6f78299594327552ed4eb5a6efa0c3a65a8aa7626b1a1eca95c3fa6601aaff08c5f55298c6b4f9029a3886bd9b3eeaab8d71f9d7968eb52c6c8bb55743e1f801913e635455cc1df13ee60e80615b197e2304496ac53064032285c561ff32ce07fdab44ac8e96e09321e7ea78a896d8c21cadf7eb56215a37881c59d0e129e00939804d3f4be2099ccf9f795a4114298f4763e806c7f8b8af1c3b53de46b7bd0eb55e112ca54e2b4ecd1ba208e75e291afb7ede43911b9dff55c47cc53c8e710cf83efcf37563d1df2f0b44d9b86f3b5ab74ab79ba581cd2ae863a80c1a69240e29c33b5cb97a2c579d8b142cd12196af5c859ebaf7a78203d79395d0c6fc9b07cdbd8d6733ea465fe44aad1dd7f87448e0b3a3e693cd154efc9dbcea264d5e03aeb1f11a61e8c39f60b55593aad18a755210c0dd20453e4b20946c6b17bcf4070e9115badff299f07f795e4196b7b0225e00e0f3194137f246ba73e4a0fa8b0e054782dd374d0b498ead21f29494359a6357178c77e3197af381fe849a6cc50e8a6fc728451a90b5e5a2aa249b67f2b5aaa8921a2adfcb07ce04b8aba4a26000084f621282d39cfdb1ede8855544048a95b480690d48d7f1ac84c84c6c8183081670325e6e2a214dee1dcd267be045fa5ef4e191d5f0264aa9659576c6007a0bceb4107533796a1d628911c45fdbfbc709f816806af0a27313ebe50807354006699c578728a1eff4f8c233ca8dcf59fb3daa29a4678459482a8f41f2215ab60628d9fd304f51b6ef52227d1422dd264cadd36dac9d34ed95611a314a5e3fee0aaccd9e8cce9ad5792e6b9c7f877a773cb27a8a14bb390accddb2643d34fb8b039bc4a0778b6f1dc7f49662f66859e05d26f9ddfff5ba2b7bab2f798eea56d30c42a02707a9125637a1bae89bf4e2e0be8ad646d11e7b66c1e7933fe1951f650023f8ef7ddb917aafac7b47583ccf486b194fafb436e11b87e3e154af9d89c90ce23bec586ff468ca2c77e6c144a748f028c9446ee786ef2892174fe439cc05012d26f569071ec14c4d091f917ea482b39cbc3d897e34f255e50ce9f33eeef3081b3689acce67a6dff68b8842e94a4841d62a82692b194cd364e05818eccf8100bb992d1f10c5f49d3987676189e394b9c21fd229639f81b01ca95d70ee763b0258b4a9b7c0495bb1e48c8fe773ea93a9461a000e4da32e655a6f5d752371080a049025c2ec8aeff8107c02d38232bcdda1c2f98ed48ff83ab24ef22c20396a037903a1bd968d4673944e51f7a649d2d6069f41420e195988f648bc70ffe5050181c81949c63afaf49009f3762afc26246e1724e956ab5ee8fa6fdce3ee7237078cf96e934621e5c06d83ec5dc938b6d8eeff0f1367fe469b5ebac6c013de290908249570217d8bdc0dc0eb9723952fd05624fcbf963099f8d16044ed3e81a8079b5e06cff0bb0b60f176cde58d85c3a076f188ed015f14ce26fc9efa047e9f0aa3b5601b1bb01e99dd7588bd3397b67dab7ffbee04c2b60b9193701be800fb0e6934ca2a2d6d31faf2f36d0c853e2c8d4944fc5d0e0e9a1e1cd241595519f20936c55c7db7c2f08bd11f8edd3a6ac8f9b5652d6929ce279ff829d343e2a2bf0076c402cf403817ff6899924887c0844af7c96b2cced7a2669f7caac0732a250d81d2cfdf87849d50fef1f67bcb54a891540b6658a1525c4e094410ad6978480aae6b4b4fe3dbe724383be90f64c05e4772765c2157f12c1924e0f2e37237b00b true +check_ring_signature 5beb00270eea75065b1bb34e8f9a1fa7ecc2b74902cb5844d39f190a8847cce7 3105e3357b9a15c1ab8689d40b553a86ac657f30b07f601c57a7310e82a46cdd 7 cebc417f26040280177f63c99e8c46fa3be4335c49442a521ae3ff155ee55309 1c29672573243c98ea8ba65757429cc084e33295e9f9c403711874c394247a3c 66f38fa84cf55d3126d5abdb98f6c45cde053344e3e8cf6ec1bf23513b094fbd ddedb3b696418fe88dbd4dd69746bdb889ef03fea05d30e56edf028ee979f012 3c99d05a90c9b11308294f77cb06fdfd7a0dd916572564a5100ca58880225690 f2d81a2d96d9e2fe6d400460decdf80a1821a5f753eb6da730650a23780f1e0f d9c52abcc494729180acbb054fbbf4becf2f1ab872e18985cbbd2eacf4dadde7 987a421c7e61ed2c0ad215a22ff09de64ac50207d8333b6487e8a5b5407e82035343ff0395416dffc09aeb2cc58298b4bf92710245f45f865a278cea4848b2025e3de28c9461e5dc1bc2c2737af8c1056c2375e813e42358a7d4d4e1e2c19a0156145aa607ea67682bfd0c3b1f61817cd64d14f2bd29a3460f6e01d2ee2d920607581362341ba1680d870041dfc17fd7dec2201a889844a52452f0043cefc30cd47a3a54cda448c613a88ea76115c05a7ec345bcb501bf6b75197a2c197001095bd7dfcb7c3f77857471ccd2697580a6a6eea319b142810c5f03f1c19c85e60a671f51866f9b00444301835955456f3c50852aa247ad6d57a2c15dcae1df2909ad6c1513363034ba3642099101616e900e0152279605a657d2771819fb40ae185e6b41bffe14d33edf67831d3d17229993c1803e1222534f73c9d0c7a978e9021090a56bf6cd1487d7dfe5f6fcfab6d5a50a9c5498407bb4f28738f84e41c006c21a9b82ebf22198b6f0d7b2acf3e24813828550ea5ed2e51c83a8f7c047e119a5d56ddda2767da58f13b6c5a798c3db4db0c29806af6630dc1cb85b3e7117085a28412889b151e4895ce171b995ae19db186645aa46188d642fd8a80609d70e false +check_ring_signature 866da671a0e5613a5e3a413b2e41753a63d94d0f46af75089f2f8615a4ceace1 0b059b73ecc3c57d1fde293fd7c18517a03d6d60621a0e2f5d7df88eb8c3791c 107 4354cfcb2c6bf7c6dd0f5c7b9af27fb2ac8e0c32669f3168e54747a72ce8db3f 3a994a7daf215cafc4106238f50bc60d9220ada77099b6fb84d3a273b76d23c8 16f3eda6124d3b92a0b22947a8b98122f3c050fbe14ea1364b1f1d13fe06c20c 2c97aa1731aa79e167010c301310ef620a41f587aceb28c2ece31f65cab67924 6db3b6ee3a1cd393bdef8143e6f2008881f39ecbeae1402f33635bc13105db19 86dc4e38da6a461e1470711c554afa104b3123640befbfa03e14ede98d149027 dbfd7401356c7e7a864dca77d08bbd338fa7d81aba19ff07c83af7d92fb18012 7804fe044668999ee772e57937a16b5a7a763e6dce8b87b3dc33cb25768e3106 a2a381ccd2cb3e71d15ca01687b5b9289f88b3fdc59fb90d49093c3b7598bb51 f252a2b1de588389303d66b68babb046085299a630c948885289f6c3b097ce14 6f1ef856e29ac8c59fe4978d3ab0eaf3c2c92741fab717ca0f23764513dcc893 6f657219d95168d113e3c74dd0b44c349523f1a4343efbef7838c749546fd479 66d4a32e7a1b1a312e8f067730a12e324cb3840665ec7f8b27da3745212591de 7abdc66d63eb30005bb3fbe0631d08919bf99df29e6c87b334f7f1fe301a9ea1 7c975a2d672a32bb9d831b1995c3d7e63912a7eaeb040603eaf4fbcc58c28867 9e402dfbbc06bfda69283c2b4790c8f9e64ee466df391d2a30d02a047210b25e 2103b7f82b28e334f70175744785d17eb26f717c9d1dc2d55e32574065acee1e 71a820b67ca28c5ce1ffe613ecbb2844b04b8513178406a195e9cfdf8d96f485 86f4e1b2403bdb67fc47df75bed072a0e1a04933b175ef0521993bab1ea86f7f e9788be3acbb6a7bde02ce088e0968f880606c305df0095b5044c211c5ed8f80 aef778267e449d4414039af1515918aaa3d2e8a0bf97ea7619ba920f0034744c 9f0f0d6040eab414a62f70f92ab0e575ab405a76e24e95f9f7f305fdd889a987 186fd40901134a05751f5352e61c375154da3928cca0c3ec08eb405c9e079d7a c21980c6c673ae5ea5efa5b5489acd5570f14e649250d145ab0be66070b68aeb 711140b2115761a6181c8b57aeec613cef7c3cc6f641fb7f0d56a20f45f4098a 1280b78f04391031f80c68f038a605d0341867bd7aeec68c13765d154d65a8b0 012efb844d797e3ab5a01e2b04d00213b648753f187bb4971055b374cd9b9a56 f1bc9f8fe97dff7945164481f4efb7218c3a9bb11402b85f96f8a175735e00dc 763265253398f334efc128c44ea1231a572eff0e5de5d42edb6757bb86b75d96 aeb22c75ad2d60a0a71ac3e6f049bc6ae4b0681f3a29962e978d9f9e132bf765 8be92af097c43e3dde495646862608cf6d585f54c4ad1e13db37f755dfe022d0 5253c76473de009a45c722f33a334e7ef1f4418ab9a5d94b6e494a76b19733b5 45626538b67bbd79777112f9e8d1ed0e0311c6f4189e364a583f360a30e766ac 4e15e37791da7912ad2556dc0dfc94e268c10d813314476d927e27435a4f1f0a 9a7fa204b9a7e7631ac555e70a027f8116173ed47d6086d04664b2da43ca6a92 652fa5a7a2a9ac19a3f04294750c5a29bc0cf5fb75ecc1fb94b0214ec4f97c23 c1d930b0ad3684418d76cf09d01fe803a3a3f9779e6bc4819483f2137c859b80 24c78cafac527185bb7edd6f9333c745ad9995ca3d33e187cd262a87d85fe403 4f3807b9f0f4a3d5c818aab62919e5b0f2ffd56dacf7ff27dd2ff86c94a0c8be dbec64bffbb430a41bac03846243526ced966d34f121adb1b7f0fba88ff22661 19e124c8beedff9e6865d4d092937ae7e2fdecd12bc3b2046df0e4c8466ace9b e70ad31987b472a1328915fe6515db8b990653ea81b573f270964ae442e443c4 393dd1d01966ebfe7d267e82f7e8af975b5765aba609176ec76789e5a5a616a4 3ce71fb0518688170e185fef4cced51b55e4848ce53c872517052a691fcf00ba aee152c9f60cb066047421d20c410a9891ce45622e112abc67e1fe925549366d 3660e9c95b5e8720c330c68c6020aebdcf177d1478e8629a8a034dcdcc8f7683 838541137e19c54af54dc861949be9e8e6b21cc8b8d3a684a346833c48b11b01 e26e7947ac56ab2e0cb888b250565328a1a5b976a2e5aa5534a04ebbdeadeb15 49eef8e61814d434659660ba8209b910ffaa17497ff48762a327c114a263f5d9 60d875e37859781285951fef767c46009c19f36c50d5ad1b48f9fd764558e2a6 be519f022f14262d622092e1f3d6ccea229e8a58e9003ab2357e9f1a99dae948 e4052367a325634e447932402a57ab47a0350544edbf6d67dba3f0f6ee718ef6 0bc57bcc41db4b79d94fe3673b2f8e35516360b1e40026b9b0460bdd807a37a6 429d7b4a57a0997aad0ed28b85663c2c80077c2f97c2c600120bd7c59ef2f459 f09dffe1397fc8217e60f8ff66b98ae157c70e6447b0b5eaf97f623e1ab9d32e 0553fdb25a951c48bc3abe93905f61bd343b2a1104fb1b06f669a70037d76b12 5f9d4ddb1450d659059e9f381d0bdad282565e27e0de4b8a583b9410baada460 e3eef1282478de6f96e04337b519722c8dc2835a1816fc671d6785fa6bc7f780 4795a98bbefe5458ee08066fce0d269203e1c52a4dfdaff38a87b48d075c1beb 1e67e09dab2c12354aa98838a61a2ddfd3f47e68f7279cf4fbcf50c47212d018 fedbec61743b4480b0ee78c5710838b49c1e8a5b1d6f34a16939e0d2f07db25b 508dabf58f841ef70314a52604db36e8acb51a7b277990343b41b673bbe3e44b 161e0a1cda28df97f8493de8ca3421d01a38c3cea6886c87ee618e1c4948a7cc c549489b6739069636db1026a79c87a207e55bcca0433aed9d1c14b26c0d8539 aed0d05a2193b21c96768063a12672db0e7252573d979054cd84a365e55202d2 eac4ad0933ed6be7f99148191cc9bd5b48677185616ca8cfe3f6f8bd6584cc43 088b5cb3b0fa678fae1e1c3292c9b6a3cf96f08ba765fec3979eb219ffdfb61a 26ff87e4c25753effb82f025be76c0587dff567bb2702410f9c08d2fe1993616 e5972ff2cba9d648b9ac751d058e42dedaa4acc9798684cadbe838c4bde81751 af38dbd72a1129f23b46ac1e6d6daca2bb28d2cfb827922b93eb6bf4e7d2e2e8 d61e72d5ae9417aca8ec992b34c0b27ea994690e3cbca794acfc79c0915cfac1 be5e023bee6608f04fc93b30d5b2ac7e0e9002291eb5bd1f0ac0c326e5ee151b c5e1117df39002e76eef7bd58a331419cf059da45c7b0b567d5576b57c979725 652e027e94379365e94535b1863c4019c745519e3a29ff2eb1ddfec35ef58a34 da99da7c14e6e4dfff8e1cdd6d15fcc2d22ca0370bd97af54c37e52340a988ab ba6f938eaf8380fd9c951848c02be2188b7b08fc46b66f294427532bdb7da46d feb0b12dcbd6706b7e2c4dbc11963bc371524c62517cf88c3201f10ad835fa2d 7ad745d9b4f44b27aca56f182a55f7e4d5b14b372e6a2defb68c620620bb470a a10035b87048de248bf0afea590cf2d13bb0a56c1812ea3381be0f8b209a1ab7 7b9e9508219344ef3948b2a996b7229ff316695e7447f0906bd6ca512642ff7d edd552f5f1a0205140c9f6982dd60644fc243e6cff33af8b3ce881ff901372ba 7923450dfedd78e3bf662544334cb961265ac79921bd26a27a6d96ca2f089e43 3af27e2537fa6fffeded16c1c26022073d2a52eb4fc46040098a13c085ceabd4 0d5508e35c28ce3ba92a1b7724f27914374f3bb724a6e3aae321e6870c44b2bc e486af1650c7c9d21a4dc008128af8f1a25fe99dc9c5d9e25df3c75afa8ed4f6 676a8800a78e02de52818480b856f7d86e40fdb752ac1b2f45a9e36800ebd808 bcdc6656dd052c38c3233e004ee53ecb50891e621af8d24403f949e39321521d 4d4f62b2cdcee0bf723f3018f814052c93266d5867cf4c62d62ee93a7ddbd6f8 3a7da55501551aad9d797836ff302e37f3f9332b5c0438b743eae1aab812f007 90054b9a0a5f55a624365fa6910b6b00dfe7f049f681fa504fcfac3a36f367b2 1d38ac21f014b038f9de606630ec433102c313b7ca8e97cfc37f9196832ed359 31b276d93df676f99e8ee291cfb3a0bbc8de195e06e5f2e000294b20cac1960d bec2b2cd5a53fbecdb0e87658fecc4cbc6923a5c8fd7e0e5370344b4c751be93 70154e2a28c2982a1cc98dd2dc7176c2d70ff0bc8fdd2c5063932d10da741265 45278c3b12ae18f78cf11c41e365a11c24ac85647e81fa80d8faee10502469bd 040857b64b6249fb4b9b8c0a2806510dbb964539128c6dc46acd19b42455910e 01f78cce46dba50d3f243aa73967a531a1ec42654822360ed181cfc756c49789 0e944c4798e4e3633cba1b42bb74a530cc70ea4e1324cf0c522de565c492bfd1 e2283262b0dcfe331455e52dddb5c8eb333c2730fa16fe69640833ca90047e79 820541bdc34474ed35262cec6ee23082584832ea61db26b26c491d93f2af7873 3debd824ff3eae455d94451c83bf07d313c0a84db4bbaf3a276c7cedf8493f9c 8a74353f8c78f1b34165ba30203365ebfe66ac59ea3b3aad5627f5c41ab741ed 236414b39196dae840107a883a44fb8133f0b774f08c0c791086e495b39c3cf8 543e7b02735f75831dc05ad143515664521635c81abb7b4d66d65e9bfddd98ed 8adb3812f1048e55e841c7cb194ad00d5ce78b37863498c3e3eff170f43b0342 69ee75cf4e9e52b38cb0a6ea37ac7efe75299b9ac6e264d305e3b2892636315f dc838d8c731abf87be737bde1aff8110f05c9793beea1cb21544df33dd4d5408 7ec9bc1671c38ac92e7841afecdb304a88d26d49164aced5f6bf26e87a1d6e05d2932693ead7a86a920c2870025ed56323f5aaf8b04af274dc979a72b3872602481986f71ca98af8885a8ff906d38cd4efda84556109b31476950ae4a696c0075c3faf452a4e625a5b4751399a8fb0890215a7e6c1dbe7b135b9ad9f36c7030c38a635e0dd26d44e0d05d74dac85e526dc6925257d518d834da2b5d24d53cb0c27b4c86826fb20574a5735031c3ed4f9fcf88f0ec37eb5830ef6ab09a5b26f0ab5a5bbc839419b79a972173bc825d6f43d722cbf750058226d32364c45f1f60b4d35af45c46adeb01afe423f317eaca4b65732f57a29b0083ff2a832bafba902d16fee27d21a62d8af9cf360ffcc16a1ad1103f918a00be0d8a4c643786a46045ee33337152a8a1ca859a379fdb6d67d10501efdf1f97281a0c4b620dad74c008669bed69b3824703820badf5caa2cf1e0335cb7c6d812bb3cc487d9e6ed7d0ab65147031f2f3070c39ea9eaa87028801fd38fbf7bc91646bcdd020819a38e09f1e06ca98f8fe6a47989957837d635d5c9ad988a2cbaedd8a6807f66558d890a84946c3cbc0bba8c3236c967b10af291165be4a1fac15f2b5586eeea3cf75601ee996bd71f477117b5f29f0b70991356d07bb7eab4e34adf7d0aff5720b732093a6445c2acb8fa9deddd2eaae152611aa783ffc1031fcbda2922a55151ab680eda9e0303345332e280c6cecfda778251d9e04f0cfe833620665a2d321ad6bb0bb3972dfa1d579a600fc161dce1e29f37a646a0933a76cf62633626fcd8b132064a58016e704ec803362cab3d34fd1f863623eaf48f87d8f2a692f803c27cb00365c8e79c0a657f3f90132aa2d42a92f69ddeebb87e077cfc732d97fd0c91ab0f25c28c3177f768070e07ff49ca142c7c18d2fd80bf10bc41da6d01d128ea400a9f5d353b3924b415b8d8aad32d6c34085a37e3ba238c72be3baea9a6d6dafc0803dbc5f1b4ca3c07004c7efd3ec3636836a99092d831d6a306db498b5f2aa70de644682d58a9485d8567901368f0e812408e801514ec3e622a5f11a1f45ef50976f644b2cf6e0415fdbf14548458131742b7a9ececf5d1c3e64a4cb0b7fc6e0a8afcddcd91fca4334ca9195b69fd7d8b437e163c52659718df6129a9af59d60bc5822e39e809faacd537e7a5693e82a4dc45084879ca51083d9b3a55a8411f0c4fb95b952c09b9297c02147e803649914143d84c52c1135a995d8e3b98e49407256b6a65d5298299bf76e0616772745659322b46167ae2b8583a1781639c900dc389132a1fbe0218900d8d8254cd1fafbac4513f5c9961b5359b16c1ec5c9606ea163bc165c494eb5ed10b0fd0e2bab3e621513f1d49e9ac9f9a1cf9d44b960ff89b25fe634c95f268537b4b7a88911d3cc1c29a85aa87fcc07e4382b8f62807a384054b3204a3ced4e32aa9a1e129237365b7c7be2d1979afbfe46b48c7f00b010268603dfaf5673793f05c15048d60fa858e18f07a9247cddc902f841ced09197e5bd60ca8c7abf72bda9c4f39318411849a4312e7ee0989ec122bc1dd1c09355f84eacf1f92e1cacb6cde04fad0915cb5a72296505abc096e02d0944e7a011a2108cdebe0834993b6df9cfd52eb12dc75dacf0803181704f00702143e46077f59746b4d81cf6cde4304cb9f117eb28b7eaa0b4ea57a709c0586f7a6e8770a3e0411f6be975cf48c5eb585635a52c12b431dca9a75c10cc4873ba7eab7b40791e535a6ade08c37d1cb213065f3a8484d7a928a19ba148d3568410eaf0c650b27d75ef4deecdb3c8aa51335186995982930e68aba6a47957c1f77d938a6050e6724bfd01a398ed17cd0861620984d481c47d9a7d426746b9025589465c08d0e6b0c8cc8ee3f070ac35fbd051a0a828d6415b6a49b314ba4b0338037c80be509e8800e51e7b2c1d797d38f74cf45f28aaa3661d902c9fd398225bcda6a8ccd02929f3deb0531dbbde1c1074d1403572ae538543ca920061a4baf4591ab3bd90947a9fe1356738e64be0567f10cffae687bb5b02d8128496246109467c8959a05c356fa07ef1e1021062599b6c57260863d7ae47d4edbe5bd9d0176d19f852c02286c8660f8a6d75b303cdf5f4bd224eb3e131dfc86bb1f9de5fbbf48036daf0aa1e685cc40ede29a5ec87206cb9c6ad396f121a648ca246bc735209e51252f0d602339c07b357c7b10fc793d333c6f7f3098ee2d4fd213dc230fa2e50c3a2c003f20db9e897177c977c6169c3ab4d0102cd17e7f4cc7a6d51bf35e7d7dc37a0aef4cdd5d45cd86cea441923d912a82af4cd71e69ed85c21d16ee9dc3d2ddeb0bef623c9715589eb1ab77f040b7858e9f80a006ac32d5f12df25ab8c9ce050e011e1671a1c323152bda382ed986781b977c30cceafade4cbd1c9a23aa57fd4408da516ef13e9258d63766697b4c8f56aba177c846aa03c2f8c4645eff8b98f50668ca5eabd0fe54e8b6e9515f7b73ce9b57db3c14352a14d0125bd74dd3eb280213e07fbc0bcab2d1f830bdbd61c23c93f157daaed405a5ae97dd155296046e098af62bf8894e5ba12b227871dc581c9b6b3c27f2eff5f9dae625db48dabd64034b223b906a11d5789ac3709226883b78abf53e01b18faf4b193e95c193e5c5053a448fef6ef2e571f15314675be13994459b07c1725293377001e35ef2c5d807c3e222f4ba6ceda42e0fc4e135907025dda85cf22bce3972fdf48ff9f7a6ff0e6e2c8acbba3219558022eac1ce7d6e199dfa9dfaac958de16e5cf250aee1f20598fedc14d21374c544f168ad5d1eb2e4c3f71ce82b802ef17cb2e1a98f45170926c486565f68f3e5eceaf4b5eb05411b88be58ea5fbbf9ea354f5a204f4f15014547d17bc02ae47bfbd7059c47c2feba003a348bdba9a5b4fc772945db8d0b003e48f71bf19cb7a78f6fece23ad6e55829acd7e150311021eac1623d97397203e49e7b5b1ae685742564cbe428c1accee5e9e5a8f34d0880a4071d63c78d360100a5517ffddbbe1bfef65e37067c73e2d4d84d98f5209c0fe85a41d8e8457a00b5c3d5ea94b273850b09a9b03c165cd3aa1ce55173b36c670f5c96777724f90de9cfa9b48234e9a29e38f7df581d3ea690d3554dba9282bf44fe13f83b25040ddfa937d59c57584aab6c8407bb4442cee70a8368d4772609b245b6582852880296801bc2b454ad0f6c4db830b381ffc613f03bcaef472924d6a2f083e68c92018870bb48c11e03b5a07c1aed26a176ad87e1ae8e6770dcd58699c3c93ddc050fe7d456a73c7edca33c77afd83665228c1a0e993e24c8098d6b5b414dd34145003d248303e30bdd1f00b794e87c207dbf1dfb2e6301bcdbdc285765f8900d4a07228ad51bb796c08f1b576189b6eb88c14e58491e117a59c416f13da8e71fff0b9180ceeaaa695f6de778d40fff2aa19c64a508c75751ebcb44d32b5bc5518705701a19ece3de045d53af7a462632069fcbfd91e0ce217672ec1bf903fa9f4b0ec3f35a5dcd7113353ae9c4afa56c45463a2defffd56cd5cc311e12f1febd080ee75185e10a9a88272d4007983759c492ae5e006be4d6ea22a365e9f3a7923e0b11699fb0fbe06328f10f010d246d389912f617ff0e593bfe0ebe68511d81c701322533e33c29c477188171fe1f77d2ddd24950e371a3faab1ceb3c7240c75b0aff64d5e96678bc91d59c2584348bcba6c4f41e95b2c5e9f1f297a3e637606007a2f39fb2d2fbdf1e8080fdc91cbdcd9ba620f08d796a7f31318671682bb20a0219976d43e8eb2ff25b3396e9959c6f521509bf50f37deddf4cae499fe4106b024b199d59d71b6d46305e56259a8ec05a75752c80a837d539afec26a7f92de10ec8a3a0736e242ad6e9db5c04a504374e39bdc283b9e409d3ecef0e4cece33a0009921efce8d297ca7ee8e4a04ae04c6d2aa3a7e1e3ca6ec52ef50a209046370149cf154e4acc0dc6f3ab6280c1cdb3dba738a6097ec9b570662b8cfdbd0e1c0bc510a90e99ad1b37aaa211cf685093c7cad3cda5cdf1556cc01bbfc4f121120947e171bb5f028e40393aae239bdf9e902e91534c98280172f250f5f9a8cd620bbd80f242cfbe3f3f77500b26636b8fb897c97bc1320920b650775ee265531d0eab61e3ab8c13eddf200e120bde70cef994ed7c7de12c0ba234e96c6eea7c0204c7077bfaf5b8fae3ba19aae3d55bbf261dab6c305230414afd8db739971d7b099c4cb729bb691d6b82fbdf80c90860c4c3a24b928cd0382263a21d6adaf6e2004cde62ac181bec41d0682fe397dcf18fe55b9a9afeed64e86ba5316baf36250eb14972885b8e902c2f6ff13f1788f502ce43efe4728e66062e75382978de7409f90d288d5aecb4820407bca003c4f23c6f344f815607a29f9dae9fbd35685b05e63735bebbebfda30b7b174be0d697589836e8188b2ac909264086974cac220b4fce2028fd43f59d52ec8a096ea94beae9ed2eba5667cac77674b07fbec2100a6016800b3f3a458b2337df4e147fe2ce4a9c41d85745c1fa2d16d7ea99ec22089629b3717f5aad6fdddaf7176e0888fdafcadc63199c3f5ae72e76866eabf50937a16657effff5aa45f5319cd7e9f109ff64ea8c8a0b9a4dae097ded3fb86503ba2e5f09d9a68fe81648913263a6bfc8c8642dbebe2be43bb7a6b8608570320287af9bb394a657807e83e91b5cbe6c1630a4fb9150b0de5b96be6249e8c0ef045f51da97801178130a43402c70fab4c093266d5d4a602cf49d724bd6dfce57031279563b1ab458edee66daef3732f671e6b301eb654bb397cc7b254de488f9040dd9942f91ef99292f10ec7faa5ff4dd2b523c3170aae6b24b59935625ff8708e434ae9d56a1a64a5b87efd0c82690cf3e599a25fd1447a3e05d30a4cb4e6b093c6d91d4f61db9f6c73f28e79ad076fd321f8cf1924fa1c5b187693366c3490a0fba8303db8418fa762ba2d6ea700ac4e8fb3b2a59a1a39460c6d13c27c01b0486bbfd35dea7a54965f7f2db85d18a1ea94b3e6798d041a493c0de0ce1683f05e877ed2954a36b31ff78e434ec4f7ba16dfae09c6c54fd088cb1b22cc5c539022d56083e988db7d86ee8d6c5e2cf0ff7155c55f6ac2834c2cf3647434922cf0c4533fa23a0b1b77ebf9c518eda5d1b8f625b2df793261ad3455616daabb6780bba0fb183b58d6579a8ebead1dcf67241acf962cebeef6376cfa3ce4730c3f20d4d6686f5db3fef59098ef5feaf6f676c62b2509376b7296be08203e11abf9e071b2b2047e85cc382dc830274f6a3d89eed0c4acdf3e5c222f1e70f5bb545b403bfe4cc7e6a572b0ddd07968c9294f59def3caa4d97530ff5127015d676451409b0b37b2c4dae6a61610116490a777f294b4d762d063ff17293581c14fcb68206c73de1afcc3ff40f0489800b8c30c5480357bd204d3a81a2044f2a3b297c3f0e9ac051bd4fb1f5aa78ada7a7889873fa7c618ccafd1171b194b5dde21ae9850df2e723a07db03bbef1eb7fe277b68ce0bdb08533dbe6ecde26ce63652115370245df8fab3e203cc66207319cd9259f17d3fad5ffc4dbc69adf8057f73e8b9d02851bbf4fa302d252e3a9f6551bd1eb123f426906e0c2afa81d485de34da02701c1eb65bf43f1408c37da0d9b043247dd223911bd0535be08071f939b16312c06fb0f6b62369cfdefe03d724564f68190c2f3b4510767bdef22682b84efaa91075800f542cfa320bd4d209b411b02a41674898b271570746955e2e30ae78d6a0e7be26fdfdc462c53207f3b1156d4410e54651264cf53fbae0a870e5262a9c1087efcdb87e300bf5da928b4d5bd836ada6f9ec92d02afe6065a023df538869f064f3eb66971ba80757e9953b3a4bc07faad81869465fda0f57a8b8a42f1dcae091e976398007313db2d435ade6170fd5d2c8929ecd48ffc0cf15c772679e6a60fe68fe0286f0f53a2ae55e574732f32bf0f6a80ad5147f21016150917dfbf5a0f833b506ce4e935497fed7ddc22428fa673ba3d5a509ff8b4a904d7569ee21403238f9a2de4d7623cd48b86c028cc42d723a5ea7ae75fb7f5e36a5026de102d0a7ad476fe9e9a4a39734ad5aad959754aa05449f9b1fb6cfaf1a7a9596c6ff00e1ec83ce4a851fb1d7f139fc493f7922e4d69bde3840c6c9d28d947d74388760ba27d2ea1429c43cf10a140c9bac43ef74a63dd3becdf3b919ebc871cc9f6940f2c53b384f447ef7d5ae84a1258efc405c36a32434d13bf8cef4a0d105a331b0977e1512db911cf47a7a8b8069ded280b2fef69f149a6dbf5493cd8c06ed3bd0e6e540434a4ad5b49eff112a145e38747eeb6f90ecc277212e2c34f8609ca0f3ab4ad25c27913e604da7c1bf48bba25044475ef906263c6c471bdd3b7f577d805b80b156cf994842635b20e1cf30ae784f6b0d55b050bd2a95eb09756bf12bf04101dd7034a0ec6c485f52307b55b66ae16706509da60a233c04afc7e8aa5b8044bc6182685f5d53fed9941d8f436e7b7117c80a1ac70d9a0c9a140ff6568fb0437fc00baf5cb3a20d63a66734655eef720197c5ffd2ad699c47fdc4c6ed6da0d14728bd161c8c3446085fc36e490b4d0117d449164f066546ca08f3fa00a080fc23fe310f7eb6cc8437acbfe50dd410bc4400c4032804e7a331d1040a40be208c8268000671e231e6e1b0dfdd09247c8d41c963da00b44ee217de062de16ce0a9976cf6166e13e757db409c587f9f08336a8953d28b9252823f196fdf65e5a0cf60bdb7f142a45150b6484fe048e41fa397051dab2dbdf6fc08aadaca968ad0ed5a82f69ec7c5046a423048484e1aa3407436d12325f28459ce34de11feedf0e71e69adf8398dbdc7d15ddf25d54d1112426ecb528c8bd6885b3feec7eef560ddfdb1976bf1fd4408854f4b723ac5fd1bebec3a9204c50eef716f45c742de5073a32fd4957fb3f4e761f25de1fca5e521a8b06bf0cc2993f67af08ad8ca5a50ff16a04b413bc1be8ad2f1f9024a9a3e6884bc4106c3eeab9e06d28c79bec720ce77cd25ba72caf8b21b3d45188657aa60b611adbd5b7a9dece0380665c50d00f02b63762c5107f2a77bdefe4670c8cf628986452cfd45a182b1dede314ff6f050b0ae13e29c623b812e6aea0ccf9ab0240d563d4a72a0181469141449eabb60ee35ae052c05961267f4eac28b5aa11033d72bc2117b76bf459c18f629da12f08141dfc1562fc499844c9564e3f3654e1f326eb25b722ab704cb0ff5e8eb0070f457f316d55ff786fc841b7fab6dfaa48a30f84576994471ccea68eb5d49c6406884856facc66a65e7fda4e4f5b07dc7d7809de8052626cec0cbbf4166a17d602cc6436d2895f796c55db2bbbcfc81c2d34c1a81166a95adf1003e751abf0460f993fa00dda270637f7ca157495cdf9cfad332fd6aaa654355336de58ad9b070ef1737006746dcd827daf85ba82551a6176f900f1c70fbb2486c163dc2aaa7c0d145586f3caf8f23193241a0a0e31ab58f8385ffed919255642623dc27ae27008ff1ef049baeedb38621138a1890603589d4d9745cff3a0138a810263567a640bebd9a4f77fb6464269d58820d8d8b9e8a18fd5460d8e5c254f3334b925f3120dea468a602993bcce00f98a0b9e68adb2ff830fdd7c90c47bf66bfa002b91be040fb44cb39e2ec069b33eb35c7ffa20cf8587ada9f2d6f638d2eb0c6bf790be0481b5749cae4c4698f5ac4dd5b67a8f0fabcee9c19be669287a428a48a6b4dd0d9c5bd8f0d29278c2b2aa69c9cfc015d0fd5bc5889897579a4fa35bc454846b0ac51c2415d438eaa97a6f11563f0ea1ffd7e32ee46c9ce0cba70dae8e27a0a108e7bbe2b542b328ac1310f3e96f4732e6a41a5fa9c51b7c47eaf8a122ff3b540c92e98eb8215b0835c8a676c88a9ccc1834bbf4a0ee088012ff412e498a0aaa0a0d2bbf7916885a688df9cac92a7f8ee7dca4f768aa84fe4e8cf92112494c370f44febbc4ac854789a5c56f19b8ac349dc5da7064d21d089cf0cbbf2d9960aa0eb72190c99e71ea65b3918a02d07f5f0cdb7628b6413d2b24cb62b012c9a9120ea2abfc80c0bcab27086b6d9831e87b4c38f638f4c9f5956ab498e9e9de0e230b1bce4b479745c9129ece2637bdc90c4426e5fb8a1bfe67d93a093ca1c42a29095ba5a4cb9d15b382c4d17f1bf0ea837af50c08d5cacb64e505e504aa74ef4205887c2afc657ed3c62153e4b52c3233efd92298b1612e01f47c5cb388f5abba07ec9e54245aecefcd567f46e578cad0faac0f4e6905fe37c8f3cb9a168d461f0cafaac46b369bf58934a9a48f5812702e9f8289f3ec4ed17342ba2f19cd31d30d9b77e54cf40c716e66b54c4c56b45ce64f89398882893ae2f1d6dbe9a6ce0c0348febe502355b108d4af4c1ff8e074356f57c7672403665d72b2f04b4a832c0d162089c04898a8802b5a544c7a283cd8f236344526b5357e0fe08892fb2c0f01c6b7b87f28af92038a52dde4e0fa7645855845e2798556236767926236e91e0e2172b118383fa38ac8c522767a4ff2ba601fecb020deb587896b1349a11a00018bf5328da4d4708295a50e5064b038f7604c1d7d26efa162bb4f935d5d38f60d47981a6842cafd6e8026c2ccfc830ce8f9e1be19fe2d2373c690bcb30284110dfaaebfbded793729d797016ef3890f0dab2ce0797296a0fa9f9da4da23c1cd0aad8b50f5b290a6fbf118c96e48b7359c3a786ffaa7369a4c0cc216adf2a1400cc0434645387d408bd536146496a1e2c2334e7c189343c1fee318335be74abd0882f7d0ab41989541fd81eba271ebb1ddcdbb54cab3ab28d764e790facc8fd704c118972f561bc3d7180df275e025e967118cebf248b26725314954510f4a240f9c1cc3f434810848aa70115976bad73e1d462fd468e5daa78b3b02d2f2cce10a7fb6f547f2e6972e0a630fbcf877003c83c43263cbb1fbc96c23f5c9886acb0f7272086f32dbeb7fccd59755a1ee1c49401b30cc998fad98712bbd71d81cca0a10835751f93fc6ea151dc6a1be9664aef99b5d44a59ff9e7bab422d863e1be0c3e2efdc8f3fef76d9f26aeeb18464db8cbae536dfd6c07b6f6b04d59acaa4a03e4c2d4eb1d826a408a1abf8113c1c3c41a51b8e1d742b5854c31975f74644f0764085c10e9acd49fe1468c6b4a72fc20b57c45d16fd83c86941cc0cb2c5e820b171275d09b40b2d90156e896353141332e747ae8a4bacd9714bf35eccac15f059ff173e99681a6c49683fac63ba1fa6b218cbb80d146e68b28b8a402bc4d100d13446196adcc677674f4d2eabdfa660666c996333892fb4c7358cfc30debdb0b53d709c661fed24ddd625e983d3ea5634a1346bea8ebfcd55eb749db094e0e0503d69fadf404de36681dcbacd2fdacbc103c269ddd91ac47221b7defdb01e302dadddf17be78ff68e9ff78d4c303f7a99ae916d13d9ed9afd4c4279d888f300ca9bf7e1776aced7491853203744e28b7c3999914961ed9f427115fe17c6d0407ece13f333bda5c7e86a027e5ee733b742bbbf20797b41accc5512b88c481b50dbd9e57464c9b7042f32f97efbaee7358516e9bef81634d7696067dbb55eb5e00a3b680d2d9001ad814ad3d957e6f3f4c777571289ace1852e2094c3f3de48b04 false +check_ring_signature 5b0a07a8018e67d6c469bb6c9e0876b72023af79b525536cc08e3efea7913c93 6ef114254fb8a22df2f189b5c6a1bf4bb9c0f24db593ad63cdfc0baa60c99233 7 15939c6255d6096ad17b9a910197872f24bd131ff919a7565f65249d2598dbc7 8253dd0da271f0a081212225d775b3d275db6bdc7b65f86e8abbb244e3d86e0b b0372489d384dacce4d807ed49ef392b34676fc17aef148fce478e2261293cb6 eb1615344bf4d450f8db575852235ab154352527f62f174da4182e3acef3e328 429d48bd296aea15a2dcd04d9c9e3ce4e1f134fbdc6bddf01a2565d5e0981a32 ca626a59539b5d39a0f4ea81787b52d2b41c358a30188d274733a3478856eb42 5b3b6911f612b4c16849936de7c958bf8bf6c68834100dfdf69dbac15af2e77c b29e09ff1d7f2a09b9c626bacf1e0df3e3423db54e68af76c489b7db33f9480c1409d16983a0836789d6afd7c32c2ef5926f4918dc098a4d3702b174d412020f60abeb4846a447ff42fa185a7c3415ee7ee1d0dd04646ce04ff474c5bd641f1923f4a338efe521667fc93bc7ce4b786acff4175fefd58cbe32c35c7e61f0b8050fd03863346d8ac3720c5e59ecb695b820fad2285c3ef23308205c907315342a3d2bf9fbb9971acaeb4cb636cb815ae719cea43f3bf2a02f8074ed5521e37a0e214457e77755c79866ffaccf38e129b9bc106a39a910728d443f0914cdf89d091ba826d16c9cdba6cc23b0aea5e3ac2f88b193ff980a7ebfe29ca0f9429636078a7e78811cb927b2afa4c09d43d7c35e6cab8048585c46b76543e9a2cfcc41a8aec1311aefa51c34180285834335d095e82a03494ee7e513b2f05581a70b4b05cc3491a2daf0aff7ddb57779fda3bc5497dce452bda8ada2e8924aa8078abc0821b04da5a8ccb4c94285c6e27339ad5499b73175af5faaaa05f2aaa72ed1f701af2453342f0c86653884a8f235b8e36632e2044fa6551408974686b2c5b1bd06617b5986028ce23242add8ce147bf9957910fcb0e1a7a97bb8d9db8159bc9a0e false +check_ring_signature b270e7fbb72f2536cf6b866714a814e199f5cbba877617b1f0a738c4f1b5ffc7 b75d3eb93d8275db5580d55a8a2207b6c4eebd5ba0205b011df23fb6af860f2c 1 077390774ece9ffd2bd1bef5541b5abc00bd685bc67ea83aef1dbdaed1eaa135 bcc85af980409fdd62ec877db1f5dc37fde8a62ce11721524ec37068a98079705e228fbe736c64b8df694617ddf29304f6a714cc8401878da6bb0432711f9968 false +check_ring_signature 936c97c350e8ade018d256de85e8542bca0c7782b360128cdac61d8a200f612e f171b43e3ef3e4533f269ec749d38dfd782b2ec6156adf9fe7fa1b2890b9ec61 4 aae33364273ab9c382dfcfc799f2eede9a73dbc9696c628b690edfa1627bf0a9 ace1cefd2576709e9ffe0b2c6f9f20769422d668857e74b7df53c5189c649baa 02e01609170579b80b5610fb788ff9f2fa326f233d18aefee0828aeb7208e452 ec6b4e7a90a9dfe90d0ec4dbca995d5f3a45f31df80dbcfa4693030992e847c2 92d567a32582cec42f8327637eac3cf514db7fb58466d0df56c5edc5bba40800cb570e23a3270f9c07fc1c03259b86d0a9ea738e1e071a7c21e86d4f6f0e180c09f44422d426ddcc0b7a0888bb811835bb00d3c8e4625eff2c8bbb34b657b305d00579926d3785ec0eb2828ecd9573917dc28e86b276583d4cd3e18e4a087709d0b546fd6cd491ff9f671d072524ae5737500bb5e02899ce78ea1969347dd0044f12ca49d081512caa0bad1b64889292425f77affbd2bd56e395e2119269b201e0d6e92fcfa81b24adfa5a2eb60d2d19cf238a26439519419f5bde51ebe7725c7c09e8ba47c3eae9fd5f5ae61fb70a4b90bf32325206bfec24a5b0130b7b2f00 false +check_ring_signature cd09587a6288e150a3a1d768360c7b31d3216a17cbbdf84702f9ff987795ec75 08ac82a6a0c3638932e64ef7c2400e57770df1b7908be82a67ebd9b370120099 2 a2f7aee6c59d8af8aceb06ae94b18a15202cd57c9488ea5645979da818b12406 9ac30604b0336f2f556b54ecb62615f94f0e2e1ef2602e55d36329da99694a96 64afe6d2c58c7bc9defcea8129c3175e7a0e63a6033dbefe7d6bab5a099c1a087be3ac3249d903c402b5c6b5dff27246e002216e397f1d650bdcbf511c2d05037c616ad4b4a41c0bc750ba8f2aa3aec130cf5ed18ca0150e8dafaad8e1815c0e7f84c89516bf5541562c49d59560f8ddc7935ab7ba83fbd4d6abad0e68299d04 true +check_ring_signature 86d10e657814b1d8c2ab18a1341bb0ab88e56102d0f33336bb83d14800840079 f6e4705cc31806b748750c47241af09a11dafb1d0581064802fe51410cdf31cf 15 751be277b5edb2455449e41e8d1ad2e3abf72a9c47ac4a027611c0737b7c57bd abc4023313ad5eb57984aa947a3fa3ee89a3ddae0106aadc56b0888fbed95331 56ca32a6206820fcac816b3a83cac2f03e4cd6c1ec2789587f8eed85bbae05e7 68149189b701fb85f40ca7543dd9a965f74bc3bfbf50c5a3b73a0c29aec0312a 599f0c0a70f4f1954a386b1de6209f5447f9e47086c3a52f056df8c43466b81b 38cb6bc437e8d4868269adefb5e73c34729aaa81a63f596176340c6ed0eef129 53bf8b30afeebea69855dbab9c7d974d00864119bfbb76f3907deed4b1b7b792 17d384221f55c5893637cfe2e59343da1494735726ab2c9db94e529523583374 42273b97859921a954cc38ff9d146e3de0e403dd7962e44a4b7ce9bd54c6d07d ba8d2641c725edde3e07e847e05f996fcfa37adbd847978e05fcc0443ce360e0 4615b7d686a553e31a2d9ff69bf0be37cee5cb78e41b77171429757465211e9a c46aa24d0810a1e091cfc2b6353eaf5cb3e4fbb27298554a053fe64c615df419 f61db4a3db3206230367d293fc9975361172d93ed545cdf0e305ecf832f0a9d5 1230cbba286b14c6f0107392bb24db4d4e5b0184f2320c2405bd5ae67fd16c3f 87b4f19f2f6f1ee729650d5262cd2c9eaa0b34655ea25e6ee15470f6fc441360 251f2d848c8e3c63c7076920b3494591e27ed7e6fb2c31072b9c521fa95e7e02e3b0fde9bbbc8d97d84077d5c83c77fd07b0559f002dc7bff93460fb0ece0a080360c06a6354e00f1a98ee01d53c66a0857f27fcf3f02bf7aa8a38a86e656e024f88d1aef5738aa72c3727813aa2a6ce4505f7f84ed3007ac54c9fb9df73ca000ad11435c701db4e4891f5e65ce37babaefa548a61393eccbc14cdc68b9be101a899d8bba2b33ecdaa7b558cc78dcfaff6d2dfd357ca93af66d79b31ac814a0d30d722d9a3608a50ead4c8e4e64f8143695f800c07b2e97c9dfdc66e8bb319069fa877dbc11f244c126515b2a371580c4c25f51a9e2b18b856593c3e3d7db10e6be6d18757aa3d11d7c0d1f836c9021d4d8b04d970f539af0bc18622f1835e066f1a560e5e9be5744eca99944bdd378a1896a5ffb8f02337c67d4cbda5572e059a2323f0da901cbd0a1be1e6793c473895c7a4d37103380e1ab3344209dfc3046a24bd46ab817b6c8a2f4f6235686094c63e7cae00a89ce5d092ba8f49241605c8e2d6470ec5eac3b76622735f32a1eb8b8f35ebdf8d9ecd3aacd65a3c5ade0a7acb40147cd0fd57a11875d39c9000b579738741bca22ca269ae81911676ef089c863763b3b500cf4d09d23acade231041176befab02dcc2af410e31126cbf00a4a3b0c97645ae4e2ae1b9ef2acd4aefc667868589bb8ac474e9d86492ce5c0a91b8ea8e842f42d120ee4198e519eafd765300c6193a965bc391ea43bf51ba017c0aefc1ad77061262f9fe8c739d6d2baf0879fcca9c323af76e783bd8107b009a1a9cb9c8d385958d2098f2f376d05369efa01a10ad5dbb7da93d471c359b042f4f6e21f61e85cb9d03cbf0ca6f85d8c87a76b874d15306bd9be0f9f833f50ae564a8901145f54db66044fe42cdb04ec1f72de7f4b5ba89834e4fbfd3a7a701c1fe2ae33d43f0714902d8b187114b0af5755ac2eb2291a11f23c8ad62a68e077a222b8e02bf3ba40defcde367f20bb6bf7a04237786df7047b4aeb5ae88ab01c122cf12bf5f1be6bab586abad17fea5ac927f025e2d6147c5b2f8c9c77f030747bddc31be80efa58965e7f0e808f49d380e21e5b6c3dc745e85a3a13a45e60e4472a48548b022494bd7b4584c85cc366c9a3c617d9c843690921f28ef52f107d101ae254f09c8ae4b5cc37d3fab955ab3e0de4f8e2878dec5f27b8eba506d0c2ca353e901e9bd60bed12c7561bc9eb80b4709a07d6c45fce1d69ed9a242e50b0a05fc82b14ad85adcc97cc88943061b559c1b4b4b9a7b1c621bd12aff60aa0c351dbb088bfd1aa9c8e1f3da6066d8d47a4928cb9643dac0f7dc19a05b2b5e04 false +check_ring_signature 09fbe671945d1ffb769bf2bd2d406920844fafa25012ef901a4ebb192b0f98e0 c71e54cf1f02fec5bb07ed18dd67bb1dbea57a96f464d14df51fabfe2687d538 2 f5ef8cf99da2e6951cddafe5e4acc792ae1544a4af49476e76007d8eb9dc0177 d6259fbe2ff4425a495ac067ea736aff2cf492ceaab6fe9be41b7f3a6bd92b06 7545cd5b366e4727f27705ede97d546f59dbd663180b6cb52f544eb16f007802435ac3b557bc30908e96cd04e8faa1b0ad932be2c8b96fab1ad7c93eb1367b09f22de02dcbfdac48ca64c3aace4610a5b10bce0300db07ac97e666a0108bcb0ac1806e5f392b41f0cb19fd57f15c22809b75165abad33d3ddbf2662b86fe1a08 true +check_ring_signature e5a3f092cdf7f61ecd3c48a79c70e12f41abffe80e575392ac0ffd7d136fa3c3 529ada555d356253cf7931057d9531dbf23d57a72eee078d8f43e38e888c7bdb 2 ec5611f0999a3690af0ce723e2cb3dd2af95c3650dc6de6ac7f6cba2d00bbf25 3222850e655e4c61eb75e2b1e2f27901760e047955b04c0de8369cfc15c7d2c0 b30879d74e612f03912781422a7602c6f2020f08478ab43df30de2c0808fbf06718e255203dd4c893f8eb68ec45d2f5774ee63bc0d00a8b598b035e28a15d30448cdf414d69bdeed119dd57b9c77bfc198d60f893a861fbe2b0cb1c16fbced0a299a95b89beb9ad3942ec64110e79ca525df5d8163ced4d589045ac41d326a06 true +check_ring_signature 86e0d798feab74718a6d37f51ef7532d009afa7dae48968c0fabd84eee8a39b6 a47163ccd5aac9da9aba41e3195af2a1b3339e73743cc90f2eca43b166b78b0e 4 313209fda2c51444c204a9c9004215b9f65b700987995e38ff3c362ccd62a779 84bdff85a9f8dd634b923e35837a754ba8f53600449394231d7a3eb0c8d9dc6c 85cf87334c8f381ac07442fc61c944081e907223a79a1bba53a4404875297303 041600463513a52f41664908d04975446b1f20fa994c2fa16f5c2d6b7ca89c93 32963e92a0b44b35e222bf69eccb45f127b5de42bb24ef47478ed89e2645e40df76334677df5197e914731e282c2a69431f30ca4c665e92247aeda49ad61ce0a061195b9e0bf69cb97547aeddf0a4f0fa62b5addd115e42268d8e168ea39ca01a2d5b23e17367449eab44bba73d94f9a5aebb7d663c61e11c7bd875f6c745b049cb06139e57ef40a1347972c9740c87295be745b1da3ebd8136a1449b0cea10b2f237bf6d2afc239dc902702dc19813977518c63d1fde8f3e11b951ea305bf0c297c73f5b847db169287c3ec88ce848a53325c08cba279c70aba8ff365144e0826f10e8d67160462f4c6793a3e8dddcad87a3f45de5b882c54bdbf3f6b83550d true +check_ring_signature 86ba31dc3270d32a99e97a9d1b14e22a376728af02f1e8102a9f0b2bb9ec4176 50575bde8ceeb096a25197d8685ea808c8a07b6420f48ba25749a048ae836593 32 f630941c7d682d5670d368805a9a4204f35904634e218b665cb524be0004996c c5d5ea5c5823b7abaa782d0fa8ca266c765bf29abce727b9ca30d809862d69f3 bbff5e4eb97fb8fcc6155a561950602ff461079bd49e4ebc475656c617963562 a6887be83eab03a5b1229df40a8de612533767b71a41a4d38744e84776110a11 56eb79984cae5b9a2d514c272498f9f695d415580ed6b16b7b97f3ea3a9b1d0f d1b382a32992d10bc7437161ec310953e6bc25c8a6aa18d558933e0b89e74081 2d8f0d602de1f187c11d0411ab1cc9c71d58073b4b312b1f09e68a9705e791df 9676eb39932f4af46e811f35dd9672ec973c9492d653b9003d9d13d7ea92ff9c ffe47342552471d073e1ab57ae8e56bc364df11aaf70a2048c3826f35dbf5b1e aa2c8ac46476ba83b2c272412774c5a121177574c63b62914c9fc91068230254 47ebc771118d4987f010f2d00b45552f0875ba442ad623ae1c32575ddfefe9e5 5963f8b0be2882adf86b552f379fdede35203163feb4ccdb1e4a5b997c472083 14db84d66efa73c5706591e744c591083ee6991575e402a20c717c0f17c2f7d0 960f116e4bb6c6d60f6b068ad985de32eb4a06f39a8cba6e56d5cf7d9d20858e 3ebef8298e1f41b0a895de7095196c2ab70b4354411fa14ccbea1ebb69416936 2347b07173777599b832b684e18c5feca27afc7026db3459c79dae9800252759 7f613809ec584dd2dfaa48c816edca3d7251810203f063ecc629fe6395078de7 7f3955ebb435fb0dc865f92b4fdf75131bc45d0ddb195c5d2aa19b42e9de279b fc72c3431c0af72f05ac755c22bfcdbf058c1bdc9a2d845a6ce972bcd4af7768 53147e892a39f10efe5f0545f3cae3e80b3a91dbc94883dc8e6ffb30acd80c7a 49a9e7f7971c0268650492e5e29afc0eb0b45f7b577b1da64703694f230e3130 32bdbd38bc1499806c9df6b55d76f73cc19d392309a8d398326cd9496201158a 8cc6ca9a7f56aa27811ecaf4186c1c7334cd2b60086d7c6cc046bf08a3347747 07e1e1c604beba9910c9befc41619ffec55a3fdeb961b3033f8c0f2af538db4a c391045313d609c7c830e1f9429b1efd4106eaf6d5b9069bf6724cb5ba61316e 58b42758cced3d7693b89ce05567194385bca45445eaf877ba42859a28ab8450 804012593c72d5478de905f97dbb6bdaef08c414bc5dc5158e113d35d6f9543f d89f51ea460a7cfd3048ce4caf5ff67814c614211c5c4f1dd7ba94de46b743a2 4186f399c00a0d49aacfda9f05e193f2386c54bbd6b8f17d919b4443eeab4afd ac0fb2a13c26ebbf77940bd78358d0944040a3ca1d1412c54895fe10158423a2 83bfd0b8c70f1a00abcb1d0948f9513f12497839843fd75f14d5f5c26f3beccf 76f95e7813517e4aa2ed86a2302cac0d889340d49bde3ff5b43241f40aceb8f0 ba6cf8ce5b76142ac8df7086b39601dd076b7702a4ae242bdc7673d140958c0a8cda63cf0a9a673fc1c9e3a428fc66921318521ee4ba2aa97b43ed635d707807212ed956a5544789fe1e622cf2f7fe58503ac0d79f203d9b132847d771568f05a07e9c0a2332f9b910b8e85bf9a878f80af287ae5252a8632fa6d43d2721790e3e4cad41b897a168ee4fdbb5be60a9d5c40051a683e606960941aa65e0f3410297dcebdac2c7db9acc9b7359ae7df8d95dcf46bba1b8d4d8473b66631e414f0fd419eeb9a1679e79ec64d0bf1f33cf727b8ec076fbad994f20c4aa93a337770342977ccf55d5c97873e5a5069bc515f04c5923b7b0118edfd00b885fe145170a8906e9277e765f7eb393c68db4ecad0e451d52520f92817e2fb524930506080fc968d9ae727c7647c56db65ea8659c438417c620f1debc32b626784cc8952c06d8588da73a88147a0d6d1476f673391a58f1d5f445319a091b76b5bdc4be63015e81b572fe51b4575aa0f2d976959d7d9313c7df394813d94e58b9b0ee0ec7070756f9074be37cfe0e514cc65bb4832d5941b8f111228a1eefecf8924a0ee4056ec6c70476307b16015ed14d5a55b575d2839d466e9e8513c055671163052d0051c0d2cea8201ab4be86fc7227126acfaffbaf429b29045f5cb6f2459cfa630c323bf5967ac7546e5842be9169618cabad5c42d390e671e6eafef4345a247b0f16615d2bef98fe0d644d89f5977efc78c8ba5d2c0df6385dd94df246f843b80a2c374f08d973a6e45e6c854cad51e2787a86148f348672e53b9c4f8f02c5ac013956bef7591f2e99dbb5acf9c3233c9461c73668dbfb9d9e01d2c060a1c53e0aa1005dc52840ac4a851839111f84533989a75bd5d4f679b71a55f8f42c70ec0fe2531da95ae8e8fee23bd4542d266089f6bd729cc1ad6213f397d86a8b3a3b09ffa794622af6246e69828ca282ac35d64ba00fa0170cd7290f065338105bd00b9d627be209b5caa56cda4ef1cd2a3590b9548cc98c1704350b09ccf20dcdcb0f34a313dfd51c157ecb7bdd5fb1b3edd64eea717a53fdd131f1211d55bfde180ea50f8ee8fb0cc6fbc6bcf20db4dd105e6794c8ce642f3c6278fbf7154219d30fd84b7cbcbcac72298bc9cf2f4cac3de15525567262428b88b243bc8bf625c30d2b40ca5617e46fd0060c7edd7342a8045c124cd017c1cd464678db6363fa2b0e42822a6e03ecc645c1633d012f2beb758661a1bd4d4cca8b35545658bd82820ad292f692bbe2cd4c8c306001b1a3fb888057c5616c1f1383bff9cf2ed10b2c0aed1f958115f844993aa5f7a94b9a330fe564c70b22c84be5c82210a272ce850c1508969cc7862ca5416be2bcbb625fec5909a110cdfa67be58a777cf37d01f0572d0e007511a8f2c2244ade17146031b0030d60ef944130df8fdd80a13cd9e021dca3f4506700fbfd58354c6d3a475f29fec9cdbdc79885fe334aea48abb6f023b8f7648f5011bc5d6118e7e606703b8b395a0b99644d14a5ae3ccdbf79d27093b45c93fcadde9f6207d6757286aa89c2c72891c1bffbf99255a56dd6e66230ff820e380f98a767484f2d185925a0ca123e3a00c1bcb36904f6b54afb96c36000b0c11dafbfa0b4196856eb4ae7bc0a53e680a8c46acd21f542fa6676253ec0b418b81a3c77ca4b58559673aa41c38b8121200aa612bfce68fc983de9ae3cc00523ef8c952ec6b8710982bedbf29f424d397fffd46741dfbf34c0a8124136a0079d3038337b293ecc44c8317494bce29e03fa933d0a9c4dd607b0eba18644f0c93fee5648f37b5057702a4e96490b199b78640fcd43cf21b7c99a8fd3ab4910cfa374d7be8067935a71968ffee90ba4bc97d8e4fe1d6eadd77156d156a9f11016e2d3dd112e7aacf2e7d2323535a8bcbf29a099494e0564b062631f799f6090ca2004b9b97916ae8b76acc86db511ea9345aa99487e2cee5c0717abf417e950b0d6f4969c94e4463ed3235be147b9e4764e5eb2752c860f95210f5ae9ee61e058fe6d4146a3035e4fb811fe4a316cfc23445a1bf238575935145315d625cbc0f5ef0f88c7d29f140b88c137b9585586d6982964630cda3125be66b584c5f8f02cfbe0692366df7478b9bf983f203870d3be2bd33b6d7f482da6469d58ee91302f802110039704d1fe1821884f8a8cb8ec81b7dc7b69131ddc544794e588fda0df82e32e1eea1e4959bd2bb3179ebab30fada125c220ee03ed0786b8e985730097d4859bbd964a59b8b09416fb075a53c1ca74e8bcb042cfd1b34d35fc65b950d1b7b39ce63e8dfe8f3bc8fd1c48ff0fbda7561c2237bf8e4e6721c5c9d280f0e5e8ffc591f61b03586c8de650901d91c533609898d55f5ebdba61d3e0cdd0c092d88fe22bae42f63b761b4a66b653f97f88245c032634d32279ab8bd4f629902efd0eafeae8270bf0f1441ae91ce290198359d34711d712871f4af8e8d8fcd09a500bb36f6467e256f2b4822bb1ba5b698382e04d1bc8d1532c9877d2dd4970d83da72a8d2d4d3ebf9a2e3971c92b60499306939a973141e1bfc2227aea6d20c2b89bc8055d1995a96170b6025afd079c7b1fc6f5049cb57b0bffefe10b7ad0a01da415f089c41a2f34a2d0ed2fd6ef3b7875be1340a6ffb0631545c09a64f08de0ec7c06cf1d92be4b0fec44a724f3b82e8263cbd1f55ef3df627c016f26e044068ba6eae4f3b29c1a1977a17aa5ed65aae249f9694443d2f7a902154889206684e63a8cec480eb17b9060cd6d59024c8fce0ddfe1eec920fbda9a6bb028d06666cf4b5c68b8fbeb0318699ece2365884f4bdc878d855c459b59658feef9c0025a6d9fb66d339c7e6eda4d1c4b38e2d0da05ec5ea6c9897cef98e94c012c605 true +check_ring_signature fbcc39e4153ea28f1064ce8bc911649507736579716c261037bd8903075563ee b19d7349dbe319adbd1fcbea9246ebf223446fe110826fc92810e071d6d43dd2 1 1487e3e72a412b5e44c9a57e4089231d7ec4590303e964ac9d2888740cf18b66 4479c530ca64fef5d453ce5aeb29339cf7768ef05e498d47c07050d27f0fa705426a7679ab620254ca249cba0a94f663350d5de1ff4ba065fc7c06c7b86f533f false +check_ring_signature 93b1b28342837c966dfc78065f859516f70a1a9b365eeb2d8eb808cec255c655 80711fbbecd8e81ad505e9c934494e4afd55e15ff3b06c5c93131c9058bdc46d 1 e2548a41ad4b31aa0fa0c80f0ab17d98e40aefb1a87bd8b3a923cb149cdd29f7 5188267520bcce9b2958153f743190cc83a5ea5db376bc95fcb516189f66c0e6729afee3cbef16c2c9716547a1899e9ac9fb9184c01358601781163cef100958 false +check_ring_signature 85514c5516a8d3fbc4c044de13aa8891c9667805dd271e01693df57c73ab9549 fdc383174c6e6cd2914ded268a9eb04047023faa96eba7ae5370c81c80a8f613 1 fd46d739cfb3eb60ab54745ff98a4a3e1fcadbb96da5d20f5236d9f3833c27a5 6acbd3446584742ea915b90e1d3c916f34382426b9f08f6c612f159a525c600fa92623d6d0770fb1bc211d40029000d2c98e8f6d01d968237bb0f2e365572e0e true +check_ring_signature 2886c8e79b838f81a3b50054ff5ba54135a294208b1573a22a57a3f8a087f97f 40dcb9f764a1f060d997f4f48cfe20b29a816a178e272f822163e00b356b835c 16 9a65f422cb8a25ef1035884196171702c15c0d96698493ad42f104fba11ae6f6 9a856798532ed4d362abd3434b8cbdfd5f0fda4e14bfbcf1d4d9d696414dd921 9b8d74a2b2fd15afcae97691a1c9a74be0990b2b9677aea8f6f99ed2d98b975f da891e9c58782deb79877eb9a24eb8cdb7ce2e6eb750b2bf4ea97556b6317d78 81d2eab21bfb3a10ad54ec224e088c10ab3ae3bfdf3c51f4f9e3cf2e03d6586d 47e23a20c7e16d9eeca363f74471d40624c987435b4eab10061daaa757a409e7 6f158ef2f6e2ef2925e87f8c49e2b1621f71c9b0d9ee04caaa8765be7fe2d817 0f4ab7107cf625a59d37155672d33acd31c901ddd1cc9f4ec3ca1105f35d93c3 9017cbf1d44eb66a8a4be26c6f29a858569cb80463e4a7d13959010725241403 2ac6dc9c6c8604d42fa03be168afb1fc61aeb60cbeccda53f4f936dea28edf9e eae18457e9c13996e992a8338cce57c9ce324884d479f15f5f2e0c3ac7ce6022 195759640b2a8c47e918fd5e968f7a9a67d3b631b52ff83cadc7ca84e66b73ec 4979f4c46250c102f3726594d3ccf1a278ecacf69400b431932edc0fdb41cb9b fe9cbc2a18d38a50cdb3c2deb4948e465a83553af7e42b5a665830dd672ba311 7e61c686e7aa25405fdb2e098545a0e8837332dc7f72b8374d570de149086b30 087826e6b12d2bbd43d768d7f42591b719b8dc71afd5b4db15d302fcc86dae6e b6b056ab679c799ea5019ffc1d78eac92976040d8a78f0190582aa6aa1fafe067c54471effb62b43c7043f667eda5bde4c6bc1643c0c374dbc58a6ecfd446c0c1a781ffcff61bc4fe1b4fb854f4cad742dcaf96c258b5f70edddae4dc1d44702b350f2a2cfc34b9ca0a2d387c2311af992f94825f56538e31fef2c9b5ff0de00506e0d2e7560d2a2360e0905d277bddcee021f98c9a96272b62c0e6056cdf904281ef00ee5b7e093d6cd6132d298b2b11df25b58ba77ac4c138f67f2795744009e62069a82a277b5de6d9abf69cdbd0392051b75ae80e0ba5c57462e86b39303716080011bfd62820b1cbc22936ad7e5d10b40ed50c1dab28c8c87939f917e0e203aeea123ff0fe607495e3c272e02fcce189af81fc9110a1f036564a975100dd01c51335256193094948a955e7337c99f492a564f3b71c3f18048a102cba507b8977f5741ee404623ff2e69e45fd8f8cd894bc3eb24bc0c21cf8f6051d41c0ed8e2f218c616cde9496150894e10d5f948627fc45396167ed14353ec7a13dd0cb77164287f50d07f91965883b556630c8f51cc0f305f6e3ba11171e7c98cee0fa9c27617c8beeb461f7d880e672c6cb0593ba1fdd4c052ae486ac21151e6eb06a1b8d73225c89d3576a84644fe1fa47b188b7a2f5dee25a01d11985c214b7207aba611cf4c4a0dc3f56c3013ea2a6041a13e0bfae06e82aaf357397e281ccc0220b0e189da706af0616a83c36dd894794e70dd0450edbf48c8a3ab4c9011550dbc5b4f02daae7e839626a6424e73b70137bff4aff92e70a7fb9b77f7d0d183002d2d42811c418fd56b0181825fd3829d8d55bb1b6ea9a6c0fe4053920f3b00060ee933c2d7cb5994e30820bdc0eaf714d23562a6e227f9feb982907de30de9077105c2597b729e9003f38fcc23f3ebc42d6b491cd603eb4c51c38c7267a0ad014d06db8eeba464316bc430b8d393dbe857d79bb2972d6e08a40decc57aa34d0254deb1b1edf63bcaccae7cebc84f3a29c660408b8cca49138ec7e34df076370955c13756b2a4d0cf0451e49fa68d8d584500f9a268bc249b9a67a35a9246e0081f546be4e95e22b4a8fc5e0263f8ea30bfa04a6772610aa97078606ff77de40c79c0f98241bba22383ba1f0a009f0aa69e1bf0e7892c12df37a72869da80a00edbc517d80c6495bc53267c05a0657aab7f9e53542ef6cc1d29d2349c6c021f07bbacfc29da6a46841690de74e306fcb575412e426446f00c3c3f31aac20aa204812d021bf0281eb6205d7d97a4809c3878661298087a331389e954c6afd1e10c6d9006504ab6a5a874266bde269d38bf8df7561942788df82543852afa39cf0efd2fe4785d3ec2c3fa3cd5c71293250b2e500dc0bae2703d6208b65e9d6ac9021c6ecb008458ca73fcc1a5b5850e30feeb324b27865fefd141a02796adaabf03 false +check_ring_signature 4b98e0bc7f3c3303b25fb0b92eca3c6ac1fb2de6ca283e7e159cbd7d00b801dd 7281719ee5be8510adf7249f23cdf8994557dbd3a7f273a3b1a59d1c2f4e3500 2 f25ec89f71a1b706005cad1238619eb2900d18c15269c9a4c3e848260fa70d26 e6623950707a4bb092ef6b2603cdacc03e09c15bdacbc88c2fb5bde21dbe5330 621aafbe3c689c68787a7dea44011a01a16581f3aa5fffca5a50f8251c6020e0c86da4d1e6940f4785b51fbc222425210bfdc250f06d3f3dfeb118a998b2f9091510951f705711cd387e7242c6c983706ab4337806fd557ad5e3c0936d308d0845fadd53a0fb545c9291adaca129fb0d625dbfc41ca9cf11267eb5ffb3551610 false +check_ring_signature 9a77035f78798870be93a653016c67978bedd38a680064d84fa7d54385a39c60 574d62ee9c6666c95a39304d85a794202c1290b32eb90ea54fdca399804e33fe 3 fd9b5752b803994f8b840ed734e89590fa06ebbae209a85f0dfdb46c68d8396c bb16b84bd86b649be48e2f078da2185cfe0cd40c8e24872fc3d30e3301dfd7fd 424453b68ee9122bc360437031a28cc7d0ea5d7f071fb7fe73eb40c9e2dcd18d f64e745fb8cf9d496f586d362ba971851febd9202acb0020069a56e714e29b05405757c611c20edfda3dc407b407e65e80daa2cf960d5a37cdff4527b823700ed6871b7bb3e71642a6a0e0d732ae0e02d7696e23758d1bc09bf8e1bdb3b71f00e1b955feda185b6ed9945cff9112747542ca7fc37e2b5208bb6397dd5363e70f946a2f3e5f56f9eec502690e3fcedfbe6e23271bcae28f0368995b79fc357a09b81d6bd5cb587418b1321e488c89c2143face61070874fd2cd10387ff9c94201 true +check_ring_signature 4bed824904a5561dc39ba67de8084b0f2b7998f72984d34628720562463822e7 8c05aab7cb2eea103ce4630e2efa12075e7ee8d90c3d6f8bfa57b17242164233 55 6dc6449f62f48c11ef93713d9271a3042027207ababcaf171a60b59b7a0f1d29 3d8645ebc17c6661c0ee83138263b7435ffa5246874df85ee7088fd5f599901a 2eee48d317ae01bf5ff53dc207b7ada30d427f0f8c859d63a2f41bc13511ab40 2d5e5cdb43e1df4723bcf4ea19828d190ada88746c76b5aa7a568d0aa85c7fb6 f1c2fe0470cdf20cd6136f9756ed90329bfd04679bfb9a1127070adae733cf2c 497a224587ec324f4786996f32315dedbaea2c70c50c0533fbfbb8e7e26a15ae b3d0eefd8041db5387c65a61efbf4f804db3000b149320ce608958750218063c b55488cbd0c2766a718d0ee61574730a1edd34144a0d1fe36da6ed1c30d66219 80a0b3ac1f9c322a632ae1ba1bbb5def65273403cd109dea88292ea0f96ecb18 43f4db49c9b80c667a1eae181e2d9a3aa3f0343d7802b906ad213836bf2e25af 5b2c46168c9b13d7c78ce2b3d6472a04a0e3fb48f8baf8cc2091b0e8d31c88a8 ab47fab64a7e05da4511ab345828a9113850013aadaea3e19c94dfe4f2e429ca c861e33ff8369bf3054e4e238480ff85f2f8b78e5a4e39e5cf1609cf4d9b411e c6729c44099a4fde97a1addbf4f75d898d3663f396823397f0a5d44c89c28e04 0e5da50429687fcbe26a7cf8406b9282b7c1a98db632eeb2ece919277b579736 14d31ba7c6696f6ed46e9073b8b079807c7dcf4846b52b7f04df3690d9bd9e2a 0b00e08f7df815c5d7ff2f2ab976283ad82c95e36ec651d58e1fc1db45a80afd fb6affc7f65f3f7b0f1a172f7f0674e883c95c1ed8046121129e0222b72f2f87 2a20b13894fc7e597df64444d0c3c53cee7983a84f00d33aed22b8238be13b62 7c19a91de10a26877ef1c3b85c7503002e8c27b18ee39829cbb795e1a1e220bc be273336eb2bfbc0e73d99a5ed2aa38be833e6290d657e0814d2b5c0ea269012 426462f9597cd3d6513061518c1314df558d607c2f8649ada209f92ff2a0b8ae 49d159e5a765bf05c87625a2e05b3161eb151016c309c9c12412fcf606f109d2 72bc38fb55eb0b579ebe68f7a0de243475ad2c4341174bd43911159987b68e00 c599e881645038a61cb2d80727ca61ae4185130341f66821df7c56b5cb6ee47f e0f713dcc1a520054296a5cd56c0e6eb98b633fff1bf38bfdead59f2fb8773b0 9a179fa395a8320ec9b1fb244e0c0560672f8af67c163f5ffe156b0d7807ad5c adf400c3e1fe929f87f9cf61a471666ee9a7cd9ee0041fbce41d1880e0db78db 10ecf4d5cf47a36f7da4d6e0271341519ea95e6d07614851314dbeb916372f8d 54479d243e9cdc1018068520099297c3f11292109e2f8ca6dbc9ec432ef1ed5f f31efac439f85c094dfd29bfd12e68dc9c9caab65a9906b8744134fee8cd6551 c810b857a232fcfe68f64cf3964ee0d40b08c3a5da110788af2223df90a6d0c3 4a9c78d40bcce62bb05d1853629bf3eb36a789e02ab82d5e16e04ef064d2c69d c9f8a33ae4da5cbc88619e8d53b17cf9f50cd4c96167c8b3240a44e6d5f73912 82774d3a5c61b26e8ddc43ccdd2ce4ba4733482b223eabb6ed1431e5eb749031 36d4fd2c3372fdd04a836935e1d1c155297bec8f1f441a982ba717d0a214cf81 a2286d4bb7313ae28ecfd3ecd1d98019abc35b959dcf7746010c07f072427417 64d94029072256ed9e7d6f43ae590dcdb1107418176a6be37eb34cde722bdf8e 78107285e723ebeb1df7a3c9ee0a35890657c32d832134ffb0a286c878830e0e 5851c2f2c335718822e769b46c7260d3fa730c5dc63e54e19833118c0a42442b 6cd087c0706bb60b96388a401ae197b61f8fafd9ab2e09737e7cc0c7ebcbafc2 5b725f4379e24b2e31e18996bde0be595f080efd0419806e2386d1fc68554b2f 32f565ede5535543986b84d78efe0382b841c3f5c8ea42a1da1785c81b4f3699 0c12110416aead780467c3519e9e38f21519555217c50171e6aac96b58ccb27b 15f2e960aaed0e3bdd45ba0c98f20361ab531537c7d0a89b10c2f525d400a5b4 1e33bf289d585c7ce4568aa5c80a5c1376b4a47fb3e9669b862ae6a259319a8e 439c231312840eb759b2f79e445f5bf9e3aee148992cd61951d84a01fa3ff635 faf85021750dbe656e65d449707b21333dda71de1fd43eba113be855c1214702 d1df6b37c43bd00eb37066ae18b7da878117d8ea5716d3f0a2bd032f109d5314 cf86dd5b456a3b7474580365b97513f758fdf6ce4bc54d433ebdb4d7dc9b6ab8 e3198c1a89fe121cfa827f600a2f7fdd18252a4fd308c5cf25dd1d0f3839f9fc 9c1a365a3f05fbc2d22e0afea7bfbf7556f2333c34e423fb58a8c4ebe06cbaf8 00661fcd6e07d7265e5abbcd1b6e494ba55a9555ff49dbc8b2ba2837787831ca b24fd7625159aa0ceb365340eebc6c623b23e27f132cba9545f2d903a3b8bf2b 100bbabf68774ae58a79184ff811bdaba40a7c5f1cdb0a67f84dbe22c3d87ded 1fa9b0778e73444ea29c72b45405cbf45dfe47b1ea2d6d534c4378396f1187036515713ca95c41e5ee4091ae3b1115070c1a40b2af9baba702ca4fb004930f0e5c03d6245c4686c7a925460c4c3ba49fee5e2b872959684b0c06c142a129d20381754be3097a7dc6b1b401c4832422244b15c6193b9f346f870f1e2eff75850a3875823d1230e2dce56463255e24e1736b04e54b35a9d093e8e497cdb381f30f65b23f20a748d3e87404ae4819a3613459c7bc7e3c02b56061d90c5152c0c105b40c018c095efad4bbcb54dbc211accea7434d7c314da23de1b28c5503dedc0e1fe96bbcef1528093b7c26a3a42e999ac7530b9468bc1840f69d9f372bff11039f883d620ec4276497bd6dec86826202a894847ab779316854649f0622bc9d0a8f6e6376500ba60eb131c4a1d857934b1812dae8f2015e50d6c208ab7722d203b6fc844ef10127ad9f72551dfbafcc25602aabf4e140f62f365edd0ca8fc8e092d2bf411e5679d8202a242b16cc515add2482ecff94133430c09bdba2dfed30d719d7ff60b1cc8f622f84edc0d75a3e515b386f8ed3769098af6c8dee2095807fac16f2aafdfab36793cb834cd40cd9556d906f03944165e82f42630e42e280f0aac91f35695431a2df9f095c58147e4913f39790545f0d18327b9818624a80e7d644f5df10a17e3d757873a901ffcf9f952fdfeff4ad96bbd3606d09b4a810f9ff800c74a677ac9bd72de55dba9c159a30ca89336a761f50bb7d948ea51850c5c7e102e39893a87f8b9e106b0cee503b2309cea1ca58d7956940112ca0295070cfcbaa262d78b86188dfaaec47e4f544170e3b088b021976d9c3427d2504a01141ae725deef3044cf230f382e49dc640902ddcd12cbc491d86a3cea1602cb01e8e2bc3373b595382e7a64c6f96c8d80f8cf45c0023a7cc2a1aa0b07391c36079bf917f906aa2e594e2d42a0dbc20cc876768e7b7378448faf269c40eccfad00ae96c03850cd2f0397ae87c264eccc14968c35c1bd022677eedb9a7578407005848e5a4a8b0223a3699be45fc920f1c8d133b84fdbae7bc5950f4a2d4a27e70fcf21e04afdc8a06a977f7fb3cc7d3e53ec4c9c8a6134c8e9f7844f22d5661c0245bdeea75870005c1c3dcc2a658faa27a049be812270344044fd8b7fb59ff50469cac2e84090d4b0419e8a5f9ab66ce91b152a38e95174eca9773794ebeaae02cd5778735dbd41d674f0bfc979bb54723779090aba40a56825c0696429245c0df5a9475ccaa7412ee967f7f94eb8f25f038eed0d4bb0ed281206d90d8ff50b06de6cf1fc3d2593c591a696d4220c800bbc6c97b39f47a3826877b628aa67d90d7ed44dc403d2fb14c371a229f1fdfcedd18183bab824323a437d47ac3028d309f735ec0ca4f9b92e956fb030fdb0e7de7370f5062974386a095d49d7a63a8f01f2bde2f57c2321bdb87424b5836287d53c3ab7bce716163c3e1b86657a54090ed23c7e664674b28132f8eb5b85f4b927e4976f41e683cded8fd44e5938347b088862a5d7ead5ac2210d3386f9351a570b41af0502db3d423c10aeacb9f1664006defb578173536fbaa0050237d585b7161758b564bf51cf91602eaad89b52003f7643da45de977a08396922c97c3225bdb64a93504117eec8deddcbe28115d012d299b0959ed9647016368adb9456f4a282a6117403cd62fe9ffbdf6055e750d7952e3c345e75561c0ae702651998565568dcfba4c17e35b3413de011fde6c0fd1ff4850602c0dc02e5700633eee5cc2910bcb4b345db129587c860703dcaf0b62c6126593655aaa13cedaa53c4bf6ecd782e2c9a2f9d6090c349a4349d77701059caa896a6fbfba7e0c98ef10257a3f5fb2defeb677bf9e3ea5f051b35bad0ec9746193514a49271d109c7ccec2ff343da060bda4892ba456d656432bc37a0a756171e4823b6ecec57dc149b3fdb99dbaf395f3b6c63f5dc40c924ae943500e7cc05581b4a47c96cdb5339c55d5dd20d8d63f677c7c43f9f3831b8ecc086006c57628145f2c7752e904501544b0cc8642bfe0a5e9ca0566cd8f3ead7eede70f3da65f2cb02200e666ef8b6fd57c5f95c24f3b2f3cf68afdcfcdbbf71038a8e93d9ca623afe8afe996d4e5337239995fc33fed4f2c227f7f86f12f0f3cd2f20c79a09eb86a2a95d21eced05b8028fcaa7dedd4c3f22575aa6c3878530f9909062bf6702d8309b62c8b1861d855456587f4aff9e0892435b0a235d08adf788d0019f7daac702c4ad3ad198693ffc49d49cb22afdf8f5aa68e7c6a6a6557b71504c05e2058396ee20251ac3d7866d165a324263c07a28a0155f8a44bb55b47b90481bb520b6673f53a3bc006af9cd82b3f27d031b9bf1a713e6a4e69efbf75af07adbcd349269e5dc7b587d478e14961a19179533357ad28519a4618b35b736c00ebe258465121834647fc47fc66623f0eb8e2ede247ec3babaff22036b73cf3002d75204793451c720d08995849bdf394570d1e457772754c746b3b638dc58b0e79bdc460500244bb5cac7bb132551c10952f45215a1d274c93b4c4984d8ca0033b8219d78bd44f4b60796b05587312f96c515d020a7a9c4c10b3c78dcb7a95041d396f6264342782001096fb667d90f6d4ab0c1b99c399bd2d72fcb4452ec50a4e10a3e332a41d913f09f420a676a5731c3afd19687db8e6a7064838d144b20e0ada3be552efa511879686bced647f86e030b26f5dbe1da5e6180632eb9f0602ebac4b86a046021a0eff18473130b6f9a5e31b82314da75d827e11fee0129405b4ffc652bf2f97b085bdc7880bd7e449537d52e2a62df33e04bb512a4b607f0d901cf995d582e27745199ecb2e4f5f543ca9231d31f79793329b5cfeb2262c0ff36c0e4e773cfef2d9e666500b7f9414e234b6aed5e1ed890c906f44bf78cd04b3166e30e0dec8889744850b202a814c9f881ce955289d2a7eec8770b288310797f54058181b0654023e0b1cfb1b16d99445f8a2bf649d6dbac81c7bdce546731c08500efc64f9fabe4609376451a1da295b549a7fed4dae277b8bfa0b8b700cdc6d40e7bd795f9769c78a111397b0b1126b303e8383546f13360f8701f2a4065227d86c62dd461c9c94ba444be0e221bb399ccd73425ce2d0eb86b2123939044cb24d8611214f1bdadaab6e3f84e7ad55fe0a3e61b19f73b3197b66c736670395ec2bef98b06c5e941ce18e0f604646798dd562cbd26f657520be5f94822f050178b7021839b63062ff918260793148fe088a529b11102b7f103a012dc8440639f002e2a8022aed0a4182901ac83cb1e717ae27bf8e1d6b9371f9c3cb93bd0bcc0748379f946d07aeb4b1e4e7ee131bbb08c0788148f041d1c6b11930a65b0af1f3985acb1c0d65384d3c53f3f28150a7d1e95a5e5fe3851c20bf9d1e7b3c017a86c26414ecf891be44e54f2c8dead2a4c6b0f12e6be48be698b5ddff3d490535699d50be741850d6a55f77430232558ed4c0f85dd367902612a6827538ec063bc20bd12755619c30b7a4fdcb40dc30073b3d9ee957a86e8b3d5405fb141605005ca036929fc222c34615af857f5bb53ce7bb0bc79fbb408be959a0be283401275c5d4c702b39fd879c304ed99bb081336d61df175c14b5a457df5393d5bc07ab7e8054815ee9bc9cc539170dd9f1781efcb329e326e6729c52079bcdec2d0e84f78901497e1159f300ba04c1b959a47065c9347a310386aa15c8865e0513000655e43617e3b2dc0799866b672fa1e0c717170cb1750f138ccdc1fa4c8f0109bb47859ecba8c6b0755354a5b7360317f99da8f6c3d7ae9c181d59b9ea26f9028371d2f7e4ceae64b1721103820437471084d92fd2db5bd3dc1d8aae0061db0febb901ef37febfc6dd702c51c87906374fb1bc1be1ba53110954d74ccd49ad093ff22f859ecaec6bb8dcda14abbef6187ffaa74d63b8964b3407477fc9323b047db045c51228a4af09bbf175cb313e8e256d4b789ee5577ec99efaa68a47d5001bd73146134030561307c78ad8641e081798eaeb9286fde4a0231072ec6fbb0fed4c7f6d272f41dd14c2bebec6d57cc69f7a0aa2f0ebcf4dd9545c0d66e02f047a16cf7ef9ef71359aac6c7e5f0e0ea1c678b621e6ed41e1280cc640b75d7600b697bb6dc5bcdd2fcff1b8259602cbd11c357ce884f20436910a500c34fc880bbd7e9eb06fa9a1f24f6537bf376669d5147629361361eb7e3a706e3cf73ee70926810256637805080c5841fb2dccbe07d1b89fa6a8b530a86e5841dd73dd91093a6067bade60a885c02f12554dd88394456b0cf445907b53015cd068d2db150ae73efa47ab969353f6d592ff560d6118a4ec3f555fb030ff0345dd02f3825e006522783b980a7dccdb1708a820491c596deb0a1c567faaa23c9adb5f2b1f0f06087c46c396166a415c8bff4ac214b8ce96078e312dbe856d7bf7a2df82d16f0ec344985d720935bdfa06a8d519cee02249f085bd47b58a9219ef544504d1b70278c0db10acb953a8f2255d817af44985073cfbc66e08683b3b1e5a005392c60f990699427b2abbf85b27f99f48a3b13d9fa867e1cf8e762a9d046aa1cfaba5012628293071943af7362dc0171cd2663b87eb49385f68428478a8b0e7841efe001287044d66a1f8edc3c5b279598402e746d803fc0b3bfe84adc2acf6921a7b0b59708ddfcb5de212869e430553004bcb38c8a2b5b6da1707e545420e0ec68102860079bd42f1aa9d918a2d5b86e0e65801127f9783f24ecf69d00aac66622e09fd18de23be8a43c3cbf7673a82d4a63640cd96363040745306c8c730ad918306a8dbce5c33dce96d6f1157b0e73d6c93ec463ed030cb28b4ac22390112ffe70bf44894bc39bf197f4adb8a2906febb978bd10fc5067976ff8c6c21a1f3895c0e82900b368c504a7af185b72ae036963bc4bb66d82ee060553335e27ffbbdb708 false +check_ring_signature f8be73b01d4d2d69144f67adb32e9be65ab2d68c8679360959b2b2ad3e5b45ce 4d612c9df10441c706f1e8ebee7181481cf235508a82996d9571e3e0955908c7 7 12837a1563a781f2f7ab01e73afc5e2f2989f644be21f534e00f5f6013a9c6e8 3159f4246b1d75e2d3c6d7236b1e97eef75b1c2b50ccd9d35f899dbd6f286124 d290137c730992ebac6df0bbb6683e303a8b2bca06163950418914230f4e8b17 b53840beeb441af25f1012f0d17070346a4069855755e22e01bba41315e63195 e19dfbf0d555ce8fb898d7af450f92108a22798349a5bfdc27a6296f11796767 1847de8bd84e7984bec8d4bd8a5ce361dbbd61a55d809751ac379d7fc3a5f3a6 ae7a8ddc1d979dc29bc10b73ab5bb978a26a851eaecf10f76cdf28ac4e36c6e7 e444879be56eb929aa8781dff15647ab2a12d5911cce0bf1a66991319e4cca0c8ecf177560518feb0dc16a1121351dbbce756821081bd6f1fc8aee710ad67e0034969e3569327f68e90178d29945eb635c970d672e671a3480a4d352ca941b062ef5fd63ecf6a2b37927d70233f4e0dd14e8c740a14aff2cd5c292dd8cf2e30a81024125c2fbe8699db96f7574fb406a1e81948f148c000b5819a7833c68450714cd4e3acc6016034d6f62a7b76f3bc3365e150afcb3ea7539bb9f41c4897401d6ef4855549cb7dcc340a5996a5a21140db43f2d34dc7c8db3018c1df38ce502be941af8737df48317987273f986bb50d0290da6723e8cf65db7d9a5dabeb707882dc0185eb746f6a9218bd06df90bcc3bd86c00a8b070c9680b7aeb42095a0adb1b2759061a6c1778bea330a7ff100d0bba500ad5bcd7ee28d4857cbb0b43063e402ddfbca029196ba60aa46ac03da3a518c7dba85f450277c83bb8d18e2a0ddd4073ac50bee87f180b9ec506f987c22e390c22e4a3e7a4727b3100d3cc360b6892e82531ef73be9355c2415c1abbc8a32eb6b03457e1948be1ecb081ef460facc7446267ce406b20cc8b74ab777f32ed09435676a8fb19e47b7800c684f009 false +check_ring_signature 20d1063c189cfee44f1b6859d4753fcebe4a4b98f919f46e99b8151ec688f5cc 020a603e1a04b7b24c0e86b60006f4e80e8e5d2971962ba21cf877c90cffe4ce 15 76df4aee4e8826ee6479afc69a0a89bda6a8a5d3e02ece5d4d8ac04d816ae10e f0a154a27eeb79461bc36125bfe87a81128255935126534f4a9c86fda951b563 b1f20da906fde12083c7830f79fac92b9ee7921f109add6500b1de2cd015cc4a e44779408e0145c9e352831196080e430296c9043bc3c5a2b54f7adb0d76ce9a 085f8fbec5f04bdfbaa338324dedea6e9367b6ee6a51c2d69e32cdc2bfbb2528 36f7517e0d55ea6fabfd6e4c05644435df3a96402d7168097df12dad4b82e908 efa53058deb0ed2a9c663b372c958a32b55cd5851c748b83596ec06c984e2b92 9c7a0cba150256032003776df1f70e83cadc8dcbc6a1be66fc02e2b84240c2b7 eea61624470bd3e753c79d1b218827f332e969067280f8f8bd421a90a3065b69 6f819843b77206364d03535225ba6eb79743a904024d1005c7861aef0298e444 197cad02ee68edbc1ad53978fbe9c0ece10544e85f666d0a8fa95e52b362719b fbc70b17b59f8b4f3c0417e23a775904212163afb15440dc8ad7f14ba1d755fa 2620ef2135fa6636d9e0c4abfd58d77ee47f55f894f1cf426d1d93781c6e04cd c32afcf1e1442f88a97a95756c8c384871285fba3c09ec692ad9b4cb69e2e9e4 d20dbc5a0afea0b4284351f2ab9e025cc752a4d4202ba0025317fe974b653186 f6b9784810ea48e08e252086762694f224e87cb1466a3669bd315ae971cf860dbfc78467c6bfe4201fe3338421e6b4d93b3f6b374343b8f09fdbba2760473d066bfec69b80706ee9d994b8c0d872414071f0351873c25b847600ed382460420730229cf375d1eb4be67f8e50f14ea5ca44bdae11aab3ce1a8d7316a6e68bb4013f1c1edfc8bd818910351aea15e34103490f22b008a484e3938dbb0d17a07f063b10e96d5d9511f9328c524956ae7ed179449319182e224263a6803d58540506a0788ba188f5490c0e74a20fc679392cfbc21c60a0c2959dfc98337f9e0a66088ad5434a8b8f0d270f705cea712a332a677000abadfcad1fe7d4a6c154ad6200f2be85a0373818da75f9773889a0fceaffbe5d6e85d27a81f7deda56d40ed0009bf7f6409a65c19afd70626241c87451a53ae3ce30c1131ac34d031aa4124a027006f79fc73f90b75e364549c4d8b35053b944fc18553ef18179e0932109310585939a0136f892ebfdfccb2050c1b6e23003184700eb01d0f6ab0039bfaab50b1e74a266a31df156bbb0cd40c5b081849881e5b0cdcaa4445e33d3f7f6b6280543ef38a9bb6b27274b9fdf9d7f634246fce1f9f662d8e4ae0b69dc4b97dce001aa4308f4266b0055938479e1364a49e70a09ae263c9c243b753587a7e9cac007d4f9452d03d1e1ea8483b8af38e6c076f805c34a5ba7b6976a7a355e6829ce05918b5303f3f1d120923dda844747de3937a9deca22c1a0585afe3873a94d3b0c18d8111335bcba100930521d9c77748fe243810a5412f4e3597e6145aba14d07fa563ef5d87a7e4fe58b345a8029d30fccd1c767c13b3eef79fc2ce8097dca0d5114c6a4952487a1f046faa0e8fe3dc409a6ac9ba8742cf35bb22385feb78c0f0c436a9a9f84781dcf53f6a17e26ba162370bccf49b12bdfd0a35b831260bd0e4180730797149b40f536f62e03409161b93d1d92ec98a768525c2a9670d2c508029a56b7fe606727d20c7589295d62482ebf38efaf2c74af0b47db55cdf8690d629e57db6d95318c1f0c77fecf7c025c959118a7d40e31a815b208cf102e0f0c39b2eed9cc92dff55056a467741c7994bf0738a39bac47dc9576448111be1b0ad52141ee874eb8e4f5b8a975406100f39cdb746381bab36861274309bea0390e8df1319801a5131e49abd58f1406e54b5346bdfea760afe5f70819973ad07e0a74286c36ebdff513929748c0aec2efc7aaf21e3a2006fb6456fd5a3b2afdfe092737a82f741f26cc7cc4abfc5cc0891f588503ed720bfd1fd76bcdd31525920fcc49dafa1e4fb0a7073fcac6be6bd914c3f916398fefc102745e6696c539d402 false +check_ring_signature 09628731367b969d2fd9c496bf62c00d28f41a3a2a0b7bbeee29b3f0ce483862 13328a6ad5857ca4f8d58e622669c784ae5e1a62f2db2a5ddb196b71e3ff936a 1 c62fc5438516c5a4940f14b16d06b385c697ffbfd2703d6cd1ce7aabd1eb6199 08ac5f3c3ec26fbaebc6100626ceec01d9865d3702deacf52e198b910d7731016a21a71d76c28c9ff0f9336ad91245d1a72c8ea4653c5d08e06acf6308fcdc02 false +check_ring_signature cbc789e4b4169b7229c00c05db50e11df63003b36e1ccc932f036c7009ee490b 8196ba01fa5e9961a41dec6d1eef8af3fdc0c0e779c2bd9ce292c56c2938362d 20 9a9fd89399ccd2507be76340542d87251ae369fab5c899c41dfbfc0b87a65108 6f06091d9e08de80443c7a7688aae699ec25478ed3a7ea08960c049b5e2bd717 92029ed9039be4cead24c6a3d3436100f761f4d59ded2f87b07ab81d903351d8 cbf67d895aa757959263e7c62c6fd2cb6a59e7b19e8ee6a87dcb511812be4109 a097addda0f02248000292b41bda3284f356fcecb159ef5bd8ae6e3c40d7303a f6d6f0a1e532702570303295c8fe6e21cfe25aa5f360a56340d737b44120207b 14fa18fa5ed15952cdd88997da95369ea87a2d979cb1fb3dbcac5ca762176b29 7a33f5501427e781e9807dc69069c2f5cacda94a04e88e3697bc966b32b34b54 7a9a6541b4d0a89c403b7e370c3086af32959071a2fcbf96189ee724596ebbf4 e606ad6053d65643aeb22db6341e12a1b4d503d63f7606c0fcf18bc29d1141da 2c1133fc9bb27fae3a2c185610078198ab4514b709aa918e43fcd5e977032118 193e3a0d8102a84c3ff68ff98743f580eeb1d56bdad3cfe238ea0e03e55aec49 d5d23f99f365996804e98b905b19fe860165a8460f35fae19a0c5e4667d9421a 1da16cdcc7e8cf188d4c504042a802168c56f75eef869ff4cd8c0219a76ca9ce b15ff7d1ad7cafdd187ad8711c36d4a8603ddac1d895cbb4e150d6a58caf845a d23fc8f629db849cd440d9a5d39d5efae214a5293d739d97e77771e4e4cfa008 5fc6624b6f766ec0b8fba8c43aa505b53e221d9e1a9a9f7dba043cc99f223f78 1dbb3381109196c53d6cf7f1e9ba54cde2a4ac3e5305521ca2af702e72771b97 8065bec7f76c9fbd49f5b21c8bd64b550a9ccbb283198a24680568ae1c1f8bcc d567053ea9fec0ba6515ff72bf738e194010963312b39b0ffbdc504870013948 5eb4a1570df2857ffee18eb1d76e3cd049852e6f01d2db9279854099b051b30c5b0774d331f8edda0dd1178385ee4157c839ec5820f719cb88d5ddce3b84170c352eb3ef2d2e2f0396086a3ad425297db2a11d51f5f3c4d9b67bd0290999010e737e95051ed0fcb5e09c8a8125670640a6000ed6837ab34a3e8892b1554e0ae728e9111f4284c2cd167cb91b54c35e0d401aa8bfbab0cc10e7ba1691076776003a699c74145ec918c54319b2e6b446724056aa5618fb58eb711156388d8e3c0bc180ffcd8041cf75f40013fa15944894a810230d62dac38035dd4868c19ff2046b93ac4bbba5f92a89fd8e2df359abd36e6ddec6059479e9df94c01edf9ea808b880eca2a150478036890c07f0de4720bb0d3f66b20bc5cea22f57e3e306190368d031165e71497de8de4958078a65445d9e1003f765d5ecc8879c8db56d100101398dbed343fc219ab6c5e5d5271c7212fbbac966e1e7363784d75c7182110ba0a79b67a8cbb71a3c412fcd5c94743c180436856c2e997943d6066ce49161086117083ebeaec8a61a5239d250707f76d1b9367ebb9ca17f1380d6ea24f2260e82ac7984d9d6cc389cf5dd62f6518f74b92171e9a8a29018483df68b6b9bf006e5bde0a20d0d48d5baefa92257b8c9e09bd7b2cc148112f5195442661c05bf0656f05e5542ce30c022ad679556c5626ea21143677bf23aec9ff05cd0fbfb9809ed8ebf0331f3beb7dddbe3e1434f3a6b1e5b0d44a6d2b006b9c0cad139734d08c8c32660cc8291ffd297f204eea86b201dba0b81d7dc9ed0d6fe6bcc08fead0aad72e4371a62b8a10843f05d393b50bd233647c767af2ad1a906717edfa0940f444affa299784ea7ac72c83972fc69d84226593309c0cb8cc89ce1b015b3c0052d64e98dcb5107d46f89b9821185546be1fb1ec800f8635f31561aae05297601d3f337c03e335dcffb4a7cd8e7a3d3aeb0992034ea05a86dee090586cd5c5f0cc4ee1295841e638ecbaa73e883cdff53f3904e2bad2c1b50e140b9ea8246a70f027c606cc8f4bfc4b3f1e5addee2c4b744090bedbbf143795e24982f72833c0ea5594ba1947469f5869d00dd77a8570455c110392e35844ee7db46635b6fd50a2c3fa57fbb375a9abc258e8dd49b92712c2bc74aede958a3569699ec2e2a3a0f26d72d9bb1b68850f7f4563647211e170857123a8d09251a1e2707a6da05520caa06a82d41bdca46e5424af0c8a5c18dfefe24d3aefc28645bdb5c48381a86011dc385f5a1695ad15f5dd7a1cb88a54901de357f02174c60c338bbdf2185b506fc7ef09917808e48338dc7617eb19bf73b57ab911b4be84e5c2b9389058bd37c3b5e584ec90f9ee20861c7f08dcfaaf2dbc059bd678424b5b8c4bdb1a0f461054c75abe02df2e9ff8e65fcf97469a6160c486ab46ad7c1ed50108dae24bbd905e8a5b24c8a4960fcf523bd634f9fff512dfe0137d40d0dc9e8c78fd0d656ce059eee68ae111fe449f782164341b835abaec8ee578a7e2562ff899ae0760e9109c81fa497102bc8eb97a9ed333ac6bc8d47856837d93d13bbda5990fa5a9bad0a930896c217d6861567bd4d716309a07a88c62ea29b94fc485270bd64c2cd010433c7fd97caeef0417418bac492cb66ac458902d303c6cb7d3b3da79e39081904d230968c746b1f406815ab6cf5419adf425c0b4733089a7e1eddef2c70ba2904dd20a61b51c417ac11c6ec0c3c66484b0fc038b127339b2087855d0dc0eb6f096e7c7ea60b766e40f37eed6c6652d782c18fb6e539171588262cccfde44c0400 false +check_ring_signature f942dae5bca2a4fe3ea1e7928307f61d3d24c632692733e381b6407966211c00 b6ce5de99948c03b541cfc2576eb45f5b9d1cbabcd7b5ecb6fdb52e87c015b67 89 f556249a23bb4a06ccebad4b9e0f3d38dec0dc6f1962bc0845ec8345e6d986d3 813ff145e6baeaba964db15227a0608e29c8a4768c26a47d7456d70cfe7fb32a ee1b78f46a251955a671a8daf5298a090c4da9f33cae9b9ac55a8f12a0fdefb7 b781fef5be1b449f6202c757ab5d1044cb1727a840c063b1439874419737b018 620f29030d12d15609b30467a552f08ba20deae73637eca8ea6c684a6c600cc1 1f47b1f38794ea5d036075d792bbce7c8746dec3a8a3fa5d254c7f085b32a756 5ddb0a76cc9e817bd3e05a0ae46c9cb5780f37738326c52c22ab3c87ff763a50 7cd7a798cbc7057abecd4a4b9129108a144d60697247a09832eb870f6481367a 84df377d983c3f05cdcf20c7dcca56832d070bd41ba5656a5ccc1d5618b423f3 1a2dd8b6eaba354d7bbd709cc1b27dc4eb360b410e1ec8279faff94e67fba115 27419a2e86d94cb7431559b48249ffd52cb9c8c8e6d09546147c504a4f1d6b94 c4b21126a92bb5be572aa9f9e41298044c42fe63a2925f9a5fa898a6dc8faab5 60726f29b7d0a23af1da1f8e9c08d6d6b4a418f63f0e698177ef1183d0c35601 3e9f79d111cab6315b3687595ed3045809b61ffe14f4e995b78e9d3b8e3ca03e b3cc359e754c501a0f289ab9e16fe21c1268ae409d37627eebf31313ab4cc535 fac1760068eae1425f32d8fb488af66c9e094bc90abf828a1a0a740a98252f97 344edf6e0c043d76f3b55214c547b350ad50af9812598ab57abb091b1d2c6c21 2d87602cbdbbc7e5be8650b0f9183c1ba11d4b7dd921aee991100ddfd0a9a071 d7d575ce7d9348d84fdf1eaaa53312e2614a89dce6373d578ec2ddca869c50e2 6def263dcabef19b1701cccd95c2d19641f2e0a8bf0cd475b9eed5457f7314a5 bb673d18995e56b6ad253b42137580b1d592f7c2338e980b218cea0f60154c96 3a80f13552ce1d81883fde126dab8c934744bab3d5136c7b93dd1d0c94c37e98 ce9989cbfdbc8136ab9492b7e83a13e2cc8338046651ce77143dc8967b32ba4b ff9955c7c43183a4c8b5cc3fab41547310adb81f445a1d4780cfbfbaff21749b 09d0baa642e5e95b27c9f44ff58ad3252979ad9de428b82e939a4535daacfbca 293187e839be0acac3dab0bb7ac3e015550e35e878ac5251746e161798046b8d a360c2eaba7e1c52e9b15866c4cbf954011949977195e94c02113aa5beffc795 19a4bb17abce4b7e52a4d9ee37be9ff10ad79b874ce220ae6d35c7cbf62172d7 8f0598f2bafa24b14d9a43a792a4f562fb1d8cef8a804d57d3753ca0bf9a64b3 e3501c0f024aed333bc5b3314f98acb02c6d96965bf3d062085451f87546454e fe4414dc443d9883220da8007665395667f8a302e9d034d6c6dbe5db1ad72970 ed5f81377e051b2d1690161308e7071d524c5d4f2ea22efb238595dd2a632424 017eef17d2007b804db615e2d16964999df9a326aa471b28c64aa7461c29c804 e75414d1b4715413044ab5eae4384c9c8e2bc2dbfe1b78d7aaeaf0dd7f0f67e6 3b5d468cb2cf80e8aca34efca89e56bdef31aa787f9ea907b4e63f0e930d5c35 a27f6fa9f6151094207ed772a45c71454728b902543caf1cfe6f9c7fbdec2959 79fea796ee773fd9f2a9a0115726ef0d93fe624fe3ad465d4cdd9e4ea334ef2f a365f62eb7de2775e6ab85b976b68f493d9effff7c5248ce2ce6f0896a51d82e f979f57b02f888feaef2fd255f4c0bded927239233b91faffed02d5bb0f09ff1 26971a9953d20c190f5845cba63fb75bfb9961846e7ab3b0c9ec2c36071bb025 f3468ffc7c099bb7fa10822f3fe74cb0ac04c1a6954e91f08bc59859870ab4f3 ab0995249d89b39b6d063b96a668d302f98414d3c24717b1a6aef146b7ea4db7 60b21e30c5868bbf3fcd4b4ac52f7b1505f225bdcc2a5d20565c3c94124a82e8 02092995513da9c8bb3724b79a84f62c4b3329a610a918dc6cfb26c85181b69f fb7ce082c808efad8a3f8156560c1ea14906a8fcc729bb93ff8005ebb53d71b7 e159c3373f66bd4b3d03048a2037f15f7a9643b78069970bb6683b1f473a6926 bba5392cd13a1b161b515e7ee2d02d11d0a6c25d62da4b89f5b22ffb108030ad 69368d4145008689465e0b82141011efca968d1cd6fbe633c9afd963a1dfb755 13ba6c77c4d8a5f399d8f506f85be3333a21b86ce035a3b03d185c17aaed138c 075b52794639d53ff559975a3613fb13afb7acf6e813028b1d9a59f0c0bf6bf8 523ad8db3301155ceab385386a1c120f9b285e8aa7711159b5c2c43fa9271b13 8215f4b5ef02137d17f514495773e43f4c8b105fa73eac178576eafccbccfdcf 34aa3b087d64f349a9c8da5035eb3dea05d548df4756e17c34e73b8c3f76e89a a8ea5e53a4f5720278ff6761dafd77c36ca2c3b004d282045822ce29f0d97cb3 907aa5e95179d57b85fd5ed63a3e64c4b6a1cb2b825ad2c7b4fc45a650e03a93 0e11b2bb1122ccedc537c9f5481f8bea285d7e9f0c1c525cecb0135a4a20299b 11748877dc7027586497c3b66fe89089747f2848da8ace688eed21e08226f71b 14fe14ff2abd1bccdf7262821812631615561cd1a131aa6e409206c936a13ae3 00ad33f3d5c65fb86a12415c5ba254a50aa447267f4fa8fcb0473b8fb0be327c d438d0b0c5065c3fd6284099fa4bb307c5ce17cc9b2ea54c2f8d53aa135a7e2e a3b3df79eb356309b3cdc42ae79ac8410c57663cf022783cac040c102524cc1d 9af32adceb4ea00936f7f69460f4f5d11e05c898a7ca5991c7494e0f8f46b55b bbb1de86b737dc96477a290d5ebfe038f9357f33365fb6bc49a739c4a8f95daa 0f5c89622fedaea0fe0177acde8cf8abe76422272b20df85b5be6a4c3eae9767 db7a554d20f8093b1fc7681b37981b8d81790398a718a4b256ae7d7dd5122021 8bf7bf7514f5c1c7fb1cc99b0a173472201ec21f7d6ab9bcbd7b691460f9a9e3 3dd133a1b2ee794af54b70eefc8556ea92addb81f6128fe843367054f9535deb 2026b043da1b5fbea4e1b8c19dbf6e9f901a1b65b8fe2eab266feef81751e8a4 21a77c289a0aa3c334c2c492208e38b5a52fe515a8086eb9199dff2acf744e1b ee55c335d78058ceb15312889ce4e7515890b662b755b863423fa5c173fcc843 cdeffa03a2dfed009afed7aa9cdbad6628b47001973520e422f48045fb65ec51 bae2d4aa3faea3b8ab161e019d0da8c3d3aa7b618aa74b7a5b03e14e1270d21d 0ccdb8f3b310741b6065541ed0fd0f005a3babf114d471901e2902f844cbc094 374e97fe4b25a3eae5bf08aa59c621082245666664a2914291ba8cd9ed712566 f62c07a7a77ca699a2143d7459d93a43be5936c669f1f05aff609868727200fe 15d493eac9aa47fb7dab2f7ba81943dfe5d8cda46a0670f43c00fb149e6c1866 08112f3a05a75ab98defd6099b93d246ab4f2f97f7d93f33ea14d5136796ddea ef4bd20ce967367de87e59b9c2a8e072ca246d80ee762b5c4db756a56f60fc24 302d5f928a0749a749d67acde9f9798667154cf87fb82c6eec279206c02feaa9 1b4f71f48a6f0f792dd8e6b2a840cbe8ddf7b6013d907aeb642ee5af822646ed a2f7827138c65bff63b73793cb57c5a4cc8ca3c8c53f80525018e406aa1cfe79 6942c059335a8ed685460ed02dd313e1cdd54b8bc178d65cd70d281f8d8493bc fd3d596709c1c0794844e3e8ac7b575c6c6d5a814809f7d9ac014b2ef1dd430f 41f8bb6333fac718f349b81f9ae2f85ab176363357d0443f931ad41187f031fc d47a1e2efd869d282508805c8a68ba392e6128b2108d9334a8ca88d3ef684c35 7108a96cad516e9086f4c555e1cf68c5ca67f23b7f8708189ff0e3c19ec9076f d6ebae90f3ad2d4eb5a13ae741cec4578e77f88badf5db599139fe2c7dfd12d6 ce696e92b2e1428456fb5d003bd0ab408ccadb07cb4b0c503dd43ab456341e8b 6b38293747dd7fc5167cc40412bfb3184f4f3c8b426c870f49e0c1cc022f1bb7 44debce4683b1c2641333621fafe1a33984fab14688e2455a8637d0661ef5600deadd11f7efff0c799c69690628152e4202eafa33db821833ce34c722817ad0176b4d967e4511edb94dd3df753a99f184cfa72d5d93a3b257b366611c4cdea053e632f964d7fc0c1d50b52ba86b43e7e6f85c4790c316d02c9582f2275b0ef006dc21b45ba085d93957aea802ff8d315ee79c0ab8c54659a692a482f00eb920b3de7ec58e1df94b4658bb16a1e4159746650d7d80eba61da92f710c6a315330258dc9a2c4dbca8263d067b4d7115178e993071cc50ce2368cb5ea79e5631e60a6b410a83ce6a1c8b12b8de691b1ee295052b327f8851d329554071a6e2a705072f830a0c9d43847bfec219dfb9c0f8094cba181d4c82b896645f58330211c807d70606990393aa0b996204124c0b8e064855c46bc548786cd165599010e8db0de52306b256f8c9c9eb829cb03fce8778b44e75fba36acb42d2ba5ad1e3c7ce06364659e8a313ceadbcb3d980279aef5648d53918a71cf3b031ae82d7756cc307979c2ddc8bcf6d6edbec38f0f5419a3ef484aaf06af89e2dc869a84062e8f80ff1b8068e0b1025a7522fdfdd5813aa7b5ffa66f621c3a55c63fff70df2a44d04f3556789a0084da4168ec3ed6994c6ee58cc94a8acf3143eee23592d4ebe2409f09cb00785a214555c68f88d6822e7627f7d3aa50015e399b70bddf77845000b73d272b5fc13644a2a678333882aaa67758ed50c66cc2468ef1169e13e785b086534fa396ac45704fba618c2f6c487929df91bdcb545d9716f7cdaf5195358014c38e7732ab03dd3d884f60d431d5aa676c1d1d3c6ae0060008a23190016eb00da61a283a0b64737391f1dcd863fca2d5932bc6c36c8f718fdacdd1ae224e20b3293ffda3545ba043549a4d0e5ecf988e29ad22b36b05ed8341c04262ae2710c39318a807c8d92b41ac9fa47a60f79b3d6b2f83f541ac1de352de59d2625a80a9ea78e0222d805a5c4e4019a87bbfb7f53ba3d7ab0854425038dcf5387470d084193b31628c1a0fc92b29c6fe15edd7992af07376991ff3ee08f17d0f1f6ed080bbdd3ceb4a877f8b653e09ba6f27326772eea65b48b824fce7646397ad0e30884077032101976c7d1a73187f1643b210927a305ffeaa14e46c59f6a44f1a405a120a192244c67f6adf39d3173226f345cd0806201ac205e16a7f9c7e6bc01019c84045312b95c92e7d4cc2d86a619bf6b116d33e138ef0c3c69a501c50c4d08cf5f2c2f421d546157da9a8e5d225e25fffaafc261a56a3da9fe24c5ddf68f07d499a2e66f5c2944f1398e6737d09531bbf29f95448f5ffba5237a65f25709090b42970e8f87a83c31a73392fc2791c451a243b298cec7cce929f27ff5184c0717832f926fec75f8e763ae7acfd2f341867ab6d2a79c248cdad636707f86e202ebfcb58b595c0d6dfe56325e78cd08bd0f498176d1bfff2afe2dc629c955a905e5946c2fb476968b0626a1df0d4b9ef5403ff6d46882cebc722ff6808aef84023b0079f8bbfcc54f67a0858bb529e9f8f63a62cda33fcf70f481e71dd891c40cb9fb0a79915711adc56aa2143a40b66ec4013ee582a6495ece65ff8c4a074e0f3871d9ebc781360a21370a2047bfe853a54d12ff5bd6040ed2d1d32ba68e1a056d648879f58301559d0ca01536630b5c3df4109d47068f42eefd93911e87d6050b25392f379dd66d864384a78ec5dd294e2edd9df97d19467b59875c83f3bc07de715034d67921de3f6a0a423b73bad461007416e0befbb81e21cbd8503af20b8e1b62bc00a3c454b918ce6a9b8f3a4ccd5e22bcf74cd2e4b646bc45b7b30c0317a5d65527d718b71c186e2e9243ea000d0809a2db387f3554dcbbb75985b70cdb4ce0bcf02ad3cf91474ef01dc5373efc7ff7618fe7d454ae37aa5e3309000d7b2a569c932ebbc4f8a6b4f80b09c6d0b595a50b983ce77080ae8ff24795ca060b2a012ea60b1dae5ce51d5a9f7793405799de2027178a230c8e3958eb6b0502f29ae622bd0bd4c2eff4fab5b95ea99cf74a4e759ae5cdd3465b3ea943e63f09175d7c325c655c33f46ad30636aba4117d51982de34cbb9646f04ea81da17a032ae5f924052416c2be80a931908e10b302af5f4f79d6b037eec4192a05ab3603b5f9679aa1e07a6d5ae46d80d47a536c21e492826af9c71a348f767ec8d9ee01d5d2d4b8b0406265117d6efef1c8471c4179d0a23e89f016d307457b8425bf0ddf43327f221a5c4f7df8ac315c88d371f588ca41397b61f51a065f3ebf6bac095ab43386a5e03d2df70f5e2d6b00993a0dd585ee5ff61b38481ef8b23d66b20f05e8bc109c0765f5b2b69439827acabd2d0da0a8e72f170aea46f64be6255f016eccae2afe2c7112fb0eb5b8690cf0f42ab8ace2a1b228c911a335096d76af04db441b7db3a66fc5d5a24af33dcc612f9e73eb233ca2db9fe31053b7616a0804e44e2d2e8bb3247ad6cd67453a76c1e01fdb38fd42e9608849c5747742a38c0835a18f62da2a5efdd0f96828028d4efe203ed572ff77e900f37a0ba675daa20edf79cf5c617e81ae86eb8f2e0c4eb53683b43f4dc2d53d40c0fbe40774ae2a05cdb6f49565beca5ccbf6841ea20a06f492c2d87bbbdc4612e6cb2e4fe41bda035e8b3ad6de771126830906ab61a1460b35cd3963d5b486e58f61a61e6905240d9ca72ba0a17616a57382b765c3fc73f997a502012995b10c2d400ede7a19e40f3a72a0a8bef907fc8a22b5b948cde339b995a243f77eb3ae1e0b7d34f272c90bd261fdb46c25a6db2dc4a632569d334720e65e107874cac04d96a380e2388a00ffd3b104be8c9753707aa511099f5e07b95182a8cc32172119540b7f992d4e098c47ba292987d1cd4175ecfa1d8b3a46536ee63f3a12034d685d03e652d88d02e1826e577aaa52825130e88a96578f2b772a24c3be5f043f51900b477d8acf0b4e8ecfa6bdf219b6ac087e399a86475720e5f9a613d455936c1d535f525b7304198cc4794c547f3668e3b68579ab8dabfd6ce4e63ea6f80adb044610537c1403779a7bd6104e66b3e768895f735699f471dace42e9dbf0e888db9c6d3f53b5072b94552e7ce4c68d5bab00de05c462ac99e36e254fb9b6a215bcf0a9bc6682096b9967ee4d9725bf9e093b2bd2f740cd52d4a658b685ffe0d5992907edd4800276cba2b590a07d2cde4205b7fd287c438e6317b829ef7ef9f5b8e75791bbc40238d30c93098fc3ac6649cbf94acd24813e5d9511b9d1427cde550c7e3648800102b44bc7d3c21c71515080e7efd3103617ee6ca96ffd99c7586c7094a6f9d7036d43293491030a9217197445ec9f8bc76bc9aa3b7c5f52763ea3804429d0cd0cfa812968991069f9f9f25f7b4e7f1799a700958211c6e706a89f8adc1eacf906a5bdd1ac0967d678784e22995421377a2bde96ea81b94326d1fc2d793af8e904eb61909429dd65b39fb4d5d8a77d15014c34e686a4fdb3803e1093d44588b7060333941f168596738ba7954dd32387caa9c8308e8fbcde80ecf1adaa7526380ba7b7822453a51d7756040b0e5783e84b369a3defd78e78103e991f6519cd2c0c405be5a245e2d7b789c1f0f5eebf1a0d272e56d561c497b2aed631ef92428c07d1e5e791c3a1aea586eadf2e0e52f42e1339fa68a6bcff9ac441985d8cb5460074f21a07fd695dc54eaf7059af1b68a6e4969d771a08d0b697e65c640feb9e06fa68ce92624837a8682b15f2624ac43f968104ca08bb8100859734981ea1b103df664e06a8b7c7343b9ad6ca39a50d74791e6ee67dd53091f40fc7d786d49904bef1e71f618025696b74e8c56e6ce43266191864d66fbd4cc9beb724c9ccec08e0929848ed3485589bc784b5654e4a00ea2678cabb1eba24f3007dcc2679480639db2871e2ee8b2a9160c176e4876bce8043e3d70a84401fb8d6fd870868ec0399cc46479e4b2131ec94f8e5cf2edf0384e122ce844365fa84fe9ced4ee5e002d3cd22f01298b16047a6695db4641475e1ea0a60fe8834069d4bed9b604eac08e8b6f14646ef227b9cee525632597ad9197d906e50d29ac382633685ab55cc0b264c34b47a7f55edacf7b792aced9585c2a311f5cbb0f6ef1f891299226ea70e627181df9b4b8935512530202925c2bd2893034ca0753fe6f0bd230c95f3550e9265c46a84664fddd498b38f5ec61d93b1511b8a7ff88f55c2afcf3d6a3bc50aa7ee19de1e08667021637972791608ae00f3dadc5b959563a09786358a880902aa53ad419eb04ef85453978fbab1d44e4769de7acd900240d130a5139388660e90a5e380fcf20f53a98371bd3eee802c24bcbda5be5aec5a6bc9a505a161a806ab6d9cf6abf13543e4ddd5378683a5b8d725ad41d35be390206b1133d3d61a0542595ef69f54b66373fe6b7d3b8310048f76ab5fb159af949fa5401a5c129d03291e8da02cc92c6b2b936153322150819bb349a7a54278a116035c6ba1e37309d9ad445b1e87564c428086d97265ab72ae1ee9dd5fef81e03b3878821e7389016e4b4c3af22389ae9347ebd92f397b1da0ac6a535a5fe46dedc8c72610293109ee5689a7ca8d38619928b7fef0fc0cf5abfc98ec3d93a8aa7861427f90412d0aaa2f54ec68d86a28fdd1dd831d2de71ec5db6b37b3eddc278325a2d365dd740b6b6b744bdb38b674822847e796493691326abb6b7391298f4f71ffdb2daa08032cec1bd6c55e3500f53c30b0270c1e465a441fbe54652cab78f0f26ab0b16402e71943c9f26680407307a5a4015229c00b7a80e6f27941338007533cfd19020f361136345957101a3e62cc283b9385934cd14965381abd31923e7f7ed3b8b30e431e2c3e410296a5203e5365fd1e9a5f0804bf18225fa530ee2490b31efa3d06776935f0ae930afa44408b5c7a500c187a568bca79642f6455866e73b5b3f102042c85c03b901f445ca39621bf48555e5efad2c5115691713fdf3a41fdae280e3efcac85ad71c33ee25c78a11aac26542e43f5267dfb2f12ea31db8c0e5d3108c90ae21c5a34b7eb6be75cddd536a0bc301a34a5bad03d6cfba94838007d7e00b24f7b4947ac565b4301287a533fc8aba0c947edb65cbdb36e6d68509236a9085bb2829055c5573be0b80de1b9569c1e5da83c9fdd1494d5b9f9b38a2d146d067a11a85235629002d7f80b32f7dc7c1e2f23020e1ed86752c6b926a6f1dc820b077a93a32796aabe9a7762a9c818b5adbf8572f13d7b49075ba6f7b56e88560e782e0bec352c384df6edf7d862621c410edc6b47eed477e096f13268651fbf020a1728560f0d9f6075b50c0b15d50cd39a459eed744d3743e05cd8b226745d03e130b7dd50bbc32b0fffe24f706873d20e84cdc48e1cd105c05852bc478b300763f06fe6e6b7e7f5512e65de44bae4624a5d200a3e9cce42d635e411e8a5160ea8442437a19f6b5219e8e89d8ab406719c2275abdb570e63f913532ba1d9a40234a59c3dcd20bac04527949ac80e5160118270bf1e7506f25f4bf1ab3d933a0fdab7c099140f9c30f47042763154b2d4abb2194e4b6a00f24a9554b3ae8ebb0a5bbc50a34dd7b2f11b5e321ff419a931daca75154e4656e4e1ec1116f8ca8008e3c1b35b3c670cd10ddd039276074a3666bd9a64e7217ce3482b5ea5230ac00750d6d8ab3d0b635bd0b355ea60eef9f760fa108504ce2d325f8bf6124016100beb1ac9d9281274017ae5f24b712ec361f7bebcd174da90e66601bef8cf89020e74066fa30e7ecab957f9bb7e28480c354f6078be92331c44430255ea060419050cae9b10b38ee8de61c3eba27457a26240ce8558eac82c414be3642ee98bb80fad21af3c9079d6d839f688f2a3068cfb0c8b584d32d9d9b84eaaf222b20c040a4fca95c36d37ce4bdf92dc001034eaba454cca73e859b4db5c37bc1036d0740ce4faf2b3c1ca7e6924d61054e62de09cef8d9f67d35e24016d32e5f57aa706016faeab17e213eed126dbca112169d14b1d8af0a5be38505bd7927e1bec890609b11b54b3c179cc4c1c3919d8a1debf4ca96b543ecf3b6bad9faf54fc8b84b702ca43fa39f87c9e1ad16f1a97f1261fd584bbd60aef7e45dc6e5de65834e44808746d8c15dd64bdfa8b1a9d4fa0160c4d71f6000f8bfc0867855e99864785c00c6ae7a688dd5e98f91579b32a0e931e895736ed5c61ae8cf7312afcb48986950858f82c126e10357c02674e7b7a7babbd4769e15c42583ab84fd3b5c015098304851a55fd9dd7f197b8390863d2dd65b7494204d24d4aacb7b1768fbc748fce06401648602f9dd88717c357a1815883bf5326ffdf87cc355d67dd6fb2e3064306717f755bfbee1b9611d28d8c2215f27f21e964a05a4dadd913707d32f6e1fd053a32bc384bd0eb2ba84cc06ec629bd8422da7c667e7a9a3345e11fe765879b0b92338c1e56bb79c09f6bef879f56ec994ffa791f5cd124716a0b46fa52e0bf0a30b817ea7940738cb496eafc9a8b1c386e4f188932005265ffbfab811ef561021997a0402396ff85c1488b8db3102460204bc78d329d2631a2c3060f1ad3a90fe9ec6fcb54242f99638a85d20cb29f898b48d520e58a4017d0186f2ebe551406b78c5c5ee31bd7dc787d7314d5a7400ad51249486b8fd983515f29cb4279bd0e3696e1637a028f711513fe646f2cbb5faff30f39cd7a33aa06a6e89886e14301aa60eb340e67206fa9e33965e64db6d8b5e8ff17014009f81f7d65fd002d5d095a9a3f038a0d21eac4641ba73f988169797bffb50f1205719c4fa2e3ba574800cc0b3069d4d06b90753ad4e0fb25793af63486465675d0c74f15dcf3a81ba505d6fcd9591def08d90d12df58e71a76d42d7df9fa53f54ec172af7c41373fed076505ebee6126a584c69d95dba32d01e34641de832bb51818ac908ae1db4a3202a33e2199fb61f9fbc2e0dc2cd288f5089b682adb7df8331c20b905adf1148a02f67da6536cd4efa031cdede65d98e7dddfd966be655fea68833aa3c3cc47690431e850125bb81803b32a2438d3d4beee6b2f8c99dc5c8b72e7113c0e1fe21b066fabc179030e37c4d2bc55c15a9c2ddf88349697fc75b79f95441c547e598d0792cce4d85e9eaf9f6b119ce63a5cbad841a0d55de6ff933a1f9ed626b7bf190ca32ea487762c82e02f505385d71c0729416af67f641202d41f2c41417e9d960e99aca05773e3f335b7bf6a04cbc76abbe816112a4915d8ba2f25d9ae6c78df01b8ad6b2e1f85b174e1a120cbee6de08427cd68f21a7846e2695fc5a7f8fc590aceb0f1b7a1f2bb469def98255f227af654e2c446dd6cd29134649dd94081a603210fe2601e9b8f0ffba81cf4f67d0d8683bed2e6bd3d0378954346084b666f0b4a0527140eb36486cce2e88280dc7aff5c193fb8183f79c9562464926e7e9f0097198bf30c546db6de27cb51ca6c6f8056af54b344e32fc19169e11549c4e403fbc0d5abc9aafaba0c63583ac462191cbdf4ee683400b56121b0a979423979066e26e684795c492c3f7b66b81cb2fa1a3efb4def5d7643bae3c42422fb5c8b068f5d9ce06f9b0f0d08a244e48a565244fd889bdfd0211b0446653095d962350a636841dc5d9507307f8f4112f0b4377b7da81539525e9eb86b4ac104b356a90e80e83ca2c328cdf48b7d8387d0552060dbccf70a7e685c50b78f16bd71fb10048a18b578bcd74625ce106b1a48395f678d0f6cbb67900fdff562fc3c5bef7f0b0c88285829790bf783105fb1e678153276481e823f7dd90166be49555692d90955b16e9e1b09f05e40b0ff4d858882e2e26dab014d8e1e308122b7685ddfbc034f2222b1e15c0002d385990d096215de4488f0846cf709db6fb74dc8ee70050d13f44ee130d7097208dec0b713ca06db42d4e8040eab4feb5e9844879439fe0b4db1a6d2d81594fc54de8b59c4d991de7d2046d358aab3368611751715395602ee936a1851f43b986b151a4900ddbee2494b7645c76d9eba86da1d24ff2bf10e true +check_ring_signature 2c2334c140235a16cbda995299fc74f5890c510a37583543e3705cf1eb845c2a ce8b160f5a6de779ef783f016b641053df3e803f7e99d506904f7dd4ef3a456e 5 a9fb8ec1c9d772d2c4a38bfd6a97f50c789d0cb3d4b9a2d6ee421d97b8b6476f 54ec65329b62ca12f4f2355c7f0f046df592981ae1e038e9dd8aec60f29da3be 5fd6185664f356951491681ec6366fa320a4b6d51ffc68348ade7396c4937fe3 42fe5850ef70b4e0b344c128e3b68e5acb4ba250c46d6539a461e0c6d36615f6 6ae6034d66742e598c0571b2c2a78f900cc3a2fe56c8d07ce60ea8beab9aae4a d3b57effc531b63ff21e0f891e27155a083fbd00b98f70834a5116df965e86432ee4cfef11e53ba83e9fd6d375b8e2775b26eff47f14f299e5271152b580bf0b53d06306f8edad9f68a5537fca548e18ebac0aad2f987d750c0d9c3fa97b6f01fbf0bf27c82c2063889c74aec025a9f654c6e917108439909fb8f5dca1d4320284c1295890cc3c85ed3096f4be10c2e13bdae52eb2c32c0525d1996f04bb3807c1c009fc4a968b3905fa9ddb91cbcb29bd01eb88c1926f7104e7f499d4e01f0cde8f844796ce113894b0b6ab4254ccd9d270f3826399c0d501dc2f3ac627b005548dbb4c27cb8812a2c636bd5f817bc1febadf35a06d1c2bf6f14117e1080860e2df0cca52a3a1d0f07a005ffd4c42a374d11d40863b1d720b811143b76c7561f1af856a92ab7bb0fb97018293cad1311c996f9302272c982316b4cd68c41e02 false +check_ring_signature 5bc3ad4fe9b8489b60e2e3f848ad2075ff3204e5211cc4ed1a9dda8356a10a75 a8b2f1f76e105de31dab9c6e72af06cb08f818593b7bc054289a930b83628150 43 6bccd2cddf3aaa5764ae0aae013d084ec9959afa25e07bede518d09efe5643d6 a0327dd383a01582180d9e059b5387eb19065815b2b6b4f70c2970d14c0ff78b b5c4568175e9f8f27f49bd160526b9cc4e635b1cda6040b7d293367fbd04c4e8 a9493cf5aa8b350a5b0802f5b6aa3f1ec036484f871fb80ef4667abdc4f2c2b3 a498ba0439fff648155502283de1d8ed194d9090cf9ffcd39a0d4d89f6d213a2 1665fae2e94bb34b22ed8a2c4277ba7f7d7edf164958709acf4151101a12a742 73ab647285f3743ebde52a37bf02c3d6baa069597c2264f60eac324367a997a5 b74b3f94e923b38420e8c6385fa06df399781c18bf4190e1162c816d69ea4e17 8a2be672e3ecd5d4c3c2d7f4ee073fcb4fe28b1e1867b6b172d4b92c83288a96 c51b38b5185f5b67a52115d1d3dc78c61f4786c7dd3afb95ef52eb976e0707e2 7883f085436cd686776b6944dab86ace918aaa9b435e4f6bb57629c6ad2a1b27 7f0b73cd213bc803390fee110bcbb090934288eb98f11ff7c7dc2cecf9244567 9e9381c6699daafc69b2cc32d67f0ccd349cfd7e7e6b50f9fbd17ccec7001a4f 31711d6abf041ff8f8297227461c9f4b9c05fdbdc25672b4a418a63e033e520b 3fb6563bada751bb7e53133bdaae9b7f51ee27c858885e9df313c20da782fd1c 119a1d132aa53b860d7f4e4489e1847eecc4a439cab158390930b920857e97a3 d9aaa4c0013be64b7fd4da1f36b66e7c012cd883e60aab0f76c86347ee1f1045 338baa161bf8cbea6c6e417c4f1bf1ecdd556583bbc022b370d989122ef00054 5a0a2215faa8addffb073524347c13ada1e39fa8157d2d9071639d421d673af6 d2242ccb702be5920a9ea4daa5878eb28a85b0c8ac429d2047f0513ea03b9771 2768444464988e70e34c97c1f45e4681ff22aac92a883946ab0f2834758b4c85 c5544576517ec45bc7cdb7f838ff3d03b641cbd3d7f11220127dc3cff7f563d6 6d9966490a8a77c5ff8f4b8b4153dbdb100e2884ab9440e0e5f710d231a44d0d b8d59fbd70deed49ffc84496c2eb56279cd32a839d1f72be5085789265c9d4e5 02b4fa8e88551266369c30ac664cfac3c041edb43a6ffc017aec0932930e63b1 041b0f29757b9917c2a37c1f7f48bd825fc2ab7e366b9daad0a05a45322ea055 e0045aa72da4a06680a134f32e605a7e3ecade41bea78ca91e142fb63c5376d5 a9ba636ab5042fcc5d834d3e08ad2c58f0fb57667f982b1c33c525dc8fedb21a 0a90fa8215143600071f5fddaa6c2fba5a19548fc309d60f188b250d52d4afdb 258c3915b22aebe1314b79820020e402fa2d58b2c642cba4604af67442caf373 73d23d713ddd5fb006bb5e30edb56cdae31b13c816081718860abbd577b46f61 5c979e361d46c1186fa96900bd62e3760495627e4812bb341d36b8d7a24aad49 d31f3ec2fd13de284b0c3f16601d2810624a2be0515c71033bd70558bc1462ed 9c89e8470bde2beefdf2b15ea9fdf8b867180859f9a5ea0a18e811746d8238ba f257a14b7637875deb3659e6c682a4de554028e98b9626ed73003b6615953291 fc238198a5eb3fa2eb475c84ef6be78b42d13b5b2de222a8fe962d93fd29e18b dfb4d20d2cb25cfba5a4a683773b02e3800e994a891c20d2f8ebb14e3d484c6b 9ce799787237e50017f87b25450a45617ec8553272cbda6d352f598416eb1eb9 b18c20ecd0e09bc926be5c3361754032ee7ab9c534075f740bf8d513ac0b75ca b7c366c36601bd5e3a82bce06415246a2cdbc476717393328c008d561a36d927 1be16770314ca49bb8e1c05c170043c22c92909e573bc02c1b3a915f48902820 34771b0fcf70b9a371acdca7c1a05192a7678d8bcd5f43b4dc1ea23693efaeba 9afa64c894d922fbcd89447efa847c102cf90f8e8ac6b5a4fdc72e09750276c9 114d2ffa8896c0beadf6fa12562101e4926092e90fda6b803b58af5a5da96908f2f7933fadf98b4ae3300c98b649988a19ce9fe216d4dc6eef2952646debb30db335de667ee8628ea800c15bebba9174b12c593407201711906421fd6369280206fd7a0c197a97a6377f632a89b2f306323b9daff9503762f5fe67ff0a4cf606d5f940d20e5eab41aa7dba289d3ee5beaf36ec73dc041ac3aa6c18888fbc960ed804c6cd8af120525bcdc34e1308c144f0b9113113236a21a83a0efd29c24f09211d8c3c643e600cea033afefd50bf317d91d100266a9882e4668ce7da57ef0db59d41863cea9ac9e8242235ef1b43af6bb6caa5cb167daf9167e5dc6fdbd507ff05b2b680b05f8243303db219a24f07470baebb955a5d8fc98c1b2ff18aa001c4edb61c9f9aabd4cc2028b5eeeade5fb05b913ad63c6071c030a7cd1c249403fe18d561fdc559569dd1adfd93adfdda6cb5f315d4f6fe9c2a7b5319bedaf2037d22287247a707af9ffda08ed7159afa3a8535b1677a8de57d596953aaaeda03d634cf1398f53d2d1e425856af0a45985e7bf8d5f30659e7c4dd786fbb78d40ac2921cc02832d3a67f271fb46c17ae5079193fd575a0579e016531c46bcc2f0f3e54d7de013de44ca6f2b68eac4e50b436d0c43f97f50a738dfb6a008924c20601635f31d076255052b84620f7ae8fec40eb0cac094603f44c634990cd61e20750cca1158cfe02bb7d115ddabe376318acb9090aac8a9d4b71975e57b23fa20670cdb2168da1fd987bd8d74522b9e38d88192052d64aaf4d1796c34e70e20f0ec04565d86e0a8ee043a0b579b94a2cf4c980780fa9a713055963b85e7259be07dba294cbbfce654ef001fe09c4008eb78ee500848981e22651163c1a1b97270bc864ebf06e9bb0a750bdb01bf65728911ae820af52cf5e4d1041225765b5dd0bccf2b7295e64bc23bf03d445e9795103a8a29d1b70b19e3ba82d3c1eb70b590238fcde5cbde2d051db2d52a86131069919fb8415993fcb94b8f46d5b63c35b0cb56cb98781f0900cb34e4aac2335b540f0f8844b46f34fa1a8bae9548c99250be9a5239dadc74e3b303c15cc819535f101d80932a36b4bd8df82346aac339a0e90f4b0aaaef0227de4d7cbad22b7b3ba379ec4e032b032d46544009d1184a40e9c180d45542d34fd8ba0f4f9ace511edb0a261d9465fcd178e89d0d17edd8c0d63b25b40def3b396fd4844e1c98e1e3ce36ff20b4f20f35182baa00aab223b0d2008ee8ec612c95263b9733ee1888025727adbbdf88944f93e354b25b81f7c0e3852aefd8206f074c7a3fdc6e5ca53fc7316386fb33f876c5da795b67cdb24044d8d2e84a4dd70ee8a4d362d2a0db1f03231ce470710c489647e4fa128f1760df527c8f2cb994bc856140cf262980b5bc7a8857f01a88d849eb426cfedb34506b9743d2a40c61ebb96515dc1d0e8d149b84577eb757bf83e83cbc5cd6968b20deba01bda4c527ec99176bcfa1bfd10f78874980314a3ab91c7e5e5c0f9953f0ce0462f35350082d849728aa5e59af0ee5eb60af5273c24ba23a2f11bc7fa44004201f0eca31d9f2d59644254bdd0a723e5dd61bda5444411e3cb7c1b5827c103ff31b38781e33f72032ef384a5db493d731b8ea1dee53fa44cc5630154bcc801df71bbdd24adcf504c6409e09b45ae7b43089e057ed381863c27b74ff66d6d0d896df4b74db917f07ef04ad800f230fffb9e2f560492d415dd417985673a8501032ebf854dc3ea630fcd193816c481113a0bf4120117db8099c66c1e51ba370f0ed2459081030c6608cff1c12aea93c62d1b2482d801f425ac066b6cfd8e120d518bd2f8293a2f7f2033c3c987d7cdb740330d7ed5fd9dd6e1514736ab9e010cf392f2cf9b6a89594dbfd593be350d8523e9b6d3516e7e06351826e4d5aa810fb9587b72ddbb081287313cd5a0873e3372eccb5346e99966ae5c5fa9ee6ee50c164be0281f1b6986a8968e8e7bd56e6380e931a746f7d178a2c79cce12ac7a0d2326c32c9a5ece0979817507e078982d26cbce270254829df0955ad63cb26c0d892019250dd0db0a4f786b7ebd3d59e861359214d3094686da318c9e48ec500fbda3091eafcb645c1993ad727f24f9c966c68ab333ba7d3b79c9441fa64c800183e5e6b91bc5a662290631377f605b749d5216009a7006ff728cb6604e67ee054a1978a54d9f9385c6142af10e72f98c3f3ff357d488bec23122a028ed6e3701e08084c2fad5da541dc1b804a00ae51df0e9abe3661f927fa42dc949b30a9000566c505bd693238b753bf93e4009cf41c6e111faa71c0df161de09f7900c8e09e6f512102d0f5f974b85e2384f8aa04c4db29870e2ec1f2f6bfc2d16f210390bf1f652f09282829df27584154c1bba5923388144aa5577b4bd622755a00bf605956f11e62b557e7178353dff96da32b6808fbe67a9dbc73edc5946652a96a80e7c890e54318ba17c054bd296df954c172891e84134cbb8f041ec09b3c082b90d727c5b24535acdcf257848ebde91856cbf36975167d76580cc82c4b55b846709c08b9b0679eb6e6ba817cd5b3a1017705bf6d2bb160a45d08163c4ba887d760afc7019dbed562a60c2d00d98eb13df15a808300594e3205e760cf4c9159d450c746003a980f48576ac7b8cead62ef94efa7079ec76f8425cdfd08352e918470165d1859ff938651425be8253924c6b3d05f9fbe4197ec061b5bedc8d5468230f98fb19e2e26dce4d92a9ea5063f2aa56314c82135032a8950f25d3aed2512d07599a0f85c5d17690be9aebcdbc0ca08402fc63c1e8c61af56dc4450d7e089a0acbe6ea1b831999394b2df7a5f65bab028ccba7884cc7f9b51fa03e5622dd4d0e545c2d0756ceeea5d8d2ce5f4551c3bfee464266d220b40da2bd2d84b093b505c6e8c64de89cfa282a06763b6564360c419e22c1e6207fef6dd1183cb12a3503ab79dce0ff6707a4542e2ef7cb3df71696a1b36e6ce73ef607d2833f15c1d400bf04a7863fa381ad2a3f6a0608911a04debc6194112bdd0fa76c2ebea0e4950846737bc29a60db50afda1be769d5a4c30bac2c2905d0d4a16853687984bd4f0e37fdc820b9355bffd3af2911c5de70b7a2172e11a74f7458b8021e30aad9fb0b3262b85c25c3de43dc2bc2b0b59906a73aae5abeb6aeb65dff3c256e35f60200c75d0c9ff89e3169b1295ad99870025114a074d20c4d6885c572e8f42c77a209916a4aa823f0dd9f0165ad4cebf38fe71fa7302c501f5a6ba1fd273582599f0877440fa99f3cdd795218389e363c456c693da42a737d56505a3b93ac13dcf0011e13e2d0d21e6217e45477794f38c2e2662b3905c4749f83fddebf2c6c91e109da10ca638e025520132e1366834ccab6b78bc2b46223be933bd5c18da4d3d306f0a52492c7d2570e8ddff86f8535e531d8ac03afc707ab6e601dc44be7e7ba0e3efb651aa500e22c6870d3ed33a479b703bd6192f854dbc65afb8ea79b632c07eeb11ee6cc3235c6fb77dc16609b93c8536fec1e75c759d7a36d8cd5a8199e01141313d915a6ad0a5034c9708c07f84de57ddff643d3c66ea2b7522fee753301e56c493339fcaa1e4812708431d52f0672a25e127fc9db534ecd9c4efe3213061e9a3f86338026137c8390eaeb04930d69fac09b8f4a1605684213e7a201890d96977c64514a5a29a3aeea39f5abdf9455d12fd3abbffd64d9680d8b108cbf0ad2967fde0014c5508631a6403c5964208250255cae0aa68d04d5839d8239eb03aab16a1c807649df8c544de058536f914a1fbe725da7ba65c2efb68c48989508aa7ebb8e598d553b2664686433fc0ae5c351f7e4544aff87faa8d1cef1d31273 false +check_ring_signature 549ed00aeaf29c1b2e341ca84b2bec4906430de943b366efed444f9777f0195a e5e4d1d2b50df563fd88c622fa0eecdb4197f58201333597f8553c0e196c9392 1 40aae1ec1d70989641c2a69939ecbbdd61a202acf89bc4cc25b86dfd1dcf8349 53e69c46ed6aec1fbb5ded56173d2c88369470aa7976951ee32abd052c44ca0f3eaf85e691311c1174e2b4df31f6f9690ddf7a43c94fdd9f20c5ce354e96700a false +check_ring_signature 7481d2e49663ecdc747fc683b120e9a2b453df6659498e1acbc78d1ec026dd39 2ed05c90afd7d10ba0783df09df219c63b034f564e7941a77dbc32c4df962bcc 2 9eea17c70b7f37b700b9d407923a61cb7887b5a70d02fb8a1ac47933a65039df c04920eac88e55e8de39f4d87d64aa6c04e9a3955c17bd0407a316246382381d 8617942f822ebcd0970d8555a24c0a62f6108ab7e132e573198126b27ce1180b357285b09d78bbb4e7111dc6d37e7e9be87f6d6a1d8d8c893235b675b5196b0450ac177030a7e5028ec05d19cfc99c23be51cf168ff3ce8097c7198f1a5d26077ca9c165c420830c35e13022c632cda070e4fa1dd4f501a553dd82fa8123550c true +check_ring_signature ba500fddc2dd4c09fdb1bb4befb5c5b0fae8418e18a75c62ce6714b35b6e2c93 41e9b20420da0db70dc2e8c80b17c856362af71f67aedbe7baa39fee40252207 2 40f42465328fd5056ee49a011c240cc6045e6f2490d5211cf0d788513c9fad58 8b55f12235a07ab83af4b33e4692838d904c463cfa4012722663c030e83a9a7d 9575fd8e2a6d49d1abee46b1e7ec4e916f2169821d99077b0c2ddf0a4fab99aad598fffbf9ba411ef221d8940295c51b0c89ea5d102abceb19de9c5ebc4d55836a1089d3667912e4edf924c71af1b3cebd1bf49a17db6ef19af7a5cb0a764b1c686d7d24857d212a6611b62cf626d179efdfcfbae731bb0a1eeae53e8d58cad5 false +check_ring_signature cf0d56064a03e2c8572329dd241268e963a666444ca85d0fa17f6feb59ce3fbb e3dac85d29e372f69f94194b889139146c182dff9ea7b12776c5af06d4e4e292 56 f4c3034595eeac1429912a1cc98cc87a83968c7a13e399a8ac02353633857d03 7469bf3a48c82e5631e6dba16b487fdcca44d0bb17d0fbe01129e5e103bfef1d d509ea29833033bc90e13766fb213b392f7d8f74c9c6d2ab1baf833815f4e2e8 1aa24196b6ee50f85bd2491bdcf0001bbcfa690b5a5c3f1ff5204be0b7b07859 78906430fae437c407f64db3e0bd3e92e895e681f190e50d15c29eadb8737621 31609fa2d20d5ab72292aca304e03107bf83514d287fa99741c9ed35c4fcc115 79147f04212037e9d004eeafd78c515e66bfd14568548195ccaca71cc0a30106 745fcc9f03aa09c211e0ae2b026277a0c8eb8d2ff1a846022b5e08b3c163165e c568651111350d16b223705f42c0d3ba93b0f35c57af277e2de3bc6d7ba8f399 1281497b85fbdc5665e966696f4f9a67ed947dc265fb5036419f4d184d460814 3350660de174f0515ce138cf293eb659583fc9c301d9bb79a743201b188ac4cf 027f12156c1644f5e6fc52599305354a5a5c5bd6d86786da66eef72b892ea5e9 0d0990272e85bd0691d6af77dbc55f7d20f71baae9d57c80cbe7369db6bc6d43 f3675469aa2d419098fa30769a4d9afe18af6a0c049ad476339f837c8a3db5d4 d36a61a0c85cbc33dee4eb8fb15d06d854171afde94d8315452dd30505e790ca 8d40469989b9228a234a6884a20b2152e14ae0dba3ce2d82df4f07caaa5534f0 3fd855081ae3b2752f70dc527804c759a6b9a92375cd62f9e96810d01a859cec eeae3cd31c567600d85b74ad58bcca27d4eeb8f54b62458ad96627bf38e88bb1 fc2d2a7da8fc8f3ff09d12b78f2435808d436f8e8960d6d85ef38430f18f3651 407ae13314a50c5b8232f6639057e9c91a5be356e34de85c5acfad40711ce880 123d97ae44dd04b69ee5b73e6c5aefd38d527141278c718bc7bd167c7996bd99 bd48b031631ff8a7475284ca2e06cd03bdfdbaa1cac0bf0b7fdbd26c05281e5e d462d01347736ff1e727c60d5e700426445fa2a57dda6a361cfe473455c30931 ddb55160dd37bb3f1dac5dc64a7ab4c6a232ee259180aefcdb9f7df6afbcff9f 3869b179283199acf2be903999df72e776251f187c104b5d4ac692ab60479571 471b98c810c20452dde3a11d1a93636ffa883849931e5f1ec9e8bd0f81e28ef6 a217e902343e154b9d062c3a5d453dbc5cc23bff15461bcba0c0529d4ab64a99 6e6184207ca0d4a2def853ce7f25589807480c29acda69f50ea4b865854dd483 a24e80722179f8528a3ddeee164ba38f9c9973868b7773055ab799c791c9b4d1 0a1314abf6dc497b3691a17f4c68cda5e4f2bb88d2ec60389413c3973d5158c4 d69a710fb1eede7fc1fd80fb98417960854cfd6f38c710352a97224803e71c9b a8fb69f642b51d04efcd1c1ac2f23a7bb3de3bd7fdd41321a0bbe5c633da9640 70ed0a6ba4ab5884e719fede50b8464f3c3f86cbbdd5928702e026ee2cd881b9 e3dce85231185172c1c762e2e08822eb1f21376faead0be58ed57be2650e4b6f a71d830c2b72153fdcb97c805a2a5d3a519c66b7f20311d3bb77d8907d28f277 d5f0329ee0a8b7fc449cebbd327c96aca6931498ce6b1b887fbc3ec09a174914 6f12da28a19910708d85387c103f6513ce2e4403ab79a29a3240a3000fc91a1b 9afd59035576646608f36ebb3904480418b79e1c02439ace782d2b8601818e54 ec23590aef5e7726e32acb01cc16387f8a7fea28df9a22ad9b2eb2d82e2b0d97 3d62c80a2fad1e35f0dd3573642790043cac670a8e7c3f4ab0c7ab7d55a056c0 756e5c7aa5aa1ed715d54f60ac411f73211610cdfbbadf8af59f414e2f212a66 69eb0c0eab1658e65eec4babcf40bc3d3608f4a487a85ffe0c7c2662aeb1804a de7b364797b2c72641ee6cd74766fb1212f34bba613b41cb9c068c423064301e 1b5e86bc03c35fa77edb9f6111835c18b76bdaa9327adcf2d3db0b0f340ec08d c85a53ee3f1626cb9b82020360c143644f83b1b49ac86bfa882b9499ac0fd430 8922037b2738e149895bef6b7f3b4027018a6e652f0a7675226dbcae048c27bf 788f2b16237b258ca0cd3ad6e3fd0a898ee2f06d32daa24829345d036c627ec3 f328d7112e1714efb4eaa99b4cdd1585fb1880a5604ebab03b27d995fd353f98 8f7e13f7ade774b98184d9008ca1f10572da95097e12fdf28d86c5302702a217 6ed7967fc98003b027765143986946706cd217afc448a010859d876238287335 a4c2918b38bfd1beafcc4c79900f067c5af38a151f05ebcdc6f5f28b2f439617 54d891dc914c86523d472dd52241a18684e5a69ce83a19e036c697c64988a158 18a291b70bbd247b89eea0186eba02f079d43104d88262a00ec6695f32ab24c4 278ae0afdf3029f78a4c94576be62ca1721d51b708e60fd15581f37e24d12388 7d68261f45712a0696b53a3784d682b88da4dd42981fe3d69ed4e3617b71eff2 92e5d4d9d6f828e09601a21f27702a62bace08f3b25274603c4105294599e14b 79f35b966b03718942173d1087446666e0065073d495c103d625af60970171098ab7d47a02f9aa8fe2e1fc21a764b2432652da32756f067b55f6d34304907e019a7693186b4da82e3ff00068a932ff9fe3377af5112d31c22e91abf4f74bc70f7d4cdde5e6a3828735ba63696a85a1c370c5558720530715f86a68baf7abf7066b25e2ae46a399a0174a25d516d175edbb42f8765202d589fbb67db78bd4cf085a55e573d502aa01ae0004d7b9ebcfe020969d5fe3a1ebfe0ecc1e0d4493f505344dc804ffb6566bd9842a776dc237706e75aaeeff6f8a5322ec52968fdd3202ea5a7308cddcf1d972aef225ed18484876de43a55a5ccdef30efe05af56d900de52ab3ed1ec08ee9ab66d4687188d9c80e05219848fc428ca601bdaa823360038ad5bfdf0f2bccbbbde2bcaf80a2ccbe0a398e09b38b59adfefdbe42ba8954048c0cec1f9ab5ee1aeedc29bc43fa49ad5034fd444a25eeeda128122b46b18202a6d11629ba4ad9b6445e2135010d2f9ed7922493b1b9df63d481ccb1dcab22094a32affccfb70ddcb1eb445542c70792bc6bcf4c0fd160819fa09d7dc5d2a306fc4b930e564433f4a7a6f22de1c8752995684fac963a46a546ae38dc402fac0b29a41b61c3ab788431288ae3c4dc8044b9d0cc999f9e38f9d9ea655e01f1e90f69f8fac6254bb9bdf8ac18905b794d743db4848f4b8d3e146accd7f444d9e9022f44d6f237f32de9e3cf6016d49564065bd178e5a2dedf97670366b1c5a87f0bab1728929db05da5d5799f49d9287055f18a922549f310f2497f69b255d50c06fe0e791fabcb6b3b12b9c5397ddcd394c7b15b9403ece845a573d7631a60fd09ffccd903276b3b1326cc0c2183ca625e6f727d5543d4e84760f89acfcb32cf038a762e644c314ae435ed40ec1de5ee98cb96edbf83c0e2d31b9f2198b44e410dbffe416fed9f0c893cd3cfd2341aa0c5538a9fc53cd80e12984cb2525af3ea07939cf44d1556cf74f988153b2e20737c44dfae73e4f44c7eca6c775c3c755b0505c12517fe5f3e35a4fb1dcad208332d08588d73db9991b98fcedaa1d5dc2a0427d0af5b0a8270dc6b33f0ad0145284ed22bec180c55e1f2db2c682868319d06df08fd922ce2520ab4dcb7153599d17d0237119053305d929e9343ffbca67a0a0643e3c84acfb441a1653bf537b87186eb9889d38e420b5d618e64b4a301770a3be269538bf84e4f53bfeaa6a86ca8ffd2aff6275ec334016c7c36015eaeb304f2fd38c2e1e13bd0a095202359a5510ed4884667d0e1309833154c87047cc20f956a22456834ad71f020df3bb898f84383d1ced499ffdc685d47bb541ac13b03f8eb482f7ca5b5e83070af16a9e72fb65f46a51e6008c71bc1cb8ebf78f78d061f997972dbec8db49c6c5647f3121e5298e9e0c9d3c0f86b3be76e6bd0a9ad0ce7030f81d3d8c76cade68eab56248c07224e5e2f7bc856c22b725e632f875901d0f78a6616f4f8ef578b6b38115f23e713bb36a30ef8df53d38e2535dc79e8091a63af84145194dc6f91fe4f049e9b635ec5f54c4ab1ca3b309580a0de22b000de03ea7541b74b56f893da91bc4a58b26c2fef73c20bdd205214161273d33c00e7f88bd6788785a498e54b69a0619f05a365f6b83756958ce97620acab868408dc442efc558b15023e214ceaa813e8bd740a5ab0db673e14228492914a2d680b8659dc15125772f2ea9c66543df80277e18d9736aa06c28a2d339bd050a5080ded2847670d6df052f505f97d2f0f058250719df623a166db937c2f5cffeaff035fd7aaf8d281d5ac1834c586f68675f516c6f147d24be1be39c2f12f7fd3ad00e6326c49f7d34b4f79a7e5de808d7676a77039019b7c5ea977eb9944c8107c0bd5b188d2e739675a4f258667ab0b0fe8ba98cc1712fdf96691d5e7a3ded7c801142e87255860fc35da44dc42f497b25e3b0490f10c1e89d6fe4dab15bd388601c93aa237c840bc8101cdc6724b7db4e6d26d25591227325d97e823529596320d2c464a91620a83a55ac776043b2f409058625942cbc623ada41fba0dad86750e681fe18f78bf0be48946c55102d49edfc668bba01aa88a797f06b770974f5602a9e83b18323c6218d2e9634e3ef0fdc0b8f763501244521eb0c97598690567079228a88aee907d9430a01deeb92176c03ec86ea1457e57e12ac0c23dd2e8740d371f0de325d58a213886d5d082bb4f9e5fd4815d72119698a7b66f886f74fc0c076e8180b7729904717eaffb01c28607cd3a9ac52ee5c1abbd27b299f7c87b063d78658c4d9bde359006628bcb3ad5ab62d9c6d791a4733ba592346d6427ba04bfda26f612306e357b9456ca0c9c8525a72fe5a6162be003991c20c348580804877b6811931830dba76dbfa549d794be185b68dcdb4a1db286a0b8504039f90257fb5fc533eea7f9e0469410da230d111500848080e82f5f77b150af943a0e00b21d52282d00a495db152802ecdb944280ee2d0892eb42ba0c7c827266de48062ba3c9e103ed1643d26988e869c9b8b3db05cd67957601cb96ad2847d0223d084945a9d804d5f14738e7a82293a87cc0d67443dccd59820113900ceaded9d90db21a004fe4b9a63013b15f647f241ce1b5b8ef3329ad4f6ad9cd14ea261fbf049b5ecb150de787dd8c1e513f1b0b86a8a0c207c84476d8826c8e7a629b57300ae730874c08bbe648df202235605125b72e553d5b3badeaee3fb11921134a080feba82ca0725f9411ae12fcbae2501358df402d7704f9bc02ed80c3ac924e5e0843070c234156005a55fbb31c9c338294476bc9d4cb2c4c395d3e88cc68db1907228e1c5642bfd80201cdfbd2768044a9f81b09da52ef8e6e4de338bb09628301f5a73f488514b4961fce6b2f93b772a4f44cb8e10231a6108a0f85bb1607a40c8ba6f584eb562a758e0e12e517de6704ffe8a3ea292eb8eca571562118ffa800a3a49925629b2c420ad9df9b3fdc34ab96712db69b6d1d9da54be39a9cb0a209f0b252bac91bfeb7653f6d18ba41775d85f2b1bd2927dd778f43c9dddfda6c0fbdffe6abee97e330c38c78acd878a8bff4399507791cf589278dd3e92bb91b06cb9136822fd4bc4d30cee13bdbb07760ad36e8d491bd2e9a551695397dc8e500682f638edb0b609eb7867ebccc7f727cd4b5ecf4614302013b3137627ea7ee0212d72b3f1c7cd15e231e077395de64f45b32051d61432f05f2c95039c84d3706616b4d5693366b7f192bdb0fcd5f227ec4733d0d81fc0c52097e3cad78c9f801714cb7f5c61ffc1e8ba63bcc73aebd7b1044e40af46686b5f0455b45be9a08014edf837e84d1ed98a0d1483bc390fb5ba269a5e8f87f9398bd32860e66fa730346c47942302851f1f6a2cc00fee2b521bece618d82bb85e0737cfce56e96490f420daf48ba5d65950b93f374d793555c4ecd2e005b00645474cf5d6e343e200268374d6c5a146d590a60d20716ef90ae30d21c6a249c7269e6ce83fec757ce071f9a63400cfdb92ee4bcf341c0f2989d051f61572263a7b70838d827b53d10018886c2f18df7a417f8cb4e13eef8838162bc9b6eb2b11b4aba8183a0946b660555cd0f2af280acf87daacfdf6609a9f80b1cb0916b75e61df5b64db1a8bc7d083ca576b9d84db7d848d066402271656657a778d4a1cc40de5c49e85a47b6880a3b465a8aa9b295bf4a83b95cd8606e017fdfc6cdede56acfae4ca72b003d3601deed6dddc44fd0cfa32dc5d95e6084c7ac891b4d475b737f423d8f3e2ed139023bc5f82c98d780cceafbbdb9f24a32cd69567b1cbbf52ae220854ba7ac727201a089d5f652394c6e5445d762ef13b557c5d821a5bdb3521acb69f0593c7eaf022d51c34e60d14620f9252bd736cbe7fc4860d82e0450acbeb8ddc8a1aa95190a231d1c0f7a2b18df7f5834479283e3f28d2129f08c76401c10f34855e766bd0318602e6f6b6126273dcd04c96224a9f7fa3fa3dfa5658f2ea9c483a01cdff8015a4547d8cbc07b646c14c53961cf7e6218641d163202b2a6fe1a8bc8d624120bc3bf748b9d4940c34317ce7fbe313c2dda3f7f3b0ee76a6e8c645ee3692b26085b46ceb8b87f33a757e9c0a8919bf9bcfcc7bff6c4952d023a46e13ee6243b0b5c9d563b707296b58267f96b1c475a673067b1e4079fea87f6d663d8c1fde5074152e3e26f062cda8429734c4187d42fd77bb7332576cad6d6ca3db567c06c0ce0ebaef8c805acb984fd7252696a81fb831a8654b26749c7954ecb9f31d4ff0b8f16fa65ea6da6dfec84f82a5c69d9a07667a34a64fed8bba3693ead43903f0d284aeef3c9ca55919b919f85a168f4332f72a2eeb5832b224934c9b5dd5a1f0c1611ab1fed2eb80f7ec6caec2908d67e14cd6a96fca7d3aa94d73bc64ac03806b82b243790cd6b00951f38e48a22c2b8c54b4ff181002afdc9ec95e26fa9bf00f0f3828725b47ada094c0b77d8281dfe43523b83044f4757b29ce3db81dfca078218f01916833e3fd6d8d3f9dff47619ed55b7ee712ab18b09ab9172dc12ae0e03ffab84b0fa0c0a75e6328993e899ac1b1303eed338a63a3ea0281c3c18a50d26694259ebe9fdb4fc1fd3794524fadea90019066951a115fcd05562dd37d601b87656092972f5bc554ffde6d671219f992272d4a70c87e0ba44ca93a8c5080bff37b72c8a5e04459f76facde3c44907649b89ef001dc198025168f729c445026fc00c265f7663d421076447aba9472960a7815ff0eece4f800257e7313fba06d16175507e4b88654ecce4ee0166addaff6e66f96659ea957240bacd0ccacc016e5e97bded0336a143e977d0dbad4d224b1bccb9f0eb0bb8df0c637627bc84051596a90a89389ad0bb733f146d24a2baafe02d5418f24acddd2fdaeac92d4d02ae00ccc8ad575dddd39e1e66695830d0e85672f9d5c8c0b506290a41eee429089fa38b37de9eb1f4a76a93b23b354b870437c38c6fa6469e3e076d6f43b398027c65ac0e4cf058d9ccee8f235685bd44214b069c2ec701f693d088478081a00f false +check_ring_signature 2514c1a82e544d65e52c2991515560a96371dd991240ba1c400a33fd7237bf52 e1b93ca695c4a7c693b3c39c14ca3fe1bd15f390e5ed52bb2d3f71b1a56e3366 3 6f6634f61f73228219916852cb5a131652d06f368bdf50e6d38a2a8f77bc36a7 1d85c818b720337fab85c60227e5eec185042fef039624cce700647dcfbfe8bd c25f1e02d902a46390362aec4b9cb3f57b2a617502bda083a2257c60f40e18f3 24f19779733a840da08b50f6da9cd2d39d1a6d161050827270532f96c5084a01adc029adac5217e99bca6b3852aa80bd394ddcddac7eb6c3b354cfa8d3058f002c58f4e5f19643b1ccdbb8cc98c96e91db61e143caceb77b573620c0cc9a9e0bddb2dd2a4aed1472d75c357ec505101ce19571fdc0825cbb16483ba684cb7e08770cbc8d1fc6599cb4d4ce9685368e083b1fc57e1b66ab34f9d65f2e30333902cebf6869696b0c322b7842e7658036a6c6a9ca5a32af513de5a98c4bd74b1805 false +check_ring_signature 2cb60cf93d80baea2a0b902af87820aa926e5887f5f99b1cfed817b7098d7c68 a826470fe224493c958122140b2981d23a0d7e1c6da4d3ffdec5bf6b29468942 3 1bc559242105e995a2a1eb8ec44753207ef2148259449b964cd7ab64543f3b48 90d94c080148b7c104920ef3576608564d57556a1114e8f4a9ba0aa3018107ae c972ccdcbbbc7d4643cc3367f9c449d485a4327f14d1ecdeef32b8f5833ebb8e eb185d56a530552830ed9928b387111c6f4e374e28a667d7dff320c5af4e700f79e91c67181263e98da5c35010d4009186d2b7b2cd95eb547224397b98a920017ac2ee5af82292ad327e8ff995e30eb39d316b9f9d72e49a4376f7891a495905524cd1675b5b2a253ceda10c99b8861f351ba4b4f92594e4b0f5350872bcca09fdf05d1f789616e44baec6303d8f9d66d0efb2dc6ca93f3946e7d08cfea12d0e4d99b8ac57e937c9d66711074fa9a3ca3eb89480b99949941c78404f057e1b0a true +check_ring_signature 2780303480718499be9ac3ed22224b11a1a9a546063b97f0ccfb696c04442771 fa13d3fee208650429e0032a3123cc09f02e626e408b28873d6a275c4035cafb 4 e40074e82d382446d2b6593e17bf71a033ce0fb9312cceee96b5c57cd324c10d 628f990ade6eacc1d0075e09d1cc556d9fa63e6f61b6063ac9ae0c1ac46345b6 fa9389f1745cacf8a4cbca24973651013fc360e07cce4ee767de3927cf2cdce5 02d197d4a8e60ee0c23de9617199ccba3c0e0f4babf6bce01dce08ee8430550f 5a17d63691c1eca9a341248c8ab451e4b96e0db2cca621f044b46eccd732f80b4a5bbc362774f67a49190248ba399d72d98f620f95cc363670bb42d00248280ebdc6f2e4dbb23b21654793ef006e3d0f577e03d2718db480668ef397c1923c007eea8c6190129c54702baf72ce283d21abe9978f452febeb5b6da3e57a404108b33f88558e0c1c42f1ef51bd299d86547b1291fc42402ab8070c1cdd3188630bc18149de4fe7f00fe34c092c255152aa15c4378e1eeb9cd933b9e99c26aba10e8ce2658461c758622b36daeae69a763b7c7092bc934fb0b48cfac4e11a782c00f3b3472713c5a1560d07f14afda501c58a20bbdf1e6d1bc7b056daa89dea5208 false +check_ring_signature db865d13c1099ca6d2046de2aa07780f443f165d0b5472562c9759007ba591cb 92ebbb8244ae9014fefab9d9c591cbea452418087b0f8d29e6bb0f206880e7f4 1 2129492b3bcbaa235b071625a39166dd7dec3d91921fa56547214be550f251d9 a2f67e536582fad6c3c8d6828fa38a55a96d54d4aea707c509407287a724f7172b6757707b4e4ade3a6f3612e15126d43c71441e04178a5209eba777244a3def false +check_ring_signature f8935e9fcf0b9a548cc4ff69ad8d6e679b8cae659a5f2f139b8465d471bc9b65 f603efc6ac8e4355c743bdcd43f4ae3c68553aec34eac2907a018f73c9fa62eb 15 aa19292ae30a66931fc498dca62190436ecf4f8436ad78a5bfc99e4c2237969f 826d1d20862cb56bd7354fe0ca862ba71a5f39bd953cec831b3b57a4cbe2ed2c 5e8d33173555eb1bbe872237a3a332ba619d94db474f9e36925af4854a81cd8d f84582df6322b2042ae032550b2cbdef9e673da044fd6965c05e6486e352b430 8bdc5603bc9a47942e2639ab74f45f15bce4a4fbb0189ca235eb5708543185b1 1c6989b3bff985695543b939c0f3d0eae5c8d49a7e2b23786c84577507dcafb8 9fd757817a92b219dc15137f3c3101cfabde558471d3af60ede334212a131f95 142282da7a76ae4d3240cc1d22580224f26a90c643fa52354ceffa7e6f11ed5c cd05f5bd24322cd64eab4d04527d8aeba9e1e40926982b44bdb70289e1ce32c5 1cef43009fe47cb9b0afea8a761323760ba09e0c150088c36a8dbb620fa7f9ad 3be5654a6ba6765d4aefc60364e3a33d54301f806720ae86c3d40a76844f48e7 c2f94dbe966469adde9ea5973341957f3e8ef01c40f0511173b22e6df62303a6 48e77668de05a1b3e016dbd37df8583e932bf22f87f0d4b5ba29f12a0d5816d1 681abbdbf12d2643878e2643ef16cfa1642f66ce7e0203dd9145764d57af5789 14eb589ab40f7fe8541bbbebb8c3736679c6a236de940b386b27e5e3071f915b 295e46cfa6ad845345f22d57719e9168bdbd8988b682654e56657fcad9658d0fcaae1a2bb8ae7601a79ec2f5946fcc8194ceac58be1c2f8d764cb0bb9a72790b6ae30e49cba66a5650fa931b11a77234e373684e6e7900d7b54a6b64963a1c0ba2412836c9509fbabfc21a78a27154009e4dc19aad08cb7c2e70b340df517e02689164c662b75307f13cb079944c2d0f42a007fa205d6b61c9ada46a20c3c40fc277dab305f916649c118f70e584be51f251d881565b4c98359e6f35a954a20b97836b29eed6042fadbac668da09c37b521225b564b1430b93427b6be26eb406465cb79a1174ee2b0cf923aa0f7f314488d09bbd29ee1867651e4a0c8954a602eba3378a5364e9276f7db695001ad813b00d9392c630948025ec941a56a9e40e20db33ecdcba8b326003dcc4d92b4e29653d6ed75fda7412b19ef06a028ef702ebe69844d018897c0234d68ed521525c867491dad48fe62f68eee0b4ddd61a0bddb238521e036e219a4fe8b58a45fc1815cf496bf725437f34380a9007c2f2047cd7cc0c2b8f5c5cc87588942d488733b5ac8ec49e081e37962cc1d4e883b3004be41b9e4ec68c654f6e8391257a31a448158c2a6d8ee6946db9ad787f572106c061679aa73227ae2b861286d3bc0bb9644fa51519efdc97e926cfa5db567c05bf34b56b3c2ad3c7ff9f0cfed1c253f735e90704564dc8d6f4ba9ec77ba4e60ab1da89d955d863b4733de776d16933b525c0de865dff959dad772162ff22ab023281a76d9535ef08cb69302a72e354e2da491c6dc5cc01c07b448885a356b904185df58f0e55aefc56513460bf5c60d11b6aafdeb139a49ef1a37f0f24198e0a8ac4e12d4b1dad01ab015fcb579729d824cf454743704da3db97858e9655df07736e69a1775b002f7ed0ad1e8cb63bc210474df99f4806ade44f42c5c657cd02cafe08e6d63cea680e5bdd0772aba4d269147fe2fa88252842b0d5a4a8529e074e39b2263f2e05492f27b99e93bc6166b478f4ca0d604e360f17fb35dc72c908fdb343bdc9c327d85be88112923402be1ee3101f76e230721cef2d347d53c103fd7458a2512cdc0d76892093e5b32fbc5c76a056321192dc94ee638649e49b0b0824f194d70dee65fd5a80e0c95e7e9d85c7d5f361f9acdd8d02ce15b4afb90bdbc9a9373431835978114a1bcc9f90244551fc558b4539c3095e0e9fe33554095432a98d8821ba6e6ee389ca90a1819574bd29fd7f9780df2344bb110e5c9d07d37dec01bed8829dcd449d3ada30bfdf7c68a56d2bb023c0342ac7cf5e11900f1251581ef1cfaf16bbda4dabb6d77d2be28e7cd3b70952a2822e2b6ddbb3370c true +check_ring_signature 429170ead00b4a8b91ad93cf4b1da6e28c9acd160d16b63b1188e295420503aa 9749bba7365c728c581b623d0c836f4c17ebbc89a128a3e189d371473ece264b 1 895fc690c56d7778bfd78c60773e43398231663e44ab07324941bcee8230b006 839dc69802c7016e204a6f9fd06b4ac8f61f3a4630a6190c92fb912d642c401a0f11989bcb26d5befd38fd04a4ef36a0f667fd3bb13b1ed58a32bd70f15df7c2 false +check_ring_signature cd815893e58e3f4da3a59bbc92ac727ce7212acc8c7b308e390931d372026ba8 1ebcfc9ec6bce66a149d78f5eaa3d5246ff959cd1722bc29469da2903c9d342f 10 6713900bcd14aab039b2710e0210ba8795b725c8bea741ced9712a5c72b31360 0a9adaced5c5b4925c2c36b09711d91bdfe7f6ebba88f1044c72b9623c5ae389 26acc81762e5cb234d3c5ba95350ca592a16bf1858df4b4b9b0946c6e3147dca d849f2fcf07916897fbd79ea550dd2df8fe1708fb10ecceecb403c389742a542 410fdbe3eae8ee08371c3fb200bb417849eae435bb811809577e4de12bd27164 c6063c131267cb027de4dccb52e753aed849fde72612abfc6b2d606f0f0867f3 f327cc4c6122a325b63fc8fb2df7fcf49dd21dcd0664b6deb0792426d294f82e 4b7bce56b859917c0207737b517bf7df8801a8a4824778c8100cc75eb33d72f1 6c4e3de964777f8fe9def5c96e0614ded51cd506df239a7a02b6dd34f776e28f a0b4165bf6070874fffaf9418dd832a6f98da120a8fd45603d256b4899afee00 5e0d7c94b5d2f12635934ac1430984d2cd8865cb925af6ae85c0d94a63bc67062b56d85e5a456251d4815fe3d9054a8501457310542a3b52460a36c74f7ef6054d780e5d2fe6182eb63eb31a23f535eaedbf8b46a26e99ed1689dc144c627002a1a1b4900dc1df4243536203d0a9c36b287fb01f0ec6ea87c768a6989155c70062b0a05c7f7c0053d0354db95a2af19b462bcc9374d2a8e925a49ef3c4dbfe03b04e03087a4f41dde2b8d3398bcbd5802268862501264b7779deb7176ba696040dc6e04acac463b64c8cb26feacce7b26398006cb871fad94fef241146eb1908a0e0fafffa4ca5dbc32b3f66539d8366f6e1bf17099418364293084e1e31380ea9a9967286917bf04ff09dec328de603f6209ae915b3a1001823c7aa9d64e9098330840430df8ce7c3b3b44ee13db03f701d7e8e33d695c87c95d6f6e360260c3962462b8e817f3d314fb5e6bd6e92cffb34ff6c1371203fbe5b520de6e9dd00bcad4f3068698c8e9dfbdd6f19bf6b5078d2e968601a3a63b0e4af736eab1203ef54d80a9ac35e315a7f83bb8a65df2b96e10ed3f56018675992c94350a89108f5803e2ac7eb2a1c78fee5c6daaf708decba732d181c6a0c7b42d06f129a95009c063cd289928112a2687bd3ab31bf02a93b9c59f2d1c6f13be3207f488ed103302eb5a293e2dd0fc831065ab3d9ce741a4940790c7c666245e360b7fa8e3801a9e31c035ddce86161ee0cb6f92c3af4d801a076d2c7a588025bea81b1c8e001229af34ab5513cac4744200d93b39ecddb776b4ac103c15e8673ec6b49608600182116b71a669236abf2ce524a05e013fd08fa5b5fc2893d8a758a049adccc0456f5384bcbbf441ac494e297e37f8fdc4228bbfbc5ccebc5e51121dd1c37eb08 true +check_ring_signature c143d0ce4023138e2b65ff09635d5461e5f3a86737bbfb5716f281e21a831784 d39656eeafd57ccc0075291913b98cb37de9d1e0d4cf7e28765e78576dbc6614 4 4be7137bf36ce30af8b3fa27dbd1871db53022da968a01ec85ccb1b33187c49f 3808f9af77091f9ce34e3af3d7764f5fe2e3ea133ca9d60981c99dd716c6177a e287fed73e5bece61d3c26f84b4158cc56398489ede1916b90e0333896a8e287 04a75a3fdc09fa14d45e030e23c6ab487e393c0969bb40b7ac21e02def4f4f23 15b07137152d861551c17b973144a4f5fe0a6e0a67873f4a11c3e158e267340c1ed0e84947e0524ab1908029d7749df6f6cf7733b11a2a11f308fb0c52b122094f746eac6fe70e2eadf588aac5ea8bc42a4378a6bb87b5292d792130fd01490b70eeb9468e3cb553014e410304cd560053b7aeb386f8f63456980a2654035708b4a22caecff8d6ec807d808d65b46061c087b2bab0a0a61ff0609f13446c3605c1edc426fae5cedd60e17932e0bd09b445ce3eb099d5e21923a6ca02624a2b0102ea66804588e0e8ca96c7cf9365d1c4cbdc93e40f20f0a3b45e22eb7f42dd0710fc048d453c0a832ae6156a29acf38c68e7c2f5490cdb8ca2fecf9c7f82b00b false +check_ring_signature 9a1a7896d3bf39adfffb6649ac6952e9d7cd9cbd03c9623b80aa7fdc31f0559d 22d16ff88226e7d8642f9aea80b278101f92075d2b44512523de97c4ea99f931 7 0bc6bc9ea128bc7f81bb9bf4d13ec04086ec89a01a15da1f950a708be6d086dc 66f3b3afa7763bcc53d8cc7bc68912d05c5a83228c3480e3203f41626ede5015 7e436aff9a071c67c8624e15099051ac55168ab2effb370b48983953a7eb2a83 c6d3950e9d579a769f3e3394fd90cf11f258b70ea25ab0df4357ca84047b8f98 08a8fa76c6b4ba6dc59e09791478f3d8245c764affca3ce190fd5c08361c5286 13b8dd76ab21b9ca7b195fe5c4dba1e627c2466cabfce2e08c1c792ddb4d2ea2 a7cbb8e8788a8f034cc10c804e0fc926f8dbc770759c95ef347cf28c59fb7933 18f01820036130b6ff5f7beecab5228c719f2434e34bbd43194d5488b931d40bd63bd99ea184e4107830333fb73c9d80e7e0159adb0831674d8891ef8319250f11b14ed7909653efd8ce331008e3bd8dc9f4f64b88e1c110b45fc73f64ea62be8e9d8301357002708af15edd9e644977254b6d90d8ba974e0828a55b06d4c208819d9ccda8af4e74c6f04d0c91d09d0643b9d523ab84b208245957253373e5021331c22e6adc91d17896b417d1dea29dd344b3effb53820c2a9938ee1508ee8a8e4977eab6e943873264c5a1b2a8aa6f7ffccf04e431611dffeba554ca0b9b076214242464d14fd1ae9ee5c8006703a35b3bac869fa742701b4f65116798933cc55745aa24418b7589c559cb171434f4b0c165ed9dd6bc6697b13defa059410fb2dd8a598900f1d51689fb6dcf14854d1e40834560934487b6f520cf77038f01479ae4e8d31cf97f708b0b85822e8125ed6cb0aea1d594e6adf94e92135aa608e8483f167b52882cbf2970708f6973eced890c160386bbbdde5cff7ebec4210f32dca22ad9951893a3af4f37a88cb69186d6662a17732cfe15991353b7b22b04d29b270a42553d4df68a7a9af1c42e47d277a57def8e2aa16d91daae4518fc06 false +check_ring_signature 577af21ee543027d0456880de9ea12ea1193229a92dba5cf1fdb8ad9627226ea db3f431d36bddfad280604a9ec47e072ab86b88cadd8ca50064a3bf0be786f4d 14 64c8ef804f4a3f7f6901a09bfeb2cae88dc6c9585ffe8030e2a5a8672e39aa09 e0114e53eb336007952edf562e320cec481c55a6a9f01e47c4303d6296793081 d04e41abd1df513aa6835d2833bcb57c827ad412b1745dc3f07bbf4fdac3e2e8 9ab5ef7331b87f56a2ce510fcc964eb233fc3b3c81211794f3b4b7e53d3ed491 e1845be11037105df7a6e26a5a02b038db8c83395f4f66c893edfc108bbf2ec9 e6496e8ffe9a26428a2e41769a5da045f864b1e5be858b5d6b3222eb405d07c0 d8e4c04e27434a05a8df095b526ee5aef039936a5db87f4296d119f4a197f2d8 bda1a94e76ea546371bc1385911d9f6928d72533427163a738bf38a147758d36 5649f4f24ab851ebfdafaa42b9639638731913e92636ced288ac6f3422db6d2f 1d3200a7abc104fdc15338649010f1ce513a1ffbf5afc07241a8fbca96bd97f2 f43f86441db409c13fdb5a56b6d42afaae1321724c72479a423a12361f61f58c 93944fcf03f7ec1ce9837d595b7ab47ee23927c61fe70cecf3ebb83b3626fc0a 225a058b51276c90b325e78ce521981439d495722ddbff601382095ea545184a 6dd73f0a787afcc2a77e66832b1f276ed66548ffaed3257383ccbeb218cacade efb1d29e4339ca5ba28a43246776339f2d1696588932393216ea13a7e67b260afef2c864f7f1fc35fe4e5f4583d2bb24a6b4f843d98549144b6c6b58f47474014da2fbb43dec074c15aba07e4d0c88bac2af9d6608778ad77733d686be648e0b639e5e28ff9bd32b9f86e9b3da6917d8660f7b9eaffe89737bd804f86b16610336e4ec021ea22529cef3cec0b7b8c7b64a64e32a891409ac1b22c05af2ba4a3bac1afbea8d8f9a5c6aaface5e93651d9028d90d822849b19394e365b8f748a00d5abbe656f9fdb0f4f5ca6e8f0f10f87f0cc2974370c0fb77f7add41966d69098ef6787648b50f97b4ad02faf87dc4be3caa027e9832205b0975a1ac836f2509d678a3d1d887ea0f974752b24d73d5efb624d79055231c75b417ec68fcaaec06b4a658a528683fa6a69528c6d17965907d97406b6574720a6caaa59134ac510914a1ea198c086bd13e3e169c00b46739730f4bec7438d53775fd4ae2177d320f640fd7ee5ab009a8771432483d9718de080c4684803a1b68957f0d53fb110105365d29e3c9027902e10b9ccaabd6b1d93c2052e9e531842ec1592b8346f52c0220ae55d3ec8dcf2d64fe03cba7bedec629845a8059f9e9493a484d524ac3bd014098c9eec17b9eb1e627c5015b09b09d63c62135a02950649d9fdb522822a50b8ae831cb1afe0b6cf4597f9f520f2e714eca8bb4ff97d8ca7a2a14c82b0ef80619e7a8f28d6be2d8ef36bad1decd787e3faa6d8b4b5dfe3c0052da603bc9c30b94c375dd5ccbde2d506f50d0dc01d523c99b3800aa640be1061c73a101a8ce030791697c435f01c10231d47bd3d0c290e6f0414295ba12bd4371b87a2af4ff0e675a068f999c450d316ee77417bcc814e33d090f2103c56e28034fe794c3eb07d982ecf69a8dc3c345366783a4bb584822003befa339c251b1028d8f5fd9e10f4981da74b0bff6a693b5c7ee2c231cb15c8d841b960b52cb63ec7deaad7c240369ca4140159bcbdc846ca6d03b9dcdaa613e24199c293cc7adbef1b541bbaa0c71d016166c17dd959a08bea981ff7a9525dd2adf63baf106048686a61001911d0261e50c2038ebb2cfdce6f6ce3e8a6caaeb30ede764ba96f237e5cf3df72a0e2c8edc2916fb8b70a249c66fa021381e5112782216778fcda086ffaf35e9db07eeabec2c4a3dfc4cd810131de8028e4bd2fa8aac918330b12a5f5769aff21707c9165d9af40c3161ea578afc7a3f78ec3d51e38d85b2f70da1091729dc70c80d false +check_ring_signature b1019812cf52849b3d6af8fa81665e4c9df2835e2a4a509c6d8ac9d004aa826c 74f9e84e3cfbc1b0851c474c24cf6caba094b09243b99e8d76bc1bdee1c033cd 4 9fb3bd2ae853299f845d4770a798f0837ad0b949bae156dcaefbd362d368215f 5f366e99893525a2212a6536a1286d2caf58250734e7aa706f3696218811a2fc 29915bfb3329df4406dc9fe612ecd69d78d48297e93a9c8f883cc3a3a3581048 9f9e2f5a9d22eba310f05038d7497c458739eb9bb652599ea34e44076c064552 55e1ffbb343678dcd8e976d70c0a84eb4bf061f92bae3e7061cc24d61dcf605730648d19ef7ab1d511c7ccc8365bd77db540e9247b0990f007f66ddd93e56c0ec31f06f9f1d8fb772c5657c7ce027d24d7c2f171ffede16c07963842e257f0072c4f1b60275149c73b69ef20d8cf1ad066ffda167bbad545826d831c1fad550245aaaca20160b259cd0cb4746c46f037f11da7ec59de2726b55f19b9500e9c08e683fce16bc93a2e8ceac7f07d37f671b0d85cb22244d9e22a722da24408dd0bdb21bac30994898f7d2cdc577ceeb7c75826d3efb3d95196d4d75ebc991e72008dd510c1008454034cabb70ef1c87de15df8bf1210307cefe0711157504d3405 false +check_ring_signature e5cf36dbff2a5f5bb70d0eb6ebd88e522cd8d8c8ad499417a196e4b374f06da4 3ff969fb31ef5f20f42e888c8cf5e473f5970944f201a72e0b73401a74e199f0 34 8a2b6c19d03fd81f8ed064f45d7b3e913ab1a2b3cc92b0be1bde58607cb700d2 7de9b3e3972371ef8722cea36172517bd2e6c1445aaf8f427d3c9d19513e1475 9a2031fe94af21cc025c99b6862c28c3acd7ec8e07cbbbc867e3eccd00708ed5 ad563c3c4ce7707b9f8dca7da6711dcb71123bbe766c2383a106a2de66d5c78f 93ce67cc53c3dee3379bf4bb1df9bcf27e84f4965a8cd8dca7dd30fe88eaac2f bad1e7be9fdd9225106a7b8cedb17d4ed0cdf26bd14410b23c967ce1601a104e 97665a17caa89a45abe9cfeca906ad6e9162c8f50706c6e4a4303155aa0d48d5 728df6f0c748099b1fcdf81648b696a48a784076e46596f53043ae70797e5596 ccd29aeb2bbd8c3b6c823d7f3da8c7185bfa44c4f79e3dedf90b710d9f8f1177 3197927b607b4cd43e2318a324abe63f7afbc2651aedd584374973c7c7a7485d 99926810540d3fb09714e2cec7b7e895ddc30317bb50d6fe3b305475a93bc466 b2952806adef9f910602bba2523d0447d4902065469e802de4d028ad8bd7ecd9 9d7f9a6c55de9a240cf2433110bfa93e2d0c8376be7714b09d7b923bac769cfd 93c35f21f5119d3b1292786c48e9192dbcac5c07e5b384f89ff5bbc6d24a97fb 84ddf056421b14d73015be2b7733a8373454689c595f292c1b111d4e34c944fb be6e064bc7a3a4fa7dd2df36c5c36ae4c1edab20c9cd13280a54179003e25860 18a204e8d7ad5bf2c074e21e00477169eefe998fa26df420a848cda3067cbb5a 000d0ad927154282b2da3428557231459e7acf86c089371110a97adb570b5a40 7a12809700b7630853db934948d2dd9c67328c1e8fc0b39ffda70a5cc2c63082 1c7f63915771863b439d182321fe5fc831b4c0cb040aa974d873b449a6315bec 144073790bbc46f744eddbc944c4ab0d2a43b6cde6771719f491bfdafd346a5c 0c2eb5cecaf06eaba046ef63e9405b38c58b97f2b1cc200736eb56bb2b4d4e79 497f6ad23b6ee08824165142e72430e7ecbe83dc59859b3807c0048055de9184 ecb46b1ee9c3249c57ce8471fcb3f78518393f98c703fa81f58444b23759daf9 fdce617fa635f80000c086a63723fb65b671cddb0612ed83d8f552ea98a4a3f8 9d19a356a1ffb66085856014a6c1e003cc5c76e55235edff211add600d241aa6 56e1ecba4bede04d18e0c202930ef04199fcd5f57dd9e9241930118de3fe4aa2 3d2fb9bd25f1b4dfb688a9185648b70663929bc21382e0145fa0bfee8bfa1b2c a059a5b1b0a9c350f737a8ea3ab10dc69aa9ec24c33ad8fef8d552a84f8d67b7 b4a4d8ae23ba947fb988a0ccea6ef27c1a4b98f29288206a7682ab401d355b35 3c45013d4a3319f02df0e8e14bd0f945d47c387285c8e30dffcc72982064a2e9 0fb3e411c0a2b34db9e672510f12e19a8fbc7bd963fbf0d4586cbaca325e1cf0 241016116657905440fd8ec18e680136c3fc888443045f87505326f80f5d1f65 869910ae120f486f327c17166370e73ec9e8bbe7f20e33436befbd43c50ae68d 8122c86a68446e41aba893aacc07041510dd789d182fc42b6015242459f8e2019c1b5e3b52917c3ec38cba1b2661c779a4804b576dd96964ba30f01f6dd40206c4a769efc7ae14ee90acab33a076b75c669d172dcb42fd3701e1636dab82d50cc9b05e3c0ac66277fac35e197361c3adea0bb597fc9ff10017be608ab010f6078984bca7ea90538673b181765ce98a18797e5e9683d3e8504f5a2271f78b18080c3aeb1e2ee7e16a3b2a5e996ce874f3f119195b9227f8b32598584ed8900f068671838a2c2a24c3c46515c56b96df2e998de93123d60fa82a866952881ae009a4f81bea084a2b362508a42af893eb34e3abb15fe2148bec3d22ce4e35a2a9071eb85914bd01e3c6bd60387963ec3a7e2260186d6acffebc680fc6961d1c050de5cdfbd83c537f274b11e6b2f4655bf74f14c5e3b252190af84cf0f2197d9b06c80ce3072d34e991f456c2f815549903612762622e9558c5a0806059ef4aeb02d2ce3ec1a185ff0d7d7dbebb499e5927f5319dec4d052dfc79a5f66e22bfa80b5faee9682216f5540181a52fabe308e7ae925e7d9294d96b8546980bd056920495513e0550651d63f7ccf38c20f83c6ebe5641f7e1c26af44eb2a38808942b051445faa6f5c66eadc398cf892373f5aa8cffb603412dd463d83218005ba89f0573c1e96d6bd6329aff51d31cdb8ba5c80a6002c7a31a797abbef1db2a90788085734c4248a13c6350d405b3cd57fff3c83a4a9c90531c828e9f65197320b1d067af318e87d05943289bff604f145ae7589346095fdc6211e6f1a7f7b06ba5d0a79e8ba5fd565634983fe7a9dfbdcfccb149b6f7d32c1a7c6ed11762728bf120d339557ee4fcad8fecd34f3be0c2e730de5f405d743943f934c3da3bba9be570f4f485c3c61e8d2bf11dcb6501f330b3c555f0d6746a762df46453d7d1955da095f40e561fe4244d69689c1c9f78fada1e968e3b678b88ea06a2c9dff151e9a04005321b97433bc189c6f037daca15f2935ff013b0de747c92d968de8998cdb0da69db83d5b9765099e16e6d2dd8b6eb84f8fad3e283090d4831564e233c0d5033703bdba921b248ca9a193cf1f25960f47e1cebd12ff27049d9efbb10e054c04b202d27e815e96c261795ddf16823ab06c6593284d8086330f3e86331708f5032ac2bdd0b501ee40628a20bda761b48a136be8e2f592781e822552135b8335074162132471297d7f5d6cbfacccf4a4e09634b59c9289e1c39fabf0b7d5f4440d1544f63eb79c30a4465dfff5334cb4eb1d5f6ea6485c29d84c594dfc0f39f402991fb9279f4a3c958a3fa2d15896233d1e5eac6880700e143603ae91b858bf007bad484e1d0e48f52b03f2027d30e4f98f9c89c35db86a14b9a8eee362e74305e2cfb5736de75c97d302a66e07d8773d9de48c0060cf83a23dcebb13520ff50efc211aac3b40f96bd237081d3a633cc750909c0ea76f7a7683ecdeb88ba1a60b9081e0fefb5257b3f8255d3091897e3b73245935828c969a89bc807e6f0d56021acc5e37cbd72115d19778fe83b5bb33d55f8e0e53e043e393c2f58ee7b604033581f46be3f3be29c0c5608c85ea5f3b437e57652177dd5bf29345dec5fffd08f3e6a60738a10223c4e9e075e5d55eaeb311b06764d758c3d284a6bfd2c8ef05d84f56c0d7c76e4560c81904687d7b46e8fef1ac689c248ca9190f86ce4fa101f002320d096f11ee5dc11ca9d45e63e3c74fe2b3e6c16ba389c9e39ffeb5f70fa2e40ad84325c0dd08a9530d773ee2d65c2d4bc9a6ebe728f11942fa4b3459060a9819cb457a9d37b755bc809734c7c3fd3551265502e7325524277597aa51075b87e6df9a6a617d89feaf4725afeca19e8a882878508ff5197fc683b680eb0eb2b73546dd96399d805144cf4c4cbace94257d9054ee097f34e57a673a03100f945e7b0c1e5fa4271887efe29b43adc01767d935decc2b7977b9680a3ea45d032c32a7fb61fdaab172c5e7ce39cb2d310fccc3adcb428194ecaba46628bccf006e037e268e0c90339bd81aabe19ece1228312735f265125e1ff05ec5890ff00dc8af69804e2b44185b646df1234510be5a32daac7011708b3a6181e2bf17610057c1c20d51ae8a88746c5c8f5a765d573f8b281c40c07d39a0d79e9421eabf056eca4fe2f740a7c94adbb94397ccab150322cce077e1f49a085b39ca05f61201b2677452dda84cec6b3df091231470be53bc9865a97915bbfa8a433ed09bf9065906fb52af5878ed9b8f8d7b51886a40d34870b5f175d4aebdd0b3eb213b980a5f479d1183ea313165419613125c95f086b8873d27a9bcf4bfdbb5ee8ea7870cb1443330a7571bd1f9be8f723cd76412dd769ccf21646818004dd0925aabc209259f4e9b470b675fc0daedbffe3d2ec83b2897e2683e0dfc84d0101c94fc9004e8d36ae96d2d1254238fe8930fd86d579cd91a67b0418db2168ef433bab0200b645a8b06b18ced7e26b8f1f90a95af233f0fadb290144c2df0bb567f3377a20c4ed2ba430315e159da1d7ff6ab3db7221c1772a508d15a8290c8975aea45a80f2a538b1b9a189424e9a0e78dee4618c931aff6f5d239a22a6e2914d14458d20df18157b5cdba906870a709ea7214d6b79dedc20808ad7f38cb283ef9b7f69502f0bad32d0520b354ee9981edfb2153bfbf3712f82d2e4005fc1fd5b566297b073f3dfe1ff4f427de60a4be151c2f3ef80b8c4e70a3ca38b89ff14be9d39a800bf31511f6fd6d18f6aef2314c9539e2f2b9e7ef2ae9f415b0765348d43f2b21049f8546ff803a0200fa79a303db05750613ccd7fa8a6a257cbdc6075e7235cd079dd64694a29e4c249ebc6ec01776979a0c20b58bc0642b44ad4b8de8bc8b260c079d89cd4328d31b3b4113fa7ae811eeac7b5f6543b08a99e5e65e2696fb790eaa58da8501a2168f14261777fd998e8b9bae834dbc4157a9c2ba4b3ff1ab230e13533ddf8d25ae32398622f968b585c75c3b596196a19c68096090095d69ae01680bb2e2d3a08166b522ef1f4da351b522615bb1e1ffa59e4eaecb7ced76020c true +check_ring_signature 4553cecd38065635eb2b54694951116652ac04d22da0eb92455455300871a630 0e80cdb195ba1dd150c4823875b9594ea3f74c389f04ff5367b662f740c2f61d 13 1de4e3e44eb45443b4be4f11e32dc30c1f4612ddc1df6127f557d4447ce34d10 11d72ae57b013518d07bd7f84673787ccd00a7410a06e03e2e40f50a6ed90935 24c1417780fe596ecc01c249211919cdd80446790a5bcbee7cae7fefc579afdc e793af00fd2978ee654e8749ad85157e451764ec6b6789dd3660aac33734ec6f 81225528cd6191ff7eb35585407e9a371ee1ced38128ef226c3ec39134e75f4c 2b6e4ac8d2c63ff607b45adfae99142b6af5a3689d83c3064a9ec6e6e59c2892 f9822922f0a02cadbf6adef9344282f7e98c978cbe6a3b4e0ced66b9c8529e21 1d6eeb6f2df1029dd7c4ab65588edd4bc721f996898467609a3d221b7110b5f2 e696c156778ad876a386257b6e253674253d431e47c1f47c4d29c06d9cbdef7e b4c60460fc717ffded8f4746d255fa7bba0b389d4128fde89a3bd75a8b223582 67900f97adfe224b5c1feb1bba73a28d4c8f39167129a060c59560f6fb144ae7 33e5d668a6e4173ddbd6c42c23f4ded93d7a0cb1353d4ec77758a919c66b4522 6b9678a6d67dcf0e280f13f1566e0f846241c80b37af205bd57867e543411a53 a7191d409cb1171749b886589c76dea65b03e7242da0a4541c73a6c359fa940a033b4b2be662dc427f5ff5a4b30b73753c57aca905b34922112f59bf29a3380658605aaec58d57d71de1c2dab3f5c74701f8859c6ae43b8fc109ecfc88a1a20e7e86f2e7b3ab01eb9fefbd2799a3fcadf4f1b2c7c8851319b2b6595d89b1b40b91a8fd974e2ad85a63e6a74ce45f362ad442ca8f9c00608d0a387a5f3044380e4fd8fe75ad72c00fb07dc624dedc3cd3a17b1cf4c7597d81d37d26272cb38a04c4391d38b491ba665ac468df755c8b2efee6e482311f4deef70fec5edbd7bb091705239bff1ac48fcb80e0658b5cf71b5cf4f9de3f141581c0a4ebf7431e7e07d7d99dc08392913940e45e235d47ba91570a24ce7714a1b65e68a7fce941410e1a1c6d0188462a45b7aba00602e409cfaf5e9f6092cdd8682c31b751345f9e0518514dbf47885cf00f24628dd4c83fea1c587244676aabcf7a959dd53a58940da78c837f6787e2bfba29db2ebe4d982e8f4f1ed598a9cacc7777bbc292d9cf0ecad63db185fd82064d13a4c43446d762b73c022de63cfa37890af05b1e6a420f531f5f01846c7d12ca5153722b61187fe4fa0daeee47d5f5cac993368b5e620e1b673f6490a8e53afb9058e7218c14d4b0423349d5e2f256ee0dd61f5696ce044e7881497311134acd5bd07c002072fe857dda981bef8920ce9404b0679b660d890db6acd4aabd4f25dfcbe3d7e8d02f5d7850204ae29dcc958464493a078200288410b08b4b100822bc1fb921904f01e95fe4fafe96dec05f9f3729200fde039ca8889acf09f099b2f11e50e2d1cd3a31dc9d4bcfe7f64cd234816eaa4e8f0b70ed1a66d79b9f70711847c7fe19233b365f77ef328f7c02f51395b583d50105af5ebdbdba3f9462ae3875f8643129a1c0941190f8e22a2f55c12549ae971802572fe1ae42877dd7186e0f711b50e883473e4a8b0290b05e7f2c26786a99860f578cc53c945421eeb7500bf221ff93508945b59b5a68799209b20e59271efb033ecf493791184c54d2f527d8b477481ff47a37e1841ffd494754a74012c17a0c4cf497e8c95e9c78bcf09d708ea2e8f0aec9c703aa5b9dda2353e2daa1071a0eff3a5b8d5f110ff8623621296c71fcde1a9d986fa8c5c7e77536f235072d4f07 true +check_ring_signature 5f05db8b4d3029c148e49612f6a974d672477c7395dfcf81a7e98fe3ee671151 479d74654dbf1d4181826721e6b98dc3ee521f3dfb8359fd9598406f7639a385 55 2be74290c27326149faf311fa3ae026f302f286e4fffb5cd7fb539ee81ead255 0176816a3614d64151804638f1c87353bbafc791a2213a31fedda6ca1fed8ab0 bca0dc835d8ee4903403ffdcd447843831be3a9e605119e33089ba0a07696cf8 8a7aa3866d2f09397645a37826d43933e4e631943dfe174fc517b34b9c93a4e3 b81008f033d29d40d727b419e0fb60c6ec0412fa4b42c6c38fa9771acffbf9ac cc3a47fecbe02a26c359bb5fb03769cf8e5f684e01056a0d6f58bec7e66e0583 39b586b5700b8b15efcf00aa0fd02234c791f94dbe93247e041cb5f725c0ac55 6a05b9c152acd56a1182086c4251f05652babe9ef56f603e8660e5db46fc859b 3bdc2fc1edf10c3d7df4de6b2ce1d26bd68edaa6ff966e29e7e27784e3aff2c5 724a7414bdfd6d48106bada53afa904735b148240aa42ec19d4055113d7ac3d3 3377c07d6cd888535d6ceb9bffe89340265e620dcbac97c539b819c7e072802c 937496a91bcb8acdeaa20f50f8cc149d76146031d55b130100de96eb0dd7429e b125f8f7687a4de7f7574e58ee1ebd732c540977e38435bf26deac2a1d14c036 3ceb78bdc25723ba9bfbfcdb93fa97f4e03c064d4ce7558d6a9ac357653ab763 90136f62789c57f6e755b9da384535120ea6b996482f6076e55127ddbddb5959 870b4243e7d7095b7d4ca18333a3a47b3360cd5c7714d2f6dab06dcfc68aa355 790a75a0a52e40ea07ee022e89d82c1f18e57f02637f9a6e58d926b63e95eb1d ced7c56327c7b53b82c2b019bc08bb155623dfbec244915d04968d53d8559f56 8f79fddca524c88c22414394f062d39c371474e07ddb786c7c4c36e85c22fb88 ee9d941813b4f665104d185accfc99b1e4f71ca0ea5df06d2b9dc8d61ef715e1 bdb4590ac007791bb4277849c157c0c9cdc9816ac40a979b391f4515ba455ffb 7a4e7fa193cf04d7ecb4aff51ea45c3e2319dd0eabf4d41a8eca7fa80396b578 998f960e1ad5f8c98732b84648b4dc639d6aa52885f92abe110944de1b3f0fac 8fa1aa531968a5dcb908f2bf239ebad8e710a7c115bb4f3bf9ef3dd64c7e6d00 4d4d12ce1fd40707f6497ce3738cc878c5a02b5588bdb1c76e17b395d4639747 89af10ad6e3a993385db6ef97b0e0a167b48dc86cb49ae081d1d553727fa8869 d76cb921d9d6b0e5749897c3e7d71b3e58551a48f77e2a14722df1b33ab14f44 e28e9c03bcbd491dc056a921ab36a2c3a2cb0fc58b7a8456d66b2ee966f53e60 957b203a04c42791b97c4a84c28d49d4d70cf7b9129d2c6f1f8cbce5a3fa9454 986f2cc5274b5a093cd882ac9b6cb99373e7cb5fe1241a912a4fb458f175281b 1dbb82e2df91cb9dbeb8f5a288459d0c095d6d5e8977e458da98c1a9993f2b55 1e101bc7b70e46af1f52e059daf92ddd2f963491e2c67d27bbfc4ad6d2460c16 0a14f2ad6439e9ab120356b215bdd3c7b14b9001f5c9986422ec3010664cb726 5062c9988a0ad60acf1ec4a07a8b9ca0faef610e8d59c289f2f0c3906c2c0ec0 74b821b5a6297cd8fec5eee4ef1c9b305ac6f2511be91d5fb89e737774fe89dc b34b24ca8badb575d6f0d0a36bebd7331040cb85a76ff05586bb21d9ac81e4c4 afd42c076570a2eb463eedf1cf21dda13a8cf0938c2322e008d60ff0419519b5 d120c5d85b3b81c3dc12df9b287bda5bb4cb31a5ed88eaea7f53b315c0b08550 4861256bfed7fa17ea89ba830335383ba3b76aeb30eeb36c78b2299bb95e0430 2c33e441697d0bf2e70792e1bb13c42c4189519ef64f426177b7d5c5ad7e6823 c153a1642d6bf645c7b5761ba3c89aa63539f68e38f98128681120a69672062c 7692991cfae4ce2d64f0150b9572ba80b944abee47aea885c1fac4c95db24c5d 5d2cc82102df1df6b45d1a006bb1fce3cfcf9b4192ce52d7eaf717b219eedb37 1481cbc9ab96c081e329abfbe52be5dbd4880d3e5257e92a50f75654b98706f3 5b6cb29ac6612d2334de2f1e374fd2268202777d14ef6d67ae5b455c0e27cdb5 13123e45cb027cbe163ba8c88c06a3cc46fdbfbccbea258bbaf882f20fa91436 859a4c41bf9b03a56fa0d142fa1e813d517b478e7d525bd28ee5103ab11d3537 6b16e15fc95843a5601634d7936af49fe688dfbc48d40f53ec862f395c72e480 a3c293194b80212ace50d1b688bc0b3d7a182ed4f989c3ccab57a7cba100c9bd 1c014adbea4ee6e2e262d33b5732ed732d702264d77b36978ea43e68e9e459fa 6a18cfd95be9517d9492f74fe53fe548b5fb7a5ae41dddcbfff5b0f05b528434 7b4a14d1c9e711a38e24cc61a464e5e2578ecd918bbda9354b65ce20b55b4b50 99b168d85198ee087643d9fe2f27fe3300f73a5fc768d0c3ac0f450147cc8e33 b91efbe2599acf437e77c342bdb6148fa44b586ef1a7fac9d0d25cfbffc43f60 a8b3cb78881a76981401f92b82cd6f89959322ab25b064cb7e4d43132083e2a2 25c9719e4f8cd64837a5e6138b9f6d1433504a9adbbd7fc6aa380ac6cd044f0a1f4aec96e9d7dac4ec85ff8f5f316c6cfe017574e3add4f729de435f69dc2209e6ac850c36f1213c18440983aad758fcd487c968a4381409ed3ae66dd8861502806dd2f0ede46ca8b51f10966d4d9b180bc8d02e6d0482cf7b4dc5dc4778d40459252910316d463d8667b1b0bd901d081802922d3149d34be4559dc02a9b8c0a18966172359b5057e2325c85f796a8806e0bd17b60e38a689722f3ff9d223b010b8b59010128bff217ef92638fa1354cd28474645d2026e29662fb5ac917a108d1b7726ecc434016577391db378716ba06917400a75461144efb10b06d43c80132fcd65a69d331bea1a7a071d034dcd700bf0eda7ccc79a6b4f124487a82f30e145404c86a2bafeb37bc2b21c48828c1602f5e63cb6bd278dd0a4a7255dc1a0ff215e9c0b28ba7d39ff0a37ccd9d9449e7180a7a483e9535557d9b52e8810b0ee1785fd75a42f638b1444bff7db15b3d0ba73215305ec42293690132c9f33d05a13f2b58670c8f6bd5e65536b6d33ed3e6210496fcb6cc031f5d1eae39af7a035c51078bb50ec1b0aa7267bdc7c2ff430013356c9cb99bffd793db9d6082ac00cb8e09b5a7e69b81c0a4a3956a2479f70e2303f8d8ab22dfc0793411ec28aa05fef32234bb75b51d34a74f059e14666edf8fb6a5925722e168c26cd272e6fa0f7bb4df2f6fee52dac433268a636cf72f1754f1bea3d18f83784ef398df8f1e0db5e818c3287ddbf36282f2a83e78af85bf06c067ac0bbb1b84f1a06cef6bdb05d7694caf731b4cf19f9979770cd439be1b2f1023b325c7c8af34472400d37d0fd418908ca86a06e6ab523945d1ac5f9eb00df74c3259cf19e491fddefb13e3090fb287e6e35966ac91fbbe0fcaadde51f51b37511e4d1794c38eae99d8b95b03fae47a97fb07d751584e51667b32af9f013e592f2080232e24408937f1b3fd0e624b0c55adbd2e7f5557ee36ed0ea46295f116d99df7eaab1af3cce2b8a3a90ab6509709671feebae11b4fdcf9b84d70bb196efd112188de50b11e8e4b3e1204635ad088f29d812556c3343132711466e89ec27d36ad6f6ecd3ba092171b2101b34dec43de9a864c7cfea0b04e3b59f5b417461d5d9d126d550fc881554253048f4d58f4d743000b17a724a3bacf8be1dbf75f5b761768f3cc29bfc46965b601c123be4e1b592fcb344670bd69037a0d5e090137699e4856e0081b232935ad0b6671fb360a0fee74d2caf0d9657678be975000dc04b252c2bb770d794be50c0b0288c2d835cf9775b3beb25289504b4a8749886a8e9b1f7017b144b4fb35080bb982ceae82c858a95fd7fab9ba058dc35e3d9e6c741526bc6b0705ace12d37082c03b42e77085c4dd24b4796f05183ddaa7d32e7dd9f8288f583ec36f3340b06e6e056f04f6102dd96a607ec9e617db840c554a90690752bee1b3c090d283e0d0b9e70a9bcfa766f00538940fb28e61e163f529b30f462ca1bfc3ae08c3d890c5829a93da4b060fb56e6651b2f25d935b3e582339b811501fa6090c06efb7801204bf93cbcab18e1464011b0736469c64c30ace81480240c02515042834b2e00248a7ec9569dd24433837386e70252cd93e6ce324a7e171b3c122a098c56c703b67acb8df35a7caae9bf1a450c5ef25c21653ad340f3917a47bf5cd73a06df0c7383570e9686795bc75e617c8c147d7bb63c0adbd00def681d24f6c1c8d2c207cea67136b49f62a178e6c6caa82de31728ab024716449e87ee88938a95628b0d8217616dc5d66b4016a13bb86ada0683dce37f2d3aa585fe36b4d2b0f3209507a44c79708d800cccfc7520d21786573ea665a2211f3d40f8045e830eea46aa0fe0e8497aed5dd78ff235a497d71a75fff03cee44a2ef7d100ef0b7683483e00782ac44093bcce3bd09081e57e079e8d474312f2d9807f13e352418b67760aa0b4e36107116f6115f752423e97ffaa5172684b600b10638830dd7d358b887b601f251c2dc1eb4ca3601a61ff972542b561f857c544df613e3f0692ec4d48b56072dfd83f85d780c96281bc89f0b08e0abce5deada471aa4cc56dc034f69fd550ed7380aa56908b91792cd788c8273ca6e52e2a24dde31fef407234c6694a51e05f0af5154223d5b9785208fdf7e8c3a210e0f1653ef64f770d65757d183af6108c4af343699042c7e59cf25777a153fe45bae7ad0fadefc31223c5195172ab90567858bca0425e0ff7da63c57b38ae13005139e28aa36e59de5466eb4b86df901662fe28d69d50b9fbdd69bbc526bb77040fd7d13b04a7c7a06394c6bda955a0974436180eb9b459c1d46215a5f3f3d07875f549333c36a5c1f9bc3cecab6ab05d1a438b85b4c8e61eede7d290538d739b55d34c403ac105cdea02b2f696773090642e903a1868a0eb3fd04d68c0a8c798c7cd581b862760065a454f398322a07c4e45a054e94ef047a8c17291f86efc4fceec8beac702eb9f7efba373f83340bcfa3d72d0c9ef2153971f92aee8bc4886627a549799b3660957df2241c9fb806eec8c5c0c0d8b0b87faa7ff3db761b23d22efee2085568143d8d9e83ce7dab00249aaea957442d433a185d4592e9708d503976c9ef384ab899bc8c2734d690058d150c56becfbcf71cbbd046a05daad30242bd32af4b413d4dcbba5a4ac67f0e479c411a29427b4f9f43a40923a1f8d4ec8109975e5982267dd77f4254d7710d7daca1895f9edded131a67c07a9dc3d367330046cab930222e1ab4df5edac501f20d8d4d5d177f7e2579d1c75f813c7b19762c088de9f5ad7806ead11d7a31082d5ab39c8ea599849eccdc0d67b5cdbe0814aa0670016ee320f8c0493b68490d0f3bb51fb2e7a1a50480d66c200dd3fda8b5502babbcd7133d1addf52fb972091c86497aa96b8c2797dc4ea247b6c5fda7a4435471b7aff29b8aa98ec7c34e0f4ac474316197f6b38008aa87c01fc687228feb868adf6562b3a819a77d397b012b581ddc3576b125e94cdd597602fce03586f2900df75eeb4e793895c6534c0dd53a9c130f0cb16d040837e3d5aa42f35ccd25cf56de7a319d6251cccdb3460ae6180cc4d7756e4d9d2bbdf39205cbe2ea4a306a8118ba648931b48d9552dd027d970ab0d4c8d07b30d4e3aeb18e8ed5a620cb41b1622f4e078da0867393a6337c056c3fea0d798a9a77c5d25558e06221c2bf273e38ef617e0a904b3684a70838d15d7791c395ce7e91565a96ef5cdba134c70b1ba68c1e7c5621831c027802a4fdd98d56094fefc70981142f6e30706d05ee379ea27c2c3ec5fe9c8e0a2c0d3ddf7ac85cd54fe829f893aa390a9249c27835fceebb570d17f99ec31f6645092873a9b94bc2ba88bdba7233ea730743aebfe7935a92daa6a700da4015361c0ba6ffeb0c1fc2e941a3b9458cfde36276e6e97bd8fedd80af807b980ba1608c08346a3bb221686e6b0a6cba6c31e3909ad1869cf9e362faf213c8d1d7271ca70bbc7ce25ed56dc5c972a33ed67b3aec7bd267fb9669ddefa47a466ee1b6a9a4073c885cb7e7370f2233a9d9c7dbda78739a628ff13e366d233bced8208ce5b70b60c5fb9478ec5427abaab281020f160014994dfe660159be4fbe58f03323690c0088798c8e45154955745374f6018143dcc0db7f7b2736594d1e5867490ef700b8a419d76142118c6501df6b2e44e14983422b997579a1fc31fa78644a88bd0100d635d38489e396c0e279eaf0a267ad90a33537596f56c56f8cb72d7de57e097ccc7c51149ffae654004b5a181441bb63b1f15796903698da92ba8fd382650cf59fd1e447364bbb16350f4288a8c4112e16e46fa010d0b4278ed4df8a004709b5771a96245af1bc5e1e2bf894f1f003e3b91166ad02ca60c54751ef089d8706851ab132a0542bf3ed4285c86192bd6dda61f490b3b80689960e67240d9da7031d28ffbddf15c4c2b3980b242953450fe2fb97638799a1e90aeb09df644b9e0d60f7279f8cb0bf422d6a9ec551b680b9d97acb0efcddf77ad00c17bc8cafdc2cfd63fb3478d049eed99eb06984961aa58dfe99cee13bed16f3ad98f7f1f1de051ce6fbc5f69eb2b7b62c7814af505ead17410b77a2680ce7afda089237e9cf0840f92e68e0fea68dbea641193ce7229251d2ac94e0d87cb816982545e8d2520cd5b807749335d09513ef07265ddb1803faac3694a3f5a6e7977dbf8dea08ff02840e54c152fe0f1c782d18ce9c4060b1986bb832d0c40475517801acdb4052082046f52559cc71adc6a65f667d8ee19ebce4d8d76af72b2f643b7960f2c59e0728493881cc6219afcd6cf13bd1a1d712a98ee021a0cfd0f7975a74a1b6fc560a7ae64081a99a941ea6ef28bfba9f0f685a4df3b4b9604014d9e1d57f7594f90fb783af17aada56659487d4f3d7f17da14d4dad89f864d31dbbfb78133ec63000cf68daaa17cdcd69edbde20b000a8140d27b0418b6e9d1f1ec805f4ccab13a07e9aab2ea60fba6cbc477c9b843cbd7c85a9e4fdea3bc328b480762af55991e05b44f3324e25e28f84c97caca136dd9ceb0c42d1c3ca7c78fcc00f96c041eee068920b0ded174c2e58a629d7930112f2a92fea720ecf098bfbab9c68c735a3b0809836faae059393d0a5c54d95c29778dd39ef7b62def9b9ff6ed0e988a998f08d0c3cf93c922d7b7c51892e0120657735908a8222990dd9d3d415e71fd39460c0491f2d192fed2f0934ca8b8ef469716ed2a04b746ae1203921af437c958530f9730d3300687b51c49c46e7bf95c9485b0d72962dbe2c36b6c46cb0271c2ff8591b1d77e90641a65491d24e0d7307d007036af8454b2a19217e3d053f78ec40713dcbaa3510856a3a91d5aae679537d8113af74ef1c6d55a8a35a30d4b61e500be3aac10688bf3afea1892f8476d420c6b149d68ef082e0d2be39bfc6265ed08 false +check_ring_signature 8c424ba112a8f8381a23b5aa0e27647878c43e10a6877fd1696d08f6b9e5c588 664165816bdf2334dacf137ff81c2c824b7282168b841120e18ae2f966f9dcae 73 546ebc06c3a98ae3f570c4597d831359cacad61acd3e258a19edb90f8357211b dc59f241f91c821c548d2781ba8496f9afd86e147c1e340c9b80ceda41758743 2fdb1638aaa30d1d8766e47c3f9170d3ba68af7f927f7453ebbe0d637be0398f 884fdaf84e5e43298e45cbc02e9d63f2275ccc2e23630e16b650f49c9ec9b03d 9886bf21262231659f034ea8fb93d7f632fb27607d24377b5d6c5f0d17a3c6ce 49e828e2118dc71cfcb05a883f67f640e9b3741df39a6d10c1b9f8178b97e44d 154f8c6cad3a3064e2c8dfed768d7ef386c28a50f073adb8afd1614459aa3dc1 170f3c3a37d875641e2771fd268d396c370eafd30299076dbdfb27944f9caa95 c1edc7725869f335cf688eeac84b4841bea287dc96c556b7ac444c20b1b35dd5 dcdd8dc6b8627e19b51ba3de80989fb3a0e9cccaa9ed1bdb97753fda107dce40 8d6823018b28564404416f3f49d2bbd80ff2d512d4371e3411f251bffac69acd 49eb68ac2642eed63b2f8793b90c4630b964729a1d15baf9c173e99c028e7334 15c20e9fdec214001ef36e5c44e0ca46f0745050c6bb0f920319b5ba5ece6182 6001cfed53c3cc084b319f0fa49af762dccfa95e049af308f055c123d0765b39 0aabb72ef794dfd1813b09f9f4875285b8bd7f6f203c8d5cb6b367e534aedc81 8d88a8adc58d97c508faa3e6bd48a9253671aca01f02695f696a2541d2f590af 965e96f9ec2204a47f0dbe1f4c8aab1bff5c17a8d947468951cd232821dcc4a7 c8c4df29db098dc8b353d8b86e3666804846a315f798bcd9c1071517b36b35ee 230c88c20f2600fe8ef5e92a14b757cbd4ba8e7d3eacbdf8af8643bb184bedb4 59943ba61a2fddee7f76c3a46621863553f9747e07062b5687fa9ea48e259d8a 47d550e220a21027784e06cee96bada114ef4600bfeef41abedc35f7da01c6a9 6aa7e671c808e98a8f2e58755f522f6936b08b36be4f976cb4ebf6bf91764026 b2a2f0f82ae0bfe0ff8396bee8fa93e2f5b9e997ea98bbfe4083862c1bdb7c49 c7bb93f845711c920d41267a0489b55be9395d9ad6f5f21c7f5fbdc0903575f1 ae43b6ee7fbbad7cd74d38bbf84ad2c0b303a551dd71981246703ee0c8d15fca b7771f88a6975cb585773bd8b30da1826a0d8d62bc06347e02eb34d7ccb560ad cf01c7bf20f561a58f1b89481dacb31d50469bc215061dc51e03b94dea8412de 21130c8cd897c4b28a9891312b74e6d6c687f903bea56fad2899e33112205478 fb4510ad270e4b31750856c505231bd980539e0b73717a21be5f5330fe9a6795 dcfbf65fe2423b5c3d67b77f08df75bc902ba893f4dbedf526dca48529e014fa 1b198cdbc9bc72d38809da3714b67d05feb7d4a9d812445fdad7c18f3749ce32 c001e41529acc0819f435565d9af61f2a92210c8a21556c5cbd764c3cca0f744 40333aa3a27f828bee921021b8efb4c095849cfa405b800203fb74efe9d87e79 627bbf61a2f319137828acf94d6e68276484b7e430e6d80bf973cbfae1e0e955 a0a80cff316a8ad2d8274fcc60b497b2f1c85c1968ae9d7d2ffbfdff4c09c7fa 7c0bee7b86475d476fd2a8c6e781fd5ca17b5bb7a386f082e6da4ddbd28dfbf7 06efa2be7e6c3bcf4c5d56b189ed6fab223963dcfd36a0829613ceaf3e569d2a 97a08f4e2d2ae289313b04489d0a16094b90422c1e2759cee977621ff99af350 0d150f0341868338fc8258b00eafd851bc4f354de8ad194e1ad08cc1c50b013e 1d171a3def45761c62a28d9596b147f5f314d032a4c2747149ab8940d22a4542 0a844ca1caaa6740efb25f92d04e135dcf17d57f42b5da6e4ba34a57fd581f85 3e24df09a1a93050e46b5672c23a244c6aa9412231fad4ba189ad39cf4f55f75 62f762372232a87bf05de063d34b0d32cbd761d49259f455adb9b36a59db7c6b b894e9900d55375ee2543682d860a79bbd206a87d43acd09e8092ba0e414041b 5453b86de888c71bb82e6605b72ab302889813a5e280b8f1231ffc448daed2e4 660170e139fe00a3456d3c7ab0fb1a632b6b0b685953c73526424d6bc650f930 6cd73d6f654b3c99f587d36258a026a6871cd8dda71dbd4f8f54f5721a262d3d 733ae69c00e5ecd0928690d7cbc5dbcd140cfd1ed7ade51a99e3b52a2164bc08 3595a107dda8b9a3a953a50d134946286c0bd0ff997ea6f85a36f0d4a1ba027c 38812d91fb79362ed446e3f5c2aa8bb9b062d704013e1da750de793ffd2fa5cf 1c882bce2b37410926dabcc5e772e66e3fd0a2a3096d19df99222f8d2f96e3cb 9186652f3cb782b8703a2f8216ad813aa774dc4cc78a6739a451350c4a3b4d72 e8da938ecb3713fb64fe014fda9dc84557ded51351595254bfb5b4c24070becb a81c92d45448e99d119f2655af7f1c94fba874aaafba917b7d8eadf61c12294e bb873d1383e97a4f4239feb43b91bbeb5047d6856d572e799e4a6f59b66de7bf 2676f77d447dd37ae4703612d63dce0b81bc0a597b449c187c6fc631bab679b1 fc84eb76adc67db20cbd4fef63090be1e7337585d9c48a1794e6080a6cefeead 16e78a60fb7e4818d7155ac4ae5c066ee3c1ccb25676f80c08ece7319deeb40e 30a1aed8da13f89c8b62d4b08a28110968a283a034d625b5ed597da80b4dc12e d401223e8dc7a83572f3ac587d98460489c5f7a388e8f118253548ff1dab50f6 53920fdbf4ce84df3b60da61763852c9409375d152ef42d895c36a0ef1a78075 444c017a3ed9def3cec17b74a8638e73588ba6c6caeb831e0fa30dcedd3174f2 1a34af316e8678e7ca07bff03effda6d5a115d351af6cc853436166f716f9537 bcda8f9584d861f33c79c57f790f4511a2d8a14e47eab016d7e6e52121477cf3 50e89a9b705cebefe17eaf28c23ab71b43d664f04605a449618c0613c6de9ea9 fd21f2a665aa5d2f8597c6161160b6405a973c11013e775d0f6e32a26080dcf7 86cee653f73eb613ebfb59f68bae6d38c7d4c8dc47a0bdc8851127c69fdd90b7 b016552edfce451a0a529c12fdc9c9c26c35228b07612bccc413f787eb77d142 53814afe85a0c34e6a09fe3651eff711ef5c4ab562bc6f3341ff6b508e4ba73b 7e474eb25ddb0e51b529f8bae919ed8724f7854cd95ec373312c1e35ea2294d6 37b4f7b54d6cfcddd3a7ae6d09dd49949463d36e4d230bbf958856ce68705626 259c9fcacf2e9f7cd8e792f5c5ade70b26708d991a389cba9e46b3ee440d9974 45f3c1919f6ee06ffff622afd8b7653e2b4eaf17d2f76e5c5755255a3408d885 483fb9f6f79185352d8573857e7270c87fa209a526488bf93ca8e04296827a0a337d9db71f13f12cc9816b845bda58f99505940c33fb3fe619a214a694ffc10540e11337b360b81ec08a240b99c61ba699352fbe5fb7f8e8828e980c3705d50317c163f0eb5dcec5fd5da24873d6fdb04ec970cfa42f2cd17a6deff426cfad0d80bf3285899895e2663e47ea676d990ed1e2609945cc7cb8a1dc262e56dd8a00585d7db0463fe0fedba8b4fbb9dc2adb07090a6e6f5163bb05a8a4680eb60c0a0bbadd3cb5ae165edf13d4ba72678eb88f347b5b87ea5ccbb48954a5a9a62a06af0afecd2c530ec51cce6a8ebe0e56cc91e8032b2ba6da14838c030591d1ae0e61e601c4f2815afa333ccfa79dbd8c34024b08c33a8c4ea822aa9706e591c70e942b2f4690c2d5dd20a54eab335821b99b5b9bb2f819863b6ea15b6b95e67c07ae2b913528f5a35e86a5469e8bbde7b955b2397a6d9871262576d458b1e86b0a89da833ee0875b0337733859655663b15f3bedce88fe96411257aac29d359a0437e0b22aa636b486f57e03377c85857921066eebc1dd9d2f51045fad91ecbb0c27da1e5f59f962ee526d5b531f066c7eb003c26c287494a3a6d99486c039cf002c99dc2b5f8515ff76922519e0b86caecad71d010096963b9db821770461980844aa43cea57d576834c5ae95ee9aa02606645240e0027c356f8c6cd5a19a1203cfa7a00c2c16aa05f636845066fb2683cf34b9f8a7f4a20f3a6cab83168f7106c251265cf28ddb09bb8fe709a15a8f702f012ad32ba864a17b2249bab7a4250d65ee56c3be49517b16e96401657c2256059d8d7ea73cf93f9769d317419bf40201199a346aaf77aa56e82f7a9aa0fa6e96f76155d7f721ee1babe479d0cd8c01b46f4991cac1cc99959366b0fea60ab188b2c5f6aa753c0e5775ee44f78fb10f696374e407a52704f5eb900c3c96496230a0fbad3e4a252405926a60267ea3035d84a419af6086225915cb4e814b7c79fde0508e72fa342d4d09ef78db8740015a72638bf5ffc1d8a02bc005250bec840f3b3a7fdf67ea1a742550793fa85b07dce784f14c71ac2d5519d1a9dda32d6fe86da182e581fec5545ee3a87b978b05e2c674c3ddc0bb869827ca253aa748e38bc4dbcc21951aa61a4a6eba7e3b2109ce7ddb448a22d0a202f68f22c49efd05af51e2c3fab54098b5adeb324056820c5018fb944272a0c43581cca1b4700aa0432c2d40baaec78c4f39b59ff8cfb20f712dc66a5903c97962e158c99af44803c5ae9cca6157f984fe8ee6be6072810a5639a2f871f909178bbb066dbc3bd5cd3281630c9753b56d2bb066d2d3c3a802af8a3092d90825ab3caf0fedc798f55ced7fcea690dfdd5a0e4629bdab821a0c19b2d42eb66de67a91a0565caf992de1940e7c463e42e46aff546f4c142aec04be8c8c49e22d9c7a552d6f91422ea86d3a108b900db72a1c5dcc5a2283f1bb0888234763b784f3cf9e64ed53f496e5aeda060668a5fd61f18c5984b0cf8928082fe9ba34e920d50d195c846e87df2c86c95b1980f66de66656e0aa5eae2df20811619ca8767699c74c28fee9e9975913d12df131d04b8c0611ca10e969cb3701fd376d3e1dc27735adcb318a9a90e32e26e889eeaa86501762490ed0d1f81e07c8986a45d811b11aeb6f9ad86f88580a6dca47bba8c3ba34b285a05a82d8fb0ab0ce163ec38ab76cc2134926130df7efc8ba8a6b7e0c1e9751d8c8959bcc2f00d8481d5acf3a156f5029c4feebdd09cc609e7b4d0a7af337e79565cbeb79740de2bf27a032f46537cbe98e706d1e77fed76386197613e46716776945cb4c790751c1a23acd6f3dd21ff1696e27addde08fa84702689999006a6e1c99537f430de8e0219622c1aed1ff82e5aa16aea64474bdde58f06f54e7e107e4355bfb7d0f1e851829b9b1c2b1483dacb774f6b9bdcf62f2a549cbcdecd6b017757464d904d448e0466c7bf5f19a70f1919e9acc9485bfaf8835fb33b796eab71c9c59780b0f2fe3a66253856df7d9575576f1eac44a6840c05d306290b385f53d6efc3e029e54860a127e4019c3e8d11834ec10b704b2e9dc9ee220d61fae1c5577d1d0072187dc134e85465254a4295344c4055779d6c8feff13a2be70199432fb76e70c6990f251759ac601e070d80e5633859421830cf3c9083c12d5eeb61c3866cb03bed221016748070eec838a806e1a252c36ce14b28c79ef81b9d1d27372db7d0970a4c252c22f26c57377c4c74fb8727204bdca2ff4194066180aabeaab60a50ba5e66b1c7772c49a7d97288aafa2c947148318d2671237b109cad08122b2e202868754cf6dc430d8dc6b7065871e2e1b07ff05c5d78a68e3da0364c6196aff0846953d5b421f0290e98ed2bb83cd245dce08cc05d1c3b9b0d00d0207358c9a057ea187240fc5f548984eb6343db7d215fc664f7dcd3b731d8e8f9938c5d226074d409d2bf964c0c14de164c89a6082abfbd3230125dc6ab3411b7eee649b920a0c7916339c8d91e1ede3e7f35ca36d47a1e913b0d3bb43c2de61f2c86412680159cde4293fb1788a83c3df2906a43c619fe1de086890ea3da0dda8aa25574c02b0657a80eeffacf084e97c76c9e05e23ee591b744025425da05b8162e7912d081e0e6b5ac75b8557c4a2e32c0a304a3bd5e7400081636ae4bfb064cef3959a0ae186f3b640a83ecc4d93fe9cf99eb73154f3b539fb521bf5a3cedae29e747f0e746f6d1d885febf114f3f50504f78c53d8233f2c46404ba6b02b017c2e20fa0ef61922029aaebdf37de00918ac304f7498809faa6f0f643309209eff42e46a0beddd822ba3702290b10e007a2178147a8c4dc2b1cbfa1a4a9b600783bced8504747575f7252e0907e54698bced94d7adceaa3a5372dc715ee70be130401bab05126e9e5f0f453316c9cfc9d5f743d277259193fc04ca20f986669824069122013331eeb7b3e221f49b5aedfb61915004f665772ff136b2e546868de0baa8760267f3b41d6c99965277f43a579b8c39ce8c8aa62e5855947de7218bad651e37072d0b6b63f18dfbf79c12db8b1a07fe5d1e646490bd8c77abdad35c8514cd3403d12818d67d4040b272a88fd1ba74a1f83195624a230dd0b7721c2ea86f64800431932d56cbdd04aaff00561e6374da820cf882b122d81e024ad8b88c4fd1c7019f8ec9c82f127fbd20ea04fe96d30f194b22094368b3c20f2afd3ae8084f88072258c115e028ce1c4d417f187c86ec8a302ef410cec03f2bf4f95783b98da104d037065300727835b5a3654ab9bd93907e11e006b4325db761b1a2e204c98d081b7c981f72dba665271dfd75135963b2cdb62477b6615484b78b485494b2210a7d5fdd37ad4a9f93a90f7d42ee4a398897ff9e1773e12318362d97f4c8375d0b557d02e34e6e0db97e4f788c080f2dcb16a9259dacd871cf23f45a16844186068af6c68cbf1f428728d98fb7c867ea30d57af4705f5e2e62aa9061389264ba039b09089b9fe7d18a677f9e55636fb68511db3e1be02a5034b0d5094e1dd9fe0bd1d4ee15f2009494481b1cc08b7b6ba3a59dcfefa55be74e6f6091d1d53d6800fdb649cb6b6fac38b8c5545381da440a9782bb2e2145c01aec233a0a40d3560aa4fa1be9cabc7644146774e448096796660fea3e7631a409948a13e04db35c09dd6247f0fc91f0374d05371304bdd7603d9a93ee0cc200038779b47fa64db4037ebc7dc53c9cb8feae62b099f1006a95a1969b469954d79a44a0df6cb666f300933365b542abbf957219705a96d4255799be6b33aaf444be92a30f2367f8460790296acca2da902f9e9dddd47073e1e914c9b44eeda17b1ca058310aba480a015c74caaef8c8d735dde0e20e24a4c55511a8f39c0f3c03f4564fc95fa7530e0a5f4bbcc520d5a4a2da266b95a277bddf80a197cfb39285f45423e0f17c2cc005c1d50fcc5a8514d64991fdacfe308ca702500f5b51a987ba4d7ad5bca2b3fc04a64281fae816750ba150f73056ecbf31eca05fbc33b74debc254e66a55763b07a101f2b127543ae801c11e437e59db0c85e7b83ee0de437cbee91a718992b10fd50d50fef1105123032a22eb9124d065df3b81bbb57577a06c218d47914f1a0509e2c4a40122991c4b320d6b23b2f28e59154960cb85317e1ac570757776dd03e54e4fd86dd48d1f1e89d9403f4813a2b5e5e6a5f825b0208e76a534e68ce40aaa6fe4412c3110f5ec07588890bd3d4765f3a6fbf5cab970c3a009d4fd2a670b021818a82b62dcba7d9a672db857140828125ca06bf9ab83d341d994fe00110447e56582af3ec61f3dae72961f53dd0eaad6452480914c77861caedbabb2b9041411d231c2e58775f5755f0467e1746c050d25577cbf8337542ed2683b34c10d8155ff3b6d69d167c3f8a466d217d8c1b0cf9e25ebf7391d10ee8ad40502870dfde50633d1d3f85a5c7b3c317466d26dcfb02253784ff9e2bc1cfa4f0648a80ac454a6eb4cf0318d40265b89126b0fa689472dcd965951cf80058e3029a8a1084c5438970d9fa72f46867198251b251d17d66c724509f5cdc12452917e586b0c51d2f3d61df8cd4a0e9a271100174a20d53b99e7d5e68b3b4defdbd75c49a30c8ce367e039ae89f90bb2afd137b152d5a5dafc4364f8aba593b5a3a50c8985092f62c60d1ccfd08daba80e9a967e4acd2799dc52fadf2344299040b4884c7a09907e35779022e5d7c33ea76c633e148e5675ff91555c10af18dc249833c0ce0a404da08da1897382fca1f26ec1d3ef2d94c3c1723a1c2f4561f3b091e1b3eb0fa6af7aedd0e00f6c45e46db6b392c2c483dad40de0d04f03f7c5044c2160c70c52f2ec385e52f2f28f3e66c2d54ceb6562834c3fdc0fe6a5d8c34d4d9851de0c93b4e06f1d7f3dbe622852b288f1409e1c74bf2464b6ef047f7255f0bf426a0674c56eb059ed86188205855d79050ac5eb16e92fbb189e9c69b5ac0339d6730817698c1397a5d69f8ba0c77b5def28ee038644b51248978ab03fae4bb176ac0cad3e75d82a298ce8cf1f723b880c3c513a103706161e34994df36fdc5bd0900b0b91df6b1f54ac25cbbf4f11315cfc463d99a4ad828ca5d92c46776fe8398a0c3eda59eabcdf1119d11c5856af53367e44d2a430464d49be7f9f3ccc16a7610abbd53952bcbbf199de9ba5f472ff65d10603969e356106e3c2a5c169c32cc705832e22a736d88abd0da2d484309e9c6aa8e8218ff3821419892ace85ff223e026e4c054969580714c4c94cd66aa96044aa2b96ce4adfd3d0240754d71122b20bb6a42f22c6263c13c337de98e82fa6cd4c2d18f664ebae94cde05ecdd413330f6b709b1ca6c4eef8987d138ee256398992a7e3e0dce94d20dc620a95f2fde90c4087de077de2d637f4142badab6f4e701601ae33bf77e7e2622fffd90e55ae0155723148d5a404c89e819b4b3a8a54a8fc60afc0d4d60287d13f0421a1f70b089c1c77faddfc861d74c00ab9c72ab0229138f7c98a1d3bdf2cbcc0d37c1af400bc41d2b61cea0ca17bc4c1067858eb10f1e343f0293a6dc6b35b55cfeff24306a6ac1d8a29bbd9a68bf07aab0e47d18306182766aa7c6d897f3f22b3005d2c0b50d35c064561b738255592a00108d42ddcaaa468a6d3c968c99af8b696560003df32c6682eba19e8040e558a9f2a8fd6dd45b0a156b1de55e6a515321dd02c0109286a43907729f1663ca8130404bdd4f700b73e505667ae9908bdab96afb40f21b7004227a6943d6bdfd411aabf1be52c21e6e313a08a646e2fe5a33b9a460e764bdb6af8b91c216da9c5d35377c46f3e745db85a665523c630e07845662506da8ed8453fdc872ec76e9c73a8dabb525a717a64a48cbc28e21b2788712e610301812a2150551e38bb57f73c07b69a623969bf7d3b88fa0d87d157f036173007c5f8d4eb7463e1b755aecb8e0160c44c6f19fcd3502a7b5cd499482b4f6e2df41b8ecff0566404c198fd47f4a18b0a9b79fed6427c73bdc3ed0236a025482d029471b2ce45059f632af40b617b2f5ba390cfa68c5e8feedd9367aadca04e0c0e5b9a8a1207eadcfecf16a7678183892e6f9590f85c6a8043c1390b78f63d180a0e53de3d5f3bde83b51e78daeded3cad1fa414770001a22e28ae315998f44c032b3c8f16eb33cac01a7799712ab563677b32b3b648017e7471d79f76c18c590a03838253ba2bb581be9faf9cc198af9bb7e0ae6bac5700bad7620badef14c8003a067bad8afce96819987d642e8bac96319a5ad464b41a6fa411f73ae002e70ec5a67354799eee3fcd08aab191ed82b4376fd23053885fa961a38338ecca070349f8121d8a52f64b9162568ee412a66fe520a69d752c508cf784d75d2e646509a9bad2e5ae8b349b3a634480af887eb447f11fe670eaad0fb53f078118d6f3047caf49db76bc3d78f62c93f755168fdb653fa512257212dd62378f906f5a5e0518c5bf95102e11fcefd20def45e5141cc21d6f5bf705e87fd455f962e94b09067c0aa1475a01d70c18b7a42374e25de25f590249244f7cdbfdf64b95140e0100 false +check_ring_signature 6c7dd1be13daa72ab5d1acc8f7b17122c40ae2486454a635a9ab66ee6ad1661b bd4f49e7f6be71e449c43118b617b9aac6915c580d1bf7dc212146df850153c7 15 379b02a93a082b98dc0854933f40c46a6d372df6eea7a24c1802aefb54876a60 08715554395b218fda3ca04443c88b488d9179b41587058c2b96fb2cca8a2419 deabf2e25e9623b39f9aa1a1c1a9b5fa550cef605c72866778c0bfff8a1e7979 ec799f5d5f9779bcd7c3ce4ca3100bdbae3ca244bcb37c41766f31fe9b8985cf 2b05995db49a21fd03ddd98a962d49c26c9d233aa541f00aba53510b76358342 22a69a2aaf2191621167c1efb8078468c0216741b46aff51cfdc93b2edb010d5 c13169f742056019404f1d913ecf242226cd643c1451d50805fb028abee01438 4c1e26775080119eec2fb8bfda401e8d4bb17e6f919d9b0b9efdc62abccd83cd 827cc3261423f4addbf78af8aa92389881949474a66b59e5b68e52c1228a5a9d fe6806731dab9ecc631648595cef9968469fcdb548804c498013465f5d62b9fd 410c3c0e3a7102d8cc7cd6fb64d50cc77ad1aa979bcd2da8eaba435fb54e3801 6ad2c4643f8bb2a7f7cd3efc52bad03b7659cb1f07237b2edeb56086f3fb3fde f9f62f6d71353c401cca7d2842bbd494a3f2b88b4e8836895936200c349ac695 9c51c35cb4dfdb19f03f9b6d77a79bff701a62a6eeffa4a3a1e626d9c5e8a5a2 70b7c32c53739701ba329f69037db711b28dd07eaecc7c68f25e01f236164a70 7c5cebcf674602d9cb57584a23bb248106835adad45af7a0bc28a2c40f79810bb2bd50a5dbaf065903233a0cb1173d6a9ef78c4893f83eb38f5b7a62f04d5809423e6197eea813d5a73aff037b43ecd502d27713dd8e95cb90d7fb374d4fbe01595b70ae2a117ed212f9886fd5488d3e4e8863914dd1e0c83734150b8c96ba08bc719e92c85ea25b401a4a8af0abae3d76333efdb3da650c0edc21610aeec803775dc5832f9851e19e1019490030b0ca2c157abe626c6ae2b15f15752afb740e6f0b45d6b6fcf10921f1197ef2e97b69b08c225fbd3661a968dd75e2ead5dc00c5ca9c52a2d71b27487c0216385d5e14e29f15d50fcef43813583e16980e0c0bb638eea89fd61a529b59911daf0c45943e933424c7aa527b59df0cbbb390b00dc115df82fef787433ff5c4e30bfde046513d433235e3cdc46ba2733a17f26307be303c2d92c6fb323d1aac74ea8a8531f4611a73c61c8de151e6cae4c4f09c0dcb3b15e7eb6d2fff880ee114c8fb99fec5e4914a80b9625517220941f4b0fa96bbe8a75fe28ddb47d32a090077c1d80504bb69de093f7f85a8c4f280f853e6034e31828a0a81a206df48c0dafc565b2cd0af05b6bb322bbae946accfdcb3850544149c087983e4a4392ae1959d606d4f02dd914d07e82ca4559fcebe7278630e8b47bf036b6e132c5be19f48cdd18a1c1b3761d5187c72d9fb53aa88e2c4c0006aa83d17334fa9e9e690d86978be541ded705b8eec4690a4d399826b2da412094281c6ac3ed52ada11a5c56251941f57ad1525014d529229cbf49444ff94f80c15cedbde79e428286e02af8ebb63321151353a288d001f41ba0420015557d40bb75dea5f63a15a48802254ae9387380343beb89ae7adc69f4109c17902fdc104ada943181d195827d673e0c33c52ef5383b13b911d236093b98e05af1333670a8279026d09332147fe9d036c5cff95267aa01b7ec488ab93e530f63e07bf8d0f8471e4c58701cf1bd5b31072e3669b985bb514f5e7f36871ef47d908f7027451fefee36db2d9613c7572a20d47689f6a4012dbb9bc2f67138ee9cd9d022f3800d95e414cef767a5597bceec5d3aa84d5eb31ee2da35d5723b51387df9019c1066e04662b26a08ede3bab54c2cce953bb5232465a69348223e004be04f657e20898b2b6c55d270034971d3da4f08113c9688e1f435aa01b45206ea7e0d270f30513276813dfcd90b3afbf90a68309282f9bb7382993b53ba7355b55f43837390d2e47c1b88174f970a2588a8bf26410003c361cd9e1ececcb90d69c2ad57d960d420c65b3acf16088f603e2c48ca4325ba9e4173ed86d2f1fb73211925efdfc0d false +check_ring_signature 36e6ff857298614bce3cecd187a7d0cf54aa6fb2214a6a1bd944f7bb921f82eb bcbed0eb68827fc3fbb3772f2123e3dc88764945ed86720f3d2ed0f3cb7b47e4 9 66208c773d1db6206f12e71f5fe3ebcfa653b07edc8f4dc142e8fb70526364de f36e4e58d2da0c0c84e9bb032e833cf4f37e0e151ffa083ff7bbc909bcd663cb 036d6b06be70bd92b9295774f4d2a7488f8b73dc210a7e4b52f183202a260005 90785e4cba8e18d0ec3c3998e5f0292011539ba5759facddeeb566c52b9d8bad 39cdd97eca1239ffabd26c878c51fab89d0fb15dd8bcefd502f9a81270b103b2 2fd370c3c00a79c227cc6d356ac40ceff7c568311a49ab95cd328ebbca6ac088 c41708962326ef6bb525e8c953655f915cd804e6765b1483f9aa9ed1bbc7d122 7ba761d82a46d0d654ddd614cd37545f4693d73fbc84140c9d6478fe8e9a718d 407206e75ad26bbcdd9f41b466514000aa553e5dcdee5b057627be32a715d494 897bbd071769f0b1b25412345eb96d8e544cac50969dd7bbfae5794b1b6d7906b86a291c4fc03f9a97cbb337499d9fab945a04bf100ca47f216517acb09e5800171e914bc60f66fb1737608a876c4642a862c7c95e54760a368eb6d4c82e9f062d23303d1c259c8587b8019eb969fbe1b16549a59e3f35dc0af2cb52c37d3164b475da96eef8f001bfe353b01f56ff10dd26f1b7cb9366a33d08509580d1f60cc5907b64cdc36d9d28ffc94f93754163af7e0220feea95c6f4fe0fa7d393360234293d1a06372ed0ec2b0f10bcf52ae56d0a34347e04e41dfa67d236d62dbd0a2a981cb4d3a7f11ac78693a0b204e02bf4230709cb053ba3f3cdeca7b8ea5e0a47e760dcc77818edb35adf604f6011c26218963bdbddfc4c3227ef58f4e6bc07752e3a4a9617b9d78d0155e5563e66b0c2539f0f1e77df7c719f2f5242e8200776f6d4b9fa5f58bb75211f3dc5407e2653aa62df6a9b5bf66aa6406419caff0301503963a8ab22139de0d6c41bacd5f98057e514c2c19654236d53e8f476c406b7d06b43ef4d19ad45971ee2f843f6d6c67f75223ec8a1492d9fe77771b7dd0cc8cde2a3fb348f870db5f7dd25263f05d860108b6ff1b8c4e7aa2d89dd60c005ccd9a3b89e2af4723026881daab05986881ec6968fc7f3150a32303be0555d0dcd4fc7b9b1da6c4af06e738ae93172da3eee86024c5fe66f39bd9e129dca700e5377bef7f8043d29f960c0ea125adaffe70abbdcc363c0fc37f094531d569308cdfaf1eb638c92e33b9c16ee2a6f53b9857d3b0fb1c62958a529dfc643c0f805 false +check_ring_signature a0fb62dec0972e65d071b33e62e1710c0792bc63a24d486995abb6194b5186da e604001fc3e1bbd8e39aac098c8a7ea27f9e09913232993465285c872f02dd39 2 3357ae3278073de8a4b60512b35a7c969dd44fb8603fdb9e11e4ae789b5bed1e e69a50ef61b4f3413eb035604b5178c9a23ddeb81ec49a157c7fdd97baab9569 ae7462d641f34c28dd35d92b98885ca1b0ffbab86cc245bbf3667da7d4d6050fd28f55ca567b18297e8fe4e4aab2f2f1c7f2e54487f4bd6511c8d70c88e54a04c8dcbce20e4862022d264b46af74840b8c226e39e70e94711346501e67799b0ed339819d9c22c7f38f7a6ddfed58be612069e65bfe58208ee605faaac57f080f true +check_ring_signature ea6125cde772b60f74829b260d190e41f6fab70c9c6116d035740e58deb51e48 65ca17585d805894ccbf9eb5238fb9ccd140f8f8cdf0e84e79d161401422d89e 34 e74fda97a4365c0d07acdc0f612dc0f611ded4f5e9fad5cbf9e01ba31ebf2f1f c4e32f4121f418d3243d458285e0c9617d9c349d4100058b58e894431c4571bc 1096326d88b88faf06d1cc94ce5424d56b55f13f6af4bf56655257931c52a4f3 f1c8723ac632d50074c1a61386e696f257d7b507599681b853c2bf75e62961ad 893e6bd22e0505170b846c6af1a6594592510e87f47b84fc5ce311d7caeada3b 63e356f4eb9fdfd508f177622df9f54fcbf6b4e15afcb1ec4fb96e8ae7305f06 546de788eee25e3f7e13468c350654994d0135622c298c703ef42bd212864094 00225a8febbabc8e6573994bcd6a33f476d0dbc8e9e567ec99b4ce75507706cf a4b0f15793e7fcad74e15b269ed5a3812a3a0f6c9acaacfc0e6f35ccdc3b518c f59e3b878b7dd74a0f9b28cb84cf8b418ec4ba9f6dcb2dc214f9e800359f100c bad3a638e85aed15f9b80800f5168bcd90ccc17e51059f30e4f15729184a15ed c0b02b55375b3c863b1763d5328c5a1d6c32668ca946179054317ff891549ede 80e80a862e1d23c2e20eccd2b84ae6aaec3e1c56fc1fa6299267e1e72f603559 3c63b8843729ec981199ffe705c6b4ac4c1cade7e282e4b998e9b24698e98696 f28cdbdb7927293ee8fc122786bcdcf8779d08217b306ec861c7fad3da62d248 8ca775ae847eaf3dd4b63f809c0879359c0e29cbe74c19fcd5dfbae794e23d2d f5965c5a199e141647d067efe18376018f6419a514ddc129ea8eab67f4411c5e 311b28d771d5ec068a8eae97c2a5c2abc44b88fc52b9f5f8e59d44a599b92885 588cd725e359064ec46fd7f0a310aea2d16a37ac8ff6be86135b60e096924789 900485146e2bd8d32a93cac24634d0f367b323adbc2242b601c8844b939d1bb3 ec0204775e97708181a61bc7fa743a2e1ddb9c22a376690bf4a1e42cbaddc736 245bcf65069c2660e4c67bf25ff85e7c557f6a052df554ef94bb897c3d1a56a5 1aeac1481b2b24808f31ae5a030767d9f8a0ca3e97b440a4f113b234306324b1 5a2c1d7cd8ff2b9c93245739a4e9ac5cfadb31dbc7087792ea3ce9acddbd7e41 d3638115ed7898d2afe6153ae66a671ffdce92a7fdcf4e47a8bae36543af58ea 53667870ec8030ebf90c04df90fa89ce142ef9b075b329c20cd8f45c902506f5 a9aee221f8bd747b57d34c245653ecc3c1c62fca85828860804914422eacf9e5 77159bb556cc194d3191d8ccc53ac8b621a3573498508cd6dfdaca2981519ea4 269090643012411eb0f1a41ee57a20ffbf266aacb63128ed41f6515e9818aed7 9f11be65f6c57349bd315732053e370db2c4d7e605f696addf0408e51d47405e 29623a8b231984434767fbf010ed3b2cd4bc2a1e52208e051b16c286c6f468f8 c4f3e0a727a7e63f300fcc518d855f092f99bbd222a1de7a7ed0e3c181b079e0 8a8b4a0a1e7db637284674a769ae3fbd0b612a239e27547bceab6b2eeca4af80 0a4f26ee8c0e9011cc5ff7498a03acdc6f93c0abaf65dbb7e6235123cf21e698 eb19649627a9fd27dd4828f9b2349afddd13078261c9737e20a72fd6d420d102c9a153746c02ac4bc4de764fdcb5c4fd095a30de3a905083deb5ebb1b9e34b0bd7be26515b386c35d3226627572a6dfef5e03ac2ed29a76a557d2261594ad309dad4087c51b21bb950213602526b83452825bb2d9edc540c7dccdd413112a8051af59b071161c35044b21546ea46b71ea2704a9e0c9a2a2cda52b3d5d008bb062e623bddd0658937b97770d101df86ee9654ff2fedc5c745e302e1d5d5a51904bf97d01aa7db299580fcfd32f80b031efbc07b3199b77888fd404d617a721800c75fc6590d4c2a1498d7922607eaa80408bdc9c7bd68d0d65f3c84058c0eb30e8622f611b711d8b806d77657393c292c28d52805fc684bb0690d13d22696cc00b22743df26ba9a9a43cf1fb24e88cb185bba2b0f9041c55954a904f851362e074ed09065622f50bce8b49eca3c78ce2ec167fdf4f632add9d6f704288df74105da3f45d71cf6dad5d2bede57ab32fa10fac990d0e7840387e86be9da76fd8b01efb690f3d1709c21f445fe230095a1c4168cee6c4935e13f29a593d43ab7bc02523cc6f138d705ae9ad1aa46eedb742fb371ddda4a43b0bccd35a467140536096c86603f33bb646148ae642b9ff38d33920d1b314e3f28d9e7ade6ed77553208e37dbe9d87d00605ae14f42c5a0e7c58222dc18f53b832b82be8037575052706d306e8cfe7ca565c08650f8a8a3534c8ddd30449eddd20828c6f17ca3360ed0a461b09ada7a092aee075d31d6becf7e7682912c775b8d932be351ba3a04e8d08fc62561b8e42d0ad965a64a12e7f4b29b8f285eba8d269828396b8a5afae01037aaabeb7f7c16533de30370d52f8640a331192d424675953b48606b2f384db0c4ac827a9a86feca9970ffda5cbf59c684d643e8c43365c45f1a6ac8b677fcf012ab6425a431c503fdbcb9dc30d129a905ae556b4bd19fe439d43bfd34246df0392b493256507f56e2659f63344252d7d17e4315bb22a3cd243d4d736268c5f0cbf2cd4aa703cbb2e38715d92c73e43da6d2e30411d55eb757ff2e87d9921b0055033e6a2f535cb6536dec429a1bd076a3fa1d63b1f95cf95199ce48b1ad5820a99ff7bfbac84e33736a8410b0f7fb39cdeeb9b638809453cb864446ca7266507d4a8e71ce769b9cbcfd3b01dfd748b9751caf7ebf1ca9f4ec589a455aa970d008787d50fca35097435161cfaf88e3a5e32dc9f1f2158d618a1e101fca99b890c352b51a23000d0590a36dcf2fee16c934400c68560f1ab3d01d436501c05140f248a6bd70648aa993157599454c118cb1c22334c176e0ec3090de1642adfd80738dae35fafac437af03b97bacdbbdc938e0b6d7935f94a65e53704767b3a550e6ea7bb826e0a0f5f4bfac8f84d3cf4d655d8c564e621cecdd09099fe859dc506d4256eb04c49d685b24e95b447dfab1d2e9f72f2480465ed6c7dfb0b7a4d340a7a7f1d1f2b85d594d2795a8e84312dd5e9c1f371a8a24118bd7e06855c5b54060371ea1eac081745622d8c7def4e6dc5d1c76f6019b34df96b2d0814b7b32f0951c7b8bd3832c2969d20ee0991e27366d7ebd560cfcd17ec6404f85eca5cab0a601d9a6e3f940537af88483d56a270b04e87850bb483f03a8ec1d4111f6a9700956cd97ab9a50ed12184f4480435ec821836ee5f45ab0a514f04808a03e8c00a94968b838baeae7da745b3b84234c1329f5ebaca53624b70642aba676a16d60cefdfca8471fb1db998c6c1d2fbd87fec0533d1083343f3d6aaecea5be0f5d40279541cf39fcf75c1f49a6179265281d17478cc08981efd9479a28fce6c4b8c0adc67973899f6449c0b744cb5f739fbf03fbb2727894f0041f3ba01c0c9a10707faa2b88124124d374b19b8a52050f9202bd3502200955f68eb563d8a01815d0c964d997eff3e7f3c9c9182662fbeb05e1820b639e561540e1c9cccbf7741ba0259006fe6bfd5a6d752f2d86ae6d9855c986c255b11b94d0f35412f65e32dce06e1e35cfccb2107fd0c672c09546a831fedb63f2efe44b6bd89752704d4473b0f499985ae6d0c01af055cee03e02fc093c8bcd160bc365c94a187630898aef500fbb94d92bd940dba86ce7db1541d91ac71794284620eda902f0352929208200b7bb02425ce94d408ecd563a225f114d4e07d81d9fa9f153022f9d5ce2fe1620c1d1ddc3bc74024cfdc8adec5ae60b61a85ab28f41c753c0864828bd99f62ae06480bdc235365e5858fa8a071aad99c9d39497a201fca44ae0430ee1ba6db050f601e3d0abb5dcfc4445f6bf3033d9d9a8682f52c816801a2727aab855f334b0636055461e2e3f73089a999b80410d72a1667ab095978775d51d3bfc08d723a05f52ccebe35df952c4910fb5654d07014f33ecbb3c6e26cdaad032d8e8b5ded06e9eb2382050f50d3a2ecff59bfb872e557387b0bfe3c5d2b5e2d7bfedd0a140b55184ff4c5bb59210e6e69b0aa17ed0769115fa6fc883007acb7ba4280d4bf0593c37be68737eab9c767cb66967299f3728711a8e6174a847410f5031ada1904cd900f9e40733ca02dde6b1cf56e7d530f58d6c6588b4ead9810451e8ce762039e62db441b8da0db9e515453c22f5ea5067815399912870bf86263c0228e610ebc5b720e2cee47270ba57fa88989ff6680dfe9129ded7f6e1821277ed419ee07fe19e8928285fd783aae5e83e998acf4a686c12f22fbffd25c3cc232e3822305f1a65f6bcdeb4714e79c9f747c7e7f96598ccfbaea25f2f970328844081a2b05fe2eccab97043521ea95ab2976f70e0a18ebc77b387a094fd0749fdefb7222091789bfe79b6ecf9ee76cabfbe77020809b5d7e5d190e2ee633b2807a88d7660dc1c51841a03484673b331bd9a64eeb79d81f55cd9ca7734d82f16a240b332b0be71a17528863b48d03c1aabea6d4f9a4c1336f97c466db0bef162117dea126013b55bb2296eaf7ed68ee393d4da8b083a7f0968935d51694514519c602b98705c663431aa0cee72a92c52720c2d67379928cd64b1573ede5f1b0f72a2cadf20f true +check_ring_signature d5a214fff0452e51bed9bfe62f0cfab4e44045980cfd727ec0559e2c2d2dce6b cbbb09405d790b54dd2f03187385f3d5b2a5f989d97c3180eeec982029d044af 1 f092c2fdcb1397acf7e66eea9bc9079982d4affdc67924609ca87df143647799 a8e13e3a80a05411701e06533c2de023477cc0563c33984b3205e05b1f25c87c9e57439cad837e72f3888328f54ab4dda37279affacc191ff23a8c21a1274003 false +check_ring_signature 0c44fc24fd8bb098acf165d2831d54a33974c28a7e106ffbfb2e535cfa8c5dfe e3d906e968a6325d43dd85d935f1f9e79530ece40ce0c9372e8adae2847ca4e3 11 23fdff88d9bd52bd9daeda445b25e371c2ef6c242db5050d816ba5658e086117 3eb945719aade7effd44fb15bd56f517fb8ad225f17c675ac2b832efa43fb203 b7eaaaf70cef1fd09860813b2f539bc79436be428b9c32ecdd0e736d1a9f7d0a 51077c1edafeef7940e445dd1725776f973e87e8876ec95e8c9988c109165471 f5e690d136f76e4b3119b8acdaade5cae1609f7d4f7d0f9a4f1569fd006ea6ff cfc1b7534450c2967b1f7f48f04642914a9de53846ff210ea1949b74c0d449cb 5e9f4939c435b9c11cc2a13abee6a2eaba6d003860dfa494b090251c0fb47312 f71359dd4b911d62b5725d88fe27caad586556580983219269f39ea1ff8d2595 d616b05292877df00f6fa39b6924f4376634e945f728f4b98a3ccc769ebd8c20 0f6c2e46e7bc8ba9bfe022c300ca6a7aa74be2a85a7fead155ded05c8a7efb63 aa763ca92fc4fa7fd4c8a9038ebf7d871c9c0be21c534e4f231a4a2dfb349fd4 c73758ea789129b2976e921ea228ac6a0b34094d1821aa4b5c8e2038255bd60ede9a8feb1a4abd9cdc179023cb5585339497f0eceb8519662f5f461ee5501803a36b08512e03c790efa85430f2ca9f8736261f18cef7ae095ac6d907da770f07533f55a42bd5a346406e71d1e18673d3999ebcde86e673f453d405a7416e1b0f2512f65f8806e7c300744d9e580fbbec4a8648a660175305a59a4399b10dff0871e5aff630f9553e82e3333c0be4bd30bea3ef2f76502a46e90da3f3e2569e095e2f5b89585b57bbda197c85f890e3fb832bb67a7e9085a433656b9237bfd40a9d7842670cab9f5352b2e5c19ac8a741952e5f0503977f61c35a29865202ce0dce690aece3d411397e617c5557fda1837d1cd5951d4e569c5324900fa205c40cd6dfcbcd3f8b1dd7aa54164f6462f71865b6b57853764432b9684a8c26b0920ee738c7176ac463e5c1411444874af39fda6a77b5db0de782873d06ee18454c0dd11be1ee5d9c8613715e70384c9fd7b180371f0b11a22e83c77f40fd26b0fc064ade7a60ab8df55484d08e1e632814bf8e91d3d6603399b1bf794f38ade62d095e257cffbcc7d1e40aefb9101a437cc9e350e6b04777f336dfa8f55bb89f92017023ab46346e04ea589c11336878b6642e8e290ea3bfdfb69b84042fd45bb50e6f548739906cce7a166959d33cdf689b1d7975a6eb50f56f4b3818e5c86e2c015dfb8a16d83bbcdc5ab618e410028ba30e94226b863bdf64789c95fa07fc11031c9c0d8cbc24d48ccc3e4ad1c2fa7defe57bd978e03973bbb129c6869d3bbe0f6806822eab94a5fd319c5b348161b7ffe5fdc070245f42d76f8b20dcec1ec30f510b1f58cabc723a712a94ad729a063ba9632bb60872ead94a681aa2e615c60126d03a40b56593eb29f5bb66ea323dc0894268d6c5ea7105e25b46c5997c1c09ee8b60f8a88cba076835d3325af15437aae44497357ab30e86bde81e39ad430b false +check_ring_signature 8e37c2891e859a8d038a6f1b1c0da2c8c732ee1e3a9493cf3bf144cd7308d165 3f22ccd318edb29ad8938174d5af9d854857ddda06821539ef8af03ec2b942a6 181 9744c0826bf7491de369bc21132e1fad7c89f7a63905344e009accb058f1e002 c48c3af0ff59fa7cb511b937a2a262443b6af9249dc7448c450ed4242ec37611 ca8170b5ece9b40334ae3d9e0fb7b3034fab8c61958b81915a67574e13d232ed ee2b82f2ed387fcd41b28ed3d0fffda015e063ca926111a16b7fcd25d1f5950b 4ba1ead40a5d0b36c4076a427fa73f71dc7f8dfe9a04a11e288ebb1e5eeb3789 de2c7240ce5555b5106920f92a75ea8d90e86e3a2563415fba0713c8b6d20d80 7f792d6f1bc37909a44fa205c9e993730a87993adca6a6912075788b7d382c38 544128df055eb246ccd28bba037b73d215b08ea2e31c05a5e6a5893a472bec7b b68e565a6fdfc29163c1eb5e8e37672edaaf1d9b79b568c719e23abd89e30100 c0b20adaee1ec281611b4e2dbf471b7ef13c95b3a0edcab39690548e1df19b12 9cb5646cdc35a3e0ab165bccce143b1f83f894fe553c15532fe293a71992959b 020cb377c9bbabfb79c0643f1b37e4b23c3de89ba2de750cb1fe33231896c6e2 91566a3bf19b8026bb96b5172469f6fdf5d0e4436c7c077559c129f3043b9068 3ddca9c1aa98ef86b5e6f5feafd473e0bf6cd056743ccad83c185e2dcb8b5756 86fc0dc5fb2e8d66c328d8a283552910e4b4795e9735983393cc287948ae21e9 b013b6a210e142502227805e28047b8a5ec917f50ec886b840c26cdbf56360c8 6da5b52912055b512f31b3c90da58c0983c53e4f8fd81bf36cb17b52caebdefd b0860bc89d99e36dce35001fd2fd56a5b5a961bc2336fad98f8ba60541185991 da110ab301e9e1f7bd6cf996e50a67b55d3a1e4e239ea35219ee76fbfe7b29c3 cae7575d6f6b45f92f60d7ed741c4364a304edd771dbb0524424cf2e1895d6aa d14a87293d42a71943072cf55dec191e63a85dc821b24badd96680d4f041d372 e36bf16786da74da463e430d156aae25299465e76a259242d044c272d04edf88 b98d497d97eac51c5ed8a9f5460a8535c10d979156383e45f6a59c297599a1ea 55416e915c63c777f7db91a1d7249e23bbcad3e0c01000f2289163bec0629938 f1ac760deecbeaf11ff4ec4dbbf62a5da17b5169eebb58b961e9e6851a04164c 9484cd07b2b56713cc72b7470b508cf642feba9ccadcb6e1943605b5f892e357 2831b62c2c3aa16743c356a458a02b0790c3b29aa34782541a87dd0a6e07d8dd b706e5eda1d1fc8cbc66f76af1df091f510d1b51707a6539a2918f058ae369cf 0de7884e298c8a2cb591218acd4d936903f0825440bf70bf47094be0de6a8675 ba973a7c79e6deb4565f7ad5a9706b67d5b161b601031a0a716fa18570f5aa37 e252c94ee9483aa307708f854befef62b9f586be9a456791df5dc9b447b40146 f75153969a85a4852604f10e83004ec474badd1a7600fcb24a22c12894bfd1af 4f148dc1ab6a056c44eeec71926009d712712c3284490881ff8cd56be439a562 9ca2eb79d9990da8be305b6bd7edb8d5213d8fe881bb39df94f5edc2d358c7c3 6b303af3c0f1f9bb4bec943e2b62d1d3da6ccdd68e5aae70d45c320ccfdf3c51 25c6f78aaaa2d695af4b22a9591a9fcc70770960548427cf81b2380ae2d6e2c8 94a0ec3ce93be0ffe9a2d593f6f20adc3ce590366c4cf2a06201f174d9d9f43e e9d1fe5df6f1ddcd988861a34970ee2c4c71155f5d062af5a38ad89becbccb7f 7e434a13d5f5893c728f709702c4070ae3d80e148356e69dc94061af49857449 fcbc5c3de45e8e435034cd468e438abd0ac0ab1ecf9be9acbc0f5140c81676bf 399ae5127546a3430e6093ff3e8f523cbc0be23ecf7d155cfb702298a51fb82b dbdccab40cc86be05077a5efd5380fa34e9df3260d43ffa0629b10c7273ffecf 6d11b6a20374fc0a3160e2937da57f4f7a5fc698300282d5121c9045afbf8dd2 3bcad34a73816207f5b3322d37128371b29f9142c6719633f66c747320324f22 4350e5b44175886d32b1d690e19b5daba3bc714908580ac4f542cc772c169c50 94506dee6dcc46a1d3523a77d8c21f184e97bc0dbc3552d4e3eca63d24fc2a97 8c62f30d6626c9862758c0936968561db1e3f7ce6288419461268db4dc0cc51d 912e355d81a9f9853c0f18a35e10d4f5f3d438216008d2622a46adafc823f28b 53e2be43637eac798be3e140845c1cf12aa81d208fc6223066910eeec2e1fa8d e12ad813d436e9ae002aa81861f563f8ed970a0526c3eb68f741ee9eec6e7011 6942615e8080f3f8109f4035f6d9356f958cdb5e15643b3fbb8267567bd576b3 5bc7e27a71e40c9a489f48128f363d6c5ac968dbdbb6a2e035fa8c3e01e7ee42 3ea4f03a2ccc8c35a3380ef6f8c4a112086dfd28de4fe90ce2c595ae839f2662 1e5ae6073304cfaa56b56c7a6d22c366f37040fa278ed0442bf4cc5be2ec7313 aec40b3793e084ba92e1d150f23461876bc5d9560ad6142cccb688e6712f536a 3d87052354e58cd7ad70196cedad98b16bfe9be1a08ec6854d085fb6e1419526 ce6400e4a0cf3a2e02e3799086e26e66abf933903e4aa05af5f4579a55c59a16 589e0125b72b82829dfc04a81a566c9824a308b33f54265c930d712953447a37 8ac1de23418dadfd9f79f3401776cc59abb7ef4b78a7523eb2891a110ad84e6f 8da01c13783c9ed4b1c011c6fbd2236b77aff985d80f54154f5101af935cae2e f68a89b94eeac21167ff78779c2f6acef2d241ae99f832f620dca6f80109485a 2d39fc592dec1b3ce9fe28ce326f0b5a46027165850af4576ca61b6c8408befc e9d6a831809daab1f62195f8aae7062135e4193dbf908bf20fc5ca045c845b9d 40eefccdc99ec9cf9fee3970f6f36a4e41283c58be926fb98e07cc2b3686a098 e9b413d28239481377ecdd48a457b8e459d64d0acb4636440c9a23b4620a22dd d7bca737fdb182e1be9312157075afe45d4f928acf9ce1c36afb5ecdd34e5967 43f190d1f33062c034e32db980018be867993fee1a5bf660724ce056d487924f 6e293bae031bf2800e60aa7678ccd5317a62acc101dead1283f1876b87c8ffa3 7f751bc238dcbfb0278d0b559fa7584f0f7362ab3e13067c6214ebf61b99cf7d 172ba6213d21f7b1dc796bcd36c717ebacf1b921103b71b126f06a8d5cc333ad 894ed48ddb33411cee556e030608ad3a10a38bb1c8d16868d13e160bfff98da6 bc8630107f14726e80eb95130c3290e24d20564126b85ed72b21bf92fd3f79a1 a3d4dedad459e3afb01411ee5dfec42f8864da598e8e51a3a17c7e389bb5d9da 818acbbc2c01b5d8f5ebdcce5a6cdad99084c9f2fa513979613d9e9365aa4b9c 8541d01328f46b14d299e8865b1971f7b62a45b0746ba22a72649554d803aacb b0792565ac46c677be3a0ef94799c66c5137edb1adce9d588879e9aca280bbe1 d5ca7086651bbf0a75b8fda91f3cb2eec2876441b932b2cd6fd554710dbb12dd 7a8533a5480590b903ce117f15da38c28e4e7c1f962fc3a475736d3b6350f1f4 b3e46dc705b5fa9559c4691e9f82769225bedf29d4a834a19fe8585d5b8a0716 ea406029c7b9d370f9dfb855b0945b1a520b7e07fce61ac06b459da5aabaf7a5 437c1e1cfdeea4ba2f85e18e8af335afc37c321967d725e86f6bd3f652adaa98 a4e7d9f932777d12ca375281ab5854b18ac4495a527f706e7024283df97818e7 94642828e3720b80af239ab74352e1524870b238b011c63cb8cc98c92770d226 244958b5bd7fcff0401a1d4a950fbe8aff2021d6de6197ca9a8cf561b396dba5 1df5dea9a7ede6aeae4fa3ea82dc597ca80f1840f82cb12ac2653c5ec5466d89 44311c780ff2bd634a4b793d5b406df2cbd09cb897d381cbc90586465da6c43c a2e39059843067e298e74424f68d4d6b68321728a51a095b7881d898c99c3d9e 8cbcd288ee7ef857208ffef82882634c746a78491a88a388ec29df2b8f461019 0620ba850b18e7c05f836078813e2223daa74f9025ec2ca3f619e0b9b0cca449 62aaf8b8fbed022ac8b29e036736481de4d88f9c54392443c755e9e6a3aace28 efeb8efc2c35a0f4003e61839838be8c409faf38fb34722646f4620eac7ce249 ce24f909629c535397b729cc0bcebe1b61e2c7b57372c734170c84f4520cc351 7d9193abef79255475cd19e079e49fd2f4219cc07a2db16083125318e8b18cda ddf293106d7a57f65495495829fbf5b675bec5ce4d6ac063ccba86c4aac11c65 e48776cc1b68deac5df90cc65f0c5eb8afb537200bbc4ac95140b578d3937b5a 5fb43fdf7b0565717ef9d0118b9d3f666dc4a010b562137b21eb8f68ee269265 240335e6239b5f4c0befed15b05d371a42fe87f5d56fd0614398f2a4273633b0 d2c0e50e156574da19095c2f7010cab2ae546547afb217ea34bd0e1c70ec9fc3 afbe56066d71d5b479e265814a56bbf2ae1003b366bdc95c8edfb00c03a38c74 f62b267a096a50d9cfb6346d366b7a2366c4ec7135b557cf9f7467808a9ceefa 8130142abe0a221a9dbe9f98289ef3065cb2ef730e2c37bac366707c95fbffd6 69769634c2ea43fc7845ed588aa81054d5f17a261cb3d55c70eaa075f560a110 58040f0053573b29430f568800971f44bd28d5ea082c1ff5183159c1dc6298b0 645499a3f563c5085346f9320e92c930252db749a6b3e548ba6a87ea949b99bb 4bc95dd9c45fa17dea8a30964ddd409171bc1a74bfc64ff20cb8f6651d6dd3f7 376209e90914f65a6c22ea89d62b48416e1af9cf1a4501e0d956f9f8ce21a998 d89b884012de5e1839b43126889c989e0ce74e46164e4730a79b42ca2166d6a7 b209f7455f810b7b51087947239bcf989de25fcb0a11dc70c5691d4cbcb17a99 cc8f9ae00f11274150166c1a7695c0391b240e7ba64d980cf728e36acefffe7f 641fa9a00b0bd3c58c9ec8e2c8ecdde590d1cec1454a1dbd90a24c681f93e287 d26a64d6d5b3749a138b2fc48e977fd22b9fb67a2c300e3059e95852fb498465 728bec1f9a7fcc6e59bd217349ef0cb817b481fefe192154f3886ecc4c9ed069 d4828e6a40c55cd9f74dd79f3caa74b62f623b28e649f44486491ae792e1a4b7 dae9f055fe13094f88d3d10fc671a433190233e4cc255cffdfd3d8581f47da5d 7cf6e6d7468fa4d6f555b2c66adb78b7592f34203e480a5ba12201b2a4099547 cfc3d22bdda49059d75dba7989ab0f6ae418ee2a770c7e8bcf78656bf61b8138 60ad99f2cdd1774421f5c88b97f8a9c5a6b6834b185b880dad3718f037bee71b c0bec2df7b655cbb1453b226ad8c9471e56d6ef4e991216dcc2789cae3da0866 41bddba20cdd0324bbc812f184ccd30aac76cb105b351849ff154e9b56ff2045 afd942b27bb6f1655ac24fc77dfbf72b963344abe1c0a04e59c88ff733461326 2b2b5a874c35befb57285426207c0b77e6d64753b80f73e91f3c2a56277e9ffd edd010f38f54db529c0665dbaa77b9bec3f2cf38942def74c5e69305d9fff9ff 6de1516e61ccc051f42538d3677abb3d5da03d6334bf35632f0363603608950c 4b85a79a56fef1fc2fa82f3dae4d0077018c09c28eab02925cd2c11a3396b7a2 3479b4276565e9ee9dfee1cac87943754eba21f7231b385c246e114b7a6203ec 2230583f4176a52d7fa10bc1716577e61f482cbd6d352b6dd53bf388f2c51382 fb7ae5f9c41d2c9decf6098cfe2af15defc6d044900858a9bfa073c7e89e8e76 a5451edbadb045d7e3d043f81746c16a970eda5ef4b9c4a8273fc5fcdb4efed2 47a9c2d03deaef12d800f223d49b0fe0a594eecd062f891e04057e3ba705c99e b3970e12d7ee0280a597d10c3d8a2bbe35c5a8e4d2475f5ae722f410792323c0 f63e78d2c5b3a1f88943d7262410c5e53546fb63f42776fb759a04ac58b78c44 4c173d96caf0e929278be9a9ef447c9aa36dcab85ef2219fef3fad20282cbb8b 533b24b1c8502caf213e3ef7b4689cfc97c31de2d6cfe19ee44be6f2ff764418 2b534eb2b84594d796271b5ba0a274ac35716c97936bd8cb33a81e875eb1f4cc d96cd006cce23158562b5a223c39feb08273d31143011f63113b280a4cdbf958 327b67996dea05f946f6b17aa53414aca09b47b8cf1035efba941024f9b42f2b d98f39eaed5dbe526ed2310be7252fa211e136f0a21ca838d8c7f7fc0b2a6d66 6fb4e2ef486c71854baf06d767cd32ec4f0c654371d3e92e9e27ec2d86590aae 6f119ebe75f691fc89e75e7125d9f925ee5a743d6019bf0952368341895e4103 c936885de46fd7880dfdc949577000a84efab3bdedbc60a7ff5b32d59ce22a31 d409cc1e43fe74dd09c084a0e73dc98378f711496cebf30f45d49bec88193f13 b0ee3a72d7d22a8945d16856648fda4599864abd4d0f6d7cb73f2a603ef1b84e e38a32eef30cf28366bc0a9ac7b42adf409045770f5ae604fe43419bfc838e32 5d68eeed6e617451b5e52d67d815dfb4af3f465cb249767f382324010a40369a 65f807174daf626afe43bc4a88d1a42a34d10ec07c1d852279abc5a245874ed2 48ef9e73057fb4175193e7a333be5414d70d3f124f41f9ecd0f2aa82f9cb9857 b1bb59a323e7cc52979ec9cc4cd5b5d74596867a2cb4a89c4db3f184c3c8db46 7c6d16cd5e9efd3626ee12d2ebcdff9fdad4d317fb9ec50934845091a9bee3c3 54a994b4870da4e7ff4cbe4b81ccc4bba897ddedd7ecc6aa18d3f7b2428ac478 854c1765bf1780488f79125060ea89147d0dc98f6af884f18ff0a1e1a87c660e f5241841a3be0b7712d19c0c8fbef12fbd063c961bfbcf7a1d14fd9b49c6a4bc 4c351f6a4167f1f537f490f0a9fddd4d11bafdcc1a478fbcce7f113b1730ad0a 9c9cbc66935a6e35ae8a9f06408924b3a33c37b09d5eff1c496a6fb38b6dc45d ebc71371f2c00f2864cc28495243849bdb1c6bce5e5e36c1d9c3b36e1aba1715 c803ff7147a09d15e759b3900a9520e303577d66ccace3a0631ee7fcd16865b4 b3aeb54960ef3603ceaa23995127067680a409333f76155b04dc3ca436240146 ac1716eb233ade81117b6fd9267663a0ced9b905733935936a728e28564bef54 1fd9e6716c88d3db3c7b46e1bde4dbb14ed56f14bd8d21a822be0d30f15a41e2 9b6c57e9a84b62f3d161dece5ba31f6438318e18adc56bf020d3a9a4e733d305 c28989887183e68dc07001bdf3510b1d325f8933de97b826936d48cfb4956f37 9a27a5429e8eeec47b938090f9248aa079ff2e8092b0f8bea54fa4f183806ad9 941e16dede78f3ffc91253667bb717abce74d0238140e6e3ccc46b54844542f2 5e744d822df3798c3dc9580c68f9ed975468c49f269e091df2b4eebceac30acd ae9134e57f16e7e6b8736d4b824a31fe3428069085c11d747e510bf5c05e4acc 3f5146acfbff4d642692bab7f090a465d4f366c479f4e4edbf5219e175327c0b a2dcc816994df492938e7105ca101112dc85ee573b0291846ecd6ee6328940a3 e615676448128ea2bbb5fc76ca8f36b753246eb0fb940b5e20bc587064f1fa4e 7990a5f5215af51de84a8bbdaffed56b862128f828cffe9336c3f18b0f3c2de9 b085cb263269b48124e699c588fbfacc9e38ad3a3678a63f4dbf4aa83b7d66e7 ba907090078a13068a097f15746360af499d98fbc080af3f23d3a0510b688cd1 3b4f63364c7db8edb72855e0a2b5a072b70ae8206e274543aa677caf3fed3ce1 fc0c7849bc01b43acad0a56f7e3ab9ec08694c0bc7236194588bd99791f0e10a 40189e8245b46857a02934b2712bb92505f8253ffa63cc88fc822262bd8f15ab 7f61cd4550beff6a546f7cae86b4dd45468c2604c14a6baadeb187f36b05410b 646cfcf9ea09d4ff73a50746c1c81f13a8ae885ecade06fad390ee70dd7a8897 41983ca6cb507d1dd12a1d45b10f818a8bf825accf0100080faea5f1d291f411 c672e08ebf426205ee88f862d4decbfb82d99e552b26b810e84c13bfe433e92d cb8d1d17c5dd6afc81bcaf26eaf22f629412fdef972734d3d9863805fef85de6 d15a639e1747b9702fc238746e5b8b64822c1287761109e8dc2e24511cc7fbf6 7841132a86f79332eedab83cf6cb62b67132a064df5886b8ca55d1036db4c53a 723455d524eb4a3d38b3259207cfd63ebd32803599ce0d93a109c8582369cb03 e4e0cef20b3ad00c4a6cbca37a1866b960b73baf6663e488453e5759715a5b04306fb0b9fd083cdaf442add150c8694110eb2bf49597060412ee05feed313b0272f36777697f6e353440bad1b1da0f50b18a661723248addd3ecd7a1574b7103bb3c6a7a8aa7fc9eb3756554de51bd993047c1052f44751bcaa6bdcc9efd9800c22e9183fdfebc2fa22afe6757fdb1f040c819993629acdab3cf3eb6f42d160dfd33681098c87368b5d02ad56db0c87c9520392d397eb66d882da65e21e1700470cd92d46c5dd1b3746b5356590aa37ca47fdca8e2f5b253fb753f1d0cec940218d0ef1b7740c0472347a5ddf754f0ab02c0d2bfcb64288c4e0028ec7c0fe401f426d6261c7446a78544c87a5283b0a01c8f8d61113cf78671997a31ee63c80764d11dae9409f8f5b1a90ae33f6e72d54625553e0256277d885b5dbbed09c40cfa578a4ac74fb9ef92a7d2b7d63199c4712c706f00f7320000343bc3c06eea0fea034652360afa37be2d804c6e8430ce318acbab8bab22e2f805b5d6142050005855a6f9809d364a5e432cb330f58128296f19e18ff2955787aea273e78c5e07f97c95c15bc1eca601339491bcdd01b71e59c998a706b5d0c0eecc6340f34501ddc05557a71b57623c4e9f19b4b27a9ce9c1ace465e47958cc5811d16a571c024797e517a6809415272d57e2f1dabadc0aae330e25298bfee0edf59c04bcd8045e954ad69c18777431035824bac43c64fd838a8814dab589acd3f59eb5ac360d8c349b6195b541f23ab48f00b8e419e501ff2f90a2c1e24c04adb7ecab78e60858ec8b1cf8381d7fcbdbf2c5eaa089899c529a962198cd48d885a815787e670933aae1465877e5b01d998f339edc46a54f34fdbaab1f19b1e2b608170c0356051a02c4e2833e7641d9079f8597f63b0b7a09c43f8630e613943f9061d884d008ebb3239eabac6ff8427605e785dc3819810cd50b20d1dea5abe76502fc0bfc09f4ee0d8a834cf3179df978485b4eb5d59e1fd0592c6fdf887b530cf0d6da9f09b1a85bd49111ae88122214799f8131851621f1a3793f9db5dc28d372d2478700192af5fd422ac818925becaadf8c774fc05c55e0f9374a6bb9c80e3b99f55c0aa253e6798dfe3f3954cbba2cc188ab9f3b97fe779abe88c10a8d62e0f1d200036fe3891bded52981daa32abde3a3551c515541baab89d5307dea5b530037e50df2ffb723fd177fbc7f31ce83a98e1d2c69b18e39df9abb516fa541eef375f105ae2e99591428a6c961cbc2e5322b64ac1d2ab87e2f399aa1112246c2130f6f039cd5c95f937af2db4d14c3daa7a7ec8affb6d302987c4a35357b413be2dad0079639de301d2d81d1059fe567510cbc18e2f9b7020a1db71d2c63edf4479cca0ad771a2f8dbc26dee3b615e4f2ce3e17617c6e6e9ce4d1b9ab3d66e5a74885d07eaf9c2e521eed33f24cc9fabd40152651d68c2de2d0ca6f9b529b99022e4ed0b24784a1e6e5403d0bb453816067334b53511909b5d8e7edee418f01e26a745061d3b2ee1515fe1d8d0660db94f39c8518027f6ce9d0131b74b2af7659ef57704ff31c6fdf63bf9a4d34c776f146bed0f5ec1028be714c6de4cda7f517b0b9601829c32eb17b41175493f95fe650ce8bbe92d182a2d314021a1ffe4190100b20b062ee7e706145e21c6d945b9fe9bc46f5b9f72c30255d35f25363b171aa5c200a6abcb94c9f352dcffe33321e201ed9b46361578342cff7e04e694e68f21570bb684469787740f841604852306fcf245cdf006983a3371fb33bdb040736a6907bbd6933271a703a2d18f4696e9ae1ab48cd4c7c968352b3cfa2a797d4a800f015546668c813364444a2b0edcc028d94f4694fa8346b37fe1e578b1bc9c72240e8a6a74fa2d1bb0b553721e831086129c74d33fdfe58fcc7e146bfa1a731c8b010ee6b7d0018312724274abeb32d16697b73e1e817971f21c70f28738cb16f907bb6913ac72ed22ce963108466fa1454cfa25a7cdfdf6b326b1a1d213c8999c0a9c9c47c22c3b0dfcc4449413616ab63354bf1c438f0e5e6f20940317728aa70841a0ebb0cee66133c883eb1a15b9aba4ee81a0a9d5dd17f98ce58930e0d8020f0d16ed265f2fd8ec1f08822326b0f800e0a0419b2d4550077b6ab0807f95040d9ae079a547f61dcfb6c8627bf1c7ccfbf349c6a592abac1af249cdd93665710eb58819036cb50c97920c69b37e02bbc6a92877165bfb463e7974cbcb4479e20b51b6d548d8cb4cbce97e2c3105d91f6c76604d693033e8731febd1f72002c4004a1e0a7c1a2c41e14fca686ee5a0777844e814fc97cb073dd16072faf8f2ba0d40b6ba3fcc201c15f8e9821110c20bfbdb589be96c51fadc74341db9068d9f0e45d1a1ecb5fb092d19822b619bb424d781fa599ca054ea11f82c414fa39d780ae273dfd40514a8cfc2de575fb218dafd012d9e7158bf15f025bc031907ddf20f1c960254b537ae25fa8ba7783144c4b78bab2a4dfb94739549283b1dbde05108dd071b7e43bf642a9b64941aa86f7f8f8bc3d9bf601dcf330aaa50959a64fa0a8857ae6ae6dede5f222f6f29affdc4bdb78fa521afae89afffe4c745c6095108afea00ae731c24be2f92771d686f09e19bb80f06ae0005e8390d55cf62ef870670914b0a65b099b477163eea49afe5a9e8f1208f54bbe6d5482cc9566c2e670bacf72eeca0faca5fe29feeeeabb1188000fc574850f527d4684f67112671330df3fb222466533800ce6536a197bf35abd5f8b91a6f5ec90ff095e17b5950aa053471e80e86f3c09c0f6f144bc11ed5af661513fd74299984dd82cad837aab20193bff20bb9e20f02407a4db98d952909a1d37c522c121942d8104147a0939a0d68bfba874608ec2a7e0d5a9d315bbf7944cca2f4e98717e124e1ebd935357f058623673c42c7df5ca6dcab66353a578d5ec98012c554736fa7277ba3d91e6f025e991918d74912472b82596f1b38b62d8b26798e6a97f7dedf54a95ba87231012672b18e3543e49753b668826f59b347b30744dda25b8fae32129988b679de09e042c8ecaabe87de0a96088bebdfeeccf2558b28acbdefce1cd585f42d3a190acb92b619900bd9531af6dddaf9fcab267cc60c4d945664effa2f89eccf66b30ee313f8554049cf89e0ef845568f6b383981bef7a4f1932eb48b0b49d9c54ec0bcbaf6cbb5c286ceebb381bdf7eb6f904549a8f49d04bb4fc6ccee923f7b1b2023d1fcff95877b4d19fcb17ec40e0296fcef31bbf2a8a07f84d2adfb823754f05e2e58bdf8597fe115dd126ae4988d9cdd98381da58f3339f82a846412a59af0650626dc7c0c3ccdf6e0be2470700c7b58885f8ffbe936b7c00538aa6a22a620fffc20cff77cfeb61daf9c6ba595bbdc7094c230032d3f69d8c1171999389970b21c0cd1e0ca5c08393fe16fc901e1d18d3f81fb88252e762b19e4dcdd7471a00d3005346ed09ea0ae31a3e865e4b60300a68441449d5cbc1ffb9d75d0424d504595ac4a096399182ec70302b3663fd3366df8f5765df2b8f2965397d5dac360a9a1c99da4c4ca4466d8a27430d6ff848d0c77d58ec368b1c8362e6ab268ef60ade37e649d38b7ed642c101c8194900a8f0312239353747f6dc3687f98cd96f0ad3e7b8636fdb21d1a23fbbf90a67f1280dd77e3d7ceff8a08181349c0aa3620a5526f131f11cbe70682b088166e23af9028997ea73112d8130bfeeeb3aa1810580b2a9cfd2cba454bc521bf972617f40349bc6540e389754ebb3c5c8faf389006321ac9089b681c14f96804d7d458e9944a93eb230bde8df357476b02c9ce106f73a78e417994d9a5afa718bd9277aa59ec1bdc509ca0d8973fd1e6ccfe9560fcf14c06bb45c6a5e7640b0b283407eddda9a8606012f815a770edb3c12785404c3f279956649a2d43b403a3bc7b9e6948aca207a95248e8045338f7c6b146601652d9d1b880abbfd8aecd9c93729120c9a86b6bf47ad0cd003426cdc8ee9010c89a418b7844b9b0f4304ae59065f3f36d2b8262bc15372973557bb4bb491a104a185d35e7568aa951d6d2fa3ba08580e17ee7d49b439c9c8b76f3c4dd7dacc0cdbd8f7667364e103ede2ad7f0643e3b4609bc801787280bb37c3cf6ac577a604caf39f128b261d8fe88125082f36628c42ebc94de9e2a688641199a863d33b070e1195d8b27a07d0fb2c5c015f0ef04d7b1c9ab6d691c3b40617c085832d1c09952cb59d2130d322c79a273124232cd3a69a4f05e2be2aff7b6f2554a4bb5d0b46f51d392b2771b3599833c23bc1f29e658bdeed5afb5679002a14147b4bbe053a4a00b0995b1ffd91a35602cd65357076eb5ff8b833e7454a95ded41565f909d7bf3903006243e55c6c797da423eafab4da5e1f493346b9c73428683f3cb003040fad528540648e3bd5e88b2114b55a52db4b5c68e4d0ef395a13155cb8d605ba397bf50a5b0cd1577736bc80a75326147bdea2dc46a8b8daf20b1bba83690420b44aa631954458f8fee5e471b4bba672b018723c63405b89f19dad7db78604072bb42b16b0259a0cfb11ef9a7928a1bdf1ff87fe05dbe0e935a21635f99201341da502c9800df95e40896ee902d8738438631ef5f43f39effd5c6ac090860899a792f43c6916518289de2c3b0afd11f9ca867d10c0ac4dc1f2bc60a7ccff09086c3ccf445b63a71aad78db20b88f80b9166f7fe0cf12b7ccea81b99524ad0cc491496fc2ff4cc8054db34cba12b2a1cc9adc13015798f8c0de844ccc35d002adc86063a50eb9e6adac300d495183010818a2198c2acbe449f2c96c87ac380a0e1952b854b113d05ce3ecc16117bde67e2595e54ac2f4be1bfc4603e41b7907fabc2f85e475bc0af6d8eb30b17069c1fc036a47defcc3a0408d55f1b7fa820919232ca934dbb3f37ecf459e7c8474c62926cb8f958c1779324f4489d1195d0b8c5ba30bedf45bb46030e1de7e8875a10d25d86870ad2bbad30413af2cbfb70e6b2f7299081b36921a0ff40f9cc8b4aa34b6794faf64abc086f513f64f86a00f2b5b1ecc16bda7775d52a4497374079b5f626fa4266bcc1af17bf0d55be75005c27f078f92ddf2f4701aadfbf1e6d23f5c84ee944b21d6e9301bdae3a66ced05345356b8a9a02a0636439d9424fe740e15774d3e226e03473922cbd90be04f0c148e8c0d6f23aead35b381e5fc2afe9d93d4d673be2e43ee0428b5b735d0b102f9edcf2715138013bd444a50f43beccf111da40c02a43a734ce79d9e017345095ab6e10dffa41f869dcf3c502ff9ee5b567eb93793f06aad6b7c4288c25a3e0cd6746eb4e4291abdfcabff88ed389e9ed4ff8a93a4713e7cf469a8e8443d1c05236e05ccddc21b87a0325950b3c019f16d15ccfd5ebddd223f33b029191c5706505f61d34c0b21c842947fd6de6d4ed9339e06cac28966713cd50fd78c761d04217c8a82d92d29994d86e5400d86e5954bcd845b6d8a8dac1869d9a668e09900d3dcf748b8b0f6a55fe6d2570eda6137a970388720c9fc76121422c95055040cc8f480d8107fa2106e05c95066520de5cc4a62dd0c06fc2e6d281050333419086e123d0560d7d5201acb7b47b897d13d75617a622033721aaa905ba8443713011cae17af87f86d98634ec0bfd65cf994f8ebfb4fc5c6476b2231868af3c2680f312b9be3a8864d4b28395f237e05bd22f15ca9d7f68e5267ab3f04e81e966f08f72d71f8c9c551b2e4c9fbde1f6b012dfc6c3e18245537ae67e0b22053479c0910e4dd0e5245fc4cdf9fdc42574981d4430cc6f540cd6b24fa2338c473cb670118ae3330f46242fe5cfcb4911bb5207cc917fb2da0df9c2f85e090b78408200f79d88635aa61554a2de8fce3ad80b9de50b34366e4bee7e91c20b663237d7605dc68f2b07d264d53c6b69c48950519d11e83c640d7e083dbc31bef91d3460804219f09d1d6863735c02570d06b245322b56478bbcbd25315a1af0e0306126905b5ebd638db7e4df5297a37eb0fb887aab967f59a5b6bc4e076265e8f2e435a0d3839716512e3284a905c8615388a589462c7fc3798512b766f7eff17a95a730945e21a54a92bbeceb298ae372e1a362bfa8749ebdb1572cf821ee1b513594c045ccb53a79867f846b61b5d56cff6a473faebf426c19fd15ac51f44990aa1bc0fdaa137f662af806d1368764e664ad96f73a153254414886c5fe844ce9f1f780c7ca4fa9035e8294fe2a30f02ad6ba8b613c424e9b9211a7ac987ddc7184b540f02b2696b36f11df1bdfccfa90da4be9a50a92769310f4a5b173772e6013d6a0fe4e2003baf6c1d1c76e20488d12c8b70fca30633d52a2721282269b18466f401d3a96294aa4649f16fa999b476ebd2c9cbf1ffb7b952a72986c959d12d32810d1d75903bbf197cc17927e9262ea6be6472f53bfec71061e9bf69093a97320a0ec5b6104b5bd06c62e057831fb0942e0e8fbc82cfe159e809a2efa1e1f2453d02a2893093adf6fe031dd028f537fbac61888840812b909c419d4db72caf4c9a0a20d490580f24cc2bac22079df294fa6c69d82b116f75c0108bfa4f569b7f0d065985443452a94763d380775c0214155e1b8b4b2b83f744d61b260de58d34490fbf12f1fafda419e15b6c9f95c319f898e1d47d6449bbcc6865a60868f5ad870f049a1113b9a1abdb197b583bd2e644d64055144b0612ba466500dca1fab12c00b132f53cbc34dee1a5848e60bc4e33d70b172dfe994a5c5f05ddd97f1edcda05389f6e5048e86950fb7a91ceccfa923aea6c4810437e92d96c92f77436d58403389336afc6ea7ee55bfa005bd63958b298bccb91ff25f6fd3c051f8e7728270106c1131e84b8709922c6a53d286c7bb190bbd61b981e7340902576e7a04c6c09cc2b3acece70252f603dc08c89e461b184dbef74009d7e89513cb1615394fc0253e115cf607bc61e09b5f9775530a0afbb5486d7cedd61132ba4eafce4c0a80e91eabc19a0fc3d8b7a559cdcd2a826347b56692a8a0f7c8dadd4c7e3eb919f06f22a9e009dde4c8262a1a6896087c566447a6ccb78841147b77a12765e15170af1037e0254050808211104a1100012cce2db048ca4317ef52aee783fdafa98069f437461a713293b61453213b4b68081a857f0a50cdfa042bf0ab3b63db7490eabd3cc516a6af894c6f41f631c9fb4a39e2db700a0866a171baed8b831c63b0d54fd4a9f37131bba5bc4e5eaa06d91d63df075f3ef255cdd050dff8da422fe096592f4df0c136fc95708690511dcad15b99a4f4c7f6a18b513dd624974ab330da80041ddb63862e2ca9c2529991dcbdff97f4add0588988e6e45a18522165a0600013b25fb3424413edba9c2c68a93a219df8728e59c7bfeab3d1b224f714903d124ae7759881c6f0b6ff09859da459a38ce168d29940e048168e3ee2fa3f800eb4aed3f36ca5caea8f5267d670d1766f782b6f4886e9c33f7f5e4ae91704b0b71a4a707c2d8ff0e6b3f51c551b8db629e2530abb219dca484473be7ff57a30c8adba281980aa08b41ae32d7ed07f2c9e9c2dda2663d22b8c37cd1c1777ebe0e51be5209c94bb8780836155c9692aa33fe0ac1c99bde6b1a6a78a7c60820670ce5e76268dcb92477e8546a5108e1ee90aed7f3000d550b0f6b4b1acbd46611062963052953b8fbc861fe1ce508a9d42b716e8b9bd420b312f2d05047dbdc97054c09524a07bacde68ce70fa6b6b4259135fe1a36eca70848e864c225f13106061a137e22694bf1e7ae7d5f928a18bcf91a5ebf563a6c4c47f25bc6adbd23970347d179b2064c8f9045e5217d88d99f9d097e666b6cd24485aca4a51673a6c009fe179f7692f73d4531bfd808b4236713ef3b6c44674b6483e4466b8ea0f1ff0e2ffe3403cef2b63be0dea7db19898f328f069abe8599fa78abe5a22da232600e1aca2aac56a70894692ad9028f9aee5748e5232d74d9c9eaf7d38a00096c9a0a1f2f143a9024e8768e85614aa78ab91eedf2d7f03d92e7c9745b45f6b8a7a901d7f037460073323ed59d32c1c3b94325f119f6fdfdc698a469c684d56a456800bfed8eecb33783fa9f3ce57f988d97a5516a3708d8b6f890dd2bfae5e8346906351fdedcf4a319ca941b81d3a97469b814add138fc2fed063dd04efc15b03c05e6fdf96493feaf2b9ea8c8985a7d7f96caf3d21e3756a357b81e6b700d2bbf0cf7aa342235ba6cb596c29d5e1fed456529c61ab034cd4dfc6226aeb430ade50e544b228446524ee5eb554025ae5ef433ebad8beeb88ec517c311672734e5f70b1456ad838e0c4ec8b9f7619fbd8a29fe23991a0ed2d2b70bcdac34658bb8ee02552cd985c2d8140cd9228773dadc1ef9bc271b4badac7dc21273863ae304ea0ac39d03e2c94586b4bb216f920de54e80c63ab4558a76b9a144a7d967bdff310b224869f313c3d01e6d022526e562b0dad1dcfb1e4c0a02d63371e8178acace0a7c60ab03142c75833d216824db9589e605f5b1aa701e5ed47921b532a6021f0eb15f6341c33c6caccddd6bde8ec656dad8a68d40f7d7aff8df2bfe2a0c8dee09b51c2d907a530977f902a0e6a4bda867d9ac7ebc1d3460fe0cf0d4f68147fc0ed95d104b70c7ea12f9bc1d459d40b438f9e9cc2192db91495174a4a9edbe5f0fc2f6b593c1bfb45584385129efa4cba5fb906a8a70a837b99a45409c1067930e1e09e60594a9ebd718957aaa8f0f4137782c04f158613dab57b3ff7754b80d0d94e7c5fbf24183394b039e366c114b1eabc1bb9811e9ab08f2046f2051440d06cd052b14ec5cc117c7435551ba110ed27f8589fc8500e1bd59ccd6ed95d7a60eece6ace5d0df35b34e50526176e9447edceac0694b7c98ffc8de842464220c0198b3abc54f3317a6bb4f27516fd2e58839e6dbf3fa2b05a6b46bd9aa9b955d022f656bb2411a7e06419da90423319d2efaf1d066d28a218513afb780108a060bbb09057b418a17d24eb1f3f94aff24970623afa429d6ff5c1a004178d193ab0701c2d4b255be6016d2ab74ca180b9d24fde61ee272b6280d3aa7c37773f86a0665314d693ca940a22d63ca69d30f3e7da36f13a459db628a48ab15940dc886031d5a2f06c120713e865c47dcee449fd757648e7876f030b8b3e2cb64218b5d065f1f9ef7208bd56c1927e1acf37d14012f34ac798b8d36975f60de48d6f069063daebb9b6beb0263962b1dcf8ceea45f169bf561093bfee3a02c84cb2c6fd20e4b48a25f7895b3eb263e37a56f06a8482c22cb4acc165a124bb8dcba8d4cac0ed11b546a0c4b8ad00182c1f6ebd203bae00c9b46bcb62864dff693de15f2450683952051ca20a5e573d8abbc2f372c399ed964bae906ea5fb281e1384bd45a059cbabce79b083568b5c73b431ce056d2465b6c85e209b1eceb0974639467eb047eba154d40456c941c4d3627a4f0030c941c6b143a496aaffbb79408ec80d2052ecaeade1eca11f67cbcb7854614a1f12cfced6590ea471258f72507f027100829b9effcb6264982773f7872b690f0da3425b1bec583a9005fc31df038f1f205a4b1fe9d70837d82b65a1355fb9d6963f46402a5c8b263bfa7621b00081865071a4fe35eee2b52c97e41885028170571ef5dae064e1cd32543ad90f130374a0531a63709c66fbad6fc7d16f0da4ab00e74c43134ce64aaaee57ac6dacfc207035b389ece5fe77cc74088fba360476322f30de75b3ded506229062140981044008de4342bdee41f3236d9661c74004dab50b520a4402842c41df3f32c614ffc0605d4ac51b8ed5f4d9eb9648331f37266a162e04fcf72062a2307c9d1d2371c0700e8d63696cffa42d5553d965daac462e47901c7cc1e8b2da8487254928fa40abe252dee8e804159b12d5409616d5e838b567cdedc78b5276ebebcee3937a40824564a35a99f420106d33dff667695a6b1afc4c5cb9485df3eafbdcaf5a04d0f466aec5a283637919f164faae0b057515142fff72b2d54346b18470b85ee6e0899153845b7facd02c78607315996e2f29658f16f7adc1fa50cf8c6283fd0ec083b5b4f4874cb1e0a709af96c0464ba65d1d24d171582cdd790e76f4aa0a15400926d575074ce41422baf5c82e8f5fe92c452903cc1e7a338d55287e2f5db3d0b95e0a2379ede07a16b9ca102d0c38e488473c74c490ba7d448551d8c45d2b00f0a53791918c4a0ecc5ad55a65a353211b284b6dd9d774247217991e33a7d7501436b47696835de4f8afc71666afb19b197d8f221618ef9d1e8beb77f19712b0bb68e13969589a3ab77280f5628ede2a3aeefcc58f479b4635326423e061605002252e479e9a9a89341762c24f5c90c84ccafcbd2b19bb9418d6c43cc9ab8c00e0540471fe462e13667d5ed8f2a5d1082e54f6885dea9e2e12e1044a66469500867eaff6bebb0646e55ddef3eb5c635c6d507f38e317c41fdb4ced57aa1b4d107bdb529558d352d1fef26326bdca6b7693d1cd64b8e3c9a7e71380894f4d89b0457b35294070338fd61c39965a69dca6f63444a1ed420ec64aed6e8e3f0432c039c43e3ce5e47c27410c27217d8d75d950532d0808b901d18b764e2fbfb9b4e065e536f5678b594fb7ef423e9ae326efb4d42e2558461aa4569bc7b25f83e300f78f19a44efba4de6b77e5f60e4f527136125bc6f3421a935ac16d910c46d900c466030d2872efb7b43205a7a71706e5387787f0818192163b92fd0d6de11b90c8505b6a5395e95595222efd6acea79bf75e734267d4c06e63c8df0b797dd3c0bf2856aec7d0cd4201eb579487f67e0e0e2c999553031e104b8a55e625d7ee60e70de38c2915db84da7ffe61eb1bc99ae16248e4ec2529162312ca22f69820f037b117f5a3901d3a7ade36aaa2d001f64a079475e13af60519261c7e13c86160f6ed4976ba3ba93db0094813ce40d8bb243365a192a5eb5dea4968168cb470d0118679624dc15accda65be1a7454be8f831b9715c6b671bff751aafa54e23500a2aaf91ca9fd572451408081566dec13219831eaf6342fecd82d9aab260d2500246d49a289fcd0a58931ffe5184762c1b314cb6717d98468c393e16b19de24900d4adf8cc0ee27fa6ef153e79aa2d6d1ed99386c83f865dc9061b451086804e0e3a883cd4b1fc0feb1b674e4ebed09d79db856802406b2a24eef260960df8450b8d60cd56009d71f0bd51a48e0ff668a07b325c57803e51efbec25d1743e046011547d40c4c156b45d03ad7dc6594bed5da99de7d0df96fb417bdee2cb2af7f08bb077a67856c28d966ada4072687e9c596eb571887047ea24580e9d5eabace0ec32fc92e72697f850a476707393a3cf299480415e7d45723a986a060180fd40448157e167af9cadf9787cde00a3c1a040e8564c7e1bc56599ef2a7f6f5dc8b0323beb29f914428a1296fa85d84f65555b5900c4a306a62aec38b65bfea16710289b52c5857515bf7846a467cf28ca3dd48a49ae07ddbaa65beb5524d5588ee01112c142fd0cd76eae8ff0985a4d90b5a425f0d2a1ba568cfc0194891e2e6480aadb562a517def295fa149c0464840f26d921639749e087687ad87c9cb98ea700d0866e8eec9e1f1366214b043dea04290d5683cb931d9759f129eb0a7feb8e06614fafb90ab12188b3d21717a51b994cb82f2f513b5a0741c67da7b50846720793db03a846f4d7a15c225e3fa1fb6c4985a55861cb93d14b86b1a1678f31140302b611cfbbf08bb965dd5689b306ffec73af0ffc4395bfb4fd24f5414463cb08a744ca6e1463fdb2aa109460b77db657f675bda80ca13f15be7201621db76b0acc5b0fe7451cad3dcc81aadcb865df8bf0af833488a595cecfd3694b6e63d90d31156d83b049874ad5990546a50955924d0e194f41e1a854147e99d9b5aeb40d8655b1a536f3d4ef2dff579f759544a3fdc73542b93cfc3153e10204b002360bd6539d044f8e2f236c99b5442186f80c87f5ec163dfb72f18d8cc2ccf4da5001a9e2f8e24aa9be06e794c77d2e1cb829d24a07a5d140040592fcc05388e98c01f895b2f5c24f65c24045f783c5f3128da52a5dfaf6f166aa06627bf68977310ec98b8c873fb6605fc4f5383c33dda920e00f7fe6d403aa722aeab90cd4f60e0978ca3b4950d1256e90eb3653ea32b650695e9a4ac1b6e494a1c5eecc5d3d750bdce5da9ac88429777b5d85c62852b1068809550208caf5e395774c61f55c320c9490ac6c701c88090a844d4fa05b712193b0c60d0e4827ff925e11abd388c00226c76d3b42f4a86075fcb144ba2ec032cb186bc9fc5bd24dd9c0e9e2ebb7580277132b6de523000693f7b548e025e6b74508b2306fc6a9dfc580e47361256f0e913c88e9fb5c82b4c17aae13a485e340043a6f0bbd11233cf5a4b36fd11fb40be73668c0b0662fc4d62fe722896da978447393c96784b21b21f47d3495aad70ae4c3741120ee89d576526ae8e922f0ceeea3550ed2d1ce328b74ebb5bb53dd0e0289b1e2dc005009dbd220a399a5c69613eff15f9aebe243668b723553a2570d05711c09716f6c454c221bb56fdacddd6b335dcef71acf86c1853ee67ca5e103fd3c52d57982ce2858c062b18e2e2bdf4e8d2ebdd4efbdc37fe423f925c71a07c1e6e664f2f016f0d504d04fc892cec493ba042f8098b0aecf0619ee500f4907c01106b013a2bccda688875a66dc3446fc5e333770adeef93cc89c20307da80ae3dda1b938c5a9e1ebdd69c05e691a788fd57b072b2bf826845d98512f76c60c500b8db05b1f12dcb5ab50fea62293bfbbd6e4230b191a44398132b6a910aa03626b4744880c196ca2917e76a618df62c8b1c39784fb406cb0b0080ffb4cbe0285c51722b9dada095a1f577a05bc24af1d29fd35a427ecea270bf480e6005d0e3b9310ef6037703e6795728c27b89ad5e69c66acb7ab13fc292966e21fe14d02a4cca0ab8c54713484d2ae38819badd70e17ad876679ae55edeb9b382c441204fc616ff677f8b875eaae7280e24304d0c04e324f69dfea38dbb167516b8e2e0bfd89f923f8c978f0ee1c2190dc0ebeabb45d171fbd004895280521bad450a70d2f9fb0407f01b4cea3c4463c4a8a71fb71a133038a45e7bc5660a497939ec50e497aed9f84dc866ac47f90864d81555c567d6162b76a00cd02e4f13e5ab3fb0fc72662f8f0097b618d400332695a44f30f2a56949706aa8452c40edd1f20610eb4061c299366ba6714e485c6db6272e15453767d0f95af4c7c18e5efb1e1ed0634d725725327ba29b9aa2a2cee6187ef0b34305877a496b3a704046097efac00b0eb308a2ff2e3a02d8e6940bc818b15692f51a9ee103d6c377442e95edfd8075b6e87bda519d4cecb1c4cae6223b4f549dafc1e05c6ca6ea25d398f679a770adcf916bb77608a3ceb7444981db32e5072896a70e5ac797dc52a72fbd58a54000b97001ddd3e97bbfca4a30636a17a14ed2af9b02985842ea07151ef6ba24104a4a29554281fb188e58f334f3c6066c229eecd3ed20f171d3c83bb260d63ad004027b7131fc8309d18bfc57b01bf4cdd6f1128b913e369832cfae20075c39a08058cf5dd830bfed7e121e9affa14a7bb431cc684b2eea998333b5c88f8b82e0f65b636211f8328c8cf627f0d1b40af237458847bfbb9cdbbe81d53e5d3f46503bb14a5414fb1220b75b63d316974995877b1b63e3e9df300cd8a9cd36ad4a9004dd7cdb7adaa798b3be2cf566e18ed7b6ce65b84eac7f3c762b597770d527b0c33c7ce5eb41658b7692539ee41ac77b22892fe177a37b92cb96993cbca4e3108702fee123b1baaa64fe60180f72d9754afe9f4f263aa7447b85f55abf1f8b008291f1cd2c5ae969e2c72295225d35ccf6a8faa5322dc14a7b60771b17fa6650890ef1ec652abeb59740298c8440bb815b9a3a39f2dfc5c2885e73aa79247e506cb074c2b96e63803bb7d401a9d96ae9ae215d2bd43886eae80b3c003ba12e2094d33cb6e8f9a2dd9ea38fd07274c2d632eb0ec35be17ef086f48e601b2c3e00fa7bd65f016cc070159a8fa2e2b587ab0493931e00279961dc1a8689ab98e4f0b1b7ed0ffaa9c5a397410aafd547f2f438df51a649c15e767ef89cf44e663ca0b4ac8ab9e795dac46eb41ace9e940a45b476bf827982883604eec98c1ab348b018e336956772b687ea4eecddadc7ee5c01184ef8c00607698f6994d1d91dfde061dd02fe1a750ab4c86b2dfde5e01f945c19458dc897ebd3315cdfaef1e28f5081e01f905cce91e9253d046e986021c10b54d3c1aa1a4dda3f76242947ee2b70af548142521fa7985c562b869766cfb8cabbf95efbe045e72376383ef55a41209ced712578c12b6708651aaf1cc91139c37b890d1ee85c9e4a7484c070863dd00ed2ea978d70a033677df1ebb2afd4f48fd9df852632d89dc8290727bbc652b0657354bedf49ef05875fecf1b6dea205606d088679213ee4406478e7d0f23720507f19d747505b1cf555d4189a6a49e44dfa2dbeb0d13c70d7defaca9468d2f0fcb95f5984b45a841abc0a0d42df6d991fab8774b18b0b34190c6a78364b04e048ea40fd14093e4050563591a9568ab509a5b4c4bf776a19f2390525a49b66702bbef693fa841bfa9e8779f73ac8dbd17839ca25dc8abf74a423c73df3dccb1066accfa7ba92682fca346e07cad67978744c53b865ce767db042112dd09a9ed0b6d01605a1932c3575750101372e4ba03a684257f1f6c9be774363370f56397073dadfcc9c812b3776b5fcd919449de92aabdaf7c7545449950c012f00caeb806835c328ea00bc99b328d7bc194fa6ffaf8b392103ecd57ba4619c7ce7195d90361ad34a7d7ee64774b5cc2f4144caa94a4999c23f30fe7a209465ac7451e0b0ad941d8ebd45989278cf6a0b87ff2d9d392873c35a1d3f63f6a2bab06c1ac7900d156e4fba93ea9a3f86f77c31f4e6590a42033c2939d08a9f6742b713d176007dc237b497c53b3ce3deda377e6572a5c14aca377d4f6e46cecd712195e99ea0ab30bdffde73c11a42e1f4414d3f066f4ac80485bb08f0e7e2b35a9aaa00e1c027c6eda6798200184dfbc915cffccc05b3252a4db744413c353cf3ae5eaa6f20ad23ac982b4bc4a0a4f838a4ccf5cafb8dffb34ad8723d6f204f291e22f4819087ce0583773f3253b3d991c54a8e46bb1f4142b53813b52c57e8af9eb2f1ce701232d12615d9feb9e39c9ab73e767f00b4832247648153f2da64c3294fbfa56042304cf53a16e3a25252af62d0ee231df862e4977899c8d6b44e66fc17cbace05e6290a73c75abb9e3ed57d5c1b543e2b9775649772b4604da119aa1cf81b550ff0fa5cd8c26e1a7b5dde27215d8930443d619c48fba2e7ba93cb53c87ad9280cdeebb9de207e7bd8ad5832483e7a722955a38ad951c62397c98457f70ba91d09bab917fc0179719fe3c1ef38e70073fe2f91e5b710c15c3718d940ae40a328087fe97a4fdd8e4fdc79120111409c8fae965e7a311ee520394590946dd9a32f0b2d9f7e82efdee1d6bd52bfe78615cd6977d0194f5b2dd83c0f13b49f6c7aed0ce8f60dd7fb71c6b9b42ace0e0fc04d23aa8e66e69103d3a675823913451f020970d0fb4e80a7d476b725259d7c59d0bf44cf40bacd2c0153d459987ab7c10d010b7864556f6d623be8ea14a438094b020a77c596181fc169cde26967ab6db605e8871f6f4f284655efbc1e1593ccbb204c7d831b3be5dd5050e9cf5d8e7d320b8b1ab08cdc39e8e3884f7e9b30f4a952d5b1f34f1c34b59bd248851844d9420eccce28f41852a75d7575e2d7b1383de0ab3e8638d2507ee9b26ccbc830c1e103159c8b15e43e7cd10c0a64f3d12f638e082e68547096f3bc1c58565c2a92e801bf81f3c4e54bcc235b568ecc51bbec88991c248a794d47a15aa5b86a98a0cd03bbe3805f57f7c6796cc9a721e0f2d5e9aed8ee12e591919c78072cb1ad570e0a6ed2cd7b0cdbbe4bd8eff2204120b4d73a14efc56a0da77e3eb9c5cd04b9650a13face013524245967c8a5ca3c3e597d2b8bd5dd49727acf460c6d7be16f310450d0133bba05a829a01e0b839491c63a77ce5573363bc2be1ca66e3777235a05fd327b5f3a71c5bfa34202b46ff1353de8480df1556e09b7dc84f7ec735275038e4a8a650f97e23cad1855ea24bf4d5a411ed72edb1fce1c52575f1249949c06ff68f866fc1cf73c2d0c3c7490c4184445de9c3224cba9f0aa9ca5745755d308420e4daf6ddbb1b3ed0968a4b38035ac40e4dbf162a39e97bb6f0ee60a41dd0b14dada73a4a685c3209bdbe6fab9bf3bdece2c812331644ead0934b18690680a false +check_ring_signature 9f179338a7b5ff7ff1b176eaa86c77c61d3ee38fc2e6d530c6465dc12305c011 1f5e4820bb814725a459c62ac543fc0dd727d28e3df1ebf4271c48492c95ba40 85 24fa11399442a82332514534e4db384ef0806a327c6468758bd04717b5e099cd a9675bb47361b8a5c9ecfe188b71a8636ae95229670e3c2e2fa58694e2e9161a 4df77e4c57c2880198264c30857f4df36bc2ade7a3620d4cc32c2f8b05e022b6 641448cbd11136352509d02bd64c46213b63b3c1a819f830a42b33fd8d327f7b 63fa807c5ba5ff63c1a71b9dd4d14eec4c06fcd44fce34fba9a5fffc107ae371 fb9305bbe30ff1b126739cc7a6b5dfb9bbf6574506790528aa04fd5d8c69be3e 64ef7a3b1ffda60588ff3f9ad5ee74a3638f520ad94d524b9888b38f9f19cf20 148fbd7adc6d5db8cc7b63bb9598bccfab112b2ba237cc0935b1058347812323 60c4595c9670bdabfd1c2694a73872486cc5ba899be5fd1cdb0f93429aed5259 1a59e4b6a6c8f5b630658636095a7303c574b69e06ce5044325126643c6c59cc 0bc88151fb34a07b25f2d5751308cb64fd3a9698620dd2cc2a701d581d43c568 080d78d9416e808d8a9422535961bc5cb30ce436a4bf793993ae1feeb61bd18d 47143d38d7bb2510959d76a4f1ce7ac7822057d84688ec8688abe872e9e20067 59d0f4254ae50158a7d388eda4f43e7010ec87346a80347428553f512644c9f8 ee9ab75a257343ab99eae22b953ea1ba290dd3157437df9f440c37b1f9ab548f b1d61de33e714b0a4919b43b2a106d775f2138e017aea441dc112c881b923337 5988a8bfbf60a35165834700d5417f8a3197f539fe5f04ce3c1f1f02cc8420c5 f528df4c67e8eff32ea1c92ae2d048214d9b67ce227cd6ea558ad851a71bd16b 7f80e82a578d3d2e386fedb61939129b63fe0069b28e939fae9b0d8ca53b3749 4ce9351a94a1928d33556c61ca5ffe4a6e5774021b246957ea2896b2162047ae 04413b34a976a45894029bab7ea469caa1fce6e2bce71f1186da8dbae073b260 8df858e7741e758b7377732a590788c2afb87e70446c81ec5c10364a4600fcd4 71b7cac2a67e94d25c0c9ed3ca72c163dc98318e3260b225e412fe0077cf14f0 6764d38ee89a3b4cc2e23eacec84430a8f7e4bd1d211a9df23cc124f802eeb21 1af466a06223e22d35cefecf61ea97cb02fbb8667855680ee39cf335ab46061d 8d6c26338136aeb5264135c44c2ae7e012206a966782bfe8d37beccf1d441ad5 847f568afc01857430c625288b1f0fd5e16d065f5ec3c614b47822e9338d5771 2682ddd6fce40e3fd42d245f37b435abce801355f74fe662ab8d7d4bb419619a a3eb46e6308a420fb6d9f7756c21f75ed8512706ed141f8bc5956f75bf147bfc bcf588a0862afbf3eeadfc13dda5c46fccc8147bcbdaffd963c414bcc627dd6a 8b1a7360574d873cd8beb108a647a15f2debe2feb4383bb76c9b03090290f663 54c0c23126e05b42f73b44d35379703b489864bb28abb85c48e57fd4e65e78bf 96af096343894d68463be0c703727877c8417c7e741909ffd9932a1f6c27222b 7cbedb9d4ead3a855af3527174ca69605354d7246b5cd38c180ca569904913e0 b4aaf7c26a94dc1059e4e67a8a726fa74709afa18f7f3dddc6432ffe71b630c3 94a70720fed5abd286f243d184d8639de93f48513391c4fc3aa723bc766ed7db 96f14c2e5e369d1de2bc55a7c6e9f1d07ed848e516332a56b7c5dd358f1145ec 2f46e150b35e0c311fe6a372b1e68fad11b923db8d956e89dae623be728c935b e4d7ad372b4d61c2d71bd59e5fdc4bd0efb25e85fa225ae83701c549e72d0ece be562fac3c1680b7ab50476b6cbd7a862845904b67317b8452c4171922448d4e 50434adca30b162ed2fc9b5abbd5b296a9a6cf20f2886faec56c4f89d2aaa940 c1a5f9faabf716ac79bdac5386eedbb0ca518ad5784576ad7e0cf7f9a26d0098 93bc16a4b4ae5ae7f69f90ce7b8f2ebe096f4c3d2423e0574afef4a4570cbae4 c506a80ef696dd6615bb71213e9f25a9632e6019422b36c01f9c5a0bb32f0dfa 3549cf43965ba60e3acbebffed3a5faf494ed42b555f58de0d887e2083007aca 901acc6558e13e63e18a10e2aacf3699b0d0c04b17c4ea4dc2eaf2cb453048bc 98d36eb719bfe0c814e437e75c6da2989e8bf3bab63178dce2739ceb534431db 205d541d80f75301c6ace6fe3925936cf69f728072011fb9b3cf1bf2e40e7fde 52e401c1f74d533da1007c381b14bd8be76be8afe4bf7c46d8c365f93aba9004 4649e1868658cc0038ea93f1208806d0cb2750bca5cf7a2262a34f721f24b1ae f7466392230bc9f10bc41fb4186e3c3fcc3c4af04d5a4b136f34a40609a68996 d542f4848ec5ac43b961eef2e23cb0351440b3832fb7feb201a71bafda57d54a 9541fffe01d4322a3b5cd5173dc1f818b27d116579804931cf8c1fbd0890e5f1 e1594fbbf939063dc85151777cdd983d73c87e49e9c9aacc83dc6f1ddcd160fb 300de5fe3ffcc2eb5c913cf18f0b2fb91464989cc548e3533c9aee49fa008dcf e0a29dba0cf9baa1666b0e1af70f196fe5a45df00038bb3aa3fb6bfeffb7fa2d 3f5470695f632a1c57c225c752827d68bd00f2ac940a832a7033ba8f3c86da46 16ad43640b76fdb099cea78e52d8b26c460157a39e478f9a27fe50cf4ecb5fb3 ebfcba23d8c0a6837f71d965cbbaf4b02b4f87eb7703c820e9ce25b5b368e430 44ad8e4a73df732c3a3780b6ef3d97d21fad3d2374b13ea8be402504cb9b4cf3 24209ce655bb09d941a68b78777a71d349a3d9cbb461728e8fbf39f6686eb1f0 dc2b87b58dc3623399434af461aa1b36ced87ccdeb9392b175e6eaf6677cea51 1468dd831910d6f1346f06b790ca12c8265b5e21c806b41be1e9e457a8638daf d0b0f32d7ff9bbf77c25881a2da33ce8763329933d37292ef429a2dffa83225c a230482ef81d7306cdefd460f2e446db9884861513ad0beb23aae7e5d72f500c 88938b8046c8e7cd99000b10cd63ffaa435be08de234df219c107e051c71bcd3 de8edd871df5b22ace94ba27fe54a849ff585e97758e480539a0e88f3f287463 c227485f58fe9b27900231bc5cee02e75aef5834b57adc7bc23f4531c3af9e1b 70d5e04e70d9daf21a9a058908169d311f7c428e70b00bc71e6fa6c75833e5aa ae47c5fdadcdcbf808fd66ff994ef316e494d7118de6ac39fae3462004c60e95 11dbc43d040998aff3c8a8f0c8fcb3eaf7e4b2d6d1a59d75eae5f26fd85af462 bbd1ed9aca0212e12af8cc4f99bdb6991f89131a840612c17e60c7e10f987adf 8556d367d101e30115d9ebffd18e72ab6e183939ff134e5ec2a3756c6f94ecd3 de2583db3384e46cb3d9daccaa21164209faaa5b798b26faecb9b71882cec39b 09a190ed76b4198fc9c299a51548274c261c34eb4a52affb2123101a54185715 64a9d6d9f469aeab6ce81a1fdfddc586804a0e9aeaad4c596c57f6d546707ff7 44e060a2c9b2cfeaf65a969d370ab86cf25b186cc6453a10a85a85219e506759 11d43c72a52cdbde898247feddd5498ef60e30414bca3954ba1b50f939a86298 bdcd066bf7392697f6ff81bffd9de700beadc1865401b9aa379ea1ca14987149 f531d7c03b443526a9ed3630fb459adccee347d1a7f2076462d21940e6b5a522 93d2edd2cdbb4448e7afcb4461f58ca6126a92700cff69733907f879dec4f73f f398c86ba7122c452d31251918c1f940fe8dc4237059272eef44496c2b03899e 5a495c1bbc74e2348d67744cb4b12e802c46c65078b67ebc734563cf67998264 52303fd6b0af270f0737414d8ba2510c511f39cc5512cd67ce976a4c94df390f 19c2175c55132e47e2aaed2bb2331bbfc5c1b64d5e17e4d16c8d5fcdc6ed0622 c3cdfb8cc00f2c121f7f30cd4d8506e3d020fdb16885cf77691bb98c96cb72019097d6fa6f79ab4e237b352713c9d3aaeb0e119765d332b3ba1c4439f8620f0ff301f1252abd29399383fc4007f23a9e88c659bd47cc32b40c2c0dd790a4330f4120655dbad167589ca7aeef9e3558d569c32fd6b5e74d5c7abab3e08421d2033cb885b3d8837fc21ba397964fb6675efd8e31348f9a74c7e9b9aeb825f9ae0b88ece5077f1ccf38e99e3dad1a3a284edc8b3c0de47e1c18f68b5f88a422310ebeeab84cab25b4ed0ee13688a00e082319ae19307eb03ed64ae5c4326511440f7ad043228451461351781fe5e4c34db3f5adee2944cfcfdfa0c15583e8dd59038b7f852538791c41e1804cb6e9c1dc6a1210f7e889674f5538b5683bb35f7403369b0ca80aef2d62a8404d681d73c5d966ce5f78a30013e83624a03bdb690f0a0766b74351360e7c4b8966a377d1e3e3855eae63efe2f462b4ba9c900e4afe09102eac6ab96ef24cab6ce2e4294148928dee1fa05b4f0d0b2d37e972f5d4910403b12ac29a578ca7dd0e9d3b1fd4398d744ad2ec8b8502fe1ca0fe025ed8930324534458622c27da21607072a76104a5aef9b103c7cb62ab09fd2b5270f92602ac0cddf9401925ff272f3157a5fa74871e3293931c61bc5b4e464f4fc5896a061c10153e0c00936e507c7c25f1ca4b171dfe53a9c0c29d17678542aaa7b7e80be9d24582e01d4887cf415dcca6cbad40e5fdccd9393778daf542f85753bfc302390ef9d8cea17d710af596d420e54d7f9f62c4a5527e0ec70e9a603239095e007e88c9db60dc31ea9eb3987e1a2526b0994c5b008b9f20cee35f6c99659d790f4a16f9a6ad382a809cdc569b88181ef3bfa062c359781628a8f4ccde00aac8066f928a2e7474f1e322b3fc99bec46d662950599e97066e2c97d6b2a5c4db39050f06178a4d5806112257ebc8881e02af075c8856e46837165d511a3a87c3920ea2d2f7775f60afa326658560208169eb71fc68456a4001c14d06d323bfb67a0c36f8546f32a54a17ebe09de462f9520dcdcda3b684a0b919ef3b39f3d0090b0506e46e94eeb8fbca8cd17591ef4a27d604aacba49f8e3cde98341a041dfa5009c6fa475e4385b1ef0e7d95e38fa8fddf89550592799a293e38461091cc9aa60ed20c8ca7619c350778ae1dcb636186f2004aec18a641a66fcfcf603fc79eae011c509844005b9dc661ce52fa3671bc3e56e4ae8b71c002e3a1214f44ec70920a9de6cb4984775c6e5ace55a0907ffd15fb3ce1058cb35b6a92132afa8ca880045e860535b8b86eb3a47e7634f4c25dfa4cee7ea11888b239e4b2ce18448eb90c70bb249f64d1fda1df3b72a35c4e4e85b6cae7ae81e212339cc6bd4753ef4509cfb4e8e662353ed1d0dcdbd8db46c3c9c346552e012344ab9bd112ace9620e03bb2ea6e1b968e1f9466a58e63bda7156f5d8bbadfaab8af14442a4c9da839e08068a3b967833d8931a4779fd5590e5108d0cebf96f9409eaf0a92cde4c19ea01485a1fc402358daf159eda68c8654c1155100e869210a8b6bf5cf74043b14508ffd5eb1525a8f15f45fa8744369b23125b8c500d285b944fbd2f1af71dd14f081a0e3f72d5fd231ca19d342ac48552cd11d51561ea4575df77cd49f6d09122046b32d0acae9a45417a61363b5c11651bfe8fde203d8257148a93a12d5574f506ee0955a5062c10d697bac877b9c31ba80dee2a2ab4d4ae383a58074b4271d30279eb9fe7d35fa3bbfaf9c2d66ba7635438ea4c132e3a237337991c8dce9ad901ef2df494d564fe55279abcd9878cbf857ad10d1791abef463bb4f17d77018800fdbdb5e11ff577e01d7dc7622b53751e23ce34645ccadb0b39505da1045e0c0f69bedc4795ba40ea6b5f2228f17ff82a20b673355a1b37794c6aff23f2302407225368a304a93e5629dbe9bcd5b8ce4d7f0fa3121964c22b1a2f511ccbdba50602e34086bb360249d797f7623a2f02b8dcb86d87c08ed47b141aaefd0c215c0c2a4cd5772c5e617e3de04a053ad569b776aea38be68f5c990c9d65c49a84530081a8f1aa077e1708bc9bc3fd248f9a34324f6ad602409ab0ba5894d2ff3f3b0ab354bbfd566cff3474721aada432ea19ac0202116d764a59501396a5e98b6c056c0528e5ee43f027d61d9ca4bbaad688a6835b9509f7dc36c5b3e3d60a11200ee1d06deb9b38d46b29399f6aa212f61334cf2f501d3a95857634cc240ffbc00ae178dafedb4f726a555276fce6483039484b417d323a8f87cfcab0e8e4e2880cf4c4ea515bce890c3919717f3d62386837d0cb86908d6e2ab6666332664ae8048608eec61205210fb68f5e31e0d6d60f89d4891c488f3c2d4e19fbcdad14ce06b1285c862f2e4a4adea09c1699e663532619da161252175857f47c935260c1076fff75b7f04486319f7533e1ac5d109344871b857e6c0d359c55529f806e1e027b4af2f76c91024b879d1b1df8869024a4e62dd45f35154e381940829979740de93b3de68d8a9f4d7ab477718d3a4279616fd1ced80309b88a309dd788cdd5075f633bf8526d0bc1d79e34a3e2acec6f604715795e0c61d831c2806765ed64031f1266f387c15098b94bb976c0e5c9c17cc0302f4612a09675f6b3e47fb9c90412472c3fd924b93f51129af3e2f5a249e30c776d4ea89aa697bb80a6a946c409596cbee89eac484bee0750f34161ae76580db38c9ae6cc644085060cc82346055584a1389d72410f443da7e3c69b67356a3258eadcb8764a46290aa322a3a5016e8c312cd0b5474bcdb35b24e8719258e5a5e17a3b446e385f1458fa4490dd0487afedec195bca0bf7b011c36d6d5f3cbac12077a45a8a027a9dccc379f4e40614b32068c81a76f7c10e15db03d6a84ce4136c160987305b65e3a9538cfff90f676ded54bd2dffeb9650a2e92bc1c1b970f73453906ff0c89c598f720e86f40eb1f9689cc15aa7cf168c7dbace951dd32db225bf5e2aad21b7adde55c691290a59f7f868f4e250d3e9535d043fdbeef28edd8e0f710214dacebfb041ca6a550e255aa8f0ad48c5fc3afb5990dcdc3fcf4aa8ec2f4ed32e08bc3685cea437480004ea9518076fffbd099c44ccf488f7c872324e671979bd94ab6c31f9b309510ccd4f530a8030f9f31ea949774799352e2513b19572262ef7ced9d4754ae88607aa662373c6bb92dbe8ec4f3afafb0aca22a366823168e80c18658172dc05660f00fb404397bbf218ed4331f41a60bc6e22297e8835c0968cacda76cf3a1d7d036746134dbaa9779cf9333948f5a7bffa8e7702daf114a20e7999117f3f6ed60ddab57d89c9a54eac865d90660de8e9f3a26281e4b72982abb8b09e8eb05bdb08475c3b425da593a435f1d22b096e0c60afe0b998adfd7896102b6989d8885b0339610e0f3851f3d1b3db39d4982cc090139c164f4b1072e70f9890d49317670fd0a4c135d2ec83218dc76bb261a17ccc39e958c9e6295581e813034a7528e1025b7781e12a505e85270e4bdc56a902a8c0bb17daf12d1798c599dd1af608750baf0253a4669b2b250211b26724b02d1cf88a5dcecba061f8d8cfe7ff4d4c610c76c7b65f92fa63d1ae9b11d4bd4e9e2656d88fdc6b0a692922cf356bc7cab00772785d163e5f436c20e2920ad7dd0c373185555b0a3a539c36494d2411cbd90b9736381bb627e9c7edcdb9fe5420eca90668317244df2caad0e74e75d4bf4b0c7193a8823a2a8367fa280473dd92f0efc3daed5b559398f0918ec86de7d90201cf7d494da45bba595eae30b69ffd7a7c2e0d4ee2fec4dc49b2f378f794cf3f07b217b9b70a3e15e8a7fe91523d415ee6822347680301db0add47c6a49c6e0d0a95cc8821780f958098c2d71e000177f82af947656457c63e58fe2cca65178d09df3300d3539af678ec182c001035ac10a3b1fdaaf2f36265d7f15cceeb40930a2bd05ea889feb575c2492c35d15cafc7c72eea1ca5ff15ab19287898cec310031f7baaa4d7f38c3f946482383d46d85c7bad6a600a307b30ec1cd5ba97d9470859f7e23f294c740aabab52d2988207c1bc0186dc3b60546b1aa82c00c8f5e10f913f728f26fd510082965b02709a261dfa4f90f89e5511b1e0bcfe43ee39bb014d1be6f6280b6e9b25189ab2d5feb62b42f8a47c0ba03cbfd8ffa7fd70f4c2063085a3f9f0c65edcabf495539c6b9c57720465f92b811c2a33fcdcb739f556002baaf0b480f4ef24e5811bcbcb6987106d66a283ba9455211261f33d96a0ad00b5175782c3bb716bd23bebcec2a928f39aff020819475ad154b271dca3df2c09ff114ef5907a56a805c2cd8c4804bd73edea46b1b7ea66da47d088f3e92d440f732e9345aa719f8e1555ece24bb20e3c71c4c06d586e29377dd22bcaaf1c520236fd08e092fe2bafff97c055c778f4d63fd70ad9dd8206b78b3ae7bd2d476a04444775b658fa454c7c02b12b44094d0a009b3b3eb7d6ee683e792781a8040e06cbd88c4d42d536712d72a82ef4f2fa85a0376b66f8d76fcab6054eded15de30f0e0d93390006c2932506a9978487d7b73287afde7904c692daea8517719a39080bc128532b9bbd144070ba159c711c18b26b1f5c75a7f4cdb5574c19f586e70de3dfe042e35dd89286a6ee5f98b87bd66c2a911ad3635afa60e898dda5753306f4dba51f14a7e6929233aa879a227a6a7ee90ca676a0f07bdb924c529680e0062dd4648b3e10220eea8d58c287063f17e66f7d4abd006de555deafbfc213590a6e5b229ad4e98f92b277ab4b7158f5119f04aa9d94f707f6a67f3eb013539601eb074673ca76050ee49aa8191607a40565101bd9b5170bf3f98d33c41a738b0d7bebad6e66c99cd4b7c02f5f43c47e4b41cd5f03f9fb0efbf157e97070490b0900cea286520d2ac8f74ccb015c6d4681f35d13ad881652f71e270d88ce69c40f3ce9cc7335f5534a3b932ecfaccdf2b4e3c7368aac243ee746682d1fd555230746594c339ec0be42a6e9e8b5b198a7a29c9974ebf46cd453add788d333226c0b9d52bfbabc440fd64cc5f6a5adaf5f475033b8533712aee866005d2c2852ed0c654ba49b2307934df297bb5bd0d244e823bfb9aac5b7e63bb46c094036093b03d59e95b0511506552de976128aea69a2cc3013f16287b52747853667c912180fcc7c901d5be74fcbc3d395d3cd96df5017cb4fc98a58d061b40aaafcf6ee8207d668a9d8f2b400710d5d9ca4b309297988122dd49bace1aa4cc77f2e89275900b13c533c898b9a8937e7b572b15c997f57f30e23eb40d1982117538e56b7e10f9494cb3041b87ad3edb9258b429e58a0e2d08d93d4838b6d287df31160cdee0ea74cd04f025f064185753bc4f63b7079f4a23d9e07ed6c6e042dc192677592069fafd08faa5633e9a3b64171062fa8c2aba8ba55db65c28b77dacaee5f471a0708b9259f481327e7fa7aca258e446495c18e497a1775ca6525878f911c16ec07a79f26c42c5329f9d97f399028d00af9eda1a4b25913500f7d63f218db609e0ecec6dbca37335dc1999bf490ada3df2ca05bbccd03326fe085cda25924826904946619486718430fa591d120b5867d70be2507c87cc77d9f5c8a38d9c53fd3071f0492810456d9408923bab2d3cd634ee0295b9204059d895a36b6241f95df080971579254096d526f4ce0a01c8ea784008c9e92b158647de052d44e102ccd0ece2147ca700f49099bd1bbbf4a8c2c4b9b61336ae8cee063cd29ea279276c1058f189198e57c30b0d8e20b34003599defc3fed51a1c67706a572e3d20a750f04e68f3fdc83911f59ff35faa3424c4215655ef135c0a32a946a4a37c488990b05992383ee3d0a13cd0cfb2bbe9cc3269db541191eb2233eb140b08bd54e4f07050bbd9001fd30ef7fac93037c40efeb0fd0f1eed475d4c6ce1f003f0b81efbb0d8346435ab26147a56b6f419dcfa5c00fb73a8bbce44d7b863f8bb17ffcb86b0eb8ba296a608904b57bf0c55c6c95471405c8008b2884e251a497239a590df706599543709dc97599581058472a4705f2ab636d0aca5c375ce96b76ba54e5490e7b4cf05409e65928f054fb5a38577d9e584460d36c78f56ddf932898fe80d40f9d55d13a3d515a6b5d04bf80a4623dc69349d817b7de1072d3570145a3f4990cb26a9df0853a72e758d75382c92b0c1d2441796e637049de661ab21ce3d69c0615c5a33cbc8db0fd5ccea40f35c01eb70b8381bf102e5757f534002cbac0160f379dc627d25dc51c78e03baf049c0b957e4274ffd921d5d3e8dc3585124c1c0ed35a00c4b6255dbc206d46c26457bac1b8fbd1aac5498dadebf185fb5306be0d52cf0dbbbbf5cb8b66ed7c311f60ae90e81ecd9cd91dd8657d752d583933000cb517e75630530e2ec589c7f6c682666f6c3bc8f6e4ac95c978ef2042c575f9005b1162fe1c295491fc45ba2cda2c352ba0b80f32dad885472ae3319bb9429900861ad56aaf39a6cf8e8d788bad9ba172ba009d14f2c2231d446662ac72f55e0036050b26596112e952e6d3144868135d6c74c276d24c1eefa9789ea9393a8606a74afa1c4d6315ea32682e7ab1c8a8c7e0f13dea7e99d2049cc082e0188bb30c4820e160a27f3c0b2b8348d13c9c1096239d3df9cc3696c63d8f61757bd608060b84b79864fb9c88478bfa04de903de5b5e2f18f1a8100d07b0ca3067143ce0374989deb26014dc199140e8cf9e12571cf74ae38dd75f344020af1c9bafb1a013c1e924857b195b7b5ef8f10fffd06b867b1c0fd820d9cf248a074fd9ca40d0067d54cc2ff79ffa95c1550abc22947b1180eb7426e07a77d7ac00cdd40566203cbe43012fc00d47a26f60e1e4884230cb0c9d4f2865fb07f77ddc996b1c0590ac26fc0fbe4334b84863858dd63ef87ca889dda5a7e3d61febcbad0f77671280054d6b486f07655e8a07e048f9ae63ea6480d82fc51e46348eee6bc8848b3b90ed3b3d3369208719cd85f3ba8ddc844f86b2cf3094207434fd110318f7ff3d006028cf558c3f0ace1309895936f9687b00bd853db8a009f9b27d5b731ca417809ffbaf4bea6bad2bb093da7f54ff59e8fc9a4bdf9cb5cd4c9481f759c75c1fb0c647d3af04548490ed95f7eb8f5e1265a81e07287ae65b914a71a3a559ff1a90d52b8444d2899fdfb27616d77862883b7f08075ab7130d0c6c13f5284a10a040ad4c1fe5bc5e8f6d233e59825d4f06c1016e2c5b8cd21a978c4548a7e215fa5082b4f104eda114be056d814e01e1d455045871f024f19e859769fd65f477d9a09f56ca7b3ce82c332c41774abddc81238fa349e858c162e1228e5810ee63de00f99f7768d4c23ea218e86bb460e46f3ef25caa5c155926338bef2dc54a2c7f70191c993fc9c66f6db708e9fcd3bb948eee3c62e12c33e511f99e683339b4f2d0e2582b34b605aedf4f8600349ebf8ed5d825d6c8be25d9764090e807f8e5337012c075543bba3f3610c9058c6ac4ed001aa0c1fba3a0a23ba07bc4db44663dc08b339311dda3af38aeef2b669750d11e86dd42b792f8aa7e8aec1adbac86df90f6d5e5deb2e987615ab3d24388d98c7156bde259b883ba09dabc15cd85a33b6014e20b990df3f1189f33ccb7a35d5b1cc5cb1406dd23eafecb228cde262f4970f false +check_ring_signature 067dcde2b85965bd74ae58cc6779003d0f69f3f625b2a4f4f4b2f6007540cf9e 00278a468d1b0d0a447e7fcf3f6965a2076d2537e20d165cbb31e3759ba78603 176 849c4c387c8078b612ee186432ff53b88068b1837ebef0e6dde426b925cdc6ee e84c33fb7809c4711219c7738586cfc2966b98901a845af047ea1a2e8e1336e1 5f218718a8ace9a01961745886f61369e0f7bcd475c28a1f54de33bd1ab79af3 ddb4fb43999bc1866df71a08850c5f3957ca782a39ea1e6040243fbfdd9d4516 477aeb9d4632c7c64c1d8d29ea927cd7f5c17807bab5da36a5787ffe2279a547 bbb11e0269f7d393e4918bc6be6586c2d531f420d24bfc8d25a7b801a1fcf0f9 41fcc0eedee30e11d7e1f713b48f4d587c79b165ac6394a19a3bcd6d03e2ae57 b7b68c0e4f1efd5b260861cf83389a87e15057e286d4f3611bfa988406bf6407 0d411a7838aa97e7f9097ab4cb6474f4f0b30a02639d7f98c4ade028c66d70dd ce1dfc3399d1beadec3c33bb28c190cd21105191dfdf3347dd3d061f14a1f55b a1dd93bbc101fab53aaa1e64a9f8c5209e78edc8eefd581f29020334a28af8d3 05873e843c2561e9504303313feaf5177358e835e6d283c3ed4cb70553205a5e 953194298b4901669c9aaa71baadc466fa25545ca75b3f7713e2e6a14d1b7e6b cb3c15197d9659146b6b640b6eea17cdd39368a3d1be6172f6b24007f9d39e39 3e02b91926afc74d86f32ac644ff6f40d14b7a1c05317a5c44fb14e2d4220fe4 c1f119c23947743900dac5bc6b08d196a6a6ec8164db70ea18766fd84afd725a 79fa5042d24e271401af91e94a4341816146d828b447cbe75e8a730212cf8ae9 dc0f18539163764d9b4a983e45d21f6a9dd10afecc64fa1cbb089b535a09c1be 0d170b6423c9bea6254b5cb2a0de7d44a2d10185a8bb30e59742702306ec33c1 03d87437d434c283bbed22344ceab004df0551fb4141cb0a86956e019b4bdef0 5e0e7df9c4b6345136459796bf4a1fb40839637b9fbb60bdd2c193b67b8a4bba 9438511c4b2fb97f0106ba0c5835ed7c4360f5d2b5ca0d35abecabb86ba06567 c477c41d7ddccc00b434e4a4c8258be489d07c8eb8e2c7468c9e999886976fa9 c6ffc9616aa16c9f1a790fc7ebc8a08648d34f8b6806be050678aa7cc27d369a 01d0bc0fb949b7a45168a44e51bb1a5d534a4a089f6a913736852a62e87ce9f1 fbecce45cb40610a00b71a2e592fba5bb18da00aa4463525686d3887102282a9 87327e916185b3773502c065a057954ccdd45ee90fe37c5d76473291add8b330 031372c61599f703da36ed4135bf2e1a5a9e001dfa00ea9c1a186fdc43d906bc ee8dc66a7a234b951320b32d1ace0e9f607ac98f6b6bb5799407d05b388b78d4 7f45196d39b074a5e483b0d1b2343a6df156b71b939a53ebfb9c47f4b194f213 8907bcb84fc651d8367dea4cd88c38ad8d1c530356acbf7decb269b1b53ee5ba a14dd16127f4d53a1e069c5020fc094394207c96b4baecabb44cd1b73cc67077 703f843bf49d92d8342e36faf4fc61dfe0f6bfb38de8c183d3c3e1fe24f1e701 dd4346f1ee1c70a38bb751e253f41897f42d9b18b9862d1bfd143b1aeb169beb be4dc64f35dfb99c9ac22087429cd597418a79df2cacaff6690d0d8f94257fb0 32ebd20655e467d8f186ab21c71283cf5e61e55b63f146c9e352fbecbeab0d50 94ee6d567836e1c36a4c16d3d94052640382fab954941b4271d051ec37ed77c5 3af47da06988caf39a0f8baa4c641653e492a2f22299066d53674c18ad331449 4e4eb497a9269775fc096f764193281f4649fe15eb28a2220b6ec8b352af265a 06495b676fac5de51b6208aea8e5a37db6774967a9da8db6c701fe99bafacc6f 64db8a8ff3c8cbea8836a812ab0cbf68ab759cfd113ff54d951ddd3b12450c52 39583e19ce9b7ab415fe6bc39848d9400881b699686f15e11473474509c49142 fc6dfb53034ba5d0b7084d461e257b2f60da0155d86a4c69b5d7e6a45817be11 d69b2b60963792f40079ca8ecbeb27a0e437b5938031fd375c01c35ae46b4024 27807196924176a078d649d16491f7089199f415b16e5a82e8aa79da9045caa9 2566fb63e79099a8d1c98a50b0f68a339ac7b8019f15cb81c1266c2aeea6a440 c732b987c23391015f5b2b4a360456d8d5c5179277183b3644f4cf592d380854 a4cbe283bbb8eb0e26d20d63fec3e07cad09f0abd94a51f7a15c57174b508e89 c0fb79b9b8a5df479ed34b7db20e800b387f4bd5cabbca08ae3f2777db6e6d9a f227e033165bb0777a0fa3b184ef4091733e5d040157f41772436900fc5dd8d9 eeb8d4cb232f0ebe2a8c4e0abeeca1e50b3ac13636daa533ada0663af43e96d7 8479f27de965041f08f8aa29a1f859b3025c89709910a12d1178d5a66a3cb3ad 815ac98655882d2b870d21ffa752852942b6f928602aa547a72ebacc45ad1b8c b4f3eaaa8013f7034a96e3bea30c36c1474401db409a8d409d1a60760e073dba a85b19fdb9eef40a83baaff2a71c69f04ba14df19ad8e32951997ec3a8a72fc1 1ac76bc6a632b392f7f45251f1c6a7ec1eda6bcbcdba30cf347bf84aa5841049 500916fad3f1b9780d2caf0adaa5a29e2986e1cf425fc150ee8acb2deed1947b cc2c57c1e5884714ac6ad31279184609c87860042279a3e2520e4d1f01a2d4ef 22ceb71db4158a48618aca42a1e24ba90e68902e2e47710e260fcb45d500e268 731b930769d09d2ff4051fa362f5feeaea394115a6a62b95714476431a7b4aa4 8a63b6d45f1f29799531afd30814b71dc8492b3dfb07740215d45d45cc4b6430 4f2010db86d0cc3f5299b9f1f3c7656dfd8973119a8658e7a68e7a0f71c6feb7 e6a1f86101de7f28b2182c5487fa1c5fda24299043400b2c6c5317de45b3de62 cb334ec25fd54221e75a76e9395d12fc26f9d1e226e5cc0d78968d03bbd9ffc9 62e4a5f92344e5915550313a4bed4e4e110d31ec03ceb2f3a32b2afb5b59e4ac ef99bbb565854544e1cf53bf258779520757ed93f0a84dde7af311896a151333 f95232a0f1c944c1b474ee6e22d0c98c616749d2c698bdc5b044113a6c43865b 868d8df95bca20defd2e6eda2630c9e5bf901625d3c44014aaa556af8dcaeca3 899807ebb8feebcb59dfb13ffaf0c7fd1365d34498d4fc05b52d788807f709a4 fd6571c2332d6c1fea3f226db09b5a0808a86772be65b6be5f0a6e62b62277c0 9de0904a8482d50cb2a5596d06663c0ceaf01511e76c7f4b6773bfa42a409dbc 0c7440e7a5933cd699e9c305b145be307cf306f8514ea179cd36f259b632505d aec7d99af1f55b58db29523238fa02f773f2b729e7a0062629c3ec91da6680ce c68a592763315567e0ace3dc572af51d9e4caecc49293833ab1f38279f720e71 96bf76bd422f64b03c655386ed0bad81bb4fba24a62cf25671e470ed7666888d 89d8789fe1b70fee1ff4a9c14dcaef16166e2fb47eb065d2073d7797cf97f6fb 10693858406b09f0252c1b8be6874de14506e864bd2c0c157b710940dcf90da0 b955c18f2a2d0d53b165ed17cfe846ff2d19dc0494d29ab5844167763e607fbf 5794e641396ba0d612acc41468ecac4fe1c951425f7383b1bc5f2ab15c132f1c 72787c7ba0249c52251f923613918a6d8012e271b546e655ed7fd94d9e547dad 9a8413f9ececcd3178b4705b7d5e64ef2e121edea523ba48843f75383ae0bc6e 1047d75fbb32a91eb661b6c4db4441a93bfb387ad9efb8eca97464f3ec1435f4 2c1e891e59ab32f3497df9b56e2b6910762e022a2aac7e1e6687a34e7b0937bd 597816d3e48b8375a88796dc8d5bb9f3785410146ca281be6c0f373f4d43fccd a856eeab67476bc412938478723e33b9671ae57f9da8c5dc07d5f3912ae1326b 4074721caddaf8fd4a4221541fec5268fc4df9aff11795c906f006a68c4a6595 8391c7aabd08c4f5c6a2b9a27b17053979f4192ab18856b758b2e61eb39e5c7d 0f4480dace757ba43f28fabfea90bae1dbd33f3db5131cf01734e9622b52206e c25fc1df2f836b1555602e45613576cecb9dcd24bb2c4301863aa2dde3379756 9848639f15e384406e053a3a0b176c3e326f6136d1f5876680e89b90323cec41 7d9f6f6d4691e689a6683c2d0be07b00adc3ce46cfbca2940ffc186c44ca549b 199dc4fbc58b12260b332b27b5439df3cff2743bc78b2ec68f20b4b94d5ce05a 00dfb86e93160bf560dbeeb89ad02648962e98040f88603fe2e0143aec773a5e fa8ddef617b913482bb2d485a44333f4b83abd3b0fff7051561098ab435c8fec 4edf6436b339028dd039fc1a41223bea9e3b4856cb88040b9c30f903504006fb 390619f0bd73557c58d537c9fe645b5bc7245b491c499685a5f2b2b177e07a62 5bbe24f86a5df08917519e71877b1a70b53bf32633f99dbe79d1ec395f88d04f 6f2d5e265a5753506a266f64e1f6a8af50e31d1b523feea2540908887119f931 882247da3fa14363596d5ad39d7654400cb7a693dfc998a4aea61486ad881023 c3f01c38ea80b671dfd2ba14200769e44abc3024de27689362685677c70f2bdf 5235c79239ad20bcf7b64aad5c7fbd2d68ecc5deb3ffe2430a92e5e573f82171 f727f6bee817a9752cc2ce492ef6355fe90ef4a096e51bb7952ec5ab667d1cc0 26b7ae08cff90667408f7e39060e69394771a734cd01f33ff152a5566c763699 ad9afd256f53c58b0b8c2044d20be97413ac8309eefa5a47e0f7ca00f43af1bc 8e088e4454344f9efb9f1832fee6510b1a2d3d857d97c3337c0e69eb7b7bd6eb e6885c9b393e03e0897a0618ee9d23265698ec8f821e91ebf4b4509ce4f76072 f120ec3720e35eff30b362e7d6f805ec2d11e9a4fbaa3341e12e6eb34d5b7eea b8895c495c530c788149e34283ac0c8d7adaf9b9db37ec8d8c58329256163a9d a480815de756ac8984b295c0a5be578342765fd7fdf9e940fbf25bab97b170bc 4da11c002ff1e37ea27cb1424d0938d6482881fa77d6d51868d6e0da92eceabb 2b473bcaf2aaff29b9de3621f4e8830894f2b89c706e7a1b8a5ca0cf8b548a62 8039ad0ad358216cc0a157719093704c12ba7164d5b500b84aed3bf5b50ff98f 7550a1b24c50e7a8cabf752a3e5f8fc687cc59ee1def738a77e3bb1ecefbad03 ede3b3f58bd0b99279cf7cfb54a03c5360ac36d80e249e245067e720fdaa7779 08ae2dbca1336525d392bd1cf5253805bf42e7d7a12dbc3ecb6efa06b0a991e8 2db979a4169abbd883b283f1ef440c77edc175efbbabc26efb0c947a7c0e640a 94831c833c68094170e23b15c162501234b5aced566024eaddd4fb4fd377c795 ce77da5c5a96a2a89a730db3637967e31594b7822ae99c6ae97a573a631349bf cdccee81a0296e6dceaa57dffed61afcf7d61193a9a1679ad5fa91a151f0e2eb cf8dda376f048ff09ac5b5debd97115d9f01460ae9e78b799483196e6994b973 ccd1d2ea1c8cc5126e8476aa3a6de361965e0ad2083c52961f432eb254669104 2e89db115251fb3bc00126e187f052e153abf96aee06ea1b6696e97ca8f3ca11 ceafee66713d8af85160806ab9a8f11d6fca7e8a90a197a95e57b4694e82c160 88875a9dbfbd64fc87551f097a19c68ea7429dfaf917c94688d9eea7cd3f92f8 9954147bb510ec921da3b94e2084c33e753cd2e8b4176b3979b1427846caac19 220be425d8d4d0c1cec5e624bc6f562bf6face1d5a41b002a1d1f32d3f9d5735 1f75f7908afa698d3d62aaf6b1a7cc45253df1b8f923dfcef67a71bb9f7b8385 4ade63459fec0f686ff0b6aeeabf2600a3fa70ee1dee1437c01ececf91788cc3 d983137c3fd7df8696527a5ef2a5dd492b58aba173a620d94e6e0da5fd2598f7 56fcc2a698fef2a9c51c982ef1ffa5d06674a79ec34017d9916f7b3a5f609e51 2a148c4e423dcad4998db71f5d1bcb05a117a24dafe691038a527352c74e33b4 05539572b1e9f7af8c453c606e8aa86a2e8875e4b7d7fbc1be5ad78dfb14337e c105232529f087cf3b37cf4731d10f234e51827fa5defe03042b6ff3f3a354ce 523496cb2d0a99194da95e8daee2bb3584c80b9fa3f7f34d4471b72ed6538f36 8aa20b156e181f78c579170e767febbd6e8e7fcc027ec580397dd7db2e21e043 ae5ba106bac68949b5b1d1cc0de3fe0580db48451177c81c87e7a166c92ce0bc dc912fd8c8170d3b9783f93dea30a6758901dee92a4f9bd7e5ced5abfe887edf 0dec2ae5d9dc041353bcd95e5c3f1cbba5c446c9dc70a7dcf52d467c12324e33 e9b09585788a776be9bc0a0a833b16c302b262d4373c6e8922c6cb01632784a6 a70c07ca79b51b4497dbfeacf3bf6dc0f4363c732adfb64aebec842f06e269f5 c82998b4a016b1205dd07bc18b13c86856d393a85597145b1d9807b4f9f7de55 a256b56cc3861b07e5237e5ddecb156e1f89a73fff497c1556b4fa5a8ef8e55b 03f5664fa0211fc35a3ace6aaeddcf2e3bcf4434a3dab0e87faa5989f1f18ad9 e8e99386501b340c9ecb2ccb18da43c4122032ec97cfcd2cc64902b5f23c3a77 1310af5f6f6dd2f6e22dfb6b4841c8db1b3c845b574a879b0754860a2dbf22ca ee2f86af8fccb1d3f91b5c2e8b29f5ba707c13c59e1e78336297f701431d760d c1b39a7499ac3cba47bf8484af622073bb55ae62a177f75192d7b3893266fd26 5cf6e2cfa12ab9b02a985505a86517112a2008bc188e1a186ce0ba8670fcc0f3 694731b0ea91c9fc2456416f30f8fe7ba5ef3ec4553d4d33b2040b4d46407036 666c20f064c3cdad82abde22a8db3381af759eb3c4943d8c4912d8ccebd1b947 cb64b1f9ce57bdb58b7040e5a5d31d3699115b3ab80da392e96361541f398f46 81995826aa10558e6f907c7950b9603dcfad1559d940d33320518910f3169cb5 bf07be6e550a49e8955e053a8a9f14fc9a61e32d5c0d422483af1792f15c0a6d 59b95327ef4fb21ba70b0cd2bfda831f1e65663ec2670a8250d9f72e2349af01 3801ab203374b4feb6adc575873ed619d6c1343828b72ecbe54a1d43802cce29 cb774cbe914c2231f17bd077cd62042de8d5231294af3e1bb89896dc13d6e164 18837529b3f698064edd8f018d5a03b2fa3fe581b052b2bb223a3249052bea88 9b2e804217e2c5ff762330d698c9be707831c8e41ec5a329b68a07ee5239d01b 0a759b4c7239736ed65bd4c5fed31df4fc28cd6a7833b15a94cae74dfdf3d902 878aa41fb6d8fa3dda2ddddc66913b754b1e5c24d1a10a5271e8cc8cdaa4fc4d 13c90737fe7f4c1703cf7894753ca06e41f46cd7fbcc3dec36934339c1154720 cc6ebafe4e0ba29b1158665f4a2ba20056a892bca96d48c05791646cde4a2a3a d2bb5fbebc554ce66fa3938f64f116af9caf6d578e3cacb0be9c1338a3b4a53b 46cc17aee24abbf808f48bdf635e3eee0ac1ed396c6d8a5ee8677f0a656dc94f baac30c07f3e3c5b6037e6e7bea2d94aa1c9f2a20876f3fc838e7bdb6f25799d 93baef80ffa68ac5ef881f6db6ac20c1005a9a673d267bc4bbefae07975ccd69 9e3a00ec0b1928d5ea96442700944696b4e547c0372640b88ac2ce7a1f5ab5d9 59d22a928d9fe4ea6d6b2273f08168cd379fc5d243d9d99fd79401a4b6d235eb db6c6b587462f0a4aef405a8088cd9c426a5d4e3db4d4a5bb17953d35b5b3254 83511591af499fd90148e68f2408e55456df4f616ae6ccf814388b73bd83bdd0 a17dec087c4d4cf058eebecc08d0024747a04c111f0c3e96429bd137e2aff4fe aa24db1c20d066d2f2cda280783d9b39c935644d7f8ad0fcbaa53b5678712bc2 0519d6d8a0f356148cad149748e6ddd705c4b36d9256168f271b7a1ed7ea348b 6a60f1ae4c5b6909462be84f6398b101a8c4650786f42a07e0333c6d9dcdb07b 8638430e4864c997f7bcaef185bab3e26db0d880cf3b79c2d73a9fbf03bffe8a 3fabea39a0f3427d5433cc3d6d6c2a3a9f8d3779d782f8597d72b5bdd4665e7d c29431ce4d6bf89084334258bac40d86a51b50e6a68d504bfcd74c9ef86b360441615ea589c12649fa7c0d889c1ee417a4540f65f60e0078aa1ac6f71323dc02c8a9bee5eab9acff65200d6b363a719c896c632bf62fc7035406268fcd8f2e0b3d4b430a254f604f9733c2fc4702a35d5e757d5370bfc0c52bf79c75e6db290edc3bebdb60b97a5a78af9c89333ccdcf39906ad6f7c060097d90ca1c4c9be508d3fe4dfc91776e90aeadb7d88eb5c6761acf408bfc9de097e39737a4b6e6b80640f34cc39a2673b8783b720fc5a885e3b9cd42eb986306c0653a88bae73fc30b94103c78983ec8f5b919d99f4485cc9ffe4fdfbba600bd5746a8ea419f6701099de7171e16ea6295b9118696e915c9c9134a27aa1a2e821128eea9c507ff750998bfa83ac52038003261013d23cca7b48c1c83139b149d9a49c89d5641b8560b6c12e61419967597eb1640b481560a17108c42e71e2a3e7066d8404613bf9b0f22e12b70b1a1f777dad4038cd1a2234a68cf53426f6095cb8b2a66ef75a8370f821d0c961af5eef1836327571d2c075e3b7ff70b1b9deec327a8609a3866240fabf4fd4ecb8df3615ab99897567705e1e193e233b9e287cb9c40a6ab7979010eccedffdc50bec6187cdae3aaeef462496f8cd5b1e2d129ea9162c7d63a2bc203c3cbee9fa9f6685561c357577609f9e4ac9112f009262ee309cd8a661e4b4104eb4d65adbe16cfee21b197d019d9bb96ac85de177a47db37b432989a0adde20a1e8ce23766da659930d6c7fb9952716e853f3ef00668cf32fa155ac4e53e850afa8d0a1e78fb7c5aee803f4cd9a7748d57681d892fa142d9ed0f2371ccd3d000db8d28d5299b01c1205f70a3f6af1289d6f340ee80b500fd81a787f4db1e3502c0bb55ee4e5e79860d52dcbe68d3a7bfa04656876c7143b97eaba177607c2a08584dcbfd803648eede04dc822fa745da3826cc7cc67573fafe3d76b842eb890d8158d67462d2e83ecb3cd59650a0e2a796c95950cf6fa0380cab7e792334b90486c716c8c667fef6b9cb87ea34e99545d5ec046d89146dfc9883f9a7670cbc052b566d495d0c66d8068c1511b4be9fae76c4da0c48324de6c069040c2c5e150e16712491df2271bb1b46403f6639ebac1acf572754e87cbd03dfb08858fa820a7665bdc5e759f2aa8038537b89d827fd0c8182e9add21ed06389e18f213979077a19b303275f5039cf5cc1c8c6a807c25796ca189eff7fc061d9ee87d33792038e8f00202f6c64b4730aaedb998d3ebe4ee7acd5f8613ba1dac14495d50c3301186ccc09806656949ce2a506ae5f9a33c2a362cf44ea7d8357715d963ec4fe0614c11355d36e3f5a5531853fc7b610612bb72ed40fb85cc0690767c4eeec600576357a29753c94be0be29253c762d669585b165681dd23670d21fdbe70574c0f8ee83830301f0a18f989a5fa06345c43a4014b70375b70bbb443a54aee40d409ea3c9ca528b64934f729b3402e6d7aba87e6d2c4bdfad611531bb6df1d5eeb04de937341d70b8414d221ef6a8f38c3b8e153193a8c3d872137f12d61ebb7e9054db954f66ad9a174db27646b858f0251843ced9931483398198cf8f2e6d5b700af29759930a3cb8d8ae6abc73f57bd7e4ab932ee945509bd68ae7b2ecbc55a090184e2ed97f4e5f2c08af263c313d0eceec5671875d3d24d19c80e7461988908f45433a4d806ecd871b200f664c7693ca2a92c75808ce3832ff1d5c3d1afed05efffd7f9bb9b8bf5d3055c67a65c9e8bf8e0cc5d34adff2e8d56daaf731c5209beddf41e3f0518812e3f86a66761cd8f1bfdb90de065c1e4165c8bcb627e1c0016dfafbfd05c555ae66c53831f41a7a8327d33edd673967d5e0e161ef83cf201785fb174928c7e7293ae7d40017cad4f27274526eb3af8437bce481475dde60072551ac728cd40492ace820df2699220e2a17252a12d3a77bd06362f013e660653a77ce69bb27c57b366bf39a9c50e0fba762c0ebfba07464821c66f9bef8d0c649e8e50998612f9cb05e2c3367f9f6617274ac3799074e9abee995e9bd2ac0e9138ad1421a20a291cb95a7712ad5f42e7348c10b7a63d022ee60dfb8757d20526e3454674a2ea804c679d875af3aef969eebc95731c1f0426c60bdca0e49903b2196610bab5dcf1a233c66109cbb21bb6f43696e892dcae97ac1766d4952605dab86216701a040cf3c41112d1add7637769fbd933408294cc9491dae6119d08f9054c9123fa9dd1486fb3e3da6c5afc6aa96119a0fcffe1ed3dd6ffb1de2e07cf8f3fb5d1dc70d205e6393190ea137876ae992a4463ef96c06dd87527e96976274ab143cfb3b2c4af17246d804e615b6294e773dc68612ea64626a2a39c6a0f8d76bb187aaf75e41e87bdd865494e25debec42eb15cf69f35c016f3634a2104a9c47d3424fb0c5d11d8e1daf99b0d9bb8aed22a9c9ef5fd3b401a7f90b0130f34358305f2d3cf35bc0b372981ae6f4f222ea19c39988c99627c51a1e96d2508001d39b3855abdaede1a15213e483ed377a23bb2a81bdbc1d297e417c38da4036351b469e2de1d353ba79d1c3ea88db820ad7286d25b417772bad8b7ac6f28047df37f74b07abf0033480a755e3763493c59db2dd763107dc5ac89d3814899083755a8cf3ac076f7a237ea246a5e12a1a880421361dabf7fe9a9db5c1508a80e651be064f208adab29619cfe572ac8bf17ccd02e32c801001d1fa4259b782607234976a7f1ce8e8c1fa09bd83fe764d6cc93641e33da04387b5fd34890a63504546eca6927ebaf79008d98cc9826687e1c93b8381ddfc5abc731bc6967a48b0f288d68759b7f0bc1f5b6e0ab6187bca10487fa5bacb0d5cd46a1b089d6fdb004257db377e1f368881eca9cfae76dc19ab19eaf41ef282489f4036c675b52fd0b1b314aefa088789ede455a00a5a2a523e456f0dd7552578467770bcd589e060a4e09f199a610ec7ebfc221f28a5e5aa9ebe655de604c196ae44892c85d83740208694ecc81b103cf512b5ad0fe6aa44a82966982c1f449625a9d262d6df0d40e5cbef33a482f442ef2e589f4eb767d660f17aa720e751bfaf3655682ef2d010e20cd6fd5ab71b025c1b7d4997bbc311e7e95367857f1673f24a7ea77aaa07b01472d3b602d16c87134a97b39d94af4457cb74123956b0187baad38945539b60ae06456083ab44eba00f21a74cef6800454d0e2b155507d3de203fa1fccc9dc0135366bcca18936b98877dfb0abddbab900ed77fb0db56a7120cbb7eda58ae1047ca63c52320d10dce2646a6f75e817153ea5b91ab6cd6e4c571687eae883b20360984d42371777fabeba6cf70c2f33050efd3adabf88dfed7790e82f53c3df07f8ef12021071e48e4632350e007d81a6ac139132c1634478ecce2906f4b9070f1c3fd15bb68e1442ad7a5bcda59da7eda5d5897bff5fef2daeaf2f21f6cd51029908a442d480ac99d36b241bf5c111b9dc6c6265283a608aa0d2ab6e74bcc705c7d0f3528e69188f59e3351c1a072ce49afc1601572d698f661a0a887729900b24f5ca309724893a3c24c7c3dc260efd5851c67be470eb3aa60ed6202be1d8043159b4a718cbc895866be71589145b50f3bcd48c9653172bb6b016efd4e74b0cc5381408d50a60def82200785d7161e4ccce7dc9483ec9bef760788506f5a00c059c4ce0cf36369b67e00499740c76a73c6056c001958bd720a1f38df8ca1908918864743e426575316f980ae96822c2542ce438f854041718578c69326d210ed67447e97465d32a42053c5e62eb33a35b988dfed890c84c16d3129371633f053b19bef00068c9bde2b09b3701a02291fee23d65fbe797ab111951024af0eb00c0a0cdf17d95579bc3d321474ae47d988c5ffc2fcc6b0f4811d7512b0a68880cf83b5a25d7bf46fa99af3655871a1a15e26f6a922c6186daffe13bb8324b3101297da25a72395396d09f422295305d67c1e94fdf664dc61f1af550f85053310075bbff580810a45685ed989b02b4ead893012e1c7bbc1a8fc16f944457c15e0d191276781c2145eaf684c7b39201245119c227e41d20addf634879e7c45e4605cdee1c9204661eee85d0a82fa2a0da790acf5b800fde530eef4851f2815b5b0b1986c6472f18a617f391ab7eb812e6c2f4a89695a61734258822e3cbafb4840275607bffcb5772debd4200617f0cc9bb7998f7fefa714fd8ee2d057169f66d0c2620ce5cdc147df03e2097c2c53a934a44fa0e4cc042eb18cc6aeaaaad73900e5988894d22f60219a6850271d084d50ba3ad9476ee0742648e7ade577bece302edbc3db40360f6576f3cff5fa1ed811ef1ec64d09878933d8d53fd1cb91e9f0be26fcbcedcf640b160b540699198f3549a3f7d5784f15ae7666f38790741a702b9a0f345794eb499ec518e4f5ee8c45833612a5788d0efc9317dd5bdac8aa20bb2098060b1cd2cb688508987ba79d5c05b5975a789619b256668d3098a02680b63e0c91cc247ca9e11a9d7b6f4cd3f5d6bfb8a7bd282a94ddbf99ac00b194f0ba62541019c5c0cac6949a81ab9125bcbcf8a1c81408d3e9285c63b6a8b7cd40b255646597a0b87b1ce28a1e485f9c5548adf4c21b9a38951aadd750687d71f08b0b6fac0879ef488e533851cd435bd19db56da88c3f68814dbd1f827026c71067c9abb42470c83025a73f1dc564e2451339f20b6900b7a88cbed369d189a7609f59b87dbb214e5690cb02163bf944b1cb4ac6b45975cccb5ec8d4e429d63b303b26b3917979138bde29b4f0052767e6455e83c17623a5da0601d9d376cc76b052df65a9ed4914aca875b03447153d121dc074da4fb4fac71f056f1ecbf6ddb0c7d6376d749de85d2d980eb7ce66f8db840ba0b7f9aa5e43f76964e607dfd31080296a99cfc02dd1144c8c87333ff624c39307d6944605afb5e0d370bf54e1804e2d1a40571f5776841f895be93a9c03af906e74cddef832d66a02617237c8a047e9594f1d1276126327580bc7a767f1d33e540d2df80052c8b6ee644746afe080576ca4e50cb35695ad686e00e6c6e8c1ecbc992125dd3c39fd8298fda5fad0d09cb0828901ee5f9576cd01e3f697604378ab05a734529dda36501003f2c330fd52f2fe8aae5f9963da485652b60f4b248d6c2a637403846d084d2e81c1d720d3a381638701994e631a541bea27eedb50caec58799e32e2e38d0ab1c2242a90d93f9eaaf829a381609f1edf61a644c1bdc30abf8b92482e1bbe4b42cf6e9a50c76e82f289c53f3f59cb369f7673a4a57acdad6ed82e0a87b0c2a153a75e26202b2cf2e05098752a540a82e4fa0a4f2bd82ef79fd78e69577a0dd835ad7980b086f1b55ecd24de12d20e9099c2001eaafd820932a25095294273dc187e04ff30033b5688e1cde251bbfbb595a71aacd953260cd7c5a6646f001af05303ee6f7006add13955747430f596f9b11e91c7b66cead5d33421d3295d18bd06c3f48ce0d42e283219698d7f36ca2a5c1837509550d8bb4bc6144543897a5844542032e0654c37b38c62583dd3cd725a827a75fbb2d5ca241db5b0ce225b4786c5e749e09e5cd62e57aa6fb88f1d064eb0b64bd9092ee229366f23bdecff7d5afa96ca20e8e6baf4025a343d00f63487c94d6fc854977021bcd34767e7e23088da1e35f0e032553e0fc825a02cc30563a15b134599a478f9eb3e414cef124cada7931e30388f01ad0a10595d99cc098d255118d89791f286c1de69416ea93300cef436f0addba2eb0bd5d7c77e627eda29bcba949ebe7d88fbbcde3a1fa0fe9b7b859f80b572031e2cd083fa510d969b53235e1b7a58f4b39a39a667408edc7009c988604c2c216924df2271055114e5d35db0e35aa18d7231f4cbf4f5066e63c4d8281026dc14aacb1427bb3ae371e10aa45d4b910dbf915eb13e75cbb18e4b03315f50ef37b1955f7b1771294f272d6d66ec4ab6c7ab09d6497c2a9dc4504de5bd8250c020fa72bf21b93d901cf060c49d0a434d1ca664e7a7f537ff6b2430f701e5e00cd35c827f4c23491997ffebcca2e844e531b7d3ad566fbe0d96a5ccac00bf00b01ddc42f22817dd94719d82c98e3cd88234567a75b6740f8b1c263f4e7330b04875f69671bbe791eedce02e0fe1dfc901875cb7ebfa0a1109feb6769b20c6e07b6ef63ae6345b0a689a97311f8a044ee2b8c30acf656c5f3bf097fafea39340d01a013250d3f4fadc1452316858aa017d315b68c68c3ced3706991fb113d28074c08e102f3d735ab58c66f89a33543716a9a59a010cc6f7ea8dcd049d6218b0fce02070ac0081a1deeb42909fd71dc17bee651ccb0abc87dc29fbf95d1cb2904b77e5c90e02ca23bcb448d356801a1c29241729f35d5a4e5c5672537ca867b0437dabcdb10f894739bd9f23111b1dd5544add3ca5d13a56723a3698ca9cdcb0a6a647be54afed5b007fa2ff8b33099bce835a7034235c7671e6a396ec55ded054ad98ca7f2f01358739ad223403b90e3071a96203c877e53b6a7bc59da95c10b50a5f268c26013ec3c558c33dbd972a0a0cf4b0bdfc59656ab7ff8e824cfe40bc19d9536a4572b21acdf16af3cd63d736e6394ec9746f58409497302ca8b1307f5eda55f6b2335b74c812dcbeb2f3a05efe64037b564cae27113b104a26c2e0b0e53f4ba58df0cfe1dae296fd745065cfabdbd2a33191a98c2621ad66ffd5c0f20fb5cae6bf970afcee45afc1ccb9f6cbabaaf531085a15bdb704e87b519dc00371d6ae0b6813505614747ae20841b6c9b311abb0f6b41acd60bc151d7df680ccf1a024ae8081946e4ab926f893ab3d477443e18f5b3d0b39780e22ca05c3e04f07ee22bdc1c16b460ab56105c75548a5c7aea52b95f1d317cb0aaa584f970095e7faff28da315a6b218498513f57e98f03112c56912d573521a8a96735de700cebc03fc07c302aad6b09d0714be24effa0de4b2e046e814ec4e0ccb6373390388b6627b406234d29a22be24a4c907c81bb6633c25cbfe804a3616be6430490a8e6611f9ee4eb469899ebeb6151decae96fb36a1ddc46ec069d5e651b7b2fe0a0a96ec31bb3f115c7b3cc18acddb6c13e5a531a3c94d2b95abc60105cb9c120adc95bcf8037af7f216a555b81a21cd97b26d5e54d81701f471ea970f297c0905009ff8f2b898112078cde50aabfb49222ae215069271be5d40a1cb09450c780269f1f3ff2cecaf95d505a3913ad39116e3e4bd44fb0847a7c996b97e1e63e40679c98bee38dc98c3b028d61f0cb483e41cc0c673daf6ddf7264366e467523308830f9215638a091e5a666c3ac952c4b1e29908b388654c2f5b84257ba4916a0dc0cdd1d9ec56ce4e3886db7bd8e8be69e1bd65204d504e3d6433493e692a1e0c1aec0e2119eec0a2ad08db7bf6ee0f95ce0dab601af6e7ffa6465f1a8ca9400ddcfba691e0d71105fa9fa3a3158dcdbac2296ca236643badfb0b9b9a18cee40d6e059107284893081a6c28157622716ad4b31d7f62dbe8c47ca51e4ed9253c08ffd0c76795661187b86f02c59d305d2af6c0e8f2c490a5c01b03ceee9633df0a40e81dd226ea2a918483bd260c6c235525f347ac420e2d941a6948ff808440012ec20ea1d87cb0b64d14d77b4d064b26b9841556f64fb929d78f2d37f101e504b0fb8eb13147f426448040815be80f22c0429bc0f281e2d4f560fc6ff3daeb0aacfa3049d4cd82cec64836eda73fcafce4d36e8d864ea23c0043a27bcc5c9607af90461ceabf0722e4309030804114ac390bfbe54994c2119bd6bc21d4bbf20dcc71d83a505628d86efd186c1bed8e95745b83b8ea09eb7c3f14c2b697c6870d5d62130dd543f1c2cb76f8604fe875ac9da0010a99cb9b75c45d93e7e68e46055bf337213ec7e217324caea7e8a451e599537687071745873d994090a0bb960571254ff5b87a53fce4bb2d04e8b17b20201d0e7bb9d4ba6ae088336810b87d070cb3ee065e4388fe6d3c67600e0b0c2026cdfebc93b65a59cadf14ee5be75f05f1b0403a1d1d2eb72010c4e827dad9179ff5a112236843a1abd8a04483fbcb0f11c9b07e9a6be6e27d4dc7a233be3cbc2bbd0e207d3bb57e67db74a58cc36d03d14c78fdc62b313c42c978526295cdc572d878b2c2ff9ef109384b22a4d1b102e35bb55aa31d97e00a83ddd35e5db33ba9c942bf0b59e0c3914739e70c80720ec16c078b15e18c71faec898b8a9151c6b4c32e7585602da35c8c0e010edc970ef0b1ce0572fff79fdb9c4630c90c3d5cd60567ddf6d85acd5cb7e5bc671df1095e682b363476c146fc21e0c223e0ef3c309e38efe04d8bd2b8c29a5d75bc8a04bceeb10899040a029ff80077e88cc76b21bc0a81f90d107d84292a9e3d5d3a090cc7bc71add72f82a80bd9634919403fda5451c9bac9640565e8582c7f7e730ac43cb1b6d0ebe489fdb3720669d6e2844cf25d5c61357405486367254510c40618217c05e580ae49e45a6d196d7b3d4662d1f73c5f745789c283cd957c41750accf833d6136c04ffac4748d2d874861dacc8c54e4cba58920d0af53c2941c800eb101ce8574de3abccf1cbd0429c8a15e625883f0b8465d679754becfbe9450687836e9f6f0107ee1fe0b5915f11f6b2b2a31aca85dc0224b3400d49c8bc000c9e72912cdb41873b91b87aefc2ec09344fb9bad44c683f847323fef5fb078b05d457007575956adf9b943ef92dacf84be16af350077672c6cdc544642a2fd607ac4013ea3b60e92ccf9c0bc93cf5fbdd0d2ef34871afc9db174cf136eeedd107c5d2df53d98cebb9e57ceabebe9222b580f8ecba335840db0638d042e373b00578b3410ff62024eb38d5feacf829f5daf02b8ec0a77b03d95609c7f21fcd260a25f0cfe05f162e35f6e20887451e69ebbc7f6c8ddcc8c592841576ddd3b14205ea23a585ca22fb18aeef108e84278290a5bec9b5db494ee4335c0028278eb90eee632c07ff3fde87824990058caeac4fb5b622d4fb2d543c973b4699bf8ad60b9bce59ea6dcdd07b41a3cbf5990ecbef12d0bb3edcc032eea71def3949839f09afa5aef31856b74be197d1c94f683b8c84f686eaa6b6ae74ef240dbfc3784a03028c9d7cb11cb018d7fcf61817b5c12b5fce40f15ef7f078b491bd363812670363716859c5d2fd28aa2e47412b50010cef43772b9b2ee49ece96f440349562035967b140b5c06bc54ba21f1fe6d84b844fc9c8a5afbef4fc7d0d0de02aabcd020688c06dbea7cb1e306d39e21a16ab9a85b4635ab970741df8f2e1e2d8418b0fd8ad16ca42af62a33280639b04c4ca30b7e876124569b046bd8ad2b7fd54f8043bb6e10e0a9ac06ba575a9ac951004819502607472781c0c1dafb8200dbe360b7085ed11dd87de928cd3005122d392073dd4d3a4e62c4889b51fc3ada9610209a9cb6bd33e6caa62dc216aa9faf885e32d115b288ac3316bbd3e91c8324bc9090e6182addcbb4f2a0d68351c663df016db892334a760dcb15ffa6b079fd0e305dba68d29cf4cfde4cda3d09bc827dc7e1fa5806068b999e84a76d03a4681bb03ca58e2dee0cc34f36b8a8484b1c87fb1d368fff9e5181af1712026cd96fe2c012af46c0048cdc941194c02994dd58b8743e5e9a8b9bf9851ab058aefa08111096dd0167cee0eb0756d0c291d0aedcfa5150bfd974982f05c184d96e9a5d4680b9bc2965836585ecf598239e1ea550c0dae42c2920b6c864f41186255dbdc1b08cf5b76b3b18e3b12b8e988001cf91dc15868164a22094c63a65230227a77780f83dbb10bf5b0ca0122af8ee6c2ba08e763e8340a586880521d7e1a8f00523e04f47621f9d9c74149697a6b54832e761b5918d91db8fe485e6873a6abfb59b601d579874c206cb6af01c2827c24328085ead83a1c004c3fe215d5ea4239f9700830144140cec599376779d78c7c15de0e4706f1c0a4e4780b4e7382440fd34301c24a71c52daa227b393eb4e4cfbf888ef15d43066bec2bae57d97ccedcb8c7067f6906f7110958ce381a3e5304b2bd9819a321c65792de8d3a21a49d7638d8089d9178af3cd375e3e7ef05c5658419a68ad8708ff0818f678e962a01f6d44a00660086b83614f1b9a82442f14913d81ed95a9399aeb41004d51c31c9edf0de0d2487299fc1172036256ef144bd4fd416e46d9ff613d365cf3fc798a9be4ae802baca8dd2750286f95319ddb2b8a364c85481d143e976d234ecbf8be3cd532d0ce838ab6364d1454c9a0ed0064159bafa1aa8c843fbafd98f957cca8b0742770c01033bd30d7e4b11d2db4227791e8ff90c19ed74a6976dac37865022f0ce360f904e816079c9377be7e225fc4dad7af9dff2ca2d5c649d6ba22263c68d81aa0ad244a0c53cf478ea7d767801fdb243fc044fada99d698c6d888f04c4cb60da0ef566ab2b76ed59cb1d13bd25a1c7b535be6df382ad049406d665adbbfa4761033a43ecfe1735a80a06de77bb6c0ebcc6f18b3fac3ca2ecb1130664ddb47a8f06b8080cb4789c915015b4f1d3a9e6186b6e2a7ab9160c79411d68a31ead87900dcd15d8f8dfe5a9da5fc9e31ac1cbf4637dc5ce0dd090684f904f5f2eb53f8b05b9c04e9811166aa2b86284a341184ff40c9e18ebc1572c6115fa34418d03430f3b34a88898ecc97f6bb0687d6de6d9e5e598172b4d180ae01d7b11cb2985cc0e9bef38f8595f716301e1e0a9afd042e4ec7c5028d0f53860b8eb2ccf851ec508131ffedd50f77704e44505813a1f0bd3fc4a3b59945007f5478a3186f00d47025e7d0177848f3fc8ffcaefac4dbf9e3e8ffec8216b045518820ebd083f12950883c43b1d5a12d282c7ae0010180b2859a3efe61602ac77ae7b7f5685efeb530f8e61844bc719845fff89dbd7b230ce88511462b2d7e04366a8b33848558bff079ff550fdd93a0b3554bdb0ac1fe616697cb3cdac78ccc26cc09cc58ed7dadc0b9af7e5c4dee6a3d8e2b96800603a62ed77dcbac67b0afd1f28e3393345e9b40f71dff1adc23d22b190e52a93b20492d3eb3d4df5964fec40208b3663e8bce60559557b39073c5f78ac9a039c376c9f1f87a73593be1b1bfab52ff2be1729a40ea9ec03cdf28546789c8e3cbdb57bd665446804e60a0c22f826f12c89e9301109f497f8ea38a38cb97fb2e4ca1872c6ff350940b66d0230da3ce658954170980b8688653830948265795ecb5cac17836436adf181ea969a1734e9f4640407f60813c21bece8e4a80ebadd7d83d71041c3ad9c1d6ea713629cd3f3468fb128f60030a087cc7d0466545ff2b812a8771e310b61ae0a4544713edc61e3d117f8070202ea39c64880f7c08f6394aee77aaee7878bd6609928ba0972009b281f5b6a0256c5c44a4d2ae5f4ff7e517cfcf9d6fa4fbfdbf256b98c5f36218b4078bc5d02943700f0add5d59f0191453ee71135107905c43f8dcc9fd8690fe34a35193b0f9f10e409f10c44a4b916ccb3d300071f398e6c499df1e227ca0011d9f588a705003c44b49f943ccd88bf39bc52a45dc7edce0dc6ca4d050ea147ec300a90ea039feb5ac052f473f381749823a981099eed04965d9a57f1cbf41ef05901c7670ab586e47034efb28bc9d0afd10f4f1499969a40a23bfe5d5c7229cf10962962071c6adcf59412a30961008a0e7917876eec3178eaec44f9078f906d9c3ab4270cca65839a4f48217a6972edd6adf4dbb9dce52f17a18e2c23266d961d86559f09b26d48bde50a73853f25db1a853b256ab07dd817d51ac347225c076d15de4603ced70722d67db8572edc59fdb57ee2df7085121b73dac339e4a09cc76223760b6f11e53f42ea720dec958447cba19624b9c3230b6b9d2bafdb4a19ebb0dc3a0a56be6e6cde735bdab4f63d898e3e1271bdf38c1eb6e3c9875a2c69afab117a097d765d389369117b97965b9b91352936da3d8af8b71c58fb8af579265932c60b8ff3e95c39d3dc205ae57de1cb459072968309598ed975e7c1d741c400f38d06c8e649ac49812cdffdecccc5d95efea056f5752dc81e8d057b52fbc70309df05c67102ec4b9da8f74d35aacea5cf197086ce82aadec0ff6345da041137d88f031213d7aa5e4e4b247b9ff009c590aff0eaf09ca377b3551b3d88aa17a173560b33441a90e5a5b1482205e97731fbd31b9c3539b11a8803ea193d9ad0091c280b4087f1838aef1974a4f6175ccc445c3f8a3f9c96e5e826c136f869f5e46dbc01d006f7b442d26fd7f1cb7557bb734c3524406057b6c96b0fee25cad50dbf850275dfddf5f4660bfb0ffc419337e02f06bbea4fc6fa23acf926e449c51546b100eafb39785a939dc43290fd1d531adea0424c40989020735071b75e14089f9a013785aab67dfd913f22bd4bd4e56e9e2d83e683b6e279c536b7fc5d1bfcd6b10f2b2306940f3f75b9aad396169ce33045b44d11e8930f32f2d00de1a724b8090be692f2dfdf6ae6a14ca9141918fdd399f2e1a8f860a3d698388d7fdb53b22905636c24587c88bf9a20c7700af526028c983d45de89c8eb0b5f7e520831b5eb04adc550aea68a7191516de98fc8b1f255c6b259b1c8f0e74f456d7bb3c81f860c3b0743f5b5a3177a6b89ffc6cde867b25dd4a0b1f02fcbec8065997ab6f8050108506ef95cfa7719bce7d89b09bd6545b6479cd39ba905738c6c52a4c2e63904ce28ecff365a307f4dc5dd39745fcd9fc1683738dfa7da87ab6c99007589cf0f8647ac362de2df2e2537c4907b5f6b0ff3ba2809bc72e87366087c828a88970c5693da95d912278fa3b70e688e7fb47dc3167100081981f2aa10b68f318d520f16f19cf2c7d2333ae52d7bd9988981e1b649453ff71cfda3794859bb7d8a63017949670cfc15741e28847d1344373e0734a506a910cd5d282d7c3277e5a3aa04e21217e8102a2beac25fa21135516480e3d40dc6275d9ea8915eac4add3434049e3145b79c468746a5916f302c22b6ea0978e34efb0bcda2abb5f5867e2dc503165f5276c307bf7e7a055006e4d7e7434f18e9566a9b17521a96bd19be61c2020e7366ecee56716e8833494e8beb8abb49da3ed6dc6d6f77401717c2e4fc6c0ad61254ba42e08e238278a1a15c065e14055f3d8db03a8088f25179647106df0593928d7e4ad10f7031e5e5d9a7810373fd7ac66138f0fa37804326b5318a2704b79c694f797ebbe4d26bbdd7231b54c8388b2616d8aa651be99f702773c664048ef16842790ee7a04e36edbbfddc10c37f3ba13e94d85c9c1fd084d6551cb00f9b78fb132b6040f14947f00c8da0a8b63fe0f5d1a2f5ddf553955c9d27d5c60828552a55437cad44a02f992354e10e30a154a8d9b052d9f6f0609b092b58a90af214d087c4ec2119a5934b61d991b887013690ae9a36c50622c1e9ceb8c0e70064c5a72423233e29a06e0be0ad3e125ccf2e2ad726ba40609e2489021c227e0edc7a83d7c588d423d2dd40428fd4f1f661cb7c765d5c7c260da2c193f6b1bc092701fd19c617a35dc724a03fb5eaf969ab83070ed6dfb237559f1a8bc3fafc0eaab7ad56033303d78600e27d0e30de290d679efea81f928e7812933f4f862509366ae1237bfd57bad71f0bf3d8a890014a263bcac7f8920c0646d8931ec650092c1cbcc40c8438578251160c71e68d0615694912b2263fd0efb2bf1754041e078c71a5e9c8c3c52a0e575eb05dacc23aee014742f283f3d1075b3f25a33a380ca5f503962a58cf1bff733909205d1e4881c09567194752109fb6ce858e27ff0b63c4df380b138ef2f9bb84f4efc50d5838cb9f48caa113fdf69bd69d0cc7b00144de14df30b488605e30ca00884053ee4261345674c5f862578c3cf71f1573034ff96f5763e19218eddeeaaabbc1ca94ba53370e5b11b84ebf10b59a93cf6e084de967706cd42468be09bc8d40cb2911c61d6edf9eaec4447e62010c5bae580542390e5328123bc025f36b640b4002106c774b0068414e4083abad504f16ed0364fc0a7f36bafebf951159f1641fbf0247bc2bd6042c6f3ea87772a9e27d940826490f737e2249dcb2ce5b33b8666142e41e0b51ec2d44c31988f2744d9df108318b3d820c5d9b410dfff93fe46573b7b2eb29b4a4d67745b7bcb924a1db860e7f1c850b047f3dc15c2b78284f7da6145ad9ee432b7414120b07a003f0cef10fa982060bc1953578603ce1d58124d2463470cf6a3f35af5ac401b2c524f659043e32555b2b1a6fd5aec347f4383aa8fda2449e109c1ced05c030d33dff1dda0af22ee1ed3708f48f4cb4437262d526c1517723898f030598d954b58f5d4c40005788155954fd03310d17fc7008fda04ae9c98f2ee5dcc8d1e47caf5fffd2d70cd9a684162dcd398a182b66bec62e59cfcfff00895377d4a0d7e6a92eabc18a06114af3ad2dc7bbaba79749183512fe81bc0b20c7ebb593e779143f20e9c41f0884304cc9d14c6e4d68e9e390094d2f3d8e3a64205c1784f18fe95a5d41798f079205030fd9e016406b74a279cddaa06191e86592cbfd906ad793c8595352b20a476b7479796b72e26be63abe5e7f565dad07465bc001a47f645ed475623a72009ed4a732b5aa3a6edd5271f457d06a7c94b247fa52eb99cb2f7c65a5408d330eff26c16247b4bb92b950a5dae105bc73f69cd2cc6abfbe36f86c2ff694bf450b2b9ac3d36b1e0e443e0bc2eba87b8128c545ca68bff547e9723856cb0d29990059caad0bceecc348852d8fdc0be1fb366553e1ff63374cd3243db57f3f466f0d6af0e2cb9e8464aadce36610a1a8839ad9e00fe667f4f453471cc9701a13500e69b658d9bfb655ce50fb611606cb20a71cb47cc64339fc70549315a1fac2090b5f2b71987f9882669ffec3a98563f9e465f5562666f0ebb528b2371684ae0b0388547d25315780c57631c9e1c51e0f6fe64cf4a207bac6ccaca75519e9180d0aebdc704900432fa99c8895af68cc4e4c40370144ed28ac32b7e0932132c4640be9bac6fc508c0ac61c4f53f55adb9e61b521ad37ada44e948c2436c65d7bbe0bad267d53a57fc742d6f830d93c17d2a769dce5d1bee2be3225e9fb25294c6906d13e36fe45562545898a04215ca905d94d7286b2086acb2685e57538587770067c9d88cbf76fea77f087e4492479956ef4eabc40a4aa098fe9b8411b53c0e00c80ce45a8b6c53fc8c4b81a6cccf17989f16dba4cc49414a32627dab7c3c1380e2e3034cfa86eca02cf2252030f09e580358a179b9508cb11b1e605f469c29c0303b707c1b883f6355fe707d95344a74df5229176672cc350a2e4642450c2f00fc4e098ab6c5f9e836afec82ee35057384b700ea94833e1fefc781dea8c92a507fcd4f2603524925295c4a3b0d55f764f4096d94d6e5bd356e248560981c8770bb1c6c220c8fbc2196cbc5dde80afd0672c1eb3e3cabf5e0a18fb4e5440ce280e3ba6baf2a671c3e115edbac56ad8149756f4ee944a2db0e6382d0807e036cf0065b44c0148fd09a03a43c6673e05f3e2812315fe3315b8713b8e8256b68b250d0ece4b804d0d383f1281c263ccc30254f1e349e81c9193a361aa1bc785ab610343b3b78a94fe863fddfbcffeaa986b46f1a44504a29d16da8282511bef2dca0619fabfa85ece2d62c9f341557d5ca2513252769a8e78b5d31e5e682907b190066a99fa9075e6727566a7055be3d3b507f4b4ccd97112312f234dc53937cf700e910981c1387cf8402767c28d410c0a833ef82c0489bb833ae67eec6eb7c52d0a77f570723b71c631869a28b29f94b71b7da83d3c1d82d7f004e70c7deb25ed02af21afb7ffa73152c8e9a4b71244b0ae263d22950d654db9efbfa2e9e4533a02033dd206723b9fcd9516dd993d940b729efa9554597fab4df97d894686d16500 false +check_ring_signature da37bd496b1bf3277e17e125415736cb40ea90e4aa6f5de589d7cbb9f40da287 9db861f667a9565757f8ed4c817be445a3bfdcd5aa9ae02bac0f21fdeec277e9 4 2d8b95491e44f2e3ac18dc7fb65d9937885e8508036c670513e59b130621b37c 16d8b806f4145c7a90c5ab9038cdb8267db3bccfb31453bf6925546263aa8d09 f51602479a2b3500200ab33d673372862a51d75674cddf5982c6371152c67c4a 4f09dce75df46512366fab76643a9f7b0232cdd23195a23eddac4545e08fc1fc d7fc184499d611c2d0a06e80f8ca9c3626275461550feaa60f3c47c4c6d75f0610757270d499e2286b28422e8bcc4372c7d3a8a70f0e205f783c9528168ab60e179f99c8b98b3b4f4aaccc70088528f0dadce00da8cfae5e45aceec2826f54042d786692f119f5912e680be44553190b55722344598bd563f0a66bd84244d10c8798dbc02f685482dab5aed5e5585635d3c859f11eeb306b3b1c1acf02d1ac0f394fbd8b6cb00f981ba635060d8b236287b02edec7bccb66ad9fb6e6fe44d805ffe03323b70d7657ee80b4cab908bd3c1b5a1b81eb09e9950ace747fcb691f03f93473d7fb81102b1d3f0aa2a6bf38497fffbbe713588dbac97cdc3bd2e8f404 false +check_ring_signature b118cdf61ef11c95e63c4ee2ccd502290b01b2df5d1dc01b7a0eac45dac4c193 6dc21eecac8f2117b83841b8a82862838b40351d9115aa82a6c74466947b8ecc 104 c976aa35b3a1610fbe2473337656781fa9fee59b6cbe2b4375e661b8849678f2 4860bad08af7e44fcc27c2772888c2d7c5834d861b31aa77f0f9230ba67085c5 81dac147037d97465c02ed8908f3a6566e690c0dd3c8e37610b01d4c78414d1b 1023a2213b29718d2363ea3c99a76469b4897ac73ed58028b1b7f8d397fe6b94 8c0a80ceef411fd6dd7822451f68ecb97389b165b56bd7630b1d8fbaa76503c5 16868d285eb6366618ccc4855daab6ddad590f159c6f6910bd17abceef2e3907 93c7b1d5d088a19a600e36719fa1f1da6980a5ef553809654fcbbe628ba0d433 f012b5f3f60365c0153126a5cf3c205b503f1ff817db9646ff831235ee427354 8837154d40cfce95430792531f494739eb7ed8294f8516bdeb2d7a03575f3897 471e45c0d08920b39372fb3642bbac80fe2656a45cde006b60e74d5916bf6049 4e3dc544f59e7d29434d2bb715ae35ce3a8960a193ce0abd61a7e6b804b439bc a22291b9f9a2152c1d7d585ad06b82711572f19955a7011fea1677376f0176f7 d87099078651b02c5843105e42b0840459c02dc9ec6402dd012ab07acf1b61a1 a02ac4aaab3c51fe6c063037ebf9ad547f722006feb20ca7f170ef53659193e5 6064fbf83d66ed3b5647086d8788cda4c0c24bdecf84ae26366a005c6a90710d 6ee2fd2a22f2b74c6d0b56233867c8483609c41c94d22832878376cd4e718c02 71f96180bad97d64955d63311909537feac10bb6db1aea063fb5d86abf7ccd4c 149959891780cfa1abfeed5a163c5d1c9a083b9ce997ea5e68e0e22ef94c3473 56a3dc8d6c712dea8c59783b8aa7e5f085460830547aa2d76dfbd96e8ed01e0d 2538e4bca227412bf79542edb2053527acc4dd2c120fbd52625baffe3a2829c6 9b3823ec3f1c4b5d17e8e4ebd660162184d2ee5c2f7cd4e19a414f5aac84e7b1 4783dd92be8f2de30fd674e078887094a1447df1894d082dee99343e040095fb d6235042c4099954838a116c609b376f66ce5a7b504c03fc2f4a3f8a4a9dcc2f fdcb35ea80eed89a2d669bd338528a8d42b4968045b1064281908d28f133c270 2087f3ec8a3a4fd0b72c061c03cc86acad9f54586725848bf975c6e3eead898a f28b900c60e88e6b23b60daef466b549abaa3b43cd33da0528e1612d8db9377a 32728b1cb5f7ace3ddc26b4544c53a07e831642ac055f7d4deba669452d9978a ddfed84089f1c51ef8bc857d74a17c15c9eee6e04abd11c6865af725fdfec42f 97dbbb8435b844b04b31ff9e71c5c1ec786588672ad17d0743c55f07ddc38122 6a64f41e7804e5d36e90102f9315e4d265444718b311273b857a53e18b069ecb a51ec8992ef41b748d9131c3ca515f085085abf2fe2ca39e586c8c3783ade936 39284088f11a6413a731ea0054a083ed7aad76c0ed2dbe9f081da9be3a90e082 083c41c2863e3e6187db79f3897e242cdd7b5d5e8fec6cf0c094ca459e5c9064 88b3badeae021002fa4f7c2e998021d0102251514f3f06e6fc2fe848f55d0291 596385c3ee74ce0cca1a2cd1d90192becabdf76707a87f0f8de5697a23927c29 5ae3dce57f99f6f27794487aaf8801c5deaf024f93064cfb2d1a657c34901221 a4d50d8b4dad0a6f8cfa279a323950be3183d058551bbeb554970bf9f17cbd14 a111706d9f2a236b47a0aed17dbbdbcfa2b7975f415af762de517d23f1e71b54 a3f26459f442c034573b26f2c98e55cb762b74afa150972ff9139214df3b530d a2797693fbc187a3f543c561d3c9ad906c82be8042acb7d0a5fea94279abd89b 5eb2b10b6a749bf5ebfd0d7f55d6d2e472fedfe784494a94f7dafc0e0837e3f2 5eaf5dc79bfbe5e42377dd015e3aad0c9f64e844b40495b4fddd7ad33172159c 999cc4a528939d1ae64a45f575d1094283c408a587fbbc13ddec93c5bd386172 82206cd6cc2aa4105e9138b59ce862d59ce4a099a2d61971e2b65b075cd49560 cf2a70bf3d7e82df094d5e3bef22843d529ff89b1a466f6939fe126712af99a1 90ec9dbcf75e15a47f0cf4310d05f5b7bfa1987002bf5ead07543d20edc6feb3 16d2307f29756ae775a2525af3ac14db37a27b3ad0581821442957297210e3f4 5c5d10348e314d2dfebe27e55814b12dd226211695a9d5a1fcf2ed2433b8ec6b 81db43d78e02acc20639a8ce7d260a2ac32048f6c7390fe1241a686874d61638 ef500cb1c91787ef3dc574b6b21807af169d3c71d2f60fd6b041e026d28a5e58 0a94a71cc6483cbd67d95f8a5eaa23a16d7811752651195d731e2e88a037535b 29b422784d31eebf871407d1ab46a8405ee3e209f13658d6712c682cfd83dd75 2e153ccd02b8947a0ddaa9c7acdc28a8db73a8da62a8edc4c6ca2427a786ac70 756eb09e1b0a1cd802bd4f79f397ea113b57c448d92478e92561ad38fce1b594 945a90a68c769ad1034b851d2d747693e95c6e42b75913edd6de4f0ef1ebdd52 89219d924421bda74e627f9d9d6b55e970a039d49f5b5e9c7b57dc8891a8f28b 2396a47d36fe6f604fd14f811bc2d6c612ad789721c0bc092eb30971b93e0d44 3f6fb601c3d00325e1a37098367d56a855b0f0bacd09e7fb03b9120fc2d2ea37 9e79f0849f410dc80b091ea070342971a8bb02113ae90dcda8509c37a7020825 a853d63d35422e791e612cb789d9a08d3b37fb68489c7fdb8c755e7b2fee1322 6cbac2d99cdf2918662d32401a0c07945c11f699ccc6ceec26f2433a0dc0b854 00d543e2d9d3b630c340bfe4a1d06dbe716963df6646a8f3620b1411bbf53403 838f530b0c0a0f846f252b805f384a8174bbdd7fcda51dac9851467decfde9cb 94cca6f4d678d32745ecbde2897a73ee9b5d55d179ef83e7efc1e74f262799b7 6088d4370daf69b8ccbb5af102a7386dfe39e72229c517a706b03aa28acba494 aaa6904575f11566bd0031e0b47f26a02655c330eaee4a4df13cd3ca17090693 6bc8064b0e52dfc4a79bb4c56d8b9aa5dc9003fd6fdde11ec9bbd0b09d7e396a 4ff7fe72313bac367f0d0a6e019518dd54554730efb61bb2bc7e55b84315c27f 731414e5b445a6a3d47171652eab337cb2e0961853649302274efde3bc77938d 51847cea7240a30fbb34a328302ad4b10bd8e800a0f008e1655c6283df09cbfd 37ae3db3c6458cdd842d32e21db209c77a4cf4fb7996ee9f07d8557efbaebd48 0a75c9ef177f980bff708bdc5d11f1c307542114dfc39978ec5978d57085acf1 c79f9d4fbc728ab0625e4aca7b297a7ad7a7930bf18877877079be6460d9a492 852fab211b39df19a5bf97d878a998f00f442a7941bfdba1891e45a3be3589a5 c63ef7579bf62705be84ef0a4eb3bc60e749c8655ba80d754d7f1e4ed268e64b c69a41661f81a74737cc9081c0deef50a435fb9cadae5ad86101d7741de731dc 7f64249d73bda009a4a9fd5da84e3d5a4ba8002941a22f16d1bf19c8c4454cd1 94dbf3a7e406e32f047656f6d1feed49100a3134d805896e88e928c9a1d4ca14 5c23a027c80b088471de1d5353c6b6c39266534ccc0b575860498bf7c9a23451 14df002d08354488b85c305331c282cbcb38c2bae3d64c29e5384160bacbfaee a89fb8fe9427bcf63b4b01ddf7a0e520ab8b68082f5e45c7966693122aec9f17 ee9ffd781ceed2ec6a568990ab5f55c45904028b891dd6871405049f01791f76 e229711f16616e30f444fb5b3a6fb03b1a5cc826cca1b3878b4fc933b4c071a3 ab186fde9be557a93d95ca736fd9d0b9f495fce936f945f6abc5495407a947d6 23b95088d0650bce039a10a91436f337f607e2d4e347c976b7dfa072582a86f4 06928bc18f853d497972cf8d07681b3934833c15e845a4a46183dab042c15bf5 fa57c795c4ff6dbdb960e204484887bd454ce21af49588d1b43685128e7691d3 06f3ec26d063cec89825b7f7813dbdd899e478a21db7b233a16edf1ec3da2b80 5ca4e151f06a7f5ff219f5373aec677a57a77d9040b324c07d8bc828b3fc8b8e 3e9b286aacbe283aa3f8311696eabfe9a5bc038a79b334c0d967c6666e8bfd73 dab1cdfcd67d8df295ce4a8ed65a70cdda0c0b46f74149ceda183f18ced1c2a0 9187ff88cb655f1e27facfc918902ddf4bb9a0cf26e2b960f9c431b4bb2a3071 e876747c42b4864969c177a5ba4f3759f472053c6218aa17a0ba1a932a731ab0 84bca26cfedd5785208d18bf1bdc736a6e48227983e67071383e44535f99338d 91b8b9e80e20f68b34b0a1404951381418f46824835ffcd20cd7ce631f82fb45 910fc71a1b7d9471ac223783683b8b0a1cf366800b5fb5b86f828b07e17f769f c389e19a0509bce3afdf1cda546bf5221fc00e744e86bb83771257b3dd045fae c1f79581841ad224fc0c3ad0ee5926d4b467441041b9ebba3d91850d2813378c 2f6b59279c93b82c75405167eac876010e667f0be4ea1dba3b162bbecd97c159 7a47831ce51c2f68a72d457d522543f346676faeb5d151e5a25c1e40d5f8ca44 30484d07424be29b8dc3f1fc6970e18c801b0bcd56be76daf5e2ec2f3355112a ede4ccb30e078cdcb617f5d47dd7b6a930c2f0ee3c4bf3d90d3d0d068b15e7ac 8e6f05c2f3da850482fe4fec2baac5a74774b79dca3b2b455acc0120638a4812 cfa67f885aab1a2b47ba80f3382c07bb06c6e76b56ba0e4edf413fc08f119def 5dc59261a83795d851b5aeb3c1cb9ae5ac0a93e704f5cd9cc00440a51487dd0632718408cce5b68ccc273fbd8592ee3993a3a52e978cd85e42b09c303496a102e0caeb3c24efafb444fd13e73b55b0163b8de9d5fe664a640a00cbc89a84310e1531d6699e56e71445dc44f5c187992bac396bc596e32d7b98066fc7ea9dcc00818d981cbfebb27447b6ec769c8b56ee3bccbb62256a0b1592fc6e0ed394aa0cd2e7960039d1612d83e52d834ef809d35d1089abf63170137010ae4d7af395093386d930298178ffe17a59c73c7e99e1ed9431715a9aaf6a80d472fd69ff8d08713fe3a1f6640e506ef5141cb329dea4e7a609d05a5397497dc4fda1f60c78099ffcef47d2a4e47acf92113d4e188ccdda4c0e73289b37f94472915ce04dd1076385b4afec6c966203fff85616955e74246dfc7fb576f07e7446a960037065041b1486f638dff4b20883159bf95d9bc7036c1d3394bb4a9e4bea04a59a8a0f07003764e412ee68ee78987dd9d69857fc7024cb95ae3403b506b3b0d7fb5c43066c1e4aceb250c2b122c7e2687a3c12a6928a58852f10da578e59bd3697a68501746baf664808a3f84bc5b57ad8bba0b3cadb4d76a3ead4d9f8644f46e0fbc205ec11422f3b75205e2c8ee2863169f48482e150783b82c1667d78898c82fb960d47264bfa3bf807b67fdbaa08ea109c247b37adc68cc6022b8f149019547d9d0b07bc846c5f77c6f5e0d6258147fc51b56b7358ae3c23098fb5d471ae71dc7a08b78b8bd84c8ee6164da0eea288f76bb583f8371f98ba1fbc043a625459fab908fd1ea6df9e595e2d07a31cd86d6b2dcd13561a3e63fca8221c48ec2d56f1980c69f92ba397d52db42313df5efd749e1a2b5448a030a23534057ff1d6e605be07c332a52d145dfa095ade75bf1ce791f44b16db70520e6d841905a0760dcfd30b458427f9a08dbf93a4b337de2531928cad39cc3c3650fd0bbe2f2a2637d12c08c0ad3e46590e548095793b2694dc2e9120a4707218f5d5a88eecd8939532e10083a8bf07e7bc5d892b084714c637971e44af8d5b6783cbfb210cf982d7f8300eaff4919276c80f066e02f4ba10d1b03654f004b8f5c6955932a24e73786b9b0d13c9d2cb74fbac09d0661665f6595eee7b66e2ebc955a5f9508b100d41876c0bb9b2034adfe1f3a3f0abf39d40e739c674158616289b94666bd2c1973853a00cafed26228bb8ee784d83a2d056bb0c82a709201a844a8c867efd66661c49ce0469c5718cf4928257255cc2059e86c215e8741f307140c9e5b809497418bcf60add490895c074c8664a0e123abea8509f9b7b186f2836230780d5f3bbeb782b0a26c6e7f99f8f3a686531f87eec5ba3a24746896c5920f5a8870cf1fd782a63057a607a682eb4ea4987a4c6f59102da74ccb4bd489dc4fdb0b390a74ca7e1010cfd631321971bc5664581a37380eecdbf3f9effaa457ca5d58914135bd4297702a8ceea36d73f88b67b1407e2e100f5ca5271277c4b3d8f8584a29db91ccca707fed68e57975ec28040e710c41ead6359cd7aad90eebfba29985e975545dcf60b170e9416194854f6bbc57e75c7a5607ad4169590d11378a0428ae7f6ff74140187fa3b3dc1b5de882720fc75516ade87b02cfec7837a3a23d0672848c14ea002aadb25fbf09aae3121b2ddd97100f645f00977f62d78cec9ebc4e55e2f09b10c4b662a908bb235e50104083255f7e16bc39e482ffecf011fd2b4185ead67c90f3b99dae07a2d2e8cc27281fcb933bc35e24b2a3464608f43f667ae93889b48087099e665ce5128aaff02028666b32862f751d8f13e59130d69da73e0a5e07f02018cad4ae9186fe4a381b22bbd40d026f951c4ec5077ef683d010ec0d4acb504e8fd7920ca4b6eb498c97b0f419d66df4ceb093e60f600dccad573454ba15400210eff664c880549380685afb62752bd225f56824aed275ba1d7667ea7c6350548907f7466d2eea90d8bfaf9289665f35cee94d41eecba6bf206bb8b19a88801958a2991d96c3c49e18fbf763d6994cdc23d14e07218105ed8668f9d5a4db80bb939d64f64c9347b45122ebd9e0473efac004826ef8c10fd4a1582f4e64bf502e0b68a8bd42efeffa02cf686b2c72754398870fe46e188e79cfc39fcddd02509c2da858757e59e52ab6dc166d1b7939931662236df63b83de191b5d92c62bd01d2d858e2694a2cfdb06f91a715f54580467dc0abf391a55e83066190880f2a0208b52f1207fde839dd278e140126140b9067ae5cb71e755dcf65fa91cead2a0c2a012159adbf79cc86ee19c492c4d64d5c1f5d9364f32b5ada70ba43cd6ee905bf3f09527d5ae2cd7eba9aa555a44f3329ebddb2ba20ad615eeebf2f4826bb0b5a8b48feec867a9420d451e4d124f3d598955198200501e4023769f7b1f0a400cf727c79c20012acca7b84fe8bfe50646a3025f9d38750e6a76f46aa3b6a70077e1ede99ed27ddacc0053b5d265ddb1d4044ee177c2dc7a4ce35c50ca932cc05e4103df336d3f44550d30f1d58935b2d8cb258d19d07ab23157752de2c9750037f7ea93d243b16405ac749919ebb172bbf7e7640a06018f6089a6c3459de360499562c27e507278efb9cb586df3d55b1b523c816ac7430276deb84e5f20cd103169b7d2e1ffadfd73cdee04e4b3cbe1db3e6b00ada994b235d6ce22b2dc9340381305d4c92b437e9226d6b872b34e9ec7fc1ec4107d6af10b544b84ccbfe58028ea308b46f2331b046bb616b8abf665708b8950a29973a95c5210cec9123850ee48b934877834cbcd45dcfbdaa53e6481652044da539613f7f657f9a19c992072ac91342da4b08c11d47a82794d0f7e89060a1f1166d50eb4c126443edc17e008d0b4aaf2a98ab6e562dd04bb61932e87a71944bae3bf4507c38b50d77889b05aef1f3703e7d63fe8f0daf9866ced71d8d52595bf52f31040e0047f7f8c3f90ee3bdc7309c1bbc14ddb3dcdbadc319ea710d50325789aa31c464e121124e6d02d8f4eb35b69e71a71929a23cb3040730dec9a128837494a9268b1855a18ab00ecad7d837bbfd24d3d21ec7b8de49f19cd7dea9f181018e400a936d982861ff067ff049e0342b5e503c782710290c83f5c7b24342f387f36eeedd357a989f150437af1ccf2d9edb60f83225110e2f49dad9fecbffe2300513270b77d0e1077a0ef2eff1a99a8bd862bfe6af29f02118e3514585599713287e4c92983172dbdc0d30ca94b91215ea47589dd462ca071de2c4f4abf814acfdd43611ff0a041d070eb3cd0c630e038a7b4b70a41efa7d307ed07ca61ee23b6198603b319ccba67e01fab753d56261aa2273f0d8a2b976f7b46a3c19c6cac6bcc63204747caa5c4302fc84cc25900fd9d1584bef454b339bc6fc2030f323a6d1c011cc68fcb8a42e038db6db2371398bbb142397af70de4301cd5aa9166e3c5d3aa8fb44fc88055907169358477a1e404f8d41a8b92f60e3a2575c3b2fa8af1d70e2b108b834158e0602285887609dadae26a70f4b117690da66a6d1f3d5be13025cb0d2e97ef1110d0b9947d182d5510c12706035b451c085f9c7c253e3b17f9b33c1cd7ae0f1850a65ba6d9e95758daf817e93e7943ac6e6382622a59d878f9c0f84968ba1009604134180ffb9813d14792c81496dea05cfc796b6af7b61892230a5bf340a4f66055358a8ebade976b7c9ede84d45ce7de690b53ee790146cc795be985d56160d02ba86d2e662f694d3b248bc34c325eeae49ebb692f0da26ad100100ad5277550036e6e8b1a0177e4605a0c97f4d19fe53eac04590ff5e289a065c42bedb945a0363148946dc8e57c4feb25a5834685022fc26cefeadd84aabb9a2248b5a6d97093bf5b9c3788048584cc8d7219b5750c15141e81cf69ca0ef5b899880896ad60ecff716de91d0e850028d0340dadc436b9cfd832e124c209d7f7422e749d0db09806b7c4a8f8bed07cdc2a89000ae99b3f60c605007fd5afa2bb85a09d020c30dd50504658880758d70d6088219d288d329c060083471232a902abbd129bde80964d0015cbe1f305c6c22f0cd91e6a4274a30f08906163025ca333fdde707980f4738249a0c28a28fc2d55e6cfaeeb3c4092a333e7a79f71d9fb475e1a0b00f0fcfff985bcf1caec95029771258c3f39f40e2983b51cd4d0ffd814df2ea95630e2e8be5886c28a9a688b2c5492e156618e65a0cb222d16d5d8753c25943afc8006949d61dd40078eb4cf86f36b11da1a7a378964b8dc768e42a23ab48215519018c88d831780716aad54932f376c70d60363db38358e631e1f256541d0fde8500fc8f0f6afd21fd67882c1b2ab690734897c7e3e7544506efae8b5f5b3874800b1dba5841e1375b7989630aa9ee336f5ea8ab2b64ec4ba500bd3bb956565d7b0965ee072f899cf5105bd8d1f3160dcde1631363769f8ffabb6435a31c53f4a60e8fb0f8b9fedf1f682fd894dac16d357d22981c6fbde7dd776386383bafe7250789caa5a155373b3e2b45429a986528198c3fb83abb6aea4c6b341d7d2ad98f099af66dcff4141cba855639f78e4ac36afcf7e7017081ddfdcebe1858dd655104d4183f2900aafe5ab4061122239d1d330c9cda593f5a20880d634f2a9eca4f0db5e8a107b24a94e94f61ce055068f2354f80ce661978077dc0065a4ebe03e80e08dca0c32cff3e780ab59800fbd09dcd8d2177e153d10267c653c71716f6170c78303c1a265833feb12088e197c10f9e9d74b79722dfae642eb7e0ecb3a0e1097508144a8ffe8f351cba3147cbee6299ccc817e62e904239a13964651a739d00ddd8dd223b4846d7a4639ec7ad6138e4c03f2bb3a3f16e0be3845874a3c44a0459bb94e71c24b43d88654550f4e195d4077b75454c3042776f2d6517f2d78f0655e8200c8b60ea552f1a528d2186fb2b90edffb7a746bc2720d6492c5016470d5cee5173978ffd7c545c14698835fd2bf321919151f7d643ba70161d76bf6d0aa1f943fb073891ac2c5d3c549a9985a9464b58568f16006bf64ca2c79313eb03c799062492c5338106da9fab472c055e585cfe6e5a7c532b75fd972eaaf3f902d897ecfaafe97a2647e0f278febe7c8097c6a060dda30dc96fbe6b5a12acee0926bbcbef6d055a1d45fbf601a1c2a53b33590364c3d698769422af641480800ccb35c5cf15d69a6fd316fdab38d9948b11ff0c30d67fece5d021714f81cb750bbaeeb0e044098f9cadd3ffedc968d0f69c9af57b272477bca1babeeea8fad50cd58a5de327fd410383b151e368eb4894e89b7a4e4036d8b0225951b7a403c8052e39b38251eaa2b368a24256b7b890ccdc5f1c65bfbe995724531768170c85074505daea189cd1f5fba66e9b6b9e942df10b044b90a2b7399f283093f311660777857eb538585a62e4da9bfa358753023c4fcbfb5ebc46d4deab4d566fade505d0b0a0588a30ca26ae86aa75efa272c104b789ed914309d34d87b71d3dc0a1036961cbafecb2b958334b9991c49fff6c191f573bb8cae187f4e5b3f498454907716f9c587a4db0d95dbf0341316249646606f92174f63910e38424923ada0203657e68b94525a1b47d077de0be609fd571705e4f199f73bfdbee3037b3f936035c57910e100c9b0aa0a91ca22332093d72867bae3b4bd8bb6e6c1435fb7a710636d3e89a4b7a4ba6758888134cf1766b9b531bc46d55814e463cac085da1be020ada46543d21a89182dae35b963ffdba840f37723dd55d771942c592f9a67e00cf07a8286b336df90dc5a74274e726d64adbe1f384570df17ce25f62eda6ed0904f4c968ca8d9926122e9881361e09d6b0ac02cfb3b8f1b2de54169a5bdb2b01732d354e1bb44949d86a7b25e0039b14669f5c06b1698fb55fdf772a2f46af0d8385e9b478f45540cb4db1e1aab9909efd0d233cc061b9e65dcd9d9955e65900453bfc599df97146fe2aabfa18c8d77c8f0db4d30d463fe55f12f073973e660604c76affe052b1af3189359e756be785226d5446e070626d301a24c5338294065668412b9cff818f2f6e259652351b27d12a91ed99ebf793d87f099cd4d6320616ee339293f6e664f3e52e37b062fd0f49b91411ad1521905b0d7db971998507506f3cf5b7a28cdea9994e1c10d18e7604460f928a34c9b3d32af0f20b6c8e0f02c061ec978e68db8092c6bb90babe92f4d297a4626883d81747d058a936e40e3691128c48921b2b1f188f10f516463768c1c3f88d014d3a0e4bb56304821e0e55b4679b7639ce19d25e64457cce5996c1e9c9ea4ebc627db92eb6ae0b96a508889d41cd7b5acfd5523ec3c050ad45c32752ed382afba8a299cd533f4665cd07482e22d307a976ee284eea93bb62d798c48c4424af384233063c81953ffda4025bc9e7c2a8f456568395bd7ebe233d37adab257ebb2957066b73226deb64b004e9e796ec6f9a610be2e72124caa15a42486ef271c7ea8f9fb5d333452322e40d15fffbe1bb053849d160da9e148dc83cd4b8a6738bbcd7285498cacb7675f706f0b83c35cb57bc6b5237ebe8bb63cb2e4ff54d909bb8f43680463e29c52381069cc8b53a4f47e478d18377612a66fce76d6cd20c24cbcc9e52fe29da8c710607b64de1134c59e36833d011b5f0ce062d80c08a3da56b04ebaa5b53063f27f9051f0df9139d1778d968370df01b28e2b49a23bb7a47ba6a380d13442945feab00d1f138204abe9041a9a075aeee79117196b6dc76e58941c61f6d678d23e9510ad69db2d651cdab860208fbb050b070aa551f31a5bd922c8e0c59ad3adcef0b0ec0bf9b3be4fd958d031c5e55b2ccda2a0d72718c55be4928112ab32857acd304a669a8dcf5604638dca14ce4c65bcbd42edd5355ce2cf49a1fcb223f3ed76a06291d1cc75d28067a813ec9fc18da88749e75eb7e83b5e4fac987356304a98a04f00724c8a782f255b63b81aeab877e636f6427e755c685b0d8287c61641cf104a61d440d5c0706340b40b5cdab599dfd41c65083a7c1b23f9baf179eee3fbf0ab846439ea5022080a3fa9594dc9c23e7e96f3a0280e8d8938489a05ffebd570e388d6bd61f724af26fd44014d4c1b48328df254d0f556a3a4f538cdf824db805870f469a65717f76abc0384d57321d37bc4ec7e3ba3ac3d7cc86deb1e2b318062048ee9cd40d556d410157816e6f98e090380462bf50e8cab9f197436fc7ed06d44fb2d9c4cb8880c870f9e8c4f0e8c55d418c809287964044c9aa98db73e509feab29b8d767a656298d5cbcd09ee0f474c1c564b01fc13d60233bf5eadf820c17b313ae0aed4c3cdb17d483131cc04366d51e13dbea36d9bf1c77191503e7097fea256fd8142984dd5bbdebd9e0feb75ee5e8111e5927cabfe7df605e6398083b212f84fb91882661927d2cc147d2551754ca4737b140664da658e7e2f35c02cb5df008f06a710b1fc04908022453d75a5a0314094b4074124aa1af60af750d65bcc5b8a7e18c6a5e1ea2b5f18e8916127b48849c87e00b22d163afdcc8c7099e44037c10afdccfd8f68a8a3910ef29a19f587e2f55be048fedb1473b381d0aa5cadac0e2d57692b7ef227f3b004cdaa0217ded09ad3ea4367b7d43532eb00f3613b2b7a2dd8d6a9a63d63d81142deb06cad6e6b10a08593eef35257329f50814cdebc7db7d31c053c1b9ebf46feff7bcc95d23e6ed7e4504177dec8ec8b20e281010d43d7e8c3ca570a583301264bc097951cdc1acb9655b74ebdda0b69a017939f09daabdb103aecc0500c3159f0f9e0548d73a49b5d2251b106ba168870b29a724c1dffe290b4ace87e16d09a4f8d23ab45cf52dacaa6a27c1848cf70e07ebc097153d8b05ac9eaa30195fe1da0c01d05e20278a621833febf2725fa9204ad39d5530385d4d83d45dc7dd705ab15602c102bd130ac6374aefa5afafbb402f4cb3713c84a74a2772bf4e94013d0135be5170ed98646c74950d8c020a3120b9792d18f28f8af78b967ba38811850655c96fc4ba68c20451049852e03b88605c706f48e082849370a22831189ed9b0645922e4b7418bcd96aa16a2c761aee0a4739c2b5b702768e5ac721bc9b9891bc9a4fc4212eaa98848c80fbc785081c0b385895b8d4bda0395d47a93c8231b629aab1650b3efdf5d8147a3673100e7f0b1b50220a356fe779e029dcf013e8657a8e61e092baf12044bcd0b5d24f8e13088c63b3886d4ff48e60154f3ddf989f3d366a6d9d2ad395a560c4ba271b247e043d8dd6b2a5ef9f7ae13a12ece5969aabbb2fa14e39520ee471d9c00ce913e30fd72f6cda745d968de44136e36182220a283ef990561e2faa9aa8356c558d37009f3404aed26bafde0f503fea17039b2b46cc8496d272fe4ad89ffbcf650f1002ae47de03258efe50f057888aef23a79406bf534483a557eef1dadb5c06cdca07094dbdfa733a5d7e2f71f933d655d043d4ad1e4ecf49f26f759b3c1ce59ba406d649a3200ba982608c2f9ee9a5dc7cc6a6ba752f803b42a423131b9db7f7b700dfa8b92b810421fdf366b8103d056b69d3ab120305b51770f0dacd4bbf19be014753ffe6c2abe1d108e52eea86fdaf92198514e01df43365bfa8193acbf372069035155666224e5a7cc64b31a4fdc5f91b2a10bd4997da09275397bc71a5410e61547d8ff6487708b8e44ee61d9379ad35deefeee64f44b6d3d0e03108d03e0da0d4e471272d319b2da8f0108b0e38c3ed19ee4fff65d5c8054b7d796b0e9c0d909c9dbfe530f8c60534d0134e7c9f0f576744834f63d3c9fdb44a66fb83530641dda0a0e0d9f33cb7af6bb8b11bceb1fb31faa19a44b1a1e1fba3d3d468a80a952c6bc72b9175a4562cf1c3aa574bfbaf6cee288c189c5bd903642363ac6a07c571de5cbd893a55036e5384a129890f05c02e3f28b958c3ff2fc2b5350eee0a6a3e3e6d88b7e7d5e0b83a95ff1617cff73ac72ea63adf64b2548fb8eebf9d0fcbf7207b472e07c90b43bce8bb5e6b924d4e6cbc712c495d50737f95e805420a0e7a6333f02e3a7a54d97312a51f062dc4a7dacb7dc29fdcb76461f01364ee05134a6b648e9f4fa87045658632e86c8c622b91a5fcb56583fdf247e61d1f650c6d3cb7deeb06c93bee2be3b9d48d40417dba6e1f99c905c176f8ee36f1311d01de4741644edcceb073c6e8861c40f7d5e6bccb6fc6b2b016f65b022288e12a0ebe7269455bdf1d2843d3ce48b5d5881e009d9a9795c7dd7594b498541f24700ccdf4fa1251678fed07aac32315a2617a349eeac206b1afd8d504411095f33d084fecc6e263efed0a91156355c460ae8f0cc67ec53b861c33100a8b22a838790d7e70ec702c69dfca3d0491fbeb5bc935947640c1e6814471528c996cdb283e0f false +check_ring_signature 2813d4239f3efb3afd989d1789baf965ff804e7b2116a45c90c9d70d8e832c85 bae36408455df2bfd0e85d333a252cc3244fddc4df5e08b49c2afee8f433c1a6 101 1aa3a1e1adcfa5d19035b11b15600e7910b5e633433c88f7f08ab6e29e93c2a6 544fb5a9847d33cc262405fdea130c6723dc46623e4ca2896ce67f6e77c3a1f2 10630d847d847ad4f6a347e194f453fce321247684c683ba84a2818c1c9c1a60 117dc4b410d7f46a7e656e407b6a7d07c2565d47512bdaa0fde836154cc0a5c3 169ad410e2059e86e2f73e7a5ed0ee1b99aed7262dda7d9e8fb60107f5ca2807 fb559689c2d0908e0261c438b62de2d7f24b493eb953dddd073a07f457300d36 66def61df9192599193a0bc63a0b9de4a50ec44ad2ad621d51795b191b3b076a dfce97dca50d887d7421fb11c7a29bc866a0e5fc4a409ec9edbe70347b70292a b58e3b9279a19f31c44c5a88f8650426f6ac40a2d82f8c22b41e87ede610cc08 77d7c6272999263cca55a3bde3796b8e517bb9b527ba81bcac08e8c7f70c3d85 522d326d2fad86582518ab461b6f916f8c99280a87f102439aa65cf8762f4242 2b89b56e892162d0598b1239c44f373b82a69f61b08ed058eaed89e913650da7 bbc76a64c86afa108ddaead57ea0ce085d3f4172eaeb5ced069a8d80d1ed8346 e371c1dfa4193d7873f0b58cde6444dff90e03b72fa47a494b8ed7c0230016a7 cc5ee5fdab16119d33fe32863625eb83f5beb6a5cb01e54d552b6edd3f9b4d4a d8fb374d974a0fd9d39651c7a7afb4af16f44a5fef4b9c8cf295223ad4f5bc18 440d0f10e5fad46b989fc362fe4e218e7b1b6a0d2c373299e1e65af0572c28da abfff803fd764490cf8a6fc74ce856a9fd6aaf1edb6ebcec19bb47db92b23bcd 48bd7ec8066cbef3eeff19c25862545c2c1e4be2ed8b012a260d9183beea5657 339c02a8c3fa98bd13dccf8366fd4f405c5eed8537afe2688b9b195f9385c479 f6c457e356e62ed18129f4451af4b99dd27a10fa090447447eb84b823a9f6eab da3cc85787b74eb525f78924719a272be66cb0c6062d7a7bc9c41d88e9fd2216 6dfc9c2bbfa436c672a80392fcaaa56b07d3ce12bddaacd55ddeecb6ee5b8c44 d8b4b13bcf0cf2a006b048bf7d8ca5faab52559b7263d8598e27bc4d580aff46 48717bec3fe51f91cc49de34f541d2a86ae94d37dd643f5b937da5320f0f5753 cf48823f64f12b1e662453cdbef2d178f0b043f59cc31700f21aaa1a666b59b5 2b4fd04df152aed53fba8af42653931cd58c51e93511a03b30ed08dd9bf282e8 1b2089d8f1adc85114f104a787ba4377be5cab06d9f164e98d535ebd52446544 a1f66d77d0372af773d15809e75b9271800b5b77150bd645b30ebc4649fc0457 8b5cc95bd17b919556e120cfd7dfe1cdbc1e871028095d08f9ce36dd862e905a 0f35611d0050c24e04630328e0e4f27fb6c24b080bc641c1d070afe303d51cb0 22476e01ca709201f15f46c3e1d2cce7a10e80fb7d10f4421a5816ccbf6651d0 16ee76a82ec33ac71a77f02d2b3eb3e826f3e0dfe5331e56498e1baf30933567 30937212870f6389fe66d4f21a1331c6fb5ca81cdae08c12e2e27c0a39ae5564 7ed30c5031409b57b6ecaa866e5730aee849ba37fb85fa2c4ec1e90fa6a595f9 a1c39590a56bec2144ca61cf2eade3e0df472036e5d1a14446baab7f98856c54 aba948391385b4b8d6c00c4bebe4a6ca6131b0bb086229e9fdd7496d814ff24f f9567ca0181169009b4e9cad0264dda1afe1946f2274bb6e15593b965f31893b b493ab8b1970d88a1fdbd7b993b9d56334fff3d5abe9ecfc76bfe0298aa8c85c b0068861fe98ed52e22459e6feb65502e3f26035c5726485dea6320ada6bc746 d505ca73ce3360f6ce6ad7fa8c83cf0d933c26f86cfb18a055cf0e84964e36f7 4734bda72cab6cf1d4ac3c7452d76fdb84e80cdf6978394708110e13a7b45eec caa0fda7b3c8fbc709fffb0834be12f2a57b1b6e937473cfc5be601898eb8217 364089d560642108df70302c2921765a1e5a1bcec726eb3360e5724247f44cae 50be0eb6ed8bf51f2163a88ec9e6d37d600ee5881585457dcc9a3276e5c5746c 6f49f0fa5d8d5a422352d64b66ef097fe2cdaf0509660faf0ebd00f288491e3b aa9fa23f95b789cad4771326b7589761c15297eef2752c3ca4c03e6a0d29a8ca 684a334338408e536e3e433680d1ce578b79ce9e22a088cd1a159dfccd79be4e 2f5bd1588866288de7b3661d12b0b68802f7bf5a6cd3c94d10703f4b34340efe 2d05e1b031248c7c400342ee90f388f13e5848286ee247f12967a630c4868e14 47e88b5db4757ce1838835c4ddb133358cd6a0291b81b583cea9bba7584e572e 34f43c23423faf904596c41ce33f1e622c460e370c4b8c28f5c5f21158ad025b 2b17b2412f0ab2b4c45e231f1ba5020416003e88c2d2b47faaa73199932aa353 17f82d95bb38afdb169e3f29cdf59b25cffcffb2bac3c8860f19963afa09a8a7 0d45dea1fe4434ea99770b3a02e3690f6e4c3d56d73d59aa8b608205f5bffc4a 60ccdd803a94e2554e287677009c0fd10a8a9f2a1e1324dc8f84058a3c906c17 4b994e98bda84d456112ad4e9ffd4a273ee8b9454551c4f51c4462155f12c623 3f48fdf1eb77ae5612fe44ac53cc3d2179a8f90e8291e72a5b805fd285245e9a ef18a66321623bf90e883a23070b9492c85683d636fd231e9b5645566f9def8a 53fff481594cc579569ed8aa9f1c28626bed1e82c710d027fec00129d1968f1e 0aeb17d033f6de1f8ce9436c26329559f783bbb67edf2a774365c8d071087edb c0d6b5de2a78fe4479833960e375110921d9b80a8d90f2072228d24d4c58d276 52d3c2018397c3178ce09eaf337f395ec17bf086885715b0222f59c6b6b8377c ab06c9a1a23441326c9892f7ea7b51b74c1b4c00aa50a390c3e7ec609a27e17b 19e67c337350b0ee2cc13c6702ed3d337e1f6857d87e15b4ae3cdc10dcd9578c 9aa2752d826c586f8443c1ec661cbc47d6628776cc089f90937955671bb2b3e5 ead1cf1cf7f7941f9e76486fa1af52452e591b8ccecf3a4b834c926f573d7db0 4cf94dfb423c0fd8e09646aef538b6dfd44dd25d6265eff2a1e1deb7b94dbc34 9fbebd6335a1e43601e4d4423d32e18081a07c6ea5aeb310090dfaff7e7447a8 d97d2828a80d8b8ea50fe9c785879413d426192976197c58c58be348dfd57365 46fd1485bd2d3c8dbc5cfa973e6b789cd8a1342fd2ca13c44e8b266602d83ee9 23266c0d3021ae2c2a75d474cbec6a047453f7caa2c77f21f023916c15af66b0 e4a7067fb8838de0d2902a20b52c28eee8218ba64bb507fbe8810cc425c2897f 496129d3010a881536ae368a0eb7d5237fad005dcf5cdb2324e20e822c4c8e5f ae94396dc8fda4bc4de9b54af8977e53034794772132098c5d989e1ae0ad8663 e353b82bf50f62fb842a71a68ee3b61518e4998d37243e4f146460023bbef7ac 4bd25effdc86345779f15f60e9372f0c86ddd036ddd7c51fc26a26d12d886912 f156897537a2958950828e70f303ae323852cff9cf7ab2547a61cdd4e627a1d1 db79582b5c988aa13e54566c214eae56cd2d4f892603e54cfec84ee829b9a34c 5df9e56c3817b11d28aa828ebe9407bb3fabb69c480b2dfda72a64e683aeec66 15a66dcb30b30d7ebab7b44e1f7a01eb31473902e9f037201c822769f8cc6e0e e48629cc25ce53b234b32b40b63dffd960cbc0beffd7a4a6c9ad27460a7bb1f3 da381502b3b9b80e17e8501ca7d61f7ab6e326c3c2c6bdbe6afe168a76bcb419 c7b40353f1c346e3f4510c11aea7a5eafe9cf5c56a35d84080a8214bba60c31b fd83d96ccaf60e3b98b0e6fd4954db2aabb832f996e8a127e7d7686d6af90da0 78deb08ca1264f1d7d9fcdc9dd08eff459636383f0873d9dd023cd57fd537aca 3f30e0ebcf5f63c14f585c2f22e42502b46adfd9814ba8391fe9adc7ca458e47 ab8403a601daa983469c3ab834b71c56fa122293b43f1e9980de81ffbd876a26 6ddc08076548fb0b515969cae7280eef26af9087adb53813dcb330be111bc498 a95340e7b79f4e94ce4a08a96fcbbb65c1e2ecff924326da9c30fab067b02b0b bc681b497a28d0c9479cce72a6d7886fe77a91a24c9b02952b59021288a77580 b0a588f469ddfee477661d32cd1987ef4279ee838d0be1b9106340f4879014bf 95124eed69890cbf7a663f466b73cd0da49b5ebf53143b8eb58e6a1b464b75fa e9bc77e38a7f8863f1f9449f0b5dc898c10fd26a60cf9e1c238c71697eb62956 5874930496ac37128271a5b1979ccb1630373faf013ffc9b9e0af849d245d100 7bd543c0b8893799f0057b996825546c1facaea299afcbded04074163f348281 618bc070b41adcc3cc1ff966b2335023be562566b5ee3696412496cc348e9c55 596ae49c9d67c68ebc3d0bf9cf02275b409e5b677bebad92543fcece83317464 5c24d271f2bd71f616fda204a0dad2a46d0c23edd5fa97cd2f4b73b6e40358c9 795bba0afed1b39bee925c07f1e7ebcf3e3c81d72e1776392b8290f14ed3dabc 4cf8aed88fac58f2b0ec2172a63488d7d4dd7599e392e5ed6e8992e2f8030c16 d9d6366f51a49dcbf0b4c9b9594605920f087855128cbcf6518188b1616f4207aa009767922f61dfc249ad30f2a99bc8949c73bc7055d6f3d41bd5f620afec0ca64dccbca6fa0c584fcba7cfc82f7709e5656268f7d8349c63b58bca4479f40c281cd95eed309369edf609945382af3319609b87ee2ea211d8d3389e1cd8d7050394f941cbbe012006a8aa4f4f9f0c7e6421270ed1ffb2f4659e326c209206025deacefd2fb9134fac42533313396e3d8aa1aad84a26496496edb3dce276a9007fd2f9900c1501d5ad37f856dd349c997fd92eaa6776bedc9eb13da89cbdaf00dceac4da7ca2e94233ce626278d6eeaa2a31171b00914bebe24831db5f50780c11fd85d7fc6c529d92c97667e6794283711cfba181b523a4c18a7f0388e36c0397ea921370e204fa832573c6b866f262726f3dee27f8401124ea45a3252cc808e87ca324b1ba6c78ef49664f3cf3f28004e56f14829afe0a17ed7383176b7c041c9a752a645791b7d0d527dda05bdcd184111cb4e88bdb04a4921b142ad74e052ba6cd69ab713a075d3567abe56a7fefa876fe1000e51e8b4d61f3429009c6072ccee63f0eb539df7af87e374302cfc1e22ed1f5ca3eeb3974fd56ea0faf6b03b7d982a1641fddb31675f4b87460ce9cc69805f95cf753d237b3360199720a02e902f64ef214675ffce9867e6e43176176641966200d2c5480e96c669f9f4d0ec6c66cdb0503c136c3fa65a725dc6fa297a83979c455f0c73c6d659ac05b2c0de3372c3e5ce3e37d64fe7f50b5f812f721a57049a5a3b30a95be2ada4aac6a06a97e4b79193b40d3ce1427f1b35bc0a5a43f1244de4bd9ea728cce0efb024001e0f074e86f552e73d4dce1ac30e9201519fce433399fc35a682ddcc0d356bc0ae613e4268c96061de83aabbcb79c74d0d625b343793db59e7e5c0efd4536020f14e93d73a499e763ef69360e765d724bced8b3316880776c2e26f9c9592c8806a4482ed7d541fcf2d81d426cfe6b663388100cde9b70691ae7b9261c96c19103067d96ab59b1bde0b780aaa1919fc7a5df368ed36b7bb323d8992c7bdb174a0d0f1142d4828c3a13e1f748c52e6b4c145d8035e18441c85348b44f58a9b226074f5d8654484349358eb4aecd0aa32cbab354c4787e7dd1671a72619712b1450eccbdadb8a8b9ec3bf9452b4071c10f7d119ba2e81e470c8beb3e27435718c207de6db1303db70c8be0d19c410a3d6fb31c0542f7cb39756f05b226cbfcc77e0c190bdb34d44fff937e6d7c97faf40e407f04bdad7081c753f24650221bcef00ec9de47c8d90fce4d6047a1caedbd0446f309383532ab4d94070e9bf7a6f9350178787050b6010e5d43efc38731cc43dd2a93881d26ce76b2098fb955ad2cc60504447a916796b1ebee11b8c8d1fbeb8f25eeb22c7935a3dbb0a89ed068d153009b04b9049612f7df1682d0bf7100c1c941d14056e2256a8d61a38718e590f4098a7919ecacf07256ae3af2567ccc0c1482cdf02425ee03d70e3ff5196c216501064b7dd82e18aedd8b1f5e1a43235fab247c0ad8e0003ad8e26a7ca8a6517e00f7f3a56f0e0ebca95d6068796d8695f3920ed9e4681b2863c84b2da308227b039773eb3b0450da2273d170e2277a92d16b54132b2d882d07526f9d4bdd31e406e75ac7d702e5eadb2e94d0ef52ce8867c47013e25b28a24fb91d6c36c3f4410e3c2fb7be55742444a89ccfd4c305a8eff299ef190712542c247f724a6c3542076d8e17262ead9779ae3e59debac5d067d3687afa852bf6358f3204841225a5000484d502b48c051d966061396d26f0f8bb4042cdcae66801b024ea77c2813e090385401ee71ec0566af00bce814ffadd749528d6248866e0eccc931bb2953e0bf3fa568e91109bcf471c01753c23ad07bdb0b85d4fc6dd393deb1a928bd7d501f662896463dc0f1334f5992d3d3865675e92c453151ece42f39ba0147dfa3e0b081046349cb00241f855159ecc979be53dbb8264613f5338e88eb779e3fd5b08fb091633b853903d9c6c796bd8585dff5018ed2d1e10716725dfafab96fd8e0fdb1b08410a711378f0dd2ecd947da4236c31d3232301a0404e96dd711ee3570218efbd0c801a1d4d3cbac7d4f5591ad8a9d51d07ecfb84c45c157ce0edaa480d112bac7558cd2b24d67aff21e90b95734cbb256f8fe16005659328457c574b02d1191b11522dd46049066e908d5fd0e1686e80b6f9e6bc39c1fe3498eb69e00a14299d2d5226a4e86a67292556e96d009af1b33a20db9fdb65b4806845b9c20671ad02fa227daa59f367e7a6e58aae27b60e1ed4ed834bda32b6d3eddbef2a09af7b74c2521eec1ea660381db9d2eddb20a3ea18045b63b7e1f0c98dcddb0b038f6a914dd03586096e7b8475c3e620765daa145ab4c3742f08020849708d08096c8649c9934cb57a7e461f8da2aef61b8133cc495f53e1a13444a6243419e20c46cf787c0af8db839762060ff8ce357f9ebe4a56acd83766eca4a9ce9973ca03f354b05c04f5ab654e2e2b92856c2b91d10ae39de6579316f2aa4fbbd2604b01a90692278fc3ca45204ea1e5b7b731801c58a7f75a619b9b7c6c27f993ee310c329fad8ca63c2d992274935d2459ec953fe0e32c999c48947c02d3e6e37474083be29dd6b4ac7de8686dd116fbda862238edd758bee7b4e1172e3fc0e3357c05328b5592f71acca1c03601c7670a4f4189b1e8fdc83dddf9d77c893c42bded0236ca4d60ba6f753cd115241ae1d73298939bd202b5a0821729bcef62847fd7037be8c99c719e08ebd5612ae2a52106cbcd7dce8178dcf41d8fc81ddd96df7a056967f1ec7f4ba7cde94b5fcf8582d5d0242297c932c54a7b75a26c54e9761f0981df08937b4101f47fdbb8612e8fcdc25468f54e0989e10880ec4b9edff33c0bae01bdb05188a4c83f9ccc6dd0b9df952863f3759d597c4e8533c8d9bbdac300dc72d7846738684a31eec5657b0a3a444f42f12b32ae8d54387d261437dcc40ec3de47566948647363e45308dc6da6850a63e1bcd23722adf49aab409262b3026ed3d0e03315d5e136749ba6036ae1c34350d96bc43aee0c8357a4ddfe910508b19a0d5b9d5236c9c64907245c14347731fc05ca9fddaeb816501098e7532308ad59d6fdeda74449679379d01a85be957c369764f132a9ee61ecf0c77486fa05a8d470ff1c47253d7b41fc7f43d0323a64ef640226471d5b9baabb9609db2603c904f609d601ab85197ff6bf909c514188f7888a6f8bfaccc879d01d275c8a03a15ef371b435896e8571f467cc4e35fd43cf971c13592981d7d25f642d707202c7436a222d8fb566d048820d041b3785e62161c3e98b35b98bffa1d61344820c94b907b693f3a04040fbc418bfbcdc7bcffc62a44ddf544534da05721a20480a2eaeded7b0ae3471895790cf2473685c58c3ea842316b0c2fd4d1cb983740f02c362f4141d2bfde5aa962f5881c3f98ac120bb3fb59d3f094b8715d9aebd990e068d137f6e1ca093c6b701da2b3cc36926d0e961753af304603dedbae2fbf40316a85bc26d4eb47c6177d3ef82b439c2e99901c7916e0b0ed01d52cb85ddcc0c1f8edffc63051e6b61675671101de7fdbfb63f06e6074882809fa648049afd0165ed0fca947aa70e8f0e1b555c25e33c477a077e678a50ea723557fa9d13530c809994ba4a54f884fefeb1e5935191a747d23631c0ea11070a638f14bebcd602e74dbec80739d057da168f82fe2a748c231db006b0f48a68b3c2242d9542e40631d7600bc56941544482c6f85395469e75f1705e195a9c0cd499c3f7f2a0bc076916810d9f27fb0a2b9e525e4dedb791ac774d408e54265396b06d6e846aea08f2d98100b4664c923f2524d6aff1edb3931f587470b742105a8265476eb1700d89b9ff7e82b9ef5b6a5b8454d5021a618623f81e5dfabfa064a87ffd2ece810e55abfb622318b5b5bbf6210c6bf5f78b2c914d13b8b31f7193b3bbd98593ae084a02ebbd07ba1e7bab77b68a305e4cd241a0a94e0e47507d0097d67c89f97300b1c18f753c0bb9163fb118df858d6aebf36e3b5faae9079332628996c357a8032df70f934b8dff602ff245886833bb1be625c89c913ed6dcf56ae58bbedb7f0f6f730ade08512e9768560453db5a93f2b27769ae7ddf5b748102e968b4939d04c4ef26b5770225eef2809f25b30d9d61b51024d1d4574afe519939b2b3c4a2089a479d75859b99860211d2b3c7a07c9e8502aaac958bed3ef9b878194758db0158b8bc170d69cc184195f956416943023f6216712fd58c79f81c10c17921ff0d99181d65764f63a2321fbbf26482f6a421be92db4ce5de4a10fd4ce8b08f36001e4b05a98e3ce2f16039c91b00c68c36071b726ef6919a040d59be60502a360b154011334219c80b020a306519901803d45ad3b8c3fd11869253b47307356002a3c2346a44c56e02718a8bff6b4dbc5a72ef9e483b17f1d2090451f53c5b52013fbeb00c494b791dd7acbdf38b6667998ad5793a6b63c19e39856b283b63eb01d2aa1888df57c4adde7b2ea22789c6352fcd7ae10b552ca0fc7a0b3548904f01c64ae16963e9f09e1b025a20d032b7ed17f3f01915072c15df4a8f819d00220d9bea4a6b0e0e91226863aae55f4957b8f749451359cc7cfae4c5d7fe0dd5210797b8672d27dbd6d179fc5347262caa1ae92917b8838b1f1bd61f2c4fbad4280b9ec657132f896060e76144b37d4d505f47468826d46fd3dde5ab0a9dfd55280cec3b094e2203805843ff41f81155f1b08502148ba26cc6928ed01a685c2bf90d8123ea951d9c96e0657c4b0c5836a5adef04ed0ba3b3e7e5f16a4eea7e0a820cf2fc40668dadf318e8b2b3293e129c68546d59a50956788215de5837a2c9e807f614883fcf00c875edefd672541aa706763558db8c8123da490d11e3478e4c02e03ffa239370ab7b8e3a0f7dd6e1071aec071f8460496ce8e54116b45d1d1d061ce54c2de90948a7a85c061e5a61d7c6f14e56ebe247291222fb2b0e4533a404ac8c1cae131627c791d43fd1bbf6db3bb05c60ae6947d300436bf9161ea13a0fb6d99256aa69b2fb1dd2c51c9b54f56f22e7dab39fb8f7f9686de788aea36004c25cb74b59af964e713ad99db7b3a1368ab977f144ebd47071ddac3515535b099854e8b8def9e60b8f574570cb8a9006b3f00e80a1a745b62a3b75912fa47e0f1d8a67b7c5177bb6419e77f3f9f2e83444da04ddcfe32d5ddc8b3074fced4e0c0bb7e6bb6450df5e4dbb19054722fa9b14793646a9d0952df56bd8746abcc20a66df9cdb631d8c22b5f16ca3fc71c83b555cf470ea5fa7fcad20b7e440abc90a6fac4158098188424d6c9d974df6ca7216814163f19fd67df5b0cdc2565dbc0ef1f1ceafe3cae371df151591b140c0e978807d4143027b2e672990f1aa363d002d41b70550085c50a91f38c3bc4e2c09ac429aa08d5312b083093d471ed826083cca125abc6db713a351339dd852db2162915dde40af746944668e7d550d5e0231f078002091324d9cecc240aa012c7c0990ddfa60af57708009731496506c0ec4d07fd3bab0a2a7df0bfa570f715821034356a84f06b7fdec77fe1f429aec0b326f8c2dd9edbbf4592ad20c7dbd003f3384909dcaf6690dd079d233b7d05102ed41ef122e76bbf983c36c8561e3560ada8be9362386fccecba485dafac5f5051413fea587d7286a0a4ec78d8a636eed3ba5b609afe4537a818e8c59a13cd9083ffcadc8f74a30d3ea3d4f54553ba44cf9468aa6878edd6ba5be54bdace9ba0f0a9d1b0719921f7da87cc5c27c8adb55437dd9de7a15e78a1ac3f09f29a1cf0cdfac629efeaa1fe2e23b9947e7ff69643b9ff06f3266e8115a1b3c63d5700908a16be139f4c5ce54c9f9943e13af2c1fcda05ad5aaa8b8aaab3fe6b954e3f50fea8bd0301d3159a3d079b11d328ab29d8f0a6e4f3e8d1bcce43f22030edb0704425b497d8c33e73c3c644a1cd67578c46e59adf81beb554cbdccd51450936208b7c5f2eee0871b8658114f3057720af3b27f32091a51d783c652298f315e9f0b54da731367ebd3f5fcfa9b8395b367f0fe7564ac09c625c036052e736269310cd4c7aeda04f6f15742e33a3243194894e8963ea7c38d3758bfb2e77f947acc06fc8308bbe3372c14a16751e504853709c8ea6fdf92e36eb1caf3a17ba4a3ee050eb869471866ccab491d142f682d7f4196b5ae568a79790083b3afa9cbe9df0b58f788a1c2e086862d3181d14f38532b354e88f4cc6b50ae871656c25e1b6a0ab84d7b58f6fb8a18bcfc2d594d94a6cf62852f091e53b460bf280ecdad66610eb914deaad454b5fa5dcfdaee56ef29d1d4ad0036472f1a1e9717b25fa322820286f89e34dd309419dd8ffa0602845ff11ae024cfe711b2a8ab7305563e1a1606e549f1e3ca531448123ed0e7c3be489fc71b44676557983ce2f1d537a456380098bd575f8796f2871556b441309f4d3d384b1de10a7e9643624927d61403cd03b8b496815a4fe07d198106c6881cd7ea0edcae18e24d3425842206845823960a822f68dfd0fe942a778c485148056ea2281420a90564b340f1d2997409a9600adc1ec4f003b4dc40faf2fe70224aa754e1f2118d597165e6affd0854e55b8c079bd8bcb463a4d040ec6a7e92b1d822e3691bbd5d4c747400d71ac56976afef0557dc29234f119ed4b71b12accffffc90d2e02c26c7d16d7450a71404e5f75700cc33176b40cf4f391f3955d6ddb8ce987f562e0834dbca1023b613b59a48f707e4fa1ea917265ad4e970b3d9407187cec0ca80ace79dde29909db97883ef510fa3c617539bda1277bacb343a216747bfa8606d79c1eff98379e2bbf9d2444805c56965965b83c10355df5cbab222366aa149fc8e1d02e3b8dda50843dd66bb0b88fdd1f60740f4eacb4e877181eb021c50bebd6dbcc33672957febe2409a8402cc3d4e66969dd8173af8a77d1f9f37a3d8e22bcf739d65adbab7c2b82062310b08e86e8c3756d741e0e916d490247f8ea32e064ea5d8e5ee7c6ce4e6f1e2f50bbd47d2d5b74ad65fc0ac7ae71d2f972e4b09daff8fca942aa541de8ea075020849fc45f435ce720f223477df5bafe97776eadaf308fb3da36c4cd66ae975030d5711b8d6ff0d72927c4ea0d6bc12a6e7e838df5510ae3a68a0845608a8631e02ef35dfa871f105c0e6fcc8af13012e08c61e9ab3a5401111b60c275531241905aae9781148ed12b4ff6b350f255c09ee4bca9a916b2f51283f296c781e1fbb0367b060c15274f654ddc21bef2207dabba3f1f9850ed429b324d3ef08ea5324018700793fe6980b0d50d7ade963fe52b476d9d251304dfd1ba6710716deacf60b0380a02d1a45d24ac8a8c2161a655164fa5108e67b87d3c6997cbc37a9f489021934f0d54df3963f4d3eeb573861aa1ba0424cf5cd0b970da31d37744de45706ec001952107b66f6fc58541110d43c5a392aa439cfb868d4719c857a60adc20c00371882d0d258258713be5ae2373e3a32448617fc6cae4eda504f189cc683052be3512b0102f341719d5fd2bd7e751cc87b93865f7e39efd5bddf94c914ce0b5be6a30a143916bec1b33c22f6f17927504d3a534aaea2b5865f904bf109ea02d7765970c7f70d12d50ac7b01cd664b808f31096c9994d99cbd3d5816636b302e7ee1c26ade55d3e12c925422b7d4c42511777d9d7eea846adb8bbb090e5b403fe6ae207db1ed40edf6416103994d935c595213f956c8ccad827bd5244252103cf95a01c6bc1d345f143477e5fac31081e4e9b23109b800b12df7f972172a506c4a9cfd39efb8b41bb8c183a4a171ad7a7109f7680f9dbfaaa35b79ed180ec091dedb9335fe1f6ca93ff646c92173944eabf36e8f6a445d5624c4bfa6c74a90583880ac2ca43cdc9133e80b1cb6b37fa70fec0c176f817789022c81bca95f90166a9326def2654354a7fc616f7ce064f83e3190bd861df677f3b3d037fb57d041fc0133ef4325089c443a103be4e0900b368c47056b387b30401805882648a03373c7cd05ad59a9cad2f49bdfc6fb0132b1274b10ce7046833d89afa1355540ef6ad689226db876923291c732d92a8ca1aa36721cdab55f64dd88c8f1919640f889877435e8af6d0f73d738125fbdc2353ca16f6d97bae76fd683c5d10a75c04bb7db4ef43c10d7070bb81865cb45bfbbe207808f74a57170c663dd4f431f207bc4e0114fac81ddd29da9dd20812e84ec85e57a8952e3103bf40d64b78a16e0189913c0370229723dbc768fe9d0552b8d6baab192fd5f0d3702e2ef271a3f00f9def60354a9330e24410bd55aa43ccbd0a97d21664e4aad1892c06f9bf60a104ed2c9395ad9dcf0302c375befa60c327162fc09c0127b3506962393903b69a022f3bab272dc3b0a007282bf0e5e7eaf6d31aea1b9507ab7e1032ed6a79a7f60261577bbfb4f09e34ebbc1578b95a37095e177e09dab61d6d92db0af04fbad30b1be9e9c770afc512b71e655c38e1b5f26f7443bca89799851a489a7c1a9ed502db258fd117bf0d1dc4829ae75d0cf3b37af6d8f8d4c5c55c05a97c2750c95e04851a664ba2a9cec9a2078e9dfdc7939ed857cf82d128abf6bfcef16484180a00c3972c9b4d742280ef5b0cb9303a4447b71fca3dfc519e5a864e3c02c0ffc807fb176394f3430fd05afc59954d650dfdff446a956811d6dcab77f9685d6d870e86014a3e9a77611fa0c0ce49ef1a35cdefa77d5b1c621035b61a339420b27d08c11efa3bf642e59584118e6a07ebb98e89597257a538cbf3a9c8763d766ba20c6f17e3ea1498a5ca654ec58218e7e8e9077e8180cb2137b6815f2aebbd9f6906f2e7fb6e301ffe05733a799dce8ca96f32ae93265ce7275ddaa9e1f4be9fe005b1ec7d6c5490fd156edf6f52a9aa816e031de695552dcec399376b824f12aa02f018345e4ef93379efcdeed444a43cc06c0262e985c8066dabdcc4054b61ca0e7c970f58f46f1d2fbb2810e759383f41a1aea9212354aa738b9a7fc3272a000cece727b22e2eb5f9fc3eb6f0362fd255d861f9be94adb9ba8a9eb04ab22c1503 false +check_ring_signature 9db69b9c86cc602958111020838082dc6701d55be3896769e3020fb2e4e4ab5b ca511811c5c5bff865d9e98d40cc9c0a9705f56575c59679c8ac06ebf65a70b1 23 7c7f37c11b065281e117b97b384812e859d07da75f1181215e1bb01ac69f7bdc 3bdf6de70434a65797d5114e6fdf959742a2f1f1a83e246a519ab5fb1e48d71b 480ee57dba06019154e101b294c2ca7ab709a380de915c26dd0b980ce72ffbea 1643aaabd35f7c5dfca551c49d61bbedbfa58fd4c39c1d69b5416106547b185e c3978d74682130cb43fd46496ccf4593584d1fea93e50987fd91bc563814d91e a158ffa3aab80e32c930a880cfab10ef28ac66deb0d90f916f49b4210fd3b27e 3a1640897fed4d38af8985e92b23570d7a1531613932a90194417597a852dc6a 5b3de31ac38cd495e9bc538d8f2a38a53ae08e3cd9582c6169de52330479a508 5acad38586edf2a931a1a80e6fc12b441c3107406acc37aa68892e25fbe053e2 c8408fa487bfe9db6cca70752905c19e6f1e93abb0d3df076cca1681e7c5d9f6 495bfc6ab1236869795fb578f34d4d542d722d39fe54efb42ac5719c4585d156 e4eb88a9fa51567aaedac3eec1eca6ff0744201499ea636819bc821e3d0bc908 6fb279447357ecb99dadce4fa66af6e96e271fabf5e06b2319697eff19ba06a8 6bd1ed7ff1667f0999175b1cf771616d45f1a88151ad280dce3179ecdc14f6b5 f49da64e5493f60ef153c37f8de47e8e780c0256d3815bd3b88b7e771240ac04 5096d9d4c014dfa30efa62b5f004e20c2ab79976e076b51000ea7fad441ac1db 68f55969d7033e426fae031cd6fe942f5616452c279b0995a5a162d71ca07000 50595e47ad31df5d188b3282c1648c16c2a0fd340e9e4f521890ac68c8d4f040 9e1bd40ab385e3b3b2d4a8583243a964b62349bf9743ad9de8e9524cdbaae882 7f7089a4e96a0f94dc1ff8384dd02aa69177af9da2b37689b7ce192fef421e9b a036dde55aedbb77ce22f672cf9dab6436d0cefa753a6fb2bc9bbd113e636d95 54644b440ba3177d3f5cea23bef906044e35164afbe1a82e9fe655ab34d2e80c dc8f04468382504159e5706f2837cddc6b1764f0ff19f078eed4bdbfa329b65d 72a91e4beeac11df67f2de1565ac96508148745e8673e5a1854049b8864be1003fe6f54790cf533cdf360d67b021bdd19ca5388b314e13d8905aba6442935f0b46066189100f066652cf12f1ca0f00da90da59ae0f7b790f33e25faede7fa10d8af3f23a7ff3ed1c61d339ae770e8d6fb9137f9b12f9340c3f44b8cb8645ea0a8e0602c1862cc8621f7609a6a1525bf719794aa127efee5ff48f8156a6c43d0112e7cbb857881ee456f075dff61077a3ea667dc66e3228f0c8e874aa2cec3e023317640832edeeaef2a889d686f8ef8a0e0df049f23db727da34d512b638ab0fe4017a3ee60897df00556d6745115f2aa2f1071c3742d20ce6f04a8daf58ef04c0562edfafd8e703f1142004e0e9a52a51a7215df2bbf8a96f071994a713d90ce33dbdead0cda72f1727a50c2d0aca2b1cbf06b9c6b19b9fed7d1931fa8215006f0bf365d376f7f6b4daf7a838bed42dac37ca2007dfc302bc2e81d70f7d0d0d1d8f02d13b9289a9fa482de71baa71699ab4d54dbae043778e0c0dea69707809f2ea1f1ea2c71d44c56cd21a6093c833cd39b75218af62dd768d59976168410f61b0fd9c1ef1d6d72dcb5b6bc12425d5013a8c17944d2e87d9f023a95f9c5d0175cd477d2dde8113a026822c16b4d373611b067d42775d2ef98fbfb5bdb2eb0eeac37b026e6e40d3383849dc265ee710a2daf4574cf34ec7816d85044648a50b0ecff608290b271096d0e81381cb97712ecf993d6d4929e36032a51c6d718a0a7a239519fa67c76eaf97e8ca623259032da3d22e5e78daddbdf6cb8519f1c1086658060ebf871d68f4d8a452d7144fb1811eca79d139b06ff5adb01365e1e80ad3f11c5d4f075edc95417a0b64c8bb31cdaa3c693ae295b27685ff32a796b309a440878b8899487966b925601832ba957b8da81f4df9b536ebdb4ec1e8ae36086b1682a70d196b142c870267c618281fae51e9924a2627035e94520dfdf9550de469c651537ca41775b950f53713763ecc2b94e1fd958a99c29d4e618de0710e08c6987e6f2b6a7f33b25669aa179fdd789dd571cb85777743a2a90b103d3001fe93a8cfd9f61fccc50f7c7631e3fa1d42ff29b0014d12b58c3080ad27b80901d94b6f19cb45c727d452c9fc142cab31235fccda49e6a5c5991454b1c3f2df0cb82ffb5b4d4367cc4435f9f34c55e53b46e539742194fc95f7d3f33e070d5d012c093d837a6072f18fe3b729c62f6e4b7a1242f24a11d6f1225ecbd52c5ca50d016aeff1f4138cdc65f3b6836254aace3aecb9055aee79daabe9d90c501bfd04ecc768aa1dd737e3e23e0758ffb0d3f5159e9395c0e16a65e0e62e9b1f554f09c0f41a308235ccbcc4e81c0cd5cf62ab0e161a5a6bad8a624d2d6f629d5b07024d06b73a47675f3a1cd95d919a1fadb72e536730e0ddd6ba9de157672f394907ff67c78bd2bab1fa034d4366a00ba75212866ceb21ac9a2031a693298680e609ac7cbd908c909083f74aafb7c7d7b91fddbb153bc4f40578a25724e529465006ccd3e5146133b1b137f1b888e0f222df09ee0e98d2e74918d5d9a258c8c32e0eee3df045ddfa0ca50163dd38f810ea9edb8994bad8bdab9275918a4172ef6104013d3819cac0569084029b030d3893d82d24a2cda863b9f75d3daf1a1f9eb60bd00dca0f9b0ee56bb3c7c7755fef2ec71ebf4930750828ba1bb5c218967e44026ad658b2606a178354e86b20c8de42c3b5573ae9019687693ed8eba26a9025086b1e11b0350b658fcbbdfe920ac71efcf3e5c70fafa981c253599c2152689701843a26ef633c47d2a9a6d9a8e45f065d79e2c4662def2397f6aeeb5bf7db3e0ed05458b1c6ef5f62da569eb2336235cf72a29a0cdd62561b178cb48ea1aa8a08a6cdbf8d3c4c746084cd2b9ebdcd250b516611bad02e49b5e5c3e657ac371a02e9384803448683074968d044a55eef03230c9525689f7e8ace301a2953e16b0077dcab72575f79adbb76c7c481616f0eb79f4ae12acdffd94b979061a528030eba17db2778e9ee66758dabad62695a5f12f6295bbcd5ccb7c50fe8efb73dd50a true +check_ring_signature 6c35174ab92de212798bc64279e90091c761f02e869661a41a752fefcb6c7b8b 1465fdc72f71134dc6164ba5d7a7d13a4bcc1633360796371a21fb16b9cf68e5 18 dc1344f4ab40df54a449561f327c4fef2f4b2d5e5c608f9ab8f1eb8e7acd9887 627ac9bb148f01fa25803c94836f807250cb84753ada2a649907b1783b7c98bf a639c9a642e9aee9a6b3d355a6b6716ff82cb442efb9655909d88514b722628a 6c6da1f0182d1d8bc56c382dae70d8691cae85d2ed067dc2802ba25eb656dbf5 790af22509784b323d06f3a93d15d06a4defeac4a64dbe48b17742f7a8b63e6b 980c0241e9953a0a7c4e2a254dab28c50ae17eef77375a167397d94a10be68ea 7e6c4fb16145390904ba3aa9ff14422072fd4ab8c788e3f6421927531fdb3950 5389ee5e8dc29bcf2086ddbc04d12bc12e9b627e445b83892c5bbdab9e7acade 611adb22a406c6c9106d05db314a07cf269e5e1da86249f5c82807ce82d7a39d 6ab134897ffc437dbae5b50e7dbfc3592d588ef7806e4ad6256f9eb74be19683 169ea43df42fa0c546a67aa89ab7128e3de5633c4e1aa1ca5880c0b4d09ed10b 946519ebc5c3f8028b04cedcf7dcc8c6cb3a63d4cd3f7c0e4d4270deef18f6a7 ba1dc31ed6d039ea10db37029d3e80364cbe7c3309b201e3a0317a47db2c5259 d23ab98b49e4b9288710c4b30293369afc6caf3afbae4c375d7d09ed357aa506 a4e11f016467cfc7f492e703f67bf792381500eab44b2dbe239337b623b172e4 6fc0ceea28d7aa24619601a32a90ee2aeed0505e601feefd9c8b80a1259ad1d1 b2e5d5ff479da94da38867f2b6bcab4cccb46bfb7e724d0d698d47cbd5e99fa1 348d2c062ea0ab159b4b2ddbb96fb59faa03fb304c3a1686989e7ae7143b0af5 6ff929e823cff4754c292042132d62d4acdc43ee3698c53887d337746929680e57f205d6e8622011f3c7978c687253350afbee2fabde6510e970357a29954c0b7811fe8f8179f1c33318d7b5bc1a9b1366e2b040dc221eb09da761edad13270f307cb9c510cf7b982a1edf4b89e681890aecaacfadbbeb4b1d20b7e1833b160fbaf377df3502f274b60c929e3ebb898ebf79322e4f4dcbf474550029ef12990a48699585987ef7e6c5080a17808d68387ffcf860b217eb65c002b9034a8e7b06057866eb2541c78c4b80a2265774d69b777882c3677f3780e750368d08681501dd58442b1a9e4bce7843575d0f52e5f17cb2a27c2b33297181a0f896810cb900f161c80f370adda4a3a0ab009f486bb4073fb4235be52ae713d43ac4f20c1d0e3e44ade9d37ea1d18f84690c55141ff7d3d580b3806fc6b0f1687c3f4948e00c42f4e733f980dae0a32f91c112da9d5c1c250ba396170327c75b47b904151a050ca075eec7ee8d4587ba5c4173ab2219177ba2f3929190234339d848f23ee407654d1f72a29734a22f9aedd407e6e3d0afaa156e784f4e78a67b6944b9f495061dbf850bc1a95272355357c40cbdc24e1f2f17a67f5c0e637139b7706b8b6901c9639c63c0f2ce2c61a66f1a6736e3b6c749e5c5b8f79b5baa4e157554e46202fa41744dfdc3c3806e82bd67cdc8417a592da710d39959d3995ccb5f642cd50918b9446591d14bda1a539e5d924b1945a9bf6da0688a58f1bf86367d827c29056e9f26f0047b723422a15e03e05c3489d9668826d5ac491ef5285ba4d72bc509c463290795a340f6ba2848c90adb035b9c1fa297d61ed682edf405d1d57e140c4a3257820fe52d306859e5c56c896d579bfdca6473512f53246a4f3206b95f029ab81f2c3e35ebb14a9f311062daa81d73ff6f8c8d22a653a64c3e4002b6e6066e6264d22b3d76fba229e804b0d2866c72f578a0c42eb3f97ce72ed6c77359013de3b3423521a10504bdd695744931f64b846cd1011cdb3192bb6aa69445ee0973b3047b765e746d67db98f1bf2948028f3a799005d5a9a06c5708fa8c069d0a4ade55808f205a31ca44d6e2c17c41fd67de7df2541a4fdef943979a325c3a03cb918433c4fb1ee69d9844a5600ebcd84f5680f28ebfc5f1bf107cc8892fe40e2f915afc3ed9f879e70ce0ab1a9e56a5075ee35afd3589a92ba8e08c8a791502186e69daac68347b216fee9569129b92cf62fc46b18f72eacc04982657b08c03e62970aef5cad526964cfa5e10dfe08e28594a235c092641abe14ec0fbf8ee062031e33a0b0a27bb8d00029b9348b35810ed373221840d43e7a6ab731300990db5d47ccb904c7ffcfd005e2cea64b33c3ab51667799cec5ef65b3a4065dd6f0825f1f78e4578b609392517db8f6548ee1940e0512dbd5e47fde9157e3cbe8f04d97717b253a9b49582a14230692c6430764b2a58fd68e41bdc0049dc1dc17c6231985a3198839e42ac354036c8ab49e9110715ce8cc2b14f4cb16d54f26cc200591becb8c1ec300b9cca3a48e35c692ef3b5e0f77e5e87ca00f0018231c3930884ad31176de25ff048af8cc18d7df50e682a49bf4c5d125ae953f04289163303 false +check_ring_signature 10d106049942c798b2f420c627a2ae86853e04830c28fb0dff8190743605e228 238e7bf64f5f5df2d2bf58f6d49a7d9e35de9ef4f3091551bc6495b8a80a6fab 7 e30e591bcbfcf111f00f2b252bfe5ad61812932d3d02002c8c091f71597504d9 5fc04737d1717222ccc8d28bf1ffb036c1aad57ebe02c152217b8064841e0f41 fd775d5b5917f6f4cec9fe9b3b55c6eea5457bddf091bf6ca76905f67e701055 36fd47231fd967122836552f0234be81a57acc11f6562ad7ccb36a24a9df59e4 629c46d010b80a0895cf868b012f692c673bc5b44995c929c105765ab0ae1562 f720f6f3bc471c14acbe517db3c2f257a3b0c60b659958ea5f69356680b1ad48 0b5917f58c4c81109bae0393bb2605876c3d025b68d01ff7c0ba3608d543878b 2d7ffe7cfb67422aeb7608c83b01fa5922da4226d15e1ec55171504cad41f50dfa2adf96a46f00b1cc911a869e76cb21524bb33ead6844a8b9101cc69ded2906783eb6a2d56ee12c0650097eae8b644ec9a5c630d97c42be93e626414a5de906a0508261784287e6abcf51649bf035fa0153533584717eda65c2d9422a5f3602a6f4d3213350bb44927519b94f76e4caf6409d0cf275f116257a9c6c1a976d042f79d3fef389d292ef232b40155f740f48bb908c65684b44094c507a1a0f6003e5471291a37166dd84d1fa0c3a614f07a986d63e3a632160c8b2b40ba6864c0d29ebb76ac5096062b4faa6b3c1fa5c8b67150ec057dd417ca3a0a1f3f6d26f0c80f2031d30e8c3a5d3d64e90ee5f80b3b549a6667ee65904a46384e113d625ce3065309375282ce4d084c47cda28c5184320be1b77c8e0d2346b8851da0ad3054756e4b78fde8d3ebb751ae04bf8e0515bed687e992a569542e03dbd86f9200e14461e761d2f97721b5ea4cd818de1f87bf28f47a74e304ff9fe7cf7ee4b7f09918d6c79c50098719b09dfe4c461c36ab38952655de90dfb9aa02b411e67aa0a8106114c0b63053e0025672a77c5405ed83eb6f8b4e3d390c8363fb1c45d1304 false +check_ring_signature a07c156e6ec39b4cf76921cc20adc082f9c36ffec2ea08bd7d680fc91eea6851 f1156b241384cd78a7e4de070c88ec9b972ce205ec188aacf03343f479680e4a 146 1da0b0656519822da9cab1d26f7bfb931dd53492c14b708fd83fd91a5b7601b0 61b166b98308e8582851e39f1996abe4544a3ff5c80686350df66836c3cd75c3 a572e24e9e79a70e47b0a4f2a798327ab271266a063b7864d77fcd16a446ab87 d526d5f9d2b29d996f1affd59cc796c5e7fe73772214ffd83f5a0c743423541f 0f8839eb2108af22a484ac4059d857050b2e97961d9de1174e1098fb66a5c91a 0a637adad05189c0a7c246ab00f5434bcc8b0def2038b72cef03be22fa112534 22f0b92569e628cdf0e2945f0f956701d6c1de7ac52ad5be4413a52d0b25ac11 7e5e9a14514b8c6ab655fc4a640721ac9b0f557b197e9eb0dfb58c7f20cae32d 39c1ee3616de4f1e85d49401acae0eb845186845bcc5c3d99221f4f681c4802a 328d48b425ea82e8dd8aa772e66a73282360913455605e2691d52bdc180a5236 be3376634c0c856de8c145a68c6b6519199b1343c6a9270e22ad456bb4abc52a 256ef5237dbcd22d397fc3f5ad75d7b35a8ca87ef1ff4e729e6a9c707289d0c2 1f60d9ee68e0833c9392dbcbf4d72f27f9abbe2589b7ff583e7d2d11b799f74f 87746b602af7e03bfcfc850926b05ad507121273102602358d86a952b70690f9 5ec0f6f75f837d82666665be76df01a72286faf5e77bfa90fcfdfff77a312dc4 15588e98a1afc77a18deddcb3341c9478e23fd687306e452492fc594c8fdbb67 638bbff10cf58b0466c35eb86d09366acfdd92adc993f1bb5d09c8383655ce97 9c2d308d589911a554fd695ab1834ff82c71cf44de15a0edcbb8a0ca396dbe67 9efd2920c8961a523bbc0e1a4ca2151cf9837fac6beb12d6abb87dc5f618789e ae82c248ddbd3ef5f7f299966fb8b4e26b2e9fd09bf9564e3921debc12d8094e fb0bc63076e2dbb5551c75a156653adf6195ede7e7d52356816dc540dfa53313 15e96351bd2c2d999f5f0883caf29cafcd9c66f085917760de6c823becb64fb3 3dae8a8376ced8f334a416d973a3415cb082050259d1e226666bfc2626b691d9 e0d0ffe34b9e989e57a96f8e077603d24c575228c5f63cba579dedb92a25fc0f b33d8fab59816946d5d5580c2956e410370d7f9578c059d956950062bb6efe17 001f6e54903f54d693068256b830ee72dc5ba2f7727c17d43ee27ccd3b155ed9 2cd8a5b3f84d8e404d4b079963a738f1e122d1be6a4bcd896e5e5c1910376578 ad7d1655c5a9bf69cd7bd5a59a2d701080503be88a90f9b954bc42a6cc5f8bc6 97aebd1d80bea798e4bc519cd5b2c54a81417f601f83bb34543bd43612806242 55bb7725b201bbf061f43d1107af3fa5134dafc65447ec538b6583ca9d4d6a31 2e5f9a415b3f6c05e69e3db32c07e4191bead08dc71acfd540cb7a39ed2027e1 af655b616f32bbb11f7455140fa8fe5e07db4d3195593920aa58da005ace9cdd 0952c1aa03c2d64211b72941aeeb2bb56f1491c3a2fece4758ce644b03bd88a4 9d3210ae3c1e1bbafa25984626fcdecd23075480c66d2c14d0b9d140e974d383 b442cbed0dff9575294bb727d03e310f432cfdadf35f6d5bd46245f0c01da617 c769335cd403d2ebd8f1eb892f5ae7cd8dfe7112fb8e473b7105f15d1529a2d1 b6aa53edfe226d0f2c0641380984c556b4cac7bf8515837841afa903b4af6944 786cb600af402c42be542659d96365d1c968d81e2d103866b7a12fa23990fa07 9b1f1c50b5d2b91c50c0837cc2fabcdd44be8a078435b316c392452e1738e1ca 82a1552c3d10f5bfb1e68317b10aefee4f37edd70fe1de45e51a60e4bcb6f716 e27645b5189549a26ab801506f7b156b48f4f8b3318124318d61dc7c1a355f6a 937c2e42d16288c8fbfb9c58836dc8e6080b230f50671c566962790d4b094d09 2fe795c5481b160e62cde859e42322bbb1f38b48b81e86c245d98e9e99855ce0 e9bd1441f2d40b58e7a61d9d033fffa371ecd9a5430643504abae952235a1d4c c0dc301685682d2ed0fd678645a86f146e3734a31757f43972c8afb618f96f8e 2c6b12ad886f6748db96ef3f12dfeee791288b6edcdf033272adca205961fed8 20645e3f7ce3baf10f0f5236bc6aa9847f49fccfb732393e7144ea4487b3c12a 30a407402bf0a228b73d62dd2888c60ebe3ec8f4051989716ee6801ac0a8be79 a30a6957201f706abab8b3a285f3194e8ca9085cc1086d87c69a3e68659d570a ccb09399d87248e5fab10eb97f5d9651aac82eb26bcd5cf72e7884e2da5f1244 8ad55a29767b9d9c1ef82ab92820a96052d8d236795b347b752f4aa9d3439c05 2310f7c3489557067e3fb9b6b5d89bb321c628b66ef001cc7a31475a65a807d6 e672cf7b05f20a2f2ab446ec2da306706630930f1f8ff61974cfb40156f83b7d 8fade2714018f7f30ccaee13e5974e512f73f4519aa497fc3b14f1fb270ab49e ff32c2fd2acd0b20eee6e09311456e4d53fa31e5bb4f0c7887e1e42a6bd1f8de 62fc9b3c9714cbdcb5ba11644e7ec588a0a084516a41d605d7ec2a88df601c6a 6f7e9cfd1a3dbd719cb589ef507ba3d5273333b4680155e1f469d6db9324ce6d 6fd75a35a04af8127cb588f2893207d57de607c5819311c26a79bffc4ee2dd5c e783f9da21b9a1bf3c49722e06220fe364b545f6a5e97de3d037a20705ca046d e64e8196cbed011361864a4bdd85a715682a05c2df4c879eb085aef77c816514 bd7cea333b9e6499256e02ab62f327df2d371536373c92f64550640c257696d6 792971e3549db1625e3899c1cdd83008c86ff4e3d0f950b69a06054232e87306 686e6efcf45f486036055c0203cab4b131e03066dc7a6232670eb6782b483acb 1d1e8ac604becdc73e08b931faf587e9eac692c1181cb0bc0feed81568e9ad86 0c14a8539cd70a12e03ab0981718ed31d2dbab9b3cd4b4871312a08c9709fef5 e248efc1f3877d195fb3c39bc4dc7af6f65d8e6bb7913cffdf44824a2632e153 41547a3812ea5f7ae1e446c0a910fe648b1f165861003dccd1883c48df1b0d23 76172cb607e66c0bbd61c3d8064935ccb002023af8e24a8025677ec887fe00cc c93e4d20c9d90fa9f3fbbc03b04e775eb88af5ef635192235de223a5f7d083e1 1b60fa2f1e730fe11117ec16a31200bac2bd58d950b3a52a51ea0cec5649634e 01307d9950265bd5cf67d007bcf5b34d0bfc874b567135cb04ac7c1b287eb94f 74206289d5530fe22982fdd6152e5610a676ecc0cafc1ef55d7b68dc50a1c8ce 841740c7f90d99e3efa966ad899a0c26076bb70c08b6b566b666d65cecadb02a 8670e9867317333cb24de264d24572d8f9948a1d54d4332625117bea93767af8 e9b98d0a6e67280161e0b34fb0b55f8dffdf76c2c3157430a6da70f04ed8b4b9 d17a2b6dc4a8c2b6050107c8a24929ee12818f037f4520f9d660b8bab7566661 c755133bad103912498573a50a7b369de1d65fbe8835a27efc865d99604d8226 b81c0ea74530b21bc62db42a77684109079a09bfa61bed2deef16381dfcceede c375dce2e44bba3f009c814b630a058dab57680879efc0f83e0d0260b96e6b27 c56267494156ebe0de844414968d9529554ba0261da117ff393826fa54070b86 e39d10364d8d329603469945a322e2107b6999572f14b85ccefc42c61bea59d2 8153f3e0537d434fa288b3ae5289049642696f5aadbaaff01a9c2a600dfee277 feb2f779e7f2a288e91fde1a1f1c509752aeff22b3ef4c5bb99b25d5c23b9934 08ca24f78200b880f5c3c08ff69389c334b2997ccb6f4d00f713fda6ece443c5 1b1817d038237b14811ac3dcc196c7762b525681d1f22e9bfa54ba3785409ec4 fe2b0558edd3aabd4d4d995615dec092739c96ca8ff7b376d128c7bd297c5c05 7f1265fd902e743855e553237d5d37af8d91b7bac1e09aef58bffeed89f11d1c ca548b731a9d25af1048b2c4ab5f3cd69e714d6845e445eda0f6f120b67d70c0 7d4446f43e26163442ca8079c1eb623df213aa353a89b704f3943ef9eaf5869b 9abee57808402b924746b8268c697841b1b3bbef119a126818a3ce5e32ee9975 6384d7f0e36dabf37a9f689c3dd0e8ef740d3e708d29a425fe73ee0c274364ca 042915ef5df54bcfd8d5144e3e20e4c1e177f778d96f4cc0af9f20fa623862b8 1285da12e0cd6aecb35b0651a3cea80aba946b12eeecf5dfdf5fba9cbcea5d49 146535847e507db5b837c78c9b49645c831d1b0372aa8cfad91becd0eec16178 24aea905bf3ad3371ed41611065231ceb43f19d1ff816365bd457d1fd1dd528d 245429ace25c3e6a2278b4df083fb4fb9636067839eedb4aad140d3795b85d69 5cf4c448bc777a6ca9f0568f214a180af1d910ca5773b4b0b85c30abcc9d6c4a e18038881a6de4bdc8d52f7b00405b2b9d685fad39cb35719efcf1ae25ac031c 855bbb36d41a7513524bd6ccd55f7d927232801035773e5381401098cdb6a855 70f9027c3b02331b8e8271c45e787bf7fcfadbaf0c19561f3566655b2f89e3d4 2fb03e4ed7f83c68e6e9a4fea05de4880c434d21df941c54625ffad1368a53af 9a09784a820e125a066daf2515186e62f312f85bb9de8f0d7e2eec9f3fd558c0 290e0ebe1024a9716874636cee6f5f997d956f3ccac6b0c0e1ccced20947e4cf e79b6027d5ead7224a7f4dfa917c8c30310f27eb42bcb17ca673cd81db392f6c 30cc657e0c6e20ae431e68bd8dfc424d48be82f6538d685a7da9d8f6f03ab3bb 950a74d13157bcebbf4749c5cc8dc1b384343132046530162d9514fd46e7afde 84a16b798fdd796e3bc79a9a11a917c4108a1a25cf5e6dee081bac9581807280 1ccde3918a871af231384e70af8ad70c879d09f702ff69205f3eae0884c1b297 3ddc5a97f42698bd61aef49477198542569329de14e72bed697b721b86190614 57a68be90bbdca57cfc1a57306ef47eee1226a61fe327680f91d4b2dc709bcae c62adfb19e390e82d8a842d9344c2292892c5641d349035ae78c13d727878ae8 7152e5350840484511e0288afe9f2cc5b34a4f16a6c8a5f76c38dcbaa012b719 21cf6af9b05def7fe3b4f3a255d7bf6df75bdb84790c6861471ced31045e651e 04883f0ec8da85700e8d37bc7c1801b9a7f3669f418521787b58a0bf22df7376 2c1e44fbc7efefcc4b364e9f48c003014b2a10dbe032255762147302bb00b437 7a5fca1af785875604f5fcff54c4ded3114c08a9f501fcfb7bc602fa04fcc75c 2febf9c548fd945506d49e111f9eaafe6b4c2f213c3c136b5e002fcde4934d4e 8d6106580e29fb9d1d53ca73cdeddd0f019dbdb9320ed608692569db179100a2 2ffbe9a319f8f6b3f10230d672184758b8ad838ca4656e766b02d462a4532a27 e7df347b1a9ce17f99d791ed2b9ac3c7d58469323a1c8d9b9761e40ff983b07d b43cb3ac758a4d19774c118003982adebf70e45719b3281f522b476bd78c9542 35a9d44dac20bb9a591146b35355c091e84e3617a7d61d8b0ce8e66fed015e5e 3280ea4fdc8179fc2f2d5a41cba40b561171616bc511feecf9408b808ea62764 23ed9a2690e332b7e1802e180cd3e6300c6f23fe1400238b8918c70d5718b4da f9170175ba01e1fe1cbb8ba7c2856acfe7ef4ac588ea0047f363ba0d02128948 cad7c3ac4d58c302c2a5dfb23ab5b39445e1c15191814f62c8d4d8abe042e693 349ca3c231c58fb6553d518d90818f75df23c47795dd7c9359d3ee0b1e98a7f1 05783c2fa7e32f31c7ba4a754fe3facbeda11d9b6448c1ab275e5354a211ccbe e7d45d13b8034865c9e4b9100888846166e5cdfff15d1a337e0547300b5ea666 58704898e478355222602e798d8b2004092455143178abe6ff25425e1880f5bc b231a1cd05a5277060c1e984dc261cbdbaf8a6eb3f37e998b76c247dd8ac3ba7 ac67d4ea9e2616afb7772c2173445e799db2a53ceaf2e273b472917409b00c14 0e3267d46ca0b8f8fee477d6799f53453e8248fddf233b231be8190625e4d009 bf37e5a10f59990e4b11e97b61d1b81102948b149552f07f1a288e098d5cd7b3 100b19cee42cf96acc9575b4d637f8a5c7be66799d7b9b3f09db545d1514eeab 815238bf75d9d3bdd95dd6c9eb059a01689a5a13aeaa568611fb42176502b212 e834c115a942fdae646aa8aad45e361975d8b7993fe02f922afeea82af5c75b1 77b6324c213967990d67162bc0e9ab953dc09deed55b3dfd79731aeb72c5a0d5 5292fe850936b5ed292a32a8465cca1421a2bc30ed840158fb82449e9227583d 5f1833e241c1f7b4a95860c6e1f85df7bd718d8b85ac158aa9db70c8bf8ad933 51e7d6091f062140340a66cb4185a91ef51c35d663e2393eb640cb0ce4364d9f 495ecac9b4abbf5e9a2be114027d5a2b307bf931ec5dea6567ecc8443e29d6e4 4b768f581aba1d8ab61c8281f0e95e56da7a6d093c63f059f487de29a51f2d63 3dbaa4274b03c73445695938253c69e1081bc88eb8c80e98ca0f7c6d3b09f984 6565a9ddb9b5c4315a86a79eedcf5037abe559ae8afeaaff8e4230a7d9a7c98f e970140bbf1c87a0e1da70b8709c81678705c2ddb2ea69a5e1eb29dfad14de19 eb2aca34f2859133e31547e1aec119565e3c2cfacec25241f49708213f0e1b00a75f46a49953d21cf008520a6fbf0d3c070e377e296bd5b8b56cbdffd5a9820a1965d40690971abcd740b271fe4d564495fea4f0f9a8baf323de56d76529b401b2bae274b3d08ea3b463b38c6cf42738adda5fb123ce255e2e1c38753cf670028c212b2ff24aa7075b6f371e8cae48331b4da262d08aa6b6e6493b237d879f0afaf090132793f3cd927160ee4b2c398db4971133210cdcbac3cbfd91a51c0b0abd2740a830d4b6bda8c2c0a33bb9902eef4b0978f8457e7202b5100e27d46406148b8b978d782f0731899e3076305ada5beb45db4f1d53a96332e1a527f5f808cb6fa55471c28b77a354be1e84d6e63529ce7aef2d30d7e874b2fde5815025014b691b15c7543ec3f27b7089861f9b5db6fe3a313e3d13e99bb96d787bc7240ca8213f6d9622204251654d0e68bb07b17671a933396e43566a3fef8d8029f902a947bade181597d0bfe261ac933cc367c403eda985ae67500d607a157385de01fc54aced045d43cc0d040e3e60c9a9a074eb5cfd6a271775dd66f2dbf8e8740432459ca6ca686297462b47b088edb2ddb0bd430c6310388cb3205de96f724a08627a5c9eb0c7e0e19de1e3788cdb7b376392f8994ab3f44e8ac68e03f6278b00bf0f4a12847186690868cd5af85470c4dee44e038c80ac815164dfd4cd646200bf0317b36636f6f32d95f774dfc69ab717f3540035c64cce8eba0cbd6542f6078e620a79f91e08988b97dfbf113427ad5abd4ec1d6c37ca89938f3dd76daba0ba1cba0e15629f1bd1c3458e011d1a5fa8eab7f167683a804a955c3c62e1f640bb32d42180d4b90ed5298491face8fed2c57836346aed23055826ded9de344404b860e85ab4b3d8cffdda64057e369921d53ed72971b17af2b3618bbad3e6bb0e2ed3a621a41ba24f8b5da0370521faa4eba83fb4b081705a41ec25b8829f1c0d460e2c2ffa35fb10a567f00e1a110d57d2109376e80dfb552181b4883ad7fb0dfbf5edd96d82cd1274541b366437ebbd508ff7bcbeabc48809d3ffd29853370d670f60f04f5a2d9c1abf9f1044371cf8217c8dafec27a443552d00d6b4413b04ed35122a122d65d06fc4530a893952c0f092f5e880202cbb673248723d931c0c09491b83cb97aa72728f872e31c80fb04f17334f9022a7f649daa031f866c20bb6c1da910933d3adaedb34c654b95f0f2dad1756775293c0923d438bd325170aa168615eefbcbfa669d2e08ec1b8b7470cb01893ed60ace9105216c9122b5b0de5913d74af8e591869ed69b684e678abec06b8c9c17e6542b29de993de2cdc07843d519646dca151f775e3688df350c9dbd91fc9d9e5105d9d5efaa9c1806600e2cccf5d9923e6bbf134b69c15eac8f3ec4d5ff46361965c6c6f0eadeb18ad036b7e991c5b4238d35868a2777d9cb44af9a2d14b1c163b9e8d2bf0af3d7a2608d4e508cc560987b4bd4fec8a0b8a3b6262fbf8935f50ec46857926e793e6ab0f8defad28f15e572cd012b6168d8118c166e5bf4d3830a4fd186be858c0b3700871e64cd6ac70c2cae99beb0afea6dc087f780e3679352b84f51b08149b838006ab35f2e3612387b8c40f188a28c0dd206cdc8d3f0fc6faf17b25603a1b1a540864146e44f1eb9ffce82d40eb473c814c8dd0352947052d61671f0305ad98e001c0ccb5603c8579b99df60756d397d5d71023d2aa943d2c107533f81879c8ba05e191e50d5cc76a38a753750373137f444fbe4a2c4d6ab79278b5c5fa61e60303eca60808964f9a34d4aef6cde04399697ef964ff7bbe3de2aed70b32390254095630659d5da899769ed244a4d910c35232261176f292aab609b5d40ebb5a0a0a6fdf250c0a863c2188ffb4e7966dc4dba67b0f6bb2caae8b38c11aaf9ba22a010fc8ceddac11cd4f8028652375b76b89c70a9eb0d89d1be8ca2623252d609901b970984e2e7783205c393e38216403c9c886cb28449e618852e2dda8bbcc1d007bb1e526a8a502ea6a7d13abf931d8ec12651c7edb2c7f3065a0c911fee5f60726de54859986fd132718be72d5d7f65d028e736af9f4f810982810af9be3cf03213030607d3decffba1ef1e3f566746d2f11055ebc70ef7ecc3266426258bc01b665ed54b67102d1fcdc76b901419275b1b00cce86b9b696cd9ceefb89efb6002e6fc86553bdc02615ec2e719c751188cf5c9397b59ff0b502236bb03637b90a7c25d57ee02537b9734b679d1385569bfa9b9cdda3d4a62998d323d8c5668e080c10ff0fd2585bdea69449a809824f1fd0fd98c2354d6160657101f3efcab704ced54bc622819b3ad02d61befc7691c71b7efee6eb511f9a68f5fc30b5ee7d0ffabfc3c2a40fd3fa89dd84dfd72577cf7ae606d51661151f279ff5cdc2b7280b20ef625f1e62cb901a3726f182932a87c4f3aa0ca834cc35ca0d8a8919cca0052e7cca2be15d433955c09397bfdffc19da2016b0611d6e861f8d44e8ec38f10ce1d0c3ff3cf0405d4a9832e41a7b04ee57bb0cffb73bfd14fd9d035e3748570d096b40c974e54552e8cf91ea738f810bdb52c6ea10f7a4973ca807f826b50903992f77b23b00d7ef82f963160152ca950c23bb7fc76bf98966abc18a1361c70cbcad14343a85819bdc2cbaf0971256ca0caf7ed163906356f671e1be04785508d50debdb457eccd0c559e2f6a2ec2768c769b83b64f5839a24bacfe3ccac4009032a1e890afb9f1485941e9b9892cebe0e716ce7ddffd27c1947c99582fce30a7dbf66ace364c1ddabb5205980a8406cf14578cdc53bdc96fffce32dd610f708391b04d48987773f98da542bfeb1da0b7fa6c95c095089bb82eefd1613fbc804d587e744887c90049d145961b09f6cc4faf79535a2d194aa11801cc14cbfb508ba5261444d721e4cedf55ca698b6ba575683f2648d024f6e70e295c788841c023531edf177c93cc0a121c6e88aea253fc79f194231188a25bae9c1d698f1d10e9e434dc834f45ffd6f12230c5b77a0b2f3f8954877bbb836fae2b34fc5557a0e75f15228872147668ea8c53ae5f83a263a15e0456e3cc822668f7172222e900bc464ac9706d70e4ee4cbd565e70d7c4534a672b558756bde185f9b289584da09fc6611e793297e28c0e89481f51770091ca7329bcd228ffeda53323748bf8d073404aac2ffbf9f271a29847582baaa898f30db73fa0aa379ff83dd9962cbbe01df57206a15243d07749a806e1ea212a4a0d5977257bd82acc829389bcaa22f00e205a59f866529b5efe0966f651037a7ba97a42df2633135890737b5f488e00eba5b4109d932934cdf57fc463d913e7ec8770679217c5fc3b71bbd9ce396c108894a98eb58a6563ae5e6659997d82341ca6f149c6349540b225e795c9eef2b07d4e70facd2f15a67306b2cda41ccec78028ddd8bc4d813204a3877042813f408666965a8bbff33c9c9a4395e4c168d68972515c320daed9d222c094a9afde90dd3c3626087b093b6645e564ae4012a3043372e14720b882c87bb03e8e059bb095a4080fb0a1b75528602a536a99f6ccf710991237d59fee42771cb8ce12a4c0ac84e1730cc9d9b65a5197249896f2c95dffcb6379b36ab53b4e1e322a9c308092eed9697458a5b0e381930981896229b0089429bc4beb0c93da11bfba6805d04fde8744a02ea61a76a2d5131eff413dfcbf2bc3402457f0e29b33cebdb7e130c50276116c79e1b6248dec0b43a6c6fef14638acddad10636935b7c3ded0a1a0acd5ab8464f0ca26a9de9899ef661810560b668e1f07a1212678d62fa7e057c0426aca10e711fe2bdbfd2f2a9fb11e55e7eb980e7e0ac6eeaec991d21c0461d0a6f282134ee8784706c8b5ee3d99947f7a2af364539c2d82a9127f0e806885a025db8ff76cbbccf3e4251429da703083d19b247c34e9f6794d9b42aef36c0ad025b6edbd6bf89d694ad56b465e0c6ba83297f9c528cf5b23ac17e21697a41b20b8cbdecad9f969c13ff701bbbe5639166a250b5fe31340f6d85b37338bdc02206bc6428d8ededd1b005b8d46c63becd35a32fbad15eed509354c3a7fe71613409dcc0ffde26118727b462a8316a9b671bcaa43b76674d14a5ef0187039fbdbb0487b7af94b57cdca7f8832553da65c49acdad70eb34f85afe1128a81ac85de101df86317d36d6ffb2c66908270db1cf803906904b1f39c3c08bd276cd6f95450e50a39523ef34712978d533d936a2c4ad60b669ef3df97384fb1edc7cd8842b0a439f0e11ece748fbeb9c47ca2712fc7332eddba20fda9b1aea1b85b3fb8be10a1997031749a2573b7946513518a1aaa1fc5f4787287addc443acc9e5dffeb70ef0797bfc54f7721346671c57bd5f0bc9eafc1a75a79994dd2e1f94d2b6dd490a55553c11c565eead36f76fc91e9888e1b6af62712488db0e6c8520b8d2744d0d4c0dd96969b2edb6577923dcd6979844efc7f4ab8cf5b6fa528ecf3a649c480ee328db6cf4e62f55742dd301af3c90b65478e1c4b6749cbe6375e67137fb68068d0e3d4ba89a37c6995e94fcf1443f1eb0bfe217b4b37bcb9f7cda119001b707152e909f713a22039ecd054f58de457f0833304085777dab2d23c00a4886860bb98a215946b8857ffa84f2f64e2013edcdaf606f84bb50171bc4c7c62768e8065ed44cee84d57a34c039ef000c8232e82b0ecff87ecc3f84365306ba495fed09d6bf089ec1ef7e659645cf601670b2444fa554c6e7023d51dcad7ccf6151d704b9357afa69a21d6009b8b14e39a1bed61d2f6cf731741a1c1f170c8d42f66c09413b870cd180899a297f59ba6e53b60d54ac12f494aecbd920b1bd84cc226c0b51409c7f0b8ec08499d7e2149458db2f6c21947441f8b586190db6a5e3662e002e612457247b7437f67caca974fe6d30d91e85ada9c8f8329e0151cf32fbd003490c425db108a05ad2c63503f1a117beca1536b8e1d326e0eff5457f7ccd1a06a81e6ed63f17a6ae9b79dffac934494f9a5ad79d66905b071524ef97c2e3900a4db65c25937fb7a8b2e49bf8aa64955fbdac827d62a95d201eadc3b19d39850998186e5653f39d631236da39a0e24765a3b40e848d96b56e62d770778d7b080e8593d8cea7ce9af8b9db86a4433484a856c4bb585e4ceb9ce86a668d69cce20092a3b82329f2e463fe14c6c90cf3f4c16224a25dae574bd9b8b5906c9d34d608d0b43a1825ea10d0d84d12ef021858b9dfa2475d58d97a41995805424452b30dfed8797e5788614a4be7b2975327f5037d05b27dd0bdf6a2f2553d6735d34d05a7e9b9292821984994881511e6e540eb7df862012bb9013a8cba895b0d0792058000dae5c0c557c40bfbc411f1ae1815365849570b4a1b9a2e1c9e15e7fdd10b966c9869b90fe664a5f8b1aebcefdc80743d8fda5b2cbd2db0c2f9d91af3400b80af78c38a890a110b8debf420a59b22098c002e9cda963fd3fa04444e1e16044cd2de6dbae7cbfaa60188fb77ecd52970502090b86c68637ac9b50ba512e106edbeb2092f8c9e2dc01e08b80af95ea0555a2e6530eed16a8da43b92e52a0c013c888fbac81f648e35d1b7b37cc5953e9b953ff147303f6ce8b60ed03cf1e90188947724c0445da8842b64974dcc7976a97ca0d19a08c8611e8a7e790caef50bf6cd08ad2daf7c01d6de4e137ea0aa747244b04ff288913bed40c5992454de0d8499ad4eed971f12113c7df44556c7da4855f489d472eddead8b1c214704e906bbcfa3e4296da3a6b06eee68b5997bdd0ad684b2002c1d20f3684bff3c73da06a7170e9cdd2c0206e64db630d4e6f9368444e4ca2feda82c68cd2880f0bbd501ca9060f4be7d10808c6bea2056a6cafbac12c74089bb994907474a5874c5be07f25c1808fbfbc6d46d975fe6ae4a6eb74425c926e8b9f46ed52650543a6eee01797bdd107e95ff9acd90fa33301b17e43712ab03d8619730047a490e1e9b12072ed1ca29c5d5f320d66f165410ef541de01714c5d90505a4cf10ded1f887ee025a0018ea5bda1fc5819354a0d08242f4d2e3ce227f3833803d1b901cf7733406b72f2705258cc89f847b3bede19a224bd9a8688c2818ad3edcc76a4927c7ea0a921cb8b380b53745b333767137173396974eda1cd51c2fa76da4d4d87c1506064816cdbdc27fd8b9919e4fd19a21b3386884355eb9632c77dc6af02b830122099857177675f3b55cfc958e422de0aff477a842ddbb4cccc078150afd070c11063ff73d4f51b6cf0b232cfd08fa516be1d074a94eceb28b917aef93aff53aa809604bd2df28c2cf077e9e2bab07586118bc556cfa90dd6a545819638cdbe4010271451f381245e1caac8a1c11ecb3705e503ead0b7fc1f05df09dfca315d5db0f87dbc39e7c259c8481e8d5cd82674762f83d3c16a09d6602fa058c0424767702a1c93f804fd92cb5c5e808e914502be1115abb53510cced794aab1d4d043440154475ba9e7313fd86a1ccee839c4bf26996610ee14513a0a3498472380edaf0c5a208f7ed7e6ce356c9cbba8bcb50ed05b453672ad9f24600b267f484243ea066cb56b453ea872a9f388dff45a635479cd319b78ad81a17868f8c1762350a70ebb6f4f535f02ee96e5ac5cf0c3fa9fb14f4a5343bac0a9ca5c78dcd33da3cf0cf9b9c95738df2510e9f0a19cfc443b8a69844cf4c317d9c6ec6d7f72311063090c2e7c6eec0bf37658b73508b4b9aec873fdce7be7e55bf54944e66e85193a0eaf69c8ff120d799bad33b0995667fd2a564650178e8fc97625934542d5f6f4043d4037477af59c74d17a60fc31f59ad380ef1383f61ab6be2cd2f51b47c0b302054038fa3f06c6805cb04ff0d0d6cf1af6a2727c787e798aa2dd94c8aededc0f0784dc052d283c7cbd495fc4a20eed94293aa519a37d8c43e28359215c632002953a44d124b4c4fb644e3b55e808e49ad170c6ee9014ab79bee41e7ada00280c9e01d2171e9ad6058074f65ee732268cff6c6a4083595306dfbad46c90f8e6032823b98af82b031174d1a7f12c08f832d16ddad4736d526d3674dae33276ae01596fd9fabb99f2b81254cea46a2ec89c0b4ccb7dd92ed1cdaa149730c838d30eeeff6865bbfc7b3e9882f59e82aef395c9d4f547a62e184fea2ec727c6dc27032d17cda4436c0e4b7d86de5eafd580294b216c8f593ce2684e42c511e2e8680377ab5e91ee0c81d57d59007ae60b6d33d995ff167ad4d80e86f4de099322f70c99e8aad5c98d2276b4f48d460d25963131d390397ff23a4385475d48835c2d0eb2f3865942828c16d8cea0dc4763d1e8eefb15e42308f18cc7652c77fac73e058a8d5ba13ddedf32952bb151643b43a812c971bb999db0c3414588bb324ed306abfbeb1a2391c5d09a93b6eab352ee0ffced15250196d1886ba6f019b8a3dd07446756ae6d9858304517987b84ce169e9ea321e9babc52d19b6bab6e7127170cf52cfff6cb979f3a3076ea7667b939f29446d6e7cd964f0cf832b42d8928e70c5aa7637e83088a72b570aa55bff2d0c5e1d79c7a21ad86f5f6143c3c98c2e20916b52a78c407e532061beb61038484cfddc425ee3d24231dc67dc310686f4709aed4136e2d7db4f3ee9030109c626999f7d1c9361b086978deb463ed5a09670751d508c16b9b1d59249e97b21de7432fe3221d2345fc74715ff391ad0288c40c0bac3a25ff56715046d28fdbe87114b06e737488bde1a9f15b76ecc2f8ee50088ab69561521d61800209dbfb4ba2641b01512a794bc037552dac9dd12760ec031c0941909617c50e55f2bd4648b9def5322d525bea78a82271a11de00a389009635ebe1f77eccb9366ce023792ac2f098ce0427baa127355328ad8929dfea4039f56afa8d80e4283a959cedc2378a97b37dd13fe303f7554b750a2105ff6df04555b80759a2bfa46188f8e5605f301139ac37a9d498e644f8f7506f10662840bf4495e3ad96e76d14800d2d1228741de4efc137407eef56f8fcced33ce215b0ee0f630952b0452c8a322667deb314ed02e59f29810d5faac7df7e9729d42a60f6d3b0d1417cf0a9cdb8bd7837d39488bcc5b4488682fe1b6fc5daf7cac5ba5003d94c1c9d2327fc0657f7b61741eaa168d88121c18b6b3ae6cdc754ff6aa9906ad772cdfca5bfeca968e35548b6b933c657f510a244bdafa2513fe41d93d890121a7a7fdc794dd9c681211f6ec4589c46a80f74b48d466e8858ae487097db900e5cef7582e4d33431dae7baad0bfb38c91f40cd087818cbc4e0c54ce33fa580389fca086ce75c2caec01758e6fc68db4a772d11a9deaaf3f5d026236eab3b30576043fa100492279b03c7268eb31e6525d47ed52b3fcc72b890bc8a3e2e80e0f0f70af93ecdc9984e8c76555ffebbd111dc7d668b8e2ae784f1ec9b7b271730b1e56f13992aa31c8a4597ea4d19ecf282574fd6207eeab10a53b4a13167e0d0a46793ca5d99f260a643f0fccb5a4bedf03f44b13d77e0632617d34f3ed41db0074d20b03c2a566c74c42d91db4fa0f09db56089bed8ef176c0ed1c8873454d032fd5a18c53c9e3c7b51d3ae6f8389860c9ee2e21439fdd2779117dc94525ae0b446d440df49c83ee2c38ff5df82812ef7bad65599bb2e58bfde26d10e4cb95076a7399bc1ef39c675566469bc9cc40c37e568db0ba4eaf196771df50bdf239091748fe4026e5615aeea969121d14c36d19f72324e62d85964b0968481b62270a604e6da861900008d2dca5160b84d975a1a867f80153d2b876bf70fdfedaf00276d23456607f426c93ffbd57e8468d7c5f0d1768262395777dd759364e518505e2e4d36f2705818c94e6255c4d3740079a2c80e61a954d64f021002539d755059839d204a9bb7459f8dd240011b8383ad1c1994ff7487818037b2032a41ec30fbece076fb6401bd91a00d6faa12b2ad9fe63172fd62da9697aa87710909bc6089aa22b91691024edfb4931267879ab3c75460f1d80c7b584b0147ed09bc3610c2a4040b33daf9979f170d6b7526ab47fcb5b4e1603ad69b703f7e04ffba0ec05961f17d75772759e74d90e7317c97b5cf1200de5d5a4bcd07aee3e8ee4c57103e6f7683f725b07908f4c41026515fbb81b4c80ca269a9fc6066fa3534e27fe02591c5faa0d7472ccdb166d992f0482e9957d6ec21ef8a33f184f6d47ae937a099124fe99bb88ccd6115ae8e8f33efea0162d4c356eba42e28778f68f69738a0c63d7eb981ce7f4927671a11e765f34579b5ea059c271ef6007df92afe7584d0356c6590c98955ca30afc587af3fc9c92cbd8e763c7431219cb4e94787c4e8106f019d82f03208b984ffe5aec3fd8a2a7a33c2ea876874e52719a03f76c1b950128854d5197305be02c252f047ee12c81a847683706aedd7df94c218ab519800669465dd1f71895f1976b3207f31275f6507503fe9eb7539e0ee9070bed77d40f341447200f9cd3d8a2eec8e763881f5d19942fe7f192be33df261364f516370fb689104304f6b6d61cb0ef68b6831ed06e04b99fb6e232e5d85ceec188275c08ecfdc7bb2dbe8cb09d0037686a46dce615a729f32b16a2405d2bf6c9fb174906fde7c0fb4110a37ff7cc05ebc51b64d63c25d5d7de97d83da7ec30c80189250ebd947fcdb880c73aa7eaca130b5155771b200e189fbf385e9a2294f58f4ddb0ccd1c58c3c4ed1ed175d572ffeafcd8368b15b08ddeac94726bc22d620a147300764ec733f057e409190ea98a2444841c2d2eb84c1654fe9f5e96f92824c11a0d8f278bfd6696866a25a30a5c741b8af33205c8faad8c08a5dfb8c7dbd53d8508bbad10736461c3425054c81cfa4154fc094331f1a02235b2df6f729779c7c906efa442af3fcf113afabac9319d289dfa268cf8fb145f990ed68a6108a554540179f2f51d88a5690ea2ce1dff02421dec4d9dc1d0c7a6d1608e1b26cb76b41900af55cf7a5f8c7448df17914f5872194eb5867c3e8f507ba4d2987e422b4a110db9bbbbda6c894aa6963d0f82bdc972e4d7b40b6b00f83e5cb0a2e9f6dc23bf0b342fc2af57b540ab32adcc38ca438d75fee5ac589428dcb0403141e7814ec903faf7d490903a3f4bb5f2940613500ee968cddbb9f5d3a3479dba7d57b87e6b0648538b725960f3b121bd572ad6034d7b26883b79c0235c84865bf382bc69e60af7b05015e8a3bec67650416e8ddb9c5fdac37f4ac668f0b142b53c20b77da8045f80ac2cc01772e3d2ff99605ac11dbb5bcee618486f7e6cf86660acaa8fa50988755c5cd54d7b964d6b56de1ad4b2680268835a99929b2d7df9956269ebee0f5fa30d550e0238f8d78c4c60bf28f6cb57d8a26de6433e4ad283aa1133129b04134db838350bd00e3a419f529a30305e0f17379b348d331d2e62c10ffe33c40c32bbe1ff75fc7986d2d29ee1a82c5bb4b84508f92ea2b0684c6929c6ac00a108a561ed72de54b79b20f4f8542b7398e1974e9751d05ca324196c247d8b42a003ad28a2ff8ade8284241af8cb74c761c90d18c4514b181959a68ca985f1c1280bb043833723d2f16dabcae4d9178d33f49ebbb95020d380874248bbaf05d75c08cd86f5b7a6b1b0d2de1862c2984620c6005eda4863178d4d6c9a7d1285a6c70ac3567c61d143566cb14972e450e75b56a54351b1e6f2c6d60080f416a5aaf90920d2431bfe11f221bd0a4e1339b2c2c80eb560266100942e540699e25b320f0e8438150d148ec7895cbdfa278538f84bc12f9cc514f976e428e50bc0534cc60aa038b6344df4cdc8f9586b7447463a087c167306d3957728150640927f25d40e3317ae8a9cb217f12f7323b68061a12cabb09bda6c4bb744734d4c1c030f91091a890aabd228aaa8dcea687c9d24f65f8340a7057953d28599dd0dd63e7c13062128c4748a01b20855db8b7b3075685ee36676cbd81a39789342126d2a86000b3181e9c39f3faff2846a9ae8004d02573b948667d6376cfe6b67ac9e5bf1d4026f729cf3a4c019867c5f9ee0949b9259b1089ea47786c5ce573f0984bb0f910820dced389b096ee29eae30c82e9b8b41ba928b534773e158f9dc136472645607cdcc5368d809301fa0ea90a6151aa4cfb34ff4c3baf17a7fe60b08c984733702dbc8d2510e8b0942e3f998b12b77267716a730667b8723936ed3c1867c970a0692449101cee7f4765ed519aaa9ac267cd3547da5c6492a140a38f5d8dbb4e603be3b8b0afc9bd316cb1edd0de7bf9366fa0de7f51e920973b11c6f85c60f7a067b2a7d8a438dd650944ca24ce6b17dd5860a47d2b3bf9fb6b38dee679efc1f0f51544dc1547e9b97c6bac66083c0e8f7f7720c8fb15b73f7c8233879dc9b090cf7a0e100454780300f9f77691dc72f7bf842e36c96e7184544d0da3c449e52050adf9e73e98cd84defa842015dfcd244167ce65a7b08f3fb77c223a0494a08020e5f3b3f74c234f8c25d649680ef82f5ee397fe8b666c594ee3ba56db66da207c5f746e669a58d27f4bfbaf2c5d67e0f954bd108aad8720e7866b2d50e73e70956dcc189e49ddfc4967e99c2bcdd8aed08473cf0eda358ead37dfff884b26203f5d00d54dfdd40f7ac60505161bb62a0cd9864f91525c252844867976114370a531374ffd96d92e5c3695a2f4f9ca5bfda59d69bc6054a8da49b48ed68f77d0f65b3cdf01a7b5f22d35567afde3c92e753553e9b55d71292b262b31361d0c6020ebda3883914a03e58c83aa32a0ea2ac91a7a52b325b204c96068bf6fd737901f76f6b42a93a71ecc3e2c44554e836e431b2298c35050ff38d701da25c7eac024900880adbc3401b37bcfdb0c4d4b1ef282a85fb253a5378346eae15279da70072e5347b4720c2e9fcf3963bc7d3c1e82fbd59b03883b918a2ab59bf21a99a002dfc6972e369500b6d2e484ca3f23c234d1c6cf0414f593ee59b78b564acee023af34e69899e960de6f813f68155db9045202065f0f6c0789c197eea19c9890ff4b359d79944dcb746ffdbc9e19c8cce8605d442f91a209c87c6cdcccaab0c0e682cacb7e402cbbed7a6237018d6b78e5bb4cdc4cbcea5949491b35990868e0e26e51b5f5beb26ac734995ba8e6a5254cb2a89e8cb48f7802c021bdac5fcf80912d72e5917739aa1ef17dd87069998c40939c4be807940ec3843dea2ee077b088cd1073352599d7641862f9cc4cfcceb6ec9b4217993599693024b41950a4404d335bda8f77a0ba769d9070012864f720a78cd53aa0c38b93c255a8507f79a0eacb24cf05e67d7df0ee6537db08a9388abffb0857e5a0b09871e7d0d011e4a067a174baf8bbaa57575b5bf5af014befbe45198867f46eded124ef4f713517803cb7df55707aa01ecf3a44c2b8c712238e0fd94e375ee971d77a2e9dda5ee5b0176679826b7d4fb13b7d132df57288e03c2bff09ebf32731bf2b2ffffd11ebd036ca1b2d95dfd2c78c7478fd7b1655d773ceb3507dd48e088155137031dcd98002196d55b7c4cd18cb3a341db285b26de0fb77f03f976f4a5fa48f9657b62bb09d3324115e64ea743f4eb279a2aed10ec95f9d4825f41d16442c60da325705e0998bb1026fe9b641f10a0b4f370aee1592cd6dba1c6250c76cc447c908abe2609c7db04e64e289eb961c2a1fe55e389f8efbcc388b81e6c4158b7370d5cab530c083cc9552b49d02adf6f6aee401225a38240de9be5b30b8ae47d0e3f2514e80c4ae94825f139b168cd78e5d912aeb0f53d993f1bdb30b2cd278cd93fc53b0209d8d9c2b7846ecf298d0994b63481508b76b7f5eae0c0a2de27c80c606e19c90b46c9b133013917c7bd16e471cc7b0573de96b079dd9e9373fe22aee637821407b40b781906205e9fd799d225f4dc8ea362323a0cddda1b905b452e1fc53d5a0198ee5d37d90495db90660cd0e6ac5357a459a75465640f68fdc15fede217b409aa1c56382313062f2373f0dacb86cb0f9a9772cc495f47d0d3975232436e4006a3fddbe17463ffdbbeb0bcf099e809e7314382e3302c1a3eada5b0737118530ea56961510333c12121f5232ea641ab4ae861040fccb0f1725a3b4d3c259de70d33c1abc125fb4c629a696470bf562f1066cb459f8779dc112c629b9675ba4a0cec5a18802f324ec5b2e9d6913081e53504a418060cb2999c3a1356f97fbf600c true +check_ring_signature 344cc9214bfbf72b50e4f4b8f485ceb4ffcdbb189b0334a2acbcb75f60a1b22f 43761d565a3b7b64d41909658fdd1f12526e75b40da76e442ff2c648192682bd 1 9fd72d85aa850d9ba0a3b9fc2b0b019e9275e6a2ccaec4b2077f8ff98417959a 5c32d96e2bb30383cdb83668a6b6c5a903a6eca5bf67b832ac8ebecd49023005d2fb8ee88d0eaa1ae1fe199013fec83d9a8276fa50f89ba28ab8a20ecd3db12d false +check_ring_signature 374b5a35fb176c28acf9171fa23b5e41400fea60647512ac160c2c1d52337e75 342fbabed00e5c2d7c4eefebcc654d4182b8b31e151d1431fd2e6206d2c7de96 1 b7b6d203103d5004ccc48da70a8d3bc669506fa2db8c1d34061bbde7572052b1 aab3eae6c16ffdc36c26b99fbd49b99eeed36c5848fc1e3d4296664846b1e4077eed7297a95cc1fa06bfee9265fe9cae0ff1ebec912f0f3e1e43710ca09ab001 false +check_ring_signature ccd349a208ce9246ee38479af3209b0543332b80f2ede92cefadcdf8df53862b dce8b711e27ab941e7b7ca98f5bedc7a0ce390b70842c3ae03911f9d693fe2bc 64 c31ed4306e45d416f243c77e67d68475f0954ca6d099770b25b4182110187d5f 189e9b5d48ff4e4859ea59130f89ac14e49eb6d3860234e7faad1491e69be4fe 873dde495ff32f5efe14bec926de40595768cc06ebfcd832c7a62c9132cdc6ad da982adc41dddbd6c93c4db4be13686d6825fe976706bca47f16acb7364f195b b904d936312e5bee337f8b8df0227afd42549209d09acaa7df5b1ced7d002792 1370fad30f214f828fdb0618a2150fb565f64b5e7b775c464499dc7b97ccb532 399a6b47660aad091c4e6218aa0b6b35478fc34463b48941c7c4bd5fa0608ce7 1d7c6dfdee8d6816069b5c08f3e65dd414bd7877673d433a36b2fd7c5d02f5e1 c9c9015c6a01ab51dfb053046e6c83b3b32fa200fc8b1c5983b6367620f2885d 50bc44736e1edcfed6d95cdd66db48dddc8151e4b62528f3527c620d2a92d678 0231aae1bca8e24bd74ba803b5f9ad9a31afd647e340dbeb4e1b578f4013b63e af00dde600ee2063c86360f0e64ffb81d66350ea748bd355f2adc69897d0ecf0 d443f2c04493451544248926aaaf7bb3f1bd6a300a2cb5cae88fd1454d86ad20 46fff0f22f3a63843828fa9a8c5c955acddba34b82a3ac68f123594e2c63b69c dd1b31cdd673082a200b53c9dbea32242b3028c5cbc7f5ef3102a0926ffa9f1f c25c95483f6a57e4c759d120dcbd6a3636683ad3f99c3a32f11a820a02ad6a13 abd0ce44ec0ec40a0840359bc2c98a52f59c82aae0e847d8f8f0653002e6decf 9f2c9d9739f4216a19828c4c5696ac2e5b532765b7b245d75b237661b09fbc22 0fd6f77302bca06a616595b4588ea51dc7c27455c4cdd44468025da98c8a1e15 b64fc8e9780e80619cc661a06be217852362c760f8e51b1fd2113fc838876c90 a7cd014ac5161ecef2b32fbea9a73000b6c49b2faa3356dcafa70b3b47bf6d06 0f2103948bc7120e3b9a15a0bcad3a26c7b4d1affc9fbee5b754245ff7530834 d20285906cd29a5512528d082334d44da5612dbd0f029bdb7ae14e6e6157e110 2a812c30c8da6dfa430313e408d06b019469ceecaa9c2ee92946c9ea0ddbd802 004001304127fabd1584dd2ee6bb27070d15bfa9323b96b7f74e54647f7353ec 59cefd57c9c47d0c3b2ffe7832a76b20a0a3b8eba0b286f0fd86c4051f176d17 8dcce531da9b9c2ed5eabba32f6c0a2affecc6ca667a94816cd3ef4d21c86276 614e3116e9c94487ccff936486e58778b3ea19bb0b460d5fd363a8292a596fbc c8d38f669afd3b932bfa90576ddb6801ec5b98f34f0c130a0187106165307fd4 e81a5bb23ea20faff45111b18881d3898b8723530264184b2e646306c5b52c12 ef84a289f8ef8485772fbdac25cdf93752a305845c179400c1bcc813b3a7cc92 1598db3ce7f8563951c2d4aeece7bde344fce20d0cab4d9678ed1d997b1df9bb 0c0bdcb5e5946e11ea117fc8975366847389a17f5a0e25f626b2ba20fa480bf0 30ad11d2f705d3449295d849e001f80a490f30795345e8bfb359d11d7c02da6d d62387c02d4dac5f14c7cd2d3d443e32bccec6542e03e9cb173c1bf713a18bf6 9af5deba7b739385df44e8a07fed90a774a14acad39c16ec37088cf52a0153bc f20fb7e3486ea5a2df2d4d4812d8ae2d5db93aaf443ac9cb5436cce9184ad332 d59777f6a83d5b4793d4306db11f04b0555c194f11cd504aa428791d776cd05c 524c942ca49bf86f89c8a485f6f0687526d1f0eac178a1fffe7c9975234b138d a12f3288f27804dc3ee802ed48b7eff3e453f394da6c92a55f638e1718957c8e 9bbee53505f091084bab376a649208e11b5fd18e4159f8403d14bab16ecbb1bd 6d3368a22662986627ecc9cd5c4177f5aa022d4ed3f6768b9a0e6cab3b708a7f b0e9e43f08da55b4a3edb1043585480ab8e49004dd605f60a2669931507794e5 193ebd5287a3df2bec4cd57643ce315c3e79c19c60a3964e7203b707a53b4357 fd67be0e35a712dce2cb8dde61555c5f184085345dcd4f5d612a2664f7bd95f4 80a5112897844c04ee76763c095becb42e1b6d45a33c2e14641eece14bd4804e d7c8ba206af9b0e363db13212820d47baabccfa5c267d4b1e4962f427e6f090d 7934d134386c17faf25c44c3edbec4ec1a509dad35ffd56bb84633012084c08c 0e295b9603de78f5191db6d5dd44ae8e865ed1a71d8576e752b82d0301aff315 c2b82f28a3651496c369328e6ca403ec2c003b75b57c6a3bca43b60573907d24 c4f6b9568636242a225693c3c7f89dfeb2ebd5f14668ffa9776fc5025dead09f b0df65cf887b002f77377a7b20cf09824625e9a1d7a1a4748908ec254848f52b 5cbde89bd8d7c652dfa26c02c2a0610c7e3164ce1106b625946078ee54331426 99e91141a983359b4b7c70c67e58d2ce156a50f1fe4e288aaf0345e5e700350d da68ea85a8b44c4c3afa2d1e1c0aea3decf4b981085742717addd2a3e81cb3ad 148c448e60e84652b0e6de6bf334cabaffdd6e790afb303aa9debfc9ef5343a6 5fba0ccdba7fc34f33e7099ef5d7f6260b7bdd83632796dcaa49639784bd931a 2286b50e8a3466e7004c12be8327ba623322699081f07e260f0b8731a18d948d a68f826acc073f9608c5adcb3e0cd6a0492258064dfe31be0f05c7bfbdd348d6 d1b883b411d288495b9bc708e0df96ee0335a0da41bf33d0716c06642b0acf12 9295123c1827a2140c84e43175fd21f8ed32961a05fbede5c3e79ad8ad8b31ce a304cade7f08711ec08740061b8ed2213e73ce00885bf5a32968b8c6bba77285 149b3c7a424f52e8c2d09dea92b9463a7c696c61ace54f1d97ce847d83f10404 5739a557f5e016fedc8b83b9926cfed567a933c0f82d3d93fb5b61e28a4db3bd 03bb46e2d3810ab359b302fdc6ed281da33ea699417aebd7ae35daf97806000d6979dfaf1e3987259f2389f816fbb545179097aec5ae3d6ea2a7ae17cf7258006b705f8f4d119e829d7d5425a6d76a69bafc69788e5a633ce2a54b2bee4b23035799f8dd864fc7d9978ec6a2d31cc006127e6d148c67d3c5567d9a3ba1db040d09df41640bfb0b34f8ac1fcf2cd7e5ec5a74d50d923fd3b50b45ebf90fb9450889af8f593c48354b1046b53b9efc343c66e8cc04359ef5574027d37e32d059020827ffa49c19ab6099664f945611a0fb5acf2063fbec5ab2bfc77368cef63e0ad1a9d8852c6d3226a6ec4f4c6469f70f5dea78bee6e455e7a45796f564d1710f5c6b69ffa9104533e30edb652726e55ee85bb84c9d5d84af2261c0e0734d44004f723f7118db2282f2c66dc335931d7b40a3fec593db0ec843ca111b1d968e0aee97d28acea305d5e2cb8f566e417a86b88665722072631e7b419c07addf7101d9145d8b7c3755ed80a150405b13d381c43a4c30a20fa2b33e1e73d1ef0cab058382a663290b4e2fa76be0f821a62bd13c3cdeb119c28edbd7bdaab347eff6006050464a2b6be6710fc46c8370457bb7961c9d0a50cabe9f9e9e0cae8725a70f71ed932d51672888c6fe600646ff242f567158055a5e7eedbd3ffb0c78464b0b3e017df16178462de1183523645ebd2f6fbfc43667b468376afc0d9ab0df5e06679888fee12d30f40dfdacc47e87afac1aa2724268b9deb56b4d7118c52cb50786ba92572fc1c27fa7cbb6815463ef1c5122eb54392bc34b006ad6847cb89a0e69ba7bdac289036ea56bc1e7fed12ca0524773ddd4a2f617c87c2c3068f45002f4bd3c18517933a55e1dd0f4757407a63951da728c95a3079022ae969e60d404da589c2bc9c45cb3d6da6ebc733fcfd576fdb6876376e324d1fccfad2474060aa79b49a865353e7c28a085ecc412941a0744c04d6eabe63d64e2b4712c0de0059e7944b2b3b3c0024ac53cef293f8ee28addf71071d796dafa6e68c0ef0d2c0cd6ba40e7a78e153c1ba949b36c363dc9f8fa8f1afe9b6241625397c4a24f3906572e5effe02411611adb07c9600080e75a5e7ad3d317cd176efc19b8c9dfcd068cb4ec771af676a8bdf585cb45c632b15ebef63fe1ffb422bbe54111206a01094bf9dc8001af7696f11d3c3cd4b15e660b88552060ba86ab4f30f8c7e2ec7f0e14b4b2b305964e06b3dec38672d405c2f7fc69ea2693c243788b28c468f07b0c2cdf8074e51dcf05ab78258aaed18ccd81f0a7bca3e27a23c320dcf567d79807b30e7832c248f7d1e6ae51f32a3914b867d336853ef4cee4bdbcf03db6a81c09eb72211060ee29f5a4e45c38792820130a97fcd3be500a5c052bdb4484e4f60081c0b5f8910db59973df71b4d3e27ab5f6719b064839e313426178653508e006c0ad48d909b0b9c6601435d4ae358ffb9fcc58f784bb717a8c35dc19f1ee2d03235dad8435d1fbdb0d628f195598be8ba50006d310e6b796d67583269b806a0ef567db1823e584381e55c37378c9526b98e6d858680120e94b65308c5ede820dcd429832f97ab04935112e7849a64ffc34d23318bd343b27f8b242f2f59836062ff01e6d636703beefb34246d37ff8971a0c7ab53bb9e6fe11fe516482d93707e9ed816e0a494ea5f30a7f4b96954a3a664a632d176c9be1b031103fedde9b024ba5d4b58193a22d815a0c2a3fe935fc3ffb5a60dbb4e42649bd98c933f2a805ae525b0a49aff5a06163c6de66caa1898db999b4633d6e46923cab986c2b760603a0b2281627d2a839d77db25810bff73921ff60de20f7837ea94744de746201c264b457d488a299697c640dbb9f2a34a5d855c095c3ab9050384e1ee016f70ad9d1dfd4308e97648159971a7542dbcef2e0a49e940a90cb315fcab1f4f09305a298d384274720f322e93e8474da10063c8e93622ec4b7eae625ed23b7cd8306a955f823ce75f6c41c943d8c0f870443cf2b0bf20fe86296cabde129509315057ce5c9eaf86b2b889b2ccda82da91315b74e0ceccd2dc0d9df74e9f9b2a87b04f2012cd2a4cb998626bd78795a9cf68801b627e03594311f0f3fe1631a572105edc50cdbd4486adc035835596613c09cb5005f5816a29a7e03130ecc6fcca601423d033925d48bf923ad755aae4ed249a0a61afa869b3e8c3dcffde9d716a50509337a07757701fb29f2d69137f8f72bafb7dc1236038df8b4aa80f747291d0510078ae3fcc5787f6c2e84c1f97f474c9ca66393ba1bf89cd135aa77a8657801560de33f39de8d0f59d7863dde099bdf9c72a274ea2ca965b96da5ec9661570ded9fa25f7d1fba5c0d654fd0854cf432fc21028b5bef597badbfbbdc2f4a8b077a38830ccc2e08cd95757ff18d8aeb40ae13d4ee56a3264133be44adecc726091e4bf365000cb01b3dd450d7244501f1222db603a563157b67924d70c478240a6e73dcad8fa03f19b5519084c46c166414d97f8b601122a12751d9c2d28ca804bd3d52ed033b1451256af14d8e03dc201acdf1a4a947b5c59454392f31f8390a995b53ea5b5a07395d4723a3f7c5be2dff185a3a4ee8956ad0aee44e197cd90856b5a8c245d54449bceb947a7b57520500a30257fe9f82e68d98cabf334f9202cb2085e85c3cf8dd03a6652071f1acb15f639e9f87be9b522eb6124df6cb6d0b21ba0504d1b51a21a5f92f6578aef51967e4a33dfb69d94258b7f2d9fd41a30bf906bef7c679534d1ec8b5a0a98ef0abf8fc7e813fbc3181728e6eaf1211ba076cdadda71b77c1384a67cfde9f4ba8f5e99a8a353fed43e543115864c1b1e6092cf8354b9921c054a2796dc7bf7d9b9331b7834abe473c9ce7b8c0d0674964029895272ebcb3608eb1b1052acdef871fa137a7866577f3cd07fd176bf32694063258c8ee402e132640acbb9b6d1639df82d32b4a5ff2b2e0b0140144d458320e3d889e0f34809121cc6fa1d113129541a16706e61dbda8ecd3076c1583064d074c90516cd5f77162686f459e513d703a3f0d10a5497a1c73088abe799fe18d01d3cc96652ab4ab4d9f4c03e713489e2b15600a4110fb44d6a0edcfb51f2e4702cc9148a64c539526f3540fe20d7be1f7e57f6f1b50a66c6c0302b1cc21f16802a096280ac2c73f1cbdc5cf20cefb306003e89b60e6f545ab5afad17fcee4fc0b45d2a67ab0e4c5c2071b2c3dc7f98fd2c6e9b0aff11df2c572cfff4a3fd6a0034db023ff282b7027ab9f3f1299881efd97f35cb60feb86fe672bcfe78949410271fc1f68051557fc6bbd7cf13f5cda4c83ca5e6f9676e924d2f0629d5c3d16069da3de335b1c53d891c0107c2d4c93920f837b2a68192a712043acbeebb7b3009b15bdc58a1c5e36ad780ddc1aa6ee2b84babc47a3c06ad2dd8bf1e205d03207ef9a37c3be782d11964ca63bf7b1751d5dd786bfe51e25835537cd8cb7204109fc882335df0e513b0e3b5d659ca0757da9a7f42c47a4abe818b052d9fb81e70ccfabacfdd9c8e71d388736f4717b95fe84a513c4e3820e2da3d8b5c42b2cfa08233b76f1a2194968d743451cd766e2ee01600978d0b0a54f192fa87977f4a702b0532a70fefda7ae1c3cb385c028ed7fa46f61722c94b9d51457a9f5529ec90f9be1e7ca97aeaab33fe1463b534c0e94e8983fbc754e538c4484af9e821fe40c11a5d6b6a0636ca1bdc832296111303956aade825927e414bf513ad565907f043e87e95f1196fa0fd14b6b39e60654c553eb8d4c74982e96665c8eff65486c0810473fa302be93a97ef81c05f22e5c722582c256e0350e1dcbbb5ae652be8e03b35f487ea33fb4bdbac53689d3a75ee88716132c911645e5fea6f20f28c76102a93091e50af7aa3edd76d2a36fc47ad6c35d11b5db699475a9fd12952d159904ace5ed2f2ac1660edc8c5b2af766b9e3eb8d4d304d9ef69aaba68f2f93d2e2064fc0f181cfa83ed19843c9a18e6fbeec0527b9d2a09ef3fbe5da29a310675f0e1409a70bc2ddbcc844bc7a513ea2d37f305788ef7edad6bb40de0c4458b73a010f9d60dfc611107b922eb3400bb6706137fd49164bd4ec4f08534a7b30fa1d081753f728093d26d7906f88f045f62fda8ef295b914c7146a1416d0c88281810c0753515058cce70d643ad9e6c5c60d50c4b871151951d8a93440cc0d8b3a7f068dc4b66847e874715ac20f6b9325cd9ebb025286bd092425ce6f7c0e9f79990fb23ab8b10d22613198b9a54ee9bf1cf802cfe50e0c3f843239c3f6a33dff13004754c6690f8d2459b70b2eb4ccefa3a9b1afff04000066ebc0ee50601081280d5c33bff410cb296f21fa1efa5ad7386b9eb61c386997c35544267b11497bfe05b47334d6bdd4ed2630d9294ee949d5b34cb26e2ba7a787f9d5479f184488650dec96beb6aea8ed15b6432aa497b0d2fd996e8773dcb0612a209e4932990a940473a0a0d2e94595478aeaa8135f6f5085550ed056a805a27ea94b05e77a951d0ac1c072f4f60fe4a596e3a99b3bebe807e558d2aaa531a7874d1109d071059f0ba817d3ef42f3f5034d2287f54cb294ab3edc722f62f3833ec68d974f72892409923ffe4f4db858e0796cfa9d85e8d977b1d5813878c11ceb1d5b8f8aef89fa021b3d029302fb17f19affd16b14e68df3f0139b3210335ae1b3929292ad14530c12627b3ccc1125ed6001c710a14a8d1228f10375f90237fdb28bf179a58ba60bf3843a41f6310c724988197a0dc2fc53856c44c30294782da9ecbfa2f58a3f0cb006f36d5d7ad772fc061feafcca951a201a3dd55951d5d883482a0bbf516f0dffc25454c222222d5928d6b174c195ea1690f4b2e73549545e7a917d0e24a40c7dba4a18fa50aabafc03795bf1f20761683ff0f680924d4e52e32527737aeb06096691b74462e0b506cbaa1e777dbb809e71143db0197426cf0b7fc73d536603ad10e214914e94ba4550fece49e5aff721adc53da3be4ca98b22652113b54a0d247e53e09a19ff029cb24335c51c1d4d7a55f506b5f3315590d39ae00d441f0b407a14779ec8681902bbdeae5179ed8e9d5b7ef1d647448ffd856110a8dadc0311c675ce6ec11800374d7cf842ec87ef0d71df4f6c5792df4f887ec25335290bead12c61fbdad8143fb8c32bc96756f36b79d67631d7e723860a59194d6c120db9cbfdc8ca73342ab24f70b5cf5ba7e1251902dff459f4dac4ebd9340d0cb706dc5e8d64bcf095af9b41ebd632be5ccd02060faa1274dee91b4fd9427f54990edb5aaf2206d23ebb7f1cdaefcae8fdf0b23b1775da8c42d1e9177b787844d900da7f5842885702cc08ce012c71a7ef1a740ff8554ce9b7a64c44f871cbb1b80d2d5c353b0acc782c9559a39806f3e2e397c1f22e978e8e0d0578d782d6bc5508fe7d1d322563c80b3dadf98984f32d69ad19a813fb9db9a6a602ee055fb4f103893342f4e3724b4d392634859eab58fbcc608671fcf3128da14159b4faed80064fce7486db6851d67a3bdfe979f639ef3e3bd663eb8f69ed68241eeca759a50171751efee33f41c66ee3b4b369c9083f26966d0a221d5c75d94c6ea43b038f09475b6a3696af25cb8b1d2238a0d26cbbcd703b12a10f210904f2517043b7080a524cf04f02556acd53848e1b34383ae2183b5b3410b868e1cbd8d4937110150dec224c7b55698d86887807e08a32ab0f291096d721aa1cdecf1faea373e5b5026f6e60393beef4ac6d40d8059c4b6580ffe665b44b082c7757ad1bd78bc27309 true +check_ring_signature cca4e449006e8a841d6aaed1ad3925f5246f7d675a3b64026b9480335fe8179e 300a7ce092de5e8f5d77cf4a7d96226c2accdc2b3005207a5a6b7ed887dfb454 2 ddd0b0706a53ba9bcbf85657fa3b7790acf7f17a48f59f9a98f37ed3db642740 b1a3296adbe6a4e11cfcd06db4e8c2d00834d40695137635c3da237dc45752f6 e7f0f05668650689d8fa0274d2f63e20615855a9085f8478762b6cb4fe16990f826c6fd65e00f54de134d2aa2b10ebe7b2a5216195bfa3b979b3a7b3caa67e9c2667eccd11273c5da85e90e725e2c6092b73ad68bd227d50cb92718c9cc28f0cd09760e363ca8be989fd8c25c5deadd8ce3a3bce327928089375470f0e35d703 false +check_ring_signature 850abe3a8f49028cee3d313b5d694728007bf18845bf7d074dde629cbd2b19ca 8ef620a2760d7a4830ea3801a990ff1ce4378661a724be578f01d0885d8fdbc4 4 688dabda7b08dcf0372a93c3e7f660caefeca112379c94d4075941d6f85ca626 2a03dfcb29b6c4d6a9506fbbb4cfb7233e3d7309fb23547069cdb1ff372e9b00 55e5029e57d886a8130ed28ecd12059d7e6e47232df321a19f03ddc811adde86 43011c13da9132037289d68ee83f1cdb431ebdfc5ecc390863d952e42f53889d a803ea64086d65291a160b0c4ebb3b78195471a9b7592c7345fb39663370cd0fb2c57693575849c6bbf497b987441cf32d54c84ace0aba41c874bc6a78e9110a2597b22af6307be3839c4474f7df901595bc22956bf9e704c8d755008430540ebc80d541491cc42f9b8b16798f30fb837b97a86371dabc2be6d648a59cf02b0e1fee7c4bdaf0eb0e4aa5aa903a7c71400a810d72c0b69e3fbbecc84dcad8070f1b3ece7d40e6ee3a9a658f99bda4deb93687fcb3af730f79296b65433331c201349cc5ccb067adab370681d2bd43352e5923467d0b5d9bd865d6875f58c113037a842d7489ba5ebc2d8672835ee867fc4cfeee55fe38dfc18bcb0dd3ceac6008 true +check_ring_signature 249c7a73ae2d4024f0c722bce8dd1eaa612ee429a899d78e06baeeb7a7529f83 ab371a6e2d2ba0a775acd7f38581aaa28173a95120a1efd28300f9aab6c1d5c1 4 7352b9bf4073ecbc26c9666f75ce0011e95325d923e27cad37c71ca96ee70dd1 f6fe60cf1ecd040034683184a10eb3e56fedbea9e0189f4f71493abc0685b2ad 3c86e54f20c004b0a1cad9743b90df7892025410a7bef24a695df46b59c606ff 8e9f5c4fe4a8559b18db1ccead24e9b7f3ab9d4ca20b0b350bebc0fb4e94c1b4 ddc31cb590b441886c5ebc5376e948fc798d47ed9c361b19ba0ff04fed257100af7ce61b3437ba81fb6a96c70d0258d6d0886413ab39b8e2470e74b07811790ab98930b73005e40dc5f20749594560418ad458be0e709e4a47a036faac5c000f9735e95ac644bb4e8cecd9ee5ea5cbdf23a48afec2b2f8de1fb9e9772e761e0be678915178d30625605ac05e003d6836f292d1ddcfdd7199ac8c7e124ecdc706a76ea1ddaee5c10da144a18d0bf0eb08d531bc98024f456ca6ba8b5721db6a089fc9a7826fbd78d685353397b04aed9a4a2238b666116ecc4196d089361a6b073fceb9a6f7b412c7cc1d0e2eb106d90a6ae766415d0cafb09959805575207009 true +check_ring_signature 1f41d0b74ea408f0c0357690baf1d83fc705559a30efe0437f1ec5a3d80c583f 2ebcedc2b508b55519ec8583e4b0b591aa0350ff45b4b5c647f3fa7763555dec 103 d2788ac576bbd7a977aba74312daa59e85336a9951d654da4ba62fc722c7264c c9ada99684547dfcc829172d72322c1e9e8238e24287330a4c344fe05675b7ed ef3732501da0e3ed8ae08ce56d51d82b3317961f0b7f8868439f40e546da4017 0d15017daef8c55b7c5d5791fa46cab2a3e5eca09927b88aa59e1ab8b5f18400 7adfd28025014257643380aae2ea7da057186f662fbda78cc3715f957e2b5b3b 5c85d5dbc53b7292626df2e264aba3f8c783b1bcde740b6cd2ecf2dc8415d837 8cedff43e1a19909e911836ff8abdddec398ba7253b801ab22c597a7586be792 311fbaeaccdaf238fb472fa0f694f9603cefe6d3326a33e247f8bb13054f7068 b2e5d87c544cdfc0a1fbd14de61d931f24ca241de5794d6fd1018ab3c0b62e9f a7514cae237292df4a24bce97609564e16d18e7a816def15125555c6b1ab7f6b 049bad1e1c9497944f7f497baf9d7c14b936ca0188766e1cfe249d08cefe5ee1 1a46e3a3a73b67dfaa3510ba2c93c7104dd4bfb874fa254578a941befbab7385 094c41b497544a1641845d70e929c3ee6eba6f54d60072f9ab53ef7e5521e6e0 d21548e6d4f289b5a0927eb6868a04f857f1e2890ecf29b46c87a0ce7094eaef 4ee86efb618dedafe000db1ad99b0338be4ca8eaa8dbf0717e686ad889801e8c e6f70c4dae4e95e30f2e6e0c5cd0c15c2f6840cb39b0c0820e2119a082a4eb1a f203c562709c85a3c991484c55751d6a857c628bbae31389c8a2ccd17c00f339 a63f2c54566ea04b716a88f1bc495f410fcd586b66fb2f2ce80412b5bba1dc50 341558c1900f7bddf73ac86004c386bbbbb4d862dca36b8ba7162fec9421350b 950833c8ed9b5a964b837c0e173598df7dc0d275bc4fbf855fa4908ad44335ef 72aecbcb4eda468f4131f1b3e583257f64bb84fd9b3c0bf2591e5b738e455adf 72e47f1f6e15256820202a86e658d3680e17ddd825fb0cc9088e386a63d3d96b bbb9fc31a62abe05d37e1a8554d2482d8ba9e23eb71f3d95475f25665f21dcc5 702c949e0487c4ef26fbcf41e451f4eee85ff8f5e53e989476cd41da9ff15ea9 cae6816dc0e02a67fcbd1c29a18ae6eb64bf479ae4df0c85e0bb44fecaa08ff8 6dba2c4b53ee8284cc8313ffac2bd621cd412ea905048e91bc96f384bd5da70c f10a88571ea96effae59f4f9fc558a9e83d6872458818dbfdd8f4e3780e2e779 70239baa63c50d7fded89ea567ba60167de14a001fde9f454a544ecf61419550 3acaffed7a421361ac90a693489f483ab595c48a1a6700fed209661bfdf6b535 3bf22a9e5821b60621e166213951f3598612fed5b88889de6a7435eaac120d1c 6b916fc773a87202a86a3d3444cec5796848387f2f7818acf2df7bad8906f4e7 46c21cd630c8895ded421b10c987d6ec0ba528fe0e29a9ccb712152dee03332d 0a57f8f3baac8bb43b6453d032a8f2f24d5033a3de51dfbdfb61660440788bcc e85b5458e44c8c82efae7ea53006aa10555f116d7bb7b13e1b91b92a9de07fc2 e8f15fb0f21fa3e876eb848163a69e409fbd1a67e02c8dd7538ecfae50e3029d e58ea8c87fc24370c2969f65ea3ff340d49412acc9f36d80407a6c54a1b8696a a2632c442e519eec4ce28f47ed050bf750e73eea80907bccadc693504e2cecb9 74313e70321ecc46900c71d17a034401e101d1697b0f4793141eea782dbfc292 c8ee7e7f3df3c3a2e8051378b938438ceb4b1a84335ba9809a03a2d5e3c11c8b ed08bc9ac7ccabf6c1e197eda6b2b898c27dce5240e4f137f9180b897249acc6 64f507ce2e406c5a1c29fe841bcbac31c9febb3280e5e06cd19655f6c67959e9 ff25817bc717da642babbc7918aae93ffd5927626460fe7156016f935ca594c5 0bf858edc33f64fae8f17a50a7e75ae532d9f46e0c021942711e9ad8aa9ab3cc c1bcbf15a920860b00e3af855a573f25b7f168cfb27a608b3b8d887e40763894 f8720e57f772018741b567d386c4745ad7a6562c8de0994de7e8864ebd2792a1 e13f8580bac6936b6e607115a448b717de4b5a91bb67257678a0b8ca1f0083ec 33652fa478d060778f787a02ae4cef0e89ddca7d525381470cba78f748f8d4f9 3ef1663737719823a5def130d5536a763425ab025f9a70159ff5b87ebcbb2623 4be65238b753032f91273212e325b3a0f22457c0f36d86e37950d66368c00c4d 2a6a8a5216f895cf482a0f89ab37344f33e27d32bee97887c670c539d5707f7a 3e6d7eaefbb61a27adfd8f51cfbe9350f97ba885f7447f5314530a1139a3aaed ad467f5a54dee01768e23e7cac62a43309833d30a998b4272de93e28d994d592 033989a9fa17cba9f6ad5d518b9885104c721cd493dbffd264e2c4c7bcbcdc9d 1af9dbe4a626e413cd48a9bf4fd758b3b6b40e04660fe788fc82c3938b4e1d87 7244df33e1c77e2e3fadafea7c891e4d71880505e7b4b9892741fa070ce4defe d4eb8ed87e469bcacac561ea27390afdfc835665e0ec958e095c600541029aa4 d2f853d57b33dc2034088ecf0f384e8ec545fc7163795e171bfb627515875e13 5dc033a3aa5c22c5ce43cea759c6d645120e404b9a34a7d36f57d4211769dccf 2b97059a8f034ca98f22d9bb9f0767de742e9254346d852e8f874389491c88c0 dfcca3bfd3bd1d67cff468e4b0d6daae09ba867348d5c5735c5941a9b71990d6 8e09c4662a1eb53cb7016da465287a4df714a22f05d431a826d9434a3648fa3c c4870693707c2fa09d6388303dd5bac475658449152771900209e873864732fb 3a20a1a4ae0f0da242161336e3e9a992cf82e588500b729d4b50d0f118cf79e5 af859825647d1c35cb6a58b2863904da237bdf788b3f56499ebf8f8983cd2f27 2c169751a0ab696ed78935e2cee2016a52cd8fc58543ebc6482d864e0d6dca43 bf99f5a6f46e93051f771af4e922cdddbe2fe310437606cf873766c79bbbfd68 240db6870a93138c1813cb5564b680966bb5fbdcefd2397fa28110cc4e244e24 bf7500eb5615bd6239a2ca52d798939f9b55d1144c3f0ef6cb9e06867ff5e385 fb2206c07357cdc076305ed345a952ea360a57d16b70c4d49541db14b93199c0 dcfa5163bd43e5b3a7d381216fdc989ecad08bb7b4c6807a30c0c3e9d573ef18 f7701a31fac3c4637d3e94668576488e92491ce9ecf45999b25abd5ed5e8db85 96512434669b5a56146d471baecf1c02fbe2ececd4a3e8f77641b94cdc6c0740 c0abf79c70aae527fd375ba2a8c291989a0e4e9069c0f65d910146d5e963499a 571f191ba0cf2a0fce5559f78fe04af2ba44008ba811ef81f9987fa7b4a254de 25cdf09f72ff3d174dfe208037e6742a5c76f8aab7d8f82b1eea06df017b0018 d349da12c2d92f4a56e8c2a4c7d12aeda11ff9ba1452892783b2c237b57b5ac0 4d640fe34f2632fa4d8b7a5bc5f46c342b3864f2da112a22b421fa36ce7b57d6 1b0b432c29175c5871a41eea720538f2457d56bd3d5bbe643d5b5bd9a6eafd50 905454da5b1377ce6cdd34f6a26bf263faa923843f23a20165cda4f9c5d9b8ec deec6122f6b367113ad15cd9a9c57fa7a61819d99d248176bd46798ca9576b4d 2270cc14b0ff694ee582e207aa7e032ebdf0eaea50e1e1be458152cdf49ed7f3 4e790a6b43a705a6a10cf9885c37dfbee50611f23282eaae30979df1d53484a2 ca9f31dacc64d0184949c311a6aa5e28268aa6e3367910c483c03b772931dfdd 4c89faf355b880469baaf3fc4ec4407b285e4035666a18e0c7714883c74f179b 1695fdbfc236126a2a68ed3fee3750da76c272dd9b80d6b3b4c4f7c584bbf95d 7cfe2ba05f34b7fcf6db8234acda4cf609e3df9507cbe9058d617b2df0b3e9ed c00a843a661d74dc99cc41c9d212bde2cb7bd2bedccf1f66a0e3e1061ca7c6fc e5ff6bd13824000e7941cd65b0c7319173bccf7c460b8b0c4f343b97e549cb01 9821f23ced0dd8abfebc52331914d7fc05e4cad6576dea67aa09b8b706e6c068 dcbff5f9c30d22cbe0129ad214b4ffbcb46d14f2920bbfbad4574d4c7d2177a3 110baae383b4b547c76fdf0a9b27859d471d2f7ab30c909af150783147d76992 860c5e8cb748d80db53ff98720be61f06f972da4253e0b874ed26a4503e95dd0 b8a89e08470b260e72a5855949a642fa50f1e77cda89aab42dc67f8c2b30d67d 6f0fb2154ca8f187809c6b2d74166dc6888e7a536026fd76bc752224fbfee4f7 4ef7a050678ed2760c1ba7981b6dd7ecdb0d3e6f1c3ddaad47819450708d7abe 3a3d0e3d45d43ddcf2781bf5888824cdc59e41c6358c9c5cee67ca8009d8e237 94837f96fd7d480be0862e3f47a25094c0a8ea1d7742b9dd0feb81a16c5924f1 e3e7cc6f758c6f436829d18bfe37e11833c23e57122df7b7c65126de6c3ef67d 0443b39dd7dce1ef1e6b820ea4ae312d7897b3c4ad55fdcb141955b9f577b6f1 d886799cf5762007fb989effb78897d780bc3a76639fb813ffd79f0fea9e4bb2 37df616dfe7cb68b0d549c3b33f7dda9213bad924e7e35112c5c94acda9b9e03 3692511d9290f89b4bb91ee35f5779827da258ad8297b881041f83fc3d82c34f a5ad26743b2aeea6e186c0382c261c41e561da64d1ba6722ff2c35bcce743c47 2ad11558d25c7f7206605ba4db65d9aef30de852f72ca4c980620c0d4341ef016c033a309fc758c02a20a53f1521fee6aa3339cad798c025914e550aa653c804950c22a75cd626231d8d1a01f0cacc98368f5de972e1cbed62ec2dccb4f48a0127ae4a687501b874cdc6324dc10ef22e4e9cc6baa1da5d2698f33c328c4491035ab8f861a6ddf30508cdb0511ab4376eb965fec301643687c642ce613725b3007dda6320322e344a9c435e05c0c5bc3490ddaa6b30398206204c35ac814b4a05ffb300eed0b2f237f842a5f4131c76511a29e552a560070d013fbd50f0413c0a447fc992919c704c8508a5eabad56235f0c6dfedc18d85bb08d7f48663540d093ea397f59ec0e68545b7b5e8164d14d86ffb76a18e463cc121dc0dc9a38e430d6cbc66836138b35ed1b5b190b37027346de6714df9dcd47d5faefc14ef59800d08984befdf6bb364999edadd3736ac7e22739b64be31ea12f3fab91b7fd8e60fc58058e186bb97bcd6daebe0db56292cc84f5da53c1d480b45fe52310c1cc70a421cc46a4231dafab99978e0f3045aad9fa9606b46b721b29f247a03c301bb09a19365780c92b6a3bbdbf030a1631adcf84e8a2e581bd45c5389296b2ed1d40186e631116032a10602ed51be3d621422903967bd22f23d48b0ff4f016f78fc0b1756f43bed48f6549b3dc923ecb17e03dfc8b337a79b015dbfc8c44023aa2a0638b7608c3071587ba6d119110a6fa9b6c61e635252349834d2a08337021c9a050b500ae5eebb7b91e12ba8cffee032f2ba88f88a36d9fbe6332c7c3f7d4be7084ddd41a7ae27dc2836ba8ab74d517be2dea3c50dfa93d012a8fed3094c07d60ddb3636fe97d0edc25328832e292201a8ecc1b75d07a63a7295203350ca07970cfdfbb3b49e845a5048260c1afb9ef4629cc8e7fff17df198981ca5dadd68630a32dfb8d6f09b3754252708322632c164551dfbbd2d6bea44e6fe9ecbcc2a3605f688386cf7fe82963c047a33de9fa2238385992560612dd9052f42e4cbb01a05f111e463747cce44feec3697ce901656440909c47ca75ce28cd2097c71073000cd9a2d73591728b18f32b37f16cb81b4c839ad050bb9eeb03d022417c69f38035d6d943318db8931fe5664dc1d5e18acfb2838e87d6b5e6f2ad29e490d1c68078652af2a9ea08a0583f8a1996b0403f965dca8d0722dc7ede059eb3852c5f5027d40b721d71da089837c685d3e4faf4846be1ea8009776c6f8a82bea7a4d1600c8664ecc6c71f6c5b0a6255907e9975cd26a75d5b53d160bf6eaa9e8743313075689610f06725b00599a338a9622f9d00ea3bd94aee03fc80abda11ae4fe730eb3eb49ddf7abba9c418fb2034bc3cbafc05769ce5f274739322a8076d4c2c800d5c2f914f0bce826386b379709b8889e6fca9491eac7f54e42b98315e4f6a601837efddac06bd3d314d284b6190630a35bb25315bfd7d23db33f3ff077438602b0e719933da48cb0bd1883a6f1f394399d6c1880058a6f66a493c4ddc1cce009a464692c2db2cc720bc5e750a03853cbfaa8bc42e8e9f964f557c7d8a1fdd90200938edaa50c86a8ba42dfdfa67df65c6f70a0f817942f8f843f303dfd11070c165367e2031b8aa632d2d54e8fc92d0a380e4655991869865b38b975a64f40075d8f086c830721655c56fa8f451b757b4e97ac1cdc3f43897bdde5f66e63a20ec1be4041e5e75f3a39f34cc9f519af1a5c7cf80707c039390e5c5d20b3724a05a8269dd35d0b9b016a81aa363ffe561b8a2f64973e4e8af8a2e2fec9877a1f0d2814fa860ed404cb569ab68995ff2fa49bd1b9c5b28eb8964fecb85a27d527093d875faa0e3417091d9405413ea37dabaec53bb380b48135c2794220ffdd120f6da5fd0e08893e587f1edc0ddfd38812bb604d4bf4dbde78a6d759c5e7e6910e9ce2f3a67f85ccaa0209829cf8ecaef910bdaaf401b209969adac2231d2cad0397704423d9e17cea22ca8903a4e7edd12409d17f44de23d182ab4d2912991e0d16ea74bb9e3e3292848d8bd48f3ef9c4d8446fbbc490d1969b7d5e541100d60f32f3bdfd846dab459e4eeac57eee70be54d792649ce48cf900f8b12a5e086e017c6f23665da0052ef4093e948880d2ac7d04945cdf0550cc80ff6031524b4901f0e83c83136aa9b2e3e8eb31e66ad0dcb36e09548f35981bcdef76cdd7acf00fe0084c3fc14be3f00012ec5a74c7fc0b6c90b08ff49d48d53834c39c84c2960546de693d4fcddffe11df846cf5a2931646a2e2e46046c37ed33abb070ecb3e043c7b1c3c19850677615394769068d449163b6b4a19f6f664c8224f80dd4f8708e40fe6a2f47845e1077f613cf4ead3f7ec0ab864940e1596bb574c1e2fa9d90049fae463cd28e8b9f08a450c25b1d1b4f7458b2f024252d9e1616ff1c9e80c0aff3373f68004d65c5bca12a2e01901116aea55d6b9216fa45801f1611a7b6f0000cd278a1f75894fac212f9197f013cd23bd6354b9ba53a6dfbc5fc0792fba0cec005fedd6dc1aa3f030d9e0a1e67b42bfd34577e07f32ddcee77a71655661087b7c5aa451a54d5ee7b5006a13d3a6ccd441d3854ddac67374777a34f5c2cb0c2498694445f7053785f4ff84ca88b09eef2aa897e7b816503956e1d45a1d1a07409784a7cdb9ab793b83b9159ffddeba0867f5f187af3f63954272e51a14f3054c4b77d066ec95b62b8b061a84d70b07b423172151977980434e9315d5d3f109706dfe85b693da6de526be0f6d6c374949887f315b30b6758d0367f223ad01043fdc6bba768c35941a9f95ccef5d5d5fcdee1d9cfd2b042a9cdca4d8ee6df30670c5076398ef08d422004494e645079368ab9c9e8b890bb9b2910781e2516904bac1c08a4e7b13b61e58318df19f6f98ff9f1554ebbc3ff490e935e07b156b0a5d789d91c3db073217dffbfdd314457762af059f377505685354642fc75de9086d67878a8cf1f1306942a9e836affed4c5edea0835118622544d211f5feede0c7a5a4b42ac34fc09b706cb6c52370b399919c8f136889c23c7fb6acd190a770a48e193e72870d6911843a3d668d1091415ede7337e1c323076a2520d39a8d809dbd646dc2d4f8b83b7a4a23d7e48664cddbc3f61a25dfd2332fffef9d1dfef0f53493afffba75ca37505fa6e9ca7529d7ecfc88429c98ea868066c1b5f049008f2a21962a2b592bf8b493bb5f8a8bf396518e5434a400621f6b6e5f85625dc0236f81faccb02a4c67c1b20c0af8ec3411c3ec4ccba93d8fcd0a22967e63b0f058e785fc46921d7e45720e99b8a2b174bef1fcd28f628255e73bc7158215c2e01f87d87247136482227076e57252c4660a633bec50cb8af0ef96578eda7661500896e9d152fee6ce841ca21bd6f4c75e8e143d94f32e078d4a494ac269620b70bc83fdad8cf30f129fd9927e970d4428f88a9d2140693548f4c04499aad8d840a1e4ae55d2dde1aa1b48d8178c151ab90f38e922c7b5f4762eefc5320dd527d0cc29b24b3f1637a1f21f203380ff7e09e331fa32e6826f5bf4d5b0ac17c112d047888a05cbfdf8abd193a73f226003fe927b0d791d3702830ac09809598091c08aa858aef033371a7f0a4cbda08f2c1119f8c12c0eb45698b01f84f8e1e3152010a16b30f4d7a46912ebffe2d6e1951f45aa62dea6662fef365a082f4a0bf56008adcb7c88fee6258a7d7e836ff3e6bc22588d1ea5a89308fdad91da7041758018e83a95fd00578212ce64a6131c1c13536ae298df14c0f89dc2e90d957a8f200735458d24e2395dbb141b3f4a2c1b96e65907cc41f2f050b6b76b8fb4fd8680cd0b3b2d09fced3849274d14d307e523e5660fa4e261d1d8f0fea2a2c38257303fe654095db2358604f31a29f166beef562e502d74ecc4b2f9cbd6d1bfbd4fc067f0aa457685fb99f851b8bbb803a247fa628fb684822880f516d134c181d960ec2beaf251e05be2e4b9b2b4f7bad8d02550fc3a8dc62d42c2d8dd939b06ae2072143a842748c19d1be42f2dbc7934f64fd6ac556b1cdaf2894df51ae2f13000231f9bc3c656fadaddeeee5d5a6d484cd5cbb2cb1fbcc406a8e8984b955f2b50f9aebf33b298855ee2bba3982be49b4bbd51d4a88d05bd39d29a8991dad63ad0d18866c2e7b43c1211e69c4072bedc77203e5401bdf64ad63df154effb124e509c902b428fb0accb37f96606f25833450fa9a435e6bb02a44c0a8d045a687d20b9590975c3f74455b3ac07be5e08840caccc34487515f0fcbc8719dab934b520fd1af146aae4a98bfd41eeeb1e88c0baa11504be8cc4710707f6ac3e1b7806408ca7786d2066196c89911239bb728c7be46b71aafbabbafaa179d889c84be30073befabcb0f7268452cc1150712a833fcbb0b59afb3d2aca2a1f3fad8a9ecfd015cdf1ea29c1f8eeef3b28e7aeef554a4a2224adbcf50175bfaf48dcf91bf8302cbdc9a0120f53c7ad013f2122993b5592dc768eb8790bbb9421782cfd799c0091aa705f7ac3369fcb962b3ef20eca5f63b0fd1fde7bd5291297779ff1de77a0856f79b3e09551d7977d1b5bea4728238b9ede0c546478a12cb936309b36d6b054df1b501742b284e9ecf144fa4c23add793310e4188dbce920a3c5037ee08303b4995e5fcf97d5cf087df9b9e4be3ae3693b79a9fca57a4657820e99a172660e8510738febc8c49ab57911870429991e1a0243387973c21ae9fe660562c8000364224dc99f3ac9291f6e93ef7daf986dd9d9e0c57ead27b5e9cc1f3a7dee400329d43eec5ba2601d2fd5d3686c49e403e1633e38d5083443f5d74f64083dd806804cd32a545463020462f7b1ea71422557b48b21282340e5a4ccb8920dfc520f0ce915d6985f0aae1079f66cb10da01138601eda6d6992c265bf2978945ce109a5023cd3c4fc2f080f012e0925d425fb47eab00fae488e58ed5db411a590b601b6ef02134bedaca6cecd82592af09eaac7a7f75c297cd94c42332936a15d270fe643b94daccf57d6a50ef252f3ba45ed3d22a3b781397f85ab09dc6f39c562069a9941182c951aee173a02e642f1095fd152665d346f33fb62184ca359a3d10a552993f576ebfbceb1badcb90912502d84132797491d24a01bc6a5e6fa4cac0ae936c04bf8834c3b1dfca0bb3f667ba53a4a0dd21fcd73a1b285c7229e05120f77521b69d50ee3b89d0e7cf86b4bfc125e308dacfc7c831bc12d13244205c60cff456179e1b1e564a7b9ca5e5532ce6f53cbe02a76735d2fcf753fd7c9fcbe07dd70534fde42cabf7a63e74a95d7e5066c2a5578f91a9bf37a0b86f9ddcc4509c9892f525aa812595adc1da555d3c953c7817fb13fad2ef7f7a52ba456ec890e1c5db0416500c52ab649d2a32f8545ab6c68936cdcbd5237139c145999ea15033e547dbdb3aaf22910cfd27e9c1d1938f7fbf9990af422bf52099f0ba564b007352d258fd831c8a24178d79c85b1a682cc515fd6bf704dcd652b568645c52e015f76e2fe81a2318f13ce8f1384200b44e1d0e5a3cf8e9d77af8731b342aab50985531d18b94e7421b9243409934f07a001a377c3f6aa4f28816fec1edb1b3e04bacf8354dfffb78fc2bf0a33a8fe921635165efe78e0ebd6e469cd4436ad0208847e45d235f6b2721fe2cc4086899178932f5a6bf462c1ce20b3c252160c2102ab9312074be055787cd7edfb91faf2dd9b813267364218d052080f0ca04a1606258e6fe1aa864525b1ff53ab0419e7044fd6769347405ccb5ca03b1bc7552e089b5f8f089988fd0e4798b6160c97750b59e223f9fe9379d75b5194abc3292b0720893095a2822a4a2d8260b030fdaaee19e60f9035b917ff60d5b827f800ef07edf1489d9e46760a7ef9abb4cfa1b28303f95812716fb0369f86555f8f87070080cde514b71973410da459c2cf02346b81bd7c8e58386c171e8230c0a9e6f1053e775a97e4e5bb2948ae626ceccc6e990e15030a62321312acb56f69bb83b40b3254f0e6b85a65f380227abbf9121df2090d768b0f9ce43d903a4569e2ecd10c736277ec216f0af2301425a3992a5e886979853fb41e056cbd106f8badb0df0984d0ee913ac80f44b963d79794157241c73c554509117a98a90bda414926940b500861cd6771e10bb1acce7dabcda28ba22790924f9d33102729e9e07cbd130be6383075eb023db5ac81fd802da2dfd26aea42cdcc5457d05f8291c65ebaeb006e092bee5ab50c3bb6584fef76fd29713d2033d0af2d1ce2935380df56bc5c0aab63ba92590ed0f642f64b2ceee26689da2df11fbe8385e76b8687a2c7489903c9fa6597cca9cd39cff03d8b5d11601b65aab94091201a2f20982fc3a19cbf0349b882107fa715d364259a1d93d033d487250b79bd5f5855cb767bee3877dd0c5c355420cc009e9ce1b8a61a4c58cab4c325f50d7ea0c57588e8e198511de4090b81cbf3e5ac8f145854b5efb76550ba3ef23eeed7bf3460f34761a111eaca0ba25acf0481d3629465856a5243713913252f46b209e17f4d53b378f44d2b6b0e5faccf6b33d2c69f9bb67d6aa0e0573e494499c48636f640b85495e6b787620b1db614caba94d36ab492af100058190ef5eb045046d70004abbec32c1b9c2607ab16f4f1c13eef1e7ae977770e6269215c832f1226c2bf8a558a777a0bcdcb0374d75311c95863075a013bf8dffcd6ac73bb03d7f02e2b0791222686d9d19c07ed6e1a587444790b581c810c31dc91afbeea0028a91ee5960862af839f380f00725f2b1c987b01876dbd51fe58256f1e1b4f823c5611110648d2700f666d610fa6b4f05e7c07b0996bcca808c083e6c6a442b3a5b242402b1d0f4c4d1073770b6cc217b641b052d4ca3f58d9b68c8b2ca1123986aa9b7d0b4911efbf1b07d70676032f873fe3f812a9bdd1b1a57ea6200f6e206bdffbc72b4e31015825fb2f0ed20fde0f42f35f61723e3c21e869068b3010743b5563a0aebb91bc30d3295f03d1ebe7c9d145447928d008a24367f99f00ea4ea7358b9c2aef9813af46c01e0160a0c848dea1bccd5b8ad6b2d3e5a49a39d09c6aa8a57270b4150205dd795a09f7aedccde5165794425d4da035d36765d7bcdcd85b1b138f9c71c828bf364001244d17c0f11508a572a8abc37c0a25e6939062cfd40394ba7e4ec41ae4b5e60d420b84f78122d1b8c643d109d297e6b3d34f9efcbf8e140e78bdb1806a62690b8ea705f0cb454bd156b52b62323978179640f5f92fa2fc7d9c010d1228bf6e07691487ba797ca016e37ba0e1a05112e3e0dbedc0553ad51167379ac2a06c930b62deb736243bfc4b7d638af4c63cbd859e56ea7f6c3c28bfac454713fed42106fe5e3947463d6dc392b25aaa2377825112747e992d001c78e09c80f32639c80d74ee922a479e94625251f767dfacde18800c31ca5eab3fca6944f0642963f60ca91b72dd018239b6c60a4aeef53b6e071ca2df8a5913ad210b00381d3299a10d8eec8eea9d79b5a208b3af3446d8902d8c01c6267a610ef5e7cc7bceb545820f03d621b76e84c3be391adc23c3313318a7750b21d53e1649baee90fc64812a0b21d9bea6af9a68584fb1b5f2da724807505d3753da368d1387b28ab428af4d0476c8d3efd25a0fb05c4c4ce6a31f9c4d8ab150c758458966cd5f57506e6dbc0640a1886450f1e6b44c0e31e9b51887b038e431cd687fb9d6b05b9e1554486d05d10cb7dc7df8fda69b64422f97c49b6ab0079071076a3a5a7e7b2e6968707a076c717c5d536eb01737fa1bb17b209f8daa78be8a9f4be32a0c279163e17175032c1337fdfcc7077c5216d2c0234fa5eda6bd11ffbb7c8a0bd6d30d42db82f60b5365bd40c6f479f6c4fe7c4b81e7fd8e75adb55e3fa41c6b3da04b45aa34760b87333b9581b487f9343dd821b9a8889b038e74b31a6d32c2dad74b9c85a0e00011959cc7ed99d9d6d60f3b5db5d07bee873a6e45c8372090a9936f10086a0b0429265c3bf735cf5e51b9ece98fd643451c7df9f37a774c2043835f8b8afcfe038ec14d9f4b4d7d6ce697fee1a8d0070dbe38eeb94a8725550314fe6f125fbd0f09ffd74a678b2027a3b8615b2dd578aa5e2ca194e78888ff7f8044f710d9be089178324300006c4b0fae56f2680d6c5172be07b781a0b93d7323c98ac273c407102bfe104a64d5b6893e84f6f87636d36dca7a83614e07f13fd376dbe43f93017ad2b669996d1d975c5f4ddbe8d65952a9945cf476de1da854fa85d97ac3150841481a4d84ee1054ae90dbfa70965dd50a9a8e5b0ed6e34caef3edbf146f7306e254c6fc9ab395af563b1386785b8f7caf6001ad6bf2bd378d357a160259f80b0093f939378b5c2c78a70786189a3af476b12300a421a00bfe77dc25c629ef01b33ec254ec9b740c853fd882b84680d2189f359072129f7f675e45644b19e40ebc1b09c5a416583f153d5daffe23a7c95e7f99b3ade9dbe77505128d5f2d380378168d397865294524634c7dda360f065efa3f2687a9e014cdeda4b833d36b06d202eb850d8100e58a13d428ec716970270ff9d1aaaad68aaef32d18ee665b058cd1631280a2706423c208faa1060f887b87ea193bb45993f01e956a231b80035d232f8aaf00dcd9c3c219202eb4f312a3c2c10113cd328855fd2048c088870fc92fb84cf0d8c70444210dbc6c50b21186a2a2d0c8ca47c1ef2391ae6b3f3d0ad4a6834ce5d3be27f7b3d583129bd6239c66f7df9be4294e5fdca4d6abf78b0df7afd0a5b5afba1a954460dcf87a2b4241ba01c214879e7de01e4b12b763c00af5894b4b9002bbd75ca0abdbe2e8b194843a14825d0c2db182e767808907c2026b3ad6d5c338bc534afb3b975200a9edcb64e1fef63719e2039fffc68abfef0205a2a27dc3b742453d865956fe6d122e0f54755ca12bd34f59d81fe958c7bb09199d6bf489855529743fb40dbb506a44a231ae8e90bff9b5c78f9cc608ba7e003b37277d7cd3b348a8b7b20c363fc444f9ff87d05e0a0b42315a5995084ee40da5bbac9caece16b17fdac14a62b203969a6b55f7f599ca1b5db8111324afc9094f18c756c180fe4ca8ddd8ff679d59d69cc2c01a46d7eeb556e46b9a9bfd8505bb53828cfd1fc7ddfa8371a2b2c41cdb970866635c25f0978777dd163428670c54d24a069ebad407ffb12d8f87338fb12dce7efdab856c1c054abe32de7ce5065be55976db24d975347a21cbc13e37e28d627f3f448ed8cb6bed449b45d4e60b68345618e6f4d47218175ff9e7412fc13ec23def62ead3824555a2eddf29a106 true +check_ring_signature 01a83e5573971de7c2714925fec54834d19ee0d7fceb18eaf9cd29cd8c0ff014 7ad1d75e6445b4f239190807c5c9eda93fc445c5fb9e91ca7eb20c45ab9d56e4 1 a8fc7fa41aa908f97d97b3490f1abc5e886c31aed6779e7fa059a7acf8ec6559 ab23a529bae8e8bdeda7f6c4ecbf6a5ccc6ca483cc384050ce9018aee58b5a097fd78fa4ea3202651def9ea2c8e7687a4a3ba37452a28bc867e05109866038a1 false +check_ring_signature 716837d0bb037434cdded7f69f592f18354133738c3c85a7a561ef5f7bc897ae 15ebac69c39f46a5ad6e401b304716ae7975b9bb05bbab2353c699e97a67457d 4 958dafa8eca28fc5d18ef1fd0b57913ddde1070254a7827562da305f132289d2 ebb7e7a3282a79060b54725a00573eaf8dea3df731ef6226b81eb1e717e35ede b78fe1a09ba51eecb49b5fa040bae1838a02c4a9c74fe7fd8111601a6d05ed30 9c5984a6358ac5c39b6cfdfc6592d8c36fb9e838d8a17124d929fc884496845a c6669d8c654a554f0982545a1ac73a652980b127faa0faf1980395c045cf2702d7d6a858212e8e794d1c5c6ad4be13aa2b828212165a04e2b802bd2927432a00d35de5b54cea64d2ff41ad07d3f1a568b864a656f42d9ddb97a73bbd4bfa740c30d1571336530dce72472fbe1cc537d1bcc275b4c5487a32581b678fdee6180ef0179ec974d9dbda13da784a925553c04e3021a0e96d11fd218f2b1e7538a309aedf3d1d39ed03cbcea7ee26f6b9b891388fb9ee6cd2fdda084a3f765684ba0cf6d6861d83cea41bbf21305a543af2462ea98655957b9f947452010f96037e07c5b0beb0f59e775c398570d7008e7f5f68f549c10131eb6ab9a31dad25aa4c02 true +check_ring_signature e5fbeda71d541a9ad62ac8897052012f965d19e08a017dc34fd51870e487ebe0 c3024b379a9a45e19ab5f82141562b7bb4f9fdc79faa2165b61bf03ac05f3189 1 b9b56d695cda620cdf0507847aabbc6e7afd4e1364048ba075f872de6ef8abb6 a57ee7452a0ba1d5e2db73805f2510bc9248f0434d2250c04c98ad3e6829d001605fa9c4dd7001bc6732c46e484f63544cbdcfdc4b02a2d4007a800768d1ba02 false +check_ring_signature 88d4589d2433b5d999296c3c54b9a9af21c220f88ac6469f3ccf5dfcde48d2cf 62845eb625bbd2a6a70eeb5219a95682695a8bf4e0c2aa5321a269581356c634 98 0b943ef661d5e1f9d463a0727696362486f4e547014288a4d0ee5e00959777b7 51e0f0d72a98c0813b25bae2883e0e3b02603344167c00245bd3c570ebf062ed b8407e30407a2147edf2a1f986650f0edb8d86073e0eae29723e4052a04333c0 3d7e9e9cb40dc212d8e1ff3be1c5fd85fe0b5d4b17f62b58e85533e3a5051b98 457559c5226125bd522ab6279e27e8ff6400cc0bbfa760cf53c161432296b855 26e1d95fe5b384b107a2845d39794c2e9b0789d11154d3636c913f190138ca15 059cfde5b7a59757caeb1615c7c40bc7785a69169f524f072400ca921864a903 4c33d010d45e19638d652719901c79edca9f182571873c069c55858b9ba11ee3 44d424a07fe9623acc825955d31cc44a4f825c56e5f51aedfb91b461ae9158e5 2071adcec15eac3084a52e599de47e8594d307ada087a479a859369c43119067 e388a883a01843b64ea13238a832288aee6fa1faef492a1aab3c67b84af4e70b c66df69340f882b07c9451a53ae0dfded8e19615d0b6942f2f92b26548573b89 f779c966958f71ebc605e4ba6972ee16aefe44f8abfed7f3d96cc98fec082486 f3b481cf5be898b84c9c41752cddeeac145d208ce40fd00c7fe2836a94fd222a e7b149010c6d438ba7c840b6325650927193ffb6ecfc6622df8dd9447a0437dc 1c8bda15e27b2ebc404758815e5864c586ae080b656d79e053747037a165df89 002a7a372428d24fc642f70c96b65a2e3c55d9f61f6c4024c39cb37313307fe8 79242e699f3bbae71692102d646cd6ab8b2165d4fc80d392dff3e6af4ef6988c 8d5c0a2c68d7209ba9ccbdbb8e3c602acfb8e8be6cb64b9141de5efc925d1a70 cdb5859105ca10c87bef09c134742e969b749b829e99ea12224e5ddeed4ece50 a8e640e6840d2016b3be877ce17286feafaf7b8e04b0333c0d626eff514e36fd 420ad6740c648f66818f4765d6d559dd6b4ed5e79954ae5aa887da6602746674 f9c4cfb8b56de31d9fddbfbe5be9756c25c0eb2638ddd5d1d9ec5f8a76e669c1 0d54a3d08b8b14e663726414db81695573c1868fe342a7c7afea8adfd62c7c6b 5dfa555e5054177d9d200413424fb88fc4c6b7a460533694f8ddf270ea424a34 7ae0ade293f4adb7fea2a42b9e1b913fba0cc49f37d30257bff3eabb9b46c1ce 5d37042ec293032d1ceb895863bef82900d134f1e065bed11591fbd9b184bc9f d001f2aac3a296fcd7f7eeaab97a16bc0e377c6ca0ef0954614b660caf289d06 5bc5d33a1df0ffb3064bb41cda80b2346bb288a1cdbc23a1b2273035d8d32b04 98edabff2ef672e0ea6b644ddd5cded2f11465fef7f5a45facce93d59ae005d3 cc286c8600175403a8768eb1815a47381ac170ba77180102bf172b194887608a 4cce4480899971db90461b6fd58c57791c31ac9e1fd277b5ea0d257730dc7396 ce0b0aba7fb5b56f0f31cf405bf813b2b4203246dce697dbadbd9a85d7706e91 0ed007c203fea5667b48e32ee6275424a152c528d17c1378efd6557310a4f734 d1e29b45f06b1e28f293195b2d61e8a8b510607ebe3338a02dc4bf68110a8018 07395ab1d7f72a67267fe531d5b753931468aa01337968ff18d10a97e80659c5 9ef56ad488addcaaabd0e57d620d74057b377cef70c10f2730249e472e01aad4 c5607d1610eb08d976cb186ce45013e1c4b69b90acd409d9cc6c2f0e474a52dd bb39ba4913eae99921d0e2122d8eaa8a1f5b8d6893b0e41a3c7acf3aa5acf41a b754c1fe739f2479b8c4c8817514176fc16286d9dd69bda38e9e6a3b9a915916 0b53b3c09f7d6db40c11f93071480341cf71959a06bfd88eaa0e9cc0b1055ce3 b4d37c3aa4d74054af46155c1e54d63c2a493ae1fad722fd0e3aea1cacaafa8e 2ebafe6e7b721fc0e4582a57fe55fa11c0da48e37fa163d5ae551a08ae91dce6 c8df20e5fa66ad4977ee222a2fa3c81d2552e4adce4425f2f088daaf4dbdb694 429ebf6ebc94121d06635714934b6ca3c76377d81559ae8fe9b59f56acbad29a df052f009aa703ae24501c625267c6ea034942ff83320d18d852b7ea22de1a5f b8846cb5158d58771070ffee2253e909663fa5aea737d78c7375dc71ead64856 018c170a0c9a65bda66cdb460aaab8aad2ca9e03de086984d66e49509d5acfb8 ff402d16e6fde45e09e3a0334533108207cadff857681abdf9c7dfe19df38cd4 4ec1e8c54002ae31f8710bdcdadf14755939c94983e0755d6975d17d6faedb10 a6d89d27f994b8bd7a5b70873a944eb43c298e01817ec01c7df1ce071200d28a f170be8f7ce1326f3c3d1d59f882a565afc95ab2ae5b1809d5206a3b39892ef5 c28e19029c7a17304d4d43550a8f9f42b230a203b0f6f4deccaa5c6dd8734fbe 3d48c1076a887eef721b4190d0a9a99e4ee849b266983de30e61109386dab334 606bde3a8218496a643481777658e5a9b0bfad23fbb5de9c269fc855f4852075 27eb398450032f710c95c1dd812f9e43aefacb15c669dbc16a8dce4ff372378a 37c6c1d0d6fb723a50977129464e44e9cdbf749cc2dd7496d75bc0eea4b17ee0 496d95307324cbdaa83500f39406d96fe52a7b687df21df996e911bd42b72109 d038150d0848070f7aacf9b414a30ce23d0b64b029880a23093f9c75b3330c50 0a95ee55a0fd6c4905fe59d728c4be7e53b6bd166fab4dd8f46d37714093da80 e0922c598ee00195d882d3bae57d13a2f1af7dbb04ad6759e1fd101ae0efb57f f8b7c11afb514177c29623511e69ff1af55605b29fd80cbe1572f62fbbaf5ea8 a3d1d7b3728fc64647622da3c70c4d491f8f61cd5d090559f1b3ac875665f9d8 bae340999e7c96bd9e90137c877c8b1a6c7c852651d07917589a97416b1139e2 a10eea8144d7d72b19f914e96f1a4b23029dfd26ddcd5ed3d33c3aa72169a108 39cdfa3b8003529f1d13d6665e7dace4658359f8676ab0e65c5f6af5688c4663 ee89154ac270709846028ea278d09ce56b3ad969a751e270afa932af3dda12e1 91e453afba7f24ab95e8bc3e6bd5db0b9d50682a9f3045f67601b4e18e8943e5 9317d738933c7bc97a610aa0874d69851fd79c43ef3ee784b45dfb18fbe0bbfc a5915be05deecd13054933d2e522f28e7dd4384fba892033d3521d5ea49aeaca c1c2a8bd831193c9eb72cc5e9b02c5646a265a0ef468719bab77406c8fd723a9 4fafc5a8137b4e04ad062adc143dd427ae20f9a4f48b862b2cd9158a2da66a8f caaa6b61f9d9c98bfea38a027f2945eec91ef8c7b8ed0eb30f88defb07e57846 a7438d23c7bdb3928ea2f83171745adefaecac061c4946e96c95d95dd0d99007 85a60e0c91ce55c60fa33dd80fbbb768fa9f95678eab89ba99693e7a317fcdb7 fd4cb98e3adfaa9aeacca91ea181b8cf882ce069bd61653454af79b310236d29 ffe6ab9322028f4d824828abf842e4318196fed939e85e7c0bb55d2150c04f1d 59b59a2edcdc473d24ecf95e62f651d60718243ce96e6ef6154597cc827d5d55 5da25db6d9e91839b038996de0c91e9c4bc244b71d5f8bab51abafbcbd82e4c3 d26c1936707df1d9242975eddd36eff55e25a85aa92dc5afae71729404382b05 9361234850d42fcd59c89dfeb81626347fee53400ac05e016668d2405f40b4eb 8b4adfe2db4930fe9aa571ce25b0669aba333d67fc1644100dfa396b0eca6280 11e46142d38c68f5d364d50fd194ba18afbe358b64e22f79bf3e10e9e374ed7f 4bddf4e70072749ffb235582e20eb01d9a27fe84bf5ad7e7d9681d3575ea37d9 647113273bfa565611a16a292ddcdeec8e7e8c1d59d2b0164c9256f7200c168a 1ded557a2c35734bbadeb39e69a9499af02175310592f88e3198973c563a6441 a0c77289b7c0299f88dae46e47bdc4e712f457d9f8e8c3c65d71eb8161135194 9c32a9763920083252118b7ae31d4ab86cbda0fa80a55af902c17ee2be46aac0 bf2b77527f1a91acc2b6be2971421a29420b6cc535cfc1f368716b39e6d42333 d24499077a0d51fd6e6292cd0ef6e5cf3214df40a87fc7d4ff957a58e4fa6dc9 ab8029e439355b928e60807237830baf8a7d4dbb7aa9e7a7c63ea4a9b32ef750 f2931cf255f32b534e18f5ef227fa75b44edb22170de92325baa421529b848f6 1ad8639ccca319ddd9672be80174653e2ea1f6e8283e0a8521cd1aa018543132 cf227d2cc93c949cc3ad4da92adaccc3dc3eb5d4f06df0caa6ec8e1129043c85 c37cbae09423ef1076521dc5d7d72335be27b50e9232d16110a7c369df5716d6 dc81fa3d8d050e37d9636c3ad1333e3074aac3289083a6d86a2805e93376c5e0 8adc63ba9711c62476c6cd5e3a7b7b2607eca38c9136b396d64dec91b35a8704 efddab20b69065415a8fe72de0282124cd2e922938397b351fe912f6dd694663 f9072e582cef8cadb04dcd982935185f5e8c2f0c5347b5f8e9abf1116b04ce0d3ddaea6cc9269b6b45bf99890954650425cb504f7a3228507251004c1eeda904ef439e121f8a6c222ef8f4f2f93da18a141db312ae4458557cbcbe531a125e0a4e69968a56768330afc2dc7fee4ec51c2e3a5feb0971ce684bc51d548b5edb014b6603665cc918ad15faf7541966fdee4f24a6fc9f46e2598cf1e86b656c7d06ae88461ca3863695b73b91289e957ecc8e625600f709cff9042a4e03039a4600171b943df5ed90abf9f14776eba0be24296d8575cbe8e0fee2bf2418104abb087182eda4db3b1869b8481eebd581ce5acc72ca1f0d33d5694698c25074bc7b0b46a68346151d9218e433cb06ee5c9363ff142c84254edf9cdf813b9c165f4e01cdb71b38064c18682414129884a7b6f156a65a75a249d01a9c99dda8b4cf910262644e62ec2e08d33ec48d2304339b2bafcb228f17abbdd335197263a372620c9354538222f3c2a6c54b6eae4259b1dc8cfa077c79a57733c8f8feddd1ef590b26a949043295f9c56cf104ca25067db84ae63d27bbc4c374b4c39d29f4419101e0a2d91088500ae8d3ba08a3b1b50a0b0aa22ea26e05235eba4b6ab3798f560a397d6036fc999738a3d59f57e9c6a45f762f12f48fa89951717f727f15d4f603724096ad39f15eaa4a64e8143202a3b47b79cc146a66b92405e02b6c9c69760089e0d0becd30096745e0fca088cc4889dd9ba4ee8522ed4c8443514a707dd604b811a521c0bc600ca8f356b8e386c539ff53b7173bac3c161f5807d1444a070fcd4979329d2d8b2b8b83a3932d1fa4b3a00f0f854720cef0acb6804725ea6c09ae44dec92e3da74feca656a2d622473040f974bf9cd4e75eb1b882cc0425ee07350e005e0e3bc9e5ac5f8ffb5e6ba44e998a9542837a96bcecda14583c5cca0d1ce76b8869a299a10c968ab11d01fe91bde53cdfb5b68898247111266fceeb01cc83fa3d43118fcbb1a642f5ac1a915080b3bb5a42f3a5a7bd417c5dfe67a90dd99f7cc74cce68d89677c105814fe8f753398b663e92cb7815218a3eacd5030608902a702202e8d321c89af8d0334431d226ef6de2b17085f9b8ee47af4ab8025ee7c5313f9c452c91e79c4c772638f2e7b32d35d40198d482bec0f383a087089dbf3cf4d1ceb55b3e255df9131ec9865fc8667f38123b0a6c36ca513c10600030246fed64e247e334c1a9804584b12b976c7fd54d6922bf1765126f9c6ee000dc6faeb71290162939c992df60b4d84cb66640b2706eed65cd9b57b66b4af2073635e4d906975d4c4eea85b736d2520b8b9012d4bb3bc97c6fa076ee72d8d0029ede1b05ae4416f9c32e2d36b2d4d26f205f96074cc72b3a8a83e0d19f27040fd003f5f071aec2dc5ca2d2f3683e4805d9bd045944ddcb2d8fd724fd34fa3704a2b5774246861b45ce0b1533769ebad2e5bb1781936c358ea260c0589b56ec00896cb4aa2b87624de5af36f6f5279110aeaed62ed00a00662e890b840bdb490ee8623d2865048f6d2f1632c0c42e49d3b6d6ae5215d10da1076a7180452d710b663228f5525e21a1222c3f4f7e97ee3eb220cec9b954b6040f5a8a843ed69b0874e7c20272a615ff7747bfd4a1f4b6df2838e35bf54ab7a25eb5ee6a40322c0041ed9746bec3237efaf4b5d71d753dae3b31f2fc5a7c9e5fe06aac5dda52ae0f6e3ddffa820f8572a957467835bf861013a2c536c7160f3ed332760556585a0d563de306dc9f9dab34c54a232aad4218e176db547ecfe81eea2b45ff7655f00f87bff79afeb0169e0ed44eb14cad34441999b03b67d266b0f97dd852ae4ba50d47abbe2472dd0195403659a8d802da005acb37262e796577a439b707dce3fe0702b46bbacb436dce9f5e49f4a6227ac0d2fa8fe3c1a62067ceaf511ea5c4bd0b647d5ff254b5ef6bd84544057db11afc7eed8fcf1b10f8eed4122a7e5b398f0920b178805868c081696597c127d25b2325e998bfb066bb47cec12aaecd35d56783879b0f854a9d556a5b4638093b2e09c56cf77509428d479af32f46bbc249002498032bb7910e4fa8bd90666a7b8b26a910eb237747f569158474bea2b74e092d969d489d6e4f6898df07d9e2cca4871423c200fa2f9ad0ce1b1ebec04cda0fa839ec095f28b1bfcb7433a4127bba65ad85a82ce1529f02fd418233f6838e075f22de1d8ed73a685d747fd06e1bcf9ba95961449d52ab6becf5e70f3b8dd704117b2d4a5e1d21ee0effbcee42eac7b022cb80afaa3c90245e4cd11a2f23460dd3ce38d96da3d0bc53873830f6fd3a0e5acba6846448a4986a1a63f5ba59aa0e2feea622744781a0f076e16188e1323407d0b3594d754affe7753f8a50cd520860898170ccc051aa817392d7c27fc6535f05cec6d52dbc0f932e8653266aed0cd020b9f70fe4ab374bd7cace9d53555857b4721a23268e049c32a193891c890d21ed8ad0e2137c00a8d2eed9919bae71284aa3e0b0e0e9a51337ef3da93a1a0ffaf0862abb3315dcf515e593998772f02544de4575cfe72cea55e890bb6c1608251f6ec953a2db9a06928922a967f7dbd064acf3390c0b1568fb158690dc7b07d89104fac86cb92fce1530ff99309a565fe0d8d22e615ce28f60aa8d36dd580336005ac402a72b50a26e3aeafddf2be488ed38a1f450654e6924bc2791c6840f772c3a3f539ae6ada7a1969240a7f6f9e2f10379b1b30f98f7a9528023697b04be6904d837acca57ec12ad5b83030baba065378f6dccc1adc4cbfedea5f67f0085b9e8ba56f88e4be49656a6d38b3520b073b9e01ad29d255bdbce538f3ef50a5fef262af9ad5df904458b1bca75a5fd4f8c705b6f7a582402bda45e9c851501d7c1a80416551cbc911f937fe03fededd5fdda1637ce2af02c31b004712b0f0ef86c81714f4d4084c2eee8e4d685a7d248c71260f3296326d6f61147a4f1f20529fefba1c344f33ed8b4691e3ffc6149321fe7180000fe2a64128dc030129604333a5e44487e1baef6639400989405a4f96f5743375c2c1bcceae604d256e305ffcb7e443e95a21e73c65e91fff9e2c1ce27f329a0940543238b8f4ee037c00e770b5009cebf9dd9e9675727a114e89e47533d6d995976057810bc64aacf8e013bb9cf35400b492221f947cf0380c784a4cc6a18e17c4d5847379c3a7bffee03efa29943c00c616291a3aad4c20aff2ce2df0794b103a5fd09baadfac9b9e30e16574589e826197a0694375ca41902f21ef319c8ff52d5c4810e6ef0a9508e08d34fd4c5ed9615481b8aac890a981ddbe1c1e30eaba614d23cefce4677995509ad94fdee6e7c7e76f2e754d6799a559e98e19db18e6b99357f742d0ab9d1280a8d612c482a397ef761ff3ff2d4adf0ae5c739b7c4d332b27b8aa6585d908190c748c71b10b80f86938101d2adb5b23b3f8fe9a00139cc2f1f687e45ed947380d7067ac0a77a278258744042e6d4a2427f9af688a07b3bfab1e6b3e19bb89f00bbffc117b65250f44ae2edc14b6ab37feeb17c1202a6b4b4d9e94cda51a949300a10c3102c0fddd91bccfa2a3ead6c0fb2463d199f7e2e087794ae86248b57909b889c4abb98c9ea8ba0ea4a5867df395de3e8a9b1dc604846fac5c9b23267907b9abb4b3b83632e17cbdb235431118a84e499e5aee03d7af5e68dba46b889d0a11fc648e7221f4c809cd06568c8de1b8fbae8a05962e9d09c0913b7461b91a0494b7771a9a959737b6753f0ce048d752a30c73a1f46bc75c2856b4574265fc0d7f9004be54715e4abfb7d2184691ef40f73f06a140a2f23479f9d1c8d642c503b6b37b5c84984dc44a8192fbf424bdb51870206ddde0f4a311eadd0163e1720dcb5978a98900ab67537d033975066f49ad88d7f2dc09df91704d01c188da110d7e63d07dac9b6c4faefdab1a5c8883497036cc20163c518d03860eee41a41c070df1d5e0b3b6b140968767334c30fb6d2673c3e000e5dd1f0d23ae1d0bce600af6dd13b27a5a19ce36e2e18d2060f6fe64b8006fa764bff3742b86c70b96c60fce0c67ed34be1860f4451cd1e3ffbcae89cbd643f4efa06a57739b8452b37a0dca9555dc596562faac7d4a4869293386c9b234790079dac695f9a5e8e53f310308701e095beb9aff3f7e0ac7b710815548acfffff3689dedb13938083ffa930e0ae16d2abea11777999549b3a29edd6bd43c1a43250b7782917d1bff38b7cd0ad3da7e54ebaa19082e9252dc2a025624dc3beb40343f28f1859b668aec56a804cf4bdfdd29f7f08b1efc9b89ae27dcd0ac4b631b1cc658553fcf810cae834c000b57ff3c2c0763d84cb599d8f074a8b9e23b77f03f10c386a6673c13a2ca4b08140d9f84f713635119f3f8ec8584190b4f438063e413656786f781968f9d9a08a31da7f68030064f9d874aa755c8fca87874eb5962d1c056ed460f0f97caa1076481a909d377be282ff98b02be3d3a830b892fa017fa469fdb55db4b3922ab006608ac6077e9166531064c2948bcddcfa5c7c146b6120c35e7cee66d67cc6f0dcacced1c9825689e1ae744d3cdedcec81e8c1905a3f21ee870eaf7b3ee12cb04e65e3939e21e7ad69e65f8b84a506046d7c024ac33758a775fc0ccbad25eca04624ac64ea3f53dfdf48fe3e357bfdecdc6069d1a98b9b333a482c8d235a9400916b8b5d22b638fcc289d640b2ba32b4ee729ebeefe145eb8d5289a5255aabb0d70d96557107e519e877a9e9a0e37059904c10685689b080f0dece72efef8e202fffbd8986a9dbf579a0c574d0a3478f10c77ab6a38a4ee274d44777c4f56390f0462eea07c8c1eae175281af9f5645df956d1180842e8874d7240f897bbac70cc230723d79ec35bea185025de577668d0799d5abc6679abf89bdb99e48fa85005fd95da7604918b6608bc921d46300f621bf8eeb4722755db0eba2ac9a3cbf0aaa160b7019cf779cc4185db2754b7f3d5e1583d0782a2294f8403a97c6727f0dfaf55c050e40262bf6b6f8a1de797c6067edb354c58c8e091184428486befa003d9c8d722b3112b3e9c748f78ab1b0653bbbc58644a4f6c5f1e3010e31919800525c6df4a096cc70329c8ee12667b060c449b82395fa94281e0302a4c86d410e9b63384cd31f3c79493c7d36b7424cb0a658531901b8265145ce589f6cf065090ab11267c724c43015950173565db49bc3f3a8800b8f98dc9e08d2448d9a9b07d21fd2b3249b5fa190c2d100705716d045534af808321d1a1f032f4495a250032ee04215da926a543f4c2792b6819eebb91398d79d44d73ace8f48be8d06d70cc7e714cee86b3f7095e0648f56581c4b433e7f9e3dfe1d259a8992479dc39409034ac3af9f64054d612776d67dd4f154f54b99d51fca76c26787337d7e68490d52c899c6bbd83b921747737895806ef2f74f87af101ccfc077a6ba35425d410a70fdda4ddfad14d90f14368bbbf8b8b5f3bffae0300bdbd5fcb7c0d7ba91fe06c9f41e82f69cfea68b0449c414c436ff16a4b19468a70ad5256a1977d49d7a02c8b51eb0104f9ac67ba63434cacc6a42c51d80f7acacb14abff02a0d1995400a5d5ae8fc21523f51814e5658c64f40790d5feb2a323eb8e8b452a6f9a138e90811d90e8d163345d2cc660f837ef6f5964c92d0019f494f2ad477a59219da9005f2320df72b7bd9a66a2ab2d16eb41793d364a82c86f0428070cccf6c6858520a520d132ea6bdbb01fb63a49a3f9fc49f730a421fbca8482189c8d98961116e03acd25ce6228cd681e572cf4b32f5c424cd83db6154fba789afbc001276a734098face32853df24ac4e3dfb2068fac3850d6b7f4daefb4768049c55eae232a40599ce1f478b89fd3ec1a41ec5cb11f291ac29773fff31fbabac55285fd757cd02f6a24c332b2464715dfc7b45d428a70e458218ef3f9e4ca290d65270eda69904c3ce55045f5869d48ae3f704ab78a00a5a3312affc1ad233d82e1fbe7c13dc05508c918e77ca0560c62508fdb01265d1bbfb5874d8771e67cfa7b2d20a99e7053a11b3ff0127ee5878223d945216f0166a7758d7cb3173411306deea9c1e750c18dc9d43762ae95ae0cc678e31f67e953f9c2bd0d5eb6fae6661a9ff7ccb97047be7d104057d3d28e1b28d17cef4c97cbcbe7fdc155f00a1ac52867ed2623008c2bb705322745bc40170375ac6a8a204789272e06d5fe1c3a566e3912ed7b103a401553e3a05f5fa5961b144aa1693a389213644e9a9a5df8f5e64a5b03d59081f402ea6ccdbe22d0dd87f30e2ce3f19c189fcb903fc10e1ca55a273e3b05b0b50467bb65ae4a2ae5b5085e8b713d15363aa66da763a4f7c23896a8aa48c110f6d774f83723a62b8fcf8dc5ab8c5047ec2855b6aacab816678911729c3c85f07b1ea719e0fff3a7db7a28c785def0d9d1067197d01644c5738c9fe3a16fe450f854bdd530bd14f3c126ffa5aa591442e746bc48dc45976fb227842d899643e0a888bf5342d021b893d6d063faae231bf286b6fa0678abc57f2e216721fae5504e6b737bc804c8e0838b58ebcebfa044936feb2e14e238f77298861a91d00f20115eadf4614f0ac194706bbe39657ecd9ff1367c5f36a9ea08021aee4220d6302a7623aa94efc9fd488e398ca1994d1359892700eb19d872c2e878440b78a8105293c7396a113888318231e730c96dd78facaaa0e724eb80344aadd755eaad1073466ce906e6eff54e35c17e4cc9da6f2aa7f9f2d7a873c6cf9260d62b648300b49cf9b0a0c6c8751c0e0c943475ce990f4f9fc403fe7cbda5794b0026f57380dec4787d123d0fb7b672c0f30a7b2f3ad596716d1f894cc726662cbe98233ba02338872ae6694b7f7c04a1f852cc37276cdc15deb53823230521b1e9fe17a400b4f955bd45cb04d3f5c14bd0335844ea408a41d17436cbb8280836d717566230d25cd63df4421d42675f37d2a826373a82d19cabd1e3dd23524d6059260640c032f794ccb327e7edca684392c6fe726dc4ba85c474e38872c98f956723e56410faf69a111857b69913090b4c081a50e1dfee3a6e4ceadfccaf0b2a3aee5de45046f37828adfb489b5353dd514e3a858a0e0803c5afccca64c121217ed4d00c80a2a0449ceaa8b83f37193ab4cdd788cf6690721e216df721b19ed377204d38b0d870d4b2ef28ec921f2043bf802ee9a2c7216b86a2553de88efd6d625df7b3e03e3433b50cd0bfb075ffc46bbc1d673f106b8693d5c7322e8e1fbb61f682e6b02adeed1f4d9d6e0a830096ee5450d180d8d2314de31a3e3b09045b210a683e6023b2edcb2eaf5c9430a700f42246fb0d5b6539b92c1000c05e94ae60b138db00d664f7d59dd289eefb8257203ee479082cdf48f1863eb941750407017b287ff043d2ed2d8bb1bc40a50d83ba517f1235fa32e2af0a06e9f3e09c24687037a4125715d5463654360cef787e4756b729c2b52d2e29ceaf261a14420f07568d3f20098768bb752c94db7d4ee64b93cc8ee6efab75e9cf251843c1db139edcb9b400573ef236ab631c58221013eee5ff6b5d9c3cd8e117e2d1f79b35c2b98e071030236128191e8fc1973676513d521d7b414709d04b3dc0c5b98b46a5fd4a27d0c0fc955c1592fdb6622fc35fca43aad532278dec0aad5aeedbf4066c7eeecbba70b2fdcbe2f7c4fab0cc7b0b3111e1c09d6cb38bef4e1dab665310007e5379f180559c3ca2799d2a14f9a63af3a3407079d90a6eaee19fead2b094359efae243e0231a8da9724bee85cbefe3942463f9700e780d58ae906a5bda3dc4fdf312f04040792d78794c6b4560518a43f633084b0dcafa017446c6537be06aa17ef711504adf3066cdfd32e60f89a4216cd2f8ef2d006e9e69d4b0fc3423b4b1f1770bf04ca3f6a7e7baefd0e0c224951d2c3a9407275acb13708a98a1e75a05f49828401c9dd65951cb9be28a728f050ad8c9528199eabd948ff8b50b6137f9df753d00852b73467c6f3a069e13cc388412ae3d6ce5d19dae286eab19962fec46187f00b820b77e7093033ccac20c20bf4c255a8cc5d97667f7564fda515611a0d0d3c0b080ce49a38c512916432ef8a81e38b1232665ec7636059bd2454fee7873b4d0e09de7cbc790a417725667df6701f33f3f497bb40d4497b3813e92b1b5ee2f80866f245e9dff332cb7cacd7b24e88336bf2da273de70352d80d17735442b33307461f44b1f9552f1e163704254f537043e92bf040d85794e820bd065381b9c6067ac8ddd1c70157c6c0c8d61cee21341adc389be100b19fb79929060cda9213041118da2f33e6a512a7a6e32a1efd86c757c7815583abd0c91100afe167bf600154d94ed85c2fa2be3a9f93dae670c9140798468837c3e578449fe310b32ae808a3626a79ef04d1a5e4e4ea172010ae1b860d91f15e5da9beb5889cf8c1a1fa027da4f04db19b73ba8de0d1909053c73409653d5eaaef29ea5d1f5a7ca047b6008c22992c3e86f46978f1e0d35565d23199f4b9e9f7fb17250ad1766fcb3bcf09ece5723288dc47189add5eaa547921c85ed9e46445dee29992254096ac08490a7e6d078869ae90b5c962cc9d3383d539de292dbd793f41289ed353fa32346104020ade4c6bf89556f9f7fdf2cacea81c5db1df6f9a57649f5041bdc0d14c340aceae72d4ae7c5f7a973e2fbe9f59114804287211bb5ee09a42de0d24db443f40bebcf3bfc16d5ab6d127e5328b5f76d4a5abeac42bc72781dac3404a2b20d0062fb22f90995defb9c22dd0c6e0d3b4d13e2c02d9e67fc94f0d994359e289de0aeae498d8e36034efcbd7d34191ce95c4a34b5323025e3fa7e4929a565bcffd00 false +check_ring_signature 91fa47a2228eeffaec8f680e0995dc73ae6f41c0ed868923ad2a6ad592fd1fdc 82804136ec3afd83f7ed59afefe8400a6096c698dd6079f1eaff6fe8c6975752 4 9b500e5ccead96d35012b71676a3ef478df507fc07cb10eac40b036708542660 17e3124af2cdab508798e41514844afd0ac2fafbd5ad962828bbd3dc394e4d56 15b27d48f181c8c15e0726fceb6f008446bf868701a64f0371c2b42238000ea7 e8ea954117053a9a9ce52393d9602b71903783900b969dbba3f8d7d2416bddb8 655705f4ae9b889f781ba7ae9674d37b24de846ac60f54ec8a81a7424f948e0c2f16b8ac674d0a2950fbb0278530ae97487a34ac151d9364118d65e2e321a20a1f5495aafc871b0037bc4f4ad060fbe48adf942cfcd14d71033d3e82e06b070c335973318e0b71d9324329e5975812ffdb413e01f29957126dea2dff17658005e5fa50c35021e89f042df8dd794a16f9240bd8775a94c372037618b18f32b901c4f9efe77e767d7f21fe0c57ddaa39dc4c371cb6f6a72d1536a7bacadc0ce500dcd4b5adb595b3cffbe28c150b8ca15ff32c15de63158e66588c7e0cb547fd0a5e7a06af9ab307d6a3fb44907af05fabb3e15843c72948a2fe1547c787ba040a true +check_ring_signature c24b42a2162da5089baaa1916691a2157538fa1cb2cb78f6b92f5bfb1d4f50b7 dec9470e69b040fb924b1cf38e80f3a10b15e9398f1af5c8d40f37fc6ceea4b5 1 fc3516674be7f259f8d4caae3c62be74543b9bb380029c1d8ad0f678673176f8 b01e473b9299d634189dc00e162ae49906dca8918945038cbbc60bfb5e71c805f5c742b0706671902d8d550f497f9eec49f5ea4badcd75106ef3867ba1009f0a true +check_ring_signature 034691e3cf561b2f5ab99646d63eabb53880361fc4b6763afdff5c6660db8e9a b4897d1c2bf278b7ffaaa6c620cff076fc78fa0a6d67256c34eacc447832ca6e 1 88c7f9cf5b01c1f3ec452dfd58d3d905ed87dd918e1aed86a80f1fe79c85c262 4902bd58223d406dc2764b61a4a67799589e09f0cf101049502cd7ccb3480e0e42a84b05649f6a79ee1be35d985839040f75de349a055142cd9da812fb8792a5 false +check_ring_signature dcc2e4af37341e9f3af6f458b578bbeb5df7e243d3481a90922867dc54dfe43c 1717c39abe80ee62d81efb793ef3245143dcdef199947438e901620c75203263 6 5cb94bcd558e3f3effbba4a59b90d1b53d5cf1dfc1272263f09c672b39acd435 9c48899d81402c57d8063a011733836f2180169b3dcaceb8456eaecb1d8576ae 8eed62cff0f6190d7baf06b31ce010b066ff087d72396ffb286e3897db8a5f86 68941f71eeff5624f68f339ad965df524210eecf1840cf5105adab6fc9c08d0e 946a777f37c709dd0ea053d64229a7f23cfad73b4d1f843c62b2882de1aa7edc 84ce44d335c20bb12286036b3054e50e50eea51513ab077f2ab090b17d2c5a39 2c1e6a0e53940019b4364253e79d254277a48fb509981fbd0d149bc9a14ef50889d4c1537264e48c6ebec33c958573bba0470431cc33aa797a013d234c7ccf0957be6eb3a0b067ea6d2799f9600d86fe10f4d120cb806ba23c67c993419abd01aa60da62ba620becc1d97af08a32f61b66dab1cbac69ce1a6401806843ce770c8d0c4f1828bd51ce131ef111c1631217fd107fdfef66fb1a35dd5ca6f331df0ad27c6faa7e97c531adcbcb75fd652700ed92d99edcec7da2cf348987f048240751fa5ac7730a7f49e0599cd079bcef941c6a129c0ad75ad8cbbebc2f1e62d400e03776de41ee5814a4c4d1f68411f42f30573eb303c0c26bb1944568482ccf00c57ed7040f2d546bff0d4ed3fbd1599e6cd35af060868792858920d3e1232d08b3de5d57019c5264c35ac0e97603daf4b3a1e10c3acd245ff61b78e393c9a30da49712467f49a06928c0f11c84914c9bddf394eda483ed9133f4b0ea0c07cf0179529e156bb7244a242318c55383cd628072693028a082be9b849596c2aa980f true +check_ring_signature d5da9175d6b511e462d3537c644c70af95b6df8f7621ae53f7b61d6cb07a25fd 0d31463752b6f2c9d1c66ab0449de850270584c857c4a206996ffa786f43df7e 15 774f60bbc495bf729e1e7124a264c2e2e6a633aa5057786f091a6829e5f8b8e3 3068ea50d115a39688492050d5a5e4a49e9733a2973b150526def9948f441868 b30bca0bc60cebc446953e30495482fe13115038ec720762453f88bb4099ce2c 24bb7ed124022612493c8db7bc0412a5fd9a1480273df6885b5c5ff988631861 d144ca4869d173f8ea4a5efe1c04163fdc2d48cea8074107c7fa7634a27b9a11 74ab769db370cb7ddb6be44e6d91d8b35502728118e31cb33c099a5602f8ba11 6a2c616d9cc5b181bcd37fa4f59e88bfc6b823e2c6914eb6b7af6ee5e7f308db fedb72387451fdc3067b91d180ec6eaa754312143bcc39cbe62ebc0009536e69 c582d5b2c9491a1ec71e7bb03f5afbc841ba7b1167c5641a790f15a7866e42ec 1f8f368cd3951e23f1e1d3c8bb3de7788c36fbbed2d4b6b512a37d5d2aa3cf79 ad95ceef7464dabdd05e32cf1b567ab966ab094085bef6aef3267677b70dd943 9bcea2859c319ee0226bd9e645419b3b73dfb95eb01ce994a66137b1369ca380 0bcb9dd30f80bef83f55ac3833d34ac17797e8a42485044045602f673dedf8bf e1d05fc13274907494d0fd5308ceb79e46184c21f57cfcd4bfe5f36c9bc9d3aa 54c0aafa2ec4492befbf33f8680d41fa268c0e1d1f634a797e477a8148169fe1 4cb6183ac79952164650e49f3fdb2b2b48261967069e6d36035c1f9aad994c074f2abb56e1ef8069bd58d5ca541ae9da4fb88ddd988889fd1d6194aa5b185b0f57767289ecb354ecb41afd2969f0fbace81005c21bb2ce293cb5a4acd6dfa6064d4a2e193a2a71115544a0c28fbac64d423c59a7d34daaf554284a0cdbeb470bdc4593b18865e70d43b11f07c13de5a5e73834fd64afdb032b8d9cd528076d0c8c9d1aedfe23bc15ddcba92e740bd8c6adb4967ff335697b6ea54a4adfba10072baf827716fee1c185ab6e67301b473c4c650255843205c62febcab71b2dab083d6c5d1c861d93af6959163b4cb8e25a645e97da23628bc48e432aa847845802697207194003fc61a93390f88065273145c3c92b51de7cd4ae3ff8e02fabd106e8dfe2e4ee330f0df23b3abe0fc9ff45844896dd55738227190d2b920f3fad0c01687c73dcda14558380c91b0396b4e140b9a23d96598180b3fda549c348cb01e6cea45326df985a62278806305b0413a566d7918b84721ad82f558494ebbf06d170b84743cd3d1a0f897a283c2f1d4c09f52d527c99f40ae1390e7320670001ed7af2fd05d830de5d4aac5d8a34889f8a7350e621ffd7114fb38c5ff9a13808d91280ac76fc85cb4d2b600ff9fd3ef24f6d7e69f6fe613281cc0aa387e54c85efdce3d6384e47209e575bd16d4594e2babde85ef3979dc5cd40f1a091f32b04ef565725c17fc0bf3a78ad53357f118d998a18b9b64fe476b999e8c2b5e8cd0230f220194a5ffbf5138415a5f1506864fdf74a3235d2341062545cf3b90a4a043b07c02f0b0c237563fa8751de3596324c89280389b5d6d2c053edb7b5255a0c2124827668c5ea666c830b302017c09213d55f7280e501eb9a505943f0fe4e0bdce7fdad6cca76b153dc2f00d1f0e9a9df8873cd35fb0189149b93aab25f61058dd3c0eea6be4725351245b7cf4b502ff3c5880081ba4f98f803740797899d08db70c1acdf9146d326acf40f24985d4d474e6342c771fdbddb784ec094f89c0bec9cb6cafa1cf7bc2ca7a5580a8c7f117f76190af6f4a8685a6ebdccd462500434d549063cb8d0b6758b61c2813ce70c925470a88cb21b7d7ccd6f3843096f09ba695d3edcbeaa4a04a25b663accc8a53d10122aa61dab7a531492f1a695f9083d6dab47edaab8544697f3e85835f153d33586187fad54220be8fb326e5ea705058bf9643fe6465ac5beb26edf205f79f2620afb348a685be166e0b81f512005d8665b6629adda74ebd5d9b58465073e420b1a15458c06bdfa180faa6e148e0714b61273cf91c48188e4e34de170ead97cf30a425d0e46f14226c49244a62309 false +check_ring_signature 0c8fa9eb282a62872244ee1f198f05c634788c21a61bee97863618323084f028 e79f55a1d3740d12b4d4e45bcab4a6b1563259f2673c7cc7e41007473a9061a6 2 85b4de6cb840bf4823c96b58365ca14eed547854b8374d8495d3cf66b6e21dce 180f9d16256f4707a070f65c87c12d932a214e317bfaf9397aca3abac66decdc 806d85e6701fb6ed3f7cbdfc08eb932db6a03e3985379bb2b049e5a39ec1dd0c525587780820aee82a60417b7f8810474cb23445c3a42dbdd1d28ead39d09d0472dab18f91d9a9af3e7430e7c7152930dac0eb554353bd24e692ab68bd7c4908ec5185dcd6e6a80c1d64a6e99fc88e7c18ec2b488384975faaf291c597d03c0c false +check_ring_signature 9a53bb2eda3104d047dc5fc84f160e4f4bd52a56f44cca10c8c8ed2fc018e047 853185dbf71b818f32f58f0903af2ac30860785636d8e00262193572613ca5a2 2 154fffb29a59d660175eda80ff6d6db3520cb74dc787d2ad888fe380cc9feb19 7204f033efaf9fdae633b53a43b83c0cd570276814d730dde12dd9a592353fd9 f4600e008a03a5e6fb43778912391acdb4f78beedf88d0d2b933cc402bfc3e04868eaf350ec3982a7d6873b6bbeeee37244380df7fdb8c204e4de4c53066d464ac756e6e935d107bbd9f395e59a678a9002cf73c525ab89c95dbad69eae84105309d5a303a726077b186828dca1a9daf49b1c562e8d43267d8323a737b35d80c false +check_ring_signature d7a186c44ea4c59a158b20c2ec122b2f4417285f9fefa7074d8d6260eec58a5e 5660ad4de034a7007d83914834a2e1a68e03e71bd5f1cf2f6f772f69d8f59836 1 3b3fad21900bf54d7af817120208d01d1bf6833ee79507de7288f66a1e7bb58e ee5548c1daefe74cfe9fd82a77f0577ad4bf4cd05a571bf0aad63af5479de2e80bf37cd8afde130aef5b8820eae38b010d56f5a87f697aff4d3d32fc72174cf8 false +check_ring_signature 0e2f15d8801d1a783f6d3fc744de4fba76adfc523b100cdad344a7edbb68c24c 746f84b6e0280c055f706f02abf3c21a6f7dc91b5cede30297304bc5f742a1c0 2 3d297d16294a255e1b07c84f92a417831e4c16039589d0ec77577c4c2a7f1019 c18da0dbdc473cf252a5f468cc201f5bfb8e889dc643c6f91b11c9aa31eb89ab 5f257c0c397e98535d50b2c16cbaba967c9431bc3e08634da07aa41a099cde05013c918245f8447dcf0e793837993900be0c12b652ab47bb927c07b179955f0439de483abbbf69c812f8e4ede816e907ae05bb23a94901fe346cedad3d92fb0da84a465dc639449adffeb7904d11f99729bec30bc0ae10e269e8e4884b191601 true +check_ring_signature b37ef72524e0ec60f9a316394cd13be8218ef8d63bbc631ec6af304d8c6f668d f999844860ef6ab71c7adf56c90818450adb1915bcb76f18297af543cc960ace 2 3ede28261f5a2b74c299a9b23bb1bbcec13c6786bcc3f641da56cdab40bfdf1a b48b7701378bebd4ac98173330c6e4279385787b6132f2e36390023e105bc3e3 721672a25e577ed26717973c319fe02c68e483f0febf72f08a3e412264b8000ecd4a1ddc860bd87031cba24ba787573fe3dcbbf936abeb2f04fef674238ca20622ad64135f024375ec8c615ec94a2db4cc75aeab0a0dc8039159e3e516547f0339c3d3b650f3df57c82d50843e979ecea1777ba27e060e36b78fd4e0c4b70b04 true +check_ring_signature af3860abe9f56588810e80f86b17d34afe6866b25932a2c1c5a9c26665197114 150bc1279deee6ad269dfb4d8915b6eb112059221754cd53e1170d4e464de3c8 4 a010fff7986d9dfe28e8a572c509a5792ff4883b0a85d1ed45f988b536ad229a 9fb12d00a8ac146dd8e5127300a0749f8a12c300c960f0bf251e7b6099d2be9b 0fc8e8496642341672004ecd0fc41bc0d03eb99e888e0a44191438d4c10fb2fb c5c7a22cc900915a525f8c14306dabc680b77707ae3ea4ad7b6ed7ad6f8bae3f 932593fb062abb969e8948e2dc083397166f16801f1c88b9142f87e1ff5b7f0f491bd6c39fc1198192fb4112f50748a07adaab9e0142a9b63eac45b6f80a140dec55a16c0b68061b50ac9150eac97f326116a83170c0e121de7e5e7e385a820e320841a79135afbb99f22a0ed16af0437d14a7d424684b34c89bf3ac2a31520991c2a057f85f2ef4d4fb6dae8f13be728ed94c240a3bd7b59f02f425bd3234013133b7339dffc3f19e1fedc0527e1d182c51134a1fe53351eb2f75f4bad0920184e22094a425c0dee2f113dc3d60e2533a9d45adfd6914cb827d0b8eaaf39908a553b63eef5514ae615e0c036f5b8aba10e94805c81d5790bf0ef669a3eae501 true +check_ring_signature c65639ffb19bef6642f4ef9d8b27b5e3fdca36c541167845b1c4536b25622237 56dd49bd146dc99dadb61cd75385f10ffc8171fd7dccb20aa20345177eabcc83 92 f279030629a70d07acfd06f41454013e85466e006417bc41dd6cc2b7da328b7a 1d7e2c585a8ba4040cd4ae0e60044dae12d3640cbf645aa1b9ce722969e37882 a43a95d3de5fb59f637eb23941612c9ca34b7d3e3ff2ecff37d816c78a8ca33a 44baa91160dee3f452a865d5c95bf00af540d2803a4c0fb0539d91e5e86bc099 3d10e8048f446a6004fae50746a22fd9997a8f8a69d1bd194cce07a1dc122e28 e8f4573dde2f92cee266b2d40da2038414b6cfe65f550ac06322c82968e293dc 631644513e3a0d4ce9327aa961307c8b5cb370b90f10e8c930f24d7bcff22858 3c3c70a259da5d5ea06100af0bdaf6f6e4cdbbb03867bb9c816c77b3564db9f9 981402af25d0acf549b3d4c3debae6352ae0ca2e9715d687fefda5ca1fa9075a c1007b0cb68c31b8d91da9636b1f9762e6669df0e2b0aed71a46a130c8048f92 3c46fd4b0ca33abfd1fa149ab1b299f6738e26c3fe6d6e0307190f0d82fd174c be5fda8e5bdf9960c6d41519891017ea4e74d3e4a4a38fdb7606fa13519e3c2b bc89789314c53e74bc719f1a805e2b732c5553ec046466bd9a40c0dbce40ea0d 85e4a2f840de86f8ae16b9582d8a48a5321ec12d4622abadf0169eac02b23e74 be9a70c0bd9d69baa3a62f57fcc146c961fd0bb719e35893df13e43557f8bb6a e9b7e754ed6105cd4a910703bf79839422fdc2bf445ff392235f4ff71fcd059b ca8466be646cf00c18b8707b83b832f323ee0a938e298a4700d25de8b3d69afa 75fb6714b59f2f67bcbc4555b420ab9475a0958dc3ff18dba1e0f3a597e20320 cd317954fc1dd93634d11744bb615f3e11935c21295f552ac6cb7f8903963889 4606055590850b64f720c61e34fb597961e79cc07b7f50dbfabd1dfee43583c6 af65009618fb192fed03ff823d422653619a995553d67b8d1981b8bb026f17f2 40066b3450b0bdf9c0b02840b1538f94c64bd47375b1ffd8d0fab81076fb3c90 d1981b8cf4c80d6d32b9009324c7768c17a40927853b9073f081296b1b4d5915 17b3e133a4567c26f727ba0d438a2e5d81f9544b519cfd4e33049f8353442ee8 98129ad0216469735e72af56a98bbd24bc0577ccf55c2b5c89d8d815e192e50d e5a7624de1f980e20c546d5e5a5be75ad30502f7b06b160bb8dfb77f680cb166 ab4c1379470db2deb9f9595f3eb9a83309466f7f0401d8d5a40078ddcb8d1215 961c18e2f0a6ab59af48873201810ba4edce4b225c6a4e3e9ade1068b74713a7 3423d6138582984e335b6bf50b0d2f70f16658218bc62faacc48fe0a477a5e85 ee434778073d44379c026624fcc5a772dbb04422d8c2e419807d9c6cdb977199 409d2e9fd54bc4dd740abe10a15b9ee2a7679cddbdd856d7fdfc6b354b5edef5 b1daecd52a4fef131cc11682c62f00ec8870f0c58ccc746bad1531f711b6a03a 8ccad7270da18805a23b60ef1dd5018152802ef03cd98713594e390925cf15ef c6eed4b8966b1f5091ab593d5f1fdee0ee567a768173a4896ead4b9b676e97b1 86af04f8b58b84f4cf0fc666d30a7e4e0b265ed82c1e3790aa8b17caccf01098 8c1502d1ebc70c9de00f1dd761874c7df0ea33e857dae55f2b1f660a69cbf172 81698686c8fd08552e1465ea1a4fb9380e8e4b746d20bbc2d2506033e79401f6 69b64561951a3ffbded9ff32194493daa6d3576d1119d03af3061d7f18ed4d9c a9d392d3d4efc28c56e819add980bf59683ab66ac02e68c4dfcfd15947ee62e5 1f517302bf2ab2dbdee91a01246af397225dd247d021d9f30d9552b08da1fbb6 4049e1f72f792d7538c7d52e5998e62e55543e3a18e418455d608b036f6898ca ea8e5f9654c67ce4df0b96a4b9ac380e1b77c5ee86253f54a4dcd0a1f1dd9587 829197d69551a9e27c69cc11f928ee2902199424422c320f8c145e47aa188198 57bff61a18a70abda3cd71626d22d9f155c8f416484af34049c2c4c3c9b42611 4f523d0b6799e3cb934295c84124b2a2cc37145046fce62ac754b562ab836947 a5f37d7638ca8c638674d4456adf3ce5647e636791c883762d9c626f8077cf43 7277ce64a75777a22af186474bf01bff2f7e1e07fe38e4e7b3fc54ac0a3ed913 e999e7f0bf36dc7ce87b51ef29676b7de6f94041e678455aeb597331a30896ca 7c41ef5147128b9d68092b95a0e418624e568519e9cf51d63e0786591c8ad1ff 051b5dfa6a4a73de369222e56e36a1c07682bd04d77aa47c8962a4835a6e06dc 6a59341276637bcc859eeb3d3a3771b3cc5e61c016dd6c27ff776929f7e57e29 4121e979a7047650d451e9b7bea980cd9b68145cd9921c5fc48775624c7f1852 48d235de3af25eb996d34c9109362221b5c28ab672695be20cecc4d7d20bf44d 02a7aa03dada0fd14678aa02a57cb2f2782ec82d0778ded2b8516f30b298bbca 9d2ea887aa5641e4404b68ceb18943e93c61cda7b3643f185fa7e9aa52a5f893 6073f27085ee1d3f3f64ba1ef4c86a048a026aecf7d8ddd8a44bb79006602bf7 b52da27e6b290d70db86438235bd032c1daa333e2b444a61744b32b4cca32bca 66ce5b117f59578c0c4f08bec2356634013f87bef7deb3ceddd857ca381c186e 7b7f71f5175c3fed26c37d71723b39f8ff2837fdad2afcf9a611d30fd56f1b62 39600ae7b43b1da28aa8906dfd3aef4cba914d401d11351ae806d23860742043 88b035b2ec4348e91a868df5b68538ed705f580a10ac89414ffd4ab521874a40 84ee08e69cc84ffef4aedc0e0304f408f1a4b41f9ad355358eed4693a25fdb4a cd6f78affe8d7fd426ab8328effcbd023248ce64c338a2502cdcb517459af0d3 6e562f38a88749b8ddd1ceeb2de34d79dc5bb48d2cae7b334ed446568508e1f5 df37f4b468a61ecbb7876d0179a4597ef2a4dd2befe91b3fb79401c4b07beaa0 7543ef69abf2e29c608094b9e2bd3a257903d5f37dd38fff30c6cb74ae497410 1eb049a24dd08eb42d2fbdee27047d2d4ebf2a3e7311c9ac8ab343cf28181560 87c2e32380ef9ad63dac12f656577404a6af7b799461ddd956401fb439fc3327 672e136848dfb709911a52293c808a8d035b8c9ac6cdd658654a6f4724262b97 d584b21dd6e64488f9668c0e878f9cc385311db802430bd8a3fc8276543fa4c6 e94faeb027b46511ef099bc91d1832260657cb5c3382c221d44a0244785d3b92 2ce6d37947485624cbfa63bd42f15278244acd25e1c55bb42d92006e72e6d0d9 fde17e25a23919d4325e496dde52fa76a9791d90aa4ffef481d3117a305c53b8 078e1cc66dcb5e3e297f2c79ecd3f2ce22b3e585c0d670686825c56620eedf75 7c232427da33b9dbed51d090e532993b91b6a35b8f2e4f451f24a3c779a449fe 3bad7fa1351603545d97de7803a012bf8385c025191b8b725bb22932ce626c98 4c0e034ab5f67cc5f1b1e588c74c5e18c2453de34e394c586d48ab02848a34ed c08016a36cd11003e3d71b1020f64c4a313191c12d8bbcf15b516bf0bc30818a 2130a0180a8c93d16ccee5fe55feddbe7ffc645f9195e74c6b318e14556e9751 b246d9da77cdd643a511b2810a38f0794e733d95cc478396a56aa2f6fdc75b1c a841a54a8ec3d784c75bde9af01c2e25db970ce5aac191cb1a3dd5c40c5ae266 eb9793515a5b3977ce00f477fe4b6c4166e94a2310b34a51520ba3c550708489 401a6e37a0e1ea1c2ce23799384f7279e2ca65d01e7e25074687e27b53764fc6 1148bb3b6e0880f7113584f3ffbf0bfab29cae1e5b07d2461598971740323950 ab478ec2e13b880d6bf53df401d65257101544fb34bc0593111bb5e7265c3777 4babd32137059beee0b013876610424ebd9f49581fea6d36a1da897069f2204d ae98554f8dc57aa29631ca74909d2a6ac74b958b2bd6f8594c63e0a765d51229 f5a8f8dcfacbabba8e5d329bea7a2308b4fe20aabf16d5a99b2b82cd234c4b7a c26d95ff04c9bc422e454eb33a1c015074a69d3f100310834bb5ea0c3e204771 49f422d949490b036cbed82aff0aae4b3cecb89d4d727c9a54d799263f2aa4fa cd344f9917f9aef47ae4c78309b707cffbdbff27d2240c7b414cbc184e3c20e2 280c75793a5d7af3c50823c92f15e94ac7bc3737140ce391dceb45f8705b9f4a 69b2999196839d31cc84d6e13fda92311cc98769c73fbdcaffa7e4e35230f80f66346b7af66f0a7e8f77c0b312c06698139ccdec71bb227044dd15f19f6da104b534cd53904aaf9e5a09ba466da8903978f8c06890df3aa0ec5a881299d92b08d510ad67ceb9aa8599d9f1d358dc8d96f3043005c360de9c641e61ae162e6b0b107987a09769af0b471cbb8aabf5368af7fde96866f0b0ed840434c61c2f010a5c768d21c49f43b71f3d1273c5035f8950c1c576cfa8cc70bf8e31cbce7a1d0d63985af80ee3a07a00e23436f445313f8a7caa79ca79e147ac52a332ae9d7702a999714b954e82aea89ffa0f7af476dd5bbd6517a27f54358cffaacb4e41a803381348bfdfc4247e7024b07c17f7ad0fa8b391e7b49c2641592a471f7f136303fc312f672fa012c23b3aaaab8aa51ac1889fbcd6e33c11b8f08fc712a80e740bd3192694a957fd5733052dc86fea141c5622eeed991d12c14118f9b87796fb02d58e25e10a2644533d2fc6b926ae372a946e8d420e62520695440479eecb4a0eddaaca1e94655e12280ed9017cfefe598cbca3d6c35ff4745833788e9ae2020aba20096a5bc60b44bbf78c009314af10630b50237eee231ee043dfe4cb73900c0c8921da117e32d951dfe347cf1df158e8b0c74143ef8d6544a4751def0b1009b7b1ddcb2cd143cfd6d70f7cca2fd727d738c6e33c4816b24e8933ec34ca020b3493e0fb9c78cf0e15d7d25331eafcc03f0a56c6087006287c00c33b2017b7065714277b60f067c189fb87cd95ba1d3c1bb1c258ef3cf69460cd14abb0761707655d5728ea2c17a44189af56b1be37f7207e14977a38ec40fb65b7e8e71f2c0d8d9cb055daf41e615f9d5bb1f3da046d19f1f267fd1ed557b4c2ce3382332c0638f5af02c9a90eda24738023c8e22d2341822464683a8669d7fb049501872a0782fa96c04d23a0da905e69da554a08b5ec243d80859998a3fb38d02b71be210a4dddbc650cc1b549586662b93cfcb6a76d551d88e9d0352f5cb402bc1832f1094d9ec154822b0f1dc33e697c9227b01387204c2f4d9819b629075c4aea0b3201f43a5f0ef122f4fdff5b450544d0f723485d251adadd5fd7537efe7e15fa930c28497c4cf7fd75d347ac354994811d0a89073811911fda022ed1df56996b150d7e6d31464faddbd11eb56104c04228114f9ff532a588252af9a84f711cce8c0b8139219745419282d2c26085f3a278912b2218f74be9a49f46f91b60f073610e77081b58e410226c7077615b58b908738e3c6f4ee39dea6d47cb1d4c85456c05f0a3c27690d588c192c79f2447ea2733e12f6f362feee70d16500e3d1c22080a401f882f5151bea628c97d9329f9761cf1bb4d19948f5ad7615b212e6ae9cd0d5366101c34191cabf57d320f18003fe0d4b55f4a376ca517789102f865352603d20da34ebb4748b7bfe69853d012e27f4303ab88cf27b92b6c6db3140b807e0f984c930ee1cf04fad0435a8808558d98a02b61e78b41c91fb26a86ee941f1905e21ef524f58c3c271c9a6454e4105c11e74dd0597a94c1e872c17732db9008038506527db0db7e1db70fd98bd561f267c34a3a02b897c38e361f5ce03193d208dc224cebc8ddc23a04ea5349d31093b7757bdf8626f334bb12a455b1a9dc770a0dc1106a676b8ee94e0e3ef2992aa44ec473eef791951064c462f921dc9d8808d1159eaed95aa991eb6dc3ebdc9814ee7cba37e12cfa874ab0e6def52e2ba303879ee203c1279b8f245af9c9ebfac6b1b73adf53954d43de871f4d961cce030368f5d9fb5bc03f4ee13c762965a122ef9c09dfa337bb180ac32d363d1a95a00ef5874f10566bdcdfa0f616842b2afaef479a91cdcc6dd0823dbe687468a6a3004bb25832fd330d7c5399a87940165c58c9b57f836231a568e11f7e13fb704a0243285fdd3e41bad1d48b5a514f7e754f253178e523637c8d8525557262f74a09ac68b07e66fbb57323be7243a03dc2b7fef0883744dd157559806be06c112d0935f0b7f5e97cc20589ed37e970f39d7893d24c98284f1286f3faee4b6d07940d2b37efba14644778309f475da347cf160776df672c64bdbef5c60220361f190edc1fb639421595314187b49848cb0c195962d1f0367c163860225c8cc972e40cbd3a2f72a951f3054b3c333489481c44f2a0b893885c25547212bc0c1fa9040b7c694a37662aa301127dab2f37c17ae6b21aff670f2467ab4792ca35618f13042ff52fc5b12510d23b4238645ef580101b64e2f8cf1e03083a1bc4bf5be90a015ef9dc641c20df5133a6469930692f0e0af074267a8893a3693cbf44a74a730f55cb987c410ac5663ba973727ae3738fb123b5fcb385ae4ba3da839e7c850a04dfa55af98f3e068910c98c7854fe92570976f8832c0a409366d04aaf1404d00809ed06ffd3ad262d20dba2bff993eecda1abb3a787288158c0b3c054b561660588824b7e657ed45a72a2308438b2f4ff183850cf8e6516d1be76a6bce8e9420a5aee25d91fab5e9064814542969ed848d54e8584cc86b74f62fd1503aa9e520f0655bbb679b9b1ad593b3b640d58b6b50d8f7dbaf68c0b876703cc3274d175034b6c7fb6f18905d8ce0a5b7a00e924de8cf6a6eb8a3b39fc3b7d961e61f5c30127ef4c0c6e44d15a9088390bda00acee3e9f940fd3fa3ff15bf28c39b81b1006bbcf59b8a5bb68a6f91bc5b51878a06fd834dc2f91a05c8ff0c34abf59dca90a2e88b9bddf5e16e655f22c3ad6f9438ad8793bd6b21b1cb24a078103134da709629700661575f2c8de34bfdad099e1c690c472e41c2cd43e1d56841c038cf30d99e21b75a7b7cabb4d572317223d91be2c9d1b5468004b8ee29095379ea3980dff0125c240f887eed160c3c3f1a3eddc1460c7d75450c58cf0a0425570cc4a96bbb12d7bc4513f275165267d1418f54f6ab1e2e2b6575f902ec24a16cd98660188e29a8f16665cfe51032d1b44eb6b3ee47f71dfb184b3474aa69753a6ff8f0116e67b930f46f5fdbf9f3e3d8d8f4e996e7e755cb69d6d14d5a8526c5dbd3e0be0a20b93568a0c7bdc0fe7d5a2f77180236b7621910bb2aba766ab6eb2557c0945c81106b7d7ef72d0f29dd7c38efa5e37d8859e8de4ad46ea4eaea2da6e17079cd1a25ffbe9058bda79096b7c723f7b943f928a04a6fd00e2c4b4c56cbb110162feb310b0c71701ba19b4669ae78a89e256b19a68d54dcafccb462333adfe03ad5114617f7df8608a970fed529eadb7fe7425fc4556a8208a43eae52c92710092847b58a5cb9c93df8aac56b5949c3e8141f29dcedb139339b09f808e80400c7d98ce95ae02452498fd97ca1d47a8716f50a2b84ee1fe97f7b271921b94a405387ebfb2888a180a5f05205c68c8f3bfea67f3675ca9b6db38a70c7795596a0ead97a307e7906a0420d2706801abf294b56aed8d6931b3d121274d69798f6000412109d884e43dd7b73a9bfae27fca97d65a2f6a3435e1e1a29318cee36c4801f8f938f5bbf8b3d97c7caa1e62a27e098a6969423b3dd5b4f624cf0e8ddac80820f45df09c4d33422d86cc7b0245d18a0bd591213a0a43065087ebc9e13ab5021e5b9b92fe5de2facdaa8f45701fde43fc4c94832eb8a33bb9743f269723940fea7daa59c28d0d199b2649339b4c64eb64f1f46765d20141298f1620dc26270575355a023d17af00fd3fc87888185d6813528d828c69fcf20cd346813252e30a8cf0656f4c7ba852e6683ff4bca210e89184036539243a39e15b3cd82321fc0535349bc9f761f7e2c231d799e6878919a72a54c9205659462741b684d047440b938cdfb4a2cb56d743960a688d98f7e6ee5b74cd972902400cb1a2166dd20a06b95851867a9dfe8c226925029c3eecb3867503f0628dffc768bac3ef84b2090b60626d4dcda87ef6aec8b156dc6fc5e8237ed8bdc49063de9ec01e6e5a3298018a7794faf6bc974a8fe38a88d7d787c5daf8b65c6791a5458d11b466b5e8810186d1f8f62fefc9d5f34dad3bfb152579ec07f8c895a43e20652a4eb8d422f00829548f92bfc0d0cb432bc3b1abb476de15cf07ffde8981e292289739e7e1fa0164ffc3841434754dee22c3fca14c958f62da31fcba0653ee66e89153e4ef3b0b5e5050c0ede65dfca8b9c47075853ae77db4c73c20fa975f8d3da20a4c787902606283a201297d533e2e7da365a9ff9ee8bb8a881affd14e37f3dfb501d3420e1a71d6795a462853c39df91a81ef504bdf584cbd08b2368ced7263a88f19db0fd187a2f2f845b62184da8b5db3e2960cea684607fbee70c0ebd15dbb3ef8b604472759b9821f8a7787af8aabcaba5761f9beb9ff963783b6805abcb175423403aa54a2d656f32c36acc001498e19a993a70427197b515363313bc500c94dbe078ec2f88f3173859ecad1b933f38541c2f30450520e237401c7c692ac1beb10087bf065d5d7c1a1eb1d191684ec6aa7d72a63b620ec147fe366c022c6cc6db40fa28bee53f22c388231a1f1848c1fefa729af28b48f5035c54292a22f522b9f09c6c7026a7ad801f226d36679919bb6459c71769bacd7d958d602c38465d25601430b5b8d6c1a6cae72dce92fa83856ff946fae00d26976cb478439d19264db0e2bf960867929973f45da9eacef57f311c26b85604886832a772605ed4bce4d0d8e1b48a7a74653cbe3272be18a1decd1f8255cbe43c1c90ab71fca42ebe8130ae77242e547ee05956e2069d1791d13430cbf1edd44a678b28930c6ab268b550ea657d9a46f163019251ac1022f9e24e94ad26161d9fd37c502bb8c717a0347dcaa054d2907a15165978242bedbaa02aa301624d34f1e61249c9c5e4e29360a04f9c932c101a7354de6d55257f3a2b8fc3cc756135f8af0dd935a0afbb3dd7f0b6b468ea40022b010451f23df1c848366c8ccd69918c22a3087fbd1a9368d040aab46aac8415b7278568e1bfb529db439cc13cf60eed6e1991fdc93e28177830241c04d5fef799d4c2fac88ef4bcc8f1c13cc897438a5349def4979a4378c2902216ba10a17b52b66a826472f9f4756b9e0541b16d9388e49ee410cb134038f045767b51a4589832ccb397585d293d47df70256b298f8e6cd80e59293652f190cefdf2f4634585d88b3567fe0a8936d21a0a56ed2605fdfd83d821cc5195c070030c595c13b2a22353fc03e03af0e6730f2542397820d6a9884b4ed7d472e8306cca214a9fae869e804fda16b96f82d084757a8c6271f6450b5405a5705051c082314d665a41bed294515a64c83e200cad12ff52dff23ee199b636d2483c3010771e4d4b42ac0e006dde406b02db08f029d8eac14a90ee7b00ef241789f4e2d0b2ce57728610c355e414f7265264106852d71160bd4f74d155f414b965945b60702671554dc0092403d398fed3ad11b6a02e50281df1ac89ff8830b27a0c02a1ee748da25063cdd6fc59bac0ed008e879e0c872a9762840ee012204cf3df8eb00c54000a0afc565ebfb586b8061ad99a6b2ffb6a2a026615dc731b03e80a78905a9cc7d13c310c413ea33e666b8c971d829a8c81979868e93971d4ad31c9028abb94271ba80c9cc42def00c67fd282a14f6ccb7e155584772108936c46e48af0edf364c5789309567c76330e41c1a36af66369114483ffe395e68d065405d7f0b77b6730ba79bb0b9fd12aca4f093d933e3b5558a1cd0dca5586f346373dc180626cd5ced1833f3594c93e37c06a22d8312462dd73a40cdfbfb9ca7a9d5dea70044ea816f0d8d6e2f9393358899f99f2a2de7ce05d20fcfa471889018a9f80a0803c8096f9ca386b98f1f767b35ba50dc5acc0828567ad2060d3a64787381d30ef75651a60b910a9a7d64bc2ace1b991f9d0bc17557e2aa827531046f3fd5ae0b6d0129241d81596670f7b46a948161d68e1e57bf5d143b511ba04e85d124a905788a6094ed9176dd978b5e459735c0f92eb1f5c0641697397a8e4b32a95015082195a6e2afa9162b45b570257e9890b2a9ae30588355aa2ef4559ed803b1040f6c27c03043384b991931c94eefcbaf504cc64ad8cac71775c99e44ecfa5cfe0e5e3dec7c4f9ab415ab3647db357c379aac16ed45fdf0d6bd9d1ca32e7aa247018c4809bb7eac5e8f9d43b61198213674f17bf3d44c7f2df97cb1a75d12783709f40fdcac6409624eaba47eba0b9e23f6f25b32d96d7a97aea13511618c85ab0ecf9d6242e07e36f1521f0b442c878d95154d5175aa1be5f588618ece6b7a0803bd52fb8db954079f30cf2dad25fc0be8705747784975e9e2f7c7a6af9e978302d56c15e7c1b2707c1de7f362a97f2d7872bfbf330e65347cd94735b40575f6021e8c65b6b1945ea14211fe208ea1c3f4953aa167b9133abb7c45d7099d6fdf0ac46cab7143d264d55300f60c9763547d12ea709bf910b1829a55c2cd9de38b019e08b9cf653fab7d65e6c1143b26bd80eb21cf0ec98f3656433717f0389e090407bf9133d1ca8349651851dbff95d1473433cded082c878acd35200f09fa6d0c4770d0a045e8a18beaac6fe8f1e9026a5ffadc799f0f1624b42f5a24a565e700a810a5ef40e0f96bce6f81af1b3fac6d0da2cc38898e400d67af5dac0f68bc0a12c7ed090502347c035afc90a7ec18684ec55747d15294205a464bee98d0910196998c54b733ba2df741578b6a1b8fed209b4b426f98c33ae074ca53e358cf09402c1ae84eccd6ac49266909a202ce94652f255275b307f1cd2ee033dedcc00bad07690854e84f633d4b2bb093ff545580963b8324f76220e41b6908fbf6cb09c7b6da92c9f78dcf25a9fafff20e04573dd2a8da0e98009259f78d80648095096c1f78d867a3ce8579ea165a94ea7128f51d1a91845e071e73a028302373fb03ab6a4ec03a82392f3aa195934cf5b1ce417a553f32b4fd07f0aa40d3898f5f069708ade621774c7f22167761ffe2737f6d681a4267cda3ba316d0517093b6102f14fe83984086a3ff69a0e1d5ddea3762cd525c0b1545811acaa0a80587868001a5e89c8356e73860b5b8263de3ee19497ed6b05e9f2a81357e8edfb56687901a5ff6663d9fe54370bf7107f33710fa6d055e15fc62af1daa089bd9f5dbb5706622db63245694c8913773d066c9d62a2f3201ce98273651913c649eaa249ce07344841608d0b64c911463db3ccfe3f8cb3b56c2b3b73d06bd0f545cd5822b10251337b7cb735386ed870a36295e92cf75c7c80f4edb01b23105c61e08586fa0529612a0bb99ce218617668789e765cc87b26e0a0c5557d10303b0f9c6698b00db142f38ecd310dfe22290520d810e1a3acc826d55fbe39424b63475eae917804a2aa4adecb24f84a5dc6df7404a5704f5826b91e14b2963209739315f259ac01a2a1524e23d4d6794612aa309a5e8578b87c1886656f611d5288be137cba2d0deb48359d4581d97235e0de708be13609429ac4e7fd86cfac66aa20756073cb0087a15053fa4c830066b28da9f6812520ff42bac4a45db021fab350c102d32502435f19f87bb3de976df7954d41211c0ca0b2542b743ccb7bfaf91de25933ce076dc45d9817f87fc6c7c7bc69512bbcd6a70ce3e5478102578ff55eb255e0760f1232e79cf4da5aea1f277e024d1034be5aa105024d61d6b7888d85f6e10a3c0ecf1cfd5e4f4a0beee4370caa6a77fef7b21d3f14be7fc466bcf6d20402ea1b0956e09174eb704c81d67ad0c11b5c73d36286eeb3436cf6fab0e972dbaa9f750fe2c816a1fdfd911e50b10da086f63836bfe37109414e1057e90d46f3e1747300a52e4c065ff875f6250a937f72f68ee5a0598f66aa0b65e45be551d453d5f30e8908085791dfdaf589190a3f7e39479424f1baa387fa28055ca50e7617f79f0ed7462e9bc0d76f8c7f16c9f101e3569e50c119998bea55f550534e48daa0b90f075275da9575ea7aee47c9aca69516a051ccab5fdf27b39b5e06e4b2206b2a0ec5ef4bc81763ea99c0ff1061ed4dfa4a8c48da39cba5c8bbf97daf6180c6770248412b646a626637d25456c8005432626b66ab8800f088a2f049962dbb507e06880227866e39083fdd9de384b1ff49d8c18b6a04f79722edd5661f1c43a5b703c7cc80cde51a7a3897bc496a75129db008c016876a4e162dbc5402af899a4e0a121464f324d7a88e36b3ab8881334e772a66474258b63a655d42f7cf203c490c0f5e31a92ab558c028ea43488fcbbfda9d647f9622202c436751581e4989c004c6d8510ed7601103ed1fdd67cc10bc9a35a88be9bd478b029db029257c49b60d false +check_ring_signature 1c8b5f126220fbc61c7012191fa10f75859527ce208be3cf864597d6eee9d289 8944f021ca91b495773537af24b747e293cd87eeeea2060d33b6a35122128b48 1 6d49167a50a81e29331dbb9d50cca42b2242f88490c07e3bfbc7df9cad3d71e0 b5ef2b3d67d415e02b6d1392e7e35cbd445615f916ae54342f5ab7a0e2ee880201b144ce0681d2001837789576f67b32e727a84909712b2a7ff0a84960e4a000 true +check_ring_signature f25df1b6ed4b52e4dc356a42e3e5c2f47e0139171a94b10e2a10343823421af7 ef0d28ea6a5f0fd11e0293d23eef2d96e315263e2ed02a8b9e6fe01fdca01f3a 2 6c2f9437a192a1cd685ded873145a8176180f35b234875d861b28b523785478f cad58deac3d4e76d488cff9aeb3925b11d556efc5a0b629459f2c8ec8f37c899 33e0d29550a43a35a85f024da8b3d823b9a4af5b632bfa09a0e5ab23a47820010d67654f4dd64227d484e285d12e10097bd0eafc047e81cbdd1a7901bc439ccfe891e72d3f8cd546362d89ce679d425c3d6bc2e7dd73b6e5dcdc755c5a560a0d3a7c7a97f699f3c4492b552ec20dcb24d22b344b9ad973cda4600db999a20b09 false +check_ring_signature 5eca1caa0ca12e4b9e22f62452de64c80a04583e7fc8f8bef9e139b83dbe9dca 276dff5f90741ebae98f5da91fde774c8a2af81d017a94b0773def7f7ff31359 39 114f8ba0111975d7544c599f70a4a975df5c4fbe0a5e3865a9bce9fc9ffee074 9979323b7e36a907f5efb007f8e5967b7a36a1113407e6df07fab8f3d87ea3c2 1a51352aae0e7dbf6ed0a7bc90bdb9257e0324b0e82b42de0e8e2a783bda0f56 640948011111dd3f20c4b90f6ffe5f336811ccef8bc9f202e259510994245ed1 c6fa5fdb08a5d669fbb3c3ce7a7e0fc9edfb4ba51ffae26894129dde2d8b28ef 6b68d634ff865e664e8c80d0e4f732a0c6d71e0614f6dc5c93f59e5b0a8abdca 5cc808a199b7ca8e2b21ac9b1b3d979ac27b8c09baa3f734d60242db1caeb9b9 184f16e5151aa965cd3776d4e560b12771daa2b40c6b0ca116e05ddf154c8b4c f663933c3c5e310f864d3377f65d063756844a9643d874b6e82dc0a534f433be 5c82e347a259b8ef9b301f81f30104970074a7b26a598b5d0abadcf041fc757a 177698c1bf1d5dd4a6c39a6d45ed0e110983a2ab0ce595cc057b8ce50929fa9f 3bbceb30e5e3a6e0c44669b6f3d2c28a216d7cd24143bd47b67a020410237d7c ac37b2490ac989f712676050e5b49ca5c05de344697fbd0a247e93bc5f5c9c9c 4e419b8eb540e5877616af2f962118d316883b3eb61e4cb8885a27b25a9a6024 a9a8d7c34dd3e63ee6e5ecda2671e5eb28c0b6a3426cd6c58ce747c753872c36 675bb408cf35e4840c1c6f64204e2c8746834d76aa9047c0305353cf915fa55e 3654cdd6cf65fbbca63719da1d3f79e65744549fbf3d450aac910c6307042391 c80394a62bc66685d1325e9327afccb6b60511613d1750f18b14ea750eff814d c223ec6e3e3fa280bb325805c691f0dd873345a59aeec27d4bd1bbe1b6e3b065 4cc5fb5932fdb093c4a01a401da963eada7e116e31fb6339fcf63de785488bec dbb866edead83c2f92695abdc319a0a262ab168481ccfc258c79c877275340e7 680b83f735d6d8ed9711b76385b109140a80500adb4f237a2bff14888de8ae75 6e616528c474d2ad240a5448c68d2fb022761fab5945b18e18543b6be3fd6bde eb7857a4d3fd818d5bc060847712607845737b3f96e1fb56306c250fb902ec1e d5ad2b854edcc4e4a0b5a94e4d4de34d46a1fd415ed5d37cf49b3293c8fd7f69 658bc72626624f96def29d5e016d529b44eb3aaf5331bc0c587adebd7827fae1 f89b90514e477893f7ca03bebbd23a2aa3ab2ea10bdf798419a44177897c0cf4 90fa6527aca89a0b15721e2d1c96bf361c0fa1ee31000c8fb6137b4da46bb084 9df27c6aa09050f87dca01f6991730c2356e72fb55f476cb704b133f9b1ac23b a6199a38dfde7ecc6dc59e8c869f9331ad0a1b748db36e073b9fbafa53f3c073 a097a983e4babfd9ac4e212a65ae39c47e92015b005c4066bcda2d01d52b8688 756d7781278f1ea6d5450146161531e29bdcf33460e1414e824b523d13ba989b 978a5e3af8c7e79ab7a88fd1090bfff9cc4a81cdd2d9018f97fd6667540f77ce f05b6283911a590f7376c67d598a2e6aeaa0daf46516eb00fe28d2782c97cd58 01ec9bfb49678358ac0e52fb08d1b5dfa2d8a7f1aea973f0981fdba8a02715ff d19b5a8077faa08086152187e5230f884b9f3f13e04309de4cb650d2b0f7c7b6 be803ae57ccaa5998dd781cf258a502c7eab87661368a2f577e7d340ada938a3 19a60557699ac8aff20f7bdd664dbb9c90e02cfe3199775426a14ba33bd8985a 81e86737cf4d6e9e27f7b4f3c317d7b03176b7287c5c30edc3aef461f589b363 ade03c6203fe98fadeed1e58dad7f10a95ecca70570436ce91c1f7a28e705c0c5bdf3641515890bd32933d5d7e62bcd410f969e6317add8d3dbf51e1edbc1501e504e945e553d2eac54276eef608eb84f4896fc7d43c4dd54ec831f3a104eb0ab5b8016acb261f577a234b9e359bf1730267cde30ae1bad800ff45f81eaf8509644682545b0c60828b3a7d6a913f4a723b7b165d2ef7d64d112efb101c735d0c99a164d479b4588407a6d480d261e393b73c023250a89d6241652c44643a8d014f064c70d96011549c01d394079f255e28a2928b301802cdeac1592ada087505d0b3aa5ae184630ed7110c299aa7767f7ca8661e04ba791bf6330c10a7109503ca5bcd564f605ddcd1d0ba7a3e942445300bf3329758a4b8d4d4f7a424c2970b730a0e378edb687059edad0795e695a37b3dba06ab869721f949f2c53cdc19038e52892c71ce02522cf12e28057d032f1fc88ba78f1360668bd9e14bc156d00f6497c8335923440c7a3548437e648fad962b7ce8b6d9f36c95a859df12cee902b6d8d2eb5dfbaba97e898ff49b1e302a1a16b5cb40f00cf391f77c6d8a38be0bfef54b5ec21013cc33097ff069a64c2b743d4f2381fed35d5aca80abdebbba06275bd673e98fe225df7d6b15deeca7ba893e8601eb2ddb02cb387edb707c9f0225799e06c939e4750ac52cab2c27e25b3a02127c9c3bbda307c47bb7acda170b04d612308d2040cc52792e375b33800306aab8e0034ab9c631ce7fbe2dabe6037cbfc87df32472bc1f89e61bffe717e91f0907c0246664700a2dfe0327ed06062e464d54a05cb0eb522080aaae1493fae6c3341bcc8e5c31e30b6ae714924e05bb61cbcd9dfa4b992f8dc3e47ef841cab85c9c0f535da6e7809bc166d28e4e02147cb84e986557c1525b67b52fea51ca50428acf265cf41de75f697edbe9bb0a4fed012be9cfa8c20fcbc3afccd9def854a2e0a012d20c1ab8de81b9b4d3d5030e1c1a34073060cd5f88ebadc3ee2ca0cf1bad4e1575293d19f7c3107afbb30ea4b66fa662cb762e139e762728197ec6b93beb566e5259e76735e32cebf6e107d13414b18ae7f750010012925d08c4cbcc7a881ad14723c8f86ed1012bf6580a0791d796156f9a0454d7b62dc379a1003ed89fa5f4300ee33a03c4ddf56a460972752ac4e6afa2829d93380c6b623833a9e8ab2889fabab3bb0308673b455d049944cf62f6b36c81d7ea3189990377b2f9339838aea64e7989eb107cde5cee025d0f4cb1d10e6ca1c3c1b866e363c9d96a3b2f4ba20d9d5e0275614b41606d02d76f8b99da111535a60f6af3c09a41003a6d197ac60a934965afa2d1489deb005cd9bd703e7f29490a5e181e383dc99a5e872906bf32adcc3778687327a1e302e132bd0361df5ff52444cd363aca2308309aa57064f842251e4d819d2de5db08bff6f59ff44a73cd4e48cfe329f8de4fc1686f55104fb725994e94b1ebfd620bac47b9a5de83f3f7e840da3280894e8f06df2b3a886dc908024769058e66c807098b044fddadc24f2d09d4fbb337ba5f656f6259e953033315a99deb35c4e1095d283171ae13ce914dbb4d36fec66495e927eb41dac9c264dc2ee920f9695e0e30b834fb9cba5d54f3fce1805bd5c8978dc07c92c3c3fb7de9ff71aff3b3600ec933b104435b810b66c771e8c489d4d8bafe473d7cf8949c78fba3e07e5ba00ab1a6620a3ff12a7a9015b4568560c4843561d08e4fd03e7c627dc21b5aaf710069d8b02f7581a56f276511887e728bbe513f1ea2fa56093d91b50728eb8988022cf285d82297648b6aac98939f58b8f1e2496fb1aea6ea02fa4365b25138830e44b12107cbda3804a8cc37addfa90dade1c56667f0634fda2a0dd83b883deb0ee346416eb4ec8289a244133b0b4fc0ce26038f512b2aadf0bba9b6c56d610c02b46779812c01327241e5298637715d18b9d92c179a90d6ccdf0a0c8dd50d7b09a0eba781415d384df6fc086caa44a8b07ec22b3b72c96fab08e106f9990013038c93559b48403b17b3ca67bfb47257e282d049d9535d7fa4f065411fd836620037394f741058924c8877a37771a1f525709863857874ef15bad0c71fd9dca40d5e3c3fbc01aa2101e3862548adcd34f101b8193d5b8826f9debe9972566309065277c3fb8db9b7f4cfc5822bbf1bd1ecccbcf5543232376547eccdbd052fed0c31b2b3105441c6592c94149f03e99897b97454201413646ef375c3a1d439360faf773d666c22206acabe27a65088209587de83cd495f710320bb1974b1bc080cf878c58873ff93cdc2ca36be360030855a1a23b1b6312f15ebb9ca06fe121005b9be9da73af8ca0a3b706ae0e0b6406925b19bcba6b621d0cc83f5b63c5c8f0bcf9e8c8f6bc223ea59525f182ed42e856753ebb5a43e66f7a80c96a238014602a982b7c32aaad4e6629b7feb96dfef1915bce2aefb78b174f9d07f48661f210bd663332a0414ca3fdcee89decabb53ebb306b4892f0a46b7f635fb1358cc4d03bf295469edb82d699b0ea1622608c24895cfee9c8a6eeab22a5f3aa278126601c89b676385f5f50dbcc02be55434c7ee664d6c507a273d739f246d718eb8290259eaad3027bcb56acd3bec8f602959254e0bd1e973e2d8a29bafa2d23f460b0f8257297c89d5b69041143c603f7fc8469cdf77229f9565277fdbb019d5f1de088b578f65675abfa5e9d1b72791d84451f1cac16fed7109b19e7124620621bd0f3288261d1641d57c11621acaa1366e0b04d8c5f2bd709dd0e2e64a170e6cfd024f490201c9b36cabf37c23f3dd1681209b4d0b8cf32f11275ac0f3b87df45a027a42bd67309c8bc240da4717f779be755c172f18f6e49bd6ae478afa80245202111a80ca914b79e3d575ee78e9d8cb80d8776c203784b145661b08f77227370f56a3ac2440e41d6fea18a675cacf26c6076bce5cf4286ec161e917e013577e0a0e1f367ea14d35842198885379bec3e012a2efd37b5866580a52d15d7ed5ee057595287c2472ef4f99fbe3c8289d6a44e3749c8c6d81d5a3b4b2b740287eb707a42437b6c597ac25eaf5ea45057a3699da39df13f5ddbfc70b5637c92d6cb00d9f580855a6e09af0f85dfad8f7ff4062b0e684eea98585d6a66bcd95cb4a0c0d6fcf07b908421f9193c99f6ec4b71bd4f6a76b9dc20053363f54c6ece9e8fa05774bebf695f8c47f8034d0ff9e58b5c3b9a0333a893ce8eb75efeeaee981d90d36face69337ccb7ae34e149585ce270001238bc8bac00e15424f83e62b30dc06a8771ece42ba0f5ca3402b3522077110fb51d61ec97cad255ae1d2a9a5c3eb02f7ff9e224143c773edf7d3f46c9310a6924e6f9db4a745aa0aa7a9bb4505b601f9d9994dbeda9ee8d5cdfb91c5827c26a5427bc79ec3584c809fcb81200e930ee17427d049a06e5d7def7725c3b01956b02a44b955652982f217b2bd1a272d04d34ab09069917fad01fbcdb89c2c3d8aa8bd633b87010bd8bc670148ef073a0c false +check_ring_signature 32544ea876d5ecd760f26b69594d8e285d45bec05c5873f6a445b1fcf0f43dfe b4f7f0a44c3fcb33fe8c87ac7beb0296dd9cb85a4bf8c243aaa9e6b43a73f26c 2 0f2facb57af265a912b480a2a9d966065c34f60c8078ffd4746da40c590cdb0f 7dea0a708a4698dc1da35227c2aa7f38366f1f20d6ffa8f0f8c0ee1f945ec3a1 05b7f4f81313eafbcab0e1f78c27b4b5f26e268eb64211ce6b8a466ae4e9080b974f4c9199fa6d07b42e7d76ebfb011f7dc49edf3a2fb11c4264c80a5fc70600452df565dc9c780f34ac0b6e75ceaa63d88d3b97db855c5ddcfa39efc3c4f60f798bca70de3a8df90e2236de7f328f2b10b6c5f9fd75c4405b3c351f1617ef04 true +check_ring_signature 580c8829525196b37546741d7a8a2d13976f491efb01a2ea4fdd6ca31a8704ed 03fd20550044db30926d4d5f38cbe9a73ffc726ee773712f462ab1b0017b5ddc 7 92ac0b179bdfec13ade58e787378327be720e0b352df00ed93b77115be6a4af3 8e9b4d730bf4f1839318c00739cc970bc7355ad5a9118dcaa02e65c38143ef7f 40b634b4a503820f004baad0eafb684c4239f6773782e8f340b528fda76b8fa5 2cd670f90a62b56d66f9b623cb069bc5e78262cfa6b70145f32714853c987398 5dcd4fc08a1bafebc6b27167c2398da75e8b79a74f77e62e8ebe0d93bc417f97 a6a7c5137c00b6b69386639fb228edbfeaaa2b9676c9ad36a123107e2ba341e5 954ad2b8660f8e417e0c98ff4566dcbe1473d6fa79fad23ec7f449e90dfa1dce 5862e589e20a552f1908c272de47fefc9f6f19a70e077c4ea80aa0d428d2e809a97e88cefd18bfd90988ca2ade9ffa52609ed3fb8a81efdb6f72e54aec31f1062fdf8ea8274c52e53f28ad3f962887cd462a73a416b5dcd122703c16673fca0b963f60d7654a1da0f1a8d09d5b10e446ebaef330f6598c6a0fef0be4e7424b07e296e24fc708c7a172557d283d4b9f01c580b8e987327108735b3e3eef99320c23d7aaab837fcdff95b1596853310979b3c7677af8b59e43c97b72a00419f50f90ab1ec2ff188b0775589d7c5f1f0e1f986c1341026ebcc79d7b74316976b00052ff7fc234d453fbdfe61b18265fcc2f4a3c78a33a65e1436994d8efb096de0246f8d7cf6b985a3dc148beb32bf876358f02699f2de9ca647c7f8a7ace11f207b5edaacd2b8bb8557bf8dd3cac97efba001e6fa78e27e15a298ed07d4dd874097d09b7456900a62c0e903707b860f59c3e418ec4d362747fc8c72b50507fb309cf11e85e860205e2e4627904f21d49d6cd9e98311831cf3877d7b7a555498706b97896573c3e4185fcb576e1959e9c6921c89810b2241083f17112cd72d104046a23bcd3b8f55479d003cd59738f5b8e814127055f8a980590371d304d5da706 false +check_ring_signature adcb8be94373333382fba3ed204c1b638afbf87e1d5b71caafb58269e3bd8bed cf65e8e1aa9fe74884dd68ca60e5e96655356c56e3c3fb2497ffdf6042bc61b3 1 c4b50be3c0a6fda3f5260168ecb316277e9e27b6d492a6d157a9920eccf902a9 69e2094bff805825fd5e6466ff616a4d6c2741a62dd54df7840174a7e6cdab094a37737e2f336b418128e1c0338392ab0bf120ad841c4d72200e6f1c07ce76c0 false +check_ring_signature 02e21de4ddb868b933aeca051631759ac655f2c41a626d2793b0b143b145fba3 b0f65993e31099c349b1c1066560bb2072184fb1a825488f13ead8947721248e 105 b6da89fe42f7071fb1dbb2275924f5faebe473e3b6c3709ef25b6c3b00a2e03c cbfaf7956dfb0f5ca80d6f6a9eb7b3591b53b98a9ffb03062b3d265da0b7fc8d 8da90502fa859df5c1413a1bb1d0f290933040c0cb3344a5f259bf6e91392726 d6a61fa7db2a56acbfd2b9239a36aab5f75cdd771fc899003f4bf4273c8297af 58999d9f7c6ab64249bb56a9cb99afa8fe24bdcc2cb41739f2fe9055f5142d71 0fc83b6a3423f797806bfccc1e1938489f38559cc0cc4d0d18e9329b544f405a e3ab3c36401916941852e7903f1a148b0134a1efc8e816e758097a1ab74b3374 03fd931e6246ea58eeea98ddc4b0adc319d311249e5f2b1ebd8ef0d321a7c1d8 921acf67010d31a13886b601886e65f32dc74618004b693d88dc4382592ba030 35d40e73adb2f3e51adcdd036ae250a19ba25f770f71752f4503b4bbbd37fc7c 052df967e1ed208e539b12d2e12c72efddf9af9c23dc6e566e8bae08e29c6dea 8b0a91b1985d08899baa6d3a4cb732375d9be904444bc8544e967c450f404ced 82c70d6dff005ea070c901713dc3f5308267b4cffe09fa9d3899ed7772d3ba69 b19150589569478a63de2e3d0be5bc156aa9733d33011fe006be4269465e32fb 6b6090f323f790da357e3da7f4a97170cfb156940f88fd75e27d25945fbebbf0 246a4fd5e0384cb325e333212a9109713647b746676299ea1f08b6729a1a0374 fa69a4c574fdb8d80f06354ee55fe4b36db6afa5b6c72748a26ddf8dfe1b971a ce72e7e5a5c21375196f5ad526b1b36f3fadb16eddbfd66e59b20cfddd2c2151 ad043c8681f41d8e42f3f8b256c48db987d6de08b7766945cc0f3995d7947c4a 8321bfdbfd9fb7b97da2e9f76b543216a888e674b33b6fd33fb4d63e4774963c b83f2f427e1b7431e771a1a51a7aa85ad9bf09910d8975b4c1ddab7dc05d761f c64a48c24d16db30246f5a3d5d04f11e0716ed67a09f799261450c873666d186 9cfab1f24867077dc4fa839fd58d0b03038ac9e849142c8ebad3ce8c8a18fae5 1f544120e5a8fd7c23e9d69991bea5e445b52699f3130674a5f558541efb3c4e 6bcd861162e853e720ce8ca2d1c3cbddf8d8a3e995c5e2d0b0d3134daca7e967 dfeb5d0d90cdc1e29253cadd7e39ab91512a9d791a3ccd7651971471fda24ed5 1c5566516c6256c159897b5fbeac47e4135527d487b5efdc49e5dc16feaeece0 ea6314ee3541ed9011c98ee72ca7678ec0bc4c35fb34671756c575727856cd9a dec6fcff9b9af08319bf41e9cd082ed1744818a28132f6e98c987ea25d66dd77 32a10aa0ec8d252aec9149267f404dec182556be625e91763c7e119c8a9bd0a1 c0c3516cf6de2c7c58f8f1d44ec7e8b2f9d24bbd6f37c0b0f517c57a06b7b248 045098352e7efa9f82789c3d95747c2f3553c12b7c625eb154e060428ae27a8d a7d9cc03ba119512bd7a2cb476db1ee807a5a08f289d6881bdf1659dc80fcf9f fe5347f7222193eaabd0b54bdbb00819244857c52fc95709f209f819292524be 51a4a1e1a883fd0804fddca0a470d0b3a5db47f9e90a66792d6f9a4d99c3cdd8 9e8aac7f083f74d9e213938b8c84cc7d49f267d9cbb6b1cf9fde3c0fca5199aa 11bcf9cca23fcb4511db1bda3deae980facf2d23b0d9b2d46b67673fd9e02ea0 63fdffc2047d749432b11c8f4428b647122e7dc30a6034c64f9d3ea8a4591518 8afd9a013b940ea0c270d84c094d8f466308b17872a4417342d509e8c1b916bc 1d7cba137897cb9426a01e8f80a95fa188890216a42a3a93555e1d91d71aaf8b 7460a7dc783d21be93cc1b55b78d6b48b86131f3f0289bf6b11f0386bf947d7a 37c65e013164bea1643253f548301ff5d4feb69534cf61336cf6149fd23c7245 3b96388b5346d6bce3bfc570b08eca54812d51f6fb32243191e02748df46112f 5031aeab15d91c50d6d7ab651a9de894ea98a698a070ba02dbaba0681e8d9262 77e1336b28684be7b449ce606d78ac2e38d925d99d7fdc69b540ec4b0837eb9a 1bf05e837d46a189ceb41c14cb0385a39fe56274642edea96a2506cccdca7921 ba3480b48f178d8798b61c036fe169c34cc36eb3e594a78809a36a585cc7953b 92043d6805637578f106e9b57f824e2f6d53130b4de5dc5b787c98604f44f632 3006e1030c68e219facc8303d8dcf404d91e0180a7390692d78579187110b178 87dc75d20aa8eb3c771dff6a25303299fcf5bc0814fb405b32124d347d424453 df445a5e442ac3535a81863c9313f692aad529fa35f69e85116446281a68004d 84dfd9dcd01c314adc6ad1e2ce1b86caf269c0d9c415d782574a153246c1f2bb 9f04d8746f603952f653ef155243c5e4585ca18dcfd58a235afa38164f015a19 0ee665dfaaec873917015fd41b5c7d16f4d50fd25a83386202ee4f8d86a4c91c 491c8103f4508fd85016fc870577f42fde1c7b11b2f6f6fdd7e1db5eb2189064 3e5ee709152e63b32beb1d0ff71818042247809c73be4c0c7968095415bfcb5a e63f6e17c24e393036b2b036576eb26812f9192185dd3c4584e0e1454f558435 8e1a5a88a4aa311bd395898bc0c71be409894180c73e70097212ef197448bc96 893a0b5435230e82637610117fcd84920fd6147d4e71a0bc04e7a772742bae1a 7995ac2f3e1b2dcbb453da33d38ac945bb7cd8b52a18dabcd757f40d5836e65b e2c2bb70c03b17d50095e61bffc02d14fd38f84be80cdea6336e925263711dfe 195f51d559003fe319e4fabebe08d078c85073535900ac99400d09a3cf4a3ee3 fcc50c3db6bcf4867e2d89d9b8ec48416a9fa991804579f042ec14351151a496 4cee50073ea2dad1b86e0a80d89114356f8c0d1dfb946ba185baccfeb774072f 1b6ea78b166e2950a976c4fe5b627228d4e08e14991c72ef9141a301bc8f6807 b5905d26c8666e9b4269a7531de76465ca6f52f60c0166514a53fe919430fb01 cc3eb092a791b8cbc3b4e3a573f5a77682bfdaed63cdc216d232e73cb22f0fbc 393775cb8b72b8a1c349d2f0f95e647f8aee368c050c7d215cd933f68ca74782 4f465224d7d55b8f60c763e2f488c8d9f0dace85070c2f9aa8a373a1e98f2a5e 150eceb242fcd6aac44f56c0756ad09379f7e526324d0a9e0e1221b3763cc81c ea07565446bf154bfcfbde35c5e3bdf4a335a63a1800201e7f1b3351e400737e e746d4c5567ef6fd38279ca86137f27f590da8cc3585fa92497c7718a990ea47 d8be4e69cd9aa5994e9e50a664558bb737d7c63bb1e8969976555bd140bc844e 382c5b75b753b1c4d620b1051547889c14e23203eb65d6ab34f5a069fc68c693 4cca11045326bc53880ddf4aca4376a33d89e357e43b8b60d707387276b5140f 8c884b4f505f9addc54c592039760d65acfabb5690e89bdba6f463d105816f52 168ce9c4224475f73422795c364757e7db61e98fa5e1332c565b1a479bc61ac0 90989e528f42f065dcb516ee9eb4da853403db583f1ecaad614c5d3bd877f897 1d4541add83c6bf61544cee68e39c8615fc277409f7ce0e9938819c3cd28565a 56cf2385ddf03a56a4660485bdede33edb8ccb35df428cafdaf2443727a32bea 56a0461a62f0314b688c908c208c3dd9a107009c6614ae6686d917cf162c7aec 544b410a0531e21f2360d9d11939072fadc6435cee3766b8e6e0401187ee2d50 c1d48be408c4945673b5e5c03bb82bafe966a306281b2eb634a6773fee532474 4b6ebf3e20dc7143cf77c23f19be9d833a570c1b208cafbb734b85c27cc6f03d 25872d8c78768c1899878f6e3b643c3fa23474505da8bba73671961a1a2e180e 9c33d46f9f5cf5ed908f76e9415748a88b3e3dad8a2f3948a00afff667a97441 bcd02c5d9dc8f82d0c32f373e97f3a3e9d5af5dc6991e09241de49784ffc9cc9 7bcf6d49940eee1ac9c8a432a145b7b6bfe3aa0291db0f28088ff175c2e94c71 31e64fa17a0d0e456ea425e2dcfa57d63d5de779e281486acaa98347e89e8dbf 595e45a8da1b5353830fcd20415de9b8b6755ecdafd4e2106af39c3fbb141ee1 233568769b938bb8ea31990ab5350dc999d65a2016cdf553fa72ce4b034f487b f8dde78275ac41b068a76cf829286e22b442178966081d53cd0d5337bec6ff8e 8b5a0943e40c577db48efc435b4d52a2cc2e1e382f36ed8821e378af27846b37 f65ebe836a5eefe7e77ce61f2644eb8aba532cc576ee4ed5021aa9523c64e15f 453d799d46be79b0b5e75860f1baae315241ab015671e62bf728cd89359e442b 0fb401ef2e9010b1671df9cb381f5166a7d559671beabfb38c48a8050e50a8bc d9ba46bc9023fb4f3b9c28466e67b82c1a0e63239d1d2a8f7a66f75fee47c355 9860c946e6d5440e594d95533190a2cb6edeb104b02656b8ad7138f27a663b98 fa0763bbd8558e96aed6535163dddc48a2607bb9035aeee7abdbbe80f4625029 9bc54d9c08503589c02420841a1db94193ab589bb1ac75ec613ed0443cf1b20c 855dcba7134edbfd9465a9cbc5bb70787247bc1a4c4ada96e284650df989c29d 03ed48d01815c5f8b98f4326d7cd597311eee6667bdb29e205dd9b916a591f91 200371b002680c2f89ea2cdc7f4abff0a97fe7e2a2a1cad50b893f0748fea57c 618b24db2c3dcf3b22e880f131838d77fa5665641783bee197d00381472e1375 899330ce12e8ee2fe3fe717c885dc9749076842b0117482b387559d6c34ec3c4 1e2a85f8a33ea91941be4b83bf750a33e7cfa3f12ea4401e922a01066febd3025450229fe18f4503fa1508dca6f87560cf1d6213cc1a5f38c8e94e0f86911d0107e66a6e2eddb01ff41fb306c74f21ad067de6ebaebf456cc6ac0739ba7f91033c534c34093bf74417f499c3f6f10fb0ce2e3874bfa9fdce34afb561074a4109225c15ea6fcdcc823ead4669e8f61fd96f1f53eacd8415e8af18e29a2c317b038d0da5cc293b23067fc4b37129624b4ad97572696e22c72717a3471cbb404b05cf95f68ff2c0cf82125f108807f6e8981bad12e218dbb3212da34a9b2ef01c055988f800b32af48b69b9987c701fe2481fe861848c58dac31d90bcae4885cc049a991d7092564d69dd0a7645df30da4b7f4bbbdc6787b9e52e06c62535c3d703adf4f78905380e09895ebdfe9a054009c52465af187b0b9360494d20aa257c0a71ae6e9062fab3c326fb1376af1b3ea6f6066d69d280b207c3930017939ab90e49b0f1272786093a3770c4994d6ed5087ae354be811422d9eb549959aa30790320008cb66ae35a422b740a854f50b270b7bc1a0b63b675fa55c6d687efb45f0418374f5395da91f515fc3636c256c73f967794f3ee12967d48278333e7f2eb0c0717869a5e6f3b7116ba741636687a77dbf43092c769af73d51c58d6542f370eabf18567de230e1a900672759ad882eead585d8cf2ba25a1b7ddeb4671bb5b026912b399f770365c632996e430616d71362e31e1fde69ef8236e7af726e4c204d0a75eb1ebbf227031efc85239c4660e6298f160acf54dbe0a817e861ea6e3052dd654545d2a1d54ecdf59d1542991c046db6a4c30c6b30cb1205d882cd85d0db71b6f41ab1c62171b2e57fc91ea973f59e1c8bb2e9fbff679f354ef48ff390e6ed9f933b98bec5a7a92498e867b023dd584bf3aa0c5788e995d66852dd9760e0ce3421e182c1461642bb1a0053b70e56b3c11228f04ebe5aa6a3427cbe218060d99a3eb87c22c293861909f321231844aed190ea8a2380ca32532e76eff230d81263aa1937db1574caac9d174606aef06b418dca2bd6b4462fea6f28db33e0712807e60c1cdb6884681ea516469f5fdd65a6c882e50a5d8f78b7aae46a10406bd3d6ac4da82878d117dc408fe112e1a92b2150e6788818b8f5e22277a298c0d8daf140a1e4dd3d09894763c0b78200ae15576af21c53f382ee9e554e4ff0c01c378e7cf5b20818cf059eac9f5192bd123f0f5ec6521a18340c24baff5fa180d176a0a7ffce62c371e87fa8ae9c7aa3cde1ea2b6e91d992ad91a7a6899ab890e42af195372f3aa15c6814656d08ac80acab0f0e874cb62d4aec64a6380b0ed05c2a831dab246de80868528a72a485c511f19d217dce72e7003ce03f62d56df0ad38ff3b76dc550a2ea7b1fa9bf33d0b5cf4495c08fe647f04411ee340ce7880f58e1bd576fc672854e7e009359494d9c697fa5986e47a3cb4f08dfcd37c20e00729c9fd8372d6f71fb8f54045ab995a9fe5aaaf1ea283eaba1752fd5d732a60eae6ac2f063ffef47cadd3d7c858a4b151a95c9ccb1bbf7940fd0fa544d9a710b08f990294041e547a197d10add8d8f1b4119fc9c5c7f0f4b632ce5264785fc0bc01acb7728b988da1c8772f45c8cbe9beb48deabdf818683f8c2e0940089ac00b7a072097390269a42073216b2d1468a97d73d6210aa60edef068c90b81ad105e86e30864e0f28160bbbb5ef2b3a95f005cc9c4c38aeedf66e115d2d5dde170080dac53d1c8c4c2b6b679351805cbf264b1bff6e1489ec1510410795663c6509d7be862f0d398f08367dc0e11d692690f736817b6245032448115e5f73af7100e1e8c4e0b7f181e05fc8ffc36dfae45e2e8e2a8ed7876dc1e3a89ed7656f4e0bc3fa9bd4292c78949a5ee7a76eafcc3bbd5d4656bdb9c7fcc906876ad97f9508d4e9fbe9ae7a4f48b856de9fb3c5200863333ffd04be3b875f60dceb84b5b40cde25d95732bab233f8ba4d2c16a804e8d10214134e97bbb2d454eb491f650c0c090a7aee319b9bdec0ecd40420f6b36b538acc53fca7be5c5daf0fdea450630a870cdc6452310ec6b57f2008a514599a7d9126f56f00e89fdea89f68ea6c520e5b7f0bc5bc85a7886af31e3e0596de167dc5ffba8e18ddd3840347e44a87fc08b2a84b75167e52e1cf5ee07ac3839aaac6b4fe54054615116abeb25ad8ab36070a59387eec97e5635cfd5dfbb78543671ecce013dccf434f6eeab990ab1f4d0ce0373009a0ae848906d4a8a982c7bc5af761596eea89b6fd99e86d8c93dc58061d8d853f379d83871bcd97e7ec1a701b729418be4eca155c8fef6dd24993ca007e3d0170f85b7c6fcf55f791eb65e0ce623901b6534f41ac19e8609417b6c304f3787df4e1ac1f79e438239eb1a831a79999594e53d285f1ab43288d706f7c0372e531778cc52d7ed7c5a412e21066a420633a9b5833d848d8222c36d8a1c20afffc2d7b9572104478a6acb97a8862dc354ca75c4c156fdf2b191f9c6c7f4f07e556452f281e4e4881bf82741f4825a5a72b4d76e2d3c77cb241cbd76f36080e7f18a874689213665b7bd3e9320c974accdf0dcc7c6dfa6c3225149c1b52470300d79db125509d4b02041c1d8371bea1da7fc470d439fc96b303d2f50f6ec90084652a7b54650ef97224b5ff10b170499b3f882abe3b7d3abefce4ca4f5d9306e8d218c24ca557646f471867fdc00f054854b958ad8c1986c34a0008561bd80a3a82f5f0a10e16104d203d675e6ccd868d5d1421eaa67a3e9099c652e9c702071ac9597127a56cda776fb436c71febf7c1bddb3f1808ce1079a0adabd8bbaf0db72d4336f8ab3d13b17759b838068224e249b75f30756628030524aa4bb4170fdefb66966910a87acbd5a691f816845f9b2ea6e5a9ad542d648e32c5b8fa320673af1c285ecd2f55cfed522100c49f4c4fd1d69987843880c2d15bcfe772e50f5d1e180da02dd32db19a8170b8104a861a5f202b3b522dfffac1d764d62d9a0338d7582aa2bb958e3bad1c04cb7411d83e37437debb1567b1ba25025b55d0d0b4899e07c03d9e68d702c0aec357a60b978aeac603a9e84fd976f226f4bb38e0b9685eef8c5140742b68cf3a950e1a4189ac7229b472638c251e5afc3d15ce706213ddf6a55aa389635ea9c056fd17a2980515513f7fd28b4cad47d7c63c79a07e2f42469c5b935dc0d7ffcfcb2bd2a7225dd120d2f70597c031e04a07bb45e0f768dc6a33aa1e73f83d5354c30fdf597d25fd34cabee8ff5542d78252fa8930d480d7a1608dedef03a7b9e95229a795c3df6eb2e9bd68105c1c61b664ac5aa046d93b76484bb3e3f9e538073227ac361323f3322277da1014795be04fea120013fb240a847b85ee151c9279d1f180363eefbe2bcdb5ff584c771415170553c0385207f73eb06009dbb97817921a9383f3f5f720f9bc843d0d281bf3e9f4bdf0bcdddae8876ef11ee27a075080d61ca5905926e1593d604fc7fcb22811a1689043509f03d36952b258dbb59defb86c738ae130a93986f67551b6c79414cb91d0ebcdc68fd52095c8cec22ab7dec3a1870962e458f0e7efbf3bf0b5fbf973f470644bfba1e384150d94c641a6ec7cf69c4cb2930b5e05b6c2f477db8c29eb97808a0d77f369986997ba8a5d97ade17e6c162dcbb23852b577a54e887b85468b6069504f0eecf2a986153a7311477c41b7a8a069bfb13a835b6a94d5fa77a5b6f065c3cbd3387ea6dead9ca6bf7915f7dd62f590c5bbc3eeb7ed7af97ba78c022038febc5f7baae0e69b531f50d19244da041505f6086c3dc6cd13c271fb2e43b0ef30dcfbf2bcd30fe6d7125f7ae5d0b3e4dd6d7342e3831d28f026bbbb8e5860ea8e8ed5c371417c516ab254080822e2e50ec23b89f5f066d3233df87ece4df0ff98983bdadf948e978708465c67a1fc79e230b417f10e0b55ee77fa41e92420140f1ed9fec45db6112ad6ad5ac636fa74d530f3193c7a7b4921e6bb011335c0e0d86d8c5ed372fb5e4c2165f06b222a5fa3583f680dd460e777c64a532ae300ab8b34bbd1b199ce83629a5dca0f7c728393a5d5ae911c2f46e56e8a9323dbb0e3fa5448564cc3ea65aa0b7e09694fd2575b88d4287ac4993c9ef2a9a00c25d0d70792657f80dae7e40dbb55f98822ef1531e45be1e99ef373eaad6d937e3210b4215ea5a5e4a5df5b92cd4076caa71dc982bcc7b3e89bc3bee21706d68b6a902cff401c59571c0ac96c4ad3bb1efaf877b5948c4c72c69a27fae18de4bfd5d0845e25a5ab20770a0d88b86c2066717e36ebe784dee9d4a75dc49a4fff922f8064d867aa0dcfdf0038d7d116f538a4ad9513a8edd00cc92de0eec1bd28fd928058c8dc229097602f48ab5f31bd654b8651a83d593ca78ad28f789e99edf370c0b5ce2aa2d8a703a79f329de15455cb7c6d3a6bac8c1529fd3723c15c8be26c70056fdc4986bde9d498009951acf3a1387ba7cc1796ecfd14dd114d5694ca3d008e319770e24244f68513fb8bc8b6d31fc60ba25ee0789abda5fc3b7f8dbcf4f062f50cfcfca7aab192331d28223df5232e8b7e3b8615ef4522c310e2580550c01aa5e42e98ac82a7c8047c9b629eb7b6f8622cac8161731a19e08b263d3b6c5088b720dbb8b01146da363668cf54c46bed3a40eb71509ad4ae2a28f90d5fa650c8be3b72fc5790388f7c4509b0a2b8e542f40456f761fbcf62f45066a9de538084fa12e3b4185d0f518b11b288e360d96da3a229fa9952ddb864be9fb829efd0f4c8e2f5f2b50ba1c6e21a397c41f370239e1e00f79b924befa953266e9f5110976a82201775f8ac52168e80bdb503b48bd62a5eccdb4af8c885682cdf971ff0936a4e49a4fb1b3250cff10032c1aff386facef21aa73d605051278afcd4ce20be6190dfd81f5ef967dcfe414e3bd68fe69ebc7c81a721f1bb7ded81c7cafd10b1d584eec804334e1f73eebe58ecb2737fc615e7c068db937443969277c3e540b654e595e259057f6e626c453a63eb44ca3f82725fd69993f0af15306054bd204e9938a9c87457f704c75560853871f096438e3d04276e46bade589fef2bb57091ed2e07196cd4020167483b5cb857277329a9677eeee620459ed02ac73b46b05c8df1491b4732316f2fe72ee825c7b5b247f033c6f5196bd3e289ffd0124240bf836daa59f70a4468e7743bfc70e68b9636d27da2c18511eb04c8cbbc1472308f243d0fe5e65437d55d5730a55e4bbdcd6dc6540ade1dd39e2867e4ccbce5006d3715e0dd41df965a2362822a4e2a49650188826e9ea4a82d002bcbe9416550480d40be35c8d6ce85b0dd294669c40ea8e05b420b5342073629563d2b260ca0dffd20dfba7aaef7f1331c09feaf5677aaaf24f4f99058468012a3c03e3daf90b74b1d74ae9ab75ca2c5568650a3d42a6644ee4324eb630a04cae8f419618150b6fb4da9c5b1dda2645aaa3c7e3fe2e40f3ff2782700a7dd3614150c4fcad7a02c5812979a55e8e0ad39d3b37998930714ea204bf697d71e1089ab0e51772b50026a22763a04e90341671d152491dd180ace16b2e5fcc373db4486a4396835f0efb089d78c6997a58a01542e0ca88e1479c229e182259a44634435fb604f43705ed1cc5a9d734b8d44fa807b1bc7d6383d8b01c2abb773850a861004c596cce01a48969bccaa83b806bad62d56a1ee4a3156ef7fa2c2e5cf3ffdfba300d667e0ac47911e1d8031623aa8772613068db97cc1c3982b2cc674791a4d8962cdb930e875f38ad80333bfaae0fa469fb6e168aa8306f5d6074fd46356690b3a9b80202ddd5455803ca0efb35db548a56e9c39c8fba8a1a8843cc6e8fb4e2d2924ae702c6fd98bae574bde383394cb66e8b665ae2059ba0f99cde8def62a22512eb2d07515b474a88a45ffeb6292236c4548b48a5d6da53ab3165ead18ef40f0ead3f0bfd7994bb1a830615b0c6b6314391df0d2af444a57c45b936e4268711b94f160eca1cc2a1f7471c0d7517d1d48efd3650bbc6ac3664383bdd6ec490cf24e3d106d00f131c61abe12f38f2af097625602c2215254c432f192475490e4d02fc3d0acbe3ec599554bee6a5c3f95f6aa821a55bda26442804273fd6fdacd453e23b0c87c129b0169a831cf82955e5c09aad3d1bb3101522cba70f5f85a00c3983e805ecfaa3f83cc547b313906ea555fb93127277c5846bae7953d8c7d3a57a26e20af3ea4d79fb655dcae31f407568e02e456e1776a2c01fa08b15383741cbd827094e61a3a2fb8d3cf0e7e8cccc670f896b38030f46be4837071cb9b5c44fc7130555599b1b98fb4edcd0d47ebc12cb4d3ce34c104a5b5dac0edac540a5a4744100d22e3c196d49724ad3299f19c8a60f9a66f3c41640afdbd88ef922e74bea80062a9218fe514e816a531d1934c36511bd27f99c9accc03ecd3cdfd58db1867c0dad3d051e6c3dba025f913ccab4b55ff7c5a518a256d44aef9e9b75fa8c2da80e3a69225c4379c8b9773c4936f2fb3c32a6085c6b5aa4de7b8fbef2bd4c760e0d315430bff01a85bb33519146164398d6f9bda59b12701865d35e5d0ee20ba90c5a9b299ce86d1abf9016f19e48e3a676bb7fe271593363540cb3d7bfaa569204b1b53004083f17021b025f6b17456143033572d4bb909dd7dd38d2e9138940091559acf7928e3c0b3845263d8854b4de7b7be558cc6a9d04aa13e11d175b8b049c3fb596c50130a3bc63833c0f1e52aa22fa18d0f4e9e616432fd32ac4ff3102ad055febd72bbbe9ad361f83fc85738cca8dff48a28a523ee4e603c05e2aaf089b0f8979051ca146e993ebed6898b2aac209e95a2fa2cf268a64461165c1df068802e10faac50d79429760889dd0813fe685ff056d14532d1f095a533201ee04c7c0d371ade3fdf518066c1b31a5eed48f8704ae9787e663fda5cd4b46366c04779f6973ad8a9c82b8272b06a42ecf29bfcd153fa80cafc8f0c0dfe3e9b3c707664f095a70c75fb7ee26a15e79d16a9f9600567998f55f3aa075f9d22a17ac0356117f9de5f6bbdfbcc31cebefb3415c2067f90b44367672c60c3b127d041d0deb7a7a2ba97f3df5259ec4ecc1917300e5f598e131c487bc9cea4ff7e0466e0cdf189d4dd33ddcd7028d9e27ab6c2713170bdf3a72a123c2f2b1611be7d45d02cee03ee3cae1178877a2e9bbc6843e7353c5559109db30268e55fa72efde5d04a9890ad26e4ab33af1f7713dab433f267befb2bd919ab9fead92d2ad809716088ce0971a719ae66fcc0d0574d31b6d6204bbf3c2df4cae72112f36cf13456c0ebfad3fef73b2493694710817f65c259e0d03a7314a7f9654bf28e36544cf2d00b7bebd4d9a0645a449fbd3552482b76ae2c128d22efcd3062c05ee8c75cd0a0a2df6c7ee7e5fabd45e6855e4ad39c189352ad7d0ef7e122e7d4b3f12cd176907e866a0d244ac3481c5ea3a726033a616396001d99d94e3e5e4b1fc99629a100b7ff7aadb33029f86e4a073ad0d0f426a520c9d8ab039885e8410a752855f7805289f7906108dfda3ac93b84802c03c0a84f2e162b73a271f57cc7c6ee0fe3800e8c3111f0e4f78c5ff10a5ff51fcb15ade01af29038e57ec3eb9b911daa0d707938daee5d4bd04fe4f861cacb35cc3c2546b483df9f32d270066c4faa804b406a004c114c82ed13c03895391d3f29e05c9bb694c8847bf254cc98d89c8e31b0eab4886029d8f9c3e7076b883661380002b44c27e6930d8ab56ceaac33cb6950bf880c469a44a4b5735b2f2ebab335ca3fc37fb9ffb9ff21f0ef84d4ef584650abad6738e98b6db23816d657d5f9cd2d584410dc8c08f13cd9935b9019c61b3087d46c2047bc8704dd84920665b6e3dee91f40deb75763ff274cb81396894b70a31ffeedc417d4b6585e14d5457b677c0cd9c5ba3c74404c5a3175916d1655f010ba50ab830eded305229eb7c76b3822786938fe28b1784e79124b3b2f14e8803e8f1c2652c6336fbc284ccf3e5ed889ed5a8806fe8c73a0e63e155cc3a37290f971f62c35b57bbf7f5bf84b1aab1f08db8e767258a94413b78f18c554298360e625a8b8bbfede1f0d2c0c3ea58e758f74d13c8918591c9f649de01f3cb99440645ec19e49f1639a164c7675083f898739ab0416884089448999517b56e480601ce370137e604d7e10509ef49caeea3578900751f5eac2fd8d0a485dafbb8db0ba531fc4b6d2ec020f16865e72a2a8bb5759fb2101f8b97d591696da4a5eae80b7627edc0037fcd76fcadc157c6d3275696a5766b8cc4a556eb656fdb9969ff065a7f354ebfe37a8013b8b46f7dfb38c5ce1e5f7859fc6cd3af39b850205865085f734bbaa1191cfaebee22e7886f286eef0051d4463c3d741181869338b228045a83ce63adc363a3908a40f1c21d5ee35e85448c7b219578e35cb88105e6800c9daf9010861a4c239d240cdc8f3907daa46d45c81c3d5f3e64bfd7394d2ffa04b3468ad103f38ae0ce037c95ae97de4f49336cc4ae932771c681fe7af876220a0442e97e59aefc5451a7561c549b7c80c7707f83ca3ec8989b778600bdf9e507f42877cdba8694c26d907bfc785527d8c0db3a23e120791553b975e21931ae0503dd3d195e8f99394395c9f6addd3a5093ec4fb5f4738af24e4fd0ed97374066a0e36a9e585d3039ea47ac89318f99c36fab547c30f4f9378f8a307526d72403553c65acb8a89ac3f40294092ecbcf34d717923114c2ce6c02de81aa0918b60e5f2c2f7e43ff7e8f21a5546792bea81cf71577e4bd800c71d188af90f5015c04a9d868ad2ff71db7850c9d3bc1c2ad1c6a4c355f7c762d186f6d692346acab087b476a8bb3ea1fa6304d999705d0c11795619a081dd770ceaf6381f983739a0e11b467a42d89ae72ff3d48aaf9e81c952d8bef7f2f05818a45e5c08442c32a0d79dd0d0b0af62eadf3f41cb9329524d6f9a86431667108c5fc2e2220191ba00164c3c1f0b973020cd0757fd56adfb8177dd31f7f7aba1cdeb674c93debbc740bf0ebcc9898ffa12da707b0a2cc5c132fe2e080baebab163a377ce75e27fa9802255042ec3dcba47acea2cd9331d45f4416992759526024c928f7d370946b980cde2d998f496ef633af8abf65eb9388e2144d469f22817633e866b3beaa59c103295cfe8e4cc9a1739096e3b866cf0e6b566738126d14cbe3fe9ab354eb7ed401019da35193cc9ed5af8ee3fef304ea52fb086a749fd0219059ecddfd6478320ac617a657aa51b2180e8650b7dae6496d83ace3e15c3095be0001fa24f65e7307f308d4ddf0e998e9340b63cdef5de62d028d96e6e0080651ef79fdb522d1fe00ea12de28c7c5e5a6a4a2db154951dd64fbe912acfb034f6fa2a05b3abfeb6e0eb64a8f29fcd05df46b21ccd12fe4a174d73d2554b3e29096d2da348c5f81fa0798b09b0bc6bc2d4a731a1b27037622c5386d206080c2e9b99ad165135c7d360c false +check_ring_signature 0dae4f7a81e7f9cf292054bbe21a950338aa4b6414a526d727cb595ce4b4d7e0 b6322c2edb6f0607fc1ce8a5be440b79b02b1e9066110fa083aea0f508fcc7ca 2 d0c6eed2d42270da2d626a29ed2117548f9e04376f3326df7de0483e8b2996ba e83ea107361d84aaae2458a4cae178554fe371df3f1c56b6b7cfc215b933d3c4 602418fba8c2b4f2568a3c5d885c4639daf9707fa9d75afe2b98b07b4df07b9a245dfb5e6c135392c860af582e8ff4b1e029cf804630749cdaeb6901e0245808a860730d102787ed43118451448c965f8783adefd1f857e29157e6fa2b2971099cd6bf85a91fc89b5e0d7b8187a6b721c225985d9d68a0d0d1d7ccdfb61e9001 false +check_ring_signature 057b234f869ad1ec297d9761778bc20ca7440bb9af3945460b70df61f16532c6 d8c452fdb9c7b16a0d7f6d6e497cd04158e8ff8ff7fb71f66768d5e389478742 54 47d67a1ae20c94bb3bb5f63f8d788c2b01cccdedcb8f9ee96dc4f110680a4ee6 c64a200d6b36b51face69b3cabf43da19972c6c095455fe80e177b4db6281dd1 2a99b8e15bf42902c434338b9c6808a49b4cbc0bf833bd6a2d58cfbaa3413622 9b65a3d7331165f1fc3bf8146561fbeac8508565398db763fe29ed298c7dfe7f e17203ac76ba1750eb30658b60fff0e81011bf5257a890d0581a675d6ea617e6 4a2c93b196fd87ec6a74814a289a5cbb3324ceb1e6ba4c60033cbb191ec44bb3 b42231c13144b7f92d623a6c0d40b65c0ee1d435e4cfe0684bea2f5a62b99360 e94573ee94bf50817e60e857c2bb2beaf8ed7e6418719b0af2a8d59460c556d2 d0a0e752cd07e9d864a4db83c5b35aa9b0a5ba7b01dab350fdc2461e4e909ee7 dab3fd9a7340d20db88d20249ceb9a77f452da753a432fe626b85350f70c74d6 393e16e920387599d4acadc7ebc552fdf981920a64207b5b434e1e20a0d30c34 40f4a0cd2c6ed19e265ebe9cdb2a977740ae8c8cd373a98176ec881e12fddf4e faca569868bea16966b526df8446995dc95ba6205e62463ce3ad0581ab9b41ae c54f0ab5bf6b31c3045be4991a223452728482bcea83a622f075e82f99a7c490 9ef573ff0015808413a06a39abfd70020e80e579bb51c394966b75628fa58452 89334c3e56b67e7fa29ce913220c6ad77c597acfd9676b91f965c519274e368e b726cffd7a430e3aa3d9e732bbb9968b934161e6599be97980de16773a1958fb ec2da00e3e98a764108981e36020d8556a946f939ffc5df04607142ceef1e570 6a2afb9c19fc7483dd49a7617338c44548feecb760a8cfdb4e6ad65d095c04a9 6c9e32d879f740af1f46971904e1af6efcfaf2f029162c6083be9a302f14b594 a85d46771064b43a70d55c64c369caecc0b65f95666f20aca522f47ef0f95673 8c10a63e2e6693558e6cf915d1dfc47d08bd6b799516b259a07dc59f7865f6c9 fb5b560682e7eb76a0e650ce99878a2f6c8fe077ff61d35cf6e99ac7faf7835e 9eb9bb33ec05e527e02027d87ec71f9ea8aa3e7328e5c811ba48102b44e17294 5c1e061e2b06945c95d6d4b158bdc9511605f5993c4500691d57f6bc2bd4df00 47805f039a619687a0bcbffdeafa5454fcfa441fdc959580da92493de0e5dfda eab19c10bf0ed24339758f298967ed7889931cef66a981f5f1593831cef8420c 88cd16aeba4c37a2cd3363dea2d7dcb4bb73bd39e4c31527004f2dbe537fdee6 5e49160ba0e297659ba00ab4b7a86f87afc62d955fdbb87f2b8751d6dbf01367 a0f301f37222cbb153b1898761f3b83eeef44092272168559713066c36c61086 39b4a0d640b01e06df80ebf97cfcfdfa0004fc223a7e49c3a0c4be1eb47bf8c5 38d456cafc859dca9e86478031ca425bc3ee170030b59c83536770795957bba4 694656cf336721726b52f4876789e3aae9794b37d3e915f0e2f53ed68f12dee3 638ffc01d9153b745b286feb3ce33ce31ca7a46b0058fd7390ce322ae8b1c796 55d1db912ad59acfc33cfe9223bed50a7afa533696d6a429557eafec059cb045 fa1077e5300786ef4d3904d4c4824e1ceadfb097a4a16e89f174efb0c8cef557 2dbe17a10c1c87d43f0f4511805a1870b06242ad8ee702f59e8d539957883d33 dba5239eccbbb043ee1eff24e8f1a0ae9876845876591d0498f05d4a10e562de d090d541f91ac149ca0b788f3d2fbe778a079d1a8b3175a8c225b8bcdb2baac1 c5f5d272c4a69f8115db54500f6c23e37e667506099a8efc5aa3a29d9b022682 26202611f8fa85592937c948ed6a95e59367d559222c517749e42a55aec7562b 7898db966b3f2d1d15075b1e336b1196a6a7a5e4883ff9060cb4455b518355cd 6ff9f54c42930312bbc28cdaa226e13d5d75ecfa869456e2a08b5b243d7458ec 92199e89e1cf32f527a955359c646a40090b6fa7323a43b73513482a7234f145 c5b199c941e0bfa43c7d5a2ce2820ed7ccdc19bcc7fe032ed71d8fd3b588a798 6fecacf55e1187059b43a2ec8ba2856769876d263ed2f33b8b97434db7cdcb75 8ca456fa7c2c6337c633e82709d232a9adc5e561445d367179f85cfeb34675c5 502017fe8e30004316c0f99fd45b138d5ca920bfeb005351c9c2285b509bad2d 7802d9313d69253d794875214f5d80b60d65562306115f569a01975805e26bdd f3933a4a339e954cd2dd4ea74951dc4f28545706cdfaa2885961cfa81ea609dc 439a54e84f1b766fb288286622d7e50b1c8f479a50a660dce55bc287eac0c76f 523538e74c9e7d0942c14b4b615a4c57f9c21da34bbe8d1ff6505cc02913fd12 e4217bb6b83f2d14971f6935ea7aa10d5f10ee9f117307cb33500fbe0852c97e fc08eb4402107a3a2fbe05137b958d2eba6c840fcbfe72c3daef5af3e277ec38 9700b39d195b21654c4d6227dbc09aa91ba548b113e9a1690973857f238ee807826bab7dbe84f092db5c4f3b68e5ab534d5727479aec611568b1c6ffa459a40847d7e823540bf866523775e673a5bc3e056b481110f6d55c5a811f0cba06310adcfe5e1238ed203ac663740eecead17c0a7c48dbd42188b888ac330a65b0ef028dc8b88f57920eb6cc404006764979c30b66a8b8a4831c49d1b6531396df17061ad4297fe9857e4f6d4d9f80f4d8f5a88bef60e5d3d6db1d155a4a6bf4d4d2085227eea10abfc89c21f988e6ea63b6b6b809364855663e2081d1cca92c8460003c8f1e92bad667e00386c78b07f1da0884076912ca4d80778dda3eefe5edf80e15bd18e15c53f62d005fcc52314ca34ab2163bdf4fdce340bd28a2f89ebfaa01f70443717d866234f547e0f1c350ec0af29c8717d0ea2b2a72cdbe6feaf8ea0a3bbd34ce15721e3c8fccc90488df28dfd5e61679797f7ce4e54dafb6b55f0c00ee12e248bad6e3227b0acc56732b9fad20ae3216a19a87d8bfc149cb43f64306ae4334a9b257b6eaa17ca389b8347b668e03c9770644b415ff594a92c5bd0501cdc61a395776e873bbf6c6f03308dac6f56ce2a7fa63df399c8ce48e4a0dec09b5ca47b51f4384aa5c53267c76b63bd6fe19117bc368eea1f7e4828b9af197065d3ad4e4c3f958260703f5d4b6bafd1e7554874dfda425802409966ea5d0be09a5b07fe913e6d58888ed6ef3fc88cd08abc4d12553f3784fe18cafaca856a806cadad0abecf5981846b70cb69ab0807db08807ff799b0a91b06e7f43969b6309857e00d03a555cdfbbac1ac40f9bf492fa113b9cdbdd1a0dd19ca5d27033c2043c262e7562866f774e303d93437a51850fb539d6f216ed2663ba75404929a9065eee7783221e871c331079a31c09726b654d7efa4623e73fba7a529dafa5ba01025afaa106adf027d962c1e9967aa09c942b24e0fabd083ba98e17e703ff2a09ba840ccf8569d9f8dfe0dcc720a2ea6c7a954fcab09263dca094c90b8116fa09500379b2a896aba8cdc09a55ff114fcf89a9c79cb6ad46e0718448f5c8e8ed0f619b6a0027c0df070a206b7e4456b5bd9ca9fff70c8c7285f08fbebde0681309889ee569eda14b34a677fa9977c6ea23b3298935f6b769310262299a2f6a8c0791ff4edef9e167e09e676a273441902dbafebd254a6846817c50dc823826690591646b2e3265a8d2f3267514bd9b76732193f81596b20b20230ac5c8b10b5204836c79c48e8d227e28cba33146e917d5e0cdddec0dc9ca5bd77f9eba87b3350469ea93154f4a0c60c40d0712d7f7b793e3507c53b6136ee03f271768da74b8094358a852d703a602509b568998f623408eae5f49dc778785abe2b2848e15ba092b8431ddb94b057ec51da2bd84690593ed35999003fbb412fd76f2810e572e0eb28e9d03ca25ee59514ceffb0538fbe98722f0ddf8571caeaaad2e1f27532f0553dca8f8e708208a606636cb9af3a54636f0023131cc77bf1a63291e616ec30b348d563c03087e2ca6fa395fe1aa3ebb18a5aa4b64486c43fc80a750511bb101ed96356afb005fd0168da5f1f33c4d8de8d047e7197399e432b1ad4a6894b10be5fc4b541378428c18c3f221a006c955f69cc9d6b56ece7fa7a0f9e557ecb10028b2320d7b6deb4562507002d0bb08e6575dc44ab474ddfe33b7d9e6c5e2ad07c62b0eebc540bb3cd07acc676906f22efb665fa09e786ffd7c9326556370e504e625bc4f94e573059e5b726b5703726f7844bc502209b673276fdb421f32bd09c7a85029080aef18f0a6cad08febb6e9cb539820213b1aac4cc78fc941a6bc0cbea4025e987678331edd83f8f235fe026b7b21df7bb71814532ed7bb3fde6b05f2a7a6d3f48bd07b99e61c94a044203d2d3986a5fc333713528e4cf43daee20940b4fb3ec56ba82f2882fe9447705a662bba576a9776e208565a5af7112bf50e974011f75b9e30b9c9a5d145aa883898185ab2b8205a88647f020d8e2da5c803f717682b7a04557fb770971136319b8df05de3d08434b8ca1866868f0bd8580f356d5984f83ed85334737c4890fc409b8229b9437ff89f24d1299639bb78200fcc9b12f9caf0fcab238d831693f020fe6b2c5087c31788f18caf8476a10638002a624436d008df91dc85b33e7ab6645f58844cc15fbf5f14c94489452315b901ec3e481df4e8289bc1744517058728756e04ef9cf4fcccb939a80d7bb98f3d012d4a60e100251bea74a3eb3e8e0d963f3525ad3ffd993fe4736a5ba811f01a04ca6bdb188af65261973b5b2f3a826f8e542933c380eced328aead8f647008f07edafc4395c8f9f16edc3205bb303792ca34b133589f71de5630fe86a2d8c810a6614f7877f2943750e64af3bbe5a7c36d6734df3c5e38ea11cdf2955c0e3e30d4e6f3814346d2527ca0b69b7ecb67f77680aee1dea379accbda127124e86d5048ac39acc027180a4ccd6cd4bc1b8372e6d7f1155a399b626ea7ef9ddf1cdb90beb8911b78d927705ce3be0d59225d36b07fff986ae4b0f53fbcf380ed024a000751e81c562470f825a8154be186fd657bd483e502ded79f9cb8704d4dbc2270e351a93e50ac4aa70aa5e1db16ae75b4a6d98533d6bd4d50fcc23c31ce0e5510ab766f486e36928d2367982e6a363b21062e3c3380987da18192f40f705017c0d3088c333ef7f1260511f431c9f1c2acd3be6d34added152636990c515c68240549907a18c308245a256d48481ff7a2254cc8ff6a092fa1d6eacd92eb41c8750fa1bf57ec20cea48401165c091d140e35b6cd53a178dbb68199cd053ddd538b0232debd2f6d117b16dc1d9b8d525e21c2bfd27d96a29fc11f20ae679bd9d68800ffd156e1a3e51cce3c28653646571f597dbfdac5b8ee32f79f7799b76d3c090bfa7f0b014d473d0d2b644d695415895ba9fbd74559b0532a389f3c05a09789025aef4f15ace2103167c5e29ac8cbd5a34b0dedecf973e605b02896a93a2a5007b2ba48a9724777ebe1086e193387483703c104cb559fa6f6a585c5b1220bf80e03457c93e6a75301ba2818fc2bf90ecdfc48caef828a2c0b09d82ff34e028d0537ac0d1eafbc148298669119e80848a325715bb8daec773b70b596af2ee212032242539918ff8019dfe6f023457561552e26c63f91ec1808bea238600b108e0d7f7ae27a44ec90aab7c920b0fd2b191d25b13b91e397905589466485ebd07700bb3bd5dc4b070c5715f39d675fb2f7bbcb489aeed93a56948ab7b6ea585b7204aac574c4b95053ff97fad5532cc61c9d0ca8d1bcbce3047f0e749b93d9daa403978a37ddfd245e854505682c2f43329f448cff62381e8b3a234cde9ae23f6908d7b13f83d3e21ad3dd57683d45905ae7893d784c9d3f93c32212fc6e3cd7ff01d970ce47a982ae6f8c8d2b06f55414ee6b148f26712bfcefe153d778d94fc305f20266021a82856b06f2281b48ea0477668bb3dcdfe79cfd46a3e94f43a226039dfadcf99de26100c5bc468fa0c78033311b6b660394a268846bdfa4a3b1d2080a050816345895b9854e28e2dcb3444bc8d87d23ca7c1004c741d8edac280408628d4f22a6340a3c1c6837d31f6f99b24f041a8561a10500a0d9178483fda60715255d1a21df5970ce740c72759fb6cfcb989dd149c096b3d34121f9426a5f0bdb679a46019f416525399481ccd9c637f6d41b900da39c2d64cbe401b411b904b157f4abef3fc611989f5d2e246ffbe73cc46222c9186339259c1cbe9d095e08813afef44afbd890c5e47f6e17a31a6ef0515f53ae5fa4fda560646aa5127402e593ae3844bb15701ffc4116ccb535cd43448bc0ff4b5245986684244aa5a2052a4ff888394f7ec98385a8e01881c2f55cd598d988726edd46b4a06893bc4d0244ea87027ee5147e9460f6c876c77396349d3a01c5aa179575d8d5293f2d7801189699789827ef65100490409e1122e5815390a2627b99dfadc780f1d59305080ac6106fe310feb3da2104d05305366b241d3714c6a0b74e07713d46afa6a10f449e01f16842d93d91058808f28955c0761aa9f13692d1044cb54b969841a60b46a63bb6eb16d60370fcc13c2207ffc48fb366dfbb07e35bb186ab9ce998e8009a9b60dd7e45d1f3cdb67c0b9ba5b56d16fd35aa7ba6349ab851485d22ae310c97c30dc7737e18016b980a948184c217216217dd18b107fa3d6b7e14d4e996008f5bb9ec55a773bab31a31cef585616b99d3683b9d8eb832e408d5fd50b7790a9da2b2a5c296f994691833720abaa842e03dbb1a1d84bfa2010236f3863f11046793499240bc41c7afc9eb51c8c3cd5f8e5e236600a97b016823a8fcf07ce7057b994fbd156f47e572c2eca70abd9d9a814ea1eed7661c325acef5ad3c9ca90673f26eb5c39138e218f08d91ee16e158a2d7faad4f0c7fe3d371e81961293a0b5a99c3d6b86c794f36dba0f2f213483a0fa0a01dbba3e63074af7ff9dce2d30ee455152480f771d85923da60f01d077c8808f11bea077265dbd3612dee39cd0b235e8e0236c760f466aad6482e150df63a93f4c0737793d2bad29bb1389ebc9b9c388967ce19cc6a55cb2d85705f632dc93c7d0e170aec78742e0c78f79c1209afbf6148736b275f4dd292831db8bb347b75f509b392e3c389af8351215db202eb31af445b79627f396371ce4b9d40b9b526f6a25029e1d87766fe2f8b27f40e8d11bd996a0053ef9be2dfdd3230f6ba946f1a26ce61d9dd97a334b26bd0100331eff16585cb7164ff21042321c3f654464474dc9a01f06350d5dc288d7be0040146b35465d6d92c23f0915eeba5eb104e2a3343dbdec51783acb5d16ab0250e false +check_ring_signature 7bb9a9574c281b1b3e5ae79dce27de6fe9de289256b169c5f2325e1b450e3a59 bf7742d065a5d670e9da21fb12aa564e0de45096a9b4de43be9ca4aae9519fbd 1 7290f8feea10361936e1604ab52fbdc548cf43e860b5cbe7a5d90a82a36de4e6 937d925312a96f024eb070534d03d37c9ac76264d7a8fa25cd413c421a54d70e25e6bab9141e5c7d0f0e28f20f05e0cd79c92535c0fa2fdfbdc98a9e5f968307 false +check_ring_signature b5e539bd45f728f2df2f503f7dc28f7f17b2c9b57e9ce89b218918811c1c256b 5e9cbe0e6f8471d643d950a0bb38521ecfd1f8d0aec3ddfc86dfa1d5eba078df 23 65c820626d585a3b77bae7455380abd12fe3a9707a875658db79ec276615ec5c 0554c9d3ae31ae050b75ae46dfed1afaa841589a34f4788d4ae157a5030acd34 1d5b126c1c5aa568195496ad778baee1ca9cd588f714893f3e5a6df83ceccf6a b513be321f8f25206f55c54a8454e05ce7421a262687276c4bd4c3a36c8d627b 67f283d89fd31297b05f696e08ceeba42bd474d645720d22ebc2dfae16168ab1 ac3b20c15a888d50bd47bd971cf04e07dfa3cb74e18bb4a013e12ad78ac54e9d adaab7e50210074d51dfa64cb8a1e4df71c55e69b229c413c6f95e12a6946c4f 36111473900550871b23326919ed9f135a6ee47c97703acf09dd908c267d5914 998ea0471e4e1bea6a76858d43e87d7153e0e9de111718b09cc157e9e7bcc2d2 2f302bb8b2baa52a755c3d6e4220cdd0f924acd8e58c7dd75268566ae75b6082 eb3965c02953b72efaac6e8a539b78a750919c8e56a8120d4985de95fc9884aa 5b56ae46535c35bf62d9e290a756408e6b98327d3b8f5bfb133c3b7fc9ef2f01 f8306a928ca9dd18284579135c8f847b3686d5b6585e4589ca003f7785429774 7a958f572ae3d3fbd0241c3cacd6e03daffc75eac45bf9493b1571a6d9356e2c e348497d36fb467a003ed1ee54deb11cd802f9c5e99d8be5e0281a40127e2d64 a53f11c7330e009455eb874c8d2eb2704c44f9e7b28f327d3b40ff816a2495b2 e278815f81c863e6a2bd7cc4df9604427917bb4638a9a18e6126a9f4e9c7a3e1 9bf2807b6bd2fad25a459ac2f1ee2786cff101f2dbb88fbb84f3246e9604b819 84c205cbe8fa5b5d4634bc553901162480fa9df7ba798ef998237fabc672a9f3 2080d9c1d006fe43ed410bbdda9086bea7ffef497ecfd268a1f4d0ca1c70b916 f459832c82e63a0c5f66ee118b257cc391ecfcbc6ad5a532e3423e2ef3fadb49 c2f3df8adba4959dc8521ca058a64e96d1aab1fa5d18cd9e6a8643d6bde1035d e8e8ebb75d404cb415c6a5b0e003ee94251d08edd17de9a81c04aa2034a664d9 dd626337a599108af5dd142b094b1c0d77bd3fa6390d76fd8f8c916f55dcbd0250639df008ea5019e42ffbc993ffa7acfc6ce48c5edfde2b9bd249af22800d0c0d10f607fab3671acda7a6d73763cd79256b7ead240b2e6323d0fb4ec0f58c08a8b77f12bf08c498918bec9a18b1741ef2d9775e8065edd8ad5a7e15e38acb07a9e175f96e032eb4bd89487b9e992b3697f49c48a6f73f9ad3225ba87bf6c503c02374a48be3f160418d9f10d97b940dfd6eaefbd4e5394df09e23f3e0fe6c0375bebd18b78765867b2de46b8c8459006b4fe18c11b8196848cdfc761458bb00299d4acc053ae7f00eb51659366a64feb26581648fd1a27c0bf860765651dc0c27fe510f65cac2a3a82d8ce63f1ec7588e5b30f2765052ebc2501b07cfea27006567f18f6a7864eb69df7c8ef9a8fa0d03abb042e7fc3a058b4ff3a959d2cc0a32140381552564c90bfc1f053f017bb970fabd922846db177a6bf61422637b025d12ab53969faaa8fcf2ef7a3431f3a559f1207e1d79d2534f4682fb8554990d24d0c36f6b8d3572b9ab02818f29a8d85d4c22726aaaccf5c87b1207a16eed0f55b772f4b48a2b8b6bc682b8431e9d6f252a55cdc5a9ef12afc8f23488e9160203a032e35aa5798e85d514eae6e7532953d5a91348d64e79986b26186e8b570e4962d369abd433970b234c982d85a817d08559f81c11078554439b2e66d5d7089bddcb55f21bc538c737a0798846c18991baf02e9334257c0e3614a3484ac60cb5938582fde2873bc7f3ca95f8f725f855522a364fa320e42a023433f06780080d0f9e85898e05ef0d44fc35a79d8071a4accfe62a49fc117180ce9158e2e70041e78a09f240a4dc72bf3926d8e23f38ada5684040cf28e25ed781dd72c7aa0c05196223a21b331c440bfd2da8daf7e548611defc566087e80e385c1e52fac0ca7d5845b717cd0caa5b634091f82b64c8e74911d0ab111cb9452a7ab1683a4010ac69f29dcc4bff94eaaab0c8914400eb502b88eb98b51aedf7220e2ef093d0d560979d73c4eab8cef229e0498a392e760d6984e1536f6495c1d6a308d2f0e0c71addd413472048b5380c4d421a3aea97a7976c66f420be91e4d4bff032f5a009f4ced0ef10cbf1149848f79f0a414a7b007462566628564717439740105cc0a6243f5f1ea430aecd4210821cd605495199022322f3e3d9242ca994569337b055f2a3284a9e24801ba9cc1539e8aaf0b9dcb36ecb1585ca4c9f8cc9fd0942b00b2156138de93c3dc19394fe12983764b1ab1db2f97b9b8d7c49034f7e263aa08fc712cd7341f1c08a0d13341ac47821f20d4ab90a33dc9e2eb27712878c4f20aa50184973061c66d556ec4efcc7e83a6b21878bc2f4d294c5153822b83db760df09ba4ec7589045eefc22b68e0f6d8571f5694835b1194af6b19b7d5c18f830af1a5c2d7a90d1597f9a16261668b6536f45aca54f99e6baa7488b7eceef536063b19c118998f04f3eb30e8ab82440d848a29052705b5acd670f2328bba355207e6655d0d0349a18bcea86e356f18dc786cc490ef2f2111c94443251da1515804964625bc0f3bc92c43d73396e2e60310b17871804f52161c3d6b4840c86f030eafa3051a2c6a8961e93b2f163b8152b2c656df4b8ad77da4f20d1069b0bae70ab9c620a5e26dcb911c6bf28d804b8d3ffa84ad7efd086f6176a5ca30023b343c24a3c087a5c01a856ed8cb2d2bdad3109e3ea526b2ce0a97d126245cf220af0efdabfeb2cc529d61933f0424007e80cdb2ea40a6c446a372bef3b1c38bff99045cf04a2299c68754b52e7118f600833989913d7485da2776ab8d54d9adaf1902b4f8695092f2d4cc4649bb049df64b7e86626bc754db5b2192a48432a2d3440b58278d1dafd6a3253437d4acd43115a8a8b52c4756bef4979b449cc9ddea79073baa6bfe75d2ceb860f73f663a17f2de8eabf8ca210ceb2c1f5d226bfcb5ef0a3054de7d26768db9c6f3ebd4aecfd41bdb758f3986c15500e90ad20a8f06b00ab15b824827528d455caf8bcd44c53cf7206390ae3b405e181c845877f3d9f70e false +check_ring_signature 14e6d83060aacec8492e82dbf904b859cc195ac9bd7adf71394e664df384b2db 7dae131c2f84c92d80ff841eb9e8a7c9f884008b464e9b0f91aea3fc6d2fa018 1 cabe9cfe25ce3ac4d9dfdc82ed7b9c02f168c357eeec7e6d0bd7b78c773ae9e9 d2a39c5e49cc165fac1e78551cb40a991c760940614f6f7cb923d0dce815730c880953419f4c51732c94243b3d81a35aae05fd1279bb3c4686d95ff0eb0986ba false +check_ring_signature dd4a456a3d2b41edaf37196f5afb78a94019dd96de3bc0d1043eee91bcfe6c8b 4c0d614e89b17522b195f4284fc8fc1a2db68a7e25fb513366eae2d0bb50a293 10 68e157cc19fa438fffc19a94b7dae353ccc3b9a96ca79c449f04a0b3ef9be76b 0004177797b5d9fa55349157561d82c1a9454ad003fffa337a70dfea79be6e7c 01d80c30e7a60fd152a8ae2bf23be0fb02f429146130d4bb0005b5117f5491d4 4f8954b27c3e7b23f052362fecdc2068ccf901b319a05f25fb65007239ccabab e96c3c319b43f5ab0238108d069ea25d12c59bde56b86d9800abbb5de7298e26 4b28b03ac356c45b92e8e05a271530895c88be5499fff6101fa7a321fa969982 c1fa607f4243f86eeb511b259c0571696ec66b7c6d34b011adf4689d9b14ee41 e49c9644141b8ef73a9d0090d37af3e5ac1858edebdf901c8e5e1443ffc08fb4 a607e1307c89d596a4313404233831bf85cf1b1a6ab6ce3b9a95ab9d766a96f6 29fea0da7469674440c70ef1be178188b87f1dfd8091717b3c07d1259004cc26 c9caae11340b8c468a4619ac445e128471da08b12f8e2d7e1305284c000d3202720561dd236651f04d67fbed3d216a4c4a8cdc61137d81f7fcf837deddfe12060c06a88643844d8f3c262bf49e8b15ba02471db55718ebdbae6ef1b0b8232a0e176b32f06a9e9a79ba8209cfa051af076b63d8896bfc62076b0023674b51360eed0d6dd3d6205fa3a33d474abfe908cbc64d3a3464eaa6dee0c767569d5e4d0f11999b4836c61aa70d256fab91c646c793b9fec2a52250aa96568b03f6fef50debd27482a9d490fe0de49ebc4ba979a5056490eb43b506af141d570f7868310915e7e640697c0589c4de7cbd95bb4d91972fdc7a9d9acb8de8ac76d7296b7507db2c385c8ff87d5e174cfe1628299dfaab0b75942b7866c6055fbef82205950899c6f0adc3ab2cce8e3cf56aee29b9298ccaa00e470b7ca95d4b2088f8d34a04d41a6f2f68e4225f3221b25b68ec114520ddf042285ec0753513cd38ed9d71083b7a5f412b66f35760fea3970a7240b1a46edc828df57567430fd63d56745f02fc4d2e7c2f413f2fd5d69fda7ac3b220393fc6008a90be60eb7f942ce0e67106f5914611eb8107a64e9426ddf3c2c8bfb65b7b6cdfd069bae4e5a7ce43c9060cb7d7e824a7d26f4ea9a63159eaec4b1f2494ffd15a89ed39e692e4e3c0cafe0d33f15991bc5c28d27c79033ac787d0455ea974a1a36e57eb780fde9ca2a8360a4354abbbf39581f7bff382910e31697d71d83e6f32b489792346fcf55c75150779ecf0c02b7332e875f97314c3db3e89cb032b4099dd56c1279bad3bbde9e901128a92fbc9896ec954201dc7cb379496a6aab6cc8691b9625781e77936ade70d09ca69e4725240bdc23e080df0e2ce0f6f607d9a9b3b8a8df60c07a7ff850201 false +check_ring_signature 4f00dfa331a12de5474459dc250750c96be9ca7c8c136c6be7f8df9c6755f75d e3f3210f176ed7e601d947ed221081c7230a8f2b04c3dbc178dc89826e012a74 32 c0e08ce5c6e75b34cee1113c893eba1272fb3c4a20e0c9a6ccdff2ebacc8db73 41d3ea3675bb1a2fbed1d391f267bba977c24fe22d7940197a5215a993abdaa7 d68fa6b7ec213a8b13fda26e7fdfd25c8b93fa5faca436e9eb85308e74a0ba11 7161469308f7cbf02ad8fa4d198661d3bc5523eeac42ac45e358ebd30547e86e 2fe2ff25b0d1e1ec3b6f1ef3cb467edd194425dd8db1156d4fcd30041516fcd2 fe174684e93d8cdd1c0e3388b27c2170a4ca7f7b3cb879da4811efffa81d22ab 64e34b9922c9f5c6ded7a0694997d796537c24be4e1a9b086acdbed5914c4a73 055ec787fd026b283cbfffd69c3f38327e28f5ef4d8ed29e4952441e5f97600e cc674276f08a75704f434781e4fa0924cdd1c94217addf080362eb93d13469ec 5c38d1f0a12f975325da709d5b8582730a4c09d0392ce7c49e7b4d933787e830 0b768cfb6d30d75d4facb84c9fbba855cc1ef8844a7f091bb18c78ea7bb95961 b93f6ae3991c48d9a6337bb9c0552af42b3d9e23cb6d5b16a8d29e071a2f1e72 4d3061743e5e98e5514f47549fa66791fcb7649953fa577651108bc01f218b09 b92ac2ef0807f9e56c4ffe346b016e8b89aeb8c18383180eaefc52b88c5f59cc 75729784d816a3e1379a0ee3e4e5b34aa5114d6b4e391ea2be202f32d23cccbf 1deba2f16e4bef6ebc8f88b3135731c80f37afcd15ec97606900960a552bae14 1fe50fc5a1bc582b832124bb3705027d3d41d121c28f2466b0bce5da88d2cce5 3289b76f08f714829de92f545f8cc14f9563689536dd1600cf47f7e7d4db076d 923511c6df8c8c538aef7c9f988aec508495bbdbed5edda3d4cd8877fd80193b fccc3bdb532096af26b60e40187b64e996ced5567a8bc32431f7cf7eba07f3c9 fe2d5950852e71596520ddf458990f7a2d8ee799b44ab405208c3f1951c91357 a47a2307b68042a0711e1360835ddca8835152d315cab46183c22ce01d1aefbb 8cd3e003f56c6a40cb43694ff85b16984752ef16f88720eec8a176b9ebb70d79 5358e054c4e3c7b8093a5f51eaebbd1662e96efec7f89e13c077bf970b25a6b2 f8797c732bc126f39285b8c8a62ebff407c5215e58dd0d178b53ada9aa761bff 0ba25fafd73e7a0be7290cc0d8f1a99a56fa15937c125a816701ef25ad452f76 9c421c9e12958d5543153657fb321b821c645b651f88a364d40dfa82d9390b9b 9890d6e8437a737b9c0d89f9619b2677ee512c957856346db53cd6a521fc7cdd 7a6af81b1b9078f1e97b158ed7b13f16fda4c8c8988a19f7151d9d311ea9fbe9 5958023213096faac9787ba7809aefa234c373ff513d24accefca908153ca2c3 43f9a6ce3921f05eeb54b9eb7f5fc1a6e5ee0ef2a4d78b102169bcb9997be178 7ebff293d5d19efa6366d8f826684f68cdfc604f80fc6fc807420f27d7656799 5a10aa5eb2dcfe5d621c37a416dd2f0e4027770547bf5bb6e34989f646429a05e34f922a1a652fdfd66a9f332600f0e27952bb53ef6d6211060cc3341ba4bb0e542ef86a9986219174e42afe7da15b0fc05dccc98474ba0bd8e9eadde64ce20a7032ea74cd61cd5a1ba2f86f8a2fe08370f7023bf290fb708ec27b2ba9643a0607d32515432bd245b1f5a8291f5fa85d7e50f1c3b2eea0d2c82fbc53f24233042e4959ff723d0da05b8c6f263eaa5b43ed4208d0dcdc99a4893526137d1186064bb75bf1bd8b24b0b2215894cc62baeda8e0ee1dede0957cf390b2f7ff903400a8c5d5f1fd75d50254ba8f078c22b07d3838be963a94e03d55999ee5fee4b60c6f93f95f506b30b53eacc32092c93398a10d4e07441b8d328c585ae90b7f44012a9790b0f6aaa84f6fc015f9e0ad439b8ec13f75e324768fdf90da5a6468b400411ca23b080b0af46d8b8eec0a8d98d6d0ef66b688a3e50aa1f91cc23f2b0d01b812c400e30c8918449fdfba9666140c440938760b911b66e20a0bb7bc59780e117701880b62930af51d94c8f9fbbb5dde00f6b5f8a7a722bbe2b9199dea460e5accd54787b0dec3169f62e11f76fa15c8e2949eaa253ae90452e958d35deb05bb691affa0aa18e1039655d64e20bdcace2628ddf3446e8eed195b213d8c4b05f2beb7ae414bafef489d6bc4402bc2318d8bc6a11f68ac12a144e99a79ecaa0059d7b78b4d7468fe934eb48725331a4734886a709a94a89835bbb8d63a91310b862d41a8b2af0fdfc3247e24d466e2c8b8668538bef5b17d089664001f532004bd7f1c69c70d5c811deaf13be38a354d7e65f8b6f6a75da27b39ff6e078962095577cd703f8b3a6ad8cf62f5b3ee4aa2706d1d15a06085777509dcd64c7b560384987c49a0dc9f77d2b3bbdda2ff98388c9da9b9f60c6ed29fc53b130bb5550121565dc65ce894773f8573e4b520d02a88c7942a08abc3ef443b1fcdc2994d04a0495f3295436be7c2f54147671ec54ffac1e9c2302267b8a60b8bec8a98870e90ab81372d2f4f6f0b276c537635d9be75b5059091c5c5f3a57a94f625d7da0fb3fc86a90af18bb4c2ab36666338acbfea1a97641cce9bfbc16d95f0e24b2805bb746932e9635e6b479273cde76b29301aa6dfc0bea0033862b0a1fcf27f1d09918ca22aaf6aef553bf51fc624bdac99928b1d9b985aba1f88268384f569510199ca112e577d967a1d99afb4191d9f7a54365c1dbd3cdd45453093a4aec5ed073638824394d7c90aab851190a0e3489499589c39ffdd379f8597f94a840d57062b050d986f9db5215e546ee507e01b321fba081615592b35febdbefa01022806fb7010aebb242c044727efe93e8a2cd25ca91e45b78119f2b3c063297ef6a208eb470b05e23eb6229c58e2d14592ae5009a5de3d575bb4e90e6c95eea49e1a04cd99208d299feb01f545e7d6fe33a69975d83c01ef05a8ebefe178aab24a070312dfca56852052884cb8af45f462fe4f88755d2f739fdd489e0193f584ffe20841dcf2683d21c22ce2de45218c978d1825e5f5b5aef76198f5180cb0b774f507d56e329876c7b6d7d668ff24a4aca25d22af309200251dfaaa94b02faca61d0b2b4dd8cc1bb2fa890149c09449aeae806427639cf7767eb2becaddb8b46b9d07e8895e1ec5eee8afaa6934574d924406b62213e298a496743dca533c0133960fea4adeb82de84788bca52aabdc28fea554871942a459e7939e3a94a2b19c8007144f6f8cd305e685a5b2e9adf9dfdbaaca3f683fabf667596e07b38031dffb0f3377b46ab19c1926093e82d0123edcebca8ac89ee9216bb4ea6273639e8fdb044fad573ddc20e3dd0169ceafc8c2f82949ce2a9bc9d1189c3ec797f7cf8ff70d9c1217c751083e97fdab15d9d3e831522d847bc492dddb920da361e80af7290513299de7efbd9527356b6147d69b650877991ca91216b6ce4193cfd101e7370f0b248b871881a29c5df21ae246eb4cdbf85d50caa51f993d3dbd7655fcd62d054e639951feafac7aec1120b28e0438fc6051c46c464171e710daf24379da000200fada8bfd84a0dbd905cafc9eaf926be65a2f53d7d7f117124a8b0bc8a3db0d737186aae065e5cc6dba5e42402e1d755bfb5455e2af273ed1a345a07c1801046deb07e69cd1259e2f6c641eadb0a46620a5f7465f66b8c57ebaaf79644cbe0ceac659e1d08606b813e3fca930f69e33be52e24423ec5066e923554c6a6e6001160cc6e6bdd3ee26e888caaea58a3168367c0a8d0d516c97c9e84c8a6213a004db22875ea1d70364d881c4ea45870c604db88c8b27d314dbaf7423a82ec15903bc9cc878d54272f4c83e569bd2bb529137f888e8866d0b7b91b01447b03f5b0efb3d870c52211106ec63ce4bf7d5de21f860b4785000baca03e97586e5745406b8fcf3bfe94cd7dfe31828877a96542a9bdfa33ffb0f2ff49829c9bc32c236051435cf1847e8bc5821f1ef0d90b6b42fc4ecb0b0d4221df266ad6ac1d011920d9ccd550c89e0417be959f444b01688f47757f631f7b5ee9cb2cc50dfa40af00dc731e6b45498d9302ec61d9f552a64229e1e78207f96f94515c61a66f29ed405291045e8aeefc2d3a27e27a32d04fc708ef3f7accb8f7b12d59dd481f70792026f64bf4ce9e4d27a61c66bdc4e024548770d4eeefca6c89fc98f1be9b3531c01fb198e387dcc948f96ada72a7e5cbbde7f1076286c55bc0b59cf33d88faa370ff7347b5635385ba2e3a11520df9456e99fc2098babd54a613114ce8b1f1b33043d0ef5d5033f2a273a4ca1078d0fbdd59b4ffc6b0d0568cf51f7458656192e0770eea33d918fe95fe8f72b4bd6ed5361870f1deaef0d0f221fd61e0bc20ebd04 true +check_ring_signature c573c261644b1102154a08d448ca078fbaa3deea41a2735474cfc033171fc0f1 db4a5da9a4ceea1628d2935aa6f23059eeaa8a47ebacfa2d1c4df13267a14ab4 3 3a626b6c50fb454e17b317d66e3c329090ba0c01e1b7c06faec81e18cd455de0 cd91f70557cbe700d3695c90a25b230bc4cddc40a8af3b8de84123040c165d98 bcee95cb38ace613aba389bdb90d69346bacc872fbfa4bf345c8f5f9c3ef02c2 f498d436299af8a7fc69b00833de33fa128a9fa25c54e5e17bcdcdd44c520d0bdd58d77f68b9b54da4a79767af1ac3f2bc4a87e4efa1d8209d76e53ab7c435030db791f9cc18dca8bf9ce4df81a6f0d98ab0213cd2335a65384ca1b310bd6601946be1d449be49f92d3c44fb41188e0bb9bd3fbe7e7ce034a97800469bcd4704d7d5150754bda0dc970197bc8e25774bdc246ec0ad0fe84d8db89ee4403ec50ad9252d4fe9d7cb17ddf3ebc7bd8e9a7e33620ae84e831b18aa1919d6c9b2ed0d false +check_ring_signature 6c19b160cfbb85e825e9fe7bd8b84281f9ac4b751426c7eb23857567e5557e2f 278c2c585b063ab264dd46ed865c81fc142cb822ea3dddd9b49da20369f817cb 91 b2ddad0256cbe2c579e619dfcab7b99e28f238fa6cfff6c3adab5eee8a084c1d a054a347b380b2e341e25aeeb4fbc65c60dc90ae8c3bbd3b956adcb0736aa21f 02dbb8233b38a93e2e02c3c27cbc05e5196ff8bda58cb2f57e10c56d3352262a 1579c647ed4e772e5c6a6e7e394fed38f602d144e608533a82e696190f4b91c3 6f414495c83d411fac8f8336a139b934b5130cefa0c101fb4418f19994a08d04 0dabfbf16493187858aec14c87efddd00a7d958e3157a64a8b00d2c2857e862a fb140bb169b35f2a3ebeb8b3143ca19e12768831cd332e7ecd673bc3972aa6ef b91208ef692fc646b34aaf433a1ce7f768ef4f2fa7070c4787bd048a48fe9277 cf38df9e23f410bf655502bb720c235c88e0acf2c447cf5a08e4d77e9a99585f 3aa1f64c85dc17c810b86d8bfeec8b11ef69ceed89f36dfc29e43e9d9734c13f 6e06cbca75ebdf8d1455c3059c6fd5eb084c70b2db9515ed6702f24b4bfcba4f 313dbba470cfe8b66953946403a9bbb02e5d6f5ecf2774a917dacfe9e748bf98 a6bc9fa0003e7189e417d212f14c63cde4eaa905ad503a965e87f5824f814ddc 3e9ea2866451a0ae3ee3439a56febceadd902e4b772acaec3182df33329d61f4 3441a05a4a380dc9a15afe4292e521f210171081b1d36f3837f0d1860734dfd3 d9cdf7a02a96a9d98b4519ed8e2e3e4d8d9e248e27fbb7d78b46962dc4075c52 7887bef82497ecbeb25b444c834639c8ccaba2126dca48ba7fa522726d428a9a c9c61b779aa3c935ea0eb9264ee3fd3ce4fbb3fa65869b344cc2ae6a894970c9 fa46fb59eb0ba9dc9b70b92079a1786a1b8c127573e895071f24d8a7ce924cce a8721a34a1b5b9258074abd7a7ac930bfb3192011c029c3fe468960e745f7a8c d299d05340f415a99b6bc0a399fa993929920d615638e9fe338770f6ce80c825 507655f5c874f2bb310b14c2f1f136ad10fda811b43b77c577c83e1e4a588e7c 8aa8ac4732e64cb12bff8784c8bf29886d2737d4fc2d8d57c9372fa92e4e9249 d0fc22b06adf1f87f0053539070664c3117ea48e0d1ee20a8575e83cdcc7a24f e2d572216a568cfdf9d38a7dc184fda86a14b27ecf1bc885cbe8b2c19f27bd19 514311806e985530562a21f956d6243b5d85b10c40ffce0d8e6dbc40eaff1dd2 d97b10c44fc3778e6c569462f8399b043b81609a45b8c1b899870cd3a2d6eb70 eeefb5e3094ed0e947b5fe16480d5d8b33a1f0e8343cd280732b387c0085cc59 dd181dc2fbe01f5a355e6b544579d6b6c99bb6b503c4308e19cdd85b06cae026 4e467f8c63b3ab92eea1ba8c9992c08202ceead6932e9455415bf13152c69f36 2e343629a9afa51554706125d461068f5c427673db437443c68eec32e8f7f693 2969ad8ba07546008a6eb6c6c1133e356c0c1b94e4ad6396916de037cae6386b 8ed241109b17862648f6f8a30e0e362c427b08d55dab21caa331a45f901257d4 1b4b7a4457e313ab6eb7fab8e8828c3b3ceaaea8953389055d46efa0943be338 aa025d15a10e964273cd32857b9626a3ccdea5aed37cf487cea1d4e0c58347f1 36fbfd863b1a376fa374383a7e6d29cb00304180acd550272ec380de98a3d7f2 3c5df893b66dada2c0787551a7af6e12ae04936efeb657bcabbfacfbcdfd9d56 5e94729006b80b670917dc64100d858c6707faa7c5d060b278fcdd9ca265990d a8d26b1758d0cfae965db6f35e3b52a6409b6b2f05b4d0de3d1a2ab6db26d886 e8b8899b728b1e68b1fc4522664e67d199f1fcc9f07f55defb6c87b1a514b036 c504881bf9ea30baf82b4d784a0fbb73034aafec72d351dd54c8d650fa732fc2 de065c72dafde6af253796c590348097488023591cab15c6004afe93b0078c88 34f5cb24f44a36eca09b249f25d6b80683db587892e7e2a62d1a3e197da05db9 100384d2e9b887c1bf86e22bf56861336e81fd2627b05aefc2cd306fb2b435b0 0564be8cff1c5afc227ab433f3c9f3a32f56d2621aa489605d248ed89642aca0 bb34c86d2c7327bb6f72c70d8e81c2acbac408dd4454e3978aa7caf52a2301b2 911ec012413a35514d7e96aba906b12d4fe404d143a4fb7008e2ee8283412002 7f7958668ebfad5810ac28a16651030831633513caf400f40025fc348f9fb636 6d281f2aecc57300032ad236d6714847c97c0242d17bbf82c9d79c24ddee88b0 ac1605a691d207196b330afaccf955f5dcc3877c1179e57d45240128649a4f3f 0928bc155333aa1efd1fe777bccded58d35239af3aaa2e4e05d5b9d6f1df64bd e564a7e9bd521c6ebcf87f051d6f3b1861e26bf80e1477e2f043096a954f340e 4db766ef21cb74bb08a7ce52ab8941d361fcc691a20389778674a7c2a0981bd0 fd0e34c03b9339da5d1e96d9f52acbb586d7b3f46f6aa35ee5955074ae9577fd e68eef03e67a39a024ee3d7af730d263ee3c324e33821fcc0e913e85cb9e616f aee40a7da5c579393af23a37322d540bb4e99f634b0f0595f419b66704490cb2 7875e7944872db2c9c73882afd292320785cf4e73e1edf7e28a805663157cd59 8129d1cfe13ed88c880092d850abba48eec0f72c721d5fcbacc65257fbfcd7eb ec5ed15bf30b204afbf8b220f104acd9447a9e402802ff7ae1110fa2ea653b07 21dfc47b81400390b040a6059133d973a475b56554e2988318e25c874922ce35 9f90f1b6e2bcf54c2ce67c1997748f417d12377d4ca1677cab38fc4192be2204 f61e7dc6eb3a8f6362a20460854cb412c912f5c8e46e0501294acfc1c190931e 45be6a69838384437da54ffe72e0ef4ed424e5065fbfd879ad8860b7b7905b68 9a2b1df2cb070dbe3281b119418a6a638119a341f03e81b0560a725333d40807 0da9b71f4e68fc49f6fc9c341997c04be4d6386cb5be314323ae740bb274e4ba 4293a104577b46cf1546d16373bcb533a24e4bf098be33874307c4b109a858c6 f5f912c95a7861b414675640c2dd6d416d1749dbdcb524d7a571c466f8eedfec 3132edf2bb97c98ac41777c314bfb5e28b5abf9ec5a115e2ba336b6f15d21c2b 5086a2aeb1a088ddeda107a45b7e6860974a7d818077e17403e04f021224a7a1 7610eaa2462c93e9ce6194f57b6527191fe66dd74d3aa4616fc8cc6cb2aacd9d d2154c0512bffd3eba2d6aba9efc9035fa4cad3862ec2fc6fd65c590853cedf8 7e4f3d306f9421b5d343c974236499dc7036430d2fd0ceeea12019857171f747 bbffb23f9c3597490406748f3d765bd18706b6689079c020d06b6de6920d7aba 8c378a59b9361f2542257638ca303e34ac78d51ffce62c5750189d6d08d479b9 a5a4ac0871e92f2677cdab98f78aa2391503708a79d707e98548f99c5de01d8c 4d4427f9c0732e7bb01b88a8fa5c7821f8768a23c29677bc40d33e6425955ba6 0fd5f14509079d712eea033f8b38d632eb48e58d0fada4a6e54e30621cd41c5d 928d10af41003f36e829536e862a9b428f4c99ec543dce37c9f2712f02d8b58a cad8a102f84133accb8c1eecbc58fc1eaa89009d25eff28a1bd89a1dfac38a76 fc77ea9ec9fc75c506794493a57f7748d427a59e57825d394e682d1ed2e07b63 4a2e6aa7f6f011ce538bfd3aed7583c56bfb59adcda29756128a9a9602f70670 8b4398c9211b0204be10b20617663145cb935cd5141e629a80d86726f7b3babd c24beff4935720643499253ff8ebb9e6d1dba0b6bc6735c7a4cd2f2e60db179a bc039f97d4c549ca8fa2576c3188fe40b08ca96fb01afa1589cb1622a3d3003a e1d0b8264394d3e478a1e16af5a35b382e1f3b23a3fe41e7138ecd71caca2555 60a4cea8573f7d71bb62d6952d97166d03544739673c3b4c3f7f0b8fbbf17f55 bb40034b33f799de54404bf0c1aff186e0722a6e6e44aa5ba06442237e70821d 4673814f2db51dd9f2092634c9b633a44942011bd3066528e4a004c69d36f2f7 777dba8049831c8ded1b3682268221bc6013ae9aea16aecdcaf7c1987029ea3a 76325f3ce0cc626c180347da545faaa4e971c8d4ba01de8e696675f70cccc689 487e9e37266c0f8de259c843a5b27db63a098e2175b7c6b76f1225061b7dd7ce a993ab6f5bda13814170ccc73d0eef3e1c8fe0871f60d664abffbe5627b03c0c5a026c6874a7dfa4628b83c17f1c41944388eaa04720520047edf85b2f84630d2bc580a664f7c132cc18f992a4c461f68ba8be978b3da4b896b4f42e0be7240ade78d66249c0f7f2ba5fef356bbfdcdf4a7984204922b83f9abce45b95259a03a4c7a52ee17e77d63343cab3f8d5732df8166c318298e03b4f4cb6a9594b5805a1433e71ae40616c0e2994e6dc6d78b57dbea00d60a8368eeabf7d6a81a9c9096178a77c81a41bbc2be6d64cac2c45e562f0b6150b958b0c881c2f4ce4cc47098a22bd3b44dac80a8532af12d493af56c2792bd3578b5b44776d9ac839c4bd0242c404f50ff21d0d236acadc98c11ba051d4e5cac8a63a2d77a6e73ebedd810d257e6b26009a44d4577e504a459302e1c76c2162282d43bcaebbb870a80fbc06f856aac22712aefcb78a715900cb61d0d1517f50920a9fb6e8aeaaced0e72f0335e929e7ab8447ced35ae6a1cc4237df56563c8ec535cf220f1a0df4fe25b90a2e1cd869d66d0d344bc06482b2b5830c74551b7d271aa93369172d68c1ab010f7db0152b692f9a5d7640a470a2b9ea85cf1ec55dfe38722d59ed62e3780d2c042ec9d505f613ea379808dadf744268688bd0c637d78638fbd3330c2e14a4eb06479fc5b2b8b790344d58c0f4d243ee11c768c20e2ee83f845401ed7fb3c4760869d4324d3c1b90cece44dff79d29e98f7b461568c11f3c785eb9f3481b16b20055480c1032edb8620b5ad74fc8d9211085bc70ed19ededd8ddaf788174c70000a4ff3cd7874300a502d6df9b46f274f7ab8700713eb7a9bf87085c36fcf77b0fefd126808243ad5876ced87e4f7272452a8d12fe7a58ba9b79f653b81796f209bf5ef47b20d5cdaa58b55ae7f27c47221f71aa3a483a65bfe12c2c914d2ccc0014ac30b235fe91c0fad7841617bb658b31af29449c867b4ece80613f577add02ba48ba60816ed6cb7f5688d36457c63581e5ac95f4b8ce4ea7d9c6948717ee04d9f0e954889120bd4eb8a6f0cf62553d8fe07ccf3b4806cccc54d72c42b6d906853d53bb3852b6f9690ee79d679339ff563c9a4d47543ee3a1ad6c2e8951df0e61ff0c41aec263cf07f1ac63dc06c4db1c90fb7a412a4d0fa6e6105cc7fa5405c1336fc1e1ea615a0c245268a88eb7b3a5ca4bc91d5adf643a88e6e23663c60b0e17d3cadc3d84dc9041f058ff53abe1afe69d4370b02ccdc3cb57ef6ec6b9036d473ceb4c8719ff32ff66e97a10d3909ec65b36f04a079f041b968a0d52680dbc9f6396b792fc5a741ae242e4d9ecbb424d773317dbadbe3552b03dc895350b46e5db042d6ef2ef0ced0e074b1be9d8b2fb9005b46a92247cf398d344beb507bf8f7deaf4190e012d8fd6001f7fe28c95d593c7d6a817819a820aa798b58804e51176940a8277b3b6b37e2baac15ce8699a5e136e4ebb3a1946bbb274d34b0215128f2d1e53921aeb880eeaaa6b0628176c29288b9494dcb18657b2194d37043dd0fd7eff8f6ef2b274662900b3d7848b00e41050d24cda0b7071f142ed490f858874bc497e5b784ccfee80f1ea8a3b03055fc7d6e9d6b6adb65ad8e0dae00a61338ce3aeba9153918a2c2fc2626bd398e2015496c471886ea52d4ca5899207b656e8a699e5fdc6c95f298c60164a0cd6a45e08be6c5a8e99e3a02757d6e2092163f5289537cba6250aa04104574749e58a49bcd33423f0cfd6a0449454b40699373fa57df499b0287dfc98833b5d40d9805757427305a0b2fa1b22ec2afc033bfa6276fedb18a4e36731e5d2a1857f7bbe8bf2418b26c080ef015d555f0d0a0f9e6e2ef97b4e6832443d62e1382fb91566130c0f3215075b7133650645cf0aea6b603873b7bff5bb2e1efb46bd6a7d8bd356e299ce4322c29589f4cd365a007e93ca9a83640374b7328603aabb802960d1dfc955ab71bb42815d0adca7f00237315731a841734e0865028c16afa692b377d2909fd2b96e62b23ae316d5b90f9da5cbc54b654b51e312b232e54c03a10e53b7f29dc562fa12b48736b06ae30cb6e9f4309f6846ee3a584a645946414810e62ca0707da1439872d10d5b25e90bb25cf8922fa519dcb3fe99d6edeb6407604e1bafc48dee88938c7906f7ece70736ecdd2360ad8876e39c432a7bf9f54a6c4f1eb2f46215a666e8c1817fefb00e9e16619ad9b4baa4515875bc343ec85d48cfac2960d937be7b929e503191b60275f7ddbd57341746d8b0e08ba4f5c5412b262b0142df2d0e8b9d8c1ab849a2099182ccc39bbc36fd353cbb86345063f6c6453b81505f481cee4c7d0e2428ad0d645b6ce3c4565a2a7d1bf038d88590941a5fcb85cc508e34c09b700e34ee0b00bed3ddf3ecfdf5a3b983c375143c14a3e4b9fa13c7a422f2bdf630b6696880081def6da8310ad866cb3547484109af859f852d352e945c57611b51ab75b66a04ae6aece233fe5edc2894a7a07a9593a2879c7ecebf7c529f97359721fe41810912bfa513f3cb2866cf001deebabb027a882c02a3ebe4c74d9118709af9f6e30c25b0c314dd9c25fda94b1c2c35654c0c9c7739673a5f05db6cceae232bc4f90a0f5b9880f06269c4ae6b042b504e3430a17737d889a2121ad68cd5847e29110539fe335097a19362374b03500e339bc48d1d9a39a1de62940e01bdbff4f6450c1a1d814fa6ded28a8639f706df471992feb371edd15d2a04e984eedb5e58660073c29e6e6ae81300e705642e9718a98fab3f244fe3b8636f5607fdb1d258750da1cc445d8716a8cc5e50e4984c202c0dfae46304c15d3770b93e24ee7fea88048c398908a8c1a98e9cfab43368a9179a55760ce39335058fcd5aec0000748400c7d9e8ccbf42cfaba2920faf945fece9eb30b3f6276c7966edeac45493eedc0dc32511b7031324eddb96161c44e3e57f17656cd98f7f0b908536c37bda05f4008f04f0bebc46655115182063b50e6543d351cf15583addb91559f19a77e3bc073ae5898a86180f576e1577d5827c9fbb0220d0f47c828d7541f4703242a6660858b0ba6ff15e31a2d4218b0887d108c02a0beb15e4059bff1ea36f8117857b0cd1a56f144b98d389f0ec126a5d7150e2c6325fe58e46212ea16bf98788d4080625cc256e84725b89c020a9c21dec7f3b4e75ef44e0e1548295dcf49bbdb6da08d90153b1edba2d8908fae603e25629c4d9faaf83191c8bfb68b92a429b5bcb0b8faa261fd62058ce78a671d37f8b3e227135a540ed45018d20d9b8ef84cf670d4257b9c8577f3cbbc4ad6cdccf2db1a982c2dabbd40eec585fe985c9e8f09906107e9025da6284f642b2bab34390219a86cb6e09eb6fc6fd32959e8e59ccf30d0167678064fafd2ef0cb177f1079f11410ce5a9ba2f4a5a4db38c168dc8dc1045ce45fcf2c243241a14de394a4c9439ebfe6477905cddcdf007043327e015002d13f4507976c3738e7091b96738e3eba664e71f5b5218e55960f5f0cabdb13059c727247e60636f97553d8941f77a6d699096ea481dfbae4124994b32ecb7905d0e7e802b35b95c9a4cfbdfba26dad92439241a59f5ff81d6183c4161d69cd06f9406896ba639de70d671b9e72cf540a56e0ed0181b026f4a66b7ef91f46e800f1b03b3d1bf18663c94b697ed6ca69811390442332c961f403c975658d687e00d83efb7d630d44921dcdb3effb82ef1c43e4001d83d1d9574fc8b60802f8990f93b915cf9da07a54b3fce7b87b78e3f4f0d0dfbb0d037146f96fcff44e6e21075666e578c00286a3045001fd4db329b84b12fbe0ad416727184edb4ed94ea403b8a0533ef661c3b69b21bbac43894e0cc7b4336060390e1f491669de7b80710da45ead1d4da697b2929986c711a25062cfe7ddaa3eac2362bef219ff7c1fc707798b4822a170e3eac332a6cb6b8ac6d7753af76b31fb286873663dc2c402d40b687774b1baa626ef591496c351727df2ad905987a8f2446b6c56544fd1a6bf06f7c791e28086ae58abedfc4eacc726ee956a095d93af9c8f36521fa92766b70c7e427e85477f7b92137f3d0dcddf434a7f7642ed996a85daa269b28368282607984c72b5891241ff4ad52d99bb356a97e4460ed7703131dad784bd1d8e7c42095ac91d2a162f6fcdb5c8b58f6177de97d1689e4b936c5bd52435630caabd480d5562ac54c38eb45d14dfb709769596911927d1e3808fbf242f0d581e486eb20f85d352fff1d4d1e736c310a244c726e14e86c3ef97095d946c76f17e4baf4e04234f945f9ed9a35b38da2fac46921b55016b538c2ddeb4ee52637842a22b8c018b27cfe7779cb1414f532e5a38dc008a7f209e41c8e6387ace8e6f130b5f21002126e8618a7e650072ca7dab96ad3fe19d1d34dc08f0bc5b49049aa418a9d40a28b99d5292a3aeb4d97e2a55732148c79ac99881a0a9d1f0ae674ecbfbab85001fc3483326a183f5919cab6500ce621254b9ad2d2468bc5e15d8d8c732d2d7082e9567309e610b5549e5540fcb40de7a55c5827b883b28e72f3a4ae3a42df706dd031f54021e781a7a7538734e11405f40113a8c65d355ab574fd66286b4550bd1557bf1167fe1081b9bd33acca0675b0e869e48d662bededee2861924b0e40c8481bf089e80cc940b349a316062c8d05e6bafd3712bcf0eff636627b11ffb051ed53cea3bb13545b08ef9b1f822e331ea52af26c98f776e4424a7623d29e90413671229ef24d8235e77fb0027698e050571a4128833b4cdd05278ff0f77c800cfb54247c1bcc41815d119e86fdbea82d7b9a71772bebcdf3ec1f850182adc0cf20ab44ab4957f61d77b2f9c0d27000532b42e527e7b8bf8eb981512e91c5505b67e86e8495c872e66bda5083b892f92ba41ff5df8824a8e41784208e17a6808580ef798e95296cf81271f1860e37a4cbb6f125b3d3bd7f9792c5bc994c681077c9618f88b8dbd1ef8c13bee5c4e6aafd448cfe05aff506ed44ab80b42b4c20a2f573da4e9ee9601648a45fdfd2cb2a2cf78c6cfd0fc7042575ffbe38742dc0c35673e5362e784f97c10ef67a71bf391273e6f347671f8fc009791467e245a0958e33bc57925c97267d623b773e7da38679e02e82c2b041447b1bb632f602c084b73de592677a45ea591a2bc7ce2877cbe40dec4913417640f0a3693d3d1460acabfd87bcf32dd6e095bba7c28b8c5046bd7b183e7351c19b5f62e62b68bdb029b95bb7a405f5e1836d2b17eaf2357df1910a09c1acaf76de3c2f038d7b8990af727298f77a2d718beef1d70ff2a17e26cd872ef471bc6b324a644ddc0f71608b913b2c6edcecbecef664500c55d3829763379e64137257e6a7845771375240ee687e4cd7c8ce7a9ecf7fe827c321d95a33bc48083924aac5b891f330ff63405611306b4366288d4e789b5afa9e99adcf569174ca49023d514163af04edf15068116fd796c8f1e199e81c656941010be9c1eaab734662db97921cb018bd6160982e7af8af810f26f8361a9924beb668899baa8f7b923e39f489acd4c0fd8160027a4b9842b5725b182f5c55f01c9d8acae191a313379f633430f1b4f705e550ec37caeafea3bbcc432a9c0279342105adbdcb0ea4c9f99801c843ab5cff6140e725c5785c47866247e41bdaf38b5e323794e7a4521a2e3b802689130c7fc7f01f678b3b4e8cf6e8fd57a70136267ca9205ed4d603700e08ddd3c44bdf3a83e0ec2153576db8851ca19237dc08485a8283ee72a02cbf103b9f2fb0f2e5b92fa0332cea440215abd17cf78153f9234630813e1bd5ea84564bfd50c7c17628cdd0b92be550d4606c73334abf288ccbdebb337dbcae8b002cbc706ba16179c6e00059eef1acf7cc87f9d75ea493562d9ddc5ec6acdd467790d99eb6f948d2176bc0a2d9899a015d5a5bf42246b9cd8cacd883150693a0effc5391f05a4f2394d5a0ccee10eeb7dbee2e43692e526615faa7e0622aa6fa60b826a453cc256c061e503998aa9b635da82eb2165b3db2c8f3ad1f7dfa19940befc2461b878ddb17fe10fb5a6e91f96cdcb3470c68218215e84bd9a9f0ca0f6111cc304b6c1650d8ff506585d23b418248ede57b28b2ff206f591b8d800a0da3c24703b2b99ee132fb60c5093bbcb1eaa59fb49e62dede02751be2e10910271bca443856fe9bf72c4ca0d3a88507b085ea6d878a5754f2c06481ec8227f72e7201dcaeaa6b11e7e024a067aa73e1fbf44c5d71c047437b495da67b1b83f88b0fc72a39eb2b705939d6b0384fb03c202c5f7e4db46d6302854f6c1ab2021fc0433efb7d1f0cb8a3f136401c44c8d653706205167336583d0936944e5f6b885f681dd836affd4cec097b0010107822abaf2c58c98075b96469227a2109c155bce0ae76d528d5803b5359309a5fbcdcded4e94c850d05b2c55c999ef0d1746c53e01b48092e7ae2d8f9c4e0ef1aeeb0dc8bde0871774a8b19f7b55705c68c5e5dcb333eee245b6d0955f2f0e4035f4f9b77fb22531c865845639f72ffea6b0b2318437e8300f146cee631607c00f9553e55ed15adfe12a194f7d5a8b3fc747d41065daff191aef51bce9b7097311657dc365e04340d64224ead48d7927299a5d6e64419c4b33a00b1c7359034151bf15e93b1417776dc3a31152c734c467a118534cb858d1c6eafd8536d90a0ee40d409293067033aba1582d3fd6f1b04b282b2b43f7e8c73ef93212cadd015a00b088966c7cac97e2f4f2c229860017836d8cf8a10d5ebf17c5d97edd0203e9bf1c62747457a3bf80de5baa7962ee30da4b172cfcadec23b78f95df681b02b1812e3bf3951f4fa0c651e2421eb701da81cb2e1918e71bd0f6d8b3d5288a097ce2b73d50ad756d01626b0f47b12a9c2e4b837e4c5b499b131e4d97932fbc0431fe58681239a886d71ef31f9f42121355c7f87973b9c5d53b1de14aa851370b1e48d9011a3a50f3f34284f3686f97f158fe94d54db2c7abf243fc6a38584e082c1a30b982872c7abfe82f45eb49a2ff030068e265cc664901d7cb758486a40449ea95748e8233a73f1cec1a46daa0e8c3dba640cf0f9ddd64031a1b291dc2076a2d231c2185f3b8a0fde1374568b115f939b2782894a2b9e48f707d66a4150560d39f188a6e7c33e5b7144534fd10df790211a75c959bedb438f7665dbf040c50a05b762c5cd9876a5130a1b272ae31c259046eaef9bac01acf08b7b969cd0e5eaa54194efd819087a264518d45727d516e62cae34b5e348087c2077a8c4a050fe405e9d185fbdcaec6d7fdd77960b3b770a5d858951a0fb895de3cddbff302ed1c33719c65a2d4cf96576fff427d5fb1395fc8acd48d7a217707c3b785c303778ae1bee6052773adea6dfbd3ed6ef0ad3d9606e830d142363deb3ade487307800ea7f4bf83944807edb9394b15f70f61dbfb0cb63aaa32d08c27aaaaeb0600b79b56e7faaf408cbeae82b00fd70c63b0676aedbfd8e32601a15e3c8a0ad2042f03611a26fdeec2cdda1fa45061fd36b7e4d5e263eb83e701dc010eb1684101251342b2790162e02416503e6f2fb549405fc1690052dc4d7bd622e90c5b560fd81a6884c105530e20695fd3086615e6e0e7bfbb6c74d1917927c306a36fdf07ac582b4c9c6f63304469cfacf1d9fb9f3a01cb34bb2f01fc2f4134b065947106e211f0fdd49b6d1b6fa5827089ef3ab2dd8a61ee8cb3221a38c5116e7e9c180c21df2900a583d9edaf52af6a60b2cece12487dc46ee341d115c0f6c8c97ce4073eb4a7d757477641bdbfa5ae4b7aca2c376fd388b08e25ada656c0387ecc070f8ecf3176978cc486226186725ef279eacedec4ac5fd0b44358c04d5386c0c30d8e3a002c98e0a50bd022068c292bedb3f024850a91f57305ddd67e7cf34ad70c93ec9e44de9256a0b90b885e350f75e07202154afd4e7da43dd7e5a9451a750492eb48b92cc68680f7a4c0e7a3b4b67b06745acc3b34d5d4e662269f4f57240d7a1438df729291a976928baaa23d13edc8df62514a84250e5a9647a94c88de0e3d0b0c76fa9509dae706fafcd7dba44aa400bd3f6f437352229219b73ae0a00c2dca7e00a3524cc2312085e496a31457a77a5e697a11e618e96e8b4512dccc03cb7bc183d01e54cd4db82d00bcb20d816a13300d12399b903b5d84dbf441e00ae5fca714374984a3126bd6b85520be3f1e358e898335c5e9c1e08c45643bdf07 false +check_ring_signature 27b49d0cb1600ed4a48efc1bcb573dcb0d52e5cb70000db49cbfb8a56c5d2c82 eb2c76a12f2c1919339ae5084f7c3a69f8f0062b13da30255bc3c2bc65c05664 7 061577bc3e99b21464eca0c20a1aa04019f6fced07084260c9296f088618ff9b 2ea433739437f903a659439ffb87f6d330108b0110f99a7cad0a3edae9621a55 a07c5c2fa25a7eb793d36714ac85aa1ec7c77eba6ae72b1324ff55bcea6eeda6 5f3329f65f308bee5022b59900744af1c6f0cb2618367ed643a295478b1a2fb4 bb354998a87ce81c0f348a7af57f110b06fd02ccecd12d99fbf8552f8efa8b10 d7ac7e95c18e5c1125d53b01f578c67f1e2ec5f0b6f8d48df952c0779639495a b4d146af06b0d18b19a3ea15154af773f3b6ffbb21da4e1bd97d6f666acd7e73 84c3d43e3cfd461ba3277a2ef1b8889c44cae4c65224352c05e945a3ab171309ff234f1c41c19fdca991c7d802eb743debe4b194f6a62a2c6e89acec8ba55909f174df029b6c558b5af0b9fe3331ef62d58307f07a1231ecd911bdc4f512590b1c4a8129102a8888bce8469dc6266e75fee5f29fd09c7031918002c753f1415779153462c0bfed6d61c8f17be756c83776587555dae89fd5303cde2f6f9c04086158689185b49e5287e8185814a4e14bee9f17b395f2d7e1579014ca10e8950f0629d825c106e9528b5adc2ec095dd530950e7cf4bfb9d0b0cc1088bbf5413063586b5b7c62a4a64f9fc2eb4e0368301c1f9845659fe1a3889b386b0a6e8853add4444f05499a9d8c62d61209272ca85fba47ab3bdd5eea9c333ec628dfb4308ee8a64b10afaddd363ffc78ad0c0ac908e6119abe580195b27d19724d0466c0d58e7f703bb6199fa36d30a84f307c5bf934562b08958c8238879f8da19cff40aad2405706b437a7645dc870f5555be5a0317cb8e8d757ecaa939f2018a10580a15df107b99ef4c533922c6652320d521b72fa7ec7ff130c7837209da431a4e7c8f383c60ab90fa92424662296dde95a45b13f20e765a20887f3137f43a8d310c false +check_ring_signature f89fabcc11f98db18ea4ca4a21aaee172ea3e0f9ceb12034ee8b52471887520c 302951a1cfc060125b3f419598e666af712cfc0ba7b0f952eb9d55bf4cdde342 2 2b079307a2204751d8fe8d6f15c47bd08b99ff1068c186c0795e0743690853b7 6d9aebd63c2518b2c2a2914e0333a0ba97c85bb3323321bd0f64477794b2a95b 42dd89de3b0ad84458c039b23799576ee8c5eca7a9d20be9dadbb4b6edcd930a6269ce3942cfcfc2c5f12f48c2cfcfb472749a11131430acf0200f86eb734601b6b482d2a901fbc88e6a0a57899d2d9cea374debbed4c76c66eb3315b791e30d277073a4bd89d852aa2315801aa69317d40c75fada06f457746d7f74add4e60d true +check_ring_signature 12a059fb511234ddb73b9151643091310844588d01cd9d8ea0f23788a1fb8dfc af66147bfebea2ae86943ec6c35b95294629db393b1a4624ae104db5def21963 1 1d801247f753039ddce8860ecb7412f6341a8a2304e2695bc6eee47da5a11014 b4a29fdd9cd8f8ecbd12040f68e13a75e93205b1c381050f6a6f7c4540c7be06ae9ce21921f52d738a5d632a63db2dc3b305a7e32a9f5349ee1147db78495d0a true +check_ring_signature 92874130610e408944233b58bddd1b78d9485a2871a069e523355fa26b52cfec 840a1cac58fd223a56ef05ecd9adef8e43f793e6c32932b94191da792a343d1b 5 0d68a15b821833335bd216a2600068b1fdf9d6c547510c2bf26bf71abcf449d2 13cbaa58963f48cc3441d9864aa0db4306381b650b181c9e9949608b33ccb5f7 e0a8a14abdd8602a7e1f3f4f5a5717d6ef5a3abdb7a16c05de467bbcfc5e4cb6 d53d059f0d2781beff1858f788f88272298a7c6cac611c32f17510f41743a74e 9b8d43bd5d30c09ddf5bb4c77ae8d531962b3882a511182a169dd5821d59cb23 5852cc65a462fc3dd9017b486c9507db23b5577096c81ddf8ea8859e10b2f40f5ead763064837bf120511602618f169f7c28373b95b05f99e5f7cd1c5cae660211c81c35900dcedf6b097857f55de2aaaa033f5c644e1260e297f5bcfb49260317603c3647629847ec0e819dda5d8ebdf8d23398998995fc2cd9bb17cd28da0bf00fdca11f42fcd66d3cbb951dfd9aaf372122df9ed07d2d0fed4fa1f678c7024c90f3f95a06b16479fe7ee4040f8a4ee8898dc3584e163eeb0463db6fa20a00ead459fa9009c816418e230002ee72c4ee0176f85f27af899c9cb7cea72be100d090348931b8ab2fe9713d15d80189893d74f63ccaa2a9fbcf1bbfef66430d0bfdaa6df2c523ead12d644e39fe8e86d3c96b9a1dd755f258c785d2766fb400086941dedd5ff7c89e6e70d5e823de53cb0a4aa33c970ea97e8c63b9fb669aec32 false +check_ring_signature 110d47d47c6380d587de1abf8fedd0acc3f4a83eefee5d017a83284f81ef10f6 caaff167f673e95fc3cac3673995a8ed79b2bd351a927efeb3b2364057537282 2 e06eb5254eb0623158fde1b54d84f208a0d5ee615104ff1d11cbd2861f071d36 73bc624edae6254e0f5fcaeac7f1d93e961acc765b019c474b647c0fe2b4dd6c 4389b424c8bef0e1a5e921c3b2892d8184ceccec0543b62aedd31d5c11495909f86aa3488f5262ffe101cd6716e9acb5dcd90f55ea2a9d4ffb37b11186786008813d6fb66e18982a71d0fae40d265c6a897e5e15e0659103e0f96b74cf54280a4a528563af528a6d01f8594bea3f25f823f3b5815ab3110fad1fd94c5113320a true +check_ring_signature fd951cf5fd96fe3d585e7737da77f56f9e68f3dffe31d5a530942946a7d64440 f4c7332a1ea1bb3da6bf7ca466d3e27313c9cbe2bc3ad62d7c3a32de8e69545b 24 24f832e0f155244746ddf6eaddb6d65c9b7410fabb275930fc2d8e5012d70f49 120da562bbd654b8eca6c3a317b81ba90fa67abdf9c58e70b41fc205c6cfbd34 ffcdc6da7ab55cd4a1d795676beca88c556e8c6bb780c40beedf9a6426879ce6 729f824c4eb76ab402183d3eaa0f43fe0e0150f731732cd24ac29d68b42c69b6 abc8bfd3ff0eb82ce20952140358ee28b045d02740030cea990548418685b130 e1f883e9f9b1c329ead41b9342602251dded0a5222fe8d187132ef7ea443f332 309e19c8ece3008c50144587509bb0e38ce92835067025431af463521a13694b b942f85f7ad58c8bfd8c65a25094264f0d1d7df320c013c1ce719d38abe99ba8 4de88f068e294b85856ca4e2c28ad2a81be4d8239a74bc41fa347ab347c0c14c 6a63f54e046b0b5851029b90b7de921402475ddb3857b057fab305be0e18be45 90bc1724995ce9e4cdfd1fae55472e0c473d318588cd2ce67eecec41556b8fe5 09016b5743ec0fbb6b2a491ac7645816abbf8dd836fa57253b800be12b7e5424 d90419e58298a87a13b417aad8bcfaf334c8b7c711e235c96cf4b42212fd03c4 6f0597e10ea568696aa6859651e908ce41c312dba984323ec95c511cc7d6cb2f d2585df41502dd1aa29ee1629c1a6a75a52e403abcaa310497fe1578de5ff20a fb57b4a3c3a3ab4273bc23a2f3702111b10a43ef87ef27f908842fafdb0b1d53 9473a0b78085bfd109fa0353d489f45a37f5dde5c22572a2a2e0a173e7fcbe7b 63a9855663296d5d3f260855dfc1f2d6ef9628363586cabad06eeff04cc7b820 959c87b2dcb78080627578a12f6189345ef4fcc60428c273d52d34918a3234d0 95f65b96504acdd83ffa85e649570023cbd66ce526babc4c1dd80e20e830cdaf c8b08f7d2a38aaf0c60f7661dda415a9600375166ef1fc3c15356150d0e07f9b ee4d1022b4507ec620bb1528ddd08017e217d2649cda0f72c1b48d08e876ad62 9d8285e4be98ccc9cf06bd02f707cbcc9601684796b02cdbba978db3b4165f2f e1d2ae7b14989b1b964b10184f01a2bf09c396464ee013584996807e8b46b9ec affbd831f26165ff3e12198c7fc000392cc418894d19e53d5a4b9c4fb90343090770321eb593f5f73df7bc64c61ca2598ea54e2ad01a3fafbbbc74768aeb500f09c356057ae479df3c3e2a434acd523b15943aeb6ac8d837907efbdc368ea1041371df7cfc7b703d9806f90557b06c280e30717ce119bbe50222166bb7a4a4072b3581e85c625684c97774b5d23bde250331a2a6794f47a155c0dac748c66f0e133fb1b996fdc38c506bb2ad560b90fdcb99a218311105a800ccee5ed89e630d4c990dc3b9379886192af5300ad334132792f6b7da25e589f722a42608934c0dddbfa313e4295d4edf06bc9062fc81e48ac5304b0bb4cd6e24f58df4ce10f0074e29d13a8e0fb994f1ee190ff5e869a94a4440d0a0ba60b9b6a6e36bd9669c08ceb3e495b7febee53f1f01dfe7a92f4d812282eb2d610323ba347f1429c98605df322136fc3d2248d899af061fc7e6de55732858e2c84bcac63487d9b9b29d07d0d47580d7260d04c564862b7081030ca8ffae75096e6d9798c7f5945da2a20956732dfee2a95f434ffd63673cbeaeb7a9691415e9af86c08d9f49903e65b00c11d1ed101e4f740464979bc2b8c1c4779f6fa814a52edecc45d261ad2e88b90a61e8e2bca63d0ac6e179e57fe7eb74cd07a49d0681a281d7d531df940a9b4f0a17b777128a706bb0abc09ac6ec04968c9473857cdf2a894366b2f3560515cf00198783dc5f54d090313a14caf3bc4e37a766101b5b5d156a42768b77c2485904f571ca49e3f171ec81fab393ef12c927900443ad88acdefc933947a426355006ed3ae8b6c3739423b312a4c43c097d8a9c367337c2fd141e43b6a90da470be05b188cc466195f104cd1e5c6cd926d8b6d7164b7577ef59f63b98c19a91f5e40c41fd2b161695aa158cd5adf633fb974d735bc75f9d23070648e23b095383840940b0a0de38782fc7db5fddbc30ff1c8be5c6c2074a0cfeab4ce03349dfe0430e2fe1a2515087f73a224426d443d436cd74e669b42a101c32b807e008627c5b05e48c31cb300ae30f36f37108749af5df9c3be5ce48478c9dc834b224948c33098e43c08eb9220487d50e7ea474d8ed733122eaed13801280f387a4afac2c970a17b8fb602854825835378bc91763e3f098e4c703b2fd7c1611957abae8a36e0063c886a0acf490f3e26a0b490fe77bffa01802a6d1a1f148c186b3b5f6276a0a6cb1628a2315ede752a66c7c9a7868855394a6c4c8b72c0ddefdb6bc6c225d00522db138fb5d6ad32cb2a53d6f08ce5ff4657c0e152784f40210f2345d59930d050de7552f5465fada59f1cf183a60284b3bea27303d3257e5e5d06aa0ad440a9b9ff23d1c94e53c49ab557496644a5a6c76da46fc1e482865abd067bdb251048a4580001f6d5cbf1f1cba0bd2f4838838da47fd79c5cb92a7ac7998ed27b402a9f67b7e258ed8233e4c77154ed7e314d377b0cdae521ccc782fd01ea871770f8df43b9888f307856076c39f9880d7688a8ca40c85c902deb8824e3c9fe55d097b3798993bba7e1be53d92e3083fc0df0dd1c81fe775d0f85c38182453d6840438863a4635d2e72bcacdfe29f55544e0f08fcba58b968d2ecbc9e5bb7eaf4907d57cc09493d14e6635345db93a047b13b68641e05c5db6770781cf49cd4b4707d233eef072075e818617d3d6398e0655e635e0312826335d28bd637819c78206406b6d60217142014b70f8f16a2b4acfed68b38f7b873ba8c0e8d0b9298c9c0d3289a3d0e31433008473e8a34032fef17b28c3a3b18f7a8057cba08106b01506e009b92451d7da8868ac989f8c8748ca8e2097de65e73419e160d203a33d6f0354571412bb241d1a08c927e3e0288be158baaeb9dfd71147ca377b314e2e490e411e86f887baf14d15888cb9b885e88f155c6d5e0275d0b182eef0095fe8410c5c1201939ab4c617202589ad3dfa11717ee609ee0bc0f38de9682f6d4e4a4d0d1276409f0036f67b925688f77aace59cfece0462992ce20c62b5ba2697002506e5b44da4553109b2ee9418a3e10577432c75e612b8457f2adbadf47347acba0516712efd8d2073f4401c3f06247d237302f1b9d9205937194528b11466711e03f403ec661531c06d13dc9cc031b34de4ec5a8b55cec79064a82758708b00e601 true +check_ring_signature c16ad1d06f189340ff79bbd3da5bcaf2562b0c9a74557b0b47589d7aa0417d9e 58ff3a95d801a268c7254ddf2a28f02f1c65aa6fcfb87eaae1a01dc1f7e7c82e 15 9da4f14704beadea248c9fd4350366d311fc4ebeca90cac0d0922283ba83cfc1 c0bd716f390e961ac3eb95f0de7d4704d4a6246f9979e57feb54d855576781be 60d1e699176acb87375582d01e6cab42e5639f82cefe30e574d7f55c7ee2969e d52f1c0cd73140ae662266dd2a3c1f593fda7321e98027278bb04c6b1f5b8f43 9eca19b6b37959d1f32660647bc608bfc294b4dd3521c0207e57202d47af3f2f 06d0557625fbb8970db25ca635d4414d2645f6d92f8028ef60477ff3da38a192 edf4c26311d7dae3c634af227015a8d76cd96fd37a82e07fcfca59be6436d613 86a0197e429c390289dc8f20b69627d1f2c68c9c8c9f5c4dfe38a388e0dac518 3828a5dcb46d765ad1b0e8cbb00d5e8b1f38bcca7fa12fda2cedade7e8863ac3 d0938b4b18d2c8a466a030ce8a7fc07bf612ed452ac5b903631f4c703d92f97b e1f456b71ff18f6254bf0fd21f9ea7e1038b181a4f25908c6b3bcdbc2878beaf 1cbfc5d2869c4701b82c060ceb358b75fbbab658de7876012289919ed423c0eb 9cdaf292f0b1ad3a89599a8b640a5ccb538a08f36b2485b79ece77fe7b6281af 7f05ec0ff9b24f956a147e2e107ec1480985ebc0ef2ec6219e0c1adc12189645 6b9e321b57019e887f9b2f0514e3dd23f9cb51c3ff4639fc6aae8c88d5d737ff 81b6ebf24be019f673eb613a8a117c43ed6071faf04d244a6efe496a0bd64e0e1e313f1d62ecad1537dc3a4674ae3ce53252d088c7d5038ea565dfb478b349082cec9ee8141b11edc7698fdf0de0bd6ed363a4adfacdbdf0fe66d748633744043105fff34c9c0afcbb194ed2a2a49556ea8b551ae62813fae401297caa61210bb1b48f5904228b49ed59f8c5f4d0b7553a28a4945ebc2cb3df76fb883e558006b6157021f78cd27e44220b2e4be86cda9d05d686d491529dab3e0704e258200771c445f61efdfa80705fed17d94b86d88cbb65d86e3a3df4039f070f647d700206ee8da20deb14fb84aeab4c2838632c5c39a5832f91d88a9cac23d8809fbb0544b4278200e20c708e1198f00af16e8fc1499b3a7520e89b7e0cfc1b5b1851091c5007f998e980399131b689be4fe71e81b121dcd7de7ae88a5fc37b913101047c5fcf9c909a3c277192d1dc1954fa2f7bf8887e2fc4583de0d557ecaa017d0d1f52e7b2f0fbe0d949d22d2ffb0168b76dbd9348cf26321d6e1ba5101527c8075c7997e1ac418fe290e681e2160cc878e4e587c4735938c8501af6c0c549d2077593f7cc8f8dbd1d3daa78a0e4a85e0b4c91f7cd5c276a81d3fedad3582f2903c35da7ec806b960c55dc7c788caf4113b68ec7cf8bc3b77f11ffbec998e93b07c1e1dd9cd95d51f2251caab801da5092f7d2b5f0b3fe8439000a377100f4e0002e792ebe2d13b23cee74f3d2b50cebc76650a00719df60f351408a3b1b02bc0852b211d07fccb19ab8e883ef53a2286e79c1cf80e4e6aba6dc80aaf32256b6a0462ed8290ea90250bb02b4dde732a61ecc4fe20af7fe6bf72c51eaf2209b39081c3e675e33c1a1a0c70811980f3d0e62a84a8e02216e717bb4fabc7b5ab4640db99bee34d64ce3151a5c3e67b6c56731cad194f866923060c671809ee248fc0258314237e5ca0a6c13c83c4a37dc0ad24a5773ff672adb8b7ecacae354df520576afa50c7e978adb0556b95a85c004a71b46bf18b90b9186b0556e99a73782002ca9e8b23f465d51437ff69a03c0b8b1f38d8856d80a0f7dc5cca33607c17f039434d607f0bb5478960d3461dcd4560397daf21a810793667adc2e5ad4aaf04b4de790861e1ea3843122ddb19dcc55115f9fbbdcf64623ff279e9174f8f50202f86248999830214078776683e7c35f6351acb9a0bbce659ac76901ee9d89c9041972e0eb7cf24ae06c2a789218afe0cf371de987bcae3d59cf97bbb087ce0202a3a15e81d8c610eb8c989a70d22c0754129d0b5766575c27647be29a56d1480d821d160682ce32672ac4787f5b2cffed8dbdb9134e3b95894b67bf3454b69c09 false +check_ring_signature 5cc52feb83d44079689c9664f0ef6b871059a3002a2260ee07190a14ecd6cfb0 f805f81bcd546cd6f207df7d58ffab78e69a0ddbf8df6a3c5b1d3ceb57b74533 8 b6775951ecf6912c5024095724ed406baccb71fc30c0e25365e9e9ed1871e579 4dc2f32e3050c92822fea55ff496c890a3d3c13e23d480e3b60706742275685a 1c01af8780632d0d3272fd642cc6d8ecc641a03e356c25564cbc5034a6a03268 75ee4934067c61a12ac95bc163ba59e9f57e6436a59316f6900322ed0a7bbcc9 088ef0e948c96a87a0342438590eaa6c61f307c55dc64e0e88107fb29bf06560 5c62a18ee4835d3bce728e9564435adf4dd090579e6a5bb5f4adea964dbb8b54 f4c503eed40df1f1108ccf9aee72a3c5b2592d213437c375c34f546cebcd0bf8 e8ba4b10cd3694afc6024308ed12267b0c7d70cad2ad69c17f3032ce52ede8e7 8908db324231ba6ffb370f7c7f45e30fca1593b4468094b0a48e782ae84bf200dfbe87c1394dfbc8465191c57a86070cbc56531c51f1feac2c07d861365f050c26270ba294ba02e42a99a75dc5120382fb4b963dfee2252ec498258e1e1f590181fe01442d3713897689bb603a82ae304e20bde4992623d28be18f0036e921066de27beacddb106dc46d86b38439d58a6b6037d6b1b515ee0e315c4a73c56608cee1f17d6da7d74b09a574103518679167915ea51baa672bec72591c4a3f0007bdcdda0f1ac66b44b5e38a870ac08fa6e28e0957153860bc191d09fd31b2170f32a28da2634934e66f7b86eec6589144ccce89121036ff8b545fd66309a9a201c1685a5962837577e34a1b1c7534fed07dbe8d09dc49379b5423440c3e9f6d09c284f1c51f45fa9745ce0a74727d10f775ca15aebff02feee14ec07b4c94f50703b588ec44752c6395d8e42e864df5324c65dd1948e4196c2394166bd9970f011e51b7e2795238cc999d51a5f1f98c27ed9294cc3762c0187ba2daaad54b640f3f661c1bbbd45bd119379c2000317ee001b2db99454680d4e6a3ec6b57750805fa1839d2b5c1f0699d96f83343f312da8d304f1fcaddca14b2779800f8a0730278d7748ecf2c3b38c74f68ffc0721bb471c2bddd493ca200b8d60d834b167e0243c1e1d4de51e052a8a2f8c9f660e897fe808c07e218fcdd34cc4f934982d90a false +check_ring_signature 5468ec31ee4da0e294ee853a6502e97f24dc66c83620aeba75688551df3db9d5 a30091a13c6e7f56c33eba74d929260c154056e4bc550836ecef1e23911551d9 103 23851d62dc45f3a97d83376c31464beec671ddfa0e3f87daa1356608b9c72a18 05c9cc308722df2ee71e7659ad13d67a3366493a1f86d6e4972c5d7fe001c091 08f72957678d5643d6f7a6f5febf22facf7c3706dce1b31a1d68583ba965df9e 88a781a6b72cfdec16b3e0c119d4bb11165083c0552acdac0804153f4ed07c59 e0d3f62cf518feecbcfcc3ca4b4c351858aaf215f0404bc9c9cb12b57694fcf7 917e981cb16a04caa0f9ac70d8d6114151e161fd1b984080297a126376422fe3 8262fc8152ecb623787a5679bd0ba24e138ac40fd82383293fd79fb248998b30 d7a3506b8fb25f0377db13849131c39c1dae19c33bad4257bd22bf3317ba9d14 ca7a8d343735f4514d2940a86102ba70b68d323e816e1858e05d2400c39ab10b 29b056b711cbb498c0fe4698d53ced4c8db245da40207dd9a3b4b5e87c862ca7 d499b18655f21decc8de67718a6595aef69e41ecec06025aed95fcd8af8be982 91f98ca0db2670c3ee21701d872a72996726d18fe17cdeaac26a5fcb15fab1bc 70a4f972d4bec602d103cc7b4b6f5e95b69460481101c7780a14307878540f16 7927c0c361b78d7e222ee255ce9f4c268c11675bc01fb1b4405f86c66d5ccb43 9e3f84bccbd2d2b63667f1f1174a64e50fc65321a87bedee2404235809b1a1c6 41f7ecfe4a2ac5a5178f25bd4b929d90beae8b3ddc8a0dd689f5ad0c9891065c dc81c140f8eb12e9535bae730b02f5fb5889b14580a73cb7cc0ed93a00e2c584 ef5f61350be99319cb382e27405afeda431ccdeb2c767485f68f80326b9b4d10 27a196d39efdd69e446f4ef7add2a5b38d53d58e4b5c329f476d8df054ae7bcd 130c381ed13e53b4888edf745f614b38abbc605dd25a39572ce1b1c801a1a76f 9b9773038905658497affe97d7300533c667f60f99fd9824cee9da71d926dec6 fab2880c427c8e06c3cf27878e4e7e9a1466eb2cfca2904b693b9e8a3e8b17bf c3b1e0f032995989fcea451979a0a1f785146de3145f83d72f3eaa134ba1b5f1 f54c881cb756d44aa5c9cba63002ebbc914a6ac9ade383e1817e64e96185ed50 099aa2c35d72d1ac20a4460da94910c9b765757f60c5e85d88541c9cb4535849 b2a937841814ea85afb555a48d7371471bf99ed652600ff614ad5fdb65de8590 3d71ab7c2ad02941446427b184657432bf2ffdf807028cfab9426ecdac1abd13 735a0cb1a17d2b451195a638cb8e686d33cca84bcd988c9815ef3f9440dbd4e1 6bad84e1d083b8b3258ffd7a039961a88a5b63b82f5849c43d5ca6af6eb99bb4 d1e290d18930ba9f0c97ce8efcbad38d8bc99fb1ccb569a2df67431070398171 a5ec4575a9ae963afdf08ee0c671abfa78afc17e365b591356292b2e9e862256 1d84c384f18a18fd58c494c12c20ac2da954344bc12c5793de8e67fb41d917b8 9328133a94d0f232c6d51c27ed445a0e7c0209505327457c2c0dd5f56f86f276 598439023fe625988dc16e0946eeaa8f55d8833c1f38d3410c6e50a14229bb15 1c1c7e139b45711ba8eec235e4a53ed1ddbab1d7e27bd334a6521f35aec3a0ec 6d52442f21583674520960c5efbf81a7ce3ba9a072b09675201973de45f92738 987822c028bd92f5ec48388ded91f79d4247cd3778698bd53454220376081cff 4666ff3f40dd658bdf0c3ade79f870ad91e3fc44ad9f9a718c2081f288cee0b3 088a789a66c1a84992bde370dbc43e8685a0be94a36b6190c10c0cf1d747d8bd db55ce63aeb35a515d2577c4b75279a1141926c9005999cf8c847acab4936710 4e5aec3c34e4c6863e3130f5bf8cb81c1facaea8eea1018ba979160230479f0b d238c0c5b1c362ee2a77dd03f13099d29fd2da09e4464677e08d7896b6d2a152 0c3dfae62f76b1cc6a73751c36488d9881ce9d9faa8b35903ba4453914eae492 84e171e4813c2c335c40bf83fb585eff0fcacb1e7e20934fab337a12785f746e 4a6fcfda228cfa58cd67eb2cd86a13397d058fead2fee04525cee42e14795d6d 561c2c7063e7356b98b2f149ec5fcb6bd5f8dd0b565e4269caa4d35148019b2f 743f75dba862945b36980cd88e1eafd38bf7955a732ad310be2fc453b2aaea19 e7eb9e8ed33dab5fd55108dcd3113261dae7b33a402407ae82e37786b967826b 80a1848205b10c5ab4d42c8f279ddfa13d9ad702e48a13c604a40e1fc0f132de 310109c3f87f51a27ee0ef2876a38777b5ff4c84034cc843491345d9a09b0970 427e7db50999e13041ecc115678adaa2800cbae50ed951f0c00db670261b1297 cc04cea6048861a577d09e17480b6d1273a281b48f29e6e0a322a87dc40180a2 87a992a20034411e32d29808f4208aad13279bb6724a2387cd9487bf2c8a0c17 2849f811e1a61247b25a0e5ce0bd11df42b5366ee36f9ebaf38a809504da303f 686bbb9d1dd5685f5099e64ebe0c4f9f37cfc12cb02809f1927bd8337fd81536 2e27f92a4791f63843f46d77806f9f600b92762594b8e8f5d21ea6802f9ee520 158e195a3b839a1eb8da38181cc3bbcfee5684f30d1e1d7d78b07d41d33cc5d4 a93caba242d9d734795c45f365468f0d56eba13c795e081670252bcb73c98613 7bb71844feab6f030a9b074505d369867593c9b5f8ffd32fc3bc547b7cc541bb d63aa000e6a12dbf038da348a7b51b40ee91365af668fbc4a53a282eeaa8802b 7b0220fff719b2edb08f79b3cf9ad978f1cf38ea447c9f31b3776c43b64076d7 8fa6eea1ca2cad018e4c42752030c354132547c2e379a213a8879598752a996c ddde56d9a1f9bfd4eee1a62e81b0ef4895864c22e468f35df33d95ea38e939d3 694f6689be4b7239b3120f2e57df63ad3e79ca37155dad38c3edeb696de1862e 3dfa46c67e60ad747c3be5601794b7f0f0b149971f82f5dbacbf4ec1693d1000 5a7c4e82aa40c845ab3399fe8975e939b8d4f5900a171475273f003dd4ecf066 687fbd1b65d9be924abd063ac26018dda74be08328bc28eff477f9320c9a78c3 41834a9826d981118a978c42bf221f895815952a3fe28b15aefdafd831bc060a 8b847528ab5efdc71a04518f16e9d6c54f9f1665ffb0269991bc90391ccd7dbf c63952961a9bea46f65b7b73c3ebfc9b5604d2def29e75af20abb7e7d69ec554 237000e3252c48239595b60915ed288126dcd62de85ba947a5a00df15a6a2e54 4857cb20b4b01e3e3c3df65e3adb7a6711afc365aeacc524967edd59f3c7471a 0ae72c13c569ea77a444ced069f07165227957f486e8ef8e28d39290a50cf89d a18b0723a66dca12dc29c564db73dd1f1f6f33614469716639739109211b03f4 971e4d20b5c46d93b1c9a25c2f3b4d42338b558c771d413cc1059750be84395f 5a298cf7d6063f21b38fa9e6bafee3761f42704de0cca5a1618bb609bc1878ab 2e0f7faf67f00e383f4e28d94344b7753f22f4230c5390b6c44f0ac2eb252bd1 b12e62442d56489d51560aa08e1b534aaddec648d0e4075361fc2bc37bad3637 5ca395fff46e7a58eed2b06d7a65346e43b4d84826bf5f69e650cfa770c2b9d3 1e3406aa83ad70862c6ca3d267b7ed98832d80093b32a3fde58690d6a7ba56a8 f879b947bdf1b841941a9bc1b13fcfdd7bd8ddff8b280346f176acd4f77b2bc3 44b762b2b2787c8aeaebe38100857720f765bbba203ef8bc55e275e1d3fa19e9 5c6f90ff5c4fa588a4fa264cdd9e72f05bfde1dd20bffceea9d440a77ea60906 ac8d95b6928ae429d559f630b39c1b9c41c36d0d4213f039c2295a247947083a 4e02a53f7c2e7431428d56e55fa3fec2e30232b3e0e4a2cfc8bae8630a4d4f5f 201c4e14349b25e730add738022854a019db684ba6c0b7b4b20e9f403b8ec7f2 5b6e02beaf7ee003272ca61b319bf58420064b038f74402cbc2f55e15dd6d9cd fc040b0436b7a5748ac0e8e8e74be650f046d99c18031a37654eca2b0ed43a01 7ec3c7e139c38f7d3096b196add32d337c5282d05994f8170eeb70d44168bf5b 35fd80cbea3ced75e8001392234fe8f853c1ddabe50ed61eeecbdd78817000b8 dda2b9ba38d89df7567cad12d1dfa0d625454e836729d5a1799b01d2ec747be0 f858e4142f5a9ff764321201399a79a2400a7aacf77cc4b44aec1094eee0b417 98712d5623b8612a3929e0d710efc1668b8cc6ff957ea90c99a5aceca8f5f2ad 13f9bca750f3209abef36e932a76bb6c947c13d212f8650e861ec5556b7b4517 bb2af8a16a9338bde32db6eb1d32b8c6e25fa0ad029f3e61c9db6527cb8d933b df877ae95436336ce5a064b35528fddcc0786535195db59470e16a732fdb5d4a e3ba9466e63031e6d4a487824e57fa4563a355fe915b497f9e5fa81c243f8d4a 7570436c8905c062bc3c849cedccdd1ba28e28a8b77260dfc2b9941df96c6706 dd27a9d3a0337a56a601fec27f7dd55e4e6a34d62d2e79c3537a64ec6473b783 90b2910d3ae23b7026a9524e87d338938ae30d513b1f204f1a7fcaae02072eaa 929f5b08e85e6e4bf581c8fc32d0319fd3570df6af123b17033688516b24d21c 2aed0f4da2cf15d543bdb317854ff716a4d4dc6b06abd8f7158f734eb6b8a7ca 2b114e6c19263b4394c535e79ba9eee6e29e26a1512f61b89b2b3c58bffa8e70 f9d3391d075e5843f9238525a62b3c53d6d46d3dc7b4a1fcdf1669ac7190d40bd900cbdb0a71e4d1c87af71fe4f07a686286e36f29691964373e914e6e2e3e014636115379a88b302996d038eb63c28c4a1151f553cbb223380e3faf3322d8030b8e0b9d21efae22bb0ec68c90a3b10933f1e0f023395f432a20cb9f915fd801df5c443bfbc9c7dcbb08bcb115d00f0eea3c6c75c56aa8f390bdb239a08890036ad10aab6c996132d792667878102a4ba82b20e09f966b4338d709bae3fcaf04af723f7298e9d0894d6d9b8ab608db43c10782196a5d89c03446e22a12f36007efc9101424d530e712643a190fd001e3179d1143a231895187480a22640ff507aeffcd637408a98e85eaa35b231ae9f5987820cc9407c4228a94ca33c708e00fa9e9e13a598d2a171ab746fb1042044aaab0be6ce6e22cb1f1feae05c3f4330a1d16da3a9c14460082c1b34f90fd02c9e1653dd2d2a81325b1abbcee1b2dd50d87d90684675cfd244f91b0d5e3992eb936f3aa14f66ab5578d60a355f36663065ea0915658705fb2e9a289264c861c65a10a90b374b53a3116a1c703ced64b01d0c0f5be9e019ca78fa6c30b1736148c9c81a1bc3e7cf02795fa9295362154062939478878cf42370981b1cf4bf5c23559ef990e210b7d61faeb2cd543b64609be2fd50a5ce68b823d5a99a6757bff6e64496dc83218f4a0c7822552aa3575022939eb6db5ac032acf457b2b8e861898e8314fe76dab473f15c9fb5abecf210eb9318a2f0bb437ff53714004db335947585ce4fee3e79a3238b26a628cca4d02fa524a6fe6d06fa104a626d2af7973709acb23427c2482cfa75898e89de1df0e20165e9fedbc76e10e6b627a7d8300206d5ae2bc8f7e89bca2d0b649eedf1d0c5688cfcc6d03a83fae9ebf18c85f10cdf9232428a68fbbfeaa8ff17c16674209b080a3a843008ad3492cdc010d20e70bfa1a207d2c3d1b6af7887553efff590a96757a343216abbddcfc3574c60c7fcb92dca9052398e869fd6471ef60eab1001861a5355558ad540c1042b736687ec3530fbb71f554d1211bd9a4df803c130a6f82d984f1dcfa38cce185d10191b20a99c5e40fd31bee23faa77d8f045718019818beeb0da4b677c9beccd8644e6d36d3ff8af1457d1f6d418a5e5a836eb602ffe7cddba55cc7c587c4d5824c8e2045da9a3dbee930ecafd498c5e8d83a930d38fe3ec496ed30e8394c533d14f79c481f29e8f97e0c1ee98084d50bd851660cfc2005a245d7693b892f391f262adfdafeda8fffb1a162c3735a63314a917e0fe19493c175f9b24ac88da636d08cde23655caeadc97a9c0ee39c90c9d889390f520f5c80cfc87e9fd9709c1f19e4de626f5f2bfe95f33b035fa46452f689070b9aba2652a15d9b0a4769261f94aa7f1932d64fbbfe34e44d834128d3bb93d601ffe49d936005b4ec41f95e0752088d9f8741360221c1f7102c80bab5c6800e0d3eef47530f45cb3365568c91f12264d677560be80613734cd66f3452df1c0700c41ab68f926173dea80ba31c935b8af9a9c25a799e73d901f690599adaafd005258c9ffae31ea479b29a0128eacf090ff69ab9977288d978c8c408863c45a605ffe7d60c04b4364eb1ff0c9ebac39956cbb7aa69d83459b391543aee5caf5e0be35aa6d5dc37235d57551ee0413d3e221f4f4c593f8322a04e4a45d91f16bd02d617d6aa160f1fd71cfa1add64a718816e658469e03df7138ed61990d85b480c773a87ab57a414482ebf79a2e605766de82ffcf19951ebde1cb9576efcfd6007c354b7244401287b5f0f9703c67e79aa9edc09ea387f948a9f7341d883f5a40e3f66a366bf3bcfb0da90951ec920881d606061ea410591f43e0b7788b27adc08e56c7e60838897cdc3d733c7220b4f520869827670247d45e35300cb399674050a678c2bbea77468e4b9f8a91a115fcd8e8bc8b425701c17477f4f7f9e6cdc0844dda47a8e68fc1325bbb4ada0f45f7f00b18efb8709dbf7f8b6188f7511180176201986a351eed6c2dd84705b16cd7e4bff95690bb481575b72aeebde4a030e724f86b24bc1ada093c7a2841eea2672b07246022cc73e6181a3e1033596610da21e639c00ecffa3d316120345369fa0c7492f018228bc0998c221482047690ee488342b54d62de104b9a936e44613e1d288c696ef7f4392a42f4d944a4cd40a7ce48b0e5ddb6e5ca6664b32ff69fa13a83348b5aab009cdc3ff206ab8bbc702079eac6b2067821c0409aa09c7a7ef9bfc4212e1bc02a9466f9c4f785a63c3064a78dd89aa12edaf83964bc14892e5b6954e8c571bc7c583c58193c940729502bfc18b60a2f618c84ee9ca18817187513028f982494a91a98102989de9080a0ba0cc901c00d7961986ecc3310f8f3b6de369b5a6edccca0a45a6c83981ae8a0ddfb80e23c7d9c0ce2de2efd23adf1b57f75740c7839a1ed2105f77a71d1700004cc8761dc31493ebe2c2b714c97b6ef48312bf20332a9ea66eed97923f94c90938f033c25fbc96abd29dd8db5db6c0d0e5d9e577c97bdbf5a9b607726f5c2e0ad78f9b4f5eefd5145f63ea5e115a817c60ed9e0074e3096b448a2e4838c2340bd2731a03917ee5940cda5639b95180d7e3963984efee0dd16d491bce3105b003ccdf7abc6ac648e122c3ff9535a0616962d4b03dcecbb9f881e586ec59547007e540ce56854e5e133b8f15b7b68a2d77af72434a105213c0d02524fc3d2fdd0fc009ca3fdea62dec85a06e86b3e491c982a056ba483cc8778c7ccc49025eb909d147ed5b23ce66b19095d63a454c22dbd4f443f0d547a99dafb36fb57d42ca02c277b0ac585fd233dcd5eee30662fc1b274b505ca08fb1441f2b0c3fe05bfd0eb5201d70f743e95e9300da07e34e3b6d9ce25b4041caea3acd949e7fbf0f970145c995cb4a440d3edb770763bd34cbfa5720bb21c8cce3f1a1da8eb0f599e50fb2c5271783fec2454a45cc1179ef5ac71e6f8d23c5f3e86412b647b7e8c50105029dbee347b884b92732ec93593cd1dd450e0530354c7ef867ba2bcda49cec048ae98f3b79b55e932b77125f5250420b0bb56f16c5cc73b5302463bcc528be07bc38062cc0be7863743ba095deae0bb7710c4387c14c0740678bbbf4ecc8de003e573340682a22447695b9cbe3a6e279d45480d82fd8f670ecf3d66bfcd8df0260f96f404a518d664b566f4ce202bd2d9accdf5a74de90c9e3294288f8208b028829368e32e38c2abeea383cf218416edc553d2e8064991f7cc9a3d5910fc8017c1cdc52600701d76b451847ff52a8022654186f3b9b1627db8a22e98811880075ac7a2b23c41dfb5fc3b83f891c6d70f26df38527ed71217f4e0bc56974860260ade5528ea3f280de029685ee600af2d590c45b4024f53fa0e75841aeed540df690b3dadb81dd22a5c79147dfe287fbd4e7aebf01ffa7cce57ae3b5dfac9403f4e2f362446e9c0e4cd28f2560f926e478677246d691e2dd34a59496a3fbc9082638e6d925a5adcec79b03fc6a5f113004cadf96d8fd33ce18f10f5bfff2b70a8847882019ebc7d49e2224d29aad88bf489472a4651e3368bd8152e217162301df7521836d16208d3dbebbbaabfca4beaf643036358beef8398f435597d0eb03776cf2b98d7ef5c8da749b54949cb711870e544da5ccfe72b31755c7a909c00edf690e5ebd935753d15baaae59955d1d5746835e39cebf0fcc7c3d161c62d0096904a8dd75f9f00c0adfc89979e11d605807375158fb92d86d22e8504e1645086ea78368fbec7466ef699b6c520e078899209fc5ed9939777d40fae05600d704b37bd9ed1a050ae0f948113718ab26c35769c49e5f8fb749d68794eeec48600ec24e98f1ea614f65d76b5eff5c173d34cc8eb1b576147b758bfde42b30980606bb5ed429ca670ad45d6f018fe45ae5f1dea983b5be0cae7e0794789a4089fe061226ddf2bc4206d4c230461ec0969bf62389cf09e48bd922213711829e826f052331b0db95088a6b03ebd7016c4a661114f0ccb1bdaa6159c6867b22157afc07320b02930d8694985ac00f5c9739faddf047e98bf0bca3f79d8982d83602bd082b1b7d2492afdc1856935e16c81c9f07a4dbc5accc37c94b0f4362728270ba04a87947c539e807782f428606778e82d85019f79505ca5d41f81ecd0aa49a87003bc072266eea803a1897b5bc870a6cd6a489698cedded0fa601fbf4ec63b54020784e5b023aceeb2aebf41d5bc8491bb6a2ffb520a71915d14bd818e9a418e0c0bc5ab1372f9f19001c91afa410650f36f71e37a971ee001f6079a87b11dc10b211b2603d5390d04c007968121408f8b941f32aace5ebdaca908e7d551a48a0bc946001a4971752331b801911c85a11e4481386d1a391abd35445c0386cd3d08f3d585762eab027eb1da5af3bb57e814d7183697255892112f91611882e1c40c5f039cd4b130ca73d88006afa71ee0f71407fa69f55425d7e0c0d0d977308b097d74306a1a1aecf6149d3f6fe8730d5e0e0c3b5d08da8ade6f28aff4b6f6860badd4d177855737ee27cd60f253ef6e025dc965fd0aa27698cf4b5db64140ff07969bcfca6a8382f21530f56571f8c2da88e62da7629e89728f733970e788ac0aac655a365e807d83d539f9e5b034e6ed94b87fe5438b2e810dc02bb7e955b5095f3270f908e9f5bac9e1ed2221362712007f3ef360ca4a9fbdf4c64a437e3401fc36a6d72492bdc061c9213b6b9e0ccd7da358b0b1710de6c844302bc5c1e005e93e97ccab4f7adb5f0664482414d305f0d304fd3a03f9b4108fea588c603c04ce1a0297b2b26de588b6010b525d75485ee0f4a978cca908a803654b303073021e34cf482032270274d21f149be1c9ddbc32ef63d20002a0c746923d664db90360abce9b938892faeaf80b5f5312c883ce864d6333c46ef65a1cf4b59dfb2c055553abe78e74a0b49266abebc356eb8fbae29d798e388e59e27acd32439d19033e14b237b1dc1fd9d0bf0157c91b7ea9e0c9406529e621b3f94f243b3701c20088656d164d5a3bbffa0f5e552321b1b418299dddb9953ec6bc1e9456850d440b3434118c4f3a9d5ea7186887d882d9d07bf46744e3b08b34d80c574d70e6a3094ac2b3569612af1a09fdcd9e144f8ec1d4f2280f00beb37a0dd9af733496b202a3b3acb99df907eccaffb103882f815ac67b309fc6e7e3c917e4b8b3375eae0b857379479024e50c8236cc7c03c53529f204a37c1bed6a761f3277c3ff78d104685298a61433a7265800401c2922ce139ddc904b442be3fba8d995dc80cc500b739b34a833dac2f6b0bce2bea84457ea43f891f8108f21e8d8f4ee0642d247015546b0454441e96e90cef0e3298383ef608c5405da138dcf414c0a87aebfee054fc85c7126e848e86ba3a628cf0ead776e5044e137a15a234ef4c17acf958003518aea5508926bab65073a94116028a0e5df425867c1a83562c4bd2b89af7a0f36635a11fcb21857bc5cd5e47960b5afc7ad759b8e5affd9c09bd491a252a10fe47b43c17654d497f335c32e0c6805b83e833117d9c142446f427408734f560fd893b7cc76e4d4e66b7642ba7997554f5f2fdc47b8c654563955b81ae2589f03f0acfb9096ad32ccbe879df97c754eb81319f08f2f1b50fabf860672c4c6fe07735601409092ff83b66a28cc7f175d862283bcf5509cee3c2c045112f80683032358fad9f5ebd254d6d1eb0c50e9a8cd24ac7c955da240dfcc9d8c1689b2260dce529b29ffb44258bf0c5e5c390be81a126d229135763ea866e7c421bbd5820489f31b1427675ea63464143cdb58d8dc2daaa939c244b1fcb080468cd08e9602ac747809c31fead0c1abc9b92813e82c00f8913035eaf971a1648eafc1e3ff0409f458cb5bd2d69ee2c196cd9a052ac85aa8bfddeeaba8ba0b14913fc9295206779193fbeb2e80fc49d97b901349ce0187d052007667ffd87705249043cebb0010cd65e566830310b952053634af5e5d8257fc882d5f3e7c2fd670b50697d30243995034f2ae310994f0a27f019c56e34510c70c5899365b1edd134d3819220d8e4be377b7a2e1928771a7373bc5c4b81e4821db42493bb5d555c828185ce60ffbedfd45e68deef89b815870dd79f964383c5e82fda5cbf0dca02f0be918490982e8fbcf5ac83f4a859151cf80d1cdafd9c810deb11b6170f1ba7c1c21cb160949f55d4edcd87a2abfaf14a7b89a79269af9b4f7bad2e713f528bd2175e58c052e68b5cdc681099db760fc199a193245789c72c964affffcbb20c0d40d49d40dd3c67ce61d943f18662cf469270d8c21308c8151e66b7675d62e70c9ff09740ee6bb10b524f112f8f555ae28c1438498ef4877c837afeea6bd480517682a1306938306c1cdbc496253f3c89bbab37b7b1e0745755444ad4b7c046b079d5b2f0371ea7624232042c31614e76a9e90fcbc6d88d448d6b89143c3d7416410bddd0c234e4b1c3709b964f965c6b80c7086ee8728a2025ceec61bb598dd1c965d6f03186575643550d92ea7890c7d24eb29a5abf726c5698465ac46ecbd8334db30079672b7bb78561271430fd13626b9b462a60800fbdaeee2aaa6be0b5def89380315ecc6e5419d5a243e1721d295dbbc8f0dde066ad6bdd799089f4ac56b87f9046b8f147eac21bed0040da447c03a4b7757f3ebf9e035cc8911efde8ea816400e02632e3147a0c19c7d9f5d5928ef3cbc87306fcf2a2f3dc24a76cee848d27f09e49c11e340aa8a778d7c0a9ac821a6a7d80b86c412e173c64aeee60a9d3fdb04ed3c104d42938443ba2dcb0176e073a237934afac73744989b8b72bd2648e40978c688f27598f0790bfd8a97e5f2fe6407dcad2e3a724f4c07fa71ae2958b906be56f67c76b43d8486a7f6356995edafa614f3b381e05db5caa34d4755c59007af1b44f488cf5aadc5c9844f5f56fa79be39a3a60d4226e19c0ad1e1fe75b20184bba4633fc509b535d7d553d9d8cb27ff854d2736ab2db6ce33664c261de802914f0fa92166ac6f9db20a51f3d977cf08f2b474dc4e832494c252a62ec3e607ec5c48b5b055655bf8238e9b3045f6b8cd425a69bf23978430169a027da4730ff594ebfa3a800fddb94365428f844ccf52226abf288e77c44323d6b221d1820a1a81f689714592d77a00f8f74f49175d89ed333ca9169ac6c99c893e7fed270fc54f398056e21a5a9cde8b824c3c7bf156442f796a5478b9fe391d528392720af076b243ff0438cf88753843fccd8fb181c3f3c1984f13b6b00724f8178538046eb0adf53917fdecf3eab7de31f67196c3447badaa4d6214ae5dc867e061e6052ff34091978fd8bed24d979e6a3b5444b22cb7ec26e06cc5ee7ec662f1e46503695dc3bcc454fa912881256c9b85dc607b8d55ecade517f3f3108d07f7356a047eab6c806f1b789a7401bca2c1205fff4518402ff27812af1a904e6164d25602f68581d5e0a63481784cfdd499da2ab93f1fa46d5291b5c8df299410759818087a9a4e962ed6d6a7055930d01f651e0d42d3b449c78ccace1f074f44e89fec0fae701f10330cdb03a61b7bd152ffe17192bd701e4d8d4823637e1f52b6b22b0e71322a66aba5d54665cba21394b9ef02fd374e240bfbdb0d92fbddbc18b5010fc2e04d5a66d2e4961ec32451e48696965b2761dc86e8fd8c6fd71c8b5b7e6c042f65edc724e5383203a36e60603f1417235521147fb967c998f13af5f0cef7045a3925c919f9852b9d3c81b72c0097d6c068c1f89ed5105e57fefbd2a1670406485a35b12770df1f36c1561463a0ac10b2c2ee53548f13be0825077574b2700d698ea0708c2fde4e16a06fff48f0dba1bd34d4b6d6218e20043334e241bdbd054c69da41ca21d76c3b4d6024a92a736542a197f1a7836f5f3205ab2e47870a09d0289eb333cb2ffa3fdc7d4ba2178b8f18ecc96b3f45483ef88cadb01f1cd3040f2f2cf933d0bc935c3808a3f881d769635eac887ca83164ecd7eae64f35610b52d3b54571c03afa45fd1145cf9b9832a62fbfd23e5c9b021f5737c3b706fd035c8e6f37e9069d82a6d25edeb998f8f641d836b08fb1037ed1ca259aa7271d095351f95412f10395b3c7d6d49bf12689a0c518b4cf429c848562a5f871490d0ae41c1a7822d8aa86e38e1ee092e1f18b0bd01604c725fe0c9a3d3cc1added70211f9c1d7d2c8e6ccdbae64b485b05f4f0aeb7d977f700ecfc87863b83a79e807fee23fea7cc4e419f58d039d700e8398f0b4309a18f4e6a547ef00413e2f7001a3f388b4b1a5acb320cfdb03fd5441c4872136fd6c23a2570de4bf24d8d4270fab7cb7335835de16ef06342beb3c0a305d26f6dad338ae603ec1169faaa3190c6d5b0506c56f323b627f346ac2554a2dacaeed666e731619d0011e2dba93fe0be4d7b78a28877e500f8b3e0243f51fc1251bfab8a82472a438df1fe3df67b40c37c78c940eff1b9945eb88388b9501587bca92a7adaa4a03b633856c389bb7073b9b960dd6e656774738776bb0c938c0f9db1e715fe2d518451ede02980b140b627a7f0822afcef7431cb9243a9743f55d3e1f00694d5e81c5b9295d4b693c05c2fccfd2d9865528940667af06ba4e062a134d223a682356f57b3b7243509d02d1d1478dde9b86c35c6e7c6165f29b93d8503bceddf72b08b5959c9bd1c822047ce8311ca008fc10f6a73b2fb5657315c189577e5ca8be2b5ece1f45a89ec50fe7563306aee5c576565f70bbb644749492b365ff46ea85d4e99d1ea298703b032d84ce2df2013a310d18d313c8a8dbf59fc4ab1ecc3f09cad70e3a7075abd8049be56f82463f2ca302737813e389e393c44f5219269b1b1dcc4338761e971a0e1315d9f704de4533f23686ef159d07520ca3bb5db84f2090ba3ce2868d5a780e4283b95f7e34c1f2afe686439ab0d685fa3e70ae1f1049c6b30a626305f3610d5c092eac71fccb35d952da399e3e52d701ad4272d4da8f9e77944927626aff0de7b96f334a838b47c2793a0b4a182a3526392fcc46e56ab7a1086e372723c702b60b42a4a3eeff443ab9c36992278e4b7f7143dc0360d4a356ede8df836bc40d53d0905cd3102be0a506e513e249a1e288cd5ed42e69cd985ab972894bb200049d4ef8d43571b3451a39ca3a486111f8760c998109fa3f3c57f1dc2404a8970c3591691f19d3e23d5472fff934dab61f3c10a0d9896eac4a04a03e4f20c76b01e20be5c967b5d4c671afc1b006f39bc7e1e638d0ec44e1e170db9091ecd15c00 false +check_ring_signature 417c742d35085645faa0a7ba26215dcf1c7222cea29dad707b2e1a3f5ff78a72 0788ba19febefac967c99db9b5409aacc5bd8ec7b0858081ad425b5e4d6b90fb 64 648bb214f6c6801ad17da4c52a199f5c39ffa27bf2103e99fde0b7c243e421ce e9178dfe8b4091f8b89d9010ffebdee5c1058bb431d54de46833151a3fec0944 40cac61da329c43ea973e9ca87df09cf8b3b4c50164bf00fd48ebf13ca4b5eba bb1c2f92710d57be24c90ed8496bae21f5afec1f910270ddfa240830a9288fd9 f0b4ab6d3bd01d95b9fea35760d2bf46a84d4e5cc5a4d680b9ee581def0be5b6 6310a9fb6f4cbc01173f3b5569850dfcbf17947d2f6f6d984114b93e619335c8 e016e288b204b89e856674400bc61ab09eaa09fe62a64b14d3c2424b0a9f7ab8 687e28f8f9fbe8f75560a408863bc426ba12e50db0a2676de2cff93cbaf3b5c5 754312fc3c12eb07e577b3657db625a5f952fe97be0a7b3df8c4730007b22c8a 48b3e60678677397768f3be6638c64ef3fe90f369bdb31b05caffd3f2cd72b94 5b0f156c42c09644f43670cc78acbda2fe6154fcfa949ff4cc36571854125ec7 114edb27a1f151e364c55bc19b35a99b54d676dd4893f7d74768be07980f3207 bfe44ebcd2d0ca78bee3ff157259c09231f0117612c20fd027565741a5f8652a d4e5a767907caa42ea57683622540b378dc2c9ad9f627db84d66b9c34e7019cc eef3d66c4fc52860c551fc8a590cb556d3e9f881837d26e4221742e4866ce961 606f9c3fc666329697531b36920d7eb8e986cd12ab246f56b20c9f75fe1ff8ba 4e99fbe0d511434495cbaf69d83bc5a8f1cc8b5890e782e62c74f69c83e52e34 0259bb302faecc3992feb7b5a557d5160c449eb28bed3403f9e94fe92694e166 30309e02d66c3f811c039277af88a48f638c940d01383e7f3e7e251dc6ceabf8 bb7f805ce55d45e2d86f8b235109ccd520f5c9908ac5a1c405463a7986e08cf2 5cd7f02ad0aad185bbad97c83b326c72e17b5271023c997cfedaaf177cb7f700 2120d41b433c7df7320e7bd10ec99c1d0f9b60e74ed77a6831ddc187d4b7a2e9 508dc5c4c9ce1af3a63e0a52cad528d4e145d7e6fa114b782c38751a07fee018 cfaa6a1cf0a23f1a85112257c6cf6f9c3182ecbd39e5e6f50d06b56b8727eeae 3311f8c2f33cb836f205e0b96855ac36f2bc54547e0a7b7e38f11b2d1f0d2d04 753f779f3ed516c56e72d90dca8f9bf8792d6fd1bf250756d1d49cb982a32912 98dc8f12cb8afe55f013e4c7eedcd90056d1e4a7e14b910088dedc364c3d6380 3832a90afb39b27b81d3c7db10e509199cad90ac616ca77372c85f7973c2f5d0 b1e76d714417cd2db4f4d020bb7197592f57f2a8cc71d02280413e9b62a4bd06 0efecbf6fb1a83f35a8bb8ce978566eb0faf45453958473e1ad36d4d3fce829a 4d411249725e94de8df042c984ab37a4b0a3d432ec3db21c370f1d01d376c15d 1e99542c49c978bb356ed1c71e8e83b66b61b8b21ff97b99a362bd2eb6ca5dd0 684d43d21819ffc9622fc7c5258c88a763dd752c55c14f778895864d6000627b f5f67fec035ebd5862859ab65fb168ab2f66c9615986f6806f2abd01116b82fc e2d00c4f1547af17165e46df78423398504bc19db4bae6140b2a7348256bc986 7e656b23110cb27149c4da86c9f1a45af6322e17ff044c8faa81d73379666f5e 2ffeac406eaa6a6f3bead88224655e967a407608e12ddc73a2f55c031badc5c9 07c4a8596964cc821465cbbc60a909eb8790e705a97a3c862d7578c391d44b17 a65118ba9f6fa59af6d23c590c3c4558ff664dfb85f9021229d37b9432073d12 d82a6f142efe37c287c851dd00190c9e35df4ac889a273bcce4ca9e2227f1b5a 137e147f15d84accdbba05c088a84cce2c9ae80e2393370f03b1fc97966f523c e8cb8cd452182e4520afdd4ee6573b58b97ce1ada1fdb3f1194845574f6f259e 6f29365069c056ab515adbfc58eec36f17fd7cb2bf1a8400c5b5a74903df1aa4 231429a51d6bb1d84a2a84659b6f8cec31854a091f88b306779e82f194da1909 32fc469718e04e7b9b612db743c6358b9dbc41c8450f3bd94de63f72fec8fc5c e8e9280ca4a687b62c7de106d587ed6a72ef6f68fab1a2b94277d58d26f2ce8a 593a28bfa460976d57f8ad8d5ddfe4445f287ca44394c0dd09f9149a48fc956e f8f578a8857ad2d9b139d8ceafddaad7c04d20c7de1564d544c0dad3b1a5e61d f3d4637d1574064e92a0466c6b41677080fc2f2d2a5660051f9b3d0e89073ba5 afb75de6f2ee0f4010c0bda57fccf49193bbad3fa4f73577f3df1f49bf64cd47 50ae2da8a7f0948b45548c55f78a78e6f448bd9752589ec5889ceda3c362eb78 83fdab956ab51a8336ea5c03afcb92945989c16c526cbfe57ceaa03dbe3f331c 3dd7e03c7a738b412dc838fc1ada26bdd83e2e000efecda06dcf48d7de2d9859 b966b3c2a91cd1e7f1fa4677c9a749bbab9398be077f8d837c1e6d4ed71bbc66 0ab26343309cbcd178cf844648b306d0727d4bee5ce94589b84287cf4b3ddc2d 23a5065a4cb0fc3c80f8aa59a2febe42b9afdf687549173a3ad7c4cf29bd1f98 006e98dbebfcc3ced6d8ddf9a49bc5d0f94bde7d67518f3cf80f96905afdde33 8f6252aec265598a583c3b71d873477bdf1729fe747cc5f35c5b926ddddfa55d 6a1c191d6aa650bf7de87d1dedb91d522eb29b771ad444965ff03db424447b0b b04f3a01e2e24c0a3f4ed204d1310e355bc949a29c944bcef22c64a371d98618 cf8bcec22e4e72e3e43f09a4c59ceb73cc132af4f901f9881eda959e22be34a4 84162ce72641b4598fbbee7da825e58f51855a551cc639b508fe4d64435e6695 8d9620b048f01f481f6c1d020355a3b4f4a40d357aa562e2a2ea983b57671066 87e3d067c43ef38075abd2c3223e7b00de8e8467a3fe878bca2a0824471c2578 96f61da2f43919ba2528085eddbc209f00ab76006b0a9227ec441c3f7a8ea109f6b4904b222930594b9c2ac93c2453fa07c7e0eb8e7c89fced6f662d59fbb50ee3e91a402cb454c2890c31e7b88362a4d095897c317391d665552e48eb33ac07f14b801aa740db10fea137c62302beb35a82268d47e90cb2d61f9dbd64e39d0b86c65f497cc02669f9958229b3a124e888ff8ead4276e893dcc8ee381af48206ecfb70fa650f9e15263ea0a36f8f46f2bbac3625b64b031954a7f23930e006019efdb483f7b715ba411888aa8c505b06fadce3decb5dbb66a64996cf6b68ae0839c4d55060bfd4895e1c01ed8a9668d2d6717c8e5c52894877184441c67d620fcaf535d8b008c2437083c20262e4c03e4684cd6e0b9a1e095f70fca80ab63302c9b3d500c9530d6e13726db42f5bec8deea5e46a471d3fe7cf03e32141a5910db8dfe46a24715ab965b32bee1ae9cffd46db26f1d2b888e47f8ff9f95685a60049d8af872f4b9ed88c3f2d9ad39bc0453a425bb2bbc1863f8248ff6674f56c098946bc69f6c6ba843fdb6abcfd4b6c5e70a6295430a1d578cedbf775e38da2059105544f6ca6ae75f126bbe5e3975bb401b54e3550a7cc16c3dea05c89604708a493e60aac545b9f7749c4fac06b9044d8bd19aafce65de16661b20e05700f0bec5dac52d01ccc84addd2cbeabe6854a33507d5a4847229ced9b2629e8f7eb039abac6ec4d9cd9b358175b938398858c63deca44ded32bb7c5f7d62acbd01e059924541ed4477825beaa598eafcbe20cb6c7edf57127c9bf27f392a854238c050eec349261bba77a7b604dce437c692135dbe51ae51e2f97c391dbe63879f3059db39b87c9b6b6645066db529a7b6ba89ca1a2d17b43c19eb7fee82753b7a505b9946534389cd6cfdcf8c4e8b965b77f0a895f9b74741d8df3069622cee8400bcd3d1bf23078e561eb46b9015646f8b1a41b4f37faa41d9b4f57637bc842910dcbb7477f8599bddc8d00e4c096f036af198b9e210dc07d3a075164ef6fb0a808216d02643dde7cd98f6b54220a57429ef5848104df3ffac15e235fd6dddf7808bc038baddf77177276714cd40f7c54b1332aea446ab175e0ca9d9938488a58074864ab09d288d58d02c97be2398ea4e43b5a5460451184cf4b0e7fd8baa68f0d95906590e1442a04306f009651e0fe493e318973e24708f26913cbd15886030db5d70b70f6c8dc6d24483ea2eb7d6f465f34d19333acaf542dd0c5c2b8e44b0baefd4df73daebbccc54e958c6cec1d28ec97c55447f33673defd9d8a465aaf08fc74ed074b09217afacc627352a6f8e37c9677e21d10ea14628b035dd116ba01741b918941cb47caecae2b235e7e7cca5ce41f56e42ae5713a6b45e25fc1e5098d731f940b45defc80a80e62490e76dbc69d749d484661c51df90c9fc4b47c01fcb2b7cff24e86480eae5f7f3b14519e56e9bf02d8b95c83feb52683bcb48503baa90397e24806248fd62bdf54fb5cba475c716cd060ee1c2abd7696d0ef04086c131a12d836a742249f0a95fca78eedbdba5e4f5f7cda675cb1152c72c34a0ceb84959ac4041865112f66fd21aef6c07a36e2ae7d92082af036b9f21ab67d07c1ede08b90912018253465ca722b486fd9aff917b5edee7d7f65df2872aeef04e37d081bedbcb3e5c840d19204f5777f517b379c1cd407b8303540a0b347600c5fb8eb7c561818a8f36772268775d76f00bd596a7dd787f4d0c2bb84ea2c620a24f0dc31933a1093dc85e4cce26f2697bf627f85b29a7aa6e595009ae4f0fe0cff129bd89c193ad81d5920f7e8f155c7a7fbdb5be44a438e0e7d1287bd41dd016d68d31932f3eec19a17040a64b86f1a59bdc1c271b078f1b0bee70737c48e01293258fba0b9bac633b0cf969401ba3dd32e63427a13450013340c0bdcb03c0b34e9744373ac93f64be60b529d9aada79f3706344f0d476cf6521e9b815a8e0de088187bdd44cc49e10687d1fa63face9c901415c971f980aee7fb07d386c80ba591e208372cc2315d30c101d6398f856ffe04161b1aac99e1e139b7f67c7605b5c165dd4bd263bed9359149422cf05e6f382ff343be8cedb22007000f6f6402546620ca1a3f0bb48ca5c097ec21e12497a16a063512cf5a53e13e4d10fcde0c4fe14f7b68cb5cef456042f6dfb65665c41b8dce1907aab71869911378667e0360c8760fbbc2c1531021c98f2bf03352f637f8c0cdaadec1491b32ba1f4cd409adfd9386388c95c1ab4b6c78605e8730a5348b3da44eba07a1c31a904969ef08fea907b7e8f3ac2f35769b205518656568bc74d5b06039d58df87bf1f1a4000fa0ec4199a4505c712e0e451a5ba2e304eccb58ef9a8f42475c3a508a4a9ac60c9c1c76c17c14a29957fa6e03abb46fed1d04c74ab698da146b2c45b890ad21059cfa3317417b120352402fe3a04a4492d71c62c82fb0553da1fc0188649d520a8d8407122eb422989fe31fb4834afc034466e68433eef09bdafda6ad9b6e0c01dbb6258ba8d8e5ff46f8c3690f3379954aa181163a754910f00ba36d922cf9059fd602dd76d9dc052ff5b1a0d8eb707e52a9b7b58e8deadfcf19c6c7816ac60abb08163104485201229db512d5f5b7365855b22b64730f5d1eb2f0ebb5ff50003459a4c806d5d258596087ba1fb0e85d87a1860fcece021ce8cae2350b837c06f1a5090ec93ba4e3e29fbc6009422a2b87b465f873eb259010c58abaec694a08be07194b9efbb43f11cfebe4e1d75038273e35b6df407a272da51a5f7dcb22002290434cd494538c9e1871778f3978e1d191c331b20bc12efed7ffd392a66c0f3cb5ef3c4a4f0197b9245f284251496fb1c7ef64cc6764f4ba842aa71eeac60cc0ad56c54d1fb91eb4e0840231fb223e6960ed3ced2a3b76d23d02ff8ab6150b9afc2b83e1054c57f1c1ab240a2b6169459df265af67247b23d869ed40fc73035d205be0f690675c49bf372e1e03e46edd13f93333a18cee9a773db8a5ebc40e82e28ebefdaf8e4b681ffabe09efc0ed95f890403e21f4327989b184712f7c0647af1c24a4a5e111664f53150c2344cf59efb7c8a74bd9aaeb3d097c73d1fe0bb37e6188c9fca932cbf96667a33671356fc0a38db5b06770a0297ba2f969e207866fe7f49aca98958ae7d8c062ce50539b53e80ff90599c68a50484a40c028003d4654ccb2e457c2046b1b67a6d23114335936589848db9fb4f132987a7d6e036244dd6483d374b20202d46abc5bf867fd98f72076f68dbd203fe7c2c437dd01a58bf9e87dc8313dc5f8137a051d77f93fbc685371e156b78184e172ff22e107c80b33e17311dae6b081f13d8dd051f79878316c5a89e0217cc474002616e407f1de2dd893e58e8bc638f2b0b816da862d1cc897ef2d66c1b1442b7bd77aa300bfe19a5bc2d0db8bacc7c939ec002b560ce070c90afae25800fb8a28fd4dc90af87dfe21626bf2c9d26bb3ee118118d3b1a8100821a178415b49eba71ac30303ab7cf938f41264100c25eada3cb80ce5020cd6f91f4bb91c98951ea8a58270071b2faac0e291d7bccf50c359579ca75db5f38f4f9fcd707907ccbb904755ef0d0613d348f57cf088b72cd76cd192e9757f80dff928e6db56df8f1b4b7172c508462418102b5c5a92881f122b9957987b45a421eed7b2c7737c8ac193ae48110625d11f6e00f5ceb702389094a607ef8fd45b8b074920bfb36e51b16a0624530c6d23951a9c199885c90e92469d8f634e6093700d658765db3b3f5308668ae9025331bc03e18ca606acdb4632212b50cb735f15f03d992ef5dc68b23f6782750460cf5694d6679dde8a0bd3706653cc6da9b1fc7057a64354ae8ad81b720c990255a34588d4eaf5ae2dce485cfdb567fd277868efd692d796bc3ef187467bfc06d3c4fd9cb01507eec8de18118fca2fcdea4a69a0250572b685dfdeb5a40a0b0da52ddb222f04f386c196d8322722cdd0395e1eda9a2cf14a349b1207197da90e562d55f191b72424db80f48dd1d07c7e0d8b13ee75122f689122c68246e7f90affae99d6b93858fb99663b1f257588ec6c4b692d522eef6c480781637fd0890cefecbc537ed9e63d3b086bdfb2a0f47e08146918be942c2c34f7952f610f820435305df747e6d144628818f2c1d566443754e1c8b86bf8e1a39213a6371ad30be5cecb41b0f092faecaac80c5722603ca70eee77df55d1b67d48f0d98e308a0c611871119de9114578070603c73eddb93f4cdf08d9b88db34ba1cde47788c0019de82a05449875156ea82206c11486681fc0a4f4544c761065e86668f83e60086b988a27cbc30637416c7506ca530fc5a09f9b41af26cc643c93f957f92cbd0a6094b83c60872e3a7ebb3086558d04db5e4c3507bf4bec6183af765cf769a100a1fdbb54cd6c9b622709fdb4f965bb54a3713000d881f9f4fe72fca26c623205169d115939ba6898e0af2e938041c2958084b587187e18d458c6ac6c0f551205ec1544fa0f96a0a7a6e9caf8c70099114651ea69b3c781c43dcf74a1b8c416009050b2003ceda8df3f943bc30eb43de717c1316d6a890539f652126e40efa30625d5ef376d9380754339a1ef547a7e0cb8305f40f8ed3052f36fa39dde48620fa30b5cab7a83368a5540d80e68633e00992cb76cd5958c8a692025071e9d59056eda918d550288bad1822dc56eacd0ba0ee3ea4bc8bce6eb146784a57779260230eba4df780c8aa9e8059cdc775ba82a7502d4f8462320f79bd84cd20061b60bd27271e7be2c5a93f3f8694c5941a1c41afbd13e2b9fd2718a7924ee4c26df0f3c1a5160c094447fef09b2f069df2e4873a5af1b3654fb681afadc3a9286b60b786ef62fabf8ab28bfbe62e5c3cdcbb70c11eda3457b80eeac8a12aaf24313020cbb0a7be14c80eac4c0eef40021b9ab3a3a03ba19cf0ef5cb732e8d239c220a7c0cebe66627b127dbc7efc9ec31c5cacfe0b23c50605da381df7d9c77a7bc0b12a4f9aacb7c9f90ed4768a009b1bee7f3bcf9b41c66a686f7f190e27e4be801123f7b30c51ed9c5886411da21486a778945ca64cfb37bdd171434e78f75240e3da6678451cffc59729f0ddc105fbef5a4f08c309c1441d690b7707fc2bbea04d7b115cf417bed75952cc0b0adde0bf9f6806ad27ac623c43548179d6ca88403ffec5916fa9c784c3569ab151a83ec7b735d62668d71e6b56cac36a39c80e104fbb8f668a302be24a146789ff0a9a754618cfa5e6419534f07185577ddfc460ca70ef7bcfa2d9796582b6fe8ff82e1d22291304e32ac00cb515cecd44ae60205ce042f0b9a2697d4478405989e59978165dd0d830eac02e4dd8a4aa16a1ac50bb3ad1742239281874156c65c264f251fb02ab4cc336f88c93a0e077404d9220a58e36c85ed14e402adf75f503ebcb1dd2e7fbaecfc97dc05d94aa47950481f056e5bf48d2ac24b406cb9940a8c18f684dff171a4d6d6fc4f0f633b345f073407b0d12c57564e8db4f160e8da596b5164ff09770d79e9cb744b7ed079d375db05cfa03911b97b8c43f5c1927ffe5945eb3f9076fb985c757b4ed7c18c0eba3c0fabbba242089342298c13072098df57656d67c0d6f0f7ab106ad60b8b46dd4f03b906d2b35c3b0e1d97b3aa3b74804b72968f2ae0b4d43daf3befdf656eda3809fa7f7869931b464b9145820e25748f850a496e350b384adf97f831337e766a039fb914748f5c9c2161d191b61a3d34c6622b1e90c08bcb4e48a31e4756dea20a false +check_ring_signature 18bfafac97fa10421b75e81ddc9f5605226c9b6e202563320947784fffbe8a75 9e226666216a89a343fd58775a245c12e9568158081da11524957dd4f2125051 4 f257c1e8231777091406b097135cde706245776cf7aa16b932f353c4fef3a5ee b9ae0aff9f23ffb2611755ef638b21810c3ccbfa9b70d2bb4aead03a819ac286 dd3d9af1c90d0a41352c7b9c9f7503db0c94efaf0e4a6d76136b08eeb514ae0a d59d4af2e00286016ab2e9fdb790e491b6a826af28110ca5d455753c179b813b 8b1083918266ac0a3ac7094cebca63332ee4ce327ab73f25d358342e6f220b05e700e0d8693bc728cf459e7d67f9b1038fc84ad59b1ed0940aaebf1cc33c700580dde6a1c869b19878bd2d696d35f0373e9666a1a7888dff3947e5477e359506cf7fb3da623643d0a225eb60dc288a4f2d4d52cbe5fca6b6fb7474f1e392f80323726093c913989a44dc2d0ae4d4d477d48223c6bd90b167fbf945fcb5767202416963950014e1542e8fac56a49c64123708d0481a2739973e7248f5fe14b60d1f2e85b92ec9679e3e505364e4f92d1387218f185a9696da2cf799c9e4eedf0191b2831a40a9eee3b136411f41dc0298fcfc5bbc3da6c3239338080bd9352f02 true +check_ring_signature 33bfa41f55fd5a1581970f2c80d99a5f3c5f9d85a89341e0e223992188e44672 d543995ffd95a2b0717d2180b82f9923e552c73be184dc94b8fe6e9465dc25be 1 f4bf30f7e15906a01c05da09a6d08abe1371a3a556a1380fd7a224cc3800d497 b98198f6b50581bb130f37f2c7f3100bec246111242a3ac5d1aa1e780a387e892493f1d370202689d0ffa1020a50a77aa9a5c37a5d80a924434ff639ce90cf0b false +check_ring_signature d40cc55c7e6d6214aff893bd5136f584343cfee0e9f18c714f99a73156a2941e 3bb633ff35213c2e356d03ff898929ae6766fe53891ca1a55beeb37a8a1d811c 47 3f8a8567f7402f07938990cd79b38296340dadb43b400b3417f348c674f9a4d0 0fbed7661cacb77eaa956128028a0ee8dd038c9c3783e6fe2d772a36f634a79e 803f0e637e2686cf3eb6fc2af9e70119c441ed1a2e0378ca4866a80d8ce1c4ac 3e876c542d44bcc00e2fc85ee1f17992f01c9cc459529c96bb03d50d8587669c e4b9a1b16786c8acbfccb7a7b57cc28fd1980f5a72aea292cce3f9bd3dba59ff 051640ebbdbe2a77e367ef44099138b725168fc80eb745929611987ddc76c390 51041e62f1b4eda09dc8131622114dffe6c9e685ded0a916d3d64255987b4926 4084f8745d96afa2aef04b0d76a00fb281a2af3683b770dc26d4b5970a630640 16c37a83eb72acb39bea107154e181ad0fd276ecd0905f9770b598ed44ff240b d9dd27fa6eceddc7aba71ba0522b9ce12230158c14e50acd20c51d06d625f085 c88c2841b9c25349e6f18def8228fa463d37b883358c5edd6e67aedb4ff60242 8e716e77c47c6cbb4aeae9b085c869d48bada2d22ff4844c2692b28f8d63a880 cadc39cc09001abb8d57249df19c6164235574e25ca4d264937ddbbe7848dca7 52a3058195155dec1c0c38b1322ce1ed9625e80f40f7df756dea7a769515c671 e39b6daeb94190f22924bc61c3e496d2edde4e083b84bf33ae991ea90d539260 439fdeab132ce4d5b27a4b61fc3a0bffee94a1250a6d2a50b85583ecbb90e4e5 85746753ae7497464916aec6885b84493dc0fa923fb4e5aff091231d944f25fb 041eebe0f240dd8297f2f2287b1b6c350ffc33ba3749536be8b8e702a6d984e8 1cc846abb2aa997f877a7c40c867be2ae5160aebfed5178e8c3e5a65fee4cea6 2863b0b2f634ddc70b1402bdb4e7a5ea05d9eda16d59d8cc66cff4ce92741c02 6702136141685a10a5a7fd46174c9f7f38c403e5a76ded92df7f85ac3d3cf28e eab05c11065459703bd3fcadd5caa51a9ca673f17a739fdd23518a28b400d1c9 b60bd57f7a87a40a27269c48012b9acf34c62f7893728b52821cdd2e0ea41159 4b51249cfc5e483971050946b0bc86fefd49e969fe1929e0f45573083a2d5eb7 c5886821157c62b2f3c6db821d550a645bcd4698fa2495941b3a04b81393b5e2 f40a71e2786ca01f4a42220d30721e1538d813b195fd24e11ade7fa5a658bd37 b90315a12a2dc1ea8a0ad724cc3caa23c56fa6e19697e710c22ee81904faed03 2edb881a3ce349c0ff4da22f53f19328a953eec23e90cd4047412a39544723a2 5d02d13f8976e2fffb5ce321b7f97e19515b05d4d269f5b19ae71a72909e5be7 62414a90aaaf70988ec5ce213a546d84d4296bf0fd9e8d528f396d49f0a4447e 2a1605e14bb2c61d7d1e7e1e97f24088994c720f2958dd80babd3aa2c478c0df 98f64a4a7a5e466926da4d8fbad3248b10f859169d9ae31958aa70ec51801244 93b69517ab84b7a766305883210263eb8ce2c03689d24bff875ffa39b7996632 d1cc08640ad6b97f33adacc69636209169c4c6b400c95cc5b054ce98b2d683ad c78999a5ac35a7a4b03f203e9505d104d22a8f905d8289786ba104d0f5b3edfe a964f009534ff1b008f09a6fe379e4e1eb4dd274628f71fe07abab9e1d5f8bb3 fe43cb43a60bdb7aba2167f3e0c8a21dab5572d68f527e2856a0bcad182ad4e3 98427448dee7ba030e4ea8f746802cd12c931adcddeaefb4d26584375f83f640 2d9a7d2a3dcf09332f71c18dddc9599ea6baf1e8ff0685e0922662017f4e07b4 133f76842b96ab9dfe2256d9921f2614b3f51156044ce8dcf4c6c1c391901dc6 03ede23760b175dc562a2d7cbfc0536a2a09041e0873aaff54cf019c3d1edc37 5b1242b58a40e8468ccf8aaae437ffdc723df475cfece097c01c6ad375b72f81 69b8846f6553d23f6708685a6e11bf6ac1ccbe5259947e51b411655617527929 e90cb7a1bdd0fb173f8d01b9d0cd33963583b526a3b4e307d563d96b98919224 8d8952034f0a21e9ea0e9d06a5427e7e156d7c00afc812531c69ff8063cad843 25017dbe3718f4f6a7d40c26ef89eb53028caf083fe959a9a25aa16c04782594 acbe5a2d25e836494efbb7805312955c6d9436f1b490e74818aefe5a65aaff6e 2442da63409299abbfa4ef69d51b91850217c3517272583937d030a8e8132b04acc8c1c99165f06dd9cb329e31d5db3aeeb1437b012cea61158903e39317e60a4fa7fd010d5ae02dd467553d0e7429c20c470f65062c14f741dff09e7fe9b7062631566bd37617a9cf952724db1e3d84244a6ff7829f4632c5786c77c1b9530f96dc61753930eb32f489a9bacb6dab8da9ba0a9787e263f1af81655b68938d00c5c7851ca0e79f38d825add2a257cf667277a1e3de3b0d40eae52d72ec69190f1418b8b100435560a1f4cd0992dfbf7057eaf75233488497fa1e8745f6c5f7030f770c894fe42dd849e0ba83cb58f02148c0dc8a8c3e765429c8135d210a5700aaf98e64f29c8f1916d10b88ba6b62ef429ebc69cd9b62d803d62d12dcb9d504fc25f18679b1901b6aacf40d4412e2fb46f380e4614db5fbd22afa809a0732014d9475153c390dc89370f84d6558bff2d155099b716beb99ae7f67578cf41607b2273204e3e2b207be08e8033998bc2a9fad753d6210af069dde88f2b9618a079d591eff09867c96469b6359dfb1e1f591d6a099bdaf732dedb2307751676803e099554aa96186a2c764e3ef1e80009c965972e1198f8fbcd243925066f58b05705523e8b435e8994fd052e0faf9abe66c686c98361653f10f63ee0edf114c024e496100a79f2c4318be6329365ea4cebcd96260c18f91d1586385c94f84820b8e50e7097d699c3c1610f9686982131f711eda965436d67261f6b9f6a599e90ee3dd4baa10c9ab1c70ba0ccf81925e6200df8853fe19aa253e77ad4553ab8806d3562494c6279d82c310087f621d622afd9065a17c10e5f77e8c0f04be97bb03614f7b9bd9cfe7e9fe48847ee913432e4d445e30f3aba330ccb3a37478ccc80b70a0cf355a4e4482a5045cc7bed68a49b7458a53570859f83bf04ddfba8d690cea18fcb4e5dd93347fdbae69c602d10bd60a483e97ccc1f8db0ffe2cd09e08085c32ce503e72a81e71ab662b5830d64eea8df2bd0c63df6f8df5779b8e49620fba31ca9f75d00266972d9add1cc8a9c5b13a49f8a3d9d485092f119b46f19007ef5fec476e1bddc8d55bcfa7370c14cc8cb7916a27c249726e721bc90ac64c0269e307ef701b789d443a60e5a465939f4302c70025069abb3119d0061b2d620e61338d38cdca057b1fe6f74211590434f6e11d64a1cf9cffe2b5d4236c4b140047daa9c59b396a10616d8fab70e9d880550c4c159c28e15cb7a47215e32ac50933291b274672cf546f8c72e6f4f87ce2a4e187785d76b9c70450d8983e78bc02b668ff6c9cc2fcc44b92dfb74b43105ed03609459a7d57bf83ef5ba0a43f720a212d8103fb5f0c955d999b91731122cfa44595c2f35588c1b1cbcedc5b7fba0f90c70b2ffc6e86d94aee63e6c6c9ca64133dfa12733ae8406f68125f05aaa80a426ad27134bca85be2366e6a96fe47d22d6b9f58bf3ecca719ed9e978acfda0c612322c799ae5947a95198eb0b90c0ee1eaadc68bca38b9834c698b9071b900b8818548e2bca99106ebd92d3800f27beddd9d851be186b965f029bd6cfff0e0a9902c5a8f75b3db55245ede3b3a48f5f6857af860210abec8ae5d7679b1064060a05fa942906344b313d6d047f3c5c5f6f149999de856ae9144b67a242315f086b57a0182951d9a55e968de09cc8b3502469f5d218125a4b8244ca673a48680093705b1eba6d1d61d106299f17230704e24a8d460a6f1045cd9a9e68eaeb6a08e8041388d21a70ddacb44a733be6c2cc9cdb3f5923632812c2bc7ed4e92818082890da27834eacd9fd4dd8db32f83e928cd58ef7a52fbc6eb0d866e94c9a580ad44e8bc487124c10db9154a7d7012c0ee779007e71864d8b7b95b0677cd3f80bfd72496811592be42a618a7f56feaefe9ced276bcda5751288cffc5e296d8d0f96d42917b353e5a3435798a80308e1455baefa61bafbb8a59cdb169c11c2c0091fe6ae46fd09270c671e696209ffacc0883c7f0e0e84b7422ee7e23001bd1b08aea6bf6e5a550aa3df88ba0773a3c217b5bb3239207c5ca8d0cdec82dff7ff027393297c0301e8fe0508ba00aac42efc1443605eda8eda9104d25344d940820a3c4dc8a1813109a52be665e18df784424468ec363cf14e1837b8f091c3a7570b6c8aefc84608dc2827921e4a58b7624c9e18494fcdb63ac52af4657b381bb805068b3b555a983a198596d241a49bf4a1708b6879d35269d55f6009fb4092760652b8d703a30eaf5da269c14e8d3c8a63307ecc43009443ab1307a9d34ff0eb05eb731fe3c8060992cafe774e0635646a3dd01ba9ce36c74e25fc4368de38df05bc1aa174aabd564a02dcd22e429b064f741376e6650f9d34f7b2854a348a65017123b7f694726e2800fea08da5813cb68891fa92d70d045539a5dc700111260f8cf59c90ad381ac122406e7ef105d5d3198f1a2cddf6aefc87fa1dfe5570550d9cfd3f40cc201cb2fe58b6f6e6ce329ee44e22c00e2b107e1d9f0951ccd616008cd92afc89ce8c85c0ca76a4d4ae681db1986ca9f542e192291ced1b326a480086156d6a059e692fa581428e3cfd9f62882059300d0183424d3f9841171d2802c0f0c82de6a7fd214f33d676eb8843e795752e0f8bddbca89f4447670656a50db3472ff315270c5786f1f4492423d341174cd09fdd26579bd6969b9bc61af90bc7ac9d29d395664196d7100945ce650b1bfc64406f8cfaace074e26eb65cb00ebe239dbabba5bf1dee129c24d4d4c6dc6a3c89f35c2c1b10f9224739f2424d0359b541808bf962f018544f2b18c8420e96b309e3db564076587700d387f71d0af44c2f6b71661bb6ff5453ec37aafe8991d32d6d93b19678e61bc1aa120eb200798f1dc51cddfdf171accc63277238a9012bad8ad86409e2c75de84f2f842d05c8ac205239a2c6cf2a0f375aeb66a4ffd9f072f191ca9d21f764eaaf72c8250655c2a5a59978c991152c5964c5d095f0c725368bd512d937a12b22b68b09ec04c98915d1be3a3f37b4c34d3c03b7b80afe184f0095ca4ba66c89b21c3730480993c3b01d5ca1825f8c2b646ab22fb79afbe2f1fdf1edcbb6ac968ec42ef94b026f86b89d2a0a8c8ea53d44bca16298ee96c4852b9c5ab240cb92318250e0e50c045e42dce9eb28e7ff10fe914d763be361ffa163dcd3c164d044da20c6d9d4037e69d0e01fa79d787d0fa3f1f31080724f765f314ed890aa07f94774d86ae50e1d177564c68f4b35cac9ba173bfd7556567b41ca36606d63b6697099040c1b048e456194f10831064c60a16a29eb5cd0797a7a3b6748997cbda25c6e7b95cd0a061b26d5b5c2fa3cac1cad26b6abdb05e1a00f137f0cb43cb1343c765c615003752f079ba06ce2cc75a7287b78ddbcf9b73a8accddd0df1b297286b6d935c101c520d9ec6ae3d19acd70b45b15e474d1dc9abe1c53c5084596c154a18463d30947937e219952dce18367e6bf8d958ca5badcbc7a9723d78b1272ee55284261087cd47758d17a48a7aef837bf182978fe7a577f2ec3f690660364a10d1e4cad09435e8866c1b8a8f31be75fb7e7d5ce45c5d9822b927201baf667f26ded2ef40e27211564a48c819c8b11b092ea403dcccb615ff0a13984b67c9f91101f2f440eae7d3a346ee8108f32df87184601310a6d0ca560aea8e257195a7ee1e9c05f0c5f1a68eb0618230321dab81df653beb6713070f6a07cc6f4cf1c89ae41c16c0be22160d6c8d71841f98b2ee8a8fd22470471f60fe8d355e68e68b9c7ebe3680590d56f21ddbdbc1a1e3205af717c61e44ab511076ce683f04c19a53b94b2ad0960589c1c937bdefdad2e376e4a64eb65923b3ef218093e7115e5b3393f42df01d0ace3f6af4c278465b100d3e8aaf1f003ee2410fd7c4a9bcf722a0439971d03948b72fc59c1001837d54a6e371997338f8459b3a7dfd2829f1a0f0e435f390dc9cdedee134e63e1ce855dc3ebbcd916e5ece0f953a0a5ec537e0c65accc54095f630788763f5c57f987295e07c484b65bb2be21941fc6ff641d3e987d724f0ede9f5d7819b9c695dcfc1b979da41db9710bda7fc08c48dec931ddf69e9d52086fac0a3ad8d7119dd650a1be61bcc1705b8a526dd4824ee5f5e8517fd7b34a0082dfd2f8c5852c1db2770869d1dc381d897341608d70cf712e2c5ebad1c9f70da79aabd4ca3de57a1eca12f4abd4289faa41900ddd8b466ae73ff160784e6a09 true +check_ring_signature a69110251c8e3925d409ba8cb71a487798dd6aa021d0ff23f0ae7b99d54af192 e69b7637f9366d6d63dfee4f17fbd47f7259448524dd560b2dbe015c8845c93e 2 ac185ea193ad30ace39087cfc6e4f1082e3ab73109353fde61a1c7e9c09cb0b5 a68010a83e367fd39ec475f4ba189c06e1754f0dc8563c6baafb1afde12fb554 aa18334a4122a237daeeeca77ae4add97903e7f5648dc20fa6b28204c4750c02cdd430d939df7031a9d8fdb3fbfa87c4c61c05946e272ca59c31036f0a5dc30888595d951e0a9bc19b5311282d29571a33a6a738d1902dec5c90c091dc43ac025d237063f9abe54fee02b8b82c4c6289c5fa9ee18d6891d74afeb2c38c189be0 false +check_ring_signature 097c221a97b0f1478dd384855e1cfee833a5d4a6842b4d9045e3efd4a5aef8db 00857ae324c696466dd6dccf2d73fae5e421665f4e97604b436fc4fe5820d5ed 4 1d50d042bde3c04a6df5d3a3daf0a4e40fc4f94d2a5071ca6f9f3b73dbea3139 046d894171b2d38c4933421a6634ab47f0531065f27efae3b7b97622c736d6aa eacac970efa978c0249a4915d300f8921baa17d9b5947488e5dba7043fdd2b65 fc951ed0615def227193f1347b58dae5367b9fbf55735005c4c709732d7e3421 f6a38b7e1daeaec52c802996825434a2a1e9bd80b04d714686aebce514c1880b2629730dc1f135eb58bbd1c24377d0bfbdce41dc84999cd588e5eb9f06e5c40b0f8bb682013515b9b979d0415b121e67aa3995c827680091c13c7d9cdbdcd60126b0de6fd82b23b5da0ee5a3c5a267dd08d1d7a24bb66c573726cfb93c52d40657f53118f90cad62cd4e92adb994aa6ee629f942c77d919a78b542468f98a585890084ab57f1aa87664144dff0b9c35e5630eef0de8328157e7b81ce19e6510baa18ffcfd7e337ec756ca7945c452874a940356c075a4e2d9c78858c2e29f10d04954085439111b0a82f7aa1bf9f26272669d58065876cb660b105624129010a false +check_ring_signature f5f5b7f2bcf397b39dd47890f643578220997b511f7198516c7aaca4c16c20c4 0a169151f6c7d84403f099a8b69ee582ccb969cc70071ecc60bfb3d14b7721cb 16 3fbea5bcffadca4b48c19d409f0c0b985654e08eaacf45f18efd5b148d6803e4 201b2548e4d67c0186c9a85eccbea3db0d6c18bdec946da78ff2c3e3acec3eb6 d1791ba05d3a23ee40d3ee05f82c43c45299a1072e950271cb3eaf7a0dadf3e1 365f47f96356c63ae6070d94abaaf1977555a4ff06ff74e92d8c03e7c38907e0 b60f795be4db1016f316cda9f075a6086d622dd9d9655a14b7be837d859d1206 dd08b4029de7ff875ef0018196c1abf651499e16a34310ecbe62a95e3a95bfb9 ca01c7b4f29c7c9a427a6dc027f905fa35c7fe04de06c5bb8c9e21271a41e4d5 eab6ed03d4fd964d1eb1a0d18f2c8e945c734047a282e5d448429601664b43d5 7941f8e53c28a9aae0f16bd0bcd21cc008dbd7c101da13a723c6f828025605c8 bbaa06539d40ae6faa40a04ff93bd2bfec055f818563e9b969248817b9391961 4d2833433b4afcfc8c00eb3d1745b3a8402faaa245e77e2ade0826d3e5b34bad afea355d565b409570e94a75437560e00178433143b4da9b9529e434ecf01bfa a1b6a52fda626d3c411a1dad9feaa7e36464ad363035d012d9e51a81039560f1 ad86144fdb7df29dba517231fa2233c62458559dffd230f36ac3e8e6de0f1f55 e4a0d65ed958300c3940dfe637a1cba94102f3b2807cc5550664ffc5c7b1cf8f 138dd5a56eea91157a4024d3b346d1ff0cffc3cd72b36c11def9a8cad10fa3db 98ab6af0591457f0f5a261abfb7d1f6b525e8a7331aaf3418d611cfbfb02a709d328be84ed0459a68d9c860bfc1f440372f6b0059001570ea1db93bb0f873209165e73ceb5cb033eb042dea03685e8612654f521269514f74d256797bf09d709ebf3dc9c12c73630eb3204c1df9af1b0467e0fa1085c9d77eb0670a38bc2080ce717f991f6a4306020325e04f88bc6eb63c2e0fbf3efffe328496a2e85ff14013a580abd2d98dbcec6ce8acdd8ee82d3dcbc59c1a6bda7933ca8019a057c550b147bbf3bb044f0c26b15b3ec9972bf3920fa9d578c79c188f1e2289fd68f4a066309a856b95d425bfdaf6a64b2842b5f3f28b91f70fa728b63195784d906f608f005efff33373974cad449a3e4063521dd85fd5cb05815d3b59bb81ea102bc01d0b4611b2be766479c9232e73af4d711734471db37503abe3e59b3998597f508f13e1b446a7448bfed5fbb8bf7f5cac31c96e01e87b3c91086e8b793ff06fe0c3f0d58ef60a927619c6e52a6bb70d7439eeffda4d8d650d444c5b21b5789e90665823d82fb15ea24a969a7ccac0868a57220e8657ea65c1dbfc2f6d9b79e71028beae9a4e67ddfb6a54bcb139a02c3f384108c9a86f104a3917f1472c8285eceae6e7372f224aea885c3ed39c4b727dad02c0c9fe4dc21af6923708fc2af4f0bf7dab313ec36972012d2859a1b63e1c379691d254d9a0a28cff05126ecddc5088f10609b6a5cb2f626b7dc6056c2a3016d0ef3d9c38c30a2a739aab93b574f018e2335f94da2cabea2efa92868001fa91b0f87791759edf91992c27b818c3207aec5d5e43b2ae6345f48ec5efeafff3cdaffad3fb0385d22b9bc6f230c69bb00f02625e4ef6e6e4ae4e010e8081038a480c14abacb2d7a3458fc10d0b6c2d205c332f069a0ea7d53bcda5eaf01578baee358db9e1a8581a493733fe2c2112401ba68261f33e04e393512a22d732b7d7890b1cc0f0926dfeee54a6a55dc869c042d2c7c1ac59cb6aeeff7745cb1990440b31f0990414bdca245f5c8a105248d0e7061fc50daa3941065d1e05539159431310e492a18818650fb872d1ef6c1c60bd07f0019487e8ca3696f9740a94bf90a9362b27bcc2007937651e395617e9a0843a0c20a70d725e40750f5d4bf44fed7e464d3ee0c5c0618b80c71bd31b56303196c4552df39b03c3613de64c13960864071f88b6581b0f8f86ef705df3e440f9708182568bc40450d187d35661e6de3192554b3017d18efdd183537077dbd0cc64a64049127a4358533382fdf4cf74df9ba8df7a5f443e3184e0b07e770080500d6e4de65ff1ec19565744bffa3698825be77e00744b3a4f905a82a1d677300525083c9f2f86a107811f2e83e3cd0a273d945c8d4eefe38bf8dd4c1f079a604a97acd1284fa13bdff1c12211d9239701a37196c3b1c9bb542eb66367b988603 false +check_ring_signature 9882b3143f7b7bbe817ead9b4e07ab74d0ec96f30f523603ff46ca612f680f96 38e8ab07437e2ce0e9a67b6300c0b7265d09c0c0ebc8205eb3321411edd2512b 2 2b63883966ae6e2d0f78de7f5d0e4b727e929ba4567256a94ac1c3ec3baf8e15 0cf4e8e8f7ec870689b7e5f00303796599217a38dd9a0b8ea3dd74f5693afc12 c4230f302ed8b4979970d22dc881f7e1765935078d2da06bd23f0fdfdb28e40250e1100214d6340d9cb100ff6b565724d1eed643f1224ca6336730f94ec3dd0cdf20a4db7ae15ae90aa067b7af62e8ef26bcd2fcfbab99ae388f3846e23e31078c937411d928db0a20ef46344a0d5575c4e74b31d8931a0493de0e1e6c743b05 true +check_ring_signature 430204ea0d22b677b54b79c1fd041708c15ad952f8717c0b0dd8149fb2b35584 4f8b53cbfdbbc875b4b1ba89bfc2bb981bcb43e6fa51a49e3ede60f4adfb4d52 132 334cf6662a9eef53893e1da3664aec8482462053aee119f574fd968de9b9adbe d4c7b4da8f2a4101cac09cedf5c4f7f465a4e6e0b957eea02f3ad262e27caef9 f24b3f57315f87068cc1e2682c27e36b54bf5d33bcb08c61dcac669db34f1a21 49008522ce5f75e83db0bb27f5689c25d48064c670671c0861f5287e8b8db4a6 97ba9b012dc197db1def7dcc99f231a1f458d25ae976edd50e9dea0d285e2d99 be92bcea7133121907c3ede83ec8acd110bf50b256a3e44c45d4efd3d73d55b3 dcbf48367e976544ad3e1576864c6719a36f398e9da4c4754bf85a1137cce7b1 b7673f0e6a7fc5ce831ee5d820fbeacad7f16f64f0e6848ca017b581266b13b9 e2a131665528af6b8f5f510ae8d1151c285127fe99cca0c0ea647f2d79e1a9f8 55a3315988a555428b4c6d08b1d7572c83985cbd488bf8078d94048b761d660e 899e016f1786aa750368df951352ac342f54090a26af49f01579328cef3b8182 143ab7f55277e226e5f22aac9e7dad1d93d23da1ad06608067c2219702693ba1 ccbc66ccc7072ea260a634af81cc0b583113fcaf283efa0cae099afb369ef25e 2bf2e939a4daa3a13a7015153bbb4052ec1550253a35201990fd5c0cd042f520 de5f6e2d384550952ca6551550294b83747c3e785fb2385b0d9206e35fff14af 3bee951af7d9cff6b42dd0092db4275c9387b68d6ec41a2c25561e1aae82bfab ea2bc83668d0b4cda165b41c5f647bdaff32bf6013d5727466ac1fe5a9501d86 1c1fdc842715fb86fd735e627fddfb00790f71ded762c1b07ef0610b59109714 8f8f43c19286c5bed6323e4aed5b601ef59341ae338b5035e36bf26b535ab716 54355b5c7ca0cdd963674c9d188e2112d3ec2334db48f53972fe5509cf4310ae 6a8103c43836098db2430b319ce7901c694a73f7f065476a42b5a31498b2f2d7 653c563c099f9b38e5e03eb3bed6e7e20825d10eef910bf9e39e2b8063d1e870 aeaf438d081506aeea687d7173fc356c190d78bd2bc133fb933cb6161f92cb7b 8939b6b8af4c664c2ff96f2893413d4cf9cc3de0592bfae9beb8c8a325b4edf9 9f5a983b930db50bb2bed8e51830955a4699111ff24b1649887cb205264902b4 fec90efc447a256a04b70b58e6325d17d670967cd5470b0d050cf6946255c9ad bc4fdf624b8ffe9b30530c40486efe34fa225c17f6bb1d7f348724d6c6e2d3af 54637c3d0b7daf25de6f9b2357b3a30cd94fe0c3c54f37842bf093bb0213c857 f4b737f31b44b5390c4fae9adfc65fdb687ab679fa9ae92838537b5d529eb660 278e6cee759cbf952e1bd6aaa58ed49e2adaf6aa69f854178796f645e7f742d4 5de4a568ad8c66fb94298b1349a3aacab0a39fef44c923ca295e5bcc48e973ab e530a9ee5bb30cc2f64a385a7562c0453b36f1a482cf519718a273c5b9adea93 3f75560574d87c576901d9a1f462d45ee18d66355b7ef9b525a8ba4aab0965b2 b054c293a960ad602b52cccd61ae0f1973aff165d2bfc5a451afb87feb9b55b2 48be662ae5b799aca8f4cac794cd99685cc6994f9fa819e26587c6c9c1621855 3df1c8159fba859576d248ba48ce79af7495b6242bee4898f49e8e53f9ed7dc6 dee40e870821a45db51b06685a96c77ae8453f9dad589a681ee5dd8c510512e8 88b9e6bf330f3e66833fc0a2b5a36c84c6af33a8f1d0c397825cbd2ef88de820 12313511f0fbcfb36ba61787ca0994e939ec62c32a39a0e7929c9d44fb41f841 64447d1499f4e459bec2489925d41acec39ffbdd7532228bed03fcdddcfac9b6 d644ba4d2e07c649fd3bfc9b9f8eecbeeb8379bb8e1507b24d783e238c2358a3 d61bbde534feefbb444ec04993cd47e5404162c7356aea64acaf22cb0b3cdaec 12fdbc7874130504dcfcafb663cbab8b6088e81e676a59298a0660b6a917845d f76c129406f96fdd4220b90794d9a06f0a1a11a6574bf03142ed63a5dfb0f159 f713089c120a8965485b2c78a0e97b9885e230603107520003dedae2c14437b4 2288ca0d1a9f3847b8f7bd62d275558bc04168772c6d4fdcd07accacbd9aec33 19a255de6b9523562ceb37c30a66dd6ca412eaa11fda5cc818f120fa51268736 0691822f0442db77c578225fab34b8da6784219cf45b3a11cb294618664ab688 53ddeb1e1b6b4aa9c7173f6f4d7d6c8e7bd068229652c471323e3593c1665b2f 00a4af3ce376cc66e8dadcdc74388442fed8fac12dd847fc567098762810132f f16f8ba3014b79525d0e36264a2e10b714f9bf57d58c5864d16a5612e6462372 166a3f8813a31dfcb42b2a404fa0f42b9bc659e3fc91bc355089319b321861d0 f080a0af6ffe82210614f92834528297e80dcb2b06d7d9246176dce029ae756e f286d343bf11416646a9c4de4e675716af503427ddd244f9bf8b1454653de9be 58f88180045743ccdefe63758576abe5320feaba30cf74289c8b81fe6e74a495 ecc9d236e9bacef77f1655ff5c8b2d0d29b0f79495a12c414677d3247dc846f6 6132460f33558086ca9c640f701fba2e7c50052ff30ec7ad339bc95aea9dde67 79e8290532b7f5f362cd9de6098ade8025adb2ff24a424afd80623d60b8bf001 438570e653e245c67228ee656563b85d6561df0c8f9b140bb74dd9df0d34e437 083f0f53bf90cd7a321c855761d288fa113fda82eaddadc53e2eddc5d8100b63 3b21409195ec7a0ee6f2f0f9b879814150263c076a16189da76fcfe8a3d2f815 6f10185d46b338a16e7eda5cf4fe2841bf7a2ca8fc95ccc497118091ade0892a 4173d8a6db83734b19e6e6a83a6a9ed03f3e55c0133b71e03d8723d2fa8fbb14 542da1213981ad2fce47a7974f02ec4cd22f15973beda42aed9847d6ae3d8970 c7e8e9c3d2cf8a3d66d288861bfedbb71623e97da3edb3ad9899b62fee650698 ae156a0e78fd98508e7ba0d0e689e528b874ec8b10f340c1c800f80f439d795e ff1cb56b794ba6856c830ddcd0d75925cfe302cbb49a6059deaf7244e2e2cffb 26f1be3da8c04cb8d4d31e892b5979b6cd30a1a554732700d876e2df8e63b4db 15813cf22a38605703d38880059dddc31bae18e1f4ae3a0b1d6f3877dca1a9ce 38973a2d9879b8c61e1edbaa128432e0e5fecfb8977811276a51f9240e335947 afd208cfbbafb8553956724f25996cbf78c95f1e0e0d7bd282e754eca34a4e12 4f54b6b6d6a85249def6ebab233bda95a62cf73cc69b05cfaf2f371221233fea dc3dcc866e41a47c5a70dc7f0bb049d9fc3bdaf1dc8702aa4f896cd28ec65d64 7e34a201af25a8526abaf9bdf3dc7b11fac4d1ff968166d34a95f67d7e0d4ee7 80b69bdf57eca0f0936af1d3226b607512556b8dc97a13a97ca10446732a4384 c93728283cb9121ffe2e95d30706ce3634ab57928d1425fc5e5d6f7722b7a684 17ad5e5fca720a66d54170e79e47b47e2b015082e99246be01ddacbb57545333 4845062541c65f598810506877cf6146a3143767fee1ad9bcdae7721a44b2141 bb629efb96ab30ff79cea55311867363aaf06f670cb5ccb8a2671dd1e291784c ddea17aa8387a0c54200c720521a3cd8bd6f2c7345ff8b699e0c5e54967ee7e0 e957f99f4a2975321c483b0b393625249a04dd0d9d0a93e8ccef0c82dcd04290 3b19463f089e9f3c921874e7206abcf7c6de94725d7b5a5451cdf9eec3c3fafe bbedbde3876dbc4bc7349b0ef3e7f56aa2d45e43b71dc7a76c4027df08003ba3 86249ef9c715afdb777f5267279849397b3813d8aeb5457342e4237c5c8a4ac4 2b772d8cdfa139c31a34b6a9eb09153223e0171fd15cd7b320432fcffadaa5f0 035f4a76c2ae2c9d13a09e08002dc2f80b10494e89701eb9476b2764829e4ed9 f33abf9b624e582bab7826ea1d276302ebec35b9c01c5ea7ae6269b1e53ef8aa ede681f3799dad00513a680c8ca78ae606a971026a6edcf3ee17e6693a2ca4b3 502a3eeb40e3475c852c1afe18e2eb99a8f1270fc9b26222c271f7bab1bb7943 4a5c0a9c963486fac0471b6f1a4ebfaf827bac87a8dd178498f2774021f10452 bd931272eee521fad53939203bd969af2c8c92e240376e76bd144bc3c0dce589 ec4f005a886d6fe3813555a3728f79d0107c31083a5fd264679e247a7ff0b0d7 5ef1a52f024d7dde3d29104da21289e926afc3149bb74b3500bc45fde11494c2 3e0fdb748a74876d5da20ae8940ab7b49dfaf92d27c700cbcc8206d5b3b590f2 68622c109079539223b05c888002baafbae7ef45d09aa8e4902b9d749ccf58bf e24bbc196e631926909e3b5c55ae23ea2b7772fab5ef0f61dd4979f680fb7587 217acb06341a90bff9dd08348ebd8638f9017684eacc4e3a6fd477883ea606d7 720d55b40c15cf39532b8512a24aed38c0649a3a903734ee2a44b257c8557702 be3d04d54d6fd5c4607a329218635f5d4cb8b347da091d319e87ce20635703c2 b3fb4f113a45bdf1a9bd792d671e113635d018a269fde5f03845883600c3bf83 c0131d48d14f0efdbd70a823594fa192cc46b54f325ea9c8aa010ae1400fa656 a7d9e46c0df420c9a342f78aa4323bea42ca93975e310f955ba077266f5fa68d 3602b34d0615863944838d161c6f549b65ed12932ffeacacd914bf775805bf87 e5148d23b8a1cd55f2442ecab7a8b887778de03f96ce50eac3d4331a9ba0b697 f71e178454b131a8e8c14d2f72675ac26e507bb2f7a497829b1d94e69b629ade dfaa96e0a800ca61d1d858990f48cc901f6052a77c4b8ac1dcf7b1b6671b814d 23770235c6de60d7b5e7bdf8a5d76efc008dda5e61a934f44e03f329ed766147 2619904ea058a5c0bc2f4b5585cf99a7d9a8ff7d6265ed50f8f9700307f31418 c4cf6c3e44e80f26fcf2718c8fc26087c3421954d346ef47786bc9834a98a1c0 73d3fff6a36aeddb3e986a0719cc378bd78b974144dacbd433863d2527c2c9c9 40bd1dd73f32627ae6bd95db5a8a595d064824959eee1318b7a74c3d4061b00c 9444ef57189ba058d9edd221d2c0a89d9e87bfe1d2af3b6450a5c9d7394abcbf da6b2ff18ea1dcddba9a0b2892d3d8a88105cbd8b9228868dcc5a18176eca3d7 89e63a0ecb4d86c6ef7aed500c577208b4e2088680a8712ddad5ffaeece27541 3942a78d369fe06118e9d7ade1608a88cc6dfbf0a529b5ced26969a336869d37 3a63f41ddf604984ca5e3a3e0556f404aff272497a3b7b008556a94269ded2b0 cfb1da49199671436f02b5a7ada1e373f852b43ff2979539c3f38f3d104b9500 9dea1f2b72b35eedff14de826b9e3726d7dcf6e28ebabbb4b0c12f2fd5bc7fd1 b7ccf6defa9e9c213aa54d9227fccdaf5b6eb36b4813564475abf037591ddd43 d0b8fde2ba15bdad421b58c016dd548573664bc147f3e3dfb7f59d898e832843 8c247f6194c7e9f4d152fa8a153a0ecaa50fad7632ace390a47cef753a72781b caffee6c900127648517d66002c63e29ed03667ab009c36edfb9e8d53a0ef06f 12bf7fcdd19f71d38b16b91f33818e4bf6fca28f50b9be014ff3aec41cf57b7b c9d3cfe08a00e65f700aa7d1dc00331fce9ea3c1fbf150f2847a25bdff7f73c5 4106a73814f9440a0dd17c21f5694b3351161cbf79af5c724c4161f07c9c52b7 cff255342d8134040392cab1338da39bac3414dc57244601b7047316b24c3775 d6a9747b13c45ee4ff8175804eda0b321fdb434ef7215f5b5c9f6478e9d1e786 b54721b6ceb7db9e2efba0221edcb2b38a59120dcd6fc36c242a0c89656b1e22 3f913d9eef3c5f72cbfcf3d08ca28f5eda1623017fd00619012a6b586ce4133a e6a7fcdc65fa05c8371bb351ab3aec865d82694c7708c1227eb316b059e7a2ba fdceb29ed697abf797321330ba4472cce52b1907a0cb46d1c3ed3a1c06752c9f d47bb7d0b36f58c897f5758eccb053bf7d72a02e8b8f71abb3f194dd7fb600f2 8a88923d96781130676236b66a16f0e85e1eb45541b8b7d7ee4ef32549050d09be1257716d940ce002e62477a9a2e8e46b8018a505106e27fbf5051ed564680ee142bc44d31c75a9b28cd2f0b083bfcb40fb3500baf96eddd20df3af9e9bc70b23654f3bdfd47b6a008b6c452e60d731391c97284fbf6a2c5187134ec36bc60a95401436eea57ca608e9ffc2067e47015d389865c03e05025fff3c062e9c900d2ffc5210be86cdc383032d45bccd04e8a836227794541fc9d17dcc1f9b89fb0ae2ab7103d9a7b3262f722b3d9f0cefbc329717c8e1770bee8f6bf5a95fa04f08179a06f9d2fa26e06557b6c69ade9cf8838deabaab7e25a116184572c513560d22e9d57b1ccd0bc82fae5721a5e394a36c9b49f1e5c388f04ebe0d9ed5fc3b04231275bc3c15de275c54bb59faa2152af8784f88bb5cccf1346763b605e7ce056853d22d1e8bd614f8e53b4843b27dff8c85b3925b933c08cefcb10ca6b08b0813129a78763cf229d608cce78df53fa1fbcf54fda9f53264ee3996cfacda030672cea8964ea3e60b71bfac46e45cb141e2f727edf0d049e79af7b2c3ccb8e9093c6ef2ebddcfed1eaef2bf92dabac583df49a5b9ce05927dce7ad3256a6c6d0215a66e3def4f713bf8248d520c5716b8bd7de7fec112d48e7bb9f283b4d2c30741944fe4c1d3d1ef19025db2af8c12e1540e5694f8c1d87173bf50ea5f3d2d04c98a02808a3ed7c2d3a276725ef00c0280cfb920b02603122f5726922f1e690b8503af7839a792cf9bcac7b53e9290e38baf4581ddea4fb31c62e2eb4677430f1d611d9ec74dff93ecc47be16d5ade3f2e93c056116dd23b2da20c30ced59f0bf13ce3aa4749349f5b5640cf082cbee9d99d5dbb3d8784c772c6cbb5f8c1380c4a424beb0b5c6cb35b8fee2ec1c8d14624475b71c3901a288d2dac5c77d7f203f31f5150291175103854adaf619185dcd2476d06d137fd000bc5e570cbadbb031965285bdb250fcca5c8d9ebdad46f6cd2c41fdefb5ac32ba0d990ce4e69db0cdd395d63efe74eff9e030d66c301eb5a4a03877121d2b804253e466d4467380fdaab7a6c7ac04b2ef92baf62b9bfc7b53dc77c4fefa8d1417f57f72f473d670e91cda20dee472605103ab352e294349a9194e0f19d1fecbb642116908132d70e9b8c614843c3750d1bbf5c1ca566b3a170fb4c6efc1561ab79194370751ec801eae92bd9b88751da863f27160406217c4bf284b32f37a2065c41d1eaf655720bb90ae629b884bf9f9d35daa6f900cad0e24d5c1a32153d2851a2c96f352daa0c6044ff75766c59e4909b827258638234099f47e3018a76e0a17c827077219f07a4a0ae04009b5dfee34ccca02679fea6c768bf7d9f229c2d3b921e313c415a0617c3e7e7c23646b67e719e2eaa1994e7d242bfff933492d5749e81af1e9a4907006410d2206e69780cec39f78f284b037c9ff8bc67f79cc2929dbb96148a260900fdbe335b473f17d024329d7c391c36a5fa8f68061ef542fad48350e84797099cabe504b8a614422a90e6bee2ac1e1d17d3c08684cc795edfcf24aa660e610c9f01fae2557038a624e6ff6f2c2929aa80ccc149dd579be5441b1cf7f5022f05265692ac5a16b0522f403255fd4cc449cfbfe42541112b6581e3e37f11297209d5264649fc7978a9cf40fcc85462dbb5e6b49caa84c5fe66cf3491aea55c10021985204c31c1fc94bcd9532e2340ff0d85dcfb35160a1f1455021fd5b860b50c50aa186a0e1f02972fb65a828aa52d7bce75f5d87310b2757d727fa3f3b71f0bd48ead2195ef3f1a666a1dc7463779bc838d40d1aa961fb76ae0bc68c554da036c83788b5c1671710186e140a73158f05874fd15db38acbd5d4a3614b30a52021398b80f3553d9e5e51bf6c2d3428d3029684aa5eae8f643070a18dd9b76630f4053982c272f855f40f00054627f51b45b2a2f14f4abc1a8211a76fec7726d0da5b67d43c9673fc78397466ca681c0bc15b8feca92734f84d58877c95ae44b0d7c9c37a353b21904f778b370274dc42ac9bbc9d30600bcd5ed30c5d32f39f006b7764ecf558dc8f4564acd0832ff7a803536dba97c1e91bbf64acb42a61552095c80016b41cbfd6fdf2eeca78934a85da3ba78562c10c56fd3ea155a6a70cc09763537760450134a074037e521f539dd7aedc1b5f889c8ef457d07e0d6b9de0e641f3b4506c874d39f9dec2d2371f3535f4eb2a39c1bd72c5923dfcd5e73140c4e167d80a560a8b6228a9f6fc585914515f53cc1537ff327b77161d37c643b08e1ff763ead7260a4662e5cb78720156a1de646b0c3e5482bed12f9851fbefd067e10787a4998f6adc4e1dacec5892711d4c36eedb956de86e73d8ccf45968509ee4bbdeaf00760a8be6401f748b2e0211623c17ad1b9eaab2989d002bda4b700e4f688a99f202544a68bf04c2754d7417a284ca55631cc5b2b09bead2bb2ab060705ede9082ab5fb5633118b1994d1b7caca5cc264b544290cb788f51e72ba0a121d160df24c716fb97b9bfc82512c35d9907a5e3522411841dfaf303301ad09e521c87bab1b9cd3bc9bb9add47c0f30d6205b020261948c6c62e84d5a8ef706dba6ed0ae7c227743fcacbb3e0c283b529983dba35a48997df66bc61905a6d0d1458fb47f2de9871ee3fd78fc1d8cc2330f39ac85cf673724ddc32ccaa42b206e1c6b13bb447196d789db53d3e63851ca17f78727b0a1cc353777f7c5e92ed091ae5e0a75dde89e29449aea7ba4aa60f72707c6b25314ea24fd0fec9f9effb09c42e3d49edceb4cfe6b98770dcecf126d96de71f67bff55f11078886b83ffb07618f4e972e85452945f4fbf77b4c43a39fcbfb944a769c3cdef9e9b7dcd023025cfec4327651945b660a220685b47884231132016705e737eae8f263f05d58039151cde8e37b7c67eac5c671737bc195dcfa2b71eed8cd16ab8c795b601aba0740cdad1654f615d969dc55e3e2adb813fd2cc963c9dab50068a3b017dc0e1a040ac27404ceeceed45a80d60dfb9e9006cf703d5d72a413859ec2c559b191ed02e96bb6955209a8bbfe42a4fe1ebb896be9607cb70cc76c48f6f33c61ec58370cc360a48c26498adcf48fcd65c34103f44853c961bab34acfd0cd084bb30924028b9f5c4229a7b07e22ed1d74b8ba87b8e2ea580725331985a88d82a70fd3f501655bd379ff29942c932808e756097b9a29d361732470c883d97347935c8bb00ee84eae185e90768013717358301bdfb238e61b221f976cd40a53c04cf7642c0862718a2d0c904391b3b5887291b60a439bfdf0cdf6d53c5bee3e84c074daf50b888173869b9e641ba3614df0431b8f9088a8060f33bdefc33fff021df3ac38053500cd97607744b3ea5985d1fe5938bdcad5091cf5fd84b4be71f2424932690f8053480a8116e800479d109f55796ac65411c1a5094b12e75395bb09988882005a3dde582458cd16f52204d7302d2481e2192382f9e74781996f141ec5bb4e0275fb9132728c64ecef7cafa02a0ba280afa7c9a1983f3899ba0fe22f8772aa071dc1d73f648ac766f65c09cf50f96ec6380e586fe7d7c4ceb86b8361f3e84806e4335c5afc85d0e60a76c4aa844acc5d5931eac406ebe19383fdae828bf6df0d793a317da8799556a159b8aedd1de1d7c1ad91c564a50778c67dd2c9b599e60219f91140f199659969b03cb8e4f9fc504fa1fee08184bc3cbc4a324f6e298e01c27856dec052fd0fb326efe4bb32b0df32d05fa86d31c291d4eff3f7b3991e0f48b2d279e4138de229877cb3b7afa41c5440abb5aec34e63f46940c251cb4901b7e93fd55779b2ed4bf7638d2f9739fa157fd276c93c3db4330917f03c8f180f2fabdc0978a612bdbfee8b78790aeec6cfd03b12e312085ce77ea3c8f4a2990d491d313c7fc3f7f1804fb72cabba1ac43d7eeaf8f68d3223eefb54f572f9ea020e821fbdca477f3d09d26538238950e4b42789b2eb0a6db552f459952a2da004ba2b95a101e3df9771cea928be3f7da32141dfc6808f2fc5e7e1f75787fbfa0f527b677a377918d2b959b81d4e8a0d92d39adb0a2d7d55e16ad7d137dc63d10b4c0d411360147556a2102fb3f8ad6c3bd33215742a1ac3e3e30f32febee12f00855abdb5be12ed563e88764859079889ee22b853c63a6f85e8324d0d46f42c0fe8d7ea3c7ad56e199837b659062d0a4f06954fcd1a5190d4ae919dc6d527c102c050ecdb4a37085b9fae92e28c7f88cd0780fa7e054018c2ae208e29394b110a3aca40e18e4730080bb10544e2c6a098662c4b2a22af6a57037d1b4e41e84a086717eff3e888d16a3e1825d7d4f69cc4a92c1366125746aae388fb2886a52b00562299cf4f6ee08a5cbafd36cd0ebcd2268cb4a1e08c66bffface3e3593dd20732a57c6d4fcdb14f29d898cb9dcd2c6d223311bc07fbc95a8f4b9c070d15e205825d8c8c3adc712228eea591d76819b0b8387948f565f81a778c33189cb03f08c17a7f6a39eed6646eefc52a913dee1ae462d9abea57b3d3d800bccd5d3ff305b18e879e5f7e9b83662307dab584341c7a375cdae03751a0ab5c0c109f103d01cca50a03ef37f069b14bfe54681275b79149371d61e2d13bf4acafd28705da070ff0ca77b1d65c9b35063d627653900ffbc925b447a35ee988e9c5774e097d00e2371245e7d867a1db8700f563f79754cfbf7e24cfb3b4ef557179cc1198da0699d4bb17476adbefed09ef81d2bf45a2e0285a853fca8364828a02bd40faa70838da422ad8685e07bb57fbd414bbf4474632c93a483ccc21e77b317dd6d9a10a1fe1757adeb8e4935cf251d8dbf21fce367cba9b8ea355844cd26706f4f58f0ffab780109013bbfcc01304278ecfa6bdf77904dc782e05365eb7d27586da2b0cfce9ae89131e026ebeed235b71671a1405fac7e5499b3079ef2e501b4a79ca0318ff054773c6c7e52a3c5bd03761dcc5b7de8ebaa0d0b4e4a7b2f1686d3176002cd7d98b76b730455954e322a82ba0a722bf5080b1f1858cdf422ec4c856af09b2a290946ebe2c06a51656029c80748ee8c68f05cb892dc8ede59d4e10db650822863669e4eb87e10ca57cfe750d7c30c9124b1605f7b0c84a09e187a736ae018c097bb4e16c1fd6594649c4b8acb50a481159bf47372df27ab502565e570b0bd226d3130a47d4fd993a6e8ae646c9efe07d083df19023bd17b64df041c6dd009e76926841f93425cd4f17147108597bd5441fc447ade7d447171e903975ca0bbf395b477a81d46b6f0af96a8f29040415d53dc71dce22fbdf4b7914aa223802676570c186b7ea7b0566475f1fd9f7489696952bbdd9b15b0739467ed447af0ce4eea4a615c5d6bf0a91fce61b3e743a282d72996f8a803aae8374f40f86700931049ed2511a34661a6dbd68221b1b7ebea4bfa532d8e05aa3ca0a9ce27b2a05743e045bc66d189f6484efe0601d048450b3e235981862e4e8a9420af0546404ca0fe53cf7f131257be1ba5d5e14385ee5bf7cab7b25e9ce1d9eb53db9e1c201fdfb7f46e9f0bb8df521d7fa8899365402d33967eaf19e04ca75f8ff6b7d7700fa8cd29530742560f79a105d35707e5ff75187ca9a001c0097451a74ad89f80e870e3c3a9eb8d6f3ca622e530448c60704ab23e4907225971513f54f08f3010959e9a7d2b196f286dec7d2d8da7bdd55a48a2e259a3c96aec0f889bb58574807975e9d49c219b6f0987f54ce68400b39bd9e51bf1fb34d3c2be2519854057800368b39f0abbff009f74065ba372308bc0d8febb4d777fab81c454210c65681082a8ac8c36f1f8307412e1b547f1d0941929549d8a4f69f0b38055bdbd6348501fd3b9ac39f1b8700685633f776d0dd0d6616278a079f82ba3c91e91f48523e0373f127258c417c964c910a44c16b5602ffcde6469c667e0748567bf47218a60d510dbb9594ec85172f34c4276c086399a09417a132a4de0758d57cfa55c24a0e869c0f1d6c8d15dca40960f9f03dfa5719713b631ff4c34b32325366bc618a07590223d43b90f1f53619ff75fb375f88d515da422c42b346f4c23855e1a3eb05109297cf917e3bcadee24ad0ad925c317d8a090ec8e30c1e204b15488e6ec40b2be160385c9b92ae49133bbd0ffde835e9daebed2b57208a4654462b4b80bd08813a9df600288e5de0bde6fa5c2985e8d633fe8594c20c7166ee9f8b257b850050f12a93ed6a16b9633210e16f678fa221856d8ede164329ee9713034d4e3f0f65d34ab42a3a54c12f75022bffc20a4c487a430f88352b332e0a9500ae98c800bcf5af160ceb58d3470c824a65df0d0935a20971830507abd503e206fa64230851b947d81131a6cd76519a27cff50abfc55fd020ec998b042d30e1de28f87d0afa3e14b25908460f99792bf54fdcae4b8e8a49ed03208e33d12d4a57b6dcc80bf62b9e224132c1d4de6812c64f7166de4b61ef35c882248071323b07842f900ffc12698d9b853ce1014d13e46aeaa76a716f8f5da99960c8d928338f9054ad0d673b577e6ac58902b94b1218213bc8cf69f0562ffe4f95dd9c24ba078388d00d35edc1e7931fb09aee6393b05e3b7e7d8277c24f8ffd0d6df1be063c80c4df0e1ffe3615e24f9a61acf6fe7d12e62e2734a7b9a74c3dc57168103bae93a4e207de40242922b28888a2682873d9f0fccb34d543730c78f28905ecfddaac673204f7ea8dc6776b7f430d1104b820cfc4a5fbe72a1299ec446f0a3ee36a3778cd06a8f7fbcdb008ac8fd2d012a3a05460aa050e897b1764c06216929250991cad007e5a127f7dbcd629b2d0d21bca164afcb6bf9941d9775f372b019051c608560f7ab38a1892aebdd2f9b3afd6ea73a40c10af27aa80e49bbde2bb22a6c9f2d400564dd1d980adb8c7808ac95ec0a291e92054f654deb3c83b7e936eeefc96420ed6277d6ce33eed5cbc266e762ff71adc512f746ee594f954c660f2399d334c084f724942c06d97df6959ebf3d28fb3f5a3bf3b8970369079ead866a39bb0cd0af0a7483bdb4d29c7eec8949436359ab0ef6ed83d8033d5d677c9d68b0fc14801b70cb798bf908180670ec2e5012d706deb0d07ec2e27459c1af99b13bf45b102baeaa261cb0ef4f177e78f27176ce551e7c4c139e8af153c948082228adab30bea5a57163b0a73f444308a131bf6ce27acef9c7c71f697f27c150e58005faa0e2516b396fee506913541f40f9305c63f68955afc931c758b59aa7e431ff61c05bc7fa0a5c533aeaade229c2090a46547b227811c69adf7d98cad7fe0089d9f04930dd08473029950120ad43064cadaa3dd0938f7c80d3a7ef622faad2dc99908b5a915b20926a40ecbdc6830ffa3bff4454f6d64638b6fce03da935ac812590d57b6f5df78be8f01dfce3b76a10b9f5b1071f0ef95601683a14b4452e47f8b050d30e693862f33f6f92005dc7b4157719dd67926d40d33b8b7e3bced98346d06355a59c0425658817bef7e31a75ee1e3a272a72353e3eec0826c2bb4a8d709076bed3b43500467d61b0624a645c9bb75fb7b6bed2b2cf240044746ea8fb64607cc705c25a31d2ef390b218c48a5c2250354c3e5fe8baa52a5cfc7e3e27371702a3fc3c5204ac1577aafb070a74ec10292b479c273e8a307f60ca760d28824e0789c87571554475875d7e8b73aa9520b5965644587eb3208c69f6e93a5a86f20bcee2a20b3775b1f27cda20062e91d3637814a5e4a682d8a9b3df7d5d70f3360528f2f94fb9442b0d1ad80967b859c5bf0772f8a194c8ace48674cddb75e19b0a91bec387fa9fa176ad4509d970b348cd44f5e5bc546ee512adf0ac69524fa2049c4a4c2e4f20f2518c75a22314de5cc9a118bc6b409fa362ba0aee9c375c530188a037d2a565319e956eb02186d8f33fdb2d8bdf3ddc34af8ccfe43ac1e0da07706bcda821e0684e7b1a4cb11c704ecd25ee6120cd26bb0695a24e7f8da87003abe07a93c59048e3c93781a3ea24b1a822df868570e893b9c43920bcff1a0f0c5c523a007d676bb09941f8608e224e4da46f2b25eb00cdc650b3a7d616556200bce22fb79b60da669d67335bac60916a4e46f0609275ed350d922e3dc1bee2000a21057e5ff6da9feeca2c4c707ddc7d79962c14f218d04d91c59e6fc9be130d62a4b33ec264719c49f4d50085e052be95b7bb3bcf8db50c0edb8c27e2fdcf0319fae48b6eaa98ee8773b01820fe0ead17e237ea4e39814fe00773bf30a3b309307ae3502ab3d4d7557195bfe2e9c0fed9caeeb4757c0c8916bc3d0ad0a2df09f755c2d274b074eba25e90474bb6a92c2a749ef2210beb8312294a6a95eb18081899aa560ebc9a8065d005bf0bffb2eb5aec1868959c8b1a8d5e391704ba0605f9878d70ffc7be23e81622b8f1887e17f95e4edba2794c0ae2f396f6042b17052a950be957d7e7b240295112409b699cb466d0cf458bed5d0085c3d68eb15f0bf8fda3834b906ae916eb812b9cb755211c851b77bce07981ff16cee89b584a0365d71e33c36663f50f74f84dba6743aa46592c173dea87d15d2509e0d92ed4013ca49351916a574320d06a8306983bfe78c4d66f9d47ae95a2fc33a92913f602d32fa774c9fb67f9d03faca3df014ede1e0360d4610bd73d579889ffb4f95009abd55242263009f7c68d3061b03d747ebfd60c905779209b3f4009882171d70a6dc603207190b78be65e0b3b3e8a6b43f8edf1d2307705fc45ee622d8446b806d7ee383a6ebc60907b9429dee09d843158bd846e1a643ef59c343d1a5ce982056d5ed506e52016e2fa3141b50ee516c1d34be726d72c30fc695609dd27c64c02f3d87617f0888e61f153edbcbcb8fb02e0b72fd7dacdf5d0e83602a765ad7808d2ac8f541ec2c030d253ecad7d395e299bfd46209a9c1050ca11c0e6436c190ca92bee47672acac6a474e0eed615621a9486c5a0a7accac9b06dae5d68db5e029799716a17e0b1683d0e890e5e019b65dcfd4850efb433a0ef52179d2bba0c0e9d7793dcd66474fad6791323e079bef98d9ccbc5331512019295046e1b79e70c112a1cfbda8dce72dfb0f39d77e5ebf0a2289414ec127ff60d1db93e70dbb001a4877bc54f8ed46288da34724eb2c170eef356ea06ffbfcb1d500ae48f9e2102a4bbf577444608d6b00eb73ad3ab4b7591a1a1f022781574c455a297a17dce0a373280281a714fb5b44aa388090a853b9f43ac8ff3ee450f78b798c585e63008094dd7d52218fdc45b0efa34031959ae11a8b211ce46a777f9b48d43e7d54a031e88f249794e3e5c933c55518db167c7ada9d081d17e220097ed84aad390a60861b527d930bce524ace37237a7edc90e14515c7b83c3ccb5913f8624e2393f0d9cc1f8006ce6f0d9934659fdbfe9a28812f09fa74b3c5e7161a31a57aa266c0f7bcb06e6bd1425896f24602ab0a78e8cdbf7a52502cd21f91c6263a0f1f697003f203b18f08501e847d0fd128aab467a4eaf217cf7bd209aa24ecbfa926e760e22782081b0d3980e64cb7748a330b54b64396ba65a10c4067a8fa8edc439b40a1ea7246e3675ed9ae05780d5c710e763f73ac13747da78808370a856fa4ee30d270eba9aa2bd90311c9799f807403ef184554fa0ffa835b9ab91a77ac8937c05a0e7530d4eb41a48442798c1eb3aa6f7f4c81596767b7f2a58bb700ec53a2e01e6c187db331e02e9c517ec8c31ce15427d2dfc50d1be5d28b6108bf837eb3e0cded42fe6c9446d8d4b2d0c39b6cc71869b1c936d452775b65241bab77f7902057846644e20bf93512d4a77b067a96f92016c33d233960453a86a991a5938fa0253b57b1b6876420ab044c30e52dda87dd197656cfe94977528b6ea796f5e2b07d88e84dc76a5a90c96f692057ff561220bc3facfc49b674de982df198077ba0741382c6e9aa43909721ba3e237c7aa2aa1a591878205da3d6fe891711490080d929ac10ee912e91e01868224ecc93e407fe749819c74eba9209766bccb474003ced58f84d9555f9761d107887325472225d0a970c5ab7a9e3979b5b7747a510aeb90634528700df1d94326762a5cf36d4bfb918d0cb09533b8e9a832ca1cc9065c595ac0be36bc29951f09eeeefdb65bda4f80195f52c7adb9ad6dc59656ba0933164022dca6b65141209dc6ff6b333f133f6fccfda949534f7a9b03290e7d04a150980bc406467544e5be48f767851677a9f238069e682b3be97671f4c55c00880e0be7a0b705c009ec6617f43f11229792de807d3745cf512fb516b91a9601172f405e6a2f9593afc9508f09a3d16c49cf11da97e36457df3fe01e05239404e4920e17fd486e4efbed51597a811f5449712a82454f756a0c0bb72d1d23530c271b9ea28ff4720ea6d5ca6c50646ba9fc5265b9a50e203c8431bd8acf55a2096be6710682de7b567088d71cc888c31c3432e4b83c6c0f93708ff617f9a97a0f75581c8ad52951798dbd34a17702c7e73fc34654688e3e94bf7b60da1cec300720dc4ebf0ee5afb25fff335e67ccbf9a98806ef0a9c90e81970c547d90b4ca0db779635ffe58c8ff2df75d45fb31260fb61040680ca12590e32f26b3e8320d0e26e321ebca192861b1583542d638634bf98b035a1b015ea02619e8a583181a091f3129e052308f054eda8e26e70f94e17e38a49b19cadd78a370c7b84dfdb303d11ef9bbd65e8ecc1f603fcbe3d3685e2271d468434b31e78bc60bd1af1f5706ae08a50f43e512cb7d99109b4d4942c049b6c8491f7a7914c4e2a990997d4d0edfd85d7f6f566a16c79cf91cdc33197ec14fc09ad8ab17d44c357c575287410a3870cf94908972ba928c0bc74dad7fffef9a46730798bf3ed694abe870bb2a059d2dcb8dfbe48975e31f439460cc467e07082be23f80aed4bff07ee11067d20d7f37d4dd71b64bed38dea14003b0464d378795791dcc3b5cfbe17852408da10040831937e5e55ac4e01955ec585ed2b1a3c91a92da8b1d5227f1e714103e730448acaba040223072fb3431c18cf88d81dd81f76f605527e90f7464399356dc0fe06caf4bfc97ab8fa1cdb1944d8b096af531634599484b9803f352429386d501258766bfee74ba7e05c3462edc6fc4f3c3db4c2a64190f2a58f3932167b2c50130409f59bdecbd3c120aa4b4eb549338cc444ca56b62d18f03736d0ead7299024e8230ee4b33257e5694b005e4520d6f05c5c9f8f2ce5c0dd482f0a1d7f8ab04f7b2f00c2a6bb6ecb9e73d0a7e3724de7a9466d52cbed902071f85e42df74008503a346fe9557adfe744f0578435b0cccb39ef8086c126b5189a98976dc7ed0a9e0e4bf29cf0042438f7b5f5368dde072c05dc91946173fb5469e60883469605f831bb559349abc21a7786e9f7b273f00a385dff12d0141f2e0a53b6f93b8b0a0420e55775789d9b34c5d220d040fdc4670987854d9e87344c1e4f084e792e0ef0b9cb3cfb0de2612b7e1c34e0b6d63b13d4a38a1bde4991f280e27cbeeee405873bab180a2271eb7f6b08d246728925d35188e41669fa72976e84d63247e30b4005af2b3d6d87a8aeeb5ed9a8bd08181e5ca590a4467a4302061b5346802f058d3a05567b00878bbe10a2df5fd2b5c5433eff6bf2de22338f6f605abf062f0ca2213f33654da6ac5fb57beb17b8cd43ae99a07c94dfa444ac7b5fa54978e10522a3698360be4d361589aa43ac7d94d2c97bb7bc464ff560a6e5b276e58dbc0b37601d1992e0f32878f585ac7601357f17130a2108042b011ff276ea55ea4e05dc2740386832d44245d57da5cca9bbe0f2a8c67d6e0b0cbb5bcbd08a989f050d5559a6d29554408b5e080c162349e704980b1517f98c39e37afd09d8d125550d2c053829492b6f31bddd11a709a9b18efbe4671f4fd9e4139c1b391addb4080f false +check_ring_signature 7fe71cfcd4bfc4d28708a1630b69305f3e0dc8c1713fed3406a80532e21831c9 94b632b386ef8e5437de8ca611dfbe9d82c485c260ae2ba0b74ba9385045b4fc 2 6c4b8b689ff06aa58fe989376601d6aefa05ef5c1bbbcb7b2d4ad6c56ed47da8 942e999e5669e9dec6d6677ecca9600041b5785564f9f516017a23f3857b1623 666c5a073a7caededa9d3232bad63d037a9237b682a2f252fb9665965c88d2084048228093aaa00052b14649b375c2ba2f01255521412c080495dffecdcd4c081a0820318bfcb117f1e7eb16ee56f6bf53ca5f949d79237e3cb16a8aff848d079b50f796f86619e51adf8b1b25a0d6620140520f9fcb11c4dfba30a89c24670d false +check_ring_signature fd5cf84af143873785eb54730387c070b17682e63c7bdf97eba3a91fcc5218f5 3b1f68668de20c5eb55909fead03802204c8ab26adf504bec1be87b622b0682d 1 9e00678b0a3c1668fcceb79d3920102f2961a15d2aed312d366650757af13788 1cfaead89af38a6aa511a4b3d310add913eec9cb6ade8020206bbf339c7970f0296c7c2a8d45e4f1586c2c7352699863c6daf07d76200feb5d8c88e231e1ff0b false +check_ring_signature 2d2969c91cebf439fcdf2b6b5b72bb72af078412509a8407f96aa417a1c1c18c 383cfe9d434e0a5db4241df07be01398497d95c41332bcf17f8829a3391f565c 1 1d1facdc11930115d79b5e9ac384517390cdfde623b6235d7ab57dc0f0dcfbc6 3879edc4a52d9d3152dc66ee0588d25deac432d32802b233b006cc7c07b86104d8fb4a3e88367d134cc045a90ddfe7a12341489ae7500df3ccef7d0c561b0e0c false +check_ring_signature 4364cbaa002624481e7bdade136bd2383eaa6207d3d87ef20492f2fcd2c36403 ecc65cc759de3d52039ff8ba61ebb2735fe3f4e5de30e43fd65c1d6ecc8b2c89 2 f0f8ee12bb184273f1c7aecaa2c98c81b92ec70ecaa3babb1621f21991d6e24f 658866b0b21c1f0cfa65dfa03b03a1a6925bf8d67e255676e370b217a6e1bccc 69b21734a5a6a26a4facc9a51ea0547d7595cec1eefd94625b14a2d7997caf01bcc328fcb9bc0d1425c00ea8effd4025e9813f735b569ef0cb6731588563b40d37fac6ff782c555c309d2d0ea0cbc7eb66d6f8e8ff52d8c6bcd1e6239f445d083ec0050ceb8833a851ecf6ac4322baafc0cf0089e594bb0dcc40b912e551bb0d false +check_ring_signature 241ccd00454260353f6f060b05fc884dfa66439a1f733c3a7ae84c474645c1f2 aeaec551d4a86c482e50cebcd0f40b0461d956e53515ffb473fdd6f4636b6efc 61 4e63b16bf1e0b200a3f29bd117905922be33241ff3f47980e1f30b78f02394da 52960b3dc38827ac08a9ec60c0f16b1753be5b7abb8d40ee971a7ab958b1fbef 624b3b23d67b19f41568e6a59413e36bb89a133fb1a529431c158b0557b97f54 c79199b9eda401c3665edc82da9c7c85be24091524b26bafdf2d4312de5bb53d 8ca346c9c7446e883d5f3238225f8b376c1e5cb4be3dca774fa8bd3b9f7cf0c0 4ab368caa2fbe6d0a2bd9fe6d6760e763d3f2623ffeb63833ddc64e268b39de1 b3ed2d140efb0931c2c6a9a3f1bab0eaecdc29200b086a49ec2d526c61dd0d22 ae1c10409ef3222390a5f1fa41b59c468ce5c3f7e4c4971edd36282b6b20d7e5 83401ea5543a776a5d9a69a498015b19343a29da34e992658da38c1939c0a8b8 7fae4ee314ff77d8d2de54f4a8211c7a89c6f9896358b8ef52790829c6e1420e 87f6a824d8cd4e8949aea81fcd29593bece1131db31ac02d07fb7776d1602b11 b28bf1197d8265ea1406c7c471a5b006e076b603d72446c03a0adf20e327a0b8 3c0beb3c67eb2b0645e046469e3c6ad878b65e4ffd19cb623f242227fffa9998 26c67a29127ff4546a276c9e2a3f62bb87710a3e014906ffcdac46c301a172c6 63d86f06001c62c903d48d733cc9a483a974cbb197fb0bbdf16aa8a40d2d751d 8b7c43d08ba7d2f9db7db8013051b9c958e47219e2a3b41807c16d69e95ded93 1248598d675d986ea7c9703a8122c903171a7df0ad9c7204c65edc49eacd0218 01271027849b1a608c77acd3c70c3b83494f4bcaeac44bc20e3c3fe8cd989282 23f2d3f5db5efdbc02f43929567d4eb3652eda7437c5f6c190d8239df9b4f12d 67f97c439442ae0bcd298ef1bd193e242ac0677edef121dfd7bad354da0ed6c2 439c211beb237fa8cd4a2f986a637455f51609c4e28c7d927a243f8c490348de c79d8c38e082b24b218cb3f451679bc0a22502f8290298b93f4c86cfe37be738 c97981ac85913f0b221f47ae91fbf5c8b35181bb6e3295b333b5bea2f1470c40 676af8ac00e67e474f4103c8d8aa64fe2a85710eae042d209503053d48895433 ecd0bf632fe97039faafbb66e4ff5aebb2b037e0adf57c550e0192397ed23028 2ff1843825b1aca747083224a473e0a36baf3e6f0cc127aa30057f19ee3400eb 18dfbead7d5676ead5b61eeac584b3d15b458f62e5158a20a56007fceaf0ce32 e1366a9a74958287afe62622e085ae37e6dc6cb1648a8307515d4a903e892afa c09fa545ab4c23f7dc9d25cbc0b2126d574102b389b1ba1862f1c8a173a79132 12fa709f40892efe73dc8e4a12d6930116c457c4d2ad5b60405f94024a014f71 9c9459dc35815d195970c85c11eb919cbbfc77490c2ede2d55edc6b1b77b5b72 bcd85f6e088002398b187e43fac0c36231e993cdd48e29cb62ec98ea4dff0622 7a6e4d7c83cd0e0b945a9f9323f05cd0003c63578877bbc5aaa7e06b940f7535 8dc09f03fc09cdd0772919f21249e7e934e079160b912e2e89a39cebd06e82f5 dfd8a484e4bbfb15d42b3ffb5738afab97871bdd6cb0fe35430814c6e6a47ec5 c8607add2d7221647ae63d1c281d50ffc994770edbd2b93ddda54acb5f78380e ef548591303ea7bf1c991a909c98fe378be2f52f2d86d5bad8e02a21f6c88950 120541c85ea37e81b1327a781b828644efa09a47aba835906311e19e581984e0 db7c1654a3bfc7a0466c3dc09a95abe6004bd819b056e63a276b0a34212054c5 b41ede8d3b04275b795fc89a2d368f200c361520d4a95ced4c8259838ad5223a c0bd4c4469769709f2bff371a97e5102d73c253fc43420e0b489b7eac5548bc9 7f1c6b81c6a096587aa9e79f78542533f0780e28449462fdb172464fd519da2f bc23bc1f027086162634bc2276e5efac7ef8ef920764b57744ba5aa78c5b16a0 6bc6d6fff249b0b49c59877aaac070a78b2c42b39317d7a33e3dacac026bf692 3756ce7e77c67f5521dd721c02740e0503537d73cfd279d91923595c0db84607 97f1d9fe2288874ef6ea93c354b873aabc35e351d02d4b3b482bbe037731259c 7d17281a90b944b7b09791b8826a37e6971ae30863bbaa84985999b1feaadbcb 100e619e9ff75f7c35f2616e3436e8cc92ade05b858b672d237b751b69a13e55 d97d6b2d9c1bf74e4347b84ff7d5c76868c4790e4e0d8409f31cf26ed3fe0c01 e6f48c57cf04f12ad288181f268c955d2e0efc07c4f613e6f9d30e9263b9b59f d80f4cc90391ef834f559daf5346479d782dc90a64360d711b0fa63d5fb401bb 60ad3bd777eb886f88d95ec39a615a1da07b49dc9e964001557f45ff115a46e8 c344b494e9599cbbd954b176ebd5b8f4c4bc0fdd26ebd7ae41098c6662f73dec fe98ba87787ed80dffbdb6bb56e50319c6ee9973501f5b4a86fdb7727ad51c22 f1da69c01727c2417c2f3ecba4c30d3625b1537b6cd07ec6f1ab8b15974072b6 74a659b339515cd008e7a071817c9498290d347d4d71919d1cae477656fdfddf fe6e211b53b4c2a511d1faeb326fd6f1ab84a52e728d0f9f332a71b4a6e83d16 668be6bd80f46e3fa3e869b9ba5fda9a0fc7b5f806572e30a1ab14dcc502a3ba ca87c6c7d937642c877cf7457c4df54945d48d985c5f7c7e8b8081cf97f8176a 77d56ee3f360b0f31bd3a242017a6841ddd10a5c04e6928a39e959aad0835202 2682abf4bb20284271f2915fc3702afd6b8078654a372c56459e25526c0e34e5 fc655e86f332f39e62f4aa1a4184259ba40b5489324a76ac29246a540703bd0a180b7746c5915dbcfbf43eef9c11b84bbfa9d242aba57b71f74206cc8a62160d7475eb688fe661eec7f7c154425e338722f644f0712fd8ff4eaca72b4dc29b079b050b2a6edec345faeaa6e72d80931041ec1c8515054768fbe0a6fbae35afd2e8aa334bcb5e37d9b2446911bab9b92b4bc3c025896e0f6324a4ee9f3380500b08d6b0675630fb0229b5c0975e635674289e362579de3456653b1944757bf10c0b389dbe0559179ef874cf8a2dfae412988743d7e8b34bcab89160ae4d8e2c07b44556f196dc062a85b06c630be823212d9cdb958b236d852a9d529d0caf1f02e120caafa6783344f2b4ab5d09af277c2686491c7451909ff6d2f79aef7ee107a8d4adb7f03aaa839601531ee50cbb546d039a53a4e6b181f6e08344b1fa530280106a1c1b363b1b20372469fb9d4e4dea6dadb51b678876f60ec1ed33458a0702940be974b792989c034394974dac41243bd7cc42438529b896835ebd2b97739d3b0f6226bdf9153136c032ec511b95c711a8a8c5fc687a49499f801e5a99039b0d2cacd0c6df6793d3214b5a5db366023886bdf6d830a85a397fb9362fd70f7b046559a79685b4dd87ee828fb2cd3e9ec6fab1536ed2fd6190a18615bb440bbff1d4a0b48881c23c62e16ea97cbe943eaa97b4c7e9dc4fd536a3dd482ac70a08c078d26d8c122c71de545549ca0748ac471ce722e72aa21de9971786565b0f16aa6548d96cb727ff88eaf9e25ddd81d4b66a1557a6a1d3e7202aced2c78f0fb4783495f0adb69cb975690e7d9404733d6b11240066ed132647cb2bca4f8401e6b84967d45799159e2f36e1b8250075ab25d2d0f67d6f1f8f7c1b492f83900dfef58484a0f943bb8a219cbcf4ee98f7aced3ab0679c5739208a2cad48522f0cdf47d18c644797533c2f7021579f5d95b5b80d65ffdab517833d46372c2d790d58df12c0f0f519ef0f8c8c66bb985027aecdcd929cf308e7859c1320df8a9a0fe0ca8b1a3f015188fb454a78818d8ee43a178a52c0ad39c721d6e89467324306a15637694088af6ca85e219f6b48b7d876adddc6d6b44e96669fff5c42e4ff01c08fd6ac54007008ac31a089f1f37002ef9d8b6d195c3d2917c1e0b021ea0b034991a6f48f40bf5b9a5b7a0b5f732210008fe025af45e49aaa3896567e8c10084770d2a3d6b0ffb23e19641a8c1490c3dd1021ab175726417b4c6c0b2bc9170cb28d6ce0d203b2b576ecabf6c8485a20c998a59903137a7574e244d8282f940fbb259a017c88be7d50efd4ed0b68600608571bc966b507953d4b96f6d9b0a905f3057e71ae399287554db4509af7277f0010843c2b2abb6414c1f56b37485506ef3ffa1333d3341b0517d4294300039454c0db07b8efcbf1238b9901e00ec208c487a3d0f7c348172524c117ca8ca4b2ad7fcc766805e6e70d5ab7cd834e9f05668e2bdf72cb0817f1837b35ff0fe9d4265bb05b004e42cd2fd6055db6cc4e01cf8ca7d06a822c4df8cc871514a826db5af8a4d83900e5ab369356a56896e00c2f31040006a62c1c275e74c510fe0ca14a40d45de803b9089610ded2754f250d0a20714fb8de64b9135cfff0fa0e24c20ba84f1a8d4827f510073ac4bb71f50c6adf9d65dda6a356c2cf955ed42c346517ff095f8e1368902335c9db492357047d3e32c6a4ae79c8fd2c4f46b1523af740a8c2489d5ab547ada157dd69a8c00d127652fce5b1cc386349d686dbf6956c9d4643dfe18067ba4d88a60bf85bb5005611307ff0568372368967a17d5eeae55d84ae7d6039c460d932be563c55ae01e00174caee8a58e5075829f8d4bbbea037cab2411252d9f4bf164fd06ac204058734a22fa6fbf29e267d575cf0ed69436bad7109aced59081dec503e702dd7074597b7f20dc9dfc1f64efa066e3ae951b742f330d733534701fa5515a3a30b07061748b784ba907cd525b28b5e119bfdb76f342c386be4267c853b9a9827140ee36a4b83fddcb7e1c50f91163fa5951864e3c693bf1301812837ef50768ae90746c6d87ed24907427c92fd8f8bd6868af6e299babd35bc7e1dd1c4d7dd691d09a7b47322deeab9677f99678d780dd4aa1d6ba30709c5ab1478cff8e78c3e690bbaaef3ed96ff1e2b68727a7578a1569c3b2ff9dcd6346f3339b2952ccfe4fb0ac55c56509d3cfd8c47fb70637bed3eba3a1f5d29d1cefbd7efd0ff032fb3e903f2c748ec7b382d636e49f3caa086c2c8cc5abb900844c6deb922107f51bbbf03782a754b33a377a178ef9fe7c5bba50b06b1e1acc911079981ab9596ed0a240cf84844954d1ad2c735c151766f50c11af8049352b58e232f0da06600714bfb01423d991b98df1bea3877b09a3eec947a20559108f9a96ed48fc7d85ab15dca00bccff2d1698ef085deb876f327b288a5e0130ab537531efef0fb6f881cd66e0ceff72e2294bad2bde7fc211d5208851408bd2fb15231bd7cefb62701d11bc30d8017765e8f565210ebec0b36be08b1a2fbf9991fb708ecaa66f9244cf9353c0b226b3b4c491a7a6b9aecbf669da7f4b7e21625eb6582528e67fadf9d9b66b50d84dc2c371931c2e2ba3368d54d66c3257cd61b4491abb0c149b43dcac9d3b30739a27f97320ec1eb6b32af128a2539c99290a62d2aa924e1ff6e949b02cb7f08d1e46f0860a78dc055a533d9c5de6c308a120fdfc4f3a04218004ab956b19a0e53a251eda6062c30f025ee66c07f1e6af5508704bcac43fd43ca788299df18098f15da0b76a7b9c951e5e012c524837f324d09130ef47be92e9646d63cb76f0910a26f392a383348f80b5556e48b0ca62a938e7cb4e2dbb2e32d45ad94468506bd7fd3bfe4bb51beb5179e8ef51c3e64cf5f15d003f26991c5ab6f47b205240c35176fac36e76e4550f4a9f36e26fb4259d9057978380399b767311c8a8b5c08a89455cfa0ae8f9aa280e51871a652fb008a6b074082625b47e5fff30f853c07a24697c07373cee023238333da6246e91eb2444da233b0073a7074c7c80872040cb56419dc4fe6a2abac583fcfd5092a16c46ff6f7105ed52afd2a81c4cd1700a8c8559f5b2d42368ded62522b1c2c77e1fa83a5f3b20b6bee15ba7325357804a83d0c5e55cca2d39d8dcbca1aaebe3635272060ae1f29d3831cf765dc7ff1000f1aa620acfa66bcb6bbf13b6fe5fdffc0bc41962bb0b3bb7fa45603edb64a06096877ddae20d4662a61642f2a234f6507fe4429c9ce8b80c083d88c0773a80f7ae4e1873de8c4326d943b9ff0e150b1957ea3b93af7546c9b493ece2d400803947a0f2de2048edb2225b3ce245e66eb055961f24a41c4b066969fa19f1af60b3618de2db28cc8e63b8f64a8c0c226c58eb598f6bcf2a063cdbbd871415f53074e0002bfc30169c76a4cf97b8da73ab2d21afc8723d73cbf242b86e0e132a2068145ba1b4817f879828d546ca68b16eb433f5858d25c9b38ef26099334cd68019276bf5f60e44110fea070d5dc1bfe52bc8beaacbdc4e9fbf7470bfb903a0a00a0803fa6a791ba0b0bcf96a806a6255f51a7c6e9a8c9313669a3189a07b38405d3e8dd4e028238f18467e222f3cf8245fa3840f1551f77ca20afcfb644f0680f250f97f16fc4a82e991c7b9752971bdbf73141f6f62bf45aa1685a6b2a2c420baf26114ddc08681d69d29a1cd1066829021089d8e98026d2cf106c9e79178e0289f597cffe87e3d454ffd144137d5e6a34d1d48878895f9398179d69944fd70f719ee7e056a05063e6b6e43957cfad34cd98b1f04b71745641881b59d1e4f50ad466c269457d39ad13678ca1e5d0d202ab1085c89b269e649dc38bef34fd4b027a166e0dd049e1e632aff086c0096e53f4a3ed8fb75682bc9adee1cf41eadd033b61128e090cd84cbc89c952b2d76a055300e6b03ae5aef5a6627251c3281d007b7b26738061f7ae2aa3636a4130c5e1d8543171635d1276d4c1169166a3060c3bb6a2145c094ed2ad338ded40066055f7ac5c9b3a9d20242d63dd8b3267190f29961d1be902123ef10fb60cbeae74e2bfd1a6ad52c30e59e2fbe654aa69dc07b4c5db23a159f6ba3390322790d13cf38bab2a55f35c520a6bf6471d4d42a005f8eb7593cbdb4674885061ad2042d872fb896e1a83d80d617cee8beceae3f804375d9d863b846ba5c428638edab770cd8f9d143a6b22d3f7fc99aedad268450eaa67d0d5479e61d933f315d514379568a96c0611bbb61b54528939e2c2159b0027ec7e479e73b2c5b2e76719a71c8a40d99877646d88e0ffbc1c7d025ca403058eff1d537a7fd98361f70212a0c009296203f8705696227dd363f21b4c15e507c99aff319f2b63e6be33d91780adf68cf70e8a50f8cc988f38a135e6511a3c0afd4ba56bcf3b32790a21c0fca07b89bd11942040a382c1dbfbafdd347b211d08cefb50fba50b9aacd35f9c074dd06bb242c568b1782fc9a1b3718306d54fe202093a966799307958829661552aa0f9bc9157764e1e5946f47d13882e0f0e5c0c9adfff6e83205bf600574ca54aaa138478ecde445be868bacc6064f7e2decf04a42d2fe37d1c2a91a210e0a0d291c79bf8a5e876f83aef7c446e66fc12a34e07c7090a009b03a5dae08302a2865793c36295752b3bca2a7cf2143717525a640f8b09098b839d39f39de83fe5253a2511903ff6e585b23af2f1a6875bce4db10b9ba3087e74ede6f81e0f1da7e3aed9da78e51f6377d74cf601663d11a011230463bef7bf9881ab081ec8103eb68c6cb7e642d96629b3fa1098424ce91178170727241f40b348fb8c8d2b265c10e1987d9932f1169b6e98d774acbf5c55f0c609f324b90f7a7ceafb810e02f70770823ec8b8ba9b82b54308d40ae525b22cc20b1bba4a039f4ff133cea90e62ed14c728fa2e7f9f61557554fa79af8697f478025a9e30a0d89494a422cea1c65141555d7d3e43995e6b09646a38e3c521222605d8f58f1e18047845d55cc8f7c63d8a2f20619067da90255ae68acfa836cb8c0aa4b6c073ed150c6c1a98fe0978771c04466552ba8ef014692bb5bd0b1dd5720d186c9f5e5ccbacd82e7ed988d10a783e3d642e0efc7cb375b33a555f3c29fd02f141c9bd0590b499ca59a914a91b420008ce7d2b95bd074305cac7656409080465b6739b9452e9883c267e34f4457c885568f04b7adfbf53f9691a5be398fb0574f414b0c3fb0aa607fdb1ac56f02cdcfc9c65c09c84e395980ec24817faff034680260304fae32ece6eed58e7bebc76cb9024b4c0830659626eddd43e4fbd0d16649365caf68ab017871a88881d775e759a42abddd5b9de8f8ac49f91ffc30d10d40f5b550ad070041ff0209a9764eda29fde7aba46eede2faa6ee0da3f7609780cfec55449bf51d85bb3783d41199f6423bf72e4fa6ae64f8dfe445333970e70f0f85641ff51b33182adc3d500692786eb4285e97a7500f2339492bec07302 false +check_ring_signature 781e80896f416213d54d9f1e42d044d815000c3114d21c8a83ec478c10ed95f4 387cef7ce89e3060c2cda927fd815483a8091f6a62562c2643d460d588206a90 23 44bef83047a041f3e1417f1f34c8f6bdb15f84f2a2cb7df01b3f7f2330863633 1d4d39f34df24aa848f3ba1f49e8c88cd4a06461f4fc51e1ec23f494f712ffad 719b14d2d92948d8689711ae426dc8ffde2265f88662116d18b4560247183561 6384e2cb2e16debce187c5f27bd3fe34e35b0b96f2b5e578bc77d80848db4199 ef109560a44b05fba9da4afce71ff6b69b3bb38bd4b0f0dbb78319a1224605de 4d670410bb3eb0df258072c356ba2ac1f588fa9241f2fa125267102c3f445b1c d73e2bcacf76ce529c3c195ba5e22fba744f9a74dbeb26f649645041613b3290 09d6c8db55f0fb39b2f4f00850c4ec49b38b0bca97846cc9d96ac54a808d947f e9624a00335772a7ea5f8350c2f734d46fded49b1091922a393ee2a0d77c7f65 e3b44c6a5332be61fd8a698670ad3218bde808de59fe76bdafe598582cf6e2c4 0abf1c97bdbc6d87d08f4fafe710ce01817d5aaca13bbf487139dc35eb6fa385 566fa5065d1dc9910c25bc4f561de093334356550ae605057d0860040dd00fd3 b1cff04d6e97461ae97537cc2c308d303e80f14b1d3ed2933d344874442c0cbc 8730cf569730e9f323dd479f55c5fe0300d5cc476912eae754e7eb9785af623d 94d40acfe90e2ca68d2f5309ff4b689561e3dd2d7ebbb70126ccbbc1568028c9 0a2444a249a0f30c58a2f7ac912b2352ab01eb162e31cc67299e5a3cf29b4ae1 c7ae8f8ef11c7cf0c91bd034fde7bf00af166ae68ced6498294ab88328d06349 765cb5086afc9966595c273d9307003a9e0b822cd1ffb25ed444e59a9e55c6c3 09b0c3df608ab6907eec536dfef42aff7a16b284e510279d155f707d29f05d21 3a695f083c43f73943ab1177599e97b429f71026f801b58d93db2a19994c48e0 bcf596ea47f26b6216add66a9ccb3a856be54e65d6c83c90c17f2dbdb7550ee9 cd012dfc886927998140f0a26d443f53a93bc3c65612b8288f0870e8de898a10 d652a902548cd881fad1aa660c4a4713a3102bbe8fc934ea66c69012468c03b3 9b7e48984f47efa8d19b90561c7d99efa42235a9ac5458cbfcd1d1f92c0a1f0cb7e149c9d08c10a27c0f3dbd413e6fd06b5063078657bf697cd0912e165fd8076b938ae2118cf15214b61a4b20133ab67238c3fe8ec66f39d03c1679ce57da0019d4f6f5949a1c46ad26db7baf0028f54a9adb4812c094d627bfb50753a6eb0af7eed3e29853621e44849c5d593d4b564b29da0ebbf588699c30f49b49e438018fd6c374e9a6d73f760e076fa67092db7f6603e1c63a8262b62d2f4e6ea9ed0ebd98d1c9ec9a8718ef09cd6b9ceacda574a1b6bd96c82bfc82566f0ade5a810354ec7cbca84a179ec707bc0082aeac3e9a948631f7c3b48105a5c8ee59b9af09aff5ec43bc3e35a12e7e9a082ceb44dffbc72eaa41e29a13a93ec6f3e5605d067c5b90baf1ffa02f409cabe0a8c6ccf6468114cb2d64def8d1f7d00f047e880d8ef51bc34f8fecd5f8c24d5ced5a03eae8c46978fa324bd54fba85bdf22f320d791c92f69545ade6b43599a972982b0d407d7062bcd6505c3e598c94e56b860cf42d90b94cdfd6f5e9af3e5b8dd43bc37775d0f00cf8d25280d4f3d85cfc830cc157bceae38d4faffecae0a820b78259ea827a89125fe89fd8616a23f553b40f4e3cd0fa1a1572106db1434da761118bcc64a45b793e4a7fdd18de452b122a0c633178aae88aad9b33edcd8b8b9c5f18aaa4195d3e42d66abf587eb10b18f40175d148f4685bb78c9217870af9df5b38ba90cdb92bf8c8e697d070febf7eff0cf66f1972b52cc05bf74d0be302d271568803380c8be9ae6864d32691a0e8f80b8dddbe6c6ccf85b8203b06af1a9dd0dc4daaf93efcc84d2be079a7cf40b8d90ec161452f2b16710b144daca2b7ad3004582000ea045515f6fb178b85bf6a4303886c00c63f3489ce257c2d3eab3fa68356ba1d5ec5e1ef57c7b2d03cdaeaed0d78e381ef3d26efdc4b8784c58fdf88f4c8534b8652d87704b78ea35e8b93c600dcf7f3ecace696add19cd0eba3cf85df9390b7f976b434394b66f7b22e810005f2e7632b0c83eb29cbd6fcefeb5131cfeb3589aa1b8f82a6a4d52ff6f19c20091049b931fb04179e118070d2182cc4d2c026d6573d9e4f847ac72c2540f8840c7e415642c117e86c5f605907ead5d3fb789c0ec2b09d312a9f2551ea55f472072ff8235a3045d2d5531f74c648543a045d285572c176980daa829af98fe39d0e370f7d310a90cac299dbd4deff2526250e9831bcc0c4ca22bb997d50745d180a8e52fb056bc5fe4935d437a1f78b1b38f1acce92bc5a5dd449df23575003c70fbc1ed05e52ed77eccc748ad5bf56046505a8cf8f63f28df40b297f08829c4c0d6bb24c6b6158febb243abc38f35d4e8267532baa57c971c84ffb5b61bd9c6b0110b42b9518a27f0d90a0fa9c4cac0543cdeae71211ac7fa863d590d8f702490f2d3657312caa528562a665e21b925c088457cc1d4f726e851af40ee63a1cca02e433794c2b5d21e148c27367b79221b45015ce4e8c5b4f6ac7060d1e0ca6c40060c761b62887aa5f447120d41c590cd984be14436122f9b9ea86fcabcdb23909eabb02bd473182031d277656153965fd9cfd3730e0ea42be003f7e96eb3e890589cff25bf9c704679715fc22dc8134ce34d68e172c4cc56bee6e4b4cfc3c6d07e592462c156dcb13edfaa51087fe5e54bb9e816b9f42d30f997f4260f71ed608394aafd1d65ebac59e23d61502dce4bcd8705a4866934546bb0908ed0417d906284cb73f6656608cb7666fdb8a9bf24cc7277876545ba4e861df187172a7f608bdafd892b145e830c13281aa6d8a86b60639ff6c6d74505c942f71c3c7fe3506373ed497dfef6b131089b90ed734088d0d9007c5ee32f80e132dfdabae99490578879432f8c4c4ddbc66f9dc1e4a6164f0d6aa1f7ec344122dd0b324b82be00c73a1b55124b9fb2b75e0c91138f5d0728a7717dcbee2e4e322a93ff8af8bf1091f648f67b82121c2631360027afcb9802527e82fb75cd26e41814b8a4c1c440948c7a5de8f3d6be0ea01234203c3d21da31a41cc65acff87a0534ed9726c820e false +check_ring_signature 000b4a1fc423d5742a942bea37d6005d640d6faa2b65b26ff8f722e79100d7a9 bfaa6aed73122e0c9be7d0fb4211c5926e7735a1a20ca53a8ec37bffa8b54be8 8 932cd6b55df677651e9e9ff4edbeded2dddff88655ea88c9a337a377f47d0bec e391a84b0ebe2e503c15cd2d73f9ba6626144dfe6a5c3050046a5ddba32f3dfb e919a82a45f9a7ce1c0d55636f5be66463e54b4c366c33cdbe2c94e3d8e08e22 232fa168abc3418f183af6cf1b2ccf5829b80a178a600a2a8a80f652ea28add9 749e54a769d5ba2c621cd487498d06b5293c9b70762ef8ea0ab16735d8ddcd82 691db283efaab70c687bb546ee95dca1cd34c70a7341bfadebe3ce010ac1262f b2277d822984d7ed11dbf1e52a70ee16945bff276c10aeb8b3dee4918ad21ce7 4d3bb2396310cb6e8bce54e7e1bc2fc10476df38a7507d2e816abf843780ff62 8da39139e1a7409b5281807adbbb9fd0e206964ace332095a4db4a6ed0d6d9064769cbde73d02a5226e387d4c71f732c9517c8cca1bde6e8c3a582583c3b23055e26a6444ff28ab33ba7646b4aaebe804a88bf9af2361088617cf28bc912b2028930a3fca3edaa8ab616a791de763d3a26bbcd0875691ffea56f8632e6e6c9b3140d0fdf2e96e53c4489fa9c9053beba74a1547232659fc03d23782ef4aed3405439c964004b94cbcecd1f2d9af52e054330dee5bf6dacc51291c0cfa893b10979e10f0616683325283ad9ba7cb408fbff715e332b4fa2674186bb56d148a50d93a8cdc34a8d76bce4358457a3fa52eeb93665f9707a1b06302ed7cb8ee68f0dff68c657c9af0a5953dda9b2a6f2fa43667b6b817578828ea7163416b551fe0025909f0294e98c92a53d1b0b1715771a64f4297bb4c7bb976fc95a22df3b790ae2996b97ad3c24eef142dc293f4901a245f06bcba018fa8ee402ffff5ebbeb047b1eeb16add167a4382d6771eba52e8dcc022b579149363ec09d29bb998b730681dab1497ab3b8261b1fabc392801cc99ab699175fa082608e4a671a7616f80a417f3f6936ad167723f529409141a6f15ebeaecbdaddaf8a49ab47c9267c6d0e1b0d5fb39d268e57a77cfd85f9066c5330dea85c04ab6f932f5ee8d7835469066a603e2e75c4211957d3c191b406e789e48cb34b89eca4835e36bbbc4552d415 false +check_ring_signature e364fa3e49bf7573476d7a5e9156e06118e6131209edada8f125c7b6dd049730 c7674aee8d540152894596556e540be2d6bb4bfbb1a1844b83a213f71889460e 32 63b3119842fe6e41cc8c81b1e846f060ce70dcd22876fa5718918f349374f2b8 6fa2e045469b68f69d2fb683f69cc1935bb3f07e9d4c00d968a151bf82d2fa43 b3a41c67041a5b291cfa1fd4bafd2b57d6f95128ebb91b24638b5b7f9047bb4c 1a9c0d2f581c729e0f726b722952c7bc922e403f74f43bae1ebae3ad600c0105 cf726d2042d682ba49f4ec9ca91d0c8b84fce8811cbd4851659600c294dba289 e9f94f3a30dc3abf7c269a903f7a27e1902dfbfdb7219999d56ce16ed78e0ad9 c536a5917f7f0cfb9ff76a10860f5f5b1e82459512669a5be8b28b02325d4b68 ee126f48fbb2f84599ab455796d5d42fb72e4951e7bf53e5357b4da619705829 4546f0688c5cc9a1869a5d214042c48a5af6ab0ef05e33f461611a4a76067d7e ba755e93c2916faad323ed08061f7db8151f9c7a273d44f5c9da79d13d0f32d7 a3c5c71069d865db9d867bd767dbdafb4453afdd201273497c3846d1c6051863 d5933cf30c7f1ef6d484c03e877aae9639b48aaf6a94fb33f015c8826727a289 1f149225e2832af22e2efdb03b3b1d321ecbd077f4eb156c78eb47370080cbd5 91ee7ea25ce019c7481bb84da52f1abb75b84020d4f218760ca82da5999eaf7e f559e4396512ac152443f9b91c325a47861f6ea054f796a3dd563540ded9e47d 601540c17d38eee351ce65acfbf1a50e85728dd5355833f42b1a1d43b284f735 1bc131e039015ced734d30a9aa3250ff0644f859120d8af66977f5acc5ba4224 77313de646e30099699a39e5fb34c39d34d3c1ee8a8a65b6316a882fe32537f7 371fba47cfd6d41e7698b1486c812855fdaf53e372c0a88c93031f2a73e6f60d c63b299bab3a77400bf45f3f31f98fffb98b08c49b06dac77036af28369d0dc0 172cfbe597593e4da8a9afe780d90aadc21a68af197074f02eb2e2e9766a020b 10a5d2c819b68bb07eb8c4a94d756ae0169c6297b8e30872f33df6b76fe9c437 26be3f7b18ad4e76dc963c6a5eae17b4bd6ad81af76448b4bed21bb3aafc1111 388d2f886a21d2b5ad36623efbcacb3392689a22f6eded40ae184c28c883e3e2 38511e86c2be6d2147acbb341e3e238bf8ac76d0909271c6d683450386b51412 9e20a8f3d0ffe3d3ad32c924f388b229926d05fc6a101cec61029fc4cc368a77 cffa1498eb6bb52664acc03e8005be043320f20cc824e63e87a509b8f5f3c043 ad256c6a767d503048ff421479270358f2e2ad8331cef52990af98944e9312b5 66bde3a0dc6eb69483bc09b0ea33212581dd23a9ec54a1669ca189971c7d632f e3231feac52866bd23a1991cf901d585921f364f0a4d72b6a0cda9fdd8eda578 862622ec4818a03d22e6b9863a65b40d546f7d972442b5d3f0532f7a9807fd49 96e4a49255f7c323788cb0caba039848cafa614dc9142b5517c7b48bf7b8f012 4a563a11b8cf60b383c5945409e489acbe8ac8e3b56293182649bea1d240f604a42159a2d76eca244b7e0a2556d9f759186165078f46c1e5a0fc074d15d1fc02f8369c9103cbeb14f1f2e735064346cbb2ce63e072783675a8f8644140e7450b5a52b21b6429b964e8ab5ebf624b5110df936cfc03edb1196195638b36769e0d4ad8ee06d84128e78e8165d1e7ac396e5512876972e9a1be8d077d8a6e7a8a0052c905a10ccd11f395062dccd292e7273718b54065a0d30c12a5d872cf2f07068faaa58575f2be7558185594dec8132a1fc117f20d515d08659cf727786f1700a74cbe298bb20cc59bc9e2dc27b346e77fea50682fbb572e9bf1d3fb3a4e2e016f5bc2b6d52a9b4eb23f6c51e7fd89d2e756d4673ef6681e7a343ae04d1d710dffb13bd9c969d2541191bbf145b28de1d4de1172086747674a4c6502afb0630ee390e8c12198ba6669f0ba622b7322c6a7689fda2977f44d4190157581717105ae7007790020116e8101521458a85c376905c99005e7c9dd0cf574b66164ec041a13ea909eff7ac29bab62a21429bffcde9f5a7c6f325a0c8cee091e8a435602713e695aab0e32f0ded2425067d08345e58d5d6dd3ee484da09d42f530d22b00038c9d22d20ced0f5a55c9cbc346d5baa35ef9f8fdc5c8ee760f8b4057b03b0404b882e82f914679a72e15f48938d5dfa549f243a81205f6dfc70b21681d250658ea4f432d3e6205184aa8d50d05416b5c246cbdf39992d1624b42c8d4283092d3e17ff506df7e3ed024c0c7ff7a0d8e2b3702fd3ec8b7140ff49f79faff6c02bfbcd19c60bf539d17798204589bb73d3c799af2c8416e59f7dc0ea54ed4ab0e7dfe2df3a19ba2c8ed855a076169ef5acf82f4aa595b8c95794ab80555b41002d66ce807b49f092727598c55c91422b9ae304af8ed0f49508b779ccd3c451a06e7db627d2ef0eb686f3b7aadda8ed36a40c1697ab812149ab18f892a608ce602a8f059d43d6f3397dedf7d4733f3763e8885955cd5da8f062cf53245792a7d046ae19ce589cedf0352fe3c7ea526488ed48cbbb7eba32d6c725b5ef9fa49b10cc5786bb1a299c7d009b7a5e2ed3ebdaa3c11d2b2565ed11d2cfb2cc6e85743002594d9a9141cfc755e2165a5c9f85cea42eacaca78826af5153a80f27ca12b021917bafdc8e6c47effd5b1d253232844c8192a8a62a3ce8e831c07e34c241e0dd28bc070331e1d740c70ace016272da381599ecd49c8ab70873ee9cb9d9b2c0b03819e30530f1dd3f9ffd697c8751e2adc04443540185fb72c4a0fecebd1e600f603823ea8beb05033d8a6399b6edddf3559a159cac4ce94c6406b3b69be3c0ee0bf30ff03eab92175733b9354a0b066290cadeee4ab569fe26bcd6df169270e55818f4a0b7c354adc7b6476ef96f779bb12d790196e04b44355f8e7fd5f8c0a72497816a42bd36ef91e61dce30aa7ad874ba91b2648956ba0e02b9a3618410e3a178efcb190b0cac67c77d22a02fa328cdd3b56d85569a05d3da82577f4d10318a2d9b01599396a54609bff643c68838f08b2cc869630e7f72c55c4cfec3f025a9f25d257830ad999c2fd30133ad2cdd8eeadfec859062eee06e51f4e741a0ac91bd628f7dc4c6477515a9325ac14dc0ce3f734f0ec0a50ba636f64fd58e1001d00b27c5246262e812f2195a82347624a8e3bf411481c882d68bf308cd3e80a05542c46f8637fc4aacc10bf39d8f50468f3528843ee6d41141055056a519608c3268e50bdc10ce97ae0450099bd85a6f1029198b1792901ae3e22980ae22b05f0fe48cbd4f5fab386b4c1adeeb542cd453ac8efe0133bbea1cb207481dce00e8e643b60101d9d8d4b582b10cab8ab556adc642f297b46f672d307af990d1009c0ae68388c6b43c45d12b36eca405a31dba4daa74dfa8dd3d6c71680251cee0bf0cd9a73f9bdfbe8612ed86a4d508633a9261ea3d1722baf275e81a7aac09604769dd3e84a5023a0ab6d45d9c85a260926cd6220aec8c45af2fa856c6e20a402035214dc583b8ae7e2abd75eb06d6185984d3bf2838afb1074443ede99243706ba9fb63b807c67b131793f8068a07ea48cf6f5d8e323c4618b23359f70faae096269ead63f51bf3be7a08a142bb9cb0a4a9289a0cd5f61eb97ea69e9daa57e04d514931a678fbe7937d16c3c1f13bde9202e6c61e877b5f5d19bf40be87540055882101a1c766d04fb3a8014a3c24be15233fe01f227913ebac3c1be6ec95c0f16ef2b2727dbe0028818adab79e48cb47b6363d076a8713bdca10abff108f007d9639b36002052edc6cdfbd6d01483e30a4594469a4337bd1b928afcd4feb20cd8d8ffef6f39a9e491282cfaef2a9aafe2999fcd2ec5d449824de32a932abd0c841edc96d7b5f68c5cde72d6f807608ba7464b98df65420e35bd0c3027520507295d32d34b9a36ad6b84f8b6004f50fafc030ff861d7613e16bdb66540fe50081fcd2ff6f576e1598dfc189d76521220318640b63d90d7fd94bdaba950392604b4a2dbaa7fe02aada76897a33195b6662d3eae372013484aa43dbd376e43a8057a3981d997286f1d3f7b0506da8a9316d32b5fd36fa77d0563e49989093e5a0d7e865e161a9255f0ab8d0c9dd463330bc18e2630ca44cac4001a1b5d7c3b9e0d93d30228dd1cff0e3974cb304ce9b18b8a97e02fc0b863ba5a15ad237742f4095c8c86b2afcc2775bb42146a8be36a791c28e70a110f6787b741c9529c531f01bd179b1ec6d5fe6a6477960ce69386ff0664346023d83f7901869f6d23562a08592096c3ad4fa8a647a0ffebf3891c293657c745996496b2911444042015010490dd52d589693ed4488b0494d4819f480345efe1be391a7f2f112dfe824a7a01 false +check_ring_signature 2d1b07f4dc0ffe4bb82e31923cd69bd39cc77dc065553069a9e289d2313faab6 205633b4fbbed29a5edc083e916b73b49c00de97bf71f75dde3a91e6ce12ab9e 1 3f12f22af8ae9284fab480655bb3aee88ead434cfc3762b756afee4005be043a a5a703219a8f5e4095565023bb850a53fe1c2fbe3bcbe7d8633525f2825443c79b5e57fc8b1febcafad11dd0f055e807f0dc10ab5b8aad814fbfffa65f667903 false +check_ring_signature 95a4c7c43ae91ea04b737f5860f7678d97299145cc3f79b245d66bc4ba1d2d5e 7774119e2d1f39b2ff7294ecc5e0afc90809e5f0477cfc52ca2d7cd723b8ae30 255 e89cb55ab3819ea964b539261683de7d1c94bf96d59e5a1db88a48ceda1854b7 3e41c1cf8058727cbe57a32b91cfacbc65c410ac5a0de565c9757d2ae10b57ad e8847c48bc321ae06008607a2631c5828c95fdd076b2b41b68e3ec043e5d656e e6c9214241ddb6be050729c4561374da1b57f64211ded04da982058c05ae1d43 4b05de4b5830506e8a151885bbbabc008c9bfa107b1df160fd55614da1115def 0ae66ac0c9e9705a80d76b6b45b7112fbd6008d651852f7c9a2b195d5913654a fe2a3416c1c42aed63d9e72a4e478e7a89e54a8f76fa049e2eccee9d55b660b6 aa7a62ae8c8a8cef5ca8339a600d96a85e6879797c0105d8e5b2ffb90de180a8 c317be8c451e352098d73e5a688517ed0335fb52669cd0010e1453793bb632e4 54ad3cc75dd93a081860e59a15b29ffad659e93a383a5356fb34c422c4064ae5 308b609ce402cfb67ca6ca5a1b5e06f3b318795beb210525c93d37afa71906e9 8d1354b18c69cf75339aacf42cf0fcb949430be92610818d6fc788a8af4bf2d5 bbf4ef8d4e6b3d6de7ea2295c72896b8547bcca7e9cec03afe2ba2e1ff32665b 125d0f612a8b1a8100ab63e6420e41687ac02ef8bde1ba4dbcf34be8f679fe07 423f97feec646016b5ca0a0bcdeac0b2818d104a5f2645cd6f2146da1b63b3e1 0c70183a0a028769a336b92f1410766d3c9b1e8bccc8256c30ed4ad067ee7e27 6fbe828283e0d78a15c992f627652b15c14a1dc008d6708607f72009749e686d d4bd8338fd87f8acc34014e7336847a9f697678387155e528b492dc20774d29e 37063d3bbdf75d1ff6680632d5a89bd622c15f7e165c1ec4d3c0b55c1da17b76 7c9b21f1784b33579f10412728e3016356c1ba7a864784ad40eea186069b1fa1 d9e43fe0179efe7c138258d1996fbf69d43f9a6d5ac2d638d0fad84aa194ad9d c43d2babbc464e064f728b2865ae0f307ba06ebb936f235e2c0cc0a416595edb 7e27f9c82f774c850646171e6fb6bf0ebec337cc6e9974407db51b80aac69e47 976ba501331d7621c59b026e652ee5675aaba1a7750f0cc86d77896b7e28f65c 0859cadd7d403c58e9bd22d9fd4df9057b9cf71f7f3f0f730121b855e1224979 fd68a615e8212e395f73212bfe7de6ecdcca212a692fdaa52ba982dd352c7891 37d91e2302d3ffdce0fe4a088264553fef80fa35fd0752cff1c9b9906864c92a 294d65f74862095b6466fe143872f63dd9d0caa09d0d4ec2711eb4ca65cfec11 290e84da7b1addc543e34e64fafd367456b94cf228cf290cdc743d13823cedf9 4dfff1c25454f8aa21fc3fcd1836729b439e8b06adab50749e46b6a51f9251e1 7331163c9a06fd469ff6f1d01402465f10e3946d82ad78c5269b98427bdf46d0 adf418c769d8011c079d18e0daef62b807c323d7989edae81537c81e72a2556c 4e28765616711ed0c3cc0e722f2a580534f11e3714e122bece8b7d04151de7fe 368d7310b433c91f146e267609ec0b80f3bc4cc980ad241c0c73e2a397f1d260 fb777326b5139c71b1b3fdc6ce634d86f3fd237ad98cd5fc68a5fd80bd274909 5b4aecb1060703467233b2b15dea21880248628f1dea7fb26ebc9f7be8d86e9f 9ae745a782f5c520f1a4f7c5e51327549d5d1fc16e6fd2429bdd057739999401 7b4696fab22c85ba42f23e7aa21df60d051381b87438363cccc183a03b178fc4 8652bd0fad51fb12602bb0c2094278ee661252c31c3616fbb5fabaf28a518e7a 29cab85abb7a43f623e6de350f15de29ac8a3523a658f76ba9ef39afa1490b58 c2557e408fbca9c3973e80d14f23c0dbaeaee2e8cb92a9f408ce1b1d83b49bdc 9ba57544cce251db727cbad109ffec15bec9daf0aec87848c0d734975f46dd82 386b8a8bcc07ea1d9b410a252599b939a9f6e4e528a7fea6f2ba81a58068a748 450bb136b5fdf9cfb435d54b11ea9e5fd0a6d59fb6adc8ca61c35131370f9eea e7dc0f3a0ec681f9108ff057254bec74cf94b619b58784aab8cba8e5403f0fba b3e79892079422cdeefc7ef545c1a69eac42320e6cf620392694fa3d091d1d88 747856a239cf2cffe01fb82f0cfdffe86fbc51ec838bf95e7fd137a35fd9fe72 d394f21cafb3ad4ae399e2ac6f9122bf282f816c223eb0b56365729d3672b3bf 3bdfb3484150dc192d439483f2132b1da4edbc051560f358af01ca4518ffe57e 1fceba2e64af865f9dddd1fc71164fa29fc155a99f23ca328ee3e3db05b57ba7 3445a1dfb4413ae96b16a1447b4e82ac92277d9d2dccd027255e0837fcdbb91a 6314dfce8db8d72111a3c1eadce0856522a8ad3b867685388f40be1a59d3b94e 34fe1e13075d4bf55a47b7cbe5f0e0559fcbb2a887a72752145d7a63da54ac3c a68912737942ec848a616f32230d6fcdac73aa34c3182ebfb7a409a84c459811 2f889135e650e8cec5fcdce3600455c5e37d55ba1685978d76113667eff03927 f9bfdceb5dec0936ebabec3c80ad120c1ee435d6cf96ad70352a8e8a409e6ea8 07cb557b973b6bacd42814ac2f89bb1cb2d9be6749c894b9b781ed9e03ee9428 0b59c03d8616ec488a6e284b9cce1ca02f1f9f47eba2f9fb2b9e0960e2ac7a06 36df8ec175d0ec18f8403f7177231d253c691d5cf83f238a730470b0f1a7644e 5c8de2e22872f931ccf9c016753f8a66266f9861ea30641c768a111e6c07fd42 aec6a3b364aeea376de954ec2ed2fca9e2f05e130a622563df2a43174c38a6d0 271499533041aab5f6f3fe1e0467f670767e3d76b8cf587d39306023ce50e5f9 8fa7b7867603d24175bd2ce27077f57d328e1e3d99d9e0f17795a517962131ec eec30ecfda1ad8507bc0055277d47bb58ef8c54cf30aea46bd35cec09b4b4367 54e549c2c3b0e79f1fc1f2922526d520c02936297a2dc8df345b6810235198af b375f3e61135dccd6218b1f1b57c1c154232712fa9166cbb5127be09eb5cd759 51a87bad6494a00c4694a22eea0dae32ca917df5e7d00bbccabd6a4572fdb235 849d27b71f00d42e8074b25db331304c96cdb0d35366092e7f262c1260cb0504 5074b769c0f124667212217ba939903cbf0016510ec58128b11d0c3f9ee431f6 cfbafa28362550889dbf14f994109e4db8a85900735d7ec680f6826528197d41 cb32cb7d171b7732fbdf4539110a48e9987b9aa2b5652c8d4958183d8b7150c6 847f15903d43a5c4e643c68a167667efa1b32468141524175d9ac8a8bcbeba89 37e981bc94e2337a34b61b4924f99037a1cd5014d539530a3693984cbc19ae1e 13b902b0955e5d2a7018103fbed4bab57ff9d04bceb424704f2c3847ead47050 7121c0aa47f456a4d33d68feaea6c97e21049572c16197e7f149ae3898b4d09b 792e901265435b8c80fb8d8a967ca2780563044edbaa2a62f078bc1242a9f0dd cb3a92e952217935ca69977a680c5aaf5c631e0f9648b83db7063b4528612dfa 175fce5c02370f07f7d9f4fdf7f38807ff28e782939796c0c86d518f9594b9d9 b123c0d042561f2cd3e60f8c20bbe7ebd3bd55d4138999030ddec82c8ab38180 4a59dd04e20525390faf787ea935cdd66f84a5d4f3c419650a166b9a52f91f7e ce5b511d46b2e16da89f8163b9d306a166a5bf068426c2fda566c3413af74b1a c80218cdacac0c910667b688a3810d0b56b20cfca879aeebc37140060f40cd04 8d661fd20950716496b6b532e38f3e9498de62159272fa092aea59c4bbc8744b 599b820821e5921eb8897e6f2a0e0e3326409bf88c980fe64cae40c6f6a78ebd 03bfe56b13ad65f610c7c4f968d8979bcd67375df67b914e73166394d0301d24 e35738699359343cc7cf1d64e00cb998226a5994d05f4bd51f586727522aa846 4472cb699616029d36e9500d4f9a5e580eb8b3ac40cac96e60727ab69eac500f b89c7fd54bc9146caff4cd965289f6f13fc4be261b7f1576829b4a517d819693 97f191a6f0706baa5fd1135293b13a0a04a424dd4d482c85f4516e53487955eb c4be352aead08db2cc3a018224f36d7a6981cf9a2b4e47eb678f02ca0a28aed6 6895e6d7530538cb6ef9d4042c36ed43de2119c8f905b01d23c0fd011f47fe21 481dc8750adf3650d44f9e7513ce2e5525f95be08b45d93565f93532016e01d7 bbb2cc8aebbd6bcd075ed28050e690bfd778f4fcd3696ab801ccd8c59d677263 f74e1730e79ce769442d530082ad5b1d07bcfa3805931af9cb24bedfbfb32189 9c510ca9afb1108c5801ecce6a9261091fa593152a707f819897975c5e19c1d9 ee3edb53933fbdc3636ad4f01c33d758f9f172ab7373ff3b2529d41f00772657 4f69a5103859e7110ae92929eb61bdc94d5c43bf03b65d7712800f039f3eec42 fcb131fd2d6bab60178c11d03b8af0b58e222dc320b8ed7157e0266b2b99b38e 277f2b9e56400f36aae5fdbc003a8ccb126dac26a90cf5a097ef0e33d78aa65f f43cf7b27006af1c7255beacb00d6bd655c2e340706079cc56cebc6bd6644efe 58ab70ebfe19fcf56762502fd6473f581ce51af9c79c3f7a471b499eae15c43e b2604304ff0ba3cd7b66c54cdcff721f21e6bebc2763aa6c0646505a73e0ed97 24087314286bad6abeaf7f9835de048579c93d47b206c5b84f96d554e4da55c1 3bd55a2571ff1d27b5147e3fa0ef429a3d681446f245f2e0773b9ca7395ec7eb 89f60b4274b7fe87329222845ece9dba31d7376c62bfc95113f9bd6cea1c83bd 1c0dfcb77257581a66e88333c1d2acb1dad1674aaec0bb2dd71c265c14c96a2e e4ecea31dd78aac014223bc4e78fa741666bef9eb3ad692f3dff47a8368284f5 16804b5c296ea01172e9a0c134e211de9ab708b692fd57c0cd3bf17c8fe7070a 9b1ee1faa37a6b22c6f3fb8832ed46b0b8f4760f8c661973db5c179a5e995ad4 2b7148a38b8b05ac9b6644750c5dd936cd09b6e1f23dd994d9d2651577b0779a ec8c493f5d6508f52f5faa78becd5b9868f7991789a1725a5bf0d3343a2b7549 15fbcd0bbf05af1c1364027f960c176d9d118d4788564eb5f0f6a84860166c50 8deaae5a160dfe839055e7b31f3090de8436b8c21c1e5d251f907ae9a127528c 5601e464feec15aa5313e9b6a936ad3f6b39ab0806f2e9599cca54006cac98d2 5818d906629c3e230c623c493fe0c58c33abef652aa04dc2e61111ec45c9daf3 03e3270d1654093220bd2b44d2d9078f6fe7caaa8c5fa7be2baa774ef599d6b3 7e0c06efa56aeb72465708120be53c836b9347765918d1b50b767aac9f9bcca6 df1c43e49d1ec1e911bb8dcad3b511e3abc59816480e815687520d48f95b4907 1cdba1b433e096e1188a43bb21b6bb4f572086564034848c1d9e5dfb89f38cee 47a83f71ec7c5a52169f17b9dbda0e6039ff1aeed1102ba54c2df92877eceb27 d1e75c7444c2b0c65c38b921234f8110cc75bf2dd5fb00649cc9bc79eba7c12b 2e16a1bd442c1daad744fd11fb76ab1fa5a5152c16ca2e282d097941b0a7121a 4694154947fe685647ceb73b35b79f763df739a1a7c458a306eeff1dd9998af9 7238e6c2463ca2985a4954bc46866ea3b204f0b90d435a8ee9d359404ab7d2c7 e1bbe01b958ab8f63309fb5c4bf055679d91181a2cf8f8673686d6e6bf896444 2c43c97176c673805e3afe34f3c056c5beb91c95620566ea67e85f5c6774b228 9993aa7adac8dee508ce1bd75678fc9fb748860ff5a08521bbdee496437d9c1e 00ebda4d601dc7dd9ff907ad3ff69f15ecfe16fff6dd83e9db58b7dcfc3b006e d506a8210145e7aa3f76b6fd06798104e74da8c5b1032df38b2b0bbfa32e3c5a 70a1fa1250b687fc8671f5eed605ffc1bc7dd38c04fcf68f344967728eee0978 294924b9b42f19f237304146338539ca71c70cba2a5d3530742673fb77a0797b 60960eff4d987e108efe9c6ef49892497e1a75846124fd1c3197f3d974e94f39 209cf099b684d9fd11d498c1650c692ce23b3afb6d1e85eca1107341fce577bb 8e0a4349e3396b4072c21b4b66e54ff5b7e546ca20b1a9fe3b4c63c955a85d0c 6eedca2187f56305e7b84b41d9d18bf6107b8f1328e4c1bc56b68ee36f422276 c173810669b0f0399b4113e13d97a6cddf0852a4e1520b23cc653d2fffd56cbf 4cc7ec42ab0e1b3f1eb67e1b49843fd0ae91a18e1ef190349e0f74d70ebc2f41 4b8363d7337f958c4392c10f3ac4e3e28e984adcea0fd297e49fdef375bdefbe 5449074f5beb4d34b2ccb84f4f1e0abc6770ad27b73b67afd075c99ba45a2ff8 c4d6be54b58cefdcb8423053db244fea968e1471954af732f5b5ac35f0d7cbce c12c94501c403d6cfc2438d7dbb329a9fa4a4271faa939847f9ef93919a89103 286d141f4ae6ca186f09eb5b53e52c4456794ce5c367fb9d61cd27e8d2b1412a e21087f2a7accab84b25da4251205c8d0ec718a1856c789adcc88cf097e7519d 127f82b243d00ce96e90f88f3daf1bfd731535f646587406e2f8db513d46eed9 1dba685b839adb85891cd09e1c20c2959c4f942aabfcc40becf5aba393d8cf1a 7c05e7869481ea6be0cde98b04b0ad054b0fe8d48b04a6ae302d83ccb7cc9670 dd331bd899fe9d675dc46ac5c03f93e063d1b7a6f255c2a5b72e204714ee4ed5 c530a2abdecfc4cc4441e75d416f4823bc298360b54fe612ceff7ad0ec1a0deb 99041666d48b2cf7df0c3159471795945b840026226fe5e1f6f2b13049d7a47b 5c102802302f887a8cb3288cef513b571ee5fa8ae3d108e7480dbd6050a59a22 49b8fa845f9058a9d1563d30bbed55e77bb2b22fab05b722f5f9cd2a069a7544 31ee33030e667ce2a1ebb52c5e50d168d927e44a7299aae13c4ee1e578ace60b e686bf618c307099cada12e10b30e6d8b666b19c74166ed57de60fdd1b063e8a f5d6f9172619bdd12d0f77f8e40ab1879aae438fdcdb7a05b09ded04dab20404 b9ccd2db6eca7bd01e6202907334a7d0ecc975c0b8da4c3272a7559424eae2a5 53aea449a21582c87a8e448a4c5a4942c8d829cf6ae88fd09da1c5adf60f6593 b92cda15add56d7ac52eba72d439a33ec1ceba70b8cbcdafbd6104f92787b12b 1dd265335f158d92a6931e41dffd69293c3968293ec33058dc8c4239e64a32ba 9306f19ed44eb8e98cee81773991b23f7545aaf1007fed21bfb4b834c87394fb 6f3ce8e133a459042c592db57505ec26e3c3f402eee84aa5163cf3aae6628093 6f9ac5e6352722cd811fc8128860ab8b99bf1b4158744661607f8cfe0dcb9c79 3d2a1a4d22fc4151ea9aa4d9133bfc77d5fdcbb8d51a8508ec4a97d617fae755 761d112ca310d000527eec10ea615ad0a01fc7e978c9c2c5f4ddbe2a31c09c42 97e939061ca41a2990c90a4214c325c5a0dc94dd27fbbc4985b7c753534532de 8e21f9b146c1dd3d11f3d158ba90278fe2120b0f556b2609ffb3b8237899c9c9 3ef9c6a698987cec26ea10966eda40f17bfbe45b6868e830975ccdd4d3d71f33 63f4747c34ba41fb645a198f2af07c172e2c8f38172de52f5826ab12222037a5 aadb3976822967edb301bdfb9624520b8e19a9ebc2ae4aa6e21b3d05edd15800 af6c14c04d470b87d82b0ead8013a69eb726b55497d0875e5542493fb23e9b85 ff489b1db933787692d3213e2ef2be68e5d83ed5cd33d2fb1617d9efedeee568 31b22d4e42872e1cefea905e0c2d8def8a8f04d9cea7bb5ab10c10eb74e56130 e4571fcd4cf05819b3c2147a84c162fd4112218c46072b0198f84c5b2e23f7af 2c70456feb9ead2ead24aa81effc2b8b71496a3172e11f6bf7bb1505a34486c5 ef794ea4831503bc6e1181a9403cf1a078e86d17539185a86a70481e06a39ca5 1880595954cf81b7dbce989796c09df2c4f50abf5948877fd90bfa73cdc5f539 dd85f6887c5d46464c3dd56d820f3346aab54bcde70fd1c28a70c18bec797231 8866822964d9a4d8a3dafe7639e418bfcb8e3b030d4e82f089fdcf5823c435cb 33ab1cc9573b3a53ce4501cde696393a4bd6cb5ed3dae64adee058a4f2a4dead afb321cff721434bcbc24bf65062e46612923fbdb7663e2fb6a07fca2e9ad47d 8c0076b980043595967220a11da0075d9a3bb2c24d8dcb1400c4584eb90bc12d 43f971b7e427db2a32c9dcb0437ed62dfa25c3710c00275d07c7e3eb2c0ea424 c532a82ff1917a62c2d56dc0401c1aac0a02b9cd03a2a501ae5dd1cbeb565e1d 9cec2017729f48a62b99bceacb5eb992a208925b7e887fe10304855eb690a5ad 9523211567dd8e714c42aa05ab9b96813922d0edec59f3f2d652208022bb91b7 546ea6df460ae3c525431bed5e16bd3596166d0d87c4d732bfb83f39422a17b0 cea32b8f777046ecf9223bf13e42d57d48a080cb962a61048fbbe69da8ab95f9 779f6036fceacaafb5cede93ac426193eb203d3d6de33256abd1afaafe05cca0 ac29ef65dfe9317bf8dc4f15ab4f6536d1f5ea018a8b33abc1573af69d965dbf d40e53d4db4c8ccaa766112554f657f03a994c2f9352a88a659217fc1d3a5ae7 0fe5874265134196d8eab550794839afc85bbd8c9d711ca07d9e996a45a2a1fd 2f5b297a56ecc95c6af4949c8db90b5de200aadd6d8a2c202f7b806188635559 5f4ae1b7ee1bce2a042a5147c8fd2f6e3a157168604b210d26b60064f8f6e468 db7af99823232331f1099da5e30f1388790597f1a079283cc2280e592a5bd207 a51468ee267ef1f3a5f966791787553d558efa5c85af720dc9ab08ee664aa54d dc2224389852c8f21e942d5b501dfd33abb6a5733c679e87658a2de39a8b6f26 37db64702466704ba3f4231d23f9a4f6c62ef3ee25eb8456c0bea7d8e7c01118 13d58ce8d612640e6b60d92425734031955683f4f4993286b7f6b5d48a7dbd85 1b85914cbce2117c93ef97019a37208b21d3211f708825d0592e461cc7127d85 4c104ca3758de37570c61980ed0672f30340c290e365b92875b88683c83847a3 05255dda745e3cc8ea2d7db0714bca212e3a49c200a36f4a82aa487efb95cd23 17e6c16d0f0f52015fff5fb4563d2c2761ddfbbb169b707303712f4cbca41a40 12b17425abba7e103d4b9db968d4ad23256e29d980218c43134be039d8fd62c6 10599c48532855a3c5419da74d2fa76a5ddc4eff914809c05b6d47a7d2cc3d40 f3c270159265011ac80bebe6e596b5e7ee15c14cdae8bcd15c7fc5f3fbdde50f 6729b7a5e4ea47350cd4284516102c7238dd4d02511bf8dc2a59faabf15b9e65 13e81c4211050728acd3bb5c8ec61ebebdcb25cdfeae050969bfcab1d49e86df cb9d36221bebef62606f2fcb8b12fdcffbcb2a4082f2413aa385870b06bf046f d566785b9f53c1eb7f3cda870e682c7cd1301acfcb5c74b820288941c1faaf54 27f1e548378af1e3466eb9a2aba3516b496f70fd05125d540aff4ee54751b907 d9a97bafa96885b96671e5799c00764cdd9a8cb1f8ef331d8ed6dcf17ffc71c4 d77418506039fd6c74d4e596bb2998d04c2216de33ae4d70fabb5867f81b78a2 82e6cd9adca58483471b8ecf6a68294ad49d2607c7b53b0754d30a4213c839b7 5e04f4a820d57c93723afaf33fd0e7f0c138c46761e32b138b4188be54bc5f1e d9ea6d1433bd1dbe68fa8e0db0d295b93670caec667cce6b518b5d4b185d591f fdeb675ef5a52b83e19354cff2bfc5753e4bce847c03a927619bf2243ba2c45c c4e32f395301cd02817db7d5d1ccddcc098b29a04886e497aedb968c6dbfdb32 bf3c091b8e982f5be924c4eeb77602a94f8e425f6482659ed40ec6ba8b0048bb 13c50bb3ef868301ba93c0fb419083f54ed5ec684878da2d9bdcbcbae75ea2fd 4e7b4cb1d6641e370ab4df0d636404f1e6c03874584855376b9528b5fce7ce93 f797820d925fc9dd2a101d79874763494ed89d707dfe7c732906b1b5f8d90097 5378c689569f9f8541a37a8386c3e3ed99d460e2431ea6a253b7ede45073d7bb 5a7f631b25677c5d94743899e426fdf87387dd3e0f8d7c3bffc9aec654fd5840 6662992d99fce95c98d5fa37c77b56ef524281abc755fa39ad0f1a95a23a421b cbbd1f476d8286985485eb13e51ae26a576a919e763d58d55a6bc89a0e161a17 cbab21b0ee3b8d6507e941fdcca2c5c1fde9738997001a4f8dcf80e45eab6cc8 aa406373ef9578349a262d3755bc7ca0735197a6002a3e7c03a6ec732270a676 547a9efa53f5937ffcda05742e693cc8788bbe8b866fa8b4281f7f44a41d6287 d5e0294999dd9df49d352c92de045af2899a1b5abac5b860710488bb0c8b86c5 336134e487919ab0b12fe0c72a222b19db25bc7b703f93ddce1b2b84151d23f2 fdcc445e1be5c34bf84505c485fe4ceb36394a398b40b1996ab8ec0d3774d83f 57c16d1f7ed41ba7847e263781f0b834e463df2d6188f46780a2c3c64159e590 07735a3b38997512a342fe34b52ac5174b27a8e454cd59bae712b9931ce8a406 eb9e6749e31c1147d60c560c2cc1c956a70837f7f6409fcb47fa5936f15bca74 038f2c27d8a67a7abce109add294628022ee09987384d7b343c98eeb7a82f3fd 5daee915b059330c104c16b90f338ee7be276b47cb8d403ca737357e557b7f47 95772d95507d67ca9de2e469038737996a9a0ba363d71f967bb67b74aa18fc9e d02b3ae8736b9da542c98780d00d1fa3f99c65ad1c814aa8681a775c85ddd061 c617ebe172e9dcd1f3cd9eb9a22f9bd9080c4e078cb350bcde35cd0e07f6731b 6f7f13cff676a177a4fc083d9b53ce77d39781354843bdac020ffcb3dc25f7c2 31bf2f6b9dbe774fb6afab5e93c333fc879e14afc5115b820ed30c886d29644a 1cb0c5dd4e8b775e96bc76a66ab4b46d5f4da71e416681879b3db38fa017ca23 bb1170bbc246d62d646fdd98ef9add40423f9192c8233235af0d23c428fe80d2 95452c2aee1fda69e2f0e534156b6bcc0bf7cfc1fb57f4410382ffbfc91bdadb 054c2c3ad26a81a516dc40a6f4b9f5dcf8159c6846cff2f1942169d0ec7ebb3d dedac4a21cbd048a1bedab746522240f9114375e37b22d7034f39cdfe71ab386 786d85aaa7fd2faa561e48405e8a28da1ff955109ab216976943ce161b43c6c3 fce3ec1f7f53c195aaaaea973611abeb63c341e7d4a5b1040fdb73107ad47033 3d0a901fd668c75d86e6e9bb344d6df67e0f3cd0cce04060e2d0208477009f45 229b95fa2dc14db7a78da5d216790cbb6ecb1e78061cd6395bd2293f23d54263 49f21825a7d83e8e203e1851f26a0261cb560d1cffb3e6fa99df39ad6390f822 d21b2aacba2a476c937c93b51f242c5cfc55167ed7211b86e9da21415632bcd3 9d77f9b6d4da448f0492b82b74a189660232b579ea3f902a3902fe42a70b4437 13702229c4a0ef1261a7a1cd1d653acafaad2730d2c7482df96a206aca07ec1c 7ac6cb007606fa9498a0c061fca23fd26fb97bd7bf5add2f98b5a526ff317362 afda34029607f336d248a92452fcaaef62ff6ef64ba81a48264022a4c52c552b b23f01dea539e2a2371cf67d1f575e4bd39d6077f0012a66186e7181feefd105282fd14058ea030323a53d2d407666af0ca252ce7f0a25f6646a47b488b372019c8b24e24bc12cdb16b9183046f20003de9e6802c5ede4bb08b3d7149c0b4301003c9629e490a65cc1bc046252f42a018e8e7f827f4f0c1a8eee1af5836c1902c49b609e31a5e76143f95b1e60b1584e202341d86af8aa95807db94a006a7d005bd5056826cedada7beed15fc4533ed41ee0b73c73a76f6849be53b33ad24005a6c4d9a70dd6ff3a5a3bd05f5f425efae73901ac45ddd1aa156ca5cbaa21700334d2e9421762f37a12d6e5f3d9a7d5ffe68668760f68a4050006ee15c178b601c0e422b129cc983d56cf4b1735ed52106abe236e071a1ce14aca1388dfad51084eaa3b47eaa31b76c92cf2c5e92d62b8858d5819c5e93e30d15d001428315e07d8690e216baffc8c90573c908eb3ab67dbd16520a02d5feb9322d359566632078abb3d31a4cd792e4cbd5d91b0a3f3eb01d413856de943f817df573f8618500f109e3a5db8e540dbed721dfa259419ec86a43c447f349d66fbf22fe25cc1f00663c079a39b92b421068ea76bb65be74f69166831943dc644e9f0f7fccf69f305e702dabf0a9e23a5e65a56d3b55fc2130a0beef7aef1869a186bc973d31bb608e9e16725643a140d860144e191ec6859a322dc6277364fc68b77114a3a01d9090f1d0d664513e6ec7e91f098989e1a636c9a9cc6e4615228e70a22e468747c0c3a886918cb950ec882646afb0b6fc328471e8fb26d3557c97555cc0274c94e02457a03f45b3c76f5151f288e86bad77ec2e51e7e413f371087c72639b80e150ef5aecb3faabfa98b1a66342d4e45230abe2ec89ee7681c96298cf283203cd30870acba3ac77e34c0bc43d62e171e86b9832ffa01df8332fb90dc1da1bbbca40bc5346d86933bb70e824c6bf643b5549acbb73c3f89e989f837a2998f0ee7a500e807707e028332a881a413862f910502de18bdeb42d9f94545f4f0867d1dfb07e23f49d54a0f2273079a68653aa8a2fbd70291670f9d2d77823d1893ee0554090a55845af4d85c660ee72c3b11537f1c512b5c19ff4e1cf18f0e55a19dee090e75c1c45e310fec9185880192b2e5b8fdb987d137ee63f0cb746dd4adce6a6203d51c6cb5835c36212a63f8f118395ce2b8c3c0d6c71742c7c630dabea31c490c0a0c10a44b1f7ae1b800f03c3b1dffdecfb2eaae738ac4f1c2b4d014bbc2d50a67c23d7c19a83064916170e2302c737d5fec06f6fc71fde8c6a0d569c69d1005c92b33a983acc0244f050bff0e57d8c49caf520a140d5ef40f223fbcc56b99053f63378bdcad0ed74e06b1e47cba6ce549cb5ed958eac5be6a73186d3ecbcc0adc64254e83aaff84c1a9142ca58ad2b44c6ab3b61073c108ea12c40788abce00101b61ce6ca33d70f675be6b1545caf0259e099b9a41ff4655a9dea0ff4b4500d26522177496fccb1643db5985410467be84e3261c9bd13b3dea8a2bbcfd9d0e0a8ccdeada1ac64c8f714ca96551cc4bb3a56a8587767d4db9e7b9988f6a660f3982aadbdd46b76478bb9c0c53afe42fc511f35ca7569d5c19a14430c6f9750408bc53724463a33c5b91b2d844b94b5c0abd9a152bf95f3a85c2f355fa8844047192ad7ae6b24d843e8d3e0d221ba4de7a5b9309d3b13159c64ae06745927405fcf2b3328209b98c26a15e07477e766726551422475d1f77e41d4fb5dc829f070f790526e8342034654339c73c38c25379f4d604c9c019f686e686c69629fa00e7afedfc0ad1869645c93c1675626e05f5be4bef25ed76cd5103ae3f373c68057db2bb0d0165c7e6424f32d033e49d5d5804804cee06c8296c82485b56d3c5062e8a64ae291aa10fcc5afe22e1953936d8b2548a3f3923467448630f5dacb70134195df06642c74131119f64e534b7ba74d9bf67784737cc185960578cc94d0e8912d8dc6763011064d350713d2b37e720bb0e6353f9670309e5203676a11f087888447591d04d9c6b7f3e060d2ec785ec54252f6dc78c150f922c00bfb84009c0114f923eb8a7abc97a41bde1081d6e763959fde16043912a3fabbbb2ed7f073a02fe4d7cc5826eb5bcc86cf41baf58ab9dc4f87ef3c931ba7e469a55d24f04532513db3719388f2839a46d0a09dfe527864563322393b45939c81253e1f906ec522ab8d85dc2d27ab66cfec3f5e68a7c23ea62a4861acdcdf87170def4370fb6a7ff70e57f4c6d55e3eeef7f65be77f2889d9d6294104887048c586fd4c60cc71b737394e8846239374797691c90e5f238fdacd024f453fcfb6e551d847b0e56cbb8c7c2cb83b8fdf3421fcaf372ad434055682cbc6f629f13601cae96a20597e9b10efd1d31a84b89a93688c62520654258e5ad697abcd3672febbf59ce0b99a130e1e30640f82647fa8deeceb4aba88a86fe5d91e750a3b1ea66d1864708338abc66fdc40ede4de34ad4096443ad55679975451611c6a828cfaee5fa21063e60a58593f90b4e628f055ca82fefa2386758a61ff88ba673c3b89a8362470f48a75101cc05e41ee341bf891b1105cb07cda50980e95fd7157292132a915a061d592ea921d6080c1275855614bf47061fcf0e209fbfc7218c037451118ef00673c421d8f447eb5ecb908e38a046cb08703842ca9bffe61e4229a6e3d4bf170333dd6c21e08662dd3b12327d216c172296bf3808bb926129e22a2bd78473d509e194d6594073c59f8ea783ae392af1b3d52c81a1cd4a6f4661db81db4263080389db4f0656a2c732a8053ec9e83f408065c4b618ad92ae2211b2598a825f1207f72d7bf87a5fa65a3caedbf3d688895ead7ec759d90dc866c893394932b94f084c9cd502f043bdf4bf4d331ab0f610405e8e029ae15b93d6c292d5c8bcaff70b35dbadf0409a0a7c6a978581a9e3dce4676f85da22d71dd98dcc0e12124ebc07f37ae5a1b3796fa215207e35550279c6c5f1d3a46b02c363ac27147af3f91e09864b42f237e364fb6e8e4c24d6cc32c41e906d58c1ea91f83b1303773e26d605399c72c7f6481ba72334151f33e1b0ebbdb5d040fc26b13b624de4c8cf8f9b0132af936749f5250b28d7e2e0bfc07323ca0cb19a09e3170fffaad84867bf110aa2d33dc8c488bca1ddfab092b2b7c6fda9309aa76cfb947fd434d51450c4c0006c4b92a98d7480f7765e38e104be3663c6040db63b096e8e146ee63774fd860f5b8c50e3a35878d2670bb731ab77344f079cbf3eeea36586860e84a461c82d0eac05b5abff9fe004dd168e9a17a1a1f7630ec482f8e3b98959f2732f72dc840ea4740ddc263e0d61ee999778be3156a782c9ecf01cacf6d052c627c2f570670c3d2938c1c60c5d7b5ccafd2b18945bcada8d224dd8eab78bfb6c5740455d3f0edd6ceafd23bc73d844aeb34baf9ab588d8e5a9be5ecfc6f211a9934f6d96870c19bdb91ce4a8f106cadc885d1154cfa7e0f3a5fc687c49a2062e4f63a5c0310fb3286e63cfa6c52011956947b6fd462428fd63c260956bbd6df41dd61fe8fa002546d9db5858d850bc33ed5749814968d8d3b2a81a6de3ba81492a79bd7b4c0ee65263a90f610c9e48ead7580a7ab8eb7ddb09faef4c0124cf87d0fd6f99370b841cd7db1a245c1db901a55115b8aa0ed1cc036c9be3ce86ccb8ab1928ce060a7056ec9ec92cea26d858082c33338294da85c3507feddec09b624a8c66d0350e00bd3c15419401e82d1a17aff9db216932868dab243021950489eb830b70d207b820633f3373589301df4082518b3996d046d51289296ae183c972a31d723903d48360985cb8e50292cfd061c587725c38686285a8fd9169c8a1644cf005a30cf758187365e97b181cd62dbb7212ab4fc0277d5c3133a0cce78ff5e20e191d06bf1c30a4b678468b3a40c2e38af153c20f2a7b3f4d66ea6552cf282b72e53d01c89f98ca6e118da6e53149b7290b25ce57e8affbea0b9d38009e1a28a9913209d5f92de117d21979c5c9914c311f2591bb05077b436c2a6e35c3669846a51a05495605ab2fbc00cfad90aca13d62adddb5241a9e78bf12c6ebc44aa035dfaf051c417d81625c85e6b6a0054e76429e8f6e941d2d00132b6dc0f24fa4a66a82053ca167c2c032465f2e033e5430d3414181a1e06e61fd72219f12b9f0d404300aa8df863e596801bee97ddb5e5db9f18c5a7dc5b4a13f8fcd4035ba44b3353c019e6ef5d2515fdb243c1f3939d241d6efcf4f2ea848a9aeb3ebbc095dfbd2270a16d51185b09fdbb7bfb0da19b3b7b80e56d45a20b43a2249c6a1d286ced89702b44403641d34f8aff762b89225f97ae87d4ca9002dd5cc012dbb93a65a357a011cf892acafd487d2399fc28709f7e82d2b180f30baad90f89c25047156db5b04a7b7e9a4788932423317eb16cdcd005339e0f2243e3836b7c133eaeee966d708fc49bb104e537ccb8a8fe0368da88fb699dab45c362f1e385424eddfae66f2011ae49d7dc348727b4f53db8210c00fcc579b5c47ca94a61a4ba728bb8d5fbb04e098dd002da4821f031609ec784870c81b160f76c9bb11e4cbebeda8542539085dc39d7c07e4e7294922b4797195610226b764357e3228bc401ada849b32320c9c6cf14da0b54769e2fe25cb537e2052a2d8056e7f4a2c10f865ea599a52a904d642ce4bb8e0dd584af0ff6ee5cf8d002c65a6b2074f4e08705c5d7b98ec65057666108bb1fed6accf46d04f2b43801ce1fdab2a80435f0819a70b62cf42e9028ab03c7a4545c8f4787fad6c7e94d3f0162a4ec5343d989588604a39d9da120c75105662acd75944161fc7afa6de7e5a0bf3edfa0737c83e22274ef43ed67a06ec6fbf1ee98bb0cbabf12d57edf79443137ef047dfada6836209001e8ff89702d506ee500911712ca1cc6daf92cab4a5b96bdf114c523a3f659c08b16f772d0875ba2a0162bf9c0226be368418109e7209b41c280c0d9e8dd3f0972f209ed902355ff36cadc3cdd32e4c2cdfe6040bdecccf7d069f264dd02590510f925df707208e9e786ccc4e81d73ed06f301ae776bb2dcf0597cf2770b8fe04e0cd478c0597037b325522ce3b0ab0c6e42fbf68c389c252e5e573098e1cc2e142e20bb20ae619626a5310b4a3acfcbef4c011b6ac0b8ea1fa47116ce84c546d16f0c95301de26132b41fb5af605931e8cd9277dbe948a0018e3d2ea77128044de0277f40b01bb68639d23aaecc9aebd9905f14120f66a46e048c28e3ff91126cf144a8c0d27205f2ed5ea3b76fbda5d20a936f3ef7399e2d49d18655c241d163233676b05a2947e637dcff91ac73de6bbf9b2e45b28cae3d7d2543f0656e71a14452738042a008957941e886042e2baf7f9c56aabbeb566b46492a4d8f71f2fce9adefc0d363a0819e002c00b30bed3ea5ad1e109b47b192dd1128830b0d991b16b230603351d63f242745fcb26eb43df59a8fee81786f89cfe236db16c19685173f37101fcd84a6992507ba24b78952433681380c399de8ddc4f0b16f930b9b468ed370c389bf69ca81bd593b31d4778c1082bdb52770b30a2d521476d7f8a3f229d40043416e6834b45da3ee32dd9a3ab1f26a9743b443a07658035efc6108f50f86804e328163785b13008dbd24ebfefd07ec798962ce1d5c11974a869f27603357c000cc1751ae50b43070c5b55117344f0936c6ed804c35a72da9148bff4194d1607ede01102beccc790f7bc9817a48559db661cbec7b548642b67551cdf1f2b1b07e6038f291c3af29422d3edb6d981fcc32f7f87a051d23368c04f0585bef217080fabf5c7f2e0de25e0f5d46c2bb5dd7c96fefd74bf5a4cd47d3839cd520bbe0e90843f267f61eb8426da731fd569013325da5ae8dc8f730171d194a3a0791e0534a2e67f1a05462a2204410e9c09b5fe7aa58f183bfc74b2adba77f9bcc3560640fc0a27b01b61c673795804c904ab14a6c851e21b774cf1811c5d496c178206729730809403908bc1646501bafa0c317b5f834dd2469fb8e674df0b71c0fc0fc0974111c135e8b0cbe0bb2c56f805c30ae48d9322bb57ac539fe7bb23d9610a01360c0438a9b003ed6c84fe9194cadd39f0e8cb7003050cded6a9c61419ce0e973cc8963ae2233b688781187282c33f9a57db38cb1d7c46973f43e2565c8d06ac86c2699d10b5f6150fb0410d78eb8a1793d232b2b5f07ef4d5eab25d7354022cd0bee9b9a6364d6faa07df4cd9982c21292a1d8894b063cd707052fb24360f31aa4b5997fdfc389562ada1ddbada80620914203ce49d8c79be73fa73ec910e6d89ad4ca85296a4c8db2a08bcb1ddf2e873fc8caaeca69c3b4260dc981263086dd9df67c40f15ff4fabc9580d1b11062c56efad0fb27d55ff24724df59dd40a184b89cc591b152baff09d88739bf41db8df2d204b4a8491094f180082a7f40b362db3a367b41ebf3c20eb7236bc348423ae3a56d4e6f8874502c5955c97f401d72b020fe091f8e13463cdc11ba595cd7a1bebf0ec69c12535620d43187b6d097812436aeeefa94b4a0a1805aaf4b7624949877470a9d75b734ded8721cd660cb6fc798ea2830c312c8ac56a642e10c0ed6f8e2c30eeb07122707b9eb9706208c5385510be9921b47dc9e659301aef4dd43387501f90ed747b1d237a16a8df0479835b8946e0ccceac7fc6f1a6a7857b26ecadfeaf443ef518691977bb6c9603ff70346f361e1581ece1d1aaa8bf2059ae605a30a1d44e05a58260e59767e10e62802d6422724c15ab356c2e2cc995217b09917208ff2b3a4137427a63850a062783d156d4449cd63e0c72d05ffb844b3f866b8bbef9273c5817aedefb32a10bc02ff78357ef251b1f6fbcd2fa47bbd073a07d2c1da3c0924943493e1bbab40799349b6958f4dfa40d3c3a3d2e372e6a3e54212585ebf08981f8b8a4e4818f083cbcaa9e745521a87dddbd998504fa49838595c03e190dcd22d2eb7799ed200c2893117e2dc5c62569db9890f7a49417a91f410290e2691afaf8ad3b61c4cb0d5f8bcff4c20604ca615ddd80ad6224b6465f4c136436ca50f93befa679d34a082bbd88d16e49a5fdab25b62bfe1dce4bdcdea59a9121d2e2f09a3ed2c105cd02defe7f3f1039b0a3a6d5f9b3846e683d7a1c169e938b003d8a4658d66181270462e79d3995b6ec12ac0d421f44356b656527ee049c99ab580b7a9b37ef1eb00d1323f9e165e97c381240b2f844b181a8d6e47ac50d498b1a04b8327b0bf3fa0b2c4d9468d61321dcfad9d6436910c4aaeded47ecd2d805bf7e22ec0cc6b80109c222acb0eaaade6dc3fdfc61e6369db53f84c37db2da7abc475ad7426522000174b7ccb12117af9d00618d57c1fec9b168bebd41ca3bcceb1359ba9d8d6e1f09a4663cf101dcbf7027077c15202023605f404ae22b220a0c4c732092b9648206f52295d1cd6aa7a25e82730013797e3a7a9443f2f54569524bd83329cacd8108d669b6c5335dc36a2d449b1879fb011f26df81c352c3a23583d9e02a737f910623968e4cf5dadccab08351a6db1459deba09e6545d7c611e849405c9e9bbe30ea74731579f1b4736de4ba9d968baa7ed97beface4dea46c50a220a74fdc82800d13e48ac88ee42fc54e8b01081673ca1bad6dc7a317382a6fd275f65db0fa30f74d235bec247d635192fc9b1b9dbbf0ef0a0a439fb69e3cda554ee80c0308307c9fd7011d9873e988ecec4c2093acc651c59ad703993e445d9cf20b0eff3a30b047cd4f3976ce87ddc9de8a062a5bcf8f65ff38f53656278ca104b4e561a0a077c77b9c34c1e6d13702fbb01095f243c5c8fc617041769c11e1016eb6eb83c0e21acedeca87a6b6c9d8343c9f886e6adf0eecbc53a32222c961f887b57d9f1007b2300ef39a4581bc637450028496c5bcd1fd442097eb624fa6e10e7aacdf503eee8fc9810bf2932ab0e9186b9d27bc998c6bff3d92fadd9d3ca036e1fabe40bc2190f9d54368ebaab1773f4fe20c2695475ba94819ea78a3c57fb2be2b56604afd05d223496d05975b19cc74cce1a9dd2c7055cd8c9919e13ae03cd090b9e0ff3604b5dd36a535f42fe2ea19fe85bf0ac1cb3240d686d6c30e229b3dba7a603680ed07f0c8c23f3f77f9484954b3679a90f72790bf0d455e1a00a2e7c08190a175de5c4d022b2021a0eabaa9f96f9a1889526038949f5db3a56c23ee2cb3a07a5ae847e325245b64065ed1fbc9075027d7b1233ca15698cc171d3530ef1d6086eb8977b76bacec43d739ddfe5eacdc3f15266ae202888dba367ff2a6ec99c076c80ed680ebe7141530d86caa6b354ad6d5871fd7189ef1789c9853080e89d0188853364960900a57d5ef73da5fcedb6a6e52d898e5a2069747b8dbf26aa9103b2b1e59804411a69e3fa4afb75787f5a93235a13895d162c7c5c15cd0f98c2097bfa4004dd21aa23bcb944edb94d7f7980b577b44f7c7c1d0a44e0a26746a20e9c311d11b80e9804979bfd0ec06db5cda9f291efc45734b31d3a0189aa4b440fcec35c86eb27f0d7a499030742968bcb0687ad798d1c922b4db9ddcfa2b01e09c53473805068938ff6bc18392d0eaf1fc018d2cb5fe49e2cc0b2ea77af6e710eb43aa8072028b452002f5eab4ca48baff512e94d5950d3f70cf0c4ea6734c701f39e68a3240bc03ee24e2607e1891ff49fa44038285a37fc2a42a7a03884cb07b64167cd8cbde52ba75146d980248ee8ec488aa396dbadda917d40b24ffd200bc331e41ce6878012b902f4bfbf68b98a5d58baeac4fa2b6a4800701804ba300e876f5bcf965e7975c15427d577b5590d84ce6fedc157e317010538d7f2a621067ca1241eb992cb34194cbb54aa24891e3dc9eb32263571829566147d93179a0fe9cf1333011f675dcf4ba398d9d5c763e1cb80166833684e92d43892382918001652882f6a5fe992d2fc70d3419b5222acd04bb16012f2d317b3ef00466a9f0cd74a10fc92b126dd5c6db6767949376be2747fe0608d9c2bca3751cba4ea67087bb82f5099a244e8cbd556e8c76f0c51966197dc01638e1e3a9e245f2b9db6055ac00f7966addab164bed71cd9b3d0416542d8bdadb20b1ae1526052b6a039032f0b587ab5d8dffd624145dda0500c5c0f0d12433c0abeb1d29ec82acf683d01887114f0c9b76e7d4be3434c2b6e1e014e31135751c0713c1bccb7b0a8b0f70357b2d16b9e2513a789abfbd29a88a6d56184ba40c811c7a31e22ceeac5d8490049b9b8c8735d5414678d5d7b76a50c1567885de0831f2611a55a049e4945e804997d4f9729adf3befdc6ee223feb2a9f874107f0d8f657dc6ecd37d69f12be00ef1e23d29ac38dc7a1c19c090eeac9285eb17cc2096431194c4e0ad2271515067b41a6081fbe19c6f63cebc00b539a78af431bb22f2fb77e84eade7aea7d3e0d3ac9bd2a5917591b5b73f2a5bfdc1544f3b3f680667d347abd06282d65bc74016bb359303cd3d7411c4348d273f9aaa03a6bd4bbcc732521f6f4f0c186f0f30ae582c19858862c8fb472a298e092d4abe035937beea63ff690f398e226621d099f9952367083e50eb67d32527f7f6244869fa98c58b0de5af605563b1fab6d015ab24e1920b22e42b2d6e58b84180aa65846451b720f87b435b07694d0fc1a0ed6e685d31fb92356f02242bfe3a2d643e598f0184621e22dd894622ce7d3d90b9c9f4cbe3a208dd634befec7e3af84713f8aa0a575093a14a4853a7875a9900b9ef47f0772a843c570cebb20b25b542bf2537a1b29af33c47ccb1d2a389fd70e763f2db95bb7fbb11bf6e8ab48f8882a54a81167ca6c7d45504f11c071961c0f96f5f9ee7976f355201e9406cb5f0f00196259aff3bdc007edd8158e87b7ee02bbde673c177f475bd062f18ac63b41cbc9764f87d250998171eeb784c2a8600b5606bdab9c90097a485b8ca34543e761a8bc832a6ab91621a81042224211ac04feb6e55f961035dde2e6998f57943346575bea87c72327059f3b8a626a79de089fe355d5a475e281cc70b2825b2e3370dc13ab6840d91f7fd8da8b9eda4347007dd4d8a7b4035968c5a3b2669e501100ce25671870efb9b5130057a630e337011bbc404bade7d5c4bab0a06f05b6eeeed04d8775a28ffe64cc7db6193bd2cd0589d57a18fc0df9c45e98eacde411cc7177adccb42260b0aea76c905aaf33d10d82ac1c29a4e8ffcbf5b0c02e333a70500c32e46550fa53aa8b2dde687ba386000f10337cfab66d5ff5c1dd711b39083dc1fd545d0162f08038bbcb606ed24b0e89681ee5abce75c2ebeaa95c302785e3a12fe849fca6db3cc309881441c3d20c750656fd1de7f34b20c0f540fdc622059b6e9dda517de3276558f94c89150207fd084c09f2f921d454e2b730c8db5c787c2122c58a62ac499b8f21585be7de0e9b0488fe20e9b102091f48a5173b2e7423c87c5a03d4415469dfc600607e050b144680272f4924eb8fe76d38875d2eaee5da29df0a3edbf6067c72602c0fe50f73ed282901d21c06af019464a10c72ed9764285401dba5988d12fa56c998a2051b2732a1150c187e6dde5f04bb524050c4a53690ddb58e05f5f1f4d868cc7d0109f528243886be05b323c897c767e1060558147285e3da69f1a07f10082dca045698093c659193da8b1487de42cd7540b379a844279b36d8de0221c249d1f80919ec51bfef87b25559ee95e5a5062e0ad268150020dbbb5f5c58a874e1a6450b94f1e25b6be547c74c3eb1c5ef116237f8d3ef54220b376b55e96d0f562a7d0d9624729af81fb1714f533f3c6721ca778e1828c47db658b57dd84077a98cee056c37226f5acc2f9acc061925ac618fb3aad2ec9964a9d99786d19aef825cd6035a0abd5409705bcfa6c6f48ceb79295a25d015312d111a179239c872a2bd3e0f7fbab01e736e229e2864b38f487838bf9d6df8506fbf5b8555172e849f3b9701ecae4f516ca2ce76bddb509fdd19b9332c0f1e7a147224895d6ddbe12404a509001cb2b8939c265a5c8cdfbbc6372f6287e93f50fc15b90e0bfc8431a7d90a0ef8c8b39d39c3dce19eb7cd821dfebe523da99f24db54b34f3e51cc827228ff048c8eb0d7849e0aa2c43a1ad38b610363ee9223f311fd2a18b27a1f72de597c02cb72bac8e3067b2aceb87137ba85ce511bcd942e068e27178ac78794892c84073d4b907580db8a2127f9f10128d62c1a45308013ad2b37b0fd91843073fecb0a999dc8121593bf8d108f924eba581714cc4246e09d346590eb45370f64926a0bb8bbd1582762c7d3aac49d3b7c568347ef43e8d6f9471f1f1e63b7302d7a170e198a158b4b74e2c879da2d96939c846d7ef2abf3280f0dfed8b9e5910dbed20baefd62fc8a50e22cf1fddefc9de016b95efc24f65de7fa1c8510cd7fd4b8320f75187f93b62c0ab8bc02b2a944304ef7ef42122bb5adc4d1ec6416b2feac5e0a81f2eacda7c6ebde512c962e9b2fa06e7177f6e7b003d917c1722a2d4231620a34201be6b135c9b39ee5a3dec39363c81133ba10f0f74ecf75487c2a09aaff0d46733243fc3f1925388b741e8327635f0a899da66ffac98eb5d1f35e5c8dfd0dd08832b4422b230396d7abb6cbd284c26e3592c1dbe05370e2757efd79daa9026c088fdf09582d647b0a37d306d33bd463c5f41f5658954b2857bd0b5ec21c05a2d01070ac4df5d5a21ca667629fc570433029186bd711da0870c46d97f3840b1c855fe80ceacec2d4a9d33de45d3974afe9f106d9a07a4988442c3ee1ec3203097292fdd16e46f1954dfc9ea2af99cdffcb8fae6bd6d2f8efc34884a4d5f2044bbb24ea076732b5f7c991a2520db83a1ae55bbd1fc7bd7d8c1cdda4a4dbda029c999189f0f12a3a37fd26b87423552790f629d424540288eee01c7b87520f08e869483bcccfead84a914731929ea2f8283c93cd9607594921bd00ed6fc0670eb7e8e2c247651ad8fb8fb931cf6f6d10760e3a288fd3d9208a468a3ea46d230fc8b6576cc621016d2167cdb93ceb71ac21db05bd8212c1286b94854a2aefa205ea4f4975352c321a433cd1947b882e3e2a4164af28cbfa5011f322133d3ee002439f4ef77c07cf25294406bdc7fd7af935828bd2f4a59ff38214e7c9855c540c252702b9abf2f77cc7d8893541a29106a7d8fa0f3b58cee5d55a6b8da5e1f70adcb74fcb4c23b1908bb1ba28cca75b93631c78157454993c2cef9eb57c33be004c0a5071854ae94489380c2a1c29857cb32950f62fa6e2a973bb9b075f908d0fd51a25225429d57823a6930617bcaaae688ee114cc4b128205aaea65ea5dd90c13ec98773c19daef0750ceb027b57db4aa8cb9625a476d0dd763214e01f3ca0b21c0aa87fecf8721db33d67c07ab3b27ca0e2d675d5b5b0e519c95748ee55e0117821299d6f2aa34ee680ab8ed707d2f8067ad0d89e7d038d3b40bdaf6ac9707e979a16c04a0ffa147c675e1d0235bc804be934ccf71a271346d51d1df58ce0dc26c5a075f9890ee2732bd9ad58fb2027ddd842d5ef798dcabf4331821739b0361130ad73c524309b5ed11e9dbc3c501a5c16acedf7e1d710d02ea884e6b630ea51739fff7768a63e81a34133af7355bcd008ba02be32e6f130d856dfd9e450e31389d2dfe4ce4579342ee0dcaf062c176ed51a177ee7b5c4b73d0b03bf1a40cc99fb8f3101a7017cf230e20b18f98d18869c6accdc9ef54c716df5b19c204000f4f1fcd3b7de905eb2c093db0d5fb55aa6da59fbce8ddd24da6cc11d595640e1591defe14b388d442460810716cfc84d8b1258352695d077f3247de08e3530a1c7ba7198eea147470a84b158d9d4ca8f2a7f465d0c62007e746a46d79272b0d62b1a1f758a1db02d2ff3ad569b01c9cc134fe6e697fbc1160afe1a38531b40c9f905e35bbee3cce8032d8d0159ca67a35687aa451bae00d048c541e9e86bf086afd0ee85fad10a70a3bcbbd1e116eaa858e6dff89c35202323488abf7029708349028b2ff01f251e3253e456ad8f2104fe4d2fc6bd8cdda952461240b5f4506b5468c8085426b8b06c33e59b57fdd0539d65a246522cf7e5da0c50dbe765b0cf87cb6d1a2f8dfb7b1ed656eb66d487fe5a80fa23858b9bdfe86f6d7b0d9fe0dce15996d740e9d51c0670155b63f8f428168aeba0fcf69b2df17055b50b0300fbbd468792d0c5268e2fd3fe62788b41ce075ee746129a29b5b06d74d1d39700f5576c81624153c409e253102302381c73821e6939df5536e953b8d4838c4c507a8188efa9489f3035c5bdc009a8c83019b1e4685f0ff8500da420e656c755f01630ef12abf768c7265766b8d97b8fa788f3e3ab83722076da3cd7ed83c6314061f292e025e7adcad0cefc266ed0b39095c4a29015002b74ad6a1dffb858aaa06ab8f030cfc5c88f1d89044640df1457e96c61739f0c5a4ffac7620d05d6a8d04906ef370baec2c1b9a2a0aeb09a6edf715060314878d9a0066835ff0bae5cc04478bbc12dd894ae09abb2a3ad870775dfbcdce2ab92821bea73c5bc5cc954300a3763c0f6444a6ad9721a4b643596bc9736f64653e56ae6d5f06a03b1216f00f6a13f74437ab039c8e22f04b0bf88f450b9f86ccb3566d925032999c4a79a208c1ab4a204e861e862629644d445cfdc1243d9a7b6a0faa1498dabd10f2d18805f91e807120368977d8b475e121895600455fc43d213c3f084f897f432bbb760c88b2996d0da02382d396447e939bb169c947c135a6bb89eccda94365775a7806eba8a6687e49ebbcb40db3e27a05bfb77032b8a89c8305892cfb6c7b77a7ad0d4352344f5cbf19d221e88c7992f5b09deeaa50eea572465ce2d65c9c0c47020db494f6c07966b95721c7caa2e6f676f0e9a45086fa6d5cca1a48f30c16074a0fbf0e193f5b4a1061cbf26e1234484220d7ac696ac76167dff0eb15a3493ed806173d96c678abac088d7958997d1d59793f48c41c4be37e0b83d4ecff329acf0cb34bba2b1831f3531d1de2801a6ef1de5b4adc1df272c8a705291669ecd37f0f9f1d2074347cf4efa5c69b6aab591e478476fa6595acfd2a842fb78147276d0999fda4cd004717a916a73a7f4e01ededdd379e84c997da009add1a3b35fd9d065d7ff759fd0f08b9bd6057ee2a3b0c6788549b0586c16a3e4db7ef65012cb60053fa4008ac3a2315be04bdffb360dbe92b51b56fcd60fa7fd5c69bcaf5d849030727a859e980e28eb99019423646a3dbe186a658fb36cddc15c93b7af09f6e01cf5a77916e14bbf235e2245533bb915cf39c0f4cb3b5413e37b0273275cb360c66a83fddecbad3091288d06e81b3e07d62ca91530437af76ee6b3e5956d0d00d330e7053362f407ba8f886ac351dbea95bd4651b8eafc99dac6b61da1e8bfe5380ff0acfa1d24c7dfd3c2468efca27afbc1ea4b2b782976ac45e8d0a6a898409aa61e8179beadfc93b4b132017b3ea4e278faee6b87a81cce7affac5f58f4d0f1410f1301a806391f741b95c3ccc137ccd5c3fa73a5f812d9438374026c7c606bd56258641404d53437f46a8c6c1072536ac2ff170638a178ec0c7c26b1315093d9e90e7668db62c416ff745bf53a428029e278ce9571911e58827f0e39e5d0fdddba117d6ca30950eca4b84469194091562c23a34e6f4397f8b9b8f0b542c027a5085b2172dde26eeba01c7e5e9ca72a175af91700ef74fbb22d1eb99c92305a0f39b315c150155722ae455984cdaf55947495df29d1cf9daccc5d2edddc9068d9ec9d33e84014ce08b2187bf7c5c5758c8a0704a3b0e340aa901fb5650e901cefbbfddd31beef756a462ef60c5d5006395a3e7dcce9982e69ee01bdfe58202a69ea87c3b4acc3b0722f628938b0d1577a86ccd289fcf0eac5056767d9b7f0cef3c017bf789e7f0e20a0a70c749f8da27b31c442ab536ba366555c3e5f6ef0ea20665a804c0d5b0f9e905c24f89fd89a67a2ca4cc6f85adb1b830920535030668237323e751308aa03a17233df12999fcb1d6ffc9dc8e3bb64244781c497e0ec0fbf1ae7a764af89f34d59aa6ef752e652b7aaeb33ae553c9247bd6918a250f84f6be589da39b8e16dce09a7e55d4108b3044b122eeee9f91557610b2caf506adef19d32fe5eacc2a609bba8cbb7ddc2da80b574ff02a55aca9a0c0ccff2d050a85a24f908422e1c4554673249db91262cf0f87b839f7e2321366c8304eca035817c43c970846abcbf3fd6832c55f888234c13e38c4fba0167e23b6afcc4d0fa88f68a8f0a7295795be5aa8adbbb94fae3ae3016fa226bcdfc9169d1b0a8b0178e8eb3cd09ab4ec03f959250f3cd8b70c46bc1ba0d5e3ede946ec19887f3d0c8c13f04ba17312c5ece8207dd19eace1334509158cfaccd2497910f809a65d080506e01611f1443f2870eef7c54e5b75f335d6653b3a93586c4bca1fde287a0d5984b08bfd74a34b21c198e2ab57286064fe8538de8f3f1adaa3eba9bdead90206cb151db3938972e0a2f8c926250ba22d18123f97cfcbaa2500b074c9ccb00cf7a60f68f1bb396060557669e063c703359c499a2699e116825db81808ff9f086a0108a8052a3e5aa21a2325fae83156c5512d42c24fcd96b36e4ad090409b09b2e532267168f7127c9b77274c015044d12fecbd9b411b3e4719190173e51e0d663f42941780aab90ac487e4b79cd2d80e3c8d1ad18a9dad8e045f4d401ac400acc883a8fb7fd7822f4ea6a0b5fdf2926fd9c25ab51f61103a18108420f0de0b2a591eb79b886088de6e5513ea4cc34b2dc12f737dcc194b6eae0a644eb8590a20b90ec143e58c522679f9c8d7445de05b54576cdcc0c14c6701ced2d8b2ed0d7abb351af4a7ef7e3c0addfca9d14c278bf7f5df8f88227e81d439e5e916770ca84387dacc55b2de166d599a9b840d1cb3b83f32e435151bd930d64d2051dc0deb8ee5a6e7426fadd3d7ecbcf46c67f3d623422acec2d8cf43de1414864f84038964e621c98250dc2bf2e40a60bee4e775273ceccde6cd1f156d6672a9254e0e188eec8c9b2defde681d5d528219eda23b61c0e180cc7ecf2e3dbb37b752bd0b73b7be44173ebadbccc4311eb0f5e4bf4a92a9a011e6908eb5539070c4ae3b074722391822afb1e20a9d9e13b984fcd12ce99450f399508a08f38d1b0ad01e01e2d6612f0b7e58f0e0f677da052a4e893ef0d95c353cf2c621d3760a8fee9d060b57122bc5985f017cbbafe0dd9bbea987c1189917e4629708dfa8a9ce30a300da924881feb6954d42bb2c1bc57aa81597507cd1609189fe06ea0d25e3475e00ea153f81518c6a398aaf2bc98737a6e2882296d43b2e0553b9dd32b17ef3410b55cb2feb6e1b5d27719d6964dab9e96d0553b17b467ed9adfb45104b97a888088287b2b0f3505d5dfbcfa9c6c208a2217781cf0b8a13c0c679d1621ef27a6706b65b687b90dfa37738780b97aab0249ca1f0badb9c291f198d8717b588ea220a2defc4953a6ee8600cd30ffeba170699072ff22c31e74bcc5412ede24f456a0a787bf7d52c4676d2890f4d5cea54eef98416989304e33f9fd7005fe7e4873f091ac9a1b3aa1cee5c8de029e60af2ef81fa0e4c5d247c9c0ca9029ed4b4e24600f5d7f42bfa09737ae19d4c32addcf6999bce3ad53995ed8c7e3a2e11defd310e738a6691b9fa8a3d8b9d94379ea2b05ecf13a96d1521fb00db0a6a75e1634e033e41e4672bbe96b0b080f3e112a335c1595f121cd680a3059f6f167d3f3837062aefa6b703a55e93c2d60e124387d268e12d53c013150e595757560a3118ed06c18072bc2d1dfa0a6cd169695890bc3bb3ac63c92f9ebc250cdafa3d510978023b19924de620b201f85105699a1aed5c4ed2ebb51cff198a6325980adabdcf044ab78687c1d5b31d3873e9c984169d339ba1163fd717926fa39f8be37246b203d5d302d6375ff0c6ebe2f37bae39731d90644301262c1d2e5963c2aafc29ff0c83756e8216ff3bc0a48fcc71fcfb183a3b44961ab9e4c263ec3a281492f18e0f4aa432cfee8af00c8bc0423b4b52f52a3132a38b1918073eb70e3c3bf2bced00b577d1c14bca2ab1a0ef2c69237096d949cb905cb2e04b99c97dab59db9c0e03116f36fe8c2d117755f05c1a7a86ee70fee683c8757c4778c58a3dc1806015022a2cfbcbf4ebf1bb3d7d1e6bef44ad5e4163b914ab5d9e1334abf9534ba5530a397d6c1482db76110371e674e408256b68ea08f2a216c0c98d94f0e839c2020c0791d8eb3913fa44466607370d8bbae57eb4ae7c55fffa20aee9a398208ba90b713c16f4d2b88775bfa4efef6cc7d3620f9240158976d4f231346a7f157bb2066873403231d4aed331083f87cf0d697651d555db3d7a20fa8a0804efaa0499038f8d60e0a7b18eaa4256f74d8cd0e379100c7c22c9e709450fd5f366b68e1b03790516d17fbb7667507d0ce52855791896c4da1401277410c72169fa6f3b1b0925f582b1bb6daf356a4ca80e2cbddc56abeb82c56210815c99180d6c3acb0800079fdced7deec50a82f8950547992f16e6d15bf3837f765bccbd3ebc8e7b05059ec4f246ed3d5f09c9fea115b7a0c63e75886010d20d6a89a2762679688d12031abbb40b8980e3af70725e76c5015051d04d446609eee7181dcd12ac53896701f86e833cc88f07eaf28cd4a099256a4cc6b0a90ffdc4fd8d52e56f7bad9de508b2b37d43788425e7156473ac5d2f224576bcda35f50866a75ba923cc11f0ad06f1142eff52352589846704f84df14357385a983d93c3a0d3cbc9c3a22ce19a08d97fb8cab4dfed3149965d98c8292d3de5cb443835bf47a3387a1dc6447a1e099134a443b05b3a2613b59619cc4ab5e31515f77c508f784b392c9f2abfabfe006f15f4c4f156c5fdada3e000bd9596d01a74e2b971775e34897b6b7ba54a43038e68e9770c654d0f5f31e4a4d0e5e45c758ef31505631809aa8c3e341fb42a0d170366c44e171f4dbefe255eba0da32808a85e549f1b17c1b0faf1e8c7017e08c1d114291506ea10d7d0697603fcf45a51a035f83d60088b02748596d4a89f0218a1beea01b5281d1bb0a696d0eac950500d3d5007a862945d8e8b0742de08059f87a41797e0e76c6d602a17c49815b199f75ddab1eefaff845e002bc8bb7c05c382757d710321eed5ec52416465d9f5011eb246367edc7629fe85572e3e820bfb095398a4e641c8432bce0d7c14c38f843608b2507419e30a92021b97cb160ffd5ccfc651239c2f9d07c060641edb1583febd5f20ce5ae8369b475a528ea402927536e56743544f2b59e6863c4f64f964dcf63319b59b06121c3414badfa90e6cc3ea203cf1fc0c728c6b920e32b179dbf9087da2508c6f89251304e7d91b0d6e1d3bd31685dd85d1ca54b68bb35aa11a3b30d13bc665e290248f4dcd48330f330f20276622405ed2a4d707cfe17f49f53cad071388cf0b636e80980e961e06f9f010f7a4ac2a5c3f8c1e21e29c7c91b526f38f5f29ccd2f3a33f0bcf27e90187ee87fdaf929a6cf60f851c3650e6c3fb6f59962e72017fc1551d0ca5d43e0f7a9bdbaabbf687f99694586c852593a44e7d8dc891269943f065347f338d3409f3fda907f8a46edcb8f7f1bff80e3a33c5b1001ef354192fdd30581ee3edfd0a1efd0642610ff40f2e7dd6d43ef529920127f668bf7fbf200da6cefb874bf808da4b0cda23807d83b279ea6770e56fb1838264bd96be40961c1bb1a511ab0a0037f44fd63e45fadfe3ce73a9e15cc61586039a311ebd2737686f28274b7fda0ab4a2bd35e92ad66cdec994ce04243b2f4f1a9aa455d5eaa9c0abad83d6fa6c0158b3b4603720b9399982f2b41223a15bbe6c6b834b55f19174e8f76f0615940671f4b5eaa0998128ea7d5b80038ffd4d443b8ebb418ffab0e46609907f01cd08c9fd651e50c4b815365f8e0516695036e00a459195e5f87284454c4d683fac06646afc5820710ad01b8d55cf1afc3c6aeb3ad4883d51d187343a7442e5838a04ba5dfb819dbf22ef81ecaeefb6224aebd50fac7f47cdad0bd8f903ce2fb3980468d11e56baa264e8ec7b450db92ea5dae96e24a37acee77fa968d09b60dbfc0323b768852b8803c0c0e36e69973ba29e8a2eaa62a80eae152d89c786e4c2db02e13751d3213781146ce7e501184472bd4f05481e4c64377366d1bad7bc244104ae4abfea7dcabd029c9acbf329ed1aa39366863e4ecfd26e4b4ffcbad2da9c01a7ac0fe862532c5438808ccacac564687e3bf6ceae5a341d0636530a59e32f026d477a73d6b74ebe93101edc619fe915997d9204da649ca891eb324768578b0c4a0e8154933bcd9de6dfc0dbe01c0dd5c75b6268435c257e44f146fe86db110836d7d4b51c60cb11a92034a2ec7f6f217759a5423cf4ef4bde8e11a06602a609ebae182af82ec5463d1659bf1f310706d0d2e69d31d253d352e0d4a59f6ffe4026c949656dabad0830dfbe99f15675e62eb2f1086caada7d5d17294fe0d22c08d67e47338517b03c78b5174c48fc7233666769676bcb6432f4587a4d7d60ff04a96ce4e44f507a6e9715ddf778314e582678cbcc04519d1e74a9aa3a46d32c0e99f2177f1af2cda0442a2a255f2809a4bca8e5b62eb2dec7056443e9df33c70508ec23ce72dc23e38162279de07f370c39efcfd4cf18aeed49e7f4f307b4d40ce4d88a589e8c9884988f6f7a8a60d1e9ecc5ccdfc05a79f64f4573b9b369c301a376196d8e187f2fba849669176dc5ba4ab541aea8c2d97f226f4bd6a0f0ae0c16d3cf5b8eaba611dc92f45899ca7debc670cfb22800af49f379924fcf61d6051844de28921dfc0b1b4929c7c51d96fd002bf666230a6290568035afb8e1de0befdfe68a0807025320c5aa83e313b65adc84622f6c806ce0754fac9549c0ac0e4bb9027d7cd612b3ad72671a5b3f32954fda7b30d80db10e25eb57164061be0ca5715f7058cafbb88c38c2a7e14577f3cfe8bbd4ca9dbe47c954b207da6d170f7d3e122c47babe603c9e3a3b143d10f74a05433b7c0d898b2fc7b1fe3182550f3b0752783959328cd2677f5496fcff95f95dde9964b2778563bde4236a3ef204db6ed208a0ad34b6c7a4b904f93b3c1fcdb534918f09b73729994583cd544a03c9182dd803b17518ac2df8bcf45634888cbd0242a985aa94d12eb33abf61520f93f1399dc8636aaaf16771904288c1dd9ac02ba9af8c3b7a22276d54a0f1d10015bfe08c1b22894f9e5f67f9c68227ce26c83f5eb094d78ec3ec3c0d037e870124213968d41c40511f66a5369d7dc50cf30e52a1e06c2da9100117f89ca6ef09c8985be4864b65c58b41dd538826cae86b1efcbca551cac90725914faf26830c3890bb9106d973d59888b914f5641c1d87e9a4d7eae4372d31b32f73b3274b00eabd838dcc2c8e037d249c215af687623c97b510620a183ab14d4ed6dd948d0eb2d4c81f6169ffa69c5df6c4a5a103d2e4ad70878874f07351da6960a200450f77f1d6ad3e0d349af186a13efd9d3e960cd44b8bbb77fb727db8f3b78b13df0dff301db64c1089b145b4092ce471d693cbda37a6f2cfe2b351c93bddf2bcde0f488951239379798d5444ad4ec1d69be8f1e05a26778c0871993d5f14b88fe90baa34cc9d928021e567b8f7daf9b5aa42ded1b55108b1e1e8ba42ed0665518209b8ee032f4b3130f05dc568749e0248c1d865f39e6aee03d93684b08ebb648a0fe874988cf3eea48d775e79810127ad4baccb18cf15e46f0dfb7380285728da0875b8a2873cb024eef53703369494b2ff57d17c7e42d5180f4b89991fc89d3003d7b8d42b0b336d40dc9f625727712ccad18cd4404bc020951e43fe6a9444a1089ff1de9dcd66609725de17f37b2d7de74956e8dcd8e622eaf05da0e61603e605852843fb98539f41be15f64767299f038484f0d01d3b2b2f3baca9f48bbd3801906db0b844fb627762a5704613112f617618833a87acb4ad265f37d7a6dc620f29a836294912be1b42f2e755d4f60c15452954bdfd70e0a9f48567a9e17bf8020c44c9efb7405bf1c16fed61b3fa6adcd195c7dc0335f9ce1d6f416c81f3e609a9dc655a6ec1825c8f76f7af94dcf79ffc711935f4a6b3fb298f61633a2cb1054d64ed8b21f13a69df7767e076a1a9b14d111f87d98a23b2e71123fc56b61e005c064b3ec59f92dffad8322dca2dc4a943350540a4f1b7321347ce0c7ac1c30faeff2897718f06a257f23945fcb289712a4aeb15944c12809e16f2ad6bfbfd068c08c222ca68ef072b148b27080ae1cfc48ad2d699a39bb96da05b2721912b0572dd8144818b69285460d296e8cfea592eebeab881b2daf82bd649cfbfe91a03bc4db5e9570ca2a042e4ce47f6e47f2e3cc8e0caf7f11c903844307cca193d076f51c26cc64afe1a3ceecc253d02b4ac7564d2fe8aeac516a4daac320e321a086a3f856c134c6defd2fe9d8f9fbd7b08b11fb2fb1dc97d0c8748b9002ce42303cbf3499a92f8ffcb40dfe11de2d45df50b2dd1d18591048151122846de187a014e618a1cfaf970d791c2d4d9cf78308f26f599ba883f73d2bbd4b76762d0bd0bba6318d50a75d2c4d9ecdffdd0346924395fd66e656be58af28b08da74233f02b2e9e4792f9eb0e3a2b2659b06ffa546f21ed79c1ccd0454ecf735db06fd1f04a61e3c847a3a5150ffa4b034c80c7fc01ebe052140c2b54d150ec4adff314106ce02a675d84e2baca2369d785ce6785a531e33f38bd46ed7d90902b7cecc3d06515dfccf8e7ca85a3cdecb411bacf5483944093b33d7ffe23a8b860746a1d601349e7397e66fbad14acc64316ed7b8ec6e261031fd1f39d0edd625eee5530104c33cf146855252e7bba51573f39ed9ff8e053c49960b2ea7b3fa48b3bb5f7c0dd7a10be08fa9bd2d08950a8c458806cc548aaabb787889eaea43649ee1b4a3025867c3d22aad24fef1bb534711d640dbf87121147537b59fb3bb87c49bb2350ef2711cb54a1706aeb6c5f9e4a3861e5552b9b304f25f2606cf54e39fb33d790c3e4442ef1301164f7b5e3f87ee6a87656e55af58eb1a0a57957980179cb11b0f3a1ea366bdd594f37a409c748c669f26723d195c9db7df0576464c9b9b669801353eed047ae7b2da43da64e6528a302d5dab68b22a2195e31825210a2ed5760927048bd7d1a77d502b32e1f950107d7a04ae57cb4ea210c06973e3243264810c77ce8633cbd6db678c09eacbff469419740e207b83165689dea6fe4def156e01b39cca5bec572407a36a8bb7ae61aae579533bd436275bcab6dc568b50b40806a7e23ef35b094d1248f9b89ed08b61e8650cc30b59d73a9dbf91163efd5dc10ca1c2519d0f72e27a0d923070c2873f8819e0c5398b45e2e5475b52d9c4fc9c004c38b03c89efe02b32aeefee97495031371a9588732a3c0ab49c75441e403c0aad32e8de9b4307765c801797fb589cbde7af0c92baaad7366ff38630317e7009a23e554a0c041d338c3f01bf84e0a712bdb891ae525ff8139a4c6d934804e603067ab39d5a624e1b8cff58f3b70b6fc4f2408b4630bc74cbe18300e534c96a0471f47b7923b53ca318ee6f7a5fe345cfca67a93934ee023330a2a6a1ec4bb60a6328c15098f4894337bc1fc8276de7e38938cdd6666de6e8a9105f934cfba0035a2851097bc906aa71e936c296aa8adb4d0d65a002c37601b6ead639b7b8a80404c1a50ef091c2d913529d6e9325378778de2277dbc19e8810d39600197a0c0eafcc73797bd782359cad9ca39ba19a834dd3267a54680de6113a4f114878a90ddf1b44cd6e9a548beb3ebe527725532580f953e4a7de5991a5adf7605a803501451ed6ae321878b4ac05e295cf2a27f648e300eb7e5e4759e94bcd54b871170724786b8f578cac7e6cbfb5893f5fc8d35c0ba59dd9574b02079ff142832a1b0ba8a03b0da1f09c350c090e509232afbd8f817cfde9cc30476ffc1858dd0e3605238dd024ecc6f247767888dab08b7231db7035a60ce40f51eb9fcff92671150d false +check_ring_signature 6c667a3ad06a1343172f7b1a1404ff25e13e383c37be6a8e19483c800be5ea0f 5442aa2817545039884a5478c55e720fcc5ddb9b6d10b37f603a5aed55c2b13d 2 7b1f6ed65f84cd63ea947e9a3c709bf8fb1b38f60c3744dd41466c6192134918 ae8fa88f76828ec1012113eadaba40bcdb262bcb08679f826e0f09eeae5fd5c3 bea27e70a514af7a595ecafe253c03368cd9a2bd5ac765bc3758822004c86d032520d40ec3bdc6c8c9c797656e9eba5c14368a7e9c8143a25302c67c0336f102fb0133900acb03635a27e5c356e144f0bd1aa8107522cce74b2130a63749ab04dca3748ec05a87d1f13105c346470dceefcb54a2eb4bd976470b0c442ff33208 true +check_ring_signature 8320bc247ef53fc23d005186438c318ddbc953f49566ee672e741ea2e651195b 00c2c596378c31bf204c8357319164950108dff94fc013f3aa4cbdae36f4bcf3 3 a6f7965c68cff1818d11047c4ed5049ee52e5c6c785d63e01d3c65b3f6cb8b16 7f3d9220f91093d5643b224adb5d5505ffa2d334c06977468b54070c14aca3d9 ebf8c16ecae5f852d56898c6becc36b7614f4d6bca1c2518e5d36142659a33ea d0f3edc70f8f3a6a95420f6a49116110e309d53ad5694e72712102854343100cb5b5ace6e95950b5bb7a4e3c8e3124d541b56675325c614a968234a160bd0e038e25c0ac1785e76995e5d045de8322344a5cbd256473349091aa85092e1f02008b6f7ec31a48a5c774b7729ae9b42ce9617325cd194e101a5eca488fe86c33099e54347fabacfba2ade77a8a32e3211bdb5b9f132ebabfbc66e2d897c5d22a0d83292a347f7673a9ae0a1af656ec43b6e8fb40b930626c5fed9196241ed48105 true +check_ring_signature a765df56686c64961e3f2cc00a1928965987dfbeff91fa937aecefafb49d9fbe dbd5b00d014f92a395c489eda64f790c42f97148e317f3302f9aea0b92f99cc5 64 538a530dc7ece07226b781148bf2c4b245419f41e488df879256ddd4c5e9c9f1 4a51a424ae34978bdaf05a48918eab75cdc3d2d600cdf0021a68c8dc3ad93748 63ec5f8b239bbdcfbf26c44ea62e9b73150311a31a747963f09e9b9d34df5067 0cd60b3b194c26548a520350ea1a6b82f454bf5f80098ef1ed11c0601edb540d 9a1806123cafd20aea44c80878d8ec2602eaa17f6a37dc39f95468240fe4d3f1 f31c581f2ace33769285a24892983ebb9599526b1702ecf4e8d9ef4d974a0b32 f7ce30ba010c57b75d2308e459e4a8601302431c373bbd792efd52774a1003c0 ca91b54b0b08bae3a1a3e0ff37e828dbdfb8f95c7f7292f1e534152ec2ddebea 9b56ede3204ac16b588ade350266565d01f65b9e0aecabf228f9b582f6b8c5ff a558050909794bfaad8623ceb143a9056e3c4eaa0bcec64be50933f0d9e4f45d 2f3c8561b5e2922070e8215356111aa89eeccb94b6e9d6f829ac0df3c7c08e6e d565b81171e60685013ad98d38aa82422da9a3f33a86514d29a6b5e1bebfeef5 231cadfea58da4216ebb6e9161ea4e8c41c027447720f61e3c7686d88b7e628e 2ff15696b1391ac0cee09b33c5859c231deb3a40c3590d1697196f9ce5fd8439 431e81b7e3f4cbfa6031b23c286bda34c904c792e61eaffec066f148d38094bf dcb5cf86b7241a3d2506e45c7ba7a41c62742c4e7de2c3a73f71ea14bcff87e0 5649fffcd3a1b189fa61b2fc8bf7e9231ed566e7350857796fa37ce2a384f162 d270591a05cf314dc68fc28ccf8271a7924e5f89629c8937705ade8503e2e843 b145df39b9c7dab52c3141c75a787d5982b00cc34a1cc825d8381baa8bda379b 9d8c77624973b50a06094ecde4d5232ff3da7adef344d7e53b60b8e1142a82cb 0033411ea79f3cac544bc6a8badcc29bf86fa072659f58c55f2c5646690ff94c de7fb5ab67e1f97529103bf662140e3a122264220d4d46cf81cad91443fc7605 4ace7a609537edb6bef18f97c3cb30f62e81d6cd891e9dc2ab145fa6ae30616a d70e788bad677e370f197e206e2b1a2681f52cec80dcd09dd2b254a285c5d64b b7e1f0e6c3a5d4279a41826b7e6f789776728541e747a201fe0e5f5e4dc8138e febc0279ff28e5fa12eb979b40fa46a8df6cef2e2a2251cdba3a8983e19d956e 7a078cbd736d61d8019229b78d19e49e500a2c96b8449fe97575f6421dccba5a 5369d45f0b4b4b3e51b16d4dabf0a52f66cfc7bcf780f39a92def65f51264512 5627e83c0d89c2f030cadf3789d060c33003afebe0d2496900bf7c17579e05f1 405db11ecf161a66222ffa813bf135c86722605269b35213130e9b7a87c19edf 07c848728f8b387c3686ac864f02041f76186dfaa715d8206d976e9aae80ff3d 39a91c38ba35c13c826776f9620c510024fbc2b77d49781e0e916c0f7d5b0998 4e1d4a4ddb9d35b9c15736eaeeb0f35ddd98639ea187b372926ac5f0ef3c3171 bb2b3f074f48a3032d70efa3883f2c3d34df1ce39dad6138dad55d225a44509d 8b692166b1eb73545dcb982be6ce6bb543c4be4f1d00298ab7cfaa6c8c1f8b1f 08b8ed89c043b049ddcd5fde9cc92c05d0779352b5fcf7bb44695c3ac354ac8c 600426f35f5eea2d6807fafef264cfcbcb5f5d6891d0648b2227c96dcf2f794a 93f4a4fba8683722945c4a3bbb9a7d48bf6d03709c8a7c6e7ac7f45a406ac7a2 357516bea87206f154ed9dee1a2a7bb1a655eb4c5dec08113e4cd52ffb2a5966 2de36532ef0e23c0115ef715bb6924da19648f03c74765ba52f6d792fbf21ba0 0cfbbf949ea84f36f6b1a29830368e686e72ea142c77d824c47a293f0775f53b a5b1462c868bf2991ab397d9c7f246efcb8e1ddc729076d0e35dd2f904303f35 66e97d6935aaa201e35ce50fd845960ed27d6a933111a4b7985d3e7d5175f269 c014fada7abfaf439a75787bae06d5cb791460fa1059b104fb2e89c171044c2e 6c7886a9349d78cad6d58847c238c908545cb34004f34660b20109fb9f76f715 df0216581d666bc460aec459db39c6748e8e34bbe7fdfef16ef0cf1dc2c03701 eb3f4fab849371715d6d338a315c041393acea23c85162f814b42657d920d218 ba7c52088bf3b18ceef683cc8e2edd88cc6243eb167944a9b4e3b28e9f75a5b8 9ce11c88c5089b39123081c5e5b5bb74636c5ec236d56163b476afae2db3edb3 2e678b55a172f09c5f1e5ea1fc29e1242d2e404fdb74a4c9dcab89146aaf1b59 eccfd7f25bd209317aca368073b9b3d97b1855a0846812c58c5292c6f2c07683 990fa96437e86290a8ced181c56d2812700c0b5de74e67f65901606b609d6157 4d847f0a0883c0f166eb3004d2cde7aaa24b3c2f698bc467f214bc67b652cf6b 4c7778eccdeb280df66e351ea4b08cf690258cff69a5fa3bb4de606731630db7 3935e968cba286b4a32bb5ab25940d6991dd14718727d80ae50f48c841fda182 d4ff4e54341e747aa20efbc689c80afec3bd367a00bc885605b609fa7fb11b47 100cec859f306781afbb7604089dd672ee2f12b8de2790d213159b186aa91e9c 98ec27bb7a3dc9b05bf717cd2d0df846e901741820bd61f7c3f1adb2b9377d6b 1e63d088e20ea6d7ec64c454027eb2e6a477539b5ac692bbc203aef00afa46f0 13e3a050bed66f86dec196c8f81e0ad01146127a0b8623179103f813e3e954fe 084e37917026f5863337f12af75fe7a7be1fcb0bc17572d1bc9c460242c8664e dd636405bf44f60b6a674831cdd55e6d92798b09580a6a38fd9f0f7d1fd44784 2ddd394ed0a5082a85ceb9592d0bbe2f163c983afd1e67fc559ec8ab7f0609ef 1deded3ff6006167e522f76c52aad74d0ef1eda1513f8fb86eb48050bdacc53e 0f2c7c6847cac7e69f02b428f58062b88e7b7e9af697cfa5c905491fb5d8810fd5973426a37499764753792a6cdae842c74e5275439396b85ed335a2a571240fef65eb913bd15859481957a26eef5c6384c7d569ac1ab8ee689778403b41e60b6c6770bc5cb0f74ea232d18cda3497468c1f0bb61d8d56f74974d8b873d68f0a34ab4d6b8e0f37507ab3f69cafd1962103eaac2632bb11b26d007538e8bb370a49f633e8973134fd4c38b2cf69a55344336e505040c61a670528d2949d3f1d01ed9f1f5a5d00da9377d48b768bb03a228c3235e62e2f5ac20248fc3d92e3b60b8ce40d0837a3717f8110fa7b29dcc847e426b5d2dee79e24d23bd020122a060c9869ed80d4fc8ddb3767dcf8335819d91aad2dcfa3d968038b4f251a10794a098c25ed80acf7f1aaed9d3253accd4a5cb67d89339ec8b8fbec161e942594bd0a190317ce81ce63a672ec69ecb20d89fed764d7468aa0e287a4397e150911970c4e1158e6c92f0bc793f0d8853fd2eb22ca99ebb07f653b54e53a16a6b6f7b706030da04ad6ac2592f4fddab9ea6f1aa45d04d9bd72841e5a5a327a2fcdbc330bfec70949d14a56daa4d71befd8e85adf0d2a8da4134526237a93f0194bd53402494993ea34644658c15f3941f8ded47294a43696bae94e987d3ba4e2a8dc5c086173901e4de8c6746b96fa2a2a487e58f43cacee34135957fbbc34e5218ef205c99c3639bb20a41454f63283752812410a871be02cd840e3b937cdfc37140909a2eb74d4bc5a2f79cb2c658431f7c444a93450de5152cdf2e3f7779bf71fb90540ea62f57d90782a482cf632f49b7f37881a2003ff41f0985a5a71d0bac4fa00fd9ab77c529c59b035038531d7b7327689a1f481c39552aa803597fa0677240bbb316765eb6d23f8d3053905b0f0667d4b914740ff88fe79821cea6115a652003b4f142c12ceb20acfdd2cfa79e175ca419d1bb374f825af9e458b2ab387cb0ce9ee9cfa379ea8de7a416c9c8d8eebb9d7c6e070454358f019d927e26f805f0ab316b5ba76fb3ff87171f5d1d946f76b81452c70640b8c967c15836dab269e062d3657072e3d778c0a58f6c1e039c9b350cb3542b7c1020f7816b6f0cd51510be957592eccaa91ce72961fec739af163d408941a0c60d51880eb91a4539a4c0713a5165178423e0c0a256f98ab33dcb6c37f2bfa58afa738ed3a369cdeac5f017223788443b2e3fcef15c51f5ec5f8e9d64ac972ff96d84eb4b2c46816a23f0cab005cb61bff96367f754b04bd406910ac01a02a0cf4d72c23bc8d2ae715830773c8841d212a4c115576713b2a1c76f08eb76898100362aecd5cc994bb963a0525bcd2343ef85b6c5bcf8f45ddf6133e4c7e536964acc1bb04b38ed9ceaaf405ab5d5c280c87d736fe569e99cc4a9c52140e1f1a55a00aa0c8b568fc87af6600de8c7fc235f7740e410db3acc3011dccc008a6bc6d630e6fba369852faeef40ec91c6e5ef2cbea9b23dc207e9426dd4dd54561fa5bf1c82e67e116be5f925b03b8c4af45ec310093d1495d74d5a3c5c1c471efb15991329e831e506a553afc06dc3541cc1a5180e1ee53455a9ff2f31736580a6359dfcbbb80c89b192131fd034220f31a9e4656885a577c06c41e737b80b8a4656eb539ecd93335b0984a4d009bcbf472581f8fc2dd6832943adbe92b073a6befe5e25c3001d04d5848825e0d45121413ffdc8c1276088a2ada8cc2949f638e34f00c0a99e263adf2fb9e650e25f8904e538b08c09423cc382b7aea3e525d94a9010552ec236d4c06f3d37901b758f1f50be5ba4b5d36e75b95ad3cb95db637e3a3b2505b9dcab52ad8280701c96dbcf817cdf3e7ea64579fd2550f6f0980cb8514352b113ecfabbf6d8c80078c2eab9e5a0442582cb22645057728024934f95c653b297fdaa886dea70f0402ed7bceb522b31b1cb27eac61e8e0dae54bf7c7ebb0c832bc471ad43b0e43ac07283fdd131ba43d2c9efc567a2bde1b625fa46113fee11ea98b9375d27eff3104592a14a9c22086a78a36bd4ae2f575193ced1b8808d6bd8669133e0daf97230436683d4a53cb2789e73b5cc0aa9c5e71608c44cdc03e3755fae9fd4b52dfe20d47b623d843c93f0f7cfbc319ebdc81232b28a6fadf5f2d7a93ff61ad92422f0129cbd176be84e18a76969c4699e4c5b13e6be610bdb5b93553ee1f742fd8690b09e9a927e780c0696f542a8e5a1910a508ab23af54e49b74619a6d9a5fab260f9948c52c537e6cdaa9be8a3b00ef9a9b865fe394ff3e36be570def0d28e08406d4dddf4f758600f89c98926d806b579c43d6e96d8bcf63a6c8a64f2b473e200b07af819b03f73591335e3689d32f33225e09327f4e8343a2e332b808948d8102e1971f0303855e308cc9172f7886747364738a5e67b460efe16e15c070e024081d7410fb2c7811382d10c44596d13e7f743834e9dc019aff4e10128a4ef54b0926058b3aea497f0105e4171875fc21418c2fcb89b9a0fdf7b74666c0d12110022c376bd8b7a265648d2a46d312e6bc4178dee831ad3d9e9a649af199120a560012e572f491a813f09cf1b5881b80a33426ae308065b7d85c95031cdbedd1d70206c6558fd40d4056a01d437337d1a43d22872ab58d683025ab8d37041e98ff07631f87869ffed808220dd2e906901fb1d3fca934ba3858d7213d4fbcaf21a208175fd2c56e439cb4b94df079e6e991b572ca647be223e43179346284e5048d0413b6820eae5419a0ee48aebbfb2ead2276ca331d0d206c18e4e6e599732e980968784624f90f0b05e747497df25647b995bf57706d8625aa86c6897be55b950c5ad448c7691ff0e3c46e9ff3a8a035a4a26ce8f7ee41f07111bfcc0d988bec080646b51b9b918bad7ff7e0251234e5ced2ae733447e2ba6b51391f9e45c7ca0dfcef8863fb3e471deb0902dd81ad4f54a5c96fee88d2a240b06da0b54159440a7ef1dde6cd0b767de7fbc6b479b75a560c63c0ea905f9b42460608cf4110280badd0fb578d53eec347d01c2406375b06e3fa712c213d0bf5357ebda7955d600cde38f3054c7728dc984a86df08d674158fc0ffc139c921f9186b604a05fd3d0e42e1450640ea7edb1cb5b5c8ae3384dfc29d47d6a3daaf414259b521772aae024eb5871c943db21c00aef094728d82e9a0c9b8fa6afeb621597baff31dd09009a41b0ca8e7792fc70acac34ee9e4531227ce5735c6efa1366372b8bb496bca026291f6340ae8163d5ab203b81f5ce1dc2d58e9e90dee5f99fcc638f2eed59f017720f1489a7c6e670ccc08233b3b9253fae9dd2baf9d9e3bca5cd0d6c2b8630819e9662f236facd405d00b261bd1711173fd4a0e15b509d54a1ae79f4a000e06fbd4ad7f32a07cc667db730f98b86a7fe68301ec30ef9f06154490ad298b300e92b8e3ef2fc35361ad57035156b0a7573cf408d769eac6d6d5c6b2aca5b6ea0f634a4b173f05a383e574eb24fa6ff45e61a59c2df1de8ac5e350ccc796eacf0609c8366c8c9c16531b6c81adb5ed7da30cab12b9dc6f994465a2f7c97d173b0e550ebad963bc99944ca0637ec57d5483494a4560bfa43f24124ec429c9b3a5067bb3a05f68f09a98c122d070b18b1aa5a61fb24048c6000e026b2de1e3473606b6c38a5e140035651e09281990d2dc4a5d69f2198373ad90caa9c92245ca250f8ff8a1382e01ff5daa2c402e6c433ccab48a29de3571a035f3ebf6d98b73cc0bc43d5f4e5ede4405a1e5d84688dd3ba538847017f001ee4924bb1636729eb906f555a5f15e6036a060a8883bc1dff0bac5d49091236372845a062a32e90a970eaffd9f4e0c4b09c57490dfd0a016de3bcecdc392615d493e7dfe1360a0a12d0d01acbff3f0764df3abf6011b244e543b9c33b8000cf381d60582828366b1d90668688209bb9cd2e7a09249a09aeac71ab87753da179d6cea426bbbf1c278ee0e379cb87b774f5530c877ef15ac2468be71177940a4d8e0de022fb7c63130f60419a85a53922848d72eaf259d4a5002c5505702233b0c3d94bd30df1f799bba0bf7752abddb778e51dc53b179f343dac2b646169d35099636c5a3f077933d9104b690ddaa8f9b9cf2291fc7ca74ce0f2f80f0d0e47fbc0acc34810cc4515dfb07999cff0048b67ccd2f0b459f718f5f38a094a13a8c6bec07992989c85e0ba907254b8959026c4aa1c44d549f088ac56983785c610056e52e22d1ff93ba74fc006f6633d8f0d814fd1d8b2a85df99f1cd9ab82eb75a530d756d4d1118ea6ce909d65f1193233963ca90ea87a86b1c01f7afedcdfe1cbb34d4434e74035e852f037f3c89deb8e855e27d3a87af432b2138041a28c018366eb50763cb4b9896a60d2a558c5f9627dec201c12e05b76d4a039dc5b9bcfdec351c6f7840d0d59d97091d8af2dae99d24f835e609da52e9b1d9638493878da5647954989eb735621b0ad0e9d6aa48626238b426a343952e5ba1b7b51c8272aadee053b7f96202621407f00e33fa942e4325bb277df630be5201800f9369edfc77f0d9db25bbaed9830b9fb0f89e8591fdafa24c835cce1e34228c0b34801e6a2547f758c880022c6e0563c76a535fd460438b72b59000e04dda2440fabe3c51c07dd0ef402e5f9c2500a62c31bc7c2d109e24591ae6455598371672712755aa3036c9d3d429fa0f590b90d13a0ec7c71bcee14a9c6e7bada29af062b5544fa2b612337f85c3e4fed50d925c81cb39824379c0512ed900e0c8ee38b8e4d1369851a937c46581427b5d09ee10e2ce967982e37ba1654c7d275d9fad8a488fe0086562e089f8e3bfd98e02a58bfe1a0c54bf386be88c40f1ff7a1f5cb377f3f7e465c51a0ad2df01ea7c089fb63760f92c2b8688e87e002372808bea972f1d545cddbc89ba59970f83e3090a1c2bef72050c044851c484a233e074f7d1fb627cf17e449f7a8acb0285790bac847648ba6bc1ab3ebdcbd23935a567cb9d8e10d8a835b78a0e2963d124b30e4ffca2cc50af9ab7410c58c8d92afc6af83d4734ee92ad454d865a93785f9d00a9361ef4616a10abd7ee9521bdc7542431ea370929d5f37819f7dfffda9d550a17db531f63e25a0f1711eb7d321552e9ef98a47a7d423feab3c460c11b7f52087a4db2ec68fcc16068489417aea1c7f873447ba4b35339ce3229c177ae8cb507af63bfd0c36ab694b2230a41cc3a4664b79504a69d0b856fe5216eb95d7728018fc18061412322597ab3fadc5f318a9444ce10cd55e1b31cc93d5cfefd454f0f0e9566586a8b7c0ed7fcc1020622860da0ca1800b82ac1ee081282f224b3f6073730afa149b4e7a6b72c36d7f23961f1fb2cc43b370eb8381fb09c3dd3ee4a0c3011d11e3f88b84c5cacec57932c1b84f8cb6a818a1134933f3ad263d061280f1ab5e6acd31c4aee70b617f3dca25f4651476e3c9927eef7a4aa40dad4ad109578c9388ddd3c1d6bf68ab91884fcfd171bf24c702cf715c881c9945aecce330c180aedee0931155c87193ecbf2023c5bbda798d7aa0cbf1ab08bc0169311740b31e7300c116c02d1dda7d9b45f82bf029db59c8a4d7c6e1753ad7d6a4e719e00125ffe5a222c21aa78402c03b7aea79eea3c712151d3aa8f82e5bfce16da8c0ee1faa1e3ed6137d841fa85015958e88d612e191c6107d926db3a0a3ca6d254026a9a205ce0db468296a177a249fe0e9b93c6b53fa800414715c30fd0ea42970bb85f66144030262b71e69be3af7c4f5a7b0e077f54692606c91eca5522f0a400 false +check_ring_signature e09bed74987ddca9249fd3406f3a601e7cb7acd98a7c0f7e9e3e73bcbd4b0861 f9cf4778f44c7ac6b2b4ddb78574a78f079a9de2527855ade04189e48dd3cf44 10 bc7501fb261a9a79efe1f0e7f9b6e935ddb0cb296579b942cc35561bc0220a43 a1d6c10cb71a3f977086d5da4d2ef694ce53f0e4329ef767392a8a3623956d78 d0c24f0ee7e9c2518d2dfe4799751a80d8ce65027e0a32d817358ffac6e507b2 5ca99e0880a841b76831ae757b4cd3ecce91542f6eb89e2064b2c266ec4bfedf becc688408aba76d77239d737dc4505deac0415ccc1084ba7744b82e101e5759 ecbc9a7f7d3efc8a2ae470a8855dff94b9564d8408d8a4360240b8040320a741 8f03841d0f6ec067c4a520f3e2ab5b8994baefef02bdee3130778eb1739e6e6e 84c465f7992bd3cb92925aa16bf92455a63bfba6b6890d350f6fe70f460d7fa3 5ac6c9457f84682ade0a2ce2493463372999f6d7121f44b99d1979ad99a9d7f0 51ec1458626cfb59fdce94419e860f8d671044b5b40d9e749873f2dfe9875241 40e5e883ea3e821ea1328907f67e2f0732a8ba9e7446e994435b05f70bf5e109d7bbf1ede5fc35b4e5a5198facedd182f9be6f082c35f59eab488de0cf9f3d0dbc67b5cd4a91282d1ee34651f3ff623f4fca171ba2c3ae3b1b596440c4eca80b05e296fd1b423b694d8b52ca3b3733916a72d857132e6ce647eeb92f4ded71038d42104f7c2e8fc08916b408c5a221a94c8833dcb3bdd44ccd2e13401e0894679a7743f18089558f8175c7f0f12f697bc8bb449024028540b9d9924370316a733836e479c566801180aca8dc7a872824a15a4c6b018217ccfad746cb244de701c25d0a8df5343560201e2698970034f3e1ac30b649ab9f2fe2d4d347dd559b02226584e71bff2f39d5d4fb48c44c5284e5b4ad33bdd2c1dd239046f99b0fe70a0f4938b7ad7aba42b3cc0f5c217978701e15423e61b86de36ad8a3656c619407d6eaa29afc6beeb82ceb66098dafe91adf8bdd150a6ca41a5ba2362428ae770b5022f7eaad49da801376266fe9952c266bff52c0f166469942819ca546d6a30f7e6e35b6a4c9178a6e6bd53c5a602b128e0f450c517f66e057bc2cfe082758069f366e758f3623a2a028958e72f796e86645051db1073451268e7864c3d35d0b93cc87c23a45df31ccee83bf5076e12e2c008d848edd368aea2bf911553efd024c0dd5a87f555fe62ddded2fdb5ece311e77e76835496160d0dc70de1685600bc4f8d8700bae9ac077a96ce31b1c7f38cde15d1ee4b49c5d02ad15ec848c670932f04f05ea0a8e5fce47507414e8f96d997cda2cbb013230191e558c3c599b034f0b6cd836b13c7ee1b2e552194d1da8800b541a940cf49efa1971f502531807a7f46b758e3bbb2b56df31ad21cde703ed9af38208699a039ca1cfb0503f7c06 false +check_ring_signature a42f694f11fb98edfa2a96c2c459be9294d2f2bf1ac2785d77c74da984762439 2c650c3740eb3d76f1a73c93136b8f0ce925f878ff131323c8f2442890fbbbaa 33 413c32c1584241e6d88122e12dead7ce666ff95304e1c5d5c03b5c7f80ece601 078f5a7b267b91f561ec52d7a5ddbe29396222916d31f50ae3af57ecb1e4a420 58e1bd1c05f43e490edf4869bd0d49581de1eecf2a2c619ee7f6a0b27f62d031 9d9aa062429945224ff6a89bfd88b09d92018669c05ef62455c3a6c999ebe6d3 764025cedaf2b7af0584c318d50715e713ec63e0570e74d0ea96c8eb137a244b ab7e6b3ea378f0a6b0cda1f5e69d362ac4b3886589ce33f8543ada4d28d4478c d54d7079635efc934fe1ddd948fe7b18b73ab3a2ada6af7b038840edba76d431 0ffc4277020e2ada6b7bdc833708804c4c3582047eab59bbf423afc8e319096d 9c87e833829bd899d08d1f3b91b558252cff2a800a452f01562a67daf4129a5b 697d19b9aa3070c1474b8b533373fb9b2577de97147c1c82e25d316ccb6cf93c 4054ce02750a3686520a603dce4a3f55fb5f428712ff10d68f7af8e0929b3129 f7a9b8a12f6ed17cbf1104fadccee8247e976b11af4575bd19439b26cc0f90ca 7f3f1e4f005a255b64181d7c128ebb8132e265436a583493f8b82173b1ba6f3c 6ee9b0c6c841d0e9536182eb81b1ff33d5ca40c918030f587e901194ce69cd67 4627849f111d8c740c6dfe472f3d9cb9733ef3eab4c74f2fdb1d482f2f83e536 37c7c5575d3ea89615b5359c527a598f26cb3928c8dd7901f960a941d205edd8 081f7946e1e57f08e06353418e7410cce54adc77b216f021400a75b374c2baf3 7544be1e1f7398a837b99d1c7c777fb06e846af334cda9b1a6f1df47f634ddeb 4fd3b147f3cb29aa0db7a163ec4f169dbecbc10c3def525dba03806edb1d9779 83077fc11c18c69d85c8c321d6f9e0edce109c221e07328b617217a87d94bf89 ba67596fa729897520fef26dd163689d6d4cf49aee087ae690c8efc4046fa1c7 e3116b5884d721551bca278af72bda0744e18bb65ac65c4c74f7b9355b884579 67d38f1e47e71552604a0946ecfa3713de34a49d2c6fa0bcea8156fdc1ff6b9a 170f388c0cae816ad63c7c676bc317cd1b35911b8e6ed88961f29161d6d1fdef 9bb1203b536fac6b05efe82697192a8bcad0df05edaf9d6f86e4022d1ce81333 c22d178feb50d1da048c53cbcdb33fa55a81867c63c8c4386ff96b11218665cc d2d5abf7df534f0d7b4abdf121cdc52bfdc3a3c981ff8cc33b1a56001f69157b 385797a863330121a2c7f5d2f33d857c7ed5df368570c847565705ff325fa503 1fdc7b8b8eb5874bfd3e20b18b15deaa7f60da6119173b6b083b165ed53b1a93 cceb1ef5223e750d7d1773d41eae7fe2c241be94b134689d2ccbf5ea57e4bf71 fafed6a5054ce2f162a3fe034d049c1da728de01c0ff4d0a69826b43109037f6 65bc46c640422a1f09afbaa0a86ec5b46c807fb574045ae82e7dd318b198ca32 c9a98d7945651d6d65eb263753f33f67a9362217f307a19434edd8cb13448f91 17d14d18aee34b50060230ec4d06f39d26fe73985402919c4e2d316e955bcb0fee3d686a9252d9fb4f48def9ac4f94fed5444b4f21511a3f2dc8261f1439cf0afb7a121b3b35c1fb8ab3b199d9109dd399c3c14ad6e01c8b51bf09c6bef1ea0f638218832a9b4517e994c03301ca8be1c3ce0ad2331ba55e7ca2fd16529d4f071bace05f3871681b87bdf3cc7b766c54b15ef38424b7ec4a7e9a9a8c1ca93707d3ac9f4118051a3c1c2eaf83cee9a0bd03e1f380aeafcf993981e8fae1f0b005b502095e53611762591c6d73a862470bfd3988fff101dc21a1995a3806476e061085b64120135d3291aa902b4fdca6e758ed2d45a8d81c33cfb6f87807a26400d2c471e9d969a1631e55783df85fa50883813663a492e1bfae2ac5d7b6e0a80d8d1449d322be5ad2f7b6fdeedb604b01d40cade0aad4026c88e32fb417967f0f3d62d3199edc3a120d87d1fbfa1e6cb4b0d8d08f689e7b8f49fa71d73135e40aac18f40b6742a5e9483eab5ec99d58bb0e742e2266f37ed2c45a341c31dd370b135d646d0696dc0a62f53a2d41b2ffcbd8e904dd1d98f231c7e9c05f36f6840f2bbead6113889f2ed81178bd34e93bf419cf3403b3b5915ef78678ec68973d0dfa355fd2e2684c2d69639ad42135ef74132d9525e689c7bd55a6971c779d44049206d7072f33d36a67ef9c278fdb4e43a7329cef1ba1b1bb3d990c512334e60b0a69301cf1b35fd38a16d4a5878f95e361d9021db0a358ba222b4c30b949d7f7cb690782fb80a08b2632cc2c98f437a938845640a6cd8a3f4ddceb470489cb0b372f1540835e636be1bac4a78deb9143ab29fb413f88c1059141be01e462d50f8040f1056c663fa9142342d5cf05b62785c638cb8bb1feb343d5f5e28282cb04858413fc7aed7c43ac3a43985ed4509e126f07b3076cf73b9477b67d9135c001e46aeea4afa8a7d02315dbba764c7aba860ece4f9c8d2a27732d56b9660d7603ba1c6f35b9c2fd1cc0821973f83112eb1939763cb4bb3ebaf4d69501227761042e6b6c49821e2198c6d718fc41a8bf30650fb2b6dd81eb81bdeb7096908c4806efb0416f9db598b26f5cc4432fbc288a194f4c88010955f086139e2b810d300a5e87543932cc840fcc9feca230a0dbfcacdd77794f541a00c32d6c54d673e8078eecdc9538dddaa57e1855dac309ecb840cfa6bd88cea53574e37a5fb44a6e053578bafd96ab95d3a16de8b217ccb7f42bce127dbe5ddc9761666755a1e04f0624812400628b889f132fd122e415445691022cc26ad90f980c0ce4d99c224108e5cabbce99fc402d05874376a7101150e497874e6796eea837c2651a550cda0fb7599c3463137412ee46efc1f738c3b4d11c39414e9988efe3b7dcc9aeff3605b44a75d35b43e49ddd72f03ac23b6b65b95ba2e696d7bca01285100ea17ffd049a620d831ef682ecd66da4d3ff378ddb1d50cca65d13ec0c435895102ad1c400c078fb4ba1a0ac462a29696ec5884636d61ac90191ba662edea00a405849bd0442b6e62a35160f830e8dbaa931a743fad25dda3914b35c8196c1eee6aec2230e8864589abaae5e14e2b045cc496b6b7c7e74774a3b6cce8e687d91d5625f7008b83a565a1220755dd9811e07531c97d5cdc7e1990fc003553fcbd7ae4b5518072e973e48f92b9244409f4b800871a866cd7adffa8b1047fd1c8159576b45a603807db8169127e63e1199473e8c2c7a4005a6efd83d3c5f2a8455fd1bae5b980a3e135d798333e9587c76510a16e2d940c60663924281946f728e5dfa2c99270aeb26b567b56ffa890f56cae8699482de8420fdc7dda0b88de84acbe3e621580c23c4d301e3d2032ccfd84934b0b78a9a6a1086f6bfeb5aba16787b0bab42010913b443fbb3bc4a7a5eb09b1b5028e73276f56709806f119dbb47ba2f04136708e010aa895a74c7a9fbebba3c938adc030112bd4deff7c1449cfd186a6c3bb50db1f776297e0c2716661dbb1383f950f9d44383d9d2f1782befba42b81477f900b884f8825335035bb4a04db001d613475385f9deb7fb302685996309beb0aa0a5484cefe98eeddfc0cedb26afd76e09a4a67be35d3d66f5c4c2b2a14da842200965a0c681d49adff3d899c17570317919fe49fdd22060339ccc9173944f1e50613a5b81d2ab39acef3ff1af18b82d07e4d28565e1e82f2d419ca25587efd7f038fdcaade36b29e235fc76905610cd5554c568409915850a89d221d2236557e035a1bfb85d9cbc3dacac8db6f46288e32a9cea664d9aea2b9adf164886cfb4208344eafa85b2987819806d482e17fc975b796ca9dc09e43662735ff8826e94e023f0d4a9e037415e75cf86cebdb916b864a69d52981a300f0aa3953a1ccad2c060c465cada9ac113d6ba1b0cdad988b6f3a2b7712472366002f3a3cffcb8a200b8ad1062876fbe75ac845554fcfed7e8af50475d72bed4383bbd2767d86579e04b2bd3230545c15ae2ea028f826e2a54a555bad896acbb70af1a6aff04c4de90026dc5b55265eca0a124359643d7f37648dc22b7b9e5803a037c21a069f63880f471036320c9e1c6a532639b69bcf06111f07e5cc55effc996e5169de26126a05816896c0a512c444087dad43bf547b29296f2eb9ad093e2ecceb3da75f3e62016608908bd6c817bba2829dfad1d0f40857dcc456f16c253b3153ab33848b3e780ac950441992fe981eddb895be2df98d4eef3b66b55a5c2c6229a6bf8cb7a40d71c4162ad7eacd123ce5c1f88715d25f4fae981da518eabdba4836e54aa1ff0c0034c6596c8b0d9910d2a4d7cfbc2ce4109c72145267500b050ab96682c44805c2986332bf2cc9c9953231eb1b8810fc3a22d5970da780ff6bfbd53b43fc030763f9715b421806cc5707e78b714ea0c22753ab5771de454c52521807543e870f44158f52724f3b3b22d320b3d3fea4f1ec8762d5e0485e43f37cbc8e6aad6500 false +check_ring_signature 3f6e19a113660518bdd3885c584057ac87af121bb0ea088cac72871bf20df9c0 c697102887c7a23b753ea625206a1fc49204244151615758782449b87cc0bb01 119 68d4404584cc77c9f763e718d7de8f7e9adf43b9dd3b4eeec959c71a442f824a d45ed18de8a3836c35b5b83369a089090d534ef7958aa4c0ba8552d96b66b551 563aa25d566aeb00357a0f6d7a4b777aa780ad44e03ae7ab6b5459beef8abac0 fbb52398621413946cc02fb8737018a4ef8ed53e44397123d88613fcb7f4ffde 3364c21fa387d1d7159da03b50653f4d3b4481264ae9723a93025a76949123a2 f60cd0ff040035dd388a679b38bbc9c54775059c2ad3a701b73eb6abc2e2b372 270c4941d45d4ac458bf43b3568b3796006e04be8b1a6ba86287506bcd3c40a8 43e47ce5db4bc0f8349204ae111efbc7e4ef853abdddc70e075d19b87ff7c561 1567783030e27fb64f55ca4727eff79145f21f2195581fcdd1d18be88f38ae0a 65a4d85fa50eb4045ed820b81810aad4989cf8d3fef5edf3fdc2f4f4d1e265f2 8a43da166d03f0ee49a1a47a033b58817fb2f22d31a56d702784b3b12a06768e bf611da78e9f6b87a9e8de258bcd95063f4b0370d9184922fe8142395ebd6a7a f9e4e577d1bc377e4965776d39ba79bb52a552024eea645085adfb3c04668bf0 bcdf2053f9d037cf5c52e74c3be8758a25e294e00a1a5cd26b69000456d8ad71 6484492a8949d8c2a5db7950d9c66faf13c66f20984b9a1f2864adde7d0f7103 99fda76902f0723308ec4fd48675593da14681bbcbd21ee6ff2fdb9faccccafc 605998d969c5d6c5541d64bc51e86a9c22e026fab8e678ecb0b6c1600c733681 37f1cc1b5585b4b40d930f2ebd014d79904d08995dbe9aeab54ebe127ed39e2c 5dfcaf86b203697b65f642cc686cf638e9c08f4421027553a55010b91155b9e2 4aa3b1ba9531e85f3a11898d9e6039f6446adf2b6869d5ceb540cd87925faa6a 06d8ff1dc3a4939f92aa56416991cf32753d95ed9563400ba92e44e0142f2a56 5b0e8f76d5a90fb6974bd116a3d0515d0c2e3a2b434c49ca079bbfdbeb53fc82 48e995e3e8f810aebb66d7777c1e0150ab1d22fe983274b79dacd760b0e316d0 16e024a459e8787a94238c224604607630c91766053ee8301dd7b63c3966b391 97b9753e522b078ef93851c77cc1fa3276af62fa001686eb6a7b9c5c70cc7e94 b5da07dd963382dbe69c36f7a3866b7b676c5eb606c5280cdbaddfeca6fd77d8 45cc0732b102f4524911c0813caec9b857c49081ff1c55fd7615971c7fe8f976 cd9e4564965d4d9d4ae7494548023e2ec354cf0f41acf5bfbec3089bbb2b49c1 87ccedeef05057a071392c32e1238d91d1b8082a5369c6090d0569059704f3a4 9695afe5bc8760a973fe467b9e9101ff46c0047b1dea5febf47ef675397f6f9d 84de758d147164f77e6eb0e1ad2ae1c675c295e307735796c34fdedf8d03c836 40fcb1305e007979ec1b953b6a97d617da7bde5e91694eb80da46bcc977de59e 45c36290499f9ab99e033e9b8e15be5d5a22e9561c3978f91d2cc5870727ebc5 ab7065c775e82ef671d5f4fe937126dd58a330bffb9457067606d8ab314b7ee0 710a98ea5744d9cd20181e94c72afeb6bee1dcf13fee4199bff3c4c500540242 962e4858fa002ec2d19d613e663bb25ca6789c8575853510dd38bb548a67bdf6 c230d71ecc8b9694387c904fcd54f6eb537e4d207bc4afd43490a5dcd45d8ff0 47eae6bee0a18c51e6c476e765308e4e00db957e046fc56b9b1125215445c2ee 672fe474a1b4122a387cf82c3fbccebac683c26017455aa9ddb2371328e8766e b1f46299e0a14cfb084c301f6597de00ebeff9166e712d45fe5fbcbc344207c3 149dfeab206e3097a45bf8672517cf3088b54d57e02f44412c7a1186fe1f732e 57a48e1fcc8d79bcbc38370716dbc9a9cde9eaf33466d9e7d33b5b8df7ee4009 d242956d482d7151a2fb4dbe1df53c9c0d26906e9a63cc077cab54712eca55c9 24f53cb7efd0eab4d87afe0d72defdfea309ef071d2ceb9d4cec3f363fade7b4 b404a807e83170adeda298118994b5c628212ef2714dcff479aa6d22e126fbbf 8aefe37382f11be6d40fd1b1331e33545b7ebfeb1690a161f1ebf770ee325841 9b61f7b60894b4c6cbf113207610f20ad966563f012fc091c03fe9fefa779604 40c8ed49b05fb8977e0b7d2c74f7f446ac5220c31dbe21f0d66f4b69368015f7 510ad88913dc6c4d89bc47993aa4cc6c6d1e594d2ec168183633a9c91b37c79f 9907fb8122d590e53af04416888ba3c7a65e9255b04ee3a7f7f051b5c5fe1112 ea91422f7d2682862bdb771f5f698ec1d468b237928423fa151d35058a6de219 3b731f86e13abce1b68281aee82d52015d70c5f40e513353da8b60bac0865eb6 b070b42b0a523955ca3b56a5d67e28f3b2ac318d9c1a8572d99546d815320bb4 9e732b889b77fe520670e558b3ba49de719119cf17333a92f68bd0bd532594b6 49edb3945dcef272cecd0ba1e3916d230a0e2c2f1ba63a216dc357504b7afaea 6631a34256778ee680b24641dc89fccb47aba8f961358699e8357839ae8cf7eb c97b6f42b5d1561b2c09d2f3cbef0df7fa5e0f89c9d441652345a5735353665c 238691ce27696b378f65c0ef2d234eb96b400cb5f519851e1d736fe0ce5fd767 83bceab34e186450c860b0475b2e9a50a390ffabd3f7fc14a73a1ce8f79dfade 026b395c35a45ce60b8abe9559975da4a1eab994ef42e45cc20b0929ad3fdd3f 459d95a223b709058fb6735b6c3ab21794677c4ed3b8346db0f8e92ceec08d5c 72d1637b0a5b13cd0a3ed25b774cd012bec8fa1c935e29be0dace81819fa8870 c4271776d2bf7ed15cb6adca14cce47a1e88a2748f0d0d529076be93999b6e46 f1aac35e94a87a0d3f6e4c0a09c99168080994cd006b8ddb9e09169d8d514faf be1f5a4a3634934d34421c7926579ccc174806c4e51a16dd149f14a78aa6ace3 9fb84ed3f7b29be5d40c12aa9a2c76d89a99ee41988a5479130da5bf4de4d230 06872e62aac5be4bf04adef32d91a299af9debe6fde7284c71ba69bc832c4ab9 525552f4f08d87c5edfc5f2c0cf3c7244ab934590f2aa3030a90d22321376d4f bc1c8604956196fb84ac47dbfead67eaa2fef58991d53ad12bcc18c5ba886e79 c9eb25a8ffb6d8edad20ea962cfe3d740c333e5919164036c24d24a2cf83478c 7a43781043d91dd70fe6c538f7bdb6e81e2fa1ce38ab912194e09f648b5bfcfb 696bfd22bc968309f0f3418d5102e68055b3b461ab7873330c1995c94a074d6e 756620982195a310a31d007f1df26506522ecd9e8b8cf5bd90e9d8d0ad09ff98 e8e0a320f5c8d04991e8cc4ac155689f02a66ef3eb14f7448f97cd48d8bfdc6f 04ec69d5047bb6d2cc0240657d52dc11dc2725a309afff7c511bee62dc791adc 1f37aaa230e0e4a52a0fe5b73e6a02f10c00ac98c6c860a9cb5bcac6fbb3ac8f d7e855793b82bc5bd9ec004dc93a62448ab6a03f8e4c61172a45a43d9ae749ad 0866ca5169f80e3a783c52ec794f7552d89920a68c57efbb39359a831deabe61 d65661c642f60ed9c29b7ed639d146cabaa009ec3cfa9082fd3e3b9a88e87a91 418a3a33367fada2cfefd9988a138420b96c1cef4cc01ff76f6d0c58b0c0c7f7 1f53fc5f4ed25ab3351659f5e33a5a6c6fb65c132e4d2c256b7f4d510a97da69 bc971d6e0fb6dc0b372fe53919eac7994b5c1b413d291153600505959157bd35 0dfb6fb23cefd29b2cd3b27092da6d03377a1c55787f58ef1fdbe7a24a8473f2 627174f0c2fbd9ce4e098ff6d322e56fa571de124eab3e6858701b7d46effa52 51936c4fdf4575e2bbe5fdcbb11a03f1e35a3aaa743d5b05f7b64a9f55a20951 969d0cf3befea893c0e46b7b48004457bbcbe5c3af80d027b27c9d7fa7eda444 c0ac35e07862d79407154d52e11348b807f6604c24abecf6ba98f3eae6ae9bf0 c2514403f3c93189b71d77f44423d3ddefe4fdcbba9f6cf3d7cba81cca3e2916 6af41000ebc64392f398cbff8bc9e15ea7263bfebf10019d78e3a5ef94e6f5e0 268bd26ee75b2c786356331a29fad0f97720b4ce20e036a2eaeb55afb6a9ba26 fb5dcd7bfb05087f179798fcdd02e5916d46b5ef003f22731eaef8e005d8e4cc 68676c68c4122e4ca2faf8ea9da085fb5e7836fd6be15591cbbf5b492d9843a0 14559572be4f5e65e03312b043cf4d5e2ab0709a47fc6ed832909014764a2cd7 f082b0644661cbb7b8c821236562d442b3de9bc1cfc0bae69b556e48382e58aa bb5d8a5df43b7e151035331c780292bcb5b575c4046480895dabce96c54bd100 129f63f6e284020cf9b005f43e36a93a4cf410a45f8c2d5b1829312d6453d5d7 6bef095bf9724c821697bcdb5240c258ac3a64fed17d8a124cda8c04342b49ac 9b61a6b4b9c75039e1917d03aff7303a9620a8e0c0ac46a0f84b4dd52cd49b18 72d0e4163e2e44e33be94227dc8581b2ad5bb17f309f004f2dbc6d77b64ebcdc e0155a818e042b422189cb6a48aa9c9b049d65df6c184e2a2c7051fd8f78772b 017f38e313238d3167d12e4739ee470e5476b2d95bc2ecbb51a36ce49a3bbc59 217b491bd63a5f7ba007fba41853615264e511bfb5214cec9703e54b8fbe76c5 ebb5d7a7172dff9376f3a99709b87b85c2a393704f0e13a8b28679e080646606 b26f08dd8d65c7a1a8b7570a00a80f49a3171f95e7184495fa9daa8604960b91 b7c905c46d11f4c40839a1c895eec2908dac7c5cf5a66442c4886a1af478a342 33e45dbd86aca22d720cfbcc350985ac6aba1a2a0080d7052bc3b70f4366144a f3912a2e93c46e82b597c48891762d28123a589f9818d32becf5e8af8a19da06 3d2e5e8fe1cb25612e20f9220b0ed93ea707de52e9bb171f75fa8910efd9d2f3 796adfa1946e2d3c4ae59181900890121b49537db5b42db6e9a02b150c9323ac 71fa510b89d5398c2c00969369a17df716562b89f99309a5bd52d55c06a6d287 9a622b736b5c842d0bba385578c4c0cc28a591e23636fc888a5585135e445dc1 a7aa4f5a3db3c4d39a2ff8d2b9f3b2b06d47c4647ccaa16a7254f49960f05526 947f7add0706e37dd9ed3af4201053ae5b3d7d9719fb066623a529f108e67de0 a6afa8c5d40cb938d101f42940caab2f822dc4ce5c9ed1b6747b7be61b164a41 efa23aa377307f7a32dd9b44c6414ffdc17b3621b65ef302bb1488c6c4c65ff3 3387d491f2c24e85808007106b48a50d97197dc398fe54eddf53646b08324335 6be812f61d93337e7b2cdc31151ef9e5298658d01ed60c4bc5ec98c5d90793a2 836188780946e7454a3d2f9bb17ded385872f5489798bbc148b37fc48c9cf1a9 ffb7125f47e5803a34ee99b21cc5f48670c993acacd4baffd83bfd952301e1ac 6c4627d2d06137159e80da1ed359181362d81e3f1c52141fb5b5f74e671dc90a71b10c37df47c117ef45f3616b7749c78f372fc57edb75a169dc4f7b22051e0f5e6355f3b154922feec7496345a15e91e76d4d7f680436ad01783d7eda409a0add42de1a663e8eb7bbdd0f6251da074f2687a717f24a464b93c25de9ab72410f279f92bd4c311f1f269b0903e186d2eb10471a1613f25a27397b4b47db20d5047364f2d59ab2f8c0c85ece999cb8cfeda88f66c61d28fbf5cde911aa59d6430b0866306f664e36b68f58c1568d335f5b987462b9626eecb02e73afa30dddfd072078a3822284f274055fcb30ebbd4b102af85a1f83de46ab03f938026fab5f0c6dfd92bd75b846b5a9eecbedce169febf244480025bff1b316ae517ad698bd05d48c36607f33db74edb1f30c2547a6faa417e3766f0ecff862a77abf1801be033fd69173c7c5a28f62cee1cbbb3b361fafdcd7482780c65f56ce65651ee7380757a7b07cf5114a37c7331f7ef32a368635c277a406569cd4a4fd6070529c23095996ee372727547e593357183dd4c561ffac360bebf5cb0cd43f9a81447f8e060d38c81290aff876e8272bbb12bd7bcbc714f9303dd4b36f417209eb56860f014376261bcbfe21e1cfc53a407293cdbf4a8c6881981541ad9abbda4aac63c106110d67c249d9bb09832910b331c7af47cd5f6d2a9205a928d45f7bb961af8908d58b7d5f197a315cad6f6063d39c7b61d9c85a689e91d403c8c633e2f468680d65423d01f9ff8570d37dc286cecf9fcbeaf6ec08853bbd842a30adf1a71290090224c90c47157ff03946019814ee532f912524eda8c313f5fd9ff431ce68d6035cbb01e780e91d685265b43c3025fe49ffe4fae5e2f55a9fe79fddbd2ae31402df23b6d27a126fb60b0df2a79dada71d3a8fa98f4ad41f1b0d3a78964f71f8030b574abc58d5f65ae05aeb504e16ab7df95a022cc10b3bd71a1b5e3b61025a02d74b7d073df015b0e9a025d98502686c6bf5e2b950fc201e1521ce1222875c0e7e48b2a6bd7fb01d60a7d49b09707b486bb4e0eef2647b93c2e52609aa75af097cf0fc4c4eede4f7cd02902dbfb2466748dedc5377d8c2075017d565b1c0b90becee7ff74679ab2f3946348befefaf12122a52bd9de8cc4fdc46dcdab750a600de07f4d039d4db874a8ffcc993ad096507cb2f54977bb762a3ab6a3f8cd3f304f4869c7f8c7bb44b496c5787e72fd39a03989b19207ba4640bfba944b7380d0245af9769f503f2b649ca9ef17b1826537ce9bc7a0de9cda36a7ff56304d8a101417b437bc853eb22a0b2d88a76171067fa4d10a9d0bd57f41e1daeb015fe6b05dc2d10c96a557390ed9e27365e328ef1ad7cc549a46756c6b33543e9ee0c0a018397032436397327657ce97f94551366a57f15e06b166c3c8f37fb15773b6e02808895521e257de157efc56a2f5876a42da713c7afa57ecf558411e92646b205ab0699a656712c6ee308b4327a5ff0f725aae2d5ecc191548f2c6d064196430a88b49c585dac562c4dfc6e961912e693d507c0e95559494ca87076e5134c0c03d7347511e95bad9dffef5ba38bbdbccb50ddadaf2763ba825acf97cf93c3920fc7679aec291407c29b46ca702d44801b470b6a7776698d42ceb7ff47a76c460f66a948d4f16937ef8bb1035e1a04adc8c3414fca0e5fe4ce2d3076aaf64f920a417f2a76a2abb2d2cf34d60f73f2c6fc2571371368abfbac0e07b217ed774e038bfed636d8d476396384a4ac9b347224fe473b64b33f03762161b35729c5a10884ab0be6f486e469857b5db3f3ff364d4b1cd32a23e52c170e41dc254a4ed200610dca4586b7ae3aa4279917b5ddd16d70b9d9a9b4940d9b5853bdfe476f570a7d14676f3f61bc1bab33665e8e79a5704de85c4b3a3fbbcf5f2ae2e312502708de4a533a9450e4bf80f69c0327afb1605b90df2904b6bb9a6213dd53cee0410157908a0298f18c317474588262b692be734cf344ca0e385c7f8109a2a64ae707984c51745f3e0dce27cf5245879ffcb7ce66092a22ade835dbece1615ad6e0032299642bf12ecb952b5864e0bf0e682959560a8a63e93f69b29c2b62cbede401dc978184264a553e31218d5f07e6591749a26ef22facce2857147bf66c048e0cfd3fe6d63605843a080a5455e4bd3bc1d71933807f62ec7d330c815cf779ae0006bb35b583cff4ccb07c8f66e8ac64759da6bfa299860c873e8ce6efe1a856084ae5da04a8e16f2716db99b445aa5a3ac1b303159b267f733d9f9bcff48f5605e687fc6e23afdc1a26d0ef7b42fa28fd4bbb9ebaa73a1605c916812c1fe62f0a4c685bee272ab956ad7966024cb378c911af5b205705bf0b63cff23076b6de0b1800cfe6764e71ae3f3bb9addfaef2f4b1fded52f93c0a6ed8de4c24f531d1094c80dd5094596b22b4883e900526855bf80fdce79bd54e590b1d3335a1c81703323e61956f5f793d06b0251cfe7b0fd393f1b413a534c3afa8c8e2e78587c90711cfd73c5ff4b989ec5a8f0a43fc8f53be84765d58971fce450dc4a5c5651e0e75fd93d8cf35de0f89ab9c589c0baa745815fedb415d6a9819457706fe51ba02b38616e35f1fad34a9771f1c609c085fb104b09d1b97acde1cf408c286ccde05a4f808084d47a43bbc645553ad236c3a6dda852322294512c7eff65ac2b9f108d8bafeafbaf8ced71c0f74428fea238e5e9b496d575f75667642ac8c544d520285e0d5d61a65d3b40cf45d69a7baab07c49d212da30c7cca758ecaf69b27ee0d4b5c63f1cbccc195c54267955c9ef70bb000388b7b8f1950d7cae932601a140f4ed41a70ae329b64d2ff34e0fc2ceb128e2919276adf1800d39705a53205d80db4c246ce5a404d89dc88936cb9ee73b85c58f4742197e106a0f4d55e1bf4820f423de9f0d2444dfef3489d3b61ac20e71424e7af7fcaf8bd6990d7cacd293703c9b417e8818a300203e68c2a8ed5e36bf99607e21e9d982382ef7cebccb526044903ab726dd104ded95f952f9f5f0f2a1abb1edc3581b762e42d88d9afe213099c3ac29b2558f6f7b05db8a0920c5f8a6319a0164808cccc49bae5667e4458007f8c8344d4029d87c64d9467dde454011b48a6e9f16fcffdd13dd9fc1130fb01d99c6f5d77d8d54cbbe21a2d0f3ce4d3c56e23a4485e403ec53f9fc40af24d02684393ab7bf4779f2da603a29f30469e5ffaaba324507f6fc19e6c3731da98041f811f4012b46f927af69fdbc4636e6b7e55be32b85cce0329b4769c7ea18907515c6cd73eb64e58a2bff9b0a1da0d24134b811f45806717ccb19a8b0569f20319c48fd2391777962b11376753afa83b2f4bbe6df45c159c8413a40487c43e035721d28d6096ffd65796a5d568da030492dbb7dcd77ff43763d0e58cfdc02107128dc1e3f5f4f5ea580c9dfc2be0078cb4b3db6df64d9ff2cb281a2fb92c280e2aeb0e00b831afd5b32d5a7abb01f4fc0c22832a60c3f7efdaf9c4110330a10738f2bd19299b72f7380e9a5832f80c44560309611aedeb1c80e695cb92f84b0b86249ace3f3c5aa3156484735ffa62b8007d8efce69c0346c1f4baed57391e0a995e31d4c17ab275ed1dfc282423edb7baf86da1ce6a195b1a74e37f91a3b20139dfefda7bd6f66337e1a23250891583b8bee0f2fdaac30cf8eb2b2809ce7400f0e87517c1fdfbf82db072f44628e59e6dc5ddf3f313b5de6c522fe313e18c0f05aa98d2ca4c7d0e5a122e75c0e6a4300fe01bf471a969f26d9d3c407a3bc001f13cd69e23440797f8828bbc56886f7dc26fe25159f27c3858c44acd5106100e341d2074aaaac4c3a02072c27dad983c00b17308db8856b80b6b87282d510007b08120f851c81e7a56ef6c2688640615ffe7c550c5758ae6d6fbfa1178785208d5e5bf4520b1c3c2916d187a440a7abac2fae6947901243a7dfede4db2829d04c05a6bbc3e7ad6ea80e8ff2f50929219a0c2f6c40490e5b3de1a972265df960195de4754ca1a3c3238261c3563ca4f06b67eba80e3df1334296b8f5858da430ed681c4c0a85dee8cd69fd69a6cae360aba6646725b91d1faa7b04a4304a39a0eb755e253b5b0c05b8d0f6f8add5806a09b33619f235e9b2155566699b33054075772acae1b842b4d4943b9e16acde9d8b3ea0d1b57fb68e794a7aa6255b11a06ddcfadb71377c1cc55a5336168f26e220f33ebe93f93ee0cdb6a14123c12c109a86e10b595ecb2eac50b7b692f0fea8502d3d1f563e36038e791dc7374b15f0fe9f471790f45f2e9f19035accfb6104d5d1010c827c092674a2337e7de02b907b1d06ec4cf6b41fd3e200a317f1073d729db9c18413546a901addcf3b2b39e03e95d8332f8294a792a8ca76ad39251325e68aea44634a41ece83b07ca772620ed16b03216dab6994a7063ba0f879680962fd516d448a60be288ef6bdf7517706a79f4036af4d99d68f9c1566bcc061249322d95792750975a28ed1ba799f310e3ea21b4b15eb875c807f3abcae4f42a3fb33f025390c48de48b9544fc48de60b858933f671875c0d89036bdfb3b87eeed4eab85a66610b2287aad9be268cbd0079c101561399f1d63ae2ca69adb31b0a9dba4e4bd2037289ede6f67cf0ac8e0d56a48e3f0cac39b3688915597bbf3af3e597121390de138ce833c0b3dc116a0f4c4ba25ec1dd4c999aba62f0e0e3d838bb6df9542ac51539859069b6b4773b0e169f6602045d559b13c8a37898ef34988aef2bc5b276dde96232de24c036e6023e85121dcca26a39ec2e8b0ddff7f112c97ce8f81851cb1c74e8b19db3cc2b0dc2a1bf736f8a7e80a1220944dd5e0334b2c0b55bb459225c9b6c22a72dae80047e631b5abd380fbcb92d181c6721165e28997ef86179992288abc6ea913fbe0549a38b118dbe68353ccb59abfa40be39ef0f96c4f645a101cca89e5cf79cef0295eccc7062f1b6cd001e3a2f25a747d4622d54e8d175d2db9d4850f51deeae015e3db4ae09f845a6e7fb04068ef0248c45f3455c3e4e31189a5f24f59ecb5f04af5fa10a61b00497c5d5bd8370e549b2901b4ead02ae0848d004cce6567be60f9700ecd8245f54f6a78e4b5e0f9568badbe1ad1ba0c4b0d1b8d8a9b8c0db4c0c050a617f430ef46959edea6ea668ccd7b6fbfe5a692102884ba908cf6587a90de9e0e4cd77ea2bf6c210164103c1ff225489e8e8886b66393dfc8399f01b6603b80cebe8b37c90a410194174638cc99410730b6169b58d9c563f89ed93a55c0cc0c8039811fb9856de28f52b6257eabb3ef181c745f20d745037bf8faa0f7805206b9643f09baba14b37b01b67df3565c29e1562d6a3177e7f02c6629355810eaeac8a96951377f31944c5db9ae014f7df8e1b4d64551eeb5a91d0bb0ab1f5090a02fcc5f37ffc626f1eaf21fd9d4ff75d662fb356e3314634e7a1cb37ee730ad0153556d38625dac5683eeab7d50109d1256591230cf3c338933c30671018070d921c623c10d3a187164214dceadd48575af0aa8438c63f670433bc8642620798313cddee7a3d5f428bfa4b96af5c8eb397dee4e52bd2f86d2dd9a1c6e28e0f770715513cf6aba3e22d72e8238a33ae72d844aac1910b6131d6d874084d260bbf2f7f1ec908fea142ae5ad0f77721fab1eb60d7b9d729f4c0d1a9f8c6a3180d74e3b256c75dbd32f609ac2af42088d65030a5836b8837d05ea66fe486e53007d2632ad3bf679b078f8590e6a4f5aafd283275ca8d36bc6a4660602c804e3500d2fad6fffcad44ced4580107a97373554058d77f823624ad1035d3df8bea6001cbc4aa6f6fd08a4642e9dc16ddb2c1eaaeb3c9ea998de575c8d74d12fc39da0c39ee384edad4d4f5bac3d7e5e7342fd95724100b4e00c7edbd237b8939a77201e1f28941666c98ed60d8b43c3b60316d792ec1093614c94b899b7f92b3056f0522390b91c9ea32b87336e6d3ec0323816bc43753162d1b5ddff3e16b0879a50256fbba2ccd326806a6f7384fe9a85aa7e019ea89d34830345888cbc113edfa0d9ecf0d819d5c37960f5d074648bdf5cd57bcb378676a21ec274b6e55e70ba700b385b1c47d5a604d8cab0ef7fb09a4dafde1eff728dda312f8979209d454d60fab32ed4fd52fedc7512251f1b813f6bf1cf428da258dc75145a58aa97c45c40726a410af5603597851ed5b87e52dbdc929d667ea5a3355f95c1085f30a2fb90d56dbbc32e95b2827ebe73b1b027cee02a4b0bf7e6d7187e71d81a9f84de61c0e62149db13b47572a83772e7e7c2ad96640d720de2f6e7cfab63fd2aa5d608900e24458451fcc01364acc60f1ac6fb0a02993b9bb735b985218277a9b839eda04be4e0e8d676c5867a3f7d12f6728e5522f5349d940bac2d4e5fbc1e34ee09f013e2c9567083582d69fc5eb76967b5a28e15a6f61bc131c7ce694bd320c883c014afcc7de956d74a54ff327dc2d8a5f45d26cbd34232053db7d0ea4424f41450a9859c4cd03cc5877640049bb8d575df0f98a9f54ac6016d852bebc4ee194cf0ce4cfb17a27ba397988d5ffb9171ee0b3c0b151247e9ca496268c3a7eb4014f0d293c9e393748d4143d1b1acb9ba1c32bfd02d76a56118b2439c7882038890107ae432145de6de91486d0b6af174ea37aefad67a4c8a93fcc5534c0ae82dbe20fc290a14dc44b98e182bf69bb510459c4e31827ce3233baccdea5d31dea59b301b294791a9ff2cdd239f77d8e3db599e7bf1fa532bc1457ae2ea6e60e05e84f01252788e2fd868ca507534d4a4d5e9abf4c15eea13b2ff2011d0a0fa0dcc16c0fe465ebbe5f10da4fd71059ab05ba3f189c7e54ec9494a9a859ce33497d68d70dd6bf25ed6ad5794b524bdb27a290c0c031af40a2c39b0c0b5a084045f0f5d90dd7785f0d749b13a311529a6cf0d68546fc242f12bc417f2a7c31a5c88fc7c10d7357afdd17ec59133a10686cfcbc64fe37f2eb2ee838639e5bae0c5166bbb20552fd7821d013c477166b42208baa85d1a327cf5996baeaf3600a077fd06b740955e6bc82caac81baf606035b004a3380c50e783d2ef289cec0ec9c2d0c3c230796aa0dbfd302da67013c53a263c704d213d6ecda3d240571a8983903d4bbe004a2c4a144df0d026d791c2c34b838edb00dfc465cd752be7bb2b99178863e310f3387d40da0169f1c6ef3ce8a1c8b6199d3fee116d227f6e2b81b2fcb38718e0255a49127474ea56a03ce3d9b0ce2f62c9bb4854158f0245150d8e7dfe03050033b376779e2b99862d8b668711559473a877ac647f975b5820781a74a8edbee05b97c86704fc198fd19e1107218e36f91b1989fc331005848e046429b55e0060af3ce6e475fea44f5cba9c59eb5ecf6ab027401f694aad715757da03cb28aee0bb279c51c2e8392d1c5ee52b92e99aa84aaf25a5150412dc92499ac1ec547070964d17ceadc3465ff71cdb8474330ecb8760d538b8bdb6073f656ee535153ee0605fa13771db5cdc5c50c2375e97c0877469f027bdaed7f8c3c89752e6a338d03f5a5f68287557c2b84cb9f5cfe6b8506be068b04ec5b14371f6a7ebe51b3f10e477516febb170113f787ceb2574aa742200b83ca6e7130de4ce372a64e311a0631ab14e2ecd2d4d5d03deb69beafa49f3b9f41ebaa25d65d10aa5e3e410a1e0753bb1797d41609eb5f236d54b7a334725f35b8df574188d7cd4e6d777cbc8b09ec2d3590389c70c1a7061e0c506e421bd1bb3e119c85d8add68010ff379ea30e9f1a59aad267e831b425699ad51d027ce8548f02bf2332d2094e562f10c08b0bfa572dfd74cf6c7a5d2bfb4a42e872ce3ce31341a0d4c26b6340a639eafbb9076fad22e8f41abc171c9053e9443c3aa870cab59353a847f4363ee374a440a702b1e146ed504f3e54ec1c2205315357082f03c3190ef8f67469a5ca0a3d36a903d51a1117a5dc3e9face077a41fb2986ad93f1f994648e973387c760712502f0da026ab93c8b086c140e016383f140a7ec40789c8cd7cc27ad7295941d4d54e07997bd4ea06c51bb962f8fe9c01ea3aedb898d882f1614abe39bfdeadc30a930a79329b53762926e2728b3b8db999723566b40420d3312ead97bb5b431848710e8f207522723bb0af84b4027f7bee49f5f0622582dae456813bbc8602a88b370d284de0600b279a1789626f5352794a1dc0402cbdf7e8a4ca5627851e317686094056d2c7a45e0334f8405b9f3b3c9651a0447df34f5a24b3c626a617a1cd4d0fc15e25f4f43171d83783ae4a56d06bc9397ae0ed3c9169c87597b856c9fd6106020dc3cb57ee5e7199a3a88848c64da51c3a10caa6c682049fe90ca75497b90a03fbc24d72e93b1fdca611f2bb12b637b11152f6d9f56f9a7d861364e52cb20ecf723e718ef6ee11166668b4ab5c2fab28543f387fb5eb6702a22ff236fb5605b6991bc1bc1f487d765b3427a606d16a77263b549443ebff912fe706b5280f01222a3c29f7a7bbe0bd6020a0511d08c2e8af8012479d98a1e521a608a571a50d12745aefbf1d77a00cd2a3828a8d425df1642b3391f5075aead6506d649af8074ef86fbfbf4e2f9047193a080a5e989378e6a958d780279b71f45051d7cfb203b6f77cf3303a33cf385f3e68f0674e27f2d363a684287e4a5284c9fc015be80ee7f18847b54e733f96a4c38c44111646a7b06acd883edf63bff4166b663fe407b40d0af4a570f201154b95b2c81552a89ec24781c0855146806a169f6171e8067c0d17bf74133d63b668d40e76e579593550c172a93533f38be1d663cc205c08561bc02fbdab0b22d9c9536e3331b7dfbe240a85bfa01468e0292aa65dee4006598ad01fe57d85028b531716d0c772da8d026780e646dedd61a6df9dd1b9da06e7a966af79f85a4553bda99ee079270445ef709081363c142161d56cec2b820ef93d9fbb628ebdcec77ab9f5b582521c69fd99fa86bacaaf56e337bd133c63039df5d0bc83a2b27dfb590a9d18254bae180bc8e6cc02f192f48531d33f32f70dde1cb9b2d8fc3b43fe39904265d629ceb6c431919c85dfaa74c6f79c312a630c500bb160d706f44fe9ac4b7e958160a195f6ff4b1b6ee7f0a199868dac731405b3bc8b39f60a1f04f7b6c24f18268da92fd65a21bc8d15f8dd5375bf6ec5bd0fddf27d0593cd00d05a225f4ab978cf0566e45235fe196e2058d951bd5a68e60e2625d670308f160cd3d4967d45b7af1e94e851f35fb39dcd7a71fe82731bbb0b4c88429c8f7c98254373296b8ab6717575f3f70c8943e6d79f9a8878f67c33012e49c8e9ba878ad68ba071331bee4af6183072b5444a0217a2eb028e367bbb0b17a9adad0df0c1462da3f6dca93f24f3a68debed415d8cfb59f1390ce7bd9d00a29d6db91ea1f897f6bfa69ac1d29678ec194f502bd4d4b1c9cdfef8008e1807a4c16c4384b19a8d892fa7c321eb259d2f9adc8d34d75bb079b8cf5883286a0c1573e5fbb3e2e766d9b489f18c446e670f4e06790a1ffeb94cc477020149e40bff576228daa1af0a41d8d9cc43cf5899e062c2396bb97f51e0d12673e809340e9e72b8af9a354235f8de6a05a076bb6d9962075392e2bd8336c4e5b789aebb07a6a3d02c186ec1eac085bcde30dacdc7389e30110415887fd7c9b938c585d90721e73d97704c36c0029a52a8f919079101061bee15fabb7a42a35ecd8b7f640874e7cef7820d07904d86ec086ca7dc3f0520e13d29d1de1e9bee3fd87ad9d80be0c6932dfc3877640b8fe6c005679cf37d221701704446a9e62d096220149f0591c11fb1d55116a946e65cf5e88fa7c46bc250077c8e0c9d46394e43a4796905c3bb5efb3279a3c6ee8aab10ab7ac9516517be2139967aed000795841b590d08090f82082aa1419a880f47491299c7d97e15f8b12038be826635e4cee59c2a000ce7f7c74d19c5f28d73e9077f0ee7af6785d8e9744445afff9fcfffaadd6e050ebf9b6572ced506980feb43510fd12000be6120f43662f262f8ba792808b5011ddeb63af0162883ccb23fd971fffa9fc65e95d3a6b31bf85985b9f18ef91d046bd16034a91b93d2a35eabeb0bd809db70c500b595cf03792a285ab445e9180f70f421b941313484bb35bc74e934332743d003ab344914056f6b6a4fd5cd1b0392af0b5e5b0bd32dda00e90a2b057a58fc09e1a41abfb859d6f6a13644e8950e31924ae9a93ded6b431f49f198d28ffd7b3bbdbcce2ef6989fe86a38c5a6cf05d57ffce1b09e27fc4da0a8483245a9473609b81b457abf1a52f37c46dde5dd0dc286e4caeaec268b328bc05c695dfaa863b65d77d9d07970547a8ab46a797403eafcedf26a4638012315c3606781b968613c5d8c2ad9b9212c6956f94805c106b072b383df0d3a4d2e3b3a6d92f27019e733cf638b907adefa7b19d3a491a80140a548fd7a670233f2487e9a4fefb0a6de0df3c7da075fd9981eea124c150a02571be33ba7609ddf64a65c78d0ea314ceba3ec3af0791dc02a22d3e6436bb905f2f6cf49cc595eddb98c55e02f655f904d2fe2d08387996b00d5616fa86b3b0fb9d0fc55384e9ec912bff0d1c997c4d3854ded3cd5198bb9b1b5de689e162105cd16b8e24ebdbfed9412f9d02dc5109951e76fd9e2131c23fcc47f1b6051dd03fdf4022c45d83f8ff4ca426c67eb04b105b852d0edd7abaf6dd53987e9974e08b5e14cdf0a202f8c0a105ae7a487aee4c0da7384d06a10743d6665f77a089b04 false +check_ring_signature ed6f933fab0bc640f10e3aa74c68fd57280cd2b782cef00c105803242eb35081 1d5f2fe3a10110c9e7b1ac37e3ba973ddab5a277e7cfe59ea6f4b48ec7bf522b 13 aab83ff17bd961445e75f7e6d91be5435b80793467852610b291be16d14b8167 c0e03246b2fe63386b1c10a1130d732bab4900dfa6f52571972bfce85d3ebdab 7d6d5f1756578ba6aa10d20e21aa43eaad2fb06529132c3b4f9f46b73cd19173 e830eef4a2930d1c08789ca69d71d8620ba9355a2640a8f8b869acec7efbee6e 729db2b1006ad343815b67140f1c3747eeead02c707ea786122957e969c5ad43 41c920833e3e5d2b480c271228ed211fccb50e13fb092f31bd01287c456f439c ac3d5f98acf3f23d1ab6cd9b2eb1e63506211b3d5151ddd2d2b70f02210f68be 6f666309b1393d4148497fafb522869d16217120b693cdb6dbf0063d6f486e56 6ea2196324a076c3f29f4f11f650d01657a1baa75a2f51d074b6a87520aaf805 7ccb33138a48cc7e046d2a7a761b9785ebdd9d834fdfc6af80d9b5f512942e13 76338e51bb409a884010e5a4e2518c335e2bcff5cb34b638eae31a936d99eed7 fb4a7cf7da17ab79975ace31fcfe729cc36e08393f7797b9d2dcca2b6168187a 9808d765e74045912c66dd837bb4ada6f2781268344e85c1facd80cf499dcd1c 6ce2f963676469677f4f3f01efa5b7a50b8fc83e07c94d80502a5c8cff92ce0cb109d5836d339d8522b792500b0ac33935a9480ac61c8cb1dc006e5a48f63c0978b74e71b624a05d2e7e272705d19f109517601becdeebdaad6f91f90b255b0ead78fdbe0e368ce142e7d15f020bb3283b0226cc1a9a5a12a1e32c0be48e0e0b326b97d33cec75d73eb59d4317866b4971bb908da4a3d20fe3d7be2fac3246061a88c9d5557b0abfec16ff3ab698d178b15a69aff027a41fd606629d94d1bf0ac979994d6c789c517bf8fedc413b5f5510cc87e2b610f86e9db728a5fb394909ebee2befe98f80889c8f211080a57c52c0d9f04d55e8c614257d56de19fde2035736ba4f6fe78606f05bf8268f86d52ab350288dd5e21d64a0a57be82031170830c1dfdfe1f17fd3439c0ba82351894bcaea204d4f60712d6e52b52f729890022b544d95d3ca22d86a5e887e8240171fd40e17ba0fd1b037b60ffff08bd5d4007f65b3ab0d8bb9b7772ff6edcaee48d616acfbd29878b2758bdfa23b888f32095dff58992c2cfac1750731694926a65c454efb453dc0e09d63a3a007df342207c565bad533482aeb85a510b0c3102c120a42a980fee15171948b8aba4bdfc6015ae5f29904e355cb550c4ec9737b26fcb0f8086e02476ffa8a9028210290260dfbeaf57969910c86037e310d6779d3184d5fba55742305b8fcd037613bcc820dce9c953c438c9db6f237b2acc3ddb7da1f36b58ea64cafcf0bd5a9de2deb630dc672ee65ceaa322afae8401fbfecf86a1da0ee2558aa193abf67b2a3017306027b83d2b8882616c33377e34317e2fa3a10b1a86f950ec3e9f674c8105722b30b812b68cc8af15f59e0b6db8af814a846e0d33f968c68020d9d093b6f8868170232dfd3f236ceaed8bd84416c1df434ae6f39c1d7dca89704f0905c40dc4cef0f6875247ccdf14af914783a1e1b69645856f202ab3b8181815a2e702331ef730ba92dda90065287c7534f2b4d1e763cb3b4f8ee1a9d4e9f88cea81d030622e107a2de7fd6e34a073c6963745d1c0307ac97fb93d83fec7bb544a932cdfa15fb0b559295b4cae28b6c63d82a5632eb0462ea8c289ea0b5427a2b357a0a15240f06b058f1c45ca465d1d67286bcb6525bc4edbe8680d513cb11d8ee9612b646e104 false +check_ring_signature 5d9b0ea35de6485fa770bdd123266045fdcb5c84f2d120d016c86fc9b2810aeb 79d8428f87f230d29f0e3f183a04f27ad57cb2e4b0eeef58db23b87bc4a77346 3 44ca9180b9727a50ab6db42cda8570d7631f6402642bee0fce6c8fd9ef1f97ed 4114ce2cc33d0111472e3c9573f0afa137806d1cc2a50d3fd65a7e6ab6baaebc d2e9764a02e5e3eac7af4491337166890fc3032f6e20ebd8384fb2b4717ba7ef af9c96b980f0236605dea124256d153633888c0141575aaee5ef074e4faed90476cc8239287c8f0ee1c456cf87881658f25719d79772347b9045cc89234f270df56cac491896a44d7b3912fd57385ad8dbb093dcde9e619e210cf4dd6a939a0b3eb1cdd5b77ae85da37ddeca4a43340a64c802b5ce173e486aa40e526aa116dfdfed96c3c8238700a585fba8ce653ce2ff3c9c50cceba00e1714364818c6b3060aaf6d0f220954c574044cc5b86298bd9a9880843eddcc5ad15117568b75c505 false +check_ring_signature 4202ae07c076d91f147572ecb5b83c42804a3f893f11e2d1e5d34dd00f302044 c8184137e587289c2c28bc1051a0f02c323a91d4d529b0aeedd2aa827fbce2a9 9 74fbd2eeb88b8ebefa375b8dd55de000e6c810d7ac1a1eff76c12ceb62ebb819 45e936f742bfd74c3cf7a8897f9a6adfaefba6e2d9820fd55ab77edfcb07adc3 433cf749383253c3bd6688bee15c66b53377374aa70af5a6dce793b4bd395554 d71c029b3a80fa28464e24ea3635b95814f74d2dd1518e6c754d6c3a06bc993f cb5ff1c6f688289c6391adfbe2f97b75f297f07ab55f52685718c509b914559e 30b6ced781643026883e610e6a48ad62336d9f7acc788fa3657b6b659fecc238 926ef2f5b51b384a4ea2f3ff9424ca2d0c226402608d98b6187ed805421b7684 a69dbf2fedaef63c62b69407a866f7fc336accac0f3e480508d7112122a699d2 b2fe1a98a1025bc8a5b8bd32f25cd660ce3775fde76caf5e1680d8ea6f10610d 379bd758cd0c70b187906ebdb4e30f3d510c26974a753b14a59f6cf5d6d47106cefef85f9ae9b41e5d10e066e24db1f06f3dfbfe0d7be56860849661b90ff3061ce68c8da1d00d63765e6405af1c6d17696a12dc63bfb43e95885eeef2c4420f9f0022e13810d371e7f8b6d40ccb355a176343d8c33db344dfbc24ad4414d10a6eb53d2b70575768bcbeba07f92e6a16a91d06a8ca5b361115fcaab1efc128037f73a133c7b1bef584f4b4adffe8bd538ce82a5b7d8071be6b20970ab6ac4d05eb4fa3b1590016bdf446acd21486822ae9f20504382420afaa67f6b6855d850bfb2c31b99998f481eff367d6be317cfc7ae2d095e8ecf55bc698810d346c080db97b6a2c315a62bcdccb7b0068ef98649b6e2caa4977459918692380e6638604096a75546eb36078b182dbd0523caae6d1228cdbe52bcd7ae0078acd46f52308f597a3098c3063a3f05f85c9a98047c19eac1f50ac0f41bd03ee7581201e4e0e3b6bbd0137e29754a70176c0ccb7a0bca86281b4b356cdcb67b3ca8c11afa40a84e47cc479804480dd114da9bd389c131522f3b9426e5b6a9b1b609c0cefd103283e37d2d9cf828e3dc1227ae0c159cc72c979fad46b3feaa84cd757a897ed078d30fc99e42d314ed0dd3931147224919717a1fbd86ffc87824cdc0b9b4fb901a73f07f83493ac019e5bad1a3bd086a58f8b0d7a701bd55544cd9a0fcd37860adf47187a8718b899f66b577bd7e06a3210512a5b348072c822104ffd5837ed02963bd260c39fc6fd1040fc5b227e2c2dce705018fcdc58dd6b09b144928fcc08 false +check_ring_signature 55167244fa0215631779f1c555cbc61c8755c75a55372ab20ee79c1aa9203b18 6e85c6ebaa68db478f57259af88f5a5a47c2a84c4e8fa72a0f13ae5319720e8b 12 37e2b6dbc52251276f373785e4e7e28c4da89c25a072a809bd53104b4bfa1573 4b42803fd5c8a607880fcde1554c3082bc5a0c586bdc695b279d473a1b50981d 23985a752ee36a532bf85a7c89fca6728d0ed925c2814b646e920932dc01a925 5b25a5db8e83f489dad55fc157ce4ff9ad6bc0f9fbab18c843bee0a13f56c7ad 23a57f2e40e0f1709b777c964b33bbce5d45ad36c14e1b389204c5da40e06311 fa5a7235c0ee17f3f2502ac40fa2efa4ccc48c59da53ac262e52b98f4bffb0a3 ece2357117806355359de38c579b8beaaa3f28e356550f3ae97f2f6f41477df4 8dd6017257249001d15c706c247aba89acbf9c738e9e4823e58078b570105ca7 6feb5f462257acb0fe643b8d6d0c48752bbf00b3325f6496a5ddde159734d444 ce18a578829e6ed0c6ce102176e08c5d4341b911963ff66378ba016ff384b782 fccb451cfab5e8076185958a4916149af0ff23b008f214243438b192edc826bb 03f0871ee6d92e6265e697314442ee74440cf5dc86fc002281d74480351525b8 1691b57797c4cd655a67fcce924b789e6bbde606b5fbcbbbb1ad3b0dd2b8060c638301a20e60d03038fc4a37322c5e397ba961d97aaa91b402c028975dbd200a19fe927e95e19495c7da49d8994de114149222f8f847d5166178ab5d2e9c1b0729b1d33a8eea74878fc132a0ce909abb86becc7684f1a4ae5decb77758b9bf096dbc92be8388f999b9dafe25d9a74c7e5c8719e5f83621670e1112867d1d09058631e92386868da1fc5f94cd0ca8e0a6d02dccafbc79bd959172befcc041940485aa7a9c43006ef544f2a746c154815f9a3b9dc837f3ce6a514148e0e1b07e0ddf0ea963cc5b53f125c180ec31d3b8740e0f61caf96fa9c77ebc2bd2403d9e0f2b941c963c8bd84174f1bdd33d6786b4f07cccd3d859caa38fab250ea5759c0af0b0c27a594eff8d6d28f0eeb1f5d409477646d514b45135c657409c7e80070c956cce53922e9b0300f2ab3dc99652a9208653498c37dc9f87b9f0ea7086800459297c6c4f2329ecce2b2f30f89ee585f428d83c60cdf05258845866e9fb2300d5dcf79654db3dff38d0db0ba03ace14d1c53912003fc576ee918c3262edb50ff496c77da882521e369d7718f44c61ffd515fb187ff681b877344ae780dce205420fb019f4f62cc22a785be38d94565be41977876b8d0eb81bf86c769f647a0024c5c7ba11345a71a927bc9c3e994e9e6187a655a297fd14262761f6a7414d07476708209fbae97a1f1fc5f223e60a381ea3df02ab8805f2867feffc5348d80691ed18dac84edec38ba3120f09878dc90bf9aadad30ec67548eb872660ac6705f6f7454e6b9e93adfb45fa02083b50959e3094bd0d34e2b5c109f11eb97b940ca00b027863decc6d50d0c45d13aaccbb8f77f5c49edaecfa9155e410bcc504078457d4dd0f6a5320e001b4851e0f774621d0b7ced800901173c8de47b8d78109fc2fda4a9c5e2c7679e84e992838f71f7a80c20978a8b7313e62363c28348a0ce88d1b5df04f81a7cefbc126f0207fe8cf3849ce1ffd87c0529c80b133a85a06b9f1f4ebac075b72d67178929328c1fc121c72a941ae6bef08286fcf8d954a00 true +check_ring_signature 6862a865b3b09bc691157b8d481f2581461131f5115ce4beca6e74aa6ac005d2 e23468e4876578e9125d834006a158abf00323421315f2c237ca4a14324357c0 2 f48a987fa903b8d54d05c1a70b2af2c11d69865cd865cd95fcf2b80a3b528b37 59ec80bbbea3e1a4bf787609e03d0f01935462a1bacbef2bcbfa581889cc967e 6b32fcc97fa5fdfd90d1d0e3121770cb9abece329d0289af47fd105b72b9a60e20a9d54ce553c814be48a42438bda1e21d6174bc5a1f05aacc87806e73390f08a3b3a71763ceab42b1df6116615e637f592e1f2d47a7bcb36ef12aaa53f11606a2566d8aadd1f26148d18372d796712fba6352daf5a2b8828e05aead0c70bfda false +check_ring_signature bb8b2052775ced23a363a0d6129d6ef812e2b4ee6fa083571242e5bc03357050 7672d753bfd237f2fccf499a3cdfda29cdcc5f11bb2c030fc0e24d156621eec9 101 f279920867f85a818cbe2c19c6838c7d611be92cb5b59cc8d964b37f641a7837 022607a73825367249f4e20e8da5b9e54f7a76efbd1b3a0f97035084f083c14f 87b6a6a3179027f68318fbc98cd0fb9300fd7720baa571182b81a2ce20f10268 fba4d8a3e606e7d8e4ae6ad0c7dbadee0649e6560723989e3dd31304580b1f5c e1a3f0dae26d23485e3d54c10e4add63a451255c7de2f82e97a50285cd77d116 184b58b3298f0bb6f6efacc90bf6556f3f5b4a17308948bdf83a76bc3473d18e 4fca1c6b474b3c59e8c55d4b4f7477242f701b8d86851e0240e78342f1686149 f341889d0cbc73845fad82d8718500c8f498b72f9d949bc9d507b6a20fc39e73 d625633d22d0d395a0a6d688dc9be504c98025291864ff294c3b314e320c3a50 d3f81f1fe6c1b615381cc301834155ec8f4e961e2a1111f23b45d07fdaf6c66b 574c1444eb4c928c0ba2676b9ad48f5ff95a7e24622d44394f96016f534c1ab5 ae457f2bb1fc92a68b90c0ad9d65b1da3fd71b4ef52e6b98961ee85c070cbaa9 958eeea9bef275f01de28484d61aee014bd2fc7f397c480393daf4b536492189 8b9957a8dc766bb153220e304722d9347635dc89b955783b124fbe640a06df8f 317903ef9c13d2fd29573ab724d945133a6ebb09d9cd8832f8d1fd519b1179d7 24be36f16e484dfceee4565a872c1f09ecc8dea9a99582d82f3f772b9454a007 e3bc5bdd18d9a9ee51ba31041a24e4ce21a871c5b8d51f33ef3f620e66a8d390 f745d6e0a60f4207422f5b5a59efcf659397b96f2e72c235c076c4548a9d957a eabe802a32082546d8e7470917138f59370d303320d9f8773fd5384952176939 005757d2b833cb96e0410037ddf2739fc4295d908e1a2add493e86877d539ab7 831da4bad90081e3e648c1ef92234ee4257ea32ca8356dcc59192ceb4ec3a91c a20d06b03fcc3490bdcc195f5e208ec4b0ca5a19b054516bc577b7f5b3812caf 93ccb87a1a9916bf9ee893f7867217bb23f312e2669cd2e6c261bcf78a4aa303 4fefaf943e2415d4ff5995142aa2e3c1ace22df202679ca9cb7884e8ccd795cd 663204e6af6f9474808804c8a119e60054f3714cf9c0bfad4d98b553c2ea5946 b60c9e358ec75bff5f205ff11eebf0c1a3f08a7c61092831fcf91a8901fd5f99 03db925aa989247ef319242cfb54e2b1724831a43bd00aed99bdef9b31b7cf3a 235b4fb6158f33003dc99838664be4d3ba73c7b5d2c8cb38dc6b48216f18352e 20df401b317f41ad34239a3265857d847aaf9c351b63212b3a8d23de1027da7c 9ab6ab0f1a3085402fd99c21111a57c75173d1e6abbc9f55c7a63d09f4b4b377 e5b6d185000829a0faa0f57c73a8151c4bb5829bfe22063035f36584bb546927 3dc88869c9c76ee495395f6a909d08b91abffb4aeae13c84c9b31509fca8e42f a4f5a61e0cd59b0838b32515b082b2f373612a5050f424f103b94c69b1b2f867 866f106cd242413d1a9741ca62c5a87589c58fbfb77dc67835cfce68a652151c 2ed2c97847395c5850906fe0f005198339a5ec31ac47393c5c98cdd4c7833e36 6b7b68f9d0228c922b84e39f83dc8d9e7aff74fd7ff98e2c95c856922731218d e4724bf294c3337192e4cf8d2d7fac2484b1d1d7414b018325af764ae55e94ff b322fc4a30365215c6539ec5083e1698f39f438a7040471bcd0f1b92fdb9b640 c5c510fd42dd145736333fc2f463b9decbd55c9c1ce4ca68dda0bb15c78e0e4b ad69d5dc5f05dbec297c98fa3c9b54b7b140e798c21112ec349852ad461bc4a2 84b5bbcf86c07ddc0a45c9b95afd8955490f86f7e6d62d637ea76cebd3d9feb7 fe6f5e2ccf8865c874e9e5511dfe9bf86362ce4e41c1115b67143465c9036164 87c8276845ce91602095fce41a6385db2cbe472fdd626cec0f4a89a44db8acc3 367a48742b9406f4f7026ae4976243ea704ba7fd9306737542346e2ce4b61c1e 4d081cf0f942023111511dd474cc308c4a22ab06538ae149ca35b4c82104decd 31d6e88c689d6ef336eadc8cc5468388df269acec1d597e112bf01a0e3e8c0ed a6dbfeb6f57daa91178e3fc0281689787a14ab69aaadf599a1a3d6f3895eb27e 817610e197c8055ebae4582b803f25a8af3b4a2dcb6a8359f95a056ab4e9853b db760467bb2bab881fee24488a1a19a67264c7c5fd862ad2c54bfdf434bece8d 055cc987f4f54f6f8d700b899a4e98c7c1369a8ef30725871a22d46c6d1190b5 8a8c6d04cf054cac3a07df29030d926c9916f56626811f2bfce88accb2f68b7e 860049f03b2c96e3262735cbf3a8ee566fb2e2454b3b7ad6370af4adcf024e52 b450af4b17b797a0b06fce0f6c921879b0c6ecdd00a56eb78418950879fb1e73 4c75ff960701dcc87b82acca52bd696e9bfb25be033c0d67abd6cc8a16b4fcd6 c499d9605dc89ca8e6766e5c00760c60fd2bc9ede7794349e42064a09f74f2e7 15cd1ba77fc6ae03f54617b09aa4a3848280caa452483c408a6da46be1e140ec e067185af4dfaa20c0feecb43584a6b9c3e74cd4df239ce609d05b10252e6a69 76229482fd128c25e60b3f2bcfa1b7690e47443f649159d7f6ad7014c4023331 21c9e7579872cef65db24eb324b9588833eaa1e0447208593675ccebf14d4b1a 39c9d246d18fdb61284f70634870254f5e170cfdd84ab996c8d81b9faeef5b64 01aa4cf6c936bf25b4373981e1706e12e408d4deaac4cb07b2d64232d8e3c2b9 6838d42d06b1906bda4b045be2a3b82e93804a43146e53bc6fb92dfe3b33a252 310b77628d90531e87d3c6fb58634ba3f2d6f7a4272dca6cabbc8d87c184d6a3 41370d714a29f9e55062122599c0acff69d25ca84d4e86e08babe04721b1043f 690f8103a90f26e706e8c8c4fcf2fb665def351620075b05782aad9d248a984b 821bae45efea630d4963696b0fe7d2d8c1b0cbbac9f18fec7f842cebdfd0eee7 a07ec7ae9d072be98d1b82ed5f4555e575d334d5dd5f7c09b31800d15cda87f4 edb997dd39c4678d19f27bc6f1fede48df6259c8be65bef4e8273c499c25e705 8d228825bdea84af7daa3c62a4d4897e49b3b15c1be86581d69da1c447fbfebf 3da6578e8a4f47915d5290af65b44f880005c1e761ce54d6019eaaa152eb6454 3e8b2b71f5a033cb80cb399a5b77d889a9ee5c8366f40d1ffdd62683436d05e1 c43153acd094ec05b61d32fafd6add8d55e82c0acd94a5d0668a362e321ad5ec 0cb0ac66c96c5f14d4f45ecdf9426b04edc65616a5e5ed614bbaf6fb35f54421 ce47c5efef3d3bf7e32af72ec3c6cc532305f5202181b19f10e4c8faf45e48df 0123ee832960a05d23d54ca7fdb8943ce7ef29d6fe7d0df4a8899f092f78add7 e083d5432c8af263dfb927f07cdd54c42eeca2344045b46c459c85eb02f57608 e8b66cfc0cb451e0c35dc1c067ffd2df1f2aff2f0494dad71b058ceb8d94e468 7dea885c970149e0a0f27765c35927cf294fbd7674474c6fd1ff3bc1d34ae316 e9b2f75e19368402cd918199f1d30a75b9b47c9eb7c5fbccc4593dc178e0ab0a 5de0a3e7eb42df540fb45f1278694ee7ec9805b0094ee2e55a3610a396a9f70a 45992e21e62aea314fdcbf86c50aad053b7f349501611819cd750fa1e766fa56 96ec23b2f68edfd1f78f79cd87700f1f1a96c2ede157d1c41672e2979962add7 077fd074fd16dfc86e8d50eda7d18ed7384fc21280f73618f392a6cfcc712b63 7f36916e17553640cc52f5dafbad40441e2743b9e0951b241c2ce454f9fb3ccb 2cbca9136feae7d4e3b250640bfc74e2d54be1197478a32e697dede3ea3fbb8d b4e04fc53a329a39085526f5ff6d158076d5baac0768092e4f16c6e34996e26f 9155648683916d3338dd11ef2d5c177d6e513d6dd7f0673026b2c404014119b7 a331e2e672d75e99e709da6a2ca5c14004ac762b35a4852d9c2a9f4324b1f7df bd0632cb9047f614b9559f01263d3d727122e6b2884d36119defb656f87fcbb2 2ce57f796953604922f21c5ca413d14fb6052bdacb67f9630ffeb633902e6883 f6462a6cee24500343faf3811b237534789ee7889c3e517a231db98b986cb445 5b68cfc2241b34e1bc7d4d774ebc51191c27942e432d7242188007de01060efe 526e7d3dbb3cf2b4c9b9411efc2e4b49ffa31c813eb9dbe5fc1b4be32ead0f82 b6ef4d36d829290111d70b02877e62fead85f261c26c68dcabc21648eedf9134 932187d661dbd46d7c4cb0ffe4fff142b51cd786b82cf51a0eacd7a461f5de5e cdc5a448f58f281d270c1ce780b7331dfaf455787043df3c22f0b6e62537dea7 64d43dae6d4ade01e68b7ffd98b930c353dddeabe133c4d509209a90ccf0ec04 f250a75d99aa45c114129e8c6405c8e36ff3146ee7bb534ca575c0ab64abbee7 bd4672371bfe9bd1a54dd36da3795127812af66a1c98a26386f1b7bea5e8a614 54055dfdd0dd1dfb57ed62fd3ee9b790fd1a3e871484d2d3cb0ecb10ff3bf3c7 23d9373c70cfbe214b9b88d69c2d80318f2ae91659d8562402bb1e050aff1c8e d6384838eed67bed4f0374e02ff81fd700bf0cf65efae6a01bac1ffed72e400f29240f65f533f117b3692d9273f4ef5fe2f2796431bbcb61c522169f3fb7130800a510e8abbbd44df38c468cba6c42f6c65541987b49f766904aa048c2255905c3b279a536bf63976e9bbda6cf2b26bb7d0109776d81e443392546fa5ed65300cd3eba8f694890832e46abcc95cfec30a5c976e8c71f57756a18bebac820580b469a94616ca6eb5f1fd11f5a6a6c30ecd8fd6eb2d801aebd7735841fdb788a03db51b2d0195658128757c424a0eb6fcb392353f16768a4fb79a2b9303c8d940d6ee46a3965f6da4a99fdb0abf165137fa06c67a11b9999b80fc630045440f206c45d8a160bb01f41ae707ae9cf22e5d4e407f31cfda6d76de084149df361bb08e0364f4f5c76bc7cf909752653a67f1188eae75153e141dd508fe9b39348c10229cd81f498ed13a8fa08f37ea1785416a0b8124e41a19f57bbed6d449e091102e3b9ef329c37e0ec41acc9dda1aa73bec5a7807158972ed18bc67c7b4a639d00217f5ce8f35216d52ef4cba2a9705f9fc929596c1b7651175c9d019c2baa2a0f8a7adb01fa310f168984e52839d84e1d915fa678d681fd55b1b7da3a995694042ea52d83a6f3b893949a86a0fe257544d11e05d958dfc5d3665be3f9a9695f012a7093e30dfc1631b12542652670a1efb6d66b7efb4702764f90c9886ed2e307974d7d17479753bd7e6d51ad73c9503c2eaa089828ccb8dc8d05aa20652a9f06a211a9afe99fa0364df7670b66d74c4ec2c6b97b0530d1c42a9c8805c710030f9a9f635e5d480bd04f7c1dae4ca4efb13a556e98da823215c5b2671bac65d10c22dfd5a5916ba2e0e08af85b0bc27364e6021accf277e7609a9cdb6c0a773d0ed8931101c5786a67daf6f86c06e29ddef4b89d0af742c8ae088e9873f85a0d067416d851cad1761aa4686a489613b195f51e9a00c3b1a43ea39497084dadf9070b5b3997aff8a9f6c22050966dbfd3914550970ba251db6f1341a3a36b0a3903ac30c4836e34e34fd4e0f55d0c7d8a89a7014caa29a589f74d655bb2459e2e0fa42e9029541c31d0810584e0c6fe79ea29e3036bd24a78c547ad6d3c782b420991f3ae3e38251797df77073f8db0214104fbd35b8a395525ee466d0c2289790a559d8862c4994163b498a1fa6886843175cec2e935f63e5e42a896809ae73f0dc3de7effcd45e4bfdb1c302e11b1534327df983fa318704a93af0c9646d33b0232c881d8f76c6d8b8c0ca2c2e0df661b3de80a2a8543ea91d00b146d4ed8620db5761fb71f9aac74d03b110fb4fb68403769f44168a40b7bec72de41abc96d0e3b53e950ffba6b43dd47646705ca5972d29a0259f8a986a367a7456173126c0ba83b097902065b9b6753c44c3b1c09b747d2ae7883e61b8105af414ee14e68029b48e4f55023605634208c4ab4a65823108fd3c41591467731203d8ceb1ebc0d248ff139730d57482e22728f797df10016a491fdbc04707de6624e833c782604dff01929c66e8b30d2642421ff85dca4e88ab34dc70625b57f48357bf9244300e5d1819df36ac3273e001968118b1d29faf72c51470df607e72064130549a70c47c03681bdb3e34e7f6621715eefd52e63ef263b97f8883a4fe42648ed486903e6cce3fe1e40b5513f6adb9b1ddaa71bef0346859397c1d038ed3eee2d4a1606f7edd83a8a0943a58a70527f2a700a70de8404ab599578623a4a844539dd2d06630e57060ba7c7188062a602b7563b48f13429cd06c7fae7b73f09fb4c5250068dacff62af9a3f532149c985d2a35c488ef27ee4f44160d0c747b96b2eb75708544c14e0c6ba6f6b26ae0b4f099d21b6eceb37607d717b1622e8d91b4e0e9c0c0d11fe82554203ff2854d593f50e89555a4fa7ec5ca785fd431f512f58a2b3047a48538a09266a4415dd04f99aef8937a94264468e6801f621e5ca4148747f07145673e804903356ce29da375401e3f5ff91db0848fed3dbe93a51864c430100bf8c18643be38900966b2a84411fb0a69bcb79b1433651e286209ab508cc494530f7e3ae5811a744eeaac67875f0f2b624540a36856f4bb5b0fe9469711d410e88dd6c880159455f12ec0d6220b55cf3b146a4f1288b6eebf5674d2f4df24403d63f3e13497600b8c4966bbde0dbb4378065894f930e7b424c0796f7da570a04f1096deaecb399bef3238528c18ca15283677c73b508f50b6252cce7d91e4b0421a5d71077c056f3ac9f4c215d352be21747709110e0c3a0be231c8c5c5bbe0e2b15342d9c32a15cb6e112da021c8ffd42bc0c540de4471786f52190f9a00506bb2a7370287ae8922ad0f328a51765a7fd42b5a666d378e5d10d4b7a47f6d80a150e721f4e9a855a84d972eb81e83de2cee886ba18074f16b5191fb71eabb90f8d00372b1a5e2cabc362f42efa5df144d89c057aec383335f546f419f5bc07085134092514d574d3fb7aeb44f2a9bca8998ea28168067c97e2525dabca4cff09b6df316b172f2fb831a6b088f19d2d2cd236251684bd86bd84dad23770015705478a844c1fd2cb582eed7e4b3a7ab867046ea209211e52982afe24e60e3fca088c311b1136277fa59b20d3fce5c4f677dfd826681ea71ac835e01d882fcfae007d5449fbe6d3397e8fabc950d97bb1cae506f3d5b63923847f1e99e6e544a00b17ca6086f4597af4aa10120eab2bf1bfc55f115072210b1aafd3fae0b0b1e4059351d29d8a795f71b6b2d60934841835fbf92a1a4025e0657e4d405c8d170300e8c27926e108f464d054dd054ad1150e481c5b3d42c86d58a032c890a88f27070d306152a4ff2e01bfdb4bd84cf152820ae61b67c446456bb912c54336605105abc66e95d82486158c0f02cfbc5ea0240218d5b7c58e15dee1e1e233d47731026f6a31f01f740eadfd366f4eab3c22a3c6734bd9897009b2e6f3feb1258b520794732217b956e0c0d8d06b6785ef80cf3d80cc6253078d5886b5e70ceb5fe302ab2d891cfe5f39f8f70f8b3a5deedcf59a7425ace187e3dbb8ffcd8298166c030434f3e1fa740ce88c34db03a86e512d1e798d866cbfe93bf0fde7d6b1606b0769e29cdda055f54a08bb248dfcb3bea6e7dcdbc936b54bd7f95cc207173e5c040da3b1c13252530589fc9711ac92e321d0b7b63af868ec50c30fcc90280b4300a1b2992012c9bbcd15f05a88e918e7963fe201ad56dd31f35a3fd1889e6c100970fe13e2466ebf8f1965a00dea23532d4a1cd57175c5a906076746558f0c5e04019b045ca58ade8adb48fcee20c1151017eb64a585e9508fb99e735422e5840bad12a93cc28d4311ec9ea70b47e906a27f1862cc6ffdf8d14e5ac7c1f790d406fa618bc3c58dd31540ea91ab23b75b5969e44add22b62c0c5afc96a6fd44e808ef36684a9208f11d78bea2f3460a299b3f6ea13d76ce11b43dee5480c7c9920927b6513085d45d8e82c44cffc8f42f98f66c941ebe742690246d25b8cebe330bc4923303c7cb001025ff08e8d2a80d74bf47e0ed67480c6325f119ce7561d30d1cae2c7d54252cf84602ca950a1e9c493e39338d0a7fef01d245b3ff22285b0a9f3c2eb432ab6ccb059278abad89a2983722e2e34090ddb90d742c504a945405c6209d214dadae8286f296c117ef1e59021a2d688ceead0271411910618ea80637e4f5d1e9787ff5dfc0260c5a5787ace1ecd7b59279ee64e16c9d0ac326dd012320a401bf1f58aaedd98d294578dbfb4e8d7d0d4cedd1fbf8414b79f0855b0dfcb2dad20bcd253779e914c391c9af1b86eef1ac0aa11b8684985f4601bbe604dd920e3f8fe69d96c5c7ae660d1e6adc91d9f1a79f5a638ef5b089212b2f4f09c044a3a5bc1b5e8a47448b8312ccfd187704cfbb00bf322eae63383170902d0c935fbd5f69c6d4f7c8423974f66c5a932deb6e7a10bd5fa891216a510456b5018115761f927bfc8a7e3a0b2a3dea0872c3c0f281df369812d5a6f55326744a006bb2d9c161ed9fda3bc42e69128d92159aa50fc4b261b4aac86ccef3f17442044cbe91e3b5d2c8966e82daa15095e38144e3fbb8106ae8235c69321ddf238f031654cf1e6435f53b66371e507e8136a4bc9d91d8ee882a0c158565702c2ec107cd9ae3c3ff5baca5bc0fd0bfcaa1c4e08d5175bc43e2a77b795fc823f14c750513ef9f7d3b5d6b133b75c7af99860938f0a42e25ef9a74d98a77eb9325ec8307aab6a6a91dba599a8ccc122d89c728cbdbc95800cbf0ac1ccd1eec0f1d46170c9e72c9801c60cc775246efc8786f56bf03a6badd55ba53db4ca5df8bd3f61d0e56db79dcc96364d08292c10ba18351cd9430ae2fbecafee30deebb2353ef3101ebe4c1f0c4cdcc5664aeb8e5fed7a20eb397235630617a44b0975dfcfa860a0599ba4a39830040596e6fb42c169f69f1391d917c474d3c8a8f53e4e844f6920c766acbd4bdb1a07b3e3b744c217f8320c8e57fe3b8c565ce9815a875eb1a960a5a09e55c09a0e2ad2b8a53ef439ff0b760c1831e64cb81fb358c06ebc8cfe201cedfb60129f60efb9bd9aefe6a5cd2148b1eded93eea28f6b2f0cb63011ded0491f0deead3420a6c5954b2dc2492ad43a7f477002d1db38cc31220cce6ce4e0cac14af22350f1b879b668149ab1da3cf015710fb9f4a24cf4bf2810886393d007bc309485b65669be5e77aeb649880d6d4defe77ff3e4059174b37b131f6880d516ccb6c171c01bc23968215753b1ab8eec0f442946e494145bd8c2fddf4030601df78aca48507126cacae36b8121f13c280981ff293388588160f9950022a0ab5a4cf248df19124dd9bd3abd68939e0ff6f8ec438397e0065decdc5bf5f2d06fb782833ef455390e4ce512d0975aa370eee5d5c5b9a623bf311886c0627e90e2ca2e77272aa31824e3308ecd84dbb7050378e0fd19eddcae50978ecf93d3d13aa3e08b7c87b5601a0e2f42c57df3fb2a9073b7fe55fbef334180ec375975d073a0251cb7b2cff4b7704e745c4d82d9e22718ca87511521f71b1904d4746c30ecd791776ee81e404b8231596f576a36267855bd67dc5ff69e6cf5ce3f0743300ce7ea3b6f6a6cb795e5199c29d8eaa3da84c1f521b8441f2b6259d7bd3a8eb08033a66e91b7f56b44728d090602f35567d03680cdea0182af0a935f274ed320039f90a7911d66cd0a2ab520c6cb8c478fc4a29c36875aca143a2c73382f6de008d87b478d1f0339075c80444c89ef838d7460cd69d20c79daf4901cb77bf1804aa52b8abf54cc44f0e385faa515e29fbd9e7ee3c08651b75034ae6c2108eac037429d223897ee20962a28baa9d646c453037f67bc9162f805058953e738396035009459ec25008eb8049c9912a3d70698c30d09364d8ce643df82bb602ee3e0dbfce819750f8b45dd2f0f230c9af72017535a98617f3db0483f01aa66e7ff50d2992f87a66c7cf9f4a875f056ef1f8692a60a8cf13c11011221e350d75f5ef0cf090f3e04e94c1355806a4d236c00da94dcfdf893d7ebbff1873289931f728013bce0b69a903c528faa4277e517d1eb3cc63b35ea39124dbbe1ec16ddc90a20fbd92623d16a079b3d969e215be9050bdb03a110a70ff3d03c0e5b6933a203b042e0b3d264baa5f4588a1538b279d025b388e775533ae4e59f2e3e412475e870e7875f28ee55316a58e594a2a85ab5b7c4f3bb991500e8d9bdb70fd6bcb40f70be3f87c55e6b7428d9b3bb4b0b64cffe5763e975b1c2bf57955dad41a48420a043c19ba791c967a22c5f1a8f1e7b183a095c34b2f8e5a7475b2b272fa8893890ae45494039276e86e06e34bd0a0313d7001e69aace57117623f5579973d435000899aae1e53aa84f7390d12168509486a7275d11a89511cf2131a85c921137b09a5a5fd58ebdab1613560141a6b84fc735fcb550f3f120862730d63794036fd081a43017fd382a838b9352fbd47fe6e69bb566b7874904e0563414e12e81571039d6472c5474a08a30ce58b9feb30f68f4016f9ab8cebafe0c6163f5cab20b40f0a61d40ab71de19499be4353edd65f57b026759685831c296e04ed35a2da50037cda63734334a10b1686357b150179e1fafc64e8bd7c53c0721635ea72dd060e8a98742afcbf67bc1634ce00085bfc6caf57797cf8c1920869e982a8f3886406b8d3bc59e3e95f6820ecf715973d9e7a5d5aa794124ca28609aee08805eb9806f1db18f15a88b4cb740a191fa2859e7f2d1097bfbfa953b5d32f6676b0ad1807d631a4c3972cd5a36e26057dfb443aeab724623220f54f56c628636f055e20062a6208e4f28f4c670804f5fa94b1c2ca647c2255df8fffe00c9ecd10b9de470e3945e4ef69ce1052d642db607e3a3a9868524f7161d6d92e3eafa0f2dbef93013bec9785542e52ab4364c9e7b86311936a6ad84a5ac31f3e9771f50d2dcfd108f011ea4a3c7b4f4751686c5f308fe4b0b6f9f615603fcbcf90109e2e43a8a60cec9e0f03ce9cd577c7cbda803e22dc913e21283c79dfa0367eadfa52ca1b5608b24cc895586a7e9d7a14de6f7fc9c05d5f075ce485c526d6af6a8c2e89314402e6e18f0725673de7b388fda33a903fed71129c71d1cfd4c3fc7b4acf7cea1c054bdd2715a3fc60bf0a2fb2894964bd9493cdbfa9ac6de141b934a9d45c8b5c01092cfbb8a066f086c9b3534352bc7bebad702fa14d04200a19ef43d190ca04094752df5198b2326a067439109940854ba5f24aa4f154bb96e43bb889ff047202d6972c861898727a0d95b51693a5fbfd515c86574d5fdd13cc26cdc0cd55670b12b4627fd37a524549efc979cfcd144c54d6536062a775db659ac83bba64d70de2fa3e72ded3853ae5adfa583e57bd8216b28fb5d01883a9e50daaa2aaf21b04f6e070de69e30e0ccda51baf75a30e029835e6466e0882744d0dbdacbc399002f6726c7231fd00688d0ce02405cedbdca50b6dd8a137ac859b26885a42c85c02c86cfa73eb9d9000eb3b34bf88edfa54ef53511b4d1c7b86bc6b4b663825540067ad073477d524bb3bf7266634c809a1f61ba773a7c431dea0d44a652bed5c0efae59579f212e7afd4f3d1bf2d4b35b3a79d0af92c9d7ed7ca0cb4a689b2410f84e0cb4058b5c8956544a639b46e84cda77bc8c85e6d2776f8c248ca77f3b409bcb2344bce2ffeeb5e8172f30acec680d7ae4955dd5ec52388282888167ee308807928164a89ca047f3519c58c11cce9a61e30131d2cf28094adda116197b5002e34d0ec47686aecd95e9684e4b58bedd50babce2335831e782e88cc32dbf407df8b6a247c802808b8eb3fa70151df8ab5b2451f72eb24e588ad8777a2376200e27898d27f062e7585341d53e6dd59c31dd4b99dc9bebb17402db4b028c287094c5eafdb8dc7326338a56093710d0c23aa4070ed7a775aa300d9043b7be5ff0131eda9aa7dc8dc53c8a064981270843f4d81f960533e254b9e12e3e26159ab0cdd2fc4611fce463d298a4ee290708e54ff50cbe169daa73e4ef732288a64400cbd2c1ae623d4639ea2c6f7bf66ac1a5570d4ce44cb377d991323014ed0b406003388add580615f868b3b62c722e3d260d1ec485be75cf329fb74979f19722405151ce9ed1d3814e88afa65f2faeb9920cc5c57cfc7f62b77ad44f4d3ba07810c514afa34e5993fd4709e22dcb49533eecae0ca16224ac58d355005b61a09c204eecc283fd38293d506fc0b066a5ca57c451247b5b71a836a7f27a9124a8eb802caab385acd56d7330e48176b2b722d53c90baa333fc9f2c5a99b5e14412c7809cf309813525a5c5e851194a68dd2a0f74f43ad1cb5b2e16d9424ab1cb598b8044124345fc40d6401b5053eb2c2ce362bb2a2f52993c9b1e17da05f980183d5007f5b351c53b13f8545ba4ba97c15771993b1dd7a4e9a3fe4b030adfe090c8390a7dde3eff9c6402a5811284f8666a7e336413d850e747c8a5d956d267ead9d0371589be965a64919bfbe1255dbe68394715813e2647bda30f148a1af56a32a03870e358d28e70b149d7a6d8370f1ff2bad835b9ab5d6f150da2ae8f09208af0a325f4a641c7dd2ebb14266f688f207f790b890b99cce13dab5181d592de26302cbcef17465006d61d2ee339ec1798409de4b045b0e40d39cb8fd6b223bcb460425bf2a15dcce954eecc796a9014308b811d6b841541e466395660226bd67960d9ee6fc612219d735e575a7c3b19ab3a9402988aef62b25586798866215e13a095c09d8d0ca4a05004b19ab0704eb4c785269aff3b5a032a3f6800a809eabf706df3f3f9982a7465d36c78f271ba25dde13db9f6fb5395a228828e95969521006b01a3d839af57e596dca49586a7b3af7a0daabdcfc4472c9a8b3c71d39dbbf0beda304d34ad36870b560d17adf6f7e9d40325369e4db5c8781f04786be75cd03267e48d7708eaf8dc59b442e931ec5bf655f7fa8d1a15437be5764b123d5050d7cddba711c4531e46b5371d241656d9e79ce3c6fc174e20b5e277149bf8bc80b81ac979402a2f230d13727f0895a986c1c52e75dfaec1e5fc98ddab8e3da5003bcc529ee3570c46cf54c21545ded7733c64acfdc11b44c46edb436418bff2402b93a60d709b3cd7242a9714afffa25b436cb6c17eb2840cf3e85fa4564a94e0475562e9df95b49ba027af1e6243164c40913021079c2a85391be84a9053c370a221d093f8b36968ea604331989244caa89b296b7d9a415a20085b38379161a0c6bad7752fa341d7af81a12d5103ec4d71c406225f745c5a0eb045a89f1a53c0787a515f63a58152a1547cff080c2480de32e60aebbc6afc8f0425d1fd2de1f05e0dbce3cb1bbc4af7e9fb256182b194e6501323f6c2f627bc9f3768f47799b0f04393ecd06eb4b0198cccc33bd71dd353c710c5a1efd8ef3a9d99a1737e6ec07d1d679eeecd6d7474906f2004c42d4fc9ef7d61d31e5d1b3966a21b05873e90f7b685fdb7fb295b8db4a738af8c54b76ae8384126efafec4bac28978f5aedf050d6ea0b508a7b9029d4c568c6d5686daa07f17a44e38f077f3b5c4e0446128001770b3275e70a1d5cdb218b794598a82cee7311c5abacf4941a1ae8392717000 false +check_ring_signature 025ec91bd3479fe58432d96aaa460a75ffadd93f7883b455758c661d13633ff7 5125c1194794140f7e3c5f3fc4f917f355960e6ff08c9ae7815716d5d3e3f665 11 fd5536e5d4f565bd01580f06a50e5e51a4d691d10c1e8c43113bf984cc3bebee 317da8b9418393f43fdf24896f75f43dd54f6c8f704d8b1e2971790f861a6fa6 4ae530473f44d5fa5ffe037123717895a2b63cedd38ebf295c23037c9a138c66 db8598a854e4b1c460d87926d658bab181ddc28b067ad02a3c9f2c9c1b6c0cb8 3b455bad634c68fba775065042c9e54cb679e60dbc49dd1e1d856fc8f33668d8 7a129166c15ad1477f3b8ae5c0e53a2f2cf615fea96e776e6d39d2272405c930 98870da5e5501a581911a6839ebe4f993e4902370e2c583a291df6396fc325a0 7e093078b34eadf595ade4f4e729b08e4ad15751076cd3a8f032dd4091030201 b2c72b2064f8b93316e69b8237ecdf1c4f90cacfaf2496ad776c0c1422d90c01 6e6c3a87ed09251ebbd7defaab73b7637aceb2804de362da2c3ec0ae4b9dc927 c4249b531bbfdf394cb9e77de6dbda2fa79c4ceebceeff20cbcca0ce8110d2d1 5c3d6bc9ad8ca6a5074958015a84c7cdd10ea2e4132f1e12b8224f1614181b080f6fd07f72c786f183c3c9a98d13a99dfd3cac974f2a441237b5210a90185a0ec8e75c304b5954e13d26fb0963d7bca803370be56598166bbfb12a80c815b50f8715c0e9487327ee29dae77458dec5c365e653088054e742722023dd729218040553972d9e9fbea18bc63c861dc15df9a427cfc2276f6e42c29744d0f13d8509ef584fe8c0267310cb6de5120b7be1bb3f43e936b6c2b01de5f07ddfba5bd907af2ee7f14fb43b01b6342faf581b8de767438684b15f1ab15f99379fe21eec0f242badae0fd1bc9a2e3035e1d711c1b40be6a373fd6a7d2cd602218e7499aa02d98ca6a864f4bbfbb51da0a88efc59286ab84f4a616ff5a4d2f3641bb6dfd40cc224427e22097bb9e4416d5c2c92c032faabe29c006f505e75650164838c740826050147f5f4a26886f79cf187c134d0a2aa90ee8e1f9f0a30d5f020114a2400a7aa8726ec010da688a1d69f6a60e6e671f03309563bc1d981cefb4873f1280d7aa6eaaa71b6d75ce9a6ce825e0a14b719ef5d0a6ce17febf9e6411242be180cd229ede8cb623bafea61ce39ba1fba6aff4b324b11e9aa20e9cf4716321edc056ef1855a9728c449981ceb7c10da23c71ec41d659b6c8e7370c070ad6ce934019f35146675de75af7eaf665eaec49de06eab3dc85142f15954b7d2cc613f37076bee157d1b5eb5925a45e024b7454b9cfcccc7409a4375f02e47f78fd5f2ae0272740d17c835a0900556f8ea13b4415688d0c534acc05a431cee58539c8ac202151317f33a5fb5ed5b6ff90d4945b64fb301cf2ef825a134293c304f34b0c707f1ac272f6e3b767b26d30744ed2bcdaa9f9572ab9fef94d12d5e5f850fad8605cacc6d9e17f7a503316107feae74c3bf9f1e762a7611e83826f10d3b09b4ea0740f46c0450fb03aa6c707019e7fbf8c79a94d138f2a86b35e308fa33cadf560b true +check_ring_signature 20200739e06479f6e64024992891a0d2088b962840c3a7192d6e0d8b1cb2f74f 6abb5dc7e98c485a5eebea3c8ff1208b8adc5d9edc0c5875127df1c1491ad148 4 9c50e141e5a25bec7bc9b49341c5b85a9fc13502eeb8626d3675a15c1f7b2b74 d9adde6f90fd9998d183651483ca2f7f929ac74ecb2b87a36e074aff675a79dd 02db2c5560b38e73f52a2c668c40338d8408369bf20c2cf7c93b676543c7cb14 bb9120be6354edd9c9406e353276cca9ec8e66783060156744959b4d48a5f1a0 ca185f0d8a501a32ae68063d2db25ad0577529ff0cdc4e541271588e28c11a07bfe332d2ed20430b70075dfe980899cb41e27ba73fc971beb0af47537976380480c9090494321b4537f0a1b94b7361f98b1895da697c6d1c82b7e782c0c2250fa354911b2b835d92b6007c5cd12cea929069e52fd09a3162af3c264f75f0330081ea3232fed682b7057d505716fe770650fd27f155197c4127edfa1cb9b059020d19bae779cdb5b72885beaca41baf5dc74f18731a17df28e666a626947f38021173b0903f062a0835307d106f8e2ef0ecd927bff419a0fe164fa83384081b009b8b3ed35a713f0cf2727f58d38c693763bb0cc76114f839484272a8c2c57c0a true +check_ring_signature b4617f24e3e4b49181381cca2db64faefc621b9ac8285c0f96086b8671f0bb30 e23c7292ee9e3d496bb95b974c5a523f4c85488d66737baab76e469fe697d2a1 2 4fb8b90b4628dcbb847a90c622844c21ffeed2f668f7ebd88352514a2a7c310c 15f26dcb5292c7177b583867a8332c5a7c7fd591fca660551fa21fe5da44dd5c b137e833655d834fdd8c81773e2b74225a6a1c2da7753a7f8037a959bebab405ed09cf4b02b2ecc19d8c6e4dbcaa6193022627059a3269d307277e49737acf0519c61a6cd7ecf5ca4dc9b30974f8c575b4cedf61a6ef53e727941e97e6824afc4993a66c01394bfaec0c04ed1168db051729148de93551ec451d50feacdbde00 false +check_ring_signature f96fe998a5b9ca9610353eca92825c7ebaf6209e234b6a6b4fad162e47a6e30a d1828cb62619932ddf93e16ce29adf677a2eec18cb389428b240dc5f193597e5 3 1508ba4adacc91cee2cbe2da348926b4db15a58e1291a82c79e168550e4ff353 3bd2400f86cd861492460846f56dc3c355e31161eb48aae8b99fba3f685f815b c65e539fc336cddf3676c9f3602ea4c5825d72c8c3293fb297e250a03a68d085 42a1ef00f702261c289fc7bb33385964a0a16d6e4a77fd0c0c8d4826ec5ed40346a126c784b32a50a140d222a4362e50eba7dee51d01f679c51fffeb25001b0c4836af96d7ee49b415c37c62fa6aefae7aedfca5c260e7cc68abcf05dc8b8fdd7480a85107204dd218e6ca402f85caba795021684308a514449a3737aa5c6293985e6bd77b9e324917431e88c0af5626ecf2d8fcc2a3b861ea12ff58f5dd760089404193199996cc7c7416d575b86d40311d1c9a42b469524aff2023aeeab309 false +check_ring_signature 3eb7b655121b6780620385584827516fc5842f85fe048176d37c538cfdb328fd 67f42d92f7de306d482a82dd9154fcd4aa751b812db27c77b42836563d61e807 14 b23db5b96eece4d94efa4486d70dc502d6cc53f2420451c9989dc6f9dc46b7b4 75129698f548db26c16cf807453b63b74eea564a1f5d7b27de36b0cb9e942f71 b3a43e8de95500eb512d10dce77d6e29c8058d1acec0c43f04dfaf24b382a154 3cb954d0273fae5143845288578996be00b1d05f7dc000fc17882d0a2dd55eb8 eaaf50918060b442992af499d104e2a37305687ffdee13ef7f029cdb059aa620 9d6f70b3b2ec5efa5bbd1466b7c94874126e25e0f75769dc8a17c166ff5a8313 5b893c97ce86bba09a2900e598e15174d171d02c19c7aca018786ce418ff7c21 51700d3ffc7f8ad7f39869dc940bcaf63a2224aeac17abe19e8c273237bfd1c2 6ac9e2330377d4027e416312ccd3a25a947ba8c120c05ea315f88b5776249228 075863dc5acfe9305580c6d2ae5fc155980043bc57df3e6995dcb764b84c0948 3e068b9c929f2a22daf3b5a6bbf40014486375a9ea474cbbc51718c61433b3b2 244df63ed4c120809fb6ba9b4c43ad1c3186f3c2071f0a9b94235af1f96dfea6 9d24e381efaf32b9b9081a647846f037b8472693092b91fd42234e33bc51291c d156fafbded8d348d66dee74989ce7075a60c475374eeb632422accfb4c1ded0 7ec4ab7c11005e903d3c91e77fcb16e6f77251992cc48a5ba4b93c99b2cfd20bf8e47d3c7b92a48d8433215cd8478ac0c7bfa97656ae66e67c644dc0baadbf035e56cf76a81638cff94dee3aaf46e0e6516f8edec36b92f4584ad23df717d80f9710674e05dba4958d4db0354ab88c72d62a83dc9aada5b5074715942626dc01d1cd81ff6fb115e4d8209817417d6719208d11f969084c78a70ff12133d7220468dfa82f985345cee757b5426d865b8c20b55660804a4c2c664a734a13a6810cb476f0c661ac6be9c58a1df92b15c4d96862a4e4415f79df2a5c336db7024a083b561a75aa63b261166627a361dc3a1a844c6aa36e297a856dde96f91d02e50f9c49f12d99ce4111810fbb2561f7197e4a2c04bc1696fda86ea5a4d6ea9fb20db3dceadf51fae85d96c0b694bad34b5ffa586472bda9707b806f395671750d0a1e31a9c2fbb7c7ebba8b8e8482cac1c74e238d5a70be199cc69441591aa4f404b202d3009c30756bbb2fd100b18a84577b2d4c03534a4528c29bb8c258fd940d5db6e7df01a32399ffc23675666489ffdff780389ae6065b3cee131a2ac9ec0552dae3d3a421b9c22207eef063f3db86abee18109c51eace43629556361d32009a021dd9607c28c78011342b43370b802422fda6b19acab1442671c736fab5038982a889b3f50e217a648b767c47ead3153b9c383fc51aebbdbd2f875303a800fce2394910df9743b3ae2412205b32b9a1efa9b1af98273cd54a741dd7a0ca07491239b718e3d538ae59f6de6183c2614118369aca6f1ca20918b28a98b14107f42a94a1ca086d64995bb1dd0629332487dc1fb8c5343466023ec84136f2fa0324c07a83e60e10a8187bc9fc072967321451071be871cc84b08cec512c2e5803f4912b89e151f1d41d36420f5335a355b194bf64198eb563284507c36c025304ce2fac25c9d6ab5e0c57aa99d9727408383f8f907a72287ec9bdd3580e02230e03b1819352eca9aee083464f5faa81071bc5b88462740da1a68416f6a61771012b3c373d53c1de9590b7aff9633994509605ea9d32e7ecab9d311180adb56c01454d9a16d8993af4138fd27727253b5b6d93a252a4850585e6b52cc44af9e909e58cbd980b319a5c47b4e15a3c2caca4f3380a47f78ed2e1d4c4db7ac849530fe3bcaa97e9610d1258cc4e53e697578c8a5ad0899b9ae87e4a0b5c3c245e7d0a2bf45fa533238c46613946c7d0278648c0bf9ba835fbd4133a09798c700ced09 false +check_ring_signature 3c989b0b923c18ae6050ea95ec3a31d6847b8e1c3ea48c67249f35e732a25445 9ad323b4716efda644c0313deba768ba4ff5d86609d2365cbef875abdb672ca6 1 ef8039bee2bd6c439181b1fa179b55a03a30b051af732289550667f4f2302610 6f077c4f1608757fd1d69f0d94b23bdea51ee005ae7d864b0820a37851c36e07d4403e22c735d20ce8ff8583bba01ac4c00b2410f9dda38c33cc57c4038bb006 false +check_ring_signature 043a3e9222de87dbd2d774fe147e05228dcee7a3d327603294dfe03cba89562e 8bc546e059389fff1dee690da878d531008a95dfa4d264927e0a002e20e85f03 86 672f441ad39fd9b05e34808c393bd6a9a998ffb27198577c04394b2fe95b2dc6 acefe4686665f5a0758501207ba9a558cb9b67b071b93992cccf890b33cf1793 a6b600ea4d25cd80c81f6c20dc4176cca7cecb233d6a05145ea2450eb1fc5a2a c0dcbb48362e09f3537a8fc70015eb491edd9f54a82e1deceec050e633fcc157 7de28b99a925e9652347e22b76ddb356c457bee39306e8561fa0be4780ff3a2b 98fe2ab58f10d3ba98e26bd74cd718e1cdf7158f8af734eabd7a1f01d362342c 8098087596c0f8ca1f7de31c2798001bf3e8cf6e1818c9dacf0c7e3b5c8690dd 6b995dc8da1c34f7c4f08dd7bb2a39e24e46c224c6ed678619989be4ecc18044 6cfdff8d3a4fa3c557f4455200f7437931fc16075ba5668c33bbadc13b9e60b6 0469653e88652bb76f5c24c4a6204bbc597558810f969d275af619260cf28bc7 8c45254321b2b0422612acbc8ccd43994b68ba866af4f4d726fd027e9ee80150 cbdad0b67333f1ad9c9b39c431017926ae05e5f0021405a1e6f237f7dfd7b564 c55c426eb416b25fb196b2da3ae759c05c9d4daa6f539130e5fe063e8164548b e81d84da9cb824c894377f0f24cc068456092e00c92955ba2010f3299e05a85b 328e04a69a12f4c3cbf72a97459fb4c5df80fd93a4078dde26d3bc807935dce1 0309c6f484b7a084c5efb259a125c5489225e0653c9edfe3a408c639a406e820 884683839033b150a10021c8377fed388b73f162175c25344443ceb3cdc288f9 cdf3f4a5127a882a3e5c00efe3baf98ab3b598a2cf14aa91450ae97e1c35ef26 4a5d6c8167702649440a64e405249cd79be33979382703a9f989b60f47264aa2 8a2618f6589946fe7707f9ee647abd7535949cbacf0082f2f3c00d0836c44a42 3ac89041fbe492cc13631efb4bd9c60cbc0f03962ac1ff0db87ad7e2bf2bd590 2b60c956fff3a1502b3249d189eb091596106582ca1bef7f7c62985a59434be7 1a7c696e67a33da17a3b335121c930177dde6286c9f5befa79ec53bfbc05d148 bc8ff3373da44577efd1ca7960e990b576838d05f4d251aaef2cd37b3fe5509a ba359076dc23217730d69d0329354e4f407d046b0113e99071a9455a9f44b264 6ee2cb606df97b3a4cfc5c5b4799d1ad7181353e162bf381162f9bbc29cba250 cafc907736db08def107f9bec87d125780442c227f9f8641c67a9a548519f05b a22900f3424d213fa176ae78315261a713392c9038e87fe03556e7bb26dbb587 41e240c162cde490c40ef7d3aef716cf86d4b6dfb177e208b5cf973af48dc141 db575885bc553fac36216384178871e5d2abf832319db3c929dd0829fc4bec84 1ecea7e8c1e7757518a5e7b793c2f0a96ed44fc1610a2c790497249766ba0b0f ebc16514e0e6b10c5a655d46e1a146c91246d7a7d1fc55d78410b3dd23a66d0f ddaf6f4140a16d5f5acaa3a300ed88b1a033d3c9ff033622938b1aa051d3e900 2e1c2ba7ce84157c7eb686b3a26f0ccd3e83cede983649bbcdcb63ebb58e3b7c a2dc5480631ec76c895512878b614799ede902ae78971e6798588f410fe99528 bf66e085590ac18b3681235219a0fc1ec5f8392efa27bd2eb09404548dbb4734 5a4ef577096ae69bb331fc30c147356a5190001264f0ef56a29678efe49f3a73 2b1113d7a3ee8df7c4f0de947fd61a104584105a41644a3d48a33724ffe3e72b 89e478f5f4f2192f31d713655c00ff02334f0767bd51fb2dff93fd94575dc45d 607f202390f2613ec120646015d74240a5445f58310a05edb5914cd931316984 afdf21ae74ab2d3a433d5737365ae4f63ac164e4b37b555d45cd35f2da42144e c9e66394caae47481ee554c8557e482d146343e6ef144224445aff98d2e55fd7 e81cf42eb99e1b313bb13b365ca79636d01e87b4388f439ed834ec7ad9d0b0be 33a8ca8b3dd6dbfd6f562e400f7eedf2f4c40aac68b6e1310e764714ef9879c3 d93c21905c379302c833115256488d9c12c11358ee9c6263b8e0c873db8f2144 ae4d2235a4bc3984b911419a4a4bbbc1540636d1d20ee1aa5dc49ef333851032 84539171d4c8f33012184db5db2aedc1a7b8b8f0089ad252723859e4c659cd3a 3fdd7c3dc8509b588fdbf14a588e4c827fc865e95ca34dd0bcd46492cfc4651f 5f71a2aad953e78c700fbe99790ffb97de4c5504fdda20f86099d65fe341aafe bb2c56a3ff998842410a4158089e044ad8fbdb297281672d2fb6de6a3534a957 6a8648ba6d79087336745dcbe582812d4c80639e862ae226df3623b8fce2fc55 0ecd1384eabe80d5c38cdf35e95b01ef85fa2ccd60bd4ff12c2e27056949c660 991e82af108e06972e1ec6f6aba5441bd7d8f0997d1bc7693d8087084e069e1c c315219c06721884ec9a14d5103653fa142a21aad53d5daf8da5fe725e1a47ae 1be9abad643ca93ef987438d742f737a1244a84850ef5a021099972be69ff1c5 5ed5a00c874277adb2c70b0762d0612afcfb9938a1b006ae84a8b15541cad797 df086c9cef018e44c2495e263ad0c07a3738b786c614a5fbb36ba5901d19c6dd ca2b9e0b03c891c9ba23ab30619b058764858caa9a56a381594558c0962511f9 2943aec6e70c7becd519b553cfc9efe2f079ee8b68e4e00b8ff775c34755becd bc4500d3cdf76f673598359a20c7f3976863590c919b10236636bb8c9fc37bd8 0f411cf8d026eb3eacbb5b2f581dfe3158ec4bd73b2cd6c36e21cc97a44878fd 6493b2c3664a62ff2e8cc3b75ca7ec86eb70440cd24a5056d3d83f227ce3a715 711bb1b1867397a25d946ef6d6b3a06cf143b39dcfc1090df5bd440e161337d5 958b590d80aed6689fe8d841347ffc89758f0889558532883514792e4d27bcbe b4d7024d09bfc88de1596210f487deb493d10ea85f69087bdda204e4e2b6ce13 34c62cfd361b5cb01311fb5f3cda7a68f5f0870d0736723236671fad0e139305 c26eb0022a9dbf65b0c788346dd6244447d365f381e41f4a3fe58c21f86f820b 72c909a1ef336df0b169291b1c477319247ddf53221cc3bfbcbde4d090e6ce88 1e9aaf81ae4194121bcc3b529d1eec4022a2fb0146b02612ad08a0238b07cb2d 15960f48d620063d3515e468d9ac0d049f16ea721c43a18e2847d02f1e668c76 cf3ecb31cc04c7854ad84ea335678f203a63c8f6edb2b0e1c93d0550ebe18174 e115175a4ef450b54544db6ab4934a3af42b097b98862f9af3ff3f59b23c5022 b9864dbf5f2e2d22ec5404c4c5a70511b96470ce2a87231ef251ba2ff4d4d45d a21d4ecaef48cb3a826af0ac8f8930cc848606952428b5cdcab3d377b48d5cc9 cc65cabf9c86d01a33d6580baba4cb380b2b0eef50a39371684ae2b8b44b65a9 9f9741d7fb32052ec5ad022093f3e5b167f7c5f887c8329f3d6f74830efdec85 65155ce0cebf2a4f65d3f4109027f682a5d67dc84638e34c07a075bab872b9e2 dc152e038f54fb1fb4f12d212a40e88b329af53480abb0138a1844c097639a00 517d7c4b3af25fb80df9762e8509e662f824f46386af8f028d2c8d09ad503a5e 9dcd695c434814effe0b0605ad7e5a2eb886fbd9a1b43ae4b220f89705ed110a 6a43f11298f7e74644b7bd66e243d03e874e1cb64c864ed3ff42076b0c391085 65324924f739e0c74e6b41bf7161c1ebd0e4268983dd9fb43493ff89255898a1 29eaced79c78a8ec9e00e24265f19f943838446b5337d42859ed917ba6d74b4c 0a1d19b6bdc250b2c0c8d36791c466086e751123dd0e97535e2471e55d21da6c 71187a9f201db81c88c8492acc88cc3a840c282a400250ebd8ed05ffb8961a37 6e8a3980ec48d034b0eb2cf89789388fb365330d94c121a35199fa468eaa5bee 1353edb66cdda9f27a530c171e6989d4533598b7131ad6a776b20ec11f60df0de636d41ff2e92667b0e97c7f50606cb4c96cc8c7df62ae95e95bfc22d6dd3c0310f9ad662b6e16c04011d5e498395c215aeb3b1740665653b6af25bf34ef620a1dfa3c973ffbbfb809d31b516c05e9062fc7d6bfe39cf9d2efd024aa587e4d0e03fb818703f84f42552bad77e62921bfbf31f081ba6f3a61e0e9f7e580bbba05e6fbf4db414d62ac1146e71783abc17a6a341c4f92751c6daa6a527edb05900c6702e14ad7d6e34624dc1eff80b62b465caf0a827302f7ac9d1f84aaaeaf740bf328f00a96362915e4a8b729e4843f8b431bde012ae80bbd319f076d9323ae094513f2f62d5118873a26725f9898d585bf8eea8f1d820ce12b5c95b3621143000e0dd05b6fe97aef0eb55aa42a33bba0ec48881807d57cea7a05151d6754be06f6f0eb183187e8ae808e4398d73425691fd56808e1352528f10917cffbc3650c34fd5492a55c3d3afcc0e492051e7f2417b1f2b62b7dd17b41e22e68b223d6096f6539c327583faa537e4952dd650ab78d989f2189bc453a9e20704582545f054d9eb5bdf84694eebfe16282b86a74865b7a04d767662aa89502c3fb008e7d015d44b566005e9d5da3259e9c03596d25f2728c5733c3036df46c7f823386dd0273887f60d9fada60f3692b8bb2a76cb240d49df343a9978c8134247eb41bc008f46851e971e74f4146a70cd5334117dd904276bbdd83b092b9857d501e2ad00f3a91a361bcaa51121ddfddd56f38e25c9b5442f99e7ef4ee9c6e5d1f3a95a200742d30f7c03c2ea619ab60fa7a87b4a778b12bc4824b809371e913b06df7f00d2eba8a9ec130ff4dc81ff8b03efe24a6a745a99a5a4fa519a845239e35be410cccf851ab35c36e31c2481af9e7a891a5c62b072a2ba5472a23894ba58f3ede0a0faa31f90f16f8d4aa1762145e91853a05f81e96e91b24a7879cb121c671e703f7d91de137ca7a057eb824ea24fa5abfbb606eb7493be394ea5e004dd68464018674373754c666bb4895c0cf22ba3d2e54e2540319ca97bff24221f163099b0498d5c965bb5c6c216275ea6e53f5e6be26024fb094885669433be2cccf76ab04ac7ac1ac0576e85cfba99fe5362f8e240ae29c8761a43b008f68ad71080f040ff77d8e3b7ce91288fc6db577d21ea8d9821ed1a301d58ba8f7cb1eac54e7880be3a2fec8e784721c81dcbc227e2f6477f28dced50542b619c757ac1bd926370e04a8470b38b5f1db3c3a1383d0918bdb693e1314beab19fc283a04a0e56976003e796582f493b9ae6d84517ce68e53e7936f57c0fef2a0e9cb292f2d52411e01361061e1194b83dc69ee2fbe3a8f88f761884a3edc57676ebff94031e5afda0af30d49e20f8bae8a14deb12e6ee8b2b92a040eb38bf9bdb42f3ffb6e5ab04505dd11b1cd71d9c0229f12701125caf1c11c2e19c4f2fdb03372b81b57c48f4102aa79d0f44d756db18b8df5341659c2ec6a6f42e06700972b5bd2acc4501da602d439ee31d2f35198b114b343a0e762a877ff5847ba40dac2e6114bd8ceb4210ee82c42bbdc341818af0d59bb67bff6d4f12a0567da99c3864bd465eb96a070046190cfe0b45b91b395270d3922a4f859acaf1b6738c7e045ef63a59233ebc70618de3682d066dee469661a1429f375fc868680c94f8788a6925f48aaad6ab90e76ed830db521e3ee199ecdaec5a25042df3271494be89b5e66a66fe5d541a6085e0a6eb63596616e52368766433736f396c51f47c8e65481d8f67adb50386c0758d7270cd6323e044f825024c6bc988924d71a659d407fc116c457cd1e4f8d0d8bfdb859060577e850b84dc8e0300486766bc5b78b214ccf83c36deea5e5d20c1bd68c6a542c33c8907c2341624a8eaab8d632965ff60bd6f05ca6c70d811407bcc2cf4bdfaeb40212410dd2877f2a9150e133d27573983b67ca510bd892700d2742cad027ea9743a5570377972a6ffbbe044c456a699c0176736c5a3306f8013414f43ef20e9838fa1af4ca7f06b6b10a58731f03709d3616cc69bf27c9d509f5a449dda60ee83fe61ebdcb5747c36ed9ea899bb24947f76861bffc3a713a0e1caffb470e3e00e20f939f122960fc0be52e33e89cd025298119a0011b683902f4c2702806538e157bd9f8388e32dda7a7eccb0bb7c9d4e4ed6c2ca58f7aea0cdb207cabf2fcd3caad52f06ee62a8b6ed99513d5c5d35851ea866e1e8d588d082583e95be6b73111426bcd231d6d628f6e8f2014f1ddedbe3500c14152b1da0a2c5be42abb29828a68506639403f254462855636bbfa3a7bbc66950c96aedb0afc2971aff9ed0217e8f167a512e97f245191ede88794796b8ac297723178d700b863871267ef1b878f997c6b852d22324e4448f454abea3402e061bbfcaa170a0c993dc25ab88aaf19a103a7daeee1da5b1315905799891d5f2c98b01eaad005846f38e67f062187b0b63aec721cbe7702eae287c3dd0d7426dc6f857285870794e2da4c309ef6ae7277ba4a134817c4b78eaa965631d525f31448a041f73f08e250124a130635700edbcfce0ed6f64ab2a22cf07711616103a2f1a47331cd031a5be8ae2e9c26ade5a67547b2f93c73814575a624cf9b291957c73ddda2f900674ee7523b262499b9da1098a6807000bb4606a45896f1bf3d2483c882dd460e2e7c4e81c12560509fe0bd6174fd028dcac1318b4eb58545014966d03659c109c28aaaf866ebf8178cb5fd8faf5ad07a999bbd58e7f9ce88503272b1d38f8c034e2c9e01fb5c7d9db0c9fbe85e6aae6467e1f36aca04b1e64df5d03a8be1af0976774fdac793a5860d33e9bc8ce57d820a961fe056b134852a2ee6a3e9dfa60955be4763aa2d1443ac3dad423f02ae593fc2b0dcdd3a986e2a5606a9a9ae93040c760f5cb4a3248678155065894883eb8d2653004acfa5ada4ba1d8cd4cc690e5206f0d4d775437b52358dbda9a3f72e2da76b8e64540fdeebc536da93b3b702851a47cdb43f580331cb94fce1b42c2878c306f8bbd70e75b3d4077e3c259200762a4b5a433072df8ddc215fd17e843fd10aaebaf4bd6dca233aaaf0ecbd0d0b9f2858abafeb8e2b0e7f23d8201dd364afb1056b24033ba39dfed1396f2597009da1fc1c30c18c5e27adab79bff8211094fb50546f3ce53377337cf62654ec0945cc294bcd0bef1b55002d542677fa4dfcafa70211df9fedcc686e8f45962d019d396a76ef0db5bb63caa63b70b20aca1ef43be77cd41f3ce6229cb1fc214f0626fe4b159428632a68c7e3b6558f814ba6f31d26b688aa2fb4cb7a24320f0e0476b4188c80002bc0c6c48f97206a271dee1dab439bc50cba71208cb1485e51095f09bae1ec6357b3415a79c6661e72da9b2c4f3759a62b2ed02fd32d97ffcc08f8b3a55b035789a1945367e3e1105c6f6657de85f5a499111b4b53899cded40f10f592b798ba31acf39b8bea69c3648e89ea8284d7933f2b638a3ab21459e70eebf022fbd7c6fb6232949b2ebe2a51b84c152ff28aad31d8034e207cce5d430bcfdf76181b50e47ee4ae5ba1c404c55ecdb44fcb46228133fcecce474733e408dc5969771c061df2ec6c9911a7d6046765ffd159404d385e1d0af37ee2b96f006cd37af8dbb6e093a8a04e896f21e6c93a8e9fe646b88b34d3ba3069fc2af80c15ab51d419ec9d8f2081bcde67e5bcf9dbca97a9c081bf74f1ee9cf6645117082f41dbee40d98bba7718eab3a2aed01c4efe028510856720e1966bf41ead5807e576a3e9bc68afcdd910932d6c2355ff398d03e2bf501b3f2b475011ec59b2014409cbf56702a842093f64d9763afa17be63067934ff3d726e50ce1aed25d409df6e3f9bcc0a3adab5466c34d328645cd273b2b9a7abc48d3b3a99b896c42206125a316cf978895546691e5da8000f510f8ab73dc960aee188cf9f34d05c6109c31eef39b4376a6b821254fc888aff4cb6fdf65c865e673cc3c68bae7bdb2c0e468b853d00ae9ab7454b1ad5be769f376666ed664108eb743bf3aa1dcb19da0d513ca4bc56c004953c1d04ccf68b36bb0b3f9be594c9be301e8c93552875d903f380063a333d4549236293826dab4557c07e9658b9f8c52f78221799a366aa0b90d0f46b323ecc09a0e6bc0e126f30108c110e4192ef8a3645e4ea5ab93ece087935122b2ddfd190592147421646413a71c11ad055c6fb9cd3e6e9cabfd6340803d19cb5733942088b36b3285f003fa6c09db5a2074d87728f6996cef7341a0c7bfdad24895084b5d4720b0666fa5f3ecdb2c48436f7b7ba7afff995ee997d0f403eba9568fa3f45cd4da3702b45013b8960b64cd4eff0c775c4edb6ce0a490468136202be4e0d85a0edcf6383c3c4f5e2e180edc879e74cc94233cb45cc270e1a8313b6128d557c130181c953edf0c216ab66d9ea0d12aaf0fac889760f2a03519d2ebf577677b398056eea2f0895a388586d1b72cc37b3cd9d8494c0a06e0b69e0c40aa830a8d1353b7aba462230a5142f1da9d06a228b01e8e310c2fc9faf449025f4e9d0b325ad115fee1a918695c4ea8a0d0cc5e35eb696652abf6cd409c1e7d1be1f09dc7831d36c1ccf03bdd5036c1741fe603e52d555a72aa59979010a09ad0e6019f8e861f3bd986e46fa1bcd85e2483d65dded1bfdd50cc0d69f03367dc49cf85ae6f9f178dfc20a09b848cb3bf0f9f61d57cb0aa83a85e5fdf505f0f6c9964a2f9949fc17f6dfb5aa5556bfc6ef5ae028f41e39665af91a65750aa4f9bf4dc74cd0e877ebd0697f6d678e660ed7f2d97cc33337bb690bb98825095c841906ee941efb84e482563f7df63271bbd90128e4025be423e16d0c2abb073450ba213ac15357205531cfd7de669bdbe9f9ebe94720e4ab4977ef19511706405616212a8f902c1329d24618e17e48e7c4afb213f4c78f004ee5a41190ff0943dbce69d7ab641f6675035470ed0bb0f66d44bb8ac226224f23fddc0134fa0b5b080d22581b0fa76d398481b9f0e7a1a791c761018418b2234955f4bcdcf301c13b5258adefec6fd4bf6201a26fc02db95043e888107750e473c7ab72c4810dfac7c4dc1b3db84e08f7aaa47b65bf4baf5629953c4b83642bdcda62a51f570fd9bd513bb468f11cf2b3762367f059dac9e6cc2ae69556933953f58576931d0b568863047128b0bf2d70ccf1f4f70731d4174a28099f64f2a0e8284cea3766099ed0186a84b4a3253b7d5226343a438f4000570637063300e46944d0fc21930dd0a833a734ade1638b843ddb74874cdd071b329e75de6b8854ebb7129036e7045d6bafa9989906b5bcc6ee08e967096051e3798fb2fd9a74d8f47322d0dab6021d07dc2b391bf524dfbd40fb49bb105ff256b852ff894246636f54f7e46d1208ad0f14573a71ea62a2450585e3e212cde891196a975985d94e374abd5527180b63c2fd725636982dfe59549f3886fac1ff588853364ae1295720b80741fba2097bcf3c56d96d0b021e8e91d8207c5f299fb1a2f55484f6aa469fa7d651079b0e019ec60bc15a6d3de634ca44a6ea80d67f87d6450b0ebc3330948ef70005150f0e7050afbb9f62e802a6c6b28a21ec97e4e96e5ecf534ac5d879e909fce2370512d80e5fbd9367cd4143f0cee8a83949fe4c33f162ae9f4233f09bfcda7ef305bdc28d7bedbdfa9085e3189822ee02b8430984a2ba41be8cf703aa212a242d057199c8131afb8e4cd4b9c418b47fb6af5fb26be8ff5ab79ee9e342d61a330f024005c4ade884b3596837a78c907b1908532592bc9290217220ac036a752cf6088024820d294c40c2cc177b74dd986ddb7d962fb209668738fd5f8dd04ce7b955e0f549217451cad103f1d50c82c7de3a80fa84d647cd4232746c384ed4070f0704233f08d086116a79c12f8f5e0d4b9b8a60bfd372026987d49693e6e2df5e04b6e4e1d808e15ad8add4b00bdb15f7345990909f0265e191217331ae38fb0c061fee60cf6881b06c476ae24ffec5fd86cfd2e1a96a6b13623efbc51f71b13201b89b83eadc8fbf571b2113b721008c45661254449e176ae61b9a8d2a57c31707a53cea534d83772dddf470c03cd5a99b70645843e2f8a32ca774efc3d9cdee3ab29860f764637e23b59171887164b7db9a136f10c358972265b3dac59c65cb0bdfce7a4be9288d6db9d269957e2f26216343b5b758cee35dd0cf8b3bad12e207d832e07278ee9f9bae55048fd16367675e98faa5926d11b3ae326efb5a1603019bb24e4ed8a384c1d8cab6c55a09f08d68734b552cb6c95d3499a14c37bb62061b226414dd03fabfb5534246fe51b5ed3446d06d2eda8e4dffe965e974ad580cf38f6b21d7cb4ad79931ce0e4a87719202727742fef5c70bac9b25efaa23f700a7e40b70c61d1e5f624b99dd58983de005b90da9a61df8c07fe03f7980364b00e2fd6bf14aba2dce4118717fa430dc183f75068625f638349bb3245dd39da30e9e3cf0cdbb584d98f54f37185d3de5c634453e9e311c2da1822f2428059d8502c1a065b68e40c326f344ef33ab4e5ebaa322b6c8b3004bbb847c20bd4bd1620308aec7b62237ad32c8454a2642a815a769b46374621af03f67177887be582600debf66006a8fc15da82998dcc35f43ae7a927561c30d114b5bff8b24bcf2490eb3be9966f028e51118df2e5f2e95ac8bbcc2316ce7cc07c49002316cd55c8b09ada462ddc7005ba36dace9b9b59509795386760ac7bdb842a093c11fc6f8770f33b08ecdfc1235e8990ac55cadb83f33200983fce022633b95251844f2092301c4423513f877a7c13732f529ceaff5a1e758be10c09489bed4636351fabf3c0a5f85950948dac82846b484e954ec7eeef71c0cef5b6c542f17c43653edf3240014e1269f4dcdc302a0fd23e2b314a44fd1b758b4c2654cb45be9e37edbee3f0de8cacdfa45028109cd4bff40d784d9a568e0b7e1b642df7a49df138dbcb12f00044eb4c0ad36d9dc1c379a76d8236903746a15b6eba41767b41363786a79140b6829f05104b8ee761b08e17d6c3146838beaf3023095c9c8134fbd99a596c303924aa1b258891cbe7d33e3e0f96af55c4f1663cfe11306175663f7d8ec86470fb1c5e339b455b5c624a8aff4ebec5150ef2ece4c755b8f7e42f060e81e0a880001f17d4e7b65cbd6049b5cc17c54e4d673a46b40db0f32e5433caa2281bf560ab07405d8643a47ec9ace0c162386f1a1c2954f91f6f09724e68ef13fc1bd58047c6feafc9e680d19e14bc3511956f28bf5361a9067cf5d60fac3e7247b601806f90b91af518430fb60a58c7fa8b35a61cacdbc077616f48a90a3f8136201ae097cd7658cac79bde6235280685cc4aabb09e6ab4419e3152ef93c88d7451c6a0770b08d2975b97c90fed50b6a593992e82f339d6433fb4ded3981d5d2101553005bc411e76575d4038ff403db04dbd8c73883ef48cc9a98f4d75ab666af074c0312c433779a1d76ad07fec124c605d033edc4ae875865a723cc55cae3e0d34908b578ff29967ebdd5f60f0eca4d6f8ba769ea062cc1985562d3a93cc52ed4a50152dfdae71be9e9f246d1fe0fe41d0bc42c127d43cb520319bf683422d4d6b70fc15e8349746eeb3899d287a25148aedf7da48c14594bf3a0a98bf0e3d023330680a44621c4d61c5e63241eb0217c0916c0cd2ad15911b647171ea7c2f2734f00f18368a18d5b63bc49a851446f3baae0c2909ac3b9074e0dd0a80502143b0b02 false +check_ring_signature 68c021e6f60410309e8da024648c52ebc7fa96e54e27c3b06a780ba4bd9c6336 fdc291fedcf8e83669af22cd5bb67f9e10599f7e4982d46b7af6400e29a391c6 6 0f26913337ebf6ab61de88b401ea74c2a2b7ff88582a37ff25754f823ffc44cf 38c6ec9c084066d77b2e79aef6f11f81ba42a9dcf6ab35aefeaba65cd8d15012 224d3b2300311fdfa91e0b6a44ac7514a983e4005e79be0a168217e3c8fbedcc 921e567d9762d7a607e2e03243a647a4a1b2a807b0476094464a626f6e228dda c9f98e31bfc453eb57715e00bf043ea1cc9e218659c000f5485150669f4a5fad 3c732a15d13a1da914f5e2e50aa0d5a4f89bf4f1c1b705dcd0b3ff2513c35648 6a857d335f844b7c152d47a9b55f7dc319d530e5f85fe438a049d28c1c37940119c9534d914d68ed0a87a6179e86e29770914a6e60507358318cfc588966d40046068adce8b8ff15dfa79a05e5b1d71d5a7a3c14908589750fe037cf5c045504e651182e3275f0813e96911ca629d9c551f85c737c05c228466c6aa3ff7f5306231071b0314c2857a09f5688d464d34cf543d975921c75e070272305c2a67e0fa4d7e546070e4571c336a5562dbc4abbf7e9e9f8c81b4b3b7a635b3946686d0c3ca911c57d94d10ed17c6ccdafa498c8c8e45fbcf620bd42a942e90e862360015392a9579415b67cd5e0b0b8b4085a971f63dd870a52d6751618806cc63ff143cae107e08c2ab8ee63520ce0ba9dbbf22aabbe0cde049d73acc2b9791b0d1904a1bbe6646cf97159719d621b1a5496758bbf65930667a2a98e8660939d581fc4616a5f69eab18fd1591d1f5b4c1bce435985c5a1b3de7ab04aadade723986a05aff275b4d7c05ad5619927aafb6d1e3d0ad9188e531eff25070200909d69b40f false +check_ring_signature ca540440a189f83f4d1e1005e72f0e6f1e3a4c16c93c3e784cf543fa1cf7f64e 062776e4aca4b5f178ea4bb2b23bab6851627b966bab02327016e13b916502c4 5 05b8252458ecb33f78427a4a72f6585a99f69fedd4c08358df44054e0fb8067d ff52661f160889734eeb4eb96ce39abd811d74495635b3c0eb4969d4585b528b b86084b472f5aec2b25188417da87cbc7a1c9947faabbc200c974b4e9cea12da 9972b5b1e038613305c424784a430e83205a5015d9dd7df0596997ca6ffe8d55 cd0d67c4909e822dd6ccdd56f8b00fbbbadca650b737fc357a6c3adcb2207563 9d834ba4ed192c18cb1f03acf85cc293c092cf37ebf1dec9b3efb3c52102a804c67dd880977ed27777f5861351c7703f66e9c49f3869d187a3494f9d8a1a390530acdfedd68e51ead5cd77f05e2712fd74b793438dc53a59f9c19d299c681106f99e5389757540534acdce483232a27f8397dfa1f1c56a1f94d3390ca2085c0d023c3d456334969914fd9285a416f61ad1b181b95f4f2bce40f85f8d5b5d30058e583e18440c336eaf4ae740a9cac891772f72453783153e29fe0c08ba9aef032d026388e2bf232f363aad7d5877fcb4295bb7b87a0bc8e7835ba03cc282af06cf9eca1c8b2ef8bfae930a9abebd036ef04f992c34a957c83276bffc4730c507cf3ecf8f9881a7fa13ff76af3c5e0c1be2c181cad133d71edf5ce430bac855025da7e8c0ad69c2fbc407b3b5392ffaff96d4d4d00ee6df89fd352498d4f5d906 false +check_ring_signature 62bc407e0c0810e5aa721da31abc5dbbc99ac8326a5485afec144aebb9928104 7f14e1b9571cff0f80741e28d7571d0ec113ad5f3215727ad5362183c08f2083 7 9edb897b18e338a94b14d7c5329e1498ec66257a4b932b08f7657ffbaa173b70 cdb1db1e415b1428b42306cece0a6805ebfeb510969faa0bcc45026b1abc8070 fe4b5ffa248b6b145610bf13808b8adfc41dc04df0cd2661ee5ba521c01be6fc 34e9e0240147efadf6db63f3b968f0c51a60ab398b7f29d498fe4ac1abbbb51a 5a8899c25d2989c5ab5c364cd10816b4f5e87568aca2b452fac1ba83f867e256 3d766090aa05a64d0236e68f01ca6306aff81a28b681caa9f3a52ae3a96f73e0 69f658aa076e62ab0e2efda50b870b93c4a4e01900fd181fd7cd188445665903 1cef132faa409bd3b2d7bdd9f7dba03a7c70fc63b278d834dd4dcb4d85c76d0c942e971206d7fa3b2ab07041d37cf1c53fefb5489538af8e69b73d3bacc6a404b048be2af5da4aac1337f8a3d25f807e9c97215f36f32af415c191f2b8727a070ec4c7c5a875f89ea38124fce015f09622794352022203b027247e24e1afa201765b7db9f110b28bf4f9924d4df8c8f72fb64924c78c0c8be492887bedefdf01f4c28fd6e74b4a911e1b5ee6a6d4e1f7ba65bf6da9f3c2405e2727e952b9c904e30930ea0507b652b8448fda9dab4f3591504b572e88016cb1333639d36d0e0abe33e9cd7bbf7bfbb629a03f2af78b5ac38beb3e838839bcaa6591669240e708a566eccba0c4098026409b7c9ec4157ae26e375ad393d8b3ad2a72573dca2605f16d408bc0c8316c8cfe8873a0180e6611d330502ea0c331305d32a8fd9e550d67e571abc070f1b57be1a89742aea79054d9dcc55aaed63fe90bc7c4c22a7f05e1320e867fc1a046ec21f7a520a4cbb9cb4e6b552bdae8601dc6a8a35efdbb022f3554e4f82db1124f1f690aadee44710a558aa70cc3bdc12e63d445d1342c0d6c5e6ea50ce3bbd4f76ed5ff59b446ac6d82fc12b62e283923d050d85a64a302 false +check_ring_signature ead79869ebc41e3ba969260820a186ca7db2b7fb1855970fbeb65be53db5c45f 5a3d022ecdb4415034470e6f16647e1061991f1cb94bca3121bdf092d4f6e96a 20 ca97f8047905950a0df7d442e7294f1b1ef580de27acdc262e180a3d66d89034 b9bcc2419c0bcaf7a2d36c14aff22aa362b3a1afbc579f1399b1052fccd1cf84 5456129b504aa5d6ef82959b5e49965cafdd398395dd2ef12d95d1a2143bdc90 3002d4f0e9091609e244e43e5357422e7d42858eb9fc34b978e7e168f2d49f18 077074187c571e6f25dff8ec88216f3bb3deb151f403b3b07d40a6edab576d6a af19fcedcf5942bded54bcdd238523af9567ea5160026c26eeef9fdfa8a4daec 95d9825b2e5ab89c068bb28670c3687c3bba1e6b10b40377779a759eb2565b2b 06ba8ccd243f573ea39666390482904209ac99d7e84bc119c0384fcf29b6fbc6 aca811a149a28b0835959626b6b451450873b5a6ac8b5d0a5c1471a69bc135e1 6cfbc9920f3c95ca3093866ab8856e2528d09da76dc93de9c07505b65f1d9b5e 8dd909f395a6fe592ea7fe9d5922d5e326f00a629c136b68729ec805da100210 9a2e2efa79164f048cff0fdbc640256feb83a81aabcee04492c380d2fd3f3396 caf887b8121882a7f3e546ff882cd902e078e0418130b3d5f56be1035496a6cf f83c78294dda6cc14ccc68de10b393e9b1762358ee65490c8d63cfe940ba30a4 882b321a8ae7183d61292b17b0608989e864122872bf6f8626e1ffecae6c7994 1ff6e639ef92871cdb3a33a88b3d2bbb8f384f8a91191c22b8b8bfd97f423779 86d0614aee1d3ac9af417268651fd1c7695dd7e1cec5d95749f9b4597098c208 f8177baa5b2562565cec08578c3887e9fd3e3c1c203bf014ebb87a00f98a7044 f15fb8ee055b3ac7012c017ed2cd19639ab51f7ffd56a179f311f647e42477c9 bdf662e90694e6ed8994c320f31c8dc54299e1899f1d9fc863a7f4b4e120eb5c 7e970786b23792d659340f88d5a52edde928039e9d75299c0a609eed6cfb0f0bc88b07bf08156661f33b33785ac046908043ffc64fc9620ddbaa58dd616be60fb8d4d3b76272a704c747e16e0afad847d52fc5be9d6bede6a380ac2cae319903c8d2d5ece3bb6cbc367aaba0325af3e43f9ef9bb1bd0885ef697537c2e3d410bc314d1eee7f5ae9f08e8144593df3f8935602d2f6a29c6693fd7a246c46b0f09fd9289315e5a9070924a0092b270e859f02fe39fbf725bc6155ccdf08efa990b40fc1ecb5459b24199f4d0c72075e465ed0d0a433699b5ba982b7c4b4fba48014c1d96b9f109e3b3a3e5de43113b74f957cb5839ea51a4bc14241fa62faf7c04653490e642a081eedd65ddd1dd11daa95e23602300dd48ba192d595ed2626a0e566e6a10439f9bdffc0ae041f82cd3c3306e4c0aeefb1b0d3fdf88480bbbb508f8d94166f242e6ec9c59fea61ccc24eed887d4e816c2688585116aff609b29071acde8e81da97b7b8518b240fb6a284443856256611e8a625d878ebab35f7e09b15a3e10a6bb98f8f6947b36b434044030118a75eaf63356e69d1414d8ec640c3d8ed21836cd9caa2dcfd073565b82f293a3b4ec551b7850266ff100adbb350289bd6285146c7bc28093b90e9e3e26be420f7c36f67c71d12e077eee7025af058d39a051fa99ab37ad085f81be14bb2867fa281263291b8d4e628d1bcecbc0087881fad55cf27edea30f13516f06d4f51d6b92414224acf0797f057966e01709c5983a08d87ca3ac55bc181e99055880d5a04852d8fbcdb3e0c95dbd1024da0d2d76c4c977ced6529b6825e398b10671d5bbc0b7631275b1eca6ed48309a0501a2a5daab6c6432eccd70d36abe0b6baaf25df1b2e5ce783c807d172910b6a600c6388a10f112033c4c8a8e1afcc60eefd9e59861a043764f3f61fbd05f1d880fa180d25650a909d99ef8e39af413b712340d29dcb5f762050b2d8be2e3c412064da7891e384149b7eff5141a0f84d9d68c835fc351d60ec39ac8a72b82ae7d07e132a9cc894131d81efa73a2b02fe8b0f61beac3cf6520fc81f7aee28b5c3104660eacf710ee2f820707cd4033ff3a9500d05f0e1daf0209f60467819b931e0be28d118b746689ec01adc82f17edafe5d0796d53a4c4d6798e5cf7db43d9db069b996ea496cb2518187a82f2aa9cb206746bb11d53a51ee0a88694d4826cb2034b389dfe03c886b9306a0e6a9ab6dd3c605699745b77918f7badc80c5f52d30a597e93f84f92896391d8692aebe6c589cb06b31ab4a424a2bc88f4b5c4247103845ee95ee6ca05ff1efa19b32ee07204db9ce0cb38b3208c2895c3dcdbad740dd0bee26f0b04f8b3d55c0fbc1a27d5944017b3bb86a6f90e689a451f7475ba0b33f89a435010eb2698af695d20d9afe99ad267803c3657fab35a0a2d23c5a1010a4a3731837dd8068a48eb466d5f53c1b5d6520074a3285b885586b7bf1e35017c655f30997b5f7967887e89b7b7f68e268d71942c3e6bbfd5ed284bdde176046849dee2440a0288de5cc38945ef88a9f5c6040e9be7a1b0559325a44d9e240904debdbd7361a0445e4fce8ffbd66ae758cb5e0b5272dc6bc9d1a46abf64eb05a7fc6f09dd2747fdb504f03303d9446f9e6320ed0ad20b1b54f0e0e6e74f4a09a3d09f3814254da68886375106d3cd363f0fa2ac4e88daa1ef3126cbfcfb2b0bc819c805a97e0788acfcb6fe8f4f67659738061db10eb19c02bd0f1d6bc7a909461cad8758327a00966ae786e1d78733ab8a71a3a3027a908527d70fe0183d06 false +check_ring_signature c6e47ed63b43dc53ca55ae971e8e8dea69d5119324772de00d4607c504eae0c1 f79c2c0bb07e852d55f600ee72833218b68f477862eb2a6a0e23f78371da51de 16 0f066249ad0efb38cddd3b43123baee9d69eb1b69973f2ca5f71d4dc2fd8c343 c2c79038f2de4d333e57e2200a758b43676ca19b2207ead81391b2e7481716ef d582831e885c9dc842c6bd4007f113e2de8f32db4f85e7459d086492f3012fba 3c8d9e06f73d849f7ed19fe997e4971ce9193e412d35190fc0190d22f470adfb ad7267ef27acb5aaf9455799eae9cb85338a3188088c5fd4fe1275b4e7506165 c181fb2a51571d1512055b6b44b3a9e34857acdd0f3bb6f86991429233e9de2b e9316e84bbc3f8cd24978068c039745df874f337243679aded22491a6f526b26 7095a946b0e2ed28da7095f6f31a1f8d0235cf6710e0a5aac64b56df31543f8b 1eb2900e12096aba6e78fd0e8501b4961593734cd761cf31bddebe5f173db214 c41203610fd964fe36ac40e36e4c681b6f5000070bac520172a15ab272bb4e77 9d9980b2f4e0b5ec4fc8a7668eca914c4430ebd56d5ed5cb3570c3f838cea573 996c14544cfe87a06cfb7ca928d70458e18066898fb4d6300d3cb242db0f4a73 009a79ca228d0ac1d674cd7d44c28c2d6df5c751d70e1028a0e757302cbe849a 304cbd90f4f308527616b7beb29119cff43767c13dd7af8384acdd77e2eccd51 b184f828595e1e02761f2f6e9f798adf945c103b83c8ed4731ef1f91d144ae7c b140fc82f8969e62c90ef2d3830fbbacc465019089b0e9120ca0e638faffbc54 ce3e62d6ead54c612e71c2ba18e893416f2d5def57e536f13c7c5610fc8fd0090268176096775d7ef77ffc61d8784c7a38a44743327c9e24200e9d06cb493f024cb5967e2eea4f5a6111cbd5f9cfecfc3bc717a436964dc3325f5b191d8fbe008993beffa9ad8410e4e7f97a84366072d789eaf93f60e193d7c075d06b5e5f03abc043cc6b7c71c4e3dca1e1981b0fd30fc81a93edf82b46e1e4217efb5be3074afaad31ba487670ea35f80ef8c0498b85b39208d01c1df30f9ccbe492ec040214a07e7dc05ef42ff151da6128d247bd730b8a40b5858afbf1e7aff8c74abb0acd19703cd0b4c71d775f97d2ca285b6302abb5215c3e101e5a898197169166086ad468707d10e3d87102959fe3e04831fded3325ba7dbdcc7f29857c3466a50bdd0e0adabf3734d2cd7cb614e6c6aadd5828739663bede10986781552d589804ca37bdae0bddf8f31f4cb34821bdfa38a5c7945dcf6ae53d704a2639a6099803f59dd324504797c1d32be830a38994c35f002910c509706e8458cdb5cda886083a3955168b667e51672013ce448ee55d431e5ec81642ad5b9c4e3ba03f3b110442ff3c63f72fad053736b3e528209b9bfa8eb19e52d41b0016c3fb8a0662960c648f55b68aa3b25b6f492d52070a5ed1d77223d1fa1978778744f69ce5df9507bcb9624df2006b52517d2cb84c8a273ed7edf580f1dce98688731be966d03b030237cc4fad3289da53d97277ee6fa97b4afef0c7469b6ed3960298997693500a9bd23230db4e62af09ac4047b482267664fa118846bc88f302ef3ed71024b50358b930a6fdddb5ad588c7571ebf35202c8753208f5cca2b80abb9f5698d63e031f76f22baff378039c3c99b0afadf6bbc2bcee3d5d11c8c0374b84e5aedb0a0ff615a79680c00dcee510adf29588a39000964191919b5498af2cbae174708f07e4640bad53d86504b2c934e3fd7f0058034986b9afa5ed7789278a4d861e6b0643de1af2e27f06c4b0425baf356f6b74175676fc67ba0d3734c338b9a3bdc70ce4b695d8250fb2dc426237ad752acc23f58e6170545250c814f82057a236d00e6a34155be6d58708c1502061f9572b258404808fd22d44c1d28648d531657e069fc35ce275c6f689687f292a6efa5ce99ae1f7b98813a174e532463d739ab70627133e3e99fa61698dd2d68ff8bb27b85c2e74493842c5d04d23b5ce4b78ce0b56d8c67e10e68b2f933796bd4497e7c401ce7aa34ba439ca472b55eccfb42a0d916a119eb4326e5c660f7a42cd8d12583efd55b22f51acb97d435abfcb4e060af9bd783e574f2317ab484f6a7a6c07fd3e00433374d40a47c6543e42833b9909f113403311e11367a35b0ea2371ae1443a344d317c7981d109136065441fe80104cc0743bce379b5d665e7bbb0df12a40c514b7de696a8e4ba9606756c7f9506 true +check_ring_signature 83c39e9fbfd0260c8dbaf36d3f5b7ab7da4d4ec5d9ff86d22e64565a094177b2 5dcb1a0ec8d10315d22740ad34d102396735a51319d8f2a16661637925d40250 13 6f4c6869f4c6e3258022698e252152cb8518dd02a9f3557f616ef2e846f78de0 f07e5a4b3f9f649c41197b8a4dcd95abc860ef2bf6d4531258b7ab922d03cd29 a02cf8528f5e397b8dca28a5e4214e4747a1d8c8b7826a240f5aa84a46bbcc49 db39675f2e2d23f253c14c982c062d630a12c481c4f00e72b0467b813061c55b c937901b3a1e97c74769b944485b8ab0e70d56317451b4a341df3b411d39f1bd 05a33a0e9bcfae1723385c301123b45c3efdef422e9a2a57cbf5a2347faa9db7 2084cce9b1bb84e1ee03ab28a20c081e36f1679e663a6aba905982b48fb11d18 6628750473f3d37b66f4caef03f52638e4a6bb7483d77cc0c5cdaa11096a9cdc 9d122dff607bb20ad2b3c7ec27a418ac4396272a3c284fe5f814b0de9243ef8a ea4808a045ad306bc00c1b6288dbbf5a11065172f5da8f85cf4f1649632f6b44 ba2c2165666c378e037ffc3df02b292451930778a003d5ba6c3785f89d81101b 2f7dc4687bad7ac58247d8c1aa73b6f56c65aca395c330f66ccc5d17d6d08b59 e316f31eb058e80759b0964beb0c5f16b1671d00838638fdf8cc6e0c3a7fb7fd c2b5f17bd10b64ef8ff537f91316d99dd574d7b11f5f91b56625dd287c78ca09db78bbafeaabce52983f86fa2dafd739e9d73d32061ad80fc20ae84a741f350e76d73480282daa6e86571abb77d777f30b90b6b52da6a7c748a1451dd93d050cff3394c894e04fc88b8883891ee28d432268c77aac9cd4b72468e20102c56d0389ee9d50a3f64b1970c66816d3b7094150ab00ea4e6f1735caaa513b25b8200221c7f010f79608a013ee1a6eb59656085dd99b5dc739e6a75ee476779952c003627df02954df2d046315fce2346b0c743a40176c759845e923c141aee5c7a70c9508e2bf040a10ed6d2eafdb44df59295e9edfdefced896388538fb11e688207f46c9988784ee8d5f8149bc101eee6b8d0b9e7f7028b1f639858c7af4ff5dd0e17709ff21414d9ad9b4f8706086d535515ee106a208597588066cc390c212d09351dd33ed72b788c14c9062296901506dd39db36772f9a74a17641f580480b093d44512e8f79bc8a9b39914c7307ad81c33e961235a7f9f2271b00eaf15097000da98cb83177b612fae9ce8612cebc5a53272af0129a5f7d3940584dde8ec10ad2b5f76f1945cb68214567e7f73d1a52cbf50e9e5896a9e9302641c5fb7b1204a166a3d77cdd741299fa45f71ee1a99aff5cc21b2e5ff28e769d41b73c04720c7f471380b32d12bdd1bceded139923525cfe634353957948a1d68275dd37e20f2de4fe26a9ca718fbe0e1399225b75224364e6381d636c2ce705ae79d38eed03403f68e716580dd3eea7adf5b427be423ed17f8529a4e4ae25d2b8e077d19b0339be0f44575d7268dab2f55aa8589f85099fa8a432c7331d4126ed6f59201504c046f2b4d79fe3afee6ca5beed82b5af0c0e79b45b8ad053973fa2452a02270075aabcc23a7d6d24e0d6dbe6bd44dd090ced6ae0c9c49181b04164b1f9d3710532d20a7bf7559245946959a469117e3e98d60a20590756d0fa907db674e9110d47b693d0079685be815479d0e011c5047a99365e5c9ce081193afddf5e57c405b2f6052d0b8ebea16cf09be483a7400484e1e058ceec55941fa31977a140900036e0c965b6ad2326be103b274aa889d963e8b6eb70c5794d8de7abff0e85ee00ae3005ee6d58203492bf98bf59a06734ba633ad3ab0b3d01605f18d1efc6c60b true +check_ring_signature d8fc1cc5181976fb09f9f09dad594620987b0f7c2d78d073d8086ddb6659f999 aa137f39fd612052669d4df25c36005fc024ec132cacc5c08b74ed2a3856973d 2 9316f0355ba064fe3d697eb6976191ce19e34dadff2aad01d8ddd69b1a11896b fca8004d98c1009d9752de255c5b844f527060c4d7b1c51dd4202c5dfb299c37 de8293f12bbfbb1e5addb1f2e5c5e52e5d84f9c4ccf5380c91722735fddaff0bec6737e60c5f192d1bf68498874c61032aeb6b425aabd59408ee1f6e9c826c3a038e6dc487e4c76af2c82a05510b3e30ffd8d4e17400ce08f1a5ddeeb511e64fed24199736e56343d565f7b54d099875d9d00596a34001955d7ffb2246d4ff05 false +check_ring_signature 0dad539399d9188e94f0ab654b3ac93ac418625683efd38f28b484e8fe7e240d 84876b2e26bbaf2dad8f8f1fd9a0219999f0e4179831b30b9eb875137decc76c 65 17ea222aa535ed334c9ac2b51543b9de80656bd01d242656934291367a4b2320 047ff4d6afa664a0b50824499db6dbf65f66d69a0c6bb9d925366161f40e3718 3c832247cb8cf93891a5bc2b7ad3bdc0a7bbca47607b18a74f249d6ff5f0579c d1b899b46d0933d1b94da7c127d914f2e0ee3bbe7177f0df1d453b712552dde4 910f7bcda8fa16afcd59d2c1d2fcfee7945e43331b9efecf4d1ec246c0b3ef5d d73f524730e86a043c991ea5047a7f63a9b2469cf858b05cc430a6a102e4c79c a637992ef9426311bc19b7d7fab1650e6068d59b71d160ff69bd9a419c4c121c 9f1946cf6a694320e43d4f9d3f7946d244686f6e23d417e09c34ea71ac0d4d33 59901d492d956900c5c89dcc171908c26d3bcda8bd0284bf067b4dc3e3d453c2 a37b529cb73a4d7dc99e34805f24db95364d29646672902461ca83b620c7a1f7 b8e5aa5b68259fec71b5b9fafd4cef057eaea48bf3cf6fd3b729b168013bc695 83118d85ed6ab7c028e5dd4a1c312035d29489074b96f94e9b79d35d8e9760cd ee85e67158ed9b3f9c41b1f50f5b50896046e94ca756fdce2bfbea2e173cc480 1052f555ca6cd5720cc9de2a87682e1b68043b139dd46c48c8ba54abfcdcb637 2221816967985fef558d15da445878f7710f13d3bdd338ec7c7d49c5aa06dba8 60b179aeb8ec76c34bf58661705c2c4b3b273dc98fbeb9f12915a91da2a962d8 bf257bb1aafb368f3e22bbb485903b40cc46aaf195c0ecf3bce7b00a385f5e11 7b0804714ad21674c5624a002339bad1c0c30c782174a73f8d404dbfbc3d8b9e 7a2c754d84a7ba94f4ca9b013b0008dbf47f5e3cb65acbb9a8a7513c9d1460bc 49a62c26c1dae6d3e2d0c90f12915facf487de4b353fa4db11f228d96e138c62 7fcfca4a10838d1ce24c720f196b3c28121b62c78570bb6da76c58e89965ab38 c897fecd763246d487abd9d55116d6012062a55d891b09063d0f74e76552b132 de94f866b02230bb121940e6a657e260bd51b7d25034e7053c3b5dc6965eff0a 4059baa70e6ffd1aa225594dcd715b4dced9f2b01b3ece9303dce6abc662d867 80b93037c50de8c1d58b1c09c6a0d2087fb34c53e1e21559e87d5838ba6c0d4d ab33118fd34e1cecdd725c8665236b1d6850d4f72aa2ee7cf5c89f8c746302a2 dcdd41542043b1db708138b83b0f7080d0ceafe9dcbdff3e47b86bd8d3498ebe 3cb2decdd80274c71b40a983019c0e9ec1a60123c90eac5ea4b2f978a55f7f60 bbbd99dcdeed42c5a8aa89ac91061a36c8b588901e5fd7345abb748ea5277056 48c651fdbe8510ebb6d217b2d35a3dc3f01d9fb8a52b7c6405bb8ec4ed68204f 5110f35883436d4d78d3c595178e6243acc14e1389488511337dec5f40709ad4 cd674abbfa58c6cb43b6463fd2aa4cab8f2d3a04807aac007b4c1da61a96ea5d 4630da7ef9a95396abc113c84311eac1817fa0df0d5e667bb90679748db935b2 af47db7fc85d889c15b87fcae7649c78c43ac6eae7981337e7d00db40f4928c1 0ca21a466b7f0b87f98ba3203b00b8cbaa7b8a1d20f7055a81f5b52c9a780835 d886cbf8fc392bd334e1a1d109763f1ad754ac89053311d53e506696cf735c9b 287a266bb7f3290c0a6474502500102d150d4abe2e4e6d46c89e05ff480fe3c9 a4cb866611c978f7bfe7ccac2ec7d8436d9f046019ed97770e7a4fe66a45c59a 04bcfc47625938746cded6ac56ece526a4d4f97bab5e83cd143d34a1cc45f9e3 df17a732e1253ca82ef4cee692f18e5ed9c9568d413f68eb944575838378bea4 e1c2ce4b1db039b04412cbf31881e1166f87f3487dd878eeb0420fd34718b754 d251bea1945a28d050d98b56bfc645ccabba2f5aee5752a04ef902d852d9b5ee 656500ba18cbfdaf8015ca3209f87911593b4e06268a3447dfdff470fb1ba2cf a24e8a9ecafcc357d52613403d428864aefa8db68c815a18adbcbff2fa0bfb70 b4fdeefbccaa0308170deb00b788b2f4c3948921f82e02e9a866cdb65d1e9316 7e2b5fc82329c3894f418c90be5d7c2ce35cc78ee8a720fdc3a304b69c67e20d ce8c76d821cea35a8a3e7188eb8c07d5d52479562b22291b33113c59691fa191 1799d774ac05a9b8f859ab1d06a095444470a4afae94e2cd6b6850f13d8e4833 20a5cb769bd5ab3f4a8c80fb2ac38c8d027df2a95b5112622ca67037a7d7f1c4 f7dd4f8cf25a879ff78bd0f8b5e14d6985210c8a14a3739d9301e929f4162065 52a1e0b3b056cdeb654cc9c32198e0ffa78caddd3e66d2aaebef5e00a7dbfdb6 8fb3280d74946fe914fa483dd802a112733cbd338c50e787658500c02163f3d3 c544153ffcdeecf79fb632b156898b02bdb7c35f91587386d209fe9c5b48c689 9b68396952b776370646c32876e221967ecc67e795cde44d6fff6a3b7e9b07a9 239d2f6ba41ad340f187660b870fdc4c95a38360200acfa346de16b3d1634a38 d03cb1a1517e7b8eadc0442fd5234d71c0ba7f4b6023c60952d03cb3403cec75 29baec09a98b0ea53ca61019e255dc193e2ded107a06349bc11a13e208da0d99 56812ecb9c28829ac2ea14560a2f7ce8ae12194bae59ba66a9ab91c700b7f14b 044a3f1f0375ddf154fc4610d0a281013aa1c4a9a255f62e927a2443c1bdbc5c cd82b132b6812fbfab31fec4864fcedd91a4707b5896f62cd701da7fe4f737d8 a7cc5f396950afc9e46cf202c7ce75e23cdfbff8ccb98a0fecc35ec12e8fb527 bacb9d14fb63d4c8fd989cbc08e92028017aeff1f32994970af1c9f5d81e45f2 751c166607205cc24eda3648a0c67633bd851a801b0bd6d6e3ac2e4632c8d1bb 2c25f8945fdaf4ff2b14f355a9423928955b15a562257fef98751b2c9c43aefe 039377e67f4573ac2bc061e34bd9c5c7b84ce610edf2030e1d91108822cf33c7 5bfc04e3ee563cef8bc7527193594f81bdf4832386f37bb977e1eafc651e4f00c0894b27ad4c7aac6a3a38c2f0504d4dd885e8045bd17b9acd1a37cc7028fe0083608d70c513ec8833a2986498aa9ae0e99c2bfb96bb0c49af5e690b324ffc0554cf8371cbec36bafa3e7aea2f39328939777055cd80a798c1b95252791d5c03db58f3e6e9e32e34449f7d8d0ac696e268734308baab62269303520e4b4920065dd556357a28bb67b91abb44a0bf7830a9387e49fa44f12cdfa795b9f3b80f0bb9296e0fcac0372ad1c6cd91dcb7023fa014dd34c4614c191ad7b567bee92908bc3a320bbcc18b0fd78944f2d5229ec1a08dfaf348239a10a6f5aaae7a47cf0697742e07f770f16f8484381a2f18a8f2b6ba67ce1ea84cd2c9bc54e3bb7643096146e0fd26b89d4f0bc2fc487cb90e45aa819b3daed008b02b37af551426020df9be0f86b299e6d0ed5864c3a2bc8f5911ee90cc9b9fb055b144124cbc5a7c09a11ea2f963f89c7f2279c0b8df47776d89677211dd2deaa940c9e8a43b89910b8107e281501de2a977e62b592610c5e796cb3a20d0807077e527c6230c9d1e0e4c99eaccf7f2a4afefa13a190087eac24ac55db55d355609d911a51d1e4d260d9cbf578b0a9b4712000a024b0bc4473da1e15f863e462705302edcff23223f03bdf0f5c3ac762fd611591683596ce43808c2ea4ba3cbc3f58471fe083838500b9733b8769e23288f1ed7eccccfef069a34f5bd446af2535f754c54f35761810289d8e4a9d486582c1dddeebc48165af6308ac237db765deefa4822a85f83c5036274ebf140f4b0a874b82425036a69938118c372416ac681ec1fdebeadc1520ed0410956b31b7c95a11eea6122071c2473c0b3c60e58cba9b119c3ca0910d203d1ca445ea1ee0d52702d7e37c135732825e35afd58e22341c3d842a02f50f804f65c430ee38ee6e1909e52237abdd66fe1f20094adfd04bb27732b01c50d490b5c92842710bd9e10fb1fd2bf6c2e22da3b360165deb893d62e04395f75d94d086767614a4f1aac86c96cc92c473aab8c20241aa89b82531b426f08710dcefe060d9499d84f15ac76f08412ffec75551652455df8e9c50c9346a3c55a240918003deaf4e87e46535718383c186f733104b4e3e4f2e43d19e6d3eeec132fb8e90d120eb611795238d595a7f1afd2386f9d900d94896cd0c9208941943696f3be0231f72023cb075c7b889e4ef9dd7919adf9dc9d23e80c744d47019ed4369cfc00d43db006a6723c452c49cede90c718e8c18b4ee6fbda0fef17bf0d8ca5d5620810f422cbcd5c3e1002468ba273f1ddfbe58a6507dfaa561b62a5436477aaae01f2484969f21d8f1483626d6ca900a6fca282f2195187ffb9c32b81a93652060ac1cb89bce3bbd0c656b113734824f63bffdca78c7a63001c0ca64c4c1e4cfc054120d7c7a5a562c32ea3dd8257979aa919ec438d259349f4ce6ba5c3728bc200365ddcd351619b26b44ff659beb29b610fc66d29c34e00d9a822651296cc1c0e43180f8a7692191e90d19fcdae2f8d30b4224564702b91011e9db550518a8b0691d71ae801cad4d91723c75096de16ef7ba8fc89825d417edac977035fe3eb0e6c6475129f0aa5f5ac8e4a1b4443f6109919acbc989bb9c782950b0231ee3801687bf3232e3b4bdfb164e34bbb1d8fd907a7a0649778ce52e35847c679a3770bf83f22a31b7e1de32612b2aef31a4aa30c8eefd1efdad87059a36f9486cc4905ff5827f6c65fefd30ade29d22f568eb8f97f4d97977977d30582ca9f1e40f70687a8020f9f1db879d2f8e6410c0056e48efe085f33b08aed4dd263aebe892b06736229edc89f3779868d40aa4f997bb3cffe1f2218971707ea56268d137fd40d448f6e95cf0eea018b3f75d8ae9ba3d13942883b50c46b7ac7d1e7c2969d1300eef39133e8d142eeee2c6efef0590eff476249ecddb32d406daf54a48f45840099e8a5341f33457be1737eec261f62884e2fa1bac0d3d468931d7d038b71a100b0cf915fc90f4694a04316887b5013ec5b6224cc9b79680eb5af44fb006e6b0684673091f44645c8668d961001a293f2008f158fe4aa1397d8d1cc0133dd68063fd9878e1d465bcba67edb11276b7b93851ea247953846ebe0292fe0550cd60f1b07ae6cae02745e583bbd7af3a8d8757a84ccdc74b0c92296ed6596c5f236c87297c1b5e099e77d55af880ad91d22d37aa6ebee4ddabf085a0b5927d83f9d0d373bf12436421e1600c03c5318039944685b832aaec8de70fbf960a153ab930aeb794db0e493f818ff5265ce42a22d0680ddd5cce319191499c5a0786cad3b037e8b8eef284f130eb75966fbe818791b7059cf3b1fa82ca6d6e1f80081f6e40fb8d5f339d67593c56abfdd53d2f98356b2970ef61ff362e7d117549b230b240bff30ec17d721df9567d66db1c3e60014420993fb1a5dee16b9179e63dafc3f065df9a2fb46444f74f5212d9fb1e3513a8581fa21f8187593900dc69098de1c0420be4f2e1f828137d1876d8f7c5a7d869d7d43244497be6a7d5a8396c84917008158fed4b537f041a03e623cde15c990be499b6d5feed4d1cac0916b8d3e6f05a87a1db98e731f196264a7400b4cf62bef2d281113a682f4fb1b3ec6f2cd950a55dbc87c0dcdb0fdc0e5b7d0be7f7f3fc50289dafa6e0fc5e5b34dbfee3cb200f67471af5c4eab92bff0f528fe517275e48d34d2b4537ebb33c387120d19d00c75782fc35e28057b0f79c322c661585f6d6c2d899d34606579ab3104c3ae160db77c6bee5f94628e4c7b8bc82ab72911541e110903e3a3e60986895c3c95130017052aca72b9be1b858c22c40cd3c832fc997302aef83b1128727f58512d0d0e49fb278e525aed3a02b758bed95dc313c884e356e479303987dcfa14247d8f00abfcb310528b5d797cd5f8d3a81fc49162ca8fba4dcc94851482f37c47f14104a1c069058b690eea6f913d3e18812a074fbe97cb34823365a8392d9a4ad2710610bdab9e8b5eccb8d65e49fadf48e1284f472d7ce8990d2bfcb73979fce07801cbe492d5a968b08b923a3e461009eae6463d96bf58dd24263b1fb285b31f54059c25a6461ca2d510f3be6b2b8aeee2ba32aaf753250e27d5a50ed6e800d9320414ac3850908e1d89276e7d886423cbc1ff8f4b338486316a1a4e74254befb40467a732c7a5d9209d13196990444df47dee27cd9d15d55bd9c662917522fab1012c1788381e80deb02b024d99b963d594175d7da3c9bdf0926f3ca5821bc3550885eba644ab124aecdab39f35467f86c534581c9d58853b4d25dc44265bc53000bd78e77f3fcb8ac41f4a46e98ed2eca3e210e5b58ec3588f5bded294a87aa30691f42f12ef8eb1c122f5991163320e62ad2b671db6e0026d69bd86a893ff4603b38c399abf012470fc45a80ffb1cfaadde1ef93f99971f484e1a0d6cd45fe100549cbd3a61ce99735ba3cef17d24c349827f2343de1650983f52f78a355fd28ee7e8bb5865a868897af23a42e5a413cddd2a931b8743e5dd384eb27a1d6b5801a38d38323b885b93307fab88af3ee5f51132d533b1cb3767bbbdffc9ba8f54046aa7fdcd327a97321d48fcd9a82fd9187b1287c9cb7521abd9589e468175bf00fd7ff73a679a97f73508413a09860aa4d37a962877bfbfa1e8e5dc06ef62c70cf5a515c09b4d6bded43473127e5245c72a183f499a8f94ec5c2328f89e220906d4595c1d0577981028f03b3acb850b34226b35e46b9c7cb3bf0166aa5002f404d909cf8cd08b459ac4b0a2a642cc48a7a848756484196e322d86e913884eb7058367eef5eac68819c130f1887b3663c0eae0f1fdd933aeb803f7aaf652c97f0f524ac818326f16fdead89406677b028eed1bcaf6f078dbac2665c7115564a20490f1cf74b2b0b9f77a2fcbf6bebe22a475438d99e71a7148edc7fce19235da07297a792cf75862251a2543739b7d85ba4de1b8dbe0c534ab84ac0cdf21f21206faa23cbae0a1404f3213a9fc841410b52e26618c707df004c64d8634ee467303a80bb9074357b23b313d2e7e1cac351f242fc84eb4531ba13e9a74df1fa54102c7ff1fdd4a1e118bd7256c1729d4b92f8ca9b4828c951430040839ab4bf9a8073d840c145b0ab3359e089fb9635db7436f1087c168a63d8019e1e3fa37d9f80b9b1dcdb7ef4221ee02f9d4cd87ddbcb7a138196401c118b9fffe88d62ebf270d458b1e03d2a0e770da0c2f852b9a9e1917a57fbbafdaf718b9afb26ec5a5160ae01309f7a338a587e33a784da3276fa586ea2d66d59f1b719abd75c2c27a64038e28bcd5182aea838010b333bd30e04db56600ec0ffb72051964a558b883d6042f1245f3baf44ad7f8ec98d5a77b07f184076eca0278cc9442c3ffc5e1865e0eb1589486615ab7991337182d14127d662fce92ac0d71905b2a457d38911b160f4f6988bbe13d49d9ce4d35ed8f1af2ca06b00170ed1e7a65ff9aed4cbcf13701abe08b8de1229165af7c86299b17ed8f6c7365b1aab807bb6a2e3e5e286a620783139bd49f8d108dd67c71bc09ab17cec0c12d71c8c513d07a45b817c85161032a5799f2cf27ddb5c60c0ae4c6c00ba56855a23368bdc0b4c208a0c048772d0835cc2de92e15a16625c219aa91253cd16e26e7194532f3df0b5eb610044f470d7f2bebec40482f6da3703a0910f5ca72f6bb5437d4496bfa4429e77040abe7033c7783fe44928907180395e021b5cb5f436083e867357c1629c4b2fc41c9780a4a2a7a6c6461f6ae84a45bfc5944c5c6653cd9add8e1bde9f8c4cfa1831635019cb372136725a4057e3211bae31e9ded5f391dd1b047b9e828850449937fbc05d305242c50eda507aa5989e005d380da1eb8ee0031f052e2d4dac657b7563902626cf56b80927b9936446b96a87acd3582b4b16b739c63e65324b0f251e5910d1f0ab328fc64f22ed32fee164193b1a1e2a0977559755db9df67668b4f76200e7add0b03bb1fa9e1b5c30ebd3867894f0f3eddf35ae7cab2ac8f5db746f2710d6a1f97184506489671dc493a3e566d26c90e6e29cf95204ccba25175a0ad51051ae8c65a726212a3e0d0cd652f366e84f03deb78f02572e41655ef373555bf0aab69afe10e580a4684fbbc3c3090cac4b5854b00ec4c42af5f5d518be09f4304ee24e9fa9eedf8b3ce20f038bc87be9bf13065f59d52a19a6dff3d13883f190c199c0252320d1a43dce2849ce8c9c994deadda9dd42534e8b58806e05e8a650b9343f0b3a654c1ab5bbd9497082fa27143eb284d7770282539dcd7eb8761580406dbeb47e0448a6156f3bc2b3bb94460cd030c7dcb739acda965826bf8212605914570044c8a99bc8fd9895715556c40ba3a19ecb5bcd34ab2ac57f4ff81ba0503fe22dc19ae5ffb17600c615cf81d53e5bba97de52b8979665c73920a37270c8925332cd0256ed4e9315d2ffb45809242d79b144d3fd19b153f6ce0b455230d9c96d5c8ac2aa19a9cf4aa86198c3be04e95ab185edafca87e637c1e1267c70be141a9eed4b8e0463883a4a46dc70b73aad4da4c556e07857ba4cdc4abb3c50b63b821a7c7ec05f55979c66d5ee1c402751aa27c8fa37f7343e8b3697a9c3908f5d96106f408a373051e92051eab0fb36b8607e3fd88b725ce8f6bd02d198c081e177433ce745fd9c8cf4c7bd31708c8e7cfe74b7a2e0184577c2288a05e030bb44d9ddd841b0f520d385376714de60ccb15bd0c5023b3fa84fe055a1cfcb00ae8290071561c0022ec828bf331a45e479b5e8a21f0621a731b39e38be4f5f00b80e18d94ae80c5a30909f5c36c6d47cc94b76963aac2ac1312b9f4794fb16f01 false +check_ring_signature a3fbeea6ddd39f887dd4f85e4b9a136c1f34f6c7636a8a2b260b03d6a24f3683 efbc8cd9595f9da23a8bffbacf87e0ab912ebea7f681c0c2aad21aa2a62a795a 2 60c138ada4dcb82ce3d9620a5b2e5425e9ee28b722b2a86988bf7a6a6509ee71 97198304be450104d0ac956805502afc0db68728473998ecce81279ca67a985e abe066b4a901cf486389a1099583840d6b79d6996de113489a03fbb5cf11cf01514d67dc34e7ed2261d195d2e789979701a3b227cdf61546702166be7e47210af20679022c58bff6f99bdfba15e030d2213f33bcd0607e3642fa92cf201d450d09cf43c8b0a46c5d312a867612976961ee4f5f780682c57db7e3c3112ff82d05 false +check_ring_signature fc44293454392b2a3c866275dc3bbeb20250cceb07163d265378441908eb2e1a f6229d0b949c2af6bb3794e0f4fd7fe93804c1e13d320e2e74de6577d986997a 2 93e1b3b1e93ec8bcba339441c6c96d76da1c463030c5332e5891eeaef2f8b1b9 29aa9ccc2829b1f4045e71e1a88a9150010277389616acc5dab329a7c0c2109c 41f86271a56fef893fdcbc02e597c53f52632739c06a6c97f362ae47a9f7160ee20fdeb02cc112339226b8e0a230f8fe7bb9df827f3fbb587ec57c3a9b9b2174e42c6de988f6dca4469aeeaea251b8f1d3219fd398ca7bc487c7eb1b4e12f304ac433a0d72b9e0b2249298da617afb5a80c605ffd2dfd80d544357e620e4c50d false +check_ring_signature baab21e439ad96302738d847140675cee5f66a5368144113b06d80834d1c7b3a 59765a4a47dd7553033a8f31d9f0d7413ad8d04b1f58203df17aaa8f2ed29190 13 c56f600e57428fe08422efe5f7acf99b7bcc15b311e40e180f1fd51997f29065 6d2294fef3f3cfff1d8687a584084c48ba7543c33f89d6aef17a9dd5980d2043 c1bc37ec97ba74db93b2dffa547d080298c6033d1c5291c05adcb9e494ebd596 de092c1f8630bac9488ed258ad38b730ee7c2ae84c202827da8e5b9d3ad00d60 3a81621fa77c8182b5ad3e8e1f871f60827af5cae8e87ee4a53632ddfaa461ac 51185ff2caab551f2c8777c5a9d454a0faa253061d02ce20982fadbb9bac38c5 60d295c97e0cd75ad29db44f82e3fc4d2e4878d091b75218336801a07844601a 9b2708557662a3cfa9833314c32c92689381325f5ba16f477a53e555766261e7 28aa664a24c2488888f7b1072188263f29d8cdba362cd71c794cfea9d9976b7d ad0b8ce38ab1c7b547d7f491a6e761a4152f065b63aceca0ba59ec4f7317716a 81f545383b38427df04dd29d2498c4c0c8fc47c69e2c7ea6c51e4efcf1fd07c2 14495b0bc72de9b6cbf1d88153b5c6714c0f66b9c1d786bc88cc49e8bc1edc7c c71872318647b35e2946ece89166d4a8636768c475d3a4c499e897b4b4e63c55 b1ed49782f82bff360d08e31ccbca8f55b73f13ffe4b04e31ffb05908f18f4057b8e7f3974d4b12f2ae27872e9f63d1469e09108da7d05d044aba9ec1d8b960eb2ad5e5c895838464951e56704f0d75eeffe34ecd1fe6dd60597b28045d69209c4e3e7e2768a4a6d1874150943b65623d14751b50bea1639fcf186a61c30d70bcde6fd927e47d74b45de7584153bf47398cb6138b9a2e5f580cd1de423b6730eee23ffe360fe02cd4cb0ac8b8fc2d84d335232dcd02aba92ee9fb35de8814903b909dd49fe1ed90aa2f7c1c8f99278e375fdfb21831e8a68a53007455bd43f0dbc5c446ab107b2f24bc1b10b22f83a6ac61251b71f3fdc3699736ecb4a717d01056da2ffdadd096ad073eac982000311c51680975f34fb547683e3236c55ed0d9f7ad7ffd3c4aab6ab64e11e8cebbe40bdd68ba618d110154f3cc7cc785e0604cb5a798cc34ea561e72013e0a2311196b1d1fd20eead56d47627efaf85ef870673cb90177187d2971fe55a1d65160f9cb42bfe4979aea0e634e5dddd8b3be9069bd2d28bca7152c50963fd4064f40d7132da9baa9380d00ff78d7e3fb501ea0fc60e9fa8d03f666803550d9e0af9bbf09ab9adb3cf998c36a413233b1a7952021ec930be4644053277d17967085c3b0c8411ce7da0f67054eae331e756c7f702dd35bf6f0ec8202206b32258818355fd01fb948516dd4453932dcea87890c5029374def5a9c64d94962405d6a7545ceb7b91801e5c8f616cda65d0634ed64e00a356aa025a502867948cb302558e76972f3d74deb820477a65a7907656e36d0e31b677eef74d9a7c1073a3963a6893b10839d2c0dd8b4458d7f71c3d1a5f8302201723510fb917e7f32729e00bf0cda0aec176d5fcd48eb804905758d57b5b0f76535afbda728d599945c4d4b4ea233ce94c2145290f3249a8e45f4699d6820696e04767f0b7884d27f2d46992eb24a9b7837fb3d53218be5502b3bd9d9e0f014236c184ee036d0dd3c50cea63b5412195d0be10cb7435bbbb023dbee2a9a6048a145b7d3ff8f7d3a81c4905b854ae48702f7fbd940467025cb3d364c33628094c85f4fbd8e80d1f5a23a0bb9f15fe12d2b10a93538550c4c324652c020495decf468ca6c26e8bc43af2a27d49f0b1b4a3ac3e7fe3fac7e4167dad65ddb78602 false +check_ring_signature 928a0ba059d058071c43dc8c77287bb8c9d2b3f61929b1a9ded0f2baf8d27172 13c1a74e17970f85b14a65cee33112f2748f7e77d296c6282a4abafeab1c1e48 25 efbbd264518e81f9b9bf575e78d795705ae690a95282ba80f1a051838c60087a 0931290ec32616fc48e8de02392b557ee8ed24b4e59fb22f774167bce9356284 10550a3f1432a24892d9ee8c167e1bc5a1c322e227b46ab376d09ff1731ad61e 9e64e4f3fa6208a1f0aca62d19bcae0294e955a1fd72449dd8a24639e5ec1bfa 3844826125592c894e9700d31faf21b325aff219c98c960776d8a6a2c57160c4 b8633bd420c69722f99fac0fcb23b68ee85e14d2eee2f8090c874c479666a6cb 8729a19544c832b9671d4388151bf347d06d642019d08c75cabe765643c6378b 0ea93af65d94bd8b5317d05303d3812150a4262b59f0261912d7228b2a644bcf e5f47b393d6f7a5536a6748ee986767c2d54adf631a4082018de152c80e5c662 051d40a8f0ebde5a042a3aec55ded4e2b95b0a7ba3843d4a10abe777b914279d d44c44819256371e8ef6eacb7ece1c120da34134a514efd08c3e363090abd6f2 a909b25d9d0fcd52b698044c5254f7fb09e2bbfe24fb12998f682e4a4938f53e 371f44903dd1c78afb4f93981fe9faaad2dff38ef7228d7d5f85ac20ee71a5e3 f0cf58d189595201f509208ce2ff07a63704ff6c97fbce30f431833b2816cb0d f833325fa4aa4a3e88c2d63f290389c465713482fc47f54a5e9b141a7d5cda3e 5bb4c52864c6e61b21f2b1416ceae75371f84f11e540b770304e579224fb5a0d baf5b0de15ddc099e2b17841ba40d752f9d69fb74d3ca28add269687a7d48088 57226b015d1cd8276b8d0a283522742312af6d1ded3d4b5a4b1ed13694f52452 43918e61692e7218ad49e6cf5f4febd3375e354989b8ede3debe36d703d742d9 6a3e71fbd91482ab94ee74c27297a195a492edfd1b89df5be6d30591a95b8660 29157f1e270c5bbf42c52aa9959706a739c17559961a76d16bc7e139db2fe79a 5eecafcd04c7c41794732e50aaf7cfd7916ca1634f0b55bce4a5ae920cb08dc2 a4850dc0481105bbe46a059b28a03daf509b5dd22040d5829b9831500a76b112 047dbe9633b90e7928be37b311626f1a58c6537aba57b1d44a8f74062f3abd92 617758e09ae18ce59a5505575e280f420b5fa5368f1f863996b9e88f3577065a 174758af951653ceefd7432db72ad242b5482cb1a8e411aead3be3f98da03a00580fcb84ea09a77620388c8cadd7520897d67394cc42102cc659153ec4575d0519cf6e63e9549c9ac5119edab11aefcdbbb1f99f71ace4699f27f416f9b716048d9feac32ae9de23726f85ff1c68d05017f91b3657003afc1536a0c14d9ee8052697a5f837c4a865fcb984f557f33ead7888546cb62053dac4b63118a8b0fd00968b3084591f4476d32a1f631df179b28b7412fffad918685301ddcc00775c071462d440011c477a758ab114015e9bda7ba27ab000ec8689cb4e39b5c2b5bc0c28715493066ca18807e1e2703b8ac50deacfc08320af39a045764906406f7e021a7c4be2d0bb0eaa5452b902d4621fa74cd0aaaa6c774863f21251df812c8804e3e9a5774e3f233b09d2758d22b027b2fbceb3cc6d6503f54874eedd56a59f060fe41fad8c19ce4bbf75cd32887393a3fbf633885b4776007dfe19da5f79390a57bdc9a95be9130dad3b4a123053414206fc4edf1f28322b525797744eefff0146143c8c6fd40873b48c9abf80834d7ec3168f1d294ecfae0b016b3701e8d408a2487117592cc1dc9c3361710c21e036c3fef744fe7a1f9cd37cde12c2e77f03ab88fc045bd6bf49212dd5c27be30137be06820c037fdcdae0300f164ecfd70d3c860e62e3085a4a92f6d672a51d47aca60c0dcdc73abf3640e7dd8fef624508cd786ae4fd7233eebcb84547cbb4e38b0872e400cb9d024430e6f473cdf6bd041d9b38d0ac224699f4e7c24367698ec6c1d1172485c8fb07c46d9512bcd0800379dae366c723e5c4bff9e707d8d11c98a12b55a77c947d91800466773f41340384f61e805a4363336b91fe7b8799455809100378c26979146deef864ebd3c00e55943c32a76b0a61a70dacb5d413c03a0d20d0b5676a1e5ca99a8a349d8dba0cf6f620dadd43149081d1b76d60c53c482ff01e240ed4ac6ba92b03bd22e15802685df2a6fbc0463c97462752bebfd361b3e47b680ebffefb734844ef6e373f05e5fb0f17cc6a39db6e46c2871d5481329034ac86f373c40035c0d5103aed380788e5a79c3488add887f9a83e4055d5b4c00d7180bd366c41877d3283b5b60b0da6b42bfab983750171ea702e631ed8eefa495e573828523c4d7c6b156a606a03641728b462d07f710d055189b33e77d36949a9d954584b57d82b3568497adc004461307d4a033ed8ed23ebe574d560d089c139458be529bd89c2ff7882adaf0dec6430d36ef1774e0a98e4a1cc3f7aca6da8657d55d8bcb59c893cd891a7d90870f35c2c716a2576ab3091a12b9c9ed23492c2c3128a6d0fd605ed0e1eda8d0eea85a2b52d4c4834bce8f43d03fb783b46a5e679badd5fde0f59b458f4477e0c34e06f1e734749f18ca2db2aeb05db6631b51f9ce0749bc421cc957f22b63d0d35699479ab46f8de41a0efaa9f1cfa8b5fb3275d31e3549c7156fce158c90902d0c90017b2e250c04830b34d27105cf28010d0df33305af8970fc3ac0f0a100122e71ef101f11a974a5d06205b944267c525930f49967b7c7bd1254f0bc5e6074b07c37c579d769592d4bc55a8df9a09ed62adc804401973c86870eb58e06a0405cd29b3da748b7cf927dd5bec75e564105f22c2d2518f1795149fd4d4a5660b7ea3be0ec73fdd040b9d01dba4d08283b90e699e71c8610d617b34e81da39d08d93f45addfe71f25a794a936ee808839f078b4313cc0f1121c565d0d936b62091ced8ee730b051768cfb310edc28280beceb5183b8073e3025579115d37a1c0053398b4a6dfde7d7638b92f543d1c528ce9b79fc9f0126d829d9cdc7626cbf0ffb6f142770749cdbe8610145de901dddc9a2410187bc4ddd5792de39dffc970d988346ec835569111a83cfe204c7ea9b0ef3e43a0b043306fc2e1d0d56259609f3be85f0587271a19b6eba6910fdd7ed3395cc5ab7b4bf20046818b7150d660913dd99fa612526c9f242dc1c033ef8aadc29b4d251a889760cfc7123c72a900da88a56ee809238865b0628fa536adfd2deb40ad58794c2a6f4123e57b5550f04e04202e0f0d86cc2f974d11555562a48895ae6620ddab06f57721c5a68449909b8bc37655f984002a6108801b76b76075780a11eb3fc0744d8ab921f2ab57b01059febcb4654ac3f938ce2a782e5bfef7cfc0c65b782f994a64856cde8706b0bf6d4b5c149986c523ef058d0add26b2ede6057b5f8bcf9daf7162313ac00060a false +check_ring_signature d35f70ead9f6401ed52cca15b0e592f76fd60c21b3a1afcd9d4fabd5b32787e3 968db243abe50d931a73336f0a4562f581ce363f489e5f26e1caf9aeaab1e5c6 1 40b51c2cd49a49d5ee922fad35e353db6b6aef0ee7f261192e9d2d886c810fbe ab792cdf66402db377aa160fa5f1f4618e58f41f2b60119a8c05e1b9a89220088d7a6ecafacfdb22935937c2e8165f83d9f9d5085eacf5a23523a9e1a32a3a93 false +check_ring_signature 41c9b0fca543123a7c7cf42b553131b8c63a2508dd7eddfa55b8732dc8fb425b d16cbb64b262e3c879950e0e7581056f87a526eceeec553a36d0416357ef6cbe 1 5107c3859c7d28d9820002029c626fdf75be81e7313b2be7b26478d620d78172 b0e9ca7e5d65d8df5d612c9bc97be213b223f4bf1470ff2260dabf030846d60f28f0ec2213066ac9cb43c49333b76d3ea1fa182ce59733ff8ec3e08807d30609 true +check_ring_signature b9d7cbd61ffe8b0802372c621c6f906679f6542fdbc078ac37aa431a97901920 ea3ede80b2c0b385d60003c90237d6aa39942b5c6de84ffc343ca0e55db46c4a 2 70464e5c336876bd89b9c7789ed1ffb0e6ea5f25a7570c4fb73a04180e67b832 77749e9ea32913aab56764e04101311bbceaf397b9f9442873cb8cda374d9564 8c249521b635d95d7c35d428e92a5e8195a1b53d32c7c1ff655e9e74deba3d0b7129f6b2e581c6fcef2e6ee2310284d47eb160bcf3d7d3ef4d4796ef63f4f70ea02f9b9a62aa1fb897c8ae5e944c98c4d074a89b0a2385347dff2f04508ab50c94cec7fb7331cb3d9ac44544fb1a16d964eeb07178746b6dbb7fd22334bc8b07 false +check_ring_signature d6dc9f3169a69d676b701a1b19f120f00c7a6a4cc72a92147136be8b857669ed bcc5efab92ca34274608b911ca7465ffc36a7a7907ea66efda178421b3403f9c 9 b853b46c6a4ed8ac94aa07a967d0d72ee0307f6356a53669ad823bd2bdbaa174 7401a561baed518df5b160e8e196ec083f7411e70598cbdf5895d8481b21b329 ac74a94124c4291d8a7dad9940cb4e06027d4f7572fbdc50cc41b83770b78c4d c6429e9f0c813faa6b537e621226fb9187323bf2330feb4e794d64ed576171ef 633b963826ee7ba485f2eed73ff79db6cb298994de5161c250d0a4b8cfc7dbb1 2f76bd918e1b3fdb47c96fa750a01831ac6654ce4797adbbe76f56ecc1af1e4d 30c0fd603520f291ae0b3c577d5e9bd510ad4140797e1a24c60ef463f3366eea e747f1448d45a4dab286dcf216d0b08060340d31a1fff576ceea7b040ffc3673 7d0d5d49f41714260196ae2197d4e2c1e1b04392fbee5f215d82d5fc6b7ea4f6 765df3db7abdd92b96f638e30a3e2be825a794caf29be90dba48c298d0a6ae0f892886f585f28082e416463ff1a3db54764e2469263b67ea0e538e944f625b0b8d8b84c54f416228ee7005588c5e1531a1ea71a7d0d8bdf661bb64d734941a04c2d0d1669ecc1739a66853f345d9347853ab8b4276fc3234ae71b1ba8e655b80ff21c9cca190abfb1295032ce830918c75c73ce204d3aa4854b0b0fe4b30130537a0e9b8235e9d04ef76eabf506273578c1249eec94545bce371b97f45861007d3471c382c4fc5e2995e277f4ecb61b84f5770bf284ef9c7a1bc7c649248650441f44a8d33fc4172ead3bf4f2c70f18e4fe458583383f6f9b50c8825637bbf0253d1c04fc6239d95e2086d91b87255d50edf290a70f97d3f47ca2c9563e08e08712df52fbcedf00e0b09c74bd973248db3d455c90b7904027cc6e39ee8ec38048714b4fb9bf2ae163ce9ff223fb0553f3aec115b52302b6120ca5d670418a7028bbb965b0c78104c7b17fe131e2126f02e24ae2fbe3dbf87d2266d414aedac00cb05658996d1c34328793494fb321b93f9603eb4d611a6990ecc0904dc6e5f044e71d4a3815148c566a2448942607a7a00f939c96ce1dfe3ca07093f6f227f004a32b61d6e711b4dd582326a2455fb8ca4eca4f0ac0fa00d3bdceaa85b58860a6d4241f087ab337556c021f47e9d3054348cf0931cba4a9906e8a35e4bd7d136962b77e68ae7a2e33224eb394f7d4a303e8ce4ca17b984a1352b08057ab12208dbe0e858d02353195e1e9dfdb2d596f9ce7494c3a78a8ef894ff628059bf7e09 false +check_ring_signature e2bb12941812190821b1be40ae40d0419ff88ddc4d037462f4d12f3d4b3aca61 0e768a66bd41016b09e168612adc91cd2e7456837eddfc9fbe76f79e1043c036 70 9a274ad48a52afb3fb0cd4284f930e7c7089d6b1421482d0cc3edfa2d20a48da c38e9f87c531453b17d684573e1a2b209bc3acd931a161d38554f41a64934d2e 8f126b741e509cfecc7f1253622aaa6597cc148dac69aa3ad4766868105b326c b756f2344770475f916d38927ed64ef2a3ea5e41dca483331db364d938fb26e6 30bcb307eb2cf6435b6107c538b0fea7cad848859587c7545c9b413f0d0b4e59 82546c53e54d481f2eb8ecf085fb42fe64fb4f14c9ca26958a15943f47b7af7a aae91d29f232623bbd5ad33604916325fc19e5b297806c6871f8e22b9a70e31e f6f562cb96ba5fd33d02f4cf13f7d96d39ace6846e259d9c54cb2e95782c37bb 12bf2f6cad057010bb0b677daa54c76bcb445d7759bcb42825db84cd9e7b2051 894d95576348e8410187b359c26baf04661f1c07d920cdd38d54f1a3b18b241e b95119fb5e554344445959ec5a93feb3a46aeb8737a40a635176835ef7bdd91b 457f6f84493dcd3b500790f78d834a4e25e9ca91a69c2f1f03159e7c26aa9a8f 0d1e5b8edae8df768d24dbf9c509610cbc9724207be791ed739e36ef5131cd0a 913c9dc69a891a5f83e9e053127b9f9195d591472eda96fd6fb010dc2bf65256 40df427dec84949397f6835b82861be7449e4b743b599f6d1915fce15b0edccc faaa6c8fcdfab740dba6e65bbbe4e9633505d472181afdad4bcfdb5b1df6a978 e6b9c007aa8f61f7a6aceee180db77e779e2a9703a2d086381a29d6cf0d0c0f0 f1b98484512bb2ef0def4d0a7c62e80437f697461e3c79492544cebdbaf2ec0f 918906ba774cfd27d116123f6c76282ec92b5cb1c2efa48fd3de05fa89e111d0 7eb016f928e0cee1fd5d556553b40c877854d85fa8e322656890fe1c0f35a3bf e14f7e737b4789ea0237f7fc639b1eaf0269ba45154cbe1453cb4c8215e4cb55 e1dbf26ce2c0e563de9605182cfbc0debfaccfc88403a4333f1409c1b93df8cc 829b754b25d36aa4229f295799e7c7fd40e37a56894b443cb2498b78d86a4c78 e9146eed2ab22d86a6fda4f31660ac6de128f7150e84bf9d0a8bae689c8ba107 d32b6d97f64fabe5a68196e0fdf406d28f886052d2dde6412ca000df56b946ee 2bcf8c50a163235cd4cdf37a6f7b6e82922a112606198306b78fd60c936364b2 6278611367a12f1bb70f2ebaea010d03af6cb0a22270c11a13aa14303e370fdd 1f6f8741d53fb47b80cea0750f005339b11995cb286d097409e85647c6bed88f e30fe97d0cfdf2c517956b63431bc9b55f2c614557b1373fd7d5472569c2a1df c9cde2996eedbef35afaeb2b18716954eed1ee1f2640da75592298c17cfb5803 949becadda2a99c359130ca1a6517552da15e23cb0fe430780b23c84e01c8b87 e77aac9243e4c7674716ba1dc21047cbfb0e37b034423390768d9cc0b608d4d7 dfc2eaaeae8723336c8c80ec90074ec8f3aeaf8a5e38c9ce18b00af74299d867 c9e6649ff6f94d9149b1bdef1f6e66e34a234eca21eada30ea4f610b0dbde829 b42deb0e923c2e37d6f1aecae1ca7dcb5600ba47de2a35f9f830a517097fdbbb d2cfb2cabb9c296f6c2d22dd9095b8dbe698552c3c17816a2e06bfe78143883d dea2d5d41294ad4e2b8c9fa2738b1aa91d29d742f2fb3cff5adf8f55197f2956 6390925c08723a0903ac7036cc6b12156dff43addd6115605677252372352f37 9a5dfed4f632bbcfa4b041c29c8b8c46e60c37d45bbce75d66bcd46abc615731 98419fee8883b09e9784438ffaf64349658cf109daf4f6cdc8c9f8376042b907 488be1de025f7d18e779ec6d16fde443241be35d408d81a41c5faf9b103c0fbc f42df7f35b9466fef10348533968ddbd03d38d06f87c77cf1f8f00d5349c097c d721e3c72670844e292885fdea81e1ab8dc2f885b4b02cbbec2c118125d14772 2766edb557c8818ebe7c2d8b5c3163c2f7abdce22b02b32a1cf1322f1c163b68 ac812afa673dc5bd251a62c5c1673d33461fec6d1ed4bdeb5b3f01fad19e7998 062329468c71254e52cf95f4a45c0e6238330623641214286082ad8bef94e65b ca33f9f4fe8d6606529572e1cc8469f4555d2fb56c714d9e18652cf666ebc378 664d16d1135a0b2053b6ba55f10be8c47ec768758fa9d50b9083095d00a94b95 c1c872a827f7198915dc37e7cc1ca797d4631209788456437e5008e874ab9d96 953a3c08bc40e2d69727b23bd83e64ac8e8cafee94b407ac37e595c76e04c9f6 aac268512cd073c4579f728fadf8051789d08d32e61c761300648190c72c29a4 be597a8159b1f2d6c875a5e6269df82142a9e0b8da1e3574c9ffa13195b6d267 449bbc2f1c88f07d807dffcd2ecee7805c3c8ba34a0f10f8400084c391c16f60 560e7599bf14be88a7457c1ef402c78056fd482b2f37cc6c09b6b4ffcdfcc7ed f48159111a00aad02b09f10ddc7e1b407f794fe9431bd4a99cc714cad7a316f7 4d79188d626729ddc2b621c47634ed426ff4759149feb9edeb92a0e06ec774a2 6b0d14c61723d7092713f3fdd81db765ce584acd1e360e28de1534b90ab365aa 17691b09c1686e7e2928cd2bd6238c9e0e50ebd74df01be1a470e0ba97e07c4d 55754367769f2c03fe87c9b945ba689461a6dabe6f6c26b5b981bec68f848a0a 9b60f93007b5e8c2cdfb45929356d1d7395cd6bf6b4845fcdc9cf590aaada4d6 113eb033af67b2ea990237475d104c1d191262b83801b66d38216a3288565c25 25d37cec93e35271b73ab45d80b1666589e89f28fbba8d9316e4238d41931778 bcedcd2774b387d12c128e17a282bc1902f8eae2f63c2a6e2482570046861b34 937ce095fe9fe5866f44957703fa53e1be9cb5dd6098c8b1581b69944bca94ea bbf0262a967a6b48deeb7537a92f83cb69266781f6d7c7fa4e94e5de5140ed20 566d25df477722151d17d23fb84042d82798f2fe1dc1690dd120fda1ae6f0fd3 563b8693f28def06a7bd277c6ab5a2cf482e1b4054b28bd0ffcee1c43a39c05f 91b1ddb6f409730910a03ccfa8db7145916382604af5c73b340e9179547d5798 bf9fea2a8ef4434519535a62cafaef6682005c559efc06c996c17ee7cd8e17b2 668c43da4c161d92db149f8fb72b89f49d902b5cf00593c1078505b9eef67acd a6840bb10a4bc2c713d691e5b8143ce8771c38e7df650c442af2ac22b089a30772bd9eeed61e229e007582d951c58ee331d7c20a67153569661f271c6c91950d0e473f5702d1eef50e167a0f486093359b5a9f8896d98c53a1635ee77e506a09534f43c657144715b69c454515261e9cb5a9df7f91cc64965cade3ce8a205f0c303362446276c089bb9cffebcb1d293356eeda568e3e3ab0f6ecdc54d9af36096ab06a96913fa2f679a1cee932a264e96fbeab4d7c0ff692d026eb7f4df18a03fd9fd655f4b65d19a91c73bd70389a2b9cf69fdc68a440e6eb6ed09ffae2e20ddf9a539e9138c88212e2598e587418ac98040d1b7d351ac56e5a11ed7a73f901c000b4096e79097d83d92d6219c8380d06f30424db58e49efe6a783d6f65ad07061366c8f266733c260cf2fc00efd0d2a668d004ec7c815420955a19d06a5501d6fc748e1fafdfba0cdb2a996d9e857e0fcc6ad105e7a3380b7ef27f7d33dc0f4e4ebad4290289e806288143f13e3d46d71b1377eaad955b1f25a9b6847458098c920be6d5f7ffb00fee31c53733a8b6047adb378ff9c33f101fa684ecedad0d909a072478154dad044c835abb21d740407dc456fc2a2c2ce54fb388e8b41506960e8dc81be58df23e97fb425e82ab5a8cf4bb2aada7b6c0d465d941f009600977e1cb73010a05f9f19d285633c58447590ac04cc2ba86762074815c382548081c4bb322c4e7e3d0cd411b7edf7b6e098055c52e9d653e7575958fedc1590c088e45bcc28d8f3acc2a3deebe4fd7ef61c4bd5899c2c3dec6f18261cf51efb501f39b157a41434970e4968eb016032b68bc9514345cc52cbbe7ee22ca31d4f30d5ebaf44df6d759c706453ad0c8ccc512e5de375250a84de7f48884bf78e6d80a1c713ba0e9668646e1b0eecb4aed287df47c41da5962387943d2bc8c32bab4084ab8454185cf7f6defd94165b89dbb4f24772a0bd2ae97f944cb7db162aa89003819c7738078e7b8cb7586a142381a0d3555b21cdc3897957cc7cd24d1479f0b1ab01c2123aaa0158593da64ee0cf28cbba2f65c7bad454bf0caa328bf5937012504cc075bbc0171fada07d6295973e44edb89004e5a187e151d17f96872fb08181536270e1bf1b45929641a0c783805ce3a1c4c233cca1d44112738f1ce0507f8677d55db21103dfc618c7c6dfe1800580fe73071baa5802080fd1c7fa532080e7ef5f820201c31ecb4832cd4c0e9a56c5ee09987ab675abb155beff3f4a601528a544a889b7514fb17ddfae5ab25e693077ce08209c7384c655052a93e5f01f9d8d7095a230df5fccbee73e78c90998a348c00dd10c4f23df2acf2e77f670f4359234b526eb2ede5e49f15d339fc81720c69dd8d7112d35829efa0abcf4b01969159ce636b60befdef5cd103dddcbc517a45e21d55b3a19f97170a80db8e07f62bb18f8010265169439497656aa02a79c5b1b2a1c98183665b5c607c430a0977e5c138c282a8d464959ea6a9a1fc358b9d11215c50a06394aea18fb300ba01d483f103ba2396f63803c5e6e12888edfecf057ee39ee3d52dbe8f2fd8603e0088676fc3ea022f225fd0fe5769def5345ee70b265c631d9abffbdb6d7883220fdd3ba5dfbb421fd68040c08193887ada356cb2e6e9aa4f3f77e485fe7a0d7c080752f123bf785a1e5ad8a58a328b35ab237a42cea9b9074826226bc18ad9b502e4cf6f4c2fa39eb9ce0477a7b8241c1b345c48818e5be9394844c66b578f2e010beaaa1c51c503d1cc5ba45ac86a3766718a9c40f16a680300c9ded77060c50409eb4f63c75b3df5b3af8dbf655098aa54053bd465121024f59f917f38b1440dfb4dbaf493f28c4546b924bf6bae27b067d20e6bd717d23c908ae83a5e1fa206226cbd718bda20f8359d0d904927bb6e3579c2cb8802b7006870a8b0166e9403c06a858f7ddc365e144d0a9442bfccefd4e4c8125a75b861b2458e3aeb301d036c3c570c01153f8d207d9407a46803992a7de3682dbcd1c0d08cdb58bd580d0710d0149f2c2ceeb94743ecd8b03ed08ff45abd7941e98108c61752668d53390694e9878ebe32212e666127f47dbe2c3597fb0cdb3282efd1476ff86b85730b0c7a07253c3074fbe731892c1bd76cad95451ba1d8f21a6e21f1eec7415a1acc06a468880148864276e451810b2ccb7625e9182f65c381b86c3898f5c4eb2a690ef23cf6b8a78cabde1dde4eb6fb5b8ab924d6601d98e312ac445140675759c6064b917d261994b60ecd3e7bccaa6e6f86460aeb5bebf9c5f3c3318f68825e590762da2da03a362a3ac0a0d98830dfeab7aeb530378430914710e58003bed4c1009d1863b8a01b3fc0e5e833449af9755975c8df52b1c4943905de5c803e18500b6bc75af429e2fbbc5e4bb981702b1b2dc751617c2df8c9bb82971789235018090ebdf4e817a933e0eac47ace0c854a01a6f6b0a05ea2e1f70d2597611138e80d44ea3313a6ad693a5d56f2b73541e5279fb9d97337b87762e1e8813cfa966b01ce03791c6f9241bd131760f6cce82c5013cfb68c4700d6a5a4d021448d05260abba8d5e81b195a06f08917d69d9048b9b5f0c54df5363e3436837246316e11000dff71572bd847dfddd9e0c4314dd3c4812442c68aa9ce6bcdc0e64078353209496e8905ed0ae87a61306298d8463f89703570f48c5abd77e47982a1f5928a048a722f6321d6a4101b15ebfbe365193a16ec780c4cf6d14b2c051fc4b6a19e03cea274c2afa7a3683425760a075e85e2ad4b379a781b0e10651c21661f889303f5ed011bd39adfe3158b167c203af5aceba7c4953b7f9bc8b5d530681b4d4d027da1e8f03f762b8b9943f21f8965388f9f5283bab7245d75d40f6eb644aa5c0f62144d334237b74c96f280051dab64a8b62b12943bf6556c954cb7888b2e1f054ea3fe30f99876798e6b76847433202e12dfd17e713a4f2b327971130c569d053c42d94ec548621a8c6a2f107fd58138b9f0bfa40ba83c51aed4413cfc23bf0bb4841d1d5509f86d49acd532dab780065da2953ec4f0fecb5a60bd3b967e2a0632351c98c18fc9a05c23a6b4544ecd05bbe3c1cd0627c00112e94039a58b9b0aa9399ff687cd661557c2207e6663ef5bde8f7d2dda19ec82cf15d12528105f0fb65cc3e91a687b45e498e78dd6ced1599d7bec797530d01c3913c2753c8a7d07bec71cfaca9620d46df33a25010e2a218dc5375f889e85804cca44534702a10867ab54078367999d75dda0803d07e80b58158232f553f544f169ea97de0ace0f6c04b8f146e1b0b380f6793be0208bed2eddb598c01fa6eefbe15c3b994101066b06599950f01d38205bb072ca40cdcbd8ca5ef3afa2726ad6cf6d5035a52d0e30334e0d0b58cbb2beedd4bf3194f30ef024e3f6ff6d5846979fbc48783c3f0d8d9c4d158e38a07b4c1ca402b162c8cff0aca098c2ac7a518cd928822f56a00d8b8424fe6eef7f2b2ab8bb8e5dbffdf2a6d2acc8952f5f09d1e857eaf7c01f03e190048baa6fd1a1c73f81108707fa2677e5e20ee794d03ca2a0014d4bc9fe09134fce0b5953acdbe5666357a6f26cfde1df63ba07429da61f1986c80fac44028af4ac494c53c0aad75e3a8679e44fe9b034c764da06f7805ab6f0f5c6452205daaa6b1f5b2a65ce42ce017be6a3eccbfc957afa47d518a96460d95c7968050480472188802c1ce6622695deb05c45418c0c1780907a126932626393782d3106fb79f79cf298768c844bf8de30fdf6920e823b4fe9ac0453a7553f9ae227f00a748f2862591f2f3de4a1b49bb2c9c94c414fd2ae94571998c2aecb1219f73c06ceec54858f0d7a2b32a7d4f5de7c1993ebb42c2f6b28e9e9662c0a3907a52d0ebf1a782a333d7df67a5a2dd73ec6cd8dfdedb136ed968e6e1b2028d3455dd20ac3c3c196fc5ed26c03e912d843cdbb59a09f815f8099ca9ed212a088fd205b0335b208ad26dc6123a8e36206fce57f941d010fba5a4be95bd31fcc2b4c77c10d619a18500d9e5ca4073c11f13cfb1c730d501b7510d44252731a446a7e3eb50caf7b237b7768117cdcd7df5fe9a5c9922fe7981181050d2d93a655b7bdd5230ad757e58ad8c0267d79e7443382122846ab7dedbada4f514fd11fd3da3df2f00447ec32d5bed867d3c7498ea5f8cc118812b0eaf40efa2f035f413b3ff758af047a91f66abe6d2538a0223f4fa651912fff06e1244ac3404307c5b0fcd6d43809b0e28ca8021b7af9c06e02fe3a1e2b247db44d3b38d43ec98dee0e85fce0520868b3a68ed46a4b070dbc38c4c2ced7a38ad2c1a8ada8b5beba1a2ea767ed580715deff3445c48c61468c97710353f5aa402abc99d20a44da8113779207eab40a34a94c7fa6d9ffb0e5efa7787a96d22e9009cac8e7cfec0dbfb457dc5c61360e01222fe36d13e2b2d174a2c826b02ac9d624fc448b215e0cb65e4221c1f3520a7f4759f8a92f38bffa8f57132940b81c9faa278fc0e8ca3df8863a9670ae730d2f81680cd1a7078225832d14c7347e131f8e89c6049402235d1fda210020c10145e36a20580f7b80ae57964aae2e97132d3fdd521a446a6957e0482a1f01580c27de2136816b006e8945e49ef7bbdeaaa9e62f4e6b36d9c2aac8d745a9e8f70356ae43580cf2b862b5841dedddf7e5d7098d0bb6957fceadf0124f5466533f0e0c055b09aaff28920b95a22698e431f2f97c094b370031c4afc23be9114e0a0d58a8c1ee3af12abc00b2d71b24c5044c2866916a4f0f3bbb34e686108d8de4048056a0f60ce7bd8ce4b85f6f8d491ce25a99db1c1cb0fd31f2d5d8eb876e70055d0b3387a05ce89e0363b05a2c35c947bfa22f4036eb87c0dcf79f987caa310d823e90360f1878e27ef57547a4f5d18e9393b7d18cad420f125de23909760a0da93cdebb2639653347b3f126f3e26e56254aee9bfe2b22dd06c0b127ccef740e3da21d74a1b70915f7df194609b6c16ee5f9f5a0fb77726db33e3a48e21bb50b56faa0ea09f636e8ec7dad71798ab1ebc22897d91eba9a4c7afaf66c3abc590956879abe4c797a965a25fd2fb9391d134acd3546d32ad443a44250dccc3a640171fc3bfe5f275a0fe19511caaece82e34fad3a82624fdd285ff6548da0dd1b0e8a68f16844ef1cded9c1e2fa2423ee84e29c54f86bd1afdfe084a8056f8d920536ae7fd3e1efdc55dfdcaa10b87e7e8a61afbb8d772f39e3b197a5a3c776a80e945b071e33809a4957a48b975e872bd5f32762fcc8a66e5a2ee894179022b907a43b7482a1383f137d821708caf7bb0b51849097405ee4dcf485f7e1fd64ee0b61013ce332fcd1bd7efcb608c2c0870387bf58881895294ace7ce23d1502760497bc00dec55b330875ca323ca64635efc4d2b3f8e38a31683a52d0a5fec089004c53cca4d7c78d268d1080433eafa07544e82d7cfab4dc8bf12ba51dced3ec0cc6a09cdd6873c7a61b5f7121a82cf14a6bedf66a63d015350a07eac0071fee0a44900e87c42dd2ef5d67473ee2548d14a33bb131f115d4eda763a91b7a08940fb0b30eb9f03d8723f7eb26f068b0919e36c7e21a5eb32edf2c6f2bc98b270a06e3e41dbb1035cea69ada75a378c742ec3774d3ba80921cf4b733127ae7947b014d2b8b93f428331e50627f536b38c088fb87d2254270de5c47e70c5005973f0cc2fb96ae432c055703ba722fc7938bcd802e7cd9601765106d3b45574f20900d0652a725b0ef2a36f870d8dc4873f28439d21bf5b6f05691d005c3135633e20fc851c2116435ed7b18a9cdce5588ef9a91c7a7ba851f681ccd3b63702146df096c73625cf1235861595c3bf8df325cbe620e26c061ee854d8cb0f6a0610f0501893b09c329d7161a245e8d89076ba13fa2d5bc092cba9100374957e975b42e01c6f3c8bf7469ab4da416d4a3933bfdf9e4f9f177560e1ddcb663d5940fa4970ef04f779183561d698f6b0dc43307c8fc03defb619b08558e21bde3bb46ae0506230776cb34bf5d3feb3f005cbdf1e55f6c5a582664eca237eb5ff7c3c6af9108165603ea174ceb5bc161b2258c2845d16d15736cce35146682ecfc4c65a5b503b94909041308b47fb6b2048c9fcb299efc750bfd16e9f4c2725ad586611f4f0c467b7f61490781489c52a9c7c9eb9622bd5ea7a8227b5fb5bb77b8ef83e6000929a1cfa228e13c248a911c6aa863faff2a4e15c06e52dbeb0b6e659ed895be007e7c3411b6fa73340fb630c42e46d1286203cc518c349501bf5ea1c245a7f20a40ee8ceb90b32cb0593b8325b4d2ab0a5c3ba53a43c2b543f8058eb63f81b20f true +check_ring_signature a36005a212ce1333fc2fefe069c90af170669c26fdd06bad723cb1fc2b048282 09b2e9b2f833b2ca044ea7824590f794810d3c504829e5eb225e9f441536b1c4 55 e6ec5a7b1458bdae7057ea5cf67a335dca2b9c9afc0a134968412839f6438a22 bc7f0439df59d85cc1601da1aaf602d9b59be1249d96007d999efbb89954b27e 7dc8277988ef1062a58e73f3d7310b117c90c27ffcda5c7e7757cd109b504c30 09555827405064476846e5c4bed58519d92d6aa992fba81682f7131bc68b2113 7ebdaee364baffe447ae9157d8251746f7ae9ae60b4961cae3acaefd2a3d1406 6fbc8bdd1ae11aa839a353d1b98e8b170de018867fbd84d8634016b65e65958e 2198fdc9064d2304316d3778a5f4f9771ce643ded70401a49ba22891b651b31a eb9ba9e2474d3d7fe7ed0cf8913a9199c1fe9838894923b4cdd0e654ae4887fe b29a7a79dd5a8c16f81e9006d422dc6fd960308c323ad0006a9029d53b6ba43e 4d896f2bbc4241c0f46310d335188f1a872544298c58e3d804c1dbb240391854 08d196f8f63079333ea27b84cdc09123ed2e3d07e460f2415682db659f83f08a af8b3b404024185d91d55177eeafafe93042682a09097d25cee38226ca98010d 435b419688c8971fbe3e1564710107c9bfbcd471dc3485b16642d33ff5386122 232dc33bbebcac1dfc8879d7c65d1b97462c43405b2ff470d9652969ac046ec9 b5412d634f0af8e828449be0de6cbf3fad070932eccd0ea600b4dbd5358750d4 29ec83f5ca513055d305e46278dcc9dfef81fa8e4a12d90500845b0772465478 f61b5c51ea8691a2dec6eb1ec084398b49090427fa8996e3318e106727bfa127 c280c37a4af669ad6009da1e1a0cdb1ce110f66d163ff43500f84601ad565fdb e057a01cb5e00a3c1dcc45a9cd26d0b69404cf3d64e930fa282332b0ef13046d 1609f3f32c1234e51a79551be7108c12c1a34cedd0f621a745666b910d0f2086 4a07ddc25a5ef2e3c2f9c85ff52ce13ff071b75f359667651c3abcb9f91f1e5d d711b6353dab2d9237c744538de0cda513e37b8922eb11f63ae2f98b96e10e49 154367a85291e3c0f224b2c4f4f00b8fe7e1c9c7158790081290ab90db55c7ea c767a78ab1f7cf30ee3b439e870a334721d543e6e27e96287b42422ab4431438 62df2593f995a25bda2324fdda2fc7add55fbec8d7acf65b4e9877ae5991a51e b18af65fcf1f8f9f1cb8d2e00763967cfc601e725df2aead633f1262991b1766 eec6da4fa41d8abb6fd70e02cadbdcdca890ca68534977a9e40692006cffac33 be53ed1dc5bf204f01b72d2574972d5bfb262595b18232eb6002c0d69732f731 ce1686b2a51858676ebcc1d7621d8bc15705fa93cbf481c1569fb1d13dd03b84 35dfe7f35ec37daee9efdf99efd85c06456e0df5e8ef905916ccfec5117be170 6d9e564130525a44c8ba87cbfa0926e37471b009f23dd354d63ba2c813aa73f6 7ab68d93dc3d81366800ff50b00c0e809ab855eddd08d5090e199ebfa6173d81 157db44457cab63ca00625c2dd77b35f3e9001fdc0dadf923bf953c9df69a511 0b282413b88a18be6add5ab1116d79af63902cb5abbbc7aca1a1967748ce24f3 a5b2473e72947b5a1f40134460c20231048f1b7db6d513f79f945fab0f48f13f d93625ed4ceeee02ffc4f2a5255422ca1eac2c143a0bf2af0fd11c7e6de84335 e8af2b03de557c6bd13ecdfeb48cc1dcd7d1b9b55c5abbcfcda0d4a0e608cf61 0f26780264cd140cd737d2424f66b97a9de5b9d71cd0e9b9db1215ee0423f2c6 92fff4e3354983aa9f88c648670afb63f82b27a4af43c3812c31186a96ba54c1 99eac9f8f3e04acbc69d402d78b07bcf01208a973a31744c9fca3c7afcf85b42 d6d24c39e87355234f422c0b2959167090ce026d8ef94d0f53746946a53a70ae fb86cf51a9c605f9d9f6ad7fe039fd401b1ee47b5f755fd4609f6ad9a8fda3be 0f1b33e466981168665e6277b97e3673400bb9c36d1f1295f66e99752261d90d 6a170dc70b9548fa8d16b59fe0dfeaf62d0ef3efcd832d5e352a6238075b6280 fda4696f8f135343d0340caafd5a1287e9be854854c9b7797d3728a4931b12cd 4053b0a7409ae3fb9e1ecdcd6bb8ae8948bedd11476acf3c98ce7e168c4d8d81 edb2a8772be93bd0a1a5212d965f491affcec4c56941d3462a5e5e21b1f19e49 17bcc8242c7058be5f1dec51941f31e85d40d4fda5acad686c621bbc6f3c0b3b e3a3de8eb24d31cbae6b9e8fc3677d4abd58a2dc70cdb544c272331a20daa8a4 d1c7db62e090a5a981a524234d3237405fb7ce16324a4011985d54c7d1980a44 b94aa63cbc866fc6e42a4cc06a598f254666fed09a6c502d93e6273a59ebd180 3c8a0410858a5fff3978b5a253bf29ebeeb9c1fe2c8e12890dcbad0196453135 07883fd417d1fb64440855a02e52be524667c70984876de3f534b3ca1720542b a11f3c36c2944a4986779d15be326a3605bc44a75ebfba1efcd8bc6b752e536b 64a93707586d009c96911abf3b031c5f517a6d9d78b15e7a0835c85de2f76a4b 6414ab28f9992674f2a4215d164e88ff980b08387bd1dbd75f78aa258d2d6308ad7fe775e690ab2c1d65c9a5b6d0eb8d67d2da032ab21360ba2da5616ca7230eab65e3d871be550648376b84ce7745cc84fde3d2c7b3c77b2b11cfd8c357aa00188ad69be3776c2720d4bc1d9d77af0ba52f106593f7e7ab18f846757073420e91d2f5de821291c629423192731c611f1db5b60d99c939e6a04a62a3801534025a9e942fe02cfc80a8a6a7c28f0b2757e8172cb41d445712047ee918642dc603fa76ee4fdf806ad5d427e7545e718f0b285a1ff7fc5a222158dd51ec969db103903c19c92b6d7850813057dd255994f99943e6679690764ba1fe6339136ed40bc828b071ab8c4e2be816ed091d8dd444fd0b4be38d16e53badf66c01cbc3c60506fcdf47b69b60eb77085b89464e0cee22ae4868d97cbac4c2ecc179feef8609575690556176aba15cd39de39e0167511f808310a4bdbfe66912b94fa6f2170db714379c31fad32dc48b638c5e90a44142fcc0c49992b9d1e6ac753c43a50306aad4ddbf281f6169134fef9a91fce3ed10b01c7fc09809708f1230fca6dfe0076905959e99f7937f3f15b29fb836a00a8d88a3c1209791e5866339c3625cd2089bed1f55dc59dd70f862a5c31d3f07ab31796965871c7c4298d918531626530a52442b25c5af13baf5f3123fdf1c5a913bb062c4a08280b9a96e67f48c8e2908afc26503f0de9327b25d444bb673f8ecd0268cd28f90c71e12ddee2c0db81f084c2f07f199c2a0e407b54e57934d65c0b1cd5a4d1c4d7d72e1140cb72f1bb90143e01c1dfce3d29e54e53bcb5f00de4f2c95ee97890c3222a392febdf68fa003a7d458146f06fbbe6e77ab9c87006c848f39af39f76c9bfd751bec300748fa09df3edbd9519470df40b6a927b6dd2f9b209fd8cff29372c460f1b154c955bc0596f71e266277deb78e7af5bb991d79f0e323dbc048b92aec37ddc87ef628310680c0d5f21b54ead47176eb026814525676409c7bb46c18e5aedaf60e5ceb08024ca5542485d0407b0021cb154457f1af60786a63afc3cac5a06f8ce2c2b706041dd1a14c9ec43cebfdf43591115442b6f54f397c6b6a8720a1710a206eef6001864fb0c577207d4f133f9ce0956a376152d9aa370f06a9bf3d824d6c5f26ae0ddcb9679cbda6a0c28c36f4424cd69d282267596bf6fdc7c9d63543a0e76261069c28f6523e470ab6e72f4c3c967d845caeff475766290c11306ede3c0e14db02e2fcc47e84df075d7473cac6f0d6f23c1cdfed6a07b6eca7e5e6e21c5762a0077abfdfd298bff2c08ae328830ee792b58b969b4b933b8d1054d69b0859d29504a0997ce81f5ac491b20447490dd979904be3fe7302d7929cffa50a1a4575570e620b41c0f97796e1f39fd0c5e8da7fea81dca23ce42de34476502194c634da04d7ab307e9b0b3869f0888f5f686deeb41c8655751ac7bdcfc84f1447195020497b90e788f8aff8a918f09aa1ce12c053b6b8f9b1fb0968a7a596bf7b26cf650cbafc61c21b2eccfbbfedb4804d9483e8a012e8676c2ced1e84e2da81e2dde40385e25d871e7bae6e4dadd0466085b9971938062e00150c3db2cd8a1bdd05a407af8730ea5d5c31a8f0b79c12b62d541a2cc17c62a33b89dc0af3773680471d0f28bf27b4bc9ff5725ef4b47391025b5ed087aded10566623e9cd7f3bbec2de0ccad1e40de0d6d01ec37fa9499c7845bdc5f9e5406954b725020a9d217b486e03ff0e4e6022ab920dd6373b0282219654c60268896117fe633d220103320dbb064c6cbb92dfa3082c29af855842274b8e26659ed2c3eb0c7e2ea3e6f05711420245f1990c452a0a957903fcd060385aabd180bd1557505e03b00ef5921fba110cb73d3e79dd68122fb0f06db52f52f5a2e10181f566d3431573215af23ceb010acea6de991e3098cbd7df4166391ca18d090a8529ce1dbe2279c82f34c9b9450b021cb216354eca567a1751b535bb8f107cf1b9359847ee9084ce35206426280f163587c3b9bd2272b54130275bb595456ed45db7495af184f0e93e80710cf10ce0e4f3d1c99e8dcc7444508af0536996f1708f7f0ae023f82d0e3883b8b570025c6ec4a72cbdbecfd9fbacde2746ec684492e05646e2c3e4d502b440bf5e2d04625f237d469485f32b3e8fa45b3cada9029d9249144560b88bcef75f147d510b3d66d06e760cbb6baed1abd9023ca839dd18a8971acb09f4ac4e336a3a978609b5933e6ed7a3f253539811d66b91177154daa86092247ce6d5272023572ccb0b117b2b896debdbd7039066f8d72cbd7e3429d0cdb6a9da89bb3c7ced26ed77034c7c1950763c8542544320f9e90c81cdd1999b86f5250dd90d393f5baff010017436facba4e4435ce6f6d4f94ae3513dd3cf36105b93d6ce68bcf638eb954805fb33d228dda21e28a6c695c33bb6a42ccabbc5981bf0f3905b1deafc55c0ae009d94825fbf9aaca712500286e01f6f92eb293bf98018617a19c88068dc92980d11be99712b64a05f466b93b8708c757cf75ac1ce715d1972b83fd58ed8813708b33456a9261d94dc42e81f90542289ef52dcb397e8b4be1475b83ea0888a370ab50123513461aec4e367e50e5b105da2fa2f4774d37c33ca928eced7bd88fa08b639fdd1262cc67d498c256d9fb3bfd095ac72ea2090324d3080403cf8e5fd044baa5ceb14d288c3abb567a9497d43f2045d258b20d3ac78b6d266cdc378e207f987929ae50fb3eaa7725215b52bbd15319cba08b4401f4ad57c0d030674fd0c1a2b72d41a626474675bce99725b01a5ed3ecc18641ec0e9601f82cf79301806801b2a78cf8078697c390bc456b0e706fb9b04f4a43aff0e361ba24534b1ae0da0f98e410bf71f938f8be803bf9fa2c3dca5314339f8f212a7dba4dfabe28f09b81be3cfabe6ec84999650b36e611cc960311addb6b813ecdef7bed07bfdf806ced77cb68ec0e05382901d1c962e27b7c27665b4d17e241ac2087c3f57742802931b17a298e24a83c2144642d151a57b3cf86dbf8bbb25a571b51c18308443048ab5743a33ce27fc5ec474eea0915bae4d9f7b3a3f8d65cf1c0883457d4ee606a441acaa1869db734c90eb2527763167975642577d9e03c98d5a4857aeaca302b7455113c5a725c8508bc4622043287b2de18c91d5ff042cf4e911a4554d900a1b5b3a59963580eae8ac5da3e60d3140a794af681010346938a3b3786e93960b1d8c2cdded569dc28db8d03ee175baf35dab92cbb4865d3d0c5b3848ed906a0c95101a08199e0ce623d516ebee52cb84337b7eefed077d310a5d108c905d560508d63050e774b2126b795e65a857117109f23fdaf816595fae6eb1e16a6607002c72df1ef775212b3fe1016d4c4307a8dc5c626374d398d72c1aa2b33deaf2093ca63ce1f3b84dd19c1261f7175d6b48e15e275164b5ddc98ed9987ad1eed1085c4b837d5d15e7a4c18aa85480fe8ecfb44cb2fc2bbb43440eefc1bf15a6e0078743e7f8e2a9e9795c8dda1b68d972a7edd5b948eea3971d68a093cf26213105f0782de7c9b6c66c281b3af1aa23d16b8dc20ff685556c8eaa67d0641a70cc054d0c0db75f6419afd89cc9b0f399d0c8efe9291e9ea1f19b176cbff585689b059db28e4cb53d34fd573d2d8e1c753557477e9b321c826f915185909fad6c380ab55763e6d6c205f723acffa3cde3de92b5e311683320514206dd684ea94bbe0ed6036cc16fd0e4fdaf734a5d490e3d5364dc1cfb36e89818014e1e03d9b87b0ef703e707d49d122ea89e6d9aa9124c34812fa7b929bc13a3ae72a2682aec0f01205eaf70b66ed8491ecd633ded0cde3529744d16b3bc85c6e5e63073f913620e07c2aa430bda2e85428bfba1534fbb15f9ba4762ba94a643dca575583e8226096e9c2b8c146e1ebffe2cf15754e329835aba80ca51deb8b039d530c0dfeef405fa37e7cca8f0de7ae9eb30a89f155c85afd03df5e71ce1a828884b0cd765f60091425c72b35a6bb2662ac054a868378788292fbf91b59176e6f3dda2e1ff5803c82b5df152ec8ac986717a0464ffca2466a6e975dd5ddac4ebcd28621df49f06a6825f5734b0e2c8d551afe6b02992389b894bc2be1138db7b3524746810610b6684fcaf72172cb26c432624e231d0fc5f037e160976fa93c7c73eb9bf35590736ae3ddf81b75105cd83226c23a80ea336ec97ef446475188316b48f028ed70462a53047917c0de8bbfe8c59364b6faa62d44374ad1b3e43a518261f5ec7730b69160dc506e5b6dedac3500283fbe4cb4040690c93fb6fef3287b52cc93aba041673dec4a34cddcd5b4ee6145ae1fe31ad1f466b6fb2ff9a25f8788bab14fc0af43df11243dda56c07344f93d536e31d8531d4ab1a009a3158dc2741f9a5280e16cf705005898bab86bdf532910588022cdd0ceb7ade2170da72eb00e2164f063be404c0adc4e391f2536712697ef5e4d3c529ad462524e823c6087a4fe27d0341f584442b9c65bc64edf3fc9ad726755cbe698baf34dcb15faa840765526e0756271e8f65381c5abe6ea5a5614d348ee430b1ec322f99044ffcdceb20c1f908f80cbbd553ab08b24531395ce8b9692a31d9393e20fddb4a5286b97b420b250bde3b744fb0d46c7aee276690baf26f63a71472770da4ed4d47ba1a32e9e792034cb78d5bb9050a5812aaaf47acd4dd74a63ed80f8c666f731b1e32a08fabcf0b4e23c422bd369a3a7a4734bfe024860a5f8e83979ba634af67b397193df8b3089a53114e794c1c8b322d6833833246f297620856e8eb70ee1032ca0a5d9d9c057e91a90c44d04c469198c1af805ad2f71736f67f03add473d566011d3bfe870264adb03a8fb4f0f5f5f3b037c2176c96e528bc7e43ca27426b589112220c5104172eee0bd2f1260cda02390f59c80926691cbc9b35bc8ddef0899be4dc53750e false +check_ring_signature 718236540e4a43907f66e4ae744b4ab66118c960ae3d9cda343f317200ad70b7 85a1845ddad8adae8c71fdf59f8b733e2df184d524e7ae2e32723176fe4abf84 7 f87bde8a53c80afba1dbe512c678fb44ae9da166ea700653efc9dfba96e6b3a3 e5803600bf9f29745314a0cc5616dc920c0e0d4d7182a075af1b2173280b037d bd8d6c2a311e647e1cac68c122549475d64989da250e82aea285ce66ed3eb200 6c876f54b7b9c3d02c57a471f2f0f47bd4320f33575bac5470143861f8df15b2 84b216cf2bb7c8854f12285a164675d9d6751a3e3b8baad9754b5f355c10fcae 0473dcc6e28971f30e51633d339849aa6dff6ef732f1651a2cae54aa63f60ec7 288dd29c89f4c92d857bd2d263bf56d5cd373d041699906a14feaa6f8a6eb8f3 f020a9a863369e06dee5504f4632409ecb521f6b4a93ac2510bfea5d826ca501b23cbebb5c6cc46849f93cc120a66eda2b62db3b890258e6a691ec45acb6db0741b31aa475ba1fea18d1ab91c41eacffd3b968bc10d1d34792a5cc24243c2c05c4e531c390f0b7fe037a71d3a6ec7d8dc89c7cee4e9c025d4143f26140211f079d82731466b3998715bce0cc6c7a85c813ba9def34d470805175d3df724cad015aa70460ec80b03fae76d3e80982244b11caa71ee4f777748dea785631027a0110130a85e78429cd610805fe88559574b3fb3a3263ff1a2a2116b6639e817b018ecab22b1f93405890788405982b3e97f6de162e3ebb15105db92416f16a8c0b29c23e1755a6b2734115e51442f1b9b5f6ded5e40ac68a63d4e8f124d6e63c0b0e13aeba6e9ff96c001b7dd6bc599be0de8c544db73b34353357e6c8c7d7620e46de211f6f24c528b2c6b34f095daf10a0c19b06e16ac58fc286875918acc2004ecde0b03a2144ed3e9a802367266173a092316f5aaf1b76436dfe45e7ffd90c319cfa068ca789b2ee287187da95b46691270f9b3799d0e8c5d13a84cf08770e8bac058c9f14d6f43fa3432c2f081a10ed998c6621c5c3122cf1f609cc1f6902 true +check_ring_signature e53805e2c8f5bbaf2787724b7a0b97dc320f7cd8ca4916f33e4f17143fc8c335 139df99b929f705774716649ce5af65cd1e123bc759f297a8e935de8e2f918ec 4 9c3ff68c92e06511033675cb7e927737e7c3ed555f76cb36c376dc6a51d16af9 b2d0e305851d7007b073bdc01a77e00dfdd6287f0fcb9d992cc283a00d747325 7205d7478456d0aebc80efba5b9fdfec065981b415ef48ef4483f68d310b3576 8d074f949226c84e7c0c5766a786144c638b0506328b2ae9ed4fc6def5318589 06237d0b07e0463f9eabb63d2b2b459f6205f4a3feb660bd8d00bf0d192fc3055fa6cf218b1debfe78cc3ad44a052c114c872caf6d0ef4bf412e63d08655cf02df1c7afd3b94fa9586e275f5cccfebc0258e86acf38217398100446c222d6600597a7835f741ac4e57d8af1792f7693821296c087abec515a442a48adaaf47021f29a0ddc072451c42801f72e91cca4a413cef8e3a82399e2bdc763e92aa7b08378c731476aa9a0f3c21a56e40a15456e19af14449cb5a3c02acee8b53349f0087f5c8f7a86c5b239fdcf91dff9d3660b1eadfb0bba5084fdc574726e9c2bb078db01b327156c66098dd80e69123ac2f5ea8acfe338b313b8586aca983ace209 true +check_ring_signature 9c44b85943d35132068c4ef80740484ce6af710414b191623ee68b2bf275fd24 8446fd5e770093c75559321015216bbbdd59456923f3bcea133ead365bf68112 60 b8bfc56ba969e563f40f51a08676d3b95c28a1d7cc27b9aa5c7b2699d55f24b9 af051fdebf24f063c8bf89fe00d02b847b920d3f7572a4e219e0cf8593c335fc 434a8586fa927effacd378c12aafeeccd89ccd9fb6785d94080a2c1d17e02b06 5607f754175ba7e6cfcaff5c054a9c276f91c34a05ce05c805650f0a9ed86327 905ce975a488aca48ff14780459110e969cdc338607d179d119d53aae26bcabe 8d4bfbeba58a6fe5dc414897cf179bf50247dc2583f28859c816dbc551fe76d7 f0460b4fc1759dbe00af6998a4154f3f30a6e0847124f89cfecf52c0132b0df4 58baa6145994168e3d7b1290c646d422f66e66dd6544e044865d6e03d7bc3f47 f069e3ab587269b1ff7d226d0deee4de149b69ab0286fab789da3b61579cafa7 fde1394b0bc08eba6480cba19def66e33b18835ddbc41adc1dbdbeb443151651 87e24a0746a73a7c1282277e488d8cdb37fadac773c3e0c85dc0d242d57b5649 d7dfb081297c32c1d12f9e9772844b92267761c5c81d7ea63dc22f6aa9bb378c f5eebb4414ee7ccbf0664459a1dba060e5d7d20ebea95daab323f45ed4beaff9 347f65e02ca15213a34b321b93d49bfbcfa8a39c5050553b5978cd1c9b0e1b0a 567d5b3e8c8116de9385f52fe42fb2624bf05b6e2760d1ee90e37e29da5bb229 d9ec6c818656b9f8e3b7a3f0020095c090b494bd114e69e85fcd59d452b76a94 d40fd18d62287b16fecfe1b8757dbbbb04bf1f7e1504718f5539951112f3d7eb e9c7be28a4cd77362edf5cf31b689609c8d81f86543cd92e9e4b49418e562885 1b1f88932a5d3f4fbebbbc710f25c4fc6b49adaf65e882fe56bffa6fd10a76bf 29209540ca0d666c197502054f7abcb2f2191a8d0e8001a4fad4fec174bdb003 cb0041898a434f9783edd3d1b4246a66e080c8ff926e22e6e5191457d1ecd230 fa36dde8217045f152b09437ed54d27e6b12ca9b754d421bbac77f007fc5a46a 6b3e4f19d13f5702b8f94c7cd234187db579a62b0fee472281832cc6a356ad83 c65cd838c3c854c1d86689f0ad5ac09c79644fc49aeb2106c1dfd25d5f2e40bf 919285b13e091c18b96d83f348e17796657eee98c84ae58a90c74e5ebcee733a 8de98f9e8b235f16c80be940bf996009b9cb0650c8a07f208bd84ebd1a0d3efb 18714c646c4b0131c9945893e6499a6b41f590ab806f46730329d551d4b217cf f4bb3c87579919d79b53a010e70ad8a9636559b5c570b325e98952efcf5d5fa9 5025ccb57c8183659a7ae348278485bc6c724e29423d9346f81731ac4eeef31d 0f138d4fdcc9fd9f65263ac468d21a1974cc9625ebdb678ba48af9f655c5bfbd aec467fdea003daa692ea5d78135ef2761dbe4ba7077a37f939832330d73f0f7 f32909dd54c6e573e36807890c60a3d1ab3bb5ce793654a3949ea64c36f9a1ca 3524c29fa258316bdcc25244237134e0d8e712d8348e4b3ab2c799f3efeff44c f139e100ef3370db2967de7337a3ecae8e05f74cbfd10297d98fa93af82e6571 105f68040ca117246bf2d869c76f2eddbc17a1a592603ee95de6d337b438c6d1 1b8cc0cea81b17e77b03d1b344b54e60be8408b11995fd12044878d021505560 6b6aaf022a12c7209a4233f33fa13ba3a14a4883b66101e5b935d3fc036c4152 52a213c55417d56eedfb6d54793fd3e84672b3d5d4f425d68157fd29c0f8e71f f9a6bd6160a01d1bc34ea5767296a1ee0692c607eb88127edf07bfad745712ad d759109ddb8390de52bd69246515fbba8eba6d9a3cc995be852354917e4b7ee4 92184b16dd61a8ee37261c2010c7556031cf895918a0ba6207a953999c16afc0 f289fa06fad691bd3a4cb54728c7d6c1a0e25d8f54000c5a616960bb0c35275b 4ac7f41042a039de4e63000865ab05788ea7d572440b0683c50445d45485121b a39b03d80580593dec5fb4efb32f9246e1ad5642795080f616567a242640a777 65d8da10bebfd1a6ab891df32e43b3d177a6a9d11952920498ee94414b329693 10f1bf3b95d005c6d50a0b6b6199285f521c02767bcc67fcc0dc178cf01a2b7e 8aa0700e482da62c62b6dc457c66e402f1a5b9a6aca4434fe460535847fb8942 4c8af172b9cbec7e8b82b3ae28746aa5b4db634efea64cb4340067027e088294 bd181cafcd1b76f5518dec64467e7948357a45cbeab9927f583e8146a2546939 7dce01d4833c6cc6290865fc73bff286ba1925a0361c57c50bf8457ebdf25a98 56f46957204989d6c656dd4eaef1f6db515e024e5ffc23442c4aab8dff493e03 f9bcfd878b03717459c58793634576cf7b7a10e1d9f6ac22296c5fb841a540af 7a3fac15fbd1e026fb9b20b95a6c5d184af2c0cb4c7b6390c6344dbf083cfbaa fb16ab346dac56f4b924697e667eb81fe93225d6097edce4c1dcafa46e89bf6a b58e403c96b2b9159d047af9df3c6e17aca56badca8a4077918eef4b575373c3 d81435afb712353e90f981472238be8d02ad3ebc489ef68ffe6db18c539da140 4a788f1652cd8d684190789f41838ed6e3ec2c6b261181825476ff05a77727fe 63022aa668dc36fbe95f178e317e3e7d7e7cda7a366caedff5ad28c0825543f0 a1d3e085b1ed48811eb3060d5f35346aa77ab542facf288442e6bdf79dd4ede6 59f0c087fa8a7b0bcc9ae039de027527b5555f4e77698febb5ff08c65f4c3e8f a037aa108a7cb92a8907b0a72b632872ecc9ab77cab334f4514e728a75047509f8dec8254891742093fc9c4c4a5848abd45141d4e684ff353fa16a4e129db50a7dab83f2f70429098f5824e340b95cd074c651f8e751846ad040fbd77422fc00417f032116d71dbc7c462505cb52ecb9d8dbc9b1f79fe60e26ee8db69755a902abb8c16dcc92e44fc5c3ef4757dd3395ee6fb39819c31feb3237b7f721ffd5008b83cdf830a9ff3d51003f5f5b630570080777420b8b5aaf6c95b4fdca1127013192be9bad403ce5f4d65ff0ae8889a302531ba37bd385b8d1a28eac62b37e0fbc6f25a81c3c2b23661f5d89807b9c432f7fb8ef2cbe833a65c61c6cecff8a0236cb3146e5fb4090b95e4dfa90f30c2bb695a5616792e5ff34731a1d8f11c60288f059ada191df57a05c70052df4bcd5a21b4071610751a8e26d8c5e630fd50ce211f9519663ccab7e572eb8af5c35d44f83e5ebd92bc1734bad5e192e52b77370ea798ade2db80a2e46de1c7cd7298135a0a8ec7aedf485859e6bff87634b0aa6c5398cf166f848f3bca833558eac040a7d0430558afb37232055f9b17b6f079276e221ebc524b6465ff6617afb7fcf22af25de5058197b3ba762233532e604994f6acd65bfa55d2e04f2c5f64fc0e1aecd5e265eb19ac04b8260a41893490704098f5c13c961498e9cab2ddd441d324e2645ea4f6bae405ed39f1ff6db0e0189c4de4520b36dfcbec393e96782ac37275a1403744cfcf65d7123ae1f9611046f66345fd7dcda7b1ffa24af67bffbacd57331da7968372687031230b0b6366f5cd088a299d597df41202d170d98112d55362871477c6939d5b30bffc2b0d8030e3727760aa750a555f36c36ff3a1f0a6ebf88755a829194f317f2c690dc1b02353010066459c7b083466f5efa6106a75e3b5d237c28b116396747b53c0d3b0e0a3b43af25c55c2b03bed60ef8cc1776007309f7e3fe3ccf2ef376a9223d5a01b3d3ef3ff20078fed32bb9dd3c19ed7b4daf045b3e60bb65524db035ca39d2664dae5cc1a4e55beeba06822965c44eb2adaac13e80aa3fe05bb461c181ddda096df6e7f95e03087744a88aaffbaf719fa909a75c4ecc0f445e8a02f73f7a0105d954d5968d73375ed01691729558f660b6211beeb4ff1e9f891ec4bee49ab00ef90230acdc523d73885892e560e53bd8ee32853de9d57ee64a1d695b18803c00bc33202bcaf4c081f4345ecf73d1f0c9598a29c119cbc1da4bb5fb7805de1a0ce519addf44a9661811cc6d7adc02d2e8ddf49ba74d4c7e18d75fd27b9838d60072c8c45b6a00c1aac4ced59c6fcf741912be6ee3a1889e68c551ee628415300defd84a0d287f46cc6a97804dea2fd2b869d633f0c158c6754266dafd65f56d06f5e1deae7bacb99b53bd0ae0342b922882ac05072d5830826d1bd9de379bf30533f9b60765ab356094c845903f05efe3af359abb721e5be85ef73d57bc5c130fa96a556a70330d696baed2aba360cb06d195d84f9e5d0e2e77662e51cdfeac00c2877613efd34245a5cd21a863afd5a519d283eb250a2cc48a9155ec9c31ad09c534e798ef03847a6b6eb36a656559197ebf355add1d3bb111b17f3fbd50f10f4ad3786eef09d07eccbf690207a3e028aabf2b191f21e1a838adc84c57a9aa0e8cb3baebcd60907fe46d4dace001e4a57ecea1794f288fc7bd222086d0ee8101982965dd97330fee9ef0aeeb52057c89c5b334408476a3fd9a2c31debe62030ef8d6d3f69b0cb646786ed0a9d652052b25e675d09c5e89f482fe51b1f0187d0d4912263005bc49f83b99df414fe4720996e03739005dea2847ad4ad40f55dc0587db03b121be2022f7da9597d3fbed0463ab3e6f9ae3619e65f5b498e5383246b1fa85f76dcb9442280aec0a4afa400ae086864a1ec26a72047c653f19603733ac1f808178d1a18a23674a4c81f5c7671bf7e553a9ec813dce491ff2cc433700c7634eb6b84022ab947b89b83d72408d77fdd43d79db6e322127192a79b0ee0ebc33a9024b29bbfbf6b563ea4570935fc6b484c33b4b54740fcb49b4aa2971061f9f2946e119acec81776274196effae2f6876906910127dc683cbe2fe06d6040a885e0c38033ecc89b6307d7d84881401814c8f9f957bd06aece01243c01e04f8222638657ce35340fbe5ea7cd5cc1a3da550b6c7a1e7962f03446c3d34f7001634a2421c1e340e867e4220a58fbd4dbe53875d027e529eb6914b7f39ecce0c24b585971e3443e86fb9083df557505f0120a377b7d3610dd003558a0b07560dee62f724bf6ae8d6704578b6ee5f929a3e14d666df7ef28224de61292febbe016a21e91bf49ede3e3b4e8da96cec7f275aff3f4e268ee9019a36c0ca10c36d052e9374fb8140996bd7dfa92d3beef4a3f352d1146d4308cfe12cd9b890d508094b7b37171eb226c14a4061b26c3242fb67e1e3cfe80e6df02a05c57b58792e055a6355d27aaed4f35b50655a46f35267e830688144ef411030d213a09de2cc0b8ae1914f1f06cfe30c52167febd134c26bc1ae045b2c418ae3e1dc873f73980b001c3da09616fca5f78198eb0cebd92a9e3beab5d985f712fb8556b44f3f57051d6441d6314ed884ad49085dad77070d6dad58bbb1458362fb7ad34852042d093060d5ebff6d85e075e49f6f6e10eb1c490565896925ac59f36e626ce040acdbc8eb16499f23edfa97cdaac7497914ffd870c73ba869c96fe5986cf56770920384b7e69d2fc2cb10115f709552d0c8930a9649caae05f1d870790d696da5830bdab6d4eb29e603f39b0af0b5a7622078ec84175762ebe78b16b0969f80a2f007837461fe97ffa6f8a3c81bb255d8db084376a04709dd423cbe1e601197e4e60badc02b16b9ac331de11c82399360be2089d6d18c62f0a0d8f2d50498b1d6b3038e7fa45b933791336ae9501b758be17b0c324efe95e60a82b4e337a387337e017ad1146f80aa7c6343c5ccf59865b7f78559a9bc1a6629f3a9a746a683364607fdd1e5f603bc81212ee493bbd70ae3159139b0259f590c9f60031734a28d7101718c87a10c57ae0a445f5462f56b4674caf85b5a59e873becfb166bae2f44408946231d894a166789a7d9f26dc4b45392e9dc29a7caa17757b81cbe8755e3f0a604f98538f0f74fc6d1f749abf41d984cb0c160369a64485f257ecea247bae091ff333ffbe6feebd1041970c7802aa43b29de95eb5daf806661c5cc0fc2416029d8eb0b4a0c0162c4aa41d062b12090e697c04da9c6808e2bcf82d328451ef02092f6d0177b67848951c916323b7c7c03a5566e0dc42a2637dd0250dbb82e00dfc196277436d8b36930c0c1684138c74d5a6aea9930d88cbb4b4c222526104082c766915d80e0ac29edc38451f91e2b52f835ffa14e817f4814769676b4d67093139696e0c1f3735fe21e771e19c118564a842ab7364e3090f91347d013f3d01e048b2edd6cbbe6d15c9db2caedf96e942e8b0bf50fb922107c5b79d6367ea093887029bbbe94a82c84f29ce48165618b1ac99cdce6f329d05dceb1e104d870ed85e72fa98ad1ce99b9b12510ce7709537554d11a906087d78d63ecf42c01e0e33e7398bed3f049fc62033289e9d5cc7744bc9861fe66103f297ea92bc12310d60d7afacbda75bc91dd6c5b9bdee5e346c9969076372699bb7a319bf6cb9e803c35a1fe793bc2fc2a87a8a377ddb8c8e836febbc2e5d5ef6116f36245febdd0818eeed5f09763cf9abd887c81a43b6df6e297e1c3161b45ea77d8a1361045c01720a7ee8d84b989d9d60fb022209185a79d40314cbe40f067e7d251e30d9210490896473bea3e213b770420bce0c3aecb835ecf18f955b2597dc8948be49c501aa7debc928fbffdd7381abf025a7762e12c3e92468d138a4755ec8cfdcbef80dd8a84b47cdda6bd7d3d138abeae7e8c3c1c3d02fac401aeea24cc40dc877a5095af0d375c2eb1dd2e62936bfe52596d33b80dec33f33c3ac498d411a8adbfa0ee3c1cd2dab1034b349e73605b8b0ecf4a4bb91d983a54e269b2fa9c2f9de070fd1f3d469deb662afb578b64f53dac4ba42b501163245a942564118be70628e030350dda69399ed24c0f2e80d82cb7c655269b6aa5ff1da06330d1747e9d91336e016cdc17f37ebed56b8d47ed6df8d880eb30e87bb1f3f1785991ffdc9dd7e0431475bb3944d5867711d9180fe06c7d89a6c933b00f68f9dcd8fa90e0a10fdd9dc19a170804156b4d3e6095c6fbdfe2eaae87262c0babe866d408c3663989304a6973f50d70cf4bd24babddca189c021f33c5060bf5c78f487381274d47c3d0da9d078a347e7bc386b6bef6485923a2bd628eeb73516803f8446645988819c0ef959efa751abc6c12a00e146014e7872073124c69b3cd7159e11db12c3758c00ef74e4d665cca64be95c348014d3c723fe4755c03d37317b82daac1cb32a6a0a29d298723f341a69da1e3d8258a9f0371fe0b08bbb3931c144a5b581c902f5033760d872b52d30969a5ca7e58dd55c738c7b17e551864b2028322878cf08f809ff1931be0f1675f5717ba3dda9a971a5af814cf53b1506dc2d7c415946b32e04dc8ae5a1d63d40a5416cb39ab5a8f64de3c98a954cfe016ccb86e31a3bc57802ba4f103947a14419b1471757d37e500ff693beef4ac042631be974fc7997f50f5cbc897e2141aac92ed163dfed1a96ca0d1d7e86132da0a77b752424ce418f0335b825ed53cffa145503e0b73ab5b7fcf96184aeed851a3884119160c63b9602d29ed59744f04e32080a6798f0c08b3a1e8a6256ce3565480eeba2463c6394098b1e75cf967c80210e8ca01bd1613aa666b8d05aa17578f0f07baa0ee20ee80556955277c91e8efbc401040bb9de4eb73d3cb4dda2f5ef39cec00cf1ff1c140e8ad6efe7da78eb958ec0b4b368f107d674552ec97d8ff9cf933c4e22f043d601c12211188f36cc9e0752e9d310af7046513195b895149caeb537f451609ac908cd191d850156ad3e7474d1d09177d33c4d9852af2a4de2962db0d9fd830f8c054e51ea69d16386fcab85074242d9d32f726955737e5dc0d3cb858bfcfae9a50130614ef665c45bd0ce0169894949afb17402b204014920d167b11479d4737604931608d7e2bb052b4dddc4a2f1936982047d835e705dffaa967cf75fbe8855042da80777c49f9c05d9ff3281c4468923e58b14c2729893597eac2119784cbe0b2a8cafd79cf26e1c08fae95c241d873ac5018128ba37e3da3470cccb53d13d0f8020f2dc32796bf1384bdcecb013559752ad462a529232f86042e898aace7208941e9a268a2e82ead662ba1b12dabc763340d4b16b69dd976864ab7cafbbeb02b783e50c58f3b43df93a15d781bc73d829d555acaf20cbc0f5bd517ce77c0900 false +check_ring_signature 222a575c2c6f79e660e2f3b37eb2f2cc2b2fb0d5fae32e26db04a9bc76087b6b c3772bdecfb2ebfe2d46895a414ec9b089a101bd11caa5ee1423dac7f336e3d1 32 bc49753d466b2d2441e926ac40b6562569949ad53761f8e44131f306f711b84f c6899a92de0c5cc932eb288ab22ad13b622bc8a013c81410ceb6b68c078e56ef bb7bb13c425eb9c7b0efa550f0526d9b13f3a485c81787ed1de9791481f2444a 1113bc761d53d4000c9d7804e4b99ef1a53c7d2f9fbae014dd176dd961acac3e e2bfe95b43551b8012276b208b671d080be321466e727b35aa072dc72fa6e2b2 4a8c92a24ed64e5344d43f84fe56d7bc472e24209fc613e57377308ba99d6852 56f5f3d8506654635d11b95551c93179bd920bdba10c383e697afea403c2c98e 8901bf5550b3fa56e13657d4bdabc51f7369bec52010067c9aa7cc33a499ad07 258bb2ba95cebaf741f93a9089ddeb101352625d7a89812067ecc1ff3cd06de6 1e8b4eb84051a98889d3d18f9b4466245ffd5efe4b08e1063bc2c668137e76cf 0f4b671e0f142778185778fe4a62599ecd378f5d3deb231f4423469e2d0fa4d8 48b18df802b5f83e6f9774ed67d9e5c00fb6161e8b802ce1688a5713108c21bf 51f43f47272cb1c47fb201388c2b7e919236ea5a8aacc14b0bfa6dff52f5a93e 78c98bb4493a5afa320076ddba1e806f11525eb04d46bc2afb5d7646839e8fda 879ec606aff4cc2be2838f8b29476e2d426862694d079c4d74cb7a60057a4a08 111a051099e7ae60453a58359dc974d1df9698c1dc7e955066e6f57816c026e3 0db1c9b4336c07aeaca38867a1c82a22d0e3d4d0290a57f87b461a7617337a09 55d4d4400aacb066200a0800d1f51b97768e2b73befb5bdfb5273ad89817cc38 81543c49140d90fb08bf6ef97dd98e3e6cf1fe707a43d79dbb2d92aea1f4c6b1 0b313cd79d92b5b517544ce5119e49d4f041a8dacc13097b1657089e36bd2ed9 dc46d1f457c1669b8d6a56d5cb3fddb6f0269991ec30cf8ad6ca6111a081350d 3460d70204a7bce74458209fb0e27a300943892257fcf996185c66a950c1dc37 ceea72a98ede2112349330c40e45e3d1a53e7aea4387ccd1cb8dcb572d93d475 80b4a34eef8cf84d4e2b0e211b63202946efac3c3d0ce47ee513cec10520374a 316777309d297f143e069accb7de984cacc4b19fa7d5484a7faade98a5ddddcc 44926706498b41f717110852808fc1226d23b9f9a1cbff881987a445f2a963fb 01f402693745a39a7db03dc47b59dbe8162e4f25e605f4974a5a2e32f8096f58 ee8dfbdfb366bcc15c51b4b7bec2db5ff2b66d229bfb65e170ab9c5f2dff3b53 5193ebc974765b557bbd96c4a96e825512e1e0c79ddb73daf16aa9eb886da158 8cacc3d8c43ee65c197bbf114500aede191d55df4427b5b165154f58172151ec aa99c5676ed1b6db36b5f249f619c78d79cc496226be62b09e6c6de0fa5fe738 4db8a6a250af5fa42881535310f629f4a5c7e2f1807e67e2941028cc8e7b7d4b 50bc7301b7e21ff334e1170d2c74d1de317f88744261179365e7893202403608cc8d0ac74b9477663b2c8963ecb7cded2ae601fc0a9b864eaa1d1a434ab0f50dbf1275c4acfcb790175409a7f3abbbcbe560327199ca9fed7720112fc9816f0466e815663ed03dad3d7ab06fb2c1e3c5a08643a99363d8d93eadff8bdbcdd905ec619bbe985184496b48207d8f6f4ffc36526ba2f343219f282a14a29003160443de01941fcf6aeb4e39ac20547e1fc198156a69577eed5671a8e0dace1ccd02d4fb176b53a12888c35493b4e17ff6243ed67a096bf5bcf4acf9a50a4b383c028e5980a86cb87dd1b07056bd9fe88f0c6b2ea4942b88cbda5372065d75fecf06dcaf0dc45683fa303dc266278ab39c8764681f8e211f6001498b98042e887f0a75f5a17878687670a69e4c60c616cc25ca5b62969c3c51ab63f4e83da755920f82ddc79769e3fdaaa1a7237237df643757971ae905d2aae39b77cd35997fba0c24f5e3c94457e043d047feac35ff5cf080be2b3f34aedc52981a6e11dbb4ae066b36d438fd9b3b800257231dd5acfa54859c9dc07ea6faf801c6d2c01e932603022957222581a5b0b392e4eaaa102651ef4e73b92e0095a31cc0b67045827702b977a0b2ca293366468a846f9774e948835c2fc1be77c1cb866d922122bf1a022b65901ec92e07ab39a1bf2801b3ceeac7d0f446ae0885bfff00452b5def3500332708504cb74a2aec5c120fccb753720d0eb8d1c72dcc5d062212cd95301c04a499f7eb91a46598a042954e576321c3527eb3e13123537e8a455917443ec90bc32641d4f326244c9f74767dfdbd45e5b8e91bb216cf44af787904a2d36ecc0cb842d131feecee4b92bf34b5d3688410bbc26e8ac2b78675b14faa1669a2350e6b57ea97e898678d998ea076d8f3ed887078a6ec85e6f7659f12f80083b5550cdbba33b10670072754db2722355e79e55c928713e2d029cd5f0d30e007120a0b07c868057df10290c42b4189ee05ef7a62e4e9f9a62e89afc39b23edd26fea0af360b5a9752892bf6f60b1929240698318e7e302d308995e536672e414975b01cc67bde1ed2972d89e571660965806f934dec01c3f8156609b73c1ec0f0156022f7e71c278a33c62e68281d1ef68bc520f1db0b84adcf5516c2d13393c94b807e028e104fb0f56385b30c67d6a1945662536aafb5c8a12244088386fc90df80b1d87b0a56235a3464051c623fb8eac53a1ef3644d0ea716a753fae3589973e0da127cfd7bfc5d31b89b6cbdff628fdcfdd5ab82b5fbbf3fe1bb43724fe688f0111d13148ca79e5a646034c2ca16bd41965522f31812fc065e5f8231300b3a30b945876ee63e23b2a2a43595a452fe477ab1f9319cddd8377cfbab9943904e60b5a37ced0a1f2cc4082326be8159c66f8746a7371408ae7391eac9df41abb7a0d5f8d00c0c17c5c54d726a6f992fc487dcaeb65179b21149879272c27a6d1d700456c614cda0693629db81357ac77ba909caa74f4d25903bd5358ea68fdb4770febb22442dc4d6e5c2565d83a166266a22030bc8f54ba94975285ee8c33591a03d2943323ab090f143254fdb3e254738909ee9af5fdaed44ccfa9ddff5284d701b487b36456b42260345ccf2bdf911ca8ca2ffce467c9544683f1f98951543100b446996820f72f8837c8a2fea666bf396b19db210682709bc8bd156edd90a209f5f222578c8f285b8282a535f0ee333177aae762d2fbedbeecaed063e6f2d50ac3afa5f57286a798bac4947c37c6cd91b1b0214b63af1be0f4ceffb2e1403605e30e84e317066c677981fc39c624a2d6426f357c0642d635a3adc218b4f2b80dcd2f5b2f0a5324c494a02e8f4401a7392f813579ab523ca899e62eda08f50702e980fd051e810c4aeb82275edf9197770488c9e2fef7fed33425f0d7c613c5098c3890ae3cb62f0c2a410fb09413cba00f1488d8ea9d79b23fe80f8cbcb60d01087476798e2de8e2f29267e5e546ab63162168812021beb3b8a4d160b93e150b41820120b71428a574b43876c1a8c1a6b96e23488b3f676dedc316756f65db0d9afd3a48ca27f9a40b61b6bf337e943911c452a00b4a6ef8b9ef854ddd14060350b8ace52794193f2ba9b30e3128903a931dc5af93bacf478df0fe964b27e803b01517bf62bf28e3cc960d88aa91d0ee93d7f221866a3e32df67af0329bd0402de43ea61988b8a82a14a4eb4738705722681374a8339fb9eea1b3ec91cb5ea0b5ccb1f3cfc25963ca94a0f32b28f9c611344c723b81e5b2fe44903392fe39c04f09429a0f66fcc7b152504885aee37a7ecb3b2dd3e34d9ef33b3b479f28a930cd9b955e416976b6727cb3bfc4898806239a218e03f75637abb03c3847e4f0b0a21a6f2cb5fa227eeeecbd94ffd3ed658b415fcffa24ef59734ec4ef8e277ff09b5becde391a2bb6a06e2ffa378f279d78b5f8b8d38a6bd8a35d97ac684e80708d5c22bd8a85d5f1eb232bbbeaa908126d539cc1db32c873019dd250a11151b0afb0d540af29bfd8b0617d9e14b66e6ab4c5c52560cf68d53bc2b790cb682620af66fff08e439df52c8bd932b985a62ce8b9d2a5f02669d371a5dae6134599404549a7cba1ba285bd430ebf4786229fd6874da901d92ccb553a90b80bb060380ee8cc0be9f163c849a819f6904e0c8d2587a6a1b1ef6cba3253606002fccbed0ff6e82bea271791873d2ad4da3277927aed1b835a4522c12f36dd9043024464092b9cc7ec4f5803b09216fb0fc32d7d21303e2ae2b9da3a81cf7d5d3959904605a78ca8428c76084dd02551546554b68b42d2289a451e409d29952872832b0000f7d07cd1213373928b533ba0c91e089b8567973137f7e0496b6a39345ff6510e true +check_ring_signature dfecb98d615fc8ca8b7b0362d7d7f14042af4c3f9d953003225d99a2aa394f4d 26a428509da6ab0280bca51f84acc7858cfd9a53dc411a95df026aeb6c0942d5 2 a5fb13129fdb7c18539294d5b449f5aadb9c9e528fac1a147e00c05f21866955 0ea207dfde0808f4465a07db221065b41cc93673d2fc865f8c054dd32491b087 092c8fbbd04850f7c9dd6270c5c56432cde4ff057f14c0a1e23b60282f05e3dd44d68281dc28806f47a4784a909053ffa0044ca3a6dd22cac0ce3022cdf171eb6aef4a2d30c91878d72e940079074000d10136e188ba5bd76508053feb367b066dce66ec658d7f9be19e1e85134aeea7622f668dbd14c3694186fd300a7c2e08 false +check_ring_signature d2d98779652f32eeb07b076726f006a3abc7bf58001df99655c2e7180b243a4c 0a4208a3509d45ebfc52f51ddce2c102523f1b7040bf8f216d9cb3071141eb7f 13 3e6e75bdc6b27e6c9a913c9f1af9cc7742858f78c148c6e1c17a65deaa408517 0ad7e96f962b94a16a8ca2c2c4ff94397fa5dbb19fec828c265919e00100e684 19b67238d4447b367481ae2fb3d788f929c364b4644a2a6e26655fb28eb953d9 eabd634798c6a00b54265aed2e94790233f89e37b3e889574217f7e6649a9196 9e3e8e47a94507308e1b645e7c407df586adb8003b14f4a358d545e9debf56f8 9fe1db491f8f68e328f9b2d7b9bf3025dcf4e3129eef9005f035ec22ae053e1b 98174cca4962b56d4a574d85ef74e701b7748e96d6f026cc2f3c06773daf9126 d7468f44c1b891157d836e6855e455301ffbd747df9e487ca127a3b24fc0787f 01efe0a008738c0d6ec6c12e2e403b43dad0caf7076b21c77ca04696ead8e21d f82b4b145b081d3fc9a39a74b450f07eee11c5006af2202c622aa762a0947e3d 18a113117a8f7c5bee480e0356c9de8310eeb5a02eff9d8628d2e3b0e47564fe 2211f001dc297e086cb5f19d5cebb1b818bbabe99f39c1270f8434281615826c 73fbb5b90f641ff1b7c923a5d88b98e6beb77e6c1650f086a179e2f241d97466 5a8e68fcef83a22644a58a49d3e86a2737cc51d9b674dd7727642d945a78410e439875bb86b7f6a9f86004c901acabfb37f3d602a6ef70f8b9d49ab86a8e040064a523b504f992f091efb8af064891d15976d69311663b9e502cb2d2775f090cd8dded4929be23272aaec9942de34bc144faf3fa8db66b360e03e07b08ce50020b779c571b2551834ce670de6c41df6f83141b6db1c649a64d54e3010cb70d03a8e3c097031ed1479d75d4fb21c0665272c07deff2bb889b3ec04303fe86f20cd620c8881fe817b5144438c17e5f991c87ed1a3a45f2e5853296b21881c471072d0e695504677990af238b0005a185d0ea57f5ee9f3f4320b60941eba86f560855356c8db5de2c30df66e97790705ec26a1bc4c5ff851577d875a4dafb548b0b853dc198a166b63e018e9144ef82c498b61e84aef21871d1afe226c9c912500ea258f1bae781701d07c67e1de81797d0f8b08bc2910f9cf1d905328117759fb553a9d61c9d0aecff8ee83e65492141bcc6286fb9ee510d9562e71953a7cd16008a443d946c2a156f6e2d8252cd3b4e98c3412ca1acb2753ca72dd11ce82b350aecc4d3bb588d3cf8598a25ee6735edfcd527a6f820ef49b62d78016db155a60487e6e6160c2bfeaaa29f77a79ba00b4229758d91f9ff1daf02017af246fd05c002f7b0087e2e3c9da38e64a61192bc1fa44be9969a4834b1213d3d218ff13c049d8a0735d79caebaa9761b5ffb5ff31646c5ba216134ac97cf6c95bb8eaf100ac28b2101cfc78a306b2b75b530a6880b9576edb22026198d9e69b514385ca80a0689a1be9bfc7e52c20eaab921731319e070971118bddb010c7e9e14ea763b0022e6219c00d92ef10629324705e8765591680ed9f338d9f7804f0070ce351701e987947c1f8e3f551ac02f90c779248d152b10dc40cfc5dd360e0d5cc6f7fe0d9a6439e05aeb2e5adafca787f502388ab62a6c090c85f7bc9352367cbda2db0bf28e42cd6619c2cb7bd9e065ca1dd8c6eade8e6e4eaffccee7d664485fd6360315d32c0b1bae59a698375603d9c35441ddcafcace14bd6be510668fdf3b3a30952a37027825e34c54299d76dfe04a031fcf20766b9c4f5fd1cb3bfab7773b40429900ee6f5197fa842e515439f3c7987750e33fed9e7a0a77e075e0ccf46f00e false +check_ring_signature 8525c78e75b9d12b8cd23f460c39d0df525fab3323cea648cebd340cfb42ee07 ad3d49326270b9f0e74d8ef537cac6000cfbb634b315c9bcf785df408916cf27 56 be5dc604e64ca5e6179f9c750647aad6178d5c02ee3bfc22fc4813a17c7933b2 6c1f721c37475a8c20bd4b716dc699f7da3fb0f117cd8d35a2d0d96fbe1e7b73 fcb47a36ca6737038752c09a823f6b16619a2f9e134b5595603489710abf5fae ba6780cc63561f43f2e5e112c362c0af6deae736283b0ec1abf7f31445a51a18 6e208d96a9dc39ecb287bf21ac492cc5ad5473c67ae77b323e06fd7bb453ac75 9a668b0568889c5f1556b3d05afba82673901cc450bf03716ce7333af2de57bd f295848a6b4436cf03993306e7be51921c9cb5fbf67ba235d235d2a842aba175 848c82be05b5ec8bedf0968d9beabe97530cff358ea65eba910b78b1b2c05bcb f65bff894f1031e247fddbd67887e52da851e9882378512af0ceef5488d6c1a4 725ecc2d0bede527e341ffa8abafce8c2811de058be4a66f778c63a3f8b3c6cc dbd3bab9b3d378b631b352cbf7744b0b74cac745889d5dab6711f14982382b97 9392d70fb741d9b9d2b71bbf573b612ae656c165baea0ca80343a584d9b47e8c c8abe4b3965dad5f46c5e0db9a531e13f127a0e618416a7aa94954ff7bb62a92 6df32d5da13571d960a1111c85126cbd2c148dcb237a29c0bf00265c4dc4d01d c0fbcd53f703f150febe62dc1b1b61fb33a6acf6aecba884c38472d26e3ad01b 6feeafc77503f6c74d3d4d1d6408569be51cc1d9a559a7f6b506e6eb0d962f5e 0293f543d3c637d24ccdc6f4dddf21f4e8699692ad3425c47829c0f605f45300 248a173fb82ffb3fbef8ed935a50d111ecae4418b97d31019b1423ec74a56251 8bae8bdea9a1b71fc7c19cd0e46ea717a9349020de06460e5e1da6d2d94e262c a939b44c7bd7fcb494b704f6ca084de420860358bc99e798cd9e0ca0cc2d9a46 779918841c72f40fb42ab90ded1ecf39e6f32e9729d8a041fe1861709ca9c423 d5af29e93eefde33dc460006ccc523c4fc318c63c0ab7e367ab17b144cf5e4f4 e06e931db1ac8209e4fd438c5fc2401db78bc5792d7294c2b896589abec9cb8f e5f098eb22e3d1add725bc0775db9587de38542bc9c5fab1acc3faeea8f3f68c ecced9541ae0d05d0f1a5c7041a38432ef97da9ee5ab171095c793425d2d057e f68ca0b45009c64d1d701d4c9de1790472a34b21ea82d60ccf0607b589cfb202 d7c46b4b081dfa5ca83951df7b3d0716bef9adbfad77b107b04b0d28c9f8e38b 1d9124f33c4e0fa0e4e53f163ba6c120c2f14910772360ab3bb6c19f393f71ac 9229d55d24f596dc5c469a964465e1627eb6976039e88b5976e5fa79041b1fe0 81a469637637f736d9743288d4747374fe39327a8009f99a59791cd9a49ffabd 3c842fc831847b62147e457bd22c526bcda2bb6a36f785a775e487f8463c5fcf c2203e705f7ae36b3078d6122e3faa32599080c821d8ebbaadb9bf6eb79a1513 ddb2a4de0d37446e2ba7019d4f204982b1afa61460b56a916d178aa7f2c3ef8a 160d4a66e86895c64a9b9f130772cf609584f98903b70e03aa58bb4718d635ab c1a753bdc79bf80a79d149ac932a1cb4eea98eb2381e577ae31d3a6645b5ffc3 8be90bdaa179ac84bbea3982e5f9623d128c1248ecb2d580891ee5c3705ecc3a 97c2bfb95646b0e9b6a99f9c5d2113a581a9b69317015a2f7951837e58f0a02f 1902c1a96a359d86c6f7c3427c251ff3c1579b77ff75d1a761b3aab47b292a55 3f48ab545d48fc1c90a2d0b3ce1accbd6722b0a528c939bc915918e0c8c4dcc0 97d9d7b6419739359de8a2489da938899085085eea04239b156f121f8b18e796 281443db4923be53fc372d7fab79df0dcedda6a55f349400d3d809b19750f59a 1044a81562bb2dbf4a0ca51592a915497e0fc786574680c98b3178564536384b f72ce9ba0efc81cf172bc6e826f4a6167984411c9e996d8a85f3a18297ca8698 8f7c4bb7316c8fea41c48b86e5532765097c5a9b5f036915d5719e8a556aaeca d395d686f8f3180b8040552024c387648c6b361e9ca07ebe644aedfec9a8fb07 15b598a800f901fa7a723141cb372f15632f0627b7916461408779e44c923f4a f5d0f84b901b4cd465f4ef1548ca537cb2d109950ac3078a891d515d6b39d8f1 c0f571973043553cf29b59d05173f404aae339cf4350684c0de2e8897a738722 f2305a4400f2d2c646c5b2ad5e10c08c033c1f10a4fd4b160b11dd2542e95cfd 7eeb9106b86e8afb2040e3406762c280cc9363e5de05429781e6ed90cff14e3a 96d4e94aa86a77b31cf1ba16c58bf749ecf65bad74c8ee7dbe3324049f183cb9 6f0d722c2659d4d9ebf0e088390a887d973dd58d3207e5a5c0d1507447d14efc 99db0d33567ae77475d7152cb127678cc88174f044f6dfad72757050276c7228 39e717367b52cd593ec9404b9325f77ce7d2b4d26551b76e09753af9db9d88c3 26356de6c866521c9c99ac3009939a77b5c1c0e4917e121bedf1aec7bf8549b2 b50293c91d30ff81b4ca0d3deae2cc8ccd3df490c29f12e210713fff97023455 f68ca6449c4f526524cb3f2cc13923147708ef77f1056af4c23901b938b0590b9d33c13b67b10ae21cb0132d733af0d34351bbe0c63c80309c9dc68cc45af90cc28eca9d1782e9724794f3cea7a3290fb0dee66a5a659053320c5d1cd9c21b05ffa40f1e6903ac2754d53bd540aadaf60b2f179bad8b2c7827b789677f94b5032755067e78bf3a3ed59bc41a61c405561bd6b872756c3a08c7c0b7623a5fb60ca2100d38c58b5e1a71dd888a0528446e7e6937f4aa4056936afd353653b1af01dfbbe34f5fae704fb48c907de215fcb6de46387e9054fae542db5cc6951f600744af663ea6ed3310e2e8ec8bdaf6abeb50722e94c7a63d1ec88cf240b75ff80f6b6bd1bed61e1c9c38927d2197bee210afa77df26b0db5b8aa56cc9d0ef29c02b5ef0ee32aa4f6a4e41d8ab9e8a99d0d8bd11a8d7b88128b5199d7af6e3bf90b54aa170177c048d4399c594982bf9be90074eef56fe858c15fc0a4561b67490debde1f118c1588eab7d63386f31edfb892f08af18a8b00aa031c01ccfcf7da01b62ac80ddc15833803518d41bb14d3a1d0f4c62cc37831f0278e7e5a89dcc80f1d12a6e16ec5ba5acc6900285a2fae709a865f7c0cd4797da381b81ef8db600cd8c4db12a4e3951facec1e594fbd3537a22c792e5b8f0b78b60dee37ad74780e1f51dadaccf3e3bb764c08909b5485176b4919f15141fa39bb97ae82e593850422fb2b0e55422410e6eb9f40d49275f77d63fa6a721ff52a60588a621ab99808138c662f9f18f8e97fc5ba1e816c57f5f187ee81d5dc009b9071fe4023ad74072586f88f890b4a549c68ef8f33810aac2ba940002e7bb991f0b5eaf6752cfe00d8baae9591dedf3d2d88f903d792e7768e45f11719f86d94547959794d13e80911695542ce5e4c5422aea5145acc0c6ad753f3431dcab7c6564b5d690f34840b90ff657ad03da8cff150793914010a798cbf9c781dcd3a67ce1297e4e2ca0c0fe53cae992d3eb71f8f1cbe1eaa4ef3d334d6dddf3dcab62954b00f01bd7b5d0d7160ec1af0749926201a3b89919d93279b027b91d2c5d9529465889e6a28670ae6df776b89169776f1140759cb67376a9cf6b1beaa94c7617f6de761d830670917926363a39b8ffa16f16df809b7742a012f40e5091470ce72492f7abb88bb054142576a17172ccb67d69de55fb8b283cc18d8780124c2fc6627e482fdc89409d7ebb62f0a2738ef1747962791e71b72404f2526a43cb5f77e688b6c9bd0b20d2930517cd1220b03e19b836c72ea4dde555c7f98361f9a5b86a1e5dcfd045e054f080cbc353fdecd5d107b3952c0b8d43368d22805be8e51dd1e106daa3cf30584d0461b6a54de16a5d264866bceec3c3255bbaf4d3284e26851f4dadd65e303e6e81d0d3a30ae8eb3322f1fd66c0ae4b1b68cee3169978383ad39fc441bac05027db0a878b9639e6178d7b7034850c638540d6a64f3cd53940f9b98529a4e0d8c251d69928057e89a5cfe6e39f81348163ed630f6b8bff2935978b443a94d0a51c88b656ba9fe9bdf60b33620b2a9d3f791b3082a08643e5574889a9a8c970170f3a2d612c816dfe108e6239ba7dad2b09d76ddac492864a9196fea8284da051218153b456609fc9054a16186f2b9a1431da9b686fefe5f145110ecfb5b8a0b9b76379f790481f56ce2121d8475e73fd9bbdc042d3b11eae9bc262e720ba806fd729b528a1d2e00571e28dc9ab4b707ef43e1913404285024e05f20531d24086e17afe95b52dfce3da5be1ac739a013158cad30c38eeac174bc3afa9fa99101ceb016c84be4f3628131260593741a1d6267f6d4e3ea31016ce997992f41150bd026ce5686a390392323d7a5c0f1eae96cc22cdedae4c8ae2a3ede54b10e4f03c0f39b2eeecf3e90f11e74a58c90426e57179e0f905d389108c9c58fef5bee0ce3a5f0f6f18524670a996fb6877ae5680e0439b03482bc87fc6a9b72bc72860c221a64e84da74f6354f3724ea4adc050f6a541954076fefac0f36c37eeafc50c638b3c75f6c09a170837dba2564c8c69d78a475f6f022573260dfbb1ab3b96032636d55002b5d99564eab6992852ac40c650cee9ea220177c04a7c66ae78010a5b41cf5cd24fdd5711840f72d44ce364515e1a25d365c7ec9da2deae0a28e20463110f822c0aa71c702ef46e17b4d2ccadf749baa2a00f613d70d0185d23ff0d74b38f66bef0f040c0f07d011d7d78009590b4bf5030e1010fa60c2b944e6d0ed86ccc6b693b8512f4a8e60a5f4ec20389054c1997eb9ac4a6810bd39185fb0fa63b22bb6921612769e46532fc81f0f82c87c401bda41febacf5b67735111e0dea6e4d7d42976ad0685d5a8c051065f7d97d63f5ee37db318c25d4a5983500063e33e5e6905dbf18da80c518fbd04094fb32e3f405fac157fd2576314d52e30ce71ca318a6d24637417b405f043d3d6f4c5f604b2780f786262b8fb17412d9048854cba55a3a7b85ed8ddf1f22ff747b98dd121b888e5c9ac05bc41e46c4e20298443b8ac530b46ebf989e8f47eb29480b29c6d4aeb6b7e60538b1123851760575479c85df5dbe58b38faaf85e20365071df15b4a6ef5c4b5c64f3f6a559890a4db224af9ddc706c8d2eab23ff65285a6c54c3511d18fa6bdcec7b910d937c0eb8d44b2eb6947d9dc781b37c7443e1462df72206ddb95bde2cdb9e4f370d480516226eabba2e2ba01e5903f20b2b1ab5a0d28a4774eba3e3053a04cdb8527403d008dc18bc8553b4598bd8e66a837eee602139a28226a2c8b5d8523988ece30d5cdec0415b00a9d85e558006a3eb75dad06ff70db2ac294023d12bbfd5ba840079436f8a396c28f5b7af19783d2338cc88c5f6851d3f0fe2a38523334d53a106698de359393558c73afc4b1b66736e1d9300e76c75a8e00fd50ff85af7b22a088b273b59353e51ff6128ff65e5558f8cfd6f5e789c987f534534f33e712ddd0c4b722e2cacf61aba770211547e8f1552136b65beedaac32d3cc134490cb9990615e350e0fe6d3088ae1892ed45db590bd059f01de0c59f6395b06c8ac7dc4407fa3a2c2b5cf9499a8ca7a9f97fc51dc706472b12aef62f78362bc81a34e96b0d446594d15085e7a21dc9a7707d1b360e9efd2767170cb5445d0fb73d7c94ec0778efa79e9859f50d3e07c1fe386dd23fc0efdf07f7c9a38c3fe21826e788540c00a18e34413478e73815dc761168068aa126717ec519af358d0b2535ed24a90c4d2acb50393e460ef4ea7f3eb3c9b5ed079d9cfc6240c8be056a41310018dc0ffd535ffef87a7e6c5a4ace421e925cdb1349530d59e34d898bb2a89ead2e9805d8a910f55d88e6fbf424bcd7a6116166b62f3c577cbfa775c7d6dd787ee2ca0b789bc56125cf8d822fe45283d7d3169c1c72c719504757ae328786791317560182ebdac229ba229f02b44e4118aca769bb87fc86db37dca0050bb679f44e68029d0315c5d1744877013a37b5fb403ddfe1314b16203c531a705ba50a8c57ee0554e0de3b245841a28e4f2b29791bd02d37a6aee8571a09ad8748ffa959fff60dc7a391278cc712d1d2f4305a9b376fcc654b043dd24249c9b6d3b03dbcc46e06ca4c74c14fa4ed2f82cf64f5ee891296bd928b41dafe530cdf64e15d729c4b060be551c710ccad716089fcfb0369c19ef8a3bb5f22347ba222c88f6726568b0bd384c85c14ced0a8731bbf5c87fb5a434f4e10419daee1a44a41b677489c950a388931a83457f3ebdaf9f3e47cafa090d1f5c6bd6e95e56eebd37947956fd4014c2611d6f6bdf68ee00bdfe89a781fc835837c5ae3b5e3d534c46f3516b7ef0b4e013b990e511ba61f1185e088feced41e105a78c459a029b8819e68f206ab074c057e64c66991f5f49cf5ba29c424614ff1e67293cb9570ec46a72cd9482b076064b84bc3b8018a4e18c2fa57d409ea9fcbec228c56305da07e78fe7ddb87005a9ba8df58564e2c3958bcff7e63212a994a58b624df0ed956ad14ccb11a600a133e1f595e1f8ff043965924fe28076f92750659209be1dd461d2c9b6e15b50221c02c10b4b4b8687818b421660812edb2a5269f45e85be138951864d266370fed1316289af01433f83e49d015ac07c8cc667bf5682dd6f7ae10dfb946305e0d3de3b12e86bc05ecf2f03b39ccb1c6fd2b0c872a63b4973fc23cefb145097e0ba8d9f232ea4f2c3c506fc64ed2ed6f5189bf44e6d552cb0e1b45cc2736b376098b750601dfa45b948605cdb8baeeede773c90f10cc29a7a0c05b90cd1ed4260c2eb7910b0f822c77f3e6bcd9ee5740abd41d4af3e9664d9d2ecab80b8ff13c03c4faf9cd435118a96c42688d6554088561c1dee2134fd220988dbd98208e860be41d0555a34a99f718207d8f176a8c7c04dfd318838677499d5b1d22e6a7c40b40cf11afb55dc546f6dd81d6795f8d50bb1a20b7eacbdd521974fd57e1f92d05abf49215e2c722675d8ad3e4ffbe2599c3f053559f02f4904f91a998dab3520ba8e7bb69afd6d2db1dbd88eb38180fca63c20e0f947a56133cc84f4538cbb60fd2bdd12b88768a58e43f7f47a9a756c4e4e2e1476533f8a641871f1c0cc97205c11ce16a3e0f0535ed456411cba97a96706c7b23e53279a9443b12d4d108130660cabeb63f66e7e355ac221b034fa4e780a0980e2f2fcf2eaa68736f68799204001f125f2c6e94dbd48f4b5733c484ea891b17424455e0e36f7e25feccdc660b4597ca259eda842dc0e324393874809a650b98b427d1598ccd120b49a5c3b80c94f605e7ad41087ae4acd7d366a6fe7794c7fd51aede61c7edc0eda3bd809c0fc2a93a4da512435972713bed3d54ecef224c50f3a2e6e4dbc3d68b28736f3e08361b77768aa2a860440b50adc5270fbc83c7ef0b7dc3bb2c6493660144abe70739bcef123b64a8290a42160bd26367bf39cc640a73deee39946f8541eb4e9b0e1cba1bd94d18e453ac599b0037b60991a724093d5caa7fd9a04a1b7501d12006b4c7aa7a3004ed56a633b99b18d0730b000d07290cc8549f99041349f6d48b0b true +check_ring_signature 8d5a3dfc524da757f7e04f541f08ef79bbdc29ff945b27223b44de095db4576e 0bae9d25e3d5cbe04cb4e224768bd4fcc15ca5f628bedea7dc86ed19de2dbfde 1 04df73de59c0d14fe8dfea4966a5393f914ca064d9f86db3331487876133b7f8 18f638055d78b2271fa5deb9ded6ced528a98a1ff65d5e167bade0d36b4bbcce3693f9f411e11576455a8f6f11d83cc868087b7017ff03a5e06459b153622500 false +check_ring_signature b76324ce9e1ac5a328f27a629bd51867a9c2ea51ac5e248eef1e96c164d4de90 9b1e5e5a6a967bb40ea6b646abc43ce2c4ecaea1a24a31532cd13868710c4137 2 82df3b5379ac70cf0ffc99aeb6d6fd3ac72ffd4d940a16933689c36cde6371e7 39bc26b069e35b755f97d0a12bccd1b61cb152fbaa6dffb1f288f2405ad54c6b 2dc942172056bbf8879610f06ab3eafc319f955704adf81d21ec8ea95cedd70e59d9b677adcbea930c75327a2ba87208e101f9dc773789cb2b160a595b4f7c012e2d9a833ebcb81967a108ab4e543b3a93403712fda2f966be8951098419ad046e851bc9946cd19ddbbcdaf6f91f96a39c42b3024a2b0f1fe23843ff1d98410c false +check_ring_signature ade3f0e9962cdc68b67f66fb6b1865ec0fa362a5456823a649ab7db7a8db0f8f 14c63e7bcc8752683ab96f4c25087b798befcebc838ebf6fd9ccc1d10c81aadd 79 29f6b8035dc9de1a4c48ea21f0d6a7bea8a95b0dd582bd334ff8a846ef231d16 4bc43f9e0d91c88935a4782b4b28d4152b22c287e8da78a7bbd04060d3e346a1 d353d2769fb56d78547d8cc10dfd6ba9ca81e768d67eaef8a545196f4e6d08fb 5c9c9de8632d637776a49b919f33cc40916ff2975fe25b3370dfad4ccdb02320 d0e97789ae0fe27965e403f1a0f2d9b9b788fc74aebba00b82005fe9a4bb6d2f 50a6e1c35352e3fa1c4814b3da1d63325181385913fb724475c56d3e7df201e9 a1150fbd896354b6efdad0a5fad89bbc5cab3dc74395f694b074f4f69af40da9 0122effa44376dcf019b70ca02abbba064cad9fdc8c11dec7b8b8c7a296d5266 a9a460794061839c6c981d5943bcebc7976ebf5dc42c0ef3c1b7cd3938ee0a49 5c0cfda1ff5d5111eed6761e73da35d2623ccdfd719da045c34cabc0d4083f2e 7309f5aba56f135948d7f718773124290b160d6f500c3d77e8c54389a1ef1d8e f43cf4c8bb527751e32bc06b02d414d13fab45cebadc96feb918b2bff45d20a3 3fbd2550bc55338c1cd92357203edd9bec745e5a79d6665720d9875caed77f77 2a4d656b8af9e663aa1764ede86c149ed5b9cc82992a522aeaa3a439a8deee89 99e962e995eeb411c1d1432ebfef04d3bd6f4b9bcb4f23be5b639e05fae415b2 c0d1cf9c564c849243b7332b2c4d4983868e3c779a0d6a4946af0af653f4c6c3 e1e393ed5b5c37844d622e30371497aa2ef7bc3e4d50462596b8f6ebfbe358ab b7179534c82043ef5ae73a62146bf0a11a13482bb95e5b82cafd391fcc4a02a7 c81fcd4b3bb1f6da612527e4761f9aac72fba551f8e9eb619a1c39d1f6fa0060 01f4dd4d2adea40e4a058ae767e9edead738042ed40396dfbaa41671aad0239d 1be5644c3fb8d844d82e74512ec7b792320cd3a6d75c275c85010a926b4b4e3d 2da0920aa3127647a3c7cfb975dc2c845a178e933e9be02880b98026a665aa3e c07a988ed7c344fad4c9ec0fdb620d7ba8febe45ccd2ba724faca8c7799dc019 3124e0f4a7287f61eb115e753343014b86409c28f1f7ae703dc94a1a91993f46 b40ef3f02dcc4173bb961790ee114723fb669be2fbcaa9c553809e7eebfc67ea 389245fe700a727f2837ee9ba2505fffde8b90e2a48a6a50c12fcf03ac339ede 523b7f5919bc72db84f1fe3493709359b0b548c3a04b4c503ab105430da385f9 36859a9bad39b2091fd40c0d0299a9ae621e557a4bd29fe9f3b998014a2478ab 8c2dcf2a60545dccaa7081f203585dc1faf175d4d4c40dd50c75465dd1588288 cd030c5756209214f2561fc3d491398f88c84e543bb791594cb40d34f3e78387 a7292018493115b6973d9b6140ac17135ac906892453fc753e5279f4d12ef8d5 41c77dfdaf6fafcca392b417222e300a03950e7545c644018a0e6626b6bee6cd 84e3ebff1c213a79bbb1b029525f187c288392be5ccd124ee541d74c9dd3c517 71bb71c2a478f98815512e0b9ccaab34c67dd98cc1401163f5a1c31283e98940 a3134a126ef1329a6f920958333cb5fddab4ada784b14322da15cf9c77845979 1b14b756919f90cb8504e6231967884f3d83dc020e58a873b01da641105ea5e1 da5036483e19d0f5e7ad70e32c7c6f84f077ecf76087849fb29710cf3cb7e863 77e54cb9b4488a122f177069e1a8c2db6ae6ff1fdd34c3cd110f9f1dc59943c3 610957cd1916c72f36e45970b3741d091fa1ec87516049d7d9915ca1cfa2f79e 419f7b4278ddd4bd14d16e1111dab3edcb3a60701e1bc84abf41745714481d9f ade873d9dfa3de6cb14ce8b7bfde55744c3a3057e1eab66ff353a94d16248064 f8b143b849f32a84903b5b0f0d31cb751d4419838c21eee8fa35d6506bea97f5 fd318c9ebdbd81a20573fe746aa35c644e2f12b9dfb97269242cb4625bff03f2 e340e7431c0e93476c1718510735286b9c5b9e10333cccc9d276fe4160c08293 a239d060bbd8d4c7bd7400e098fd177e13759a6f552545e90c4b4b46830ebe14 db8c06a9551a2c9be5235c646a002abbf18114719f2d2f8ae5d52ad355e66d8a b3183c6de5b9db00f6484318ae765dc744aac458962983f753fcc6c153644962 c5d73ad8fd4b30511d90c7ff1314615de542b3c5ef5cbab07211bca6cbb23cc9 43f70faedf318f9becaf55b2b71b61b52fae237baf390daa9112bb630b77638f 05c79758dade3932271e66c0686f3f012ba8482f775ba0a8bd53d4e75f700c77 96d7d136f8eeeec642151f0618ca6780dcc62dee6ed1a51274e61658851dda29 39f5d485b11ef43d3712f7737c6258cce42de2ef78e5c4f3a754b4a67027029b 12feeceec5c1def263d0a92c1957dcf8948132d009bc1e603678ba16f5e14382 b92dfee06bd9e671759e677bca243b91b0ee469d053c47a01e8b83e730c1057a ebcd4e75df497244833ae742439e7cda100d71f8d47f0e568956777ba082e9b2 09c313c6fe9ecc2e70cdc681e95104de32e962ee5d97e28d4e0f3e4edda8b741 84c78dba9d9bb3da0398dc69145eec4d19c916143de49552c239637d717410e3 9bf6ad099dabc1925c48ddb395595a2be069eda0f67ee6ab9b7124394475cf44 d31c8d860e29c9750877fb761320eecfda668942f24e91b98336ff4995ab6be8 a7b113dd2167c38ee4832cae20474ea50d9e8b58e81765127d6fe05a01a9b3e3 e8056a2afbc6f1208e72bf5e1253ccfad0df0fb483e75a516f83b5d94662c98e 9a15e0975f3ebca073c9e9435f02850d458b845e17c9063e2fde6653391f344a f252aa622fbd991b0fbc667c7a87ccda0e1c8f5dee4ae2a14b9bf95e3a78035d 684fb5c58ab3b5df1704c92e7baffd325922f428f14c919bb51b82840bc17c97 f3300cadb1d959d84718c6e30f842dde54209430814612deb512fcdb1593bb79 17fd4dc2d208c268171c68704cb9233ef0e67f24496b0c57d2f8ea99df3c2da3 9929e8d8edec61b227bb2de027ab7e2cbef63688e8f4385a69fc5a74010a066a 22be1578330143f2fe732f7ad86288112ee60c44f2114734c7edac1fe3f32ffb 20776a59bbf66fb245f98e07004c5b9b0d2bfe37e5c8f6de49455e6fa1a73f0b 718e54cf963c7a8991faf64f59d6900b5074afffa2549108696c25eb16cb9e82 a584bf86ba37afd9cbbdf252cb60952210a39c6218b931e839f9abb849640b56 5c74b0e4343cbea2a49a326464a4a4dbefb1ccc5e0e1890a2e3327a0dae2cc32 4e25c9f82feb70cf1dd5aaf7772fa335d91238cb48d0106967768ed36ce05d9e bfa0197cd50764e75964c18cc98a5332303fe371872e635809e479b9b82e181f 353fcc9996ef2834b9ef36f6ef137cd4455e64aa27234a8970701e51b8b00e23 44fe5e1643c578ff0403bfa6fd5078eeca410ee41e922107275127d56450f083 6a4e74eefe9622e8d447ec902151549b254a11adb85f7bb7f0f22833bc9895fe d273f2731fe34a9cabefea8894164690da36807308bc3268acd877b9a37c69af ad7a9e8c7dce11ed491b5b302c611624282d7ab6765c7be8c23e31b921555549 5d6eda30107c41f1b71a5136d188e54ed036ca5961a72c1c546d9efceb92b2069190c567629e54f4511fd46926413fa4af45fa5bfe923c81c7f49ac448a11e08ec12e1a4ca43e033410edd9d7e7fa93ac9a1f912949d6a031826a8c76431a503efe773ee7a90e678c1917cb291d9bc57bc49a2a0ba14a3cd465197d9ff0e100d52745599d8b6dd3eed0357ba3502a4965062fb503eae81f84105a7c8b85f090381c3976a22ba0d1b997d0f72dcb8947c02dc8e0a00c66e7a9bf3c7605138f90639eb765d09de2d5715875ad0a0d874d21a138dd3fd5f774f1de69028e5ca0205b7f7ee2cb9ba338338f90be9465eabd011bfecb07b8af652fb957b6b1d75320955bf8af040e190bd7ae802e7224d3bbb15c0469df61ee057ecd4ffa269d3d306e8db1e0f2ae2eda5583cf00af2518be738a85a7bdbb651a1c06f5ff60609f00d6d20be5ad9d306c209fbaf2232458b9ab03691fc4a68b4b200cc77fb2adddd023e02c4fc04e25c36dbdc68bfc933870b0fcea259a075792f83f83d6f9af80a055553b8063e00983dc7eeb7ac2dd85519078cc6d38cfba077a7a0ec5f8e10190960bc42b078e43c20a290ac24fcdcd8014bf4d2a34133aebe89f49444195ac50825760648f2d7e33814b6edce98673cb925aad47b8d635b6bfe0f645f5766f30c69106fd742441f7341ee706d9174d0f35c6ddd27e9e27c327a31f0d507b6d605a340a049a410f6dae36017462fd2fcc73cefe83fc293e3a805b5501a09c03c05e01dc9e90250f90959a3460163126b8496dc5f77d1f7640169f894cc25a6fd02c368e3952b1496014ba1a124796b02c313e419a818bf8575ec402e295962e70dba0496334e713552f0d9ce46d03ace8a5f6bfbf947df3de9e2308db4450ba102a48914db9b837531d62795a1554c27693d132cb514fecabf332992e2e9042a0d2ef1af311a5eb26e27178bc3f0bb89fde94133e429116abda17b2b92b73fa002d4556b4413681cc133647a6a9d90784ccc18e68692c5414349575a9462f860068b2d184fec102d698b40239665a4d2ec9a26750e1bcc69008b2881a1bbaf9b0daad1cd995e4ed4df3d265272242d109ec9f098354c9a9ff77ddd5d96c24dbe077f3548a92318260035412baa367bb901745293ad1adda9d1bc88afe6df92630c2e92d19daaed8f9025c555b4e37159924e9285200f8d17400ab67be1e28b91065f097cd752f46a150f24c73620bb24676e895bfc27d799fea9e7e84169856c0afa98a2350a827559866b3a64e754f8a3775492f62b6099ca36d1fc14b1d5f80cedea99d3c3f8272d897d22df3e11f8c7c0a18573fc62752862e9bd7c9b439f09f27cc5788799a594028982804011eb58cdb3722b7078d8518df501c8751ed4024ad3e515150bca9e10caa1e32a7018ca6b110a5d88f65bc72daa142a9ccebf03d23c01de00f39d9eafd4f34ce1f8161389531b7d3dff049b1461f7197fe4f60b2dfc7928acc5d710544d669b64def0ef7c391023ec080914ae495520c00c2b0b82f47e026fa905488b4c1ae70239eb858ad2e6290cf284b95d2d596a6934c2023f5a6460aa29fcd685b4fc89e1e0b5d49a1d19d69bb9dcec1ab53ae70ff09b0eabec35818e1b60ee5a5107581acf3789f920bcb8c8fbfe38a4dd101c0fca1b029de56d7a251dd41c5355a4a41165bbd8d056f57877fd4ae68f7c03dbe16a250d788f5fcad1f1bef3f34d19158ba5ac5ffb3d595fc3b44fba5ebdfa20b5d1b70fb11a0a8c208667c08a3479abc142dcb09695032fb60ecda4b0dba51e368bba0e57e4294cda597ac2f0796235f3e2197bcd19fbced2d35112a7349497c53a0306da08e41d67b5172dee20afac64e0d147ef77de3568fe7e9ffe1ddd2f9a0d1b076c07056a50be14dc165ff935cd22eeecaf94e9c7fd074817768e9ae9aa8e2a00205c349307226dc3e1dd12c21fe11ac71da822658c432151ed458738471f210ff093bd1062ce95ac471ae0990a1c4887223a0d22714bf9358e1a4937ab4add05699b5eaa9b0f1aaf5b0e1b2e8c5aa09473f1ff02ceeb926c9d7ec19cd356930e74ec31892247468975d6885e95520488685955f6e2b3722298bb1d37789e650b22dc61bc5944ac39ea4c500b02658ab6f799e96ad8803263acb89175cc80100d927d2403dd78854fdfe24e6a2e7bd6e231ecf9f08014df31a811654253a6460dab62c1bbab54fd5e60ca464225d9f66f848bdae97cb7c347c642a1b19b3af10f30caaca42f32da48134460e831739db9956e009fa3916da6ad3a23c1c6f17d0b44689d86d9e85d22ff53afa17b4cc0369939e8b8fea08d92977e68ac02fad00c492f4765b426b30f962293797d1dac8f73f5fb07500d3e3eb144898e4c3a3609663338bdbee2336565aaff031697c4541167b77daba5229566d26495a106e200a5cbd19cde371b79d2c7b5143bba161982095c445e90d84cba4a744f7eea27004d861d31d35f6ac9479804b5398ea71f0a5b98946303ccb7652d4e51cfabb30f5863b895c8c1ae65905c77f012ef749eee25650842052883d01b15f194659505fa334b9704093542291ea6810b5a171a52ca3b6e477b36e438e83699b1c3c6041373e3c92bcf42898c7871f70918c2c77bdfd1fdfed441dd8443680e88869202f9511db1d5ca03935cae9fd9e6e4c65ed5eb2e199a2c99d696c61381adece60786d6533b0c95c59e61a8b70f23405333c6188c6947ec9326ddeec2ef8ca5f30d4307cf4ec0187efcdde58b41aa6c85bb3f98772cfecc81262e0dd94e450137091b231887a8a3de9cb4e31e1eaf45940cb59b47c4e4c367e6401b8f26144ee503492be2a626edde51d9797f32392a88bb0c2c20d21ef8c139188b6765c050c3063ca88d6c7eccb5390d191b42c34fb215bee5f0fa12ad0ef25b32ddeae11b9309756e7581b5b02eb09e4a13c2b2315cdddd9371e166ef6648cb75f90ed4207a0f595b0f89fb096920c6803750a0b55516e4496f9f81dc25acfb824550b92518034506fd3352b0ce49080b00821828091cae72ba0da06baeea5bad5e553d30150df0f4aee4ed425dbe858ed08c138b99de9c741accb8bb24cec21c0364cf1b920a096e99c4f240ad299030c395df2998225d5201bcdfac962bd6312b5b3ffa4e0351813d7084ea1f834c2e16283975640458586b4dd19da732dd3ad42563116309af349264e54564926b4d0ed39d48adeb197276cef6968db9e253d584b5ba270b10543b98f6fb5a00ea052cb3920931115a9610c76e446d0d493a48a7e1cb34048793de8dc3073b6d830fe7def1047def8fd38a16238f50337630c2e49eedca03ad5710759970233134eb84f5d122466ca393cfc6ec464a8f104f51b5aa5a970ea97dd022b489f451e209b668d3d6f3d6633af7528cd4449520e5a76513193a0dcb4db627aa8cc0ab3445c5277144fef8e180d4d17bfc7ef2966d0c64b8bec907e6e8d1b77b46791580e3b75b338efb876ed7ccb4c424a3b3ac386b97251d200bb83746bb08b0e00117e05d3f2670be956f568861d968681929fc4ceb154a960cee656556f16206952f4555fbfca3404777c42a73d8268008bd6c5a49ab05160ba3d52abdc3f990c7bde2d489727a27ad9179a49a2ff692f02380c2439d24db03da1d371bee9d319f9f5ffcde0db0dfab033d2fb11c576e795693aef70e01fe080e31fb7b437cfe65a54bd45fe663dfa80ad27d851dd64f5c242b788277e698061b0f0ba081c1c2b1dccace186d3a0c5e17cf6df5075ad72df1fac1d91ea3b901fadedcfc7dc8c464588258345ad488779d5137eabf3b417a3bc89e07a6ed08073c59d7bdf194a8e35e2833ea8b819aa4855bab199d61b84a677def992822340d5c1722372705ff4ede411b3560786669a013d2722958f76a8d6ff3480e0ee10ad135c0f255fa7612df6b8106276b88779eb2c33dd957b21c9ee701f6d7d908054301aac13c4cadc793ffb736f50a9d1d1bb49a5b6a8e4964dbd02e801c2f9b0153551a34f4477d428ae1bc288a93148ed930604099ab00fb826eb62de85dc008655597d23d2883b9976cbbbfd35e5d8d7d9c1fb41444d992b82e1f1c0cf3be06325fa3cb59fd57cdb36a9b4bed7d4539ea8f072b502e47998acc733ec1c977057ea6100cfdef0fc63a741c20972abe3a011cf14b15513b2c2891c8a716d2d003bd1c1b05cb7a64630103693d8af8567cfb2c9d18db2d6495fcf48183c8a6d4090e8e5ea1e6296b0948ce9271e512eee3641166a62de95eb048b90611f4e55703d7f63a166acbab6c400dbbddbee07d473157f50b2f5ffabe8bd30582ba755d0cc7ca39db0fb1164e02768551dc934a58445860e6e84aef615848935df4a4410d2eff45b052c7e86339ceee6e6dfc8fb1c78c372e6c43e5e6865deee2306feb0e98a44f510542d19c55f636288d3439c662cb19dd3d1a01efcaaf65bd512d0101e59646e8be714a1140ed487687659ab04a3f73017ea017bd182ec0095560410362cc0e3bd5b7e5d6eda1ca4c6ea35293dd2050ff7ceee473e2699fc77f68610dd9d20adfe7ed3711ae9efda7f109f81a9bb89430bfb59062ea08c5bf6d5dea0bd2e72037bd26e2a98fa2c7f509b5532b7f9dda2e6ea2e251f80257d2b0b677033f70e79877994e4bf32c741676c5d092f27984e974d4c087816ea78ea3ac0503ce17ae35886fe53b93e1e9c6696cebd08c86c5bbcbb4806712755c8ae8628e09478a411fe97a73f20792e9025e22164694d38ca5e15ee8f5a78b8753b319310beb6fb1cd9944c5aaaeaa8c91ec26dcd7849debe9b8c993ec355abe4754bf3907639dd063a5888a0346f63ca55566d8f47706acf51a928e72de0e488af160d609341e9f18a17bd8b6a7b6f2337433055bb13f7273769cd334e2990755a0d392035ae0295d2da4c5eadc1d1ee3387878544e7257447d75b78bc4019c585346b7002c2547af59a530e6d9d6c92364307111cd1c815d6bc5fb5d835253576510a309ee1aaa562c3d5f7cc5315c5cdefcfd229e3e991e07dfa30a49a99a752dc73c09b639c92f47b076af43ffd199aa545c7e85af8f50ea0002e4c65fee8b6c58da071a704372e0aaaf42bc4e5376e70eefba9baf06b9af834fa25be9897daa52ed025772bdf91fe85163db529a82c2b662fcc4d69673ba93afa7c0eb171cc96c8f0102201c675762e9925462ec9117d5e93f6b7763c43def58f7ec709e7109a9860ae0462850763943808d1bfd4323632d1bced198ac8dea806b6ae2f7c0f3cb7d0291021e0aa03ce92831cbc0c9c7b5c020bda8e7244796b9bc0ecba88224e5ba06e62c422f545bb5f2f09769d254a66d354d89837857152bea4c29e83fffe5f908f4d0159ec55b0a7062640bbe2741925d585b4b18b3c0b26c4275a2d6364c6b0961add9460b0faf8f33f6b80bcf4138453295f0e9b8d785dfa3f809d55d072501da33459a0ae841b6e0445b322a78975477edbb22ce90e7a7bcc343454e9ed40f164c86935bd5c46df69ae4a1b7950ec7926e0c4ffebec87803c838edfef78d0cc79ca1a072bc026fafc69b1732dfc37a65f13c9a6ee0cec856b065c3898bb00cc91761a5fc551f3e34e1f9acefbb3090311a010e2b53823ef934262fa973fb0430c90c4300dec502f30e039083b6d9308c02140937bd8086874c31beb2ec7c0e1906b794be3fe492b22c9ab2125d52f8286d814b634231d8edc36eb0c6465308a9cdbca9d0d88a671967dccebc8167990adc785090ae9a0b6ee99942f7780a0bf91c3ae933901538458562b29ae824f07fd898f7bf22e519ec06b7da5853c80e62b3aed0c5a44535d4a724243c26c43adda9251868f14dada16243e957eabc038616815365ee3a46b3e9a181d869e59900ef54ccfbda4f4be5178fbe32077e0baad347c2f10a128cacdda5707d08363c5bf8463cf2f3ce2318569d7aa4309201923694d7274584c062329dd4dcedec9a7b6703f7dfb6cae622bb4ac165fc4403fb389fc41eb2e235858253c02f48cef05a42f4b62f2ce56a0af209aba620a103faaef80c4e441f9e43bd14604e454f169c74d095f3dbcac62bff37268097c20ce57eac4f46aa98a9025cc6a8342aced9fe9b9ea9520fe6a5cb8a9b7e0239b60007cde7fa22b5ee5dc7d65c6417a9b10d1cfab12a83e11dbdc449724d16624e06cb7f4635c5c693fdac48179da9e0e71a69c899efc4d10daafc16f08c2d9931026b29a02f76b8b8b8a9ad3e7a787319285508c6e415d140ba09002d4cdf63e706b7e6b69f77bc42538f1b1b8548d5003d5572d4c8c3591bf5a193e654e782090e52e5a1bec385ea28381f915860c60c391eb2b1808e365692cd055d3f6fce6b05a70f57d8bf750ce9934705f1c4f19d90bf26058f9db009c2bd16a3d07dfd5e044a81de81d1d486c8c20ee340a10fab4c0c735a6103a61464f6611a19346ee205512a8b77999bd96074426b0ceeb28943f0866bf318ea73b50555ca6ad67ec30c91ac803823701c8367de553050a0f4a394e2782cc8f5bab8693d01d0d56fc60012abd6bf9f8d6f84b53731c9761ae88daafa1bddf078ae9483c1d944e8d0180b9b8871f7047c396c67e5199cd0424a64a7c25b81d4755c01ea4efae9825f000d48918e86d12acbdf8fad018e4a31719ed3d1026478501d1c1dac316cc0aedf0c6a1ad863a04243e53553d7a04aa1f27e368260e0f1e56dd85cd81cb2335b2e03ae4d42fdb68e9b27198b3ceb4248b021fe8539cc426778f9f2e1c83348b82a074357ccb809a424e6d88e172782900a0ff2de098ac117cd007403a4400484eb04120b164291a61610346a98f7565eaced879a269ea9174c4292c7ec56b94c3309bce4df39d97f3741cdf17c51457aab5a531afa090c8da42fe550998d236fd90e787e0b8481305385367c4cdfb4dbc4246712931fa925ee5d35912f95289d4d024c7eada6fcad9ed2154d7ca684a5c03d97713cabefbcacd4f552e73cbc6a6b00932f283f2377f743034b4acbd200945d8fc701f082f43a29d69c2798a325f80cc118fc5e661fb98f30c5c7e61dd50d39b89c4e07e31d60c65607f1744484d309091b1b76585ff6f854c6133aebc3999d48c09772c496c38774ad4032a0a6420b true +check_ring_signature 7823ace913c9a11e88eb3aba6a5b15b775a4b73b37fe397296433e7ba46bba18 8403445672309b26bcb2e30243088bb5743307101596e312f2ad62e6499f9a0e 137 a5d2f33ffe576152ec3f2403d9ce54b5f1f573067afa4a645338459ef472b060 517af42bb9b7a12ec3436858f0a9ab42144ec102d850ab21a10df26adf8be7bd f04d30a35a2aba4733bf06e71df47a80106c4646c998066fa4665399d804d49d 8dca2e5bc63a21f4cef49a4202a517f356c92a4aab1decc901e9a15497f0a944 c39c8bb4b8d2d94deb817fd0d74434561496156cda6f9196d712b99ff3aa7779 a133948457bc44cc602826ee8df947eac44a26daf9ce229178bd16f727c4bd6f de44c56d5f07b6c1e21a2d8ae049eb8dc5cd93ecb813024c74e3c6d0adc4b424 d90252e19d3a19bf32724227f770dd45501b6b692b7a746c82f9b5a09f4783fc b7e8d62430219ef526a7dcef233b00fb7077538618d9b550ff9b0fd57fb96b2d eb0c503c19d3a7899b17af245ec631a24b329849586c88c4f74a2b842ad9fa19 2a403ba0a98e1f48c46c4f9016dacb6f0507803a8179e3b83aa74be2813b111c 9ee32458ba5afb20d7c8c3d2c6bc9870e417446b22ad34aefb58dfea8a97b408 6715c692908eb882499b350bcfefaf5c7756d9436e5933543abc0e63c195db35 756d925fbf64fb0a8ccb992dc1b7f2c9a99c8c16f8d81e3448fb64c354cced67 d7f33beafd7ed93ec71d91fa371c9e25f46d22b7b367130bffc7b16a3fddaea0 c3f9d08510fdbc6f9c23254cc01bbc76dc5ece6b0a8eba511b26bf1f181763ad 2132f56bf99915138a008dee55be712a8fc7295fa6b37ec50ee193dc38f6d86b 95f17af914bdfee0ae3b80f5dfcc6ac7813714fbf64add28a256dab5b1e80e3f d7f9701bc8fd737d75b42f29446537bc75dd585ec5f77edc225f3fad2d545765 e766bd9b90cb9c49a182d714d617916bd86c7cdd56d7024b6d8d6dce54d579aa 6de487218d30ad4871f460a19676fc5ddfe588985bc320c873d9d0f557bf0d51 16e5f63574c6ecc93ae16191861dca50b5e5b096b303f41463e6604b4d013dce 9bec1ff3c6e5c3de33ce3c3349c436d1ea7d13ea67f566484948d33bb3cd8961 966ee2b88b6b1afe96813fa8d5ac9a2fd42a7f4b3b2868879faf12710c893cfd 40007531c00ab1a68b2cb56311f2125f1d1bbbad812f02f7abaa066ab3c4fc8a 685fefd19fd55ad39374502b6375821d1e316b6adfb2b44953fd314959a734f1 0c87efc11a22eebd280b8b2edb9b6aa818470fda0fcb46d449cd6de4c61aa8e6 84aba62457fadc981990ed90796b233ccf873016cf88f358b0d14f4e10975933 19ceec73b8f2fb8c1915e2e90b622b2e8544062bb51eb5d340cf3d0528745e33 47c05b8dadf27453ab92909474f9fa119210cb652bec3c25af6167e04c3c3a5c 52b5ef449265d599dd6f9347a76ca1ff3af9ed561b15b7dfd6bab1c3ba690427 53e7ce174a9ec2dcc883ca1791e6fc9a2c5247c300eeac4cdb3c5c3e27825dfd f4067a33037e333616035844ca293021a045480b415181dae96cb01c822eee43 385fcf761fd52254b02e481a0ccd66fe89d1cc328cb68be7f7da35daf11249df 5c18aceec2f5917771a842dda80fbcf2767a475112e9e9078d6661617fff32e4 a558aece2579f9dd51f1f2136ae43953e44244942a4e16e88c0a9e911c44c269 94703462f9b1e065b2b561bbb3bd159bdc690dd9ac40f2c849cb1f1862cb33b0 0194a8c88c3491b318eed45ac3c12b8f22496b402dc916ef335083f6d30f8c0a 79364c8cca9eca68e5aca321e062e3633d9a5c8cf569f153800b0408ed9c11c4 381f77c691ea4c634069b8729bbcf75c14274d1e3bd67ee720fd9d03132189cc e43f8b25d79ebf110ee9cc322b5e4e18d595b946de1a69cf250de96f71daad5a cdf0ec4a87202e3503745c7be164697af3461509969ed957faca4703606ea70c fc1e3cb5fe77a1c57941bc5a1200e49849d790969a7259f5bca780287163e602 331040b6a83e3085e8cd5b7b9d4d3b26552ccf5eb9a1ac0e06732e9631ddb7f6 cca842f1384bb92dc33f93cbff6adae58207108f7160c8d2209425d1389400e3 2fe1de553cbd2c3713aa39ae39b16da3502ab66e46d6dd1bd1fbddc68391cfe5 e92866088742b6a5f5fc0d0761b0762824d2f88611090ca77c1c6fd7b02ece54 ccdf94048155c298e4ea77b34da33d2a07667a18dbb950f5bdb018d9c089c955 4ccf85239f60a2b77879d44a3a93670b07c6e9ade25a743821cdb7e3c5cbda2f 8574814f5fb9dbb20774be88ba76a0659795d8e6924154862c8cb824e0fdecd9 fde1343bfc43df1a9597ccd4a0a5a5aba8900e0609b9cbfa1f0922262c0392bf 0a48895210f223058288325b4192cadf853cf2d76a43f6cff2b6bbcc9b89671a 525afe0a7639ff5a6d90d565ce1b742204a04a6ff0088f3e74f0045c73702d6a d38cb968006edc88209d816a8211df7ef532967247bd98b357f16eefd4ff118d 750b553e7a068ac49397309540db4eaf882f0263ad133b6eb07041cb1256502c 13ee477644ea8e9ff21a5a901eece139f63e42311efa29fbf23d451addc7df6e 0db88b22e2f70b13a6e77fb51365dbdcd9c79dacbb31324566b3e5b4a5de60c1 0898771dff24b3133e4d30d89bf9a3ed08344b478531ccb31a699f05038b11d2 21c4d1152467ea4b0c6c49b549c07ddf6316edf0aeefad0c34cd50fe4d9821f9 b0ac6e339de42881f923653ced48a45c69ef3e72c2666d989e4371dbf40ade7b aadc79d428a42bdc9e7da24158e34f295ebde776ea656f374dab46587334e5f4 ef7e3a4653af861023b05d1d715216277e00cf3a378745d1dc733bdb5f9b78a6 20e2ec92c417f4048e4e47f4713a58290fd4e5d4acafd997be916614b508814e 97a658c764e4469cf57c4c5a04504dbd08ead593d245fce5989add57cb0380b7 a3005e2585ad237ac161303090d219a1cf9d8fdf296774cb3bd4126118e6833f ce7958e2f2a121d9638eefd7dd20439f47356e3a371b4b9bcff9b589a37cb746 b4f34c650b206563321d0140293f24b65d53c3efacfa9d6d75a68f461c1abcb3 b940ac7808fe9d9ec1f94e8bb863bc6b1b006d8e62ceb535793a400cbf3bc83b 1d881e66e766383f666e5534a5f2f45a12506d90bc571607dacf42aa8231d2a1 36c861b8b9b701865e5dce877f0cdcd6380c1b4ee17e06e993a80239be1312ab 9b2b1910e3bff29b2209f43fff3b387927174630ba89f264704743a43e0a249e c412d87a1eb68c896b797c27ba8ff309dfdcd30988d1c4fae0b37123168707bf 1a2843ffa4ba391458d430214588e8de4fdf221c8bf22ff22b6ac3483bc4ffad 8588c369cdb7921d57125fa9758fedcf05811cdb36895633cf104681cfeeb53f 6c7bfbe236487b1faa8540db6a302af9a2e5434be15b6a198786cc7f26bf5963 b2bcec9f3cc533f6b5915615c770f0014b84729b39e03b36f44e4142a7376adf cbcab957616fb326cc04fc9b528492d6f521e0c82c1ae1453ee7fe1bae177d63 e9a18a73cbac8ea6dad6f54abffb71509fb8113df0b315ab5eae9d651eb57f11 405b2324e4beaad0d648a9a19da10d85d622d9f16729081115ccc84e7cbd2239 8a677afce1fd977daee8957eba0dbb9c85409aeea4c18d78886dad28afc4c753 68a7b488a80ee4571ba818a6002577dc2ed8f78957db6dff92d98291061a9c61 0bca2cee014529868996c6dc170f092d3bfb51a02bf337a0c39cf587633c0346 bc03f9a9408f08f5b734eecf279cc189e535447d2bc6ace7ea64f96abbc688a6 4f136638d7bd4d414aaee0223887ea24a594b67c0a13d7268dc0ef06c575db55 e7e12484cedbbf894aec9aa8a36fa7c3126e589822c85ea191e327bd771310c1 4d21ce10ff2e14492d347c3deae5ff6e73bf441cff74d12757ea265748260202 78ef2de00201e939ab76ea5c8cfe4feca5e4e89cd0c1d764d029df73475df773 9c2d621ea708e4e2218e0a57083482cbd5ce11bc708eb127979f496b6215594b 2c9c4d20f19efa61e1842d0b19b718d5d3e488272e72d9c2cff5752367b00645 6c78623df1ea489670bb61bd5c8811c3f3f72939ba057d185ed177dff85251d8 b98845a6a3423b905c659cd7390f911e4423693bc4948f5b1111baa4a42c37a2 f31767d62ec910dfbe08d96572382c1275f7ea6144dbaac3c8de47f526630660 3f9f69951a24d0796176a30d8f741fd835ac18f11efed3e527c870baef55cff0 68e1f2f05b1f72efb58f46841dabd7294fc7136824984a5a88f412929dac0548 d2b7f7ad2ece4e56a9acaf086d16eadfc4cf7fcfc971a4c781a3c31e307e1320 9ae67720ccca181132618c1832b2d74f70449b932bef27ae228847d73266a93e 1838b1d454be998c8b23120a17c6ebe8868391459a2549ee1ab0d33926a99ebb 48b26da02f66552c8b20309cecca7f777fe75338a8d8cf7dc21d77ab61285373 9e765b6ea3d7b13d3d0e17aaaf8602499710ce411ec9c6bc62803877e394f3df da491f7ea15a9eaa42a5780f731ac8f34536b6efc8417485e02b1f5240b355a5 c60f6f60232297e572a0e2f1e67b4c0ab79d3db3e4dbfc12da1e781c9761e2a4 93298ae23cb87e434540f26982164ddb6c5174c782e2788f26300c800280a297 4025e1e7e5fc92b8a9acfdf82b9cadd7940edc26993825d8206709209da3db36 a9c27cc13b70791e396a866eb0f996a831603f81ea13556758f83c79c86d0b5f 14e9515ad7aba6d8b49115216aed5c11a639860ad05495468e26cc85cbda012b 76738722048cfe4f5cb2149a4aa4c4f36fddbd14de052edd728dacfd433ed2d8 90af77d8beeebd9e37bf322c127762847f9a4b07559c9296da4be8eebd81fcaf fc4acf4661fc2380a9dbd29cb97c1a9eefdc46f787038d68515b2e274af4b5e8 08ac6e34746a0fb7dace93cabca5db80c3320d13025014572830dfad6e4b216c 2c1a5803807b715df0771f220b48bff8d000a46b3f1f979ef0f2c2f234c419c2 4b8b89e5994abb24e547554ff7f874c5e87316028f393f76614ecefe299dd3d0 867870cc1d173bd827a4aa1d65f095ce7a07a2ee4c556f90aa58d0fcb094355b 25481a7390ad27b816738ba44367ecc15bce983e80992c6ff2dba8f046fd379b 826ea04890ffdff42a0df4743f7a0d04415d1542315d1518dbb020a65322e148 ef9e99978d9c5957055a4be01cfaf5ad0de591520d2f8a3f28e19e6942d5bc45 882fd3c022bba1ff486ceb61ce4ebddb5444e66e18f2316f63b7c601f0ecd5e1 8682271432043a0017441c67ef4fd18203c49ff80282c906e263406813cfda6a bc3cdf7f8f52eb0cc75cf15df15844b92988cb4a22f17bad6c1d86103c0b6930 0a3b90d0d9d9f3863ef70770594c32f617966bd4e318f8f2cc0019853b420fc6 68e1c14210965a036c4ea3da1d122dc28d3a4e8ecc7f317ee86ac1b9a0373e8b 900fd7fd39cae5e70bcc5d2a7aae25583dd24189ff5b3311abbe44324d26d427 fce895776393ba6244c3ab80db6f752f6a255da9ce608aa055598127a1fe7193 2f131074067ce9e5a63914269467b7763abc97d08af58060b554b323e8f9093b 8575e25eda57b08d3f596a84d9f57955308f34fec84ae8936a8aad3535ba495f ee13c84fd7e8edded34515b97f920a6f1ad8418d351cc8f5b761603d1218cd11 fd99bd103fee24e979365f048af62a4f20da90f73d56848e6c2c118ff7431316 863d7e180dfa2a07fed26133cee833db3c1e9b4f2544353e63aefd73e5bcaadb 6838b80da2498b47e63be81f2e8c115881efe01bfb1988dc77e77fb773ffc3e1 69d4a728b5c7eca35c00834bd96082850a4cb46c91139d2af55a1eef2a681b44 044f1c70caabb58fe6825ec82d36883914ca81b01f639305d6439aa75e04739a 7a331533ccca7cfe6f998edb2047f8b1542375cbf14ca88b8f6a4ede389830ec a90962b777b703e9a51f6ee19f5c6f8cca77f184285993f7711a8626aee8b5d2 5e140837753db29439fd45a72c1d2d447c7e4fb3b701f415e3cbf43e653f91c2 b87c5d2d2a14fb5ade4216d2f4d57097e32e620805a574ad79f542f7d98ab446 cd63f1ef07c6eac1a3ded71629303c784e841448fe0a22a63c38e6d2b655fb8b 3c4d51b25163741674c8637e8dcc37529be27bc511e3f8e1f20442383107ce36 0abf62a414e99f5dd8beac37540417251cbbdaf03e87c550abc6bcaae5339d47 7696cccf4efea6c543b595043062bb550a818547037127e5679f63715ec04002445512f49a608e39d4dd8b20cdea169559b8e0b2bbfd105deb2407c26fc6390c6bcc1a7d9963c3146fa0c500696d0f4a446a4dc385171139a3523313550b9e0a0ab987b1eaeaf57c01328da4cdfc26048c8a1a4206939458b7fc0d4d60fd7d0fea37476c905f00c397d78be6d653e790c87efa50b01048d49ed26b51bc59b800f06e6f5f37f5515832ec51c227a62ba4b3c1142a0383fb0a69d705c6da0b6c0e7a81baf6dd78bbbe1639a6c700729521cb30363a50720d7e0cb9c3c61aae2c0ab3913f67113e67605c69847a36abe6cc200f3b2fcddb3ebf6648f0d7f093b20f11aa52dcf82e4bf47ae1f6bee17b1d5f4f23e75cd1febdbc83697cf6b7346907147e0e84e0ea742851c2c9f8713c607d36b2db8228cb43085c1cb6c2bfb48e0a22827c792701fafb39214110349bcc62fdb036f328f5a1adb01f11496138220cc923776d7813b2164933ab75a2aeb76be9cd8c061a7f3c051bbc73601d41040cc5f85296b09e32218ed313c88decdf9bfbd2fe69b6fcc0950b5630957e149307128b4d6b0e99a1d7f3e59738fb39cbacc63f745f997ad418d6db395857141b02cb47084c51d36c77acb45ee9a43a4672c5c9b2d11d336a3b74c686376da0fa04739c0aa2d07483d5da0592f4abdd76e022c2cde00de66e7e6abdea2f3e4a310208e4124789d852ccede75349d1236e348328afe3ccc7b7d5f05a3c9eef924900e7f37bcf2d6a90fb6f4afa9c1688927ee2f65acccac221bf3a28d76eded8b904827bfcf6bac2324dbfdeb45c8ea20957a63f7d44fa577f384aa6179e01dc3001cf8d179812b3b23c11c49af7d6deedc973b7e2aa525e39a1762e0d372acbdb049d63b9a89d2160cebfc79e54fa64e5d7dd5675b3fc9f0d61b3d6ea473042780fe4638b917f9118fade8b10b1951a633a4b2fe27adda41daee6a5d1899bce9a038b6bbb59de85b53dcafed2029799e9cf2483ce752425808711d33181ccc49c0e7f0cfaf022b2ea800201352d3b265439efcc21c90e25305797574659a5a5ef07da1fa823c34b416567309bb6b3ec9ebd235ba803e4e36f4ebbfd3f2f28412201d6dbbd3626c304a77a10d436ee7db9c5f0b2f17ed0655760262d3eb14fbbbf0e1b91112d620bd60aad34899ce568e698346883d859caef206970a3af90bc6600be40071d7e5fcfcba8fb0f5c9f0d01f5d7b8f8fff3579fa83e28c00f766b6c094dd795fec20a040ca85135d53bcb1dd7b6b7741e03677762104e1b3ddde1e707b3489b60fbe84aefaf38510e39dd26a1926a60c0106167403f6adb5fa2bcee0bd2d0dff03e4cf42dbf0cd652681958efc7c1f21ee08abd98a48de0bbba648607c4b72197eb9aec159441212d8b234fb4079259eafaf8ef9773efa4ebaa72260d858bb905ed29a1a01f73e2125e4259d88511b673da789ea7d2d0f1627917d1046db12193ba30ac9070f279a5279b87140ef60bcdd6ddd52f22179689a2ea3305e5b45d419ef3395f897188613abca765bea2019f5072a536f72aa44be1dbd101018645eb897b4630824d9f8ef79911e9d283d971055e9769c4e3ad2c921cef00243b6f6256dbc655dfb056cce838d1293f539c78b1cdb71b1b4b69da06f81005d0427ff5576f9b1b37a5281558e74111d83868d348aaaa3bf6de3af9a3732c0153d2ba9b441b2e285fd4ffeb3a9668a110233877873fe31229b3b5f15e84290790341305d7b7bd7acafa5e7888c8703902850c3397b198f4fecc0d288234050f3b3fb811daa835829963f8eabe19a7a09790eb3a32e2a285a191f04cf07b220032808bc655a16cb0ad4b412cd0dd1845f16eb4bcef57ecdc54005122b8902907bed93964d20e66932f3ce709d4a9c3237647159ff256bdd2680d5b04b5dbf10e732a44bae5628ba7132c127926bae6f15aea1560efa21b5bed5f682516e1430f7e1605a293895281590cec365088bfeaa2844496ed01e34e71174c192e68f004f56f3a9ebb47e7dc727ea36d3a70db43bdd3196ec843b352594b0d960678bb026dd13650cf74c9a4061ca67a478ff58e4da19e15f6298c2efa81ad5cfecf26070a0049e3d098c2d8035b0b5866eb08781655a14f91dac64e44b4bba989960f0fbe16515330a44dbb271a10a13e063c848249da8f81458197d0fc9c281bf8fa0e2b9c1f4a200c727cdc35a9c660107a9c7eb88d74dac949e373ba80cbdb7e070d66551d07e85d91f207db477543032edf9158ef216b4096071325a3494d16e10a4b7e7d444ecb887500988d1b048fde20b06a8d5c6679f0db4185f9a693a6fb0a258801a98934cf809c4fc634fd0cae7c4dc0bef0a4cb9f19adfc27ed1c613104328e8e538797cb31502ee8c4f5630183d668dcbf33c9529dde34d5d58ea6370f9d57b4b99db656e90ecda6aaf4b6dc14bc4e74c1b79c46cc9cb229ac3364e6029bb9d9a95bea2a7ea87b34d325d68b2ea8597d488adc4a0a16cc3b288748fa0f1480991d0481bed7022f92b2078439ff3b7d5c21e27c04e7b63f0e703520c70227fcab155481a979bf41919615a980ca190f1d80ce9fb5d1a37bedc2c1330604b9c3b0a7f94c5d499729c4415a339c2957b9c8455367416e79e24299b1ef440867e6ea5223e5492d6341100bc78b2ed7adc811f9df9d30333b40b38253315305a61f57792ab5c8486c9df085456b062664203b0d6c427ebe78a6057e6111f602482e2d217d5eea576102db6de5efe356205c7da887ad1d171f6782a23a18950c9c925d001bba392393020633448a127c73d2ce73961e95d83641349a0b8c3901eda7ac595fef6ed47c90c9360bfc9fe1aaf20f3a7755670f7505a6ad089271049ed5d77dbc26d83638b6af3bc83c7cf53d0ffe616b42d7dda9edf65f9f7b5507c1ad80b9d3957fd2b02f0222296b120e69b55c51d81a932ec7814d846ad17c084bc298a27771f0c5e41d55983dd8f790d64a45d1f47c273b7048b6a662b6d50948f8aad1ee708cad7f25447069d421a9e0e99548594d5c478708c6b4b5f5890332237e7794f661da38bf33f852d758c7284a1eeaa181d7fa424b33be2a663f08287c23156e3aece0fd0cb96fe88963e11555516e0ddefb92a0fff35910df100b4ed383e3f7a50bfd6c9f4a60011e2268d1414594638113c2ed41c6fd543e0e0a554a15618625e73d853098ad3a17f58234ed510d89552b6ccbb0738a75627408203948fa13e15538c5a5433e344a8e4b89916d21e0990bb4fb99b094b99d3404cd2e19e9555f120962b6f41a4673f70f431221f27db9a323409a62117305920e2e72b16fcf35eb44a0acbac3de5c2529f32508c53d0e8619a3cdf6859e550f040f51fb3991f70e373f9065a1280b1e546d0df5dd7ddbf3f7df3cf20f67eaea03f7a8302cd9d6f49b825970446b36ab37d7ef7f92f8be82edfc6863ef8b7ef0079a585480c68826b243677747533695ad23ca3b2dab5c053e8f1c42a198ac840642b381f1b9e3d9f739359301bb6cfa6e71afcea684890aca7f3c7cd7570eb40813572c456f4cf97f26aaa50da9fef0d5aee76763ac820ef6adcdd0a2c1bb5502c52e70999fc1b63ce239f563d172c31de7a5cd0b3e922277a443343ebf4ffb01e3f2d2a1f0cf2471fb6e1ebad2f74aed6f24d6872b3d9aea953cc7f67dc37d0148d5811ce966ca9db29000c32e283089052abc22f580ce1346d6a8e332a8c70510b5123fb3f8c1516e7c4267aabacaec6bc1fe07f00c5907681f6b601f71aa043b585b390ebad62d31330a95308a5308e02e953010b1bd97542efecc5df6ba0869f929e85ce5c5a573cd58590ede8e571a9c3865c68c09bf4053ee63d6779d0dcfc7c6ca4720c73cbb7e032499941fc6e02567f0b3f2b3304533cbf65fd2810835f1613e79dc4e66487d6cfd241d657b9ffed349d10ce5caf316ffbd745e0b0baf509dc0cf73d930c0441bced7a9271125399a73c833e36f38d9a43d4dca3e009fa4c0725ba1636624ed37ae8677667c28da82e71f5a25c57f96d285a85d5a08df6ab3cc4d3537d7f67833830b66a81dd9004f88a5778d95e8fec7d802dbb60ce2fd4159997f4b1e476ecdf1e8d61c07c834d2df2c8aa26803cfb0660b464d01f2162e64c6dec90bed16b82f0b7936e7a0db2ad3d26be2ea01032e0a9b1d910b6ce8264ffcc0f3c6d9318672e13dc8ebf002390e37eebea220d7fc49a928ea04ccb03df5223abb55aaafc17b1198ea35c14e84e059c441cc2f4f3180d4e3450979e70e94afad65f7083ea0c03403e9a63d044f59c21375c68e49c25ba394670f7b20c99ccc8e4b5ac2dd17841631fd26672ef2ee12f447f83b8a92e4adee5808663962b49ff48f507e58d7aff0117886c0c9e50881ec0cf906a39257a0d32c0cfde2202e7c63ecdf2e28a28772375b237118e1f45bd89e4da5d1ad8b3236d1058e2913a251cb278b2369b36dbf3645068193268506081decad8091579df4240a5fa40fe0dfe112bf1039ee39e92a5599efe33076a8c943148edd2179b73aa10e85e277edd2c0c78d79512a37b346482c062a7f93b4455138d2eab2e85e00f9027a9e9675d9e7631273ee81327ad0218567fc88eb88a70ee5a5fd1b87f7275f0e17321894fa197b088ae57bb02066cc6e3e959b58b6afab4546e0634995ea2a0c9831d35337086f7ff1c29874cf0bf52bbca08ea8d7b6e9b4c77124969aff4c0e3e7c05db8ed5625d0758c9b11f722a46f11f0002a600942a8172b18bc496730032f66ce290f5fa55bbd0a728fc5f44dc638fec357e1a73b221cd989c9ab3e7099f853547760cab9290c2ed2150b59b619da7d9c231e8c8549416e24948c151087f9e452aa6befaa228e8fe8cca938dec28395eb1d42a271e7f26bae631df3d08c70bc3b9751da281b241dd1366d5e0b38198a62d9edce31e67ac71bf76af380d136b3fea8dcd903e21655316d508f30aeb29c1a6a38bbedac78c05942c58480d2ab6f46388a04e7835b47adb2998cbba7d85a7441da0063c2eda71f52d8df907a5c956b7a177017b6cafc4bf2dee762f87dcbd8446e80d1b0145bf70c8d751068fec89cd965d76bf4a7c53937404c131881506a25880f0a40c53ce4129a67d0716eb3af2e5721e483eb27b8589b7586f5b5a860248ce7bc5f0ea1646007e600d62766eeaad8e95daa629e0345955062cddd66dcac9b97c1a8d904ef1243dcf06eeb3c8f03e17f9802420878c50ccbc7e87b6d43f28b8ca2ce19fd93aa6044c049991c02a9a2a6c96bae5e0b7ca19d58abc3a353987bc1176ef8f9c982f1fcf09b24ef5ff5a20b10a51c6f7463b2c66b191d915dae68fc4d91b6f34c5bc336f07d8585d4bbcd0b1bdb3e6ce2910f3877a4602694985c9015053efea0a90be270a237d109806cf34a43b75e35cd0a754cf5622c271f18003e5ca1e51408e5e5d016e7166d3682b07d363f305f6f5d2ff588d7eb132293cc00767cefd7a6269a60d9168f02aed93bfa47d06295f61adc1c4028535ff8a1fe095c56217415353ec0d50b0b9e3d3f96ae366196ed117900c2b060f1aa76d6360066af5e534f29fb80b786af1a0f2c81c850febae25d4636d82f7012e189ed6bf793884c3c88bfa77004bcc04fa45577546fa5f5c84b683b4ff1031800756a713f06607ff766ddd7d0dfffb03ae75f3f84c4da0dc7333842a95230780d14a52a194aa038d0fd15c70029eaac402320971b552887c79cb52a9d246f5095a08c2982788aa40e5f2a0ef049f07f54259fb2549d188bfc3a4714a8f20bb7af6778c90a77cdb8c255d268e0868f87d8ac79924ee5bd920811a85ca8185aacb1b1ce01d12398b4561c09c150c77d8eb5ace255364f96c41603aa865a4d4eb7387aa7c2b8c50eff5c615012f0c9ed2b97fe9be801850156b6d107e3706370fee0c549eab71e545d43b87085c0c5eab90ac5f395d26960770c04430ccfbc97227bb29cfd2bb16ec406686c45109b71c2db7c4f6339f725f4416cff7681702e3938bedc11ef9692cf9af3d39cd0b523510c3e5bada34beb31ae361207fdc722a05adccb2543b52150b8fa6accb067250bab7a40f504883620782d6b22b49f62a02cafe9078d94ad9074e971a4e06eaac24c2c697ff7078504fb4cc17fb67d8620957367a5bc7fee915db22e31607e35fd6cbb0451a807681e0c88f4fb3e620628f1718019c162cfd31ddd8ddd70d1e9ee71ab0420fe68dbec0b280bcda5a4e5d6ab200fad973149c3da436da1e013b7b60c8f1aa4160220a3ee75795e58f4eb108a39bae2405d92200846526910ddee2e84e9485a67916a600516f836755a602a7dc51a96b9a7fb9113f5df4be05c852a0d6518903afc8d025aad0ab12ca1473e7b1f6115654a1d6c98f94bb6d0f13aae33576edd4797f449b70e3893576f357618179eace40f7413f33f3493f0702e59d958ca4608dfe1c74521e56481e732e80ecb6fcf8104dfd47af80d7b502151b1bcfad5c3a8a02a81a57c734205599ce68054bbfb82357567d1403507f0df34f5d32c3916f0a7e601e2f75c8231476d8d08031c119b988a15ab435b16900fbc661501d1add2f5326cff7222f83ea689526beb66106cb1e35ce23a81c1a08a15ad6bc6b2433edbcbd45972c06fa7fab11c3ca4af18ff380a53479a7905b056fc8eab7fa6a154d10c2a29c4b5ccd18b2890e3c3bc724dfbc5971b22e20ee014f0212beb5e05ebef001131f2388a5429e2e506527a8ddb7c2922390ab524700842aaf102c35dcb6480b25174170a9a107625d8f2e8abb5dbc82050929c49e01163f3c6636fc317ec2559e33dd2b262bd2df28618602017bc0237b8a6366e9090642ebc6517eb62708adb522188ab798cd24588bdcb51c356a49f59c6bbfe401ab9cf33d8c1918e52c582b49c323fee72fd49d3cd8850573f906032f7b62ab021b90fa17db2c7eb73b10fd1254db40297c6f0b43c308e1cee4b80bbd5de45c08f7c91248a97613381e365f172608f3b15ffc8a14ad89122c61b482c029f922015294dc54d5e4e1fa75154cf3070dcdd605913efe856c0d5b86d16d4cf24e73011637cd47e10af87ea25cd16c57e9c9c466378b42757789736a92fe66b5f8100885f1426b9e043d87f9b66ddbeb51bfd02bb79e3826c6d68dcbab1512fb22010ecc868ef039aa66e2cf7ad70c75237edb32cbcc19f1c523723aa2325b7dde6f03ef693cfb12fad07edfb067a7852abac3e8a601d00529dad846413cd647adc10c406e6671dbe4b8db3570a527a3a335ed348b7310df0b5cf6ae80cc7fb770c606650fe2480aca1f3384f7a655bbc310f23f2beee1e7ac9411da875284b7e8520b49e35adfb1a8f322772eff1b3e287b42beca5d6340856326d15aeb830d15a00ef07e86c1c8098a7c0e5c57031d532cd37dd3f0ac7128cf7531ce34a9016ac707453ff85c78dbcf9eac2d249b161560818ba81536dac0365f8bece62cdfdd3f0eb407e71ac1694373d9841a851bb8db041356b01f45fffc3c56e006b89e08ed0d5f28a08995547508168f510faf96dfcb5535913935fd872cd13a9b72d5735209765c57a98066518c0eb1f42f54004667d754dd4af6c1ea4ceecb0abce8c1610ded9ee4e15c8bea44094bdfc745ac756d42e1a0808d5fa46ebd57c4d27f9e3401deecf17eba6e3e504ceb43fd0c42f0c59b6111d474665274cc8123743e60820272792f7be7e0306e594eb0c558ebff4bbe0428a122d6cb919804c85d65d47e0695b7a70513348fe3a0dc9acd4c9e01c538d2b3abf48edda677b594ffbc4f7e03cacefdf3e186aa5a8652c4a005c117070cf4897b71d55f9c2dc3fa3c09e8560d369f80baea5f6e0fdc20d0809f76f85274366552d8ab27e9ab97622770055701506f8db27e7601b2c88f71a9c05a87a8654c2d675284662ac8d9566d3fa41f0d570ab61bc68910bf1d22e62a8c58f2da1669a261f37df897b65901a2d273d50b350bb28768af9335b5270c4957cffd6f5c35f056656fc5b753abb4c8bf4bbb08ff048ecfacc3075a22fea8be1059b8e1abf6bca294456ef94913ea9b4732970f3a07a6fdc9e92699a6071376f5eea972eaea0aea50275a4723c2ab42229f23040f15297ff28d24ec6d7d30f22f4de8721cbafe52377a3b9ea54da781f1ca7f08381044cc4dae536d1dd191060ceb3293c55f4003d88a7350fa282be8a3f79d075904e80f0a263d1bfcf474eb2f054cce6958b5f8c4187607d8bb43b270e82c0c67fa04b453a6c0bac35bdb1acc4797ea730b0be36dd3ae9266b286d2ec27960db5dcf1152613c366dfe7d7500aeb3ad3a52ae62ef513d89f7d6b6a4e70b70b0f943eb8efd0d33fc8701ae3e42d778367f6bf89150bcbb9c6be894a3b12f80c08ee55c7adc3adc0d3b712a369ec4de79e371ff7148315575d814478a766870704edb005fd6e85f1010c0ecb678c5641ce5b355f9feb8ae383fd4b9e0abf3f86061b2d4264f9417ae5c5b1de0ca2df3f19a671abb27baef096cb54bc32ba62a3098419cf08b216352dc3f0c05d5b56d340823194da3736e4a6dedfec61c850010fbdd34e4e2489a42fa43f43d9f995ef2882719e49faafeee382a5b81c75ccfd04e388d14cebb4fcbda0566178fcaf2f7687d65e4ac09f9014337a0d065642ca00c457e2f747ba5f168a7589eec46ac2cabac6b4c5ca303a08ab9dc7c844959f00466b3d5dd9b45cde2440283eb4b13851ac5aeaa166b37a3d9a3135fcb5079001d1af98aca6e71e1c2689a0bb8311fba162fd4cb4cabf8eb0f2a05e7015c404096eb86c9d842cc95a8510574df84e6b3e9e5561f3d7bcb9b9b1bbe2fc6cc2b6003cb0cb86aeebdda9f308d9336e1b5fae6110985acd68d13e91cc5605d0f872020ad2ede5616cb960c6f0a14abfcc7b596f73c0895f52c7864bf9ede226b9a8018674621efb7434905f73117c410d01c48bbf31c8568fc5902b5b2d07b2d74706f59827b6d176f24992d13a5988eacc4ea5ab73cbe6437330b10c20c030bfcc0068537199dabea4e195e341faa67d785b4ffe45af2e9ec2021c69eba5a358a90cdea27e161aad848139ff3da824b92ae64e259daf06ff5aa3ae746e818ac71d0c570603d041492057867c92af21fd467ec1f8f37ec356db8ba97f1862059e400c2153b4490ff7d7b7ad11da8d751d5416907c3c81ae6dd4bdf83701bff44c81031da2a0360563dc1e437ff819ea1f24c9fd6fccb426f374166d663715dbca1b00b4dc5e48c4f0c26058b28204ed766756b13375002bb1f78620a830a50248880ef0e1adeedc749845fb86ffaae8e74cb4b54f9e578adb9f8fb9e7df4e8098130ee4671cd43bd9205eddc23f8571ef0f463f0af2a42c529899be9854268b0388098018d5da2024aab2b9e4d39a8fe15fca4dc2acfb541661061aba9e5e90646c0001ed4c5dd04746401022239cf071ccd52e4008fbfc6e9134f15312b1dacb2a07e87529d981aad2eb2481f65ad451e07d88cd0bef077dc555a083f2c5d2aedb0cefd03d8139b483ae442510bc49d579399af7fb36b9b1773dbca1cf7f9830ac089aa6881c06fdde762039b73f5583a9fb9a078e1e083571bad7de103577fe67036a5237c5e0ec66240b2eb633bb08e672f4f81945d0c55fd6f3d80d97891dfa05b99b39504400ee90c6e728745e2bb4203976576fcb45594095b92c40ac151104cb1d11f9127134c92357d046fb868f418a05d783deed5ec9dea9df247b788e06acaa0c16f7fa60fc5536eabfb40b3800e5148e86a056da667bb5ff694b70e3060b595b7123177a2915e2bce66e78849a6232f606556bbaf6ec8f7157e7adf90a6939b55fcf113a7a708ceb82b8c9d1e2cb3444b22ce91badc6a406a30b6bb409d4451c026463ac29c28f63bc53fc0d277fa8f8c3fc56e01f3ff8b657db0d220d34d75bb6ffc04e0417320453db6172df50749b338960eeda9eb5db131db64905a8709038632aed78e787156cd89d98efb5c37c2b39e3d21f8be4c72f0fd76905cfa2781fecc099e2c0d942529355e1caea62915830d91abeafe67358a24dfb02d036bc25177978e39e05b16984388eea979602691bd24fbc5d3aaeb3d640e9007071f7a86b993af8666bbd92cc982c0bbef41e3697cc6679e75e24d836d8300204805ee9157dd8fa42eb1dce1f5cc2dd77ae5650b19d45e290a8f46a3457c7035d4ee73cca51993903f06c112e660f001bd4a6aa50f8dbb706ae04705b86e301b4556a20950be26ef5fb074364859b29c41ad12461e8c9c45cb01b08f64e1606a5560163da26c4a1ad62b7946f2f03638a4a47580142ace9bfc46171efe2cd0d18ee2a096d6bad37bed0779a5842dfeec7c35458dca632704b2040ea90a6b70948653e385e260f129cad53ac524adb1aab2ae21fc16bfc107940edf82d79a50ad05902cbbb059864e9ca43833140932f2fe0578f4c4157673afb9be303ef750b55313f1db885f9dfa2b1c20987ee236a592cab2620e63f31769742f3454d4b0c18300eab6c57ddf747c58f594c7fae0209912b207867beb728d4d7180f2080091b7105d393b55d3100a481dec26c34394bfc51eec4c78ce7c4469fe2aca1ad02202089c2ca6a54c1e2e8d2e2eba8d2d4dc87ca2118dd67b300cdcd4d0e06ce02053a6f1ef728e5fdf4eaaa1257d71826ccc8085a8a59f675441141a87b7f300d708be34b6283fc1abe69e8aba86146d367858e09609dedce4bd2921c18a1fc09d315dee40008d4ff6cb65468c71ae17343741994e6e4d0cbadf94e148ff47b08898f91ab6ef73bc045f16de5e448e2f1caba78395050ff5eeb331028997f80080d7a3d7da4deeec5324663df365fcecf25b4931cc5f04b97d095aa434132f00661ad9ba1f3b562ec9b7eb239d410bcfd032047321e24a1445c8988d46390770af493787f62545ef5e193c5632e06d97012f2fd4595ea7a5e163405f5f0904a0971ebabcb6d23d08ae8d72a7834580ce7aefadc2db0370bc7393112b48153de0e19c9de8581ee90135934c7269f2d14fa134e881e9bbb83563f2b4abb61a998023a6d0a972740e84162b957989803e92514ed400821cb288bdf7b0faca22d08093094c7394fb0b2c9cff8230eda3ae8b4f752b0508bf56bc58a675fc1ebd7580e2e0ab6a93f56b1e858eade0e54cf52eed4738cba700f4efbc819fc9c3d9f880418d9269fa6e7c10fba8d72a658bab555537f19d4d95bf3af30c5a0a1c47b470c69c2f0075eb01953765157c1b9b2a28705bbb187a5b3dfa3d96c56b48bce9f07485cac756a5608e0eee4d5e19c724fae06f03d3a6b5f430bce62af38783be504e14f158c98c8aceadeeb6aa137faa69c6c322cca62aacdaefab66f32e687170f349c6a1c415e588489bc28bf20757d5645603116b83445dfd8bc620131cbcb0264e7cc10520ce9da604aa45577169dbb6a3716d9dbe13e2274e0f4650f9f270e43907123c710cbff1d84127deabeb9f26a80f1e16cdd01fcc12e45054929c305f8bca3fc53d78b1a27186f1b6cb6f0b09c2d65d0c25b446cb63ebbeb09d39704d1ab4d1b8734a4c3dc1723d4243cab08a9766b6991795d158c6fdf17cf8ea80499116b1ba2ecc2a856cd28cf13f58aa2e416b846cea314d731d071735ab0a703c7417069789da5302841df84173b344b7d05ab5991c290e9ac9ae7b41220c50a50623e2390fea9789389552782ac758b23ee19fb6ec21f215201f39fb4e6710e0d1835fda3235b12ac56359bedd212c42231f6b260762814e8d55bb525de4c0311857b9975011eefa6e8bcd2f684f6aab24528dcaf062bfb1b0136400d5dee06a48359e11e3462fb797edc66d163df25baf762c2ec6634bd3a8b2cf1b6ab6e0df39a43d581dc2daacf67b0e890f5145ae8b29f4661f9f1028e42700ce023cd08b78574d88a89c060b64c09535e1e99e80a9aff095a3b505ff2ca0cd822e1ef0a4be84cdb2008e2e7d74536eccb20fbd069f0ed25bdc94126ad162e5bddeaee0b40b0e409bd6380c7abf3ceffae4b6008c9152754038639c2e9faa7a72be7210048c97a0608a25226c8233492bacf0bf397bd064272a7b3ff3bc059cad44fb20940ac96864bcd5c28a6c183e661f51e725cea4e0d3dd8f6bf91cccb8c921dc10032f223ac591537c67d5c7cb4ad676cb980cda46c4fd0c496387e8bf017b5ab08d1b4dc556ce8572051bba34a7ac5dfb6d7f637ce22dba012ff992ab4e46aab057ccd6c1062fe081ba0b5a334c4fcd965cb9971987ea7b4accb80c077fe2cb30c2c2b97f87c04f732c5db3cfb45ac98838b04df481578253c42b3d89b3a421b0525ca10b3766ddee33f025608b8b586e729f8213079909ca6bb1e19246750a60c false +check_ring_signature e72cb29f13bec0e60112a60bbd6b70edfbc9e2cc50c515b9929d8187e04f5592 394a1512d4be0a030a95cb1eb9ab4d0d36bdcd91f1b638321ce4bedd09a85f3a 3 dfdfcd778a12bc89963a6df6653a537836fca5ee05b46248f91019ef5c7cecdd a720234ae2212f78894ce6ffabb7597eeb94c6b09641f13fc26272932a14bd75 fe1eb019fef0b7d7136a1e138d299a959aaf84b4bdb7387a38ccdd7cc2c52cc6 0f46df2bae1ca2d78a919acfbe41678333b514768c87e7125a62a7c010a60807422f0d85eb067b331f4d5a3a859f882ca4fb9dd540a5ac6ab54b4fee74517d03a0dca7ddffe627a7adfdc9ab3772db1dc4b2d90a9780bdadf9cb59142f869ad955d4178944aa315d009ba832ec397d435e9f60cd98d34287ae9a26af7eed3f046440b0d1b5a03649debf65d0e37c7ef7c080f59728f71eba3ca99ced4413980fdcb963c44ae79e533bf0a6293d092013bf05e7506bb32f383a0594f26d94d00f false +check_ring_signature 134e42384fe7aee86ca8a09aa303e4dbb940113ec5465baa7194b07c12d6417b cae2b76e4ce84ff75dbaadda18cdcf8079d03683196e686ab9ebd8df9432e1dc 224 d852404bc791bce3c5efb07705972113b7ebe6324f2559e617c28a9b7fba7759 24c446e8691fcd1f9530de754f1770ac22594a71df930175dfd5a9898019f9d5 0a7191c87457f1e3132274ccd7e37f0cf4121167d190f887f8ba80e2d37c1cde bc18fdcc52e3138580c49118b7dcbaa933f291391f9db474af61dd647e75b18e 2665611ac46654a1bd8f7add7ad7f6b82cc4ed11055152d74dee1b849f207e27 d016b7cd49d2221be5907c84915c1bcc1d8fea85ab158fa997455980f216aa83 2bcdc22fbb35b2a98e0be542ac5cdaa721f8f99e5645250e0c3480d219da83ce f6028533a3afbae03ca5703e2302636cfb992e9d96c97a61988902a88525ca6b b4d41e09aab96f0699ed7d47920b09535f33c5b68ea8bc7751af8090f0dfd7a6 96d07a48a80ed1f9d774921537fa6d6aa9875b3a17726bebdb42139fc6b41ffe f722f9c7233443cd527f7c6c02db0bfa0d9c9d890b738a46b323a8788c34bbf3 bd920e92bc33dffbe785df1b904b2701bd14e6ff83bbff449a56b4d512fe6534 ec322d584c9abe2187771dda8f8d2319b52e2183f4a96f45afd82fbedca3b041 db005272537eadeb695f1d4ef4fe0ce0bfece089eb93678e06247033b3c5b45e cee60201933fe6b696d4a25e96d9fc2be66532472aca7d8fe4194db935b5937f 3785841be75ed34ef5db1b76686611e2b76e18f3e302bb12b70157d505377b4c 1b98933d2831c1beb17fca5e14bafc1c0cbdd400e8252f2aa767e59c6805cfc8 da46ac756d1abab0c5b3e542e5c1d33f918289addad354f4c761fadf4f0c5b5b e9ae0da3479a21953e25ebc0fb076ddd50c453db8fa572281b533c368d1219d8 c1c30acd5ef63ea26561ff7860779af188f2de46c130224a42b40428e815f3b0 70fd5cf293b297c21d749a3326eabc9e2bd6174c42335184d557b23abf78f195 ec80a84428912d3530d73fb1e6d5d1b97e47cc582b7fee0d0ded3dd24e19246b 6528ef56356e5362be0f0e27b1e03185abee356240a571cd771d54e7a334e6b5 5608ac54e45542fbd2cbdbec58b8397662377745cce08f83eb65291989c0678d 2fc11aac056cffbd7413abcf837ae89fb8bb8013a119cbf06d9a67c654ebf964 c85f34dd3f7a2488252d8bc3093517170275f689e3194f2ccea411dd465816ef 38c2979f0a2fdaca78c9f64f817ae3c527572468bd1470af6a222c40aee014d7 a06ad63e3d230b5e236e125d6393c6f3668d291a030b4147b21fd6c3d0030a91 320023987469d2b8e1d01f97836917e935bfd88c12433e317a1ae5386151a781 e9a2e8c569a93ed4e6d57c8284ce8d57627013b8ca4bda100d03c65e7ccfc613 7272b08a77f89b1ae78fd222537eaec7761dd95606c02b5d2ce22128b18c7ed7 1ece19a8533e9114a8f9737e399a83664bd132e4957709fc21e7655f0e593a5a ad004eab46a34daba2ac26d0f717c7578e6877d401a7f8f83d58c782374da4b7 a07f6b6f36712946f56d59e7cc4e3fb5fbc2f899a6d89c4698c42c68d41f96a4 7040298d58aaeb85c715d002bd9ca4ef98f288170e7c547def749e8df09754f9 7db2178228701541cd499f4ff2c75316d44579b213ff5cce8510966d559abdd2 bb57a8e30a37350a117a76b482f40ace7970e159f8a1a1c5723d04ae0ff1d28a 1c81054cf615d07a0babbab0277194bf5c8635b27271d0c09226e6b6ab616711 6f12acf9e73782086514867b7d2a7df82c50c09df5e9520495d771707da86bfb 8d05ade246eb88dab2bfd73bfc9458c225f63c70bb680b654d5850b092d6c94e 5c33910f7e40656a5090a4900a9a07da1ea3c4a9fe05a1ad46b426bdadf45c88 9d5b97a810a4dd9168fd132ed43d4b0fe9e8aa60686a047f2211702d9ae93177 e154d7f1de2766c30f3770223a390fdb47d33ed8cd5aced796b372f62b0783f6 38b1f425a3018e330a88b800a0baa4dba6ab0e46ccd09b6ec43f5780718c9590 d9df233bccc115d42dc86885e292a628b801553294597525ce9f854cd615a5bc d9daab0cdf625067a856d82a00d9f879ecdc3eb0c46be6aafffe93ce2d5bd54a d73ecf5d94d5875db5ca6decb7c8148e79b85ca2e2e415c585f26e412c5962fb 82130250c24ba6cd11ab1272f689017d3f2c2ae05508b80ccd6756cbd57c0085 9bdf3bf30e7c8753f43b128b65d87bcff520705478d33288439f5ff49532ef62 7f9fd5ca8301551ae76f9007de7a74cb94b6d2ce8f381f1db4be33007833b2fb 611a7f96e410d4d4fed99954e765a9db35bf5e71f2bb9019c00aa703557c6829 230c7eebdf62cb5d1a8195ce5308287d293e3d8c87be948e76948bcbc9cd07e3 1e38e14875f5c4585f82ec1da5762dbc491bb9560c4bfc11676182f5613666a8 74d4fe802d457597ab1a4ada6e6ace19fa6968e5eb9b89191cbc6dd19c1c31d2 29c2556433c3942910f0b9a2cc4a1596ef35eca1b2e9ec9ae5e6785eea692ba4 8caffaa3071a042b9af8c967787cf620dbea1c7d05c90a7b211d30c127950bbc caeb91208f5c547382e26bfa9b78ad0e9d692070e65ba3bbf4215983e84daabf 6281ff2322e5442d5143a43cbf4e98fe76afc49adb5286290a273fb5e89dbb04 3ab3f24b7cded247aa7308a3a76bc0cb5de2c16606645ff4a755ed646aa8a43c eda56b260cc88245a5456db68e531fd4d64b90e59a888ae5c4126cc372ddf6d3 6aa11fc67884e30a63724f5575072ab1534128633b0e435a7b6835468a4e8112 397f13f24ce0a62c85af638afaeeb99faa1847c6e5429e36be91e6ae167d9118 8e2a741cf3c4a567db8bf67a3445c782f364672a3b2e28ecbd641cb080a3200e b428de6494077fbcedfa9c5650a2416c37fc5a4cd4493ad4be40dacdb99b44e6 e9f6ebb76e7d06837540060f07e61c122cd5050330cca2111cac02e1c1385bc9 40b5c6c44bfa89da0010a4e7e929f6b7ecfb8db4830d3e5b014f028e3cc63457 acf135fdcc42c2c5ac19a0ecb7116a1d4f629f738a3fbb7885a3442aaa7c9ba5 212f0378b274fd4e318b78b120f2c73165e80919e126b53d0cf7c919b7fa37ac 1314b10163ed074b68f59f5e1ba69b787da21caad427637da5c29a7260e5fbb4 e17080b7151864490502d5ed12a49861813e89e99da896b656b5fe7b53796ce2 0f3fd1f4ce0429406d8fb7d607cf9eb3f5c5833b2bb7faff813f77e737215387 c2164e3c42074d3a4cc7380d8e1f8f98fac0cd1608d661199a51d528a1d26463 bcef635b1e86047ef1665188578e818f0424c9f34b1e0ea474886da88c0418aa dc3d27f81c84a30ec0b0828b5eea574f86b92f71a82b3cc3ad3ab2b4bc335f05 3e7de6a861a5e3f1b8d05b1e136c86d28b27d74fc18c5791bd8ce8ae10180e3d f8e46096d12baebea3ec35718f809a2535616263cd4c801ff81faedc18f784c3 9ca40cf71242200050050c369b89e90efaaf6bc8fda2504a360d44b5b054e82e 1863893b1f651e4d5da08225e6c481e04fdf7f5f117a98f73a0a3c41aa1ea9b0 f4eaf83b527b7215f403ca1cbad30838c43c7e25a0a9d574922adbbb6eebe5f9 f30a9d66cbf000184627683c7ffce657e7df2dce264f6f97b93e975e8b4ee3b8 5877df10c2a8227c8a195bee1b56d2a4f6c2f6623d35dfef89215dcdb1429d33 63d68b5adf8fc126ec75a5cda709292fe8fee445f47e778174b481393e42118f 0a50912ef9b0a524aa3617c30b3726583b2de47985741ef7c3174ad620ecac78 ae4cbb4b42fdf79e6d0ba67602f4fb0fd5bb7b699adbcc0067615f9eec3f7ecd a4cfbcb22b92554f27d0c89ca089b67ac14337ddf20601ea7d490510917df8f8 e3d4ecb913ef972bafc844bb137340a009f3821fd49e43a064c5c1a6cffd82d1 b518dc4abd184c6aa7cb7b0cb007d6a6fc7525e2866eb2dcb5c7ce1a38713a8d b2aa6ab77e8d23f5e6bd47475e72b4ed7346d8542de54b53b69bf7256f5c67dd b3457fb4814e971cd6ae9f613e9036ddee0ac22856ee47adc42986660b942bad 06cbf773f358408e505297c8848d914f9c12c13da9cb439062cca503a02b71bd fdc86659307070592226ca5eeb9e4c8ad08c12d88d5458029beb34ffa13aeec1 a796ddb9c5f5e2638a68ef6a6f8ba35700fc12371a0914b59a66693384d18543 42b0cfb83ba1a31c382649b173c83c36ff959b996522156131410c65ad870957 944bdc39da1e91f02a5d37709f947b1859078b543c748539a5a9ec62a3c90333 50e0d3e5f09bb25e998f190058d95d8d1ec4960277f367855d36f218c868de81 99761478283bdbff7898599fcfb2cf94248f427cdd68a59f3ccc8b2d08ef39c5 5dbbbe3dcf0b6039b83c195709e2ad5e198aa9b73f4582b71cf6383f0ddaa9f6 c8823a87ae280ca3f277bbe7220f3752c98727d91cb5d2bc95586534013061c4 e33fc3ad96dab94e629e97515b1496091943b6b5fea5d867732b6dd6e23b2716 050178b693f8ff4a8fa95b08c592c017dfa548385cc3c2845ea13b8f4cb2c29f 5aeea043449ada22f73778ac8aeb6e9ead9e89742094080742ccbd798bf0d962 5a235e085e04ed1f7e51ad30981874f3262140fb093bb5acd9ab525b4b849670 2aa3ee91a30bae9327ea322dffdb55997b5fe564d1f4f497b106c7f7e4d9f2c1 34d95032d62502eb803875bf13254285625077078a124b4332a3ec1dcdb0f2c7 5cf92a755bf05fe88182eb3900bb065c74baa73aea79643a958cec0e3da3c128 cd688fb8c76aea1eeec85a0eab8ba7b465e28ced7d54217fb02f9eaf6b66e350 73a45b827e968477e54f330df27da961f2d854d95580422ee3715904bec0ba5d 68b96c03a9bc0b2bc51d82690869dddf22165e31d520c9c5070285ba4cefaf81 bae1a3ae573a8208a7d185a929a35d00c49c02d0fa2cccd83540f4329e980364 39339a963b9f186a1c6f531784a8c655a96b8d34d5a0a6ad4b3537ebb74eeda1 09550a2a668f10dc6b6eb67232a072f80d32517c2c93710f3ec775b79f2e176b 64edac975d759cf7d6e84b407bf1595ab9daade5b2685dde64a458825d210f25 8b2552cc01d0e1fd86995d1f5cd5c5c0f05c08ea50206f2f9f6de3d448ce5cf0 8f479bc0456d9002d42985a7ff15dda1a8fda11f46e5edf5c37799c60b6cadf4 4877df051b83f54cb372d9ecb8aaaa7e69bf0645909c8792ddcb360e657c2cc1 74093a7cf7fd3f7e082416ec3f10ee66f3658c229cd86dfd28b67b560a66ae9c a84fbb1b3a645a4bf8bc6ee09dde34a02cc5f64fa8298139be9917669c07a271 b01e4e401ea1a70b66ce26478428178324875b9f4bfa23a22f81ef046ee5f575 aa90b95892e4b4c6b33b069465d2b0d062a4b6c413ec1395c8fb89300cc3bc3d ca191300943c3de0f45a44961b4a4862e194188bbbd205ff6eab0f43fdb0b7a6 50e49e4ed7f42c9c85e38dcd4963cd911b0df7347d665dac155677ade85d0521 6f04020c0ef7803c82ab0af865417378eaa5d838c717f93180a793fe3f04d587 b87f38abddde26a45bcba00ddb1cd51d3341ad5f9f5eb37c2059d3eb4dcb2856 70dae94de46dc47a87a91789e7c894eb0c2730a737a48256cc42e6f21c6b6818 a21a4d0072b953614ca4010c2796966db821a27eff358d61f7c8cd84c679f770 c4c74794b1a328098775609f8d9b32dc79036a6fe52a823f1b3eb4eac33fe2b2 508685014abb37e9241fca5a5f70df78d99e0bf2c7e93c294725c81955f74b3d d2c8df21d4aa253f6502fae49dfa95c1f5f782e03f3ecce75c036b2d15987426 540b010330f8826988f5261376e7a3f6bbafe1310276d2d558d33823af07d36f 0309c07a852b5e75e95df557cd9535a565befc1c4d57b0fd4697bdc6cbfb917f 44d3f69cbabd7e85d603c14951b1696d05b46b7246ca4b0b553f27217c314cdb 559ecc7d59c448ddf99eb187dfc88c6c4f6cf67a7a1c17eb0b6b86802cf09f57 5b4cccb2277192cc95065f0e07922ba763b4d3a7ba8587741c1964343564fbab f137c5a98ef6e6a310bc66ac4ccffe67261759ba1fee8397b78ab9ab399cf730 56a1a0d50e904fde471c5b25f121afed00a836fd23606621655854cb1a5d9791 59837e3d2ef54218f71dd55dff17895c1279fa4ee0ab2a3d2e9eab61995abbdd e00a8f0e66381197865e24e73bca59c02a78f5d257a1a93c77f06339aec6c1a0 25ee5160d0e68725f50b45b13a6cf33415cf6626f608763157bece81e7f9e2f1 47760c379a4035b9d3d5036fa9c54c1242c5b8af72dea49d61a1ac5d228ea76d 8970c0febaf2cf3068bdce918719528a83a3ac12a0a7076bded44b291e758cd8 28815c1b02024bd2f8add603bd492c0e6021b1cbb1417e1cf82b6c913d5930c8 ac0b3dce821c9fe80ae2c13a1a82a0ea835a21f9657d06751fc871066ee312c9 04f288dd2b8ddf1c0f8966945aeedbd54739f493ba0eba0298027cc9e6ed119d 527e8eb120971e8a89882dd4954926eb99ac781f70052832dd6f080fe8cd258f 7339cc8f868a415d846c2c5835f961bca6f7eb7d7eb857dab373d127571488cd 8116951f6eb0e09ee784d3dd99175fca35e6cf4536da0d77a88068e3478af04c e02cf4417fd6d7cef5fcf9845ff601b6074672e8fbe3eb28275ed3f48c322faf 1610b699cecf5e4e8a5f90dbe5fe9c0fa4c18042262745a5554c3708f40a10fb 91b21931cc8e887cbcacf99508c8254e621fe6e68e23fdfc658c439bb9b799a2 e3c08b787c4ff3affa0385f429f3e734c70b80f370756d7d2aa92ea207d704f6 ef62ef94081d5515092a63b04ffae773a7683153ffc8d35e48d1a5e850d1bc8e db90714479517e26fbc20f9b5a96afd1f13cd284fc186f1d61a2c318755b4237 2c76401aa3567d757267b9f6c3210872a9ead54e039b121ff3369ba995b6fe4f 5eadbd4db7652990be34c86528dedbb44cc8579f5167a68a19b8226966494353 2d2663550e4a86392e09b60c4412e1b9786ba0dc463549e17277d3b28888e4e5 73dced49bda04b7b03009f9b55f7f402e6a37a42c6f8831b2625659772fdd3bf cc7a76774192d521b8c8cf227079266d446bfed1bc2c22190022872a241ea0b2 736b055baf2643f6a00a10f1827f193d55aa37359de4e9e78d4b659c655ef802 405a6ce1c2952126b3a93a1a7b7a2f1645008d7face55bb4ce3f2d11b7ae167b cafa49a4142d2ff8e3dace1567fa0b627b4f79669806d4be9b613cf0f5549397 ef98745772a915cf30095899da9e6ca90f517cb1fa1b1010b5ac47c9d067b545 38329cfc68a7764694af576e7dc6e8e1a8c3fb725d6535787c0e01e7e9cf352b 9060a369637993cf3f5935506c68523d353c99f4ab61c56a84a017c6e430928b 7b18d3435d56f85db1b2629bec56056956c8e12ec10d7d26f0a55feb9ec666c3 607fcf4def690c0fa82b2c56db3a5cf21d1370fe0ca672cf57f3007f5df1ed75 50543e46adcca951afbad8fd876d85a357d10e2bf07cdf6e5ecea489098426e4 5a235e7096f8e09e18dc66efc05dead655ff1aa55b7a8a6d65f4667d02453466 58b9cbc0e067d7685811e22e492872608d480e01a3de0c0e7bc6d057d3e7abbe cdaa205ec2295840534f430410af99b5149cb38e50a8d581eb64d1677d018f8d c19234e183949a3ff3f87f0b0b88ffbb4f473a4a26d8d0ec32f443906dda29cb e37667e75b1886cfea3b1d7bec612991548de2417bc3c23f21511d9a992c7f90 62b0a18fc254191be318faac0d04df56d9e2b8bc782fc773c0454add8b69e48f f20f00b4e49e5b3fd79845ce88200dbdaa4af7ab74b6c0985e01b83012d9f510 f56014c95dfedcd65f84739fd363a831becb7baba576d8aca0deb5d0f515c38e c1a135bc80a3064eb49a43526cc9794bd4302eb84aa14bec98d8197b59f6e32e 265ae5c39efedd69e17201e7fb8dcfa08ffde6cd5d693f0339db77ee6c15535a 65027c4f40032a60323ab87262e2ecde7eaa03988f48ab1584213bfa1a4cbeb2 2fb531fa84ac2c0d71cd3cbd7f67d9961a03de3369f6678848a10f799f7ff965 49265ae1a1d7e2970737aac3fdfa65e5e9f4cbaf81fcde3ca0e8b290d39803d9 257983b4a823dd6bb913c020dd34bdf864b1c0101c2254b88f2ad8c8522e2cf0 21649b6c47d10accb5365016f5429bf54fe5e868eebeb03ee9f05285a720439a b262f7280638bc2eb63266e8a4b2af857b0481ad44dba0ac61cdb80c39f02c7c 8dcf86abc57e111cb76c1085072e7f45266a121543dd67b8e67ad28b5743d2b5 1f54f81b3a56b3d5416bf279a1a8464e4fae4fe9562f86d7025620d8dfd93239 7c18df7d1521eeef560d456ea4da567ecf275fab6c45c87c42f7633e5f48b601 f7f289adf88c99327cfef437e3d3cb330ec79f57f86c3571e2866382655410a9 b6705ae7b21cc4017f5c3cc3a3d30d73d8ac26af486619a18b5a2fc2964dbc73 a0218f1f777d4bb138d45b5ca1ed254a907bfa7fde21d175e158b5174c85c0d5 56aee672f96b31599c79a1d211d4bfa0aebcc7d86c7608103d7c8d9a09dcad15 9e291e4178f5f4983f111939b2226984156159e992aa259f1c30ee3bf086b5a9 ecf93d0f5dac16e2d190e228251b56fa80af163d19bce183824bb929629767c1 00fe9596faa73aeadad64d90344a18de15bbd8420f5c8111d3ad1b289aaf921f cd8dd3c47c1b4154ae667d704f9906430ebb4e321cf1865f58b140c5c71fa55f ccc0e7b1de14dcc5d50696649b34a5e83dc3016fffd96ff4d86343b4ab93d792 5aea572a97333f923a302e9e5afbaad1fa99c39761163a2484942ac53666261a b6b9950e08c3d8fab00787574f5331b50cb2cda1f6877f29f0b7b9a583ecd057 57260887983694df613d3fb3a24df57a98bdc87b2c49baab6dd659bd7a690470 8f0a466333a26280d98e682b0c2622b9be38a07bd3d80ad74a39a622fc3b9bef 6e03d51366f436adae45a75bf70c068ddc456c41be1fbc4e6e776cfc2872e6bf 8a2c9ff010d514f546e2151c8aa23da2e4614dc19ba29f4841b1089d3b8e859e f0fd5e38a5d42f9bfb0e14c99e94f830bf2fd4706b1718bda326fa994c4a53af 2cb472f4e5beff4fa856b05a3a804904b01f317895a32192916e008c2b8a4714 3e670ab7b3f1dc9d117c6ea06e5a7bdb17a9daf6e0f0ecd7bb4306d20b32eb66 4b5bbf29564acaaea6696df1572ebeb27faa8e4f029ee64239fb631f9171f057 a7ba2b13882d23f2e472ee3c9ec22a958a2b3550c576c29feef70901cf67ce3e 51dfc57ebf80716e7277981bc31af513961579611a71a0151d2cd78f09a7f106 86895713cb7926df7ee57134b2a2e1f1e6ae37b6430d4348d061b0ad3248b637 3c8734aedf8fb083cd48df82e63783cd91e4071e3a255e86ee459966de3add85 07daaa1b1f469ff7a65d0cdeb88787a3cf44c9413f558a9fdca2e2aaa8144df2 6f724d754e9f1c2ae88f8903be615255b21f757b1bd5522a1a1c166989d35aa3 498885763af6fa3cc4470d799b272ba9eaba7db2801ad45014758a9cccabac6a f5c07e13c4d599347255e8cf017ac0859206599f6bc3b1f9ee898d35f86a6855 1cb2dd913cfe04367c0189f780bbac8186813cb41de7c4ff566bca4ba8464bf6 3f46825d0d43013629a15c9c585a0a63575a75fdcc0ca8c21ca97d4ef583b616 1f930f7386ebcb1df9f9c79c74c2d0b0ea630cc523671efbc81ece8c93a6b15e 3b83cac1d4ecb2d0ed46ad189e24ca496aae08277dc991b3417d6bc560a84545 8c77d8c538fc6fc211932c2ccd0b953cb5dc1153c3aed077efc0e46431a720c9 6e9a168ee9ce7cc9edfbc4734e41e23269f2f5bba363165a60dba527546adcc5 25d141d402eb655f87390a7df836eab00889deeee74fc11bbf2c0d7c4870ac4b 3c7cc956c2c8786d0da97a2c99944088a0d5e729db5a7ea29c1ee7e94ec37ba2 ef89f68faf27722fe2f0f60af1f66824ba9f74879c1c64d766a165c715659967 27c803b5ee390ead2617299fad1ef33f90bf181cc020d56e2d1fd7933b8a818d 4987fcd03d8ff03adb682e59549473cefc946f3fa1c959eddfa84521dc986bc1 d606168abbe93512e1edb1cf8cd2423945cc9082383952431f52f8dfe1491078 c1102b5cd15d48f94ac07184ae107184226d2c2bf7ff872ac54c2d59e64cd605f3bcfcb2a324b0e8be73a695eb439ebad0a6d000b7431628a2ba8c54c4f3d6060c824810b8ec1414cc747ad4bf3dbfc3b18c03879d5e0dbe30e876efab978104210cbd2528094507062a5de8d1e944417464cd5d8a4b1819b7195cfd318450033f917f937ade80bffee2d7b01d2d14fc8993699f4e9c95f1bbde5e44fdb77a05296a6479c37a6d31156fc99e487cc08160a9047625afe1b40e2b67ce567ce506af0d8b547ecd41b8f3e1fe00d4b66b8d5a7a1e9e6d733bf19e2cd7db89cd3405dcc387efe062e4bf7a4211e2442b3b81d57fee14a2bea28b4cd29277121e650bc6f6861bc38db0dd53e75f72b710bee7a46c59f8db0cec4e9e6d44801aa2ba0a96a28a5fb62ddd8a6e0776ceb9ec55d46f2cbd57fdc083459c9e7e53fcdc42070b590c03332016dce70451b379fdb2d9f4219609f44dac7913b5ff681f05a30c802a6bac37628333684d6908c7ba6600913612502442b8abcaf954d87f8b52003e6a189df76d4ca7c13bfb405b5ae84a214f09589d03993b5a78bea795f8df00e9524e0febaeff80e95df6eb3cd17575cd6ee60e59288e2df6f497a40c9dc3077a2641cfa44d9937b7fc2ed84e5f4937c7c0cdbe8fcd702337c642219aa94c0dc35cfd8c4b5f3708acad0466c4d35954741cb77784fec9e2464abfac436ea60800a96573f2ad04fe7683b6f61e8a6a53c794f1bf996161e1c27bc963d4489903a0ebea46e1817d200d72351b4d3720244498eb9c97dbcd0ba3e85e22519b0d0f97c654f21106d49087884c410341305264dc20ff2a0d107990239ff80c3f000e1ac49055e7a10d24738c710317b51a8f9e64546c4526f2ad68bc210a7e50a2070924f394fe11238351c97c9e874e8cfe7860d94b670f0cdef6ff2c6812d45c0745be8c978de06096cdddf14a30f328d78fbe0fb105346fedea57653dbbeeb40c3d30ddac94aa4cca1abfb7e03d459684cb13c056a9d4972bb99116d2923d6c01c720720dfe67f1a0226d34e8eee635469c28732843592abf4be3f65e7a3e3400eec12066d868133a3819868e344e38aa7eca2bdde210a1a009cc06b77ea4fd0195e25ec8b6a30f247bb5cb9154cc4c8c4de820d3cd912233cf8a3e7457974805344c22523999cc5de4652da5ab7787a647f1797d2a86a80699240c558f1507063f4a70fd6bba03e928e5996652d3f9c6658fcaf9980b12405a97b583fab92b0274d472636362c4aa84df4725aa3c7ec3f3900dc69e25034a7970fa7a57450d046e40e493838259d6dfb5a521cb3432f11246f027709742af3a6909f5ce084e041bdb7105217e50aa4c1c394633a279b84b57d7260f4767d98614d7203af65f0ec68854bdeadb98c9667a9df3e810917c9dd178151433b5d675e1eca5cbe73c05eb9915e6c729829cabebd084ad01ad273d3b289e90cea92d6dff40bf8f943d0018027b5d7b6e631451da9076fc647bcca471b77092c0a0ba518f206fa5017b0ecbd2d5f87afd93243938182d4f8513f045021b8323b3cc9b57f41e8ed1713103beab08dc1100e4a51787ed3c83c8cafa8d173ae583d6f47948487eb683c95c0cfa48d8b30444421e6c52b1560401adb995c7b7553864ce99cf2121c474fe0b07c77245212a87d9f0c1a11ff9030a7b323630520aa680ac8fe59f8dda2481bd0941d63386f79bc3ef635b9c9bf53804b2c5d81f416b1ec5f9d4305eff368377056d79eff735a783d26305e64e0b2cb8bac5f40d100d4d4baa0885a3a265088602c999a4e69b78008138a66c2fe1dc386f6128220412d338f1e962d9d26a86c80c2cedc6c549b0920a56e3ffa0092abe5beabc348206446addcf7143ae72aad9068bcf93f6d29a8c61f08dc87f722e1c6ef3b84fbb18de2519711cb7e344200202500474a0c882ae260c263c2c48aa39b1060ef19e6acd9ff9e5d23c184846fa04cae4f8597ac6e9ea742811184149103d84bde0fa84f6448440cb9aae3f36240d38b0963707bff2c60f1b0eeebbcbed89ae35aeedbb28a0d0f7b034ae3ad1660e663ba0e6aa65407e554955bdf75b43c39ef6547defeb2eebba84b209890f9d02f46426690caae26e8cde29547ec429e35840f5fbf9427906273e300eae37f40358fd8238ab5ba07ea949329a4a685b9c5292c2cd446c32a639b3f5457b1fa708d2b85dc715b4f4ea581e42b86fba17a2954d2d714299d151fa32b06ca6763107a47b371f4a23b0151c896f497c3d1f833b99c69bb5399c8439521bfe4fd4ca05f3e7eec523ab33a56ff917d7cc758d136bc4a1a2e0be2a2a64790f27acbb55051bfb3ee0976f5a9a6f9e181b2f68bc38249de3f811ba44d608fb262db2db1803238a5fb13b08a99e28005567c2c64568343fa56745eae97cc7023690d3180e08a9df0a4d5f83e69e34068cdaad55b9868071ec3fbe74388e7f4800dc8b3b1e060b94fb676c18c59bfd1a0a6e33483e1bc2eea94aa0ff635e26a5ec5523104908a7b14646a5c6d71f7a63ff7666c03e7267f621d9b690876c49e645216affb60986650b3cfedf757a1b17f4c55c5c909ececef57880b18ead91ee4b80b683ef0d431db09802403ceaee3d0f847b18c5ddb59c6c1475b38ebfcdafd04eb2e3fe0739176642302a78eb7c3d05898ee867f6c61ea108864d4cc20cef9f531cd76e0dea1b41c2cce2f2e39da2c6b58e48fa9a92bfbe2d4c6ec5520cfbdd536d53910f9d65430681e678f3b37d39035d8b3cb89ecd6fad27a6f63b812bba0c6eb2ef0742ceaed658abc917d3b84239ce598f380381b840ab7b14a9cc0ab92cc44c430da4681b92e8e237aa76e537cdf11a72dd74bc03458401b1a81148877ac26730027dbf289d57a67715608146e3edc936459fedf650621c3ac3ffad5f298f513903bfd20271eac91889267e8a8e5b270737fa4f7b817a8aafcbecf87a9591800b0a1216457a87b2384d530514e57fcd03d3cfa82480681db803283d8643fbdaed063080e94a962582a47fab5c5332433c38065ca145c0811352d25122fdfeff2907608cad548ae7d8d3ab60de2eb8bc6039800f1250a8334e6cef9e3a7e32629600031dfbfd2e43edf94dbeeaaf49051ea367a085f8ed47c0a7aa4c8087dab2f50035828ffc454b18b5ff38f28e67bbe1056093a78ebf59165504a67abdbd955500a89860aafbd47f8c9e63d1cfb2a6648cabcf6a664df0d41f50779b2642e4b203876b50e2e3a3649831189a76db3bca93b162a3bb16307b613bcaeddcbd602c07505960d60610ef280e9ffa1d34e769a04a2b18c50a8925480a7e51f3c368550dd5771cd60f69818496855b7cdaf5aaaf7d3a33d8377ae6be4444e0d30c756b0c7d4888e89c2ed8a515d9b25d69e76ffd44187308d876c8af9b07b5c07e650b07934905674d03237b37732944ee9b20acddb1acec66a1d3d0067f4d69923a5f0bb9e1adcbab547fe875f725f283d5f05381405f7b4e0c08da493eab81485b7302d636befacd0a3bf83912b1a3eeaa9f5c6d9d304258514afd717b23104daa220c6492b0cfa5da9d4abd11f971761fa9e7b9c01d679e5714b1b2b699bef8c3050cc11172e306b8799922813c42a4f0e33ec7236b54b1e34576d10b057c5b716e0755ae89df886831bf28c8ab5bdb4dc17c090856fa00a1e40f9d1d760afeed14091a8f9baa34099c2e7e11c8c494b11d071b2d1975e7f0cf0e46773fe036dc0c0990b56462855cd2ccbbf299d63801a3976b42edbd2955f7d1b152512db0e4bc018c8a0b7fc02e0b8a6d455c24850042796d66b81de09f9449930f58483d46250495cceee3543ac74ddb7bb4c8cc91363fcb9e0189c44b564258824e0cff70980e6b793817ea0f7a9cfa00e8fbc29f6f1098183280506635cb0d31c9c63e390f03dcbbb9ea1357dfea6e3d81d8fafb7099de83fba4ad9d883481995c5a2631f009dc88391117fc75ef0c33d81d8baaf8e5c3c62b491cba9043b872f020e2cedf0e429aba83a7af0a933544cba28f14f2299899073464437edc12f3b733d334ce0b0e8ca4525a2cc48ea4dda5395a816afb099d49b41588ff237c4cef2384b7ce06cbad9bab0cdb9d86a4a17f46f1f1cd8ce89cedbe83947ad31f8e42e94e881e0be322def6c429599945b70c85940fbca7bef6e31474ca7b4fdfe1de6e2890820cb5916d0703bf8a2a2f52301f257bd174c0c41524e77e46b4be1ce5c77d8fce0b09b1684fd42fc663d361c8cc1e8f0a0669b7d4319f7e50c495a63635cbeb4205da1bd7b2cda29bcd85c875f850478c16df691c232ff6058d14b59675461f3209bcc7bba0e949507b13a22aa4866bff6d385ad11c0902da0a08a8d33afba08c0f6efddc1370981bec920b33c3c0837b9c85c838b7735e6ad68a8f7ede5339df0750fa1fbe482caba4f106c8288ab2f998a097ebf94a87d01c70f62dd15f83b9046737bd9337e1eace768ceb4e684200a6f1cea7aab43e15e875d13dc44d05e10fde3ba580dedd0faacb849db9bdc2fb492847f2c0a13c055988d39031d4af220e9179d116a04af7feace1afb0941d65d4d3d9abf9c90dd036bd862858439e010ee6c91c1406822fccd7d1fc6102c027b950ad109de729df9e147d3c4f92528a02743f4d820a122a89ea235616df6ba224fb4647c855d879386b4566cefa71a80e8d56c7412b04eb474a282d0f7f4958504089f985f930ea447dcfb8c9f79c8c08d6f55ee77bd03b76f67ab3675c3542309935b901b6061460af0c9e82574bd70a4289a5ef67cf5f0dab26fbddefc0cda7fd60dd9421943cf1ffdd3738963578050482b5dde34e73d28892fb50eaf0d5f1c35534ba1c7b28da67abd75ac8047a0012d1bf92b6736de38b1d5fa43a496fe06e67c4fe1bb045fa959dc175ceb7170fb0b2066705f979572e4ec340ca56e0d2cc2bdde197a75299bd3a3b7ac2b0ba0ba16f42d4825f46525e84005f0bd72461b3cf783fbfbce308888b90de1c08590a823d8a2330ff7a6667899a8689e287b9cbcfcf9974c83aa539834d27bb623e0b5fed229324c3458fc019dc277a83fe099d0376c78b7ef6e017923d7747f3130ea5af98fed590e592d030035c7da5069f33a9d296ac477195e50a6d229518c40bcb2d41a836bb98c8ad5e7bee66978fd0fb4ac6d1b6e9beb57d672be7431b0904f5824c26feab38328e3db969147a72ad0224bc34873bdb7f59fdfe54d14c7e0b3ca8d121cfccadf63d06c93e737dee891c81cafb1be32447186762175e1b8e087283675e234c48d4bc7bd01d8f8da952784a28b99e763de2dac6144dcebae002cf42f7342d34cb1f514439c3ab9278e73febdade27fa89f2d23a4ff202ad640e29fb8a5484f63c9f6a59cd7778e8a7600869e9004d1cecbd083046cf4a3c4a062e9054627cdfcd5176cdef530b746f0e3186809636bd09a0831631c667ad900cb3dd1501094d116e4907d92a26547a5064de2b06cc75eee5f87204f74cecb9035f149845e59242f5963fa21a5484e37e696db2c9510b53691c5808f0131f560cd436856efebfcd1e5ee49f1354e0d6bcdf029c0b7c7a6c9d3616d1981533070c65e8cbbbcf01478f2798b151c00167811e320f18ac340ffa740f15ef6053ae0e53910f9fec8db44e31b4ed05a916ec5fa42e0af8d2c9cceec125c4255567890fa7b477b629171af491b9162034c337d716d1ae078ffd7e37035607625ebe720bd724b7e5d2d6a1fb503bdb6212132e0ea903a8c122c506d127fb03e6c8fc1e095609c7f423668fd202ab3652c778bf0258f5e6769a326e5c5f52cf8eb24fb8023ec65583b3bcfd46fecca9f2118099f07b965de4b854d26796a422531931ed023826a134f2aecc2478741d868265903603fb3639d73d253fc7e20e7c6ba3ca079f97e685c747b552e91a4a3aa26c8a6b145ba890d49b27dd14e157e428ee3601e73fc3fdf1c68dd3467eb6971d4c8e38518feb62979fd04ca4b1d86f43ab1a076477841e1b958123e7b8e3e0d6aa7f1d11ca12e72e9861704190c6bb5384cc06980ad06026add7fb61953d1ccc7f9abf6a24fc9ae18a1a2598a24f83b553080872f82b53e4c2d321feef670a1d9f251017cdcd7378d5d1f32fbf903c54fc09014a2b24089d5c379653ac9b58e0e1f5019c6d2e1e8cdc2b3858b2fe908d260405fb4e13cbe353523d376c09a8dfb7c94854da8a30a17c303f7b49a2e8d788e90f11ef33cda04db0f66b375d20f531ad808202f8f7e65006f9e123f2d671f9b80db32277f9d94741e77723671e993e14d41f215645ad6f111256178abebd8c3b052f3ee7c40c40a01d56240b3c1260d9ac5f79f1b7c9c74552037247956cedb60bbf804bc0460fc361feba9a327dd6d70273c4040763d5fb435f105a346fa0340f5fdea398fe9eeedaa1985ce03b04ce54a184217e4ae2b5521452819c3ded1f0196ec6928668de046449e632c6de4e4c3bb57d5f001d0dff0907f4177ff83a50b35a52f2f64f1c7a6fedd2dc2ea8feea4480ac4f68ffffc7f8343ffe2dfd7fa054d882484bea22998246205d8991c64e308487f4e56b36279e0988f06fb50360dcafa909694e1484de91a6ce0c9e077e86fc4ae6582d303be160b9626835cf005e68f94e829bbdfacad785f9c2437d44555e27763b76214d8532d4ce97f911500184cdaf9c6ed16167dd4f1f1b5a559d6602ac416188241cd44dd1845a4f7ea00e051ae261ffb6e18bfebef991bb81e5a515e3569214552df8fcb302f6d61e206bdc14305dfadde13bb966aa7ee49aab82693f606b976077eab376b08736d7e0c6cdc20358788983c8f016f7251e8fa744e08a7d078b7ae1cc0f58c47be61740a00573b5117ee75c03560e054387c9c3815065568c05b1626b7aac16c720a4e077457bc88ba3bddcf4a6f948b9124eb6edfc1c21011ef2e98d297b92d08366f04cb6fe4ace0dbf0a512e442ec1b4d165d9dcb2d69cc789b958ffd073abd6e5c066cf674c8592bedad2e1a67c618332576bd8c8b31f70225eb5762ef255bea2b05536f7fffc4c9f3e9c4c436849d4c0d38640020110fa584bb38ba8d966b4d6b0027cec867de8879fd61439be576ed8e570189218701fad05d6b6de6a6b633610009f92891f0c1f5239cfa1d1074cee86b059230487880c0795cf954a72fa08e0d6fb01ff14efdf99b2b0d6c0db62253445578bb62a6fad3faf9dc02a53981840c79f356b0ef82551d6e58331cc6db14962962d0c61766fdb1ef3e617dca4cf70955bfc9dc3cbc7c93c1b657e47de505ce687b2371d1d818d003bd3e05902cac010e345b86122d045ad17120a154025b8715e511c9aef02198dd50e94234d8c7038b29887532d64f7fc40d169165332a56251ffb5aa3e7773802dec44d74ca380cc880ea572a8572468896ca35b471d51d81e6e0522403be10464ee36f0d1db200012470f71b2289521db73f7501821367139c369054ba777daf38c07561f82f051b9b7c7798d89a275186e37d080e756aca55a8c6e6b75d61bfd7b151d221970b7939121ae275416bd39b2b4451245ca2b1f68280bade1e1eaa698b07a854ed084439a500c0ccb6b361fcc368a166f9626e2505a8d28b3f89e1c62eab856abf02c067bdd2cd5e615af03c803ec42129238f74d298b2902a17a9a01a844a9cdf0ee34ecbaf56cf8ca277a17ec42bdc3c0eaad452ca86eb21d8e3c6eac358a5590cc2d1209302ca9f654326d7fc3d511579eae509023871c00339c510dfb41b050ced480ba15caf3a4ddae9ae0df415dc045ea321e5a0c92722bf888a5115e618058303b4ad03c2c944227af2c73fa257e97a76039291e17ea6c3116e1cf96d2f0d4aaa82f4d65080dcd60bf1d8fc599f9acc24b51b282f959929c3dc970725cb04f8124dee5a95b897e17d489d6eb5f55551b3d800e39a34f790fd3829f35d810cd50b71eebc0c7e6e3e5d44519fb3d8b80663227ad6ede71fb7e64de215374c0effb75b8dc87d5e65e8e70fda9151ce5accdfebcc52de4fa361fcf027bb8dfa0e42102f8577483dd3cd41b20188f420fe5d27a71f74f85429c02397389bad6f00d4141b94f0821dc7c109b22a2f894c128ff0e287646018997d660630ef0d3404f2c8e34bb19439acd14cf56a4e8748bd175e49441a37e2966e0f4a5f5d6131033067ff4f881cd35b3dfb85f2790d42379aaff9f28bab1654725536308d9f920bd2d83d01b796880c0ba82b8469e966d64b15b91adaf575886ef5ff7b23c5050ebc070cf118d0800b723484482e7abb51aa40db99229d116e505a03a26ef6690a74e6b54e4bd018147f9c9a449c716ae388956e9a986fee085b045ea7d43f9d0e6450ee44338f7fbffc1655cf58b2eb8174b2b4b3f587bfe44210ce5f0694b70f9c30c98dd9dd706b42afa6d33b0868e1b67ed45a221ab0254a2fa1772301fb0bb86d17c472459b6e32a30f39c368f4058efca86044eea060d5520bbf469e3d085c0c5d7f1c7b281e5ccace0e257e164879ee1155e2d59db12b795fb0a3575b083905846ff0b5476c30ddc87f9400dd61fe9c748a62e5c9e1b92ed53c2c14190b5af3896f7437401d5625c5d47c10ba167672a7a5f8aaf19bdc028cc61ee6e40f41fc03f974d8c335bd6d401aa96062f78329fcd94dd5ef73454a579ea393f90babf6ad5f0c54d3388359946543b3b579928e5c3e66a346e419f493ad880675058ea1a136fa31a086c8400f04139ad49a33459259979804045e58144a41a65301ce45128a0bf04e3e8d4cf967920c3062360c1b1e2706e7fbe18900dd8fd4db0195fed3c53185f7c582dc68ffd8759fcc17cf8aa3fcd3201a3c609b49abf115051636cce462f6648fff29f1a0bb2e12d523008091c0a04afe898f481e0de17a03914aa3877f24d832512c0d11f2223f4d3cb82a5fed01387c8c557b18e5105b02b4c3551278a063358c310856fda3f4b42ba58084d29383d2e289efa6b778c104324d2b131a99b0304bce9b00350d21bac5d84f4801f01a7ce105c0a4528f0c0daee740834154a43e179c65d5f9be4ad736c8190d83632afd43c00072be06590be0c462dd933ee555f0c61824792bfffd54c4e60ffd0a6e9495afb44080bcae0c691fbc2c85c56f152b4d8bd5087abd52e21a3730d2cedd4347fca2defa824609dc16bf4b70f26fd227c84e7e7d725dcabcccd6af79ba3efffd636566a1763406320d8bba822952665dfec174476bf8b8d605c58fbcd3679679e0a8cb78d7be0e884ec136e7a98680a8961730d5995b456892a1260f7576d6c2379f9ee48c4d01f225607b161dc79804637ba1c54b21753350c9b41ecdf9224ac14b0b3875d00cad8fe5f842e8a79513be6c78d3b8e3c001ac70b023935dcd0f1ce4abd0136802360b68b8c7a46d403599b2cbd88d025882ab732cf9e55522f5cc83eb62e9900e35c4bd4b235e6f35fcac32527d45a7b080376e220911f7639b97fd847db74b086bc57a6ca51b6d96735d0380c459390bec8159259b877432a2846710581b520d46f85721e8f29e9e7810820fd355d123748e65e9a7c3b55f163450309a6b2d04a1607d5fd265e44c5523e3c33f84bb96fb21379e8f438c2617aaccc2bbbe430c396856eb6287048aa5ef963bed4d8ffd39fa0da8c596cbb290e2b8aa7fd9f80bee57191a59614c975fd7aeffa9662624c7c9cd1c13c556111f1df9c627fd5307cac984ac6f025429a6edc77321d9292a9c32f2bd68ed99100d0209be181f5e0a2d9e9f903e91a26dbc2e587bac1779deffea9b2f87e9f2b20721eab3b7f5e1060a43391178aeb79cc50fce379c331c09701769304ec2916bf8c42edaae10980b4de7d7700a983f076e34bbd5a12ea100de79bbe52ba490f7799a5267e25ee406122e55e2ff599aa0e05418bab69a35c3a200bf904a2759861e43791cc516300aa3ec1766bfe697964dec60c87a7dc93298bc453ba9665b6e1ea6b85f3aa237055c1aaafc5b6f82ee8abba7a19400811599ea5aeefb8e132a916bdbeb1b54d500b63e55b0bbf3ebab4610a0cd507b38681700030a54d19874fd300c137b6d46009f8a798d7388e4623a0cc80a65fcbf6cf5ac81fde447e2d0f8de16b7ac23a70d98cce3a0195045e398065598e3569bbc3fee7814545d5a4073f40a521c1c5003f08f24521fa884ec1691afe92fa33c9b2e590b4f0756b73b7e855391315a9d0d0c95ec9f0f4cadb3c45858d7c5b33f1ad06673e0002177801ec2afb0c8c9d00ae653a7a80bc9c1568f11fc0c4f0d8cad0860b07f484bc3612b5f9ec9930e0e0398f851ef8c30c9f14ebb0ac47c23d667f0660262050465cf63e9d3bb9ff6ec088143c4ea7ae72ec53cf0d49fcc9b3ee225bfb89d95608917ba152706196b740e7325012c6ab9f22f53d93889dfdc80cba093005e8d1bd5a7a745f40b9bbfd203eee2ed0f8facd2e80aaa2bcb9cbc11c04edf4061759b017c0d41b7d8326be8019a31cedd76936c3a3e4662a8a91830214618c988675a8e429f6d15249848c20ffa4dd005096b3615f1447cf1c245ad7aee48978fcfcbf38a6d5c6b521b2b140064fa08eb0ff54dec66ec645000b4e11953967754348255a2e9864e73a3dc33019a56ae48f1b1e3f1c17dd97cc295c3b08b86b4a0d4861194fa1d0be80e76570560128a3f52b7e47d8bd214fa1e65ce3ba26db28f73957e4ec5c8842c53a3f101ac2f270660b367c8291e61acb99dc22a61e1351c8e2e1d8872ff5b84096656047e50ec97cc8db2f1acfacb5b7bd342ca800c46d1a8b7b7c3601c9af6dbd8ce08f992c86d0b96bd19b0a1f132634c45c307eeb2118414a048c6f86b8f643dc90a06ed5121180eb46773a0548c8aefd6aa59b1451a7866a409dec4ad239e7c1302f5c97bf6ea610aadd9510db1a9615dfffdaca74f530f60ae6b0d3f77b4e2dc0615dbe77b21c793454d3db4c4f384553f7eb0f7f8af0357f5258563194a2e460697979dabcd0c6ceff7cee844bef568082fc188b6e57d7a58b8286e2036dea90bc2577447287e8adf10dae8c12e892b641ee0112a9055ecbde306553a14608f068f0004c36f439eff93e131c4165ae681c1a7df737cf5b18416882ad10ea34502a26b48d43c09d348b6dd8e34248d48c6ea1ff3ff4438cc81ca327259f0da480e462fb1b68dbb580e8530876f73c8595173a5aa6a9a4f4a4ff2f8b7be5f85f702afc7a71330c1783f604c8ca62040562f9f27a83619df33909189403f86f8260ee2104df720dd437d46087c9d0d93f751bb35fd0977ab83459b0215009217430aa9e3271d9d0df6d3ae9ff90ab355effbe996033cbdd6a7df755f19adf336b708128298df6017569790777681319dc0cc9a9a721c1e1fac7c767986c86f008708af4dd3833bb8fce29ac88b63c214cb02e24cdcb3dab117053c93793fa3b4c30be13bddff52a0349ef51538c1aa919a177d9f91f678fb9966044993b0dbfce10967cf014695708cef041448ab78d39481244eba0621d55356204efb46fcc76207078be2d12e1dfe6596b21246774dd5f63106a41262b4d26b84ce2009c1268c07e43251c6697b8e76e95c0de94415aa52a51dc650e647870092cba60a0d8f3c028afb98a4e139da00d1f8b62525e73131e62e34924777d6277314b7bf21ca5a0d04592d9b8cdaa01c4f6c31f1d9115eb03b9f1da89873a6e6040e1de41525750ba3d3d2475482597ae098f078f59e50a332ddfb0637e53f21a2a08e0c88d199038fe83ef3e802a09b30ac41cd5c7c6b876d661b8d93e6b2c3310c3009f03b4f06c332782f699e4982fc4f907076dc18ffa95d9d8f283e87a90f9ec1f6edb77206d65ff2a5d997c473748791d01d3893d44e9dc24387bbdd5b353eee2568c23a054ae58124be0cebd983636120cd6c9f1645d4c508b66be90d0f1752eaae466f0c665f6dc9d587cef011d3b00e84b23156e615792082d159f8395cb340daba9001e4a14498184a8b118b0f2bdf93e20d829781f38ce49da40d718b4af19701370bba67db246b7bc195694dd560ff9ae16ec3f9d6d0aee44fa65f7b304c8a907f07c5409c4245d45ffd986ae56dc3503aecedf435754df3d80a3bc619e397323f0a2e92b04bcc0b74f18dc967279f86ac06c08a1adb0706a2ab31a4970cb4ff15079a51c907e577121cf17f929d30fa1eb4d895c6746f37bc53e0b7caf186a9800520bca6f4603c92a6f646866fdaf543739006b4f1f5ce7c257db6953582518306470feadab5b5292a06f0f823bfba4121c7e2cd3082a692dbcdf0e77ed4a6460982f423daf50c274bbbe595a02057e43fc5ed638b0f29472d979562e3bfe1c502ca98c0900a38844248a040a48645cf44e202f2966bb0fefc8affb77d50cbb50edd5a4d1acf2b9d504d5da15ba966c4c2b67045e7219d4b4e67f9a713c76c810e40a8e88366b0a463aef705eb6124654fe65f1ee6b5ea0ac6340db5d8e37510085f3541fda60b2c73c3436b9152cbbac81dbfb90c02cd47d37063f5ce6cdc770ea5d81cba0f4ac797d86ca3fc0ebaa3a454688db18dc6d23819575667e90662083227b3c53df70fda00e5b88d4cfb08cd879bfb76f4db558d4e7bfee2aa44220a2e0130acc71249a70daf79900cd33f9ff52ba3692c14b8bd7a1ee6d72111e00a2c4b49ff76c9b2ef088d9e416e7d682acd569e79fb0ec0957105db91df8abd09702bfaec260fddf8b3662335045b9ac7f513cb444e6ce625159a94ec3d311a0defbb1a6c1c4f3fec5982977bc2bfdcee402905e3769c235500c3847d33deb30695597fabc305659b296121b0c4fe0fc7f9e5049f7a49f68969f4810193bb0c09c9248f4a7b8c8cf0c8acaa3e44554f08f93fc03ba6baeb367b2330f24c0be70595fc05ecc815fcfbe1f685d388eebe63a9e7685bca40af83279a3c3903577a0c29aa727e3db532d72a6258e13e02a181d9da201ff3f02cb263d860f5e0d11e0a629632c021ed645f9b47c5b54866d72bc088254e46787a3b988d9da7f477b401ab63361bb116e4b23d5f4488b8c399cf7e67cb763ee02024c7043caa9ef152068b6768725d3a0bb3fe714a0302c7c36bbc8b477aa8f4b69c4afc42a68194cf0a14d000f089ffdee7190ad2a393a416deb4d3071b65f069e68c3bd4e8b27f1e018fca32ba0243d55422d919991a62dd67612830b9944febb321eb9fdc6125fd0f0cbb56cc16df91fe4037cdddd68f407de9e2e310d688ee68cf91661110c9830c656d148c1de689b6469f868859963755981c4bd094d172cde73bf0385400d104f639af67a2fcfe305913ced2bf29850e7038f1e8c91a5bdf1449860ca20e4e085d1c3b172d9636fa32604d24fcef6c8c0b428e40a0cb61bd80bc464e2f2a40011eecf85bec730cc4ef63d00e2736e35429bea7cca992fcb4f3a9625a7534c306bb57f476cbabd9067e2234f4c70cca299a8922293e18fdab9bae0c89b5f8490b6310df1541dcc6791ba4b260678ef6fb663ff9499c98830d8cadaaf7bbb63904080c8d58f0a50a3cd0287969d4ed63b0db0a7011d5ccd328e9ee7322b81b6500d1e32a723d234d85f9027d7f94bb9b4ede9c11693e8c19a2da3b21aa0a84f20597abe6e09d58d90d8501ac4a5127b333086ef4775338dddfe8bd4a569fa84c0343450af773cd2bd52f1592155925e821183d7b905bd7a90c5f668ee990f25f0c994dcba60a5fadfe9fe906f891c99baf5c5c541149b8240925b28925a623010263c64916024e4de682d7857be331fd1cde48381158f36bddb1a7a6c195638b0d34a01f5c4c23da5b0437e344aa9f950594cebd7b625877ae4977eb369d0ca202abfd5153b41582cc206f0f8fd388e428949305c6eefdfe56438a92934fcf160170a1296c806167b3bacc435110f499d78303d0e03554a35c7ce4f4fbccdddd0d18925609124f5abe9ab7449bd9035118c007c63ed6dc862205d0895382c6ed054f94cd95159401201be9fea476aa158c67b8f6082a2dd67f72b1b3c7dc2b4c070739d4e34574c51b1b49aabf3c9e26a981d964b8199fb73564ad979305133204d8ea15045b9cb93b4a1e070deb33c9aed05455ae8784650f958cae8429f7de0de783249e80b2ede4fe1092b1af5615407acdaac1178d992e3c530b10b5e6890d464021d4ce20f864e48e19d308dea335530b18eb45e9abd4102e5edd2777c40f7c942b3cb669b004dd93f2b8e98c8ff99637aa1b451a9ac6d17f037532790d0ad70d6d76a016de419111a0a2ed7173a35c68b48b8ea42aa8f9d8102cce72f904bd74ea586dffdc03fa4017c31671084cacbe41ea891a374b12606d147162ed04d206c53b2c9d9ced728d8163420a4b665c81fe0932467b607cb0e085b57e9703b5d97b03b2cd1110fe96f825df4e473aa1090b7f8e14347a1f1262f4553d2702157dc0e75c898aec8189f2b455d913892de4aa4c9e8b19bccef1b3d2552ada01aa88a7cd476b6ddd99c9fa4caea7b56e71e41d17372906fa63c69d666cb95406be93cff7a21a293a13c66f60ae4a854e4f8ef1ce7e8c817a9ffc2077d8c9b4027a74b2c58f3c22da3dc800fe6e7e35e4ed21af04069da1fe1415c8fd482d1c0c4dd858872e225bfcaa30539dd149c018be38258e6346d4f11857788558133d089b29f6c38a529e02ab51ceb8ff369a4d7f725ef0786633affccb8c07d12bdd057abb0891e3d2398b2bf2e838ef39815580220660226cb823934a740738d4510c46cb6924e5a5c75d54d231a7b986262fd781efdfa8f1eb55cc66370f88a9970887e129cd99e84302b8ced345cb002ba2cfc89e33274a040672e3a721bdcd3c0aa297961cd07b2238dbf17e5af3d240b61bbd201385ff075b46d778434a01fc0eb387e7201437bcbd287707d39016b1fc7d447de3b12fe42c04ad6e9f98f39c0a0b294a425bfc55111a882dbcf970f2ceb396c32dba5886bebf59661cbe61a509e7b36b542115a1e9d102aee1d40f4f5a97131f95fa97549c38835aaef722bf03e71682ad0d6c63f7811d2df2054275a84b94d4d197fa7a8a0b203f5d61409f085c6c8e17b91eaa1bb0c93fdeec598268a8f329e478f56c14900be1f00de52b0788a4d9bf860cd0235665b1a2ca7228e4ac8ebc2c7d13719b1fe98aa1b2e44003f3f9cbc11613e3a2bcc51180b555e216272fd95fe983d7418824e2daa6f80704472615a17fb2dc4858ede7a4ded43bdb764fca40166e8339557f955e5683c50cf603fdfb1d7cc0b867c25863c027ed21d82ff602d9b917427dca262caf01870dfdd10be6e5f16bdcd64c5cada70b6a3dfff07ea758adc155df021fd495549806afa693f4958b8bf6725f2842a3f2443b61147d4a119adf102e21476670c3bc0ed91d9f0271e517ad550479ac306bcb77ae0aad75a6c1479011a0e4ff75824e07c2e8820679c1800ac87b8495864611b683195158a84050e5b7b2d6384ea44d020c914e1b2a5a3c436971643c8458d79bd2cb032edb3553e66df97a1081ae300a5d06f0c4ae907b4301a06afd82880a597339de59e50ddce1bd7e941c4fa5ae0e682a2ba649b64f8f3a4d05b34301f700b9190f1889fe8ac79b80d7b969ad610e1443bde4d7ce1c485aabe7140e7b727a145d602cdd8c8c1a3d57c48e1fedb100b35e635db2ba1094b6aa0ea972184818d694d0adefac6cb48df647c1ae75d40927b5a6be4748057bc0485b118a987d949da96be8319b739a66a7084720dfea0ba395f7222c3a5d04844ad5c835d9d6db2c2b5a5979c73b71a122202dc395b20cd987ac8444c1754cab7ac3c275e1ea5a43d2e7a8bd85098ab46f13c51c314e0ae77bdc318ae8715d78a7459d8554169e9fa9d3eb4688f7a46f953289b4e452005b8840990a8c74a7845d3af6d387f94b6e17151201c0bce48d6614ad8fa57c06d9b73674d8db0d1c9cc17e9c2aabf67cdfd20f06255f435a64c4d717d5323c0466b3296bd2f98bb9afd5b0948523044f4a0248169b78241a995b4361ee907f0d2f616d8ca09931c4baa9fed0432c9af0069a668f7c6add004f3e48d11975fa0785dfff89fe425f06ab2b5f9651dfc77aefe8e3f530142ce41287b49729f21009e249af43902bad2e2e7d3990bc90e2968d1542a91662b51419e92e8a39d5e303e16958d2c99c7ada49eae38cc70b08dfc0f3ff47ec5c27bffc95dd06f85dbf088e295c569c509fa69b6fca1c9ff347e1ea1834728b4fb69c40306f85bc44ff0124a9ef0f8a54247ccd72b90a1bc2b3aec8a514ea7e7dc4c3484e05084731a308b411580739292e23b2b9ff3ce97f809fbeabb744eff97c4c195626d37445ed0be44b15912e200eec0d9615af5b261c3a5ff0f0bf8fef954e5a049c77055a0b0178657982c11ca687f7b473e5c28b504e8ea26435951a4190444d7c7f2ca1200cbb69b7a6b7351f8605378cbe44f401c453d0458057758fbfd62044a809e3840dbb23f981de0d7501048315790f071ff5cd5560d4ba0780f09584db138a223401a448b8578ab50dbc35706149b1e2c93b8361830a4cfaabadd1b4dc8302077503dec5bf6146786716794646f346e125b8e5fb3089d078c9543099a366a649f700c2b4c35f2ca6b5a151b57a9c859c8b22839fbed14a31867cf222bfcd4bd91a0c82f7c6de3f8cf5e79d61c5c2b29deb8835eeeecce612d2282ce50847c1455f03b41fd7a20dc8c8f1437b1710437747708e8aa2ebae66dbbc16cb35b266cd510d01de5ceec89264b956b537021a78c77ab202f0b06db648dd4d7aa33634ce14082cee0f4682c9f51f0d6f08692557d9f03e46ac2418d40845033901e6785b3a086f9f5932aa29558dfb1f9ed5a42540738a100fe01d234fb379f9151c1b8efa0864c6e9b1c8e9f9ca8609c548901c8c380c884c3ddb4f25a84095425356cda00c8352a787f38a0ef266e510005dc8b1a395cfa4be432c65a95f20ae218dea5b04121f87dbd2cf96a5d1aa2aa6bc8197d63c44e3b115a758e3360d13e8a371c700891dd05a900a91b69ea31668549c7738418a4c1b02759518672d5e69b521060d8f5b9beb9f67996ff6567540a75b60bd64e06cc7a1e0c61947c84fc644224b02dd76e362980bb920441384dc0968eee7f55f14c991d381721fa4307344c67a027fc5c91465103e37f5cfb4cc643f4504b4617cf3401ec1f492f43ac87abb5c042a7722c09898819e845252e63bdcc1e2a9413d9e333f5bc50c7f6d6ca46b480f94e1d8692e23288bd2726a2fdaf957601fb99b715c4ab87961b199ba548f5c0bb337f51bff407eac927a25fe01f11509e36838f03ee3a107da4d71743c86a80d9dedaa518fcc282a3cb31e3c2efbe5e45cd9c1874cbe247a21a0c98fb425e80a3a92918b1e98f53b388ea3712fecc3ced349387acae16b8d9387617e6baa900986de13dc4729c0fb93fcc13d0f4a5278d0a68ea95d50a4197a91a0739e03bf0d63e56264b824839127c191fcffc78da5251f23769363117c396697992c6c1a04d7450af4bbbcd4ab35d73625a299c5a3d1c311fb5674c48be6c0337413176007b05766e8500f3e6c33f17c004028a0c8e8b3adbd96cea5e9d72b76ab6c53dc0ed75add12d2d6142bbf892d9a56515f62091dbbefaf7e40e21fc240cd92009d090c6401b83d2b43ccc48fc00ea8611e949449d2e9a5a664bd9c2d3292fd55430baf3cd9df463f57743f7a86d3061e6b7a1e0ca5d00bd17324b4d7ee66b0238801dce7882beaed36132096d30134a6fe5fe3666d27366ce5b6d87db82a7aa6b5038e44a4b1797f4c326658fbde49b147a0e3c63099e8559c7a3d35e8d579ed5e013a030b952a54d36a3689241731897d19552283302328923897052badc6d82706871bb3e4bec52da92be13f54ac49b8094ebc83bf694be760071fe2e5b926e30a9080f6c0dc55d6f3bdd4f657fbf89c2c3d05f3fd9d9adf51fe79ffb3ddcd1505cb2215bc57c468254dd188cc52e5961775b461cc2c6d86343c99d53662ea1405a169e17ff1550922130c4aec5d19ae94bf669e1c9cc33d201efe51a789fb130d64d3a299cc939127825acb1184b1a99128fb6efb176705070d1a08256dc4b9054902ad8935a7e1c9e19d2b088ecef82ef3cdbdbbad7c67f5766df21d35edf20e32356cc6f884b3b8d22236496b66893e51730bd34363aea33dc0dda59e496303b9c0b19a702885114c1cf2955a382ded32ced3d1346e16d10622157e68ebaa0cc83a0ef4587c0f2adcb5bd5b88152a68c4722635e5594c06bf119cf4b93cb60b48e48b04ecd9b0c89d7d1bb6c41254b91d1c729a00236825f9a570eec223ba084b681292322f142b11c98dbf3d8da1c26604325a3850198141ba6f5278af7100817582f04d051e9a92a53ef1efdac3dbd50ff308b82bfaa6be78f294cc54aa03c65bdf4239ce0862267fddb475b43ff48f642a39ce11be9620a8d209f5aa58012a1ffb37453ddd6b5bb00cdc345394b4c3667af7469327de6c1ce61557e6ba0190a1f091b9b397db7a7d7105d216370a5610adcddbc1cc0a91789b870c128709b29e3a6748f47a9c882f97ddea6cb813af123de455fff938818c9b0fc5dd330c59439bc7cf22ecb749a2aa5438d1218b4e5515f153eb1d38c4a7e6ec3b3e4204fcb7775d982cbc8e6b0fda1688e0a20274ca4328c840b64ec7eea4e427f19b0db2bbe9e3484680bb0914a6832adffd3959b8f9b77b053d7464ce8d6e43570c096eab9fe7c1c656ca09d1288c0ba161cae9af1971a54df02b8f8c5568af49bb050ab80a6a52c314209bc3628b9348e3d5fe8670c9fd8b939ef37028ff6218e90db95f3293ad336743f08bb1aa5e2dcf681957b589c89d7af7a288dfe18a9e0509ccdf7bd02f9d94ebcc47ae9a561a089b4f534e679ab9c027e99010afbe04a705296d128708c58751068148c223666e38eb85579290ba4fef4fc3913304d48e0b5443444074d982bedb0f58fb45b1056681b9ddaeda90e1073ac5ae5b26e42c085a736e51cd680534add5e93da352e9409fc71bc1285632958137469aa9c66f053189b423273bd824a990fb916818148ccf86b987f3469daa43440cd298d6a101de4c05693a3624039baa14b22c0448073e0c8c4c8f185c04e7ad76d843dafc0eedb95e3bbd2adeb99d950c6025a83eafc824c2bd2bc71b0dd932d86cfba924025ef540a6cd2a2c12afbdece565c5c9d21bf2a0dfd165609b7835105c70109e04deb42420a04cecb42727067c79901e6e15c24fd993bebe9616b24d92a2b2dd01e3c06c9e80b9b7ea682861dc16691cbcfdb9c6c5baf527dabae92edb41dada06ddb0424fe8bf97210f4c89940dc9d44af284282680d9c87051ce2111ab60cc88357ce467222cd0c6ba406337ef207b92c7f50800a2f9d55de6699c068f37980e9242ce405ad1fc3ad2802ae868480c16f01d3d307bc77b96fc36af5461a7ca060853c4427b7be810987d6d725e26e8d5656f7bd9afc55a99fa25f06f4111a0006cf741911d540ec512fc0e74dca6a21aadb598618ee2e50894d91e6ea2002502843fbbbd599ae1ccf2c662da99965db394a566a02f12d2e63b65152351a34f0c99d1a4b4f821a48c25fa110da61c33fc6d9d5d9b7c118ef2c8e8cd2b7e70ab0eafdd4810cf2d47b8a65dc4f54fc7be6f02fc5763fd3c42e67088f92307bade0262ffcae8289e3e082642ca06ef8a53defd8d3cd458bdf03e6cd53a37c23b02024780a7fbaa7c4c6d094b34e14c770f2df5964994541f8dbc3d8db0b481f0910284c6c4bbaf3f7d4379e26a13de261aeb79477300ba654b4f1ea29621bb8134021d69ded26ae49b26f215b105cfc14a436fe9f7132268b93689485f701f327700ce16eb74301d6561b7f3435281081e8ac99313d808b2de24c88ca269bac0830a58b976a0a29e6743623683b08a9f45ec62dd758b78b3cdf58a66e5744b486e029abb2462b85e8ebd073336e4ff903b9ccf9e970b92467a4b7856cc79d0005e0dc21d1841c8f7f231e3743da585a7f1c58e4246b9cbf1285f258f6951415cb80999253cfef79aaa494475f6f6bc9a65cd0627cc8dee6de60f2aa02dedd43dfd03d630677802f2292220210258d6cd4e210c54452708574bee1be2d34f936b84042b967fe569cb7c3fed6f4cc9e9e595da0f46f49066aeae9911fdadca7f1e4c0e206b836b9926f597e073929e04804982a4105f5d27107c66ca4fae608f62840edf274431627d5b0df2f32b058db81650ab5c6416ff3a49141490f3c150fee009 false +check_ring_signature 17c68ba6aa249676cf9e76f8ece67979b320f14c7dbaf9376307325b384c1575 7415ef309b79213b70f42587ced5aa043d61c66bf645ea6b8b40fbf62c483d74 1 b84d8aee1376477274fcdd390980ea02f20af6282fc0ad8757494464683aac3f d8edf49611cbb95a7d65554686342d2a0b5beadefb2253d96ee306a2ecd952d71a8e10f426c75b9ec3ea4695a347e4094f1869e55c61fafdaab10ea4b9706b0a false +check_ring_signature 300edde02a9ba9c84a346dc62a724e606e581494aaf6f97bae4ffb2711bf856a 0f39f33d3d9942b98cf197d2e08e747d1c38b140db752372477b554df552405c 8 11d566706d080c943dbe57f9d782476353074942b20517884ba1696138560ec0 ef8ad937ca760db572b3bee104d13313301f39a8ba273afc58df3be533c03f8a 85497776a82735411def994f859882e79c43665742310fabac56983479c2fd66 f4828ab789f8be0c2f5c7b8fac296e3d3372cf4cde0db871e5b2ee9798dea1f0 6bf466b57c29082e8aef94c4968514f028d945af673117eec54c526e82c42ad7 e2dc9d86ae7c8ad973d93eafc84fe1e31a4d8e30ece16465f042ff57d6b25e5d 787db0b38accc94fcddf3629de254d9b41e85280acc865ab6074e4bdc8c9cd6b 7a5941a74ec7a9fe23567698a28eba6b32d87364c23b9e2925512527e7bf90f8 9c9e2f2e1692e5dfe140d387665a5ea94d0b9e9ce2a696f6ea813224afaf4c0d30df2bf8befefff969109720cc20a0aa0bedfdfe010ea93e6e56b6d5a0fd1a0e7cd604cf68de5fb18a5479c0df091de719a616b26a2f4fa5ed49cc185a56090cee0f5b00b8e0e87ba6c7611b795d302212c61deedc0a57ad216924fbd4676a0f6af94ef154520f2adc8c97095539d3df421904c22638ef8de36c1486a3b6ab0e0d43332a3012ce7038d6600d3c881a0facfd60bf90683e4f4d74b37fc2115c00abae6d12eac776a474a0937eb4c2aea0e2b9d97ffb437a2c1ca419c6cdafe101e11b75f807d7414aab8964f000d72d6ff9ff70802e44a0148326a875a2631c047c922b9a0aa1ca53e35a0a6713009fdda6c95844c0842453438a2a053f20f705a03f8f5a59a65f8c2e33a6086774f1883cefa7ccd8e52b2e76ff75aea2d8490438ea9c8817467285d8c3ee2298ccce14fbf298c2fb8271da84cbf514375493098674ef794cb11391d8ec08f8c8857fd6588dad26954f0817394fd92c01bd830c80d4f396e07ea8ef39a8aa3ea28d56c5d7e2ffb79504ce43b81e27864fb5470d9636e84a5189c58ec7842aec1efcb6156154ba98794afe35ac46efb2c20be109ef37ffbc47b262470316a6add61e7b7eda13e59d818d3d7e9e1baefad424520cd4f4f2f9c8b473293e01ebaf0b868e310e7934c758901c666ef64e77afb7cd0b true +check_ring_signature 7ab92fe0b225997bf09d5972cd06bad195fd205a4ef31c778af77d4aa336804b 52eb5ccd295c4212c0742e418bf93cdd64d99340b1123699bf06613652fb7932 113 9e7817216f808962a0943f5381f2aecd45675c194f5bdff600a332f19f16fe91 7b762104c4a4093449e09f8a64cb3f84c25ebf3d8e72694a3e16f52c3d6b04bc 7b2f778df558bc5d5ec8e9348762de41abcac20e59d38c99063a7df48e6e7d80 2d562f7d44dcf062b05955b30ccd46fd710841f74715a4c2f3ceb3f328680099 b033dd75a4a2edf6c42f3f1b8acfa5ddd59bff16e2a5a01b680056532cc4a564 317b89a82c86c04550935dad108e482a235806ddace50f390c34333e7da179a6 996e2b97ab096f014db6f6c4706c56de2a2c0cf5b6e8260a65dfe5c359ad5fb4 9d6aedc5f3e3236721898edc11e39254bfb055722c7692bd9f55363084a716c6 695e37532fb871ca342553fc4f0d26c461e955c746a8afe8ff2cc8233840008e 17f643d914e0f552e91af021b98ed300829377bcbcf574f83e5f7ca7f0fb148a 5fb22476dc2da660ab5614ad066e0733cf05eb6b9b53bcb9bb2e7dd64564f049 00775bbc3f92661977ae30bbff1bc3f5082d29c3fff7f4db4a86ddd357695933 3e993034f71afe7b2b013b43dd814e377290f8f5064363c3dbe35230981da185 bf7be7340d544251a125f305f523593b044ecc1f0ee9754303b8f28208168d42 b47f30d0d6255d7b7f685347ee672e6759af3341dfb68200646504fb32c5b245 0f080bceb1d87537c766f177d516634266bdb2cc86824a60c6259b2c8fdcf63a f767dea0b187b55edaee33fc5d8bb1fc6a718e5075cb871a42f7c2425e519005 0f0dc6d0fd37cec3170ad972a908dd766a66147313a7282dfec74d9a7ddfdb6d d0adc3db6c62a3fcd72d3dc20a086579192333734fd380568cadc82cb4d18d5f 2f1e20327010bbaee0721c1e00821ffe6b98e387a93ced09fdf62a51134312bc 8d4933c1fb844d459ffb74fa51ea3e02a0c12f7a0b88a2036fde470739581fa7 77351130104674f85b9059e4b49c4a230e119068c33729f2c68e76c91a34b387 fb5aea98f9eb9c93d368cebf5b49b6a625ed7c27dd6aa9815e4c99ed8eff5484 c9b7fea805bf0ac8212fd6a3fbcd787d582cbdd6c6994ad34e9c8a783c201f64 8416e853e5f522784220bc8d31abb4320886c543d67862ac705b5b444c832de7 24cfe24446305b6a727356f1f31186dd7299d63f87e69f0ffee58f9ef6ab51f6 317af7f6bceea19f55fe6df5b936b79403c5d7247445f1f0fafe26c88b6445fb e127692b4caec408c62003ccf63430de256d222791d68722918ead368e146982 7384803e0d0d2dd58edd1e3388cfcf91a4c6bfcb819f43b7a25925e6e2479932 b655f4f8241c8b89e43b2e46e745260ce0baf7bd3ee4b9fbaa5520101af2b4cd 26165cd2024938267ebd62fc474200c078a85c9249bb537f01267dbe7e70c8c5 a571a0b9977e6645ca58cc9dd8ce1ff3d0a9154d706c5e5befe56c272aac688c 35f750340d840c2f0158db288fee4ab1ad264489c8cca13057a730f12279675c 8247861858832fb1d4018232efacfcfa89bacb08a8bbf4230f4ecb83c4aabf31 7ef05b1b92f4ca1572a4bcf8d1d458871a7eb56044fdcf49a1fe5e92c36b639e 73e4199d595f6335d257a943c34b802adb454308417ee9d4a5be4981d525df25 76a6f5bbedcd776ce6208107170eadee4b15b0385683a42b2d00bb13d803be0b f8bb5db4bc983e388c8b78fde060d20b728d3d53b9a6b273afd90c33830510c5 f8be7b25407fa67efbff993b5b0217561c0c6e4efcdcf78ab27fefd1b15b071a 53fc833c8deb54402669b69f279868d0dc94565891c40a6200c7b2f91b93fdca 4a8755d6c418d18d533e4d8875acfe719915068d568be80280023bb9a5cfe90f be53a39404e5d91ce459ba278d247af1372fa272d5df674a0899f4bc28f85c52 c871f79591df85d477766f820ccade89b76e7bfae471e8911c192c60d6e71815 9a68a185292d685602c67e381084f16b857947b74d8f617adfd73013cf902422 e6878996a5e6418bc2339d848efe458c6dae70a5e6f6d41fd0b32e8ea8b894fd ae0a8a3a22a6596b3f8f40026353c79ee2dc92a4427fbc983e776780289b381f 19af7b1be01cfbca00c8b1bce167b76a6956f712fdbbdd7f21c4e530177b806e 8e7e894b436ca0b1750e023e5bf52c927de5d21c63b6205bdc84f811a8b122c4 34bd79d29d6f185301e3257c645835ccac80ea7bd715231263fa00fd6820f223 12125ac069e2595272d29a7770e7e5e6d4ddf65ed5ea23295e7bd9c011227e74 6e053c3afe8307dd6ba81357dedb17b2f95353bd3e606fab8c9f658974f92700 9f6b71456ce91bd0290a99c8dfb557cbc5a2420f6fdd4be0a9fba33990f1c4a5 6e53e8fd978d1bcb18e8c45c9cf2b6bdde84c252abde637ed2b658f355208521 e8951a995146e56e21f1b1bd377ea8dc4fc698c8b9c6b9b450127c65467ea529 fb189589da7886751a2622c2f3432a77ffc7d082217b605b09aec02c358bc73f d0d639b9153e6eceffe2ab64ac1c332adaacb2bb0048b48b4cd6d91090abedd0 eb76fe294901a2bbbb213d0d28d5d3a0cdb8ad27f483365290ee25726e4fd3eb 52d648580d80d2ac6772926c58c42d15a1ecf5274ce795460059acd45ca22342 7b2b5b7e3033820389a28d58ac3e34f243eda6054233ad8aaed4e9c08bcdf1c1 9cb1f61cd25e2a9c85efc6208374aadba01ac3a8640ef6dacc795458fb1d00bd 0540da50d691f07319bec4065ca29960bd68695fd53d5856d38a974f06ae4ae6 7966fcfd8e5bf8c5c682897ac78ad8f992bc0d0a8498df1c292980e76e67935b 134143e05c8517111eb5d330c03321910764fce7696fce68e9374c3cde2f3a5a 25a540ec8e842c0ced540e4d2ce8ff1c912917f56c74a7e4cb50fd6bba82f476 ccb869e622654b3dc3baf02444b72a12460a982e2ce971e1304aad2b52994159 f13ac1dd918a4038f3c67b390433569f58c4f99678b54032ebe5f2568c8b4420 3b60c6ca280c2cc1d1c76c30d587fa1b325676ae56abe50a8395ff497653254a 50a39e07533ba0c8670c1316f74c4d1c56bc45b671999117b99b06f2c1455cc0 0b71c50e158b3dea08986c0bf0a36a89d1cc1e39ea44f247c54084ffb15d057a 97d5a17c3b5a451bc262f71e2878d9c7c29cb432e98f835f79ca8ef73a333e95 4e624703bfac1cdd0610f0612084638910383f2a2cfdbd92a664275833d138d7 0d69addda7ae13fe735e374f046ee30b0cce614196a306867e35ecb299a2f8c6 ef907a6d5fc1c5b8c213f359d3e0be42f02c695c28a1dd1edab9919f4c0f4bdf 8057292976ff046c34b464b8177cdedbba58d451a45f39ad8ebdf53d19252417 bbfab4062ab498ed073800a928e18ce646289b92496aa4b83ebcdb31fee8f0c3 8f02af549cc39ef7c9a45ae45e3381c5ebc593624cb3896afe57d1f081befe3f be6c23bc4b9f969f2cb35294993ba98eee9d35bb7c9972aa15e3ffc5e3740011 c36016799ad8eeb959eed948625ee1bec8036ecf1b3a6136390dcf3dedd99be6 37cb6810cfd15971ee393c7a7f3668cde2605f0cc0b4d5ed33b4bba691ef2b8c ffaf9e6a0479fda68331898a498b8c526ec1167821b62e58e9820b8fa48602fd b50e4040216f4e57cbb06141c29f82ae0cf278c067a2c18f984a157cfd32a369 0574a241021418cb6db3d78108d6668162d629dd864543886b3ab671763d1003 a07388b2b4aa377c1fe96311e4abcc1b9fa581cb67e21e21d496f36c543cd257 94c4a4bb08d0d6bd6ff9b2c89fd34462289d31706e700da8ea20f7a27dbe4068 8ce72c54ebfdbccc3c1484f86dd305e13dc6bfedca62b6bf976230801ae7e05f 7bb6e55dc5c26a42cf787dc66e615e84cc38651b8fb02c2255bc71c65f76d861 d3233c6617421b998c7ef7c3df26354aa03775dc223515d10116f58e1fc2f8bf 051e685bb40c1de77f820b2aa4010afbb0afe51e23d3606feea1861df9db4714 0a0ff2c9ffa4418cbd190990141568cfb7d858aa0b0ba440a49ea519dd2439fe f0b082a58c63bb1454874fd572e3f8cd3e3a760d155ae95027a3cff10f56ba3d 7a2f7f7639438756a0385b5a17a8b373682c978c241853f94fbdc81e83bd48ec 391b69ecf7359c41540ff4781d78b95fd335748aea3d645728fbb8bdec4ef3a5 07648251e355154c4ff144af4a86c8b8cae766c3af316e3716b3ddee01c103cd 13741b7dd21f487bfe585f12b0ee94a88e02204762a08323bd9a3d7a5103cf66 4c60f310b572dbfb6c27310337065e8533f17af2ad279a87876039c8a84ce134 cfffe192d97ca87f002a795b66d153ae58474c37542ae0b0a93448a2c8972638 f095a9c294275fbb6b3aa4435ade44aa7cb27770911532f4bd072c063a76a1eb 4e6a9068eab8d58a04dee0afddfc38635dca61687687045754e6be4d49cdd013 e52f02433ebd86dc7a9ec143405b5b0b90220187c788c35ae85a8a01c0cbf60f 4ed152299ba9886fc67c0d35d2b964171bdc8f33af427f89ccbd8b5e615d9556 d30c108158ab91a94e50a7a2da51478f6492643939e8fca173773584bfe9b4cd a98e7834061650f89dc231964e4d659e9b5e65c884e9345b3d4b622dcf01cc83 2eb858c3007d43b3514aa10cb58ad96f0e4efc575a013626f453307461c80a7c 0239ebcddc47252f900556e2feb9c78bfa223a56180ebdd94b6dcbc521c3d2b0 b8494bf130fe85dd782d65b7157cd814cb35ef97d2cdf456580ff88108456bfa 68b33589a6816ceab297fc4b436386748364a685b25d86327b206d29cbf1ac4a 92f978c24a7fd0823030959ad757875f7d9c75fc692f3764aefa84ef2db9381f f41908cdba3af1ef0558955bff257d7b434a25128671331ea16092270037f290 5593fd38ab4843d8da2016d2eb48a9c2d5a279e30897008fe60688354b324557 ef3569a07d5d80db0586ebec33a328bb49298c279c3d394f5c01a1612abbd463 b5c744d728a5992cc4fddfe3ebf66812a461b8012e86c4ebf53265e9e38f5338 f9a29b3921a3ab31fe25668b8db82a4350bc7f5b4a9700e39e1af43a11eff1a6 0d01fed2d84a3949e5c59637341fd8de99cb9f24f6c348599faf4c38c098046e eb63ca2cbd15e91d0032d60952793d6a18b49613fef5bb917d7e285dff9f4a0fbaa93b168e8ec323613adf24fde51b1f4736164bd51fd5024c59e961d371fe0c13629a84a19f79f1afce2af94f0a19e1a97dcc2b0bbadb20da7ec2824eb8540e7710060da2c9f19d40bb06fb45f2dd2bba526ca857dba093e66959867bf0860a620975009cc3467ac230059833323442840c6fad8524cac61035581b27a5440c3933e3caa3a5b8ff1b96ec8f63677c235a646f036c0462f910405e16ab244a00c2eb769f6573134e2daea490ae370d4ea49148abd88c174245a1e82fca2a880c1bf576c387a9d6f0abc5dc6747e0fea33bfcca722a744016684ecc821ef7370f951d48516f399ebca2ff6fc28794784051733dc8d2e82c14e43743ff0f4a5d0e19cbe4af9e886827d239f9b0a79503904703918492c7e7786b49e30ad29d430270efabf0ecd21eae41e9f185c6fcfe9e5b805168cc0f53c3a8bd6adc42895908dcac7305d56c0afed42ec49aec70526c69f6a65af9a6a56003979f6c717ec1090b997b4123f86057a302b1d6f3ff84473523e80aed6f88a7f74eab6876232008bc223be1f51290a9410c13e89b4485401bfafbb81a1619dc7cb6e73c3e28280d3f62f75a03aaf4a46022750bda2413b2fcd960cb31d0cef608a155a871de840b9e08f0c39f83b64096438c8b9e35dcf21a7def1e155f7c0fd4278f2cf41cb00b6754655e7312e5ed1fe44576b0886a343eee32d3bd0b9d52a103942481ddb505326d50aa6a0df50e44609c70122622282d1265b783706ef3764a850c0e7cbb04f8acc5da07e147c9eb9f6b3f0f105ab99eb5f7a530d60226e5751e25bb9c9b03440fedf7c1efeeaa47782e131b77e6dfe4a2fdfb4cfb1a9a43f3be891c1173070e1ec33265797c4be6af21fb866cfa84af83a6ce186aeced7b41bd5c4d0df60f81aa7e7172257e36fe21b983b19b4ce885c48a988bacaccc3035959853dce90c5e0412c2db11a8da1e0514d2d6c0ea103e015fa5ca8c759b457d17869c20bd03c70a7d6e6601f9aa537913c927f6f31c79a3c8970dc4f9fb73584a4b5c6f0905c1776e99340d100fa39acd1b87c4bcad474dd6d823ceec31059c23c2fdb3ce0b0bf1df8ec9f951265b2865cbfbe112aaec6f1f72230af142b2ca095f238b6b0ae5c8cad5320f4f5e87f8a00ead25bbe12b8450a6b47352ba6f6742be51c69d06e02a1814dcc7312af97f560d91b396effe35ee33db0b02dab4e37f347db9b30112a6ed1a7689550a9575c415f279524da8f602d9820e1a33ee71a4aaad02ae043c1abefc1a16f00857a7302e9a84c363fee11ae72067af3c53a3189dabbfe809932ad2eb7affcb3d2853df5616976dc71e6cbca3dab1618d14458e06cf45fb0a38561e24f497787e42ce31d22402a09a77fb63c4e6b2f787925c35738e3c5905f16ab37be52242b5525004fbf8b344dce30fc6ade5a54ffa17bbd71c9eb0850029d5d73970c0e1b5e4efb66fa8a64bc5a566b718c7a28c17fe0c1527deb83f023d3f176fdcbb891ba1094a49e52f50399d887fd98f19d0ed5244640f34801d0d60e3cdc1f0576149bb399ccc1a9da2fec43ef5984bea158a52b90691c0f3d903d0514dd7cbf92a4ace00ae1ed463e0f758080b1cbe790854094d2905342bdf9c447040120020451b594469667fe7c716753727e85d36364c4c82dacbb8784f00ce5cd46d9923bb81588b5887a36b7875bfbe02270f7843081b3260310af4bc0d45b94f66a869e61699c56954acef2f4934a335d1e44b545e5f6af3f8edb22c044f62a85fef696b46a13e8e9bfc49f9189b2d3544357ad468b1e35d7f607ae00f64f662da6ebf81c3551fd8d152da4c3b2334908ee6696094947de59ea4746401f3571677c8fd51f71af95e102ead37b8e2a9b065031f843fa89a45cbe1265f05263466bf56348d4b4545ccbb09e8da171d7211fc16ecf460ae83a97d2e51c202f3ec56a93d29922192a4bb843b9b4ae7c7d50ee2e73ed613ee3471eb072efa0c10f0a073d43d06adee1ebbfac80d42dc50115e31e56548fb8692d6ab2bf1c00cc0c917d4bc1d32397ac7c0db81927398db7b4837c69b8cdfbd4f3b9e6fbe1204aba4288c426963386396a762ff4b44ce58b7cadfdbde2306bd77a9dc37e4ba07a8b299fc2001c30578c693f377e6d9489a9570dfb55c618af18e2d8c8059a10c4f1960c42ef45861cd8611ac67c2f984113d79ab04cfd3dad95d46a0874cac0fb22bb2f8478c3b1401bc98436aab03a9c45e4c56c53157f0e6571456ffd29c07fc4e5dd1c66299ba698fa35e07eef95a1754f917f6bade320821ff7a71a49c09efaef9ffb94215a88bedf81eab177e56bc1274801a05530a9da0142613fa0e0a33af6c93ead23136eddb2717bce3f162cae94f146c34b19ab4acc72d02a3bb0b0b998d3b67e3709c27f362d4de48137e0adb5e39e1d9f62e97d0432bfdabd77a43b4930df8b475947ceb7db5ec3c7108776d328d1bb1ecea96c9a069030f21058ac072c7556b3592d498466ddc05dad4c72eb9356a272339332bf7560f2e8708f8ddfb2e30c71969fc7c2f68b7ce675f3a551d5c78b01326e7aeb4edbff3a30c9a966bc5f864f0f977d56788aba1b0e2e120f3987e6ff31ec530e98ffcc51703ae54150d4d39d6f7f79ac357d90493a1c18a00b707d592d1c5b63637aa796301ad4fd5ca04335e4c2f3e2dc8db50ac33fc33b31c290fd74a72ef12590226070b9c59d18b8d03da97ae7397474011d1174ef4746f8a2e9ac9132ec8e258614f05d6463093b57f4405a72e0b514e3c3e6a9f224f0321ec64ed394af8fbddf8b901c845b907b9312f160c053e1ae604ceef513ad057aa8636b8f9351649ccef420e2c9606feac216f787788b2b91dec8b05bdfc82626ba967f49186e0dbac21ac0a32c50a020169cd498972d3af7b51985737c62f20654238628f790a6a433aec0c9002a867b21ebf4de4d47ae017c2cd8e29ea532d5f95bc23ad19da788714e00c733cf41fdd08e4e43187bfadd8a180f8fff346b720e6122c321efbc6dd38ce07ad5de47f7a0dda327f4a6ff6d821d47ff388a57b16203d78c889a515c510f20314a5cde2a881f1f4ca26757321a4f1f9f9aa1678c666eb0b4c065399852f13038215b8f65587e0f424bd142c246790dc6bb81765b461896d28d57a2b3204a20ac8db7fcfae974d20541715c9b2d887f2ad5e0bb6a45d290f6c6f76d0013458021899f323c3a6bf1f2e7f41ee7500059ad59db3e3556ac6b085d02cfadc5926050c27bc2fb729e94a28109560e6b38e48ebaaed61c057ca91fcb4849eeef298077eb618fb2c44d01b2d1f6fd5d1be84096a18c2339adaa12e4e1debb6fcf108031f8f11837ee5b31e1c2e19be738f4a590bb7dc848b3067260cc14f601f983508a29ac1122bc0173edb930a210f4ab8ee6ba4cc964c0a202cebcbcbf7b9622208a8f128ae2d0ef877958a945af4e7bf3eb3dfb116ee478446490b98e09ba7680432e7a172d6039b94ab6a23e40ef99d616d7b90c784535a0bf551cda88e8337016bea47e7eb9086771f76b144cbf344c3d80e6bdc70262299018d83672b730d06f64a6aaeef2af6b33e30abc4742c077c323d915a61a81bf1caa9ca5b17df05017862078a62974da45cbf405deb914f876e13935b607a09d40736b1dbc07404094c6b949eb2b9969e58c6e9e0b8d0c8067ef0193dbd98a8153afc465243582b0b38c2fa38a702e84271a6dcd71b0da47b5a21e89ef798757bcb6c82b53436a70ceacd679960362f4f72af39656e84de844118546a2b2a700f2961a2436453fc000a8c025141ed6b04f5fb5dda506ab521d11fa32962d47a939a3bb78636e24502be5d81e837ff6aadc31d0a210c427dd2c9a058ac55848d2625daee77c4bbeb0f67185039c63d13ed20b439d6dddf5d2489c87fc854e6b7aed04c2ca6cd9c5c08ceed71250e78c5414569f18a78685eb75f81eb24d770debcb9c36520060728056ec6d07ce79f9bf5a7c12541a101f83732ec18dd88b26352ea88b4f9562515083630b547e95b541253a44ecc78eb76ded8780f239e2c8534670b3486fefb200622d72f71ec71976bfb8c42d1d525fd2b7b6514ac1e0ce8b0ae4c87b48277e40b2e01c6a066123cfd96c490cbdcd98b7be03c84718b059b138111f37ad6fbd5021561f045bdc94d87bbdb14b6667d91da5fafe799339f177fa8e5a2683f6c7404f98009b1fb99fcbfdfa36fdf7f570e464c0310b9c19c66b017982978ec0925063f37287d9b78309753ed1835b6edcb1b78f3cd2dbc1652d11f399cd09648850018146502536917e1d3aa3b2b4359a86d311bb3e718312a10ce3dc3e694726807e79566c453adcc8e23ac10ce6f1531c0f6296f119465c061fd34f110207d020216d2d83ca83c53ab917d022c2532b5dc702580360d1e3363b84552dfc940bc0d758254d36898f8f804055b43012e33d8a9a42fe1d7f2429562cc37ad8a72000318a9361e2dad956b2db20be664b82d87c1de650deb68823fbb5bcf9e37fb3c0277f3fe0d6498a4ce33351fce7dd78b2c59211a5b34743d9e4c222f5d6470f400a08345cc0f563f487a1123429d805226376ec99d5e0fa3e7fe183a6c0386ad064b748b5b14a609a44fa206e46b39839e47c87000867d811ceb8fca42529456b5bbaed1b3fdae5d7185f3e3719dc145f943db8491fb55ee4839cafad4682bb406f6f6b47bbd0e8e5e41756ffbac880018a7c9e70fe331eb98308c1c236e7e40005286c157759dc484edc9314af0ce3a4e68581654fed5ad39e1520f88063867068373f3f74a531aa01e15307a0eaf43e399760d506e79ed3d46f6e1a0661efd030a1ef3caf62269b1ae6fba865e91cc1caa3a81613b3c83997409ddcbe0125f0581c9d5b1becfefa69de2c31f1f2a7d12217036791dc507d0430b169897bea108007906cb95f9de3c2ff2a85afa044656ffc19b7dfbf29839703d7b5edd60b9061fe61a6ab9e55f9cfe438abd0fac1b96893e5dd61e09082d5fff35f74a7997aa2e90c77e0191fbe4f7f12ec71eadcc84014e4904f4cdbc77e4d94abf55caf602a3265869ad84ff835fc86fbdd4a6f1be87dd1a9fd1d17e3ceab66d8526293102c46d11f14608ecf32a47760d63d235ee324b6648388b342a32ed4249f444b607e2057edad29d25680bc722bb710686cd53d30ed6caa685586f7cb97c8bd8af0c90c90dd442b7840e1be4f26e784b85316d0f767ca41a76932c09c755ded589070b13cc8c1a9187b5b28f136c0bbded12f88694987c18a53f6b382fcc5913e90b28fbb9b9894ce11cfc4d583b6970266932b439868420c160585fd3defd793803d0bd2667c60d51813afce1906f2c8b3742cfba7ce5f93a0735456e589ee36c0e76f31700a27885b6f50e0cd1bcf1fe99dac75a02ac83211747e23943b3f119075ac78965b38a063b7d928b7c229452ff52325213a74506406acbf12ac0273a06b45a36f94e35883d3bff3a9132353c16d0af1d64e454d60cfec743546648ca083eb3b2ddae71f4ca171220fc6e809c470c498c4e84504f7d7f4c71f12e5581002b85278e5d973a31e1ed31e960b617af71c76d44a864f8276eee6e509ab0f80c2c0f0d87e3d48bc699626531aa1fa2a42d5735d3e5c7dedc65bfa225894e62051befaa5d14b4b098bd0b129bbe5fffcb2e76dfd1a9b3714dc28cebd90fb3f902574c4b34a2fccfcc2306304f77d7eb8b26d2e4a90ca67273cf8f2e268bb1910cd3d9ba82acd6a0bf8537e197bd31e6f829a1e03dd39c2ab7304fef8ec8aed10c1dbe3fc96e39791a34013204452d827460b8bc792d2984af9221d1f4a3e3770bb5045efe1a0cdeb937668bc2c88e14d831813b56eea08e2b0a731550a3eb2f06f3ec881e4751db39a990c2332e66124f6ea47b3a8ecd4b450ab9bf0e6ba8a501e89c2ea2e34803adc6b7aa4b824f25d41e691371d8e88f69f52da4de6880180fc7616ae0934d35e46d3eda0ba13e2d6a0b9fe87fbaaba26e3f285ee930570c04b7d3ed10309e5cda38bbeacf92ccde0c8113d8eceff2f66f515fa82c5904d90217063d8a5f8f5de8915fa5129219acebefffd6b0c6fff55917f141b88ceeb604fe2ae5a45cc9754a8bfa27e80cd194b7d6e63bedaf690e162630380b9ca5520dfcab175f7e8cfceb33f0ab03cc745f0284b8b2b31b04beaeb00c57f8071dff06a962f3d041c8694403f80cd6256e1f16a42e772166a15c5cae43fcd31a815d0a1bae9cc3f745dc67dc2f8808649eea37e1cc06bb8085b146b2332e0c529799011b5aab53cabe1658afe9dfc1fdc229f64755854ad02605dfae79803a372e340c50ecfe494247a1d30b7ca981ce8950316e408838aeabe7876c433ced17e9e00f15b1f7205fbdccd0a3320f4e4b352be7e0131b25dcb0825a72c3abe70ccb4f0e88b53c8b59f9d1d00853fa87ef23f13b5272431c44bacf355a3505d08ba60a0f36de4845c48585e65a23d3dd19befdc203e9ce9946a08bed8c253d8756d6d1034dd53a1e81857f46361b96a36c3618af5c425dd88ff124a4773a6f6f41e07106c76897769720a887d055a6381f6479032d081e7c5956c8100b8030e0eff9c00ae57c1f59ea8a121ea3ba3a2bf8bfa489c101e582a38ae34021a4352ec85cb104cf9abaaa0c7bbee2b27a84508e25056f6ac0cbbca4428f08f2fa970bf09c5c05adcbc4816f77ad08e556c13ab99abaaacdd5b8d6c61d9cc573c23b3a6945ae0721968580825f293c37bd3544d7e4571530039178afafd18018559b4b0573c40c1ee19536c67ad679f97efadf8ccbdd17585351c42b9c2f2323f4818b615e3407fbe6234813b24c66046a3b9be445251addcc64da994316b4701d997b56c51e0e258e37a181f7e9d2d394a3e05adbbd46a360b77a0b538de9ac329ba40bd7fa05438c10378bf7a7d8381f70d1e324a8d30880f4f23b0c7f949c98633edfdfa7052aab9293a9772ab17dd07e1b55eb809420d0e5b62c8016dd50d22c1aee13a207e60968a72d5334323bc567febd832d45ecfc6a064cb2752792badac75034c503aa7350ce4edc394968820c51fe90e14707058fc279c915a70bd2ebbbe8abf501cddc6c163d07ebabaff97ff6133822daeefb9cfd938d2a1fb6f6fc0d9949760189b4bccdc6371aeae5e770e4e41c1d849175a4788fca5473fc1864ebcb329f0647db0abe653b5982fa52b76fcfd0ffc5504d422a27f744183f7ff6e5da7c74084841bf56f689507b48039f2204273cfde56c2ca0f4c856d43f66c870fef18e05682a9081cea8af8c704d6a42440ddb62879ccecca401bc8364d6e723baa1b40ffa123ed015822450cdeb0c0f99b92161ff943191dba3463ade414947f724de06e3163e12eb1f7bb41effbfbbf6b61de763f6cdc124053e17ed65e16ede20d10a908d7b31ac5e136638a049314d03bc01576d435424e2f48c43a2dfd94414e0048e318d4e9c9b4c2546a0d1b68df19738a95b8831c4b577ffdc539765d3b9c70484214bf7533c109db3b3949c8ab663fb9ffc3b3d8a4ca0dc0556c125b7baeb0f473a9b9eb2243acf5a7be15a7afe130ddd8afd6efde9d216010eff4848e8d90e3f3e76185118d37c8d622900b2624706c63e3ba51ecb567a034bf4b95fab600596f7bc2afa293a9f29c887dbe3fb5b4e47d2166a416edfbaf783ccfef6503102863c77da50a068d2f3c181bf5f3865e9e210125cb335b7f39b5a1fd451049c0714cbf50925cfabc9bfe2c5b10376ae220763d6bb104b50767329cb66ab9328005ee4dddd7bb463bce6f27b9b5fd21a867b552fef6ceb482213a702ebeeaeda0f37098c4d6151b94f4d62c49cb1349f91387bcfd7e5c92bde7d4406e6ce66af03c629a5bbc88feee67854b197bd4be9bd32c71ea974c57c60e23a2c395f687508814a7b51874a8ca65549188773b76ba0278ce596de9c2a85e54905497e6d8b0c1a987a61b2b9a7dcef2a99bca4d785eecd5155ac00ad5b33d1b5e94e9db29d07f10caccb177acd4f04faa48532a36238b543bd4d47ca5172a87a526b0889310983b661b9f37e0ae400d74ca74517f7fca1519ec2e015e1dbc2112add17ffed02ccc65721c3adefc4d9d23a614fdc8e0c893fbe0e815d2f478e153d2461638f0d22e1961b1dc5548646912fcfb402d972bea8bbf9692eaa398df43629a2a9370083490ec9b48472f93b3fe0d33321cf73986510a0bbda41e8fef127f8a9904602ada9f51a0a9883d1be3bd4c33feb166ab14f7cf615b8ed6624b78a97fcaf5d0794368f504d963ca7b746d7639af6ab7ce93606561203edd31a9ec4aa978fcd0b79b966d09266b5aef22a411445bdbce251861da9e7b44925357e63c74abb3e03da89930367c696a1d2471c823ed08827f8901d3eefb823ded0796846ce46920bd6a83ee20d6ecd8821d297b21b71b0e1b7dccee5ad2daecfb7058055a14ea40a80d45686644d6c987ad315c1abadf28bc0ec186238295818736504d49009a20f6923dbd672afeedc9d595e02c53332047256d5a8691890f99525d9f9e8059f05c81fd8da608a8fcb90aac250c18db7b7290021c4e01c5720acf692845e9a6c05dd66fbd477e9c4a2354611c38960d14aaa72be5ed0c4d0a4d0784b0d786818036e38e48a66e66a828fbc46d86992e199156c3e1c4d17441ce7394d2b64c947061e156e7bb918abff79b96ab1ce36b2eac61460e14da91fa1cd2330d545388d03e442131b37779061cfa6dbdeddf394ead2da82c20734674f2b9d803e5dc50b01ac2a6d440eb04464e2ea8518577dabc5232d3ed0fdb043d0c4636af48329310e9b8f765b34f73a2284cf18d1eb98174923c48a582ad4c2e542062dea55adf20a266d3eee4c64a1e74ba9754a44c10cac94fa13bd2386dfe1e26b2a3b9e701b0a1c5297bc27e937a2eeadec3727b7966d1a366d8b5b96cf0f5f87f4f97b1bc900a5b3f6c5e846e33c4fefc543fdbe7b0bbf554e53937aaae8d82c93f7caa5010cd865b2f4b90c78f7ca0f96415bdb8a0c2411bbf16e1268ae62fca2c9972395020a4744069ee62104749eadc0989741ed1d65a2a23457b8d974f3576e0bf0860cc71f1b769e8deb388527189f24fac1818a5b01026a7456352d4d48576083dd00a2698b9af9282d40a8b1b6157db28a9b0cf58b632d2d57ce18dff645b0ec9509253933e48679399f383a05e265f74827a1be3c6aff64bbbaecee9a280fb0d20f0f7ac3b54e3234019c05b8402b123fa9e09f66e0b31c8be594b75d46a3f48e0a2460ebdfcd97cf3b38e562c26864abc2506ec5da76c13dd7ea917020479a09086b614b197fed29bcf82e232bb4d67a8491ea373ab4f67af65dcb55a84bac58040890e74addc3c3857e801565ffdd5a6781f5f4ae4463610b4822e30494d8620aaf6f0ae166fe9afec07e4980853625142fbf655d8bd642f71002fac53dc43206fdfb194b964e06b383516ff15e757f90006dad189d70e0acd4b7dfe7bd14b90990088f70055249e8a0dabfb8b006a15ee9158468ab4281c19502e65d6be15f0098855455b59cc7ab2e5f93f473ad1a67cd8e595ef41d105fc3e4cf70648d0b07057082abe51de1da2b8be96853c579cbe55e01bed1cd869281d49a8c05b5cc0127c89d7a909a7dc267fcc2b1bd1d29d2109967d8977f3d2535d3881cbe3a720a2147fdbd670d2cd0ce93d12cb4af25249415b3fdeb771252b536bfd59f1ff96c91261bc87cb516b23d7679fa9c50db5d3eb52b4adfa2cb8a12e77a4dc54f310f236d870bbe6fc6705f29012faa5a162cb1539bff8872b1c6280d80dcc641d1066ad7bc90fc7dd740f4589f7b9838fe55d7d57ae784b16fe8cb09e109f963d209c17e2b4961794c27834e08d199d62c081f15c03f367f327391e9e9f00c8a31059d885b1e4d420b5467619e7cdcf7002d948bbaa3120b1dc877d4158889e64200e40f4c9f22dae38ee988c1ac43e33bad7da0da841a4ed692427859630b2821082c0743ceacbb1f0446107855eba686945821c72cc645344ab53b9ccab00e1508162aafe0da36fc0c6f44b79dd23592dc4443b1e44cb1f4aed5bf305da35247057ff1d6a9d2b2d9ba26e973156978ac20229ec104cd0fed593a5f228a23506d0708b9845156f29ad352beb366752145c7e45ecfed3bcdd29cb60da6cb71c3c507 false +check_ring_signature 87a8bc07ecd156e5f35ba0f799e3d07c002f127388c3e0aadbc661a3eb1c9e1d ba59ee5975298ebca14c3e9fc5d623a4684a46d44ca1b171bb8fdfb3c96cef2f 29 9b0a3a0f2d803f3f0329e83f61e9396fcabed7da99e14eeeea8fcc8a94a4db55 1c0a1d5615cb61822f046ee714c19b27cec1a0d3c78cb443ad68144fb2835605 d5053a5ac98b461579b31a9c768a0711e3d81d7f6ed96f0ac93f1613a0cf7841 498a869a9459f19246bda947491771f3c382533ca3f43585926f20f1756b2b3b b449f09d18b4c4bc6e5321df6eaf1f35c000b54f6f15f4ed4d4d2a728007d649 621e85aa48c761aa101f43ce0324ebd7da8680ca6c095b074661a674be699732 edfb27ce9ff610d80a5353965ce6b59adee54e4df6ffdc83d69f4177c93b76c9 85f473398722a41368d272853284deecb723f2759be640780e62e0236c3a872a c0c3903a49d5267dba8352450a8226d90e83b3aa54490fb60fe08ad1d8a1a1bf 00e6076da08e1fa27811526e6f879845bdee86ec6cc594fe30fe68dbe86391f5 ce3f03c7236741fc67ddea0aff4bc36af2b07b52925a919794fbb68e795b8fe0 1e44fbf395efe2a027b8087eee05eb76bc00ea0f5ba2d27ca7876dac3bfb219b 94334b6c97f6e8b7af1c8dbd77ec55980368f1e32f6cafb653a8113e32d88376 3b24490c7e62d7db9f9dcf4d032fb719f03aa7e5a56b25638242b1da793244e3 6ca5d36fc66a6bf0210c25fb02c79b2a571b8c63674ff5d08d6f674d3369a285 c97cbefef34bd7c991792986d0653d83acca28ad8b0ce2022a3120aad7245723 f7f0b7e2c37cf504933347b06ea56ac5dfd1313a2b5916cb400575ae95354b04 f717be5334f52eb7452aa079b32511a364df7cba7fda72881cc3881d2cb8d227 cef529b894a7247bbe646939950fd3a063cf7bf3d06949cc9714e4b2ccc8ce10 c65077ed8d14fd93f6513dd71b2e9cecbd7a63f783150a760f3190f3ef021619 36cdf738e92f8ba32bc3084f775ae2067fc9626c6b83513bee0f11bf96a6c47e 913bceabebf7aa923c6fb7ef02c1bb89ed18d2984ad23fb49a81878fe21ba1ad 14c01dbf9e6f6102b09d5be5fbe2d77bff62c21dd64123a039b2a70b41030ffe 7f380200a68d62bec98964ef947ad4624727101bd4e0db9793aa5082f9ef4590 026d13f4b8bd62ae45ce61dfc2a6462ac0dbda74384112949bf270a3b0332f0a d25ffe0ed73c3c29d0eecbc6bbb57fcd8c1857ec443c88214fd9f43de19ddfe0 4b65ae77ec77c1f460a2344ead47c79cacbbf4d1d94a469884445716844201bc bf84e411fa0a46fa85709455f8f931f3cd4153c66e5cc03175e2e3505606027d eef2e3d4046fd2f400007e4d65ab4ac267b6a1f5d5fbc3164350eafc64053aab b62268d9d5f2bf069fe1e1b185cd17ba70eb8b313a4d0fc2c73aa167e57a190c897c5b87bb22b65376c89b2a115cd516e2efba21145a92e5f5df5cb37e9cb0066e0b7bb942c156dc7bd107d53f8f7e3f4ecbb4352aab71831ec1fe61dfce1c0f9282ec21da93c411a43b68644de16356ff38c42f04eb40c619337a08b5cd3108c42e6880ccf5ea46f5b7b1ed9e3640720f4fa81324e052606a91ddd4226b560bc2aa9a6d7640c1374f55f34a0cdbe12621089f0f26be5ce679e7de588ae97800444546845cb74d611f30c046344c1e1132d8cf201d6fa4f8e18a7953836d6704e9c01096cd53ddb591f10abe6f8313b46179fba58b512f5ea9d512c2b8d47f0bed7910db88834207defc92ca616b1613323e7e2e76a6961ee38c49bcc5fcac02f73b467f6c8b7a5b90f2655d906853fa20974cf0cc0d4a8233b50d3d099f8b0fb4fb95b74d4c0a15616d263fc011ce984747fc63003fc45325a026a10f053b05f8f168bc6c80e10eb24ed9ed837e416e461c420c6032645ba620d61430c27d027ec4a9657784d587559889a8b69988f02921583a6d107016b3fed46e0cfafb0ba6f7dc51c910b659f6a73618c837768ae67c59152edaa079fcf9570ebb30cf089f03168808c3bedcd1392e67c62027d3088e50e294f12dae288935e157c6690c802faa680c5042a0d28a11588cf307e88d426f58121db46bfe4bc16af458aa06b894cf7cc376657b7780ccc6eb0c506b39eb69c287354650bd2cedc0ba4f0903d291c4bf4adaf0847fd091177fe6120d507657ecb417024d27bbe5e6805203089ada3f20a6461cf19ac6b10cdb462b116e13959c4f9b046ff5f20ee189bf3301f6a5c690d0e4b2245d0d2d30108bc18fbf1fcf19858868f789b182db07fd070fc1319fae9bb69497390ddd575aab857c2ed3f97ca19c03667ac7c78e4e94890b692247d700d5f68a441850a5e7641c8d04922c2bef658de47216bf2c5512c9099786ed76822a46dedc8b668387c54e7b5e2875d9da8d52ac56c8799970813a0ef99373fbff2dbdaec2d19f5b9ce32c25e5afef518935768ddf877f9f6ca33603cb447f82e82a558f3a2e135225b68190796e4d558991ddb1fb1e0f97b3737503ca6cec9f64eea80b59cc7263b7ff0ed4448e540dbe7c73ccabb681bf4484f60697c5f7d75348ad650f3fe1c81b6968d4095f2950626f8f2d6f7400860f88ed04335e9e04c2436ffa662c6fb17cb5606b7304901e2a7508a2ca3fd14406da8a069d6e5fa1d06f059260da7e4c62d3e9ba055d4747e31852f39e65c23f2a2023073d3a05b2487d1c2ee11c5beff33e0c3a5594da24d8d10f2eb053e362c54206048de123e520865cfb09a9676ff785dafaa47db87026d99978a9a2fa5048c3d90ff21d617ba5ff67e95d4d86263125604ed840475da8bbd8a5d8d01a0b3740cf020395b170736a9a8e86b575fbfb2c927ef0efcb97e5fb0385524995b7de36730ad73de25ff9c231d69040f0ef8cd4025bcb3ade1eda97fac4830c0d0bfa725e04ce6045f83fe68fcaac7d0824108123d53a33d266941fb9587e3ce58f4e92890896d5642284f892b1fda17361b0e1c9c61ead110a3b688627aeda63bfc62a200289dd5eaa71615b149eedc3b2989133edaf06e6dfd0608d33a7d3aefd78e10609db5d21235ad610633a7ff0f010f6d939190f95ad69d85286a9b0b540dbcd640defee06ab972d3fc4e977179fb126ae56c92dc1682a3f0057459f9313a5617c04a282ecc6a8036562714bd2dc64126007435c7ac7ecf0a6b7831e6861b5e441076a41f3abc2772def54633a97ed900b505e22bc25ef4c3556895c59231cf23108a5386ee2c15e7ee2f775f458a2547bae7071348e698a83070bf3d164bcca1f01d69cf774371846adf09e4c2bc48432bf0d22988584e8a5947fea50ba3ae9670eedb8b8bb34ee6ecb928043f45abae151394a399904ec0bedf1462762da081a0dad49a6f1bdb0d420a1383f032306c08c7e659095baf251dc5cb2100d18e6bb003b83be459c02e726c0db242b6133a8bca03220d24def622c2a6f659f2fade204c27c996e7f3bd49ef0c4649e27c237d486e24a9bcaeab45183e49526b9a17200dbcf71da6f40e98cca3522e9cc376f5ff530f8e79a3ec2aaaee85ff86f44de0958a3ca912243be8d3d19f0a9c3420e8c31c16bef3bd558a79a8637369b12c308d250c0c8efabd3a5b43f241f9ad47911d1d43e6fb6bc90a794b810f482d1fd004c6e88320b3c3a5e5a90f48e2939ecb8aa4ee187263ca4e37b39a04cdf5d3e04b4d6920a9131c1352ad82573604c39430b5ae57c4a8e0aa311528da35c54cf0ac9a076514b2d29ebd8387a5adb384ef4c234b3602806a3cbab0752057e76bc02be05ed50d1131e32b7ae46af27e520c40fac44eb8b399ea60f3b6132607b19014656fbc88d296de0ef3fca7fea3651db973872e9c480579f85a0b38f138faf0ba58bf39b760a72b3fc6c726b2216788e951558563cd0e3dd8870095860e7a7031ddee097b5b8caf7fa50713eae37f3ca88db2030dce159af440c823969501107f6cae79863e2b7eb4c7ca4d19cfd77c40ddf8a50a0dca4e2d83d7b312e349f0b false +check_ring_signature 480c5d117fdd07112f509195b7ec9c3406643c9fceaba5d4da6e45de34a73ca0 9992922b2bdee6641dd34051f1479820ea1c4c7fa375c881916d947119608f55 102 ead84e70af88c01f24ae032e9fefc10384f2b39474d2313d39492a806ea2a09f b4ac3f25a4c87035c1db8bc9f8d00f4d93442d453a7e82b642426d905758b939 3efb32f3ca919f39de800c820676489fdc5c0074730fe379a0ccb55d8a471823 ce3dcece132b781285c250bf09a7b4d9d26ef877f8b472e49646d0d5f7da0890 a76b9dd70807b6f166df52f509d5bd7f606c2f39069270415aa3106334215e40 258181740fc11b8c76555a98349cc0c3b5635576775d701c2e7e83c092e01f33 b48a22aff82f635573108597fbce8139a61948df3586f969f471733376a94bdf c9203a9321982d3a4d05671f3ec3611e08e158ddf15dd47430eb2135f346a00f 5983e75ca04cdb21acb3815741a59d57d3daa3d7bf81f89a3e76ec21da2da462 b001d2876941d5cf22ce849d4aae2e6e7ab2166d94e5d7a84442a349b8c409f6 c503d2e74d9b09e78db2cc7fa836b3604d32c36f46521886ddaacf5c42f9d494 5013204c4139f244abf9652adfd260f721060b623c60c29fdd15aaaca2c9b889 8b0192138e1c88e4eda40ea0d5f9025463e027f03440f87f2bf93fb67dbe12a2 a54e2d402499b968180fa11f9851d622786d8368cb7380cfbcb45ac759d5bd90 bf55c793ec72c61961d7864db734797d91976a0dd2fa6f800fe73551fb3e8d37 22358195813a6d914a8111b6a249d61e4cc91cd4170910a8682c2d2ff4098658 319c4d197cad8c17e454e770f072a9408043c122e7c15b7df56e0b1b5a19472b 26ffc7c80d08864b9d55d841ae60ee2b19b4f2c2bbf8237c68f0b8524b81850a ab0e3decb80cedc0c3e459af676a6a6412143bd5e96793632f02566f7b10d6b9 af8ca2aa3caa01bc9831fb576a0b890a80dde92ef1aaeebdc013b943399ea1c5 c5573f7b3c358b3fa4f671d850314c66dbeca63ffa88cd10ac08a2aa10374b70 c0706360f83a0cedef10787c6075092f737c0cb51dbcc66da31b22a046e6ce24 88a5876661becd6162cc535280320fb487daaf9a1c6b8101b36d6ed4afb21fa2 0bb84f4e2122ad935a0aaa9ca48d1e3bba95a96afe5f9983c4c2a69ccfac7d2f 4a9ca4dcf6b77622ebab26ef92d09c15145aebc4a7e12613ab078e8c6a94e39b 00a69dcf9f0df1c6e1443e2fc392afedffca6737e075aa1ef873d9ab34372d17 a650d31a505457681de58d7db8d3adb839fd1bc331bb29e27f60177d505fa4b8 9cc3e813b96bb29d0849f47f2ab6421251c04e25e4985254d31c137daa586ca2 52e5b67771ca098e1af2cc2e554a3570f4a5ebd43e82bc9c4cf7a99482bab210 a891c9dbeb9b75c4d933f297c061bf84cf9d0b0d65085380bab0d74236f5c0ee 221259c0ab13d60d5b235172cefc460657c3283a4bddd20b4c31cb44aba0c4ad 4814d45dfae6d865f5f4c11584b26320c321a91b4f600a737b0d66738eacd00c c3cfa977da49bc122ced7f19a7adcc0ec161eef42f97a4daf42c9e42c3ba706d ada859dfc72752c6de14937ed8199ff7748f3004eaa14be95c1dc60f55a90a86 0edec3982d2742d68e8386f402f23a92f8dc0102ecbc99feca9ae3038fa58456 ad7b3eca862fc351317672b26f8711f5cb73c3994e3a1f885d85b98d00c67e04 a3781e37e86954c2d148831f64d9fa21d24035eeec3cc77ff479a28cc7effdfa 70dfca0d79868f81aa2ba9cb6ecfe8c9f8e84cb61bcffe5f920311a3a3fc0775 de2127884ed2c6ca7f6f4ad1f3db6f4edaed0b4673cec54512200aa8b563aae8 bdaada7fc6a6054f73a269073d8555370511a317a928595f42cf819afc6db217 821c5e13f6420c5d85b2cd39767dd3246b1234958ab7fa828e0c5f714e56adb1 a939013e76ecc1f82ceec78dfb265a83bcc0bedba44c24cac3f2455f0182aaeb 529f08c748b068e3dce0c61229ef7ea0c6660e3c7f93d217e25a08ea24890efd 843642b0fa52ec05e46db3b5a8a9e6ff08f3ad423f4db8f9d4c2a58d51d75bdc fb8c1dff65674e99ea02b175eff188bc8cfe7cefd003d93b38458df15427ca93 d415e39fc28511bafdd8460e4a373c861d6dc71a162d03653aa60301224ee976 f85a65125bacf415d3f3eae88e7de32ca35ea30b6dcdbd497e264a50d3193a98 bd8ce6c48b355eed74c4c1162fdd9abed6d15f5a5e4a8ab6a0d547e794ff6b35 1cd7a2a9d0f4292799fd0384cfd9b185cddd790d7dd1f2a092abe2c9119fa75e 53c426a45d4442cd9513a6ac0b1eb36a9ef284ea53e5c02c9cfadd23177f4d17 afec6d9eb0e99e1931cdfb4998f3cc820be3f1b96e88e0ac3b2ef52d3f8eb80e faa92ad663e88f19ca8d28b0553d5b87787c36bad728957aede0abdaf7300165 67e3dd6af1ca886f3874f5c01a79c78834394dc0a3a17b3483f8c3c534b16994 0756f62997daaa5e0c544378ca4f33fd245331bde5ad96a8bc3a0448b98933ee 5e30753a97e0461838064e55bd1c3d6920074228ef4c6cc3150e46579328efe2 722e401d95d8e3ec5acc7d4b2eaaf231161000cb86f8ccc55ab0cd35c942f0a6 d6edb7640c014f5fadf1957f6f1d6cabf20723f59364b4a7d3b8d142ae44b72f c4a03ae70b2b226c2298889eefff33f0307058ccee1b601ca44b0938448a1c8f 0e497e11458ddfb3121b8aa01bc2b6618819919ffda52d00bf26b08f1f9932da fd2c2eff331f7ae01259532d766dd469f06d98073f34583da9cc0a54e0e3fb30 70c4fda863b5700dd19d27c11982294b0c54bfca8e2e28fae8508079fc45573c 56da2cdbc27280aab49ce994dc39093a94d9e719948ae1789987820050855eb2 385ec6251f1150ad5922f791f89279f3fdc18d8f546005c037b732e7dfc5cdd3 53895ce1764a7cbcf57f5613e87031d7db7971871c3b72ff5ef2364e07889705 1ed4fdf630e78f730914e5f2b54cb44a6a1ad750bc82b895dbecc93dd4ce0241 fb7b46bb97a2918f6bf74d189480674e066505255cd1ff246c995b5afe925f70 c6275fb306119c06e63620e6ae50e79d65c2c22c9ab2fb400eacf9d84ec79ce9 26b3fc2757841485c371c6a7c674003ece0c487088886352aa26f8d27c088b64 498bfb48282423c5acd6a3dcb4c3448383679b06610d5515450f48a78f82f612 9163e40a9ef2b810f89f713ccaff89fbdeaa97990a317e22612c8f08a9107317 64bab3f1265ca89bc2b8d0d0c9802c4a795feef6a6cb6e3ff204d3bdda158f99 1eac6f8406467c3553b564436ebb8bd67d55501747d5a3b59a022be9b1f29b79 3349de5fb28afa338683318b68c0c648a159ef6ee2d810664ffba940f96733d9 40e1cdb7775b8f2c96e3207fbe6c6f39c712de247e589c61f429d080e01c75ae bfa44dd0d4bb82645ce24dd3ee3a94e7d7b6d709ded1979670ca6370e1577db6 a73eb9f9ba5318a5caf366a1642effd2f11522c835437297397b3ffe6d9ab5b9 954b45aee820364ae306ed5502659b56d7d78e31df1d803ea25c826cffc86b51 9616655f9cd9233912275f5c16ac4488934630efe30b79fbcd6c3fae965c00aa 59658276a6540819b9049ffba952e8da5603d93be6b35f41131c2e9064a196d2 590d46f3d7ef23a0642acfd1a6cca42f4d873030912f361df8b5a6fbb94f5f06 5e4731a35849b671a1c438060a58feb083a17293afacb81d3e1c28b7f0c00f1c f0a1444ee07618a10c8b7d141376cbb62da8778925c4d3feffc6721e71109def a85a78942cafce1c6b4688866a1717637b20393390ec64ec218227823f01067e 16ce77e28ccb5f17f2a60e26e48917b508cab9dc550eee2c5ee5c722cbd23903 50a5478b8d3d7f3a1558af60afe1e0763c28d47b693ecaaf3290c7c48b06b86e 6ce2f480ed0a7e8d31276ff175526d49470eb73d896e6b951c8445d7274b4e4a b37440329b633db59d5ec3738e17e51635bf4cfd0a7c7cf2f05414efaa07296e 05182bbb9620221605a1264900d4c411f8a8e82a4437c3d2d3b4c0a8cf301640 58f28bcd82bdbbff2be7b5478bba1bce825440484618d1a4a777a8ad9974701e 35bcf8b040bdc4c8913eff92af206842ce7218dbeaeb37c6ff0c289a679c0d8b 0481b771c881625ed4ed0758167c416e303390f7436546c838ff09a0a95df911 951bf4ea54c31bed3a2a5bc735c80dbab9acac4bbab1d268b990a7b029d86dc3 073481ed130f8cbe3298be2862293122c9b8c6d2f1d2a4d779fb9a5ab2321776 8a16ce948f72075fda9a172b645d40f4524245753aaef51ccbd23bc4e61fddfb 2d6d75c80b8e56a0f744bd5922a1c21db8aa217c6f9ca9d695820c326351f1fd d3edbe90120f0368c9b689f10788804f545978a5ab97bdc12c685c70970778d7 e2a77cb8d4b74935f2b84258054fbbccf986f271e0ab814bd2b587d05c449231 088e52109a41bfa8e992576d6d5b07d2a2dd38fbe726009143797619477214be 392d1bfb736b217f5ee1551404c0536876aec7b15f6863735e44de3faa719002 c8c1a87521e1b1b8dc5a5f08f01e88dbf28082588fa548e83dc810fc6e14fade 05d5fd28109be098b6ca02bb5c7a7ad46ec2affb4b4a1bb5d8c54b2fa089c773 cdb688f75afdc3b525ba798f4d42866715e2f49da49368f9c44e9ad10968c679 10576f2372d331360d77a17b7a9e3d7f07c17a21ed0d7b24eca791c61f9b5609c3c02c350c08256194a798fe3e34f5c834cf50a010fda84b57933a4f27c1e5036cd2d566959fa08920ce4edeff122523697faef39321320979963dcf4e25440381c67716e48d57d36b176a2188630d3c7d7143bc6edc94846484f15face9120ee08915cb9a32103c8adb7ea1d24fcf653307788b41c621108970b2ed92043d072078d542b69a600da1cdd38f521f5967702c9e0f6da327f54de5ebcc26ef3d064832d3c58cd06b3637eb366123f87cc181bbd664d063f5deb9262bf3520a0c0a2b04dd433a670bbabd542d3a80392be2b6a5575e35f8e31686065852cd0d5b099a32addefd53337159e97182fe8905dfa8d86a4d7e1efc443e5078f6cb2ed90304b6cee62f189e278bb815124a5cda7b7d14d1760ba7329a9ddbddf9b7d8ff08a0245fefe88c787f11bc4ca7ce041ce547fd80ca0ec886561e9c1b6388a2c605af4bda2c63c3a75fcb69c40f142da0351390e6f4e729cb88bd3c3f0581c4dc0c9720468ccbf61a622d723cb5cf3983da2081529188ec773d27eff03e2bdb4506e301791eb2a4b828156c802143dbd2f13b2cce4000030b666b2748705d142e012f5ea4125ef96e377eeacfc494f1ecbba987aa5b882752ca30821c50572e71013eece01ff3648a2840b325e8014504f810f9f9d64d25d5852d5a130719d6d90ec31fc9a437e68fca2bef760a1854896bb8b7e291a710889f29fc97e40c56090a2c407d4772348ade5ad19071991238fdfe5af566100cccff88a92bad53dede0df529ba80c74890f978054a9759e7e4092ba84f1ef398d7b90859f93ce2e38d0c7f902971d02287be05da605cb61b2f9a1f7633481aa9ff37e2895b3c3a78720642efb1df4a9283c9496667a0faa127a08ff35ab683819e5570cc7a09a0420e0d530de6d125bdd8aa66b0bcdd85042c7a3770c45bf1df3e12acf7e7ce0421ed0f6d4ed16ac97c190bdc802b2f2b8438cd67fa874989bd61b33436ae05dec83f0af48b7ceec48208656626c0892ac4e1b00e3bf6cb6128ffa6e64d8d2b6c83c70a6edec7363528064860df3464a04452cfc0bfd420e645472bb46f1a2ecb5a0a0ac23f1461cbf88a81759a4b24380a6a1950e5082274e33ffc3ec91ebbcf21eb0009d157eb7a893edc554f630d07f24e793c1e06cee897b2b3392e5a6cba92fec5d83233b35b4cc9be7415ca89fc4ede4db35bf1ff82c064c8f72b5ce4483c6d027351d41b98eb14bc92739486b32b8e7913a961b589f7c45be1756721f20c920560be387b3f7acb44b543d36e495b6795a8cc1fa884ef1c25e63af153c5f75d000807e96921512ee99d340916b264429c4187ab11d3d499d5f87fa9da85cf53096847a1e76bc02e085758bc2a02da1c7a247ccc2106595c7d5d740b815568ad0cbdbcd53d09dcc092cb6f536e5e59da858dd188444fd159039e28f37c8a8d8f02169c63942b38f3368ae4572a49333c0591f63e5f9d80b033333779cabf04500eec577cb5bf62da8cd95feabf94f7e87995022dfde3fe51341dd2b88b35c6f109699ee5bc4ef0c1a7828f97d7e7839652d05f35660170df6cbdac1bb33e75ee0531dde67e298e62d0a79bb3723667c0995da492dade942e00cee6cad29d82bb011910629c139e8d41be6f230c72de1a40641a575226f34f7be8327d8b0d62db03a6b85e2b54b130e4c30cfd7d49aa9e99e6f423a8e5775709f8beea269724400ac7ddc0310c42695703efd9794f57c3a3312e438283ce95dbcfca58dcce9057043273716729c1c7417ab0d81c1a365af095ea95549d3dfda6645e21583f3b9d01444cbc2d1ae990a5ada5892b255aebfb46ba2473056f5a05a47a5ad9b3e77b0ab20878e0e5603ea397de1ab5eace90fc38275482833f0b9413078b5ac8066c0213ad2fc87602ed28a893880210eec601d2bf99d0c075436aa45fe756f9a5700bad2c902bd860ec8c3521315803227928fdb157c8259ebb4226b947fb92cdba0110a903e507c5362d327db5a582ba2a2c1bb345a8914eb59db860d9fdec48fd09692d774e6d649e7bd1dd33a4f4f906f5a117da316537ed1a541512ac8221fd0257aa36dbc21ae3be63bc743bd422b65e79adfd36797c786c7559728bb2c1d7075fb257b76fffa968a56163087b99d65268600af63503b90367ee70d12070d10280aacd4f52554621425dc18258c15daf6a98c61a070b2a54a245e0ad596cfd0f2c379a79f521a9b556612c12dbe137a4cd0855fdc345bb7399ee171757e9c10d3f55d49d9bde6b1a2de9033dd8abd155c3ad4971a3df682f062e0f4a7a289b015eec87c89e37e7806fde05b6cbce455770b280eaeae00aa4f1094ac284c8390f86ab94967a4be9b88014262e95d4e1defc08b2b8f99b95a11f2f650e3543a103ad5908ad3403cb24b7e08bd4dda06e5d22a5ae74814a59876650000403583d01ddd56b41a119b1fae9ee9d50aaafac7b1c1b39bbdba952585f7c269b6c731b08e9063f354778dc8c3b17b1b35160818ff5fbeff65dbfa582dfae0b2c556a3101852361322deabc57c897ab2fddb361ec1fda28882c6052a76fa0886e42f48e02c74f58034165aa1210816537a326898a11be9c455b92a180b0df913d865aab05ef2298aab92502d6117a611be867761648a8281da67509756768bc75184dfc0cbd531cd363991dd06368be1fd250a5104e1a513b9a8cd7bc1026dc4f04f4600684877aacaba70742f93be8635fd1d7d185e8427169aea42a4657801ab744da078e471d8b1b473f75c90160583891459031509e0b81df0db6de96d7abd129c604a05077f8307f3abf66d3b0aefd3d5514bf5ee9fcbc77c291013d867d71ef4c070fd9b3996df3a15f6e3972cda1e590eaedd00df231be177332efa38419209b0204d8dab90f7ad0894261615a2ca007d8fd06dbe2b12a78561055a8ab8ac732008d0370f9abf933337f5a8ab431e547a543a636f03753077a1a1a5e164fb9a00ecde4c425f081a8019bbb3f7ca1e271b3be567433c81d483f0143893cf943790b056ce2917b95c506b2baacdb2b9fd389fbfa83e0875e6c0a819dfa1fa8cc0d0856b6b722b881725ba1709b2ffd36b07ba1d8ea0729bad7488d18dd2bb2b79c0db3752a274e0e4d60ab12b6d1cd5751c7bb980b3902b3d7d0f7ba87ac5f24740f8048a83c6e9330870748bbf1a25c9ab28f914deabbc8f8d2fb2954bcd8d3f0098765bbb0226a80457c71636110eaa7f7c62f53fb788825eb86a60dc13869e807d04850c2d84dbf6b7edf6fbaeb5182c1a18e17d1e33970b965f1d643dfc7a90360b7c4128620c1a4a35bf04ad2068df0e90dbb29b5d6fb837b0988fa0d99bc0ec1af32d2534b42900d376f377b370fb317003957cdbb72339fb650f22386030a0ea654705023e38f65154546663132af82f09fb82ddc454e0f822baa7d5cff0f4067d26bddfff063eb8bfa313fca7016478e98b94e82ec88a092a431f92b2d05cb81d80b687adf1bd42d6c9d841eaf9b06433f18dc1370276c9f1bafcb93cc082b0380f1499e8e6691aa5fd67ce8bfed07fd74c6ea76d09bf2a69c27cb419c0f1c4cf45a7e7d00ea3c5e806feb62a30f35619ed93b0766defb51fea2baf77204778a256303b86a14637ee9adc3434cc985dbe201b71e1802b48a460e224df909694a2f84de02a06d50058cd76da1170cb33b4a623316309f8231e0d96dce9002c6c27301ed394859a7a57019e1d997bd936e72432b9858d4bd776c39752ed904dddd84aaefccaf089d8c64553e9996fa1953fa2216d77e0f4e11260338f3790e408615061019f1d971cd5a7b03dd100bad3245bfc40846a8083f88f3570f06019ba607e1ca631b8e6bfcb4e8bef03f9da8646dda4cb38fb015dba53be3e8e00f0a370713d3b7512fb11ec0efe142ee83453a4cee197b39a0b9a07cce3b617b03ff076ad662d974d9a8d1bad437e765f18ebf9557ed9cacf1fd60c8d70fa07c06da2ae37bc39f7e85f6ab056edf0903c247fdcd45e998c001022dac5fa7c2cf06c9b9ca8ed614dc97ee8be6ce389101701ec17fd4470b2417a8b143f4ae01cd09562d49a92141cd0bb967c3fd842a6c85e332226512c2b1b2e2798fb4b59e4207638810bb6595bc40b6653788fc74ee4c53f949a424d0620abc925ff8f15d2c01d51d97ae94c240104b8b18d80a033e51fc0d58f3b7f8d92d7ddd7465c91eb4055ef8fb8533b800565e48ade7786ff23fd9f03b9eeedff704834528e5470aa4056c93a17b400675b6dd3688e0131b289df293141d86ce70e4ced402bdd5b9b60245a9a0f9ba3cf29a1eec567f6275e4dd23fee40edd9d2259dc0bd6f0b3a9840269f036b70c7a59fa959737d9f9f20164a43b80a986088b743727f7eaad9b75020c10b7e9df8c462810fac582bd7a18a922215b113081672efea644909654380e4852e0ceb678c9da596ac4857200ebb596c243a54eefcb609a26d2f7838ef70176c8440f4bedd50a53e6ff442777402c67ee0b2086da087d6c0c17368757d8026695b11e214ece0aae11f812f30bed3489371057389699d394cb65b16df0dd0922eb32f51e851321dd68ad7a28194bc6c81e11b46c542b077d26c00f333238080b3f7b988572ca579cc9590304d58c3b1de3323da5c3ee49130e5d7bc52e2f0fd416422aa699983fb06e9ca402e45000fdf9d444b91cf6b8611345423356f408c68a17c243db0ae390e28b965840d566d522fe54f245efe593b67020822b7f067c30552be91dd4536c5ebd0ab6c8431e59866b4c18d8f3a6a991c0520b02fd02e9069d1a54c8663bda4e187bdb3faa1421ad8cf678b8ee9a6f45bceb34cd96060dbffe8e78f10f9004cd0975f7e1460fcb7c444203d0bafecdaa8cecc3b89900a0024b7c3a5f67672a3c1bff18e09188220567cdef4193f39e19d69a85a6d10015e60e34c40184f0cf9748238111ca04e982280b093d8ea46151fa3b986d3705a5f0320d27ec45571ad93060e0c4afcc6a99d8b2e91fc29372e9324290bfe305678b3ed146052ff0a23ffbe827b4b33b283fda66493ba0e2587e38f57fabac0bf7690777cc6752e8fe315158a9d42929072f47da270af3e71593d751aeee9f057a602bcc443b134fad7d99f314f8806aa8523a60c3af69c920e5d530b5e3f30ffdcce8bafc093489d0055fd774ab09abecfbd007d67bdd150d2b9882c19bcc0cd336625dccdf7c1dd11fa055ca3b942f2e23602e39d32078ebda91a7d1961f0c9748a133b4f1117cfff3b45e254a42180d58a6749d34b1f9f79893470233450a70814db9c0baa2c2ca6b704b70d7eb4b235bfba7ac5325126c7be0e1b113690fb69beeb25cb58da744a3376db5a31234abd1c59c9870f2eaec323f4f19d48103b826676ee51b48664f7fc7f05f669d0bd48a79778dbd3329d647d4f2ff83200bf9db31a7a8692b160cdaa6b4f2ce95214331fe8b52bf2ed45d11342d5d5fe0091a4cbbda6c7138d2147cfa29c4a398a765daed9b2119f57d0c89f5e44695900306ca90b669e8341260cac673a04b53919f441217eebab8ccecf56fed47565203552b619f4a6965034111cbc74257231d9ef554dee52b8fcb718de13b69446608373da38975636975225224cc81d8aa86a6ee33bf9a8bb0eabfee98a730d81e0dd218ace912f9132b285387305b3fd849c1f56b5dbb2d4dc097001cae5bfe40025abab830229ab795bf7a48d3254c2b8381930988cbd6272e2f927756b5920f09a4c45d99dacb892188ae100699cf01c2d5a431680b5f0a711b0994763f61e1022241a0d165547430978e1ba53a751e8654529ad39afba0d7b2e2650669a1d301659ccf07f6db907a37bb9ac0338dc4f61ea6e5c52b69d08e37e2e8e7523e010abe7cd1fd861c81eadfeb881a54465194acf2a637ee8d7e3ceff77a1cb4079f0ad7d4406a47f604d58e04188eb5fcac13d6ba772a6a2d18e5c600a554763434098d1b201a1f842a8606ecb3c27f2d371d3b01fdf03c938fd1ddc2d1b533e39403f711652469f0fff1f082f0f44d55d552be51fd9dbd714b2383de3476819ed505b6fbfdffff62cf3f6e266b64e12315a2c2bd5e4d5cf64f0b79d50c012e477c0e02e61074b58edef4fb16d92404eec382fc83fb65ff027d8903c5608534acc808ae728814aaef4c6ffe505b254eb74874df4a16f831bb8d6298d5bacebba1630da3f8e192b3cba0041d11d5022be9529718de2c7bbf0f30f91eb3f98af2bae60c217468f4182769ac74ac81ceee84104ea4e22a4750589245e90c0a13a7c3dc09f1b58c88c6e248419447933f772d8af83d7e6837a004eaf0609d358788239f0251b7c5df91d8361d4baf5c86e9776146380824b1af90ea9a706e9d3832874c09a743b4a16ab11c12fe01c7d0580644dca4ea5f5c7d176b101fa77bbbda776308b5eea3ef1fd332b5a6e205b80077989fe3613df75ef63d8e35d004ad6c40e20fc179f06b4f3be916678740aaa84844753730e48a9470f0d5759758ded1b0a4041b46c12efc95ebf1ebfa5f249d37c95d3a60df1dba78399c29d4f39775619c06219841aee381dfbcfa604b522fa5d00bdee61cc6ab3df1ecfaa4fa9f68f5f00c618d4639ac10aaac02810da91a47266fa822bffc4d63d11018557d613320290d4ce73d25ea5421d6af948dbd75ea019284e4149d1e19ceadf329382fd5265a06429fcbd817afe5542ae8535d5c833d1de9e78c55db3ff2f576d5052f44617902d994d36366b326c8650009d5d4408aa5666a54a256b85842ef324a8e0d31ce00d708fe20915cc914606ba33d9d531796ccbe13e3f86b7cbd491dda8d5bc3930344db2fef452d157c91b221a2cd4e08c4fa3964bd0a0de16fec1fc1d6d4b51d0afcf0cfb1f211f9aa7f7fd00b84cc3f76cdd0647208e7c47368816c46aa651a050a80dfea4a6dc74afa12c88bf51bada4c5fcd4bf878a9c49e3baa4198cde2303c76eb11e8a3b5025b43da9230cc8fee3f299ac82eb3904167176ed007c714b07a3a38483d4ea1c6c3ef052bed0680bee64c631be01fa421ad16d1735af348d09a540acfd93f3be964eed62a2c594cdbef88503e6a1eaddf3d1efde8a54add00a5043ff24196e192d6244f807f4901bcd34dfec80282d0d9906606fbd937fb901abcff8e4c3673d9d2f89de68c8414d5440b41d43f2b5516e41168498e1512d0c00879baa449442f18ff4ba9300faad14437fff9a131c1d401910bb7b0f9676045d8f880cf604376001b8e9bba2f9ae5970366f77bf4a86c68cfa9c2eceb4460635d424a37b411df06033dbccda02967f8e8be73671bcd58ffc4f3aa54c75240314bff8940e08173b0a1367ba5ee496ce65ff6b79ea9ed6ed27d81e8b3daa680490774c00ebf3e775df2c36f8409a6fb036114c14f725e905292eb90f3245bf08286e51454640c2e6ca2caa5a610e76493d7a473aeea11cc832bc247b3fc56103c74e7224610e988634991253362f3e7a796a05674fd2a1163fea4ca9c1eeab0a61d3723d3d0fdfb8eab46dc30ae2c7ed6f12e3c7b4ef7ad8101e53462ae95b0cd2fb8c0c009369ebf66eb284e41c62fb7ca9c1003d4c290461089a3112247303b9cb3d7051fcdb31d9c0e73f5483876058027d6ef8b9c2db881714293ef63a03c329376af9349e0ccdbb443f99874a3c751cee21c9420ebe5e6b5d10abf778043ef533e23106319c3f84d9affe1714e6977d070c4821e33a09126b56adea530ca74c8c21ddb25ce498b28f8ef7f06aed4496ce23c02663d208362ffbcde8e70d353b0f11525928629ee5d0913526afaf78d12c1bb67b7402fbbd0e54bd94b6019931b8c0f256c308948c0153911d0e4ba6c92a7b3ef26deaef4b91d2bf08fa05d86dcb11a102d16d91d4aaf04a766031655720eca82b991ae7f57b735a9e790a4ae500aecab53fe12a6e57167e677178733f43112a3a184c5baef41278ac09036a487c58a25b2c4ad6e774428677ab84c38b17ba5fcbd1ab98dc2637d5261705b86f1b6bdc671e6fd6cd60b5f5f4e86fbb8a4fa6c707288b6e4a20abfa22c90ec864696073004ecbc8410782014cc6b4f7d63f84f6fdedb6deda9a1c21523b0f790ccbcdea8a396a6b1c83e34e093d31da4328d826baf1d93fa48a8dc6b96a0ac14b489c7966a63c63b11db0216f28fae01a1c2824558df4f6d804c4ca8f68024a201163e294042e86c225eb2dc380e27df78442f4aff9d0e7243d188546fb073d81c54e089349712f40a1720f271ec2a11e4de37cf1dddb476ee3dc54651405d49de749d4faf3ef74b6fd357437a233374cfd6d76ce084858a354bdb21ffc0cecbd0e6d21b1d5bf096d5823f713095c4933bd1f3a77db4eacb773d67ac8830e779aea1ef30ecbfb2e3e62ab80e2b4be0842abf6d74d4a19197513b4d9b0100c5c91e32b0c202645e91d51b2e822ca1db145063e36d26c534751a02955d6e805511d7ff069a13766a4b9cf95662850697b047c0c1f77cfa618fe0a486feb8f05f4cd2894c4445e08bf3dd1cf0117346d25d4812720075aeee7a696ba9b4c3e03ef3cf292e9a85f5136931e6bcaaed50f2f15837488328047083642361cd0070a3162c677a89f3464485a6b68e72b5acf13028754958eb92db43741156d4a5709193fc770d3dbed6e92c66df3d25f160d81154e17a2c5f2e238e292d1fba4070e89da625688e4feff1edec9a5ed4d63ef878458b4e0e26a73bc679f96ff81ff04fd00120f80433e89209fc50efb60e85582d9f8d10eff1ffb4156f86ef42bcc00209206fd68a2eb021d8a9b1c1b99d08605e57e5dcebab859c56bcbda2ba1270cccb643a4c96a6497af1e0b7c2e4289af846d8b8a6aa8cb37741f1dbf713c7a07703a888dd2547675f0d7992a4f54ea7090afa053eccbe4bc8aa78c64b8dd130e27a2370c13a0d62a3d29d22fdc325964b775b4b522822983bbe0f56075e05401b9438dd587297cfaadaa8c9bb1911e049c72ab9effacb9b2f0308f3f669b87079e047a1e44e74edd68ba03d5fa4eaf860127053dd3c88571318ee5052d65ca0c8f796c2378094c72b2eb285cb1996252edddfd547dbfe3768efec3cc1715f3015e48b2161d6a601f928c15ee034b9b19069b9792e7b9684042b80fdeaa59af0a5b94495d3008b1d845259b19124d3805e29b9dce2e6f16265d0e36c2c63a8b0b false +check_ring_signature 12c72637e08c355e900ee8d98b3ae5099fdde007931a8943ae3e1eaadd381426 5d610fbca12d9a92dfed899223ef587d1cf02b170088bacd8f82b643ad46f47c 1 ddfb1dd0cb88eb2a51a0ff536ea3c410929ef2e45a367e86f79301c26b7cb01b 02bfe30588b5a97be0f8e6be55dbc7e7ce30f29d7693b61479823d668c9d9a01b00647c596da6856d23e7a99a454183b6ea5099b2275ff660b2f09fc28129703 true +check_ring_signature 740b2f58e23675a74731aaa34cf0ea491a5dde6d44da60c5faf59416e7937009 df10fc70814834df3ef2a7fba9f004e3ce459f54beded745df069d9b61d5fe57 37 304a73dcb7db3667fdad01b48416bff516dc978e7fcb76bad174680f63018c0f 2ea594e4bc66344ebfb6cb78d9a93dff2536425ec559fc077378aecf7f751c24 c6751dee70519966254cca3d75d63bb5c167b681c6601a87bb93ecc718309bb5 50ebbf70d4621f52304b85dbdf4bb2f2202bfeee6ed43441a394d84ec2e3d357 9b39859a69228fc38979a41375f2c86db61ccc884c1f1d8ddf3558be8fdc415e 18c673cd4343cc08f8ee68a9951c5ef1e9f7ab87bf3a1508b40dc92da6bc05df ebf34acf90c8ec8ffb18b2b0a599107613067e986536eafc35725c74000b11b2 830a2b5f4c0af3ff92febe4ff78b4d558544b6536a1aed12e76da3931fed7a68 851e6441de9d2caa6a018469f844c0bdef1bfc3226863f5e9b34c0a38dad4cf3 97f7cab2343b0e567b15480dba824643cb3abb045e5ef6950a79e95fd79e5fe1 9976339fb2c9c2ec9a9b32f0148fd395c5759105c456927d1fe4ce396e598c38 0b0d7f7718fa529dcea9f68c4650534a76dbb99b55d5e12d2bd695d7fc47fd98 bafe4bcc86807b242df5f5a6030fb5b42d21e680285ed48cca8a053e067eb315 486110d7d1aa5f9b452071f00d9d1d1a4f6e0a7f2d503297587f7726b5847b7c b2d5493d865eb588913d60932402aa8f7c871e23be571e3a42e131878c979a12 c1eb58b1e98873b65a79f1d9d09b175c55c0f40291f5a18281628176025905d8 de5417fd065b61540640595b880752689732f4a52948dc7b0ffa68022492ab06 ef07c0b14f7ab54ef8e7b48510d1390ec37c15ba8c367b324a04aed354a348f2 d014c8f0f78a21c5b0d7a0caeff8fa5e103d39ef1156e42c6bc3f118db648fb7 ac04f0a8938a5802ef408c8f9fb7adb02dbe6ae2646094bf4997ad231d6e982b be0fd0f94a952f66528e1104bb064e248854ecc5c6f6c11a237db19762418115 9389f0771771a9cac2b140640cbfc797c16093588aed73c3832346aef426eb24 af658f9f957ce8dad818a1bf6c5c6e2a0feeb49d582d73d2b9e42eac6ed4be4f cb2ccb6663fce9ca9af453ac5d97ce1a24df988f614490d92209c6020739559a 5c4d693905f79ee6353d38ad3b96dd95e9bc9a46c349617d42de3a60dda54f4d a2d15f1f798a26bfd256e4287142b9e11079c2faa01186a054ffb4e5eb12bbad d4a7585909b763490b4f7c8404bcf2cd3c6f204bbd6124bfce591bafbed33df1 650be67ab2b34270c6a2632b3c091a5e55c0369b287bf9d5b5a55dfeb70c28c7 f28f76bdeb38d6a539f5cb3d91317f955d99a05978e4429533c122d9fbb988c4 731b2bf6a83ac3569c57045085e2b89ab4b2962c7fe1f4c97984039c83b15e94 b20e1d9f16300e4df0b1ad04c12a06139d50e1db0be5f00e5e78a2e7d338e6c5 fd4cd88ddf0a1c6a89d42e745a487283d477428c0133f7f1dbb3083d0b908084 e855df785e46515e1eb37fcca12e6c7a638793a250f9f63f369e11ebda4a1412 0fd1306ff3fef6b4a932e023e7e1158fa32b629ef83dd2cf4703a0a50b3d3177 226353a208bd250a3a28110fc1d8984a6ee0aded41bad601bcf1eac098531262 ef5417e390128280b6a5343ee783e0eb5381e230735d26b642f9582be6d61de6 6e6316b52fc1d0f8dc7b3dbf4e9bb08fe6a8dc57439c162fe013da476ea47026 d6542d4d874d0624ec5a869dd25120d001ab253ee261a789e861b0408046e10bd332598523f10d36112dba7e302e6eb2700408f7e5b0037058e8512c376da20b211a0fea7fd2649ed7e6d92f9ac3de8085728e3fdd5415b75711eb2ab2d1c00717feb2d8897195a54a8e255c48e8df34c909122d1d337f89d90c2cb376ec550044dc2014e52b6f5fcc969b6efba7356a11b7e3a6b2cf79c8bfff53444dcd2b0fb960a2376527c79cc22e5512adc92df47d08c87047176941ca5a9f0c2a7ce00f648882c8522285df72c08bab1500a211f751175e51f1f05d74ad08ebde2a390526e1456595255b0dc93c720f37ffac91c60f888615211d6f8b4a8eb54ce4ed005b24a38f19612adecd489951b0bb98aebf9cb2e5c53ab832d567a140f93bb003088dc6dfef6002c2d3408eb3abe39d7c45760610ca70e2a40914d3c706f1da0c32f20d1d3d08516dd0b952577c3645fe4a5e7d05970ed99f0aeb783ee89fa9066149fd9fc25b26947acb03a0957b8947c6173e9ecc400146036f205646c37d00d548d4cec980c5e27e3e557bf4a9986829a10f93a2f90e97a68ddc497c85af094cae94d4560ce25d9a90c4d07ceea1a21b42b1bdb8f38d90a5d4f59054f34d032c22596dcc54cfd35222f9768e7e89b2220c80f9fd206c136589b9b4ee81a402235bfcbb2b030bcd066f842656f6a763fde2aa2973ba1f33bb21b586e128fb0a62b02e4dc4588db3d011865f74b180a58c5fb2b03806f81625327f6223d2cc00ae7b937ee6f138159759042b43ab05b2b65217883c99a01b6a8deb354d233503ce8f9f88766b7f5590c911d1473c1813bdb456cb8336258b684b933bf42566003f7c7862fcd04e7682ef68a919a175d2ea820dab5937139da9ba969ab3456600c92c191ac7935e689b9af01253a96b2d89e036c8698016da8931ad46216ee50c89ba6e66435b09d7feadbfd4f2c928aeedc81b4ba358d253336ddf35e9afea08d57d62e751bc4abded274b76e81d8ae3c82a10cad8161c8732cce2d9886b4c073f7add53bf7dc096989e3dcff10a8e8638ca39252c130fa6fc16a083b714fd0a5da2b0beff6ab88f559fca2451325503b5fae7d1fc1dc41624ac92da74aa4d0598930944dfa6e7352f53fa9a06ae9f82054076629768577704d81861aeccb50b61de096fb42832ccba8d422e944e6d302b8e3303999d80c781ea594f964c710bd1bf9654e04c9fc7498172ed0107c3678355d9240f6e1e1e07a4bf60f9b5780f14c5078a3b5fd76ee6b4f89e7cefec61fe02e662c50d5e06642957c64db7690b88d47967497310df49dccb9aeafb3dcb031bcf4601d897ba512461b40198c90c774a7c5f7b287ab45817832ac11cadf7aa59a48b5a0173dc3f10b61745b5db0171b234ca2473c3f68db9dc8a9f0d32c35aca8e807103cfbf4e1d8a8b124ea209b21d02d30d87dba127175e30f1331f84e305e8ac039892430bb314ff36cb0706f191201f9aa1d4538983546f0286f208e590ff09f48631d6ecf453f7491ab6017e3800604f197348caeb3a835ccd72328574fd49e9f3be476941d1dea4e0b3049ed79b59b32d3543bb6d3c80f6013ae3cfbd83f63de5def1fc8872a767c06c089106d36d35996732d52972aae89fbd2430964b7c98f2164209f51b725d9f31057d17faa766f96aa56383cf3e762b6928c96a810732318e13ba43cf97ac965a0e16603d1728bbe1a582aa4eac36627fcb1d2882e3136a6e0ee0aef937c7660408e24c95fd03f6f152f70837f429272ac6369f3f75b60cd2d3ee02a4b9ae5e2d03426c2c264cf971a0278983a2786f5539e4bde5e5a0e77ad05f73f21ddd7ec50d4c17a512601ecce88d0d308c71d7c669851b50d001497c7abdcef8738a222a0b10659d12c813f0318eb4c927151a282087f2b733b00d4ad9b7a701b6d4633a06a5d6810f0648421c75bd36954e83c0e7f57a827d3af99e250adc576b6ea73905c729d2e1b315ff8ef568ff0b88256189389e8abf1c2232fa33b185b07b1fa90a8da5f1be9f0d2be7d3f440f139f8b45b34c5c236fbedfe9cde2735cadc59fb000a9cd71d70dcc9e0ee9a3db2300a2f290b7e36984be6a6d5765171816e1e100762d5437f1efe1446f52771fd4ea5e78d8f34069c998051950a880a37dea97503303a0d09f64608c0e12044a8880214caf4c5350285f0d6b738b18d8ce6d6230737566dae9434873893c0bf26ab8ab636087fed0d8d317ee8f016c246c3f07a001a6192c2fc33248f0488da0d2fd419a2d173f76f9a93a27e22f119c9da0cd701b0b65b75f5e06382b6b5b1c8b06cb269449ba6266a3c31b2eefa8b416cf17e0728aa386ff2277136f5ce5d3975de3d25800f7c37fc6a369cd1f303ffb032c806699967d37dc664321732b2137bb0798064faa472ac514b5944b9f7acf39e1f0ec2a2cda1bddb57035ed16f6744785675d8e7d94df2e9d765f1a7854b0ec60609c0a6c2050783d892902f8b42abc3933669f42a25a92a7ada76b01cfd459d330374c169425a520d5658fa888125321ab0a5ea22c5848a1f1738bc28a157fc9005a8312b21cab488280e675f785fb4b4354ef9aafe4d735cfa1917527da706640220c9d84c601acf32880627384315bd3a55d503e23b1d02b5ee19b410fedf0c0e67e491faa3d4594c722bc50251b20abb8613df91af1dfb58d0b34c753ddb390e1bea2a2a03f86f70045652a34ebce395098c4f6568ce9a5ce30e833d18316d0c86a8032197e38dacd6250f75ac08d140fcd31ac94054824d2cfa31b9e39b5d0612c633f6f59e6cae0365ac8b5cda68bb426e0bfc2e573b333a1afc6e91f0f904489fe60e29686b500575b34d7cd2fd7302edfced197a034903dc197585f7c90c1be1937698be315a6d9fb9fd2d057c9cc9816e01af3afc908bc775d22498780e102e5c9b67cfa37840a19437940ba80d2debcdf6a889e746fbb5b530e458d702e12a1985b24300a1165238f471eee26b1478b5455adf7cc00a88dc5cd0d4fe0fe7cafcf67d069de3878a0fc46d7b7ab0a7f7f16630a817b4521e681a79e98100a0d75be9ad5f7b15fc9fd99b54418b3c2c91b36a69249b6806c7fe128ae4c602d8c2edf0a6ab18ec6e8483b267a16b7a9c19ac9f369241335f7b5853736d5201a91e10fe733af8f92eb16a09bdc66b42a2be5a1d9c7c43115183313979e4a40b5e0351d5cd1f80d7894ebac9ecf494d026858845f09e84a58aa06db3459fad0539aba54830121fd56ff4c84ed6574c91d552c54da9055cf288cd9d4c4f416508d16460c7b6faaa667be2801fca2dad82accc4db44de1641483cdf62f38916b07 false +check_ring_signature bf84d7852f781745e8dd0a254f6ea4f26824a189acff7727cc6df7032ee674c7 298cf75a2ca7f054235d56f0c33963c57f9b74f362813324dd19f10b10f060cb 1 d17cd313a63d2b184e0f020dbb873d6c40c026c1622f7b5cefda346f63242615 e79b0af87c3d57ab95b4da0b8413b04f301fc9e7704569162501eced16f2f50ac3aa8f945de36f785e6109d8dc76fd80d2609dc66a609d642114ff7f3d665304 true +check_ring_signature 5e22f77b3f8f7cb2906df55316188cc3111c085d272fc9a25eaa5736cd0df558 4de1492c759b9b1e9be2b60ff11f70affee7cd9cea220600518c49b6f2384bd7 5 84f1a8cf60cc0e0b4ca5673221d97953eada690ed80c566539a2fa28bc223765 099bcc2edb0d1ccf3dce6d6bc8ee2bb5cff50cd95c10930e633f139218760cd7 3ad1b647a0cdc77f1fea866e5f3123ea99fcf78b95f0c99d09a76e3534731d72 48cdc19e63ac9b7f397d5d204ca11d28c2a2d7df5179a45a624c64a5bff3acbc fa673d78a89e8e55c04bf491fe4247b2510e9af6a7904c90a10dad0ebb7452d7 139c00e37b1a26300a2b7947fcd87303919e36d9ddec0f55d58c669c560af5073d0d2d0c750890b9304fdb44025a2a4340425b54968d0f357dd72ef913eab3059ad99038681654779fba233d8f316b26113c95da79e4bcdf56f238a550f4cb066c5bc49b5d5f01a57ee2fc0719bb81695456a9d25f87acf1f7e1ff246c44630e4e889d179d54f9a6ec5e27bd2b27394a49887e91acb749f07e1e61147cb870009db174a335e288c9fea3d116382b8ec5606893313c1ea073acee64fea50ab30ead85fe09ec5543e0cbc16e871079e37e58bcd8ae93b389e5f30c861716226800563ac263d86ee4f2d39fafd5c83864699abb54b9f0211d3996f89749c0067507a8a5ea7ddd52621bbbe97551039fb634c9862677259e6663b7aab2fab4239a0ae9fe68048f1029f0fb1a7f6f7d1d018ef02da1d4d1fbd3ad410871f34f34b304 false +check_ring_signature 71331479bb749457b6a396e4bc4fe778d960ccb510e2920f34355b1575b01fea ed491bd4a43d47d10b440e002d0ec051f494e6b32b2645bc53343c386ad9c2f5 1 049c492aedb666d43c9acbc8ab6ae3f1dfa29b8538bbc2dd16a33555dbbd231e 1f4304cc6cc0dd9e5e4ac64275ebfdbefb576328052dc41679ca0af794e50e0a44bfba85b76533f1f4d92294790eb42bd40c179fe008d22c7bb8673912c17e0a true +check_ring_signature 86d77be510b4148396bde50a42907f490c4233a5e23ea29e84eff081c65e51f2 cb0951bcb6d5c5bd358856bafa589a797fa1c30a4879754070c0b69ded67196a 2 039937e7a108d2262822e27a972b049dcfbc332c3610eae1b2d68dd48b0e829d 0c8f67010b09bb3331ea665d3a1d2cc7d023f6c11cb891049adfd38dfce502ab fbfe5b5b279324efdccdc5d1dc6d08369342d274ab9bceecc97b474432c5507892a941f8a7789960961dacae33b3c1212d77df47afdfc5ad1b728432fcf40d08d1fb2949961d96b125adff3ec2bc3b4dfc36d5ef5c370df67a60eb8c95af3d07b25fa9e8a0581c5094444aa78c8d0ed3547c80dc2146ae17eee95674d3eb0905 false +check_ring_signature acef42266aecf78b5a2102370e9dcead329bb902482e27c1f360d17bb8e04ae7 8a400a83f97d303979f77858175b50afb191448f2b35f1fb64bd7e1815b736d8 115 eb0f20734a9e57ebaae066b1a9a10d5c75421fa3b7dfebc0e50ddf30df06a5b1 2c352c64922239155b8491b030955107c318e408e2a739005475b3a6c4267b89 a65b82813b7f9499e0cbc3f3ba433b3b13c41debdcaa386c99545aafd12c4201 59b5757796eed47ec1621f37b6107e20d705a9dbca81a6394375880f29bd162b 01461aca4146639b3d42a2365b32a7652c99d6cee9cb3379c64b0812ccdd62b9 5c5880f50231dd9885fa77984b2dc3d2584528f3bae3ab999772fbc71f8f83f6 a241ef2e1a7d5549f9b5115284503af727d5054f1600000c04f9723f4868bce2 b6b3e2bb95aae81f551a1c09282c6f620695826abfaad0d23c4cc5831f571141 9b886fdfb948017f94537c44c85114cf0cb4750f34204a57982727c9d58fcf3f f99f19dfaad130c1be83cf637989a7ff1d00a20fe238f27a9d3651776a7b293f a6ef1e4e2c494258ab1b4d68b4a99f87aeee51bbbaaf45a3a89c0c66d27fe35e 8855ec2e4323a945fb455e751aad16bb138672d7d52e0c5e3563fc5e7b45bc20 ea8b2a46ab0b9a26f44228e245c1e9b7aaff894c2aa376bee59453e11bd33390 246198138cc24fdea28421db4898c2c61a5be44077997912e01eac04014b43b2 9a45c49ad3220454d5ec6b1cb0d57b7f8ae6c9fd1a3197df3c0f67067c97f6e7 20d81e9a26fe10fef895ee7f3094f8a4592b0851b7d3ab2b19d65ef8b63655e8 b4d511f46258b652b78fab36669718b0085348bd199569441e3dd2db02a9b57b 71cb29da0d31d9188aa7bc202559f1b848e1b9261a81465bcd63475bf2b02b61 8ba9297e12e4a9656e05d34fdde191c44b7d3c0bbdd99d4207ed04e19118bf5d 2d97df48d987ca12f7b3dad194e534a6126ac79826b733b15254cf2f11d309f0 27c0d91ce23df3a82b23a0af31da005181313582b3e23979d508e88d216075bd a5f182ceab5db9ea108ee5d3d5ae1932dce4098a6c29cccca68ecd4bff3511b5 385ce63c74d0df240df2956e48d244a1169257136e96dfd4dac8701e84edcf65 e80842e9789fd95604fff34a3b2453952a63b631113c1fb655146bdfe86ad3d4 bd419fb757806c0eb4e6c7743a217f0e961c668b327644af0008d6d04c1eaba6 1cd9006ec7cc0dfacd989220c85ccf36acf942d087c7251e54d01383a898b0b3 a38c46b681abfc60bdf6b43ce8a7ee56733523b3fc39d4467f391bd9a2e0905a ddea2e194485ec47298d5c5bec92cc8e978213400e58aeef3e8bfe2d22f57c24 11cdf3bf00de4de1963d1afd84035f5d756c4436e005fcb7b4f5ba3871f4b8e4 50cd5bc95648efe5dbf89f9651fede1b337f16306da5a66443e6252290e6aaac f4c536131f99f55ef8f9f895f4412bc2e3ad627aa42336e0e9f78365ddd443eb 777e75ac294d659c4e5667604a213144b29a2e07e8e17a00e90dcf9857556b46 1193830be7795e806468b51ab8e340cfb6f251162d570b1797c2b53f28283a52 a9143da9974de6b0aa32f917ebf8cda8222739493302bf5dc63b5dd56947a116 8a10bfd98bc59852f70e3e5fc8c7088e52e0c5cb1e82a774a008839056f892ec 34a0c05f9b4eff7a51b4735903c19b746cbd67de035a2fa1f05574bea4a4f1b1 8035bada3f26a08093a3c1543737404edce083dce96373e858ef051af801987d 2816e12e9e2c95206ba491927490f81b49337bfa94466bf2ae8877b63a2b318f dbad6871522aa495e412964acc7214b1c9090f9f5fd75e500c8f6e74efb38617 90fa409fb52a02b87567b3262524cac8223359f84b4f944a7b3fad6585964274 ee20527bd119f2c49b23d7ef4dfc00e17d63709cffb3e6a477880a63e593efa9 faf3e953f8f915d23423c45080ab40891c3cb6154b88b20d434672b470165ea1 ae6b646af03afd86d8aaecf47f77e9af554e25294c082dcf1b9c969f28e5397d 268ba69b94898f38390dd0cdd7c87fe9b6ee0f28e21a2090bb8ec7b4e21b4fab 2ac9655fea3b5bd32b32a426b39ec52ef80cea4768ee5add5e78d3b64c3edc63 55c3aeb44c4af52aff06a14bdd33cda9e1105b7c92c29d3f09530a7ab920ec1b 8d2b00d10f5deeeb82cea50a48d72a25726c4966b38f2e81e84fc1658a4d503a e082444b357a2f557a27c5fc763835a7d5ab753e3935539e519c32ea738005e9 53aca75c8d3c4cf85023c8adb84607be35a01f09be12f8fab0800326f4b71d42 7680072513ff16a08fb393e3238a606f19a2d05b49aad8f9e9a16353a6a07619 31e48462b44b8792ebffb801badcba285221ae0531dc16f4660a8e42bc4a8316 e33e9c06fbc8cd8f124769e84bc4213edb9cf2239c82553ca19c235a27595742 25834c094248e157140cd13e751ab46c43097090bedf365f462fe52c3ecacaeb 7c993d615f0b7538db771e2500f26a51665736f468ec6dcfdf126c676fff0bbe f42704f079a7dd6bd331c3a6c64e34498642f2432a1d9c094f0a53173e23be22 24beb995f715d1559021d275315be058839a1c31304d661c882e56eb356ad8ca 18940ebf40504f3b00bfb89d1fcc93ce8640f279edee15e48bb26d1485f86257 591fffda46dab09ff4ae7f4241ccfdae353c3fa55c684bb7862de95d33708fb5 ef5dbe1f8c114eea7ec39980a0596cec21ea78f9c8e1343cbcbfe238ff6e2aa4 b753e2d1a3d8f14918392163abb846dcf785959d58f05a703ded98e9234ecdae cb736160e9971fb735691894a22f345ba418a3c1bb855f47f432e27153effa1f 549ba14865b311e2283aa094fcc5c1d0d2a3697425e64508b7172888ff3146a5 6e30ca5c1cf3315a62cd1e3d5f8ab2dca13828258795a03efe4db90dfd218664 c65012d31773abd5090b84c5ab92ec60cba08493c81998e93ca164456598321c 08abcd858415b7c26dc21ec5b9f7357d281008254b2113eec19a21e08a16a880 dbf47bdf25da9b05df8103aa934771dd905bc3865140009746bbc8afadaaa09a 1cf00ac67720bf748c246d1047a0c498e96679acaeb78a560103a96fd6b59d18 dd82c9434c48db28350531c02e65ac3a2b21a1b81e8aa5b60dad83514b32a842 bcffc5ec5fa724c3e61331014599b0f5174ec6c9a2f9ebb2beabaa48e7e1cf19 e70cc3953c37487dd09493b5ab7cfb1b2587a7ef648432cf7bc1dad283818f3c 86858a37f774259baf7b9c73619086379b140c35461cca6fdc02ac49e8382d0b d7b0510a6eeca0af4ecd2d3de8c6559462aaf9a4b9746f90f50d1ff6033c63f8 04f737cae10d4e88669b14098b7eb940141ff55ad04e40817a7d9b82615b45d0 e68248daf9a57e3f86279aa211e857df08133cf683144d39f79cbd7af348506c 5f97316b0ec966a2d141e3e028e71ee5fd3696d3f0e43e07b18248a00e56fb96 d3315957d1dc71092f02224e33d5287967f2fae787a5a51906985f308fd1a34d 6f92bd90794c68f0f0b8cc3b3ddbc80a70b8f930bdc92155907373796095d46f c52cef238d836de1d7cfaa5b947315e1c4fcdfb3465e47ecd5ebdf1c2accff1f d802693e26a7ea2f5d605e0ffad76945724da77312083a44e604e1353a7f9777 84696fae1ac02138241003e738288c5d75d70b04abb642053283afff5edc21b5 b508fd540ab36d68a41e2beed2b7c910628beb1b283bf2e944d8b85203799c1b 58f717a10179aa5c0c5bcb43ba1c2b0e10b1a0f986b7bb2584d20f91766a64b3 315144ba794fc4ddee7556e43c5e2f5d1ab4afbcc48a685ab36f23103b9a81e9 ec43a2d9f8deb76f00763cd506b89ccb8f369ed05e29cb5986a100b2f78d8bc7 f6e02c7784329aaec9fe63c7c0a87e6b544320a66e423607e78580ad66a499e6 29a8a694f818a89eeeee825b230e8d11872311ec1c8e43fb48590b2ebfb5932c c7f376bd38b0543778876971d6aff366e28f415017b23b42a4848402e96b16f6 5d0b251fc060a93d03d9c042aa44ec3f2dd708ac1e00e5e9711fb9f652a61e64 594df2303c5cae12230c90931400c0f9c496de9e7d5aabc95a33db87ee8051a2 87035e5d48fffc2d5ccb1aaef9b71be63d3992b2304210e3631257190412c2ca da838b2f6233afdf23b12b8e834bb7107764bb1209b949e63ece6feb15c9a0df 94e5ad1f93c03989cc161bb65c713b027ed6dfc0ee989a13a2638070f6e41905 7880f1b69b188f69f257fd3dcf2e7cc66e0bfb8a47eb4171102dd894950f207f d67825d3be3619a73191c42d7b91b1f3ca014272755321ed31d0ed41481fdbab 7be93f5e966f10591fe9cf6992bc4c9b9a26e6d0b48c353c8f80b3e2377617f2 1a85a4daf70a7c37c7b54a6ee450bcf7e9061b7065d53b12026fef6c447543ea 5dfd3173143e289d0c3ebf2b3d5127d5d694743520621d42a62b7d6ccb6c0cf2 5aff0fecba0a19df577d4717dffd24bcb355b054ea775d7e3519659d622a0f28 f1cd9b4fa0a3c300499a808551c982753aa91aff3fc471e1999f2154a0296242 dc1386f425dab82588d10b8641a55aabbba455059a6e59d1d4663605ff77ff3b 1d61cb7f44f0fcbdf4458bb5ebe9201709687f770ae4adb5057054ac77489b87 1df95d15dc5f26c4a218c2608c67a0e3f03c1c9ff77ac1ee7a48523863f49aaf 8aa60725aeff26f49a92916a7838334c51a4e2d26bbbbee12ebcf1a92870f049 81cc781c0cadaf76b77d8bb8ab1f684448190c403cd7fee4fa6fd2935975a732 078e0930fd84e4f89eefd45e24607f12beb1f5e6a0555d5db6dfdb1f231ad220 af98f4de302152d4816ee6c0b55298b42aff00fd567fee7ee2e388cc60c3b994 9d41614210b66735b64e6169b840d1be93b1ccbe17505d7b0589aa549a35d3ea 32bdc87b1fe9f5b067c6030cdbf82377ae39b0e53717d9ff5ffc393f693c2f41 81338f8d94813f464f0e1b33eac7b85e4e4a51c4e45644933b8b68714396972f d8a686438713ea1a881846676797a6da633f7083527fa09d61dcffbae5a0e853 e67ba52a0d02514f8252231959aede176e20702c186e0bdf49185a63a0e6c311 65c44fd115a73bdb049edc403eafc3efcf6983ba3f94341789be0f69e66e63af 32d10815113998f8a54f1baacb27805e952ae1db9ff17b75d1d53c30519cb8d4 4f41cf7418119ed77075a0a12e64a8aa3716b233843f06be08d55b89b86b4c0a 03048360642ef1ade23d871334974439c8d02510fe91149372c82ea7cebff05f cc9a7a795aa36134e5ca3db7ab8757fa8c535c96d737b27b14b6f4e3710f1c0dd81ddc73af84af09bf0c490a8c087eeb4e70e13c626abb907e70c984b9542c07453560451269a96255253f2bff86e98b5645d26ad9d9af09b6c09c98a237b804ebcb2449f2a9f39fd50f64a9b1615d87e52d648b687c296b834eee624be8c90f95605bb53e5da168bce5fc59e480979033cf4044b6c96a529c847e5a6f97fa02b588b947ba9098bc2b5168f0e02b700c13197cfba0bca30df084115d8d9a0402267a077a73e1f850a189dc7af1102a720740f56019287c2b88998d6cd4c8f60885061cb567471f3299c629ee04348f91169a954e2862854d4f2db60b25585106bf5f5932db8d103223fcac4c62987959b9025c3d93c7270b1ce3ba7a2fa78b01074afb1fa979b883ec2d9ab16c534a6b5f29bea84442bb8cbc9cb382b3740b0b2c1d9cf10163c2fa416c918f6731bdb38f404dddfb083ac1e8e6f7e88828a901dea50cdb4fa353189f945541130f44e1e194a8da331511e09a782671fb87400f74a6880b5987ef37c112b5c5aebdfac7558675aad601fc03a6c511e21381b105eb8f67facf612f56774d7d2d08e0eeb18d657a996ff9426e625bc3dfff508802e707fb0bd693c061309df95712c4593e13ba790d1e0426933d6bdf05fde1fc0a0a329f1e7088c1252eb58ea6b190df216e1452c06e890ffaf48da0474a15800099a1412e2750ddee2f2ff3fb139f03b457031fe3d220c323073075f0a9b3d902b3bd1d5b07813ab41cca3516f678c3ac4923507409f535459a15fd207e7546020ec2237f2dfdb68e88f23a71ee64af3c48dad04bd5bb4c44c6e986cd738673010735ec736ba7a01e6986e7018aff6b70ba00731592141c1c1e1a5c8640def805d122059ef8eb26e3acd9d64c2f41913ef66208809195e02f4e6fd41f52c7c6019f0e721e5a5938dfee5210dc0b25ff54c9706c813525de2a1fc830e5161644003cfb78b0580a273b8413a77ffc8a5e44a92e897579cfeb589af41073fc69e7092549433107afd82421a7d94abe904f8ed235ab9639cee872996d89c99ef094012955585c63d52328c41915e398b123e07a40626c59541f28f019771cb1130d09550688cd0b692c161d9154049402548f866afbc7f58ca9c10a55eab4337f6000c449c6ef40e13c8f71ef0676149c679d8a6f2d224ef38de7bbfe8fdaa379e9075131e1f3d0a7ce1554ecd2444b1294d01a38a9ab94ff4e1840dc55782cfbb20fd45b279d6b1fe0a3b22e1d215776eb3aa616f08cee6e4f64359d143c556792074d1e5afd006ce8afc44ed95bba9a9c10feeb3a510db420464ea2341c413d8b0d44f6574c3ada9172f366d8096af238a63b4e05205aed5c8bd8211207654a490bfbce8843868f160e882f58fbdb24e2628db518e3b6034bca8228a1544b5bc80c72446571efda5ee51bf113ebe5bfccf3cd9a373d45a2d58422ab51adc3e286052e3799c06d3fafdcfcbb7c3187b0f6c380ce532d7513cf02e9af3ad5369d84090cb4d7c1a080f57cf2719abf9e2f2e330463cfa0594f1308ddbbfd8e0cc6b60cfbd79f1c326d2c779528855463a1978a7956a371e87d8906dbc2cbeb1c140306df774ba65e2711159adfeada8315e394570bb78a3575b10bb7306e44a74d5a0b0767f43cc681d4814737e22d4da0ecf7511792c04d8f05ed4ed22cc5e029a5055bffd200c7945ee5c04c51ba1c9336ad71ef373f27551120f34fa82a8289180680d31f9d343f79044f36fe303e8f46cf30689cf4e8e2a2c712113a66694c420bd30b8e0c738613abca99da43bb53e83131c9a06ab891d7b6a9558950a95f490b4ce76c17b8524a74a8fc2d1e14e87c952d02d1c7af0be84a3b3868517296820de9b31f07e2068cde84386f767e52779b58ce3f03cb2d89fdf160f2532958c7075031ede927b04b730ac75aa488d6e67a1d6c51cdd404b5b11de65137c5f997039eb66ec06e1f87aae6219f790e78391b5416a965efe01ac83e68bfe857dfb003579c94dbe89849d70a124392419f70b7bdee813b7abfa089abd04c0dc588e10d1d1509e28a4ed8defc106e64377b14ba0d80eba843e1cbbd103056eea756ea0621e6f0d70a474064e25a6723cb51025ca62c81d50884aafefd04e618c5ef6d09f26bffaf74ff836e6e794d967c6873d089e909604f79dd36d7a817c89ffdf409bb0e4707d4ae422d3debb353ae0c43be2db4da6a21ce9b7ae427d862a69403006ab56efafeb8d3e65b53051f4c54ca2457329dd28b413437d8b17ccb9403110528d26b7dc7effb2a4993b2f15ae70ca832c3eb33ad359a3bc1e0b6c7560d8e094664bd5253b4af7b1466a2c1e0cec13c239a1f53ab144eddcf995fa617ed770059650d8f8cfacdeaca2db35a36dfdd1ebe6d94eac46d058b9e57558aa3db8700eafaebc60bbd186db1cce3e80da76528ab5371d92e431e154a8b12f562c4d504e50bc1deab6b3e76e3dec8e9da53de9ed6878c534aaa1f6b8c45f262f0fea70dea9233fff37dbbc1741a2c0a7b69c612b33c0c9d007b4c5dfbf300c62398e30b67fa064fc91351c2575a745c3219d9939523d5736978edd99f1e4a06143da20c000ae07cffda080558bd10349f7dccfe9c6e990c73392835b8f2062683afdc03a39c32c3aa258db158070ac1ca314f49b2d6b78b3e55a0c224e45113fbd29e0925bad9ca9363ffa9387e5ce8cdcdaed50971a76ada01d37ed398dd145b30bf09adfa4bfca8727d7107518ad1150a2adc63da357555818cc1aad7616d79679405ec8ba510f0e017956975f255b5dbf02fa097b3d80d4a09a941ee21142bb8000f38c1bd28de54569f7570b7cbb23fa66ef0ec0c2efbee5f03f9ccbcfbe545650beaa8c275eaf4f94039b66050a0e897e3c1913939c3ae2cceb3cfbc84a36b1d0e46f70255f0948fa2886442b848e3cb3412f223b9e494d619e501197bb3ec3e024c369ca45e9e608115d4c6ba6718bf7aa65a1b0c833262e0d0fb9e2ded876609679445e3af6a78d235d197a5777a1588582fdc57dac306ade8c01636a308d9083a4a6a590bf6a2931bbdb2cac36136e0b2cefe984c9daa52d23b2a4ae7a6470f0e6ac3d22a0b86595e46c11f75e04f8865936cf4cb7ab6b37547f9db8aa7b90b437f507bc827999ee15eb262ccc9f1e3bf3f6a6a32685ac299682e2347f858047fd7f7bbb36dd8cf282ef3a1a1c17172aed70be562177794f2c472da97a058094ef06dc6e02b6e52b9cde259fd07bcb7133378153888ddbd712f480e986a73089fe96c3bd537b0d8f40aa46c73a749bb6f6801fe03106cab251bf46f4d82240e777a130f1e24d751acb9a424c6b87de3d74e99938c70c819a5f59ce6e1efe304bf46837cbfb94386396feed9ecbb9e666b186e280cc2d29eefb1d3560cf8fc06cf1501d6cc57c98b071a7f6d68fd34ffc2b7f7d7cb9fec9a870ebcce2a9e7100db9ae5d42025bd56cc05edd946130ca139c36d6b4b3832f0a1c9afcbe4ad490d0c718e91aef5642cf50267e15bb8bf84250ef0e0b70597aeae37d0ada48ebf08b20c42a561c11a9f12c6ee53ccf39213a2080589feef6a8c915cc412e730940ff84bd6fe8bbd6ba7e9b9c01ec4691d878e8aa0bc644a128bccb7b096c135d70e53f2195ae583bae176bd50bc6d77dfc4a6bf847567b53327c304fbf65b86be0232d0817e6aff92204d73593c087a4081391a245ee45e7956670f17f9fe199d0d8b0571bdfd378369f03befef3bdc9adfc17bbf8533df562faa9fae05c6b6dc0ee8eaa7293041dc74af88afd68e9de6b762515bcb9497a4dc2798eb79a240fe06eb970742ecca5a6c0248fc37ee2183b8fbdff723584803a75bed514a706ec30cc3b861aa0fcf4ce34a053fe9b00b0c7ad7c0f431c111b2353ab8406632ccab0d4d0edc1dce2eb721fbd25812519ac5eee09573d7f44da0011bc9596bc3ef6e0e7251a6a8d70145a8e552e6bc626204ac13158f7db201712b573a41c184bc110dc2dda8d045ad1befcaeb0889c6e31e753ada33f14117316c2c53630fb101ab052dfcf20b9bce5d9c1ff867006b1872b668f8ddfab79acaa59039ca49e2b57a05ad2bce2f2025db6c0b274e31b469921853fcd0d7297dd4b3d1734fc773dc8f01fb2f030b28aa28d7d6ed889206f88e9b9da629356f376cc4d6c439c6b19e940aeebd3764fb6b4f960cf450c70ece871f0e59ffca0f24c2adc5bba75316d09e02cfcb59fe97ac865f7ebd68995e6ed599f0a95a9fc7518cb6812c57862fcc20028330e2afd5125fe032f7d99d6d1861f57adf4144bffacb1259dafa9d7c36bc0afc821818901f11376a2c2a445428175958b0ccf01c047c9c9b7a4fbc79e4b707386d33b152991952b88e0aba8d8c2023eb80e9e3426d4a9141bffd6c49b8ab016945c5214aff4dfd1e61f37972c5b4699355ebafb352a7783f76bb362f9bd8051d7781f28dd66ab718fff1f0d9e98c0acd99390205358c1c46c512fef49baa08de61313889ec6a5c86602d89a57e5ef04978be500b64cee4be3ab5a15edf5b0b6f40ea99d2040ace4fedc6c6d12c4c4964fc9cbd9d8a1ff80e71cddfc1b8d508fa3f79c580e4453763a00241d4c7f237308662bf19ea4e163f9d2d3edf1bc507d828b5b1e4d3478fce6ad28e16c7f65ce10c9b5965bda46f6bc569c0966c300c2ab6811b9b0ff90688437ccaac74eabc26dcc625925f9c7c448745997d2dd304dcec64dc96891a77624c687a2f69d6f4c85dddc7275112673b5f81ba510d110daae274181b9fe9f7ee21febe9e91474622bea315efcb301653c7b27aec576b07f802fee8eba4594b079abe9d1fd41e39889a907556081cb01f67879ac1f7b102da0806b9d9e6052b751b581bbf751e3dce986318d06fbd05eb04f11dcc5ac302d448070e3113cb9882ecdf7fc06d195e4739fe4a976c8c9c2e11e340d80e900f8287fa7da8384204b8eba19c016cd6d0906c4640b5c2a6084038ad216fc414031f688e9ae5fe92ccaf246877d9dfc7de61c19575b6e314a78b30cc6cb9a31003b440e0fbeda14a847f3f3ad646b7e706fb84aa1892c3e90736b16222f2ca3d01f1f5639c827085b10a9966369ea0cc71fbcd37bf2c8c29618744b32d81cebc003a0a4433edfdaddabacfc9d1b99dc4eb3ed59eff831eb97b4d45355422193a0f7ec626dcd18646b021cddd99c4947392cd5ac179090953d8ebafc41868afc100e3a36782a1bb60e9584454d545f72ca85cef31c54f3fa08171ae8455396e2e04b85ad7cd9587de315332271445b376573f250f76862133905ad5c3ff23242c0ba912632b78826f1010bb5012b0256a20a7e744fbc3d3cc7bac897f8d6f7b2e01adcbb824cfc4500c5632576209c3be1feb3c19069b2c0a05a597ae52102d070de7983924aafc5665535680018c2cdfdb7d8cdd44004a9d44c08a2dd80c39fe0702db191b97488ce9c1dfc8f07e1b262b9aae8205ebd3f540abfc6c5fb60506053060f240643ebd5ceabf11bfcc343effc74a469230e654c17bf9eb8dd874df0eb6563aa52977e9594b78318f61c27dfd411a05f0c9d9e84d0e8a3cbada14670b2102c5f3ec02ff20bd3ed4294369aea7193f1a3f1e40f0f6fbf3c48ed4d2c80042078cb099360ef4717f43a68cdd13a2251a3cf6cd3c167af18566e4129f0d0714e52d20840d9a33bb06a012e38cc88d20e44fc5541b6027e13bc3f80880f5061b4ceda5e5717be2e370fb5a3867848ff8629085a4403d11b217f10f64394105e0a389617a8349d38a6608b3cd2a0a713d0611de39c3c6573fccf1d91111c80c3ba3bebc43dede9dff587663a99c5f3ed1fac828fe23c1b84b0bcc0893ac25079ea63a6a60d3f3701e4c61c0374fbc5bf90e8053a794b2117428b59fd6d31a05a8b57f2720974759d6b05f8394798738004ffe6c68f6cd7f7d917f422980fc0cad31c392f738577494f75eaffd21ce766403c3129519feb72821c6e179ffe501e64fe8a74d522462fb151c68f0d5c6895e178d5042a9bcf051de726e2e57260a0fa26b96d6221847c5b62551bfbfe78472e37a80d9cecaa461c8acdbf4482900bc622d0db6793f84ce7e82be6ac81f3721867dc8c0d02df09ba7924645e22907e922b33c1f853f22c9bdfd00633f3d523a21dce1ae3a7a6c78cfc5094f43680457c84962082ead95150b13ce4f4b60bc3adc754b051cf3ae389dd30896e2ea0b730c31cd20e59fb754200186be0228c5584000724e0302efc4846be71eb1e40b32ab2132c089ec85006b94e459d215409e281edce21c3cf9a7bb951ff7e8e50286502b65495510be2cc927af0ea5a8971e514e42fcdb3bbf4beffd9bb3510f0e72f33ac7937bb2040219abc368a2624bb8b94e9f0c47da35d30194875813790a3e983a3a63a65a56f8545fb20d68e9caddf9ab77895b3aef2942025b13f0fd07acdb310916f6e064e480ab7d002170e69ecc51b2cd0afc239d1a5f250c8a27085f1b588c9fadcf2e45197f8df6033fd3e0d17f921440c42a2719e1b4aac852028e629861d199d0fc320f2a3fe928f8f55a33f34ad79c501c84c5417c111acf02a0f2ffdda6c5b4629ffa973bb09d3301c057621d8df3e63bcdabc02eabef9f04bccea969096910072495beed63f21dc5ca88f54e36ec1e29f6944d623029130aa8e1ee0f8e6329431f2cbe708dd31da2c20e2e9cb7353cdce3cb49c0172c1b0902d113c763ed90c1a7bc32b10990eb02fffd7d02390a2c44a519bd8f3c4d520f77c106060eed1efbb67a5f4abfb5a1994e8df94f01db3e234e9798d030ba290f373a2bef4d08eb57a7c05aff2908e620fbc3d53f242f65976cb7d1162a0d990bcd425781be12588cb39dfb668d7975346ace91e83d3aea7f31fd0cce63b9940909598da86fc81e10b5626fa56bc6cce60778863d90f4deeac2a24e45172cb906dce8a5b1b5935da0f442821d6b741f05b321333b7a604d1a798ee0877fa7d60d3fb60e9c45a8dfe558a4f3b8d384121272fe03fd7a4cf54d4b48993711d4970d617b588ae3d25a3286444173812eb58a46b4e1d6afed8b352df5b2a4e9f0cd09964e754fcf742009dc0c21c4171cf6dd26ba7ba7bd909a4b5b4dfca434f06d0d701e5e4103c88693d3e7d587bd5383cb5bfa197fa71c09bddd84c057a8a86104ce339c0ecef0fe9cc4bf11640fb7fb26fd6b613119b8ccde0bd715020a9368014c59e70bb174d2968e608306364bcf62ccb9d432510362760c3b4cf66ef7100d1298713ede2e0914dca041947fbf1e7fc32725bf5eb14d2b46a13d9e285d4905ddec5259681e72776f7d7b1b0792ff1f0ed763b9a8eea699414a0cd281cfa30b37c4f0ce388daefdfebe0bb6eea2e0be2d83cc8ca608fd8b52fcf510fc32180ec63ae825770e567fd99bddf06a12a367ce2a1a22ac2dfb4f50605db7e3fbc306bf77b839637fb7e44ecf5ed2abeef01c6e5ea8084c4b81d5934dac7746ee2e063df5e7449a020ca7e112705531f1df01e8fe20bdce79d4ee2df10a02e6773d020749dbcdb1ef4d811e5cc35ac891e3eb519f9c2ddae05ae6c2343fa9d545e10edb7685043036c755087df6c4bf58c79b8aa42cd8aaf0d02b294537d6a9d18d0dab26a30d34aed4f8973fdf9476e259324778c396412b7f00210f9552c9060c05e6799cade45e8dcaade915cb94bf33c9e55c36fa801d6b185de5b39a72488b0f090c0b58e4c1c47e6fb261b96bb84fc880530ed0ef955bd3816688ae0f43fd0c7e3d4466555de5651b2cc2ccbae4bf05356f33985a2c25ba50ef26effdf5c5062c45515ef95585adc986c8d05db5ef90da6f5514a043cf82184015cd9a552f01228cd5608528bb5d857f54d68bbc31ed897a9ca5593eea4f9f064a49e811390b3a326de4eae572565388d8274951a1ad30877f1a6c7252f9ff6c304457331606e2e8220354cbbcd1c7a186c109b40bbf58ff9ade3f7188bcac8b6659c3bfd40597140fcc423b3d6e99ac2e42b2dc35996d0a71773f9cb41b78205464d7f0f703a4be137e07c3f1e2655996aa0bbcd51377c24ba4ae5601f4209a2c67959dd40ee18bba9053de08db26b7aa833c8f8595f2adc1ef0eb862d9047ccc10a2c4770c49dbdef4d82498bfa3b3c5ebe28becf1980cdc6a36d1e2e01fe7b6d116de9c0c39c60cfcf7a757628cfcc20209ad33bc9ac757a58313c8144313cb8e4d4b8c06281029c4204ee7d8b663553f34e1a7ee2ccc205d7c256cab04029302268fe606b43c3e4048d83b0bcb92ca1e4f433201eac6d06476d64de4d065694183b62b02f2a6c16d101dfded41caa01a2e2293244ec8fec1071cacf4e4957d7a6566e301eee19616ebd33438be03dd151e5aec5edb92894126e3a46ee3e7cf3447facf071da52f2bc8065438247a65779c3414449180cbae7aed41e5229e1381b710170b376da96837b2a696124557ca1075ff2ce4c8c268e99be38163b8efb30df3410953620f5ebe8d5aada5aed9e14ad0fc3d78b8a64c1aa69179c2914597ef13d709fe9630c19e911021c5a451e88da58d178d5e34cee9d6f87076a7c40e854ae80821018ee93d402af16e14a8a388018906414cafd08225fb2ac9ced7502d9e9d06b5ef80149c458863165c5370bcecdd8897be51540fbea13128b33f3d6e203f088ad3c3cea3644ebed89fc20e3cd7b4709092ce7d2550f3326730b3975f1cf90ac950353d6c31614d31d4e4f9a2fa0bb163044a90ecd21b353305f47fd4d4e50c87bcff4bb9e8335c6c028a8c7e3e42984930fad06d6732d3903290f5a4821f09d24fcce552e8b8598ae8808ca225598cf0957b5df1893cf7de6b292b9e69590143a12771d8f9be30a3987e67cb45f3ec13fb5b90af5ecd44b2e6e8943289f2041a651e9ea6e5034fbfde3455fb96cbb77813664cd96bb6a1dab46f5e4b4f250c50724d9ed0c5b6d1d071e0235043bce57e69f08941e0d675b34f18e1be9ea00489665a3baea1c1cb9cdbb7db81e5dfc84a60b9d44f3e0e5896fbdbf0d2c2030a6bf2d8f9e33941661fd95af754948ef6d59b80191d4bff68c63b27840293b000fe924497300169681dbf493169cc0516bdc56110754d83e662841203098bff09f818687a72edae05dd97836d50911131515341abb7bc287044a3f08036926b0859d4e9b613c26ef9b54f34399fccd598542c06b9d0eea56608cd98690b23e40314887bae984b092ca091cf041d19007d81d66a002e7d850e346e92869b3c59045eee598c744b23573c8e8f1895148694f16477ce8dae39c09a2fade75bfaa400b31c2c620aad3d20d05666e81972b9a748766af1ecb10b3694d9d48d64af9301a8042b9c7488aa0aa5c7a3139808537b3f8512be783c697160f12f565eab5e033c89ea4b6322c2e5c52f23a9d884a54dd8b940336ecb5a12a44ddcea88f0ac0d5220fb337f1daabb3188c8afebb2ee3e116f6c7d55ab7042a37924cd12e53006c00a26077820ae8598ebad5eaaaddcb27eafa7a81d6d6d1ffbec34a90cb3e600aa1c700ec8540d7ccea4e911ac3e9192144702168d5a14bea8f040e84eebca005b50af8e363b745d6ce552b4e330f6fa012146944ecc99f01d4409d2efe12f0fc29ca4ec93e3df585628d7815f4e37d50538b64003718cd7bd6f86a96501f403e46a71356af0c812a0b59b0e5029b71c281bf2fb658459f2a56f53c9d229100691359895620ce383c4498400582216c2cebd77f773e4e86b276bc99a9dc1f50ab54ecd69d2dc2aa29b2ed437c4f7069b5f8980fc7b9337451cc9d8c21fc27a07fb0fdf6902ae4e3b4a30790bb49ccb8214414a379d05915d724b614debf3d1030272dd319805b4516e74e39a1e677d4851717f2064db3d89dbb80b6cf8df840e87923204abcebeba60498171c9cb26acf4c80b10d2917f18260aa216c636bc0c53a3e3bfaf4feebbf839491b7be69fa80a519dc27bbbf56dc36f05539ed5320c48b6e93e0c25c11d9763b993a1ba604777f960372bc91ffb173a6d6ac1cd7504d5dbea46598fc8dc41b6615eaddb03fb962cb754ce6d7b4daeecd297907be00e01b76578aace30e7dc4739f5b0b621d497806cb6ba45c3a17aac37765625b80dfbf4f7a9b21d4f2a254f1ef3cf7ce6124819f1fed25c6fd964725ab4c1e059063e4e2954ef7c8f8f0a745cdbb95ea3853735c358ce86be9ba6f38563402f5e08d51ab2eea6e3a7b971c852d377ca94ece63d55d647f448518daeb0997f85f00c678d7d936621e288f492d513953994d306c894618556a17523b7497848beba0f0ab8818005affff49b4356c1cf3d43252a031b89d3fb57753ff73990397a4b0c9c20a2b72b122111075bca50912f403077800d9be2fdc4b9ad16f56bd0716c01 true +check_ring_signature fa782ec96e0c9f293de8b41c7149f9ff452f7539ffd54ddf654f483992485c81 5c794ce4977d5f62173be428e919307b9207d9c57580b519b109973f951868f4 24 a5b5f990b194f67d30727ba0fa5b386de6427dd6c24a2909a714625c84ea3348 2312bb97cc02b58ca0da0bf0615ace38d00b58326a38ea79511ad25d494f3370 0e9bbc34ace6390067fc47406aeefe9ddb815b5bbff14cbc0405748e3bc02f19 129fed0897d2e5a442150d9d45302bd8167c845b27344b98537a9c5f61684aa5 4cf2dcab972af57764bccf93b3795444a109d4ff2b943658bf39fb0bd78e9245 91435fe164aee4cdd66c364247251bbcb6f91165d15cd9e476a624c23e902264 34d22a216e87204942d43ebe602a6e4c914df079830985d15e47e8e2c555fa03 522465b351bffe0a2f8c1fe904b6226226a5599b428ab8ca8cf9e618cc00c5e3 6a380e754dee5c76099df1d33253ad7feafa844b78969f6f7eb62a5fb72bced0 94e406222291758414064c7fcb7b06b3d5d75409f2b54f51b5e2ccf5383168b5 10351fabbc0868f7720e1a1b99b8f048a6c7f41a4c28e747e7f7eafab8e615e7 6aa4b6f511b57bb8f58c270584a4f33510168f60a513512657f631b993339e00 fdd197fc4f8127fd69e7b8e67d8a2ef03a84479ba6d85e5b0a4e67816cf8c393 791d1bd6aa27875356642bd74cb800a99992b228b1a1ba9e2133008a3bf6197e e16eecf9440027e7af9ac3497112775885c6505692231aba04f6628c7cea7c71 74df000e0b421740b4e4ecf81ac796659b1228d2270a5d45bfbede47b1f65077 a4838e9f36b6564ce1d98d5024621781e4cc6a0c6b8945a54527a396fc708aef 46df9bc5894e1e3468479973bf90581e285dc326f73705f258b6a791f0159987 1cf084e18df5edb63ab3022daad7a2eb3e4d574af56ba0efa20981cbde8fe34e ce1800b4ecf764962ff7c513cfcd3e245cca6364f3b5654499273b062ba69d15 c1b044698e68bfe8d80b03cc214b09d986d3631d631ac65cc0bf230deb3a414b f03be46e4e2c69bbea894b11b3091db4c28003a3202ec2fae8f9d949a4d7c7d8 1001e5cbbdebc6febb7b96a155537ab344892a4614d0352db3962e01ce932f4d ca0b3749903033e063a2db7e05af11ae2bd9f06a3efedc272e803af5edf50f31 5a259f16ff0399072ee0666f02a64e8bc8676ca07ad1bee9538a775fed3d500c0dee2eb81eb8e11c716a4cd4d971118bec51d5079d1694cc0e5bda126b0a500304a2461d91681f317c69edbb140ad1fe7fe3d8ac88c79aaebb5fb19d9d059f0c7ff05a0bb8aee4728f0c6c766167e44e2f4aa03641b5149ede4fdfa58a5b330455cd71752f706d4aef8e2c5f77b87f7f8c563849e66944d4308f59f67b3a7208ac5dca9e912542430ae58b88098cb1e27e98be2787431e126ec3ca74997fb7049d602fb489c2badbb75c9be7cfa82b11e99b3cefd02b5a8a7b569d4f4550970b31fd925c3ed4cf8f91db7b399f28713d6d009d1fb2f4a32c6df5997f36b4b009244dda29ee06363be41df612b61df64bdbd07d663708c33a8c0065859c908f014063f958bf7dbf0be1eb75eb818814a1d2a8a30ccdb5de916c7a5bbc3ade300bc50aaf464467f84317f844227c2d81d39a0a467e6fdfd36595268ad55c697a03e35dd8234f1aaa163b1288c0fc1e9f1b0cffc8dcd026c96825f1e7a8e2c3820a7454da0d3727bd621ab8d9a766bd66267876aabfd1b4cf329c63ba5b4135810d8492dd038a1491e15dc02871ee0f498ca4ee05af785d9cdfff15cacbde31460bb7fab58ba5de18f004ebcf72d3faa300f188d2b233bfa1d6f06d482b7e71e008268d4f1f2257694736a00cee5dda6bab08dce786dcea207013110ac3dc013e001f9c40f325bf2bef76809aeacad60b202b955e1ecaec3792e5eca8f4f6023c0d4041865c67fa71a7f01d91710520e619de1a574385fc616e6dc61e800351f408d139c9803e719d436da23c7587878e865d2dd9a6c8079ea5f8dc8f62b4e8ea0303f6c5dff4091405ebb685c0ea08301839e2b3ce33f07748ff16cd0bb226eb0efbc4d8e157760e26e2c0399ff66ec9c6123a682531d809d34df58455557f4b07fee18cdfcc38f0f1378b6fc12d417cea2d5e657a121b3bde55e499748672cd0783478ac126a5690e9da2f1001da45e7f057ff0d1a93ca4bbdf58e788b66f9104d3b7610286849578898785a68232a3ac51f09f779d6480036776c8026a10f30e04a8cbaded3ac1e571bfcda292e188b3f417699d43b4234c4010f26f99ba0c01c308b8eb992a2b4f11b8cdcf709e400658fad37cb37d85baa9df96a1e92c4602d1a4eff6bb580fe1a19fba4bd2ebf9f5a4caba59109e013de8f445c0153558099ffe9f67bb3dc8b75dd8550342cc7a9e3f3bfdc5461d2f840e25bd75ceae950f119289845c97d54206b604625a254689330cdd393ed34c3c5a22aae6640ffb0f36531a4df1d14915de2721adc4061afe62c3b626bfe1a8b8fa51a22c10d44d07dafdaa707e209c8f6ac0ad3c7ad526bd0631b0a7329cdbdc6baaad7eb6810209318c2610a5647cdad0b14b621fb9305a84b29976a01d148096b9ddb4c715cf00baf826d7a7016d12ebc1c4f8781c3ecd065ce273220f6ee0cea71a16f2906e0a850246a4c86112ba5d471f2061f929c00289e554f0e551ec57ae665a4c48fa0b318436b856f953b0f970c885b95eb9f707ee1eaa6df370014e9bf728bebef6047691060a74bf44b3848a0ea520d1282883dc311bd3a7ed92b817197714275700126607bead69c046a93cbd419d0ea21469bb089722fad116494665b86662600eb7cf279d4d9a076aa996aa69a3c605514d1e0949e6d85d139895f5dac4735d0b3895f76cf9cee1254be7f8ae867856e1d49b3d63fdc10275ddc5ee9bc83bfe0b0a76b3c88794f06c7eb43915b4143b9eb83ac8d83e7abe040d277f2c877f6e02c3c33ea909d0aabee1c3304d9d935e016086df48a033b2753c87c3a35a1b400ce50676cd7fa550e5a4daabfc5eaea52061fcda00343664ed71644db543fd7e0bb57314d68eeff3b73c91f2ab7a5b2d78ee24f96181abd5e72d56050c291429086277b664c290eb65178d77dc4046a0f4a45eb322c3f3591ca413c88c0f422000e23fdb19741de33ba2236bfc176f239060450d819ed7f13bbe92a05215116c05dbcad80c03082e9258f3ef030b24935e8ea03c9546277c866c0ed7e6e6fc190e08c8d5c50efc7d3415039a08040a4983120b002a8ff01891ba316772a17d760b10d04dbe91df3d73f47d91953a109f94ad931400ae927118190ce6a829f0080a true +check_ring_signature e7e17384e96459e641905d30201fb3b2453f134e4b5962fadacb84e13427eec2 23601e4f0b147d4d66d03447a60f0c8c4e899978526668d24c31fd9cfb39bd20 8 f04eb5cbefdcbec90b3765a225d81728ebe330c4f881657d8d51a926cf1201be 04c08d457cd7f860ac8ca4567ab90b0f354a0240f3fc05bfa2d145992ad6e2a9 4408ebeb1dbc29e541b72563fdb3176faadfafb16b701e0fa8e29a3df2490e7d d24ca154404dc527f28c17a54262c70db844a414ae8bcc0a8e5ce745ae7833c6 317ec7d4d5559d3c6190bd5ac64a824f07a43bf1e0215397ea6a17c9cd30c94c f029ccc47b6258556c17223bbcf9620f6fb33322eee45c5e711b825a4bfe4184 45c34fc14aeb1a40ff35225cf574eaa3e36c38d34f7ee8b1c1b63831d0c4810a 54b70d8a5c6f32ac9fba025a5ee17b399b3886827df5c54529204b37006713b3 df463a510b61fe69f50f1f36aeca08647bbad21b80401cc9c754017d27582a0bc2ce2106769fdda65cbc520edf82fc0e7698ec842627175f893e8518424b330f4b33076e2f02ee35dc8534ed8d41446f1d3757d65d2c6119f7d59ac6a28c350cee4ac4f2a54e85e74913fd3810d3c94d18b0934904c9e661f41a4fe7da9a1904c5d4c41a78a575104548aab9ebbf63205f7f9b41846c1bf249809c19c592610b2341af04e4e2c4b88db4ee9a25cbeac8b23ae24524ee050b2f9f0768065bac0a4db1178bfe0f41cc40283aac13a248e9fb9015ea79b3039e38a3c9951a4b590f0ec84d64c7d24a89b24e46982732621d36f32948d15ea0e1875a6353b79ed1071a7c74d2ced2ee77ee4d470903b5a28a7a6eff312c16df3a1abc1483148d990826771e2fe34b52df66ddf20e3dcec3f6151452325617e4da059c7ded19ad59030e16d1717dceb174adaa2f3fffef6a6d171f8a763596f01fa5f3301c721400070f774d41a41b8bcd9ef201c75ffd96e6a22a3f768ea34e16d721adc5118ceb044c03bade6fd99ddb45a46b2f4469d92b3b5567f6acb1db9dae66fb3992850e0e66a4a9bd20446bca18d215ee6788e269dd8405c9b8ecedd4fa777883055f39046e4737bca71dc790e3d252d94fd08448e75465599ab247add0db91be94db7c040281fb284c992b753c17aac7412b4dd2847373a4b74787eae034a9cf6b78b70a true +check_ring_signature db0a64be2e8a3ee151b8ef249c80cadc61b154417411753a1dbc5666807b4432 35a3067faceb45bb7f7a75f5ac4a5ba491f13d3a44476c85b03ae86ca48cf05e 2 59e33ec5f598cd25427f3c3fd88bb10279fd8321c24cb66f16a77ea1ef968092 06a3e824e1c2006d3bb72eef9334066b2d20cb44b0ce921c20537d874188a36a bf33409988205643fffdb2b1011637b6e9b49b022fa5fca833e5800747f79c08a15c06ce0abdb24662e3cf9f1cb0e97f523a43469425d85331932f76dca33106cecb97efae1e159a162fdf423eeb8e24d13bdec24457093e662acea6890876000b95d20edcb59b5de92502516874d0379aef3a00e8d7c815ce76e7ac19703300 true +check_ring_signature edcb18d6e0e86e28869be6898a6d92759b78452e4f5b289cbf7a887960c9c8e7 b028bb120a79030aa4ddb220d4f0e85dfdfd00356ce9832a98b3048ec1a342cb 112 a87d11e222e61f3a2aaa9032f91d79c6ee250c2476e01b7934eadc4f3b8fa076 fcf704de8a4c3e9b997db1472427a1e1eeb00047bb22b671cb4d800fd11a1f16 71bacd5ede7416530ad135fc40264678ff1eb2a3c7101853e182431b15257e5e 6334f51f0426879f050daad35a9da0368f132bc860b4fdc1d3239e99141927dd 3b0777fde39ad49def5ae012f6a3a35c885d65468e19ce1f3d97998f6e4ad9c8 6f174e92053701622177289595f8d7b84e9c1ab89a8110cce03b8842f28119dc bda6d7a8c8b27dbaa655baf60262109e0a38eaa9905a573028a04eda6c905e1d 00af1fd098250619bacfa81f79ff9eb68f75abf857fd3ae3afe2d65ec229c457 825b8a56dcb5b20016bdf098e4d8413f9307e50392b366addcccb20b604d75d8 8e57f318892e87593e77e2fcc870c9f231836c603e712f750b0e75433ce5dac2 2dd4c5aa2f11b003ad5b72a128d5edafe7194ecacd6e68d43b80c8203450066e 4336ea4afb329bd4097989f1ecee829532482fe06be716eacec304540331c983 fa50f3961e44250cc2b99dce1b418a2360d29056ffab57e01ef70a1abf5f9921 14175313eea7dd90f1c60621f275893384693d2dcad438999dca0a472bfc32d8 0515cf376ae5a045742b391bee6b4d278c2f5ab404823071add99b3411875596 55eee6cfa40846d5e13e54ebe1c4d2e04230285b7bc7b6f2b2903e80a3f26d16 3fcd979d1efb688d7a775873bfc3f612472879cf9c5d0ef38d2beb1c5b8dbdde ea8ea5cbb84edc755b8f241b3221e2d7467c1c4ed16bfba2ba9ed52144bd4429 86b894d94483279f395db41aa787b7f9431fdc3541c93c648964f2dd5f5ce888 15191d915b896152ad1dbd15a8ccd38eb2c89756f25ff6b3a8bb754b3a68ac7c fdff1e9d95ed59aafea95155a49de609209d4e98f1e708cfe96f0cc743511455 97be273215bf954c1365edae5ed2a83a2e7bc39ffa62a8282e6df59e73a85d4e 5c480afabd506e815f770a2a565d8ba34f4ac53f642058efe180bc1c16e1600d 5292771453c1da2c0af8643039fb2a70f38618e88ad5b3bf5b462197c19df6bb 3fa3e3e2a51455cb84a59be4c6883fc6909af1dfd32e1883e627e7b83ac75da9 aa7165f26f1462c00eba024c1e06f857a4173c0180e16a0789419826acb505ec ab11238b6fb4bb0e5345d304ca97b4d4fcbe5069d33c1ddace045c3270d5138d ab6823885a3ea15cfc4f34c6487dd9253a0632bc92e3f0745747f8013ddd413a b369cb96b1190cdd30a6d4d8ecc19f3e6969682b0e1dec63b94e4ee24c80fd84 6ef0db8fd97c1692b4aab2079f2321fd659b0df32927dee0bdc0e9138449710a bb2aa58938208f5255d39b0ad60ec9567ada95f83bf021e79808edf0c0d69a72 1ea41c4a6fe3f9f42dd12c1e32fa2a265b30416a4479de0238f32af775c50f7e 8f72943fb9ce85e3d45d4d8c4d74fd99b0df249cf0718426b9fc5bf0b7712ef6 875b18cef49130ac77ca16fbec7a852c94daf3e381e21f0e62c580004667e33f 00deb0899f57d648d5cf9ca8ccab783fe91ae01f5acd830ac6ffe89257b3f244 a716f45544375156a27ecff54a16f055d932c63844c9255d9063797b7734264e 0ff1897921cd21988b675ca3bdd095b33bb8d9c3fd7935d66eb27702e1273175 b59d937f821b6a72ecf8e7e25986cf4c0524e14ef14ed1ca4395e9b9e99dc639 125be38584b4ed2dbe37c9aa39a78a95d9c2885c131a91c2983fc91c63031c68 a2956956c5f58e950172ea136168532d1dfc3b6b6c9f85187139cf440b3221d9 3480d0215e6c5a64516c93a97977f59628db8dcfb766a6b829110e9668335e9a 97e8d203e58221e892a282c7922ccb2d89f1a0154e1680ee6d6a169a736632f2 5b20981d91e9d9873bd5fa8c5efb101fc8fd6805e7c6067d1bd46ed0a5e3a5b4 344a94420544b95a18cbb3d354ae269c84f9bdb13fb74c6e998436145d981574 ce06c2beeb09bdb50b306d6cff3bcbcfc6c5e4d9d274fe2d942381c39264e451 af66684b592aca530a9c7bf25ea9da6c6bd422c0287f39ca65943a195f918e9d f798a850fe5d31cfbc1b6dddcfe92db09a5ce5ead8f47c9f43f970fd56971625 080564031b435446084035602a05045237d2a27860d9bda09d8499df71515ee7 616d980a4843f88e8adb40fae1baa5ad3c140410b192922ca872a03e084497ea 07e17134b543156dbc29fb4b99bb17587eafafd8d9e2f7f37f80764e93adccce b8d261bf6be7bc04a8e13701aa563973091cc826427e57a8be4b113238de0c0f 40d197b195de8b30a14d2d76f49fa34be05d489760c2c42b7d6efd9819c33fee 16a6bd67eb8db761575f7ac568c4265c837dd21af6f777e3242b0a3897ab6efa 54252da775dee528bcd56a81312aa2d1ae8fc5024832934a4a0014e9067b02c4 99fd9c4f621d53574072c73c0b867aa2507d6107c2d0e8f8a89a5bae002df9c0 8b73c3b3969eb51aea3566b554684565a7508e45be90b2b13f79c5fe665b2d41 05f2d408f3c908847450c06fd094ddea892e63d715867a1afa09ae7d16617a26 2df7d57ec9aba0791160f5e262f9647425bafcf4ac876400ff862951bdf5910d e6dfbddef6802a1207f0a0db224fbf6d88029d862e8ee52cf8782d8832417978 68aa89db91cf14918ad3d2cfc9e734dda4fb078c14a88b939fe1c7154d0c7d44 dcc1e2ca58738157911118dad593a23f9229682fde45d927b084f2be5e6acf7a af4335b947cbbd861ce6b6b5e29f59fbe9da4eb179a3396d9f00660e7edc56dc 8cd841c5022c458ddf1c612cbeda2e16306938d356ac5ff932632a9b99e76006 0ae5f82125534166564f1c0402afde98f5090c3d8fd81aeca1dd7109b39f11b7 aa999033c46be435ffb3631a9e2a3a45f33bf087e0b54acf6c26ceb65dd5a9b8 9fd51e79f68db2879e481b4d8a75df4efca5af33a70654447f32c4ad784ed1a6 4c543fc8f69fe0609ed24a8a8f230f673a524535a00df3b8a5143c9c584590df 25414cb1253ec8a295a42dfb0f8d6c7ec243e4d78846aa51b804e5975cd2a718 a30258abf3d8e00e473766eab1892c0f56ec17ecb5c416149089bb0891c2826b a8888fe8c58fd0f3b7c8c848592487cf22df5085a47acbf5c091dc1f1697448c 91cad71759591867d2f9d7eeeaa4e31e384aa2aa0f6d30886e0ea68d3b7bbf8c f04329fac67c20127a28cf9f8fb82e7a2d756b3de18db34a075ded9a965ddfb6 9b90e3c685b30d55da93b6d945ff2c74b98c8b5dd3f63f7e8198208c243910bd c565cda2898ec01aeec3b1e3018a9d5ec37cfa6e8145a12cfee0b6fbad715a23 a71d68294b006482f1ca038459a554740ae316b863a717ae6660032bd0bddde3 f527e6c01b5e9cd057d568b933506fc4773db2011a93d897aa0abea8e195296d 14e539866062e1067f9d1a12349431b3126fb65b2785c1f8d73181d8c82718a4 17b0511215c9176240efbf15f7408a1dd470dbe726e17b1582cf8078e592145a e880c5cf7d13157b7164a41d608c0f6c8a8f03bbde45683ba9df82203f0bb66c 472845ff614ac88001d837a7874dc082697b71514e709514e495c1095134f59e 9a7a47ea0f5a0722fdd9977e32ca399da5e3eaa10d1d6d756e3a82ace12976f2 fd4280fbad855834cfc307bda9619f661f43c119219ec8b1da0f35ded8cfeb31 4f74064fcb8c36d845bb024236682edd41a75cfefe050af2dfd5bdafec810e71 a295863bb1109cf63df652867fc6ec9b06058ff5094eb542ec30c57c011a8b2f 19e151a861fe7d5eb7840a634ebde84fcf01f14e9e129a010ecc9a5ac9aed4b3 281821686d25850af191d1a7eb633b4583e37ca5f40cf687a46189d674d2e196 acaac24d972801092a0a5c78c6f71c5af8c9784b40d9b3614866f0be4a6b4890 1f42cffdce2f8ec9a22cfe3f2e5942216efbd4312b33eb0a0bbc7990c4d53c00 28f1976f423c8c0e7bf698b839015ee03f7378caec7dd5db3eeaedcf4296d1e7 ec108ff22785289ea27ef103a91e967b86b0b22f9a047b82db1b3b0fc80bdab8 0595f85be9e761ca3b0cebb92466cf2deb2ecba7eb9b9d1305831bdfb7f8f50c 922cb5032237e7f1725797c06d127f93ee4cc23ff0087111e5c21abbd5479572 641290074dfde2fb3a11de3e734eb5143f4eb823cd3c5d5db288c7b3bcfd8e35 cf2f6f3ed644fef501bf23e72fcc6347bfe4493cc40e3ce96bd6c1949f001187 c475dadda806fd5f42cf3a79d4216f419e7cdeef62dcb0b2c2f8a53d0fea9ebf 80e65ca02669257eea871f92265924dc06d96a79423e63cd594ed4950b8098c0 b8ab446f95d0698a7ee9987ad4531e7ff92a01a2bc306adede29d78a4c0155f1 7e15fe3fd4c252f49b150e896eff8a1ca852780e2b7eaa6e329abf2b1b4d83e8 24684c2eb3d669fdeec3c5e7c473ddeda246448f0f056f8020afece161622552 1f45c5e40be64ebc09d7091d3cfc1910b9dd6893cc420f752f44478bbc320eb7 96803130eec4cc597b3c257abee07b1e91205f4058d59ec3b160c5a8a34470f6 9582001dda431f86284017ac9a96f0ab166a15bcadd325c1e6d48146c22d706c 3b64f4a32438ee88168df7d941d8c7f84f229aead45c14c935b080fdedad3ce7 a7b482340a22160ef2dbc1ae988ecc88462bad02c12578616a71a384fa0ad469 4a9c2b82513f11a1eb35dc396ef2b9ad52a5b066c7ab680a41a7728f852f2bed cfd8dce0bd4b1466d7e98b55c9c591b3c853bc6f7403f09d8713c5b068bbd65f c2bc34ad4f34fb289eccf5437d16d7776780190fec25a71d80d6d6e3df2a32d2 fb4877c2a1153587e08ec34219219df2b79dda446fdc53adfffea9e8d3542aa5 f5e8b3fae898b7ac5cf1ef5e6b21910cb7aa2004d7319fe6745cabfc8c7f89e6 6288d08e1f3de47846dd8a4e7ddfd8a750f064103e25d6d68835d4dc1bfc3337 1c215b40eef1fb15a5817f1aea9a576eb7cc63a5729ebf66d397cf75b512dfe8 0799632731c7f005ed7029ae1846cade999583a089c6b05f1a94ad4c5b266231 61c3cd67a76987f12193a7aea4ed5b018a79ec0ea3c3f82117a7e86dff8b1300553a41dd59e84b168c87ce8be565f2a7beb32b82a6dc1be9c51aa6ff7c3fc501e367cb7a52e2d1ccfe5f358b540dfe6601e5b1eca574c03316853ec50fc2a3037a093ca0966acb091e9d8fb393734b14797fe8d74c07c70b3c63c647c52c5b048cfd90a4632082c931dff5be94befb8c9c1a6048eca8ddbb881cdc1184e8ef04f76d06a7669c71682f979dab42a6437a8e183e2a5045e6a52f60c25af251be0e8c1a6926168c03dd63c6484c56c73ce6cab49051711fe5436811563dc3d4c00ee55b5aa371b97a8dc13fcb9196909eecddc7df2e4cf64246f41418c5f9685d073a81d104358202ee8c2dd8562884b3cb2080500aeaf14747a70c5d413884390b242bda3fffc170a86a3f479be80a1611f3872f0f807baa55ad64219d4c28040a683fd8fa48c4afd2c8a9eddb9e500fc2861fbb029b33e9bfaff72d97db974209525854c48803a1b44845a97daed2c8d1f5a82490bc4fb7d36595fbc25d3a3309421afca49a67966e4669a3cdbd28b5950d3f83376fc699a1146a2cb801fae803a4786d251f03955c2f949ce6271e97174b0f3ee4c30e00970dad3eed5397690d4d512da9839e1566a366392897205599023278571a2e87af1c8ebe0260d8be099b6e19e297407659197e89d26137af1f47ef8a235431e72fe8ec8abb678bdb01ef027341f8db34a697a95088fd4a01951d51a100b6d2bf024033f592a624f10aa186601ba06382eb41383eea14302ae6dbd34cd9a9a5dda9132c2296b04e1d02329b8698c099638d87f78a1d51b71143d82632c911dbfe89a51d534b9f7ecb0327b0eeb4ee088a25d57688a11f76271efb5b28c459b9bede4fdd6e0318f1b90fde27b1a2b0966239d437de06c49b9012dbe61883dbbc5f2feb8b43a9958a6c020a8c4e40451566368b126aa89180d9feb7aca7e6adaa4d8e79235a8f326d7d0d501ecba448d0ebeb2782e38958276c03885c2ee6af65bf077495de1de73b430bda8594acf6e7a8b2762891497592aac8cbfcfa5264119f14f0c7fae177fa7409937326cb83ca63620adcc59d21c4bfa90de036f11d23f81cdf7524ed27b09003356a848dcfbb4dc9ae189dd4d746ed561f510257f076e7dab3cb8535388fd802385357d25e0a8028d60832fadda59a287f637e97bb2aaa0bce32d45278debd0bd4c96a063db0f506c2f3a1d97fba9a71f77173cf5e95b75622a69c27fff48002e1622bc2d4fbab0df3e0913cfcdc30f17735b13490e64114f653ad1522634f045da8e3d504fda3a71d90d4be26df83212626a24ef6bf2f76a2b1d8f94322b809744d55dea10f4d8217d63df76a8f2d7377d543111d7c37807ae79707c0f72a0c9553a2b357722633f9fc449b8ad2f63eae5908cc3ba2bf08246e934552adda0481ff3d1abb5ad918e991653dfba8bad1687839d4debfbbc168ee5fba735940028bbe73437470b187514d06cd0f1ab070d5c1a633bfdfab63d55a6c58a180130e75a3d9ef65cdfe7964e279908535a61ff4b8c5429fe63a659c7c22d7da626200a760c835fb9b3c98a8eabdabfe7bc94e9f1da635fb468cdcb60e40ba47f8cf041e90db5e8b1ece2ad72d206be5978fcf16577b61ea87d5e809e1c8b607faf80e5bafbf621a9903ce3786c4e6deefa2c1a17a098dc0cd636ee2869e94bc7b2c08cc44b2e87e36e801652e3c828a7b0a1be47839739908613b0c8e7da3df48510f89decb1efffd86ccf7f0dbed19cf460ea6a9c4067ecf59d7355b900bc63c6f09631f6d764144c92dd9171fab7d649972fdc4e359d31ef50fecae58e69fdcef057b7694eefac9eb74d6375c7f426bb2c89c16de62c072ec1dc3d9cb1bffccbe0943e58f1968a09e3cc9f26d870edded184a08fe72b34850deb773ada952d1be030bb38650096fbdabe573c4d785497c5a90231ec30eca65c31c731b819701c20746ab10b6c821936f3e749b0f0b3287a656b5cd866d23b0e24185695eb372940655b1b1991137d1b5ed8b0c3759321a317f92e5697c9bd0a69091ad151fee78095352b86dfce8d384033eada739d775283955b06521dce8b0005b545178799300be8fe92a20cbbcd0119fd535fb179c11d95cde137375de4884eb6881774ce0078adb2b446135494a31da3723841b2ec71c1f6b865dfa6de9c6ad55b9b6a8b0043db2fe63a189222ed74c37966d7acb3052a8bc3df44853e59d765ab9e66b58057c001163d5b8a162dc07c2c56cde7c85f6ec77f57d49f4501ba59bd44efb940bac77e646b0e0be45971552fd2901d1200d64a70f93bf8a155670ddb9cc6f4a0c41ff3c483b017ecc8eda53d67808f8daefc72c96b6b4302244b1d1806143c30b4afd274afa54e208ccca03a7b5f43160203880e805de6909ec10d52f0da5c5025629e05262a2660eda77c10cb37fd2f152e7420df079de3475d9a1ec07667e03ad23277234535e7de315b3930c00b64ea6b2564ca2b0d147262d1624d4de4900bd59548f52ea286092001e1a10a680ad7e435cb3585d21ff433724c67055d600a6df08e3e3b4c245e09caccb452c89af67aad19cf7c0716f8a1226282574f0004ac50b6d8d9da24c810c1b576932fe65a7487228c5250b1b3730a30cecb4470d69b323c0d1fc573e41bdeb4e9e609f0eb822758f42c4b49853af21c88651f30211eb98a28e23f11b4b4e042b15fe1b7371a92bc0983952296013dfdeb9e3fb0984d9717b5a0a9328782c72410805443bfb22d7005a9899a9be892987064a9a095b2445e7bc498152de8a3cccc4848138b13528c284e40eed8f38262077c00a08d1a078c7c019777a73cbec89b74aa4cc6dd4fab445b77891a1f0904717163d0c3dcc023091e372507b0f4432050a7c32b11f04fc86ca125e6cd1f09d6a6f3f09b32df8d1ab0d692eceebcdb356867682eb8a00d40cf07e83ec6734108304a905436a6c412bf021f88c91fbd8d32b8d264e1e7f5d6aeb20d70869782d3b8cb10fe96e7bb11ea472773963713ebef40ea25f8c5b1442b77260279e16fa21f08c0a33d2938821ed6e8b2efb2f2c654d2bc4a859a48e3d5d923d25c8dba30a01280a2dbaee77f70793734e2037e84b828c02471c0ff866acb19ac7fd45a5f684a000afd20146f6a17d9855042962d9f657b498e3f12f780b219f479138f7141dd10bc406598c272d0f88a217fee514d588c2c3055fb657f82909f0ff8769363e9b0387bb845eba549b130202d069c5d754e16e3dfeda1a8fc001d52f9f9005eb3a030a5569f426bd651dc4286c367976e906f0ca21f4a01482d539992ab53402af0f351b86b7de2d65bd54873848c48ff36d7d7e5c43f3805c49b1de4dbf731443045512fcc16f59860d219f43bd7bf735b263e1e8f244a6f3a3af13f6ca0838ab0e196f53a2469486ec0f774ee4ddd7b5c45b9666af94754b1d6901ec88e8d543076ed37e8b11611a70cd3100b91097f7e07ef6c459f3f6a679a702475d199a3c08b276903425553a76e05cffba06d3e96d43a393d331640d8e47ea3fa5ca732a0c62a40c59820d7b83e5430f8e12cd8c4c9f359e3c66991d39e971e4cce0210f0aa6303dc20d8863920aa3af3fb2384a0a92479d6f6969c832ba83fa81b8c72906c69fdf37de9c6f33313c6d80ec43c169143d5a761191751c99a79a540ca0f20ac30850d9ed1b3fba2c2f8d6fdf8eb2a0dc5389350ca19a2966aa20d26c20d50026809cfe0700c3d5282669f368372d0b42e23a372b379d6d541a03718fb8ab0ae0eadf08d688bafa5a75553ab4f2a630fdd8c6ce79789724f3a0807e4bc4e5072885a5f67a7e5c0bbfb2f4a304e7afb95c1cd22cb2f03effaff58a420fcb8109a5f602fe3604c2ca6af2f44c3d9a2f55ed3434573f9a003553e833f3ef7fc3089c3f7e8463f48d0517e0c1b5831b28cb9e813f780c055bb7f760cd87e951370ed76a7b88ecf28c52b0c403f2c537c4b2c9bb57810c0ea17de945c96dfa6ca1088fa24cc8a2e8112435e151b75ddffd46b31488cac1f192df6a4ec61939db1b069f1ea94468183779f9e6ee88df965e234312803abf9a9ac080cd22bea717f10020a0d776c3f94c3f9b8c18a453ed0cd3497e3ce8f6e1b0012ba9389111a67c02543adc80cd2c7dc6ef68af09539d2298d629f0e610d511bc150bc5b33d5ac60b0355c67c6f70ee9c9c154750cfb2c0d2e52bdd4682e3fb4f86795328c756dc07154dab5114e4e087666461043a46a93ed6a9b1aa83792e888a955d931112510a83fa3531396fe0352d50f800efc3ae0c0644f0846ddebe238ed408c665050b0ed4c5ea4be1caf3ce0466ebbe5b8999081ad03f559f7a2ad665f9e52924b26207552f46fda4dea69326058d3c6bfa66a59ba79b252f16aff927cd4d2d422e650e92934eb43b61174db5a2a53be4bcfe8b9fb69d9adbe85a99acb05c22ca1e9a0ad4051a245551608762d531c4043d2b368093de97722eaad6576aeabc8bd1d601ca1e47c594ec1c405ced784c3f95b1a2aec8adde04e6233f4048fd651336730b05210086ce0215401a9a9c6685434181776c81c231a9e0add21d365088aa1400426a5989e4374bc940f198ae2a997e2eb2e095b4ab53de8a392b81a5b1727b070510ee428819fc5437d7b21f66df7527b5707f3796ac1dea430b8d89024ccf0e019838a4e47dad3925603ab6b188e7d123ba5a357decf848bdeefbaddd96e608842fa327cc75be1f54e205eca39df3e79d203f285f01dd5c16ceab389fcf9f094d72ca92ab9e4ad5cfb4864618d2ca6c29ad2a311726a9b3056fa7888cf58f0401c223e28ab5cdb7b843c7b125d4b0dea655eaaf99a8f606bf2167c333693f04552805930e64bf25a43443c40008235a5db8def6de23723ed03d3c3c5c5e18029e641930c6d2350531b8f74c66f13dc8bb13c54f34f1c09a253c6cec7296b30f1959bab1becf97d50c592cb30177101bb79fb891491abeff26a8e499796bf50f76d671f3753328497a8c4d841a6c25cf0efe721bdb9579fc6552b7463575c40b71385cb962a821a35c001ae461a1399d1da12bc5907c992df6a59667cac90501cf8ac05c2a83f0ba90000b922ced2e0b4fcbf5f58f2711f4c6fbcf0eaefc2508aeb31f074cbfd766a77ee68b12f6659715d0aafa6b4640482c8eaf7d5f6cc608b40d3f98fc76b9efd26a8c0de1d469bcf1ef384cfe98d4ffd1770a0274d68705b1e2ab4f2799393b43d55cd1d9602000cc2b66167439830bd9f8e8a36149710084f2ccf40f858bc48d2370acc0182e8e221b523ffcb7d3501b573cf4cacd980723887e8a5a08e03de8982e777176cfcb4838a40597748b3d52a72e957144ec0e633b1ac3be67952719d469afb1920d4899706ef13f3f2285310f0534816fa30f8e699d12722a4314b1cef6a82341e3c468856fe1f5f32f7bc7ce3de941dcd80a92552f09cdaebe02e3daafc7cfb8c1be8f4e8dfcc44852975adf7132e161400911d39e52ec0c8f582adf361db03bfdc2a58a4f0ccf8f4f2cbd3961acf8aee1076437217b6a60d4d1acd981071c26aa2b1cff862bdb4974aa23b7d41c8824cd02db25ebb6623c7bc6d6c1ced5f8a9a1285a424f92d014d15e8f6d1edcc8802d0a1c51bbe797cb02308dee86d514f5885562de064185f9c857c874c8ff20c04e0e6353320a1e049e9ed00b6cc7270da862d42c3d98538ba33dde1bf2382a014b09d07c4faffb31a978a021c1949ff6ce5bc533fec625feba23ce3fbf97c4745c07c85959772688e26ee97efd8a7dbcb0ee9dfc119208c4b50e8b08b9838f94d20ee93f6c140b8dc6b6d99b217e3e513fa5224f7f3b3e05ded809f36b5f0ece47070cf5e8327fa8a2572a259f042587ffc2d1ddd6f8adaa1188129a8aef4cacd705dc6373152e344e81ad07b0f0dd4e33248eef68a0a6aca9695eb2d1e95277ed0a9884510eb3a13e02b7ef6f8abad37a9b8947772a69a7300852a3de369160cf0d28757d74cdcd5a2babf6c156a44339ac6a13fee4096b6af32df77b65bb229b06a49c46b15a9f44ea7869bb987f82702ff495b69fe3747f57a76456fb35870c05b83f041c5c6e463865ba81e10ddf7bcf4a0baa45e8dbd8ace88056f773a37b09607ec9ae341747baf070d79d67fd37a1a61c3c7c8e8b4e428b1502e6ea3c1102292622e0e2436b9368e29105b4e1cdb2b30aefc85fca95ad7154f623f210ec084ac3a81e5acde02ff33a716e54a171f22288d17295c0544373ec5eb053fac902a3ca71d87df897778d3404dab1e5a3a7c602896b71a05196b2d27b748585cc0f859b21f517cdaf84bee09d02f2f6fc7f874837c195f670429bff6ccc1806ee0a30bc8a6c87b92c54ea68b70b7a6a05379df7ddb7b7b510f664421e304df4b8060f9a3913a22c9a4b7ce10cb5be69a2ff453f4e3253820833e917dcc68cd59002a3b9fc4cc77f0cf70a69aca1e2b924c38b3eefcb4ecfe2cce533815fcaad4d046ee7eba24c5e13a2e7311b84a98d889b424170a1178bc6edb44861746cf9460724dfcbf9f45c3157b950cb80bdcc43b585d20129b26a8648423e3344a12e790e389535d2c5f30ab28736239f43ff28206478c6eb88a131e76563b7f27491280b6222804878e454ae8eb6d05ebf7829551cb671e3f947d79601ec6d320e974f0e33d865763039eb88f79d42cfab1c934a736e51c5066505663eeb4d0eddbc0f05c3cbce940e66a80b0dcf0c83402ed5aa6448fc9610d131d5403a7b84dcf66a032ecbbbdf2bee97aa9847f8a1fc92be369ad3a1d65334a7cb314931fb5df5530e983b1da10d6ccc7fc51d988d39bb714792c5f0a6c5d5418ec510debdafd6c304fd6371061aacf764f62e04ea65667507e505ec239b8439cdc0736cd6e945330032c54a222f13f85ee073867fa9429a1bd6b3fb5eee52e39765e39f5d360c280d0dd78a707a1fbeccaadb5c7fd41a0c01b7a9a4721a1592038913916747655502fa8e7fac4f86b2ca31151998a8a35a541a3cf7452861dca7fd9732d26fa4fa0f8ade5cc2c66d57a952c3b7dc7fa2d804152b30f19560732c05ffe9eb3a64c5079706912230f9f57c39fd318e9dbb46e60f1636ef54419fe92fc6296776e3550cf0ddad0218c829ddeec230a41ecccba1cb921d57cca0e907ba75eb273e10d8062f53291945ae5aa62beeb2c605ca2d62967d65bea959cb0651ec9cd7d34ec8059871a09d4f8e84756bf4a198e9aa604c7af74a79e06c4402d20fc815d20a7b019c5a2d50144be9aabca71dfe8ea3434eecf711860c7334e02bc4dd838b1cb9088a1ee34478c8794b89aeb7d742f2c7468839891443fd9c8d38405af49da74408ab93ff6f62135d4be8f3a309cf79ac32f6e83276dfbd12b4fd515989be927d042bfc87e2f20ed7b5cff4a98e9d86ce3562b2a8d3ddbd796a2758114bf668c906b647ae6e018716af5bd0884e263be26a40f6f1bb459b39b010f739d82cfc220116ebe99baa2b6d20fa18dea4f26310d91d1037e0868e26ac02b97ad6e77d6d0b4554b6367b9b2c66a1e1fb18966fdb986a4ff0706386c2bf2d06ac72e7e2960ad9a0121216437c0908897437887978d5e41724c5c84a6f69cd5ff152de67970858e38d28a10896a8163698b4388a7a9e2469d819fb612a21291cd750952936029d4f7249ea1c7567c2288deb1e491bd8bcad46d1d76c3213786f08a3181ad50123ebf21f18aa6136e3242bbc7331d0f58461c45cad3fc9420682292fc2e1d10d024d57498dc5ca1614090fc067037205f892ebb965be687abef95badd6b6810f4ff5ad1a12edd134226fe2ab388e943837503d5e3c538a6c529676a7e0134604f2a4ba4d0b648f4d76c3f740ba5110ac7f1772b4e544d96bd5c262862efb950cd9d19335d847a75731bf8d465cde7a071334e46543146d6a149ac81b2984a5004ff91fcb6604ccb6a3d2c06f515bbcb52abe91063f43627dcd704b64f1150105ce5d44efbf00523e672994d3317616d39dc180486aca5dc3432fd2630df2cd09f3f23eb825bf94711fae3fb485489015f499180e5e42ac5835e1088f7ac44a0ac90b20bb05a03b2f5dfa1d7488f6adc781a78c0cb0290fb6cc54f073dafbea0bfbddc3bfcc0f7a2b76d7bec7b44ebedca4bb1ed4d730bcc3c1e8c9a2b8fe13015f243c979996b2cea7bc117b0e05694af5884bdefd8e0b4d39ca9dcf7e3bbd0a995746c19e8e6dd70e2264020a40a02e3a0b9c9d3ac8131c554ace404a3fdf044782aec796a4fe13af320d10a95df0d93c7ede3208fe89567e681b413b347c025fbdb7780a06dfed6d7ffd4a32178826cc13b7769d7c70665b76749ba831370397c3247678f47daf581a2288f49b3d66f0e5ff51bd6be400fe92cb2bffff870a00692e7a24ad0ccd12e40cf41f5fe38f1dce618c982c5c8c302a13cc66defc0a0e5a6c675f590b462e1b9ad5f9b7b35a45f6872b39741087de0ecf25f947ee0911e054b10cdac9f239b5b6245993e8d7aa7e27336212621207331889bd84b409f095bd3b6fad1a54b14f09b6b142389c42d19beb8bfbab50b7df337a1ba5d109eba060fa756bd2032eeee828a28032308e39d2a6c36ed32a7a5ccbd361b527081fbc5f1952854aa0b50395e1029eb2f95def9d67843aae529d1f6cc4f944e1053db4687345ffdc2e7445d434ff52e4ed5f3f937198f7d6906d140be52b23f40cdcaadab239308bae3777b5ce7618672799072ad32a79c559e029a9904255980f8f216dbe85db7dc0287f485def0db33167220531195558f75408653bd9a28e020d411b175b5262d61170aeef95cf61933dc6117514d4cb359c65a841ea17660f054404df17841a429a82208b39a228ee63df4c2ab481de8a5f9b22dd877d670d7605fd77ecbfbb0aea3ab9ccb665f899a939b25f0752fe6996c77767ed717005784848957fe7a43671a2bcd6837da2327bbe19b73022d1648adbcf542538050597d4e5bfb7a50138ad0687f429ed889fcc66bf45b44dfdbcffdddd4bc1c847061b9453bc3fc8b497e81beec9d707a804a399e07fa9d60eb12da09da20cbe9a059ceb3268afa97844d2421b812057778c43aec16d8c3ced5082b52de4dc025402309e020193d106e1bd3d6588adb0d53d08a7df1f08b4baa7a73132d2728d0f0dbc84bc7945b468f6ad93382bb19dfa2458afd5d1d20970cd0596fd06fbd936028f2fab1a93f6da106a1603770d5b856c47be17b1cc46ab7ab762bc26e01fe109581d7172ebf279e50d70f3769d7d6d3cc199889156d32d2eba037bdb55ae5a0e56ad92c80588e2ada19e036442620e2434fdf2c693a7f89accf2895dc44c6b06cbdd437783d6094a04ba8853080e3b489c5c546023d8d2e87135021c73911a0a408f395cf897cd263c4937acb061c9b11ec000b9c42a304cfcde563df082b3086e1bffd4d0799ab11d4739025b77c4d06a9bcb724732f87d5f344bc5a2671f045564cca89c4899d2261531b8c1fc1b7eb7aebe342aeac3080b71013c70b2360085c51d7d6ff1adc7e0219ceac166cd4abdd57d3bd92527d7185cfa5e53b11e06cee473c491fe948c4bb9b6bb28dd9599a1e8bd4b901d87cf3d9e159bbb08cd09cec94309ee499122bdb99ceda2bb9d6d798146c448064f2c2c4e6e04ee62730689efce6d56826974f0689574bead8a0fd1103f8d5c3f394c3cea2bb9f9e6d90143ded8120aa5ceb55259f827d7eb989dc3e8947d4c9afe40a2a8f05a2abc5a0a9186a3b1744107b1dca185ad897c2edae922890993c13c0481a0d0676cd4aa0279cb2079b4afa9e9485ab31edc4387995be5343627b9b41ab71f848feeeca308a8636cb46bb68d4bc93d320d300a449baf0229c1fe83f0c90ab6434906c9700dd7d41a04ab2c8d00200311e19e7137d373e0cc82e10d048e10364ab5d51f4101f358503b7adab19d66b43db11933784e52b007f771523c5915827e6456a0a50901d74dbbf7b8f77133eede2d1620d9260d518792622f4a546007fafa562c0208b6413aa3a892e99c4b8eb0d9fe63a5a5ccbf6d394a4f2d3333f364cabd88d109a26ab6cc4f72cecb32c5c46715df38f8ecd688b11fcde42dd797e567227d740d false +check_ring_signature 2d77d972112038fe57643895f3f1d6d3e664cb91f2c20dd98958f95ede04273e 102608e2f7d045ac5fb0e3bc0afd9cad4d3071c7c26721760cd36e7f60ea2985 2 8bacfc219f37b90351093d1561c2c8b39174f3193aaa552eb5059eb2d331a816 712bfaa88ffbaae3565c70b54977e71653d9f8ae9f61fba2f6d05c5758fc60c2 327eac12cac85ab04a3d2dd33b5fedf4fb8bb623a01e5e002d8d455a3a28078b2bd80f8a74ab8ff0e94ee011ae9939f54a4fef18240bc3ec05de4f34c3cb7e4bcffef767cf7f5b59f3475fbe978277dbfb153837f5b46fcca5017af6ccea3205b49d3e56bbe668613b98aeb67dae97d32834aa9fe612ab1f83f1b79e12dc9403 false +check_ring_signature c6cb1ace03e3a155bcb84f99a24974e019790c9c829c2bc5ec82130564ad98b0 3f0c6e7845056329f17de4163a4085431ae0a9fc986725de4950eae9f2cbe96c 4 3473175e019ff9f905ba89a9a4718b34e1d47144fe0d59d316aab71c5663c8b3 50072153c9daf5d2a0fdb90af8fe2b9c2c6cac602ed80f5ea3a072c03f09c589 35ea8c73f82f6a8c87c2977eeebb518c3ace6a2dcd3c3a7c58d239405adc84e5 1f403a1496c211af976a06fd53aa53821e0070064385ee7be48795f4cdef1417 5ec4e8725716b4158159ae8636140506e4230e8b9994e7def503719c9416460001e2d5d7228c2ad6095d4e1fe9fec89f4c45c56d6ec44d22d46385ebe555e207f231e7e4cb46b93ee055a5b7cfaa8137d30f31a42b472bdbb961954d51d6ec09fe4a44ddc802565cf2fcf1db91461de583206fe2f92a945d19a61a9c356c2b04ac5de60b8c887b98a638bf8ded83deeeaee24889095ed8fe605ec71983d0aab26c4d904142f9199caac9f8fc578900ab6ecf3dc2812c222fadeedc3672bf9d144e8fec249e764115b37c3df80398140766df2772b60432d4904aa8feccfa710cd93cd9c4b6658cc7e6e78b9b8430e253ce5feae699d4ba74c791ffd5721daaa1 false +check_ring_signature 66b4e5217160de1a5f428f24b92aa2828ae312c50176ea4fbb534bd617d28d4c 532a72b7a35d11baa0d9c056acf9ab5423e01d8a142a2380094a7b6d9f7c591d 1 82c1335ff65202595d06fb01f90e70e2ecbf89511d59df6d08963d4e388e6114 bcd24c032728895b08f31b9411627e375c4bf545d148c9320d3f19d6b423e203489ff38d14764e3cb89bb8922b2c4dbde1557da6af6e65bccbd15b31ab53e303 false +check_ring_signature a390b2dce0ee90a9f09e975c6a5e07a286b091f199dc0a8dff11ed0e310cdb41 4676e21cf0c45ef092954a017154e1d959946f9c337efae13d814edd62ccb3bc 2 f1802f517b1272f77ab2c5e210a527c66a3683ad9d10d026cc0394ff448005e6 bc1c8b1dbee8272235a8a17a4785734f97a936d4cc3e945eaa7cbb3659171702 51e629e64e4384955c206b71d421ba657c12ef5e76e111c77132eb01738afa030664f9168f39de4da6fc8687e39175c33c32a089b2284fd94a2ed3d2b282000eb5d8d98c2008b8bac4a782b5a7c9b258a6f0aa0c6bb331efe9ea82d1f104690adcab23762b09167df2dc85def70120f6fa03c8b0404f1296ffa09e95725fb20a false +check_ring_signature 69f08ecdbba55e31cf66c48d7680526d96d07394836e81a5f0588c6d2d16590b b0e35966a037e6e2aa5ffdec72250e03d31bb308f3d8254c9f440ce86106a4a3 1 d471204d2a18dfe7a09aea695afa7a7ed331055989a6c7a146fb3fb64e70c610 e9fef37e2fc0384a2ed95241018d94ae574d17288a7013d370a1471c4648610f9685e90cc14f9702b84eace102d77c1cc7f076672f8c21f9a24c575a38ca1b0b false +check_ring_signature 54d8c91fab8cfa426b82de811667b5a591695c6dd0fb119b4cee863f0ba1372e 54433a0fe539404805e56b08e453f1fd182bb167b15f004b1136fbf021914923 58 6732effae0bef3f2cccc77f087245979cbd9367337a013d10af817079cdcee5a 8d4a591c443a68785b41f43107f6cf39fc6c80ae41b3d2ef3a9460bbda8405a8 0eee46e06e6da4d0eb8404e80247ba5897e57bf7bb98efdb1f401565eac0a7f8 fdf3a917ed668a045ad37ba7b2d813601d0ce4ae129104e8d209e09cbe5d4d0d bc153dc546d6013a2a64bf8af326022419e763af611a12ed6b539f85a483aad5 fe8a46d2d07dee75337a02ccd0b8c56a09733a7664086ffb800c9851344a5dcb 30356ba324284f5ceffa93df1f33cb0bf3c863391905ce4234ba778e741e72ab c36416c1c427f5b93ab511e72b596b5cbf3b8cea6242c4c5441467db8ee73f7b e700e69ac8422bdcc2e8f13a86a8140b83e962d198852609600b670e5badaa68 e4e689ff251e33769fc68761bfdd1f7945bda2c2e9fe932f01f813197ad0b3ac 51fc399a5a3a5b409ac064bc0206b5846388aad6e775a56874e1bcd2cea9d0b8 abd9ddd4306a69909a960a566a9ec6ccf76369e636606fa3268cc22871395486 0b6e1709786b6d508c702b06152945a7cc2cd365e0a1462af34e63d540fe11c2 4a6efc0714a023ec0162a758f7cfa65ebc28d2dea8edb09edf3ce74aed7f24d2 176579f33be2e9fa27c3baa9e9f03d47e728b777e23d2d6dff85d8d7bd9f367f ccecbdfb2c8a5bcab64bf1a0471bbf555b021e12fa4e08cb5aed988f02dd81bc 7860e9c13a7730f2b27946ebe645654b5ada6d9810910c7b08e03e9bb91c519e 161747bd2b9edb29792fab8a18f1bf127e53dac10fdadf3753228d80921518cf d5e9d857dcd428cfc95368708eae440de97f8091dd5b2b5dc889b6726f41f50c b4cbd41be30e835d4e861ec788b3ee8dcebcc2530637f951b9064471596e90c8 fd7957a5e81373a74e9b933abf20c294f50ae5a54a5fd23b4055b3ba02d7a680 237acf946e46087bdcc25bdbb6242d5c6738030aa5fcdffc115d8bb9aac9182b 2ccd1573f4ef37ed7236e98ecfd1dfca7a0ba1ee705625c4f84768b51ff10b00 b572123d6365bf81f9a3fcf43c445cc5644aece12fa8d0de9b11e531ece7145a bba8cffac6404d98f17db4bd1cdb46229f83650a0ed78a86116dee377ef6849f 81c1b2b18a2dc3b3af8c0073f804b370249493cbe9eb7b62928ece8ba8540ac5 c10cbb9d845940de3b675a31ab2405b598f27ec7a0d10b245f81d540381aa7f7 6d3d5a5f711fe59dfdd99dd29f96a6c247a8e961f70200e8d455d85da264d33c 37e38b3c00a953aac009660c5209280314b1bbe2ef8c9e4c54dd916ab128e8a1 9e1ab2ddbdefa8c2824ec58f54732a09a2a31d028c550127e669ef5cd61af6e7 11bf19642e4b6bdcea4de0f530c45638faaabfa7bb7ce1343325d40f059a85ce 6acf3f176cd6b2bcc87ffb3fcafa857dac82a094c74b5dcaa2f72ddd3a122ed4 20595b07dd7ce501bc09ce67f562b50a98938ebdd3eedc1c40124ab3e2ff0c41 468af246987933e9e136cd4282acda18b3ba68b2d6ff72b39ed61989df669e0e 2e27fe22c4412ee39fae6b82b6aa1d9e0be4d4d2252175db7d7128da6aa25db1 a42ced8209cd427f754d701a84d80494f0d1303d23db1436787341bf960f9f85 45630f7709e64d540e65e3d5b3d5c8817aceb7bc5124595f9b2ab83d0ca0c306 9215d6bd019402f35db4c25f170becbc1e7714a646d1399fb2c6cb8758205d7e 12f2c069ca74356ba99514cb11efdfa288dccc88a052189d6578d7cc77d0ccec 61a8643c123903b9ba31424e896811a6314772f8152e05daab5b7f203b6128c7 c4da19ca067fb5a62cd1d48ce113cde74cc28563e91bf6220ce420fdef808147 c9707eee65f3d9bcbf457470edafff4b81aeb7c61110d8a940c7afeacf20c0ef a0144d3c2956eb643fcedd580b3ab2014f85cbc694564ce1987f38c23165a4f1 07779aa27027475abd0fbd491a4fde5f176cd4f96f4ac8cd94a47c8d278aa51b 0582f6c41f022708d54f5dc1f33c13a59c6a9539b5fb80a21c1ad728479787ba 01fc8bb5ff56cdb71ed6cefb8b66b96997b11d7754d28faeb19874103c14d754 1df5b62e41cff1a4cc285bd31988d8096711bf2ecb6ffe2bad0b0568c5f59185 6c612dc4cda6424e15c667b516c1c427f060fcc00ba8a3292ac75702929b2460 a3c66d9a6ee67d490270d9e2f917c6892e80c73fd0d938ae9e0161fff8666c50 d8cc6daa8204fbb329e8a55f54ced181a39d07b7bf91805ab3598b5f074042ed c09707dc94ddc0818736b97fbe2d089d3f1c0f9c2945c2e30c4922ef2b86ca53 255452cd8b3517021e4abfb90d9fc8166e5671022fd094bf3ab43af5b854928d 03874bda6a58c9f4b2d730feddb72d604860e6f272f8945bc264527b0a06d386 703899545504960485d6a03ea7447e11352c297f25937ed885afc7f3807e9f0d 6766f276e2afbb1049089255694e0c61989aef36793d392eeaf0bebcfb3661b6 d7e65a8aaf366e6add528374e6a02ca137f3a97ccbd4f134f43b827af8548f72 e86b658d1279192f5708e69d74fb94bea145770e081d1e76810f462ca1e0fbc5 670c147efe10956e9de2a9e3e55a45119685d62a3eb369430c8b66d2edd09ace e2b13a49add3bc4057625861f4a467c3eb9dbc7b1e279867450a77ef54c8ad009b2cf4558dcc880e020cd8b4f8f4c5efed9dd0e4f3dc1bff7b6c33d298e16f07320b4bbd0fd54400f3d887b52e54b28a1d50f9a85c6615cc0faded31e4322202b8d728bba709045dc31a2fb677593f9f58b1fd46ec4fc77cef4e9302946ce209d6744c950f8e801c1ab9e02159cbd3d835aed5761895198c53f241d35e0e320f56821b19c51ec7238ddf978bc81a2a51d0f9f255b593eab6e2476f664194f203a1ef29f6261e53aa9ea6b01966bc0b5e72ff8aaf6b95f96c92040d652e8e2c0d001bde755c72f0f49a3eec92bb6076ccf17f4867f848c93a592f8ff5c15d0b0749aaff52f7a2281e42e5cf4898e9a1453d5933d9f820d21a5e31a04ecc71f205ef7779417da9a302ea20bec4e1ac60f9cb7808ebd524c9078c574b345ae5bd0c17651618412de396f18d99783f932d00758343a21ecf504c5b954fce0c078803ad3627a6bc278602fe1366349f65849f15fce4bb3be50bfc307b4556585fe90c6590d938644099a49f21094fe251a02137512a80eeee88faed86613224b0640fd9c003ca40a166c5a374b7f04af8a76a178831f83849e026e25ac7e1876a0a0f84b496bbb3f7ef1fdb9d8ca3489b0c243a6f37457dd7ddfff29a523abd27560f82b83cab9eef1f63271f6bdc24f79a502b129c6686d7046eb23db01e6d1339099b9f09b9810b164f658d5bc1c20350326992bf3e355fd260ec0ecbda90fcfc0f148a3c69253cc46db96fbb292df2a0fa058febf81b0bc6d0e55420f8b7e96b0340fd05364a7af071c6b99a702a9227372d6c9eadd0584cb77a0f8efeeaf8ae0e6b75633c2c07fbb9c26df7152d8fede243d96c83d0b136624bee8e5404603802e0560e295aafb934eed29165d045bdac48a27934d9e4e0f96054a6351a647e0aad8528d6e86844c56ce0a1cfc39c3cace249c078b02ee9070f24a4e23e03f007f7500c5b4a8021e88d34bbc066ac9916479448b75f9a0a9934d279333bb70b0472192e8be0c2079bc2ccdb6bfec6a08ea55f91cbd0e1d5b563e7e4d4d52b5108cd44137696c7b0831e2764a9a8cca9d0dce0c687ac1caf2f0c9a6c61c19a500f4529280859b638b7b7d938efdb0b5c9603ec2b0afad07f7e39108073a1bd5e0993863f62d29b32a843ff38fadd178f8fe49b4e2329d2fae23313b72f3f913e08d9c200aad32125f983e5ca79f28706c269284d28890d1166b06092246bd7330b62d02cd97761b4f6286065a973c2ef914fdb2dd0cc4e9118d9323bba70a3c6069f4fef13afbcb66624cfab283926f5f07078448c3a1a1f60028022d0e45190016d3ac08708ebe4596381c554c6aa8bea704e315bcf3935a9ebc037b1012be70544fdb2be20b245be9b8d68e570bd8b2376e7ed2560cc514852409311a614480b22dffc326b9d7232b11b66b27b4e251e93dcb568f33359b9cb251050f732fc074f75a37e92aa296b3a9fd0d486e51aeab6bbc3abc57a257f8dea6f881ad97700a90e41a336b1845da9df522676d1e1ba765a93753c3dccd028d488ec41b0a20f6f44edcce04b4aa4c673abc876dd82e2e7d9657ecbfa185d4412f623e5b400034b5d23a076bdf74ced9bd41819a0811db14baadd101e6d093c3543246c08cc017c7b59a62f1a6d83d2ca6cb9da2c7361f6d4ef5763701f2ff926112481bbe60e8c5d103650f387844bf324ba9cbd605486ab76ba4fc94aec01294fe22a28f4008fa4e52bff2cf3161b357fb9bd23c8fbabfa61979255a266c2d2561dc16d54040f2d2b28312d9c6dfba124820624190f654f5f390f9b1209689b70e936fa2208a6baa71923dac2f2a4cdac698eff073356a9149d3b7ca42b3fb0e97f9221800f66a3446a7fc96487dd3ae1dcbb574e07094521839501e9409821473af140c3045a0bd7fd66f9bfc0e8bd8aa20ba50bcedf67154b18ad6b594a14054411bd4a0ec6f44b4189783ae73f21080a2bc255b5c7de5209067f7a03cb0cdaea13f2c30fb9e1c5a9a77ac50b311c4648954da46ab16d7c6d58880cb84e2ce491064e1f0ffc195a31128d09f78aaa61c61993629736a4c4909c26ad9e598df0d3b838670c9f597bd1d02dfb132775461de4497b86277685132b040dc08b3ebf499cc87b0c9341b53f044906ed976b48de030869fd466808b5df6c994f7341afa8335815088fe33ba602e0948e52f6519bf673b7246bcd2240055b58707f14a25875112509d0ea537127a637422bd40debb1fe8fe736271f1923d1887edff3541bb40d280750ec0cc4f32d6aded3117d4ac9e8a20e03c3dd76085ff215af7ebd5969d3a6066c10950118549dfe496b27cd67319678d405528f6325561eb881cacba0eef008899e2776825462035e3e80176b1644803544099ffeda5c2311fe3fb1546ecd0da503469568af87a9414c4e35f5622e0141e98a5047d4e2fb1626b8dccaa0ed0f88d0553b4dae03d2579e37ac63b142995d2da3200bfdf73c6f4ac7d18a01110416145c441c2ca9222e9a801bc223622178ad604b6ddc143b309116097ddba00059ed1a7439d3e5c0ff9844ae9f566492b927ef58f70625d3190c4c7ab79ee908e13df220823b40af348cccb133fee9ee2b0739ce2a0e0ad04a85790fa2bac902b312e429ffbbecf78255b7d9244fc9586c5b874cf15c8ef978a4f14d60ca160b132a0509d6ecfd21e8f6ff52dffd332fd61398b66f5cbe9ce5592c95787f9b0807a0e26aca24190a9489b07cee832e5d4ff14a6e8f69ada289622598e0dcaa05ecff277b7b33be2ad9a0706c730e34ff8153d946f1bdcc673ef0a12a09bfc705033a870bc3431c516be0522eb5ad2a36956ac9974996c2dd382a6c2e8c088b0bcd64f1c6dcc387f42ab5efd5753d5764a5cac2e699e7c9bf6e990d1ffa340100198a505766a0fc68874d3806ebf3875abd456db36c0d86a7c7e946ebd8f9860c7d2c246f9e2dd7250e46d4a01bd5daf5eda0ff6b4f6f4f99e97e3c4cb6b1510154e701ecb61087019cdb73163042d88c2f9d07f50af6cb0b2e15ff678d6f48091e008ae648e31793edd7dd24afcda1a430e53291a962729a61b5b9fcb8f52c0a1fa9a08c95770c7fd9b95550d9d9fe229c18ecba04f3bc16012ae23ca057160f946a5b03b68d9b2aacc2584ad577e8c21cec405a064c459c27cef82c7347130995e35b3fc279fe10ce2a5b760dec50a56870507b7f73ff53d4ad422d89a97e068ea19faafe1379f9867623436626698874a853e305963e2bb236b585fd183709965ced416975d9e3b9d79ecda98a597e717efd1a99010ce0a2933780dc4ab709f5d9c31693685d8326ac40d00c8c9163148ecd7e5b5077853ebe8851ff433702fa81d2676aab16de1d4ba47e02ef993ffc5ed6202c8e3ef4dd3257d2496adf0cd8e2e9524d2c510b150fd8d216529e05f2048ec93ed30137f1e0e3c9ff8e0d0847834b9c23947b7e222959ec04fd815a83c37b912405d87876e3e0920059d80009e6563a563bdf38e804c02a3001a95bbcdf6e4704c0cacc1b58638ae1760e0ad2dc28ed389803464428f191451256e9e3ff6d983400e0025f182792ba5c6f0509b522337490c6dabbf51c438afb06b84e428651ef8a538f6a060508849467053bcc0c466972150fc4833c7c6ab8edfa2a053d763ac568120ac0e7dc07abc303c42d369eb26acd16ba1abc5613aa4ef4e1b3db9b4882529232a51f5e496f0c01f52f096567a2032b6c463b8bfefc7ad2db484f4213abd49bcd2494dc02009d017e6a429e27f9dc80e235aef5b1b055b3b51eb1ab6767573ade8b3eded034d80149b07f1a9f4ea3e2fd0eb1c245636e3548f833d4dbc5fd4b440ec2428ff537011334736e2c5bd202adcb49bcb8e0d02a9cf488c1836b83db3213a7075d3a9302e0fa11601f36c66e71b06b4ee3ccccb8f0034a83bd9524c2be0cd519c93cde07b530989904529bb03388c2f8751609c9c0456417b24054d3242e25a7e3fe3800654ff1512ca23854f2d81ed879e1afdcb822ef0d8c02b67d9aefa125323a5c08c09339ee8c01baa05dd7cc6b319bd8d1400ea9d6f178289e03f782da1b158e08163954d73ddf2863e7c68f51e5bdd24098b3df40f2b6bde573ecd83f7f4899005147a926a3da31f9a717e11c4086740f129994f6818ccfa76d7e37818c3c9a0fe85cb5c97cd515dad119ab5eecdde4fef2282da8afaa243bb7554aeddd796208d2ef611767e9684a7bf1b9ae1cb642d2af43357d7386e066c5601b49d09c6b0b64afdb233493978929239250f243255929319baac0f36cc6ef17481e0c31e10a9c6f9b49975588711f8002c93ce2bf6bdd25195b45b518df537ae78d97879d0f116c92d8bf4bb9d9fcec65bfa2518db1c25e29c350922766e0c780bf4bbf5f090cb4dc2a3b01a9e0fffaec2e2d430bbcbf2930d34869c800eeb00a324316e00512512e2dcc61dcbf580737d635e251fc0ff599d232d7c5e311cedbfdd4d2b30e49ab425b2b25bc025190e40e9d0692249f3a09816618e8b0d06de1a49379a20c4b78b852639df3edfdbe73bc9d00b9dc94bd33558b138d0269722f6f28c3390ae4ed3db0b3efddfc678803e7a4fd1f97a359ab849c69446adfb1b5e6c53a7f0467770eb88b5d3d2cd7bb936bc560b26441741aad1ad21e533652f770763549039baf9f76eb596187c7d9142d8faea98e874671d20a5c15b971b2bea779bdf70615150aac6177d9e3eb4bbcff9d4372c8146290cbec65439412f7b527154cd602d3bcb3abc58de94247512e521b8340ea023073918479ad9702581fd3990be90509aa4bee2efa1eef17107428296c4a5103446f5f32de98827dc7095e5bb3fb02e93cd389f4ca588a50a68d319e5b9f8f73ecda7575a2c57f1a354f8d666be5035f9922b7bcca28f9f92c212523fee8bc7b56bd8421d410fcc6bb59c80111840afcd2d7de6666c50b557fb08e1ddd4ab9050fb74c8c14e5091aa083644def4408d0b5e5966ffcdef84e56ad7fc2d3777e72eae6944786f20c8bef193791a9f2017be55613a6381b8e6cbc9fa3462e668b64db0bb0852d528ec4e87b82f9fe6c0b6355381a9306e9dde089e9b23c9bb85c9985372295b2ed51a37ce2bdf33a6003ac22c8519ea6137573b44e134db6c77de39891438e97a8b6fd70e007b1c4440a4ea3e281111e83623903582c1750102c158672efee97aa56144f1dba2520bf06 false +check_ring_signature cda954a1a60d59e6dbf14eafa9e630ef901bfb6f503110512d5b255a4d512274 5dca4a44795620247881e8515db7b89611263ae3155046c91dc525cf14422792 9 ae59479b70fc8c28cc99a1c8305ddfcd7ac020cc6e63528881f0f3c47e1b4aa5 1ab18532d4dad9795508359d09ed129f5378814f267c187660b8ebc0317c6692 afd57cb0a1522a2ffd02836a3cda67ae2e5de5de5de4bfae2ef645eca6f58a08 3c2eaaeb8ff647b7ca62417113e06365556f3f658dfee3c2fb11a46575b918ab 09f86cb845a3e7c7da7c6a138ea165be0235c56c41dc8b8e9ff98867ede0f417 ef4bee73f81bc6b3993f6eb8ebd0464987a2cc7ba797cb4b7b4f6741211504ef 925a9a965373172797e2ea454aabe069ee7ed0676ea53c27c5286ec56af094a2 5db2c81805b044b44e945f3538412df7d5f3a254651e612fd3f92d3996f006ce de2f6c05d60fbadef86a19b18b1033b3bd3480411c2eaf91efb9198351d8d0aa 26854cae571d5a38f99a77347f9dd779d17e63d30780b3fceb7b1c7e7b3cff0b36d1b05cdda22805669d778a1abc83902557edeec7551f03eb240f4e4429fc0bb4b91b5410f46d769255e87aa3b58102183cd205cd38760f5519c5e70bd78f07acda03fbf7e67fd78daa3fbfb13a14180a4f0816c33ad194db7c90c161b7ff0d7528ba1a63746d822cb70346cfed866c00a87fad2d7df54fb791faef504a700f511768b2fa9d7bd752f03dad63675887256a4bb81e9fa81e8719112d922be80c8aa8d10fa84a635c9248a8080bc454cc551d380cfa580731fda7a2c75854d5043e666c5c5231eae945f3e865fe601b0c030b6d33a0d1b385f55aafc153a20f0fe45b21ca7f33a52ac38c53bf892b3a4f45a0e2df3cc446b88c1c750dcbbf8c04a9114d44fade0f177a0916f391160586c779b8f4fb655e51d07c57a81bdffa0be734cfc38567a908dbba274b85d74bc22cd9ffd35083e4c5fdd9705c4592c50ab7708e91deb49cc4bf1abdadcf91cf0ddaee21b16d70c10eebc0b888f7b11a09dcdd32f0cbcd319922995a21b3ca4a241935473917f7ddbb4a063e9c14cc280df20248ea244d49858893d323146e977bcf3e1a32108c6eb4fa4e12146e9901008996f855e70ad7ffe9373df35c47b8f13b2a5e7470eca05ba3ec042b2ecc4101ccf84be1d078d3cf432f9f3210655732131bf25d956f701debbfefcfea94020dce5e9015353e423e82acd947365bdb03b850697c6f95b60460df90642d9744020fbbe3bc972377fb1c0ea85986f71f220cc634731418da3d349563f5abbb1b0f true +check_ring_signature 8edd5503b9f89ecc3e90968553e3ddd884e6233d70c1a6e88a6b07f2001b4531 93f5fcc8c459f0209f6cc7e5a780d2b548a7a13f5476114911e4abcae42265f3 73 f4581a08c018b1755f0e6ecac44f436c7898348ff57407e1f38d8dde17505dd2 4625c0e16eb4266b396e5b3366799a18fb0b01ca265e12815f31bb8560ec3ed9 3a4f7935e278b2e22b59e566ac2822b66403596ab6489454c43172f1d41a9035 484427632cb732123c3bc3353b6411820166b9d156d189aba2d4ed18dca35558 dda485b686670c60963ace4e96dae0e2534db3e765083a533ec6781e38b4f275 63cc76838591154e12d74dbac0c583fd56889b797245f0e940e1274c3f0870d9 3fa931be2546c6b4e23d55ee6ed7c9f64b4f84e91ad11db4ac1bfd52a50e9442 3a4f8e29ae13766fffdce357e76176e7b13c923bdfd8d636ed1c3a002b5037f3 0935ce02f0935d3ab8b77d43093f6e3346df69b86a4520635e336632c8ee59ee 4d36b22f50cf6b7f252474c7deff66a84b7d82210789d8eda27ce519c892fd2e 62628cdf33f341063f6a877a980b1658928fade5679bfa52360046ce4390f0b6 954a37e6d4304ebc729607b6a01eb05a88267945b26b53bd68f93d60af7ee22f 411e0bc15ccef581eef10cbdc704ffd6369351d343f6417b09cf1dda412ff034 930830f2bfef6cb34e20dad4d00812566ae9be1e8199e779b9038b96b7b3d55e 431727ed912d1d1822770b4a0a2f7a50e6d8b9bafabea29d98e0b4db418c53d9 98280d32169949850e94e9976685d70ce8d1db99f1c63aebaa780a37ac45d61b 1a6dad2d7e9f25dffea9604c17afb0c51d6cc00109267df18ba2bf78b06c6884 1b95a27fbb85323466c4530619d59024c85d9014371b1aec27c279d6a215f823 6fe53bba7a64479e4929892dedc7853221a393bbe568e383515e83d580dbd9b6 a1c5c3a83dc7310ab98a020b9a459a2206d16261a1f66ae615562275cc720079 339082a15ac79dc6b77a4ad42a2c10063cd9f7565b68894bc84e22421d04f115 df4088c5d0322a883d04cd61b5569cf6d3be17fe0d1f1fede41bc2555a24be3e fae9d60f91e105d69cf05979c2ee51198075d60c52e56c4051170b8d8dfd1e63 fee178caccc64cba26b3a0a48afd2cc34a66c883552eef006ea60670cceed676 a93b292d783f40a542bfd77af3ea248463bb05f3f69b32c37aa112972c65bc0e aa716678a4ba73fd4f026fc2e550a3c8ef1ffd4413c2e42ab77b224f64b01215 29a8ffa177bf2a49f25cd32bc98b37484175d9fab988c920829a01a104a1496c 5ccdbe9731836d0cd8ddb72d7fd7be8d9e2d270a0ae4f3b004e7b06cb628ebfd b03d20be46daccbd961eb6043dc53551d84564dca23b31fbef23c4e46a9266ae b231ff55ed3f29e4bfb1c11fb72e60105883afa529ad6f86c85078116155a12d c4e59ecc98ff1f5c0344beca4e7682f989d599ded67641aa2307c7d7fd2c5b18 5bbd7d8c22911e0f865377f8f03f458ea4ce826c0ce5b5f82ca9d53f285fd00c 86d114961f584ec46c9e0f1f4275e279b262d6d1a766613a6e55e50f273edfb4 1736c381f260ef04fade9373d5a508b90f411beddcc4f508594acd2c745c9ccb 42c9c8b3108b6cedc899f6745676c017c4dc3d67c2964df7e650eefe3a0eb252 29f7453bb805c10d30f0731da17081bee192dfe5a7b1aeae918fd0608215b9d9 6bf9269ae2076ebe536e59c55c250524e3925741b7663dac69e169b4c8df08a7 359a23f1a06b3cb6419beaca524c969dddf58e1a5b675f9436d98526e5a29f74 c5248d6a4d535eb1e5b907f539afde7313e1ca652e0d94dcadbb0a24fa808695 a164477e6157c8f09173c6dae6400966f7343cb634d9cb3e1fed9601c17e724a 201b28f751c152a6754ed6312c85cbeb36cc8015cbc53356a06b4c81184dfa56 ff16085c61d470e3a2886a041678dd7d1b050d1429e779c9543e8e690c90cddd 90b50a0973a5f6b4c7d30e2ab25a5c13c5d413986a517d8aad7b46dc87c7d30a 2ba63c68d3a0e12cd162f563506f48fd91604c55d3702442bc19cc703998b492 aeafc6cf06a0b7d503fde5f59dd8a1b4de7f63509eba22bca6db686ac0ae8cbe c13ba4b007aad287125f69b14b369781754d0e93d503bf13cfc8bc2b5450df60 9369642a5e9961a34992181cb6643ee5ba9343664ddec2c69fe78b3cdd7b985b 1a515a8eeebe8e02a8385a4074398f493f50e6a1463019aca2d1e7c20c6b4f60 7afd3328ad208ce23388597454e64e187d0e9ca72d87320685aad842ac7370cd 9b2d6fbf1d7c99cf7463aa64767b81888bbaac202003e8c0ee9551ee1a2b00d3 d70cf35b24a146d20dc6c28925835219a1410599f6d17dd52d8a9cd3c2ddffd2 7456e7d6745ca825358e23453e09bab03db85ec5606b98bce1dfcfd62188cec0 0b6887c1c8f9a8d8781a29d1dd703fbb58cb92e78638f5ed7b5767071ae5cc03 050a7cd8ecfb29b96b3abc80fab6ceed15eafef2939e2194deaa6cfb9c3bca75 715d623f582290551cd77043c319cf4317a3b675c1eab829ce0ad45a668a654b 95ca53531cd6af4668dce64f8e54df4fedaae4220acdc5967fe04a9af75ac062 fb0a00f6dd956494d952ba9fbe198f67e7131995230c0df381321f9f64ec9182 5dd7a45230543a122ca10e9b2567a289bfa13ef3d6c4873cbe747da34f03931c d4b6ae6cbd33729b2ba3c8022598bc906e5bec9fc18fd67f694fba90bf2254d8 9fda2c5b75684110a984256bb98c0e59457afa87a372e8d800643bcf5f37dd84 5e7cbe263bb9c38d1d9f37037fa07b3eafec8ed6f78c519b64f3fa3d2d822080 43bf1fc9571b4b2d4ca4cea5b74c0e48d1168ecb9031c58ff45733ac755a9f04 75c52e6878f494067b360830c5b9b77c193c4f0e3ce15fa44a5c6a3eee27e78b 2d35eb1845bf2747fbd7561f211c6477d42f6ede68bb52943d63370607850b02 fe6338a7ecc6ddfc7f204e4366d1ea989c4e4bedf867e57aee8480a1dc384938 e45017d6be9f7e4a31cb200a24c3b8f86ca84b429fc81f4bda5a82955ae281d2 6094af63fdfaecce9ebbe58ae02a1e4b06e32579508094bbc2dadf2f9762441d 2cae1e65af2cfb2a1bfec80abfc503dae1ff84ee2c978f1ce7a0fcad05cc0853 b8376083d1ac5a8459496fafb98eb417d2a0b3cf06bcff5a109d71c3c1d23724 3739d3e662c705a028991baeca4e285a8f8b01f8216cd4b6c8670c818390f51c 4a6898b95616f530f40ff7a506debce5c50d9be25cdd3eef592a0087a4e2eb22 cff628fd2b873d5e67036a662c1f9f6375eb18def752500b7addbea24bd2e2b3 13ab6741ee12259452579677848e694c398e3719daca02da663f8a0afa26ea06 44f929f7de7ef292086f75b9cfa9c004e68d45a5d385efacab3fabb78f86090d261972e507feebc3fba5967b86dd774add93ca8d0f75ca8b14ddd5e4dccb79098e352e78c835e287ce2fb125fa70c2906c3fa870d7c27afc00c1a7635f7b8c001cd848c2faa28000b7746df8442b976a424382c2f22ee9fc40c3045696825d06b08c227a72d9bfc3077d302d00f9affd0c2981ebc584718a483e5fb8a8623b05bfe32a8aefe1ebfb0bf7c18518899c171a167cdaeb666e4718c4924bd51db70d7fa40c67f2e8af650e239523c6084f5f036c379737a8b02655c0be4dca2d6d0653e7194963ecec301e86cfa1240490e2a5ab25d5805db7473ef4ac3174053c07081423278665c53ab62ef59291a2fc1ea3f56efff479624e3e11d4a4e4a9d30d79f464a3f4068eae84e59f72576f9bb11d4e7edfe9b7ca5c5bfc89fa3651b30107f76d8b2a406461d3b2aa50494067c48440efc9050f86886ee4a810fd99140492c5f321c0ad5ae9c8d93f50771d818bcb3b9cda2f0ffda4327e4b5ff53fd8066c920f07ca6c062817d721a99736256620e89222fbda8f65c1a2673c9996330dc556e0d14f978b55eb8cf17de3a15a8043717e658228d79acae17c1cfc1bcd075439819b7d6cc7e1f39e1975abb58458b0b2d911d6cafc4811bac755c1e50f0a2bee5b140665593fa8b43eaf9f60ed3825c3a98916c214fdd771a3d78bd1f90f24b219c8e83edf5bad82339d9f61e3130a4a5c14233d82ccf66dc55db0cc2507b36c00b87ffc32ac552aeff62b5aa70dfbd75becf60934185b4043e35e153705e48bda5ea4c315922693c239c96719bb83d5a31455e67bd4686d1d859a44170e514925a8b96f4cb16c929e1683c135a200b7fa0c86065342d4c8eba937866a0e1dd018189f653abf31f839f3d46a8a9355bcd4b7f15434d06060096852c3cd0e4e7bb8cc00588e20715f97627f0ab9de389c7b7d46cb37f715297bae0be50008362fb435c3e10081ad7c1f33c1ecccde0df59e8c673943b4bfd5768b880863021597273ec5314bfc9de8268ef467230cbbdcc0b711c8dfeeba43089f60d55f014a4be78429a91117916435caada5a41f9ec95982313973cee38f881d344ed908b930726e3f2bf3e76933d8112e6b072c24b4ddbaed60cc549a766f27104b6d0f2781eb22f34e46aa0a96190117a9609fc24b7248f0194d958bba1c2327154f0ea30fa9cbd23258c8234b7a104d802731cfda31a343b4184349ec4b8cf38b7b051d087964c1c1fa9852e0054efb58f85ce9fd0746ebd1b01ed69d3f686df39108ae014a730ff550a802dccd6da5351e43b34f20e9a8925ee018ad98db67de4908a307aaf13d614af03df160d1e2b8eebc99e7a2eb678134bcfac3a50d7b7a460af54606d16baf6565f5aef1a3f42955c2103eb41bfc83089a5af228c090aa00002c97d658c89bca8b861bacfc0bbd9a21bcdb822fdd6dfed93fde16bb58c98f0495fabd321cbdd9b13ab20c36c3192e0660430029660c18580e4a0154829a3e0c560eca7ede01292e7e82c7a007895b2ea00bc5ab4f608687c9a619f6e8866b054ef7f872abf43f584785ab7659c870f3c75390edebd61071fa0d5a386415c706ecf4bd7b300c08cbaac11dffa1646f1e54cafd53da94caf8df68197e85e01a0e59a3c5deec2d38c6baa96884123490d570cfa60eae0d19dad2b13db3dc776a0a40637e0bfa9b2b9093ac8c8bdf1f86eae34235e1e9ccaaf64f930cc83a48ef0e40ed7829115319426dd1563c748028fb49cd2766db322384c84a04f1bcfffc0f791f158b8e701d3932304ae255e12c2954445fdb732bd1188388a582cfffe003593658b94a8941efbe3840ab48dcf8ea0dc383c7672cb55c8367ac8476198801f6d5edf777ddd79fdad65a03c0d6293be8a0f955d76f269df4e7b32dc53ba70c31a7a577784334cec01e99310ce2d5fe47dd8f22fcbce1794e26012e4abb3a026d7d679ab2e63eb206ec5e0c1afb4a427ef62cbad832a118973b5eed2a2e16093f920120ccfc4561ac780669458e97e3bb1d37a901b81e73db1382f09e32c806a142f7f216701961ac8da1914b805110e0f2f0d6f183badeaede6065bcbb480c8bfd7eae9c902819ed15e4b72c703e03da9be6afa68a6048be642050f0921900912a35fed4446bb046ba663d11405c9ffa3b10ec90bfa5f910fe4350b2ecc00ca508192bcab65c26afed291428b2b11d005d83b78d1678352c5254f5b62af5005a7ebaad3a8313648b85aa84e75d74bd0c1934fa9772411d5ddf61f9ae06fc0abc5fe328465d40c1141ec6ac28fa40a6f20c6e2262cf87384c781c37d4910f033283f69fa4be5890d2457d67fee870b6493749c306ced09455c4aef34bec910646f569a5a23bcfd0b5dbfa88be1d760c439d04dbccfed4d18b027ff95bf3100f86127839c31afe3cddd0cfdd5aa69c4a6318b79f2690d3336f8f2648e711a80e9bf8474eea7bad51f917038f9eb21aabe0fb4bf86c7b1e1aacb8c3648f84570d92492281d818a83f10bd0fd735e9fb07c3ff3dae94bcc1b1aa39fe15063cd70593cf15850f1a5394532c164e4d07b306104f438392da31954657c0f3b75e1c0510a9bef0950534e7cf870e5553516ea1cf6a7aaafacfc65d8e0dec268756c40952911c79592287580a81cb0df8128cafbf2930d61cc9f2d22bbaa0cfb07bc302062c3901cc8a516b01dbbf3409bd8f01267e9d00a277db347d49da854f7f05059bb29702793045d3ffa15a802d246df41b3c8c5a82c6cf419c8ffd21d46f8f0aeab3f0bb54019dee574e1153d021c98099353a43815f4fee25617517b5bc920155725d2fc7122b16e9559588b034a57f6a5c3e3b22ea037232a54b230b4cc90fb80966268a48fbf34c4b9ce8fd5c4cb0aa034de8114b3937056092e8fe4f290f127f3c02099b1b303f4049d6ef47e7e8512d0ab069e051839663d20e9c9ea60cb2d846873bea8b54a7be4fed11d46aa80f2467f790e17f83799c3463cd1af803f8bb01f8f9e186f33a0402a365849b2b846ebf8dac152c2d2a2d1ec5df9c9d0b35422e523f8c4bdc1878968cd48f9dbfad0483cb701b916c7b43421db0f3ef0fdd80a1d09c83b5c49b02d9ffc715f46b906ea506f5b1f11bd7f3195f45f34a0db4581b4348a9658ffc51393e7fa80372203d5de4774256aa20807869112023099910ef9df19b5c81f28e21fb99f40eddeadcde7a4f430c89b9708df003a3c602c6a741222ae9751ccd1026b9f3cae31c36dec8711b799ca4107a2068d91c61003329eecd11747da46e052e70d0853c85b0e98169552b6b8fe93c676d1a101e855039f4424260d292b52523a2c466935bbbacc811a2f00625433ba3e4255c7c07d5d01e952d0de5205abefb8847e332e9201d72e00d958784dfe7d3da1625fd05e7ace77668d48661083b36fac6470e3a3f284f0495b2ff304bf11705a4c48c007b7077b52462cb0ac2e1aa7f47ba1358f270fc63bd35ad0b0fd53e9833b29a055e804448aa579c7c0d9b33ee3a42aaad1e65a991f5920919d2e02331a14ccf0e36d1d7610cb93786b9c812584f5f346bd25356938a29824d5efcdfb2a48e23000373a88813d76c1f34b6cf671e206806ab87c5a305b48fb79a86108f559ca40f4c9c8603c5312569fae4b0f93a470bd07880841bb2cf2c92b9d683d93be04a0c7cd95f63b73ce1fc2c851300a4adae355508ebb6deaa6fbeae711ef9d0e24e06aef889fb4e7ea4a465c604ca62683799f31853f48ab40f75dd003873c376f80ac3c601b166ceb141ddb16da92089fcf1c7ef586902be9af19d57a313c02bb000777a40dee907e000fec5cc4af1126f47141162a2843e542879e86a404d01380da2568ec07d4e7f103a59e6e386602bd6ebb008cae1705555e5da0ea92eb0a0053ef8d44094713dde9dc290622eff7acbafd69211ca006dae6dbbc6ab73ea7b020550a3562bb612bcf7884ffdc8f7fc49c7ebc18110cb3572a61e0994ecdea80521d7b431a0e3427ae7d6f4e5607577195202ebc8023840b7473d593fcca85305f053b12cb06d5695afbe2e4291eb8211c363f39469d86ca63fff99e515e1e10e76eb69b5978bf02781221a37b92441e27a5cc404f4e5e974e9ea402a213d330d4d48fc4f7f33a1b658d4af8f08ab453af430fb3d07a47557044608350f1b650d4bce6175895d52a1496c06074f28dbc0b1b62853402dbc68899e9dca2e09270960f6121f8938fd6b145d730da3db99f515580abf8fba99fd50a96c09482db10eb8029073635011518e694f888157226cf20f427a87e10904ba7d609277694b074346d995bfe94d397dfe33b2cc3d3b231a1b2b7ceb740dd72d4dbdc179fd36012d8269f9dde5b217f1a308dce9819d993136bd7a2300bf1085b312de3dde30029519545b145685e58c20ef7b3e1fc77506c1c2c6c3e4dbbb52e551c59493860315855f8e6c991af2de7de80defa15ba6a14c5a7d6e1160bf8e05493ba71be80fa173bd49f7259e85efc1150d0b14285b25070c74917313a31be2502076c1a1050f6cbda5b0fa64be088c67bcc9b0f23bb39273533686eb846195020005bb6b05453ae084ae1b89c71dcf2113ec6937289db3b74032e5f2e66c98faa4cbdd1b0b1635e3ca8ff9e62b5f37e0bd05668c0d4b51f13d76f68a030129c806b0d0c809ebb30494fea120fbb9510caab99ea4a8f99ab3c9191e557851a045248d449608bcc92ec71fc65cd1b07e0238778fcfd03190131e81cc4cd4a11074b529e53608e230f3e353e2ad7250f961d1354bf0f9d183b4d40f3e3ecc4c96edec102a6d07b7e80a471680d6a20c291e88f14d23e5e8533d0093c474fa4678c20851192501575ce2fcd3dc2fd574d21818adbabcc9581549b54eda066157c71020723d630685b3bd75666f5b94b91b20fb530f08d6babba99dbd1dd609df666a0e8dd05c04eec0d02eed132e5a7c95dea3229faf2cbaea997152c42d12f1ddb7fda22212094a512235b7eeb55f2bec416511ff922401ebc529897cbff1dfd6e75b660b6f055484198079729cb5e98f7fd9bdf91efa48d6e73aeee30a0d28ae4ff2c08ec107439f16988cf2bd39561e7d5e0bc7cf2da6fd555b0c11f859a7a941acc4f5e90c4f0d9a1bb9fb31c02d7394716631376a1660a6b5b7d6d60286b6da8daaf4ab047e8392c53328ab7dcfd49559c40484776cec445c231706b6dc04f92748de9905bc32cfc820bfa111982875b037d16509e46945648512deb910f32c717a20620dcbd4c71c050871794bfad39ce7619dae2fb92b97b7dbc2ac33919642e07b15057ff37e9a9773341ed65a8282c10eea184e1c0db2603297861486190b1f626b0757791461e978ad61fdcbfaa9c0cff4259217d9d361245401980f6d4f9b2516050affca60c76c4d0f08152cb48e164e299ed3b37d6292068446c4c17ff6cfdc0dbf19186dd9cfef23094584a4d322950ca4991ac7afb014a675b2f2522f783c05c9cdaa5bbb1edb40ae28aca040ac673ae5e9add995469b8b952779a377dac4091df4d0ff8c68d378831e7959623a9e9982cad17b6679abc57f6944be4a77310386b4f5c9a70aa6d9e446aa4671a7fdc467b324939dc73be28b9a18fa02714301a17796776465cefff0201a37dc23283ede4456fb2e566051a0ed1622a2ba04099c4da6660c71f449b9a64779d54b6c0187786ae555671cb3653330deb1789e094580bcd4c7196853132d703e7bddbf4ae7e06201b5b66c4fc303fb50de902304b3c4a68809f6ffc61850c7a19c2cc8a887d828881e1d0e646056175789abeb01fbbeb03944f48ec0adac417c03471458a9d6b95c1532b8ad2ed5695bbf0d0f01ee7e6d735538af5c0430a4bd1cf5037ea5ac132e53a5f43297d98b50f9078806cbc06f1e9a7549499a0d0b22d6b0742507e0171dd977948ede8fe4329863c306a66f295d869e8d1d2837c05e811fe545c4f4d443285a0705d139146967def4087ceafaa1c4b2df835b1657e2c5a5ed36304296d915a5e06d60f05f14e57946003340adb60523a6bc27c321deb978b7d08bcc7bcb211fe87c19cea3e254b4050bf89ea8f1542bd28d5f4697617e2ffce6836bfe1f6382bac144be1f9fca65a10faf316fe97aec1d32c419b9d5ab9226674dbd9de2c83e26a7b570bc165914580cd345ed21a9cf0437deddcaea43b995ebdfa879a87210f99bfba89879356b5b00b36175c47bf40b0a7b588033a8994863acb97684d2ba2bb45aaf160ce11b9a02f17863f26930eba574a63c19d339c7a95ee1b02c888e301c58a5bd77a3b40f0339a72a246221ef44bb4db00732030afdaeecaab804e814545fe05448ded22908f92438cfab6744e6672bff64c54b82d134ca3cbe16c225e5d8045341b4dc4b019a3c944d6c91f8168dec216f426d101981ab9735d3d0e8451fb0cbec371f1f0359fa22fa590fe247b3a18384613f0d97b2703362ff553367055572cb507a3e0d0404e9b571208bbe3b518e71ab82a2289e74115aa0651e56e59e1225c14ae109869995f50adec643d601d44cd1a79a467ac5dc7aa8bd167d4918bf945418c609 false +check_ring_signature 7b72d0068ea8d2f62a01f33b8dcdc7f372bc0b6f2696a22b6838379164e11523 2a9bb43e82130ee6ede68f9724abdb0e1ac450b86693a54f06536ffcfc2eec30 2 3463e66cac201a6b84785c6cc34ead60be7dfd67e0093520d3bafc414230ba9d 7c2acc63f3d2890a5389f9800666a13bc61e09466500ea2b1f0a9831e84b3d07 64c0adb08a358f3d5bf5f953189442ac1e10f8ed1547136cbb52c4feacbc9908964691bfb8c5bd223805488ad8b08460d51a5bcca6796a45b67b57066419d00b6019e1e3e0496316cd283645875412f33e7071dd408c5818c5f2e422363f1a054bbad78b743f6d856df4e12c305c40ef91993f0472277547d25896c09455b8d9 false +check_ring_signature f1a638968fcad5ca6e7f90ba112d3596731c77752c591d06520fd24d656b5afc 4945bfc3cbbd008edd50db778911d7fd7a04a61b7f9b7003e87197a43082b140 7 aa284d0499242497b372b25cf31080b02ba25d01f6ee8f51814bd9584546c222 06c1dadcb37ec8debe4c1d2b2183e67fff1ff9027ca0799786aa4f2a8f871c15 eb3e72f5c1fca06d4b68344af19f62ccbab39a27f9a6431d9e7f6cc0230a4a0f 9db1b15dba3660ea98b24f0bf121118c5e74d6bf10bc358f88c1e6a1651ff127 cee7e16a4d74ffca636ed8f0b305e1a68b92dcaad088a4da1c2e9463fa474771 42c76699dfe8be3e72d9f650f1742c078bcdf5d646c27e2fdd94c446042dda57 910d5ba6124e2f52c8e2fea8aedfe0a9e71de3bb6864ee917f3853a5203f6464 b8db371e1a367980a850714c67addd68415848fe925fe595c1ad706597c15626579daf8d2262a4c3c6b729ba1e2c3e721ce0c904708d074774a58d2ed128c3038556100bfc1ee215b3904a8bb6fae3cc3604e24798ce023edfa1ff56e1942a06149d9a93fb7c05f972d1d0ed3065370efd60b1c2ea539e3d85faa751e2ed100c709c91b9508c61e0148718afd3beab939230f6a79c06f1edbfd41573c6491107cf5d56960ca5f82fd6e1f65134704b4123c486cccfea4220bf1b51b26a6aa8008c3098ba7b034adbaa99b6f5eef3302f9c43d148d4ba7dde8e496af8e2b09601f38375445cc7dc502289c5cb2dcbbcf415c0481aee940edda58677f25f57be00313ab998575009bc79cb05b7c784ee39976e872140a0d36c918d2e4c29d6900031369f338fc2795f75a46b00aedbf6f75a0107e31070676f94ba9410524af705dbf92b3889e0f5df9fb261a2356a1d2c65f0ab2df53bf6a891de1c0ba6751b0816e6a479186b1b7d9cfeb57acd0e1dc8c194f5e2a0be245a84136bd42713df060d3d556a8b88b4945e67c0a7b25a4ad4275f9f0856bc66ca7a3fa426c6fcb00c8461ee185cb18c5f0d0329e87812b2ed03fc09a540d6a71a2d94b6285b223206 false +check_ring_signature 8e77f4c9bcd0f3936dae120fad94567901ea54249ad8df766e702cd8e9b72d93 3ea0590d6833d2b025ffe617bd0e2ce8f07f84f5364aaad83c4f96940e1d8024 23 9579dc753596104db2424ce27a52358bbbc9be1f0f39abcf04dff4630ceaa538 89d1cd57aa1a7446f081efdac44f0ffd662d66529f62f81754c0ee6a05aa6a2c 281943c7576b31816835f31740e100030d276c00151b99f99a6746cb9603ac2d d288a3860254b642c5bbdfcea99563477e76bc9ae31ab8047dfc733d1e628f28 56abd4d6803f6bb17e95e90c3982c971ee5c60f6559cfc550a0693d71c0c9d7a 5490e25ae2dca745eaaa85e84c1bf5b7d1f32810c43e1b8bcf7d0e20a1954891 502e7f01c96ee44fef903ad2c67830000e4310112d6cd9569b3f43a85ea07861 a34e4e8700473ebe382caa9341c7858dbc0fa1d9a37e6235f7a0eb2600c27dc7 43de5d6a04906497931fd1dbc5bf00f10242e43eba57370bb2aa69f261d8a569 02a7d9841bcbb2f7dcffc04890c34defa4a9938f235d68863492ada3af05483a 2d4960b3ccb5cbf3866377214e31293c13202f37f8736fdc1d41057120767b1c 62b91f9a9de538fd32fbc87b20d3662125c8de016fc800816d6d2e016083b7ca 7702c5f42851bca4d25c884f5e8415c565fa24ced376781c4ee504115364c0e4 9f8f04fc8c43c89c235e58a3352bd6fee7f2d6240f7bb49731e39add959cc711 0de91cf46a700d953193759a961ca60082924e97d352f274f58efaf1bd5dc9ee e77eec81625cc6fc64494c2ca112b52b9decdec1e28dc184860b27d2407a7be0 aa6b075feccb5a462fb9d94d49bf36780142214ffea778e725cad5511e7a585d 03e7680d04a0f8dfe3230955311f6f19585041033dec2ce80355b590575d8641 3687c8cc8a344382e4468cb0ec0ca9df8ee779392d4c0af919709ac24873a896 d454735f65056a8db0c92b5c81edc1ed616358d27047f8f8a4517692e4ab2feb 98c21715ebb4ca53075eaebc0ecaeb57fddee40d27d75e71972b9205b81497e0 ac8c7c974824a693817513a50aaee88e2559a6b4c04dde8977a288db55099b06 561fd72945c7b5dca911aa1f645a5fb9fe3f558e3de023b96ecf862977f83b09 c3d865e612b360de798d5eb55ed5a6c717d8d87dac65d65813e2c0df6aade8017aacd27baa6e1afd986721815eca0579bb97251e4f16cc1a6cb9e56c89163504562e3efe08cfc3c485f45639830c4b3393f9c7fee419bbb3df1dcb18706fca0d2d0898aa621a307b24895336dfa5b949a67e0ec1bd58e3fa24627c5efeadfa004ee350ded0f5c49a39b0a6f8b26540e55bb656d66ad4c4b14d8a67234c7abb043597cb395ec35a2ee3169e19b803c3d72c581bf20b3f9129fe01d0d57c76da0e3fdd60edf9e9693a802ccaa933ca5461c782c55dc53aba2247a6bdbb35aa6b0d8119527c72efac958968813f7a8f6c2d98095dc013a3962529a68a49a147c7001f39891f7380c295f102632a72ab3482645de64cb002c3ecd2721660148b5d05e133e69e90f72c4e05626d3ea2c93b21cb4312dfab7d4cb07d2c3b8af101e706a0314088093905e9b774c33e66194109ae5304c70740cb3ccdf79c3fe30c01028f51fd547fe7cd4c303af459f5782be473c12c13860d8b2b9915f15161d05c0cf117a9ac1fb5abb46b46e5b40a209e86b4aadd2897efe0b25f50155e56a6bf07a87665b7ecc3c7699afa6be43f938257369083ec72a35386488303629dc7660dc0e3170c892361dcc7ef776f2f32af2a2a53a3f9bf097759526822f00d2a900fcc91dfa54afd71777e145a5a8bee6cf731c291ac3f09c111cddd606a6e91850081b7d2e97ecb43d191127cca6ad8bad822892d0c7c911db311d61e0ff8649e087a8f3318f754c9ee2f18d1e7bbf55076f0bf81a8d2bd6c3ada2a68d3de7cba07bc7cbfafacda3ff647833db9dd1c703fac0a9693425d01aa7da5ac4ee9f3920bbd1403eccf71f95ad00160e5cf51a8ab5dc29ad8524316ebf810cd3da1fe510f231da2bb49e519d5da2395f52be467fe6e716e44886f08b98c64afd5e3784c018b46963aec0e83ef10579b9c6096b39f609e327d655f8eb7a1c4cd3ae762c905327bd615e78011bb4f2a8d316ac687fe477f5137ef5f04ed85cdf0587df6520b78383928a39feb4d186766a9c3220ea58b946587f7855f2db45ebd441e3c910d8d99959082aac4da205991580e2cd7fabca974b18b3e27663b980e5b2109dc05ed4389890f690ba20c8fddb36054dabb886ff4caf7e3a13b8bf155a175f21b0f245dd9a874195b68dd54ecf20411e0b314dcdaaee90d5a71c5a4331dfc2181004648c2437b25bbebb8bd9ae12921108ff10c8c61e17b384fa509833cf7166c0404480507d0255408b22ecaf67fcc919f93f113119fd859cc328a6fe6bc9ce8095511ab90b769d536fc9452341acfb64f239ff61ea2a3afc61fa981dc56aa8201c630a804419b3ada86fae97865ac48ec1e29642cc3c68018e8a732d8a45b680cde11088496d81d1e26aab603756ece7cd69bdabe4b0ee50ea1b2078ca29ec40d54a170d6262f5d8ce48880185cd5e30783428c84d5db2aa2b5835a396f82b2059fadc9d51b2a39cc50edae2f4008b84e78d5d5c93d7540543d498d60b3a5590cd3cfa2cacce3ecddc82944d8c2629bff0e000d152b511ec2340284646a979a08830826372b8bc4d52615fab4537d0d04af9ecb04e025cc7b2a49fb3df8f14e05a8312faa3fc5ba82a5f662803cec3cf5964318c1b81e67eb5113b5868d146e0c932769633e02077af4ac2bee140d56f6fe677252a1bc03dd97b95aca195ee701b73875e0f783432e4537caa541e24f486dafa00627ae5147435522d3158d4b016f2a03668356fa4a2d5b76c74968ca86b9e10d378dd437716070c4c5a0fd66035b24c651485f61b92e638d6db83b0bbc2fbc3f068b8ad48068926e56d01b9d0f8025726da4205b65e7b0f445b582f061100df9a5da66f9374d1bad913faf7f0f70a5801b2a3a9f4b0340844dc6306b2f9594e3f0265a4a9183c7525ab467500891efbc6a592df5bf8282ce11d71a38e02e72136b1cb8cd89be00189c0a53d60bb7e6d2d1c57d8c045af6213d56875f277cc8035488e84f8483722b8c2258bd0c0a691d9f473f655ff1e8677ae98b112280dcb7be3c2d83c7f4ae3582933ea608 false +check_ring_signature 77c7309ed108df93d6d6bf74a9247fc546047c6c1deafbf34fd601c4b2b36403 84c1be5ca8e379c3a47acef748ab351258abf2a121d7ee27c808b77626e44c5b 1 e075e1acd91be272cc3cf803619b23d1ecc034bd4a687f9b1db618314bc4ed51 8fc48252dc5657601a6e8bb2222e9955e32ac532a4581bb337976ad45244990105e89be68e3716c15fc97e376a1ae532a229792e2b033095362372f3fae132fd false +check_ring_signature 20075e0fb3e2a1c4092afbdaa12096a775ca8b6eb2c61c19e89d73a301b7fb36 93a2f1a9930b27702160e42def5f8774ad4fecc02345eb90b139ce4d1e6df30e 1 f82184498614c52c79ed02342094ceb59beca38c956c58c2c909f18f0bf1d228 100628f427e05251076526722661c43479ccfd45ac36c329e036fc57e1fcea0ef9f9fc5cad4334784ff0b336eeed6331c9e4fc7f2e7e8b9ef21c20a5e4fb901a false +check_ring_signature 140bc5eb5c0727d8ee2fb7b0f12d061aa5be32fc7c0797526fbb6f80338f2b8c 60379457c32af4675236d5ef46abb072210e6a0ef7947e3f886996e1430f6f9e 91 043a2c1c6064d4d19aa8fe18a80541da4b19ec93be962b91e6fc2816402b2302 39ffdf8ca83ae5da25445ec4c72d68ab1b2967c43dc9d8cf7937c8c3bb312276 3efae230b49faaa8aa847b05fa0156bea1e49f199fed4e9b60e1cf57a05b218a 00bf865907daa1f7e98ae8e247a611a6426a2e5eab07da9255de1dbcf356bce4 2ec145d2fdc11fd714721815cc48a205e8d98862a27c94a98911ec3e25b76d4a 90cc80f78578e38eaf210138876fedefecf749a1a9318eb9ef6d1b70db63db5d 795c2cc60b73b98add46f26a6a5e195c80efc16d8c3b74f2a548d7a6f6db58fb 279a4c46be63f93e929f9f6ac56ae7c749a8b82e077c2303ab67cab76ee95ded d42081b69d3539f1c77fc65353f7b29daec64687804d98d3df6851799b4974f1 f4cdd2f67b217eea1bf9f7b4f89a95e4280215c6990fb08c0059bebb394cb4f9 22adac3c45b1f830a6687697c347a1d3dd5a0750f79a4e6289cfa6e3887fbc05 ef599f8c3fcd317ba9540070995337804c950649f2f0806e4ad3248cf0afb92d 88f2058cda11ec8aaef934500410ac23d98e46536e556951c590cb321f56f7be 9f5981d85568265be99536fdfef3e7af82dc391b959566e2b44750591b623615 e0f95356eb211a1d41a86c6859e2dd60f3566f5e9fd0778730c073c088c7dc9e 9f3c00786693e42e36d1470a50f9c5d1835c5c18633e6dc56cbf9858baa5ec01 486619fc618c78f471fdb1beadc78a1f42faacf11948b07b34ab3d7113f36db5 1c7269f9cc790afe2e90245f79f2ac1eaf0a14957b1878fe1d5df2f402d615c4 987972970e9a53cf8ceeb9f1cd6e2ef4822c304dbbd47f1dbb77977b33b7f897 17320092cd823edc65a8ee5b3e47a3316d6964e6d0504af908287016c12a819b 8c4af6ca652b150296ba08485d214582e809bacd99c5dbf351204dc73f74133c 7a6099912ac407537178091fea4159788a46dd0dc308ce3dfead3e1fc296fd1d 12926bde192faa95fef8a974aaf0ac9058ecef5e9e61a701bccd28090bceb308 19caf5ca4a8eb4cbfb9d43d6be9f862761c2cc981eaeb38186351f47016a6693 7a922960dea511edd3c80a79a18671e89a0f4de184bd563d91a59d748a3e7329 c427b692de7aee15813d498c481fe5aeca9cd5bd91872fa0d6eae506d1ed06b1 4085051e52c29b83a713291321d55e6216e3b12a947b289ef928b12bdfbc5c8c 9aeb6eeee23684f7a70554ce2e3053688062055a049827ff176e590ed48319bd ca3aa32163d7e84c8a25dea6de384c483644aaa81f3a5db51d69bb16eaaae42e 27538adff77fc2647c2d577265256bc6b1a009ae209de92812969028d192976c dc2a121acebcd219957096cb39f33248bd50f842a16921d17ce1589b3e46a2ed 315934066f24adeb9b5339fb7a037f4518e4489bed5a4b8f9ddd681d0a815946 370e4ac76d8766c72f9ac5e78ea71229c1736f0373e8118ca5f0ef134c7e8a73 20a3de395c4262e6c99a75a4cd482d70129f1d2110b731b8423647fa8bc0511c 44ff8975b7c179030aa55762c1784a4a19c805f66a5c676dfeadaeed3a890990 3f4d50dbe51431ea3bd8eb8cbd98b3295291f1d8e9c14e0ed9b6361cb1735b00 3a17375b7cd02a8e7309e3f891e88f9071bfb3b09c959f871009b26697fdd5c2 304d432869aadb008ecd1fc56092480310d88ff78fc0eb94dca086e7f373fbe7 91a3dfb93f2f33f965116f3efe43008b0686cdce20f4fcdbc2394abdea424e92 cad4dd9393db03530896f9de15df8fafdd35bd45c51f890963e61181b481ec1d ce086a690176d150b5838ed5e627c13ef34800d0f83accdd447784bf195e88d5 27d666240ee8cc1f93ec677e3bbb49468aa505cc1d9d28ce8f94a3ddb3dfc665 5abb445487a0e6720cd979e6a9b08315ec4e455ff8bf54b3b5ce3fce62633afd 40a0af0e6779ffe366370dd9fafd9aa939ccf9790315513c6f5e2a2ea3f34ab5 44a9bccb488114c3ee65405c578109825fb80fe8b2125eacd9e7a80a6c8549ba ad41fb4f4c0dd0317022594125eeebce1a74c9c0f086773e1f84dcd587721cfd cf8953d0bd33ccf7c5becd22a60c47ab7849252fb5dd60c82a1771dcc5e976e0 542db3df165814f552cd681ecbe9b58f323fcbdbc024339178ef927a9ee59167 831262a0fc1840eb74bc44f31928116b5713aee1a0bdddcd23131014070ae29f c91fa3af071de7dbec9fd89dc5e23934873a99928419e88896944f761bd44f4e b65aa9ee1634093ad9ba6480fa5660f458fdcf33fde027804c9eee4ed6c90d93 f02c99f4807628dab2f94eac8e84522bb7065b606ffcd87b45d4ffa2ff2ea480 6a43375b794fd95b37a4c49d3c48b13cb7268f9ff5e8191bdd7987706b5e149f 23b04b3d0d2f91ef4ed2710b7dad6e1586f78353f4fe9121bd3bfb409cb50dde fdeecf288c32dd52801fada3bd8796e45858d874b7c2a025ac0c2719264150c9 2fa63af94b4fb3b4a2f50220ff6fe7374ad296be0d048e7598dcb5194df07efd 994c80093df57ba9a35710caaffc78caa390fcb89233aaa7630a5f8e09c3461b f582e2065bb31b7a8a836f41a32b615672c9a702939d8eaf49eea67d2c7e8374 7b039a5a3a9e8be8de9e1c3886802ea70c5a4700e2e15745f62ad902289304c0 b1a34402e9470ac177c5cb7dfe94753dc94a349e77b97fe8c6f1fb39882af2d5 faeeefe14ae660d9751bfe31a1c89586feb67505af3cca590072d9bd52261f93 ebe2e0543d35f40996a5fb913757f3b628d8f8963ad216ad404b6adb32245017 32a88881bef89cf5fb6c26a4995fd0ce4581b738c5e1b7884537f2c41f090d66 d74d04ed6a12e73b35b5549007d6020c14f658ab69e6abc7fd2a8d3fc3013f96 62e944302c2e4d4a47c89ced31f3bcc9640f9d1de421a06a44daee9fe3ace32e 9106b32c08a579593edf1ba0e02f54918b49f62ef7f90823b7ffc85fff9d2b1c 0513acf0a139313cd02e3583f3a2a4dcdb1d483e6fe581c3a820a5622f459e45 b707c8f73f0c594f58fc10b02cfa986cb296d812f1c8cd7de7a6a2512695acfd f7fb5d399c222dbe5438b5fbd45e3c3bbb710361a87298947750d875f990e2c7 65e0979d44bc8f2589e458ba7604f8edb080c5dc85489efc481bfa057c484459 c7dd133d3a268049f696a9180182bee491565de0e738e30396788e943d1b965b 91e9486fee3b69f99e9cdd4631b9c6338ad7f7ae68492cb9de07cb00b461146f 3f16d8614006a91376e156f082308fb8cb43783dbf684927a40e944271b29133 7c81cf9f7752cba40f5321c91ed964ff406a43031c7cb04ed866b9560f3027a4 4f6e04c1be9671f93665e948a1c1e232f5cc1c41fc56844bd04b6b74d23854f5 68f5618eccc371a0b6bd449fcfa3dd560e96385c8b7247484d8ebf667ae2df8d 76248a93398e334f062d721e0af5937d3b0bb6cf38ec5570689b489b37115f07 436177820b174e956b5a741f563ce4e80359f8f0984a9e78afe65b10e91da8c9 f93feb8b7cc25b2c2460436a117ae90c6cd9d4b22b2b03ff73e047c4fe9d114d 1e7780a95cd20b1d4a8a929730c54ae99d795794788f614463c854a98dd42937 2853ed5e8b6b9244904f2bcf65e783d2e846e7d7caddf8b1ac46485f797b46e1 4de94c05a5b760d46e25a3b464ed860a91d710153c049e8744af2b731d1ed84c 0341d2ac3f912a022c5ffa7e5da7e05a5b9f46af9dae0c9182950c41aae870e9 93e44df34d05732e460f5213bc07d0b063064e2d9ea3b7016b54107a298d6d69 b5344923439010c182fdf8ce90cd831960045f04d0fcb227f1fb735242ccfb68 0158c57fff8fc39451caadd118aed6d9f53becfac93d8bbe476e50fc27fd6420 88a28a99be9f92204a2e9e6088947a7507b7ec9fa80e4306c5881dcedbfb928d f316c92bffa4cb0b9da6edc2ba2bae663c655ca8e835d6660e14c4269237b1e3 f0b60a4fece5e41ca8c2e2277dd27269930d29ad3c846f155147a029c875a81f b9ab8be7d5ede91cbdd631e62a447d723e5f388a07f44e4c6d11efea116b44b6 0bccd3ba17824213d147abc94cc30f97a7a6c991bb3818316796dd458a94df9e 4bb502f3d7fb373b255bcb3c53eb6cbd807a46d3f9cf9d22b1a7d2cbb62b0100940ecd694ca0d12041601f70da104bf591d85f3a85d7a3c3f2b212d342f7d302e4f5ee3f2a73d2a61323f16d76d1bfc36dfd8d278810fc3ae8f0c17db376720eb9a8333620b80099fab6623b6e27eefbf40a5619abcb8b5ef6eb90abf4760f0d740ead466d04485d4238cb6adab85dd745c976ba909944866efb1b0ad29c850a728014f40c53f9cc5b6cb0ece92cf26a59154e96430122dacc5ebb3274fd560cfee54a3ab625e2885ec8b2100101f8cceafa5178ce216c0541b460f011431a001898a9463fe180285dcc623360b0ffb94f8505eae23d33009b69aeee2db55c0d6a52dda860d3966980927d97cfd92589db32ee10b54f6f1d43308fbbbd84cb065ef4db264e80665d355f20ee1d767a63a9c9791ea631a629ae1ad0b9d09d830919f0ae5b5bdf027ce5ca7e1e156c360ce828b50d88f04dc8097fb060ae04c4050ba3f3e45942374378ffa2c83025858c26f24e3846ee641c992965679f55e50814b0e7b988980f61b53a4c1e102b58a2e4074e6cd1eeb4d4ec335e0f9fbe7b014e77907bb6b15b73106a4a12dc91513a5ab9fd2d2ef6e33777713b5549ac6d044e3c9238f1b541168226a470ee0a831e9d018cc1808cde3a69d83f1b4a4b6904c02dab13ad3c5656ed1212a28372e876a5cb352308346c0f0b971b598b12e9019352aad2dbeaa6ad161237ac6a9c706c9cb19cd1212ed526a70a093ed540830cc0a1943ebf80602a7363c743b2c5c763b79111a096226d0ea5db8c9f8000ca0517bfd9b0fa5a0aa3e1a820a445c79d523eb8b240b638f9d25cf0677646e78c09fa1cb4d1086d77b37d2c0e8188ccfe04b123d9b48bf31c60c2b6acce8047b20759b8cd60007e921c1913f4a037f52f1c8bce30e95cd6469a55042cb77b93ff07aeb5eb4bfd580f99bc8d2bb93031db58ad7673f68fbd80ab8db9ef5454cfac0f3a2c16a96a46995a04ed597aed657facdae6ca930b647cf9d33ee85bbeec950dfd50d1634183d98fbc7d4ef4f31706b990cf0343419dd65e3c15962bbf2e720ad16c3a2ce741ff968ea5671badbfabc8969c3e4bb9671775c765e60593dce300f2590c40a430e73e3c89f38136b4eb578a8754ee00b480cb5d842975bd0a9c0a4f0d8685d625f53a1d481cdef4505dc6abd9d908d219b1b0d7d2f06187507b02dc1aa749ebb8bc43231fd5adf2707a634063adfaa475435682bb15d9e901d60be10efe91601d5522157fa9b22e8599ecb0322aa4fc0b384213217f10f96932071b836e7de4b7c7a5e7a208865d400e48ff8fcd96035b728830c34e7d79b9fa0295ded58ba73107cc3d45c393b7a88ebca441aff3df4db65b98443bcadc368f0222d5221bfb0296856540587ac7bda5b35e3f183071cb2e696937bdfa96211a0100833484c7b6fa60c34c4c44075402ff3856e1777852fbbcb78d38d458726f01a7768fd48fb3f2b5a076814f483d291aa1d4267ae4b76be11291f3ae0d10e300a3ad09ac5fa0253d0a67bdf4f6bc6bf5be3b38e1e35369db55e3861c4f603108300e4b0686acf70bcf3dc846af8815cf6ddc629f630ac1d523d4c96fd12ffd04a3b4c160ce39378bc1a8bd85d54c24fd7de1a41121d51ce30d0b8d7bf8b7480139ae61ebeb9620fb30051a8bd7e75510ebd8c611363760c77ccb0eeb82b0340059f2bdde420ddae6ff0983b5a4665ad2265022041c3ccfae821ea839bc944e06ac86e4a5572c3e4acdf280e557eedd9799a35254a7d1a384fa59a729b1add20f0f74eef24face64bb3e80f45de10ac32571e964be322b701d742418cde9060067c2d1fcd26588ed9ea4a4e238ee10399bb82bc905ece2c6d205297ebdd3ea0040db5d25015f688b72be133a75cc18624a6ac0a7c80bab98061e7c83764ed690cd1e5a01c1c982b7978862979b0bcf32b02867be5c93e86d52ba8a9fa54159309a171219d8c76d3e6c81e8211e26cb784c58a7f3ff39fc829f4751fea939b6f024583094587fcf08fbc5dbe82b34d36914521a245a2883a5ea19750c4d15ee308b68368248e545849d6a43079316c29e1df27068d6bbf3e7854d580fdabe4080986112cc14875c82efcfa759244b59587bc3b3841214a7f7f761906513dab630be2e5791136fcc5e263bb942216bbdcfbedcaabedd1723cb16c042c54082a3c0d51458f5cf2423d82395d5bea7ffeac000a18383a7dff0cce15f400cf6f95bf0c10c083a39030a2ef76ce8f4c2f11b292aaece2d1680b88a7c8e758f4d075260e58f5a3a6018cf49faa05854ba270dd085403f57de93f7d6718dc9e96b513c50390967c04c5ee627cdac103da04c8267b311ed7cc2c138443db8f721dbd837f0a341f89797ea989d1ac6da35867c081cc939a93a854c929dfbdd485f83773952ed1f687d12ec7ac8f800c0daf81473b5eae4567c810d6634b84b782b3cc7d230afd9b42a0112e2e82fa74b9b9d508626fa947dea8a9fece8a68c9f57eaff64309f8dfe84e704915d7737ba8f9984c5a35f23c5c26a8008a7988aba6e46037f300a2e860befa4e50d1de4c56d538c5c7d97d367d5ff23dde8021d33819cf8ade0db6b3bda0e3ecf4b1540f7581f190a071a5ad8fcd5741734c536ca42836d9b30cdbcfb60193583ac1b534dfabcb78b9634ef06b9f2f3e9b0448cab4ccb5803c0be47687114efe151579d09122a1cf4fc181aab1a305960ea7c4a5b336f91dac0da243e64dfd8af45a58c08f9f64e6282674d0cb71bb9f4baf84eae6be80479a0742858dbbaa425882a5970106fdbfaa30fe3b3861ff58900945cea56b6ac901069331239263af15c9e6ac2d6e6217cbc85682c3da993687e450167d9cdf713e05b0679994a5639cc2ed6f4d90a2eb6f63a2db515e9ee56fbcab22742135590c00735d8469e1ecc576ec1819a9b1dd9773ee9519dbf14630ddec1bf2fcbb449b07f004b7ba479c0c618e832e2f3b1d7ab9b334f4ae425ff0031459b6e4648b2b0842d8ad149f3fb277cea151d0561eb872f23ad9fb98a6ffcc8cfb20d78bd7ea7a93acff0982eb790ae9bc4f6419411a7af8421d43dd2143217d263bcd14ee0e0136170f33ae7c4a134bea5a5911abf2e88dff4fa3fa29c518cd478f4d57492b08b70e648231e8c09c96060bc8043e25beb3f2b8020e6a9266f16d297f92a50d045eeaad6618a9874921cbe99b115a0cf12af19b547e2388df293387bab49a4e0d7989e9a85374e75948c106dd31918b175bf835380c3b858b546d9e0dd0bc420a62e599d73ceed149a46b144572fa22a6f879e00f2f2a8d507a1a60c020723e043819461f5d7b46de01762bd60547e91c9fb19bc44e7a41715692e366f66797013f90943532599881a4f97ad0c97e67f7109c8ae782af907774da8b06d9082006bb2a18c0cf2f7b9aac768fa0bac976c60aef0740657c8f17fc23c1ba8a3d43090417ec97ef610281fa142e6d972963250d75696b2b3d2762f78a6762acf8d00684320fb93d66712ba845f05324370788722de0c79760be75d6a4d68265dbdb0c730059e5569cc2099c18352bfcc0de0def9f7f8717a415fce1036efc27c6220b533a6eab14936408f370fd19a9d684bab2a611770315eb6919a0def3370660059b5b8723d117e508eae7a27d32c7d74ab2818e53470b96f6b7442630ecb5ad07faed173fb4fa2296d48c924ae7731a2f0bf5fef7a0a9a97bf3757903d2b3260daa12161277962d5d6ed521040a0ecf7c1b77b0373a26811aeaa77601afa7830f852164ce00a39ca1343b39cf234a3068d0bf39959ea6bb96740d24ac6c73e20ce102a6f5114f94b7495e3ffe1be21dbdc0bb1434af43a774875069ff2d306c04ae31ee1c91cf6c8550a05d1aa9829cc6ed8ede4b8847c747728a7fcb5261b60984937f0ef7759567a9f38667c9eb647a968b7461a7fd58ab486ac14ef9edf90c17fda54a3f74e6069d1855b99fab8cee5ab2e64072e09d63e4639f17336080093668d96af2ae0ae709640648d336a0f5ba0a42bc29d066cc5773c1509d044904c43546dc9ed8ed07f1bfd9575b03fc25fd853df0154499a827697c5a4d5a4400d258ae5b62a3a20c97a9c8f82fafc2ec26ac52935cd2afca3c85160705538202d47706974a125405be4657f0304c331eb6903db4ee48b5cff8e2d5577bd4350264f469ffb80fddb437011376bcf41f66abc9dc7b0f172691c7e18c169c0d46077c64fc17f21c88acb4c9f7be48c80faba7e252a770f86d4a7863bff8fc0c08096401134acfca41dd22e0c574ed311b2cfd28106d03a3fd9aa618d9bb63d8470e63c6d4017231716eec0c8f274e7ac51fee0122a6349ef84b8cc54c49d1a95c07f221337783cffc9a0423f8638b2b8a80bf9842811963fc79762563da1a74b504d22f96d89e195ed887aa30798d9602ae7b56770086b20cf15553f4cc21ebe80ccc60425d6e81feab2ca5187233502106c46d68198a24810d759e5ba98789160b75f3fccb5e6e9a8f23225abcca9ddbe52c307373271dd6dbad48d583d512de05ebaee6b0b27d029d9cb7b94b75cf2f0b3551bc5c9df440ea486b91fac7beb10528f089fcefb39045910ff08403962debdaec64842d348d1bb82e9d4553aa46060f5997aefc7a2bf108b16eda6d262edd0db3ad79fb95f25931fbdbc194fe38013a7617d14d80cd2d940ac18a753fec8d2866db3deda56b9407d920c963e20002bacc43d5204116d293b3c841a2f04195c63d7eaf5523adab6fd01551ab35a10b6f780fb8e6d936fbb5f003b56f07dd3374c03971bd06bbd86974ab1da505570e5f6ea8ef8a391c99231bd4b23f52e5efc1a0350568fbeac3aaa3001eca32f805ea6c8b7d67195049c10d25b99b0f7fcf0176ef69b14ec8943de18a23740061053363571878093ba13c51b26ba6e89282465c2b03ad6f75b147ff90d9330f5e085f2e02d53e5b54c3bc4d26ddfd103a2a327039de2b0ed95c132afd4c1ba9870c60d0ebd84e439ae9b280330d51200b20567fdf5e32b7a4af198065a6025b6003f9d8a79d3f04bac748faa8dd809d02c48cb9dca60ee3ac84f6592193df99d60cd21071e40ebc14ab0cffa0ee7c7674490d2fd36098710599660a6614fa1c3209182c507c9413e2e8f425f0a99a15f1bfdac0bddb81e48eaa59b5c60546d04908e57d0ebffb5e47ba4c8c3326035b2a1e99102eef2eeea1af99fb65fef0a7b60059494fe496805cc4ea21520db620e19ccb18ae45b634f678e7c0ce2248af4a057714fffc0d90f99adbbd15c6cfe71be75cf094935b3a6f19f3cd6a90b26d260906bae2c783bbab56898770ef4340949995d77fe02c37c3108fb7748077e7e00705a3347e3bdccfd6c43304460f3f47e657228121065b9011a8b1d60387a14706ed4da65f3f5676aa08ed8bcf4bdfc921f5b953d10d4cc07f5a283c09e3a1710a155cc91751e6c2609488f14135046e3ba1d7221e5d4ba121989cf4341622a308ae2b42b5c3b0f53286b184a2f3bd273933647baf778d6c4ef366096635030a0206fdbee6e34b78993d47295ca4e24d06682aa4581bd243f3f507a2bb5ee61c042261afa32a9afa89694916c00abfadc0289c084364fd96e0b3a31216735940074e698989b13887940846a9e9816dff36f55b4c094786eeef679bd2edb39fd007d474c9ab10f731771967005132fa0539e4eec8d71aba389cc4b517f60a454804da895b3feeea1b43b849109917aaf64a2c7d79a14612a970bf1d0f6e8bbe120e10cf205c41a2668f52545e8812bfd13a8a8acf7d5f9a9acd7dcb304b4d89850b0455f92da2fb58171172756242176217637b3d6bd421debdaaea79773925060037ba6a337f6a0cec50c61e904ec833ccac38603cf02644c28c5e6f79397ed50d2e0da517617f9ed2892fd5d8164233211310dd6c6507ca2ff95c8c72658c600b3078c583bea0a1041f1ac4c5d1e05d5653586d941c9e0f290f2cdb712b39f60bb419420ac88919d5d66ce54dcb1fabbb5b2571f224e992bf3f33ebafb20d5203d106806a9218938997c02b9a8eef60d287b276efbfd72852251e548d4efd95063ff53c61e89fc4724ac20b595bd7440725c0d4121bfec198215858780a0b7e007e717f02f8d4c6c3c005bcfff5c371db18135935924e23a5a70413f724b0910d8d0888418c053d2f1962bf5d8cfd1241b0b8f76f56dce7191587320708062204ab08845c859cee62aae19f0382519a66c40eec46fa8a8a6f0505f58b0198af0361befd2822d533044981245a2d6f3ba4e1824bae5b21d94d195755a93457bc05d859cfb27170fca76e22101b99a3b72409e2a8a10bbb836e8edbd47db763dd0c1a7e17aa552155fbed7cbe72d1a01c7d6b601708f53a23fa9b732e4d1d0672087cdedddde79fe41b15bd1c4770eeedb40586907918338ab853c7eedbfdd34a0903e8afd05a3c191867cb3b0e1e71d9b7a272bc12258eb60a6c4793d62623ea01b2b294fdc7dc28a4ad882e029522d2965029faeed0b6861de2953cc429c4cf0adaf22f4eafe23c4d7787b19ea19aac4f0f164c7670425949c634e7b564c2780e0d93164ecf373996b39355dde934dc007322f8dfdb447c1de751f075fa6f500480136c925f252d68772e75d2ddcf4ee9805f90ed3ac374723ab4e670a0b4ca025f8ce27fe82b71649a9b7df6fce17c15c93dc8552d09bb0ab82e599b807c320aa88515ef99f6eb6b678bf9e2a5b37359e3b5b542cc89cd53b2f59e62cdff5302f539f200d2f743133546bbf1e694d9614a8155178080f0aa671de2468cc30f00bef9748add534f000fdca7e9700a3e7270effcc9e91f6da9b0caf18c84ff420882fa675f0779aeedd9cdc2bc90295c6fa504ff3a4ddb46c43caa1b256095950ee2bf8176498716825df103a73bb7fcf22e2f79ad060ca822ce686d7ed981330b4403ecff728730e79d230e63b39937259cc470d1163e2e3f9e16863f7be8670746137e42ed37256753c6630345ef1a5280e474393eb44dccb6f538301372a60266d0022d724c3e02e94c8d624fe9cd061198a96f583f7c3626046de867e143070c40a9044cf9df486247c3f15a7f3ea609a689a95aa353b075881d18d174a20f334697d714dbad002ec4a9a75b347594ef540d6c29f3604a356f168207bbb70ccbc81ad99514356ab49c396dcb05b5e7bb292d7d5dd8da9e7a2d574a0f36d3082fa41ebc1cb110525f82e47a74892e7311e7f02fae16e3aed6b370f2f3cdb709b1fa1ba9cb28f8b80495bb093252d0110747085edf073def49454bda858ba3022c28a27e90c6d9484adfce0803c94c52dc693a35c1a490d38a096065b95cd00007b2e188542f3fa4ba62c218b6bee2f9b63a77d0ab9990b0d97c3a67d283610cb2854cf67091986e8d429ffe9e63edbc00ce9500ca8712824c901954cf3c2f06fa809c438f25c07f6428367b5020a37c125a539f9f14d3a4dd6c675be9e4630bdd861888e553838e6782e1546b1c71f59586f9d5c7624c578a0e200770ea3f0ef91c61f96158188e22cc3c762ce9d780a9db52bb86c6f36baa8c3e0770b446024c75c42209d6fd6201db9f758b9d32dcdf6114deaeaf62ef48eb64342f6b140c13f2de67214248f7c32c3462c1f3454e7c0a810913ce2686cf4f1e025734740e10a9fb5f3a27dddd2fbb4435dc00f35f88ab025f53f7f56b10a87972765d0a04bc5214395772e4234e36198b661d07986dd1920d7c1a739adc15dfa8c615d7083f3dfde1d2a183e093107fc468081eca73fd085b6d4f1da0cd497708c579490d02189f8f65c09db9a26dc34d7a402b9c6d8dc67386a75834e1f4159253b8ea028ee86213df39e3201ac478ea02d3ec2e7c8e883abb247546a5355c47545e250c62ef0c6f3945a40c32c3998df98138355cdcb600b4204125fdf9ba0581ee6403c74d04dac5fd99df9a6322448094af1376c467db89a13acd4ebca797a1c9c60684c267b32d92500d68d87d466522479bdf659622e712b8ccbee26ab47476d3051e09e815f9195977e6e24d1f6e2ee16a1179500a01b0956c885e62176298fe04e4a6987117f1f2ee00c4cee07ff2280587274b385419d8b5861a5a1e0b95a20ddaba0cd75dc635f6c55dc05692a7acbf6e96e20271db674df7bc51cadeb5460fabda7b840bf5442439441f623d230b4ca2f8a5de206e4d0697e53d88f561d00c false +check_ring_signature 4aa026a23a2a79c48e65b608cd399ab61d0d7018fc91beb3b32e4d3b24040198 b32e4670f6196ef23ab97f6b49c4d3ba93f1a912a3d961f7606a6bf202983fe5 63 c8f941526d4bd51805deab0598bcf75c44763e33ded994260a01bba21cce6f57 f6e4517bd28717fd2eea94b0ab9e60db65fd0334b0fb83ae1b8f0f4dd951545b 02f46fc3c2e7c455d2847013d699e4435990a088b7b34fdf8f467f37c1d7cc9c 981ab44d70aad301da36e777f67752996aa74acc68c69a532915eb0a62af9c3b c6da290d56854e9f0d1a5562b2fbdc032ae94abf8255b69e01f368411c8f26e3 96199e652ed10106b557c122ddfc7a269e3e65ed33ea0846e0ee1f3fcd20da33 12745701f80bb15c55a24ff1deebaa20b3ec4c2f3939b3428df8ca754a3762fe 6f4cc9443dbb7f4f4d4c6c07a2ceaa39a4b756772be802d9fb999f226f324f6c 65293fa0cd2133752f2cbb811316c1f3f2b60f494ab45b798dadb7aef85927f8 38d96fae58645dfa0011c055fc073a206d7946026383d6148f8d703abd1d8e9f fa48cd056441a6b89c661c1c709607da100f065167a71beac3e244ccd6f73ffd e87b7d98b3a2dd2e4b4fde2db6b2040914d59e49a7db77aaa2e67c226de999e8 128922807c2b8eddc9cb3fc74ca1b7617fbcdfc503609120a97ed1261b5df7be 2e417bf164849c6dc55fed060d20002e000d161be041d88d6fbe8c343608c96c 8b7e8d692e385bea2b0c56a06c13a1b9ef62686eccc18a6fbf63af6e06a0ab2d c29bbdff8d7e7a7498ad79a6c58f8b1aae2f0b57b72f59e46715f25e72d88bb7 204362f00a101e3b3021f2f382b63b2f2a7e735f038f737cf6aa65fb994856f3 ed0baf9b06ce20151396511bf948caf99a3d27bf8a38ac787dc27b2d4ba8d012 afab9471b67b6b073f826ac08ebcfc564d68de7c43bf968acc851456ed5ccda8 25357fd03956f31f51f05fcef403b6d88063dae76e647c9951509438e1ba579d 459542d35ec977751db7cfd0fa2d39a7170669aa621faa1ba905801e5b353722 71228f2512c84fb7a51d78058aa19f18e70190d6905008fc531ad5d2761ac89e 4ac8e4536ab751983343496611bf8291f2eb4799230d73b3e8718c648ed47082 2323372a78019bc506cf9fe8b39021aaf6ea127e98f78e43dac502933603566d 5897fd2c6984e28381bc31427d45d70010e254fb383110cc54f51584c9099369 7742e4a6804072bb8b116051f30062b71e1ee169a5d5d893723ad5b608b1579d 2e5d7ebd117fe39d02615554e6ec44668464dffa3e3aafc348ed0891f607f8ae 58373a916b90d6c531fc20739c3e2e7bf2ea08b60fdda88b55acc80bc1a81bc3 bc142a434de53895f74d8c4e985bdc4c0173f5535c6cb4e86130b5dc288501b8 ed5d9447ca91419659655aa84b9619871dfeb9ca696374a3829046191f5d5d13 4e79fe8f08dc17a781890d4ba74b876581dde77bdae1d6c88f872b07c23b7c38 85fddd2f51e80de232854cfc5bc3e91b7318b1a20610fc352d99d45c208958f8 b2a96dac7dee8bae8a319e4911a6d95df6b57690b001b99d1a907dd5b59afbb4 1088b052acc06ca4a0a8f41d74adc42a2aeda811999eacadb4ed4f5859fc1e47 5479cdba18b7067b326ffb983b3869b752705b26a236a2a4280dfb45bb0a3bba 4590a038f14207c1a60927600fa14a4d88e872841a52e108f4844872c7d92a73 79d8d35bb65f8ec858d34269001115155493f970dba571b78d34bbe1eb62f716 d6512a02d5416336a4dfbf5eb0b8b47eb538316f9b9d69fc423c1b014bb216d2 6c0ccab3c4ecce8d3fe05e7ad9e23587c8657f4788b513274f7f7d9cc43f95a3 3e8bad00d388f5298beee417b0209c28b11a90bc22f011b231aa34b41b832ac3 ee42b53bd39336ab73627056dcb7aff4f3ab4f2f2d31484c0f6d91c7396cf3af a2a43c785e401e5d1b9f29a19af13d42d5a4519cccc845e42d2dd75af3c64525 5b057889550614260b2a69b39d7aad7a53bd1d4d35bc2d05cd603657d8886dbb 4241042b0ca1add963b58d289c31429000992b9e30e40769c8e77d41db195ff5 3ad51dc82168048f0b4292e5fb7a653c525526115463f86a873a23f27c9339aa 77fcbf9d7e989a09d68df37e0b0f6f434bfd7daf4905f604bb409f8ba9a95d55 b51fe48625da7adfa1b33d047e5833b8f3dedb89be90247103c822901aeec783 33fc0b02dc0976fc61f89d6fc78749446d5b1bc6f3ff0b827a480f24c3f6be83 1758eda1a18f886927acbf4f9115fa82eeb44be87f97ae8756dc09c54101a583 eadfc808d2f5a06250365fdb59dbfce9582d026c38cf450bca5db6271e5c0027 0016806587a075016d27a3060b18eea20f1ad6ff89b0425925a4e848e5ce2147 dcf8936cf67a38be1c253ce468ce93185c4216059daed8443ae0e7be86f25041 5cabfde9a594f8cefc4913f0b01f5cc36d35d4a37ee59cd8cf4da7560391b9ec 97d1f3fdc2a16c0144afaa8bce01696bd281b714898462913bee4ca5fda46dd3 5ce9c37102c7fc27fedda97501094f0eb8a70183f722c78c678128c99e561bd2 9672e43832d256ef3e5682fdabd3cb03f6e08debb953c5ffca1a49a092708534 580067e21cc40e623c1d877f3dfc429f42fe75f5dd9bd86be028a5bdee936882 731d2767389de4f195267c041820ba8579e1006924e15d07a4b4f11e543ed87a f165e73b89b4845688a51dfeb796cbe0f1a505d8e16704bd732b3298c21dc753 d339b8393b3e5eeb56901d47ed49ddb8719dff864e11186eb83682fa64da7e7b 9234ea8ed2258a8fbda13c9bf6f8643c59839ac209cfeb60f5b89dc641e85876 8dda7f9a5b62374994d45059619662c4d17b1f48c06b72bff2abbf875c690513 8354339c4460882134f32504a47dcaf81de462e027cd361802ec6197cc90af23 dfd86c894bb5bbd57c60929df3dbbc31eae9ada36f9a5ac3950b91b920e91c04b370422323e95c8fcc36a28fe92f4202d04d9d90a666a36a7bb22c5ec6ffb206f127de8db7c2d83f112cc60990a40ef7bdc4c81ad6ac81f6fe8428b20b76a901d719281d6e75f98178ebde06ff2b4be7f292f11a814261f9da91a2c94f56fc0f7e00201ac50bcbf2016ad99b3f57814d6e13c7b893a56bb1efa4c58e9566280654f93211f52a3bdf90a08df49560f866a9361751011f243daa7efe91b44aa101c961b0d296308edad1cfd47f209f3503363a14bcbef1a283cbcc5df29d513f0c8f80e8344f108895150fc25397cedc4bd569235593b43e59284592080748340719fada952b69548c19db5f9ad49bcad8db160a47eaa6b59851a0bf9e6f410d08a434800045f1363d69a366ca34d58ff57afe0326f1ac3af5cd1cdd91b14ea80391d2d8aee66064a76f61cfa320f3164919e3101395d6f8e21761ce343dd41c000152cc091528be676e157ef546d582c407575e7a9b46d358c299e8d043261c0ec0455f21df1e9480838022482d38147a993bfbda0ae7b1d43bce3d33ce6c6404f1febbbe0148d506b60346539f50952191fcfa829c0c25347e7e0da98a79280548e0eacf4a26f9092fe96bbde6e83b69207a9c745ad766dc5dd9dba96a0e7b02bb8b7c22bef12589bd1a9688504174f234284d3b9607e3d3b92baefa6dd8430c30c93a3a72d97a98d4902d042157ba0d1a4398198f74dff5cc2ae3ce4f5712079773dbe2e8afcd89ad39847a56aefc17b2e929ab8fec00925697e62622f877030db1839fe37c487317fb9046fa04e31c2365b9c49f5e86a4dbe814be029eae0ab5e4c4e53c7efe87450bb0661672addb23467c5af7452abff2ac31247790a50981b29c3bfab299e7f01c39b3aeea591d9c8bc126693b57c8e9ffd1ba940c4608e418a90ad3152c98b9eb32be27aa1719af4d7ac4fe8b50c0731a6b4ceaf7550359ed35fd51ff0e4fc25b7b6753e37c3cf398d7cdb713d4adb21c76a65574e90314906374dd86ce817871f27f4551156e9e6e355e1fb86921f72d98a2173e9c0dcd9f2cb431f26a79728d588e12704217292805ec4d9684ef5db09a2172768c0589a5475ac5d0d20fa9778a5fdbf74e82bb498ae1091530c802dce6370f13d0077edfefc62a75d48b4a05a4c84ee4ed4841d9f84ee70cd5fbcbd2d845e96f5c046bb4b53527a58f6440064fddc93bf70595ce2a8a7b5fbc57845210c047336504732113147810df6513864accb611addd9597bb4b252eb97d6fe5b72ad8b60f0be620ead02153b3725b591f26597d9d56ca79c67840ae72a733020603a71eb602e7aa423ab3199f3919a0408c9e7ceddf75cb9c937515f444777a41df1b5a7502519d516fcb6512b6f0c51aebf6cfde3dfe71d48ca8ef8fe2a6b946d71295280561a9f7463090be61dca85f8364166a95c49ea8ab30a6e17798041e636730510f768eb6f3091d66561a76d1e99b89cba921f1aacc5f878b3f66e6b47e63c8dc088ea96a656539d73207f9948feb4766bb242bd0c411a243e917d2a98090fa070aa5dbdedbc59a22938429d7f1ed51c59f29ad5ec90ce9c423f98259b89e73cf0e624b7b665523017b964ae9795c74a7c56fbede13664e588a1ccd65ba94595509fe6435af9915f810c7405eec852bd308a1e5a28a7f308584f0680a5431aea80715f7a2c7e4b764769fed8e3e9b4fcb228528b3f9edabc945cb6759361866c203016f26530e4579b905efe542ef8dbf07e8c112ba39949769ec133e10545a4f0b7ab1f5e3ce4259127318fee10e35ced5256257a6d7fbb5cf3f148ca01722480f499ba964804ddb3cce39541be6d2a9f2a2b192c3c85c2d65eb24a4271b33b903eba692711f0fb7aaae68c213df078e3367b556d7693ae63bf5155fdab1b4c30b556dc00ffc74cfb99085f22409f5aee68b0afd104d7cf25c462044b47724bc05fe7ebabda021e812b4df6ed65dbd7d07ae0f9ac6a85fc1307b769de9213e050cc40c2f9ad2bae548fd81285a270f0ed38ab8f64b8ffcb00cd1141341db6c830cbd3cd1c3babd0ee890f71072ff93e21b11c8d1ac2ef0151a9c33b4ee46063f0e057da9f99164a76ac7daef229c67ad9af9eb9bde02195038abadb9f11072e40f4fbd3eefa6582ed1cd920bb1f1a778474360534f7bab0400b36967ffbb572b0ccbf77039fe17de82ad4824a22940900ced2373edaea661e750d289f9c98cd6007f186ffc42f415c09ef595d1d49b7f02fd9d391fd4fe621c31004a76e67ec40e6219a48e1dca8a3edb32487788aed5d67ac5e2435ee132f39c1dd8dc1ac0c00ff6db7d4d8d130082077f0efce32b8da611513df757abc7863832c973047a3905aacb390b86c2c22adb7269769311c6c31906a0e4bcdb3d67b67d310b41c7580bfb4c6224a6c9ebb927cccdb60d0e2900303983c3cae3133e46b4d221f49b380a1fcebef7f97d9ed815afe05a56f32bfad2f7d9939b16627c454d4614c1abb9053ca951aa41a3220b56754ebd6353275979d3fb7ec05dc35b2912a1b3310f9a077e99d3020b303a17bc7c43a4e1092a7b90cf6eb6b89fb786e99d6f99b799300c907fe13d041cd8678b76fdf206d7533426df76ed0dbf3ea1eb3915d2d1aced084d252c72f9c9e69568659db514ce40e628b51c5e3f8fd708746d97e151e2d80a8b23b5ff4ddb5d80ad940fd2d61c9c332b6b105e41fcdb6b0d39df9bc449a00bd72fe005bf2dc5a56e7efec5da64731a0271130a79bdf5a91bc891e8af66e6028fe88b818ea0cd53f4e94652a0eaec6f4f372f17cfa329a39a57abdd40d32d0dc084e75ebe58c4a106ca7fa7df41d980c902ff85ca8de540514fabadbe4a4d08a356643fa08cc6fe45cafbd0ed05466bc44329317f32830cf18b8efbeb457f0f9e099db28f1141ba04e85dfa5821ca52b251c02acaad837e089fe50cd564510c03c9e19e6c99ad92f9fbf738fa4b3a986b8096acfce4601788378018a87765080d5173f22ea4aab0ebfa5d36f3f38338ea4588e3353227d80ecea0be4b83aa0e5e82e509e1ffb2481644ee33119535f5864cc6c848d5c10a4a3925d253a2000fb635e25b8d877a7fe63c4d0c79fd67b2f29dd5a8024641d39ddda848b8f972031f5d9bec82ad2c7829b68358f2a098287bce5da633856e0a895c2ce08c4fa20e8b85b263dd0072dc41e9cd145cfe0f81e33f86e17de114f79b5dbdd7c6f2a00f5c65502f32a409192b8dae7d95ab97d72a0decbbd404358e82a88ac34a402804dde2190fe9a6371707c78696d0658200f42140a4a2c71afc25637a3cf0c46f0b7b6e21c06ae9a26325eb3dd343c9197eb0063237123b0a76ed1c2b286ab3d945b7dcea63d6e3085a178c2819ed08c6b28453182c29e0f7a878c75147001fb7041e97885b0945388852d8971238b492070c81f7228e256c4101d6354d9540bd0e4c05b80fbc632d4133f6bc9d3d796a5bd9bcb782270adee48b306299747fe90f65e1fa3028bfd54b7a0ebd3d4a6a0ab35787a1d1be942d8630eba407fb7d3a04a2628471334ff09e070a9f307d065679a0565c8cde97a63631fce56f6ffd32047f445a64d0e0257e3cf8c7966bab0a46c32cda3020702b8a5801c706c161bf0ea4d09c01f4e135ec37005d0cf903ec62c4ee5c45a206bec4a06eae7e86b8e2072f7cd8797ed0f1aa80275c2643762f98c5fe5a25b6b656b17a07c312add9f709858dc7f7e869517ad17e2416dceac6f95715aaaa29453d4195027a9fcece960a7ec83c02163ea7ff3361a67c0ce3c91edf82501c47e06d0351c2247dbf4c260de35ca78f041dd59213115417e4b738bfbf5f8c4044e470e1551ca9c312524a0222eab72dbf9c15a48669b58f8048837f4a341345fa25d70c4e08d90bb5353c08eecfda65d122f089bb8cffc568fe9308b0afdba6a4d937819435527270a1b208a8fc97406e1eb9b76058988580e43575e3a5757cc77f6db0a0e3118c0e0cf30e50e35691eb3a68b3b947975fde3db6c454a3b22840909de0ab95ba0aa31dd40bbf1d9c712082334f2e9aea4bc029f9818b360e96b1e407274991cf9db4f3270dddaaa8c85f6bb2bbdc7da35056d837b157245fe935eeb9dc470178201204b8058e8709d9c59c742be998570b3fd9658d3483277b789f3f36f1e8989e9e953109413ac0f28343aee14a605bda60b1d024e533b46d68d913e381936cce16cd890b0fcc54cec51ddca4ea3a358f6e8dc893388a3d9a4d6415f7a6de500fe2ba7709fca132db2fd0064fd1f54470b36e4546f27307876a1a3f51d60eed095855b605188dd8b6a0045a4251926a54ad1728655dc309a52213f4aad2e46afbe368da000da3c45d2779f52c5efbb805b80ec60bfa8363339f68baec6316b09d15148700a18ef6a8bbbbafbdb2b20551b9c8b15467122f04bc712179701fc302ee8c0c0a9d9add3c91a24177e6aaac58ed651097c6608a4f1237c970248b98d0f647b9086d709e653de5efe4b96e5bc0759c9e68f8f8cdc4066884f1c3d5b4cad8d1540899341c386fea203d35bddecfaf9c3dfbed03a799e17d9e98e197132d067e150b2eb7804febefe4f9cc28b145781b0f3733326d9255dee9f49e661da2590f6a0e00027b08dfabb976b01fc327a2c0c32d9b8f22788358aa7715a94592d712a70ffc1d9d7792f01ad5dfd1f81576203d95b171128508faea87313f680f02bef7015c060adfdd8a47947adfd62270eec63c7e2b5bcdcc6be6ca0d877ff8629b2a0abbc1d56c73831585190078f254754f91f870631aae6019592a4e4892c620970a10699141c7fb68afe80fc322f60395d15238df64abd6df63ce48f15ff35ce002e23403e341e82526c29cac5c3c8dd539d33a8da8ed8713ab5c74baaafc4bc70ea5e06c90e2d66a2977afc34140346ce6a9a29e8fd4674d2a75672fb7312e3b03a0371d1d8c32d4dfa772dd359c192cca1b83e118c185c07f7146f8e3d53a250d540c22ba64e8620134e32f7da38805a5455e5f4995926f2f4ae754fac3064d09da76442b0e314f007bc629445d65944479127c645aad2536f4fbd4e55e53a40923ff1b2b77ba677fc5b42ffa5f4aa3e7aefcd4249574f29b3ec980b15d1c2e0f5038b1865dde99f495a4e5d0a6f46967daaecdcc3a86b1b45ece7ce335391300d70dda26be0d30f54e5d1858ab612570ede53d35eb6610797ba49b50f6f1c80334845139a4388b4deaa8fcf1ae07c7a24e3a11b1c030d8c43a6869a0f0cb5002d5ac1128915313a40b1a3eed05a63d37f21c72cd7c1c0f70cefd0420f95d7c0ad3137ba6dd3669290324bd77d1389bbb352c08a390b9557d8fab13a1c36e5403eeea6655a74253ec9e5c51cec179acc57a363d674a0d0c6088350ecf7e4d7204efa18076ab4d947906d18259c664e0634eea93125090e44f54c6b5d4db935f0af5c956502a19e5dc367b33b2b9c0ec9368c82d537014cd571bbed8da0b59a50679e75803ca8ead2ac985990cf25ee15f2aff52dbab33d496e53deaf7392a8806d38ead8cee6fe472adcd7073ddfbff3d9a9aeede1578e059c4c391b760e722016caeb417c7ad83da43226cca98ada74c7b06cccfe6b087bcf40f75c51349a70ee71f55ec8c7aea7934b8d5a92bc172f733d640031138444d1a0272ed2916ab08 false +check_ring_signature 1b92b7ac9bf836f51752dcce06df1aaefb20ffbf8067cbc7624a8f88c2e09d61 8f9ac385d3f59b951fdba4882d58ca74e4672d575ffb8e2cd7909a2ace0f4b6a 87 44f0310a4dfd6f61d68a858398de4819f71e325ca0bf586a672fd6f5d5fce472 d35c326021be0eb24cfbe3283a2b119fd86cfa27406460b2fa8132595a419fb7 41cc75ff9e26a27d468c7d4927fa2fd5ccbd98ac627b600a75f823fda22eb4f4 a9100677fbf5f7eafa678401cd33998fef8f734c935c5a982eeaeaeb82895717 b1289c089c5e7b245ac9a194b22bf01d7de8289ac33d6acc85b1975a6343646f 6b5024534710fc6be2374ec933f0cb9f87b078cbe28e9699fa62a62aeb65b81e 360525bdf3c735d96b72fb6754c93b67b53540a97253ca31db919bca9fec5f3f 6b582ab17ea2d42c6e109b489edc365be9664c54a082ea7536403c8be4a1ec18 2415ecb30ad7a85a00b01b44e743bf044c648be4f00cb06a913a324c15f3e584 6ee9a25ebdd10e3da0ef0db36ffe238d9e3378f505aac84d61d67e15a0e206d0 0571fe8018e07f0b944198721af1c6aa80694101c75bdd6950ed451c992d95d4 05741ffa9c1e397e82ee1d577d67937f29436ccee05bcba201227822d843c233 ce2eab19b22e897917b9008f32b618184afbdd4987c9fce04f767f4640ccb742 02106ba804f62851cb416cd9347e743aaa33ab1cd71da89ae9d21d8a205f03a5 cfda1576029def2f692183da2395abc0a6c78c61a58a8ee0c620dfc11bc39425 1d0ae204159e365233af4cd65b927a4e7157e0bb0907f3810e267bae873f474b a560580edba23fb03fc086c4520adbce9b7a9c35636ea4a24c19f21195898d66 009a7a82fc4d7917e7ef42a0cae846f845f9e1073192e688fd9280d0a5e1ad3b c5c26672bb5578d1712c51cc98a5e0c6f4b7884d5f0220c9f31de32c2bdf05b4 64dfd7f2e030b10f08c0714df8d8d23ecda29d66ee225fdef0b26ac2e12548a5 df63b3391c7827e33dca240b87a9cff0b5cfd2579cb0fdcd480a0f1f13a3d0fc 85f3fe46ef4f7971a26a604e1af157447eac300cfd13ad44f9f4c042d9a16a85 605f081552bbfdb3c47330d86079600df3a23652ee71b4497fcbb615dd09abf9 6e04f22c47943e6be7c4f485ca2449f155688fdb8b7c60ee0340854c2eada263 17f5eb517fbed3a1b27c2321afcb5800193977640525c3279380253d9fb75574 1b9940641d08b77b97b76ecfd2e5bbc03ce30b86913264ce7023822753130719 08245db8f74fb4e525e3caed0343d57632ba36e69018bdd48c1b76771b0fd90b fe6a42a050738da18190e287ebe3cffa4c5ca747a160e9f50140fc8fdd490d7c 12f9ef9b2eb479f523757cb9c46ab08af7f3faa2903bc7fad3be3b7b8cc67a14 c55a7239aa5a95e0d96742890288ec440b82ea4dcc12a427ca81d6ac29dfa8a4 f0925445382301e7bba50570fd4b901a1de4563a897bd0bfa1934ca323732f69 ff7b8c9b56ebe3ae08a22944557e60d0ec37d89160e97dba60b9e060f5ccfbae 273cd6ae79cf282b6cd216690a0d2fa069f97dd1deb354ac589f375a6a5eccc0 6be037d5f06310f5c2332e09c27e0490317f1edf83fecf2b98bfd06a6cefe294 585b273c36b8245a3ceeb3204e6fb4571048280d39c74fc4600cccffab235bcd ff7502a3e8b5588cb9ffa1207057dbcaf437eef41bf0fa2501fbcfdebd013efc 4dfe5ffd39167f170735142d434d3f0756eedee73065f61ee4991deb226986fb 1b31c7335ad5f58bd22e8fd45ef8aa3959d77f0499c9fa2c3756d19cebf2ae64 9b5bc15eab7ed5800697a374a0b32a9f920229ffdefc21c81d939a1f330cbeb4 a623e6590b59bb9be2975e1b75b7020f3063a3abb9902e3e90708971755cfbea bd0e3f07ec1c65d2dfe36b172d21e405373e8927309ea09e65af1b4a9864d80b 681d2c2537e6cef31e933762f4bf3be9f563a94f26e3f1a06d7f098b55ded0bf 9057c79976d73efac19774134dc075b2a55006792cbc4144cc5b6e097c63905c 1e19569af1a8413bb2210afb077142355580bfd5d4fcbc1a131cbd3b047ab457 52dd55c3244a574a013d7da559d0fc2430bf3205c43396504eda4aa786ed512e 25d63b71ddc78a6a6467b61ab9e02d246b172ff788e1fb8ecb50eaf4fc57da94 cd05b94ce87468b907f68571ad6e6c997d8c539f5f642274136267833f0f4f7c 6e18940d8b4cae53dafd4be26545c752dd12f03c01c03f21f8a71824b62e93d1 28f45417b34a2792fdc20d3730e6710c84060f06fad703923e66d0d422a32464 947cdb7fbd5df4f8f21c185a59edfdbef98b566df9d9a36d91c24b870527ea24 6421d4216671e60a46f271fdd6becf1c2dfb300eb90b41b52754b94800bb4b2c bf2b41dcc9978d582570ca26f0f8c7617f1649bc71e0340dfb16aee51f309dc5 e9fc5c4e9a229ed5e43e1db7486f4812d495d8eaa7d9ffff6a6756063ab09a29 0ed9f14db184746d9d93632c4eafd1b333333978ea115c3368e1f79556ff2c97 adf2c7dd2ea5cfeca83ab06caea1676fe7988bbf5d13d40dd179c613c144c049 7b30a13e01866e4ff8e187e0736dfa32944a1f3bd69b31ab40e9bf06b8680fb7 364b4456106b8750fa7b7cfc5411fd62632c44d10f33fd410235b80513736442 41f6e5e6f1c1ca648e8bb02b6ae3dddd54575c8934481c1d1f0a92f0c816e176 0624009f1dfa4298053edbec42c927970bbee62bc2f88d98c4a1fd5f4231deeb f06cc9b54244de6720674b475c5dccca8c7f99e3c3048b5c9eae037f10448c41 28525cad25bf793ce583418d7c8c08d7676434c602beee99dc87fa2fd3d8d36a 527b1fdf99ee67b70b8b508627f708af15ac04490fa0048cc62423a0b6f8fc8a fff778aae28621160e2e1ce1815546c4d48bc47545dae16b44031c7eccf54ba9 2db93423b39db5ddd3e3ef8d99231ee6d49f92fcceeb66d868a6807af3292049 cdddaeb29df4a229924f1fa0d73d4542232a7f8259dd9047493c74011a52921f 69a97ed930e9ec164858069b26516c714a39ab59105d9b84ca7752c16b123406 709d6efee37d77dec61fb66697807de9209e9bfe1b3f114884dd90a53cfc57b6 d3f4128c1deefa8026e54953b355936ade32bf691d236a255ae53b154ca6b3b9 a3ec0778661cab15d51b61f2e4b70b2340faac509bfb889e05b32512f32d5980 f5fe2acd2f573dcf77a4d87d1e5b9388957fa380e01200a0e75c7d4e4acd6fb0 31632e51f66cb6cd62ca399dc03d941510a8e6ff880b470d8a4563398dfd6e9f 9c2f4f97094e52f676ba5dd3621460459e0e4c8ed962053350803b054c900b08 93251c48ac5846e85f870945ed870f2e043411afd622160e5cb8cb9272a5a752 50ce0a50d18d2ca5f4f86196e52874291ecd989d2334d9908c3b6a3c06a9bab7 b372daa17b55f277266a9f324b5e0f5c94c3f04b4dbe733f5bc114303f351415 0f9c52d801503df71c69ed5442fbfb29e2bfbc051dfba3f2c55006d0ca06ef74 ba0da64d61feb3a8573ea290b38474657df7adfd398326ef7a122773601b2fa4 9947558f48c3806a3e4f4c085811a2d3c5254431032791c6f3ee7a2a9b3d599b beeb3b963cfdabba563829cdf72a03a5e759fdf0ea4244e43fbdcf5b3b7dc30c 12762ee1b466ca490bf115632fc523b8699d389b809fb4d7879d425ea1e4a762 7921b499ef9d500e49e47476299857de11f4fb1fe84589ec6eb93676c5f5fc5f 1507a169aba7dfd28196c0309d7314cd0a38cdce194d4a073775cfc5df1a13c1 6f20a9307d4c65eb0cebbfa9101b73fadec2d03d1aa75bf02d3651c809632e98 a7a6989175d855950b3690d7735712ced79c0f7affe536895febee298b5db10a 80f6f4e3f1fce9fe099abc5c1aa44014b04eb1c9e950fc6d26964c40b021ab5d 0fc934054a63622ca132d23b75b585105d484a1d0e23520464106853a8339022 6b1709f4f672a78a6c3683f1017fcc05732d458641b00b73a62c529f7f37dfa8 c8564c9e046c677433ad2cd8503d373185c5d47ba5157cd9a1bbc5922cd1420705add1b2e6697876633b3c7c4ab1bd2372e59539678af1d222e73d4afd223f0eb61d64fe880e4bd820a743ff2c32e433c04b2c435468b28b59a6d62ee3f7ff008cc6fd43fc8562b927067ae3c49f217b35f3a7c28533f8c340ade2a3e0b68b0098a6248f80f297c434de2c3ebe450efc968840455e99e6780808068c191a08075f4ad984136e01be032fb169ffba3ae0b8b3dda6921cff438a246a4e17f9b20843d90c862268232747850e6b660f12ef68924df681e90fc01e98f3bac7d6f30fe38ec53c5981aaf80c4e64966b7ff975990ec5451ee99662cb2b7bd478269301098b06ea9ad2005e4270615c6b398ec736978d7e69eeb3e0609a22c0e89c2f0f614f9d6aacad297178b3c168f76bff2c10254397383aa2922996bece6a23a60e755be9826c203669047849e81174e4061bcd0bf2ae6160225f452d6eb993df0b44012a3da8fb97cdaa2e567110ab9827565107fb76c5cdcdbe48f37376c39e05540c0a6ec831a1cceec1149d6acdd19163fa1132abe9273de8e82c2277c21203387fc6b148d3adeb80b64bd036880364eb1f5fc62f8f159c62782de42ebcc90ffa408b7e51314f0d26e1ac81196af980403a7e80dcc0c409c5ac667b628cba0873e11a946749a39101ad2e0ef8a4cb78bcccaa80511213d6693b149217dc9e0d1f53bf88ba14085232a17b062bcb458a2c3388b9d532477ae9af9e3b273ff60452e241bfd84a749cb06988826f17ad79a79707d761cadc9a31dee6ee63a5cd01cba06daf414034b0da5bdf857d1dc65cce688bc91d1e17444f6dd80104fe010e32e02884e7c354eed7c701969d2a8e2ef3ba06e73eac6459d3b0b76d29063a0c6497af3aad623e7b99984c21e20ddf5bc20f8f7188c6ad049213fde47d6a1409ef44d919bde62185efbe2fce58960454b0c5d761b8db062208d413beb6e3630dccfe5286450be793a577140a9322e1bdbd3ce2dfc111602435d23a933468b00bba884c5ab63e1e1cb0bca8d8275540eeb08bf808323b759574ea8ce08a0dc20a2d6fdb3814e04285a5f71d80af92ef64b8aa92df75e52aa6e4bc0501f4eb0f0c1f84a2f0dfdc1b98ee3cd3e10546a3cbff162c58e00b6c53001670a4bfcbe805409fa75f3115a20fccc4c03b2f86249feea10e72d0057c96cfc3d775ac2abe0f3ea5cfd8419f322d52acb9ee3e0446e27616edd12a34f10fa6255da0d655490c6c07d709fa3d18354828e20975b4974230adac174901ff23ec17578e9cbfb207fef134ad4d97759bf74211887ce6354172a97bc67d9960380295cc68542ae70e311b407e41d64e5b9ba7dce7e5cbd40cd5f94481975b7eb8b1c28bbfd6c3bf0e670c7a9eb64926a9ba65b492997c4a2f5a11074539669460b9487f5331ef52005b6557ce37892770366ba423499ef651b3ee4a7650f1df54792dafab576f540eb9c0e97c75e602215265f7c115740314e6214d00f3f6e20f946948de8dc3100db062efd158c4cee2268d966cefdedcc98344eb519e594fea955d765bf26dc009996ec330f70c751d53e8b103f58d4bddc07fc87958be9454cb53ed17be885e0ea9ff97580feebe2e3810d84fd4cbf7673422079e09a4934079b97825cc9c6c0ef5346f37d6618c5aadd9813cdd8d2b187cf0fe85a90aecc221d6b52485dc93092ea988c4119b506e81d6fe86b5da9a8d563db60034e3b0e446701d20656902030653f9dd65acb6c5c30e2a3eab04e54ef7fb0fb861175e9ef94ab432d5fc010b394458126962deec1c4947d58d6765e8b1e335a6557e4390843d48831f9a570a9bae8bb5083cdb7cb2794b25cca2f58b3de6c5cdce79ed489b5807fb41374c05636d78dabf81654485eed85010ac8129a04d153128086d064a2ef135928ccf0e4a7dd000879819b8b42be4452f5a4f76a0219cfcfa768afadb950967e1439205421c8b333280fb4825e49bd8288dee24bd98a22937fcb24cc657f6dab078ff0feb65cb66f35a772e22961bf47055ac6396424b9002f6fbfd2fb5887078a6c20233acaa96100cde044814acead393c3d6527e4f49d2e321e940447231ed97df097a3ca20c127b9d67bc6976394ecf13182b11bb5df50cc0842fa4e8f9abe04d0e06e65315b9cd503ca87a24de7fd5d8f2e28bfcadba1288695619187bc0ee8e0a0789eab3450bc2a30ab585c83b461d46d575a289e4d51e1f1e70514911f1dd0981e43c3d44663430a025e113e03d4f3639ed319c41f7bf78f150d5eaa23e2803b7460c9a45804b40a276808bcbfb5cd373cbaa7b7f373fc2908356defa781e0172efc215fd409ff42d7451ca6638f1d85b6c80eb5ca3674edaacd7c287de1904d36ac451769befd0b2cb4f37c19ab4f33928a73a4d13fa1b38c9d7e93fcb5b075de444ef82856c73f09dd1a01bc9b5a8ca608ed7fca2964349c0b91b6a96d10c48471d1127bdce2fcb10479696ecb6882ceace37cb30d34647c24bdc348da40ab90a804caf12e0e307f5a3faf04d515edc32e800b46f6823b77a772552808504973c081d07c841e74211a57679e62a0103703f373f1c0f721153611682960201bd7ef856017182d15f5c42d3f911407e57f40af06fc1abdeded78b62b3e93304f51c91f8443ef2323eab772867d6cbc4e92617dd2bade619ad79278564c0940b0623b6b3875c8a5a3d67508af6b8bd939911e469c59caccdd953a8ed4893ef008142612cf1fec3ff6ab094266c3f6b6d9148e10b4fce175722d6b5a19acd1a0e701ea1306a354fb6564b4379a39c30f2f62b301f73b31d54b7ed25aa3ff851017582f2d0f8a04b10ea313a0d47aab0154302178eb6461307328cad4a132737095a8440b4c529ea00112d07bcc0754522335e0405b9cad9176d34f818e821e90571421a43cc8e064ae240f9d47f2eeaf511c4ba17cd9251583d98cc7d755e56050e34ddac210018b21e45c612e952f656edc68292f55f82e87097cb7388d20109789165f1e22d20ae3ebee1d13cbe7d747e759f08ca9bff185677b13a7359550f2b3bebf69cbf8ff8efe1493ccc985c5fada4ce8d8125b659f0c375d2d8498a02bbf45995ee123501d1a1d9847ad11093053356f72fabac74721ddcfc7aa3f90b578a2a5af1e5613b5ca5079d8511e8092111afddacfafe05f02885aed6e3a30f18f0e9c6da2e545e52ea24388d46139a052c9b8ced9228d44ec11d68558a210744d4b29f6732ef4f32668f5cf51a0183a34427d03c86cb6afd9a7961e95ba50188ad940f4dd0b0a31ca2c6db8b0e67511abf93cc25549ae25abf1ecf458a030e1e93ed1ea3163ebb771eb5973ebb12a22bbc97b9f4b88abd810d1a149454d409b48a29d74dd06c41c6049703f4a0f74d8387a9cb3fcfcb1983061b6eb7c731069a6726ab7febe7ed45c6fa169d604f61fd82e87f07c649534361ceecc212a701928bb84f3b787dbe8e30edb6d8aa6edcc3c3bd09c42aea5441b0a51ee623f80950fee9e1e1568026cfd8125e3a120219935ec677ac19089290ff321c5b898302343496519a7046b464a18f80016855c52d14def3bb6390d4b31317e7e17b9505caabc0a86b5e485efcfb52712ba08b38c6078152b736f3890bb95dd7a5935b0cc0f596bcd4db292b8f9eef3db8a722c14a93c73907669f61304a7159a10d9a01f5b9488c1e466bb04f7389d6e9381ea3e83136dc528b3fdcdeda9d849c11970dd11f57cbe7502241525efc8497b49d94c794e56dcdc1d6bd24b82f3fbee6b9082bbb4d8f70c1ea5a17a047964f90d3a6f1f860a671470dd6379de17792831603e3ca9dda15430b984d3cd7a462943da42fbf9fddb4643f6a372adeffea38f8082071945af1faa2163f022ab2ccc6cbbfb13aa386487aad89a97f8a58a796480c0558deaa31090c03d2dad795b2b6a0770065b0f89692ef7df1b930f3899d400090ecd100c1c90c9b8d1802da3136df1e1063af1e264c779079f39f13913597076c7f8314bbc6903e9e312189aa62035d944777fd2b9fa3188411a94e5c03c1085c2066dd3e9289c97dc4fe45e0aed8c1e39fb6f813f03343d2b99c765416fa03d181aecb0a748b6f0c4ba050e22de5935ed4603ec1bdb61ae7826c1e4f439c08131d86c90664a49f8a025a87555c59cc1435d1532e4bcf48563f2ffe5650760fe0b8f1727fd8c19ec4171801d3b18f78e0c1ac929b6b45b874e5453ffb037604a7e7a178b40ab8f685b815c60b8f82a0b26e3d933c170a8847e63ed4aa6bff0b8bd52e6f64de11781a163664fe81f798b8358c2ef89e8d0695ff867718b1650055d1bd7070f136d4d50795b5ad15830d0f6c8b918ff270abc38b09501c59fc0c9fbfade775ac3707f2d6e976668d1b4bcae51dc0ea77e4ce4c0ad5c47b680e03c4a2a23f72708ea15a3cf62e6d69f88fcebd18050ca1f4e812784e2ff1341d0e9420f476c0e58456908162ed51c1f93d953aa8ef31d8c5c4f18c400c23ea0f0149df075283f28bd1dd08d31f04d2fd1a0ce80d6454c4ddfd7c560205623d1e07b97934b5c4d3818f690b64f76b0b39ece3a0c630495e69b5ede07cdcaf3d9d024ff94587d24dcdadf96c0bb120352e2c3b2324eb347620d32b4f081f8d22e0014f0f313fe3f5046bf1f0b25955922388d26f7f908b9e81755e90456780628a01e1c86825a59cd18347aee83c07095de1b893d6e7952b9b5ddf5c2c95d3d38d02e5a2a2860964729cdbfc96f0aed06cf6acbfdf47aa001768312b3d74a2a59a09ea6c09ceda56d515ed9048609cad9d4f24e090fdfd1b24c0cc9835b735762a052dcbb66aa512aef6073c38890cb8b185497f9b83eb32b357a7e6e6347f8a7800961d79e93034f653f7b38a69a61730b0251f45c41f8efc782f7ce8032436e50d16b1891bde355a656be5410f90951942e14d545cb0e279329fe13b46faacad01319e11493c2f2715239673486139477aa3ffb15c29c47c4dead0b4f2a131c50367d6c2d1a1b7150930a2539aa92481d9829c845ee3c9d3863d820e565645fc0fdb11c6d9eb6bafe653754a7af6649f516c34c354bc0701e6dddf3b0b97e85c0968b8dcf1e35a9dfc5759f805e2eb3d7fd9d2618c1de59c3aedc299991588d803a32c189e6b540802efa6b102dc290afee5b422afc62eb89a2ee8d71c6daa170740377eddac46225cef0a2c2ab1b9090b2a07618660e923bf5960e0121cf918066cd3218698e9cb2c3c94b1973bba4d8d5c1bbedcbcefde61ffcf382b874e790af666c79bd1f0ed244c55428e3d8a2cdd067d48365fee6161c11b0f9301a38f0200d485f3f82130949a7502c297a69924592c28bb7cf33a717e74fd5c712768089905c7ee409c88d8c63baeba700ed65c9b21d6c58c18614e146043f198193b0e61728eb7134616174030b42a6832bcd6878876fe25b9cda1181143de9609850b4a282a314a41d256bcc33856cac434fdc04ebc28f90c9d5f97050078570c5b0c2789d895db4d5017df0e88219407e9c899368e47404309c369ddfd6e6b13510b643861e3ad839e3799f1e026f76660c47bb0bea64a64ecc2d3ea6abfef3ba00fed4b7aa3824872edc405106e2cd669aedcab49f1a11d0ae63500d8c2cb54050f38e8a5e5789e4a1d34d582f78e19414ac4fbc43fa614f9a5fb841d61f610390b64b06a5e8530e9626a91859aab0082d8dac37edd05e8daecf6f6c52acc761d0fa42a2becdc3fe0399a5631a841d37c00c5d084bbbf901d7a1e9171d089e61d0caa36b2597e60f28a610191cc32f68def13eedb925099fb5d14bef57b63e87e054922bfdc6307f713b63627f0635e2bb952d45a6526d3c838edd1f618ad674407583b2eabbd06d604052062068a4496ceed7d4dca2aef86e9fd40886429b40009b86ba25904cf07e77932b6d3feb034b5d960780ee4a085493ec9d4a98ca6360ac2125a9daab01784ed6145b774eb8fa0d1ceba72662a09b6d699ec99157021058d89e4ea9e99e8e61a2bac0b83f98049a1bfe7b408256c8a995a1d0e9eeb7d023c844d3047cf0b881516db492dc9d1567685b8ef213732f36688989ab399230a74df97b546791fb95b372444d4997586bde574977ae8db9d59702549784b32058b982143884779891b1c2babf3b58ddce7810def653d25e6b4dfa85d3ba1620c0df8d2fd607052ab140bb89a82e23cded977d59a5d80f6b2cadba9022e6ea60a688892b4dd02b942a9d4d91075877e6056130a1bb4b6339fda93af29206b61023a054ca58a0c5ccb2c24dd979eae5dcf4862e8bb83b46b5d3656d519a2e0630261edd0cad7a18ead9c0b639af5ff3fe4dac02a87744fce3113c4ee3599a3370e2846f7e686c2f302cd899acd5627123cd743c4a8a69356eef0cd23b1aba7c2064e6394294af7879524de595e94cf7516684067afa1b7bf465b7aa750ab5be80fde9a696377d2d3094b59c4b643b016e0ef05f1c4d8e9df5e77aea0f27a244301a13d4b2db4f7c814f69bd2889ff563941a3fc00f1e008dbe24a1a224c981a0056a04116472c13984fda6b3528c16e2799756de3cd87cd13f0869eb4765b3c508ea55b05a620d9d8489f3c7b6d80ef45b21939c69ce08c83b75820a1bfe923800fa55e39f417ace821f042a43be5e5e65b91bedda612e400eaaf10a1f35f9780e653a982a1cb70dfeb620aa0ed3b760a009cf5b8daf7c8e91ea9971b9db619a0f5b6872dc07f818ed9480d3993c482f9e3e7d4e56987a9429afb0c49c7f2d2e0381759406e7f9332917e52d0c0b97f4a8bbc0a144d3345fdc6e9bf6cb02c65b026f2146986882d7819f73f50627e3143c6794da9dcd174d42b554dd18bc369605f876dab5fbbff083e36d3e3ba57184ebd38de00dd9a6c2eddf1a2fdaa73f4a016022b17baddc102a49cedbec9ce1972bd6edcd7bc3105dc3ca0ecfdf29004f0b447ff4559f4fc093df6c711fcf6a611655ac1551e15838b705da2bb36fbcc302a2cd4333c09e7e2344b21feb28bba6d31c0b3a32817f245af2dbdefd6979d3082a2507389e729a345665d2804353c8d5eb0336a073eadd4040c3afc9c03756060fa7cd4864af33631d7807d0f04846d98b0832efb856699420ad92e93f9839084fe0c88391d4c1a4e93696d89714e4fd6d819d763c7e378cb159b449d4fdb309e8eb774bfe7b3f97f1fe7d52f2a97eb2a12f3399031374b03f4d00c8dbe41f02c5e8e5e5b081bae92d52dc72bdc789071a40d54160c35215c879aedef4295d0157381efb0327eed6ae6aa5df015a4e6ba3948b938379dd616bae637877d5240a9c8ca89b9b9e45e48265c0e29aaa4ab1de0b7cb0cacb9ebabdbd7d3c5e24c1085ad3c8d142647326ca75ad51f57e4647a766647f56ebf42a15ed5c695ccbd70e316ecb8121e83c7d17cb6b8fc506617d453de8381c6d453a2f42556d02650f076dd7a3d789c85554e9c5437cec68098d62d9865105f8b8bc24eab944f0195009fc29c063eddfabe4ca504d158eaf82be911caf6d470a5b5b1179ec78a3e07505f9464cc9ca3adf934565d3a2dacdd52d2b0a9926cf8c313aa76f0e34008c6306d5f9236eb40b59d3ac7640b724f4ed4fdf8769b8b720e3572c86b8a55f806e05b98806f4d1361e18ab5853b4ee6a2e0a2a39f74c8260d935d125498b700bc80233a956aad9f7861c816f36484da9ef9b2623d0c319d7fc822d5b1924d026c6046be9f9a2de43aa6cfb1c8e4e5ee93a239d11b971a7925cabef653a09ef50ec02f1a558c3eb29ce60baa6589b8880f47cb0670a2f11643c9611071369a5fb130ac96c6506e1c759ee6935b995bf2d3cf71ebfc214579a170eae8ac75bd4442e0b false +check_ring_signature f0d288b988e122c18f6c745422bac4266bcad2e46057f1586ea4ea1091a3ce71 488c37f0c03bb47aa3f17bc717ddb22685265fc5117280178043110a55b16079 1 ce77ff544e8ec605009461f66a5da4274df065f368f8ba3416ba886d45c3b255 aab98b21da0a5311154234f3bacf0d782039ba8ea85f6191b86a87104fc8d3bbe7ef27108ca4294ecefdb0d08620b718e9b593b79d96d1904d1eefe24b43b609 false +check_ring_signature f7a3ba48883b36a890eb0dc56359c1271d2849e067aab973c1aaf837c69d3e48 87406f72e0e0b562c016ae5cccc053953be156eb9e0e00db04cac53cdde4ae06 9 00022557d4436c4590eb490551d75710828e65efc250211bb2b6122e787da399 013901284cae2ca8634b228d3f8d6816323319312d7aa2a91150c2be19024fbc a0c95d5b650a67aac1aaae83f4c0a528c44c16d6849dd5e8a05daaf4f9f6c199 701e057b6cc29dfabfb55d340d232d79918663ed1aaa1c7ea9731138e1832cbd 718e3b40715120205d815b812ab1ded79136ce6041871b04eaa26a1a3fa45cbf 427d00d55a8838c77b8c9b8a3c68f36f719bb544610ab3535a15f29e2dc9322d df0ef359b1757b4da17dbda8cf6ab2b66340f5ffe65b3e15e40090b6289023db 9130d791a8e7233d4d46aeae23b500626ec2e9ffb3320948f2b34e8d98668ff6 04b2c8ad26b7bc1bd74461fefea764854e322f3e27934ebdebe87fcb2bd7b37d 02d67ced3dfbea61f11b0de51c790d14dc371d512fbd3e8d43af7f602e9c530653611d815d25470bba5b50413e4798affc53a5b8d11fa1414e0ba7f94f750108f195f6f0c9c3fe165f14dc8a38a77baebcda99c081069514128eb5427b647608e77ebe0139d9eee02546754ee541ff331423b32e78bd0db0a262a6966d02730edb2e11c822a6445f8584b0c53423239f7fc79c0545d26c02aeb06323473514025f03efdbb6f67fa10507e6f7dc8a52c9a48cdbdd7216bfd31c7bcb9d81e1f4009f77100ed53618fe88e842c59d97b2cce912798faf014b9351bc9519635af009356e9665b3f31a8e5ba6a84c50583cfa9bbc22f675926ad8e834120aeffea70a3231eaf74dddddcc8895c076c39f517a3a202b1b0cff9171d2c0179fea03270a3a8bb8d6196920605f2ed853ebfeecf2b26171cded89ee859939a33606bf1b0737aa1c4ce6dd1bada0b060fc3936c20767ab2886ac69a3057950e8a2647ded03fd1bd0841b7982a5a6590d4494f1fed27d75fbab7b933f10b6214170d78ac103cdf9993eb9457a0cfe7584fd312065d4adc0313d9696c8d4d1cb4dd3115b580b23eae8e67f9cd323d16b78561c338fd22c3ca9a2ba7507cf45686b17e463380cc4a4a5a0a67ba25cca5dd2d28578167760cb1bfa57271da668cef0dea6277004ba6c8af3154438d3c86f441b47d09987dd7b62a1739c24ba285e2e2e7dc1f6071ca123723b62417fdd9ae9a8bd7c3da88ea381f86dbc93d5fa235c56f75de70101112cf6fe53cec8135c990408c92a0fd978a810ad5eb22b3ab0e4c8c0cf230b false +check_ring_signature 0c96e60e1e8b6fc5b41bf75806fb05c7bd8b08b5addcc5544e82e8f0e2d5ea3c 2446bcc8b2b48ae2455695692f308da1dfca0800e3c39436bccd7200f07112c8 1 a4034988dd5709802f13bee19e53ddafd48a894a696a6a4b1b2e6a4d4993bea7 d50a441b352918ef801760d293c185ece60919076d7a47cef56aeb93b4aaaff4ed926ba29f0b6fbf1b79f5b5a4fbf2f39577bfa645a9c36483f6b58b2bc21a09 false +check_ring_signature 2fd5a129204d236e5591ce0e67eeda09f9d5138e0cedab2f4421c6db0598b7c9 13d648481a443dafaee7096ee94678c7e4374c28f4c13b4f94f0169c337dfd7d 13 7cad735a56e8ebdfba575b210fdf870c8b6f975af6b15ab111b892cba52ed555 a68df0ec96cac665f9610e13ee679d7fc58adb69755ee663fcc02ce8899e5c30 66a11d81761a8d95735da95ba50f91a04d0b6a2dbe125e1f0e43adbb1a58af94 db2d0bdca7c9c67d281c4fd5e6971c0e0db5a41068077daa240958bcfdf84634 a0dc70e2f4a4227ba964b236c420a8264e905145679720cb5ee1cb69113ccc3a c1853a4021bf0919757d446fea81da52d75148028b61895f2c73e2c8272b3d45 35d6685053455fb2547b5ba0e1c2b637ad5d090f8c902a914b8b62a457d23f09 3acbe6567b70a0dfa48d724497cceb9dc7eff83ec08157f215407f34a6895a2d 230b5c257d69bad46bb12de6730a0d4bf7d070acbdc7296aa057b52a1b35be2d 08bb3aa753599a70208c614238bcaa1e7f4f263f711f45653ca382e8082ca05e 8cbb90052cb2d3315f068cc90e3e2a047ce273447e353404679d518f335e705e 7c8d366b782b61488a08eb732225dabdd8cfbdf8be91832d3c3842fe3bb05d80 51513029e3350e63bc4961edd732f6de97831da103257df05c06dc68074e4ab1 ba9ca45be6ca5cec8d9cd0fa4088a6459ec7c783f1bf999a1dbcd6a69eb84a09e728a2855cf8eacd5f075d60e5bd43e181619de42d25d23ac772861cc36036057aec590269e7316205a99fafadc28a83ada185824a185e2a06b82949eab92e022c0309167035e73ce2c4d5700fda768b385ab3ed264144f819341be7d14c97066d664a53e752728475c1efb443277baaedf315ac014e2e2e145a553f2c28be088d0136c970ab92a68151c30155bc0372e3261ad4d6f44247daaebb086d1a000867097d0ce2adbe4abe80addc1e34b9de44a5d40001a20b88b9732c3ae48766004afc1c666ef2dbb6b9de37ef0004ffc8b3e031ed5397223170740fef5e94260627c86d801bde57725b52135cb98cd44c962a2b4a67765832b9e4024433d4d50c99363307c81805e6b3f1f143655b537f1c7b9522c31beeaff607a41ead6a2902afedca27822218326120f1672a388f5145dff1408526c530947ad37e3b28200c73b9145f99ccd8ee911fcef03fb263d9a68fd760b1240dad637f41214446e3093150bcc3bab22b34d7240d761d8d170e2b30223be957cd3be01733479d249004e6e6a3b22b9161179d0916f50ea429c060850350d818ed2c75b696a833abd60b96394b4cc70713873f8e9b3c8f8b5553435794c9ac55f2492cfd6fd7f19780074a8a14319f0033f8eef562ec128e4b93ddb5dc419209ef90add7e616152ae40de0824fb621cd8cd17593dd7ae523046802d31a493ba550e301e3d84212c5be0a6836ced55108f105b2f9bc206e10e79e73c9886dac43e6d0856055d941e9300c9a16a9a86cac3c2a83e1f5434537f76404e59cf630661fe6966b644d3bc640046605bad0d7b5f37344f847d19a789f59b042e31745c0ca9089d07df40d3a1b064e5af224c62b5f5496dbad58d30972ae81980df22ff67bf475393360d99fbc09a1d7bb2edc9de6aa0ee260bbecb6975a83f1e6d3085a384c69aeb577758f4e0c495d356cfe949783e20cc0637b47e9eb70db23904ef971120fb3fcd7da0e4b0f18176ef847b810ce8def2e0d40788900f7f9422e960971bb94fc67488d22030ad88109e53a763cad09535ea050df33066bea660c7e93bf852aaafc12933b310e6f552f7e42ae6fbcd87c0488f00279f63248efd987414a711868c0ebd80abe0e false +check_ring_signature 425e1fe84a665ca648f329f8cfec47420d6b43d7d699ffa732e4e362c24a939d 48049396e3aa796e7b8778fe1f97ea7b7b2dc1b75f80259e94c5870363b59c08 1 1f261724276d0e780a373dfc1830024097430ca8e2227d20911008129668a38b c3526853d4af98f79a06ebce42f45591d8a0193ac4b202e13b11edf9d199a3040acb91f7aacf4faa264d3f36bde7294ff9b4be6d6a30f2b0e6967a5f164534b5 false +check_ring_signature 6f53dc997c9d7028454c6e0eb6d0b65fbaca0684356123bf496942e9b479d0d9 86ea5b39308619265176fe0a1c84fcef78e791a8ff65f803f65ebf85c1027703 7 a202b29f4b728df6b0810778ffea96507a28c6077995a1815b39ba8bc715f2ac 4bf1e66bfb538cda3dd089ecaa80ecaffe611e84a3498e4e933bd2a6e6965e4e 73f7e094d3981d673f654e98d0f5be064b2fa74f3663efdb963ff6186ec5d576 c4316be2b132fe298ba856c0b9deb806ad73598a8877e911f31ea7f5bfa0286d ccdb36746f3d3b04766abd484fd6dce6b37880c51c450342e3804816b87ed179 8d0c0f877910b64669d9c209eb475456fb8bf7e4c100d2e4b1a72335d4dc2ae9 31293b153b7aa62d1aba4b446720e88ce8754ea216d5e3bc2e71d447c6f3e68b 8aed5906068204769a453f302bc50d149fb3eec9c37eedd23a3f8b5d27d9fd02a6263f14ef20aed89ebc1562c5aa11df52283f67c34d5d36e110d5071f3c910be10b2c5ea51107dd2d9e803a98690a01b79e011597edfbdd6e5f744b0334b10c5af5e896b472db529037d8761ac00a55629ab7fb29bbcdb831c957ea74316e0971470ede4f5845386a8e4215eabddfd5840e8a058b84d9b54c5554cc91b14903646ccf3fc942ba800fc1d7a76c018d1126abc4be889c9e585ff2fc5bcd1b9f00459b69dc2e54445eb5a599a5cd71cf8df528d2367b442cf349fe8b4af518a202c68b1fe7f131a5720c64454dd4b2b1865c98baf226dc5661f1ef87f893399b0ef1edbaa505df02ce0082dec7b346e4a33c9fe5870ec38f3b1aa161635276b80ab6916bdd7491527a5eb609d1d736ddbbdeec1431a7be8e10aeed8971fc094b0fdea1f240b0df24f13f28b9f99f5a1c5913931c8245d6aa28df4bc266a0580c055585967ab8a95d162cfc3ad2be5eaab4f380f7620a54ff0bbe332cc2259f500d8b8a0914dbd6b436f6df69c5cebf0931c9160f0c56e35b7c4d45a98f932e970c8b8afb325b5f457ec20fe0ee3a544134544f18ec715094a60c063bba06ad7901 false +check_ring_signature da19a2cd107e6345d38f897b2df5a6c639b09877e3988c1f2713d51b181889a4 c0ec276a7aeb5b6beb3381e1df76e96af66a8867d90b4ea93a7ac2c46d51d68d 19 58235399dc352c39ea1606c2e020fa6d4336ff3ccd5d23d0c186d4febfcc7404 7990edb7cc84784873f120829d33ba059c18a2ec256686b5369d3a66305ee6ed 53b7d6b5031b68d18434dd4722255eb21e42ca93ea4c70008adc2416ff680a12 f32e463cce9aaff4238e7e2ebd9e38398a5ef0950a65e349578d1ae971f218f3 febccd5ccdbc6593f6917c163935f0b4df7f431113a82f61ff32217781cca5a8 1522ba813775d7698bc803bbd9be1dbbc7c00ce560a9a0b4175a1239c4700cda 74e18dc76a72fc975f7df88e492902ae923a2ee9c01f819126ddfc498f60634e 653a4b96b343831a647930a2ebbc9c41a2fefcd156651f7acfc89202bc7047f7 a81fbf20085acb8798710d49b1345b8cc50adf21966ac7ba7ff3443163045f78 2c7fa7a2429330d93a27f47eb481b33e8f0e5e4ad7f316f5ed3ef9465aa27a79 077ec3aeff0385df104ef52b05e6aa66e1e422e4e40dc117fed660f0adf9b3b8 605dab8be4c729fc469cc7411b61e5ddc70dbd05edae2866243529a15d6f9307 13dbf750164d09e54a668730b3f02fb71422a27d956ec3cfb4bffde6f8192f37 a84aeb2ca2b5ab769fd731486080f945a13a6c4f2cf5aeddba24cf1c72f8cd1d c492dc5440e2dd9c88df7c48050a21253a30b311b24b7a879f434bd9dabc67df e2674bc329309b80b625b868696ad0e04a18794401482b7925f287b45711c1a4 13a2d15ff6e4ae010994c7ded74ae4490349b1bd31f9bb84ee484fdbaa57a3b9 242602843aa968145f166ac9cb6a34a887f30aa608803d3393e8d7581357427a 46d28ed72313bb1cabd592427a9c74282ae5fd54e83a2c550ee03ebd9a64168c 2779bc033cc7f449f73c5670a3a8823c1a8c949855527d13d18930b404619f04a6e344d05eb5c2b73ca866136cc909d09621f2519137c9aac33289fe73b5cd002633a4cfa6ac1753b128e17df4b88173a1f6503e1a569ac8139303041c07060829119922c4e1bc29202207ee3629ba23a743465e5319061fb2afec795fb2e70a3472db30a25f6bfdff0acbb80d1dc0cceba8e4d395d3a473a789af59f6f8331cf9a9521cbd41cd29d7a1e3108ffb6c9375ed0b2f01ea982e1b2cea2f0199af0fca186e7cb987ff19f70fdeaccccbba5ca86c8105825aea941905e21aeb6d8903f9ecb334301f1b6771efda419f1cd19bf1d224a2298a9ab536f0fd153c6e870e4e4d74dbfb074cbf83e639bf04e5c6d2abcdb1bdd4c5c7ae24b0592c5e334e0fa96783de6256bd081790e9d1eff0dee41340861a56253a81577067404caa45017c4af7e9189e19a664a5bcc5b4e2a67623d71fa02f4320aab4a5de8e29890d010c37e73206ffec2b869fadc78486a35a17e36576fa980971a79127263c1fed0e52c3bdaaacd85e4640e0af22da33ae35922d8987245f58a8d240d216a5655d07f1d11482eadc7dc8d50c7e3eebed2035fece3ff51c6c771e5e1a437dc2d23d0a0818d2cb65d00a0c10472a3231fe00aacf490663faaa60c2dd6c0be250fe0c09b7bc5e4fb354471de52a6787cd553e53215d2799ec68e2ee2935904117f4cd083f8201f2e6fcfadda216d98ec193cc16e7074d18ac59cafe5e43c2e37df9740c5b03b2410665af8ce5913b108075abfbede89f41f337e968907a5e9f3534f8015fa57ef43d25fc09e828a8e35e1af56bf5e88f3456bb6c1754b1959307be350805a30f74687e1b7baefda6d530cfab0c33790e7b08d3199074c46c39eea3530be126677e33dc78af16fa02c50861fb1e02bb147c28b6f8dd6344ff5c957f2b0094f477a779e6c9af724916be495f81ac8aca80b6ef7956d4623099a935a94e05c69d06ecc9342c6915f2ab3f1766cf14855ef6e9134bb65c3ef4491c78cc8c00eea25d1645bc3ca0c1e8708fec654cfd8955d8dfb7b85905bb96d9382f4a1b0bb787134b7f72403f2469760e4f65c5f6003f94cce0761030e30f8121f013470a58c6f6bf5dea855ed9b89e5608c3952e73439f9e1051adb4f00fd8ad51c65a03f63046adb9722c443f24a8d351835b17496b03cf8e4c9ebf3e3830559a33c20d6887f08d9eed808c033085593a131ac6a4e1940eb475b94e15eda2abd6a5f00836994f7cb183b8b89b2e4e925b8d44915d79047f14ada2eb23931dbb52423e0965444828ab278346aacd713c4de86935c5af0f30c36131d15f9bb0cf4f18fe0c270c898b03f92e13cf24523f1875142dfc895590c6055084804ba9622a685903a6f9d47bb3fdbb0b3c5acc61d0f3f0491c5e347871b3b5be5cc99b42509eb004bfb80f3084b6bca23e523e34b447603852d7261576065082ac65ebb7ad247305bc8d7bf2605ebdc838b0a7f20571924f1ae7ac8ed6ba6d3418b5bf54866cdd024696406209434e62949c72d671450214da30cd7e7474c79109038209325a9f05f9a63bf7d525b39c68e46c3b9bd0030798e421be3dd30a922a9026d5b3ea7004c504d350a85ddba7950bf71ce8ee27ab470b95913d292b5ac434413a94d3c309eee1ad70006e50d2ae4856da411321ea12c6267e2f3a7456d31373f1e4f9fb0e false +check_ring_signature ba46caa2a05df55a6937d22198355d973a3c5cea85c6ad97975784ae7641cd69 1d0105f933272503ad670fc8d82f77d326089e91fb9681ab44790bcc40707597 1 2930f8bbb3508d57fc316bde03dbd7c59205305087dd3243bf6132429ee64775 80fa35dabd2bd02f87ac13582718118ed224fdfb8e4900ce8b8a5c1423a31805e592a0ce7c61286d7aff1e81d7bf555ddfe8e7a7825e459ce3328f1704e4740a true +check_ring_signature 1a890c211440dba86a3517771a786c5f27e6d848c69ee52f0de33fa243dde3c5 614cef056ab02fdad1d3f3030c6a485e6a972ee98156c5da56eba77137bb434f 46 95aafc43ee4fd1baaa38bcb800e399147e1289ba133f59b93b3a3fb8cf95d921 5f251ae843b1b3df704d9564487fe66d5090734b69aac9123865d672f033cadc 92b44c1ea5db8dbf5f7e5a56ec15419f0838a50ad8a60f5924eda99b24a5ca1b 147d24936d6b548ab235da0d963ec02a79f60e477de53ef19447405bf04d003f a5260275168df9ba713ddeac5556a6f99f372aa786d768be2dc0140ac5bee15e 0c514db0835a70a3a94ce8fddbf768e9d1973645aba7068d9a8a0075ac75cd92 e70abbee64dade639c5eee4325c2892aff033839d0c83e6a1e79090a0c9c2395 7d55fd633ba91b884c7054ed9dcf5bd46bb0ce00feabb490bed6a4ef79ec3bd9 840ec7c207e201b2870b8b71fbe0fc65e4b17cc9f478c78a23faf32202275eff fe271e6146818e3473ca3b945b35b58cf5cac5b8cde716f6e0aefcf843a03035 cb3963a308b268986f5adadc2e14c87d7c904cf0b625bf049ec782a5dabd59f3 b3d3dd270a2bdc8fb0325513257c34c253a6efb5d677887937ff294d214e5d30 7a925d0e5d6b45f8674c49998815dd374b7db5f1ec17b88dc473e6f7b98fe53f 76d9b0372e0ae24120c81dcf62030d8eda8b5801e543e380cf7796d1d0de73bd cd32a290cf3e2a8f1bb99760ba314b3c8293311bd41acd3c0b73d38ae22d7b84 4519d64bb54e5886786781b951fb3b2a98e5da760566ec53c7dd17f9194892d7 9e4edb7f2b930fbf4607dad614c8b09d26f99e75f771758b891169ebd6f06636 b7aa035e7099fce350e460b12ec3aed0c90ceb7c985cc944edff09554da97b73 2e7cd609cf8b0e851762381af7995d344cc210e63686a9818aea8fe3d61b8b84 1e4772a6db54810e5cbc2197788f40f748925a7575a9cce93c61ee74e70d4cde fd0196ecea8742245b6a6cf52c0c83ddf3341bb5ee2e1c932f36b9304fe4259c 7c80cfd3712ac398b551bacc4b61d9d04b96efb27cd956d7a3ac8b893b529d6d fca43ff0e77b8b65dc5c543758e773210fe40e440c1dfe0e2f44dbaa619a5932 36977031f38347fc496cd7344858fd9e3ebd84d66e3508c120fa31b5c5e740e9 500b35c7d2070ae4782607b0e57c99ecc2bc79531e23e740cacfad1f7073ac92 84c67347f1af9b8c2979ad9745de0fa6e9901a689afc012688eaf20984e70a91 3e4ad9d19547174c1d928edd0c68dda6ab2325b7275367636bb4669261df2b6c d76e7845e2380dc1ed860f97398cee79044686b35f8c15b1d29ced0a10dea8af 9874d841383f19e265364633a54509cf29e9d0221de37dfb21d994bc5e81c858 da4dc829aac793e38a435ad71ffb976acec8feedb5e69da62522d361a3f4f35e 930aa11e0060860051b96c625ff09bfb322101f82761cf47a915f7912d6d4829 f6d2b35a05c470e030ab015f44c5ef5a9c6995e36d54997261cd2fca3c644524 f486e0c329110e0d15a9c5ae5cf04fa2f5877970f4969b1082140a6e7bbfaf79 5f0b40d83002383709eff46a916919c49f47d7dfc1b772db98c7e8519af33714 d8bdfaca504786baac6cb64cf2437864b05661558838a94c3291796ff3366fdd 1816fa19bd811d007f3283349006dffaa68bb9bd345855552fc1342b5e71c358 6d234b69fbf7a060a9deb77a0f65aa1ae56a8865a51e24b35d85be7f7f3cb017 4dbc6ce4b985ec7040efa234c51967a14a8b9405bf138f5209d32460164c87d8 54ec45d7c98511d845f2c8f9a32dd1cd61c51a811137cbeced0b6dd29e2d4adf 640c87d49b0f84ecee193ed12efa81f306667057feacda8f13feb9ba953ca697 62498f42b51cdbe26af33525927a5724331bdc4bd7c64787f15e800905834dad b6b50237c809eaff35c58728a63e0f52597c242550e5fd1b73f20a0c0621bb3e 601870dfa54e5659cfb6796c2958b0b6cdc923a586763c73948bdcb8163c3539 2a0252aed1dfcc874db68bd869c7947f563c16c3d2db167cb50c0240000c32e4 fa2e5d1f41ec0e404ad38c5c4985be41d55491f548faa9f401b8bf11d966880f e62f4a043d514f1ef9f51e5b26579c2502c89b8ea8f879bd09040e5d2a25df5f a41c98dfdfba8de7abd1b2b7099133da7e833e5c5ca1db759023d788e94bd30de9237a58ac309cb5e69e61db991f7dd2d81ebefa989a77d79a2502844980f60ff877db7f43008229619e5ad8ce9a54e24864f002a93db2af5f6f4a2698088506a89e70ef9d91683aea4e589c92bdb5bfbd97c9a121ad0f7b0e3c11b2f9b8ad045cb33a7f54a9b8e751895548c99c44fd6c62934825a7e301014a6e3eadf625038041804f758862533db1d3dc4961336a9461cefcb4750f4c5cbed8ab5020fb0a534baebe7b3ba63cea31acb53d714a598bca50e6d17686c83156cbbb77282dc13a53b51fcc248316f7f2da97be8198837a5160da91c554990217d74ee41e7c05137cbde642955b67800128659c8699563fc18e97b4d3c8ebd451901e85ad3f0947184bc72e4a5a950a98779aeb433ba0df6e189b4e2b8c02f48091aa5335690acaa7a0bf9c7246cea1f58c23f3a767db6eed89b8fdf50a8941f484eb1783f206feea1c3b3b12c8072e2e89c4695b3fd0ba6ae4950a4cad3225bb5b57db30a80e261b77c5e0d9c945397ce096d885bdc1d58e76da808942ac486fc05eef5e0001160cfa1a14c9cb3ba0e710285b25c4869a1a1e62e66bbbadf5ac7d6276ca7c07cfaf7c7163bd074e07391020806035aec27607bfaf93de33235131482dbf3d09a8e1e7f19d3a5c3ca9563ca54fc0ad34b012892fdee0024029f92406a2cde901700791094ed978ce1bce159aa23f1001d0477f2d483e35e3148897e36ec40703c3da3df2f9bad8cb3994e8db763b78d2254ec48cc55dcd3dbeba4529fb002e0788bc3460f3912b790620a12377734f9354576d66712875d93eaf5d3b062e9e01f27c2fcd380ea7427109cabd86fb4648213c074256afde6cfc18f5bd58b3ae069bd1b7dea9afd7090534957212fb1385492989a4982dfd5ee26933d98dbc0f0a528bd6f97a77389659f6f34bda88a2a6c4f0fd0749a308d3d935e893df38c90fd91e8dfef64d83095389e3d4e569021b9478154962f87406c29e5c646d6588094e6a8d565343742fe680f0c4523e53624d21149e9912ec33c04aec594886fa09856fae45b62bf23ba95571bf3b8436c072195934b533a64839bca0b92bee2a035c4c2463d1d461d885b4b74ec4f8453a2d6fa000e22f04523e1ce1b01023ac02f8f19e5ad8c562f927fb4c5d5abd5e621277197414e081e3d25f213e02f6e401dc146c67544bd3a2d542fa5778933823e615bdaa534a0d8ad506df5e6ee5b405e70554e62730066b545eda05f2fe14d4ef85dc5fd9d9b0fd34cda9e44b2b18024bbc8a407aa2ca676ccfe186805ed52e0c1500e6a76ce834fa669221c277100d8b4869758df10bf663247d5d5356bc8d4f06e3a352519056ccbe66d30bdc6d09558635114c1961da9ad57c7c40f5f9defede1d6abaacabff3a152c949d5b1206abd4dc0e619a744bb3717205b1c31ba3b20a4d28161e09310a68c42c8429190f09f6302d88d7c6026950844a87ca08c317cf425bae1243a799238c7e2a1d98080f177f5d1528f71b2a955931bed30a4e4fa3a3cb15f5660555da10634c0512075afa762617f17dbff6808aef188d4074662170c55c6cd7a5378474a405680b0236c777dd729d8c9c0aa51a031ecc8ab0dfffc4e463e83cf853b96d6a65207d03d1d2f710e7ac4bf07b2e64ea595363ed936c7fa043bc7b355b70171ed3e5800ee8e697887b4a94842590e0d9b407109f1ae20005adf2054631968e3c1564d40c85a392425cd3c22e7514369c0b9232928428e7a48d3c17612e210094a7787c0bd6b7d0831b7a9dfe12ceff7da1cfc1ee8943a9756a00d1d68b5d58fd54233c0821ef3e481e48b15069b57f5354f5cb8e00fab69b5ccd5e0b6251aa98b9e771023252d80f370e508d2b15bd935a6a3587fa33f06a3e7087d43ffbbead6e30340a9335f69bcb1cc4f0c9a9b9587a0a55319445c2548fc00d2532c691c5152ee6089781a865f6c0864edee7fe240c8dbdb0dffe028c8f6f01f6f1b194270816d6060318c1ae94d0b626bc150b0dbb0426bf2a442754d45e9689e24959ea7139ff0bbea07beadd49f778078ecf934fe61bb3bbf65884b28fbf99e81edfc17ed97608bb44e74c160a6a1c37485a5d45405924f85eba1d3827971c80cc812fc5da590929001045caed81da3e783b706e2ec5b06aec1246fce79a904bd897b627df760883adede17a41bfc4bda40d783ec7ce9ba0d43266afe5eb3edd8a11e685304f04f95d55525d356453612884fd98d2c3d43ce58023b4c78e0bdcb72b8f698112018d481e425b4d7c9715eada0abe779278384aa0269909230b084adcf7b11db10dd33d1bd98f515b5ab0795d50e44083b353c1c5f0a599c3c8521a7bfc7de3550ce67189acecaf1a3ed621f473fe971bedb566aaebf5b3183a6361f6072646300c58ee354042883debfadae2fc414b64b40c05d1e9d5d2a70f6442c0d8effe540055323cf570f5fbf6548006d2090c1a991a1074c2392e3fa9a14dccff3f73d00b0f99cc0f9c9c311ee43f502c1f8507771d2726b8d875fd389d39fb0c00a2e90950aeda9a4c10829b4fb40070e3c88c1898e5fffb85c0da92c7496635c850130ab03e98672f3334c9773ad3eef1ff043a9bb055367fba11915eb9f11984d9340e54bddc966ba2b28539fc2e1096410f7e463f2bdc7739fda928c914230053c408f6d44df720a2e76a8ca1019daa4828ac2db3ee833ed14770c09711da8025710ed3ffc390f16e213e5cfcd790c68b9b495d17725cb0d13597a5d59183b3e85e07e31f68e5074ae62932d4f9e96d56b88994d267e521ca5e05566e92296478fc04fae62f7c4a625a76c4723c4840480fa5973a353fc424683d9112157e8cd4b50df1c3f9c69857b77723c9999c2aa13732ce605932a68ea766c627cb08562a8006087952b3551aef18098ffbe5d01017f909520cb105dc48470ab561222172df0e7f0d046cbfc64a7aa354a434c29da7a6cba0f19acc7d9c8107e2d030ccf8e6052fb441c6c571ae68090970224d6d5e73f1ceaa19dbcdc6fe1345886605bccd06099421ba1143e6abc37d5029ccc1569f92d45a8bbb8507f479570354404db906721a27c62792158c302fe275d0a63051dea122e5730b3dab0526b6124df04b0295f609f3e1b0b9d8064d138bbe13b37a59fcc554f1af2112a69086d9ae5fb8090dd507ccf711a2805da82c3c3b384ed6a4565575d28174cc347f6474be62900651045dce177bb65b3f6d85f5d796443186706fca9bdc23dd09209d5baffbae0f17e599a87510121cc773b37d3b1c8893740c94c687a143a7f0ca9eaaea21ef065d92908dfd58745c97596c7a03babbc5e6ce73fa8ddb6ce91178a74e781af204c295f65941514434d71116a7c528d9280188c41e7d8a4aa85a39b6fda1801c02d7f2ae5730c8abf256fa0761d74f454dcf897f5b68c82fc91b5360de510828061f3d5a758cda81ca5e8a1468987b1168d1974b3dbe9d7554b71c9c2e55f863051a2ff5ee95a0307f2df9fa25eb0f18db39b0c76b64f5ac9ef36036decfd7400ef9b19f19d06b4d74a514e556a732b3c8bd99fc275b0d856faa54e150a62189002a14e77022ce4f7abe190851363139970f8e57d24f6c67806e235a42957f990f34b7f4ba3c3bc778727837b4629ce237a51f7877203bf3630c253bbd68fc5906860040499fccb8de2b8c3731ff9be0f362c86ef98f07fb7e45df35e77c008b0cf4c656b32b4f4270ccc6fcbb3f1240db6e03df5d3c6fd8a394875d3aa0028c0c0a4e0ed2d488a86e8558b17eb44875d965852c66e98f972278098a6bf03bf70166acf132b68bf9bdcb3b99a898d218be41f412d7288792d93ae022ac124ad60e71715c476b306d601ecb30a367081687a93f46d43baeff81e1cc0d12424532009d10e351dcd1fc435219e0d89983994701818850f80945563f1f6f61b693560c97f22c436ddad88a250dec051f5921b3b8b98d6e9bec47a0489b9fe19b945205559245aefe994deeeffbb64491bd10c2cb39c5cc0dbba65b01dccdfcdb231308a804ec1dd2f6c4ae288c27bc2b921ccf0d3195f6da4e47abc262bfd8004afe0c71bb9d027a11da13703e10cdd1db2981e0199dac2f773584799db4375972f40e false +check_ring_signature 1a6accf1855c0f6027aa946983297d5644d298fc5caf2001cab1840464edcc48 2e2889ebf866b2c66777e5f8b7a37dd1f9f815fcc8bc96d5e2788b557582396b 3 9afd900ad0fdb86d8c43d58b6257a9f0ee469d0285d312d0a09f6a6e183418e0 98d5603af695e49d6ad886ae4e5712e74c8cdc9ca61cac9eac4f130e260f4af5 2d16465a8c153247fdcffb8e0b17546cfa886f996a8f142115332c5fb6dca138 b9f325ec0896cbfc46101808b39a43c67a8e42424a7f7ce67c06d32fe531b700341ef3337647262f53621134fbcd417aa7ba99014cc9fcaf280e3d59bf135c012fa077f5504dfc8c23654128d75b91bf2ce72628800bdb59987765017d8da6081ff1c2dc148558a1b98f7436aaf8c4f9785484106d8f778a86d3594d43896b0a7e058628531df36eeab5fabbb0cdd4ddfaf86ca3d5d511374514dce82216c1027352d65010f73dc81aa7e90ae834db1df6fbefa3c960280041cd74208151f606 true +check_ring_signature 9cde237c0e66052a331afea1814aab5aa0f1ca4992aca83c6542551c717ab4cf 6f93d3fb1a2fbd42b8eb2ebc0d416215dc19d0010112d510ada951e4a2d0b298 4 60a7632a74bc43bc200a2feb8d69066878850b4a27a931c71e4f6d73c30f909a d7d2d554cf455f165790a28900b495aee648a1f85fb0893a53902915ea3f5b6c ce8bf75620ba65a219a8acc75597a219e6d4b2bd858d4dc14aaf1367ee7599cb 2fe65b3c859636a599a81b5041c2baec7e951ee1fca9b4012bfebcec242b4023 cb8eb4427aacac3ed268e6bcf41cd7ea9a3aa246f640d6c276114487b487082e2dad9d179b7bcac1d268b79d5c6c14896e45f5a99098a6cfae3671cf46f7b10b7639c6d3938f95ae3a31e7eaa3a9b6092b29dddb4cb45a5492432f5e5dad8d06c92b7a847ed0ec249373c5d89ba79952a8a2c759b945d6ec82436c6d45e06dd12ecd831287f6e2014aa485d67a074411a180564137fdf9cbf32f025cde55f7071690e4f8db3aa26197e810ade4ef9473c2e8e41a3b9c1c1f5ab15818ad8bae0ed75e9516f4f8c368035b0f2b15c4a862139dee404436bbd853c774f07a75870978e0f4c5168953f2cab4ce1ddb5ccdae84b0bcdaf77d9aabdd12273686c6fa0d false +check_ring_signature a4be8f17cdec1a2c125fd5cf5134a83f47e38354c8c00fd1be80892cc0314abf f3332e24044c1c150bed5a921db9e38cd1bb2e02bca5bf299eaab4790d9efd25 47 4ff19d5f290a6f5d5824dc7dda1e42f965f3264ecb0b9a3da8dbad31245b9d72 f0243ac8b93cd4ceafd51241f58cb0cb2c573d90cfc3bdc77ffde56200c6f06b e61f4706b15fbecaea98cfdfd4ab4412d425d418ef4f081825663a83e5b89883 bf4b0aedb956093d10a51920c4ab420594c76694800240efa2a777b07f92a1bf 4ca8df46e096c5650782b38e45eb4669ef5cee4a620111b734194c8d1e0dc8b5 55aea4abb25294d62a66defd1147e1298e5021a81dd842aaa4238809df0612ba b4a7a2984d085d5b84c528765a386d96a9fb8dc6b34a235b543d08a5bd901c5e 6b75162f89dc68f2354e06e9506338d60a02933a7d0e7deb35bf5233f87561b3 daee07c6e5dba9b771a7802b4066eaca502d536ecc3c582cde84a0abffdee541 b01da4a917d07bced287f5fa22f666c73b8f6ea8583b0933fe8d904a329e9b31 1dadd6337cc00d1e628657251e26ddbcb22dcc255118794fe6891a8df27cec76 a77bb80235362ab9f1e0e10490c115ce954d6f2c9ea7d98cc696fae371c4f992 5710674d2e778a21340be9e09930f55ddc0636d7a03f9fc54d4599d56c8766a0 612e029206c49267c2ae4733d7ba6292c8699702493e291282a59824e36b7ab0 aab3db5d5529efd462f17d442c701811b7c2bdfdb3e0e198b177f2ac985308b0 81dc4ba78fcb504b74a51453d46ed5140b619e58490497ad4f387d735fcd9cd9 70bbbf2b48fe47f8070295e0ecad67e3cc526300f7ef36ff700aebf90386a8fd ce8c0dbd26d1d1d8e445a18af9c39acb07632de4bda795dc98ddaa31ba3efc89 340b877353df2c04d9116c98b1b1a9224501bff20dd2e34467279274dabebf2b bd9cf2c7ddb22e54164f437802ec83dbae487fe0fd7b3c86ec44243e66d19fe5 5049e062478ec8c78d29234c8e8fc1a5c38ace99984b1ecdde51c5c24164407c 8d9c1365a3122e9202a9e80190c95530d0af7ec66961bb74005165b0efe21ad0 9ec242a2844d139b5c07f2a29ecce485d960a6753fe2e3e403e1a6389c985d5e b4b4427b970e370f4f63c29a0c53f25de5ceb9de2efbc2dce423bf9ee7431877 737af32e8ae2bc24a77cfed08e414c159fdfc14988276513b9e0bc0cebbab77d 87670c13766b0c7f46388235af98ed37b7be7ef73ba053a25d19cfedb9a0ba8e c153a850db0515b11c851887bd0bb16fde2216d912d36a36e470febce45fe4f8 d37c60bf5e0df3ff0608f4c8d7b9f2b6ed66313e25b2f178e58accdeda76f510 38a653c8781dd83b00ac9c23438889aadc334e30bb27c90614987e02d023c858 39e10199b63f757f5e369472037f253586a36858a8f25aa1ed6cbbab9e69ec14 0144fcf0fe7b61d0567ff5e22b5e5e4e093459912973b6976b5bfe86565588b0 514268dba61badd5673650cc6078f9c91bc78194a7622052fa9b9a5399d6a11b 7a1abb307599152189f729f24dd4227093b5efcc069c477a59d638361952dd77 35daa437e5698b57ed3e623480bb03664dd93da6a7994ee58e21a042a392a6d3 da21b5d9b6320854c45bcd0df2dbe408f2031e2838ed2b0f39f471b3e28c551e ed34a4de1a49deae9687e3d6f5c7de7c50ef25ec8467d251995b69c947073d67 06482c18387d721f8410c33cfbe1679c5809af489129f4d26298bf6d2d09ddce c6b2dc1e5b578ffa33b20e4b707c10cc13d1f5a9faf8efac7afaae0b3fbe56e4 8e35e27804f151a50598c307c5f686d42e60b447970018615a53272a587987ff 1d41fdaaaaa573d340b137403fb275ccae526c1a05494d685877ea3a65f75075 2ecad7e2dcbaba62a6aa6878784070cf42c1e97618d7464bb2f7a85a34203a98 dedb9933d690109c948f96ace4d732940b1fe7fe2c67715213baf6f2db6090ff 49336441ffa1f49890071fcd4a656ad4536b6ca897f0852ddea2b801261604f4 fb3a5552093b20dc7a7d47c92a1b0aea14bf31494f86f47d77d16728e012a8ac faea682dbb91a18f2881fbe1c8b8e79b4c8b3963b72fa112c7e30bd188cdfdce 3c60434873aebc3d839fce0cb69b6613e03423f17dfd539c411aff41fffe6b6f 7c841f9830555d22c906750943ac74fdb112fa1ee8da2c52acb0d5352b0779e2 b58bf26193cadfbb4b2bd29db77bcc9ec28b775196c754e0e4f3b29cd6ee6109050f79a4e5c8c46e726501470e294ddb987d1a90f5c0b46028349250f8edf809ec84979aef677f7cf22b954a4f42b5af18f2d3283c1c443c1fd32759929a2f0ac718dfafede3c30aa0e08787c589f5ffa6dbde68192bc29e3dacc75bcb7cc702b075776c80c6d19e26cb8287b31837a205c7565c922eaa176149e1895bdcbf01f25f16416abc74715f2c29f4254bf8352ffb5689c46f36435cd3179151e6650d4ed8a18cdf31274534509bceb241fbd10f5a8ed7625b062ca39020d769e83b0082ce7af980fd47ed93110bc1c2b545527258256e960643abfe7cb0c472e1830b2ce0d6d109ebc749c158aa7ab55f426c23ea5c3b4c6e4b67bb71a5bd6f3e2b09d43883a98b3aab85887b1870d381197ca2168e0f827e1a816be79720bcd566023eb018bd504ddc30ab5bfbdad58956c944a1b57ba1418f52b9a24f5f76ece006b1a4a03c33e4daefc3d83bb3a0dd2bb69aeefcc26def2f49580d5f773c0c4708960059d040823af1d681fdca4f8d393bf8e6c94f19dda41ab295d9dd6393cf04f3eacc93d0de6442bfa74de60f8e46a3e80669027f0c0ab43bcbc7ae377eef066be0b1901cb7cebd9760a218ae5a590fcd8db04766b2fc774de432d115594a034f86675c6805ccc7d6bfd10b5950229a9f6bf24c49f7a1545491ff55c6163103e733a2a6dc3efa51153ec5ac34ba0eb65f6be7fb98186b55c2c235955d83530064364d52bc2bb2e09517c020990ac04d196cb119fa4cba86bc4aa52c19270802e27133da0b3e29f4ed47f65de07b2cb069ce8aa8b9648ad93f2fbfe604adb1054320e84f8d10e83d3614d0fdf0ae6234e52a0c31f24c91206780fb85fdb57f0dda8286f0cd0e65660b393aa4f1ab62ab63d6ccfc07e19215a6457919d715bf0a11d331f9e457fe1cab7cfe7d9f0d0576b5749fac740cbe489022561bd0d38f019fd518e82c73eb1f7e640e8a7b875d1513b56fe1672c65d6a40f2d9ffe3252079a1b60d2a35ca7ecb991e08f21f9acc68e1200e53369741fa1b4ce958e4bd10c422ebcdf28755e94da7b063c6b69a751e201e5236a6b95d06f0588dd6b21ad0898d6e63d565f068853c30807f47ca830989c2b350edbc26a6465ee7237ae420a075e6a194cd296f9c47cf0ba7e681ac0359c77f04324291dff88b8aa064aa3aa3de21d25a291a823132e9b408760d907cf47aa510deaeddf3e012e4532094b0aaad22e0da2c024517a19507eeaea7104ebe0bfe27f7b3f131203c624960ef902ae132b291241f44aafae7be1094ed106f086d1e9b98d2de7198abaddaccbed0e4b873865f1a343dd6c623f401e06da4501044aa759020b28a0befea08ac5120af73b2c7496ff88c79b5d3f25e5e8790c3bbe947560b6b70f494396e8f2957705888e2ac1adfc1d20cce7f6e95690b89fc906f7acfcb9eb1324c9dc2f5f996306e221ff19bd9317ea52f302bed6ec11ee907370c0c7bfe4ddd5d0345af5669c0c864fbb4ab89a54b3ad0be7151bc5292be69d3fec0a46bba28e1a28cf877a4900aee3cd0b899e65efa4f118654a6858810dfeec49d1801670cb807069376cc30bf2e545917cbb7964009c6f4551311c3d0db58c7e7a9948b7b2699d7d359bd20e51c915e625d55f6e7a021b76b0be15aee9f2c18686de8da9d7948c6dc37ab0049094485c009a0ee8a81b6bc83aa2bd5a8153d34460ee326bc00ba5df78734406d61c4f452e1a172ec84fbf79f9b10bcd2f379c88ef605a1fffa742f50146200976031c86656bfbb1f8a7af74e1d616ef7a523bad0e4f81defb7ce9f82ca6db0a0b1c1ae597bb40857590111e41cf553ef85908208e99896d5f8bf86e32711201f6596491153fa7c77592777daf90b964b49f9c6b305d0efaea6a4069d9400c072f059104972fb28999d5008b5b6a80f14b96d2f7193aa60b8daaf0929457aa083e639aafe8f0ca0f783690e4f88dfa5eac1545c330c1ed7fb475bec4c7dcf30b049daa0d26bf1aa61d524b7c1834187dd0e6f81f6d0b85c66e337199776b3403b64a2c54d84672bbd3989aebbd67b94e29cdf86f3853ceb9ec8fd0c24bd49409e5040c4ac140fbdd2c96bd3b2bccccf15c3c896a77dacc245469f89a43a8430d9b7b0b1adb2f107caee48e8dab69f8ee0b93cca3a1b19db2c1b14d22da0b720893732f05493eaa2ab82a028fa9cac4c63554077afd9dbeaa7d58439ff062e60e3b890d5b5fddfebfaa6f134cd47452a0ab43b9841fa3c939d4dd505b0851470009e6c7fb2c432a9e0a4da671fd99e8fbbab79a160692a1796a39586a26cc8704a5ddce720a86077375058d7f5124e2247ac073fa84e1b68f6800025afd495c08b2cf707c0af357f86ee15052c41bff7819965e2d7f495e1fbf289ef9f2aa150511a42cc5dc3a1526a0681a80e5351f73aa17973cf921d6f6324fa30fa8d34b0e72441da9a989cd67a055a5d64edc7d1ef4e1b72ae30831ffeaec8e0a58153801f7d8b123a7977691cead80b51fe8fa1a3fbf682f80c4272910ba6d4fbf24b70854a793c0872ed55416e765cc32618c69baf1e5c47275c17cf0aa629f93e38c091af07019c82178db4947e566c4715d2e7cca39eac02a38a3aff6f53b06bb00034b21a9f8e077e26403d970a0fb936e87b856247cac8a7074dd853f5cbc14e60ce084ab997f0760276a3480c3cac7757c18953294dc9d33df985afce054b05606e2cc985444e49871739868cc4d435b01deebc03420907a2fd12a93f4bf784e0a927d6f65b69083f11f5d831e22c2cff97edb44688fd91e91ac0ab2474257d10541915f0156afcaf748a96b5f6778e41619685ae8bb989f7a11ca598b6b98ed07d15bb8f5091c21b002c48831f473f80871da5f4d97a2c730fb19d99bed61fb0fb7cfafac8de845e734ad6015ce5fe5f3901e98f0ef20bb6d9030b0f22ef0600f633ce5d2da435625c92720760904ad959d961a7ed1e64e269475e24b37a0f50b94a55a8bb323286270789092d70e3bfb201caebeb27dd0053804455fdff020065cac8ad5b7531fc611904038baea0dfca142b58128a7aa8a200ad757ad32db0458afac269f012c4d87c14d3bddb254628374ef102932e3ebfe245761cb553805671655fa7b222cca6edddf4a7c02f967c8149dea628ea16dd5e962566a99930ea02d150dabd2728682c10f010d402cb4d16feabe71d523c645c7a4754e6efe0331e1b931bf98dd1a62aa84b8e28eb1bb6ab816bb011d61ab098d34fdfb39a8083904fedceb400fdc0a0a699fca5cd8c9e225dbe18e753c7a672741084c5ecd035df6bffb2baa1d282794c990f1155efb0b45deb30b7cb5da2732895c6f70a90788c013474764f3cbac7d5dd909e291feafb9db4414c6961bc52f6f84ae5d380f3c45a7c0a58337e77164d9e57b54ef6644c07936306515e9d0b45fe332b391067c3af3b59a4a732206c5036f644c7c39466b97d681f54ddb809f29653fd4e101455fcddb96549de337d823526f5fb9052599b0916b306c98266859a291badb00ac02cf423b8b765125a22b03a7fa121c25b4920c456610f5fa76298476d3990a6028275910c79f4d457e8d37eb45f44a4108e21c2e4c0771b75cbc1aff509f0390fd0d5265c6a2458614ebdbe0822c2a9923ef143f39eeeff17257df1bfcd602937ab86d3d47878ac040624e756b85a52d66188e0c87094c0a00398a85c203082b5381b353bbac0eea946805cbd11f22072b54edfb7707776bc0edf4911f79080dd14b30cb861855351db123cf4d890440b916130eaea982085dc57ae2cf2a01f00495c2375c40a5985e729b6e6ce5087d0ed973b1d19842cf57f81c8ee2cc061cc0f1bb3368d7b7c5670d35356f3f6d4240ffebe45dad9d47de8a7a74ab370c7cff1b29bdd6bf3de65c76ac1ce9a1886b44fd882bbce988e43f4aa3150ad20caf4de477ba28d4a5a8374fc9f4e7ef10206636a9df0843e89e2730e58b6201002044ebd3ce8a0c2db07e55ece458bc4d22651aaf5da9cb229b720be940810402bff8ffaa78fd53fc9675c688f29da61e63c7b5f94f837937158c81d2ab717d0a3e57a2ed4199caec3b885501c41ed33f6b638bedcbcba2ba0dbdaca3fbd83d03e2f95305c87b005a68f68a52aa3fb143b0d6ee2e9d67bba862880ff301627c0bcdc3b14002c2e00c3c064f5466205f37234f4a7d92dcbc103dc0793298e8cb0e false +check_ring_signature bc0c92433f91919d9fea793e74801b7eaca916179d2f9e83d13cc43b35bdf31f 20bd581cf4c9d92e59e6aeb7bf7732fab1c5e0a6babfef8e61e632d3e6a63bf7 12 6b0b7200589a25d1a7d30f8135812fc98c1160d7538b9a263bdf8a3f296b1a75 213a4bf68b51619a89e2f32a643570d401e4195ffc635e63aa05952caabc591e be893ecd39a5dc7b7989588c5bccd8f2667e92380f610479fced8dab1d62a2aa 78273bf4e75281645553c6e05a912d154befc4799904a7a44d8d334e8910152e 4136ad231ae81b1680c1010876a0001fa8c601d04106b2e7d3ef0ad44c5731e9 5130c9215940cc87e03279ea0d26d690d7c401cb31691a76cf82e8802ebeac26 174a903151183f36e20d3a31fdfaf85151b4a542946cf13644395dae655fa0ca 5d3f928e933ade68ae6632afab6c0aaf394b60ccbc23756eca6e0c5ed4da323e ad8ab5ea855b59168628165fad1ead54cc3da28b99c12e74c152db6da68111ff 6125401ecd4bae8b443b3d0757dc580aceb5d90ab0a355df823a0039e22c6385 240510433b77fc116c13544ce9f2f4cdeaca2166468e2857bd60217e1ab7cb22 9bd8e1f1d0044941e1aade0a34af6e53051141d7b786773f429b5881bfbc4f93 214bedbdf496fb656637791b97fbad3c1c817af33889bbe0604fbff4b0c2c70f867d1e74a8ac3eb70c883b33047c6d419d55a715fa753312ef5b3d3c709844098c50efbb59ebf2c13d9a2f7628384265122e7571181c0ef22b8db057cf831d08515c190141f24abc0eb0a5fe7b69137096ba3280f4816020dbb34ef19eb2f50e73a9f8895036ed504ec75246c2f4722bb40e1bf2abe2280bddd9ac6bfdeda4f502539b4fd10c454c69f7a41a7b8a650f93e2b3731d012a3227fd295c50664b9d3258de8f5a34209fc523ea960225f0d32951f0f026aa88a67a727d9684a7bb0191a5bd8174d6bafacd81bda180be09a283ae58aacaa70762990ba5d18770e00e29f064125be7abb1cff2b2d48fc6521d88d3176298e18a639a1deaeaf38ce5073f484bcee0650022575cbe5b95251166d9a396d50faa03bdc62a4cdd3836f606f887a19fcbe7061a407ea20468b6989e3611c7d48796c404d0b3683b56a34d0e68491e9dd9c76eef34bcb696c7d7596bb05116e33fd8e743e06406a917c8c105c1a3b21450d9a0d0dbf5a242fce64796ad94f54cc02d4634595cf5dd7c2fed05524f21ffdb4d44858d66679310c964393dbda1c026cfe3d2fce7fd00a6ed2e01dbb2c57f36960f1c51de703996ff318165f8eea213471a4b223f6e93cac6b700ca4d05d4793948f34c2d5d66641c5b81195cdc9f1025168a84f0d101628dbd00716bc062e85ecde5bafe632a7611be8b948acef485a2a247d3f67e53693cb901ebb8c724c7296ef2fb995d4c62b27d2ad4bcff00cc538926497b3b6240991d0f393056a9ba06e96ae46bfc9be562e2d20f87ced541b390d3477ac7942b958027567f6483f33d732cb7775f41922fada3df51dd96ea7be3d60604c1c2e1f7a7071bfebbcbb3e7040ec98da92a8851aeb6c08ca971fea6a3fa5a5434d81321cdc66418526dd7c493188d9e6afcf4e3b96ab2f7555fea6054929d977200b539a402fbd9644e925078ce0f2a31e2e1d36f1543d3e2e24286e638ca5e0b79311b1d668aba7944ea072b185b4871f6505cae22e9b28d4d0cfc7003877937353d97950e false +check_ring_signature 47089e6d40d5e6bb1d574a2eb2bad1565e49ae0968339c4c1a5e64fe3e49e7fc dadfaf76597df9d474209cc1844983952f20517ec704e52024606d7f2c5813b3 3 14a7a65a68395d6fc72ae575bec8aae182626b2767ef07568c357f2f38fe6411 2a79cdf47fa13c9231718dbdabaef9a18a0db4bc91196de358da3b3a675ad5a4 5b2bae0148508bf2186e89df566ce16af7a2f8f2e6cc40c5556784f75df86b5c 89aeab2eff87c5a222f18b3ca18b1743ad1bd424cccf9aa2fa0527ac7ba0bd01674d072b66e039fa53088023e86d7f2d0f7737e838f5f8d67414797d16188f0ffb0d6e6698d1f1d120b24f8439ea02ab80c0162367e3ca98f91e494139edc805c6336630dc47a260b6f9bd291fedc6393444b715cf41c99429b55eba1e7ae6ae20908507a873f2fc4eaf8f8d1fc836a4aae4b9aad97d41f888abcbbb56d7b20e1303472726df9bd5d799f17da1d7604d8a5dcbdb860b737cd8af15575bc4857f false +check_ring_signature ef4d09473c78573f5fc44976b8e39ae668af5eb82b21aa6ad46bdcdd1ab68930 75dfbbaa346a5af179864e5eb5bbcc988f2bdf256fd53eaab9ccc74d00bd1e51 7 09ba3f711a78ff51cfdb431a0d0f0236f6fe4cfb1f75270d21d31c099b040139 5f6809f21425dcf4a3f05af4d13b69ee02101513bee697b1acfdde739f69f4c7 076fb5750d9c459e2601758bda3394d85f6ea0a2fb968b6ae8f04ae4d129461c edf300eb864f5aac90588ec081ace9af444c8beae5a610143e7471a1018d81bb cd686e691777f818c55898ba837727fe4c640fddec3e3e56c2c3781107c957de 2d705e863fe6599aecc9849d8ef0ca9fabae10a004e78ce926a25b0dabd87ac2 950a7d686f3fb4f53b77841ee6b1fbb9d1fb30eab2d05c693c3fcd399364ccfc 0365d63d96f07d601751c3aeed3ecd7114dca5df22bd6a134004da007f2b3d08bb6591f596c90c515019af7479bd2b26413f90ad6cde5099935672cc1cddd206977bb008bd4839383e99afe4da9ac52d8d3a3f1d0fae4a9d6c80b014bbbd6e09c40fb389571b19b336befbf8ff2aee54d4b3f863b7a88b92ed9c96893a8fa30e0e73d48873dabaa80a29de42125f474196442f714ec9c20ea7be70304bc0e40d85336a861e02c29873a65acf143c88bf3f986a16ae071f30349fc0f088590208b44f0f1b6867d331f266cef9e3ee446a13a529c1eb78f08db328f276a729c70c26da81c7aff2047c7ba86399d60a4c824e461b1d7a5a9b4229ac746718659b04c04f25cb753ad0a64518b6e0be738545e143c66d4ed08266159837dff99ffa0d924ccf6f33da5923cae630f15356429196fc409cef6424bf95174f8d09133e030bd26f1f3d03efdf419b37b772309c3598016deb7888d51bdbaacd40fdbe66010bef85a06d8526f3866ba6a221a4b097ed08ab1e3bd138e993593f94eb4cda0afa790db0cbeeabce3aa22dadd147adb74fd4fddbf634deb30c30d6607d7b53091e529a2d199574b7d8fce5cc61953ed047b4765d892a66818f934b7df6386201 true +check_ring_signature dc7d7739b9332fd3ea5e2b3808f7b0224be9817e541eab0c4fecbe2496c34ffb 435a9c89e9e647196200ecf3484acc26164502eb54a40e3dfd3e151fdc31f479 164 0eaa532319db9fa07ba9214c276bffe0f49998d7c2956f85088fc47abbb71894 49639c728ef50912c90cfe785797030d73b95a7c466b28b261bc49d43e6d976f 3f92dd301203c4dcd1ebe7788085ed3ad85954dd4b9be8173c4683ef0c5c7f3d 775851c63ef5170343652c7fc0209bf6a873fa61dcc7d96c5d6ee442b7f456b6 35699036969de3412d076369d97b67df0ccc073d960507afe7628414ae5154f6 92b5e84252d4407121881c774f118d21938998c821d6296a4301218e5731ed02 298bbd7e8b846b4f74f0a7ec302bc089a54fdcb8508c0b172c779d82559f303c 0eb2101eecea56b7e0d85218043f19b1c0a721ca57999db3a39a9b482e4e3a51 21f0e49595805b5a7b0be47ed0c3e23d9ed430ef56b09b4ffff4cd98ceb84a60 8dc8e8a6f961608021c4547e413a2c2120984c048aa5c306fb42c3ff9c0c4d3b 2bc51e281c056812e159c89440810174afc6a60a0b08d74185f401a908d5841e cafb4a8b613528806ed6c26510f95879219b220d650049c278557af928501b2a c3175ce38e0147c34add4305a2359b345a8d8f767afcd5dfe66e812564868495 c42c13c6c5aa646b078467f02cf5a308872f6a0ed8cd0a99d8bc89d38cc55676 3af3fd8fe9762b61fed6c54be990d193dc96c060be47b4e49b813f058554b5a3 10026e0abc0ae69a6aa4cae12ed856bff22d4651fd43156b73b0e43449bde461 39abf4fd53a78e54ddb9d4189c1ee8bb8ed4601074d515728ac9601716d97cdc 50e24c8bdf0fe740e9777369a9a3afe796a8f701b0a4e5fe95cdbfc8006221f3 8276bf1f278d3920f30b8beab684d2ff373b2c78c62dd65fea955257f7ab85e6 90d8e2b0f3416570023f2e4a5726736ef9c182681ddaa4d3bb675217fab5f0a6 262031340f0e6046d4c8141453e50bb1df6f2a55d62524a274fc592238f61ef7 a8de96c2cf0407a487eccd8a1e3c41817d654adc8ebfc2657014945e8719dcaf b76dc196d06c63025bb7a62553461adcbf7489911ff7e8b7b118688432d88f76 51b15d37a8fd3d580364cc72b47af24689eeb66b5ccad9e2f78d9532efcf3aa5 e87cfe13ec1f5ddd212196208b2da1903b8f307ba02ed25cf8d2cf7011c32f73 7f379b5aca97bc852df7f1270e33c2908e6fb62923fdb8331d9cc34129019178 db6b482e81b4fda9cc2163f83c65fe4357d9ec0a55b0e40ac3c1df26f88e6110 4b5ca53449501554f8943846d40e62f5f0e69bd9ea61141687feb2442642d848 401b1e601a1d2b4b6b8307cee13488bdb04d66a4723942ce570edc18a56b335e 792bf73f436b0fe3ef2f4c9de1c490b2f10320cabe429abe461a1fcd3b8af31d 2e2a6eaa8e61933b11e5900742cc83ab4facd0d07639175eaecd8031b9576fa3 d44613d4d9ee1ee7783a88c80b8900671350706e44c0570bd67ff9879ca73bf5 b4f753fd89ae7edd045e2278631be1b547204fe917aec384b2776e62968dd4e3 dc7c0c99345e330411fb04ede29a046cc2d70fd656f1c66247bbba38676d9bae 60afa451c18f0c8a499eae86d49677a8123b27524bb2665aa8121f2d8ffd653d 8e8f2066c8d1e585fe687ee88833fb6cc2e1a06e0e7ce4046379ddf7ecd16318 cbb9138475ad86fcb77716576755b67951f55c946edce8d850705cb8a511904d 6b52d294ff4a7ae42fe24db9d76cfc7db9b84f9afd9be83c80ae879548ea0c11 72a44066944d34d591ac542ed77ba0499e488b368c970be0df3bf15e78f01792 d9f2599a33914eb4323984370f6ea9bd0f97721a98316b877f78aa3bb7fddab1 31e9f96d894f5704926e2d054688e8ac31c37d33c68919ead2c7e0e1747b8bb6 1bd9688078f1e7f207c5ba60905cc832d350072d2b0916a05364a1420dbaddbd 90d13a6c64f40ff32f7fbebc81251b0270eee1a6780b245fbe6dc12e816355c4 d6cce2214008abd3abc328da099e5362ecfca66b68e9d3dc0af9a10dd7605155 ef0739ea99770d41ee2c5ede82c47ec0f0b0f8ada5f901b218051440b020a04a 14b70df40f55adc8037fc1d6a866b775fe2afcd47f455ce4b087af70b6e30fe7 981850c5db1b8ae19f739882fb86949a521f83e38515c24fca6e867f3d72fd22 a7bb6b18d4ac42ab1c36ada7fe696deb27ca2c95b2cbec6b5ccafa9e69772973 b1a70513d898ab3ba48253bde7c2d3ae265bb0b628577bb9aef8465eae181867 37e95802466580130290a625b1d1cb8541fdb4c5a26e5ebbb092e2648dd81cad 997709c5ea062ceda4898bd5a047f22ee4c1648d6589527f2d800697a4f6e6ec 1eb32d167bb44c57e07292bb4ac08d1e3ff0056fa16a940c5f3aed0fc69e3a9e 4e1ae4f94369730e392b8cdbf5464e54cdac0fab322e5703a1438e2aeba0bf0f faaf4a9e57b00c8d3a66fd3d1d1d0256351631d7ccf1e7ae097dce8805d33381 da76238c06afa81d33177e4ed2b28a38d425b85d362d0e8081577f491bee43e2 943e4dbd71a0d87b5cd88ad0fcc0e36da272bd5951d445bf4f1ac14c661625ef cd4cf9a54368f91432c302ba7b0e29bc0809387748bf33bcd46705e8f48a24a3 a16e49adce5681d3bd57aa2ae27c8142a7a68941412ca7af5577a97451343c84 53451d1ee9111d96cdbb9fdc267704fae3955960b11a81ba6f4fa992dc2a3473 9b9173a7b948ffff9a688575f7a5c89c732001028fe794a0dae2358ab31af4c8 f41045045489cb4528530858670684fcadc9aa061c5bcf9ad5cb010daea602a6 70545ec3e6d406bd31cc154039d35e65e426996a351f3ea7a20cc6724aea5c83 5cf26963df5bba161d95ae638d487617decb4787e343ef48b46e157c8bcd686a 164573059080affb2580b58d10d287d75248442eb692d9b7d0affc94c1c93505 2c46a1028297b2be75e39e2036b598309e84ef1f2a315896513dbdd2816c646f cd1430ddb55dabada8fe348f4f304de96dcbf7099b87e869898fbd1155ceebdc de8e6254703702fd4a68fe0a3999d8f5d9777f56c8c1dbae7d2a7868d4edb236 2b750a74137d26589a3630ee80d19ef87b3b1d6f2fbd9846cbeffaf70a6348a4 bf5b7a6a29799e0906ec33e47e393e48392d7b2bb2362b28174f69b978e5dd56 37076d4224bb39d4a509e0fbbe5c3c5573a760eecdeac06e2a8bb4fcc8aa2f7d 10109739ae2284cd0b0b96f1b886d5214e9aa93fdfa6d782a1bdba83d962efdf 9c92d63535026ec26d88fec67db4c97561f7be9de419251a89773ca26e728c22 64d11fe408939f37d3a382b53bf08ac2f24417782cabf2faf16a515c7cd96e44 de838c41d8d109c8c576e277576b8d6ee2f18e2905b377e8d905633e1f1c5d86 f1786d6abb12e250fdc137a1ff7816d48fa35ff62cf0071933e4f9a58c0e0a86 04e6e8c12dcc9cebb938beff075720ef4c2c62e638aa78111f48fa57c54e0d9e 6a1121332e175376f26b72794414c2612d11ea37ed7dfb4fcec0d9d03c553fb5 1756252e45c5216b9ae2f2cdeb6f3562f6dd200be8b06ea633ca95c0ec667247 47443276aab5280f66052940bdaf94f05e256b7a62dcf04fe39bcb4b8ca7c168 1424630d4b1b630230d9a4c61707265b82a12d113b82d9f5cb9321ceed1af750 db057603fbd6c4714c5359e156297ce48c912897751d1d11a1d6282205fa369b e63501b3dfdbcba6d43ffc6b69dfb7f9373c6f3102ee3e2d455c532629e76e23 f4e014723345883641e7c8e8977fa167d1ac35372241c9bcb5c8423e14140a2f fac59bd750d9fd809f2d6c57ce72a7b7ad98a80e78118a7c1c8320cb744828ab d640371071505a4f96da86461ef371746976c51ba8858ec603971d959f8611d4 e2dc4645ba8f2c7600a714dc0e28143d426bfc596973ce1c7c1aea3e1faf5bfa 6b3eb312a56d84442663a27ea64bfe70824bfc8aea4d35dbd58b54e86174de42 fab293f10d2c05fa1aacc40e957a436d388984e90c9631964736b3784180e347 2322c1a7b5d409bebfcdfb469669c9121ba4aa7d17d989f8538733e3bc7c32e9 5805f1004ac55347ec8d150510d2b924fbabcff2758c281d7cf981af72b6149a bdf181b24e8aa91130d8f052bc1b38aee3d9b6d871f876d64d9b17d48ae4d48d bed5291acc93cdff3f716426f2c3c4f55a61ceaf030728d68891eae1781b34aa ce5bd13b9e422ba62a785a8645f691d4465e053807c562d5717d8142219d23fc 1a0c9f1c16c8a3e3d563f470434126cdf62d6f77d5b260fef8057cef28ce9d39 653e4e125186c846e59242b0da8d138c34d11312bc164afabe11109e1c145d43 0320fec4c28ce0ce68b92c79fb41bfa1ecdc48d089d436e5cebf9004820e5c86 c7ccf855d71b91f913e709a32b5c4180b13df62a8966f160526736d35e6448c4 45cc01837c19f3efe17d76b248fcbc116e3a755e428b7c1068f6b92eafdae070 61f219f240448c3fdf3d23a93b55564ff728059eaf1ba827d4b3d941e1fff4ab 2cfcd160d3c53c4269a0ea153209ade7d5bc1dfa3f3df8cae7110c16cd22dac4 a633258106303a1764200995b95bcb7de2f3a7cec824160d9888fdcd8b55747e 946a6bba867939e21549e3af71cb6a2ab9172898a071b8ca210d3c2dbb23895f ff830cd84fef90bebfeb223d458f5d0b0bbcba58d923b238266ba8747a35e117 e8560e6101ffe925ffb3898193f6620b50941fbebcd6831b53f9a7344991ee45 11e9354bfda42a414f7a52f9ab21d9c44176b09ee1678d60a0ab117caac23c86 16582957ea2c3fc7d6eb43d23004f409bc84cedecb3ed23709c10a6af7c08074 37e93a86f6bf0223b4e6aae1f34a2cbecf0332e780aefc5b92ad0686ffa3deda 1cd216abd26168fe266c2ea5dae40fe70c4e72f436f3ca14cbf1b88b3d3a996f 41f747689e01170c3dff2048a449bd05447761db7eb7dce737e6c945cd981302 960d16b890830597484005755b389d00a5535d089eb313e7b4961e5eefa6d75c df2e05d00712f1da847da9f745eba712e20b6bb0303b0c0d3dcff5fd45bb66b3 09124ba1ad9996d6be97b07811fe2a183d17112b5a1a7f8a0d69eb3e4b282610 1074219b5343c1aad94677101b3b75965220c1fc8b9d797f6f6e7b3810ed59b4 e72f37230ca0c2d5378063fdeea19acd0fa26ed1344544a6187ffc1dc0639128 3d2ffa3a24f2598edfe75df5652f2e3cc92961040e6224473a87bfed70c94101 3375f34bf2f3d5da80c1362c68dc2b8e5514187ba501262c5da33765807ddef5 83b40c00ce8ec9e6b1876c01ec4a1b1b347d61414af7e173e9dd7795589872cf 4fe2055ae7048454a78ed409631d3d931ca274e2719043c21ab51ccc983a4051 2d6aeaaf9407449635ebfc45bf44abaf6a0d808e695905e6053b6252db99740d 6c22e0c30077b1055eebf7c24007ad1cee96a7b29508c022024f6820c3bcaf57 7c7286d0db4fa0957bd24d21c2a1fd98a839e349858b77979e7fdef2472e4959 50f40597ee46a77cbca922cebe7d1cd47df04fc88c87e01137811cf172c5215e 5adde3584bb65ed17aa143cb15d22125cbde85290bf3caa837a261ed0e1692e1 11ee262c28266367cd081a554169158b7ea87a81153ccc759fbfaf476ea782be 197b2091e37a701b6665442620a1d6641fc0d24ddfc67f47469c5c1c527f9c36 2721fbd8bf0a723eef2c0f73399429c401268482cbf7b3d615186090b16ef492 cd34213dd42933c75c5519069e17ecdf2f584590ecc45b16682200b3c7143943 3f0049d81c2b30c78020dd01f09d70ced0c146739832d2ee55ad2baec0e53782 4a43e8843f843fdc6a5cda583a552b35913b0791966760b2aa79616e777c084e 56fa59139321ff3ab48fbde7088c8d898507a6a0d233bca7c0c1a14703585959 8ddc33228dca10fc571632f964bb916d197397e31cf6b4abbabf254f37881289 b631633ef7c95a328b4b15bfebb04ba304670c1e5a7088af42bb79d9471db0e3 3ca379212c46d1905f5be62ce7c98016fe25c0fce7c133fd727d9d27731efd91 b97302e5fe5b50c05734563dd408481699fde2b5b0773f86cf177a6505a2b144 f5d5ea846c0bf0ebc9c2acd0cc814d047f694f2f35c5d167fbab595dc99649ef cc71d9f5034750bfac19f158f06e54280fec84cddd5904ed56052dc6eaa59002 9c929e1ce7e29f6c880aa1c8b3004c78ce80cee6e7599700932727f48f9bbf78 a700c9fb38cd12d98a9aba03ed207873bcddbfe12de014cdacac23f3b872a7cf 1e9e6d5fb6399e27265f24ded54c059b5343237ac0f0b1ca83d0f556dfdc9ee9 2e414401cb313e343835ce34fba818991ce680a700e391bd9a3672ca9cf7d6bf af0fc23d87f24761193a8de7dc049ea30fb7fc3a7b13c25f3468d28287f0ab3e 3d2d01fc8d2f4ba534dad294d4bd73b2f2e2a08f025990333c104d3a8cd2c4ab 1b3f6caa55220e137b81d6907593eb1fef51f23e3453b338c4938664d08026b3 9ce455a9061fc49f51a6ca91c7495a1e0474fb8bea8e8176fce0f1c11b2b5bad 1da8b477825e45f75075d4a1f09ef906eab0e02a2413780d08983f667f90995c 68bf942de9ccb1b5989545e8e706790f61bc51b621325268f15608568f50c453 80cb50c969308132760f7842a42ec612f935a432a8191be239f478ea4ea8dc1c 36328710f8b49f50b82bc30d0c5ed1c4eafcfcdd434f7a9eadcdf276353a45da d2997bb6df6aa50f34378419aba8fae48edb710fa4f606aa60c36b06af3c529d 0726eb0f087d3039790ad26672af4eae4a3c945c5ed64eb5c8f21ac505934d8a 66b9c60303c26b91129b139f5f83752da78ba82a8b5a783132b0431118b9389f 3eaea3b1979e4d526901b81cfa0c3aec73cc70b7257248beab156b66c9a0bb7f 8c932098d7601c874878a4dcab1adddb2a3b7e1c093e9b58be976a9ad105a334 475e074f5423a2ee7c664823961f57ab000b3e6fa1cc46f26d1f7d53ab10ed62 4f38c0ceae45f9ffd7b6e83091fbe793c0bbe011fe1c3b3cf572ca426058d696 ede5a90ad387259ef9352b66757fc02a6635ed36dc53a784e085feddb26bb338 292f492915d311d797a0dd6b64d8e0fa58592f17ec52abdffc637cd9b1e1c9e2 546197f26558ed5f52d8833fe7a541b8a432a568da99685b7a952bfcd3dc07c3 afff68aadbda0263663576b36122da494a0cbd041e67d5aac7ae7202785e3e99 74302e391e57e35558e9768e2e7c62926fb452e9eb5c5af31ef02bc8557679c2 f5469f9bf156f6a65eba3b38323d2ce9c5809476aeb7de9946bc523a5842e3c2 b596112a82dd999546a8bc1a4ff2ce55a27f354c73521d377c19154fa579a3bb d966cc43d469261c1e3326c89687191ec41ea6f564a14fd224f0ee956440016a 0ea0c7f59e2da4d199cb6f4a6df9e73aeb9f6f8bd31dda7f01e1932e2dc1a271 5e1906c55af04d1bee5dc76258b8a6eb183195d496ed9f2ac18ee9cad011700a119de4150a2327f0f58423989ba050bebe8506da57fe685f75a5788fbb21ba0025fb91bd619e69e722c59148d7e182e1ee68d293502d6f9c23a8cae60fdc800a640d2f2acf8b91bd3537abce585bae7b29fdbbdaea801c759b2df643214d3b0bad974b4e3247bf5396ea0a66f7b3c15fd5e2de3a998e78ac3ed69d846235af0b6eaaf6d23523837e3522a4ec75708475aee4259ecc8f8e4aae0a49b9fa2106097a7fa2929bf7de37998dd11c6fb3be47226a5ad68ed5a792e888df217f736600ca6102d95363b0431dbd696382a11d6ea85424bd7784e766ddb0c5a65c14280b3a61d82d2836fde81fd2ccf5daeb1d30333a0febed824788efa4554ed905d10ae054d4db37728f380713dab0523b11a10448e3065da2ad421fa87efcd05a0106a9e6b15c930fefbdb5c42ca5b2b9c25820b8b9e2f0d91c4e5ff97fd9882d7c06320d3886c7083516d84109efc42fa8721adca82430a65b06cf4cc4221e2e280a3705be7128ce5401666494f418333995bdf4335aa2f34d55d4055aac6fe6a8a24acf4d2e579e44531423601b234e078b229fd99683b2f594d719dfa81b69990e92183fcd73ec36e24c48a5a6a90911bc849277b2a14d142b3414c6ef4329d301c57e98f3b6ff240f2816686b7c171241ea5fda83430e4b61a79f57b7f8cd030a3fba5bdd3c63f54b1f206014728e9af60e1ddef03e1d0553a2b73aa4dd2f5b0b932538495087f3234607e14b7811548dd0dff4fed135c5a00cd4a0d3233b930cd2ebaf9dfb54afaa3f2d6de9ac9431307fc7e7b2455a55a85bcfed7e6e11c6056ef144d66e69adc444ee9ac109a378f8809b42818a51cbfc66390d230df4b80b2f742fb83136ebdc416c8a72fe6cf61afb2addae14a3e6b17498b96389f5f40b7c09b09cf4032ee199bc187dfc67065ba315dbe04595b6b32e33098c55bef60bbfaeb5bd233c7a558eecc57ae3beee366744cf4938cace81a9ddae2eb1712402b17c555e84d3657c8e1990bc32a88f748405f8caca56938a02f02dc572d9c70516572e3e67e15fbb48fb21a4f85fd53a9152ae639a09ebe05a635a2198391908a14289498829062fb46c1ddf8bd075f77051945741e8e5d8b5eccb9ccb58ce0452c9acc77e53412cca013964cb027313819e0bdd390a1cdbf7f90e1df4b77500c80356de9a9ec7141f12f0efdd5e32a11e6b742ad7f4ebb71ac9aebb6c5cc004a1e83e9ee2c0f2e50a7a55c9ddf9d31a2a0995fd33f25635575bb4fc4d4a72016539510a49faa44e3e5ce9360785260ff34eac9a996ad216d5bd4f553c9f0a0e2ccaabdb21c2cf7dd06eaa92c03e8aa6b85e811bf9f1644a97bca488c09ae80abf6b6428a98d52988e2a9d475b05d3a23912ac35e2a61ebcc3e55cef72158405e2a991ef60b7e41ec3e7f4abee101bbd52de1be5f50107d10e0cf5cc166fab03f619dcd870892d6a4ce226a083522f9060b32545e1bc1df8fe92549210d53b03652c1d7cca828080619fa009202b311e1de02a06fe9d89222d908e6b97ec1904e7bdd37f80e83166f916f078713ab7b5a7cad13025fb4354d22da34bb07ecd094f7775be0352013ba12e7874ead4a2cacc7abd2d9bfc58a8eb70f3b81ef49d05ac1d25011b8c978110355ea98e53594732d94334fb981b6d5f51f2f096adca0dd3e5d181668961e8f7c692017b4cf8fb3fddf67561b4b70a40616096bde59a0ce0086f84c552f0979a8cfaae3a5749cda7a30f228c24a46ea9502552e4c7f70306f13682bd17b9a104f658e38ce1c2ca0fc75552d5835204db8f8f74c7541609bec0a3f8a4e07352dfe2ef2de1ae8ccbb9fd31be054ac8b9aa32134ed0964b044364972c0e15ea8cff4500c3ff7fa5cc18aa95522d751c668d6e17ebc1ca3a2dac274165074017590493bd6ee2a7824d92b048aad63532175eb406ec5880490cbe0ccf91c272202a960b5e9f09ee78aaffbbdc396e1ddaeacfb873d29de5cb08c2c428ca50f85ea07533395bde0ebd30b51966fd444e6f25835e9409a16077051f19a6fadbccd2dea0baa9dcc47ee53b48822aa91f845419fc9abcaff1b08507b3a9306cc6c8183762d6f2f8b35d6cb57722793234f970df7c87cf4e1b6c3a0c9f7a68e0de6fd18510e65ca82539a9e90f8580c73c4a394b1c0a21fed646d7010cc956f27fb0e731bb64c64d7b86e186d3fe2a00215c46b44a823613d424c60b3bbf4bd0e82e6ffe73261e49d83aa6a598f588fcec62d555b855ecd0fbd52101d217b452cc5e349be85d2c8d9701f26f001422bd97a2eaca75959e8ff675bc06af46ab89e00195439cd06ebe2b29bd8d1fc68b172c8918ce26c70fbdbc81ff02b0f2e93054223a28c7cfb798d68f223df0b49e9b16997e6ebbb9f58f4b2c3002b70d69b900497de7718d9ef306d98a050a78d81769ad619ee2dfc42bb0e17b0d4e7e38c7d6fababcd7bcdc9ce39403cb6a11ac524665fecf5c2d1c0a3c17e60e5b7922f9c83664cf4d78504df1292141925d267137dc7a738de0ce99bf701d0de0762c6c1a69d09a0e86d37773cb1daecf37ab1805561c9e4754c65084aae30fe27ff26fe87e10174e3aefafd14281e947cbea6379f24b6b1644110adb34040bbecea951d9ee8fdac39479845f5a19952be171dee4b3a1fd5c8f4f7117670504dbfc7e87678386d8648926f3662b77d00f953deb9c7cb3eba2ab6cffb95ac1097f7a37fd1baff33898c4c86cbb93cfd38240da8fbd9b398f9978cee2e2df640ad476deac22241e8eb733b3693a19ca08ec2744b39bb0cda9e1448adf2670c80e6fd37bcacacf67426eb921d45094b1e7537f8382d3f8a4e6de9dc08f2dcfc8072ca90cdae9815808eecce0331b82abd8e06a816d2e536548e7145f0f428d780f8e55f9a28bbc3c73b6b93195da78ae62e8720edfb52e1f8c94acbad926eb1304609dacb79ce2cedbc4a48fe7cdf3aab4e33e373e110c43130441c7fa12756c0300b1c78af6f846796fc0fd28ac6780a3a71fdd5080734dd99eaf85d31455bf0869d146c9c4b7c64dcb548bf1cf5d01de78ce381f242784bb52e0440c4b2bbb0e3c76f496994be2fda7c014274dc9523a53ab6129abc2c7c97d0b24d60f9439006b072b5a292f65e1defe78e7987ea44ac5462619d20b93dd1027f3a8a0c8520d9adb349f5fbff6a3045ed1838997c3d52a1c36ffccfae4661090e29ce7691f0de47fd08f22be3266225c851a9f10a1ccb8de5aca8e98be89337a7069ba28ec0705c07150502fd7e53cdcdae6c958305421ca1b303fa91ffde90d8f36564eb206a52423d48d14cf20ac89be327992218fd6a809b443fc693b6650fe8aede56d015f1b619af3641f6cc6f4f7e2976d5122bfcb4df103d1295f01f6b5db77eab305f969063a18c8167ce2c988d1db5841b847ee2d6c95d7e61527e693c4708f7e0ed3d5f902a98917798ceb6e714e96df134407bc6b942115d37241978895329d089f1fec1b5293a040ef6f5e48d25cc6e67ac48261f8eabdce8a6bc7a0fc551a00952438034574c26196cc17511f156e6352b3431b8375a1e9b2e56bb8ec677f0db2ec4a9fb409896f5dba3b63bc2f5d4e4c6aa8757314757e95065eb06af6bf02e09e283ba13a70c8e084dee204ff723eb3cb5791276c7187bfa075d7c0c0a403243e9d95e7a23fc4516d17dbb96cb186f9a38c96450c17a87698b129797bbc066e4dcef2177732fdf2f4a1a6294ccbe7bd2cb573367573c5c4fd70ae11e6b606943a0b01c3772af4e443cbc52249822bd83ff286c59d66fd5d8f49c62e8a600f737a9342e0931435bec7587bbeaffd46f28194d36654b91933e3083e6fdd5406d7f2e790fb5f0e6832182d9fbdfbf35bf98df8cd6ebade4cf12b933efcba220880b679234332d0c40eb831343a67097eab996f099572f457021303df5066360e764bc4186fc76e17c3037d227524f8d7a433c8cd73c5eb48089b2c6357a84e0dea7abd885a5d0f6a0d393e6d4a0aa0dbf2a2cdb2f79a9172cd815ea6bd9300024fb583c1e017d88a236cf6a2c02a9b181fa339b2f515f6e663e78bebffd2b80c7eb22c45c7e3aee6557147579ec654d7510db056ea72994e6aaf67e58ffb9a0446e7f1f703c790c0d39fea46a80eb8aae1478b3c68c2bebfd1e732596035f3019d356f2c349d64fe96ace84328c6a42cee3276b17854bc18301ea25256b4a9035f722781b8f94d1c80e146852df3806b5a8293b97b12569eeeb43d45ef60c00a261fcf31f635c7af63377f767211e0160024f6702e5f985c6e58e7daaf1b7609b209586d46f996a63a8b1227e928d8293e98940f30155bebc2591eb2be9b3904a1713dec7c738a286524842a3381595952fa4ecc09d0b001ab27867e8675060dbb93451a63b5cdc906f464d401a527fc7d2b7cc4812bff28439aaffbf606b104f77905e9c0e897413f6575dd5d62b861830914483f010004009e24ca8bfae8063323a4e0354ee160d590ba469599ccf59671b3277da029a2e1b4ab582162740020f7a376b4d4b4b4d2fec0eb33e84039d11f7ddeb49c0b53fc2751d08d458a06a209d9e2694c9657b5f491de3911095aba79821ab070452423cd56ea4315650d3df2332b096557dd33ed37a7cbdcbcf134eee25ef1ad34a0cedbbe39ba176e005050b257b28f07a6a1af4874b563674c47233ada9dbaf5d7c64db8b08e6e27096ebc5bf6cac8d1de108957fcd830bec78a9fcb570c03885230203e1b542e65066b330221aa9978ea7cfcf63884e8dcd807322bde8b43e850d0fead2478538d0b66795f535de14869be570717cdb78cff05551ca03d64d3c7bdf4049a94e1db07efc46da5221769608e4a0b1985971604eb25485c75e5ed67131ffab3c21d240b20cac2714391bb85fad78f060aa523efe720f4437fb13eb6f0df132542610902b45f30d5baa8a46470294e77ccc0e8bb96c9cd68b19cd88e031c9063efeb58068def51535488379ff2a65e6a0f3dae0c691d91bba7a6dc3e2e47903d9a9fa60b6578579eab91855a32936d414ce4d2c802f4d188487faeac8111c0cb8348fe026c94be66817f5f77eea3935629938455b8d83613a24f8e75e93e6e98f1604c0eb6eaa6587fdb51e59254f8a6fff777fbbcf4a41090065988f79d7c0c7c004a05d1026e2f0baecc66dc66c43c9fb37367ba2f5d82fe7c3671918ac951234ee00b5744f14aede5dea91cff8814672d35e3d105fe58639536a2c8623fa179fb8f0aa1742a46a4cd6b6c67ffe4396c936628ba324565932106fe8e158c1d67e3b006477e90a07cdc990906e03c2f28b2062790b4c4337795c177c3e5081e23587f0bfdbf89efd72b1619e1f0098f089841a185a4bf58402b67ac225b2df149a03104d9cad01b7972ffcef0139fd9701e9f5a4ec6acfe2a92e04cb46877173b6d48045a5fa25adad7756ee3922002b1403831f921115b33e2f80677a8b270c39892085018cab40ea746d878ef2d6693f251998e0fa73afec95ffd3cc71120531414053e2cadf7dccfa8cd1596df8cd51382e7444c7aeb873b532336f5dc92400b2803273d2c1a059a092ecfa3b154086b8db20781053a7fbd05aca8feb27da0f1ff0264f8dcc9022bb3f987f2e71f61f095ed2475521c03fc1a4fd314b8a068921f0a9ee48f35a3589a4affa533c2e809d4ac80839e34bd98c218a75487457722a50070eb6803d77552f96d13707ae0b3f7338844a160a33611f0e156386495d62f0a92a5967e6f887ca0cee1b0da4ff6de4b1f0c01434da9ba502927498bc327c20d1155fe20e5d447ac0851d08cb4743b2674b17dd8c62b580ef86825701db9c205870e75db7d08fe592e0a653342d579eb40b4067384554dffed1a3671305ef40fe1ed6e44ffb9be531eb555527f96c05b307b720877611500eca0c86961aaa608200b095d6072a456b66326b97996f86f2bf067df6b61c3fae39f84e6ef136d07a9897ca6040ebbcf557b8535feabde9d86818040951fe1728941ede9fd22230a778c37746e6f7d0ada96e9d6ac0f38efcc7715697eecc8829768cdd33253ea0664a2bad0787eff8d85f2cd901bea9cfba090bf00923fee3742696bc067321b04448668181e4b4fed82acea845d3597e1ff5a0a4806c13d57228e6896ff942e078baf5a68b9a3b03fd871acfef0ceb9d8b0ceb6bfb3d9341d82ee814577c6370c8af0a43cda011a794d73235eed77f9e67503a4dad17abd5e65c27783fad2910cb09f269586f37921137697ff5519559908533f4fabaae818563d9b82eb81ee006c58b526ed00b4593456159171eae0de0264212a03127a0bcc2f2162ccd30601ad72b4dd90187d2388d93396a49a0c1fb7f89fdf4ca012e1ff34713fda8663014c55533bf9f2c9e2c2a7966eddd2d1e2682abef1f39a48474fd57cb3a87db209765ba701c4f9cee7cc46853179b7bae52b0edc79e3b8b831da7a1d157102c6004dcdff82da322480cd1350a482e9c8633da537f1422b95fb61af88bd3f32680fbfea8e80311b98b14a7f153637470fb475d9070d5812f6b30a9ee3579f8110072c83b4c85f853a09b2ce75957003f8e700a8509026a70761649d1d2e4432ea0652db8cf376cb9113d522e28e844c00abef918d057df87a88e10dc08ab4d2ef03e89854b624e62884cb2c3ab661fb8ae64dc4f8317fdba97d71996c537c42850b0d2cf432fa2fad1eb8dd74ba568823bd6ad0cef960f3e61082e79d08c0a05d07f8087058fe3c0b30174d3a1c4c76fbeb79638856f8bd68ebc2fa29781030160ccf1206832d5eb6a1b74d4a8791f87927a26a42735aaa86125e64bf1920debd0eb39b11dc6afb44badb9e68d8ea7d27b6242d6132de3162a6bb7c8087b1f5c805512d9f34420ee301fa73fe772f485c23557767a2e8be34d439fe154489acb90813f9c0f2f653a11be191be4c4ca64b695e22a5187837761cabad901385b411038d2d3aea323c8792de81a359403096daddc9fa9f8e29b6095841072e06a46a0167345ee69390a75f765563e502e465606cccb32ca39c8f0130b7dbff4997790ebf2393822cde4233db817c88dabfed25f14e36cc445cc7fe9fbae2cd0547eb09e80307628341ff377ab9c23587281ef0ca33e878dac1fd4dceb5d4d9da3e6102ef61664f9e9993743ba962bc0f1a62c0d3829185bdec12581e6a8694c0307a0d05f77ab50857cb5670637274714164cdf10d1e9c6f40e40a4af5e8710f61c9072f482185aac01cbd3d30bf85cc71b54b3ac93fe0cc8a4a8ec22bc43b0b67920519ca792abdf381598973dd747fa808377f405105671bf57e5fbf193b944d570151df92ab7fd67daa079d73fb2013264d2cc32cf43c0dbbcfb764f8dad97dde030d6b1a1b500785a31bd55d531a0f2be4e408204b9c5a0270e0dd7b53b0050009edef9101e809657ad2aacacaa95327f16d2032ae10d51f7acec230769978ad035aa836fa78258a7accff01342591d361520099b4d4a1c7d2ec8f045bbb33ce011276442aeee4f29d7e25b8d1cd9eb6784c0ba81c1a1ece93369b2b7a78f5a106896561133ec3ccf76b3423c5f217554e80e9cf2db9ef94932853ebb3d6a1a00951cf89783b6114aa4ecdb1ea83079c99cd57f9386754b9e67f21bcb09d539a09849e72e811a2ad48fad40b98527db6b98561da847a5851ef99450ef267b6fe003246864b877a7a8c40ba42ab7c4c26347136c6b45a49ebcea43a9828c14f600789a2f6bb4e59cd3067f9c3e25b832290caefdab50b40b981a40c17cfbdfb9905243335dc0ef9c3efbc09afd58534da82993860d14057fd2c0e5a3f1bc3c2b5068bbed443c70426799abdadc03b7c7191f14688c4bd9b553ddd1a17ff0b247b0d8441f58bda8638ad2ec53d4a59a48ed58ef0fb73efda8659c0f5f48bd23a420b27e1acd14322b3d257beced60fab830c46eaff843de02117404755bd8143a20095df49c3effd56e0dc12e9bc2e941900cc49c5df72d8d663ce766ae2c542370d6fc5fbd562c6d1e69df60d349937dc4d53398216a29b396b63c316cb5af99f0ebb9fac73eb4f84ae7a1782d7cda95e03e4e10feebc256a3a02b5819dbb1ab807eff9a4acc96767a1a65457cc4aa071aa3050ee500aff4a61bf3d5d98ee21d408eef6c31023f399af70b2dd88c38ed6985046c83ce4026416009fe8c4cca82d0b0f799b77944ad7a38c74fb7683b511ee3ba32835404d9208b2b3db0311d01b016f0dadae011b1ce84aaa989d09487362e179f42044c666c995b55ff3292c9f0b1cfc63b82ebd4dc57cbf666a387e4a130268c4533d0a28e6e71da47f7843dc02036ec38bb51555efd625375f7b51054a198f1be2738a5c74e2dfb8d794c0de01a0738f976b8e016b7e25b6f1c3c3a4bdee534b079aff953e51e19f16ca274b091bd082c3a6430bb7f26f739b068d756b21324054482e102c8e6df0d8ee1d0c0f7995e897e5c12597ad71e034f63a1db4e15c4cf389c89bcf9647840ab7ec3b004e192c5a7d239be5761e3fc10f3a10228fbade7c36464f7b1e6b8dc1ee190e03c2b7c8a06e4b0d2f473c730f948ea4b2b6958d88ba58b256c6137eea85bcef033c95ea45c1180f1e384825ad4e4fa81e1f444690a931ba4adcd13f75a0491e0c236583edf36aa41e6530cdbdb12ee2410c3427cb40c206e761aa338eb81fd807c28034ab405c4d30d42a165b2137e90d9c5e5943f6e6139225faec2a8641a804dade6691dbf94085fb2cb53259780e22d70f94bd363bcf4f2425bff18b99d501d6cfe3570e0d4b1bd2d802284557badde674ff72735a7f2760de728ff800900851ab74c365e6d5ccfcc98d5868d3150755cab61852150dd72fe0eab96e732b08ddd00fe969a220c46b96f10aeb1c99609cb8eee1d822e895c701dea8dfefbc0405d0beb303ee88d7ca4bceb1837fd55dbd561ad9252d36ee188c766b7c50ba03dc61420487f59641bd629e704205f32eb25232baac7074b92400355babad3906ce7b8765db9b062b3776c95d1368c63a67b5dfc44a21f8b1b93150e12c63290499bf252a6ceabe097d054c510f5c39e5d90e07de672d99bab42da2dab218310a2add1db7e03de16a2c7a9f280f916fe17131cd85c1b51550b22f71243cb6e90c34d03818f20e18eccbfd02775dc7c7396546efc465d5569c60b8d029becadb0f1e8da3bf8c28c3edaaf0384f95207d4ad23a17745c014768405b603738b20f08c253fb1aa669c95a9c9355692d6ef07bf03ff4aebff4e7627f83e2678b01b00d5e5fad03dc846c6ded7e9a15bbe749db901833f5053f5abed0f1c05ba63ba20e2b131a626266f7cd24b7f1a465c3b78f5f9eb8983bef31a07f55258ea6901d034a5bdcfbce93b1c2d21caad6c4c0e913205fa8ab54f75424e322d5802e60200b5776b9997edfaeabc1bd889a77251995ce9a2bd9988343eb5b4de5bb8736bc00e90efaccbcc44f38d99ff365d95915582122ff5a4f1dc976bca68353d29d010122120552a8cd61c76819f5b813e5e37a2af0df8e1348537dc5870372fc44b50367920b32af372bd63e34ddc0270ea5e8016b22907772615490484203427ea4005b0f745cd9be8b82643e763103e3ccdd801c02461b3462c3619b8d7452734a0c63e6b1f1974bdd03848b90c8d033a1642d58ad48a9b0480dde1f2ec02afb6f0ef5235a0f7bc6fd359848658ee0ae03964bf7633a16368ccc654bad777799e10a5c45269733888a0617cfb3a446d3c53c54d131ba083f6da1d6ef84e1d81861037abc41d6c05fdbbeb93594cc2a8b302a22017f85305ddf2eef63482bf17f8d06ad0cc1977640e9b9c86a36faf0e647e1d3e5d6fb63428a09f4bf288e6413cf0d97389dabf6571abb4c8e300e1bfe10a9bf7db8b7496349b39d8fe87149a0f908414bf95bc55d5018ca8bef92ceff8b330cda6f20438dd165d137224b01160104909ea4a7ed95ba6a6e113f37833131eeed131aa576193e9c54052116f1d78ba3a03d39363c0bcd2aaff09f08faa1763885901eac40beb8dc6d2266725a2a6105ebb2e18f528a69041cf10f352d880b29583f7daaa3ea3f4d59d32340d8104203ca8b93867d6e776e31e6eedce43d2069a45407143b82ef889fa1a599ddca230ca487312c33fe1a9407c40e2d3b7a67f491583a5fc5bce2094b4cff1e0b3f9504ee7a29f7560cfe6265f3f3500a036fc0f5df953e9c57104b2206c695b3be1c09bc6fdb0f8ba88215bfc0024a557bd1e4c3e6af8b4e13a7179621ad26bc1a2807797279e37b5826fcb679b5c930170f2effef097bcc53a41c92f28112980de00b2869b50db04ca82d2dc88203d226daa0cfc39a3bd1fa11b36f932636ebd63f0843b6830207acd83493eb144d12af2b42a178cad0114fa929be8e0043103f9b033f08b1687a319bd6b3417405c1516120f7fc5667e16867952f9b1367bc9efb09a90f0570c6e6f67b54cf0fbfd3f12ef26edc0ef14d08582ecf015124fcb38505fd4b7649f1e477ab51087d1b77c465d62cedf7e6fd8b42f85a9b039b041e8d0bedc281694d2ef63f945cfb0d01e255d23c0808674c958d53051c4e52fb916f0e2a0261b44a5674099338c56434004e5c430fb92ebc61c2619a105f0398c81b024da5de0883d17151d0ad175dc128edff8d5947d3c60951d7c83c3958710624080ffe704848a37c3a431b1962cb939b9a703af84ade22de2894cb88bb2b3a6e0b783f3b97ec137e79d1a9dec08437b14be6cf58e3aa38354f7fd59f6f0a411d0dc44d6a7ce2875b2a83115797b4f355119db0524fca8da21e425fdccccd88040971bfa5042cde876f59a98b6fdf5fe7c32d84b237ed28f0a1e5198582a2b2600546469adf5b0fea76eee500d2722aaa357f2565455e140196f85261628bc02a0b2f27ffa6573a87783fa86add7522c198377a4d5c48f611de12d67936ac055b074c87ed93e96b7daa329ab135a219d3774c126bce535f0f06ec0b11a6e2af7e03cad80dc57ffe1f3698248b1fb067da85ba4fcd61efe96d35b01ba85c0d62f10f7d13be712cb6ebf074402c8fade70700d5ae02f2552ebdaa41d3be6350420e0bf4ce3050fe86b7d90819f0cba082de0ddf8a32b3fa8e33a2d437565436fce40c56a170dea1c1b4ec6d6637995ab1b778d480ca6f92211f36278e6067040b2e0d89bb7f0a748b2e0b08642a010fd07825bf6b0944d4aef22ac2dca9eb0fbd890f43fcc0088f335bfaefba858981888d0567de4e878805991cfc5336384481630ef3505871c03f6194279159a776bbe3bf94e51202b75ae18eda4693fb4ce7640861b8409abd7cf73770fd6a3c48e85e696cf3204df194b9db2d64d79580297e01f93a7a06a800469bf81cba3285cd580b2b8865af43fe8f651f40342342b7e30249d4dd3ea0e0f6f5f1d53f36635fb7f8d5d2cdaff68eeaa433db64d25854e60f0571a97630b9b43463a99cd7ff4fe72a4defa90f1d508b653fc25a9ae4e16f093695b52894d5d054663bbf51f49134667040b6362b540c7802bdc73c60ef8e0258b379fe3c703c812a9002a803f95248b5932c1724f1bf90c6996e4ecda45405b6e34fb8c97ab8a027d27b8d4980e07ec020f69e72a5f7c2c7687b6914b955020cf1b0ebb6cd6aaf161426b95c7b1ad4ad39706900b39f01337a186ad06ee70d49ccf89a4b003077e8818365af3625953884ded64e81cfe32742c683b80e10021e14c7591a60ef05c2de879efa1ff7d71932ceaa5d18a0ae3702d682ec034202e211dd514957d92fe686ed327df3d747d08959ee5a2614ce8ce254361e9b29061e8c0ddccd186cb139e70e3ce832f87f05413a8b692dfa52c748a4bfe945cd07cee8458afe10566a422a6756b0f40d252d60f43cd61668def3a49ade0c98b9081e0069bf359dd854db74c4d19b3f8fbfc24c083849fe69b4d6b20e30737b740edb81a2d2caf8be0da61acc3dd27bf03f63e109d1ea8b423826a5f0505eba570dd2a34d4ee439b89dd10ab705aaa4f720d2bc1ffdf1c9cab36fe9c42d425e6c06319b4b41a8e66e329d38df8e64601f784d82e7c6fb920e257bb7271d52c6ca03ee26caa79f3651a9c3bd77cad268ca818497d0419c6f7beda289cedd8f2dd30aeb21b719ccff8a0cd6542a6942bce792bdf49c3ead6a00379e2bb4d188ae3606d970bb88982b73da92c9aaf2d3658657be79af721cf8db1450000ff1b53eec0e5e3ec0ed14ce04d8a4f46fa8f42fede0b386b187e8fb246a3e4f231d6b34be06a474d6f8478cc4899776f6ef0d80f690db80a1aa6d164a888a459b32c89bbb012e7217b3207a8e224cd4fbf43676efa14bfb3be6369a6a40f20763bb5bd33906b7680734cbcc034eda65429c3692e5071cf5d7f00660c73eff35ebbb88c4f5035bb0cb9a6c46a68cd8fb7a0e3a398b1b974a60eda91b0f1f3bbe08acb6aecd0d325a4c819c7d912cbea88bf2b9e67c5605bd7d5e3f8f762aa1a0b76981227d063f70b195fd8564cc868b40e3d4acd81d558fa5967186e081cf02f39069f12a0f74d653b9612bdd233644d807f12c18e34254a0c5d607e6e8c5d7e276823f6206f6138feda33027f00ffbde9467bff3a89c02b18177f66f00b86e5dded868fb0466a2434e19c0acdb39072b9493fa225046c3582336286962a02c99c289124d00469dc04ee39de73f73d838b1d1f93b81f6ac2b0b18200110fe2c7b91789b350283d224e8e49a6324cd1bc832926bafe37d3b44272f3d298006fa21eb2504fe0b5f0576e4b2065301d2eb8adde0e370fdedabaf90e4894d09c0ddcefda66f410a70a8854446fa279520417b3f8eb38189344a97d4232d390612a7a4275b46dd068368c527cc8ab51f1169ceb41497f79d154f77383f2f6a254cb6f4ddc749350f234147cbc2be1c9f9f9b6c453d219071ad1351c791c1ff2007ad8ab930371d09afdd72165a86cb73d9f02d11021a15353208fd666093da3a0d4c69eadee93b0814f8acd6bf137496e82be42d778bd3ce053d1eaed2c21ae12cc62a5eb76f320e5eef7bbb51a675a27239d9d8bb3fce21dde87476526da01dad88543c3259e203ca848d5597058d91968cfc1276a839a8b78122bf19b201b1f26649c45aa26404ddd493a9d33d9f982f435c50e29785545fa5d77696b6e9584aa6cb2688808d05362a263a6dded33e8d61979b6c70ec4fb0c45f668c24639a7b6514ddb189e90dc24aeb56726b7907331478d7addc874b6e855ebf8e04a6747385e494050d1d09b4bc5463b3b7f86f097e0ebc779fb0da88382b29e263eaa1b7879e3e64a41f01494ec63163e4e9eeef25cb98fefe4302ae96723da080e178460afad1a95b5a0e800a8d0f9c7ce12aec3b56ed362234105d2823a22cc393a6559ec0c70046b20d980e291687b5c02054710d8f0d8ae4f1eb995f57b704604f775ae88605c5e50cf4ce8c824d69c94c5fdb61a107b6521f4f5be025495a0aeb1f40dca8f5eaf6085555d28af65818db89f77aadaad19dc38ff90bee78389e14f0b241326e4b27060f897beaf8250749a0c8836cef27095d41ae2858cc0b35e17a6bdc7d18d6ed0aa4c34ff422194a23afa8ed87dc48a3c0269e0684af370d7669738c8d2371d50bce0959d5c37f1458dc54249d99c4b5e3e983d36836338c1a0bf977050958380ef2c382f5556b6710033eb721a4977820e238b264a2eeaeea1a4b67127fdc5c021034601ceab916497ebcac6a96c273334662ed394b7f2de04900bd71b70c2d096c4fdfe709fc3725e3227ad6a0e903aaa829e6a9d27306f475ec5caa55737f0511899f7d2b19c5cf62c49295f40ab42f0a38001e6c2f9885498573a7d879cf0a3710458013361d3ea1bb6df8d7cf9e2cd22e3568639d7672222a8223cc887905fcf4d0e1ab719491dfb375f06892e4751e3624e508b1dabc41f8ada3e5e6630d15ff5f994ead451749d4144c1ae5184d2ff86060524400ed22485f7d17cc3500e7a5062ee1c03f33c2cccd4f03d5aa97557223ab6e0aa5093a01cdc55e6da90a51868a59f2ce3b6fa992dce55c5aa1e028bfdcebfbbd85a01ec94becbf40b30fa511d0701e6b631c52c3ab68708f3019b475c4574afeea4e48d2815de46e390f32030408c695ab15991b2d0e60e9a61da9d70638029277afdc0ecb6eedd16c08832a5c76cb1f5f1c40f0fdb5189e6252de61c93711ec1e230bcf105428d99b0a61fefb8a10a191a4758ae9a90ad6d8a9fe086392baa69128c4799e20e624960dca95c44730695643722a91c9ba79c4935b775e668de4b2118851693dad480d03bc126f099cce14fd6df69b3119e3a82052abd50ba44216ad3338ba0e65b21d021e97ddfae2ac4bd92fb36af4fbf6a76816546a7657704bb1b8e373b296010507179bc1cf8313e10dacec4748680ec4da7038027be5108028a070756d1736b904f478d35f4d475ce227c29c2426ef5f7f2dc7d7fda578c66542e635670d42c901cac9f85cd4ff7ed433e19876b8b1866c932945afb8dca356610ca60c0f87f90841bc88453f263903c91d86b10ca00ea1877dac70e38d8450437e7b1f1314dd0d3f47efe66cb9b13e90100cf2b6d4a3ae4e98fea265e8e2a67989c931fb23d90f97a9be23973bc625e0e146b7e7200e81cf61121755c8dbdaa49e470c82d48d0a3b87de4992646f47aa10fc7c1c477cf6d7aa6b795e42104581cfec7f0fe7830e6f07673a0d080f1617365de39790e074ed4d79a68a7d46522aa02fb1580d9a04128bea7571d42073ccc1032273b8c5a1473c129b6941814aa61b5cfa9fc7690e false +check_ring_signature b7eef746f0034c88daca3fee546f4231330cd52b37756e42c88215d54918c7a2 53e9639c6734a9141b5d37740649da0c255f555521e700589431003e2148c827 47 8bc007967638f4f61e5185b6b32c5a94e7d0ceff14134f2860e32db4a049267c b3b3410e4fab35332359914d0ab0cf8bf7452930ddf607be67d38ae1b3f2e07f 63531b553f8f7b213c7f2ec2cb7f60ca4ae5666635010f82ba6b88737cc98ce8 55da6221669357b3b498b5dd114ca28ed4cf1b6c353530d1eb1e7926ec748a8d 15ab87218896060e305d94b089642caba852f1eadc2f0bd3cca86cba33939370 5c12aaeb179ee327c692c71d51c54fc36f49c32c7597051f241fc410e6508564 5873d459c2edc48a0e5058dfa231e4274442b455bf7c3ea3297b0156f5481620 1ae4ce315a3cb3db76f2066d5c3738395d9478c0734d7528d78bb983118e652e 7b635b98164f04cda617af9abe314d9f3daf552b397c30a0ee68901eca4606e9 449798d69f504bd37b3c633091fca32bb4986017337fa2e4cb0a32729cbb7cab 643aa631b20cd9003886bd32a66eeb72d3fdace3f253b046eb72121d2dfea93d d696c5c833b69b341a03b02393409395c970f3bdea8c2c537fb99c4bed753a1a 996737e99a2572e53cb958e69ed195e26d956657ecd171cb812b47f2b312680a d14f3e777cf3cff421bf67f253fe42b41bd77083042de903c8c28a8010fb9ac7 d573975546e2b3d30bb8cee618009fac6c5daf94546185730bcec33c433e945a 6d3d131c5855657f7f80085783bbe0483a8f4721f417639b3d6bce3a27e8dbfc 4183a1b5d85023466e2ec636e63f0d59a8b08a01215a28a654c54a391bf9cdd3 65cc3cbe5b1613ace913f82dc078f478b59781d35d0c96d48c284e83f34d1cb6 8a5a8a5863383c2356167387a1ea96dfc69dee1e4fd71fc9ac802e13f83a8ab7 261f64db2f2f84da5d321ab816b7789a01e11713208a8e9037dc47c342e853f2 e2fdbb498fd2f370cb0312286cb429ca68db4e2b5e12cfe8067040e09d1b8f30 8371905e871a8ac831ff906852903e80b0960fe35c4f4ceea84c271ff6668c98 0d1bc2c1556316fcc362211234a56fa44d1288b27d05516dda192cec12fc2028 06fb8979368bf9bfe7a3a90ccf5580a0531abff0784d779541b1ea3883ca7ad6 eb1d6d761ea8c88e0b394c3613ec0ee4c2dfffd4d3109cee9045267ae1c87d5f 0982ac180a60121f8ba20fb0aacadf6095d93c70739cce6fb21c77507d6453dc cc51deb3fe9e3c0805e0d17b3fa4fbaf2570ed864f91483668ab7de09b485f1f 9363265f645d6807c64f2c1390be73625b4b5a505827b0b38b8c9224913c7898 00f5be6054a93859ac0a737d1555a84a7dbad65055479a00b2b001f87b9e39b3 ccbad25a89a581894a8862ebda4e0aee3707da0c7221aef64274ba2d0203ce0c b4e73b89417fd80f3425f500ceaaeeb17043bc48ddd9acd3594269cbcb1fa909 dd7e125314cd758e945ef5b9feba2d8149d6dd56582909ca01ddc108e9dca99e c973a434a4e1f901f4ac6f0b3ed0df00532aaee344e0e630b5a92137ce80f3ad a1a0d5b9c3531d109a298fef20ec3ae54b207529e6a0fe2bd914d04100761518 f7df6ab04089e4aeef5da64928293e2bdbbd7cbd5b58f83e590d9cbd33a0b697 de29d55e7c9b4c5943068a0b626ca7e01b4163382bd159c78019fe0cd060050f 4ecb8927f13217c3c69267400b5d7a8baf2534d1492ba5ad38af7be8768e98dd be1e68da6bae5e22c85debbb2312552746f92031595b993fb1f13a89dc655349 11b1ae1c890fb2f63d7acdc49fece20ccc3a6efc7473389221b9877bbc5f4ab3 8872b41c0a81316ce6e032fc7e577490a9b9ac4107ad1b4f54372ab167408c4f 1aeaa750ed50108ab49de19b0d161f9ba7f2a6345be0a81d21c57479e2707d26 622202338becfdcaca1687c6b98a62cb9efa246d2a00b9bb4fc97646e8b9c5c0 366b9a0ca275db70324acb3ddca9d7c7933d51f4d429f2d08edbecf4b3a5436b 0c126ece7a8a81143e52efccaa5874cc399f4e894c723d914970b8ad52c0a9f5 80b1e9063d9c18f2cde998b20a892e21f3fc845b46d9cbf50407cce2569d4ef2 b528a557beea91690804f088287c75c2b609e72f14ce02ddda1c13bf0c6346ad 40a0024ab8d9851573f3b74c34c5e93ecdada4547b13baa08998c0ad5825273b bf312be691366d12a5a5f942a1966b9edac551888126dce2f40270a91220ed05ab5c5cc8d220871ab38064d2ff006f65fa1dc6596ea1d6e9c8d7dfcac6bd310f13e9677dda5552cadedc9e8f40a0cb031c90685f6a4cfb32c7311a6e38df1a0a7817687534d926ce9f5ab24cdc41ea5cafe7d25d32917d81cd91023164075708b7260fac238c29f6a3ef54d210323c904a6ab2970211e7ba98d154aedcf2ac06007b9b6c7757f7742059dfef096268eb8acccf4338353de88695bb7a0d653a0631d9c0ba229d2f78b2ec62a69799537fe5d1b5b15290f6783854395744347f099edf10b7ac044a66eaf849959275de07ff4ea55e958d6d9b209e596c3e5e920329a6fbb61654fc883e86d818de0c3a3ca6a9bb1a00c86d8872e2b248e3459c024a78b7164b3db559db6c3836c1fd68fd04d52043e2ffacd69a88f628d5d6a30e109536ee0a8862c9eee264be4b7372745f35c018321161ecfd9052fc80d99105e9ae87543f932329c5e1470bca804ea8ec2de7467892503afd13b5ddbcb6570a0136f13f5960ba707f1d680440f6d7dd289b08613bf816f1b4703192e4e36f0e414578e1de2974ca9e6fe61b13a585dab921b8420c28149c186e60f28b6c28031edb45dacf1fef39cbdaea8fc5dcd610f7846308c156723941f9ca7bf7185d01db315d371c9e8980e472e1ee206d0a41920c72e6e4b16eea720ac732648a18001283959eb56aeda997c918842db25bd7bb22b08e03ed7646b77431e369672503370f6f0914400699c02e1735a284a2a2aa66aeaa20d8a8ed3d30667e4dfeb7057416cfc1cbb474f60cad6e0036119fac7c8701f9cffccf89ebbc6907d5a4e8027f311a6a2a05f6ec5abb64d6748dc02899013817858234f45b8068f3b73c350d9e52f2f0dbec799064e1179c0e9b86a4e91cc2278008c75e6a7710c20ec0ba0f0c83b75a18b0b2e123f2f5ad2a9c1749a000bfb8d2c18f80274847205a4d5306a6d59d22a79e077d33d3f6b3f9f2dfeed27e5ea8f7e4302970126ed85731e50a81290132d066ada6b5350403cb0400c8106455eca7843ba65ce2267f0959d4082da50c6f3b0c6c915ebe49d8c145f5d0c865d98bdfdb8503beadea52d8b9e80b8bc4efc8d3c11f93ce5e6ee0e7233f00e0dbf7e9d209159b2e919b702ded45061c36af100090ea2e03e4db89cb83604d8a16f4e132879eb344c226d7e10e9804e2df25b075a8de26aa3e6c214aa521045e0608e3ff0e24fadfaf89d3fe8e29069dd3a9c43d7719b6cccc1e58ecd08095daef4f15c5e349fb5835aaf71b3c490174e2f9d08dd748f7d08ef9c51d0c926017967b01b5d90160a3cc1a6d384a960dadf4586c89fb208fd08b6ab26bd6c566b05b4a41d2a68cc812953490d68bdf089ec8747c6f3f3055c0f1186f3f138ef26e6776d409b2adac0934780dc8d8be0f91aded713013bb5ca872dc3bd49e955017f1fa1d01fa877a7872f2d393d4a6004ec95c70cdbdcd38ff9ab2c4e629dfa1d115863d1ba4fc08708263a64f66f2088ff5596d6b5ad9574cb2a136f22d347bbff13400044d2a1d8fb84ca68201be0bf785529e096d53f83466825907e42f16dc5635638b07df9694ed198cdc99400999e13427c794c2339143114a8dafc49adb02039502350efed7663ce8ab10f90861f8ce2dde08cd5365a1209acce515814036929f849be885fbec837192eff20622bbcbcace7178a57e6f55471f249f28c489c167a52ec2a717ae9dee85451d02b1a61ef5525c35dffe0b23055d8b71fa58bee9884ad66eb0b52689fcfd40ee090f16f2497c818371118b35b0e9aa5bf53c125b40f2cc0a40ebf4de189282ad0f58625753e506838ec1731e86df2000e2cb6ad0c217447062c7aa4b0c6f06ed063105179158c067792d10dd2f060ca59fae9b98b413e152d703afdac1a37ba100fce0807cf10c939465635d95022265158cb93d87304fc5789dc3d1afc3a2b50c8ca7ad9b822e88aee3ec4d23f892d5e592da44af108ef22f91059332bada5d0277071e25ce656232e1805661c88d05c65449198651c56c1b5f67b35e3ef55c0a908bb86643e583cab76cd51cb693f71dbb92290bf33832e315878721b8e37c0dcab5b793846856affb0ed8be72163ec48d36835a7dc9b1d1b8574f373797520f664023f134ce6a682c14c9c2d48fd873ff8b6c70c761a6ae44366ff54cf5580e33136ecce4ed081bbba72fa8619aca482d7e71942cd044af19c337c497704200de4691c591f726bd18c79a33a54e1632b05d1bf6fff1df93c831f99970a48a0eba67c945e5ca87cb72ee27a3139bc406964242c9dda9fed7670d654daa8f600c557ed4b765a8c703c04c54201217c183c4feb8eda4366cfce97530e6538722062820066c7aba1a00139ecb86db534e8847c99d96341d501596607476410d5a0564ea3b1fdd6d74b4b7bdb1c9cec56f82e424970eeb5f0a9e39f983c440f7b60b85bea3ca8a0ebe705ae747ce5c7917f56e2d3c99af13d272d107cb6e3639d90755b03a908777556972e85f3756fd3a5c41faa5c5aea099700beaf758ed1e9e034462bd18fa92f9190cb957295fbb2513c96ef69bb6827199594682e428fc3607de34f239535ef31b8260b848dd3bb56b61c88d64a4b4e9a77ac4043fcb962702033eb27743bc72630159076005dfbd2a674642972ce90b76c599941a45f2e3038b2ccaeddc485204286697567d96ff748f1565018cda04643286e06ae0feff0f0a1712d978f852bc8b806af637434016c41cc3669654f52cd781e2b0213fb202a4059b6cf186a65bfa8a930591b4556e530ed422a554647ea925e3cdc9fd5108a58e27deeb496d200f07f22b84fa7d2139937c4ec8e5da041ac844c724e2aa0282bb755937ef670ae3706a6b7895d43bad5df90da95f18e8b712284cb528da02915e19cb676072763fb11ca216f0a269de10f63ceba7c32cd7e9837f84ce700518873940e88a2eaac21907ec641e13ede87a46983091ee82ead335e1c9c79d07f0fad6b307c5e1832b1c51460fb7fe118e029bd69c5fef4631120ca4823dc80cf773055a9bdc4b557106fcfc999c462e3dc8f1dd2a3f1b751f9ad75752fac203e243bf417b65719d8c12e11f79ef04a8c48e251a952a683799bcaed1c8a5190c2acf55d77cc50e26e8c03de2a64b8b65fd45925395712f959f7ca5875fe0ca031a5712763bdc85c5315fb3897cd0f0406e8b7aad3d384666a02c63ab945e810990544571aec88e9fbcac3e01e71dd958e3845115578a99b2f093a195f2d3d507d6c5fc934f27f4995865b680eb3f91150fe89872ad287db165d7fef3d533d906ef2a43498c8eb74b0061ecb14c2556a74600911365b142eb76fdf73eabb9210b3ecbea987df7eda3a9a05749f38d1da65c8b68b2f7232c8fb269384fe26f0a099faf148ea77ad010427fce6d1851316dff5fd819481cd686dc4d48731c027901280e7ce76d43673bc610b079f892bcdcccd986a6648de0185ffae5395c63640edc87b3759f8893bb28f1c519098bb6ebba8350403d291dfc126a2d2e2295050925240ea17a1cfb8e0b4e4a52d11e1a23a18b074823ca78bf312fe766af18ca05f8432bd2f03ab7bae880efc1eb1bbdc9490485cdf3c510193118a1fe0bfe1003eca2aaab4c9df8300c5f5d572f53e6d3b40775099521f6491a025d2ddec1130ab571bac89b55a9c15410206307c6ce7c696fc5748ce03d66da896bd1488ac1026408bc08839acb0cfb335b9ba6692354dafe3b8d87d81a86ee674c66ee0ffd0dc629d90a3d4bb00f7a8c8a7d0f259ed1a4f52e708ea1f431fa808dcc8ee6300c01dbc5c02dae904d9ad2424d2ce2c0836e4ad34050ff3f169f1a17171ea5f4066df314bf64b4e1edf746629aeeebb48133e5aba0f33258d10fb7b0aafaeff80719cb3e59a1f580409ee08b8a20862815d3a1f64b8450f958990e49f2a02bd50fbb753463a2524bbe0847a52e9de9ba395164ec9d2d55a93c15290d1026ccfa01bb02a8c7cf6b14339de0a7b3f4ea49c9dffac9278fefdd10b83544281144e60f8c075c5d91e14e547d2d5d7c38515ac77aa896133f4e43be57316a73d7d5d8033e536276b8397c026faf8059b96e2e9be881e99c3f8b78bb9df884d2bec88e0597e85a1da3bd27d4be05b77ce1b698ed60bd4d483799ce8ead4d4b3a28924e03ff51fe5fd8ae6a3213055e986111ed70fde0a491c4d26ead6cc3631e987f1209 false +check_ring_signature 8a679d66c14aab2ef6e37b043f81b3e56f49557871d6a0ba00d11412e38d3c79 543b519d74ea78727408843ffa57e5f5969adb045cf06de667747048bd00e894 42 823bd254d598937bcae10fe1cb2440e9cbc762773478d71622d8bcfdfd13b780 3a96db2a74e13b30529bf41590960982d361892c0faa5fa1c36830b9d40e79cf 2c3c24c7c59e4059e4dd26d0928bc6b78dcbafc54b76604e29cac11fac2cd1e1 8252c18a8bce822e6b87c80bfae78679b419717b13ab2078f35771b0a1e1adca 8ec9516c287a1898b23aa2b8c917cfa78b99b7f4c18340e18745acb2db80aa33 77c67b7edc7d205cdb34064694e456ed81bd0e11933961df3fad17a0162d8b15 1ce90b6fb1531130addb0abeba5c847121e5dc3aebe4cc2eb8898780fe47de53 a913f551fa0cd6716317364f72b1a3cc3dedf5dcfd306e659611a04d18aa5f28 562c3d39ba02d07124720b4623709c9f560b1805114835a74988be347d5dc4b7 7a64107d79d84ae0660554a97ca9579042c673c64275976a11449b3b95b227b3 eaf2b5ee719f69d20e13a035d33b26bd2bf3b6e7dfa28f5feb493248c35afcd2 641023e40d53a52f8a72726b6ea2e1e8a55c280db84dfa43aa26f33164910192 00c3f9fb8c2be24e6df091baafa903284bce885043246a24e09534a90e478cb3 a465cfee2f8bcb2f29d1ca872aaef2fcf782c34be50b665140d274f110268398 a17418f81d1373b7aac4e85a12c3d9044c81dfe9c2d6bb7ff4c612a9d5b4264c 2ed706f4890d3807064f8f9c716e3ca43d027105159ff464a3a3d4a24d0bda16 774c70dd0fa1708b03a57c30606e4fff0ef8dd2734b8dc448f56203263abf0c3 c2d42283443ef4bb761eb4fd928ab939de3faf1e62b589ca23169b8609f4f7cb edbad70a1ea42a10996295027f91703dc6aebba1b1664254e771e5153f38a54f 0840ed18ca6dd93b1caa0b5c534d54154019d34882221600c2e19a710050c108 fa7cb16a5f647e92d68504f4d5bd00afef5c62532aee666611dcf06ef4da1e94 6a7bbffeb8373ea4b06dfcc11371e20fdcf9836fdc3d9fad484aecd5275df649 ad9c16d31aff0a2f737ab8d9ce6905fce25b4636a084b5357a8a71533f6cc455 8c01ee515557a5fcc31f14090763339f23067c62521b0e1dd12cb5f75db1874f f8e528701f8c16cda2f1f0e3a8692db425b7ceda0ef58bbb7393f3ecf335697b f260b961d5c7002cda87b39f50ac49cd4877094b83e25d133dd81d894f86e1e7 dbd936f9ddf5c6d856448876ee9247f4c3696c841012fcd4beb74c05380efcf2 3f7b10be038f39fa739374582c26796d5c7de51201f3659849feaaba06ad3fba bd8cb95056a90d623bae6e419b01678f9f9eb8c19b468ea42a6bd0a7bdfd2186 77fc824ed0b35be118c43037be7b891b6eef8424aa06e9b2baac7400451985cd d31738692b51ca4837e3ed3144342e92e3f7e4c2d5522d132b5b8bbf9c6eb13c f411fac352f98b965a94f5db4c189a9a42a746aaa88f453948c395d2c97cc7fa dad5262cad356c32091fa790934636ab4c93a4927d72fe3bd1f8f61dd997acf1 26e704a9c2654eb64460b22829af86e5416e2e46875aef233b1dd85111d08dc1 8551f0a646afb84d0bac9d03d68b7c50b2dd4b7557e894d572b447806661f1be 3633c66283a6b203b094696d84a57507cd8b5b9d852705c9a1b9031e19f97522 9707ea319bd5d7303e0eddaf1386d1407ecd0fc45a38d0c4290506e7831296b4 e061dc1f7c6db0cf2af5563adc4b768f97e1d6267b3f932f06f64c92098b2ee4 ac58c097fc5a2ea33abb91b99c2771096cd037f46432b16878ae8895d36af7e1 d6779f0be7060b5a1181dfa579eac077cb687d6591f09d50477d4f5b8fce2aa1 4a894b48b7d7fb36f0bd843aa14b5af4b62e5bf34a605ae717b9d7afc0dab361 12f1711e64e7491f088c4fc4d7b57ba8cc3a7139f8fcd827c77c3acbcee3fb4a 109a2f22d733c6b1863c32288c5ff7e5d3c6ae4ecdb17b2ca626d10c42ab9d0aef6c21370a0c15716f06cd5532e933bddd518d63396772c01126aca91a7cbd0fea465e0695d3c34180f6e1894adf298024183352dbfaf0c39de522eabffde806ef839794e61dd6d35c3d9f96a5bee65cec086593f28fbfa61d47cbd1c69f8d0ce6169916b7282a491b333c0c9b5c87becd8a5c79a56651f15c111cc5eb32420a9317d81e90a24d9c49ab7bf5596267da95e7d1d19d80d8efd12b3657e0ce0601b2aa8bd55fa8e08c2a6fc4d79d0979758fd5e6598230f210fffc98c04928e10daf5e296d01455d4e06f0f81cdd00e66b526dc9b1427ade2db53c94e494b30607864213e25fffa75811fd7127c3cda08a8c54aca4904ab8a48369e1d2cb15850e4252bf4ef3a324370f46609e4f125c162191354d5f419c46c8b5bb44e8088f0ce77258511157555a14944ca8c6cbc8500bbe2c42746c354ffe302518ee2da20165c8169ea8466286dfb22bbcb9767bac40f697fda2727c1b728f3bc6c3bb3b0a9e5479e089d97e67fe7c9efb25926479bd080359f7097042111cc648101033087087a4f249e959c64a7f849d54008b57ff419d196bf79bbbaad49c5f37cf9d06f139a30bdbbaedf03527c3194fa90a33e6411a7bc6acb6f945163cd2e0806c0e5c4956ac9bdc6ec49b4d1408ea14ace4815f7d0fd45b13e370e9d3486ec91b0a303d209c326ba67f3c207742fcf7e006d106d00428b18e28987c65bbf28e580c4c6320cc2401bcb37259c4c7e32620ed8ae630dc494c426e1b99526bfa579604d963adbb3d76bd670efa4e9b67cf7981edc269231d929577ac5a77a87f7844028721aa218f6b1c3daac382d71bf571b4a7ad2b7c1e0900f90b8f3fd1110af30b3c70ca18f807e073fc4cdf7339094667629d35e075d5428007ce9e345556c40117c9c0f013bf699e21f0ba9ee5c19b096e396bcd27f3e860c769f2e9e551bb0caa271bc2dc6e83a3f6ecda2121d6de7c5f36e56190c3146c397f1200aa5a160b47c9fe1adbffa1e17d8a4f741678a14a303bd218f0c31267ec38540b1a4d830d9c00f4a5c8b2f07b7635f7f8ea1a47524f223461f8da5ef946036f6a9912260a07cdb25d69a9aeb4d1c7f84a356ea2fbf23f26d9e26603487571c003684a450e08f2acf7073cc3c278e9672e6893acb08a01e0bc99ec358268d3d2b03066600560209e202072b349513145fc81bf689ef526502edffae2be158e9726b412440f0dca1d0c0f9ffbe50d5a02c33c6ad20003a1603e15f71ca1963a0011241f4409e1700689fc66c3946d94d719769a9cd43a8420d7aa41c976a849647524fabd036653f2ed6e4619e88a111e4546ebcea389cb95c451ed0a39313e119feaa2bd014806ccc15f06e8a3cf22c8edb19d876db095386ccedbf830a08b77ca667edd0e94439bb3c681b6b99380d651a15999eba687c63c22598e314a74796a5f10fa0315afeff96bb56f22c92834945eda79f84e2fe8429f9c8373bbbc0c8aa4cbcf0677a0a862002a8b28b9630331506e123c76f1873ef14474d22c8bc5f39fe083094072f175f946b538526eb07eb4bf98aeea3b572ab545d9d7c6f7f604665ccb01d3401366718bd50727c367b3a0f36f2c8b356d4f22b49fc9fd63f66763f9ca08bf2755f191f0b5430113690be86e6542072d126868aa20d535356b64397fea01ca63a50e5e357a0df29b22412935121c0819e688b8787edeb4c49cff9685740e23f77d1d7f4eb506fce4d4ce08d5ac93056f2072f4951fef24ff9b6d7a007a06351414e07ffe163d06b3aa80afbfbc0d19024b76f89b6d652732a3d45e53550a7a5102c3124db485202164ea0af37855668d6959b7546f9fa277609251e3ba075ecfad8eb3a090783535d3777779331a86d69a91ea6c5b7ec79f4477c0eb6d0b41ba1516e0d0a961424a27809847b0ee996d6913827759773b27e00a7862240e893a5b070fd87f4c0c36cc6a8320326f95619b253297f00df16a88bef7923403317eb7b825bc085c92e74757f2aaae4173b7fed41f00330472b520b976c18105ef8a9fd8e7889b90f29367ed0b33056d80b70c4dae989da25a9e552f7243d40ed49d313b9fb928e6d91ee695b130d6621e881e5503776a58476670854ecf99004c58d7cf93f70bda2ef9f44e2edac69b8bc521cf6872846d1e5f331931f9be07baae7ad34bc5f9b76da178dbb48f128f6c8383a5d41341c8e3d762a16d041d0e8d2d4f5c393e7de859ea9c6a3c0623eac3c8ce3cf2eb1fbe614f271828287c0617d0eed5c41c46ef47d29e7c17ce4658a84019a814cf3e9e544dd28bee0c5b0c4d86fe23fee200f2f90f27237500d4dbfa1000833db28d6358f0d5ac3e60490aa6b5f77d8a8c84b368fcd3dcc30f606b145ae0498400a5c8a32cce87c488f704ca87fff91016f01b9cbff21ef0af94f7612237466b548fad2ddb2109eacece012818560668684bb4e04a4508cdaef8583523f8284a159ab542917f07bb1e300b51566d41f7f8b8d16e56bdfe784a1a30f9e89a351d7326f87ae88b0f79109e0f95fe96c4347ab449f6d413d0f74fa792b20ad285a229434915d7316f48fa9b0ac79fc198069776d6ffecce1605572dbe5b90659cc8c24c082ad3871cf653b00430a7c5f1b6c5ee2ad7d6b04fb9de359a9e9c9fba449587f28f94a9049a255a0e2fcc55c8a531154f07b9170f6bc6bc5e23395ecc5cb9d7b4354bca0051ef110c110d6f63514f8bccd4e5181c860bb2af498e7b0cbe042698a8ffd070d36aed0b6d8c6713f08f74947eed9c0f52a97c4beabbb457a0b616a307a4efa92efbea0747d6f96ff837ce0c4e2fe18e077f38010467f7000493c0abb09c5c580604da0180824117c106f7c16670bbcaf6304d20525e06dc67e1d88d8d7cda5ad6d08e03cada0049531e590603648f40e4b01d3b972b1cb2b656204c5834f154d82834066dab0b8d575f82ad12e8e1ed3f83acb850254b81058c2cd382c0fabd4ff0a203b670a292c72aad9297493d08f863fe0e2f95d08c35749db3a589595c46b1c7053611a02dd9da0f6540d37acf44060252e841582e22bbff5c42afcf9fe141ef01cac4c33fc2ba264a3f8afb924cba9a20b5468e7cd2c04e2c20994432c45b2e0f2b25272d35460a17a500263de88b69f9c0fdcb9f519ef2865788b607cb0af30c7958d37f90ceb1b9c88a5937e53cf5a5f092b85aef65a8a28bb790d916c969058edbb58420c3edcd0625da0d4a84ff32b3dd1a6d24fdc33c9f3ade2fcf048907b8fd26c56d4288285349b75d6ccd2e2a05583136b2fe1fbb613934d6ac757f023e2d444eb211ef12d41d61327f171191a5584162fe2600a8b7ab530a09d76d080750d3461cd07b3344d56f69c5c98af2e82fd95bb0927fecae55eb88a61871037728579eb256951f065fcb29900952ef263c4d365e51174e9028ccdcafb91f0047c745164feca994d64b7fc1743b635e7b28ab3ed8aba40f1ddfc6757e347009f729edfbdb7d4b6bd3d4b10ede96cdfb7940b70d6b07f474e34461360c6e66064a0a8d668febec8250fcadcb3d551bc3782238527431d4cb12eab9fdf434ce0e4f576f967f9257cdcd2aa857fd4495f654f42a661add4577df90eb962a4aa5014fe839ce41791f53f26056272da8f5b89bf4c35d53dd602ec82521af91e1d704bc3fb13fbfc82859e797027dd30c776c5ab610fe76ae91e1aa47ff26c0c68f0856c5b96fe60ea56f87a2def6db39122202141eb232a248ac2c674291b97caf02 true +check_ring_signature 7b17530a49f56a4a18e3b60d221a7e80b554c0fc75afa0ebb688ef2728ad2d01 b145b368eec6465c691723732ec95d204a1949116e0761ad872404ed218cea9b 1 bc623efa3190e9c12d558ed0a2ffff359e2326ccf9090c0903e5166e333090e0 8b31ba8eb54904c550769473deea2bf7b5a082ccff0b34945d397eb19875010d1cd5cd1375642d82940a01445dfbc1bd946753f6a66b5141335ad5386818740e false diff --git a/tests/functional_tests/main.cpp b/tests/functional_tests/main.cpp deleted file mode 100644 index 87eac61b61..0000000000 --- a/tests/functional_tests/main.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include - -#include "include_base_utils.h" -#include "string_tools.h" -using namespace epee; - -#include "common/command_line.h" -#include "transactions_flow_test.h" - -namespace po = boost::program_options; - -namespace -{ - const command_line::arg_descriptor arg_test_transactions_flow = {"test_transactions_flow", ""}; - - const command_line::arg_descriptor arg_working_folder = {"working-folder", "", "."}; - const command_line::arg_descriptor arg_source_wallet = {"source-wallet", "", "", true}; - const command_line::arg_descriptor arg_dest_wallet = {"dest-wallet", "", "", true}; - const command_line::arg_descriptor arg_daemon_addr_a = {"daemon-addr-a", "", "127.0.0.1:8080"}; - const command_line::arg_descriptor arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"}; - - const command_line::arg_descriptor arg_transfer_amount = {"transfer_amount", "", 60000000000000}; - const command_line::arg_descriptor arg_mix_in_factor = {"mix-in-factor", "", 10}; - const command_line::arg_descriptor arg_tx_count = {"tx-count", "", 100}; - const command_line::arg_descriptor arg_tx_per_second = {"tx-per-second", "", 20}; - const command_line::arg_descriptor arg_test_repeat_count = {"test_repeat_count", "", 1}; -} - -int main(int argc, char* argv[]) -{ - TRY_ENTRY(); - string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str()); - - po::options_description desc_options("Allowed options"); - command_line::add_arg(desc_options, command_line::arg_help); - - command_line::add_arg(desc_options, arg_test_transactions_flow); - - command_line::add_arg(desc_options, arg_working_folder); - command_line::add_arg(desc_options, arg_source_wallet); - command_line::add_arg(desc_options, arg_dest_wallet); - command_line::add_arg(desc_options, arg_daemon_addr_a); - command_line::add_arg(desc_options, arg_daemon_addr_b); - - command_line::add_arg(desc_options, arg_transfer_amount); - command_line::add_arg(desc_options, arg_mix_in_factor); - command_line::add_arg(desc_options, arg_tx_count); - command_line::add_arg(desc_options, arg_tx_per_second); - command_line::add_arg(desc_options, arg_test_repeat_count); - - po::variables_map vm; - bool r = command_line::handle_error_helper(desc_options, [&]() - { - po::store(po::parse_command_line(argc, argv, desc_options), vm); - po::notify(vm); - return true; - }); - if (!r) - return 1; - - if (command_line::get_arg(vm, command_line::arg_help)) - { - std::cout << desc_options << std::endl; - return 0; - } - - if (command_line::get_arg(vm, arg_test_transactions_flow)) - { - std::string working_folder = command_line::get_arg(vm, arg_working_folder); - std::string path_source_wallet, path_target_wallet; - if(command_line::has_arg(vm, arg_source_wallet)) - path_source_wallet = command_line::get_arg(vm, arg_source_wallet); - if(command_line::has_arg(vm, arg_dest_wallet)) - path_target_wallet = command_line::get_arg(vm, arg_dest_wallet); - - std::string daemon_addr_a = command_line::get_arg(vm, arg_daemon_addr_a); - std::string daemon_addr_b = command_line::get_arg(vm, arg_daemon_addr_b); - uint64_t amount_to_transfer = command_line::get_arg(vm, arg_transfer_amount); - size_t mix_in_factor = command_line::get_arg(vm, arg_mix_in_factor); - size_t transactions_count = command_line::get_arg(vm, arg_tx_count); - size_t transactions_per_second = command_line::get_arg(vm, arg_tx_per_second); - size_t repeat_count = command_line::get_arg(vm, arg_test_repeat_count); - - for(size_t i = 0; i != repeat_count; i++) - if(!transactions_flow_test(working_folder, path_source_wallet, path_target_wallet, daemon_addr_a, daemon_addr_b, amount_to_transfer, mix_in_factor, transactions_count, transactions_per_second)) - break; - - std::string s; - std::cin >> s; - - return 1; - } - else - { - std::cout << desc_options << std::endl; - return 1; - } - - CATCH_ENTRY_L0("main", 1); - - return 0; -} diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp deleted file mode 100644 index b97c51d666..0000000000 --- a/tests/functional_tests/transactions_flow_test.cpp +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include -#include -#include -#include - -#include "include_base_utils.h" -using namespace epee; - -#include "cryptonote_core/Currency.h" -#include "wallet/wallet2.h" -using namespace cryptonote; - -std::string generate_random_wallet_name() -{ - std::stringstream ss; - ss << boost::uuids::random_generator()(); - return ss.str(); -} - -inline uint64_t random(const uint64_t max_value) { - return (uint64_t(rand()) ^ - (uint64_t(rand())<<16) ^ - (uint64_t(rand())<<32) ^ - (uint64_t(rand())<<48)) % max_value; -} - -bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor, uint64_t amount_to_transfer, Transaction& tx, size_t parts=1) -{ - CHECK_AND_ASSERT_MES(parts > 0, false, "parts must be > 0"); - - std::vector dsts; - dsts.reserve(parts); - uint64_t amount_used = 0; - uint64_t max_part = amount_to_transfer / parts; - - for (size_t i = 0; i < parts; ++i) - { - cryptonote::tx_destination_entry de; - de.addr = w2.get_account().get_keys().m_account_address; - - if (i < parts - 1) - de.amount = random(max_part); - else - de.amount = amount_to_transfer - amount_used; - amount_used += de.amount; - - //std::cout << "PARTS (" << amount_to_transfer << ") " << amount_used << " " << de.amount << std::endl; - - dsts.push_back(de); - } - - try - { - w1.transfer(dsts, mix_in_factor, 0, w1.currency().minimumFee(), std::vector(), - tools::detail::null_split_strategy, tools::tx_dust_policy(w1.currency().defaultDustThreshold()), tx); - return true; - } - catch (const std::exception&) - { - return false; - } -} - -uint64_t get_money_in_first_transfers(const tools::wallet2::transfer_container& incoming_transfers, size_t n_transfers) -{ - uint64_t summ = 0; - size_t count = 0; - BOOST_FOREACH(const tools::wallet2::transfer_details& td, incoming_transfers) - { - summ += td.m_tx.vout[td.m_internal_output_index].amount; - if(++count >= n_transfers) - return summ; - } - return summ; -} - -#define FIRST_N_TRANSFERS 10*10 - -bool transactions_flow_test(std::string& working_folder, - std::string path_source_wallet, - std::string path_terget_wallet, - std::string& daemon_addr_a, - std::string& daemon_addr_b, - uint64_t amount_to_transfer, size_t mix_in_factor, size_t transactions_count, size_t transactions_per_second) -{ - LOG_PRINT_L0("-----------------------STARTING TRANSACTIONS FLOW TEST-----------------------"); - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - tools::wallet2 w1(currency), w2(currency); - if(path_source_wallet.empty()) - path_source_wallet = generate_random_wallet_name(); - - if(path_terget_wallet.empty()) - path_terget_wallet = generate_random_wallet_name(); - - - try - { - w1.generate(working_folder + "/" + path_source_wallet, ""); - w2.generate(working_folder + "/" + path_terget_wallet, ""); - } - catch (const std::exception& e) - { - LOG_ERROR("failed to generate wallet: " << e.what()); - return false; - } - - w1.init(daemon_addr_a); - - size_t blocks_fetched = 0; - bool received_money; - bool ok; - if(!w1.refresh(blocks_fetched, received_money, ok)) - { - LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a ); - return false; - } - - w2.init(daemon_addr_b); - - LOG_PRINT_GREEN("Using wallets: " << ENDL - << "Source: " << currency.accountAddressAsString(w1.get_account()) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL - << "Target: " << currency.accountAddressAsString(w2.get_account()) << ENDL << "Path: " << working_folder + "/" + path_terget_wallet, LOG_LEVEL_1); - - //lets do some money - epee::net_utils::http::http_simple_client http_client; - COMMAND_RPC_STOP_MINING::request daemon1_req = AUTO_VAL_INIT(daemon1_req); - COMMAND_RPC_STOP_MINING::response daemon1_rsp = AUTO_VAL_INIT(daemon1_rsp); - bool r = net_utils::invoke_http_json_remote_command2(daemon_addr_a + "/stop_mine", daemon1_req, daemon1_rsp, http_client, 10000); - CHECK_AND_ASSERT_MES(r, false, "failed to stop mining"); - - COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); - COMMAND_RPC_START_MINING::response daemon_rsp = AUTO_VAL_INIT(daemon_rsp); - daemon_req.miner_address = currency.accountAddressAsString(w1.get_account()); - daemon_req.threads_count = 9; - r = net_utils::invoke_http_json_remote_command2(daemon_addr_a + "/start_mining", daemon_req, daemon_rsp, http_client, 10000); - 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"); - - //wait for money, until balance will have enough money - w1.refresh(blocks_fetched, received_money, ok); - while(w1.unlocked_balance() < amount_to_transfer) - { - misc_utils::sleep_no_w(1000); - w1.refresh(blocks_fetched, received_money, ok); - } - - //lets make a lot of small outs to ourselves - //since it is not possible to start from transaction that bigger than 20Kb, we gonna make transactions - //with 500 outs (about 18kb), and we have to wait appropriate count blocks, mined for test wallet - while(true) - { - tools::wallet2::transfer_container incoming_transfers; - w1.get_transfers(incoming_transfers); - if(incoming_transfers.size() > FIRST_N_TRANSFERS && get_money_in_first_transfers(incoming_transfers, FIRST_N_TRANSFERS) < w1.unlocked_balance() ) - { - //lets go! - size_t count = 0; - BOOST_FOREACH(tools::wallet2::transfer_details& td, incoming_transfers) - { - cryptonote::Transaction tx_s; - bool r = do_send_money(w1, w1, 0, td.m_tx.vout[td.m_internal_output_index].amount - currency.minimumFee(), tx_s, 50); - CHECK_AND_ASSERT_MES(r, false, "Failed to send starter tx " << get_transaction_hash(tx_s)); - LOG_PRINT_GREEN("Starter transaction sent " << get_transaction_hash(tx_s), LOG_LEVEL_0); - if(++count >= FIRST_N_TRANSFERS) - break; - } - break; - }else - { - misc_utils::sleep_no_w(1000); - w1.refresh(blocks_fetched, received_money, ok); - } - } - //do actual transfer - uint64_t transfered_money = 0; - uint64_t transfer_size = amount_to_transfer/transactions_count; - size_t i = 0; - struct tx_test_entry - { - Transaction tx; - size_t m_received_count; - uint64_t amount_transfered; - }; - crypto::key_image lst_sent_ki = AUTO_VAL_INIT(lst_sent_ki); - std::unordered_map txs; - for(i = 0; i != transactions_count; i++) - { - uint64_t amount_to_tx = (amount_to_transfer - transfered_money) > transfer_size ? transfer_size: (amount_to_transfer - transfered_money); - while(w1.unlocked_balance() < amount_to_tx + currency.minimumFee()) - { - misc_utils::sleep_no_w(1000); - LOG_PRINT_L0("not enough money, waiting for cashback or mining"); - w1.refresh(blocks_fetched, received_money, ok); - } - - Transaction tx; - /*size_t n_attempts = 0; - while (!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx)) { - n_attempts++; - std::cout << "failed to transfer money, refresh and try again (attempts=" << n_attempts << ")" << std::endl; - w1.refresh(); - }*/ - - - 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); - 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" ); - LOCAL_ASSERT(false); - return false; - } - } - lst_sent_ki = boost::get(tx.vin[0]).keyImage; - - transfered_money += amount_to_tx; - - LOG_PRINT_L0("transferred " << amount_to_tx << ", i=" << i ); - tx_test_entry& ent = txs[get_transaction_hash(tx)] = boost::value_initialized(); - ent.amount_transfered = amount_to_tx; - ent.tx = tx; - //if(i % transactions_per_second) - // misc_utils::sleep_no_w(1000); - } - - - LOG_PRINT_L0( "waiting some new blocks..."); - //wait two blocks before sync on another wallet on another daemon - misc_utils::sleep_no_w(static_cast(currency.difficultyTarget() * 20 * 1000)); - LOG_PRINT_L0( "refreshing..."); - bool recvd_money = false; - while(w2.refresh(blocks_fetched, recvd_money, ok) && ( (blocks_fetched && recvd_money) || !blocks_fetched ) ) - { - //wait two blocks before sync on another wallet on another daemon - misc_utils::sleep_no_w(static_cast(currency.difficultyTarget() * 1000)); - } - - uint64_t money_2 = w2.balance(); - if(money_2 == transfered_money) - { - LOG_PRINT_GREEN("-----------------------FINISHING TRANSACTIONS FLOW TEST OK-----------------------", LOG_LEVEL_0); - LOG_PRINT_GREEN("transferred " << currency.formatAmount(transfered_money) << " via " << i << " transactions" , LOG_LEVEL_0); - return true; - }else - { - tools::wallet2::transfer_container tc; - w2.get_transfers(tc); - BOOST_FOREACH(tools::wallet2::transfer_details& td, tc) - { - auto it = txs.find(get_transaction_hash(td.m_tx)); - CHECK_AND_ASSERT_MES(it != txs.end(), false, "transaction not found in local cache"); - it->second.m_received_count += 1; - } - - BOOST_FOREACH(auto& tx_pair, txs) - { - if(tx_pair.second.m_received_count != 1) - { - LOG_PRINT_RED_L0("Transaction lost: " << get_transaction_hash(tx_pair.second.tx)); - } - - } - - LOG_PRINT_RED_L0("-----------------------FINISHING TRANSACTIONS FLOW TEST FAILED-----------------------" ); - LOG_PRINT_RED_L0("income " << currency.formatAmount(money_2) << " via " << i << - " transactions, expected money = " << currency.formatAmount(transfered_money) ); - LOCAL_ASSERT(false); - return false; - } - - return true; -} diff --git a/tests/functional_tests/transactions_flow_test.h b/tests/functional_tests/transactions_flow_test.h deleted file mode 100644 index c9680a3d65..0000000000 --- a/tests/functional_tests/transactions_flow_test.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - - -bool transactions_flow_test(std::string& working_folder, - std::string path_source_wallet, - std::string path_terget_wallet, - std::string& daemon_addr_a, - std::string& daemon_addr_b, - uint64_t amount_to_transfer, size_t mix_in_factor, size_t transactions_count, size_t transactions_per_second); diff --git a/tests/functional_tests/transactions_generation_from_blockchain.cpp b/tests/functional_tests/transactions_generation_from_blockchain.cpp deleted file mode 100644 index 14f659f635..0000000000 --- a/tests/functional_tests/transactions_generation_from_blockchain.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "include_base_utils.h" -using namespace epee; -#include "wallet/wallet2.h" -#include "cryptonote_core/blockchain_storage.h" - -using namespace cryptonote; - -/* -bool transactions_generation_from_blockchain(std::string& blockchain_folder_path) -{ - string_tools::parse_hexstr_to_binbuff() - tx_memory_pool pool; - blockchain_storage bchs(pool); - bool r = bchs.init(blockchain_folder_path); - CHECK_AND_ASSERT_MES(r, false, "failed to load blockchain"); - - //amount = 3000000000000 - //keyOffsets = 1,2,3,4,5,10,12,27,31,33,34 - // -} - -tx_source_entry::output_entry make_outptu_entr_for_gindex(size_t i, std::map& txs, std::vector >& v) -{ - tx_source_entry::output_entry oe; - oe = i; - oe.second = txs[v[i].first].boost::get(vout[v[i].second].target).key; - return oe; -} - -bool make_tx(blockchain_storage& bch) -{ - std::map txs; - std::vector > v; - bch.get_outs_for_amounts(3000000000000, v); - - std::vector sources(11); - sources[0].amount = 3000000000000; - sources[0].outputs.push_back(make_outptu_entr_for_gindex(1, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(2, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(3, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(4, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(5, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(10, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(12, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(27, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(31, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(33, txs, v)); - sources[0].outputs.push_back(make_outptu_entr_for_gindex(34, txs, v)); - sources[0].real_out_tx_key = - - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - { - sources.resize(sources.size()+1); - cryptonote::tx_source_entry& src = sources.back(); - transfer_details& td = *it; - src.amount = td.m_tx.vout[td.m_internal_output_index].amount; - //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;}); - BOOST_FOREACH(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 = daemon_oe.out_key; - 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 = boost::get(td.m_tx.vout[td.m_internal_output_index].target).key; - auto interted_it = src.outputs.insert(it_to_insert, real_oe); - src.real_out_tx_key = td.m_tx.tx_pub_key; - src.real_output = interted_it - src.outputs.begin(); - src.real_output_in_tx_index = td.m_internal_output_index; - ++i; - } - - - if(found_money != needed_money) - { - //lets make last output to odd money - dsts.resize(dsts.size()+1); - cryptonote::tx_destination_entry& destination = dsts.back(); - CHECK_AND_ASSERT_MES(found_money > needed_money, false, "internal error found_money=" << found_money << " !> needed_money=" << needed_money); - destination.amount = found_money - needed_money; - } - - - transaction tx; - bool r = cryptonote::construct_tx(m_account.get_keys(), sources, dsts, tx, unlock_time); - if(!r) - { - std::cout << "transaction construction failed" << std::endl; - } - - COMMAND_RPC_SEND_RAW_TX::request req; - req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx)); - COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; - r = net_utils::http::invoke_http_json_remote_command(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client); - CHECK_AND_ASSERT_MES(r, false, "failed to send transaction"); - if(daemon_send_resp.status != CORE_RPC_STATUS_OK) - { - std::cout << "daemon failed to accept generated transaction" << ENDL; - return false; - } - - std::cout << "transaction generated ok and sent to daemon" << std::endl; - BOOST_FOREACH(transfer_container::iterator it, selected_transfers) - it->m_spent = true; - - return true; -}*/ diff --git a/tests/integration_test_lib/BaseFunctionalTest.cpp b/tests/integration_test_lib/BaseFunctionalTest.cpp new file mode 100755 index 0000000000..cfeedea607 --- /dev/null +++ b/tests/integration_test_lib/BaseFunctionalTest.cpp @@ -0,0 +1,345 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BaseFunctionalTest.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "p2p/NetNodeConfig.h" +#include "cryptonote_core/CoreConfig.h" + +#include "RPCTestNode.h" +#include "wallet/Wallet.h" +#include "Logger.h" + +#if defined __linux__ +#include +#include +#include +#endif + +using namespace Tests::Common; + +void BaseFunctionalTest::launchTestnet(size_t count, Topology t) { + if (count < 1) LOG_WARNING("Testnet has no nodes"); + for (uint16_t i = 0; i < count; ++i) { + std::string dataDirPath = m_dataDir + "/node"; + dataDirPath += boost::lexical_cast(i); + boost::filesystem::create_directory(dataDirPath); + + std::ofstream config(dataDirPath + "/daemon.conf", std::ios_base::trunc | std::ios_base::out); + + uint16_t rpcPort = RPC_FIRST_PORT + i; + uint16_t p2pPort = P2P_FIRST_PORT + i; + + config + << "rpc-bind-port=" << rpcPort << std::endl + << "p2p-bind-port=" << p2pPort << std::endl + << "log-level=2" << std::endl + << "log-file=test_bytecoind_" << i + 1 << ".log" << std::endl; + + switch (t) { + case Line: + if (i != count - 1) config << "add-exclusive-node=127.0.0.1:" << p2pPort + 1 << std::endl; + if (i != 0) config << "add-exclusive-node=127.0.0.1:" << p2pPort - 1 << std::endl; + break; + case Ring: { + uint16_t p2pExternalPort = P2P_FIRST_PORT + (i + 1) % count; + config << "add-exclusive-node=127.0.0.1:" << p2pExternalPort + 1 << std::endl; + } + break; + case Star: + if (i == 0) { + for (size_t node = 1; node < count; ++node) + config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT + node << std::endl; + } + else { + config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT << std::endl; + } + break; + } + config.close(); +#if defined WIN32 + std::string commandLine = "start /MIN \"bytecoind\" \"" + m_daemonDir + "\\bytecoind.exe\" --testnet --data-dir=\"" + dataDirPath + "\" --config-file=daemon.conf"; + LOG_DEBUG(commandLine); + system(commandLine.c_str()); +#elif defined __linux__ + auto pid = fork(); + if( pid == 0 ) { + std::string pathToDaemon = "" + m_daemonDir + "/bytecoind"; + close(1); + close(2); + std::string dataDir = "--data-dir=" + dataDirPath + ""; + if(execl(pathToDaemon.c_str(), "bytecoind", "--testnet", dataDir.c_str(), "--config-file=daemon.conf", NULL) == -1) { + LOG_ERROR(TO_STRING(errno)); + } + throw std::runtime_error("failed to start daemon"); + } else if(pid > 0) { + pids.push_back(pid); + } +#else + +#endif + + nodeDaemons.push_back( + std::unique_ptr(new RPCTestNode(rpcPort, m_dispatcher)) + ); + } + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); //for initial update + nodeDaemons[0]->makeINode(mainNode); + makeWallet(workingWallet, mainNode); +} + +void BaseFunctionalTest::launchTestnetWithInprocNode(size_t count, Topology t) { + if (count < 1) LOG_WARNING("Testnet has no nodes"); + for (uint16_t i = 0; i < count-1; ++i) { + std::string dataDirPath = m_dataDir + "/node"; + dataDirPath += boost::lexical_cast(i); + boost::filesystem::create_directory(dataDirPath); + + std::ofstream config(dataDirPath + "/daemon.conf", std::ios_base::trunc | std::ios_base::out); + + uint16_t rpcPort = RPC_FIRST_PORT + i; + uint16_t p2pPort = P2P_FIRST_PORT + i; + + config + << "rpc-bind-port=" << rpcPort << std::endl + << "p2p-bind-port=" << p2pPort << std::endl + << "log-level=2" << std::endl + << "log-file=test_bytecoind_" << i + 1 << ".log" << std::endl; + + switch (t) { + case Line: + config << "add-exclusive-node=127.0.0.1:" << p2pPort + 1 << std::endl; + if (i != 0) config << "add-exclusive-node=127.0.0.1:" << p2pPort - 1 << std::endl; + break; + case Ring: { + uint16_t p2pExternalPort = P2P_FIRST_PORT + (i + 1) % count; + config << "add-exclusive-node=127.0.0.1:" << p2pExternalPort + 1 << std::endl; + } + break; + case Star: + if (i == 0) { + for (size_t node = 1; node < count; ++node) + config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT + node << std::endl; + } else { + config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT << std::endl; + } + break; + } + config.close(); +#if defined WIN32 + std::string commandLine = "start /MIN \"bytecoind\" \"" + m_daemonDir + "\\bytecoind.exe\" --testnet --data-dir=\"" + dataDirPath + "\" --config-file=daemon.conf"; + LOG_DEBUG(commandLine); + system(commandLine.c_str()); +#elif defined __linux__ + auto pid = fork(); + if (pid == 0) { + std::string pathToDaemon = "" + m_daemonDir + "/bytecoind"; + close(1); + close(2); + std::string dataDir = "--data-dir=" + dataDirPath + ""; + if (execl(pathToDaemon.c_str(), "bytecoind", "--testnet", dataDir.c_str(), "--config-file=daemon.conf", NULL) == -1) { + LOG_ERROR(TO_STRING(errno)); + } + throw std::runtime_error("failed to start daemon"); + } else if (pid > 0) { + pids.push_back(pid); + } +#else + +#endif + + nodeDaemons.push_back( + std::unique_ptr(new RPCTestNode(rpcPort, m_dispatcher)) + ); + } + + this->core.reset(new cryptonote::core(m_currency, NULL)); + this->protocol.reset(new cryptonote::t_cryptonote_protocol_handler(*core, NULL)); + this->p2pNode.reset(new nodetool::node_server>(*protocol)); + protocol->set_p2p_endpoint(p2pNode.get()); + core->set_cryptonote_protocol(protocol.get()); + + std::string dataDirPath = m_dataDir + "/node"; + dataDirPath += boost::lexical_cast(count - 1); + boost::filesystem::create_directory(dataDirPath); + + uint16_t p2pPort = P2P_FIRST_PORT + static_cast(count) - 1; + + nodetool::NetNodeConfig p2pConfig; + p2pConfig.bindIp = "127.0.0.1"; + p2pConfig.bindPort = boost::lexical_cast(p2pPort); + nodetool::net_address addr; + addr.ip = 0x7f000001; + + p2pConfig.externalPort = 0; + p2pConfig.allowLocalIp = false; + p2pConfig.hideMyPort = false; + p2pConfig.configFolder = dataDirPath; + + + switch (t) { + case Line: + addr.port = p2pPort - 1; + p2pConfig.exclusiveNodes.push_back(addr); + break; + case Ring: + addr.port = p2pPort - 1; + p2pConfig.exclusiveNodes.push_back(addr); + addr.port = P2P_FIRST_PORT; + p2pConfig.exclusiveNodes.push_back(addr); + break; + case Star: + addr.port = P2P_FIRST_PORT; + p2pConfig.exclusiveNodes.push_back(addr); + break; + } + + if (!p2pNode->init(p2pConfig, true)) { + throw std::runtime_error("Failed to init p2pNode"); + } + + protocol->init(); + + cryptonote::MinerConfig emptyMiner; + cryptonote::CoreConfig coreConfig; + coreConfig.configFolder = dataDirPath; + core->init(coreConfig, emptyMiner, true); + + inprocNode.reset(new CryptoNote::InProcessNode(*core, *protocol)); + std::promise p; + auto future = p.get_future(); + inprocNode->init([&p](std::error_code ec) { + p.set_value(); + if (ec) { + std::cout << ec.message() << std::endl; + } + }); + + future.get(); + + std::thread serverThread( + std::bind( + &nodetool::node_server>::run, + p2pNode.get() + ) + ); + serverThread.detach(); + + + + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); //for initial update + nodeDaemons[0]->makeINode(mainNode); + makeWallet(workingWallet, mainNode); + + + + +} + + +BaseFunctionalTest::~BaseFunctionalTest() { + if (mainNode) { + mainNode->shutdown(); + } + + if (inprocNode) { + inprocNode->shutdown(); + } + + if (p2pNode) { + p2pNode->send_stop_signal(); + } + + std::this_thread::sleep_for(std::chrono::seconds(2)); + stopTestnet(); +} + +namespace { + class WaitForCoinBaseObserver : public CryptoNote::IWalletObserver { + Semaphore& m_gotReward; + CryptoNote::IWallet& m_wallet; + public: + WaitForCoinBaseObserver(Semaphore& gotReward, CryptoNote::IWallet& wallet) : m_gotReward(gotReward), m_wallet(wallet) { } + virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override { + CryptoNote::TransactionInfo trInfo; + m_wallet.getTransaction(transactionId, trInfo); + if (trInfo.isCoinbase) m_gotReward.notify(); + } + }; +} + +bool BaseFunctionalTest::mineBlock(std::unique_ptr& wallet) { + if (nodeDaemons.empty() || !wallet) return false; + if (!nodeDaemons.front()->stopMining()) return false; + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + Semaphore gotReward; + WaitForCoinBaseObserver cbo(gotReward, *wallet.get()); + wallet->addObserver(&cbo); + if(!nodeDaemons.front()->startMining(1, wallet->getAddress())) return false; + gotReward.wait(); + if (!nodeDaemons.front()->stopMining()) return false; + wallet->removeObserver(&cbo); + return true; +} +bool BaseFunctionalTest::mineBlock() { + return mineBlock(workingWallet); +} + +bool BaseFunctionalTest::startMining(size_t threads) { + if (nodeDaemons.empty() || !workingWallet) return false; + if(!stopMining()) return false; + return nodeDaemons.front()->startMining(threads, workingWallet->getAddress()); +} + +bool BaseFunctionalTest::stopMining() { + if (nodeDaemons.empty()) return false; + return nodeDaemons.front()->stopMining(); +} + +bool BaseFunctionalTest::makeWallet(std::unique_ptr & wallet, std::unique_ptr& node, const std::string& password) { + if (!node) return false; + wallet = std::unique_ptr(new CryptoNote::Wallet(m_currency, *node)); + wallet->initAndGenerate(password); + return true; +} + +void BaseFunctionalTest::stopTestnet() { + for (auto& Daemon : nodeDaemons) { + Daemon->stopDaemon(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); +#ifdef __linux__ + for (auto& pid : pids) { + int status; + while (-1 == waitpid(pid, &status, 0)); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + std::cerr << "Process " << " (pid " << pid << ") failed" << std::endl; + exit(1); + } + } +#endif +} diff --git a/tests/integration_test_lib/BaseFunctionalTest.h b/tests/integration_test_lib/BaseFunctionalTest.h new file mode 100755 index 0000000000..fbd5505dc9 --- /dev/null +++ b/tests/integration_test_lib/BaseFunctionalTest.h @@ -0,0 +1,160 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "TestNode.h" +#include +#include "cryptonote_core/Currency.h" +#include "inprocess_node/InProcessNode.h" + +#include "../../cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" + +#include "IWallet.h" +#include "INode.h" + +namespace Tests { + namespace Common { + + namespace po = boost::program_options; + class Semaphore{ + private: + std::mutex mtx; + std::condition_variable cv; + bool available; + + public: + Semaphore() : available(false) { } + + void notify() { + std::unique_lock lck(mtx); + available = true; + cv.notify_one(); + } + + void wait() { + std::unique_lock lck(mtx); + cv.wait(lck, [this](){ return available; }); + available = false; + } + + bool wait_for(const std::chrono::milliseconds& rel_time) { + std::unique_lock lck(mtx); + auto result = cv.wait_for(lck, rel_time, [this](){ return available; }); + available = false; + return result; + } + }; + + const uint16_t P2P_FIRST_PORT = 8000; + const uint16_t RPC_FIRST_PORT = 8200; + + + class BaseFunctionalTestConfig { + public: + BaseFunctionalTestConfig() {} + + void init(po::options_description& desc) { + desc.add_options() + ("daemon-dir,d", po::value()->default_value("."), "path to bytecoind.exe") + ("data-dir,n", po::value()->default_value("."), "path to daemon's data directory"); + } + + bool handleCommandLine(const po::variables_map& vm) { + if (vm.count("daemon-dir")) { + daemonDir = vm["daemon-dir"].as(); + } + + if (vm.count("data-dir")) { + dataDir = vm["data-dir"].as(); + } + return true; + } + + + protected: + friend class BaseFunctionalTest; + + std::string daemonDir; + std::string dataDir; + }; + + + + class BaseFunctionalTest : boost::noncopyable { + public: + BaseFunctionalTest(const cryptonote::Currency& currency, System::Dispatcher& d, const BaseFunctionalTestConfig& config) : m_currency(currency), m_dataDir(config.dataDir), m_daemonDir(config.daemonDir), m_dispatcher(d), inprocNode(nullptr) { + if (m_dataDir.empty()) m_dataDir = "."; + if (m_daemonDir.empty()) m_daemonDir = "."; + }; + + ~BaseFunctionalTest(); + + enum Topology { + Ring, + Line, + Star + }; + + private: + std::unique_ptr core; + std::unique_ptr> protocol; + std::unique_ptr>> p2pNode; + + protected: + std::vector< std::unique_ptr > nodeDaemons; + System::Dispatcher& m_dispatcher; + const cryptonote::Currency& m_currency; + std::unique_ptr inprocNode; + + void launchTestnet(size_t count, Topology t = Line); + void launchTestnetWithInprocNode(size_t count, Topology t = Line); + void stopTestnet(); + bool makeWallet(std::unique_ptr & wallet, std::unique_ptr& node, const std::string& password = "pass"); + bool mineBlock(std::unique_ptr& wallet); + bool mineBlock(); + bool startMining(size_t threads); + bool stopMining(); + + private: +#ifdef __linux__ + std::vector<__pid_t> pids; +#endif + + cryptonote::CurrencyBuilder currencyBuilder; + std::unique_ptr mainNode; + std::unique_ptr workingWallet; + + + std::string m_dataDir; + std::string m_daemonDir; + uint16_t m_mainDaemonRPCPort; + }; + } +} diff --git a/tests/integration_test_lib/CoreRpcSerialization.cpp b/tests/integration_test_lib/CoreRpcSerialization.cpp new file mode 100755 index 0000000000..b3ef07a483 --- /dev/null +++ b/tests/integration_test_lib/CoreRpcSerialization.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CoreRpcSerialization.h" + +namespace cryptonote { + +void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(value.miner_address, "miner_address"); + serializer(value.threads_count, "threads_count"); + serializer.endObject(); +} + +void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(value.status, "status"); + serializer.endObject(); +} + +void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer.endObject(); +} + +void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(value.status, "status"); + serializer.endObject(); +} + +void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer.endObject(); +} + +void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(value.status, "status"); + serializer.endObject(); +} + +} //namespace cryptonote diff --git a/tests/integration_test_lib/CoreRpcSerialization.h b/tests/integration_test_lib/CoreRpcSerialization.h new file mode 100755 index 0000000000..17cdb527c5 --- /dev/null +++ b/tests/integration_test_lib/CoreRpcSerialization.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "../../src/serialization/ISerializer.h" +#include "../../src/rpc/core_rpc_server_commands_defs.h" + +namespace cryptonote { + +void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer); + +void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer); + +void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, cryptonote::ISerializer& serializer); + +} //namespace cryptonote diff --git a/tests/integration_test_lib/Logger.cpp b/tests/integration_test_lib/Logger.cpp new file mode 100755 index 0000000000..71937558e1 --- /dev/null +++ b/tests/integration_test_lib/Logger.cpp @@ -0,0 +1,31 @@ +#include "Logger.h" + +#include +#include + +CLogger& CLogger::Instance() +{ + static CLogger theSingleInstance; + return theSingleInstance; +} +void CLogger::init(LOG_LEVEL log_lvl) +{ + level_names[TRACE] = "[ TRACE ]"; + level_names[DEBUG] = "[ DEBUG ]"; + level_names[_ERROR] = "[ ERROR ]"; + level_names[WARNING] = "[WARNING]"; + level_names[VERBOSE] = "[VERBOSE]"; + log_level = log_lvl; + indent = 0; +} +void CLogger::Log(const std::string & log_info, LOG_LEVEL log_lvl, int indent_inc) +{ + if(log_lvl>=log_level) + { + std::lock_guard lock(mutex); + if (indent_inc<0)indent+=indent_inc; + std::string sindent(std::max(0,indent),' '); + if (indent_inc>0)indent+=indent_inc; + (log_lvl +#include +#include +#include + +#ifdef _WIN32 +#define __FUNCTION_SIGNATURE__ __FUNCSIG__ +#else +#define __FUNCTION_SIGNATURE__ __PRETTY_FUNCTION__ +#endif + +#define LOG_(str , lvl , idnt) (CLogger::Instance().Log((std::string("")+(str)), (lvl), (idnt))) +#define LOG_VERBOSE(str) LOG_((str), (CLogger::VERBOSE),0 ) +#define LOG_TRACE(str) LOG_((str), (CLogger::TRACE),0 ) +#define LOG_DEBUG(str) LOG_((str), (CLogger::DEBUG),0 ) +#define LOG_ERROR(str) LOG_((str), (CLogger::_ERROR), 0 ) +#define LOG_WARNING(str) LOG_((str), (CLogger::WARNING),0 ) + +#define TO_STRING(param) boost::lexical_cast((param)) + + +class CLogger +{ +public: + enum LOG_LEVEL + { + VERBOSE, + DEBUG, + TRACE, + WARNING, + _ERROR + }; + static CLogger& Instance(); + void init(LOG_LEVEL log_lvl); + void Log(const std::string & log_info, LOG_LEVEL log_lvl, int indent_inc=0); + +private: + int indent; + std::map level_names; + LOG_LEVEL log_level; + std::mutex mutex; + CLogger(){}; + CLogger(const CLogger& root); + CLogger& operator=(const CLogger&); +}; + diff --git a/tests/integration_test_lib/RPCTestNode.cpp b/tests/integration_test_lib/RPCTestNode.cpp new file mode 100755 index 0000000000..b7ccb1b8b6 --- /dev/null +++ b/tests/integration_test_lib/RPCTestNode.cpp @@ -0,0 +1,208 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "RPCTestNode.h" + +#include +#include + +#include "rpc/core_rpc_server_commands_defs.h" +#include "node_rpc_proxy/NodeRpcProxy.h" + +#include "serialization/JsonOutputStreamSerializer.h" +#include "serialization/JsonInputStreamSerializer.h" +#include "storages/portable_storage_base.h" +#include "storages/portable_storage_template_helper.h" + +#include "../contrib/epee/include/net/jsonrpc_structs.h" + +#include +#include +#include +#include "HTTP/HttpParser.h" + +#include "CoreRpcSerialization.h" +#include "Logger.h" + +using namespace Tests; +using namespace cryptonote; +using namespace System; + +void RPCTestNode::prepareRequest(HttpRequest& httpReq, const std::string& method, const std::string& params){ + httpReq.setUrl(method); + httpReq.addHeader("Host", "127.0.0.1:" + boost::lexical_cast(m_rpcPort)); + httpReq.addHeader("Content-Type", "application/json-rpc"); + httpReq.setBody(params); +} + +void RPCTestNode::sendRequest(const HttpRequest& httpReq, HttpResponse& httpResp) { + TcpConnector connector(m_dispatcher, "127.0.0.1", m_rpcPort); + TcpConnection connection = connector.connect(); + TcpStreambuf streambuf(connection); + std::iostream connectionStream(&streambuf); + LOG_DEBUG("invoke rpc:" + httpReq.getMethod() + " " + httpReq.getBody()); + connectionStream << httpReq; + connectionStream.flush(); + HttpParser parser; + parser.receiveResponse(connectionStream, httpResp); +} + +bool RPCTestNode::startMining(size_t threadsCount, const std::string& address) { + LOG_DEBUG("startMining()"); + using namespace cryptonote; + COMMAND_RPC_START_MINING::request req; + COMMAND_RPC_START_MINING::response resp; + req.miner_address = address; + req.threads_count = threadsCount; + std::stringstream requestStream; + JsonOutputStreamSerializer enumerator; + enumerator(req, ""); + requestStream << enumerator; + HttpRequest httpReq; + prepareRequest(httpReq, "/start_mining", requestStream.str()); + HttpResponse httpResp; + sendRequest(httpReq, httpResp); + if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; + std::stringstream responseStream(httpResp.getBody()); + JsonInputStreamSerializer en(responseStream); + en(resp, ""); + if (resp.status != CORE_RPC_STATUS_OK) { + std::cout << "startMining() RPC call fail: " << resp.status; + return false; + } + + return true; +} + +bool RPCTestNode::submitBlock(const std::string& block) { + HttpRequest httpReq; + httpReq.setUrl("/json_rpc"); + httpReq.addHeader("Host", "127.0.0.1:" + boost::lexical_cast(m_rpcPort)); + httpReq.addHeader("Content-Type", "application/json-rpc"); + JsonValue request(cryptonote::JsonValue::OBJECT); + JsonValue jsonRpc; + jsonRpc = "2.0"; + request.insert("jsonrpc", jsonRpc); + JsonValue methodString; + methodString = "submitblock"; + request.insert("method", methodString); + JsonValue id; + id = "sync"; + request.insert("id", id); + JsonValue params(JsonValue::ARRAY); + JsonValue blockstr; + blockstr = block.c_str(); + params.pushBack(blockstr); + request.insert("params", params); + std::stringstream jsonOutputStream; + jsonOutputStream << request; + httpReq.setBody(jsonOutputStream.str()); + TcpConnector connector(m_dispatcher, "127.0.0.1", m_rpcPort); + TcpConnection connection = connector.connect(); + TcpStreambuf streambuf(connection); + std::iostream connectionStream(&streambuf); + LOG_DEBUG("invoke json-rpc: " + httpReq.getBody()); + connectionStream << httpReq; + connectionStream.flush(); + HttpResponse httpResp; + HttpParser parser; + parser.receiveResponse(connectionStream, httpResp); + connectionStream.flush(); + if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; + + epee::serialization::portable_storage ps; + if (!ps.load_from_json(httpResp.getBody())) { + LOG_ERROR("cannot parse response from daemon: " + httpResp.getBody()); + return false; + } + + epee::json_rpc::response jsonRpcResponse; + jsonRpcResponse.load(ps); + + if (jsonRpcResponse.error.code || jsonRpcResponse.error.message.size()) { + LOG_ERROR("RPC call of submit_block returned error: " + TO_STRING(jsonRpcResponse.error.code) + ", message: " + jsonRpcResponse.error.message); + return false; + } + + if (jsonRpcResponse.result.status != CORE_RPC_STATUS_OK) return false; + return true; +} + +bool RPCTestNode::stopMining() { + LOG_DEBUG("stopMining()"); + using namespace cryptonote; + COMMAND_RPC_STOP_MINING::request req; + COMMAND_RPC_STOP_MINING::response resp; + std::stringstream requestStream; + JsonOutputStreamSerializer enumerator; + enumerator(req, ""); + requestStream << enumerator; + HttpRequest httpReq; + prepareRequest(httpReq, "/stop_mining", requestStream.str()); + HttpResponse httpResp; + sendRequest(httpReq, httpResp); + if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; + std::stringstream responseStream(httpResp.getBody()); + JsonInputStreamSerializer en(responseStream); + en(resp, ""); + if (resp.status != CORE_RPC_STATUS_OK) { + std::cout << "stopMining() RPC call fail: " << resp.status; + return false; + } + + return true; +} + +bool RPCTestNode::makeINode(std::unique_ptr& node) { + node.reset(new cryptonote::NodeRpcProxy("127.0.0.1", m_rpcPort)); + node->init([&](std::error_code ec) { + if (ec) { + LOG_ERROR("init error: " + ec.message() + ':' + TO_STRING(ec.value())); + } else { + LOG_DEBUG("NodeRPCProxy on port " + TO_STRING(m_rpcPort) + " initialized"); + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); //for initial update + return true; +} + + +bool RPCTestNode::stopDaemon() { + LOG_DEBUG("stopDaemon()"); + using namespace cryptonote; + COMMAND_RPC_STOP_DAEMON::request req; + COMMAND_RPC_STOP_DAEMON::response resp; + std::stringstream requestStream; + JsonOutputStreamSerializer enumerator; + enumerator(req, ""); + requestStream << enumerator; + HttpRequest httpReq; + prepareRequest(httpReq, "/stop_daemon", requestStream.str()); + HttpResponse httpResp; + sendRequest(httpReq, httpResp); + if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; + std::stringstream responseStream(httpResp.getBody()); + JsonInputStreamSerializer en(responseStream); + en(resp, ""); + if (resp.status != CORE_RPC_STATUS_OK) { + std::cout << "stopDaemon() RPC call fail: " << resp.status; + return false; + } + + return true; +} \ No newline at end of file diff --git a/tests/integration_test_lib/RPCTestNode.h b/tests/integration_test_lib/RPCTestNode.h new file mode 100755 index 0000000000..706c1bd81c --- /dev/null +++ b/tests/integration_test_lib/RPCTestNode.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include "HTTP/HttpRequest.h" +#include "HTTP/HttpResponse.h" + + +#include "TestNode.h" + +using namespace cryptonote; + +namespace Tests { + class RPCTestNode : public Common::TestNode { + public: + RPCTestNode(uint16_t port, System::Dispatcher& d) : m_rpcPort(port), m_dispatcher(d) {} + virtual bool startMining(size_t threadsCount, const std::string& address) override; + virtual bool stopMining() override; + virtual bool stopDaemon() override; + virtual bool submitBlock(const std::string& block) override; + virtual bool makeINode(std::unique_ptr& node) override; + virtual ~RPCTestNode() { } + + private: + void prepareRequest(HttpRequest& httpReq, const std::string& method, const std::string& params); + void sendRequest(const HttpRequest& httpReq, HttpResponse& httpResp); + + uint16_t m_rpcPort; + System::Dispatcher& m_dispatcher; + }; +} diff --git a/tests/integration_test_lib/TestNode.h b/tests/integration_test_lib/TestNode.h new file mode 100755 index 0000000000..5e6f1582e9 --- /dev/null +++ b/tests/integration_test_lib/TestNode.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + + +namespace Tests { + namespace Common { + + class TestNode { + public: + virtual bool startMining(size_t threadsCount, const std::string& address) = 0; + virtual bool stopMining() = 0; + virtual bool stopDaemon() = 0; + virtual bool submitBlock(const std::string& block) = 0; + virtual bool makeINode(std::unique_ptr& node) = 0; + virtual ~TestNode() { } + }; + } +} \ No newline at end of file diff --git a/tests/integration_tests/main.cpp b/tests/integration_tests/main.cpp new file mode 100755 index 0000000000..0084748ee0 --- /dev/null +++ b/tests/integration_tests/main.cpp @@ -0,0 +1,826 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include + +#include "boost/lexical_cast.hpp" +#include +#include "cryptonote_core/cryptonote_format_utils.h" +#include "string_tools.h" + +#include "../integration_test_lib/BaseFunctionalTest.h" +#include "../integration_test_lib/Logger.h" + +#ifndef CHECK_AND_ASSERT_MES +#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_ERROR(message); return fail_ret_val;};}while(0) +#endif + +#ifndef CHECK_AND_ASSERT_MES_NON_FATAL +#define CHECK_AND_ASSERT_MES_NON_FATAL(expr, fail_ret_val, message) do{if(!(expr)) {LOG_WARNING(message); };}while(0) +#endif + + + + + +namespace po = boost::program_options; +namespace { +class ConfigurationError : public std::runtime_error { +public: + ConfigurationError(const char* desc) : std::runtime_error(desc) {} +}; + +struct Configuration : public Tests::Common::BaseFunctionalTestConfig { + Configuration() : desc("Allowed options") { + init(); + } + + bool handleCommandLine(int argc, char** argv) { + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + BaseFunctionalTestConfig::handleCommandLine(vm); + if (vm.count("help")) { + std::cout << desc << std::endl; + return false; + } + + if (vm.count("test-type")) { + auto testType = vm["test-type"].as(); + if (testType<1 || testType>6) throw ConfigurationError("Incorrect test type."); + _testType = (TestType)testType; + } else throw ConfigurationError("Missing test type."); + return true; + } + + enum TestType { + WALLET2WALLET = 1, + BLOCKTHRUDAEMONS = 3, + RELAYBLOCKTHRUDAEMONS = 4, + TESTPOOLANDINPROCNODE = 5, + TESTPOOLDELETION = 6 + } _testType; + + po::options_description desc; + +protected: + void init() { + desc.add_options() + ("help,h", "produce this help message and exit") + ("test-type,t", po::value()->default_value(1), "test type:\r\n1 - wallet to wallet test,\r\n3 - block thru daemons test\r\n4 - relay block thru daemons\r\n5 - test tx pool and inproc node\r\n6 - deleting tx from pool due to timeout"); + BaseFunctionalTestConfig::init(desc); + } +}; +} + + +class SimpleTest : public Tests::Common::BaseFunctionalTest { +public: + + SimpleTest(const cryptonote::Currency& currency, System::Dispatcher& system, const Configuration& config) : BaseFunctionalTest(currency, system, config) {} + + class WaitForActualGrowObserver : public CryptoNote::IWalletObserver { + Tests::Common::Semaphore& m_GotActual; + + uint64_t m_lastFunds; + + public: + WaitForActualGrowObserver(Tests::Common::Semaphore& GotActual, uint64_t lastFunds) : m_GotActual(GotActual), m_lastFunds(lastFunds) { } + + virtual void actualBalanceUpdated(uint64_t actualBalance) { + if (m_lastFunds < actualBalance) { + m_GotActual.notify(); + } + m_lastFunds = actualBalance; + } + }; + + class WaitForActualDwindleObserver : public CryptoNote::IWalletObserver { + Tests::Common::Semaphore& m_GotActual; + + uint64_t m_lastFunds; + + public: + WaitForActualDwindleObserver(Tests::Common::Semaphore& GotActual, uint64_t lastFunds) : m_GotActual(GotActual), m_lastFunds(lastFunds) { } + + virtual void actualBalanceUpdated(uint64_t actualBalance) { + if (m_lastFunds > actualBalance) { + m_GotActual.notify(); + } + m_lastFunds = actualBalance; + } + }; + + class WaitForPendingGrowObserver : public CryptoNote::IWalletObserver { + Tests::Common::Semaphore& m_GotActual; + + uint64_t m_lastFunds; + + public: + WaitForPendingGrowObserver(Tests::Common::Semaphore& GotActual, uint64_t lastFunds) : m_GotActual(GotActual), m_lastFunds(lastFunds) { } + + virtual void pendingBalanceUpdated(uint64_t pendingBalance) { + if (m_lastFunds < pendingBalance) { + m_GotActual.notify(); + } + m_lastFunds = pendingBalance; + } + }; + + class WaitForConfirmationObserver : public CryptoNote::IWalletObserver { + Tests::Common::Semaphore& m_confirmed; + + std::function m_pred; + public: + WaitForConfirmationObserver(Tests::Common::Semaphore& confirmed, std::function pred) : m_confirmed(confirmed), m_pred(pred) { } + + virtual void pendingBalanceUpdated(uint64_t pendingBalance) override { + if (m_pred(pendingBalance)) m_confirmed.notify(); + } + }; + + class WaitForSendCompletedObserver : public CryptoNote::IWalletObserver { + Tests::Common::Semaphore& m_Sent; + std::error_code& m_error; + CryptoNote::TransactionId& m_transactionId; + + public: + WaitForSendCompletedObserver(Tests::Common::Semaphore& Sent, CryptoNote::TransactionId& transactionId, std::error_code& error) : m_Sent(Sent), m_transactionId(transactionId), m_error(error) { } + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { + m_error = result; + m_transactionId = transactionId; + m_Sent.notify(); + } + }; + + class WaitForExternalTransactionObserver : public CryptoNote::IWalletObserver { + public: + WaitForExternalTransactionObserver() { } + std::promise promise; + + virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override { + promise.set_value(transactionId); + } + + }; + + + class WaitForTransactionUpdated : public CryptoNote::IWalletObserver { + public: + WaitForTransactionUpdated() {} + std::promise promise; + + virtual void transactionUpdated(CryptoNote::TransactionId transactionId) override { + if (expectindTxId == transactionId) { + promise.set_value(); + } + } + + CryptoNote::TransactionId expectindTxId; + }; + + + bool perform1() { + using namespace Tests::Common; + using namespace CryptoNote; + using namespace cryptonote; + const uint64_t FEE = 1000000; + launchTestnet(2); + LOG_TRACE("STEP 1 PASSED"); + + std::unique_ptr node1; + std::unique_ptr node2; + + nodeDaemons.front()->makeINode(node1); + nodeDaemons.front()->makeINode(node2); + + std::unique_ptr wallet1; + std::unique_ptr wallet2; + + makeWallet(wallet1, node1); + makeWallet(wallet2, node2); + + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + LOG_TRACE("STEP 2 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + CHECK_AND_ASSERT_MES(mineBlock(wallet1), false, "can't mine block on wallet 1"); + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + LOG_TRACE("STEP 3 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + Semaphore wallet1GotActual; + WaitForConfirmationObserver wallet1ActualGrown(wallet1GotActual, [](uint64_t pending)->bool {return pending == 0; }); + wallet1->addObserver(&wallet1ActualGrown); + CHECK_AND_ASSERT_MES(startMining(1) , false, "startMining(1) failed"); + wallet1GotActual.wait(); + LOG_TRACE("STEP 4 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + CHECK_AND_ASSERT_MES(stopMining() , false, "stopMining() failed"); + auto wallet1ActualBeforeTransaction = wallet1->actualBalance(); + auto wallet1PendingBeforeTransaction = wallet1->pendingBalance(); + auto wallet2ActualBeforeTransaction = wallet2->actualBalance(); + auto wallet2PendingBeforeTransaction = wallet2->pendingBalance(); + CryptoNote::Transfer tr; + tr.address = wallet2->getAddress(); + tr.amount = wallet1ActualBeforeTransaction / 2; + TransactionId sendTransaction; + std::error_code result; + Semaphore moneySent; + WaitForSendCompletedObserver sco1(moneySent, sendTransaction, result); + Semaphore w2GotPending; + WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction); + wallet2->addObserver(&pgo1); + wallet1->addObserver(&sco1); + wallet1->sendTransaction(tr, FEE); + CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed"); + moneySent.wait(); + w2GotPending.wait(); + CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed"); + auto wallet2PendingAfterTransaction = wallet2->pendingBalance(); + auto wallet1PendingAfterTransaction = wallet1->pendingBalance(); + auto w2PendingDiff = wallet2PendingAfterTransaction - wallet2PendingBeforeTransaction; + auto wallet1ActualAfterTransaction = wallet1->actualBalance(); + + LOG_TRACE("STEP 5 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + CHECK_AND_ASSERT_MES((tr.amount == w2PendingDiff), false, "STEP 6 ASSERTION 1 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount " + m_currency.formatAmount(w2PendingDiff)); + CHECK_AND_ASSERT_MES((wallet1ActualBeforeTransaction - wallet1PendingAfterTransaction - wallet1ActualAfterTransaction - tr.amount - FEE == 0), false, + "STEP 6 ASSERTION 2 FAILED\r\n wallet1 Actual Before Transaction doesn't match wallet1 total After Transaction + Transfered amount + Fee " + + m_currency.formatAmount(wallet1ActualBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1PendingAfterTransaction) + " + " + m_currency.formatAmount(wallet1ActualAfterTransaction) + " + " + m_currency.formatAmount(tr.amount) + " + " + m_currency.formatAmount(FEE)); + LOG_TRACE("STEP 6 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed"); + Semaphore confirmed2; + Semaphore confirmed1; + WaitForConfirmationObserver confirmationObserver2(confirmed2, [](uint64_t pending)->bool {return pending == 0; }); + WaitForConfirmationObserver confirmationObserver1(confirmed1, [](uint64_t pending)->bool {return pending == 0; }); + wallet2->addObserver(&confirmationObserver2); + wallet1->addObserver(&confirmationObserver1); + if (wallet2->pendingBalance() != 0) confirmed2.wait(); + if (wallet1->pendingBalance() != 0) confirmed1.wait(); + CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed"); + auto wallet1ActualAfterTransactionAndConfirmation = wallet1->actualBalance(); + auto wallet2ActualAfterTransactionAndConfirmation = wallet2->actualBalance(); + auto w2ActualDiff = wallet2ActualAfterTransactionAndConfirmation - wallet2ActualBeforeTransaction; + auto w1ActualDiff = wallet1ActualBeforeTransaction - wallet1ActualAfterTransactionAndConfirmation; + CHECK_AND_ASSERT_MES((tr.amount == w2ActualDiff), false, "STEP 7 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match confirmed recieved amount " + m_currency.formatAmount(w2ActualDiff)); + CHECK_AND_ASSERT_MES((w1ActualDiff - tr.amount - FEE == 0), false, + "STEP 7 FAILED\r\n wallet1 Actual Before Transaction doesn't match wallet1 Actual After Transaction + Transfered amount + Fee " + + m_currency.formatAmount(wallet1ActualBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1ActualAfterTransactionAndConfirmation) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE)); + LOG_TRACE("STEP 7 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + wallet1->removeObserver(&wallet1ActualGrown); + wallet2->removeObserver(&pgo1); + wallet1->removeObserver(&sco1); + wallet2->removeObserver(&confirmationObserver2); + wallet1->removeObserver(&confirmationObserver1); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + return true; + } + + class WaitForBlockchainHeightChangeObserver : public CryptoNote::INodeObserver { + Tests::Common::Semaphore& m_changed; + public: + WaitForBlockchainHeightChangeObserver(Tests::Common::Semaphore& changed) : m_changed(changed) { } + virtual void lastKnownBlockHeightUpdated(uint64_t height) override { + m_changed.notify(); + } + }; + + class CallbackHeightChangeObserver : public CryptoNote::INodeObserver { + std::function m_callback; + public: + CallbackHeightChangeObserver(std::function callback) : m_callback(callback) { } + virtual void lastKnownBlockHeightUpdated(uint64_t height) override { + m_callback(height); + } + }; + + bool perform2(size_t blocksCount = 10) + { + using namespace Tests::Common; + launchTestnet(3, Line); + std::this_thread::sleep_for(std::chrono::milliseconds(10000)); + LOG_TRACE("STEP 1 PASSED"); + mineBlock(); + mineBlock(); + LOG_TRACE("STEP 2 PASSED"); + std::unique_ptr localNode; + std::unique_ptr remoteNode; + + nodeDaemons.front()->makeINode(localNode); + nodeDaemons.back()->makeINode(remoteNode); + + std::unique_ptr wallet; + makeWallet(wallet, localNode); + + LOG_TRACE("STEP 3 PASSED"); + Semaphore blockMined; + Semaphore blockArrivedToRemote; + + WaitForBlockchainHeightChangeObserver localHCO(blockMined); + WaitForBlockchainHeightChangeObserver remoteHCO(blockArrivedToRemote); + + localNode->addObserver(&localHCO); + remoteNode->addObserver(&remoteHCO); + for (size_t blockNumber = 0; blockNumber < blocksCount; ++blockNumber) { + nodeDaemons.front()->startMining(1, wallet->getAddress()); + blockMined.wait(); + CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(5000)), false, "block propagation too slow >5000ms."); + nodeDaemons.front()->stopMining(); + LOG_TRACE("STEP 4 STAGE " + TO_STRING(blockNumber+1) + " of " + TO_STRING(blocksCount)+" PASSED"); + } + + return true; + } + + bool perform4() { + using namespace CryptoNote; + using namespace Tests::Common; + launchTestnet(3, Star); + LOG_TRACE("STEP 1 PASSED"); + + std::unique_ptr hopNode; + std::unique_ptr localNode; + std::unique_ptr remoteNode; + + nodeDaemons[0]->makeINode(hopNode); + nodeDaemons[1]->makeINode(localNode); + nodeDaemons[2]->makeINode(remoteNode); + + LOG_TRACE("STEP 2 PASSED"); + + + std::string test_block1_hex = + "0101b392d79f05a742885cb01d11b7b36fb8bf14616d42cd3d8c1429a224df41afa81b86b8a3a84e" + "d8c33f010b01ff0108c0a62d02cc353782cbe4c6067bd30510f11d1f2993f2c7fed37239f299ffe3" + "f96f135675c096b102023e8d4b2c22d73f91d0d9f8e0e12c8df24e5917f00d0b2dd99786c5bb0e5b" + "300580bbb021022764ae61c084db07e7cd83c55e9c833f42b1d422e1008220fdb4acc726b94ea980" + "88debe01023330c2b7dc4840f478066370ae48b148ce8dd010c59f6ecc08598682d32f07d080a0d9" + "e61d02bcf35dc40ead54a614174774e60d8f5d0e46272c70bc7e70f205f7ccef25c34980b09dc2df" + "01026ddcf1aed901f018453fd9352a01d5a44067d271ca403b4cd799d9832076daa280f092cbdd08" + "021f613eab32b76ed03f6a796de7a5c92009ea9f9b9e3299ec91df7657cd694e5580c089a9a2f50f" + "02f545046885a297ba63a2c7b305a74fdb741129cc367330661c1363e0bb0f0d0b2101acf052dcbe" + "407bc34df1b7fffc17f0bfb0ffc23002e2b6de48a210df6f78bf1400"; + std::string test_block2_hex = + "0101b492d79f05456231a956ed3a8c1ac0bfe8efc1bb5d522d8474e566b051919ddea0ceab478a74" + "e35210010c01ff0207c0e41202b2d7e697c6e2e894f9e98262c278235720b39f3a149774cb58cb52" + "e5dde21601c09fab0302abffefad3afab42ca1ce2f7dccfa6942256f31387b307becd43571cfe22a" + "10688084af5f02e2ab32d9b8fb8ced4bf4a81de0f48c23dc575076e8d233a3532d28f36e79035380" + "a0d9e61d020c10664fe1ca35418733fa32ae2deadd4bf7ed982bb5d11ba98a7940a73e161580b09d" + "c2df010266c3bfa27436b480a217a2fe06df714f4d2094ec1a0ced3bac2d96881972e28a80f092cb" + "dd0802f31e9ac25fb8afd1d9d964331242a94f023c3188db5e532b5a9c800a843a3ebc80c089a9a2" + "f50f0206244fcc73941c3da62ea6d62d679bedb311fc530d149099bdfd04c59cd507a121019d4b74" + "f09454ccfdd6ca44b8c5f73c6805ea08dbe6a71769b058e158b2d4df5100"; + std::string test_block3_hex = + "0101b492d79f051f6fe6d9f7c14c0d5e16ba82d9ea68e4e6d6f30726854d45330aeb2fae5c1cd3fb" + "7f4352010d01ff0308ffae350220f4c1c7631ecf4247688c376665df2b9dd935af6e4c027c9cddcb" + "400fefec7380a4e803029e05ef9b3295e178d0f3199fca420f909f04fdab09b97c14290c8a913e42" + "19c68087a70e02693641fefb1a6da81c2308370f349ef5e4adab792ae06b5da989ae3f1b7a13ca80" + "d293ad0302c14d721ed8da5c98f108ef17c326737765857ddfa0b705fd4483cfa7ffeaad51808cee" + "891a02f5a8e2ac24d6a9f789e5514de520c3ac28387788e130e22c4250b7d1be47460380b09dc2df" + "0102cc3b2f894b416f3e09afae0395fc01cc2ec9763dff72839944e60055049ea37d80f092cbdd08" + "02772df06a2cd92c174815ae1572799430ea01e903796f6a763648c7b350151ce580c089a9a2f50f" + "0211c7bea98edba4fad6d3f19b330a676b8fb0391f7a99f45542e7cf52d39d6c632101e0370c5c79" + "e99d772b41e0569bc41e1ebde2e563cdb7f5bdd23984899fad103200"; + std::string test_block4_hex = + "0101b492d79f0537da79424e1cc69d16aadf174dcf443947f8027695a5d1e30b2be4f5aa71904194" + "47fe54010e01ff0407fffc1a0278eb82c9ea2f1e998906cec55caf26e347224c3391fb0aa2213bc1" + "5eec4dacc580bbb021024ce32a63614269f43f698644c98fd9b7a11694dc69fd5126f6f6735ba6c5" + "98dd808c8d9e0202d539ead46faf6d786964dd5106004612eb8d64778ad4fe8befa8c63e4d666f92" + "808cee891a021a6c6669298dcc1c86af887804f128123d95a6d96b5884db97cfc96fa9ad018e80b0" + "9dc2df0102dd3b9bbfef1eddeef8c406de9c0c4fc469c8069c910541252491df5a482fd5e380f092" + "cbdd0802176a4cb411309761b7f50b0f495e99cc55cbaae70011d3c901e409a8a938f1b680c089a9" + "a2f50f02bb232a77911350a1315de0b3de447142390f97e5ef25ecc1bf5837a8972b4b5e2101ef54" + "5c318e38cfdd92362340fab6ec6630e4134b93cfd01db4d9a42fa945fdef00"; + + Semaphore blockArrivedToRemote; + + WaitForBlockchainHeightChangeObserver remoteHCO(blockArrivedToRemote); + + std::chrono::steady_clock::time_point localAdded; + std::chrono::steady_clock::time_point hoplAdded; + std::chrono::steady_clock::time_point remoteAdded; + std::chrono::steady_clock::time_point submitInvokingStart; + std::chrono::steady_clock::time_point submitInvoked; + + //auto height = localNode->getLastKnownBlockHeight(); + + CallbackHeightChangeObserver CHCOLocal([&localAdded](uint64_t new_height){localAdded = std::chrono::steady_clock::now(); }); + CallbackHeightChangeObserver CHCOHop([&hoplAdded](uint64_t new_height){hoplAdded = std::chrono::steady_clock::now(); }); + CallbackHeightChangeObserver CHCORemote([&remoteAdded](uint64_t new_height){remoteAdded = std::chrono::steady_clock::now(); }); + + localNode->addObserver(&CHCOLocal); + hopNode->addObserver(&CHCOHop); + remoteNode->addObserver(&CHCORemote); + remoteNode->addObserver(&remoteHCO); + + LOG_TRACE("test_block1"); + submitInvokingStart = std::chrono::steady_clock::now(); + if (!nodeDaemons[1]->submitBlock(test_block1_hex)) return false; + submitInvoked = std::chrono::steady_clock::now(); + CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 1 propagation too slow >10000ms."); + LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast(submitInvoked - submitInvokingStart).count()) + " ms"); + LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast(localAdded - submitInvoked).count()) + " ms"); + LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast(hoplAdded - localAdded).count()) + " ms"); + LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - hoplAdded).count()) + " ms"); + LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - localAdded).count()) + " ms"); + + LOG_TRACE("test_block2"); + submitInvokingStart = std::chrono::steady_clock::now(); + if (!nodeDaemons[1]->submitBlock(test_block2_hex)) return false; + submitInvoked = std::chrono::steady_clock::now(); + CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 2 propagation too slow >10000ms."); + LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast(submitInvoked - submitInvokingStart).count()) + " ms"); + LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast(localAdded - submitInvoked).count()) + " ms"); + LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast(hoplAdded - localAdded).count()) + " ms"); + LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - hoplAdded).count()) + " ms"); + LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - localAdded).count()) + " ms"); + + LOG_TRACE("test_block3"); + submitInvokingStart = std::chrono::steady_clock::now(); + if (!nodeDaemons[1]->submitBlock(test_block3_hex)) return false; + submitInvoked = std::chrono::steady_clock::now(); + CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 3 propagation too slow >10000ms."); + LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast(submitInvoked - submitInvokingStart).count()) + " ms"); + LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast(localAdded - submitInvoked).count()) + " ms"); + LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast(hoplAdded - localAdded).count()) + " ms"); + LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - hoplAdded).count()) + " ms"); + LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - localAdded).count()) + " ms"); + + LOG_TRACE("test_block4"); + submitInvokingStart = std::chrono::steady_clock::now(); + if (!nodeDaemons[1]->submitBlock(test_block4_hex)) return false; + submitInvoked = std::chrono::steady_clock::now(); + CHECK_AND_ASSERT_MES(blockArrivedToRemote.wait_for(std::chrono::milliseconds(10000)), false, "block 4 propagation too slow >10000ms."); + LOG_TRACE("submitBlock() invocation takes: " + TO_STRING(std::chrono::duration_cast(submitInvoked - submitInvokingStart).count()) + " ms"); + LOG_TRACE("HeightChangedCallback() since submit : " + TO_STRING(std::chrono::duration_cast(localAdded - submitInvoked).count()) + " ms"); + LOG_TRACE("Local -> HopNode: " + TO_STRING(std::chrono::duration_cast(hoplAdded - localAdded).count()) + " ms"); + LOG_TRACE("HopNode -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - hoplAdded).count()) + " ms"); + LOG_TRACE("Local -> Remote: " + TO_STRING(std::chrono::duration_cast(remoteAdded - localAdded).count()) + " ms"); + + localNode.release(); + remoteNode.release(); + hopNode.release(); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + return true; + } + + + bool perform5() { + using namespace Tests::Common; + using namespace CryptoNote; + using namespace cryptonote; + const uint64_t FEE = 1000000; + launchTestnetWithInprocNode(2); + + std::unique_ptr node1; + nodeDaemons.front()->makeINode(node1); + + while (node1->getLastLocalBlockHeight() != inprocNode->getLastLocalBlockHeight()) { + LOG_TRACE("Syncing..."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + LOG_TRACE("STEP 1 PASSED"); + + std::unique_ptr wallet1; + std::unique_ptr wallet2; + + makeWallet(wallet1, node1); + makeWallet(wallet2, inprocNode); + + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + LOG_TRACE("STEP 2 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + CHECK_AND_ASSERT_MES(mineBlock(wallet1), false, "can't mine block on wallet 1"); + + LOG_TRACE("STEP 3 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + Semaphore wallet1GotActual; + WaitForConfirmationObserver wallet1ActualGrown(wallet1GotActual, [&wallet1](uint64_t actual)->bool {return wallet1->pendingBalance() == actual; }); + wallet1->addObserver(&wallet1ActualGrown); + CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed"); + wallet1GotActual.wait(); + + LOG_TRACE("STEP 4 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed"); + + auto wallet1ActualBeforeTransaction = wallet1->actualBalance(); + auto wallet1PendingBeforeTransaction = wallet1->pendingBalance(); + auto wallet2ActualBeforeTransaction = wallet2->actualBalance(); + auto wallet2PendingBeforeTransaction = wallet2->pendingBalance(); + CryptoNote::Transfer tr; + tr.address = wallet2->getAddress(); + tr.amount = wallet1ActualBeforeTransaction / 2; + TransactionId sendTransaction; + std::error_code result; + Semaphore w2GotPending; + WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction); + wallet2->addObserver(&pgo1); + + WaitForExternalTransactionObserver poolTxWaiter; + auto future = poolTxWaiter.promise.get_future(); + wallet2->addObserver(&poolTxWaiter); + + wallet1->sendTransaction(tr, FEE); + + auto txId = future.get(); + w2GotPending.wait(); + + wallet2->removeObserver(&poolTxWaiter); + CryptoNote::TransactionInfo txInfo; + wallet2->getTransaction(txId, txInfo); + + auto wallet2PendingAfterTransaction = wallet2->pendingBalance(); + auto wallet1PendingAfterTransaction = wallet1->pendingBalance(); + auto w2PendingDiff = wallet2PendingAfterTransaction - wallet2PendingBeforeTransaction; + auto w1PendingDiff = wallet1PendingBeforeTransaction - wallet1PendingAfterTransaction; + CHECK_AND_ASSERT_MES((txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT), false, "STEP 5 ASSERTION 1 FAILED\r\n Transaction blockHeight differs unconfirmed_tx_height"); + CHECK_AND_ASSERT_MES((tr.amount == txInfo.totalAmount), false, "STEP 5 ASSERTION 2 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount from pool transaction " + m_currency.formatAmount(txInfo.totalAmount)); + CHECK_AND_ASSERT_MES((tr.amount == w2PendingDiff), false, "STEP 5 ASSERTION 3 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount " + m_currency.formatAmount(w2PendingDiff)); + CHECK_AND_ASSERT_MES((w1PendingDiff - tr.amount - FEE == 0), false, + "STEP 5 ASSERTION 4 FAILED\r\n wallet1 Pending Before Transaction doesn't match wallet1 Pending After Transaction + Transfered amount + Fee " + + m_currency.formatAmount(wallet1PendingBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1PendingAfterTransaction) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE)); + + LOG_TRACE("STEP 5 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + WaitForTransactionUpdated trasactionConfirmationObserver; + trasactionConfirmationObserver.expectindTxId = txId; + + wallet2->addObserver(&trasactionConfirmationObserver); + auto txUpdated = trasactionConfirmationObserver.promise.get_future(); + + CHECK_AND_ASSERT_MES(mineBlock(), false, "mineBlock() failed"); + CHECK_AND_ASSERT_MES(mineBlock(), false, "mineBlock() failed"); + txUpdated.get(); + wallet2->getTransaction(txId, txInfo); + wallet2->removeObserver(&trasactionConfirmationObserver); + + CHECK_AND_ASSERT_MES(txInfo.blockHeight <= inprocNode->getLastLocalBlockHeight(), false, "STEP 6 ASSERTION FAILED tx height confirmation failed"); + LOG_TRACE("STEP 6 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + + CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed"); + Semaphore confirmed2; + Semaphore confirmed1; + WaitForConfirmationObserver confirmationObserver2(confirmed2, [&wallet2](uint64_t actual)->bool {return wallet2->pendingBalance() == actual; }); + WaitForConfirmationObserver confirmationObserver1(confirmed1, [&wallet1](uint64_t actual)->bool {return wallet1->pendingBalance() == actual; }); + wallet2->addObserver(&confirmationObserver2); + wallet1->addObserver(&confirmationObserver1); + if (wallet2->pendingBalance() != wallet2->actualBalance()) confirmed2.wait(); + if (wallet1->pendingBalance() != wallet1->actualBalance()) confirmed1.wait(); + CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed"); + auto wallet1ActualAfterTransactionAndConfirmation = wallet1->actualBalance(); + auto wallet2ActualAfterTransactionAndConfirmation = wallet2->actualBalance(); + auto w2ActualDiff = wallet2ActualAfterTransactionAndConfirmation - wallet2ActualBeforeTransaction; + auto w1ActualDiff = wallet1ActualBeforeTransaction - wallet1ActualAfterTransactionAndConfirmation; + CHECK_AND_ASSERT_MES((tr.amount == w2ActualDiff), false, "STEP 7 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match confirmed recieved amount " + m_currency.formatAmount(w2ActualDiff)); + CHECK_AND_ASSERT_MES((w1ActualDiff - tr.amount - FEE == 0), false, + "STEP 7 FAILED\r\n wallet1 Actual Before Transaction doesn't match wallet1 Actual After Transaction + Transfered amount + Fee " + + m_currency.formatAmount(wallet1ActualBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1ActualAfterTransactionAndConfirmation) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE)); + LOG_TRACE("STEP 7 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + wallet1->removeObserver(&wallet1ActualGrown); + wallet2->removeObserver(&pgo1); + wallet2->removeObserver(&confirmationObserver2); + wallet1->removeObserver(&confirmationObserver1); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + return true; + } + + + bool perform6() { + using namespace Tests::Common; + using namespace CryptoNote; + using namespace cryptonote; + const uint64_t FEE = 1000000; + launchTestnetWithInprocNode(2); + + std::unique_ptr node1; + nodeDaemons.front()->makeINode(node1); + + while (node1->getLastLocalBlockHeight() != inprocNode->getLastLocalBlockHeight()) { + LOG_TRACE("Syncing..."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + LOG_TRACE("STEP 1 PASSED"); + + std::unique_ptr wallet1; + std::unique_ptr wallet2; + + makeWallet(wallet1, node1); + makeWallet(wallet2, inprocNode); + + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + CHECK_AND_ASSERT_MES(mineBlock(), false, "can't mine block"); + LOG_TRACE("STEP 2 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + CHECK_AND_ASSERT_MES(mineBlock(wallet1), false, "can't mine block on wallet 1"); + + LOG_TRACE("STEP 3 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + Semaphore wallet1GotActual; + WaitForConfirmationObserver wallet1ActualGrown(wallet1GotActual, [&wallet1](uint64_t actual)->bool {return wallet1->pendingBalance() == actual; }); + wallet1->addObserver(&wallet1ActualGrown); + CHECK_AND_ASSERT_MES(startMining(1), false, "startMining(1) failed"); + wallet1GotActual.wait(); + + LOG_TRACE("STEP 4 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + CHECK_AND_ASSERT_MES(stopMining(), false, "stopMining() failed"); + + auto wallet1ActualBeforeTransaction = wallet1->actualBalance(); + auto wallet1PendingBeforeTransaction = wallet1->pendingBalance(); + auto wallet2ActualBeforeTransaction = wallet2->actualBalance(); + auto wallet2PendingBeforeTransaction = wallet2->pendingBalance(); + CryptoNote::Transfer tr; + tr.address = wallet2->getAddress(); + tr.amount = wallet1ActualBeforeTransaction / 2; + TransactionId sendTransaction; + std::error_code result; + Semaphore w2GotPending; + WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction); + wallet2->addObserver(&pgo1); + + WaitForExternalTransactionObserver poolTxWaiter; + auto future = poolTxWaiter.promise.get_future(); + wallet2->addObserver(&poolTxWaiter); + + wallet1->sendTransaction(tr, FEE); + + auto txId = future.get(); + w2GotPending.wait(); + + wallet2->removeObserver(&poolTxWaiter); + CryptoNote::TransactionInfo txInfo; + wallet2->getTransaction(txId, txInfo); + + auto wallet2PendingAfterTransaction = wallet2->pendingBalance(); + auto wallet1PendingAfterTransaction = wallet1->pendingBalance(); + auto w2PendingDiff = wallet2PendingAfterTransaction - wallet2PendingBeforeTransaction; + auto w1PendingDiff = wallet1PendingBeforeTransaction - wallet1PendingAfterTransaction; + CHECK_AND_ASSERT_MES((txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT), false, "STEP 5 ASSERTION 1 FAILED\r\n Transaction blockHeight differs unconfirmed_tx_height"); + CHECK_AND_ASSERT_MES((tr.amount == txInfo.totalAmount), false, "STEP 5 ASSERTION 2 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount from pool transaction " + m_currency.formatAmount(txInfo.totalAmount)); + CHECK_AND_ASSERT_MES((tr.amount == w2PendingDiff), false, "STEP 5 ASSERTION 3 FAILED\r\n Transfered amount " + m_currency.formatAmount(tr.amount) + " doesn't match recieved amount " + m_currency.formatAmount(w2PendingDiff)); + CHECK_AND_ASSERT_MES((w1PendingDiff - tr.amount - FEE == 0), false, + "STEP 5 ASSERTION 4 FAILED\r\n wallet1 Pending Before Transaction doesn't match wallet1 Pending After Transaction + Transfered amount + Fee " + + m_currency.formatAmount(wallet1PendingBeforeTransaction) + " <> " + m_currency.formatAmount(wallet1PendingAfterTransaction) + "+" + m_currency.formatAmount(tr.amount) + "+" + m_currency.formatAmount(FEE)); + + LOG_TRACE("STEP 5 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + + + + + WaitForTransactionUpdated trasactionDeletionObserver; + trasactionDeletionObserver.expectindTxId = txId; + + wallet2->addObserver(&trasactionDeletionObserver); + auto txUpdated = trasactionDeletionObserver.promise.get_future(); + + txUpdated.get(); + wallet2->getTransaction(txId, txInfo); + wallet2->removeObserver(&trasactionDeletionObserver); + + + CHECK_AND_ASSERT_MES(txInfo.state == TransactionState::Deleted, false, "STEP 6 ASSERTION 1 FAILED tx not deleted"); + CHECK_AND_ASSERT_MES(wallet2PendingBeforeTransaction == wallet2->pendingBalance(), false, "STEP 6 ASSERTION 2 FAILED current pending balance <> pending balance before transaction"); + + LOG_TRACE("STEP 6 PASSED"); + LOG_DEBUG("Wallet1 pending: " + m_currency.formatAmount(wallet1->pendingBalance())); + LOG_DEBUG("Wallet1 actual: " + m_currency.formatAmount(wallet1->actualBalance())); + LOG_DEBUG("Wallet2 pending: " + m_currency.formatAmount(wallet2->pendingBalance())); + LOG_DEBUG("Wallet2 actual: " + m_currency.formatAmount(wallet2->actualBalance())); + + wallet1->removeObserver(&wallet1ActualGrown); + wallet2->removeObserver(&pgo1); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + return true; + } + + +}; + + + +int main(int argc, char** argv) { + CLogger::Instance().init(CLogger::DEBUG); + try { + ::Configuration config; + if (!config.handleCommandLine(argc, argv)) { + return 0; //help message requested or so + } + + cryptonote::Currency currency = cryptonote::CurrencyBuilder().testnet(true).currency(); + if (config._testType == Configuration::TESTPOOLDELETION) { + currency = cryptonote::CurrencyBuilder().testnet(true).mempoolTxLiveTime(60).currency(); + } + + System::Dispatcher system; + SimpleTest t(currency, system, config); + bool success = false; + switch (config._testType) + { + case Configuration::WALLET2WALLET: success = t.perform1(); break; + case Configuration::BLOCKTHRUDAEMONS: success = t.perform2(); break; + case Configuration::RELAYBLOCKTHRUDAEMONS: success = t.perform4(); break; + case Configuration::TESTPOOLANDINPROCNODE: success = t.perform5(); break; + case Configuration::TESTPOOLDELETION: success = t.perform6(); break; + default: throw std::runtime_error("Oh snap! Serious crap happened..."); + }; + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + if (!success) { + LOG_ERROR("TEST FAILED"); + return 1; + } + LOG_TRACE("TEST PASSED"); + } + catch (::ConfigurationError& ex) { + std::cerr << "Configuration error: " << ex.what() << std::endl; + return 1; + } + catch (std::exception& ex) { + LOG_ERROR("Fatal error: " + std::string(ex.what())); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/tests/transfers_tests/globals.h b/tests/transfers_tests/globals.h new file mode 100644 index 0000000000..74db915284 --- /dev/null +++ b/tests/transfers_tests/globals.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "../integration_test_lib/BaseFunctionalTest.h" +#include "../integration_test_lib/Logger.h" +#include "gtest/gtest.h" + +extern System::Dispatcher globalSystem; +extern cryptonote::Currency currency; +extern Tests::Common::BaseFunctionalTestConfig config; + +class TransfersTest : + public Tests::Common::BaseFunctionalTest, + public ::testing::Test { + +public: + TransfersTest() : BaseFunctionalTest(currency, globalSystem, config) { + } +}; + diff --git a/tests/transfers_tests/main.cpp b/tests/transfers_tests/main.cpp new file mode 100644 index 0000000000..876204ae3c --- /dev/null +++ b/tests/transfers_tests/main.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" +#include "globals.h" + +System::Dispatcher globalSystem; +cryptonote::Currency currency = cryptonote::CurrencyBuilder().testnet(true).currency(); +Tests::Common::BaseFunctionalTestConfig config; + + +namespace po = boost::program_options; + +int main(int argc, char** argv) { + CLogger::Instance().init(CLogger::DEBUG); + + po::options_description desc; + po::variables_map vm; + + config.init(desc); + po::store(po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(), vm); + po::notify(vm); + config.handleCommandLine(vm); + + try { + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); + + } catch (std::exception& ex) { + LOG_ERROR("Fatal error: " + std::string(ex.what())); + return 1; + } +} \ No newline at end of file diff --git a/tests/transfers_tests/tests.cpp b/tests/transfers_tests/tests.cpp new file mode 100644 index 0000000000..e7d35d3fd1 --- /dev/null +++ b/tests/transfers_tests/tests.cpp @@ -0,0 +1,520 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "globals.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/TransactionApi.h" + +#include "transfers/TransfersSynchronizer.h" +#include "transfers/BlockchainSynchronizer.h" + +#include +#include +#include +#include + +using namespace CryptoNote; + + +template +std::string bin2str(const std::array& data) { + std::string result; + result.resize(size * 2 + 1); + + for (size_t i = 0; i < size; ++i) { + sprintf(&result[i * 2], "%02x", data[i]); + } + + return result; +} + +class WalletObserver : public IWalletObserver { +public: + virtual void actualBalanceUpdated(uint64_t actualBalance) { + std::cout << "Actual balance updated = " << currency.formatAmount(actualBalance) << std::endl; + m_actualBalance = actualBalance; + m_sem.notify(); + } + + virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) { + std::cout << "Transaction sended, result = " << result << std::endl; + } + + std::atomic m_actualBalance; + Tests::Common::Semaphore m_sem; +}; + +class TransactionConsumer : public IBlockchainConsumer { +public: + + TransactionConsumer() { + syncStart.timestamp = time(nullptr); + syncStart.height = 0; + } + + virtual SynchronizationStart getSyncStart() override { + return syncStart; + } + + virtual void onBlockchainDetach(uint64_t height) override { + std::lock_guard lk(m_mutex); + auto it = m_transactions.lower_bound(height); + m_transactions.erase(it, m_transactions.end()); + } + + virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override { + std::lock_guard lk(m_mutex); + for(size_t i = 0; i < count; ++i) { + for (const auto& tx : blocks[i].transactions) { + m_transactions[startHeight + i].insert(tx->getTransactionHash()); + } + } + m_cv.notify_all(); + return true; + } + + bool waitForTransaction(const Hash& txHash) { + std::unique_lock lk(m_mutex); + while (!hasTransaction(txHash)) { + m_cv.wait(lk); + } + return true; + } + + std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { + //stub + return std::error_code(); + } + + void getKnownPoolTxIds(std::vector& ids) override { + //stub + } + +private: + + bool hasTransaction(const Hash& txHash) { + for (const auto& kv : m_transactions) { + if (kv.second.count(txHash) > 0) + return true; + } + return false; + } + + std::mutex m_mutex; + std::condition_variable m_cv; + std::map> m_transactions; + SynchronizationStart syncStart; +}; + +class TransfersObserver : public ITransfersObserver { +public: + virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override { + { + std::lock_guard lk(m_mutex); + m_transfers.push_back(transactionHash); + + auto address = epee::string_tools::pod_to_hex(object->getAddress().spendPublicKey); + LOG_DEBUG("Transfer to " + address); + } + m_cv.notify_all(); + } + + bool waitTransfer() { + std::unique_lock lk(m_mutex); + size_t prevSize = m_transfers.size(); + + while (m_transfers.size() == prevSize) { + m_cv.wait_for(lk, std::chrono::seconds(10)); + } + + return true; + } + + bool waitTransactionTransfer(const Hash& transactionHash) { + std::unique_lock lk(m_mutex); + + while (!hasTransaction(transactionHash)) { + m_cv.wait_for(lk, std::chrono::seconds(10)); + } + + return true; + } + +private: + + bool hasTransaction(const Hash& transactionHash) { + return std::find(m_transfers.begin(), m_transfers.end(), transactionHash) != m_transfers.end(); + } + + + std::mutex m_mutex; + std::condition_variable m_cv; + std::vector m_transfers; +}; + + +class AccountGroup { +public: + + AccountGroup(ITransfersSynchronizer& sync) : + m_sync(sync) {} + + void generateAccounts(size_t count) { + cryptonote::account_base acc; + + while (count--) { + acc.generate(); + + AccountSubscription sub; + sub.keys = reinterpret_cast(acc.get_keys()); + sub.syncStart.timestamp = acc.get_createtime(); + sub.syncStart.height = 0; + sub.transactionSpendableAge = 5; + + m_accounts.push_back(sub); + m_addresses.push_back(currency.accountAddressAsString(acc)); + } + } + + void subscribeAll() { + m_observers.reset(new TransfersObserver[m_accounts.size()]); + for (size_t i = 0; i < m_accounts.size(); ++i) { + m_sync.addSubscription(m_accounts[i]).addObserver(&m_observers[i]); + } + } + + std::vector getAddresses() { + std::vector addr; + for (const auto& acc : m_accounts) { + addr.push_back(acc.keys.address); + } + return addr; + } + + ITransfersContainer& getTransfers(size_t idx) { + return m_sync.getSubscription(m_accounts[idx].keys.address)->getContainer(); + } + + std::vector m_accounts; + std::vector m_addresses; + ITransfersSynchronizer& m_sync; + std::unique_ptr m_observers; +}; + +class MultisignatureTest : public TransfersTest { +public: + + virtual void SetUp() override { + launchTestnet(2); + } +}; + +TEST_F(TransfersTest, base) { + + uint64_t TRANSFER_AMOUNT; + currency.parseAmount("500000.5", TRANSFER_AMOUNT); + + launchTestnet(2); + + std::unique_ptr node1; + std::unique_ptr node2; + + nodeDaemons[0]->makeINode(node1); + nodeDaemons[1]->makeINode(node2); + + cryptonote::account_base dstAcc; + dstAcc.generate(); + + AccountKeys dstKeys = reinterpret_cast(dstAcc.get_keys()); + + BlockchainSynchronizer blockSync(*node2.get(), currency.genesisBlockHash()); + TransfersSyncronizer transferSync(currency, blockSync, *node2.get()); + TransfersObserver transferObserver; + WalletObserver walletObserver; + + AccountSubscription sub; + sub.syncStart.timestamp = 0; + sub.syncStart.height = 0; + sub.keys = dstKeys; + sub.transactionSpendableAge = 5; + + ITransfersSubscription& transferSub = transferSync.addSubscription(sub); + ITransfersContainer& transferContainer = transferSub.getContainer(); + transferSub.addObserver(&transferObserver); + + std::unique_ptr wallet1; + + makeWallet(wallet1, node1); + mineBlock(wallet1); + + wallet1->addObserver(&walletObserver); + + startMining(1); + + while (wallet1->actualBalance() < TRANSFER_AMOUNT) { + walletObserver.m_sem.wait(); + } + + // start syncing and wait for a transfer + auto waitFuture = std::async(std::launch::async, [&transferObserver] { return transferObserver.waitTransfer(); }); + blockSync.start(); + + Transfer transfer; + transfer.address = currency.accountAddressAsString(dstAcc); + transfer.amount = TRANSFER_AMOUNT; + + wallet1->sendTransaction(transfer, currency.minimumFee()); + + auto result = waitFuture.get(); + + std::cout << "Received transfer: " << currency.formatAmount(transferContainer.balance(ITransfersContainer::IncludeAll)) << std::endl; + + ASSERT_EQ(TRANSFER_AMOUNT, transferContainer.balance(ITransfersContainer::IncludeAll)); + + auto BACK_TRANSFER = TRANSFER_AMOUNT / 2; + + stopMining(); + blockSync.stop(); +} + + +std::unique_ptr createTransferToMultisignature( + ITransfersContainer& tc, // money source + uint64_t amount, + uint64_t fee, + const AccountKeys& senderKeys, + const std::vector& recipients, + uint32_t requiredSignatures) { + + std::vector transfers; + tc.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked | ITransfersContainer::IncludeStateSoftLocked); + + auto tx = createTransaction(); + + std::vector> inputs; + + uint64_t foundMoney = 0; + + for (const auto& t : transfers) { + TransactionTypes::InputKeyInfo info; + + info.amount = t.amount; + + TransactionTypes::GlobalOutput globalOut; + globalOut.outputIndex = t.globalOutputIndex; + globalOut.targetKey = t.outputKey; + info.outputs.push_back(globalOut); + + info.realOutput.outputInTransaction = t.outputInTransaction; + info.realOutput.transactionIndex = 0; + info.realOutput.transactionPublicKey = t.transactionPublicKey; + + KeyPair kp; + tx->addInput(senderKeys, info, kp); + + inputs.push_back(std::make_pair(info, kp)); + + foundMoney += info.amount; + + if (foundMoney >= amount + fee) { + break; + } + } + + // output to receiver + tx->addOutput(amount, recipients, requiredSignatures); + + // change + uint64_t change = foundMoney - amount - fee; + if (change) { + tx->addOutput(change, senderKeys.address); + } + + for (size_t inputIdx = 0; inputIdx < inputs.size(); ++inputIdx) { + tx->signInputKey(inputIdx, inputs[inputIdx].first, inputs[inputIdx].second); + } + + return tx; +} + +std::error_code submitTransaction(INode& node, ITransactionReader& tx) { + auto data = tx.getTransactionData(); + + cryptonote::blobdata txblob(data.data(), data.data() + data.size()); + cryptonote::Transaction outTx; + cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + + LOG_DEBUG("Submitting transaction " + bin2str(tx.getTransactionHash())); + + std::promise result; + node.relayTransaction(outTx, [&result](std::error_code ec) { result.set_value(ec); }); + auto err = result.get_future().get(); + + if (err) { + LOG_DEBUG("Error: " + err.message()); + } else { + LOG_DEBUG("Submitted successfully"); + } + + return err; +} + + +std::unique_ptr createTransferFromMultisignature( + AccountGroup& consilium, const AccountAddress& receiver, const Hash& txHash, uint64_t amount, uint64_t fee) { + + auto& tc = consilium.getTransfers(0); + + std::vector transfers = tc.getTransactionOutputs(txHash, + ITransfersContainer::IncludeTypeMultisignature | + ITransfersContainer::IncludeStateSoftLocked | + ITransfersContainer::IncludeStateUnlocked); + + const TransactionOutputInformation& out = transfers[0]; + + auto tx = createTransaction(); + + TransactionTypes::InputMultisignature msigInput; + + msigInput.amount = out.amount; + msigInput.outputIndex = out.globalOutputIndex; + msigInput.signatures = out.requiredSignatures; + + tx->addInput(msigInput); + tx->addOutput(amount, receiver); + + uint64_t change = out.amount - amount - fee; + + tx->addOutput(change, consilium.getAddresses(), out.requiredSignatures); + + for (size_t i = 0; i < out.requiredSignatures; ++i) { + tx->signInputMultisignature(0, out.transactionPublicKey, out.outputInTransaction, consilium.m_accounts[i].keys); + } + + return tx; +} + +TEST_F(MultisignatureTest, createMulitisignatureTransaction) { + + std::unique_ptr node1; + std::unique_ptr node2; + + nodeDaemons[0]->makeINode(node1); + nodeDaemons[1]->makeINode(node2); + + BlockchainSynchronizer blockSync(*node2.get(), currency.genesisBlockHash()); + TransfersSyncronizer transferSync(currency, blockSync, *node2.get()); + + // add transaction collector + TransactionConsumer txConsumer; + blockSync.addConsumer(&txConsumer); + + AccountGroup sender(transferSync); + AccountGroup consilium(transferSync); + + sender.generateAccounts(1); + sender.subscribeAll(); + + consilium.generateAccounts(3); + consilium.subscribeAll(); + + auto senderSubscription = transferSync.getSubscription(sender.m_accounts[0].keys.address); + auto& senderContainer = senderSubscription->getContainer(); + + blockSync.start(); + + // start mining for sender + nodeDaemons[0]->startMining(1, sender.m_addresses[0]); + + // wait for incoming transfer + while (senderContainer.balance() == 0) { + sender.m_observers[0].waitTransfer(); + + auto unlockedBalance = senderContainer.balance(ITransfersContainer::IncludeAllUnlocked | ITransfersContainer::IncludeStateSoftLocked); + auto totalBalance = senderContainer.balance(ITransfersContainer::IncludeAll); + + LOG_DEBUG("Balance: " + currency.formatAmount(unlockedBalance) + " (" + currency.formatAmount(totalBalance) + ")"); + } + + uint64_t fundBalance = 0; + + for (int iteration = 1; iteration <= 3; ++iteration) { + LOG_DEBUG("***** Iteration " + std::to_string(iteration) + " ******"); + + auto sendAmount = senderContainer.balance() / 2; + + LOG_DEBUG("Creating transaction with amount = " + currency.formatAmount(sendAmount)); + + auto tx2msig = createTransferToMultisignature( + senderContainer, sendAmount, currency.minimumFee(), sender.m_accounts[0].keys, consilium.getAddresses(), 3); + + auto txHash = tx2msig->getTransactionHash(); + auto err = submitTransaction(*node2, *tx2msig); + ASSERT_EQ(std::error_code(), err); + + LOG_DEBUG("Waiting for transaction to be included in block..."); + txConsumer.waitForTransaction(txHash); + + LOG_DEBUG("Transaction in blockchain, waiting for observers to receive transaction..."); + + uint64_t expectedFundBalance = fundBalance + sendAmount; + + // wait for consilium to receive the transfer + for (size_t i = 0; i < consilium.m_accounts.size(); ++i) { + auto& observer = consilium.m_observers[i]; + observer.waitTransactionTransfer(txHash); + + auto sub = transferSync.getSubscription(consilium.m_accounts[i].keys.address); + ASSERT_TRUE(sub != nullptr); + ASSERT_EQ(expectedFundBalance, sub->getContainer().balance( + ITransfersContainer::IncludeStateAll | ITransfersContainer::IncludeTypeMultisignature)); + } + + LOG_DEBUG("Creating transaction to spend multisignature output"); + + uint64_t returnAmount = sendAmount / 2; + + auto spendMsigTx = createTransferFromMultisignature( + consilium, sender.m_accounts[0].keys.address, txHash, returnAmount, currency.minimumFee()); + + auto spendMsigTxHash = spendMsigTx->getTransactionHash(); + + err = submitTransaction(*node2, *spendMsigTx); + + ASSERT_EQ(std::error_code(), err); + + LOG_DEBUG("Waiting for transaction to be included in block..."); + txConsumer.waitForTransaction(spendMsigTxHash); + + LOG_DEBUG("Checking left balances"); + // check that outputs were correctly marked as spent + uint64_t leftAmount = expectedFundBalance - returnAmount - currency.minimumFee(); + for (size_t i = 0; i < consilium.m_accounts.size(); ++i) { + auto& observer = consilium.m_observers[i]; + observer.waitTransactionTransfer(spendMsigTxHash); + ASSERT_EQ(leftAmount, consilium.getTransfers(i).balance(ITransfersContainer::IncludeAll)); + } + + fundBalance = leftAmount; + } + + stopMining(); + blockSync.stop(); + + LOG_DEBUG("Success!!!"); +} diff --git a/tests/unit_tests/EventWaiter.cpp b/tests/unit_tests/EventWaiter.cpp new file mode 100644 index 0000000000..3fedc762f1 --- /dev/null +++ b/tests/unit_tests/EventWaiter.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "EventWaiter.h" + +void EventWaiter::notify() { + std::unique_lock lck(mtx); + available = true; + cv.notify_all(); +} + +void EventWaiter::wait() { + std::unique_lock lck(mtx); + cv.wait(lck, [this]() { return available; }); + available = false; +} + +bool EventWaiter::wait_for(const std::chrono::milliseconds& rel_time) { + std::unique_lock lck(mtx); + auto result = cv.wait_for(lck, rel_time, [this]() { return available; }); + available = false; + return result; +} \ No newline at end of file diff --git a/tests/unit_tests/EventWaiter.h b/tests/unit_tests/EventWaiter.h new file mode 100644 index 0000000000..a1a8f14538 --- /dev/null +++ b/tests/unit_tests/EventWaiter.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include + +#pragma once + +class EventWaiter { +private: + std::mutex mtx; + std::condition_variable cv; + bool available; + +public: + EventWaiter() : available(false) {} + + void notify(); + void wait(); + bool wait_for(const std::chrono::milliseconds& rel_time); +}; \ No newline at end of file diff --git a/tests/unit_tests/ICoreStub.cpp b/tests/unit_tests/ICoreStub.cpp new file mode 100755 index 0000000000..248249a3fc --- /dev/null +++ b/tests/unit_tests/ICoreStub.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ICoreStub.h" + +bool ICoreStub::addObserver(cryptonote::ICoreObserver* observer) { + return true; +} + +bool ICoreStub::removeObserver(cryptonote::ICoreObserver* observer) { + return true; +} + +bool ICoreStub::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { + height = topHeight; + top_id = topId; + return topResult; +} + +bool ICoreStub::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, + uint64_t& total_height, uint64_t& start_height, size_t max_count) +{ + return true; +} + +bool ICoreStub::find_blockchain_supplement(const std::list& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp) { + return true; +} + +bool ICoreStub::get_random_outs_for_amounts(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) { + res = randomOuts; + return randomOutsResult; +} + +bool ICoreStub::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { + std::copy(globalIndices.begin(), globalIndices.end(), std::back_inserter(indexs)); + return globalIndicesResult; +} + +cryptonote::i_cryptonote_protocol* ICoreStub::get_protocol() { + return nullptr; +} + +bool ICoreStub::handle_incoming_tx(cryptonote::blobdata const& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block) { + return true; +} + +void ICoreStub::set_blockchain_top(uint64_t height, const crypto::hash& top_id, bool result) { + topHeight = height; + topId = top_id; + topResult = result; +} + +void ICoreStub::set_outputs_gindexs(const std::vector& indexs, bool result) { + globalIndices.clear(); + std::copy(indexs.begin(), indexs.end(), std::back_inserter(globalIndices)); + globalIndicesResult = result; +} + +void ICoreStub::set_random_outs(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result) { + randomOuts = resp; + randomOutsResult = result; +} + +bool ICoreStub::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { + return true; +} + +bool ICoreStub::queryBlocks(const std::list& block_ids, uint64_t timestamp, + uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) { + //stub + return true; +} + +bool ICoreStub::getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) { + //stub + return true; +} + diff --git a/tests/unit_tests/ICoreStub.h b/tests/unit_tests/ICoreStub.h new file mode 100755 index 0000000000..70bcb179bf --- /dev/null +++ b/tests/unit_tests/ICoreStub.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/ICore.h" +#include "cryptonote_core/ICoreObserver.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/core_rpc_server_commands_defs.h" + +class ICoreStub: public cryptonote::ICore { +public: + ICoreStub() : topHeight(0), topResult(false), globalIndicesResult(false), randomOutsResult(false) {}; + + virtual bool addObserver(cryptonote::ICoreObserver* observer); + virtual bool removeObserver(cryptonote::ICoreObserver* observer); + virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id); + virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, + uint64_t& total_height, uint64_t& start_height, size_t max_count); + virtual bool find_blockchain_supplement(const std::list& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); + virtual bool get_random_outs_for_amounts(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); + virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); + virtual cryptonote::i_cryptonote_protocol* get_protocol(); + virtual bool handle_incoming_tx(cryptonote::blobdata const& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block); + virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; + virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, + uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); + + virtual bool getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) override; + + void set_blockchain_top(uint64_t height, const crypto::hash& top_id, bool result); + void set_outputs_gindexs(const std::vector& indexs, bool result); + void set_random_outs(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result); + +private: + uint64_t topHeight; + crypto::hash topId; + bool topResult; + + std::vector globalIndices; + bool globalIndicesResult; + + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response randomOuts; + bool randomOutsResult; +}; diff --git a/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp b/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp new file mode 100644 index 0000000000..6211389ad7 --- /dev/null +++ b/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ICryptonoteProtocolQueryStub.h" + +bool ICryptonoteProtocolQueryStub::addObserver(cryptonote::ICryptonoteProtocolObserver* observer) { + return false; +} + +bool ICryptonoteProtocolQueryStub::removeObserver(cryptonote::ICryptonoteProtocolObserver* observer) { + return false; +} + +uint64_t ICryptonoteProtocolQueryStub::getObservedHeight() const { + return observedHeight; +} + +size_t ICryptonoteProtocolQueryStub::getPeerCount() const { + return peers; +} + +void ICryptonoteProtocolQueryStub::setPeerCount(uint32_t count) { + peers = count; +} + +void ICryptonoteProtocolQueryStub::setObservedHeight(uint64_t height) { + observedHeight = height; +} diff --git a/tests/unit_tests/ICryptonoteProtocolQueryStub.h b/tests/unit_tests/ICryptonoteProtocolQueryStub.h new file mode 100644 index 0000000000..88ae43e75d --- /dev/null +++ b/tests/unit_tests/ICryptonoteProtocolQueryStub.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" +#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" + +class ICryptonoteProtocolQueryStub: public cryptonote::ICryptonoteProtocolQuery { +public: + ICryptonoteProtocolQueryStub() : peers(0), observedHeight(0) {} + + virtual bool addObserver(cryptonote::ICryptonoteProtocolObserver* observer); + virtual bool removeObserver(cryptonote::ICryptonoteProtocolObserver* observer); + virtual uint64_t getObservedHeight() const; + virtual size_t getPeerCount() const; + void setPeerCount(uint32_t count); + void setObservedHeight(uint64_t height); + +private: + size_t peers; + uint64_t observedHeight; +}; + diff --git a/tests/unit_tests/INodeStubs.cpp b/tests/unit_tests/INodeStubs.cpp index 6f83aeb1fb..9f07f91741 100644 --- a/tests/unit_tests/INodeStubs.cpp +++ b/tests/unit_tests/INodeStubs.cpp @@ -23,20 +23,66 @@ #include #include #include +#include #include "crypto/crypto.h" +namespace { + +class ContextCounterHolder { +public: + ContextCounterHolder(CryptoNote::WalletAsyncContextCounter& shutdowner) : m_shutdowner(shutdowner) {} + ~ContextCounterHolder() { m_shutdowner.delAsyncContext(); } + +private: + CryptoNote::WalletAsyncContextCounter& m_shutdowner; +}; + +} + + +void INodeDummyStub::updateObservers() { + observerManager.notify(&CryptoNote::INodeObserver::lastKnownBlockHeightUpdated, getLastKnownBlockHeight()); +} + +bool INodeDummyStub::addObserver(CryptoNote::INodeObserver* observer) { + return observerManager.add(observer); +} + +bool INodeDummyStub::removeObserver(CryptoNote::INodeObserver* observer) { + return observerManager.remove(observer); +} + void INodeTrivialRefreshStub::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { - std::thread task(&INodeTrivialRefreshStub::doGetNewBlocks, this, knownBlockIds, std::ref(newBlocks), std::ref(startHeight), callback); + m_asyncCounter.addAsyncContext(); + std::thread task(std::bind(&INodeTrivialRefreshStub::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), std::ref(startHeight), callback)); task.detach(); } void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + auto& blockchain = m_blockchainGenerator.getBlockchain(); - startHeight = m_lastHeight; + std::vector::iterator start = blockchain.end(); + + for (const auto& id : knownBlockIds) { + start = std::find_if(blockchain.begin(), blockchain.end(), + [&id](cryptonote::Block& block) { return get_block_hash(block) == id; }); + if (start != blockchain.end()) + break; + } + + if (start == blockchain.end()) { + callback(std::error_code()); + return; + } + + m_lastHeight = std::distance(blockchain.begin(), start); + startHeight = m_lastHeight; for (; m_lastHeight < blockchain.size(); ++m_lastHeight) { @@ -53,51 +99,84 @@ void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockI } newBlocks.push_back(e); + + if (newBlocks.size() >= m_getMaxBlocks) { + break; + } } - m_lastHeight = blockchain.size() - 1; + m_lastHeight = startHeight + newBlocks.size(); + // m_lastHeight = startHeight + blockchain.size() - 1; callback(std::error_code()); } void INodeTrivialRefreshStub::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { + m_asyncCounter.addAsyncContext(); + std::unique_lock lock(m_multiWalletLock); + calls_getTransactionOutsGlobalIndices.push_back(transactionHash); std::thread task(&INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices, this, transactionHash, std::ref(outsGlobalIndices), callback); task.detach(); } void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { - outsGlobalIndices.resize(20); //random + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + + cryptonote::Transaction tx; + + if (m_blockchainGenerator.getTransactionByHash(transactionHash, tx)) { + outsGlobalIndices.resize(tx.vout.size()); + } else { + outsGlobalIndices.resize(20); //random + } + callback(std::error_code()); } void INodeTrivialRefreshStub::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { + m_asyncCounter.addAsyncContext(); std::thread task(&INodeTrivialRefreshStub::doRelayTransaction, this, transaction, callback); task.detach(); } void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + if (m_nextTxError) { - callback(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); m_nextTxError = false; + callback(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + return; + } + + if (m_nextTxToPool) { + m_nextTxToPool = false; + m_blockchainGenerator.putTxToPool(transaction); + callback(std::error_code()); return; } + m_blockchainGenerator.addTxToBlockchain(transaction); callback(std::error_code()); } void INodeTrivialRefreshStub::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { + m_asyncCounter.addAsyncContext(); std::thread task(&INodeTrivialRefreshStub::doGetRandomOutsByAmounts, this, amounts, outsCount, std::ref(result), callback); task.detach(); } void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); for (uint64_t amount: amounts) { cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount out; @@ -120,12 +199,39 @@ void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amo callback(std::error_code()); } +void INodeTrivialRefreshStub::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, + std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + + auto resultHolder = std::make_shared>(); + + getNewBlocks(std::move(knownBlockIds), *resultHolder, startHeight, [resultHolder, callback, &startHeight, &newBlocks](std::error_code ec) + { + if (ec == std::error_code()) { + for (const auto& item : *resultHolder) { + CryptoNote::BlockCompleteEntry entry; + cryptonote::Block block; + + cryptonote::parse_and_validate_block_from_blob(item.block, block); + + entry.blockHash = cryptonote::get_block_hash(block); + entry.block = item.block; + entry.txs = std::move(item.txs); + + newBlocks.push_back(std::move(entry)); + } + } + callback(ec); + }); + +} + + void INodeTrivialRefreshStub::startAlternativeChain(uint64_t height) { std::vector& blockchain = m_blockchainGenerator.getBlockchain(); assert(height < blockchain.size()); - assert(height > m_lastHeight); + //assert(height > m_lastHeight); auto it = blockchain.begin(); std::advance(it, height); @@ -139,3 +245,42 @@ void INodeTrivialRefreshStub::setNextTransactionError() { m_nextTxError = true; } + +void INodeTrivialRefreshStub::setNextTransactionToPool() { + m_nextTxToPool = true; +} + +void INodeTrivialRefreshStub::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) +{ + m_asyncCounter.addAsyncContext(); + std::thread task( + std::bind(&INodeTrivialRefreshStub::doGetPoolSymmetricDifference, this, + std::move(known_pool_tx_ids), + known_block_id, + std::ref(is_bc_actual), + std::ref(new_txs), + std::ref(deleted_tx_ids), + callback + ) + ); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetPoolSymmetricDifference(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) +{ + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + + m_blockchainGenerator.getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids); + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::includeTransactionsFromPoolToBlock() { + m_blockchainGenerator.putTxPoolToBlockchain(); +} + +INodeTrivialRefreshStub::~INodeTrivialRefreshStub() { + m_asyncCounter.waitAsyncContextsFinish(); +} diff --git a/tests/unit_tests/INodeStubs.h b/tests/unit_tests/INodeStubs.h index 60ce379e98..ed688fd146 100644 --- a/tests/unit_tests/INodeStubs.h +++ b/tests/unit_tests/INodeStubs.h @@ -18,16 +18,20 @@ #pragma once #include +#include #include "INode.h" #include "cryptonote_core/cryptonote_basic.h" #include "TestBlockchainGenerator.h" +#include "common/ObserverManager.h" +#include "wallet/WalletAsyncContextCounter.h" + class INodeDummyStub : public CryptoNote::INode { public: - virtual bool addObserver(CryptoNote::INodeObserver* observer) { return true; }; - virtual bool removeObserver(CryptoNote::INodeObserver* observer) { return true; }; + virtual bool addObserver(CryptoNote::INodeObserver* observer) override; + virtual bool removeObserver(CryptoNote::INodeObserver* observer) override; virtual void init(const CryptoNote::INode::Callback& callback) {callback(std::error_code());}; virtual bool shutdown() { return true; }; @@ -35,18 +39,29 @@ class INodeDummyStub : public CryptoNote::INode virtual size_t getPeerCount() const { return 0; }; virtual uint64_t getLastLocalBlockHeight() const { return 0; }; virtual uint64_t getLastKnownBlockHeight() const { return 0; }; + virtual uint64_t getLastLocalBlockTimestamp() const override { return 0; } virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) {callback(std::error_code());}; virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) {callback(std::error_code());}; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) {callback(std::error_code());}; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); }; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { is_bc_actual = true; callback(std::error_code()); }; + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { callback(std::error_code()); }; + + void updateObservers(); + + tools::ObserverManager observerManager; + }; class INodeTrivialRefreshStub : public INodeDummyStub { public: - INodeTrivialRefreshStub(TestBlockchainGenerator& generator) : m_lastHeight(1), m_blockchainGenerator(generator), m_nextTxError(false) {}; + INodeTrivialRefreshStub(TestBlockchainGenerator& generator) : + m_lastHeight(1), m_blockchainGenerator(generator), m_nextTxError(false), m_getMaxBlocks(std::numeric_limits::max()), m_nextTxToPool(false) {}; + + void setGetNewBlocksLimit(size_t maxBlocks) { m_getMaxBlocks = maxBlocks; } virtual uint64_t getLastLocalBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; }; virtual uint64_t getLastKnownBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; }; @@ -56,17 +71,32 @@ class INodeTrivialRefreshStub : public INodeDummyStub virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; virtual void startAlternativeChain(uint64_t height); - virtual void setNextTransactionError(); + void setNextTransactionError(); + void setNextTransactionToPool(); + void includeTransactionsFromPoolToBlock(); + + std::vector calls_getTransactionOutsGlobalIndices; + + virtual ~INodeTrivialRefreshStub(); private: void doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); void doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); void doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + void doGetPoolSymmetricDifference(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback); + size_t m_getMaxBlocks; uint64_t m_lastHeight; TestBlockchainGenerator& m_blockchainGenerator; bool m_nextTxError; + bool m_nextTxToPool; + std::mutex m_multiWalletLock; + CryptoNote::WalletAsyncContextCounter m_asyncCounter; }; diff --git a/tests/unit_tests/TestBlockchainGenerator.cpp b/tests/unit_tests/TestBlockchainGenerator.cpp index 92d5e61909..43ccda0564 100644 --- a/tests/unit_tests/TestBlockchainGenerator.cpp +++ b/tests/unit_tests/TestBlockchainGenerator.cpp @@ -15,9 +15,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include #include "TestBlockchainGenerator.h" +#include +#include + +#include "cryptonote_core/cryptonote_format_utils.h" + #include "../performance_tests/multi_tx_test_base.h" class TransactionForAddressCreator : public multi_tx_test_base<5> @@ -33,9 +37,19 @@ class TransactionForAddressCreator : public multi_tx_test_base<5> void generate(const cryptonote::AccountPublicAddress& address, cryptonote::Transaction& tx) { - cryptonote::tx_destination_entry destination(this->m_source_amount, address); std::vector destinations; - destinations.push_back(destination); + + cryptonote::decompose_amount_into_digits(this->m_source_amount, 0, + [&](uint64_t chunk) { destinations.push_back(cryptonote::tx_destination_entry(chunk, address)); }, + [&](uint64_t a_dust) { destinations.push_back(cryptonote::tx_destination_entry(a_dust, address)); }); + + cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0); + } + + void generateSingleOutputTx(const cryptonote::AccountPublicAddress& address, uint64_t amount, cryptonote::Transaction& tx) { + std::vector destinations; + + destinations.push_back(cryptonote::tx_destination_entry(amount, address)); cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0); } @@ -48,6 +62,7 @@ TestBlockchainGenerator::TestBlockchainGenerator(const cryptonote::Currency& cur { miner_acc.generate(); addGenesisBlock(); + addMiningBlock(); } std::vector& TestBlockchainGenerator::getBlockchain() @@ -65,19 +80,21 @@ bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cry return true; } -void TestBlockchainGenerator::addGenesisBlock() -{ - cryptonote::Block genesis; - uint64_t timestamp = time(NULL); +void TestBlockchainGenerator::addGenesisBlock() { + std::vector bsizes; + generator.addBlock(m_currency.genesisBlock(), 0, 0, bsizes, 0); + m_blockchain.push_back(m_currency.genesisBlock()); +} - generator.constructBlock(genesis, miner_acc, timestamp); - m_blockchain.push_back(genesis); +void TestBlockchainGenerator::addMiningBlock() { + cryptonote::Block block; + uint64_t timestamp = time(NULL); + generator.constructBlock(block, miner_acc, timestamp); + m_blockchain.push_back(block); } void TestBlockchainGenerator::generateEmptyBlocks(size_t count) { - addGenesisBlock(); - for (size_t i = 0; i < count; ++i) { cryptonote::Block& prev_block = m_blockchain.back(); @@ -111,7 +128,26 @@ bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::Account cryptonote::Transaction tx; creator.generate(address, tx); - crypto::hash txHash = cryptonote::get_transaction_hash(tx); + addToBlockchain(tx); + + return true; +} + +bool TestBlockchainGenerator::getSingleOutputTransaction(const cryptonote::AccountPublicAddress& address, uint64_t amount) { + TransactionForAddressCreator creator; + if (!creator.init()) + return false; + + cryptonote::Transaction tx; + creator.generateSingleOutputTx(address, amount, tx); + + addToBlockchain(tx); + + return true; +} + +void TestBlockchainGenerator::addToBlockchain(const cryptonote::Transaction& tx) { + crypto::hash txHash = get_transaction_hash(tx); m_txs[txHash] = tx; std::list txs; @@ -122,6 +158,60 @@ bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::Account generator.constructBlock(block, prev_block, miner_acc, txs); m_blockchain.push_back(block); +} - return true; +void TestBlockchainGenerator::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids) +{ + if (known_block_id != cryptonote::get_block_hash(m_blockchain.back())) { + is_bc_actual = false; + return; + } + + is_bc_actual = true; + + std::unordered_set txIds; + for (const auto& kv : m_txPool) { + txIds.insert(kv.first); + } + + std::unordered_set known_set(known_pool_tx_ids.begin(), known_pool_tx_ids.end()); + for (auto it = txIds.begin(), e = txIds.end(); it != e;) { + auto known_it = known_set.find(*it); + if (known_it != known_set.end()) { + known_set.erase(known_it); + it = txIds.erase(it); + } + else { + new_txs.push_back(m_txPool[*it]); + ++it; + } + } + + deleted_tx_ids.assign(known_set.begin(), known_set.end()); +} + +void TestBlockchainGenerator::putTxToPool(const cryptonote::Transaction& tx) { + crypto::hash txHash = cryptonote::get_transaction_hash(tx); + m_txPool[txHash] = tx; +} + +void TestBlockchainGenerator::putTxPoolToBlockchain() { + std::list txs; + + for (const auto& kv: m_txPool) { + m_txs[kv.first] = kv.second; + txs.push_back(kv.second); + } + + cryptonote::Block& prev_block = m_blockchain.back(); + cryptonote::Block block; + + generator.constructBlock(block, prev_block, miner_acc, txs); + m_blockchain.push_back(block); + m_txPool.clear(); +} + +void TestBlockchainGenerator::clearTxPool() { + m_txPool.clear(); } diff --git a/tests/unit_tests/TestBlockchainGenerator.h b/tests/unit_tests/TestBlockchainGenerator.h index 6863d3b8fc..1e6303a80e 100644 --- a/tests/unit_tests/TestBlockchainGenerator.h +++ b/tests/unit_tests/TestBlockchainGenerator.h @@ -33,16 +33,30 @@ class TestBlockchainGenerator TestBlockchainGenerator(const cryptonote::Currency& currency); std::vector& getBlockchain(); - void addGenesisBlock(); void generateEmptyBlocks(size_t count); bool getBlockRewardForAddress(const cryptonote::AccountPublicAddress& address); + bool getSingleOutputTransaction(const cryptonote::AccountPublicAddress& address, uint64_t amount); void addTxToBlockchain(const cryptonote::Transaction& transaction); bool getTransactionByHash(const crypto::hash& hash, cryptonote::Transaction& tx); + const cryptonote::account_base& getMinerAccount() const { return miner_acc; } + + void putTxToPool(const cryptonote::Transaction& tx); + void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids); + void putTxPoolToBlockchain(); + void clearTxPool(); private: + + void addGenesisBlock(); + void addMiningBlock(); + const cryptonote::Currency& m_currency; test_generator generator; cryptonote::account_base miner_acc; std::vector m_blockchain; std::unordered_map m_txs; + std::unordered_map m_txPool; + + void addToBlockchain(cryptonote::Transaction const& tx); }; diff --git a/tests/unit_tests/TransactionApi.cpp b/tests/unit_tests/TransactionApi.cpp new file mode 100644 index 0000000000..9709fe99cd --- /dev/null +++ b/tests/unit_tests/TransactionApi.cpp @@ -0,0 +1,317 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" +#include +#include "cryptonote_core/TransactionApi.h" +#include "cryptonote_core/cryptonote_format_utils.h" // TODO: delete +#include "cryptonote_core/account.h" +#include "crypto/crypto.h" +#include "TransactionApiHelpers.h" + +using namespace CryptoNote; + +namespace { + + template + void fillRandomBytes(std::array& data) { + for (size_t i = 0; i < size; ++i) { + data[i] = std::rand() % std::numeric_limits::max(); + } + } + + template + Array randomArray() { + Array a; + fillRandomBytes(a); + return a; + } + + void derivePublicKey(const AccountKeys& reciever, const crypto::public_key& srcTxKey, size_t outputIndex, PublicKey& ephemeralKey) { + crypto::key_derivation derivation; + crypto::generate_key_derivation(srcTxKey, reinterpret_cast(reciever.viewSecretKey), derivation); + crypto::derive_public_key(derivation, outputIndex, + reinterpret_cast(reciever.address.spendPublicKey), + reinterpret_cast(ephemeralKey)); + } + + + std::unique_ptr reloadedTx(const std::unique_ptr& tx) { + auto txBlob = tx->getTransactionData(); + return createTransaction(txBlob); + } + + void checkTxReload(const std::unique_ptr& tx) { + auto txBlob = tx->getTransactionData(); + auto tx2 = createTransaction(txBlob); + ASSERT_EQ(tx2->getTransactionData(), txBlob); + } + + + class TransactionApi : public testing::Test { + protected: + + virtual void SetUp() override { + sender = generateAccountKeys(); + tx = createTransaction(); + } + + TransactionTypes::InputKeyInfo createInputInfo(uint64_t amount) { + TransactionTypes::InputKeyInfo info; + + cryptonote::KeyPair srcTxKeys = cryptonote::KeyPair::generate(); + + PublicKey targetKey; + + derivePublicKey(sender, srcTxKeys.pub, 5, targetKey); + + TransactionTypes::GlobalOutput gout = { targetKey, 0 }; + + info.amount = 1000; + info.outputs.push_back(gout); + + info.realOutput.transactionIndex = 0; + info.realOutput.outputInTransaction = 5; + info.realOutput.transactionPublicKey = reinterpret_cast(srcTxKeys.pub); + + return info; + } + + AccountKeys sender; + std::unique_ptr tx; + }; + +} + +TEST_F(TransactionApi, createEmptyReload) { + auto pk = tx->getTransactionPublicKey(); + checkTxReload(tx); + // transaction key should not change on reload + ASSERT_EQ(pk, reloadedTx(tx)->getTransactionPublicKey()); +} + +TEST_F(TransactionApi, addAndSignInput) { + ASSERT_EQ(0, tx->getInputCount()); + ASSERT_EQ(0, tx->getInputTotalAmount()); + + TransactionTypes::InputKeyInfo info = createInputInfo(1000); + KeyPair ephKeys; + size_t index = tx->addInput(sender, info, ephKeys); + + ASSERT_EQ(0, index); + ASSERT_EQ(1, tx->getInputCount()); + ASSERT_EQ(1000, tx->getInputTotalAmount()); + ASSERT_EQ(TransactionTypes::InputType::Key, tx->getInputType(index)); + ASSERT_EQ(1, tx->getRequiredSignaturesCount(index)); + + ASSERT_TRUE(tx->validateInputs()); + ASSERT_FALSE(tx->validateSignatures()); // signature not present + + tx->signInputKey(index, info, ephKeys); + + ASSERT_TRUE(tx->validateSignatures()); // now it's ok + + auto txBlob = tx->getTransactionData(); + ASSERT_FALSE(txBlob.empty()); +} + +TEST_F(TransactionApi, addAndSignInputMsig) { + + TransactionTypes::InputMultisignature inputMsig; + + inputMsig.amount = 1000; + inputMsig.outputIndex = 0; + inputMsig.signatures = 3; + + auto index = tx->addInput(inputMsig); + + ASSERT_EQ(0, index); + ASSERT_EQ(1, tx->getInputCount()); + ASSERT_EQ(1000, tx->getInputTotalAmount()); + ASSERT_EQ(TransactionTypes::InputType::Multisignature, tx->getInputType(index)); + ASSERT_EQ(3, tx->getRequiredSignaturesCount(index)); + + auto srcTxKey = generateKeys().publicKey; + AccountKeys accounts[] = { generateAccountKeys(), generateAccountKeys(), generateAccountKeys() }; + + tx->signInputMultisignature(index, srcTxKey, 0, accounts[0]); + + ASSERT_FALSE(tx->validateSignatures()); + + tx->signInputMultisignature(index, srcTxKey, 0, accounts[1]); + tx->signInputMultisignature(index, srcTxKey, 0, accounts[2]); + + ASSERT_TRUE(tx->validateSignatures()); + + auto txBlob = tx->getTransactionData(); + ASSERT_FALSE(txBlob.empty()); +} + +TEST_F(TransactionApi, addOutputKey) { + ASSERT_EQ(0, tx->getOutputCount()); + ASSERT_EQ(0, tx->getOutputTotalAmount()); + + size_t index = tx->addOutput(1000, sender.address); + + ASSERT_EQ(0, index); + ASSERT_EQ(1, tx->getOutputCount()); + ASSERT_EQ(1000, tx->getOutputTotalAmount()); + ASSERT_EQ(TransactionTypes::OutputType::Key, tx->getOutputType(index)); +} + +TEST_F(TransactionApi, addOutputMsig) { + ASSERT_EQ(0, tx->getOutputCount()); + ASSERT_EQ(0, tx->getOutputTotalAmount()); + + AccountKeys accounts[] = { generateAccountKeys(), generateAccountKeys(), generateAccountKeys() }; + std::vector targets; + + for (size_t i = 0; i < sizeof(accounts)/sizeof(accounts[0]); ++i) + targets.push_back(accounts[i].address); + + size_t index = tx->addOutput(1000, targets, 2); + + ASSERT_EQ(0, index); + ASSERT_EQ(1, tx->getOutputCount()); + ASSERT_EQ(1000, tx->getOutputTotalAmount()); + ASSERT_EQ(TransactionTypes::OutputType::Multisignature, tx->getOutputType(index)); +} + +TEST_F(TransactionApi, secretKey) { + size_t index = tx->addOutput(1000, sender.address); + ASSERT_EQ(1000, tx->getOutputTotalAmount()); + // reloaded transaction does not have secret key, cannot add outputs + auto tx2 = reloadedTx(tx); + ASSERT_ANY_THROW(tx2->addOutput(1000, sender.address)); + // take secret key from first transaction and add to second (reloaded) + SecretKey txSecretKey; + ASSERT_TRUE(tx->getTransactionSecretKey(txSecretKey)); + + SecretKey sk = generateKeys().secretKey; + ASSERT_ANY_THROW(tx2->setTransactionSecretKey(sk)); // unrelated secret key should not be accepted + + tx2->setTransactionSecretKey(txSecretKey); + // adding output should succeed + tx2->addOutput(500, sender.address); + ASSERT_EQ(1500, tx2->getOutputTotalAmount()); +} + +TEST_F(TransactionApi, prefixHash) { + auto hash = tx->getTransactionPrefixHash(); + tx->addOutput(1000, sender.address); + // transaction hash should change + ASSERT_NE(hash, tx->getTransactionPrefixHash()); + hash = tx->getTransactionPrefixHash(); + // prefix hash should not change on reload + ASSERT_EQ(hash, reloadedTx(tx)->getTransactionPrefixHash()); +} + +TEST_F(TransactionApi, findOutputs) { + AccountKeys accounts[] = { generateAccountKeys(), generateAccountKeys(), generateAccountKeys() }; + + tx->addOutput(1111, accounts[0].address); + tx->addOutput(2222, accounts[1].address); + tx->addOutput(3333, accounts[2].address); + + std::vector outs; + uint64_t amount = 0; + + tx->findOutputsToAccount(accounts[2].address, accounts[2].viewSecretKey, outs, amount); + + ASSERT_EQ(1, outs.size()); + ASSERT_EQ(2, outs[0]); + ASSERT_EQ(3333, amount); +} + +TEST_F(TransactionApi, setGetPaymentId) { + Hash paymentId = randomArray(); + + ASSERT_FALSE(tx->getPaymentId(paymentId)); + + tx->setPaymentId(paymentId); + + Hash paymentId2; + ASSERT_TRUE(tx->getPaymentId(paymentId2)); + ASSERT_EQ(paymentId, paymentId2); + + auto tx2 = reloadedTx(tx); + + Hash paymentId3; + ASSERT_TRUE(tx->getPaymentId(paymentId3)); + ASSERT_EQ(paymentId, paymentId3); +} + +TEST_F(TransactionApi, setExtraNonce) { + std::string extraNonce = "Hello, world"; // just a sequence of bytes + std::string s; + + ASSERT_FALSE(tx->getExtraNonce(s)); + tx->setExtraNonce(extraNonce); + + ASSERT_TRUE(tx->getExtraNonce(s)); + ASSERT_EQ(extraNonce, s); + + s.clear(); + + ASSERT_TRUE(reloadedTx(tx)->getExtraNonce(s)); + ASSERT_EQ(extraNonce, s); +} + +TEST_F(TransactionApi, doubleSpendInTransactionKey) { + TransactionTypes::InputKeyInfo info = createInputInfo(1000); + + KeyPair ephKeys; + tx->addInput(sender, info, ephKeys); + ASSERT_TRUE(tx->validateInputs()); + // now, add the same output again + tx->addInput(sender, info, ephKeys); + ASSERT_FALSE(tx->validateInputs()); +} + +TEST_F(TransactionApi, doubleSpendInTransactionMultisignature) { + TransactionTypes::InputMultisignature inputMsig = { 1000, 0, 2 }; + + tx->addInput(inputMsig); + ASSERT_TRUE(tx->validateInputs()); + tx->addInput(inputMsig); + ASSERT_FALSE(tx->validateInputs()); +} + + +TEST_F(TransactionApi, unableToModifySignedTransaction) { + + TransactionTypes::InputMultisignature inputMsig; + + inputMsig.amount = 1000; + inputMsig.outputIndex = 0; + inputMsig.signatures = 2; + auto index = tx->addInput(inputMsig); + + auto srcTxKey = generateKeys().publicKey; + + tx->signInputMultisignature(index, srcTxKey, 0, generateAccountKeys()); + + // from now on, we cannot modify transaction prefix + ASSERT_ANY_THROW(tx->addInput(inputMsig)); + ASSERT_ANY_THROW(tx->addOutput(500, sender.address)); + Hash paymentId; + ASSERT_ANY_THROW(tx->setPaymentId(paymentId)); + ASSERT_ANY_THROW(tx->setExtraNonce("smth")); + + // but can add more signatures + tx->signInputMultisignature(index, srcTxKey, 0, generateAccountKeys()); +} diff --git a/tests/unit_tests/TransactionApiHelpers.h b/tests/unit_tests/TransactionApiHelpers.h new file mode 100644 index 0000000000..3b777b34f5 --- /dev/null +++ b/tests/unit_tests/TransactionApiHelpers.h @@ -0,0 +1,102 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ITransaction.h" +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "transfers/TransfersContainer.h" + +namespace { + + using namespace CryptoNote; + + KeyPair generateKeys() { + KeyPair kp; + crypto::generate_keys( + reinterpret_cast(kp.publicKey), + reinterpret_cast(kp.secretKey)); + return kp; + } + + AccountKeys accountKeysFromKeypairs(const KeyPair& viewKeys, const KeyPair& spendKeys) { + AccountKeys ak; + ak.address.spendPublicKey = spendKeys.publicKey; + ak.address.viewPublicKey = viewKeys.publicKey; + ak.spendSecretKey = spendKeys.secretKey; + ak.viewSecretKey = viewKeys.secretKey; + return ak; + } + + AccountKeys generateAccountKeys() { + return accountKeysFromKeypairs(generateKeys(), generateKeys()); + } + + KeyImage generateKeyImage() { + return crypto::rand(); + } + + KeyImage generateKeyImage(const AccountKeys& keys, size_t idx, const PublicKey& txPubKey) { + KeyImage keyImage; + cryptonote::KeyPair in_ephemeral; + cryptonote::generate_key_image_helper( + reinterpret_cast(keys), + reinterpret_cast(txPubKey), + idx, + in_ephemeral, + reinterpret_cast(keyImage)); + return keyImage; + } + + void addTestInput(ITransaction& transaction, uint64_t amount) { + TransactionTypes::InputKey input; + input.amount = amount; + input.keyImage = generateKeyImage(); + input.keyOffsets.emplace_back(1); + + transaction.addInput(input); + } + + TransactionOutputInformationIn addTestKeyOutput(ITransaction& transaction, uint64_t amount, + uint64_t globalOutputIndex, const AccountKeys& senderKeys = generateAccountKeys()) { + + uint32_t index = static_cast(transaction.addOutput(amount, senderKeys.address)); + + TransactionTypes::OutputKey output; + transaction.getOutput(index, output); + + TransactionOutputInformationIn outputInfo; + outputInfo.type = TransactionTypes::OutputType::Key; + outputInfo.amount = output.amount; + outputInfo.globalOutputIndex = globalOutputIndex; + outputInfo.outputInTransaction = index; + outputInfo.transactionPublicKey = transaction.getTransactionPublicKey(); + outputInfo.outputKey = output.key; + outputInfo.keyImage = generateKeyImage(senderKeys, index, transaction.getTransactionPublicKey()); + + return outputInfo; + } + + +} + +namespace CryptoNote { +inline bool operator == (const AccountKeys& a, const AccountKeys& b) { + return memcmp(&a, &b, sizeof(a)) == 0; +} +} diff --git a/tests/unit_tests/TransfersObserver.h b/tests/unit_tests/TransfersObserver.h new file mode 100644 index 0000000000..0787200a98 --- /dev/null +++ b/tests/unit_tests/TransfersObserver.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include "ITransfersSynchronizer.h" + +namespace CryptoNote { + +class TransfersObserver : public ITransfersObserver { +public: + virtual void onError(ITransfersSubscription* object, uint64_t height, std::error_code ec) override { + errors.emplace_back(height, ec); + } + + virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override { + updated.push_back(transactionHash); + } + + virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) override { + deleted.push_back(transactionHash); + } + + std::vector> errors; + std::vector updated; + std::vector deleted; +}; + + +} + diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 20452ba6c5..4de6fcdf55 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -22,6 +22,7 @@ #include "common/base58.cpp" #include "cryptonote_core/cryptonote_basic_impl.h" #include "serialization/binary_utils.h" +#include "cryptonote_core/Currency.h" using namespace tools; @@ -491,9 +492,11 @@ TEST(parseAccountAddressString, fails_on_invalid_address_prefix) { std::string addr_str = base58::encode_addr(0, test_serialized_keys); - uint64_t prefix; + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + cryptonote::AccountPublicAddress addr; - ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); + + ASSERT_FALSE(currency.parseAccountAddressString(addr_str, addr)); } TEST(parseAccountAddressString, fails_on_invalid_address_content) diff --git a/tests/unit_tests/binary_serialization_compatibility.cpp b/tests/unit_tests/binary_serialization_compatibility.cpp new file mode 100644 index 0000000000..f0edee0675 --- /dev/null +++ b/tests/unit_tests/binary_serialization_compatibility.cpp @@ -0,0 +1,544 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + + +#include "gtest/gtest.h" + +#include +#include +#include + +#include "serialization/BinaryOutputStreamSerializer.h" +#include "serialization/BinaryInputStreamSerializer.h" +#include "serialization/serialization.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_serialization.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "serialization_structs_comparators.h" + +#include +#include "string_tools.h" + +template +void checkEqualBinary(Struct& original) { + std::stringstream newStream; + std::stringstream oldStream; + + cryptonote::BinaryOutputStreamSerializer binarySerializer(newStream); + binarySerializer(original, ""); + + binary_archive ba(oldStream); + bool r = ::serialization::serialize(ba, original); + ASSERT_TRUE(r); + + ASSERT_EQ(oldStream.str(), newStream.str()); +} + +template +void checkEnumeratorToLegacy(Struct& original) { + std::stringstream archive; + + cryptonote::BinaryOutputStreamSerializer binarySerializer(archive); + binarySerializer(original, ""); + + //std::cout << "enumerated string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; + + Struct restored; + binary_archive ba(archive); + bool r = ::serialization::serialize(ba, restored); + //ASSERT_TRUE(r); + + ASSERT_EQ(original, restored); +} + +template +void checkLegacyToEnumerator(Struct& original) { + std::stringstream archive; + + binary_archive ba(archive); + bool r = ::serialization::serialize(ba, original); + ASSERT_TRUE(r); + + //std::cout << "legacy string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; + + Struct restored; + + cryptonote::BinaryInputStreamSerializer binarySerializer(archive); + binarySerializer(restored, ""); + + ASSERT_EQ(original, restored); +} + +template +void checkEnumeratorToEnumerator(Struct& original) { + std::stringstream archive; + + cryptonote::BinaryOutputStreamSerializer output(archive); + output(original, ""); + + Struct restored; + cryptonote::BinaryInputStreamSerializer input(archive); + input(restored, ""); + + ASSERT_EQ(original, restored); +} + +template +void checkCompatibility(Struct& original) { + checkEqualBinary(original); + ASSERT_NO_FATAL_FAILURE(checkEnumeratorToEnumerator(original)); + ASSERT_NO_FATAL_FAILURE(checkEnumeratorToLegacy(original)); + ASSERT_NO_FATAL_FAILURE(checkLegacyToEnumerator(original)); +} + +void fillData(char* data, size_t size, char startByte) { + for (size_t i = 0; i < size; ++i) { + data[i] = startByte++; + } +} + +void fillPublicKey(crypto::public_key& key, char startByte = 120) { + fillData(reinterpret_cast(&key), sizeof(crypto::public_key), startByte); +} + +void fillHash(crypto::hash& hash, char startByte = 120) { + fillData(reinterpret_cast(&hash), sizeof(crypto::hash), startByte); +} + +void fillKeyImage(crypto::key_image& image, char startByte = 120) { + fillData(reinterpret_cast(&image), sizeof(crypto::key_image), startByte); +} + +void fillSignature(crypto::signature& sig, char startByte = 120) { + fillData(reinterpret_cast(&sig), sizeof(crypto::signature), startByte); +} + +void fillTransactionOutputMultisignature(cryptonote::TransactionOutputMultisignature& s) { + crypto::public_key key; + fillPublicKey(key, 0); + s.keys.push_back(key); + + char start = 120; + + fillPublicKey(key, start++); + s.keys.push_back(key); + + fillPublicKey(key, start++); + s.keys.push_back(key); + + fillPublicKey(key, start++); + s.keys.push_back(key); + + fillPublicKey(key, start++); + s.keys.push_back(key); + + fillPublicKey(key, start++); + s.keys.push_back(key); + + s.requiredSignatures = 12; +} + +void fillTransaction(cryptonote::Transaction& tx) { + tx.version = 1; + tx.unlockTime = 0x7f1234560089ABCD; + + cryptonote::TransactionInputGenerate gen; + gen.height = 0xABCD123456EF; + tx.vin.push_back(gen); + + cryptonote::TransactionInputToKey key; + key.amount = 500123; + key.keyOffsets = {12,3323,0x7f0000000000, std::numeric_limits::max(), 0}; + fillKeyImage(key.keyImage); + tx.vin.push_back(key); + + cryptonote::TransactionInputMultisignature multisig; + multisig.amount = 490000000; + multisig.outputIndex = 424242; + multisig.signatures = 4; + tx.vin.push_back(multisig); + + cryptonote::TransactionOutput txOutput; + txOutput.amount = 0xfff000ffff778822; + cryptonote::TransactionOutputToKey out; + fillPublicKey(out.key); + txOutput.target = out; + tx.vout.push_back(txOutput); + + tx.extra = {1,2,3,127,0,128,255}; + + tx.signatures.resize(3); + + for (size_t i = 0; i < boost::get(tx.vin[1]).keyOffsets.size(); ++i) { + crypto::signature sig; + fillSignature(sig, i); + tx.signatures[1].push_back(sig); + } + + for (size_t i = 0; i < boost::get(tx.vin[2]).signatures; ++i) { + crypto::signature sig; + fillSignature(sig, i+120); + tx.signatures[2].push_back(sig); + } +} + +void fillParentBlock(cryptonote::ParentBlock& pb) { + pb.majorVersion = 1; + pb.minorVersion = 1; + + fillHash(pb.prevId, 120); + + pb.numberOfTransactions = 3; + size_t branchSize = crypto::tree_depth(pb.numberOfTransactions); + for (size_t i = 0; i < branchSize; ++i) { + crypto::hash hash; + fillHash(hash, i); + pb.minerTxBranch.push_back(hash); + } + + fillTransaction(pb.minerTx); + + cryptonote::tx_extra_merge_mining_tag mmTag; + mmTag.depth = 10; + fillHash(mmTag.merkle_root); + pb.minerTx.extra.clear(); + cryptonote::append_mm_tag_to_extra(pb.minerTx.extra, mmTag); + + std::string my; + std::copy(pb.minerTx.extra.begin(), pb.minerTx.extra.end(), std::back_inserter(my)); + + for (size_t i = 0; i < mmTag.depth; ++i) { + crypto::hash hash; + fillHash(hash, i); + pb.blockchainBranch.push_back(hash); + } +} + +void fillBlockHeaderVersion1(cryptonote::BlockHeader& header) { + header.majorVersion = 1; + header.minorVersion = 1; + header.nonce = 0x807F00AB; + header.timestamp = 1408106672; + fillHash(header.prevId); +} + +void fillBlockHeaderVersion2(cryptonote::BlockHeader& header) { + fillBlockHeaderVersion1(header); + header.majorVersion = 2; +} + +TEST(BinarySerializationCompatibility, TransactionOutputMultisignature) { + cryptonote::TransactionOutputMultisignature s; + + fillTransactionOutputMultisignature(s); + + checkCompatibility(s); +} + +TEST(BinarySerializationCompatibility, TransactionInputGenerate) { + cryptonote::TransactionInputGenerate s; + s.height = 0x8000000000000001; + checkCompatibility(s); + + s.height = 0x7FFFFFFFFFFFFFFF; + checkCompatibility(s); + + s.height = 0; + checkCompatibility(s); +}; + +TEST(BinarySerializationCompatibility, TransactionInputToKey) { + cryptonote::TransactionInputToKey s; + + s.amount = 123456987032; + s.keyOffsets = {12,3323,0x7f00000000000000, std::numeric_limits::max(), 0}; + fillKeyImage(s.keyImage); + + checkCompatibility(s); +} + +TEST(BinarySerializationCompatibility, TransactionInputMultisignature) { + cryptonote::TransactionInputMultisignature s; + s.amount = 0xfff000ffff778822; + s.signatures = 0x7f259200; + s.outputIndex = 0; + + checkCompatibility(s); +} + +TEST(BinarySerializationCompatibility, TransactionOutput_TransactionOutputToKey) { + cryptonote::TransactionOutput s; + s.amount = 0xfff000ffff778822; + + cryptonote::TransactionOutputToKey out; + fillPublicKey(out.key); + s.target = out; + + checkCompatibility(s); +} + +TEST(BinarySerializationCompatibility, TransactionOutput_TransactionOutputMultisignature) { + cryptonote::TransactionOutput s; + s.amount = 0xfff000ffff778822; + + cryptonote::TransactionOutputMultisignature out; + fillTransactionOutputMultisignature(out); + s.target = out; + + checkCompatibility(s); +} + +TEST(BinarySerializationCompatibility, Transaction) { + cryptonote::Transaction tx; + + fillTransaction(tx); + + checkCompatibility(tx); +} + +void compareParentBlocks(cryptonote::ParentBlock& pb, cryptonote::ParentBlock& restoredPb, bool headerOnly) { + EXPECT_EQ(pb.majorVersion, restoredPb.majorVersion); + EXPECT_EQ(pb.minorVersion, restoredPb.minorVersion); + EXPECT_EQ(pb.prevId, restoredPb.prevId); + + if (headerOnly) { + return; + } + + EXPECT_EQ(pb.numberOfTransactions, restoredPb.numberOfTransactions); + EXPECT_EQ(pb.minerTxBranch, restoredPb.minerTxBranch); + EXPECT_EQ(pb.minerTx, restoredPb.minerTx); + EXPECT_EQ(pb.blockchainBranch, restoredPb.blockchainBranch); +} + +void checkEnumeratorToLegacy(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { + std::stringstream archive; + + cryptonote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); + cryptonote::BinaryOutputStreamSerializer output(archive); + output(original, ""); + + cryptonote::ParentBlock restoredPb; + uint64_t restoredTs; + uint32_t restoredNonce; + + cryptonote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); + binary_archive ba(archive); + bool r = ::serialization::serialize(ba, restored); + ASSERT_TRUE(r); + + EXPECT_EQ(nonce, restoredNonce); + EXPECT_EQ(ts, restoredTs); + + ASSERT_NO_FATAL_FAILURE(compareParentBlocks(pb, restoredPb, headerOnly)); +} + +void checkLegacyToEnumerator(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { + std::stringstream archive; + + cryptonote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); + binary_archive ba(archive); + bool r = ::serialization::serialize(ba, original); + ASSERT_TRUE(r); + + cryptonote::ParentBlock restoredPb; + uint64_t restoredTs; + uint32_t restoredNonce; + + cryptonote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); + + cryptonote::BinaryInputStreamSerializer input(archive); + input(restored, ""); + + EXPECT_EQ(nonce, restoredNonce); + EXPECT_EQ(ts, restoredTs); + + ASSERT_NO_FATAL_FAILURE(compareParentBlocks(pb, restoredPb, headerOnly)); +} + +void checkEnumeratorToEnumerator(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { + std::stringstream archive; + + cryptonote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); + cryptonote::BinaryOutputStreamSerializer output(archive); + output(original, ""); + + cryptonote::ParentBlock restoredPb; + uint64_t restoredTs; + uint32_t restoredNonce; + + cryptonote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); + + cryptonote::BinaryInputStreamSerializer input(archive); + input(restored, ""); + + EXPECT_EQ(nonce, restoredNonce); + EXPECT_EQ(ts, restoredTs); + + ASSERT_NO_FATAL_FAILURE(compareParentBlocks(pb, restoredPb, headerOnly)); +} + +void checkCompatibility(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { + ASSERT_NO_FATAL_FAILURE(checkEnumeratorToEnumerator(pb, ts, nonce, hashingSerialization, headerOnly)); + ASSERT_NO_FATAL_FAILURE(checkEnumeratorToLegacy(pb, ts, nonce, hashingSerialization, headerOnly)); + ASSERT_NO_FATAL_FAILURE(checkLegacyToEnumerator(pb, ts, nonce, hashingSerialization, headerOnly)); +} + +TEST(BinarySerializationCompatibility, ParentBlockSerializer) { + cryptonote::ParentBlock pb; + fillParentBlock(pb); + uint64_t timestamp = 1408106672; + uint32_t nonce = 1234567; + + checkCompatibility(pb, timestamp, nonce, false, false); + checkCompatibility(pb, timestamp, nonce, true, false); + checkCompatibility(pb, timestamp, nonce, false, true); +} + +void compareBlocks(cryptonote::Block& block, cryptonote::Block& restoredBlock) { + ASSERT_EQ(block.majorVersion, restoredBlock.majorVersion); + ASSERT_EQ(block.minorVersion, restoredBlock.minorVersion); + if (block.majorVersion == cryptonote::BLOCK_MAJOR_VERSION_1) { + ASSERT_EQ(block.timestamp, restoredBlock.timestamp); + ASSERT_EQ(block.prevId, restoredBlock.prevId); + ASSERT_EQ(block.nonce, restoredBlock.nonce); + } else if (block.majorVersion == cryptonote::BLOCK_MAJOR_VERSION_2) { + ASSERT_EQ(block.prevId, restoredBlock.prevId); + ASSERT_NO_FATAL_FAILURE(compareParentBlocks(block.parentBlock, restoredBlock.parentBlock, false)); + } else { + throw std::runtime_error("Unknown major block version. Check your test"); + } + ASSERT_EQ(block.minerTx, restoredBlock.minerTx); + ASSERT_EQ(block.txHashes, restoredBlock.txHashes); +} + +void checkEnumeratorToLegacy(cryptonote::Block& block) { + std::stringstream archive; + + cryptonote::BinaryOutputStreamSerializer output(archive); + output(block, ""); + + cryptonote::Block restoredBlock; + + binary_archive ba(archive); + bool r = ::serialization::serialize(ba, restoredBlock); + ASSERT_TRUE(r); + + ASSERT_NO_FATAL_FAILURE(compareBlocks(block, restoredBlock)); +} + +void checkLegacyToEnumerator(cryptonote::Block& block) { + std::stringstream archive; + + binary_archive ba(archive); + bool r = ::serialization::serialize(ba, block); + ASSERT_TRUE(r); + + cryptonote::Block restoredBlock; + + cryptonote::BinaryInputStreamSerializer output(archive); + output(restoredBlock, ""); + + ASSERT_NO_FATAL_FAILURE(compareBlocks(block, restoredBlock)); +} + +void checkEnumeratorToEnumerator(cryptonote::Block& block) { + std::stringstream archive; + + cryptonote::BinaryOutputStreamSerializer output(archive); + output(block, ""); + + cryptonote::Block restoredBlock; + +// std::cout << "enumerated string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; + + cryptonote::BinaryInputStreamSerializer input(archive); + input(restoredBlock, ""); + + ASSERT_NO_FATAL_FAILURE(compareBlocks(block, restoredBlock)); +} + +void checkCompatibility(cryptonote::Block& block) { + ASSERT_NO_FATAL_FAILURE(checkEnumeratorToEnumerator(block)); + ASSERT_NO_FATAL_FAILURE(checkEnumeratorToLegacy(block)); + ASSERT_NO_FATAL_FAILURE(checkLegacyToEnumerator(block)); +} + +TEST(BinarySerializationCompatibility, BlockVersion1) { + cryptonote::Block block; + fillBlockHeaderVersion1(block); + fillParentBlock(block.parentBlock); + fillTransaction(block.minerTx); + + for (size_t i = 0; i < 7; ++i) { + crypto::hash hash; + fillHash(hash, 0x7F + i); + block.txHashes.push_back(hash); + } + + checkCompatibility(block); +} + +TEST(BinarySerializationCompatibility, BlockVersion2) { + cryptonote::Block block; + fillBlockHeaderVersion2(block); + fillParentBlock(block.parentBlock); + fillTransaction(block.minerTx); + + for (size_t i = 0; i < 7; ++i) { + crypto::hash hash; + fillHash(hash, 0x7F + i); + block.txHashes.push_back(hash); + } + + checkCompatibility(block); +} + +TEST(BinarySerializationCompatibility, account_public_address) { + cryptonote::AccountPublicAddress addr; + + fillPublicKey(addr.m_spendPublicKey, 0x50); + fillPublicKey(addr.m_viewPublicKey, 0xAA); + + checkCompatibility(addr); +} + +TEST(BinarySerializationCompatibility, tx_extra_merge_mining_tag) { + cryptonote::tx_extra_merge_mining_tag tag; + tag.depth = 0xdeadbeef; + fillHash(tag.merkle_root); + + checkCompatibility(tag); +} + +TEST(BinarySerializationCompatibility, readFromEmptyStream) { + cryptonote::TransactionOutput t; + std::stringstream emptyStream; + cryptonote::BinaryInputStreamSerializer s(emptyStream); + + ASSERT_ANY_THROW(s(t, "")); +} + +TEST(BinarySerializationCompatibility, writeToBadStream) { + cryptonote::TransactionOutput t; + std::stringstream badStream; + cryptonote::BinaryOutputStreamSerializer s(badStream); + + badStream.setstate(std::ios::badbit); + ASSERT_ANY_THROW(s(t, "")); +} diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 261e73addb..929364ad84 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -269,7 +269,7 @@ namespace } //-------------------------------------------------------------------------------------------------------------------- const unsigned int testEmissionSpeedFactor = 4; - const size_t testGrantedFullRewardZone = 1000; + const size_t testGrantedFullRewardZone = cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; const size_t testMedian = testGrantedFullRewardZone; const size_t testBlockSize = testMedian + testMedian * 8 / 10; // expected penalty 0.64 * reward const uint64_t testPenalty = 64; // percentage diff --git a/tests/unit_tests/serialization_kv.cpp b/tests/unit_tests/serialization_kv.cpp new file mode 100644 index 0000000000..166f9f27a5 --- /dev/null +++ b/tests/unit_tests/serialization_kv.cpp @@ -0,0 +1,240 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include + +#include "serialization/KVBinaryInputStreamSerializer.h" +#include "serialization/KVBinaryOutputStreamSerializer.h" + +#include "serialization/keyvalue_serialization.h" +#include "serialization/keyvalue_serialization_overloads.h" +#include "storages/portable_storage.h" +#include "storages/portable_storage_from_bin.h" +#include "storages/portable_storage_template_helper.h" + +#include + +using namespace cryptonote; + +namespace cryptonote { + + +template +void serializeAsPod(Cont& cont, const std::string& name, ISerializer& s) { + + typedef typename Cont::value_type ElementType; + const size_t elementSize = sizeof(ElementType); + std::string buf; + + if (s.type() == ISerializer::INPUT) { + s.binary(buf, name); + const ElementType* ptr = reinterpret_cast(buf.data()); + size_t count = buf.size() / elementSize; + cont.insert(cont.begin(), ptr, ptr + count); + } else { + auto rawSize = cont.size() * elementSize; + auto ptr = reinterpret_cast(cont.data()); + buf.assign(ptr, ptr + rawSize); + s.binary(buf, name); + } +} + + +struct TestElement { + std::string name; + uint32_t nonce; + std::array blob; + std::vector u32array; + + bool operator == (const TestElement& other) const { + return + name == other.name && + nonce == other.nonce && + blob == other.blob && + u32array == other.u32array; + } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(name) + KV_SERIALIZE(nonce) + KV_SERIALIZE_VAL_POD_AS_BLOB(blob) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(u32array) + END_KV_SERIALIZE_MAP() + + + void serialize(ISerializer& s, const std::string& nm) { + s.beginObject(nm); + s(name, "name"); + s(nonce, "nonce"); + s.binary(blob.data(), blob.size(), "blob"); + serializeAsPod(u32array, "u32array", s); + s.endObject(); + } +}; + +struct TestStruct { + uint8_t u8; + uint32_t u32; + uint64_t u64; + std::vector vec1; + std::vector vec2; + TestElement root; + + bool operator == (const TestStruct& other) const { + return + root == other.root && + u8 == other.u8 && + u32 == other.u32 && + u64 == other.u64 && + vec1 == other.vec1 && + vec2 == other.vec2; + } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(root) + KV_SERIALIZE(vec1) + KV_SERIALIZE(vec2) + KV_SERIALIZE(u8) + KV_SERIALIZE(u32) + KV_SERIALIZE(u64) + END_KV_SERIALIZE_MAP() + + void serialize(ISerializer& s, const std::string& name) { + s.beginObject(name); + s(root, "root"); + s(vec1, "vec1"); + s(vec2, "vec2"); + s(u8, "u8"); + s(u32, "u32"); + s(u64, "u64"); + s.endObject(); + } + +}; + +} + + +#include + +typedef std::chrono::high_resolution_clock hclock; + +class HiResTimer { +public: + HiResTimer() : + start(hclock::now()) {} + + std::chrono::duration duration() { + return hclock::now() - start; + } + +private: + hclock::time_point start; +}; + +TEST(KVSerialize, Simple) { + TestElement testData1, testData2; + std::string buf; + + testData1.name = "hello"; + testData1.nonce = 12345; + testData1.u32array.resize(128); + + testData2.name = "bye"; + testData2.nonce = 54321; + + epee::serialization::store_t_to_binary(testData1, buf); + + std::stringstream s(buf); + KVBinaryInputStreamSerializer kvInput(s); + kvInput.parse(); + kvInput(testData2, ""); + + EXPECT_EQ(testData1, testData2); +} + + +TEST(KVSerialize, NewWriterOldReader) { + std::string bufOld, bufNew; + TestStruct s1; + TestStruct s2; + + s1.u64 = 0xffULL << 50; + s1.vec1.resize(37); + s1.root.name = "somename"; + s1.root.u32array.resize(128); + + s2.u64 = 13; + s2.vec2.resize(10); + + { + HiResTimer t; + epee::serialization::store_t_to_binary(s1, bufOld); + std::cout << "Old serialization: " << t.duration().count() << std::endl; + } + + { + HiResTimer t; + + KVBinaryOutputStreamSerializer kvOut; + kvOut(s1, ""); + std::stringstream out; + kvOut.write(out); + bufNew = out.str(); + + std::cout << "New serialization: " << t.duration().count() << std::endl; + } + + { + HiResTimer t; + TestStruct outStruct(s2); + + std::stringstream s(bufOld); + KVBinaryInputStreamSerializer kvInput(s); + kvInput.parse(); + kvInput(outStruct, ""); + + std::cout << "New deserialization: " << t.duration().count() << std::endl; + + EXPECT_EQ(s1, outStruct); + } + + + { + HiResTimer t; + TestStruct outStruct(s2); + + bool parseOld = epee::serialization::load_t_from_binary(outStruct, bufOld); + + ASSERT_TRUE(parseOld); + + std::cout << "Old deserialization: " << t.duration().count() << std::endl; + + EXPECT_EQ(s1, outStruct); + } + + { + TestStruct outStruct(s2); + bool parseNew = epee::serialization::load_t_from_binary(outStruct, bufNew); + ASSERT_TRUE(parseNew); + EXPECT_EQ(s1, outStruct); + } + + +} diff --git a/tests/unit_tests/serialization_structs_comparators.h b/tests/unit_tests/serialization_structs_comparators.h new file mode 100644 index 0000000000..3021802e04 --- /dev/null +++ b/tests/unit_tests/serialization_structs_comparators.h @@ -0,0 +1,138 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/cryptonote_basic.h" + +template +class Comparator { +public: + static bool compare(const T& t1, const T& t2) { return t1 == t2; } +}; + +namespace cryptonote { + +bool operator==(const cryptonote::TransactionOutputToScript& t1, const cryptonote::TransactionOutputToScript& t2) { + return true; +} + +bool operator==(const cryptonote::TransactionOutputMultisignature& t1, const cryptonote::TransactionOutputMultisignature& t2) { + if (t1.keys != t2.keys) { + return false; + } + + return t1.requiredSignatures == t2.requiredSignatures; +} + +bool operator==(const cryptonote::TransactionInputGenerate& t1, const cryptonote::TransactionInputGenerate& t2) { + return t1.height == t2.height; +} + +bool operator==(const cryptonote::TransactionInputToScript& t1, const cryptonote::TransactionInputToScript& t2) { + return true; +} + +bool operator==(const cryptonote::TransactionInputToScriptHash& t1, const cryptonote::TransactionInputToScriptHash& t2) { + return true; +} + +bool operator==(const cryptonote::TransactionInputToKey& t1, const cryptonote::TransactionInputToKey& t2) { + if (t1.amount != t2.amount) { + return false; + } + + if (t1.keyOffsets != t2.keyOffsets) { + return false; + } + + return t1.keyImage == t2.keyImage; +} + +bool operator==(const cryptonote::TransactionInputMultisignature& t1, const cryptonote::TransactionInputMultisignature& t2) { + if (t1.amount != t2.amount) { + return false; + } + + if (t1.signatures != t2.signatures) { + return false; + } + + return t1.outputIndex == t2.outputIndex; +} + +bool operator==(const cryptonote::TransactionOutputToScriptHash& t1, const cryptonote::TransactionOutputToScriptHash& t2) { + return true; +} + +bool operator==(const cryptonote::TransactionOutputToKey& t1, const cryptonote::TransactionOutputToKey& t2) { + return t1.key == t2.key; +} + +bool operator==(const cryptonote::TransactionOutput& t1, const cryptonote::TransactionOutput& t2) { + if (t1.amount != t2.amount) { + return false; + } + + return t1.target == t2.target; +} + +bool operator==(const cryptonote::ParentBlock& t1, const cryptonote::ParentBlock& t2) { + if (t1.majorVersion != t2.majorVersion) { + return false; + } + + if (t1.minorVersion != t2.minorVersion) { + return false; + } + + if (t1.prevId != t2.prevId) { + return false; + } + + if (t1.numberOfTransactions != t2.numberOfTransactions) { + return false; + } + + if (t1.minerTxBranch != t2.minerTxBranch) { + return false; + } + + if (!(t1.minerTx == t2.minerTx)) { + return false; + } + + return t1.blockchainBranch == t2.blockchainBranch; +} + +bool operator==(const cryptonote::AccountPublicAddress& t1, const cryptonote::AccountPublicAddress& t2) { + if (t1.m_spendPublicKey != t2.m_spendPublicKey) { + return false; + } + + return t1.m_viewPublicKey == t2.m_viewPublicKey; +} + +bool operator==(const cryptonote::tx_extra_merge_mining_tag& t1, const cryptonote::tx_extra_merge_mining_tag& t2) { + if (t1.depth != t2.depth) { + return false; + } + + return t1.merkle_root == t2.merkle_root; +} + +} diff --git a/tests/unit_tests/shuffle.cpp b/tests/unit_tests/shuffle.cpp new file mode 100644 index 0000000000..e5b818728c --- /dev/null +++ b/tests/unit_tests/shuffle.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include + +#include "gtest/gtest.h" + +#include +#include "crypto/crypto.h" +#include "common/ShuffleGenerator.h" + +class ShuffleTest : public ::testing::Test { +public: + + typedef ShuffleGenerator DefaultShuffleGenerator; + typedef ShuffleGenerator> CryptoShuffleGenerator; + + template + void checkUniqueness(Gen& gen, size_t count) { + + std::unordered_set values; + + for (auto i = 0; i < count; ++i) { + auto value = gen(); + bool inserted = values.insert(value).second; + EXPECT_TRUE(inserted); + } + } + + template + void consume(Gen& gen, size_t count) { + for (auto i = 0; i < count; ++i) { + gen(); + } + } + + template + void checkEngine(size_t N, size_t consumeCount, bool check) { + ShuffleT gen(N); + check ? checkUniqueness(gen, consumeCount) : consume(gen, consumeCount); + } + +}; + + +namespace { +const size_t ITERATIONS = 10000; +} + +TEST_F(ShuffleTest, correctness) { + checkEngine(ITERATIONS, ITERATIONS, true); +} + +TEST_F(ShuffleTest, correctness_fractionalSize) { + checkEngine(ITERATIONS, ITERATIONS, true); + checkEngine(ITERATIONS, ITERATIONS/2, true); + checkEngine(ITERATIONS, ITERATIONS/3, true); +} + + +TEST_F(ShuffleTest, cryptoGenerator) { + checkEngine(ITERATIONS * 3, ITERATIONS, false); +} + diff --git a/tests/unit_tests/test_BcS.cpp b/tests/unit_tests/test_BcS.cpp new file mode 100755 index 0000000000..5a3f6deb00 --- /dev/null +++ b/tests/unit_tests/test_BcS.cpp @@ -0,0 +1,1434 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "transfers/BlockchainSynchronizer.h" +#include "transfers/TransfersConsumer.h" + +#include "cryptonote_core/TransactionApi.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +#include "INodeStubs.h" +#include "TestBlockchainGenerator.h" +#include "EventWaiter.h" + +using namespace CryptoNote; + +namespace { +cryptonote::Transaction createTx(ITransactionReader& tx) { + auto data = tx.getTransactionData(); + + cryptonote::blobdata txblob(data.data(), data.data() + data.size()); + cryptonote::Transaction outTx; + cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + + return outTx; +} +} + +class INodeNonTrivialRefreshStub : public INodeTrivialRefreshStub { +public: + + INodeNonTrivialRefreshStub(TestBlockchainGenerator& generator) : INodeTrivialRefreshStub(generator), blocksWasQueried(false), poolWasQueried(false) {} + + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override { + blocksWasQueried = true; + INodeTrivialRefreshStub::queryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight, callback); + } + + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { + poolWasQueried = true; + INodeTrivialRefreshStub::getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback); + } + + void notifyAboutPool() { + observerManager.notify(&CryptoNote::INodeObserver::poolChanged); + } + + bool blocksWasQueried; + bool poolWasQueried; + +}; + +class INodeFunctorialStub : public INodeNonTrivialRefreshStub { +public: + + INodeFunctorialStub(TestBlockchainGenerator& generator) + : INodeNonTrivialRefreshStub(generator) + , queryBlocksFunctor([](const std::list&, uint64_t, std::list&, uint64_t&, const Callback&)->bool {return true; }) + , getPoolSymmetricDifferenceFunctor([](const std::vector&, crypto::hash, bool&, std::vector&, std::vector&, const Callback&)->bool {return true; }) { + } + + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override { + if (queryBlocksFunctor(knownBlockIds, timestamp, newBlocks, startHeight, callback)) { + INodeNonTrivialRefreshStub::queryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight, callback); + } + } + + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { + if (getPoolSymmetricDifferenceFunctor(known_pool_tx_ids, known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback)) { + INodeNonTrivialRefreshStub::getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback); + } + } + + std::function&, uint64_t, std::list&, uint64_t&, const Callback&)> queryBlocksFunctor; + std::function&, crypto::hash, bool&, std::vector&, std::vector&, const Callback&)> getPoolSymmetricDifferenceFunctor; + +}; + +class IBlockchainSynchronizerTrivialObserver : public IBlockchainSynchronizerObserver { +public: + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override { m_current = current; m_total = total; } + virtual void synchronizationCompleted(std::error_code result) override { completionResult = result; } + + std::error_code completionResult; + uint64_t m_current; + uint64_t m_total; +}; + +class IBlockchainSynchronizerFunctorialObserver : public IBlockchainSynchronizerObserver { +public: + IBlockchainSynchronizerFunctorialObserver() : updFunc([](uint64_t, uint64_t) {}), syncFunc([](std::error_code) {}) { + } + + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override { updFunc(current, total); } + virtual void synchronizationCompleted(std::error_code result) override { syncFunc(result); } + + std::function updFunc; + std::function syncFunc; +}; + +class ConsumerStub : public IBlockchainConsumer { +public: + ConsumerStub(const crypto::hash& genesisBlockHash) { + m_blockchain.push_back(genesisBlockHash); + } + + virtual SynchronizationStart getSyncStart() override { + SynchronizationStart start = { 0, 0 }; + return start; + } + + virtual void onBlockchainDetach(uint64_t height) override { + assert(height < m_blockchain.size()); + m_blockchain.resize(height); + } + + virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override { + //assert(m_blockchain.size() == startHeight); + while (count--) { + m_blockchain.push_back(blocks->blockHash); + ++blocks; + } + return true; + } + + const std::vector& getBlockchain() const { + return m_blockchain; + } + + virtual void getKnownPoolTxIds(std::vector& ids) override { + ids.clear(); + for (auto& tx : m_pool) { + ids.push_back(cryptonote::get_transaction_hash(tx)); + } + } + + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { + m_pool.insert(m_pool.end(), addedTransactions.begin(), addedTransactions.end()); + + for (auto& hash : deletedTransactions) { + auto pos = std::find_if(m_pool.begin(), m_pool.end(), [&hash](const cryptonote::Transaction& t)->bool { return hash == cryptonote::get_transaction_hash(t); }); + if (pos != m_pool.end()) { + m_pool.erase(pos); + } + } + + return std::error_code(); + } + +private: + std::vector m_pool; + std::vector m_blockchain; +}; + +class BcSTest : public ::testing::Test, public IBlockchainSynchronizerObserver { +public: + BcSTest() : + m_currency(cryptonote::CurrencyBuilder().currency()), + generator(m_currency), + m_node(generator), + m_sync(m_node, m_currency.genesisBlockHash()) { + m_node.setGetNewBlocksLimit(5); // sync max 5 blocks per request + } + + void addConsumers(size_t count = 1) { + while (count--) { + std::shared_ptr stub(new ConsumerStub(m_currency.genesisBlockHash())); + m_sync.addConsumer(stub.get()); + m_consumers.push_back(stub); + } + } + + void checkSyncedBlockchains() { + std::vector generatorBlockchain; + std::transform( + generator.getBlockchain().begin(), + generator.getBlockchain().end(), + std::back_inserter(generatorBlockchain), + [](const cryptonote::Block& b) { return cryptonote::get_block_hash(b); }); + + for (const auto& consumer : m_consumers) { + ASSERT_EQ(consumer->getBlockchain(), generatorBlockchain); + } + } + + void startSync() { + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_sync.start(); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + } + + void refreshSync() { + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_node.updateObservers(); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + } + + void synchronizationCompleted(std::error_code result) override { + decltype(syncCompleted) detachedPromise = std::move(syncCompleted); + detachedPromise.set_value(result); + } + +protected: + cryptonote::Currency m_currency; + TestBlockchainGenerator generator; + + INodeFunctorialStub m_node; + BlockchainSynchronizer m_sync; + std::vector> m_consumers; + + std::promise syncCompleted; + std::future syncCompletedFuture; +}; + +TEST_F(BcSTest, addConsumerStopped) { + ASSERT_NO_THROW(addConsumers()); +} + +TEST_F(BcSTest, addConsumerStartStop) { + addConsumers(); + m_sync.start(); + m_sync.stop(); + ASSERT_NO_THROW(addConsumers()); +} + +TEST_F(BcSTest, addConsumerStartThrow) { + addConsumers(); + m_sync.start(); + ASSERT_ANY_THROW(addConsumers()); + m_sync.stop(); +} + +TEST_F(BcSTest, removeConsumerWhichIsNotExist) { + ConsumerStub c(m_currency.genesisBlockHash()); + ASSERT_FALSE(m_sync.removeConsumer(&c)); +} + +TEST_F(BcSTest, removeConsumerStartThrow) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + ASSERT_ANY_THROW(m_sync.removeConsumer(&c)); + m_sync.stop(); +} + +TEST_F(BcSTest, removeConsumerStopped) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + EXPECT_EQ(true, m_sync.removeConsumer(&c)); +} + +TEST_F(BcSTest, removeConsumerStartStop) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + m_sync.stop(); + EXPECT_EQ(true, m_sync.removeConsumer(&c)); +} + +TEST_F(BcSTest, getConsumerStateWhichIsNotExist) { + ConsumerStub c(m_currency.genesisBlockHash()); + EXPECT_EQ(nullptr, m_sync.getConsumerState(&c)); +} + +TEST_F(BcSTest, getConsumerStateStartThrow) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + ASSERT_ANY_THROW(m_sync.getConsumerState(&c)); + m_sync.stop(); +} + +TEST_F(BcSTest, getConsumerStateStopped) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + EXPECT_NE(nullptr, m_sync.getConsumerState(&c)); +} + +TEST_F(BcSTest, getConsumerStateStartStop) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + m_sync.stop(); + EXPECT_NE(nullptr, m_sync.getConsumerState(&c)); +} + +TEST_F(BcSTest, startWithoutConsumersThrow) { + ASSERT_ANY_THROW(m_sync.start()); +} + +TEST_F(BcSTest, doubleStart) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + ASSERT_ANY_THROW(m_sync.start()); + m_sync.stop(); +} + +TEST_F(BcSTest, startAfterStop) { + addConsumers(); + m_sync.start(); + m_sync.stop(); + ASSERT_NO_THROW(m_sync.start()); + m_sync.stop(); +} + +TEST_F(BcSTest, startAndObserve) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + ASSERT_ANY_THROW(m_sync.start()); + m_sync.stop(); +} + +TEST_F(BcSTest, noObservationsBeforeStart) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_node.updateObservers(); + ASSERT_FALSE(m_node.blocksWasQueried); +} + +TEST_F(BcSTest, noObservationsAfterStop) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + m_sync.stop(); + m_node.blocksWasQueried = false; + m_node.updateObservers(); + ASSERT_FALSE(m_node.blocksWasQueried); +} + +TEST_F(BcSTest, stopOnCreation) { + ASSERT_NO_THROW(m_sync.stop()); +} + +TEST_F(BcSTest, doubleStopAfterStart) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + m_sync.start(); + m_sync.stop(); + ASSERT_NO_THROW(m_sync.stop()); +} + +TEST_F(BcSTest, stopIsWaiting) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + generator.generateEmptyBlocks(20); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + + bool flag = false; + + o1.updFunc = std::move([&e, &flag](uint64_t, uint64_t) { + e.notify(); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); flag = true; + + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(flag, true); +} + +TEST_F(BcSTest, syncCompletedError) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + generator.generateEmptyBlocks(20); + IBlockchainSynchronizerTrivialObserver o; + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + + o1.updFunc = std::move([&e](uint64_t curr, uint64_t total) { + e.notify(); std::this_thread::sleep_for(std::chrono::milliseconds(200)); + }); + + m_sync.addObserver(&o); + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(std::errc::interrupted, o.completionResult); +} + +TEST_F(BcSTest, onLastKnownBlockHeightUpdated) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + generator.generateEmptyBlocks(20); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + + e.wait(); + m_node.blocksWasQueried = false; + m_node.poolWasQueried = false; + m_node.updateObservers(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(true, m_node.blocksWasQueried); + EXPECT_EQ(true, m_node.poolWasQueried); +} + +TEST_F(BcSTest, onPoolChanged) { + ConsumerStub c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&c); + generator.generateEmptyBlocks(20); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + + e.wait(); + m_node.poolWasQueried = false; + m_node.notifyAboutPool(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(true, m_node.poolWasQueried); +} + +TEST_F(BcSTest, serializationCheck) { + addConsumers(2); + + std::stringstream memstream; + m_sync.save(memstream); + + ASSERT_GT(memstream.str().size(), 0); + + std::string first = memstream.str(); + + BlockchainSynchronizer sync2(m_node, m_currency.genesisBlockHash()); + + ASSERT_NO_THROW(sync2.load(memstream)); + std::stringstream memstream2; + m_sync.save(memstream2); + EXPECT_EQ(memstream2.str(), first); +} + +class FunctorialPoolConsumerStub : public ConsumerStub { +public: + + FunctorialPoolConsumerStub(const crypto::hash& genesisBlockHash) : ConsumerStub(genesisBlockHash) {} + + virtual void getKnownPoolTxIds(std::vector& ids) override { + getKnownPoolTxIdsFunctor(ids); + } + + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { + return onPoolUpdatedFunctor(addedTransactions, deletedTransactions); + } + + std::function&)> getKnownPoolTxIdsFunctor; + std::function&, const std::vector&)> onPoolUpdatedFunctor; +}; + +TEST_F(BcSTest, firstPoolSynchronizationCheck) { + auto tx1ptr = CryptoNote::createTransaction(); + auto tx2ptr = CryptoNote::createTransaction(); + auto tx3ptr = CryptoNote::createTransaction(); + + auto tx1 = ::createTx(*tx1ptr.get()); + auto tx2 = ::createTx(*tx2ptr.get()); + auto tx3 = ::createTx(*tx3ptr.get()); + + auto tx1hash = cryptonote::get_transaction_hash(tx1); + auto tx2hash = cryptonote::get_transaction_hash(tx2); + auto tx3hash = cryptonote::get_transaction_hash(tx3); + + std::vector consumer1Pool = { tx1hash, tx2hash }; + std::vector consumer2Pool = { tx2hash, tx3hash }; + std::unordered_set firstExpectedPool = { tx1hash, tx2hash, tx3hash }; + std::unordered_set secondExpectedPool = { tx2hash }; + + std::vector expectedDeletedPoolAnswer = { tx3hash }; + std::vector expectedNewPoolAnswer = { tx1 }; + + FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); + FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); + + c1.getKnownPoolTxIdsFunctor = [&](std::vector& ids) { ids.assign(consumer1Pool.begin(), consumer1Pool.end()); }; + c2.getKnownPoolTxIdsFunctor = [&](std::vector& ids) { ids.assign(consumer2Pool.begin(), consumer2Pool.end()); }; + + std::vector c1ResponseDeletedPool; + std::vector c2ResponseDeletedPool; + std::vector c1ResponseNewPool; + std::vector c2ResponseNewPool; + + + c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c1ResponseDeletedPool.assign(deleted.begin(), deleted.end()); + c1ResponseNewPool.assign(new_txs.begin(), new_txs.end()); + return std::error_code(); + }; + + c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c2ResponseDeletedPool.assign(deleted.begin(), deleted.end()); + c2ResponseNewPool.assign(new_txs.begin(), new_txs.end()); + return std::error_code(); + }; + + m_sync.addConsumer(&c1); + m_sync.addConsumer(&c2); + + int requestsCount = 0; + std::unordered_set firstKnownPool; + std::unordered_set secondKnownPool; + + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + new_txs.assign(expectedNewPoolAnswer.begin(), expectedNewPoolAnswer.end()); + deleted.assign(expectedDeletedPoolAnswer.begin(), expectedDeletedPoolAnswer.end()); + + if (requestsCount == 1) { + firstKnownPool.insert(known.begin(), known.end()); + } + + if (requestsCount == 2) { + secondKnownPool.insert(known.begin(), known.end()); + } + + callback(std::error_code()); + + return false; + }; + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(2, requestsCount); + EXPECT_EQ(firstExpectedPool, firstKnownPool); + EXPECT_EQ(secondExpectedPool, secondKnownPool); + EXPECT_EQ(expectedDeletedPoolAnswer, c1ResponseDeletedPool); + EXPECT_EQ(expectedDeletedPoolAnswer, c2ResponseDeletedPool); + EXPECT_EQ(expectedNewPoolAnswer, c1ResponseNewPool); + EXPECT_EQ(expectedNewPoolAnswer, c2ResponseNewPool); +} + +TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) { + addConsumers(2); + + int requestsCount = 0; + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + if (requestsCount == 2) { + is_actual = false; + } + + callback(std::error_code()); + return false; + }; + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + + EXPECT_EQ(4, requestsCount); +} + +TEST_F(BcSTest, firstPoolSynchronizationCheckGetPoolErr) { + addConsumers(2); + + int requestsCount = 0; + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + if (requestsCount == 2) { + callback(std::make_error_code(std::errc::invalid_argument)); + } else { + callback(std::error_code()); + } + + return false; + }; + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_node.notifyAboutPool(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(4, requestsCount); +} + +TEST_F(BcSTest, poolSynchronizationCheckActual) { + addConsumers(1); + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + + int requestsCount = 0; + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + if (requestsCount == 1) { + is_actual = false; + } + + callback(std::error_code()); + return false; + }; + + m_node.notifyAboutPool(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(2, requestsCount); +} + +TEST_F(BcSTest, poolSynchronizationCheckError) { + addConsumers(1); + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + + int requestsCount = 0; + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + if (requestsCount == 1) { + callback(std::make_error_code(std::errc::invalid_argument)); + } else { + callback(std::error_code()); + } + return false; + }; + + m_node.notifyAboutPool(); + e.wait(); + EXPECT_NE(0, errc.value()); + m_node.notifyAboutPool(); //error, notify again + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(2, requestsCount); +} + +TEST_F(BcSTest, poolSynchronizationCheckTxAdded) { + auto tx1ptr = CryptoNote::createTransaction(); + auto tx1 = ::createTx(*tx1ptr.get()); + auto tx1hash = cryptonote::get_transaction_hash(tx1); + + std::vector newPoolAnswer = { tx1 }; + std::vector expectedKnownPoolHashes = { tx1hash }; + + + addConsumers(1); + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + + int requestsCount = 0; + std::vector knownPool; + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + + if (requestsCount == 1) { + new_txs.assign(newPoolAnswer.begin(), newPoolAnswer.end()); + } + + if (requestsCount == 2) { + knownPool.assign(known.begin(), known.end()); + } + + callback(std::error_code()); + + return false; + }; + + m_node.notifyAboutPool(); + e.wait(); + m_node.notifyAboutPool(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(2, requestsCount); + EXPECT_EQ(expectedKnownPoolHashes, knownPool); +} + +TEST_F(BcSTest, poolSynchronizationCheckTxDeleted) { + auto tx1ptr = CryptoNote::createTransaction(); + auto tx1 = ::createTx(*tx1ptr.get()); + auto tx1hash = cryptonote::get_transaction_hash(tx1); + + std::vector newPoolAnswer = { tx1 }; + std::vector deletedPoolAnswer = { tx1hash }; + std::vector expectedKnownPoolHashes = {}; + + + addConsumers(1); + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + + int requestsCount = 0; + std::vector knownPool; + + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + is_actual = true; + requestsCount++; + + + if (requestsCount == 1) { + new_txs.assign(newPoolAnswer.begin(), newPoolAnswer.end()); + } + + if (requestsCount == 2) { + deleted.assign(deletedPoolAnswer.begin(), deletedPoolAnswer.end()); + } + + if (requestsCount == 3) { + knownPool.assign(known.begin(), known.end()); + } + + callback(std::error_code()); + + return false; + }; + + m_node.notifyAboutPool(); // add + e.wait(); + m_node.notifyAboutPool(); // delete + e.wait(); + m_node.notifyAboutPool(); //getknown + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(3, requestsCount); + EXPECT_EQ(expectedKnownPoolHashes, knownPool); +} + +TEST_F(BcSTest, poolSynchronizationCheckNotififcation) { + addConsumers(1); + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.start(); + EXPECT_EQ(true, e.wait_for(std::chrono::milliseconds(300))); + m_sync.stop(); +} + +TEST_F(BcSTest, poolSynchronizationCheckConsumersNotififcation) { + FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); + FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); + + c1.getKnownPoolTxIdsFunctor = [&](std::vector& ids) {}; + c2.getKnownPoolTxIdsFunctor = [&](std::vector& ids) {}; + + bool c1Notified = false; + bool c2Notified = false; + c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c1Notified = true; + return std::error_code(); + }; + + c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c2Notified = true; + return std::error_code(); + }; + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + o1.syncFunc = std::move([&e](std::error_code) { + e.notify(); + }); + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c1); + m_sync.addConsumer(&c2); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + ASSERT_TRUE(c1Notified); + ASSERT_TRUE(c2Notified); +} + +TEST_F(BcSTest, poolSynchronizationCheckConsumerReturnError) { + FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); + FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); + + c1.getKnownPoolTxIdsFunctor = [&](std::vector& ids) {}; + c2.getKnownPoolTxIdsFunctor = [&](std::vector& ids) {}; + + bool c1Notified = false; + bool c2Notified = false; + c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c1Notified = true; + return std::make_error_code(std::errc::invalid_argument); + }; + + c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c2Notified = true; + return std::make_error_code(std::errc::invalid_argument); + }; + + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c1); + m_sync.addConsumer(&c2); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + ASSERT_TRUE(c1Notified != c2Notified); + EXPECT_NE(0, errc.value()); +} + +class FunctorialBlockhainConsumerStub : public ConsumerStub { +public: + + FunctorialBlockhainConsumerStub(const crypto::hash& genesisBlockHash) : ConsumerStub(genesisBlockHash), onBlockchainDetachFunctor([](uint64_t) {}) {} + + virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override { + return onNewBlocksFunctor(blocks, startHeight, count); + } + + virtual void onBlockchainDetach(uint64_t height) override { + onBlockchainDetachFunctor(height); + } + + std::function onNewBlocksFunctor; + std::function onBlockchainDetachFunctor; +}; + +TEST_F(BcSTest, checkINodeError) { + addConsumers(1); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + m_node.queryBlocksFunctor = [](const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const INode::Callback& callback) -> bool { + callback(std::make_error_code(std::errc::invalid_argument)); + return false; + }; + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(std::make_error_code(std::errc::invalid_argument), errc); +} + +TEST_F(BcSTest, checkConsumerError) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + generator.generateEmptyBlocks(10); + + c.onNewBlocksFunctor = [](const CompleteBlock*, uint64_t, size_t) -> bool { + return false; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(std::make_error_code(std::errc::invalid_argument), errc); +} + +TEST_F(BcSTest, checkINodeReturnBadBlock) { + addConsumers(1); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + m_node.queryBlocksFunctor = [](const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const INode::Callback& callback) -> bool { + CryptoNote::BlockCompleteEntry block; + block.block = "badblock"; + startHeight = 1; + newBlocks.push_back(block); + callback(std::error_code()); + return false; + }; + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(std::make_error_code(std::errc::invalid_argument), errc); +} + +TEST_F(BcSTest, checkINodeReturnBadTx) { + addConsumers(1); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + generator.generateEmptyBlocks(2); + + CryptoNote::BlockCompleteEntry bce; + + auto last_block = generator.getBlockchain().back(); + bce.blockHash = cryptonote::get_block_hash(last_block); + bce.block = cryptonote::block_to_blob(last_block); + bce.txs.push_back("badtx"); + + + m_node.queryBlocksFunctor = [&bce](const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const INode::Callback& callback) -> bool { + startHeight = 1; + newBlocks.push_back(bce); + callback(std::error_code()); + return false; + }; + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(std::make_error_code(std::errc::invalid_argument), errc); +} + +TEST_F(BcSTest, checkBlocksRequesting) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + + size_t blocksExpected = 20; + + generator.generateEmptyBlocks(blocksExpected - 1); //-1 for genesis + m_node.setGetNewBlocksLimit(3); + + size_t blocksRequested = 0; + + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t, size_t count) -> bool { + blocksRequested += count; + return true; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(blocksExpected, blocksRequested); +} + +TEST_F(BcSTest, checkConsumerHeightReceived) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + + uint64_t firstlySnchronizedHeight = 20; + + generator.generateEmptyBlocks(firstlySnchronizedHeight - 1);//-1 for genesis + m_node.setGetNewBlocksLimit(50); + + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t startHeight, size_t) -> bool { + return true; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + + generator.generateEmptyBlocks(20); + + ConsumerStub fake_c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&fake_c); + uint64_t receivedStartHeight = 0; + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t startHeight, size_t) -> bool { + receivedStartHeight = startHeight; + return true; + }; + + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(firstlySnchronizedHeight + 1, receivedStartHeight); +} + +TEST_F(BcSTest, checkConsumerOldBlocksNotIvoked) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + generator.generateEmptyBlocks(20); + m_node.setGetNewBlocksLimit(50); + + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t startHeight, size_t) -> bool { + return true; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + + ConsumerStub fake_c(m_currency.genesisBlockHash()); + m_sync.addConsumer(&fake_c); + + bool onNewBlocksInvoked = false; + + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t startHeight, size_t) -> bool { + onNewBlocksInvoked = true; + return true; + }; + + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + ASSERT_FALSE(onNewBlocksInvoked); +} + +TEST_F(BcSTest, checkConsumerHeightReceivedOnDetach) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + generator.generateEmptyBlocks(20); + m_node.setGetNewBlocksLimit(50); + + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t startHeight, size_t) -> bool { + return true; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + + uint64_t alternativeHeight = 10; + + m_node.startAlternativeChain(alternativeHeight); + generator.generateEmptyBlocks(20); + + uint64_t receivedStartHeight = 0; + c.onNewBlocksFunctor = [&](const CompleteBlock*, uint64_t startHeight, size_t) -> bool { + receivedStartHeight = startHeight; + return true; + }; + + uint64_t receivedetachHeight = 0; + c.onBlockchainDetachFunctor = [&](uint64_t detachHeight) { + receivedetachHeight = detachHeight; + }; + + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(alternativeHeight, receivedetachHeight); + EXPECT_EQ(alternativeHeight, receivedStartHeight); +} + +TEST_F(BcSTest, checkStatePreservingBetweenSynchronizations) { + addConsumers(1); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + generator.generateEmptyBlocks(20); + + crypto::hash lastBlockHash = cryptonote::get_block_hash(generator.getBlockchain().back()); + + m_sync.addObserver(&o1); + m_sync.start(); + e.wait(); + m_sync.stop(); + + crypto::hash receivedLastBlockHash; + + m_node.queryBlocksFunctor = [&receivedLastBlockHash](const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const INode::Callback& callback) -> bool { + receivedLastBlockHash = knownBlockIds.front(); + startHeight = 1; + callback(std::make_error_code(std::errc::interrupted)); + return false; + }; + + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(lastBlockHash, receivedLastBlockHash); +} + +TEST_F(BcSTest, checkBlocksRerequestingOnError) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + generator.generateEmptyBlocks(20); + m_node.setGetNewBlocksLimit(10); + + int requestsCount = 0; + std::list firstlyKnownBlockIdsTaken; + std::list secondlyKnownBlockIdsTaken; + + std::vector firstlyReceivedBlocks; + std::vector secondlyReceivedBlocks; + + + c.onNewBlocksFunctor = [&](const CompleteBlock* blocks, uint64_t, size_t count) -> bool { + + if (requestsCount == 2) { + for (size_t i = 0; i < count; ++i) { + firstlyReceivedBlocks.push_back(blocks[i].blockHash); + } + + return false; + } + + if (requestsCount == 3) { + for (size_t i = 0; i < count; ++i) { + secondlyReceivedBlocks.push_back(blocks[i].blockHash); + } + } + + return true; + }; + + m_node.queryBlocksFunctor = [&](const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const INode::Callback& callback) -> bool { + if (requestsCount == 1) { + firstlyKnownBlockIdsTaken.assign(knownBlockIds.begin(), knownBlockIds.end()); + } + + if (requestsCount == 2) { + secondlyKnownBlockIdsTaken.assign(knownBlockIds.begin(), knownBlockIds.end()); + } + + + ++requestsCount; + return true; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(firstlyKnownBlockIdsTaken, secondlyKnownBlockIdsTaken); + EXPECT_EQ(firstlyReceivedBlocks, secondlyReceivedBlocks); +} + +TEST_F(BcSTest, checkTxOrder) { + FunctorialBlockhainConsumerStub c(m_currency.genesisBlockHash()); + IBlockchainSynchronizerFunctorialObserver o1; + EventWaiter e; + std::error_code errc; + o1.syncFunc = std::move([&](std::error_code ec) { + e.notify(); + errc = ec; + }); + + + auto tx1ptr = CryptoNote::createTransaction(); + auto tx2ptr = CryptoNote::createTransaction(); + auto tx3ptr = CryptoNote::createTransaction(); + + auto tx1 = ::createTx(*tx1ptr.get()); + auto tx2 = ::createTx(*tx2ptr.get()); + auto tx3 = ::createTx(*tx3ptr.get()); + + auto tx1hash = cryptonote::get_transaction_hash(tx1); + auto tx2hash = cryptonote::get_transaction_hash(tx2); + auto tx3hash = cryptonote::get_transaction_hash(tx3); + + + generator.generateEmptyBlocks(2); + + CryptoNote::BlockCompleteEntry bce; + + auto last_block = generator.getBlockchain().back(); + bce.blockHash = cryptonote::get_block_hash(last_block); + bce.block = cryptonote::block_to_blob(last_block); + bce.txs.push_back(cryptonote::tx_to_blob(tx1)); + bce.txs.push_back(cryptonote::tx_to_blob(tx2)); + bce.txs.push_back(cryptonote::tx_to_blob(tx3)); + + + std::vector expectedTxHashes = { cryptonote::get_transaction_hash(last_block.minerTx), tx1hash, tx2hash, tx3hash }; + + int requestNumber = 0; + + m_node.queryBlocksFunctor = [&bce, &requestNumber](const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const INode::Callback& callback) -> bool { + startHeight = 1; + newBlocks.push_back(bce); + if (requestNumber > 0) { + callback(std::make_error_code(std::errc::interrupted)); + } else { + callback(std::error_code()); + } + + requestNumber++; + return false; + }; + + std::vector receivedTxHashes = {}; + + c.onNewBlocksFunctor = [&](const CompleteBlock* blocks, uint64_t, size_t count) -> bool { + for (auto& tx : blocks[count - 1].transactions) { + auto hash = tx->getTransactionHash(); + receivedTxHashes.push_back(*reinterpret_cast(&hash)); + } + + return true; + }; + + m_sync.addObserver(&o1); + m_sync.addConsumer(&c); + m_sync.start(); + e.wait(); + m_sync.stop(); + m_sync.removeObserver(&o1); + o1.syncFunc = [](std::error_code) {}; + + EXPECT_EQ(expectedTxHashes, receivedTxHashes); +} diff --git a/tests/unit_tests/test_TransfersConsumer.cpp b/tests/unit_tests/test_TransfersConsumer.cpp new file mode 100644 index 0000000000..f6bca31d8d --- /dev/null +++ b/tests/unit_tests/test_TransfersConsumer.cpp @@ -0,0 +1,904 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "cryptonote_core/TransactionApi.h" +#include "transfers/TransfersConsumer.h" + +#include +#include +#include +#include + +#include "INodeStubs.h" +#include "TransactionApiHelpers.h" +#include "TransfersObserver.h" +#include "TestBlockchainGenerator.h" + +using namespace CryptoNote; + +AccountSubscription getAccountSubscription(const AccountKeys& accountKeys) { + AccountSubscription subscription; + subscription.keys = accountKeys; + + return subscription; +} + +AccountKeys getAccountKeysWithViewKey(const PublicKey& publicViewKey, const SecretKey& secretViewKey) { + KeyPair viewKp; + viewKp.publicKey = publicViewKey; + viewKp.secretKey = secretViewKey; + AccountKeys accountKeys = accountKeysFromKeypairs(viewKp, generateKeys()); + + return accountKeys; +} + +cryptonote::Transaction convertTx(ITransactionReader& tx) { + auto blob = tx.getTransactionData(); + cryptonote::blobdata data(reinterpret_cast(blob.data()), blob.size()); + cryptonote::Transaction oldTx; + cryptonote::parse_and_validate_tx_from_blob(data, oldTx); // ignore return code + return oldTx; +} + +class TransfersConsumerTest : public ::testing::Test { +public: + TransfersConsumerTest(); + +protected: + + ITransfersSubscription& addSubscription(TransfersConsumer& consumer, const AccountKeys& acc, uint64_t height = 0, + uint64_t timestamp = 0, size_t age = 0) + { + AccountSubscription subscription = getAccountSubscription(acc); + subscription.syncStart.height = height; + subscription.syncStart.timestamp = timestamp; + subscription.transactionSpendableAge = age; + return consumer.addSubscription(subscription); + } + + ITransfersSubscription& addSubscription(const AccountKeys& acc, uint64_t height = 0, uint64_t timestamp = 0, size_t age = 0) { + return addSubscription(m_consumer, acc, height, timestamp, age); + } + + ITransfersSubscription& addSubscription(uint64_t height = 0, uint64_t timestamp = 0, size_t age = 0) { + return addSubscription(m_consumer, m_accountKeys, height, timestamp, age); + } + + ITransfersSubscription& addSubscription(TransfersConsumer& consumer, uint64_t height = 0, uint64_t timestamp = 0, size_t age = 0) { + return addSubscription(consumer, m_accountKeys, height, timestamp, age); + } + + AccountKeys generateAccount() { + return getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + } + + cryptonote::Currency m_currency; + TestBlockchainGenerator m_generator; + INodeTrivialRefreshStub m_node; + AccountKeys m_accountKeys; + TransfersConsumer m_consumer; +}; + +TransfersConsumerTest::TransfersConsumerTest() : + m_currency(cryptonote::CurrencyBuilder().currency()), + m_generator(m_currency), + m_node(m_generator), + m_accountKeys(generateAccountKeys()), + m_consumer(m_currency, m_node, m_accountKeys.viewSecretKey) +{ +} + +bool amountFound(const std::vector& outs, uint64_t amount) { + return std::find_if(outs.begin(), outs.end(), [amount] (const TransactionOutputInformation& inf) { return inf.amount == amount; }) != outs.end(); +} + +AccountSubscription getAccountSubscriptionWithSyncStart(const AccountKeys& keys, uint64_t timestamp, uint64_t height) { + AccountSubscription subscription = getAccountSubscription(keys); + subscription.syncStart.timestamp = timestamp; + subscription.syncStart.height = height; + + return subscription; +} + +TEST_F(TransfersConsumerTest, addSubscription_Success) { + AccountSubscription subscription; + subscription.keys = m_accountKeys; + + ITransfersSubscription& accountSubscription = m_consumer.addSubscription(subscription); + ASSERT_EQ(subscription.keys.address, accountSubscription.getAddress()); +} + +TEST_F(TransfersConsumerTest, addSubscription_WrongViewKey) { + AccountKeys accountKeys = generateAccountKeys(); + AccountSubscription subscription = getAccountSubscription(accountKeys); + + ASSERT_ANY_THROW(m_consumer.addSubscription(subscription)); +} + +TEST_F(TransfersConsumerTest, addSubscription_SameSubscription) { + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + ITransfersSubscription* ts1 = &m_consumer.addSubscription(subscription); + ITransfersSubscription* ts2 = &m_consumer.addSubscription(subscription); + + ASSERT_EQ(ts1, ts2); +} + +TEST_F(TransfersConsumerTest, removeSubscription_Success) { + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + m_consumer.addSubscription(subscription); + + ITransfersSubscription* ts = m_consumer.getSubscription(m_accountKeys.address); + ASSERT_NE(nullptr, ts); + + m_consumer.removeSubscription(m_accountKeys.address); + ts = m_consumer.getSubscription(m_accountKeys.address); + ASSERT_EQ(nullptr, ts); +} + +TEST_F(TransfersConsumerTest, removeSubscription_OneAddressLeft) { + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + m_consumer.addSubscription(subscription1); + + AccountKeys accountKeys = getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + AccountSubscription subscription2 = getAccountSubscription(accountKeys); + + m_consumer.addSubscription(subscription2); + + ASSERT_FALSE(m_consumer.removeSubscription(subscription1.keys.address)); +} + +TEST_F(TransfersConsumerTest, removeSubscription_RemoveAllAddresses) { + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + m_consumer.addSubscription(subscription1); + + ASSERT_TRUE(m_consumer.removeSubscription(subscription1.keys.address)); +} + +TEST_F(TransfersConsumerTest, getSubscription_ReturnSameValueForSameAddress) { + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + m_consumer.addSubscription(subscription); + + ITransfersSubscription* ts1 = m_consumer.getSubscription(m_accountKeys.address); + ITransfersSubscription* ts2 = m_consumer.getSubscription(m_accountKeys.address); + + ASSERT_EQ(ts1, ts2); +} + +TEST_F(TransfersConsumerTest, getSubscription_ReturnNullForNonExistentAddr) { + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + m_consumer.addSubscription(subscription1); + + AccountKeys accountKeys = getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + + ASSERT_EQ(nullptr, m_consumer.getSubscription(accountKeys.address)); +} + +TEST_F(TransfersConsumerTest, getSubscriptions_Empty) { + std::vector subscriptions; + m_consumer.getSubscriptions(subscriptions); + + ASSERT_TRUE(subscriptions.empty()); +} + +TEST_F(TransfersConsumerTest, getSubscriptions_TwoSubscriptions) { + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + m_consumer.addSubscription(subscription1); + + AccountKeys accountKeys = getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + AccountSubscription subscription2 = getAccountSubscription(accountKeys); + m_consumer.addSubscription(subscription2); + + std::vector subscriptions; + m_consumer.getSubscriptions(subscriptions); + + ASSERT_EQ(2, subscriptions.size()); + ASSERT_NE(subscriptions.end(), std::find(subscriptions.begin(), subscriptions.end(), subscription1.keys.address)); + ASSERT_NE(subscriptions.end(), std::find(subscriptions.begin(), subscriptions.end(), subscription2.keys.address)); +} + +TEST_F(TransfersConsumerTest, getSyncStart_Empty) { + auto syncStart = m_consumer.getSyncStart(); + + EXPECT_EQ(std::numeric_limits::max(), syncStart.height); + EXPECT_EQ(std::numeric_limits::max(), syncStart.timestamp); +} + +TEST_F(TransfersConsumerTest, getSyncStart_OneSubscription) { + const uint64_t height = 1209384; + const uint64_t timestamp = 99284512; + + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + subscription.syncStart.height = height; + subscription.syncStart.timestamp = timestamp; + + m_consumer.addSubscription(subscription); + + auto sync = m_consumer.getSyncStart(); + ASSERT_EQ(height, sync.height); + ASSERT_EQ(timestamp, sync.timestamp); +} + +TEST_F(TransfersConsumerTest, getSyncStart_MinSyncSameSubscription) { + const uint64_t height = 1209384; + const uint64_t timestamp = 99284512; + const uint64_t minHeight = 120984; + const uint64_t minTimestamp = 9984512; + + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + subscription1.syncStart.height = height; + subscription1.syncStart.timestamp = timestamp; + + AccountKeys accountKeys = getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + AccountSubscription subscription2 = getAccountSubscription(accountKeys); + + subscription2.syncStart.height = minHeight; + subscription2.syncStart.timestamp = minTimestamp; + + m_consumer.addSubscription(subscription1); + m_consumer.addSubscription(subscription2); + + auto sync = m_consumer.getSyncStart(); + ASSERT_EQ(minHeight, sync.height); + ASSERT_EQ(minTimestamp, sync.timestamp); +} + +TEST_F(TransfersConsumerTest, getSyncStart_MinSyncDifferentSubscriptions) { + const uint64_t height = 1209384; + const uint64_t timestamp = 99284512; + const uint64_t minHeight = 120984; + const uint64_t minTimestamp = 9984512; + + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + subscription1.syncStart.height = minHeight; + subscription1.syncStart.timestamp = timestamp; + + AccountKeys accountKeys = getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + AccountSubscription subscription2 = getAccountSubscription(accountKeys); + + subscription2.syncStart.height = height; + subscription2.syncStart.timestamp = minTimestamp; + + m_consumer.addSubscription(subscription1); + m_consumer.addSubscription(subscription2); + + auto sync = m_consumer.getSyncStart(); + ASSERT_EQ(minHeight, sync.height); + ASSERT_EQ(minTimestamp, sync.timestamp); +} + +TEST_F(TransfersConsumerTest, getSyncStart_RemoveMinSyncSubscription) { + const uint64_t height = 1209384; + const uint64_t timestamp = 99284512; + const uint64_t minHeight = 120984; + const uint64_t minTimestamp = 9984512; + + AccountSubscription subscription1 = getAccountSubscription(m_accountKeys); + subscription1.syncStart.height = height; + subscription1.syncStart.timestamp = timestamp; + + AccountKeys accountKeys = getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); + AccountSubscription subscription2 = getAccountSubscription(accountKeys); + + subscription2.syncStart.height = minHeight; + subscription2.syncStart.timestamp = minTimestamp; + + m_consumer.addSubscription(subscription1); + m_consumer.addSubscription(subscription2); + m_consumer.removeSubscription(subscription2.keys.address); + + auto sync = m_consumer.getSyncStart(); + ASSERT_EQ(height, sync.height); + ASSERT_EQ(timestamp, sync.timestamp); +} + +TEST_F(TransfersConsumerTest, onBlockchainDetach) { + auto& container1 = addSubscription().getContainer(); + auto keys = generateAccount(); + auto& container2 = addSubscription(keys).getContainer(); + + std::shared_ptr tx1 = createTransaction(); + addTestInput(*tx1, 100); + addTestKeyOutput(*tx1, 50, 1, m_accountKeys); + + std::shared_ptr tx2 = createTransaction(); + addTestInput(*tx1, 100); + addTestKeyOutput(*tx1, 50, 1, keys); + + CompleteBlock blocks[3]; + blocks[0].block = cryptonote::Block(); + blocks[0].block->timestamp = 1233; + + blocks[1].block = cryptonote::Block(); + blocks[1].block->timestamp = 1234; + blocks[1].transactions.push_back(tx1); + + blocks[2].block = cryptonote::Block(); + blocks[2].block->timestamp = 1235; + blocks[2].transactions.push_back(tx2); + + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 3)); + + m_consumer.onBlockchainDetach(0); + std::vector trs; + container1.getOutputs(trs, ITransfersContainer::IncludeAll); + ASSERT_EQ(0, trs.size()); + + container2.getOutputs(trs, ITransfersContainer::IncludeAll); + ASSERT_EQ(0, trs.size()); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_OneEmptyBlockOneFilled) { + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + subscription.syncStart.height = 1; + subscription.syncStart.timestamp = 1234; + + std::shared_ptr ignoredTx(createTransaction()); + addTestInput(*ignoredTx, 1000); + addTestKeyOutput(*ignoredTx, 123, 1, m_accountKeys); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 900, 2, m_accountKeys); + addTestKeyOutput(*tx, 850, 3, m_accountKeys); + + CompleteBlock blocks[2]; + blocks[0].transactions.push_back(ignoredTx); + blocks[1].block = cryptonote::Block(); + blocks[1].block->timestamp = 1235; + blocks[1].transactions.push_back(tx); + + ITransfersContainer& container = m_consumer.addSubscription(subscription).getContainer(); + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 1, 2)); + + auto outs = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_TRUE(amountFound(outs, 850)); + ASSERT_TRUE(amountFound(outs, 900)); + + auto ignoredOuts = container.getTransactionOutputs(ignoredTx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(0, ignoredOuts.size()); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_DifferentTimestamps) { + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + subscription.syncStart.timestamp = 12345; + subscription.syncStart.height = 12; + + std::shared_ptr ignoredTx(createTransaction()); + addTestInput(*ignoredTx, 1000); + addTestKeyOutput(*ignoredTx, 123, 1, m_accountKeys); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 900, 2, m_accountKeys); + addTestKeyOutput(*tx, 850, 3, m_accountKeys); + + CompleteBlock blocks[2]; + blocks[0].transactions.push_back(ignoredTx); + blocks[0].block = cryptonote::Block(); + blocks[0].block->timestamp = subscription.syncStart.timestamp - 1; + + blocks[1].block = cryptonote::Block(); + blocks[1].block->timestamp = subscription.syncStart.timestamp; + blocks[1].transactions.push_back(tx); + + ITransfersContainer& container = m_consumer.addSubscription(subscription).getContainer(); + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 2, 2)); + + auto ignoredOuts = container.getTransactionOutputs(ignoredTx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(0, ignoredOuts.size()); + + auto outs = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_TRUE(amountFound(outs, 850)); + ASSERT_TRUE(amountFound(outs, 900)); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesError) { + class INodeGlobalIndicesStub: public INodeDummyStub { + public: + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, + std::vector& outsGlobalIndices, const Callback& callback) override { + callback(std::make_error_code(std::errc::operation_canceled)); + }; + }; + + INodeGlobalIndicesStub node; + + TransfersConsumer consumer(m_currency, node, m_accountKeys.viewSecretKey); + + auto subscription = getAccountSubscriptionWithSyncStart(m_accountKeys, 1234, 10); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 900, 2, m_accountKeys); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = subscription.syncStart.timestamp; + block.transactions.push_back(tx); + + consumer.addSubscription(subscription); + ASSERT_FALSE(consumer.onNewBlocks(&block, subscription.syncStart.height, 1)); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) { + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + subscription.syncStart.timestamp = 2131; + subscription.syncStart.height = 32; + subscription.transactionSpendableAge = 5; + + auto& container = m_consumer.addSubscription(subscription).getContainer(); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 900, 0, m_accountKeys); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = subscription.syncStart.timestamp; + block.transactions.push_back(tx); + + ASSERT_TRUE(m_consumer.onNewBlocks(&block, subscription.syncStart.height, 1)); + ASSERT_EQ(900, container.balance(ITransfersContainer::IncludeAllLocked)); + + std::unique_ptr blocks(new CompleteBlock[subscription.transactionSpendableAge]); + for (size_t i = 0; i < subscription.transactionSpendableAge; ++i) { + blocks[i].block = cryptonote::Block(); + auto tr = createTransaction(); + addTestInput(*tr, 1000); + addTestKeyOutput(*tr, 100, i + 1, generateAccountKeys()); + } + + ASSERT_TRUE(m_consumer.onNewBlocks(blocks.get(), subscription.syncStart.height + 1, subscription.transactionSpendableAge)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(900, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_DifferentSubscribers) { + auto& container1 = addSubscription().getContainer(); + + auto keys = generateAccount(); + auto& container2 = addSubscription(keys).getContainer(); + + uint64_t amount1 = 900; + uint64_t amount2 = 850; + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, amount1, 0, m_accountKeys); + addTestKeyOutput(*tx, amount2, 1, keys); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = 0; + block.transactions.push_back(tx); + + ASSERT_TRUE(m_consumer.onNewBlocks(&block, 0, 1)); + auto outs1 = container1.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(1, outs1.size()); + ASSERT_EQ(amount1, outs1[0].amount); + + auto outs2 = container2.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(1, outs2.size()); + ASSERT_EQ(amount2, outs2[0].amount); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_MultisignatureTransaction) { + auto& container1 = addSubscription().getContainer(); + + auto keys = generateAccount(); + + auto keys2 = generateAccount(); + auto keys3 = generateAccount(); + + uint64_t amount = 900; + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + tx->addOutput(amount, { m_accountKeys.address, keys.address, keys2.address } , 3); + tx->addOutput(800, { keys.address, keys2.address, keys3.address }, 3); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = 0; + block.transactions.push_back(tx); + + ASSERT_TRUE(m_consumer.onNewBlocks(&block, 0, 1)); + auto outs1 = container1.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(1, outs1.size()); + ASSERT_EQ(amount, outs1[0].amount); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesIsProperlyCalled) { + class INodeGlobalIndicesStub: public INodeDummyStub { + public: + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, + std::vector& outsGlobalIndices, const Callback& callback) override { + outsGlobalIndices.push_back(3); + hash = transactionHash; + callback(std::error_code()); + }; + + crypto::hash hash; + }; + + INodeGlobalIndicesStub node; + TransfersConsumer consumer(m_currency, node, m_accountKeys.viewSecretKey); + + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + subscription.syncStart.height = 0; + subscription.syncStart.timestamp = 0; + consumer.addSubscription(subscription); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 900, 2, m_accountKeys); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = 0; + block.transactions.push_back(tx); + + ASSERT_TRUE(consumer.onNewBlocks(&block, 1, 1)); + const CryptoNote::Hash &hash = tx->getTransactionHash(); + const crypto::hash expectedHash = *reinterpret_cast(&hash); + ASSERT_EQ(expectedHash, node.hash); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesIsNotCalled) { + class INodeGlobalIndicesStub: public INodeDummyStub { + public: + INodeGlobalIndicesStub() : called(false) {}; + + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, + std::vector& outsGlobalIndices, const Callback& callback) override { + outsGlobalIndices.push_back(3); + called = true; + callback(std::error_code()); + }; + + bool called; + }; + + INodeGlobalIndicesStub node; + TransfersConsumer consumer(m_currency, node, m_accountKeys.viewSecretKey); + + AccountSubscription subscription = getAccountSubscription(m_accountKeys); + subscription.syncStart.height = 0; + subscription.syncStart.timestamp = 0; + consumer.addSubscription(subscription); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 900, 2, generateAccount()); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = 0; + block.transactions.push_back(tx); + ASSERT_TRUE(consumer.onNewBlocks(&block, 1, 1)); + + ASSERT_FALSE(node.called); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_markTransactionConfirmed) { + auto& container = addSubscription().getContainer(); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 10000, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, m_accountKeys); + + m_consumer.onPoolUpdated({convertTx(*tx)}, {}); + + auto lockedOuts = container.getTransactionOutputs(tx->getTransactionHash(), + ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeKey); + ASSERT_EQ(1, lockedOuts.size()); + ASSERT_EQ(10000, lockedOuts[0].amount); + + CompleteBlock blocks[2]; + blocks[0].block = cryptonote::Block(); + blocks[0].block->timestamp = 0; + blocks[0].transactions.push_back(tx); + blocks[1].block = cryptonote::Block(); + blocks[1].block->timestamp = 0; + blocks[1].transactions.push_back(createTransaction()); + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 2)); + + auto softLockedOuts = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeKeyUnlocked); + ASSERT_EQ(1, softLockedOuts.size()); + ASSERT_EQ(10000, softLockedOuts[0].amount); +} + +class INodeGlobalIndexStub: public INodeDummyStub { +public: + + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, + std::vector& outsGlobalIndices, const Callback& callback) override { + outsGlobalIndices.push_back(globalIndex); + callback(std::error_code()); + }; + + uint64_t globalIndex; +}; + +TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionOutputInformation) { + const uint64_t index = 2; + + INodeGlobalIndexStub node; + TransfersConsumer consumer(m_currency, node, m_accountKeys.viewSecretKey); + + node.globalIndex = index; + + auto& container = addSubscription(consumer).getContainer(); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + auto out = addTestKeyOutput(*tx, 10000, index, m_accountKeys); + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = 0; + block.transactions.push_back(tx); + ASSERT_TRUE(consumer.onNewBlocks(&block, 0, 1)); + + auto outs = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(1, outs.size()); + + auto& o = outs[0]; + + ASSERT_EQ(out.type, o.type); + ASSERT_EQ(out.amount, o.amount); + ASSERT_EQ(out.outputKey, o.outputKey); + ASSERT_EQ(out.globalOutputIndex, o.globalOutputIndex); + ASSERT_EQ(out.outputInTransaction, o.outputInTransaction); + ASSERT_EQ(out.transactionPublicKey, o.transactionPublicKey); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionOutputInformationMultisignature) { + const uint64_t index = 2; + + INodeGlobalIndexStub node; + TransfersConsumer consumer(m_currency, node, m_accountKeys.viewSecretKey); + + node.globalIndex = index; + + auto& container = addSubscription(consumer).getContainer(); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + uint32_t txIndex = tx->addOutput(300, { m_accountKeys.address, generateAccountKeys().address}, 2); + + TransactionOutputInformation expectedOut; + expectedOut.type = TransactionTypes::OutputType::Multisignature; + expectedOut.amount = 300; + expectedOut.globalOutputIndex = index; + expectedOut.outputInTransaction = txIndex; + expectedOut.transactionPublicKey = tx->getTransactionPublicKey(); + expectedOut.requiredSignatures = 2; + + CompleteBlock block; + block.block = cryptonote::Block(); + block.block->timestamp = 0; + block.transactions.push_back(tx); + ASSERT_TRUE(consumer.onNewBlocks(&block, 0, 1)); + + auto outs = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(1, outs.size()); + + auto& o = outs[0]; + ASSERT_EQ(expectedOut.type, o.type); + ASSERT_EQ(expectedOut.amount, o.amount); + ASSERT_EQ(expectedOut.requiredSignatures, o.requiredSignatures); + ASSERT_EQ(expectedOut.globalOutputIndex, o.globalOutputIndex); + ASSERT_EQ(expectedOut.outputInTransaction, o.outputInTransaction); + ASSERT_EQ(expectedOut.transactionPublicKey, o.transactionPublicKey); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionInformation) { + auto& container = addSubscription().getContainer(); + + std::shared_ptr tx(createTransaction()); + addTestInput(*tx, 10000); + addTestKeyOutput(*tx, 1000, 2, m_accountKeys); + Hash paymentId = crypto::rand(); + uint64_t unlockTime = 10; + tx->setPaymentId(paymentId); + tx->setUnlockTime(unlockTime); + + CompleteBlock blocks[2]; + blocks[0].block = cryptonote::Block(); + blocks[0].block->timestamp = 0; + blocks[0].transactions.push_back(createTransaction()); + + blocks[1].block = cryptonote::Block(); + blocks[1].block->timestamp = 11; + blocks[1].transactions.push_back(tx); + + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 2)); + + TransactionInformation info; + int64_t balance; + ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), info, balance)); + + ASSERT_EQ(tx->getTransactionHash(), info.transactionHash); + ASSERT_EQ(tx->getTransactionPublicKey(), info.publicKey); + ASSERT_EQ(1, info.blockHeight); + ASSERT_EQ(11, info.timestamp); + ASSERT_EQ(unlockTime, info.unlockTime); + ASSERT_EQ(10000, info.totalAmountIn); + ASSERT_EQ(1000, info.totalAmountOut); + ASSERT_EQ(paymentId, info.paymentId); +} + +TEST_F(TransfersConsumerTest, onNewBlocks_manyBlocks) { + const size_t blocksCount = 1000; + const size_t txPerBlock = 10; + + auto& container = addSubscription().getContainer(); + + std::vector blocks(blocksCount); + + uint64_t timestamp = 10000; + uint64_t expectedAmount = 0; + size_t expectedTransactions = 0; + uint64_t globalOut = 0; + size_t blockIdx = 0; + + for (auto& b : blocks) { + b.block = cryptonote::Block(); + b.block->timestamp = timestamp++; + + if (++blockIdx % 10 == 0) { + for (size_t i = 0; i < txPerBlock; ++i) { + auto tx = createTransaction(); + addTestInput(*tx, 10000); + if ((i % 3) == 0) { + addTestKeyOutput(*tx, 1000, ++globalOut, m_accountKeys); + addTestKeyOutput(*tx, 2000, ++globalOut, m_accountKeys); + expectedAmount += 3000; + ++expectedTransactions; + } + + b.transactions.push_back(std::move(tx)); + } + } + } + + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size())); + + ASSERT_EQ(expectedTransactions, container.transactionsCount()); + ASSERT_EQ(expectedAmount, container.balance(ITransfersContainer::IncludeAll)); +} + + + +TEST_F(TransfersConsumerTest, onPoolUpdated_addTransaction) { + auto& sub = addSubscription(); + + // construct tx + auto tx = createTransaction(); + addTestInput(*tx, 10000); + auto out = addTestKeyOutput(*tx, 10000, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, m_accountKeys); + + m_consumer.onPoolUpdated({convertTx(*tx)}, {}); + + auto outputs = sub.getContainer().getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + + ASSERT_EQ(1, outputs.size()); + + auto& o = outputs[0]; + + ASSERT_EQ(out.type, o.type); + ASSERT_EQ(out.amount, o.amount); + ASSERT_EQ(out.outputKey, o.outputKey); + ASSERT_EQ(UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, o.globalOutputIndex); +} + +TEST_F(TransfersConsumerTest, onPoolUpdated_addTransactionMultisignature) { + auto& sub = addSubscription(); + + // construct tx with multisignature output + auto tx = createTransaction(); + addTestInput(*tx, 10000); + auto addresses = { m_accountKeys.address, generateAccountKeys().address }; + tx->addOutput(10000, addresses, 1); + + m_consumer.onPoolUpdated({ convertTx(*tx) }, {}); + + auto outputs = sub.getContainer().getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + + ASSERT_EQ(1, outputs.size()); + + auto& o = outputs[0]; + + TransactionTypes::OutputMultisignature out; + tx->getOutput(0, out); + + ASSERT_EQ(TransactionTypes::OutputType::Multisignature, o.type); + ASSERT_EQ(out.amount, o.amount); + ASSERT_EQ(out.requiredSignatures, o.requiredSignatures); + ASSERT_EQ(UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, o.globalOutputIndex); +} + + +TEST_F(TransfersConsumerTest, onPoolUpdated_addTransactionDoesNotGetsGlobalIndices) { + auto& sub = addSubscription(); + // construct tx + auto tx = createTransaction(); + addTestInput(*tx, 10000); + auto out = addTestKeyOutput(*tx, 10000, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, m_accountKeys); + m_consumer.onPoolUpdated({ convertTx(*tx) }, {}); + + ASSERT_TRUE(m_node.calls_getTransactionOutsGlobalIndices.empty()); +} + +TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransaction) { + auto& sub = addSubscription(); + TransfersObserver observer; + sub.addObserver(&observer); + + std::vector deleted = { + crypto::rand(), + crypto::rand() + }; + + m_consumer.onPoolUpdated({}, deleted); + + ASSERT_EQ(deleted.size(), observer.deleted.size()); + ASSERT_EQ(reinterpret_cast(deleted[0]), observer.deleted[0]); + ASSERT_EQ(reinterpret_cast(deleted[1]), observer.deleted[1]); +} + +TEST_F(TransfersConsumerTest, getKnownPoolTxIds_empty) { + auto& sub = addSubscription(); + std::vector ids; + m_consumer.getKnownPoolTxIds(ids); + ASSERT_TRUE(ids.empty()); +} + +std::unique_ptr createTransactionTo(const AccountKeys& to, uint64_t amountIn, uint64_t amountOut) { + auto tx = createTransaction(); + addTestInput(*tx, amountIn); + addTestKeyOutput(*tx, amountOut, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, to); + return tx; +} + +TEST_F(TransfersConsumerTest, getKnownPoolTxIds_returnsUnconfirmed) { + auto acc1 = generateAccount(); + auto acc2 = generateAccount(); + + auto& sub1 = addSubscription(acc1); + auto& sub2 = addSubscription(acc2); + + std::vector> txs; + txs.push_back(createTransactionTo(acc1, 10000, 10000)); + txs.push_back(createTransactionTo(acc1, 20000, 20000)); + txs.push_back(createTransactionTo(acc2, 30000, 30000)); + + m_consumer.onPoolUpdated({ convertTx(*txs[0]), convertTx(*txs[1]), convertTx(*txs[2])}, {}); + + std::vector ids; + m_consumer.getKnownPoolTxIds(ids); + + ASSERT_EQ(3, ids.size()); + + for (int i = 0; i < 3; ++i) { + auto txhash = txs[i]->getTransactionHash(); + ASSERT_TRUE(std::find(ids.begin(), ids.end(), reinterpret_cast(txhash)) != ids.end()); + } +} diff --git a/tests/unit_tests/test_TransfersContainer.cpp b/tests/unit_tests/test_TransfersContainer.cpp new file mode 100644 index 0000000000..6269bb4c02 --- /dev/null +++ b/tests/unit_tests/test_TransfersContainer.cpp @@ -0,0 +1,1209 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "IWallet.h" + +#include "crypto/crypto.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/TransactionApi.h" +#include "transfers/TransfersContainer.h" + +#include "TransactionApiHelpers.h" + +using namespace CryptoNote; +using namespace cryptonote; + + +namespace { + const size_t TEST_TRANSACTION_SPENDABLE_AGE = 1; + const uint64_t TEST_OUTPUT_AMOUNT = 100; + const uint64_t TEST_BLOCK_HEIGHT = 99; + const uint64_t TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX = 113; + + AccountAddress generateAddress() { + account_base account; + account.generate(); + return reinterpret_cast(account.get_keys().m_account_address); + } + + void addInput(ITransaction& tx, const AccountKeys& senderKeys, const TransactionOutputInformation& t) { + CryptoNote::KeyPair kp; + TransactionTypes::InputKeyInfo info; + info.amount = t.amount; + + TransactionTypes::GlobalOutput globalOut; + globalOut.outputIndex = t.globalOutputIndex; + globalOut.targetKey = t.outputKey; + info.outputs.push_back(globalOut); + + info.realOutput.outputInTransaction = t.outputInTransaction; + info.realOutput.transactionIndex = 0; + info.realOutput.transactionPublicKey = t.transactionPublicKey; + + tx.addInput(senderKeys, info, kp); + } + + TransactionOutputInformationIn addTestMultisignatureOutput(ITransaction& transaction, uint64_t amount, + uint64_t globalOutputIndex) { + std::vector addresses; + addresses.emplace_back(generateAddress()); + addresses.emplace_back(generateAddress()); + + uint32_t index = static_cast(transaction.addOutput(amount, addresses, static_cast(addresses.size()))); + + TransactionTypes::OutputMultisignature output; + transaction.getOutput(index, output); + + TransactionOutputInformationIn outputInfo; + outputInfo.type = TransactionTypes::OutputType::Multisignature; + outputInfo.amount = output.amount; + outputInfo.globalOutputIndex = globalOutputIndex; + outputInfo.outputInTransaction = index; + outputInfo.transactionPublicKey = transaction.getTransactionPublicKey(); + // Doesn't used in multisignature output, so can contain garbage + outputInfo.keyImage = generateKeyImage(); + outputInfo.requiredSignatures = output.requiredSignatures; + + return outputInfo; + } + + + class TransfersContainerTest : public ::testing::Test { + public: + enum : uint64_t { + TEST_CONTAINER_CURRENT_HEIGHT = 1000 + }; + + TransfersContainerTest() : + currency(CurrencyBuilder().currency()), + container(currency, TEST_TRANSACTION_SPENDABLE_AGE), + account(generateAccountKeys()) { + } + + protected: + + BlockInfo blockInfo(uint64_t height) const { + return BlockInfo{ height, 1000000 }; + } + + std::unique_ptr addTransaction(uint64_t height = UNCONFIRMED_TRANSACTION_HEIGHT, + uint64_t outputAmount = TEST_OUTPUT_AMOUNT) { + auto tx = createTransaction(); + addTestInput(*tx, outputAmount + 1); + auto outputIndex = (height == UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; + auto outInfo = addTestKeyOutput(*tx, outputAmount, outputIndex, account); + std::vector outputs = { outInfo }; + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, outputs)); + return tx; + } + + std::unique_ptr addSpendingTransaction(const Hash& sourceTx, uint64_t height, uint64_t outputIndex, uint64_t amount = TEST_OUTPUT_AMOUNT) { + std::unique_ptr tx; + auto outputs = container.getTransactionOutputs(sourceTx, ITransfersContainer::IncludeTypeAll | + ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeStateSoftLocked); + + EXPECT_FALSE(outputs.empty()); + + if (outputs.empty()) + return tx; + + tx = createTransaction(); + + size_t inputAmount = 0; + for (const auto& t : outputs) { + inputAmount += t.amount; + addInput(*tx, account, t); + } + + EXPECT_GE(inputAmount, amount); + + std::vector transfers; + + addTestKeyOutput(*tx, amount, outputIndex); // output to some random address + + if (inputAmount > amount) { + transfers.emplace_back(addTestKeyOutput(*tx, inputAmount - amount, outputIndex + 1, account)); // change + } + + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, transfers)); + + return tx; + } + + Currency currency; + TransfersContainer container; + AccountKeys account; + }; + +} + +//--------------------------------------------------------------------------- +// TransfersContainer_addTransaction +//--------------------------------------------------------------------------- +class TransfersContainer_addTransaction : public TransfersContainerTest {}; + +TEST_F(TransfersContainer_addTransaction, orderIsRequired_sameHeight) { + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); +} + +TEST_F(TransfersContainer_addTransaction, orderIsRequired_confirmed) { + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT + 1)); + ASSERT_ANY_THROW(addTransaction(TEST_BLOCK_HEIGHT)); +} + +TEST_F(TransfersContainer_addTransaction, orderIsRequired_unconfirmedAtAnyHeight) { + ASSERT_NO_THROW(addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT)); + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); + ASSERT_NO_THROW(addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT)); + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT + 1)); + ASSERT_NO_THROW(addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT)); +} + +TEST_F(TransfersContainer_addTransaction, orderIsRequired_afterDetach) { + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT + 1)); + container.detach(TEST_BLOCK_HEIGHT + 1); + ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); +} + + +TEST_F(TransfersContainer_addTransaction, addingTransactionTwiceCausesException) { + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, { outInfo })); + ASSERT_ANY_THROW(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT + 1), *tx, { outInfo })); +} + +TEST_F(TransfersContainer_addTransaction, addingTwoIdenticalUnconfirmedMultisignatureOutputsDoesNotCauseException) { + + CryptoNote::BlockInfo blockInfo{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + + auto tx1 = createTransaction(); + addTestInput(*tx1, TEST_OUTPUT_AMOUNT + 1); + auto outInfo1 = addTestMultisignatureOutput(*tx1, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs1; + outputs1.emplace_back(outInfo1); + + ASSERT_TRUE(container.addTransaction(blockInfo, *tx1, outputs1)); + + auto tx2 = createTransaction(); + addTestInput(*tx2, TEST_OUTPUT_AMOUNT + 1); + auto outInfo2 = addTestMultisignatureOutput(*tx2, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs2; + outputs2.emplace_back(outInfo2); + + ASSERT_TRUE(container.addTransaction(blockInfo, *tx2, outputs2)); + + container.advanceHeight(1000); + + ASSERT_EQ(2, container.transfersCount()); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(2 * TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIdenticalAnotherUnspentOuputCausesException) { + CryptoNote::BlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 }; + + auto tx1 = createTransaction(); + addTestInput(*tx1, TEST_OUTPUT_AMOUNT + 1); + auto outInfo1 = addTestMultisignatureOutput(*tx1, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs1; + outputs1.emplace_back(outInfo1); + + ASSERT_TRUE(container.addTransaction(blockInfo, *tx1, outputs1)); + + auto tx2 = createTransaction(); + addTestInput(*tx2, TEST_OUTPUT_AMOUNT + 1); + auto outInfo2 = addTestMultisignatureOutput(*tx2, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs2; + outputs2.emplace_back(outInfo2); + + ASSERT_ANY_THROW(container.addTransaction(blockInfo, *tx2, outputs2)); + + container.advanceHeight(1000); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_addTransaction, addingConfirmedMultisignatureOutputIdenticalAnotherSpentOuputCausesException) { + CryptoNote::BlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 }; + auto tx1 = createTransaction(); + addTestInput(*tx1, TEST_OUTPUT_AMOUNT + 1); + auto outInfo1 = addTestMultisignatureOutput(*tx1, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs1; + outputs1.emplace_back(outInfo1); + + ASSERT_TRUE(container.addTransaction(blockInfo1, *tx1, outputs1)); + + // Spend output + CryptoNote::BlockInfo blockInfo2{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + auto tx2 = createTransaction(); + TransactionTypes::InputMultisignature input2; + input2.amount = TEST_OUTPUT_AMOUNT; + input2.outputIndex = outInfo1.globalOutputIndex; + input2.signatures = outInfo1.requiredSignatures; + tx2->addInput(input2); + ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2, std::vector())); + + CryptoNote::BlockInfo blockInfo3{ TEST_BLOCK_HEIGHT + 3, 1000000 }; + auto tx3 = createTransaction(); + addTestInput(*tx3, TEST_OUTPUT_AMOUNT + 1); + auto outInfo3 = addTestMultisignatureOutput(*tx3, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs3; + outputs3.emplace_back(outInfo3); + + ASSERT_ANY_THROW(container.addTransaction(blockInfo3, *tx3, outputs3)); + + container.advanceHeight(1000); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_addTransaction, addingConfirmedBlockAndUnconfirmedOutputCausesException) { + CryptoNote::BlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 }; + + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs; + outputs.emplace_back(outInfo); + + ASSERT_ANY_THROW(container.addTransaction(blockInfo, *tx, outputs)); +} + +TEST_F(TransfersContainer_addTransaction, addingUnconfirmedBlockAndConfirmedOutputCausesException) { + CryptoNote::BlockInfo blockInfo{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs; + outputs.emplace_back(outInfo); + + ASSERT_ANY_THROW(container.addTransaction(blockInfo, *tx, outputs)); +} + +TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputToKey) { + CryptoNote::BlockInfo blockInfo{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs; + outputs.emplace_back(outInfo); + + ASSERT_TRUE(container.addTransaction(blockInfo, *tx, outputs)); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeAllLocked); + ASSERT_EQ(1, transfers.size()); + + transfers.clear(); + container.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + ASSERT_TRUE(transfers.empty()); + + transfers = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAllLocked); + ASSERT_EQ(1, transfers.size()); + + transfers = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAllUnlocked); + ASSERT_TRUE(transfers.empty()); + + TransactionInformation txInfo; + int64_t txBalance; + ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance)); + ASSERT_EQ(blockInfo.height, txInfo.blockHeight); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, txBalance); + + std::vector unconfirmedTransactions; + container.getUnconfirmedTransactions(unconfirmedTransactions); + ASSERT_EQ(1, unconfirmedTransactions.size()); +} + +TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputToKey) { + CryptoNote::BlockInfo blockInfo{ TEST_BLOCK_HEIGHT, 1000000 }; + + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs; + outputs.emplace_back(outInfo); + + ASSERT_TRUE(container.addTransaction(blockInfo, *tx, outputs)); + + container.advanceHeight(1000); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeAllLocked); + ASSERT_TRUE(transfers.empty()); + + transfers.clear(); + container.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + ASSERT_EQ(1, transfers.size()); + + transfers = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAllLocked); + ASSERT_TRUE(transfers.empty()); + + transfers = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAllUnlocked); + ASSERT_EQ(1, transfers.size()); + + TransactionInformation txInfo; + int64_t txBalance; + ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance)); + ASSERT_EQ(blockInfo.height, txInfo.blockHeight); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, txBalance); + + std::vector unconfirmedTransactions; + container.getUnconfirmedTransactions(unconfirmedTransactions); + ASSERT_TRUE(unconfirmedTransactions.empty()); +} + +TEST_F(TransfersContainer_addTransaction, addingEmptyTransactionOuptutsDoesNotChaingeContainer) { + CryptoNote::BlockInfo blockInfo{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + + std::vector outputs; + + ASSERT_FALSE(container.addTransaction(blockInfo, *tx, outputs)); + + ASSERT_EQ(0, container.transfersCount()); + ASSERT_EQ(0, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeAllLocked); + ASSERT_TRUE(transfers.empty()); + + transfers.clear(); + container.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + ASSERT_TRUE(transfers.empty()); + + transfers = container.getTransactionOutputs(tx->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_TRUE(transfers.empty()); + + TransactionInformation txInfo; + int64_t txBalance; + ASSERT_FALSE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance)); + + std::vector unconfirmedTransactions; + container.getUnconfirmedTransactions(unconfirmedTransactions); + ASSERT_TRUE(unconfirmedTransactions.empty()); +} + + +TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputMultisignature) { + auto tx = createTransaction(); + auto out = addTestMultisignatureOutput(*tx, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + + ASSERT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *tx, { out })); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeTypeMultisignature | ITransfersContainer::IncludeStateLocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeTypeMultisignature | ITransfersContainer::IncludeStateUnlocked)); +} + +TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputMultisignature) { + auto tx = createTransaction(); + auto out = addTestMultisignatureOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, { out })); + + container.advanceHeight(1000); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeTypeMultisignature | ITransfersContainer::IncludeStateUnlocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeTypeMultisignature | ITransfersContainer::IncludeStateLocked)); +} + +TEST_F(TransfersContainer_addTransaction, addingConfirmedOutputMultisignatureTwiceFails) { + + { + auto tx = createTransaction(); + auto out = addTestMultisignatureOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, { out })); + } + + { + auto tx = createTransaction(); + auto out = addTestMultisignatureOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_ANY_THROW(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT + 1), *tx, { out })); + } +} + + +TEST_F(TransfersContainer_addTransaction, ignoresUnrelatedTransactionsWithKeyInput) { + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT); + ASSERT_FALSE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, {})); +} + +TEST_F(TransfersContainer_addTransaction, ignoresUnrelatedTransactionsWithMultisignatureInput) { + auto tx = createTransaction(); + + TransactionTypes::InputMultisignature input; + input.amount = TEST_OUTPUT_AMOUNT; + input.outputIndex = TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; + input.signatures = 1; + + tx->addInput(input); + + ASSERT_FALSE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, {})); +} + +TEST_F(TransfersContainer_addTransaction, spendingUnconfirmedOutputFails) { + auto tx = addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); + + auto outputs = container.getTransactionOutputs( + tx->getTransactionHash(), ITransfersContainer::IncludeAll); + + ASSERT_EQ(1, outputs.size()); + + auto spendingTx = createTransaction(); + for (const auto& t : outputs) { + addInput(*spendingTx, account, t); + } + + ASSERT_ANY_THROW(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *spendingTx, {})); +} + +TEST_F(TransfersContainer_addTransaction, spendingConfirmedOutputWithUnconfirmedTxSucceed) { + auto tx = addTransaction(TEST_BLOCK_HEIGHT); + container.advanceHeight(1000); + auto spendingTx = addSpendingTransaction(tx->getTransactionHash(), + UNCONFIRMED_TRANSACTION_HEIGHT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); // no new outputs + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_addTransaction, spendingConfirmedOutputWithConfirmedTxSucceed) { + auto tx = addTransaction(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + auto spendingTx = addSpendingTransaction(tx->getTransactionHash(), + TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX + 1); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); // no new outputs + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + + +TEST_F(TransfersContainer_addTransaction, spendingConfirmedMultisignatureOutputWithUnconfirmedTxSucceed) { + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto out = addTestMultisignatureOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, { out })); + + container.advanceHeight(1000); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + auto spendingTx = createTransaction(); + TransactionTypes::InputMultisignature msigInput{ TEST_OUTPUT_AMOUNT, 2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX }; + spendingTx->addInput(msigInput); + + ASSERT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *spendingTx, {})); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_addTransaction, spendingConfirmedMultisignatureOutputWithConfirmedTxSucceed) { + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto out = addTestMultisignatureOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, { out })); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + auto spendingTx = createTransaction(); + TransactionTypes::InputMultisignature msigInput{ TEST_OUTPUT_AMOUNT, 2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX }; + spendingTx->addInput(msigInput); + + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE), *spendingTx, {})); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + + +//--------------------------------------------------------------------------- +// TransfersContainer_deleteUnconfirmedTransaction +//--------------------------------------------------------------------------- +class TransfersContainer_deleteUnconfirmedTransaction : public TransfersContainerTest{}; + +TEST_F(TransfersContainer_deleteUnconfirmedTransaction, tryDeleteNonExistingTx) { + addTransaction(); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_FALSE(container.deleteUnconfirmedTransaction(crypto::rand())); + ASSERT_EQ(1, container.transactionsCount()); +} + +TEST_F(TransfersContainer_deleteUnconfirmedTransaction, tryDeleteConfirmedTx) { + auto txHash = addTransaction(TEST_BLOCK_HEIGHT)->getTransactionHash(); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_FALSE(container.deleteUnconfirmedTransaction(txHash)); + ASSERT_EQ(1, container.transactionsCount()); +} + +TEST_F(TransfersContainer_deleteUnconfirmedTransaction, deleteUnconfirmedSpendingTx) { + addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + + ASSERT_EQ(1, transfers.size()); + + auto spendingTx = createTransaction(); + + { + CryptoNote::BlockInfo blockInfo{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + addInput(*spendingTx, account, transfers[0]); + auto outInfo = addTestKeyOutput(*spendingTx, TEST_OUTPUT_AMOUNT - 1, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs; + ASSERT_TRUE(container.addTransaction(blockInfo, *spendingTx, outputs)); + } + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_TRUE(container.deleteUnconfirmedTransaction(spendingTx->getTransactionHash())); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_deleteUnconfirmedTransaction, deleteTx) { + auto txHash = addTransaction()->getTransactionHash(); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_TRUE(container.deleteUnconfirmedTransaction(txHash)); + ASSERT_EQ(0, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); +} + + +//--------------------------------------------------------------------------- +// TransfersContainer_markTransactionConfirmed +//--------------------------------------------------------------------------- +class TransfersContainer_markTransactionConfirmed : public TransfersContainerTest { +public: + bool markConfirmed(const Hash& txHash, uint64_t height = TEST_BLOCK_HEIGHT, + const std::vector& globalIndices = { TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX }) { + return container.markTransactionConfirmed(blockInfo(height), txHash, globalIndices); + } +}; + +TEST_F(TransfersContainer_markTransactionConfirmed, unconfirmedBlockHeight) { + ASSERT_ANY_THROW(markConfirmed(addTransaction()->getTransactionHash(), UNCONFIRMED_TRANSACTION_HEIGHT)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, nonExistingTransaction) { + addTransaction(); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_FALSE(markConfirmed(crypto::rand())); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, confirmedTransaction) { + auto txHash = addTransaction(TEST_BLOCK_HEIGHT)->getTransactionHash(); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_FALSE(markConfirmed(txHash)); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, globalIndicesSmaller) { + auto tx = createTransaction(); + + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + + auto outputs = { + addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT / 2, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, account), + addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT / 2, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, account) + }; + + ASSERT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *tx, outputs)); + ASSERT_EQ(2, container.transfersCount()); + ASSERT_ANY_THROW(markConfirmed(tx->getTransactionHash(), TEST_BLOCK_HEIGHT)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, confirmationWorks) { + auto txHash = addTransaction()->getTransactionHash(); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_TRUE(markConfirmed(txHash)); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, confirmationTxWithNoOutputs) { + addTransaction(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + auto tx = createTransaction(); + + { + addInput(*tx, account, transfers[0]); + ASSERT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *tx, {})); + } + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_TRUE(markConfirmed(tx->getTransactionHash())); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutputIdenticalAnotherUnspentOuputCausesException) { + // Add tx1 + CryptoNote::BlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 }; + auto tx1 = createTransaction(); + addTestInput(*tx1, TEST_OUTPUT_AMOUNT + 1); + auto outInfo1 = addTestMultisignatureOutput(*tx1, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs1; + outputs1.emplace_back(outInfo1); + + ASSERT_TRUE(container.addTransaction(blockInfo1, *tx1, outputs1)); + + // Spend output, add tx2 + CryptoNote::BlockInfo blockInfo2{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + auto tx2 = createTransaction(); + TransactionTypes::InputMultisignature input2; + input2.amount = TEST_OUTPUT_AMOUNT; + input2.outputIndex = outInfo1.globalOutputIndex; + input2.signatures = outInfo1.requiredSignatures; + tx2->addInput(input2); + ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2, std::vector())); + + // Add tx3 + CryptoNote::BlockInfo blockInfo3{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + auto tx3 = createTransaction(); + addTestInput(*tx3, TEST_OUTPUT_AMOUNT + 1); + auto outInfo3 = addTestMultisignatureOutput(*tx3, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs3; + outputs3.emplace_back(outInfo3); + + ASSERT_TRUE(container.addTransaction(blockInfo3, *tx3, outputs3)); + + // Confirm tx3 + blockInfo3.height = TEST_BLOCK_HEIGHT + 2; + std::vector globalIndices3; + globalIndices3.emplace_back(TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_ANY_THROW(container.markTransactionConfirmed(blockInfo3, tx3->getTransactionHash(), globalIndices3)); + + container.advanceHeight(1000); + + ASSERT_EQ(2, container.transfersCount()); + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainer_markTransactionConfirmed, confirmingMultisignatureOutputIdenticalAnotherSpentOuputCausesException) { + CryptoNote::BlockInfo blockInfo1{ TEST_BLOCK_HEIGHT, 1000000 }; + auto tx1 = createTransaction(); + addTestInput(*tx1, TEST_OUTPUT_AMOUNT + 1); + auto outInfo1 = addTestMultisignatureOutput(*tx1, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs1; + outputs1.emplace_back(outInfo1); + + ASSERT_TRUE(container.addTransaction(blockInfo1, *tx1, outputs1)); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + CryptoNote::BlockInfo blockInfo2{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; + auto tx2 = createTransaction(); + addTestInput(*tx2, TEST_OUTPUT_AMOUNT + 1); + auto outInfo2 = addTestMultisignatureOutput(*tx2, TEST_OUTPUT_AMOUNT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs2; + outputs2.emplace_back(outInfo2); + + ASSERT_TRUE(container.addTransaction(blockInfo2, *tx2, outputs2)); + + blockInfo2.height = TEST_BLOCK_HEIGHT + 2; + std::vector globalIndices2; + globalIndices2.emplace_back(TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_ANY_THROW(container.markTransactionConfirmed(blockInfo2, tx2->getTransactionHash(), globalIndices2)); + + ASSERT_EQ(2, container.transfersCount()); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +//--------------------------------------------------------------------------- +// TransfersContainer_detach +//--------------------------------------------------------------------------- +class TransfersContainer_detach : public TransfersContainerTest { +public: + +}; + +TEST_F(TransfersContainer_detach, detachConfirmed) { + addTransaction(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + container.detach(TEST_BLOCK_HEIGHT); + ASSERT_EQ(0, container.transfersCount()); + ASSERT_EQ(0, container.transactionsCount()); +} + +TEST_F(TransfersContainer_detach, detachConfirmedSpendingTransaction) { + auto tx = addTransaction(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + auto spendingTx = addSpendingTransaction( + tx->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + container.detach(TEST_BLOCK_HEIGHT+1); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainer_detach, threeRelatedTransactions) { + auto tx = addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + auto spendingTx1 = addSpendingTransaction( + tx->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT / 2); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2); + + auto spendingTx2 = addSpendingTransaction( + spendingTx1->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX + 2, TEST_OUTPUT_AMOUNT / 2); + + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainer_detach, detachConfirmedTransactionWithUnrelatedUnconfirmed) { + auto tx1 = addTransaction(TEST_BLOCK_HEIGHT); + auto tx2 = addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAll)); + + container.detach(TEST_BLOCK_HEIGHT); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainer_detach, confirmedWithUnconfirmedSpendingTransaction_H1) { + + auto tx = addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + auto spendingTx = addSpendingTransaction( + tx->getTransactionHash(), UNCONFIRMED_TRANSACTION_HEIGHT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + container.detach(TEST_BLOCK_HEIGHT + 1); + + ASSERT_EQ(1, container.transfersCount()); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainer_detach, confirmedWithUnconfirmedSpendingTransaction_H0) { + auto tx = addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + auto spendingTx = addSpendingTransaction( + tx->getTransactionHash(), UNCONFIRMED_TRANSACTION_HEIGHT, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + container.detach(TEST_BLOCK_HEIGHT); + + ASSERT_EQ(0, container.transfersCount()); + ASSERT_EQ(0, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainer_detach, confirmedTwoOfThree) { + auto txHash = addTransaction(TEST_BLOCK_HEIGHT - 1)->getTransactionHash(); + addTransaction(TEST_BLOCK_HEIGHT); + addTransaction(TEST_BLOCK_HEIGHT + 1); + + ASSERT_EQ(3, container.transactionsCount()); + + container.detach(TEST_BLOCK_HEIGHT); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(1, container.getTransactionOutputs(txHash, ITransfersContainer::IncludeAll).size()); +} + +TEST_F(TransfersContainer_detach, transactionDetachAfterAdvance) { + container.detach(TEST_BLOCK_HEIGHT); + addTransaction(TEST_BLOCK_HEIGHT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + container.detach(TEST_BLOCK_HEIGHT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + + +//--------------------------------------------------------------------------- +// TransfersContainer_advanceHeight +//--------------------------------------------------------------------------- +class TransfersContainer_advanceHeight : public TransfersContainerTest { +public: + TransfersContainer_advanceHeight(){} +}; + + +TEST_F(TransfersContainer_advanceHeight, advanceFailed) { + ASSERT_TRUE(container.advanceHeight(1000)); + ASSERT_FALSE(container.advanceHeight(999)); // 1000 -> 999 +} + +TEST_F(TransfersContainer_advanceHeight, advanceSucceeded) { + ASSERT_TRUE(container.advanceHeight(1000)); // 1000 -> 1000 + ASSERT_TRUE(container.advanceHeight(1001)); // 1000 -> 1001 +} + +TEST_F(TransfersContainer_advanceHeight, advanceUnlocksTransaction) { + container.detach(TEST_BLOCK_HEIGHT); + addTransaction(TEST_BLOCK_HEIGHT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + addTransaction(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + + +//--------------------------------------------------------------------------- +// TransfersContainer_balance +//--------------------------------------------------------------------------- + +class TransfersContainer_balance : public TransfersContainerTest { +public: + TransfersContainer_balance() { + } + + enum TestAmounts : uint64_t { + AMOUNT_1 = 13, + AMOUNT_2 = 17 + }; +}; + + +TEST_F(TransfersContainer_balance, treatsUnconfirmedTransfersAsLocked) { + auto tx1 = addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT, AMOUNT_1); + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_2); + + ASSERT_EQ(AMOUNT_1, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainer_balance, handlesLockedByTimeTransferAsLocked) { + auto tx1 = createTransaction(); + tx1->setUnlockTime(time(nullptr) + 60 * 60 * 24); + addTestInput(*tx1, AMOUNT_1 + 1); + auto outInfo = addTestKeyOutput(*tx1, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + std::vector outputs = { outInfo }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx1, outputs)); + + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_2); + + ASSERT_EQ(AMOUNT_1, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainer_balance, handlesLockedByHeightTransferAsLocked) { + auto tx1 = createTransaction(); + tx1->setUnlockTime(TEST_CONTAINER_CURRENT_HEIGHT + 1); + addTestInput(*tx1, AMOUNT_1 + 1); + auto outInfo = addTestKeyOutput(*tx1, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + std::vector outputs = { outInfo }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx1, outputs)); + + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_2); + + ASSERT_EQ(AMOUNT_1, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainer_balance, handlesTransferStateSoftLocked) { + auto tx1 = addTransaction(TEST_CONTAINER_CURRENT_HEIGHT - TEST_TRANSACTION_SPENDABLE_AGE, AMOUNT_2); + auto tx2 = addTransaction(TEST_CONTAINER_CURRENT_HEIGHT, AMOUNT_1); + + ASSERT_EQ(AMOUNT_1, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainer_balance, handlesTransferStateUnLocked) { + auto tx1 = addTransaction(TEST_CONTAINER_CURRENT_HEIGHT - TEST_TRANSACTION_SPENDABLE_AGE, AMOUNT_2); + auto tx2 = addTransaction(TEST_CONTAINER_CURRENT_HEIGHT, AMOUNT_1); + + ASSERT_EQ(AMOUNT_2, container.balance(ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainer_balance, handlesTransferTypeKey) { + auto tx = createTransaction(); + addTestInput(*tx, AMOUNT_1 + AMOUNT_2 + 1); + auto outInfo1 = addTestKeyOutput(*tx, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + auto outInfo2 = addTestMultisignatureOutput(*tx, AMOUNT_2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs = { outInfo1, outInfo2 }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, outputs)); + + ASSERT_EQ(AMOUNT_1, container.balance(ITransfersContainer::IncludeStateAll | ITransfersContainer::IncludeTypeKey)); +} + +TEST_F(TransfersContainer_balance, handlesTransferTypeMultisignature) { + auto tx = createTransaction(); + addTestInput(*tx, AMOUNT_1 + AMOUNT_2 + 1); + auto outInfo1 = addTestKeyOutput(*tx, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + auto outInfo2 = addTestMultisignatureOutput(*tx, AMOUNT_2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs = { outInfo1, outInfo2 }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, outputs)); + + ASSERT_EQ(AMOUNT_2, container.balance(ITransfersContainer::IncludeStateAll | ITransfersContainer::IncludeTypeMultisignature)); +} + +TEST_F(TransfersContainer_balance, filtersByStateAndKeySimultaneously) { + auto tx1 = createTransaction(); + addTestInput(*tx1, AMOUNT_1 + AMOUNT_2 + 1); + auto outInfo1 = addTestKeyOutput(*tx1, AMOUNT_1, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, account); + auto outInfo2 = addTestMultisignatureOutput(*tx1, AMOUNT_2, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs = { outInfo1, outInfo2 }; + ASSERT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *tx1, outputs)); + + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_1 + AMOUNT_2); + + container.advanceHeight(TEST_CONTAINER_CURRENT_HEIGHT); + + ASSERT_EQ(AMOUNT_1, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeKey)); + ASSERT_EQ(AMOUNT_2, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeMultisignature)); + ASSERT_EQ(AMOUNT_1 + AMOUNT_2, container.balance(ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeTypeKey)); +} + + +//--------------------------------------------------------------------------- +// TransfersContainer_getOutputs +//--------------------------------------------------------------------------- + +class TransfersContainer_getOutputs : public TransfersContainerTest { +public: + TransfersContainer_getOutputs() { + } + + enum TestAmounts : uint64_t { + AMOUNT_1 = 13, + AMOUNT_2 = 17 + }; +}; + + +TEST_F(TransfersContainer_getOutputs, treatsUnconfirmedTransfersAsLocked) { + auto tx1 = addTransaction(UNCONFIRMED_TRANSACTION_HEIGHT, AMOUNT_1); + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_2); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, handlesLockedByTimeTransferAsLocked) { + auto tx1 = createTransaction(); + tx1->setUnlockTime(time(nullptr) + 60 * 60 * 24); + addTestInput(*tx1, AMOUNT_1 + 1); + auto outInfo = addTestKeyOutput(*tx1, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + std::vector outputs = { outInfo }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx1, outputs)); + + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_2); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, handlesLockedByHeightTransferAsLocked) { + auto tx1 = createTransaction(); + tx1->setUnlockTime(TEST_CONTAINER_CURRENT_HEIGHT + 1); + addTestInput(*tx1, AMOUNT_1 + 1); + auto outInfo = addTestKeyOutput(*tx1, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + std::vector outputs = { outInfo }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx1, outputs)); + + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_2); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, handlesTransferStateSoftLocked) { + addTransaction(TEST_CONTAINER_CURRENT_HEIGHT - TEST_TRANSACTION_SPENDABLE_AGE, AMOUNT_2); + addTransaction(TEST_CONTAINER_CURRENT_HEIGHT, AMOUNT_1); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, handlesTransferStateUnLocked) { + addTransaction(TEST_CONTAINER_CURRENT_HEIGHT - TEST_TRANSACTION_SPENDABLE_AGE, AMOUNT_2); + addTransaction(TEST_CONTAINER_CURRENT_HEIGHT, AMOUNT_1); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeTypeAll); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_2, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, handlesTransferTypeKey) { + auto tx = createTransaction(); + addTestInput(*tx, AMOUNT_1 + AMOUNT_2 + 1); + auto outInfo1 = addTestKeyOutput(*tx, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + auto outInfo2 = addTestMultisignatureOutput(*tx, AMOUNT_2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs = { outInfo1, outInfo2 }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, outputs)); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateAll | ITransfersContainer::IncludeTypeKey); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, handlesTransferTypeMultisignature) { + auto tx = createTransaction(); + addTestInput(*tx, AMOUNT_1 + AMOUNT_2 + 1); + auto outInfo1 = addTestKeyOutput(*tx, AMOUNT_1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, account); + auto outInfo2 = addTestMultisignatureOutput(*tx, AMOUNT_2, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + std::vector outputs = { outInfo1, outInfo2 }; + ASSERT_TRUE(container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *tx, outputs)); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateAll | ITransfersContainer::IncludeTypeMultisignature); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_2, transfers.front().amount); +} + +TEST_F(TransfersContainer_getOutputs, filtersByStateAndKeySimultaneously) { + auto tx1 = createTransaction(); + addTestInput(*tx1, AMOUNT_1 + AMOUNT_2 + 1); + auto outInfo1 = addTestKeyOutput(*tx1, AMOUNT_1, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, account); + auto outInfo2 = addTestMultisignatureOutput(*tx1, AMOUNT_2, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + std::vector outputs = { outInfo1, outInfo2 }; + ASSERT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *tx1, outputs)); + + auto tx2 = addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_1 + AMOUNT_2); + + container.advanceHeight(TEST_CONTAINER_CURRENT_HEIGHT); + + std::vector transfers; + container.getOutputs(transfers, ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeKey); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1, transfers.front().amount); + + transfers.clear(); + container.getOutputs(transfers, ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeMultisignature); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_2, transfers.front().amount); + + transfers.clear(); + container.getOutputs(transfers, ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeTypeKey); + ASSERT_EQ(1, transfers.size()); + ASSERT_EQ(AMOUNT_1 + AMOUNT_2, transfers.front().amount); +} diff --git a/tests/unit_tests/test_TransfersContainerKeyImage.cpp b/tests/unit_tests/test_TransfersContainerKeyImage.cpp new file mode 100644 index 0000000000..6c7b696c00 --- /dev/null +++ b/tests/unit_tests/test_TransfersContainerKeyImage.cpp @@ -0,0 +1,732 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "IWallet.h" + +#include "crypto/crypto.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/TransactionApi.h" +#include "transfers/TransfersContainer.h" + +#include "TransactionApiHelpers.h" + +using namespace CryptoNote; +using namespace cryptonote; + + +namespace { + const size_t TEST_TRANSACTION_SPENDABLE_AGE = 1; + const uint64_t TEST_OUTPUT_AMOUNT = 100; + const uint64_t TEST_BLOCK_HEIGHT = 99; + const uint64_t TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX = 113; + const uint64_t UNCONFIRMED = UNCONFIRMED_TRANSACTION_HEIGHT; + const uint64_t TEST_TIMESTAMP = 1000000; + + AccountAddress generateAddress() { + account_base account; + account.generate(); + return reinterpret_cast(account.get_keys().m_account_address); + } + + void addInput(ITransaction& tx, const AccountKeys& senderKeys, const TransactionOutputInformation& t) { + CryptoNote::KeyPair kp; + TransactionTypes::InputKeyInfo info; + info.amount = t.amount; + + TransactionTypes::GlobalOutput globalOut; + globalOut.outputIndex = t.globalOutputIndex; + globalOut.targetKey = t.outputKey; + info.outputs.push_back(globalOut); + + info.realOutput.outputInTransaction = t.outputInTransaction; + info.realOutput.transactionIndex = 0; + info.realOutput.transactionPublicKey = t.transactionPublicKey; + + tx.addInput(senderKeys, info, kp); + } + + TransactionOutputInformationIn addTestMultisignatureOutput(ITransaction& transaction, uint64_t amount, + uint64_t globalOutputIndex) { + std::vector addresses; + addresses.emplace_back(generateAddress()); + addresses.emplace_back(generateAddress()); + + uint32_t index = static_cast(transaction.addOutput(amount, addresses, static_cast(addresses.size()))); + + TransactionTypes::OutputMultisignature output; + transaction.getOutput(index, output); + + TransactionOutputInformationIn outputInfo; + outputInfo.type = TransactionTypes::OutputType::Multisignature; + outputInfo.amount = output.amount; + outputInfo.globalOutputIndex = globalOutputIndex; + outputInfo.outputInTransaction = index; + outputInfo.transactionPublicKey = transaction.getTransactionPublicKey(); + // Doesn't used in multisignature output, so can contain garbage + outputInfo.keyImage = generateKeyImage(); + outputInfo.requiredSignatures = output.requiredSignatures; + + return outputInfo; + } + + + class TransfersContainerKeyImage : public ::testing::Test { + public: + + TransfersContainerKeyImage() : + currency(CurrencyBuilder().currency()), + container(currency, TEST_TRANSACTION_SPENDABLE_AGE), + account(generateAccountKeys()), + txTemplate(createTransaction()) { + txTemplate->getTransactionSecretKey(txSecretKey); + } + + protected: + + std::vector getOutputs(uint32_t flags) { + std::vector outs; + container.getOutputs(outs, flags); + return outs; + } + + size_t outputsCount(uint32_t flags) { + return getOutputs(flags).size(); + } + + BlockInfo blockInfo(uint64_t height) const { + return BlockInfo{ height, 1000000 }; + } + + + + std::unique_ptr createTransactionWithFixedKey() { + auto tx = createTransaction(txTemplate->getTransactionData()); + tx->setTransactionSecretKey(txSecretKey); + return tx; + } + + std::unique_ptr addTransactionWithFixedKey(uint64_t height, size_t inputs = 1, uint64_t amount = TEST_OUTPUT_AMOUNT, uint32_t txIndex = 0) { + auto tx = createTransactionWithFixedKey(); + + while (inputs--) { + addTestInput(*tx, amount + 1); + } + + auto outputIndex = (height == UNCONFIRMED_TRANSACTION_HEIGHT) ? + UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; + auto outInfo = addTestKeyOutput(*tx, amount, outputIndex, account); + EXPECT_TRUE(container.addTransaction(BlockInfo{ height, 1000000, txIndex }, *tx, { outInfo })); + return tx; + } + + std::unique_ptr addTransaction(uint64_t height = UNCONFIRMED_TRANSACTION_HEIGHT) { + auto tx = createTransaction(); + addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); + auto outputIndex = (height == UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; + auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, outputIndex, account); + std::vector outputs = { outInfo }; + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, outputs)); + return tx; + } + + std::unique_ptr addSpendingTransaction(const Hash& sourceTx, uint64_t height, uint64_t outputIndex, uint64_t amount = TEST_OUTPUT_AMOUNT, bool fixedKey = false) { + std::unique_ptr tx; + auto outputs = container.getTransactionOutputs(sourceTx, ITransfersContainer::IncludeTypeAll | + ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeStateSoftLocked); + + EXPECT_FALSE(outputs.empty()); + + if (outputs.empty()) + return tx; + + tx = fixedKey ? createTransactionWithFixedKey() : createTransaction(); + + size_t inputAmount = 0; + for (const auto& t : outputs) { + inputAmount += t.amount; + addInput(*tx, account, t); + } + + EXPECT_GE(inputAmount, amount); + + std::vector transfers; + + addTestKeyOutput(*tx, amount, outputIndex); // output to some random address + + if (inputAmount > amount) { + transfers.emplace_back(addTestKeyOutput(*tx, inputAmount - amount, outputIndex + 1, account)); // change + } + + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, transfers)); + + return tx; + } + + Currency currency; + TransfersContainer container; + AccountKeys account; + std::unique_ptr txTemplate; + SecretKey txSecretKey; + }; + +} + +///////////////////////////////////////////////////////////////////////////// +// addTransaction +///////////////////////////////////////////////////////////////////////////// + +TEST_F(TransfersContainerKeyImage, addTransaction_addingSecondUnconfirmedTransferHidesBothUnconfirmedTransfers) { + // add first transaction + auto tx1 = createTransactionWithFixedKey(); + addTestInput(*tx1, TEST_OUTPUT_AMOUNT); + auto tx1out = addTestKeyOutput(*tx1, TEST_OUTPUT_AMOUNT, UNCONFIRMED, account); + + ASSERT_TRUE(container.addTransaction({ UNCONFIRMED, 100000 }, *tx1, { tx1out })); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(1, outputsCount(ITransfersContainer::IncludeAllLocked)); + + auto tx2 = createTransactionWithFixedKey(); + ASSERT_EQ(tx1->getTransactionPublicKey(), tx2->getTransactionPublicKey()); + + // fill tx2 + addTestInput(*tx2, TEST_OUTPUT_AMOUNT); + addTestInput(*tx2, TEST_OUTPUT_AMOUNT); + auto tx2out = addTestKeyOutput(*tx2, TEST_OUTPUT_AMOUNT, UNCONFIRMED, account); + + ASSERT_EQ(tx1out.keyImage, tx2out.keyImage); + ASSERT_NE(tx1->getTransactionPrefixHash(), tx2->getTransactionPrefixHash()); + + ASSERT_TRUE(container.addTransaction({ UNCONFIRMED, 100000 }, *tx2, { tx2out })); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); // transactions' outputs should shadow one another + ASSERT_EQ(0, outputsCount(ITransfersContainer::IncludeAllLocked)); +} + +TEST_F(TransfersContainerKeyImage, addTransaction_unconfirmedTransferAddedAfterConfirmedBecomeHidden) { + // fill tx1 + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(1, outputsCount(ITransfersContainer::IncludeAllUnlocked)); + + auto tx2 = createTransactionWithFixedKey(); + ASSERT_EQ(tx1->getTransactionPublicKey(), tx2->getTransactionPublicKey()); + + // fill tx2 + addTestInput(*tx2, TEST_OUTPUT_AMOUNT); + addTestInput(*tx2, TEST_OUTPUT_AMOUNT); + auto tx2out = addTestKeyOutput(*tx2, TEST_OUTPUT_AMOUNT, UNCONFIRMED, account); + + ASSERT_TRUE(container.addTransaction({ UNCONFIRMED, 100000 }, *tx2, { tx2out })); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); + ASSERT_EQ(1, outputsCount(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(0, outputsCount(ITransfersContainer::IncludeAllLocked)); +} + + +TEST_F(TransfersContainerKeyImage, addTransaction_unconfirmedTransferAddedAfterSpentBecomeHidden) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + addSpendingTransaction(tx1->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(0, outputsCount(ITransfersContainer::IncludeAll)); + + addTransactionWithFixedKey(UNCONFIRMED, 2); + + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(0, outputsCount(ITransfersContainer::IncludeAll)); +} + + +TEST_F(TransfersContainerKeyImage, addTransaction_confirmedTransferAddedAfterUnconfirmedHidesUnconfirmed) { + auto tx1 = addTransactionWithFixedKey(UNCONFIRMED); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 2, TEST_OUTPUT_AMOUNT * 2); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAllUnlocked)); // confirmed added + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllLocked)); // unconfirmed shadowed +} + +TEST_F(TransfersContainerKeyImage, addTransaction_secondConfirmedTransferAddedAsHidden_BothTransfersInTheSameBlock) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 1, TEST_OUTPUT_AMOUNT, 1); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllLocked)); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 2, TEST_OUTPUT_AMOUNT * 2, 2); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainerKeyImage, addTransaction_secondConfirmedTransferAddedAsHidden_TransfersInDifferentBlocks) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + ASSERT_EQ(1, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + container.advanceHeight(TEST_BLOCK_HEIGHT + 1 + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainerKeyImage, addTransaction_confirmedTransferAddedAfterSpentBecomeHidden) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + addSpendingTransaction(tx1->getTransactionHash(), TEST_BLOCK_HEIGHT + 1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); // all spent + + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); // all spent +} + +///////////////////////////////////////////////////////////////////////////// +// markTransactionConfirmed +///////////////////////////////////////////////////////////////////////////// + +TEST_F(TransfersContainerKeyImage, markTransactionConfirmed_confirmingOneOfAFewUnconfirmedTransfersMakesThisTransferVisible) { + addTransactionWithFixedKey(UNCONFIRMED, 1, TEST_OUTPUT_AMOUNT); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.markTransactionConfirmed( + { TEST_BLOCK_HEIGHT, 100000 }, tx2->getTransactionHash(), { TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX })); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainerKeyImage, markTransactionConfirmed_oneConfirmedOtherUnconfirmed_confirmingOneUnconfirmed) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 1, TEST_OUTPUT_AMOUNT, 1); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeStateLocked | ITransfersContainer::IncludeTypeAll)); + + // same block, but smaller transactionIndex + ASSERT_TRUE(container.markTransactionConfirmed( + { TEST_BLOCK_HEIGHT, TEST_TIMESTAMP, 2 }, tx2->getTransactionHash(), { TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX })); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, markTransactionConfirmed_oneSpentOtherUnconfirmed_confirmingOneUnconfirmed) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 1, TEST_OUTPUT_AMOUNT); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + addSpendingTransaction(tx1->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.markTransactionConfirmed( + { TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, TEST_TIMESTAMP, 0 }, tx2->getTransactionHash(), {TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX})); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +///////////////////////////////////////////////////////////////////////////// +// addTransaction - spending fails +///////////////////////////////////////////////////////////////////////////// + +class TransfersContainerKeyImage_Spend : public TransfersContainerKeyImage { +public: + + bool spendOutput(const TransactionOutputInformation& outInfo) { + auto amount = outInfo.amount; + auto spendTx = createTransaction(); + addInput(*spendTx, account, outInfo); + addTestKeyOutput(*spendTx, amount, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + return container.addTransaction({ TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, TEST_TIMESTAMP, 0 }, *spendTx, {}); + } +}; + + +TEST_F(TransfersContainerKeyImage_Spend, spendingKeyImageWithWrongAmountCausesException) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + + auto outputs = getOutputs(ITransfersContainer::IncludeTypeAll | ITransfersContainer::IncludeStateSoftLocked); + + ASSERT_EQ(1, outputs.size()); + // mess with amount + outputs[0].amount = TEST_OUTPUT_AMOUNT * 2; + ASSERT_ANY_THROW(spendOutput(outputs[0])); + ASSERT_EQ(1, container.transactionsCount()); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendUnconfirmedeKeyImageCausesException) { + auto tx1 = addTransactionWithFixedKey(UNCONFIRMED); + auto outputs = getOutputs(ITransfersContainer::IncludeAllLocked); + ASSERT_FALSE(outputs.empty()); + + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(2, container.transactionsCount()); + ASSERT_ANY_THROW(spendOutput(outputs[0])); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendingUnconfirmedTransferIfThereIsConfirmedWithAnotherAmountCausesException) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(2, container.transactionsCount()); + + auto outputs = getOutputs(ITransfersContainer::IncludeTypeAll | ITransfersContainer::IncludeStateSoftLocked); + ASSERT_FALSE(outputs.empty()); + outputs[0].amount = TEST_OUTPUT_AMOUNT * 2; + + ASSERT_ANY_THROW(spendOutput(outputs[0])); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendingTransferIfThereIsSpentTransferWithAnotherAmountCausesException) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto outputs = container.getTransactionOutputs(tx1->getTransactionHash(), ITransfersContainer::IncludeAll); + ASSERT_EQ(1, outputs.size()); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + addSpendingTransaction(tx1->getTransactionHash(), + TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_ANY_THROW(spendOutput(outputs[0])); +} + + +///////////////////////////////////////////////////////////////////////////// +// addTransaction - spending succeeds +///////////////////////////////////////////////////////////////////////////// +TEST_F(TransfersContainerKeyImage_Spend, spendingVisibleConfirmedTransfer) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_NE(tx1->getTransactionHash(), tx2->getTransactionHash()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + // spend first confirmed transaction + addSpendingTransaction(tx1->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto spentOutputs = container.getSpentOutputs(); + ASSERT_EQ(1, spentOutputs.size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, spentOutputs[0].amount); + ASSERT_EQ(tx1->getTransactionHash(), spentOutputs[0].transactionHash); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendHiddenConfirmedTransfer) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_NE(tx1->getTransactionHash(), tx2->getTransactionHash()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + // spend second confirmed transaction (hidden) + addSpendingTransaction(tx2->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto spentOutputs = container.getSpentOutputs(); + ASSERT_EQ(1, spentOutputs.size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, spentOutputs[0].amount); + ASSERT_EQ(tx2->getTransactionHash(), spentOutputs[0].transactionHash); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendSecondHiddenConfirmedOutput) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 2, 3, TEST_OUTPUT_AMOUNT * 3); + + // spend third confirmed transaction (hidden) + addSpendingTransaction(tx3->getTransactionHash(), TEST_BLOCK_HEIGHT + 2 + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto spentOutputs = container.getSpentOutputs(); + ASSERT_EQ(1, spentOutputs.size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 3, spentOutputs[0].amount); + ASSERT_EQ(tx3->getTransactionHash(), spentOutputs[0].transactionHash); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendHiddenWithSameAmount_oneBlock) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT - 1); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 2, TEST_OUTPUT_AMOUNT * 2, 1); + auto tx3 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 3, TEST_OUTPUT_AMOUNT * 2, 2); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + addSpendingTransaction(tx3->getTransactionHash(), TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto spentOutputs = container.getSpentOutputs(); + ASSERT_EQ(1, spentOutputs.size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, spentOutputs[0].amount); + ASSERT_EQ(tx2->getTransactionHash(), spentOutputs[0].transactionHash); +} + +TEST_F(TransfersContainerKeyImage_Spend, spendHiddenWithSameAmount_differentBlocks) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 3, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 2, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + addSpendingTransaction(tx3->getTransactionHash(), TEST_BLOCK_HEIGHT + 2 + TEST_TRANSACTION_SPENDABLE_AGE, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto spentOutputs = container.getSpentOutputs(); + ASSERT_EQ(1, spentOutputs.size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, spentOutputs[0].amount); + ASSERT_EQ(tx2->getTransactionHash(), spentOutputs[0].transactionHash); +} + +///////////////////////////////////////////////////////////////////////////// +// remove spending transaction +///////////////////////////////////////////////////////////////////////////// +class TransfersContainerKeyImage_Remove : public TransfersContainerKeyImage { +public: + void checkSpentOutputs(const Hash& expectedTxHash) { + auto spentOutputs = container.getSpentOutputs(); + ASSERT_EQ(1, spentOutputs.size()); + ASSERT_EQ(expectedTxHash, spentOutputs[0].transactionHash); + } +}; + +TEST_F(TransfersContainerKeyImage_Remove, removeUnconfirmedTxSpendingVisibleOutput) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 1, TEST_OUTPUT_AMOUNT, 1); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 2, TEST_OUTPUT_AMOUNT * 2, 2); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + auto spendingTx = addSpendingTransaction(tx1->getTransactionHash(), + UNCONFIRMED, UNCONFIRMED, TEST_OUTPUT_AMOUNT); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_NO_FATAL_FAILURE(checkSpentOutputs(tx1->getTransactionHash())); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(spendingTx->getTransactionHash())); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage_Remove, removeUnconfirmedTxSpendingHiddenOut) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 1, TEST_OUTPUT_AMOUNT, 1); + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 2, TEST_OUTPUT_AMOUNT * 2, 2); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + auto spendingTx = addSpendingTransaction(tx2->getTransactionHash(), + UNCONFIRMED, UNCONFIRMED, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_NO_FATAL_FAILURE(checkSpentOutputs(tx2->getTransactionHash())); + ASSERT_TRUE(container.deleteUnconfirmedTransaction(spendingTx->getTransactionHash())); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage_Remove, removeUnconfirmedTxAfterAddingMoreTx) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT, 1, TEST_OUTPUT_AMOUNT, 1); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + auto spendingTx = addSpendingTransaction(tx1->getTransactionHash(), + UNCONFIRMED, UNCONFIRMED, TEST_OUTPUT_AMOUNT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(spendingTx->getTransactionHash())); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +///////////////////////////////////////////////////////////////////////////// +// remove unconfirmed output +///////////////////////////////////////////////////////////////////////////// +TEST_F(TransfersContainerKeyImage, removingOneOfTwoUnconfirmedTransfersMakesAnotherVisible) { + auto tx1 = addTransactionWithFixedKey(UNCONFIRMED); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2, 2); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(tx2->getTransactionHash())); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removingOneOfThreeUnconfirmedTransfersDoesNotMakeVisibleAnyOfRemaining) { + auto tx1 = addTransactionWithFixedKey(UNCONFIRMED); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(tx2->getTransactionHash())); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removingOneOfTwoUnconfirmedTransfersIfThereIsConfirmedTransferDoesNotAffectBalance) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(tx2->getTransactionHash())); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removingOnlyUnconfirmedTransfersIfThereIsConfirmedTransferDoesNotAffectBalance) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(tx2->getTransactionHash())); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removingOneOfTwoUnconfirmedTransfersIfThereIsSpentTransferDoesNotAffectBalance) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + auto tx2 = addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + auto spendingTx = addSpendingTransaction(tx1->getTransactionHash(), + TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_TRUE(container.deleteUnconfirmedTransaction(tx2->getTransactionHash())); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +///////////////////////////////////////////////////////////////////////////// +// remove confirmed output +///////////////////////////////////////////////////////////////////////////// +TEST_F(TransfersContainerKeyImage, removeConfirmed_oneOfThree) { + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 2, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + container.detach(TEST_BLOCK_HEIGHT + 2); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); +} + +TEST_F(TransfersContainerKeyImage, removeConfirmed_oneOfTwo) { + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); + + container.detach(TEST_BLOCK_HEIGHT + 1); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); +} + +TEST_F(TransfersContainerKeyImage, removeConfirmed_revealsUnconfirmed) { + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT).size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAllLocked)); +} + +TEST_F(TransfersContainerKeyImage, removeConfirmed_twoUnconfirmedHidden) { + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); + addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT).size()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removeConfirmed_twoConfirmedOneUnconfirmedHidden) { + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + 1, 2, TEST_OUTPUT_AMOUNT * 2); + addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT + 1).size()); + ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removeConfirmed_oneSpentOneConfirmed) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addSpendingTransaction(tx1->getTransactionHash(), TEST_BLOCK_HEIGHT + 1, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, 2, TEST_OUTPUT_AMOUNT * 2); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + + ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1).size()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} + +TEST_F(TransfersContainerKeyImage, removeConfirmed_oneSpentTwoConfirmed) { + auto tx1 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT); + addSpendingTransaction(tx1->getTransactionHash(), TEST_BLOCK_HEIGHT + 1, + TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX, TEST_OUTPUT_AMOUNT); + + auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, 2, TEST_OUTPUT_AMOUNT * 2); + auto tx3 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 2, 3, TEST_OUTPUT_AMOUNT * 3); + + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(4, container.transactionsCount()); + + ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 2).size()); + ASSERT_EQ(3, container.transactionsCount()); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); +} diff --git a/tests/unit_tests/test_TransfersSubscription.cpp b/tests/unit_tests/test_TransfersSubscription.cpp new file mode 100644 index 0000000000..2ed3051520 --- /dev/null +++ b/tests/unit_tests/test_TransfersSubscription.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" +#include + +#include "cryptonote_core/TransactionApi.h" +#include "transfers/TransfersSubscription.h" +#include "transfers/TypeHelpers.h" +#include "ITransfersContainer.h" + +#include "TransactionApiHelpers.h" +#include "TransfersObserver.h" + +using namespace CryptoNote; +using namespace cryptonote; + +namespace { + +const uint64_t UNCONFIRMED = std::numeric_limits::max(); + +std::error_code createError() { + return std::make_error_code(std::errc::invalid_argument); +} + + +class TransfersSubscriptionTest : public ::testing::Test { +public: + + TransfersSubscriptionTest() : + currency(CurrencyBuilder().currency()), + account(generateAccountKeys()), + syncStart(SynchronizationStart{ 0, 0 }), + sub(currency, AccountSubscription{ account, syncStart, 10 }) { + sub.addObserver(&observer); + } + + std::unique_ptr addTransaction(uint64_t amount, uint64_t height, uint64_t outputIndex) { + auto tx = createTransaction(); + addTestInput(*tx, amount); + auto outInfo = addTestKeyOutput(*tx, amount, outputIndex, account); + std::vector outputs = { outInfo }; + sub.addTransaction(BlockInfo{ height, 100000 }, *tx, outputs); + return tx; + } + + Currency currency; + AccountKeys account; + SynchronizationStart syncStart; + TransfersSubscription sub; + TransfersObserver observer; +}; +} + + + +TEST_F(TransfersSubscriptionTest, getInitParameters) { + ASSERT_EQ(syncStart.height, sub.getSyncStart().height); + ASSERT_EQ(syncStart.timestamp, sub.getSyncStart().timestamp); + ASSERT_EQ(account.address, sub.getAddress()); + ASSERT_EQ(account, sub.getKeys()); +} + +TEST_F(TransfersSubscriptionTest, addTransaction) { + auto tx1 = addTransaction(10000, 1, 0); + auto tx2 = addTransaction(10000, 2, 1); + + // this transaction should not be added, so no notification + auto tx = createTransaction(); + addTestInput(*tx, 20000); + sub.addTransaction(BlockInfo{ 2, 100000 }, *tx, {}); + + ASSERT_EQ(2, sub.getContainer().transactionsCount()); + ASSERT_EQ(2, observer.updated.size()); + ASSERT_EQ(tx1->getTransactionHash(), observer.updated[0]); + ASSERT_EQ(tx2->getTransactionHash(), observer.updated[1]); +} + +TEST_F(TransfersSubscriptionTest, onBlockchainDetach) { + addTransaction(10000, 10, 0); + auto txHash = addTransaction(10000, 11, 1)->getTransactionHash(); + ASSERT_EQ(2, sub.getContainer().transactionsCount()); + + sub.onBlockchainDetach(11); + + ASSERT_EQ(1, sub.getContainer().transactionsCount()); + ASSERT_EQ(1, observer.deleted.size()); + ASSERT_EQ(txHash, observer.deleted[0]); +} + +TEST_F(TransfersSubscriptionTest, onError) { + + auto err = createError(); + + addTransaction(10000, 10, 0); + addTransaction(10000, 11, 1); + + ASSERT_EQ(2, sub.getContainer().transactionsCount()); + + sub.onError(err, 12); + + ASSERT_EQ(2, sub.getContainer().transactionsCount()); + ASSERT_EQ(1, observer.errors.size()); + ASSERT_EQ(std::make_tuple(12, err), observer.errors[0]); + + sub.onError(err, 11); + + ASSERT_EQ(1, sub.getContainer().transactionsCount()); // one transaction should be detached + ASSERT_EQ(2, observer.errors.size()); + + ASSERT_EQ(std::make_tuple(12, err), observer.errors[0]); + ASSERT_EQ(std::make_tuple(11, err), observer.errors[1]); +} + +TEST_F(TransfersSubscriptionTest, advanceHeight) { + ASSERT_TRUE(sub.advanceHeight(10)); + ASSERT_FALSE(sub.advanceHeight(9)); // can't go backwards +} + + +TEST_F(TransfersSubscriptionTest, markTransactionConfirmed) { + auto txHash = addTransaction(10000, UNCONFIRMED, UNCONFIRMED)->getTransactionHash(); + ASSERT_EQ(1, sub.getContainer().transactionsCount()); + ASSERT_EQ(1, observer.updated.size()); // added + + sub.markTransactionConfirmed(BlockInfo{ 10, 100000 }, txHash, { 1 }); + + ASSERT_EQ(2, observer.updated.size()); // added + updated + ASSERT_EQ(txHash, observer.updated[0]); +} + +TEST_F(TransfersSubscriptionTest, deleteUnconfirmedTransaction) { + auto txHash = addTransaction(10000, UNCONFIRMED, UNCONFIRMED)->getTransactionHash(); + ASSERT_EQ(1, sub.getContainer().transactionsCount()); + + sub.deleteUnconfirmedTransaction(txHash); + + ASSERT_EQ(0, sub.getContainer().transactionsCount()); + ASSERT_EQ(1, observer.deleted.size()); + ASSERT_EQ(txHash, observer.deleted[0]); +} + diff --git a/tests/unit_tests/test_inprocess_node.cpp b/tests/unit_tests/test_inprocess_node.cpp new file mode 100644 index 0000000000..f70f6d101f --- /dev/null +++ b/tests/unit_tests/test_inprocess_node.cpp @@ -0,0 +1,287 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include + +#include "EventWaiter.h" +#include "ICoreStub.h" +#include "ICryptonoteProtocolQueryStub.h" +#include "inprocess_node/InProcessNode.h" + +struct CallbackStatus { + CallbackStatus() {} + + bool wait() { return waiter.wait_for(std::chrono::milliseconds(3000)); } + bool ok() { return waiter.wait_for(std::chrono::milliseconds(3000)) && !static_cast(code); } + void setStatus(const std::error_code& ec) { code = ec; waiter.notify(); } + std::error_code getStatus() const { return code; } + + std::error_code code; + EventWaiter waiter; +}; + +class InProcessNode : public ::testing::Test { +public: + InProcessNode() : node(coreStub, protocolQueryStub) {} + void SetUp(); + +protected: + void initNode(); + + ICoreStub coreStub; + ICryptonoteProtocolQueryStub protocolQueryStub; + CryptoNote::InProcessNode node; +}; + +void InProcessNode::SetUp() { + initNode(); +} + +void InProcessNode::initNode() { + CallbackStatus status; + + node.init([&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.ok()); +} + +TEST_F(InProcessNode, initOk) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + CallbackStatus status; + + newNode.init([&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.ok()); +} + +TEST_F(InProcessNode, doubleInit) { + CallbackStatus status; + node.init([&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + + std::error_code ec = status.getStatus(); + ASSERT_NE(ec, std::error_code()); +} + +TEST_F(InProcessNode, shutdownNotInited) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + ASSERT_FALSE(newNode.shutdown()); +} + +TEST_F(InProcessNode, shutdown) { + ASSERT_TRUE(node.shutdown()); +} + +TEST_F(InProcessNode, getPeersCountSuccess) { + protocolQueryStub.setPeerCount(1); + ASSERT_EQ(1, node.getPeerCount()); +} + +TEST_F(InProcessNode, getLastLocalBlockHeightSuccess) { + crypto::hash ignore; + coreStub.set_blockchain_top(10, ignore, true); + + ASSERT_EQ(10, node.getLastLocalBlockHeight()); +} + +TEST_F(InProcessNode, getLastLocalBlockHeightFailure) { + crypto::hash ignore; + coreStub.set_blockchain_top(10, ignore, false); + + ASSERT_ANY_THROW(node.getLastLocalBlockHeight()); +} + +TEST_F(InProcessNode, getLastKnownBlockHeightSuccess) { + protocolQueryStub.setObservedHeight(10); + ASSERT_EQ(10, node.getLastKnownBlockHeight()); +} + +TEST_F(InProcessNode, getTransactionOutsGlobalIndicesSuccess) { + crypto::hash ignore; + std::vector indices; + std::vector expectedIndices; + + uint64_t start = 10; + std::generate_n(std::back_inserter(expectedIndices), 5, [&start] () { return start++; }); + coreStub.set_outputs_gindexs(expectedIndices, true); + + CallbackStatus status; + node.getTransactionOutsGlobalIndices(ignore, indices, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.ok()); + + ASSERT_EQ(expectedIndices.size(), indices.size()); + std::sort(indices.begin(), indices.end()); + ASSERT_TRUE(std::equal(indices.begin(), indices.end(), expectedIndices.begin())); +} + +TEST_F(InProcessNode, getTransactionOutsGlobalIndicesFailure) { + crypto::hash ignore; + std::vector indices; + coreStub.set_outputs_gindexs(indices, false); + + CallbackStatus status; + node.getTransactionOutsGlobalIndices(ignore, indices, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getRandomOutsByAmountsSuccess) { + crypto::public_key ignoredPublicKey; + crypto::secret_key ignoredSectetKey; + crypto::generate_keys(ignoredPublicKey, ignoredSectetKey); + + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response expectedResp; + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount out; + out.amount = 10; + out.outs.push_back({ 11, ignoredPublicKey }); + expectedResp.outs.push_back(out); + coreStub.set_random_outs(expectedResp, true); + + std::vector outs; + + CallbackStatus status; + node.getRandomOutsByAmounts({1,2,3}, 1, outs, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.ok()); + ASSERT_EQ(1, outs.size()); + + ASSERT_EQ(10, outs[0].amount); + ASSERT_EQ(1, outs[0].outs.size()); + ASSERT_EQ(11, outs[0].outs.front().global_amount_index); +} + +TEST_F(InProcessNode, getRandomOutsByAmountsFailure) { + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response expectedResp; + coreStub.set_random_outs(expectedResp, false); + + std::vector outs; + + CallbackStatus status; + node.getRandomOutsByAmounts({1,2,3}, 1, outs, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getPeerCountUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + ASSERT_ANY_THROW(newNode.getPeerCount()); +} + +TEST_F(InProcessNode, getLastLocalBlockHeightUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + ASSERT_ANY_THROW(newNode.getLastLocalBlockHeight()); +} + +TEST_F(InProcessNode, getLastKnownBlockHeightUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + ASSERT_ANY_THROW(newNode.getLastKnownBlockHeight()); +} + +TEST_F(InProcessNode, getNewBlocksUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + std::list knownBlockIds; + std::list newBlocks; + uint64_t startHeight; + + CallbackStatus status; + newNode.getNewBlocks(std::move(knownBlockIds), newBlocks, startHeight, [&] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getTransactionOutsGlobalIndicesUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + std::vector outsGlobalIndices; + + CallbackStatus status; + newNode.getTransactionOutsGlobalIndices(crypto::hash(), outsGlobalIndices, [&] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getRandomOutsByAmountsUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + std::vector outs; + + CallbackStatus status; + newNode.getRandomOutsByAmounts({1,2,3}, 1, outs, [&] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, relayTransactionUninitialized) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + + CallbackStatus status; + newNode.relayTransaction(cryptonote::Transaction(), [&] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getLastLocalBlockTimestamp) { + class GetBlockTimestampCore : public ICoreStub { + public: + GetBlockTimestampCore(uint64_t timestamp) : timestamp(timestamp) {} + virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) override { + return true; + } + + virtual bool getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) override { + blk.timestamp = timestamp; + return true; + } + + uint64_t timestamp; + }; + + uint64_t expectedTimestamp = 1234567890; + GetBlockTimestampCore core(expectedTimestamp); + CryptoNote::InProcessNode newNode(core, protocolQueryStub); + + CallbackStatus initStatus; + newNode.init([&initStatus] (std::error_code ec) { initStatus.setStatus(ec); }); + ASSERT_TRUE(initStatus.wait()); + + uint64_t timestamp = newNode.getLastLocalBlockTimestamp(); + + ASSERT_EQ(expectedTimestamp, timestamp); +} + +TEST_F(InProcessNode, getLastLocalBlockTimestampError) { + class GetBlockTimestampErrorCore : public ICoreStub { + public: + virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) override { + return true; + } + + virtual bool getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) override { + return false; + } + }; + + GetBlockTimestampErrorCore core; + CryptoNote::InProcessNode newNode(core, protocolQueryStub); + + CallbackStatus initStatus; + newNode.init([&initStatus] (std::error_code ec) { initStatus.setStatus(ec); }); + ASSERT_TRUE(initStatus.wait()); + + ASSERT_THROW(newNode.getLastLocalBlockTimestamp(), std::exception); +} + +//TODO: make relayTransaction unit test +//TODO: make getNewBlocks unit test +//TODO: make queryBlocks unit test diff --git a/tests/unit_tests/test_transfers.cpp b/tests/unit_tests/test_transfers.cpp new file mode 100644 index 0000000000..678a9ad8cf --- /dev/null +++ b/tests/unit_tests/test_transfers.cpp @@ -0,0 +1,429 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "transfers/BlockchainSynchronizer.h" +#include "transfers/TransfersSynchronizer.h" + +#include "INodeStubs.h" +#include "TestBlockchainGenerator.h" +#include "TransactionApiHelpers.h" +#include "cryptonote_core/TransactionApi.h" + +#include + +#include +#include + +using namespace CryptoNote; + +class TransfersObserver : public ITransfersObserver { +public: + + virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override { + std::lock_guard lk(m_mutex); + m_transfers.emplace_back(transactionHash); + } + + std::vector m_transfers; + std::mutex m_mutex; +}; + +class TransfersApi : public ::testing::Test, public IBlockchainSynchronizerObserver { +public: + + TransfersApi() : + m_currency(cryptonote::CurrencyBuilder().currency()), + generator(m_currency), + m_node(generator), + m_sync(m_node, m_currency.genesisBlockHash()), + m_transfersSync(m_currency, m_sync, m_node) { + } + + void addAccounts(size_t count) { + while (count--) { + m_accounts.push_back(generateAccountKeys()); + } + } + + void addPaymentAccounts(size_t count) { + auto viewKeys = generateKeys(); + while (count--) { + m_accounts.push_back(accountKeysFromKeypairs(viewKeys, generateKeys())); + } + } + + void addMinerAccount() { + m_accounts.push_back(reinterpret_cast(generator.getMinerAccount())); + } + + AccountSubscription createSubscription(size_t acc, uint64_t timestamp = 0) { + const auto& keys = m_accounts[acc]; + AccountSubscription sub; + sub.keys = keys; + sub.syncStart.timestamp = timestamp; + sub.syncStart.height = 0; + sub.transactionSpendableAge = 5; + return sub; + } + + void subscribeAccounts() { + + m_transferObservers.reset(new TransfersObserver[m_accounts.size()]); + + for (size_t i = 0; i < m_accounts.size(); ++i) { + m_subscriptions.push_back(&m_transfersSync.addSubscription(createSubscription(i))); + m_subscriptions.back()->addObserver(&m_transferObservers[i]); + } + } + + void startSync() { + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_sync.start(); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + } + + void refreshSync() { + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_sync.lastKnownBlockHeightUpdated(0); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + } + + void synchronizationCompleted(std::error_code result) override { + decltype(syncCompleted) detachedPromise = std::move(syncCompleted); + detachedPromise.set_value(result); + } + + void generateMoneyForAccount(size_t idx) { + generator.getBlockRewardForAddress( + reinterpret_cast(m_accounts[idx].address)); + } + + std::error_code submitTransaction(ITransactionReader& tx) { + auto data = tx.getTransactionData(); + + cryptonote::blobdata txblob(data.data(), data.data() + data.size()); + cryptonote::Transaction outTx; + cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + + std::promise result; + m_node.relayTransaction(outTx, [&result](std::error_code ec) { + std::promise detachedPromise = std::move(result); + detachedPromise.set_value(ec); + }); + return result.get_future().get(); + } + +protected: + + boost::scoped_array m_transferObservers; + std::vector m_accounts; + std::vector m_subscriptions; + + cryptonote::Currency m_currency; + TestBlockchainGenerator generator; + INodeTrivialRefreshStub m_node; + BlockchainSynchronizer m_sync; + TransfersSyncronizer m_transfersSync; + + std::promise syncCompleted; + std::future syncCompletedFuture; +}; + + +namespace CryptoNote { + inline bool operator == (const TransactionOutputInformation& t1, const TransactionOutputInformation& t2) { + return + t1.type == t2.type && + t1.amount == t2.amount && + t1.outputInTransaction == t2.outputInTransaction && + t1.transactionPublicKey == t2.transactionPublicKey; + } +} + + +TEST_F(TransfersApi, testSubscriptions) { + addAccounts(1); + + m_transfersSync.addSubscription(createSubscription(0)); + + std::vector subscriptions; + + m_transfersSync.getSubscriptions(subscriptions); + + const auto& addr = m_accounts[0].address; + + ASSERT_EQ(1, subscriptions.size()); + ASSERT_EQ(addr, subscriptions[0]); + ASSERT_TRUE(m_transfersSync.getSubscription(addr) != 0); + ASSERT_TRUE(m_transfersSync.removeSubscription(addr)); + + subscriptions.clear(); + m_transfersSync.getSubscriptions(subscriptions); + ASSERT_EQ(0, subscriptions.size()); +} + +TEST_F(TransfersApi, syncOneBlock) { + addAccounts(2); + subscribeAccounts(); + + generator.getBlockRewardForAddress(reinterpret_cast(m_accounts[0].address)); + generator.generateEmptyBlocks(15); + + startSync(); + + auto& tc1 = m_transfersSync.getSubscription(m_accounts[0].address)->getContainer(); + auto& tc2 = m_transfersSync.getSubscription(m_accounts[1].address)->getContainer(); + + ASSERT_NE(&tc1, &tc2); + + ASSERT_GT(tc1.balance(ITransfersContainer::IncludeAll), 0); + ASSERT_GT(tc1.transfersCount(), 0); + ASSERT_EQ(0, tc2.transfersCount()); +} + + +TEST_F(TransfersApi, syncMinerAcc) { + addMinerAccount(); + subscribeAccounts(); + + generator.generateEmptyBlocks(10); + + startSync(); + + ASSERT_NE(0, m_subscriptions[0]->getContainer().transfersCount()); +} + + +namespace { + std::unique_ptr createMoneyTransfer( + uint64_t amount, + uint64_t fee, + const AccountKeys& senderKeys, + const AccountAddress& reciever, + ITransfersContainer& tc) { + + std::vector transfers; + tc.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + + auto tx = createTransaction(); + + std::vector> inputs; + + uint64_t foundMoney = 0; + + for (const auto& t : transfers) { + TransactionTypes::InputKeyInfo info; + + info.amount = t.amount; + + TransactionTypes::GlobalOutput globalOut; + globalOut.outputIndex = t.globalOutputIndex; + globalOut.targetKey = t.outputKey; + info.outputs.push_back(globalOut); + + info.realOutput.outputInTransaction = t.outputInTransaction; + info.realOutput.transactionIndex = 0; + info.realOutput.transactionPublicKey = t.transactionPublicKey; + + KeyPair kp; + tx->addInput(senderKeys, info, kp); + + inputs.push_back(std::make_pair(info, kp)); + + foundMoney += info.amount; + + if (foundMoney >= amount + fee) { + break; + } + } + + // output to reciever + tx->addOutput(amount, reciever); + // change + uint64_t change = foundMoney - amount - fee; + if (change) { + tx->addOutput(change, senderKeys.address); + } + + for (size_t inputIdx = 0; inputIdx < inputs.size(); ++inputIdx) { + tx->signInputKey(inputIdx, inputs[inputIdx].first, inputs[inputIdx].second); + } + + return tx; + } +} + +TEST_F(TransfersApi, moveMoney) { + addMinerAccount(); + addAccounts(2); + subscribeAccounts(); + + generator.generateEmptyBlocks(2 * m_currency.minedMoneyUnlockWindow()); + + // sendAmount is an even number + uint64_t sendAmount = (cryptonote::get_outs_money_amount(generator.getBlockchain()[1].minerTx) / 4) * 2; + auto fee = m_currency.minimumFee(); + + startSync(); + + auto& tc0 = m_subscriptions[0]->getContainer(); + auto prevBalance = tc0.balance(); + + ASSERT_LE(sendAmount, tc0.balance(ITransfersContainer::IncludeAllUnlocked)); + + auto tx = createMoneyTransfer(sendAmount, fee, m_accounts[0], m_accounts[1].address, tc0); + submitTransaction(*tx); + + refreshSync(); + + ASSERT_EQ(1, m_transferObservers[1].m_transfers.size()); + ASSERT_EQ(tx->getTransactionHash(), m_transferObservers[1].m_transfers[0]); + + auto& tc1 = m_subscriptions[1]->getContainer(); + + ASSERT_EQ(sendAmount, tc1.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(0, tc1.balance(ITransfersContainer::IncludeAllUnlocked)); + + generator.generateEmptyBlocks(m_currency.minedMoneyUnlockWindow()); // unlock money + + refreshSync(); + + ASSERT_EQ(sendAmount, tc1.balance(ITransfersContainer::IncludeAllUnlocked)); + + auto tx2 = createMoneyTransfer(sendAmount / 2, fee, m_accounts[1], m_accounts[2].address, tc1); + submitTransaction(*tx2); + + refreshSync(); + + ASSERT_EQ(2, m_transferObservers[1].m_transfers.size()); + ASSERT_EQ(tx2->getTransactionHash(), m_transferObservers[1].m_transfers.back()); + + ASSERT_EQ(sendAmount / 2 - fee, m_subscriptions[1]->getContainer().balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(sendAmount / 2, m_subscriptions[2]->getContainer().balance(ITransfersContainer::IncludeAll)); +} + + +struct lessOutKey { + bool operator()(const TransactionOutputInformation& t1, const TransactionOutputInformation& t2) { + return t1.outputKey < t2.outputKey; + } +}; + +bool compareStates(TransfersSyncronizer& sync1, TransfersSyncronizer& sync2) { + + std::vector subs; + sync1.getSubscriptions(subs); + + for (const auto& s : subs) { + auto& tc1 = sync1.getSubscription(s)->getContainer(); + + if (sync2.getSubscription(s) == nullptr) + return false; + + auto& tc2 = sync2.getSubscription(s)->getContainer(); + + std::vector out1; + std::vector out2; + + tc1.getOutputs(out1); + tc2.getOutputs(out2); + + std::sort(out1.begin(), out1.end(), lessOutKey()); + std::sort(out2.begin(), out2.end(), lessOutKey()); + + if (out1 != out2) + return false; + + } + + return true; +} + +TEST_F(TransfersApi, state) { + addMinerAccount(); + subscribeAccounts(); + + generator.generateEmptyBlocks(20); + + startSync(); + + m_sync.stop(); + std::stringstream memstm; + m_transfersSync.save(memstm); + m_sync.start(); + + BlockchainSynchronizer bsync2(m_node, m_currency.genesisBlockHash()); + TransfersSyncronizer sync2(m_currency, bsync2, m_node); + + for (size_t i = 0; i < m_accounts.size(); ++i) { + sync2.addSubscription(createSubscription(i)); + } + + sync2.load(memstm); + + // compare transfers + ASSERT_TRUE(compareStates(m_transfersSync, sync2)); + + // generate more blocks + generator.generateEmptyBlocks(10); + + refreshSync(); + + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + bsync2.addObserver(this); + bsync2.start(); + syncCompletedFuture.get(); + bsync2.removeObserver(this); + + // check again + ASSERT_TRUE(compareStates(m_transfersSync, sync2)); +} + +TEST_F(TransfersApi, sameTrackingKey) { + + size_t offset = 2; // miner account + ordinary account + size_t paymentAddresses = 1000; + size_t payments = 10; + + addMinerAccount(); + addAccounts(1); + addPaymentAccounts(paymentAddresses); + + subscribeAccounts(); + + for (size_t i = 0; i < payments; ++i) { + generateMoneyForAccount(i + offset); + } + + startSync(); + + for (size_t i = 0; i < payments; ++i) { + auto sub = m_subscriptions[offset + i]; + EXPECT_NE(0, sub->getContainer().balance(ITransfersContainer::IncludeAll)); + } + +} + diff --git a/tests/unit_tests/test_tx_pool_detach.cpp b/tests/unit_tests/test_tx_pool_detach.cpp new file mode 100755 index 0000000000..f8fe3efb5c --- /dev/null +++ b/tests/unit_tests/test_tx_pool_detach.cpp @@ -0,0 +1,443 @@ +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "transfers/BlockchainSynchronizer.h" +#include "transfers/TransfersSynchronizer.h" + +#include "INodeStubs.h" +#include "TestBlockchainGenerator.h" +#include "TransactionApiHelpers.h" +#include "cryptonote_core/TransactionApi.h" + +#include "wallet/Wallet.h" + +#include + +#include +#include + +using namespace CryptoNote; + +/* +class TransfersObserver : public ITransfersObserver { +public: + + virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash, + uint64_t amountIn, uint64_t amountOut) override { + std::lock_guard lk(m_mutex); + m_transfers.push_back(std::make_pair(transactionHash, amountIn - amountOut)); + } + + std::vector> m_transfers; + std::mutex m_mutex; +}; */ + + +class INodeStubWithPoolTx : public INodeTrivialRefreshStub { +public: + INodeStubWithPoolTx(TestBlockchainGenerator& generator) : INodeTrivialRefreshStub(generator), detached(false) {} + + void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) override { + relayedTxs.push_back(std::make_pair(this->getLastLocalBlockHeight(), transaction)); + INodeTrivialRefreshStub::relayTransaction(transaction, callback); + } + + void startAlternativeChain(uint64_t height) override { + INodeTrivialRefreshStub::startAlternativeChain(height); + detachHeight = height; + detached = true; + } + + + void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override + { + std::sort(relayedTxs.begin(), relayedTxs.end(), [](const std::pair& val1, const std::pair& val2)->bool {return val1.first < val2.first; }); + is_bc_actual = true; + + if (detached) { + size_t i = 0; + for (; i < relayedTxs.size(); ++i) { + if (relayedTxs[i].first >= detachHeight) { + break; + } + } + + for (; i < relayedTxs.size(); ++i) { + new_txs.push_back(relayedTxs[i].second); + } + } + + callback(std::error_code()); + }; + + + std::vector> relayedTxs; + uint64_t detachHeight; + bool detached; + +}; + + +class DetachTest : public ::testing::Test, public IBlockchainSynchronizerObserver { +public: + + DetachTest() : + m_currency(cryptonote::CurrencyBuilder().currency()), + generator(m_currency), + m_node(generator), + m_sync(m_node, m_currency.genesisBlockHash()), + m_transfersSync(m_currency, m_sync, m_node) { + } + + void addAccounts(size_t count) { + while (count--) { + m_accounts.push_back(generateAccountKeys()); + } + } + + void addMinerAccount() { + m_accounts.push_back(reinterpret_cast(generator.getMinerAccount())); + } + + AccountSubscription createSubscription(size_t acc, uint64_t timestamp = 0) { + const auto& keys = m_accounts[acc]; + AccountSubscription sub; + sub.keys = keys; + sub.syncStart.timestamp = timestamp; + sub.syncStart.height = 0; + sub.transactionSpendableAge = 5; + return sub; + } + + void subscribeAccounts() { + + // m_transferObservers.reset(new TransfersObserver[m_accounts.size()]); + + for (size_t i = 0; i < m_accounts.size(); ++i) { + m_subscriptions.push_back(&m_transfersSync.addSubscription(createSubscription(i))); + //m_subscriptions.back()->addObserver(&m_transferObservers[i]); + } + } + + void generateMoneyForAccount(size_t idx) { + generator.getBlockRewardForAddress( + reinterpret_cast(m_accounts[idx].address)); + } + + std::error_code submitTransaction(ITransactionReader& tx) { + auto data = tx.getTransactionData(); + + cryptonote::blobdata txblob(data.data(), data.data() + data.size()); + cryptonote::Transaction outTx; + cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + + std::promise result; + m_node.relayTransaction(outTx, [&result](std::error_code ec) { result.set_value(ec); }); + return result.get_future().get(); + } + + void synchronizationCompleted(std::error_code result) override { + decltype(syncCompleted) detachedPromise = std::move(syncCompleted); + detachedPromise.set_value(result); + } + + +protected: + std::vector m_accounts; + std::vector m_subscriptions; + + cryptonote::Currency m_currency; + TestBlockchainGenerator generator; + INodeStubWithPoolTx m_node; + BlockchainSynchronizer m_sync; + TransfersSyncronizer m_transfersSync; + + std::promise syncCompleted; + std::future syncCompletedFuture; + +}; + + +namespace CryptoNote { +inline bool operator == (const TransactionOutputInformation& t1, const TransactionOutputInformation& t2) { + return + t1.type == t2.type && + t1.amount == t2.amount && + t1.outputInTransaction == t2.outputInTransaction && + t1.transactionPublicKey == t2.transactionPublicKey; +} +} + +namespace { + std::unique_ptr createMoneyTransfer( + uint64_t amount, + uint64_t fee, + const AccountKeys& senderKeys, + const AccountAddress& reciever, + ITransfersContainer& tc) { + + std::vector transfers; + tc.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + + auto tx = createTransaction(); + + std::vector> inputs; + + uint64_t foundMoney = 0; + + for (const auto& t : transfers) { + TransactionTypes::InputKeyInfo info; + + info.amount = t.amount; + + TransactionTypes::GlobalOutput globalOut; + globalOut.outputIndex = t.globalOutputIndex; + globalOut.targetKey = t.outputKey; + info.outputs.push_back(globalOut); + + info.realOutput.outputInTransaction = t.outputInTransaction; + info.realOutput.transactionIndex = 0; + info.realOutput.transactionPublicKey = t.transactionPublicKey; + + KeyPair kp; + tx->addInput(senderKeys, info, kp); + + inputs.push_back(std::make_pair(info, kp)); + + foundMoney += info.amount; + + if (foundMoney >= amount + fee) { + break; + } + } + + // output to reciever + tx->addOutput(amount, reciever); + // change + uint64_t change = foundMoney - amount - fee; + if (change) { + tx->addOutput(change, senderKeys.address); + } + + for (size_t inputIdx = 0; inputIdx < inputs.size(); ++inputIdx) { + tx->signInputKey(inputIdx, inputs[inputIdx].first, inputs[inputIdx].second); + } + + return tx; + } +} + + +TEST_F(DetachTest, testBlockchainDetach) { + uint64_t sendAmount = 70000000000000; + auto fee = m_currency.minimumFee(); + + addMinerAccount(); + addAccounts(2); + subscribeAccounts(); + + generator.generateEmptyBlocks(20); + + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_sync.start(); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + + auto& tc = m_subscriptions[0]->getContainer(); + auto prevBalance = tc.balance(); + + ASSERT_LE(sendAmount, tc.balance(ITransfersContainer::IncludeAllUnlocked)); + + auto tx = createMoneyTransfer(sendAmount, fee, m_accounts[0], m_accounts[1].address, tc); + submitTransaction(*tx); + + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_node.updateObservers(); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + auto& tc2 = m_subscriptions[1]->getContainer(); + + ASSERT_EQ(sendAmount, tc2.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(0, tc2.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(1, tc2.transactionsCount()); + + std::vector unconfirmed; + tc2.getUnconfirmedTransactions(unconfirmed); + ASSERT_EQ(0, unconfirmed.size()); + + m_node.startAlternativeChain(m_node.getLastLocalBlockHeight() - 1); + generator.generateEmptyBlocks(2); + + syncCompleted = std::promise(); + syncCompletedFuture = syncCompleted.get_future(); + m_sync.addObserver(this); + m_node.updateObservers(); + syncCompletedFuture.get(); + m_sync.removeObserver(this); + auto& tc3 = m_subscriptions[1]->getContainer(); + + ASSERT_EQ(sendAmount, tc3.balance(ITransfersContainer::IncludeAll)); + ASSERT_EQ(0, tc3.balance(ITransfersContainer::IncludeAllUnlocked)); + ASSERT_EQ(1, tc3.transactionsCount()); + + tc3.getUnconfirmedTransactions(unconfirmed); + ASSERT_EQ(1, unconfirmed.size()); + ASSERT_EQ(reinterpret_cast(unconfirmed[0]), tx->getTransactionHash()); + m_sync.stop(); +} + + +struct CompletionWalletObserver : public IWalletObserver { + virtual void synchronizationCompleted(std::error_code result) override { + syncCompleted.set_value(result); + } + + std::promise syncCompleted; + std::future syncCompletedFuture; +}; + + +struct WaitForExternalTransactionObserver : public CryptoNote::IWalletObserver { +public: + WaitForExternalTransactionObserver() {} + std::promise promise; + + virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override { + promise.set_value(transactionId); + } + +}; + +TEST_F(DetachTest, testDetachWithWallet) { + auto fee = m_currency.minimumFee(); + + generator.generateEmptyBlocks(5); + Wallet Alice(m_currency, m_node); + Wallet Bob(m_currency, m_node); + + CompletionWalletObserver AliceCompleted, BobCompleted; + AliceCompleted.syncCompleted = std::promise(); + AliceCompleted.syncCompletedFuture = AliceCompleted.syncCompleted.get_future(); + BobCompleted.syncCompleted = std::promise(); + BobCompleted.syncCompletedFuture = BobCompleted.syncCompleted.get_future(); + Alice.addObserver(&AliceCompleted); + Bob.addObserver(&BobCompleted); + Alice.initAndGenerate("pass"); + Bob.initAndGenerate("pass"); + AliceCompleted.syncCompletedFuture.get(); + BobCompleted.syncCompletedFuture.get(); + Alice.removeObserver(&AliceCompleted); + Bob.removeObserver(&BobCompleted); + + + cryptonote::AccountPublicAddress AliceAddr; + WalletAccountKeys AliceKeys; + Alice.getAccountKeys(AliceKeys); + AliceAddr.m_spendPublicKey = *reinterpret_cast(&AliceKeys.spendPublicKey); + AliceAddr.m_viewPublicKey = *reinterpret_cast(&AliceKeys.viewPublicKey); + generator.getBlockRewardForAddress(AliceAddr); + + + generator.generateEmptyBlocks(10); + + AliceCompleted.syncCompleted = std::promise(); + AliceCompleted.syncCompletedFuture = AliceCompleted.syncCompleted.get_future(); + BobCompleted.syncCompleted = std::promise(); + BobCompleted.syncCompletedFuture = BobCompleted.syncCompleted.get_future(); + Alice.addObserver(&AliceCompleted); + Bob.addObserver(&BobCompleted); + + m_node.updateObservers(); + + AliceCompleted.syncCompletedFuture.get(); + BobCompleted.syncCompletedFuture.get(); + Alice.removeObserver(&AliceCompleted); + Bob.removeObserver(&BobCompleted); + + + ASSERT_EQ(0, Alice.pendingBalance()); + ASSERT_NE(0, Alice.actualBalance()); + + CryptoNote::Transfer tr; + + tr.amount = Alice.actualBalance() / 2; + tr.address = Bob.getAddress(); + + Alice.sendTransaction(tr, fee); + + WaitForExternalTransactionObserver etxo; + auto externalTxFuture = etxo.promise.get_future(); + Bob.addObserver(&etxo); + AliceCompleted.syncCompleted = std::promise(); + AliceCompleted.syncCompletedFuture = AliceCompleted.syncCompleted.get_future(); + BobCompleted.syncCompleted = std::promise(); + BobCompleted.syncCompletedFuture = BobCompleted.syncCompleted.get_future(); + Alice.addObserver(&AliceCompleted); + Bob.addObserver(&BobCompleted); + + m_node.updateObservers(); + + AliceCompleted.syncCompletedFuture.get(); + BobCompleted.syncCompletedFuture.get(); + Alice.removeObserver(&AliceCompleted); + Bob.removeObserver(&BobCompleted); + + auto txId = externalTxFuture.get(); + Bob.removeObserver(&etxo); + + TransactionInfo txInfo; + + Bob.getTransaction(txId, txInfo); + + + ASSERT_EQ(txInfo.blockHeight, m_node.getLastLocalBlockHeight()); + ASSERT_EQ(txInfo.totalAmount, tr.amount); + + ASSERT_EQ(Bob.pendingBalance(), tr.amount); + ASSERT_EQ(Bob.actualBalance(), 0); + + m_node.startAlternativeChain(m_node.getLastLocalBlockHeight() - 1); + generator.generateEmptyBlocks(2); + + //sync Bob + AliceCompleted.syncCompleted = std::promise(); + AliceCompleted.syncCompletedFuture = AliceCompleted.syncCompleted.get_future(); + BobCompleted.syncCompleted = std::promise(); + BobCompleted.syncCompletedFuture = BobCompleted.syncCompleted.get_future(); + Alice.addObserver(&AliceCompleted); + Bob.addObserver(&BobCompleted); + + m_node.updateObservers(); + + AliceCompleted.syncCompletedFuture.get(); + BobCompleted.syncCompletedFuture.get(); + Alice.removeObserver(&AliceCompleted); + Bob.removeObserver(&BobCompleted); + + Bob.getTransaction(txId, txInfo); + ASSERT_EQ(txInfo.blockHeight, UNCONFIRMED_TRANSACTION_HEIGHT); + ASSERT_EQ(txInfo.totalAmount, tr.amount); + + ASSERT_EQ(Bob.pendingBalance(), tr.amount); + ASSERT_EQ(Bob.actualBalance(), 0); +} diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index f64f8315cc..eed3e7b997 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -21,6 +21,7 @@ #include #include +#include "EventWaiter.h" #include "INode.h" #include "wallet/Wallet.h" #include "cryptonote_core/account.h" @@ -29,98 +30,81 @@ #include "INodeStubs.h" #include "TestBlockchainGenerator.h" + class TrivialWalletObserver : public CryptoNote::IWalletObserver { public: - TrivialWalletObserver() {} + TrivialWalletObserver() : actualBalance(0), pendingBalance(0) {} bool waitForSyncEnd() { - auto future = syncPromise.get_future(); - return future.wait_for(std::chrono::seconds(3)) == std::future_status::ready; + return synced.wait_for(std::chrono::milliseconds(3000)); } bool waitForSendEnd(std::error_code& ec) { - auto future = sendPromise.get_future(); - - if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { - return false; - } - - ec = future.get(); + if (!sent.wait_for(std::chrono::milliseconds(5000))) return false; + ec = sendResult; return true; - } bool waitForSaveEnd(std::error_code& ec) { - auto future = savePromise.get_future(); - - if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { - return false; - } - - ec = future.get(); + if (!saved.wait_for(std::chrono::milliseconds(5000))) return false; + ec = saveResult; return true; } bool waitForLoadEnd(std::error_code& ec) { - auto future = loadPromise.get_future(); - - if (future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) { - return false; - } - - ec = future.get(); + if (!loaden.wait_for(std::chrono::milliseconds(5000))) return false; + ec = loadResult; return true; } - void reset() { - syncPromise = std::promise(); - sendPromise = std::promise(); - savePromise = std::promise(); - loadPromise = std::promise(); + virtual void synchronizationCompleted(std::error_code result) override { + synced.notify(); } - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total, std::error_code result) { - if (result) { - syncPromise.set_value(); - return; - } - - if (current == total) { - syncPromise.set_value(); - } + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { + sendResult = result; + sent.notify(); } - virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) { - sendPromise.set_value(result); + virtual void saveCompleted(std::error_code result) override { + saveResult = result; + saved.notify(); } - virtual void saveCompleted(std::error_code result) { - savePromise.set_value(result); + virtual void initCompleted(std::error_code result) override { + loadResult = result; + loaden.notify(); } - virtual void initCompleted(std::error_code result) { - loadPromise.set_value(result); + virtual void actualBalanceUpdated(uint64_t actualBalance) override { + // std::cout << "actual balance: " << actualBalance << std::endl; + this->actualBalance = actualBalance; } - virtual void actualBalanceUpdated(uint64_t actualBalance) { -// std::cout << "actual balance: " << actualBalance << std::endl; - } - virtual void pendingBalanceUpdated(uint64_t pendingBalance) { + virtual void pendingBalanceUpdated(uint64_t pendingBalance) override { // std::cout << "pending balance: " << pendingBalance << std::endl; + this->pendingBalance = pendingBalance; } - std::promise syncPromise; - std::promise sendPromise; - std::promise savePromise; - std::promise loadPromise; + std::error_code sendResult; + std::error_code saveResult; + std::error_code loadResult; + + std::atomic actualBalance; + std::atomic pendingBalance; + + EventWaiter synced; + EventWaiter saved; + EventWaiter loaden; + EventWaiter sent; }; struct SaveOnInitWalletObserver: public CryptoNote::IWalletObserver { SaveOnInitWalletObserver(CryptoNote::Wallet* wallet) : wallet(wallet) {}; virtual ~SaveOnInitWalletObserver() {} - virtual void initCompleted(std::error_code result) { + virtual void initCompleted(std::error_code result) override { wallet->save(stream, true, true); } @@ -139,29 +123,32 @@ CryptoNote::TransactionId TransferMoney(CryptoNote::Wallet& from, CryptoNote::Wa } void WaitWalletSync(TrivialWalletObserver* observer) { - observer->reset(); ASSERT_TRUE(observer->waitForSyncEnd()); } void WaitWalletSend(TrivialWalletObserver* observer) { std::error_code ec; - observer->reset(); ASSERT_TRUE(observer->waitForSendEnd(ec)); } void WaitWalletSend(TrivialWalletObserver* observer, std::error_code& ec) { - observer->reset(); ASSERT_TRUE(observer->waitForSendEnd(ec)); } void WaitWalletSave(TrivialWalletObserver* observer) { - observer->reset(); std::error_code ec; ASSERT_TRUE(observer->waitForSaveEnd(ec)); EXPECT_FALSE(ec); } +void WaitWalletLoad(TrivialWalletObserver* observer) { + std::error_code ec; + + ASSERT_TRUE(observer->waitForLoadEnd(ec)); + EXPECT_FALSE(ec); +} + class WalletApi : public ::testing::Test { public: @@ -203,10 +190,12 @@ void WalletApi::SetUp() { } void WalletApi::prepareAliceWallet() { - aliceNode.reset(new INodeTrivialRefreshStub(generator)); - aliceWalletObserver.reset(new TrivialWalletObserver()); + decltype(aliceNode) newNode(new INodeTrivialRefreshStub(generator)); + + alice.reset(new CryptoNote::Wallet(m_currency, *newNode)); + aliceNode = newNode; - alice.reset(new CryptoNote::Wallet(m_currency, *aliceNode)); + aliceWalletObserver.reset(new TrivialWalletObserver()); alice->addObserver(aliceWalletObserver.get()); } @@ -271,7 +260,6 @@ void WalletApi::performTransferWithErrorTx(const std::array& amounts void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn, const std::string& extra) { prepareBobWallet(); - prepareCarolWallet(); alice->initAndGenerate("pass"); @@ -282,12 +270,15 @@ void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mix generator.generateEmptyBlocks(10); uint64_t expectedBalance = TEST_BLOCK_REWARD; - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - EXPECT_EQ(alice->pendingBalance(), expectedBalance); - EXPECT_EQ(alice->actualBalance(), expectedBalance); + EXPECT_EQ(0, alice->pendingBalance()); + EXPECT_EQ(expectedBalance, alice->actualBalance()); + + EXPECT_EQ(expectedBalance, aliceWalletObserver->actualBalance); + EXPECT_EQ(0, aliceWalletObserver->pendingBalance); bob->initAndGenerate("pass2"); @@ -297,25 +288,23 @@ void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mix generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - bob->startRefresh(); + bobNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); - EXPECT_EQ(bob->pendingBalance(), transferAmount); - EXPECT_EQ(bob->actualBalance(), transferAmount); + EXPECT_EQ(0, bob->pendingBalance()); + EXPECT_EQ(transferAmount, bob->actualBalance()); - EXPECT_EQ(alice->pendingBalance(), expectedBalance - transferAmount - fee); - EXPECT_EQ(alice->actualBalance(), expectedBalance - transferAmount - fee); + EXPECT_EQ(0, alice->pendingBalance()); + EXPECT_EQ(expectedBalance - transferAmount - fee, alice->actualBalance()); alice->shutdown(); bob->shutdown(); } void WaitWalletLoad(TrivialWalletObserver* observer, std::error_code& ec) { - observer->reset(); - ASSERT_TRUE(observer->waitForLoadEnd(ec)); } @@ -323,7 +312,7 @@ TEST_F(WalletApi, initAndSave) { SaveOnInitWalletObserver saveOnInit(alice.get()); alice->addObserver(&saveOnInit); alice->initAndGenerate("pass"); - ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); alice->shutdown(); } @@ -339,7 +328,7 @@ TEST_F(WalletApi, refreshWithMoney) { ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getBlockRewardForAddress(address); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); @@ -391,9 +380,11 @@ TEST_F(WalletApi, TransactionsAndTransfersAfterSend) { //unblock Alice's money generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + EXPECT_EQ(alice->getTransactionCount(), 1); + bob->initAndGenerate("pass2"); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); @@ -405,6 +396,10 @@ TEST_F(WalletApi, TransactionsAndTransfersAfterSend) { int64_t amount2 = 1234500; ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount2, fee, 0)); ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); int64_t amount3 = 1234567; ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, amount3, fee, 0)); @@ -423,28 +418,28 @@ TEST_F(WalletApi, TransactionsAndTransfersAfterSend) { //Transaction with id = 0 is tested in getTransactionSuccess ASSERT_TRUE(alice->getTransaction(1, tx)); - EXPECT_EQ(tx.totalAmount, amount1 + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amount1 + fee)); EXPECT_EQ(tx.fee, fee); EXPECT_EQ(tx.isCoinbase, false); EXPECT_EQ(tx.firstTransferId, 0); EXPECT_EQ(tx.transferCount, 1); ASSERT_TRUE(alice->getTransaction(2, tx)); - EXPECT_EQ(tx.totalAmount, amount2 + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amount2 + fee)); EXPECT_EQ(tx.fee, fee); EXPECT_EQ(tx.isCoinbase, false); EXPECT_EQ(tx.firstTransferId, 1); EXPECT_EQ(tx.transferCount, 1); ASSERT_TRUE(alice->getTransaction(3, tx)); - EXPECT_EQ(tx.totalAmount, amount3 + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amount3 + fee)); EXPECT_EQ(tx.fee, fee); EXPECT_EQ(tx.isCoinbase, false); EXPECT_EQ(tx.firstTransferId, 2); EXPECT_EQ(tx.transferCount, 1); ASSERT_TRUE(alice->getTransaction(4, tx)); - EXPECT_EQ(tx.totalAmount, amount4 + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amount4 + fee)); EXPECT_EQ(tx.fee, fee); EXPECT_EQ(tx.isCoinbase, false); EXPECT_EQ(tx.firstTransferId, 3); @@ -487,7 +482,7 @@ TEST_F(WalletApi, saveAndLoadCacheDetails) { //unblock Alice's money generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); bob->initAndGenerate("pass2"); @@ -526,25 +521,38 @@ TEST_F(WalletApi, saveAndLoadCacheDetails) { alice->save(archive, true, true); ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + auto prevActualBalance = alice->actualBalance(); + auto prevPendingBalance = alice->pendingBalance(); + alice->shutdown(); prepareAliceWallet(); alice->initAndLoad(archive, "pass"); + std::error_code ec; + + WaitWalletLoad(aliceWalletObserver.get(), ec); + ASSERT_FALSE(ec); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); ASSERT_EQ(alice->getTransactionCount(), 3); ASSERT_EQ(alice->getTransferCount(), 3); + EXPECT_EQ(prevActualBalance, alice->actualBalance()); + EXPECT_EQ(prevPendingBalance, alice->pendingBalance()); + CryptoNote::TransactionInfo tx; ASSERT_TRUE(alice->getTransaction(1, tx)); - EXPECT_EQ(tx.totalAmount, amount1 + amount2 + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amount1 + amount2 + fee)); EXPECT_EQ(tx.fee, fee); EXPECT_EQ(tx.firstTransferId, 0); EXPECT_EQ(tx.transferCount, 2); ASSERT_TRUE(alice->getTransaction(2, tx)); - EXPECT_EQ(tx.totalAmount, amount3 + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amount3 + fee)); EXPECT_EQ(tx.fee, fee); EXPECT_EQ(tx.firstTransferId, 2); EXPECT_EQ(tx.transferCount, 1); @@ -584,7 +592,7 @@ TEST_F(WalletApi, getTransactionSuccess) { ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); CryptoNote::TransactionInfo tx; @@ -681,31 +689,52 @@ TEST_F(WalletApi, detachBlockchain) { ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); aliceNode->startAlternativeChain(3); generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - EXPECT_EQ(alice->actualBalance(), 0); - EXPECT_EQ(alice->pendingBalance(), 0); + EXPECT_EQ(0, alice->actualBalance()); + EXPECT_EQ(0, alice->pendingBalance()); alice->shutdown(); } +TEST_F(WalletApi, saveAndLoad) { + alice->initAndGenerate("pass"); + + std::error_code result; + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); + ASSERT_EQ(result.value(), 0); + + std::stringstream archive; + ASSERT_NO_FATAL_FAILURE(alice->save(archive)); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + prepareAliceWallet(); + alice->initAndLoad(archive, "pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); + ASSERT_EQ(result.value(), 0); +} + TEST_F(WalletApi, saveAndLoadErroneousTxsCacheDetails) { prepareBobWallet(); prepareCarolWallet(); + std::error_code result; + alice->initAndGenerate("pass"); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); bob->initAndGenerate("pass"); @@ -732,7 +761,6 @@ TEST_F(WalletApi, saveAndLoadErroneousTxsCacheDetails) { prepareAliceWallet(); alice->initAndLoad(archive, "pass"); - std::error_code result; ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); ASSERT_EQ(result.value(), 0); @@ -741,7 +769,7 @@ TEST_F(WalletApi, saveAndLoadErroneousTxsCacheDetails) { CryptoNote::TransactionInfo tx; ASSERT_TRUE(alice->getTransaction(1, tx)); - EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee); + EXPECT_EQ(tx.totalAmount, -static_cast(amounts[3] + amounts[4] + fee)); EXPECT_EQ(tx.firstTransferId, 0); EXPECT_EQ(tx.transferCount, 2); @@ -767,7 +795,7 @@ TEST_F(WalletApi, saveAndLoadErroneousTxsCacheNoDetails) { ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); generator.generateEmptyBlocks(10); - alice->startRefresh(); + aliceNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); bob->initAndGenerate("pass"); @@ -798,14 +826,500 @@ TEST_F(WalletApi, saveAndLoadErroneousTxsCacheNoDetails) { ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); ASSERT_EQ(result.value(), 0); - EXPECT_EQ(alice->getTransactionCount(), 2); - EXPECT_EQ(alice->getTransferCount(), 0); + EXPECT_EQ(0, alice->getTransactionCount()); + EXPECT_EQ(0, alice->getTransferCount()); - CryptoNote::TransactionInfo tx; - ASSERT_TRUE(alice->getTransaction(1, tx)); - EXPECT_EQ(tx.totalAmount, amounts[3] + amounts[4] + fee); - EXPECT_EQ(tx.firstTransferId, CryptoNote::INVALID_TRANSFER_ID); - EXPECT_EQ(tx.transferCount, 0); + alice->shutdown(); +} + +TEST_F(WalletApi, mineSaveNoCacheNoDetailsRefresh) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + generator.getBlockRewardForAddress(address); + generator.getBlockRewardForAddress(address); + generator.getBlockRewardForAddress(address); + + aliceNode->updateObservers(); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + std::stringstream archive; + alice->save(archive, false, false); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + alice->shutdown(); + + prepareAliceWallet(); + alice->initAndLoad(archive, "pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get())); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD * 3, alice->pendingBalance()); + alice->shutdown(); +} + + +TEST_F(WalletApi, sendMoneyToMyself) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + generator.getBlockRewardForAddress(address); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::TransactionId txId = TransferMoney(*alice, *alice, 100000000, 100); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD - 100, alice->actualBalance()); + ASSERT_EQ(0, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, sendSeveralTransactions) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + for (int i = 0; i < 5; ++i) { + GetOneBlockReward(*alice); + } + + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + auto aliceBalance = alice->actualBalance(); + + uint64_t sendAmount = 100000; + uint64_t totalSentAmount = 0; + size_t transactionCount = 0; + + for (int i = 0; i < 10 && alice->actualBalance() > sendAmount; ++i) { + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = sendAmount; + + auto txId = alice->sendTransaction(tr, m_currency.minimumFee(), "", 1, 0); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + + std::error_code sendResult; + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), sendResult)); + ASSERT_EQ(std::error_code(), sendResult); + + ++transactionCount; + totalSentAmount += sendAmount; + } + + generator.generateEmptyBlocks(10); + + bobNode->updateObservers(); + + while (totalSentAmount != bob->actualBalance()) { + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + } + + EXPECT_EQ(transactionCount, bob->getTransactionCount()); + EXPECT_EQ(0, bob->pendingBalance()); + EXPECT_EQ(totalSentAmount, bob->actualBalance()); + + uint64_t aliceTotalBalance = alice->actualBalance() + alice->pendingBalance(); + EXPECT_EQ(aliceBalance - transactionCount * (sendAmount + m_currency.minimumFee()), aliceTotalBalance); +} + +TEST_F(WalletApi, balanceAfterFailedTransaction) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + auto actualBalance = alice->actualBalance(); + auto pendingBalance = alice->pendingBalance(); + + uint64_t send = 11000000; + uint64_t fee = m_currency.minimumFee(); + + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = send; + + aliceNode->setNextTransactionError(); + + alice->sendTransaction(tr, fee, "", 1, 0); + generator.generateEmptyBlocks(1); + + ASSERT_EQ(actualBalance, alice->actualBalance()); + ASSERT_EQ(pendingBalance, alice->pendingBalance()); + + alice->shutdown(); + bob->shutdown(); +} + +TEST_F(WalletApi, checkPendingBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + uint64_t startActualBalance = alice->actualBalance(); + int64_t sendAmount = 304050; + uint64_t fee = m_currency.minimumFee(); + + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = sendAmount; + + auto txId = alice->sendTransaction(tr, fee, "", 1, 0); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + + std::error_code sendResult; + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), sendResult)); + ASSERT_EQ(std::error_code(), sendResult); + + uint64_t totalBalance = alice->actualBalance() + alice->pendingBalance(); + ASSERT_EQ(startActualBalance - sendAmount - fee, totalBalance); + + generator.generateEmptyBlocks(1); + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + ASSERT_EQ(0, bob->actualBalance()); + ASSERT_EQ(sendAmount, bob->pendingBalance()); + + alice->shutdown(); + bob->shutdown(); +} + +TEST_F(WalletApi, checkChange) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + uint64_t banknote = 1000000000; + uint64_t sendAmount = 50000; + uint64_t fee = m_currency.minimumFee(); + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + generator.getSingleOutputTransaction(address, banknote); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = sendAmount; + + auto txId = alice->sendTransaction(tr, fee, "", 1, 0); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + + std::error_code sendResult; + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get(), sendResult)); + ASSERT_EQ(std::error_code(), sendResult); + + EXPECT_EQ(0, alice->actualBalance()); + EXPECT_EQ(banknote - sendAmount - fee, alice->pendingBalance()); +} + +TEST_F(WalletApi, checkBalanceAfterSend) { + alice->initAndGenerate("pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + uint64_t banknote = 1000000000; + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + + //Once wallet takes outputs in random fashion we don't know for sure which outputs will be taken. + //In this case we generate controllable set of outs. + generator.getSingleOutputTransaction(address, banknote); + generator.getSingleOutputTransaction(address, banknote); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + const uint64_t sendAmount = 10000000; + const uint64_t fee = 100; + CryptoNote::TransactionId txId = TransferMoney(*alice, *alice, sendAmount, fee); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + ASSERT_EQ(banknote, alice->actualBalance()); + ASSERT_EQ(banknote - sendAmount - fee, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, moneyInPoolDontAffectActualBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + uint64_t banknote = 1000000000; + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + generator.getSingleOutputTransaction(address, banknote); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + const uint64_t sendAmount = 10000000; + const uint64_t fee = 100; + aliceNode->setNextTransactionToPool(); + CryptoNote::TransactionId txId = TransferMoney(*alice, *bob, sendAmount, fee); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(0, alice->actualBalance()); + EXPECT_EQ(banknote - sendAmount - fee, alice->pendingBalance()); + + alice->shutdown(); + bob->shutdown(); +} + +TEST_F(WalletApi, balanceAfterTransactionsPlacedInBlockchain) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + uint64_t banknote = 1000000000; + + cryptonote::AccountPublicAddress address; + ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); + generator.getSingleOutputTransaction(address, banknote); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + const uint64_t sendAmount = 10000000; + const uint64_t fee = 100; + aliceNode->setNextTransactionToPool(); + CryptoNote::TransactionId txId = TransferMoney(*alice, *bob, sendAmount, fee); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + aliceNode->includeTransactionsFromPoolToBlock(); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + EXPECT_EQ(banknote - sendAmount - fee, alice->actualBalance()); + EXPECT_EQ(0, alice->pendingBalance()); alice->shutdown(); + bob->shutdown(); +} + +TEST_F(WalletApi, checkMyMoneyInTxPool) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + uint64_t sendAmount = 8821902; + uint64_t fee = 10000; + + aliceNode->setNextTransactionToPool(); + CryptoNote::TransactionId txId = TransferMoney(*alice, *bob, sendAmount, fee); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + EXPECT_EQ(0, bob->actualBalance()); + EXPECT_EQ(sendAmount, bob->pendingBalance()); + + alice->shutdown(); + bob->shutdown(); +} + +TEST_F(WalletApi, initWithKeys) { + CryptoNote::WalletAccountKeys accountKeys; + + uint8_t byte = 0; + + std::generate(accountKeys.spendPublicKey.begin(), accountKeys.spendPublicKey.end(), + [&byte] () { return byte++; } ); + + std::generate(accountKeys.spendSecretKey.begin(), accountKeys.spendSecretKey.end(), + [&byte] () { return byte++; } ); + + std::generate(accountKeys.viewPublicKey.begin(), accountKeys.viewPublicKey.end(), + [&byte] () { return byte++; } ); + + std::generate(accountKeys.viewSecretKey.begin(), accountKeys.viewSecretKey.end(), + [&byte] () { return byte++; } ); + + alice->initWithKeys(accountKeys, "pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get())); + + CryptoNote::WalletAccountKeys keys; + alice->getAccountKeys(keys); + + EXPECT_TRUE(std::equal(accountKeys.spendPublicKey.begin(), accountKeys.spendPublicKey.end(), keys.spendPublicKey.begin())); + EXPECT_TRUE(std::equal(accountKeys.spendSecretKey.begin(), accountKeys.spendSecretKey.end(), keys.spendSecretKey.begin())); + EXPECT_TRUE(std::equal(accountKeys.viewPublicKey.begin(), accountKeys.viewPublicKey.end(), keys.viewPublicKey.begin())); + EXPECT_TRUE(std::equal(accountKeys.viewSecretKey.begin(), accountKeys.viewSecretKey.end(), keys.viewSecretKey.begin())); + + alice->shutdown(); +} + +TEST_F(WalletApi, deleteTxFromPool) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + uint64_t sendAmount = 9748291; + uint64_t fee = 10000; + + aliceNode->setNextTransactionToPool(); + CryptoNote::TransactionId txId = TransferMoney(*alice, *bob, sendAmount, fee); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + alice->shutdown(); + + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + generator.clearTxPool(); + + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + EXPECT_EQ(0, bob->actualBalance()); + EXPECT_EQ(0, bob->pendingBalance()); + + bob->shutdown(); +} + +TEST_F(WalletApi, sendAfterFailedTransaction) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::Transfer tr; + tr.amount = 100000; + tr.address = "wrong_address"; + + EXPECT_THROW(alice->sendTransaction(tr, 1000, "", 2, 0), std::system_error); + CryptoNote::TransactionId txId = TransferMoney(*alice, *alice, 100000, 100); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + alice->shutdown(); +} + +TEST_F(WalletApi, loadingBrokenCache) { + alice->initAndGenerate("pass"); + + std::error_code result; + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + ASSERT_EQ(result.value(), 0); + + std::stringstream archive; + ASSERT_NO_FATAL_FAILURE(alice->save(archive, false, true)); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + size_t sizeWithEmptyCache = archive.str().size(); + + for (size_t i = 0; i < 3; ++i) { + GetOneBlockReward(*alice); + } + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + archive.str(""); + archive.clear(); + + ASSERT_NO_FATAL_FAILURE(alice->save(archive, false, true)); + ASSERT_NO_FATAL_FAILURE(WaitWalletSave(aliceWalletObserver.get())); + + std::string state = archive.str(); + for (size_t i = sizeWithEmptyCache; i < state.size(); ++i) { + state[i] = '\xff'; + } + archive.str(state); + + prepareAliceWallet(); + alice->initAndLoad(archive, "pass"); + + ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); + ASSERT_EQ(result.value(), 0); } diff --git a/tests/unit_tests/tx_pool.cpp b/tests/unit_tests/tx_pool.cpp index d506492d6a..f13facc61f 100644 --- a/tests/unit_tests/tx_pool.cpp +++ b/tests/unit_tests/tx_pool.cpp @@ -395,3 +395,31 @@ TEST(tx_pool, cleanup_stale_tx) ASSERT_EQ(3, pool.get_transactions_count()); } + +TEST(tx_pool, add_tx_after_cleanup) +{ + cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + TestPool pool(currency); + const uint64_t fee = currency.minimumFee(); + + time_t startTime = pool.timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, fee, 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain + ASSERT_TRUE(tvc.m_added_to_pool); + + pool.timeProvider.timeNow = startTime + currency.mempoolTxLiveTime() + 1; + pool.on_idle(); + + ASSERT_EQ(0, pool.get_transactions_count()); + + // add again + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain + ASSERT_TRUE(tvc.m_added_to_pool); + + ASSERT_EQ(1, pool.get_transactions_count()); + +} From e15c01585fe268f27173c3adaab394d5b3eb05a4 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Wed, 8 Apr 2015 16:08:54 +0100 Subject: [PATCH 22/59] Balance format improvement --- README | 4 ++-- src/simplewallet/simplewallet.cpp | 22 +++++++++++++------- src/wallet/Wallet.cpp | 2 +- tests/unit_tests/TestBlockchainGenerator.cpp | 1 + tests/unit_tests/test_tx_pool_detach.cpp | 4 ++-- tests/unit_tests/test_wallet.cpp | 4 ++-- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/README b/README index 87a89c5871..75c2dce57c 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ On *nix: -Dependencies: GCC 4.7.3 or later, CMake 2.8.6 or later, and Boost 1.53 or later (except 1.54, more details here: http://goo.gl/RrCFmA). +Dependencies: GCC 4.7.3 or later, CMake 2.8.6 or later, and Boost 1.55. You may download them from: http://gcc.gnu.org/ http://www.cmake.org/ @@ -18,7 +18,7 @@ Test suite: run `make test-release' to run tests in addition to building. Runnin Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++' before running `make'. On Windows: -Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.53 or later. You may download them from: +Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.55. You may download them from: http://www.microsoft.com/ http://www.cmake.org/ http://www.boost.org/ diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ae5ab7dfdf..34f703bf72 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -575,7 +575,9 @@ bool simple_wallet::deinit() if (!m_wallet.get()) return true; - return close_wallet(); + bool r = close_wallet(); + m_wallet->shutdown(); + return r; } //---------------------------------------------------------------------------------------------------- void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm) @@ -781,17 +783,24 @@ void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transac TransactionInfo txInfo; m_wallet->getTransaction(transactionId, txInfo); + if (txInfo.totalAmount >= 0) { message_writer(epee::log_space::console_color_green, false) << "Height " << txInfo.blockHeight << ", transaction " << epee::string_tools::pod_to_hex(txInfo.hash) << - ", received " << m_currency.formatAmount(txInfo.totalAmount); + ", received " << m_currency.formatAmount(static_cast(txInfo.totalAmount)); + } else { + message_writer(epee::log_space::console_color_magenta, false) << + "Height " << txInfo.blockHeight << + ", transaction " << epee::string_tools::pod_to_hex(txInfo.hash) << + ", spent " << m_currency.formatAmount(static_cast(-txInfo.totalAmount)); + } m_refresh_progress_reporter.update(txInfo.blockHeight, true); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) { - success_msg_writer() << "balance: " << m_currency.formatAmount(m_wallet->pendingBalance()) << - ", unlocked balance: " << m_currency.formatAmount(m_wallet->actualBalance()); + success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) << + ", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance()); return true; } //---------------------------------------------------------------------------------------------------- @@ -1001,7 +1010,6 @@ bool simple_wallet::run() void simple_wallet::stop() { m_cmd_binder.stop_handling(); - m_wallet->shutdown(); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::print_address(const std::vector &args/* = std::vector()*/) @@ -1145,8 +1153,8 @@ int main(int argc, char* argv[]) try { walletFileName = ::tryToOpenWalletOrLoadKeysOrThrow(wallet, wallet_file, wallet_password); - LOG_PRINT_L1("balance: " << currency.formatAmount(wallet->pendingBalance()) << - ", unlocked balance: " << currency.formatAmount(wallet->actualBalance())); + LOG_PRINT_L1("available balance: " << currency.formatAmount(wallet->actualBalance()) << + ", locked amount: " << currency.formatAmount(wallet->pendingBalance())); LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0); } catch (const std::exception& e) diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index b864cb842b..dd7522f5f0 100755 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -223,7 +223,7 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { void Wallet::initSync() { AccountSubscription sub; sub.keys = reinterpret_cast(m_account.get_keys()); - sub.transactionSpendableAge = 10; + sub.transactionSpendableAge = 1; sub.syncStart.height = 0; sub.syncStart.timestamp = m_account.get_createtime() - (60 * 60 * 24); diff --git a/tests/unit_tests/TestBlockchainGenerator.cpp b/tests/unit_tests/TestBlockchainGenerator.cpp index 43ccda0564..d62314d21d 100644 --- a/tests/unit_tests/TestBlockchainGenerator.cpp +++ b/tests/unit_tests/TestBlockchainGenerator.cpp @@ -127,6 +127,7 @@ bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::Account cryptonote::Transaction tx; creator.generate(address, tx); + tx.unlockTime = 10; //default unlock time for coinbase transactions addToBlockchain(tx); diff --git a/tests/unit_tests/test_tx_pool_detach.cpp b/tests/unit_tests/test_tx_pool_detach.cpp index f8fe3efb5c..55f3f60637 100755 --- a/tests/unit_tests/test_tx_pool_detach.cpp +++ b/tests/unit_tests/test_tx_pool_detach.cpp @@ -413,8 +413,8 @@ TEST_F(DetachTest, testDetachWithWallet) { ASSERT_EQ(txInfo.blockHeight, m_node.getLastLocalBlockHeight()); ASSERT_EQ(txInfo.totalAmount, tr.amount); - ASSERT_EQ(Bob.pendingBalance(), tr.amount); - ASSERT_EQ(Bob.actualBalance(), 0); + ASSERT_EQ(Bob.pendingBalance(), 0); + ASSERT_EQ(Bob.actualBalance(), tr.amount); m_node.startAlternativeChain(m_node.getLastLocalBlockHeight() - 1); generator.generateEmptyBlocks(2); diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index eed3e7b997..9ad57005fa 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -1016,8 +1016,8 @@ TEST_F(WalletApi, checkPendingBalance) { bobNode->updateObservers(); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); - ASSERT_EQ(0, bob->actualBalance()); - ASSERT_EQ(sendAmount, bob->pendingBalance()); + ASSERT_EQ(sendAmount, bob->actualBalance()); + ASSERT_EQ(0, bob->pendingBalance()); alice->shutdown(); bob->shutdown(); From 89271f54f3c687fde855c779cd21292745228568 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Tue, 14 Apr 2015 19:00:44 +0100 Subject: [PATCH 23/59] Simplewallet improvements --- src/simplewallet/simplewallet.cpp | 148 ++++++++++++++++-------------- src/wallet/WalletHelper.cpp | 16 ++-- 2 files changed, 87 insertions(+), 77 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 34f703bf72..106c9023f8 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -272,6 +272,17 @@ struct TransferCommand { } }; +std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, const std::string& password) { + WalletHelper::InitWalletResultObserver initObserver; + std::future f_initError = initObserver.initResult.get_future(); + + wallet.addObserver(&initObserver); + wallet.initAndLoad(walletFile, password); + auto initError = f_initError.get(); + wallet.removeObserver(&initObserver); + + return initError; +} std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { std::string keys_file, walletFileName; @@ -280,20 +291,25 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c boost::system::error_code ignore; bool keysExists = boost::filesystem::exists(keys_file, ignore); bool walletExists = boost::filesystem::exists(walletFileName, ignore); + if (!walletExists && !keysExists && boost::filesystem::exists(walletFile, ignore)) { + auto replaceEc = tools::replace_file(walletFile, walletFileName); + if (replaceEc) { + throw std::runtime_error("failed to rename file '" + walletFile + "' to '" + walletFileName + "'"); + } + + walletExists = true; + } if (walletExists) { LOG_PRINT_L0("Loading wallet..."); std::ifstream walletFile; walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::in); - if (walletFile.fail()) - throw std::runtime_error("error opening walletfile"); + if (walletFile.fail()) { + throw std::runtime_error("error opening wallet file '" + walletFileName + "'"); + } + + auto initError = initAndLoadWallet(*wallet, walletFile, password); - WalletHelper::InitWalletResultObserver initObserver; - std::future f_initError = initObserver.initResult.get_future(); - wallet->addObserver(&initObserver); - wallet->initAndLoad(walletFile, password); - auto initError = f_initError.get(); - wallet->removeObserver(&initObserver); walletFile.close(); if (initError) { //bad password, or legacy format if (keysExists) { @@ -302,11 +318,7 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c boost::filesystem::rename(keys_file, keys_file + ".back"); boost::filesystem::rename(walletFileName, walletFileName + ".back"); - f_initError = initObserver.initResult.get_future(); - wallet->addObserver(&initObserver); - wallet->initAndLoad(ss, password); - auto initError = f_initError.get(); - wallet->removeObserver(&initObserver); + initError = initAndLoadWallet(*wallet, ss, password); if (initError) { throw std::runtime_error("failed to load wallet: " + initError.message()); } @@ -314,8 +326,9 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c LOG_PRINT_L0("Storing wallet..."); std::ofstream walletFile; walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - throw std::runtime_error("error saving walletfile"); + if (walletFile.fail()) { + throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); + } WalletHelper::SaveWalletResultObserver saveObserver; std::future f_saveError = saveObserver.saveResult.get_future(); wallet->addObserver(&saveObserver); @@ -324,54 +337,53 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c wallet->removeObserver(&saveObserver); if (saveError) { fail_msg_writer() << "Failed to store wallet: " << saveError.message(); - throw std::runtime_error("error saving walletfile"); + throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); } LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); return walletFileName; } else { // no keys, wallet error loading - throw std::runtime_error("can't load walletfile, check password"); + throw std::runtime_error("can't load wallet file '" + walletFileName + "', check password"); } } else { //new wallet ok return walletFileName; } - } else { - if (keysExists) { //wallet not exists but keys presented - std::stringstream ss; - cryptonote::importLegacyKeys(keys_file, password, ss); - boost::filesystem::rename(keys_file, keys_file + ".back"); - - WalletHelper::InitWalletResultObserver initObserver; - std::future f_initError = initObserver.initResult.get_future(); - wallet->addObserver(&initObserver); - wallet->initAndLoad(ss, password); - auto initError = f_initError.get(); - wallet->removeObserver(&initObserver); - if (initError) { - throw std::runtime_error("failed to load wallet: " + initError.message()); - } + } else if (keysExists) { //wallet not exists but keys presented + std::stringstream ss; + cryptonote::importLegacyKeys(keys_file, password, ss); + boost::filesystem::rename(keys_file, keys_file + ".back"); - LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - throw std::runtime_error("error saving walletfile"); - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile, false, false); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); - throw std::runtime_error("error saving walletfile"); - } + WalletHelper::InitWalletResultObserver initObserver; + std::future f_initError = initObserver.initResult.get_future(); + wallet->addObserver(&initObserver); + wallet->initAndLoad(ss, password); + auto initError = f_initError.get(); + wallet->removeObserver(&initObserver); + if (initError) { + throw std::runtime_error("failed to load wallet: " + initError.message()); + } - LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); - return walletFileName; - } else { //no wallet no keys - throw std::runtime_error("walletfile not found"); + LOG_PRINT_L0("Storing wallet..."); + std::ofstream walletFile; + walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (walletFile.fail()) { + throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); } + WalletHelper::SaveWalletResultObserver saveObserver; + std::future f_saveError = saveObserver.saveResult.get_future(); + wallet->addObserver(&saveObserver); + wallet->save(walletFile, false, false); + auto saveError = f_saveError.get(); + wallet->removeObserver(&saveObserver); + if (saveError) { + fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); + } + + LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); + return walletFileName; + } else { //no wallet no keys + throw std::runtime_error("wallet file '" + walletFileName + "' is not found"); } } @@ -448,8 +460,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { handle_command_line(vm); - if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port) - { + if (!m_daemon_address.empty() && (!m_daemon_host.empty() || 0 != m_daemon_port)) { fail_msg_writer() << "you can't specify daemon host or port several times"; return false; } @@ -462,7 +473,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) std::getline(std::cin, answer); c = answer[0]; if (!(c == 'O' || c == 'G' || c == 'E' || c == 'o' || c == 'g' || c == 'e')) { - std::cout << "Unknown command: " << c<getAddress(); - success_msg_writer() << "**********************************************************************\n" << "Use \"help\" command to see the list of available commands.\n" << diff --git a/src/wallet/WalletHelper.cpp b/src/wallet/WalletHelper.cpp index 806982357c..6e5c286eaa 100755 --- a/src/wallet/WalletHelper.cpp +++ b/src/wallet/WalletHelper.cpp @@ -9,12 +9,14 @@ using namespace epee; void WalletHelper::prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { - keys_file = file_path; - wallet_file = file_path; - boost::system::error_code e; - if (string_tools::get_extension(keys_file) == "keys") {//provided keys file name - wallet_file = string_tools::cut_off_extension(wallet_file); - } else {//provided wallet file name - keys_file += ".keys"; + if (string_tools::get_extension(file_path) == "wallet") { + keys_file = string_tools::cut_off_extension(file_path) + ".keys"; + wallet_file = file_path; + } else if (string_tools::get_extension(file_path) == "keys") { + keys_file = file_path; + wallet_file = string_tools::cut_off_extension(file_path) + ".wallet"; + } else { + keys_file = file_path + ".keys"; + wallet_file = file_path + ".wallet"; } } From ad291f528542c71d9bbac603bea02404edaf0f18 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Wed, 27 May 2015 13:08:46 +0100 Subject: [PATCH 24/59] Bytecoin RPC Wallet --- .gitignore | 1 + CMakeLists.txt | 38 +- README | 4 +- ReleaseNotes.txt | 7 + contrib/epee/include/console_handler.h | 1 + .../epee/include/net/abstract_tcp_server2.inl | 1 + external/CMakeLists.txt | 5 +- external/gtest/CHANGES | 157 + {tests => external}/gtest/CMakeLists.txt | 42 +- external/gtest/CONTRIBUTORS | 37 + external/gtest/LICENSE | 28 + external/gtest/Makefile.am | 306 + external/gtest/Makefile.in | 1360 ++ external/gtest/README | 435 + external/gtest/aclocal.m4 | 1198 + external/gtest/build-aux/config.guess | 1530 ++ external/gtest/build-aux/config.h.in | 69 + external/gtest/build-aux/config.sub | 1773 ++ external/gtest/build-aux/depcomp | 688 + external/gtest/build-aux/install-sh | 527 + external/gtest/build-aux/ltmain.sh | 9661 ++++++++ external/gtest/build-aux/missing | 331 + .../gtest/cmake/internal_utils.cmake | 13 +- external/gtest/codegear/gtest.cbproj | 138 + external/gtest/codegear/gtest.groupproj | 54 + external/gtest/codegear/gtest_all.cc | 38 + external/gtest/codegear/gtest_link.cc | 40 + external/gtest/codegear/gtest_main.cbproj | 82 + external/gtest/codegear/gtest_unittest.cbproj | 88 + external/gtest/configure | 18222 ++++++++++++++ external/gtest/configure.ac | 68 + external/gtest/fused-src/gtest/gtest-all.cc | 9592 ++++++++ external/gtest/fused-src/gtest/gtest.h | 20061 ++++++++++++++++ .../gtest/fused-src/gtest}/gtest_main.cc | 5 +- .../gtest/include/gtest/gtest-death-test.h | 17 +- .../gtest/include/gtest/gtest-message.h | 74 +- .../gtest/include/gtest/gtest-param-test.h | 2 +- .../include/gtest/gtest-param-test.h.pump | 2 +- .../gtest/include/gtest/gtest-printers.h | 91 +- .../gtest/include/gtest/gtest-spi.h | 2 +- .../gtest/include/gtest/gtest-test-part.h | 17 +- .../gtest/include/gtest/gtest-typed-test.h | 0 .../gtest/include/gtest/gtest.h | 311 +- .../gtest/include/gtest/gtest_pred_impl.h | 12 +- .../gtest/include/gtest/gtest_prod.h | 0 .../internal/gtest-death-test-internal.h | 21 +- .../include/gtest/internal/gtest-filepath.h | 14 +- .../include/gtest/internal/gtest-internal.h | 182 +- .../include/gtest/internal/gtest-linked_ptr.h | 8 +- .../internal/gtest-param-util-generated.h | 593 +- .../gtest-param-util-generated.h.pump | 2 +- .../include/gtest/internal/gtest-param-util.h | 8 +- .../gtest/include/gtest/internal/gtest-port.h | 274 +- .../include/gtest/internal/gtest-string.h | 167 + .../include/gtest/internal/gtest-tuple.h | 92 +- .../include/gtest/internal/gtest-tuple.h.pump | 13 +- .../include/gtest/internal/gtest-type-util.h | 21 +- .../gtest/internal/gtest-type-util.h.pump | 21 +- external/gtest/m4/acx_pthread.m4 | 363 + external/gtest/m4/gtest.m4 | 74 + external/gtest/m4/libtool.m4 | 8001 ++++++ external/gtest/m4/ltoptions.m4 | 384 + external/gtest/m4/ltsugar.m4 | 123 + external/gtest/m4/ltversion.m4 | 23 + external/gtest/m4/lt~obsolete.m4 | 98 + external/gtest/make/Makefile | 82 + external/gtest/msvc/gtest-md.sln | 45 + external/gtest/msvc/gtest-md.vcproj | 126 + external/gtest/msvc/gtest.sln | 45 + external/gtest/msvc/gtest.vcproj | 126 + external/gtest/msvc/gtest_main-md.vcproj | 129 + external/gtest/msvc/gtest_main.vcproj | 129 + external/gtest/msvc/gtest_prod_test-md.vcproj | 164 + external/gtest/msvc/gtest_prod_test.vcproj | 164 + external/gtest/msvc/gtest_unittest-md.vcproj | 147 + external/gtest/msvc/gtest_unittest.vcproj | 147 + external/gtest/samples/prime_tables.h | 123 + external/gtest/samples/sample1.cc | 68 + external/gtest/samples/sample1.h | 43 + external/gtest/samples/sample10_unittest.cc | 144 + external/gtest/samples/sample1_unittest.cc | 153 + external/gtest/samples/sample2.cc | 56 + external/gtest/samples/sample2.h | 85 + external/gtest/samples/sample2_unittest.cc | 109 + external/gtest/samples/sample3-inl.h | 172 + external/gtest/samples/sample3_unittest.cc | 151 + external/gtest/samples/sample4.cc | 46 + external/gtest/samples/sample4.h | 53 + external/gtest/samples/sample4_unittest.cc | 45 + external/gtest/samples/sample5_unittest.cc | 199 + external/gtest/samples/sample6_unittest.cc | 224 + external/gtest/samples/sample7_unittest.cc | 130 + external/gtest/samples/sample8_unittest.cc | 173 + external/gtest/samples/sample9_unittest.cc | 160 + external/gtest/scripts/fuse_gtest_files.py | 250 + external/gtest/scripts/gen_gtest_pred_impl.py | 730 + external/gtest/scripts/gtest-config.in | 274 + external/gtest/scripts/pump.py | 855 + external/gtest/scripts/test/Makefile | 59 + {tests => external}/gtest/src/gtest-all.cc | 0 .../gtest/src/gtest-death-test.cc | 280 +- .../gtest/src/gtest-filepath.cc | 30 +- .../gtest/src/gtest-internal-inl.h | 242 +- {tests => external}/gtest/src/gtest-port.cc | 119 +- .../gtest/src/gtest-printers.cc | 101 +- .../gtest/src/gtest-test-part.cc | 6 +- .../gtest/src/gtest-typed-test.cc | 6 +- {tests => external}/gtest/src/gtest.cc | 1250 +- external/gtest/src/gtest_main.cc | 38 + .../gtest/test/gtest-death-test_ex_test.cc | 93 + external/gtest/test/gtest-death-test_test.cc | 1367 ++ external/gtest/test/gtest-filepath_test.cc | 680 + external/gtest/test/gtest-linked_ptr_test.cc | 154 + external/gtest/test/gtest-listener_test.cc | 310 + external/gtest/test/gtest-message_test.cc | 159 + external/gtest/test/gtest-options_test.cc | 215 + external/gtest/test/gtest-param-test2_test.cc | 65 + external/gtest/test/gtest-param-test_test.cc | 904 + external/gtest/test/gtest-param-test_test.h | 57 + external/gtest/test/gtest-port_test.cc | 1253 + external/gtest/test/gtest-printers_test.cc | 1566 ++ external/gtest/test/gtest-test-part_test.cc | 208 + external/gtest/test/gtest-tuple_test.cc | 320 + external/gtest/test/gtest-typed-test2_test.cc | 45 + external/gtest/test/gtest-typed-test_test.cc | 360 + external/gtest/test/gtest-typed-test_test.h | 66 + .../gtest/test/gtest-unittest-api_test.cc | 341 + external/gtest/test/gtest_all_test.cc | 47 + .../test/gtest_break_on_failure_unittest.py | 212 + .../test/gtest_break_on_failure_unittest_.cc | 88 + .../gtest/test/gtest_catch_exceptions_test.py | 237 + .../test/gtest_catch_exceptions_test_.cc | 311 + external/gtest/test/gtest_color_test.py | 130 + external/gtest/test/gtest_color_test_.cc | 71 + external/gtest/test/gtest_env_var_test.py | 103 + external/gtest/test/gtest_env_var_test_.cc | 126 + external/gtest/test/gtest_environment_test.cc | 192 + external/gtest/test/gtest_filter_unittest.py | 633 + external/gtest/test/gtest_filter_unittest_.cc | 140 + external/gtest/test/gtest_help_test.py | 172 + external/gtest/test/gtest_help_test_.cc | 46 + .../gtest/test/gtest_list_tests_unittest.py | 207 + .../gtest/test/gtest_list_tests_unittest_.cc | 157 + external/gtest/test/gtest_main_unittest.cc | 45 + external/gtest/test/gtest_no_test_unittest.cc | 56 + external/gtest/test/gtest_output_test.py | 335 + external/gtest/test/gtest_output_test_.cc | 1034 + .../test/gtest_output_test_golden_lin.txt | 720 + .../gtest/test/gtest_pred_impl_unittest.cc | 2427 ++ .../gtest/test/gtest_premature_exit_test.cc | 141 + external/gtest/test/gtest_prod_test.cc | 57 + external/gtest/test/gtest_repeat_test.cc | 253 + external/gtest/test/gtest_shuffle_test.py | 325 + external/gtest/test/gtest_shuffle_test_.cc | 103 + external/gtest/test/gtest_sole_header_test.cc | 57 + external/gtest/test/gtest_stress_test.cc | 256 + external/gtest/test/gtest_test_utils.py | 320 + .../test/gtest_throw_on_failure_ex_test.cc | 92 + .../gtest/test/gtest_throw_on_failure_test.py | 171 + .../test/gtest_throw_on_failure_test_.cc | 72 + .../gtest/test/gtest_uninitialized_test.py | 70 + .../gtest/test/gtest_uninitialized_test_.cc | 43 + external/gtest/test/gtest_unittest.cc | 7415 ++++++ .../gtest/test/gtest_xml_outfile1_test_.cc | 49 + .../gtest/test/gtest_xml_outfile2_test_.cc | 49 + .../gtest/test/gtest_xml_outfiles_test.py | 132 + .../gtest/test/gtest_xml_output_unittest.py | 307 + .../gtest/test/gtest_xml_output_unittest_.cc | 181 + external/gtest/test/gtest_xml_test_utils.py | 194 + external/gtest/test/production.cc | 36 + external/gtest/test/production.h | 55 + .../gtest/xcode/Config/DebugProject.xcconfig | 30 + .../xcode/Config/FrameworkTarget.xcconfig | 17 + external/gtest/xcode/Config/General.xcconfig | 41 + .../xcode/Config/ReleaseProject.xcconfig | 32 + .../xcode/Config/StaticLibraryTarget.xcconfig | 18 + .../gtest/xcode/Config/TestTarget.xcconfig | 8 + external/gtest/xcode/Resources/Info.plist | 30 + .../xcode/Samples/FrameworkSample/Info.plist | 28 + .../WidgetFramework.xcodeproj/project.pbxproj | 457 + .../xcode/Samples/FrameworkSample/runtests.sh | 62 + .../xcode/Samples/FrameworkSample/widget.cc | 63 + .../xcode/Samples/FrameworkSample/widget.h | 59 + .../Samples/FrameworkSample/widget_test.cc | 68 + external/gtest/xcode/Scripts/runtests.sh | 65 + .../gtest/xcode/Scripts/versiongenerate.py | 100 + .../xcode/gtest.xcodeproj/project.pbxproj | 1135 + include/IMultiWallet.h | 101 + include/INode.h | 16 +- include/IObservable.h | 2 +- include/IStreamSerializable.h | 2 +- include/ITransaction.h | 17 +- include/ITransfersContainer.h | 2 +- include/ITransfersSynchronizer.h | 2 +- include/IWallet.h | 2 +- src/CMakeLists.txt | 117 +- src/Common/ArrayRef.h | 441 + src/Common/ArrayView.h | 422 + src/{common => Common}/BlockingQueue.cpp | 2 +- src/{common => Common}/BlockingQueue.h | 2 +- src/Common/ConsoleHandler.cpp | 245 + src/Common/ConsoleHandler.h | 86 + src/Common/ConsoleTools.cpp | 88 + src/Common/ConsoleTools.h | 46 + .../IInputStream.cpp} | 4 +- src/Common/IInputStream.h | 30 + .../IOutputStream.cpp} | 4 +- src/Common/IOutputStream.h | 30 + src/Common/JsonValue.cpp | 945 + src/Common/JsonValue.h | 156 + src/{common => Common}/ObserverManager.h | 2 +- src/Common/PathTools.cpp | 111 + src/Common/PathTools.h | 36 + src/{common => Common}/ShuffleGenerator.h | 3 +- src/{common => Common}/SignalHandler.cpp | 71 +- src/Common/SignalHandler.h | 29 + src/Common/StreamTools.cpp | 250 + src/Common/StreamTools.h | 76 + src/Common/StringBuffer.h | 554 + src/Common/StringInputStream.cpp | 36 + src/Common/StringInputStream.h | 35 + src/Common/StringOutputStream.cpp | 30 + .../StringOutputStream.h} | 23 +- src/Common/StringTools.cpp | 348 + src/Common/StringTools.h | 118 + src/Common/StringView.cpp | 347 + src/Common/StringView.h | 204 + src/{common => Common}/base58.cpp | 2 +- src/{common => Common}/base58.h | 2 +- src/Common/boost_serialization_helper.h | 106 + src/{common => Common}/command_line.cpp | 2 +- src/{common => Common}/command_line.h | 15 +- src/{common => Common}/int-util.h | 2 +- src/{common => Common}/pod-class.h | 2 +- .../static_assert.h} | 49 +- ...unordered_containers_boost_serialization.h | 9 +- src/{common => Common}/util.cpp | 39 +- src/{common => Common}/util.h | 10 +- src/{common => Common}/varint.h | 6 +- src/CryptoNote/BaseTransaction.cpp | 79 + src/CryptoNote/BaseTransaction.h | 62 + src/CryptoNote/Block.cpp | 118 + src/CryptoNote/Block.h | 78 + src/CryptoNote/KeyInput.cpp | 45 + src/CryptoNote/KeyInput.h | 46 + src/CryptoNote/KeyOutput.cpp | 33 + .../System/Event.h => CryptoNote/KeyOutput.h} | 30 +- src/CryptoNote/MultisignatureInput.cpp | 41 + src/CryptoNote/MultisignatureInput.h | 40 + src/CryptoNote/MultisignatureOutput.cpp | 43 + src/CryptoNote/MultisignatureOutput.h | 40 + src/CryptoNote/Transaction.cpp | 111 + src/CryptoNote/Transaction.h | 89 + src/CryptoNote/UnsignedKeyInput.cpp | 41 + src/CryptoNote/UnsignedKeyInput.h | 40 + .../UnsignedMultisignatureInput.cpp | 33 + src/CryptoNote/UnsignedMultisignatureInput.h | 37 + src/CryptoNote/UnsignedTransaction.cpp | 111 + src/CryptoNote/UnsignedTransaction.h | 89 + src/HTTP/HttpParser.cpp | 56 +- src/HTTP/HttpParser.h | 6 +- src/HTTP/HttpParserErrorCodes.cpp | 26 + src/HTTP/HttpParserErrorCodes.h | 83 + src/HTTP/HttpRequest.cpp | 6 +- src/HTTP/HttpRequest.h | 6 +- src/HTTP/HttpResponse.cpp | 32 +- src/HTTP/HttpResponse.h | 6 +- .../InProcessNode.cpp | 174 +- .../InProcessNode.h | 47 +- .../InProcessNodeErrors.cpp | 7 +- .../InProcessNodeErrors.h | 11 +- src/Logging/CommonLogger.cpp | 99 + src/Logging/CommonLogger.h | 44 + src/Logging/ConsoleLogger.cpp | 79 + src/{p2p/stdafx.h => Logging/ConsoleLogger.h} | 27 +- src/Logging/FileLogger.cpp | 30 + src/Logging/FileLogger.h | 34 + src/Logging/ILogger.cpp | 49 + src/Logging/ILogger.h | 62 + .../LoggerGroup.cpp} | 41 +- src/Logging/LoggerGroup.h | 37 + src/Logging/LoggerManager.cpp | 127 + src/Logging/LoggerManager.h | 39 + src/{logger => Logging}/LoggerMessage.cpp | 207 +- src/Logging/LoggerMessage.h | 45 + src/Logging/LoggerRef.cpp | 33 + src/Logging/LoggerRef.h | 36 + src/Logging/StreamLogger.cpp | 50 + src/Logging/StreamLogger.h | 41 + src/Platform/Linux/System/Dispatcher.cpp | 261 +- src/Platform/Linux/System/Dispatcher.h | 54 +- src/Platform/Linux/System/Ipv4Resolver.cpp | 98 + src/Platform/Linux/System/Ipv4Resolver.h | 45 + src/Platform/Linux/System/TcpConnection.cpp | 278 +- src/Platform/Linux/System/TcpConnection.h | 18 +- src/Platform/Linux/System/TcpConnector.cpp | 220 +- src/Platform/Linux/System/TcpConnector.h | 12 +- src/Platform/Linux/System/TcpListener.cpp | 200 +- src/Platform/Linux/System/TcpListener.h | 8 +- src/Platform/Linux/System/Timer.cpp | 173 +- src/Platform/Linux/System/Timer.h | 8 +- src/Platform/OSX/System/Dispatcher.cpp | 257 +- src/Platform/OSX/System/Dispatcher.h | 52 +- src/Platform/OSX/System/Event.cpp | 106 - src/Platform/OSX/System/Ipv4Resolver.cpp | 98 + src/Platform/OSX/System/Ipv4Resolver.h | 45 + src/Platform/OSX/System/TcpConnection.cpp | 221 +- src/Platform/OSX/System/TcpConnection.h | 11 +- src/Platform/OSX/System/TcpConnector.cpp | 197 +- src/Platform/OSX/System/TcpConnector.h | 11 +- src/Platform/OSX/System/TcpListener.cpp | 200 +- src/Platform/OSX/System/TcpListener.h | 5 +- src/Platform/OSX/System/Timer.cpp | 117 +- src/Platform/OSX/System/Timer.h | 2 +- src/Platform/OSX/System/asm.s | 47 + src/Platform/OSX/System/context.c | 42 + src/Platform/OSX/System/context.h | 102 + src/Platform/Windows/System/Dispatcher.cpp | 283 +- src/Platform/Windows/System/Dispatcher.h | 35 +- src/Platform/Windows/System/Event.cpp | 107 - src/Platform/Windows/System/Ipv4Resolver.cpp | 98 + src/Platform/Windows/System/Ipv4Resolver.h | 45 + src/Platform/Windows/System/TcpConnection.cpp | 202 +- src/Platform/Windows/System/TcpConnection.h | 15 +- src/Platform/Windows/System/TcpConnector.cpp | 182 +- src/Platform/Windows/System/TcpConnector.h | 9 +- src/Platform/Windows/System/TcpListener.cpp | 130 +- src/Platform/Windows/System/TcpListener.h | 5 +- src/Platform/Windows/System/Timer.cpp | 99 +- src/Platform/Windows/System/Timer.h | 5 +- src/{platform => Platform}/mingw/alloca.h | 2 +- src/{platform => Platform}/msc/alloca.h | 4 +- src/{platform => Platform}/msc/stdbool.h | 2 +- src/{platform => Platform}/msc/sys/param.h | 2 +- src/{Platform/Linux => }/System/Event.cpp | 61 +- src/{Platform/OSX => }/System/Event.h | 4 +- src/System/EventLock.cpp | 35 + .../EventLock.h} | 14 +- .../OSX => }/System/InterruptedException.cpp | 2 +- .../System/InterruptedException.h | 2 +- src/System/Ipv4Address.cpp | 125 + src/System/Ipv4Address.h | 40 + src/System/Latch.cpp | 119 + .../Linux/System/Event.h => System/Latch.h} | 28 +- src/System/LatchGuard.cpp | 32 + src/System/LatchGuard.h | 33 + src/System/TcpStream.cpp | 82 +- src/System/TcpStream.h | 29 +- src/common/SignalHandler.h | 59 - src/common/boost_serialization_helper.h | 106 - src/common/static_assert.h | 9 - src/connectivity_tool/conn_tool.cpp | 451 +- src/crypto/blake256.c | 2 +- src/crypto/chacha8.c | 5 +- src/crypto/chacha8.h | 17 - src/crypto/crypto-ops.c | 429 +- src/crypto/crypto-ops.h | 17 - src/crypto/crypto.cpp | 27 +- src/crypto/crypto.h | 19 +- src/crypto/generic-ops.h | 17 - src/crypto/hash-ops.h | 27 +- src/crypto/hash.c | 2 +- src/crypto/hash.h | 19 +- src/crypto/initializer.h | 17 - src/crypto/oaes_lib.c | 8 +- src/crypto/random.h | 17 - src/crypto/skein_port.h | 2 +- src/crypto/slow-hash.c | 2 +- src/crypto/slow-hash.cpp | 17 - src/crypto/slow-hash.inl | 6 +- src/cryptonote_config.h | 12 +- src/cryptonote_core/AccountKVSerialization.h | 4 +- src/cryptonote_core/BlockIndex.cpp | 2 +- src/cryptonote_core/BlockIndex.h | 3 +- src/cryptonote_core/CoreConfig.cpp | 11 +- src/cryptonote_core/CoreConfig.h | 6 +- src/cryptonote_core/Currency.cpp | 685 +- src/cryptonote_core/Currency.h | 320 +- .../IBlockchainStorageObserver.h | 6 +- src/cryptonote_core/ICore.h | 24 +- src/cryptonote_core/ICoreObserver.h | 6 +- src/cryptonote_core/ITimeProvider.cpp | 2 +- src/cryptonote_core/ITimeProvider.h | 2 +- src/cryptonote_core/ITransactionValidator.h | 12 +- src/cryptonote_core/ITxPoolObserver.h | 6 +- src/cryptonote_core/MinerConfig.cpp | 7 +- src/cryptonote_core/MinerConfig.h | 4 +- src/cryptonote_core/OnceInInterval.h | 49 + src/cryptonote_core/SwappedMap.cpp | 2 +- src/cryptonote_core/SwappedMap.h | 2 +- src/cryptonote_core/SwappedVector.cpp | 2 +- src/cryptonote_core/SwappedVector.h | 2 +- src/cryptonote_core/Transaction.cpp | 179 +- src/cryptonote_core/TransactionApi.h | 6 +- src/cryptonote_core/TransactionExtra.h | 20 +- src/cryptonote_core/UpgradeDetector.cpp | 2 +- src/cryptonote_core/UpgradeDetector.h | 50 +- src/cryptonote_core/account.cpp | 13 +- src/cryptonote_core/account.h | 4 +- .../account_boost_serialization.h | 8 +- src/cryptonote_core/blockchain_storage.cpp | 1179 +- src/cryptonote_core/blockchain_storage.h | 55 +- .../blockchain_storage_boost_serialization.h | 6 +- src/cryptonote_core/checkpoints.cpp | 119 +- src/cryptonote_core/checkpoints.h | 10 +- src/cryptonote_core/connection_context.h | 69 - src/cryptonote_core/cryptonote_basic.cpp | 321 + src/cryptonote_core/cryptonote_basic.h | 104 +- src/cryptonote_core/cryptonote_basic_impl.cpp | 56 +- src/cryptonote_core/cryptonote_basic_impl.h | 16 +- .../cryptonote_boost_serialization.h | 30 +- src/cryptonote_core/cryptonote_core.cpp | 963 +- src/cryptonote_core/cryptonote_core.h | 38 +- .../cryptonote_format_utils.cpp | 1266 +- src/cryptonote_core/cryptonote_format_utils.h | 370 +- .../cryptonote_serialization.cpp | 105 +- .../cryptonote_serialization.h | 18 +- src/cryptonote_core/cryptonote_stat_info.h | 4 +- src/cryptonote_core/difficulty.cpp | 6 +- src/cryptonote_core/difficulty.h | 4 +- src/cryptonote_core/i_miner_handler.h | 6 +- src/cryptonote_core/miner.cpp | 216 +- src/cryptonote_core/miner.h | 39 +- src/cryptonote_core/tx_extra.h | 17 +- src/cryptonote_core/tx_pool.cpp | 116 +- src/cryptonote_core/tx_pool.h | 39 +- src/cryptonote_core/verification_context.h | 4 +- .../ICryptonoteProtocolObserver.h | 6 +- .../ICryptonoteProtocolQuery.h | 6 +- src/cryptonote_protocol/blobdatatype.h | 20 +- .../cryptonote_protocol_defs.h | 20 +- .../cryptonote_protocol_handler.cpp | 630 + .../cryptonote_protocol_handler.h | 94 +- .../cryptonote_protocol_handler.inl | 613 - .../cryptonote_protocol_handler_common.h | 19 +- src/daemon/DaemonCommandsHandler.h | 72 + src/daemon/DeamonCommandsHandler.cpp | 320 + src/daemon/daemon.cpp | 381 +- src/daemon/daemon_commands_handler.h | 354 - src/logger/CommonLogger.cpp | 35 - src/logger/CommonLogger.h | 23 - src/logger/ConsoleLogger.cpp | 162 - src/logger/ConsoleLogger.h | 19 - src/logger/ILogger.cpp | 30 - src/logger/ILogger.h | 48 - src/logger/LoggerGroup.cpp | 29 - src/logger/LoggerGroup.h | 20 - src/logger/LoggerMessage.h | 28 - src/logger/LoggerRef.cpp | 20 - src/logger/LoggerRef.h | 21 - src/logger/StreamLogger.cpp | 28 - src/logger/StreamLogger.h | 20 - src/miner/simpleminer.cpp | 234 - src/miner/simpleminer.h | 46 - src/miner/simpleminer_protocol_defs.h | 120 - src/node_rpc_proxy/InitState.h | 10 +- src/node_rpc_proxy/NodeErrors.cpp | 4 +- src/node_rpc_proxy/NodeErrors.h | 8 +- src/node_rpc_proxy/NodeRpcProxy.cpp | 194 +- src/node_rpc_proxy/NodeRpcProxy.h | 29 +- src/p2p/LevinProtocol.cpp | 150 + src/p2p/LevinProtocol.h | 97 + src/p2p/NetNodeConfig.cpp | 28 +- src/p2p/NetNodeConfig.h | 6 +- src/p2p/PeerListManager.cpp | 222 + src/p2p/PeerListManager.h | 94 + src/p2p/connection_context.h | 79 + src/p2p/net_node.cpp | 1335 + src/p2p/net_node.h | 204 +- src/p2p/net_node.inl | 1091 - src/p2p/net_node_common.h | 62 +- src/p2p/net_peerlist.h | 372 - src/p2p/net_peerlist_boost_serialization.h | 6 +- src/p2p/p2p_networks.h | 4 +- src/p2p/p2p_protocol_defs.h | 111 +- src/p2p/p2p_protocol_types.h | 75 + src/payment_service/ConfigurationManager.cpp | 128 + src/payment_service/ConfigurationManager.h | 56 + src/payment_service/JsonRpcMessages.cpp | 248 + src/payment_service/JsonRpcMessages.h | 188 + src/payment_service/JsonRpcServer.cpp | 451 + src/payment_service/JsonRpcServer.h | 89 + src/payment_service/NodeFactory.cpp | 117 + src/payment_service/NodeFactory.h | 56 + .../PaymentServiceConfiguration.cpp | 145 + .../PaymentServiceConfiguration.h | 74 + src/payment_service/RpcNodeConfiguration.cpp | 62 + src/payment_service/RpcNodeConfiguration.h | 53 + src/payment_service/WalletFactory.cpp | 59 + src/payment_service/WalletFactory.h | 59 + src/payment_service/WalletObservers.cpp | 90 + src/payment_service/WalletObservers.h | 89 + src/payment_service/WalletService.cpp | 583 + src/payment_service/WalletService.h | 124 + .../WalletServiceErrorCodes.cpp | 43 + src/payment_service/WalletServiceErrorCodes.h | 77 + src/payment_service/main.cpp | 546 + src/rpc/HttpClient.cpp | 61 + src/rpc/HttpClient.h | 85 + src/rpc/HttpServer.cpp | 107 + src/rpc/HttpServer.h | 61 + src/rpc/JsonRpc.cpp | 64 + src/rpc/JsonRpc.h | 223 + src/rpc/RpcServer.cpp | 606 + src/rpc/RpcServer.h | 78 + src/rpc/RpcServerConfig.cpp | 51 + src/rpc/RpcServerConfig.h | 38 + src/rpc/core_rpc_server.cpp | 604 - src/rpc/core_rpc_server.h | 104 - src/rpc/core_rpc_server_commands_defs.h | 6 +- src/rpc/core_rpc_server_error_codes.h | 2 +- .../BinaryInputStreamSerializer.cpp | 6 +- .../BinaryInputStreamSerializer.h | 4 +- .../BinaryOutputStreamSerializer.cpp | 4 +- .../BinaryOutputStreamSerializer.h | 4 +- src/serialization/ISerializer.h | 4 +- src/serialization/IStream.h | 4 +- .../JsonInputStreamSerializer.cpp | 6 +- src/serialization/JsonInputStreamSerializer.h | 12 +- .../JsonInputValueSerializer.cpp | 37 +- src/serialization/JsonInputValueSerializer.h | 19 +- .../JsonOutputStreamSerializer.cpp | 39 +- .../JsonOutputStreamSerializer.h | 17 +- .../JsonSerializationDispatcher.h | 65 - src/serialization/JsonValue.cpp | 614 - src/serialization/JsonValue.h | 103 - src/serialization/KVBinaryCommon.h | 2 +- .../KVBinaryInputStreamSerializer.cpp | 25 +- .../KVBinaryInputStreamSerializer.h | 27 +- .../KVBinaryOutputStreamSerializer.cpp | 5 +- .../KVBinaryOutputStreamSerializer.h | 4 +- src/serialization/MemoryStream.cpp | 3 +- src/serialization/MemoryStream.h | 5 +- src/serialization/SerializationOverloads.cpp | 14 +- src/serialization/SerializationOverloads.h | 37 +- src/serialization/binary_archive.h | 13 +- src/serialization/binary_utils.h | 2 +- src/serialization/crypto.h | 9 +- src/serialization/json_archive.h | 6 +- src/serialization/json_utils.h | 2 +- src/serialization/serialization.h | 6 +- src/serialization/string.h | 2 +- src/serialization/variant.h | 2 +- src/serialization/vector.h | 2 +- src/simplewallet/password_container.cpp | 2 +- src/simplewallet/password_container.h | 2 +- src/simplewallet/simplewallet.cpp | 718 +- src/simplewallet/simplewallet.h | 71 +- src/transfers/BlockchainSynchronizer.cpp | 62 +- src/transfers/BlockchainSynchronizer.h | 4 +- src/transfers/CommonTypes.h | 4 +- src/transfers/IBlockchainSynchronizer.h | 4 +- src/transfers/IObservableImpl.h | 4 +- src/transfers/SerializationHelpers.h | 4 +- src/transfers/SynchronizationState.cpp | 12 +- src/transfers/SynchronizationState.h | 6 +- src/transfers/TransfersConsumer.cpp | 171 +- src/transfers/TransfersConsumer.h | 12 +- src/transfers/TransfersContainer.cpp | 73 +- src/transfers/TransfersContainer.h | 14 +- src/transfers/TransfersSubscription.cpp | 162 +- src/transfers/TransfersSubscription.h | 6 +- src/transfers/TransfersSynchronizer.cpp | 10 +- src/transfers/TransfersSynchronizer.h | 9 +- src/transfers/TypeHelpers.h | 2 +- src/version.h.in | 4 +- src/wallet/KeysStorage.cpp | 4 +- src/wallet/KeysStorage.h | 6 +- src/wallet/LegacyKeysImporter.cpp | 45 +- src/wallet/LegacyKeysImporter.h | 6 +- src/wallet/MultiWallet.h | 44 + src/wallet/SyncWallet.cpp | 70 + src/wallet/SyncWallet.h | 49 + src/wallet/Wallet.cpp | 93 +- src/wallet/Wallet.h | 13 +- src/wallet/WalletAsyncContextCounter.cpp | 2 +- src/wallet/WalletAsyncContextCounter.h | 2 +- src/wallet/WalletErrors.cpp | 4 +- src/wallet/WalletErrors.h | 8 +- src/wallet/WalletEvent.h | 4 +- src/wallet/WalletHelper.cpp | 121 +- src/wallet/WalletHelper.h | 46 +- src/wallet/WalletRequest.h | 6 +- src/wallet/WalletSendTransactionContext.h | 8 +- src/wallet/WalletSerialization.cpp | 8 +- src/wallet/WalletSerialization.h | 11 +- src/wallet/WalletSerializer.cpp | 32 +- src/wallet/WalletSerializer.h | 12 +- src/wallet/WalletTransactionSender.cpp | 102 +- src/wallet/WalletTransactionSender.h | 22 +- src/wallet/WalletUnconfirmedTransactions.cpp | 10 +- src/wallet/WalletUnconfirmedTransactions.h | 10 +- src/wallet/WalletUserTransactionsCache.cpp | 16 +- src/wallet/WalletUserTransactionsCache.h | 8 +- src/wallet/WalletUtils.h | 4 +- src/wallet/wallet_errors.h | 632 - src/wallet/wallet_rpc_server.cpp | 228 +- src/wallet/wallet_rpc_server.h | 82 +- src/wallet/wallet_rpc_server_commans_defs.h | 10 +- src/wallet/wallet_rpc_server_error_codes.h | 2 +- tests/CMakeLists.txt | 165 +- tests/System/DispatcherTests.cpp | 405 + tests/System/EventLockTests.cpp | 84 + tests/System/EventTests.cpp | 299 + tests/System/Ipv4AddressTests.cpp | 113 + tests/System/Ipv4ResolverTests.cpp | 64 + tests/System/LatchTests.cpp | 33 + tests/System/TcpConnectionTests.cpp | 260 + tests/System/TcpConnectorTests.cpp | 76 + tests/System/TcpListenerTests.cpp | 84 + tests/System/TimerTests.cpp | 161 + tests/System/main.cpp | 23 + tests/TestGenerator/TestGenerator.cpp | 62 +- tests/TestGenerator/TestGenerator.h | 58 +- tests/core_proxy/core_proxy.cpp | 273 - tests/core_proxy/core_proxy.h | 80 - tests/core_tests/TestGenerator.h | 48 +- tests/core_tests/TransactionBuilder.cpp | 24 +- tests/core_tests/TransactionBuilder.h | 34 +- tests/core_tests/block_reward.cpp | 24 +- tests/core_tests/block_reward.h | 10 +- tests/core_tests/block_validation.cpp | 66 +- tests/core_tests/block_validation.h | 46 +- tests/core_tests/chain_split_1.cpp | 50 +- tests/core_tests/chain_split_1.h | 34 +- tests/core_tests/chain_switch_1.cpp | 12 +- tests/core_tests/chain_switch_1.h | 18 +- tests/core_tests/chaingen.cpp | 49 +- tests/core_tests/chaingen.h | 160 +- tests/core_tests/chaingen001.cpp | 26 +- tests/core_tests/chaingen001.h | 10 +- tests/core_tests/chaingen_main.cpp | 18 +- tests/core_tests/double_spend.cpp | 24 +- tests/core_tests/double_spend.h | 40 +- tests/core_tests/double_spend.inl | 59 +- tests/core_tests/integer_overflow.cpp | 36 +- tests/core_tests/integer_overflow.h | 8 +- tests/core_tests/random_outs.cpp | 16 +- tests/core_tests/random_outs.h | 14 +- tests/core_tests/ring_signature_1.cpp | 30 +- tests/core_tests/ring_signature_1.h | 26 +- tests/core_tests/transaction_tests.cpp | 17 +- tests/core_tests/transaction_tests.h | 2 +- tests/core_tests/tx_validation.cpp | 8 +- tests/core_tests/tx_validation.h | 12 +- tests/core_tests/upgrade.cpp | 37 +- tests/core_tests/upgrade.h | 24 +- tests/crypto/crypto-ops-data.c | 17 + tests/crypto/crypto-ops.c | 17 + tests/crypto/crypto-tests.h | 2 +- tests/crypto/crypto.cpp | 2 +- tests/crypto/hash.c | 17 + tests/crypto/main.cpp | 5 +- tests/crypto/random.c | 17 + tests/daemon_tests/CMakeLists.txt | 5 - tests/daemon_tests/transfers.cpp | 90 - tests/difficulty/difficulty.cpp | 8 +- .../include/gtest/internal/gtest-string.h | 350 - tests/hash-target.cpp | 4 +- tests/hash/main.cpp | 9 +- .../BaseFunctionalTest.cpp | 630 +- .../integration_test_lib/BaseFunctionalTest.h | 82 +- .../CoreRpcSerialization.cpp | 18 +- .../CoreRpcSerialization.h | 18 +- tests/integration_test_lib/InProcTestNode.cpp | 199 + tests/integration_test_lib/InProcTestNode.h | 66 + tests/integration_test_lib/Logger.cpp | 19 +- tests/integration_test_lib/Logger.h | 18 +- .../NetworkConfiguration.h | 46 + tests/integration_test_lib/NodeObserver.h | 124 + tests/integration_test_lib/Process.cpp | 82 + tests/integration_test_lib/Process.h | 36 + tests/integration_test_lib/RPCTestNode.cpp | 271 +- tests/integration_test_lib/RPCTestNode.h | 46 +- tests/integration_test_lib/TestNetwork.cpp | 261 + tests/integration_test_lib/TestNetwork.h | 87 + tests/integration_test_lib/TestNode.h | 30 +- tests/integration_test_lib/TestWallet.cpp | 145 + tests/integration_test_lib/TestWallet.h | 60 + tests/integration_tests/BlockchainInfo.h | 69 + tests/integration_tests/IntegrationTests.cpp | 232 + tests/integration_tests/MultiVersion.cpp | 250 + tests/integration_tests/Node.cpp | 469 + tests/integration_tests/WalletObserver.h | 166 + tests/integration_tests/main.cpp | 122 +- tests/io.h | 2 +- tests/net_load_tests/clt.cpp | 621 - tests/net_load_tests/net_load_tests.h | 343 - tests/net_load_tests/srv.cpp | 227 - .../node_rpc_proxy_test.cpp | 6 +- .../performance_tests/check_ring_signature.h | 12 +- tests/performance_tests/cn_slow_hash.h | 22 +- tests/performance_tests/construct_tx.h | 12 +- tests/performance_tests/derive_public_key.h | 4 +- tests/performance_tests/derive_secret_key.h | 4 +- .../generate_key_derivation.h | 2 +- tests/performance_tests/generate_key_image.h | 6 +- .../generate_key_image_helper.h | 6 +- tests/performance_tests/is_out_to_acc.h | 6 +- tests/performance_tests/main.cpp | 2 +- tests/performance_tests/multi_tx_test_base.h | 15 +- tests/performance_tests/performance_tests.h | 2 +- tests/performance_tests/performance_utils.h | 2 +- tests/performance_tests/single_tx_test_base.h | 14 +- tests/transfers_tests/globals.h | 5 +- tests/transfers_tests/main.cpp | 9 +- tests/transfers_tests/tests.cpp | 21 +- tests/unit_tests/ArrayRefTests.cpp | 344 + tests/unit_tests/ArrayViewTests.cpp | 316 + tests/unit_tests/BlockingQueue.cpp | 8 +- tests/unit_tests/EventWaiter.cpp | 4 +- tests/unit_tests/EventWaiter.h | 4 +- tests/unit_tests/ICoreStub.cpp | 27 +- tests/unit_tests/ICoreStub.h | 41 +- .../ICryptonoteProtocolQueryStub.cpp | 6 +- .../unit_tests/ICryptonoteProtocolQueryStub.h | 9 +- tests/unit_tests/INodeStubs.cpp | 78 +- tests/unit_tests/INodeStubs.h | 33 +- tests/unit_tests/StringBufferTests.cpp | 441 + tests/unit_tests/StringViewTests.cpp | 399 + tests/unit_tests/TestBlockchainGenerator.cpp | 178 +- tests/unit_tests/TestBlockchainGenerator.h | 41 +- tests/unit_tests/TestUpgradeDetector.cpp | 143 +- tests/unit_tests/TransactionApi.cpp | 36 +- tests/unit_tests/TransactionApiHelpers.h | 18 +- tests/unit_tests/TransfersObserver.h | 3 +- tests/unit_tests/base58.cpp | 34 +- .../binary_serialization_compatibility.cpp | 164 +- tests/unit_tests/block_reward.cpp | 27 +- tests/unit_tests/chacha8.cpp | 2 +- tests/unit_tests/checkpoints.cpp | 17 +- .../decompose_amount_into_digits.cpp | 16 +- tests/unit_tests/epee_boosted_tcp_server.cpp | 122 - .../epee_levin_protocol_handler_async.cpp | 519 - tests/unit_tests/get_xtype_from_string.cpp | 2 +- tests/unit_tests/main.cpp | 11 +- tests/unit_tests/mul_div.cpp | 4 +- tests/unit_tests/parse_amount.cpp | 11 +- tests/unit_tests/serialization.cpp | 8 +- tests/unit_tests/serialization_kv.cpp | 6 +- .../serialization_structs_comparators.h | 30 +- tests/unit_tests/shuffle.cpp | 5 +- tests/unit_tests/test_BcS.cpp | 108 +- tests/unit_tests/test_TransfersConsumer.cpp | 194 +- tests/unit_tests/test_TransfersContainer.cpp | 10 +- .../test_TransfersContainerKeyImage.cpp | 9 +- .../unit_tests/test_TransfersSubscription.cpp | 8 +- tests/unit_tests/test_format_utils.cpp | 94 +- tests/unit_tests/test_inprocess_node.cpp | 26 +- tests/unit_tests/test_path.cpp | 81 + tests/unit_tests/test_peerlist.cpp | 30 +- tests/unit_tests/test_protocol_pack.cpp | 6 +- tests/unit_tests/test_transfers.cpp | 92 +- tests/unit_tests/test_tx_pool_detach.cpp | 79 +- tests/unit_tests/test_wallet.cpp | 42 +- tests/unit_tests/tx_pool.cpp | 68 +- tests/unit_tests/unit_tests_utils.h | 2 +- 757 files changed, 144576 insertions(+), 19417 deletions(-) create mode 100644 external/gtest/CHANGES rename {tests => external}/gtest/CMakeLists.txt (88%) create mode 100644 external/gtest/CONTRIBUTORS create mode 100644 external/gtest/LICENSE create mode 100644 external/gtest/Makefile.am create mode 100644 external/gtest/Makefile.in create mode 100644 external/gtest/README create mode 100644 external/gtest/aclocal.m4 create mode 100644 external/gtest/build-aux/config.guess create mode 100644 external/gtest/build-aux/config.h.in create mode 100644 external/gtest/build-aux/config.sub create mode 100644 external/gtest/build-aux/depcomp create mode 100644 external/gtest/build-aux/install-sh create mode 100644 external/gtest/build-aux/ltmain.sh create mode 100644 external/gtest/build-aux/missing rename {tests => external}/gtest/cmake/internal_utils.cmake (93%) create mode 100644 external/gtest/codegear/gtest.cbproj create mode 100644 external/gtest/codegear/gtest.groupproj create mode 100644 external/gtest/codegear/gtest_all.cc create mode 100644 external/gtest/codegear/gtest_link.cc create mode 100644 external/gtest/codegear/gtest_main.cbproj create mode 100644 external/gtest/codegear/gtest_unittest.cbproj create mode 100644 external/gtest/configure create mode 100644 external/gtest/configure.ac create mode 100644 external/gtest/fused-src/gtest/gtest-all.cc create mode 100644 external/gtest/fused-src/gtest/gtest.h rename {tests/gtest/src => external/gtest/fused-src/gtest}/gtest_main.cc (95%) rename {tests => external}/gtest/include/gtest/gtest-death-test.h (94%) rename {tests => external}/gtest/include/gtest/gtest-message.h (77%) rename {tests => external}/gtest/include/gtest/gtest-param-test.h (99%) rename {tests => external}/gtest/include/gtest/gtest-param-test.h.pump (99%) rename {tests => external}/gtest/include/gtest/gtest-printers.h (93%) rename {tests => external}/gtest/include/gtest/gtest-spi.h (99%) rename {tests => external}/gtest/include/gtest/gtest-test-part.h (94%) rename {tests => external}/gtest/include/gtest/gtest-typed-test.h (100%) rename {tests => external}/gtest/include/gtest/gtest.h (88%) rename {tests => external}/gtest/include/gtest/gtest_pred_impl.h (98%) rename {tests => external}/gtest/include/gtest/gtest_prod.h (100%) rename {tests => external}/gtest/include/gtest/internal/gtest-death-test-internal.h (94%) rename {tests => external}/gtest/include/gtest/internal/gtest-filepath.h (96%) rename {tests => external}/gtest/include/gtest/internal/gtest-internal.h (88%) rename {tests => external}/gtest/include/gtest/internal/gtest-linked_ptr.h (98%) rename {tests => external}/gtest/include/gtest/internal/gtest-param-util-generated.h (83%) rename {tests => external}/gtest/include/gtest/internal/gtest-param-util-generated.h.pump (99%) rename {tests => external}/gtest/include/gtest/internal/gtest-param-util.h (99%) rename {tests => external}/gtest/include/gtest/internal/gtest-port.h (87%) create mode 100644 external/gtest/include/gtest/internal/gtest-string.h rename {tests => external}/gtest/include/gtest/internal/gtest-tuple.h (93%) rename {tests => external}/gtest/include/gtest/internal/gtest-tuple.h.pump (97%) rename {tests => external}/gtest/include/gtest/internal/gtest-type-util.h (99%) rename {tests => external}/gtest/include/gtest/internal/gtest-type-util.h.pump (96%) create mode 100644 external/gtest/m4/acx_pthread.m4 create mode 100644 external/gtest/m4/gtest.m4 create mode 100644 external/gtest/m4/libtool.m4 create mode 100644 external/gtest/m4/ltoptions.m4 create mode 100644 external/gtest/m4/ltsugar.m4 create mode 100644 external/gtest/m4/ltversion.m4 create mode 100644 external/gtest/m4/lt~obsolete.m4 create mode 100644 external/gtest/make/Makefile create mode 100644 external/gtest/msvc/gtest-md.sln create mode 100644 external/gtest/msvc/gtest-md.vcproj create mode 100644 external/gtest/msvc/gtest.sln create mode 100644 external/gtest/msvc/gtest.vcproj create mode 100644 external/gtest/msvc/gtest_main-md.vcproj create mode 100644 external/gtest/msvc/gtest_main.vcproj create mode 100644 external/gtest/msvc/gtest_prod_test-md.vcproj create mode 100644 external/gtest/msvc/gtest_prod_test.vcproj create mode 100644 external/gtest/msvc/gtest_unittest-md.vcproj create mode 100644 external/gtest/msvc/gtest_unittest.vcproj create mode 100644 external/gtest/samples/prime_tables.h create mode 100644 external/gtest/samples/sample1.cc create mode 100644 external/gtest/samples/sample1.h create mode 100644 external/gtest/samples/sample10_unittest.cc create mode 100644 external/gtest/samples/sample1_unittest.cc create mode 100644 external/gtest/samples/sample2.cc create mode 100644 external/gtest/samples/sample2.h create mode 100644 external/gtest/samples/sample2_unittest.cc create mode 100644 external/gtest/samples/sample3-inl.h create mode 100644 external/gtest/samples/sample3_unittest.cc create mode 100644 external/gtest/samples/sample4.cc create mode 100644 external/gtest/samples/sample4.h create mode 100644 external/gtest/samples/sample4_unittest.cc create mode 100644 external/gtest/samples/sample5_unittest.cc create mode 100644 external/gtest/samples/sample6_unittest.cc create mode 100644 external/gtest/samples/sample7_unittest.cc create mode 100644 external/gtest/samples/sample8_unittest.cc create mode 100644 external/gtest/samples/sample9_unittest.cc create mode 100644 external/gtest/scripts/fuse_gtest_files.py create mode 100644 external/gtest/scripts/gen_gtest_pred_impl.py create mode 100644 external/gtest/scripts/gtest-config.in create mode 100644 external/gtest/scripts/pump.py create mode 100644 external/gtest/scripts/test/Makefile rename {tests => external}/gtest/src/gtest-all.cc (100%) rename {tests => external}/gtest/src/gtest-death-test.cc (82%) rename {tests => external}/gtest/src/gtest-filepath.cc (94%) rename {tests => external}/gtest/src/gtest-internal-inl.h (83%) rename {tests => external}/gtest/src/gtest-port.cc (86%) rename {tests => external}/gtest/src/gtest-printers.cc (80%) rename {tests => external}/gtest/src/gtest-test-part.cc (95%) rename {tests => external}/gtest/src/gtest-typed-test.cc (96%) rename {tests => external}/gtest/src/gtest.cc (84%) create mode 100644 external/gtest/src/gtest_main.cc create mode 100644 external/gtest/test/gtest-death-test_ex_test.cc create mode 100644 external/gtest/test/gtest-death-test_test.cc create mode 100644 external/gtest/test/gtest-filepath_test.cc create mode 100644 external/gtest/test/gtest-linked_ptr_test.cc create mode 100644 external/gtest/test/gtest-listener_test.cc create mode 100644 external/gtest/test/gtest-message_test.cc create mode 100644 external/gtest/test/gtest-options_test.cc create mode 100644 external/gtest/test/gtest-param-test2_test.cc create mode 100644 external/gtest/test/gtest-param-test_test.cc create mode 100644 external/gtest/test/gtest-param-test_test.h create mode 100644 external/gtest/test/gtest-port_test.cc create mode 100644 external/gtest/test/gtest-printers_test.cc create mode 100644 external/gtest/test/gtest-test-part_test.cc create mode 100644 external/gtest/test/gtest-tuple_test.cc create mode 100644 external/gtest/test/gtest-typed-test2_test.cc create mode 100644 external/gtest/test/gtest-typed-test_test.cc create mode 100644 external/gtest/test/gtest-typed-test_test.h create mode 100644 external/gtest/test/gtest-unittest-api_test.cc create mode 100644 external/gtest/test/gtest_all_test.cc create mode 100644 external/gtest/test/gtest_break_on_failure_unittest.py create mode 100644 external/gtest/test/gtest_break_on_failure_unittest_.cc create mode 100644 external/gtest/test/gtest_catch_exceptions_test.py create mode 100644 external/gtest/test/gtest_catch_exceptions_test_.cc create mode 100644 external/gtest/test/gtest_color_test.py create mode 100644 external/gtest/test/gtest_color_test_.cc create mode 100644 external/gtest/test/gtest_env_var_test.py create mode 100644 external/gtest/test/gtest_env_var_test_.cc create mode 100644 external/gtest/test/gtest_environment_test.cc create mode 100644 external/gtest/test/gtest_filter_unittest.py create mode 100644 external/gtest/test/gtest_filter_unittest_.cc create mode 100644 external/gtest/test/gtest_help_test.py create mode 100644 external/gtest/test/gtest_help_test_.cc create mode 100644 external/gtest/test/gtest_list_tests_unittest.py create mode 100644 external/gtest/test/gtest_list_tests_unittest_.cc create mode 100644 external/gtest/test/gtest_main_unittest.cc create mode 100644 external/gtest/test/gtest_no_test_unittest.cc create mode 100644 external/gtest/test/gtest_output_test.py create mode 100644 external/gtest/test/gtest_output_test_.cc create mode 100644 external/gtest/test/gtest_output_test_golden_lin.txt create mode 100644 external/gtest/test/gtest_pred_impl_unittest.cc create mode 100644 external/gtest/test/gtest_premature_exit_test.cc create mode 100644 external/gtest/test/gtest_prod_test.cc create mode 100644 external/gtest/test/gtest_repeat_test.cc create mode 100644 external/gtest/test/gtest_shuffle_test.py create mode 100644 external/gtest/test/gtest_shuffle_test_.cc create mode 100644 external/gtest/test/gtest_sole_header_test.cc create mode 100644 external/gtest/test/gtest_stress_test.cc create mode 100644 external/gtest/test/gtest_test_utils.py create mode 100644 external/gtest/test/gtest_throw_on_failure_ex_test.cc create mode 100644 external/gtest/test/gtest_throw_on_failure_test.py create mode 100644 external/gtest/test/gtest_throw_on_failure_test_.cc create mode 100644 external/gtest/test/gtest_uninitialized_test.py create mode 100644 external/gtest/test/gtest_uninitialized_test_.cc create mode 100644 external/gtest/test/gtest_unittest.cc create mode 100644 external/gtest/test/gtest_xml_outfile1_test_.cc create mode 100644 external/gtest/test/gtest_xml_outfile2_test_.cc create mode 100644 external/gtest/test/gtest_xml_outfiles_test.py create mode 100644 external/gtest/test/gtest_xml_output_unittest.py create mode 100644 external/gtest/test/gtest_xml_output_unittest_.cc create mode 100644 external/gtest/test/gtest_xml_test_utils.py create mode 100644 external/gtest/test/production.cc create mode 100644 external/gtest/test/production.h create mode 100644 external/gtest/xcode/Config/DebugProject.xcconfig create mode 100644 external/gtest/xcode/Config/FrameworkTarget.xcconfig create mode 100644 external/gtest/xcode/Config/General.xcconfig create mode 100644 external/gtest/xcode/Config/ReleaseProject.xcconfig create mode 100644 external/gtest/xcode/Config/StaticLibraryTarget.xcconfig create mode 100644 external/gtest/xcode/Config/TestTarget.xcconfig create mode 100644 external/gtest/xcode/Resources/Info.plist create mode 100644 external/gtest/xcode/Samples/FrameworkSample/Info.plist create mode 100644 external/gtest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj create mode 100644 external/gtest/xcode/Samples/FrameworkSample/runtests.sh create mode 100644 external/gtest/xcode/Samples/FrameworkSample/widget.cc create mode 100644 external/gtest/xcode/Samples/FrameworkSample/widget.h create mode 100644 external/gtest/xcode/Samples/FrameworkSample/widget_test.cc create mode 100644 external/gtest/xcode/Scripts/runtests.sh create mode 100644 external/gtest/xcode/Scripts/versiongenerate.py create mode 100644 external/gtest/xcode/gtest.xcodeproj/project.pbxproj create mode 100755 include/IMultiWallet.h mode change 100755 => 100644 src/CMakeLists.txt create mode 100755 src/Common/ArrayRef.h create mode 100755 src/Common/ArrayView.h rename src/{common => Common}/BlockingQueue.cpp (92%) rename src/{common => Common}/BlockingQueue.h (97%) create mode 100644 src/Common/ConsoleHandler.cpp create mode 100644 src/Common/ConsoleHandler.h create mode 100644 src/Common/ConsoleTools.cpp create mode 100644 src/Common/ConsoleTools.h rename src/{Platform/Windows/System/InterruptedException.cpp => Common/IInputStream.cpp} (87%) create mode 100755 src/Common/IInputStream.h rename src/{Platform/Linux/System/InterruptedException.cpp => Common/IOutputStream.cpp} (87%) create mode 100755 src/Common/IOutputStream.h create mode 100644 src/Common/JsonValue.cpp create mode 100644 src/Common/JsonValue.h rename src/{common => Common}/ObserverManager.h (98%) create mode 100644 src/Common/PathTools.cpp create mode 100644 src/Common/PathTools.h rename src/{common => Common}/ShuffleGenerator.h (96%) rename src/{common => Common}/SignalHandler.cpp (52%) create mode 100644 src/Common/SignalHandler.h create mode 100755 src/Common/StreamTools.cpp create mode 100755 src/Common/StreamTools.h create mode 100755 src/Common/StringBuffer.h create mode 100755 src/Common/StringInputStream.cpp create mode 100755 src/Common/StringInputStream.h create mode 100755 src/Common/StringOutputStream.cpp rename src/{miner/target_helper.h => Common/StringOutputStream.h} (68%) mode change 100644 => 100755 create mode 100755 src/Common/StringTools.cpp create mode 100755 src/Common/StringTools.h create mode 100755 src/Common/StringView.cpp create mode 100755 src/Common/StringView.h rename src/{common => Common}/base58.cpp (99%) rename src/{common => Common}/base58.h (94%) create mode 100644 src/Common/boost_serialization_helper.h rename src/{common => Common}/command_line.cpp (94%) rename src/{common => Common}/command_line.h (90%) rename src/{common => Common}/int-util.h (99%) rename src/{common => Common}/pod-class.h (92%) rename src/{Platform/Linux/System/InterruptedException.h => Common/static_assert.h} (80%) rename src/{common => Common}/unordered_containers_boost_serialization.h (96%) rename src/{common => Common}/util.cpp (92%) rename src/{common => Common}/util.h (82%) rename src/{common => Common}/varint.h (97%) create mode 100755 src/CryptoNote/BaseTransaction.cpp create mode 100755 src/CryptoNote/BaseTransaction.h create mode 100755 src/CryptoNote/Block.cpp create mode 100755 src/CryptoNote/Block.h create mode 100755 src/CryptoNote/KeyInput.cpp create mode 100755 src/CryptoNote/KeyInput.h create mode 100755 src/CryptoNote/KeyOutput.cpp rename src/{Platform/Windows/System/Event.h => CryptoNote/KeyOutput.h} (62%) create mode 100755 src/CryptoNote/MultisignatureInput.cpp create mode 100755 src/CryptoNote/MultisignatureInput.h create mode 100755 src/CryptoNote/MultisignatureOutput.cpp create mode 100755 src/CryptoNote/MultisignatureOutput.h create mode 100755 src/CryptoNote/Transaction.cpp create mode 100755 src/CryptoNote/Transaction.h create mode 100755 src/CryptoNote/UnsignedKeyInput.cpp create mode 100755 src/CryptoNote/UnsignedKeyInput.h create mode 100755 src/CryptoNote/UnsignedMultisignatureInput.cpp create mode 100755 src/CryptoNote/UnsignedMultisignatureInput.h create mode 100755 src/CryptoNote/UnsignedTransaction.cpp create mode 100755 src/CryptoNote/UnsignedTransaction.h create mode 100644 src/HTTP/HttpParserErrorCodes.cpp create mode 100644 src/HTTP/HttpParserErrorCodes.h rename src/{inprocess_node => InProcessNode}/InProcessNode.cpp (71%) rename src/{inprocess_node => InProcessNode}/InProcessNode.h (79%) rename src/{inprocess_node => InProcessNode}/InProcessNodeErrors.cpp (88%) rename src/{inprocess_node => InProcessNode}/InProcessNodeErrors.h (88%) create mode 100755 src/Logging/CommonLogger.cpp create mode 100755 src/Logging/CommonLogger.h create mode 100755 src/Logging/ConsoleLogger.cpp rename src/{p2p/stdafx.h => Logging/ConsoleLogger.h} (68%) mode change 100644 => 100755 create mode 100755 src/Logging/FileLogger.cpp create mode 100755 src/Logging/FileLogger.h create mode 100755 src/Logging/ILogger.cpp create mode 100755 src/Logging/ILogger.h rename src/{serialization/debug_archive.h => Logging/LoggerGroup.cpp} (50%) mode change 100644 => 100755 create mode 100755 src/Logging/LoggerGroup.h create mode 100755 src/Logging/LoggerManager.cpp create mode 100755 src/Logging/LoggerManager.h rename src/{logger => Logging}/LoggerMessage.cpp (71%) create mode 100755 src/Logging/LoggerMessage.h create mode 100755 src/Logging/LoggerRef.cpp create mode 100755 src/Logging/LoggerRef.h create mode 100755 src/Logging/StreamLogger.cpp create mode 100755 src/Logging/StreamLogger.h create mode 100755 src/Platform/Linux/System/Ipv4Resolver.cpp create mode 100755 src/Platform/Linux/System/Ipv4Resolver.h delete mode 100755 src/Platform/OSX/System/Event.cpp create mode 100755 src/Platform/OSX/System/Ipv4Resolver.cpp create mode 100755 src/Platform/OSX/System/Ipv4Resolver.h create mode 100644 src/Platform/OSX/System/asm.s create mode 100644 src/Platform/OSX/System/context.c create mode 100644 src/Platform/OSX/System/context.h delete mode 100755 src/Platform/Windows/System/Event.cpp create mode 100755 src/Platform/Windows/System/Ipv4Resolver.cpp create mode 100755 src/Platform/Windows/System/Ipv4Resolver.h rename src/{platform => Platform}/mingw/alloca.h (92%) rename src/{platform => Platform}/msc/alloca.h (89%) rename src/{platform => Platform}/msc/stdbool.h (92%) rename src/{platform => Platform}/msc/sys/param.h (92%) rename src/{Platform/Linux => }/System/Event.cpp (61%) rename src/{Platform/OSX => }/System/Event.h (94%) create mode 100644 src/System/EventLock.cpp rename src/{Platform/OSX/System/InterruptedException.h => System/EventLock.h} (80%) mode change 100755 => 100644 rename src/{Platform/OSX => }/System/InterruptedException.cpp (92%) rename src/{Platform/Windows => }/System/InterruptedException.h (92%) create mode 100755 src/System/Ipv4Address.cpp create mode 100755 src/System/Ipv4Address.h create mode 100755 src/System/Latch.cpp rename src/{Platform/Linux/System/Event.h => System/Latch.h} (66%) create mode 100644 src/System/LatchGuard.cpp create mode 100644 src/System/LatchGuard.h delete mode 100644 src/common/SignalHandler.h delete mode 100644 src/common/boost_serialization_helper.h delete mode 100755 src/common/static_assert.h create mode 100644 src/cryptonote_core/OnceInInterval.h delete mode 100644 src/cryptonote_core/connection_context.h create mode 100755 src/cryptonote_core/cryptonote_basic.cpp mode change 100755 => 100644 src/cryptonote_core/cryptonote_core.cpp create mode 100644 src/cryptonote_protocol/cryptonote_protocol_handler.cpp delete mode 100644 src/cryptonote_protocol/cryptonote_protocol_handler.inl create mode 100644 src/daemon/DaemonCommandsHandler.h create mode 100644 src/daemon/DeamonCommandsHandler.cpp delete mode 100644 src/daemon/daemon_commands_handler.h delete mode 100755 src/logger/CommonLogger.cpp delete mode 100755 src/logger/CommonLogger.h delete mode 100755 src/logger/ConsoleLogger.cpp delete mode 100755 src/logger/ConsoleLogger.h delete mode 100755 src/logger/ILogger.cpp delete mode 100755 src/logger/ILogger.h delete mode 100755 src/logger/LoggerGroup.cpp delete mode 100755 src/logger/LoggerGroup.h delete mode 100755 src/logger/LoggerMessage.h delete mode 100755 src/logger/LoggerRef.cpp delete mode 100755 src/logger/LoggerRef.h delete mode 100755 src/logger/StreamLogger.cpp delete mode 100755 src/logger/StreamLogger.h delete mode 100644 src/miner/simpleminer.cpp delete mode 100644 src/miner/simpleminer.h delete mode 100644 src/miner/simpleminer_protocol_defs.h create mode 100644 src/p2p/LevinProtocol.cpp create mode 100644 src/p2p/LevinProtocol.h create mode 100644 src/p2p/PeerListManager.cpp create mode 100644 src/p2p/PeerListManager.h create mode 100644 src/p2p/connection_context.h create mode 100644 src/p2p/net_node.cpp delete mode 100644 src/p2p/net_node.inl delete mode 100644 src/p2p/net_peerlist.h create mode 100644 src/p2p/p2p_protocol_types.h create mode 100644 src/payment_service/ConfigurationManager.cpp create mode 100644 src/payment_service/ConfigurationManager.h create mode 100644 src/payment_service/JsonRpcMessages.cpp create mode 100644 src/payment_service/JsonRpcMessages.h create mode 100644 src/payment_service/JsonRpcServer.cpp create mode 100644 src/payment_service/JsonRpcServer.h create mode 100644 src/payment_service/NodeFactory.cpp create mode 100644 src/payment_service/NodeFactory.h create mode 100644 src/payment_service/PaymentServiceConfiguration.cpp create mode 100644 src/payment_service/PaymentServiceConfiguration.h create mode 100644 src/payment_service/RpcNodeConfiguration.cpp create mode 100644 src/payment_service/RpcNodeConfiguration.h create mode 100644 src/payment_service/WalletFactory.cpp create mode 100644 src/payment_service/WalletFactory.h create mode 100644 src/payment_service/WalletObservers.cpp create mode 100644 src/payment_service/WalletObservers.h create mode 100644 src/payment_service/WalletService.cpp create mode 100644 src/payment_service/WalletService.h create mode 100644 src/payment_service/WalletServiceErrorCodes.cpp create mode 100644 src/payment_service/WalletServiceErrorCodes.h create mode 100644 src/payment_service/main.cpp create mode 100644 src/rpc/HttpClient.cpp create mode 100644 src/rpc/HttpClient.h create mode 100644 src/rpc/HttpServer.cpp create mode 100644 src/rpc/HttpServer.h create mode 100644 src/rpc/JsonRpc.cpp create mode 100644 src/rpc/JsonRpc.h create mode 100644 src/rpc/RpcServer.cpp create mode 100644 src/rpc/RpcServer.h create mode 100644 src/rpc/RpcServerConfig.cpp create mode 100644 src/rpc/RpcServerConfig.h delete mode 100644 src/rpc/core_rpc_server.cpp delete mode 100644 src/rpc/core_rpc_server.h delete mode 100644 src/serialization/JsonSerializationDispatcher.h delete mode 100644 src/serialization/JsonValue.cpp delete mode 100644 src/serialization/JsonValue.h mode change 100755 => 100644 src/transfers/TransfersSubscription.cpp create mode 100644 src/wallet/MultiWallet.h create mode 100644 src/wallet/SyncWallet.cpp create mode 100644 src/wallet/SyncWallet.h delete mode 100644 src/wallet/wallet_errors.h mode change 100755 => 100644 tests/CMakeLists.txt create mode 100755 tests/System/DispatcherTests.cpp create mode 100755 tests/System/EventLockTests.cpp create mode 100755 tests/System/EventTests.cpp create mode 100755 tests/System/Ipv4AddressTests.cpp create mode 100755 tests/System/Ipv4ResolverTests.cpp create mode 100755 tests/System/LatchTests.cpp create mode 100755 tests/System/TcpConnectionTests.cpp create mode 100755 tests/System/TcpConnectorTests.cpp create mode 100755 tests/System/TcpListenerTests.cpp create mode 100755 tests/System/TimerTests.cpp create mode 100755 tests/System/main.cpp delete mode 100644 tests/core_proxy/core_proxy.cpp delete mode 100644 tests/core_proxy/core_proxy.h delete mode 100644 tests/daemon_tests/CMakeLists.txt delete mode 100644 tests/daemon_tests/transfers.cpp delete mode 100644 tests/gtest/include/gtest/internal/gtest-string.h create mode 100644 tests/integration_test_lib/InProcTestNode.cpp create mode 100644 tests/integration_test_lib/InProcTestNode.h create mode 100644 tests/integration_test_lib/NetworkConfiguration.h create mode 100644 tests/integration_test_lib/NodeObserver.h create mode 100644 tests/integration_test_lib/Process.cpp create mode 100644 tests/integration_test_lib/Process.h create mode 100644 tests/integration_test_lib/TestNetwork.cpp create mode 100644 tests/integration_test_lib/TestNetwork.h create mode 100644 tests/integration_test_lib/TestWallet.cpp create mode 100644 tests/integration_test_lib/TestWallet.h create mode 100644 tests/integration_tests/BlockchainInfo.h create mode 100644 tests/integration_tests/IntegrationTests.cpp create mode 100644 tests/integration_tests/MultiVersion.cpp create mode 100644 tests/integration_tests/Node.cpp create mode 100644 tests/integration_tests/WalletObserver.h delete mode 100644 tests/net_load_tests/clt.cpp delete mode 100644 tests/net_load_tests/net_load_tests.h delete mode 100644 tests/net_load_tests/srv.cpp create mode 100755 tests/unit_tests/ArrayRefTests.cpp create mode 100755 tests/unit_tests/ArrayViewTests.cpp mode change 100755 => 100644 tests/unit_tests/ICoreStub.h create mode 100755 tests/unit_tests/StringBufferTests.cpp create mode 100755 tests/unit_tests/StringViewTests.cpp delete mode 100644 tests/unit_tests/epee_boosted_tcp_server.cpp delete mode 100644 tests/unit_tests/epee_levin_protocol_handler_async.cpp create mode 100644 tests/unit_tests/test_path.cpp diff --git a/.gitignore b/.gitignore index 20a3894a65..5a86724462 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store /build /tags +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 102399c4cc..8893c32b7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,18 @@ set(VERSION "0.1") # $Format:Packaged from commit %H%nset(COMMIT %h)%nset(REFS "%d")$ set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(CMAKE_CONFIGURATION_TYPES "Debug;Release") -enable_testing() +set(CMAKE_CONFIGURATION_TYPES Debug RelWithDebInfo Release CACHE TYPE INTERNAL) +set(CMAKE_SKIP_INSTALL_RULES ON) +set(CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY ON) +set(CMAKE_SUPPRESS_REGENERATION ON) +#enable_testing() + +project(Bytecoin) include_directories(include src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") if(APPLE) include_directories(SYSTEM /usr/include/malloc) + enable_language(ASM) endif() if(MSVC) @@ -20,26 +26,30 @@ else() include_directories(src/Platform/Linux) endif() - set(STATIC ${MSVC} CACHE BOOL "Link libraries statically") 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 /D_VARIADIC_MAX=8 /D__SSE4_1__") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760") if(STATIC) - foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) + foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE) string(REPLACE "/MD" "/MT" ${VAR} "${${VAR}}") endforeach() endif() include_directories(SYSTEM src/platform/msc) else() + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + # This option has no effect in glibc version less than 2.20. + # Since glibc 2.20 _BSD_SOURCE is deprecated, this macro is recomended instead + add_definitions("-D_DEFAULT_SOURCE -D_GNU_SOURCE") + endif() set(ARCH native CACHE STRING "CPU to build for: -march value or default") if("${ARCH}" STREQUAL "default") set(ARCH_FLAG "") else() set(ARCH_FLAG "-march=${ARCH}") endif() - set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized -Wno-error=unused-result") + set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=unused-function -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized -Wno-error=unused-result") if(CMAKE_C_COMPILER_ID STREQUAL "Clang") set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration -Wno-error=unused-function") else() @@ -54,8 +64,11 @@ else() endif() set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes") set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes") + if(NOT APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes") if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0") endif() @@ -67,6 +80,12 @@ else() set(RELEASE_FLAGS "-Ofast -DNDEBUG -Wno-unused-variable") if(NOT APPLE) # There is a clang bug that does not allow to compile code that uses AES-NI intrinsics if -flto is enabled + if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" + AND ${CMAKE_BUILD_TYPE} STREQUAL "Release" AND ((CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.9) OR (CMAKE_C_COMPILER_VERSION VERSION_EQUAL 4.9))) + # On linux, to build in lto mode, check that ld.gold linker is used: 'update-alternatives --install /usr/bin/ld ld /usr/bin/ld.gold HIGHEST_PRIORITY' + set(CMAKE_AR gcc-ar) + set(CMAKE_RANLIB gcc-ranlib) + endif() set(RELEASE_FLAGS "${RELEASE_FLAGS} -flto") endif() #if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT MINGW) @@ -85,10 +104,7 @@ if(STATIC) set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_RUNTIME ON) endif() -find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options coroutine context) -if((${Boost_MAJOR_VERSION} EQUAL 1) AND (${Boost_MINOR_VERSION} EQUAL 54)) - message(SEND_ERROR "Boost version 1.54 is unsupported, more details are available here http://goo.gl/RrCFmA") -endif() +find_package(Boost 1.55 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization program_options) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) if(MINGW) set(Boost_LIBRARIES "${Boost_LIBRARIES};ws2_32;mswsock") diff --git a/README b/README index 75c2dce57c..1ab44e315c 100644 --- a/README +++ b/README @@ -18,7 +18,7 @@ Test suite: run `make test-release' to run tests in addition to building. Runnin Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++' before running `make'. On Windows: -Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.55. You may download them from: +Dependencies: MSVC 2013 or later, CMake 2.8.6 or later, and Boost 1.55. You may download them from: http://www.microsoft.com/ http://www.cmake.org/ http://www.boost.org/ @@ -26,7 +26,7 @@ http://www.boost.org/ To build, change to a directory where this file is located, and run this commands: mkdir build cd build -cmake -G "Visual Studio 11 Win64" .. +cmake -G "Visual Studio 12 Win64" .. And then do Build. Good luck! \ No newline at end of file diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index d00ac9aaf4..dcda199d29 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,10 @@ +Release notes 1.0.4 + +- Bytecoin RPC Wallet +- New multithreading library +- Improved console logging +- Further optimizations + Release notes 1.0.3 - Multisignature API diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index a1df78395f..39bd0ae491 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -35,6 +35,7 @@ #include #include +#include "include_base_utils.h" #include "string_tools.h" namespace epee diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl index 0265d57ee9..1b34425b5f 100644 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -757,6 +757,7 @@ POP_WARNINGS boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); 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); diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0a7a5d1ae8..a7d1860586 100755 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,9 +1,12 @@ set(UPNPC_BUILD_STATIC ON CACHE BOOL "Build static library") set(UPNPC_BUILD_SHARED OFF CACHE BOOL "Build shared library") set(UPNPC_BUILD_TESTS OFF CACHE BOOL "Build test executables") + add_subdirectory(miniupnpc) +add_subdirectory(gtest) + +set_property(TARGET upnpc-static gtest gtest_main PROPERTY FOLDER "external") -set_property(TARGET upnpc-static PROPERTY FOLDER "external") if(MSVC) set_property(TARGET upnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267") elseif(NOT MSVC) diff --git a/external/gtest/CHANGES b/external/gtest/CHANGES new file mode 100644 index 0000000000..0552132421 --- /dev/null +++ b/external/gtest/CHANGES @@ -0,0 +1,157 @@ +Changes for 1.7.0: + +* New feature: death tests are supported on OpenBSD and in iOS + simulator now. +* New feature: Google Test now implements a protocol to allow + a test runner to detect that a test program has exited + prematurely and report it as a failure (before it would be + falsely reported as a success if the exit code is 0). +* New feature: Test::RecordProperty() can now be used outside of the + lifespan of a test method, in which case it will be attributed to + the current test case or the test program in the XML report. +* New feature (potentially breaking): --gtest_list_tests now prints + the type parameters and value parameters for each test. +* Improvement: char pointers and char arrays are now escaped properly + in failure messages. +* Improvement: failure summary in XML reports now includes file and + line information. +* Improvement: the XML element now has a timestamp attribute. +* Improvement: When --gtest_filter is specified, XML report now doesn't + contain information about tests that are filtered out. +* Fixed the bug where long --gtest_filter flag values are truncated in + death tests. +* Potentially breaking change: RUN_ALL_TESTS() is now implemented as a + function instead of a macro in order to work better with Clang. +* Compatibility fixes with C++ 11 and various platforms. +* Bug/warning fixes. + +Changes for 1.6.0: + +* New feature: ADD_FAILURE_AT() for reporting a test failure at the + given source location -- useful for writing testing utilities. +* New feature: the universal value printer is moved from Google Mock + to Google Test. +* New feature: type parameters and value parameters are reported in + the XML report now. +* A gtest_disable_pthreads CMake option. +* Colored output works in GNU Screen sessions now. +* Parameters of value-parameterized tests are now printed in the + textual output. +* Failures from ad hoc test assertions run before RUN_ALL_TESTS() are + now correctly reported. +* Arguments of ASSERT_XY and EXPECT_XY no longer need to support << to + ostream. +* More complete handling of exceptions. +* GTEST_ASSERT_XY can be used instead of ASSERT_XY in case the latter + name is already used by another library. +* --gtest_catch_exceptions is now true by default, allowing a test + program to continue after an exception is thrown. +* Value-parameterized test fixtures can now derive from Test and + WithParamInterface separately, easing conversion of legacy tests. +* Death test messages are clearly marked to make them more + distinguishable from other messages. +* Compatibility fixes for Android, Google Native Client, MinGW, HP UX, + PowerPC, Lucid autotools, libCStd, Sun C++, Borland C++ Builder (Code Gear), + IBM XL C++ (Visual Age C++), and C++0x. +* Bug fixes and implementation clean-ups. +* Potentially incompatible changes: disables the harmful 'make install' + command in autotools. + +Changes for 1.5.0: + + * New feature: assertions can be safely called in multiple threads + where the pthreads library is available. + * New feature: predicates used inside EXPECT_TRUE() and friends + can now generate custom failure messages. + * New feature: Google Test can now be compiled as a DLL. + * New feature: fused source files are included. + * New feature: prints help when encountering unrecognized Google Test flags. + * Experimental feature: CMake build script (requires CMake 2.6.4+). + * Experimental feature: the Pump script for meta programming. + * double values streamed to an assertion are printed with enough precision + to differentiate any two different values. + * Google Test now works on Solaris and AIX. + * Build and test script improvements. + * Bug fixes and implementation clean-ups. + + Potentially breaking changes: + + * Stopped supporting VC++ 7.1 with exceptions disabled. + * Dropped support for 'make install'. + +Changes for 1.4.0: + + * New feature: the event listener API + * New feature: test shuffling + * New feature: the XML report format is closer to junitreport and can + be parsed by Hudson now. + * New feature: when a test runs under Visual Studio, its failures are + integrated in the IDE. + * New feature: /MD(d) versions of VC++ projects. + * New feature: elapsed time for the tests is printed by default. + * New feature: comes with a TR1 tuple implementation such that Boost + is no longer needed for Combine(). + * New feature: EXPECT_DEATH_IF_SUPPORTED macro and friends. + * New feature: the Xcode project can now produce static gtest + libraries in addition to a framework. + * Compatibility fixes for Solaris, Cygwin, minGW, Windows Mobile, + Symbian, gcc, and C++Builder. + * Bug fixes and implementation clean-ups. + +Changes for 1.3.0: + + * New feature: death tests on Windows, Cygwin, and Mac. + * New feature: ability to use Google Test assertions in other testing + frameworks. + * New feature: ability to run disabled test via + --gtest_also_run_disabled_tests. + * New feature: the --help flag for printing the usage. + * New feature: access to Google Test flag values in user code. + * New feature: a script that packs Google Test into one .h and one + .cc file for easy deployment. + * New feature: support for distributing test functions to multiple + machines (requires support from the test runner). + * Bug fixes and implementation clean-ups. + +Changes for 1.2.1: + + * Compatibility fixes for Linux IA-64 and IBM z/OS. + * Added support for using Boost and other TR1 implementations. + * Changes to the build scripts to support upcoming release of Google C++ + Mocking Framework. + * Added Makefile to the distribution package. + * Improved build instructions in README. + +Changes for 1.2.0: + + * New feature: value-parameterized tests. + * New feature: the ASSERT/EXPECT_(NON)FATAL_FAILURE(_ON_ALL_THREADS) + macros. + * Changed the XML report format to match JUnit/Ant's. + * Added tests to the Xcode project. + * Added scons/SConscript for building with SCons. + * Added src/gtest-all.cc for building Google Test from a single file. + * Fixed compatibility with Solaris and z/OS. + * Enabled running Python tests on systems with python 2.3 installed, + e.g. Mac OS X 10.4. + * Bug fixes. + +Changes for 1.1.0: + + * New feature: type-parameterized tests. + * New feature: exception assertions. + * New feature: printing elapsed time of tests. + * Improved the robustness of death tests. + * Added an Xcode project and samples. + * Adjusted the output format on Windows to be understandable by Visual Studio. + * Minor bug fixes. + +Changes for 1.0.1: + + * Added project files for Visual Studio 7.1. + * Fixed issues with compiling on Mac OS X. + * Fixed issues with compiling on Cygwin. + +Changes for 1.0.0: + + * Initial Open Source release of Google Test diff --git a/tests/gtest/CMakeLists.txt b/external/gtest/CMakeLists.txt similarity index 88% rename from tests/gtest/CMakeLists.txt rename to external/gtest/CMakeLists.txt index 0fe26540b3..57470c84f3 100644 --- a/tests/gtest/CMakeLists.txt +++ b/external/gtest/CMakeLists.txt @@ -77,7 +77,7 @@ target_link_libraries(gtest_main gtest) # # They are not built by default. To build them, set the # gtest_build_samples option to ON. You can do it by running ccmake -# or specifying the -Dbuild_gtest_samples=ON flag when running cmake. +# or specifying the -Dgtest_build_samples=ON flag when running cmake. if (gtest_build_samples) cxx_executable(sample1_unittest samples gtest_main samples/sample1.cc) @@ -124,6 +124,8 @@ if (gtest_build_tests) test/gtest-param-test2_test.cc) cxx_test(gtest-port_test gtest_main) cxx_test(gtest_pred_impl_unittest gtest_main) + cxx_test(gtest_premature_exit_test gtest + test/gtest_premature_exit_test.cc) cxx_test(gtest-printers_test gtest_main) cxx_test(gtest_prod_test gtest_main test/production.cc) @@ -140,10 +142,13 @@ if (gtest_build_tests) ############################################################ # C++ tests built with non-standard compiler flags. - cxx_library(gtest_no_exception "${cxx_no_exception}" - src/gtest-all.cc) - cxx_library(gtest_main_no_exception "${cxx_no_exception}" - src/gtest-all.cc src/gtest_main.cc) + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_library(gtest_no_exception "${cxx_no_exception}" + src/gtest-all.cc) + cxx_library(gtest_main_no_exception "${cxx_no_exception}" + src/gtest-all.cc src/gtest_main.cc) + endif() cxx_library(gtest_main_no_rtti "${cxx_no_rtti}" src/gtest-all.cc src/gtest_main.cc) @@ -189,11 +194,15 @@ if (gtest_build_tests) cxx_executable(gtest_break_on_failure_unittest_ test gtest) py_test(gtest_break_on_failure_unittest) - cxx_executable_with_flags( - gtest_catch_exceptions_no_ex_test_ - "${cxx_no_exception}" - gtest_main_no_exception - test/gtest_catch_exceptions_test_.cc) + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_executable_with_flags( + gtest_catch_exceptions_no_ex_test_ + "${cxx_no_exception}" + gtest_main_no_exception + test/gtest_catch_exceptions_test_.cc) + endif() + cxx_executable_with_flags( gtest_catch_exceptions_ex_test_ "${cxx_exception}" @@ -222,11 +231,14 @@ if (gtest_build_tests) cxx_executable(gtest_shuffle_test_ test gtest) py_test(gtest_shuffle_test) - cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception) - set_target_properties(gtest_throw_on_failure_test_ - PROPERTIES - COMPILE_FLAGS "${cxx_no_exception}") - py_test(gtest_throw_on_failure_test) + # MSVC 7.1 does not support STL with exceptions disabled. + if (NOT MSVC OR MSVC_VERSION GREATER 1310) + cxx_executable(gtest_throw_on_failure_test_ test gtest_no_exception) + set_target_properties(gtest_throw_on_failure_test_ + PROPERTIES + COMPILE_FLAGS "${cxx_no_exception}") + py_test(gtest_throw_on_failure_test) + endif() cxx_executable(gtest_uninitialized_test_ test gtest) py_test(gtest_uninitialized_test) diff --git a/external/gtest/CONTRIBUTORS b/external/gtest/CONTRIBUTORS new file mode 100644 index 0000000000..feae2fc044 --- /dev/null +++ b/external/gtest/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Testing Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Ajay Joshi +Balázs Dán +Bharat Mediratta +Chandler Carruth +Chris Prince +Chris Taylor +Dan Egnor +Eric Roman +Hady Zalek +Jeffrey Yasskin +Jói Sigurðsson +Keir Mierle +Keith Ray +Kenton Varda +Manuel Klimek +Markus Heule +Mika Raento +Miklós Fazekas +Pasi Valminen +Patrick Hanna +Patrick Riley +Peter Kaminski +Preston Jackson +Rainer Klaffenboeck +Russ Cox +Russ Rufer +Sean Mcafee +Sigurður Ãsgeirsson +Tracy Bialik +Vadim Berman +Vlad Losev +Zhanyong Wan diff --git a/external/gtest/LICENSE b/external/gtest/LICENSE new file mode 100644 index 0000000000..1941a11f8c --- /dev/null +++ b/external/gtest/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/gtest/Makefile.am b/external/gtest/Makefile.am new file mode 100644 index 0000000000..9c96b42572 --- /dev/null +++ b/external/gtest/Makefile.am @@ -0,0 +1,306 @@ +# Automake file + +ACLOCAL_AMFLAGS = -I m4 + +# Nonstandard package files for distribution +EXTRA_DIST = \ + CHANGES \ + CONTRIBUTORS \ + LICENSE \ + include/gtest/gtest-param-test.h.pump \ + include/gtest/internal/gtest-param-util-generated.h.pump \ + include/gtest/internal/gtest-tuple.h.pump \ + include/gtest/internal/gtest-type-util.h.pump \ + make/Makefile \ + scripts/fuse_gtest_files.py \ + scripts/gen_gtest_pred_impl.py \ + scripts/pump.py \ + scripts/test/Makefile + +# gtest source files that we don't compile directly. They are +# #included by gtest-all.cc. +GTEST_SRC = \ + src/gtest-death-test.cc \ + src/gtest-filepath.cc \ + src/gtest-internal-inl.h \ + src/gtest-port.cc \ + src/gtest-printers.cc \ + src/gtest-test-part.cc \ + src/gtest-typed-test.cc \ + src/gtest.cc + +EXTRA_DIST += $(GTEST_SRC) + +# Sample files that we don't compile. +EXTRA_DIST += \ + samples/prime_tables.h \ + samples/sample2_unittest.cc \ + samples/sample3_unittest.cc \ + samples/sample4_unittest.cc \ + samples/sample5_unittest.cc \ + samples/sample6_unittest.cc \ + samples/sample7_unittest.cc \ + samples/sample8_unittest.cc \ + samples/sample9_unittest.cc + +# C++ test files that we don't compile directly. +EXTRA_DIST += \ + test/gtest-death-test_ex_test.cc \ + test/gtest-death-test_test.cc \ + test/gtest-filepath_test.cc \ + test/gtest-linked_ptr_test.cc \ + test/gtest-listener_test.cc \ + test/gtest-message_test.cc \ + test/gtest-options_test.cc \ + test/gtest-param-test2_test.cc \ + test/gtest-param-test2_test.cc \ + test/gtest-param-test_test.cc \ + test/gtest-param-test_test.cc \ + test/gtest-param-test_test.h \ + test/gtest-port_test.cc \ + test/gtest_premature_exit_test.cc \ + test/gtest-printers_test.cc \ + test/gtest-test-part_test.cc \ + test/gtest-tuple_test.cc \ + test/gtest-typed-test2_test.cc \ + test/gtest-typed-test_test.cc \ + test/gtest-typed-test_test.h \ + test/gtest-unittest-api_test.cc \ + test/gtest_break_on_failure_unittest_.cc \ + test/gtest_catch_exceptions_test_.cc \ + test/gtest_color_test_.cc \ + test/gtest_env_var_test_.cc \ + test/gtest_environment_test.cc \ + test/gtest_filter_unittest_.cc \ + test/gtest_help_test_.cc \ + test/gtest_list_tests_unittest_.cc \ + test/gtest_main_unittest.cc \ + test/gtest_no_test_unittest.cc \ + test/gtest_output_test_.cc \ + test/gtest_pred_impl_unittest.cc \ + test/gtest_prod_test.cc \ + test/gtest_repeat_test.cc \ + test/gtest_shuffle_test_.cc \ + test/gtest_sole_header_test.cc \ + test/gtest_stress_test.cc \ + test/gtest_throw_on_failure_ex_test.cc \ + test/gtest_throw_on_failure_test_.cc \ + test/gtest_uninitialized_test_.cc \ + test/gtest_unittest.cc \ + test/gtest_unittest.cc \ + test/gtest_xml_outfile1_test_.cc \ + test/gtest_xml_outfile2_test_.cc \ + test/gtest_xml_output_unittest_.cc \ + test/production.cc \ + test/production.h + +# Python tests that we don't run. +EXTRA_DIST += \ + test/gtest_break_on_failure_unittest.py \ + test/gtest_catch_exceptions_test.py \ + test/gtest_color_test.py \ + test/gtest_env_var_test.py \ + test/gtest_filter_unittest.py \ + test/gtest_help_test.py \ + test/gtest_list_tests_unittest.py \ + test/gtest_output_test.py \ + test/gtest_output_test_golden_lin.txt \ + test/gtest_shuffle_test.py \ + test/gtest_test_utils.py \ + test/gtest_throw_on_failure_test.py \ + test/gtest_uninitialized_test.py \ + test/gtest_xml_outfiles_test.py \ + test/gtest_xml_output_unittest.py \ + test/gtest_xml_test_utils.py + +# CMake script +EXTRA_DIST += \ + CMakeLists.txt \ + cmake/internal_utils.cmake + +# MSVC project files +EXTRA_DIST += \ + msvc/gtest-md.sln \ + msvc/gtest-md.vcproj \ + msvc/gtest.sln \ + msvc/gtest.vcproj \ + msvc/gtest_main-md.vcproj \ + msvc/gtest_main.vcproj \ + msvc/gtest_prod_test-md.vcproj \ + msvc/gtest_prod_test.vcproj \ + msvc/gtest_unittest-md.vcproj \ + msvc/gtest_unittest.vcproj + +# xcode project files +EXTRA_DIST += \ + xcode/Config/DebugProject.xcconfig \ + xcode/Config/FrameworkTarget.xcconfig \ + xcode/Config/General.xcconfig \ + xcode/Config/ReleaseProject.xcconfig \ + xcode/Config/StaticLibraryTarget.xcconfig \ + xcode/Config/TestTarget.xcconfig \ + xcode/Resources/Info.plist \ + xcode/Scripts/runtests.sh \ + xcode/Scripts/versiongenerate.py \ + xcode/gtest.xcodeproj/project.pbxproj + +# xcode sample files +EXTRA_DIST += \ + xcode/Samples/FrameworkSample/Info.plist \ + xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj \ + xcode/Samples/FrameworkSample/runtests.sh \ + xcode/Samples/FrameworkSample/widget.cc \ + xcode/Samples/FrameworkSample/widget.h \ + xcode/Samples/FrameworkSample/widget_test.cc + +# C++Builder project files +EXTRA_DIST += \ + codegear/gtest.cbproj \ + codegear/gtest.groupproj \ + codegear/gtest_all.cc \ + codegear/gtest_link.cc \ + codegear/gtest_main.cbproj \ + codegear/gtest_unittest.cbproj + +# Distribute and install M4 macro +m4datadir = $(datadir)/aclocal +m4data_DATA = m4/gtest.m4 +EXTRA_DIST += $(m4data_DATA) + +# We define the global AM_CPPFLAGS as everything we compile includes from these +# directories. +AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/include + +# Modifies compiler and linker flags for pthreads compatibility. +if HAVE_PTHREADS + AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1 + AM_LIBS = @PTHREAD_LIBS@ +else + AM_CXXFLAGS = -DGTEST_HAS_PTHREAD=0 +endif + +# Build rules for libraries. +lib_LTLIBRARIES = lib/libgtest.la lib/libgtest_main.la + +lib_libgtest_la_SOURCES = src/gtest-all.cc + +pkginclude_HEADERS = \ + include/gtest/gtest-death-test.h \ + include/gtest/gtest-message.h \ + include/gtest/gtest-param-test.h \ + include/gtest/gtest-printers.h \ + include/gtest/gtest-spi.h \ + include/gtest/gtest-test-part.h \ + include/gtest/gtest-typed-test.h \ + include/gtest/gtest.h \ + include/gtest/gtest_pred_impl.h \ + include/gtest/gtest_prod.h + +pkginclude_internaldir = $(pkgincludedir)/internal +pkginclude_internal_HEADERS = \ + include/gtest/internal/gtest-death-test-internal.h \ + include/gtest/internal/gtest-filepath.h \ + include/gtest/internal/gtest-internal.h \ + include/gtest/internal/gtest-linked_ptr.h \ + include/gtest/internal/gtest-param-util-generated.h \ + include/gtest/internal/gtest-param-util.h \ + include/gtest/internal/gtest-port.h \ + include/gtest/internal/gtest-string.h \ + include/gtest/internal/gtest-tuple.h \ + include/gtest/internal/gtest-type-util.h + +lib_libgtest_main_la_SOURCES = src/gtest_main.cc +lib_libgtest_main_la_LIBADD = lib/libgtest.la + +# Bulid rules for samples and tests. Automake's naming for some of +# these variables isn't terribly obvious, so this is a brief +# reference: +# +# TESTS -- Programs run automatically by "make check" +# check_PROGRAMS -- Programs built by "make check" but not necessarily run + +noinst_LTLIBRARIES = samples/libsamples.la + +samples_libsamples_la_SOURCES = \ + samples/sample1.cc \ + samples/sample1.h \ + samples/sample2.cc \ + samples/sample2.h \ + samples/sample3-inl.h \ + samples/sample4.cc \ + samples/sample4.h + +TESTS= +TESTS_ENVIRONMENT = GTEST_SOURCE_DIR="$(srcdir)/test" \ + GTEST_BUILD_DIR="$(top_builddir)/test" +check_PROGRAMS= + +# A simple sample on using gtest. +TESTS += samples/sample1_unittest +check_PROGRAMS += samples/sample1_unittest +samples_sample1_unittest_SOURCES = samples/sample1_unittest.cc +samples_sample1_unittest_LDADD = lib/libgtest_main.la \ + lib/libgtest.la \ + samples/libsamples.la + +# Another sample. It also verifies that libgtest works. +TESTS += samples/sample10_unittest +check_PROGRAMS += samples/sample10_unittest +samples_sample10_unittest_SOURCES = samples/sample10_unittest.cc +samples_sample10_unittest_LDADD = lib/libgtest.la + +# This tests most constructs of gtest and verifies that libgtest_main +# and libgtest work. +TESTS += test/gtest_all_test +check_PROGRAMS += test/gtest_all_test +test_gtest_all_test_SOURCES = test/gtest_all_test.cc +test_gtest_all_test_LDADD = lib/libgtest_main.la \ + lib/libgtest.la + +# Tests that fused gtest files compile and work. +FUSED_GTEST_SRC = \ + fused-src/gtest/gtest-all.cc \ + fused-src/gtest/gtest.h \ + fused-src/gtest/gtest_main.cc + +if HAVE_PYTHON +TESTS += test/fused_gtest_test +check_PROGRAMS += test/fused_gtest_test +test_fused_gtest_test_SOURCES = $(FUSED_GTEST_SRC) \ + samples/sample1.cc samples/sample1_unittest.cc +test_fused_gtest_test_CPPFLAGS = -I"$(srcdir)/fused-src" + +# Build rules for putting fused Google Test files into the distribution +# package. The user can also create those files by manually running +# scripts/fuse_gtest_files.py. +$(test_fused_gtest_test_SOURCES): fused-gtest + +fused-gtest: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \ + $(GTEST_SRC) src/gtest-all.cc src/gtest_main.cc \ + scripts/fuse_gtest_files.py + mkdir -p "$(srcdir)/fused-src" + chmod -R u+w "$(srcdir)/fused-src" + rm -f "$(srcdir)/fused-src/gtest/gtest-all.cc" + rm -f "$(srcdir)/fused-src/gtest/gtest.h" + "$(srcdir)/scripts/fuse_gtest_files.py" "$(srcdir)/fused-src" + cp -f "$(srcdir)/src/gtest_main.cc" "$(srcdir)/fused-src/gtest/" + +maintainer-clean-local: + rm -rf "$(srcdir)/fused-src" +endif + +# Death tests may produce core dumps in the build directory. In case +# this happens, clean them to keep distcleancheck happy. +CLEANFILES = core + +# Disables 'make install' as installing a compiled version of Google +# Test can lead to undefined behavior due to violation of the +# One-Definition Rule. + +install-exec-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system." + false + +install-data-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system." + false diff --git a/external/gtest/Makefile.in b/external/gtest/Makefile.in new file mode 100644 index 0000000000..874de7473f --- /dev/null +++ b/external/gtest/Makefile.in @@ -0,0 +1,1360 @@ +# Makefile.in generated by automake 1.11.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Automake file + + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +TESTS = samples/sample1_unittest$(EXEEXT) \ + samples/sample10_unittest$(EXEEXT) \ + test/gtest_all_test$(EXEEXT) $(am__EXEEXT_1) +check_PROGRAMS = samples/sample1_unittest$(EXEEXT) \ + samples/sample10_unittest$(EXEEXT) \ + test/gtest_all_test$(EXEEXT) $(am__EXEEXT_1) +@HAVE_PYTHON_TRUE@am__append_1 = test/fused_gtest_test +@HAVE_PYTHON_TRUE@am__append_2 = test/fused_gtest_test +subdir = . +DIST_COMMON = README $(am__configure_deps) $(pkginclude_HEADERS) \ + $(pkginclude_internal_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/build-aux/config.h.in \ + $(top_srcdir)/configure $(top_srcdir)/scripts/gtest-config.in \ + build-aux/config.guess build-aux/config.sub build-aux/depcomp \ + build-aux/install-sh build-aux/ltmain.sh build-aux/missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/acx_pthread.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/build-aux/config.h +CONFIG_CLEAN_FILES = scripts/gtest-config +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(m4datadir)" \ + "$(DESTDIR)$(pkgincludedir)" \ + "$(DESTDIR)$(pkginclude_internaldir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +lib_libgtest_la_LIBADD = +am__dirstamp = $(am__leading_dot)dirstamp +am_lib_libgtest_la_OBJECTS = src/gtest-all.lo +lib_libgtest_la_OBJECTS = $(am_lib_libgtest_la_OBJECTS) +lib_libgtest_main_la_DEPENDENCIES = lib/libgtest.la +am_lib_libgtest_main_la_OBJECTS = src/gtest_main.lo +lib_libgtest_main_la_OBJECTS = $(am_lib_libgtest_main_la_OBJECTS) +samples_libsamples_la_LIBADD = +am_samples_libsamples_la_OBJECTS = samples/sample1.lo \ + samples/sample2.lo samples/sample4.lo +samples_libsamples_la_OBJECTS = $(am_samples_libsamples_la_OBJECTS) +@HAVE_PYTHON_TRUE@am__EXEEXT_1 = test/fused_gtest_test$(EXEEXT) +am_samples_sample10_unittest_OBJECTS = \ + samples/sample10_unittest.$(OBJEXT) +samples_sample10_unittest_OBJECTS = \ + $(am_samples_sample10_unittest_OBJECTS) +samples_sample10_unittest_DEPENDENCIES = lib/libgtest.la +am_samples_sample1_unittest_OBJECTS = \ + samples/sample1_unittest.$(OBJEXT) +samples_sample1_unittest_OBJECTS = \ + $(am_samples_sample1_unittest_OBJECTS) +samples_sample1_unittest_DEPENDENCIES = lib/libgtest_main.la \ + lib/libgtest.la samples/libsamples.la +am__test_fused_gtest_test_SOURCES_DIST = fused-src/gtest/gtest-all.cc \ + fused-src/gtest/gtest.h fused-src/gtest/gtest_main.cc \ + samples/sample1.cc samples/sample1_unittest.cc +am__objects_1 = \ + fused-src/gtest/test_fused_gtest_test-gtest-all.$(OBJEXT) \ + fused-src/gtest/test_fused_gtest_test-gtest_main.$(OBJEXT) +@HAVE_PYTHON_TRUE@am_test_fused_gtest_test_OBJECTS = $(am__objects_1) \ +@HAVE_PYTHON_TRUE@ samples/test_fused_gtest_test-sample1.$(OBJEXT) \ +@HAVE_PYTHON_TRUE@ samples/test_fused_gtest_test-sample1_unittest.$(OBJEXT) +test_fused_gtest_test_OBJECTS = $(am_test_fused_gtest_test_OBJECTS) +test_fused_gtest_test_LDADD = $(LDADD) +am_test_gtest_all_test_OBJECTS = test/gtest_all_test.$(OBJEXT) +test_gtest_all_test_OBJECTS = $(am_test_gtest_all_test_OBJECTS) +test_gtest_all_test_DEPENDENCIES = lib/libgtest_main.la \ + lib/libgtest.la +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/build-aux +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(lib_libgtest_la_SOURCES) $(lib_libgtest_main_la_SOURCES) \ + $(samples_libsamples_la_SOURCES) \ + $(samples_sample10_unittest_SOURCES) \ + $(samples_sample1_unittest_SOURCES) \ + $(test_fused_gtest_test_SOURCES) \ + $(test_gtest_all_test_SOURCES) +DIST_SOURCES = $(lib_libgtest_la_SOURCES) \ + $(lib_libgtest_main_la_SOURCES) \ + $(samples_libsamples_la_SOURCES) \ + $(samples_sample10_unittest_SOURCES) \ + $(samples_sample1_unittest_SOURCES) \ + $(am__test_fused_gtest_test_SOURCES_DIST) \ + $(test_gtest_all_test_SOURCES) +DATA = $(m4data_DATA) +HEADERS = $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.bz2 $(distdir).zip +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +PYTHON = @PYTHON@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +acx_pthread_config = @acx_pthread_config@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +ACLOCAL_AMFLAGS = -I m4 + +# Nonstandard package files for distribution + +# Sample files that we don't compile. + +# C++ test files that we don't compile directly. + +# Python tests that we don't run. + +# CMake script + +# MSVC project files + +# xcode project files + +# xcode sample files + +# C++Builder project files +EXTRA_DIST = CHANGES CONTRIBUTORS LICENSE \ + include/gtest/gtest-param-test.h.pump \ + include/gtest/internal/gtest-param-util-generated.h.pump \ + include/gtest/internal/gtest-tuple.h.pump \ + include/gtest/internal/gtest-type-util.h.pump make/Makefile \ + scripts/fuse_gtest_files.py scripts/gen_gtest_pred_impl.py \ + scripts/pump.py scripts/test/Makefile $(GTEST_SRC) \ + samples/prime_tables.h samples/sample2_unittest.cc \ + samples/sample3_unittest.cc samples/sample4_unittest.cc \ + samples/sample5_unittest.cc samples/sample6_unittest.cc \ + samples/sample7_unittest.cc samples/sample8_unittest.cc \ + samples/sample9_unittest.cc test/gtest-death-test_ex_test.cc \ + test/gtest-death-test_test.cc test/gtest-filepath_test.cc \ + test/gtest-linked_ptr_test.cc test/gtest-listener_test.cc \ + test/gtest-message_test.cc test/gtest-options_test.cc \ + test/gtest-param-test2_test.cc test/gtest-param-test2_test.cc \ + test/gtest-param-test_test.cc test/gtest-param-test_test.cc \ + test/gtest-param-test_test.h test/gtest-port_test.cc \ + test/gtest_premature_exit_test.cc test/gtest-printers_test.cc \ + test/gtest-test-part_test.cc test/gtest-tuple_test.cc \ + test/gtest-typed-test2_test.cc test/gtest-typed-test_test.cc \ + test/gtest-typed-test_test.h test/gtest-unittest-api_test.cc \ + test/gtest_break_on_failure_unittest_.cc \ + test/gtest_catch_exceptions_test_.cc test/gtest_color_test_.cc \ + test/gtest_env_var_test_.cc test/gtest_environment_test.cc \ + test/gtest_filter_unittest_.cc test/gtest_help_test_.cc \ + test/gtest_list_tests_unittest_.cc test/gtest_main_unittest.cc \ + test/gtest_no_test_unittest.cc test/gtest_output_test_.cc \ + test/gtest_pred_impl_unittest.cc test/gtest_prod_test.cc \ + test/gtest_repeat_test.cc test/gtest_shuffle_test_.cc \ + test/gtest_sole_header_test.cc test/gtest_stress_test.cc \ + test/gtest_throw_on_failure_ex_test.cc \ + test/gtest_throw_on_failure_test_.cc \ + test/gtest_uninitialized_test_.cc test/gtest_unittest.cc \ + test/gtest_unittest.cc test/gtest_xml_outfile1_test_.cc \ + test/gtest_xml_outfile2_test_.cc \ + test/gtest_xml_output_unittest_.cc test/production.cc \ + test/production.h test/gtest_break_on_failure_unittest.py \ + test/gtest_catch_exceptions_test.py test/gtest_color_test.py \ + test/gtest_env_var_test.py test/gtest_filter_unittest.py \ + test/gtest_help_test.py test/gtest_list_tests_unittest.py \ + test/gtest_output_test.py \ + test/gtest_output_test_golden_lin.txt \ + test/gtest_shuffle_test.py test/gtest_test_utils.py \ + test/gtest_throw_on_failure_test.py \ + test/gtest_uninitialized_test.py \ + test/gtest_xml_outfiles_test.py \ + test/gtest_xml_output_unittest.py test/gtest_xml_test_utils.py \ + CMakeLists.txt cmake/internal_utils.cmake msvc/gtest-md.sln \ + msvc/gtest-md.vcproj msvc/gtest.sln msvc/gtest.vcproj \ + msvc/gtest_main-md.vcproj msvc/gtest_main.vcproj \ + msvc/gtest_prod_test-md.vcproj msvc/gtest_prod_test.vcproj \ + msvc/gtest_unittest-md.vcproj msvc/gtest_unittest.vcproj \ + xcode/Config/DebugProject.xcconfig \ + xcode/Config/FrameworkTarget.xcconfig \ + xcode/Config/General.xcconfig \ + xcode/Config/ReleaseProject.xcconfig \ + xcode/Config/StaticLibraryTarget.xcconfig \ + xcode/Config/TestTarget.xcconfig xcode/Resources/Info.plist \ + xcode/Scripts/runtests.sh xcode/Scripts/versiongenerate.py \ + xcode/gtest.xcodeproj/project.pbxproj \ + xcode/Samples/FrameworkSample/Info.plist \ + xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj \ + xcode/Samples/FrameworkSample/runtests.sh \ + xcode/Samples/FrameworkSample/widget.cc \ + xcode/Samples/FrameworkSample/widget.h \ + xcode/Samples/FrameworkSample/widget_test.cc \ + codegear/gtest.cbproj codegear/gtest.groupproj \ + codegear/gtest_all.cc codegear/gtest_link.cc \ + codegear/gtest_main.cbproj codegear/gtest_unittest.cbproj \ + $(m4data_DATA) + +# gtest source files that we don't compile directly. They are +# #included by gtest-all.cc. +GTEST_SRC = \ + src/gtest-death-test.cc \ + src/gtest-filepath.cc \ + src/gtest-internal-inl.h \ + src/gtest-port.cc \ + src/gtest-printers.cc \ + src/gtest-test-part.cc \ + src/gtest-typed-test.cc \ + src/gtest.cc + + +# Distribute and install M4 macro +m4datadir = $(datadir)/aclocal +m4data_DATA = m4/gtest.m4 + +# We define the global AM_CPPFLAGS as everything we compile includes from these +# directories. +AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/include +@HAVE_PTHREADS_FALSE@AM_CXXFLAGS = -DGTEST_HAS_PTHREAD=0 + +# Modifies compiler and linker flags for pthreads compatibility. +@HAVE_PTHREADS_TRUE@AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1 +@HAVE_PTHREADS_TRUE@AM_LIBS = @PTHREAD_LIBS@ + +# Build rules for libraries. +lib_LTLIBRARIES = lib/libgtest.la lib/libgtest_main.la +lib_libgtest_la_SOURCES = src/gtest-all.cc +pkginclude_HEADERS = \ + include/gtest/gtest-death-test.h \ + include/gtest/gtest-message.h \ + include/gtest/gtest-param-test.h \ + include/gtest/gtest-printers.h \ + include/gtest/gtest-spi.h \ + include/gtest/gtest-test-part.h \ + include/gtest/gtest-typed-test.h \ + include/gtest/gtest.h \ + include/gtest/gtest_pred_impl.h \ + include/gtest/gtest_prod.h + +pkginclude_internaldir = $(pkgincludedir)/internal +pkginclude_internal_HEADERS = \ + include/gtest/internal/gtest-death-test-internal.h \ + include/gtest/internal/gtest-filepath.h \ + include/gtest/internal/gtest-internal.h \ + include/gtest/internal/gtest-linked_ptr.h \ + include/gtest/internal/gtest-param-util-generated.h \ + include/gtest/internal/gtest-param-util.h \ + include/gtest/internal/gtest-port.h \ + include/gtest/internal/gtest-string.h \ + include/gtest/internal/gtest-tuple.h \ + include/gtest/internal/gtest-type-util.h + +lib_libgtest_main_la_SOURCES = src/gtest_main.cc +lib_libgtest_main_la_LIBADD = lib/libgtest.la + +# Bulid rules for samples and tests. Automake's naming for some of +# these variables isn't terribly obvious, so this is a brief +# reference: +# +# TESTS -- Programs run automatically by "make check" +# check_PROGRAMS -- Programs built by "make check" but not necessarily run +noinst_LTLIBRARIES = samples/libsamples.la +samples_libsamples_la_SOURCES = \ + samples/sample1.cc \ + samples/sample1.h \ + samples/sample2.cc \ + samples/sample2.h \ + samples/sample3-inl.h \ + samples/sample4.cc \ + samples/sample4.h + +TESTS_ENVIRONMENT = GTEST_SOURCE_DIR="$(srcdir)/test" \ + GTEST_BUILD_DIR="$(top_builddir)/test" + +samples_sample1_unittest_SOURCES = samples/sample1_unittest.cc +samples_sample1_unittest_LDADD = lib/libgtest_main.la \ + lib/libgtest.la \ + samples/libsamples.la + +samples_sample10_unittest_SOURCES = samples/sample10_unittest.cc +samples_sample10_unittest_LDADD = lib/libgtest.la +test_gtest_all_test_SOURCES = test/gtest_all_test.cc +test_gtest_all_test_LDADD = lib/libgtest_main.la \ + lib/libgtest.la + + +# Tests that fused gtest files compile and work. +FUSED_GTEST_SRC = \ + fused-src/gtest/gtest-all.cc \ + fused-src/gtest/gtest.h \ + fused-src/gtest/gtest_main.cc + +@HAVE_PYTHON_TRUE@test_fused_gtest_test_SOURCES = $(FUSED_GTEST_SRC) \ +@HAVE_PYTHON_TRUE@ samples/sample1.cc samples/sample1_unittest.cc + +@HAVE_PYTHON_TRUE@test_fused_gtest_test_CPPFLAGS = -I"$(srcdir)/fused-src" + +# Death tests may produce core dumps in the build directory. In case +# this happens, clean them to keep distcleancheck happy. +CLEANFILES = core +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +build-aux/config.h: build-aux/stamp-h1 + @if test ! -f $@; then rm -f build-aux/stamp-h1; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) build-aux/stamp-h1; else :; fi + +build-aux/stamp-h1: $(top_srcdir)/build-aux/config.h.in $(top_builddir)/config.status + @rm -f build-aux/stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status build-aux/config.h +$(top_srcdir)/build-aux/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f build-aux/stamp-h1 + touch $@ + +distclean-hdr: + -rm -f build-aux/config.h build-aux/stamp-h1 +scripts/gtest-config: $(top_builddir)/config.status $(top_srcdir)/scripts/gtest-config.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +src/$(am__dirstamp): + @$(MKDIR_P) src + @: > src/$(am__dirstamp) +src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/$(DEPDIR) + @: > src/$(DEPDIR)/$(am__dirstamp) +src/gtest-all.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) +lib/$(am__dirstamp): + @$(MKDIR_P) lib + @: > lib/$(am__dirstamp) +lib/libgtest.la: $(lib_libgtest_la_OBJECTS) $(lib_libgtest_la_DEPENDENCIES) $(EXTRA_lib_libgtest_la_DEPENDENCIES) lib/$(am__dirstamp) + $(CXXLINK) -rpath $(libdir) $(lib_libgtest_la_OBJECTS) $(lib_libgtest_la_LIBADD) $(LIBS) +src/gtest_main.lo: src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) +lib/libgtest_main.la: $(lib_libgtest_main_la_OBJECTS) $(lib_libgtest_main_la_DEPENDENCIES) $(EXTRA_lib_libgtest_main_la_DEPENDENCIES) lib/$(am__dirstamp) + $(CXXLINK) -rpath $(libdir) $(lib_libgtest_main_la_OBJECTS) $(lib_libgtest_main_la_LIBADD) $(LIBS) +samples/$(am__dirstamp): + @$(MKDIR_P) samples + @: > samples/$(am__dirstamp) +samples/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) samples/$(DEPDIR) + @: > samples/$(DEPDIR)/$(am__dirstamp) +samples/sample1.lo: samples/$(am__dirstamp) \ + samples/$(DEPDIR)/$(am__dirstamp) +samples/sample2.lo: samples/$(am__dirstamp) \ + samples/$(DEPDIR)/$(am__dirstamp) +samples/sample4.lo: samples/$(am__dirstamp) \ + samples/$(DEPDIR)/$(am__dirstamp) +samples/libsamples.la: $(samples_libsamples_la_OBJECTS) $(samples_libsamples_la_DEPENDENCIES) $(EXTRA_samples_libsamples_la_DEPENDENCIES) samples/$(am__dirstamp) + $(CXXLINK) $(samples_libsamples_la_OBJECTS) $(samples_libsamples_la_LIBADD) $(LIBS) + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +samples/sample10_unittest.$(OBJEXT): samples/$(am__dirstamp) \ + samples/$(DEPDIR)/$(am__dirstamp) +samples/sample10_unittest$(EXEEXT): $(samples_sample10_unittest_OBJECTS) $(samples_sample10_unittest_DEPENDENCIES) $(EXTRA_samples_sample10_unittest_DEPENDENCIES) samples/$(am__dirstamp) + @rm -f samples/sample10_unittest$(EXEEXT) + $(CXXLINK) $(samples_sample10_unittest_OBJECTS) $(samples_sample10_unittest_LDADD) $(LIBS) +samples/sample1_unittest.$(OBJEXT): samples/$(am__dirstamp) \ + samples/$(DEPDIR)/$(am__dirstamp) +samples/sample1_unittest$(EXEEXT): $(samples_sample1_unittest_OBJECTS) $(samples_sample1_unittest_DEPENDENCIES) $(EXTRA_samples_sample1_unittest_DEPENDENCIES) samples/$(am__dirstamp) + @rm -f samples/sample1_unittest$(EXEEXT) + $(CXXLINK) $(samples_sample1_unittest_OBJECTS) $(samples_sample1_unittest_LDADD) $(LIBS) +fused-src/gtest/$(am__dirstamp): + @$(MKDIR_P) fused-src/gtest + @: > fused-src/gtest/$(am__dirstamp) +fused-src/gtest/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) fused-src/gtest/$(DEPDIR) + @: > fused-src/gtest/$(DEPDIR)/$(am__dirstamp) +fused-src/gtest/test_fused_gtest_test-gtest-all.$(OBJEXT): \ + fused-src/gtest/$(am__dirstamp) \ + fused-src/gtest/$(DEPDIR)/$(am__dirstamp) +fused-src/gtest/test_fused_gtest_test-gtest_main.$(OBJEXT): \ + fused-src/gtest/$(am__dirstamp) \ + fused-src/gtest/$(DEPDIR)/$(am__dirstamp) +samples/test_fused_gtest_test-sample1.$(OBJEXT): \ + samples/$(am__dirstamp) samples/$(DEPDIR)/$(am__dirstamp) +samples/test_fused_gtest_test-sample1_unittest.$(OBJEXT): \ + samples/$(am__dirstamp) samples/$(DEPDIR)/$(am__dirstamp) +test/$(am__dirstamp): + @$(MKDIR_P) test + @: > test/$(am__dirstamp) +test/fused_gtest_test$(EXEEXT): $(test_fused_gtest_test_OBJECTS) $(test_fused_gtest_test_DEPENDENCIES) $(EXTRA_test_fused_gtest_test_DEPENDENCIES) test/$(am__dirstamp) + @rm -f test/fused_gtest_test$(EXEEXT) + $(CXXLINK) $(test_fused_gtest_test_OBJECTS) $(test_fused_gtest_test_LDADD) $(LIBS) +test/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) test/$(DEPDIR) + @: > test/$(DEPDIR)/$(am__dirstamp) +test/gtest_all_test.$(OBJEXT): test/$(am__dirstamp) \ + test/$(DEPDIR)/$(am__dirstamp) +test/gtest_all_test$(EXEEXT): $(test_gtest_all_test_OBJECTS) $(test_gtest_all_test_DEPENDENCIES) $(EXTRA_test_gtest_all_test_DEPENDENCIES) test/$(am__dirstamp) + @rm -f test/gtest_all_test$(EXEEXT) + $(CXXLINK) $(test_gtest_all_test_OBJECTS) $(test_gtest_all_test_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f fused-src/gtest/test_fused_gtest_test-gtest-all.$(OBJEXT) + -rm -f fused-src/gtest/test_fused_gtest_test-gtest_main.$(OBJEXT) + -rm -f samples/sample1.$(OBJEXT) + -rm -f samples/sample1.lo + -rm -f samples/sample10_unittest.$(OBJEXT) + -rm -f samples/sample1_unittest.$(OBJEXT) + -rm -f samples/sample2.$(OBJEXT) + -rm -f samples/sample2.lo + -rm -f samples/sample4.$(OBJEXT) + -rm -f samples/sample4.lo + -rm -f samples/test_fused_gtest_test-sample1.$(OBJEXT) + -rm -f samples/test_fused_gtest_test-sample1_unittest.$(OBJEXT) + -rm -f src/gtest-all.$(OBJEXT) + -rm -f src/gtest-all.lo + -rm -f src/gtest_main.$(OBJEXT) + -rm -f src/gtest_main.lo + -rm -f test/gtest_all_test.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/sample1.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/sample10_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/sample1_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/sample2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/sample4.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/test_fused_gtest_test-sample1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/gtest-all.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/gtest_main.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/gtest_all_test.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< + +fused-src/gtest/test_fused_gtest_test-gtest-all.o: fused-src/gtest/gtest-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT fused-src/gtest/test_fused_gtest_test-gtest-all.o -MD -MP -MF fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Tpo -c -o fused-src/gtest/test_fused_gtest_test-gtest-all.o `test -f 'fused-src/gtest/gtest-all.cc' || echo '$(srcdir)/'`fused-src/gtest/gtest-all.cc +@am__fastdepCXX_TRUE@ $(am__mv) fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Tpo fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='fused-src/gtest/gtest-all.cc' object='fused-src/gtest/test_fused_gtest_test-gtest-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o fused-src/gtest/test_fused_gtest_test-gtest-all.o `test -f 'fused-src/gtest/gtest-all.cc' || echo '$(srcdir)/'`fused-src/gtest/gtest-all.cc + +fused-src/gtest/test_fused_gtest_test-gtest-all.obj: fused-src/gtest/gtest-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT fused-src/gtest/test_fused_gtest_test-gtest-all.obj -MD -MP -MF fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Tpo -c -o fused-src/gtest/test_fused_gtest_test-gtest-all.obj `if test -f 'fused-src/gtest/gtest-all.cc'; then $(CYGPATH_W) 'fused-src/gtest/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/fused-src/gtest/gtest-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Tpo fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='fused-src/gtest/gtest-all.cc' object='fused-src/gtest/test_fused_gtest_test-gtest-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o fused-src/gtest/test_fused_gtest_test-gtest-all.obj `if test -f 'fused-src/gtest/gtest-all.cc'; then $(CYGPATH_W) 'fused-src/gtest/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/fused-src/gtest/gtest-all.cc'; fi` + +fused-src/gtest/test_fused_gtest_test-gtest_main.o: fused-src/gtest/gtest_main.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT fused-src/gtest/test_fused_gtest_test-gtest_main.o -MD -MP -MF fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Tpo -c -o fused-src/gtest/test_fused_gtest_test-gtest_main.o `test -f 'fused-src/gtest/gtest_main.cc' || echo '$(srcdir)/'`fused-src/gtest/gtest_main.cc +@am__fastdepCXX_TRUE@ $(am__mv) fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Tpo fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='fused-src/gtest/gtest_main.cc' object='fused-src/gtest/test_fused_gtest_test-gtest_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o fused-src/gtest/test_fused_gtest_test-gtest_main.o `test -f 'fused-src/gtest/gtest_main.cc' || echo '$(srcdir)/'`fused-src/gtest/gtest_main.cc + +fused-src/gtest/test_fused_gtest_test-gtest_main.obj: fused-src/gtest/gtest_main.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT fused-src/gtest/test_fused_gtest_test-gtest_main.obj -MD -MP -MF fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Tpo -c -o fused-src/gtest/test_fused_gtest_test-gtest_main.obj `if test -f 'fused-src/gtest/gtest_main.cc'; then $(CYGPATH_W) 'fused-src/gtest/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/fused-src/gtest/gtest_main.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Tpo fused-src/gtest/$(DEPDIR)/test_fused_gtest_test-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='fused-src/gtest/gtest_main.cc' object='fused-src/gtest/test_fused_gtest_test-gtest_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o fused-src/gtest/test_fused_gtest_test-gtest_main.obj `if test -f 'fused-src/gtest/gtest_main.cc'; then $(CYGPATH_W) 'fused-src/gtest/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/fused-src/gtest/gtest_main.cc'; fi` + +samples/test_fused_gtest_test-sample1.o: samples/sample1.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT samples/test_fused_gtest_test-sample1.o -MD -MP -MF samples/$(DEPDIR)/test_fused_gtest_test-sample1.Tpo -c -o samples/test_fused_gtest_test-sample1.o `test -f 'samples/sample1.cc' || echo '$(srcdir)/'`samples/sample1.cc +@am__fastdepCXX_TRUE@ $(am__mv) samples/$(DEPDIR)/test_fused_gtest_test-sample1.Tpo samples/$(DEPDIR)/test_fused_gtest_test-sample1.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='samples/sample1.cc' object='samples/test_fused_gtest_test-sample1.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o samples/test_fused_gtest_test-sample1.o `test -f 'samples/sample1.cc' || echo '$(srcdir)/'`samples/sample1.cc + +samples/test_fused_gtest_test-sample1.obj: samples/sample1.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT samples/test_fused_gtest_test-sample1.obj -MD -MP -MF samples/$(DEPDIR)/test_fused_gtest_test-sample1.Tpo -c -o samples/test_fused_gtest_test-sample1.obj `if test -f 'samples/sample1.cc'; then $(CYGPATH_W) 'samples/sample1.cc'; else $(CYGPATH_W) '$(srcdir)/samples/sample1.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) samples/$(DEPDIR)/test_fused_gtest_test-sample1.Tpo samples/$(DEPDIR)/test_fused_gtest_test-sample1.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='samples/sample1.cc' object='samples/test_fused_gtest_test-sample1.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o samples/test_fused_gtest_test-sample1.obj `if test -f 'samples/sample1.cc'; then $(CYGPATH_W) 'samples/sample1.cc'; else $(CYGPATH_W) '$(srcdir)/samples/sample1.cc'; fi` + +samples/test_fused_gtest_test-sample1_unittest.o: samples/sample1_unittest.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT samples/test_fused_gtest_test-sample1_unittest.o -MD -MP -MF samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Tpo -c -o samples/test_fused_gtest_test-sample1_unittest.o `test -f 'samples/sample1_unittest.cc' || echo '$(srcdir)/'`samples/sample1_unittest.cc +@am__fastdepCXX_TRUE@ $(am__mv) samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Tpo samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='samples/sample1_unittest.cc' object='samples/test_fused_gtest_test-sample1_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o samples/test_fused_gtest_test-sample1_unittest.o `test -f 'samples/sample1_unittest.cc' || echo '$(srcdir)/'`samples/sample1_unittest.cc + +samples/test_fused_gtest_test-sample1_unittest.obj: samples/sample1_unittest.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT samples/test_fused_gtest_test-sample1_unittest.obj -MD -MP -MF samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Tpo -c -o samples/test_fused_gtest_test-sample1_unittest.obj `if test -f 'samples/sample1_unittest.cc'; then $(CYGPATH_W) 'samples/sample1_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/samples/sample1_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Tpo samples/$(DEPDIR)/test_fused_gtest_test-sample1_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='samples/sample1_unittest.cc' object='samples/test_fused_gtest_test-sample1_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_fused_gtest_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o samples/test_fused_gtest_test-sample1_unittest.obj `if test -f 'samples/sample1_unittest.cc'; then $(CYGPATH_W) 'samples/sample1_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/samples/sample1_unittest.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf lib/.libs lib/_libs + -rm -rf samples/.libs samples/_libs + -rm -rf src/.libs src/_libs + -rm -rf test/.libs test/_libs + +distclean-libtool: + -rm -f libtool config.lt +install-m4dataDATA: $(m4data_DATA) + @$(NORMAL_INSTALL) + test -z "$(m4datadir)" || $(MKDIR_P) "$(DESTDIR)$(m4datadir)" + @list='$(m4data_DATA)'; test -n "$(m4datadir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(m4datadir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(m4datadir)" || exit $$?; \ + done + +uninstall-m4dataDATA: + @$(NORMAL_UNINSTALL) + @list='$(m4data_DATA)'; test -n "$(m4datadir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(m4datadir)'; $(am__uninstall_files_from_dir) +install-pkgincludeHEADERS: $(pkginclude_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(pkgincludedir)" || $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \ + done + +uninstall-pkgincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir) +install-pkginclude_internalHEADERS: $(pkginclude_internal_HEADERS) + @$(NORMAL_INSTALL) + test -z "$(pkginclude_internaldir)" || $(MKDIR_P) "$(DESTDIR)$(pkginclude_internaldir)" + @list='$(pkginclude_internal_HEADERS)'; test -n "$(pkginclude_internaldir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginclude_internaldir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginclude_internaldir)" || exit $$?; \ + done + +uninstall-pkginclude_internalHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginclude_internal_HEADERS)'; test -n "$(pkginclude_internaldir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginclude_internaldir)'; $(am__uninstall_files_from_dir) + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__remove_distdir) + +dist-lzma: distdir + tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma + $(am__remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lzma*) \ + lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(m4datadir)" "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkginclude_internaldir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f fused-src/gtest/$(DEPDIR)/$(am__dirstamp) + -rm -f fused-src/gtest/$(am__dirstamp) + -rm -f lib/$(am__dirstamp) + -rm -f samples/$(DEPDIR)/$(am__dirstamp) + -rm -f samples/$(am__dirstamp) + -rm -f src/$(DEPDIR)/$(am__dirstamp) + -rm -f src/$(am__dirstamp) + -rm -f test/$(DEPDIR)/$(am__dirstamp) + -rm -f test/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +@HAVE_PYTHON_FALSE@maintainer-clean-local: +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf fused-src/gtest/$(DEPDIR) samples/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-data-local install-m4dataDATA \ + install-pkgincludeHEADERS install-pkginclude_internalHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-exec-local install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf fused-src/gtest/$(DEPDIR) samples/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic \ + maintainer-clean-local + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-m4dataDATA \ + uninstall-pkgincludeHEADERS \ + uninstall-pkginclude_internalHEADERS + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \ + clean clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES ctags dist dist-all \ + dist-bzip2 dist-gzip dist-lzip dist-lzma dist-shar dist-tarZ \ + dist-xz dist-zip distcheck distclean distclean-compile \ + distclean-generic distclean-hdr distclean-libtool \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-local install-dvi \ + install-dvi-am install-exec install-exec-am install-exec-local \ + install-html install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-m4dataDATA install-man \ + install-pdf install-pdf-am install-pkgincludeHEADERS \ + install-pkginclude_internalHEADERS install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-local mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-m4dataDATA uninstall-pkgincludeHEADERS \ + uninstall-pkginclude_internalHEADERS + + +# Build rules for putting fused Google Test files into the distribution +# package. The user can also create those files by manually running +# scripts/fuse_gtest_files.py. +@HAVE_PYTHON_TRUE@$(test_fused_gtest_test_SOURCES): fused-gtest + +@HAVE_PYTHON_TRUE@fused-gtest: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \ +@HAVE_PYTHON_TRUE@ $(GTEST_SRC) src/gtest-all.cc src/gtest_main.cc \ +@HAVE_PYTHON_TRUE@ scripts/fuse_gtest_files.py +@HAVE_PYTHON_TRUE@ mkdir -p "$(srcdir)/fused-src" +@HAVE_PYTHON_TRUE@ chmod -R u+w "$(srcdir)/fused-src" +@HAVE_PYTHON_TRUE@ rm -f "$(srcdir)/fused-src/gtest/gtest-all.cc" +@HAVE_PYTHON_TRUE@ rm -f "$(srcdir)/fused-src/gtest/gtest.h" +@HAVE_PYTHON_TRUE@ "$(srcdir)/scripts/fuse_gtest_files.py" "$(srcdir)/fused-src" +@HAVE_PYTHON_TRUE@ cp -f "$(srcdir)/src/gtest_main.cc" "$(srcdir)/fused-src/gtest/" + +@HAVE_PYTHON_TRUE@maintainer-clean-local: +@HAVE_PYTHON_TRUE@ rm -rf "$(srcdir)/fused-src" + +# Disables 'make install' as installing a compiled version of Google +# Test can lead to undefined behavior due to violation of the +# One-Definition Rule. + +install-exec-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system." + false + +install-data-local: + echo "'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system." + false + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/external/gtest/README b/external/gtest/README new file mode 100644 index 0000000000..26f35a8479 --- /dev/null +++ b/external/gtest/README @@ -0,0 +1,435 @@ +Google C++ Testing Framework +============================ + +http://code.google.com/p/googletest/ + +Overview +-------- + +Google's framework for writing C++ tests on a variety of platforms +(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the +xUnit architecture. Supports automatic test discovery, a rich set of +assertions, user-defined assertions, death tests, fatal and non-fatal +failures, various options for running the tests, and XML test report +generation. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Requirements for End Users +-------------------------- + +Google Test is designed to have fairly minimal requirements to build +and use with your projects, but there are some. Currently, we support +Linux, Windows, Mac OS X, and Cygwin. We will also make our best +effort to support other platforms (e.g. Solaris, AIX, and z/OS). +However, since core members of the Google Test project have no access +to these platforms, Google Test may have outstanding issues there. If +you notice any problems on your platform, please notify +googletestframework@googlegroups.com. Patches for fixing them are +even more welcome! + +### Linux Requirements ### + +These are the base requirements to build and use Google Test from a source +package (as described below): + * GNU-compatible Make or gmake + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98-standard-compliant compiler + +### Windows Requirements ### + + * Microsoft Visual C++ 7.1 or newer + +### Cygwin Requirements ### + + * Cygwin 1.5.25-14 or newer + +### Mac OS X Requirements ### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +Also, you'll need CMake 2.6.4 or higher if you want to build the +samples using the provided CMake script, regardless of the platform. + +Requirements for Contributors +----------------------------- + +We welcome patches. If you plan to contribute a patch, you need to +build Google Test and its own tests from an SVN checkout (described +below), which has further requirements: + + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + * CMake 2.6.4 or newer + +Getting the Source +------------------ + +There are two primary ways of getting Google Test's source code: you +can download a stable source release in your preferred archive format, +or directly check out the source from our Subversion (SVN) repositary. +The SVN checkout requires a few extra steps and some extra software +packages on your system, but lets you track the latest development and +make patches much more easily, so we highly encourage it. + +### Source Package ### + +Google Test is released in versioned source packages which can be +downloaded from the download page [1]. Several different archive +formats are provided, but the only difference is the tools used to +manipulate them, and the size of the resulting file. Download +whichever you are most comfortable with. + + [1] http://code.google.com/p/googletest/downloads/list + +Once the package is downloaded, expand it using whichever tools you +prefer for that type. This will result in a new directory with the +name "gtest-X.Y.Z" which contains all of the source code. Here are +some examples on Linux: + + tar -xvzf gtest-X.Y.Z.tar.gz + tar -xvjf gtest-X.Y.Z.tar.bz2 + unzip gtest-X.Y.Z.zip + +### SVN Checkout ### + +To check out the main branch (also known as the "trunk") of Google +Test, run the following Subversion command: + + svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + +Setting up the Build +-------------------- + +To build Google Test and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +### Generic Build Instructions ### + +Suppose you put Google Test in directory ${GTEST_DIR}. To build it, +create a library build target (or a project as called by Visual Studio +and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc + +with ${GTEST_DIR}/include in the system header search path and ${GTEST_DIR} +in the normal header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -pthread -c ${GTEST_DIR}/src/gtest-all.cc + ar -rv libgtest.a gtest-all.o + +(We need -pthread as Google Test uses threads.) + +Next, you should compile your test source file with +${GTEST_DIR}/include in the system header search path, and link it +with gtest and any other necessary libraries: + + g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ + -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Test on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Test's own tests. Instead, it just builds the Google Test library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GTEST_DIR}/make + make + ./sample1_unittest + +If you see errors, try to tweak the contents of make/Makefile to make +them go away. There are instructions in make/Makefile on how to do +it. + +### Using CMake ### + +Google Test comes with a CMake build script (CMakeLists.txt) that can +be used on a wide range of platforms ("C" stands for cross-platofrm.). +If you don't have CMake installed already, you can download it for +free from http://www.cmake.org/. + +CMake works by generating native makefiles or build projects that can +be used in the compiler environment of your choice. The typical +workflow starts with: + + mkdir mybuild # Create a directory to hold the build output. + cd mybuild + cmake ${GTEST_DIR} # Generate native build scripts. + +If you want to build Google Test's samples, you should replace the +last command with + + cmake -Dgtest_build_samples=ON ${GTEST_DIR} + +If you are on a *nix system, you should now see a Makefile in the +current directory. Just type 'make' to build gtest. + +If you use Windows and have Vistual Studio installed, a gtest.sln file +and several .vcproj files will be created. You can then build them +using Visual Studio. + +On Mac OS X with Xcode installed, a .xcodeproj file will be generated. + +### Legacy Build Scripts ### + +Before settling on CMake, we have been providing hand-maintained build +projects/scripts for Visual Studio, Xcode, and Autotools. While we +continue to provide them for convenience, they are not actively +maintained any more. We highly recommend that you follow the +instructions in the previous two sections to integrate Google Test +with your existing build system. + +If you still need to use the legacy build scripts, here's how: + +The msvc\ folder contains two solutions with Visual C++ projects. +Open the gtest.sln or gtest-md.sln file using Visual Studio, and you +are ready to build Google Test the same way you build any Visual +Studio project. Files that have names ending with -md use DLL +versions of Microsoft runtime libraries (the /MD or the /MDd compiler +option). Files without that suffix use static versions of the runtime +libraries (the /MT or the /MTd option). Please note that one must use +the same option to compile both gtest and the test code. If you use +Visual Studio 2005 or above, we recommend the -md version as /MD is +the default for new projects in these versions of Visual Studio. + +On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using +Xcode. Build the "gtest" target. The universal binary framework will +end up in your selected build directory (selected in the Xcode +"Preferences..." -> "Building" pane and defaults to xcode/build). +Alternatively, at the command line, enter: + + xcodebuild + +This will build the "Release" configuration of gtest.framework in your +default build location. See the "xcodebuild" man page for more +information about building different configurations and building in +different locations. + +If you wish to use the Google Test Xcode project with Xcode 4.x and +above, you need to either: + * update the SDK configuration options in xcode/Config/General.xconfig. + Comment options SDKROOT, MACOS_DEPLOYMENT_TARGET, and GCC_VERSION. If + you choose this route you lose the ability to target earlier versions + of MacOS X. + * Install an SDK for an earlier version. This doesn't appear to be + supported by Apple, but has been reported to work + (http://stackoverflow.com/questions/5378518). + +Tweaking Google Test +-------------------- + +Google Test can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Test by +defining control macros on the compiler command line. Generally, +these macros are named like GTEST_XYZ and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file include/gtest/internal/gtest-port.h. + +### Choosing a TR1 Tuple Library ### + +Some Google Test features require the C++ Technical Report 1 (TR1) +tuple library, which is not yet available with all compilers. The +good news is that Google Test implements a subset of TR1 tuple that's +enough for its own need, and will automatically use this when the +compiler doesn't provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +uses. However, if your project already uses TR1 tuple, you need to +tell Google Test to use the same TR1 tuple library the rest of your +project uses, or the two tuple implementations will clash. To do +that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test and your tests. If +you want to force Google Test to use its own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you don't want Google Test to use tuple at all, add + + -DGTEST_HAS_TR1_TUPLE=0 + +and all features using tuple will be disabled. + +### Multi-threaded Tests ### + +Google Test is thread-safe where the pthread library is available. +After #include "gtest/gtest.h", you can check the GTEST_IS_THREADSAFE +macro to see whether this is the case (yes if the macro is #defined to +1, no if it's undefined.). + +If Google Test doesn't correctly detect whether pthread is available +in your environment, you can force it with + + -DGTEST_HAS_PTHREAD=1 + +or + + -DGTEST_HAS_PTHREAD=0 + +When Google Test uses pthread, you may need to add flags to your +compiler and/or linker to select the pthread library, or you'll get +link errors. If you use the CMake script or the deprecated Autotools +script, this is taken care of for you. If you use your own build +script, you'll need to read your compiler and linker's manual to +figure out what flags to add. + +### As a Shared Library (DLL) ### + +Google Test is compact, so most users can build and link it as a +static library for the simplicity. You can choose to use Google Test +as a shared library (known as a DLL on Windows) if you prefer. + +To compile *gtest* as a shared library, add + + -DGTEST_CREATE_SHARED_LIBRARY=1 + +to the compiler flags. You'll also need to tell the linker to produce +a shared library instead - consult your linker's manual for how to do +it. + +To compile your *tests* that use the gtest shared library, add + + -DGTEST_LINKED_AS_SHARED_LIBRARY=1 + +to the compiler flags. + +Note: while the above steps aren't technically necessary today when +using some compilers (e.g. GCC), they may become necessary in the +future, if we decide to improve the speed of loading the library (see +http://gcc.gnu.org/wiki/Visibility for details). Therefore you are +recommended to always add the above flags when using Google Test as a +shared library. Otherwise a future release of Google Test may break +your build script. + +### Avoiding Macro Name Clashes ### + +In C++, macros don't obey namespaces. Therefore two libraries that +both define a macro of the same name will clash if you #include both +definitions. In case a Google Test macro clashes with another +library, you can force Google Test to rename its macro to avoid the +conflict. + +Specifically, if both Google Test and some other code define macro +FOO, you can add + + -DGTEST_DONT_DEFINE_FOO=1 + +to the compiler flags to tell Google Test to change the macro's name +from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST. +For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write + + GTEST_TEST(SomeTest, DoesThis) { ... } + +instead of + + TEST(SomeTest, DoesThis) { ... } + +in order to define a test. + +Upgrating from an Earlier Version +--------------------------------- + +We strive to keep Google Test releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Test. + +### Upgrading from 1.3.0 or Earlier ### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "Choosing a TR1 Tuple +Library". + +### Upgrading from 1.4.0 or Earlier ### + +The Autotools build script (configure + make) is no longer officially +supportted. You are encouraged to migrate to your own build system or +use CMake. If you still need to use Autotools, you can find +instructions in the README file from Google Test 1.4.0. + +On platforms where the pthread library is available, Google Test uses +it in order to be thread-safe. See the "Multi-threaded Tests" section +for what this means to your build script. + +If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google +Test will no longer compile. This should affect very few people, as a +large portion of STL (including ) doesn't compile in this mode +anyway. We decided to stop supporting it in order to greatly simplify +Google Test's implementation. + +Developing Google Test +---------------------- + +This section discusses how to make your own changes to Google Test. + +### Testing Google Test Itself ### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you can use CMake: + + mkdir mybuild + cd mybuild + cmake -Dgtest_build_tests=ON ${GTEST_DIR} + +Make sure you have Python installed, as some of Google Test's tests +are written in Python. If the cmake command complains about not being +able to find Python ("Could NOT find PythonInterp (missing: +PYTHON_EXECUTABLE)"), try telling it explicitly where your Python +executable can be found: + + cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR} + +Next, you can build Google Test and all of its own tests. On *nix, +this is usually done by 'make'. To run the tests, do + + make test + +All tests should pass. + +### Regenerating Source Files ### + +Some of Google Test's source files are generated from templates (not +in the C++ sense) using a script. A template file is named FOO.pump, +where FOO is the name of the file it will generate. For example, the +file include/gtest/internal/gtest-type-util.h.pump is used to generate +gtest-type-util.h in the same directory. + +Normally you don't need to worry about regenerating the source files, +unless you need to modify them. In that case, you should modify the +corresponding .pump files instead and run the pump.py Python script to +regenerate them. You can find pump.py in the scripts/ directory. +Read the Pump manual [2] for how to use it. + + [2] http://code.google.com/p/googletest/wiki/PumpManual + +### Contributing a Patch ### + +We welcome patches. Please read the Google Test developer's guide [3] +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + + [3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide + +Happy testing! diff --git a/external/gtest/aclocal.m4 b/external/gtest/aclocal.m4 new file mode 100644 index 0000000000..e7df9fe0ec --- /dev/null +++ b/external/gtest/aclocal.m4 @@ -0,0 +1,1198 @@ +# generated automatically by aclocal 1.11.3 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, +# Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.68],, +[m4_warning([this file was generated for autoconf 2.68. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically `autoreconf'.])]) + +# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software +# Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.11' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.11.3], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.11.3])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 9 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009, +# 2010, 2011 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 12 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], UPC, [depcc="$UPC" am_compiler_list=], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 5 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 16 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.62])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl +]) +_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl +dnl The `parallel-tests' driver may need to know about EXEEXT, so add the +dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro +dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl +]) + +dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation, +# Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 6 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation, +# Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# AM_PROG_MKDIR_P +# --------------- +# Check for `mkdir -p'. +AC_DEFUN([AM_PROG_MKDIR_P], +[AC_PREREQ([2.60])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, +dnl while keeping a definition of mkdir_p for backward compatibility. +dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. +dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of +dnl Makefile.ins that do not define MKDIR_P, so we do our own +dnl adjustment using top_builddir (which is defined more often than +dnl MKDIR_P). +AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl +case $mkdir_p in + [[\\/$]]* | ?:[[\\/]]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software +# Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 5 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, +# 2011 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# --------------------------------------------------------------------------- +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. +# +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. +# +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). +# +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 2.0 are not + dnl supported. (2.0 was released on October 16, 2000). + m4_define_default([_AM_PYTHON_INTERPRETER_LIST], +[python python2 python3 python3.2 python3.1 python3.0 python2.7 dnl + python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0]) + + AC_ARG_VAR([PYTHON], [the Python interpreter]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT(yes)], + [AC_MSG_ERROR(too old)]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [if test "x$prefix" = xNONE + then + am_py_prefix=$ac_default_prefix + else + am_py_prefix=$prefix + fi + am_cv_python_pythondir=`$PYTHON -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_python_lib(0,0,prefix='$am_py_prefix'))" 2>/dev/null` + case $am_cv_python_pythondir in + $am_py_prefix*) + am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'` + am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"` + ;; + *) + case $am_py_prefix in + /usr|/System*) ;; + *) + am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [if test "x$exec_prefix" = xNONE + then + am_py_exec_prefix=$am_py_prefix + else + am_py_exec_prefix=$exec_prefix + fi + am_cv_python_pyexecdir=`$PYTHON -c "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.get_python_lib(1,0,prefix='$am_py_exec_prefix'))" 2>/dev/null` + case $am_cv_python_pyexecdir in + $am_py_exec_prefix*) + am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'` + am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"` + ;; + *) + case $am_py_exec_prefix in + /usr|/System*) ;; + *) + am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages + ;; + esac + ;; + esac + ]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) + +# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 5 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/libtool.m4]) +m4_include([m4/ltoptions.m4]) +m4_include([m4/ltsugar.m4]) +m4_include([m4/ltversion.m4]) +m4_include([m4/lt~obsolete.m4]) diff --git a/external/gtest/build-aux/config.guess b/external/gtest/build-aux/config.guess new file mode 100644 index 0000000000..d622a44e55 --- /dev/null +++ b/external/gtest/build-aux/config.guess @@ -0,0 +1,1530 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011, 2012 Free Software Foundation, Inc. + +timestamp='2012-02-10' + +# 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 2 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. + + +# Originally written by Per Bothner. Please send patches (context +# diff format) to and include a ChangeLog +# entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD + +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 (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +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 + +# 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=`(/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 ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|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 + # 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/[-_].*/\./'` + ;; + 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}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${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 ;; + 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/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + 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 ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*: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-gnu`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 '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-gnu + 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="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${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-gnu + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-gnueabi + else + echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + LIBC=gnu + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + 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-gnu"; exit; } + ;; + or32:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-gnu + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + 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-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + 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 configury 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 ;; + 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 ;; + 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 + case $UNAME_PROCESSOR in + i386) + eval $set_cc_for_build + 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 + UNAME_PROCESSOR="x86_64" + fi + fi ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + 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 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/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/external/gtest/build-aux/config.h.in b/external/gtest/build-aux/config.h.in new file mode 100644 index 0000000000..843b5b10cb --- /dev/null +++ b/external/gtest/build-aux/config.h.in @@ -0,0 +1,69 @@ +/* build-aux/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION diff --git a/external/gtest/build-aux/config.sub b/external/gtest/build-aux/config.sub new file mode 100644 index 0000000000..c894da4550 --- /dev/null +++ b/external/gtest/build-aux/config.sub @@ -0,0 +1,1773 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +# 2011, 2012 Free Software Foundation, Inc. + +timestamp='2012-02-10' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# 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 2 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. + + +# Please send patches to . Submit a context +# diff and a properly formatted GNU ChangeLog entry. +# +# 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;hb=HEAD + +# 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 + $0 [OPTION] 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 (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +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-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### 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*) + 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 | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | be32 | be64 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | epiphany \ + | fido | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | 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 \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 \ + | ns16k | ns32k \ + | open8 \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | 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 + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | 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-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | 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-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \ + | 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-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | 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 + ;; + 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 + ;; + 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 + ;; + 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 + ;; + mingw32) + basic_machine=i386-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 + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i386-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 | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + 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* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -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* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -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*) + # 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 + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -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 + ;; + 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 + ;; + *-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/external/gtest/build-aux/depcomp b/external/gtest/build-aux/depcomp new file mode 100644 index 0000000000..bd0ac08958 --- /dev/null +++ b/external/gtest/build-aux/depcomp @@ -0,0 +1,688 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2011-12-04.11; # UTC + +# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010, +# 2011 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> "$depfile" + echo >> "$depfile" + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" + # Add `dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mechanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test "$stat" = 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/ \1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/ / + G + p +}' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/external/gtest/build-aux/install-sh b/external/gtest/build-aux/install-sh new file mode 100644 index 0000000000..a9244eb078 --- /dev/null +++ b/external/gtest/build-aux/install-sh @@ -0,0 +1,527 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2011-01-19.21; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit=${DOITPROG-} +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_glob='?' +initialize_posix_glob=' + test "$posix_glob" != "?" || { + if (set -f) 2>/dev/null; then + posix_glob= + else + posix_glob=: + fi + } +' + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +no_target_directory= + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) dst_arg=$2 + # Protect names problematic for `test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for `test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for `test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + eval "$initialize_posix_glob" + + oIFS=$IFS + IFS=/ + $posix_glob set -f + set fnord $dstdir + shift + $posix_glob set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + + eval "$initialize_posix_glob" && + $posix_glob set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + $posix_glob set +f && + + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/external/gtest/build-aux/ltmain.sh b/external/gtest/build-aux/ltmain.sh new file mode 100644 index 0000000000..c2852d8561 --- /dev/null +++ b/external/gtest/build-aux/ltmain.sh @@ -0,0 +1,9661 @@ + +# libtool (GNU libtool) 2.4.2 +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, +# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. +# +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --no-quiet, --no-silent +# print informational messages (default) +# --no-warn don't display warning messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print more informational messages than default +# --no-verbose don't print the extra informational messages +# --version print version information +# -h, --help, --help-all print short, long, or detailed help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. When passed as first option, +# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1ubuntu1 +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . +# GNU libtool home page: . +# General help using GNU software: . + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.2 Debian-2.4.2-1ubuntu1" +TIMESTAMP="" +package_revision=1.3337 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# NLS nuisances: We save the old values to restore during execute mode. +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done +LC_ALL=C +LANGUAGE=C +export LANGUAGE LC_ALL + +$lt_unset CDPATH + + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + + + +: ${CP="cp -f"} +test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} # func_dirname may be replaced by extended shell implementation + + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "${1}" | $SED "$basename"` +} # func_basename may be replaced by extended shell implementation + + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` +} # func_dirname_and_basename may be replaced by extended shell implementation + + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname may be replaced by extended shell implementation + + +# These SED scripts presuppose an absolute path with a trailing slash. +pathcar='s,^/\([^/]*\).*$,\1,' +pathcdr='s,^/[^/]*,,' +removedotparts=':dotsl + s@/\./@/@g + t dotsl + s,/\.$,/,' +collapseslashes='s@/\{1,\}@/@g' +finalslash='s,/*$,/,' + +# func_normal_abspath PATH +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +# value returned in "$func_normal_abspath_result" +func_normal_abspath () +{ + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` + while :; do + # Processed it all yet? + if test "$func_normal_abspath_tpath" = / ; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result" ; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + +# func_relative_path SRCDIR DSTDIR +# generates a relative path from SRCDIR to DSTDIR, with a trailing +# slash if non-empty, suitable for immediately appending a filename +# without needing to append a separator. +# value returned in "$func_relative_path_result" +func_relative_path () +{ + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=${func_dirname_result} + if test "x$func_relative_path_tlibdir" = x ; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test "x$func_stripname_result" != x ; then + func_relative_path_result=${func_relative_path_result}/${func_stripname_result} + fi + + # Normalisation. If bindir is libdir, return empty string, + # else relative path ending with a slash; either way, target + # file name can be directly appended. + if test ! -z "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result/" + func_relative_path_result=$func_stripname_result + fi +} + +# The name of this program: +func_dirname_and_basename "$progpath" +progname=$func_basename_result + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' + +# Sed substitution that converts a w32 file name or path +# which contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }$*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` + done + my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "$my_tmpdir" +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "$1" | $SED \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + +# func_tr_sh +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $opt_debug + + $SED -n '/(C)/!b go + :more + /\./!{ + N + s/\n# / / + b more + } + :go + /^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $opt_debug + + $SED -n '/^# Usage:/,/^# *.*--help/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + echo + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help [NOEXIT] +# Echo long help message to standard output and exit, +# unless 'noexit' is passed as argument. +func_help () +{ + $opt_debug + + $SED -n '/^# Usage:/,/# Report bugs to/ { + :print + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/ + p + d + } + /^# .* home page:/b print + /^# General help using/b print + ' < "$progpath" + ret=$? + if test -z "$1"; then + exit $ret + fi +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $opt_debug + + func_error "missing argument for $1." + exit_cmd=exit +} + + +# func_split_short_opt shortopt +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +func_split_short_opt () +{ + my_sed_short_opt='1s/^\(..\).*$/\1/;q' + my_sed_short_rest='1s/^..\(.*\)$/\1/;q' + + func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` + func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` +} # func_split_short_opt may be replaced by extended shell implementation + + +# func_split_long_opt longopt +# Set func_split_long_opt_name and func_split_long_opt_arg shell +# variables after splitting LONGOPT at the `=' sign. +func_split_long_opt () +{ + my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' + my_sed_long_arg='1s/^--[^=]*=//' + + func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` + func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` +} # func_split_long_opt may be replaced by extended shell implementation + +exit_cmd=: + + + + + +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" + +# Global variables. +nonopt= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "${1}=\$${1}\${2}" +} # func_append may be replaced by extended shell implementation + +# func_append_quoted var value +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +func_append_quoted () +{ + func_quote_for_eval "${2}" + eval "${1}=\$${1}\\ \$func_quote_for_eval_result" +} # func_append_quoted may be replaced by extended shell implementation + + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "${@}"` +} # func_arith may be replaced by extended shell implementation + + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` +} # func_len may be replaced by extended shell implementation + + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` +} # func_lo2o may be replaced by extended shell implementation + + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` +} # func_xform may be replaced by extended shell implementation + + +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + +# func_features +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test "$build_libtool_libs" = yes; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# Shorthand for --mode=foo, only valid as the first argument +case $1 in +clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; +compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; +execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; +finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; +install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; +link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; +uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; +esac + + + +# Option defaults: +opt_debug=: +opt_dry_run=false +opt_config=false +opt_preserve_dup_deps=false +opt_features=false +opt_finish=false +opt_help=false +opt_help_all=false +opt_silent=: +opt_warning=: +opt_verbose=: +opt_silent=false +opt_verbose=false + + +# Parse options once, thoroughly. This comes as soon as possible in the +# script to make things like `--version' happen as quickly as we can. +{ + # this just eases exit handling + while test $# -gt 0; do + opt="$1" + shift + case $opt in + --debug|-x) opt_debug='set -x' + func_echo "enabling shell trace mode" + $opt_debug + ;; + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + --config) + opt_config=: +func_config + ;; + --dlopen|-dlopen) + optarg="$1" + opt_dlopen="${opt_dlopen+$opt_dlopen +}$optarg" + shift + ;; + --preserve-dup-deps) + opt_preserve_dup_deps=: + ;; + --features) + opt_features=: +func_features + ;; + --finish) + opt_finish=: +set dummy --mode finish ${1+"$@"}; shift + ;; + --help) + opt_help=: + ;; + --help-all) + opt_help_all=: +opt_help=': help-all' + ;; + --mode) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_mode="$optarg" +case $optarg in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; +esac + shift + ;; + --no-silent|--no-quiet) + opt_silent=false +func_append preserve_args " $opt" + ;; + --no-warning|--no-warn) + opt_warning=false +func_append preserve_args " $opt" + ;; + --no-verbose) + opt_verbose=false +func_append preserve_args " $opt" + ;; + --silent|--quiet) + opt_silent=: +func_append preserve_args " $opt" + opt_verbose=false + ;; + --verbose|-v) + opt_verbose=: +func_append preserve_args " $opt" +opt_silent=false + ;; + --tag) + test $# = 0 && func_missing_arg $opt && break + optarg="$1" + opt_tag="$optarg" +func_append preserve_args " $opt $optarg" +func_enable_tag "$optarg" + shift + ;; + + -\?|-h) func_usage ;; + --help) func_help ;; + --version) func_version ;; + + # Separate optargs to long options: + --*=*) + func_split_long_opt "$opt" + set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-n*|-v*) + func_split_short_opt "$opt" + set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognized option \`$opt'" ;; + *) set dummy "$opt" ${1+"$@"}; shift; break ;; + esac + done + + # Validate options: + + # save first non-option argument + if test "$#" -gt 0; then + nonopt="$opt" + shift + fi + + # preserve --debug + test "$opt_debug" = : || func_append preserve_args " --debug" + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test "$opt_mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$opt_mode' for more information." + } + + + # Bail if the options were screwed + $exit_cmd $EXIT_FAILURE +} + + + + +## ----------- ## +## Main. ## +## ----------- ## + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case "$lt_sysroot:$1" in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result="=$func_stripname_result" + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T </dev/null` + if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$lt_sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $opt_debug + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result="" + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $opt_debug + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $opt_debug + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $opt_debug + if test -z "$2" && test -n "$1" ; then + func_error "Could not determine host file name corresponding to" + func_error " \`$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result="$1" + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $opt_debug + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " \`$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result="$3" + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $opt_debug + case $4 in + $1 ) func_to_host_path_result="$3$func_to_host_path_result" + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via `$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $opt_debug + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $opt_debug + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result="$1" +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result="$func_convert_core_msys_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $opt_debug + func_to_host_file_result="$1" + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result="$func_cygpath_result" + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via `$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $opt_debug + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd="func_convert_path_${func_stripname_result}" + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $opt_debug + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result="$1" +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $opt_debug + func_to_host_path_result="$1" + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result="$func_cygpath_result" + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_mode_compile arg... +func_mode_compile () +{ + $opt_debug + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify \`-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + func_append_quoted lastarg "$arg" + done + IFS="$save_ifs" + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with \`-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from \`$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test "$opt_mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode \`$opt_mode'" + ;; + esac + + echo + $ECHO "Try \`$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test "$opt_help" = :; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | sed -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + sed '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "\`$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument \`$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test "$opt_mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test "x$prev" = x-m && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$opt_mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename="" + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname" ; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename="$func_basename_result" + else + # no lafile. user explicitly requested -dlpreopen . + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename" ; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $opt_debug + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $opt_debug + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive which possess that section. Heuristic: eliminate + # all those which have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $opt_debug + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $opt_debug + if func_cygming_gnu_implib_p "$1" ; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1" ; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result="" + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + if test "$lock_old_archive_extraction" = yes; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test "$lock_old_archive_extraction" = yes; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +#else +# include +# include +# ifdef __CYGWIN__ +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +/* declarations of non-ANSI functions */ +#if defined(__MINGW32__) +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined(__CYGWIN__) +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined (other platforms) ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined(_MSC_VER) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +# ifndef _INTPTR_T_DEFINED +# define _INTPTR_T_DEFINED +# define intptr_t int +# endif +#elif defined(__MINGW32__) +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined(__CYGWIN__) +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined (other platforms) ... */ +#endif + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#if defined(LT_DEBUGWRAPPER) +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $opt_debug + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir="$arg" + prev= + continue + ;; + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps ; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + else + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test "$prefer_static_libs" = yes || + test "$prefer_static_libs,$installed" = "built,no"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib="$l" + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$lt_sysroot$libdir" + absdir="$lt_sysroot$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + case "$host" in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + echo + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$opt_mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$absdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$opt_mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system can not link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps ; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|qnx|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type \`$version_type'" + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + func_append verstring ":${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + func_append libobjs " $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$opt_mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test "X$deplibs_check_method" = "Xnone"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + # Remove ${wl} instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$opt_mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd1 in $cmds; do + IFS="$save_ifs" + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test "$try_normal_branch" = yes \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=${output_objdir}/${output_la}.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$opt_mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " ${wl}-bind_at_load" + func_append finalize_command " ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=no + ;; + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + func_append oldobjs " $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + func_resolve_sysroot "$deplib" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test "x$bindir" != x ; + then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$opt_mode" = link || test "$opt_mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=yes ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + odir="$objdir" + else + odir="$dir/$objdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$opt_mode" = uninstall && odir="$dir" + + # Remember odir for removal later, being careful to avoid duplicates + if test "$opt_mode" = clean; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case "$opt_mode" in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$opt_mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + func_append rmfiles " $odir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$opt_mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# vi:sw=2 + diff --git a/external/gtest/build-aux/missing b/external/gtest/build-aux/missing new file mode 100644 index 0000000000..86a8fc31e3 --- /dev/null +++ b/external/gtest/build-aux/missing @@ -0,0 +1,331 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2012-01-06.13; # UTC + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, +# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: +sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' +sed_minuso='s/.* -o \([^ ]*\).*/\1/p' + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case $1 in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + autom4te touch the output file, or create a stub one + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and +\`g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# normalize program name to check for. +program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). This is about non-GNU programs, so use $1 not +# $program. +case $1 in + lex*|yacc*) + # Not GNU programs, they don't have --version. + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case $program in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case $f in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te*) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison*|yacc*) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if test $# -ne 1; then + eval LASTARG=\${$#} + case $LASTARG in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if test ! -f y.tab.h; then + echo >y.tab.h + fi + if test ! -f y.tab.c; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex*|flex*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if test $# -ne 1; then + eval LASTARG=\${$#} + case $LASTARG in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if test ! -f lex.yy.c; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit $? + fi + ;; + + makeinfo*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n ' + /^@setfilename/{ + s/.* \([^ ]*\) *$/\1/ + p + q + }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/tests/gtest/cmake/internal_utils.cmake b/external/gtest/cmake/internal_utils.cmake similarity index 93% rename from tests/gtest/cmake/internal_utils.cmake rename to external/gtest/cmake/internal_utils.cmake index 7efc2ac797..8cb21894ce 100644 --- a/tests/gtest/cmake/internal_utils.cmake +++ b/external/gtest/cmake/internal_utils.cmake @@ -56,6 +56,16 @@ macro(config_compiler_and_linker) # Newlines inside flags variables break CMake's NMake generator. # TODO(vladl@google.com): Add -RTCs and -RTCu to debug builds. set(cxx_base_flags "-GS -W4 -WX -wd4127 -wd4251 -wd4275 -nologo -J -Zi") + if (MSVC_VERSION LESS 1400) + # Suppress spurious warnings MSVC 7.1 sometimes issues. + # Forcing value to bool. + set(cxx_base_flags "${cxx_base_flags} -wd4800") + # Copy constructor and assignment operator could not be generated. + set(cxx_base_flags "${cxx_base_flags} -wd4511 -wd4512") + # Compatibility warnings not applicable to Google Test. + # Resolved overload was found by argument-dependent lookup. + set(cxx_base_flags "${cxx_base_flags} -wd4675") + endif() set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") @@ -69,7 +79,8 @@ macro(config_compiler_and_linker) # whether RTTI is enabled. Therefore we define GTEST_HAS_RTTI # explicitly. set(cxx_no_rtti_flags "-fno-rtti -DGTEST_HAS_RTTI=0") - set(cxx_strict_flags "-Wextra") + set(cxx_strict_flags + "-Wextra -Wno-unused-parameter -Wno-missing-field-initializers") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") set(cxx_exception_flags "-features=except") # Sun Pro doesn't provide macros to indicate whether exceptions and diff --git a/external/gtest/codegear/gtest.cbproj b/external/gtest/codegear/gtest.cbproj new file mode 100644 index 0000000000..285bb2a87b --- /dev/null +++ b/external/gtest/codegear/gtest.cbproj @@ -0,0 +1,138 @@ + + + + {bca37a72-5b07-46cf-b44e-89f8e06451a2} + Release + + + true + + + true + true + Base + + + true + true + Base + + + true + lib + JPHNE + NO_STRICT + true + true + CppStaticLibrary + true + rtl.bpi;vcl.bpi;bcbie.bpi;vclx.bpi;vclactnband.bpi;xmlrtl.bpi;bcbsmp.bpi;dbrtl.bpi;vcldb.bpi;bdertl.bpi;vcldbx.bpi;dsnap.bpi;dsnapcon.bpi;vclib.bpi;ibxpress.bpi;adortl.bpi;dbxcds.bpi;dbexpress.bpi;DbxCommonDriver.bpi;websnap.bpi;vclie.bpi;webdsnap.bpi;inet.bpi;inetdbbde.bpi;inetdbxpress.bpi;soaprtl.bpi;Rave75VCL.bpi;teeUI.bpi;tee.bpi;teedb.bpi;IndyCore.bpi;IndySystem.bpi;IndyProtocols.bpi;IntrawebDB_90_100.bpi;Intraweb_90_100.bpi;dclZipForged11.bpi;vclZipForged11.bpi;GR32_BDS2006.bpi;GR32_DSGN_BDS2006.bpi;Jcl.bpi;JclVcl.bpi;JvCoreD11R.bpi;JvSystemD11R.bpi;JvStdCtrlsD11R.bpi;JvAppFrmD11R.bpi;JvBandsD11R.bpi;JvDBD11R.bpi;JvDlgsD11R.bpi;JvBDED11R.bpi;JvCmpD11R.bpi;JvCryptD11R.bpi;JvCtrlsD11R.bpi;JvCustomD11R.bpi;JvDockingD11R.bpi;JvDotNetCtrlsD11R.bpi;JvEDID11R.bpi;JvGlobusD11R.bpi;JvHMID11R.bpi;JvInterpreterD11R.bpi;JvJansD11R.bpi;JvManagedThreadsD11R.bpi;JvMMD11R.bpi;JvNetD11R.bpi;JvPageCompsD11R.bpi;JvPluginD11R.bpi;JvPrintPreviewD11R.bpi;JvRuntimeDesignD11R.bpi;JvTimeFrameworkD11R.bpi;JvValidatorsD11R.bpi;JvWizardD11R.bpi;JvXPCtrlsD11R.bpi;VclSmp.bpi;CExceptionExpert11.bpi + false + $(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;.. + rtl.lib;vcl.lib + 32 + $(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk + + + false + false + true + _DEBUG;$(Defines) + true + false + true + None + DEBUG + true + Debug + true + true + true + $(BDS)\lib\debug;$(ILINK_LibraryPath) + Full + true + + + NDEBUG;$(Defines) + Release + $(BDS)\lib\release;$(ILINK_LibraryPath) + None + + + CPlusPlusBuilder.Personality + CppStaticLibrary + +FalseFalse1000FalseFalseFalseFalseFalse103312521.0.0.01.0.0.0FalseFalseFalseTrueFalse + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + FalseTrueTrue3$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\src;..\include1$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk1NO_STRICT13216 + + + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + 0 + + + 1 + + + 2 + + + 9 + + + 10 + + + 11 + + + 12 + + + 14 + + + 13 + + + 15 + + + 16 + + + 17 + + + 18 + + + Cfg_1 + + + Cfg_2 + + + \ No newline at end of file diff --git a/external/gtest/codegear/gtest.groupproj b/external/gtest/codegear/gtest.groupproj new file mode 100644 index 0000000000..849f4c4b81 --- /dev/null +++ b/external/gtest/codegear/gtest.groupproj @@ -0,0 +1,54 @@ + + + {c1d923e0-6cba-4332-9b6f-3420acbf5091} + + + + + + + + + Default.Personality + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/gtest/codegear/gtest_all.cc b/external/gtest/codegear/gtest_all.cc new file mode 100644 index 0000000000..ba7ad68ad1 --- /dev/null +++ b/external/gtest/codegear/gtest_all.cc @@ -0,0 +1,38 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Josh Kelley (joshkel@gmail.com) +// +// Google C++ Testing Framework (Google Test) +// +// C++Builder's IDE cannot build a static library from files with hyphens +// in their name. See http://qc.codegear.com/wc/qcmain.aspx?d=70977 . +// This file serves as a workaround. + +#include "src/gtest-all.cc" diff --git a/external/gtest/codegear/gtest_link.cc b/external/gtest/codegear/gtest_link.cc new file mode 100644 index 0000000000..b955ebf2f9 --- /dev/null +++ b/external/gtest/codegear/gtest_link.cc @@ -0,0 +1,40 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Josh Kelley (joshkel@gmail.com) +// +// Google C++ Testing Framework (Google Test) +// +// Links gtest.lib and gtest_main.lib into the current project in C++Builder. +// This means that these libraries can't be renamed, but it's the only way to +// ensure that Debug versus Release test builds are linked against the +// appropriate Debug or Release build of the libraries. + +#pragma link "gtest.lib" +#pragma link "gtest_main.lib" diff --git a/external/gtest/codegear/gtest_main.cbproj b/external/gtest/codegear/gtest_main.cbproj new file mode 100644 index 0000000000..fae32cb29b --- /dev/null +++ b/external/gtest/codegear/gtest_main.cbproj @@ -0,0 +1,82 @@ + + + + {bca37a72-5b07-46cf-b44e-89f8e06451a2} + Release + + + true + + + true + true + Base + + + true + true + Base + + + true + lib + JPHNE + NO_STRICT + true + true + CppStaticLibrary + true + rtl.bpi;vcl.bpi;bcbie.bpi;vclx.bpi;vclactnband.bpi;xmlrtl.bpi;bcbsmp.bpi;dbrtl.bpi;vcldb.bpi;bdertl.bpi;vcldbx.bpi;dsnap.bpi;dsnapcon.bpi;vclib.bpi;ibxpress.bpi;adortl.bpi;dbxcds.bpi;dbexpress.bpi;DbxCommonDriver.bpi;websnap.bpi;vclie.bpi;webdsnap.bpi;inet.bpi;inetdbbde.bpi;inetdbxpress.bpi;soaprtl.bpi;Rave75VCL.bpi;teeUI.bpi;tee.bpi;teedb.bpi;IndyCore.bpi;IndySystem.bpi;IndyProtocols.bpi;IntrawebDB_90_100.bpi;Intraweb_90_100.bpi;dclZipForged11.bpi;vclZipForged11.bpi;GR32_BDS2006.bpi;GR32_DSGN_BDS2006.bpi;Jcl.bpi;JclVcl.bpi;JvCoreD11R.bpi;JvSystemD11R.bpi;JvStdCtrlsD11R.bpi;JvAppFrmD11R.bpi;JvBandsD11R.bpi;JvDBD11R.bpi;JvDlgsD11R.bpi;JvBDED11R.bpi;JvCmpD11R.bpi;JvCryptD11R.bpi;JvCtrlsD11R.bpi;JvCustomD11R.bpi;JvDockingD11R.bpi;JvDotNetCtrlsD11R.bpi;JvEDID11R.bpi;JvGlobusD11R.bpi;JvHMID11R.bpi;JvInterpreterD11R.bpi;JvJansD11R.bpi;JvManagedThreadsD11R.bpi;JvMMD11R.bpi;JvNetD11R.bpi;JvPageCompsD11R.bpi;JvPluginD11R.bpi;JvPrintPreviewD11R.bpi;JvRuntimeDesignD11R.bpi;JvTimeFrameworkD11R.bpi;JvValidatorsD11R.bpi;JvWizardD11R.bpi;JvXPCtrlsD11R.bpi;VclSmp.bpi;CExceptionExpert11.bpi + false + $(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;.. + rtl.lib;vcl.lib + 32 + $(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk + + + false + false + true + _DEBUG;$(Defines) + true + false + true + None + DEBUG + true + Debug + true + true + true + $(BDS)\lib\debug;$(ILINK_LibraryPath) + Full + true + + + NDEBUG;$(Defines) + Release + $(BDS)\lib\release;$(ILINK_LibraryPath) + None + + + CPlusPlusBuilder.Personality + CppStaticLibrary + +FalseFalse1000FalseFalseFalseFalseFalse103312521.0.0.01.0.0.0FalseFalseFalseTrueFalse + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + FalseTrueTrue3$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\include;..$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\src;..\src;..\include1$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk1NO_STRICT13216 + + + + + 0 + + + Cfg_1 + + + Cfg_2 + + + diff --git a/external/gtest/codegear/gtest_unittest.cbproj b/external/gtest/codegear/gtest_unittest.cbproj new file mode 100644 index 0000000000..33f7056346 --- /dev/null +++ b/external/gtest/codegear/gtest_unittest.cbproj @@ -0,0 +1,88 @@ + + + + {eea63393-5ac5-4b9c-8909-d75fef2daa41} + Release + + + true + + + true + true + Base + + + true + true + Base + + + exe + true + NO_STRICT + JPHNE + true + ..\test + true + CppConsoleApplication + true + true + rtl.bpi;vcl.bpi;bcbie.bpi;vclx.bpi;vclactnband.bpi;xmlrtl.bpi;bcbsmp.bpi;dbrtl.bpi;vcldb.bpi;bdertl.bpi;vcldbx.bpi;dsnap.bpi;dsnapcon.bpi;vclib.bpi;ibxpress.bpi;adortl.bpi;dbxcds.bpi;dbexpress.bpi;DbxCommonDriver.bpi;websnap.bpi;vclie.bpi;webdsnap.bpi;inet.bpi;inetdbbde.bpi;inetdbxpress.bpi;soaprtl.bpi;Rave75VCL.bpi;teeUI.bpi;tee.bpi;teedb.bpi;IndyCore.bpi;IndySystem.bpi;IndyProtocols.bpi;IntrawebDB_90_100.bpi;Intraweb_90_100.bpi;Jcl.bpi;JclVcl.bpi;JvCoreD11R.bpi;JvSystemD11R.bpi;JvStdCtrlsD11R.bpi;JvAppFrmD11R.bpi;JvBandsD11R.bpi;JvDBD11R.bpi;JvDlgsD11R.bpi;JvBDED11R.bpi;JvCmpD11R.bpi;JvCryptD11R.bpi;JvCtrlsD11R.bpi;JvCustomD11R.bpi;JvDockingD11R.bpi;JvDotNetCtrlsD11R.bpi;JvEDID11R.bpi;JvGlobusD11R.bpi;JvHMID11R.bpi;JvInterpreterD11R.bpi;JvJansD11R.bpi;JvManagedThreadsD11R.bpi;JvMMD11R.bpi;JvNetD11R.bpi;JvPageCompsD11R.bpi;JvPluginD11R.bpi;JvPrintPreviewD11R.bpi;JvRuntimeDesignD11R.bpi;JvTimeFrameworkD11R.bpi;JvValidatorsD11R.bpi;JvWizardD11R.bpi;JvXPCtrlsD11R.bpi;VclSmp.bpi + false + $(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\include;..\test;.. + $(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;..\test + true + + + false + false + true + _DEBUG;$(Defines) + true + false + true + None + DEBUG + true + Debug + true + true + true + $(BDS)\lib\debug;$(ILINK_LibraryPath) + Full + true + + + NDEBUG;$(Defines) + Release + $(BDS)\lib\release;$(ILINK_LibraryPath) + None + + + CPlusPlusBuilder.Personality + CppConsoleApplication + +FalseFalse1000FalseFalseFalseFalseFalse103312521.0.0.01.0.0.0FalseFalseFalseTrueFalse + + + CodeGear C++Builder Office 2000 Servers Package + CodeGear C++Builder Office XP Servers Package + FalseTrueTrue3$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\include;..\test;..$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\include;..\test$(BDS)\include;$(BDS)\include\dinkumware;$(BDS)\include\vcl;..\include1$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;..\test$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;..\test$(BDS)\lib;$(BDS)\lib\obj;$(BDS)\lib\psdk;$(OUTPUTDIR);..\test2NO_STRICTSTRICT + + + + + 0 + + + 1 + + + Cfg_1 + + + Cfg_2 + + + \ No newline at end of file diff --git a/external/gtest/configure b/external/gtest/configure new file mode 100644 index 0000000000..582a9a05f5 --- /dev/null +++ b/external/gtest/configure @@ -0,0 +1,18222 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.68 for Google C++ Testing Framework 1.7.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software +# Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + # Preserve -v and -x to the replacement shell. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; + esac + exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: googletestframework@googlegroups.com about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='Google C++ Testing Framework' +PACKAGE_TARNAME='gtest' +PACKAGE_VERSION='1.7.0' +PACKAGE_STRING='Google C++ Testing Framework 1.7.0' +PACKAGE_BUGREPORT='googletestframework@googlegroups.com' +PACKAGE_URL='' + +ac_unique_file="./LICENSE" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +HAVE_PTHREADS_FALSE +HAVE_PTHREADS_TRUE +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CC +acx_pthread_config +HAVE_PYTHON_FALSE +HAVE_PYTHON_TRUE +PYTHON +CXXCPP +CPP +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +RANLIB +ac_ct_AR +AR +DLLTOOL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +EGREP +GREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +ac_ct_CXX +CXXFLAGS +CXX +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +with_sysroot +enable_libtool_lock +with_pthreads +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CXX +CXXFLAGS +CCC +CPP +CXXCPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used" >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures Google C++ Testing Framework 1.7.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/gtest] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of Google C++ Testing Framework 1.7.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --enable-shared[=PKGS] build shared libraries [default=yes] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot=DIR Search for dependent libraries within DIR + (or the compiler's sysroot if not specified). + --with-pthreads use pthreads (default is yes) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CPP C preprocessor + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +Google C++ Testing Framework configure 1.7.0 +generated by GNU Autoconf 2.68 + +Copyright (C) 2010 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by Google C++ Testing Framework $as_me 1.7.0, which was +generated by GNU Autoconf 2.68. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Provide various options to initialize the Autoconf and configure processes. + + + +ac_aux_dir= +for ac_dir in build-aux "$srcdir"/build-aux; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +ac_config_headers="$ac_config_headers build-aux/config.h" + +ac_config_files="$ac_config_files Makefile" + +ac_config_files="$ac_config_files scripts/gtest-config" + + +# Initialize Automake with various options. We require at least v1.9, prevent +# pedantic complaints about package files, and enable various distribution +# targets. +am__api_version='1.11' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Just in case +sleep 1 +echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; +esac + +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken +alias in your environment" "$LINENO" 5 + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +mkdir_p="$MKDIR_P" +case $mkdir_p in + [\\/$]* | ?:[\\/]*) ;; + */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; +esac + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='gtest' + VERSION='1.7.0' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# Check for programs used in building Google Test. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.2' +macro_revision='1.3337' + + + + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case "$ECHO" in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } + + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test "$GCC" != yes; then + reload_cmds=false + fi + ;; + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. + if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5 +$as_echo "${with_sysroot}" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[012]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes +fi + + + + + + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + pic_mode=default +fi + + +test -z "$pic_mode" && pic_mode=default + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac +fi + +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='${wl}--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + link_all_deplibs=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test x"$lt_cv_prog_compiler__b" = xyes; then + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test "$lt_cv_irix_exported_symbol" = yes; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([A-Za-z]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +CC="$lt_save_CC" + + if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + export_dynamic_flag_spec_CXX='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX="/usr/lib:/lib" + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + gnu*) + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[89] | openbsd2.[89].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + +# TODO(chandlerc@google.com): Currently we aren't running the Python tests +# against the interpreter detected by AM_PATH_PYTHON, and so we condition +# HAVE_PYTHON by requiring "python" to be in the PATH, and that interpreter's +# version to be >= 2.3. This will allow the scripts to use a "/usr/bin/env" +# hashbang. +PYTHON= # We *do not* allow the user to specify a python interpreter +# Extract the first word of "python", so it can be a program name with args. +set dummy python; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PYTHON+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHON in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_PYTHON" && ac_cv_path_PYTHON=":" + ;; +esac +fi +PYTHON=$ac_cv_path_PYTHON +if test -n "$PYTHON"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 +$as_echo "$PYTHON" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +if test "$PYTHON" != ":"; then : + prog="import sys +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +# map returns an iterator in Python 3.0 and a list in 2.x +minver = list(map(int, '2.3'.split('.'))) + [0, 0, 0] +minverhex = 0 +# xrange is not present in Python 3.0 and range returns an iterator +for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i] +sys.exit(sys.hexversion < minverhex)" + if { echo "$as_me:$LINENO: $PYTHON -c "$prog"" >&5 + ($PYTHON -c "$prog") >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then : + : +else + PYTHON=":" +fi +fi + if test "$PYTHON" != ":"; then + HAVE_PYTHON_TRUE= + HAVE_PYTHON_FALSE='#' +else + HAVE_PYTHON_TRUE='#' + HAVE_PYTHON_FALSE= +fi + + +# Configure pthreads. + +# Check whether --with-pthreads was given. +if test "${with_pthreads+set}" = set; then : + withval=$with_pthreads; with_pthreads=$withval +else + with_pthreads=check +fi + + +have_pthreads=no +if test "x$with_pthreads" != "xno"; then : + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 +$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_join (); +int +main () +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + acx_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5 +$as_echo "$acx_pthread_ok" >&6; } + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +$as_echo_n "checking whether pthreads work without any flags... " >&6; } + ;; + + -*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 +$as_echo_n "checking whether pthreads work with $flag... " >&6; } + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_acx_pthread_config+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$acx_pthread_config"; then + ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_acx_pthread_config="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no" +fi +fi +acx_pthread_config=$ac_cv_prog_acx_pthread_config +if test -n "$acx_pthread_config"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5 +$as_echo "$acx_pthread_config" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 +$as_echo_n "checking for the pthreads library -l$flag... " >&6; } + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + acx_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5 +$as_echo "$acx_pthread_ok" >&6; } + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +$as_echo_n "checking for joinable pthread attribute... " >&6; } + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int attr=$attr; return attr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + attr_name=$attr; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 +$as_echo "$attr_name" >&6; } + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + +cat >>confdefs.h <<_ACEOF +#define PTHREAD_CREATE_JOINABLE $attr_name +_ACEOF + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 +$as_echo_n "checking if more special flags are required for pthreads... " >&6; } + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5 +$as_echo "${flag}" >&6; } + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + for ac_prog in xlc_r cc_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PTHREAD_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" + + else + PTHREAD_CC=$CC + fi + + # The next part tries to detect GCC inconsistency with -shared on some + # architectures and systems. The problem is that in certain + # configurations, when -shared is specified, GCC "forgets" to + # internally use various flags which are still necessary. + + # + # Prepare the flags + # + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_CC="$CC" + + # Try with the flags determined by the earlier checks. + # + # -Wl,-z,defs forces link-time symbol resolution, so that the + # linking checks with -shared actually have any value + # + # FIXME: -fPIC is required for -shared on many architectures, + # so we specify it here, but the right way would probably be to + # properly detect whether it is actually required. + CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CC="$PTHREAD_CC" + + # In order not to create several levels of indentation, we test + # the value of "$done" until we find the cure or run out of ideas. + done="no" + + # First, make sure the CFLAGS we added are actually accepted by our + # compiler. If not (and OS X's ld, for instance, does not accept -z), + # then we can't do this test. + if test x"$done" = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to check for GCC pthread/shared inconsistencies" >&5 +$as_echo_n "checking whether to check for GCC pthread/shared inconsistencies... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +else + done=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + if test "x$done" = xyes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + fi + fi + + if test x"$done" = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -pthread is sufficient with -shared" >&5 +$as_echo_n "checking whether -pthread is sufficient with -shared... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + done=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + if test "x$done" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + fi + + # + # Linux gcc on some architectures such as mips/mipsel forgets + # about -lpthread + # + if test x"$done" = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lpthread fixes that" >&5 +$as_echo_n "checking whether -lpthread fixes that... " >&6; } + LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + done=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + if test "x$done" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + fi + # + # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc + # + if test x"$done" = xno; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc_r fixes that" >&5 +$as_echo_n "checking whether -lc_r fixes that... " >&6; } + LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + done=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + if test "x$done" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + fi + if test x"$done" = xno; then + # OK, we have run out of ideas + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Impossible to determine how to use pthreads with shared libraries" >&5 +$as_echo "$as_me: WARNING: Impossible to determine how to use pthreads with shared libraries" >&2;} + + # so it's not safe to assume that we may use pthreads + acx_pthread_ok=no + fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + CC="$save_CC" +else + PTHREAD_CC="$CC" +fi + + + + + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h + + : +else + acx_pthread_ok=no + if test "x$with_pthreads" != "xcheck"; then : + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "--with-pthreads was specified, but unable to be used +See \`config.log' for more details" "$LINENO" 5; } +fi +fi +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + have_pthreads="$acx_pthread_ok" +fi + if test "x$have_pthreads" = "xyes"; then + HAVE_PTHREADS_TRUE= + HAVE_PTHREADS_FALSE='#' +else + HAVE_PTHREADS_TRUE='#' + HAVE_PTHREADS_FALSE= +fi + + + + +# TODO(chandlerc@google.com) Check for the necessary system headers. + +# TODO(chandlerc@google.com) Check the types, structures, and other compiler +# and architecture characteristics. + +# Output the generated files. No further autoconf macros may be used. +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_PYTHON_TRUE}" && test -z "${HAVE_PYTHON_FALSE}"; then + as_fn_error $? "conditional \"HAVE_PYTHON\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_PTHREADS_TRUE}" && test -z "${HAVE_PTHREADS_FALSE}"; then + as_fn_error $? "conditional \"HAVE_PTHREADS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by Google C++ Testing Framework $as_me 1.7.0, which was +generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +Google C++ Testing Framework config.status 1.7.0 +configured by $0, generated by GNU Autoconf 2.68, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2010 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +nm_file_list_spec \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' + + + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "build-aux/config.h") CONFIG_HEADERS="$CONFIG_HEADERS build-aux/config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "scripts/gtest-config") CONFIG_FILES="$CONFIG_FILES scripts/gtest-config" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "scripts/gtest-config":F) chmod +x scripts/gtest-config ;; + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +# The names of the tagged configurations supported by this script. +available_tags="CXX " + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and in which our libraries should be installed. +lt_sysroot=$lt_sysroot + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain="$ac_aux_dir/ltmain.sh" + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + if test x"$xsi_shell" = xyes; then + sed -e '/^func_dirname ()$/,/^} # func_dirname /c\ +func_dirname ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_basename ()$/,/^} # func_basename /c\ +func_basename ()\ +{\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\ +func_dirname_and_basename ()\ +{\ +\ case ${1} in\ +\ */*) func_dirname_result="${1%/*}${2}" ;;\ +\ * ) func_dirname_result="${3}" ;;\ +\ esac\ +\ func_basename_result="${1##*/}"\ +} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_stripname ()$/,/^} # func_stripname /c\ +func_stripname ()\ +{\ +\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\ +\ # positional parameters, so assign one to ordinary parameter first.\ +\ func_stripname_result=${3}\ +\ func_stripname_result=${func_stripname_result#"${1}"}\ +\ func_stripname_result=${func_stripname_result%"${2}"}\ +} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\ +func_split_long_opt ()\ +{\ +\ func_split_long_opt_name=${1%%=*}\ +\ func_split_long_opt_arg=${1#*=}\ +} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\ +func_split_short_opt ()\ +{\ +\ func_split_short_opt_arg=${1#??}\ +\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\ +} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\ +func_lo2o ()\ +{\ +\ case ${1} in\ +\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\ +\ *) func_lo2o_result=${1} ;;\ +\ esac\ +} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_xform ()$/,/^} # func_xform /c\ +func_xform ()\ +{\ + func_xform_result=${1%.*}.lo\ +} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_arith ()$/,/^} # func_arith /c\ +func_arith ()\ +{\ + func_arith_result=$(( $* ))\ +} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_len ()$/,/^} # func_len /c\ +func_len ()\ +{\ + func_len_result=${#1}\ +} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + +fi + +if test x"$lt_shell_append" = xyes; then + sed -e '/^func_append ()$/,/^} # func_append /c\ +func_append ()\ +{\ + eval "${1}+=\\${2}"\ +} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\ +func_append_quoted ()\ +{\ +\ func_quote_for_eval "${2}"\ +\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\ +} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: + + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5 +$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;} +fi + + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/external/gtest/configure.ac b/external/gtest/configure.ac new file mode 100644 index 0000000000..cc592e1583 --- /dev/null +++ b/external/gtest/configure.ac @@ -0,0 +1,68 @@ +m4_include(m4/acx_pthread.m4) + +# At this point, the Xcode project assumes the version string will be three +# integers separated by periods and surrounded by square brackets (e.g. +# "[1.0.1]"). It also asumes that there won't be any closing parenthesis +# between "AC_INIT(" and the closing ")" including comments and strings. +AC_INIT([Google C++ Testing Framework], + [1.7.0], + [googletestframework@googlegroups.com], + [gtest]) + +# Provide various options to initialize the Autoconf and configure processes. +AC_PREREQ([2.59]) +AC_CONFIG_SRCDIR([./LICENSE]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_HEADERS([build-aux/config.h]) +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([scripts/gtest-config], [chmod +x scripts/gtest-config]) + +# Initialize Automake with various options. We require at least v1.9, prevent +# pedantic complaints about package files, and enable various distribution +# targets. +AM_INIT_AUTOMAKE([1.9 dist-bzip2 dist-zip foreign subdir-objects]) + +# Check for programs used in building Google Test. +AC_PROG_CC +AC_PROG_CXX +AC_LANG([C++]) +AC_PROG_LIBTOOL + +# TODO(chandlerc@google.com): Currently we aren't running the Python tests +# against the interpreter detected by AM_PATH_PYTHON, and so we condition +# HAVE_PYTHON by requiring "python" to be in the PATH, and that interpreter's +# version to be >= 2.3. This will allow the scripts to use a "/usr/bin/env" +# hashbang. +PYTHON= # We *do not* allow the user to specify a python interpreter +AC_PATH_PROG([PYTHON],[python],[:]) +AS_IF([test "$PYTHON" != ":"], + [AM_PYTHON_CHECK_VERSION([$PYTHON],[2.3],[:],[PYTHON=":"])]) +AM_CONDITIONAL([HAVE_PYTHON],[test "$PYTHON" != ":"]) + +# Configure pthreads. +AC_ARG_WITH([pthreads], + [AS_HELP_STRING([--with-pthreads], + [use pthreads (default is yes)])], + [with_pthreads=$withval], + [with_pthreads=check]) + +have_pthreads=no +AS_IF([test "x$with_pthreads" != "xno"], + [ACX_PTHREAD( + [], + [AS_IF([test "x$with_pthreads" != "xcheck"], + [AC_MSG_FAILURE( + [--with-pthreads was specified, but unable to be used])])]) + have_pthreads="$acx_pthread_ok"]) +AM_CONDITIONAL([HAVE_PTHREADS],[test "x$have_pthreads" = "xyes"]) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_LIBS) + +# TODO(chandlerc@google.com) Check for the necessary system headers. + +# TODO(chandlerc@google.com) Check the types, structures, and other compiler +# and architecture characteristics. + +# Output the generated files. No further autoconf macros may be used. +AC_OUTPUT diff --git a/external/gtest/fused-src/gtest/gtest-all.cc b/external/gtest/fused-src/gtest/gtest-all.cc new file mode 100644 index 0000000000..a9a03b2e3b --- /dev/null +++ b/external/gtest/fused-src/gtest/gtest-all.cc @@ -0,0 +1,9592 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is +// part of Google Test's implementation; otherwise it's undefined. +#if !GTEST_IMPLEMENTATION_ +// A user is trying to include this from his code - just say no. +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include + + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool print_time_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() : caller_frame_(NULL) {} + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + +#if GTEST_HAS_PARAM_TEST + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } +#endif // GTEST_HAS_PARAM_TEST + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector test_case_indices_; + +#if GTEST_HAS_PARAM_TEST + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; +#endif // GTEST_HAS_PARAM_TEST + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +# if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +class AutoHandle { + public: + AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} + explicit AutoHandle(HANDLE handle) : handle_(handle) {} + + ~AutoHandle() { Reset(); } + + HANDLE Get() const { return handle_; } + void Reset() { Reset(INVALID_HANDLE_VALUE); } + void Reset(HANDLE handle) { + if (handle != handle_) { + if (handle_ != INVALID_HANDLE_VALUE) + ::CloseHandle(handle_); + handle_ = handle; + } + } + + private: + HANDLE handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; +# endif // GTEST_OS_WINDOWS + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise."); + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +// +// A user must call testing::InitGoogleTest() to initialize Google +// Test. g_init_gtest_count is set to the number of times +// InitGoogleTest() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +GTEST_API_ int g_init_gtest_count = 0; +static bool GTestIsInitialized() { return g_init_gtest_count != 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// Application pathname gotten in InitGoogleTest. +std::string g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return std::string(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return ""; + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return ""; +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + +# ifdef _MSC_VER + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +# pragma warning(pop) // Restores the warning state. +# else + + _ftime64(&now); + +# endif // _MSC_VER + + return static_cast(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(2 * (end - start)); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +#if GTEST_HAS_PARAM_TEST +void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors.GetString().c_str()); +} +#endif // GTEST_HAS_PARAM_TEST + +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { +#if GTEST_HAS_PARAM_TEST + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +#endif +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static std::string FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS + const bool use_color = false; +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_info.test_case_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_info.test_case_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << ms/1000.0; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + // Using non-reentrant version as localtime_r is not portable. + time_t seconds = static_cast(ms / 1000); +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe). + const struct tm* const time_struct = localtime(&seconds); // NOLINT +# pragma warning(pop) // Restores the warning state again. +#else + const struct tm* const time_struct = localtime(&seconds); // NOLINT +#endif + if (time_struct == NULL) + return ""; // Invalid ms value + + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct->tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct->tm_mday) + "T" + + String::FormatIntWidth2(time_struct->tm_hour) + ":" + + String::FormatIntWidth2(time_struct->tm_min) + ":" + + String::FormatIntWidth2(time_struct->tm_sec); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != NULL) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + + *stream << " \n"; + } + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0) + *stream << " />\n"; + else + *stream << " \n"; +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; + + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as an std::string. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; +} + +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +#if GTEST_HAS_PARAM_TEST +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} +#endif // GTEST_HAS_PARAM_TEST + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 + // (using this in initializer). + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +# pragma warning(pop) // Restores the warning state again. +#else + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +#endif // _MSC_VER + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), +#if GTEST_HAS_PARAM_TEST + parameterized_test_registry_(), + parameterized_tests_registered_(false), +#endif // GTEST_HAS_PARAM_TEST + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const std::string& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(test_case_name, + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // Makes sure InitGoogleTest() was called. + if (!GTestIsInitialized()) { + printf("%s", + "\nThis test program did NOT call ::testing::InitGoogleTest " + "before calling RUN_ALL_TESTS(). Please fix it.\n"); + return false; + } + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const std::string &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + &(current_test_info_->result_) : &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate an XML report in the given directory or with the given file\n" +" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + g_init_gtest_count++; + + // We don't want to run the initialization code twice. + if (g_init_gtest_count != 1) return; + + if (*argc <= 0) return; + + internal::g_executable_path = internal::StreamableToString(argv[0]); + +#if GTEST_HAS_DEATH_TEST + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#endif // GTEST_HAS_DEATH_TEST + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +} // namespace testing +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +#endif // GTEST_HAS_DEATH_TEST + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS +} + +# if !GTEST_OS_WINDOWS +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} +# else // We are not on Windows. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +# if !GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // !GTEST_OS_QNX + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy; + *result = (&dummy < ptr); +} + +bool StackGrowsDown() { + int dummy; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keith.ray@gmail.com (Keith Ray) + + +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kPathSeparatorString[] = "\\"; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + + +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include // For TerminateProcess() +#elif GTEST_OS_WINDOWS +# include +# include +#else +# include +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_QNX +# include +# include +#endif // GTEST_OS_QNX + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_MAC + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_MAC + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +#endif // _MSC_VER + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); + + // Returns the size (in bytes) of a file. + static size_t GetFileSize(FILE* file); + + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +// Returns the size (in bytes) of a file. +size_t CapturedStream::GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +std::string CapturedStream::ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector g_argvs; + +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. + +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include +#include +#include // NOLINT +#include + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast(c); + return kAsIs; + } else { + *os << "\\x" + String::FormatHexInt(static_cast(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a wchar_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo(wc, os); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << kQuoteBegin; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing Framework (Google Test) + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? message : + std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +// Verifies that registered_tests match the test names in +// defined_test_names_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef ::std::set::const_iterator DefinedTestIter; + registered_ = true; + + // Skip initial whitespace in registered_tests since some + // preprocessors prefix stringizied literals with whitespace. + registered_tests = SkipSpaces(registered_tests); + + Message errors; + ::std::set tests; + for (const char* names = registered_tests; names != NULL; + names = SkipComma(names)) { + const std::string name = GetPrefixUntilComma(names); + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (name == *it) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (tests.count(*it) == 0) { + errors << "You forgot to list test " << *it << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing diff --git a/external/gtest/fused-src/gtest/gtest.h b/external/gtest/fused-src/gtest/gtest.h new file mode 100644 index 0000000000..4f3804f703 --- /dev/null +++ b/external/gtest/fused-src/gtest/gtest.h @@ -0,0 +1,20061 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// The user can define the following macros in the build script to +// control Google Test's behavior. If the user doesn't define a macro +// in this list, Google Test will define it. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::string, which is different to std::string). +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::wstring, which is different to std::wstring). +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple +// is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google +// Test's own tr1 tuple implementation should be +// used. Unused when the user sets +// GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. + +// This header defines the following utilities: +// +// Macros indicating the current platform (defined to 1 if compiled on +// the given platform; otherwise undefined): +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_IOS_SIMULATOR - iOS simulator +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_SYMBIAN - Symbian +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// Note that it is possible that none of the GTEST_OS_* macros are defined. +// +// Macros indicating available Google Test features (defined to 1 if +// the corresponding feature is supported; otherwise undefined): +// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized +// tests) +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_PARAM_TEST - value-parameterized tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above two are mutually exclusive. +// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above +// synchronization primitives have real implementations +// and Google Test is thread-safe; or 0 otherwise. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like +// platforms, or a reduced regular exception syntax on +// other platforms, including Windows. +// +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +#elif defined __SYMBIAN32__ +# define GTEST_OS_SYMBIAN 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# if TARGET_IPHONE_SIMULATOR +# define GTEST_OS_IOS_SIMULATOR 1 +# endif +# endif +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#endif // __CYGWIN__ + +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if !GTEST_OS_WINDOWS +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include +# include +#elif !GTEST_OS_WINDOWS_MOBILE +# include +# include +#endif + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true iff Google Test can use POSIX regular expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif +#endif + +#if GTEST_HAS_POSIX_RE + +// On some platforms, needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included , which is guaranteed to define size_t through +// . +# include // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_HAS_POSIX_RE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#if !defined(GTEST_HAS_STD_STRING) +// Even though we don't use this macro any longer, we keep it in case +// some clients still depend on it. +# define GTEST_HAS_STD_STRING 1 +#elif !GTEST_HAS_STD_STRING +// The user told us that ::std::string isn't available. +# error "Google Test cannot be used where ::std::string isn't available." +#endif // !defined(GTEST_HAS_STD_STRING) + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +# define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// is available. + +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +# define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) + +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_GLOBAL_WSTRING +// The user didn't tell us whether ::wstring is available, so we need +// to figure it out. +# define GTEST_HAS_GLOBAL_WSTRING \ + (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we assume pthreads support is +// available on Linux and Mac. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \ + || GTEST_OS_QNX) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +# include // NOLINT + +// For timespec and nanosleep, used below. +# include // NOLINT +#endif + +// Determines whether Google Test can use tr1/tuple. You can define +// this macro to 0 to prevent Google Test from using tuple (any +// feature depending on tuple with be disabled in this mode). +#ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# else +// The user didn't tell us not to do it, so we assume it's OK. +# define GTEST_HAS_TR1_TUPLE 1 +# endif +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether Google Test's own tr1 tuple implementation +// should be used. +#ifndef GTEST_USE_OWN_TR1_TUPLE +// The user didn't tell us, so we need to figure it out. + +// We use our own TR1 tuple if we aren't sure the user has an +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif + +#endif // GTEST_USE_OWN_TR1_TUPLE + +// To avoid conditional compilation everywhere, we make it +// gtest-port.h's responsibility to #include the header implementing +// tr1/tuple. +#if GTEST_HAS_TR1_TUPLE + +# if GTEST_USE_OWN_TR1_TUPLE +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> +#define GTEST_1_TUPLE_(T) tuple +#define GTEST_2_TUPLE_(T) tuple +#define GTEST_3_TUPLE_(T) tuple +#define GTEST_4_TUPLE_(T) tuple +#define GTEST_5_TUPLE_(T) tuple +#define GTEST_6_TUPLE_(T) tuple +#define GTEST_7_TUPLE_(T) tuple +#define GTEST_8_TUPLE_(T) tuple +#define GTEST_9_TUPLE_(T) tuple +#define GTEST_10_TUPLE_(T) tuple + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. +#define GTEST_0_TYPENAMES_(T) +#define GTEST_1_TYPENAMES_(T) typename T##0 +#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 +#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 +#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3 +#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4 +#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5 +#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6 +#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 +#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8 +#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8, typename T##9 + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + +template +struct TupleElement { + typedef T0 type; +}; + +template +struct TupleElement { + typedef T1 type; +}; + +template +struct TupleElement { + typedef T2 type; +}; + +template +struct TupleElement { + typedef T3 type; +}; + +template +struct TupleElement { + typedef T4 type; +}; + +template +struct TupleElement { + typedef T5 type; +}; + +template +struct TupleElement { + typedef T6 type; +}; + +template +struct TupleElement { + typedef T7 type; +}; + +template +struct TupleElement { + typedef T8 type; +}; + +template +struct TupleElement { + typedef T9 type; +}; + +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + +template +class GTEST_1_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} + + tuple(const tuple& t) : f0_(t.f0_) {} + + template + tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_1_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { + f0_ = t.f0_; + return *this; + } + + T0 f0_; +}; + +template +class GTEST_2_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), + f1_(f1) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} + + template + tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_2_TUPLE_(U)& t) { + return CopyFrom(t); + } + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + return *this; + } + + T0 f0_; + T1 f1_; +}; + +template +class GTEST_3_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + template + tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_3_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; +}; + +template +class GTEST_4_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} + + template + tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_4_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; +}; + +template +class GTEST_5_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, + GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_) {} + + template + tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_5_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; +}; + +template +class GTEST_6_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_) {} + + template + tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_6_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; +}; + +template +class GTEST_7_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + template + tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_7_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; +}; + +template +class GTEST_8_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, + GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + template + tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_8_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; +}; + +template +class GTEST_9_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + template + tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_9_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; +}; + +template +class tuple { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), + f9_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} + + template + tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), + f9_(t.f9_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_10_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + f9_ = t.f9_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; + T9 f9_; +}; + +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +template +inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { + return GTEST_1_TUPLE_(T)(f0); +} + +template +inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { + return GTEST_2_TUPLE_(T)(f0, f1); +} + +template +inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { + return GTEST_3_TUPLE_(T)(f0, f1, f2); +} + +template +inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3) { + return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); +} + +template +inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4) { + return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); +} + +template +inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5) { + return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); +} + +template +inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6) { + return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); +} + +template +inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { + return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); +} + +template +inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8) { + return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); +} + +template +inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8, const T9& f9) { + return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); +} + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + +template +struct tuple_size { + static const int value = 0; +}; + +template +struct tuple_size { + static const int value = 1; +}; + +template +struct tuple_size { + static const int value = 2; +}; + +template +struct tuple_size { + static const int value = 3; +}; + +template +struct tuple_size { + static const int value = 4; +}; + +template +struct tuple_size { + static const int value = 5; +}; + +template +struct tuple_size { + static const int value = 6; +}; + +template +struct tuple_size { + static const int value = 7; +}; + +template +struct tuple_size { + static const int value = 8; +}; + +template +struct tuple_size { + static const int value = 9; +}; + +template +struct tuple_size { + static const int value = 10; +}; + +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + +template <> +class Get<0> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + Field(Tuple& t) { return t.f0_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + ConstField(const Tuple& t) { return t.f0_; } +}; + +template <> +class Get<1> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + Field(Tuple& t) { return t.f1_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + ConstField(const Tuple& t) { return t.f1_; } +}; + +template <> +class Get<2> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + Field(Tuple& t) { return t.f2_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + ConstField(const Tuple& t) { return t.f2_; } +}; + +template <> +class Get<3> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + Field(Tuple& t) { return t.f3_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + ConstField(const Tuple& t) { return t.f3_; } +}; + +template <> +class Get<4> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + Field(Tuple& t) { return t.f4_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + ConstField(const Tuple& t) { return t.f4_; } +}; + +template <> +class Get<5> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + Field(Tuple& t) { return t.f5_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + ConstField(const Tuple& t) { return t.f5_; } +}; + +template <> +class Get<6> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + Field(Tuple& t) { return t.f6_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + ConstField(const Tuple& t) { return t.f6_; } +}; + +template <> +class Get<7> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + Field(Tuple& t) { return t.f7_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + ConstField(const Tuple& t) { return t.f7_; } +}; + +template <> +class Get<8> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + Field(Tuple& t) { return t.f8_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + ConstField(const Tuple& t) { return t.f8_; } +}; + +template <> +class Get<9> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + Field(Tuple& t) { return t.f9_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + ConstField(const Tuple& t) { return t.f9_; } +}; + +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(const GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + +#undef GTEST_0_TUPLE_ +#undef GTEST_1_TUPLE_ +#undef GTEST_2_TUPLE_ +#undef GTEST_3_TUPLE_ +#undef GTEST_4_TUPLE_ +#undef GTEST_5_TUPLE_ +#undef GTEST_6_TUPLE_ +#undef GTEST_7_TUPLE_ +#undef GTEST_8_TUPLE_ +#undef GTEST_9_TUPLE_ +#undef GTEST_10_TUPLE_ + +#undef GTEST_0_TYPENAMES_ +#undef GTEST_1_TYPENAMES_ +#undef GTEST_2_TYPENAMES_ +#undef GTEST_3_TYPENAMES_ +#undef GTEST_4_TYPENAMES_ +#undef GTEST_5_TYPENAMES_ +#undef GTEST_6_TYPENAMES_ +#undef GTEST_7_TYPENAMES_ +#undef GTEST_8_TYPENAMES_ +#undef GTEST_9_TYPENAMES_ +#undef GTEST_10_TYPENAMES_ + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +# elif GTEST_ENV_HAS_STD_TUPLE_ +# include +// C++11 puts its tuple into the ::std namespace rather than +// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. +// This causes undefined behavior, but supported compilers react in +// the way we intend. +namespace std { +namespace tr1 { +using ::std::get; +using ::std::make_tuple; +using ::std::tuple; +using ::std::tuple_element; +using ::std::tuple_size; +} +} + +# elif GTEST_OS_SYMBIAN + +// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to +// use STLport's tuple implementation, which unfortunately doesn't +// work as the copy of STLport distributed with Symbian is incomplete. +// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to +// use its own tuple implementation. +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE + +// This prevents , which defines +// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include + +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +// GCC 4.0+ implements tr1/tuple in the header. This does +// not conform to the TR1 spec, which requires the header to be . + +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +// Until version 4.3.2, gcc has a bug that causes , +// which is #included by , to not compile when RTTI is +// disabled. _TR1_FUNCTIONAL is the header guard for +// . Hence the following #define is a hack to prevent +// from being included. +# define _TR1_FUNCTIONAL 1 +# include +# undef _TR1_FUNCTIONAL // Allows the user to #include + // if he chooses to. +# else +# include // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 + +# else +// If the compiler is not GCC 4.0+, we assume the user is using a +// spec-conforming TR1 implementation. +# include // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE + +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() is only available on ARM starting with Gingerbread. +# if defined(__arm__) && __ANDROID_API__ >= 9 +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// Google Test does not support death tests for VC 7.1 and earlier as +// abort() in a VC 7.1 application compiled as GUI in debug config +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX) +# define GTEST_HAS_DEATH_TEST 1 +# include // NOLINT +#endif + +// We don't support MSVC 7.1 with exceptions disabled now. Therefore +// all the compilers we care about are adequate for supporting +// value-parameterized tests. +#define GTEST_HAS_PARAM_TEST 1 + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether to support Combine(). This only makes sense when +// value-parameterized tests are enabled. The implementation doesn't +// work on Sun Studio since it doesn't understand templated conversion +// operators. +#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) +# define GTEST_HAS_COMBINE 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#else +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// A macro to disallow operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type)\ + void operator=(type const &) + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ + type(type const &);\ + GTEST_DISALLOW_ASSIGN_(type) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifdef _MSC_VER + +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif + +#endif // _MSC_VER + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +# define GTEST_HAS_CXXABI_H_ 1 +#else +# define GTEST_HAS_CXXABI_H_ 0 +#endif + +namespace testing { + +class Message; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template +struct CompileAssert { +}; + +#define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template +struct StaticAssertTypeEqHelper; + +template +struct StaticAssertTypeEqHelper {}; + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); +}; + +// Defines RE. + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + +#if GTEST_HAS_GLOBAL_STRING + + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + +#endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true iff regular expression re matches + // the entire str. + // PartialMatch(str, re) returns true iff regular expression re + // matches a substring of str (including str itself). + // + // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // when str contains NUL characters. + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#if GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const ::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#endif // GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. + const char* pattern_; + bool is_valid_; + +#if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +#endif + + GTEST_DISALLOW_ASSIGN_(RE); +}; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + const To to = NULL; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast(f) != NULL); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION + + +#if GTEST_HAS_DEATH_TEST + +const ::std::vector& GetInjectableArgvs(); +void SetInjectableArgvs(const ::std::vector* + new_argvs); + +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector g_argvs; + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +#if GTEST_HAS_PTHREAD + +// Sleeps for (roughly) n milli-seconds. This function is only for +// testing Google Test's own constructs. Don't use it in user tests, +// either directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, NULL); +} + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return NULL; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void (*UserThreadFunc)(T); + + ThreadWithParam( + UserThreadFunc func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); + finished_ = true; + } + } + + virtual void Run() { + if (thread_can_start_ != NULL) + thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + const UserThreadFunc func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true iff we know that the thread function has finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// MutexBase and Mutex implement mutex on pthreads-based platforms. They +// are used in conjunction with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end +// // of the current scope. +// +// MutexBase implements behavior for both statically and dynamically +// allocated mutexes. Do not use MutexBase directly. Instead, write +// the following to define a static mutex: +// +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// +// You can forward declare a static mutex like this: +// +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// To create a dynamic mutex, just define an object of type Mutex. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false } + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// An object managed for a thread by a ThreadLocal instance is deleted +// when the thread exits. Or, if the ThreadLocal instance dies in +// that thread, when the ThreadLocal dies. It's the user's +// responsibility to ensure that all other threads using a ThreadLocal +// have exited when it dies, or the per-thread objects for those +// threads will not be deleted. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal { + public: + ThreadLocal() : key_(CreateKey()), + default_() {} + explicit ThreadLocal(const T& value) : key_(CreateKey()), + default_(value) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != NULL) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = new ValueHolder(default_); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + const T default_; // The default value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# define GTEST_IS_THREADSAFE 1 + +#else // GTEST_HAS_PTHREAD + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +// The above synchronization primitives have dummy implementations. +// Therefore Google Test is not thread-safe. +# define GTEST_IS_THREADSAFE 0 + +#endif // GTEST_HAS_PTHREAD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +// Passing non-POD classes through ellipsis (...) crashes the ARM +// compiler and generates a warning in Sun Studio. The Nokia Symbian +// and the IBM XL C/C++ compiler try to instantiate a copy constructor +// for objects passed through ellipsis (...), failing for uncopyable +// objects. We define this to ensure that only POD is passed through +// ellipsis on these systems. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 +#else +# define GTEST_CAN_COMPARE_NULL 1 +#endif + +// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between +// const T& and const T* in a function template. These compilers +// _can_ decide between class template specializations for T and T*, +// so a tr1::type_traits-like is_pointer works. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) +# define GTEST_NEEDS_IS_POINTER_ 1 +#endif + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +template +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +// The biggest signed integer type the compiler supports. +typedef __int64 BiggestInt; +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int IsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int IsATTY(int /* fd */) { return 0; } +# else +inline int IsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int IsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +// Functions deprecated by MSVC 8.0. + +#ifdef _MSC_VER +// Temporarily disable warning 4996 (deprecated function). +# pragma warning(push) +# pragma warning(disable:4996) +#endif + +inline const char* StrNCpy(char* dest, const char* src, size_t n) { + return strncpy(dest, src, n); +} + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { + return fopen(path, mode); +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE + // We are on Windows CE, which has no environment variables. + return NULL; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != NULL && env[0] != '\0') ? env : NULL; +#else + return getenv(name); +#endif +} + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +void Abort(); +#else +inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#if GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) +#define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +// Thread annotations +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#if GTEST_OS_LINUX +# include +# include +# include +# include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#include +#include +#include +#include +#include +#include + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include + + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + +#if GTEST_OS_SYMBIAN + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } +#endif // GTEST_OS_SYMBIAN + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + +#if GTEST_OS_SYMBIAN + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + } + template + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; + } +#endif // GTEST_OS_SYMBIAN + + // We'll hold the text streamed to this object here. + const internal::scoped_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by . +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include + + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true iff the given string ends with the given suffix, ignoring + // case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in . +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true iff the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +// This file was GENERATED by command: +// pump.py gtest-type-util.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most 50 types in a list, and at most 50 +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// GetTypeName() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +# endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return name_str; +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; +template +struct Types2 { + typedef T1 Head; + typedef Types1 Tail; +}; + +template +struct Types3 { + typedef T1 Head; + typedef Types2 Tail; +}; + +template +struct Types4 { + typedef T1 Head; + typedef Types3 Tail; +}; + +template +struct Types5 { + typedef T1 Head; + typedef Types4 Tail; +}; + +template +struct Types6 { + typedef T1 Head; + typedef Types5 Tail; +}; + +template +struct Types7 { + typedef T1 Head; + typedef Types6 Tail; +}; + +template +struct Types8 { + typedef T1 Head; + typedef Types7 Tail; +}; + +template +struct Types9 { + typedef T1 Head; + typedef Types8 Tail; +}; + +template +struct Types10 { + typedef T1 Head; + typedef Types9 Tail; +}; + +template +struct Types11 { + typedef T1 Head; + typedef Types10 Tail; +}; + +template +struct Types12 { + typedef T1 Head; + typedef Types11 Tail; +}; + +template +struct Types13 { + typedef T1 Head; + typedef Types12 Tail; +}; + +template +struct Types14 { + typedef T1 Head; + typedef Types13 Tail; +}; + +template +struct Types15 { + typedef T1 Head; + typedef Types14 Tail; +}; + +template +struct Types16 { + typedef T1 Head; + typedef Types15 Tail; +}; + +template +struct Types17 { + typedef T1 Head; + typedef Types16 Tail; +}; + +template +struct Types18 { + typedef T1 Head; + typedef Types17 Tail; +}; + +template +struct Types19 { + typedef T1 Head; + typedef Types18 Tail; +}; + +template +struct Types20 { + typedef T1 Head; + typedef Types19 Tail; +}; + +template +struct Types21 { + typedef T1 Head; + typedef Types20 Tail; +}; + +template +struct Types22 { + typedef T1 Head; + typedef Types21 Tail; +}; + +template +struct Types23 { + typedef T1 Head; + typedef Types22 Tail; +}; + +template +struct Types24 { + typedef T1 Head; + typedef Types23 Tail; +}; + +template +struct Types25 { + typedef T1 Head; + typedef Types24 Tail; +}; + +template +struct Types26 { + typedef T1 Head; + typedef Types25 Tail; +}; + +template +struct Types27 { + typedef T1 Head; + typedef Types26 Tail; +}; + +template +struct Types28 { + typedef T1 Head; + typedef Types27 Tail; +}; + +template +struct Types29 { + typedef T1 Head; + typedef Types28 Tail; +}; + +template +struct Types30 { + typedef T1 Head; + typedef Types29 Tail; +}; + +template +struct Types31 { + typedef T1 Head; + typedef Types30 Tail; +}; + +template +struct Types32 { + typedef T1 Head; + typedef Types31 Tail; +}; + +template +struct Types33 { + typedef T1 Head; + typedef Types32 Tail; +}; + +template +struct Types34 { + typedef T1 Head; + typedef Types33 Tail; +}; + +template +struct Types35 { + typedef T1 Head; + typedef Types34 Tail; +}; + +template +struct Types36 { + typedef T1 Head; + typedef Types35 Tail; +}; + +template +struct Types37 { + typedef T1 Head; + typedef Types36 Tail; +}; + +template +struct Types38 { + typedef T1 Head; + typedef Types37 Tail; +}; + +template +struct Types39 { + typedef T1 Head; + typedef Types38 Tail; +}; + +template +struct Types40 { + typedef T1 Head; + typedef Types39 Tail; +}; + +template +struct Types41 { + typedef T1 Head; + typedef Types40 Tail; +}; + +template +struct Types42 { + typedef T1 Head; + typedef Types41 Tail; +}; + +template +struct Types43 { + typedef T1 Head; + typedef Types42 Tail; +}; + +template +struct Types44 { + typedef T1 Head; + typedef Types43 Tail; +}; + +template +struct Types45 { + typedef T1 Head; + typedef Types44 Tail; +}; + +template +struct Types46 { + typedef T1 Head; + typedef Types45 Tail; +}; + +template +struct Types47 { + typedef T1 Head; + typedef Types46 Tail; +}; + +template +struct Types48 { + typedef T1 Head; + typedef Types47 Tail; +}; + +template +struct Types49 { + typedef T1 Head; + typedef Types48 Tail; +}; + +template +struct Types50 { + typedef T1 Head; + typedef Types49 Tail; +}; + + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. +template +struct Types { + typedef internal::Types50 type; +}; + +template <> +struct Types { + typedef internal::Types0 type; +}; +template +struct Types { + typedef internal::Types1 type; +}; +template +struct Types { + typedef internal::Types2 type; +}; +template +struct Types { + typedef internal::Types3 type; +}; +template +struct Types { + typedef internal::Types4 type; +}; +template +struct Types { + typedef internal::Types5 type; +}; +template +struct Types { + typedef internal::Types6 type; +}; +template +struct Types { + typedef internal::Types7 type; +}; +template +struct Types { + typedef internal::Types8 type; +}; +template +struct Types { + typedef internal::Types9 type; +}; +template +struct Types { + typedef internal::Types10 type; +}; +template +struct Types { + typedef internal::Types11 type; +}; +template +struct Types { + typedef internal::Types12 type; +}; +template +struct Types { + typedef internal::Types13 type; +}; +template +struct Types { + typedef internal::Types14 type; +}; +template +struct Types { + typedef internal::Types15 type; +}; +template +struct Types { + typedef internal::Types16 type; +}; +template +struct Types { + typedef internal::Types17 type; +}; +template +struct Types { + typedef internal::Types18 type; +}; +template +struct Types { + typedef internal::Types19 type; +}; +template +struct Types { + typedef internal::Types20 type; +}; +template +struct Types { + typedef internal::Types21 type; +}; +template +struct Types { + typedef internal::Types22 type; +}; +template +struct Types { + typedef internal::Types23 type; +}; +template +struct Types { + typedef internal::Types24 type; +}; +template +struct Types { + typedef internal::Types25 type; +}; +template +struct Types { + typedef internal::Types26 type; +}; +template +struct Types { + typedef internal::Types27 type; +}; +template +struct Types { + typedef internal::Types28 type; +}; +template +struct Types { + typedef internal::Types29 type; +}; +template +struct Types { + typedef internal::Types30 type; +}; +template +struct Types { + typedef internal::Types31 type; +}; +template +struct Types { + typedef internal::Types32 type; +}; +template +struct Types { + typedef internal::Types33 type; +}; +template +struct Types { + typedef internal::Types34 type; +}; +template +struct Types { + typedef internal::Types35 type; +}; +template +struct Types { + typedef internal::Types36 type; +}; +template +struct Types { + typedef internal::Types37 type; +}; +template +struct Types { + typedef internal::Types38 type; +}; +template +struct Types { + typedef internal::Types39 type; +}; +template +struct Types { + typedef internal::Types40 type; +}; +template +struct Types { + typedef internal::Types41 type; +}; +template +struct Types { + typedef internal::Types42 type; +}; +template +struct Types { + typedef internal::Types43 type; +}; +template +struct Types { + typedef internal::Types44 type; +}; +template +struct Types { + typedef internal::Types45 type; +}; +template +struct Types { + typedef internal::Types46 type; +}; +template +struct Types { + typedef internal::Types47 type; +}; +template +struct Types { + typedef internal::Types48 type; +}; +template +struct Types { + typedef internal::Types49 type; +}; + +namespace internal { + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; +template +struct Templates2 { + typedef TemplateSel Head; + typedef Templates1 Tail; +}; + +template +struct Templates3 { + typedef TemplateSel Head; + typedef Templates2 Tail; +}; + +template +struct Templates4 { + typedef TemplateSel Head; + typedef Templates3 Tail; +}; + +template +struct Templates5 { + typedef TemplateSel Head; + typedef Templates4 Tail; +}; + +template +struct Templates6 { + typedef TemplateSel Head; + typedef Templates5 Tail; +}; + +template +struct Templates7 { + typedef TemplateSel Head; + typedef Templates6 Tail; +}; + +template +struct Templates8 { + typedef TemplateSel Head; + typedef Templates7 Tail; +}; + +template +struct Templates9 { + typedef TemplateSel Head; + typedef Templates8 Tail; +}; + +template +struct Templates10 { + typedef TemplateSel Head; + typedef Templates9 Tail; +}; + +template +struct Templates11 { + typedef TemplateSel Head; + typedef Templates10 Tail; +}; + +template +struct Templates12 { + typedef TemplateSel Head; + typedef Templates11 Tail; +}; + +template +struct Templates13 { + typedef TemplateSel Head; + typedef Templates12 Tail; +}; + +template +struct Templates14 { + typedef TemplateSel Head; + typedef Templates13 Tail; +}; + +template +struct Templates15 { + typedef TemplateSel Head; + typedef Templates14 Tail; +}; + +template +struct Templates16 { + typedef TemplateSel Head; + typedef Templates15 Tail; +}; + +template +struct Templates17 { + typedef TemplateSel Head; + typedef Templates16 Tail; +}; + +template +struct Templates18 { + typedef TemplateSel Head; + typedef Templates17 Tail; +}; + +template +struct Templates19 { + typedef TemplateSel Head; + typedef Templates18 Tail; +}; + +template +struct Templates20 { + typedef TemplateSel Head; + typedef Templates19 Tail; +}; + +template +struct Templates21 { + typedef TemplateSel Head; + typedef Templates20 Tail; +}; + +template +struct Templates22 { + typedef TemplateSel Head; + typedef Templates21 Tail; +}; + +template +struct Templates23 { + typedef TemplateSel Head; + typedef Templates22 Tail; +}; + +template +struct Templates24 { + typedef TemplateSel Head; + typedef Templates23 Tail; +}; + +template +struct Templates25 { + typedef TemplateSel Head; + typedef Templates24 Tail; +}; + +template +struct Templates26 { + typedef TemplateSel Head; + typedef Templates25 Tail; +}; + +template +struct Templates27 { + typedef TemplateSel Head; + typedef Templates26 Tail; +}; + +template +struct Templates28 { + typedef TemplateSel Head; + typedef Templates27 Tail; +}; + +template +struct Templates29 { + typedef TemplateSel Head; + typedef Templates28 Tail; +}; + +template +struct Templates30 { + typedef TemplateSel Head; + typedef Templates29 Tail; +}; + +template +struct Templates31 { + typedef TemplateSel Head; + typedef Templates30 Tail; +}; + +template +struct Templates32 { + typedef TemplateSel Head; + typedef Templates31 Tail; +}; + +template +struct Templates33 { + typedef TemplateSel Head; + typedef Templates32 Tail; +}; + +template +struct Templates34 { + typedef TemplateSel Head; + typedef Templates33 Tail; +}; + +template +struct Templates35 { + typedef TemplateSel Head; + typedef Templates34 Tail; +}; + +template +struct Templates36 { + typedef TemplateSel Head; + typedef Templates35 Tail; +}; + +template +struct Templates37 { + typedef TemplateSel Head; + typedef Templates36 Tail; +}; + +template +struct Templates38 { + typedef TemplateSel Head; + typedef Templates37 Tail; +}; + +template +struct Templates39 { + typedef TemplateSel Head; + typedef Templates38 Tail; +}; + +template +struct Templates40 { + typedef TemplateSel Head; + typedef Templates39 Tail; +}; + +template +struct Templates41 { + typedef TemplateSel Head; + typedef Templates40 Tail; +}; + +template +struct Templates42 { + typedef TemplateSel Head; + typedef Templates41 Tail; +}; + +template +struct Templates43 { + typedef TemplateSel Head; + typedef Templates42 Tail; +}; + +template +struct Templates44 { + typedef TemplateSel Head; + typedef Templates43 Tail; +}; + +template +struct Templates45 { + typedef TemplateSel Head; + typedef Templates44 Tail; +}; + +template +struct Templates46 { + typedef TemplateSel Head; + typedef Templates45 Tail; +}; + +template +struct Templates47 { + typedef TemplateSel Head; + typedef Templates46 Tail; +}; + +template +struct Templates48 { + typedef TemplateSel Head; + typedef Templates47 Tail; +}; + +template +struct Templates49 { + typedef TemplateSel Head; + typedef Templates48 Tail; +}; + +template +struct Templates50 { + typedef TemplateSel Head; + typedef Templates49 Tail; +}; + + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. +template +struct Templates { + typedef Templates50 type; +}; + +template <> +struct Templates { + typedef Templates0 type; +}; +template +struct Templates { + typedef Templates1 type; +}; +template +struct Templates { + typedef Templates2 type; +}; +template +struct Templates { + typedef Templates3 type; +}; +template +struct Templates { + typedef Templates4 type; +}; +template +struct Templates { + typedef Templates5 type; +}; +template +struct Templates { + typedef Templates6 type; +}; +template +struct Templates { + typedef Templates7 type; +}; +template +struct Templates { + typedef Templates8 type; +}; +template +struct Templates { + typedef Templates9 type; +}; +template +struct Templates { + typedef Templates10 type; +}; +template +struct Templates { + typedef Templates11 type; +}; +template +struct Templates { + typedef Templates12 type; +}; +template +struct Templates { + typedef Templates13 type; +}; +template +struct Templates { + typedef Templates14 type; +}; +template +struct Templates { + typedef Templates15 type; +}; +template +struct Templates { + typedef Templates16 type; +}; +template +struct Templates { + typedef Templates17 type; +}; +template +struct Templates { + typedef Templates18 type; +}; +template +struct Templates { + typedef Templates19 type; +}; +template +struct Templates { + typedef Templates20 type; +}; +template +struct Templates { + typedef Templates21 type; +}; +template +struct Templates { + typedef Templates22 type; +}; +template +struct Templates { + typedef Templates23 type; +}; +template +struct Templates { + typedef Templates24 type; +}; +template +struct Templates { + typedef Templates25 type; +}; +template +struct Templates { + typedef Templates26 type; +}; +template +struct Templates { + typedef Templates27 type; +}; +template +struct Templates { + typedef Templates28 type; +}; +template +struct Templates { + typedef Templates29 type; +}; +template +struct Templates { + typedef Templates30 type; +}; +template +struct Templates { + typedef Templates31 type; +}; +template +struct Templates { + typedef Templates32 type; +}; +template +struct Templates { + typedef Templates33 type; +}; +template +struct Templates { + typedef Templates34 type; +}; +template +struct Templates { + typedef Templates35 type; +}; +template +struct Templates { + typedef Templates36 type; +}; +template +struct Templates { + typedef Templates37 type; +}; +template +struct Templates { + typedef Templates38 type; +}; +template +struct Templates { + typedef Templates39 type; +}; +template +struct Templates { + typedef Templates40 type; +}; +template +struct Templates { + typedef Templates41 type; +}; +template +struct Templates { + typedef Templates42 type; +}; +template +struct Templates { + typedef Templates43 type; +}; +template +struct Templates { + typedef Templates44 type; +}; +template +struct Templates { + typedef Templates45 type; +}; +template +struct Templates { + typedef Templates46 type; +}; +template +struct Templates { + typedef Templates47 type; +}; +template +struct Templates { + typedef Templates48 type; +}; +template +struct Templates { + typedef Templates49 type; +}; + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + +template +struct TypeList > { + typedef typename Types::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +class ProtocolMessage; +namespace proto2 { class Message; } + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test cases. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// How many times InitGoogleTest() has been called. +GTEST_API_ extern int g_init_gtest_count; + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef GTEST_ELLIPSIS_NEEDS_POD_ +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_IS_NULL_LITERAL_(x) false +#else +# define GTEST_IS_NULL_LITERAL_(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // GTEST_ELLIPSIS_NEEDS_POD_ + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// A helper class for creating scoped traces in user programs. +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& message); + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + virtual Test* CreateTest() { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// State of the definition of a type-parameterized test case. +class GTEST_API_ TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + defined_test_names_.insert(test_name); + return true; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests); + + private: + bool registered_; + ::std::set defined_test_names_; +}; + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == NULL) { + return NULL; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == NULL ? str : std::string(str, comma); +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const char* case_name, + const char* test_names, int index) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" + + StreamableToString(index)).c_str(), + GetPrefixUntilComma(test_names).c_str(), + GetTypeName().c_str(), + NULL, // No value parameter. + GetTypeId(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest + ::Register(prefix, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/, int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase + ::Register(prefix, case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const UInt32 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Defining a variable of type CompileAssertTypesEqual will cause a +// compiler error iff T1 and T2 are different types. +template +struct CompileAssertTypesEqual; + +template +struct CompileAssertTypesEqual { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template +struct RemoveReference { typedef T type; }; // NOLINT +template +struct RemoveReference { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template +struct RemoveConst { typedef T type; }; // NOLINT +template +struct RemoveConst { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template +struct AddReference { typedef T& type; }; // NOLINT +template +struct AddReference { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference::type + +// Adds a reference to const on top of T as necessary. For example, +// it transforms +// +// char ==> const char& +// const char ==> const char& +// char& ==> const char& +// const char& ==> const char& +// +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static From MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4244) // Temporarily disables warning 4244. + + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +# pragma warning(pop) // Restores the warning state. +#elif defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +#endif // _MSV_VER +}; +template +const bool ImplicitlyConvertible::value; + +// IsAProtocolMessage::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible::value || + ImplicitlyConvertible::value> { +}; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// Note that we look for both C::iterator and C::const_iterator. The +// reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// EnableIf::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf::type* = 0" as the last parameter. +template struct EnableIf; +template<> struct EnableIf { typedef void type; }; // NOLINT + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. + NativeArray(const Element* array, size_t count, RelationToSource relation) { + Init(array, count, relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + static_cast(StaticAssertTypeEqHelper()); + if (relation_to_source_ == kCopy) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t a_size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + } + size_ = a_size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +// Suppresses MSVC warnings 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { statement; } + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + catch (...) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtest_msg.value) + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: it throws.") + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// represenation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + test_case_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, NULL, NULL, \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + + +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed, the regex is +// ignored, and the macro must accept a streamed message even though the message +// is never printed. +# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#else // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on +// systems that support death tests. This allows one to write such a macro +// on a system that does not support death tests and be sure that it will +// compile on a death-test supporting system. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter iff EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// +// TODO(wan@google.com): make thread-safe death tests search the PATH. + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + // No implementation - assignment is unsupported. + void operator=(const ExitedWithCode& other); + + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) +#endif + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + + +#if !GTEST_OS_SYMBIAN +# include +#endif + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include +#include +#include + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2003 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Dan Egnor (egnor@google.com) +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Bill Gibbons suggested we use something like this. +// +// Thread Safety: +// Unlike other linked_ptr implementations, in this implementation +// a linked_ptr object is thread-safe in the sense that: +// - it's safe to copy linked_ptr objects concurrently, +// - it's safe to copy *from* a linked_ptr and read its underlying +// raw pointer (e.g. via get()) concurrently, and +// - it's safe to write to two linked_ptrs that point to the same +// shared object concurrently. +// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// confusion with normal linked_ptr. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ + +#include +#include + + +namespace testing { +namespace internal { + +// Protects copying of all linked_ptr objects. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Many linked_ptr operations may change p.link_ for some linked_ptr + // variable p in the same circle as this object. Therefore we need + // to prevent two such operations from occurring concurrently. + // + // Note that different types of linked_ptr objects can coexist in a + // circle (e.g. linked_ptr, linked_ptr, and + // linked_ptr). Therefore we must use a single mutex to + // protect all linked_ptr objects. This can create serious + // contention in production code, but is acceptable in a testing + // framework. + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) p = p->next_; + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true if we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { // NOLINT + assert(&ptr != this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include // NOLINT +#include +#include +#include +#include + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) + kOtherType // anything else +}; + +// TypeWithoutFormatter::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(reinterpret_cast(&value), + sizeof(value), os); + } +}; + +// We print a protobuf using its ShortDebugString() when the string +// doesn't exceed this many characters; otherwise we print it using +// DebugString() for better readability. +const size_t kProtobufOneLinerMaxLength = 50; + +template +class TypeWithoutFormatter { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + const ::testing::internal::string short_str = value.ShortDebugString(); + const ::testing::internal::string pretty_str = + short_str.length() <= kProtobufOneLinerMaxLength ? + short_str : ("\n" + value.DebugString()); + *os << ("<" + pretty_str + ">"); + } +}; + +template +class TypeWithoutFormatter { + public: + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream, const Foo&) is more +// specific. +template +::std::basic_ostream& operator<<( + ::std::basic_ostream& os, const T& x) { + TypeWithoutFormatter::value ? kProtobuf : + internal::ImplicitlyConvertible::value ? + kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +template +void DefaultPrintTo(IsContainer /* dummy */, + false_type /* is not a pointer */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; +} + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +template +void DefaultPrintTo(IsNotContainer /* dummy */, + true_type /* is a pointer */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // C++ doesn't allow casting from a function pointer to any object + // pointer. + // + // IsTrue() silences warnings: "Condition is always true", + // "unreachable code". + if (IsTrue(ImplicitlyConvertible::value)) { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. However, we cannot cast it to const void* directly, + // even using reinterpret_cast, as earlier versions of gcc + // (e.g. 3.4.5) cannot compile the cast when p is a function + // pointer. Casting to UInt64 first solves the problem. + *os << reinterpret_cast( + reinterpret_cast(p)); + } + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template +void DefaultPrintTo(IsNotContainer /* dummy */, + false_type /* is not a pointer */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + // DefaultPrintTo() is overloaded. The type of its first two + // arguments determine which version will be picked. If T is an + // STL-style container, the version for container will be called; if + // T is a pointer, the pointer version will be called; otherwise the + // generic version will be called. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // The second argument of DefaultPrintTo() is needed to bypass a bug + // in Symbian's C++ compiler that prevents it from picking the right + // overload between: + // + // PrintTo(const T& x, ...); + // PrintTo(T* x, ...); + DefaultPrintTo(IsContainerTest(0), is_pointer(), value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os); + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo( + const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + // TODO(wan@google.com): let the user control the threshold using a flag. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +#if GTEST_HAS_TR1_TUPLE +typedef ::std::vector Strings; + +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter. + +// The inductive case. +template +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter::PrintPrefixTo(t, os); + *os << ", "; + UniversalPrinter::type> + ::Print(::std::tr1::get(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base cases. +template <> +struct TuplePrefixPrinter<0> { + template + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; +// We have to specialize the entire TuplePrefixPrinter<> class +// template here, even though the definition of +// TersePrintPrefixToStrings() is the same as the generic version, as +// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't +// support specializing a method template of a class template. +template <> +struct TuplePrefixPrinter<1> { + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + UniversalPrinter::type>:: + Print(::std::tr1::get<0>(t), os); + } + + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<0>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + PrintPrefixTo(t, os); + *os << ")"; +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Outputs a message explaining invalid registration of different +// fixture class for the same test case. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line); + +template class ParamGeneratorInterface; +template class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + scoped_ptr > impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + linked_ptr > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + virtual ~RangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, begin_, 0, step_); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + value_ = value_ + step_; + index_++; + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const T* Current() const { return &value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = i + step) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + virtual ~ValuesInIteratorRangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, container_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + ++iterator_; + value_.reset(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + virtual const T* Current() const { + if (value_.get() == NULL) + value_.reset(new T(*iterator_)); + return value_.get(); + } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of scoped_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable scoped_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + virtual Test* CreateTest() { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestCaseInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + typedef typename TestCase::ParamType ParamType; + + TestMetaFactory() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfoBase is a generic interface +// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestCaseRegistry class holds +// a collection of pointers to the ParameterizedTestCaseInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestCaseInfoBase { + public: + virtual ~ParameterizedTestCaseInfoBase() {} + + // Base part of test case name for display purposes. + virtual const string& GetTestCaseName() const = 0; + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test case right before running them in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestCaseInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test case and generators +// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that +// test case. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestCaseInstantiation(). + typedef typename TestCase::ParamType ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + + explicit ParameterizedTestCaseInfo(const char* name) + : test_case_name_(name) {} + + // Test case base name for display purposes. + virtual const string& GetTestCaseName() const { return test_case_name_; } + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_case_name is the base name of the test case (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test case base name and DoBar is test base name. + void AddTestPattern(const char* test_case_name, + const char* test_base_name, + TestMetaFactoryBase* meta_factory) { + tests_.push_back(linked_ptr(new TestInfo(test_case_name, + test_base_name, + meta_factory))); + } + // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestCaseInstantiation(const string& instantiation_name, + GeneratorCreationFunc* func, + const char* /* file */, + int /* line */) { + instantiations_.push_back(::std::make_pair(instantiation_name, func)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test case + // test cases right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more then once. + virtual void RegisterTests() { + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + linked_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const string& instantiation_name = gen_it->first; + ParamGenerator generator((*gen_it->second)()); + + string test_case_name; + if ( !instantiation_name.empty() ) + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; + + int i = 0; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + Message test_name_stream; + test_name_stream << test_info->test_base_name << "/" << i; + MakeAndRegisterTestInfo( + test_case_name.c_str(), + test_name_stream.GetString().c_str(), + NULL, // No type parameter. + PrintToString(*param_it).c_str(), + GetTestCaseTypeId(), + TestCase::SetUpTestCase, + TestCase::TearDownTestCase, + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_case_base_name, + const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory) : + test_case_base_name(a_test_case_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory) {} + + const string test_case_base_name; + const string test_base_name; + const scoped_ptr > test_meta_factory; + }; + typedef ::std::vector > TestInfoContainer; + // Keeps pairs of + // received from INSTANTIATE_TEST_CASE_P macros. + typedef ::std::vector > + InstantiationContainer; + + const string test_case_name_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); +}; // class ParameterizedTestCaseInfo + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase +// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P +// macros use it to locate their corresponding ParameterizedTestCaseInfo +// descriptors. +class ParameterizedTestCaseRegistry { + public: + ParameterizedTestCaseRegistry() {} + ~ParameterizedTestCaseRegistry() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + delete *it; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test case. + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, + const char* file, + int line) { + ParameterizedTestCaseInfo* typed_test_info = NULL; + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + if ((*it)->GetTestCaseName() == test_case_name) { + if ((*it)->GetTestCaseTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test case setup and tear-down in this case. + ReportInvalidTestCaseType(test_case_name, file, line); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestCaseInfo >(*it); + } + break; + } + } + if (typed_test_info == NULL) { + typed_test_info = new ParameterizedTestCaseInfo(test_case_name); + test_case_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + (*it)->RegisterTests(); + } + } + + private: + typedef ::std::vector TestCaseInfoContainer; + + TestCaseInfoContainer test_case_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most 50 arguments in Values, +// and at most 10 arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tr1::tuple which is +// currently set at 10. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template + operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +template +class ValueArray2 { + public: + ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray2& other); + + const T1 v1_; + const T2 v2_; +}; + +template +class ValueArray3 { + public: + ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray3& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; +}; + +template +class ValueArray4 { + public: + ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray4& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; +}; + +template +class ValueArray5 { + public: + ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray5& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; +}; + +template +class ValueArray6 { + public: + ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray6& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; +}; + +template +class ValueArray7 { + public: + ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray7& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; +}; + +template +class ValueArray8 { + public: + ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray8& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; +}; + +template +class ValueArray9 { + public: + ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray9& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; +}; + +template +class ValueArray10 { + public: + ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray10& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; +}; + +template +class ValueArray11 { + public: + ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray11& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; +}; + +template +class ValueArray12 { + public: + ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray12& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; +}; + +template +class ValueArray13 { + public: + ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray13& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; +}; + +template +class ValueArray14 { + public: + ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray14& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; +}; + +template +class ValueArray15 { + public: + ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray15& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; +}; + +template +class ValueArray16 { + public: + ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray16& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; +}; + +template +class ValueArray17 { + public: + ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray17& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; +}; + +template +class ValueArray18 { + public: + ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray18& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; +}; + +template +class ValueArray19 { + public: + ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray19& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; +}; + +template +class ValueArray20 { + public: + ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray20& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; +}; + +template +class ValueArray21 { + public: + ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray21& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; +}; + +template +class ValueArray22 { + public: + ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray22& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; +}; + +template +class ValueArray23 { + public: + ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray23& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; +}; + +template +class ValueArray24 { + public: + ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray24& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; +}; + +template +class ValueArray25 { + public: + ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray25& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; +}; + +template +class ValueArray26 { + public: + ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray26& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; +}; + +template +class ValueArray27 { + public: + ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray27& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; +}; + +template +class ValueArray28 { + public: + ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray28& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; +}; + +template +class ValueArray29 { + public: + ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray29& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; +}; + +template +class ValueArray30 { + public: + ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray30& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; +}; + +template +class ValueArray31 { + public: + ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray31& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; +}; + +template +class ValueArray32 { + public: + ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray32& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; +}; + +template +class ValueArray33 { + public: + ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray33& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; +}; + +template +class ValueArray34 { + public: + ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray34& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; +}; + +template +class ValueArray35 { + public: + ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray35& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; +}; + +template +class ValueArray36 { + public: + ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray36& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; +}; + +template +class ValueArray37 { + public: + ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray37& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; +}; + +template +class ValueArray38 { + public: + ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray38& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; +}; + +template +class ValueArray39 { + public: + ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray39& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; +}; + +template +class ValueArray40 { + public: + ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray40& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; +}; + +template +class ValueArray41 { + public: + ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray41& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; +}; + +template +class ValueArray42 { + public: + ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray42& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; +}; + +template +class ValueArray43 { + public: + ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), + v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray43& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; +}; + +template +class ValueArray44 { + public: + ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), + v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), + v43_(v43), v44_(v44) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray44& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; +}; + +template +class ValueArray45 { + public: + ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), + v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray45& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; +}; + +template +class ValueArray46 { + public: + ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray46& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; +}; + +template +class ValueArray47 { + public: + ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), + v47_(v47) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray47& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; +}; + +template +class ValueArray48 { + public: + ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), + v46_(v46), v47_(v47), v48_(v48) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray48& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; +}; + +template +class ValueArray49 { + public: + ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, + T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray49& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; +}; + +template +class ValueArray50 { + public: + ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, + T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray50& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; + const T50 v50_; +}; + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +template +class CartesianProductGenerator2 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator2(const ParamGenerator& g1, + const ParamGenerator& g2) + : g1_(g1), g2_(g2) {} + virtual ~CartesianProductGenerator2() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current2_; + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + ParamType current_value_; + }; // class CartesianProductGenerator2::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator2& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; +}; // class CartesianProductGenerator2 + + +template +class CartesianProductGenerator3 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator3(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + virtual ~CartesianProductGenerator3() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current3_; + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + ParamType current_value_; + }; // class CartesianProductGenerator3::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator3& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; +}; // class CartesianProductGenerator3 + + +template +class CartesianProductGenerator4 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator4(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + virtual ~CartesianProductGenerator4() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current4_; + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + ParamType current_value_; + }; // class CartesianProductGenerator4::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator4& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; +}; // class CartesianProductGenerator4 + + +template +class CartesianProductGenerator5 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator5(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + virtual ~CartesianProductGenerator5() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current5_; + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + ParamType current_value_; + }; // class CartesianProductGenerator5::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator5& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; +}; // class CartesianProductGenerator5 + + +template +class CartesianProductGenerator6 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator6(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + virtual ~CartesianProductGenerator6() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current6_; + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + ParamType current_value_; + }; // class CartesianProductGenerator6::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator6& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; +}; // class CartesianProductGenerator6 + + +template +class CartesianProductGenerator7 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator7(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + virtual ~CartesianProductGenerator7() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current7_; + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + ParamType current_value_; + }; // class CartesianProductGenerator7::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator7& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; +}; // class CartesianProductGenerator7 + + +template +class CartesianProductGenerator8 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator8(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + virtual ~CartesianProductGenerator8() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current8_; + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + ParamType current_value_; + }; // class CartesianProductGenerator8::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator8& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; +}; // class CartesianProductGenerator8 + + +template +class CartesianProductGenerator9 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator9(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + virtual ~CartesianProductGenerator9() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current9_; + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + ParamType current_value_; + }; // class CartesianProductGenerator9::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator9& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; +}; // class CartesianProductGenerator9 + + +template +class CartesianProductGenerator10 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator10(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9, + const ParamGenerator& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + virtual ~CartesianProductGenerator10() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end(), g10_, g10_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9, + const ParamGenerator& g10, + const typename ParamGenerator::iterator& current10) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9), + begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current10_; + if (current10_ == end10_) { + current10_ = begin10_; + ++current9_; + } + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_ && + current10_ == typed_other->current10_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_), + begin10_(other.begin10_), + end10_(other.end10_), + current10_(other.current10_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_, *current10_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_ || + current10_ == end10_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + const typename ParamGenerator::iterator begin10_; + const typename ParamGenerator::iterator end10_; + typename ParamGenerator::iterator current10_; + ParamType current_value_; + }; // class CartesianProductGenerator10::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator10& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; + const ParamGenerator g10_; +}; // class CartesianProductGenerator10 + + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +template +class CartesianProductHolder2 { + public: +CartesianProductHolder2(const Generator1& g1, const Generator2& g2) + : g1_(g1), g2_(g2) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator2( + static_cast >(g1_), + static_cast >(g2_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder2& other); + + const Generator1 g1_; + const Generator2 g2_; +}; // class CartesianProductHolder2 + +template +class CartesianProductHolder3 { + public: +CartesianProductHolder3(const Generator1& g1, const Generator2& g2, + const Generator3& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator3( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder3& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; +}; // class CartesianProductHolder3 + +template +class CartesianProductHolder4 { + public: +CartesianProductHolder4(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator4( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder4& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; +}; // class CartesianProductHolder4 + +template +class CartesianProductHolder5 { + public: +CartesianProductHolder5(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator5( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder5& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; +}; // class CartesianProductHolder5 + +template +class CartesianProductHolder6 { + public: +CartesianProductHolder6(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator6( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder6& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; +}; // class CartesianProductHolder6 + +template +class CartesianProductHolder7 { + public: +CartesianProductHolder7(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator7( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder7& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; +}; // class CartesianProductHolder7 + +template +class CartesianProductHolder8 { + public: +CartesianProductHolder8(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator8( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder8& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; +}; // class CartesianProductHolder8 + +template +class CartesianProductHolder9 { + public: +CartesianProductHolder9(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator9( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder9& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; +}; // class CartesianProductHolder9 + +template +class CartesianProductHolder10 { + public: +CartesianProductHolder10(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9, const Generator10& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator10( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_), + static_cast >(g10_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder10& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; + const Generator10 g10_; +}; // class CartesianProductHolder10 + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to 50 parameters. +// +template +internal::ValueArray1 Values(T1 v1) { + return internal::ValueArray1(v1); +} + +template +internal::ValueArray2 Values(T1 v1, T2 v2) { + return internal::ValueArray2(v1, v2); +} + +template +internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { + return internal::ValueArray3(v1, v2, v3); +} + +template +internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { + return internal::ValueArray4(v1, v2, v3, v4); +} + +template +internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5) { + return internal::ValueArray5(v1, v2, v3, v4, v5); +} + +template +internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6) { + return internal::ValueArray6(v1, v2, v3, v4, v5, v6); +} + +template +internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7) { + return internal::ValueArray7(v1, v2, v3, v4, v5, + v6, v7); +} + +template +internal::ValueArray8 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { + return internal::ValueArray8(v1, v2, v3, v4, + v5, v6, v7, v8); +} + +template +internal::ValueArray9 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { + return internal::ValueArray9(v1, v2, v3, + v4, v5, v6, v7, v8, v9); +} + +template +internal::ValueArray10 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { + return internal::ValueArray10(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10); +} + +template +internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) { + return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); +} + +template +internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) { + return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); +} + +template +internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) { + return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); +} + +template +internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { + return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14); +} + +template +internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { + return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); +} + +template +internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16) { + return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16); +} + +template +internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17) { + return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17); +} + +template +internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18) { + return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18); +} + +template +internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { + return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); +} + +template +internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { + return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); +} + +template +internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { + return internal::ValueArray21(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); +} + +template +internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22) { + return internal::ValueArray22(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22); +} + +template +internal::ValueArray23 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23) { + return internal::ValueArray23(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23); +} + +template +internal::ValueArray24 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24) { + return internal::ValueArray24(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24); +} + +template +internal::ValueArray25 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { + return internal::ValueArray25(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25); +} + +template +internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) { + return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); +} + +template +internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) { + return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); +} + +template +internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) { + return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28); +} + +template +internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) { + return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29); +} + +template +internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { + return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30); +} + +template +internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { + return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31); +} + +template +internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32) { + return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32); +} + +template +internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33) { + return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); +} + +template +internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34) { + return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); +} + +template +internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { + return internal::ValueArray35(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); +} + +template +internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { + return internal::ValueArray36(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36); +} + +template +internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37) { + return internal::ValueArray37(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37); +} + +template +internal::ValueArray38 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38) { + return internal::ValueArray38(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, + v33, v34, v35, v36, v37, v38); +} + +template +internal::ValueArray39 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38, T39 v39) { + return internal::ValueArray39(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, + v32, v33, v34, v35, v36, v37, v38, v39); +} + +template +internal::ValueArray40 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, + T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, + T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { + return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); +} + +template +internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { + return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); +} + +template +internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) { + return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, + v42); +} + +template +internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) { + return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, + v41, v42, v43); +} + +template +internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) { + return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v40, v41, v42, v43, v44); +} + +template +internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { + return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, + v39, v40, v41, v42, v43, v44, v45); +} + +template +internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { + return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46); +} + +template +internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { + return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); +} + +template +internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, + T48 v48) { + return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, + v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); +} + +template +internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, + T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, + T47 v47, T48 v48, T49 v49) { + return internal::ValueArray49(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, + v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); +} + +template +internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, + T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, + T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { + return internal::ValueArray50(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, + v48, v49, v50); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to 10 arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder2 Combine( + const Generator1& g1, const Generator2& g2) { + return internal::CartesianProductHolder2( + g1, g2); +} + +template +internal::CartesianProductHolder3 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3) { + return internal::CartesianProductHolder3( + g1, g2, g3); +} + +template +internal::CartesianProductHolder4 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4) { + return internal::CartesianProductHolder4( + g1, g2, g3, g4); +} + +template +internal::CartesianProductHolder5 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5) { + return internal::CartesianProductHolder5( + g1, g2, g3, g4, g5); +} + +template +internal::CartesianProductHolder6 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6) { + return internal::CartesianProductHolder6( + g1, g2, g3, g4, g5, g6); +} + +template +internal::CartesianProductHolder7 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7) { + return internal::CartesianProductHolder7( + g1, g2, g3, g4, g5, g6, g7); +} + +template +internal::CartesianProductHolder8 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8) { + return internal::CartesianProductHolder8( + g1, g2, g3, g4, g5, g6, g7, g8); +} + +template +internal::CartesianProductHolder9 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9) { + return internal::CartesianProductHolder9( + g1, g2, g3, g4, g5, g6, g7, g8, g9); +} + +template +internal::CartesianProductHolder10 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9, + const Generator10& g10) { + return internal::CartesianProductHolder10( + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); +} +# endif // GTEST_HAS_COMBINE + + + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ + ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + int gtest_##prefix##test_case_name##_dummy_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure // Failed and the test should be terminated. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, + const char* a_file_name, + int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == NULL ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) { + } + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? NULL : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != kSuccess; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + virtual ~HasNewFatalFailureHelper(); + virtual void ReportTestPartResult(const TestPartResult& result); + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test case, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_CASE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_CASE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test case as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + // Since we are inside a derived class template, C++ requires use to + // visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test case +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_CASE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test case as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test case name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_CASE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test case name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); + +#endif // 0 + + +// Implements typed tests. + +#if GTEST_HAS_TYPED_TEST + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test case. +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define TYPED_TEST_CASE(CaseName, Types) \ + typedef ::testing::internal::TypeList< Types >::type \ + GTEST_TYPE_PARAMS_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel< \ + GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ + GTEST_TYPE_PARAMS_(CaseName)>::Register(\ + "", #CaseName, #TestName, 0); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() + +#endif // GTEST_HAS_TYPED_TEST + +// Implements type-parameterized tests. + +#if GTEST_HAS_TYPED_TEST_P + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test case are defined in. The exact +// name of the namespace is subject to change without notice. +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ + gtest_case_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test case. +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ + gtest_typed_test_case_p_state_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test case. +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ + gtest_registered_test_names_##TestCaseName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +# define TYPED_TEST_CASE_P(CaseName) \ + static ::testing::internal::TypedTestCasePState \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) + +# define TYPED_TEST_P(CaseName, TestName) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + template \ + class TestName : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ + __FILE__, __LINE__, #CaseName, #TestName); \ + } \ + template \ + void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() + +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ + __FILE__, __LINE__, #__VA_ARGS__) + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase::type>::Register(\ + #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// Depending on the platform, different string classes are available. +// On Linux, in addition to ::std::string, Google also makes use of +// class ::string, which has the same interface as ::std::string, but +// has a different implementation. +// +// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// ::string is available AND is a distinct type to ::std::string, or +// define it to 0 to indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined +// heuristically. + +namespace testing { + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestCase; +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + // Used in the EXPECT_TRUE/FALSE(bool_expression). + explicit AssertionResult(bool success) : success_(success) {} + + // Returns true iff the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != NULL ? message_->c_str() : ""; + } + // TODO(vladl@google.com): Remove this after making sure no clients use it. + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + internal::scoped_ptr< ::std::string> message_; + + GTEST_DISALLOW_ASSIGN_(AssertionResult); +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true iff the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test part result among all the results. i can range + // from 0 to test_property_count() - 1. If i is not in that range, aborts + // the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestCase; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properites_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; + } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // For now, the XML report includes all tests matching the filter. + // In the future, we may trim tests that are excluded because of + // sharding. + return matches_filter_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestCase; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr value_param_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test case, which consists of a vector of TestInfos. +// +// TestCase is not copyable. +class GTEST_API_ TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestCase. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} + virtual void OnTestStart(const TestInfo& /*test_info*/) {} + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} + virtual void OnTestEnd(const TestInfo& /*test_info*/) {} + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestCase; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + +#if GTEST_HAS_PARAM_TEST + // Returns the ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); +#endif // GTEST_HAS_PARAM_TEST + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const; + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const; + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and funcions are friends as they need to access private + // members of UnitTest. + friend class Test; + friend class internal::AssertHelper; + friend class internal::ScopedTrace; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +namespace internal { + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4389) // Temporarily disables warning on + // signed/unsigned mismatch. +#endif + + if (expected == actual) { + return AssertionSuccess(); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal, like NULL, false, or 0. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf::value>::type* = 0) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* expected (NULL) */, + T* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast(NULL), actual); + } +}; + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +}\ +GTEST_API_ AssertionResult CmpHelper##op_name(\ + const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=); +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=); +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <); +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=); +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >); + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream expected_ss; + expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << expected; + + ::std::stringstream actual_ss; + actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StringStreamToString(&expected_ss), + StringStreamToString(&actual_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +#if GTEST_HAS_PARAM_TEST +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// virtual ~FooTest() { +// // Can use GetParam() here. +// } +// virtual void SetUp() { +// // Can use GetParam() here. +// } +// virtual void TearDown { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface::GetParam()' for a test that + // uses a fixture whose parameter type is int. + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +#endif // GTEST_HAS_PARAM_TEST + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles iff type1 and type2 are +// the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +bool StaticAssertTypeEq() { + (void)internal::StaticAssertTypeEqHelper(); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using this +// macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_case_name, test_name)\ + GTEST_TEST_(test_case_name, test_name, \ + ::testing::Test, ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/tests/gtest/src/gtest_main.cc b/external/gtest/fused-src/gtest/gtest_main.cc similarity index 95% rename from tests/gtest/src/gtest_main.cc rename to external/gtest/fused-src/gtest/gtest_main.cc index a09bbe0c6c..f302822552 100644 --- a/tests/gtest/src/gtest_main.cc +++ b/external/gtest/fused-src/gtest/gtest_main.cc @@ -27,13 +27,12 @@ // (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" GTEST_API_ int main(int argc, char **argv) { - std::cout << "Running main() from gtest_main.cc\n"; - + printf("Running main() from gtest_main.cc\n"); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/gtest/include/gtest/gtest-death-test.h b/external/gtest/include/gtest/gtest-death-test.h similarity index 94% rename from tests/gtest/include/gtest/gtest-death-test.h rename to external/gtest/include/gtest/gtest-death-test.h index a27883f0a4..957a69c6a9 100644 --- a/tests/gtest/include/gtest/gtest-death-test.h +++ b/external/gtest/include/gtest/gtest-death-test.h @@ -51,6 +51,17 @@ GTEST_DECLARE_string_(death_test_style); #if GTEST_HAS_DEATH_TEST +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + // The following macros are useful for writing death tests. // Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is @@ -75,7 +86,7 @@ GTEST_DECLARE_string_(death_test_style); // for (int i = 0; i < 5; i++) { // EXPECT_DEATH(server.ProcessRequest(i), // "Invalid request .* in ProcessRequest()") -// << "Failed to die on request " << i); +// << "Failed to die on request " << i; // } // // ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); @@ -245,10 +256,10 @@ class GTEST_API_ KilledBySignal { # ifdef NDEBUG # define EXPECT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) + GTEST_EXECUTE_STATEMENT_(statement, regex) # define ASSERT_DEBUG_DEATH(statement, regex) \ - do { statement; } while (::testing::internal::AlwaysFalse()) + GTEST_EXECUTE_STATEMENT_(statement, regex) # else diff --git a/tests/gtest/include/gtest/gtest-message.h b/external/gtest/include/gtest/gtest-message.h similarity index 77% rename from tests/gtest/include/gtest/gtest-message.h rename to external/gtest/include/gtest/gtest-message.h index 9b7142f320..fe879bca79 100644 --- a/tests/gtest/include/gtest/gtest-message.h +++ b/external/gtest/include/gtest/gtest-message.h @@ -48,8 +48,11 @@ #include -#include "gtest/internal/gtest-string.h" -#include "gtest/internal/gtest-internal.h" +#include "gtest/internal/gtest-port.h" + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); namespace testing { @@ -87,15 +90,7 @@ class GTEST_API_ Message { public: // Constructs an empty Message. - // We allocate the stringstream separately because otherwise each use of - // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's - // stack frame leading to huge stack frames in some cases; gcc does not reuse - // the stack space. - Message() : ss_(new ::std::stringstream) { - // By default, we want there to be enough precision when printing - // a double to a Message. - *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); - } + Message(); // Copy constructor. Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT @@ -118,7 +113,22 @@ class GTEST_API_ Message { // Streams a non-pointer value to this object. template inline Message& operator <<(const T& val) { - ::GTestStreamToHelper(ss_.get(), val); + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; return *this; } @@ -140,7 +150,7 @@ class GTEST_API_ Message { if (pointer == NULL) { *ss_ << "(null)"; } else { - ::GTestStreamToHelper(ss_.get(), pointer); + *ss_ << pointer; } return *this; } @@ -164,12 +174,8 @@ class GTEST_API_ Message { // These two overloads allow streaming a wide C string to a Message // using the UTF-8 encoding. - Message& operator <<(const wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } - Message& operator <<(wchar_t* wide_c_str) { - return *this << internal::String::ShowWideCString(wide_c_str); - } + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); #if GTEST_HAS_STD_WSTRING // Converts the given wide string to a narrow string using the UTF-8 @@ -183,13 +189,11 @@ class GTEST_API_ Message { Message& operator <<(const ::wstring& wstr); #endif // GTEST_HAS_GLOBAL_WSTRING - // Gets the text streamed to this object so far as a String. + // Gets the text streamed to this object so far as an std::string. // Each '\0' character in the buffer is replaced with "\\0". // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::String GetString() const { - return internal::StringStreamToString(ss_.get()); - } + std::string GetString() const; private: @@ -199,16 +203,20 @@ class GTEST_API_ Message { // decide between class template specializations for T and T*, so a // tr1::type_traits-like is_pointer works, and we can overload on that. template - inline void StreamHelper(internal::true_type /*dummy*/, T* pointer) { + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { if (pointer == NULL) { *ss_ << "(null)"; } else { - ::GTestStreamToHelper(ss_.get(), pointer); + *ss_ << pointer; } } template - inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { - ::GTestStreamToHelper(ss_.get(), value); + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; } #endif // GTEST_OS_SYMBIAN @@ -225,6 +233,18 @@ inline std::ostream& operator <<(std::ostream& os, const Message& sb) { return os << sb.GetString(); } +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal } // namespace testing #endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/tests/gtest/include/gtest/gtest-param-test.h b/external/gtest/include/gtest/gtest-param-test.h similarity index 99% rename from tests/gtest/include/gtest/gtest-param-test.h rename to external/gtest/include/gtest/gtest-param-test.h index 6407cfd685..d6702c8f16 100644 --- a/tests/gtest/include/gtest/gtest-param-test.h +++ b/external/gtest/include/gtest/gtest-param-test.h @@ -1257,7 +1257,7 @@ inline internal::ParamGenerator Bool() { // Boolean flags: // // class FlagDependentTest -// : public testing::TestWithParam > { +// : public testing::TestWithParam > { // virtual void SetUp() { // // Assigns external_flag_1 and external_flag_2 values from the tuple. // tie(external_flag_1, external_flag_2) = GetParam(); diff --git a/tests/gtest/include/gtest/gtest-param-test.h.pump b/external/gtest/include/gtest/gtest-param-test.h.pump similarity index 99% rename from tests/gtest/include/gtest/gtest-param-test.h.pump rename to external/gtest/include/gtest/gtest-param-test.h.pump index 401cb513a8..2dc9303b5e 100644 --- a/tests/gtest/include/gtest/gtest-param-test.h.pump +++ b/external/gtest/include/gtest/gtest-param-test.h.pump @@ -414,7 +414,7 @@ inline internal::ParamGenerator Bool() { // Boolean flags: // // class FlagDependentTest -// : public testing::TestWithParam > { +// : public testing::TestWithParam > { // virtual void SetUp() { // // Assigns external_flag_1 and external_flag_2 values from the tuple. // tie(external_flag_1, external_flag_2) = GetParam(); diff --git a/tests/gtest/include/gtest/gtest-printers.h b/external/gtest/include/gtest/gtest-printers.h similarity index 93% rename from tests/gtest/include/gtest/gtest-printers.h rename to external/gtest/include/gtest/gtest-printers.h index 9cbab3ff4b..0639d9f586 100644 --- a/tests/gtest/include/gtest/gtest-printers.h +++ b/external/gtest/include/gtest/gtest-printers.h @@ -630,9 +630,12 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { } } // This overload prints a (const) char array compactly. -GTEST_API_ void UniversalPrintArray(const char* begin, - size_t len, - ::std::ostream* os); +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); // Implements printing an array type T[N]. template @@ -673,19 +676,72 @@ class UniversalPrinter { // Prints a value tersely: for a reference type, the referenced value // (but not the address) is printed; for a (const) char pointer, the // NUL-terminated string (but not the pointer) is printed. + template -void UniversalTersePrint(const T& value, ::std::ostream* os) { - UniversalPrint(value, os); -} -inline void UniversalTersePrint(const char* str, ::std::ostream* os) { - if (str == NULL) { - *os << "NULL"; - } else { - UniversalPrint(string(str), os); +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); } -} -inline void UniversalTersePrint(char* str, ::std::ostream* os) { - UniversalTersePrint(static_cast(str), os); +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); } // Prints a value using the type inferred by the compiler. The @@ -694,7 +750,10 @@ inline void UniversalTersePrint(char* str, ::std::ostream* os) { // NUL-terminated string. template void UniversalPrint(const T& value, ::std::ostream* os) { - UniversalPrinter::Print(value, os); + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); } #if GTEST_HAS_TR1_TUPLE @@ -787,7 +846,7 @@ Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { template ::std::string PrintToString(const T& value) { ::std::stringstream ss; - internal::UniversalTersePrint(value, &ss); + internal::UniversalTersePrinter::Print(value, &ss); return ss.str(); } diff --git a/tests/gtest/include/gtest/gtest-spi.h b/external/gtest/include/gtest/gtest-spi.h similarity index 99% rename from tests/gtest/include/gtest/gtest-spi.h rename to external/gtest/include/gtest/gtest-spi.h index b226e55048..f63fa9a1b2 100644 --- a/tests/gtest/include/gtest/gtest-spi.h +++ b/external/gtest/include/gtest/gtest-spi.h @@ -223,7 +223,7 @@ class GTEST_API_ SingleFailureChecker { (substr));\ {\ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ - ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ >est_failures);\ if (::testing::internal::AlwaysTrue()) { statement; }\ }\ diff --git a/tests/gtest/include/gtest/gtest-test-part.h b/external/gtest/include/gtest/gtest-test-part.h similarity index 94% rename from tests/gtest/include/gtest/gtest-test-part.h rename to external/gtest/include/gtest/gtest-test-part.h index 8aeea14984..77eb844839 100644 --- a/tests/gtest/include/gtest/gtest-test-part.h +++ b/external/gtest/include/gtest/gtest-test-part.h @@ -62,7 +62,7 @@ class GTEST_API_ TestPartResult { int a_line_number, const char* a_message) : type_(a_type), - file_name_(a_file_name), + file_name_(a_file_name == NULL ? "" : a_file_name), line_number_(a_line_number), summary_(ExtractSummary(a_message)), message_(a_message) { @@ -73,7 +73,9 @@ class GTEST_API_ TestPartResult { // Gets the name of the source file where the test part took place, or // NULL if it's unknown. - const char* file_name() const { return file_name_.c_str(); } + const char* file_name() const { + return file_name_.empty() ? NULL : file_name_.c_str(); + } // Gets the line in the source file where the test part took place, // or -1 if it's unknown. @@ -96,21 +98,22 @@ class GTEST_API_ TestPartResult { // Returns true iff the test part fatally failed. bool fatally_failed() const { return type_ == kFatalFailure; } + private: Type type_; // Gets the summary of the failure message by omitting the stack // trace in it. - static internal::String ExtractSummary(const char* message); + static std::string ExtractSummary(const char* message); // The name of the source file where the test part took place, or - // NULL if the source file is unknown. - internal::String file_name_; + // "" if the source file is unknown. + std::string file_name_; // The line in the source file where the test part took place, or -1 // if the line number is unknown. int line_number_; - internal::String summary_; // The test failure summary. - internal::String message_; // The test failure message. + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. }; // Prints a TestPartResult object. diff --git a/tests/gtest/include/gtest/gtest-typed-test.h b/external/gtest/include/gtest/gtest-typed-test.h similarity index 100% rename from tests/gtest/include/gtest/gtest-typed-test.h rename to external/gtest/include/gtest/gtest-typed-test.h diff --git a/tests/gtest/include/gtest/gtest.h b/external/gtest/include/gtest/gtest.h similarity index 88% rename from tests/gtest/include/gtest/gtest.h rename to external/gtest/include/gtest/gtest.h index 1e51547157..6fa0a3925e 100644 --- a/tests/gtest/include/gtest/gtest.h +++ b/external/gtest/include/gtest/gtest.h @@ -52,6 +52,7 @@ #define GTEST_INCLUDE_GTEST_GTEST_H_ #include +#include #include #include "gtest/internal/gtest-internal.h" @@ -153,25 +154,15 @@ class ExecDeathTest; class NoExecDeathTest; class FinalSuccessChecker; class GTestFlagSaver; +class StreamingListenerTest; class TestResultAccessor; class TestEventListenersAccessor; class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; class WindowsDeathTest; class UnitTestImpl* GetUnitTestImpl(); void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared in gtest-internal.h but defined here, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable) { - return (Message() << streamable).GetString(); -} + const std::string& message); } // namespace internal @@ -391,20 +382,21 @@ class GTEST_API_ Test { // non-fatal) failure. static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } - // Logs a property for the current test. Only the last value for a given - // key is remembered. - // These are public static so they can be called from utility functions - // that are not members of the test fixture. - // The arguments are const char* instead strings, as Google Test is used - // on platforms where string doesn't compile. - // - // Note that a driving consideration for these RecordProperty methods - // was to produce xml output suited to the Greenspan charting utility, - // which at present will only chart values that fit in a 32-bit int. It - // is the user's responsibility to restrict their values to 32-bit ints - // if they intend them to be used with Greenspan. - static void RecordProperty(const char* key, const char* value); - static void RecordProperty(const char* key, int value); + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); protected: // Creates a Test object. @@ -473,7 +465,7 @@ class TestProperty { // C'tor. TestProperty does NOT have a default constructor. // Always use this constructor (with parameters) to create a // TestProperty object. - TestProperty(const char* a_key, const char* a_value) : + TestProperty(const std::string& a_key, const std::string& a_value) : key_(a_key), value_(a_value) { } @@ -488,15 +480,15 @@ class TestProperty { } // Sets a new value, overriding the one supplied in the constructor. - void SetValue(const char* new_value) { + void SetValue(const std::string& new_value) { value_ = new_value; } private: // The key supplied by the user. - internal::String key_; + std::string key_; // The value supplied by the user. - internal::String value_; + std::string value_; }; // The result of a single Test. This includes a list of @@ -547,6 +539,7 @@ class GTEST_API_ TestResult { private: friend class TestInfo; + friend class TestCase; friend class UnitTest; friend class internal::DefaultGlobalTestPartResultReporter; friend class internal::ExecDeathTest; @@ -571,13 +564,16 @@ class GTEST_API_ TestResult { // a non-fatal failure if invalid (e.g., if it conflicts with reserved // key names). If a property is already recorded for the same key, the // value will be updated, rather than storing multiple values for the same - // key. - void RecordProperty(const TestProperty& test_property); + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); // Adds a failure if the key is a reserved attribute of Google Test // testcase tags. Returns true if the property is valid. // TODO(russr): Validate attribute names are legal and human readable. - static bool ValidateTestProperty(const TestProperty& test_property); + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); // Adds a test part result to the list. void AddTestPartResult(const TestPartResult& test_part_result); @@ -650,9 +646,9 @@ class GTEST_API_ TestInfo { return NULL; } - // Returns true if this test should run, that is if the test is not disabled - // (or it is disabled but the also_run_disabled_tests flag has been specified) - // and its full name matches the user-specified filter. + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. // // Google Test allows the user to filter the tests by their full names. // The full name of a test Bar in test case Foo is defined as @@ -668,22 +664,28 @@ class GTEST_API_ TestInfo { // contains the character 'A' or starts with "Foo.". bool should_run() const { return should_run_; } - // Returns true if the test was filtered out by --gtest_filter - bool filtered_out() const { return !matches_filter_; } + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // For now, the XML report includes all tests matching the filter. + // In the future, we may trim tests that are excluded because of + // sharding. + return matches_filter_; + } // Returns the result of the test. const TestResult* result() const { return &result_; } private: - #if GTEST_HAS_DEATH_TEST friend class internal::DefaultDeathTestFactory; #endif // GTEST_HAS_DEATH_TEST friend class Test; friend class TestCase; friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; friend TestInfo* internal::MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, internal::TypeId fixture_class_id, @@ -693,9 +695,10 @@ class GTEST_API_ TestInfo { // Constructs a TestInfo object. The newly constructed instance assumes // ownership of the factory object. - TestInfo(const char* test_case_name, const char* name, - const char* a_type_param, - const char* a_value_param, + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test internal::TypeId fixture_class_id, internal::TestFactoryBase* factory); @@ -775,18 +778,21 @@ class GTEST_API_ TestCase { // Returns true if any test in this test case should run. bool should_run() const { return should_run_; } - // Returns true if this test case should be skipped in the report. - bool should_skip_report() const { return should_skip_report_; } - // Gets the number of successful tests in this test case. int successful_test_count() const; // Gets the number of failed tests in this test case. int failed_test_count() const; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + // Gets the number of disabled tests in this test case. int disabled_test_count() const; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + // Get the number of tests in this test case that should run. int test_to_run_count() const; @@ -806,6 +812,10 @@ class GTEST_API_ TestCase { // total_test_count() - 1. If i is not in that range, returns NULL. const TestInfo* GetTestInfo(int i) const; + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + private: friend class Test; friend class internal::UnitTestImpl; @@ -824,7 +834,6 @@ class GTEST_API_ TestCase { // Sets the should_run member. void set_should_run(bool should) { should_run_ = should; } - void set_should_skip_report(bool should) { should_skip_report_ = should; } // Adds a TestInfo to this test case. Will delete the TestInfo upon // destruction of the TestCase object. @@ -859,11 +868,22 @@ class GTEST_API_ TestCase { return test_info->should_run() && test_info->result()->Failed(); } + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + // Returns true iff test is disabled. static bool TestDisabled(const TestInfo* test_info) { return test_info->is_disabled_; } + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + // Returns true if the given test should run. static bool ShouldRunTest(const TestInfo* test_info) { return test_info->should_run(); @@ -876,7 +896,7 @@ class GTEST_API_ TestCase { void UnshuffleTests(); // Name of the test case. - internal::String name_; + std::string name_; // Name of the parameter type, or NULL if this is not a typed or a // type-parameterized test. const internal::scoped_ptr type_param_; @@ -893,10 +913,11 @@ class GTEST_API_ TestCase { Test::TearDownTestCaseFunc tear_down_tc_; // True iff any test in this test case should run. bool should_run_; - // True if this test case should not be reported - bool should_skip_report_; // Elapsed time, in milliseconds. TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; // We disallow copying TestCases. GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); @@ -1116,11 +1137,13 @@ class GTEST_API_ UnitTest { // Returns the TestCase object for the test that's currently running, // or NULL if no test is running. - const TestCase* current_test_case() const; + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); // Returns the TestInfo object for the test that's currently running, // or NULL if no test is running. - const TestInfo* current_test_info() const; + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); // Returns the random seed used at the start of the current test run. int random_seed() const; @@ -1130,7 +1153,8 @@ class GTEST_API_ UnitTest { // value-parameterized tests and instantiate and register them. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. - internal::ParameterizedTestCaseRegistry& parameterized_test_registry(); + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); #endif // GTEST_HAS_PARAM_TEST // Gets the number of successful test cases. @@ -1152,15 +1176,25 @@ class GTEST_API_ UnitTest { // Gets the number of failed tests. int failed_test_count() const; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + // Gets the number of disabled tests. int disabled_test_count() const; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + // Gets the number of all tests. int total_test_count() const; // Gets the number of tests that should run. int test_to_run_count() const; + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + // Gets the elapsed time, in milliseconds. TimeInMillis elapsed_time() const; @@ -1175,6 +1209,10 @@ class GTEST_API_ UnitTest { // total_test_case_count() - 1. If i is not in that range, returns NULL. const TestCase* GetTestCase(int i) const; + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + const TestResult& ad_hoc_test_result() const; + // Returns the list of event listeners that can be used to track events // inside Google Test. TestEventListeners& listeners(); @@ -1198,12 +1236,16 @@ class GTEST_API_ UnitTest { void AddTestPartResult(TestPartResult::Type result_type, const char* file_name, int line_number, - const internal::String& message, - const internal::String& os_stack_trace); + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); - // Adds a TestProperty to the current TestResult object. If the result already - // contains a property with the same key, the value will be updated. - void RecordPropertyForCurrentTest(const char* key, const char* value); + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); // Gets the i-th test case among all the test cases. i can range from 0 to // total_test_case_count() - 1. If i is not in that range, returns NULL. @@ -1218,11 +1260,13 @@ class GTEST_API_ UnitTest { friend class Test; friend class internal::AssertHelper; friend class internal::ScopedTrace; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; friend Environment* AddGlobalTestEnvironment(Environment* env); friend internal::UnitTestImpl* internal::GetUnitTestImpl(); friend void internal::ReportFailureInUnknownLocation( TestPartResult::Type result_type, - const internal::String& message); + const std::string& message); // Creates an empty UnitTest. UnitTest(); @@ -1232,10 +1276,12 @@ class GTEST_API_ UnitTest { // Pushes a trace defined by SCOPED_TRACE() on to the per-thread // Google Test trace stack. - void PushGTestTrace(const internal::TraceInfo& trace); + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); // Pops a trace from the per-thread Google Test trace stack. - void PopGTestTrace(); + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); // Protects mutable state in *impl_. This is mutable as some const // methods need to lock it too. @@ -1290,24 +1336,101 @@ GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); namespace internal { +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + // Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) // operand to be used in a failure message. The type (but not value) // of the other operand may affect the format. This allows us to // print a char* as a raw pointer when it is compared against another -// char*, and print it as a C string when it is compared against an -// std::string object, for example. -// -// The default implementation ignores the type of the other operand. -// Some specialized versions are used to handle formatting wide or -// narrow C strings. +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. // // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. template -String FormatForComparisonFailureMessage(const T1& value, - const T2& /* other_operand */) { - // C++Builder compiles this incorrectly if the namespace isn't explicitly - // given. - return ::testing::PrintToString(value); +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); } // The helper function for {ASSERT|EXPECT}_EQ. @@ -1319,7 +1442,7 @@ AssertionResult CmpHelperEQ(const char* expected_expression, #ifdef _MSC_VER # pragma warning(push) // Saves the current warning state. # pragma warning(disable:4389) // Temporarily disables warning on - // signed/unsigned mismatch. + // signed/unsigned mismatch. #endif if (expected == actual) { @@ -1455,11 +1578,11 @@ GTEST_IMPL_CMP_HELPER_(NE, !=); // Implements the helper function for {ASSERT|EXPECT}_LE GTEST_IMPL_CMP_HELPER_(LE, <=); // Implements the helper function for {ASSERT|EXPECT}_LT -GTEST_IMPL_CMP_HELPER_(LT, < ); +GTEST_IMPL_CMP_HELPER_(LT, <); // Implements the helper function for {ASSERT|EXPECT}_GE GTEST_IMPL_CMP_HELPER_(GE, >=); // Implements the helper function for {ASSERT|EXPECT}_GT -GTEST_IMPL_CMP_HELPER_(GT, > ); +GTEST_IMPL_CMP_HELPER_(GT, >); #undef GTEST_IMPL_CMP_HELPER_ @@ -1623,9 +1746,9 @@ class GTEST_API_ AssertHelper { : type(t), file(srcfile), line(line_num), message(msg) { } TestPartResult::Type const type; - const char* const file; - int const line; - String const message; + const char* const file; + int const line; + std::string const message; private: GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); @@ -1684,7 +1807,12 @@ class WithParamInterface { // references static data, to reduce the opportunity for incorrect uses // like writing 'WithParamInterface::GetParam()' for a test that // uses a fixture whose parameter type is int. - const ParamType& GetParam() const { return *parameter_; } + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } private: // Sets parameter value. The caller is responsible for making sure the value @@ -1730,12 +1858,6 @@ class TestWithParam : public Test, public WithParamInterface { // usually want the fail-fast behavior of FAIL and ASSERT_*, but those // writing data-driven tests often find themselves using ADD_FAILURE // and EXPECT_* more. -// -// Examples: -// -// EXPECT_TRUE(server.StatusIsOK()); -// ASSERT_FALSE(server.HasPendingRequest(port)) -// << "There are still pending requests " << "on port " << port; // Generates a nonfatal failure with a generic message. #define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") @@ -1909,7 +2031,7 @@ class TestWithParam : public Test, public WithParamInterface { # define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) #endif -// C String Comparisons. All tests treat NULL and any non-NULL string +// C-string Comparisons. All tests treat NULL and any non-NULL string // as different. Two NULLs are equal. // // * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 @@ -2150,15 +2272,20 @@ bool StaticAssertTypeEq() { GTEST_TEST_(test_fixture, test_name, test_fixture, \ ::testing::internal::GetTypeId()) -// Use this macro in main() to run all tests. It returns 0 if all +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all // tests are successful, or 1 otherwise. // // RUN_ALL_TESTS() should be invoked after the command line has been // parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; -#define RUN_ALL_TESTS()\ - (::testing::UnitTest::GetInstance()->Run()) - -} // namespace testing +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} #endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/tests/gtest/include/gtest/gtest_pred_impl.h b/external/gtest/include/gtest/gtest_pred_impl.h similarity index 98% rename from tests/gtest/include/gtest/gtest_pred_impl.h rename to external/gtest/include/gtest/gtest_pred_impl.h index 3805f85bdb..30ae712f50 100644 --- a/tests/gtest/include/gtest/gtest_pred_impl.h +++ b/external/gtest/include/gtest/gtest_pred_impl.h @@ -27,7 +27,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// This file is AUTOMATICALLY GENERATED on 09/24/2010 by command +// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command // 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! // // Implements a family of generic predicate assertion macros. @@ -98,7 +98,7 @@ AssertionResult AssertPred1Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. // Don't use this in your code. #define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, v1),\ + GTEST_ASSERT_(pred_format(#v1, v1), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use @@ -144,7 +144,7 @@ AssertionResult AssertPred2Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. // Don't use this in your code. #define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2),\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use @@ -197,7 +197,7 @@ AssertionResult AssertPred3Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. // Don't use this in your code. #define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3),\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use @@ -257,7 +257,7 @@ AssertionResult AssertPred4Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. // Don't use this in your code. #define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use @@ -324,7 +324,7 @@ AssertionResult AssertPred5Helper(const char* pred_text, // Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. // Don't use this in your code. #define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ - GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ on_failure) // Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use diff --git a/tests/gtest/include/gtest/gtest_prod.h b/external/gtest/include/gtest/gtest_prod.h similarity index 100% rename from tests/gtest/include/gtest/gtest_prod.h rename to external/gtest/include/gtest/gtest_prod.h diff --git a/tests/gtest/include/gtest/internal/gtest-death-test-internal.h b/external/gtest/include/gtest/internal/gtest-death-test-internal.h similarity index 94% rename from tests/gtest/include/gtest/internal/gtest-death-test-internal.h rename to external/gtest/include/gtest/internal/gtest-death-test-internal.h index 1d9f83b652..2b3a78f5bf 100644 --- a/tests/gtest/include/gtest/internal/gtest-death-test-internal.h +++ b/external/gtest/include/gtest/internal/gtest-death-test-internal.h @@ -127,11 +127,11 @@ class GTEST_API_ DeathTest { // the last death test. static const char* LastMessage(); - static void set_last_death_test_message(const String& message); + static void set_last_death_test_message(const std::string& message); private: // A string containing a description of the outcome of the last death test. - static String last_death_test_message_; + static std::string last_death_test_message_; GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); }; @@ -217,12 +217,23 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status); // The symbol "fail" here expands to something into which a message // can be streamed. +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed, the regex is +// ignored, and the macro must accept a streamed message even though the message +// is never printed. +# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else \ + ::testing::Message() + // A class representing the parsed contents of the // --gtest_internal_run_death_test flag, as it existed when // RUN_ALL_TESTS was called. class InternalRunDeathTestFlag { public: - InternalRunDeathTestFlag(const String& a_file, + InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index, int a_write_fd) @@ -234,13 +245,13 @@ class InternalRunDeathTestFlag { posix::Close(write_fd_); } - String file() const { return file_; } + const std::string& file() const { return file_; } int line() const { return line_; } int index() const { return index_; } int write_fd() const { return write_fd_; } private: - String file_; + std::string file_; int line_; int index_; int write_fd_; diff --git a/tests/gtest/include/gtest/internal/gtest-filepath.h b/external/gtest/include/gtest/internal/gtest-filepath.h similarity index 96% rename from tests/gtest/include/gtest/internal/gtest-filepath.h rename to external/gtest/include/gtest/internal/gtest-filepath.h index b36b3cf210..7a13b4b0de 100644 --- a/tests/gtest/include/gtest/internal/gtest-filepath.h +++ b/external/gtest/include/gtest/internal/gtest-filepath.h @@ -61,11 +61,7 @@ class GTEST_API_ FilePath { FilePath() : pathname_("") { } FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } - explicit FilePath(const char* pathname) : pathname_(pathname) { - Normalize(); - } - - explicit FilePath(const String& pathname) : pathname_(pathname) { + explicit FilePath(const std::string& pathname) : pathname_(pathname) { Normalize(); } @@ -78,7 +74,7 @@ class GTEST_API_ FilePath { pathname_ = rhs.pathname_; } - String ToString() const { return pathname_; } + const std::string& string() const { return pathname_; } const char* c_str() const { return pathname_.c_str(); } // Returns the current working directory, or "" if unsuccessful. @@ -111,8 +107,8 @@ class GTEST_API_ FilePath { const FilePath& base_name, const char* extension); - // Returns true iff the path is NULL or "". - bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; } + // Returns true iff the path is "". + bool IsEmpty() const { return pathname_.empty(); } // If input name has a trailing separator character, removes it and returns // the name, otherwise return the name string unmodified. @@ -201,7 +197,7 @@ class GTEST_API_ FilePath { // separators. Returns NULL if no path separator was found. const char* FindLastPathSeparator() const; - String pathname_; + std::string pathname_; }; // class FilePath } // namespace internal diff --git a/tests/gtest/include/gtest/internal/gtest-internal.h b/external/gtest/include/gtest/internal/gtest-internal.h similarity index 88% rename from tests/gtest/include/gtest/internal/gtest-internal.h rename to external/gtest/include/gtest/internal/gtest-internal.h index 7aa1197f15..0dcc3a3194 100644 --- a/tests/gtest/include/gtest/internal/gtest-internal.h +++ b/external/gtest/include/gtest/internal/gtest-internal.h @@ -46,12 +46,18 @@ # include #endif // GTEST_OS_LINUX +#if GTEST_HAS_EXCEPTIONS +# include +#endif + #include +#include #include #include #include #include +#include "gtest/gtest-message.h" #include "gtest/internal/gtest-string.h" #include "gtest/internal/gtest-filepath.h" #include "gtest/internal/gtest-type-util.h" @@ -67,36 +73,6 @@ #define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar -// Google Test defines the testing::Message class to allow construction of -// test messages via the << operator. The idea is that anything -// streamable to std::ostream can be streamed to a testing::Message. -// This allows a user to use his own types in Google Test assertions by -// overloading the << operator. -// -// util/gtl/stl_logging-inl.h overloads << for STL containers. These -// overloads cannot be defined in the std namespace, as that will be -// undefined behavior. Therefore, they are defined in the global -// namespace instead. -// -// C++'s symbol lookup rule (i.e. Koenig lookup) says that these -// overloads are visible in either the std namespace or the global -// namespace, but not other namespaces, including the testing -// namespace which Google Test's Message class is in. -// -// To allow STL containers (and other types that has a << operator -// defined in the global namespace) to be used in Google Test assertions, -// testing::Message must access the custom << operator from the global -// namespace. Hence this helper function. -// -// Note: Jeffrey Yasskin suggested an alternative fix by "using -// ::operator<<;" in the definition of Message's operator<<. That fix -// doesn't require a helper function, but unfortunately doesn't -// compile with MSVC. -template -inline void GTestStreamToHelper(std::ostream* os, const T& val) { - *os << val; -} - class ProtocolMessage; namespace proto2 { class Message; } @@ -122,17 +98,12 @@ class TestInfoImpl; // Opaque implementation of TestInfo class UnitTestImpl; // Opaque implementation of UnitTest // How many times InitGoogleTest() has been called. -extern int g_init_gtest_count; +GTEST_API_ extern int g_init_gtest_count; // The text used in failure messages to indicate the start of the // stack trace. GTEST_API_ extern const char kStackTraceMarker[]; -// A secret type that Google Test users don't know about. It has no -// definition on purpose. Therefore it's impossible to create a -// Secret object, which is what we want. -class Secret; - // Two overloaded helpers for checking at compile time whether an // expression is a null pointer literal (i.e. NULL or any 0-valued // compile-time integral constant). Their return values have @@ -163,8 +134,23 @@ char (&IsNullLiteralHelper(...))[2]; // NOLINT #endif // GTEST_ELLIPSIS_NEEDS_POD_ // Appends the user-supplied message to the Google-Test-generated message. -GTEST_API_ String AppendUserMessage(const String& gtest_msg, - const Message& user_msg); +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +#endif // GTEST_HAS_EXCEPTIONS // A helper class for creating scoped traces in user programs. class GTEST_API_ ScopedTrace { @@ -185,77 +171,6 @@ class GTEST_API_ ScopedTrace { // c'tor and d'tor. Therefore it doesn't // need to be used otherwise. -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); - -// The Symbian compiler has a bug that prevents it from selecting the -// correct overload of FormatForComparisonFailureMessage (see below) -// unless we pass the first argument by reference. If we do that, -// however, Visual Age C++ 10.1 generates a compiler error. Therefore -// we only apply the work-around for Symbian. -#if defined(__SYMBIAN32__) -# define GTEST_CREF_WORKAROUND_ const& -#else -# define GTEST_CREF_WORKAROUND_ -#endif - -// When this operand is a const char* or char*, if the other operand -// is a ::std::string or ::string, we print this operand as a C string -// rather than a pointer (we do the same for wide strings); otherwise -// we print it as a pointer to be safe. - -// This internal macro is used to avoid duplicated code. -#define GTEST_FORMAT_IMPL_(operand2_type, operand1_printer)\ -inline String FormatForComparisonFailureMessage(\ - operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ - const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -}\ -inline String FormatForComparisonFailureMessage(\ - const operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ - const operand2_type& /*operand2*/) {\ - return operand1_printer(str);\ -} - -GTEST_FORMAT_IMPL_(::std::string, String::ShowCStringQuoted) -#if GTEST_HAS_STD_WSTRING -GTEST_FORMAT_IMPL_(::std::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_STD_WSTRING - -#if GTEST_HAS_GLOBAL_STRING -GTEST_FORMAT_IMPL_(::string, String::ShowCStringQuoted) -#endif // GTEST_HAS_GLOBAL_STRING -#if GTEST_HAS_GLOBAL_WSTRING -GTEST_FORMAT_IMPL_(::wstring, String::ShowWideCStringQuoted) -#endif // GTEST_HAS_GLOBAL_WSTRING - -#undef GTEST_FORMAT_IMPL_ - -// The next four overloads handle the case where the operand being -// printed is a char/wchar_t pointer and the other operand is not a -// string/wstring object. In such cases, we just print the operand as -// a pointer to be safe. -#define GTEST_FORMAT_CHAR_PTR_IMPL_(CharType) \ - template \ - String FormatForComparisonFailureMessage(CharType* GTEST_CREF_WORKAROUND_ p, \ - const T&) { \ - return PrintToString(static_cast(p)); \ - } - -GTEST_FORMAT_CHAR_PTR_IMPL_(char) -GTEST_FORMAT_CHAR_PTR_IMPL_(const char) -GTEST_FORMAT_CHAR_PTR_IMPL_(wchar_t) -GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) - -#undef GTEST_FORMAT_CHAR_PTR_IMPL_ - // Constructs and returns the message for an equality assertion // (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. // @@ -273,12 +188,12 @@ GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) // be inserted into the message. GTEST_API_ AssertionResult EqFailure(const char* expected_expression, const char* actual_expression, - const String& expected_value, - const String& actual_value, + const std::string& expected_value, + const std::string& actual_value, bool ignoring_case); // Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -GTEST_API_ String GetBoolAssertionFailureMessage( +GTEST_API_ std::string GetBoolAssertionFailureMessage( const AssertionResult& assertion_result, const char* expression_text, const char* actual_predicate_value, @@ -353,7 +268,7 @@ class FloatingPoint { // bits. Therefore, 4 should be enough for ordinary use. // // See the following article for more details on ULP: - // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ static const size_t kMaxUlps = 4; // Constructs a FloatingPoint from a raw floating-point number. @@ -380,6 +295,9 @@ class FloatingPoint { return ReinterpretBits(kExponentBitMask); } + // Returns the maximum representable finite floating-point number. + static RawType Max(); + // Non-static methods // Returns the bits that represents this number. @@ -460,6 +378,13 @@ class FloatingPoint { FloatingPointUnion u_; }; +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + // Typedefs the instances of the FloatingPoint template class that we // care to use. typedef FloatingPoint Float; @@ -554,7 +479,7 @@ typedef void (*TearDownTestCaseFunc)(); // test_case_name: name of the test case // name: name of the test // type_param the name of the test's type parameter, or NULL if -// this is not a typed or a type-parameterized test. +// this is not a typed or a type-parameterized test. // value_param text representation of the test's value parameter, // or NULL if this is not a type-parameterized test. // fixture_class_id: ID of the test fixture class @@ -564,7 +489,8 @@ typedef void (*TearDownTestCaseFunc)(); // The newly created TestInfo instance will assume // ownership of the factory object. GTEST_API_ TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, @@ -624,9 +550,9 @@ inline const char* SkipComma(const char* str) { // Returns the prefix of 'str' before the first comma in it; returns // the entire string if it contains no comma. -inline String GetPrefixUntilComma(const char* str) { +inline std::string GetPrefixUntilComma(const char* str) { const char* comma = strchr(str, ','); - return comma == NULL ? String(str) : String(str, comma - str); + return comma == NULL ? str : std::string(str, comma); } // TypeParameterizedTest::Register() @@ -652,8 +578,8 @@ class TypeParameterizedTest { // First, registers the first type-parameterized test in the type // list. MakeAndRegisterTestInfo( - String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", - case_name, index).c_str(), + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" + + StreamableToString(index)).c_str(), GetPrefixUntilComma(test_names).c_str(), GetTypeName().c_str(), NULL, // No value parameter. @@ -711,7 +637,7 @@ class TypeParameterizedTestCase { #endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -721,8 +647,8 @@ class TypeParameterizedTestCase { // For example, if Foo() calls Bar(), which in turn calls // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -GTEST_API_ String GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, - int skip_count); +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); // Helpers for suppressing warnings on unreachable code or constant // condition. @@ -797,13 +723,19 @@ struct RemoveConst { typedef T type; }; // NOLINT // MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above // definition to fail to remove the const in 'const int[3]' and 'const // char[3][4]'. The following specialization works around the bug. -// However, it causes trouble with GCC and thus needs to be -// conditionally compiled. -#if defined(_MSC_VER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) template struct RemoveConst { typedef typename RemoveConst::type type[N]; }; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; #endif // A handy wrapper around RemoveConst that works when the argument diff --git a/tests/gtest/include/gtest/internal/gtest-linked_ptr.h b/external/gtest/include/gtest/internal/gtest-linked_ptr.h similarity index 98% rename from tests/gtest/include/gtest/internal/gtest-linked_ptr.h rename to external/gtest/include/gtest/internal/gtest-linked_ptr.h index 57147b4e8b..b1362cd002 100644 --- a/tests/gtest/include/gtest/internal/gtest-linked_ptr.h +++ b/external/gtest/include/gtest/internal/gtest-linked_ptr.h @@ -105,8 +105,8 @@ class linked_ptr_internal { // framework. // Join an existing circle. - // L < g_linked_ptr_mutex - void join(linked_ptr_internal const* ptr) { + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { MutexLock lock(&g_linked_ptr_mutex); linked_ptr_internal const* p = ptr; @@ -117,8 +117,8 @@ class linked_ptr_internal { // Leave whatever circle we're part of. Returns true if we were the // last member of the circle. Once this is done, you can join() another. - // L < g_linked_ptr_mutex - bool depart() { + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { MutexLock lock(&g_linked_ptr_mutex); if (next_ == this) return true; diff --git a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h b/external/gtest/include/gtest/internal/gtest-param-util-generated.h similarity index 83% rename from tests/gtest/include/gtest/internal/gtest-param-util-generated.h rename to external/gtest/include/gtest/internal/gtest-param-util-generated.h index 258267500e..e80548592c 100644 --- a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h +++ b/external/gtest/include/gtest/internal/gtest-param-util-generated.h @@ -95,7 +95,7 @@ class ValueArray2 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_}; + const T array[] = {static_cast(v1_), static_cast(v2_)}; return ValuesIn(array); } @@ -114,7 +114,8 @@ class ValueArray3 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; return ValuesIn(array); } @@ -135,7 +136,8 @@ class ValueArray4 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; return ValuesIn(array); } @@ -157,7 +159,8 @@ class ValueArray5 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; return ValuesIn(array); } @@ -181,7 +184,9 @@ class ValueArray6 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; return ValuesIn(array); } @@ -206,7 +211,9 @@ class ValueArray7 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; return ValuesIn(array); } @@ -233,7 +240,9 @@ class ValueArray8 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; return ValuesIn(array); } @@ -261,7 +270,10 @@ class ValueArray9 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; return ValuesIn(array); } @@ -290,7 +302,10 @@ class ValueArray10 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; return ValuesIn(array); } @@ -321,7 +336,10 @@ class ValueArray11 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; return ValuesIn(array); } @@ -353,8 +371,11 @@ class ValueArray12 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; return ValuesIn(array); } @@ -388,8 +409,11 @@ class ValueArray13 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; return ValuesIn(array); } @@ -424,8 +448,11 @@ class ValueArray14 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; return ValuesIn(array); } @@ -461,8 +488,12 @@ class ValueArray15 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; return ValuesIn(array); } @@ -501,8 +532,12 @@ class ValueArray16 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; return ValuesIn(array); } @@ -542,8 +577,12 @@ class ValueArray17 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; return ValuesIn(array); } @@ -584,8 +623,13 @@ class ValueArray18 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; return ValuesIn(array); } @@ -627,8 +671,13 @@ class ValueArray19 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; return ValuesIn(array); } @@ -672,8 +721,13 @@ class ValueArray20 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; return ValuesIn(array); } @@ -719,8 +773,14 @@ class ValueArray21 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; return ValuesIn(array); } @@ -767,8 +827,14 @@ class ValueArray22 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; return ValuesIn(array); } @@ -817,9 +883,14 @@ class ValueArray23 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, - v23_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; return ValuesIn(array); } @@ -869,9 +940,15 @@ class ValueArray24 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; return ValuesIn(array); } @@ -922,9 +999,15 @@ class ValueArray25 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; return ValuesIn(array); } @@ -977,9 +1060,15 @@ class ValueArray26 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; return ValuesIn(array); } @@ -1034,9 +1123,16 @@ class ValueArray27 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; return ValuesIn(array); } @@ -1092,9 +1188,16 @@ class ValueArray28 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; return ValuesIn(array); } @@ -1151,9 +1254,16 @@ class ValueArray29 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; return ValuesIn(array); } @@ -1212,9 +1322,17 @@ class ValueArray30 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; return ValuesIn(array); } @@ -1275,9 +1393,17 @@ class ValueArray31 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; return ValuesIn(array); } @@ -1339,9 +1465,17 @@ class ValueArray32 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; return ValuesIn(array); } @@ -1405,9 +1539,18 @@ class ValueArray33 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; return ValuesIn(array); } @@ -1472,9 +1615,18 @@ class ValueArray34 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; return ValuesIn(array); } @@ -1540,10 +1692,18 @@ class ValueArray35 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, - v35_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; return ValuesIn(array); } @@ -1611,10 +1771,19 @@ class ValueArray36 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; return ValuesIn(array); } @@ -1684,10 +1853,19 @@ class ValueArray37 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; return ValuesIn(array); } @@ -1758,10 +1936,19 @@ class ValueArray38 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; return ValuesIn(array); } @@ -1833,10 +2020,20 @@ class ValueArray39 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; return ValuesIn(array); } @@ -1910,10 +2107,20 @@ class ValueArray40 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; return ValuesIn(array); } @@ -1989,10 +2196,20 @@ class ValueArray41 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; return ValuesIn(array); } @@ -2069,10 +2286,21 @@ class ValueArray42 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; return ValuesIn(array); } @@ -2150,10 +2378,21 @@ class ValueArray43 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; return ValuesIn(array); } @@ -2233,10 +2472,21 @@ class ValueArray44 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; return ValuesIn(array); } @@ -2317,10 +2567,22 @@ class ValueArray45 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; return ValuesIn(array); } @@ -2403,10 +2665,22 @@ class ValueArray46 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; return ValuesIn(array); } @@ -2491,11 +2765,22 @@ class ValueArray47 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, - v47_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; return ValuesIn(array); } @@ -2581,11 +2866,23 @@ class ValueArray48 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; return ValuesIn(array); } @@ -2672,11 +2969,23 @@ class ValueArray49 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; return ValuesIn(array); } @@ -2764,11 +3073,23 @@ class ValueArray50 { template operator ParamGenerator() const { - const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, - v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, - v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, - v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, - v48_, v49_, v50_}; + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; return ValuesIn(array); } diff --git a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump b/external/gtest/include/gtest/internal/gtest-param-util-generated.h.pump similarity index 99% rename from tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump rename to external/gtest/include/gtest/internal/gtest-param-util-generated.h.pump index dbe9386305..009206fd31 100644 --- a/tests/gtest/include/gtest/internal/gtest-param-util-generated.h.pump +++ b/external/gtest/include/gtest/internal/gtest-param-util-generated.h.pump @@ -98,7 +98,7 @@ class ValueArray$i { template operator ParamGenerator() const { - const T array[] = {$for j, [[v$(j)_]]}; + const T array[] = {$for j, [[static_cast(v$(j)_)]]}; return ValuesIn(array); } diff --git a/tests/gtest/include/gtest/internal/gtest-param-util.h b/external/gtest/include/gtest/internal/gtest-param-util.h similarity index 99% rename from tests/gtest/include/gtest/internal/gtest-param-util.h rename to external/gtest/include/gtest/internal/gtest-param-util.h index 0ef9718cf4..d5e1028b0c 100644 --- a/tests/gtest/include/gtest/internal/gtest-param-util.h +++ b/external/gtest/include/gtest/internal/gtest-param-util.h @@ -494,10 +494,10 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { const string& instantiation_name = gen_it->first; ParamGenerator generator((*gen_it->second)()); - Message test_case_name_stream; + string test_case_name; if ( !instantiation_name.empty() ) - test_case_name_stream << instantiation_name << "/"; - test_case_name_stream << test_info->test_case_base_name; + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; int i = 0; for (typename ParamGenerator::iterator param_it = @@ -506,7 +506,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { Message test_name_stream; test_name_stream << test_info->test_base_name << "/" << i; MakeAndRegisterTestInfo( - test_case_name_stream.GetString().c_str(), + test_case_name.c_str(), test_name_stream.GetString().c_str(), NULL, // No type parameter. PrintToString(*param_it).c_str(), diff --git a/tests/gtest/include/gtest/internal/gtest-port.h b/external/gtest/include/gtest/internal/gtest-port.h similarity index 87% rename from tests/gtest/include/gtest/internal/gtest-port.h rename to external/gtest/include/gtest/internal/gtest-port.h index 157b47f86a..dc4fe0cb6b 100644 --- a/tests/gtest/include/gtest/internal/gtest-port.h +++ b/external/gtest/include/gtest/internal/gtest-port.h @@ -32,6 +32,10 @@ // Low-level types and utilities for porting Google Test to various // platforms. They are subject to change without notice. DO NOT USE // THEM IN USER CODE. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. #ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ @@ -72,6 +76,8 @@ // Test's own tr1 tuple implementation should be // used. Unused when the user sets // GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. // GTEST_LINKED_AS_SHARED_LIBRARY // - Define to 1 when compiling tests that use // Google Test as a shared library (known as @@ -90,7 +96,11 @@ // GTEST_OS_LINUX - Linux // GTEST_OS_LINUX_ANDROID - Google Android // GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_IOS_SIMULATOR - iOS simulator // GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX // GTEST_OS_SOLARIS - Sun Solaris // GTEST_OS_SYMBIAN - Symbian // GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) @@ -175,7 +185,7 @@ // GTEST_FLAG() - references a flag. // GTEST_DECLARE_*() - declares a flag. // GTEST_DEFINE_*() - defines a flag. -// GetArgvs() - returns the command line as a vector of strings. +// GetInjectableArgvs() - returns the command line as a vector of strings. // // Environment variable utilities: // GetEnv() - gets the value of an environment variable. @@ -193,6 +203,11 @@ # include #endif // !_WIN32_WCE +#if defined __APPLE__ +# include +# include +#endif + #include // NOLINT #include // NOLINT #include // NOLINT @@ -227,11 +242,17 @@ # endif // _WIN32_WCE #elif defined __APPLE__ # define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# if TARGET_IPHONE_SIMULATOR +# define GTEST_OS_IOS_SIMULATOR 1 +# endif +# endif #elif defined __linux__ # define GTEST_OS_LINUX 1 -# ifdef ANDROID +# if defined __ANDROID__ # define GTEST_OS_LINUX_ANDROID 1 -# endif // ANDROID +# endif #elif defined __MVS__ # define GTEST_OS_ZOS 1 #elif defined(__sun) && defined(__SVR4) @@ -242,8 +263,25 @@ # define GTEST_OS_HPUX 1 #elif defined __native_client__ # define GTEST_OS_NACL 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 #endif // __CYGWIN__ +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#endif + // Brings in definitions for functions used in the testing::internal::posix // namespace (read, write, close, chdir, isatty, stat). We do not currently // use them on Windows Mobile. @@ -252,20 +290,25 @@ // is not the case, we need to include headers that provide the functions // mentioned above. # include -# if !GTEST_OS_NACL -// TODO(vladl@google.com): Remove this condition when Native Client SDK adds -// strings.h (tracked in -// http://code.google.com/p/nativeclient/issues/detail?id=1175). -# include // Native Client doesn't provide strings.h. -# endif +# include #elif !GTEST_OS_WINDOWS_MOBILE # include # include #endif +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + // Defines this to true iff Google Test can use POSIX regular expressions. #ifndef GTEST_HAS_POSIX_RE -# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif #endif #if GTEST_HAS_POSIX_RE @@ -380,11 +423,27 @@ # elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) # ifdef __GXX_RTTI -# define GTEST_HAS_RTTI 1 +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS # else # define GTEST_HAS_RTTI 0 # endif // __GXX_RTTI +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + // Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if // both the typeid and dynamic_cast features are present. # elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) @@ -417,7 +476,8 @@ // // To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 // to your compiler flags. -# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX) +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \ + || GTEST_OS_QNX) #endif // GTEST_HAS_PTHREAD #if GTEST_HAS_PTHREAD @@ -433,8 +493,13 @@ // this macro to 0 to prevent Google Test from using tuple (any // feature depending on tuple with be disabled in this mode). #ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# else // The user didn't tell us not to do it, so we assume it's OK. -# define GTEST_HAS_TR1_TUPLE 1 +# define GTEST_HAS_TR1_TUPLE 1 +# endif #endif // GTEST_HAS_TR1_TUPLE // Determines whether Google Test's own tr1 tuple implementation @@ -443,14 +508,28 @@ // The user didn't tell us, so we need to figure it out. // We use our own TR1 tuple if we aren't sure the user has an -// implementation of it already. At this time, GCC 4.0.0+ and MSVC -// 2010 are the only mainstream compilers that come with a TR1 tuple -// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by -// defining __GNUC__ and friends, but cannot compile GCC's tuple -// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB -// Feature Pack download, which we cannot assume the user has. -# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000)) \ - || _MSC_VER >= 1600 +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ # define GTEST_USE_OWN_TR1_TUPLE 0 # else # define GTEST_USE_OWN_TR1_TUPLE 1 @@ -465,6 +544,22 @@ # if GTEST_USE_OWN_TR1_TUPLE # include "gtest/internal/gtest-tuple.h" +# elif GTEST_ENV_HAS_STD_TUPLE_ +# include +// C++11 puts its tuple into the ::std namespace rather than +// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. +// This causes undefined behavior, but supported compilers react in +// the way we intend. +namespace std { +namespace tr1 { +using ::std::get; +using ::std::make_tuple; +using ::std::tuple; +using ::std::tuple_element; +using ::std::tuple_size; +} +} + # elif GTEST_OS_SYMBIAN // On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to @@ -515,7 +610,16 @@ // The user didn't tell us, so we need to figure it out. # if GTEST_OS_LINUX && !defined(__ia64__) -# define GTEST_HAS_CLONE 1 +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() is only available on ARM starting with Gingerbread. +# if defined(__arm__) && __ANDROID_API__ >= 9 +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif # else # define GTEST_HAS_CLONE 0 # endif // GTEST_OS_LINUX && !defined(__ia64__) @@ -538,9 +642,11 @@ // Google Test does not support death tests for VC 7.1 and earlier as // abort() in a VC 7.1 application compiled as GUI in debug config // pops up a dialog window that cannot be suppressed programmatically. -#if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \ (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ - GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX) + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX) # define GTEST_HAS_DEATH_TEST 1 # include // NOLINT #endif @@ -669,13 +775,23 @@ # define GTEST_NO_INLINE_ #endif +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +# define GTEST_HAS_CXXABI_H_ 1 +#else +# define GTEST_HAS_CXXABI_H_ 0 +#endif + namespace testing { class Message; namespace internal { -class String; +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; // The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time // expression is true. For example, you could use it to verify the @@ -697,8 +813,8 @@ struct CompileAssert { }; #define GTEST_COMPILE_ASSERT_(expr, msg) \ - typedef ::testing::internal::CompileAssert<(bool(expr))> \ - msg[bool(expr) ? 1 : -1] + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ // Implementation details of GTEST_COMPILE_ASSERT_: // @@ -796,6 +912,7 @@ class scoped_ptr { ptr_ = p; } } + private: T* ptr_; @@ -858,10 +975,9 @@ class GTEST_API_ RE { private: void Init(const char* regex); - // We use a const char* instead of a string, as Google Test may be used - // where string is not available. We also do not use Google Test's own - // String type here, in order to simplify dependencies between the - // files. + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. const char* pattern_; bool is_valid_; @@ -1044,20 +1160,21 @@ Derived* CheckedDowncastToActualType(Base* base) { // GetCapturedStderr - stops capturing stderr and returns the captured string. // GTEST_API_ void CaptureStdout(); -GTEST_API_ String GetCapturedStdout(); +GTEST_API_ std::string GetCapturedStdout(); GTEST_API_ void CaptureStderr(); -GTEST_API_ String GetCapturedStderr(); +GTEST_API_ std::string GetCapturedStderr(); #endif // GTEST_HAS_STREAM_REDIRECTION #if GTEST_HAS_DEATH_TEST -// A copy of all command line arguments. Set by InitGoogleTest(). -extern ::std::vector g_argvs; +const ::std::vector& GetInjectableArgvs(); +void SetInjectableArgvs(const ::std::vector* + new_argvs); -// GTEST_HAS_DEATH_TEST implies we have ::std::string. -const ::std::vector& GetArgvs(); +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector g_argvs; #endif // GTEST_HAS_DEATH_TEST @@ -1084,22 +1201,37 @@ inline void SleepMilliseconds(int n) { // use it in user tests, either directly or indirectly. class Notification { public: - Notification() : notified_(false) {} + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } // Notifies all threads created with this notification to start. Must // be called from the controller thread. - void Notify() { notified_ = true; } + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } // Blocks until the controller thread notifies. Must be called from a test // thread. void WaitForNotification() { - while(!notified_) { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; SleepMilliseconds(10); } } private: - volatile bool notified_; + pthread_mutex_t mutex_; + bool notified_; GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); }; @@ -1207,21 +1339,23 @@ class MutexBase { void Lock() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); owner_ = pthread_self(); + has_owner_ = true; } // Releases this mutex. void Unlock() { - // We don't protect writing to owner_ here, as it's the caller's - // responsibility to ensure that the current thread holds the + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the // mutex when this is called. - owner_ = 0; + has_owner_ = false; GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); } // Does nothing if the current thread holds the mutex. Otherwise, crashes // with high probability. void AssertHeld() const { - GTEST_CHECK_(owner_ == pthread_self()) + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) << "The current thread is not holding the mutex @" << this; } @@ -1232,7 +1366,14 @@ class MutexBase { // have to be public. public: pthread_mutex_t mutex_; // The underlying pthread mutex. - pthread_t owner_; // The thread holding the mutex; 0 means no one holds it. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. }; // Forward-declares a static mutex. @@ -1240,8 +1381,13 @@ class MutexBase { extern ::testing::internal::MutexBase mutex // Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. # define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ - ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 } + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false } // The Mutex class can only be used for mutexes created at runtime. It // shares its API with MutexBase otherwise. @@ -1249,7 +1395,7 @@ class Mutex : public MutexBase { public: Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); - owner_ = 0; + has_owner_ = false; } ~Mutex() { GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); @@ -1399,6 +1545,8 @@ class ThreadLocal { class Mutex { public: Mutex() {} + void Lock() {} + void Unlock() {} void AssertHeld() const {} }; @@ -1529,6 +1677,10 @@ inline bool IsUpper(char ch) { inline bool IsXDigit(char ch) { return isxdigit(static_cast(ch)) != 0; } +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} inline char ToLower(char ch) { return static_cast(tolower(static_cast(ch))); @@ -1666,6 +1818,23 @@ inline void Abort() { abort(); } } // namespace posix +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + // The maximum number a BiggestInt can represent. This definition // works no matter BiggestInt is represented in one's complement or // two's complement. @@ -1718,7 +1887,6 @@ class TypeWithSize<4> { template <> class TypeWithSize<8> { public: - #if GTEST_OS_WINDOWS typedef __int64 Int; typedef unsigned __int64 UInt; @@ -1745,7 +1913,7 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. #define GTEST_DECLARE_int32_(name) \ GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) #define GTEST_DECLARE_string_(name) \ - GTEST_API_ extern ::testing::internal::String GTEST_FLAG(name) + GTEST_API_ extern ::std::string GTEST_FLAG(name) // Macros for defining flags. #define GTEST_DEFINE_bool_(name, default_val, doc) \ @@ -1753,7 +1921,11 @@ typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. #define GTEST_DEFINE_int32_(name, default_val, doc) \ GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) #define GTEST_DEFINE_string_(name, default_val, doc) \ - GTEST_API_ ::testing::internal::String GTEST_FLAG(name) = (default_val) + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +// Thread annotations +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) // Parses 'str' for a 32-bit signed integer. If successful, writes the result // to *value and returns true; otherwise leaves *value unchanged and returns diff --git a/external/gtest/include/gtest/internal/gtest-string.h b/external/gtest/include/gtest/internal/gtest-string.h new file mode 100644 index 0000000000..97f1a7fdd2 --- /dev/null +++ b/external/gtest/include/gtest/internal/gtest-string.h @@ -0,0 +1,167 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by . +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include + +#include "gtest/internal/gtest-port.h" + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true iff the given string ends with the given suffix, ignoring + // case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/tests/gtest/include/gtest/internal/gtest-tuple.h b/external/gtest/include/gtest/internal/gtest-tuple.h similarity index 93% rename from tests/gtest/include/gtest/internal/gtest-tuple.h rename to external/gtest/include/gtest/internal/gtest-tuple.h index d1af50e188..7b3dfc312d 100644 --- a/tests/gtest/include/gtest/internal/gtest-tuple.h +++ b/external/gtest/include/gtest/internal/gtest-tuple.h @@ -1,4 +1,6 @@ -// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! // Copyright 2009 Google Inc. // All Rights Reserved. @@ -140,34 +142,54 @@ template struct TupleElement; template -struct TupleElement { typedef T0 type; }; +struct TupleElement { + typedef T0 type; +}; template -struct TupleElement { typedef T1 type; }; +struct TupleElement { + typedef T1 type; +}; template -struct TupleElement { typedef T2 type; }; +struct TupleElement { + typedef T2 type; +}; template -struct TupleElement { typedef T3 type; }; +struct TupleElement { + typedef T3 type; +}; template -struct TupleElement { typedef T4 type; }; +struct TupleElement { + typedef T4 type; +}; template -struct TupleElement { typedef T5 type; }; +struct TupleElement { + typedef T5 type; +}; template -struct TupleElement { typedef T6 type; }; +struct TupleElement { + typedef T6 type; +}; template -struct TupleElement { typedef T7 type; }; +struct TupleElement { + typedef T7 type; +}; template -struct TupleElement { typedef T8 type; }; +struct TupleElement { + typedef T8 type; +}; template -struct TupleElement { typedef T9 type; }; +struct TupleElement { + typedef T9 type; +}; } // namespace gtest_internal @@ -708,37 +730,59 @@ inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, template struct tuple_size; template -struct tuple_size { static const int value = 0; }; +struct tuple_size { + static const int value = 0; +}; template -struct tuple_size { static const int value = 1; }; +struct tuple_size { + static const int value = 1; +}; template -struct tuple_size { static const int value = 2; }; +struct tuple_size { + static const int value = 2; +}; template -struct tuple_size { static const int value = 3; }; +struct tuple_size { + static const int value = 3; +}; template -struct tuple_size { static const int value = 4; }; +struct tuple_size { + static const int value = 4; +}; template -struct tuple_size { static const int value = 5; }; +struct tuple_size { + static const int value = 5; +}; template -struct tuple_size { static const int value = 6; }; +struct tuple_size { + static const int value = 6; +}; template -struct tuple_size { static const int value = 7; }; +struct tuple_size { + static const int value = 7; +}; template -struct tuple_size { static const int value = 8; }; +struct tuple_size { + static const int value = 8; +}; template -struct tuple_size { static const int value = 9; }; +struct tuple_size { + static const int value = 9; +}; template -struct tuple_size { static const int value = 10; }; +struct tuple_size { + static const int value = 10; +}; template struct tuple_element { @@ -922,8 +966,8 @@ template inline bool operator==(const GTEST_10_TUPLE_(T)& t, const GTEST_10_TUPLE_(U)& u) { return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); + tuple_size::value, + tuple_size::value>::Eq(t, u); } template diff --git a/tests/gtest/include/gtest/internal/gtest-tuple.h.pump b/external/gtest/include/gtest/internal/gtest-tuple.h.pump similarity index 97% rename from tests/gtest/include/gtest/internal/gtest-tuple.h.pump rename to external/gtest/include/gtest/internal/gtest-tuple.h.pump index ef519094a6..c7d9e039b1 100644 --- a/tests/gtest/include/gtest/internal/gtest-tuple.h.pump +++ b/external/gtest/include/gtest/internal/gtest-tuple.h.pump @@ -118,8 +118,9 @@ struct TupleElement; $for i [[ template -struct TupleElement [[]] -{ typedef T$i type; }; +struct TupleElement { + typedef T$i type; +}; ]] @@ -220,7 +221,9 @@ template struct tuple_size; $for j [[ template -struct tuple_size { static const int value = $j; }; +struct tuple_size { + static const int value = $j; +}; ]] @@ -302,8 +305,8 @@ template inline bool operator==(const GTEST_$(n)_TUPLE_(T)& t, const GTEST_$(n)_TUPLE_(U)& u) { return gtest_internal::SameSizeTuplePrefixComparator< - tuple_size::value, - tuple_size::value>::Eq(t, u); + tuple_size::value, + tuple_size::value>::Eq(t, u); } template diff --git a/tests/gtest/include/gtest/internal/gtest-type-util.h b/external/gtest/include/gtest/internal/gtest-type-util.h similarity index 99% rename from tests/gtest/include/gtest/internal/gtest-type-util.h rename to external/gtest/include/gtest/internal/gtest-type-util.h index b7b01b0948..e46f7cfcb4 100644 --- a/tests/gtest/include/gtest/internal/gtest-type-util.h +++ b/external/gtest/include/gtest/internal/gtest-type-util.h @@ -45,15 +45,14 @@ #define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ #include "gtest/internal/gtest-port.h" -#include "gtest/internal/gtest-string.h" // #ifdef __GNUC__ is too general here. It is possible to use gcc without using // libstdc++ (which is where cxxabi.h comes from). -# ifdef __GLIBCXX__ +# if GTEST_HAS_CXXABI_H_ # include # elif defined(__HP_aCC) # include -# endif // __GLIBCXX__ +# endif // GTEST_HASH_CXXABI_H_ namespace testing { namespace internal { @@ -62,24 +61,24 @@ namespace internal { // NB: This function is also used in Google Mock, so don't move it inside of // the typed-test-only section below. template -String GetTypeName() { +std::string GetTypeName() { # if GTEST_HAS_RTTI const char* const name = typeid(T).name(); -# if defined(__GLIBCXX__) || defined(__HP_aCC) +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) int status = 0; // gcc's implementation of typeid(T).name() mangles the type name, // so we have to demangle it. -# ifdef __GLIBCXX__ +# if GTEST_HAS_CXXABI_H_ using abi::__cxa_demangle; -# endif // __GLIBCXX__ +# endif // GTEST_HAS_CXXABI_H_ char* const readable_name = __cxa_demangle(name, 0, 0, &status); - const String name_str(status == 0 ? readable_name : name); + const std::string name_str(status == 0 ? readable_name : name); free(readable_name); return name_str; # else return name; -# endif // __GLIBCXX__ || __HP_aCC +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC # else @@ -3300,7 +3299,9 @@ struct Templates -struct TypeList { typedef Types1 type; }; +struct TypeList { + typedef Types1 type; +}; template # elif defined(__HP_aCC) # include -# endif // __GLIBCXX__ +# endif // GTEST_HASH_CXXABI_H_ namespace testing { namespace internal { @@ -60,24 +59,24 @@ namespace internal { // NB: This function is also used in Google Mock, so don't move it inside of // the typed-test-only section below. template -String GetTypeName() { +std::string GetTypeName() { # if GTEST_HAS_RTTI const char* const name = typeid(T).name(); -# if defined(__GLIBCXX__) || defined(__HP_aCC) +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) int status = 0; // gcc's implementation of typeid(T).name() mangles the type name, // so we have to demangle it. -# ifdef __GLIBCXX__ +# if GTEST_HAS_CXXABI_H_ using abi::__cxa_demangle; -# endif // __GLIBCXX__ +# endif // GTEST_HAS_CXXABI_H_ char* const readable_name = __cxa_demangle(name, 0, 0, &status); - const String name_str(status == 0 ? readable_name : name); + const std::string name_str(status == 0 ? readable_name : name); free(readable_name); return name_str; # else return name; -# endif // __GLIBCXX__ || __HP_aCC +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC # else @@ -279,7 +278,9 @@ struct Templates<$for j, [[T$j]]$for k[[, NoneT]]> { // INSTANTIATE_TYPED_TEST_CASE_P(). template -struct TypeList { typedef Types1 type; }; +struct TypeList { + typedef Types1 type; +}; $range i 1..n diff --git a/external/gtest/m4/acx_pthread.m4 b/external/gtest/m4/acx_pthread.m4 new file mode 100644 index 0000000000..2cf20de144 --- /dev/null +++ b/external/gtest/m4/acx_pthread.m4 @@ -0,0 +1,363 @@ +# This was retrieved from +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi +# See also (perhaps for new versions?) +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi +# +# We've rewritten the inconsistency check code (from avahi), to work +# more broadly. In particular, it no longer assumes ld accepts -zdefs. +# This caused a restructing of the code, but the functionality has only +# changed a little. + +dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl +dnl @summary figure out how to build C programs using POSIX threads +dnl +dnl This macro figures out how to build C programs using POSIX threads. +dnl It sets the PTHREAD_LIBS output variable to the threads library and +dnl linker flags, and the PTHREAD_CFLAGS output variable to any special +dnl C compiler flags that are needed. (The user can also force certain +dnl compiler flags/libs to be tested by setting these environment +dnl variables.) +dnl +dnl Also sets PTHREAD_CC to any special C compiler that is needed for +dnl multi-threaded programs (defaults to the value of CC otherwise). +dnl (This is necessary on AIX to use the special cc_r compiler alias.) +dnl +dnl NOTE: You are assumed to not only compile your program with these +dnl flags, but also link it with them as well. e.g. you should link +dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS +dnl $LIBS +dnl +dnl If you are only building threads programs, you may wish to use +dnl these variables in your default LIBS, CFLAGS, and CC: +dnl +dnl LIBS="$PTHREAD_LIBS $LIBS" +dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +dnl CC="$PTHREAD_CC" +dnl +dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to +dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if a threads +dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to +dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the +dnl default action will define HAVE_PTHREAD. +dnl +dnl Please let the authors know if this macro fails on any platform, or +dnl if you have any other suggestions or comments. This macro was based +dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with +dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros +dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. +dnl We are also grateful for the helpful feedback of numerous users. +dnl +dnl @category InstalledPackages +dnl @author Steven G. Johnson +dnl @version 2006-05-29 +dnl @license GPLWithACException +dnl +dnl Checks for GCC shared/pthread inconsistency based on work by +dnl Marcin Owsiany + + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi + + # The next part tries to detect GCC inconsistency with -shared on some + # architectures and systems. The problem is that in certain + # configurations, when -shared is specified, GCC "forgets" to + # internally use various flags which are still necessary. + + # + # Prepare the flags + # + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_CC="$CC" + + # Try with the flags determined by the earlier checks. + # + # -Wl,-z,defs forces link-time symbol resolution, so that the + # linking checks with -shared actually have any value + # + # FIXME: -fPIC is required for -shared on many architectures, + # so we specify it here, but the right way would probably be to + # properly detect whether it is actually required. + CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CC="$PTHREAD_CC" + + # In order not to create several levels of indentation, we test + # the value of "$done" until we find the cure or run out of ideas. + done="no" + + # First, make sure the CFLAGS we added are actually accepted by our + # compiler. If not (and OS X's ld, for instance, does not accept -z), + # then we can't do this test. + if test x"$done" = xno; then + AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) + AC_TRY_LINK(,, , [done=yes]) + + if test "x$done" = xyes ; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + fi + fi + + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + fi + + # + # Linux gcc on some architectures such as mips/mipsel forgets + # about -lpthread + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lpthread fixes that]) + LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + # + # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lc_r fixes that]) + LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + if test x"$done" = xno; then + # OK, we have run out of ideas + AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) + + # so it's not safe to assume that we may use pthreads + acx_pthread_ok=no + fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + CC="$save_CC" +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/external/gtest/m4/gtest.m4 b/external/gtest/m4/gtest.m4 new file mode 100644 index 0000000000..6598ba75a4 --- /dev/null +++ b/external/gtest/m4/gtest.m4 @@ -0,0 +1,74 @@ +dnl GTEST_LIB_CHECK([minimum version [, +dnl action if found [,action if not found]]]) +dnl +dnl Check for the presence of the Google Test library, optionally at a minimum +dnl version, and indicate a viable version with the HAVE_GTEST flag. It defines +dnl standard variables for substitution including GTEST_CPPFLAGS, +dnl GTEST_CXXFLAGS, GTEST_LDFLAGS, and GTEST_LIBS. It also defines +dnl GTEST_VERSION as the version of Google Test found. Finally, it provides +dnl optional custom action slots in the event GTEST is found or not. +AC_DEFUN([GTEST_LIB_CHECK], +[ +dnl Provide a flag to enable or disable Google Test usage. +AC_ARG_ENABLE([gtest], + [AS_HELP_STRING([--enable-gtest], + [Enable tests using the Google C++ Testing Framework. + (Default is enabled.)])], + [], + [enable_gtest=]) +AC_ARG_VAR([GTEST_CONFIG], + [The exact path of Google Test's 'gtest-config' script.]) +AC_ARG_VAR([GTEST_CPPFLAGS], + [C-like preprocessor flags for Google Test.]) +AC_ARG_VAR([GTEST_CXXFLAGS], + [C++ compile flags for Google Test.]) +AC_ARG_VAR([GTEST_LDFLAGS], + [Linker path and option flags for Google Test.]) +AC_ARG_VAR([GTEST_LIBS], + [Library linking flags for Google Test.]) +AC_ARG_VAR([GTEST_VERSION], + [The version of Google Test available.]) +HAVE_GTEST="no" +AS_IF([test "x${enable_gtest}" != "xno"], + [AC_MSG_CHECKING([for 'gtest-config']) + AS_IF([test "x${enable_gtest}" != "xyes"], + [AS_IF([test -x "${enable_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${enable_gtest}/scripts/gtest-config"], + [GTEST_CONFIG="${enable_gtest}/bin/gtest-config"]) + AS_IF([test -x "${GTEST_CONFIG}"], [], + [AC_MSG_RESULT([no]) + AC_MSG_ERROR([dnl +Unable to locate either a built or installed Google Test. +The specific location '${enable_gtest}' was provided for a built or installed +Google Test, but no 'gtest-config' script could be found at this location.]) + ])], + [AC_PATH_PROG([GTEST_CONFIG], [gtest-config])]) + AS_IF([test -x "${GTEST_CONFIG}"], + [AC_MSG_RESULT([${GTEST_CONFIG}]) + m4_ifval([$1], + [_gtest_min_version="--min-version=$1" + AC_MSG_CHECKING([for Google Test at least version >= $1])], + [_gtest_min_version="--min-version=0" + AC_MSG_CHECKING([for Google Test])]) + AS_IF([${GTEST_CONFIG} ${_gtest_min_version}], + [AC_MSG_RESULT([yes]) + HAVE_GTEST='yes'], + [AC_MSG_RESULT([no])])], + [AC_MSG_RESULT([no])]) + AS_IF([test "x${HAVE_GTEST}" = "xyes"], + [GTEST_CPPFLAGS=`${GTEST_CONFIG} --cppflags` + GTEST_CXXFLAGS=`${GTEST_CONFIG} --cxxflags` + GTEST_LDFLAGS=`${GTEST_CONFIG} --ldflags` + GTEST_LIBS=`${GTEST_CONFIG} --libs` + GTEST_VERSION=`${GTEST_CONFIG} --version` + AC_DEFINE([HAVE_GTEST],[1],[Defined when Google Test is available.])], + [AS_IF([test "x${enable_gtest}" = "xyes"], + [AC_MSG_ERROR([dnl +Google Test was enabled, but no viable version could be found.]) + ])])]) +AC_SUBST([HAVE_GTEST]) +AM_CONDITIONAL([HAVE_GTEST],[test "x$HAVE_GTEST" = "xyes"]) +AS_IF([test "x$HAVE_GTEST" = "xyes"], + [m4_ifval([$2], [$2])], + [m4_ifval([$3], [$3])]) +]) diff --git a/external/gtest/m4/libtool.m4 b/external/gtest/m4/libtool.m4 new file mode 100644 index 0000000000..828104cfde --- /dev/null +++ b/external/gtest/m4/libtool.m4 @@ -0,0 +1,8001 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) + +# serial 57 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to `config.status' so that its +# declaration there will have the same value as in `configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# `#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test $lt_write_fail = 0 && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_REPLACE_SHELLFNS + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test $_lt_result -eq 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test "$lt_cv_ld_force_load" = "yes"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test "${lt_cv_aix_libpath+set}" = set; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script which will find a shell with a builtin +# printf (which we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case "$ECHO" in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[ --with-sysroot[=DIR] Search for dependent libraries within DIR + (or the compiler's sysroot if not specified).], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case ${with_sysroot} in #( + yes) + if test "$GCC" = yes; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([${with_sysroot}]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and in which our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD="${LD-ld}_sol2" + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -eq 0; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test "$ac_status" -ne 0; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test "x$lt_cv_ar_at_file" = xno; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisbility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; + *) lt_sed_strip_eq="s,=/,/,g" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + library_names_spec='${libname}.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec="$LIB" + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=yes + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh + # decide which to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd="$ECHO" + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test "x$lt_cv_path_mainfest_tool" != xyes; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +/* DATA imports from DLLs on WIN32 con't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined(__osf__) +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global defined + # symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test "$with_gnu_ld" = yes; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test "$lt_use_gnu_ld_interface" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + # Also, AIX nm treats weak defined symbols like other global + # defined symbols, whereas GNU nm marks them as "W". + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes && test "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS="$save_LDFLAGS"]) + if test "$lt_cv_irix_exported_symbol" = yes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + fi + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + if test "$with_gnu_ld" = yes; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; + else + $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile="$lt_outputfile.exe" + lt_tool_outputfile="$lt_tool_outputfile.exe" + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + gnu*) + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case ${prev}${p} in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test "$pre_test_object_deps_done" = no; then + case ${prev} in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC* | sunCC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" + CFLAGS="$lt_save_CFLAGS" +fi # test "$_lt_disable_F77" != yes + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test "$_lt_disable_FC" != yes + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,b/c, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) +# ------------------------------------------------------ +# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and +# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. +m4_defun([_LT_PROG_FUNCTION_REPLACE], +[dnl { +sed -e '/^$1 ()$/,/^} # $1 /c\ +$1 ()\ +{\ +m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) +} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") +test 0 -eq $? || _lt_function_replace_fail=: +]) + + +# _LT_PROG_REPLACE_SHELLFNS +# ------------------------- +# Replace existing portable implementations of several shell functions with +# equivalent extended shell implementations where those features are available.. +m4_defun([_LT_PROG_REPLACE_SHELLFNS], +[if test x"$xsi_shell" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}"]) + + _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl + func_split_long_opt_name=${1%%=*} + func_split_long_opt_arg=${1#*=}]) + + _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) + + _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac]) + + _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) + + _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) + + _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) +fi + +if test x"$lt_shell_append" = xyes; then + _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) + + _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl + func_quote_for_eval "${2}" +dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ + eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) + + # Save a `func_append' function call where possible by direct use of '+=' + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +else + # Save a `func_append' function call even when '+=' is not available + sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ + && mv -f "$cfgfile.tmp" "$cfgfile" \ + || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") + test 0 -eq $? || _lt_function_replace_fail=: +fi + +if test x"$_lt_function_replace_fail" = x":"; then + AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) +fi +]) + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine which file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/external/gtest/m4/ltoptions.m4 b/external/gtest/m4/ltoptions.m4 new file mode 100644 index 0000000000..5d9acd8e23 --- /dev/null +++ b/external/gtest/m4/ltoptions.m4 @@ -0,0 +1,384 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 7 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option `$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the `shared' and +# `disable-shared' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the `static' and +# `disable-static' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the `fast-install' +# and `disable-fast-install' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# LT_INIT options. +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for lt_pkg in $withval; do + IFS="$lt_save_ifs" + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [pic_mode=default]) + +test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/external/gtest/m4/ltsugar.m4 b/external/gtest/m4/ltsugar.m4 new file mode 100644 index 0000000000..9000a057d3 --- /dev/null +++ b/external/gtest/m4/ltsugar.m4 @@ -0,0 +1,123 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59 which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/external/gtest/m4/ltversion.m4 b/external/gtest/m4/ltversion.m4 new file mode 100644 index 0000000000..07a8602d48 --- /dev/null +++ b/external/gtest/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 3337 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.2]) +m4_define([LT_PACKAGE_REVISION], [1.3337]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.2' +macro_revision='1.3337' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/external/gtest/m4/lt~obsolete.m4 b/external/gtest/m4/lt~obsolete.m4 new file mode 100644 index 0000000000..c573da90c5 --- /dev/null +++ b/external/gtest/m4/lt~obsolete.m4 @@ -0,0 +1,98 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/external/gtest/make/Makefile b/external/gtest/make/Makefile new file mode 100644 index 0000000000..9ac74493ba --- /dev/null +++ b/external/gtest/make/Makefile @@ -0,0 +1,82 @@ +# A sample Makefile for building Google Test and using it in user +# tests. Please tweak it to suit your environment and project. You +# may want to move it to your project's root directory. +# +# SYNOPSIS: +# +# make [all] - makes everything. +# make TARGET - makes the given target. +# make clean - removes all files generated by make. + +# Please tweak the following variable definitions as needed by your +# project, except GTEST_HEADERS, which you can use in your own targets +# but shouldn't modify. + +# Points to the root of Google Test, relative to where this file is. +# Remember to tweak this if you move this file. +GTEST_DIR = .. + +# Where to find user code. +USER_DIR = ../samples + +# Flags passed to the preprocessor. +# Set Google Test's header directory as a system directory, such that +# the compiler doesn't generate warnings in Google Test headers. +CPPFLAGS += -isystem $(GTEST_DIR)/include + +# Flags passed to the C++ compiler. +CXXFLAGS += -g -Wall -Wextra -pthread + +# All tests produced by this Makefile. Remember to add new tests you +# created to the list. +TESTS = sample1_unittest + +# All Google Test headers. Usually you shouldn't change this +# definition. +GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \ + $(GTEST_DIR)/include/gtest/internal/*.h + +# House-keeping build targets. + +all : $(TESTS) + +clean : + rm -f $(TESTS) gtest.a gtest_main.a *.o + +# Builds gtest.a and gtest_main.a. + +# Usually you shouldn't tweak such internal variables, indicated by a +# trailing _. +GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS) + +# For simplicity and to avoid depending on Google Test's +# implementation details, the dependencies specified below are +# conservative and not optimized. This is fine as Google Test +# compiles fast and for ordinary users its source rarely changes. +gtest-all.o : $(GTEST_SRCS_) + $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \ + $(GTEST_DIR)/src/gtest-all.cc + +gtest_main.o : $(GTEST_SRCS_) + $(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \ + $(GTEST_DIR)/src/gtest_main.cc + +gtest.a : gtest-all.o + $(AR) $(ARFLAGS) $@ $^ + +gtest_main.a : gtest-all.o gtest_main.o + $(AR) $(ARFLAGS) $@ $^ + +# Builds a sample test. A test should link with either gtest.a or +# gtest_main.a, depending on whether it defines its own main() +# function. + +sample1.o : $(USER_DIR)/sample1.cc $(USER_DIR)/sample1.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/sample1.cc + +sample1_unittest.o : $(USER_DIR)/sample1_unittest.cc \ + $(USER_DIR)/sample1.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/sample1_unittest.cc + +sample1_unittest : sample1.o sample1_unittest.o gtest_main.a + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ diff --git a/external/gtest/msvc/gtest-md.sln b/external/gtest/msvc/gtest-md.sln new file mode 100644 index 0000000000..829b4019ae --- /dev/null +++ b/external/gtest/msvc/gtest-md.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-md", "gtest-md.vcproj", "{C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_main-md", "gtest_main-md.vcproj", "{3AF54C8A-10BF-4332-9147-F68ED9862033}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_prod_test-md", "gtest_prod_test-md.vcproj", "{24848551-EF4F-47E8-9A9D-EA4D49BC3ECB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_unittest-md", "gtest_unittest-md.vcproj", "{4D9FDFB5-986A-4139-823C-F4EE0ED481A2}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Debug.ActiveCfg = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Debug.Build.0 = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Release.ActiveCfg = Release|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE8}.Release.Build.0 = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug.ActiveCfg = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Debug.Build.0 = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release.ActiveCfg = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862033}.Release.Build.0 = Release|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECB}.Debug.ActiveCfg = Debug|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECB}.Debug.Build.0 = Debug|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECB}.Release.ActiveCfg = Release|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECB}.Release.Build.0 = Release|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A2}.Debug.ActiveCfg = Debug|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A2}.Debug.Build.0 = Debug|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A2}.Release.ActiveCfg = Release|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A2}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/external/gtest/msvc/gtest-md.vcproj b/external/gtest/msvc/gtest-md.vcproj new file mode 100644 index 0000000000..1c1496ccbf --- /dev/null +++ b/external/gtest/msvc/gtest-md.vcproj @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest.sln b/external/gtest/msvc/gtest.sln new file mode 100644 index 0000000000..c1b2929649 --- /dev/null +++ b/external/gtest/msvc/gtest.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "gtest.vcproj", "{C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_main", "gtest_main.vcproj", "{3AF54C8A-10BF-4332-9147-F68ED9862032}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_unittest", "gtest_unittest.vcproj", "{4D9FDFB5-986A-4139-823C-F4EE0ED481A1}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest_prod_test", "gtest_prod_test.vcproj", "{24848551-EF4F-47E8-9A9D-EA4D49BC3ECA}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Debug.ActiveCfg = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Debug.Build.0 = Debug|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Release.ActiveCfg = Release|Win32 + {C8F6C172-56F2-4E76-B5FA-C3B423B31BE7}.Release.Build.0 = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862032}.Debug.ActiveCfg = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862032}.Debug.Build.0 = Debug|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862032}.Release.ActiveCfg = Release|Win32 + {3AF54C8A-10BF-4332-9147-F68ED9862032}.Release.Build.0 = Release|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A1}.Debug.ActiveCfg = Debug|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A1}.Debug.Build.0 = Debug|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A1}.Release.ActiveCfg = Release|Win32 + {4D9FDFB5-986A-4139-823C-F4EE0ED481A1}.Release.Build.0 = Release|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECA}.Debug.ActiveCfg = Debug|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECA}.Debug.Build.0 = Debug|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECA}.Release.ActiveCfg = Release|Win32 + {24848551-EF4F-47E8-9A9D-EA4D49BC3ECA}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/external/gtest/msvc/gtest.vcproj b/external/gtest/msvc/gtest.vcproj new file mode 100644 index 0000000000..449e7e09ea --- /dev/null +++ b/external/gtest/msvc/gtest.vcproj @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest_main-md.vcproj b/external/gtest/msvc/gtest_main-md.vcproj new file mode 100644 index 0000000000..d00956cd3d --- /dev/null +++ b/external/gtest/msvc/gtest_main-md.vcproj @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest_main.vcproj b/external/gtest/msvc/gtest_main.vcproj new file mode 100644 index 0000000000..e7e9f41768 --- /dev/null +++ b/external/gtest/msvc/gtest_main.vcproj @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest_prod_test-md.vcproj b/external/gtest/msvc/gtest_prod_test-md.vcproj new file mode 100644 index 0000000000..4071d28fed --- /dev/null +++ b/external/gtest/msvc/gtest_prod_test-md.vcproj @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest_prod_test.vcproj b/external/gtest/msvc/gtest_prod_test.vcproj new file mode 100644 index 0000000000..998c75808a --- /dev/null +++ b/external/gtest/msvc/gtest_prod_test.vcproj @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest_unittest-md.vcproj b/external/gtest/msvc/gtest_unittest-md.vcproj new file mode 100644 index 0000000000..1525939750 --- /dev/null +++ b/external/gtest/msvc/gtest_unittest-md.vcproj @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/msvc/gtest_unittest.vcproj b/external/gtest/msvc/gtest_unittest.vcproj new file mode 100644 index 0000000000..2b2d743457 --- /dev/null +++ b/external/gtest/msvc/gtest_unittest.vcproj @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/gtest/samples/prime_tables.h b/external/gtest/samples/prime_tables.h new file mode 100644 index 0000000000..92ce16a014 --- /dev/null +++ b/external/gtest/samples/prime_tables.h @@ -0,0 +1,123 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// Author: vladl@google.com (Vlad Losev) + +// This provides interface PrimeTable that determines whether a number is a +// prime and determines a next prime number. This interface is used +// in Google Test samples demonstrating use of parameterized tests. + +#ifndef GTEST_SAMPLES_PRIME_TABLES_H_ +#define GTEST_SAMPLES_PRIME_TABLES_H_ + +#include + +// The prime table interface. +class PrimeTable { + public: + virtual ~PrimeTable() {} + + // Returns true iff n is a prime number. + virtual bool IsPrime(int n) const = 0; + + // Returns the smallest prime number greater than p; or returns -1 + // if the next prime is beyond the capacity of the table. + virtual int GetNextPrime(int p) const = 0; +}; + +// Implementation #1 calculates the primes on-the-fly. +class OnTheFlyPrimeTable : public PrimeTable { + public: + virtual bool IsPrime(int n) const { + if (n <= 1) return false; + + for (int i = 2; i*i <= n; i++) { + // n is divisible by an integer other than 1 and itself. + if ((n % i) == 0) return false; + } + + return true; + } + + virtual int GetNextPrime(int p) const { + for (int n = p + 1; n > 0; n++) { + if (IsPrime(n)) return n; + } + + return -1; + } +}; + +// Implementation #2 pre-calculates the primes and stores the result +// in an array. +class PreCalculatedPrimeTable : public PrimeTable { + public: + // 'max' specifies the maximum number the prime table holds. + explicit PreCalculatedPrimeTable(int max) + : is_prime_size_(max + 1), is_prime_(new bool[max + 1]) { + CalculatePrimesUpTo(max); + } + virtual ~PreCalculatedPrimeTable() { delete[] is_prime_; } + + virtual bool IsPrime(int n) const { + return 0 <= n && n < is_prime_size_ && is_prime_[n]; + } + + virtual int GetNextPrime(int p) const { + for (int n = p + 1; n < is_prime_size_; n++) { + if (is_prime_[n]) return n; + } + + return -1; + } + + private: + void CalculatePrimesUpTo(int max) { + ::std::fill(is_prime_, is_prime_ + is_prime_size_, true); + is_prime_[0] = is_prime_[1] = false; + + for (int i = 2; i <= max; i++) { + if (!is_prime_[i]) continue; + + // Marks all multiples of i (except i itself) as non-prime. + for (int j = 2*i; j <= max; j += i) { + is_prime_[j] = false; + } + } + } + + const int is_prime_size_; + bool* const is_prime_; + + // Disables compiler warning "assignment operator could not be generated." + void operator=(const PreCalculatedPrimeTable& rhs); +}; + +#endif // GTEST_SAMPLES_PRIME_TABLES_H_ diff --git a/external/gtest/samples/sample1.cc b/external/gtest/samples/sample1.cc new file mode 100644 index 0000000000..f171e2609d --- /dev/null +++ b/external/gtest/samples/sample1.cc @@ -0,0 +1,68 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "sample1.h" + +// Returns n! (the factorial of n). For negative n, n! is defined to be 1. +int Factorial(int n) { + int result = 1; + for (int i = 1; i <= n; i++) { + result *= i; + } + + return result; +} + +// Returns true iff n is a prime number. +bool IsPrime(int n) { + // Trivial case 1: small numbers + if (n <= 1) return false; + + // Trivial case 2: even numbers + if (n % 2 == 0) return n == 2; + + // Now, we have that n is odd and n >= 3. + + // Try to divide n by every odd number i, starting from 3 + for (int i = 3; ; i += 2) { + // We only have to try i up to the squre root of n + if (i > n/i) break; + + // Now, we have i <= n/i < n. + // If n is divisible by i, n is not prime. + if (n % i == 0) return false; + } + + // n has no integer factor in the range (1, n), and thus is prime. + return true; +} diff --git a/external/gtest/samples/sample1.h b/external/gtest/samples/sample1.h new file mode 100644 index 0000000000..3dfeb98c45 --- /dev/null +++ b/external/gtest/samples/sample1.h @@ -0,0 +1,43 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_SAMPLES_SAMPLE1_H_ +#define GTEST_SAMPLES_SAMPLE1_H_ + +// Returns n! (the factorial of n). For negative n, n! is defined to be 1. +int Factorial(int n); + +// Returns true iff n is a prime number. +bool IsPrime(int n); + +#endif // GTEST_SAMPLES_SAMPLE1_H_ diff --git a/external/gtest/samples/sample10_unittest.cc b/external/gtest/samples/sample10_unittest.cc new file mode 100644 index 0000000000..0051cd5dcd --- /dev/null +++ b/external/gtest/samples/sample10_unittest.cc @@ -0,0 +1,144 @@ +// Copyright 2009 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// This sample shows how to use Google Test listener API to implement +// a primitive leak checker. + +#include +#include + +#include "gtest/gtest.h" + +using ::testing::EmptyTestEventListener; +using ::testing::InitGoogleTest; +using ::testing::Test; +using ::testing::TestCase; +using ::testing::TestEventListeners; +using ::testing::TestInfo; +using ::testing::TestPartResult; +using ::testing::UnitTest; + +namespace { + +// We will track memory used by this class. +class Water { + public: + // Normal Water declarations go here. + + // operator new and operator delete help us control water allocation. + void* operator new(size_t allocation_size) { + allocated_++; + return malloc(allocation_size); + } + + void operator delete(void* block, size_t /* allocation_size */) { + allocated_--; + free(block); + } + + static int allocated() { return allocated_; } + + private: + static int allocated_; +}; + +int Water::allocated_ = 0; + +// This event listener monitors how many Water objects are created and +// destroyed by each test, and reports a failure if a test leaks some Water +// objects. It does this by comparing the number of live Water objects at +// the beginning of a test and at the end of a test. +class LeakChecker : public EmptyTestEventListener { + private: + // Called before a test starts. + virtual void OnTestStart(const TestInfo& /* test_info */) { + initially_allocated_ = Water::allocated(); + } + + // Called after a test ends. + virtual void OnTestEnd(const TestInfo& /* test_info */) { + int difference = Water::allocated() - initially_allocated_; + + // You can generate a failure in any event handler except + // OnTestPartResult. Just use an appropriate Google Test assertion to do + // it. + EXPECT_LE(difference, 0) << "Leaked " << difference << " unit(s) of Water!"; + } + + int initially_allocated_; +}; + +TEST(ListenersTest, DoesNotLeak) { + Water* water = new Water; + delete water; +} + +// This should fail when the --check_for_leaks command line flag is +// specified. +TEST(ListenersTest, LeaksWater) { + Water* water = new Water; + EXPECT_TRUE(water != NULL); +} + +} // namespace + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + + bool check_for_leaks = false; + if (argc > 1 && strcmp(argv[1], "--check_for_leaks") == 0 ) + check_for_leaks = true; + else + printf("%s\n", "Run this program with --check_for_leaks to enable " + "custom leak checking in the tests."); + + // If we are given the --check_for_leaks command line flag, installs the + // leak checker. + if (check_for_leaks) { + TestEventListeners& listeners = UnitTest::GetInstance()->listeners(); + + // Adds the leak checker to the end of the test event listener list, + // after the default text output printer and the default XML report + // generator. + // + // The order is important - it ensures that failures generated in the + // leak checker's OnTestEnd() method are processed by the text and XML + // printers *before* their OnTestEnd() methods are called, such that + // they are attributed to the right test. Remember that a listener + // receives an OnXyzStart event *after* listeners preceding it in the + // list received that event, and receives an OnXyzEnd event *before* + // listeners preceding it. + // + // We don't need to worry about deleting the new listener later, as + // Google Test will do it. + listeners.Append(new LeakChecker); + } + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/samples/sample1_unittest.cc b/external/gtest/samples/sample1_unittest.cc new file mode 100644 index 0000000000..aefc4f1d86 --- /dev/null +++ b/external/gtest/samples/sample1_unittest.cc @@ -0,0 +1,153 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + + +// This sample shows how to write a simple unit test for a function, +// using Google C++ testing framework. +// +// Writing a unit test using Google C++ testing framework is easy as 1-2-3: + + +// Step 1. Include necessary header files such that the stuff your +// test logic needs is declared. +// +// Don't forget gtest.h, which declares the testing framework. + +#include +#include "sample1.h" +#include "gtest/gtest.h" + + +// Step 2. Use the TEST macro to define your tests. +// +// TEST has two parameters: the test case name and the test name. +// After using the macro, you should define your test logic between a +// pair of braces. You can use a bunch of macros to indicate the +// success or failure of a test. EXPECT_TRUE and EXPECT_EQ are +// examples of such macros. For a complete list, see gtest.h. +// +// +// +// In Google Test, tests are grouped into test cases. This is how we +// keep test code organized. You should put logically related tests +// into the same test case. +// +// The test case name and the test name should both be valid C++ +// identifiers. And you should not use underscore (_) in the names. +// +// Google Test guarantees that each test you define is run exactly +// once, but it makes no guarantee on the order the tests are +// executed. Therefore, you should write your tests in such a way +// that their results don't depend on their order. +// +// + + +// Tests Factorial(). + +// Tests factorial of negative numbers. +TEST(FactorialTest, Negative) { + // This test is named "Negative", and belongs to the "FactorialTest" + // test case. + EXPECT_EQ(1, Factorial(-5)); + EXPECT_EQ(1, Factorial(-1)); + EXPECT_GT(Factorial(-10), 0); + + // + // + // EXPECT_EQ(expected, actual) is the same as + // + // EXPECT_TRUE((expected) == (actual)) + // + // except that it will print both the expected value and the actual + // value when the assertion fails. This is very helpful for + // debugging. Therefore in this case EXPECT_EQ is preferred. + // + // On the other hand, EXPECT_TRUE accepts any Boolean expression, + // and is thus more general. + // + // +} + +// Tests factorial of 0. +TEST(FactorialTest, Zero) { + EXPECT_EQ(1, Factorial(0)); +} + +// Tests factorial of positive numbers. +TEST(FactorialTest, Positive) { + EXPECT_EQ(1, Factorial(1)); + EXPECT_EQ(2, Factorial(2)); + EXPECT_EQ(6, Factorial(3)); + EXPECT_EQ(40320, Factorial(8)); +} + + +// Tests IsPrime() + +// Tests negative input. +TEST(IsPrimeTest, Negative) { + // This test belongs to the IsPrimeTest test case. + + EXPECT_FALSE(IsPrime(-1)); + EXPECT_FALSE(IsPrime(-2)); + EXPECT_FALSE(IsPrime(INT_MIN)); +} + +// Tests some trivial cases. +TEST(IsPrimeTest, Trivial) { + EXPECT_FALSE(IsPrime(0)); + EXPECT_FALSE(IsPrime(1)); + EXPECT_TRUE(IsPrime(2)); + EXPECT_TRUE(IsPrime(3)); +} + +// Tests positive input. +TEST(IsPrimeTest, Positive) { + EXPECT_FALSE(IsPrime(4)); + EXPECT_TRUE(IsPrime(5)); + EXPECT_FALSE(IsPrime(6)); + EXPECT_TRUE(IsPrime(23)); +} + +// Step 3. Call RUN_ALL_TESTS() in main(). +// +// We do this by linking in src/gtest_main.cc file, which consists of +// a main() function which calls RUN_ALL_TESTS() for us. +// +// This runs all the tests you've defined, prints the result, and +// returns 0 if successful, or 1 otherwise. +// +// Did you notice that we didn't register the tests? The +// RUN_ALL_TESTS() macro magically knows about all the tests we +// defined. Isn't this convenient? diff --git a/external/gtest/samples/sample2.cc b/external/gtest/samples/sample2.cc new file mode 100644 index 0000000000..5f763b9bdf --- /dev/null +++ b/external/gtest/samples/sample2.cc @@ -0,0 +1,56 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "sample2.h" + +#include + +// Clones a 0-terminated C string, allocating memory using new. +const char* MyString::CloneCString(const char* a_c_string) { + if (a_c_string == NULL) return NULL; + + const size_t len = strlen(a_c_string); + char* const clone = new char[ len + 1 ]; + memcpy(clone, a_c_string, len + 1); + + return clone; +} + +// Sets the 0-terminated C string this MyString object +// represents. +void MyString::Set(const char* a_c_string) { + // Makes sure this works when c_string == c_string_ + const char* const temp = MyString::CloneCString(a_c_string); + delete[] c_string_; + c_string_ = temp; +} diff --git a/external/gtest/samples/sample2.h b/external/gtest/samples/sample2.h new file mode 100644 index 0000000000..cb485c70fb --- /dev/null +++ b/external/gtest/samples/sample2.h @@ -0,0 +1,85 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_SAMPLES_SAMPLE2_H_ +#define GTEST_SAMPLES_SAMPLE2_H_ + +#include + + +// A simple string class. +class MyString { + private: + const char* c_string_; + const MyString& operator=(const MyString& rhs); + + public: + // Clones a 0-terminated C string, allocating memory using new. + static const char* CloneCString(const char* a_c_string); + + //////////////////////////////////////////////////////////// + // + // C'tors + + // The default c'tor constructs a NULL string. + MyString() : c_string_(NULL) {} + + // Constructs a MyString by cloning a 0-terminated C string. + explicit MyString(const char* a_c_string) : c_string_(NULL) { + Set(a_c_string); + } + + // Copy c'tor + MyString(const MyString& string) : c_string_(NULL) { + Set(string.c_string_); + } + + //////////////////////////////////////////////////////////// + // + // D'tor. MyString is intended to be a final class, so the d'tor + // doesn't need to be virtual. + ~MyString() { delete[] c_string_; } + + // Gets the 0-terminated C string this MyString object represents. + const char* c_string() const { return c_string_; } + + size_t Length() const { + return c_string_ == NULL ? 0 : strlen(c_string_); + } + + // Sets the 0-terminated C string this MyString object represents. + void Set(const char* c_string); +}; + + +#endif // GTEST_SAMPLES_SAMPLE2_H_ diff --git a/external/gtest/samples/sample2_unittest.cc b/external/gtest/samples/sample2_unittest.cc new file mode 100644 index 0000000000..4fa19b71c7 --- /dev/null +++ b/external/gtest/samples/sample2_unittest.cc @@ -0,0 +1,109 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + + +// This sample shows how to write a more complex unit test for a class +// that has multiple member functions. +// +// Usually, it's a good idea to have one test for each method in your +// class. You don't have to do that exactly, but it helps to keep +// your tests organized. You may also throw in additional tests as +// needed. + +#include "sample2.h" +#include "gtest/gtest.h" + +// In this example, we test the MyString class (a simple string). + +// Tests the default c'tor. +TEST(MyString, DefaultConstructor) { + const MyString s; + + // Asserts that s.c_string() returns NULL. + // + // + // + // If we write NULL instead of + // + // static_cast(NULL) + // + // in this assertion, it will generate a warning on gcc 3.4. The + // reason is that EXPECT_EQ needs to know the types of its + // arguments in order to print them when it fails. Since NULL is + // #defined as 0, the compiler will use the formatter function for + // int to print it. However, gcc thinks that NULL should be used as + // a pointer, not an int, and therefore complains. + // + // The root of the problem is C++'s lack of distinction between the + // integer number 0 and the null pointer constant. Unfortunately, + // we have to live with this fact. + // + // + EXPECT_STREQ(NULL, s.c_string()); + + EXPECT_EQ(0u, s.Length()); +} + +const char kHelloString[] = "Hello, world!"; + +// Tests the c'tor that accepts a C string. +TEST(MyString, ConstructorFromCString) { + const MyString s(kHelloString); + EXPECT_EQ(0, strcmp(s.c_string(), kHelloString)); + EXPECT_EQ(sizeof(kHelloString)/sizeof(kHelloString[0]) - 1, + s.Length()); +} + +// Tests the copy c'tor. +TEST(MyString, CopyConstructor) { + const MyString s1(kHelloString); + const MyString s2 = s1; + EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString)); +} + +// Tests the Set method. +TEST(MyString, Set) { + MyString s; + + s.Set(kHelloString); + EXPECT_EQ(0, strcmp(s.c_string(), kHelloString)); + + // Set should work when the input pointer is the same as the one + // already in the MyString object. + s.Set(s.c_string()); + EXPECT_EQ(0, strcmp(s.c_string(), kHelloString)); + + // Can we set the MyString to NULL? + s.Set(NULL); + EXPECT_STREQ(NULL, s.c_string()); +} diff --git a/external/gtest/samples/sample3-inl.h b/external/gtest/samples/sample3-inl.h new file mode 100644 index 0000000000..7e3084d638 --- /dev/null +++ b/external/gtest/samples/sample3-inl.h @@ -0,0 +1,172 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_SAMPLES_SAMPLE3_INL_H_ +#define GTEST_SAMPLES_SAMPLE3_INL_H_ + +#include + + +// Queue is a simple queue implemented as a singled-linked list. +// +// The element type must support copy constructor. +template // E is the element type +class Queue; + +// QueueNode is a node in a Queue, which consists of an element of +// type E and a pointer to the next node. +template // E is the element type +class QueueNode { + friend class Queue; + + public: + // Gets the element in this node. + const E& element() const { return element_; } + + // Gets the next node in the queue. + QueueNode* next() { return next_; } + const QueueNode* next() const { return next_; } + + private: + // Creates a node with a given element value. The next pointer is + // set to NULL. + explicit QueueNode(const E& an_element) : element_(an_element), next_(NULL) {} + + // We disable the default assignment operator and copy c'tor. + const QueueNode& operator = (const QueueNode&); + QueueNode(const QueueNode&); + + E element_; + QueueNode* next_; +}; + +template // E is the element type. +class Queue { + public: + // Creates an empty queue. + Queue() : head_(NULL), last_(NULL), size_(0) {} + + // D'tor. Clears the queue. + ~Queue() { Clear(); } + + // Clears the queue. + void Clear() { + if (size_ > 0) { + // 1. Deletes every node. + QueueNode* node = head_; + QueueNode* next = node->next(); + for (; ;) { + delete node; + node = next; + if (node == NULL) break; + next = node->next(); + } + + // 2. Resets the member variables. + head_ = last_ = NULL; + size_ = 0; + } + } + + // Gets the number of elements. + size_t Size() const { return size_; } + + // Gets the first element of the queue, or NULL if the queue is empty. + QueueNode* Head() { return head_; } + const QueueNode* Head() const { return head_; } + + // Gets the last element of the queue, or NULL if the queue is empty. + QueueNode* Last() { return last_; } + const QueueNode* Last() const { return last_; } + + // Adds an element to the end of the queue. A copy of the element is + // created using the copy constructor, and then stored in the queue. + // Changes made to the element in the queue doesn't affect the source + // object, and vice versa. + void Enqueue(const E& element) { + QueueNode* new_node = new QueueNode(element); + + if (size_ == 0) { + head_ = last_ = new_node; + size_ = 1; + } else { + last_->next_ = new_node; + last_ = new_node; + size_++; + } + } + + // Removes the head of the queue and returns it. Returns NULL if + // the queue is empty. + E* Dequeue() { + if (size_ == 0) { + return NULL; + } + + const QueueNode* const old_head = head_; + head_ = head_->next_; + size_--; + if (size_ == 0) { + last_ = NULL; + } + + E* element = new E(old_head->element()); + delete old_head; + + return element; + } + + // Applies a function/functor on each element of the queue, and + // returns the result in a new queue. The original queue is not + // affected. + template + Queue* Map(F function) const { + Queue* new_queue = new Queue(); + for (const QueueNode* node = head_; node != NULL; node = node->next_) { + new_queue->Enqueue(function(node->element())); + } + + return new_queue; + } + + private: + QueueNode* head_; // The first node of the queue. + QueueNode* last_; // The last node of the queue. + size_t size_; // The number of elements in the queue. + + // We disallow copying a queue. + Queue(const Queue&); + const Queue& operator = (const Queue&); +}; + +#endif // GTEST_SAMPLES_SAMPLE3_INL_H_ diff --git a/external/gtest/samples/sample3_unittest.cc b/external/gtest/samples/sample3_unittest.cc new file mode 100644 index 0000000000..bf3877d013 --- /dev/null +++ b/external/gtest/samples/sample3_unittest.cc @@ -0,0 +1,151 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + + +// In this example, we use a more advanced feature of Google Test called +// test fixture. +// +// A test fixture is a place to hold objects and functions shared by +// all tests in a test case. Using a test fixture avoids duplicating +// the test code necessary to initialize and cleanup those common +// objects for each test. It is also useful for defining sub-routines +// that your tests need to invoke a lot. +// +// +// +// The tests share the test fixture in the sense of code sharing, not +// data sharing. Each test is given its own fresh copy of the +// fixture. You cannot expect the data modified by one test to be +// passed on to another test, which is a bad idea. +// +// The reason for this design is that tests should be independent and +// repeatable. In particular, a test should not fail as the result of +// another test's failure. If one test depends on info produced by +// another test, then the two tests should really be one big test. +// +// The macros for indicating the success/failure of a test +// (EXPECT_TRUE, FAIL, etc) need to know what the current test is +// (when Google Test prints the test result, it tells you which test +// each failure belongs to). Technically, these macros invoke a +// member function of the Test class. Therefore, you cannot use them +// in a global function. That's why you should put test sub-routines +// in a test fixture. +// +// + +#include "sample3-inl.h" +#include "gtest/gtest.h" + +// To use a test fixture, derive a class from testing::Test. +class QueueTest : public testing::Test { + protected: // You should make the members protected s.t. they can be + // accessed from sub-classes. + + // virtual void SetUp() will be called before each test is run. You + // should define it if you need to initialize the varaibles. + // Otherwise, this can be skipped. + virtual void SetUp() { + q1_.Enqueue(1); + q2_.Enqueue(2); + q2_.Enqueue(3); + } + + // virtual void TearDown() will be called after each test is run. + // You should define it if there is cleanup work to do. Otherwise, + // you don't have to provide it. + // + // virtual void TearDown() { + // } + + // A helper function that some test uses. + static int Double(int n) { + return 2*n; + } + + // A helper function for testing Queue::Map(). + void MapTester(const Queue * q) { + // Creates a new queue, where each element is twice as big as the + // corresponding one in q. + const Queue * const new_q = q->Map(Double); + + // Verifies that the new queue has the same size as q. + ASSERT_EQ(q->Size(), new_q->Size()); + + // Verifies the relationship between the elements of the two queues. + for ( const QueueNode * n1 = q->Head(), * n2 = new_q->Head(); + n1 != NULL; n1 = n1->next(), n2 = n2->next() ) { + EXPECT_EQ(2 * n1->element(), n2->element()); + } + + delete new_q; + } + + // Declares the variables your tests want to use. + Queue q0_; + Queue q1_; + Queue q2_; +}; + +// When you have a test fixture, you define a test using TEST_F +// instead of TEST. + +// Tests the default c'tor. +TEST_F(QueueTest, DefaultConstructor) { + // You can access data in the test fixture here. + EXPECT_EQ(0u, q0_.Size()); +} + +// Tests Dequeue(). +TEST_F(QueueTest, Dequeue) { + int * n = q0_.Dequeue(); + EXPECT_TRUE(n == NULL); + + n = q1_.Dequeue(); + ASSERT_TRUE(n != NULL); + EXPECT_EQ(1, *n); + EXPECT_EQ(0u, q1_.Size()); + delete n; + + n = q2_.Dequeue(); + ASSERT_TRUE(n != NULL); + EXPECT_EQ(2, *n); + EXPECT_EQ(1u, q2_.Size()); + delete n; +} + +// Tests the Queue::Map() function. +TEST_F(QueueTest, Map) { + MapTester(&q0_); + MapTester(&q1_); + MapTester(&q2_); +} diff --git a/external/gtest/samples/sample4.cc b/external/gtest/samples/sample4.cc new file mode 100644 index 0000000000..ae44bda6f1 --- /dev/null +++ b/external/gtest/samples/sample4.cc @@ -0,0 +1,46 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#include + +#include "sample4.h" + +// Returns the current counter value, and increments it. +int Counter::Increment() { + return counter_++; +} + +// Prints the current counter value to STDOUT. +void Counter::Print() const { + printf("%d", counter_); +} diff --git a/external/gtest/samples/sample4.h b/external/gtest/samples/sample4.h new file mode 100644 index 0000000000..cd60f0dd2d --- /dev/null +++ b/external/gtest/samples/sample4.h @@ -0,0 +1,53 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A sample program demonstrating using Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_SAMPLES_SAMPLE4_H_ +#define GTEST_SAMPLES_SAMPLE4_H_ + +// A simple monotonic counter. +class Counter { + private: + int counter_; + + public: + // Creates a counter that starts at 0. + Counter() : counter_(0) {} + + // Returns the current counter value, and increments it. + int Increment(); + + // Prints the current counter value to STDOUT. + void Print() const; +}; + +#endif // GTEST_SAMPLES_SAMPLE4_H_ diff --git a/external/gtest/samples/sample4_unittest.cc b/external/gtest/samples/sample4_unittest.cc new file mode 100644 index 0000000000..fa5afc7d5a --- /dev/null +++ b/external/gtest/samples/sample4_unittest.cc @@ -0,0 +1,45 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest.h" +#include "sample4.h" + +// Tests the Increment() method. +TEST(Counter, Increment) { + Counter c; + + // EXPECT_EQ() evaluates its arguments exactly once, so they + // can have side effects. + + EXPECT_EQ(0, c.Increment()); + EXPECT_EQ(1, c.Increment()); + EXPECT_EQ(2, c.Increment()); +} diff --git a/external/gtest/samples/sample5_unittest.cc b/external/gtest/samples/sample5_unittest.cc new file mode 100644 index 0000000000..43d8e57775 --- /dev/null +++ b/external/gtest/samples/sample5_unittest.cc @@ -0,0 +1,199 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// This sample teaches how to reuse a test fixture in multiple test +// cases by deriving sub-fixtures from it. +// +// When you define a test fixture, you specify the name of the test +// case that will use this fixture. Therefore, a test fixture can +// be used by only one test case. +// +// Sometimes, more than one test cases may want to use the same or +// slightly different test fixtures. For example, you may want to +// make sure that all tests for a GUI library don't leak important +// system resources like fonts and brushes. In Google Test, you do +// this by putting the shared logic in a super (as in "super class") +// test fixture, and then have each test case use a fixture derived +// from this super fixture. + +#include +#include +#include "sample3-inl.h" +#include "gtest/gtest.h" +#include "sample1.h" + +// In this sample, we want to ensure that every test finishes within +// ~5 seconds. If a test takes longer to run, we consider it a +// failure. +// +// We put the code for timing a test in a test fixture called +// "QuickTest". QuickTest is intended to be the super fixture that +// other fixtures derive from, therefore there is no test case with +// the name "QuickTest". This is OK. +// +// Later, we will derive multiple test fixtures from QuickTest. +class QuickTest : public testing::Test { + protected: + // Remember that SetUp() is run immediately before a test starts. + // This is a good place to record the start time. + virtual void SetUp() { + start_time_ = time(NULL); + } + + // TearDown() is invoked immediately after a test finishes. Here we + // check if the test was too slow. + virtual void TearDown() { + // Gets the time when the test finishes + const time_t end_time = time(NULL); + + // Asserts that the test took no more than ~5 seconds. Did you + // know that you can use assertions in SetUp() and TearDown() as + // well? + EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long."; + } + + // The UTC time (in seconds) when the test starts + time_t start_time_; +}; + + +// We derive a fixture named IntegerFunctionTest from the QuickTest +// fixture. All tests using this fixture will be automatically +// required to be quick. +class IntegerFunctionTest : public QuickTest { + // We don't need any more logic than already in the QuickTest fixture. + // Therefore the body is empty. +}; + + +// Now we can write tests in the IntegerFunctionTest test case. + +// Tests Factorial() +TEST_F(IntegerFunctionTest, Factorial) { + // Tests factorial of negative numbers. + EXPECT_EQ(1, Factorial(-5)); + EXPECT_EQ(1, Factorial(-1)); + EXPECT_GT(Factorial(-10), 0); + + // Tests factorial of 0. + EXPECT_EQ(1, Factorial(0)); + + // Tests factorial of positive numbers. + EXPECT_EQ(1, Factorial(1)); + EXPECT_EQ(2, Factorial(2)); + EXPECT_EQ(6, Factorial(3)); + EXPECT_EQ(40320, Factorial(8)); +} + + +// Tests IsPrime() +TEST_F(IntegerFunctionTest, IsPrime) { + // Tests negative input. + EXPECT_FALSE(IsPrime(-1)); + EXPECT_FALSE(IsPrime(-2)); + EXPECT_FALSE(IsPrime(INT_MIN)); + + // Tests some trivial cases. + EXPECT_FALSE(IsPrime(0)); + EXPECT_FALSE(IsPrime(1)); + EXPECT_TRUE(IsPrime(2)); + EXPECT_TRUE(IsPrime(3)); + + // Tests positive input. + EXPECT_FALSE(IsPrime(4)); + EXPECT_TRUE(IsPrime(5)); + EXPECT_FALSE(IsPrime(6)); + EXPECT_TRUE(IsPrime(23)); +} + + +// The next test case (named "QueueTest") also needs to be quick, so +// we derive another fixture from QuickTest. +// +// The QueueTest test fixture has some logic and shared objects in +// addition to what's in QuickTest already. We define the additional +// stuff inside the body of the test fixture, as usual. +class QueueTest : public QuickTest { + protected: + virtual void SetUp() { + // First, we need to set up the super fixture (QuickTest). + QuickTest::SetUp(); + + // Second, some additional setup for this fixture. + q1_.Enqueue(1); + q2_.Enqueue(2); + q2_.Enqueue(3); + } + + // By default, TearDown() inherits the behavior of + // QuickTest::TearDown(). As we have no additional cleaning work + // for QueueTest, we omit it here. + // + // virtual void TearDown() { + // QuickTest::TearDown(); + // } + + Queue q0_; + Queue q1_; + Queue q2_; +}; + + +// Now, let's write tests using the QueueTest fixture. + +// Tests the default constructor. +TEST_F(QueueTest, DefaultConstructor) { + EXPECT_EQ(0u, q0_.Size()); +} + +// Tests Dequeue(). +TEST_F(QueueTest, Dequeue) { + int* n = q0_.Dequeue(); + EXPECT_TRUE(n == NULL); + + n = q1_.Dequeue(); + EXPECT_TRUE(n != NULL); + EXPECT_EQ(1, *n); + EXPECT_EQ(0u, q1_.Size()); + delete n; + + n = q2_.Dequeue(); + EXPECT_TRUE(n != NULL); + EXPECT_EQ(2, *n); + EXPECT_EQ(1u, q2_.Size()); + delete n; +} + +// If necessary, you can derive further test fixtures from a derived +// fixture itself. For example, you can derive another fixture from +// QueueTest. Google Test imposes no limit on how deep the hierarchy +// can be. In practice, however, you probably don't want it to be too +// deep as to be confusing. diff --git a/external/gtest/samples/sample6_unittest.cc b/external/gtest/samples/sample6_unittest.cc new file mode 100644 index 0000000000..8f2036a516 --- /dev/null +++ b/external/gtest/samples/sample6_unittest.cc @@ -0,0 +1,224 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// This sample shows how to test common properties of multiple +// implementations of the same interface (aka interface tests). + +// The interface and its implementations are in this header. +#include "prime_tables.h" + +#include "gtest/gtest.h" + +// First, we define some factory functions for creating instances of +// the implementations. You may be able to skip this step if all your +// implementations can be constructed the same way. + +template +PrimeTable* CreatePrimeTable(); + +template <> +PrimeTable* CreatePrimeTable() { + return new OnTheFlyPrimeTable; +} + +template <> +PrimeTable* CreatePrimeTable() { + return new PreCalculatedPrimeTable(10000); +} + +// Then we define a test fixture class template. +template +class PrimeTableTest : public testing::Test { + protected: + // The ctor calls the factory function to create a prime table + // implemented by T. + PrimeTableTest() : table_(CreatePrimeTable()) {} + + virtual ~PrimeTableTest() { delete table_; } + + // Note that we test an implementation via the base interface + // instead of the actual implementation class. This is important + // for keeping the tests close to the real world scenario, where the + // implementation is invoked via the base interface. It avoids + // got-yas where the implementation class has a method that shadows + // a method with the same name (but slightly different argument + // types) in the base interface, for example. + PrimeTable* const table_; +}; + +#if GTEST_HAS_TYPED_TEST + +using testing::Types; + +// Google Test offers two ways for reusing tests for different types. +// The first is called "typed tests". You should use it if you +// already know *all* the types you are gonna exercise when you write +// the tests. + +// To write a typed test case, first use +// +// TYPED_TEST_CASE(TestCaseName, TypeList); +// +// to declare it and specify the type parameters. As with TEST_F, +// TestCaseName must match the test fixture name. + +// The list of types we want to test. +typedef Types Implementations; + +TYPED_TEST_CASE(PrimeTableTest, Implementations); + +// Then use TYPED_TEST(TestCaseName, TestName) to define a typed test, +// similar to TEST_F. +TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) { + // Inside the test body, you can refer to the type parameter by + // TypeParam, and refer to the fixture class by TestFixture. We + // don't need them in this example. + + // Since we are in the template world, C++ requires explicitly + // writing 'this->' when referring to members of the fixture class. + // This is something you have to learn to live with. + EXPECT_FALSE(this->table_->IsPrime(-5)); + EXPECT_FALSE(this->table_->IsPrime(0)); + EXPECT_FALSE(this->table_->IsPrime(1)); + EXPECT_FALSE(this->table_->IsPrime(4)); + EXPECT_FALSE(this->table_->IsPrime(6)); + EXPECT_FALSE(this->table_->IsPrime(100)); +} + +TYPED_TEST(PrimeTableTest, ReturnsTrueForPrimes) { + EXPECT_TRUE(this->table_->IsPrime(2)); + EXPECT_TRUE(this->table_->IsPrime(3)); + EXPECT_TRUE(this->table_->IsPrime(5)); + EXPECT_TRUE(this->table_->IsPrime(7)); + EXPECT_TRUE(this->table_->IsPrime(11)); + EXPECT_TRUE(this->table_->IsPrime(131)); +} + +TYPED_TEST(PrimeTableTest, CanGetNextPrime) { + EXPECT_EQ(2, this->table_->GetNextPrime(0)); + EXPECT_EQ(3, this->table_->GetNextPrime(2)); + EXPECT_EQ(5, this->table_->GetNextPrime(3)); + EXPECT_EQ(7, this->table_->GetNextPrime(5)); + EXPECT_EQ(11, this->table_->GetNextPrime(7)); + EXPECT_EQ(131, this->table_->GetNextPrime(128)); +} + +// That's it! Google Test will repeat each TYPED_TEST for each type +// in the type list specified in TYPED_TEST_CASE. Sit back and be +// happy that you don't have to define them multiple times. + +#endif // GTEST_HAS_TYPED_TEST + +#if GTEST_HAS_TYPED_TEST_P + +using testing::Types; + +// Sometimes, however, you don't yet know all the types that you want +// to test when you write the tests. For example, if you are the +// author of an interface and expect other people to implement it, you +// might want to write a set of tests to make sure each implementation +// conforms to some basic requirements, but you don't know what +// implementations will be written in the future. +// +// How can you write the tests without committing to the type +// parameters? That's what "type-parameterized tests" can do for you. +// It is a bit more involved than typed tests, but in return you get a +// test pattern that can be reused in many contexts, which is a big +// win. Here's how you do it: + +// First, define a test fixture class template. Here we just reuse +// the PrimeTableTest fixture defined earlier: + +template +class PrimeTableTest2 : public PrimeTableTest { +}; + +// Then, declare the test case. The argument is the name of the test +// fixture, and also the name of the test case (as usual). The _P +// suffix is for "parameterized" or "pattern". +TYPED_TEST_CASE_P(PrimeTableTest2); + +// Next, use TYPED_TEST_P(TestCaseName, TestName) to define a test, +// similar to what you do with TEST_F. +TYPED_TEST_P(PrimeTableTest2, ReturnsFalseForNonPrimes) { + EXPECT_FALSE(this->table_->IsPrime(-5)); + EXPECT_FALSE(this->table_->IsPrime(0)); + EXPECT_FALSE(this->table_->IsPrime(1)); + EXPECT_FALSE(this->table_->IsPrime(4)); + EXPECT_FALSE(this->table_->IsPrime(6)); + EXPECT_FALSE(this->table_->IsPrime(100)); +} + +TYPED_TEST_P(PrimeTableTest2, ReturnsTrueForPrimes) { + EXPECT_TRUE(this->table_->IsPrime(2)); + EXPECT_TRUE(this->table_->IsPrime(3)); + EXPECT_TRUE(this->table_->IsPrime(5)); + EXPECT_TRUE(this->table_->IsPrime(7)); + EXPECT_TRUE(this->table_->IsPrime(11)); + EXPECT_TRUE(this->table_->IsPrime(131)); +} + +TYPED_TEST_P(PrimeTableTest2, CanGetNextPrime) { + EXPECT_EQ(2, this->table_->GetNextPrime(0)); + EXPECT_EQ(3, this->table_->GetNextPrime(2)); + EXPECT_EQ(5, this->table_->GetNextPrime(3)); + EXPECT_EQ(7, this->table_->GetNextPrime(5)); + EXPECT_EQ(11, this->table_->GetNextPrime(7)); + EXPECT_EQ(131, this->table_->GetNextPrime(128)); +} + +// Type-parameterized tests involve one extra step: you have to +// enumerate the tests you defined: +REGISTER_TYPED_TEST_CASE_P( + PrimeTableTest2, // The first argument is the test case name. + // The rest of the arguments are the test names. + ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime); + +// At this point the test pattern is done. However, you don't have +// any real test yet as you haven't said which types you want to run +// the tests with. + +// To turn the abstract test pattern into real tests, you instantiate +// it with a list of types. Usually the test pattern will be defined +// in a .h file, and anyone can #include and instantiate it. You can +// even instantiate it more than once in the same program. To tell +// different instances apart, you give each of them a name, which will +// become part of the test case name and can be used in test filters. + +// The list of types we want to test. Note that it doesn't have to be +// defined at the time we write the TYPED_TEST_P()s. +typedef Types + PrimeTableImplementations; +INSTANTIATE_TYPED_TEST_CASE_P(OnTheFlyAndPreCalculated, // Instance name + PrimeTableTest2, // Test case name + PrimeTableImplementations); // Type list + +#endif // GTEST_HAS_TYPED_TEST_P diff --git a/external/gtest/samples/sample7_unittest.cc b/external/gtest/samples/sample7_unittest.cc new file mode 100644 index 0000000000..1b651a21d6 --- /dev/null +++ b/external/gtest/samples/sample7_unittest.cc @@ -0,0 +1,130 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// This sample shows how to test common properties of multiple +// implementations of an interface (aka interface tests) using +// value-parameterized tests. Each test in the test case has +// a parameter that is an interface pointer to an implementation +// tested. + +// The interface and its implementations are in this header. +#include "prime_tables.h" + +#include "gtest/gtest.h" + +#if GTEST_HAS_PARAM_TEST + +using ::testing::TestWithParam; +using ::testing::Values; + +// As a general rule, to prevent a test from affecting the tests that come +// after it, you should create and destroy the tested objects for each test +// instead of reusing them. In this sample we will define a simple factory +// function for PrimeTable objects. We will instantiate objects in test's +// SetUp() method and delete them in TearDown() method. +typedef PrimeTable* CreatePrimeTableFunc(); + +PrimeTable* CreateOnTheFlyPrimeTable() { + return new OnTheFlyPrimeTable(); +} + +template +PrimeTable* CreatePreCalculatedPrimeTable() { + return new PreCalculatedPrimeTable(max_precalculated); +} + +// Inside the test body, fixture constructor, SetUp(), and TearDown() you +// can refer to the test parameter by GetParam(). In this case, the test +// parameter is a factory function which we call in fixture's SetUp() to +// create and store an instance of PrimeTable. +class PrimeTableTest : public TestWithParam { + public: + virtual ~PrimeTableTest() { delete table_; } + virtual void SetUp() { table_ = (*GetParam())(); } + virtual void TearDown() { + delete table_; + table_ = NULL; + } + + protected: + PrimeTable* table_; +}; + +TEST_P(PrimeTableTest, ReturnsFalseForNonPrimes) { + EXPECT_FALSE(table_->IsPrime(-5)); + EXPECT_FALSE(table_->IsPrime(0)); + EXPECT_FALSE(table_->IsPrime(1)); + EXPECT_FALSE(table_->IsPrime(4)); + EXPECT_FALSE(table_->IsPrime(6)); + EXPECT_FALSE(table_->IsPrime(100)); +} + +TEST_P(PrimeTableTest, ReturnsTrueForPrimes) { + EXPECT_TRUE(table_->IsPrime(2)); + EXPECT_TRUE(table_->IsPrime(3)); + EXPECT_TRUE(table_->IsPrime(5)); + EXPECT_TRUE(table_->IsPrime(7)); + EXPECT_TRUE(table_->IsPrime(11)); + EXPECT_TRUE(table_->IsPrime(131)); +} + +TEST_P(PrimeTableTest, CanGetNextPrime) { + EXPECT_EQ(2, table_->GetNextPrime(0)); + EXPECT_EQ(3, table_->GetNextPrime(2)); + EXPECT_EQ(5, table_->GetNextPrime(3)); + EXPECT_EQ(7, table_->GetNextPrime(5)); + EXPECT_EQ(11, table_->GetNextPrime(7)); + EXPECT_EQ(131, table_->GetNextPrime(128)); +} + +// In order to run value-parameterized tests, you need to instantiate them, +// or bind them to a list of values which will be used as test parameters. +// You can instantiate them in a different translation module, or even +// instantiate them several times. +// +// Here, we instantiate our tests with a list of two PrimeTable object +// factory functions: +INSTANTIATE_TEST_CASE_P( + OnTheFlyAndPreCalculated, + PrimeTableTest, + Values(&CreateOnTheFlyPrimeTable, &CreatePreCalculatedPrimeTable<1000>)); + +#else + +// Google Test may not support value-parameterized tests with some +// compilers. If we use conditional compilation to compile out all +// code referring to the gtest_main library, MSVC linker will not link +// that library at all and consequently complain about missing entry +// point defined in that library (fatal error LNK1561: entry point +// must be defined). This dummy test keeps gtest_main linked in. +TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {} + +#endif // GTEST_HAS_PARAM_TEST diff --git a/external/gtest/samples/sample8_unittest.cc b/external/gtest/samples/sample8_unittest.cc new file mode 100644 index 0000000000..5ad2e2c9f1 --- /dev/null +++ b/external/gtest/samples/sample8_unittest.cc @@ -0,0 +1,173 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// This sample shows how to test code relying on some global flag variables. +// Combine() helps with generating all possible combinations of such flags, +// and each test is given one combination as a parameter. + +// Use class definitions to test from this header. +#include "prime_tables.h" + +#include "gtest/gtest.h" + +#if GTEST_HAS_COMBINE + +// Suppose we want to introduce a new, improved implementation of PrimeTable +// which combines speed of PrecalcPrimeTable and versatility of +// OnTheFlyPrimeTable (see prime_tables.h). Inside it instantiates both +// PrecalcPrimeTable and OnTheFlyPrimeTable and uses the one that is more +// appropriate under the circumstances. But in low memory conditions, it can be +// told to instantiate without PrecalcPrimeTable instance at all and use only +// OnTheFlyPrimeTable. +class HybridPrimeTable : public PrimeTable { + public: + HybridPrimeTable(bool force_on_the_fly, int max_precalculated) + : on_the_fly_impl_(new OnTheFlyPrimeTable), + precalc_impl_(force_on_the_fly ? NULL : + new PreCalculatedPrimeTable(max_precalculated)), + max_precalculated_(max_precalculated) {} + virtual ~HybridPrimeTable() { + delete on_the_fly_impl_; + delete precalc_impl_; + } + + virtual bool IsPrime(int n) const { + if (precalc_impl_ != NULL && n < max_precalculated_) + return precalc_impl_->IsPrime(n); + else + return on_the_fly_impl_->IsPrime(n); + } + + virtual int GetNextPrime(int p) const { + int next_prime = -1; + if (precalc_impl_ != NULL && p < max_precalculated_) + next_prime = precalc_impl_->GetNextPrime(p); + + return next_prime != -1 ? next_prime : on_the_fly_impl_->GetNextPrime(p); + } + + private: + OnTheFlyPrimeTable* on_the_fly_impl_; + PreCalculatedPrimeTable* precalc_impl_; + int max_precalculated_; +}; + +using ::testing::TestWithParam; +using ::testing::Bool; +using ::testing::Values; +using ::testing::Combine; + +// To test all code paths for HybridPrimeTable we must test it with numbers +// both within and outside PreCalculatedPrimeTable's capacity and also with +// PreCalculatedPrimeTable disabled. We do this by defining fixture which will +// accept different combinations of parameters for instantiating a +// HybridPrimeTable instance. +class PrimeTableTest : public TestWithParam< ::std::tr1::tuple > { + protected: + virtual void SetUp() { + // This can be written as + // + // bool force_on_the_fly; + // int max_precalculated; + // tie(force_on_the_fly, max_precalculated) = GetParam(); + // + // once the Google C++ Style Guide allows use of ::std::tr1::tie. + // + bool force_on_the_fly = ::std::tr1::get<0>(GetParam()); + int max_precalculated = ::std::tr1::get<1>(GetParam()); + table_ = new HybridPrimeTable(force_on_the_fly, max_precalculated); + } + virtual void TearDown() { + delete table_; + table_ = NULL; + } + HybridPrimeTable* table_; +}; + +TEST_P(PrimeTableTest, ReturnsFalseForNonPrimes) { + // Inside the test body, you can refer to the test parameter by GetParam(). + // In this case, the test parameter is a PrimeTable interface pointer which + // we can use directly. + // Please note that you can also save it in the fixture's SetUp() method + // or constructor and use saved copy in the tests. + + EXPECT_FALSE(table_->IsPrime(-5)); + EXPECT_FALSE(table_->IsPrime(0)); + EXPECT_FALSE(table_->IsPrime(1)); + EXPECT_FALSE(table_->IsPrime(4)); + EXPECT_FALSE(table_->IsPrime(6)); + EXPECT_FALSE(table_->IsPrime(100)); +} + +TEST_P(PrimeTableTest, ReturnsTrueForPrimes) { + EXPECT_TRUE(table_->IsPrime(2)); + EXPECT_TRUE(table_->IsPrime(3)); + EXPECT_TRUE(table_->IsPrime(5)); + EXPECT_TRUE(table_->IsPrime(7)); + EXPECT_TRUE(table_->IsPrime(11)); + EXPECT_TRUE(table_->IsPrime(131)); +} + +TEST_P(PrimeTableTest, CanGetNextPrime) { + EXPECT_EQ(2, table_->GetNextPrime(0)); + EXPECT_EQ(3, table_->GetNextPrime(2)); + EXPECT_EQ(5, table_->GetNextPrime(3)); + EXPECT_EQ(7, table_->GetNextPrime(5)); + EXPECT_EQ(11, table_->GetNextPrime(7)); + EXPECT_EQ(131, table_->GetNextPrime(128)); +} + +// In order to run value-parameterized tests, you need to instantiate them, +// or bind them to a list of values which will be used as test parameters. +// You can instantiate them in a different translation module, or even +// instantiate them several times. +// +// Here, we instantiate our tests with a list of parameters. We must combine +// all variations of the boolean flag suppressing PrecalcPrimeTable and some +// meaningful values for tests. We choose a small value (1), and a value that +// will put some of the tested numbers beyond the capability of the +// PrecalcPrimeTable instance and some inside it (10). Combine will produce all +// possible combinations. +INSTANTIATE_TEST_CASE_P(MeaningfulTestParameters, + PrimeTableTest, + Combine(Bool(), Values(1, 10))); + +#else + +// Google Test may not support Combine() with some compilers. If we +// use conditional compilation to compile out all code referring to +// the gtest_main library, MSVC linker will not link that library at +// all and consequently complain about missing entry point defined in +// that library (fatal error LNK1561: entry point must be +// defined). This dummy test keeps gtest_main linked in. +TEST(DummyTest, CombineIsNotSupportedOnThisPlatform) {} + +#endif // GTEST_HAS_COMBINE diff --git a/external/gtest/samples/sample9_unittest.cc b/external/gtest/samples/sample9_unittest.cc new file mode 100644 index 0000000000..b2e2079bf3 --- /dev/null +++ b/external/gtest/samples/sample9_unittest.cc @@ -0,0 +1,160 @@ +// Copyright 2009 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// This sample shows how to use Google Test listener API to implement +// an alternative console output and how to use the UnitTest reflection API +// to enumerate test cases and tests and to inspect their results. + +#include + +#include "gtest/gtest.h" + +using ::testing::EmptyTestEventListener; +using ::testing::InitGoogleTest; +using ::testing::Test; +using ::testing::TestCase; +using ::testing::TestEventListeners; +using ::testing::TestInfo; +using ::testing::TestPartResult; +using ::testing::UnitTest; + +namespace { + +// Provides alternative output mode which produces minimal amount of +// information about tests. +class TersePrinter : public EmptyTestEventListener { + private: + // Called before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& /* unit_test */) {} + + // Called after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) { + fprintf(stdout, "TEST %s\n", unit_test.Passed() ? "PASSED" : "FAILED"); + fflush(stdout); + } + + // Called before a test starts. + virtual void OnTestStart(const TestInfo& test_info) { + fprintf(stdout, + "*** Test %s.%s starting.\n", + test_info.test_case_name(), + test_info.name()); + fflush(stdout); + } + + // Called after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult(const TestPartResult& test_part_result) { + fprintf(stdout, + "%s in %s:%d\n%s\n", + test_part_result.failed() ? "*** Failure" : "Success", + test_part_result.file_name(), + test_part_result.line_number(), + test_part_result.summary()); + fflush(stdout); + } + + // Called after a test ends. + virtual void OnTestEnd(const TestInfo& test_info) { + fprintf(stdout, + "*** Test %s.%s ending.\n", + test_info.test_case_name(), + test_info.name()); + fflush(stdout); + } +}; // class TersePrinter + +TEST(CustomOutputTest, PrintsMessage) { + printf("Printing something from the test body...\n"); +} + +TEST(CustomOutputTest, Succeeds) { + SUCCEED() << "SUCCEED() has been invoked from here"; +} + +TEST(CustomOutputTest, Fails) { + EXPECT_EQ(1, 2) + << "This test fails in order to demonstrate alternative failure messages"; +} + +} // namespace + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + + bool terse_output = false; + if (argc > 1 && strcmp(argv[1], "--terse_output") == 0 ) + terse_output = true; + else + printf("%s\n", "Run this program with --terse_output to change the way " + "it prints its output."); + + UnitTest& unit_test = *UnitTest::GetInstance(); + + // If we are given the --terse_output command line flag, suppresses the + // standard output and attaches own result printer. + if (terse_output) { + TestEventListeners& listeners = unit_test.listeners(); + + // Removes the default console output listener from the list so it will + // not receive events from Google Test and won't print any output. Since + // this operation transfers ownership of the listener to the caller we + // have to delete it as well. + delete listeners.Release(listeners.default_result_printer()); + + // Adds the custom output listener to the list. It will now receive + // events from Google Test and print the alternative output. We don't + // have to worry about deleting it since Google Test assumes ownership + // over it after adding it to the list. + listeners.Append(new TersePrinter); + } + int ret_val = RUN_ALL_TESTS(); + + // This is an example of using the UnitTest reflection API to inspect test + // results. Here we discount failures from the tests we expected to fail. + int unexpectedly_failed_tests = 0; + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + // Counts failed tests that were not meant to fail (those without + // 'Fails' in the name). + if (test_info.result()->Failed() && + strcmp(test_info.name(), "Fails") != 0) { + unexpectedly_failed_tests++; + } + } + } + + // Test that were meant to fail should not affect the test program outcome. + if (unexpectedly_failed_tests == 0) + ret_val = 0; + + return ret_val; +} diff --git a/external/gtest/scripts/fuse_gtest_files.py b/external/gtest/scripts/fuse_gtest_files.py new file mode 100644 index 0000000000..57ef72f0e3 --- /dev/null +++ b/external/gtest/scripts/fuse_gtest_files.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +# +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""fuse_gtest_files.py v0.2.0 +Fuses Google Test source code into a .h file and a .cc file. + +SYNOPSIS + fuse_gtest_files.py [GTEST_ROOT_DIR] OUTPUT_DIR + + Scans GTEST_ROOT_DIR for Google Test source code, and generates + two files: OUTPUT_DIR/gtest/gtest.h and OUTPUT_DIR/gtest/gtest-all.cc. + Then you can build your tests by adding OUTPUT_DIR to the include + search path and linking with OUTPUT_DIR/gtest/gtest-all.cc. These + two files contain everything you need to use Google Test. Hence + you can "install" Google Test by copying them to wherever you want. + + GTEST_ROOT_DIR can be omitted and defaults to the parent + directory of the directory holding this script. + +EXAMPLES + ./fuse_gtest_files.py fused_gtest + ./fuse_gtest_files.py path/to/unpacked/gtest fused_gtest + +This tool is experimental. In particular, it assumes that there is no +conditional inclusion of Google Test headers. Please report any +problems to googletestframework@googlegroups.com. You can read +http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide for +more information. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sets +import sys + +# We assume that this file is in the scripts/ directory in the Google +# Test root directory. +DEFAULT_GTEST_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..') + +# Regex for matching '#include "gtest/..."'. +INCLUDE_GTEST_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(gtest/.+)"') + +# Regex for matching '#include "src/..."'. +INCLUDE_SRC_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(src/.+)"') + +# Where to find the source seed files. +GTEST_H_SEED = 'include/gtest/gtest.h' +GTEST_SPI_H_SEED = 'include/gtest/gtest-spi.h' +GTEST_ALL_CC_SEED = 'src/gtest-all.cc' + +# Where to put the generated files. +GTEST_H_OUTPUT = 'gtest/gtest.h' +GTEST_ALL_CC_OUTPUT = 'gtest/gtest-all.cc' + + +def VerifyFileExists(directory, relative_path): + """Verifies that the given file exists; aborts on failure. + + relative_path is the file path relative to the given directory. + """ + + if not os.path.isfile(os.path.join(directory, relative_path)): + print 'ERROR: Cannot find %s in directory %s.' % (relative_path, + directory) + print ('Please either specify a valid project root directory ' + 'or omit it on the command line.') + sys.exit(1) + + +def ValidateGTestRootDir(gtest_root): + """Makes sure gtest_root points to a valid gtest root directory. + + The function aborts the program on failure. + """ + + VerifyFileExists(gtest_root, GTEST_H_SEED) + VerifyFileExists(gtest_root, GTEST_ALL_CC_SEED) + + +def VerifyOutputFile(output_dir, relative_path): + """Verifies that the given output file path is valid. + + relative_path is relative to the output_dir directory. + """ + + # Makes sure the output file either doesn't exist or can be overwritten. + output_file = os.path.join(output_dir, relative_path) + if os.path.exists(output_file): + # TODO(wan@google.com): The following user-interaction doesn't + # work with automated processes. We should provide a way for the + # Makefile to force overwriting the files. + print ('%s already exists in directory %s - overwrite it? (y/N) ' % + (relative_path, output_dir)) + answer = sys.stdin.readline().strip() + if answer not in ['y', 'Y']: + print 'ABORTED.' + sys.exit(1) + + # Makes sure the directory holding the output file exists; creates + # it and all its ancestors if necessary. + parent_directory = os.path.dirname(output_file) + if not os.path.isdir(parent_directory): + os.makedirs(parent_directory) + + +def ValidateOutputDir(output_dir): + """Makes sure output_dir points to a valid output directory. + + The function aborts the program on failure. + """ + + VerifyOutputFile(output_dir, GTEST_H_OUTPUT) + VerifyOutputFile(output_dir, GTEST_ALL_CC_OUTPUT) + + +def FuseGTestH(gtest_root, output_dir): + """Scans folder gtest_root to generate gtest/gtest.h in output_dir.""" + + output_file = file(os.path.join(output_dir, GTEST_H_OUTPUT), 'w') + processed_files = sets.Set() # Holds all gtest headers we've processed. + + def ProcessFile(gtest_header_path): + """Processes the given gtest header file.""" + + # We don't process the same header twice. + if gtest_header_path in processed_files: + return + + processed_files.add(gtest_header_path) + + # Reads each line in the given gtest header. + for line in file(os.path.join(gtest_root, gtest_header_path), 'r'): + m = INCLUDE_GTEST_FILE_REGEX.match(line) + if m: + # It's '#include "gtest/..."' - let's process it recursively. + ProcessFile('include/' + m.group(1)) + else: + # Otherwise we copy the line unchanged to the output file. + output_file.write(line) + + ProcessFile(GTEST_H_SEED) + output_file.close() + + +def FuseGTestAllCcToFile(gtest_root, output_file): + """Scans folder gtest_root to generate gtest/gtest-all.cc in output_file.""" + + processed_files = sets.Set() + + def ProcessFile(gtest_source_file): + """Processes the given gtest source file.""" + + # We don't process the same #included file twice. + if gtest_source_file in processed_files: + return + + processed_files.add(gtest_source_file) + + # Reads each line in the given gtest source file. + for line in file(os.path.join(gtest_root, gtest_source_file), 'r'): + m = INCLUDE_GTEST_FILE_REGEX.match(line) + if m: + if 'include/' + m.group(1) == GTEST_SPI_H_SEED: + # It's '#include "gtest/gtest-spi.h"'. This file is not + # #included by "gtest/gtest.h", so we need to process it. + ProcessFile(GTEST_SPI_H_SEED) + else: + # It's '#include "gtest/foo.h"' where foo is not gtest-spi. + # We treat it as '#include "gtest/gtest.h"', as all other + # gtest headers are being fused into gtest.h and cannot be + # #included directly. + + # There is no need to #include "gtest/gtest.h" more than once. + if not GTEST_H_SEED in processed_files: + processed_files.add(GTEST_H_SEED) + output_file.write('#include "%s"\n' % (GTEST_H_OUTPUT,)) + else: + m = INCLUDE_SRC_FILE_REGEX.match(line) + if m: + # It's '#include "src/foo"' - let's process it recursively. + ProcessFile(m.group(1)) + else: + output_file.write(line) + + ProcessFile(GTEST_ALL_CC_SEED) + + +def FuseGTestAllCc(gtest_root, output_dir): + """Scans folder gtest_root to generate gtest/gtest-all.cc in output_dir.""" + + output_file = file(os.path.join(output_dir, GTEST_ALL_CC_OUTPUT), 'w') + FuseGTestAllCcToFile(gtest_root, output_file) + output_file.close() + + +def FuseGTest(gtest_root, output_dir): + """Fuses gtest.h and gtest-all.cc.""" + + ValidateGTestRootDir(gtest_root) + ValidateOutputDir(output_dir) + + FuseGTestH(gtest_root, output_dir) + FuseGTestAllCc(gtest_root, output_dir) + + +def main(): + argc = len(sys.argv) + if argc == 2: + # fuse_gtest_files.py OUTPUT_DIR + FuseGTest(DEFAULT_GTEST_ROOT_DIR, sys.argv[1]) + elif argc == 3: + # fuse_gtest_files.py GTEST_ROOT_DIR OUTPUT_DIR + FuseGTest(sys.argv[1], sys.argv[2]) + else: + print __doc__ + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/external/gtest/scripts/gen_gtest_pred_impl.py b/external/gtest/scripts/gen_gtest_pred_impl.py new file mode 100644 index 0000000000..3e7ab042ea --- /dev/null +++ b/external/gtest/scripts/gen_gtest_pred_impl.py @@ -0,0 +1,730 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""gen_gtest_pred_impl.py v0.1 + +Generates the implementation of Google Test predicate assertions and +accompanying tests. + +Usage: + + gen_gtest_pred_impl.py MAX_ARITY + +where MAX_ARITY is a positive integer. + +The command generates the implementation of up-to MAX_ARITY-ary +predicate assertions, and writes it to file gtest_pred_impl.h in the +directory where the script is. It also generates the accompanying +unit test in file gtest_pred_impl_unittest.cc. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import sys +import time + +# Where this script is. +SCRIPT_DIR = os.path.dirname(sys.argv[0]) + +# Where to store the generated header. +HEADER = os.path.join(SCRIPT_DIR, '../include/gtest/gtest_pred_impl.h') + +# Where to store the generated unit test. +UNIT_TEST = os.path.join(SCRIPT_DIR, '../test/gtest_pred_impl_unittest.cc') + + +def HeaderPreamble(n): + """Returns the preamble for the header file. + + Args: + n: the maximum arity of the predicate macros to be generated. + """ + + # A map that defines the values used in the preamble template. + DEFS = { + 'today' : time.strftime('%m/%d/%Y'), + 'year' : time.strftime('%Y'), + 'command' : '%s %s' % (os.path.basename(sys.argv[0]), n), + 'n' : n + } + + return ( +"""// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on %(today)s by command +// '%(command)s'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most %(n)s. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \\ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \\ + if (const ::testing::AssertionResult gtest_ar = (expression)) \\ + ; \\ + else \\ + on_failure(gtest_ar.failure_message()) +""" % DEFS) + + +def Arity(n): + """Returns the English name of the given arity.""" + + if n < 0: + return None + elif n <= 3: + return ['nullary', 'unary', 'binary', 'ternary'][n] + else: + return '%s-ary' % n + + +def Title(word): + """Returns the given word in title case. The difference between + this and string's title() method is that Title('4-ary') is '4-ary' + while '4-ary'.title() is '4-Ary'.""" + + return word[0].upper() + word[1:] + + +def OneTo(n): + """Returns the list [1, 2, 3, ..., n].""" + + return range(1, n + 1) + + +def Iter(n, format, sep=''): + """Given a positive integer n, a format string that contains 0 or + more '%s' format specs, and optionally a separator string, returns + the join of n strings, each formatted with the format string on an + iterator ranged from 1 to n. + + Example: + + Iter(3, 'v%s', sep=', ') returns 'v1, v2, v3'. + """ + + # How many '%s' specs are in format? + spec_count = len(format.split('%s')) - 1 + return sep.join([format % (spec_count * (i,)) for i in OneTo(n)]) + + +def ImplementationForArity(n): + """Returns the implementation of n-ary predicate assertions.""" + + # A map the defines the values used in the implementation template. + DEFS = { + 'n' : str(n), + 'vs' : Iter(n, 'v%s', sep=', '), + 'vts' : Iter(n, '#v%s', sep=', '), + 'arity' : Arity(n), + 'Arity' : Title(Arity(n)) + } + + impl = """ + +// Helper function for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use +// this in your code. +template +AssertionResult AssertPred%(n)sHelper(const char* pred_text""" % DEFS + + impl += Iter(n, """, + const char* e%s""") + + impl += """, + Pred pred""" + + impl += Iter(n, """, + const T%s& v%s""") + + impl += """) { + if (pred(%(vs)s)) return AssertionSuccess(); + +""" % DEFS + + impl += ' return AssertionFailure() << pred_text << "("' + + impl += Iter(n, """ + << e%s""", sep=' << ", "') + + impl += ' << ") evaluates to false, where"' + + impl += Iter(n, """ + << "\\n" << e%s << " evaluates to " << v%s""") + + impl += """; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT%(n)s. +// Don't use this in your code. +#define GTEST_PRED_FORMAT%(n)s_(pred_format, %(vs)s, on_failure)\\ + GTEST_ASSERT_(pred_format(%(vts)s, %(vs)s), \\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use +// this in your code. +#define GTEST_PRED%(n)s_(pred, %(vs)s, on_failure)\\ + GTEST_ASSERT_(::testing::AssertPred%(n)sHelper(#pred""" % DEFS + + impl += Iter(n, """, \\ + #v%s""") + + impl += """, \\ + pred""" + + impl += Iter(n, """, \\ + v%s""") + + impl += """), on_failure) + +// %(Arity)s predicate assertion macros. +#define EXPECT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\ + GTEST_PRED_FORMAT%(n)s_(pred_format, %(vs)s, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED%(n)s(pred, %(vs)s) \\ + GTEST_PRED%(n)s_(pred, %(vs)s, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\ + GTEST_PRED_FORMAT%(n)s_(pred_format, %(vs)s, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED%(n)s(pred, %(vs)s) \\ + GTEST_PRED%(n)s_(pred, %(vs)s, GTEST_FATAL_FAILURE_) + +""" % DEFS + + return impl + + +def HeaderPostamble(): + """Returns the postamble for the header file.""" + + return """ + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +""" + + +def GenerateFile(path, content): + """Given a file path and a content string, overwrites it with the + given content.""" + + print 'Updating file %s . . .' % path + + f = file(path, 'w+') + print >>f, content, + f.close() + + print 'File %s has been updated.' % path + + +def GenerateHeader(n): + """Given the maximum arity n, updates the header file that implements + the predicate assertions.""" + + GenerateFile(HEADER, + HeaderPreamble(n) + + ''.join([ImplementationForArity(i) for i in OneTo(n)]) + + HeaderPostamble()) + + +def UnitTestPreamble(): + """Returns the preamble for the unit test file.""" + + # A map that defines the values used in the preamble template. + DEFS = { + 'today' : time.strftime('%m/%d/%Y'), + 'year' : time.strftime('%Y'), + 'command' : '%s %s' % (os.path.basename(sys.argv[0]), sys.argv[1]), + } + + return ( +"""// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on %(today)s by command +// '%(command)s'. DO NOT EDIT BY HAND! + +// Regression test for gtest_pred_impl.h +// +// This file is generated by a script and quite long. If you intend to +// learn how Google Test works by reading its unit tests, read +// gtest_unittest.cc instead. +// +// This is intended as a regression test for the Google Test predicate +// assertions. We compile it as part of the gtest_unittest target +// only to keep the implementation tidy and compact, as it is quite +// involved to set up the stage for testing Google Test using Google +// Test itself. +// +// Currently, gtest_unittest takes ~11 seconds to run in the testing +// daemon. In the future, if it grows too large and needs much more +// time to finish, we should consider separating this file into a +// stand-alone regression test. + +#include + +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +// A user-defined data type. +struct Bool { + explicit Bool(int val) : value(val != 0) {} + + bool operator>(int n) const { return value > Bool(n).value; } + + Bool operator+(const Bool& rhs) const { return Bool(value + rhs.value); } + + bool operator==(const Bool& rhs) const { return value == rhs.value; } + + bool value; +}; + +// Enables Bool to be used in assertions. +std::ostream& operator<<(std::ostream& os, const Bool& x) { + return os << (x.value ? "true" : "false"); +} + +""" % DEFS) + + +def TestsForArity(n): + """Returns the tests for n-ary predicate assertions.""" + + # A map that defines the values used in the template for the tests. + DEFS = { + 'n' : n, + 'es' : Iter(n, 'e%s', sep=', '), + 'vs' : Iter(n, 'v%s', sep=', '), + 'vts' : Iter(n, '#v%s', sep=', '), + 'tvs' : Iter(n, 'T%s v%s', sep=', '), + 'int_vs' : Iter(n, 'int v%s', sep=', '), + 'Bool_vs' : Iter(n, 'Bool v%s', sep=', '), + 'types' : Iter(n, 'typename T%s', sep=', '), + 'v_sum' : Iter(n, 'v%s', sep=' + '), + 'arity' : Arity(n), + 'Arity' : Title(Arity(n)), + } + + tests = ( +"""// Sample functions/functors for testing %(arity)s predicate assertions. + +// A %(arity)s predicate function. +template <%(types)s> +bool PredFunction%(n)s(%(tvs)s) { + return %(v_sum)s > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction%(n)sInt(%(int_vs)s) { + return %(v_sum)s > 0; +} +bool PredFunction%(n)sBool(%(Bool_vs)s) { + return %(v_sum)s > 0; +} +""" % DEFS) + + tests += """ +// A %(arity)s predicate functor. +struct PredFunctor%(n)s { + template <%(types)s> + bool operator()(""" % DEFS + + tests += Iter(n, 'const T%s& v%s', sep=""", + """) + + tests += """) { + return %(v_sum)s > 0; + } +}; +""" % DEFS + + tests += """ +// A %(arity)s predicate-formatter function. +template <%(types)s> +testing::AssertionResult PredFormatFunction%(n)s(""" % DEFS + + tests += Iter(n, 'const char* e%s', sep=""", + """) + + tests += Iter(n, """, + const T%s& v%s""") + + tests += """) { + if (PredFunction%(n)s(%(vs)s)) + return testing::AssertionSuccess(); + + return testing::AssertionFailure() + << """ % DEFS + + tests += Iter(n, 'e%s', sep=' << " + " << ') + + tests += """ + << " is expected to be positive, but evaluates to " + << %(v_sum)s << "."; +} +""" % DEFS + + tests += """ +// A %(arity)s predicate-formatter functor. +struct PredFormatFunctor%(n)s { + template <%(types)s> + testing::AssertionResult operator()(""" % DEFS + + tests += Iter(n, 'const char* e%s', sep=""", + """) + + tests += Iter(n, """, + const T%s& v%s""") + + tests += """) const { + return PredFormatFunction%(n)s(%(es)s, %(vs)s); + } +}; +""" % DEFS + + tests += """ +// Tests for {EXPECT|ASSERT}_PRED_FORMAT%(n)s. + +class Predicate%(n)sTest : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false;""" % DEFS + + tests += """ + """ + Iter(n, 'n%s_ = ') + """0; + } +""" + + tests += """ + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once.""" + + tests += ''.join([""" + EXPECT_EQ(1, n%s_) << + "The predicate assertion didn't evaluate argument %s " + "exactly once.";""" % (i, i + 1) for i in OneTo(n)]) + + tests += """ + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; +""" % DEFS + + tests += Iter(n, """ + static int n%s_;""") + + tests += """ +}; + +bool Predicate%(n)sTest::expected_to_finish_; +bool Predicate%(n)sTest::finished_; +""" % DEFS + + tests += Iter(n, """int Predicate%%(n)sTest::n%s_; +""") % DEFS + + tests += """ +typedef Predicate%(n)sTest EXPECT_PRED_FORMAT%(n)sTest; +typedef Predicate%(n)sTest ASSERT_PRED_FORMAT%(n)sTest; +typedef Predicate%(n)sTest EXPECT_PRED%(n)sTest; +typedef Predicate%(n)sTest ASSERT_PRED%(n)sTest; +""" % DEFS + + def GenTest(use_format, use_assert, expect_failure, + use_functor, use_user_type): + """Returns the test for a predicate assertion macro. + + Args: + use_format: true iff the assertion is a *_PRED_FORMAT*. + use_assert: true iff the assertion is a ASSERT_*. + expect_failure: true iff the assertion is expected to fail. + use_functor: true iff the first argument of the assertion is + a functor (as opposed to a function) + use_user_type: true iff the predicate functor/function takes + argument(s) of a user-defined type. + + Example: + + GenTest(1, 0, 0, 1, 0) returns a test that tests the behavior + of a successful EXPECT_PRED_FORMATn() that takes a functor + whose arguments have built-in types.""" + + if use_assert: + assrt = 'ASSERT' # 'assert' is reserved, so we cannot use + # that identifier here. + else: + assrt = 'EXPECT' + + assertion = assrt + '_PRED' + + if use_format: + pred_format = 'PredFormat' + assertion += '_FORMAT' + else: + pred_format = 'Pred' + + assertion += '%(n)s' % DEFS + + if use_functor: + pred_format_type = 'functor' + pred_format += 'Functor%(n)s()' + else: + pred_format_type = 'function' + pred_format += 'Function%(n)s' + if not use_format: + if use_user_type: + pred_format += 'Bool' + else: + pred_format += 'Int' + + test_name = pred_format_type.title() + + if use_user_type: + arg_type = 'user-defined type (Bool)' + test_name += 'OnUserType' + if expect_failure: + arg = 'Bool(n%s_++)' + else: + arg = 'Bool(++n%s_)' + else: + arg_type = 'built-in type (int)' + test_name += 'OnBuiltInType' + if expect_failure: + arg = 'n%s_++' + else: + arg = '++n%s_' + + if expect_failure: + successful_or_failed = 'failed' + expected_or_not = 'expected.' + test_name += 'Failure' + else: + successful_or_failed = 'successful' + expected_or_not = 'UNEXPECTED!' + test_name += 'Success' + + # A map that defines the values used in the test template. + defs = DEFS.copy() + defs.update({ + 'assert' : assrt, + 'assertion' : assertion, + 'test_name' : test_name, + 'pf_type' : pred_format_type, + 'pf' : pred_format, + 'arg_type' : arg_type, + 'arg' : arg, + 'successful' : successful_or_failed, + 'expected' : expected_or_not, + }) + + test = """ +// Tests a %(successful)s %(assertion)s where the +// predicate-formatter is a %(pf_type)s on a %(arg_type)s. +TEST_F(%(assertion)sTest, %(test_name)s) {""" % defs + + indent = (len(assertion) + 3)*' ' + extra_indent = '' + + if expect_failure: + extra_indent = ' ' + if use_assert: + test += """ + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT""" + else: + test += """ + EXPECT_NONFATAL_FAILURE({ // NOLINT""" + + test += '\n' + extra_indent + """ %(assertion)s(%(pf)s""" % defs + + test = test % defs + test += Iter(n, ',\n' + indent + extra_indent + '%(arg)s' % defs) + test += ');\n' + extra_indent + ' finished_ = true;\n' + + if expect_failure: + test += ' }, "");\n' + + test += '}\n' + return test + + # Generates tests for all 2**6 = 64 combinations. + tests += ''.join([GenTest(use_format, use_assert, expect_failure, + use_functor, use_user_type) + for use_format in [0, 1] + for use_assert in [0, 1] + for expect_failure in [0, 1] + for use_functor in [0, 1] + for use_user_type in [0, 1] + ]) + + return tests + + +def UnitTestPostamble(): + """Returns the postamble for the tests.""" + + return '' + + +def GenerateUnitTest(n): + """Returns the tests for up-to n-ary predicate assertions.""" + + GenerateFile(UNIT_TEST, + UnitTestPreamble() + + ''.join([TestsForArity(i) for i in OneTo(n)]) + + UnitTestPostamble()) + + +def _Main(): + """The entry point of the script. Generates the header file and its + unit test.""" + + if len(sys.argv) != 2: + print __doc__ + print 'Author: ' + __author__ + sys.exit(1) + + n = int(sys.argv[1]) + GenerateHeader(n) + GenerateUnitTest(n) + + +if __name__ == '__main__': + _Main() diff --git a/external/gtest/scripts/gtest-config.in b/external/gtest/scripts/gtest-config.in new file mode 100644 index 0000000000..780f8432ef --- /dev/null +++ b/external/gtest/scripts/gtest-config.in @@ -0,0 +1,274 @@ +#!/bin/sh + +# These variables are automatically filled in by the configure script. +name="@PACKAGE_TARNAME@" +version="@PACKAGE_VERSION@" + +show_usage() +{ + echo "Usage: gtest-config [OPTIONS...]" +} + +show_help() +{ + show_usage + cat <<\EOF + +The `gtest-config' script provides access to the necessary compile and linking +flags to connect with Google C++ Testing Framework, both in a build prior to +installation, and on the system proper after installation. The installation +overrides may be issued in combination with any other queries, but will only +affect installation queries if called on a built but not installed gtest. The +installation queries may not be issued with any other types of queries, and +only one installation query may be made at a time. The version queries and +compiler flag queries may be combined as desired but not mixed. Different +version queries are always combined with logical "and" semantics, and only the +last of any particular query is used while all previous ones ignored. All +versions must be specified as a sequence of numbers separated by periods. +Compiler flag queries output the union of the sets of flags when combined. + + Examples: + gtest-config --min-version=1.0 || echo "Insufficient Google Test version." + + g++ $(gtest-config --cppflags --cxxflags) -o foo.o -c foo.cpp + g++ $(gtest-config --ldflags --libs) -o foo foo.o + + # When using a built but not installed Google Test: + g++ $(../../my_gtest_build/scripts/gtest-config ...) ... + + # When using an installed Google Test, but with installation overrides: + export GTEST_PREFIX="/opt" + g++ $(gtest-config --libdir="/opt/lib64" ...) ... + + Help: + --usage brief usage information + --help display this help message + + Installation Overrides: + --prefix= overrides the installation prefix + --exec-prefix= overrides the executable installation prefix + --libdir= overrides the library installation prefix + --includedir= overrides the header file installation prefix + + Installation Queries: + --prefix installation prefix + --exec-prefix executable installation prefix + --libdir library installation directory + --includedir header file installation directory + --version the version of the Google Test installation + + Version Queries: + --min-version=VERSION return 0 if the version is at least VERSION + --exact-version=VERSION return 0 if the version is exactly VERSION + --max-version=VERSION return 0 if the version is at most VERSION + + Compilation Flag Queries: + --cppflags compile flags specific to the C-like preprocessors + --cxxflags compile flags appropriate for C++ programs + --ldflags linker flags + --libs libraries for linking + +EOF +} + +# This function bounds our version with a min and a max. It uses some clever +# POSIX-compliant variable expansion to portably do all the work in the shell +# and avoid any dependency on a particular "sed" or "awk" implementation. +# Notable is that it will only ever compare the first 3 components of versions. +# Further components will be cleanly stripped off. All versions must be +# unadorned, so "v1.0" will *not* work. The minimum version must be in $1, and +# the max in $2. TODO(chandlerc@google.com): If this ever breaks, we should +# investigate expanding this via autom4te from AS_VERSION_COMPARE rather than +# continuing to maintain our own shell version. +check_versions() +{ + major_version=${version%%.*} + minor_version="0" + point_version="0" + if test "${version#*.}" != "${version}"; then + minor_version=${version#*.} + minor_version=${minor_version%%.*} + fi + if test "${version#*.*.}" != "${version}"; then + point_version=${version#*.*.} + point_version=${point_version%%.*} + fi + + min_version="$1" + min_major_version=${min_version%%.*} + min_minor_version="0" + min_point_version="0" + if test "${min_version#*.}" != "${min_version}"; then + min_minor_version=${min_version#*.} + min_minor_version=${min_minor_version%%.*} + fi + if test "${min_version#*.*.}" != "${min_version}"; then + min_point_version=${min_version#*.*.} + min_point_version=${min_point_version%%.*} + fi + + max_version="$2" + max_major_version=${max_version%%.*} + max_minor_version="0" + max_point_version="0" + if test "${max_version#*.}" != "${max_version}"; then + max_minor_version=${max_version#*.} + max_minor_version=${max_minor_version%%.*} + fi + if test "${max_version#*.*.}" != "${max_version}"; then + max_point_version=${max_version#*.*.} + max_point_version=${max_point_version%%.*} + fi + + test $(($major_version)) -lt $(($min_major_version)) && exit 1 + if test $(($major_version)) -eq $(($min_major_version)); then + test $(($minor_version)) -lt $(($min_minor_version)) && exit 1 + if test $(($minor_version)) -eq $(($min_minor_version)); then + test $(($point_version)) -lt $(($min_point_version)) && exit 1 + fi + fi + + test $(($major_version)) -gt $(($max_major_version)) && exit 1 + if test $(($major_version)) -eq $(($max_major_version)); then + test $(($minor_version)) -gt $(($max_minor_version)) && exit 1 + if test $(($minor_version)) -eq $(($max_minor_version)); then + test $(($point_version)) -gt $(($max_point_version)) && exit 1 + fi + fi + + exit 0 +} + +# Show the usage line when no arguments are specified. +if test $# -eq 0; then + show_usage + exit 1 +fi + +while test $# -gt 0; do + case $1 in + --usage) show_usage; exit 0;; + --help) show_help; exit 0;; + + # Installation overrides + --prefix=*) GTEST_PREFIX=${1#--prefix=};; + --exec-prefix=*) GTEST_EXEC_PREFIX=${1#--exec-prefix=};; + --libdir=*) GTEST_LIBDIR=${1#--libdir=};; + --includedir=*) GTEST_INCLUDEDIR=${1#--includedir=};; + + # Installation queries + --prefix|--exec-prefix|--libdir|--includedir|--version) + if test -n "${do_query}"; then + show_usage + exit 1 + fi + do_query=${1#--} + ;; + + # Version checking + --min-version=*) + do_check_versions=yes + min_version=${1#--min-version=} + ;; + --max-version=*) + do_check_versions=yes + max_version=${1#--max-version=} + ;; + --exact-version=*) + do_check_versions=yes + exact_version=${1#--exact-version=} + ;; + + # Compiler flag output + --cppflags) echo_cppflags=yes;; + --cxxflags) echo_cxxflags=yes;; + --ldflags) echo_ldflags=yes;; + --libs) echo_libs=yes;; + + # Everything else is an error + *) show_usage; exit 1;; + esac + shift +done + +# These have defaults filled in by the configure script but can also be +# overridden by environment variables or command line parameters. +prefix="${GTEST_PREFIX:-@prefix@}" +exec_prefix="${GTEST_EXEC_PREFIX:-@exec_prefix@}" +libdir="${GTEST_LIBDIR:-@libdir@}" +includedir="${GTEST_INCLUDEDIR:-@includedir@}" + +# We try and detect if our binary is not located at its installed location. If +# it's not, we provide variables pointing to the source and build tree rather +# than to the install tree. This allows building against a just-built gtest +# rather than an installed gtest. +bindir="@bindir@" +this_relative_bindir=`dirname $0` +this_bindir=`cd ${this_relative_bindir}; pwd -P` +if test "${this_bindir}" = "${this_bindir%${bindir}}"; then + # The path to the script doesn't end in the bindir sequence from Autoconf, + # assume that we are in a build tree. + build_dir=`dirname ${this_bindir}` + src_dir=`cd ${this_bindir}; cd @top_srcdir@; pwd -P` + + # TODO(chandlerc@google.com): This is a dangerous dependency on libtool, we + # should work to remove it, and/or remove libtool altogether, replacing it + # with direct references to the library and a link path. + gtest_libs="${build_dir}/lib/libgtest.la @PTHREAD_CFLAGS@ @PTHREAD_LIBS@" + gtest_ldflags="" + + # We provide hooks to include from either the source or build dir, where the + # build dir is always preferred. This will potentially allow us to write + # build rules for generated headers and have them automatically be preferred + # over provided versions. + gtest_cppflags="-I${build_dir}/include -I${src_dir}/include" + gtest_cxxflags="@PTHREAD_CFLAGS@" +else + # We're using an installed gtest, although it may be staged under some + # prefix. Assume (as our own libraries do) that we can resolve the prefix, + # and are present in the dynamic link paths. + gtest_ldflags="-L${libdir}" + gtest_libs="-l${name} @PTHREAD_CFLAGS@ @PTHREAD_LIBS@" + gtest_cppflags="-I${includedir}" + gtest_cxxflags="@PTHREAD_CFLAGS@" +fi + +# Do an installation query if requested. +if test -n "$do_query"; then + case $do_query in + prefix) echo $prefix; exit 0;; + exec-prefix) echo $exec_prefix; exit 0;; + libdir) echo $libdir; exit 0;; + includedir) echo $includedir; exit 0;; + version) echo $version; exit 0;; + *) show_usage; exit 1;; + esac +fi + +# Do a version check if requested. +if test "$do_check_versions" = "yes"; then + # Make sure we didn't receive a bad combination of parameters. + test "$echo_cppflags" = "yes" && show_usage && exit 1 + test "$echo_cxxflags" = "yes" && show_usage && exit 1 + test "$echo_ldflags" = "yes" && show_usage && exit 1 + test "$echo_libs" = "yes" && show_usage && exit 1 + + if test "$exact_version" != ""; then + check_versions $exact_version $exact_version + # unreachable + else + check_versions ${min_version:-0.0.0} ${max_version:-9999.9999.9999} + # unreachable + fi +fi + +# Do the output in the correct order so that these can be used in-line of +# a compiler invocation. +output="" +test "$echo_cppflags" = "yes" && output="$output $gtest_cppflags" +test "$echo_cxxflags" = "yes" && output="$output $gtest_cxxflags" +test "$echo_ldflags" = "yes" && output="$output $gtest_ldflags" +test "$echo_libs" = "yes" && output="$output $gtest_libs" +echo $output + +exit 0 diff --git a/external/gtest/scripts/pump.py b/external/gtest/scripts/pump.py new file mode 100644 index 0000000000..5efb653c20 --- /dev/null +++ b/external/gtest/scripts/pump.py @@ -0,0 +1,855 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""pump v0.2.0 - Pretty Useful for Meta Programming. + +A tool for preprocessor meta programming. Useful for generating +repetitive boilerplate code. Especially useful for writing C++ +classes, functions, macros, and templates that need to work with +various number of arguments. + +USAGE: + pump.py SOURCE_FILE + +EXAMPLES: + pump.py foo.cc.pump + Converts foo.cc.pump to foo.cc. + +GRAMMAR: + CODE ::= ATOMIC_CODE* + ATOMIC_CODE ::= $var ID = EXPRESSION + | $var ID = [[ CODE ]] + | $range ID EXPRESSION..EXPRESSION + | $for ID SEPARATOR [[ CODE ]] + | $($) + | $ID + | $(EXPRESSION) + | $if EXPRESSION [[ CODE ]] ELSE_BRANCH + | [[ CODE ]] + | RAW_CODE + SEPARATOR ::= RAW_CODE | EMPTY + ELSE_BRANCH ::= $else [[ CODE ]] + | $elif EXPRESSION [[ CODE ]] ELSE_BRANCH + | EMPTY + EXPRESSION has Python syntax. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sys + + +TOKEN_TABLE = [ + (re.compile(r'\$var\s+'), '$var'), + (re.compile(r'\$elif\s+'), '$elif'), + (re.compile(r'\$else\s+'), '$else'), + (re.compile(r'\$for\s+'), '$for'), + (re.compile(r'\$if\s+'), '$if'), + (re.compile(r'\$range\s+'), '$range'), + (re.compile(r'\$[_A-Za-z]\w*'), '$id'), + (re.compile(r'\$\(\$\)'), '$($)'), + (re.compile(r'\$'), '$'), + (re.compile(r'\[\[\n?'), '[['), + (re.compile(r'\]\]\n?'), ']]'), + ] + + +class Cursor: + """Represents a position (line and column) in a text file.""" + + def __init__(self, line=-1, column=-1): + self.line = line + self.column = column + + def __eq__(self, rhs): + return self.line == rhs.line and self.column == rhs.column + + def __ne__(self, rhs): + return not self == rhs + + def __lt__(self, rhs): + return self.line < rhs.line or ( + self.line == rhs.line and self.column < rhs.column) + + def __le__(self, rhs): + return self < rhs or self == rhs + + def __gt__(self, rhs): + return rhs < self + + def __ge__(self, rhs): + return rhs <= self + + def __str__(self): + if self == Eof(): + return 'EOF' + else: + return '%s(%s)' % (self.line + 1, self.column) + + def __add__(self, offset): + return Cursor(self.line, self.column + offset) + + def __sub__(self, offset): + return Cursor(self.line, self.column - offset) + + def Clone(self): + """Returns a copy of self.""" + + return Cursor(self.line, self.column) + + +# Special cursor to indicate the end-of-file. +def Eof(): + """Returns the special cursor to denote the end-of-file.""" + return Cursor(-1, -1) + + +class Token: + """Represents a token in a Pump source file.""" + + def __init__(self, start=None, end=None, value=None, token_type=None): + if start is None: + self.start = Eof() + else: + self.start = start + if end is None: + self.end = Eof() + else: + self.end = end + self.value = value + self.token_type = token_type + + def __str__(self): + return 'Token @%s: \'%s\' type=%s' % ( + self.start, self.value, self.token_type) + + def Clone(self): + """Returns a copy of self.""" + + return Token(self.start.Clone(), self.end.Clone(), self.value, + self.token_type) + + +def StartsWith(lines, pos, string): + """Returns True iff the given position in lines starts with 'string'.""" + + return lines[pos.line][pos.column:].startswith(string) + + +def FindFirstInLine(line, token_table): + best_match_start = -1 + for (regex, token_type) in token_table: + m = regex.search(line) + if m: + # We found regex in lines + if best_match_start < 0 or m.start() < best_match_start: + best_match_start = m.start() + best_match_length = m.end() - m.start() + best_match_token_type = token_type + + if best_match_start < 0: + return None + + return (best_match_start, best_match_length, best_match_token_type) + + +def FindFirst(lines, token_table, cursor): + """Finds the first occurrence of any string in strings in lines.""" + + start = cursor.Clone() + cur_line_number = cursor.line + for line in lines[start.line:]: + if cur_line_number == start.line: + line = line[start.column:] + m = FindFirstInLine(line, token_table) + if m: + # We found a regex in line. + (start_column, length, token_type) = m + if cur_line_number == start.line: + start_column += start.column + found_start = Cursor(cur_line_number, start_column) + found_end = found_start + length + return MakeToken(lines, found_start, found_end, token_type) + cur_line_number += 1 + # We failed to find str in lines + return None + + +def SubString(lines, start, end): + """Returns a substring in lines.""" + + if end == Eof(): + end = Cursor(len(lines) - 1, len(lines[-1])) + + if start >= end: + return '' + + if start.line == end.line: + return lines[start.line][start.column:end.column] + + result_lines = ([lines[start.line][start.column:]] + + lines[start.line + 1:end.line] + + [lines[end.line][:end.column]]) + return ''.join(result_lines) + + +def StripMetaComments(str): + """Strip meta comments from each line in the given string.""" + + # First, completely remove lines containing nothing but a meta + # comment, including the trailing \n. + str = re.sub(r'^\s*\$\$.*\n', '', str) + + # Then, remove meta comments from contentful lines. + return re.sub(r'\s*\$\$.*', '', str) + + +def MakeToken(lines, start, end, token_type): + """Creates a new instance of Token.""" + + return Token(start, end, SubString(lines, start, end), token_type) + + +def ParseToken(lines, pos, regex, token_type): + line = lines[pos.line][pos.column:] + m = regex.search(line) + if m and not m.start(): + return MakeToken(lines, pos, pos + m.end(), token_type) + else: + print 'ERROR: %s expected at %s.' % (token_type, pos) + sys.exit(1) + + +ID_REGEX = re.compile(r'[_A-Za-z]\w*') +EQ_REGEX = re.compile(r'=') +REST_OF_LINE_REGEX = re.compile(r'.*?(?=$|\$\$)') +OPTIONAL_WHITE_SPACES_REGEX = re.compile(r'\s*') +WHITE_SPACE_REGEX = re.compile(r'\s') +DOT_DOT_REGEX = re.compile(r'\.\.') + + +def Skip(lines, pos, regex): + line = lines[pos.line][pos.column:] + m = re.search(regex, line) + if m and not m.start(): + return pos + m.end() + else: + return pos + + +def SkipUntil(lines, pos, regex, token_type): + line = lines[pos.line][pos.column:] + m = re.search(regex, line) + if m: + return pos + m.start() + else: + print ('ERROR: %s expected on line %s after column %s.' % + (token_type, pos.line + 1, pos.column)) + sys.exit(1) + + +def ParseExpTokenInParens(lines, pos): + def ParseInParens(pos): + pos = Skip(lines, pos, OPTIONAL_WHITE_SPACES_REGEX) + pos = Skip(lines, pos, r'\(') + pos = Parse(pos) + pos = Skip(lines, pos, r'\)') + return pos + + def Parse(pos): + pos = SkipUntil(lines, pos, r'\(|\)', ')') + if SubString(lines, pos, pos + 1) == '(': + pos = Parse(pos + 1) + pos = Skip(lines, pos, r'\)') + return Parse(pos) + else: + return pos + + start = pos.Clone() + pos = ParseInParens(pos) + return MakeToken(lines, start, pos, 'exp') + + +def RStripNewLineFromToken(token): + if token.value.endswith('\n'): + return Token(token.start, token.end, token.value[:-1], token.token_type) + else: + return token + + +def TokenizeLines(lines, pos): + while True: + found = FindFirst(lines, TOKEN_TABLE, pos) + if not found: + yield MakeToken(lines, pos, Eof(), 'code') + return + + if found.start == pos: + prev_token = None + prev_token_rstripped = None + else: + prev_token = MakeToken(lines, pos, found.start, 'code') + prev_token_rstripped = RStripNewLineFromToken(prev_token) + + if found.token_type == '$var': + if prev_token_rstripped: + yield prev_token_rstripped + yield found + id_token = ParseToken(lines, found.end, ID_REGEX, 'id') + yield id_token + pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX) + + eq_token = ParseToken(lines, pos, EQ_REGEX, '=') + yield eq_token + pos = Skip(lines, eq_token.end, r'\s*') + + if SubString(lines, pos, pos + 2) != '[[': + exp_token = ParseToken(lines, pos, REST_OF_LINE_REGEX, 'exp') + yield exp_token + pos = Cursor(exp_token.end.line + 1, 0) + elif found.token_type == '$for': + if prev_token_rstripped: + yield prev_token_rstripped + yield found + id_token = ParseToken(lines, found.end, ID_REGEX, 'id') + yield id_token + pos = Skip(lines, id_token.end, WHITE_SPACE_REGEX) + elif found.token_type == '$range': + if prev_token_rstripped: + yield prev_token_rstripped + yield found + id_token = ParseToken(lines, found.end, ID_REGEX, 'id') + yield id_token + pos = Skip(lines, id_token.end, OPTIONAL_WHITE_SPACES_REGEX) + + dots_pos = SkipUntil(lines, pos, DOT_DOT_REGEX, '..') + yield MakeToken(lines, pos, dots_pos, 'exp') + yield MakeToken(lines, dots_pos, dots_pos + 2, '..') + pos = dots_pos + 2 + new_pos = Cursor(pos.line + 1, 0) + yield MakeToken(lines, pos, new_pos, 'exp') + pos = new_pos + elif found.token_type == '$': + if prev_token: + yield prev_token + yield found + exp_token = ParseExpTokenInParens(lines, found.end) + yield exp_token + pos = exp_token.end + elif (found.token_type == ']]' or found.token_type == '$if' or + found.token_type == '$elif' or found.token_type == '$else'): + if prev_token_rstripped: + yield prev_token_rstripped + yield found + pos = found.end + else: + if prev_token: + yield prev_token + yield found + pos = found.end + + +def Tokenize(s): + """A generator that yields the tokens in the given string.""" + if s != '': + lines = s.splitlines(True) + for token in TokenizeLines(lines, Cursor(0, 0)): + yield token + + +class CodeNode: + def __init__(self, atomic_code_list=None): + self.atomic_code = atomic_code_list + + +class VarNode: + def __init__(self, identifier=None, atomic_code=None): + self.identifier = identifier + self.atomic_code = atomic_code + + +class RangeNode: + def __init__(self, identifier=None, exp1=None, exp2=None): + self.identifier = identifier + self.exp1 = exp1 + self.exp2 = exp2 + + +class ForNode: + def __init__(self, identifier=None, sep=None, code=None): + self.identifier = identifier + self.sep = sep + self.code = code + + +class ElseNode: + def __init__(self, else_branch=None): + self.else_branch = else_branch + + +class IfNode: + def __init__(self, exp=None, then_branch=None, else_branch=None): + self.exp = exp + self.then_branch = then_branch + self.else_branch = else_branch + + +class RawCodeNode: + def __init__(self, token=None): + self.raw_code = token + + +class LiteralDollarNode: + def __init__(self, token): + self.token = token + + +class ExpNode: + def __init__(self, token, python_exp): + self.token = token + self.python_exp = python_exp + + +def PopFront(a_list): + head = a_list[0] + a_list[:1] = [] + return head + + +def PushFront(a_list, elem): + a_list[:0] = [elem] + + +def PopToken(a_list, token_type=None): + token = PopFront(a_list) + if token_type is not None and token.token_type != token_type: + print 'ERROR: %s expected at %s' % (token_type, token.start) + print 'ERROR: %s found instead' % (token,) + sys.exit(1) + + return token + + +def PeekToken(a_list): + if not a_list: + return None + + return a_list[0] + + +def ParseExpNode(token): + python_exp = re.sub(r'([_A-Za-z]\w*)', r'self.GetValue("\1")', token.value) + return ExpNode(token, python_exp) + + +def ParseElseNode(tokens): + def Pop(token_type=None): + return PopToken(tokens, token_type) + + next = PeekToken(tokens) + if not next: + return None + if next.token_type == '$else': + Pop('$else') + Pop('[[') + code_node = ParseCodeNode(tokens) + Pop(']]') + return code_node + elif next.token_type == '$elif': + Pop('$elif') + exp = Pop('code') + Pop('[[') + code_node = ParseCodeNode(tokens) + Pop(']]') + inner_else_node = ParseElseNode(tokens) + return CodeNode([IfNode(ParseExpNode(exp), code_node, inner_else_node)]) + elif not next.value.strip(): + Pop('code') + return ParseElseNode(tokens) + else: + return None + + +def ParseAtomicCodeNode(tokens): + def Pop(token_type=None): + return PopToken(tokens, token_type) + + head = PopFront(tokens) + t = head.token_type + if t == 'code': + return RawCodeNode(head) + elif t == '$var': + id_token = Pop('id') + Pop('=') + next = PeekToken(tokens) + if next.token_type == 'exp': + exp_token = Pop() + return VarNode(id_token, ParseExpNode(exp_token)) + Pop('[[') + code_node = ParseCodeNode(tokens) + Pop(']]') + return VarNode(id_token, code_node) + elif t == '$for': + id_token = Pop('id') + next_token = PeekToken(tokens) + if next_token.token_type == 'code': + sep_token = next_token + Pop('code') + else: + sep_token = None + Pop('[[') + code_node = ParseCodeNode(tokens) + Pop(']]') + return ForNode(id_token, sep_token, code_node) + elif t == '$if': + exp_token = Pop('code') + Pop('[[') + code_node = ParseCodeNode(tokens) + Pop(']]') + else_node = ParseElseNode(tokens) + return IfNode(ParseExpNode(exp_token), code_node, else_node) + elif t == '$range': + id_token = Pop('id') + exp1_token = Pop('exp') + Pop('..') + exp2_token = Pop('exp') + return RangeNode(id_token, ParseExpNode(exp1_token), + ParseExpNode(exp2_token)) + elif t == '$id': + return ParseExpNode(Token(head.start + 1, head.end, head.value[1:], 'id')) + elif t == '$($)': + return LiteralDollarNode(head) + elif t == '$': + exp_token = Pop('exp') + return ParseExpNode(exp_token) + elif t == '[[': + code_node = ParseCodeNode(tokens) + Pop(']]') + return code_node + else: + PushFront(tokens, head) + return None + + +def ParseCodeNode(tokens): + atomic_code_list = [] + while True: + if not tokens: + break + atomic_code_node = ParseAtomicCodeNode(tokens) + if atomic_code_node: + atomic_code_list.append(atomic_code_node) + else: + break + return CodeNode(atomic_code_list) + + +def ParseToAST(pump_src_text): + """Convert the given Pump source text into an AST.""" + tokens = list(Tokenize(pump_src_text)) + code_node = ParseCodeNode(tokens) + return code_node + + +class Env: + def __init__(self): + self.variables = [] + self.ranges = [] + + def Clone(self): + clone = Env() + clone.variables = self.variables[:] + clone.ranges = self.ranges[:] + return clone + + def PushVariable(self, var, value): + # If value looks like an int, store it as an int. + try: + int_value = int(value) + if ('%s' % int_value) == value: + value = int_value + except Exception: + pass + self.variables[:0] = [(var, value)] + + def PopVariable(self): + self.variables[:1] = [] + + def PushRange(self, var, lower, upper): + self.ranges[:0] = [(var, lower, upper)] + + def PopRange(self): + self.ranges[:1] = [] + + def GetValue(self, identifier): + for (var, value) in self.variables: + if identifier == var: + return value + + print 'ERROR: meta variable %s is undefined.' % (identifier,) + sys.exit(1) + + def EvalExp(self, exp): + try: + result = eval(exp.python_exp) + except Exception, e: + print 'ERROR: caught exception %s: %s' % (e.__class__.__name__, e) + print ('ERROR: failed to evaluate meta expression %s at %s' % + (exp.python_exp, exp.token.start)) + sys.exit(1) + return result + + def GetRange(self, identifier): + for (var, lower, upper) in self.ranges: + if identifier == var: + return (lower, upper) + + print 'ERROR: range %s is undefined.' % (identifier,) + sys.exit(1) + + +class Output: + def __init__(self): + self.string = '' + + def GetLastLine(self): + index = self.string.rfind('\n') + if index < 0: + return '' + + return self.string[index + 1:] + + def Append(self, s): + self.string += s + + +def RunAtomicCode(env, node, output): + if isinstance(node, VarNode): + identifier = node.identifier.value.strip() + result = Output() + RunAtomicCode(env.Clone(), node.atomic_code, result) + value = result.string + env.PushVariable(identifier, value) + elif isinstance(node, RangeNode): + identifier = node.identifier.value.strip() + lower = int(env.EvalExp(node.exp1)) + upper = int(env.EvalExp(node.exp2)) + env.PushRange(identifier, lower, upper) + elif isinstance(node, ForNode): + identifier = node.identifier.value.strip() + if node.sep is None: + sep = '' + else: + sep = node.sep.value + (lower, upper) = env.GetRange(identifier) + for i in range(lower, upper + 1): + new_env = env.Clone() + new_env.PushVariable(identifier, i) + RunCode(new_env, node.code, output) + if i != upper: + output.Append(sep) + elif isinstance(node, RawCodeNode): + output.Append(node.raw_code.value) + elif isinstance(node, IfNode): + cond = env.EvalExp(node.exp) + if cond: + RunCode(env.Clone(), node.then_branch, output) + elif node.else_branch is not None: + RunCode(env.Clone(), node.else_branch, output) + elif isinstance(node, ExpNode): + value = env.EvalExp(node) + output.Append('%s' % (value,)) + elif isinstance(node, LiteralDollarNode): + output.Append('$') + elif isinstance(node, CodeNode): + RunCode(env.Clone(), node, output) + else: + print 'BAD' + print node + sys.exit(1) + + +def RunCode(env, code_node, output): + for atomic_code in code_node.atomic_code: + RunAtomicCode(env, atomic_code, output) + + +def IsSingleLineComment(cur_line): + return '//' in cur_line + + +def IsInPreprocessorDirective(prev_lines, cur_line): + if cur_line.lstrip().startswith('#'): + return True + return prev_lines and prev_lines[-1].endswith('\\') + + +def WrapComment(line, output): + loc = line.find('//') + before_comment = line[:loc].rstrip() + if before_comment == '': + indent = loc + else: + output.append(before_comment) + indent = len(before_comment) - len(before_comment.lstrip()) + prefix = indent*' ' + '// ' + max_len = 80 - len(prefix) + comment = line[loc + 2:].strip() + segs = [seg for seg in re.split(r'(\w+\W*)', comment) if seg != ''] + cur_line = '' + for seg in segs: + if len((cur_line + seg).rstrip()) < max_len: + cur_line += seg + else: + if cur_line.strip() != '': + output.append(prefix + cur_line.rstrip()) + cur_line = seg.lstrip() + if cur_line.strip() != '': + output.append(prefix + cur_line.strip()) + + +def WrapCode(line, line_concat, output): + indent = len(line) - len(line.lstrip()) + prefix = indent*' ' # Prefix of the current line + max_len = 80 - indent - len(line_concat) # Maximum length of the current line + new_prefix = prefix + 4*' ' # Prefix of a continuation line + new_max_len = max_len - 4 # Maximum length of a continuation line + # Prefers to wrap a line after a ',' or ';'. + segs = [seg for seg in re.split(r'([^,;]+[,;]?)', line.strip()) if seg != ''] + cur_line = '' # The current line without leading spaces. + for seg in segs: + # If the line is still too long, wrap at a space. + while cur_line == '' and len(seg.strip()) > max_len: + seg = seg.lstrip() + split_at = seg.rfind(' ', 0, max_len) + output.append(prefix + seg[:split_at].strip() + line_concat) + seg = seg[split_at + 1:] + prefix = new_prefix + max_len = new_max_len + + if len((cur_line + seg).rstrip()) < max_len: + cur_line = (cur_line + seg).lstrip() + else: + output.append(prefix + cur_line.rstrip() + line_concat) + prefix = new_prefix + max_len = new_max_len + cur_line = seg.lstrip() + if cur_line.strip() != '': + output.append(prefix + cur_line.strip()) + + +def WrapPreprocessorDirective(line, output): + WrapCode(line, ' \\', output) + + +def WrapPlainCode(line, output): + WrapCode(line, '', output) + + +def IsMultiLineIWYUPragma(line): + return re.search(r'/\* IWYU pragma: ', line) + + +def IsHeaderGuardIncludeOrOneLineIWYUPragma(line): + return (re.match(r'^#(ifndef|define|endif\s*//)\s*[\w_]+\s*$', line) or + re.match(r'^#include\s', line) or + # Don't break IWYU pragmas, either; that causes iwyu.py problems. + re.search(r'// IWYU pragma: ', line)) + + +def WrapLongLine(line, output): + line = line.rstrip() + if len(line) <= 80: + output.append(line) + elif IsSingleLineComment(line): + if IsHeaderGuardIncludeOrOneLineIWYUPragma(line): + # The style guide made an exception to allow long header guard lines, + # includes and IWYU pragmas. + output.append(line) + else: + WrapComment(line, output) + elif IsInPreprocessorDirective(output, line): + if IsHeaderGuardIncludeOrOneLineIWYUPragma(line): + # The style guide made an exception to allow long header guard lines, + # includes and IWYU pragmas. + output.append(line) + else: + WrapPreprocessorDirective(line, output) + elif IsMultiLineIWYUPragma(line): + output.append(line) + else: + WrapPlainCode(line, output) + + +def BeautifyCode(string): + lines = string.splitlines() + output = [] + for line in lines: + WrapLongLine(line, output) + output2 = [line.rstrip() for line in output] + return '\n'.join(output2) + '\n' + + +def ConvertFromPumpSource(src_text): + """Return the text generated from the given Pump source text.""" + ast = ParseToAST(StripMetaComments(src_text)) + output = Output() + RunCode(Env(), ast, output) + return BeautifyCode(output.string) + + +def main(argv): + if len(argv) == 1: + print __doc__ + sys.exit(1) + + file_path = argv[-1] + output_str = ConvertFromPumpSource(file(file_path, 'r').read()) + if file_path.endswith('.pump'): + output_file_path = file_path[:-5] + else: + output_file_path = '-' + if output_file_path == '-': + print output_str, + else: + output_file = file(output_file_path, 'w') + output_file.write('// This file was GENERATED by command:\n') + output_file.write('// %s %s\n' % + (os.path.basename(__file__), os.path.basename(file_path))) + output_file.write('// DO NOT EDIT BY HAND!!!\n\n') + output_file.write(output_str) + output_file.close() + + +if __name__ == '__main__': + main(sys.argv) diff --git a/external/gtest/scripts/test/Makefile b/external/gtest/scripts/test/Makefile new file mode 100644 index 0000000000..cdff584637 --- /dev/null +++ b/external/gtest/scripts/test/Makefile @@ -0,0 +1,59 @@ +# A Makefile for fusing Google Test and building a sample test against it. +# +# SYNOPSIS: +# +# make [all] - makes everything. +# make TARGET - makes the given target. +# make check - makes everything and runs the built sample test. +# make clean - removes all files generated by make. + +# Points to the root of fused Google Test, relative to where this file is. +FUSED_GTEST_DIR = output + +# Paths to the fused gtest files. +FUSED_GTEST_H = $(FUSED_GTEST_DIR)/gtest/gtest.h +FUSED_GTEST_ALL_CC = $(FUSED_GTEST_DIR)/gtest/gtest-all.cc + +# Where to find the sample test. +SAMPLE_DIR = ../../samples + +# Where to find gtest_main.cc. +GTEST_MAIN_CC = ../../src/gtest_main.cc + +# Flags passed to the preprocessor. +# We have no idea here whether pthreads is available in the system, so +# disable its use. +CPPFLAGS += -I$(FUSED_GTEST_DIR) -DGTEST_HAS_PTHREAD=0 + +# Flags passed to the C++ compiler. +CXXFLAGS += -g + +all : sample1_unittest + +check : all + ./sample1_unittest + +clean : + rm -rf $(FUSED_GTEST_DIR) sample1_unittest *.o + +$(FUSED_GTEST_H) : + ../fuse_gtest_files.py $(FUSED_GTEST_DIR) + +$(FUSED_GTEST_ALL_CC) : + ../fuse_gtest_files.py $(FUSED_GTEST_DIR) + +gtest-all.o : $(FUSED_GTEST_H) $(FUSED_GTEST_ALL_CC) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(FUSED_GTEST_DIR)/gtest/gtest-all.cc + +gtest_main.o : $(FUSED_GTEST_H) $(GTEST_MAIN_CC) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(GTEST_MAIN_CC) + +sample1.o : $(SAMPLE_DIR)/sample1.cc $(SAMPLE_DIR)/sample1.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(SAMPLE_DIR)/sample1.cc + +sample1_unittest.o : $(SAMPLE_DIR)/sample1_unittest.cc \ + $(SAMPLE_DIR)/sample1.h $(FUSED_GTEST_H) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(SAMPLE_DIR)/sample1_unittest.cc + +sample1_unittest : sample1.o sample1_unittest.o gtest-all.o gtest_main.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $^ -o $@ diff --git a/tests/gtest/src/gtest-all.cc b/external/gtest/src/gtest-all.cc similarity index 100% rename from tests/gtest/src/gtest-all.cc rename to external/gtest/src/gtest-all.cc diff --git a/tests/gtest/src/gtest-death-test.cc b/external/gtest/src/gtest-death-test.cc similarity index 82% rename from tests/gtest/src/gtest-death-test.cc rename to external/gtest/src/gtest-death-test.cc index 8b2e4131ca..a6023fce4f 100644 --- a/tests/gtest/src/gtest-death-test.cc +++ b/external/gtest/src/gtest-death-test.cc @@ -43,6 +43,11 @@ # include # include # include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + # include # if GTEST_OS_WINDOWS @@ -52,6 +57,10 @@ # include # endif // GTEST_OS_WINDOWS +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + #endif // GTEST_HAS_DEATH_TEST #include "gtest/gtest-message.h" @@ -100,13 +109,42 @@ GTEST_DEFINE_string_( "Indicates the file, line number, temporal index of " "the single death test to run, and a file descriptor to " "which a success code may be sent, all separated by " - "colons. This flag is specified if and only if the current " + "the '|' characters. This flag is specified if and only if the current " "process is a sub-process launched for running a thread-safe " "death test. FOR INTERNAL USE ONLY."); } // namespace internal #if GTEST_HAS_DEATH_TEST +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + // ExitedWithCode constructor. ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { } @@ -141,7 +179,7 @@ namespace internal { // Generates a textual description of a given exit code, in the format // specified by wait(2). -static String ExitSummary(int exit_code) { +static std::string ExitSummary(int exit_code) { Message m; # if GTEST_OS_WINDOWS @@ -176,7 +214,7 @@ bool ExitedUnsuccessfully(int exit_status) { // one thread running, or cannot determine the number of threads, prior // to executing the given statement. It is the responsibility of the // caller not to pass a thread_count of 1. -static String DeathTestThreadWarning(size_t thread_count) { +static std::string DeathTestThreadWarning(size_t thread_count) { Message msg; msg << "Death tests use fork(), which is unsafe particularly" << " in a threaded context. For this test, " << GTEST_NAME_ << " "; @@ -210,7 +248,7 @@ enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; // message is propagated back to the parent process. Otherwise, the // message is simply printed to stderr. In either case, the program // then exits with status 1. -void DeathTestAbort(const String& message) { +void DeathTestAbort(const std::string& message) { // On a POSIX system, this function may be called from a threadsafe-style // death test child process, which operates on a very small stack. Use // the heap for any additional non-minuscule memory requirements. @@ -234,9 +272,10 @@ void DeathTestAbort(const String& message) { # define GTEST_DEATH_TEST_CHECK_(expression) \ do { \ if (!::testing::internal::IsTrue(expression)) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s", \ - __FILE__, __LINE__, #expression)); \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ } \ } while (::testing::internal::AlwaysFalse()) @@ -254,15 +293,16 @@ void DeathTestAbort(const String& message) { gtest_retval = (expression); \ } while (gtest_retval == -1 && errno == EINTR); \ if (gtest_retval == -1) { \ - DeathTestAbort(::testing::internal::String::Format( \ - "CHECK failed: File %s, line %d: %s != -1", \ - __FILE__, __LINE__, #expression)); \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ } \ } while (::testing::internal::AlwaysFalse()) // Returns the message describing the last system error in errno. -String GetLastErrnoDescription() { - return String(errno == 0 ? "" : posix::StrError(errno)); +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); } // This is called from a death test parent process to read a failure @@ -312,11 +352,11 @@ const char* DeathTest::LastMessage() { return last_death_test_message_.c_str(); } -void DeathTest::set_last_death_test_message(const String& message) { +void DeathTest::set_last_death_test_message(const std::string& message) { last_death_test_message_ = message; } -String DeathTest::last_death_test_message_; +std::string DeathTest::last_death_test_message_; // Provides cross platform implementation for some death functionality. class DeathTestImpl : public DeathTest { @@ -491,7 +531,7 @@ bool DeathTestImpl::Passed(bool status_ok) { if (!spawned()) return false; - const String error_message = GetCapturedStderr(); + const std::string error_message = GetCapturedStderr(); bool success = false; Message buffer; @@ -673,22 +713,19 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() { FALSE, // The initial state is non-signalled. NULL)); // The even is unnamed. GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); - const String filter_flag = String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), - info->name()); - const String internal_flag = String::Format( - "--%s%s=%s|%d|%d|%u|%Iu|%Iu", - GTEST_FLAG_PREFIX_, - kInternalRunDeathTestFlag, - file_, line_, - death_test_index, - static_cast(::GetCurrentProcessId()), - // size_t has the same with as pointers on both 32-bit and 64-bit + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit // Windows platforms. // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. - reinterpret_cast(write_handle), - reinterpret_cast(event_handle_.Get())); + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); char executable_path[_MAX_PATH + 1]; // NOLINT GTEST_DEATH_TEST_CHECK_( @@ -696,10 +733,9 @@ DeathTest::TestRole WindowsDeathTest::AssumeRole() { executable_path, _MAX_PATH)); - String command_line = String::Format("%s %s \"%s\"", - ::GetCommandLineA(), - filter_flag.c_str(), - internal_flag.c_str()); + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; DeathTest::set_last_death_test_message(""); @@ -816,6 +852,7 @@ DeathTest::TestRole NoExecDeathTest::AssumeRole() { // Event forwarding to the listeners of event listener API mush be shut // down in death test subprocesses. GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; return EXECUTE_TEST; } else { GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); @@ -835,6 +872,11 @@ class ExecDeathTest : public ForkingDeathTest { ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } virtual TestRole AssumeRole(); private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } // The name of the file in which the death test is located. const char* const file_; // The line number on which the death test is located. @@ -869,6 +911,7 @@ class Arguments { char* const* Argv() { return &args_[0]; } + private: std::vector args_; }; @@ -894,6 +937,7 @@ extern "C" char** environ; inline char** GetEnviron() { return environ; } # endif // GTEST_OS_MAC +# if !GTEST_OS_QNX // The main function for a threadsafe-style death test child process. // This function is called in a clone()-ed process and thus must avoid // any potentially unsafe operations like malloc or libc functions. @@ -908,9 +952,8 @@ static int ExecDeathTestChildMain(void* child_arg) { UnitTest::GetInstance()->original_working_dir(); // We can safely call chdir() as it's a direct system call. if (chdir(original_dir) != 0) { - DeathTestAbort(String::Format("chdir(\"%s\") failed: %s", - original_dir, - GetLastErrnoDescription().c_str())); + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); return EXIT_FAILURE; } @@ -920,12 +963,12 @@ static int ExecDeathTestChildMain(void* child_arg) { // invoke the test program via a valid path that contains at least // one path separator. execve(args->argv[0], args->argv, GetEnviron()); - DeathTestAbort(String::Format("execve(%s, ...) in %s failed: %s", - args->argv[0], - original_dir, - GetLastErrnoDescription().c_str())); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); return EXIT_FAILURE; } +# endif // !GTEST_OS_QNX // Two utility routines that together determine the direction the stack // grows. @@ -936,25 +979,75 @@ static int ExecDeathTestChildMain(void* child_arg) { // GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining // StackLowerThanAddress into StackGrowsDown, which then doesn't give // correct answer. -bool StackLowerThanAddress(const void* ptr) GTEST_NO_INLINE_; -bool StackLowerThanAddress(const void* ptr) { +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { int dummy; - return &dummy < ptr; + *result = (&dummy < ptr); } bool StackGrowsDown() { int dummy; - return StackLowerThanAddress(&dummy); + bool result; + StackLowerThanAddress(&dummy, &result); + return result; } -// A threadsafe implementation of fork(2) for threadsafe-style death tests -// that uses clone(2). It dies with an error message if anything goes -// wrong. -static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { ExecDeathTestArgs args = { argv, close_fd }; pid_t child_pid = -1; -# if GTEST_HAS_CLONE +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE const bool use_fork = GTEST_FLAG(death_test_use_fork); if (!use_fork) { @@ -964,21 +1057,37 @@ static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; void* const stack_top = - static_cast(stack) + (stack_grows_down ? stack_size : 0); + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); } -# else +# else const bool use_fork = true; -# endif // GTEST_HAS_CLONE +# endif // GTEST_HAS_CLONE if (use_fork && (child_pid = fork()) == 0) { ExecDeathTestChildMain(&args); _exit(0); } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX GTEST_DEATH_TEST_CHECK_(child_pid != -1); return child_pid; @@ -1006,16 +1115,16 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() { // it be closed when the child process does an exec: GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); - const String filter_flag = - String::Format("--%s%s=%s.%s", - GTEST_FLAG_PREFIX_, kFilterFlag, - info->test_case_name(), info->name()); - const String internal_flag = - String::Format("--%s%s=%s|%d|%d|%d", - GTEST_FLAG_PREFIX_, kInternalRunDeathTestFlag, - file_, line_, death_test_index, pipe_fd[1]); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); Arguments args; - args.AddArguments(GetArgvs()); + args.AddArguments(GetArgvsForDeathTestChildProcess()); args.AddArgument(filter_flag.c_str()); args.AddArgument(internal_flag.c_str()); @@ -1026,7 +1135,7 @@ DeathTest::TestRole ExecDeathTest::AssumeRole() { // is necessary. FlushInfoLog(); - const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); set_child_pid(child_pid); set_read_fd(pipe_fd[0]); @@ -1052,9 +1161,10 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, if (flag != NULL) { if (death_test_index > flag->index()) { - DeathTest::set_last_death_test_message(String::Format( - "Death test count (%d) somehow exceeded expected maximum (%d)", - death_test_index, flag->index())); + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); return false; } @@ -1083,9 +1193,9 @@ bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, # endif // GTEST_OS_WINDOWS else { // NOLINT - this is more readable than unbalanced brackets inside #if. - DeathTest::set_last_death_test_message(String::Format( - "Unknown death test style \"%s\" encountered", - GTEST_FLAG(death_test_style).c_str())); + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); return false; } @@ -1123,8 +1233,8 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, FALSE, // Non-inheritable. parent_process_id)); if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { - DeathTestAbort(String::Format("Unable to open parent process %u", - parent_process_id)); + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); } // TODO(vladl@google.com): Replace the following check with a @@ -1144,9 +1254,10 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, // DUPLICATE_SAME_ACCESS is used. FALSE, // Request non-inheritable handler. DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the pipe handle %Iu from the parent process %u", - write_handle_as_size_t, parent_process_id)); + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); } const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); @@ -1157,17 +1268,18 @@ int GetStatusFileDescriptor(unsigned int parent_process_id, 0x0, FALSE, DUPLICATE_SAME_ACCESS)) { - DeathTestAbort(String::Format( - "Unable to duplicate the event handle %Iu from the parent process %u", - event_handle_as_size_t, parent_process_id)); + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); } const int write_fd = ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); if (write_fd == -1) { - DeathTestAbort(String::Format( - "Unable to convert pipe handle %Iu to a file descriptor", - write_handle_as_size_t)); + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); } // Signals the parent that the write end of the pipe has been acquired @@ -1204,9 +1316,8 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { || !ParseNaturalNumber(fields[3], &parent_process_id) || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); } write_fd = GetStatusFileDescriptor(parent_process_id, write_handle_as_size_t, @@ -1217,9 +1328,8 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { || !ParseNaturalNumber(fields[1], &line) || !ParseNaturalNumber(fields[2], &index) || !ParseNaturalNumber(fields[3], &write_fd)) { - DeathTestAbort(String::Format( - "Bad --gtest_internal_run_death_test flag: %s", - GTEST_FLAG(internal_run_death_test).c_str())); + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); } # endif // GTEST_OS_WINDOWS diff --git a/tests/gtest/src/gtest-filepath.cc b/external/gtest/src/gtest-filepath.cc similarity index 94% rename from tests/gtest/src/gtest-filepath.cc rename to external/gtest/src/gtest-filepath.cc index 91b2571380..6be58b6fca 100644 --- a/tests/gtest/src/gtest-filepath.cc +++ b/external/gtest/src/gtest-filepath.cc @@ -29,6 +29,7 @@ // // Authors: keith.ray@gmail.com (Keith Ray) +#include "gtest/gtest-message.h" #include "gtest/internal/gtest-filepath.h" #include "gtest/internal/gtest-port.h" @@ -39,8 +40,8 @@ #elif GTEST_OS_WINDOWS # include # include -#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL -// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h # include #else # include @@ -116,9 +117,10 @@ FilePath FilePath::GetCurrentDir() { // FilePath("dir/file"). If a case-insensitive extension is not // found, returns a copy of the original FilePath. FilePath FilePath::RemoveExtension(const char* extension) const { - String dot_extension(String::Format(".%s", extension)); - if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { - return FilePath(String(pathname_.c_str(), pathname_.length() - 4)); + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); } return *this; } @@ -147,7 +149,7 @@ const char* FilePath::FindLastPathSeparator() const { // On Windows platform, '\' is the path separator, otherwise it is '/'. FilePath FilePath::RemoveDirectoryName() const { const char* const last_sep = FindLastPathSeparator(); - return last_sep ? FilePath(String(last_sep + 1)) : *this; + return last_sep ? FilePath(last_sep + 1) : *this; } // RemoveFileName returns the directory path with the filename removed. @@ -158,9 +160,9 @@ FilePath FilePath::RemoveDirectoryName() const { // On Windows platform, '\' is the path separator, otherwise it is '/'. FilePath FilePath::RemoveFileName() const { const char* const last_sep = FindLastPathSeparator(); - String dir; + std::string dir; if (last_sep) { - dir = String(c_str(), last_sep + 1 - c_str()); + dir = std::string(c_str(), last_sep + 1 - c_str()); } else { dir = kCurrentDirectoryString; } @@ -177,11 +179,12 @@ FilePath FilePath::MakeFileName(const FilePath& directory, const FilePath& base_name, int number, const char* extension) { - String file; + std::string file; if (number == 0) { - file = String::Format("%s.%s", base_name.c_str(), extension); + file = base_name.string() + "." + extension; } else { - file = String::Format("%s_%d.%s", base_name.c_str(), number, extension); + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; } return ConcatPaths(directory, FilePath(file)); } @@ -193,8 +196,7 @@ FilePath FilePath::ConcatPaths(const FilePath& directory, if (directory.IsEmpty()) return relative_path; const FilePath dir(directory.RemoveTrailingPathSeparator()); - return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator, - relative_path.c_str())); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); } // Returns true if pathname describes something findable in the file-system, @@ -338,7 +340,7 @@ bool FilePath::CreateFolder() const { // On Windows platform, uses \ as the separator, other platforms use /. FilePath FilePath::RemoveTrailingPathSeparator() const { return IsDirectory() - ? FilePath(String(pathname_.c_str(), pathname_.length() - 1)) + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) : *this; } diff --git a/tests/gtest/src/gtest-internal-inl.h b/external/gtest/src/gtest-internal-inl.h similarity index 83% rename from tests/gtest/src/gtest-internal-inl.h rename to external/gtest/src/gtest-internal-inl.h index 65a2101a4d..35df303cca 100644 --- a/tests/gtest/src/gtest-internal-inl.h +++ b/external/gtest/src/gtest-internal-inl.h @@ -58,6 +58,11 @@ #include "gtest/internal/gtest-port.h" +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + #if GTEST_OS_WINDOWS # include // NOLINT #endif // GTEST_OS_WINDOWS @@ -112,6 +117,12 @@ GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); // Formats the given time in milliseconds as seconds. GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + // Parses a string for an Int32 flag, in the form of "--flag=value". // // On success, stores the value of the flag in *value, and returns @@ -190,37 +201,35 @@ class GTestFlagSaver { GTEST_FLAG(stream_result_to) = stream_result_to_; GTEST_FLAG(throw_on_failure) = throw_on_failure_; } + private: // Fields for saving the original values of flags. bool also_run_disabled_tests_; bool break_on_failure_; bool catch_exceptions_; - String color_; - String death_test_style_; + std::string color_; + std::string death_test_style_; bool death_test_use_fork_; - String filter_; - String internal_run_death_test_; + std::string filter_; + std::string internal_run_death_test_; bool list_tests_; - String output_; + std::string output_; bool print_time_; - bool pretty_; internal::Int32 random_seed_; internal::Int32 repeat_; bool shuffle_; internal::Int32 stack_trace_depth_; - String stream_result_to_; + std::string stream_result_to_; bool throw_on_failure_; } GTEST_ATTRIBUTE_UNUSED_; // Converts a Unicode code point to a narrow string in UTF-8 encoding. // code_point parameter is of type UInt32 because wchar_t may not be // wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. // If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); // Converts a wide string to a narrow string in UTF-8 encoding. // The wide string is assumed to have the following encoding: @@ -235,7 +244,7 @@ GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); // as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding // and contains invalid UTF-16 surrogate pairs, values in those pairs // will be encoded as individual Unicode characters from Basic Normal Plane. -GTEST_API_ String WideStringToUtf8(const wchar_t* str, int num_chars); +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); // Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file // if the variable is present. If a file already exists at this location, this @@ -339,16 +348,15 @@ class TestPropertyKeyIs { // Constructor. // // TestPropertyKeyIs has NO default constructor. - explicit TestPropertyKeyIs(const char* key) - : key_(key) {} + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} // Returns true iff the test name of test property matches on key_. bool operator()(const TestProperty& test_property) const { - return String(test_property.key()).Compare(key_) == 0; + return test_property.key() == key_; } private: - String key_; + std::string key_; }; // Class UnitTestOptions. @@ -366,12 +374,12 @@ class GTEST_API_ UnitTestOptions { // Functions for processing the gtest_output flag. // Returns the output format, or "" for normal printed output. - static String GetOutputFormat(); + static std::string GetOutputFormat(); // Returns the absolute path of the requested output file, or the // default (test_detail.xml in the original working directory) if // none was explicitly specified. - static String GetAbsolutePathToOutputFile(); + static std::string GetAbsolutePathToOutputFile(); // Functions for processing the gtest_filter flag. @@ -384,8 +392,8 @@ class GTEST_API_ UnitTestOptions { // Returns true iff the user-specified filter matches the test case // name and the test name. - static bool FilterMatchesTest(const String &test_case_name, - const String &test_name); + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); #if GTEST_OS_WINDOWS // Function for supporting the gtest_catch_exception flag. @@ -398,7 +406,7 @@ class GTEST_API_ UnitTestOptions { // Returns true if "name" matches the ':' separated list of glob-style // filters in "filter". - static bool MatchesFilter(const String& name, const char* filter); + static bool MatchesFilter(const std::string& name, const char* filter); }; // Returns the current application's name, removing directory path if that @@ -411,13 +419,13 @@ class OsStackTraceGetterInterface { OsStackTraceGetterInterface() {} virtual ~OsStackTraceGetterInterface() {} - // Returns the current OS stack trace as a String. Parameters: + // Returns the current OS stack trace as an std::string. Parameters: // // max_depth - the maximum number of stack frames to be included // in the trace. // skip_count - the number of top frames to be skipped; doesn't count // against max_depth. - virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; // UponLeavingGTest() should be called immediately before Google Test calls // user code. It saves some information about the current stack that @@ -432,8 +440,11 @@ class OsStackTraceGetterInterface { class OsStackTraceGetter : public OsStackTraceGetterInterface { public: OsStackTraceGetter() : caller_frame_(NULL) {} - virtual String CurrentStackTrace(int max_depth, int skip_count); - virtual void UponLeavingGTest(); + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); // This string is inserted in place of stack frames that are part of // Google Test's implementation. @@ -455,7 +466,7 @@ class OsStackTraceGetter : public OsStackTraceGetterInterface { struct TraceInfo { const char* file; int line; - String message; + std::string message; }; // This is the default global test part result reporter used in UnitTestImpl. @@ -539,15 +550,25 @@ class GTEST_API_ UnitTestImpl { // Gets the number of failed tests. int failed_test_count() const; + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + // Gets the number of disabled tests. int disabled_test_count() const; + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + // Gets the number of all tests. int total_test_count() const; // Gets the number of tests that should run. int test_to_run_count() const; + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + // Gets the elapsed time, in milliseconds. TimeInMillis elapsed_time() const { return elapsed_time_; } @@ -596,7 +617,7 @@ class GTEST_API_ UnitTestImpl { // getter, and returns it. OsStackTraceGetterInterface* os_stack_trace_getter(); - // Returns the current OS stack trace as a String. + // Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -606,7 +627,7 @@ class GTEST_API_ UnitTestImpl { // For example, if Foo() calls Bar(), which in turn calls // CurrentOsStackTraceExceptTop(1), Foo() will be included in the // trace but Bar() and CurrentOsStackTraceExceptTop() won't. - String CurrentOsStackTraceExceptTop(int skip_count); + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; // Finds and returns a TestCase with the given name. If one doesn't // exist, creates one and returns it. @@ -696,6 +717,12 @@ class GTEST_API_ UnitTestImpl { ad_hoc_test_result_.Clear(); } + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + enum ReactionToSharding { HONOR_SHARDING_PROTOCOL, IGNORE_SHARDING_PROTOCOL @@ -880,6 +907,10 @@ class GTEST_API_ UnitTestImpl { // Our random number generator. internal::Random random_; + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + // How long the test took to run, in milliseconds. TimeInMillis elapsed_time_; @@ -935,7 +966,7 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); // Returns the message describing the last system error, regardless of the // platform. -GTEST_API_ String GetLastErrnoDescription(); +GTEST_API_ std::string GetLastErrnoDescription(); # if GTEST_OS_WINDOWS // Provides leak-safe Windows kernel handle ownership. @@ -1018,8 +1049,9 @@ bool ParseNaturalNumber(const ::std::string& str, Integer* number) { class TestResultAccessor { public: static void RecordProperty(TestResult* test_result, + const std::string& xml_element, const TestProperty& property) { - test_result->RecordProperty(property); + test_result->RecordProperty(xml_element, property); } static void ClearTestPartResults(TestResult* test_result) { @@ -1032,6 +1064,154 @@ class TestResultAccessor { } }; +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + } // namespace internal } // namespace testing diff --git a/tests/gtest/src/gtest-port.cc b/external/gtest/src/gtest-port.cc similarity index 86% rename from tests/gtest/src/gtest-port.cc rename to external/gtest/src/gtest-port.cc index b860d4812e..0c4df5f29a 100644 --- a/tests/gtest/src/gtest-port.cc +++ b/external/gtest/src/gtest-port.cc @@ -51,6 +51,11 @@ # include #endif // GTEST_OS_MAC +#if GTEST_OS_QNX +# include +# include +#endif // GTEST_OS_QNX + #include "gtest/gtest-spi.h" #include "gtest/gtest-message.h" #include "gtest/internal/gtest-internal.h" @@ -98,6 +103,26 @@ size_t GetThreadCount() { } } +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + #else size_t GetThreadCount() { @@ -222,7 +247,7 @@ bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { } // Helper function used by ValidateRegex() to format error messages. -String FormatRegexSyntaxError(const char* regex, int index) { +std::string FormatRegexSyntaxError(const char* regex, int index) { return (Message() << "Syntax error at index " << index << " in simple regular expression \"" << regex << "\": ").GetString(); } @@ -429,15 +454,15 @@ const char kUnknownFile[] = "unknown file"; // Formats a source file path and a line number as they would appear // in an error message from the compiler used to compile this code. GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { - const char* const file_name = file == NULL ? kUnknownFile : file; + const std::string file_name(file == NULL ? kUnknownFile : file); if (line < 0) { - return String::Format("%s:", file_name).c_str(); + return file_name + ":"; } #ifdef _MSC_VER - return String::Format("%s(%d):", file_name, line).c_str(); + return file_name + "(" + StreamableToString(line) + "):"; #else - return String::Format("%s:%d:", file_name, line).c_str(); + return file_name + ":" + StreamableToString(line) + ":"; #endif // _MSC_VER } @@ -448,12 +473,12 @@ GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { // to the file location it produces, unlike FormatFileLocation(). GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( const char* file, int line) { - const char* const file_name = file == NULL ? kUnknownFile : file; + const std::string file_name(file == NULL ? kUnknownFile : file); if (line < 0) return file_name; else - return String::Format("%s:%d", file_name, line).c_str(); + return file_name + ":" + StreamableToString(line); } @@ -488,8 +513,7 @@ GTestLog::~GTestLog() { class CapturedStream { public: // The ctor redirects the stream to a temporary file. - CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { - + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { # if GTEST_OS_WINDOWS char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT @@ -506,10 +530,29 @@ class CapturedStream { << temp_file_path; filename_ = temp_file_path; # else - // There's no guarantee that a test has write access to the - // current directory, so we create the temporary file in the /tmp - // directory instead. + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID const int captured_fd = mkstemp(name_template); filename_ = name_template; # endif // GTEST_OS_WINDOWS @@ -522,7 +565,7 @@ class CapturedStream { remove(filename_.c_str()); } - String GetCapturedString() { + std::string GetCapturedString() { if (uncaptured_fd_ != -1) { // Restores the original stream. fflush(NULL); @@ -532,14 +575,14 @@ class CapturedStream { } FILE* const file = posix::FOpen(filename_.c_str(), "r"); - const String content = ReadEntireFile(file); + const std::string content = ReadEntireFile(file); posix::FClose(file); return content; } private: - // Reads the entire content of a file as a String. - static String ReadEntireFile(FILE* file); + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); // Returns the size (in bytes) of a file. static size_t GetFileSize(FILE* file); @@ -559,7 +602,7 @@ size_t CapturedStream::GetFileSize(FILE* file) { } // Reads the entire content of a file as a string. -String CapturedStream::ReadEntireFile(FILE* file) { +std::string CapturedStream::ReadEntireFile(FILE* file) { const size_t file_size = GetFileSize(file); char* const buffer = new char[file_size]; @@ -575,7 +618,7 @@ String CapturedStream::ReadEntireFile(FILE* file) { bytes_read += bytes_last_read; } while (bytes_last_read > 0 && bytes_read < file_size); - const String content(buffer, bytes_read); + const std::string content(buffer, bytes_read); delete[] buffer; return content; @@ -598,8 +641,8 @@ void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { } // Stops capturing the output stream and returns the captured string. -String GetCapturedStream(CapturedStream** captured_stream) { - const String content = (*captured_stream)->GetCapturedString(); +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); delete *captured_stream; *captured_stream = NULL; @@ -618,21 +661,37 @@ void CaptureStderr() { } // Stops capturing stdout and returns the captured string. -String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); } +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} // Stops capturing stderr and returns the captured string. -String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); } +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} #endif // GTEST_HAS_STREAM_REDIRECTION #if GTEST_HAS_DEATH_TEST // A copy of all command line arguments. Set by InitGoogleTest(). -::std::vector g_argvs; +::std::vector g_argvs; -// Returns the command line as a vector of strings. -const ::std::vector& GetArgvs() { return g_argvs; } +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} #endif // GTEST_HAS_DEATH_TEST #if GTEST_OS_WINDOWS_MOBILE @@ -647,8 +706,8 @@ void Abort() { // Returns the name of the environment variable corresponding to the // given flag. For example, FlagToEnvVar("foo") will return // "GTEST_FOO" in the open-source version. -static String FlagToEnvVar(const char* flag) { - const String full_flag = +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); Message env_var; @@ -705,7 +764,7 @@ bool ParseInt32(const Message& src_text, const char* str, Int32* value) { // // The value is considered true iff it's not "0". bool BoolFromGTestEnv(const char* flag, bool default_value) { - const String env_var = FlagToEnvVar(flag); + const std::string env_var = FlagToEnvVar(flag); const char* const string_value = posix::GetEnv(env_var.c_str()); return string_value == NULL ? default_value : strcmp(string_value, "0") != 0; @@ -715,7 +774,7 @@ bool BoolFromGTestEnv(const char* flag, bool default_value) { // variable corresponding to the given flag; if it isn't set or // doesn't represent a valid 32-bit integer, returns default_value. Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { - const String env_var = FlagToEnvVar(flag); + const std::string env_var = FlagToEnvVar(flag); const char* const string_value = posix::GetEnv(env_var.c_str()); if (string_value == NULL) { // The environment variable is not set. @@ -737,7 +796,7 @@ Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { // Reads and returns the string environment variable corresponding to // the given flag; if it's not set, returns default_value. const char* StringFromGTestEnv(const char* flag, const char* default_value) { - const String env_var = FlagToEnvVar(flag); + const std::string env_var = FlagToEnvVar(flag); const char* const value = posix::GetEnv(env_var.c_str()); return value == NULL ? default_value : value; } diff --git a/tests/gtest/src/gtest-printers.cc b/external/gtest/src/gtest-printers.cc similarity index 80% rename from tests/gtest/src/gtest-printers.cc rename to external/gtest/src/gtest-printers.cc index ed63c7b3b9..75fa408100 100644 --- a/tests/gtest/src/gtest-printers.cc +++ b/external/gtest/src/gtest-printers.cc @@ -55,14 +55,6 @@ namespace { using ::std::ostream; -#if GTEST_OS_WINDOWS_MOBILE // Windows CE does not define _snprintf_s. -# define snprintf _snprintf -#elif _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf. -# define snprintf _snprintf_s -#elif _MSC_VER -# define snprintf _snprintf -#endif // GTEST_OS_WINDOWS_MOBILE - // Prints a segment of bytes in the given object. void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, size_t count, ostream* os) { @@ -77,7 +69,7 @@ void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, else *os << '-'; } - snprintf(text, sizeof(text), "%02X", obj_bytes[j]); + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); *os << text; } } @@ -184,16 +176,16 @@ static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { *os << static_cast(c); return kAsIs; } else { - *os << String::Format("\\x%X", static_cast(c)); + *os << "\\x" + String::FormatHexInt(static_cast(c)); return kHexEscape; } } return kSpecialEscape; } -// Prints a char c as if it's part of a string literal, escaping it when +// Prints a wchar_t c as if it's part of a string literal, escaping it when // necessary; returns how c was formatted. -static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { switch (c) { case L'\'': *os << "'"; @@ -208,8 +200,9 @@ static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { // Prints a char c as if it's part of a string literal, escaping it when // necessary; returns how c was formatted. -static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) { - return PrintAsWideStringLiteralTo(static_cast(c), os); +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); } // Prints a wide or narrow character c and its code. '\0' is printed @@ -228,7 +221,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) { // obvious). if (c == 0) return; - *os << " (" << String::Format("%d", c).c_str(); + *os << " (" << static_cast(c); // For more convenience, we print c's code again in hexidecimal, // unless c was already printed in the form '\x##' or the code is in @@ -236,8 +229,7 @@ void PrintCharAndCodeTo(Char c, ostream* os) { if (format == kHexEscape || (1 <= c && c <= 9)) { // Do nothing. } else { - *os << String::Format(", 0x%X", - static_cast(c)).c_str(); + *os << ", 0x" << String::FormatHexInt(static_cast(c)); } *os << ")"; } @@ -255,48 +247,63 @@ void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); } -// Prints the given array of characters to the ostream. -// The array starts at *begin, the length is len, it may include '\0' characters -// and may not be null-terminated. -static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) { - *os << "\""; +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; bool is_previous_hex = false; for (size_t index = 0; index < len; ++index) { - const char cur = begin[index]; + const CharType cur = begin[index]; if (is_previous_hex && IsXDigit(cur)) { // Previous character is of '\x..' form and this character can be // interpreted as another hexadecimal digit in its number. Break string to // disambiguate. - *os << "\" \""; + *os << "\" " << kQuoteBegin; } - is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape; + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; } *os << "\""; } +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + // Prints a (const) char array of 'len' elements, starting at address 'begin'. void UniversalPrintArray(const char* begin, size_t len, ostream* os) { - PrintCharsAsStringTo(begin, len, os); + UniversalPrintCharArray(begin, len, os); } -// Prints the given array of wide characters to the ostream. -// The array starts at *begin, the length is len, it may include L'\0' -// characters and may not be null-terminated. -static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len, - ostream* os) { - *os << "L\""; - bool is_previous_hex = false; - for (size_t index = 0; index < len; ++index) { - const wchar_t cur = begin[index]; - if (is_previous_hex && isascii(cur) && IsXDigit(static_cast(cur))) { - // Previous character is of '\x..' form and this character can be - // interpreted as another hexadecimal digit in its number. Break string to - // disambiguate. - *os << "\" L\""; - } - is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape; - } - *os << "\""; +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); } // Prints the given C string to the ostream. @@ -322,7 +329,7 @@ void PrintTo(const wchar_t* s, ostream* os) { *os << "NULL"; } else { *os << ImplicitCast_(s) << " pointing to "; - PrintWideCharsAsStringTo(s, wcslen(s), os); + PrintCharsAsStringTo(s, wcslen(s), os); } } #endif // wchar_t is native @@ -341,13 +348,13 @@ void PrintStringTo(const ::std::string& s, ostream* os) { // Prints a ::wstring object. #if GTEST_HAS_GLOBAL_WSTRING void PrintWideStringTo(const ::wstring& s, ostream* os) { - PrintWideCharsAsStringTo(s.data(), s.size(), os); + PrintCharsAsStringTo(s.data(), s.size(), os); } #endif // GTEST_HAS_GLOBAL_WSTRING #if GTEST_HAS_STD_WSTRING void PrintWideStringTo(const ::std::wstring& s, ostream* os) { - PrintWideCharsAsStringTo(s.data(), s.size(), os); + PrintCharsAsStringTo(s.data(), s.size(), os); } #endif // GTEST_HAS_STD_WSTRING diff --git a/tests/gtest/src/gtest-test-part.cc b/external/gtest/src/gtest-test-part.cc similarity index 95% rename from tests/gtest/src/gtest-test-part.cc rename to external/gtest/src/gtest-test-part.cc index 5ddc67c1c9..c60eef3ab3 100644 --- a/tests/gtest/src/gtest-test-part.cc +++ b/external/gtest/src/gtest-test-part.cc @@ -48,10 +48,10 @@ using internal::GetUnitTestImpl; // Gets the summary of the failure message by omitting the stack trace // in it. -internal::String TestPartResult::ExtractSummary(const char* message) { +std::string TestPartResult::ExtractSummary(const char* message) { const char* const stack_trace = strstr(message, internal::kStackTraceMarker); - return stack_trace == NULL ? internal::String(message) : - internal::String(message, stack_trace - message); + return stack_trace == NULL ? message : + std::string(message, stack_trace); } // Prints a TestPartResult object. diff --git a/tests/gtest/src/gtest-typed-test.cc b/external/gtest/src/gtest-typed-test.cc similarity index 96% rename from tests/gtest/src/gtest-typed-test.cc rename to external/gtest/src/gtest-typed-test.cc index a5cc88f920..f0079f407c 100644 --- a/tests/gtest/src/gtest-typed-test.cc +++ b/external/gtest/src/gtest-typed-test.cc @@ -58,10 +58,10 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( registered_tests = SkipSpaces(registered_tests); Message errors; - ::std::set tests; + ::std::set tests; for (const char* names = registered_tests; names != NULL; names = SkipComma(names)) { - const String name = GetPrefixUntilComma(names); + const std::string name = GetPrefixUntilComma(names); if (tests.count(name) != 0) { errors << "Test " << name << " is listed more than once.\n"; continue; @@ -93,7 +93,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames( } } - const String& errors_str = errors.GetString(); + const std::string& errors_str = errors.GetString(); if (errors_str != "") { fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), errors_str.c_str()); diff --git a/tests/gtest/src/gtest.cc b/external/gtest/src/gtest.cc similarity index 84% rename from tests/gtest/src/gtest.cc rename to external/gtest/src/gtest.cc index ee5eb1c495..6de53dd019 100644 --- a/tests/gtest/src/gtest.cc +++ b/external/gtest/src/gtest.cc @@ -39,10 +39,13 @@ #include #include #include +#include #include #include #include +#include +#include #include // NOLINT #include #include @@ -179,6 +182,10 @@ bool g_help_flag = false; } // namespace internal +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + GTEST_DEFINE_bool_( also_run_disabled_tests, internal::BoolFromGTestEnv("also_run_disabled_tests", false), @@ -201,11 +208,11 @@ GTEST_DEFINE_string_( "Whether to use colors in the output. Valid values: yes, no, " "and auto. 'auto' means to use colors if the output is " "being sent to a terminal and the TERM environment variable " - "is set to xterm, xterm-color, xterm-256color, linux or cygwin."); + "is set to a terminal type that supports colors."); GTEST_DEFINE_string_( filter, - internal::StringFromGTestEnv("filter", kUniversalFilter), + internal::StringFromGTestEnv("filter", GetDefaultFilter()), "A colon-separated list of glob (not regex) patterns " "for filtering the tests to run, optionally followed by a " "'-' and a : separated list of negative patterns (tests to " @@ -305,7 +312,7 @@ UInt32 Random::Generate(UInt32 range) { // Test. g_init_gtest_count is set to the number of times // InitGoogleTest() has been called. We don't protect this variable // under a mutex as it is only accessed in the main thread. -int g_init_gtest_count = 0; +GTEST_API_ int g_init_gtest_count = 0; static bool GTestIsInitialized() { return g_init_gtest_count != 0; } // Iterates over a vector of TestCases, keeping a running sum of the @@ -360,10 +367,10 @@ void AssertHelper::operator=(const Message& message) const { } // Mutex for linked pointers. -GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); // Application pathname gotten in InitGoogleTest. -String g_executable_path; +std::string g_executable_path; // Returns the current application's name, removing directory path if that // is present. @@ -382,29 +389,29 @@ FilePath GetCurrentExecutableName() { // Functions for processing the gtest_output flag. // Returns the output format, or "" for normal printed output. -String UnitTestOptions::GetOutputFormat() { +std::string UnitTestOptions::GetOutputFormat() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); - if (gtest_output_flag == NULL) return String(""); + if (gtest_output_flag == NULL) return std::string(""); const char* const colon = strchr(gtest_output_flag, ':'); return (colon == NULL) ? - String(gtest_output_flag) : - String(gtest_output_flag, colon - gtest_output_flag); + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); } // Returns the name of the requested output file, or the default if none // was explicitly specified. -String UnitTestOptions::GetAbsolutePathToOutputFile() { +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); if (gtest_output_flag == NULL) - return String(""); + return ""; const char* const colon = strchr(gtest_output_flag, ':'); if (colon == NULL) - return String(internal::FilePath::ConcatPaths( - internal::FilePath( - UnitTest::GetInstance()->original_working_dir()), - internal::FilePath(kDefaultOutputFile)).ToString() ); + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); internal::FilePath output_name(colon + 1); if (!output_name.IsAbsolutePath()) @@ -417,12 +424,12 @@ String UnitTestOptions::GetAbsolutePathToOutputFile() { internal::FilePath(colon + 1)); if (!output_name.IsDirectory()) - return output_name.ToString(); + return output_name.string(); internal::FilePath result(internal::FilePath::GenerateUniqueFileName( output_name, internal::GetCurrentExecutableName(), GetOutputFormat().c_str())); - return result.ToString(); + return result.string(); } // Returns true iff the wildcard pattern matches the string. The @@ -447,7 +454,8 @@ bool UnitTestOptions::PatternMatchesString(const char *pattern, } } -bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { const char *cur_pattern = filter; for (;;) { if (PatternMatchesString(cur_pattern, name.c_str())) { @@ -467,28 +475,24 @@ bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { } } -// TODO(keithray): move String function implementations to gtest-string.cc. - // Returns true iff the user-specified filter matches the test case // name and the test name. -bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, - const String &test_name) { - const String& full_name = String::Format("%s.%s", - test_case_name.c_str(), - test_name.c_str()); +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); // Split --gtest_filter at '-', if there is one, to separate into // positive filter and negative filter portions const char* const p = GTEST_FLAG(filter).c_str(); const char* const dash = strchr(p, '-'); - String positive; - String negative; + std::string positive; + std::string negative; if (dash == NULL) { positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter - negative = String(""); + negative = ""; } else { - positive = String(p, dash - p); // Everything up to the dash - negative = String(dash+1); // Everything after the dash + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash if (positive.empty()) { // Treat '-test1' as the same as '*-test1' positive = kUniversalFilter; @@ -608,7 +612,7 @@ AssertionResult HasOneFailure(const char* /* results_expr */, const TestPartResultArray& results, TestPartResult::Type type, const string& substr) { - const String expected(type == TestPartResult::kFatalFailure ? + const std::string expected(type == TestPartResult::kFatalFailure ? "1 fatal failure" : "1 non-fatal failure"); Message msg; @@ -731,11 +735,22 @@ int UnitTestImpl::failed_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); } +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + // Gets the number of disabled tests. int UnitTestImpl::disabled_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); } +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + // Gets the number of all tests. int UnitTestImpl::total_test_count() const { return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); @@ -746,7 +761,7 @@ int UnitTestImpl::test_to_run_count() const { return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); } -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -756,9 +771,9 @@ int UnitTestImpl::test_to_run_count() const { // For example, if Foo() calls Bar(), which in turn calls // CurrentOsStackTraceExceptTop(1), Foo() will be included in the // trace but Bar() and CurrentOsStackTraceExceptTop() won't. -String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { (void)skip_count; - return String(""); + return ""; } // Returns the current time in milliseconds. @@ -815,41 +830,7 @@ TimeInMillis GetTimeInMillis() { // Utilities -// class String - -// Returns the input enclosed in double quotes if it's not NULL; -// otherwise returns "(null)". For example, "\"Hello\"" is returned -// for input "Hello". -// -// This is useful for printing a C string in the syntax of a literal. -// -// Known issue: escape sequences are not handled yet. -String String::ShowCStringQuoted(const char* c_str) { - return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); -} - -// Copies at most length characters from str into a newly-allocated -// piece of memory of size length+1. The memory is allocated with new[]. -// A terminating null byte is written to the memory, and a pointer to it -// is returned. If str is NULL, NULL is returned. -static char* CloneString(const char* str, size_t length) { - if (str == NULL) { - return NULL; - } else { - char* const clone = new char[length + 1]; - posix::StrNCpy(clone, str, length); - clone[length] = '\0'; - return clone; - } -} - -// Clones a 0-terminated C string, allocating memory using new. The -// caller is responsible for deleting[] the return value. Returns the -// cloned string, or NULL if the input is NULL. -const char * String::CloneCString(const char* c_str) { - return (c_str == NULL) ? - NULL : CloneString(c_str, strlen(c_str)); -} +// class String. #if GTEST_OS_WINDOWS_MOBILE // Creates a UTF-16 wide string from the given ANSI string, allocating @@ -906,11 +887,6 @@ bool String::CStringEquals(const char * lhs, const char * rhs) { // encoding, and streams the result to the given Message object. static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, Message* msg) { - // TODO(wan): consider allowing a testing::String object to - // contain '\0'. This will make it behave more like std::string, - // and will allow ToUtf8String() to return the correct encoding - // for '\0' s.t. we can get rid of the conditional here (and in - // several other places). for (size_t i = 0; i != length; ) { // NOLINT if (wstr[i] != L'\0') { *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); @@ -927,6 +903,26 @@ static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, } // namespace internal +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + #if GTEST_HAS_STD_WSTRING // Converts the given wide string to a narrow string using the UTF-8 // encoding, and streams the result to this Message object. @@ -945,6 +941,12 @@ Message& Message::operator <<(const ::wstring& wstr) { } #endif // GTEST_HAS_GLOBAL_WSTRING +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + // AssertionResult constructors. // Used in EXPECT_TRUE/FALSE(assertion_result). AssertionResult::AssertionResult(const AssertionResult& other) @@ -997,8 +999,8 @@ namespace internal { // be inserted into the message. AssertionResult EqFailure(const char* expected_expression, const char* actual_expression, - const String& expected_value, - const String& actual_value, + const std::string& expected_value, + const std::string& actual_value, bool ignoring_case) { Message msg; msg << "Value of: " << actual_expression; @@ -1018,10 +1020,11 @@ AssertionResult EqFailure(const char* expected_expression, } // Constructs a failure message for Boolean assertions such as EXPECT_TRUE. -String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result, - const char* expression_text, - const char* actual_predicate_value, - const char* expected_predicate_value) { +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { const char* actual_message = assertion_result.message(); Message msg; msg << "Value of: " << expression_text @@ -1168,8 +1171,8 @@ AssertionResult CmpHelperSTREQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), false); } @@ -1184,8 +1187,8 @@ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowCStringQuoted(expected), - String::ShowCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), true); } @@ -1349,7 +1352,7 @@ AssertionResult HRESULTFailureHelper(const char* expr, // want inserts expanded. const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - const DWORD kBufSize = 4096; // String::Format can't exceed this length. + const DWORD kBufSize = 4096; // Gets the system's human readable message string for this HRESULT. char error_text[kBufSize] = { '\0' }; DWORD message_length = ::FormatMessageA(kFlags, @@ -1359,7 +1362,7 @@ AssertionResult HRESULTFailureHelper(const char* expr, error_text, // output buffer kBufSize, // buf size NULL); // no arguments for inserts - // Trims tailing white space (FormatMessage leaves a trailing cr-lf) + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) for (; message_length && IsSpace(error_text[message_length - 1]); --message_length) { error_text[message_length - 1] = '\0'; @@ -1367,10 +1370,10 @@ AssertionResult HRESULTFailureHelper(const char* expr, # endif // GTEST_OS_WINDOWS_MOBILE - const String error_hex(String::Format("0x%08X ", hr)); + const std::string error_hex("0x" + String::FormatHexInt(hr)); return ::testing::AssertionFailure() << "Expected: " << expr << " " << expected << ".\n" - << " Actual: " << error_hex << error_text << "\n"; + << " Actual: " << error_hex << " " << error_text << "\n"; } } // namespace @@ -1427,12 +1430,15 @@ inline UInt32 ChopLowBits(UInt32* bits, int n) { // Converts a Unicode code point to a narrow string in UTF-8 encoding. // code_point parameter is of type UInt32 because wchar_t may not be // wide enough to contain a code point. -// The output buffer str must containt at least 32 characters. -// The function returns the address of the output buffer. // If the code_point is not a valid Unicode code point -// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output -// as '(Invalid Unicode 0xXXXXXXXX)'. -char* CodePointToUtf8(UInt32 code_point, char* str) { +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. if (code_point <= kMaxCodePoint1) { str[1] = '\0'; str[0] = static_cast(code_point); // 0xxxxxxx @@ -1445,22 +1451,12 @@ char* CodePointToUtf8(UInt32 code_point, char* str) { str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[0] = static_cast(0xE0 | code_point); // 1110xxxx - } else if (code_point <= kMaxCodePoint4) { + } else { // code_point <= kMaxCodePoint4 str[4] = '\0'; str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx str[0] = static_cast(0xF0 | code_point); // 11110xxx - } else { - // The longest string String::Format can produce when invoked - // with these parameters is 28 character long (not including - // the terminating nul character). We are asking for 32 character - // buffer just in case. This is also enough for strncpy to - // null-terminate the destination string. - posix::StrNCpy( - str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32); - str[31] = '\0'; // Makes sure no change in the format to strncpy leaves - // the result unterminated. } return str; } @@ -1501,7 +1497,7 @@ inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, // as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding // and contains invalid UTF-16 surrogate pairs, values in those pairs // will be encoded as individual Unicode characters from Basic Normal Plane. -String WideStringToUtf8(const wchar_t* str, int num_chars) { +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { if (num_chars == -1) num_chars = static_cast(wcslen(str)); @@ -1519,27 +1515,17 @@ String WideStringToUtf8(const wchar_t* str, int num_chars) { unicode_code_point = static_cast(str[i]); } - char buffer[32]; // CodePointToUtf8 requires a buffer this big. - stream << CodePointToUtf8(unicode_code_point, buffer); + stream << CodePointToUtf8(unicode_code_point); } return StringStreamToString(&stream); } -// Converts a wide C string to a String using the UTF-8 encoding. +// Converts a wide C string to an std::string using the UTF-8 encoding. // NULL will be converted to "(null)". -String String::ShowWideCString(const wchar_t * wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; - return String(internal::WideStringToUtf8(wide_c_str, -1).c_str()); -} - -// Similar to ShowWideCString(), except that this function encloses -// the converted string in double quotes. -String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { - if (wide_c_str == NULL) return String("(null)"); - - return String::Format("L\"%s\"", - String::ShowWideCString(wide_c_str).c_str()); + return internal::WideStringToUtf8(wide_c_str, -1); } // Compares two wide C strings. Returns true iff they have the same @@ -1567,8 +1553,8 @@ AssertionResult CmpHelperSTREQ(const char* expected_expression, return EqFailure(expected_expression, actual_expression, - String::ShowWideCStringQuoted(expected), - String::ShowWideCStringQuoted(actual), + PrintToString(expected), + PrintToString(actual), false); } @@ -1583,8 +1569,8 @@ AssertionResult CmpHelperSTRNE(const char* s1_expression, return AssertionFailure() << "Expected: (" << s1_expression << ") != (" << s2_expression << "), actual: " - << String::ShowWideCStringQuoted(s1) - << " vs " << String::ShowWideCStringQuoted(s2); + << PrintToString(s1) + << " vs " << PrintToString(s2); } // Compares two C strings, ignoring case. Returns true iff they have @@ -1635,135 +1621,69 @@ bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, #endif // OS selector } -// Compares this with another String. -// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 -// if this is greater than rhs. -int String::Compare(const String & rhs) const { - const char* const lhs_c_str = c_str(); - const char* const rhs_c_str = rhs.c_str(); - - if (lhs_c_str == NULL) { - return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL - } else if (rhs_c_str == NULL) { - return 1; - } - - const size_t shorter_str_len = - length() <= rhs.length() ? length() : rhs.length(); - for (size_t i = 0; i != shorter_str_len; i++) { - if (lhs_c_str[i] < rhs_c_str[i]) { - return -1; - } else if (lhs_c_str[i] > rhs_c_str[i]) { - return 1; - } - } - return (length() < rhs.length()) ? -1 : - (length() > rhs.length()) ? 1 : 0; +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); } -// Returns true iff this String ends with the given suffix. *Any* -// String is considered to end with a NULL or empty suffix. -bool String::EndsWith(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CStringEquals(c_str() + this_len - suffix_len, suffix); +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); } -// Returns true iff this String ends with the given suffix, ignoring case. -// Any String is considered to end with a NULL or empty suffix. -bool String::EndsWithCaseInsensitive(const char* suffix) const { - if (suffix == NULL || CStringEquals(suffix, "")) return true; - - if (c_str() == NULL) return false; - - const size_t this_len = strlen(c_str()); - const size_t suffix_len = strlen(suffix); - return (this_len >= suffix_len) && - CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix); +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); } -// Formats a list of arguments to a String, using the same format -// spec string as for printf. -// -// We do not use the StringPrintf class as it is not universally -// available. -// -// The result is limited to 4096 characters (including the tailing 0). -// If 4096 characters are not enough to format the input, or if -// there's an error, "" is -// returned. -String String::Format(const char * format, ...) { - va_list args; - va_start(args, format); - - char buffer[4096]; - const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]); - - // MSVC 8 deprecates vsnprintf(), so we want to suppress warning - // 4996 (deprecated function) there. -#ifdef _MSC_VER // We are using MSVC. -# pragma warning(push) // Saves the current warning state. -# pragma warning(disable:4996) // Temporarily disables warning 4996. - - const int size = vsnprintf(buffer, kBufferSize, format, args); - -# pragma warning(pop) // Restores the warning state. -#else // We are not using MSVC. - const int size = vsnprintf(buffer, kBufferSize, format, args); -#endif // _MSC_VER - va_end(args); - - // vsnprintf()'s behavior is not portable. When the buffer is not - // big enough, it returns a negative value in MSVC, and returns the - // needed buffer size on Linux. When there is an output error, it - // always returns a negative value. For simplicity, we lump the two - // error cases together. - if (size < 0 || size >= kBufferSize) { - return String(""); - } else { - return String(buffer, size); - } +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); } -// Converts the buffer in a stringstream to a String, converting NUL +// Converts the buffer in a stringstream to an std::string, converting NUL // bytes to "\\0" along the way. -String StringStreamToString(::std::stringstream* ss) { +std::string StringStreamToString(::std::stringstream* ss) { const ::std::string& str = ss->str(); const char* const start = str.c_str(); const char* const end = start + str.length(); - // We need to use a helper stringstream to do this transformation - // because String doesn't support push_back(). - ::std::stringstream helper; + std::string result; + result.reserve(2 * (end - start)); for (const char* ch = start; ch != end; ++ch) { if (*ch == '\0') { - helper << "\\0"; // Replaces NUL with "\\0"; + result += "\\0"; // Replaces NUL with "\\0"; } else { - helper.put(*ch); + result += *ch; } } - return String(helper.str().c_str()); + return result; } // Appends the user-supplied message to the Google-Test-generated message. -String AppendUserMessage(const String& gtest_msg, - const Message& user_msg) { +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { // Appends the user message if it's non-empty. - const String user_msg_string = user_msg.GetString(); + const std::string user_msg_string = user_msg.GetString(); if (user_msg_string.empty()) { return gtest_msg; } - Message msg; - msg << gtest_msg << "\n" << user_msg_string; - - return msg.GetString(); + return gtest_msg + "\n" + user_msg_string; } } // namespace internal @@ -1811,8 +1731,9 @@ void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { // Adds a test property to the list. If a property with the same key as the // supplied property is already represented, the value of this test_property // replaces the old value for that key. -void TestResult::RecordProperty(const TestProperty& test_property) { - if (!ValidateTestProperty(test_property)) { +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { return; } internal::MutexLock lock(&test_properites_mutex_); @@ -1826,21 +1747,94 @@ void TestResult::RecordProperty(const TestProperty& test_property) { property_with_matching_key->SetValue(test_property.value()); } -// Adds a failure if the key is a reserved attribute of Google Test -// testcase tags. Returns true if the property is valid. -bool TestResult::ValidateTestProperty(const TestProperty& test_property) { - internal::String key(test_property.key()); - if (key == "name" || key == "status" || key == "time" || key == "classname") { - ADD_FAILURE() - << "Reserved key used in RecordProperty(): " - << key - << " ('name', 'status', 'time', and 'classname' are reserved by " - << GTEST_NAME_ << ")"; +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; return false; } return true; } +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + // Clears the object. void TestResult::Clear() { test_part_results_.clear(); @@ -1916,12 +1910,12 @@ void Test::TearDown() { } // Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, const char* value) { - UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); } // Allows user supplied key value pairs to be recorded for later output. -void Test::RecordProperty(const char* key, int value) { +void Test::RecordProperty(const std::string& key, int value) { Message value_message; value_message << value; RecordProperty(key, value_message.GetString().c_str()); @@ -1930,7 +1924,7 @@ void Test::RecordProperty(const char* key, int value) { namespace internal { void ReportFailureInUnknownLocation(TestPartResult::Type result_type, - const String& message) { + const std::string& message) { // This function is a friend of UnitTest and as such has access to // AddTestPartResult. UnitTest::GetInstance()->AddTestPartResult( @@ -1938,7 +1932,7 @@ void ReportFailureInUnknownLocation(TestPartResult::Type result_type, NULL, // No info about the source file where the exception occurred. -1, // We have no info on which line caused the exception. message, - String()); // No stack trace, either. + ""); // No stack trace, either. } } // namespace internal @@ -2015,22 +2009,24 @@ bool Test::HasSameFixtureClass() { // function returns its result via an output parameter pointer because VC++ // prohibits creation of objects with destructors on stack in functions // using __try (see error C2712). -static internal::String* FormatSehExceptionMessage(DWORD exception_code, - const char* location) { +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { Message message; message << "SEH exception with code 0x" << std::setbase(16) << exception_code << std::setbase(10) << " thrown in " << location << "."; - return new internal::String(message.GetString()); + return new std::string(message.GetString()); } #endif // GTEST_HAS_SEH +namespace internal { + #if GTEST_HAS_EXCEPTIONS // Adds an "exception thrown" fatal failure to the current test. -static internal::String FormatCxxExceptionMessage(const char* description, - const char* location) { +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { Message message; if (description != NULL) { message << "C++ exception with description \"" << description << "\""; @@ -2042,23 +2038,15 @@ static internal::String FormatCxxExceptionMessage(const char* description, return message.GetString(); } -static internal::String PrintTestPartResultToString( +static std::string PrintTestPartResultToString( const TestPartResult& test_part_result); -// A failed Google Test assertion will throw an exception of this type when -// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We -// derive it from std::runtime_error, which is for errors presumably -// detectable only at run time. Since std::runtime_error inherits from -// std::exception, many testing frameworks know how to extract and print the -// message inside it. -class GoogleTestFailureException : public ::std::runtime_error { - public: - explicit GoogleTestFailureException(const TestPartResult& failure) - : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} -}; +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + #endif // GTEST_HAS_EXCEPTIONS -namespace internal { // We put these helper functions in the internal namespace as IBM's xlC // compiler rejects the code if they were declared static. @@ -2078,7 +2066,7 @@ Result HandleSehExceptionsInMethodIfSupported( // We create the exception message on the heap because VC++ prohibits // creation of objects with destructors on stack in functions using __try // (see error C2712). - internal::String* exception_message = FormatSehExceptionMessage( + std::string* exception_message = FormatSehExceptionMessage( GetExceptionCode(), location); internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, *exception_message); @@ -2124,9 +2112,10 @@ Result HandleExceptionsInMethodIfSupported( #if GTEST_HAS_EXCEPTIONS try { return HandleSehExceptionsInMethodIfSupported(object, method, location); - } catch (const GoogleTestFailureException&) { // NOLINT - // This exception doesn't originate in code under test. It makes no - // sense to report it as a test failure. + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. throw; } catch (const std::exception& e) { // NOLINT internal::ReportFailureInUnknownLocation( @@ -2185,10 +2174,8 @@ bool Test::HasNonfatalFailure() { // Constructs a TestInfo object. It assumes ownership of the test factory // object. -// TODO(vladl@google.com): Make a_test_case_name and a_name const string&'s -// to signify they cannot be NULLs. -TestInfo::TestInfo(const char* a_test_case_name, - const char* a_name, +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, const char* a_type_param, const char* a_value_param, internal::TypeId fixture_class_id, @@ -2227,7 +2214,8 @@ namespace internal { // The newly created TestInfo instance will assume // ownership of the factory object. TestInfo* MakeAndRegisterTestInfo( - const char* test_case_name, const char* name, + const char* test_case_name, + const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, @@ -2282,11 +2270,11 @@ class TestNameIs { // Returns true iff the test name of test_info matches name_. bool operator()(const TestInfo * test_info) const { - return test_info && internal::String(test_info->name()).Compare(name_) == 0; + return test_info && test_info->name() == name_; } private: - internal::String name_; + std::string name_; }; } // namespace @@ -2365,10 +2353,21 @@ int TestCase::failed_test_count() const { return CountIf(test_info_list_, TestFailed); } +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. int TestCase::disabled_test_count() const { return CountIf(test_info_list_, TestDisabled); } +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + // Get the number of tests in this test case that should run. int TestCase::test_to_run_count() const { return CountIf(test_info_list_, ShouldRunTest); @@ -2456,6 +2455,7 @@ void TestCase::Run() { // Clears the results of all tests in this test case. void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); ForEach(test_info_list_, TestInfo::ClearTestResult); } @@ -2476,20 +2476,20 @@ void TestCase::UnshuffleTests() { // // FormatCountableNoun(1, "formula", "formuli") returns "1 formula". // FormatCountableNoun(5, "book", "books") returns "5 books". -static internal::String FormatCountableNoun(int count, - const char * singular_form, - const char * plural_form) { - return internal::String::Format("%d %s", count, - count == 1 ? singular_form : plural_form); +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); } // Formats the count of tests. -static internal::String FormatTestCount(int test_count) { +static std::string FormatTestCount(int test_count) { return FormatCountableNoun(test_count, "test", "tests"); } // Formats the count of test cases. -static internal::String FormatTestCaseCount(int test_case_count) { +static std::string FormatTestCaseCount(int test_case_count) { return FormatCountableNoun(test_case_count, "test case", "test cases"); } @@ -2514,8 +2514,10 @@ static const char * TestPartResultTypeToString(TestPartResult::Type type) { } } -// Prints a TestPartResult to a String. -static internal::String PrintTestPartResultToString( +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( const TestPartResult& test_part_result) { return (Message() << internal::FormatFileLocation(test_part_result.file_name(), @@ -2526,7 +2528,7 @@ static internal::String PrintTestPartResultToString( // Prints a TestPartResult. static void PrintTestPartResult(const TestPartResult& test_part_result) { - const internal::String& result = + const std::string& result = PrintTestPartResultToString(test_part_result); printf("%s\n", result.c_str()); fflush(stdout); @@ -2545,8 +2547,6 @@ static void PrintTestPartResult(const TestPartResult& test_part_result) { // class PrettyUnitTestResultPrinter -namespace internal { - enum GTestColor { COLOR_DEFAULT, COLOR_RED, @@ -2598,6 +2598,7 @@ bool ShouldUseColor(bool stdout_is_tty) { String::CStringEquals(term, "xterm-color") || String::CStringEquals(term, "xterm-256color") || String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || String::CStringEquals(term, "linux") || String::CStringEquals(term, "cygwin"); return stdout_is_tty && term_supports_color; @@ -2621,7 +2622,7 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_list args; va_start(args, fmt); -#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS const bool use_color = false; #else static const bool in_color_mode = @@ -2663,6 +2664,11 @@ void ColoredPrintf(GTestColor color, const char* fmt, ...) { va_end(args); } +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + void PrintFullTestCommentIfPresent(const TestInfo& test_info) { const char* const type_param = test_info.type_param(); const char* const value_param = test_info.value_param(); @@ -2670,12 +2676,12 @@ void PrintFullTestCommentIfPresent(const TestInfo& test_info) { if (type_param != NULL || value_param != NULL) { printf(", where "); if (type_param != NULL) { - printf("TypeParam = %s", type_param); + printf("%s = %s", kTypeParamLabel, type_param); if (value_param != NULL) printf(" and "); } if (value_param != NULL) { - printf("GetParam() = %s", value_param); + printf("%s = %s", kValueParamLabel, value_param); } } } @@ -2707,8 +2713,6 @@ class PrettyUnitTestResultPrinter : public TestEventListener { private: static void PrintFailedTests(const UnitTest& unit_test); - - internal::String test_case_name_; }; // Fired before each iteration of tests starts. @@ -2721,7 +2725,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationStart( // Prints the filter if it's not *. This reminds the user that some // tests may be skipped. - if (!internal::String::CStringEquals(filter, kUniversalFilter)) { + if (!String::CStringEquals(filter, kUniversalFilter)) { ColoredPrintf(COLOR_YELLOW, "Note: %s filter = %s\n", GTEST_NAME_, filter); } @@ -2755,22 +2759,21 @@ void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( } void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { - test_case_name_ = test_case.name(); - const internal::String counts = + const std::string counts = FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); - printf("%s from %s", counts.c_str(), test_case_name_.c_str()); + printf("%s from %s", counts.c_str(), test_case.name()); if (test_case.type_param() == NULL) { printf("\n"); } else { - printf(", where TypeParam = %s\n", test_case.type_param()); + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); } fflush(stdout); } void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { ColoredPrintf(COLOR_GREEN, "[ RUN ] "); - PrintTestName(test_case_name_.c_str(), test_info.name()); + PrintTestName(test_info.test_case_name(), test_info.name()); printf("\n"); fflush(stdout); } @@ -2793,7 +2796,7 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { } else { ColoredPrintf(COLOR_RED, "[ FAILED ] "); } - PrintTestName(test_case_name_.c_str(), test_info.name()); + PrintTestName(test_info.test_case_name(), test_info.name()); if (test_info.result()->Failed()) PrintFullTestCommentIfPresent(test_info); @@ -2809,12 +2812,11 @@ void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { if (!GTEST_FLAG(print_time)) return; - test_case_name_ = test_case.name(); - const internal::String counts = + const std::string counts = FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); ColoredPrintf(COLOR_GREEN, "[----------] "); printf("%s from %s (%s ms total)\n\n", - counts.c_str(), test_case_name_.c_str(), + counts.c_str(), test_case.name(), internal::StreamableToString(test_case.elapsed_time()).c_str()); fflush(stdout); } @@ -2875,7 +2877,7 @@ void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, num_failures == 1 ? "TEST" : "TESTS"); } - int num_disabled = unit_test.disabled_test_count(); + int num_disabled = unit_test.reportable_disabled_test_count(); if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { if (!num_failures) { printf("\n"); // Add a spacer if no FAILURE banner is displayed. @@ -3029,18 +3031,27 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { // is_attribute is true, the text is meant to appear as an attribute // value, and normalizable whitespace is preserved by replacing it // with character references. - static String EscapeXml(const char* str, bool is_attribute); + static std::string EscapeXml(const std::string& str, bool is_attribute); // Returns the given string with all characters invalid in XML removed. - static string RemoveInvalidXmlCharacters(const string& str); + static std::string RemoveInvalidXmlCharacters(const std::string& str); // Convenience wrapper around EscapeXml when str is an attribute value. - static String EscapeXmlAttribute(const char* str) { + static std::string EscapeXmlAttribute(const std::string& str) { return EscapeXml(str, true); } // Convenience wrapper around EscapeXml when str is not an attribute value. - static String EscapeXmlText(const char* str) { return EscapeXml(str, false); } + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. static void OutputXmlCDataSection(::std::ostream* stream, const char* data); @@ -3051,19 +3062,21 @@ class XmlUnitTestResultPrinter : public EmptyTestEventListener { const TestInfo& test_info); // Prints an XML representation of a TestCase object - static void PrintXmlTestCase(FILE* out, const TestCase& test_case); + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); // Prints an XML summary of unit_test to output stream out. - static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test); + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. - // When the String is not empty, it includes a space at the beginning, + // When the std::string is not empty, it includes a space at the beginning, // to delimit this attribute from prior attributes. - static String TestPropertiesAsXmlAttributes(const TestResult& result); + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); // The output file. - const String output_file_; + const std::string output_file_; GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); }; @@ -3105,7 +3118,9 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, fflush(stderr); exit(EXIT_FAILURE); } - PrintXmlUnitTest(xmlout, unit_test); + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); fclose(xmlout); } @@ -3121,42 +3136,43 @@ void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, // most invalid characters can be retained using character references. // TODO(wan): It might be nice to have a minimally invasive, human-readable // escaping scheme for invalid characters, rather than dropping them. -String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { Message m; - if (str != NULL) { - for (const char* src = str; *src; ++src) { - switch (*src) { - case '<': - m << "<"; - break; - case '>': - m << ">"; - break; - case '&': - m << "&"; - break; - case '\'': - if (is_attribute) - m << "'"; - else - m << '\''; - break; - case '"': - if (is_attribute) - m << """; + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; else - m << '"'; - break; - default: - if (IsValidXmlCharacter(*src)) { - if (is_attribute && IsNormalizableWhitespace(*src)) - m << String::Format("&#x%02X;", unsigned(*src)); - else - m << *src; - } - break; - } + m << ch; + } + break; } } @@ -3166,10 +3182,11 @@ String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { // Returns the given string with all characters invalid in XML removed. // Currently invalid characters are dropped from the string. An // alternative is to replace them with certain characters such as . or ?. -string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { - string output; +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; output.reserve(str.size()); - for (string::const_iterator it = str.begin(); it != str.end(); ++it) + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) if (IsValidXmlCharacter(*it)) output.push_back(*it); @@ -3199,6 +3216,32 @@ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { return ss.str(); } +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + // Using non-reentrant version as localtime_r is not portable. + time_t seconds = static_cast(ms / 1000); +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe). + const struct tm* const time_struct = localtime(&seconds); // NOLINT +# pragma warning(pop) // Restores the warning state again. +#else + const struct tm* const time_struct = localtime(&seconds); // NOLINT +#endif + if (time_struct == NULL) + return ""; // Invalid ms value + + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct->tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct->tm_mday) + "T" + + String::FormatIntWidth2(time_struct->tm_hour) + ":" + + String::FormatIntWidth2(time_struct->tm_min) + ":" + + String::FormatIntWidth2(time_struct->tm_sec); +} + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, const char* data) { @@ -3219,48 +3262,63 @@ void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, *stream << "]]>"; } +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + // Prints an XML representation of a TestInfo object. // TODO(wan): There is also value in printing properties with the plain printer. void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, const char* test_case_name, const TestInfo& test_info) { - if (test_info.filtered_out ()) - return; - const TestResult& result = *test_info.result(); - *stream << " \n"; - *stream << " "; + } const string location = internal::FormatCompilerIndependentFileLocation( part.file_name(), part.line_number()); - const string message = location + "\n" + part.message(); - OutputXmlCDataSection(stream, - RemoveInvalidXmlCharacters(message).c_str()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); *stream << "\n"; } } @@ -3272,52 +3330,73 @@ void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, } // Prints an XML representation of a TestCase object -void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, const TestCase& test_case) { - if (test_case.should_skip_report ()) - return; + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; - fprintf(out, - " \n", - FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); for (int i = 0; i < test_case.total_test_count(); ++i) { - ::std::stringstream stream; - OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); - fprintf(out, "%s", StringStreamToString(&stream).c_str()); + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); } - fprintf(out, " \n"); + *stream << " \n"; } // Prints an XML summary of unit_test to output stream out. -void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, const UnitTest& unit_test) { - fprintf(out, "\n"); - fprintf(out, - "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + if (GTEST_FLAG(shuffle)) { - fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed()); + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); } - fprintf(out, "name=\"AllTests\">\n"); - for (int i = 0; i < unit_test.total_test_case_count(); ++i) - PrintXmlTestCase(out, *unit_test.GetTestCase(i)); - fprintf(out, "\n"); + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; } // Produces a string representing the test properties in a result as space // delimited XML attributes based on the property key="value" pairs. -String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( const TestResult& result) { Message attributes; for (int i = 0; i < result.test_property_count(); ++i) { @@ -3332,112 +3411,6 @@ String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( #if GTEST_CAN_STREAM_RESULTS_ -// Streams test results to the given port on the given host machine. -class StreamingListener : public EmptyTestEventListener { - public: - // Escapes '=', '&', '%', and '\n' characters in str as "%xx". - static string UrlEncode(const char* str); - - StreamingListener(const string& host, const string& port) - : sockfd_(-1), host_name_(host), port_num_(port) { - MakeConnection(); - Send("gtest_streaming_protocol_version=1.0\n"); - } - - virtual ~StreamingListener() { - if (sockfd_ != -1) - CloseConnection(); - } - - void OnTestProgramStart(const UnitTest& /* unit_test */) { - Send("event=TestProgramStart\n"); - } - - void OnTestProgramEnd(const UnitTest& unit_test) { - // Note that Google Test current only report elapsed time for each - // test iteration, not for the entire test program. - Send(String::Format("event=TestProgramEnd&passed=%d\n", - unit_test.Passed())); - - // Notify the streaming server to stop. - CloseConnection(); - } - - void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { - Send(String::Format("event=TestIterationStart&iteration=%d\n", - iteration)); - } - - void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { - Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n", - unit_test.Passed(), - StreamableToString(unit_test.elapsed_time()).c_str())); - } - - void OnTestCaseStart(const TestCase& test_case) { - Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name())); - } - - void OnTestCaseEnd(const TestCase& test_case) { - Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n", - test_case.Passed(), - StreamableToString(test_case.elapsed_time()).c_str())); - } - - void OnTestStart(const TestInfo& test_info) { - Send(String::Format("event=TestStart&name=%s\n", test_info.name())); - } - - void OnTestEnd(const TestInfo& test_info) { - Send(String::Format( - "event=TestEnd&passed=%d&elapsed_time=%sms\n", - (test_info.result())->Passed(), - StreamableToString((test_info.result())->elapsed_time()).c_str())); - } - - void OnTestPartResult(const TestPartResult& test_part_result) { - const char* file_name = test_part_result.file_name(); - if (file_name == NULL) - file_name = ""; - Send(String::Format("event=TestPartResult&file=%s&line=%d&message=", - UrlEncode(file_name).c_str(), - test_part_result.line_number())); - Send(UrlEncode(test_part_result.message()) + "\n"); - } - - private: - // Creates a client socket and connects to the server. - void MakeConnection(); - - // Closes the socket. - void CloseConnection() { - GTEST_CHECK_(sockfd_ != -1) - << "CloseConnection() can be called only when there is a connection."; - - close(sockfd_); - sockfd_ = -1; - } - - // Sends a string to the socket. - void Send(const string& message) { - GTEST_CHECK_(sockfd_ != -1) - << "Send() can be called only when there is a connection."; - - const int len = static_cast(message.length()); - if (write(sockfd_, message.c_str(), len) != len) { - GTEST_LOG_(WARNING) - << "stream_result_to: failed to stream to " - << host_name_ << ":" << port_num_; - } - } - - int sockfd_; // socket file descriptor - const string host_name_; - const string port_num_; - - GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); -}; // class StreamingListener - // Checks if str contains '=', '&', '%' or '\n' characters. If yes, // replaces them by "%xx" where xx is their hexadecimal value. For // example, replaces "=" with "%3D". This algorithm is O(strlen(str)) @@ -3452,7 +3425,7 @@ string StreamingListener::UrlEncode(const char* str) { case '=': case '&': case '\n': - result.append(String::Format("%%%02x", static_cast(ch))); + result.append("%" + String::FormatByte(static_cast(ch))); break; default: result.push_back(ch); @@ -3462,7 +3435,7 @@ string StreamingListener::UrlEncode(const char* str) { return result; } -void StreamingListener::MakeConnection() { +void StreamingListener::SocketWriter::MakeConnection() { GTEST_CHECK_(sockfd_ == -1) << "MakeConnection() can't be called when there is already a connection."; @@ -3510,8 +3483,8 @@ void StreamingListener::MakeConnection() { // Pushes the given source file location and message onto a per-thread // trace stack maintained by Google Test. -// L < UnitTest::mutex_ -ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { TraceInfo trace; trace.file = file; trace.line = line; @@ -3521,35 +3494,64 @@ ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { } // Pops the info pushed by the c'tor. -// L < UnitTest::mutex_ -ScopedTrace::~ScopedTrace() { +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { UnitTest::GetInstance()->PopGTestTrace(); } // class OsStackTraceGetter -// Returns the current OS stack trace as a String. Parameters: +// Returns the current OS stack trace as an std::string. Parameters: // // max_depth - the maximum number of stack frames to be included // in the trace. // skip_count - the number of top frames to be skipped; doesn't count // against max_depth. // -// L < mutex_ -// We use "L < mutex_" to denote that the function may acquire mutex_. -String OsStackTraceGetter::CurrentStackTrace(int, int) { - return String(""); +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; } -// L < mutex_ -void OsStackTraceGetter::UponLeavingGTest() { +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { } const char* const OsStackTraceGetter::kElidedFramesMarker = "... " GTEST_NAME_ " internal frames ..."; +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + } // namespace internal // class TestEventListeners @@ -3636,7 +3638,7 @@ void TestEventListeners::SuppressEventForwarding() { // We don't protect this under mutex_ as a user is not supposed to // call this before main() starts, from which point on the return // value will never change. -UnitTest * UnitTest::GetInstance() { +UnitTest* UnitTest::GetInstance() { // When compiled with MSVC 7.1 in optimized mode, destroying the // UnitTest object upon exiting the program messes up the exit code, // causing successful tests to appear failed. We have to use a @@ -3686,17 +3688,33 @@ int UnitTest::successful_test_count() const { // Gets the number of failed tests. int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + // Gets the number of disabled tests. int UnitTest::disabled_test_count() const { return impl()->disabled_test_count(); } +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + // Gets the number of all tests. int UnitTest::total_test_count() const { return impl()->total_test_count(); } // Gets the number of tests that should run. int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + // Gets the elapsed time, in milliseconds. internal::TimeInMillis UnitTest::elapsed_time() const { return impl()->elapsed_time(); @@ -3715,6 +3733,12 @@ const TestCase* UnitTest::GetTestCase(int i) const { return impl()->GetTestCase(i); } +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + // Gets the i-th test case among all the test cases. i can range from 0 to // total_test_case_count() - 1. If i is not in that range, returns NULL. TestCase* UnitTest::GetMutableTestCase(int i) { @@ -3750,12 +3774,12 @@ Environment* UnitTest::AddEnvironment(Environment* env) { // assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call // this to report their results. The user code should use the // assertion macros instead of calling this directly. -// L < mutex_ -void UnitTest::AddTestPartResult(TestPartResult::Type result_type, - const char* file_name, - int line_number, - const internal::String& message, - const internal::String& os_stack_trace) { +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { Message msg; msg << message; @@ -3802,7 +3826,7 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, #endif // GTEST_OS_WINDOWS } else if (GTEST_FLAG(throw_on_failure)) { #if GTEST_HAS_EXCEPTIONS - throw GoogleTestFailureException(result); + throw internal::GoogleTestFailureException(result); #else // We cannot call abort() as it generates a pop-up in debug mode // that cannot be suppressed in VC 7.1 or below. @@ -3812,12 +3836,14 @@ void UnitTest::AddTestPartResult(TestPartResult::Type result_type, } } -// Creates and adds a property to the current TestResult. If a property matching -// the supplied value already exists, updates its value instead. -void UnitTest::RecordPropertyForCurrentTest(const char* key, - const char* value) { - const TestProperty test_property(key, value); - impl_->current_test_result()->RecordProperty(test_property); +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); } // Runs all tests in this UnitTest object and prints the result. @@ -3826,20 +3852,44 @@ void UnitTest::RecordPropertyForCurrentTest(const char* key, // We don't protect this under mutex_, as we only support calling it // from the main thread. int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be // used for the duration of the program. impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); #if GTEST_HAS_SEH - const bool in_death_test_child_process = - internal::GTEST_FLAG(internal_run_death_test).length() > 0; - // Either the user wants Google Test to catch exceptions thrown by the // tests or this is executing in the context of death test child // process. In either case the user does not want to see pop-up dialogs // about crashes - they are expected. if (impl()->catch_exceptions() || in_death_test_child_process) { - # if !GTEST_OS_WINDOWS_MOBILE // SetErrorMode doesn't exist on CE. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | @@ -3870,7 +3920,6 @@ int UnitTest::Run() { 0x0, // Clear the following flags: _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. # endif - } #endif // GTEST_HAS_SEH @@ -3888,16 +3937,16 @@ const char* UnitTest::original_working_dir() const { // Returns the TestCase object for the test that's currently running, // or NULL if no test is running. -// L < mutex_ -const TestCase* UnitTest::current_test_case() const { +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); return impl_->current_test_case(); } // Returns the TestInfo object for the test that's currently running, // or NULL if no test is running. -// L < mutex_ -const TestInfo* UnitTest::current_test_info() const { +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); return impl_->current_test_info(); } @@ -3908,9 +3957,9 @@ int UnitTest::random_seed() const { return impl_->random_seed(); } #if GTEST_HAS_PARAM_TEST // Returns ParameterizedTestCaseRegistry object used to keep track of // value-parameterized tests and instantiate and register them. -// L < mutex_ internal::ParameterizedTestCaseRegistry& - UnitTest::parameterized_test_registry() { + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { return impl_->parameterized_test_registry(); } #endif // GTEST_HAS_PARAM_TEST @@ -3927,15 +3976,15 @@ UnitTest::~UnitTest() { // Pushes a trace defined by SCOPED_TRACE() on to the per-thread // Google Test trace stack. -// L < mutex_ -void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); impl_->gtest_trace_stack().push_back(trace); } // Pops a trace from the per-thread Google Test trace stack. -// L < mutex_ -void UnitTest::PopGTestTrace() { +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { internal::MutexLock lock(&mutex_); impl_->gtest_trace_stack().pop_back(); } @@ -3971,9 +4020,9 @@ UnitTestImpl::UnitTestImpl(UnitTest* parent) post_flag_parse_init_performed_(false), random_seed_(0), // Will be overridden by the flag before first use. random_(0), // Will be reseeded before first use. + start_timestamp_(0), elapsed_time_(0), #if GTEST_HAS_DEATH_TEST - internal_run_death_test_flag_(NULL), death_test_factory_(new DefaultDeathTestFactory), #endif // Will be overridden by the flag before first use. @@ -3991,6 +4040,28 @@ UnitTestImpl::~UnitTestImpl() { delete os_stack_trace_getter_; } +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + #if GTEST_HAS_DEATH_TEST // Disables event forwarding if the control is currently in a death test // subprocess. Must not be called before InitGoogleTest. @@ -4003,7 +4074,7 @@ void UnitTestImpl::SuppressTestEventsIfInSubprocess() { // Initializes event listeners performing XML output as specified by // UnitTestOptions. Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureXmlOutput() { - const String& output_format = UnitTestOptions::GetOutputFormat(); + const std::string& output_format = UnitTestOptions::GetOutputFormat(); if (output_format == "xml") { listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); @@ -4015,13 +4086,13 @@ void UnitTestImpl::ConfigureXmlOutput() { } #if GTEST_CAN_STREAM_RESULTS_ -// Initializes event listeners for streaming test results in String form. +// Initializes event listeners for streaming test results in string form. // Must not be called before InitGoogleTest. void UnitTestImpl::ConfigureStreamingOutput() { - const string& target = GTEST_FLAG(stream_result_to); + const std::string& target = GTEST_FLAG(stream_result_to); if (!target.empty()) { const size_t pos = target.find(':'); - if (pos != string::npos) { + if (pos != std::string::npos) { listeners()->Append(new StreamingListener(target.substr(0, pos), target.substr(pos+1))); } else { @@ -4075,7 +4146,7 @@ void UnitTestImpl::PostFlagParsingInit() { class TestCaseNameIs { public: // Constructor. - explicit TestCaseNameIs(const String& name) + explicit TestCaseNameIs(const std::string& name) : name_(name) {} // Returns true iff the name of test_case matches name_. @@ -4084,7 +4155,7 @@ class TestCaseNameIs { } private: - String name_; + std::string name_; }; // Finds and returns a TestCase with the given name. If one doesn't @@ -4116,7 +4187,7 @@ TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); // Is this a death test case? - if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), + if (internal::UnitTestOptions::MatchesFilter(test_case_name, kDeathTestCaseFilter)) { // Yes. Inserts the test case after the last death test case // defined so far. This only works when the test cases haven't @@ -4202,6 +4273,7 @@ bool UnitTestImpl::RunAllTests() { TestEventListener* repeater = listeners()->repeater(); + start_timestamp_ = GetTimeInMillis(); repeater->OnTestProgramStart(*parent_); // How many times to repeat the tests? We don't want to repeat them @@ -4394,14 +4466,12 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { int num_selected_tests = 0; for (size_t i = 0; i < test_cases_.size(); i++) { TestCase* const test_case = test_cases_[i]; - const String &test_case_name = test_case->name(); + const std::string &test_case_name = test_case->name(); test_case->set_should_run(false); - bool any_matched_filter = false; - for (size_t j = 0; j < test_case->test_info_list().size(); j++) { TestInfo* const test_info = test_case->test_info_list()[j]; - const String test_name(test_info->name()); + const std::string test_name(test_info->name()); // A test is disabled if test case name or test name matches // kDisableTestFilter. const bool is_disabled = @@ -4415,7 +4485,6 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { internal::UnitTestOptions::FilterMatchesTest(test_case_name, test_name); test_info->matches_filter_ = matches_filter; - any_matched_filter |= matches_filter; const bool is_runnable = (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && @@ -4432,14 +4501,37 @@ int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { test_info->should_run_ = is_selected; test_case->set_should_run(test_case->should_run() || is_selected); } - - test_case->set_should_skip_report(!any_matched_filter); } return num_selected_tests; } +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + // Prints the names of the tests matching the user-specified filter flag. void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + for (size_t i = 0; i < test_cases_.size(); i++) { const TestCase* const test_case = test_cases_[i]; bool printed_test_case_name = false; @@ -4450,9 +4542,23 @@ void UnitTestImpl::ListTestsMatchingFilter() { if (test_info->matches_filter_) { if (!printed_test_case_name) { printed_test_case_name = true; - printf("%s.\n", test_case->name()); + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); } - printf(" %s\n", test_info->name()); + printf("\n"); } } } @@ -4516,7 +4622,7 @@ void UnitTestImpl::UnshuffleTests() { } } -// Returns the current OS stack trace as a String. +// Returns the current OS stack trace as an std::string. // // The maximum number of stack frames to be included is specified by // the gtest_stack_trace_depth flag. The skip_count parameter @@ -4526,8 +4632,8 @@ void UnitTestImpl::UnshuffleTests() { // For example, if Foo() calls Bar(), which in turn calls // GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in // the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. -String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, - int skip_count) { +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { // We pass skip_count + 1 to skip this wrapper function in addition // to what the user really wants to skip. return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); @@ -4575,7 +4681,7 @@ const char* ParseFlagValue(const char* str, if (str == NULL || flag == NULL) return NULL; // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. - const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag); + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; const size_t flag_len = flag_str.length(); if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; @@ -4640,7 +4746,7 @@ bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { // // On success, stores the value of the flag in *value, and returns // true. On failure, returns false without changing *value. -bool ParseStringFlag(const char* str, const char* flag, String* value) { +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { // Gets the value of the flag as a string. const char* const value_str = ParseFlagValue(str, flag, false); @@ -4692,7 +4798,7 @@ static void PrintColorEncoded(const char* str) { return; } - ColoredPrintf(color, "%s", String(str, p - str).c_str()); + ColoredPrintf(color, "%s", std::string(str, p).c_str()); const char ch = p[1]; str = p + 2; @@ -4782,7 +4888,7 @@ static const char kColorEncodedHelpMessage[] = template void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { for (int i = 1; i < *argc; i++) { - const String arg_string = StreamableToString(argv[i]); + const std::string arg_string = StreamableToString(argv[i]); const char* const arg = arg_string.c_str(); using internal::ParseBoolFlag; diff --git a/external/gtest/src/gtest_main.cc b/external/gtest/src/gtest_main.cc new file mode 100644 index 0000000000..f302822552 --- /dev/null +++ b/external/gtest/src/gtest_main.cc @@ -0,0 +1,38 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "gtest/gtest.h" + +GTEST_API_ int main(int argc, char **argv) { + printf("Running main() from gtest_main.cc\n"); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest-death-test_ex_test.cc b/external/gtest/test/gtest-death-test_ex_test.cc new file mode 100644 index 0000000000..b50a13d5e2 --- /dev/null +++ b/external/gtest/test/gtest-death-test_ex_test.cc @@ -0,0 +1,93 @@ +// Copyright 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) +// +// Tests that verify interaction of exceptions and death tests. + +#include "gtest/gtest-death-test.h" +#include "gtest/gtest.h" + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_HAS_SEH +# include // For RaiseException(). +# endif + +# include "gtest/gtest-spi.h" + +# if GTEST_HAS_EXCEPTIONS + +# include // For std::exception. + +// Tests that death tests report thrown exceptions as failures and that the +// exceptions do not escape death test macros. +TEST(CxxExceptionDeathTest, ExceptionIsFailure) { + try { + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(throw 1, ""), "threw an exception"); + } catch (...) { // NOLINT + FAIL() << "An exception escaped a death test macro invocation " + << "with catch_exceptions " + << (testing::GTEST_FLAG(catch_exceptions) ? "enabled" : "disabled"); + } +} + +class TestException : public std::exception { + public: + virtual const char* what() const throw() { return "exceptional message"; } +}; + +TEST(CxxExceptionDeathTest, PrintsMessageForStdExceptions) { + // Verifies that the exception message is quoted in the failure text. + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(throw TestException(), ""), + "exceptional message"); + // Verifies that the location is mentioned in the failure text. + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(throw TestException(), ""), + "gtest-death-test_ex_test.cc"); +} +# endif // GTEST_HAS_EXCEPTIONS + +# if GTEST_HAS_SEH +// Tests that enabling interception of SEH exceptions with the +// catch_exceptions flag does not interfere with SEH exceptions being +// treated as death by death tests. +TEST(SehExceptionDeasTest, CatchExceptionsDoesNotInterfere) { + EXPECT_DEATH(RaiseException(42, 0x0, 0, NULL), "") + << "with catch_exceptions " + << (testing::GTEST_FLAG(catch_exceptions) ? "enabled" : "disabled"); +} +# endif + +#endif // GTEST_HAS_DEATH_TEST + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + testing::GTEST_FLAG(catch_exceptions) = GTEST_ENABLE_CATCH_EXCEPTIONS_ != 0; + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest-death-test_test.cc b/external/gtest/test/gtest-death-test_test.cc new file mode 100644 index 0000000000..c2d26df993 --- /dev/null +++ b/external/gtest/test/gtest-death-test_test.cc @@ -0,0 +1,1367 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests for death tests. + +#include "gtest/gtest-death-test.h" +#include "gtest/gtest.h" +#include "gtest/internal/gtest-filepath.h" + +using testing::internal::AlwaysFalse; +using testing::internal::AlwaysTrue; + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_WINDOWS +# include // For chdir(). +# else +# include +# include // For waitpid. +# endif // GTEST_OS_WINDOWS + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include "gtest/gtest-spi.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +# define GTEST_IMPLEMENTATION_ 1 +# include "src/gtest-internal-inl.h" +# undef GTEST_IMPLEMENTATION_ + +namespace posix = ::testing::internal::posix; + +using testing::Message; +using testing::internal::DeathTest; +using testing::internal::DeathTestFactory; +using testing::internal::FilePath; +using testing::internal::GetLastErrnoDescription; +using testing::internal::GetUnitTestImpl; +using testing::internal::InDeathTestChild; +using testing::internal::ParseNaturalNumber; + +namespace testing { +namespace internal { + +// A helper class whose objects replace the death test factory for a +// single UnitTest object during their lifetimes. +class ReplaceDeathTestFactory { + public: + explicit ReplaceDeathTestFactory(DeathTestFactory* new_factory) + : unit_test_impl_(GetUnitTestImpl()) { + old_factory_ = unit_test_impl_->death_test_factory_.release(); + unit_test_impl_->death_test_factory_.reset(new_factory); + } + + ~ReplaceDeathTestFactory() { + unit_test_impl_->death_test_factory_.release(); + unit_test_impl_->death_test_factory_.reset(old_factory_); + } + private: + // Prevents copying ReplaceDeathTestFactory objects. + ReplaceDeathTestFactory(const ReplaceDeathTestFactory&); + void operator=(const ReplaceDeathTestFactory&); + + UnitTestImpl* unit_test_impl_; + DeathTestFactory* old_factory_; +}; + +} // namespace internal +} // namespace testing + +void DieWithMessage(const ::std::string& message) { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); // Make sure the text is printed before the process exits. + + // We call _exit() instead of exit(), as the former is a direct + // system call and thus safer in the presence of threads. exit() + // will invoke user-defined exit-hooks, which may do dangerous + // things that conflict with death tests. + // + // Some compilers can recognize that _exit() never returns and issue the + // 'unreachable code' warning for code following this function, unless + // fooled by a fake condition. + if (AlwaysTrue()) + _exit(1); +} + +void DieInside(const ::std::string& function) { + DieWithMessage("death inside " + function + "()."); +} + +// Tests that death tests work. + +class TestForDeathTest : public testing::Test { + protected: + TestForDeathTest() : original_dir_(FilePath::GetCurrentDir()) {} + + virtual ~TestForDeathTest() { + posix::ChDir(original_dir_.c_str()); + } + + // A static member function that's expected to die. + static void StaticMemberFunction() { DieInside("StaticMemberFunction"); } + + // A method of the test fixture that may die. + void MemberFunction() { + if (should_die_) + DieInside("MemberFunction"); + } + + // True iff MemberFunction() should die. + bool should_die_; + const FilePath original_dir_; +}; + +// A class with a member function that may die. +class MayDie { + public: + explicit MayDie(bool should_die) : should_die_(should_die) {} + + // A member function that may die. + void MemberFunction() const { + if (should_die_) + DieInside("MayDie::MemberFunction"); + } + + private: + // True iff MemberFunction() should die. + bool should_die_; +}; + +// A global function that's expected to die. +void GlobalFunction() { DieInside("GlobalFunction"); } + +// A non-void function that's expected to die. +int NonVoidFunction() { + DieInside("NonVoidFunction"); + return 1; +} + +// A unary function that may die. +void DieIf(bool should_die) { + if (should_die) + DieInside("DieIf"); +} + +// A binary function that may die. +bool DieIfLessThan(int x, int y) { + if (x < y) { + DieInside("DieIfLessThan"); + } + return true; +} + +// Tests that ASSERT_DEATH can be used outside a TEST, TEST_F, or test fixture. +void DeathTestSubroutine() { + EXPECT_DEATH(GlobalFunction(), "death.*GlobalFunction"); + ASSERT_DEATH(GlobalFunction(), "death.*GlobalFunction"); +} + +// Death in dbg, not opt. +int DieInDebugElse12(int* sideeffect) { + if (sideeffect) *sideeffect = 12; + +# ifndef NDEBUG + + DieInside("DieInDebugElse12"); + +# endif // NDEBUG + + return 12; +} + +# if GTEST_OS_WINDOWS + +// Tests the ExitedWithCode predicate. +TEST(ExitStatusPredicateTest, ExitedWithCode) { + // On Windows, the process's exit code is the same as its exit status, + // so the predicate just compares the its input with its parameter. + EXPECT_TRUE(testing::ExitedWithCode(0)(0)); + EXPECT_TRUE(testing::ExitedWithCode(1)(1)); + EXPECT_TRUE(testing::ExitedWithCode(42)(42)); + EXPECT_FALSE(testing::ExitedWithCode(0)(1)); + EXPECT_FALSE(testing::ExitedWithCode(1)(0)); +} + +# else + +// Returns the exit status of a process that calls _exit(2) with a +// given exit code. This is a helper function for the +// ExitStatusPredicateTest test suite. +static int NormalExitStatus(int exit_code) { + pid_t child_pid = fork(); + if (child_pid == 0) { + _exit(exit_code); + } + int status; + waitpid(child_pid, &status, 0); + return status; +} + +// Returns the exit status of a process that raises a given signal. +// If the signal does not cause the process to die, then it returns +// instead the exit status of a process that exits normally with exit +// code 1. This is a helper function for the ExitStatusPredicateTest +// test suite. +static int KilledExitStatus(int signum) { + pid_t child_pid = fork(); + if (child_pid == 0) { + raise(signum); + _exit(1); + } + int status; + waitpid(child_pid, &status, 0); + return status; +} + +// Tests the ExitedWithCode predicate. +TEST(ExitStatusPredicateTest, ExitedWithCode) { + const int status0 = NormalExitStatus(0); + const int status1 = NormalExitStatus(1); + const int status42 = NormalExitStatus(42); + const testing::ExitedWithCode pred0(0); + const testing::ExitedWithCode pred1(1); + const testing::ExitedWithCode pred42(42); + EXPECT_PRED1(pred0, status0); + EXPECT_PRED1(pred1, status1); + EXPECT_PRED1(pred42, status42); + EXPECT_FALSE(pred0(status1)); + EXPECT_FALSE(pred42(status0)); + EXPECT_FALSE(pred1(status42)); +} + +// Tests the KilledBySignal predicate. +TEST(ExitStatusPredicateTest, KilledBySignal) { + const int status_segv = KilledExitStatus(SIGSEGV); + const int status_kill = KilledExitStatus(SIGKILL); + const testing::KilledBySignal pred_segv(SIGSEGV); + const testing::KilledBySignal pred_kill(SIGKILL); + EXPECT_PRED1(pred_segv, status_segv); + EXPECT_PRED1(pred_kill, status_kill); + EXPECT_FALSE(pred_segv(status_kill)); + EXPECT_FALSE(pred_kill(status_segv)); +} + +# endif // GTEST_OS_WINDOWS + +// Tests that the death test macros expand to code which may or may not +// be followed by operator<<, and that in either case the complete text +// comprises only a single C++ statement. +TEST_F(TestForDeathTest, SingleStatement) { + if (AlwaysFalse()) + // This would fail if executed; this is a compilation test only + ASSERT_DEATH(return, ""); + + if (AlwaysTrue()) + EXPECT_DEATH(_exit(1), ""); + else + // This empty "else" branch is meant to ensure that EXPECT_DEATH + // doesn't expand into an "if" statement without an "else" + ; + + if (AlwaysFalse()) + ASSERT_DEATH(return, "") << "did not die"; + + if (AlwaysFalse()) + ; + else + EXPECT_DEATH(_exit(1), "") << 1 << 2 << 3; +} + +void DieWithEmbeddedNul() { + fprintf(stderr, "Hello%cmy null world.\n", '\0'); + fflush(stderr); + _exit(1); +} + +# if GTEST_USES_PCRE +// Tests that EXPECT_DEATH and ASSERT_DEATH work when the error +// message has a NUL character in it. +TEST_F(TestForDeathTest, EmbeddedNulInMessage) { + // TODO(wan@google.com): doesn't support matching strings + // with embedded NUL characters - find a way to workaround it. + EXPECT_DEATH(DieWithEmbeddedNul(), "my null world"); + ASSERT_DEATH(DieWithEmbeddedNul(), "my null world"); +} +# endif // GTEST_USES_PCRE + +// Tests that death test macros expand to code which interacts well with switch +// statements. +TEST_F(TestForDeathTest, SwitchStatement) { +// Microsoft compiler usually complains about switch statements without +// case labels. We suppress that warning for this test. +# ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4065) +# endif // _MSC_VER + + switch (0) + default: + ASSERT_DEATH(_exit(1), "") << "exit in default switch handler"; + + switch (0) + case 0: + EXPECT_DEATH(_exit(1), "") << "exit in switch case"; + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER +} + +// Tests that a static member function can be used in a "fast" style +// death test. +TEST_F(TestForDeathTest, StaticMemberFunctionFastStyle) { + testing::GTEST_FLAG(death_test_style) = "fast"; + ASSERT_DEATH(StaticMemberFunction(), "death.*StaticMember"); +} + +// Tests that a method of the test fixture can be used in a "fast" +// style death test. +TEST_F(TestForDeathTest, MemberFunctionFastStyle) { + testing::GTEST_FLAG(death_test_style) = "fast"; + should_die_ = true; + EXPECT_DEATH(MemberFunction(), "inside.*MemberFunction"); +} + +void ChangeToRootDir() { posix::ChDir(GTEST_PATH_SEP_); } + +// Tests that death tests work even if the current directory has been +// changed. +TEST_F(TestForDeathTest, FastDeathTestInChangedDir) { + testing::GTEST_FLAG(death_test_style) = "fast"; + + ChangeToRootDir(); + EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); + + ChangeToRootDir(); + ASSERT_DEATH(_exit(1), ""); +} + +# if GTEST_OS_LINUX +void SigprofAction(int, siginfo_t*, void*) { /* no op */ } + +// Sets SIGPROF action and ITIMER_PROF timer (interval: 1ms). +void SetSigprofActionAndTimer() { + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1; + timer.it_value = timer.it_interval; + ASSERT_EQ(0, setitimer(ITIMER_PROF, &timer, NULL)); + struct sigaction signal_action; + memset(&signal_action, 0, sizeof(signal_action)); + sigemptyset(&signal_action.sa_mask); + signal_action.sa_sigaction = SigprofAction; + signal_action.sa_flags = SA_RESTART | SA_SIGINFO; + ASSERT_EQ(0, sigaction(SIGPROF, &signal_action, NULL)); +} + +// Disables ITIMER_PROF timer and ignores SIGPROF signal. +void DisableSigprofActionAndTimer(struct sigaction* old_signal_action) { + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + timer.it_value = timer.it_interval; + ASSERT_EQ(0, setitimer(ITIMER_PROF, &timer, NULL)); + struct sigaction signal_action; + memset(&signal_action, 0, sizeof(signal_action)); + sigemptyset(&signal_action.sa_mask); + signal_action.sa_handler = SIG_IGN; + ASSERT_EQ(0, sigaction(SIGPROF, &signal_action, old_signal_action)); +} + +// Tests that death tests work when SIGPROF handler and timer are set. +TEST_F(TestForDeathTest, FastSigprofActionSet) { + testing::GTEST_FLAG(death_test_style) = "fast"; + SetSigprofActionAndTimer(); + EXPECT_DEATH(_exit(1), ""); + struct sigaction old_signal_action; + DisableSigprofActionAndTimer(&old_signal_action); + EXPECT_TRUE(old_signal_action.sa_sigaction == SigprofAction); +} + +TEST_F(TestForDeathTest, ThreadSafeSigprofActionSet) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + SetSigprofActionAndTimer(); + EXPECT_DEATH(_exit(1), ""); + struct sigaction old_signal_action; + DisableSigprofActionAndTimer(&old_signal_action); + EXPECT_TRUE(old_signal_action.sa_sigaction == SigprofAction); +} +# endif // GTEST_OS_LINUX + +// Repeats a representative sample of death tests in the "threadsafe" style: + +TEST_F(TestForDeathTest, StaticMemberFunctionThreadsafeStyle) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + ASSERT_DEATH(StaticMemberFunction(), "death.*StaticMember"); +} + +TEST_F(TestForDeathTest, MemberFunctionThreadsafeStyle) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + should_die_ = true; + EXPECT_DEATH(MemberFunction(), "inside.*MemberFunction"); +} + +TEST_F(TestForDeathTest, ThreadsafeDeathTestInLoop) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + + for (int i = 0; i < 3; ++i) + EXPECT_EXIT(_exit(i), testing::ExitedWithCode(i), "") << ": i = " << i; +} + +TEST_F(TestForDeathTest, ThreadsafeDeathTestInChangedDir) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + + ChangeToRootDir(); + EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); + + ChangeToRootDir(); + ASSERT_DEATH(_exit(1), ""); +} + +TEST_F(TestForDeathTest, MixedStyles) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + EXPECT_DEATH(_exit(1), ""); + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_DEATH(_exit(1), ""); +} + +# if GTEST_HAS_CLONE && GTEST_HAS_PTHREAD + +namespace { + +bool pthread_flag; + +void SetPthreadFlag() { + pthread_flag = true; +} + +} // namespace + +TEST_F(TestForDeathTest, DoesNotExecuteAtforkHooks) { + if (!testing::GTEST_FLAG(death_test_use_fork)) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + pthread_flag = false; + ASSERT_EQ(0, pthread_atfork(&SetPthreadFlag, NULL, NULL)); + ASSERT_DEATH(_exit(1), ""); + ASSERT_FALSE(pthread_flag); + } +} + +# endif // GTEST_HAS_CLONE && GTEST_HAS_PTHREAD + +// Tests that a method of another class can be used in a death test. +TEST_F(TestForDeathTest, MethodOfAnotherClass) { + const MayDie x(true); + ASSERT_DEATH(x.MemberFunction(), "MayDie\\:\\:MemberFunction"); +} + +// Tests that a global function can be used in a death test. +TEST_F(TestForDeathTest, GlobalFunction) { + EXPECT_DEATH(GlobalFunction(), "GlobalFunction"); +} + +// Tests that any value convertible to an RE works as a second +// argument to EXPECT_DEATH. +TEST_F(TestForDeathTest, AcceptsAnythingConvertibleToRE) { + static const char regex_c_str[] = "GlobalFunction"; + EXPECT_DEATH(GlobalFunction(), regex_c_str); + + const testing::internal::RE regex(regex_c_str); + EXPECT_DEATH(GlobalFunction(), regex); + +# if GTEST_HAS_GLOBAL_STRING + + const string regex_str(regex_c_str); + EXPECT_DEATH(GlobalFunction(), regex_str); + +# endif // GTEST_HAS_GLOBAL_STRING + + const ::std::string regex_std_str(regex_c_str); + EXPECT_DEATH(GlobalFunction(), regex_std_str); +} + +// Tests that a non-void function can be used in a death test. +TEST_F(TestForDeathTest, NonVoidFunction) { + ASSERT_DEATH(NonVoidFunction(), "NonVoidFunction"); +} + +// Tests that functions that take parameter(s) can be used in a death test. +TEST_F(TestForDeathTest, FunctionWithParameter) { + EXPECT_DEATH(DieIf(true), "DieIf\\(\\)"); + EXPECT_DEATH(DieIfLessThan(2, 3), "DieIfLessThan"); +} + +// Tests that ASSERT_DEATH can be used outside a TEST, TEST_F, or test fixture. +TEST_F(TestForDeathTest, OutsideFixture) { + DeathTestSubroutine(); +} + +// Tests that death tests can be done inside a loop. +TEST_F(TestForDeathTest, InsideLoop) { + for (int i = 0; i < 5; i++) { + EXPECT_DEATH(DieIfLessThan(-1, i), "DieIfLessThan") << "where i == " << i; + } +} + +// Tests that a compound statement can be used in a death test. +TEST_F(TestForDeathTest, CompoundStatement) { + EXPECT_DEATH({ // NOLINT + const int x = 2; + const int y = x + 1; + DieIfLessThan(x, y); + }, + "DieIfLessThan"); +} + +// Tests that code that doesn't die causes a death test to fail. +TEST_F(TestForDeathTest, DoesNotDie) { + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(DieIf(false), "DieIf"), + "failed to die"); +} + +// Tests that a death test fails when the error message isn't expected. +TEST_F(TestForDeathTest, ErrorMessageMismatch) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_DEATH(DieIf(true), "DieIfLessThan") << "End of death test message."; + }, "died but not with expected error"); +} + +// On exit, *aborted will be true iff the EXPECT_DEATH() statement +// aborted the function. +void ExpectDeathTestHelper(bool* aborted) { + *aborted = true; + EXPECT_DEATH(DieIf(false), "DieIf"); // This assertion should fail. + *aborted = false; +} + +// Tests that EXPECT_DEATH doesn't abort the test on failure. +TEST_F(TestForDeathTest, EXPECT_DEATH) { + bool aborted = true; + EXPECT_NONFATAL_FAILURE(ExpectDeathTestHelper(&aborted), + "failed to die"); + EXPECT_FALSE(aborted); +} + +// Tests that ASSERT_DEATH does abort the test on failure. +TEST_F(TestForDeathTest, ASSERT_DEATH) { + static bool aborted; + EXPECT_FATAL_FAILURE({ // NOLINT + aborted = true; + ASSERT_DEATH(DieIf(false), "DieIf"); // This assertion should fail. + aborted = false; + }, "failed to die"); + EXPECT_TRUE(aborted); +} + +// Tests that EXPECT_DEATH evaluates the arguments exactly once. +TEST_F(TestForDeathTest, SingleEvaluation) { + int x = 3; + EXPECT_DEATH(DieIf((++x) == 4), "DieIf"); + + const char* regex = "DieIf"; + const char* regex_save = regex; + EXPECT_DEATH(DieIfLessThan(3, 4), regex++); + EXPECT_EQ(regex_save + 1, regex); +} + +// Tests that run-away death tests are reported as failures. +TEST_F(TestForDeathTest, RunawayIsFailure) { + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH(static_cast(0), "Foo"), + "failed to die."); +} + +// Tests that death tests report executing 'return' in the statement as +// failure. +TEST_F(TestForDeathTest, ReturnIsFailure) { + EXPECT_FATAL_FAILURE(ASSERT_DEATH(return, "Bar"), + "illegal return in test statement."); +} + +// Tests that EXPECT_DEBUG_DEATH works as expected, that is, you can stream a +// message to it, and in debug mode it: +// 1. Asserts on death. +// 2. Has no side effect. +// +// And in opt mode, it: +// 1. Has side effects but does not assert. +TEST_F(TestForDeathTest, TestExpectDebugDeath) { + int sideeffect = 0; + + EXPECT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death.*DieInDebugElse12") + << "Must accept a streamed message"; + +# ifdef NDEBUG + + // Checks that the assignment occurs in opt mode (sideeffect). + EXPECT_EQ(12, sideeffect); + +# else + + // Checks that the assignment does not occur in dbg mode (no sideeffect). + EXPECT_EQ(0, sideeffect); + +# endif +} + +// Tests that ASSERT_DEBUG_DEATH works as expected, that is, you can stream a +// message to it, and in debug mode it: +// 1. Asserts on death. +// 2. Has no side effect. +// +// And in opt mode, it: +// 1. Has side effects but does not assert. +TEST_F(TestForDeathTest, TestAssertDebugDeath) { + int sideeffect = 0; + + ASSERT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death.*DieInDebugElse12") + << "Must accept a streamed message"; + +# ifdef NDEBUG + + // Checks that the assignment occurs in opt mode (sideeffect). + EXPECT_EQ(12, sideeffect); + +# else + + // Checks that the assignment does not occur in dbg mode (no sideeffect). + EXPECT_EQ(0, sideeffect); + +# endif +} + +# ifndef NDEBUG + +void ExpectDebugDeathHelper(bool* aborted) { + *aborted = true; + EXPECT_DEBUG_DEATH(return, "") << "This is expected to fail."; + *aborted = false; +} + +# if GTEST_OS_WINDOWS +TEST(PopUpDeathTest, DoesNotShowPopUpOnAbort) { + printf("This test should be considered failing if it shows " + "any pop-up dialogs.\n"); + fflush(stdout); + + EXPECT_DEATH({ + testing::GTEST_FLAG(catch_exceptions) = false; + abort(); + }, ""); +} +# endif // GTEST_OS_WINDOWS + +// Tests that EXPECT_DEBUG_DEATH in debug mode does not abort +// the function. +TEST_F(TestForDeathTest, ExpectDebugDeathDoesNotAbort) { + bool aborted = true; + EXPECT_NONFATAL_FAILURE(ExpectDebugDeathHelper(&aborted), ""); + EXPECT_FALSE(aborted); +} + +void AssertDebugDeathHelper(bool* aborted) { + *aborted = true; + ASSERT_DEBUG_DEATH(return, "") << "This is expected to fail."; + *aborted = false; +} + +// Tests that ASSERT_DEBUG_DEATH in debug mode aborts the function on +// failure. +TEST_F(TestForDeathTest, AssertDebugDeathAborts) { + static bool aborted; + aborted = false; + EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), ""); + EXPECT_TRUE(aborted); +} + +# endif // _NDEBUG + +// Tests the *_EXIT family of macros, using a variety of predicates. +static void TestExitMacros() { + EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); + ASSERT_EXIT(_exit(42), testing::ExitedWithCode(42), ""); + +# if GTEST_OS_WINDOWS + + // Of all signals effects on the process exit code, only those of SIGABRT + // are documented on Windows. + // See http://msdn.microsoft.com/en-us/library/dwwzkt4c(VS.71).aspx. + EXPECT_EXIT(raise(SIGABRT), testing::ExitedWithCode(3), "") << "b_ar"; + +# else + + EXPECT_EXIT(raise(SIGKILL), testing::KilledBySignal(SIGKILL), "") << "foo"; + ASSERT_EXIT(raise(SIGUSR2), testing::KilledBySignal(SIGUSR2), "") << "bar"; + + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_EXIT(_exit(0), testing::KilledBySignal(SIGSEGV), "") + << "This failure is expected, too."; + }, "This failure is expected, too."); + +# endif // GTEST_OS_WINDOWS + + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_EXIT(raise(SIGSEGV), testing::ExitedWithCode(0), "") + << "This failure is expected."; + }, "This failure is expected."); +} + +TEST_F(TestForDeathTest, ExitMacros) { + TestExitMacros(); +} + +TEST_F(TestForDeathTest, ExitMacrosUsingFork) { + testing::GTEST_FLAG(death_test_use_fork) = true; + TestExitMacros(); +} + +TEST_F(TestForDeathTest, InvalidStyle) { + testing::GTEST_FLAG(death_test_style) = "rococo"; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_DEATH(_exit(0), "") << "This failure is expected."; + }, "This failure is expected."); +} + +TEST_F(TestForDeathTest, DeathTestFailedOutput) { + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_NONFATAL_FAILURE( + EXPECT_DEATH(DieWithMessage("death\n"), + "expected message"), + "Actual msg:\n" + "[ DEATH ] death\n"); +} + +TEST_F(TestForDeathTest, DeathTestUnexpectedReturnOutput) { + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_NONFATAL_FAILURE( + EXPECT_DEATH({ + fprintf(stderr, "returning\n"); + fflush(stderr); + return; + }, ""), + " Result: illegal return in test statement.\n" + " Error msg:\n" + "[ DEATH ] returning\n"); +} + +TEST_F(TestForDeathTest, DeathTestBadExitCodeOutput) { + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_NONFATAL_FAILURE( + EXPECT_EXIT(DieWithMessage("exiting with rc 1\n"), + testing::ExitedWithCode(3), + "expected message"), + " Result: died but not with expected exit code:\n" + " Exited with exit status 1\n" + "Actual msg:\n" + "[ DEATH ] exiting with rc 1\n"); +} + +TEST_F(TestForDeathTest, DeathTestMultiLineMatchFail) { + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_NONFATAL_FAILURE( + EXPECT_DEATH(DieWithMessage("line 1\nline 2\nline 3\n"), + "line 1\nxyz\nline 3\n"), + "Actual msg:\n" + "[ DEATH ] line 1\n" + "[ DEATH ] line 2\n" + "[ DEATH ] line 3\n"); +} + +TEST_F(TestForDeathTest, DeathTestMultiLineMatchPass) { + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_DEATH(DieWithMessage("line 1\nline 2\nline 3\n"), + "line 1\nline 2\nline 3\n"); +} + +// A DeathTestFactory that returns MockDeathTests. +class MockDeathTestFactory : public DeathTestFactory { + public: + MockDeathTestFactory(); + virtual bool Create(const char* statement, + const ::testing::internal::RE* regex, + const char* file, int line, DeathTest** test); + + // Sets the parameters for subsequent calls to Create. + void SetParameters(bool create, DeathTest::TestRole role, + int status, bool passed); + + // Accessors. + int AssumeRoleCalls() const { return assume_role_calls_; } + int WaitCalls() const { return wait_calls_; } + int PassedCalls() const { return passed_args_.size(); } + bool PassedArgument(int n) const { return passed_args_[n]; } + int AbortCalls() const { return abort_args_.size(); } + DeathTest::AbortReason AbortArgument(int n) const { + return abort_args_[n]; + } + bool TestDeleted() const { return test_deleted_; } + + private: + friend class MockDeathTest; + // If true, Create will return a MockDeathTest; otherwise it returns + // NULL. + bool create_; + // The value a MockDeathTest will return from its AssumeRole method. + DeathTest::TestRole role_; + // The value a MockDeathTest will return from its Wait method. + int status_; + // The value a MockDeathTest will return from its Passed method. + bool passed_; + + // Number of times AssumeRole was called. + int assume_role_calls_; + // Number of times Wait was called. + int wait_calls_; + // The arguments to the calls to Passed since the last call to + // SetParameters. + std::vector passed_args_; + // The arguments to the calls to Abort since the last call to + // SetParameters. + std::vector abort_args_; + // True if the last MockDeathTest returned by Create has been + // deleted. + bool test_deleted_; +}; + + +// A DeathTest implementation useful in testing. It returns values set +// at its creation from its various inherited DeathTest methods, and +// reports calls to those methods to its parent MockDeathTestFactory +// object. +class MockDeathTest : public DeathTest { + public: + MockDeathTest(MockDeathTestFactory *parent, + TestRole role, int status, bool passed) : + parent_(parent), role_(role), status_(status), passed_(passed) { + } + virtual ~MockDeathTest() { + parent_->test_deleted_ = true; + } + virtual TestRole AssumeRole() { + ++parent_->assume_role_calls_; + return role_; + } + virtual int Wait() { + ++parent_->wait_calls_; + return status_; + } + virtual bool Passed(bool exit_status_ok) { + parent_->passed_args_.push_back(exit_status_ok); + return passed_; + } + virtual void Abort(AbortReason reason) { + parent_->abort_args_.push_back(reason); + } + + private: + MockDeathTestFactory* const parent_; + const TestRole role_; + const int status_; + const bool passed_; +}; + + +// MockDeathTestFactory constructor. +MockDeathTestFactory::MockDeathTestFactory() + : create_(true), + role_(DeathTest::OVERSEE_TEST), + status_(0), + passed_(true), + assume_role_calls_(0), + wait_calls_(0), + passed_args_(), + abort_args_() { +} + + +// Sets the parameters for subsequent calls to Create. +void MockDeathTestFactory::SetParameters(bool create, + DeathTest::TestRole role, + int status, bool passed) { + create_ = create; + role_ = role; + status_ = status; + passed_ = passed; + + assume_role_calls_ = 0; + wait_calls_ = 0; + passed_args_.clear(); + abort_args_.clear(); +} + + +// Sets test to NULL (if create_ is false) or to the address of a new +// MockDeathTest object with parameters taken from the last call +// to SetParameters (if create_ is true). Always returns true. +bool MockDeathTestFactory::Create(const char* /*statement*/, + const ::testing::internal::RE* /*regex*/, + const char* /*file*/, + int /*line*/, + DeathTest** test) { + test_deleted_ = false; + if (create_) { + *test = new MockDeathTest(this, role_, status_, passed_); + } else { + *test = NULL; + } + return true; +} + +// A test fixture for testing the logic of the GTEST_DEATH_TEST_ macro. +// It installs a MockDeathTestFactory that is used for the duration +// of the test case. +class MacroLogicDeathTest : public testing::Test { + protected: + static testing::internal::ReplaceDeathTestFactory* replacer_; + static MockDeathTestFactory* factory_; + + static void SetUpTestCase() { + factory_ = new MockDeathTestFactory; + replacer_ = new testing::internal::ReplaceDeathTestFactory(factory_); + } + + static void TearDownTestCase() { + delete replacer_; + replacer_ = NULL; + delete factory_; + factory_ = NULL; + } + + // Runs a death test that breaks the rules by returning. Such a death + // test cannot be run directly from a test routine that uses a + // MockDeathTest, or the remainder of the routine will not be executed. + static void RunReturningDeathTest(bool* flag) { + ASSERT_DEATH({ // NOLINT + *flag = true; + return; + }, ""); + } +}; + +testing::internal::ReplaceDeathTestFactory* MacroLogicDeathTest::replacer_ + = NULL; +MockDeathTestFactory* MacroLogicDeathTest::factory_ = NULL; + + +// Test that nothing happens when the factory doesn't return a DeathTest: +TEST_F(MacroLogicDeathTest, NothingHappens) { + bool flag = false; + factory_->SetParameters(false, DeathTest::OVERSEE_TEST, 0, true); + EXPECT_DEATH(flag = true, ""); + EXPECT_FALSE(flag); + EXPECT_EQ(0, factory_->AssumeRoleCalls()); + EXPECT_EQ(0, factory_->WaitCalls()); + EXPECT_EQ(0, factory_->PassedCalls()); + EXPECT_EQ(0, factory_->AbortCalls()); + EXPECT_FALSE(factory_->TestDeleted()); +} + +// Test that the parent process doesn't run the death test code, +// and that the Passed method returns false when the (simulated) +// child process exits with status 0: +TEST_F(MacroLogicDeathTest, ChildExitsSuccessfully) { + bool flag = false; + factory_->SetParameters(true, DeathTest::OVERSEE_TEST, 0, true); + EXPECT_DEATH(flag = true, ""); + EXPECT_FALSE(flag); + EXPECT_EQ(1, factory_->AssumeRoleCalls()); + EXPECT_EQ(1, factory_->WaitCalls()); + ASSERT_EQ(1, factory_->PassedCalls()); + EXPECT_FALSE(factory_->PassedArgument(0)); + EXPECT_EQ(0, factory_->AbortCalls()); + EXPECT_TRUE(factory_->TestDeleted()); +} + +// Tests that the Passed method was given the argument "true" when +// the (simulated) child process exits with status 1: +TEST_F(MacroLogicDeathTest, ChildExitsUnsuccessfully) { + bool flag = false; + factory_->SetParameters(true, DeathTest::OVERSEE_TEST, 1, true); + EXPECT_DEATH(flag = true, ""); + EXPECT_FALSE(flag); + EXPECT_EQ(1, factory_->AssumeRoleCalls()); + EXPECT_EQ(1, factory_->WaitCalls()); + ASSERT_EQ(1, factory_->PassedCalls()); + EXPECT_TRUE(factory_->PassedArgument(0)); + EXPECT_EQ(0, factory_->AbortCalls()); + EXPECT_TRUE(factory_->TestDeleted()); +} + +// Tests that the (simulated) child process executes the death test +// code, and is aborted with the correct AbortReason if it +// executes a return statement. +TEST_F(MacroLogicDeathTest, ChildPerformsReturn) { + bool flag = false; + factory_->SetParameters(true, DeathTest::EXECUTE_TEST, 0, true); + RunReturningDeathTest(&flag); + EXPECT_TRUE(flag); + EXPECT_EQ(1, factory_->AssumeRoleCalls()); + EXPECT_EQ(0, factory_->WaitCalls()); + EXPECT_EQ(0, factory_->PassedCalls()); + EXPECT_EQ(1, factory_->AbortCalls()); + EXPECT_EQ(DeathTest::TEST_ENCOUNTERED_RETURN_STATEMENT, + factory_->AbortArgument(0)); + EXPECT_TRUE(factory_->TestDeleted()); +} + +// Tests that the (simulated) child process is aborted with the +// correct AbortReason if it does not die. +TEST_F(MacroLogicDeathTest, ChildDoesNotDie) { + bool flag = false; + factory_->SetParameters(true, DeathTest::EXECUTE_TEST, 0, true); + EXPECT_DEATH(flag = true, ""); + EXPECT_TRUE(flag); + EXPECT_EQ(1, factory_->AssumeRoleCalls()); + EXPECT_EQ(0, factory_->WaitCalls()); + EXPECT_EQ(0, factory_->PassedCalls()); + // This time there are two calls to Abort: one since the test didn't + // die, and another from the ReturnSentinel when it's destroyed. The + // sentinel normally isn't destroyed if a test doesn't die, since + // _exit(2) is called in that case by ForkingDeathTest, but not by + // our MockDeathTest. + ASSERT_EQ(2, factory_->AbortCalls()); + EXPECT_EQ(DeathTest::TEST_DID_NOT_DIE, + factory_->AbortArgument(0)); + EXPECT_EQ(DeathTest::TEST_ENCOUNTERED_RETURN_STATEMENT, + factory_->AbortArgument(1)); + EXPECT_TRUE(factory_->TestDeleted()); +} + +// Tests that a successful death test does not register a successful +// test part. +TEST(SuccessRegistrationDeathTest, NoSuccessPart) { + EXPECT_DEATH(_exit(1), ""); + EXPECT_EQ(0, GetUnitTestImpl()->current_test_result()->total_part_count()); +} + +TEST(StreamingAssertionsDeathTest, DeathTest) { + EXPECT_DEATH(_exit(1), "") << "unexpected failure"; + ASSERT_DEATH(_exit(1), "") << "unexpected failure"; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_DEATH(_exit(0), "") << "expected failure"; + }, "expected failure"); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_DEATH(_exit(0), "") << "expected failure"; + }, "expected failure"); +} + +// Tests that GetLastErrnoDescription returns an empty string when the +// last error is 0 and non-empty string when it is non-zero. +TEST(GetLastErrnoDescription, GetLastErrnoDescriptionWorks) { + errno = ENOENT; + EXPECT_STRNE("", GetLastErrnoDescription().c_str()); + errno = 0; + EXPECT_STREQ("", GetLastErrnoDescription().c_str()); +} + +# if GTEST_OS_WINDOWS +TEST(AutoHandleTest, AutoHandleWorks) { + HANDLE handle = ::CreateEvent(NULL, FALSE, FALSE, NULL); + ASSERT_NE(INVALID_HANDLE_VALUE, handle); + + // Tests that the AutoHandle is correctly initialized with a handle. + testing::internal::AutoHandle auto_handle(handle); + EXPECT_EQ(handle, auto_handle.Get()); + + // Tests that Reset assigns INVALID_HANDLE_VALUE. + // Note that this cannot verify whether the original handle is closed. + auto_handle.Reset(); + EXPECT_EQ(INVALID_HANDLE_VALUE, auto_handle.Get()); + + // Tests that Reset assigns the new handle. + // Note that this cannot verify whether the original handle is closed. + handle = ::CreateEvent(NULL, FALSE, FALSE, NULL); + ASSERT_NE(INVALID_HANDLE_VALUE, handle); + auto_handle.Reset(handle); + EXPECT_EQ(handle, auto_handle.Get()); + + // Tests that AutoHandle contains INVALID_HANDLE_VALUE by default. + testing::internal::AutoHandle auto_handle2; + EXPECT_EQ(INVALID_HANDLE_VALUE, auto_handle2.Get()); +} +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_WINDOWS +typedef unsigned __int64 BiggestParsable; +typedef signed __int64 BiggestSignedParsable; +# else +typedef unsigned long long BiggestParsable; +typedef signed long long BiggestSignedParsable; +# endif // GTEST_OS_WINDOWS + +// We cannot use std::numeric_limits::max() as it clashes with the +// max() macro defined by . +const BiggestParsable kBiggestParsableMax = ULLONG_MAX; +const BiggestSignedParsable kBiggestSignedParsableMax = LLONG_MAX; + +TEST(ParseNaturalNumberTest, RejectsInvalidFormat) { + BiggestParsable result = 0; + + // Rejects non-numbers. + EXPECT_FALSE(ParseNaturalNumber("non-number string", &result)); + + // Rejects numbers with whitespace prefix. + EXPECT_FALSE(ParseNaturalNumber(" 123", &result)); + + // Rejects negative numbers. + EXPECT_FALSE(ParseNaturalNumber("-123", &result)); + + // Rejects numbers starting with a plus sign. + EXPECT_FALSE(ParseNaturalNumber("+123", &result)); + errno = 0; +} + +TEST(ParseNaturalNumberTest, RejectsOverflownNumbers) { + BiggestParsable result = 0; + + EXPECT_FALSE(ParseNaturalNumber("99999999999999999999999", &result)); + + signed char char_result = 0; + EXPECT_FALSE(ParseNaturalNumber("200", &char_result)); + errno = 0; +} + +TEST(ParseNaturalNumberTest, AcceptsValidNumbers) { + BiggestParsable result = 0; + + result = 0; + ASSERT_TRUE(ParseNaturalNumber("123", &result)); + EXPECT_EQ(123U, result); + + // Check 0 as an edge case. + result = 1; + ASSERT_TRUE(ParseNaturalNumber("0", &result)); + EXPECT_EQ(0U, result); + + result = 1; + ASSERT_TRUE(ParseNaturalNumber("00000", &result)); + EXPECT_EQ(0U, result); +} + +TEST(ParseNaturalNumberTest, AcceptsTypeLimits) { + Message msg; + msg << kBiggestParsableMax; + + BiggestParsable result = 0; + EXPECT_TRUE(ParseNaturalNumber(msg.GetString(), &result)); + EXPECT_EQ(kBiggestParsableMax, result); + + Message msg2; + msg2 << kBiggestSignedParsableMax; + + BiggestSignedParsable signed_result = 0; + EXPECT_TRUE(ParseNaturalNumber(msg2.GetString(), &signed_result)); + EXPECT_EQ(kBiggestSignedParsableMax, signed_result); + + Message msg3; + msg3 << INT_MAX; + + int int_result = 0; + EXPECT_TRUE(ParseNaturalNumber(msg3.GetString(), &int_result)); + EXPECT_EQ(INT_MAX, int_result); + + Message msg4; + msg4 << UINT_MAX; + + unsigned int uint_result = 0; + EXPECT_TRUE(ParseNaturalNumber(msg4.GetString(), &uint_result)); + EXPECT_EQ(UINT_MAX, uint_result); +} + +TEST(ParseNaturalNumberTest, WorksForShorterIntegers) { + short short_result = 0; + ASSERT_TRUE(ParseNaturalNumber("123", &short_result)); + EXPECT_EQ(123, short_result); + + signed char char_result = 0; + ASSERT_TRUE(ParseNaturalNumber("123", &char_result)); + EXPECT_EQ(123, char_result); +} + +# if GTEST_OS_WINDOWS +TEST(EnvironmentTest, HandleFitsIntoSizeT) { + // TODO(vladl@google.com): Remove this test after this condition is verified + // in a static assertion in gtest-death-test.cc in the function + // GetStatusFileDescriptor. + ASSERT_TRUE(sizeof(HANDLE) <= sizeof(size_t)); +} +# endif // GTEST_OS_WINDOWS + +// Tests that EXPECT_DEATH_IF_SUPPORTED/ASSERT_DEATH_IF_SUPPORTED trigger +// failures when death tests are available on the system. +TEST(ConditionalDeathMacrosDeathTest, ExpectsDeathWhenDeathTestsAvailable) { + EXPECT_DEATH_IF_SUPPORTED(DieInside("CondDeathTestExpectMacro"), + "death inside CondDeathTestExpectMacro"); + ASSERT_DEATH_IF_SUPPORTED(DieInside("CondDeathTestAssertMacro"), + "death inside CondDeathTestAssertMacro"); + + // Empty statement will not crash, which must trigger a failure. + EXPECT_NONFATAL_FAILURE(EXPECT_DEATH_IF_SUPPORTED(;, ""), ""); + EXPECT_FATAL_FAILURE(ASSERT_DEATH_IF_SUPPORTED(;, ""), ""); +} + +#else + +using testing::internal::CaptureStderr; +using testing::internal::GetCapturedStderr; + +// Tests that EXPECT_DEATH_IF_SUPPORTED/ASSERT_DEATH_IF_SUPPORTED are still +// defined but do not trigger failures when death tests are not available on +// the system. +TEST(ConditionalDeathMacrosTest, WarnsWhenDeathTestsNotAvailable) { + // Empty statement will not crash, but that should not trigger a failure + // when death tests are not supported. + CaptureStderr(); + EXPECT_DEATH_IF_SUPPORTED(;, ""); + std::string output = GetCapturedStderr(); + ASSERT_TRUE(NULL != strstr(output.c_str(), + "Death tests are not supported on this platform")); + ASSERT_TRUE(NULL != strstr(output.c_str(), ";")); + + // The streamed message should not be printed as there is no test failure. + CaptureStderr(); + EXPECT_DEATH_IF_SUPPORTED(;, "") << "streamed message"; + output = GetCapturedStderr(); + ASSERT_TRUE(NULL == strstr(output.c_str(), "streamed message")); + + CaptureStderr(); + ASSERT_DEATH_IF_SUPPORTED(;, ""); // NOLINT + output = GetCapturedStderr(); + ASSERT_TRUE(NULL != strstr(output.c_str(), + "Death tests are not supported on this platform")); + ASSERT_TRUE(NULL != strstr(output.c_str(), ";")); + + CaptureStderr(); + ASSERT_DEATH_IF_SUPPORTED(;, "") << "streamed message"; // NOLINT + output = GetCapturedStderr(); + ASSERT_TRUE(NULL == strstr(output.c_str(), "streamed message")); +} + +void FuncWithAssert(int* n) { + ASSERT_DEATH_IF_SUPPORTED(return;, ""); + (*n)++; +} + +// Tests that ASSERT_DEATH_IF_SUPPORTED does not return from the current +// function (as ASSERT_DEATH does) if death tests are not supported. +TEST(ConditionalDeathMacrosTest, AssertDeatDoesNotReturnhIfUnsupported) { + int n = 0; + FuncWithAssert(&n); + EXPECT_EQ(1, n); +} + +TEST(InDeathTestChildDeathTest, ReportsDeathTestCorrectlyInFastStyle) { + testing::GTEST_FLAG(death_test_style) = "fast"; + EXPECT_FALSE(InDeathTestChild()); + EXPECT_DEATH({ + fprintf(stderr, InDeathTestChild() ? "Inside" : "Outside"); + fflush(stderr); + _exit(1); + }, "Inside"); +} + +TEST(InDeathTestChildDeathTest, ReportsDeathTestCorrectlyInThreadSafeStyle) { + testing::GTEST_FLAG(death_test_style) = "threadsafe"; + EXPECT_FALSE(InDeathTestChild()); + EXPECT_DEATH({ + fprintf(stderr, InDeathTestChild() ? "Inside" : "Outside"); + fflush(stderr); + _exit(1); + }, "Inside"); +} + +#endif // GTEST_HAS_DEATH_TEST + +// Tests that the death test macros expand to code which may or may not +// be followed by operator<<, and that in either case the complete text +// comprises only a single C++ statement. +// +// The syntax should work whether death tests are available or not. +TEST(ConditionalDeathMacrosSyntaxDeathTest, SingleStatement) { + if (AlwaysFalse()) + // This would fail if executed; this is a compilation test only + ASSERT_DEATH_IF_SUPPORTED(return, ""); + + if (AlwaysTrue()) + EXPECT_DEATH_IF_SUPPORTED(_exit(1), ""); + else + // This empty "else" branch is meant to ensure that EXPECT_DEATH + // doesn't expand into an "if" statement without an "else" + ; // NOLINT + + if (AlwaysFalse()) + ASSERT_DEATH_IF_SUPPORTED(return, "") << "did not die"; + + if (AlwaysFalse()) + ; // NOLINT + else + EXPECT_DEATH_IF_SUPPORTED(_exit(1), "") << 1 << 2 << 3; +} + +// Tests that conditional death test macros expand to code which interacts +// well with switch statements. +TEST(ConditionalDeathMacrosSyntaxDeathTest, SwitchStatement) { +// Microsoft compiler usually complains about switch statements without +// case labels. We suppress that warning for this test. +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4065) +#endif // _MSC_VER + + switch (0) + default: + ASSERT_DEATH_IF_SUPPORTED(_exit(1), "") + << "exit in default switch handler"; + + switch (0) + case 0: + EXPECT_DEATH_IF_SUPPORTED(_exit(1), "") << "exit in switch case"; + +#ifdef _MSC_VER +# pragma warning(pop) +#endif // _MSC_VER +} + +// Tests that a test case whose name ends with "DeathTest" works fine +// on Windows. +TEST(NotADeathTest, Test) { + SUCCEED(); +} diff --git a/external/gtest/test/gtest-filepath_test.cc b/external/gtest/test/gtest-filepath_test.cc new file mode 100644 index 0000000000..ae9f55a0c9 --- /dev/null +++ b/external/gtest/test/gtest-filepath_test.cc @@ -0,0 +1,680 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This file tests classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included from gtest_unittest.cc, to avoid changing +// build or make-files for some existing Google Test clients. Do not +// #include this file anywhere else! + +#include "gtest/internal/gtest-filepath.h" +#include "gtest/gtest.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS_MOBILE +# include // NOLINT +#elif GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS_MOBILE + +namespace testing { +namespace internal { +namespace { + +#if GTEST_OS_WINDOWS_MOBILE +// TODO(wan@google.com): Move these to the POSIX adapter section in +// gtest-port.h. + +// Windows CE doesn't have the remove C function. +int remove(const char* path) { + LPCWSTR wpath = String::AnsiToUtf16(path); + int ret = DeleteFile(wpath) ? 0 : -1; + delete [] wpath; + return ret; +} +// Windows CE doesn't have the _rmdir C function. +int _rmdir(const char* path) { + FilePath filepath(path); + LPCWSTR wpath = String::AnsiToUtf16( + filepath.RemoveTrailingPathSeparator().c_str()); + int ret = RemoveDirectory(wpath) ? 0 : -1; + delete [] wpath; + return ret; +} + +#else + +TEST(GetCurrentDirTest, ReturnsCurrentDir) { + const FilePath original_dir = FilePath::GetCurrentDir(); + EXPECT_FALSE(original_dir.IsEmpty()); + + posix::ChDir(GTEST_PATH_SEP_); + const FilePath cwd = FilePath::GetCurrentDir(); + posix::ChDir(original_dir.c_str()); + +# if GTEST_OS_WINDOWS + + // Skips the ":". + const char* const cwd_without_drive = strchr(cwd.c_str(), ':'); + ASSERT_TRUE(cwd_without_drive != NULL); + EXPECT_STREQ(GTEST_PATH_SEP_, cwd_without_drive + 1); + +# else + + EXPECT_EQ(GTEST_PATH_SEP_, cwd.string()); + +# endif +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +TEST(IsEmptyTest, ReturnsTrueForEmptyPath) { + EXPECT_TRUE(FilePath("").IsEmpty()); +} + +TEST(IsEmptyTest, ReturnsFalseForNonEmptyPath) { + EXPECT_FALSE(FilePath("a").IsEmpty()); + EXPECT_FALSE(FilePath(".").IsEmpty()); + EXPECT_FALSE(FilePath("a/b").IsEmpty()); + EXPECT_FALSE(FilePath("a\\b\\").IsEmpty()); +} + +// RemoveDirectoryName "" -> "" +TEST(RemoveDirectoryNameTest, WhenEmptyName) { + EXPECT_EQ("", FilePath("").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName "afile" -> "afile" +TEST(RemoveDirectoryNameTest, ButNoDirectory) { + EXPECT_EQ("afile", + FilePath("afile").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName "/afile" -> "afile" +TEST(RemoveDirectoryNameTest, RootFileShouldGiveFileName) { + EXPECT_EQ("afile", + FilePath(GTEST_PATH_SEP_ "afile").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName "adir/" -> "" +TEST(RemoveDirectoryNameTest, WhereThereIsNoFileName) { + EXPECT_EQ("", + FilePath("adir" GTEST_PATH_SEP_).RemoveDirectoryName().string()); +} + +// RemoveDirectoryName "adir/afile" -> "afile" +TEST(RemoveDirectoryNameTest, ShouldGiveFileName) { + EXPECT_EQ("afile", + FilePath("adir" GTEST_PATH_SEP_ "afile").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName "adir/subdir/afile" -> "afile" +TEST(RemoveDirectoryNameTest, ShouldAlsoGiveFileName) { + EXPECT_EQ("afile", + FilePath("adir" GTEST_PATH_SEP_ "subdir" GTEST_PATH_SEP_ "afile") + .RemoveDirectoryName().string()); +} + +#if GTEST_HAS_ALT_PATH_SEP_ + +// Tests that RemoveDirectoryName() works with the alternate separator +// on Windows. + +// RemoveDirectoryName("/afile") -> "afile" +TEST(RemoveDirectoryNameTest, RootFileShouldGiveFileNameForAlternateSeparator) { + EXPECT_EQ("afile", FilePath("/afile").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName("adir/") -> "" +TEST(RemoveDirectoryNameTest, WhereThereIsNoFileNameForAlternateSeparator) { + EXPECT_EQ("", FilePath("adir/").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName("adir/afile") -> "afile" +TEST(RemoveDirectoryNameTest, ShouldGiveFileNameForAlternateSeparator) { + EXPECT_EQ("afile", FilePath("adir/afile").RemoveDirectoryName().string()); +} + +// RemoveDirectoryName("adir/subdir/afile") -> "afile" +TEST(RemoveDirectoryNameTest, ShouldAlsoGiveFileNameForAlternateSeparator) { + EXPECT_EQ("afile", + FilePath("adir/subdir/afile").RemoveDirectoryName().string()); +} + +#endif + +// RemoveFileName "" -> "./" +TEST(RemoveFileNameTest, EmptyName) { +#if GTEST_OS_WINDOWS_MOBILE + // On Windows CE, we use the root as the current directory. + EXPECT_EQ(GTEST_PATH_SEP_, FilePath("").RemoveFileName().string()); +#else + EXPECT_EQ("." GTEST_PATH_SEP_, FilePath("").RemoveFileName().string()); +#endif +} + +// RemoveFileName "adir/" -> "adir/" +TEST(RemoveFileNameTest, ButNoFile) { + EXPECT_EQ("adir" GTEST_PATH_SEP_, + FilePath("adir" GTEST_PATH_SEP_).RemoveFileName().string()); +} + +// RemoveFileName "adir/afile" -> "adir/" +TEST(RemoveFileNameTest, GivesDirName) { + EXPECT_EQ("adir" GTEST_PATH_SEP_, + FilePath("adir" GTEST_PATH_SEP_ "afile").RemoveFileName().string()); +} + +// RemoveFileName "adir/subdir/afile" -> "adir/subdir/" +TEST(RemoveFileNameTest, GivesDirAndSubDirName) { + EXPECT_EQ("adir" GTEST_PATH_SEP_ "subdir" GTEST_PATH_SEP_, + FilePath("adir" GTEST_PATH_SEP_ "subdir" GTEST_PATH_SEP_ "afile") + .RemoveFileName().string()); +} + +// RemoveFileName "/afile" -> "/" +TEST(RemoveFileNameTest, GivesRootDir) { + EXPECT_EQ(GTEST_PATH_SEP_, + FilePath(GTEST_PATH_SEP_ "afile").RemoveFileName().string()); +} + +#if GTEST_HAS_ALT_PATH_SEP_ + +// Tests that RemoveFileName() works with the alternate separator on +// Windows. + +// RemoveFileName("adir/") -> "adir/" +TEST(RemoveFileNameTest, ButNoFileForAlternateSeparator) { + EXPECT_EQ("adir" GTEST_PATH_SEP_, + FilePath("adir/").RemoveFileName().string()); +} + +// RemoveFileName("adir/afile") -> "adir/" +TEST(RemoveFileNameTest, GivesDirNameForAlternateSeparator) { + EXPECT_EQ("adir" GTEST_PATH_SEP_, + FilePath("adir/afile").RemoveFileName().string()); +} + +// RemoveFileName("adir/subdir/afile") -> "adir/subdir/" +TEST(RemoveFileNameTest, GivesDirAndSubDirNameForAlternateSeparator) { + EXPECT_EQ("adir" GTEST_PATH_SEP_ "subdir" GTEST_PATH_SEP_, + FilePath("adir/subdir/afile").RemoveFileName().string()); +} + +// RemoveFileName("/afile") -> "\" +TEST(RemoveFileNameTest, GivesRootDirForAlternateSeparator) { + EXPECT_EQ(GTEST_PATH_SEP_, FilePath("/afile").RemoveFileName().string()); +} + +#endif + +TEST(MakeFileNameTest, GenerateWhenNumberIsZero) { + FilePath actual = FilePath::MakeFileName(FilePath("foo"), FilePath("bar"), + 0, "xml"); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar.xml", actual.string()); +} + +TEST(MakeFileNameTest, GenerateFileNameNumberGtZero) { + FilePath actual = FilePath::MakeFileName(FilePath("foo"), FilePath("bar"), + 12, "xml"); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar_12.xml", actual.string()); +} + +TEST(MakeFileNameTest, GenerateFileNameWithSlashNumberIsZero) { + FilePath actual = FilePath::MakeFileName(FilePath("foo" GTEST_PATH_SEP_), + FilePath("bar"), 0, "xml"); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar.xml", actual.string()); +} + +TEST(MakeFileNameTest, GenerateFileNameWithSlashNumberGtZero) { + FilePath actual = FilePath::MakeFileName(FilePath("foo" GTEST_PATH_SEP_), + FilePath("bar"), 12, "xml"); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar_12.xml", actual.string()); +} + +TEST(MakeFileNameTest, GenerateWhenNumberIsZeroAndDirIsEmpty) { + FilePath actual = FilePath::MakeFileName(FilePath(""), FilePath("bar"), + 0, "xml"); + EXPECT_EQ("bar.xml", actual.string()); +} + +TEST(MakeFileNameTest, GenerateWhenNumberIsNotZeroAndDirIsEmpty) { + FilePath actual = FilePath::MakeFileName(FilePath(""), FilePath("bar"), + 14, "xml"); + EXPECT_EQ("bar_14.xml", actual.string()); +} + +TEST(ConcatPathsTest, WorksWhenDirDoesNotEndWithPathSep) { + FilePath actual = FilePath::ConcatPaths(FilePath("foo"), + FilePath("bar.xml")); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar.xml", actual.string()); +} + +TEST(ConcatPathsTest, WorksWhenPath1EndsWithPathSep) { + FilePath actual = FilePath::ConcatPaths(FilePath("foo" GTEST_PATH_SEP_), + FilePath("bar.xml")); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar.xml", actual.string()); +} + +TEST(ConcatPathsTest, Path1BeingEmpty) { + FilePath actual = FilePath::ConcatPaths(FilePath(""), + FilePath("bar.xml")); + EXPECT_EQ("bar.xml", actual.string()); +} + +TEST(ConcatPathsTest, Path2BeingEmpty) { + FilePath actual = FilePath::ConcatPaths(FilePath("foo"), FilePath("")); + EXPECT_EQ("foo" GTEST_PATH_SEP_, actual.string()); +} + +TEST(ConcatPathsTest, BothPathBeingEmpty) { + FilePath actual = FilePath::ConcatPaths(FilePath(""), + FilePath("")); + EXPECT_EQ("", actual.string()); +} + +TEST(ConcatPathsTest, Path1ContainsPathSep) { + FilePath actual = FilePath::ConcatPaths(FilePath("foo" GTEST_PATH_SEP_ "bar"), + FilePath("foobar.xml")); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar" GTEST_PATH_SEP_ "foobar.xml", + actual.string()); +} + +TEST(ConcatPathsTest, Path2ContainsPathSep) { + FilePath actual = FilePath::ConcatPaths( + FilePath("foo" GTEST_PATH_SEP_), + FilePath("bar" GTEST_PATH_SEP_ "bar.xml")); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar" GTEST_PATH_SEP_ "bar.xml", + actual.string()); +} + +TEST(ConcatPathsTest, Path2EndsWithPathSep) { + FilePath actual = FilePath::ConcatPaths(FilePath("foo"), + FilePath("bar" GTEST_PATH_SEP_)); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar" GTEST_PATH_SEP_, actual.string()); +} + +// RemoveTrailingPathSeparator "" -> "" +TEST(RemoveTrailingPathSeparatorTest, EmptyString) { + EXPECT_EQ("", FilePath("").RemoveTrailingPathSeparator().string()); +} + +// RemoveTrailingPathSeparator "foo" -> "foo" +TEST(RemoveTrailingPathSeparatorTest, FileNoSlashString) { + EXPECT_EQ("foo", FilePath("foo").RemoveTrailingPathSeparator().string()); +} + +// RemoveTrailingPathSeparator "foo/" -> "foo" +TEST(RemoveTrailingPathSeparatorTest, ShouldRemoveTrailingSeparator) { + EXPECT_EQ("foo", + FilePath("foo" GTEST_PATH_SEP_).RemoveTrailingPathSeparator().string()); +#if GTEST_HAS_ALT_PATH_SEP_ + EXPECT_EQ("foo", FilePath("foo/").RemoveTrailingPathSeparator().string()); +#endif +} + +// RemoveTrailingPathSeparator "foo/bar/" -> "foo/bar/" +TEST(RemoveTrailingPathSeparatorTest, ShouldRemoveLastSeparator) { + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar", + FilePath("foo" GTEST_PATH_SEP_ "bar" GTEST_PATH_SEP_) + .RemoveTrailingPathSeparator().string()); +} + +// RemoveTrailingPathSeparator "foo/bar" -> "foo/bar" +TEST(RemoveTrailingPathSeparatorTest, ShouldReturnUnmodified) { + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar", + FilePath("foo" GTEST_PATH_SEP_ "bar") + .RemoveTrailingPathSeparator().string()); +} + +TEST(DirectoryTest, RootDirectoryExists) { +#if GTEST_OS_WINDOWS // We are on Windows. + char current_drive[_MAX_PATH]; // NOLINT + current_drive[0] = static_cast(_getdrive() + 'A' - 1); + current_drive[1] = ':'; + current_drive[2] = '\\'; + current_drive[3] = '\0'; + EXPECT_TRUE(FilePath(current_drive).DirectoryExists()); +#else + EXPECT_TRUE(FilePath("/").DirectoryExists()); +#endif // GTEST_OS_WINDOWS +} + +#if GTEST_OS_WINDOWS +TEST(DirectoryTest, RootOfWrongDriveDoesNotExists) { + const int saved_drive_ = _getdrive(); + // Find a drive that doesn't exist. Start with 'Z' to avoid common ones. + for (char drive = 'Z'; drive >= 'A'; drive--) + if (_chdrive(drive - 'A' + 1) == -1) { + char non_drive[_MAX_PATH]; // NOLINT + non_drive[0] = drive; + non_drive[1] = ':'; + non_drive[2] = '\\'; + non_drive[3] = '\0'; + EXPECT_FALSE(FilePath(non_drive).DirectoryExists()); + break; + } + _chdrive(saved_drive_); +} +#endif // GTEST_OS_WINDOWS + +#if !GTEST_OS_WINDOWS_MOBILE +// Windows CE _does_ consider an empty directory to exist. +TEST(DirectoryTest, EmptyPathDirectoryDoesNotExist) { + EXPECT_FALSE(FilePath("").DirectoryExists()); +} +#endif // !GTEST_OS_WINDOWS_MOBILE + +TEST(DirectoryTest, CurrentDirectoryExists) { +#if GTEST_OS_WINDOWS // We are on Windows. +# ifndef _WIN32_CE // Windows CE doesn't have a current directory. + + EXPECT_TRUE(FilePath(".").DirectoryExists()); + EXPECT_TRUE(FilePath(".\\").DirectoryExists()); + +# endif // _WIN32_CE +#else + EXPECT_TRUE(FilePath(".").DirectoryExists()); + EXPECT_TRUE(FilePath("./").DirectoryExists()); +#endif // GTEST_OS_WINDOWS +} + +// "foo/bar" == foo//bar" == "foo///bar" +TEST(NormalizeTest, MultipleConsecutiveSepaparatorsInMidstring) { + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar", + FilePath("foo" GTEST_PATH_SEP_ "bar").string()); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar", + FilePath("foo" GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string()); + EXPECT_EQ("foo" GTEST_PATH_SEP_ "bar", + FilePath("foo" GTEST_PATH_SEP_ GTEST_PATH_SEP_ + GTEST_PATH_SEP_ "bar").string()); +} + +// "/bar" == //bar" == "///bar" +TEST(NormalizeTest, MultipleConsecutiveSepaparatorsAtStringStart) { + EXPECT_EQ(GTEST_PATH_SEP_ "bar", + FilePath(GTEST_PATH_SEP_ "bar").string()); + EXPECT_EQ(GTEST_PATH_SEP_ "bar", + FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string()); + EXPECT_EQ(GTEST_PATH_SEP_ "bar", + FilePath(GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_ "bar").string()); +} + +// "foo/" == foo//" == "foo///" +TEST(NormalizeTest, MultipleConsecutiveSepaparatorsAtStringEnd) { + EXPECT_EQ("foo" GTEST_PATH_SEP_, + FilePath("foo" GTEST_PATH_SEP_).string()); + EXPECT_EQ("foo" GTEST_PATH_SEP_, + FilePath("foo" GTEST_PATH_SEP_ GTEST_PATH_SEP_).string()); + EXPECT_EQ("foo" GTEST_PATH_SEP_, + FilePath("foo" GTEST_PATH_SEP_ GTEST_PATH_SEP_ GTEST_PATH_SEP_).string()); +} + +#if GTEST_HAS_ALT_PATH_SEP_ + +// Tests that separators at the end of the string are normalized +// regardless of their combination (e.g. "foo\" =="foo/\" == +// "foo\\/"). +TEST(NormalizeTest, MixAlternateSeparatorAtStringEnd) { + EXPECT_EQ("foo" GTEST_PATH_SEP_, + FilePath("foo/").string()); + EXPECT_EQ("foo" GTEST_PATH_SEP_, + FilePath("foo" GTEST_PATH_SEP_ "/").string()); + EXPECT_EQ("foo" GTEST_PATH_SEP_, + FilePath("foo//" GTEST_PATH_SEP_).string()); +} + +#endif + +TEST(AssignmentOperatorTest, DefaultAssignedToNonDefault) { + FilePath default_path; + FilePath non_default_path("path"); + non_default_path = default_path; + EXPECT_EQ("", non_default_path.string()); + EXPECT_EQ("", default_path.string()); // RHS var is unchanged. +} + +TEST(AssignmentOperatorTest, NonDefaultAssignedToDefault) { + FilePath non_default_path("path"); + FilePath default_path; + default_path = non_default_path; + EXPECT_EQ("path", default_path.string()); + EXPECT_EQ("path", non_default_path.string()); // RHS var is unchanged. +} + +TEST(AssignmentOperatorTest, ConstAssignedToNonConst) { + const FilePath const_default_path("const_path"); + FilePath non_default_path("path"); + non_default_path = const_default_path; + EXPECT_EQ("const_path", non_default_path.string()); +} + +class DirectoryCreationTest : public Test { + protected: + virtual void SetUp() { + testdata_path_.Set(FilePath( + TempDir() + GetCurrentExecutableName().string() + + "_directory_creation" GTEST_PATH_SEP_ "test" GTEST_PATH_SEP_)); + testdata_file_.Set(testdata_path_.RemoveTrailingPathSeparator()); + + unique_file0_.Set(FilePath::MakeFileName(testdata_path_, FilePath("unique"), + 0, "txt")); + unique_file1_.Set(FilePath::MakeFileName(testdata_path_, FilePath("unique"), + 1, "txt")); + + remove(testdata_file_.c_str()); + remove(unique_file0_.c_str()); + remove(unique_file1_.c_str()); + posix::RmDir(testdata_path_.c_str()); + } + + virtual void TearDown() { + remove(testdata_file_.c_str()); + remove(unique_file0_.c_str()); + remove(unique_file1_.c_str()); + posix::RmDir(testdata_path_.c_str()); + } + + std::string TempDir() const { +#if GTEST_OS_WINDOWS_MOBILE + return "\\temp\\"; +#elif GTEST_OS_WINDOWS + const char* temp_dir = posix::GetEnv("TEMP"); + if (temp_dir == NULL || temp_dir[0] == '\0') + return "\\temp\\"; + else if (temp_dir[strlen(temp_dir) - 1] == '\\') + return temp_dir; + else + return std::string(temp_dir) + "\\"; +#elif GTEST_OS_LINUX_ANDROID + return "/sdcard/"; +#else + return "/tmp/"; +#endif // GTEST_OS_WINDOWS_MOBILE + } + + void CreateTextFile(const char* filename) { + FILE* f = posix::FOpen(filename, "w"); + fprintf(f, "text\n"); + fclose(f); + } + + // Strings representing a directory and a file, with identical paths + // except for the trailing separator character that distinquishes + // a directory named 'test' from a file named 'test'. Example names: + FilePath testdata_path_; // "/tmp/directory_creation/test/" + FilePath testdata_file_; // "/tmp/directory_creation/test" + FilePath unique_file0_; // "/tmp/directory_creation/test/unique.txt" + FilePath unique_file1_; // "/tmp/directory_creation/test/unique_1.txt" +}; + +TEST_F(DirectoryCreationTest, CreateDirectoriesRecursively) { + EXPECT_FALSE(testdata_path_.DirectoryExists()) << testdata_path_.string(); + EXPECT_TRUE(testdata_path_.CreateDirectoriesRecursively()); + EXPECT_TRUE(testdata_path_.DirectoryExists()); +} + +TEST_F(DirectoryCreationTest, CreateDirectoriesForAlreadyExistingPath) { + EXPECT_FALSE(testdata_path_.DirectoryExists()) << testdata_path_.string(); + EXPECT_TRUE(testdata_path_.CreateDirectoriesRecursively()); + // Call 'create' again... should still succeed. + EXPECT_TRUE(testdata_path_.CreateDirectoriesRecursively()); +} + +TEST_F(DirectoryCreationTest, CreateDirectoriesAndUniqueFilename) { + FilePath file_path(FilePath::GenerateUniqueFileName(testdata_path_, + FilePath("unique"), "txt")); + EXPECT_EQ(unique_file0_.string(), file_path.string()); + EXPECT_FALSE(file_path.FileOrDirectoryExists()); // file not there + + testdata_path_.CreateDirectoriesRecursively(); + EXPECT_FALSE(file_path.FileOrDirectoryExists()); // file still not there + CreateTextFile(file_path.c_str()); + EXPECT_TRUE(file_path.FileOrDirectoryExists()); + + FilePath file_path2(FilePath::GenerateUniqueFileName(testdata_path_, + FilePath("unique"), "txt")); + EXPECT_EQ(unique_file1_.string(), file_path2.string()); + EXPECT_FALSE(file_path2.FileOrDirectoryExists()); // file not there + CreateTextFile(file_path2.c_str()); + EXPECT_TRUE(file_path2.FileOrDirectoryExists()); +} + +TEST_F(DirectoryCreationTest, CreateDirectoriesFail) { + // force a failure by putting a file where we will try to create a directory. + CreateTextFile(testdata_file_.c_str()); + EXPECT_TRUE(testdata_file_.FileOrDirectoryExists()); + EXPECT_FALSE(testdata_file_.DirectoryExists()); + EXPECT_FALSE(testdata_file_.CreateDirectoriesRecursively()); +} + +TEST(NoDirectoryCreationTest, CreateNoDirectoriesForDefaultXmlFile) { + const FilePath test_detail_xml("test_detail.xml"); + EXPECT_FALSE(test_detail_xml.CreateDirectoriesRecursively()); +} + +TEST(FilePathTest, DefaultConstructor) { + FilePath fp; + EXPECT_EQ("", fp.string()); +} + +TEST(FilePathTest, CharAndCopyConstructors) { + const FilePath fp("spicy"); + EXPECT_EQ("spicy", fp.string()); + + const FilePath fp_copy(fp); + EXPECT_EQ("spicy", fp_copy.string()); +} + +TEST(FilePathTest, StringConstructor) { + const FilePath fp(std::string("cider")); + EXPECT_EQ("cider", fp.string()); +} + +TEST(FilePathTest, Set) { + const FilePath apple("apple"); + FilePath mac("mac"); + mac.Set(apple); // Implement Set() since overloading operator= is forbidden. + EXPECT_EQ("apple", mac.string()); + EXPECT_EQ("apple", apple.string()); +} + +TEST(FilePathTest, ToString) { + const FilePath file("drink"); + EXPECT_EQ("drink", file.string()); +} + +TEST(FilePathTest, RemoveExtension) { + EXPECT_EQ("app", FilePath("app.cc").RemoveExtension("cc").string()); + EXPECT_EQ("app", FilePath("app.exe").RemoveExtension("exe").string()); + EXPECT_EQ("APP", FilePath("APP.EXE").RemoveExtension("exe").string()); +} + +TEST(FilePathTest, RemoveExtensionWhenThereIsNoExtension) { + EXPECT_EQ("app", FilePath("app").RemoveExtension("exe").string()); +} + +TEST(FilePathTest, IsDirectory) { + EXPECT_FALSE(FilePath("cola").IsDirectory()); + EXPECT_TRUE(FilePath("koala" GTEST_PATH_SEP_).IsDirectory()); +#if GTEST_HAS_ALT_PATH_SEP_ + EXPECT_TRUE(FilePath("koala/").IsDirectory()); +#endif +} + +TEST(FilePathTest, IsAbsolutePath) { + EXPECT_FALSE(FilePath("is" GTEST_PATH_SEP_ "relative").IsAbsolutePath()); + EXPECT_FALSE(FilePath("").IsAbsolutePath()); +#if GTEST_OS_WINDOWS + EXPECT_TRUE(FilePath("c:\\" GTEST_PATH_SEP_ "is_not" + GTEST_PATH_SEP_ "relative").IsAbsolutePath()); + EXPECT_FALSE(FilePath("c:foo" GTEST_PATH_SEP_ "bar").IsAbsolutePath()); + EXPECT_TRUE(FilePath("c:/" GTEST_PATH_SEP_ "is_not" + GTEST_PATH_SEP_ "relative").IsAbsolutePath()); +#else + EXPECT_TRUE(FilePath(GTEST_PATH_SEP_ "is_not" GTEST_PATH_SEP_ "relative") + .IsAbsolutePath()); +#endif // GTEST_OS_WINDOWS +} + +TEST(FilePathTest, IsRootDirectory) { +#if GTEST_OS_WINDOWS + EXPECT_TRUE(FilePath("a:\\").IsRootDirectory()); + EXPECT_TRUE(FilePath("Z:/").IsRootDirectory()); + EXPECT_TRUE(FilePath("e://").IsRootDirectory()); + EXPECT_FALSE(FilePath("").IsRootDirectory()); + EXPECT_FALSE(FilePath("b:").IsRootDirectory()); + EXPECT_FALSE(FilePath("b:a").IsRootDirectory()); + EXPECT_FALSE(FilePath("8:/").IsRootDirectory()); + EXPECT_FALSE(FilePath("c|/").IsRootDirectory()); +#else + EXPECT_TRUE(FilePath("/").IsRootDirectory()); + EXPECT_TRUE(FilePath("//").IsRootDirectory()); + EXPECT_FALSE(FilePath("").IsRootDirectory()); + EXPECT_FALSE(FilePath("\\").IsRootDirectory()); + EXPECT_FALSE(FilePath("/x").IsRootDirectory()); +#endif +} + +} // namespace +} // namespace internal +} // namespace testing diff --git a/external/gtest/test/gtest-linked_ptr_test.cc b/external/gtest/test/gtest-linked_ptr_test.cc new file mode 100644 index 0000000000..6fcf5124a8 --- /dev/null +++ b/external/gtest/test/gtest-linked_ptr_test.cc @@ -0,0 +1,154 @@ +// Copyright 2003, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Dan Egnor (egnor@google.com) +// Ported to Windows: Vadim Berman (vadimb@google.com) + +#include "gtest/internal/gtest-linked_ptr.h" + +#include +#include "gtest/gtest.h" + +namespace { + +using testing::Message; +using testing::internal::linked_ptr; + +int num; +Message* history = NULL; + +// Class which tracks allocation/deallocation +class A { + public: + A(): mynum(num++) { *history << "A" << mynum << " ctor\n"; } + virtual ~A() { *history << "A" << mynum << " dtor\n"; } + virtual void Use() { *history << "A" << mynum << " use\n"; } + protected: + int mynum; +}; + +// Subclass +class B : public A { + public: + B() { *history << "B" << mynum << " ctor\n"; } + ~B() { *history << "B" << mynum << " dtor\n"; } + virtual void Use() { *history << "B" << mynum << " use\n"; } +}; + +class LinkedPtrTest : public testing::Test { + public: + LinkedPtrTest() { + num = 0; + history = new Message; + } + + virtual ~LinkedPtrTest() { + delete history; + history = NULL; + } +}; + +TEST_F(LinkedPtrTest, GeneralTest) { + { + linked_ptr a0, a1, a2; + // Use explicit function call notation here to suppress self-assign warning. + a0.operator=(a0); + a1 = a2; + ASSERT_EQ(a0.get(), static_cast(NULL)); + ASSERT_EQ(a1.get(), static_cast(NULL)); + ASSERT_EQ(a2.get(), static_cast(NULL)); + ASSERT_TRUE(a0 == NULL); + ASSERT_TRUE(a1 == NULL); + ASSERT_TRUE(a2 == NULL); + + { + linked_ptr a3(new A); + a0 = a3; + ASSERT_TRUE(a0 == a3); + ASSERT_TRUE(a0 != NULL); + ASSERT_TRUE(a0.get() == a3); + ASSERT_TRUE(a0 == a3.get()); + linked_ptr a4(a0); + a1 = a4; + linked_ptr a5(new A); + ASSERT_TRUE(a5.get() != a3); + ASSERT_TRUE(a5 != a3.get()); + a2 = a5; + linked_ptr b0(new B); + linked_ptr a6(b0); + ASSERT_TRUE(b0 == a6); + ASSERT_TRUE(a6 == b0); + ASSERT_TRUE(b0 != NULL); + a5 = b0; + a5 = b0; + a3->Use(); + a4->Use(); + a5->Use(); + a6->Use(); + b0->Use(); + (*b0).Use(); + b0.get()->Use(); + } + + a0->Use(); + a1->Use(); + a2->Use(); + + a1 = a2; + a2.reset(new A); + a0.reset(); + + linked_ptr a7; + } + + ASSERT_STREQ( + "A0 ctor\n" + "A1 ctor\n" + "A2 ctor\n" + "B2 ctor\n" + "A0 use\n" + "A0 use\n" + "B2 use\n" + "B2 use\n" + "B2 use\n" + "B2 use\n" + "B2 use\n" + "B2 dtor\n" + "A2 dtor\n" + "A0 use\n" + "A0 use\n" + "A1 use\n" + "A3 ctor\n" + "A0 dtor\n" + "A3 dtor\n" + "A1 dtor\n", + history->GetString().c_str()); +} + +} // Unnamed namespace diff --git a/external/gtest/test/gtest-listener_test.cc b/external/gtest/test/gtest-listener_test.cc new file mode 100644 index 0000000000..99662cff33 --- /dev/null +++ b/external/gtest/test/gtest-listener_test.cc @@ -0,0 +1,310 @@ +// Copyright 2009 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) +// +// The Google C++ Testing Framework (Google Test) +// +// This file verifies Google Test event listeners receive events at the +// right times. + +#include "gtest/gtest.h" +#include + +using ::testing::AddGlobalTestEnvironment; +using ::testing::Environment; +using ::testing::InitGoogleTest; +using ::testing::Test; +using ::testing::TestCase; +using ::testing::TestEventListener; +using ::testing::TestInfo; +using ::testing::TestPartResult; +using ::testing::UnitTest; + +// Used by tests to register their events. +std::vector* g_events = NULL; + +namespace testing { +namespace internal { + +class EventRecordingListener : public TestEventListener { + public: + explicit EventRecordingListener(const char* name) : name_(name) {} + + protected: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) { + g_events->push_back(GetFullMethodName("OnTestProgramStart")); + } + + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int iteration) { + Message message; + message << GetFullMethodName("OnTestIterationStart") + << "(" << iteration << ")"; + g_events->push_back(message.GetString()); + } + + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) { + g_events->push_back(GetFullMethodName("OnEnvironmentsSetUpStart")); + } + + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) { + g_events->push_back(GetFullMethodName("OnEnvironmentsSetUpEnd")); + } + + virtual void OnTestCaseStart(const TestCase& /*test_case*/) { + g_events->push_back(GetFullMethodName("OnTestCaseStart")); + } + + virtual void OnTestStart(const TestInfo& /*test_info*/) { + g_events->push_back(GetFullMethodName("OnTestStart")); + } + + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) { + g_events->push_back(GetFullMethodName("OnTestPartResult")); + } + + virtual void OnTestEnd(const TestInfo& /*test_info*/) { + g_events->push_back(GetFullMethodName("OnTestEnd")); + } + + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) { + g_events->push_back(GetFullMethodName("OnTestCaseEnd")); + } + + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) { + g_events->push_back(GetFullMethodName("OnEnvironmentsTearDownStart")); + } + + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) { + g_events->push_back(GetFullMethodName("OnEnvironmentsTearDownEnd")); + } + + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int iteration) { + Message message; + message << GetFullMethodName("OnTestIterationEnd") + << "(" << iteration << ")"; + g_events->push_back(message.GetString()); + } + + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) { + g_events->push_back(GetFullMethodName("OnTestProgramEnd")); + } + + private: + std::string GetFullMethodName(const char* name) { + return name_ + "." + name; + } + + std::string name_; +}; + +class EnvironmentInvocationCatcher : public Environment { + protected: + virtual void SetUp() { + g_events->push_back("Environment::SetUp"); + } + + virtual void TearDown() { + g_events->push_back("Environment::TearDown"); + } +}; + +class ListenerTest : public Test { + protected: + static void SetUpTestCase() { + g_events->push_back("ListenerTest::SetUpTestCase"); + } + + static void TearDownTestCase() { + g_events->push_back("ListenerTest::TearDownTestCase"); + } + + virtual void SetUp() { + g_events->push_back("ListenerTest::SetUp"); + } + + virtual void TearDown() { + g_events->push_back("ListenerTest::TearDown"); + } +}; + +TEST_F(ListenerTest, DoesFoo) { + // Test execution order within a test case is not guaranteed so we are not + // recording the test name. + g_events->push_back("ListenerTest::* Test Body"); + SUCCEED(); // Triggers OnTestPartResult. +} + +TEST_F(ListenerTest, DoesBar) { + g_events->push_back("ListenerTest::* Test Body"); + SUCCEED(); // Triggers OnTestPartResult. +} + +} // namespace internal + +} // namespace testing + +using ::testing::internal::EnvironmentInvocationCatcher; +using ::testing::internal::EventRecordingListener; + +void VerifyResults(const std::vector& data, + const char* const* expected_data, + int expected_data_size) { + const int actual_size = data.size(); + // If the following assertion fails, a new entry will be appended to + // data. Hence we save data.size() first. + EXPECT_EQ(expected_data_size, actual_size); + + // Compares the common prefix. + const int shorter_size = expected_data_size <= actual_size ? + expected_data_size : actual_size; + int i = 0; + for (; i < shorter_size; ++i) { + ASSERT_STREQ(expected_data[i], data[i].c_str()) + << "at position " << i; + } + + // Prints extra elements in the actual data. + for (; i < actual_size; ++i) { + printf(" Actual event #%d: %s\n", i, data[i].c_str()); + } +} + +int main(int argc, char **argv) { + std::vector events; + g_events = &events; + InitGoogleTest(&argc, argv); + + UnitTest::GetInstance()->listeners().Append( + new EventRecordingListener("1st")); + UnitTest::GetInstance()->listeners().Append( + new EventRecordingListener("2nd")); + + AddGlobalTestEnvironment(new EnvironmentInvocationCatcher); + + GTEST_CHECK_(events.size() == 0) + << "AddGlobalTestEnvironment should not generate any events itself."; + + ::testing::GTEST_FLAG(repeat) = 2; + int ret_val = RUN_ALL_TESTS(); + + const char* const expected_events[] = { + "1st.OnTestProgramStart", + "2nd.OnTestProgramStart", + "1st.OnTestIterationStart(0)", + "2nd.OnTestIterationStart(0)", + "1st.OnEnvironmentsSetUpStart", + "2nd.OnEnvironmentsSetUpStart", + "Environment::SetUp", + "2nd.OnEnvironmentsSetUpEnd", + "1st.OnEnvironmentsSetUpEnd", + "1st.OnTestCaseStart", + "2nd.OnTestCaseStart", + "ListenerTest::SetUpTestCase", + "1st.OnTestStart", + "2nd.OnTestStart", + "ListenerTest::SetUp", + "ListenerTest::* Test Body", + "1st.OnTestPartResult", + "2nd.OnTestPartResult", + "ListenerTest::TearDown", + "2nd.OnTestEnd", + "1st.OnTestEnd", + "1st.OnTestStart", + "2nd.OnTestStart", + "ListenerTest::SetUp", + "ListenerTest::* Test Body", + "1st.OnTestPartResult", + "2nd.OnTestPartResult", + "ListenerTest::TearDown", + "2nd.OnTestEnd", + "1st.OnTestEnd", + "ListenerTest::TearDownTestCase", + "2nd.OnTestCaseEnd", + "1st.OnTestCaseEnd", + "1st.OnEnvironmentsTearDownStart", + "2nd.OnEnvironmentsTearDownStart", + "Environment::TearDown", + "2nd.OnEnvironmentsTearDownEnd", + "1st.OnEnvironmentsTearDownEnd", + "2nd.OnTestIterationEnd(0)", + "1st.OnTestIterationEnd(0)", + "1st.OnTestIterationStart(1)", + "2nd.OnTestIterationStart(1)", + "1st.OnEnvironmentsSetUpStart", + "2nd.OnEnvironmentsSetUpStart", + "Environment::SetUp", + "2nd.OnEnvironmentsSetUpEnd", + "1st.OnEnvironmentsSetUpEnd", + "1st.OnTestCaseStart", + "2nd.OnTestCaseStart", + "ListenerTest::SetUpTestCase", + "1st.OnTestStart", + "2nd.OnTestStart", + "ListenerTest::SetUp", + "ListenerTest::* Test Body", + "1st.OnTestPartResult", + "2nd.OnTestPartResult", + "ListenerTest::TearDown", + "2nd.OnTestEnd", + "1st.OnTestEnd", + "1st.OnTestStart", + "2nd.OnTestStart", + "ListenerTest::SetUp", + "ListenerTest::* Test Body", + "1st.OnTestPartResult", + "2nd.OnTestPartResult", + "ListenerTest::TearDown", + "2nd.OnTestEnd", + "1st.OnTestEnd", + "ListenerTest::TearDownTestCase", + "2nd.OnTestCaseEnd", + "1st.OnTestCaseEnd", + "1st.OnEnvironmentsTearDownStart", + "2nd.OnEnvironmentsTearDownStart", + "Environment::TearDown", + "2nd.OnEnvironmentsTearDownEnd", + "1st.OnEnvironmentsTearDownEnd", + "2nd.OnTestIterationEnd(1)", + "1st.OnTestIterationEnd(1)", + "2nd.OnTestProgramEnd", + "1st.OnTestProgramEnd" + }; + VerifyResults(events, + expected_events, + sizeof(expected_events)/sizeof(expected_events[0])); + + // We need to check manually for ad hoc test failures that happen after + // RUN_ALL_TESTS finishes. + if (UnitTest::GetInstance()->Failed()) + ret_val = 1; + + return ret_val; +} diff --git a/external/gtest/test/gtest-message_test.cc b/external/gtest/test/gtest-message_test.cc new file mode 100644 index 0000000000..175238ef4e --- /dev/null +++ b/external/gtest/test/gtest-message_test.cc @@ -0,0 +1,159 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests for the Message class. + +#include "gtest/gtest-message.h" + +#include "gtest/gtest.h" + +namespace { + +using ::testing::Message; + +// Tests the testing::Message class + +// Tests the default constructor. +TEST(MessageTest, DefaultConstructor) { + const Message msg; + EXPECT_EQ("", msg.GetString()); +} + +// Tests the copy constructor. +TEST(MessageTest, CopyConstructor) { + const Message msg1("Hello"); + const Message msg2(msg1); + EXPECT_EQ("Hello", msg2.GetString()); +} + +// Tests constructing a Message from a C-string. +TEST(MessageTest, ConstructsFromCString) { + Message msg("Hello"); + EXPECT_EQ("Hello", msg.GetString()); +} + +// Tests streaming a float. +TEST(MessageTest, StreamsFloat) { + const std::string s = (Message() << 1.23456F << " " << 2.34567F).GetString(); + // Both numbers should be printed with enough precision. + EXPECT_PRED_FORMAT2(testing::IsSubstring, "1.234560", s.c_str()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, " 2.345669", s.c_str()); +} + +// Tests streaming a double. +TEST(MessageTest, StreamsDouble) { + const std::string s = (Message() << 1260570880.4555497 << " " + << 1260572265.1954534).GetString(); + // Both numbers should be printed with enough precision. + EXPECT_PRED_FORMAT2(testing::IsSubstring, "1260570880.45", s.c_str()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, " 1260572265.19", s.c_str()); +} + +// Tests streaming a non-char pointer. +TEST(MessageTest, StreamsPointer) { + int n = 0; + int* p = &n; + EXPECT_NE("(null)", (Message() << p).GetString()); +} + +// Tests streaming a NULL non-char pointer. +TEST(MessageTest, StreamsNullPointer) { + int* p = NULL; + EXPECT_EQ("(null)", (Message() << p).GetString()); +} + +// Tests streaming a C string. +TEST(MessageTest, StreamsCString) { + EXPECT_EQ("Foo", (Message() << "Foo").GetString()); +} + +// Tests streaming a NULL C string. +TEST(MessageTest, StreamsNullCString) { + char* p = NULL; + EXPECT_EQ("(null)", (Message() << p).GetString()); +} + +// Tests streaming std::string. +TEST(MessageTest, StreamsString) { + const ::std::string str("Hello"); + EXPECT_EQ("Hello", (Message() << str).GetString()); +} + +// Tests that we can output strings containing embedded NULs. +TEST(MessageTest, StreamsStringWithEmbeddedNUL) { + const char char_array_with_nul[] = + "Here's a NUL\0 and some more string"; + const ::std::string string_with_nul(char_array_with_nul, + sizeof(char_array_with_nul) - 1); + EXPECT_EQ("Here's a NUL\\0 and some more string", + (Message() << string_with_nul).GetString()); +} + +// Tests streaming a NUL char. +TEST(MessageTest, StreamsNULChar) { + EXPECT_EQ("\\0", (Message() << '\0').GetString()); +} + +// Tests streaming int. +TEST(MessageTest, StreamsInt) { + EXPECT_EQ("123", (Message() << 123).GetString()); +} + +// Tests that basic IO manipulators (endl, ends, and flush) can be +// streamed to Message. +TEST(MessageTest, StreamsBasicIoManip) { + EXPECT_EQ("Line 1.\nA NUL char \\0 in line 2.", + (Message() << "Line 1." << std::endl + << "A NUL char " << std::ends << std::flush + << " in line 2.").GetString()); +} + +// Tests Message::GetString() +TEST(MessageTest, GetString) { + Message msg; + msg << 1 << " lamb"; + EXPECT_EQ("1 lamb", msg.GetString()); +} + +// Tests streaming a Message object to an ostream. +TEST(MessageTest, StreamsToOStream) { + Message msg("Hello"); + ::std::stringstream ss; + ss << msg; + EXPECT_EQ("Hello", testing::internal::StringStreamToString(&ss)); +} + +// Tests that a Message object doesn't take up too much stack space. +TEST(MessageTest, DoesNotTakeUpMuchStackSpace) { + EXPECT_LE(sizeof(Message), 16U); +} + +} // namespace diff --git a/external/gtest/test/gtest-options_test.cc b/external/gtest/test/gtest-options_test.cc new file mode 100644 index 0000000000..5586dc3b1b --- /dev/null +++ b/external/gtest/test/gtest-options_test.cc @@ -0,0 +1,215 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keith.ray@gmail.com (Keith Ray) +// +// Google Test UnitTestOptions tests +// +// This file tests classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included from gtest.cc, to avoid changing build or +// make-files on Windows and other platforms. Do not #include this file +// anywhere else! + +#include "gtest/gtest.h" + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +#endif // GTEST_OS_WINDOWS_MOBILE + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { +namespace { + +// Turns the given relative path into an absolute path. +FilePath GetAbsolutePathOf(const FilePath& relative_path) { + return FilePath::ConcatPaths(FilePath::GetCurrentDir(), relative_path); +} + +// Testing UnitTestOptions::GetOutputFormat/GetOutputFile. + +TEST(XmlOutputTest, GetOutputFormatDefault) { + GTEST_FLAG(output) = ""; + EXPECT_STREQ("", UnitTestOptions::GetOutputFormat().c_str()); +} + +TEST(XmlOutputTest, GetOutputFormat) { + GTEST_FLAG(output) = "xml:filename"; + EXPECT_STREQ("xml", UnitTestOptions::GetOutputFormat().c_str()); +} + +TEST(XmlOutputTest, GetOutputFileDefault) { + GTEST_FLAG(output) = ""; + EXPECT_EQ(GetAbsolutePathOf(FilePath("test_detail.xml")).string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +} + +TEST(XmlOutputTest, GetOutputFileSingleFile) { + GTEST_FLAG(output) = "xml:filename.abc"; + EXPECT_EQ(GetAbsolutePathOf(FilePath("filename.abc")).string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +} + +TEST(XmlOutputTest, GetOutputFileFromDirectoryPath) { + GTEST_FLAG(output) = "xml:path" GTEST_PATH_SEP_; + const std::string expected_output_file = + GetAbsolutePathOf( + FilePath(std::string("path") + GTEST_PATH_SEP_ + + GetCurrentExecutableName().string() + ".xml")).string(); + const std::string& output_file = + UnitTestOptions::GetAbsolutePathToOutputFile(); +#if GTEST_OS_WINDOWS + EXPECT_STRCASEEQ(expected_output_file.c_str(), output_file.c_str()); +#else + EXPECT_EQ(expected_output_file, output_file.c_str()); +#endif +} + +TEST(OutputFileHelpersTest, GetCurrentExecutableName) { + const std::string exe_str = GetCurrentExecutableName().string(); +#if GTEST_OS_WINDOWS + const bool success = + _strcmpi("gtest-options_test", exe_str.c_str()) == 0 || + _strcmpi("gtest-options-ex_test", exe_str.c_str()) == 0 || + _strcmpi("gtest_all_test", exe_str.c_str()) == 0 || + _strcmpi("gtest_dll_test", exe_str.c_str()) == 0; +#else + // TODO(wan@google.com): remove the hard-coded "lt-" prefix when + // Chandler Carruth's libtool replacement is ready. + const bool success = + exe_str == "gtest-options_test" || + exe_str == "gtest_all_test" || + exe_str == "lt-gtest_all_test" || + exe_str == "gtest_dll_test"; +#endif // GTEST_OS_WINDOWS + if (!success) + FAIL() << "GetCurrentExecutableName() returns " << exe_str; +} + +class XmlOutputChangeDirTest : public Test { + protected: + virtual void SetUp() { + original_working_dir_ = FilePath::GetCurrentDir(); + posix::ChDir(".."); + // This will make the test fail if run from the root directory. + EXPECT_NE(original_working_dir_.string(), + FilePath::GetCurrentDir().string()); + } + + virtual void TearDown() { + posix::ChDir(original_working_dir_.string().c_str()); + } + + FilePath original_working_dir_; +}; + +TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithDefault) { + GTEST_FLAG(output) = ""; + EXPECT_EQ(FilePath::ConcatPaths(original_working_dir_, + FilePath("test_detail.xml")).string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +} + +TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithDefaultXML) { + GTEST_FLAG(output) = "xml"; + EXPECT_EQ(FilePath::ConcatPaths(original_working_dir_, + FilePath("test_detail.xml")).string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +} + +TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithRelativeFile) { + GTEST_FLAG(output) = "xml:filename.abc"; + EXPECT_EQ(FilePath::ConcatPaths(original_working_dir_, + FilePath("filename.abc")).string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +} + +TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithRelativePath) { + GTEST_FLAG(output) = "xml:path" GTEST_PATH_SEP_; + const std::string expected_output_file = + FilePath::ConcatPaths( + original_working_dir_, + FilePath(std::string("path") + GTEST_PATH_SEP_ + + GetCurrentExecutableName().string() + ".xml")).string(); + const std::string& output_file = + UnitTestOptions::GetAbsolutePathToOutputFile(); +#if GTEST_OS_WINDOWS + EXPECT_STRCASEEQ(expected_output_file.c_str(), output_file.c_str()); +#else + EXPECT_EQ(expected_output_file, output_file.c_str()); +#endif +} + +TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithAbsoluteFile) { +#if GTEST_OS_WINDOWS + GTEST_FLAG(output) = "xml:c:\\tmp\\filename.abc"; + EXPECT_EQ(FilePath("c:\\tmp\\filename.abc").string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +#else + GTEST_FLAG(output) ="xml:/tmp/filename.abc"; + EXPECT_EQ(FilePath("/tmp/filename.abc").string(), + UnitTestOptions::GetAbsolutePathToOutputFile()); +#endif +} + +TEST_F(XmlOutputChangeDirTest, PreserveOriginalWorkingDirWithAbsolutePath) { +#if GTEST_OS_WINDOWS + const std::string path = "c:\\tmp\\"; +#else + const std::string path = "/tmp/"; +#endif + + GTEST_FLAG(output) = "xml:" + path; + const std::string expected_output_file = + path + GetCurrentExecutableName().string() + ".xml"; + const std::string& output_file = + UnitTestOptions::GetAbsolutePathToOutputFile(); + +#if GTEST_OS_WINDOWS + EXPECT_STRCASEEQ(expected_output_file.c_str(), output_file.c_str()); +#else + EXPECT_EQ(expected_output_file, output_file.c_str()); +#endif +} + +} // namespace +} // namespace internal +} // namespace testing diff --git a/external/gtest/test/gtest-param-test2_test.cc b/external/gtest/test/gtest-param-test2_test.cc new file mode 100644 index 0000000000..4a782fe708 --- /dev/null +++ b/external/gtest/test/gtest-param-test2_test.cc @@ -0,0 +1,65 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) +// +// Tests for Google Test itself. This verifies that the basic constructs of +// Google Test work. + +#include "gtest/gtest.h" + +#include "test/gtest-param-test_test.h" + +#if GTEST_HAS_PARAM_TEST + +using ::testing::Values; +using ::testing::internal::ParamGenerator; + +// Tests that generators defined in a different translation unit +// are functional. The test using extern_gen is defined +// in gtest-param-test_test.cc. +ParamGenerator extern_gen = Values(33); + +// Tests that a parameterized test case can be defined in one translation unit +// and instantiated in another. The test is defined in gtest-param-test_test.cc +// and ExternalInstantiationTest fixture class is defined in +// gtest-param-test_test.h. +INSTANTIATE_TEST_CASE_P(MultiplesOf33, + ExternalInstantiationTest, + Values(33, 66)); + +// Tests that a parameterized test case can be instantiated +// in multiple translation units. Another instantiation is defined +// in gtest-param-test_test.cc and InstantiationInMultipleTranslaionUnitsTest +// fixture is defined in gtest-param-test_test.h +INSTANTIATE_TEST_CASE_P(Sequence2, + InstantiationInMultipleTranslaionUnitsTest, + Values(42*3, 42*4, 42*5)); + +#endif // GTEST_HAS_PARAM_TEST diff --git a/external/gtest/test/gtest-param-test_test.cc b/external/gtest/test/gtest-param-test_test.cc new file mode 100644 index 0000000000..f60cb8a558 --- /dev/null +++ b/external/gtest/test/gtest-param-test_test.cc @@ -0,0 +1,904 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) +// +// Tests for Google Test itself. This file verifies that the parameter +// generators objects produce correct parameter sequences and that +// Google Test runtime instantiates correct tests from those sequences. + +#include "gtest/gtest.h" + +#if GTEST_HAS_PARAM_TEST + +# include +# include +# include +# include +# include +# include + +// To include gtest-internal-inl.h. +# define GTEST_IMPLEMENTATION_ 1 +# include "src/gtest-internal-inl.h" // for UnitTestOptions +# undef GTEST_IMPLEMENTATION_ + +# include "test/gtest-param-test_test.h" + +using ::std::vector; +using ::std::sort; + +using ::testing::AddGlobalTestEnvironment; +using ::testing::Bool; +using ::testing::Message; +using ::testing::Range; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +# if GTEST_HAS_COMBINE +using ::testing::Combine; +using ::std::tr1::get; +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +# endif // GTEST_HAS_COMBINE + +using ::testing::internal::ParamGenerator; +using ::testing::internal::UnitTestOptions; + +// Prints a value to a string. +// +// TODO(wan@google.com): remove PrintValue() when we move matchers and +// EXPECT_THAT() from Google Mock to Google Test. At that time, we +// can write EXPECT_THAT(x, Eq(y)) to compare two tuples x and y, as +// EXPECT_THAT() and the matchers know how to print tuples. +template +::std::string PrintValue(const T& value) { + ::std::stringstream stream; + stream << value; + return stream.str(); +} + +# if GTEST_HAS_COMBINE + +// These overloads allow printing tuples in our tests. We cannot +// define an operator<< for tuples, as that definition needs to be in +// the std namespace in order to be picked up by Google Test via +// Argument-Dependent Lookup, yet defining anything in the std +// namespace in non-STL code is undefined behavior. + +template +::std::string PrintValue(const tuple& value) { + ::std::stringstream stream; + stream << "(" << get<0>(value) << ", " << get<1>(value) << ")"; + return stream.str(); +} + +template +::std::string PrintValue(const tuple& value) { + ::std::stringstream stream; + stream << "(" << get<0>(value) << ", " << get<1>(value) + << ", "<< get<2>(value) << ")"; + return stream.str(); +} + +template +::std::string PrintValue( + const tuple& value) { + ::std::stringstream stream; + stream << "(" << get<0>(value) << ", " << get<1>(value) + << ", "<< get<2>(value) << ", " << get<3>(value) + << ", "<< get<4>(value) << ", " << get<5>(value) + << ", "<< get<6>(value) << ", " << get<7>(value) + << ", "<< get<8>(value) << ", " << get<9>(value) << ")"; + return stream.str(); +} + +# endif // GTEST_HAS_COMBINE + +// Verifies that a sequence generated by the generator and accessed +// via the iterator object matches the expected one using Google Test +// assertions. +template +void VerifyGenerator(const ParamGenerator& generator, + const T (&expected_values)[N]) { + typename ParamGenerator::iterator it = generator.begin(); + for (size_t i = 0; i < N; ++i) { + ASSERT_FALSE(it == generator.end()) + << "At element " << i << " when accessing via an iterator " + << "created with the copy constructor.\n"; + // We cannot use EXPECT_EQ() here as the values may be tuples, + // which don't support <<. + EXPECT_TRUE(expected_values[i] == *it) + << "where i is " << i + << ", expected_values[i] is " << PrintValue(expected_values[i]) + << ", *it is " << PrintValue(*it) + << ", and 'it' is an iterator created with the copy constructor.\n"; + it++; + } + EXPECT_TRUE(it == generator.end()) + << "At the presumed end of sequence when accessing via an iterator " + << "created with the copy constructor.\n"; + + // Test the iterator assignment. The following lines verify that + // the sequence accessed via an iterator initialized via the + // assignment operator (as opposed to a copy constructor) matches + // just the same. + it = generator.begin(); + for (size_t i = 0; i < N; ++i) { + ASSERT_FALSE(it == generator.end()) + << "At element " << i << " when accessing via an iterator " + << "created with the assignment operator.\n"; + EXPECT_TRUE(expected_values[i] == *it) + << "where i is " << i + << ", expected_values[i] is " << PrintValue(expected_values[i]) + << ", *it is " << PrintValue(*it) + << ", and 'it' is an iterator created with the copy constructor.\n"; + it++; + } + EXPECT_TRUE(it == generator.end()) + << "At the presumed end of sequence when accessing via an iterator " + << "created with the assignment operator.\n"; +} + +template +void VerifyGeneratorIsEmpty(const ParamGenerator& generator) { + typename ParamGenerator::iterator it = generator.begin(); + EXPECT_TRUE(it == generator.end()); + + it = generator.begin(); + EXPECT_TRUE(it == generator.end()); +} + +// Generator tests. They test that each of the provided generator functions +// generates an expected sequence of values. The general test pattern +// instantiates a generator using one of the generator functions, +// checks the sequence produced by the generator using its iterator API, +// and then resets the iterator back to the beginning of the sequence +// and checks the sequence again. + +// Tests that iterators produced by generator functions conform to the +// ForwardIterator concept. +TEST(IteratorTest, ParamIteratorConformsToForwardIteratorConcept) { + const ParamGenerator gen = Range(0, 10); + ParamGenerator::iterator it = gen.begin(); + + // Verifies that iterator initialization works as expected. + ParamGenerator::iterator it2 = it; + EXPECT_TRUE(*it == *it2) << "Initialized iterators must point to the " + << "element same as its source points to"; + + // Verifies that iterator assignment works as expected. + it++; + EXPECT_FALSE(*it == *it2); + it2 = it; + EXPECT_TRUE(*it == *it2) << "Assigned iterators must point to the " + << "element same as its source points to"; + + // Verifies that prefix operator++() returns *this. + EXPECT_EQ(&it, &(++it)) << "Result of the prefix operator++ must be " + << "refer to the original object"; + + // Verifies that the result of the postfix operator++ points to the value + // pointed to by the original iterator. + int original_value = *it; // Have to compute it outside of macro call to be + // unaffected by the parameter evaluation order. + EXPECT_EQ(original_value, *(it++)); + + // Verifies that prefix and postfix operator++() advance an iterator + // all the same. + it2 = it; + it++; + ++it2; + EXPECT_TRUE(*it == *it2); +} + +// Tests that Range() generates the expected sequence. +TEST(RangeTest, IntRangeWithDefaultStep) { + const ParamGenerator gen = Range(0, 3); + const int expected_values[] = {0, 1, 2}; + VerifyGenerator(gen, expected_values); +} + +// Edge case. Tests that Range() generates the single element sequence +// as expected when provided with range limits that are equal. +TEST(RangeTest, IntRangeSingleValue) { + const ParamGenerator gen = Range(0, 1); + const int expected_values[] = {0}; + VerifyGenerator(gen, expected_values); +} + +// Edge case. Tests that Range() with generates empty sequence when +// supplied with an empty range. +TEST(RangeTest, IntRangeEmpty) { + const ParamGenerator gen = Range(0, 0); + VerifyGeneratorIsEmpty(gen); +} + +// Tests that Range() with custom step (greater then one) generates +// the expected sequence. +TEST(RangeTest, IntRangeWithCustomStep) { + const ParamGenerator gen = Range(0, 9, 3); + const int expected_values[] = {0, 3, 6}; + VerifyGenerator(gen, expected_values); +} + +// Tests that Range() with custom step (greater then one) generates +// the expected sequence when the last element does not fall on the +// upper range limit. Sequences generated by Range() must not have +// elements beyond the range limits. +TEST(RangeTest, IntRangeWithCustomStepOverUpperBound) { + const ParamGenerator gen = Range(0, 4, 3); + const int expected_values[] = {0, 3}; + VerifyGenerator(gen, expected_values); +} + +// Verifies that Range works with user-defined types that define +// copy constructor, operator=(), operator+(), and operator<(). +class DogAdder { + public: + explicit DogAdder(const char* a_value) : value_(a_value) {} + DogAdder(const DogAdder& other) : value_(other.value_.c_str()) {} + + DogAdder operator=(const DogAdder& other) { + if (this != &other) + value_ = other.value_; + return *this; + } + DogAdder operator+(const DogAdder& other) const { + Message msg; + msg << value_.c_str() << other.value_.c_str(); + return DogAdder(msg.GetString().c_str()); + } + bool operator<(const DogAdder& other) const { + return value_ < other.value_; + } + const std::string& value() const { return value_; } + + private: + std::string value_; +}; + +TEST(RangeTest, WorksWithACustomType) { + const ParamGenerator gen = + Range(DogAdder("cat"), DogAdder("catdogdog"), DogAdder("dog")); + ParamGenerator::iterator it = gen.begin(); + + ASSERT_FALSE(it == gen.end()); + EXPECT_STREQ("cat", it->value().c_str()); + + ASSERT_FALSE(++it == gen.end()); + EXPECT_STREQ("catdog", it->value().c_str()); + + EXPECT_TRUE(++it == gen.end()); +} + +class IntWrapper { + public: + explicit IntWrapper(int a_value) : value_(a_value) {} + IntWrapper(const IntWrapper& other) : value_(other.value_) {} + + IntWrapper operator=(const IntWrapper& other) { + value_ = other.value_; + return *this; + } + // operator+() adds a different type. + IntWrapper operator+(int other) const { return IntWrapper(value_ + other); } + bool operator<(const IntWrapper& other) const { + return value_ < other.value_; + } + int value() const { return value_; } + + private: + int value_; +}; + +TEST(RangeTest, WorksWithACustomTypeWithDifferentIncrementType) { + const ParamGenerator gen = Range(IntWrapper(0), IntWrapper(2)); + ParamGenerator::iterator it = gen.begin(); + + ASSERT_FALSE(it == gen.end()); + EXPECT_EQ(0, it->value()); + + ASSERT_FALSE(++it == gen.end()); + EXPECT_EQ(1, it->value()); + + EXPECT_TRUE(++it == gen.end()); +} + +// Tests that ValuesIn() with an array parameter generates +// the expected sequence. +TEST(ValuesInTest, ValuesInArray) { + int array[] = {3, 5, 8}; + const ParamGenerator gen = ValuesIn(array); + VerifyGenerator(gen, array); +} + +// Tests that ValuesIn() with a const array parameter generates +// the expected sequence. +TEST(ValuesInTest, ValuesInConstArray) { + const int array[] = {3, 5, 8}; + const ParamGenerator gen = ValuesIn(array); + VerifyGenerator(gen, array); +} + +// Edge case. Tests that ValuesIn() with an array parameter containing a +// single element generates the single element sequence. +TEST(ValuesInTest, ValuesInSingleElementArray) { + int array[] = {42}; + const ParamGenerator gen = ValuesIn(array); + VerifyGenerator(gen, array); +} + +// Tests that ValuesIn() generates the expected sequence for an STL +// container (vector). +TEST(ValuesInTest, ValuesInVector) { + typedef ::std::vector ContainerType; + ContainerType values; + values.push_back(3); + values.push_back(5); + values.push_back(8); + const ParamGenerator gen = ValuesIn(values); + + const int expected_values[] = {3, 5, 8}; + VerifyGenerator(gen, expected_values); +} + +// Tests that ValuesIn() generates the expected sequence. +TEST(ValuesInTest, ValuesInIteratorRange) { + typedef ::std::vector ContainerType; + ContainerType values; + values.push_back(3); + values.push_back(5); + values.push_back(8); + const ParamGenerator gen = ValuesIn(values.begin(), values.end()); + + const int expected_values[] = {3, 5, 8}; + VerifyGenerator(gen, expected_values); +} + +// Edge case. Tests that ValuesIn() provided with an iterator range specifying a +// single value generates a single-element sequence. +TEST(ValuesInTest, ValuesInSingleElementIteratorRange) { + typedef ::std::vector ContainerType; + ContainerType values; + values.push_back(42); + const ParamGenerator gen = ValuesIn(values.begin(), values.end()); + + const int expected_values[] = {42}; + VerifyGenerator(gen, expected_values); +} + +// Edge case. Tests that ValuesIn() provided with an empty iterator range +// generates an empty sequence. +TEST(ValuesInTest, ValuesInEmptyIteratorRange) { + typedef ::std::vector ContainerType; + ContainerType values; + const ParamGenerator gen = ValuesIn(values.begin(), values.end()); + + VerifyGeneratorIsEmpty(gen); +} + +// Tests that the Values() generates the expected sequence. +TEST(ValuesTest, ValuesWorks) { + const ParamGenerator gen = Values(3, 5, 8); + + const int expected_values[] = {3, 5, 8}; + VerifyGenerator(gen, expected_values); +} + +// Tests that Values() generates the expected sequences from elements of +// different types convertible to ParamGenerator's parameter type. +TEST(ValuesTest, ValuesWorksForValuesOfCompatibleTypes) { + const ParamGenerator gen = Values(3, 5.0f, 8.0); + + const double expected_values[] = {3.0, 5.0, 8.0}; + VerifyGenerator(gen, expected_values); +} + +TEST(ValuesTest, ValuesWorksForMaxLengthList) { + const ParamGenerator gen = Values( + 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, + 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, + 210, 220, 230, 240, 250, 260, 270, 280, 290, 300, + 310, 320, 330, 340, 350, 360, 370, 380, 390, 400, + 410, 420, 430, 440, 450, 460, 470, 480, 490, 500); + + const int expected_values[] = { + 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, + 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, + 210, 220, 230, 240, 250, 260, 270, 280, 290, 300, + 310, 320, 330, 340, 350, 360, 370, 380, 390, 400, + 410, 420, 430, 440, 450, 460, 470, 480, 490, 500}; + VerifyGenerator(gen, expected_values); +} + +// Edge case test. Tests that single-parameter Values() generates the sequence +// with the single value. +TEST(ValuesTest, ValuesWithSingleParameter) { + const ParamGenerator gen = Values(42); + + const int expected_values[] = {42}; + VerifyGenerator(gen, expected_values); +} + +// Tests that Bool() generates sequence (false, true). +TEST(BoolTest, BoolWorks) { + const ParamGenerator gen = Bool(); + + const bool expected_values[] = {false, true}; + VerifyGenerator(gen, expected_values); +} + +# if GTEST_HAS_COMBINE + +// Tests that Combine() with two parameters generates the expected sequence. +TEST(CombineTest, CombineWithTwoParameters) { + const char* foo = "foo"; + const char* bar = "bar"; + const ParamGenerator > gen = + Combine(Values(foo, bar), Values(3, 4)); + + tuple expected_values[] = { + make_tuple(foo, 3), make_tuple(foo, 4), + make_tuple(bar, 3), make_tuple(bar, 4)}; + VerifyGenerator(gen, expected_values); +} + +// Tests that Combine() with three parameters generates the expected sequence. +TEST(CombineTest, CombineWithThreeParameters) { + const ParamGenerator > gen = Combine(Values(0, 1), + Values(3, 4), + Values(5, 6)); + tuple expected_values[] = { + make_tuple(0, 3, 5), make_tuple(0, 3, 6), + make_tuple(0, 4, 5), make_tuple(0, 4, 6), + make_tuple(1, 3, 5), make_tuple(1, 3, 6), + make_tuple(1, 4, 5), make_tuple(1, 4, 6)}; + VerifyGenerator(gen, expected_values); +} + +// Tests that the Combine() with the first parameter generating a single value +// sequence generates a sequence with the number of elements equal to the +// number of elements in the sequence generated by the second parameter. +TEST(CombineTest, CombineWithFirstParameterSingleValue) { + const ParamGenerator > gen = Combine(Values(42), + Values(0, 1)); + + tuple expected_values[] = {make_tuple(42, 0), make_tuple(42, 1)}; + VerifyGenerator(gen, expected_values); +} + +// Tests that the Combine() with the second parameter generating a single value +// sequence generates a sequence with the number of elements equal to the +// number of elements in the sequence generated by the first parameter. +TEST(CombineTest, CombineWithSecondParameterSingleValue) { + const ParamGenerator > gen = Combine(Values(0, 1), + Values(42)); + + tuple expected_values[] = {make_tuple(0, 42), make_tuple(1, 42)}; + VerifyGenerator(gen, expected_values); +} + +// Tests that when the first parameter produces an empty sequence, +// Combine() produces an empty sequence, too. +TEST(CombineTest, CombineWithFirstParameterEmptyRange) { + const ParamGenerator > gen = Combine(Range(0, 0), + Values(0, 1)); + VerifyGeneratorIsEmpty(gen); +} + +// Tests that when the second parameter produces an empty sequence, +// Combine() produces an empty sequence, too. +TEST(CombineTest, CombineWithSecondParameterEmptyRange) { + const ParamGenerator > gen = Combine(Values(0, 1), + Range(1, 1)); + VerifyGeneratorIsEmpty(gen); +} + +// Edge case. Tests that combine works with the maximum number +// of parameters supported by Google Test (currently 10). +TEST(CombineTest, CombineWithMaxNumberOfParameters) { + const char* foo = "foo"; + const char* bar = "bar"; + const ParamGenerator > gen = Combine(Values(foo, bar), + Values(1), Values(2), + Values(3), Values(4), + Values(5), Values(6), + Values(7), Values(8), + Values(9)); + + tuple + expected_values[] = {make_tuple(foo, 1, 2, 3, 4, 5, 6, 7, 8, 9), + make_tuple(bar, 1, 2, 3, 4, 5, 6, 7, 8, 9)}; + VerifyGenerator(gen, expected_values); +} + +# endif // GTEST_HAS_COMBINE + +// Tests that an generator produces correct sequence after being +// assigned from another generator. +TEST(ParamGeneratorTest, AssignmentWorks) { + ParamGenerator gen = Values(1, 2); + const ParamGenerator gen2 = Values(3, 4); + gen = gen2; + + const int expected_values[] = {3, 4}; + VerifyGenerator(gen, expected_values); +} + +// This test verifies that the tests are expanded and run as specified: +// one test per element from the sequence produced by the generator +// specified in INSTANTIATE_TEST_CASE_P. It also verifies that the test's +// fixture constructor, SetUp(), and TearDown() have run and have been +// supplied with the correct parameters. + +// The use of environment object allows detection of the case where no test +// case functionality is run at all. In this case TestCaseTearDown will not +// be able to detect missing tests, naturally. +template +class TestGenerationEnvironment : public ::testing::Environment { + public: + static TestGenerationEnvironment* Instance() { + static TestGenerationEnvironment* instance = new TestGenerationEnvironment; + return instance; + } + + void FixtureConstructorExecuted() { fixture_constructor_count_++; } + void SetUpExecuted() { set_up_count_++; } + void TearDownExecuted() { tear_down_count_++; } + void TestBodyExecuted() { test_body_count_++; } + + virtual void TearDown() { + // If all MultipleTestGenerationTest tests have been de-selected + // by the filter flag, the following checks make no sense. + bool perform_check = false; + + for (int i = 0; i < kExpectedCalls; ++i) { + Message msg; + msg << "TestsExpandedAndRun/" << i; + if (UnitTestOptions::FilterMatchesTest( + "TestExpansionModule/MultipleTestGenerationTest", + msg.GetString().c_str())) { + perform_check = true; + } + } + if (perform_check) { + EXPECT_EQ(kExpectedCalls, fixture_constructor_count_) + << "Fixture constructor of ParamTestGenerationTest test case " + << "has not been run as expected."; + EXPECT_EQ(kExpectedCalls, set_up_count_) + << "Fixture SetUp method of ParamTestGenerationTest test case " + << "has not been run as expected."; + EXPECT_EQ(kExpectedCalls, tear_down_count_) + << "Fixture TearDown method of ParamTestGenerationTest test case " + << "has not been run as expected."; + EXPECT_EQ(kExpectedCalls, test_body_count_) + << "Test in ParamTestGenerationTest test case " + << "has not been run as expected."; + } + } + + private: + TestGenerationEnvironment() : fixture_constructor_count_(0), set_up_count_(0), + tear_down_count_(0), test_body_count_(0) {} + + int fixture_constructor_count_; + int set_up_count_; + int tear_down_count_; + int test_body_count_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestGenerationEnvironment); +}; + +const int test_generation_params[] = {36, 42, 72}; + +class TestGenerationTest : public TestWithParam { + public: + enum { + PARAMETER_COUNT = + sizeof(test_generation_params)/sizeof(test_generation_params[0]) + }; + + typedef TestGenerationEnvironment Environment; + + TestGenerationTest() { + Environment::Instance()->FixtureConstructorExecuted(); + current_parameter_ = GetParam(); + } + virtual void SetUp() { + Environment::Instance()->SetUpExecuted(); + EXPECT_EQ(current_parameter_, GetParam()); + } + virtual void TearDown() { + Environment::Instance()->TearDownExecuted(); + EXPECT_EQ(current_parameter_, GetParam()); + } + + static void SetUpTestCase() { + bool all_tests_in_test_case_selected = true; + + for (int i = 0; i < PARAMETER_COUNT; ++i) { + Message test_name; + test_name << "TestsExpandedAndRun/" << i; + if ( !UnitTestOptions::FilterMatchesTest( + "TestExpansionModule/MultipleTestGenerationTest", + test_name.GetString())) { + all_tests_in_test_case_selected = false; + } + } + EXPECT_TRUE(all_tests_in_test_case_selected) + << "When running the TestGenerationTest test case all of its tests\n" + << "must be selected by the filter flag for the test case to pass.\n" + << "If not all of them are enabled, we can't reliably conclude\n" + << "that the correct number of tests have been generated."; + + collected_parameters_.clear(); + } + + static void TearDownTestCase() { + vector expected_values(test_generation_params, + test_generation_params + PARAMETER_COUNT); + // Test execution order is not guaranteed by Google Test, + // so the order of values in collected_parameters_ can be + // different and we have to sort to compare. + sort(expected_values.begin(), expected_values.end()); + sort(collected_parameters_.begin(), collected_parameters_.end()); + + EXPECT_TRUE(collected_parameters_ == expected_values); + } + + protected: + int current_parameter_; + static vector collected_parameters_; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestGenerationTest); +}; +vector TestGenerationTest::collected_parameters_; + +TEST_P(TestGenerationTest, TestsExpandedAndRun) { + Environment::Instance()->TestBodyExecuted(); + EXPECT_EQ(current_parameter_, GetParam()); + collected_parameters_.push_back(GetParam()); +} +INSTANTIATE_TEST_CASE_P(TestExpansionModule, TestGenerationTest, + ValuesIn(test_generation_params)); + +// This test verifies that the element sequence (third parameter of +// INSTANTIATE_TEST_CASE_P) is evaluated in InitGoogleTest() and neither at +// the call site of INSTANTIATE_TEST_CASE_P nor in RUN_ALL_TESTS(). For +// that, we declare param_value_ to be a static member of +// GeneratorEvaluationTest and initialize it to 0. We set it to 1 in +// main(), just before invocation of InitGoogleTest(). After calling +// InitGoogleTest(), we set the value to 2. If the sequence is evaluated +// before or after InitGoogleTest, INSTANTIATE_TEST_CASE_P will create a +// test with parameter other than 1, and the test body will fail the +// assertion. +class GeneratorEvaluationTest : public TestWithParam { + public: + static int param_value() { return param_value_; } + static void set_param_value(int param_value) { param_value_ = param_value; } + + private: + static int param_value_; +}; +int GeneratorEvaluationTest::param_value_ = 0; + +TEST_P(GeneratorEvaluationTest, GeneratorsEvaluatedInMain) { + EXPECT_EQ(1, GetParam()); +} +INSTANTIATE_TEST_CASE_P(GenEvalModule, + GeneratorEvaluationTest, + Values(GeneratorEvaluationTest::param_value())); + +// Tests that generators defined in a different translation unit are +// functional. Generator extern_gen is defined in gtest-param-test_test2.cc. +extern ParamGenerator extern_gen; +class ExternalGeneratorTest : public TestWithParam {}; +TEST_P(ExternalGeneratorTest, ExternalGenerator) { + // Sequence produced by extern_gen contains only a single value + // which we verify here. + EXPECT_EQ(GetParam(), 33); +} +INSTANTIATE_TEST_CASE_P(ExternalGeneratorModule, + ExternalGeneratorTest, + extern_gen); + +// Tests that a parameterized test case can be defined in one translation +// unit and instantiated in another. This test will be instantiated in +// gtest-param-test_test2.cc. ExternalInstantiationTest fixture class is +// defined in gtest-param-test_test.h. +TEST_P(ExternalInstantiationTest, IsMultipleOf33) { + EXPECT_EQ(0, GetParam() % 33); +} + +// Tests that a parameterized test case can be instantiated with multiple +// generators. +class MultipleInstantiationTest : public TestWithParam {}; +TEST_P(MultipleInstantiationTest, AllowsMultipleInstances) { +} +INSTANTIATE_TEST_CASE_P(Sequence1, MultipleInstantiationTest, Values(1, 2)); +INSTANTIATE_TEST_CASE_P(Sequence2, MultipleInstantiationTest, Range(3, 5)); + +// Tests that a parameterized test case can be instantiated +// in multiple translation units. This test will be instantiated +// here and in gtest-param-test_test2.cc. +// InstantiationInMultipleTranslationUnitsTest fixture class +// is defined in gtest-param-test_test.h. +TEST_P(InstantiationInMultipleTranslaionUnitsTest, IsMultipleOf42) { + EXPECT_EQ(0, GetParam() % 42); +} +INSTANTIATE_TEST_CASE_P(Sequence1, + InstantiationInMultipleTranslaionUnitsTest, + Values(42, 42*2)); + +// Tests that each iteration of parameterized test runs in a separate test +// object. +class SeparateInstanceTest : public TestWithParam { + public: + SeparateInstanceTest() : count_(0) {} + + static void TearDownTestCase() { + EXPECT_GE(global_count_, 2) + << "If some (but not all) SeparateInstanceTest tests have been " + << "filtered out this test will fail. Make sure that all " + << "GeneratorEvaluationTest are selected or de-selected together " + << "by the test filter."; + } + + protected: + int count_; + static int global_count_; +}; +int SeparateInstanceTest::global_count_ = 0; + +TEST_P(SeparateInstanceTest, TestsRunInSeparateInstances) { + EXPECT_EQ(0, count_++); + global_count_++; +} +INSTANTIATE_TEST_CASE_P(FourElemSequence, SeparateInstanceTest, Range(1, 4)); + +// Tests that all instantiations of a test have named appropriately. Test +// defined with TEST_P(TestCaseName, TestName) and instantiated with +// INSTANTIATE_TEST_CASE_P(SequenceName, TestCaseName, generator) must be named +// SequenceName/TestCaseName.TestName/i, where i is the 0-based index of the +// sequence element used to instantiate the test. +class NamingTest : public TestWithParam {}; + +TEST_P(NamingTest, TestsReportCorrectNamesAndParameters) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + EXPECT_STREQ("ZeroToFiveSequence/NamingTest", test_info->test_case_name()); + + Message index_stream; + index_stream << "TestsReportCorrectNamesAndParameters/" << GetParam(); + EXPECT_STREQ(index_stream.GetString().c_str(), test_info->name()); + + EXPECT_EQ(::testing::PrintToString(GetParam()), test_info->value_param()); +} + +INSTANTIATE_TEST_CASE_P(ZeroToFiveSequence, NamingTest, Range(0, 5)); + +// Class that cannot be streamed into an ostream. It needs to be copyable +// (and, in case of MSVC, also assignable) in order to be a test parameter +// type. Its default copy constructor and assignment operator do exactly +// what we need. +class Unstreamable { + public: + explicit Unstreamable(int value) : value_(value) {} + + private: + int value_; +}; + +class CommentTest : public TestWithParam {}; + +TEST_P(CommentTest, TestsCorrectlyReportUnstreamableParams) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + EXPECT_EQ(::testing::PrintToString(GetParam()), test_info->value_param()); +} + +INSTANTIATE_TEST_CASE_P(InstantiationWithComments, + CommentTest, + Values(Unstreamable(1))); + +// Verify that we can create a hierarchy of test fixtures, where the base +// class fixture is not parameterized and the derived class is. In this case +// ParameterizedDerivedTest inherits from NonParameterizedBaseTest. We +// perform simple tests on both. +class NonParameterizedBaseTest : public ::testing::Test { + public: + NonParameterizedBaseTest() : n_(17) { } + protected: + int n_; +}; + +class ParameterizedDerivedTest : public NonParameterizedBaseTest, + public ::testing::WithParamInterface { + protected: + ParameterizedDerivedTest() : count_(0) { } + int count_; + static int global_count_; +}; + +int ParameterizedDerivedTest::global_count_ = 0; + +TEST_F(NonParameterizedBaseTest, FixtureIsInitialized) { + EXPECT_EQ(17, n_); +} + +TEST_P(ParameterizedDerivedTest, SeesSequence) { + EXPECT_EQ(17, n_); + EXPECT_EQ(0, count_++); + EXPECT_EQ(GetParam(), global_count_++); +} + +class ParameterizedDeathTest : public ::testing::TestWithParam { }; + +TEST_F(ParameterizedDeathTest, GetParamDiesFromTestF) { + EXPECT_DEATH_IF_SUPPORTED(GetParam(), + ".* value-parameterized test .*"); +} + +INSTANTIATE_TEST_CASE_P(RangeZeroToFive, ParameterizedDerivedTest, Range(0, 5)); + +#endif // GTEST_HAS_PARAM_TEST + +TEST(CompileTest, CombineIsDefinedOnlyWhenGtestHasParamTestIsDefined) { +#if GTEST_HAS_COMBINE && !GTEST_HAS_PARAM_TEST + FAIL() << "GTEST_HAS_COMBINE is defined while GTEST_HAS_PARAM_TEST is not\n" +#endif +} + +int main(int argc, char **argv) { +#if GTEST_HAS_PARAM_TEST + // Used in TestGenerationTest test case. + AddGlobalTestEnvironment(TestGenerationTest::Environment::Instance()); + // Used in GeneratorEvaluationTest test case. Tests that the updated value + // will be picked up for instantiating tests in GeneratorEvaluationTest. + GeneratorEvaluationTest::set_param_value(1); +#endif // GTEST_HAS_PARAM_TEST + + ::testing::InitGoogleTest(&argc, argv); + +#if GTEST_HAS_PARAM_TEST + // Used in GeneratorEvaluationTest test case. Tests that value updated + // here will NOT be used for instantiating tests in + // GeneratorEvaluationTest. + GeneratorEvaluationTest::set_param_value(2); +#endif // GTEST_HAS_PARAM_TEST + + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest-param-test_test.h b/external/gtest/test/gtest-param-test_test.h new file mode 100644 index 0000000000..26ea122b10 --- /dev/null +++ b/external/gtest/test/gtest-param-test_test.h @@ -0,0 +1,57 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file provides classes and functions used internally +// for testing Google Test itself. + +#ifndef GTEST_TEST_GTEST_PARAM_TEST_TEST_H_ +#define GTEST_TEST_GTEST_PARAM_TEST_TEST_H_ + +#include "gtest/gtest.h" + +#if GTEST_HAS_PARAM_TEST + +// Test fixture for testing definition and instantiation of a test +// in separate translation units. +class ExternalInstantiationTest : public ::testing::TestWithParam { +}; + +// Test fixture for testing instantiation of a test in multiple +// translation units. +class InstantiationInMultipleTranslaionUnitsTest + : public ::testing::TestWithParam { +}; + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_TEST_GTEST_PARAM_TEST_TEST_H_ diff --git a/external/gtest/test/gtest-port_test.cc b/external/gtest/test/gtest-port_test.cc new file mode 100644 index 0000000000..43f1f20105 --- /dev/null +++ b/external/gtest/test/gtest-port_test.cc @@ -0,0 +1,1253 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev), wan@google.com (Zhanyong Wan) +// +// This file tests the internal cross-platform support utilities. + +#include "gtest/internal/gtest-port.h" + +#include + +#if GTEST_OS_MAC +# include +#endif // GTEST_OS_MAC + +#include +#include // For std::pair and std::make_pair. +#include + +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +using std::make_pair; +using std::pair; + +namespace testing { +namespace internal { + +TEST(IsXDigitTest, WorksForNarrowAscii) { + EXPECT_TRUE(IsXDigit('0')); + EXPECT_TRUE(IsXDigit('9')); + EXPECT_TRUE(IsXDigit('A')); + EXPECT_TRUE(IsXDigit('F')); + EXPECT_TRUE(IsXDigit('a')); + EXPECT_TRUE(IsXDigit('f')); + + EXPECT_FALSE(IsXDigit('-')); + EXPECT_FALSE(IsXDigit('g')); + EXPECT_FALSE(IsXDigit('G')); +} + +TEST(IsXDigitTest, ReturnsFalseForNarrowNonAscii) { + EXPECT_FALSE(IsXDigit(static_cast(0x80))); + EXPECT_FALSE(IsXDigit(static_cast('0' | 0x80))); +} + +TEST(IsXDigitTest, WorksForWideAscii) { + EXPECT_TRUE(IsXDigit(L'0')); + EXPECT_TRUE(IsXDigit(L'9')); + EXPECT_TRUE(IsXDigit(L'A')); + EXPECT_TRUE(IsXDigit(L'F')); + EXPECT_TRUE(IsXDigit(L'a')); + EXPECT_TRUE(IsXDigit(L'f')); + + EXPECT_FALSE(IsXDigit(L'-')); + EXPECT_FALSE(IsXDigit(L'g')); + EXPECT_FALSE(IsXDigit(L'G')); +} + +TEST(IsXDigitTest, ReturnsFalseForWideNonAscii) { + EXPECT_FALSE(IsXDigit(static_cast(0x80))); + EXPECT_FALSE(IsXDigit(static_cast(L'0' | 0x80))); + EXPECT_FALSE(IsXDigit(static_cast(L'0' | 0x100))); +} + +class Base { + public: + // Copy constructor and assignment operator do exactly what we need, so we + // use them. + Base() : member_(0) {} + explicit Base(int n) : member_(n) {} + virtual ~Base() {} + int member() { return member_; } + + private: + int member_; +}; + +class Derived : public Base { + public: + explicit Derived(int n) : Base(n) {} +}; + +TEST(ImplicitCastTest, ConvertsPointers) { + Derived derived(0); + EXPECT_TRUE(&derived == ::testing::internal::ImplicitCast_(&derived)); +} + +TEST(ImplicitCastTest, CanUseInheritance) { + Derived derived(1); + Base base = ::testing::internal::ImplicitCast_(derived); + EXPECT_EQ(derived.member(), base.member()); +} + +class Castable { + public: + explicit Castable(bool* converted) : converted_(converted) {} + operator Base() { + *converted_ = true; + return Base(); + } + + private: + bool* converted_; +}; + +TEST(ImplicitCastTest, CanUseNonConstCastOperator) { + bool converted = false; + Castable castable(&converted); + Base base = ::testing::internal::ImplicitCast_(castable); + EXPECT_TRUE(converted); +} + +class ConstCastable { + public: + explicit ConstCastable(bool* converted) : converted_(converted) {} + operator Base() const { + *converted_ = true; + return Base(); + } + + private: + bool* converted_; +}; + +TEST(ImplicitCastTest, CanUseConstCastOperatorOnConstValues) { + bool converted = false; + const ConstCastable const_castable(&converted); + Base base = ::testing::internal::ImplicitCast_(const_castable); + EXPECT_TRUE(converted); +} + +class ConstAndNonConstCastable { + public: + ConstAndNonConstCastable(bool* converted, bool* const_converted) + : converted_(converted), const_converted_(const_converted) {} + operator Base() { + *converted_ = true; + return Base(); + } + operator Base() const { + *const_converted_ = true; + return Base(); + } + + private: + bool* converted_; + bool* const_converted_; +}; + +TEST(ImplicitCastTest, CanSelectBetweenConstAndNonConstCasrAppropriately) { + bool converted = false; + bool const_converted = false; + ConstAndNonConstCastable castable(&converted, &const_converted); + Base base = ::testing::internal::ImplicitCast_(castable); + EXPECT_TRUE(converted); + EXPECT_FALSE(const_converted); + + converted = false; + const_converted = false; + const ConstAndNonConstCastable const_castable(&converted, &const_converted); + base = ::testing::internal::ImplicitCast_(const_castable); + EXPECT_FALSE(converted); + EXPECT_TRUE(const_converted); +} + +class To { + public: + To(bool* converted) { *converted = true; } // NOLINT +}; + +TEST(ImplicitCastTest, CanUseImplicitConstructor) { + bool converted = false; + To to = ::testing::internal::ImplicitCast_(&converted); + (void)to; + EXPECT_TRUE(converted); +} + +TEST(IteratorTraitsTest, WorksForSTLContainerIterators) { + StaticAssertTypeEq::const_iterator>::value_type>(); + StaticAssertTypeEq::iterator>::value_type>(); +} + +TEST(IteratorTraitsTest, WorksForPointerToNonConst) { + StaticAssertTypeEq::value_type>(); + StaticAssertTypeEq::value_type>(); +} + +TEST(IteratorTraitsTest, WorksForPointerToConst) { + StaticAssertTypeEq::value_type>(); + StaticAssertTypeEq::value_type>(); +} + +// Tests that the element_type typedef is available in scoped_ptr and refers +// to the parameter type. +TEST(ScopedPtrTest, DefinesElementType) { + StaticAssertTypeEq::element_type>(); +} + +// TODO(vladl@google.com): Implement THE REST of scoped_ptr tests. + +TEST(GtestCheckSyntaxTest, BehavesLikeASingleStatement) { + if (AlwaysFalse()) + GTEST_CHECK_(false) << "This should never be executed; " + "It's a compilation test only."; + + if (AlwaysTrue()) + GTEST_CHECK_(true); + else + ; // NOLINT + + if (AlwaysFalse()) + ; // NOLINT + else + GTEST_CHECK_(true) << ""; +} + +TEST(GtestCheckSyntaxTest, WorksWithSwitch) { + switch (0) { + case 1: + break; + default: + GTEST_CHECK_(true); + } + + switch (0) + case 0: + GTEST_CHECK_(true) << "Check failed in switch case"; +} + +// Verifies behavior of FormatFileLocation. +TEST(FormatFileLocationTest, FormatsFileLocation) { + EXPECT_PRED_FORMAT2(IsSubstring, "foo.cc", FormatFileLocation("foo.cc", 42)); + EXPECT_PRED_FORMAT2(IsSubstring, "42", FormatFileLocation("foo.cc", 42)); +} + +TEST(FormatFileLocationTest, FormatsUnknownFile) { + EXPECT_PRED_FORMAT2( + IsSubstring, "unknown file", FormatFileLocation(NULL, 42)); + EXPECT_PRED_FORMAT2(IsSubstring, "42", FormatFileLocation(NULL, 42)); +} + +TEST(FormatFileLocationTest, FormatsUknownLine) { + EXPECT_EQ("foo.cc:", FormatFileLocation("foo.cc", -1)); +} + +TEST(FormatFileLocationTest, FormatsUknownFileAndLine) { + EXPECT_EQ("unknown file:", FormatFileLocation(NULL, -1)); +} + +// Verifies behavior of FormatCompilerIndependentFileLocation. +TEST(FormatCompilerIndependentFileLocationTest, FormatsFileLocation) { + EXPECT_EQ("foo.cc:42", FormatCompilerIndependentFileLocation("foo.cc", 42)); +} + +TEST(FormatCompilerIndependentFileLocationTest, FormatsUknownFile) { + EXPECT_EQ("unknown file:42", + FormatCompilerIndependentFileLocation(NULL, 42)); +} + +TEST(FormatCompilerIndependentFileLocationTest, FormatsUknownLine) { + EXPECT_EQ("foo.cc", FormatCompilerIndependentFileLocation("foo.cc", -1)); +} + +TEST(FormatCompilerIndependentFileLocationTest, FormatsUknownFileAndLine) { + EXPECT_EQ("unknown file", FormatCompilerIndependentFileLocation(NULL, -1)); +} + +#if GTEST_OS_MAC || GTEST_OS_QNX +void* ThreadFunc(void* data) { + pthread_mutex_t* mutex = static_cast(data); + pthread_mutex_lock(mutex); + pthread_mutex_unlock(mutex); + return NULL; +} + +TEST(GetThreadCountTest, ReturnsCorrectValue) { + EXPECT_EQ(1U, GetThreadCount()); + pthread_mutex_t mutex; + pthread_attr_t attr; + pthread_t thread_id; + + // TODO(vladl@google.com): turn mutex into internal::Mutex for automatic + // destruction. + pthread_mutex_init(&mutex, NULL); + pthread_mutex_lock(&mutex); + ASSERT_EQ(0, pthread_attr_init(&attr)); + ASSERT_EQ(0, pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)); + + const int status = pthread_create(&thread_id, &attr, &ThreadFunc, &mutex); + ASSERT_EQ(0, pthread_attr_destroy(&attr)); + ASSERT_EQ(0, status); + EXPECT_EQ(2U, GetThreadCount()); + pthread_mutex_unlock(&mutex); + + void* dummy; + ASSERT_EQ(0, pthread_join(thread_id, &dummy)); + +# if GTEST_OS_MAC + + // MacOS X may not immediately report the updated thread count after + // joining a thread, causing flakiness in this test. To counter that, we + // wait for up to .5 seconds for the OS to report the correct value. + for (int i = 0; i < 5; ++i) { + if (GetThreadCount() == 1) + break; + + SleepMilliseconds(100); + } + +# endif // GTEST_OS_MAC + + EXPECT_EQ(1U, GetThreadCount()); + pthread_mutex_destroy(&mutex); +} +#else +TEST(GetThreadCountTest, ReturnsZeroWhenUnableToCountThreads) { + EXPECT_EQ(0U, GetThreadCount()); +} +#endif // GTEST_OS_MAC || GTEST_OS_QNX + +TEST(GtestCheckDeathTest, DiesWithCorrectOutputOnFailure) { + const bool a_false_condition = false; + const char regex[] = +#ifdef _MSC_VER + "gtest-port_test\\.cc\\(\\d+\\):" +#elif GTEST_USES_POSIX_RE + "gtest-port_test\\.cc:[0-9]+" +#else + "gtest-port_test\\.cc:\\d+" +#endif // _MSC_VER + ".*a_false_condition.*Extra info.*"; + + EXPECT_DEATH_IF_SUPPORTED(GTEST_CHECK_(a_false_condition) << "Extra info", + regex); +} + +#if GTEST_HAS_DEATH_TEST + +TEST(GtestCheckDeathTest, LivesSilentlyOnSuccess) { + EXPECT_EXIT({ + GTEST_CHECK_(true) << "Extra info"; + ::std::cerr << "Success\n"; + exit(0); }, + ::testing::ExitedWithCode(0), "Success"); +} + +#endif // GTEST_HAS_DEATH_TEST + +// Verifies that Google Test choose regular expression engine appropriate to +// the platform. The test will produce compiler errors in case of failure. +// For simplicity, we only cover the most important platforms here. +TEST(RegexEngineSelectionTest, SelectsCorrectRegexEngine) { +#if GTEST_HAS_POSIX_RE + + EXPECT_TRUE(GTEST_USES_POSIX_RE); + +#else + + EXPECT_TRUE(GTEST_USES_SIMPLE_RE); + +#endif +} + +#if GTEST_USES_POSIX_RE + +# if GTEST_HAS_TYPED_TEST + +template +class RETest : public ::testing::Test {}; + +// Defines StringTypes as the list of all string types that class RE +// supports. +typedef testing::Types< + ::std::string, +# if GTEST_HAS_GLOBAL_STRING + ::string, +# endif // GTEST_HAS_GLOBAL_STRING + const char*> StringTypes; + +TYPED_TEST_CASE(RETest, StringTypes); + +// Tests RE's implicit constructors. +TYPED_TEST(RETest, ImplicitConstructorWorks) { + const RE empty(TypeParam("")); + EXPECT_STREQ("", empty.pattern()); + + const RE simple(TypeParam("hello")); + EXPECT_STREQ("hello", simple.pattern()); + + const RE normal(TypeParam(".*(\\w+)")); + EXPECT_STREQ(".*(\\w+)", normal.pattern()); +} + +// Tests that RE's constructors reject invalid regular expressions. +TYPED_TEST(RETest, RejectsInvalidRegex) { + EXPECT_NONFATAL_FAILURE({ + const RE invalid(TypeParam("?")); + }, "\"?\" is not a valid POSIX Extended regular expression."); +} + +// Tests RE::FullMatch(). +TYPED_TEST(RETest, FullMatchWorks) { + const RE empty(TypeParam("")); + EXPECT_TRUE(RE::FullMatch(TypeParam(""), empty)); + EXPECT_FALSE(RE::FullMatch(TypeParam("a"), empty)); + + const RE re(TypeParam("a.*z")); + EXPECT_TRUE(RE::FullMatch(TypeParam("az"), re)); + EXPECT_TRUE(RE::FullMatch(TypeParam("axyz"), re)); + EXPECT_FALSE(RE::FullMatch(TypeParam("baz"), re)); + EXPECT_FALSE(RE::FullMatch(TypeParam("azy"), re)); +} + +// Tests RE::PartialMatch(). +TYPED_TEST(RETest, PartialMatchWorks) { + const RE empty(TypeParam("")); + EXPECT_TRUE(RE::PartialMatch(TypeParam(""), empty)); + EXPECT_TRUE(RE::PartialMatch(TypeParam("a"), empty)); + + const RE re(TypeParam("a.*z")); + EXPECT_TRUE(RE::PartialMatch(TypeParam("az"), re)); + EXPECT_TRUE(RE::PartialMatch(TypeParam("axyz"), re)); + EXPECT_TRUE(RE::PartialMatch(TypeParam("baz"), re)); + EXPECT_TRUE(RE::PartialMatch(TypeParam("azy"), re)); + EXPECT_FALSE(RE::PartialMatch(TypeParam("zza"), re)); +} + +# endif // GTEST_HAS_TYPED_TEST + +#elif GTEST_USES_SIMPLE_RE + +TEST(IsInSetTest, NulCharIsNotInAnySet) { + EXPECT_FALSE(IsInSet('\0', "")); + EXPECT_FALSE(IsInSet('\0', "\0")); + EXPECT_FALSE(IsInSet('\0', "a")); +} + +TEST(IsInSetTest, WorksForNonNulChars) { + EXPECT_FALSE(IsInSet('a', "Ab")); + EXPECT_FALSE(IsInSet('c', "")); + + EXPECT_TRUE(IsInSet('b', "bcd")); + EXPECT_TRUE(IsInSet('b', "ab")); +} + +TEST(IsAsciiDigitTest, IsFalseForNonDigit) { + EXPECT_FALSE(IsAsciiDigit('\0')); + EXPECT_FALSE(IsAsciiDigit(' ')); + EXPECT_FALSE(IsAsciiDigit('+')); + EXPECT_FALSE(IsAsciiDigit('-')); + EXPECT_FALSE(IsAsciiDigit('.')); + EXPECT_FALSE(IsAsciiDigit('a')); +} + +TEST(IsAsciiDigitTest, IsTrueForDigit) { + EXPECT_TRUE(IsAsciiDigit('0')); + EXPECT_TRUE(IsAsciiDigit('1')); + EXPECT_TRUE(IsAsciiDigit('5')); + EXPECT_TRUE(IsAsciiDigit('9')); +} + +TEST(IsAsciiPunctTest, IsFalseForNonPunct) { + EXPECT_FALSE(IsAsciiPunct('\0')); + EXPECT_FALSE(IsAsciiPunct(' ')); + EXPECT_FALSE(IsAsciiPunct('\n')); + EXPECT_FALSE(IsAsciiPunct('a')); + EXPECT_FALSE(IsAsciiPunct('0')); +} + +TEST(IsAsciiPunctTest, IsTrueForPunct) { + for (const char* p = "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"; *p; p++) { + EXPECT_PRED1(IsAsciiPunct, *p); + } +} + +TEST(IsRepeatTest, IsFalseForNonRepeatChar) { + EXPECT_FALSE(IsRepeat('\0')); + EXPECT_FALSE(IsRepeat(' ')); + EXPECT_FALSE(IsRepeat('a')); + EXPECT_FALSE(IsRepeat('1')); + EXPECT_FALSE(IsRepeat('-')); +} + +TEST(IsRepeatTest, IsTrueForRepeatChar) { + EXPECT_TRUE(IsRepeat('?')); + EXPECT_TRUE(IsRepeat('*')); + EXPECT_TRUE(IsRepeat('+')); +} + +TEST(IsAsciiWhiteSpaceTest, IsFalseForNonWhiteSpace) { + EXPECT_FALSE(IsAsciiWhiteSpace('\0')); + EXPECT_FALSE(IsAsciiWhiteSpace('a')); + EXPECT_FALSE(IsAsciiWhiteSpace('1')); + EXPECT_FALSE(IsAsciiWhiteSpace('+')); + EXPECT_FALSE(IsAsciiWhiteSpace('_')); +} + +TEST(IsAsciiWhiteSpaceTest, IsTrueForWhiteSpace) { + EXPECT_TRUE(IsAsciiWhiteSpace(' ')); + EXPECT_TRUE(IsAsciiWhiteSpace('\n')); + EXPECT_TRUE(IsAsciiWhiteSpace('\r')); + EXPECT_TRUE(IsAsciiWhiteSpace('\t')); + EXPECT_TRUE(IsAsciiWhiteSpace('\v')); + EXPECT_TRUE(IsAsciiWhiteSpace('\f')); +} + +TEST(IsAsciiWordCharTest, IsFalseForNonWordChar) { + EXPECT_FALSE(IsAsciiWordChar('\0')); + EXPECT_FALSE(IsAsciiWordChar('+')); + EXPECT_FALSE(IsAsciiWordChar('.')); + EXPECT_FALSE(IsAsciiWordChar(' ')); + EXPECT_FALSE(IsAsciiWordChar('\n')); +} + +TEST(IsAsciiWordCharTest, IsTrueForLetter) { + EXPECT_TRUE(IsAsciiWordChar('a')); + EXPECT_TRUE(IsAsciiWordChar('b')); + EXPECT_TRUE(IsAsciiWordChar('A')); + EXPECT_TRUE(IsAsciiWordChar('Z')); +} + +TEST(IsAsciiWordCharTest, IsTrueForDigit) { + EXPECT_TRUE(IsAsciiWordChar('0')); + EXPECT_TRUE(IsAsciiWordChar('1')); + EXPECT_TRUE(IsAsciiWordChar('7')); + EXPECT_TRUE(IsAsciiWordChar('9')); +} + +TEST(IsAsciiWordCharTest, IsTrueForUnderscore) { + EXPECT_TRUE(IsAsciiWordChar('_')); +} + +TEST(IsValidEscapeTest, IsFalseForNonPrintable) { + EXPECT_FALSE(IsValidEscape('\0')); + EXPECT_FALSE(IsValidEscape('\007')); +} + +TEST(IsValidEscapeTest, IsFalseForDigit) { + EXPECT_FALSE(IsValidEscape('0')); + EXPECT_FALSE(IsValidEscape('9')); +} + +TEST(IsValidEscapeTest, IsFalseForWhiteSpace) { + EXPECT_FALSE(IsValidEscape(' ')); + EXPECT_FALSE(IsValidEscape('\n')); +} + +TEST(IsValidEscapeTest, IsFalseForSomeLetter) { + EXPECT_FALSE(IsValidEscape('a')); + EXPECT_FALSE(IsValidEscape('Z')); +} + +TEST(IsValidEscapeTest, IsTrueForPunct) { + EXPECT_TRUE(IsValidEscape('.')); + EXPECT_TRUE(IsValidEscape('-')); + EXPECT_TRUE(IsValidEscape('^')); + EXPECT_TRUE(IsValidEscape('$')); + EXPECT_TRUE(IsValidEscape('(')); + EXPECT_TRUE(IsValidEscape(']')); + EXPECT_TRUE(IsValidEscape('{')); + EXPECT_TRUE(IsValidEscape('|')); +} + +TEST(IsValidEscapeTest, IsTrueForSomeLetter) { + EXPECT_TRUE(IsValidEscape('d')); + EXPECT_TRUE(IsValidEscape('D')); + EXPECT_TRUE(IsValidEscape('s')); + EXPECT_TRUE(IsValidEscape('S')); + EXPECT_TRUE(IsValidEscape('w')); + EXPECT_TRUE(IsValidEscape('W')); +} + +TEST(AtomMatchesCharTest, EscapedPunct) { + EXPECT_FALSE(AtomMatchesChar(true, '\\', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, '\\', ' ')); + EXPECT_FALSE(AtomMatchesChar(true, '_', '.')); + EXPECT_FALSE(AtomMatchesChar(true, '.', 'a')); + + EXPECT_TRUE(AtomMatchesChar(true, '\\', '\\')); + EXPECT_TRUE(AtomMatchesChar(true, '_', '_')); + EXPECT_TRUE(AtomMatchesChar(true, '+', '+')); + EXPECT_TRUE(AtomMatchesChar(true, '.', '.')); +} + +TEST(AtomMatchesCharTest, Escaped_d) { + EXPECT_FALSE(AtomMatchesChar(true, 'd', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 'd', 'a')); + EXPECT_FALSE(AtomMatchesChar(true, 'd', '.')); + + EXPECT_TRUE(AtomMatchesChar(true, 'd', '0')); + EXPECT_TRUE(AtomMatchesChar(true, 'd', '9')); +} + +TEST(AtomMatchesCharTest, Escaped_D) { + EXPECT_FALSE(AtomMatchesChar(true, 'D', '0')); + EXPECT_FALSE(AtomMatchesChar(true, 'D', '9')); + + EXPECT_TRUE(AtomMatchesChar(true, 'D', '\0')); + EXPECT_TRUE(AtomMatchesChar(true, 'D', 'a')); + EXPECT_TRUE(AtomMatchesChar(true, 'D', '-')); +} + +TEST(AtomMatchesCharTest, Escaped_s) { + EXPECT_FALSE(AtomMatchesChar(true, 's', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 's', 'a')); + EXPECT_FALSE(AtomMatchesChar(true, 's', '.')); + EXPECT_FALSE(AtomMatchesChar(true, 's', '9')); + + EXPECT_TRUE(AtomMatchesChar(true, 's', ' ')); + EXPECT_TRUE(AtomMatchesChar(true, 's', '\n')); + EXPECT_TRUE(AtomMatchesChar(true, 's', '\t')); +} + +TEST(AtomMatchesCharTest, Escaped_S) { + EXPECT_FALSE(AtomMatchesChar(true, 'S', ' ')); + EXPECT_FALSE(AtomMatchesChar(true, 'S', '\r')); + + EXPECT_TRUE(AtomMatchesChar(true, 'S', '\0')); + EXPECT_TRUE(AtomMatchesChar(true, 'S', 'a')); + EXPECT_TRUE(AtomMatchesChar(true, 'S', '9')); +} + +TEST(AtomMatchesCharTest, Escaped_w) { + EXPECT_FALSE(AtomMatchesChar(true, 'w', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 'w', '+')); + EXPECT_FALSE(AtomMatchesChar(true, 'w', ' ')); + EXPECT_FALSE(AtomMatchesChar(true, 'w', '\n')); + + EXPECT_TRUE(AtomMatchesChar(true, 'w', '0')); + EXPECT_TRUE(AtomMatchesChar(true, 'w', 'b')); + EXPECT_TRUE(AtomMatchesChar(true, 'w', 'C')); + EXPECT_TRUE(AtomMatchesChar(true, 'w', '_')); +} + +TEST(AtomMatchesCharTest, Escaped_W) { + EXPECT_FALSE(AtomMatchesChar(true, 'W', 'A')); + EXPECT_FALSE(AtomMatchesChar(true, 'W', 'b')); + EXPECT_FALSE(AtomMatchesChar(true, 'W', '9')); + EXPECT_FALSE(AtomMatchesChar(true, 'W', '_')); + + EXPECT_TRUE(AtomMatchesChar(true, 'W', '\0')); + EXPECT_TRUE(AtomMatchesChar(true, 'W', '*')); + EXPECT_TRUE(AtomMatchesChar(true, 'W', '\n')); +} + +TEST(AtomMatchesCharTest, EscapedWhiteSpace) { + EXPECT_FALSE(AtomMatchesChar(true, 'f', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 'f', '\n')); + EXPECT_FALSE(AtomMatchesChar(true, 'n', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 'n', '\r')); + EXPECT_FALSE(AtomMatchesChar(true, 'r', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 'r', 'a')); + EXPECT_FALSE(AtomMatchesChar(true, 't', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 't', 't')); + EXPECT_FALSE(AtomMatchesChar(true, 'v', '\0')); + EXPECT_FALSE(AtomMatchesChar(true, 'v', '\f')); + + EXPECT_TRUE(AtomMatchesChar(true, 'f', '\f')); + EXPECT_TRUE(AtomMatchesChar(true, 'n', '\n')); + EXPECT_TRUE(AtomMatchesChar(true, 'r', '\r')); + EXPECT_TRUE(AtomMatchesChar(true, 't', '\t')); + EXPECT_TRUE(AtomMatchesChar(true, 'v', '\v')); +} + +TEST(AtomMatchesCharTest, UnescapedDot) { + EXPECT_FALSE(AtomMatchesChar(false, '.', '\n')); + + EXPECT_TRUE(AtomMatchesChar(false, '.', '\0')); + EXPECT_TRUE(AtomMatchesChar(false, '.', '.')); + EXPECT_TRUE(AtomMatchesChar(false, '.', 'a')); + EXPECT_TRUE(AtomMatchesChar(false, '.', ' ')); +} + +TEST(AtomMatchesCharTest, UnescapedChar) { + EXPECT_FALSE(AtomMatchesChar(false, 'a', '\0')); + EXPECT_FALSE(AtomMatchesChar(false, 'a', 'b')); + EXPECT_FALSE(AtomMatchesChar(false, '$', 'a')); + + EXPECT_TRUE(AtomMatchesChar(false, '$', '$')); + EXPECT_TRUE(AtomMatchesChar(false, '5', '5')); + EXPECT_TRUE(AtomMatchesChar(false, 'Z', 'Z')); +} + +TEST(ValidateRegexTest, GeneratesFailureAndReturnsFalseForInvalid) { + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex(NULL)), + "NULL is not a valid simple regular expression"); + EXPECT_NONFATAL_FAILURE( + ASSERT_FALSE(ValidateRegex("a\\")), + "Syntax error at index 1 in simple regular expression \"a\\\": "); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("a\\")), + "'\\' cannot appear at the end"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("\\n\\")), + "'\\' cannot appear at the end"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("\\s\\hb")), + "invalid escape sequence \"\\h\""); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("^^")), + "'^' can only appear at the beginning"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex(".*^b")), + "'^' can only appear at the beginning"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("$$")), + "'$' can only appear at the end"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("^$a")), + "'$' can only appear at the end"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("a(b")), + "'(' is unsupported"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("ab)")), + "')' is unsupported"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("[ab")), + "'[' is unsupported"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("a{2")), + "'{' is unsupported"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("?")), + "'?' can only follow a repeatable token"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("^*")), + "'*' can only follow a repeatable token"); + EXPECT_NONFATAL_FAILURE(ASSERT_FALSE(ValidateRegex("5*+")), + "'+' can only follow a repeatable token"); +} + +TEST(ValidateRegexTest, ReturnsTrueForValid) { + EXPECT_TRUE(ValidateRegex("")); + EXPECT_TRUE(ValidateRegex("a")); + EXPECT_TRUE(ValidateRegex(".*")); + EXPECT_TRUE(ValidateRegex("^a_+")); + EXPECT_TRUE(ValidateRegex("^a\\t\\&?")); + EXPECT_TRUE(ValidateRegex("09*$")); + EXPECT_TRUE(ValidateRegex("^Z$")); + EXPECT_TRUE(ValidateRegex("a\\^Z\\$\\(\\)\\|\\[\\]\\{\\}")); +} + +TEST(MatchRepetitionAndRegexAtHeadTest, WorksForZeroOrOne) { + EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "a", "ba")); + // Repeating more than once. + EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "b", "aab")); + + // Repeating zero times. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "b", "ba")); + // Repeating once. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, 'a', '?', "b", "ab")); + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '#', '?', ".", "##")); +} + +TEST(MatchRepetitionAndRegexAtHeadTest, WorksForZeroOrMany) { + EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, '.', '*', "a$", "baab")); + + // Repeating zero times. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '.', '*', "b", "bc")); + // Repeating once. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '.', '*', "b", "abc")); + // Repeating more than once. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(true, 'w', '*', "-", "ab_1-g")); +} + +TEST(MatchRepetitionAndRegexAtHeadTest, WorksForOneOrMany) { + EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, '.', '+', "a$", "baab")); + // Repeating zero times. + EXPECT_FALSE(MatchRepetitionAndRegexAtHead(false, '.', '+', "b", "bc")); + + // Repeating once. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(false, '.', '+', "b", "abc")); + // Repeating more than once. + EXPECT_TRUE(MatchRepetitionAndRegexAtHead(true, 'w', '+', "-", "ab_1-g")); +} + +TEST(MatchRegexAtHeadTest, ReturnsTrueForEmptyRegex) { + EXPECT_TRUE(MatchRegexAtHead("", "")); + EXPECT_TRUE(MatchRegexAtHead("", "ab")); +} + +TEST(MatchRegexAtHeadTest, WorksWhenDollarIsInRegex) { + EXPECT_FALSE(MatchRegexAtHead("$", "a")); + + EXPECT_TRUE(MatchRegexAtHead("$", "")); + EXPECT_TRUE(MatchRegexAtHead("a$", "a")); +} + +TEST(MatchRegexAtHeadTest, WorksWhenRegexStartsWithEscapeSequence) { + EXPECT_FALSE(MatchRegexAtHead("\\w", "+")); + EXPECT_FALSE(MatchRegexAtHead("\\W", "ab")); + + EXPECT_TRUE(MatchRegexAtHead("\\sa", "\nab")); + EXPECT_TRUE(MatchRegexAtHead("\\d", "1a")); +} + +TEST(MatchRegexAtHeadTest, WorksWhenRegexStartsWithRepetition) { + EXPECT_FALSE(MatchRegexAtHead(".+a", "abc")); + EXPECT_FALSE(MatchRegexAtHead("a?b", "aab")); + + EXPECT_TRUE(MatchRegexAtHead(".*a", "bc12-ab")); + EXPECT_TRUE(MatchRegexAtHead("a?b", "b")); + EXPECT_TRUE(MatchRegexAtHead("a?b", "ab")); +} + +TEST(MatchRegexAtHeadTest, + WorksWhenRegexStartsWithRepetionOfEscapeSequence) { + EXPECT_FALSE(MatchRegexAtHead("\\.+a", "abc")); + EXPECT_FALSE(MatchRegexAtHead("\\s?b", " b")); + + EXPECT_TRUE(MatchRegexAtHead("\\(*a", "((((ab")); + EXPECT_TRUE(MatchRegexAtHead("\\^?b", "^b")); + EXPECT_TRUE(MatchRegexAtHead("\\\\?b", "b")); + EXPECT_TRUE(MatchRegexAtHead("\\\\?b", "\\b")); +} + +TEST(MatchRegexAtHeadTest, MatchesSequentially) { + EXPECT_FALSE(MatchRegexAtHead("ab.*c", "acabc")); + + EXPECT_TRUE(MatchRegexAtHead("ab.*c", "ab-fsc")); +} + +TEST(MatchRegexAnywhereTest, ReturnsFalseWhenStringIsNull) { + EXPECT_FALSE(MatchRegexAnywhere("", NULL)); +} + +TEST(MatchRegexAnywhereTest, WorksWhenRegexStartsWithCaret) { + EXPECT_FALSE(MatchRegexAnywhere("^a", "ba")); + EXPECT_FALSE(MatchRegexAnywhere("^$", "a")); + + EXPECT_TRUE(MatchRegexAnywhere("^a", "ab")); + EXPECT_TRUE(MatchRegexAnywhere("^", "ab")); + EXPECT_TRUE(MatchRegexAnywhere("^$", "")); +} + +TEST(MatchRegexAnywhereTest, ReturnsFalseWhenNoMatch) { + EXPECT_FALSE(MatchRegexAnywhere("a", "bcde123")); + EXPECT_FALSE(MatchRegexAnywhere("a.+a", "--aa88888888")); +} + +TEST(MatchRegexAnywhereTest, ReturnsTrueWhenMatchingPrefix) { + EXPECT_TRUE(MatchRegexAnywhere("\\w+", "ab1_ - 5")); + EXPECT_TRUE(MatchRegexAnywhere(".*=", "=")); + EXPECT_TRUE(MatchRegexAnywhere("x.*ab?.*bc", "xaaabc")); +} + +TEST(MatchRegexAnywhereTest, ReturnsTrueWhenMatchingNonPrefix) { + EXPECT_TRUE(MatchRegexAnywhere("\\w+", "$$$ ab1_ - 5")); + EXPECT_TRUE(MatchRegexAnywhere("\\.+=", "= ...=")); +} + +// Tests RE's implicit constructors. +TEST(RETest, ImplicitConstructorWorks) { + const RE empty(""); + EXPECT_STREQ("", empty.pattern()); + + const RE simple("hello"); + EXPECT_STREQ("hello", simple.pattern()); +} + +// Tests that RE's constructors reject invalid regular expressions. +TEST(RETest, RejectsInvalidRegex) { + EXPECT_NONFATAL_FAILURE({ + const RE normal(NULL); + }, "NULL is not a valid simple regular expression"); + + EXPECT_NONFATAL_FAILURE({ + const RE normal(".*(\\w+"); + }, "'(' is unsupported"); + + EXPECT_NONFATAL_FAILURE({ + const RE invalid("^?"); + }, "'?' can only follow a repeatable token"); +} + +// Tests RE::FullMatch(). +TEST(RETest, FullMatchWorks) { + const RE empty(""); + EXPECT_TRUE(RE::FullMatch("", empty)); + EXPECT_FALSE(RE::FullMatch("a", empty)); + + const RE re1("a"); + EXPECT_TRUE(RE::FullMatch("a", re1)); + + const RE re("a.*z"); + EXPECT_TRUE(RE::FullMatch("az", re)); + EXPECT_TRUE(RE::FullMatch("axyz", re)); + EXPECT_FALSE(RE::FullMatch("baz", re)); + EXPECT_FALSE(RE::FullMatch("azy", re)); +} + +// Tests RE::PartialMatch(). +TEST(RETest, PartialMatchWorks) { + const RE empty(""); + EXPECT_TRUE(RE::PartialMatch("", empty)); + EXPECT_TRUE(RE::PartialMatch("a", empty)); + + const RE re("a.*z"); + EXPECT_TRUE(RE::PartialMatch("az", re)); + EXPECT_TRUE(RE::PartialMatch("axyz", re)); + EXPECT_TRUE(RE::PartialMatch("baz", re)); + EXPECT_TRUE(RE::PartialMatch("azy", re)); + EXPECT_FALSE(RE::PartialMatch("zza", re)); +} + +#endif // GTEST_USES_POSIX_RE + +#if !GTEST_OS_WINDOWS_MOBILE + +TEST(CaptureTest, CapturesStdout) { + CaptureStdout(); + fprintf(stdout, "abc"); + EXPECT_STREQ("abc", GetCapturedStdout().c_str()); + + CaptureStdout(); + fprintf(stdout, "def%cghi", '\0'); + EXPECT_EQ(::std::string("def\0ghi", 7), ::std::string(GetCapturedStdout())); +} + +TEST(CaptureTest, CapturesStderr) { + CaptureStderr(); + fprintf(stderr, "jkl"); + EXPECT_STREQ("jkl", GetCapturedStderr().c_str()); + + CaptureStderr(); + fprintf(stderr, "jkl%cmno", '\0'); + EXPECT_EQ(::std::string("jkl\0mno", 7), ::std::string(GetCapturedStderr())); +} + +// Tests that stdout and stderr capture don't interfere with each other. +TEST(CaptureTest, CapturesStdoutAndStderr) { + CaptureStdout(); + CaptureStderr(); + fprintf(stdout, "pqr"); + fprintf(stderr, "stu"); + EXPECT_STREQ("pqr", GetCapturedStdout().c_str()); + EXPECT_STREQ("stu", GetCapturedStderr().c_str()); +} + +TEST(CaptureDeathTest, CannotReenterStdoutCapture) { + CaptureStdout(); + EXPECT_DEATH_IF_SUPPORTED(CaptureStdout(), + "Only one stdout capturer can exist at a time"); + GetCapturedStdout(); + + // We cannot test stderr capturing using death tests as they use it + // themselves. +} + +#endif // !GTEST_OS_WINDOWS_MOBILE + +TEST(ThreadLocalTest, DefaultConstructorInitializesToDefaultValues) { + ThreadLocal t1; + EXPECT_EQ(0, t1.get()); + + ThreadLocal t2; + EXPECT_TRUE(t2.get() == NULL); +} + +TEST(ThreadLocalTest, SingleParamConstructorInitializesToParam) { + ThreadLocal t1(123); + EXPECT_EQ(123, t1.get()); + + int i = 0; + ThreadLocal t2(&i); + EXPECT_EQ(&i, t2.get()); +} + +class NoDefaultContructor { + public: + explicit NoDefaultContructor(const char*) {} + NoDefaultContructor(const NoDefaultContructor&) {} +}; + +TEST(ThreadLocalTest, ValueDefaultContructorIsNotRequiredForParamVersion) { + ThreadLocal bar(NoDefaultContructor("foo")); + bar.pointer(); +} + +TEST(ThreadLocalTest, GetAndPointerReturnSameValue) { + ThreadLocal thread_local_string; + + EXPECT_EQ(thread_local_string.pointer(), &(thread_local_string.get())); + + // Verifies the condition still holds after calling set. + thread_local_string.set("foo"); + EXPECT_EQ(thread_local_string.pointer(), &(thread_local_string.get())); +} + +TEST(ThreadLocalTest, PointerAndConstPointerReturnSameValue) { + ThreadLocal thread_local_string; + const ThreadLocal& const_thread_local_string = + thread_local_string; + + EXPECT_EQ(thread_local_string.pointer(), const_thread_local_string.pointer()); + + thread_local_string.set("foo"); + EXPECT_EQ(thread_local_string.pointer(), const_thread_local_string.pointer()); +} + +#if GTEST_IS_THREADSAFE + +void AddTwo(int* param) { *param += 2; } + +TEST(ThreadWithParamTest, ConstructorExecutesThreadFunc) { + int i = 40; + ThreadWithParam thread(&AddTwo, &i, NULL); + thread.Join(); + EXPECT_EQ(42, i); +} + +TEST(MutexDeathTest, AssertHeldShouldAssertWhenNotLocked) { + // AssertHeld() is flaky only in the presence of multiple threads accessing + // the lock. In this case, the test is robust. + EXPECT_DEATH_IF_SUPPORTED({ + Mutex m; + { MutexLock lock(&m); } + m.AssertHeld(); + }, + "thread .*hold"); +} + +TEST(MutexTest, AssertHeldShouldNotAssertWhenLocked) { + Mutex m; + MutexLock lock(&m); + m.AssertHeld(); +} + +class AtomicCounterWithMutex { + public: + explicit AtomicCounterWithMutex(Mutex* mutex) : + value_(0), mutex_(mutex), random_(42) {} + + void Increment() { + MutexLock lock(mutex_); + int temp = value_; + { + // Locking a mutex puts up a memory barrier, preventing reads and + // writes to value_ rearranged when observed from other threads. + // + // We cannot use Mutex and MutexLock here or rely on their memory + // barrier functionality as we are testing them here. + pthread_mutex_t memory_barrier_mutex; + GTEST_CHECK_POSIX_SUCCESS_( + pthread_mutex_init(&memory_barrier_mutex, NULL)); + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&memory_barrier_mutex)); + + SleepMilliseconds(random_.Generate(30)); + + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&memory_barrier_mutex)); + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&memory_barrier_mutex)); + } + value_ = temp + 1; + } + int value() const { return value_; } + + private: + volatile int value_; + Mutex* const mutex_; // Protects value_. + Random random_; +}; + +void CountingThreadFunc(pair param) { + for (int i = 0; i < param.second; ++i) + param.first->Increment(); +} + +// Tests that the mutex only lets one thread at a time to lock it. +TEST(MutexTest, OnlyOneThreadCanLockAtATime) { + Mutex mutex; + AtomicCounterWithMutex locked_counter(&mutex); + + typedef ThreadWithParam > ThreadType; + const int kCycleCount = 20; + const int kThreadCount = 7; + scoped_ptr counting_threads[kThreadCount]; + Notification threads_can_start; + // Creates and runs kThreadCount threads that increment locked_counter + // kCycleCount times each. + for (int i = 0; i < kThreadCount; ++i) { + counting_threads[i].reset(new ThreadType(&CountingThreadFunc, + make_pair(&locked_counter, + kCycleCount), + &threads_can_start)); + } + threads_can_start.Notify(); + for (int i = 0; i < kThreadCount; ++i) + counting_threads[i]->Join(); + + // If the mutex lets more than one thread to increment the counter at a + // time, they are likely to encounter a race condition and have some + // increments overwritten, resulting in the lower then expected counter + // value. + EXPECT_EQ(kCycleCount * kThreadCount, locked_counter.value()); +} + +template +void RunFromThread(void (func)(T), T param) { + ThreadWithParam thread(func, param, NULL); + thread.Join(); +} + +void RetrieveThreadLocalValue( + pair*, std::string*> param) { + *param.second = param.first->get(); +} + +TEST(ThreadLocalTest, ParameterizedConstructorSetsDefault) { + ThreadLocal thread_local_string("foo"); + EXPECT_STREQ("foo", thread_local_string.get().c_str()); + + thread_local_string.set("bar"); + EXPECT_STREQ("bar", thread_local_string.get().c_str()); + + std::string result; + RunFromThread(&RetrieveThreadLocalValue, + make_pair(&thread_local_string, &result)); + EXPECT_STREQ("foo", result.c_str()); +} + +// DestructorTracker keeps track of whether its instances have been +// destroyed. +static std::vector g_destroyed; + +class DestructorTracker { + public: + DestructorTracker() : index_(GetNewIndex()) {} + DestructorTracker(const DestructorTracker& /* rhs */) + : index_(GetNewIndex()) {} + ~DestructorTracker() { + // We never access g_destroyed concurrently, so we don't need to + // protect the write operation under a mutex. + g_destroyed[index_] = true; + } + + private: + static int GetNewIndex() { + g_destroyed.push_back(false); + return g_destroyed.size() - 1; + } + const int index_; +}; + +typedef ThreadLocal* ThreadParam; + +void CallThreadLocalGet(ThreadParam thread_local_param) { + thread_local_param->get(); +} + +// Tests that when a ThreadLocal object dies in a thread, it destroys +// the managed object for that thread. +TEST(ThreadLocalTest, DestroysManagedObjectForOwnThreadWhenDying) { + g_destroyed.clear(); + + { + // The next line default constructs a DestructorTracker object as + // the default value of objects managed by thread_local_tracker. + ThreadLocal thread_local_tracker; + ASSERT_EQ(1U, g_destroyed.size()); + ASSERT_FALSE(g_destroyed[0]); + + // This creates another DestructorTracker object for the main thread. + thread_local_tracker.get(); + ASSERT_EQ(2U, g_destroyed.size()); + ASSERT_FALSE(g_destroyed[0]); + ASSERT_FALSE(g_destroyed[1]); + } + + // Now thread_local_tracker has died. It should have destroyed both the + // default value shared by all threads and the value for the main + // thread. + ASSERT_EQ(2U, g_destroyed.size()); + EXPECT_TRUE(g_destroyed[0]); + EXPECT_TRUE(g_destroyed[1]); + + g_destroyed.clear(); +} + +// Tests that when a thread exits, the thread-local object for that +// thread is destroyed. +TEST(ThreadLocalTest, DestroysManagedObjectAtThreadExit) { + g_destroyed.clear(); + + { + // The next line default constructs a DestructorTracker object as + // the default value of objects managed by thread_local_tracker. + ThreadLocal thread_local_tracker; + ASSERT_EQ(1U, g_destroyed.size()); + ASSERT_FALSE(g_destroyed[0]); + + // This creates another DestructorTracker object in the new thread. + ThreadWithParam thread( + &CallThreadLocalGet, &thread_local_tracker, NULL); + thread.Join(); + + // Now the new thread has exited. The per-thread object for it + // should have been destroyed. + ASSERT_EQ(2U, g_destroyed.size()); + ASSERT_FALSE(g_destroyed[0]); + ASSERT_TRUE(g_destroyed[1]); + } + + // Now thread_local_tracker has died. The default value should have been + // destroyed too. + ASSERT_EQ(2U, g_destroyed.size()); + EXPECT_TRUE(g_destroyed[0]); + EXPECT_TRUE(g_destroyed[1]); + + g_destroyed.clear(); +} + +TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) { + ThreadLocal thread_local_string; + thread_local_string.set("Foo"); + EXPECT_STREQ("Foo", thread_local_string.get().c_str()); + + std::string result; + RunFromThread(&RetrieveThreadLocalValue, + make_pair(&thread_local_string, &result)); + EXPECT_TRUE(result.empty()); +} + +#endif // GTEST_IS_THREADSAFE + +} // namespace internal +} // namespace testing diff --git a/external/gtest/test/gtest-printers_test.cc b/external/gtest/test/gtest-printers_test.cc new file mode 100644 index 0000000000..c2beca7d8d --- /dev/null +++ b/external/gtest/test/gtest-printers_test.cc @@ -0,0 +1,1566 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file tests the universal value printer. + +#include "gtest/gtest-printers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" + +// hash_map and hash_set are available under Visual C++. +#if _MSC_VER +# define GTEST_HAS_HASH_MAP_ 1 // Indicates that hash_map is available. +# include // NOLINT +# define GTEST_HAS_HASH_SET_ 1 // Indicates that hash_set is available. +# include // NOLINT +#endif // GTEST_OS_WINDOWS + +// Some user-defined types for testing the universal value printer. + +// An anonymous enum type. +enum AnonymousEnum { + kAE1 = -1, + kAE2 = 1 +}; + +// An enum without a user-defined printer. +enum EnumWithoutPrinter { + kEWP1 = -2, + kEWP2 = 42 +}; + +// An enum with a << operator. +enum EnumWithStreaming { + kEWS1 = 10 +}; + +std::ostream& operator<<(std::ostream& os, EnumWithStreaming e) { + return os << (e == kEWS1 ? "kEWS1" : "invalid"); +} + +// An enum with a PrintTo() function. +enum EnumWithPrintTo { + kEWPT1 = 1 +}; + +void PrintTo(EnumWithPrintTo e, std::ostream* os) { + *os << (e == kEWPT1 ? "kEWPT1" : "invalid"); +} + +// A class implicitly convertible to BiggestInt. +class BiggestIntConvertible { + public: + operator ::testing::internal::BiggestInt() const { return 42; } +}; + +// A user-defined unprintable class template in the global namespace. +template +class UnprintableTemplateInGlobal { + public: + UnprintableTemplateInGlobal() : value_() {} + private: + T value_; +}; + +// A user-defined streamable type in the global namespace. +class StreamableInGlobal { + public: + virtual ~StreamableInGlobal() {} +}; + +inline void operator<<(::std::ostream& os, const StreamableInGlobal& /* x */) { + os << "StreamableInGlobal"; +} + +void operator<<(::std::ostream& os, const StreamableInGlobal* /* x */) { + os << "StreamableInGlobal*"; +} + +namespace foo { + +// A user-defined unprintable type in a user namespace. +class UnprintableInFoo { + public: + UnprintableInFoo() : z_(0) { memcpy(xy_, "\xEF\x12\x0\x0\x34\xAB\x0\x0", 8); } + private: + char xy_[8]; + double z_; +}; + +// A user-defined printable type in a user-chosen namespace. +struct PrintableViaPrintTo { + PrintableViaPrintTo() : value() {} + int value; +}; + +void PrintTo(const PrintableViaPrintTo& x, ::std::ostream* os) { + *os << "PrintableViaPrintTo: " << x.value; +} + +// A type with a user-defined << for printing its pointer. +struct PointerPrintable { +}; + +::std::ostream& operator<<(::std::ostream& os, + const PointerPrintable* /* x */) { + return os << "PointerPrintable*"; +} + +// A user-defined printable class template in a user-chosen namespace. +template +class PrintableViaPrintToTemplate { + public: + explicit PrintableViaPrintToTemplate(const T& a_value) : value_(a_value) {} + + const T& value() const { return value_; } + private: + T value_; +}; + +template +void PrintTo(const PrintableViaPrintToTemplate& x, ::std::ostream* os) { + *os << "PrintableViaPrintToTemplate: " << x.value(); +} + +// A user-defined streamable class template in a user namespace. +template +class StreamableTemplateInFoo { + public: + StreamableTemplateInFoo() : value_() {} + + const T& value() const { return value_; } + private: + T value_; +}; + +template +inline ::std::ostream& operator<<(::std::ostream& os, + const StreamableTemplateInFoo& x) { + return os << "StreamableTemplateInFoo: " << x.value(); +} + +} // namespace foo + +namespace testing { +namespace gtest_printers_test { + +using ::std::deque; +using ::std::list; +using ::std::make_pair; +using ::std::map; +using ::std::multimap; +using ::std::multiset; +using ::std::pair; +using ::std::set; +using ::std::vector; +using ::testing::PrintToString; +using ::testing::internal::FormatForComparisonFailureMessage; +using ::testing::internal::ImplicitCast_; +using ::testing::internal::NativeArray; +using ::testing::internal::RE; +using ::testing::internal::Strings; +using ::testing::internal::UniversalPrint; +using ::testing::internal::UniversalPrinter; +using ::testing::internal::UniversalTersePrint; +using ::testing::internal::UniversalTersePrintTupleFieldsToStrings; +using ::testing::internal::kReference; +using ::testing::internal::string; + +#if GTEST_HAS_TR1_TUPLE +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +#endif + +// The hash_* classes are not part of the C++ standard. STLport +// defines them in namespace std. MSVC defines them in ::stdext. GCC +// defines them in ::. +#ifdef _STLP_HASH_MAP // We got from STLport. +using ::std::hash_map; +using ::std::hash_set; +using ::std::hash_multimap; +using ::std::hash_multiset; +#elif _MSC_VER +using ::stdext::hash_map; +using ::stdext::hash_set; +using ::stdext::hash_multimap; +using ::stdext::hash_multiset; +#endif + +// Prints a value to a string using the universal value printer. This +// is a helper for testing UniversalPrinter::Print() for various types. +template +string Print(const T& value) { + ::std::stringstream ss; + UniversalPrinter::Print(value, &ss); + return ss.str(); +} + +// Prints a value passed by reference to a string, using the universal +// value printer. This is a helper for testing +// UniversalPrinter::Print() for various types. +template +string PrintByRef(const T& value) { + ::std::stringstream ss; + UniversalPrinter::Print(value, &ss); + return ss.str(); +} + +// Tests printing various enum types. + +TEST(PrintEnumTest, AnonymousEnum) { + EXPECT_EQ("-1", Print(kAE1)); + EXPECT_EQ("1", Print(kAE2)); +} + +TEST(PrintEnumTest, EnumWithoutPrinter) { + EXPECT_EQ("-2", Print(kEWP1)); + EXPECT_EQ("42", Print(kEWP2)); +} + +TEST(PrintEnumTest, EnumWithStreaming) { + EXPECT_EQ("kEWS1", Print(kEWS1)); + EXPECT_EQ("invalid", Print(static_cast(0))); +} + +TEST(PrintEnumTest, EnumWithPrintTo) { + EXPECT_EQ("kEWPT1", Print(kEWPT1)); + EXPECT_EQ("invalid", Print(static_cast(0))); +} + +// Tests printing a class implicitly convertible to BiggestInt. + +TEST(PrintClassTest, BiggestIntConvertible) { + EXPECT_EQ("42", Print(BiggestIntConvertible())); +} + +// Tests printing various char types. + +// char. +TEST(PrintCharTest, PlainChar) { + EXPECT_EQ("'\\0'", Print('\0')); + EXPECT_EQ("'\\'' (39, 0x27)", Print('\'')); + EXPECT_EQ("'\"' (34, 0x22)", Print('"')); + EXPECT_EQ("'?' (63, 0x3F)", Print('?')); + EXPECT_EQ("'\\\\' (92, 0x5C)", Print('\\')); + EXPECT_EQ("'\\a' (7)", Print('\a')); + EXPECT_EQ("'\\b' (8)", Print('\b')); + EXPECT_EQ("'\\f' (12, 0xC)", Print('\f')); + EXPECT_EQ("'\\n' (10, 0xA)", Print('\n')); + EXPECT_EQ("'\\r' (13, 0xD)", Print('\r')); + EXPECT_EQ("'\\t' (9)", Print('\t')); + EXPECT_EQ("'\\v' (11, 0xB)", Print('\v')); + EXPECT_EQ("'\\x7F' (127)", Print('\x7F')); + EXPECT_EQ("'\\xFF' (255)", Print('\xFF')); + EXPECT_EQ("' ' (32, 0x20)", Print(' ')); + EXPECT_EQ("'a' (97, 0x61)", Print('a')); +} + +// signed char. +TEST(PrintCharTest, SignedChar) { + EXPECT_EQ("'\\0'", Print(static_cast('\0'))); + EXPECT_EQ("'\\xCE' (-50)", + Print(static_cast(-50))); +} + +// unsigned char. +TEST(PrintCharTest, UnsignedChar) { + EXPECT_EQ("'\\0'", Print(static_cast('\0'))); + EXPECT_EQ("'b' (98, 0x62)", + Print(static_cast('b'))); +} + +// Tests printing other simple, built-in types. + +// bool. +TEST(PrintBuiltInTypeTest, Bool) { + EXPECT_EQ("false", Print(false)); + EXPECT_EQ("true", Print(true)); +} + +// wchar_t. +TEST(PrintBuiltInTypeTest, Wchar_t) { + EXPECT_EQ("L'\\0'", Print(L'\0')); + EXPECT_EQ("L'\\'' (39, 0x27)", Print(L'\'')); + EXPECT_EQ("L'\"' (34, 0x22)", Print(L'"')); + EXPECT_EQ("L'?' (63, 0x3F)", Print(L'?')); + EXPECT_EQ("L'\\\\' (92, 0x5C)", Print(L'\\')); + EXPECT_EQ("L'\\a' (7)", Print(L'\a')); + EXPECT_EQ("L'\\b' (8)", Print(L'\b')); + EXPECT_EQ("L'\\f' (12, 0xC)", Print(L'\f')); + EXPECT_EQ("L'\\n' (10, 0xA)", Print(L'\n')); + EXPECT_EQ("L'\\r' (13, 0xD)", Print(L'\r')); + EXPECT_EQ("L'\\t' (9)", Print(L'\t')); + EXPECT_EQ("L'\\v' (11, 0xB)", Print(L'\v')); + EXPECT_EQ("L'\\x7F' (127)", Print(L'\x7F')); + EXPECT_EQ("L'\\xFF' (255)", Print(L'\xFF')); + EXPECT_EQ("L' ' (32, 0x20)", Print(L' ')); + EXPECT_EQ("L'a' (97, 0x61)", Print(L'a')); + EXPECT_EQ("L'\\x576' (1398)", Print(static_cast(0x576))); + EXPECT_EQ("L'\\xC74D' (51021)", Print(static_cast(0xC74D))); +} + +// Test that Int64 provides more storage than wchar_t. +TEST(PrintTypeSizeTest, Wchar_t) { + EXPECT_LT(sizeof(wchar_t), sizeof(testing::internal::Int64)); +} + +// Various integer types. +TEST(PrintBuiltInTypeTest, Integer) { + EXPECT_EQ("'\\xFF' (255)", Print(static_cast(255))); // uint8 + EXPECT_EQ("'\\x80' (-128)", Print(static_cast(-128))); // int8 + EXPECT_EQ("65535", Print(USHRT_MAX)); // uint16 + EXPECT_EQ("-32768", Print(SHRT_MIN)); // int16 + EXPECT_EQ("4294967295", Print(UINT_MAX)); // uint32 + EXPECT_EQ("-2147483648", Print(INT_MIN)); // int32 + EXPECT_EQ("18446744073709551615", + Print(static_cast(-1))); // uint64 + EXPECT_EQ("-9223372036854775808", + Print(static_cast(1) << 63)); // int64 +} + +// Size types. +TEST(PrintBuiltInTypeTest, Size_t) { + EXPECT_EQ("1", Print(sizeof('a'))); // size_t. +#if !GTEST_OS_WINDOWS + // Windows has no ssize_t type. + EXPECT_EQ("-2", Print(static_cast(-2))); // ssize_t. +#endif // !GTEST_OS_WINDOWS +} + +// Floating-points. +TEST(PrintBuiltInTypeTest, FloatingPoints) { + EXPECT_EQ("1.5", Print(1.5f)); // float + EXPECT_EQ("-2.5", Print(-2.5)); // double +} + +// Since ::std::stringstream::operator<<(const void *) formats the pointer +// output differently with different compilers, we have to create the expected +// output first and use it as our expectation. +static string PrintPointer(const void *p) { + ::std::stringstream expected_result_stream; + expected_result_stream << p; + return expected_result_stream.str(); +} + +// Tests printing C strings. + +// const char*. +TEST(PrintCStringTest, Const) { + const char* p = "World"; + EXPECT_EQ(PrintPointer(p) + " pointing to \"World\"", Print(p)); +} + +// char*. +TEST(PrintCStringTest, NonConst) { + char p[] = "Hi"; + EXPECT_EQ(PrintPointer(p) + " pointing to \"Hi\"", + Print(static_cast(p))); +} + +// NULL C string. +TEST(PrintCStringTest, Null) { + const char* p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// Tests that C strings are escaped properly. +TEST(PrintCStringTest, EscapesProperly) { + const char* p = "'\"?\\\a\b\f\n\r\t\v\x7F\xFF a"; + EXPECT_EQ(PrintPointer(p) + " pointing to \"'\\\"?\\\\\\a\\b\\f" + "\\n\\r\\t\\v\\x7F\\xFF a\"", + Print(p)); +} + + + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + +// const wchar_t*. +TEST(PrintWideCStringTest, Const) { + const wchar_t* p = L"World"; + EXPECT_EQ(PrintPointer(p) + " pointing to L\"World\"", Print(p)); +} + +// wchar_t*. +TEST(PrintWideCStringTest, NonConst) { + wchar_t p[] = L"Hi"; + EXPECT_EQ(PrintPointer(p) + " pointing to L\"Hi\"", + Print(static_cast(p))); +} + +// NULL wide C string. +TEST(PrintWideCStringTest, Null) { + const wchar_t* p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// Tests that wide C strings are escaped properly. +TEST(PrintWideCStringTest, EscapesProperly) { + const wchar_t s[] = {'\'', '"', '?', '\\', '\a', '\b', '\f', '\n', '\r', + '\t', '\v', 0xD3, 0x576, 0x8D3, 0xC74D, ' ', 'a', '\0'}; + EXPECT_EQ(PrintPointer(s) + " pointing to L\"'\\\"?\\\\\\a\\b\\f" + "\\n\\r\\t\\v\\xD3\\x576\\x8D3\\xC74D a\"", + Print(static_cast(s))); +} +#endif // native wchar_t + +// Tests printing pointers to other char types. + +// signed char*. +TEST(PrintCharPointerTest, SignedChar) { + signed char* p = reinterpret_cast(0x1234); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// const signed char*. +TEST(PrintCharPointerTest, ConstSignedChar) { + signed char* p = reinterpret_cast(0x1234); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// unsigned char*. +TEST(PrintCharPointerTest, UnsignedChar) { + unsigned char* p = reinterpret_cast(0x1234); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// const unsigned char*. +TEST(PrintCharPointerTest, ConstUnsignedChar) { + const unsigned char* p = reinterpret_cast(0x1234); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// Tests printing pointers to simple, built-in types. + +// bool*. +TEST(PrintPointerToBuiltInTypeTest, Bool) { + bool* p = reinterpret_cast(0xABCD); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// void*. +TEST(PrintPointerToBuiltInTypeTest, Void) { + void* p = reinterpret_cast(0xABCD); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// const void*. +TEST(PrintPointerToBuiltInTypeTest, ConstVoid) { + const void* p = reinterpret_cast(0xABCD); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// Tests printing pointers to pointers. +TEST(PrintPointerToPointerTest, IntPointerPointer) { + int** p = reinterpret_cast(0xABCD); + EXPECT_EQ(PrintPointer(p), Print(p)); + p = NULL; + EXPECT_EQ("NULL", Print(p)); +} + +// Tests printing (non-member) function pointers. + +void MyFunction(int /* n */) {} + +TEST(PrintPointerTest, NonMemberFunctionPointer) { + // We cannot directly cast &MyFunction to const void* because the + // standard disallows casting between pointers to functions and + // pointers to objects, and some compilers (e.g. GCC 3.4) enforce + // this limitation. + EXPECT_EQ( + PrintPointer(reinterpret_cast( + reinterpret_cast(&MyFunction))), + Print(&MyFunction)); + int (*p)(bool) = NULL; // NOLINT + EXPECT_EQ("NULL", Print(p)); +} + +// An assertion predicate determining whether a one string is a prefix for +// another. +template +AssertionResult HasPrefix(const StringType& str, const StringType& prefix) { + if (str.find(prefix, 0) == 0) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(prefix[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << begin_string_quote << prefix << "\" is not a prefix of " + << begin_string_quote << str << "\"\n"; +} + +// Tests printing member variable pointers. Although they are called +// pointers, they don't point to a location in the address space. +// Their representation is implementation-defined. Thus they will be +// printed as raw bytes. + +struct Foo { + public: + virtual ~Foo() {} + int MyMethod(char x) { return x + 1; } + virtual char MyVirtualMethod(int /* n */) { return 'a'; } + + int value; +}; + +TEST(PrintPointerTest, MemberVariablePointer) { + EXPECT_TRUE(HasPrefix(Print(&Foo::value), + Print(sizeof(&Foo::value)) + "-byte object ")); + int (Foo::*p) = NULL; // NOLINT + EXPECT_TRUE(HasPrefix(Print(p), + Print(sizeof(p)) + "-byte object ")); +} + +// Tests printing member function pointers. Although they are called +// pointers, they don't point to a location in the address space. +// Their representation is implementation-defined. Thus they will be +// printed as raw bytes. +TEST(PrintPointerTest, MemberFunctionPointer) { + EXPECT_TRUE(HasPrefix(Print(&Foo::MyMethod), + Print(sizeof(&Foo::MyMethod)) + "-byte object ")); + EXPECT_TRUE( + HasPrefix(Print(&Foo::MyVirtualMethod), + Print(sizeof((&Foo::MyVirtualMethod))) + "-byte object ")); + int (Foo::*p)(char) = NULL; // NOLINT + EXPECT_TRUE(HasPrefix(Print(p), + Print(sizeof(p)) + "-byte object ")); +} + +// Tests printing C arrays. + +// The difference between this and Print() is that it ensures that the +// argument is a reference to an array. +template +string PrintArrayHelper(T (&a)[N]) { + return Print(a); +} + +// One-dimensional array. +TEST(PrintArrayTest, OneDimensionalArray) { + int a[5] = { 1, 2, 3, 4, 5 }; + EXPECT_EQ("{ 1, 2, 3, 4, 5 }", PrintArrayHelper(a)); +} + +// Two-dimensional array. +TEST(PrintArrayTest, TwoDimensionalArray) { + int a[2][5] = { + { 1, 2, 3, 4, 5 }, + { 6, 7, 8, 9, 0 } + }; + EXPECT_EQ("{ { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0 } }", PrintArrayHelper(a)); +} + +// Array of const elements. +TEST(PrintArrayTest, ConstArray) { + const bool a[1] = { false }; + EXPECT_EQ("{ false }", PrintArrayHelper(a)); +} + +// char array without terminating NUL. +TEST(PrintArrayTest, CharArrayWithNoTerminatingNul) { + // Array a contains '\0' in the middle and doesn't end with '\0'. + char a[] = { 'H', '\0', 'i' }; + EXPECT_EQ("\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a)); +} + +// const char array with terminating NUL. +TEST(PrintArrayTest, ConstCharArrayWithTerminatingNul) { + const char a[] = "\0Hi"; + EXPECT_EQ("\"\\0Hi\"", PrintArrayHelper(a)); +} + +// const wchar_t array without terminating NUL. +TEST(PrintArrayTest, WCharArrayWithNoTerminatingNul) { + // Array a contains '\0' in the middle and doesn't end with '\0'. + const wchar_t a[] = { L'H', L'\0', L'i' }; + EXPECT_EQ("L\"H\\0i\" (no terminating NUL)", PrintArrayHelper(a)); +} + +// wchar_t array with terminating NUL. +TEST(PrintArrayTest, WConstCharArrayWithTerminatingNul) { + const wchar_t a[] = L"\0Hi"; + EXPECT_EQ("L\"\\0Hi\"", PrintArrayHelper(a)); +} + +// Array of objects. +TEST(PrintArrayTest, ObjectArray) { + string a[3] = { "Hi", "Hello", "Ni hao" }; + EXPECT_EQ("{ \"Hi\", \"Hello\", \"Ni hao\" }", PrintArrayHelper(a)); +} + +// Array with many elements. +TEST(PrintArrayTest, BigArray) { + int a[100] = { 1, 2, 3 }; + EXPECT_EQ("{ 1, 2, 3, 0, 0, 0, 0, 0, ..., 0, 0, 0, 0, 0, 0, 0, 0 }", + PrintArrayHelper(a)); +} + +// Tests printing ::string and ::std::string. + +#if GTEST_HAS_GLOBAL_STRING +// ::string. +TEST(PrintStringTest, StringInGlobalNamespace) { + const char s[] = "'\"?\\\a\b\f\n\0\r\t\v\x7F\xFF a"; + const ::string str(s, sizeof(s)); + EXPECT_EQ("\"'\\\"?\\\\\\a\\b\\f\\n\\0\\r\\t\\v\\x7F\\xFF a\\0\"", + Print(str)); +} +#endif // GTEST_HAS_GLOBAL_STRING + +// ::std::string. +TEST(PrintStringTest, StringInStdNamespace) { + const char s[] = "'\"?\\\a\b\f\n\0\r\t\v\x7F\xFF a"; + const ::std::string str(s, sizeof(s)); + EXPECT_EQ("\"'\\\"?\\\\\\a\\b\\f\\n\\0\\r\\t\\v\\x7F\\xFF a\\0\"", + Print(str)); +} + +TEST(PrintStringTest, StringAmbiguousHex) { + // "\x6BANANA" is ambiguous, it can be interpreted as starting with either of: + // '\x6', '\x6B', or '\x6BA'. + + // a hex escaping sequence following by a decimal digit + EXPECT_EQ("\"0\\x12\" \"3\"", Print(::std::string("0\x12" "3"))); + // a hex escaping sequence following by a hex digit (lower-case) + EXPECT_EQ("\"mm\\x6\" \"bananas\"", Print(::std::string("mm\x6" "bananas"))); + // a hex escaping sequence following by a hex digit (upper-case) + EXPECT_EQ("\"NOM\\x6\" \"BANANA\"", Print(::std::string("NOM\x6" "BANANA"))); + // a hex escaping sequence following by a non-xdigit + EXPECT_EQ("\"!\\x5-!\"", Print(::std::string("!\x5-!"))); +} + +// Tests printing ::wstring and ::std::wstring. + +#if GTEST_HAS_GLOBAL_WSTRING +// ::wstring. +TEST(PrintWideStringTest, StringInGlobalNamespace) { + const wchar_t s[] = L"'\"?\\\a\b\f\n\0\r\t\v\xD3\x576\x8D3\xC74D a"; + const ::wstring str(s, sizeof(s)/sizeof(wchar_t)); + EXPECT_EQ("L\"'\\\"?\\\\\\a\\b\\f\\n\\0\\r\\t\\v" + "\\xD3\\x576\\x8D3\\xC74D a\\0\"", + Print(str)); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +// ::std::wstring. +TEST(PrintWideStringTest, StringInStdNamespace) { + const wchar_t s[] = L"'\"?\\\a\b\f\n\0\r\t\v\xD3\x576\x8D3\xC74D a"; + const ::std::wstring str(s, sizeof(s)/sizeof(wchar_t)); + EXPECT_EQ("L\"'\\\"?\\\\\\a\\b\\f\\n\\0\\r\\t\\v" + "\\xD3\\x576\\x8D3\\xC74D a\\0\"", + Print(str)); +} + +TEST(PrintWideStringTest, StringAmbiguousHex) { + // same for wide strings. + EXPECT_EQ("L\"0\\x12\" L\"3\"", Print(::std::wstring(L"0\x12" L"3"))); + EXPECT_EQ("L\"mm\\x6\" L\"bananas\"", + Print(::std::wstring(L"mm\x6" L"bananas"))); + EXPECT_EQ("L\"NOM\\x6\" L\"BANANA\"", + Print(::std::wstring(L"NOM\x6" L"BANANA"))); + EXPECT_EQ("L\"!\\x5-!\"", Print(::std::wstring(L"!\x5-!"))); +} +#endif // GTEST_HAS_STD_WSTRING + +// Tests printing types that support generic streaming (i.e. streaming +// to std::basic_ostream for any valid Char and +// CharTraits types). + +// Tests printing a non-template type that supports generic streaming. + +class AllowsGenericStreaming {}; + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const AllowsGenericStreaming& /* a */) { + return os << "AllowsGenericStreaming"; +} + +TEST(PrintTypeWithGenericStreamingTest, NonTemplateType) { + AllowsGenericStreaming a; + EXPECT_EQ("AllowsGenericStreaming", Print(a)); +} + +// Tests printing a template type that supports generic streaming. + +template +class AllowsGenericStreamingTemplate {}; + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const AllowsGenericStreamingTemplate& /* a */) { + return os << "AllowsGenericStreamingTemplate"; +} + +TEST(PrintTypeWithGenericStreamingTest, TemplateType) { + AllowsGenericStreamingTemplate a; + EXPECT_EQ("AllowsGenericStreamingTemplate", Print(a)); +} + +// Tests printing a type that supports generic streaming and can be +// implicitly converted to another printable type. + +template +class AllowsGenericStreamingAndImplicitConversionTemplate { + public: + operator bool() const { return false; } +}; + +template +std::basic_ostream& operator<<( + std::basic_ostream& os, + const AllowsGenericStreamingAndImplicitConversionTemplate& /* a */) { + return os << "AllowsGenericStreamingAndImplicitConversionTemplate"; +} + +TEST(PrintTypeWithGenericStreamingTest, TypeImplicitlyConvertible) { + AllowsGenericStreamingAndImplicitConversionTemplate a; + EXPECT_EQ("AllowsGenericStreamingAndImplicitConversionTemplate", Print(a)); +} + +#if GTEST_HAS_STRING_PIECE_ + +// Tests printing StringPiece. + +TEST(PrintStringPieceTest, SimpleStringPiece) { + const StringPiece sp = "Hello"; + EXPECT_EQ("\"Hello\"", Print(sp)); +} + +TEST(PrintStringPieceTest, UnprintableCharacters) { + const char str[] = "NUL (\0) and \r\t"; + const StringPiece sp(str, sizeof(str) - 1); + EXPECT_EQ("\"NUL (\\0) and \\r\\t\"", Print(sp)); +} + +#endif // GTEST_HAS_STRING_PIECE_ + +// Tests printing STL containers. + +TEST(PrintStlContainerTest, EmptyDeque) { + deque empty; + EXPECT_EQ("{}", Print(empty)); +} + +TEST(PrintStlContainerTest, NonEmptyDeque) { + deque non_empty; + non_empty.push_back(1); + non_empty.push_back(3); + EXPECT_EQ("{ 1, 3 }", Print(non_empty)); +} + +#if GTEST_HAS_HASH_MAP_ + +TEST(PrintStlContainerTest, OneElementHashMap) { + hash_map map1; + map1[1] = 'a'; + EXPECT_EQ("{ (1, 'a' (97, 0x61)) }", Print(map1)); +} + +TEST(PrintStlContainerTest, HashMultiMap) { + hash_multimap map1; + map1.insert(make_pair(5, true)); + map1.insert(make_pair(5, false)); + + // Elements of hash_multimap can be printed in any order. + const string result = Print(map1); + EXPECT_TRUE(result == "{ (5, true), (5, false) }" || + result == "{ (5, false), (5, true) }") + << " where Print(map1) returns \"" << result << "\"."; +} + +#endif // GTEST_HAS_HASH_MAP_ + +#if GTEST_HAS_HASH_SET_ + +TEST(PrintStlContainerTest, HashSet) { + hash_set set1; + set1.insert("hello"); + EXPECT_EQ("{ \"hello\" }", Print(set1)); +} + +TEST(PrintStlContainerTest, HashMultiSet) { + const int kSize = 5; + int a[kSize] = { 1, 1, 2, 5, 1 }; + hash_multiset set1(a, a + kSize); + + // Elements of hash_multiset can be printed in any order. + const string result = Print(set1); + const string expected_pattern = "{ d, d, d, d, d }"; // d means a digit. + + // Verifies the result matches the expected pattern; also extracts + // the numbers in the result. + ASSERT_EQ(expected_pattern.length(), result.length()); + std::vector numbers; + for (size_t i = 0; i != result.length(); i++) { + if (expected_pattern[i] == 'd') { + ASSERT_NE(isdigit(static_cast(result[i])), 0); + numbers.push_back(result[i] - '0'); + } else { + EXPECT_EQ(expected_pattern[i], result[i]) << " where result is " + << result; + } + } + + // Makes sure the result contains the right numbers. + std::sort(numbers.begin(), numbers.end()); + std::sort(a, a + kSize); + EXPECT_TRUE(std::equal(a, a + kSize, numbers.begin())); +} + +#endif // GTEST_HAS_HASH_SET_ + +TEST(PrintStlContainerTest, List) { + const string a[] = { + "hello", + "world" + }; + const list strings(a, a + 2); + EXPECT_EQ("{ \"hello\", \"world\" }", Print(strings)); +} + +TEST(PrintStlContainerTest, Map) { + map map1; + map1[1] = true; + map1[5] = false; + map1[3] = true; + EXPECT_EQ("{ (1, true), (3, true), (5, false) }", Print(map1)); +} + +TEST(PrintStlContainerTest, MultiMap) { + multimap map1; + // The make_pair template function would deduce the type as + // pair here, and since the key part in a multimap has to + // be constant, without a templated ctor in the pair class (as in + // libCstd on Solaris), make_pair call would fail to compile as no + // implicit conversion is found. Thus explicit typename is used + // here instead. + map1.insert(pair(true, 0)); + map1.insert(pair(true, 1)); + map1.insert(pair(false, 2)); + EXPECT_EQ("{ (false, 2), (true, 0), (true, 1) }", Print(map1)); +} + +TEST(PrintStlContainerTest, Set) { + const unsigned int a[] = { 3, 0, 5 }; + set set1(a, a + 3); + EXPECT_EQ("{ 0, 3, 5 }", Print(set1)); +} + +TEST(PrintStlContainerTest, MultiSet) { + const int a[] = { 1, 1, 2, 5, 1 }; + multiset set1(a, a + 5); + EXPECT_EQ("{ 1, 1, 1, 2, 5 }", Print(set1)); +} + +TEST(PrintStlContainerTest, Pair) { + pair p(true, 5); + EXPECT_EQ("(true, 5)", Print(p)); +} + +TEST(PrintStlContainerTest, Vector) { + vector v; + v.push_back(1); + v.push_back(2); + EXPECT_EQ("{ 1, 2 }", Print(v)); +} + +TEST(PrintStlContainerTest, LongSequence) { + const int a[100] = { 1, 2, 3 }; + const vector v(a, a + 100); + EXPECT_EQ("{ 1, 2, 3, 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, ... }", Print(v)); +} + +TEST(PrintStlContainerTest, NestedContainer) { + const int a1[] = { 1, 2 }; + const int a2[] = { 3, 4, 5 }; + const list l1(a1, a1 + 2); + const list l2(a2, a2 + 3); + + vector > v; + v.push_back(l1); + v.push_back(l2); + EXPECT_EQ("{ { 1, 2 }, { 3, 4, 5 } }", Print(v)); +} + +TEST(PrintStlContainerTest, OneDimensionalNativeArray) { + const int a[3] = { 1, 2, 3 }; + NativeArray b(a, 3, kReference); + EXPECT_EQ("{ 1, 2, 3 }", Print(b)); +} + +TEST(PrintStlContainerTest, TwoDimensionalNativeArray) { + const int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; + NativeArray b(a, 2, kReference); + EXPECT_EQ("{ { 1, 2, 3 }, { 4, 5, 6 } }", Print(b)); +} + +// Tests that a class named iterator isn't treated as a container. + +struct iterator { + char x; +}; + +TEST(PrintStlContainerTest, Iterator) { + iterator it = {}; + EXPECT_EQ("1-byte object <00>", Print(it)); +} + +// Tests that a class named const_iterator isn't treated as a container. + +struct const_iterator { + char x; +}; + +TEST(PrintStlContainerTest, ConstIterator) { + const_iterator it = {}; + EXPECT_EQ("1-byte object <00>", Print(it)); +} + +#if GTEST_HAS_TR1_TUPLE +// Tests printing tuples. + +// Tuples of various arities. +TEST(PrintTupleTest, VariousSizes) { + tuple<> t0; + EXPECT_EQ("()", Print(t0)); + + tuple t1(5); + EXPECT_EQ("(5)", Print(t1)); + + tuple t2('a', true); + EXPECT_EQ("('a' (97, 0x61), true)", Print(t2)); + + tuple t3(false, 2, 3); + EXPECT_EQ("(false, 2, 3)", Print(t3)); + + tuple t4(false, 2, 3, 4); + EXPECT_EQ("(false, 2, 3, 4)", Print(t4)); + + tuple t5(false, 2, 3, 4, true); + EXPECT_EQ("(false, 2, 3, 4, true)", Print(t5)); + + tuple t6(false, 2, 3, 4, true, 6); + EXPECT_EQ("(false, 2, 3, 4, true, 6)", Print(t6)); + + tuple t7(false, 2, 3, 4, true, 6, 7); + EXPECT_EQ("(false, 2, 3, 4, true, 6, 7)", Print(t7)); + + tuple t8( + false, 2, 3, 4, true, 6, 7, true); + EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true)", Print(t8)); + + tuple t9( + false, 2, 3, 4, true, 6, 7, true, 9); + EXPECT_EQ("(false, 2, 3, 4, true, 6, 7, true, 9)", Print(t9)); + + const char* const str = "8"; + // VC++ 2010's implementation of tuple of C++0x is deficient, requiring + // an explicit type cast of NULL to be used. + tuple + t10(false, 'a', 3, 4, 5, 1.5F, -2.5, str, + ImplicitCast_(NULL), "10"); + EXPECT_EQ("(false, 'a' (97, 0x61), 3, 4, 5, 1.5, -2.5, " + PrintPointer(str) + + " pointing to \"8\", NULL, \"10\")", + Print(t10)); +} + +// Nested tuples. +TEST(PrintTupleTest, NestedTuple) { + tuple, char> nested(make_tuple(5, true), 'a'); + EXPECT_EQ("((5, true), 'a' (97, 0x61))", Print(nested)); +} + +#endif // GTEST_HAS_TR1_TUPLE + +// Tests printing user-defined unprintable types. + +// Unprintable types in the global namespace. +TEST(PrintUnprintableTypeTest, InGlobalNamespace) { + EXPECT_EQ("1-byte object <00>", + Print(UnprintableTemplateInGlobal())); +} + +// Unprintable types in a user namespace. +TEST(PrintUnprintableTypeTest, InUserNamespace) { + EXPECT_EQ("16-byte object ", + Print(::foo::UnprintableInFoo())); +} + +// Unprintable types are that too big to be printed completely. + +struct Big { + Big() { memset(array, 0, sizeof(array)); } + char array[257]; +}; + +TEST(PrintUnpritableTypeTest, BigObject) { + EXPECT_EQ("257-byte object <00-00 00-00 00-00 00-00 00-00 00-00 " + "00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 " + "00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 " + "00-00 00-00 00-00 00-00 00-00 00-00 ... 00-00 00-00 00-00 " + "00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 " + "00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 " + "00-00 00-00 00-00 00-00 00-00 00-00 00-00 00-00 00>", + Print(Big())); +} + +// Tests printing user-defined streamable types. + +// Streamable types in the global namespace. +TEST(PrintStreamableTypeTest, InGlobalNamespace) { + StreamableInGlobal x; + EXPECT_EQ("StreamableInGlobal", Print(x)); + EXPECT_EQ("StreamableInGlobal*", Print(&x)); +} + +// Printable template types in a user namespace. +TEST(PrintStreamableTypeTest, TemplateTypeInUserNamespace) { + EXPECT_EQ("StreamableTemplateInFoo: 0", + Print(::foo::StreamableTemplateInFoo())); +} + +// Tests printing user-defined types that have a PrintTo() function. +TEST(PrintPrintableTypeTest, InUserNamespace) { + EXPECT_EQ("PrintableViaPrintTo: 0", + Print(::foo::PrintableViaPrintTo())); +} + +// Tests printing a pointer to a user-defined type that has a << +// operator for its pointer. +TEST(PrintPrintableTypeTest, PointerInUserNamespace) { + ::foo::PointerPrintable x; + EXPECT_EQ("PointerPrintable*", Print(&x)); +} + +// Tests printing user-defined class template that have a PrintTo() function. +TEST(PrintPrintableTypeTest, TemplateInUserNamespace) { + EXPECT_EQ("PrintableViaPrintToTemplate: 5", + Print(::foo::PrintableViaPrintToTemplate(5))); +} + +#if GTEST_HAS_PROTOBUF_ + +// Tests printing a protocol message. +TEST(PrintProtocolMessageTest, PrintsShortDebugString) { + testing::internal::TestMessage msg; + msg.set_member("yes"); + EXPECT_EQ("", Print(msg)); +} + +// Tests printing a short proto2 message. +TEST(PrintProto2MessageTest, PrintsShortDebugStringWhenItIsShort) { + testing::internal::FooMessage msg; + msg.set_int_field(2); + msg.set_string_field("hello"); + EXPECT_PRED2(RE::FullMatch, Print(msg), + ""); +} + +// Tests printing a long proto2 message. +TEST(PrintProto2MessageTest, PrintsDebugStringWhenItIsLong) { + testing::internal::FooMessage msg; + msg.set_int_field(2); + msg.set_string_field("hello"); + msg.add_names("peter"); + msg.add_names("paul"); + msg.add_names("mary"); + EXPECT_PRED2(RE::FullMatch, Print(msg), + "<\n" + "int_field:\\s*2\n" + "string_field:\\s*\"hello\"\n" + "names:\\s*\"peter\"\n" + "names:\\s*\"paul\"\n" + "names:\\s*\"mary\"\n" + ">"); +} + +#endif // GTEST_HAS_PROTOBUF_ + +// Tests that the universal printer prints both the address and the +// value of a reference. +TEST(PrintReferenceTest, PrintsAddressAndValue) { + int n = 5; + EXPECT_EQ("@" + PrintPointer(&n) + " 5", PrintByRef(n)); + + int a[2][3] = { + { 0, 1, 2 }, + { 3, 4, 5 } + }; + EXPECT_EQ("@" + PrintPointer(a) + " { { 0, 1, 2 }, { 3, 4, 5 } }", + PrintByRef(a)); + + const ::foo::UnprintableInFoo x; + EXPECT_EQ("@" + PrintPointer(&x) + " 16-byte object " + "", + PrintByRef(x)); +} + +// Tests that the universal printer prints a function pointer passed by +// reference. +TEST(PrintReferenceTest, HandlesFunctionPointer) { + void (*fp)(int n) = &MyFunction; + const string fp_pointer_string = + PrintPointer(reinterpret_cast(&fp)); + // We cannot directly cast &MyFunction to const void* because the + // standard disallows casting between pointers to functions and + // pointers to objects, and some compilers (e.g. GCC 3.4) enforce + // this limitation. + const string fp_string = PrintPointer(reinterpret_cast( + reinterpret_cast(fp))); + EXPECT_EQ("@" + fp_pointer_string + " " + fp_string, + PrintByRef(fp)); +} + +// Tests that the universal printer prints a member function pointer +// passed by reference. +TEST(PrintReferenceTest, HandlesMemberFunctionPointer) { + int (Foo::*p)(char ch) = &Foo::MyMethod; + EXPECT_TRUE(HasPrefix( + PrintByRef(p), + "@" + PrintPointer(reinterpret_cast(&p)) + " " + + Print(sizeof(p)) + "-byte object ")); + + char (Foo::*p2)(int n) = &Foo::MyVirtualMethod; + EXPECT_TRUE(HasPrefix( + PrintByRef(p2), + "@" + PrintPointer(reinterpret_cast(&p2)) + " " + + Print(sizeof(p2)) + "-byte object ")); +} + +// Tests that the universal printer prints a member variable pointer +// passed by reference. +TEST(PrintReferenceTest, HandlesMemberVariablePointer) { + int (Foo::*p) = &Foo::value; // NOLINT + EXPECT_TRUE(HasPrefix( + PrintByRef(p), + "@" + PrintPointer(&p) + " " + Print(sizeof(p)) + "-byte object ")); +} + +// Tests that FormatForComparisonFailureMessage(), which is used to print +// an operand in a comparison assertion (e.g. ASSERT_EQ) when the assertion +// fails, formats the operand in the desired way. + +// scalar +TEST(FormatForComparisonFailureMessageTest, WorksForScalar) { + EXPECT_STREQ("123", + FormatForComparisonFailureMessage(123, 124).c_str()); +} + +// non-char pointer +TEST(FormatForComparisonFailureMessageTest, WorksForNonCharPointer) { + int n = 0; + EXPECT_EQ(PrintPointer(&n), + FormatForComparisonFailureMessage(&n, &n).c_str()); +} + +// non-char array +TEST(FormatForComparisonFailureMessageTest, FormatsNonCharArrayAsPointer) { + // In expression 'array == x', 'array' is compared by pointer. + // Therefore we want to print an array operand as a pointer. + int n[] = { 1, 2, 3 }; + EXPECT_EQ(PrintPointer(n), + FormatForComparisonFailureMessage(n, n).c_str()); +} + +// Tests formatting a char pointer when it's compared with another pointer. +// In this case we want to print it as a raw pointer, as the comparision is by +// pointer. + +// char pointer vs pointer +TEST(FormatForComparisonFailureMessageTest, WorksForCharPointerVsPointer) { + // In expression 'p == x', where 'p' and 'x' are (const or not) char + // pointers, the operands are compared by pointer. Therefore we + // want to print 'p' as a pointer instead of a C string (we don't + // even know if it's supposed to point to a valid C string). + + // const char* + const char* s = "hello"; + EXPECT_EQ(PrintPointer(s), + FormatForComparisonFailureMessage(s, s).c_str()); + + // char* + char ch = 'a'; + EXPECT_EQ(PrintPointer(&ch), + FormatForComparisonFailureMessage(&ch, &ch).c_str()); +} + +// wchar_t pointer vs pointer +TEST(FormatForComparisonFailureMessageTest, WorksForWCharPointerVsPointer) { + // In expression 'p == x', where 'p' and 'x' are (const or not) char + // pointers, the operands are compared by pointer. Therefore we + // want to print 'p' as a pointer instead of a wide C string (we don't + // even know if it's supposed to point to a valid wide C string). + + // const wchar_t* + const wchar_t* s = L"hello"; + EXPECT_EQ(PrintPointer(s), + FormatForComparisonFailureMessage(s, s).c_str()); + + // wchar_t* + wchar_t ch = L'a'; + EXPECT_EQ(PrintPointer(&ch), + FormatForComparisonFailureMessage(&ch, &ch).c_str()); +} + +// Tests formatting a char pointer when it's compared to a string object. +// In this case we want to print the char pointer as a C string. + +#if GTEST_HAS_GLOBAL_STRING +// char pointer vs ::string +TEST(FormatForComparisonFailureMessageTest, WorksForCharPointerVsString) { + const char* s = "hello \"world"; + EXPECT_STREQ("\"hello \\\"world\"", // The string content should be escaped. + FormatForComparisonFailureMessage(s, ::string()).c_str()); + + // char* + char str[] = "hi\1"; + char* p = str; + EXPECT_STREQ("\"hi\\x1\"", // The string content should be escaped. + FormatForComparisonFailureMessage(p, ::string()).c_str()); +} +#endif + +// char pointer vs std::string +TEST(FormatForComparisonFailureMessageTest, WorksForCharPointerVsStdString) { + const char* s = "hello \"world"; + EXPECT_STREQ("\"hello \\\"world\"", // The string content should be escaped. + FormatForComparisonFailureMessage(s, ::std::string()).c_str()); + + // char* + char str[] = "hi\1"; + char* p = str; + EXPECT_STREQ("\"hi\\x1\"", // The string content should be escaped. + FormatForComparisonFailureMessage(p, ::std::string()).c_str()); +} + +#if GTEST_HAS_GLOBAL_WSTRING +// wchar_t pointer vs ::wstring +TEST(FormatForComparisonFailureMessageTest, WorksForWCharPointerVsWString) { + const wchar_t* s = L"hi \"world"; + EXPECT_STREQ("L\"hi \\\"world\"", // The string content should be escaped. + FormatForComparisonFailureMessage(s, ::wstring()).c_str()); + + // wchar_t* + wchar_t str[] = L"hi\1"; + wchar_t* p = str; + EXPECT_STREQ("L\"hi\\x1\"", // The string content should be escaped. + FormatForComparisonFailureMessage(p, ::wstring()).c_str()); +} +#endif + +#if GTEST_HAS_STD_WSTRING +// wchar_t pointer vs std::wstring +TEST(FormatForComparisonFailureMessageTest, WorksForWCharPointerVsStdWString) { + const wchar_t* s = L"hi \"world"; + EXPECT_STREQ("L\"hi \\\"world\"", // The string content should be escaped. + FormatForComparisonFailureMessage(s, ::std::wstring()).c_str()); + + // wchar_t* + wchar_t str[] = L"hi\1"; + wchar_t* p = str; + EXPECT_STREQ("L\"hi\\x1\"", // The string content should be escaped. + FormatForComparisonFailureMessage(p, ::std::wstring()).c_str()); +} +#endif + +// Tests formatting a char array when it's compared with a pointer or array. +// In this case we want to print the array as a row pointer, as the comparison +// is by pointer. + +// char array vs pointer +TEST(FormatForComparisonFailureMessageTest, WorksForCharArrayVsPointer) { + char str[] = "hi \"world\""; + char* p = NULL; + EXPECT_EQ(PrintPointer(str), + FormatForComparisonFailureMessage(str, p).c_str()); +} + +// char array vs char array +TEST(FormatForComparisonFailureMessageTest, WorksForCharArrayVsCharArray) { + const char str[] = "hi \"world\""; + EXPECT_EQ(PrintPointer(str), + FormatForComparisonFailureMessage(str, str).c_str()); +} + +// wchar_t array vs pointer +TEST(FormatForComparisonFailureMessageTest, WorksForWCharArrayVsPointer) { + wchar_t str[] = L"hi \"world\""; + wchar_t* p = NULL; + EXPECT_EQ(PrintPointer(str), + FormatForComparisonFailureMessage(str, p).c_str()); +} + +// wchar_t array vs wchar_t array +TEST(FormatForComparisonFailureMessageTest, WorksForWCharArrayVsWCharArray) { + const wchar_t str[] = L"hi \"world\""; + EXPECT_EQ(PrintPointer(str), + FormatForComparisonFailureMessage(str, str).c_str()); +} + +// Tests formatting a char array when it's compared with a string object. +// In this case we want to print the array as a C string. + +#if GTEST_HAS_GLOBAL_STRING +// char array vs string +TEST(FormatForComparisonFailureMessageTest, WorksForCharArrayVsString) { + const char str[] = "hi \"w\0rld\""; + EXPECT_STREQ("\"hi \\\"w\"", // The content should be escaped. + // Embedded NUL terminates the string. + FormatForComparisonFailureMessage(str, ::string()).c_str()); +} +#endif + +// char array vs std::string +TEST(FormatForComparisonFailureMessageTest, WorksForCharArrayVsStdString) { + const char str[] = "hi \"world\""; + EXPECT_STREQ("\"hi \\\"world\\\"\"", // The content should be escaped. + FormatForComparisonFailureMessage(str, ::std::string()).c_str()); +} + +#if GTEST_HAS_GLOBAL_WSTRING +// wchar_t array vs wstring +TEST(FormatForComparisonFailureMessageTest, WorksForWCharArrayVsWString) { + const wchar_t str[] = L"hi \"world\""; + EXPECT_STREQ("L\"hi \\\"world\\\"\"", // The content should be escaped. + FormatForComparisonFailureMessage(str, ::wstring()).c_str()); +} +#endif + +#if GTEST_HAS_STD_WSTRING +// wchar_t array vs std::wstring +TEST(FormatForComparisonFailureMessageTest, WorksForWCharArrayVsStdWString) { + const wchar_t str[] = L"hi \"w\0rld\""; + EXPECT_STREQ( + "L\"hi \\\"w\"", // The content should be escaped. + // Embedded NUL terminates the string. + FormatForComparisonFailureMessage(str, ::std::wstring()).c_str()); +} +#endif + +// Useful for testing PrintToString(). We cannot use EXPECT_EQ() +// there as its implementation uses PrintToString(). The caller must +// ensure that 'value' has no side effect. +#define EXPECT_PRINT_TO_STRING_(value, expected_string) \ + EXPECT_TRUE(PrintToString(value) == (expected_string)) \ + << " where " #value " prints as " << (PrintToString(value)) + +TEST(PrintToStringTest, WorksForScalar) { + EXPECT_PRINT_TO_STRING_(123, "123"); +} + +TEST(PrintToStringTest, WorksForPointerToConstChar) { + const char* p = "hello"; + EXPECT_PRINT_TO_STRING_(p, "\"hello\""); +} + +TEST(PrintToStringTest, WorksForPointerToNonConstChar) { + char s[] = "hello"; + char* p = s; + EXPECT_PRINT_TO_STRING_(p, "\"hello\""); +} + +TEST(PrintToStringTest, EscapesForPointerToConstChar) { + const char* p = "hello\n"; + EXPECT_PRINT_TO_STRING_(p, "\"hello\\n\""); +} + +TEST(PrintToStringTest, EscapesForPointerToNonConstChar) { + char s[] = "hello\1"; + char* p = s; + EXPECT_PRINT_TO_STRING_(p, "\"hello\\x1\""); +} + +TEST(PrintToStringTest, WorksForArray) { + int n[3] = { 1, 2, 3 }; + EXPECT_PRINT_TO_STRING_(n, "{ 1, 2, 3 }"); +} + +TEST(PrintToStringTest, WorksForCharArray) { + char s[] = "hello"; + EXPECT_PRINT_TO_STRING_(s, "\"hello\""); +} + +TEST(PrintToStringTest, WorksForCharArrayWithEmbeddedNul) { + const char str_with_nul[] = "hello\0 world"; + EXPECT_PRINT_TO_STRING_(str_with_nul, "\"hello\\0 world\""); + + char mutable_str_with_nul[] = "hello\0 world"; + EXPECT_PRINT_TO_STRING_(mutable_str_with_nul, "\"hello\\0 world\""); +} + +#undef EXPECT_PRINT_TO_STRING_ + +TEST(UniversalTersePrintTest, WorksForNonReference) { + ::std::stringstream ss; + UniversalTersePrint(123, &ss); + EXPECT_EQ("123", ss.str()); +} + +TEST(UniversalTersePrintTest, WorksForReference) { + const int& n = 123; + ::std::stringstream ss; + UniversalTersePrint(n, &ss); + EXPECT_EQ("123", ss.str()); +} + +TEST(UniversalTersePrintTest, WorksForCString) { + const char* s1 = "abc"; + ::std::stringstream ss1; + UniversalTersePrint(s1, &ss1); + EXPECT_EQ("\"abc\"", ss1.str()); + + char* s2 = const_cast(s1); + ::std::stringstream ss2; + UniversalTersePrint(s2, &ss2); + EXPECT_EQ("\"abc\"", ss2.str()); + + const char* s3 = NULL; + ::std::stringstream ss3; + UniversalTersePrint(s3, &ss3); + EXPECT_EQ("NULL", ss3.str()); +} + +TEST(UniversalPrintTest, WorksForNonReference) { + ::std::stringstream ss; + UniversalPrint(123, &ss); + EXPECT_EQ("123", ss.str()); +} + +TEST(UniversalPrintTest, WorksForReference) { + const int& n = 123; + ::std::stringstream ss; + UniversalPrint(n, &ss); + EXPECT_EQ("123", ss.str()); +} + +TEST(UniversalPrintTest, WorksForCString) { + const char* s1 = "abc"; + ::std::stringstream ss1; + UniversalPrint(s1, &ss1); + EXPECT_EQ(PrintPointer(s1) + " pointing to \"abc\"", string(ss1.str())); + + char* s2 = const_cast(s1); + ::std::stringstream ss2; + UniversalPrint(s2, &ss2); + EXPECT_EQ(PrintPointer(s2) + " pointing to \"abc\"", string(ss2.str())); + + const char* s3 = NULL; + ::std::stringstream ss3; + UniversalPrint(s3, &ss3); + EXPECT_EQ("NULL", ss3.str()); +} + +TEST(UniversalPrintTest, WorksForCharArray) { + const char str[] = "\"Line\0 1\"\nLine 2"; + ::std::stringstream ss1; + UniversalPrint(str, &ss1); + EXPECT_EQ("\"\\\"Line\\0 1\\\"\\nLine 2\"", ss1.str()); + + const char mutable_str[] = "\"Line\0 1\"\nLine 2"; + ::std::stringstream ss2; + UniversalPrint(mutable_str, &ss2); + EXPECT_EQ("\"\\\"Line\\0 1\\\"\\nLine 2\"", ss2.str()); +} + +#if GTEST_HAS_TR1_TUPLE + +TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsEmptyTuple) { + Strings result = UniversalTersePrintTupleFieldsToStrings(make_tuple()); + EXPECT_EQ(0u, result.size()); +} + +TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsOneTuple) { + Strings result = UniversalTersePrintTupleFieldsToStrings(make_tuple(1)); + ASSERT_EQ(1u, result.size()); + EXPECT_EQ("1", result[0]); +} + +TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTwoTuple) { + Strings result = UniversalTersePrintTupleFieldsToStrings(make_tuple(1, 'a')); + ASSERT_EQ(2u, result.size()); + EXPECT_EQ("1", result[0]); + EXPECT_EQ("'a' (97, 0x61)", result[1]); +} + +TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsTersely) { + const int n = 1; + Strings result = UniversalTersePrintTupleFieldsToStrings( + tuple(n, "a")); + ASSERT_EQ(2u, result.size()); + EXPECT_EQ("1", result[0]); + EXPECT_EQ("\"a\"", result[1]); +} + +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace gtest_printers_test +} // namespace testing diff --git a/external/gtest/test/gtest-test-part_test.cc b/external/gtest/test/gtest-test-part_test.cc new file mode 100644 index 0000000000..ca8ba933ae --- /dev/null +++ b/external/gtest/test/gtest-test-part_test.cc @@ -0,0 +1,208 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// + +#include "gtest/gtest-test-part.h" + +#include "gtest/gtest.h" + +using testing::Message; +using testing::Test; +using testing::TestPartResult; +using testing::TestPartResultArray; + +namespace { + +// Tests the TestPartResult class. + +// The test fixture for testing TestPartResult. +class TestPartResultTest : public Test { + protected: + TestPartResultTest() + : r1_(TestPartResult::kSuccess, "foo/bar.cc", 10, "Success!"), + r2_(TestPartResult::kNonFatalFailure, "foo/bar.cc", -1, "Failure!"), + r3_(TestPartResult::kFatalFailure, NULL, -1, "Failure!") {} + + TestPartResult r1_, r2_, r3_; +}; + + +TEST_F(TestPartResultTest, ConstructorWorks) { + Message message; + message << "something is terribly wrong"; + message << static_cast(testing::internal::kStackTraceMarker); + message << "some unimportant stack trace"; + + const TestPartResult result(TestPartResult::kNonFatalFailure, + "some_file.cc", + 42, + message.GetString().c_str()); + + EXPECT_EQ(TestPartResult::kNonFatalFailure, result.type()); + EXPECT_STREQ("some_file.cc", result.file_name()); + EXPECT_EQ(42, result.line_number()); + EXPECT_STREQ(message.GetString().c_str(), result.message()); + EXPECT_STREQ("something is terribly wrong", result.summary()); +} + +TEST_F(TestPartResultTest, ResultAccessorsWork) { + const TestPartResult success(TestPartResult::kSuccess, + "file.cc", + 42, + "message"); + EXPECT_TRUE(success.passed()); + EXPECT_FALSE(success.failed()); + EXPECT_FALSE(success.nonfatally_failed()); + EXPECT_FALSE(success.fatally_failed()); + + const TestPartResult nonfatal_failure(TestPartResult::kNonFatalFailure, + "file.cc", + 42, + "message"); + EXPECT_FALSE(nonfatal_failure.passed()); + EXPECT_TRUE(nonfatal_failure.failed()); + EXPECT_TRUE(nonfatal_failure.nonfatally_failed()); + EXPECT_FALSE(nonfatal_failure.fatally_failed()); + + const TestPartResult fatal_failure(TestPartResult::kFatalFailure, + "file.cc", + 42, + "message"); + EXPECT_FALSE(fatal_failure.passed()); + EXPECT_TRUE(fatal_failure.failed()); + EXPECT_FALSE(fatal_failure.nonfatally_failed()); + EXPECT_TRUE(fatal_failure.fatally_failed()); +} + +// Tests TestPartResult::type(). +TEST_F(TestPartResultTest, type) { + EXPECT_EQ(TestPartResult::kSuccess, r1_.type()); + EXPECT_EQ(TestPartResult::kNonFatalFailure, r2_.type()); + EXPECT_EQ(TestPartResult::kFatalFailure, r3_.type()); +} + +// Tests TestPartResult::file_name(). +TEST_F(TestPartResultTest, file_name) { + EXPECT_STREQ("foo/bar.cc", r1_.file_name()); + EXPECT_STREQ(NULL, r3_.file_name()); +} + +// Tests TestPartResult::line_number(). +TEST_F(TestPartResultTest, line_number) { + EXPECT_EQ(10, r1_.line_number()); + EXPECT_EQ(-1, r2_.line_number()); +} + +// Tests TestPartResult::message(). +TEST_F(TestPartResultTest, message) { + EXPECT_STREQ("Success!", r1_.message()); +} + +// Tests TestPartResult::passed(). +TEST_F(TestPartResultTest, Passed) { + EXPECT_TRUE(r1_.passed()); + EXPECT_FALSE(r2_.passed()); + EXPECT_FALSE(r3_.passed()); +} + +// Tests TestPartResult::failed(). +TEST_F(TestPartResultTest, Failed) { + EXPECT_FALSE(r1_.failed()); + EXPECT_TRUE(r2_.failed()); + EXPECT_TRUE(r3_.failed()); +} + +// Tests TestPartResult::fatally_failed(). +TEST_F(TestPartResultTest, FatallyFailed) { + EXPECT_FALSE(r1_.fatally_failed()); + EXPECT_FALSE(r2_.fatally_failed()); + EXPECT_TRUE(r3_.fatally_failed()); +} + +// Tests TestPartResult::nonfatally_failed(). +TEST_F(TestPartResultTest, NonfatallyFailed) { + EXPECT_FALSE(r1_.nonfatally_failed()); + EXPECT_TRUE(r2_.nonfatally_failed()); + EXPECT_FALSE(r3_.nonfatally_failed()); +} + +// Tests the TestPartResultArray class. + +class TestPartResultArrayTest : public Test { + protected: + TestPartResultArrayTest() + : r1_(TestPartResult::kNonFatalFailure, "foo/bar.cc", -1, "Failure 1"), + r2_(TestPartResult::kFatalFailure, "foo/bar.cc", -1, "Failure 2") {} + + const TestPartResult r1_, r2_; +}; + +// Tests that TestPartResultArray initially has size 0. +TEST_F(TestPartResultArrayTest, InitialSizeIsZero) { + TestPartResultArray results; + EXPECT_EQ(0, results.size()); +} + +// Tests that TestPartResultArray contains the given TestPartResult +// after one Append() operation. +TEST_F(TestPartResultArrayTest, ContainsGivenResultAfterAppend) { + TestPartResultArray results; + results.Append(r1_); + EXPECT_EQ(1, results.size()); + EXPECT_STREQ("Failure 1", results.GetTestPartResult(0).message()); +} + +// Tests that TestPartResultArray contains the given TestPartResults +// after two Append() operations. +TEST_F(TestPartResultArrayTest, ContainsGivenResultsAfterTwoAppends) { + TestPartResultArray results; + results.Append(r1_); + results.Append(r2_); + EXPECT_EQ(2, results.size()); + EXPECT_STREQ("Failure 1", results.GetTestPartResult(0).message()); + EXPECT_STREQ("Failure 2", results.GetTestPartResult(1).message()); +} + +typedef TestPartResultArrayTest TestPartResultArrayDeathTest; + +// Tests that the program dies when GetTestPartResult() is called with +// an invalid index. +TEST_F(TestPartResultArrayDeathTest, DiesWhenIndexIsOutOfBound) { + TestPartResultArray results; + results.Append(r1_); + + EXPECT_DEATH_IF_SUPPORTED(results.GetTestPartResult(-1), ""); + EXPECT_DEATH_IF_SUPPORTED(results.GetTestPartResult(1), ""); +} + +// TODO(mheule@google.com): Add a test for the class HasNewFatalFailureHelper. + +} // namespace diff --git a/external/gtest/test/gtest-tuple_test.cc b/external/gtest/test/gtest-tuple_test.cc new file mode 100644 index 0000000000..bfaa3e0ac4 --- /dev/null +++ b/external/gtest/test/gtest-tuple_test.cc @@ -0,0 +1,320 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/internal/gtest-tuple.h" +#include +#include "gtest/gtest.h" + +namespace { + +using ::std::tr1::get; +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +using ::std::tr1::tuple_element; +using ::std::tr1::tuple_size; +using ::testing::StaticAssertTypeEq; + +// Tests that tuple_element >::type returns TK. +TEST(tuple_element_Test, ReturnsElementType) { + StaticAssertTypeEq >::type>(); + StaticAssertTypeEq >::type>(); + StaticAssertTypeEq >::type>(); +} + +// Tests that tuple_size::value gives the number of fields in tuple +// type T. +TEST(tuple_size_Test, ReturnsNumberOfFields) { + EXPECT_EQ(0, +tuple_size >::value); + EXPECT_EQ(1, +tuple_size >::value); + EXPECT_EQ(1, +tuple_size >::value); + EXPECT_EQ(1, +(tuple_size > >::value)); + EXPECT_EQ(2, +(tuple_size >::value)); + EXPECT_EQ(3, +(tuple_size >::value)); +} + +// Tests comparing a tuple with itself. +TEST(ComparisonTest, ComparesWithSelf) { + const tuple a(5, 'a', false); + + EXPECT_TRUE(a == a); + EXPECT_FALSE(a != a); +} + +// Tests comparing two tuples with the same value. +TEST(ComparisonTest, ComparesEqualTuples) { + const tuple a(5, true), b(5, true); + + EXPECT_TRUE(a == b); + EXPECT_FALSE(a != b); +} + +// Tests comparing two different tuples that have no reference fields. +TEST(ComparisonTest, ComparesUnequalTuplesWithoutReferenceFields) { + typedef tuple FooTuple; + + const FooTuple a(0, 'x'); + const FooTuple b(1, 'a'); + + EXPECT_TRUE(a != b); + EXPECT_FALSE(a == b); + + const FooTuple c(1, 'b'); + + EXPECT_TRUE(b != c); + EXPECT_FALSE(b == c); +} + +// Tests comparing two different tuples that have reference fields. +TEST(ComparisonTest, ComparesUnequalTuplesWithReferenceFields) { + typedef tuple FooTuple; + + int i = 5; + const char ch = 'a'; + const FooTuple a(i, ch); + + int j = 6; + const FooTuple b(j, ch); + + EXPECT_TRUE(a != b); + EXPECT_FALSE(a == b); + + j = 5; + const char ch2 = 'b'; + const FooTuple c(j, ch2); + + EXPECT_TRUE(b != c); + EXPECT_FALSE(b == c); +} + +// Tests that a tuple field with a reference type is an alias of the +// variable it's supposed to reference. +TEST(ReferenceFieldTest, IsAliasOfReferencedVariable) { + int n = 0; + tuple t(true, n); + + n = 1; + EXPECT_EQ(n, get<1>(t)) + << "Changing a underlying variable should update the reference field."; + + // Makes sure that the implementation doesn't do anything funny with + // the & operator for the return type of get<>(). + EXPECT_EQ(&n, &(get<1>(t))) + << "The address of a reference field should equal the address of " + << "the underlying variable."; + + get<1>(t) = 2; + EXPECT_EQ(2, n) + << "Changing a reference field should update the underlying variable."; +} + +// Tests that tuple's default constructor default initializes each field. +// This test needs to compile without generating warnings. +TEST(TupleConstructorTest, DefaultConstructorDefaultInitializesEachField) { + // The TR1 report requires that tuple's default constructor default + // initializes each field, even if it's a primitive type. If the + // implementation forgets to do this, this test will catch it by + // generating warnings about using uninitialized variables (assuming + // a decent compiler). + + tuple<> empty; + + tuple a1, b1; + b1 = a1; + EXPECT_EQ(0, get<0>(b1)); + + tuple a2, b2; + b2 = a2; + EXPECT_EQ(0, get<0>(b2)); + EXPECT_EQ(0.0, get<1>(b2)); + + tuple a3, b3; + b3 = a3; + EXPECT_EQ(0.0, get<0>(b3)); + EXPECT_EQ('\0', get<1>(b3)); + EXPECT_TRUE(get<2>(b3) == NULL); + + tuple a10, b10; + b10 = a10; + EXPECT_EQ(0, get<0>(b10)); + EXPECT_EQ(0, get<1>(b10)); + EXPECT_EQ(0, get<2>(b10)); + EXPECT_EQ(0, get<3>(b10)); + EXPECT_EQ(0, get<4>(b10)); + EXPECT_EQ(0, get<5>(b10)); + EXPECT_EQ(0, get<6>(b10)); + EXPECT_EQ(0, get<7>(b10)); + EXPECT_EQ(0, get<8>(b10)); + EXPECT_EQ(0, get<9>(b10)); +} + +// Tests constructing a tuple from its fields. +TEST(TupleConstructorTest, ConstructsFromFields) { + int n = 1; + // Reference field. + tuple a(n); + EXPECT_EQ(&n, &(get<0>(a))); + + // Non-reference fields. + tuple b(5, 'a'); + EXPECT_EQ(5, get<0>(b)); + EXPECT_EQ('a', get<1>(b)); + + // Const reference field. + const int m = 2; + tuple c(true, m); + EXPECT_TRUE(get<0>(c)); + EXPECT_EQ(&m, &(get<1>(c))); +} + +// Tests tuple's copy constructor. +TEST(TupleConstructorTest, CopyConstructor) { + tuple a(0.0, true); + tuple b(a); + + EXPECT_DOUBLE_EQ(0.0, get<0>(b)); + EXPECT_TRUE(get<1>(b)); +} + +// Tests constructing a tuple from another tuple that has a compatible +// but different type. +TEST(TupleConstructorTest, ConstructsFromDifferentTupleType) { + tuple a(0, 1, 'a'); + tuple b(a); + + EXPECT_DOUBLE_EQ(0.0, get<0>(b)); + EXPECT_EQ(1, get<1>(b)); + EXPECT_EQ('a', get<2>(b)); +} + +// Tests constructing a 2-tuple from an std::pair. +TEST(TupleConstructorTest, ConstructsFromPair) { + ::std::pair a(1, 'a'); + tuple b(a); + tuple c(a); +} + +// Tests assigning a tuple to another tuple with the same type. +TEST(TupleAssignmentTest, AssignsToSameTupleType) { + const tuple a(5, 7L); + tuple b; + b = a; + EXPECT_EQ(5, get<0>(b)); + EXPECT_EQ(7L, get<1>(b)); +} + +// Tests assigning a tuple to another tuple with a different but +// compatible type. +TEST(TupleAssignmentTest, AssignsToDifferentTupleType) { + const tuple a(1, 7L, true); + tuple b; + b = a; + EXPECT_EQ(1L, get<0>(b)); + EXPECT_EQ(7, get<1>(b)); + EXPECT_TRUE(get<2>(b)); +} + +// Tests assigning an std::pair to a 2-tuple. +TEST(TupleAssignmentTest, AssignsFromPair) { + const ::std::pair a(5, true); + tuple b; + b = a; + EXPECT_EQ(5, get<0>(b)); + EXPECT_TRUE(get<1>(b)); + + tuple c; + c = a; + EXPECT_EQ(5L, get<0>(c)); + EXPECT_TRUE(get<1>(c)); +} + +// A fixture for testing big tuples. +class BigTupleTest : public testing::Test { + protected: + typedef tuple BigTuple; + + BigTupleTest() : + a_(1, 0, 0, 0, 0, 0, 0, 0, 0, 2), + b_(1, 0, 0, 0, 0, 0, 0, 0, 0, 3) {} + + BigTuple a_, b_; +}; + +// Tests constructing big tuples. +TEST_F(BigTupleTest, Construction) { + BigTuple a; + BigTuple b(b_); +} + +// Tests that get(t) returns the N-th (0-based) field of tuple t. +TEST_F(BigTupleTest, get) { + EXPECT_EQ(1, get<0>(a_)); + EXPECT_EQ(2, get<9>(a_)); + + // Tests that get() works on a const tuple too. + const BigTuple a(a_); + EXPECT_EQ(1, get<0>(a)); + EXPECT_EQ(2, get<9>(a)); +} + +// Tests comparing big tuples. +TEST_F(BigTupleTest, Comparisons) { + EXPECT_TRUE(a_ == a_); + EXPECT_FALSE(a_ != a_); + + EXPECT_TRUE(a_ != b_); + EXPECT_FALSE(a_ == b_); +} + +TEST(MakeTupleTest, WorksForScalarTypes) { + tuple a; + a = make_tuple(true, 5); + EXPECT_TRUE(get<0>(a)); + EXPECT_EQ(5, get<1>(a)); + + tuple b; + b = make_tuple('a', 'b', 5); + EXPECT_EQ('a', get<0>(b)); + EXPECT_EQ('b', get<1>(b)); + EXPECT_EQ(5, get<2>(b)); +} + +TEST(MakeTupleTest, WorksForPointers) { + int a[] = { 1, 2, 3, 4 }; + const char* const str = "hi"; + int* const p = a; + + tuple t; + t = make_tuple(str, p); + EXPECT_EQ(str, get<0>(t)); + EXPECT_EQ(p, get<1>(t)); +} + +} // namespace diff --git a/external/gtest/test/gtest-typed-test2_test.cc b/external/gtest/test/gtest-typed-test2_test.cc new file mode 100644 index 0000000000..c284700b02 --- /dev/null +++ b/external/gtest/test/gtest-typed-test2_test.cc @@ -0,0 +1,45 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include + +#include "test/gtest-typed-test_test.h" +#include "gtest/gtest.h" + +#if GTEST_HAS_TYPED_TEST_P + +// Tests that the same type-parameterized test case can be +// instantiated in different translation units linked together. +// (ContainerTest is also instantiated in gtest-typed-test_test.cc.) +INSTANTIATE_TYPED_TEST_CASE_P(Vector, ContainerTest, + testing::Types >); + +#endif // GTEST_HAS_TYPED_TEST_P diff --git a/external/gtest/test/gtest-typed-test_test.cc b/external/gtest/test/gtest-typed-test_test.cc new file mode 100644 index 0000000000..dd4ba43bc2 --- /dev/null +++ b/external/gtest/test/gtest-typed-test_test.cc @@ -0,0 +1,360 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include +#include + +#include "test/gtest-typed-test_test.h" +#include "gtest/gtest.h" + +using testing::Test; + +// Used for testing that SetUpTestCase()/TearDownTestCase(), fixture +// ctor/dtor, and SetUp()/TearDown() work correctly in typed tests and +// type-parameterized test. +template +class CommonTest : public Test { + // For some technical reason, SetUpTestCase() and TearDownTestCase() + // must be public. + public: + static void SetUpTestCase() { + shared_ = new T(5); + } + + static void TearDownTestCase() { + delete shared_; + shared_ = NULL; + } + + // This 'protected:' is optional. There's no harm in making all + // members of this fixture class template public. + protected: + // We used to use std::list here, but switched to std::vector since + // MSVC's doesn't compile cleanly with /W4. + typedef std::vector Vector; + typedef std::set IntSet; + + CommonTest() : value_(1) {} + + virtual ~CommonTest() { EXPECT_EQ(3, value_); } + + virtual void SetUp() { + EXPECT_EQ(1, value_); + value_++; + } + + virtual void TearDown() { + EXPECT_EQ(2, value_); + value_++; + } + + T value_; + static T* shared_; +}; + +template +T* CommonTest::shared_ = NULL; + +// This #ifdef block tests typed tests. +#if GTEST_HAS_TYPED_TEST + +using testing::Types; + +// Tests that SetUpTestCase()/TearDownTestCase(), fixture ctor/dtor, +// and SetUp()/TearDown() work correctly in typed tests + +typedef Types TwoTypes; +TYPED_TEST_CASE(CommonTest, TwoTypes); + +TYPED_TEST(CommonTest, ValuesAreCorrect) { + // Static members of the fixture class template can be visited via + // the TestFixture:: prefix. + EXPECT_EQ(5, *TestFixture::shared_); + + // Typedefs in the fixture class template can be visited via the + // "typename TestFixture::" prefix. + typename TestFixture::Vector empty; + EXPECT_EQ(0U, empty.size()); + + typename TestFixture::IntSet empty2; + EXPECT_EQ(0U, empty2.size()); + + // Non-static members of the fixture class must be visited via + // 'this', as required by C++ for class templates. + EXPECT_EQ(2, this->value_); +} + +// The second test makes sure shared_ is not deleted after the first +// test. +TYPED_TEST(CommonTest, ValuesAreStillCorrect) { + // Static members of the fixture class template can also be visited + // via 'this'. + ASSERT_TRUE(this->shared_ != NULL); + EXPECT_EQ(5, *this->shared_); + + // TypeParam can be used to refer to the type parameter. + EXPECT_EQ(static_cast(2), this->value_); +} + +// Tests that multiple TYPED_TEST_CASE's can be defined in the same +// translation unit. + +template +class TypedTest1 : public Test { +}; + +// Verifies that the second argument of TYPED_TEST_CASE can be a +// single type. +TYPED_TEST_CASE(TypedTest1, int); +TYPED_TEST(TypedTest1, A) {} + +template +class TypedTest2 : public Test { +}; + +// Verifies that the second argument of TYPED_TEST_CASE can be a +// Types<...> type list. +TYPED_TEST_CASE(TypedTest2, Types); + +// This also verifies that tests from different typed test cases can +// share the same name. +TYPED_TEST(TypedTest2, A) {} + +// Tests that a typed test case can be defined in a namespace. + +namespace library1 { + +template +class NumericTest : public Test { +}; + +typedef Types NumericTypes; +TYPED_TEST_CASE(NumericTest, NumericTypes); + +TYPED_TEST(NumericTest, DefaultIsZero) { + EXPECT_EQ(0, TypeParam()); +} + +} // namespace library1 + +#endif // GTEST_HAS_TYPED_TEST + +// This #ifdef block tests type-parameterized tests. +#if GTEST_HAS_TYPED_TEST_P + +using testing::Types; +using testing::internal::TypedTestCasePState; + +// Tests TypedTestCasePState. + +class TypedTestCasePStateTest : public Test { + protected: + virtual void SetUp() { + state_.AddTestName("foo.cc", 0, "FooTest", "A"); + state_.AddTestName("foo.cc", 0, "FooTest", "B"); + state_.AddTestName("foo.cc", 0, "FooTest", "C"); + } + + TypedTestCasePState state_; +}; + +TEST_F(TypedTestCasePStateTest, SucceedsForMatchingList) { + const char* tests = "A, B, C"; + EXPECT_EQ(tests, + state_.VerifyRegisteredTestNames("foo.cc", 1, tests)); +} + +// Makes sure that the order of the tests and spaces around the names +// don't matter. +TEST_F(TypedTestCasePStateTest, IgnoresOrderAndSpaces) { + const char* tests = "A,C, B"; + EXPECT_EQ(tests, + state_.VerifyRegisteredTestNames("foo.cc", 1, tests)); +} + +typedef TypedTestCasePStateTest TypedTestCasePStateDeathTest; + +TEST_F(TypedTestCasePStateDeathTest, DetectsDuplicates) { + EXPECT_DEATH_IF_SUPPORTED( + state_.VerifyRegisteredTestNames("foo.cc", 1, "A, B, A, C"), + "foo\\.cc.1.?: Test A is listed more than once\\."); +} + +TEST_F(TypedTestCasePStateDeathTest, DetectsExtraTest) { + EXPECT_DEATH_IF_SUPPORTED( + state_.VerifyRegisteredTestNames("foo.cc", 1, "A, B, C, D"), + "foo\\.cc.1.?: No test named D can be found in this test case\\."); +} + +TEST_F(TypedTestCasePStateDeathTest, DetectsMissedTest) { + EXPECT_DEATH_IF_SUPPORTED( + state_.VerifyRegisteredTestNames("foo.cc", 1, "A, C"), + "foo\\.cc.1.?: You forgot to list test B\\."); +} + +// Tests that defining a test for a parameterized test case generates +// a run-time error if the test case has been registered. +TEST_F(TypedTestCasePStateDeathTest, DetectsTestAfterRegistration) { + state_.VerifyRegisteredTestNames("foo.cc", 1, "A, B, C"); + EXPECT_DEATH_IF_SUPPORTED( + state_.AddTestName("foo.cc", 2, "FooTest", "D"), + "foo\\.cc.2.?: Test D must be defined before REGISTER_TYPED_TEST_CASE_P" + "\\(FooTest, \\.\\.\\.\\)\\."); +} + +// Tests that SetUpTestCase()/TearDownTestCase(), fixture ctor/dtor, +// and SetUp()/TearDown() work correctly in type-parameterized tests. + +template +class DerivedTest : public CommonTest { +}; + +TYPED_TEST_CASE_P(DerivedTest); + +TYPED_TEST_P(DerivedTest, ValuesAreCorrect) { + // Static members of the fixture class template can be visited via + // the TestFixture:: prefix. + EXPECT_EQ(5, *TestFixture::shared_); + + // Non-static members of the fixture class must be visited via + // 'this', as required by C++ for class templates. + EXPECT_EQ(2, this->value_); +} + +// The second test makes sure shared_ is not deleted after the first +// test. +TYPED_TEST_P(DerivedTest, ValuesAreStillCorrect) { + // Static members of the fixture class template can also be visited + // via 'this'. + ASSERT_TRUE(this->shared_ != NULL); + EXPECT_EQ(5, *this->shared_); + EXPECT_EQ(2, this->value_); +} + +REGISTER_TYPED_TEST_CASE_P(DerivedTest, + ValuesAreCorrect, ValuesAreStillCorrect); + +typedef Types MyTwoTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, DerivedTest, MyTwoTypes); + +// Tests that multiple TYPED_TEST_CASE_P's can be defined in the same +// translation unit. + +template +class TypedTestP1 : public Test { +}; + +TYPED_TEST_CASE_P(TypedTestP1); + +// For testing that the code between TYPED_TEST_CASE_P() and +// TYPED_TEST_P() is not enclosed in a namespace. +typedef int IntAfterTypedTestCaseP; + +TYPED_TEST_P(TypedTestP1, A) {} +TYPED_TEST_P(TypedTestP1, B) {} + +// For testing that the code between TYPED_TEST_P() and +// REGISTER_TYPED_TEST_CASE_P() is not enclosed in a namespace. +typedef int IntBeforeRegisterTypedTestCaseP; + +REGISTER_TYPED_TEST_CASE_P(TypedTestP1, A, B); + +template +class TypedTestP2 : public Test { +}; + +TYPED_TEST_CASE_P(TypedTestP2); + +// This also verifies that tests from different type-parameterized +// test cases can share the same name. +TYPED_TEST_P(TypedTestP2, A) {} + +REGISTER_TYPED_TEST_CASE_P(TypedTestP2, A); + +// Verifies that the code between TYPED_TEST_CASE_P() and +// REGISTER_TYPED_TEST_CASE_P() is not enclosed in a namespace. +IntAfterTypedTestCaseP after = 0; +IntBeforeRegisterTypedTestCaseP before = 0; + +// Verifies that the last argument of INSTANTIATE_TYPED_TEST_CASE_P() +// can be either a single type or a Types<...> type list. +INSTANTIATE_TYPED_TEST_CASE_P(Int, TypedTestP1, int); +INSTANTIATE_TYPED_TEST_CASE_P(Int, TypedTestP2, Types); + +// Tests that the same type-parameterized test case can be +// instantiated more than once in the same translation unit. +INSTANTIATE_TYPED_TEST_CASE_P(Double, TypedTestP2, Types); + +// Tests that the same type-parameterized test case can be +// instantiated in different translation units linked together. +// (ContainerTest is also instantiated in gtest-typed-test_test.cc.) +typedef Types, std::set > MyContainers; +INSTANTIATE_TYPED_TEST_CASE_P(My, ContainerTest, MyContainers); + +// Tests that a type-parameterized test case can be defined and +// instantiated in a namespace. + +namespace library2 { + +template +class NumericTest : public Test { +}; + +TYPED_TEST_CASE_P(NumericTest); + +TYPED_TEST_P(NumericTest, DefaultIsZero) { + EXPECT_EQ(0, TypeParam()); +} + +TYPED_TEST_P(NumericTest, ZeroIsLessThanOne) { + EXPECT_LT(TypeParam(0), TypeParam(1)); +} + +REGISTER_TYPED_TEST_CASE_P(NumericTest, + DefaultIsZero, ZeroIsLessThanOne); +typedef Types NumericTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, NumericTest, NumericTypes); + +} // namespace library2 + +#endif // GTEST_HAS_TYPED_TEST_P + +#if !defined(GTEST_HAS_TYPED_TEST) && !defined(GTEST_HAS_TYPED_TEST_P) + +// Google Test may not support type-parameterized tests with some +// compilers. If we use conditional compilation to compile out all +// code referring to the gtest_main library, MSVC linker will not link +// that library at all and consequently complain about missing entry +// point defined in that library (fatal error LNK1561: entry point +// must be defined). This dummy test keeps gtest_main linked in. +TEST(DummyTest, TypedTestsAreNotSupportedOnThisPlatform) {} + +#endif // #if !defined(GTEST_HAS_TYPED_TEST) && !defined(GTEST_HAS_TYPED_TEST_P) diff --git a/external/gtest/test/gtest-typed-test_test.h b/external/gtest/test/gtest-typed-test_test.h new file mode 100644 index 0000000000..41d75704cf --- /dev/null +++ b/external/gtest/test/gtest-typed-test_test.h @@ -0,0 +1,66 @@ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_TEST_GTEST_TYPED_TEST_TEST_H_ +#define GTEST_TEST_GTEST_TYPED_TEST_TEST_H_ + +#include "gtest/gtest.h" + +#if GTEST_HAS_TYPED_TEST_P + +using testing::Test; + +// For testing that the same type-parameterized test case can be +// instantiated in different translation units linked together. +// ContainerTest will be instantiated in both gtest-typed-test_test.cc +// and gtest-typed-test2_test.cc. + +template +class ContainerTest : public Test { +}; + +TYPED_TEST_CASE_P(ContainerTest); + +TYPED_TEST_P(ContainerTest, CanBeDefaultConstructed) { + TypeParam container; +} + +TYPED_TEST_P(ContainerTest, InitialSizeIsZero) { + TypeParam container; + EXPECT_EQ(0U, container.size()); +} + +REGISTER_TYPED_TEST_CASE_P(ContainerTest, + CanBeDefaultConstructed, InitialSizeIsZero); + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_TEST_GTEST_TYPED_TEST_TEST_H_ diff --git a/external/gtest/test/gtest-unittest-api_test.cc b/external/gtest/test/gtest-unittest-api_test.cc new file mode 100644 index 0000000000..07083e51b5 --- /dev/null +++ b/external/gtest/test/gtest-unittest-api_test.cc @@ -0,0 +1,341 @@ +// Copyright 2009 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) +// +// The Google C++ Testing Framework (Google Test) +// +// This file contains tests verifying correctness of data provided via +// UnitTest's public methods. + +#include "gtest/gtest.h" + +#include // For strcmp. +#include + +using ::testing::InitGoogleTest; + +namespace testing { +namespace internal { + +template +struct LessByName { + bool operator()(const T* a, const T* b) { + return strcmp(a->name(), b->name()) < 0; + } +}; + +class UnitTestHelper { + public: + // Returns the array of pointers to all test cases sorted by the test case + // name. The caller is responsible for deleting the array. + static TestCase const** const GetSortedTestCases() { + UnitTest& unit_test = *UnitTest::GetInstance(); + TestCase const** const test_cases = + new const TestCase*[unit_test.total_test_case_count()]; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) + test_cases[i] = unit_test.GetTestCase(i); + + std::sort(test_cases, + test_cases + unit_test.total_test_case_count(), + LessByName()); + return test_cases; + } + + // Returns the test case by its name. The caller doesn't own the returned + // pointer. + static const TestCase* FindTestCase(const char* name) { + UnitTest& unit_test = *UnitTest::GetInstance(); + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase* test_case = unit_test.GetTestCase(i); + if (0 == strcmp(test_case->name(), name)) + return test_case; + } + return NULL; + } + + // Returns the array of pointers to all tests in a particular test case + // sorted by the test name. The caller is responsible for deleting the + // array. + static TestInfo const** const GetSortedTests(const TestCase* test_case) { + TestInfo const** const tests = + new const TestInfo*[test_case->total_test_count()]; + + for (int i = 0; i < test_case->total_test_count(); ++i) + tests[i] = test_case->GetTestInfo(i); + + std::sort(tests, tests + test_case->total_test_count(), + LessByName()); + return tests; + } +}; + +#if GTEST_HAS_TYPED_TEST +template class TestCaseWithCommentTest : public Test {}; +TYPED_TEST_CASE(TestCaseWithCommentTest, Types); +TYPED_TEST(TestCaseWithCommentTest, Dummy) {} + +const int kTypedTestCases = 1; +const int kTypedTests = 1; +#else +const int kTypedTestCases = 0; +const int kTypedTests = 0; +#endif // GTEST_HAS_TYPED_TEST + +// We can only test the accessors that do not change value while tests run. +// Since tests can be run in any order, the values the accessors that track +// test execution (such as failed_test_count) can not be predicted. +TEST(ApiTest, UnitTestImmutableAccessorsWork) { + UnitTest* unit_test = UnitTest::GetInstance(); + + ASSERT_EQ(2 + kTypedTestCases, unit_test->total_test_case_count()); + EXPECT_EQ(1 + kTypedTestCases, unit_test->test_case_to_run_count()); + EXPECT_EQ(2, unit_test->disabled_test_count()); + EXPECT_EQ(5 + kTypedTests, unit_test->total_test_count()); + EXPECT_EQ(3 + kTypedTests, unit_test->test_to_run_count()); + + const TestCase** const test_cases = UnitTestHelper::GetSortedTestCases(); + + EXPECT_STREQ("ApiTest", test_cases[0]->name()); + EXPECT_STREQ("DISABLED_Test", test_cases[1]->name()); +#if GTEST_HAS_TYPED_TEST + EXPECT_STREQ("TestCaseWithCommentTest/0", test_cases[2]->name()); +#endif // GTEST_HAS_TYPED_TEST + + delete[] test_cases; + + // The following lines initiate actions to verify certain methods in + // FinalSuccessChecker::TearDown. + + // Records a test property to verify TestResult::GetTestProperty(). + RecordProperty("key", "value"); +} + +AssertionResult IsNull(const char* str) { + if (str != NULL) { + return testing::AssertionFailure() << "argument is " << str; + } + return AssertionSuccess(); +} + +TEST(ApiTest, TestCaseImmutableAccessorsWork) { + const TestCase* test_case = UnitTestHelper::FindTestCase("ApiTest"); + ASSERT_TRUE(test_case != NULL); + + EXPECT_STREQ("ApiTest", test_case->name()); + EXPECT_TRUE(IsNull(test_case->type_param())); + EXPECT_TRUE(test_case->should_run()); + EXPECT_EQ(1, test_case->disabled_test_count()); + EXPECT_EQ(3, test_case->test_to_run_count()); + ASSERT_EQ(4, test_case->total_test_count()); + + const TestInfo** tests = UnitTestHelper::GetSortedTests(test_case); + + EXPECT_STREQ("DISABLED_Dummy1", tests[0]->name()); + EXPECT_STREQ("ApiTest", tests[0]->test_case_name()); + EXPECT_TRUE(IsNull(tests[0]->value_param())); + EXPECT_TRUE(IsNull(tests[0]->type_param())); + EXPECT_FALSE(tests[0]->should_run()); + + EXPECT_STREQ("TestCaseDisabledAccessorsWork", tests[1]->name()); + EXPECT_STREQ("ApiTest", tests[1]->test_case_name()); + EXPECT_TRUE(IsNull(tests[1]->value_param())); + EXPECT_TRUE(IsNull(tests[1]->type_param())); + EXPECT_TRUE(tests[1]->should_run()); + + EXPECT_STREQ("TestCaseImmutableAccessorsWork", tests[2]->name()); + EXPECT_STREQ("ApiTest", tests[2]->test_case_name()); + EXPECT_TRUE(IsNull(tests[2]->value_param())); + EXPECT_TRUE(IsNull(tests[2]->type_param())); + EXPECT_TRUE(tests[2]->should_run()); + + EXPECT_STREQ("UnitTestImmutableAccessorsWork", tests[3]->name()); + EXPECT_STREQ("ApiTest", tests[3]->test_case_name()); + EXPECT_TRUE(IsNull(tests[3]->value_param())); + EXPECT_TRUE(IsNull(tests[3]->type_param())); + EXPECT_TRUE(tests[3]->should_run()); + + delete[] tests; + tests = NULL; + +#if GTEST_HAS_TYPED_TEST + test_case = UnitTestHelper::FindTestCase("TestCaseWithCommentTest/0"); + ASSERT_TRUE(test_case != NULL); + + EXPECT_STREQ("TestCaseWithCommentTest/0", test_case->name()); + EXPECT_STREQ(GetTypeName().c_str(), test_case->type_param()); + EXPECT_TRUE(test_case->should_run()); + EXPECT_EQ(0, test_case->disabled_test_count()); + EXPECT_EQ(1, test_case->test_to_run_count()); + ASSERT_EQ(1, test_case->total_test_count()); + + tests = UnitTestHelper::GetSortedTests(test_case); + + EXPECT_STREQ("Dummy", tests[0]->name()); + EXPECT_STREQ("TestCaseWithCommentTest/0", tests[0]->test_case_name()); + EXPECT_TRUE(IsNull(tests[0]->value_param())); + EXPECT_STREQ(GetTypeName().c_str(), tests[0]->type_param()); + EXPECT_TRUE(tests[0]->should_run()); + + delete[] tests; +#endif // GTEST_HAS_TYPED_TEST +} + +TEST(ApiTest, TestCaseDisabledAccessorsWork) { + const TestCase* test_case = UnitTestHelper::FindTestCase("DISABLED_Test"); + ASSERT_TRUE(test_case != NULL); + + EXPECT_STREQ("DISABLED_Test", test_case->name()); + EXPECT_TRUE(IsNull(test_case->type_param())); + EXPECT_FALSE(test_case->should_run()); + EXPECT_EQ(1, test_case->disabled_test_count()); + EXPECT_EQ(0, test_case->test_to_run_count()); + ASSERT_EQ(1, test_case->total_test_count()); + + const TestInfo* const test_info = test_case->GetTestInfo(0); + EXPECT_STREQ("Dummy2", test_info->name()); + EXPECT_STREQ("DISABLED_Test", test_info->test_case_name()); + EXPECT_TRUE(IsNull(test_info->value_param())); + EXPECT_TRUE(IsNull(test_info->type_param())); + EXPECT_FALSE(test_info->should_run()); +} + +// These two tests are here to provide support for testing +// test_case_to_run_count, disabled_test_count, and test_to_run_count. +TEST(ApiTest, DISABLED_Dummy1) {} +TEST(DISABLED_Test, Dummy2) {} + +class FinalSuccessChecker : public Environment { + protected: + virtual void TearDown() { + UnitTest* unit_test = UnitTest::GetInstance(); + + EXPECT_EQ(1 + kTypedTestCases, unit_test->successful_test_case_count()); + EXPECT_EQ(3 + kTypedTests, unit_test->successful_test_count()); + EXPECT_EQ(0, unit_test->failed_test_case_count()); + EXPECT_EQ(0, unit_test->failed_test_count()); + EXPECT_TRUE(unit_test->Passed()); + EXPECT_FALSE(unit_test->Failed()); + ASSERT_EQ(2 + kTypedTestCases, unit_test->total_test_case_count()); + + const TestCase** const test_cases = UnitTestHelper::GetSortedTestCases(); + + EXPECT_STREQ("ApiTest", test_cases[0]->name()); + EXPECT_TRUE(IsNull(test_cases[0]->type_param())); + EXPECT_TRUE(test_cases[0]->should_run()); + EXPECT_EQ(1, test_cases[0]->disabled_test_count()); + ASSERT_EQ(4, test_cases[0]->total_test_count()); + EXPECT_EQ(3, test_cases[0]->successful_test_count()); + EXPECT_EQ(0, test_cases[0]->failed_test_count()); + EXPECT_TRUE(test_cases[0]->Passed()); + EXPECT_FALSE(test_cases[0]->Failed()); + + EXPECT_STREQ("DISABLED_Test", test_cases[1]->name()); + EXPECT_TRUE(IsNull(test_cases[1]->type_param())); + EXPECT_FALSE(test_cases[1]->should_run()); + EXPECT_EQ(1, test_cases[1]->disabled_test_count()); + ASSERT_EQ(1, test_cases[1]->total_test_count()); + EXPECT_EQ(0, test_cases[1]->successful_test_count()); + EXPECT_EQ(0, test_cases[1]->failed_test_count()); + +#if GTEST_HAS_TYPED_TEST + EXPECT_STREQ("TestCaseWithCommentTest/0", test_cases[2]->name()); + EXPECT_STREQ(GetTypeName().c_str(), test_cases[2]->type_param()); + EXPECT_TRUE(test_cases[2]->should_run()); + EXPECT_EQ(0, test_cases[2]->disabled_test_count()); + ASSERT_EQ(1, test_cases[2]->total_test_count()); + EXPECT_EQ(1, test_cases[2]->successful_test_count()); + EXPECT_EQ(0, test_cases[2]->failed_test_count()); + EXPECT_TRUE(test_cases[2]->Passed()); + EXPECT_FALSE(test_cases[2]->Failed()); +#endif // GTEST_HAS_TYPED_TEST + + const TestCase* test_case = UnitTestHelper::FindTestCase("ApiTest"); + const TestInfo** tests = UnitTestHelper::GetSortedTests(test_case); + EXPECT_STREQ("DISABLED_Dummy1", tests[0]->name()); + EXPECT_STREQ("ApiTest", tests[0]->test_case_name()); + EXPECT_FALSE(tests[0]->should_run()); + + EXPECT_STREQ("TestCaseDisabledAccessorsWork", tests[1]->name()); + EXPECT_STREQ("ApiTest", tests[1]->test_case_name()); + EXPECT_TRUE(IsNull(tests[1]->value_param())); + EXPECT_TRUE(IsNull(tests[1]->type_param())); + EXPECT_TRUE(tests[1]->should_run()); + EXPECT_TRUE(tests[1]->result()->Passed()); + EXPECT_EQ(0, tests[1]->result()->test_property_count()); + + EXPECT_STREQ("TestCaseImmutableAccessorsWork", tests[2]->name()); + EXPECT_STREQ("ApiTest", tests[2]->test_case_name()); + EXPECT_TRUE(IsNull(tests[2]->value_param())); + EXPECT_TRUE(IsNull(tests[2]->type_param())); + EXPECT_TRUE(tests[2]->should_run()); + EXPECT_TRUE(tests[2]->result()->Passed()); + EXPECT_EQ(0, tests[2]->result()->test_property_count()); + + EXPECT_STREQ("UnitTestImmutableAccessorsWork", tests[3]->name()); + EXPECT_STREQ("ApiTest", tests[3]->test_case_name()); + EXPECT_TRUE(IsNull(tests[3]->value_param())); + EXPECT_TRUE(IsNull(tests[3]->type_param())); + EXPECT_TRUE(tests[3]->should_run()); + EXPECT_TRUE(tests[3]->result()->Passed()); + EXPECT_EQ(1, tests[3]->result()->test_property_count()); + const TestProperty& property = tests[3]->result()->GetTestProperty(0); + EXPECT_STREQ("key", property.key()); + EXPECT_STREQ("value", property.value()); + + delete[] tests; + +#if GTEST_HAS_TYPED_TEST + test_case = UnitTestHelper::FindTestCase("TestCaseWithCommentTest/0"); + tests = UnitTestHelper::GetSortedTests(test_case); + + EXPECT_STREQ("Dummy", tests[0]->name()); + EXPECT_STREQ("TestCaseWithCommentTest/0", tests[0]->test_case_name()); + EXPECT_TRUE(IsNull(tests[0]->value_param())); + EXPECT_STREQ(GetTypeName().c_str(), tests[0]->type_param()); + EXPECT_TRUE(tests[0]->should_run()); + EXPECT_TRUE(tests[0]->result()->Passed()); + EXPECT_EQ(0, tests[0]->result()->test_property_count()); + + delete[] tests; +#endif // GTEST_HAS_TYPED_TEST + delete[] test_cases; + } +}; + +} // namespace internal +} // namespace testing + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + + AddGlobalTestEnvironment(new testing::internal::FinalSuccessChecker()); + + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_all_test.cc b/external/gtest/test/gtest_all_test.cc new file mode 100644 index 0000000000..955aa62828 --- /dev/null +++ b/external/gtest/test/gtest_all_test.cc @@ -0,0 +1,47 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests for Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build most of Google Test's own tests +// by compiling a single file. This file serves this purpose. +#include "test/gtest-filepath_test.cc" +#include "test/gtest-linked_ptr_test.cc" +#include "test/gtest-message_test.cc" +#include "test/gtest-options_test.cc" +#include "test/gtest-port_test.cc" +#include "test/gtest_pred_impl_unittest.cc" +#include "test/gtest_prod_test.cc" +#include "test/gtest-test-part_test.cc" +#include "test/gtest-typed-test_test.cc" +#include "test/gtest-typed-test2_test.cc" +#include "test/gtest_unittest.cc" +#include "test/production.cc" diff --git a/external/gtest/test/gtest_break_on_failure_unittest.py b/external/gtest/test/gtest_break_on_failure_unittest.py new file mode 100644 index 0000000000..78f3e0f53b --- /dev/null +++ b/external/gtest/test/gtest_break_on_failure_unittest.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test for Google Test's break-on-failure mode. + +A user can ask Google Test to seg-fault when an assertion fails, using +either the GTEST_BREAK_ON_FAILURE environment variable or the +--gtest_break_on_failure flag. This script tests such functionality +by invoking gtest_break_on_failure_unittest_ (a program written with +Google Test) with different environments and command line flags. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import gtest_test_utils +import os +import sys + + +# Constants. + +IS_WINDOWS = os.name == 'nt' + +# The environment variable for enabling/disabling the break-on-failure mode. +BREAK_ON_FAILURE_ENV_VAR = 'GTEST_BREAK_ON_FAILURE' + +# The command line flag for enabling/disabling the break-on-failure mode. +BREAK_ON_FAILURE_FLAG = 'gtest_break_on_failure' + +# The environment variable for enabling/disabling the throw-on-failure mode. +THROW_ON_FAILURE_ENV_VAR = 'GTEST_THROW_ON_FAILURE' + +# The environment variable for enabling/disabling the catch-exceptions mode. +CATCH_EXCEPTIONS_ENV_VAR = 'GTEST_CATCH_EXCEPTIONS' + +# Path to the gtest_break_on_failure_unittest_ program. +EXE_PATH = gtest_test_utils.GetTestExecutablePath( + 'gtest_break_on_failure_unittest_') + + +environ = gtest_test_utils.environ +SetEnvVar = gtest_test_utils.SetEnvVar + +# Tests in this file run a Google-Test-based test program and expect it +# to terminate prematurely. Therefore they are incompatible with +# the premature-exit-file protocol by design. Unset the +# premature-exit filepath to prevent Google Test from creating +# the file. +SetEnvVar(gtest_test_utils.PREMATURE_EXIT_FILE_ENV_VAR, None) + + +def Run(command): + """Runs a command; returns 1 if it was killed by a signal, or 0 otherwise.""" + + p = gtest_test_utils.Subprocess(command, env=environ) + if p.terminated_by_signal: + return 1 + else: + return 0 + + +# The tests. + + +class GTestBreakOnFailureUnitTest(gtest_test_utils.TestCase): + """Tests using the GTEST_BREAK_ON_FAILURE environment variable or + the --gtest_break_on_failure flag to turn assertion failures into + segmentation faults. + """ + + def RunAndVerify(self, env_var_value, flag_value, expect_seg_fault): + """Runs gtest_break_on_failure_unittest_ and verifies that it does + (or does not) have a seg-fault. + + Args: + env_var_value: value of the GTEST_BREAK_ON_FAILURE environment + variable; None if the variable should be unset. + flag_value: value of the --gtest_break_on_failure flag; + None if the flag should not be present. + expect_seg_fault: 1 if the program is expected to generate a seg-fault; + 0 otherwise. + """ + + SetEnvVar(BREAK_ON_FAILURE_ENV_VAR, env_var_value) + + if env_var_value is None: + env_var_value_msg = ' is not set' + else: + env_var_value_msg = '=' + env_var_value + + if flag_value is None: + flag = '' + elif flag_value == '0': + flag = '--%s=0' % BREAK_ON_FAILURE_FLAG + else: + flag = '--%s' % BREAK_ON_FAILURE_FLAG + + command = [EXE_PATH] + if flag: + command.append(flag) + + if expect_seg_fault: + should_or_not = 'should' + else: + should_or_not = 'should not' + + has_seg_fault = Run(command) + + SetEnvVar(BREAK_ON_FAILURE_ENV_VAR, None) + + msg = ('when %s%s, an assertion failure in "%s" %s cause a seg-fault.' % + (BREAK_ON_FAILURE_ENV_VAR, env_var_value_msg, ' '.join(command), + should_or_not)) + self.assert_(has_seg_fault == expect_seg_fault, msg) + + def testDefaultBehavior(self): + """Tests the behavior of the default mode.""" + + self.RunAndVerify(env_var_value=None, + flag_value=None, + expect_seg_fault=0) + + def testEnvVar(self): + """Tests using the GTEST_BREAK_ON_FAILURE environment variable.""" + + self.RunAndVerify(env_var_value='0', + flag_value=None, + expect_seg_fault=0) + self.RunAndVerify(env_var_value='1', + flag_value=None, + expect_seg_fault=1) + + def testFlag(self): + """Tests using the --gtest_break_on_failure flag.""" + + self.RunAndVerify(env_var_value=None, + flag_value='0', + expect_seg_fault=0) + self.RunAndVerify(env_var_value=None, + flag_value='1', + expect_seg_fault=1) + + def testFlagOverridesEnvVar(self): + """Tests that the flag overrides the environment variable.""" + + self.RunAndVerify(env_var_value='0', + flag_value='0', + expect_seg_fault=0) + self.RunAndVerify(env_var_value='0', + flag_value='1', + expect_seg_fault=1) + self.RunAndVerify(env_var_value='1', + flag_value='0', + expect_seg_fault=0) + self.RunAndVerify(env_var_value='1', + flag_value='1', + expect_seg_fault=1) + + def testBreakOnFailureOverridesThrowOnFailure(self): + """Tests that gtest_break_on_failure overrides gtest_throw_on_failure.""" + + SetEnvVar(THROW_ON_FAILURE_ENV_VAR, '1') + try: + self.RunAndVerify(env_var_value=None, + flag_value='1', + expect_seg_fault=1) + finally: + SetEnvVar(THROW_ON_FAILURE_ENV_VAR, None) + + if IS_WINDOWS: + def testCatchExceptionsDoesNotInterfere(self): + """Tests that gtest_catch_exceptions doesn't interfere.""" + + SetEnvVar(CATCH_EXCEPTIONS_ENV_VAR, '1') + try: + self.RunAndVerify(env_var_value='1', + flag_value='1', + expect_seg_fault=1) + finally: + SetEnvVar(CATCH_EXCEPTIONS_ENV_VAR, None) + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_break_on_failure_unittest_.cc b/external/gtest/test/gtest_break_on_failure_unittest_.cc new file mode 100644 index 0000000000..dd07478c07 --- /dev/null +++ b/external/gtest/test/gtest_break_on_failure_unittest_.cc @@ -0,0 +1,88 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Unit test for Google Test's break-on-failure mode. +// +// A user can ask Google Test to seg-fault when an assertion fails, using +// either the GTEST_BREAK_ON_FAILURE environment variable or the +// --gtest_break_on_failure flag. This file is used for testing such +// functionality. +// +// This program will be invoked from a Python unit test. It is +// expected to fail. Don't run it directly. + +#include "gtest/gtest.h" + +#if GTEST_OS_WINDOWS +# include +# include +#endif + +namespace { + +// A test that's expected to fail. +TEST(Foo, Bar) { + EXPECT_EQ(2, 3); +} + +#if GTEST_HAS_SEH && !GTEST_OS_WINDOWS_MOBILE +// On Windows Mobile global exception handlers are not supported. +LONG WINAPI ExitWithExceptionCode( + struct _EXCEPTION_POINTERS* exception_pointers) { + exit(exception_pointers->ExceptionRecord->ExceptionCode); +} +#endif + +} // namespace + +int main(int argc, char **argv) { +#if GTEST_OS_WINDOWS + // Suppresses display of the Windows error dialog upon encountering + // a general protection fault (segment violation). + SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS); + +# if GTEST_HAS_SEH && !GTEST_OS_WINDOWS_MOBILE + + // The default unhandled exception filter does not always exit + // with the exception code as exit code - for example it exits with + // 0 for EXCEPTION_ACCESS_VIOLATION and 1 for EXCEPTION_BREAKPOINT + // if the application is compiled in debug mode. Thus we use our own + // filter which always exits with the exception code for unhandled + // exceptions. + SetUnhandledExceptionFilter(ExitWithExceptionCode); + +# endif +#endif + + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_catch_exceptions_test.py b/external/gtest/test/gtest_catch_exceptions_test.py new file mode 100644 index 0000000000..e6fc22fd1f --- /dev/null +++ b/external/gtest/test/gtest_catch_exceptions_test.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python +# +# Copyright 2010 Google Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests Google Test's exception catching behavior. + +This script invokes gtest_catch_exceptions_test_ and +gtest_catch_exceptions_ex_test_ (programs written with +Google Test) and verifies their output. +""" + +__author__ = 'vladl@google.com (Vlad Losev)' + +import os + +import gtest_test_utils + +# Constants. +FLAG_PREFIX = '--gtest_' +LIST_TESTS_FLAG = FLAG_PREFIX + 'list_tests' +NO_CATCH_EXCEPTIONS_FLAG = FLAG_PREFIX + 'catch_exceptions=0' +FILTER_FLAG = FLAG_PREFIX + 'filter' + +# Path to the gtest_catch_exceptions_ex_test_ binary, compiled with +# exceptions enabled. +EX_EXE_PATH = gtest_test_utils.GetTestExecutablePath( + 'gtest_catch_exceptions_ex_test_') + +# Path to the gtest_catch_exceptions_test_ binary, compiled with +# exceptions disabled. +EXE_PATH = gtest_test_utils.GetTestExecutablePath( + 'gtest_catch_exceptions_no_ex_test_') + +environ = gtest_test_utils.environ +SetEnvVar = gtest_test_utils.SetEnvVar + +# Tests in this file run a Google-Test-based test program and expect it +# to terminate prematurely. Therefore they are incompatible with +# the premature-exit-file protocol by design. Unset the +# premature-exit filepath to prevent Google Test from creating +# the file. +SetEnvVar(gtest_test_utils.PREMATURE_EXIT_FILE_ENV_VAR, None) + +TEST_LIST = gtest_test_utils.Subprocess( + [EXE_PATH, LIST_TESTS_FLAG], env=environ).output + +SUPPORTS_SEH_EXCEPTIONS = 'ThrowsSehException' in TEST_LIST + +if SUPPORTS_SEH_EXCEPTIONS: + BINARY_OUTPUT = gtest_test_utils.Subprocess([EXE_PATH], env=environ).output + +EX_BINARY_OUTPUT = gtest_test_utils.Subprocess( + [EX_EXE_PATH], env=environ).output + + +# The tests. +if SUPPORTS_SEH_EXCEPTIONS: + # pylint:disable-msg=C6302 + class CatchSehExceptionsTest(gtest_test_utils.TestCase): + """Tests exception-catching behavior.""" + + + def TestSehExceptions(self, test_output): + self.assert_('SEH exception with code 0x2a thrown ' + 'in the test fixture\'s constructor' + in test_output) + self.assert_('SEH exception with code 0x2a thrown ' + 'in the test fixture\'s destructor' + in test_output) + self.assert_('SEH exception with code 0x2a thrown in SetUpTestCase()' + in test_output) + self.assert_('SEH exception with code 0x2a thrown in TearDownTestCase()' + in test_output) + self.assert_('SEH exception with code 0x2a thrown in SetUp()' + in test_output) + self.assert_('SEH exception with code 0x2a thrown in TearDown()' + in test_output) + self.assert_('SEH exception with code 0x2a thrown in the test body' + in test_output) + + def testCatchesSehExceptionsWithCxxExceptionsEnabled(self): + self.TestSehExceptions(EX_BINARY_OUTPUT) + + def testCatchesSehExceptionsWithCxxExceptionsDisabled(self): + self.TestSehExceptions(BINARY_OUTPUT) + + +class CatchCxxExceptionsTest(gtest_test_utils.TestCase): + """Tests C++ exception-catching behavior. + + Tests in this test case verify that: + * C++ exceptions are caught and logged as C++ (not SEH) exceptions + * Exception thrown affect the remainder of the test work flow in the + expected manner. + """ + + def testCatchesCxxExceptionsInFixtureConstructor(self): + self.assert_('C++ exception with description ' + '"Standard C++ exception" thrown ' + 'in the test fixture\'s constructor' + in EX_BINARY_OUTPUT) + self.assert_('unexpected' not in EX_BINARY_OUTPUT, + 'This failure belongs in this test only if ' + '"CxxExceptionInConstructorTest" (no quotes) ' + 'appears on the same line as words "called unexpectedly"') + + if ('CxxExceptionInDestructorTest.ThrowsExceptionInDestructor' in + EX_BINARY_OUTPUT): + + def testCatchesCxxExceptionsInFixtureDestructor(self): + self.assert_('C++ exception with description ' + '"Standard C++ exception" thrown ' + 'in the test fixture\'s destructor' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInDestructorTest::TearDownTestCase() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + + def testCatchesCxxExceptionsInSetUpTestCase(self): + self.assert_('C++ exception with description "Standard C++ exception"' + ' thrown in SetUpTestCase()' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInConstructorTest::TearDownTestCase() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTestCaseTest constructor ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTestCaseTest destructor ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTestCaseTest::SetUp() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTestCaseTest::TearDown() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTestCaseTest test body ' + 'called as expected.' + in EX_BINARY_OUTPUT) + + def testCatchesCxxExceptionsInTearDownTestCase(self): + self.assert_('C++ exception with description "Standard C++ exception"' + ' thrown in TearDownTestCase()' + in EX_BINARY_OUTPUT) + + def testCatchesCxxExceptionsInSetUp(self): + self.assert_('C++ exception with description "Standard C++ exception"' + ' thrown in SetUp()' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTest::TearDownTestCase() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTest destructor ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInSetUpTest::TearDown() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('unexpected' not in EX_BINARY_OUTPUT, + 'This failure belongs in this test only if ' + '"CxxExceptionInSetUpTest" (no quotes) ' + 'appears on the same line as words "called unexpectedly"') + + def testCatchesCxxExceptionsInTearDown(self): + self.assert_('C++ exception with description "Standard C++ exception"' + ' thrown in TearDown()' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInTearDownTest::TearDownTestCase() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInTearDownTest destructor ' + 'called as expected.' + in EX_BINARY_OUTPUT) + + def testCatchesCxxExceptionsInTestBody(self): + self.assert_('C++ exception with description "Standard C++ exception"' + ' thrown in the test body' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInTestBodyTest::TearDownTestCase() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInTestBodyTest destructor ' + 'called as expected.' + in EX_BINARY_OUTPUT) + self.assert_('CxxExceptionInTestBodyTest::TearDown() ' + 'called as expected.' + in EX_BINARY_OUTPUT) + + def testCatchesNonStdCxxExceptions(self): + self.assert_('Unknown C++ exception thrown in the test body' + in EX_BINARY_OUTPUT) + + def testUnhandledCxxExceptionsAbortTheProgram(self): + # Filters out SEH exception tests on Windows. Unhandled SEH exceptions + # cause tests to show pop-up windows there. + FITLER_OUT_SEH_TESTS_FLAG = FILTER_FLAG + '=-*Seh*' + # By default, Google Test doesn't catch the exceptions. + uncaught_exceptions_ex_binary_output = gtest_test_utils.Subprocess( + [EX_EXE_PATH, + NO_CATCH_EXCEPTIONS_FLAG, + FITLER_OUT_SEH_TESTS_FLAG], + env=environ).output + + self.assert_('Unhandled C++ exception terminating the program' + in uncaught_exceptions_ex_binary_output) + self.assert_('unexpected' not in uncaught_exceptions_ex_binary_output) + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_catch_exceptions_test_.cc b/external/gtest/test/gtest_catch_exceptions_test_.cc new file mode 100644 index 0000000000..d0fc82c998 --- /dev/null +++ b/external/gtest/test/gtest_catch_exceptions_test_.cc @@ -0,0 +1,311 @@ +// Copyright 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) +// +// Tests for Google Test itself. Tests in this file throw C++ or SEH +// exceptions, and the output is verified by gtest_catch_exceptions_test.py. + +#include "gtest/gtest.h" + +#include // NOLINT +#include // For exit(). + +#if GTEST_HAS_SEH +# include +#endif + +#if GTEST_HAS_EXCEPTIONS +# include // For set_terminate(). +# include +#endif + +using testing::Test; + +#if GTEST_HAS_SEH + +class SehExceptionInConstructorTest : public Test { + public: + SehExceptionInConstructorTest() { RaiseException(42, 0, 0, NULL); } +}; + +TEST_F(SehExceptionInConstructorTest, ThrowsExceptionInConstructor) {} + +class SehExceptionInDestructorTest : public Test { + public: + ~SehExceptionInDestructorTest() { RaiseException(42, 0, 0, NULL); } +}; + +TEST_F(SehExceptionInDestructorTest, ThrowsExceptionInDestructor) {} + +class SehExceptionInSetUpTestCaseTest : public Test { + public: + static void SetUpTestCase() { RaiseException(42, 0, 0, NULL); } +}; + +TEST_F(SehExceptionInSetUpTestCaseTest, ThrowsExceptionInSetUpTestCase) {} + +class SehExceptionInTearDownTestCaseTest : public Test { + public: + static void TearDownTestCase() { RaiseException(42, 0, 0, NULL); } +}; + +TEST_F(SehExceptionInTearDownTestCaseTest, ThrowsExceptionInTearDownTestCase) {} + +class SehExceptionInSetUpTest : public Test { + protected: + virtual void SetUp() { RaiseException(42, 0, 0, NULL); } +}; + +TEST_F(SehExceptionInSetUpTest, ThrowsExceptionInSetUp) {} + +class SehExceptionInTearDownTest : public Test { + protected: + virtual void TearDown() { RaiseException(42, 0, 0, NULL); } +}; + +TEST_F(SehExceptionInTearDownTest, ThrowsExceptionInTearDown) {} + +TEST(SehExceptionTest, ThrowsSehException) { + RaiseException(42, 0, 0, NULL); +} + +#endif // GTEST_HAS_SEH + +#if GTEST_HAS_EXCEPTIONS + +class CxxExceptionInConstructorTest : public Test { + public: + CxxExceptionInConstructorTest() { + // Without this macro VC++ complains about unreachable code at the end of + // the constructor. + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_( + throw std::runtime_error("Standard C++ exception")); + } + + static void TearDownTestCase() { + printf("%s", + "CxxExceptionInConstructorTest::TearDownTestCase() " + "called as expected.\n"); + } + + protected: + ~CxxExceptionInConstructorTest() { + ADD_FAILURE() << "CxxExceptionInConstructorTest destructor " + << "called unexpectedly."; + } + + virtual void SetUp() { + ADD_FAILURE() << "CxxExceptionInConstructorTest::SetUp() " + << "called unexpectedly."; + } + + virtual void TearDown() { + ADD_FAILURE() << "CxxExceptionInConstructorTest::TearDown() " + << "called unexpectedly."; + } +}; + +TEST_F(CxxExceptionInConstructorTest, ThrowsExceptionInConstructor) { + ADD_FAILURE() << "CxxExceptionInConstructorTest test body " + << "called unexpectedly."; +} + +// Exceptions in destructors are not supported in C++11. +#if !defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L +class CxxExceptionInDestructorTest : public Test { + public: + static void TearDownTestCase() { + printf("%s", + "CxxExceptionInDestructorTest::TearDownTestCase() " + "called as expected.\n"); + } + + protected: + ~CxxExceptionInDestructorTest() { + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_( + throw std::runtime_error("Standard C++ exception")); + } +}; + +TEST_F(CxxExceptionInDestructorTest, ThrowsExceptionInDestructor) {} +#endif // C++11 mode + +class CxxExceptionInSetUpTestCaseTest : public Test { + public: + CxxExceptionInSetUpTestCaseTest() { + printf("%s", + "CxxExceptionInSetUpTestCaseTest constructor " + "called as expected.\n"); + } + + static void SetUpTestCase() { + throw std::runtime_error("Standard C++ exception"); + } + + static void TearDownTestCase() { + printf("%s", + "CxxExceptionInSetUpTestCaseTest::TearDownTestCase() " + "called as expected.\n"); + } + + protected: + ~CxxExceptionInSetUpTestCaseTest() { + printf("%s", + "CxxExceptionInSetUpTestCaseTest destructor " + "called as expected.\n"); + } + + virtual void SetUp() { + printf("%s", + "CxxExceptionInSetUpTestCaseTest::SetUp() " + "called as expected.\n"); + } + + virtual void TearDown() { + printf("%s", + "CxxExceptionInSetUpTestCaseTest::TearDown() " + "called as expected.\n"); + } +}; + +TEST_F(CxxExceptionInSetUpTestCaseTest, ThrowsExceptionInSetUpTestCase) { + printf("%s", + "CxxExceptionInSetUpTestCaseTest test body " + "called as expected.\n"); +} + +class CxxExceptionInTearDownTestCaseTest : public Test { + public: + static void TearDownTestCase() { + throw std::runtime_error("Standard C++ exception"); + } +}; + +TEST_F(CxxExceptionInTearDownTestCaseTest, ThrowsExceptionInTearDownTestCase) {} + +class CxxExceptionInSetUpTest : public Test { + public: + static void TearDownTestCase() { + printf("%s", + "CxxExceptionInSetUpTest::TearDownTestCase() " + "called as expected.\n"); + } + + protected: + ~CxxExceptionInSetUpTest() { + printf("%s", + "CxxExceptionInSetUpTest destructor " + "called as expected.\n"); + } + + virtual void SetUp() { throw std::runtime_error("Standard C++ exception"); } + + virtual void TearDown() { + printf("%s", + "CxxExceptionInSetUpTest::TearDown() " + "called as expected.\n"); + } +}; + +TEST_F(CxxExceptionInSetUpTest, ThrowsExceptionInSetUp) { + ADD_FAILURE() << "CxxExceptionInSetUpTest test body " + << "called unexpectedly."; +} + +class CxxExceptionInTearDownTest : public Test { + public: + static void TearDownTestCase() { + printf("%s", + "CxxExceptionInTearDownTest::TearDownTestCase() " + "called as expected.\n"); + } + + protected: + ~CxxExceptionInTearDownTest() { + printf("%s", + "CxxExceptionInTearDownTest destructor " + "called as expected.\n"); + } + + virtual void TearDown() { + throw std::runtime_error("Standard C++ exception"); + } +}; + +TEST_F(CxxExceptionInTearDownTest, ThrowsExceptionInTearDown) {} + +class CxxExceptionInTestBodyTest : public Test { + public: + static void TearDownTestCase() { + printf("%s", + "CxxExceptionInTestBodyTest::TearDownTestCase() " + "called as expected.\n"); + } + + protected: + ~CxxExceptionInTestBodyTest() { + printf("%s", + "CxxExceptionInTestBodyTest destructor " + "called as expected.\n"); + } + + virtual void TearDown() { + printf("%s", + "CxxExceptionInTestBodyTest::TearDown() " + "called as expected.\n"); + } +}; + +TEST_F(CxxExceptionInTestBodyTest, ThrowsStdCxxException) { + throw std::runtime_error("Standard C++ exception"); +} + +TEST(CxxExceptionTest, ThrowsNonStdCxxException) { + throw "C-string"; +} + +// This terminate handler aborts the program using exit() rather than abort(). +// This avoids showing pop-ups on Windows systems and core dumps on Unix-like +// ones. +void TerminateHandler() { + fprintf(stderr, "%s\n", "Unhandled C++ exception terminating the program."); + fflush(NULL); + exit(3); +} + +#endif // GTEST_HAS_EXCEPTIONS + +int main(int argc, char** argv) { +#if GTEST_HAS_EXCEPTIONS + std::set_terminate(&TerminateHandler); +#endif + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_color_test.py b/external/gtest/test/gtest_color_test.py new file mode 100644 index 0000000000..d02a53ed85 --- /dev/null +++ b/external/gtest/test/gtest_color_test.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Verifies that Google Test correctly determines whether to use colors.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import gtest_test_utils + + +IS_WINDOWS = os.name = 'nt' + +COLOR_ENV_VAR = 'GTEST_COLOR' +COLOR_FLAG = 'gtest_color' +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_color_test_') + + +def SetEnvVar(env_var, value): + """Sets the env variable to 'value'; unsets it when 'value' is None.""" + + if value is not None: + os.environ[env_var] = value + elif env_var in os.environ: + del os.environ[env_var] + + +def UsesColor(term, color_env_var, color_flag): + """Runs gtest_color_test_ and returns its exit code.""" + + SetEnvVar('TERM', term) + SetEnvVar(COLOR_ENV_VAR, color_env_var) + + if color_flag is None: + args = [] + else: + args = ['--%s=%s' % (COLOR_FLAG, color_flag)] + p = gtest_test_utils.Subprocess([COMMAND] + args) + return not p.exited or p.exit_code + + +class GTestColorTest(gtest_test_utils.TestCase): + def testNoEnvVarNoFlag(self): + """Tests the case when there's neither GTEST_COLOR nor --gtest_color.""" + + if not IS_WINDOWS: + self.assert_(not UsesColor('dumb', None, None)) + self.assert_(not UsesColor('emacs', None, None)) + self.assert_(not UsesColor('xterm-mono', None, None)) + self.assert_(not UsesColor('unknown', None, None)) + self.assert_(not UsesColor(None, None, None)) + self.assert_(UsesColor('linux', None, None)) + self.assert_(UsesColor('cygwin', None, None)) + self.assert_(UsesColor('xterm', None, None)) + self.assert_(UsesColor('xterm-color', None, None)) + self.assert_(UsesColor('xterm-256color', None, None)) + + def testFlagOnly(self): + """Tests the case when there's --gtest_color but not GTEST_COLOR.""" + + self.assert_(not UsesColor('dumb', None, 'no')) + self.assert_(not UsesColor('xterm-color', None, 'no')) + if not IS_WINDOWS: + self.assert_(not UsesColor('emacs', None, 'auto')) + self.assert_(UsesColor('xterm', None, 'auto')) + self.assert_(UsesColor('dumb', None, 'yes')) + self.assert_(UsesColor('xterm', None, 'yes')) + + def testEnvVarOnly(self): + """Tests the case when there's GTEST_COLOR but not --gtest_color.""" + + self.assert_(not UsesColor('dumb', 'no', None)) + self.assert_(not UsesColor('xterm-color', 'no', None)) + if not IS_WINDOWS: + self.assert_(not UsesColor('dumb', 'auto', None)) + self.assert_(UsesColor('xterm-color', 'auto', None)) + self.assert_(UsesColor('dumb', 'yes', None)) + self.assert_(UsesColor('xterm-color', 'yes', None)) + + def testEnvVarAndFlag(self): + """Tests the case when there are both GTEST_COLOR and --gtest_color.""" + + self.assert_(not UsesColor('xterm-color', 'no', 'no')) + self.assert_(UsesColor('dumb', 'no', 'yes')) + self.assert_(UsesColor('xterm-color', 'no', 'auto')) + + def testAliasesOfYesAndNo(self): + """Tests using aliases in specifying --gtest_color.""" + + self.assert_(UsesColor('dumb', None, 'true')) + self.assert_(UsesColor('dumb', None, 'YES')) + self.assert_(UsesColor('dumb', None, 'T')) + self.assert_(UsesColor('dumb', None, '1')) + + self.assert_(not UsesColor('xterm', None, 'f')) + self.assert_(not UsesColor('xterm', None, 'false')) + self.assert_(not UsesColor('xterm', None, '0')) + self.assert_(not UsesColor('xterm', None, 'unknown')) + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_color_test_.cc b/external/gtest/test/gtest_color_test_.cc new file mode 100644 index 0000000000..f61ebb89b8 --- /dev/null +++ b/external/gtest/test/gtest_color_test_.cc @@ -0,0 +1,71 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// A helper program for testing how Google Test determines whether to use +// colors in the output. It prints "YES" and returns 1 if Google Test +// decides to use colors, and prints "NO" and returns 0 otherwise. + +#include + +#include "gtest/gtest.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +using testing::internal::ShouldUseColor; + +// The purpose of this is to ensure that the UnitTest singleton is +// created before main() is entered, and thus that ShouldUseColor() +// works the same way as in a real Google-Test-based test. We don't actual +// run the TEST itself. +TEST(GTestColorTest, Dummy) { +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + + if (ShouldUseColor(true)) { + // Google Test decides to use colors in the output (assuming it + // goes to a TTY). + printf("YES\n"); + return 1; + } else { + // Google Test decides not to use colors in the output. + printf("NO\n"); + return 0; + } +} diff --git a/external/gtest/test/gtest_env_var_test.py b/external/gtest/test/gtest_env_var_test.py new file mode 100644 index 0000000000..ac24337fa1 --- /dev/null +++ b/external/gtest/test/gtest_env_var_test.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Verifies that Google Test correctly parses environment variables.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import gtest_test_utils + + +IS_WINDOWS = os.name == 'nt' +IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux' + +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_env_var_test_') + +environ = os.environ.copy() + + +def AssertEq(expected, actual): + if expected != actual: + print 'Expected: %s' % (expected,) + print ' Actual: %s' % (actual,) + raise AssertionError + + +def SetEnvVar(env_var, value): + """Sets the env variable to 'value'; unsets it when 'value' is None.""" + + if value is not None: + environ[env_var] = value + elif env_var in environ: + del environ[env_var] + + +def GetFlag(flag): + """Runs gtest_env_var_test_ and returns its output.""" + + args = [COMMAND] + if flag is not None: + args += [flag] + return gtest_test_utils.Subprocess(args, env=environ).output + + +def TestFlag(flag, test_val, default_val): + """Verifies that the given flag is affected by the corresponding env var.""" + + env_var = 'GTEST_' + flag.upper() + SetEnvVar(env_var, test_val) + AssertEq(test_val, GetFlag(flag)) + SetEnvVar(env_var, None) + AssertEq(default_val, GetFlag(flag)) + + +class GTestEnvVarTest(gtest_test_utils.TestCase): + def testEnvVarAffectsFlag(self): + """Tests that environment variable should affect the corresponding flag.""" + + TestFlag('break_on_failure', '1', '0') + TestFlag('color', 'yes', 'auto') + TestFlag('filter', 'FooTest.Bar', '*') + TestFlag('output', 'xml:tmp/foo.xml', '') + TestFlag('print_time', '0', '1') + TestFlag('repeat', '999', '1') + TestFlag('throw_on_failure', '1', '0') + TestFlag('death_test_style', 'threadsafe', 'fast') + TestFlag('catch_exceptions', '0', '1') + + if IS_LINUX: + TestFlag('death_test_use_fork', '1', '0') + TestFlag('stack_trace_depth', '0', '100') + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_env_var_test_.cc b/external/gtest/test/gtest_env_var_test_.cc new file mode 100644 index 0000000000..539afc9683 --- /dev/null +++ b/external/gtest/test/gtest_env_var_test_.cc @@ -0,0 +1,126 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// A helper program for testing that Google Test parses the environment +// variables correctly. + +#include "gtest/gtest.h" + +#include + +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +using ::std::cout; + +namespace testing { + +// The purpose of this is to make the test more realistic by ensuring +// that the UnitTest singleton is created before main() is entered. +// We don't actual run the TEST itself. +TEST(GTestEnvVarTest, Dummy) { +} + +void PrintFlag(const char* flag) { + if (strcmp(flag, "break_on_failure") == 0) { + cout << GTEST_FLAG(break_on_failure); + return; + } + + if (strcmp(flag, "catch_exceptions") == 0) { + cout << GTEST_FLAG(catch_exceptions); + return; + } + + if (strcmp(flag, "color") == 0) { + cout << GTEST_FLAG(color); + return; + } + + if (strcmp(flag, "death_test_style") == 0) { + cout << GTEST_FLAG(death_test_style); + return; + } + + if (strcmp(flag, "death_test_use_fork") == 0) { + cout << GTEST_FLAG(death_test_use_fork); + return; + } + + if (strcmp(flag, "filter") == 0) { + cout << GTEST_FLAG(filter); + return; + } + + if (strcmp(flag, "output") == 0) { + cout << GTEST_FLAG(output); + return; + } + + if (strcmp(flag, "print_time") == 0) { + cout << GTEST_FLAG(print_time); + return; + } + + if (strcmp(flag, "repeat") == 0) { + cout << GTEST_FLAG(repeat); + return; + } + + if (strcmp(flag, "stack_trace_depth") == 0) { + cout << GTEST_FLAG(stack_trace_depth); + return; + } + + if (strcmp(flag, "throw_on_failure") == 0) { + cout << GTEST_FLAG(throw_on_failure); + return; + } + + cout << "Invalid flag name " << flag + << ". Valid names are break_on_failure, color, filter, etc.\n"; + exit(1); +} + +} // namespace testing + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + + if (argc != 2) { + cout << "Usage: gtest_env_var_test_ NAME_OF_FLAG\n"; + return 1; + } + + testing::PrintFlag(argv[1]); + return 0; +} diff --git a/external/gtest/test/gtest_environment_test.cc b/external/gtest/test/gtest_environment_test.cc new file mode 100644 index 0000000000..3cff19e70e --- /dev/null +++ b/external/gtest/test/gtest_environment_test.cc @@ -0,0 +1,192 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests using global test environments. + +#include +#include +#include "gtest/gtest.h" + +#define GTEST_IMPLEMENTATION_ 1 // Required for the next #include. +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +GTEST_DECLARE_string_(filter); +} + +namespace { + +enum FailureType { + NO_FAILURE, NON_FATAL_FAILURE, FATAL_FAILURE +}; + +// For testing using global test environments. +class MyEnvironment : public testing::Environment { + public: + MyEnvironment() { Reset(); } + + // Depending on the value of failure_in_set_up_, SetUp() will + // generate a non-fatal failure, generate a fatal failure, or + // succeed. + virtual void SetUp() { + set_up_was_run_ = true; + + switch (failure_in_set_up_) { + case NON_FATAL_FAILURE: + ADD_FAILURE() << "Expected non-fatal failure in global set-up."; + break; + case FATAL_FAILURE: + FAIL() << "Expected fatal failure in global set-up."; + break; + default: + break; + } + } + + // Generates a non-fatal failure. + virtual void TearDown() { + tear_down_was_run_ = true; + ADD_FAILURE() << "Expected non-fatal failure in global tear-down."; + } + + // Resets the state of the environment s.t. it can be reused. + void Reset() { + failure_in_set_up_ = NO_FAILURE; + set_up_was_run_ = false; + tear_down_was_run_ = false; + } + + // We call this function to set the type of failure SetUp() should + // generate. + void set_failure_in_set_up(FailureType type) { + failure_in_set_up_ = type; + } + + // Was SetUp() run? + bool set_up_was_run() const { return set_up_was_run_; } + + // Was TearDown() run? + bool tear_down_was_run() const { return tear_down_was_run_; } + + private: + FailureType failure_in_set_up_; + bool set_up_was_run_; + bool tear_down_was_run_; +}; + +// Was the TEST run? +bool test_was_run; + +// The sole purpose of this TEST is to enable us to check whether it +// was run. +TEST(FooTest, Bar) { + test_was_run = true; +} + +// Prints the message and aborts the program if condition is false. +void Check(bool condition, const char* msg) { + if (!condition) { + printf("FAILED: %s\n", msg); + testing::internal::posix::Abort(); + } +} + +// Runs the tests. Return true iff successful. +// +// The 'failure' parameter specifies the type of failure that should +// be generated by the global set-up. +int RunAllTests(MyEnvironment* env, FailureType failure) { + env->Reset(); + env->set_failure_in_set_up(failure); + test_was_run = false; + testing::internal::GetUnitTestImpl()->ClearAdHocTestResult(); + return RUN_ALL_TESTS(); +} + +} // namespace + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + + // Registers a global test environment, and verifies that the + // registration function returns its argument. + MyEnvironment* const env = new MyEnvironment; + Check(testing::AddGlobalTestEnvironment(env) == env, + "AddGlobalTestEnvironment() should return its argument."); + + // Verifies that RUN_ALL_TESTS() runs the tests when the global + // set-up is successful. + Check(RunAllTests(env, NO_FAILURE) != 0, + "RUN_ALL_TESTS() should return non-zero, as the global tear-down " + "should generate a failure."); + Check(test_was_run, + "The tests should run, as the global set-up should generate no " + "failure"); + Check(env->tear_down_was_run(), + "The global tear-down should run, as the global set-up was run."); + + // Verifies that RUN_ALL_TESTS() runs the tests when the global + // set-up generates no fatal failure. + Check(RunAllTests(env, NON_FATAL_FAILURE) != 0, + "RUN_ALL_TESTS() should return non-zero, as both the global set-up " + "and the global tear-down should generate a non-fatal failure."); + Check(test_was_run, + "The tests should run, as the global set-up should generate no " + "fatal failure."); + Check(env->tear_down_was_run(), + "The global tear-down should run, as the global set-up was run."); + + // Verifies that RUN_ALL_TESTS() runs no test when the global set-up + // generates a fatal failure. + Check(RunAllTests(env, FATAL_FAILURE) != 0, + "RUN_ALL_TESTS() should return non-zero, as the global set-up " + "should generate a fatal failure."); + Check(!test_was_run, + "The tests should not run, as the global set-up should generate " + "a fatal failure."); + Check(env->tear_down_was_run(), + "The global tear-down should run, as the global set-up was run."); + + // Verifies that RUN_ALL_TESTS() doesn't do global set-up or + // tear-down when there is no test to run. + testing::GTEST_FLAG(filter) = "-*"; + Check(RunAllTests(env, NO_FAILURE) == 0, + "RUN_ALL_TESTS() should return zero, as there is no test to run."); + Check(!env->set_up_was_run(), + "The global set-up should not run, as there is no test to run."); + Check(!env->tear_down_was_run(), + "The global tear-down should not run, " + "as the global set-up was not run."); + + printf("PASS\n"); + return 0; +} diff --git a/external/gtest/test/gtest_filter_unittest.py b/external/gtest/test/gtest_filter_unittest.py new file mode 100644 index 0000000000..0d1a770058 --- /dev/null +++ b/external/gtest/test/gtest_filter_unittest.py @@ -0,0 +1,633 @@ +#!/usr/bin/env python +# +# Copyright 2005 Google Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test for Google Test test filters. + +A user can specify which test(s) in a Google Test program to run via either +the GTEST_FILTER environment variable or the --gtest_filter flag. +This script tests such functionality by invoking +gtest_filter_unittest_ (a program written with Google Test) with different +environments and command line flags. + +Note that test sharding may also influence which tests are filtered. Therefore, +we test that here also. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sets +import sys + +import gtest_test_utils + +# Constants. + +# Checks if this platform can pass empty environment variables to child +# processes. We set an env variable to an empty string and invoke a python +# script in a subprocess to print whether the variable is STILL in +# os.environ. We then use 'eval' to parse the child's output so that an +# exception is thrown if the input is anything other than 'True' nor 'False'. +os.environ['EMPTY_VAR'] = '' +child = gtest_test_utils.Subprocess( + [sys.executable, '-c', 'import os; print \'EMPTY_VAR\' in os.environ']) +CAN_PASS_EMPTY_ENV = eval(child.output) + + +# Check if this platform can unset environment variables in child processes. +# We set an env variable to a non-empty string, unset it, and invoke +# a python script in a subprocess to print whether the variable +# is NO LONGER in os.environ. +# We use 'eval' to parse the child's output so that an exception +# is thrown if the input is neither 'True' nor 'False'. +os.environ['UNSET_VAR'] = 'X' +del os.environ['UNSET_VAR'] +child = gtest_test_utils.Subprocess( + [sys.executable, '-c', 'import os; print \'UNSET_VAR\' not in os.environ']) +CAN_UNSET_ENV = eval(child.output) + + +# Checks if we should test with an empty filter. This doesn't +# make sense on platforms that cannot pass empty env variables (Win32) +# and on platforms that cannot unset variables (since we cannot tell +# the difference between "" and NULL -- Borland and Solaris < 5.10) +CAN_TEST_EMPTY_FILTER = (CAN_PASS_EMPTY_ENV and CAN_UNSET_ENV) + + +# The environment variable for specifying the test filters. +FILTER_ENV_VAR = 'GTEST_FILTER' + +# The environment variables for test sharding. +TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS' +SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX' +SHARD_STATUS_FILE_ENV_VAR = 'GTEST_SHARD_STATUS_FILE' + +# The command line flag for specifying the test filters. +FILTER_FLAG = 'gtest_filter' + +# The command line flag for including disabled tests. +ALSO_RUN_DISABED_TESTS_FLAG = 'gtest_also_run_disabled_tests' + +# Command to run the gtest_filter_unittest_ program. +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_filter_unittest_') + +# Regex for determining whether parameterized tests are enabled in the binary. +PARAM_TEST_REGEX = re.compile(r'/ParamTest') + +# Regex for parsing test case names from Google Test's output. +TEST_CASE_REGEX = re.compile(r'^\[\-+\] \d+ tests? from (\w+(/\w+)?)') + +# Regex for parsing test names from Google Test's output. +TEST_REGEX = re.compile(r'^\[\s*RUN\s*\].*\.(\w+(/\w+)?)') + +# The command line flag to tell Google Test to output the list of tests it +# will run. +LIST_TESTS_FLAG = '--gtest_list_tests' + +# Indicates whether Google Test supports death tests. +SUPPORTS_DEATH_TESTS = 'HasDeathTest' in gtest_test_utils.Subprocess( + [COMMAND, LIST_TESTS_FLAG]).output + +# Full names of all tests in gtest_filter_unittests_. +PARAM_TESTS = [ + 'SeqP/ParamTest.TestX/0', + 'SeqP/ParamTest.TestX/1', + 'SeqP/ParamTest.TestY/0', + 'SeqP/ParamTest.TestY/1', + 'SeqQ/ParamTest.TestX/0', + 'SeqQ/ParamTest.TestX/1', + 'SeqQ/ParamTest.TestY/0', + 'SeqQ/ParamTest.TestY/1', + ] + +DISABLED_TESTS = [ + 'BarTest.DISABLED_TestFour', + 'BarTest.DISABLED_TestFive', + 'BazTest.DISABLED_TestC', + 'DISABLED_FoobarTest.Test1', + 'DISABLED_FoobarTest.DISABLED_Test2', + 'DISABLED_FoobarbazTest.TestA', + ] + +if SUPPORTS_DEATH_TESTS: + DEATH_TESTS = [ + 'HasDeathTest.Test1', + 'HasDeathTest.Test2', + ] +else: + DEATH_TESTS = [] + +# All the non-disabled tests. +ACTIVE_TESTS = [ + 'FooTest.Abc', + 'FooTest.Xyz', + + 'BarTest.TestOne', + 'BarTest.TestTwo', + 'BarTest.TestThree', + + 'BazTest.TestOne', + 'BazTest.TestA', + 'BazTest.TestB', + ] + DEATH_TESTS + PARAM_TESTS + +param_tests_present = None + +# Utilities. + +environ = os.environ.copy() + + +def SetEnvVar(env_var, value): + """Sets the env variable to 'value'; unsets it when 'value' is None.""" + + if value is not None: + environ[env_var] = value + elif env_var in environ: + del environ[env_var] + + +def RunAndReturnOutput(args = None): + """Runs the test program and returns its output.""" + + return gtest_test_utils.Subprocess([COMMAND] + (args or []), + env=environ).output + + +def RunAndExtractTestList(args = None): + """Runs the test program and returns its exit code and a list of tests run.""" + + p = gtest_test_utils.Subprocess([COMMAND] + (args or []), env=environ) + tests_run = [] + test_case = '' + test = '' + for line in p.output.split('\n'): + match = TEST_CASE_REGEX.match(line) + if match is not None: + test_case = match.group(1) + else: + match = TEST_REGEX.match(line) + if match is not None: + test = match.group(1) + tests_run.append(test_case + '.' + test) + return (tests_run, p.exit_code) + + +def InvokeWithModifiedEnv(extra_env, function, *args, **kwargs): + """Runs the given function and arguments in a modified environment.""" + try: + original_env = environ.copy() + environ.update(extra_env) + return function(*args, **kwargs) + finally: + environ.clear() + environ.update(original_env) + + +def RunWithSharding(total_shards, shard_index, command): + """Runs a test program shard and returns exit code and a list of tests run.""" + + extra_env = {SHARD_INDEX_ENV_VAR: str(shard_index), + TOTAL_SHARDS_ENV_VAR: str(total_shards)} + return InvokeWithModifiedEnv(extra_env, RunAndExtractTestList, command) + +# The unit test. + + +class GTestFilterUnitTest(gtest_test_utils.TestCase): + """Tests the env variable or the command line flag to filter tests.""" + + # Utilities. + + def AssertSetEqual(self, lhs, rhs): + """Asserts that two sets are equal.""" + + for elem in lhs: + self.assert_(elem in rhs, '%s in %s' % (elem, rhs)) + + for elem in rhs: + self.assert_(elem in lhs, '%s in %s' % (elem, lhs)) + + def AssertPartitionIsValid(self, set_var, list_of_sets): + """Asserts that list_of_sets is a valid partition of set_var.""" + + full_partition = [] + for slice_var in list_of_sets: + full_partition.extend(slice_var) + self.assertEqual(len(set_var), len(full_partition)) + self.assertEqual(sets.Set(set_var), sets.Set(full_partition)) + + def AdjustForParameterizedTests(self, tests_to_run): + """Adjust tests_to_run in case value parameterized tests are disabled.""" + + global param_tests_present + if not param_tests_present: + return list(sets.Set(tests_to_run) - sets.Set(PARAM_TESTS)) + else: + return tests_to_run + + def RunAndVerify(self, gtest_filter, tests_to_run): + """Checks that the binary runs correct set of tests for a given filter.""" + + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) + + # First, tests using the environment variable. + + # Windows removes empty variables from the environment when passing it + # to a new process. This means it is impossible to pass an empty filter + # into a process using the environment variable. However, we can still + # test the case when the variable is not supplied (i.e., gtest_filter is + # None). + # pylint: disable-msg=C6403 + if CAN_TEST_EMPTY_FILTER or gtest_filter != '': + SetEnvVar(FILTER_ENV_VAR, gtest_filter) + tests_run = RunAndExtractTestList()[0] + SetEnvVar(FILTER_ENV_VAR, None) + self.AssertSetEqual(tests_run, tests_to_run) + # pylint: enable-msg=C6403 + + # Next, tests using the command line flag. + + if gtest_filter is None: + args = [] + else: + args = ['--%s=%s' % (FILTER_FLAG, gtest_filter)] + + tests_run = RunAndExtractTestList(args)[0] + self.AssertSetEqual(tests_run, tests_to_run) + + def RunAndVerifyWithSharding(self, gtest_filter, total_shards, tests_to_run, + args=None, check_exit_0=False): + """Checks that binary runs correct tests for the given filter and shard. + + Runs all shards of gtest_filter_unittest_ with the given filter, and + verifies that the right set of tests were run. The union of tests run + on each shard should be identical to tests_to_run, without duplicates. + + Args: + gtest_filter: A filter to apply to the tests. + total_shards: A total number of shards to split test run into. + tests_to_run: A set of tests expected to run. + args : Arguments to pass to the to the test binary. + check_exit_0: When set to a true value, make sure that all shards + return 0. + """ + + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) + + # Windows removes empty variables from the environment when passing it + # to a new process. This means it is impossible to pass an empty filter + # into a process using the environment variable. However, we can still + # test the case when the variable is not supplied (i.e., gtest_filter is + # None). + # pylint: disable-msg=C6403 + if CAN_TEST_EMPTY_FILTER or gtest_filter != '': + SetEnvVar(FILTER_ENV_VAR, gtest_filter) + partition = [] + for i in range(0, total_shards): + (tests_run, exit_code) = RunWithSharding(total_shards, i, args) + if check_exit_0: + self.assertEqual(0, exit_code) + partition.append(tests_run) + + self.AssertPartitionIsValid(tests_to_run, partition) + SetEnvVar(FILTER_ENV_VAR, None) + # pylint: enable-msg=C6403 + + def RunAndVerifyAllowingDisabled(self, gtest_filter, tests_to_run): + """Checks that the binary runs correct set of tests for the given filter. + + Runs gtest_filter_unittest_ with the given filter, and enables + disabled tests. Verifies that the right set of tests were run. + + Args: + gtest_filter: A filter to apply to the tests. + tests_to_run: A set of tests expected to run. + """ + + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) + + # Construct the command line. + args = ['--%s' % ALSO_RUN_DISABED_TESTS_FLAG] + if gtest_filter is not None: + args.append('--%s=%s' % (FILTER_FLAG, gtest_filter)) + + tests_run = RunAndExtractTestList(args)[0] + self.AssertSetEqual(tests_run, tests_to_run) + + def setUp(self): + """Sets up test case. + + Determines whether value-parameterized tests are enabled in the binary and + sets the flags accordingly. + """ + + global param_tests_present + if param_tests_present is None: + param_tests_present = PARAM_TEST_REGEX.search( + RunAndReturnOutput()) is not None + + def testDefaultBehavior(self): + """Tests the behavior of not specifying the filter.""" + + self.RunAndVerify(None, ACTIVE_TESTS) + + def testDefaultBehaviorWithShards(self): + """Tests the behavior without the filter, with sharding enabled.""" + + self.RunAndVerifyWithSharding(None, 1, ACTIVE_TESTS) + self.RunAndVerifyWithSharding(None, 2, ACTIVE_TESTS) + self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) - 1, ACTIVE_TESTS) + self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS), ACTIVE_TESTS) + self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) + 1, ACTIVE_TESTS) + + def testEmptyFilter(self): + """Tests an empty filter.""" + + self.RunAndVerify('', []) + self.RunAndVerifyWithSharding('', 1, []) + self.RunAndVerifyWithSharding('', 2, []) + + def testBadFilter(self): + """Tests a filter that matches nothing.""" + + self.RunAndVerify('BadFilter', []) + self.RunAndVerifyAllowingDisabled('BadFilter', []) + + def testFullName(self): + """Tests filtering by full name.""" + + self.RunAndVerify('FooTest.Xyz', ['FooTest.Xyz']) + self.RunAndVerifyAllowingDisabled('FooTest.Xyz', ['FooTest.Xyz']) + self.RunAndVerifyWithSharding('FooTest.Xyz', 5, ['FooTest.Xyz']) + + def testUniversalFilters(self): + """Tests filters that match everything.""" + + self.RunAndVerify('*', ACTIVE_TESTS) + self.RunAndVerify('*.*', ACTIVE_TESTS) + self.RunAndVerifyWithSharding('*.*', len(ACTIVE_TESTS) - 3, ACTIVE_TESTS) + self.RunAndVerifyAllowingDisabled('*', ACTIVE_TESTS + DISABLED_TESTS) + self.RunAndVerifyAllowingDisabled('*.*', ACTIVE_TESTS + DISABLED_TESTS) + + def testFilterByTestCase(self): + """Tests filtering by test case name.""" + + self.RunAndVerify('FooTest.*', ['FooTest.Abc', 'FooTest.Xyz']) + + BAZ_TESTS = ['BazTest.TestOne', 'BazTest.TestA', 'BazTest.TestB'] + self.RunAndVerify('BazTest.*', BAZ_TESTS) + self.RunAndVerifyAllowingDisabled('BazTest.*', + BAZ_TESTS + ['BazTest.DISABLED_TestC']) + + def testFilterByTest(self): + """Tests filtering by test name.""" + + self.RunAndVerify('*.TestOne', ['BarTest.TestOne', 'BazTest.TestOne']) + + def testFilterDisabledTests(self): + """Select only the disabled tests to run.""" + + self.RunAndVerify('DISABLED_FoobarTest.Test1', []) + self.RunAndVerifyAllowingDisabled('DISABLED_FoobarTest.Test1', + ['DISABLED_FoobarTest.Test1']) + + self.RunAndVerify('*DISABLED_*', []) + self.RunAndVerifyAllowingDisabled('*DISABLED_*', DISABLED_TESTS) + + self.RunAndVerify('*.DISABLED_*', []) + self.RunAndVerifyAllowingDisabled('*.DISABLED_*', [ + 'BarTest.DISABLED_TestFour', + 'BarTest.DISABLED_TestFive', + 'BazTest.DISABLED_TestC', + 'DISABLED_FoobarTest.DISABLED_Test2', + ]) + + self.RunAndVerify('DISABLED_*', []) + self.RunAndVerifyAllowingDisabled('DISABLED_*', [ + 'DISABLED_FoobarTest.Test1', + 'DISABLED_FoobarTest.DISABLED_Test2', + 'DISABLED_FoobarbazTest.TestA', + ]) + + def testWildcardInTestCaseName(self): + """Tests using wildcard in the test case name.""" + + self.RunAndVerify('*a*.*', [ + 'BarTest.TestOne', + 'BarTest.TestTwo', + 'BarTest.TestThree', + + 'BazTest.TestOne', + 'BazTest.TestA', + 'BazTest.TestB', ] + DEATH_TESTS + PARAM_TESTS) + + def testWildcardInTestName(self): + """Tests using wildcard in the test name.""" + + self.RunAndVerify('*.*A*', ['FooTest.Abc', 'BazTest.TestA']) + + def testFilterWithoutDot(self): + """Tests a filter that has no '.' in it.""" + + self.RunAndVerify('*z*', [ + 'FooTest.Xyz', + + 'BazTest.TestOne', + 'BazTest.TestA', + 'BazTest.TestB', + ]) + + def testTwoPatterns(self): + """Tests filters that consist of two patterns.""" + + self.RunAndVerify('Foo*.*:*A*', [ + 'FooTest.Abc', + 'FooTest.Xyz', + + 'BazTest.TestA', + ]) + + # An empty pattern + a non-empty one + self.RunAndVerify(':*A*', ['FooTest.Abc', 'BazTest.TestA']) + + def testThreePatterns(self): + """Tests filters that consist of three patterns.""" + + self.RunAndVerify('*oo*:*A*:*One', [ + 'FooTest.Abc', + 'FooTest.Xyz', + + 'BarTest.TestOne', + + 'BazTest.TestOne', + 'BazTest.TestA', + ]) + + # The 2nd pattern is empty. + self.RunAndVerify('*oo*::*One', [ + 'FooTest.Abc', + 'FooTest.Xyz', + + 'BarTest.TestOne', + + 'BazTest.TestOne', + ]) + + # The last 2 patterns are empty. + self.RunAndVerify('*oo*::', [ + 'FooTest.Abc', + 'FooTest.Xyz', + ]) + + def testNegativeFilters(self): + self.RunAndVerify('*-BazTest.TestOne', [ + 'FooTest.Abc', + 'FooTest.Xyz', + + 'BarTest.TestOne', + 'BarTest.TestTwo', + 'BarTest.TestThree', + + 'BazTest.TestA', + 'BazTest.TestB', + ] + DEATH_TESTS + PARAM_TESTS) + + self.RunAndVerify('*-FooTest.Abc:BazTest.*', [ + 'FooTest.Xyz', + + 'BarTest.TestOne', + 'BarTest.TestTwo', + 'BarTest.TestThree', + ] + DEATH_TESTS + PARAM_TESTS) + + self.RunAndVerify('BarTest.*-BarTest.TestOne', [ + 'BarTest.TestTwo', + 'BarTest.TestThree', + ]) + + # Tests without leading '*'. + self.RunAndVerify('-FooTest.Abc:FooTest.Xyz:BazTest.*', [ + 'BarTest.TestOne', + 'BarTest.TestTwo', + 'BarTest.TestThree', + ] + DEATH_TESTS + PARAM_TESTS) + + # Value parameterized tests. + self.RunAndVerify('*/*', PARAM_TESTS) + + # Value parameterized tests filtering by the sequence name. + self.RunAndVerify('SeqP/*', [ + 'SeqP/ParamTest.TestX/0', + 'SeqP/ParamTest.TestX/1', + 'SeqP/ParamTest.TestY/0', + 'SeqP/ParamTest.TestY/1', + ]) + + # Value parameterized tests filtering by the test name. + self.RunAndVerify('*/0', [ + 'SeqP/ParamTest.TestX/0', + 'SeqP/ParamTest.TestY/0', + 'SeqQ/ParamTest.TestX/0', + 'SeqQ/ParamTest.TestY/0', + ]) + + def testFlagOverridesEnvVar(self): + """Tests that the filter flag overrides the filtering env. variable.""" + + SetEnvVar(FILTER_ENV_VAR, 'Foo*') + args = ['--%s=%s' % (FILTER_FLAG, '*One')] + tests_run = RunAndExtractTestList(args)[0] + SetEnvVar(FILTER_ENV_VAR, None) + + self.AssertSetEqual(tests_run, ['BarTest.TestOne', 'BazTest.TestOne']) + + def testShardStatusFileIsCreated(self): + """Tests that the shard file is created if specified in the environment.""" + + shard_status_file = os.path.join(gtest_test_utils.GetTempDir(), + 'shard_status_file') + self.assert_(not os.path.exists(shard_status_file)) + + extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file} + try: + InvokeWithModifiedEnv(extra_env, RunAndReturnOutput) + finally: + self.assert_(os.path.exists(shard_status_file)) + os.remove(shard_status_file) + + def testShardStatusFileIsCreatedWithListTests(self): + """Tests that the shard file is created with the "list_tests" flag.""" + + shard_status_file = os.path.join(gtest_test_utils.GetTempDir(), + 'shard_status_file2') + self.assert_(not os.path.exists(shard_status_file)) + + extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file} + try: + output = InvokeWithModifiedEnv(extra_env, + RunAndReturnOutput, + [LIST_TESTS_FLAG]) + finally: + # This assertion ensures that Google Test enumerated the tests as + # opposed to running them. + self.assert_('[==========]' not in output, + 'Unexpected output during test enumeration.\n' + 'Please ensure that LIST_TESTS_FLAG is assigned the\n' + 'correct flag value for listing Google Test tests.') + + self.assert_(os.path.exists(shard_status_file)) + os.remove(shard_status_file) + + if SUPPORTS_DEATH_TESTS: + def testShardingWorksWithDeathTests(self): + """Tests integration with death tests and sharding.""" + + gtest_filter = 'HasDeathTest.*:SeqP/*' + expected_tests = [ + 'HasDeathTest.Test1', + 'HasDeathTest.Test2', + + 'SeqP/ParamTest.TestX/0', + 'SeqP/ParamTest.TestX/1', + 'SeqP/ParamTest.TestY/0', + 'SeqP/ParamTest.TestY/1', + ] + + for flag in ['--gtest_death_test_style=threadsafe', + '--gtest_death_test_style=fast']: + self.RunAndVerifyWithSharding(gtest_filter, 3, expected_tests, + check_exit_0=True, args=[flag]) + self.RunAndVerifyWithSharding(gtest_filter, 5, expected_tests, + check_exit_0=True, args=[flag]) + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_filter_unittest_.cc b/external/gtest/test/gtest_filter_unittest_.cc new file mode 100644 index 0000000000..77deffc38f --- /dev/null +++ b/external/gtest/test/gtest_filter_unittest_.cc @@ -0,0 +1,140 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Unit test for Google Test test filters. +// +// A user can specify which test(s) in a Google Test program to run via +// either the GTEST_FILTER environment variable or the --gtest_filter +// flag. This is used for testing such functionality. +// +// The program will be invoked from a Python unit test. Don't run it +// directly. + +#include "gtest/gtest.h" + +namespace { + +// Test case FooTest. + +class FooTest : public testing::Test { +}; + +TEST_F(FooTest, Abc) { +} + +TEST_F(FooTest, Xyz) { + FAIL() << "Expected failure."; +} + +// Test case BarTest. + +TEST(BarTest, TestOne) { +} + +TEST(BarTest, TestTwo) { +} + +TEST(BarTest, TestThree) { +} + +TEST(BarTest, DISABLED_TestFour) { + FAIL() << "Expected failure."; +} + +TEST(BarTest, DISABLED_TestFive) { + FAIL() << "Expected failure."; +} + +// Test case BazTest. + +TEST(BazTest, TestOne) { + FAIL() << "Expected failure."; +} + +TEST(BazTest, TestA) { +} + +TEST(BazTest, TestB) { +} + +TEST(BazTest, DISABLED_TestC) { + FAIL() << "Expected failure."; +} + +// Test case HasDeathTest + +TEST(HasDeathTest, Test1) { + EXPECT_DEATH_IF_SUPPORTED(exit(1), ".*"); +} + +// We need at least two death tests to make sure that the all death tests +// aren't on the first shard. +TEST(HasDeathTest, Test2) { + EXPECT_DEATH_IF_SUPPORTED(exit(1), ".*"); +} + +// Test case FoobarTest + +TEST(DISABLED_FoobarTest, Test1) { + FAIL() << "Expected failure."; +} + +TEST(DISABLED_FoobarTest, DISABLED_Test2) { + FAIL() << "Expected failure."; +} + +// Test case FoobarbazTest + +TEST(DISABLED_FoobarbazTest, TestA) { + FAIL() << "Expected failure."; +} + +#if GTEST_HAS_PARAM_TEST +class ParamTest : public testing::TestWithParam { +}; + +TEST_P(ParamTest, TestX) { +} + +TEST_P(ParamTest, TestY) { +} + +INSTANTIATE_TEST_CASE_P(SeqP, ParamTest, testing::Values(1, 2)); +INSTANTIATE_TEST_CASE_P(SeqQ, ParamTest, testing::Values(5, 6)); +#endif // GTEST_HAS_PARAM_TEST + +} // namespace + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_help_test.py b/external/gtest/test/gtest_help_test.py new file mode 100644 index 0000000000..093c838d9e --- /dev/null +++ b/external/gtest/test/gtest_help_test.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests the --help flag of Google C++ Testing Framework. + +SYNOPSIS + gtest_help_test.py --build_dir=BUILD/DIR + # where BUILD/DIR contains the built gtest_help_test_ file. + gtest_help_test.py +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import gtest_test_utils + + +IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux' +IS_WINDOWS = os.name == 'nt' + +PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_help_test_') +FLAG_PREFIX = '--gtest_' +DEATH_TEST_STYLE_FLAG = FLAG_PREFIX + 'death_test_style' +STREAM_RESULT_TO_FLAG = FLAG_PREFIX + 'stream_result_to' +UNKNOWN_FLAG = FLAG_PREFIX + 'unknown_flag_for_testing' +LIST_TESTS_FLAG = FLAG_PREFIX + 'list_tests' +INCORRECT_FLAG_VARIANTS = [re.sub('^--', '-', LIST_TESTS_FLAG), + re.sub('^--', '/', LIST_TESTS_FLAG), + re.sub('_', '-', LIST_TESTS_FLAG)] +INTERNAL_FLAG_FOR_TESTING = FLAG_PREFIX + 'internal_flag_for_testing' + +SUPPORTS_DEATH_TESTS = "DeathTest" in gtest_test_utils.Subprocess( + [PROGRAM_PATH, LIST_TESTS_FLAG]).output + +# The help message must match this regex. +HELP_REGEX = re.compile( + FLAG_PREFIX + r'list_tests.*' + + FLAG_PREFIX + r'filter=.*' + + FLAG_PREFIX + r'also_run_disabled_tests.*' + + FLAG_PREFIX + r'repeat=.*' + + FLAG_PREFIX + r'shuffle.*' + + FLAG_PREFIX + r'random_seed=.*' + + FLAG_PREFIX + r'color=.*' + + FLAG_PREFIX + r'print_time.*' + + FLAG_PREFIX + r'output=.*' + + FLAG_PREFIX + r'break_on_failure.*' + + FLAG_PREFIX + r'throw_on_failure.*' + + FLAG_PREFIX + r'catch_exceptions=0.*', + re.DOTALL) + + +def RunWithFlag(flag): + """Runs gtest_help_test_ with the given flag. + + Returns: + the exit code and the text output as a tuple. + Args: + flag: the command-line flag to pass to gtest_help_test_, or None. + """ + + if flag is None: + command = [PROGRAM_PATH] + else: + command = [PROGRAM_PATH, flag] + child = gtest_test_utils.Subprocess(command) + return child.exit_code, child.output + + +class GTestHelpTest(gtest_test_utils.TestCase): + """Tests the --help flag and its equivalent forms.""" + + def TestHelpFlag(self, flag): + """Verifies correct behavior when help flag is specified. + + The right message must be printed and the tests must + skipped when the given flag is specified. + + Args: + flag: A flag to pass to the binary or None. + """ + + exit_code, output = RunWithFlag(flag) + self.assertEquals(0, exit_code) + self.assert_(HELP_REGEX.search(output), output) + + if IS_LINUX: + self.assert_(STREAM_RESULT_TO_FLAG in output, output) + else: + self.assert_(STREAM_RESULT_TO_FLAG not in output, output) + + if SUPPORTS_DEATH_TESTS and not IS_WINDOWS: + self.assert_(DEATH_TEST_STYLE_FLAG in output, output) + else: + self.assert_(DEATH_TEST_STYLE_FLAG not in output, output) + + def TestNonHelpFlag(self, flag): + """Verifies correct behavior when no help flag is specified. + + Verifies that when no help flag is specified, the tests are run + and the help message is not printed. + + Args: + flag: A flag to pass to the binary or None. + """ + + exit_code, output = RunWithFlag(flag) + self.assert_(exit_code != 0) + self.assert_(not HELP_REGEX.search(output), output) + + def testPrintsHelpWithFullFlag(self): + self.TestHelpFlag('--help') + + def testPrintsHelpWithShortFlag(self): + self.TestHelpFlag('-h') + + def testPrintsHelpWithQuestionFlag(self): + self.TestHelpFlag('-?') + + def testPrintsHelpWithWindowsStyleQuestionFlag(self): + self.TestHelpFlag('/?') + + def testPrintsHelpWithUnrecognizedGoogleTestFlag(self): + self.TestHelpFlag(UNKNOWN_FLAG) + + def testPrintsHelpWithIncorrectFlagStyle(self): + for incorrect_flag in INCORRECT_FLAG_VARIANTS: + self.TestHelpFlag(incorrect_flag) + + def testRunsTestsWithoutHelpFlag(self): + """Verifies that when no help flag is specified, the tests are run + and the help message is not printed.""" + + self.TestNonHelpFlag(None) + + def testRunsTestsWithGtestInternalFlag(self): + """Verifies that the tests are run and no help message is printed when + a flag starting with Google Test prefix and 'internal_' is supplied.""" + + self.TestNonHelpFlag(INTERNAL_FLAG_FOR_TESTING) + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_help_test_.cc b/external/gtest/test/gtest_help_test_.cc new file mode 100644 index 0000000000..31f78c2441 --- /dev/null +++ b/external/gtest/test/gtest_help_test_.cc @@ -0,0 +1,46 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// This program is meant to be run by gtest_help_test.py. Do not run +// it directly. + +#include "gtest/gtest.h" + +// When a help flag is specified, this program should skip the tests +// and exit with 0; otherwise the following test will be executed, +// causing this program to exit with a non-zero code. +TEST(HelpFlagTest, ShouldNotBeRun) { + ASSERT_TRUE(false) << "Tests shouldn't be run when --help is specified."; +} + +#if GTEST_HAS_DEATH_TEST +TEST(DeathTest, UsedByPythonScriptToDetectSupportForDeathTestsInThisBinary) {} +#endif diff --git a/external/gtest/test/gtest_list_tests_unittest.py b/external/gtest/test/gtest_list_tests_unittest.py new file mode 100644 index 0000000000..925b09d9cb --- /dev/null +++ b/external/gtest/test/gtest_list_tests_unittest.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test for Google Test's --gtest_list_tests flag. + +A user can ask Google Test to list all tests by specifying the +--gtest_list_tests flag. This script tests such functionality +by invoking gtest_list_tests_unittest_ (a program written with +Google Test) the command line flags. +""" + +__author__ = 'phanna@google.com (Patrick Hanna)' + +import gtest_test_utils +import re + + +# Constants. + +# The command line flag for enabling/disabling listing all tests. +LIST_TESTS_FLAG = 'gtest_list_tests' + +# Path to the gtest_list_tests_unittest_ program. +EXE_PATH = gtest_test_utils.GetTestExecutablePath('gtest_list_tests_unittest_') + +# The expected output when running gtest_list_tests_unittest_ with +# --gtest_list_tests +EXPECTED_OUTPUT_NO_FILTER_RE = re.compile(r"""FooDeathTest\. + Test1 +Foo\. + Bar1 + Bar2 + DISABLED_Bar3 +Abc\. + Xyz + Def +FooBar\. + Baz +FooTest\. + Test1 + DISABLED_Test2 + Test3 +TypedTest/0\. # TypeParam = (VeryLo{245}|class VeryLo{239})\.\.\. + TestA + TestB +TypedTest/1\. # TypeParam = int\s*\* + TestA + TestB +TypedTest/2\. # TypeParam = .*MyArray + TestA + TestB +My/TypeParamTest/0\. # TypeParam = (VeryLo{245}|class VeryLo{239})\.\.\. + TestA + TestB +My/TypeParamTest/1\. # TypeParam = int\s*\* + TestA + TestB +My/TypeParamTest/2\. # TypeParam = .*MyArray + TestA + TestB +MyInstantiation/ValueParamTest\. + TestA/0 # GetParam\(\) = one line + TestA/1 # GetParam\(\) = two\\nlines + TestA/2 # GetParam\(\) = a very\\nlo{241}\.\.\. + TestB/0 # GetParam\(\) = one line + TestB/1 # GetParam\(\) = two\\nlines + TestB/2 # GetParam\(\) = a very\\nlo{241}\.\.\. +""") + +# The expected output when running gtest_list_tests_unittest_ with +# --gtest_list_tests and --gtest_filter=Foo*. +EXPECTED_OUTPUT_FILTER_FOO_RE = re.compile(r"""FooDeathTest\. + Test1 +Foo\. + Bar1 + Bar2 + DISABLED_Bar3 +FooBar\. + Baz +FooTest\. + Test1 + DISABLED_Test2 + Test3 +""") + +# Utilities. + + +def Run(args): + """Runs gtest_list_tests_unittest_ and returns the list of tests printed.""" + + return gtest_test_utils.Subprocess([EXE_PATH] + args, + capture_stderr=False).output + + +# The unit test. + +class GTestListTestsUnitTest(gtest_test_utils.TestCase): + """Tests using the --gtest_list_tests flag to list all tests.""" + + def RunAndVerify(self, flag_value, expected_output_re, other_flag): + """Runs gtest_list_tests_unittest_ and verifies that it prints + the correct tests. + + Args: + flag_value: value of the --gtest_list_tests flag; + None if the flag should not be present. + expected_output_re: regular expression that matches the expected + output after running command; + other_flag: a different flag to be passed to command + along with gtest_list_tests; + None if the flag should not be present. + """ + + if flag_value is None: + flag = '' + flag_expression = 'not set' + elif flag_value == '0': + flag = '--%s=0' % LIST_TESTS_FLAG + flag_expression = '0' + else: + flag = '--%s' % LIST_TESTS_FLAG + flag_expression = '1' + + args = [flag] + + if other_flag is not None: + args += [other_flag] + + output = Run(args) + + if expected_output_re: + self.assert_( + expected_output_re.match(output), + ('when %s is %s, the output of "%s" is "%s",\n' + 'which does not match regex "%s"' % + (LIST_TESTS_FLAG, flag_expression, ' '.join(args), output, + expected_output_re.pattern))) + else: + self.assert_( + not EXPECTED_OUTPUT_NO_FILTER_RE.match(output), + ('when %s is %s, the output of "%s" is "%s"'% + (LIST_TESTS_FLAG, flag_expression, ' '.join(args), output))) + + def testDefaultBehavior(self): + """Tests the behavior of the default mode.""" + + self.RunAndVerify(flag_value=None, + expected_output_re=None, + other_flag=None) + + def testFlag(self): + """Tests using the --gtest_list_tests flag.""" + + self.RunAndVerify(flag_value='0', + expected_output_re=None, + other_flag=None) + self.RunAndVerify(flag_value='1', + expected_output_re=EXPECTED_OUTPUT_NO_FILTER_RE, + other_flag=None) + + def testOverrideNonFilterFlags(self): + """Tests that --gtest_list_tests overrides the non-filter flags.""" + + self.RunAndVerify(flag_value='1', + expected_output_re=EXPECTED_OUTPUT_NO_FILTER_RE, + other_flag='--gtest_break_on_failure') + + def testWithFilterFlags(self): + """Tests that --gtest_list_tests takes into account the + --gtest_filter flag.""" + + self.RunAndVerify(flag_value='1', + expected_output_re=EXPECTED_OUTPUT_FILTER_FOO_RE, + other_flag='--gtest_filter=Foo*') + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_list_tests_unittest_.cc b/external/gtest/test/gtest_list_tests_unittest_.cc new file mode 100644 index 0000000000..907c176ba9 --- /dev/null +++ b/external/gtest/test/gtest_list_tests_unittest_.cc @@ -0,0 +1,157 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: phanna@google.com (Patrick Hanna) + +// Unit test for Google Test's --gtest_list_tests flag. +// +// A user can ask Google Test to list all tests that will run +// so that when using a filter, a user will know what +// tests to look for. The tests will not be run after listing. +// +// This program will be invoked from a Python unit test. +// Don't run it directly. + +#include "gtest/gtest.h" + +// Several different test cases and tests that will be listed. +TEST(Foo, Bar1) { +} + +TEST(Foo, Bar2) { +} + +TEST(Foo, DISABLED_Bar3) { +} + +TEST(Abc, Xyz) { +} + +TEST(Abc, Def) { +} + +TEST(FooBar, Baz) { +} + +class FooTest : public testing::Test { +}; + +TEST_F(FooTest, Test1) { +} + +TEST_F(FooTest, DISABLED_Test2) { +} + +TEST_F(FooTest, Test3) { +} + +TEST(FooDeathTest, Test1) { +} + +// A group of value-parameterized tests. + +class MyType { + public: + explicit MyType(const std::string& a_value) : value_(a_value) {} + + const std::string& value() const { return value_; } + + private: + std::string value_; +}; + +// Teaches Google Test how to print a MyType. +void PrintTo(const MyType& x, std::ostream* os) { + *os << x.value(); +} + +class ValueParamTest : public testing::TestWithParam { +}; + +TEST_P(ValueParamTest, TestA) { +} + +TEST_P(ValueParamTest, TestB) { +} + +INSTANTIATE_TEST_CASE_P( + MyInstantiation, ValueParamTest, + testing::Values(MyType("one line"), + MyType("two\nlines"), + MyType("a very\nloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line"))); // NOLINT + +// A group of typed tests. + +// A deliberately long type name for testing the line-truncating +// behavior when printing a type parameter. +class VeryLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooogName { // NOLINT +}; + +template +class TypedTest : public testing::Test { +}; + +template +class MyArray { +}; + +typedef testing::Types > MyTypes; + +TYPED_TEST_CASE(TypedTest, MyTypes); + +TYPED_TEST(TypedTest, TestA) { +} + +TYPED_TEST(TypedTest, TestB) { +} + +// A group of type-parameterized tests. + +template +class TypeParamTest : public testing::Test { +}; + +TYPED_TEST_CASE_P(TypeParamTest); + +TYPED_TEST_P(TypeParamTest, TestA) { +} + +TYPED_TEST_P(TypeParamTest, TestB) { +} + +REGISTER_TYPED_TEST_CASE_P(TypeParamTest, TestA, TestB); + +INSTANTIATE_TYPED_TEST_CASE_P(My, TypeParamTest, MyTypes); + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_main_unittest.cc b/external/gtest/test/gtest_main_unittest.cc new file mode 100644 index 0000000000..ecd9bb876f --- /dev/null +++ b/external/gtest/test/gtest_main_unittest.cc @@ -0,0 +1,45 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest.h" + +// Tests that we don't have to define main() when we link to +// gtest_main instead of gtest. + +namespace { + +TEST(GTestMainTest, ShouldSucceed) { +} + +} // namespace + +// We are using the main() function defined in src/gtest_main.cc, so +// we don't define it here. diff --git a/external/gtest/test/gtest_no_test_unittest.cc b/external/gtest/test/gtest_no_test_unittest.cc new file mode 100644 index 0000000000..292599af8d --- /dev/null +++ b/external/gtest/test/gtest_no_test_unittest.cc @@ -0,0 +1,56 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Tests that a Google Test program that has no test defined can run +// successfully. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + + // An ad-hoc assertion outside of all tests. + // + // This serves three purposes: + // + // 1. It verifies that an ad-hoc assertion can be executed even if + // no test is defined. + // 2. It verifies that a failed ad-hoc assertion causes the test + // program to fail. + // 3. We had a bug where the XML output won't be generated if an + // assertion is executed before RUN_ALL_TESTS() is called, even + // though --gtest_output=xml is specified. This makes sure the + // bug is fixed and doesn't regress. + EXPECT_EQ(1, 2); + + // The above EXPECT_EQ() should cause RUN_ALL_TESTS() to return non-zero. + return RUN_ALL_TESTS() ? 0 : 1; +} diff --git a/external/gtest/test/gtest_output_test.py b/external/gtest/test/gtest_output_test.py new file mode 100644 index 0000000000..f409e2a78b --- /dev/null +++ b/external/gtest/test/gtest_output_test.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests the text output of Google C++ Testing Framework. + +SYNOPSIS + gtest_output_test.py --build_dir=BUILD/DIR --gengolden + # where BUILD/DIR contains the built gtest_output_test_ file. + gtest_output_test.py --gengolden + gtest_output_test.py +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import re +import sys +import gtest_test_utils + + +# The flag for generating the golden file +GENGOLDEN_FLAG = '--gengolden' +CATCH_EXCEPTIONS_ENV_VAR_NAME = 'GTEST_CATCH_EXCEPTIONS' + +IS_WINDOWS = os.name == 'nt' + +# TODO(vladl@google.com): remove the _lin suffix. +GOLDEN_NAME = 'gtest_output_test_golden_lin.txt' + +PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath('gtest_output_test_') + +# At least one command we exercise must not have the +# --gtest_internal_skip_environment_and_ad_hoc_tests flag. +COMMAND_LIST_TESTS = ({}, [PROGRAM_PATH, '--gtest_list_tests']) +COMMAND_WITH_COLOR = ({}, [PROGRAM_PATH, '--gtest_color=yes']) +COMMAND_WITH_TIME = ({}, [PROGRAM_PATH, + '--gtest_print_time', + '--gtest_internal_skip_environment_and_ad_hoc_tests', + '--gtest_filter=FatalFailureTest.*:LoggingTest.*']) +COMMAND_WITH_DISABLED = ( + {}, [PROGRAM_PATH, + '--gtest_also_run_disabled_tests', + '--gtest_internal_skip_environment_and_ad_hoc_tests', + '--gtest_filter=*DISABLED_*']) +COMMAND_WITH_SHARDING = ( + {'GTEST_SHARD_INDEX': '1', 'GTEST_TOTAL_SHARDS': '2'}, + [PROGRAM_PATH, + '--gtest_internal_skip_environment_and_ad_hoc_tests', + '--gtest_filter=PassingTest.*']) + +GOLDEN_PATH = os.path.join(gtest_test_utils.GetSourceDir(), GOLDEN_NAME) + + +def ToUnixLineEnding(s): + """Changes all Windows/Mac line endings in s to UNIX line endings.""" + + return s.replace('\r\n', '\n').replace('\r', '\n') + + +def RemoveLocations(test_output): + """Removes all file location info from a Google Test program's output. + + Args: + test_output: the output of a Google Test program. + + Returns: + output with all file location info (in the form of + 'DIRECTORY/FILE_NAME:LINE_NUMBER: 'or + 'DIRECTORY\\FILE_NAME(LINE_NUMBER): ') replaced by + 'FILE_NAME:#: '. + """ + + return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\: ', r'\1:#: ', test_output) + + +def RemoveStackTraceDetails(output): + """Removes all stack traces from a Google Test program's output.""" + + # *? means "find the shortest string that matches". + return re.sub(r'Stack trace:(.|\n)*?\n\n', + 'Stack trace: (omitted)\n\n', output) + + +def RemoveStackTraces(output): + """Removes all traces of stack traces from a Google Test program's output.""" + + # *? means "find the shortest string that matches". + return re.sub(r'Stack trace:(.|\n)*?\n\n', '', output) + + +def RemoveTime(output): + """Removes all time information from a Google Test program's output.""" + + return re.sub(r'\(\d+ ms', '(? ms', output) + + +def RemoveTypeInfoDetails(test_output): + """Removes compiler-specific type info from Google Test program's output. + + Args: + test_output: the output of a Google Test program. + + Returns: + output with type information normalized to canonical form. + """ + + # some compilers output the name of type 'unsigned int' as 'unsigned' + return re.sub(r'unsigned int', 'unsigned', test_output) + + +def NormalizeToCurrentPlatform(test_output): + """Normalizes platform specific output details for easier comparison.""" + + if IS_WINDOWS: + # Removes the color information that is not present on Windows. + test_output = re.sub('\x1b\\[(0;3\d)?m', '', test_output) + # Changes failure message headers into the Windows format. + test_output = re.sub(r': Failure\n', r': error: ', test_output) + # Changes file(line_number) to file:line_number. + test_output = re.sub(r'((\w|\.)+)\((\d+)\):', r'\1:\3:', test_output) + + return test_output + + +def RemoveTestCounts(output): + """Removes test counts from a Google Test program's output.""" + + output = re.sub(r'\d+ tests?, listed below', + '? tests, listed below', output) + output = re.sub(r'\d+ FAILED TESTS', + '? FAILED TESTS', output) + output = re.sub(r'\d+ tests? from \d+ test cases?', + '? tests from ? test cases', output) + output = re.sub(r'\d+ tests? from ([a-zA-Z_])', + r'? tests from \1', output) + return re.sub(r'\d+ tests?\.', '? tests.', output) + + +def RemoveMatchingTests(test_output, pattern): + """Removes output of specified tests from a Google Test program's output. + + This function strips not only the beginning and the end of a test but also + all output in between. + + Args: + test_output: A string containing the test output. + pattern: A regex string that matches names of test cases or + tests to remove. + + Returns: + Contents of test_output with tests whose names match pattern removed. + """ + + test_output = re.sub( + r'.*\[ RUN \] .*%s(.|\n)*?\[( FAILED | OK )\] .*%s.*\n' % ( + pattern, pattern), + '', + test_output) + return re.sub(r'.*%s.*\n' % pattern, '', test_output) + + +def NormalizeOutput(output): + """Normalizes output (the output of gtest_output_test_.exe).""" + + output = ToUnixLineEnding(output) + output = RemoveLocations(output) + output = RemoveStackTraceDetails(output) + output = RemoveTime(output) + return output + + +def GetShellCommandOutput(env_cmd): + """Runs a command in a sub-process, and returns its output in a string. + + Args: + env_cmd: The shell command. A 2-tuple where element 0 is a dict of extra + environment variables to set, and element 1 is a string with + the command and any flags. + + Returns: + A string with the command's combined standard and diagnostic output. + """ + + # Spawns cmd in a sub-process, and gets its standard I/O file objects. + # Set and save the environment properly. + environ = os.environ.copy() + environ.update(env_cmd[0]) + p = gtest_test_utils.Subprocess(env_cmd[1], env=environ) + + return p.output + + +def GetCommandOutput(env_cmd): + """Runs a command and returns its output with all file location + info stripped off. + + Args: + env_cmd: The shell command. A 2-tuple where element 0 is a dict of extra + environment variables to set, and element 1 is a string with + the command and any flags. + """ + + # Disables exception pop-ups on Windows. + environ, cmdline = env_cmd + environ = dict(environ) # Ensures we are modifying a copy. + environ[CATCH_EXCEPTIONS_ENV_VAR_NAME] = '1' + return NormalizeOutput(GetShellCommandOutput((environ, cmdline))) + + +def GetOutputOfAllCommands(): + """Returns concatenated output from several representative commands.""" + + return (GetCommandOutput(COMMAND_WITH_COLOR) + + GetCommandOutput(COMMAND_WITH_TIME) + + GetCommandOutput(COMMAND_WITH_DISABLED) + + GetCommandOutput(COMMAND_WITH_SHARDING)) + + +test_list = GetShellCommandOutput(COMMAND_LIST_TESTS) +SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list +SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list +SUPPORTS_THREADS = 'ExpectFailureWithThreadsTest' in test_list +SUPPORTS_STACK_TRACES = False + +CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and + SUPPORTS_TYPED_TESTS and + SUPPORTS_THREADS) + + +class GTestOutputTest(gtest_test_utils.TestCase): + def RemoveUnsupportedTests(self, test_output): + if not SUPPORTS_DEATH_TESTS: + test_output = RemoveMatchingTests(test_output, 'DeathTest') + if not SUPPORTS_TYPED_TESTS: + test_output = RemoveMatchingTests(test_output, 'TypedTest') + test_output = RemoveMatchingTests(test_output, 'TypedDeathTest') + test_output = RemoveMatchingTests(test_output, 'TypeParamDeathTest') + if not SUPPORTS_THREADS: + test_output = RemoveMatchingTests(test_output, + 'ExpectFailureWithThreadsTest') + test_output = RemoveMatchingTests(test_output, + 'ScopedFakeTestPartResultReporterTest') + test_output = RemoveMatchingTests(test_output, + 'WorksConcurrently') + if not SUPPORTS_STACK_TRACES: + test_output = RemoveStackTraces(test_output) + + return test_output + + def testOutput(self): + output = GetOutputOfAllCommands() + + golden_file = open(GOLDEN_PATH, 'rb') + # A mis-configured source control system can cause \r appear in EOL + # sequences when we read the golden file irrespective of an operating + # system used. Therefore, we need to strip those \r's from newlines + # unconditionally. + golden = ToUnixLineEnding(golden_file.read()) + golden_file.close() + + # We want the test to pass regardless of certain features being + # supported or not. + + # We still have to remove type name specifics in all cases. + normalized_actual = RemoveTypeInfoDetails(output) + normalized_golden = RemoveTypeInfoDetails(golden) + + if CAN_GENERATE_GOLDEN_FILE: + self.assertEqual(normalized_golden, normalized_actual) + else: + normalized_actual = NormalizeToCurrentPlatform( + RemoveTestCounts(normalized_actual)) + normalized_golden = NormalizeToCurrentPlatform( + RemoveTestCounts(self.RemoveUnsupportedTests(normalized_golden))) + + # This code is very handy when debugging golden file differences: + if os.getenv('DEBUG_GTEST_OUTPUT_TEST'): + open(os.path.join( + gtest_test_utils.GetSourceDir(), + '_gtest_output_test_normalized_actual.txt'), 'wb').write( + normalized_actual) + open(os.path.join( + gtest_test_utils.GetSourceDir(), + '_gtest_output_test_normalized_golden.txt'), 'wb').write( + normalized_golden) + + self.assertEqual(normalized_golden, normalized_actual) + + +if __name__ == '__main__': + if sys.argv[1:] == [GENGOLDEN_FLAG]: + if CAN_GENERATE_GOLDEN_FILE: + output = GetOutputOfAllCommands() + golden_file = open(GOLDEN_PATH, 'wb') + golden_file.write(output) + golden_file.close() + else: + message = ( + """Unable to write a golden file when compiled in an environment +that does not support all the required features (death tests, typed tests, +and multiple threads). Please generate the golden file using a binary built +with those features enabled.""") + + sys.stderr.write(message) + sys.exit(1) + else: + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_output_test_.cc b/external/gtest/test/gtest_output_test_.cc new file mode 100644 index 0000000000..07ab633d4d --- /dev/null +++ b/external/gtest/test/gtest_output_test_.cc @@ -0,0 +1,1034 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The purpose of this file is to generate Google Test output under +// various conditions. The output will then be verified by +// gtest_output_test.py to ensure that Google Test generates the +// desired messages. Therefore, most tests in this file are MEANT TO +// FAIL. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest-spi.h" +#include "gtest/gtest.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +#include + +#if GTEST_IS_THREADSAFE +using testing::ScopedFakeTestPartResultReporter; +using testing::TestPartResultArray; + +using testing::internal::Notification; +using testing::internal::ThreadWithParam; +#endif + +namespace posix = ::testing::internal::posix; +using testing::internal::scoped_ptr; + +// Tests catching fatal failures. + +// A subroutine used by the following test. +void TestEq1(int x) { + ASSERT_EQ(1, x); +} + +// This function calls a test subroutine, catches the fatal failure it +// generates, and then returns early. +void TryTestSubroutine() { + // Calls a subrountine that yields a fatal failure. + TestEq1(2); + + // Catches the fatal failure and aborts the test. + // + // The testing::Test:: prefix is necessary when calling + // HasFatalFailure() outside of a TEST, TEST_F, or test fixture. + if (testing::Test::HasFatalFailure()) return; + + // If we get here, something is wrong. + FAIL() << "This should never be reached."; +} + +TEST(PassingTest, PassingTest1) { +} + +TEST(PassingTest, PassingTest2) { +} + +// Tests that parameters of failing parameterized tests are printed in the +// failing test summary. +class FailingParamTest : public testing::TestWithParam {}; + +TEST_P(FailingParamTest, Fails) { + EXPECT_EQ(1, GetParam()); +} + +// This generates a test which will fail. Google Test is expected to print +// its parameter when it outputs the list of all failed tests. +INSTANTIATE_TEST_CASE_P(PrintingFailingParams, + FailingParamTest, + testing::Values(2)); + +static const char kGoldenString[] = "\"Line\0 1\"\nLine 2"; + +TEST(NonfatalFailureTest, EscapesStringOperands) { + std::string actual = "actual \"string\""; + EXPECT_EQ(kGoldenString, actual); + + const char* golden = kGoldenString; + EXPECT_EQ(golden, actual); +} + +// Tests catching a fatal failure in a subroutine. +TEST(FatalFailureTest, FatalFailureInSubroutine) { + printf("(expecting a failure that x should be 1)\n"); + + TryTestSubroutine(); +} + +// Tests catching a fatal failure in a nested subroutine. +TEST(FatalFailureTest, FatalFailureInNestedSubroutine) { + printf("(expecting a failure that x should be 1)\n"); + + // Calls a subrountine that yields a fatal failure. + TryTestSubroutine(); + + // Catches the fatal failure and aborts the test. + // + // When calling HasFatalFailure() inside a TEST, TEST_F, or test + // fixture, the testing::Test:: prefix is not needed. + if (HasFatalFailure()) return; + + // If we get here, something is wrong. + FAIL() << "This should never be reached."; +} + +// Tests HasFatalFailure() after a failed EXPECT check. +TEST(FatalFailureTest, NonfatalFailureInSubroutine) { + printf("(expecting a failure on false)\n"); + EXPECT_TRUE(false); // Generates a nonfatal failure + ASSERT_FALSE(HasFatalFailure()); // This should succeed. +} + +// Tests interleaving user logging and Google Test assertions. +TEST(LoggingTest, InterleavingLoggingAndAssertions) { + static const int a[4] = { + 3, 9, 2, 6 + }; + + printf("(expecting 2 failures on (3) >= (a[i]))\n"); + for (int i = 0; i < static_cast(sizeof(a)/sizeof(*a)); i++) { + printf("i == %d\n", i); + EXPECT_GE(3, a[i]); + } +} + +// Tests the SCOPED_TRACE macro. + +// A helper function for testing SCOPED_TRACE. +void SubWithoutTrace(int n) { + EXPECT_EQ(1, n); + ASSERT_EQ(2, n); +} + +// Another helper function for testing SCOPED_TRACE. +void SubWithTrace(int n) { + SCOPED_TRACE(testing::Message() << "n = " << n); + + SubWithoutTrace(n); +} + +// Tests that SCOPED_TRACE() obeys lexical scopes. +TEST(SCOPED_TRACETest, ObeysScopes) { + printf("(expected to fail)\n"); + + // There should be no trace before SCOPED_TRACE() is invoked. + ADD_FAILURE() << "This failure is expected, and shouldn't have a trace."; + + { + SCOPED_TRACE("Expected trace"); + // After SCOPED_TRACE(), a failure in the current scope should contain + // the trace. + ADD_FAILURE() << "This failure is expected, and should have a trace."; + } + + // Once the control leaves the scope of the SCOPED_TRACE(), there + // should be no trace again. + ADD_FAILURE() << "This failure is expected, and shouldn't have a trace."; +} + +// Tests that SCOPED_TRACE works inside a loop. +TEST(SCOPED_TRACETest, WorksInLoop) { + printf("(expected to fail)\n"); + + for (int i = 1; i <= 2; i++) { + SCOPED_TRACE(testing::Message() << "i = " << i); + + SubWithoutTrace(i); + } +} + +// Tests that SCOPED_TRACE works in a subroutine. +TEST(SCOPED_TRACETest, WorksInSubroutine) { + printf("(expected to fail)\n"); + + SubWithTrace(1); + SubWithTrace(2); +} + +// Tests that SCOPED_TRACE can be nested. +TEST(SCOPED_TRACETest, CanBeNested) { + printf("(expected to fail)\n"); + + SCOPED_TRACE(""); // A trace without a message. + + SubWithTrace(2); +} + +// Tests that multiple SCOPED_TRACEs can be used in the same scope. +TEST(SCOPED_TRACETest, CanBeRepeated) { + printf("(expected to fail)\n"); + + SCOPED_TRACE("A"); + ADD_FAILURE() + << "This failure is expected, and should contain trace point A."; + + SCOPED_TRACE("B"); + ADD_FAILURE() + << "This failure is expected, and should contain trace point A and B."; + + { + SCOPED_TRACE("C"); + ADD_FAILURE() << "This failure is expected, and should " + << "contain trace point A, B, and C."; + } + + SCOPED_TRACE("D"); + ADD_FAILURE() << "This failure is expected, and should " + << "contain trace point A, B, and D."; +} + +#if GTEST_IS_THREADSAFE +// Tests that SCOPED_TRACE()s can be used concurrently from multiple +// threads. Namely, an assertion should be affected by +// SCOPED_TRACE()s in its own thread only. + +// Here's the sequence of actions that happen in the test: +// +// Thread A (main) | Thread B (spawned) +// ===============================|================================ +// spawns thread B | +// -------------------------------+-------------------------------- +// waits for n1 | SCOPED_TRACE("Trace B"); +// | generates failure #1 +// | notifies n1 +// -------------------------------+-------------------------------- +// SCOPED_TRACE("Trace A"); | waits for n2 +// generates failure #2 | +// notifies n2 | +// -------------------------------|-------------------------------- +// waits for n3 | generates failure #3 +// | trace B dies +// | generates failure #4 +// | notifies n3 +// -------------------------------|-------------------------------- +// generates failure #5 | finishes +// trace A dies | +// generates failure #6 | +// -------------------------------|-------------------------------- +// waits for thread B to finish | + +struct CheckPoints { + Notification n1; + Notification n2; + Notification n3; +}; + +static void ThreadWithScopedTrace(CheckPoints* check_points) { + { + SCOPED_TRACE("Trace B"); + ADD_FAILURE() + << "Expected failure #1 (in thread B, only trace B alive)."; + check_points->n1.Notify(); + check_points->n2.WaitForNotification(); + + ADD_FAILURE() + << "Expected failure #3 (in thread B, trace A & B both alive)."; + } // Trace B dies here. + ADD_FAILURE() + << "Expected failure #4 (in thread B, only trace A alive)."; + check_points->n3.Notify(); +} + +TEST(SCOPED_TRACETest, WorksConcurrently) { + printf("(expecting 6 failures)\n"); + + CheckPoints check_points; + ThreadWithParam thread(&ThreadWithScopedTrace, + &check_points, + NULL); + check_points.n1.WaitForNotification(); + + { + SCOPED_TRACE("Trace A"); + ADD_FAILURE() + << "Expected failure #2 (in thread A, trace A & B both alive)."; + check_points.n2.Notify(); + check_points.n3.WaitForNotification(); + + ADD_FAILURE() + << "Expected failure #5 (in thread A, only trace A alive)."; + } // Trace A dies here. + ADD_FAILURE() + << "Expected failure #6 (in thread A, no trace alive)."; + thread.Join(); +} +#endif // GTEST_IS_THREADSAFE + +TEST(DisabledTestsWarningTest, + DISABLED_AlsoRunDisabledTestsFlagSuppressesWarning) { + // This test body is intentionally empty. Its sole purpose is for + // verifying that the --gtest_also_run_disabled_tests flag + // suppresses the "YOU HAVE 12 DISABLED TESTS" warning at the end of + // the test output. +} + +// Tests using assertions outside of TEST and TEST_F. +// +// This function creates two failures intentionally. +void AdHocTest() { + printf("The non-test part of the code is expected to have 2 failures.\n\n"); + EXPECT_TRUE(false); + EXPECT_EQ(2, 3); +} + +// Runs all TESTs, all TEST_Fs, and the ad hoc test. +int RunAllTests() { + AdHocTest(); + return RUN_ALL_TESTS(); +} + +// Tests non-fatal failures in the fixture constructor. +class NonFatalFailureInFixtureConstructorTest : public testing::Test { + protected: + NonFatalFailureInFixtureConstructorTest() { + printf("(expecting 5 failures)\n"); + ADD_FAILURE() << "Expected failure #1, in the test fixture c'tor."; + } + + ~NonFatalFailureInFixtureConstructorTest() { + ADD_FAILURE() << "Expected failure #5, in the test fixture d'tor."; + } + + virtual void SetUp() { + ADD_FAILURE() << "Expected failure #2, in SetUp()."; + } + + virtual void TearDown() { + ADD_FAILURE() << "Expected failure #4, in TearDown."; + } +}; + +TEST_F(NonFatalFailureInFixtureConstructorTest, FailureInConstructor) { + ADD_FAILURE() << "Expected failure #3, in the test body."; +} + +// Tests fatal failures in the fixture constructor. +class FatalFailureInFixtureConstructorTest : public testing::Test { + protected: + FatalFailureInFixtureConstructorTest() { + printf("(expecting 2 failures)\n"); + Init(); + } + + ~FatalFailureInFixtureConstructorTest() { + ADD_FAILURE() << "Expected failure #2, in the test fixture d'tor."; + } + + virtual void SetUp() { + ADD_FAILURE() << "UNEXPECTED failure in SetUp(). " + << "We should never get here, as the test fixture c'tor " + << "had a fatal failure."; + } + + virtual void TearDown() { + ADD_FAILURE() << "UNEXPECTED failure in TearDown(). " + << "We should never get here, as the test fixture c'tor " + << "had a fatal failure."; + } + + private: + void Init() { + FAIL() << "Expected failure #1, in the test fixture c'tor."; + } +}; + +TEST_F(FatalFailureInFixtureConstructorTest, FailureInConstructor) { + ADD_FAILURE() << "UNEXPECTED failure in the test body. " + << "We should never get here, as the test fixture c'tor " + << "had a fatal failure."; +} + +// Tests non-fatal failures in SetUp(). +class NonFatalFailureInSetUpTest : public testing::Test { + protected: + virtual ~NonFatalFailureInSetUpTest() { + Deinit(); + } + + virtual void SetUp() { + printf("(expecting 4 failures)\n"); + ADD_FAILURE() << "Expected failure #1, in SetUp()."; + } + + virtual void TearDown() { + FAIL() << "Expected failure #3, in TearDown()."; + } + private: + void Deinit() { + FAIL() << "Expected failure #4, in the test fixture d'tor."; + } +}; + +TEST_F(NonFatalFailureInSetUpTest, FailureInSetUp) { + FAIL() << "Expected failure #2, in the test function."; +} + +// Tests fatal failures in SetUp(). +class FatalFailureInSetUpTest : public testing::Test { + protected: + virtual ~FatalFailureInSetUpTest() { + Deinit(); + } + + virtual void SetUp() { + printf("(expecting 3 failures)\n"); + FAIL() << "Expected failure #1, in SetUp()."; + } + + virtual void TearDown() { + FAIL() << "Expected failure #2, in TearDown()."; + } + private: + void Deinit() { + FAIL() << "Expected failure #3, in the test fixture d'tor."; + } +}; + +TEST_F(FatalFailureInSetUpTest, FailureInSetUp) { + FAIL() << "UNEXPECTED failure in the test function. " + << "We should never get here, as SetUp() failed."; +} + +TEST(AddFailureAtTest, MessageContainsSpecifiedFileAndLineNumber) { + ADD_FAILURE_AT("foo.cc", 42) << "Expected failure in foo.cc"; +} + +#if GTEST_IS_THREADSAFE + +// A unary function that may die. +void DieIf(bool should_die) { + GTEST_CHECK_(!should_die) << " - death inside DieIf()."; +} + +// Tests running death tests in a multi-threaded context. + +// Used for coordination between the main and the spawn thread. +struct SpawnThreadNotifications { + SpawnThreadNotifications() {} + + Notification spawn_thread_started; + Notification spawn_thread_ok_to_terminate; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(SpawnThreadNotifications); +}; + +// The function to be executed in the thread spawn by the +// MultipleThreads test (below). +static void ThreadRoutine(SpawnThreadNotifications* notifications) { + // Signals the main thread that this thread has started. + notifications->spawn_thread_started.Notify(); + + // Waits for permission to finish from the main thread. + notifications->spawn_thread_ok_to_terminate.WaitForNotification(); +} + +// This is a death-test test, but it's not named with a DeathTest +// suffix. It starts threads which might interfere with later +// death tests, so it must run after all other death tests. +class DeathTestAndMultiThreadsTest : public testing::Test { + protected: + // Starts a thread and waits for it to begin. + virtual void SetUp() { + thread_.reset(new ThreadWithParam( + &ThreadRoutine, ¬ifications_, NULL)); + notifications_.spawn_thread_started.WaitForNotification(); + } + // Tells the thread to finish, and reaps it. + // Depending on the version of the thread library in use, + // a manager thread might still be left running that will interfere + // with later death tests. This is unfortunate, but this class + // cleans up after itself as best it can. + virtual void TearDown() { + notifications_.spawn_thread_ok_to_terminate.Notify(); + } + + private: + SpawnThreadNotifications notifications_; + scoped_ptr > thread_; +}; + +#endif // GTEST_IS_THREADSAFE + +// The MixedUpTestCaseTest test case verifies that Google Test will fail a +// test if it uses a different fixture class than what other tests in +// the same test case use. It deliberately contains two fixture +// classes with the same name but defined in different namespaces. + +// The MixedUpTestCaseWithSameTestNameTest test case verifies that +// when the user defines two tests with the same test case name AND +// same test name (but in different namespaces), the second test will +// fail. + +namespace foo { + +class MixedUpTestCaseTest : public testing::Test { +}; + +TEST_F(MixedUpTestCaseTest, FirstTestFromNamespaceFoo) {} +TEST_F(MixedUpTestCaseTest, SecondTestFromNamespaceFoo) {} + +class MixedUpTestCaseWithSameTestNameTest : public testing::Test { +}; + +TEST_F(MixedUpTestCaseWithSameTestNameTest, + TheSecondTestWithThisNameShouldFail) {} + +} // namespace foo + +namespace bar { + +class MixedUpTestCaseTest : public testing::Test { +}; + +// The following two tests are expected to fail. We rely on the +// golden file to check that Google Test generates the right error message. +TEST_F(MixedUpTestCaseTest, ThisShouldFail) {} +TEST_F(MixedUpTestCaseTest, ThisShouldFailToo) {} + +class MixedUpTestCaseWithSameTestNameTest : public testing::Test { +}; + +// Expected to fail. We rely on the golden file to check that Google Test +// generates the right error message. +TEST_F(MixedUpTestCaseWithSameTestNameTest, + TheSecondTestWithThisNameShouldFail) {} + +} // namespace bar + +// The following two test cases verify that Google Test catches the user +// error of mixing TEST and TEST_F in the same test case. The first +// test case checks the scenario where TEST_F appears before TEST, and +// the second one checks where TEST appears before TEST_F. + +class TEST_F_before_TEST_in_same_test_case : public testing::Test { +}; + +TEST_F(TEST_F_before_TEST_in_same_test_case, DefinedUsingTEST_F) {} + +// Expected to fail. We rely on the golden file to check that Google Test +// generates the right error message. +TEST(TEST_F_before_TEST_in_same_test_case, DefinedUsingTESTAndShouldFail) {} + +class TEST_before_TEST_F_in_same_test_case : public testing::Test { +}; + +TEST(TEST_before_TEST_F_in_same_test_case, DefinedUsingTEST) {} + +// Expected to fail. We rely on the golden file to check that Google Test +// generates the right error message. +TEST_F(TEST_before_TEST_F_in_same_test_case, DefinedUsingTEST_FAndShouldFail) { +} + +// Used for testing EXPECT_NONFATAL_FAILURE() and EXPECT_FATAL_FAILURE(). +int global_integer = 0; + +// Tests that EXPECT_NONFATAL_FAILURE() can reference global variables. +TEST(ExpectNonfatalFailureTest, CanReferenceGlobalVariables) { + global_integer = 0; + EXPECT_NONFATAL_FAILURE({ + EXPECT_EQ(1, global_integer) << "Expected non-fatal failure."; + }, "Expected non-fatal failure."); +} + +// Tests that EXPECT_NONFATAL_FAILURE() can reference local variables +// (static or not). +TEST(ExpectNonfatalFailureTest, CanReferenceLocalVariables) { + int m = 0; + static int n; + n = 1; + EXPECT_NONFATAL_FAILURE({ + EXPECT_EQ(m, n) << "Expected non-fatal failure."; + }, "Expected non-fatal failure."); +} + +// Tests that EXPECT_NONFATAL_FAILURE() succeeds when there is exactly +// one non-fatal failure and no fatal failure. +TEST(ExpectNonfatalFailureTest, SucceedsWhenThereIsOneNonfatalFailure) { + EXPECT_NONFATAL_FAILURE({ + ADD_FAILURE() << "Expected non-fatal failure."; + }, "Expected non-fatal failure."); +} + +// Tests that EXPECT_NONFATAL_FAILURE() fails when there is no +// non-fatal failure. +TEST(ExpectNonfatalFailureTest, FailsWhenThereIsNoNonfatalFailure) { + printf("(expecting a failure)\n"); + EXPECT_NONFATAL_FAILURE({ + }, ""); +} + +// Tests that EXPECT_NONFATAL_FAILURE() fails when there are two +// non-fatal failures. +TEST(ExpectNonfatalFailureTest, FailsWhenThereAreTwoNonfatalFailures) { + printf("(expecting a failure)\n"); + EXPECT_NONFATAL_FAILURE({ + ADD_FAILURE() << "Expected non-fatal failure 1."; + ADD_FAILURE() << "Expected non-fatal failure 2."; + }, ""); +} + +// Tests that EXPECT_NONFATAL_FAILURE() fails when there is one fatal +// failure. +TEST(ExpectNonfatalFailureTest, FailsWhenThereIsOneFatalFailure) { + printf("(expecting a failure)\n"); + EXPECT_NONFATAL_FAILURE({ + FAIL() << "Expected fatal failure."; + }, ""); +} + +// Tests that EXPECT_NONFATAL_FAILURE() fails when the statement being +// tested returns. +TEST(ExpectNonfatalFailureTest, FailsWhenStatementReturns) { + printf("(expecting a failure)\n"); + EXPECT_NONFATAL_FAILURE({ + return; + }, ""); +} + +#if GTEST_HAS_EXCEPTIONS + +// Tests that EXPECT_NONFATAL_FAILURE() fails when the statement being +// tested throws. +TEST(ExpectNonfatalFailureTest, FailsWhenStatementThrows) { + printf("(expecting a failure)\n"); + try { + EXPECT_NONFATAL_FAILURE({ + throw 0; + }, ""); + } catch(int) { // NOLINT + } +} + +#endif // GTEST_HAS_EXCEPTIONS + +// Tests that EXPECT_FATAL_FAILURE() can reference global variables. +TEST(ExpectFatalFailureTest, CanReferenceGlobalVariables) { + global_integer = 0; + EXPECT_FATAL_FAILURE({ + ASSERT_EQ(1, global_integer) << "Expected fatal failure."; + }, "Expected fatal failure."); +} + +// Tests that EXPECT_FATAL_FAILURE() can reference local static +// variables. +TEST(ExpectFatalFailureTest, CanReferenceLocalStaticVariables) { + static int n; + n = 1; + EXPECT_FATAL_FAILURE({ + ASSERT_EQ(0, n) << "Expected fatal failure."; + }, "Expected fatal failure."); +} + +// Tests that EXPECT_FATAL_FAILURE() succeeds when there is exactly +// one fatal failure and no non-fatal failure. +TEST(ExpectFatalFailureTest, SucceedsWhenThereIsOneFatalFailure) { + EXPECT_FATAL_FAILURE({ + FAIL() << "Expected fatal failure."; + }, "Expected fatal failure."); +} + +// Tests that EXPECT_FATAL_FAILURE() fails when there is no fatal +// failure. +TEST(ExpectFatalFailureTest, FailsWhenThereIsNoFatalFailure) { + printf("(expecting a failure)\n"); + EXPECT_FATAL_FAILURE({ + }, ""); +} + +// A helper for generating a fatal failure. +void FatalFailure() { + FAIL() << "Expected fatal failure."; +} + +// Tests that EXPECT_FATAL_FAILURE() fails when there are two +// fatal failures. +TEST(ExpectFatalFailureTest, FailsWhenThereAreTwoFatalFailures) { + printf("(expecting a failure)\n"); + EXPECT_FATAL_FAILURE({ + FatalFailure(); + FatalFailure(); + }, ""); +} + +// Tests that EXPECT_FATAL_FAILURE() fails when there is one non-fatal +// failure. +TEST(ExpectFatalFailureTest, FailsWhenThereIsOneNonfatalFailure) { + printf("(expecting a failure)\n"); + EXPECT_FATAL_FAILURE({ + ADD_FAILURE() << "Expected non-fatal failure."; + }, ""); +} + +// Tests that EXPECT_FATAL_FAILURE() fails when the statement being +// tested returns. +TEST(ExpectFatalFailureTest, FailsWhenStatementReturns) { + printf("(expecting a failure)\n"); + EXPECT_FATAL_FAILURE({ + return; + }, ""); +} + +#if GTEST_HAS_EXCEPTIONS + +// Tests that EXPECT_FATAL_FAILURE() fails when the statement being +// tested throws. +TEST(ExpectFatalFailureTest, FailsWhenStatementThrows) { + printf("(expecting a failure)\n"); + try { + EXPECT_FATAL_FAILURE({ + throw 0; + }, ""); + } catch(int) { // NOLINT + } +} + +#endif // GTEST_HAS_EXCEPTIONS + +// This #ifdef block tests the output of typed tests. +#if GTEST_HAS_TYPED_TEST + +template +class TypedTest : public testing::Test { +}; + +TYPED_TEST_CASE(TypedTest, testing::Types); + +TYPED_TEST(TypedTest, Success) { + EXPECT_EQ(0, TypeParam()); +} + +TYPED_TEST(TypedTest, Failure) { + EXPECT_EQ(1, TypeParam()) << "Expected failure"; +} + +#endif // GTEST_HAS_TYPED_TEST + +// This #ifdef block tests the output of type-parameterized tests. +#if GTEST_HAS_TYPED_TEST_P + +template +class TypedTestP : public testing::Test { +}; + +TYPED_TEST_CASE_P(TypedTestP); + +TYPED_TEST_P(TypedTestP, Success) { + EXPECT_EQ(0U, TypeParam()); +} + +TYPED_TEST_P(TypedTestP, Failure) { + EXPECT_EQ(1U, TypeParam()) << "Expected failure"; +} + +REGISTER_TYPED_TEST_CASE_P(TypedTestP, Success, Failure); + +typedef testing::Types UnsignedTypes; +INSTANTIATE_TYPED_TEST_CASE_P(Unsigned, TypedTestP, UnsignedTypes); + +#endif // GTEST_HAS_TYPED_TEST_P + +#if GTEST_HAS_DEATH_TEST + +// We rely on the golden file to verify that tests whose test case +// name ends with DeathTest are run first. + +TEST(ADeathTest, ShouldRunFirst) { +} + +# if GTEST_HAS_TYPED_TEST + +// We rely on the golden file to verify that typed tests whose test +// case name ends with DeathTest are run first. + +template +class ATypedDeathTest : public testing::Test { +}; + +typedef testing::Types NumericTypes; +TYPED_TEST_CASE(ATypedDeathTest, NumericTypes); + +TYPED_TEST(ATypedDeathTest, ShouldRunFirst) { +} + +# endif // GTEST_HAS_TYPED_TEST + +# if GTEST_HAS_TYPED_TEST_P + + +// We rely on the golden file to verify that type-parameterized tests +// whose test case name ends with DeathTest are run first. + +template +class ATypeParamDeathTest : public testing::Test { +}; + +TYPED_TEST_CASE_P(ATypeParamDeathTest); + +TYPED_TEST_P(ATypeParamDeathTest, ShouldRunFirst) { +} + +REGISTER_TYPED_TEST_CASE_P(ATypeParamDeathTest, ShouldRunFirst); + +INSTANTIATE_TYPED_TEST_CASE_P(My, ATypeParamDeathTest, NumericTypes); + +# endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_HAS_DEATH_TEST + +// Tests various failure conditions of +// EXPECT_{,NON}FATAL_FAILURE{,_ON_ALL_THREADS}. +class ExpectFailureTest : public testing::Test { + public: // Must be public and not protected due to a bug in g++ 3.4.2. + enum FailureMode { + FATAL_FAILURE, + NONFATAL_FAILURE + }; + static void AddFailure(FailureMode failure) { + if (failure == FATAL_FAILURE) { + FAIL() << "Expected fatal failure."; + } else { + ADD_FAILURE() << "Expected non-fatal failure."; + } + } +}; + +TEST_F(ExpectFailureTest, ExpectFatalFailure) { + // Expected fatal failure, but succeeds. + printf("(expecting 1 failure)\n"); + EXPECT_FATAL_FAILURE(SUCCEED(), "Expected fatal failure."); + // Expected fatal failure, but got a non-fatal failure. + printf("(expecting 1 failure)\n"); + EXPECT_FATAL_FAILURE(AddFailure(NONFATAL_FAILURE), "Expected non-fatal " + "failure."); + // Wrong message. + printf("(expecting 1 failure)\n"); + EXPECT_FATAL_FAILURE(AddFailure(FATAL_FAILURE), "Some other fatal failure " + "expected."); +} + +TEST_F(ExpectFailureTest, ExpectNonFatalFailure) { + // Expected non-fatal failure, but succeeds. + printf("(expecting 1 failure)\n"); + EXPECT_NONFATAL_FAILURE(SUCCEED(), "Expected non-fatal failure."); + // Expected non-fatal failure, but got a fatal failure. + printf("(expecting 1 failure)\n"); + EXPECT_NONFATAL_FAILURE(AddFailure(FATAL_FAILURE), "Expected fatal failure."); + // Wrong message. + printf("(expecting 1 failure)\n"); + EXPECT_NONFATAL_FAILURE(AddFailure(NONFATAL_FAILURE), "Some other non-fatal " + "failure."); +} + +#if GTEST_IS_THREADSAFE + +class ExpectFailureWithThreadsTest : public ExpectFailureTest { + protected: + static void AddFailureInOtherThread(FailureMode failure) { + ThreadWithParam thread(&AddFailure, failure, NULL); + thread.Join(); + } +}; + +TEST_F(ExpectFailureWithThreadsTest, ExpectFatalFailure) { + // We only intercept the current thread. + printf("(expecting 2 failures)\n"); + EXPECT_FATAL_FAILURE(AddFailureInOtherThread(FATAL_FAILURE), + "Expected fatal failure."); +} + +TEST_F(ExpectFailureWithThreadsTest, ExpectNonFatalFailure) { + // We only intercept the current thread. + printf("(expecting 2 failures)\n"); + EXPECT_NONFATAL_FAILURE(AddFailureInOtherThread(NONFATAL_FAILURE), + "Expected non-fatal failure."); +} + +typedef ExpectFailureWithThreadsTest ScopedFakeTestPartResultReporterTest; + +// Tests that the ScopedFakeTestPartResultReporter only catches failures from +// the current thread if it is instantiated with INTERCEPT_ONLY_CURRENT_THREAD. +TEST_F(ScopedFakeTestPartResultReporterTest, InterceptOnlyCurrentThread) { + printf("(expecting 2 failures)\n"); + TestPartResultArray results; + { + ScopedFakeTestPartResultReporter reporter( + ScopedFakeTestPartResultReporter::INTERCEPT_ONLY_CURRENT_THREAD, + &results); + AddFailureInOtherThread(FATAL_FAILURE); + AddFailureInOtherThread(NONFATAL_FAILURE); + } + // The two failures should not have been intercepted. + EXPECT_EQ(0, results.size()) << "This shouldn't fail."; +} + +#endif // GTEST_IS_THREADSAFE + +TEST_F(ExpectFailureTest, ExpectFatalFailureOnAllThreads) { + // Expected fatal failure, but succeeds. + printf("(expecting 1 failure)\n"); + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(SUCCEED(), "Expected fatal failure."); + // Expected fatal failure, but got a non-fatal failure. + printf("(expecting 1 failure)\n"); + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(AddFailure(NONFATAL_FAILURE), + "Expected non-fatal failure."); + // Wrong message. + printf("(expecting 1 failure)\n"); + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(AddFailure(FATAL_FAILURE), + "Some other fatal failure expected."); +} + +TEST_F(ExpectFailureTest, ExpectNonFatalFailureOnAllThreads) { + // Expected non-fatal failure, but succeeds. + printf("(expecting 1 failure)\n"); + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(SUCCEED(), "Expected non-fatal " + "failure."); + // Expected non-fatal failure, but got a fatal failure. + printf("(expecting 1 failure)\n"); + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(AddFailure(FATAL_FAILURE), + "Expected fatal failure."); + // Wrong message. + printf("(expecting 1 failure)\n"); + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(AddFailure(NONFATAL_FAILURE), + "Some other non-fatal failure."); +} + + +// Two test environments for testing testing::AddGlobalTestEnvironment(). + +class FooEnvironment : public testing::Environment { + public: + virtual void SetUp() { + printf("%s", "FooEnvironment::SetUp() called.\n"); + } + + virtual void TearDown() { + printf("%s", "FooEnvironment::TearDown() called.\n"); + FAIL() << "Expected fatal failure."; + } +}; + +class BarEnvironment : public testing::Environment { + public: + virtual void SetUp() { + printf("%s", "BarEnvironment::SetUp() called.\n"); + } + + virtual void TearDown() { + printf("%s", "BarEnvironment::TearDown() called.\n"); + ADD_FAILURE() << "Expected non-fatal failure."; + } +}; + +bool GTEST_FLAG(internal_skip_environment_and_ad_hoc_tests) = false; + +// The main function. +// +// The idea is to use Google Test to run all the tests we have defined (some +// of them are intended to fail), and then compare the test results +// with the "golden" file. +int main(int argc, char **argv) { + testing::GTEST_FLAG(print_time) = false; + + // We just run the tests, knowing some of them are intended to fail. + // We will use a separate Python script to compare the output of + // this program with the golden file. + + // It's hard to test InitGoogleTest() directly, as it has many + // global side effects. The following line serves as a sanity test + // for it. + testing::InitGoogleTest(&argc, argv); + if (argc >= 2 && + (std::string(argv[1]) == + "--gtest_internal_skip_environment_and_ad_hoc_tests")) + GTEST_FLAG(internal_skip_environment_and_ad_hoc_tests) = true; + +#if GTEST_HAS_DEATH_TEST + if (testing::internal::GTEST_FLAG(internal_run_death_test) != "") { + // Skip the usual output capturing if we're running as the child + // process of an threadsafe-style death test. +# if GTEST_OS_WINDOWS + posix::FReopen("nul:", "w", stdout); +# else + posix::FReopen("/dev/null", "w", stdout); +# endif // GTEST_OS_WINDOWS + return RUN_ALL_TESTS(); + } +#endif // GTEST_HAS_DEATH_TEST + + if (GTEST_FLAG(internal_skip_environment_and_ad_hoc_tests)) + return RUN_ALL_TESTS(); + + // Registers two global test environments. + // The golden file verifies that they are set up in the order they + // are registered, and torn down in the reverse order. + testing::AddGlobalTestEnvironment(new FooEnvironment); + testing::AddGlobalTestEnvironment(new BarEnvironment); + + return RunAllTests(); +} diff --git a/external/gtest/test/gtest_output_test_golden_lin.txt b/external/gtest/test/gtest_output_test_golden_lin.txt new file mode 100644 index 0000000000..960eedce24 --- /dev/null +++ b/external/gtest/test/gtest_output_test_golden_lin.txt @@ -0,0 +1,720 @@ +The non-test part of the code is expected to have 2 failures. + +gtest_output_test_.cc:#: Failure +Value of: false + Actual: false +Expected: true +gtest_output_test_.cc:#: Failure +Value of: 3 +Expected: 2 +[==========] Running 63 tests from 28 test cases. +[----------] Global test environment set-up. +FooEnvironment::SetUp() called. +BarEnvironment::SetUp() called. +[----------] 1 test from ADeathTest +[ RUN ] ADeathTest.ShouldRunFirst +[ OK ] ADeathTest.ShouldRunFirst +[----------] 1 test from ATypedDeathTest/0, where TypeParam = int +[ RUN ] ATypedDeathTest/0.ShouldRunFirst +[ OK ] ATypedDeathTest/0.ShouldRunFirst +[----------] 1 test from ATypedDeathTest/1, where TypeParam = double +[ RUN ] ATypedDeathTest/1.ShouldRunFirst +[ OK ] ATypedDeathTest/1.ShouldRunFirst +[----------] 1 test from My/ATypeParamDeathTest/0, where TypeParam = int +[ RUN ] My/ATypeParamDeathTest/0.ShouldRunFirst +[ OK ] My/ATypeParamDeathTest/0.ShouldRunFirst +[----------] 1 test from My/ATypeParamDeathTest/1, where TypeParam = double +[ RUN ] My/ATypeParamDeathTest/1.ShouldRunFirst +[ OK ] My/ATypeParamDeathTest/1.ShouldRunFirst +[----------] 2 tests from PassingTest +[ RUN ] PassingTest.PassingTest1 +[ OK ] PassingTest.PassingTest1 +[ RUN ] PassingTest.PassingTest2 +[ OK ] PassingTest.PassingTest2 +[----------] 1 test from NonfatalFailureTest +[ RUN ] NonfatalFailureTest.EscapesStringOperands +gtest_output_test_.cc:#: Failure +Value of: actual + Actual: "actual \"string\"" +Expected: kGoldenString +Which is: "\"Line" +gtest_output_test_.cc:#: Failure +Value of: actual + Actual: "actual \"string\"" +Expected: golden +Which is: "\"Line" +[ FAILED ] NonfatalFailureTest.EscapesStringOperands +[----------] 3 tests from FatalFailureTest +[ RUN ] FatalFailureTest.FatalFailureInSubroutine +(expecting a failure that x should be 1) +gtest_output_test_.cc:#: Failure +Value of: x + Actual: 2 +Expected: 1 +[ FAILED ] FatalFailureTest.FatalFailureInSubroutine +[ RUN ] FatalFailureTest.FatalFailureInNestedSubroutine +(expecting a failure that x should be 1) +gtest_output_test_.cc:#: Failure +Value of: x + Actual: 2 +Expected: 1 +[ FAILED ] FatalFailureTest.FatalFailureInNestedSubroutine +[ RUN ] FatalFailureTest.NonfatalFailureInSubroutine +(expecting a failure on false) +gtest_output_test_.cc:#: Failure +Value of: false + Actual: false +Expected: true +[ FAILED ] FatalFailureTest.NonfatalFailureInSubroutine +[----------] 1 test from LoggingTest +[ RUN ] LoggingTest.InterleavingLoggingAndAssertions +(expecting 2 failures on (3) >= (a[i])) +i == 0 +i == 1 +gtest_output_test_.cc:#: Failure +Expected: (3) >= (a[i]), actual: 3 vs 9 +i == 2 +i == 3 +gtest_output_test_.cc:#: Failure +Expected: (3) >= (a[i]), actual: 3 vs 6 +[ FAILED ] LoggingTest.InterleavingLoggingAndAssertions +[----------] 6 tests from SCOPED_TRACETest +[ RUN ] SCOPED_TRACETest.ObeysScopes +(expected to fail) +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and shouldn't have a trace. +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and should have a trace. +Google Test trace: +gtest_output_test_.cc:#: Expected trace +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and shouldn't have a trace. +[ FAILED ] SCOPED_TRACETest.ObeysScopes +[ RUN ] SCOPED_TRACETest.WorksInLoop +(expected to fail) +gtest_output_test_.cc:#: Failure +Value of: n + Actual: 1 +Expected: 2 +Google Test trace: +gtest_output_test_.cc:#: i = 1 +gtest_output_test_.cc:#: Failure +Value of: n + Actual: 2 +Expected: 1 +Google Test trace: +gtest_output_test_.cc:#: i = 2 +[ FAILED ] SCOPED_TRACETest.WorksInLoop +[ RUN ] SCOPED_TRACETest.WorksInSubroutine +(expected to fail) +gtest_output_test_.cc:#: Failure +Value of: n + Actual: 1 +Expected: 2 +Google Test trace: +gtest_output_test_.cc:#: n = 1 +gtest_output_test_.cc:#: Failure +Value of: n + Actual: 2 +Expected: 1 +Google Test trace: +gtest_output_test_.cc:#: n = 2 +[ FAILED ] SCOPED_TRACETest.WorksInSubroutine +[ RUN ] SCOPED_TRACETest.CanBeNested +(expected to fail) +gtest_output_test_.cc:#: Failure +Value of: n + Actual: 2 +Expected: 1 +Google Test trace: +gtest_output_test_.cc:#: n = 2 +gtest_output_test_.cc:#: +[ FAILED ] SCOPED_TRACETest.CanBeNested +[ RUN ] SCOPED_TRACETest.CanBeRepeated +(expected to fail) +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and should contain trace point A. +Google Test trace: +gtest_output_test_.cc:#: A +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and should contain trace point A and B. +Google Test trace: +gtest_output_test_.cc:#: B +gtest_output_test_.cc:#: A +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and should contain trace point A, B, and C. +Google Test trace: +gtest_output_test_.cc:#: C +gtest_output_test_.cc:#: B +gtest_output_test_.cc:#: A +gtest_output_test_.cc:#: Failure +Failed +This failure is expected, and should contain trace point A, B, and D. +Google Test trace: +gtest_output_test_.cc:#: D +gtest_output_test_.cc:#: B +gtest_output_test_.cc:#: A +[ FAILED ] SCOPED_TRACETest.CanBeRepeated +[ RUN ] SCOPED_TRACETest.WorksConcurrently +(expecting 6 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected failure #1 (in thread B, only trace B alive). +Google Test trace: +gtest_output_test_.cc:#: Trace B +gtest_output_test_.cc:#: Failure +Failed +Expected failure #2 (in thread A, trace A & B both alive). +Google Test trace: +gtest_output_test_.cc:#: Trace A +gtest_output_test_.cc:#: Failure +Failed +Expected failure #3 (in thread B, trace A & B both alive). +Google Test trace: +gtest_output_test_.cc:#: Trace B +gtest_output_test_.cc:#: Failure +Failed +Expected failure #4 (in thread B, only trace A alive). +gtest_output_test_.cc:#: Failure +Failed +Expected failure #5 (in thread A, only trace A alive). +Google Test trace: +gtest_output_test_.cc:#: Trace A +gtest_output_test_.cc:#: Failure +Failed +Expected failure #6 (in thread A, no trace alive). +[ FAILED ] SCOPED_TRACETest.WorksConcurrently +[----------] 1 test from NonFatalFailureInFixtureConstructorTest +[ RUN ] NonFatalFailureInFixtureConstructorTest.FailureInConstructor +(expecting 5 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected failure #1, in the test fixture c'tor. +gtest_output_test_.cc:#: Failure +Failed +Expected failure #2, in SetUp(). +gtest_output_test_.cc:#: Failure +Failed +Expected failure #3, in the test body. +gtest_output_test_.cc:#: Failure +Failed +Expected failure #4, in TearDown. +gtest_output_test_.cc:#: Failure +Failed +Expected failure #5, in the test fixture d'tor. +[ FAILED ] NonFatalFailureInFixtureConstructorTest.FailureInConstructor +[----------] 1 test from FatalFailureInFixtureConstructorTest +[ RUN ] FatalFailureInFixtureConstructorTest.FailureInConstructor +(expecting 2 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected failure #1, in the test fixture c'tor. +gtest_output_test_.cc:#: Failure +Failed +Expected failure #2, in the test fixture d'tor. +[ FAILED ] FatalFailureInFixtureConstructorTest.FailureInConstructor +[----------] 1 test from NonFatalFailureInSetUpTest +[ RUN ] NonFatalFailureInSetUpTest.FailureInSetUp +(expecting 4 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected failure #1, in SetUp(). +gtest_output_test_.cc:#: Failure +Failed +Expected failure #2, in the test function. +gtest_output_test_.cc:#: Failure +Failed +Expected failure #3, in TearDown(). +gtest_output_test_.cc:#: Failure +Failed +Expected failure #4, in the test fixture d'tor. +[ FAILED ] NonFatalFailureInSetUpTest.FailureInSetUp +[----------] 1 test from FatalFailureInSetUpTest +[ RUN ] FatalFailureInSetUpTest.FailureInSetUp +(expecting 3 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected failure #1, in SetUp(). +gtest_output_test_.cc:#: Failure +Failed +Expected failure #2, in TearDown(). +gtest_output_test_.cc:#: Failure +Failed +Expected failure #3, in the test fixture d'tor. +[ FAILED ] FatalFailureInSetUpTest.FailureInSetUp +[----------] 1 test from AddFailureAtTest +[ RUN ] AddFailureAtTest.MessageContainsSpecifiedFileAndLineNumber +foo.cc:42: Failure +Failed +Expected failure in foo.cc +[ FAILED ] AddFailureAtTest.MessageContainsSpecifiedFileAndLineNumber +[----------] 4 tests from MixedUpTestCaseTest +[ RUN ] MixedUpTestCaseTest.FirstTestFromNamespaceFoo +[ OK ] MixedUpTestCaseTest.FirstTestFromNamespaceFoo +[ RUN ] MixedUpTestCaseTest.SecondTestFromNamespaceFoo +[ OK ] MixedUpTestCaseTest.SecondTestFromNamespaceFoo +[ RUN ] MixedUpTestCaseTest.ThisShouldFail +gtest.cc:#: Failure +Failed +All tests in the same test case must use the same test fixture +class. However, in test case MixedUpTestCaseTest, +you defined test FirstTestFromNamespaceFoo and test ThisShouldFail +using two different test fixture classes. This can happen if +the two classes are from different namespaces or translation +units and have the same name. You should probably rename one +of the classes to put the tests into different test cases. +[ FAILED ] MixedUpTestCaseTest.ThisShouldFail +[ RUN ] MixedUpTestCaseTest.ThisShouldFailToo +gtest.cc:#: Failure +Failed +All tests in the same test case must use the same test fixture +class. However, in test case MixedUpTestCaseTest, +you defined test FirstTestFromNamespaceFoo and test ThisShouldFailToo +using two different test fixture classes. This can happen if +the two classes are from different namespaces or translation +units and have the same name. You should probably rename one +of the classes to put the tests into different test cases. +[ FAILED ] MixedUpTestCaseTest.ThisShouldFailToo +[----------] 2 tests from MixedUpTestCaseWithSameTestNameTest +[ RUN ] MixedUpTestCaseWithSameTestNameTest.TheSecondTestWithThisNameShouldFail +[ OK ] MixedUpTestCaseWithSameTestNameTest.TheSecondTestWithThisNameShouldFail +[ RUN ] MixedUpTestCaseWithSameTestNameTest.TheSecondTestWithThisNameShouldFail +gtest.cc:#: Failure +Failed +All tests in the same test case must use the same test fixture +class. However, in test case MixedUpTestCaseWithSameTestNameTest, +you defined test TheSecondTestWithThisNameShouldFail and test TheSecondTestWithThisNameShouldFail +using two different test fixture classes. This can happen if +the two classes are from different namespaces or translation +units and have the same name. You should probably rename one +of the classes to put the tests into different test cases. +[ FAILED ] MixedUpTestCaseWithSameTestNameTest.TheSecondTestWithThisNameShouldFail +[----------] 2 tests from TEST_F_before_TEST_in_same_test_case +[ RUN ] TEST_F_before_TEST_in_same_test_case.DefinedUsingTEST_F +[ OK ] TEST_F_before_TEST_in_same_test_case.DefinedUsingTEST_F +[ RUN ] TEST_F_before_TEST_in_same_test_case.DefinedUsingTESTAndShouldFail +gtest.cc:#: Failure +Failed +All tests in the same test case must use the same test fixture +class, so mixing TEST_F and TEST in the same test case is +illegal. In test case TEST_F_before_TEST_in_same_test_case, +test DefinedUsingTEST_F is defined using TEST_F but +test DefinedUsingTESTAndShouldFail is defined using TEST. You probably +want to change the TEST to TEST_F or move it to another test +case. +[ FAILED ] TEST_F_before_TEST_in_same_test_case.DefinedUsingTESTAndShouldFail +[----------] 2 tests from TEST_before_TEST_F_in_same_test_case +[ RUN ] TEST_before_TEST_F_in_same_test_case.DefinedUsingTEST +[ OK ] TEST_before_TEST_F_in_same_test_case.DefinedUsingTEST +[ RUN ] TEST_before_TEST_F_in_same_test_case.DefinedUsingTEST_FAndShouldFail +gtest.cc:#: Failure +Failed +All tests in the same test case must use the same test fixture +class, so mixing TEST_F and TEST in the same test case is +illegal. In test case TEST_before_TEST_F_in_same_test_case, +test DefinedUsingTEST_FAndShouldFail is defined using TEST_F but +test DefinedUsingTEST is defined using TEST. You probably +want to change the TEST to TEST_F or move it to another test +case. +[ FAILED ] TEST_before_TEST_F_in_same_test_case.DefinedUsingTEST_FAndShouldFail +[----------] 8 tests from ExpectNonfatalFailureTest +[ RUN ] ExpectNonfatalFailureTest.CanReferenceGlobalVariables +[ OK ] ExpectNonfatalFailureTest.CanReferenceGlobalVariables +[ RUN ] ExpectNonfatalFailureTest.CanReferenceLocalVariables +[ OK ] ExpectNonfatalFailureTest.CanReferenceLocalVariables +[ RUN ] ExpectNonfatalFailureTest.SucceedsWhenThereIsOneNonfatalFailure +[ OK ] ExpectNonfatalFailureTest.SucceedsWhenThereIsOneNonfatalFailure +[ RUN ] ExpectNonfatalFailureTest.FailsWhenThereIsNoNonfatalFailure +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: 0 failures +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenThereIsNoNonfatalFailure +[ RUN ] ExpectNonfatalFailureTest.FailsWhenThereAreTwoNonfatalFailures +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: 2 failures +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure 1. + +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure 2. + +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenThereAreTwoNonfatalFailures +[ RUN ] ExpectNonfatalFailureTest.FailsWhenThereIsOneFatalFailure +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenThereIsOneFatalFailure +[ RUN ] ExpectNonfatalFailureTest.FailsWhenStatementReturns +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: 0 failures +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenStatementReturns +[ RUN ] ExpectNonfatalFailureTest.FailsWhenStatementThrows +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: 0 failures +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenStatementThrows +[----------] 8 tests from ExpectFatalFailureTest +[ RUN ] ExpectFatalFailureTest.CanReferenceGlobalVariables +[ OK ] ExpectFatalFailureTest.CanReferenceGlobalVariables +[ RUN ] ExpectFatalFailureTest.CanReferenceLocalStaticVariables +[ OK ] ExpectFatalFailureTest.CanReferenceLocalStaticVariables +[ RUN ] ExpectFatalFailureTest.SucceedsWhenThereIsOneFatalFailure +[ OK ] ExpectFatalFailureTest.SucceedsWhenThereIsOneFatalFailure +[ RUN ] ExpectFatalFailureTest.FailsWhenThereIsNoFatalFailure +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: 0 failures +[ FAILED ] ExpectFatalFailureTest.FailsWhenThereIsNoFatalFailure +[ RUN ] ExpectFatalFailureTest.FailsWhenThereAreTwoFatalFailures +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: 2 failures +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +[ FAILED ] ExpectFatalFailureTest.FailsWhenThereAreTwoFatalFailures +[ RUN ] ExpectFatalFailureTest.FailsWhenThereIsOneNonfatalFailure +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure. + +[ FAILED ] ExpectFatalFailureTest.FailsWhenThereIsOneNonfatalFailure +[ RUN ] ExpectFatalFailureTest.FailsWhenStatementReturns +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: 0 failures +[ FAILED ] ExpectFatalFailureTest.FailsWhenStatementReturns +[ RUN ] ExpectFatalFailureTest.FailsWhenStatementThrows +(expecting a failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: 0 failures +[ FAILED ] ExpectFatalFailureTest.FailsWhenStatementThrows +[----------] 2 tests from TypedTest/0, where TypeParam = int +[ RUN ] TypedTest/0.Success +[ OK ] TypedTest/0.Success +[ RUN ] TypedTest/0.Failure +gtest_output_test_.cc:#: Failure +Value of: TypeParam() + Actual: 0 +Expected: 1 +Expected failure +[ FAILED ] TypedTest/0.Failure, where TypeParam = int +[----------] 2 tests from Unsigned/TypedTestP/0, where TypeParam = unsigned char +[ RUN ] Unsigned/TypedTestP/0.Success +[ OK ] Unsigned/TypedTestP/0.Success +[ RUN ] Unsigned/TypedTestP/0.Failure +gtest_output_test_.cc:#: Failure +Value of: TypeParam() + Actual: '\0' +Expected: 1U +Which is: 1 +Expected failure +[ FAILED ] Unsigned/TypedTestP/0.Failure, where TypeParam = unsigned char +[----------] 2 tests from Unsigned/TypedTestP/1, where TypeParam = unsigned int +[ RUN ] Unsigned/TypedTestP/1.Success +[ OK ] Unsigned/TypedTestP/1.Success +[ RUN ] Unsigned/TypedTestP/1.Failure +gtest_output_test_.cc:#: Failure +Value of: TypeParam() + Actual: 0 +Expected: 1U +Which is: 1 +Expected failure +[ FAILED ] Unsigned/TypedTestP/1.Failure, where TypeParam = unsigned int +[----------] 4 tests from ExpectFailureTest +[ RUN ] ExpectFailureTest.ExpectFatalFailure +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: +gtest_output_test_.cc:#: Success: +Succeeded + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure. + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 fatal failure containing "Some other fatal failure expected." + Actual: +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +[ FAILED ] ExpectFailureTest.ExpectFatalFailure +[ RUN ] ExpectFailureTest.ExpectNonFatalFailure +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: +gtest_output_test_.cc:#: Success: +Succeeded + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure containing "Some other non-fatal failure." + Actual: +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure. + +[ FAILED ] ExpectFailureTest.ExpectNonFatalFailure +[ RUN ] ExpectFailureTest.ExpectFatalFailureOnAllThreads +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: +gtest_output_test_.cc:#: Success: +Succeeded + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure. + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 fatal failure containing "Some other fatal failure expected." + Actual: +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +[ FAILED ] ExpectFailureTest.ExpectFatalFailureOnAllThreads +[ RUN ] ExpectFailureTest.ExpectNonFatalFailureOnAllThreads +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: +gtest_output_test_.cc:#: Success: +Succeeded + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: +gtest_output_test_.cc:#: Fatal failure: +Failed +Expected fatal failure. + +(expecting 1 failure) +gtest.cc:#: Failure +Expected: 1 non-fatal failure containing "Some other non-fatal failure." + Actual: +gtest_output_test_.cc:#: Non-fatal failure: +Failed +Expected non-fatal failure. + +[ FAILED ] ExpectFailureTest.ExpectNonFatalFailureOnAllThreads +[----------] 2 tests from ExpectFailureWithThreadsTest +[ RUN ] ExpectFailureWithThreadsTest.ExpectFatalFailure +(expecting 2 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected fatal failure. +gtest.cc:#: Failure +Expected: 1 fatal failure + Actual: 0 failures +[ FAILED ] ExpectFailureWithThreadsTest.ExpectFatalFailure +[ RUN ] ExpectFailureWithThreadsTest.ExpectNonFatalFailure +(expecting 2 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected non-fatal failure. +gtest.cc:#: Failure +Expected: 1 non-fatal failure + Actual: 0 failures +[ FAILED ] ExpectFailureWithThreadsTest.ExpectNonFatalFailure +[----------] 1 test from ScopedFakeTestPartResultReporterTest +[ RUN ] ScopedFakeTestPartResultReporterTest.InterceptOnlyCurrentThread +(expecting 2 failures) +gtest_output_test_.cc:#: Failure +Failed +Expected fatal failure. +gtest_output_test_.cc:#: Failure +Failed +Expected non-fatal failure. +[ FAILED ] ScopedFakeTestPartResultReporterTest.InterceptOnlyCurrentThread +[----------] 1 test from PrintingFailingParams/FailingParamTest +[ RUN ] PrintingFailingParams/FailingParamTest.Fails/0 +gtest_output_test_.cc:#: Failure +Value of: GetParam() + Actual: 2 +Expected: 1 +[ FAILED ] PrintingFailingParams/FailingParamTest.Fails/0, where GetParam() = 2 +[----------] Global test environment tear-down +BarEnvironment::TearDown() called. +gtest_output_test_.cc:#: Failure +Failed +Expected non-fatal failure. +FooEnvironment::TearDown() called. +gtest_output_test_.cc:#: Failure +Failed +Expected fatal failure. +[==========] 63 tests from 28 test cases ran. +[ PASSED ] 21 tests. +[ FAILED ] 42 tests, listed below: +[ FAILED ] NonfatalFailureTest.EscapesStringOperands +[ FAILED ] FatalFailureTest.FatalFailureInSubroutine +[ FAILED ] FatalFailureTest.FatalFailureInNestedSubroutine +[ FAILED ] FatalFailureTest.NonfatalFailureInSubroutine +[ FAILED ] LoggingTest.InterleavingLoggingAndAssertions +[ FAILED ] SCOPED_TRACETest.ObeysScopes +[ FAILED ] SCOPED_TRACETest.WorksInLoop +[ FAILED ] SCOPED_TRACETest.WorksInSubroutine +[ FAILED ] SCOPED_TRACETest.CanBeNested +[ FAILED ] SCOPED_TRACETest.CanBeRepeated +[ FAILED ] SCOPED_TRACETest.WorksConcurrently +[ FAILED ] NonFatalFailureInFixtureConstructorTest.FailureInConstructor +[ FAILED ] FatalFailureInFixtureConstructorTest.FailureInConstructor +[ FAILED ] NonFatalFailureInSetUpTest.FailureInSetUp +[ FAILED ] FatalFailureInSetUpTest.FailureInSetUp +[ FAILED ] AddFailureAtTest.MessageContainsSpecifiedFileAndLineNumber +[ FAILED ] MixedUpTestCaseTest.ThisShouldFail +[ FAILED ] MixedUpTestCaseTest.ThisShouldFailToo +[ FAILED ] MixedUpTestCaseWithSameTestNameTest.TheSecondTestWithThisNameShouldFail +[ FAILED ] TEST_F_before_TEST_in_same_test_case.DefinedUsingTESTAndShouldFail +[ FAILED ] TEST_before_TEST_F_in_same_test_case.DefinedUsingTEST_FAndShouldFail +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenThereIsNoNonfatalFailure +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenThereAreTwoNonfatalFailures +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenThereIsOneFatalFailure +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenStatementReturns +[ FAILED ] ExpectNonfatalFailureTest.FailsWhenStatementThrows +[ FAILED ] ExpectFatalFailureTest.FailsWhenThereIsNoFatalFailure +[ FAILED ] ExpectFatalFailureTest.FailsWhenThereAreTwoFatalFailures +[ FAILED ] ExpectFatalFailureTest.FailsWhenThereIsOneNonfatalFailure +[ FAILED ] ExpectFatalFailureTest.FailsWhenStatementReturns +[ FAILED ] ExpectFatalFailureTest.FailsWhenStatementThrows +[ FAILED ] TypedTest/0.Failure, where TypeParam = int +[ FAILED ] Unsigned/TypedTestP/0.Failure, where TypeParam = unsigned char +[ FAILED ] Unsigned/TypedTestP/1.Failure, where TypeParam = unsigned int +[ FAILED ] ExpectFailureTest.ExpectFatalFailure +[ FAILED ] ExpectFailureTest.ExpectNonFatalFailure +[ FAILED ] ExpectFailureTest.ExpectFatalFailureOnAllThreads +[ FAILED ] ExpectFailureTest.ExpectNonFatalFailureOnAllThreads +[ FAILED ] ExpectFailureWithThreadsTest.ExpectFatalFailure +[ FAILED ] ExpectFailureWithThreadsTest.ExpectNonFatalFailure +[ FAILED ] ScopedFakeTestPartResultReporterTest.InterceptOnlyCurrentThread +[ FAILED ] PrintingFailingParams/FailingParamTest.Fails/0, where GetParam() = 2 + +42 FAILED TESTS + YOU HAVE 1 DISABLED TEST + +Note: Google Test filter = FatalFailureTest.*:LoggingTest.* +[==========] Running 4 tests from 2 test cases. +[----------] Global test environment set-up. +[----------] 3 tests from FatalFailureTest +[ RUN ] FatalFailureTest.FatalFailureInSubroutine +(expecting a failure that x should be 1) +gtest_output_test_.cc:#: Failure +Value of: x + Actual: 2 +Expected: 1 +[ FAILED ] FatalFailureTest.FatalFailureInSubroutine (? ms) +[ RUN ] FatalFailureTest.FatalFailureInNestedSubroutine +(expecting a failure that x should be 1) +gtest_output_test_.cc:#: Failure +Value of: x + Actual: 2 +Expected: 1 +[ FAILED ] FatalFailureTest.FatalFailureInNestedSubroutine (? ms) +[ RUN ] FatalFailureTest.NonfatalFailureInSubroutine +(expecting a failure on false) +gtest_output_test_.cc:#: Failure +Value of: false + Actual: false +Expected: true +[ FAILED ] FatalFailureTest.NonfatalFailureInSubroutine (? ms) +[----------] 3 tests from FatalFailureTest (? ms total) + +[----------] 1 test from LoggingTest +[ RUN ] LoggingTest.InterleavingLoggingAndAssertions +(expecting 2 failures on (3) >= (a[i])) +i == 0 +i == 1 +gtest_output_test_.cc:#: Failure +Expected: (3) >= (a[i]), actual: 3 vs 9 +i == 2 +i == 3 +gtest_output_test_.cc:#: Failure +Expected: (3) >= (a[i]), actual: 3 vs 6 +[ FAILED ] LoggingTest.InterleavingLoggingAndAssertions (? ms) +[----------] 1 test from LoggingTest (? ms total) + +[----------] Global test environment tear-down +[==========] 4 tests from 2 test cases ran. (? ms total) +[ PASSED ] 0 tests. +[ FAILED ] 4 tests, listed below: +[ FAILED ] FatalFailureTest.FatalFailureInSubroutine +[ FAILED ] FatalFailureTest.FatalFailureInNestedSubroutine +[ FAILED ] FatalFailureTest.NonfatalFailureInSubroutine +[ FAILED ] LoggingTest.InterleavingLoggingAndAssertions + + 4 FAILED TESTS +Note: Google Test filter = *DISABLED_* +[==========] Running 1 test from 1 test case. +[----------] Global test environment set-up. +[----------] 1 test from DisabledTestsWarningTest +[ RUN ] DisabledTestsWarningTest.DISABLED_AlsoRunDisabledTestsFlagSuppressesWarning +[ OK ] DisabledTestsWarningTest.DISABLED_AlsoRunDisabledTestsFlagSuppressesWarning +[----------] Global test environment tear-down +[==========] 1 test from 1 test case ran. +[ PASSED ] 1 test. +Note: Google Test filter = PassingTest.* +Note: This is test shard 2 of 2. +[==========] Running 1 test from 1 test case. +[----------] Global test environment set-up. +[----------] 1 test from PassingTest +[ RUN ] PassingTest.PassingTest2 +[ OK ] PassingTest.PassingTest2 +[----------] Global test environment tear-down +[==========] 1 test from 1 test case ran. +[ PASSED ] 1 test. diff --git a/external/gtest/test/gtest_pred_impl_unittest.cc b/external/gtest/test/gtest_pred_impl_unittest.cc new file mode 100644 index 0000000000..a84eff860a --- /dev/null +++ b/external/gtest/test/gtest_pred_impl_unittest.cc @@ -0,0 +1,2427 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! + +// Regression test for gtest_pred_impl.h +// +// This file is generated by a script and quite long. If you intend to +// learn how Google Test works by reading its unit tests, read +// gtest_unittest.cc instead. +// +// This is intended as a regression test for the Google Test predicate +// assertions. We compile it as part of the gtest_unittest target +// only to keep the implementation tidy and compact, as it is quite +// involved to set up the stage for testing Google Test using Google +// Test itself. +// +// Currently, gtest_unittest takes ~11 seconds to run in the testing +// daemon. In the future, if it grows too large and needs much more +// time to finish, we should consider separating this file into a +// stand-alone regression test. + +#include + +#include "gtest/gtest.h" +#include "gtest/gtest-spi.h" + +// A user-defined data type. +struct Bool { + explicit Bool(int val) : value(val != 0) {} + + bool operator>(int n) const { return value > Bool(n).value; } + + Bool operator+(const Bool& rhs) const { return Bool(value + rhs.value); } + + bool operator==(const Bool& rhs) const { return value == rhs.value; } + + bool value; +}; + +// Enables Bool to be used in assertions. +std::ostream& operator<<(std::ostream& os, const Bool& x) { + return os << (x.value ? "true" : "false"); +} + +// Sample functions/functors for testing unary predicate assertions. + +// A unary predicate function. +template +bool PredFunction1(T1 v1) { + return v1 > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction1Int(int v1) { + return v1 > 0; +} +bool PredFunction1Bool(Bool v1) { + return v1 > 0; +} + +// A unary predicate functor. +struct PredFunctor1 { + template + bool operator()(const T1& v1) { + return v1 > 0; + } +}; + +// A unary predicate-formatter function. +template +testing::AssertionResult PredFormatFunction1(const char* e1, + const T1& v1) { + if (PredFunction1(v1)) + return testing::AssertionSuccess(); + + return testing::AssertionFailure() + << e1 + << " is expected to be positive, but evaluates to " + << v1 << "."; +} + +// A unary predicate-formatter functor. +struct PredFormatFunctor1 { + template + testing::AssertionResult operator()(const char* e1, + const T1& v1) const { + return PredFormatFunction1(e1, v1); + } +}; + +// Tests for {EXPECT|ASSERT}_PRED_FORMAT1. + +class Predicate1Test : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false; + n1_ = 0; + } + + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once. + EXPECT_EQ(1, n1_) << + "The predicate assertion didn't evaluate argument 2 " + "exactly once."; + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; + + static int n1_; +}; + +bool Predicate1Test::expected_to_finish_; +bool Predicate1Test::finished_; +int Predicate1Test::n1_; + +typedef Predicate1Test EXPECT_PRED_FORMAT1Test; +typedef Predicate1Test ASSERT_PRED_FORMAT1Test; +typedef Predicate1Test EXPECT_PRED1Test; +typedef Predicate1Test ASSERT_PRED1Test; + +// Tests a successful EXPECT_PRED1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED1Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED1(PredFunction1Int, + ++n1_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED1Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED1(PredFunction1Bool, + Bool(++n1_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED1Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED1(PredFunctor1(), + ++n1_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED1Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED1(PredFunctor1(), + Bool(++n1_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED1Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED1(PredFunction1Int, + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED1Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED1(PredFunction1Bool, + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED1Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED1(PredFunctor1(), + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED1Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED1(PredFunctor1(), + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED1Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED1(PredFunction1Int, + ++n1_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED1Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED1(PredFunction1Bool, + Bool(++n1_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED1Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED1(PredFunctor1(), + ++n1_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED1Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED1(PredFunctor1(), + Bool(++n1_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED1Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED1(PredFunction1Int, + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED1Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED1(PredFunction1Bool, + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED1Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED1(PredFunctor1(), + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED1Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED1(PredFunctor1(), + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a successful EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT1(PredFormatFunction1, + ++n1_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED_FORMAT1(PredFormatFunction1, + Bool(++n1_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT1(PredFormatFunctor1(), + ++n1_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED_FORMAT1(PredFormatFunctor1(), + Bool(++n1_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT1(PredFormatFunction1, + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT1(PredFormatFunction1, + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT1(PredFormatFunctor1(), + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT1Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT1(PredFormatFunctor1(), + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT1(PredFormatFunction1, + ++n1_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED_FORMAT1(PredFormatFunction1, + Bool(++n1_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT1(PredFormatFunctor1(), + ++n1_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED_FORMAT1(PredFormatFunctor1(), + Bool(++n1_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT1(PredFormatFunction1, + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT1(PredFormatFunction1, + Bool(n1_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT1(PredFormatFunctor1(), + n1_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT1 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT1Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT1(PredFormatFunctor1(), + Bool(n1_++)); + finished_ = true; + }, ""); +} +// Sample functions/functors for testing binary predicate assertions. + +// A binary predicate function. +template +bool PredFunction2(T1 v1, T2 v2) { + return v1 + v2 > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction2Int(int v1, int v2) { + return v1 + v2 > 0; +} +bool PredFunction2Bool(Bool v1, Bool v2) { + return v1 + v2 > 0; +} + +// A binary predicate functor. +struct PredFunctor2 { + template + bool operator()(const T1& v1, + const T2& v2) { + return v1 + v2 > 0; + } +}; + +// A binary predicate-formatter function. +template +testing::AssertionResult PredFormatFunction2(const char* e1, + const char* e2, + const T1& v1, + const T2& v2) { + if (PredFunction2(v1, v2)) + return testing::AssertionSuccess(); + + return testing::AssertionFailure() + << e1 << " + " << e2 + << " is expected to be positive, but evaluates to " + << v1 + v2 << "."; +} + +// A binary predicate-formatter functor. +struct PredFormatFunctor2 { + template + testing::AssertionResult operator()(const char* e1, + const char* e2, + const T1& v1, + const T2& v2) const { + return PredFormatFunction2(e1, e2, v1, v2); + } +}; + +// Tests for {EXPECT|ASSERT}_PRED_FORMAT2. + +class Predicate2Test : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false; + n1_ = n2_ = 0; + } + + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once. + EXPECT_EQ(1, n1_) << + "The predicate assertion didn't evaluate argument 2 " + "exactly once."; + EXPECT_EQ(1, n2_) << + "The predicate assertion didn't evaluate argument 3 " + "exactly once."; + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; + + static int n1_; + static int n2_; +}; + +bool Predicate2Test::expected_to_finish_; +bool Predicate2Test::finished_; +int Predicate2Test::n1_; +int Predicate2Test::n2_; + +typedef Predicate2Test EXPECT_PRED_FORMAT2Test; +typedef Predicate2Test ASSERT_PRED_FORMAT2Test; +typedef Predicate2Test EXPECT_PRED2Test; +typedef Predicate2Test ASSERT_PRED2Test; + +// Tests a successful EXPECT_PRED2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED2Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED2(PredFunction2Int, + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED2Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED2(PredFunction2Bool, + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED2Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED2(PredFunctor2(), + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED2Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED2(PredFunctor2(), + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED2Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED2(PredFunction2Int, + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED2Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED2(PredFunction2Bool, + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED2Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED2(PredFunctor2(), + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED2Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED2(PredFunctor2(), + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED2Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED2(PredFunction2Int, + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED2Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED2(PredFunction2Bool, + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED2Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED2(PredFunctor2(), + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED2Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED2(PredFunctor2(), + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED2Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED2(PredFunction2Int, + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED2Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED2(PredFunction2Bool, + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED2Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED2(PredFunctor2(), + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED2Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED2(PredFunctor2(), + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a successful EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT2(PredFormatFunction2, + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED_FORMAT2(PredFormatFunction2, + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT2(PredFormatFunctor2(), + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED_FORMAT2(PredFormatFunctor2(), + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(PredFormatFunction2, + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(PredFormatFunction2, + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(PredFormatFunctor2(), + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT2Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(PredFormatFunctor2(), + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT2(PredFormatFunction2, + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED_FORMAT2(PredFormatFunction2, + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT2(PredFormatFunctor2(), + ++n1_, + ++n2_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED_FORMAT2(PredFormatFunctor2(), + Bool(++n1_), + Bool(++n2_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT2(PredFormatFunction2, + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT2(PredFormatFunction2, + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT2(PredFormatFunctor2(), + n1_++, + n2_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT2 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT2Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT2(PredFormatFunctor2(), + Bool(n1_++), + Bool(n2_++)); + finished_ = true; + }, ""); +} +// Sample functions/functors for testing ternary predicate assertions. + +// A ternary predicate function. +template +bool PredFunction3(T1 v1, T2 v2, T3 v3) { + return v1 + v2 + v3 > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction3Int(int v1, int v2, int v3) { + return v1 + v2 + v3 > 0; +} +bool PredFunction3Bool(Bool v1, Bool v2, Bool v3) { + return v1 + v2 + v3 > 0; +} + +// A ternary predicate functor. +struct PredFunctor3 { + template + bool operator()(const T1& v1, + const T2& v2, + const T3& v3) { + return v1 + v2 + v3 > 0; + } +}; + +// A ternary predicate-formatter function. +template +testing::AssertionResult PredFormatFunction3(const char* e1, + const char* e2, + const char* e3, + const T1& v1, + const T2& v2, + const T3& v3) { + if (PredFunction3(v1, v2, v3)) + return testing::AssertionSuccess(); + + return testing::AssertionFailure() + << e1 << " + " << e2 << " + " << e3 + << " is expected to be positive, but evaluates to " + << v1 + v2 + v3 << "."; +} + +// A ternary predicate-formatter functor. +struct PredFormatFunctor3 { + template + testing::AssertionResult operator()(const char* e1, + const char* e2, + const char* e3, + const T1& v1, + const T2& v2, + const T3& v3) const { + return PredFormatFunction3(e1, e2, e3, v1, v2, v3); + } +}; + +// Tests for {EXPECT|ASSERT}_PRED_FORMAT3. + +class Predicate3Test : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false; + n1_ = n2_ = n3_ = 0; + } + + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once. + EXPECT_EQ(1, n1_) << + "The predicate assertion didn't evaluate argument 2 " + "exactly once."; + EXPECT_EQ(1, n2_) << + "The predicate assertion didn't evaluate argument 3 " + "exactly once."; + EXPECT_EQ(1, n3_) << + "The predicate assertion didn't evaluate argument 4 " + "exactly once."; + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; + + static int n1_; + static int n2_; + static int n3_; +}; + +bool Predicate3Test::expected_to_finish_; +bool Predicate3Test::finished_; +int Predicate3Test::n1_; +int Predicate3Test::n2_; +int Predicate3Test::n3_; + +typedef Predicate3Test EXPECT_PRED_FORMAT3Test; +typedef Predicate3Test ASSERT_PRED_FORMAT3Test; +typedef Predicate3Test EXPECT_PRED3Test; +typedef Predicate3Test ASSERT_PRED3Test; + +// Tests a successful EXPECT_PRED3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED3Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED3(PredFunction3Int, + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED3Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED3(PredFunction3Bool, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED3Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED3(PredFunctor3(), + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED3Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED3(PredFunctor3(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED3Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED3(PredFunction3Int, + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED3Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED3(PredFunction3Bool, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED3Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED3(PredFunctor3(), + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED3Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED3(PredFunctor3(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED3Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED3(PredFunction3Int, + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED3Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED3(PredFunction3Bool, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED3Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED3(PredFunctor3(), + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED3Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED3(PredFunctor3(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED3Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED3(PredFunction3Int, + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED3Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED3(PredFunction3Bool, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED3Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED3(PredFunctor3(), + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED3Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED3(PredFunctor3(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a successful EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT3(PredFormatFunction3, + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED_FORMAT3(PredFormatFunction3, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT3(PredFormatFunctor3(), + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED_FORMAT3(PredFormatFunctor3(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT3(PredFormatFunction3, + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT3(PredFormatFunction3, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT3(PredFormatFunctor3(), + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT3Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT3(PredFormatFunctor3(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT3(PredFormatFunction3, + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED_FORMAT3(PredFormatFunction3, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT3(PredFormatFunctor3(), + ++n1_, + ++n2_, + ++n3_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED_FORMAT3(PredFormatFunctor3(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT3(PredFormatFunction3, + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT3(PredFormatFunction3, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT3(PredFormatFunctor3(), + n1_++, + n2_++, + n3_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT3 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT3Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT3(PredFormatFunctor3(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++)); + finished_ = true; + }, ""); +} +// Sample functions/functors for testing 4-ary predicate assertions. + +// A 4-ary predicate function. +template +bool PredFunction4(T1 v1, T2 v2, T3 v3, T4 v4) { + return v1 + v2 + v3 + v4 > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction4Int(int v1, int v2, int v3, int v4) { + return v1 + v2 + v3 + v4 > 0; +} +bool PredFunction4Bool(Bool v1, Bool v2, Bool v3, Bool v4) { + return v1 + v2 + v3 + v4 > 0; +} + +// A 4-ary predicate functor. +struct PredFunctor4 { + template + bool operator()(const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + return v1 + v2 + v3 + v4 > 0; + } +}; + +// A 4-ary predicate-formatter function. +template +testing::AssertionResult PredFormatFunction4(const char* e1, + const char* e2, + const char* e3, + const char* e4, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (PredFunction4(v1, v2, v3, v4)) + return testing::AssertionSuccess(); + + return testing::AssertionFailure() + << e1 << " + " << e2 << " + " << e3 << " + " << e4 + << " is expected to be positive, but evaluates to " + << v1 + v2 + v3 + v4 << "."; +} + +// A 4-ary predicate-formatter functor. +struct PredFormatFunctor4 { + template + testing::AssertionResult operator()(const char* e1, + const char* e2, + const char* e3, + const char* e4, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) const { + return PredFormatFunction4(e1, e2, e3, e4, v1, v2, v3, v4); + } +}; + +// Tests for {EXPECT|ASSERT}_PRED_FORMAT4. + +class Predicate4Test : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false; + n1_ = n2_ = n3_ = n4_ = 0; + } + + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once. + EXPECT_EQ(1, n1_) << + "The predicate assertion didn't evaluate argument 2 " + "exactly once."; + EXPECT_EQ(1, n2_) << + "The predicate assertion didn't evaluate argument 3 " + "exactly once."; + EXPECT_EQ(1, n3_) << + "The predicate assertion didn't evaluate argument 4 " + "exactly once."; + EXPECT_EQ(1, n4_) << + "The predicate assertion didn't evaluate argument 5 " + "exactly once."; + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; + + static int n1_; + static int n2_; + static int n3_; + static int n4_; +}; + +bool Predicate4Test::expected_to_finish_; +bool Predicate4Test::finished_; +int Predicate4Test::n1_; +int Predicate4Test::n2_; +int Predicate4Test::n3_; +int Predicate4Test::n4_; + +typedef Predicate4Test EXPECT_PRED_FORMAT4Test; +typedef Predicate4Test ASSERT_PRED_FORMAT4Test; +typedef Predicate4Test EXPECT_PRED4Test; +typedef Predicate4Test ASSERT_PRED4Test; + +// Tests a successful EXPECT_PRED4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED4Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED4(PredFunction4Int, + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED4Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED4(PredFunction4Bool, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED4Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED4(PredFunctor4(), + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED4Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED4(PredFunctor4(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED4Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED4(PredFunction4Int, + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED4Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED4(PredFunction4Bool, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED4Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED4(PredFunctor4(), + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED4Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED4(PredFunctor4(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED4Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED4(PredFunction4Int, + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED4Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED4(PredFunction4Bool, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED4Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED4(PredFunctor4(), + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED4Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED4(PredFunctor4(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED4Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED4(PredFunction4Int, + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED4Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED4(PredFunction4Bool, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED4Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED4(PredFunctor4(), + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED4Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED4(PredFunctor4(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a successful EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT4(PredFormatFunction4, + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED_FORMAT4(PredFormatFunction4, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT4(PredFormatFunctor4(), + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED_FORMAT4(PredFormatFunctor4(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT4(PredFormatFunction4, + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT4(PredFormatFunction4, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT4(PredFormatFunctor4(), + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT4Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT4(PredFormatFunctor4(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT4(PredFormatFunction4, + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED_FORMAT4(PredFormatFunction4, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT4(PredFormatFunctor4(), + ++n1_, + ++n2_, + ++n3_, + ++n4_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED_FORMAT4(PredFormatFunctor4(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT4(PredFormatFunction4, + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT4(PredFormatFunction4, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT4(PredFormatFunctor4(), + n1_++, + n2_++, + n3_++, + n4_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT4 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT4Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT4(PredFormatFunctor4(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++)); + finished_ = true; + }, ""); +} +// Sample functions/functors for testing 5-ary predicate assertions. + +// A 5-ary predicate function. +template +bool PredFunction5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) { + return v1 + v2 + v3 + v4 + v5 > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction5Int(int v1, int v2, int v3, int v4, int v5) { + return v1 + v2 + v3 + v4 + v5 > 0; +} +bool PredFunction5Bool(Bool v1, Bool v2, Bool v3, Bool v4, Bool v5) { + return v1 + v2 + v3 + v4 + v5 > 0; +} + +// A 5-ary predicate functor. +struct PredFunctor5 { + template + bool operator()(const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + return v1 + v2 + v3 + v4 + v5 > 0; + } +}; + +// A 5-ary predicate-formatter function. +template +testing::AssertionResult PredFormatFunction5(const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (PredFunction5(v1, v2, v3, v4, v5)) + return testing::AssertionSuccess(); + + return testing::AssertionFailure() + << e1 << " + " << e2 << " + " << e3 << " + " << e4 << " + " << e5 + << " is expected to be positive, but evaluates to " + << v1 + v2 + v3 + v4 + v5 << "."; +} + +// A 5-ary predicate-formatter functor. +struct PredFormatFunctor5 { + template + testing::AssertionResult operator()(const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) const { + return PredFormatFunction5(e1, e2, e3, e4, e5, v1, v2, v3, v4, v5); + } +}; + +// Tests for {EXPECT|ASSERT}_PRED_FORMAT5. + +class Predicate5Test : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false; + n1_ = n2_ = n3_ = n4_ = n5_ = 0; + } + + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once. + EXPECT_EQ(1, n1_) << + "The predicate assertion didn't evaluate argument 2 " + "exactly once."; + EXPECT_EQ(1, n2_) << + "The predicate assertion didn't evaluate argument 3 " + "exactly once."; + EXPECT_EQ(1, n3_) << + "The predicate assertion didn't evaluate argument 4 " + "exactly once."; + EXPECT_EQ(1, n4_) << + "The predicate assertion didn't evaluate argument 5 " + "exactly once."; + EXPECT_EQ(1, n5_) << + "The predicate assertion didn't evaluate argument 6 " + "exactly once."; + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; + + static int n1_; + static int n2_; + static int n3_; + static int n4_; + static int n5_; +}; + +bool Predicate5Test::expected_to_finish_; +bool Predicate5Test::finished_; +int Predicate5Test::n1_; +int Predicate5Test::n2_; +int Predicate5Test::n3_; +int Predicate5Test::n4_; +int Predicate5Test::n5_; + +typedef Predicate5Test EXPECT_PRED_FORMAT5Test; +typedef Predicate5Test ASSERT_PRED_FORMAT5Test; +typedef Predicate5Test EXPECT_PRED5Test; +typedef Predicate5Test ASSERT_PRED5Test; + +// Tests a successful EXPECT_PRED5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED5Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED5(PredFunction5Int, + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED5Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED5(PredFunction5Bool, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED5Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED5(PredFunctor5(), + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED5Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED5(PredFunctor5(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED5Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED5(PredFunction5Int, + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED5Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED5(PredFunction5Bool, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED5Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED5(PredFunctor5(), + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED5Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED5(PredFunctor5(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED5Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED5(PredFunction5Int, + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED5Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED5(PredFunction5Bool, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED5Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED5(PredFunctor5(), + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED5Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED5(PredFunctor5(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED5Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED5(PredFunction5Int, + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED5Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED5(PredFunction5Bool, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED5Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED5(PredFunctor5(), + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED5Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED5(PredFunctor5(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a successful EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctionOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT5(PredFormatFunction5, + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctionOnUserTypeSuccess) { + EXPECT_PRED_FORMAT5(PredFormatFunction5, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctorOnBuiltInTypeSuccess) { + EXPECT_PRED_FORMAT5(PredFormatFunctor5(), + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctorOnUserTypeSuccess) { + EXPECT_PRED_FORMAT5(PredFormatFunctor5(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a failed EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctionOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT5(PredFormatFunction5, + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctionOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT5(PredFormatFunction5, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctorOnBuiltInTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT5(PredFormatFunctor5(), + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed EXPECT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(EXPECT_PRED_FORMAT5Test, FunctorOnUserTypeFailure) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT5(PredFormatFunctor5(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a successful ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctionOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT5(PredFormatFunction5, + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctionOnUserTypeSuccess) { + ASSERT_PRED_FORMAT5(PredFormatFunction5, + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctorOnBuiltInTypeSuccess) { + ASSERT_PRED_FORMAT5(PredFormatFunctor5(), + ++n1_, + ++n2_, + ++n3_, + ++n4_, + ++n5_); + finished_ = true; +} + +// Tests a successful ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctorOnUserTypeSuccess) { + ASSERT_PRED_FORMAT5(PredFormatFunctor5(), + Bool(++n1_), + Bool(++n2_), + Bool(++n3_), + Bool(++n4_), + Bool(++n5_)); + finished_ = true; +} + +// Tests a failed ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a function on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctionOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT5(PredFormatFunction5, + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a function on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctionOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT5(PredFormatFunction5, + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a built-in type (int). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctorOnBuiltInTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT5(PredFormatFunctor5(), + n1_++, + n2_++, + n3_++, + n4_++, + n5_++); + finished_ = true; + }, ""); +} + +// Tests a failed ASSERT_PRED_FORMAT5 where the +// predicate-formatter is a functor on a user-defined type (Bool). +TEST_F(ASSERT_PRED_FORMAT5Test, FunctorOnUserTypeFailure) { + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT5(PredFormatFunctor5(), + Bool(n1_++), + Bool(n2_++), + Bool(n3_++), + Bool(n4_++), + Bool(n5_++)); + finished_ = true; + }, ""); +} diff --git a/external/gtest/test/gtest_premature_exit_test.cc b/external/gtest/test/gtest_premature_exit_test.cc new file mode 100644 index 0000000000..f6b6be9aeb --- /dev/null +++ b/external/gtest/test/gtest_premature_exit_test.cc @@ -0,0 +1,141 @@ +// Copyright 2013, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests that Google Test manipulates the premature-exit-detection +// file correctly. + +#include + +#include "gtest/gtest.h" + +using ::testing::InitGoogleTest; +using ::testing::Test; +using ::testing::internal::posix::GetEnv; +using ::testing::internal::posix::Stat; +using ::testing::internal::posix::StatStruct; + +namespace { + +// Is the TEST_PREMATURE_EXIT_FILE environment variable expected to be +// set? +const bool kTestPrematureExitFileEnvVarShouldBeSet = false; + +class PrematureExitTest : public Test { + public: + // Returns true iff the given file exists. + static bool FileExists(const char* filepath) { + StatStruct stat; + return Stat(filepath, &stat) == 0; + } + + protected: + PrematureExitTest() { + premature_exit_file_path_ = GetEnv("TEST_PREMATURE_EXIT_FILE"); + + // Normalize NULL to "" for ease of handling. + if (premature_exit_file_path_ == NULL) { + premature_exit_file_path_ = ""; + } + } + + // Returns true iff the premature-exit file exists. + bool PrematureExitFileExists() const { + return FileExists(premature_exit_file_path_); + } + + const char* premature_exit_file_path_; +}; + +typedef PrematureExitTest PrematureExitDeathTest; + +// Tests that: +// - the premature-exit file exists during the execution of a +// death test (EXPECT_DEATH*), and +// - a death test doesn't interfere with the main test process's +// handling of the premature-exit file. +TEST_F(PrematureExitDeathTest, FileExistsDuringExecutionOfDeathTest) { + if (*premature_exit_file_path_ == '\0') { + return; + } + + EXPECT_DEATH_IF_SUPPORTED({ + // If the file exists, crash the process such that the main test + // process will catch the (expected) crash and report a success; + // otherwise don't crash, which will cause the main test process + // to report that the death test has failed. + if (PrematureExitFileExists()) { + exit(1); + } + }, ""); +} + +// Tests that TEST_PREMATURE_EXIT_FILE is set where it's expected to +// be set. +TEST_F(PrematureExitTest, TestPrematureExitFileEnvVarIsSet) { + if (kTestPrematureExitFileEnvVarShouldBeSet) { + const char* const filepath = GetEnv("TEST_PREMATURE_EXIT_FILE"); + ASSERT_TRUE(filepath != NULL); + ASSERT_NE(*filepath, '\0'); + } +} + +// Tests that the premature-exit file exists during the execution of a +// normal (non-death) test. +TEST_F(PrematureExitTest, PrematureExitFileExistsDuringTestExecution) { + if (*premature_exit_file_path_ == '\0') { + return; + } + + EXPECT_TRUE(PrematureExitFileExists()) + << " file " << premature_exit_file_path_ + << " should exist during test execution, but doesn't."; +} + +} // namespace + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + const int exit_code = RUN_ALL_TESTS(); + + // Test that the premature-exit file is deleted upon return from + // RUN_ALL_TESTS(). + const char* const filepath = GetEnv("TEST_PREMATURE_EXIT_FILE"); + if (filepath != NULL && *filepath != '\0') { + if (PrematureExitTest::FileExists(filepath)) { + printf( + "File %s shouldn't exist after the test program finishes, but does.", + filepath); + return 1; + } + } + + return exit_code; +} diff --git a/external/gtest/test/gtest_prod_test.cc b/external/gtest/test/gtest_prod_test.cc new file mode 100644 index 0000000000..060abce187 --- /dev/null +++ b/external/gtest/test/gtest_prod_test.cc @@ -0,0 +1,57 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Unit test for include/gtest/gtest_prod.h. + +#include "gtest/gtest.h" +#include "test/production.h" + +// Tests that private members can be accessed from a TEST declared as +// a friend of the class. +TEST(PrivateCodeTest, CanAccessPrivateMembers) { + PrivateCode a; + EXPECT_EQ(0, a.x_); + + a.set_x(1); + EXPECT_EQ(1, a.x_); +} + +typedef testing::Test PrivateCodeFixtureTest; + +// Tests that private members can be accessed from a TEST_F declared +// as a friend of the class. +TEST_F(PrivateCodeFixtureTest, CanAccessPrivateMembers) { + PrivateCode a; + EXPECT_EQ(0, a.x_); + + a.set_x(2); + EXPECT_EQ(2, a.x_); +} diff --git a/external/gtest/test/gtest_repeat_test.cc b/external/gtest/test/gtest_repeat_test.cc new file mode 100644 index 0000000000..481012adc2 --- /dev/null +++ b/external/gtest/test/gtest_repeat_test.cc @@ -0,0 +1,253 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Tests the --gtest_repeat=number flag. + +#include +#include +#include "gtest/gtest.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +GTEST_DECLARE_string_(death_test_style); +GTEST_DECLARE_string_(filter); +GTEST_DECLARE_int32_(repeat); + +} // namespace testing + +using testing::GTEST_FLAG(death_test_style); +using testing::GTEST_FLAG(filter); +using testing::GTEST_FLAG(repeat); + +namespace { + +// We need this when we are testing Google Test itself and therefore +// cannot use Google Test assertions. +#define GTEST_CHECK_INT_EQ_(expected, actual) \ + do {\ + const int expected_val = (expected);\ + const int actual_val = (actual);\ + if (::testing::internal::IsTrue(expected_val != actual_val)) {\ + ::std::cout << "Value of: " #actual "\n"\ + << " Actual: " << actual_val << "\n"\ + << "Expected: " #expected "\n"\ + << "Which is: " << expected_val << "\n";\ + ::testing::internal::posix::Abort();\ + }\ + } while (::testing::internal::AlwaysFalse()) + + +// Used for verifying that global environment set-up and tear-down are +// inside the gtest_repeat loop. + +int g_environment_set_up_count = 0; +int g_environment_tear_down_count = 0; + +class MyEnvironment : public testing::Environment { + public: + MyEnvironment() {} + virtual void SetUp() { g_environment_set_up_count++; } + virtual void TearDown() { g_environment_tear_down_count++; } +}; + +// A test that should fail. + +int g_should_fail_count = 0; + +TEST(FooTest, ShouldFail) { + g_should_fail_count++; + EXPECT_EQ(0, 1) << "Expected failure."; +} + +// A test that should pass. + +int g_should_pass_count = 0; + +TEST(FooTest, ShouldPass) { + g_should_pass_count++; +} + +// A test that contains a thread-safe death test and a fast death +// test. It should pass. + +int g_death_test_count = 0; + +TEST(BarDeathTest, ThreadSafeAndFast) { + g_death_test_count++; + + GTEST_FLAG(death_test_style) = "threadsafe"; + EXPECT_DEATH_IF_SUPPORTED(::testing::internal::posix::Abort(), ""); + + GTEST_FLAG(death_test_style) = "fast"; + EXPECT_DEATH_IF_SUPPORTED(::testing::internal::posix::Abort(), ""); +} + +#if GTEST_HAS_PARAM_TEST +int g_param_test_count = 0; + +const int kNumberOfParamTests = 10; + +class MyParamTest : public testing::TestWithParam {}; + +TEST_P(MyParamTest, ShouldPass) { + // TODO(vladl@google.com): Make parameter value checking robust + // WRT order of tests. + GTEST_CHECK_INT_EQ_(g_param_test_count % kNumberOfParamTests, GetParam()); + g_param_test_count++; +} +INSTANTIATE_TEST_CASE_P(MyParamSequence, + MyParamTest, + testing::Range(0, kNumberOfParamTests)); +#endif // GTEST_HAS_PARAM_TEST + +// Resets the count for each test. +void ResetCounts() { + g_environment_set_up_count = 0; + g_environment_tear_down_count = 0; + g_should_fail_count = 0; + g_should_pass_count = 0; + g_death_test_count = 0; +#if GTEST_HAS_PARAM_TEST + g_param_test_count = 0; +#endif // GTEST_HAS_PARAM_TEST +} + +// Checks that the count for each test is expected. +void CheckCounts(int expected) { + GTEST_CHECK_INT_EQ_(expected, g_environment_set_up_count); + GTEST_CHECK_INT_EQ_(expected, g_environment_tear_down_count); + GTEST_CHECK_INT_EQ_(expected, g_should_fail_count); + GTEST_CHECK_INT_EQ_(expected, g_should_pass_count); + GTEST_CHECK_INT_EQ_(expected, g_death_test_count); +#if GTEST_HAS_PARAM_TEST + GTEST_CHECK_INT_EQ_(expected * kNumberOfParamTests, g_param_test_count); +#endif // GTEST_HAS_PARAM_TEST +} + +// Tests the behavior of Google Test when --gtest_repeat is not specified. +void TestRepeatUnspecified() { + ResetCounts(); + GTEST_CHECK_INT_EQ_(1, RUN_ALL_TESTS()); + CheckCounts(1); +} + +// Tests the behavior of Google Test when --gtest_repeat has the given value. +void TestRepeat(int repeat) { + GTEST_FLAG(repeat) = repeat; + + ResetCounts(); + GTEST_CHECK_INT_EQ_(repeat > 0 ? 1 : 0, RUN_ALL_TESTS()); + CheckCounts(repeat); +} + +// Tests using --gtest_repeat when --gtest_filter specifies an empty +// set of tests. +void TestRepeatWithEmptyFilter(int repeat) { + GTEST_FLAG(repeat) = repeat; + GTEST_FLAG(filter) = "None"; + + ResetCounts(); + GTEST_CHECK_INT_EQ_(0, RUN_ALL_TESTS()); + CheckCounts(0); +} + +// Tests using --gtest_repeat when --gtest_filter specifies a set of +// successful tests. +void TestRepeatWithFilterForSuccessfulTests(int repeat) { + GTEST_FLAG(repeat) = repeat; + GTEST_FLAG(filter) = "*-*ShouldFail"; + + ResetCounts(); + GTEST_CHECK_INT_EQ_(0, RUN_ALL_TESTS()); + GTEST_CHECK_INT_EQ_(repeat, g_environment_set_up_count); + GTEST_CHECK_INT_EQ_(repeat, g_environment_tear_down_count); + GTEST_CHECK_INT_EQ_(0, g_should_fail_count); + GTEST_CHECK_INT_EQ_(repeat, g_should_pass_count); + GTEST_CHECK_INT_EQ_(repeat, g_death_test_count); +#if GTEST_HAS_PARAM_TEST + GTEST_CHECK_INT_EQ_(repeat * kNumberOfParamTests, g_param_test_count); +#endif // GTEST_HAS_PARAM_TEST +} + +// Tests using --gtest_repeat when --gtest_filter specifies a set of +// failed tests. +void TestRepeatWithFilterForFailedTests(int repeat) { + GTEST_FLAG(repeat) = repeat; + GTEST_FLAG(filter) = "*ShouldFail"; + + ResetCounts(); + GTEST_CHECK_INT_EQ_(1, RUN_ALL_TESTS()); + GTEST_CHECK_INT_EQ_(repeat, g_environment_set_up_count); + GTEST_CHECK_INT_EQ_(repeat, g_environment_tear_down_count); + GTEST_CHECK_INT_EQ_(repeat, g_should_fail_count); + GTEST_CHECK_INT_EQ_(0, g_should_pass_count); + GTEST_CHECK_INT_EQ_(0, g_death_test_count); +#if GTEST_HAS_PARAM_TEST + GTEST_CHECK_INT_EQ_(0, g_param_test_count); +#endif // GTEST_HAS_PARAM_TEST +} + +} // namespace + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + testing::AddGlobalTestEnvironment(new MyEnvironment); + + TestRepeatUnspecified(); + TestRepeat(0); + TestRepeat(1); + TestRepeat(5); + + TestRepeatWithEmptyFilter(2); + TestRepeatWithEmptyFilter(3); + + TestRepeatWithFilterForSuccessfulTests(3); + + TestRepeatWithFilterForFailedTests(4); + + // It would be nice to verify that the tests indeed loop forever + // when GTEST_FLAG(repeat) is negative, but this test will be quite + // complicated to write. Since this flag is for interactive + // debugging only and doesn't affect the normal test result, such a + // test would be an overkill. + + printf("PASS\n"); + return 0; +} diff --git a/external/gtest/test/gtest_shuffle_test.py b/external/gtest/test/gtest_shuffle_test.py new file mode 100644 index 0000000000..30d0303d19 --- /dev/null +++ b/external/gtest/test/gtest_shuffle_test.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python +# +# Copyright 2009 Google Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Verifies that test shuffling works.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import gtest_test_utils + +# Command to run the gtest_shuffle_test_ program. +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_shuffle_test_') + +# The environment variables for test sharding. +TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS' +SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX' + +TEST_FILTER = 'A*.A:A*.B:C*' + +ALL_TESTS = [] +ACTIVE_TESTS = [] +FILTERED_TESTS = [] +SHARDED_TESTS = [] + +SHUFFLED_ALL_TESTS = [] +SHUFFLED_ACTIVE_TESTS = [] +SHUFFLED_FILTERED_TESTS = [] +SHUFFLED_SHARDED_TESTS = [] + + +def AlsoRunDisabledTestsFlag(): + return '--gtest_also_run_disabled_tests' + + +def FilterFlag(test_filter): + return '--gtest_filter=%s' % (test_filter,) + + +def RepeatFlag(n): + return '--gtest_repeat=%s' % (n,) + + +def ShuffleFlag(): + return '--gtest_shuffle' + + +def RandomSeedFlag(n): + return '--gtest_random_seed=%s' % (n,) + + +def RunAndReturnOutput(extra_env, args): + """Runs the test program and returns its output.""" + + environ_copy = os.environ.copy() + environ_copy.update(extra_env) + + return gtest_test_utils.Subprocess([COMMAND] + args, env=environ_copy).output + + +def GetTestsForAllIterations(extra_env, args): + """Runs the test program and returns a list of test lists. + + Args: + extra_env: a map from environment variables to their values + args: command line flags to pass to gtest_shuffle_test_ + + Returns: + A list where the i-th element is the list of tests run in the i-th + test iteration. + """ + + test_iterations = [] + for line in RunAndReturnOutput(extra_env, args).split('\n'): + if line.startswith('----'): + tests = [] + test_iterations.append(tests) + elif line.strip(): + tests.append(line.strip()) # 'TestCaseName.TestName' + + return test_iterations + + +def GetTestCases(tests): + """Returns a list of test cases in the given full test names. + + Args: + tests: a list of full test names + + Returns: + A list of test cases from 'tests', in their original order. + Consecutive duplicates are removed. + """ + + test_cases = [] + for test in tests: + test_case = test.split('.')[0] + if not test_case in test_cases: + test_cases.append(test_case) + + return test_cases + + +def CalculateTestLists(): + """Calculates the list of tests run under different flags.""" + + if not ALL_TESTS: + ALL_TESTS.extend( + GetTestsForAllIterations({}, [AlsoRunDisabledTestsFlag()])[0]) + + if not ACTIVE_TESTS: + ACTIVE_TESTS.extend(GetTestsForAllIterations({}, [])[0]) + + if not FILTERED_TESTS: + FILTERED_TESTS.extend( + GetTestsForAllIterations({}, [FilterFlag(TEST_FILTER)])[0]) + + if not SHARDED_TESTS: + SHARDED_TESTS.extend( + GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', + SHARD_INDEX_ENV_VAR: '1'}, + [])[0]) + + if not SHUFFLED_ALL_TESTS: + SHUFFLED_ALL_TESTS.extend(GetTestsForAllIterations( + {}, [AlsoRunDisabledTestsFlag(), ShuffleFlag(), RandomSeedFlag(1)])[0]) + + if not SHUFFLED_ACTIVE_TESTS: + SHUFFLED_ACTIVE_TESTS.extend(GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(1)])[0]) + + if not SHUFFLED_FILTERED_TESTS: + SHUFFLED_FILTERED_TESTS.extend(GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(1), FilterFlag(TEST_FILTER)])[0]) + + if not SHUFFLED_SHARDED_TESTS: + SHUFFLED_SHARDED_TESTS.extend( + GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', + SHARD_INDEX_ENV_VAR: '1'}, + [ShuffleFlag(), RandomSeedFlag(1)])[0]) + + +class GTestShuffleUnitTest(gtest_test_utils.TestCase): + """Tests test shuffling.""" + + def setUp(self): + CalculateTestLists() + + def testShufflePreservesNumberOfTests(self): + self.assertEqual(len(ALL_TESTS), len(SHUFFLED_ALL_TESTS)) + self.assertEqual(len(ACTIVE_TESTS), len(SHUFFLED_ACTIVE_TESTS)) + self.assertEqual(len(FILTERED_TESTS), len(SHUFFLED_FILTERED_TESTS)) + self.assertEqual(len(SHARDED_TESTS), len(SHUFFLED_SHARDED_TESTS)) + + def testShuffleChangesTestOrder(self): + self.assert_(SHUFFLED_ALL_TESTS != ALL_TESTS, SHUFFLED_ALL_TESTS) + self.assert_(SHUFFLED_ACTIVE_TESTS != ACTIVE_TESTS, SHUFFLED_ACTIVE_TESTS) + self.assert_(SHUFFLED_FILTERED_TESTS != FILTERED_TESTS, + SHUFFLED_FILTERED_TESTS) + self.assert_(SHUFFLED_SHARDED_TESTS != SHARDED_TESTS, + SHUFFLED_SHARDED_TESTS) + + def testShuffleChangesTestCaseOrder(self): + self.assert_(GetTestCases(SHUFFLED_ALL_TESTS) != GetTestCases(ALL_TESTS), + GetTestCases(SHUFFLED_ALL_TESTS)) + self.assert_( + GetTestCases(SHUFFLED_ACTIVE_TESTS) != GetTestCases(ACTIVE_TESTS), + GetTestCases(SHUFFLED_ACTIVE_TESTS)) + self.assert_( + GetTestCases(SHUFFLED_FILTERED_TESTS) != GetTestCases(FILTERED_TESTS), + GetTestCases(SHUFFLED_FILTERED_TESTS)) + self.assert_( + GetTestCases(SHUFFLED_SHARDED_TESTS) != GetTestCases(SHARDED_TESTS), + GetTestCases(SHUFFLED_SHARDED_TESTS)) + + def testShuffleDoesNotRepeatTest(self): + for test in SHUFFLED_ALL_TESTS: + self.assertEqual(1, SHUFFLED_ALL_TESTS.count(test), + '%s appears more than once' % (test,)) + for test in SHUFFLED_ACTIVE_TESTS: + self.assertEqual(1, SHUFFLED_ACTIVE_TESTS.count(test), + '%s appears more than once' % (test,)) + for test in SHUFFLED_FILTERED_TESTS: + self.assertEqual(1, SHUFFLED_FILTERED_TESTS.count(test), + '%s appears more than once' % (test,)) + for test in SHUFFLED_SHARDED_TESTS: + self.assertEqual(1, SHUFFLED_SHARDED_TESTS.count(test), + '%s appears more than once' % (test,)) + + def testShuffleDoesNotCreateNewTest(self): + for test in SHUFFLED_ALL_TESTS: + self.assert_(test in ALL_TESTS, '%s is an invalid test' % (test,)) + for test in SHUFFLED_ACTIVE_TESTS: + self.assert_(test in ACTIVE_TESTS, '%s is an invalid test' % (test,)) + for test in SHUFFLED_FILTERED_TESTS: + self.assert_(test in FILTERED_TESTS, '%s is an invalid test' % (test,)) + for test in SHUFFLED_SHARDED_TESTS: + self.assert_(test in SHARDED_TESTS, '%s is an invalid test' % (test,)) + + def testShuffleIncludesAllTests(self): + for test in ALL_TESTS: + self.assert_(test in SHUFFLED_ALL_TESTS, '%s is missing' % (test,)) + for test in ACTIVE_TESTS: + self.assert_(test in SHUFFLED_ACTIVE_TESTS, '%s is missing' % (test,)) + for test in FILTERED_TESTS: + self.assert_(test in SHUFFLED_FILTERED_TESTS, '%s is missing' % (test,)) + for test in SHARDED_TESTS: + self.assert_(test in SHUFFLED_SHARDED_TESTS, '%s is missing' % (test,)) + + def testShuffleLeavesDeathTestsAtFront(self): + non_death_test_found = False + for test in SHUFFLED_ACTIVE_TESTS: + if 'DeathTest.' in test: + self.assert_(not non_death_test_found, + '%s appears after a non-death test' % (test,)) + else: + non_death_test_found = True + + def _VerifyTestCasesDoNotInterleave(self, tests): + test_cases = [] + for test in tests: + [test_case, _] = test.split('.') + if test_cases and test_cases[-1] != test_case: + test_cases.append(test_case) + self.assertEqual(1, test_cases.count(test_case), + 'Test case %s is not grouped together in %s' % + (test_case, tests)) + + def testShuffleDoesNotInterleaveTestCases(self): + self._VerifyTestCasesDoNotInterleave(SHUFFLED_ALL_TESTS) + self._VerifyTestCasesDoNotInterleave(SHUFFLED_ACTIVE_TESTS) + self._VerifyTestCasesDoNotInterleave(SHUFFLED_FILTERED_TESTS) + self._VerifyTestCasesDoNotInterleave(SHUFFLED_SHARDED_TESTS) + + def testShuffleRestoresOrderAfterEachIteration(self): + # Get the test lists in all 3 iterations, using random seed 1, 2, + # and 3 respectively. Google Test picks a different seed in each + # iteration, and this test depends on the current implementation + # picking successive numbers. This dependency is not ideal, but + # makes the test much easier to write. + [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = ( + GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)])) + + # Make sure running the tests with random seed 1 gets the same + # order as in iteration 1 above. + [tests_with_seed1] = GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(1)]) + self.assertEqual(tests_in_iteration1, tests_with_seed1) + + # Make sure running the tests with random seed 2 gets the same + # order as in iteration 2 above. Success means that Google Test + # correctly restores the test order before re-shuffling at the + # beginning of iteration 2. + [tests_with_seed2] = GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(2)]) + self.assertEqual(tests_in_iteration2, tests_with_seed2) + + # Make sure running the tests with random seed 3 gets the same + # order as in iteration 3 above. Success means that Google Test + # correctly restores the test order before re-shuffling at the + # beginning of iteration 3. + [tests_with_seed3] = GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(3)]) + self.assertEqual(tests_in_iteration3, tests_with_seed3) + + def testShuffleGeneratesNewOrderInEachIteration(self): + [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = ( + GetTestsForAllIterations( + {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)])) + + self.assert_(tests_in_iteration1 != tests_in_iteration2, + tests_in_iteration1) + self.assert_(tests_in_iteration1 != tests_in_iteration3, + tests_in_iteration1) + self.assert_(tests_in_iteration2 != tests_in_iteration3, + tests_in_iteration2) + + def testShuffleShardedTestsPreservesPartition(self): + # If we run M tests on N shards, the same M tests should be run in + # total, regardless of the random seeds used by the shards. + [tests1] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', + SHARD_INDEX_ENV_VAR: '0'}, + [ShuffleFlag(), RandomSeedFlag(1)]) + [tests2] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', + SHARD_INDEX_ENV_VAR: '1'}, + [ShuffleFlag(), RandomSeedFlag(20)]) + [tests3] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3', + SHARD_INDEX_ENV_VAR: '2'}, + [ShuffleFlag(), RandomSeedFlag(25)]) + sorted_sharded_tests = tests1 + tests2 + tests3 + sorted_sharded_tests.sort() + sorted_active_tests = [] + sorted_active_tests.extend(ACTIVE_TESTS) + sorted_active_tests.sort() + self.assertEqual(sorted_active_tests, sorted_sharded_tests) + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_shuffle_test_.cc b/external/gtest/test/gtest_shuffle_test_.cc new file mode 100644 index 0000000000..6fb441bd4d --- /dev/null +++ b/external/gtest/test/gtest_shuffle_test_.cc @@ -0,0 +1,103 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Verifies that test shuffling works. + +#include "gtest/gtest.h" + +namespace { + +using ::testing::EmptyTestEventListener; +using ::testing::InitGoogleTest; +using ::testing::Message; +using ::testing::Test; +using ::testing::TestEventListeners; +using ::testing::TestInfo; +using ::testing::UnitTest; +using ::testing::internal::scoped_ptr; + +// The test methods are empty, as the sole purpose of this program is +// to print the test names before/after shuffling. + +class A : public Test {}; +TEST_F(A, A) {} +TEST_F(A, B) {} + +TEST(ADeathTest, A) {} +TEST(ADeathTest, B) {} +TEST(ADeathTest, C) {} + +TEST(B, A) {} +TEST(B, B) {} +TEST(B, C) {} +TEST(B, DISABLED_D) {} +TEST(B, DISABLED_E) {} + +TEST(BDeathTest, A) {} +TEST(BDeathTest, B) {} + +TEST(C, A) {} +TEST(C, B) {} +TEST(C, C) {} +TEST(C, DISABLED_D) {} + +TEST(CDeathTest, A) {} + +TEST(DISABLED_D, A) {} +TEST(DISABLED_D, DISABLED_B) {} + +// This printer prints the full test names only, starting each test +// iteration with a "----" marker. +class TestNamePrinter : public EmptyTestEventListener { + public: + virtual void OnTestIterationStart(const UnitTest& /* unit_test */, + int /* iteration */) { + printf("----\n"); + } + + virtual void OnTestStart(const TestInfo& test_info) { + printf("%s.%s\n", test_info.test_case_name(), test_info.name()); + } +}; + +} // namespace + +int main(int argc, char **argv) { + InitGoogleTest(&argc, argv); + + // Replaces the default printer with TestNamePrinter, which prints + // the test name only. + TestEventListeners& listeners = UnitTest::GetInstance()->listeners(); + delete listeners.Release(listeners.default_result_printer()); + listeners.Append(new TestNamePrinter); + + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_sole_header_test.cc b/external/gtest/test/gtest_sole_header_test.cc new file mode 100644 index 0000000000..ccd091a281 --- /dev/null +++ b/external/gtest/test/gtest_sole_header_test.cc @@ -0,0 +1,57 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// This test verifies that it's possible to use Google Test by including +// the gtest.h header file alone. + +#include "gtest/gtest.h" + +namespace { + +void Subroutine() { + EXPECT_EQ(42, 42); +} + +TEST(NoFatalFailureTest, ExpectNoFatalFailure) { + EXPECT_NO_FATAL_FAILURE(;); + EXPECT_NO_FATAL_FAILURE(SUCCEED()); + EXPECT_NO_FATAL_FAILURE(Subroutine()); + EXPECT_NO_FATAL_FAILURE({ SUCCEED(); }); +} + +TEST(NoFatalFailureTest, AssertNoFatalFailure) { + ASSERT_NO_FATAL_FAILURE(;); + ASSERT_NO_FATAL_FAILURE(SUCCEED()); + ASSERT_NO_FATAL_FAILURE(Subroutine()); + ASSERT_NO_FATAL_FAILURE({ SUCCEED(); }); +} + +} // namespace diff --git a/external/gtest/test/gtest_stress_test.cc b/external/gtest/test/gtest_stress_test.cc new file mode 100644 index 0000000000..e7daa430df --- /dev/null +++ b/external/gtest/test/gtest_stress_test.cc @@ -0,0 +1,256 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Tests that SCOPED_TRACE() and various Google Test assertions can be +// used in a large number of threads concurrently. + +#include "gtest/gtest.h" + +#include +#include + +// We must define this macro in order to #include +// gtest-internal-inl.h. This is how Google Test prevents a user from +// accidentally depending on its internal implementation. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_IS_THREADSAFE + +namespace testing { +namespace { + +using internal::Notification; +using internal::TestPropertyKeyIs; +using internal::ThreadWithParam; +using internal::scoped_ptr; + +// In order to run tests in this file, for platforms where Google Test is +// thread safe, implement ThreadWithParam. See the description of its API +// in gtest-port.h, where it is defined for already supported platforms. + +// How many threads to create? +const int kThreadCount = 50; + +std::string IdToKey(int id, const char* suffix) { + Message key; + key << "key_" << id << "_" << suffix; + return key.GetString(); +} + +std::string IdToString(int id) { + Message id_message; + id_message << id; + return id_message.GetString(); +} + +void ExpectKeyAndValueWereRecordedForId( + const std::vector& properties, + int id, const char* suffix) { + TestPropertyKeyIs matches_key(IdToKey(id, suffix).c_str()); + const std::vector::const_iterator property = + std::find_if(properties.begin(), properties.end(), matches_key); + ASSERT_TRUE(property != properties.end()) + << "expecting " << suffix << " value for id " << id; + EXPECT_STREQ(IdToString(id).c_str(), property->value()); +} + +// Calls a large number of Google Test assertions, where exactly one of them +// will fail. +void ManyAsserts(int id) { + GTEST_LOG_(INFO) << "Thread #" << id << " running..."; + + SCOPED_TRACE(Message() << "Thread #" << id); + + for (int i = 0; i < kThreadCount; i++) { + SCOPED_TRACE(Message() << "Iteration #" << i); + + // A bunch of assertions that should succeed. + EXPECT_TRUE(true); + ASSERT_FALSE(false) << "This shouldn't fail."; + EXPECT_STREQ("a", "a"); + ASSERT_LE(5, 6); + EXPECT_EQ(i, i) << "This shouldn't fail."; + + // RecordProperty() should interact safely with other threads as well. + // The shared_key forces property updates. + Test::RecordProperty(IdToKey(id, "string").c_str(), IdToString(id).c_str()); + Test::RecordProperty(IdToKey(id, "int").c_str(), id); + Test::RecordProperty("shared_key", IdToString(id).c_str()); + + // This assertion should fail kThreadCount times per thread. It + // is for testing whether Google Test can handle failed assertions in a + // multi-threaded context. + EXPECT_LT(i, 0) << "This should always fail."; + } +} + +void CheckTestFailureCount(int expected_failures) { + const TestInfo* const info = UnitTest::GetInstance()->current_test_info(); + const TestResult* const result = info->result(); + GTEST_CHECK_(expected_failures == result->total_part_count()) + << "Logged " << result->total_part_count() << " failures " + << " vs. " << expected_failures << " expected"; +} + +// Tests using SCOPED_TRACE() and Google Test assertions in many threads +// concurrently. +TEST(StressTest, CanUseScopedTraceAndAssertionsInManyThreads) { + { + scoped_ptr > threads[kThreadCount]; + Notification threads_can_start; + for (int i = 0; i != kThreadCount; i++) + threads[i].reset(new ThreadWithParam(&ManyAsserts, + i, + &threads_can_start)); + + threads_can_start.Notify(); + + // Blocks until all the threads are done. + for (int i = 0; i != kThreadCount; i++) + threads[i]->Join(); + } + + // Ensures that kThreadCount*kThreadCount failures have been reported. + const TestInfo* const info = UnitTest::GetInstance()->current_test_info(); + const TestResult* const result = info->result(); + + std::vector properties; + // We have no access to the TestResult's list of properties but we can + // copy them one by one. + for (int i = 0; i < result->test_property_count(); ++i) + properties.push_back(result->GetTestProperty(i)); + + EXPECT_EQ(kThreadCount * 2 + 1, result->test_property_count()) + << "String and int values recorded on each thread, " + << "as well as one shared_key"; + for (int i = 0; i < kThreadCount; ++i) { + ExpectKeyAndValueWereRecordedForId(properties, i, "string"); + ExpectKeyAndValueWereRecordedForId(properties, i, "int"); + } + CheckTestFailureCount(kThreadCount*kThreadCount); +} + +void FailingThread(bool is_fatal) { + if (is_fatal) + FAIL() << "Fatal failure in some other thread. " + << "(This failure is expected.)"; + else + ADD_FAILURE() << "Non-fatal failure in some other thread. " + << "(This failure is expected.)"; +} + +void GenerateFatalFailureInAnotherThread(bool is_fatal) { + ThreadWithParam thread(&FailingThread, is_fatal, NULL); + thread.Join(); +} + +TEST(NoFatalFailureTest, ExpectNoFatalFailureIgnoresFailuresInOtherThreads) { + EXPECT_NO_FATAL_FAILURE(GenerateFatalFailureInAnotherThread(true)); + // We should only have one failure (the one from + // GenerateFatalFailureInAnotherThread()), since the EXPECT_NO_FATAL_FAILURE + // should succeed. + CheckTestFailureCount(1); +} + +void AssertNoFatalFailureIgnoresFailuresInOtherThreads() { + ASSERT_NO_FATAL_FAILURE(GenerateFatalFailureInAnotherThread(true)); +} +TEST(NoFatalFailureTest, AssertNoFatalFailureIgnoresFailuresInOtherThreads) { + // Using a subroutine, to make sure, that the test continues. + AssertNoFatalFailureIgnoresFailuresInOtherThreads(); + // We should only have one failure (the one from + // GenerateFatalFailureInAnotherThread()), since the EXPECT_NO_FATAL_FAILURE + // should succeed. + CheckTestFailureCount(1); +} + +TEST(FatalFailureTest, ExpectFatalFailureIgnoresFailuresInOtherThreads) { + // This statement should fail, since the current thread doesn't generate a + // fatal failure, only another one does. + EXPECT_FATAL_FAILURE(GenerateFatalFailureInAnotherThread(true), "expected"); + CheckTestFailureCount(2); +} + +TEST(FatalFailureOnAllThreadsTest, ExpectFatalFailureOnAllThreads) { + // This statement should succeed, because failures in all threads are + // considered. + EXPECT_FATAL_FAILURE_ON_ALL_THREADS( + GenerateFatalFailureInAnotherThread(true), "expected"); + CheckTestFailureCount(0); + // We need to add a failure, because main() checks that there are failures. + // But when only this test is run, we shouldn't have any failures. + ADD_FAILURE() << "This is an expected non-fatal failure."; +} + +TEST(NonFatalFailureTest, ExpectNonFatalFailureIgnoresFailuresInOtherThreads) { + // This statement should fail, since the current thread doesn't generate a + // fatal failure, only another one does. + EXPECT_NONFATAL_FAILURE(GenerateFatalFailureInAnotherThread(false), + "expected"); + CheckTestFailureCount(2); +} + +TEST(NonFatalFailureOnAllThreadsTest, ExpectNonFatalFailureOnAllThreads) { + // This statement should succeed, because failures in all threads are + // considered. + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS( + GenerateFatalFailureInAnotherThread(false), "expected"); + CheckTestFailureCount(0); + // We need to add a failure, because main() checks that there are failures, + // But when only this test is run, we shouldn't have any failures. + ADD_FAILURE() << "This is an expected non-fatal failure."; +} + +} // namespace +} // namespace testing + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + + const int result = RUN_ALL_TESTS(); // Expected to fail. + GTEST_CHECK_(result == 1) << "RUN_ALL_TESTS() did not fail as expected"; + + printf("\nPASS\n"); + return 0; +} + +#else +TEST(StressTest, + DISABLED_ThreadSafetyTestsAreSkippedWhenGoogleTestIsNotThreadSafe) { +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif // GTEST_IS_THREADSAFE diff --git a/external/gtest/test/gtest_test_utils.py b/external/gtest/test/gtest_test_utils.py new file mode 100644 index 0000000000..28884bdc17 --- /dev/null +++ b/external/gtest/test/gtest_test_utils.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test utilities for Google C++ Testing Framework.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import atexit +import os +import shutil +import sys +import tempfile +import unittest +_test_module = unittest + +# Suppresses the 'Import not at the top of the file' lint complaint. +# pylint: disable-msg=C6204 +try: + import subprocess + _SUBPROCESS_MODULE_AVAILABLE = True +except: + import popen2 + _SUBPROCESS_MODULE_AVAILABLE = False +# pylint: enable-msg=C6204 + +GTEST_OUTPUT_VAR_NAME = 'GTEST_OUTPUT' + +IS_WINDOWS = os.name == 'nt' +IS_CYGWIN = os.name == 'posix' and 'CYGWIN' in os.uname()[0] + +# The environment variable for specifying the path to the premature-exit file. +PREMATURE_EXIT_FILE_ENV_VAR = 'TEST_PREMATURE_EXIT_FILE' + +environ = os.environ.copy() + + +def SetEnvVar(env_var, value): + """Sets/unsets an environment variable to a given value.""" + + if value is not None: + environ[env_var] = value + elif env_var in environ: + del environ[env_var] + + +# Here we expose a class from a particular module, depending on the +# environment. The comment suppresses the 'Invalid variable name' lint +# complaint. +TestCase = _test_module.TestCase # pylint: disable-msg=C6409 + +# Initially maps a flag to its default value. After +# _ParseAndStripGTestFlags() is called, maps a flag to its actual value. +_flag_map = {'source_dir': os.path.dirname(sys.argv[0]), + 'build_dir': os.path.dirname(sys.argv[0])} +_gtest_flags_are_parsed = False + + +def _ParseAndStripGTestFlags(argv): + """Parses and strips Google Test flags from argv. This is idempotent.""" + + # Suppresses the lint complaint about a global variable since we need it + # here to maintain module-wide state. + global _gtest_flags_are_parsed # pylint: disable-msg=W0603 + if _gtest_flags_are_parsed: + return + + _gtest_flags_are_parsed = True + for flag in _flag_map: + # The environment variable overrides the default value. + if flag.upper() in os.environ: + _flag_map[flag] = os.environ[flag.upper()] + + # The command line flag overrides the environment variable. + i = 1 # Skips the program name. + while i < len(argv): + prefix = '--' + flag + '=' + if argv[i].startswith(prefix): + _flag_map[flag] = argv[i][len(prefix):] + del argv[i] + break + else: + # We don't increment i in case we just found a --gtest_* flag + # and removed it from argv. + i += 1 + + +def GetFlag(flag): + """Returns the value of the given flag.""" + + # In case GetFlag() is called before Main(), we always call + # _ParseAndStripGTestFlags() here to make sure the --gtest_* flags + # are parsed. + _ParseAndStripGTestFlags(sys.argv) + + return _flag_map[flag] + + +def GetSourceDir(): + """Returns the absolute path of the directory where the .py files are.""" + + return os.path.abspath(GetFlag('source_dir')) + + +def GetBuildDir(): + """Returns the absolute path of the directory where the test binaries are.""" + + return os.path.abspath(GetFlag('build_dir')) + + +_temp_dir = None + +def _RemoveTempDir(): + if _temp_dir: + shutil.rmtree(_temp_dir, ignore_errors=True) + +atexit.register(_RemoveTempDir) + + +def GetTempDir(): + """Returns a directory for temporary files.""" + + global _temp_dir + if not _temp_dir: + _temp_dir = tempfile.mkdtemp() + return _temp_dir + + +def GetTestExecutablePath(executable_name, build_dir=None): + """Returns the absolute path of the test binary given its name. + + The function will print a message and abort the program if the resulting file + doesn't exist. + + Args: + executable_name: name of the test binary that the test script runs. + build_dir: directory where to look for executables, by default + the result of GetBuildDir(). + + Returns: + The absolute path of the test binary. + """ + + path = os.path.abspath(os.path.join(build_dir or GetBuildDir(), + executable_name)) + if (IS_WINDOWS or IS_CYGWIN) and not path.endswith('.exe'): + path += '.exe' + + if not os.path.exists(path): + message = ( + 'Unable to find the test binary. Please make sure to provide path\n' + 'to the binary via the --build_dir flag or the BUILD_DIR\n' + 'environment variable.') + print >> sys.stderr, message + sys.exit(1) + + return path + + +def GetExitStatus(exit_code): + """Returns the argument to exit(), or -1 if exit() wasn't called. + + Args: + exit_code: the result value of os.system(command). + """ + + if os.name == 'nt': + # On Windows, os.WEXITSTATUS() doesn't work and os.system() returns + # the argument to exit() directly. + return exit_code + else: + # On Unix, os.WEXITSTATUS() must be used to extract the exit status + # from the result of os.system(). + if os.WIFEXITED(exit_code): + return os.WEXITSTATUS(exit_code) + else: + return -1 + + +class Subprocess: + def __init__(self, command, working_dir=None, capture_stderr=True, env=None): + """Changes into a specified directory, if provided, and executes a command. + + Restores the old directory afterwards. + + Args: + command: The command to run, in the form of sys.argv. + working_dir: The directory to change into. + capture_stderr: Determines whether to capture stderr in the output member + or to discard it. + env: Dictionary with environment to pass to the subprocess. + + Returns: + An object that represents outcome of the executed process. It has the + following attributes: + terminated_by_signal True iff the child process has been terminated + by a signal. + signal Sygnal that terminated the child process. + exited True iff the child process exited normally. + exit_code The code with which the child process exited. + output Child process's stdout and stderr output + combined in a string. + """ + + # The subprocess module is the preferrable way of running programs + # since it is available and behaves consistently on all platforms, + # including Windows. But it is only available starting in python 2.4. + # In earlier python versions, we revert to the popen2 module, which is + # available in python 2.0 and later but doesn't provide required + # functionality (Popen4) under Windows. This allows us to support Mac + # OS X 10.4 Tiger, which has python 2.3 installed. + if _SUBPROCESS_MODULE_AVAILABLE: + if capture_stderr: + stderr = subprocess.STDOUT + else: + stderr = subprocess.PIPE + + p = subprocess.Popen(command, + stdout=subprocess.PIPE, stderr=stderr, + cwd=working_dir, universal_newlines=True, env=env) + # communicate returns a tuple with the file obect for the child's + # output. + self.output = p.communicate()[0] + self._return_code = p.returncode + else: + old_dir = os.getcwd() + + def _ReplaceEnvDict(dest, src): + # Changes made by os.environ.clear are not inheritable by child + # processes until Python 2.6. To produce inheritable changes we have + # to delete environment items with the del statement. + for key in dest.keys(): + del dest[key] + dest.update(src) + + # When 'env' is not None, backup the environment variables and replace + # them with the passed 'env'. When 'env' is None, we simply use the + # current 'os.environ' for compatibility with the subprocess.Popen + # semantics used above. + if env is not None: + old_environ = os.environ.copy() + _ReplaceEnvDict(os.environ, env) + + try: + if working_dir is not None: + os.chdir(working_dir) + if capture_stderr: + p = popen2.Popen4(command) + else: + p = popen2.Popen3(command) + p.tochild.close() + self.output = p.fromchild.read() + ret_code = p.wait() + finally: + os.chdir(old_dir) + + # Restore the old environment variables + # if they were replaced. + if env is not None: + _ReplaceEnvDict(os.environ, old_environ) + + # Converts ret_code to match the semantics of + # subprocess.Popen.returncode. + if os.WIFSIGNALED(ret_code): + self._return_code = -os.WTERMSIG(ret_code) + else: # os.WIFEXITED(ret_code) should return True here. + self._return_code = os.WEXITSTATUS(ret_code) + + if self._return_code < 0: + self.terminated_by_signal = True + self.exited = False + self.signal = -self._return_code + else: + self.terminated_by_signal = False + self.exited = True + self.exit_code = self._return_code + + +def Main(): + """Runs the unit test.""" + + # We must call _ParseAndStripGTestFlags() before calling + # unittest.main(). Otherwise the latter will be confused by the + # --gtest_* flags. + _ParseAndStripGTestFlags(sys.argv) + # The tested binaries should not be writing XML output files unless the + # script explicitly instructs them to. + # TODO(vladl@google.com): Move this into Subprocess when we implement + # passing environment into it as a parameter. + if GTEST_OUTPUT_VAR_NAME in os.environ: + del os.environ[GTEST_OUTPUT_VAR_NAME] + + _test_module.main() diff --git a/external/gtest/test/gtest_throw_on_failure_ex_test.cc b/external/gtest/test/gtest_throw_on_failure_ex_test.cc new file mode 100644 index 0000000000..8d46c76f16 --- /dev/null +++ b/external/gtest/test/gtest_throw_on_failure_ex_test.cc @@ -0,0 +1,92 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Tests Google Test's throw-on-failure mode with exceptions enabled. + +#include "gtest/gtest.h" + +#include +#include +#include +#include + +// Prints the given failure message and exits the program with +// non-zero. We use this instead of a Google Test assertion to +// indicate a failure, as the latter is been tested and cannot be +// relied on. +void Fail(const char* msg) { + printf("FAILURE: %s\n", msg); + fflush(stdout); + exit(1); +} + +// Tests that an assertion failure throws a subclass of +// std::runtime_error. +void TestFailureThrowsRuntimeError() { + testing::GTEST_FLAG(throw_on_failure) = true; + + // A successful assertion shouldn't throw. + try { + EXPECT_EQ(3, 3); + } catch(...) { + Fail("A successful assertion wrongfully threw."); + } + + // A failed assertion should throw a subclass of std::runtime_error. + try { + EXPECT_EQ(2, 3) << "Expected failure"; + } catch(const std::runtime_error& e) { + if (strstr(e.what(), "Expected failure") != NULL) + return; + + printf("%s", + "A failed assertion did throw an exception of the right type, " + "but the message is incorrect. Instead of containing \"Expected " + "failure\", it is:\n"); + Fail(e.what()); + } catch(...) { + Fail("A failed assertion threw the wrong type of exception."); + } + Fail("A failed assertion should've thrown but didn't."); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + + // We want to ensure that people can use Google Test assertions in + // other testing frameworks, as long as they initialize Google Test + // properly and set the thrown-on-failure mode. Therefore, we don't + // use Google Test's constructs for defining and running tests + // (e.g. TEST and RUN_ALL_TESTS) here. + + TestFailureThrowsRuntimeError(); + return 0; +} diff --git a/external/gtest/test/gtest_throw_on_failure_test.py b/external/gtest/test/gtest_throw_on_failure_test.py new file mode 100644 index 0000000000..5678ffeaf6 --- /dev/null +++ b/external/gtest/test/gtest_throw_on_failure_test.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# +# Copyright 2009, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Tests Google Test's throw-on-failure mode with exceptions disabled. + +This script invokes gtest_throw_on_failure_test_ (a program written with +Google Test) with different environments and command line flags. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import gtest_test_utils + + +# Constants. + +# The command line flag for enabling/disabling the throw-on-failure mode. +THROW_ON_FAILURE = 'gtest_throw_on_failure' + +# Path to the gtest_throw_on_failure_test_ program, compiled with +# exceptions disabled. +EXE_PATH = gtest_test_utils.GetTestExecutablePath( + 'gtest_throw_on_failure_test_') + + +# Utilities. + + +def SetEnvVar(env_var, value): + """Sets an environment variable to a given value; unsets it when the + given value is None. + """ + + env_var = env_var.upper() + if value is not None: + os.environ[env_var] = value + elif env_var in os.environ: + del os.environ[env_var] + + +def Run(command): + """Runs a command; returns True/False if its exit code is/isn't 0.""" + + print 'Running "%s". . .' % ' '.join(command) + p = gtest_test_utils.Subprocess(command) + return p.exited and p.exit_code == 0 + + +# The tests. TODO(wan@google.com): refactor the class to share common +# logic with code in gtest_break_on_failure_unittest.py. +class ThrowOnFailureTest(gtest_test_utils.TestCase): + """Tests the throw-on-failure mode.""" + + def RunAndVerify(self, env_var_value, flag_value, should_fail): + """Runs gtest_throw_on_failure_test_ and verifies that it does + (or does not) exit with a non-zero code. + + Args: + env_var_value: value of the GTEST_BREAK_ON_FAILURE environment + variable; None if the variable should be unset. + flag_value: value of the --gtest_break_on_failure flag; + None if the flag should not be present. + should_fail: True iff the program is expected to fail. + """ + + SetEnvVar(THROW_ON_FAILURE, env_var_value) + + if env_var_value is None: + env_var_value_msg = ' is not set' + else: + env_var_value_msg = '=' + env_var_value + + if flag_value is None: + flag = '' + elif flag_value == '0': + flag = '--%s=0' % THROW_ON_FAILURE + else: + flag = '--%s' % THROW_ON_FAILURE + + command = [EXE_PATH] + if flag: + command.append(flag) + + if should_fail: + should_or_not = 'should' + else: + should_or_not = 'should not' + + failed = not Run(command) + + SetEnvVar(THROW_ON_FAILURE, None) + + msg = ('when %s%s, an assertion failure in "%s" %s cause a non-zero ' + 'exit code.' % + (THROW_ON_FAILURE, env_var_value_msg, ' '.join(command), + should_or_not)) + self.assert_(failed == should_fail, msg) + + def testDefaultBehavior(self): + """Tests the behavior of the default mode.""" + + self.RunAndVerify(env_var_value=None, flag_value=None, should_fail=False) + + def testThrowOnFailureEnvVar(self): + """Tests using the GTEST_THROW_ON_FAILURE environment variable.""" + + self.RunAndVerify(env_var_value='0', + flag_value=None, + should_fail=False) + self.RunAndVerify(env_var_value='1', + flag_value=None, + should_fail=True) + + def testThrowOnFailureFlag(self): + """Tests using the --gtest_throw_on_failure flag.""" + + self.RunAndVerify(env_var_value=None, + flag_value='0', + should_fail=False) + self.RunAndVerify(env_var_value=None, + flag_value='1', + should_fail=True) + + def testThrowOnFailureFlagOverridesEnvVar(self): + """Tests that --gtest_throw_on_failure overrides GTEST_THROW_ON_FAILURE.""" + + self.RunAndVerify(env_var_value='0', + flag_value='0', + should_fail=False) + self.RunAndVerify(env_var_value='0', + flag_value='1', + should_fail=True) + self.RunAndVerify(env_var_value='1', + flag_value='0', + should_fail=False) + self.RunAndVerify(env_var_value='1', + flag_value='1', + should_fail=True) + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_throw_on_failure_test_.cc b/external/gtest/test/gtest_throw_on_failure_test_.cc new file mode 100644 index 0000000000..2b88fe3d9b --- /dev/null +++ b/external/gtest/test/gtest_throw_on_failure_test_.cc @@ -0,0 +1,72 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Tests Google Test's throw-on-failure mode with exceptions disabled. +// +// This program must be compiled with exceptions disabled. It will be +// invoked by gtest_throw_on_failure_test.py, and is expected to exit +// with non-zero in the throw-on-failure mode or 0 otherwise. + +#include "gtest/gtest.h" + +#include // for fflush, fprintf, NULL, etc. +#include // for exit +#include // for set_terminate + +// This terminate handler aborts the program using exit() rather than abort(). +// This avoids showing pop-ups on Windows systems and core dumps on Unix-like +// ones. +void TerminateHandler() { + fprintf(stderr, "%s\n", "Unhandled C++ exception terminating the program."); + fflush(NULL); + exit(1); +} + +int main(int argc, char** argv) { +#if GTEST_HAS_EXCEPTIONS + std::set_terminate(&TerminateHandler); +#endif + testing::InitGoogleTest(&argc, argv); + + // We want to ensure that people can use Google Test assertions in + // other testing frameworks, as long as they initialize Google Test + // properly and set the throw-on-failure mode. Therefore, we don't + // use Google Test's constructs for defining and running tests + // (e.g. TEST and RUN_ALL_TESTS) here. + + // In the throw-on-failure mode with exceptions disabled, this + // assertion will cause the program to exit with a non-zero code. + EXPECT_EQ(2, 3); + + // When not in the throw-on-failure mode, the control will reach + // here. + return 0; +} diff --git a/external/gtest/test/gtest_uninitialized_test.py b/external/gtest/test/gtest_uninitialized_test.py new file mode 100644 index 0000000000..6ae57eeeda --- /dev/null +++ b/external/gtest/test/gtest_uninitialized_test.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Verifies that Google Test warns the user when not initialized properly.""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import gtest_test_utils + + +COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_uninitialized_test_') + + +def Assert(condition): + if not condition: + raise AssertionError + + +def AssertEq(expected, actual): + if expected != actual: + print 'Expected: %s' % (expected,) + print ' Actual: %s' % (actual,) + raise AssertionError + + +def TestExitCodeAndOutput(command): + """Runs the given command and verifies its exit code and output.""" + + # Verifies that 'command' exits with code 1. + p = gtest_test_utils.Subprocess(command) + Assert(p.exited) + AssertEq(1, p.exit_code) + Assert('InitGoogleTest' in p.output) + + +class GTestUninitializedTest(gtest_test_utils.TestCase): + def testExitCodeAndOutput(self): + TestExitCodeAndOutput(COMMAND) + + +if __name__ == '__main__': + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_uninitialized_test_.cc b/external/gtest/test/gtest_uninitialized_test_.cc new file mode 100644 index 0000000000..44316987fb --- /dev/null +++ b/external/gtest/test/gtest_uninitialized_test_.cc @@ -0,0 +1,43 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#include "gtest/gtest.h" + +TEST(DummyTest, Dummy) { + // This test doesn't verify anything. We just need it to create a + // realistic stage for testing the behavior of Google Test when + // RUN_ALL_TESTS() is called without testing::InitGoogleTest() being + // called first. +} + +int main() { + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_unittest.cc b/external/gtest/test/gtest_unittest.cc new file mode 100644 index 0000000000..0cab07d156 --- /dev/null +++ b/external/gtest/test/gtest_unittest.cc @@ -0,0 +1,7415 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Tests for Google Test itself. This verifies that the basic constructs of +// Google Test work. + +#include "gtest/gtest.h" + +// Verifies that the command line flag variables can be accessed +// in code once has been #included. +// Do not move it after other #includes. +TEST(CommandLineFlagsTest, CanBeAccessedInCodeOnceGTestHIsIncluded) { + bool dummy = testing::GTEST_FLAG(also_run_disabled_tests) + || testing::GTEST_FLAG(break_on_failure) + || testing::GTEST_FLAG(catch_exceptions) + || testing::GTEST_FLAG(color) != "unknown" + || testing::GTEST_FLAG(filter) != "unknown" + || testing::GTEST_FLAG(list_tests) + || testing::GTEST_FLAG(output) != "unknown" + || testing::GTEST_FLAG(print_time) + || testing::GTEST_FLAG(random_seed) + || testing::GTEST_FLAG(repeat) > 0 + || testing::GTEST_FLAG(show_internal_stack_frames) + || testing::GTEST_FLAG(shuffle) + || testing::GTEST_FLAG(stack_trace_depth) > 0 + || testing::GTEST_FLAG(stream_result_to) != "unknown" + || testing::GTEST_FLAG(throw_on_failure); + EXPECT_TRUE(dummy || !dummy); // Suppresses warning that dummy is unused. +} + +#include // For INT_MAX. +#include +#include +#include + +#include +#include +#include + +#include "gtest/gtest-spi.h" + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#include "src/gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if GTEST_CAN_STREAM_RESULTS_ + +class StreamingListenerTest : public Test { + public: + class FakeSocketWriter : public StreamingListener::AbstractSocketWriter { + public: + // Sends a string to the socket. + virtual void Send(const string& message) { output_ += message; } + + string output_; + }; + + StreamingListenerTest() + : fake_sock_writer_(new FakeSocketWriter), + streamer_(fake_sock_writer_), + test_info_obj_("FooTest", "Bar", NULL, NULL, 0, NULL) {} + + protected: + string* output() { return &(fake_sock_writer_->output_); } + + FakeSocketWriter* const fake_sock_writer_; + StreamingListener streamer_; + UnitTest unit_test_; + TestInfo test_info_obj_; // The name test_info_ was taken by testing::Test. +}; + +TEST_F(StreamingListenerTest, OnTestProgramEnd) { + *output() = ""; + streamer_.OnTestProgramEnd(unit_test_); + EXPECT_EQ("event=TestProgramEnd&passed=1\n", *output()); +} + +TEST_F(StreamingListenerTest, OnTestIterationEnd) { + *output() = ""; + streamer_.OnTestIterationEnd(unit_test_, 42); + EXPECT_EQ("event=TestIterationEnd&passed=1&elapsed_time=0ms\n", *output()); +} + +TEST_F(StreamingListenerTest, OnTestCaseStart) { + *output() = ""; + streamer_.OnTestCaseStart(TestCase("FooTest", "Bar", NULL, NULL)); + EXPECT_EQ("event=TestCaseStart&name=FooTest\n", *output()); +} + +TEST_F(StreamingListenerTest, OnTestCaseEnd) { + *output() = ""; + streamer_.OnTestCaseEnd(TestCase("FooTest", "Bar", NULL, NULL)); + EXPECT_EQ("event=TestCaseEnd&passed=1&elapsed_time=0ms\n", *output()); +} + +TEST_F(StreamingListenerTest, OnTestStart) { + *output() = ""; + streamer_.OnTestStart(test_info_obj_); + EXPECT_EQ("event=TestStart&name=Bar\n", *output()); +} + +TEST_F(StreamingListenerTest, OnTestEnd) { + *output() = ""; + streamer_.OnTestEnd(test_info_obj_); + EXPECT_EQ("event=TestEnd&passed=1&elapsed_time=0ms\n", *output()); +} + +TEST_F(StreamingListenerTest, OnTestPartResult) { + *output() = ""; + streamer_.OnTestPartResult(TestPartResult( + TestPartResult::kFatalFailure, "foo.cc", 42, "failed=\n&%")); + + // Meta characters in the failure message should be properly escaped. + EXPECT_EQ( + "event=TestPartResult&file=foo.cc&line=42&message=failed%3D%0A%26%25\n", + *output()); +} + +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Provides access to otherwise private parts of the TestEventListeners class +// that are needed to test it. +class TestEventListenersAccessor { + public: + static TestEventListener* GetRepeater(TestEventListeners* listeners) { + return listeners->repeater(); + } + + static void SetDefaultResultPrinter(TestEventListeners* listeners, + TestEventListener* listener) { + listeners->SetDefaultResultPrinter(listener); + } + static void SetDefaultXmlGenerator(TestEventListeners* listeners, + TestEventListener* listener) { + listeners->SetDefaultXmlGenerator(listener); + } + + static bool EventForwardingEnabled(const TestEventListeners& listeners) { + return listeners.EventForwardingEnabled(); + } + + static void SuppressEventForwarding(TestEventListeners* listeners) { + listeners->SuppressEventForwarding(); + } +}; + +class UnitTestRecordPropertyTestHelper : public Test { + protected: + UnitTestRecordPropertyTestHelper() {} + + // Forwards to UnitTest::RecordProperty() to bypass access controls. + void UnitTestRecordProperty(const char* key, const std::string& value) { + unit_test_.RecordProperty(key, value); + } + + UnitTest unit_test_; +}; + +} // namespace internal +} // namespace testing + +using testing::AssertionFailure; +using testing::AssertionResult; +using testing::AssertionSuccess; +using testing::DoubleLE; +using testing::EmptyTestEventListener; +using testing::Environment; +using testing::FloatLE; +using testing::GTEST_FLAG(also_run_disabled_tests); +using testing::GTEST_FLAG(break_on_failure); +using testing::GTEST_FLAG(catch_exceptions); +using testing::GTEST_FLAG(color); +using testing::GTEST_FLAG(death_test_use_fork); +using testing::GTEST_FLAG(filter); +using testing::GTEST_FLAG(list_tests); +using testing::GTEST_FLAG(output); +using testing::GTEST_FLAG(print_time); +using testing::GTEST_FLAG(random_seed); +using testing::GTEST_FLAG(repeat); +using testing::GTEST_FLAG(show_internal_stack_frames); +using testing::GTEST_FLAG(shuffle); +using testing::GTEST_FLAG(stack_trace_depth); +using testing::GTEST_FLAG(stream_result_to); +using testing::GTEST_FLAG(throw_on_failure); +using testing::IsNotSubstring; +using testing::IsSubstring; +using testing::Message; +using testing::ScopedFakeTestPartResultReporter; +using testing::StaticAssertTypeEq; +using testing::Test; +using testing::TestCase; +using testing::TestEventListeners; +using testing::TestInfo; +using testing::TestPartResult; +using testing::TestPartResultArray; +using testing::TestProperty; +using testing::TestResult; +using testing::TimeInMillis; +using testing::UnitTest; +using testing::kMaxStackTraceDepth; +using testing::internal::AddReference; +using testing::internal::AlwaysFalse; +using testing::internal::AlwaysTrue; +using testing::internal::AppendUserMessage; +using testing::internal::ArrayAwareFind; +using testing::internal::ArrayEq; +using testing::internal::CodePointToUtf8; +using testing::internal::CompileAssertTypesEqual; +using testing::internal::CopyArray; +using testing::internal::CountIf; +using testing::internal::EqFailure; +using testing::internal::FloatingPoint; +using testing::internal::ForEach; +using testing::internal::FormatEpochTimeInMillisAsIso8601; +using testing::internal::FormatTimeInMillisAsSeconds; +using testing::internal::GTestFlagSaver; +using testing::internal::GetCurrentOsStackTraceExceptTop; +using testing::internal::GetElementOr; +using testing::internal::GetNextRandomSeed; +using testing::internal::GetRandomSeedFromFlag; +using testing::internal::GetTestTypeId; +using testing::internal::GetTimeInMillis; +using testing::internal::GetTypeId; +using testing::internal::GetUnitTestImpl; +using testing::internal::ImplicitlyConvertible; +using testing::internal::Int32; +using testing::internal::Int32FromEnvOrDie; +using testing::internal::IsAProtocolMessage; +using testing::internal::IsContainer; +using testing::internal::IsContainerTest; +using testing::internal::IsNotContainer; +using testing::internal::NativeArray; +using testing::internal::ParseInt32Flag; +using testing::internal::RemoveConst; +using testing::internal::RemoveReference; +using testing::internal::ShouldRunTestOnShard; +using testing::internal::ShouldShard; +using testing::internal::ShouldUseColor; +using testing::internal::Shuffle; +using testing::internal::ShuffleRange; +using testing::internal::SkipPrefix; +using testing::internal::StreamableToString; +using testing::internal::String; +using testing::internal::TestEventListenersAccessor; +using testing::internal::TestResultAccessor; +using testing::internal::UInt32; +using testing::internal::WideStringToUtf8; +using testing::internal::kCopy; +using testing::internal::kMaxRandomSeed; +using testing::internal::kReference; +using testing::internal::kTestTypeIdInGoogleTest; +using testing::internal::scoped_ptr; + +#if GTEST_HAS_STREAM_REDIRECTION +using testing::internal::CaptureStdout; +using testing::internal::GetCapturedStdout; +#endif + +#if GTEST_IS_THREADSAFE +using testing::internal::ThreadWithParam; +#endif + +class TestingVector : public std::vector { +}; + +::std::ostream& operator<<(::std::ostream& os, + const TestingVector& vector) { + os << "{ "; + for (size_t i = 0; i < vector.size(); i++) { + os << vector[i] << " "; + } + os << "}"; + return os; +} + +// This line tests that we can define tests in an unnamed namespace. +namespace { + +TEST(GetRandomSeedFromFlagTest, HandlesZero) { + const int seed = GetRandomSeedFromFlag(0); + EXPECT_LE(1, seed); + EXPECT_LE(seed, static_cast(kMaxRandomSeed)); +} + +TEST(GetRandomSeedFromFlagTest, PreservesValidSeed) { + EXPECT_EQ(1, GetRandomSeedFromFlag(1)); + EXPECT_EQ(2, GetRandomSeedFromFlag(2)); + EXPECT_EQ(kMaxRandomSeed - 1, GetRandomSeedFromFlag(kMaxRandomSeed - 1)); + EXPECT_EQ(static_cast(kMaxRandomSeed), + GetRandomSeedFromFlag(kMaxRandomSeed)); +} + +TEST(GetRandomSeedFromFlagTest, NormalizesInvalidSeed) { + const int seed1 = GetRandomSeedFromFlag(-1); + EXPECT_LE(1, seed1); + EXPECT_LE(seed1, static_cast(kMaxRandomSeed)); + + const int seed2 = GetRandomSeedFromFlag(kMaxRandomSeed + 1); + EXPECT_LE(1, seed2); + EXPECT_LE(seed2, static_cast(kMaxRandomSeed)); +} + +TEST(GetNextRandomSeedTest, WorksForValidInput) { + EXPECT_EQ(2, GetNextRandomSeed(1)); + EXPECT_EQ(3, GetNextRandomSeed(2)); + EXPECT_EQ(static_cast(kMaxRandomSeed), + GetNextRandomSeed(kMaxRandomSeed - 1)); + EXPECT_EQ(1, GetNextRandomSeed(kMaxRandomSeed)); + + // We deliberately don't test GetNextRandomSeed() with invalid + // inputs, as that requires death tests, which are expensive. This + // is fine as GetNextRandomSeed() is internal and has a + // straightforward definition. +} + +static void ClearCurrentTestPartResults() { + TestResultAccessor::ClearTestPartResults( + GetUnitTestImpl()->current_test_result()); +} + +// Tests GetTypeId. + +TEST(GetTypeIdTest, ReturnsSameValueForSameType) { + EXPECT_EQ(GetTypeId(), GetTypeId()); + EXPECT_EQ(GetTypeId(), GetTypeId()); +} + +class SubClassOfTest : public Test {}; +class AnotherSubClassOfTest : public Test {}; + +TEST(GetTypeIdTest, ReturnsDifferentValuesForDifferentTypes) { + EXPECT_NE(GetTypeId(), GetTypeId()); + EXPECT_NE(GetTypeId(), GetTypeId()); + EXPECT_NE(GetTypeId(), GetTestTypeId()); + EXPECT_NE(GetTypeId(), GetTestTypeId()); + EXPECT_NE(GetTypeId(), GetTestTypeId()); + EXPECT_NE(GetTypeId(), GetTypeId()); +} + +// Verifies that GetTestTypeId() returns the same value, no matter it +// is called from inside Google Test or outside of it. +TEST(GetTestTypeIdTest, ReturnsTheSameValueInsideOrOutsideOfGoogleTest) { + EXPECT_EQ(kTestTypeIdInGoogleTest, GetTestTypeId()); +} + +// Tests FormatTimeInMillisAsSeconds(). + +TEST(FormatTimeInMillisAsSecondsTest, FormatsZero) { + EXPECT_EQ("0", FormatTimeInMillisAsSeconds(0)); +} + +TEST(FormatTimeInMillisAsSecondsTest, FormatsPositiveNumber) { + EXPECT_EQ("0.003", FormatTimeInMillisAsSeconds(3)); + EXPECT_EQ("0.01", FormatTimeInMillisAsSeconds(10)); + EXPECT_EQ("0.2", FormatTimeInMillisAsSeconds(200)); + EXPECT_EQ("1.2", FormatTimeInMillisAsSeconds(1200)); + EXPECT_EQ("3", FormatTimeInMillisAsSeconds(3000)); +} + +TEST(FormatTimeInMillisAsSecondsTest, FormatsNegativeNumber) { + EXPECT_EQ("-0.003", FormatTimeInMillisAsSeconds(-3)); + EXPECT_EQ("-0.01", FormatTimeInMillisAsSeconds(-10)); + EXPECT_EQ("-0.2", FormatTimeInMillisAsSeconds(-200)); + EXPECT_EQ("-1.2", FormatTimeInMillisAsSeconds(-1200)); + EXPECT_EQ("-3", FormatTimeInMillisAsSeconds(-3000)); +} + +// Tests FormatEpochTimeInMillisAsIso8601(). The correctness of conversion +// for particular dates below was verified in Python using +// datetime.datetime.fromutctimestamp(/1000). + +// FormatEpochTimeInMillisAsIso8601 depends on the current timezone, so we +// have to set up a particular timezone to obtain predictable results. +class FormatEpochTimeInMillisAsIso8601Test : public Test { + public: + // On Cygwin, GCC doesn't allow unqualified integer literals to exceed + // 32 bits, even when 64-bit integer types are available. We have to + // force the constants to have a 64-bit type here. + static const TimeInMillis kMillisPerSec = 1000; + + private: + virtual void SetUp() { + saved_tz_ = NULL; +#if _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe + // for getenv, function is deprecated for + // strdup). + if (getenv("TZ")) + saved_tz_ = strdup(getenv("TZ")); +# pragma warning(pop) // Restores the warning state again. +#else + if (getenv("TZ")) + saved_tz_ = strdup(getenv("TZ")); +#endif + + // Set up the time zone for FormatEpochTimeInMillisAsIso8601 to use. We + // cannot use the local time zone because the function's output depends + // on the time zone. + SetTimeZone("UTC+00"); + } + + virtual void TearDown() { + SetTimeZone(saved_tz_); + free(const_cast(saved_tz_)); + saved_tz_ = NULL; + } + + static void SetTimeZone(const char* time_zone) { + // tzset() distinguishes between the TZ variable being present and empty + // and not being present, so we have to consider the case of time_zone + // being NULL. +#if _MSC_VER + // ...Unless it's MSVC, whose standard library's _putenv doesn't + // distinguish between an empty and a missing variable. + const std::string env_var = + std::string("TZ=") + (time_zone ? time_zone : ""); + _putenv(env_var.c_str()); +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function is deprecated). + tzset(); +# pragma warning(pop) // Restores the warning state again. +#else + if (time_zone) { + setenv(("TZ"), time_zone, 1); + } else { + unsetenv("TZ"); + } + tzset(); +#endif + } + + const char* saved_tz_; +}; + +const TimeInMillis FormatEpochTimeInMillisAsIso8601Test::kMillisPerSec; + +TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsTwoDigitSegments) { + EXPECT_EQ("2011-10-31T18:52:42", + FormatEpochTimeInMillisAsIso8601(1320087162 * kMillisPerSec)); +} + +TEST_F(FormatEpochTimeInMillisAsIso8601Test, MillisecondsDoNotAffectResult) { + EXPECT_EQ( + "2011-10-31T18:52:42", + FormatEpochTimeInMillisAsIso8601(1320087162 * kMillisPerSec + 234)); +} + +TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsLeadingZeroes) { + EXPECT_EQ("2011-09-03T05:07:02", + FormatEpochTimeInMillisAsIso8601(1315026422 * kMillisPerSec)); +} + +TEST_F(FormatEpochTimeInMillisAsIso8601Test, Prints24HourTime) { + EXPECT_EQ("2011-09-28T17:08:22", + FormatEpochTimeInMillisAsIso8601(1317229702 * kMillisPerSec)); +} + +TEST_F(FormatEpochTimeInMillisAsIso8601Test, PrintsEpochStart) { + EXPECT_EQ("1970-01-01T00:00:00", FormatEpochTimeInMillisAsIso8601(0)); +} + +#if GTEST_CAN_COMPARE_NULL + +# ifdef __BORLANDC__ +// Silences warnings: "Condition is always true", "Unreachable code" +# pragma option push -w-ccc -w-rch +# endif + +// Tests that GTEST_IS_NULL_LITERAL_(x) is true when x is a null +// pointer literal. +TEST(NullLiteralTest, IsTrueForNullLiterals) { + EXPECT_TRUE(GTEST_IS_NULL_LITERAL_(NULL)); + EXPECT_TRUE(GTEST_IS_NULL_LITERAL_(0)); + EXPECT_TRUE(GTEST_IS_NULL_LITERAL_(0U)); + EXPECT_TRUE(GTEST_IS_NULL_LITERAL_(0L)); +} + +// Tests that GTEST_IS_NULL_LITERAL_(x) is false when x is not a null +// pointer literal. +TEST(NullLiteralTest, IsFalseForNonNullLiterals) { + EXPECT_FALSE(GTEST_IS_NULL_LITERAL_(1)); + EXPECT_FALSE(GTEST_IS_NULL_LITERAL_(0.0)); + EXPECT_FALSE(GTEST_IS_NULL_LITERAL_('a')); + EXPECT_FALSE(GTEST_IS_NULL_LITERAL_(static_cast(NULL))); +} + +# ifdef __BORLANDC__ +// Restores warnings after previous "#pragma option push" suppressed them. +# pragma option pop +# endif + +#endif // GTEST_CAN_COMPARE_NULL +// +// Tests CodePointToUtf8(). + +// Tests that the NUL character L'\0' is encoded correctly. +TEST(CodePointToUtf8Test, CanEncodeNul) { + EXPECT_EQ("", CodePointToUtf8(L'\0')); +} + +// Tests that ASCII characters are encoded correctly. +TEST(CodePointToUtf8Test, CanEncodeAscii) { + EXPECT_EQ("a", CodePointToUtf8(L'a')); + EXPECT_EQ("Z", CodePointToUtf8(L'Z')); + EXPECT_EQ("&", CodePointToUtf8(L'&')); + EXPECT_EQ("\x7F", CodePointToUtf8(L'\x7F')); +} + +// Tests that Unicode code-points that have 8 to 11 bits are encoded +// as 110xxxxx 10xxxxxx. +TEST(CodePointToUtf8Test, CanEncode8To11Bits) { + // 000 1101 0011 => 110-00011 10-010011 + EXPECT_EQ("\xC3\x93", CodePointToUtf8(L'\xD3')); + + // 101 0111 0110 => 110-10101 10-110110 + // Some compilers (e.g., GCC on MinGW) cannot handle non-ASCII codepoints + // in wide strings and wide chars. In order to accomodate them, we have to + // introduce such character constants as integers. + EXPECT_EQ("\xD5\xB6", + CodePointToUtf8(static_cast(0x576))); +} + +// Tests that Unicode code-points that have 12 to 16 bits are encoded +// as 1110xxxx 10xxxxxx 10xxxxxx. +TEST(CodePointToUtf8Test, CanEncode12To16Bits) { + // 0000 1000 1101 0011 => 1110-0000 10-100011 10-010011 + EXPECT_EQ("\xE0\xA3\x93", + CodePointToUtf8(static_cast(0x8D3))); + + // 1100 0111 0100 1101 => 1110-1100 10-011101 10-001101 + EXPECT_EQ("\xEC\x9D\x8D", + CodePointToUtf8(static_cast(0xC74D))); +} + +#if !GTEST_WIDE_STRING_USES_UTF16_ +// Tests in this group require a wchar_t to hold > 16 bits, and thus +// are skipped on Windows, Cygwin, and Symbian, where a wchar_t is +// 16-bit wide. This code may not compile on those systems. + +// Tests that Unicode code-points that have 17 to 21 bits are encoded +// as 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. +TEST(CodePointToUtf8Test, CanEncode17To21Bits) { + // 0 0001 0000 1000 1101 0011 => 11110-000 10-010000 10-100011 10-010011 + EXPECT_EQ("\xF0\x90\xA3\x93", CodePointToUtf8(L'\x108D3')); + + // 0 0001 0000 0100 0000 0000 => 11110-000 10-010000 10-010000 10-000000 + EXPECT_EQ("\xF0\x90\x90\x80", CodePointToUtf8(L'\x10400')); + + // 1 0000 1000 0110 0011 0100 => 11110-100 10-001000 10-011000 10-110100 + EXPECT_EQ("\xF4\x88\x98\xB4", CodePointToUtf8(L'\x108634')); +} + +// Tests that encoding an invalid code-point generates the expected result. +TEST(CodePointToUtf8Test, CanEncodeInvalidCodePoint) { + EXPECT_EQ("(Invalid Unicode 0x1234ABCD)", CodePointToUtf8(L'\x1234ABCD')); +} + +#endif // !GTEST_WIDE_STRING_USES_UTF16_ + +// Tests WideStringToUtf8(). + +// Tests that the NUL character L'\0' is encoded correctly. +TEST(WideStringToUtf8Test, CanEncodeNul) { + EXPECT_STREQ("", WideStringToUtf8(L"", 0).c_str()); + EXPECT_STREQ("", WideStringToUtf8(L"", -1).c_str()); +} + +// Tests that ASCII strings are encoded correctly. +TEST(WideStringToUtf8Test, CanEncodeAscii) { + EXPECT_STREQ("a", WideStringToUtf8(L"a", 1).c_str()); + EXPECT_STREQ("ab", WideStringToUtf8(L"ab", 2).c_str()); + EXPECT_STREQ("a", WideStringToUtf8(L"a", -1).c_str()); + EXPECT_STREQ("ab", WideStringToUtf8(L"ab", -1).c_str()); +} + +// Tests that Unicode code-points that have 8 to 11 bits are encoded +// as 110xxxxx 10xxxxxx. +TEST(WideStringToUtf8Test, CanEncode8To11Bits) { + // 000 1101 0011 => 110-00011 10-010011 + EXPECT_STREQ("\xC3\x93", WideStringToUtf8(L"\xD3", 1).c_str()); + EXPECT_STREQ("\xC3\x93", WideStringToUtf8(L"\xD3", -1).c_str()); + + // 101 0111 0110 => 110-10101 10-110110 + const wchar_t s[] = { 0x576, '\0' }; + EXPECT_STREQ("\xD5\xB6", WideStringToUtf8(s, 1).c_str()); + EXPECT_STREQ("\xD5\xB6", WideStringToUtf8(s, -1).c_str()); +} + +// Tests that Unicode code-points that have 12 to 16 bits are encoded +// as 1110xxxx 10xxxxxx 10xxxxxx. +TEST(WideStringToUtf8Test, CanEncode12To16Bits) { + // 0000 1000 1101 0011 => 1110-0000 10-100011 10-010011 + const wchar_t s1[] = { 0x8D3, '\0' }; + EXPECT_STREQ("\xE0\xA3\x93", WideStringToUtf8(s1, 1).c_str()); + EXPECT_STREQ("\xE0\xA3\x93", WideStringToUtf8(s1, -1).c_str()); + + // 1100 0111 0100 1101 => 1110-1100 10-011101 10-001101 + const wchar_t s2[] = { 0xC74D, '\0' }; + EXPECT_STREQ("\xEC\x9D\x8D", WideStringToUtf8(s2, 1).c_str()); + EXPECT_STREQ("\xEC\x9D\x8D", WideStringToUtf8(s2, -1).c_str()); +} + +// Tests that the conversion stops when the function encounters \0 character. +TEST(WideStringToUtf8Test, StopsOnNulCharacter) { + EXPECT_STREQ("ABC", WideStringToUtf8(L"ABC\0XYZ", 100).c_str()); +} + +// Tests that the conversion stops when the function reaches the limit +// specified by the 'length' parameter. +TEST(WideStringToUtf8Test, StopsWhenLengthLimitReached) { + EXPECT_STREQ("ABC", WideStringToUtf8(L"ABCDEF", 3).c_str()); +} + +#if !GTEST_WIDE_STRING_USES_UTF16_ +// Tests that Unicode code-points that have 17 to 21 bits are encoded +// as 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. This code may not compile +// on the systems using UTF-16 encoding. +TEST(WideStringToUtf8Test, CanEncode17To21Bits) { + // 0 0001 0000 1000 1101 0011 => 11110-000 10-010000 10-100011 10-010011 + EXPECT_STREQ("\xF0\x90\xA3\x93", WideStringToUtf8(L"\x108D3", 1).c_str()); + EXPECT_STREQ("\xF0\x90\xA3\x93", WideStringToUtf8(L"\x108D3", -1).c_str()); + + // 1 0000 1000 0110 0011 0100 => 11110-100 10-001000 10-011000 10-110100 + EXPECT_STREQ("\xF4\x88\x98\xB4", WideStringToUtf8(L"\x108634", 1).c_str()); + EXPECT_STREQ("\xF4\x88\x98\xB4", WideStringToUtf8(L"\x108634", -1).c_str()); +} + +// Tests that encoding an invalid code-point generates the expected result. +TEST(WideStringToUtf8Test, CanEncodeInvalidCodePoint) { + EXPECT_STREQ("(Invalid Unicode 0xABCDFF)", + WideStringToUtf8(L"\xABCDFF", -1).c_str()); +} +#else // !GTEST_WIDE_STRING_USES_UTF16_ +// Tests that surrogate pairs are encoded correctly on the systems using +// UTF-16 encoding in the wide strings. +TEST(WideStringToUtf8Test, CanEncodeValidUtf16SUrrogatePairs) { + const wchar_t s[] = { 0xD801, 0xDC00, '\0' }; + EXPECT_STREQ("\xF0\x90\x90\x80", WideStringToUtf8(s, -1).c_str()); +} + +// Tests that encoding an invalid UTF-16 surrogate pair +// generates the expected result. +TEST(WideStringToUtf8Test, CanEncodeInvalidUtf16SurrogatePair) { + // Leading surrogate is at the end of the string. + const wchar_t s1[] = { 0xD800, '\0' }; + EXPECT_STREQ("\xED\xA0\x80", WideStringToUtf8(s1, -1).c_str()); + // Leading surrogate is not followed by the trailing surrogate. + const wchar_t s2[] = { 0xD800, 'M', '\0' }; + EXPECT_STREQ("\xED\xA0\x80M", WideStringToUtf8(s2, -1).c_str()); + // Trailing surrogate appearas without a leading surrogate. + const wchar_t s3[] = { 0xDC00, 'P', 'Q', 'R', '\0' }; + EXPECT_STREQ("\xED\xB0\x80PQR", WideStringToUtf8(s3, -1).c_str()); +} +#endif // !GTEST_WIDE_STRING_USES_UTF16_ + +// Tests that codepoint concatenation works correctly. +#if !GTEST_WIDE_STRING_USES_UTF16_ +TEST(WideStringToUtf8Test, ConcatenatesCodepointsCorrectly) { + const wchar_t s[] = { 0x108634, 0xC74D, '\n', 0x576, 0x8D3, 0x108634, '\0'}; + EXPECT_STREQ( + "\xF4\x88\x98\xB4" + "\xEC\x9D\x8D" + "\n" + "\xD5\xB6" + "\xE0\xA3\x93" + "\xF4\x88\x98\xB4", + WideStringToUtf8(s, -1).c_str()); +} +#else +TEST(WideStringToUtf8Test, ConcatenatesCodepointsCorrectly) { + const wchar_t s[] = { 0xC74D, '\n', 0x576, 0x8D3, '\0'}; + EXPECT_STREQ( + "\xEC\x9D\x8D" "\n" "\xD5\xB6" "\xE0\xA3\x93", + WideStringToUtf8(s, -1).c_str()); +} +#endif // !GTEST_WIDE_STRING_USES_UTF16_ + +// Tests the Random class. + +TEST(RandomDeathTest, GeneratesCrashesOnInvalidRange) { + testing::internal::Random random(42); + EXPECT_DEATH_IF_SUPPORTED( + random.Generate(0), + "Cannot generate a number in the range \\[0, 0\\)"); + EXPECT_DEATH_IF_SUPPORTED( + random.Generate(testing::internal::Random::kMaxRange + 1), + "Generation of a number in \\[0, 2147483649\\) was requested, " + "but this can only generate numbers in \\[0, 2147483648\\)"); +} + +TEST(RandomTest, GeneratesNumbersWithinRange) { + const UInt32 kRange = 10000; + testing::internal::Random random(12345); + for (int i = 0; i < 10; i++) { + EXPECT_LT(random.Generate(kRange), kRange) << " for iteration " << i; + } + + testing::internal::Random random2(testing::internal::Random::kMaxRange); + for (int i = 0; i < 10; i++) { + EXPECT_LT(random2.Generate(kRange), kRange) << " for iteration " << i; + } +} + +TEST(RandomTest, RepeatsWhenReseeded) { + const int kSeed = 123; + const int kArraySize = 10; + const UInt32 kRange = 10000; + UInt32 values[kArraySize]; + + testing::internal::Random random(kSeed); + for (int i = 0; i < kArraySize; i++) { + values[i] = random.Generate(kRange); + } + + random.Reseed(kSeed); + for (int i = 0; i < kArraySize; i++) { + EXPECT_EQ(values[i], random.Generate(kRange)) << " for iteration " << i; + } +} + +// Tests STL container utilities. + +// Tests CountIf(). + +static bool IsPositive(int n) { return n > 0; } + +TEST(ContainerUtilityTest, CountIf) { + std::vector v; + EXPECT_EQ(0, CountIf(v, IsPositive)); // Works for an empty container. + + v.push_back(-1); + v.push_back(0); + EXPECT_EQ(0, CountIf(v, IsPositive)); // Works when no value satisfies. + + v.push_back(2); + v.push_back(-10); + v.push_back(10); + EXPECT_EQ(2, CountIf(v, IsPositive)); +} + +// Tests ForEach(). + +static int g_sum = 0; +static void Accumulate(int n) { g_sum += n; } + +TEST(ContainerUtilityTest, ForEach) { + std::vector v; + g_sum = 0; + ForEach(v, Accumulate); + EXPECT_EQ(0, g_sum); // Works for an empty container; + + g_sum = 0; + v.push_back(1); + ForEach(v, Accumulate); + EXPECT_EQ(1, g_sum); // Works for a container with one element. + + g_sum = 0; + v.push_back(20); + v.push_back(300); + ForEach(v, Accumulate); + EXPECT_EQ(321, g_sum); +} + +// Tests GetElementOr(). +TEST(ContainerUtilityTest, GetElementOr) { + std::vector a; + EXPECT_EQ('x', GetElementOr(a, 0, 'x')); + + a.push_back('a'); + a.push_back('b'); + EXPECT_EQ('a', GetElementOr(a, 0, 'x')); + EXPECT_EQ('b', GetElementOr(a, 1, 'x')); + EXPECT_EQ('x', GetElementOr(a, -2, 'x')); + EXPECT_EQ('x', GetElementOr(a, 2, 'x')); +} + +TEST(ContainerUtilityDeathTest, ShuffleRange) { + std::vector a; + a.push_back(0); + a.push_back(1); + a.push_back(2); + testing::internal::Random random(1); + + EXPECT_DEATH_IF_SUPPORTED( + ShuffleRange(&random, -1, 1, &a), + "Invalid shuffle range start -1: must be in range \\[0, 3\\]"); + EXPECT_DEATH_IF_SUPPORTED( + ShuffleRange(&random, 4, 4, &a), + "Invalid shuffle range start 4: must be in range \\[0, 3\\]"); + EXPECT_DEATH_IF_SUPPORTED( + ShuffleRange(&random, 3, 2, &a), + "Invalid shuffle range finish 2: must be in range \\[3, 3\\]"); + EXPECT_DEATH_IF_SUPPORTED( + ShuffleRange(&random, 3, 4, &a), + "Invalid shuffle range finish 4: must be in range \\[3, 3\\]"); +} + +class VectorShuffleTest : public Test { + protected: + static const int kVectorSize = 20; + + VectorShuffleTest() : random_(1) { + for (int i = 0; i < kVectorSize; i++) { + vector_.push_back(i); + } + } + + static bool VectorIsCorrupt(const TestingVector& vector) { + if (kVectorSize != static_cast(vector.size())) { + return true; + } + + bool found_in_vector[kVectorSize] = { false }; + for (size_t i = 0; i < vector.size(); i++) { + const int e = vector[i]; + if (e < 0 || e >= kVectorSize || found_in_vector[e]) { + return true; + } + found_in_vector[e] = true; + } + + // Vector size is correct, elements' range is correct, no + // duplicate elements. Therefore no corruption has occurred. + return false; + } + + static bool VectorIsNotCorrupt(const TestingVector& vector) { + return !VectorIsCorrupt(vector); + } + + static bool RangeIsShuffled(const TestingVector& vector, int begin, int end) { + for (int i = begin; i < end; i++) { + if (i != vector[i]) { + return true; + } + } + return false; + } + + static bool RangeIsUnshuffled( + const TestingVector& vector, int begin, int end) { + return !RangeIsShuffled(vector, begin, end); + } + + static bool VectorIsShuffled(const TestingVector& vector) { + return RangeIsShuffled(vector, 0, static_cast(vector.size())); + } + + static bool VectorIsUnshuffled(const TestingVector& vector) { + return !VectorIsShuffled(vector); + } + + testing::internal::Random random_; + TestingVector vector_; +}; // class VectorShuffleTest + +const int VectorShuffleTest::kVectorSize; + +TEST_F(VectorShuffleTest, HandlesEmptyRange) { + // Tests an empty range at the beginning... + ShuffleRange(&random_, 0, 0, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); + + // ...in the middle... + ShuffleRange(&random_, kVectorSize/2, kVectorSize/2, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); + + // ...at the end... + ShuffleRange(&random_, kVectorSize - 1, kVectorSize - 1, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); + + // ...and past the end. + ShuffleRange(&random_, kVectorSize, kVectorSize, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); +} + +TEST_F(VectorShuffleTest, HandlesRangeOfSizeOne) { + // Tests a size one range at the beginning... + ShuffleRange(&random_, 0, 1, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); + + // ...in the middle... + ShuffleRange(&random_, kVectorSize/2, kVectorSize/2 + 1, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); + + // ...and at the end. + ShuffleRange(&random_, kVectorSize - 1, kVectorSize, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsUnshuffled, vector_); +} + +// Because we use our own random number generator and a fixed seed, +// we can guarantee that the following "random" tests will succeed. + +TEST_F(VectorShuffleTest, ShufflesEntireVector) { + Shuffle(&random_, &vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + EXPECT_FALSE(VectorIsUnshuffled(vector_)) << vector_; + + // Tests the first and last elements in particular to ensure that + // there are no off-by-one problems in our shuffle algorithm. + EXPECT_NE(0, vector_[0]); + EXPECT_NE(kVectorSize - 1, vector_[kVectorSize - 1]); +} + +TEST_F(VectorShuffleTest, ShufflesStartOfVector) { + const int kRangeSize = kVectorSize/2; + + ShuffleRange(&random_, 0, kRangeSize, &vector_); + + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + EXPECT_PRED3(RangeIsShuffled, vector_, 0, kRangeSize); + EXPECT_PRED3(RangeIsUnshuffled, vector_, kRangeSize, kVectorSize); +} + +TEST_F(VectorShuffleTest, ShufflesEndOfVector) { + const int kRangeSize = kVectorSize / 2; + ShuffleRange(&random_, kRangeSize, kVectorSize, &vector_); + + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + EXPECT_PRED3(RangeIsUnshuffled, vector_, 0, kRangeSize); + EXPECT_PRED3(RangeIsShuffled, vector_, kRangeSize, kVectorSize); +} + +TEST_F(VectorShuffleTest, ShufflesMiddleOfVector) { + int kRangeSize = kVectorSize/3; + ShuffleRange(&random_, kRangeSize, 2*kRangeSize, &vector_); + + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + EXPECT_PRED3(RangeIsUnshuffled, vector_, 0, kRangeSize); + EXPECT_PRED3(RangeIsShuffled, vector_, kRangeSize, 2*kRangeSize); + EXPECT_PRED3(RangeIsUnshuffled, vector_, 2*kRangeSize, kVectorSize); +} + +TEST_F(VectorShuffleTest, ShufflesRepeatably) { + TestingVector vector2; + for (int i = 0; i < kVectorSize; i++) { + vector2.push_back(i); + } + + random_.Reseed(1234); + Shuffle(&random_, &vector_); + random_.Reseed(1234); + Shuffle(&random_, &vector2); + + ASSERT_PRED1(VectorIsNotCorrupt, vector_); + ASSERT_PRED1(VectorIsNotCorrupt, vector2); + + for (int i = 0; i < kVectorSize; i++) { + EXPECT_EQ(vector_[i], vector2[i]) << " where i is " << i; + } +} + +// Tests the size of the AssertHelper class. + +TEST(AssertHelperTest, AssertHelperIsSmall) { + // To avoid breaking clients that use lots of assertions in one + // function, we cannot grow the size of AssertHelper. + EXPECT_LE(sizeof(testing::internal::AssertHelper), sizeof(void*)); +} + +// Tests String::EndsWithCaseInsensitive(). +TEST(StringTest, EndsWithCaseInsensitive) { + EXPECT_TRUE(String::EndsWithCaseInsensitive("foobar", "BAR")); + EXPECT_TRUE(String::EndsWithCaseInsensitive("foobaR", "bar")); + EXPECT_TRUE(String::EndsWithCaseInsensitive("foobar", "")); + EXPECT_TRUE(String::EndsWithCaseInsensitive("", "")); + + EXPECT_FALSE(String::EndsWithCaseInsensitive("Foobar", "foo")); + EXPECT_FALSE(String::EndsWithCaseInsensitive("foobar", "Foo")); + EXPECT_FALSE(String::EndsWithCaseInsensitive("", "foo")); +} + +// C++Builder's preprocessor is buggy; it fails to expand macros that +// appear in macro parameters after wide char literals. Provide an alias +// for NULL as a workaround. +static const wchar_t* const kNull = NULL; + +// Tests String::CaseInsensitiveWideCStringEquals +TEST(StringTest, CaseInsensitiveWideCStringEquals) { + EXPECT_TRUE(String::CaseInsensitiveWideCStringEquals(NULL, NULL)); + EXPECT_FALSE(String::CaseInsensitiveWideCStringEquals(kNull, L"")); + EXPECT_FALSE(String::CaseInsensitiveWideCStringEquals(L"", kNull)); + EXPECT_FALSE(String::CaseInsensitiveWideCStringEquals(kNull, L"foobar")); + EXPECT_FALSE(String::CaseInsensitiveWideCStringEquals(L"foobar", kNull)); + EXPECT_TRUE(String::CaseInsensitiveWideCStringEquals(L"foobar", L"foobar")); + EXPECT_TRUE(String::CaseInsensitiveWideCStringEquals(L"foobar", L"FOOBAR")); + EXPECT_TRUE(String::CaseInsensitiveWideCStringEquals(L"FOOBAR", L"foobar")); +} + +#if GTEST_OS_WINDOWS + +// Tests String::ShowWideCString(). +TEST(StringTest, ShowWideCString) { + EXPECT_STREQ("(null)", + String::ShowWideCString(NULL).c_str()); + EXPECT_STREQ("", String::ShowWideCString(L"").c_str()); + EXPECT_STREQ("foo", String::ShowWideCString(L"foo").c_str()); +} + +# if GTEST_OS_WINDOWS_MOBILE +TEST(StringTest, AnsiAndUtf16Null) { + EXPECT_EQ(NULL, String::AnsiToUtf16(NULL)); + EXPECT_EQ(NULL, String::Utf16ToAnsi(NULL)); +} + +TEST(StringTest, AnsiAndUtf16ConvertBasic) { + const char* ansi = String::Utf16ToAnsi(L"str"); + EXPECT_STREQ("str", ansi); + delete [] ansi; + const WCHAR* utf16 = String::AnsiToUtf16("str"); + EXPECT_EQ(0, wcsncmp(L"str", utf16, 3)); + delete [] utf16; +} + +TEST(StringTest, AnsiAndUtf16ConvertPathChars) { + const char* ansi = String::Utf16ToAnsi(L".:\\ \"*?"); + EXPECT_STREQ(".:\\ \"*?", ansi); + delete [] ansi; + const WCHAR* utf16 = String::AnsiToUtf16(".:\\ \"*?"); + EXPECT_EQ(0, wcsncmp(L".:\\ \"*?", utf16, 3)); + delete [] utf16; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#endif // GTEST_OS_WINDOWS + +// Tests TestProperty construction. +TEST(TestPropertyTest, StringValue) { + TestProperty property("key", "1"); + EXPECT_STREQ("key", property.key()); + EXPECT_STREQ("1", property.value()); +} + +// Tests TestProperty replacing a value. +TEST(TestPropertyTest, ReplaceStringValue) { + TestProperty property("key", "1"); + EXPECT_STREQ("1", property.value()); + property.SetValue("2"); + EXPECT_STREQ("2", property.value()); +} + +// AddFatalFailure() and AddNonfatalFailure() must be stand-alone +// functions (i.e. their definitions cannot be inlined at the call +// sites), or C++Builder won't compile the code. +static void AddFatalFailure() { + FAIL() << "Expected fatal failure."; +} + +static void AddNonfatalFailure() { + ADD_FAILURE() << "Expected non-fatal failure."; +} + +class ScopedFakeTestPartResultReporterTest : public Test { + public: // Must be public and not protected due to a bug in g++ 3.4.2. + enum FailureMode { + FATAL_FAILURE, + NONFATAL_FAILURE + }; + static void AddFailure(FailureMode failure) { + if (failure == FATAL_FAILURE) { + AddFatalFailure(); + } else { + AddNonfatalFailure(); + } + } +}; + +// Tests that ScopedFakeTestPartResultReporter intercepts test +// failures. +TEST_F(ScopedFakeTestPartResultReporterTest, InterceptsTestFailures) { + TestPartResultArray results; + { + ScopedFakeTestPartResultReporter reporter( + ScopedFakeTestPartResultReporter::INTERCEPT_ONLY_CURRENT_THREAD, + &results); + AddFailure(NONFATAL_FAILURE); + AddFailure(FATAL_FAILURE); + } + + EXPECT_EQ(2, results.size()); + EXPECT_TRUE(results.GetTestPartResult(0).nonfatally_failed()); + EXPECT_TRUE(results.GetTestPartResult(1).fatally_failed()); +} + +TEST_F(ScopedFakeTestPartResultReporterTest, DeprecatedConstructor) { + TestPartResultArray results; + { + // Tests, that the deprecated constructor still works. + ScopedFakeTestPartResultReporter reporter(&results); + AddFailure(NONFATAL_FAILURE); + } + EXPECT_EQ(1, results.size()); +} + +#if GTEST_IS_THREADSAFE + +class ScopedFakeTestPartResultReporterWithThreadsTest + : public ScopedFakeTestPartResultReporterTest { + protected: + static void AddFailureInOtherThread(FailureMode failure) { + ThreadWithParam thread(&AddFailure, failure, NULL); + thread.Join(); + } +}; + +TEST_F(ScopedFakeTestPartResultReporterWithThreadsTest, + InterceptsTestFailuresInAllThreads) { + TestPartResultArray results; + { + ScopedFakeTestPartResultReporter reporter( + ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, &results); + AddFailure(NONFATAL_FAILURE); + AddFailure(FATAL_FAILURE); + AddFailureInOtherThread(NONFATAL_FAILURE); + AddFailureInOtherThread(FATAL_FAILURE); + } + + EXPECT_EQ(4, results.size()); + EXPECT_TRUE(results.GetTestPartResult(0).nonfatally_failed()); + EXPECT_TRUE(results.GetTestPartResult(1).fatally_failed()); + EXPECT_TRUE(results.GetTestPartResult(2).nonfatally_failed()); + EXPECT_TRUE(results.GetTestPartResult(3).fatally_failed()); +} + +#endif // GTEST_IS_THREADSAFE + +// Tests EXPECT_FATAL_FAILURE{,ON_ALL_THREADS}. Makes sure that they +// work even if the failure is generated in a called function rather than +// the current context. + +typedef ScopedFakeTestPartResultReporterTest ExpectFatalFailureTest; + +TEST_F(ExpectFatalFailureTest, CatchesFatalFaliure) { + EXPECT_FATAL_FAILURE(AddFatalFailure(), "Expected fatal failure."); +} + +#if GTEST_HAS_GLOBAL_STRING +TEST_F(ExpectFatalFailureTest, AcceptsStringObject) { + EXPECT_FATAL_FAILURE(AddFatalFailure(), ::string("Expected fatal failure.")); +} +#endif + +TEST_F(ExpectFatalFailureTest, AcceptsStdStringObject) { + EXPECT_FATAL_FAILURE(AddFatalFailure(), + ::std::string("Expected fatal failure.")); +} + +TEST_F(ExpectFatalFailureTest, CatchesFatalFailureOnAllThreads) { + // We have another test below to verify that the macro catches fatal + // failures generated on another thread. + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(AddFatalFailure(), + "Expected fatal failure."); +} + +#ifdef __BORLANDC__ +// Silences warnings: "Condition is always true" +# pragma option push -w-ccc +#endif + +// Tests that EXPECT_FATAL_FAILURE() can be used in a non-void +// function even when the statement in it contains ASSERT_*. + +int NonVoidFunction() { + EXPECT_FATAL_FAILURE(ASSERT_TRUE(false), ""); + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(FAIL(), ""); + return 0; +} + +TEST_F(ExpectFatalFailureTest, CanBeUsedInNonVoidFunction) { + NonVoidFunction(); +} + +// Tests that EXPECT_FATAL_FAILURE(statement, ...) doesn't abort the +// current function even though 'statement' generates a fatal failure. + +void DoesNotAbortHelper(bool* aborted) { + EXPECT_FATAL_FAILURE(ASSERT_TRUE(false), ""); + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(FAIL(), ""); + + *aborted = false; +} + +#ifdef __BORLANDC__ +// Restores warnings after previous "#pragma option push" suppressed them. +# pragma option pop +#endif + +TEST_F(ExpectFatalFailureTest, DoesNotAbort) { + bool aborted = true; + DoesNotAbortHelper(&aborted); + EXPECT_FALSE(aborted); +} + +// Tests that the EXPECT_FATAL_FAILURE{,_ON_ALL_THREADS} accepts a +// statement that contains a macro which expands to code containing an +// unprotected comma. + +static int global_var = 0; +#define GTEST_USE_UNPROTECTED_COMMA_ global_var++, global_var++ + +TEST_F(ExpectFatalFailureTest, AcceptsMacroThatExpandsToUnprotectedComma) { +#ifndef __BORLANDC__ + // ICE's in C++Builder. + EXPECT_FATAL_FAILURE({ + GTEST_USE_UNPROTECTED_COMMA_; + AddFatalFailure(); + }, ""); +#endif + + EXPECT_FATAL_FAILURE_ON_ALL_THREADS({ + GTEST_USE_UNPROTECTED_COMMA_; + AddFatalFailure(); + }, ""); +} + +// Tests EXPECT_NONFATAL_FAILURE{,ON_ALL_THREADS}. + +typedef ScopedFakeTestPartResultReporterTest ExpectNonfatalFailureTest; + +TEST_F(ExpectNonfatalFailureTest, CatchesNonfatalFailure) { + EXPECT_NONFATAL_FAILURE(AddNonfatalFailure(), + "Expected non-fatal failure."); +} + +#if GTEST_HAS_GLOBAL_STRING +TEST_F(ExpectNonfatalFailureTest, AcceptsStringObject) { + EXPECT_NONFATAL_FAILURE(AddNonfatalFailure(), + ::string("Expected non-fatal failure.")); +} +#endif + +TEST_F(ExpectNonfatalFailureTest, AcceptsStdStringObject) { + EXPECT_NONFATAL_FAILURE(AddNonfatalFailure(), + ::std::string("Expected non-fatal failure.")); +} + +TEST_F(ExpectNonfatalFailureTest, CatchesNonfatalFailureOnAllThreads) { + // We have another test below to verify that the macro catches + // non-fatal failures generated on another thread. + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(AddNonfatalFailure(), + "Expected non-fatal failure."); +} + +// Tests that the EXPECT_NONFATAL_FAILURE{,_ON_ALL_THREADS} accepts a +// statement that contains a macro which expands to code containing an +// unprotected comma. +TEST_F(ExpectNonfatalFailureTest, AcceptsMacroThatExpandsToUnprotectedComma) { + EXPECT_NONFATAL_FAILURE({ + GTEST_USE_UNPROTECTED_COMMA_; + AddNonfatalFailure(); + }, ""); + + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS({ + GTEST_USE_UNPROTECTED_COMMA_; + AddNonfatalFailure(); + }, ""); +} + +#if GTEST_IS_THREADSAFE + +typedef ScopedFakeTestPartResultReporterWithThreadsTest + ExpectFailureWithThreadsTest; + +TEST_F(ExpectFailureWithThreadsTest, ExpectFatalFailureOnAllThreads) { + EXPECT_FATAL_FAILURE_ON_ALL_THREADS(AddFailureInOtherThread(FATAL_FAILURE), + "Expected fatal failure."); +} + +TEST_F(ExpectFailureWithThreadsTest, ExpectNonFatalFailureOnAllThreads) { + EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS( + AddFailureInOtherThread(NONFATAL_FAILURE), "Expected non-fatal failure."); +} + +#endif // GTEST_IS_THREADSAFE + +// Tests the TestProperty class. + +TEST(TestPropertyTest, ConstructorWorks) { + const TestProperty property("key", "value"); + EXPECT_STREQ("key", property.key()); + EXPECT_STREQ("value", property.value()); +} + +TEST(TestPropertyTest, SetValue) { + TestProperty property("key", "value_1"); + EXPECT_STREQ("key", property.key()); + property.SetValue("value_2"); + EXPECT_STREQ("key", property.key()); + EXPECT_STREQ("value_2", property.value()); +} + +// Tests the TestResult class + +// The test fixture for testing TestResult. +class TestResultTest : public Test { + protected: + typedef std::vector TPRVector; + + // We make use of 2 TestPartResult objects, + TestPartResult * pr1, * pr2; + + // ... and 3 TestResult objects. + TestResult * r0, * r1, * r2; + + virtual void SetUp() { + // pr1 is for success. + pr1 = new TestPartResult(TestPartResult::kSuccess, + "foo/bar.cc", + 10, + "Success!"); + + // pr2 is for fatal failure. + pr2 = new TestPartResult(TestPartResult::kFatalFailure, + "foo/bar.cc", + -1, // This line number means "unknown" + "Failure!"); + + // Creates the TestResult objects. + r0 = new TestResult(); + r1 = new TestResult(); + r2 = new TestResult(); + + // In order to test TestResult, we need to modify its internal + // state, in particular the TestPartResult vector it holds. + // test_part_results() returns a const reference to this vector. + // We cast it to a non-const object s.t. it can be modified (yes, + // this is a hack). + TPRVector* results1 = const_cast( + &TestResultAccessor::test_part_results(*r1)); + TPRVector* results2 = const_cast( + &TestResultAccessor::test_part_results(*r2)); + + // r0 is an empty TestResult. + + // r1 contains a single SUCCESS TestPartResult. + results1->push_back(*pr1); + + // r2 contains a SUCCESS, and a FAILURE. + results2->push_back(*pr1); + results2->push_back(*pr2); + } + + virtual void TearDown() { + delete pr1; + delete pr2; + + delete r0; + delete r1; + delete r2; + } + + // Helper that compares two two TestPartResults. + static void CompareTestPartResult(const TestPartResult& expected, + const TestPartResult& actual) { + EXPECT_EQ(expected.type(), actual.type()); + EXPECT_STREQ(expected.file_name(), actual.file_name()); + EXPECT_EQ(expected.line_number(), actual.line_number()); + EXPECT_STREQ(expected.summary(), actual.summary()); + EXPECT_STREQ(expected.message(), actual.message()); + EXPECT_EQ(expected.passed(), actual.passed()); + EXPECT_EQ(expected.failed(), actual.failed()); + EXPECT_EQ(expected.nonfatally_failed(), actual.nonfatally_failed()); + EXPECT_EQ(expected.fatally_failed(), actual.fatally_failed()); + } +}; + +// Tests TestResult::total_part_count(). +TEST_F(TestResultTest, total_part_count) { + ASSERT_EQ(0, r0->total_part_count()); + ASSERT_EQ(1, r1->total_part_count()); + ASSERT_EQ(2, r2->total_part_count()); +} + +// Tests TestResult::Passed(). +TEST_F(TestResultTest, Passed) { + ASSERT_TRUE(r0->Passed()); + ASSERT_TRUE(r1->Passed()); + ASSERT_FALSE(r2->Passed()); +} + +// Tests TestResult::Failed(). +TEST_F(TestResultTest, Failed) { + ASSERT_FALSE(r0->Failed()); + ASSERT_FALSE(r1->Failed()); + ASSERT_TRUE(r2->Failed()); +} + +// Tests TestResult::GetTestPartResult(). + +typedef TestResultTest TestResultDeathTest; + +TEST_F(TestResultDeathTest, GetTestPartResult) { + CompareTestPartResult(*pr1, r2->GetTestPartResult(0)); + CompareTestPartResult(*pr2, r2->GetTestPartResult(1)); + EXPECT_DEATH_IF_SUPPORTED(r2->GetTestPartResult(2), ""); + EXPECT_DEATH_IF_SUPPORTED(r2->GetTestPartResult(-1), ""); +} + +// Tests TestResult has no properties when none are added. +TEST(TestResultPropertyTest, NoPropertiesFoundWhenNoneAreAdded) { + TestResult test_result; + ASSERT_EQ(0, test_result.test_property_count()); +} + +// Tests TestResult has the expected property when added. +TEST(TestResultPropertyTest, OnePropertyFoundWhenAdded) { + TestResult test_result; + TestProperty property("key_1", "1"); + TestResultAccessor::RecordProperty(&test_result, "testcase", property); + ASSERT_EQ(1, test_result.test_property_count()); + const TestProperty& actual_property = test_result.GetTestProperty(0); + EXPECT_STREQ("key_1", actual_property.key()); + EXPECT_STREQ("1", actual_property.value()); +} + +// Tests TestResult has multiple properties when added. +TEST(TestResultPropertyTest, MultiplePropertiesFoundWhenAdded) { + TestResult test_result; + TestProperty property_1("key_1", "1"); + TestProperty property_2("key_2", "2"); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_1); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_2); + ASSERT_EQ(2, test_result.test_property_count()); + const TestProperty& actual_property_1 = test_result.GetTestProperty(0); + EXPECT_STREQ("key_1", actual_property_1.key()); + EXPECT_STREQ("1", actual_property_1.value()); + + const TestProperty& actual_property_2 = test_result.GetTestProperty(1); + EXPECT_STREQ("key_2", actual_property_2.key()); + EXPECT_STREQ("2", actual_property_2.value()); +} + +// Tests TestResult::RecordProperty() overrides values for duplicate keys. +TEST(TestResultPropertyTest, OverridesValuesForDuplicateKeys) { + TestResult test_result; + TestProperty property_1_1("key_1", "1"); + TestProperty property_2_1("key_2", "2"); + TestProperty property_1_2("key_1", "12"); + TestProperty property_2_2("key_2", "22"); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_1_1); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_2_1); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_1_2); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_2_2); + + ASSERT_EQ(2, test_result.test_property_count()); + const TestProperty& actual_property_1 = test_result.GetTestProperty(0); + EXPECT_STREQ("key_1", actual_property_1.key()); + EXPECT_STREQ("12", actual_property_1.value()); + + const TestProperty& actual_property_2 = test_result.GetTestProperty(1); + EXPECT_STREQ("key_2", actual_property_2.key()); + EXPECT_STREQ("22", actual_property_2.value()); +} + +// Tests TestResult::GetTestProperty(). +TEST(TestResultPropertyTest, GetTestProperty) { + TestResult test_result; + TestProperty property_1("key_1", "1"); + TestProperty property_2("key_2", "2"); + TestProperty property_3("key_3", "3"); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_1); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_2); + TestResultAccessor::RecordProperty(&test_result, "testcase", property_3); + + const TestProperty& fetched_property_1 = test_result.GetTestProperty(0); + const TestProperty& fetched_property_2 = test_result.GetTestProperty(1); + const TestProperty& fetched_property_3 = test_result.GetTestProperty(2); + + EXPECT_STREQ("key_1", fetched_property_1.key()); + EXPECT_STREQ("1", fetched_property_1.value()); + + EXPECT_STREQ("key_2", fetched_property_2.key()); + EXPECT_STREQ("2", fetched_property_2.value()); + + EXPECT_STREQ("key_3", fetched_property_3.key()); + EXPECT_STREQ("3", fetched_property_3.value()); + + EXPECT_DEATH_IF_SUPPORTED(test_result.GetTestProperty(3), ""); + EXPECT_DEATH_IF_SUPPORTED(test_result.GetTestProperty(-1), ""); +} + +// Tests that GTestFlagSaver works on Windows and Mac. + +class GTestFlagSaverTest : public Test { + protected: + // Saves the Google Test flags such that we can restore them later, and + // then sets them to their default values. This will be called + // before the first test in this test case is run. + static void SetUpTestCase() { + saver_ = new GTestFlagSaver; + + GTEST_FLAG(also_run_disabled_tests) = false; + GTEST_FLAG(break_on_failure) = false; + GTEST_FLAG(catch_exceptions) = false; + GTEST_FLAG(death_test_use_fork) = false; + GTEST_FLAG(color) = "auto"; + GTEST_FLAG(filter) = ""; + GTEST_FLAG(list_tests) = false; + GTEST_FLAG(output) = ""; + GTEST_FLAG(print_time) = true; + GTEST_FLAG(random_seed) = 0; + GTEST_FLAG(repeat) = 1; + GTEST_FLAG(shuffle) = false; + GTEST_FLAG(stack_trace_depth) = kMaxStackTraceDepth; + GTEST_FLAG(stream_result_to) = ""; + GTEST_FLAG(throw_on_failure) = false; + } + + // Restores the Google Test flags that the tests have modified. This will + // be called after the last test in this test case is run. + static void TearDownTestCase() { + delete saver_; + saver_ = NULL; + } + + // Verifies that the Google Test flags have their default values, and then + // modifies each of them. + void VerifyAndModifyFlags() { + EXPECT_FALSE(GTEST_FLAG(also_run_disabled_tests)); + EXPECT_FALSE(GTEST_FLAG(break_on_failure)); + EXPECT_FALSE(GTEST_FLAG(catch_exceptions)); + EXPECT_STREQ("auto", GTEST_FLAG(color).c_str()); + EXPECT_FALSE(GTEST_FLAG(death_test_use_fork)); + EXPECT_STREQ("", GTEST_FLAG(filter).c_str()); + EXPECT_FALSE(GTEST_FLAG(list_tests)); + EXPECT_STREQ("", GTEST_FLAG(output).c_str()); + EXPECT_TRUE(GTEST_FLAG(print_time)); + EXPECT_EQ(0, GTEST_FLAG(random_seed)); + EXPECT_EQ(1, GTEST_FLAG(repeat)); + EXPECT_FALSE(GTEST_FLAG(shuffle)); + EXPECT_EQ(kMaxStackTraceDepth, GTEST_FLAG(stack_trace_depth)); + EXPECT_STREQ("", GTEST_FLAG(stream_result_to).c_str()); + EXPECT_FALSE(GTEST_FLAG(throw_on_failure)); + + GTEST_FLAG(also_run_disabled_tests) = true; + GTEST_FLAG(break_on_failure) = true; + GTEST_FLAG(catch_exceptions) = true; + GTEST_FLAG(color) = "no"; + GTEST_FLAG(death_test_use_fork) = true; + GTEST_FLAG(filter) = "abc"; + GTEST_FLAG(list_tests) = true; + GTEST_FLAG(output) = "xml:foo.xml"; + GTEST_FLAG(print_time) = false; + GTEST_FLAG(random_seed) = 1; + GTEST_FLAG(repeat) = 100; + GTEST_FLAG(shuffle) = true; + GTEST_FLAG(stack_trace_depth) = 1; + GTEST_FLAG(stream_result_to) = "localhost:1234"; + GTEST_FLAG(throw_on_failure) = true; + } + + private: + // For saving Google Test flags during this test case. + static GTestFlagSaver* saver_; +}; + +GTestFlagSaver* GTestFlagSaverTest::saver_ = NULL; + +// Google Test doesn't guarantee the order of tests. The following two +// tests are designed to work regardless of their order. + +// Modifies the Google Test flags in the test body. +TEST_F(GTestFlagSaverTest, ModifyGTestFlags) { + VerifyAndModifyFlags(); +} + +// Verifies that the Google Test flags in the body of the previous test were +// restored to their original values. +TEST_F(GTestFlagSaverTest, VerifyGTestFlags) { + VerifyAndModifyFlags(); +} + +// Sets an environment variable with the given name to the given +// value. If the value argument is "", unsets the environment +// variable. The caller must ensure that both arguments are not NULL. +static void SetEnv(const char* name, const char* value) { +#if GTEST_OS_WINDOWS_MOBILE + // Environment variables are not supported on Windows CE. + return; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // C++Builder's putenv only stores a pointer to its parameter; we have to + // ensure that the string remains valid as long as it might be needed. + // We use an std::map to do so. + static std::map added_env; + + // Because putenv stores a pointer to the string buffer, we can't delete the + // previous string (if present) until after it's replaced. + std::string *prev_env = NULL; + if (added_env.find(name) != added_env.end()) { + prev_env = added_env[name]; + } + added_env[name] = new std::string( + (Message() << name << "=" << value).GetString()); + + // The standard signature of putenv accepts a 'char*' argument. Other + // implementations, like C++Builder's, accept a 'const char*'. + // We cast away the 'const' since that would work for both variants. + putenv(const_cast(added_env[name]->c_str())); + delete prev_env; +#elif GTEST_OS_WINDOWS // If we are on Windows proper. + _putenv((Message() << name << "=" << value).GetString().c_str()); +#else + if (*value == '\0') { + unsetenv(name); + } else { + setenv(name, value, 1); + } +#endif // GTEST_OS_WINDOWS_MOBILE +} + +#if !GTEST_OS_WINDOWS_MOBILE +// Environment variables are not supported on Windows CE. + +using testing::internal::Int32FromGTestEnv; + +// Tests Int32FromGTestEnv(). + +// Tests that Int32FromGTestEnv() returns the default value when the +// environment variable is not set. +TEST(Int32FromGTestEnvTest, ReturnsDefaultWhenVariableIsNotSet) { + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", ""); + EXPECT_EQ(10, Int32FromGTestEnv("temp", 10)); +} + +// Tests that Int32FromGTestEnv() returns the default value when the +// environment variable overflows as an Int32. +TEST(Int32FromGTestEnvTest, ReturnsDefaultWhenValueOverflows) { + printf("(expecting 2 warnings)\n"); + + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", "12345678987654321"); + EXPECT_EQ(20, Int32FromGTestEnv("temp", 20)); + + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", "-12345678987654321"); + EXPECT_EQ(30, Int32FromGTestEnv("temp", 30)); +} + +// Tests that Int32FromGTestEnv() returns the default value when the +// environment variable does not represent a valid decimal integer. +TEST(Int32FromGTestEnvTest, ReturnsDefaultWhenValueIsInvalid) { + printf("(expecting 2 warnings)\n"); + + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", "A1"); + EXPECT_EQ(40, Int32FromGTestEnv("temp", 40)); + + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", "12X"); + EXPECT_EQ(50, Int32FromGTestEnv("temp", 50)); +} + +// Tests that Int32FromGTestEnv() parses and returns the value of the +// environment variable when it represents a valid decimal integer in +// the range of an Int32. +TEST(Int32FromGTestEnvTest, ParsesAndReturnsValidValue) { + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", "123"); + EXPECT_EQ(123, Int32FromGTestEnv("temp", 0)); + + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "TEMP", "-321"); + EXPECT_EQ(-321, Int32FromGTestEnv("temp", 0)); +} +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Tests ParseInt32Flag(). + +// Tests that ParseInt32Flag() returns false and doesn't change the +// output value when the flag has wrong format +TEST(ParseInt32FlagTest, ReturnsFalseForInvalidFlag) { + Int32 value = 123; + EXPECT_FALSE(ParseInt32Flag("--a=100", "b", &value)); + EXPECT_EQ(123, value); + + EXPECT_FALSE(ParseInt32Flag("a=100", "a", &value)); + EXPECT_EQ(123, value); +} + +// Tests that ParseInt32Flag() returns false and doesn't change the +// output value when the flag overflows as an Int32. +TEST(ParseInt32FlagTest, ReturnsDefaultWhenValueOverflows) { + printf("(expecting 2 warnings)\n"); + + Int32 value = 123; + EXPECT_FALSE(ParseInt32Flag("--abc=12345678987654321", "abc", &value)); + EXPECT_EQ(123, value); + + EXPECT_FALSE(ParseInt32Flag("--abc=-12345678987654321", "abc", &value)); + EXPECT_EQ(123, value); +} + +// Tests that ParseInt32Flag() returns false and doesn't change the +// output value when the flag does not represent a valid decimal +// integer. +TEST(ParseInt32FlagTest, ReturnsDefaultWhenValueIsInvalid) { + printf("(expecting 2 warnings)\n"); + + Int32 value = 123; + EXPECT_FALSE(ParseInt32Flag("--abc=A1", "abc", &value)); + EXPECT_EQ(123, value); + + EXPECT_FALSE(ParseInt32Flag("--abc=12X", "abc", &value)); + EXPECT_EQ(123, value); +} + +// Tests that ParseInt32Flag() parses the value of the flag and +// returns true when the flag represents a valid decimal integer in +// the range of an Int32. +TEST(ParseInt32FlagTest, ParsesAndReturnsValidValue) { + Int32 value = 123; + EXPECT_TRUE(ParseInt32Flag("--" GTEST_FLAG_PREFIX_ "abc=456", "abc", &value)); + EXPECT_EQ(456, value); + + EXPECT_TRUE(ParseInt32Flag("--" GTEST_FLAG_PREFIX_ "abc=-789", + "abc", &value)); + EXPECT_EQ(-789, value); +} + +// Tests that Int32FromEnvOrDie() parses the value of the var or +// returns the correct default. +// Environment variables are not supported on Windows CE. +#if !GTEST_OS_WINDOWS_MOBILE +TEST(Int32FromEnvOrDieTest, ParsesAndReturnsValidValue) { + EXPECT_EQ(333, Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER_ "UnsetVar", 333)); + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "UnsetVar", "123"); + EXPECT_EQ(123, Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER_ "UnsetVar", 333)); + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "UnsetVar", "-123"); + EXPECT_EQ(-123, Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER_ "UnsetVar", 333)); +} +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Tests that Int32FromEnvOrDie() aborts with an error message +// if the variable is not an Int32. +TEST(Int32FromEnvOrDieDeathTest, AbortsOnFailure) { + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "VAR", "xxx"); + EXPECT_DEATH_IF_SUPPORTED( + Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER_ "VAR", 123), + ".*"); +} + +// Tests that Int32FromEnvOrDie() aborts with an error message +// if the variable cannot be represnted by an Int32. +TEST(Int32FromEnvOrDieDeathTest, AbortsOnInt32Overflow) { + SetEnv(GTEST_FLAG_PREFIX_UPPER_ "VAR", "1234567891234567891234"); + EXPECT_DEATH_IF_SUPPORTED( + Int32FromEnvOrDie(GTEST_FLAG_PREFIX_UPPER_ "VAR", 123), + ".*"); +} + +// Tests that ShouldRunTestOnShard() selects all tests +// where there is 1 shard. +TEST(ShouldRunTestOnShardTest, IsPartitionWhenThereIsOneShard) { + EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 0)); + EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 1)); + EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 2)); + EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 3)); + EXPECT_TRUE(ShouldRunTestOnShard(1, 0, 4)); +} + +class ShouldShardTest : public testing::Test { + protected: + virtual void SetUp() { + index_var_ = GTEST_FLAG_PREFIX_UPPER_ "INDEX"; + total_var_ = GTEST_FLAG_PREFIX_UPPER_ "TOTAL"; + } + + virtual void TearDown() { + SetEnv(index_var_, ""); + SetEnv(total_var_, ""); + } + + const char* index_var_; + const char* total_var_; +}; + +// Tests that sharding is disabled if neither of the environment variables +// are set. +TEST_F(ShouldShardTest, ReturnsFalseWhenNeitherEnvVarIsSet) { + SetEnv(index_var_, ""); + SetEnv(total_var_, ""); + + EXPECT_FALSE(ShouldShard(total_var_, index_var_, false)); + EXPECT_FALSE(ShouldShard(total_var_, index_var_, true)); +} + +// Tests that sharding is not enabled if total_shards == 1. +TEST_F(ShouldShardTest, ReturnsFalseWhenTotalShardIsOne) { + SetEnv(index_var_, "0"); + SetEnv(total_var_, "1"); + EXPECT_FALSE(ShouldShard(total_var_, index_var_, false)); + EXPECT_FALSE(ShouldShard(total_var_, index_var_, true)); +} + +// Tests that sharding is enabled if total_shards > 1 and +// we are not in a death test subprocess. +// Environment variables are not supported on Windows CE. +#if !GTEST_OS_WINDOWS_MOBILE +TEST_F(ShouldShardTest, WorksWhenShardEnvVarsAreValid) { + SetEnv(index_var_, "4"); + SetEnv(total_var_, "22"); + EXPECT_TRUE(ShouldShard(total_var_, index_var_, false)); + EXPECT_FALSE(ShouldShard(total_var_, index_var_, true)); + + SetEnv(index_var_, "8"); + SetEnv(total_var_, "9"); + EXPECT_TRUE(ShouldShard(total_var_, index_var_, false)); + EXPECT_FALSE(ShouldShard(total_var_, index_var_, true)); + + SetEnv(index_var_, "0"); + SetEnv(total_var_, "9"); + EXPECT_TRUE(ShouldShard(total_var_, index_var_, false)); + EXPECT_FALSE(ShouldShard(total_var_, index_var_, true)); +} +#endif // !GTEST_OS_WINDOWS_MOBILE + +// Tests that we exit in error if the sharding values are not valid. + +typedef ShouldShardTest ShouldShardDeathTest; + +TEST_F(ShouldShardDeathTest, AbortsWhenShardingEnvVarsAreInvalid) { + SetEnv(index_var_, "4"); + SetEnv(total_var_, "4"); + EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*"); + + SetEnv(index_var_, "4"); + SetEnv(total_var_, "-2"); + EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*"); + + SetEnv(index_var_, "5"); + SetEnv(total_var_, ""); + EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*"); + + SetEnv(index_var_, ""); + SetEnv(total_var_, "5"); + EXPECT_DEATH_IF_SUPPORTED(ShouldShard(total_var_, index_var_, false), ".*"); +} + +// Tests that ShouldRunTestOnShard is a partition when 5 +// shards are used. +TEST(ShouldRunTestOnShardTest, IsPartitionWhenThereAreFiveShards) { + // Choose an arbitrary number of tests and shards. + const int num_tests = 17; + const int num_shards = 5; + + // Check partitioning: each test should be on exactly 1 shard. + for (int test_id = 0; test_id < num_tests; test_id++) { + int prev_selected_shard_index = -1; + for (int shard_index = 0; shard_index < num_shards; shard_index++) { + if (ShouldRunTestOnShard(num_shards, shard_index, test_id)) { + if (prev_selected_shard_index < 0) { + prev_selected_shard_index = shard_index; + } else { + ADD_FAILURE() << "Shard " << prev_selected_shard_index << " and " + << shard_index << " are both selected to run test " << test_id; + } + } + } + } + + // Check balance: This is not required by the sharding protocol, but is a + // desirable property for performance. + for (int shard_index = 0; shard_index < num_shards; shard_index++) { + int num_tests_on_shard = 0; + for (int test_id = 0; test_id < num_tests; test_id++) { + num_tests_on_shard += + ShouldRunTestOnShard(num_shards, shard_index, test_id); + } + EXPECT_GE(num_tests_on_shard, num_tests / num_shards); + } +} + +// For the same reason we are not explicitly testing everything in the +// Test class, there are no separate tests for the following classes +// (except for some trivial cases): +// +// TestCase, UnitTest, UnitTestResultPrinter. +// +// Similarly, there are no separate tests for the following macros: +// +// TEST, TEST_F, RUN_ALL_TESTS + +TEST(UnitTestTest, CanGetOriginalWorkingDir) { + ASSERT_TRUE(UnitTest::GetInstance()->original_working_dir() != NULL); + EXPECT_STRNE(UnitTest::GetInstance()->original_working_dir(), ""); +} + +TEST(UnitTestTest, ReturnsPlausibleTimestamp) { + EXPECT_LT(0, UnitTest::GetInstance()->start_timestamp()); + EXPECT_LE(UnitTest::GetInstance()->start_timestamp(), GetTimeInMillis()); +} + +// When a property using a reserved key is supplied to this function, it +// tests that a non-fatal failure is added, a fatal failure is not added, +// and that the property is not recorded. +void ExpectNonFatalFailureRecordingPropertyWithReservedKey( + const TestResult& test_result, const char* key) { + EXPECT_NONFATAL_FAILURE(Test::RecordProperty(key, "1"), "Reserved key"); + ASSERT_EQ(0, test_result.test_property_count()) << "Property for key '" << key + << "' recorded unexpectedly."; +} + +void ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + const char* key) { + const TestInfo* test_info = UnitTest::GetInstance()->current_test_info(); + ASSERT_TRUE(test_info != NULL); + ExpectNonFatalFailureRecordingPropertyWithReservedKey(*test_info->result(), + key); +} + +void ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + const char* key) { + const TestCase* test_case = UnitTest::GetInstance()->current_test_case(); + ASSERT_TRUE(test_case != NULL); + ExpectNonFatalFailureRecordingPropertyWithReservedKey( + test_case->ad_hoc_test_result(), key); +} + +void ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + const char* key) { + ExpectNonFatalFailureRecordingPropertyWithReservedKey( + UnitTest::GetInstance()->ad_hoc_test_result(), key); +} + +// Tests that property recording functions in UnitTest outside of tests +// functions correcly. Creating a separate instance of UnitTest ensures it +// is in a state similar to the UnitTest's singleton's between tests. +class UnitTestRecordPropertyTest : + public testing::internal::UnitTestRecordPropertyTestHelper { + public: + static void SetUpTestCase() { + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + "disabled"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + "errors"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + "failures"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + "name"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + "tests"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTestCase( + "time"); + + Test::RecordProperty("test_case_key_1", "1"); + const TestCase* test_case = UnitTest::GetInstance()->current_test_case(); + ASSERT_TRUE(test_case != NULL); + + ASSERT_EQ(1, test_case->ad_hoc_test_result().test_property_count()); + EXPECT_STREQ("test_case_key_1", + test_case->ad_hoc_test_result().GetTestProperty(0).key()); + EXPECT_STREQ("1", + test_case->ad_hoc_test_result().GetTestProperty(0).value()); + } +}; + +// Tests TestResult has the expected property when added. +TEST_F(UnitTestRecordPropertyTest, OnePropertyFoundWhenAdded) { + UnitTestRecordProperty("key_1", "1"); + + ASSERT_EQ(1, unit_test_.ad_hoc_test_result().test_property_count()); + + EXPECT_STREQ("key_1", + unit_test_.ad_hoc_test_result().GetTestProperty(0).key()); + EXPECT_STREQ("1", + unit_test_.ad_hoc_test_result().GetTestProperty(0).value()); +} + +// Tests TestResult has multiple properties when added. +TEST_F(UnitTestRecordPropertyTest, MultiplePropertiesFoundWhenAdded) { + UnitTestRecordProperty("key_1", "1"); + UnitTestRecordProperty("key_2", "2"); + + ASSERT_EQ(2, unit_test_.ad_hoc_test_result().test_property_count()); + + EXPECT_STREQ("key_1", + unit_test_.ad_hoc_test_result().GetTestProperty(0).key()); + EXPECT_STREQ("1", unit_test_.ad_hoc_test_result().GetTestProperty(0).value()); + + EXPECT_STREQ("key_2", + unit_test_.ad_hoc_test_result().GetTestProperty(1).key()); + EXPECT_STREQ("2", unit_test_.ad_hoc_test_result().GetTestProperty(1).value()); +} + +// Tests TestResult::RecordProperty() overrides values for duplicate keys. +TEST_F(UnitTestRecordPropertyTest, OverridesValuesForDuplicateKeys) { + UnitTestRecordProperty("key_1", "1"); + UnitTestRecordProperty("key_2", "2"); + UnitTestRecordProperty("key_1", "12"); + UnitTestRecordProperty("key_2", "22"); + + ASSERT_EQ(2, unit_test_.ad_hoc_test_result().test_property_count()); + + EXPECT_STREQ("key_1", + unit_test_.ad_hoc_test_result().GetTestProperty(0).key()); + EXPECT_STREQ("12", + unit_test_.ad_hoc_test_result().GetTestProperty(0).value()); + + EXPECT_STREQ("key_2", + unit_test_.ad_hoc_test_result().GetTestProperty(1).key()); + EXPECT_STREQ("22", + unit_test_.ad_hoc_test_result().GetTestProperty(1).value()); +} + +TEST_F(UnitTestRecordPropertyTest, + AddFailureInsideTestsWhenUsingTestCaseReservedKeys) { + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + "name"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + "value_param"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + "type_param"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + "status"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + "time"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyForCurrentTest( + "classname"); +} + +TEST_F(UnitTestRecordPropertyTest, + AddRecordWithReservedKeysGeneratesCorrectPropertyList) { + EXPECT_NONFATAL_FAILURE( + Test::RecordProperty("name", "1"), + "'classname', 'name', 'status', 'time', 'type_param', and 'value_param'" + " are reserved"); +} + +class UnitTestRecordPropertyTestEnvironment : public Environment { + public: + virtual void TearDown() { + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "tests"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "failures"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "disabled"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "errors"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "name"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "timestamp"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "time"); + ExpectNonFatalFailureRecordingPropertyWithReservedKeyOutsideOfTestCase( + "random_seed"); + } +}; + +// This will test property recording outside of any test or test case. +static Environment* record_property_env = + AddGlobalTestEnvironment(new UnitTestRecordPropertyTestEnvironment); + +// This group of tests is for predicate assertions (ASSERT_PRED*, etc) +// of various arities. They do not attempt to be exhaustive. Rather, +// view them as smoke tests that can be easily reviewed and verified. +// A more complete set of tests for predicate assertions can be found +// in gtest_pred_impl_unittest.cc. + +// First, some predicates and predicate-formatters needed by the tests. + +// Returns true iff the argument is an even number. +bool IsEven(int n) { + return (n % 2) == 0; +} + +// A functor that returns true iff the argument is an even number. +struct IsEvenFunctor { + bool operator()(int n) { return IsEven(n); } +}; + +// A predicate-formatter function that asserts the argument is an even +// number. +AssertionResult AssertIsEven(const char* expr, int n) { + if (IsEven(n)) { + return AssertionSuccess(); + } + + Message msg; + msg << expr << " evaluates to " << n << ", which is not even."; + return AssertionFailure(msg); +} + +// A predicate function that returns AssertionResult for use in +// EXPECT/ASSERT_TRUE/FALSE. +AssertionResult ResultIsEven(int n) { + if (IsEven(n)) + return AssertionSuccess() << n << " is even"; + else + return AssertionFailure() << n << " is odd"; +} + +// A predicate function that returns AssertionResult but gives no +// explanation why it succeeds. Needed for testing that +// EXPECT/ASSERT_FALSE handles such functions correctly. +AssertionResult ResultIsEvenNoExplanation(int n) { + if (IsEven(n)) + return AssertionSuccess(); + else + return AssertionFailure() << n << " is odd"; +} + +// A predicate-formatter functor that asserts the argument is an even +// number. +struct AssertIsEvenFunctor { + AssertionResult operator()(const char* expr, int n) { + return AssertIsEven(expr, n); + } +}; + +// Returns true iff the sum of the arguments is an even number. +bool SumIsEven2(int n1, int n2) { + return IsEven(n1 + n2); +} + +// A functor that returns true iff the sum of the arguments is an even +// number. +struct SumIsEven3Functor { + bool operator()(int n1, int n2, int n3) { + return IsEven(n1 + n2 + n3); + } +}; + +// A predicate-formatter function that asserts the sum of the +// arguments is an even number. +AssertionResult AssertSumIsEven4( + const char* e1, const char* e2, const char* e3, const char* e4, + int n1, int n2, int n3, int n4) { + const int sum = n1 + n2 + n3 + n4; + if (IsEven(sum)) { + return AssertionSuccess(); + } + + Message msg; + msg << e1 << " + " << e2 << " + " << e3 << " + " << e4 + << " (" << n1 << " + " << n2 << " + " << n3 << " + " << n4 + << ") evaluates to " << sum << ", which is not even."; + return AssertionFailure(msg); +} + +// A predicate-formatter functor that asserts the sum of the arguments +// is an even number. +struct AssertSumIsEven5Functor { + AssertionResult operator()( + const char* e1, const char* e2, const char* e3, const char* e4, + const char* e5, int n1, int n2, int n3, int n4, int n5) { + const int sum = n1 + n2 + n3 + n4 + n5; + if (IsEven(sum)) { + return AssertionSuccess(); + } + + Message msg; + msg << e1 << " + " << e2 << " + " << e3 << " + " << e4 << " + " << e5 + << " (" + << n1 << " + " << n2 << " + " << n3 << " + " << n4 << " + " << n5 + << ") evaluates to " << sum << ", which is not even."; + return AssertionFailure(msg); + } +}; + + +// Tests unary predicate assertions. + +// Tests unary predicate assertions that don't use a custom formatter. +TEST(Pred1Test, WithoutFormat) { + // Success cases. + EXPECT_PRED1(IsEvenFunctor(), 2) << "This failure is UNEXPECTED!"; + ASSERT_PRED1(IsEven, 4); + + // Failure cases. + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED1(IsEven, 5) << "This failure is expected."; + }, "This failure is expected."); + EXPECT_FATAL_FAILURE(ASSERT_PRED1(IsEvenFunctor(), 5), + "evaluates to false"); +} + +// Tests unary predicate assertions that use a custom formatter. +TEST(Pred1Test, WithFormat) { + // Success cases. + EXPECT_PRED_FORMAT1(AssertIsEven, 2); + ASSERT_PRED_FORMAT1(AssertIsEvenFunctor(), 4) + << "This failure is UNEXPECTED!"; + + // Failure cases. + const int n = 5; + EXPECT_NONFATAL_FAILURE(EXPECT_PRED_FORMAT1(AssertIsEvenFunctor(), n), + "n evaluates to 5, which is not even."); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT1(AssertIsEven, 5) << "This failure is expected."; + }, "This failure is expected."); +} + +// Tests that unary predicate assertions evaluates their arguments +// exactly once. +TEST(Pred1Test, SingleEvaluationOnFailure) { + // A success case. + static int n = 0; + EXPECT_PRED1(IsEven, n++); + EXPECT_EQ(1, n) << "The argument is not evaluated exactly once."; + + // A failure case. + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT1(AssertIsEvenFunctor(), n++) + << "This failure is expected."; + }, "This failure is expected."); + EXPECT_EQ(2, n) << "The argument is not evaluated exactly once."; +} + + +// Tests predicate assertions whose arity is >= 2. + +// Tests predicate assertions that don't use a custom formatter. +TEST(PredTest, WithoutFormat) { + // Success cases. + ASSERT_PRED2(SumIsEven2, 2, 4) << "This failure is UNEXPECTED!"; + EXPECT_PRED3(SumIsEven3Functor(), 4, 6, 8); + + // Failure cases. + const int n1 = 1; + const int n2 = 2; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED2(SumIsEven2, n1, n2) << "This failure is expected."; + }, "This failure is expected."); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED3(SumIsEven3Functor(), 1, 2, 4); + }, "evaluates to false"); +} + +// Tests predicate assertions that use a custom formatter. +TEST(PredTest, WithFormat) { + // Success cases. + ASSERT_PRED_FORMAT4(AssertSumIsEven4, 4, 6, 8, 10) << + "This failure is UNEXPECTED!"; + EXPECT_PRED_FORMAT5(AssertSumIsEven5Functor(), 2, 4, 6, 8, 10); + + // Failure cases. + const int n1 = 1; + const int n2 = 2; + const int n3 = 4; + const int n4 = 6; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT4(AssertSumIsEven4, n1, n2, n3, n4); + }, "evaluates to 13, which is not even."); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT5(AssertSumIsEven5Functor(), 1, 2, 4, 6, 8) + << "This failure is expected."; + }, "This failure is expected."); +} + +// Tests that predicate assertions evaluates their arguments +// exactly once. +TEST(PredTest, SingleEvaluationOnFailure) { + // A success case. + int n1 = 0; + int n2 = 0; + EXPECT_PRED2(SumIsEven2, n1++, n2++); + EXPECT_EQ(1, n1) << "Argument 1 is not evaluated exactly once."; + EXPECT_EQ(1, n2) << "Argument 2 is not evaluated exactly once."; + + // Another success case. + n1 = n2 = 0; + int n3 = 0; + int n4 = 0; + int n5 = 0; + ASSERT_PRED_FORMAT5(AssertSumIsEven5Functor(), + n1++, n2++, n3++, n4++, n5++) + << "This failure is UNEXPECTED!"; + EXPECT_EQ(1, n1) << "Argument 1 is not evaluated exactly once."; + EXPECT_EQ(1, n2) << "Argument 2 is not evaluated exactly once."; + EXPECT_EQ(1, n3) << "Argument 3 is not evaluated exactly once."; + EXPECT_EQ(1, n4) << "Argument 4 is not evaluated exactly once."; + EXPECT_EQ(1, n5) << "Argument 5 is not evaluated exactly once."; + + // A failure case. + n1 = n2 = n3 = 0; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED3(SumIsEven3Functor(), ++n1, n2++, n3++) + << "This failure is expected."; + }, "This failure is expected."); + EXPECT_EQ(1, n1) << "Argument 1 is not evaluated exactly once."; + EXPECT_EQ(1, n2) << "Argument 2 is not evaluated exactly once."; + EXPECT_EQ(1, n3) << "Argument 3 is not evaluated exactly once."; + + // Another failure case. + n1 = n2 = n3 = n4 = 0; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT4(AssertSumIsEven4, ++n1, n2++, n3++, n4++); + }, "evaluates to 1, which is not even."); + EXPECT_EQ(1, n1) << "Argument 1 is not evaluated exactly once."; + EXPECT_EQ(1, n2) << "Argument 2 is not evaluated exactly once."; + EXPECT_EQ(1, n3) << "Argument 3 is not evaluated exactly once."; + EXPECT_EQ(1, n4) << "Argument 4 is not evaluated exactly once."; +} + + +// Some helper functions for testing using overloaded/template +// functions with ASSERT_PREDn and EXPECT_PREDn. + +bool IsPositive(double x) { + return x > 0; +} + +template +bool IsNegative(T x) { + return x < 0; +} + +template +bool GreaterThan(T1 x1, T2 x2) { + return x1 > x2; +} + +// Tests that overloaded functions can be used in *_PRED* as long as +// their types are explicitly specified. +TEST(PredicateAssertionTest, AcceptsOverloadedFunction) { + // C++Builder requires C-style casts rather than static_cast. + EXPECT_PRED1((bool (*)(int))(IsPositive), 5); // NOLINT + ASSERT_PRED1((bool (*)(double))(IsPositive), 6.0); // NOLINT +} + +// Tests that template functions can be used in *_PRED* as long as +// their types are explicitly specified. +TEST(PredicateAssertionTest, AcceptsTemplateFunction) { + EXPECT_PRED1(IsNegative, -5); + // Makes sure that we can handle templates with more than one + // parameter. + ASSERT_PRED2((GreaterThan), 5, 0); +} + + +// Some helper functions for testing using overloaded/template +// functions with ASSERT_PRED_FORMATn and EXPECT_PRED_FORMATn. + +AssertionResult IsPositiveFormat(const char* /* expr */, int n) { + return n > 0 ? AssertionSuccess() : + AssertionFailure(Message() << "Failure"); +} + +AssertionResult IsPositiveFormat(const char* /* expr */, double x) { + return x > 0 ? AssertionSuccess() : + AssertionFailure(Message() << "Failure"); +} + +template +AssertionResult IsNegativeFormat(const char* /* expr */, T x) { + return x < 0 ? AssertionSuccess() : + AssertionFailure(Message() << "Failure"); +} + +template +AssertionResult EqualsFormat(const char* /* expr1 */, const char* /* expr2 */, + const T1& x1, const T2& x2) { + return x1 == x2 ? AssertionSuccess() : + AssertionFailure(Message() << "Failure"); +} + +// Tests that overloaded functions can be used in *_PRED_FORMAT* +// without explicitly specifying their types. +TEST(PredicateFormatAssertionTest, AcceptsOverloadedFunction) { + EXPECT_PRED_FORMAT1(IsPositiveFormat, 5); + ASSERT_PRED_FORMAT1(IsPositiveFormat, 6.0); +} + +// Tests that template functions can be used in *_PRED_FORMAT* without +// explicitly specifying their types. +TEST(PredicateFormatAssertionTest, AcceptsTemplateFunction) { + EXPECT_PRED_FORMAT1(IsNegativeFormat, -5); + ASSERT_PRED_FORMAT2(EqualsFormat, 3, 3); +} + + +// Tests string assertions. + +// Tests ASSERT_STREQ with non-NULL arguments. +TEST(StringAssertionTest, ASSERT_STREQ) { + const char * const p1 = "good"; + ASSERT_STREQ(p1, p1); + + // Let p2 have the same content as p1, but be at a different address. + const char p2[] = "good"; + ASSERT_STREQ(p1, p2); + + EXPECT_FATAL_FAILURE(ASSERT_STREQ("bad", "good"), + "Expected: \"bad\""); +} + +// Tests ASSERT_STREQ with NULL arguments. +TEST(StringAssertionTest, ASSERT_STREQ_Null) { + ASSERT_STREQ(static_cast(NULL), NULL); + EXPECT_FATAL_FAILURE(ASSERT_STREQ(NULL, "non-null"), + "non-null"); +} + +// Tests ASSERT_STREQ with NULL arguments. +TEST(StringAssertionTest, ASSERT_STREQ_Null2) { + EXPECT_FATAL_FAILURE(ASSERT_STREQ("non-null", NULL), + "non-null"); +} + +// Tests ASSERT_STRNE. +TEST(StringAssertionTest, ASSERT_STRNE) { + ASSERT_STRNE("hi", "Hi"); + ASSERT_STRNE("Hi", NULL); + ASSERT_STRNE(NULL, "Hi"); + ASSERT_STRNE("", NULL); + ASSERT_STRNE(NULL, ""); + ASSERT_STRNE("", "Hi"); + ASSERT_STRNE("Hi", ""); + EXPECT_FATAL_FAILURE(ASSERT_STRNE("Hi", "Hi"), + "\"Hi\" vs \"Hi\""); +} + +// Tests ASSERT_STRCASEEQ. +TEST(StringAssertionTest, ASSERT_STRCASEEQ) { + ASSERT_STRCASEEQ("hi", "Hi"); + ASSERT_STRCASEEQ(static_cast(NULL), NULL); + + ASSERT_STRCASEEQ("", ""); + EXPECT_FATAL_FAILURE(ASSERT_STRCASEEQ("Hi", "hi2"), + "(ignoring case)"); +} + +// Tests ASSERT_STRCASENE. +TEST(StringAssertionTest, ASSERT_STRCASENE) { + ASSERT_STRCASENE("hi1", "Hi2"); + ASSERT_STRCASENE("Hi", NULL); + ASSERT_STRCASENE(NULL, "Hi"); + ASSERT_STRCASENE("", NULL); + ASSERT_STRCASENE(NULL, ""); + ASSERT_STRCASENE("", "Hi"); + ASSERT_STRCASENE("Hi", ""); + EXPECT_FATAL_FAILURE(ASSERT_STRCASENE("Hi", "hi"), + "(ignoring case)"); +} + +// Tests *_STREQ on wide strings. +TEST(StringAssertionTest, STREQ_Wide) { + // NULL strings. + ASSERT_STREQ(static_cast(NULL), NULL); + + // Empty strings. + ASSERT_STREQ(L"", L""); + + // Non-null vs NULL. + EXPECT_NONFATAL_FAILURE(EXPECT_STREQ(L"non-null", NULL), + "non-null"); + + // Equal strings. + EXPECT_STREQ(L"Hi", L"Hi"); + + // Unequal strings. + EXPECT_NONFATAL_FAILURE(EXPECT_STREQ(L"abc", L"Abc"), + "Abc"); + + // Strings containing wide characters. + EXPECT_NONFATAL_FAILURE(EXPECT_STREQ(L"abc\x8119", L"abc\x8120"), + "abc"); + + // The streaming variation. + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_STREQ(L"abc\x8119", L"abc\x8121") << "Expected failure"; + }, "Expected failure"); +} + +// Tests *_STRNE on wide strings. +TEST(StringAssertionTest, STRNE_Wide) { + // NULL strings. + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_STRNE(static_cast(NULL), NULL); + }, ""); + + // Empty strings. + EXPECT_NONFATAL_FAILURE(EXPECT_STRNE(L"", L""), + "L\"\""); + + // Non-null vs NULL. + ASSERT_STRNE(L"non-null", NULL); + + // Equal strings. + EXPECT_NONFATAL_FAILURE(EXPECT_STRNE(L"Hi", L"Hi"), + "L\"Hi\""); + + // Unequal strings. + EXPECT_STRNE(L"abc", L"Abc"); + + // Strings containing wide characters. + EXPECT_NONFATAL_FAILURE(EXPECT_STRNE(L"abc\x8119", L"abc\x8119"), + "abc"); + + // The streaming variation. + ASSERT_STRNE(L"abc\x8119", L"abc\x8120") << "This shouldn't happen"; +} + +// Tests for ::testing::IsSubstring(). + +// Tests that IsSubstring() returns the correct result when the input +// argument type is const char*. +TEST(IsSubstringTest, ReturnsCorrectResultForCString) { + EXPECT_FALSE(IsSubstring("", "", NULL, "a")); + EXPECT_FALSE(IsSubstring("", "", "b", NULL)); + EXPECT_FALSE(IsSubstring("", "", "needle", "haystack")); + + EXPECT_TRUE(IsSubstring("", "", static_cast(NULL), NULL)); + EXPECT_TRUE(IsSubstring("", "", "needle", "two needles")); +} + +// Tests that IsSubstring() returns the correct result when the input +// argument type is const wchar_t*. +TEST(IsSubstringTest, ReturnsCorrectResultForWideCString) { + EXPECT_FALSE(IsSubstring("", "", kNull, L"a")); + EXPECT_FALSE(IsSubstring("", "", L"b", kNull)); + EXPECT_FALSE(IsSubstring("", "", L"needle", L"haystack")); + + EXPECT_TRUE(IsSubstring("", "", static_cast(NULL), NULL)); + EXPECT_TRUE(IsSubstring("", "", L"needle", L"two needles")); +} + +// Tests that IsSubstring() generates the correct message when the input +// argument type is const char*. +TEST(IsSubstringTest, GeneratesCorrectMessageForCString) { + EXPECT_STREQ("Value of: needle_expr\n" + " Actual: \"needle\"\n" + "Expected: a substring of haystack_expr\n" + "Which is: \"haystack\"", + IsSubstring("needle_expr", "haystack_expr", + "needle", "haystack").failure_message()); +} + +// Tests that IsSubstring returns the correct result when the input +// argument type is ::std::string. +TEST(IsSubstringTest, ReturnsCorrectResultsForStdString) { + EXPECT_TRUE(IsSubstring("", "", std::string("hello"), "ahellob")); + EXPECT_FALSE(IsSubstring("", "", "hello", std::string("world"))); +} + +#if GTEST_HAS_STD_WSTRING +// Tests that IsSubstring returns the correct result when the input +// argument type is ::std::wstring. +TEST(IsSubstringTest, ReturnsCorrectResultForStdWstring) { + EXPECT_TRUE(IsSubstring("", "", ::std::wstring(L"needle"), L"two needles")); + EXPECT_FALSE(IsSubstring("", "", L"needle", ::std::wstring(L"haystack"))); +} + +// Tests that IsSubstring() generates the correct message when the input +// argument type is ::std::wstring. +TEST(IsSubstringTest, GeneratesCorrectMessageForWstring) { + EXPECT_STREQ("Value of: needle_expr\n" + " Actual: L\"needle\"\n" + "Expected: a substring of haystack_expr\n" + "Which is: L\"haystack\"", + IsSubstring( + "needle_expr", "haystack_expr", + ::std::wstring(L"needle"), L"haystack").failure_message()); +} + +#endif // GTEST_HAS_STD_WSTRING + +// Tests for ::testing::IsNotSubstring(). + +// Tests that IsNotSubstring() returns the correct result when the input +// argument type is const char*. +TEST(IsNotSubstringTest, ReturnsCorrectResultForCString) { + EXPECT_TRUE(IsNotSubstring("", "", "needle", "haystack")); + EXPECT_FALSE(IsNotSubstring("", "", "needle", "two needles")); +} + +// Tests that IsNotSubstring() returns the correct result when the input +// argument type is const wchar_t*. +TEST(IsNotSubstringTest, ReturnsCorrectResultForWideCString) { + EXPECT_TRUE(IsNotSubstring("", "", L"needle", L"haystack")); + EXPECT_FALSE(IsNotSubstring("", "", L"needle", L"two needles")); +} + +// Tests that IsNotSubstring() generates the correct message when the input +// argument type is const wchar_t*. +TEST(IsNotSubstringTest, GeneratesCorrectMessageForWideCString) { + EXPECT_STREQ("Value of: needle_expr\n" + " Actual: L\"needle\"\n" + "Expected: not a substring of haystack_expr\n" + "Which is: L\"two needles\"", + IsNotSubstring( + "needle_expr", "haystack_expr", + L"needle", L"two needles").failure_message()); +} + +// Tests that IsNotSubstring returns the correct result when the input +// argument type is ::std::string. +TEST(IsNotSubstringTest, ReturnsCorrectResultsForStdString) { + EXPECT_FALSE(IsNotSubstring("", "", std::string("hello"), "ahellob")); + EXPECT_TRUE(IsNotSubstring("", "", "hello", std::string("world"))); +} + +// Tests that IsNotSubstring() generates the correct message when the input +// argument type is ::std::string. +TEST(IsNotSubstringTest, GeneratesCorrectMessageForStdString) { + EXPECT_STREQ("Value of: needle_expr\n" + " Actual: \"needle\"\n" + "Expected: not a substring of haystack_expr\n" + "Which is: \"two needles\"", + IsNotSubstring( + "needle_expr", "haystack_expr", + ::std::string("needle"), "two needles").failure_message()); +} + +#if GTEST_HAS_STD_WSTRING + +// Tests that IsNotSubstring returns the correct result when the input +// argument type is ::std::wstring. +TEST(IsNotSubstringTest, ReturnsCorrectResultForStdWstring) { + EXPECT_FALSE( + IsNotSubstring("", "", ::std::wstring(L"needle"), L"two needles")); + EXPECT_TRUE(IsNotSubstring("", "", L"needle", ::std::wstring(L"haystack"))); +} + +#endif // GTEST_HAS_STD_WSTRING + +// Tests floating-point assertions. + +template +class FloatingPointTest : public Test { + protected: + // Pre-calculated numbers to be used by the tests. + struct TestValues { + RawType close_to_positive_zero; + RawType close_to_negative_zero; + RawType further_from_negative_zero; + + RawType close_to_one; + RawType further_from_one; + + RawType infinity; + RawType close_to_infinity; + RawType further_from_infinity; + + RawType nan1; + RawType nan2; + }; + + typedef typename testing::internal::FloatingPoint Floating; + typedef typename Floating::Bits Bits; + + virtual void SetUp() { + const size_t max_ulps = Floating::kMaxUlps; + + // The bits that represent 0.0. + const Bits zero_bits = Floating(0).bits(); + + // Makes some numbers close to 0.0. + values_.close_to_positive_zero = Floating::ReinterpretBits( + zero_bits + max_ulps/2); + values_.close_to_negative_zero = -Floating::ReinterpretBits( + zero_bits + max_ulps - max_ulps/2); + values_.further_from_negative_zero = -Floating::ReinterpretBits( + zero_bits + max_ulps + 1 - max_ulps/2); + + // The bits that represent 1.0. + const Bits one_bits = Floating(1).bits(); + + // Makes some numbers close to 1.0. + values_.close_to_one = Floating::ReinterpretBits(one_bits + max_ulps); + values_.further_from_one = Floating::ReinterpretBits( + one_bits + max_ulps + 1); + + // +infinity. + values_.infinity = Floating::Infinity(); + + // The bits that represent +infinity. + const Bits infinity_bits = Floating(values_.infinity).bits(); + + // Makes some numbers close to infinity. + values_.close_to_infinity = Floating::ReinterpretBits( + infinity_bits - max_ulps); + values_.further_from_infinity = Floating::ReinterpretBits( + infinity_bits - max_ulps - 1); + + // Makes some NAN's. Sets the most significant bit of the fraction so that + // our NaN's are quiet; trying to process a signaling NaN would raise an + // exception if our environment enables floating point exceptions. + values_.nan1 = Floating::ReinterpretBits(Floating::kExponentBitMask + | (static_cast(1) << (Floating::kFractionBitCount - 1)) | 1); + values_.nan2 = Floating::ReinterpretBits(Floating::kExponentBitMask + | (static_cast(1) << (Floating::kFractionBitCount - 1)) | 200); + } + + void TestSize() { + EXPECT_EQ(sizeof(RawType), sizeof(Bits)); + } + + static TestValues values_; +}; + +template +typename FloatingPointTest::TestValues + FloatingPointTest::values_; + +// Instantiates FloatingPointTest for testing *_FLOAT_EQ. +typedef FloatingPointTest FloatTest; + +// Tests that the size of Float::Bits matches the size of float. +TEST_F(FloatTest, Size) { + TestSize(); +} + +// Tests comparing with +0 and -0. +TEST_F(FloatTest, Zeros) { + EXPECT_FLOAT_EQ(0.0, -0.0); + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(-0.0, 1.0), + "1.0"); + EXPECT_FATAL_FAILURE(ASSERT_FLOAT_EQ(0.0, 1.5), + "1.5"); +} + +// Tests comparing numbers close to 0. +// +// This ensures that *_FLOAT_EQ handles the sign correctly and no +// overflow occurs when comparing numbers whose absolute value is very +// small. +TEST_F(FloatTest, AlmostZeros) { + // In C++Builder, names within local classes (such as used by + // EXPECT_FATAL_FAILURE) cannot be resolved against static members of the + // scoping class. Use a static local alias as a workaround. + // We use the assignment syntax since some compilers, like Sun Studio, + // don't allow initializing references using construction syntax + // (parentheses). + static const FloatTest::TestValues& v = this->values_; + + EXPECT_FLOAT_EQ(0.0, v.close_to_positive_zero); + EXPECT_FLOAT_EQ(-0.0, v.close_to_negative_zero); + EXPECT_FLOAT_EQ(v.close_to_positive_zero, v.close_to_negative_zero); + + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_FLOAT_EQ(v.close_to_positive_zero, + v.further_from_negative_zero); + }, "v.further_from_negative_zero"); +} + +// Tests comparing numbers close to each other. +TEST_F(FloatTest, SmallDiff) { + EXPECT_FLOAT_EQ(1.0, values_.close_to_one); + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(1.0, values_.further_from_one), + "values_.further_from_one"); +} + +// Tests comparing numbers far apart. +TEST_F(FloatTest, LargeDiff) { + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(2.5, 3.0), + "3.0"); +} + +// Tests comparing with infinity. +// +// This ensures that no overflow occurs when comparing numbers whose +// absolute value is very large. +TEST_F(FloatTest, Infinity) { + EXPECT_FLOAT_EQ(values_.infinity, values_.close_to_infinity); + EXPECT_FLOAT_EQ(-values_.infinity, -values_.close_to_infinity); +#if !GTEST_OS_SYMBIAN + // Nokia's STLport crashes if we try to output infinity or NaN. + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(values_.infinity, -values_.infinity), + "-values_.infinity"); + + // This is interesting as the representations of infinity and nan1 + // are only 1 DLP apart. + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(values_.infinity, values_.nan1), + "values_.nan1"); +#endif // !GTEST_OS_SYMBIAN +} + +// Tests that comparing with NAN always returns false. +TEST_F(FloatTest, NaN) { +#if !GTEST_OS_SYMBIAN +// Nokia's STLport crashes if we try to output infinity or NaN. + + // In C++Builder, names within local classes (such as used by + // EXPECT_FATAL_FAILURE) cannot be resolved against static members of the + // scoping class. Use a static local alias as a workaround. + // We use the assignment syntax since some compilers, like Sun Studio, + // don't allow initializing references using construction syntax + // (parentheses). + static const FloatTest::TestValues& v = this->values_; + + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(v.nan1, v.nan1), + "v.nan1"); + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(v.nan1, v.nan2), + "v.nan2"); + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(1.0, v.nan1), + "v.nan1"); + + EXPECT_FATAL_FAILURE(ASSERT_FLOAT_EQ(v.nan1, v.infinity), + "v.infinity"); +#endif // !GTEST_OS_SYMBIAN +} + +// Tests that *_FLOAT_EQ are reflexive. +TEST_F(FloatTest, Reflexive) { + EXPECT_FLOAT_EQ(0.0, 0.0); + EXPECT_FLOAT_EQ(1.0, 1.0); + ASSERT_FLOAT_EQ(values_.infinity, values_.infinity); +} + +// Tests that *_FLOAT_EQ are commutative. +TEST_F(FloatTest, Commutative) { + // We already tested EXPECT_FLOAT_EQ(1.0, values_.close_to_one). + EXPECT_FLOAT_EQ(values_.close_to_one, 1.0); + + // We already tested EXPECT_FLOAT_EQ(1.0, values_.further_from_one). + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(values_.further_from_one, 1.0), + "1.0"); +} + +// Tests EXPECT_NEAR. +TEST_F(FloatTest, EXPECT_NEAR) { + EXPECT_NEAR(-1.0f, -1.1f, 0.2f); + EXPECT_NEAR(2.0f, 3.0f, 1.0f); + EXPECT_NONFATAL_FAILURE(EXPECT_NEAR(1.0f,1.5f, 0.25f), // NOLINT + "The difference between 1.0f and 1.5f is 0.5, " + "which exceeds 0.25f"); + // To work around a bug in gcc 2.95.0, there is intentionally no + // space after the first comma in the previous line. +} + +// Tests ASSERT_NEAR. +TEST_F(FloatTest, ASSERT_NEAR) { + ASSERT_NEAR(-1.0f, -1.1f, 0.2f); + ASSERT_NEAR(2.0f, 3.0f, 1.0f); + EXPECT_FATAL_FAILURE(ASSERT_NEAR(1.0f,1.5f, 0.25f), // NOLINT + "The difference between 1.0f and 1.5f is 0.5, " + "which exceeds 0.25f"); + // To work around a bug in gcc 2.95.0, there is intentionally no + // space after the first comma in the previous line. +} + +// Tests the cases where FloatLE() should succeed. +TEST_F(FloatTest, FloatLESucceeds) { + EXPECT_PRED_FORMAT2(FloatLE, 1.0f, 2.0f); // When val1 < val2, + ASSERT_PRED_FORMAT2(FloatLE, 1.0f, 1.0f); // val1 == val2, + + // or when val1 is greater than, but almost equals to, val2. + EXPECT_PRED_FORMAT2(FloatLE, values_.close_to_positive_zero, 0.0f); +} + +// Tests the cases where FloatLE() should fail. +TEST_F(FloatTest, FloatLEFails) { + // When val1 is greater than val2 by a large margin, + EXPECT_NONFATAL_FAILURE(EXPECT_PRED_FORMAT2(FloatLE, 2.0f, 1.0f), + "(2.0f) <= (1.0f)"); + + // or by a small yet non-negligible margin, + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(FloatLE, values_.further_from_one, 1.0f); + }, "(values_.further_from_one) <= (1.0f)"); + +#if !GTEST_OS_SYMBIAN && !defined(__BORLANDC__) + // Nokia's STLport crashes if we try to output infinity or NaN. + // C++Builder gives bad results for ordered comparisons involving NaNs + // due to compiler bugs. + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(FloatLE, values_.nan1, values_.infinity); + }, "(values_.nan1) <= (values_.infinity)"); + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(FloatLE, -values_.infinity, values_.nan1); + }, "(-values_.infinity) <= (values_.nan1)"); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT2(FloatLE, values_.nan1, values_.nan1); + }, "(values_.nan1) <= (values_.nan1)"); +#endif // !GTEST_OS_SYMBIAN && !defined(__BORLANDC__) +} + +// Instantiates FloatingPointTest for testing *_DOUBLE_EQ. +typedef FloatingPointTest DoubleTest; + +// Tests that the size of Double::Bits matches the size of double. +TEST_F(DoubleTest, Size) { + TestSize(); +} + +// Tests comparing with +0 and -0. +TEST_F(DoubleTest, Zeros) { + EXPECT_DOUBLE_EQ(0.0, -0.0); + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(-0.0, 1.0), + "1.0"); + EXPECT_FATAL_FAILURE(ASSERT_DOUBLE_EQ(0.0, 1.0), + "1.0"); +} + +// Tests comparing numbers close to 0. +// +// This ensures that *_DOUBLE_EQ handles the sign correctly and no +// overflow occurs when comparing numbers whose absolute value is very +// small. +TEST_F(DoubleTest, AlmostZeros) { + // In C++Builder, names within local classes (such as used by + // EXPECT_FATAL_FAILURE) cannot be resolved against static members of the + // scoping class. Use a static local alias as a workaround. + // We use the assignment syntax since some compilers, like Sun Studio, + // don't allow initializing references using construction syntax + // (parentheses). + static const DoubleTest::TestValues& v = this->values_; + + EXPECT_DOUBLE_EQ(0.0, v.close_to_positive_zero); + EXPECT_DOUBLE_EQ(-0.0, v.close_to_negative_zero); + EXPECT_DOUBLE_EQ(v.close_to_positive_zero, v.close_to_negative_zero); + + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_DOUBLE_EQ(v.close_to_positive_zero, + v.further_from_negative_zero); + }, "v.further_from_negative_zero"); +} + +// Tests comparing numbers close to each other. +TEST_F(DoubleTest, SmallDiff) { + EXPECT_DOUBLE_EQ(1.0, values_.close_to_one); + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(1.0, values_.further_from_one), + "values_.further_from_one"); +} + +// Tests comparing numbers far apart. +TEST_F(DoubleTest, LargeDiff) { + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(2.0, 3.0), + "3.0"); +} + +// Tests comparing with infinity. +// +// This ensures that no overflow occurs when comparing numbers whose +// absolute value is very large. +TEST_F(DoubleTest, Infinity) { + EXPECT_DOUBLE_EQ(values_.infinity, values_.close_to_infinity); + EXPECT_DOUBLE_EQ(-values_.infinity, -values_.close_to_infinity); +#if !GTEST_OS_SYMBIAN + // Nokia's STLport crashes if we try to output infinity or NaN. + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(values_.infinity, -values_.infinity), + "-values_.infinity"); + + // This is interesting as the representations of infinity_ and nan1_ + // are only 1 DLP apart. + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(values_.infinity, values_.nan1), + "values_.nan1"); +#endif // !GTEST_OS_SYMBIAN +} + +// Tests that comparing with NAN always returns false. +TEST_F(DoubleTest, NaN) { +#if !GTEST_OS_SYMBIAN + // In C++Builder, names within local classes (such as used by + // EXPECT_FATAL_FAILURE) cannot be resolved against static members of the + // scoping class. Use a static local alias as a workaround. + // We use the assignment syntax since some compilers, like Sun Studio, + // don't allow initializing references using construction syntax + // (parentheses). + static const DoubleTest::TestValues& v = this->values_; + + // Nokia's STLport crashes if we try to output infinity or NaN. + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(v.nan1, v.nan1), + "v.nan1"); + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(v.nan1, v.nan2), "v.nan2"); + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(1.0, v.nan1), "v.nan1"); + EXPECT_FATAL_FAILURE(ASSERT_DOUBLE_EQ(v.nan1, v.infinity), + "v.infinity"); +#endif // !GTEST_OS_SYMBIAN +} + +// Tests that *_DOUBLE_EQ are reflexive. +TEST_F(DoubleTest, Reflexive) { + EXPECT_DOUBLE_EQ(0.0, 0.0); + EXPECT_DOUBLE_EQ(1.0, 1.0); +#if !GTEST_OS_SYMBIAN + // Nokia's STLport crashes if we try to output infinity or NaN. + ASSERT_DOUBLE_EQ(values_.infinity, values_.infinity); +#endif // !GTEST_OS_SYMBIAN +} + +// Tests that *_DOUBLE_EQ are commutative. +TEST_F(DoubleTest, Commutative) { + // We already tested EXPECT_DOUBLE_EQ(1.0, values_.close_to_one). + EXPECT_DOUBLE_EQ(values_.close_to_one, 1.0); + + // We already tested EXPECT_DOUBLE_EQ(1.0, values_.further_from_one). + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(values_.further_from_one, 1.0), + "1.0"); +} + +// Tests EXPECT_NEAR. +TEST_F(DoubleTest, EXPECT_NEAR) { + EXPECT_NEAR(-1.0, -1.1, 0.2); + EXPECT_NEAR(2.0, 3.0, 1.0); + EXPECT_NONFATAL_FAILURE(EXPECT_NEAR(1.0, 1.5, 0.25), // NOLINT + "The difference between 1.0 and 1.5 is 0.5, " + "which exceeds 0.25"); + // To work around a bug in gcc 2.95.0, there is intentionally no + // space after the first comma in the previous statement. +} + +// Tests ASSERT_NEAR. +TEST_F(DoubleTest, ASSERT_NEAR) { + ASSERT_NEAR(-1.0, -1.1, 0.2); + ASSERT_NEAR(2.0, 3.0, 1.0); + EXPECT_FATAL_FAILURE(ASSERT_NEAR(1.0, 1.5, 0.25), // NOLINT + "The difference between 1.0 and 1.5 is 0.5, " + "which exceeds 0.25"); + // To work around a bug in gcc 2.95.0, there is intentionally no + // space after the first comma in the previous statement. +} + +// Tests the cases where DoubleLE() should succeed. +TEST_F(DoubleTest, DoubleLESucceeds) { + EXPECT_PRED_FORMAT2(DoubleLE, 1.0, 2.0); // When val1 < val2, + ASSERT_PRED_FORMAT2(DoubleLE, 1.0, 1.0); // val1 == val2, + + // or when val1 is greater than, but almost equals to, val2. + EXPECT_PRED_FORMAT2(DoubleLE, values_.close_to_positive_zero, 0.0); +} + +// Tests the cases where DoubleLE() should fail. +TEST_F(DoubleTest, DoubleLEFails) { + // When val1 is greater than val2 by a large margin, + EXPECT_NONFATAL_FAILURE(EXPECT_PRED_FORMAT2(DoubleLE, 2.0, 1.0), + "(2.0) <= (1.0)"); + + // or by a small yet non-negligible margin, + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(DoubleLE, values_.further_from_one, 1.0); + }, "(values_.further_from_one) <= (1.0)"); + +#if !GTEST_OS_SYMBIAN && !defined(__BORLANDC__) + // Nokia's STLport crashes if we try to output infinity or NaN. + // C++Builder gives bad results for ordered comparisons involving NaNs + // due to compiler bugs. + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(DoubleLE, values_.nan1, values_.infinity); + }, "(values_.nan1) <= (values_.infinity)"); + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_PRED_FORMAT2(DoubleLE, -values_.infinity, values_.nan1); + }, " (-values_.infinity) <= (values_.nan1)"); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_PRED_FORMAT2(DoubleLE, values_.nan1, values_.nan1); + }, "(values_.nan1) <= (values_.nan1)"); +#endif // !GTEST_OS_SYMBIAN && !defined(__BORLANDC__) +} + + +// Verifies that a test or test case whose name starts with DISABLED_ is +// not run. + +// A test whose name starts with DISABLED_. +// Should not run. +TEST(DisabledTest, DISABLED_TestShouldNotRun) { + FAIL() << "Unexpected failure: Disabled test should not be run."; +} + +// A test whose name does not start with DISABLED_. +// Should run. +TEST(DisabledTest, NotDISABLED_TestShouldRun) { + EXPECT_EQ(1, 1); +} + +// A test case whose name starts with DISABLED_. +// Should not run. +TEST(DISABLED_TestCase, TestShouldNotRun) { + FAIL() << "Unexpected failure: Test in disabled test case should not be run."; +} + +// A test case and test whose names start with DISABLED_. +// Should not run. +TEST(DISABLED_TestCase, DISABLED_TestShouldNotRun) { + FAIL() << "Unexpected failure: Test in disabled test case should not be run."; +} + +// Check that when all tests in a test case are disabled, SetupTestCase() and +// TearDownTestCase() are not called. +class DisabledTestsTest : public Test { + protected: + static void SetUpTestCase() { + FAIL() << "Unexpected failure: All tests disabled in test case. " + "SetupTestCase() should not be called."; + } + + static void TearDownTestCase() { + FAIL() << "Unexpected failure: All tests disabled in test case. " + "TearDownTestCase() should not be called."; + } +}; + +TEST_F(DisabledTestsTest, DISABLED_TestShouldNotRun_1) { + FAIL() << "Unexpected failure: Disabled test should not be run."; +} + +TEST_F(DisabledTestsTest, DISABLED_TestShouldNotRun_2) { + FAIL() << "Unexpected failure: Disabled test should not be run."; +} + +// Tests that disabled typed tests aren't run. + +#if GTEST_HAS_TYPED_TEST + +template +class TypedTest : public Test { +}; + +typedef testing::Types NumericTypes; +TYPED_TEST_CASE(TypedTest, NumericTypes); + +TYPED_TEST(TypedTest, DISABLED_ShouldNotRun) { + FAIL() << "Unexpected failure: Disabled typed test should not run."; +} + +template +class DISABLED_TypedTest : public Test { +}; + +TYPED_TEST_CASE(DISABLED_TypedTest, NumericTypes); + +TYPED_TEST(DISABLED_TypedTest, ShouldNotRun) { + FAIL() << "Unexpected failure: Disabled typed test should not run."; +} + +#endif // GTEST_HAS_TYPED_TEST + +// Tests that disabled type-parameterized tests aren't run. + +#if GTEST_HAS_TYPED_TEST_P + +template +class TypedTestP : public Test { +}; + +TYPED_TEST_CASE_P(TypedTestP); + +TYPED_TEST_P(TypedTestP, DISABLED_ShouldNotRun) { + FAIL() << "Unexpected failure: " + << "Disabled type-parameterized test should not run."; +} + +REGISTER_TYPED_TEST_CASE_P(TypedTestP, DISABLED_ShouldNotRun); + +INSTANTIATE_TYPED_TEST_CASE_P(My, TypedTestP, NumericTypes); + +template +class DISABLED_TypedTestP : public Test { +}; + +TYPED_TEST_CASE_P(DISABLED_TypedTestP); + +TYPED_TEST_P(DISABLED_TypedTestP, ShouldNotRun) { + FAIL() << "Unexpected failure: " + << "Disabled type-parameterized test should not run."; +} + +REGISTER_TYPED_TEST_CASE_P(DISABLED_TypedTestP, ShouldNotRun); + +INSTANTIATE_TYPED_TEST_CASE_P(My, DISABLED_TypedTestP, NumericTypes); + +#endif // GTEST_HAS_TYPED_TEST_P + +// Tests that assertion macros evaluate their arguments exactly once. + +class SingleEvaluationTest : public Test { + public: // Must be public and not protected due to a bug in g++ 3.4.2. + // This helper function is needed by the FailedASSERT_STREQ test + // below. It's public to work around C++Builder's bug with scoping local + // classes. + static void CompareAndIncrementCharPtrs() { + ASSERT_STREQ(p1_++, p2_++); + } + + // This helper function is needed by the FailedASSERT_NE test below. It's + // public to work around C++Builder's bug with scoping local classes. + static void CompareAndIncrementInts() { + ASSERT_NE(a_++, b_++); + } + + protected: + SingleEvaluationTest() { + p1_ = s1_; + p2_ = s2_; + a_ = 0; + b_ = 0; + } + + static const char* const s1_; + static const char* const s2_; + static const char* p1_; + static const char* p2_; + + static int a_; + static int b_; +}; + +const char* const SingleEvaluationTest::s1_ = "01234"; +const char* const SingleEvaluationTest::s2_ = "abcde"; +const char* SingleEvaluationTest::p1_; +const char* SingleEvaluationTest::p2_; +int SingleEvaluationTest::a_; +int SingleEvaluationTest::b_; + +// Tests that when ASSERT_STREQ fails, it evaluates its arguments +// exactly once. +TEST_F(SingleEvaluationTest, FailedASSERT_STREQ) { + EXPECT_FATAL_FAILURE(SingleEvaluationTest::CompareAndIncrementCharPtrs(), + "p2_++"); + EXPECT_EQ(s1_ + 1, p1_); + EXPECT_EQ(s2_ + 1, p2_); +} + +// Tests that string assertion arguments are evaluated exactly once. +TEST_F(SingleEvaluationTest, ASSERT_STR) { + // successful EXPECT_STRNE + EXPECT_STRNE(p1_++, p2_++); + EXPECT_EQ(s1_ + 1, p1_); + EXPECT_EQ(s2_ + 1, p2_); + + // failed EXPECT_STRCASEEQ + EXPECT_NONFATAL_FAILURE(EXPECT_STRCASEEQ(p1_++, p2_++), + "ignoring case"); + EXPECT_EQ(s1_ + 2, p1_); + EXPECT_EQ(s2_ + 2, p2_); +} + +// Tests that when ASSERT_NE fails, it evaluates its arguments exactly +// once. +TEST_F(SingleEvaluationTest, FailedASSERT_NE) { + EXPECT_FATAL_FAILURE(SingleEvaluationTest::CompareAndIncrementInts(), + "(a_++) != (b_++)"); + EXPECT_EQ(1, a_); + EXPECT_EQ(1, b_); +} + +// Tests that assertion arguments are evaluated exactly once. +TEST_F(SingleEvaluationTest, OtherCases) { + // successful EXPECT_TRUE + EXPECT_TRUE(0 == a_++); // NOLINT + EXPECT_EQ(1, a_); + + // failed EXPECT_TRUE + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(-1 == a_++), "-1 == a_++"); + EXPECT_EQ(2, a_); + + // successful EXPECT_GT + EXPECT_GT(a_++, b_++); + EXPECT_EQ(3, a_); + EXPECT_EQ(1, b_); + + // failed EXPECT_LT + EXPECT_NONFATAL_FAILURE(EXPECT_LT(a_++, b_++), "(a_++) < (b_++)"); + EXPECT_EQ(4, a_); + EXPECT_EQ(2, b_); + + // successful ASSERT_TRUE + ASSERT_TRUE(0 < a_++); // NOLINT + EXPECT_EQ(5, a_); + + // successful ASSERT_GT + ASSERT_GT(a_++, b_++); + EXPECT_EQ(6, a_); + EXPECT_EQ(3, b_); +} + +#if GTEST_HAS_EXCEPTIONS + +void ThrowAnInteger() { + throw 1; +} + +// Tests that assertion arguments are evaluated exactly once. +TEST_F(SingleEvaluationTest, ExceptionTests) { + // successful EXPECT_THROW + EXPECT_THROW({ // NOLINT + a_++; + ThrowAnInteger(); + }, int); + EXPECT_EQ(1, a_); + + // failed EXPECT_THROW, throws different + EXPECT_NONFATAL_FAILURE(EXPECT_THROW({ // NOLINT + a_++; + ThrowAnInteger(); + }, bool), "throws a different type"); + EXPECT_EQ(2, a_); + + // failed EXPECT_THROW, throws nothing + EXPECT_NONFATAL_FAILURE(EXPECT_THROW(a_++, bool), "throws nothing"); + EXPECT_EQ(3, a_); + + // successful EXPECT_NO_THROW + EXPECT_NO_THROW(a_++); + EXPECT_EQ(4, a_); + + // failed EXPECT_NO_THROW + EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW({ // NOLINT + a_++; + ThrowAnInteger(); + }), "it throws"); + EXPECT_EQ(5, a_); + + // successful EXPECT_ANY_THROW + EXPECT_ANY_THROW({ // NOLINT + a_++; + ThrowAnInteger(); + }); + EXPECT_EQ(6, a_); + + // failed EXPECT_ANY_THROW + EXPECT_NONFATAL_FAILURE(EXPECT_ANY_THROW(a_++), "it doesn't"); + EXPECT_EQ(7, a_); +} + +#endif // GTEST_HAS_EXCEPTIONS + +// Tests {ASSERT|EXPECT}_NO_FATAL_FAILURE. +class NoFatalFailureTest : public Test { + protected: + void Succeeds() {} + void FailsNonFatal() { + ADD_FAILURE() << "some non-fatal failure"; + } + void Fails() { + FAIL() << "some fatal failure"; + } + + void DoAssertNoFatalFailureOnFails() { + ASSERT_NO_FATAL_FAILURE(Fails()); + ADD_FAILURE() << "shold not reach here."; + } + + void DoExpectNoFatalFailureOnFails() { + EXPECT_NO_FATAL_FAILURE(Fails()); + ADD_FAILURE() << "other failure"; + } +}; + +TEST_F(NoFatalFailureTest, NoFailure) { + EXPECT_NO_FATAL_FAILURE(Succeeds()); + ASSERT_NO_FATAL_FAILURE(Succeeds()); +} + +TEST_F(NoFatalFailureTest, NonFatalIsNoFailure) { + EXPECT_NONFATAL_FAILURE( + EXPECT_NO_FATAL_FAILURE(FailsNonFatal()), + "some non-fatal failure"); + EXPECT_NONFATAL_FAILURE( + ASSERT_NO_FATAL_FAILURE(FailsNonFatal()), + "some non-fatal failure"); +} + +TEST_F(NoFatalFailureTest, AssertNoFatalFailureOnFatalFailure) { + TestPartResultArray gtest_failures; + { + ScopedFakeTestPartResultReporter gtest_reporter(>est_failures); + DoAssertNoFatalFailureOnFails(); + } + ASSERT_EQ(2, gtest_failures.size()); + EXPECT_EQ(TestPartResult::kFatalFailure, + gtest_failures.GetTestPartResult(0).type()); + EXPECT_EQ(TestPartResult::kFatalFailure, + gtest_failures.GetTestPartResult(1).type()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "some fatal failure", + gtest_failures.GetTestPartResult(0).message()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "it does", + gtest_failures.GetTestPartResult(1).message()); +} + +TEST_F(NoFatalFailureTest, ExpectNoFatalFailureOnFatalFailure) { + TestPartResultArray gtest_failures; + { + ScopedFakeTestPartResultReporter gtest_reporter(>est_failures); + DoExpectNoFatalFailureOnFails(); + } + ASSERT_EQ(3, gtest_failures.size()); + EXPECT_EQ(TestPartResult::kFatalFailure, + gtest_failures.GetTestPartResult(0).type()); + EXPECT_EQ(TestPartResult::kNonFatalFailure, + gtest_failures.GetTestPartResult(1).type()); + EXPECT_EQ(TestPartResult::kNonFatalFailure, + gtest_failures.GetTestPartResult(2).type()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "some fatal failure", + gtest_failures.GetTestPartResult(0).message()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "it does", + gtest_failures.GetTestPartResult(1).message()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "other failure", + gtest_failures.GetTestPartResult(2).message()); +} + +TEST_F(NoFatalFailureTest, MessageIsStreamable) { + TestPartResultArray gtest_failures; + { + ScopedFakeTestPartResultReporter gtest_reporter(>est_failures); + EXPECT_NO_FATAL_FAILURE(FAIL() << "foo") << "my message"; + } + ASSERT_EQ(2, gtest_failures.size()); + EXPECT_EQ(TestPartResult::kNonFatalFailure, + gtest_failures.GetTestPartResult(0).type()); + EXPECT_EQ(TestPartResult::kNonFatalFailure, + gtest_failures.GetTestPartResult(1).type()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "foo", + gtest_failures.GetTestPartResult(0).message()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "my message", + gtest_failures.GetTestPartResult(1).message()); +} + +// Tests non-string assertions. + +// Tests EqFailure(), used for implementing *EQ* assertions. +TEST(AssertionTest, EqFailure) { + const std::string foo_val("5"), bar_val("6"); + const std::string msg1( + EqFailure("foo", "bar", foo_val, bar_val, false) + .failure_message()); + EXPECT_STREQ( + "Value of: bar\n" + " Actual: 6\n" + "Expected: foo\n" + "Which is: 5", + msg1.c_str()); + + const std::string msg2( + EqFailure("foo", "6", foo_val, bar_val, false) + .failure_message()); + EXPECT_STREQ( + "Value of: 6\n" + "Expected: foo\n" + "Which is: 5", + msg2.c_str()); + + const std::string msg3( + EqFailure("5", "bar", foo_val, bar_val, false) + .failure_message()); + EXPECT_STREQ( + "Value of: bar\n" + " Actual: 6\n" + "Expected: 5", + msg3.c_str()); + + const std::string msg4( + EqFailure("5", "6", foo_val, bar_val, false).failure_message()); + EXPECT_STREQ( + "Value of: 6\n" + "Expected: 5", + msg4.c_str()); + + const std::string msg5( + EqFailure("foo", "bar", + std::string("\"x\""), std::string("\"y\""), + true).failure_message()); + EXPECT_STREQ( + "Value of: bar\n" + " Actual: \"y\"\n" + "Expected: foo (ignoring case)\n" + "Which is: \"x\"", + msg5.c_str()); +} + +// Tests AppendUserMessage(), used for implementing the *EQ* macros. +TEST(AssertionTest, AppendUserMessage) { + const std::string foo("foo"); + + Message msg; + EXPECT_STREQ("foo", + AppendUserMessage(foo, msg).c_str()); + + msg << "bar"; + EXPECT_STREQ("foo\nbar", + AppendUserMessage(foo, msg).c_str()); +} + +#ifdef __BORLANDC__ +// Silences warnings: "Condition is always true", "Unreachable code" +# pragma option push -w-ccc -w-rch +#endif + +// Tests ASSERT_TRUE. +TEST(AssertionTest, ASSERT_TRUE) { + ASSERT_TRUE(2 > 1); // NOLINT + EXPECT_FATAL_FAILURE(ASSERT_TRUE(2 < 1), + "2 < 1"); +} + +// Tests ASSERT_TRUE(predicate) for predicates returning AssertionResult. +TEST(AssertionTest, AssertTrueWithAssertionResult) { + ASSERT_TRUE(ResultIsEven(2)); +#ifndef __BORLANDC__ + // ICE's in C++Builder. + EXPECT_FATAL_FAILURE(ASSERT_TRUE(ResultIsEven(3)), + "Value of: ResultIsEven(3)\n" + " Actual: false (3 is odd)\n" + "Expected: true"); +#endif + ASSERT_TRUE(ResultIsEvenNoExplanation(2)); + EXPECT_FATAL_FAILURE(ASSERT_TRUE(ResultIsEvenNoExplanation(3)), + "Value of: ResultIsEvenNoExplanation(3)\n" + " Actual: false (3 is odd)\n" + "Expected: true"); +} + +// Tests ASSERT_FALSE. +TEST(AssertionTest, ASSERT_FALSE) { + ASSERT_FALSE(2 < 1); // NOLINT + EXPECT_FATAL_FAILURE(ASSERT_FALSE(2 > 1), + "Value of: 2 > 1\n" + " Actual: true\n" + "Expected: false"); +} + +// Tests ASSERT_FALSE(predicate) for predicates returning AssertionResult. +TEST(AssertionTest, AssertFalseWithAssertionResult) { + ASSERT_FALSE(ResultIsEven(3)); +#ifndef __BORLANDC__ + // ICE's in C++Builder. + EXPECT_FATAL_FAILURE(ASSERT_FALSE(ResultIsEven(2)), + "Value of: ResultIsEven(2)\n" + " Actual: true (2 is even)\n" + "Expected: false"); +#endif + ASSERT_FALSE(ResultIsEvenNoExplanation(3)); + EXPECT_FATAL_FAILURE(ASSERT_FALSE(ResultIsEvenNoExplanation(2)), + "Value of: ResultIsEvenNoExplanation(2)\n" + " Actual: true\n" + "Expected: false"); +} + +#ifdef __BORLANDC__ +// Restores warnings after previous "#pragma option push" supressed them +# pragma option pop +#endif + +// Tests using ASSERT_EQ on double values. The purpose is to make +// sure that the specialization we did for integer and anonymous enums +// isn't used for double arguments. +TEST(ExpectTest, ASSERT_EQ_Double) { + // A success. + ASSERT_EQ(5.6, 5.6); + + // A failure. + EXPECT_FATAL_FAILURE(ASSERT_EQ(5.1, 5.2), + "5.1"); +} + +// Tests ASSERT_EQ. +TEST(AssertionTest, ASSERT_EQ) { + ASSERT_EQ(5, 2 + 3); + EXPECT_FATAL_FAILURE(ASSERT_EQ(5, 2*3), + "Value of: 2*3\n" + " Actual: 6\n" + "Expected: 5"); +} + +// Tests ASSERT_EQ(NULL, pointer). +#if GTEST_CAN_COMPARE_NULL +TEST(AssertionTest, ASSERT_EQ_NULL) { + // A success. + const char* p = NULL; + // Some older GCC versions may issue a spurious waring in this or the next + // assertion statement. This warning should not be suppressed with + // static_cast since the test verifies the ability to use bare NULL as the + // expected parameter to the macro. + ASSERT_EQ(NULL, p); + + // A failure. + static int n = 0; + EXPECT_FATAL_FAILURE(ASSERT_EQ(NULL, &n), + "Value of: &n\n"); +} +#endif // GTEST_CAN_COMPARE_NULL + +// Tests ASSERT_EQ(0, non_pointer). Since the literal 0 can be +// treated as a null pointer by the compiler, we need to make sure +// that ASSERT_EQ(0, non_pointer) isn't interpreted by Google Test as +// ASSERT_EQ(static_cast(NULL), non_pointer). +TEST(ExpectTest, ASSERT_EQ_0) { + int n = 0; + + // A success. + ASSERT_EQ(0, n); + + // A failure. + EXPECT_FATAL_FAILURE(ASSERT_EQ(0, 5.6), + "Expected: 0"); +} + +// Tests ASSERT_NE. +TEST(AssertionTest, ASSERT_NE) { + ASSERT_NE(6, 7); + EXPECT_FATAL_FAILURE(ASSERT_NE('a', 'a'), + "Expected: ('a') != ('a'), " + "actual: 'a' (97, 0x61) vs 'a' (97, 0x61)"); +} + +// Tests ASSERT_LE. +TEST(AssertionTest, ASSERT_LE) { + ASSERT_LE(2, 3); + ASSERT_LE(2, 2); + EXPECT_FATAL_FAILURE(ASSERT_LE(2, 0), + "Expected: (2) <= (0), actual: 2 vs 0"); +} + +// Tests ASSERT_LT. +TEST(AssertionTest, ASSERT_LT) { + ASSERT_LT(2, 3); + EXPECT_FATAL_FAILURE(ASSERT_LT(2, 2), + "Expected: (2) < (2), actual: 2 vs 2"); +} + +// Tests ASSERT_GE. +TEST(AssertionTest, ASSERT_GE) { + ASSERT_GE(2, 1); + ASSERT_GE(2, 2); + EXPECT_FATAL_FAILURE(ASSERT_GE(2, 3), + "Expected: (2) >= (3), actual: 2 vs 3"); +} + +// Tests ASSERT_GT. +TEST(AssertionTest, ASSERT_GT) { + ASSERT_GT(2, 1); + EXPECT_FATAL_FAILURE(ASSERT_GT(2, 2), + "Expected: (2) > (2), actual: 2 vs 2"); +} + +#if GTEST_HAS_EXCEPTIONS + +void ThrowNothing() {} + +// Tests ASSERT_THROW. +TEST(AssertionTest, ASSERT_THROW) { + ASSERT_THROW(ThrowAnInteger(), int); + +# ifndef __BORLANDC__ + + // ICE's in C++Builder 2007 and 2009. + EXPECT_FATAL_FAILURE( + ASSERT_THROW(ThrowAnInteger(), bool), + "Expected: ThrowAnInteger() throws an exception of type bool.\n" + " Actual: it throws a different type."); +# endif + + EXPECT_FATAL_FAILURE( + ASSERT_THROW(ThrowNothing(), bool), + "Expected: ThrowNothing() throws an exception of type bool.\n" + " Actual: it throws nothing."); +} + +// Tests ASSERT_NO_THROW. +TEST(AssertionTest, ASSERT_NO_THROW) { + ASSERT_NO_THROW(ThrowNothing()); + EXPECT_FATAL_FAILURE(ASSERT_NO_THROW(ThrowAnInteger()), + "Expected: ThrowAnInteger() doesn't throw an exception." + "\n Actual: it throws."); +} + +// Tests ASSERT_ANY_THROW. +TEST(AssertionTest, ASSERT_ANY_THROW) { + ASSERT_ANY_THROW(ThrowAnInteger()); + EXPECT_FATAL_FAILURE( + ASSERT_ANY_THROW(ThrowNothing()), + "Expected: ThrowNothing() throws an exception.\n" + " Actual: it doesn't."); +} + +#endif // GTEST_HAS_EXCEPTIONS + +// Makes sure we deal with the precedence of <<. This test should +// compile. +TEST(AssertionTest, AssertPrecedence) { + ASSERT_EQ(1 < 2, true); + bool false_value = false; + ASSERT_EQ(true && false_value, false); +} + +// A subroutine used by the following test. +void TestEq1(int x) { + ASSERT_EQ(1, x); +} + +// Tests calling a test subroutine that's not part of a fixture. +TEST(AssertionTest, NonFixtureSubroutine) { + EXPECT_FATAL_FAILURE(TestEq1(2), + "Value of: x"); +} + +// An uncopyable class. +class Uncopyable { + public: + explicit Uncopyable(int a_value) : value_(a_value) {} + + int value() const { return value_; } + bool operator==(const Uncopyable& rhs) const { + return value() == rhs.value(); + } + private: + // This constructor deliberately has no implementation, as we don't + // want this class to be copyable. + Uncopyable(const Uncopyable&); // NOLINT + + int value_; +}; + +::std::ostream& operator<<(::std::ostream& os, const Uncopyable& value) { + return os << value.value(); +} + + +bool IsPositiveUncopyable(const Uncopyable& x) { + return x.value() > 0; +} + +// A subroutine used by the following test. +void TestAssertNonPositive() { + Uncopyable y(-1); + ASSERT_PRED1(IsPositiveUncopyable, y); +} +// A subroutine used by the following test. +void TestAssertEqualsUncopyable() { + Uncopyable x(5); + Uncopyable y(-1); + ASSERT_EQ(x, y); +} + +// Tests that uncopyable objects can be used in assertions. +TEST(AssertionTest, AssertWorksWithUncopyableObject) { + Uncopyable x(5); + ASSERT_PRED1(IsPositiveUncopyable, x); + ASSERT_EQ(x, x); + EXPECT_FATAL_FAILURE(TestAssertNonPositive(), + "IsPositiveUncopyable(y) evaluates to false, where\ny evaluates to -1"); + EXPECT_FATAL_FAILURE(TestAssertEqualsUncopyable(), + "Value of: y\n Actual: -1\nExpected: x\nWhich is: 5"); +} + +// Tests that uncopyable objects can be used in expects. +TEST(AssertionTest, ExpectWorksWithUncopyableObject) { + Uncopyable x(5); + EXPECT_PRED1(IsPositiveUncopyable, x); + Uncopyable y(-1); + EXPECT_NONFATAL_FAILURE(EXPECT_PRED1(IsPositiveUncopyable, y), + "IsPositiveUncopyable(y) evaluates to false, where\ny evaluates to -1"); + EXPECT_EQ(x, x); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(x, y), + "Value of: y\n Actual: -1\nExpected: x\nWhich is: 5"); +} + +enum NamedEnum { + kE1 = 0, + kE2 = 1 +}; + +TEST(AssertionTest, NamedEnum) { + EXPECT_EQ(kE1, kE1); + EXPECT_LT(kE1, kE2); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(kE1, kE2), "Which is: 0"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(kE1, kE2), "Actual: 1"); +} + +// The version of gcc used in XCode 2.2 has a bug and doesn't allow +// anonymous enums in assertions. Therefore the following test is not +// done on Mac. +// Sun Studio and HP aCC also reject this code. +#if !GTEST_OS_MAC && !defined(__SUNPRO_CC) && !defined(__HP_aCC) + +// Tests using assertions with anonymous enums. +enum { + kCaseA = -1, + +# if GTEST_OS_LINUX + + // We want to test the case where the size of the anonymous enum is + // larger than sizeof(int), to make sure our implementation of the + // assertions doesn't truncate the enums. However, MSVC + // (incorrectly) doesn't allow an enum value to exceed the range of + // an int, so this has to be conditionally compiled. + // + // On Linux, kCaseB and kCaseA have the same value when truncated to + // int size. We want to test whether this will confuse the + // assertions. + kCaseB = testing::internal::kMaxBiggestInt, + +# else + + kCaseB = INT_MAX, + +# endif // GTEST_OS_LINUX + + kCaseC = 42 +}; + +TEST(AssertionTest, AnonymousEnum) { +# if GTEST_OS_LINUX + + EXPECT_EQ(static_cast(kCaseA), static_cast(kCaseB)); + +# endif // GTEST_OS_LINUX + + EXPECT_EQ(kCaseA, kCaseA); + EXPECT_NE(kCaseA, kCaseB); + EXPECT_LT(kCaseA, kCaseB); + EXPECT_LE(kCaseA, kCaseB); + EXPECT_GT(kCaseB, kCaseA); + EXPECT_GE(kCaseA, kCaseA); + EXPECT_NONFATAL_FAILURE(EXPECT_GE(kCaseA, kCaseB), + "(kCaseA) >= (kCaseB)"); + EXPECT_NONFATAL_FAILURE(EXPECT_GE(kCaseA, kCaseC), + "-1 vs 42"); + + ASSERT_EQ(kCaseA, kCaseA); + ASSERT_NE(kCaseA, kCaseB); + ASSERT_LT(kCaseA, kCaseB); + ASSERT_LE(kCaseA, kCaseB); + ASSERT_GT(kCaseB, kCaseA); + ASSERT_GE(kCaseA, kCaseA); + +# ifndef __BORLANDC__ + + // ICE's in C++Builder. + EXPECT_FATAL_FAILURE(ASSERT_EQ(kCaseA, kCaseB), + "Value of: kCaseB"); + EXPECT_FATAL_FAILURE(ASSERT_EQ(kCaseA, kCaseC), + "Actual: 42"); +# endif + + EXPECT_FATAL_FAILURE(ASSERT_EQ(kCaseA, kCaseC), + "Which is: -1"); +} + +#endif // !GTEST_OS_MAC && !defined(__SUNPRO_CC) + +#if GTEST_OS_WINDOWS + +static HRESULT UnexpectedHRESULTFailure() { + return E_UNEXPECTED; +} + +static HRESULT OkHRESULTSuccess() { + return S_OK; +} + +static HRESULT FalseHRESULTSuccess() { + return S_FALSE; +} + +// HRESULT assertion tests test both zero and non-zero +// success codes as well as failure message for each. +// +// Windows CE doesn't support message texts. +TEST(HRESULTAssertionTest, EXPECT_HRESULT_SUCCEEDED) { + EXPECT_HRESULT_SUCCEEDED(S_OK); + EXPECT_HRESULT_SUCCEEDED(S_FALSE); + + EXPECT_NONFATAL_FAILURE(EXPECT_HRESULT_SUCCEEDED(UnexpectedHRESULTFailure()), + "Expected: (UnexpectedHRESULTFailure()) succeeds.\n" + " Actual: 0x8000FFFF"); +} + +TEST(HRESULTAssertionTest, ASSERT_HRESULT_SUCCEEDED) { + ASSERT_HRESULT_SUCCEEDED(S_OK); + ASSERT_HRESULT_SUCCEEDED(S_FALSE); + + EXPECT_FATAL_FAILURE(ASSERT_HRESULT_SUCCEEDED(UnexpectedHRESULTFailure()), + "Expected: (UnexpectedHRESULTFailure()) succeeds.\n" + " Actual: 0x8000FFFF"); +} + +TEST(HRESULTAssertionTest, EXPECT_HRESULT_FAILED) { + EXPECT_HRESULT_FAILED(E_UNEXPECTED); + + EXPECT_NONFATAL_FAILURE(EXPECT_HRESULT_FAILED(OkHRESULTSuccess()), + "Expected: (OkHRESULTSuccess()) fails.\n" + " Actual: 0x0"); + EXPECT_NONFATAL_FAILURE(EXPECT_HRESULT_FAILED(FalseHRESULTSuccess()), + "Expected: (FalseHRESULTSuccess()) fails.\n" + " Actual: 0x1"); +} + +TEST(HRESULTAssertionTest, ASSERT_HRESULT_FAILED) { + ASSERT_HRESULT_FAILED(E_UNEXPECTED); + +# ifndef __BORLANDC__ + + // ICE's in C++Builder 2007 and 2009. + EXPECT_FATAL_FAILURE(ASSERT_HRESULT_FAILED(OkHRESULTSuccess()), + "Expected: (OkHRESULTSuccess()) fails.\n" + " Actual: 0x0"); +# endif + + EXPECT_FATAL_FAILURE(ASSERT_HRESULT_FAILED(FalseHRESULTSuccess()), + "Expected: (FalseHRESULTSuccess()) fails.\n" + " Actual: 0x1"); +} + +// Tests that streaming to the HRESULT macros works. +TEST(HRESULTAssertionTest, Streaming) { + EXPECT_HRESULT_SUCCEEDED(S_OK) << "unexpected failure"; + ASSERT_HRESULT_SUCCEEDED(S_OK) << "unexpected failure"; + EXPECT_HRESULT_FAILED(E_UNEXPECTED) << "unexpected failure"; + ASSERT_HRESULT_FAILED(E_UNEXPECTED) << "unexpected failure"; + + EXPECT_NONFATAL_FAILURE( + EXPECT_HRESULT_SUCCEEDED(E_UNEXPECTED) << "expected failure", + "expected failure"); + +# ifndef __BORLANDC__ + + // ICE's in C++Builder 2007 and 2009. + EXPECT_FATAL_FAILURE( + ASSERT_HRESULT_SUCCEEDED(E_UNEXPECTED) << "expected failure", + "expected failure"); +# endif + + EXPECT_NONFATAL_FAILURE( + EXPECT_HRESULT_FAILED(S_OK) << "expected failure", + "expected failure"); + + EXPECT_FATAL_FAILURE( + ASSERT_HRESULT_FAILED(S_OK) << "expected failure", + "expected failure"); +} + +#endif // GTEST_OS_WINDOWS + +#ifdef __BORLANDC__ +// Silences warnings: "Condition is always true", "Unreachable code" +# pragma option push -w-ccc -w-rch +#endif + +// Tests that the assertion macros behave like single statements. +TEST(AssertionSyntaxTest, BasicAssertionsBehavesLikeSingleStatement) { + if (AlwaysFalse()) + ASSERT_TRUE(false) << "This should never be executed; " + "It's a compilation test only."; + + if (AlwaysTrue()) + EXPECT_FALSE(false); + else + ; // NOLINT + + if (AlwaysFalse()) + ASSERT_LT(1, 3); + + if (AlwaysFalse()) + ; // NOLINT + else + EXPECT_GT(3, 2) << ""; +} + +#if GTEST_HAS_EXCEPTIONS +// Tests that the compiler will not complain about unreachable code in the +// EXPECT_THROW/EXPECT_ANY_THROW/EXPECT_NO_THROW macros. +TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) { + int n = 0; + + EXPECT_THROW(throw 1, int); + EXPECT_NONFATAL_FAILURE(EXPECT_THROW(n++, int), ""); + EXPECT_NONFATAL_FAILURE(EXPECT_THROW(throw 1, const char*), ""); + EXPECT_NO_THROW(n++); + EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW(throw 1), ""); + EXPECT_ANY_THROW(throw 1); + EXPECT_NONFATAL_FAILURE(EXPECT_ANY_THROW(n++), ""); +} + +TEST(AssertionSyntaxTest, ExceptionAssertionsBehavesLikeSingleStatement) { + if (AlwaysFalse()) + EXPECT_THROW(ThrowNothing(), bool); + + if (AlwaysTrue()) + EXPECT_THROW(ThrowAnInteger(), int); + else + ; // NOLINT + + if (AlwaysFalse()) + EXPECT_NO_THROW(ThrowAnInteger()); + + if (AlwaysTrue()) + EXPECT_NO_THROW(ThrowNothing()); + else + ; // NOLINT + + if (AlwaysFalse()) + EXPECT_ANY_THROW(ThrowNothing()); + + if (AlwaysTrue()) + EXPECT_ANY_THROW(ThrowAnInteger()); + else + ; // NOLINT +} +#endif // GTEST_HAS_EXCEPTIONS + +TEST(AssertionSyntaxTest, NoFatalFailureAssertionsBehavesLikeSingleStatement) { + if (AlwaysFalse()) + EXPECT_NO_FATAL_FAILURE(FAIL()) << "This should never be executed. " + << "It's a compilation test only."; + else + ; // NOLINT + + if (AlwaysFalse()) + ASSERT_NO_FATAL_FAILURE(FAIL()) << ""; + else + ; // NOLINT + + if (AlwaysTrue()) + EXPECT_NO_FATAL_FAILURE(SUCCEED()); + else + ; // NOLINT + + if (AlwaysFalse()) + ; // NOLINT + else + ASSERT_NO_FATAL_FAILURE(SUCCEED()); +} + +// Tests that the assertion macros work well with switch statements. +TEST(AssertionSyntaxTest, WorksWithSwitch) { + switch (0) { + case 1: + break; + default: + ASSERT_TRUE(true); + } + + switch (0) + case 0: + EXPECT_FALSE(false) << "EXPECT_FALSE failed in switch case"; + + // Binary assertions are implemented using a different code path + // than the Boolean assertions. Hence we test them separately. + switch (0) { + case 1: + default: + ASSERT_EQ(1, 1) << "ASSERT_EQ failed in default switch handler"; + } + + switch (0) + case 0: + EXPECT_NE(1, 2); +} + +#if GTEST_HAS_EXCEPTIONS + +void ThrowAString() { + throw "std::string"; +} + +// Test that the exception assertion macros compile and work with const +// type qualifier. +TEST(AssertionSyntaxTest, WorksWithConst) { + ASSERT_THROW(ThrowAString(), const char*); + + EXPECT_THROW(ThrowAString(), const char*); +} + +#endif // GTEST_HAS_EXCEPTIONS + +} // namespace + +namespace testing { + +// Tests that Google Test tracks SUCCEED*. +TEST(SuccessfulAssertionTest, SUCCEED) { + SUCCEED(); + SUCCEED() << "OK"; + EXPECT_EQ(2, GetUnitTestImpl()->current_test_result()->total_part_count()); +} + +// Tests that Google Test doesn't track successful EXPECT_*. +TEST(SuccessfulAssertionTest, EXPECT) { + EXPECT_TRUE(true); + EXPECT_EQ(0, GetUnitTestImpl()->current_test_result()->total_part_count()); +} + +// Tests that Google Test doesn't track successful EXPECT_STR*. +TEST(SuccessfulAssertionTest, EXPECT_STR) { + EXPECT_STREQ("", ""); + EXPECT_EQ(0, GetUnitTestImpl()->current_test_result()->total_part_count()); +} + +// Tests that Google Test doesn't track successful ASSERT_*. +TEST(SuccessfulAssertionTest, ASSERT) { + ASSERT_TRUE(true); + EXPECT_EQ(0, GetUnitTestImpl()->current_test_result()->total_part_count()); +} + +// Tests that Google Test doesn't track successful ASSERT_STR*. +TEST(SuccessfulAssertionTest, ASSERT_STR) { + ASSERT_STREQ("", ""); + EXPECT_EQ(0, GetUnitTestImpl()->current_test_result()->total_part_count()); +} + +} // namespace testing + +namespace { + +// Tests the message streaming variation of assertions. + +TEST(AssertionWithMessageTest, EXPECT) { + EXPECT_EQ(1, 1) << "This should succeed."; + EXPECT_NONFATAL_FAILURE(EXPECT_NE(1, 1) << "Expected failure #1.", + "Expected failure #1"); + EXPECT_LE(1, 2) << "This should succeed."; + EXPECT_NONFATAL_FAILURE(EXPECT_LT(1, 0) << "Expected failure #2.", + "Expected failure #2."); + EXPECT_GE(1, 0) << "This should succeed."; + EXPECT_NONFATAL_FAILURE(EXPECT_GT(1, 2) << "Expected failure #3.", + "Expected failure #3."); + + EXPECT_STREQ("1", "1") << "This should succeed."; + EXPECT_NONFATAL_FAILURE(EXPECT_STRNE("1", "1") << "Expected failure #4.", + "Expected failure #4."); + EXPECT_STRCASEEQ("a", "A") << "This should succeed."; + EXPECT_NONFATAL_FAILURE(EXPECT_STRCASENE("a", "A") << "Expected failure #5.", + "Expected failure #5."); + + EXPECT_FLOAT_EQ(1, 1) << "This should succeed."; + EXPECT_NONFATAL_FAILURE(EXPECT_DOUBLE_EQ(1, 1.2) << "Expected failure #6.", + "Expected failure #6."); + EXPECT_NEAR(1, 1.1, 0.2) << "This should succeed."; +} + +TEST(AssertionWithMessageTest, ASSERT) { + ASSERT_EQ(1, 1) << "This should succeed."; + ASSERT_NE(1, 2) << "This should succeed."; + ASSERT_LE(1, 2) << "This should succeed."; + ASSERT_LT(1, 2) << "This should succeed."; + ASSERT_GE(1, 0) << "This should succeed."; + EXPECT_FATAL_FAILURE(ASSERT_GT(1, 2) << "Expected failure.", + "Expected failure."); +} + +TEST(AssertionWithMessageTest, ASSERT_STR) { + ASSERT_STREQ("1", "1") << "This should succeed."; + ASSERT_STRNE("1", "2") << "This should succeed."; + ASSERT_STRCASEEQ("a", "A") << "This should succeed."; + EXPECT_FATAL_FAILURE(ASSERT_STRCASENE("a", "A") << "Expected failure.", + "Expected failure."); +} + +TEST(AssertionWithMessageTest, ASSERT_FLOATING) { + ASSERT_FLOAT_EQ(1, 1) << "This should succeed."; + ASSERT_DOUBLE_EQ(1, 1) << "This should succeed."; + EXPECT_FATAL_FAILURE(ASSERT_NEAR(1,1.2, 0.1) << "Expect failure.", // NOLINT + "Expect failure."); + // To work around a bug in gcc 2.95.0, there is intentionally no + // space after the first comma in the previous statement. +} + +// Tests using ASSERT_FALSE with a streamed message. +TEST(AssertionWithMessageTest, ASSERT_FALSE) { + ASSERT_FALSE(false) << "This shouldn't fail."; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_FALSE(true) << "Expected failure: " << 2 << " > " << 1 + << " evaluates to " << true; + }, "Expected failure"); +} + +// Tests using FAIL with a streamed message. +TEST(AssertionWithMessageTest, FAIL) { + EXPECT_FATAL_FAILURE(FAIL() << 0, + "0"); +} + +// Tests using SUCCEED with a streamed message. +TEST(AssertionWithMessageTest, SUCCEED) { + SUCCEED() << "Success == " << 1; +} + +// Tests using ASSERT_TRUE with a streamed message. +TEST(AssertionWithMessageTest, ASSERT_TRUE) { + ASSERT_TRUE(true) << "This should succeed."; + ASSERT_TRUE(true) << true; + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_TRUE(false) << static_cast(NULL) + << static_cast(NULL); + }, "(null)(null)"); +} + +#if GTEST_OS_WINDOWS +// Tests using wide strings in assertion messages. +TEST(AssertionWithMessageTest, WideStringMessage) { + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_TRUE(false) << L"This failure is expected.\x8119"; + }, "This failure is expected."); + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_EQ(1, 2) << "This failure is " + << L"expected too.\x8120"; + }, "This failure is expected too."); +} +#endif // GTEST_OS_WINDOWS + +// Tests EXPECT_TRUE. +TEST(ExpectTest, EXPECT_TRUE) { + EXPECT_TRUE(true) << "Intentional success"; + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(false) << "Intentional failure #1.", + "Intentional failure #1."); + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(false) << "Intentional failure #2.", + "Intentional failure #2."); + EXPECT_TRUE(2 > 1); // NOLINT + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(2 < 1), + "Value of: 2 < 1\n" + " Actual: false\n" + "Expected: true"); + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(2 > 3), + "2 > 3"); +} + +// Tests EXPECT_TRUE(predicate) for predicates returning AssertionResult. +TEST(ExpectTest, ExpectTrueWithAssertionResult) { + EXPECT_TRUE(ResultIsEven(2)); + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(ResultIsEven(3)), + "Value of: ResultIsEven(3)\n" + " Actual: false (3 is odd)\n" + "Expected: true"); + EXPECT_TRUE(ResultIsEvenNoExplanation(2)); + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(ResultIsEvenNoExplanation(3)), + "Value of: ResultIsEvenNoExplanation(3)\n" + " Actual: false (3 is odd)\n" + "Expected: true"); +} + +// Tests EXPECT_FALSE with a streamed message. +TEST(ExpectTest, EXPECT_FALSE) { + EXPECT_FALSE(2 < 1); // NOLINT + EXPECT_FALSE(false) << "Intentional success"; + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(true) << "Intentional failure #1.", + "Intentional failure #1."); + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(true) << "Intentional failure #2.", + "Intentional failure #2."); + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(2 > 1), + "Value of: 2 > 1\n" + " Actual: true\n" + "Expected: false"); + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(2 < 3), + "2 < 3"); +} + +// Tests EXPECT_FALSE(predicate) for predicates returning AssertionResult. +TEST(ExpectTest, ExpectFalseWithAssertionResult) { + EXPECT_FALSE(ResultIsEven(3)); + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(ResultIsEven(2)), + "Value of: ResultIsEven(2)\n" + " Actual: true (2 is even)\n" + "Expected: false"); + EXPECT_FALSE(ResultIsEvenNoExplanation(3)); + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(ResultIsEvenNoExplanation(2)), + "Value of: ResultIsEvenNoExplanation(2)\n" + " Actual: true\n" + "Expected: false"); +} + +#ifdef __BORLANDC__ +// Restores warnings after previous "#pragma option push" supressed them +# pragma option pop +#endif + +// Tests EXPECT_EQ. +TEST(ExpectTest, EXPECT_EQ) { + EXPECT_EQ(5, 2 + 3); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(5, 2*3), + "Value of: 2*3\n" + " Actual: 6\n" + "Expected: 5"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(5, 2 - 3), + "2 - 3"); +} + +// Tests using EXPECT_EQ on double values. The purpose is to make +// sure that the specialization we did for integer and anonymous enums +// isn't used for double arguments. +TEST(ExpectTest, EXPECT_EQ_Double) { + // A success. + EXPECT_EQ(5.6, 5.6); + + // A failure. + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(5.1, 5.2), + "5.1"); +} + +#if GTEST_CAN_COMPARE_NULL +// Tests EXPECT_EQ(NULL, pointer). +TEST(ExpectTest, EXPECT_EQ_NULL) { + // A success. + const char* p = NULL; + // Some older GCC versions may issue a spurious warning in this or the next + // assertion statement. This warning should not be suppressed with + // static_cast since the test verifies the ability to use bare NULL as the + // expected parameter to the macro. + EXPECT_EQ(NULL, p); + + // A failure. + int n = 0; + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(NULL, &n), + "Value of: &n\n"); +} +#endif // GTEST_CAN_COMPARE_NULL + +// Tests EXPECT_EQ(0, non_pointer). Since the literal 0 can be +// treated as a null pointer by the compiler, we need to make sure +// that EXPECT_EQ(0, non_pointer) isn't interpreted by Google Test as +// EXPECT_EQ(static_cast(NULL), non_pointer). +TEST(ExpectTest, EXPECT_EQ_0) { + int n = 0; + + // A success. + EXPECT_EQ(0, n); + + // A failure. + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(0, 5.6), + "Expected: 0"); +} + +// Tests EXPECT_NE. +TEST(ExpectTest, EXPECT_NE) { + EXPECT_NE(6, 7); + + EXPECT_NONFATAL_FAILURE(EXPECT_NE('a', 'a'), + "Expected: ('a') != ('a'), " + "actual: 'a' (97, 0x61) vs 'a' (97, 0x61)"); + EXPECT_NONFATAL_FAILURE(EXPECT_NE(2, 2), + "2"); + char* const p0 = NULL; + EXPECT_NONFATAL_FAILURE(EXPECT_NE(p0, p0), + "p0"); + // Only way to get the Nokia compiler to compile the cast + // is to have a separate void* variable first. Putting + // the two casts on the same line doesn't work, neither does + // a direct C-style to char*. + void* pv1 = (void*)0x1234; // NOLINT + char* const p1 = reinterpret_cast(pv1); + EXPECT_NONFATAL_FAILURE(EXPECT_NE(p1, p1), + "p1"); +} + +// Tests EXPECT_LE. +TEST(ExpectTest, EXPECT_LE) { + EXPECT_LE(2, 3); + EXPECT_LE(2, 2); + EXPECT_NONFATAL_FAILURE(EXPECT_LE(2, 0), + "Expected: (2) <= (0), actual: 2 vs 0"); + EXPECT_NONFATAL_FAILURE(EXPECT_LE(1.1, 0.9), + "(1.1) <= (0.9)"); +} + +// Tests EXPECT_LT. +TEST(ExpectTest, EXPECT_LT) { + EXPECT_LT(2, 3); + EXPECT_NONFATAL_FAILURE(EXPECT_LT(2, 2), + "Expected: (2) < (2), actual: 2 vs 2"); + EXPECT_NONFATAL_FAILURE(EXPECT_LT(2, 1), + "(2) < (1)"); +} + +// Tests EXPECT_GE. +TEST(ExpectTest, EXPECT_GE) { + EXPECT_GE(2, 1); + EXPECT_GE(2, 2); + EXPECT_NONFATAL_FAILURE(EXPECT_GE(2, 3), + "Expected: (2) >= (3), actual: 2 vs 3"); + EXPECT_NONFATAL_FAILURE(EXPECT_GE(0.9, 1.1), + "(0.9) >= (1.1)"); +} + +// Tests EXPECT_GT. +TEST(ExpectTest, EXPECT_GT) { + EXPECT_GT(2, 1); + EXPECT_NONFATAL_FAILURE(EXPECT_GT(2, 2), + "Expected: (2) > (2), actual: 2 vs 2"); + EXPECT_NONFATAL_FAILURE(EXPECT_GT(2, 3), + "(2) > (3)"); +} + +#if GTEST_HAS_EXCEPTIONS + +// Tests EXPECT_THROW. +TEST(ExpectTest, EXPECT_THROW) { + EXPECT_THROW(ThrowAnInteger(), int); + EXPECT_NONFATAL_FAILURE(EXPECT_THROW(ThrowAnInteger(), bool), + "Expected: ThrowAnInteger() throws an exception of " + "type bool.\n Actual: it throws a different type."); + EXPECT_NONFATAL_FAILURE( + EXPECT_THROW(ThrowNothing(), bool), + "Expected: ThrowNothing() throws an exception of type bool.\n" + " Actual: it throws nothing."); +} + +// Tests EXPECT_NO_THROW. +TEST(ExpectTest, EXPECT_NO_THROW) { + EXPECT_NO_THROW(ThrowNothing()); + EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW(ThrowAnInteger()), + "Expected: ThrowAnInteger() doesn't throw an " + "exception.\n Actual: it throws."); +} + +// Tests EXPECT_ANY_THROW. +TEST(ExpectTest, EXPECT_ANY_THROW) { + EXPECT_ANY_THROW(ThrowAnInteger()); + EXPECT_NONFATAL_FAILURE( + EXPECT_ANY_THROW(ThrowNothing()), + "Expected: ThrowNothing() throws an exception.\n" + " Actual: it doesn't."); +} + +#endif // GTEST_HAS_EXCEPTIONS + +// Make sure we deal with the precedence of <<. +TEST(ExpectTest, ExpectPrecedence) { + EXPECT_EQ(1 < 2, true); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(true, true && false), + "Value of: true && false"); +} + + +// Tests the StreamableToString() function. + +// Tests using StreamableToString() on a scalar. +TEST(StreamableToStringTest, Scalar) { + EXPECT_STREQ("5", StreamableToString(5).c_str()); +} + +// Tests using StreamableToString() on a non-char pointer. +TEST(StreamableToStringTest, Pointer) { + int n = 0; + int* p = &n; + EXPECT_STRNE("(null)", StreamableToString(p).c_str()); +} + +// Tests using StreamableToString() on a NULL non-char pointer. +TEST(StreamableToStringTest, NullPointer) { + int* p = NULL; + EXPECT_STREQ("(null)", StreamableToString(p).c_str()); +} + +// Tests using StreamableToString() on a C string. +TEST(StreamableToStringTest, CString) { + EXPECT_STREQ("Foo", StreamableToString("Foo").c_str()); +} + +// Tests using StreamableToString() on a NULL C string. +TEST(StreamableToStringTest, NullCString) { + char* p = NULL; + EXPECT_STREQ("(null)", StreamableToString(p).c_str()); +} + +// Tests using streamable values as assertion messages. + +// Tests using std::string as an assertion message. +TEST(StreamableTest, string) { + static const std::string str( + "This failure message is a std::string, and is expected."); + EXPECT_FATAL_FAILURE(FAIL() << str, + str.c_str()); +} + +// Tests that we can output strings containing embedded NULs. +// Limited to Linux because we can only do this with std::string's. +TEST(StreamableTest, stringWithEmbeddedNUL) { + static const char char_array_with_nul[] = + "Here's a NUL\0 and some more string"; + static const std::string string_with_nul(char_array_with_nul, + sizeof(char_array_with_nul) + - 1); // drops the trailing NUL + EXPECT_FATAL_FAILURE(FAIL() << string_with_nul, + "Here's a NUL\\0 and some more string"); +} + +// Tests that we can output a NUL char. +TEST(StreamableTest, NULChar) { + EXPECT_FATAL_FAILURE({ // NOLINT + FAIL() << "A NUL" << '\0' << " and some more string"; + }, "A NUL\\0 and some more string"); +} + +// Tests using int as an assertion message. +TEST(StreamableTest, int) { + EXPECT_FATAL_FAILURE(FAIL() << 900913, + "900913"); +} + +// Tests using NULL char pointer as an assertion message. +// +// In MSVC, streaming a NULL char * causes access violation. Google Test +// implemented a workaround (substituting "(null)" for NULL). This +// tests whether the workaround works. +TEST(StreamableTest, NullCharPtr) { + EXPECT_FATAL_FAILURE(FAIL() << static_cast(NULL), + "(null)"); +} + +// Tests that basic IO manipulators (endl, ends, and flush) can be +// streamed to testing::Message. +TEST(StreamableTest, BasicIoManip) { + EXPECT_FATAL_FAILURE({ // NOLINT + FAIL() << "Line 1." << std::endl + << "A NUL char " << std::ends << std::flush << " in line 2."; + }, "Line 1.\nA NUL char \\0 in line 2."); +} + +// Tests the macros that haven't been covered so far. + +void AddFailureHelper(bool* aborted) { + *aborted = true; + ADD_FAILURE() << "Intentional failure."; + *aborted = false; +} + +// Tests ADD_FAILURE. +TEST(MacroTest, ADD_FAILURE) { + bool aborted = true; + EXPECT_NONFATAL_FAILURE(AddFailureHelper(&aborted), + "Intentional failure."); + EXPECT_FALSE(aborted); +} + +// Tests ADD_FAILURE_AT. +TEST(MacroTest, ADD_FAILURE_AT) { + // Verifies that ADD_FAILURE_AT does generate a nonfatal failure and + // the failure message contains the user-streamed part. + EXPECT_NONFATAL_FAILURE(ADD_FAILURE_AT("foo.cc", 42) << "Wrong!", "Wrong!"); + + // Verifies that the user-streamed part is optional. + EXPECT_NONFATAL_FAILURE(ADD_FAILURE_AT("foo.cc", 42), "Failed"); + + // Unfortunately, we cannot verify that the failure message contains + // the right file path and line number the same way, as + // EXPECT_NONFATAL_FAILURE() doesn't get to see the file path and + // line number. Instead, we do that in gtest_output_test_.cc. +} + +// Tests FAIL. +TEST(MacroTest, FAIL) { + EXPECT_FATAL_FAILURE(FAIL(), + "Failed"); + EXPECT_FATAL_FAILURE(FAIL() << "Intentional failure.", + "Intentional failure."); +} + +// Tests SUCCEED +TEST(MacroTest, SUCCEED) { + SUCCEED(); + SUCCEED() << "Explicit success."; +} + +// Tests for EXPECT_EQ() and ASSERT_EQ(). +// +// These tests fail *intentionally*, s.t. the failure messages can be +// generated and tested. +// +// We have different tests for different argument types. + +// Tests using bool values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, Bool) { + EXPECT_EQ(true, true); + EXPECT_FATAL_FAILURE({ + bool false_value = false; + ASSERT_EQ(false_value, true); + }, "Value of: true"); +} + +// Tests using int values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, Int) { + ASSERT_EQ(32, 32); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(32, 33), + "33"); +} + +// Tests using time_t values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, Time_T) { + EXPECT_EQ(static_cast(0), + static_cast(0)); + EXPECT_FATAL_FAILURE(ASSERT_EQ(static_cast(0), + static_cast(1234)), + "1234"); +} + +// Tests using char values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, Char) { + ASSERT_EQ('z', 'z'); + const char ch = 'b'; + EXPECT_NONFATAL_FAILURE(EXPECT_EQ('\0', ch), + "ch"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ('a', ch), + "ch"); +} + +// Tests using wchar_t values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, WideChar) { + EXPECT_EQ(L'b', L'b'); + + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(L'\0', L'x'), + "Value of: L'x'\n" + " Actual: L'x' (120, 0x78)\n" + "Expected: L'\0'\n" + "Which is: L'\0' (0, 0x0)"); + + static wchar_t wchar; + wchar = L'b'; + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(L'a', wchar), + "wchar"); + wchar = 0x8119; + EXPECT_FATAL_FAILURE(ASSERT_EQ(static_cast(0x8120), wchar), + "Value of: wchar"); +} + +// Tests using ::std::string values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, StdString) { + // Compares a const char* to an std::string that has identical + // content. + ASSERT_EQ("Test", ::std::string("Test")); + + // Compares two identical std::strings. + static const ::std::string str1("A * in the middle"); + static const ::std::string str2(str1); + EXPECT_EQ(str1, str2); + + // Compares a const char* to an std::string that has different + // content + EXPECT_NONFATAL_FAILURE(EXPECT_EQ("Test", ::std::string("test")), + "\"test\""); + + // Compares an std::string to a char* that has different content. + char* const p1 = const_cast("foo"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(::std::string("bar"), p1), + "p1"); + + // Compares two std::strings that have different contents, one of + // which having a NUL character in the middle. This should fail. + static ::std::string str3(str1); + str3.at(2) = '\0'; + EXPECT_FATAL_FAILURE(ASSERT_EQ(str1, str3), + "Value of: str3\n" + " Actual: \"A \\0 in the middle\""); +} + +#if GTEST_HAS_STD_WSTRING + +// Tests using ::std::wstring values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, StdWideString) { + // Compares two identical std::wstrings. + const ::std::wstring wstr1(L"A * in the middle"); + const ::std::wstring wstr2(wstr1); + ASSERT_EQ(wstr1, wstr2); + + // Compares an std::wstring to a const wchar_t* that has identical + // content. + const wchar_t kTestX8119[] = { 'T', 'e', 's', 't', 0x8119, '\0' }; + EXPECT_EQ(::std::wstring(kTestX8119), kTestX8119); + + // Compares an std::wstring to a const wchar_t* that has different + // content. + const wchar_t kTestX8120[] = { 'T', 'e', 's', 't', 0x8120, '\0' }; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_EQ(::std::wstring(kTestX8119), kTestX8120); + }, "kTestX8120"); + + // Compares two std::wstrings that have different contents, one of + // which having a NUL character in the middle. + ::std::wstring wstr3(wstr1); + wstr3.at(2) = L'\0'; + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(wstr1, wstr3), + "wstr3"); + + // Compares a wchar_t* to an std::wstring that has different + // content. + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_EQ(const_cast(L"foo"), ::std::wstring(L"bar")); + }, ""); +} + +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_STRING +// Tests using ::string values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, GlobalString) { + // Compares a const char* to a ::string that has identical content. + EXPECT_EQ("Test", ::string("Test")); + + // Compares two identical ::strings. + const ::string str1("A * in the middle"); + const ::string str2(str1); + ASSERT_EQ(str1, str2); + + // Compares a ::string to a const char* that has different content. + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(::string("Test"), "test"), + "test"); + + // Compares two ::strings that have different contents, one of which + // having a NUL character in the middle. + ::string str3(str1); + str3.at(2) = '\0'; + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(str1, str3), + "str3"); + + // Compares a ::string to a char* that has different content. + EXPECT_FATAL_FAILURE({ // NOLINT + ASSERT_EQ(::string("bar"), const_cast("foo")); + }, ""); +} + +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING + +// Tests using ::wstring values in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, GlobalWideString) { + // Compares two identical ::wstrings. + static const ::wstring wstr1(L"A * in the middle"); + static const ::wstring wstr2(wstr1); + EXPECT_EQ(wstr1, wstr2); + + // Compares a const wchar_t* to a ::wstring that has identical content. + const wchar_t kTestX8119[] = { 'T', 'e', 's', 't', 0x8119, '\0' }; + ASSERT_EQ(kTestX8119, ::wstring(kTestX8119)); + + // Compares a const wchar_t* to a ::wstring that has different + // content. + const wchar_t kTestX8120[] = { 'T', 'e', 's', 't', 0x8120, '\0' }; + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_EQ(kTestX8120, ::wstring(kTestX8119)); + }, "Test\\x8119"); + + // Compares a wchar_t* to a ::wstring that has different content. + wchar_t* const p1 = const_cast(L"foo"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(p1, ::wstring(L"bar")), + "bar"); + + // Compares two ::wstrings that have different contents, one of which + // having a NUL character in the middle. + static ::wstring wstr3; + wstr3 = wstr1; + wstr3.at(2) = L'\0'; + EXPECT_FATAL_FAILURE(ASSERT_EQ(wstr1, wstr3), + "wstr3"); +} + +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Tests using char pointers in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, CharPointer) { + char* const p0 = NULL; + // Only way to get the Nokia compiler to compile the cast + // is to have a separate void* variable first. Putting + // the two casts on the same line doesn't work, neither does + // a direct C-style to char*. + void* pv1 = (void*)0x1234; // NOLINT + void* pv2 = (void*)0xABC0; // NOLINT + char* const p1 = reinterpret_cast(pv1); + char* const p2 = reinterpret_cast(pv2); + ASSERT_EQ(p1, p1); + + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(p0, p2), + "Value of: p2"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(p1, p2), + "p2"); + EXPECT_FATAL_FAILURE(ASSERT_EQ(reinterpret_cast(0x1234), + reinterpret_cast(0xABC0)), + "ABC0"); +} + +// Tests using wchar_t pointers in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, WideCharPointer) { + wchar_t* const p0 = NULL; + // Only way to get the Nokia compiler to compile the cast + // is to have a separate void* variable first. Putting + // the two casts on the same line doesn't work, neither does + // a direct C-style to char*. + void* pv1 = (void*)0x1234; // NOLINT + void* pv2 = (void*)0xABC0; // NOLINT + wchar_t* const p1 = reinterpret_cast(pv1); + wchar_t* const p2 = reinterpret_cast(pv2); + EXPECT_EQ(p0, p0); + + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(p0, p2), + "Value of: p2"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(p1, p2), + "p2"); + void* pv3 = (void*)0x1234; // NOLINT + void* pv4 = (void*)0xABC0; // NOLINT + const wchar_t* p3 = reinterpret_cast(pv3); + const wchar_t* p4 = reinterpret_cast(pv4); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(p3, p4), + "p4"); +} + +// Tests using other types of pointers in {EXPECT|ASSERT}_EQ. +TEST(EqAssertionTest, OtherPointer) { + ASSERT_EQ(static_cast(NULL), + static_cast(NULL)); + EXPECT_FATAL_FAILURE(ASSERT_EQ(static_cast(NULL), + reinterpret_cast(0x1234)), + "0x1234"); +} + +// A class that supports binary comparison operators but not streaming. +class UnprintableChar { + public: + explicit UnprintableChar(char ch) : char_(ch) {} + + bool operator==(const UnprintableChar& rhs) const { + return char_ == rhs.char_; + } + bool operator!=(const UnprintableChar& rhs) const { + return char_ != rhs.char_; + } + bool operator<(const UnprintableChar& rhs) const { + return char_ < rhs.char_; + } + bool operator<=(const UnprintableChar& rhs) const { + return char_ <= rhs.char_; + } + bool operator>(const UnprintableChar& rhs) const { + return char_ > rhs.char_; + } + bool operator>=(const UnprintableChar& rhs) const { + return char_ >= rhs.char_; + } + + private: + char char_; +}; + +// Tests that ASSERT_EQ() and friends don't require the arguments to +// be printable. +TEST(ComparisonAssertionTest, AcceptsUnprintableArgs) { + const UnprintableChar x('x'), y('y'); + ASSERT_EQ(x, x); + EXPECT_NE(x, y); + ASSERT_LT(x, y); + EXPECT_LE(x, y); + ASSERT_GT(y, x); + EXPECT_GE(x, x); + + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(x, y), "1-byte object <78>"); + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(x, y), "1-byte object <79>"); + EXPECT_NONFATAL_FAILURE(EXPECT_LT(y, y), "1-byte object <79>"); + EXPECT_NONFATAL_FAILURE(EXPECT_GT(x, y), "1-byte object <78>"); + EXPECT_NONFATAL_FAILURE(EXPECT_GT(x, y), "1-byte object <79>"); + + // Code tested by EXPECT_FATAL_FAILURE cannot reference local + // variables, so we have to write UnprintableChar('x') instead of x. +#ifndef __BORLANDC__ + // ICE's in C++Builder. + EXPECT_FATAL_FAILURE(ASSERT_NE(UnprintableChar('x'), UnprintableChar('x')), + "1-byte object <78>"); + EXPECT_FATAL_FAILURE(ASSERT_LE(UnprintableChar('y'), UnprintableChar('x')), + "1-byte object <78>"); +#endif + EXPECT_FATAL_FAILURE(ASSERT_LE(UnprintableChar('y'), UnprintableChar('x')), + "1-byte object <79>"); + EXPECT_FATAL_FAILURE(ASSERT_GE(UnprintableChar('x'), UnprintableChar('y')), + "1-byte object <78>"); + EXPECT_FATAL_FAILURE(ASSERT_GE(UnprintableChar('x'), UnprintableChar('y')), + "1-byte object <79>"); +} + +// Tests the FRIEND_TEST macro. + +// This class has a private member we want to test. We will test it +// both in a TEST and in a TEST_F. +class Foo { + public: + Foo() {} + + private: + int Bar() const { return 1; } + + // Declares the friend tests that can access the private member + // Bar(). + FRIEND_TEST(FRIEND_TEST_Test, TEST); + FRIEND_TEST(FRIEND_TEST_Test2, TEST_F); +}; + +// Tests that the FRIEND_TEST declaration allows a TEST to access a +// class's private members. This should compile. +TEST(FRIEND_TEST_Test, TEST) { + ASSERT_EQ(1, Foo().Bar()); +} + +// The fixture needed to test using FRIEND_TEST with TEST_F. +class FRIEND_TEST_Test2 : public Test { + protected: + Foo foo; +}; + +// Tests that the FRIEND_TEST declaration allows a TEST_F to access a +// class's private members. This should compile. +TEST_F(FRIEND_TEST_Test2, TEST_F) { + ASSERT_EQ(1, foo.Bar()); +} + +// Tests the life cycle of Test objects. + +// The test fixture for testing the life cycle of Test objects. +// +// This class counts the number of live test objects that uses this +// fixture. +class TestLifeCycleTest : public Test { + protected: + // Constructor. Increments the number of test objects that uses + // this fixture. + TestLifeCycleTest() { count_++; } + + // Destructor. Decrements the number of test objects that uses this + // fixture. + ~TestLifeCycleTest() { count_--; } + + // Returns the number of live test objects that uses this fixture. + int count() const { return count_; } + + private: + static int count_; +}; + +int TestLifeCycleTest::count_ = 0; + +// Tests the life cycle of test objects. +TEST_F(TestLifeCycleTest, Test1) { + // There should be only one test object in this test case that's + // currently alive. + ASSERT_EQ(1, count()); +} + +// Tests the life cycle of test objects. +TEST_F(TestLifeCycleTest, Test2) { + // After Test1 is done and Test2 is started, there should still be + // only one live test object, as the object for Test1 should've been + // deleted. + ASSERT_EQ(1, count()); +} + +} // namespace + +// Tests that the copy constructor works when it is NOT optimized away by +// the compiler. +TEST(AssertionResultTest, CopyConstructorWorksWhenNotOptimied) { + // Checks that the copy constructor doesn't try to dereference NULL pointers + // in the source object. + AssertionResult r1 = AssertionSuccess(); + AssertionResult r2 = r1; + // The following line is added to prevent the compiler from optimizing + // away the constructor call. + r1 << "abc"; + + AssertionResult r3 = r1; + EXPECT_EQ(static_cast(r3), static_cast(r1)); + EXPECT_STREQ("abc", r1.message()); +} + +// Tests that AssertionSuccess and AssertionFailure construct +// AssertionResult objects as expected. +TEST(AssertionResultTest, ConstructionWorks) { + AssertionResult r1 = AssertionSuccess(); + EXPECT_TRUE(r1); + EXPECT_STREQ("", r1.message()); + + AssertionResult r2 = AssertionSuccess() << "abc"; + EXPECT_TRUE(r2); + EXPECT_STREQ("abc", r2.message()); + + AssertionResult r3 = AssertionFailure(); + EXPECT_FALSE(r3); + EXPECT_STREQ("", r3.message()); + + AssertionResult r4 = AssertionFailure() << "def"; + EXPECT_FALSE(r4); + EXPECT_STREQ("def", r4.message()); + + AssertionResult r5 = AssertionFailure(Message() << "ghi"); + EXPECT_FALSE(r5); + EXPECT_STREQ("ghi", r5.message()); +} + +// Tests that the negation flips the predicate result but keeps the message. +TEST(AssertionResultTest, NegationWorks) { + AssertionResult r1 = AssertionSuccess() << "abc"; + EXPECT_FALSE(!r1); + EXPECT_STREQ("abc", (!r1).message()); + + AssertionResult r2 = AssertionFailure() << "def"; + EXPECT_TRUE(!r2); + EXPECT_STREQ("def", (!r2).message()); +} + +TEST(AssertionResultTest, StreamingWorks) { + AssertionResult r = AssertionSuccess(); + r << "abc" << 'd' << 0 << true; + EXPECT_STREQ("abcd0true", r.message()); +} + +TEST(AssertionResultTest, CanStreamOstreamManipulators) { + AssertionResult r = AssertionSuccess(); + r << "Data" << std::endl << std::flush << std::ends << "Will be visible"; + EXPECT_STREQ("Data\n\\0Will be visible", r.message()); +} + +// Tests streaming a user type whose definition and operator << are +// both in the global namespace. +class Base { + public: + explicit Base(int an_x) : x_(an_x) {} + int x() const { return x_; } + private: + int x_; +}; +std::ostream& operator<<(std::ostream& os, + const Base& val) { + return os << val.x(); +} +std::ostream& operator<<(std::ostream& os, + const Base* pointer) { + return os << "(" << pointer->x() << ")"; +} + +TEST(MessageTest, CanStreamUserTypeInGlobalNameSpace) { + Message msg; + Base a(1); + + msg << a << &a; // Uses ::operator<<. + EXPECT_STREQ("1(1)", msg.GetString().c_str()); +} + +// Tests streaming a user type whose definition and operator<< are +// both in an unnamed namespace. +namespace { +class MyTypeInUnnamedNameSpace : public Base { + public: + explicit MyTypeInUnnamedNameSpace(int an_x): Base(an_x) {} +}; +std::ostream& operator<<(std::ostream& os, + const MyTypeInUnnamedNameSpace& val) { + return os << val.x(); +} +std::ostream& operator<<(std::ostream& os, + const MyTypeInUnnamedNameSpace* pointer) { + return os << "(" << pointer->x() << ")"; +} +} // namespace + +TEST(MessageTest, CanStreamUserTypeInUnnamedNameSpace) { + Message msg; + MyTypeInUnnamedNameSpace a(1); + + msg << a << &a; // Uses ::operator<<. + EXPECT_STREQ("1(1)", msg.GetString().c_str()); +} + +// Tests streaming a user type whose definition and operator<< are +// both in a user namespace. +namespace namespace1 { +class MyTypeInNameSpace1 : public Base { + public: + explicit MyTypeInNameSpace1(int an_x): Base(an_x) {} +}; +std::ostream& operator<<(std::ostream& os, + const MyTypeInNameSpace1& val) { + return os << val.x(); +} +std::ostream& operator<<(std::ostream& os, + const MyTypeInNameSpace1* pointer) { + return os << "(" << pointer->x() << ")"; +} +} // namespace namespace1 + +TEST(MessageTest, CanStreamUserTypeInUserNameSpace) { + Message msg; + namespace1::MyTypeInNameSpace1 a(1); + + msg << a << &a; // Uses namespace1::operator<<. + EXPECT_STREQ("1(1)", msg.GetString().c_str()); +} + +// Tests streaming a user type whose definition is in a user namespace +// but whose operator<< is in the global namespace. +namespace namespace2 { +class MyTypeInNameSpace2 : public ::Base { + public: + explicit MyTypeInNameSpace2(int an_x): Base(an_x) {} +}; +} // namespace namespace2 +std::ostream& operator<<(std::ostream& os, + const namespace2::MyTypeInNameSpace2& val) { + return os << val.x(); +} +std::ostream& operator<<(std::ostream& os, + const namespace2::MyTypeInNameSpace2* pointer) { + return os << "(" << pointer->x() << ")"; +} + +TEST(MessageTest, CanStreamUserTypeInUserNameSpaceWithStreamOperatorInGlobal) { + Message msg; + namespace2::MyTypeInNameSpace2 a(1); + + msg << a << &a; // Uses ::operator<<. + EXPECT_STREQ("1(1)", msg.GetString().c_str()); +} + +// Tests streaming NULL pointers to testing::Message. +TEST(MessageTest, NullPointers) { + Message msg; + char* const p1 = NULL; + unsigned char* const p2 = NULL; + int* p3 = NULL; + double* p4 = NULL; + bool* p5 = NULL; + Message* p6 = NULL; + + msg << p1 << p2 << p3 << p4 << p5 << p6; + ASSERT_STREQ("(null)(null)(null)(null)(null)(null)", + msg.GetString().c_str()); +} + +// Tests streaming wide strings to testing::Message. +TEST(MessageTest, WideStrings) { + // Streams a NULL of type const wchar_t*. + const wchar_t* const_wstr = NULL; + EXPECT_STREQ("(null)", + (Message() << const_wstr).GetString().c_str()); + + // Streams a NULL of type wchar_t*. + wchar_t* wstr = NULL; + EXPECT_STREQ("(null)", + (Message() << wstr).GetString().c_str()); + + // Streams a non-NULL of type const wchar_t*. + const_wstr = L"abc\x8119"; + EXPECT_STREQ("abc\xe8\x84\x99", + (Message() << const_wstr).GetString().c_str()); + + // Streams a non-NULL of type wchar_t*. + wstr = const_cast(const_wstr); + EXPECT_STREQ("abc\xe8\x84\x99", + (Message() << wstr).GetString().c_str()); +} + + +// This line tests that we can define tests in the testing namespace. +namespace testing { + +// Tests the TestInfo class. + +class TestInfoTest : public Test { + protected: + static const TestInfo* GetTestInfo(const char* test_name) { + const TestCase* const test_case = GetUnitTestImpl()-> + GetTestCase("TestInfoTest", "", NULL, NULL); + + for (int i = 0; i < test_case->total_test_count(); ++i) { + const TestInfo* const test_info = test_case->GetTestInfo(i); + if (strcmp(test_name, test_info->name()) == 0) + return test_info; + } + return NULL; + } + + static const TestResult* GetTestResult( + const TestInfo* test_info) { + return test_info->result(); + } +}; + +// Tests TestInfo::test_case_name() and TestInfo::name(). +TEST_F(TestInfoTest, Names) { + const TestInfo* const test_info = GetTestInfo("Names"); + + ASSERT_STREQ("TestInfoTest", test_info->test_case_name()); + ASSERT_STREQ("Names", test_info->name()); +} + +// Tests TestInfo::result(). +TEST_F(TestInfoTest, result) { + const TestInfo* const test_info = GetTestInfo("result"); + + // Initially, there is no TestPartResult for this test. + ASSERT_EQ(0, GetTestResult(test_info)->total_part_count()); + + // After the previous assertion, there is still none. + ASSERT_EQ(0, GetTestResult(test_info)->total_part_count()); +} + +// Tests setting up and tearing down a test case. + +class SetUpTestCaseTest : public Test { + protected: + // This will be called once before the first test in this test case + // is run. + static void SetUpTestCase() { + printf("Setting up the test case . . .\n"); + + // Initializes some shared resource. In this simple example, we + // just create a C string. More complex stuff can be done if + // desired. + shared_resource_ = "123"; + + // Increments the number of test cases that have been set up. + counter_++; + + // SetUpTestCase() should be called only once. + EXPECT_EQ(1, counter_); + } + + // This will be called once after the last test in this test case is + // run. + static void TearDownTestCase() { + printf("Tearing down the test case . . .\n"); + + // Decrements the number of test cases that have been set up. + counter_--; + + // TearDownTestCase() should be called only once. + EXPECT_EQ(0, counter_); + + // Cleans up the shared resource. + shared_resource_ = NULL; + } + + // This will be called before each test in this test case. + virtual void SetUp() { + // SetUpTestCase() should be called only once, so counter_ should + // always be 1. + EXPECT_EQ(1, counter_); + } + + // Number of test cases that have been set up. + static int counter_; + + // Some resource to be shared by all tests in this test case. + static const char* shared_resource_; +}; + +int SetUpTestCaseTest::counter_ = 0; +const char* SetUpTestCaseTest::shared_resource_ = NULL; + +// A test that uses the shared resource. +TEST_F(SetUpTestCaseTest, Test1) { + EXPECT_STRNE(NULL, shared_resource_); +} + +// Another test that uses the shared resource. +TEST_F(SetUpTestCaseTest, Test2) { + EXPECT_STREQ("123", shared_resource_); +} + +// The InitGoogleTestTest test case tests testing::InitGoogleTest(). + +// The Flags struct stores a copy of all Google Test flags. +struct Flags { + // Constructs a Flags struct where each flag has its default value. + Flags() : also_run_disabled_tests(false), + break_on_failure(false), + catch_exceptions(false), + death_test_use_fork(false), + filter(""), + list_tests(false), + output(""), + print_time(true), + random_seed(0), + repeat(1), + shuffle(false), + stack_trace_depth(kMaxStackTraceDepth), + stream_result_to(""), + throw_on_failure(false) {} + + // Factory methods. + + // Creates a Flags struct where the gtest_also_run_disabled_tests flag has + // the given value. + static Flags AlsoRunDisabledTests(bool also_run_disabled_tests) { + Flags flags; + flags.also_run_disabled_tests = also_run_disabled_tests; + return flags; + } + + // Creates a Flags struct where the gtest_break_on_failure flag has + // the given value. + static Flags BreakOnFailure(bool break_on_failure) { + Flags flags; + flags.break_on_failure = break_on_failure; + return flags; + } + + // Creates a Flags struct where the gtest_catch_exceptions flag has + // the given value. + static Flags CatchExceptions(bool catch_exceptions) { + Flags flags; + flags.catch_exceptions = catch_exceptions; + return flags; + } + + // Creates a Flags struct where the gtest_death_test_use_fork flag has + // the given value. + static Flags DeathTestUseFork(bool death_test_use_fork) { + Flags flags; + flags.death_test_use_fork = death_test_use_fork; + return flags; + } + + // Creates a Flags struct where the gtest_filter flag has the given + // value. + static Flags Filter(const char* filter) { + Flags flags; + flags.filter = filter; + return flags; + } + + // Creates a Flags struct where the gtest_list_tests flag has the + // given value. + static Flags ListTests(bool list_tests) { + Flags flags; + flags.list_tests = list_tests; + return flags; + } + + // Creates a Flags struct where the gtest_output flag has the given + // value. + static Flags Output(const char* output) { + Flags flags; + flags.output = output; + return flags; + } + + // Creates a Flags struct where the gtest_print_time flag has the given + // value. + static Flags PrintTime(bool print_time) { + Flags flags; + flags.print_time = print_time; + return flags; + } + + // Creates a Flags struct where the gtest_random_seed flag has + // the given value. + static Flags RandomSeed(Int32 random_seed) { + Flags flags; + flags.random_seed = random_seed; + return flags; + } + + // Creates a Flags struct where the gtest_repeat flag has the given + // value. + static Flags Repeat(Int32 repeat) { + Flags flags; + flags.repeat = repeat; + return flags; + } + + // Creates a Flags struct where the gtest_shuffle flag has + // the given value. + static Flags Shuffle(bool shuffle) { + Flags flags; + flags.shuffle = shuffle; + return flags; + } + + // Creates a Flags struct where the GTEST_FLAG(stack_trace_depth) flag has + // the given value. + static Flags StackTraceDepth(Int32 stack_trace_depth) { + Flags flags; + flags.stack_trace_depth = stack_trace_depth; + return flags; + } + + // Creates a Flags struct where the GTEST_FLAG(stream_result_to) flag has + // the given value. + static Flags StreamResultTo(const char* stream_result_to) { + Flags flags; + flags.stream_result_to = stream_result_to; + return flags; + } + + // Creates a Flags struct where the gtest_throw_on_failure flag has + // the given value. + static Flags ThrowOnFailure(bool throw_on_failure) { + Flags flags; + flags.throw_on_failure = throw_on_failure; + return flags; + } + + // These fields store the flag values. + bool also_run_disabled_tests; + bool break_on_failure; + bool catch_exceptions; + bool death_test_use_fork; + const char* filter; + bool list_tests; + const char* output; + bool print_time; + Int32 random_seed; + Int32 repeat; + bool shuffle; + Int32 stack_trace_depth; + const char* stream_result_to; + bool throw_on_failure; +}; + +// Fixture for testing InitGoogleTest(). +class InitGoogleTestTest : public Test { + protected: + // Clears the flags before each test. + virtual void SetUp() { + GTEST_FLAG(also_run_disabled_tests) = false; + GTEST_FLAG(break_on_failure) = false; + GTEST_FLAG(catch_exceptions) = false; + GTEST_FLAG(death_test_use_fork) = false; + GTEST_FLAG(filter) = ""; + GTEST_FLAG(list_tests) = false; + GTEST_FLAG(output) = ""; + GTEST_FLAG(print_time) = true; + GTEST_FLAG(random_seed) = 0; + GTEST_FLAG(repeat) = 1; + GTEST_FLAG(shuffle) = false; + GTEST_FLAG(stack_trace_depth) = kMaxStackTraceDepth; + GTEST_FLAG(stream_result_to) = ""; + GTEST_FLAG(throw_on_failure) = false; + } + + // Asserts that two narrow or wide string arrays are equal. + template + static void AssertStringArrayEq(size_t size1, CharType** array1, + size_t size2, CharType** array2) { + ASSERT_EQ(size1, size2) << " Array sizes different."; + + for (size_t i = 0; i != size1; i++) { + ASSERT_STREQ(array1[i], array2[i]) << " where i == " << i; + } + } + + // Verifies that the flag values match the expected values. + static void CheckFlags(const Flags& expected) { + EXPECT_EQ(expected.also_run_disabled_tests, + GTEST_FLAG(also_run_disabled_tests)); + EXPECT_EQ(expected.break_on_failure, GTEST_FLAG(break_on_failure)); + EXPECT_EQ(expected.catch_exceptions, GTEST_FLAG(catch_exceptions)); + EXPECT_EQ(expected.death_test_use_fork, GTEST_FLAG(death_test_use_fork)); + EXPECT_STREQ(expected.filter, GTEST_FLAG(filter).c_str()); + EXPECT_EQ(expected.list_tests, GTEST_FLAG(list_tests)); + EXPECT_STREQ(expected.output, GTEST_FLAG(output).c_str()); + EXPECT_EQ(expected.print_time, GTEST_FLAG(print_time)); + EXPECT_EQ(expected.random_seed, GTEST_FLAG(random_seed)); + EXPECT_EQ(expected.repeat, GTEST_FLAG(repeat)); + EXPECT_EQ(expected.shuffle, GTEST_FLAG(shuffle)); + EXPECT_EQ(expected.stack_trace_depth, GTEST_FLAG(stack_trace_depth)); + EXPECT_STREQ(expected.stream_result_to, + GTEST_FLAG(stream_result_to).c_str()); + EXPECT_EQ(expected.throw_on_failure, GTEST_FLAG(throw_on_failure)); + } + + // Parses a command line (specified by argc1 and argv1), then + // verifies that the flag values are expected and that the + // recognized flags are removed from the command line. + template + static void TestParsingFlags(int argc1, const CharType** argv1, + int argc2, const CharType** argv2, + const Flags& expected, bool should_print_help) { + const bool saved_help_flag = ::testing::internal::g_help_flag; + ::testing::internal::g_help_flag = false; + +#if GTEST_HAS_STREAM_REDIRECTION + CaptureStdout(); +#endif + + // Parses the command line. + internal::ParseGoogleTestFlagsOnly(&argc1, const_cast(argv1)); + +#if GTEST_HAS_STREAM_REDIRECTION + const std::string captured_stdout = GetCapturedStdout(); +#endif + + // Verifies the flag values. + CheckFlags(expected); + + // Verifies that the recognized flags are removed from the command + // line. + AssertStringArrayEq(argc1 + 1, argv1, argc2 + 1, argv2); + + // ParseGoogleTestFlagsOnly should neither set g_help_flag nor print the + // help message for the flags it recognizes. + EXPECT_EQ(should_print_help, ::testing::internal::g_help_flag); + +#if GTEST_HAS_STREAM_REDIRECTION + const char* const expected_help_fragment = + "This program contains tests written using"; + if (should_print_help) { + EXPECT_PRED_FORMAT2(IsSubstring, expected_help_fragment, captured_stdout); + } else { + EXPECT_PRED_FORMAT2(IsNotSubstring, + expected_help_fragment, captured_stdout); + } +#endif // GTEST_HAS_STREAM_REDIRECTION + + ::testing::internal::g_help_flag = saved_help_flag; + } + + // This macro wraps TestParsingFlags s.t. the user doesn't need + // to specify the array sizes. + +#define GTEST_TEST_PARSING_FLAGS_(argv1, argv2, expected, should_print_help) \ + TestParsingFlags(sizeof(argv1)/sizeof(*argv1) - 1, argv1, \ + sizeof(argv2)/sizeof(*argv2) - 1, argv2, \ + expected, should_print_help) +}; + +// Tests parsing an empty command line. +TEST_F(InitGoogleTestTest, Empty) { + const char* argv[] = { + NULL + }; + + const char* argv2[] = { + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags(), false); +} + +// Tests parsing a command line that has no flag. +TEST_F(InitGoogleTestTest, NoFlag) { + const char* argv[] = { + "foo.exe", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags(), false); +} + +// Tests parsing a bad --gtest_filter flag. +TEST_F(InitGoogleTestTest, FilterBad) { + const char* argv[] = { + "foo.exe", + "--gtest_filter", + NULL + }; + + const char* argv2[] = { + "foo.exe", + "--gtest_filter", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Filter(""), true); +} + +// Tests parsing an empty --gtest_filter flag. +TEST_F(InitGoogleTestTest, FilterEmpty) { + const char* argv[] = { + "foo.exe", + "--gtest_filter=", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Filter(""), false); +} + +// Tests parsing a non-empty --gtest_filter flag. +TEST_F(InitGoogleTestTest, FilterNonEmpty) { + const char* argv[] = { + "foo.exe", + "--gtest_filter=abc", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Filter("abc"), false); +} + +// Tests parsing --gtest_break_on_failure. +TEST_F(InitGoogleTestTest, BreakOnFailureWithoutValue) { + const char* argv[] = { + "foo.exe", + "--gtest_break_on_failure", + NULL +}; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::BreakOnFailure(true), false); +} + +// Tests parsing --gtest_break_on_failure=0. +TEST_F(InitGoogleTestTest, BreakOnFailureFalse_0) { + const char* argv[] = { + "foo.exe", + "--gtest_break_on_failure=0", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::BreakOnFailure(false), false); +} + +// Tests parsing --gtest_break_on_failure=f. +TEST_F(InitGoogleTestTest, BreakOnFailureFalse_f) { + const char* argv[] = { + "foo.exe", + "--gtest_break_on_failure=f", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::BreakOnFailure(false), false); +} + +// Tests parsing --gtest_break_on_failure=F. +TEST_F(InitGoogleTestTest, BreakOnFailureFalse_F) { + const char* argv[] = { + "foo.exe", + "--gtest_break_on_failure=F", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::BreakOnFailure(false), false); +} + +// Tests parsing a --gtest_break_on_failure flag that has a "true" +// definition. +TEST_F(InitGoogleTestTest, BreakOnFailureTrue) { + const char* argv[] = { + "foo.exe", + "--gtest_break_on_failure=1", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::BreakOnFailure(true), false); +} + +// Tests parsing --gtest_catch_exceptions. +TEST_F(InitGoogleTestTest, CatchExceptions) { + const char* argv[] = { + "foo.exe", + "--gtest_catch_exceptions", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::CatchExceptions(true), false); +} + +// Tests parsing --gtest_death_test_use_fork. +TEST_F(InitGoogleTestTest, DeathTestUseFork) { + const char* argv[] = { + "foo.exe", + "--gtest_death_test_use_fork", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::DeathTestUseFork(true), false); +} + +// Tests having the same flag twice with different values. The +// expected behavior is that the one coming last takes precedence. +TEST_F(InitGoogleTestTest, DuplicatedFlags) { + const char* argv[] = { + "foo.exe", + "--gtest_filter=a", + "--gtest_filter=b", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Filter("b"), false); +} + +// Tests having an unrecognized flag on the command line. +TEST_F(InitGoogleTestTest, UnrecognizedFlag) { + const char* argv[] = { + "foo.exe", + "--gtest_break_on_failure", + "bar", // Unrecognized by Google Test. + "--gtest_filter=b", + NULL + }; + + const char* argv2[] = { + "foo.exe", + "bar", + NULL + }; + + Flags flags; + flags.break_on_failure = true; + flags.filter = "b"; + GTEST_TEST_PARSING_FLAGS_(argv, argv2, flags, false); +} + +// Tests having a --gtest_list_tests flag +TEST_F(InitGoogleTestTest, ListTestsFlag) { + const char* argv[] = { + "foo.exe", + "--gtest_list_tests", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ListTests(true), false); +} + +// Tests having a --gtest_list_tests flag with a "true" value +TEST_F(InitGoogleTestTest, ListTestsTrue) { + const char* argv[] = { + "foo.exe", + "--gtest_list_tests=1", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ListTests(true), false); +} + +// Tests having a --gtest_list_tests flag with a "false" value +TEST_F(InitGoogleTestTest, ListTestsFalse) { + const char* argv[] = { + "foo.exe", + "--gtest_list_tests=0", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ListTests(false), false); +} + +// Tests parsing --gtest_list_tests=f. +TEST_F(InitGoogleTestTest, ListTestsFalse_f) { + const char* argv[] = { + "foo.exe", + "--gtest_list_tests=f", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ListTests(false), false); +} + +// Tests parsing --gtest_list_tests=F. +TEST_F(InitGoogleTestTest, ListTestsFalse_F) { + const char* argv[] = { + "foo.exe", + "--gtest_list_tests=F", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ListTests(false), false); +} + +// Tests parsing --gtest_output (invalid). +TEST_F(InitGoogleTestTest, OutputEmpty) { + const char* argv[] = { + "foo.exe", + "--gtest_output", + NULL + }; + + const char* argv2[] = { + "foo.exe", + "--gtest_output", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags(), true); +} + +// Tests parsing --gtest_output=xml +TEST_F(InitGoogleTestTest, OutputXml) { + const char* argv[] = { + "foo.exe", + "--gtest_output=xml", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Output("xml"), false); +} + +// Tests parsing --gtest_output=xml:file +TEST_F(InitGoogleTestTest, OutputXmlFile) { + const char* argv[] = { + "foo.exe", + "--gtest_output=xml:file", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Output("xml:file"), false); +} + +// Tests parsing --gtest_output=xml:directory/path/ +TEST_F(InitGoogleTestTest, OutputXmlDirectory) { + const char* argv[] = { + "foo.exe", + "--gtest_output=xml:directory/path/", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, + Flags::Output("xml:directory/path/"), false); +} + +// Tests having a --gtest_print_time flag +TEST_F(InitGoogleTestTest, PrintTimeFlag) { + const char* argv[] = { + "foo.exe", + "--gtest_print_time", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::PrintTime(true), false); +} + +// Tests having a --gtest_print_time flag with a "true" value +TEST_F(InitGoogleTestTest, PrintTimeTrue) { + const char* argv[] = { + "foo.exe", + "--gtest_print_time=1", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::PrintTime(true), false); +} + +// Tests having a --gtest_print_time flag with a "false" value +TEST_F(InitGoogleTestTest, PrintTimeFalse) { + const char* argv[] = { + "foo.exe", + "--gtest_print_time=0", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::PrintTime(false), false); +} + +// Tests parsing --gtest_print_time=f. +TEST_F(InitGoogleTestTest, PrintTimeFalse_f) { + const char* argv[] = { + "foo.exe", + "--gtest_print_time=f", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::PrintTime(false), false); +} + +// Tests parsing --gtest_print_time=F. +TEST_F(InitGoogleTestTest, PrintTimeFalse_F) { + const char* argv[] = { + "foo.exe", + "--gtest_print_time=F", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::PrintTime(false), false); +} + +// Tests parsing --gtest_random_seed=number +TEST_F(InitGoogleTestTest, RandomSeed) { + const char* argv[] = { + "foo.exe", + "--gtest_random_seed=1000", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::RandomSeed(1000), false); +} + +// Tests parsing --gtest_repeat=number +TEST_F(InitGoogleTestTest, Repeat) { + const char* argv[] = { + "foo.exe", + "--gtest_repeat=1000", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Repeat(1000), false); +} + +// Tests having a --gtest_also_run_disabled_tests flag +TEST_F(InitGoogleTestTest, AlsoRunDisabledTestsFlag) { + const char* argv[] = { + "foo.exe", + "--gtest_also_run_disabled_tests", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, + Flags::AlsoRunDisabledTests(true), false); +} + +// Tests having a --gtest_also_run_disabled_tests flag with a "true" value +TEST_F(InitGoogleTestTest, AlsoRunDisabledTestsTrue) { + const char* argv[] = { + "foo.exe", + "--gtest_also_run_disabled_tests=1", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, + Flags::AlsoRunDisabledTests(true), false); +} + +// Tests having a --gtest_also_run_disabled_tests flag with a "false" value +TEST_F(InitGoogleTestTest, AlsoRunDisabledTestsFalse) { + const char* argv[] = { + "foo.exe", + "--gtest_also_run_disabled_tests=0", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, + Flags::AlsoRunDisabledTests(false), false); +} + +// Tests parsing --gtest_shuffle. +TEST_F(InitGoogleTestTest, ShuffleWithoutValue) { + const char* argv[] = { + "foo.exe", + "--gtest_shuffle", + NULL +}; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Shuffle(true), false); +} + +// Tests parsing --gtest_shuffle=0. +TEST_F(InitGoogleTestTest, ShuffleFalse_0) { + const char* argv[] = { + "foo.exe", + "--gtest_shuffle=0", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Shuffle(false), false); +} + +// Tests parsing a --gtest_shuffle flag that has a "true" +// definition. +TEST_F(InitGoogleTestTest, ShuffleTrue) { + const char* argv[] = { + "foo.exe", + "--gtest_shuffle=1", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::Shuffle(true), false); +} + +// Tests parsing --gtest_stack_trace_depth=number. +TEST_F(InitGoogleTestTest, StackTraceDepth) { + const char* argv[] = { + "foo.exe", + "--gtest_stack_trace_depth=5", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::StackTraceDepth(5), false); +} + +TEST_F(InitGoogleTestTest, StreamResultTo) { + const char* argv[] = { + "foo.exe", + "--gtest_stream_result_to=localhost:1234", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_( + argv, argv2, Flags::StreamResultTo("localhost:1234"), false); +} + +// Tests parsing --gtest_throw_on_failure. +TEST_F(InitGoogleTestTest, ThrowOnFailureWithoutValue) { + const char* argv[] = { + "foo.exe", + "--gtest_throw_on_failure", + NULL +}; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ThrowOnFailure(true), false); +} + +// Tests parsing --gtest_throw_on_failure=0. +TEST_F(InitGoogleTestTest, ThrowOnFailureFalse_0) { + const char* argv[] = { + "foo.exe", + "--gtest_throw_on_failure=0", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ThrowOnFailure(false), false); +} + +// Tests parsing a --gtest_throw_on_failure flag that has a "true" +// definition. +TEST_F(InitGoogleTestTest, ThrowOnFailureTrue) { + const char* argv[] = { + "foo.exe", + "--gtest_throw_on_failure=1", + NULL + }; + + const char* argv2[] = { + "foo.exe", + NULL + }; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, Flags::ThrowOnFailure(true), false); +} + +#if GTEST_OS_WINDOWS +// Tests parsing wide strings. +TEST_F(InitGoogleTestTest, WideStrings) { + const wchar_t* argv[] = { + L"foo.exe", + L"--gtest_filter=Foo*", + L"--gtest_list_tests=1", + L"--gtest_break_on_failure", + L"--non_gtest_flag", + NULL + }; + + const wchar_t* argv2[] = { + L"foo.exe", + L"--non_gtest_flag", + NULL + }; + + Flags expected_flags; + expected_flags.break_on_failure = true; + expected_flags.filter = "Foo*"; + expected_flags.list_tests = true; + + GTEST_TEST_PARSING_FLAGS_(argv, argv2, expected_flags, false); +} +#endif // GTEST_OS_WINDOWS + +// Tests current_test_info() in UnitTest. +class CurrentTestInfoTest : public Test { + protected: + // Tests that current_test_info() returns NULL before the first test in + // the test case is run. + static void SetUpTestCase() { + // There should be no tests running at this point. + const TestInfo* test_info = + UnitTest::GetInstance()->current_test_info(); + EXPECT_TRUE(test_info == NULL) + << "There should be no tests running at this point."; + } + + // Tests that current_test_info() returns NULL after the last test in + // the test case has run. + static void TearDownTestCase() { + const TestInfo* test_info = + UnitTest::GetInstance()->current_test_info(); + EXPECT_TRUE(test_info == NULL) + << "There should be no tests running at this point."; + } +}; + +// Tests that current_test_info() returns TestInfo for currently running +// test by checking the expected test name against the actual one. +TEST_F(CurrentTestInfoTest, WorksForFirstTestInATestCase) { + const TestInfo* test_info = + UnitTest::GetInstance()->current_test_info(); + ASSERT_TRUE(NULL != test_info) + << "There is a test running so we should have a valid TestInfo."; + EXPECT_STREQ("CurrentTestInfoTest", test_info->test_case_name()) + << "Expected the name of the currently running test case."; + EXPECT_STREQ("WorksForFirstTestInATestCase", test_info->name()) + << "Expected the name of the currently running test."; +} + +// Tests that current_test_info() returns TestInfo for currently running +// test by checking the expected test name against the actual one. We +// use this test to see that the TestInfo object actually changed from +// the previous invocation. +TEST_F(CurrentTestInfoTest, WorksForSecondTestInATestCase) { + const TestInfo* test_info = + UnitTest::GetInstance()->current_test_info(); + ASSERT_TRUE(NULL != test_info) + << "There is a test running so we should have a valid TestInfo."; + EXPECT_STREQ("CurrentTestInfoTest", test_info->test_case_name()) + << "Expected the name of the currently running test case."; + EXPECT_STREQ("WorksForSecondTestInATestCase", test_info->name()) + << "Expected the name of the currently running test."; +} + +} // namespace testing + +// These two lines test that we can define tests in a namespace that +// has the name "testing" and is nested in another namespace. +namespace my_namespace { +namespace testing { + +// Makes sure that TEST knows to use ::testing::Test instead of +// ::my_namespace::testing::Test. +class Test {}; + +// Makes sure that an assertion knows to use ::testing::Message instead of +// ::my_namespace::testing::Message. +class Message {}; + +// Makes sure that an assertion knows to use +// ::testing::AssertionResult instead of +// ::my_namespace::testing::AssertionResult. +class AssertionResult {}; + +// Tests that an assertion that should succeed works as expected. +TEST(NestedTestingNamespaceTest, Success) { + EXPECT_EQ(1, 1) << "This shouldn't fail."; +} + +// Tests that an assertion that should fail works as expected. +TEST(NestedTestingNamespaceTest, Failure) { + EXPECT_FATAL_FAILURE(FAIL() << "This failure is expected.", + "This failure is expected."); +} + +} // namespace testing +} // namespace my_namespace + +// Tests that one can call superclass SetUp and TearDown methods-- +// that is, that they are not private. +// No tests are based on this fixture; the test "passes" if it compiles +// successfully. +class ProtectedFixtureMethodsTest : public Test { + protected: + virtual void SetUp() { + Test::SetUp(); + } + virtual void TearDown() { + Test::TearDown(); + } +}; + +// StreamingAssertionsTest tests the streaming versions of a representative +// sample of assertions. +TEST(StreamingAssertionsTest, Unconditional) { + SUCCEED() << "expected success"; + EXPECT_NONFATAL_FAILURE(ADD_FAILURE() << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(FAIL() << "expected failure", + "expected failure"); +} + +#ifdef __BORLANDC__ +// Silences warnings: "Condition is always true", "Unreachable code" +# pragma option push -w-ccc -w-rch +#endif + +TEST(StreamingAssertionsTest, Truth) { + EXPECT_TRUE(true) << "unexpected failure"; + ASSERT_TRUE(true) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(false) << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_TRUE(false) << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, Truth2) { + EXPECT_FALSE(false) << "unexpected failure"; + ASSERT_FALSE(false) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(true) << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_FALSE(true) << "expected failure", + "expected failure"); +} + +#ifdef __BORLANDC__ +// Restores warnings after previous "#pragma option push" supressed them +# pragma option pop +#endif + +TEST(StreamingAssertionsTest, IntegerEquals) { + EXPECT_EQ(1, 1) << "unexpected failure"; + ASSERT_EQ(1, 1) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_EQ(1, 2) << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_EQ(1, 2) << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, IntegerLessThan) { + EXPECT_LT(1, 2) << "unexpected failure"; + ASSERT_LT(1, 2) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_LT(2, 1) << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_LT(2, 1) << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, StringsEqual) { + EXPECT_STREQ("foo", "foo") << "unexpected failure"; + ASSERT_STREQ("foo", "foo") << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_STREQ("foo", "bar") << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_STREQ("foo", "bar") << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, StringsNotEqual) { + EXPECT_STRNE("foo", "bar") << "unexpected failure"; + ASSERT_STRNE("foo", "bar") << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_STRNE("foo", "foo") << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_STRNE("foo", "foo") << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, StringsEqualIgnoringCase) { + EXPECT_STRCASEEQ("foo", "FOO") << "unexpected failure"; + ASSERT_STRCASEEQ("foo", "FOO") << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_STRCASEEQ("foo", "bar") << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_STRCASEEQ("foo", "bar") << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, StringNotEqualIgnoringCase) { + EXPECT_STRCASENE("foo", "bar") << "unexpected failure"; + ASSERT_STRCASENE("foo", "bar") << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_STRCASENE("foo", "FOO") << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_STRCASENE("bar", "BAR") << "expected failure", + "expected failure"); +} + +TEST(StreamingAssertionsTest, FloatingPointEquals) { + EXPECT_FLOAT_EQ(1.0, 1.0) << "unexpected failure"; + ASSERT_FLOAT_EQ(1.0, 1.0) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_FLOAT_EQ(0.0, 1.0) << "expected failure", + "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_FLOAT_EQ(0.0, 1.0) << "expected failure", + "expected failure"); +} + +#if GTEST_HAS_EXCEPTIONS + +TEST(StreamingAssertionsTest, Throw) { + EXPECT_THROW(ThrowAnInteger(), int) << "unexpected failure"; + ASSERT_THROW(ThrowAnInteger(), int) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_THROW(ThrowAnInteger(), bool) << + "expected failure", "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_THROW(ThrowAnInteger(), bool) << + "expected failure", "expected failure"); +} + +TEST(StreamingAssertionsTest, NoThrow) { + EXPECT_NO_THROW(ThrowNothing()) << "unexpected failure"; + ASSERT_NO_THROW(ThrowNothing()) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_NO_THROW(ThrowAnInteger()) << + "expected failure", "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_NO_THROW(ThrowAnInteger()) << + "expected failure", "expected failure"); +} + +TEST(StreamingAssertionsTest, AnyThrow) { + EXPECT_ANY_THROW(ThrowAnInteger()) << "unexpected failure"; + ASSERT_ANY_THROW(ThrowAnInteger()) << "unexpected failure"; + EXPECT_NONFATAL_FAILURE(EXPECT_ANY_THROW(ThrowNothing()) << + "expected failure", "expected failure"); + EXPECT_FATAL_FAILURE(ASSERT_ANY_THROW(ThrowNothing()) << + "expected failure", "expected failure"); +} + +#endif // GTEST_HAS_EXCEPTIONS + +// Tests that Google Test correctly decides whether to use colors in the output. + +TEST(ColoredOutputTest, UsesColorsWhenGTestColorFlagIsYes) { + GTEST_FLAG(color) = "yes"; + + SetEnv("TERM", "xterm"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + EXPECT_TRUE(ShouldUseColor(false)); // Stdout is not a TTY. + + SetEnv("TERM", "dumb"); // TERM doesn't support colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + EXPECT_TRUE(ShouldUseColor(false)); // Stdout is not a TTY. +} + +TEST(ColoredOutputTest, UsesColorsWhenGTestColorFlagIsAliasOfYes) { + SetEnv("TERM", "dumb"); // TERM doesn't support colors. + + GTEST_FLAG(color) = "True"; + EXPECT_TRUE(ShouldUseColor(false)); // Stdout is not a TTY. + + GTEST_FLAG(color) = "t"; + EXPECT_TRUE(ShouldUseColor(false)); // Stdout is not a TTY. + + GTEST_FLAG(color) = "1"; + EXPECT_TRUE(ShouldUseColor(false)); // Stdout is not a TTY. +} + +TEST(ColoredOutputTest, UsesNoColorWhenGTestColorFlagIsNo) { + GTEST_FLAG(color) = "no"; + + SetEnv("TERM", "xterm"); // TERM supports colors. + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + EXPECT_FALSE(ShouldUseColor(false)); // Stdout is not a TTY. + + SetEnv("TERM", "dumb"); // TERM doesn't support colors. + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + EXPECT_FALSE(ShouldUseColor(false)); // Stdout is not a TTY. +} + +TEST(ColoredOutputTest, UsesNoColorWhenGTestColorFlagIsInvalid) { + SetEnv("TERM", "xterm"); // TERM supports colors. + + GTEST_FLAG(color) = "F"; + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + + GTEST_FLAG(color) = "0"; + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + + GTEST_FLAG(color) = "unknown"; + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. +} + +TEST(ColoredOutputTest, UsesColorsWhenStdoutIsTty) { + GTEST_FLAG(color) = "auto"; + + SetEnv("TERM", "xterm"); // TERM supports colors. + EXPECT_FALSE(ShouldUseColor(false)); // Stdout is not a TTY. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. +} + +TEST(ColoredOutputTest, UsesColorsWhenTermSupportsColors) { + GTEST_FLAG(color) = "auto"; + +#if GTEST_OS_WINDOWS + // On Windows, we ignore the TERM variable as it's usually not set. + + SetEnv("TERM", "dumb"); + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", ""); + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "xterm"); + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. +#else + // On non-Windows platforms, we rely on TERM to determine if the + // terminal supports colors. + + SetEnv("TERM", "dumb"); // TERM doesn't support colors. + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "emacs"); // TERM doesn't support colors. + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "vt100"); // TERM doesn't support colors. + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "xterm-mono"); // TERM doesn't support colors. + EXPECT_FALSE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "xterm"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "xterm-color"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "xterm-256color"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "screen"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "screen-256color"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "linux"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. + + SetEnv("TERM", "cygwin"); // TERM supports colors. + EXPECT_TRUE(ShouldUseColor(true)); // Stdout is a TTY. +#endif // GTEST_OS_WINDOWS +} + +// Verifies that StaticAssertTypeEq works in a namespace scope. + +static bool dummy1 GTEST_ATTRIBUTE_UNUSED_ = StaticAssertTypeEq(); +static bool dummy2 GTEST_ATTRIBUTE_UNUSED_ = + StaticAssertTypeEq(); + +// Verifies that StaticAssertTypeEq works in a class. + +template +class StaticAssertTypeEqTestHelper { + public: + StaticAssertTypeEqTestHelper() { StaticAssertTypeEq(); } +}; + +TEST(StaticAssertTypeEqTest, WorksInClass) { + StaticAssertTypeEqTestHelper(); +} + +// Verifies that StaticAssertTypeEq works inside a function. + +typedef int IntAlias; + +TEST(StaticAssertTypeEqTest, CompilesForEqualTypes) { + StaticAssertTypeEq(); + StaticAssertTypeEq(); +} + +TEST(GetCurrentOsStackTraceExceptTopTest, ReturnsTheStackTrace) { + testing::UnitTest* const unit_test = testing::UnitTest::GetInstance(); + + // We don't have a stack walker in Google Test yet. + EXPECT_STREQ("", GetCurrentOsStackTraceExceptTop(unit_test, 0).c_str()); + EXPECT_STREQ("", GetCurrentOsStackTraceExceptTop(unit_test, 1).c_str()); +} + +TEST(HasNonfatalFailureTest, ReturnsFalseWhenThereIsNoFailure) { + EXPECT_FALSE(HasNonfatalFailure()); +} + +static void FailFatally() { FAIL(); } + +TEST(HasNonfatalFailureTest, ReturnsFalseWhenThereIsOnlyFatalFailure) { + FailFatally(); + const bool has_nonfatal_failure = HasNonfatalFailure(); + ClearCurrentTestPartResults(); + EXPECT_FALSE(has_nonfatal_failure); +} + +TEST(HasNonfatalFailureTest, ReturnsTrueWhenThereIsNonfatalFailure) { + ADD_FAILURE(); + const bool has_nonfatal_failure = HasNonfatalFailure(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_nonfatal_failure); +} + +TEST(HasNonfatalFailureTest, ReturnsTrueWhenThereAreFatalAndNonfatalFailures) { + FailFatally(); + ADD_FAILURE(); + const bool has_nonfatal_failure = HasNonfatalFailure(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_nonfatal_failure); +} + +// A wrapper for calling HasNonfatalFailure outside of a test body. +static bool HasNonfatalFailureHelper() { + return testing::Test::HasNonfatalFailure(); +} + +TEST(HasNonfatalFailureTest, WorksOutsideOfTestBody) { + EXPECT_FALSE(HasNonfatalFailureHelper()); +} + +TEST(HasNonfatalFailureTest, WorksOutsideOfTestBody2) { + ADD_FAILURE(); + const bool has_nonfatal_failure = HasNonfatalFailureHelper(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_nonfatal_failure); +} + +TEST(HasFailureTest, ReturnsFalseWhenThereIsNoFailure) { + EXPECT_FALSE(HasFailure()); +} + +TEST(HasFailureTest, ReturnsTrueWhenThereIsFatalFailure) { + FailFatally(); + const bool has_failure = HasFailure(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_failure); +} + +TEST(HasFailureTest, ReturnsTrueWhenThereIsNonfatalFailure) { + ADD_FAILURE(); + const bool has_failure = HasFailure(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_failure); +} + +TEST(HasFailureTest, ReturnsTrueWhenThereAreFatalAndNonfatalFailures) { + FailFatally(); + ADD_FAILURE(); + const bool has_failure = HasFailure(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_failure); +} + +// A wrapper for calling HasFailure outside of a test body. +static bool HasFailureHelper() { return testing::Test::HasFailure(); } + +TEST(HasFailureTest, WorksOutsideOfTestBody) { + EXPECT_FALSE(HasFailureHelper()); +} + +TEST(HasFailureTest, WorksOutsideOfTestBody2) { + ADD_FAILURE(); + const bool has_failure = HasFailureHelper(); + ClearCurrentTestPartResults(); + EXPECT_TRUE(has_failure); +} + +class TestListener : public EmptyTestEventListener { + public: + TestListener() : on_start_counter_(NULL), is_destroyed_(NULL) {} + TestListener(int* on_start_counter, bool* is_destroyed) + : on_start_counter_(on_start_counter), + is_destroyed_(is_destroyed) {} + + virtual ~TestListener() { + if (is_destroyed_) + *is_destroyed_ = true; + } + + protected: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) { + if (on_start_counter_ != NULL) + (*on_start_counter_)++; + } + + private: + int* on_start_counter_; + bool* is_destroyed_; +}; + +// Tests the constructor. +TEST(TestEventListenersTest, ConstructionWorks) { + TestEventListeners listeners; + + EXPECT_TRUE(TestEventListenersAccessor::GetRepeater(&listeners) != NULL); + EXPECT_TRUE(listeners.default_result_printer() == NULL); + EXPECT_TRUE(listeners.default_xml_generator() == NULL); +} + +// Tests that the TestEventListeners destructor deletes all the listeners it +// owns. +TEST(TestEventListenersTest, DestructionWorks) { + bool default_result_printer_is_destroyed = false; + bool default_xml_printer_is_destroyed = false; + bool extra_listener_is_destroyed = false; + TestListener* default_result_printer = new TestListener( + NULL, &default_result_printer_is_destroyed); + TestListener* default_xml_printer = new TestListener( + NULL, &default_xml_printer_is_destroyed); + TestListener* extra_listener = new TestListener( + NULL, &extra_listener_is_destroyed); + + { + TestEventListeners listeners; + TestEventListenersAccessor::SetDefaultResultPrinter(&listeners, + default_result_printer); + TestEventListenersAccessor::SetDefaultXmlGenerator(&listeners, + default_xml_printer); + listeners.Append(extra_listener); + } + EXPECT_TRUE(default_result_printer_is_destroyed); + EXPECT_TRUE(default_xml_printer_is_destroyed); + EXPECT_TRUE(extra_listener_is_destroyed); +} + +// Tests that a listener Append'ed to a TestEventListeners list starts +// receiving events. +TEST(TestEventListenersTest, Append) { + int on_start_counter = 0; + bool is_destroyed = false; + TestListener* listener = new TestListener(&on_start_counter, &is_destroyed); + { + TestEventListeners listeners; + listeners.Append(listener); + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_EQ(1, on_start_counter); + } + EXPECT_TRUE(is_destroyed); +} + +// Tests that listeners receive events in the order they were appended to +// the list, except for *End requests, which must be received in the reverse +// order. +class SequenceTestingListener : public EmptyTestEventListener { + public: + SequenceTestingListener(std::vector* vector, const char* id) + : vector_(vector), id_(id) {} + + protected: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) { + vector_->push_back(GetEventDescription("OnTestProgramStart")); + } + + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) { + vector_->push_back(GetEventDescription("OnTestProgramEnd")); + } + + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) { + vector_->push_back(GetEventDescription("OnTestIterationStart")); + } + + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) { + vector_->push_back(GetEventDescription("OnTestIterationEnd")); + } + + private: + std::string GetEventDescription(const char* method) { + Message message; + message << id_ << "." << method; + return message.GetString(); + } + + std::vector* vector_; + const char* const id_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SequenceTestingListener); +}; + +TEST(EventListenerTest, AppendKeepsOrder) { + std::vector vec; + TestEventListeners listeners; + listeners.Append(new SequenceTestingListener(&vec, "1st")); + listeners.Append(new SequenceTestingListener(&vec, "2nd")); + listeners.Append(new SequenceTestingListener(&vec, "3rd")); + + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + ASSERT_EQ(3U, vec.size()); + EXPECT_STREQ("1st.OnTestProgramStart", vec[0].c_str()); + EXPECT_STREQ("2nd.OnTestProgramStart", vec[1].c_str()); + EXPECT_STREQ("3rd.OnTestProgramStart", vec[2].c_str()); + + vec.clear(); + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramEnd( + *UnitTest::GetInstance()); + ASSERT_EQ(3U, vec.size()); + EXPECT_STREQ("3rd.OnTestProgramEnd", vec[0].c_str()); + EXPECT_STREQ("2nd.OnTestProgramEnd", vec[1].c_str()); + EXPECT_STREQ("1st.OnTestProgramEnd", vec[2].c_str()); + + vec.clear(); + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestIterationStart( + *UnitTest::GetInstance(), 0); + ASSERT_EQ(3U, vec.size()); + EXPECT_STREQ("1st.OnTestIterationStart", vec[0].c_str()); + EXPECT_STREQ("2nd.OnTestIterationStart", vec[1].c_str()); + EXPECT_STREQ("3rd.OnTestIterationStart", vec[2].c_str()); + + vec.clear(); + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestIterationEnd( + *UnitTest::GetInstance(), 0); + ASSERT_EQ(3U, vec.size()); + EXPECT_STREQ("3rd.OnTestIterationEnd", vec[0].c_str()); + EXPECT_STREQ("2nd.OnTestIterationEnd", vec[1].c_str()); + EXPECT_STREQ("1st.OnTestIterationEnd", vec[2].c_str()); +} + +// Tests that a listener removed from a TestEventListeners list stops receiving +// events and is not deleted when the list is destroyed. +TEST(TestEventListenersTest, Release) { + int on_start_counter = 0; + bool is_destroyed = false; + // Although Append passes the ownership of this object to the list, + // the following calls release it, and we need to delete it before the + // test ends. + TestListener* listener = new TestListener(&on_start_counter, &is_destroyed); + { + TestEventListeners listeners; + listeners.Append(listener); + EXPECT_EQ(listener, listeners.Release(listener)); + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_TRUE(listeners.Release(listener) == NULL); + } + EXPECT_EQ(0, on_start_counter); + EXPECT_FALSE(is_destroyed); + delete listener; +} + +// Tests that no events are forwarded when event forwarding is disabled. +TEST(EventListenerTest, SuppressEventForwarding) { + int on_start_counter = 0; + TestListener* listener = new TestListener(&on_start_counter, NULL); + + TestEventListeners listeners; + listeners.Append(listener); + ASSERT_TRUE(TestEventListenersAccessor::EventForwardingEnabled(listeners)); + TestEventListenersAccessor::SuppressEventForwarding(&listeners); + ASSERT_FALSE(TestEventListenersAccessor::EventForwardingEnabled(listeners)); + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_EQ(0, on_start_counter); +} + +// Tests that events generated by Google Test are not forwarded in +// death test subprocesses. +TEST(EventListenerDeathTest, EventsNotForwardedInDeathTestSubprecesses) { + EXPECT_DEATH_IF_SUPPORTED({ + GTEST_CHECK_(TestEventListenersAccessor::EventForwardingEnabled( + *GetUnitTestImpl()->listeners())) << "expected failure";}, + "expected failure"); +} + +// Tests that a listener installed via SetDefaultResultPrinter() starts +// receiving events and is returned via default_result_printer() and that +// the previous default_result_printer is removed from the list and deleted. +TEST(EventListenerTest, default_result_printer) { + int on_start_counter = 0; + bool is_destroyed = false; + TestListener* listener = new TestListener(&on_start_counter, &is_destroyed); + + TestEventListeners listeners; + TestEventListenersAccessor::SetDefaultResultPrinter(&listeners, listener); + + EXPECT_EQ(listener, listeners.default_result_printer()); + + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + + EXPECT_EQ(1, on_start_counter); + + // Replacing default_result_printer with something else should remove it + // from the list and destroy it. + TestEventListenersAccessor::SetDefaultResultPrinter(&listeners, NULL); + + EXPECT_TRUE(listeners.default_result_printer() == NULL); + EXPECT_TRUE(is_destroyed); + + // After broadcasting an event the counter is still the same, indicating + // the listener is not in the list anymore. + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_EQ(1, on_start_counter); +} + +// Tests that the default_result_printer listener stops receiving events +// when removed via Release and that is not owned by the list anymore. +TEST(EventListenerTest, RemovingDefaultResultPrinterWorks) { + int on_start_counter = 0; + bool is_destroyed = false; + // Although Append passes the ownership of this object to the list, + // the following calls release it, and we need to delete it before the + // test ends. + TestListener* listener = new TestListener(&on_start_counter, &is_destroyed); + { + TestEventListeners listeners; + TestEventListenersAccessor::SetDefaultResultPrinter(&listeners, listener); + + EXPECT_EQ(listener, listeners.Release(listener)); + EXPECT_TRUE(listeners.default_result_printer() == NULL); + EXPECT_FALSE(is_destroyed); + + // Broadcasting events now should not affect default_result_printer. + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_EQ(0, on_start_counter); + } + // Destroying the list should not affect the listener now, too. + EXPECT_FALSE(is_destroyed); + delete listener; +} + +// Tests that a listener installed via SetDefaultXmlGenerator() starts +// receiving events and is returned via default_xml_generator() and that +// the previous default_xml_generator is removed from the list and deleted. +TEST(EventListenerTest, default_xml_generator) { + int on_start_counter = 0; + bool is_destroyed = false; + TestListener* listener = new TestListener(&on_start_counter, &is_destroyed); + + TestEventListeners listeners; + TestEventListenersAccessor::SetDefaultXmlGenerator(&listeners, listener); + + EXPECT_EQ(listener, listeners.default_xml_generator()); + + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + + EXPECT_EQ(1, on_start_counter); + + // Replacing default_xml_generator with something else should remove it + // from the list and destroy it. + TestEventListenersAccessor::SetDefaultXmlGenerator(&listeners, NULL); + + EXPECT_TRUE(listeners.default_xml_generator() == NULL); + EXPECT_TRUE(is_destroyed); + + // After broadcasting an event the counter is still the same, indicating + // the listener is not in the list anymore. + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_EQ(1, on_start_counter); +} + +// Tests that the default_xml_generator listener stops receiving events +// when removed via Release and that is not owned by the list anymore. +TEST(EventListenerTest, RemovingDefaultXmlGeneratorWorks) { + int on_start_counter = 0; + bool is_destroyed = false; + // Although Append passes the ownership of this object to the list, + // the following calls release it, and we need to delete it before the + // test ends. + TestListener* listener = new TestListener(&on_start_counter, &is_destroyed); + { + TestEventListeners listeners; + TestEventListenersAccessor::SetDefaultXmlGenerator(&listeners, listener); + + EXPECT_EQ(listener, listeners.Release(listener)); + EXPECT_TRUE(listeners.default_xml_generator() == NULL); + EXPECT_FALSE(is_destroyed); + + // Broadcasting events now should not affect default_xml_generator. + TestEventListenersAccessor::GetRepeater(&listeners)->OnTestProgramStart( + *UnitTest::GetInstance()); + EXPECT_EQ(0, on_start_counter); + } + // Destroying the list should not affect the listener now, too. + EXPECT_FALSE(is_destroyed); + delete listener; +} + +// Sanity tests to ensure that the alternative, verbose spellings of +// some of the macros work. We don't test them thoroughly as that +// would be quite involved. Since their implementations are +// straightforward, and they are rarely used, we'll just rely on the +// users to tell us when they are broken. +GTEST_TEST(AlternativeNameTest, Works) { // GTEST_TEST is the same as TEST. + GTEST_SUCCEED() << "OK"; // GTEST_SUCCEED is the same as SUCCEED. + + // GTEST_FAIL is the same as FAIL. + EXPECT_FATAL_FAILURE(GTEST_FAIL() << "An expected failure", + "An expected failure"); + + // GTEST_ASSERT_XY is the same as ASSERT_XY. + + GTEST_ASSERT_EQ(0, 0); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_EQ(0, 1) << "An expected failure", + "An expected failure"); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_EQ(1, 0) << "An expected failure", + "An expected failure"); + + GTEST_ASSERT_NE(0, 1); + GTEST_ASSERT_NE(1, 0); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_NE(0, 0) << "An expected failure", + "An expected failure"); + + GTEST_ASSERT_LE(0, 0); + GTEST_ASSERT_LE(0, 1); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_LE(1, 0) << "An expected failure", + "An expected failure"); + + GTEST_ASSERT_LT(0, 1); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_LT(0, 0) << "An expected failure", + "An expected failure"); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_LT(1, 0) << "An expected failure", + "An expected failure"); + + GTEST_ASSERT_GE(0, 0); + GTEST_ASSERT_GE(1, 0); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_GE(0, 1) << "An expected failure", + "An expected failure"); + + GTEST_ASSERT_GT(1, 0); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_GT(0, 1) << "An expected failure", + "An expected failure"); + EXPECT_FATAL_FAILURE(GTEST_ASSERT_GT(1, 1) << "An expected failure", + "An expected failure"); +} + +// Tests for internal utilities necessary for implementation of the universal +// printing. +// TODO(vladl@google.com): Find a better home for them. + +class ConversionHelperBase {}; +class ConversionHelperDerived : public ConversionHelperBase {}; + +// Tests that IsAProtocolMessage::value is a compile-time constant. +TEST(IsAProtocolMessageTest, ValueIsCompileTimeConstant) { + GTEST_COMPILE_ASSERT_(IsAProtocolMessage::value, + const_true); + GTEST_COMPILE_ASSERT_(!IsAProtocolMessage::value, const_false); +} + +// Tests that IsAProtocolMessage::value is true when T is +// proto2::Message or a sub-class of it. +TEST(IsAProtocolMessageTest, ValueIsTrueWhenTypeIsAProtocolMessage) { + EXPECT_TRUE(IsAProtocolMessage< ::proto2::Message>::value); + EXPECT_TRUE(IsAProtocolMessage::value); +} + +// Tests that IsAProtocolMessage::value is false when T is neither +// ProtocolMessage nor a sub-class of it. +TEST(IsAProtocolMessageTest, ValueIsFalseWhenTypeIsNotAProtocolMessage) { + EXPECT_FALSE(IsAProtocolMessage::value); + EXPECT_FALSE(IsAProtocolMessage::value); +} + +// Tests that CompileAssertTypesEqual compiles when the type arguments are +// equal. +TEST(CompileAssertTypesEqual, CompilesWhenTypesAreEqual) { + CompileAssertTypesEqual(); + CompileAssertTypesEqual(); +} + +// Tests that RemoveReference does not affect non-reference types. +TEST(RemoveReferenceTest, DoesNotAffectNonReferenceType) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +// Tests that RemoveReference removes reference from reference types. +TEST(RemoveReferenceTest, RemovesReference) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +// Tests GTEST_REMOVE_REFERENCE_. + +template +void TestGTestRemoveReference() { + CompileAssertTypesEqual(); +} + +TEST(RemoveReferenceTest, MacroVersion) { + TestGTestRemoveReference(); + TestGTestRemoveReference(); +} + + +// Tests that RemoveConst does not affect non-const types. +TEST(RemoveConstTest, DoesNotAffectNonConstType) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +// Tests that RemoveConst removes const from const types. +TEST(RemoveConstTest, RemovesConst) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +// Tests GTEST_REMOVE_CONST_. + +template +void TestGTestRemoveConst() { + CompileAssertTypesEqual(); +} + +TEST(RemoveConstTest, MacroVersion) { + TestGTestRemoveConst(); + TestGTestRemoveConst(); + TestGTestRemoveConst(); +} + +// Tests GTEST_REMOVE_REFERENCE_AND_CONST_. + +template +void TestGTestRemoveReferenceAndConst() { + CompileAssertTypesEqual(); +} + +TEST(RemoveReferenceToConstTest, Works) { + TestGTestRemoveReferenceAndConst(); + TestGTestRemoveReferenceAndConst(); + TestGTestRemoveReferenceAndConst(); + TestGTestRemoveReferenceAndConst(); + TestGTestRemoveReferenceAndConst(); +} + +// Tests that AddReference does not affect reference types. +TEST(AddReferenceTest, DoesNotAffectReferenceType) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +// Tests that AddReference adds reference to non-reference types. +TEST(AddReferenceTest, AddsReference) { + CompileAssertTypesEqual::type>(); + CompileAssertTypesEqual::type>(); +} + +// Tests GTEST_ADD_REFERENCE_. + +template +void TestGTestAddReference() { + CompileAssertTypesEqual(); +} + +TEST(AddReferenceTest, MacroVersion) { + TestGTestAddReference(); + TestGTestAddReference(); +} + +// Tests GTEST_REFERENCE_TO_CONST_. + +template +void TestGTestReferenceToConst() { + CompileAssertTypesEqual(); +} + +TEST(GTestReferenceToConstTest, Works) { + TestGTestReferenceToConst(); + TestGTestReferenceToConst(); + TestGTestReferenceToConst(); + TestGTestReferenceToConst(); +} + +// Tests that ImplicitlyConvertible::value is a compile-time constant. +TEST(ImplicitlyConvertibleTest, ValueIsCompileTimeConstant) { + GTEST_COMPILE_ASSERT_((ImplicitlyConvertible::value), const_true); + GTEST_COMPILE_ASSERT_((!ImplicitlyConvertible::value), + const_false); +} + +// Tests that ImplicitlyConvertible::value is true when T1 can +// be implicitly converted to T2. +TEST(ImplicitlyConvertibleTest, ValueIsTrueWhenConvertible) { + EXPECT_TRUE((ImplicitlyConvertible::value)); + EXPECT_TRUE((ImplicitlyConvertible::value)); + EXPECT_TRUE((ImplicitlyConvertible::value)); + EXPECT_TRUE((ImplicitlyConvertible::value)); + EXPECT_TRUE((ImplicitlyConvertible::value)); + EXPECT_TRUE((ImplicitlyConvertible::value)); +} + +// Tests that ImplicitlyConvertible::value is false when T1 +// cannot be implicitly converted to T2. +TEST(ImplicitlyConvertibleTest, ValueIsFalseWhenNotConvertible) { + EXPECT_FALSE((ImplicitlyConvertible::value)); + EXPECT_FALSE((ImplicitlyConvertible::value)); + EXPECT_FALSE((ImplicitlyConvertible::value)); + EXPECT_FALSE((ImplicitlyConvertible::value)); +} + +// Tests IsContainerTest. + +class NonContainer {}; + +TEST(IsContainerTestTest, WorksForNonContainer) { + EXPECT_EQ(sizeof(IsNotContainer), sizeof(IsContainerTest(0))); + EXPECT_EQ(sizeof(IsNotContainer), sizeof(IsContainerTest(0))); + EXPECT_EQ(sizeof(IsNotContainer), sizeof(IsContainerTest(0))); +} + +TEST(IsContainerTestTest, WorksForContainer) { + EXPECT_EQ(sizeof(IsContainer), + sizeof(IsContainerTest >(0))); + EXPECT_EQ(sizeof(IsContainer), + sizeof(IsContainerTest >(0))); +} + +// Tests ArrayEq(). + +TEST(ArrayEqTest, WorksForDegeneratedArrays) { + EXPECT_TRUE(ArrayEq(5, 5L)); + EXPECT_FALSE(ArrayEq('a', 0)); +} + +TEST(ArrayEqTest, WorksForOneDimensionalArrays) { + // Note that a and b are distinct but compatible types. + const int a[] = { 0, 1 }; + long b[] = { 0, 1 }; + EXPECT_TRUE(ArrayEq(a, b)); + EXPECT_TRUE(ArrayEq(a, 2, b)); + + b[0] = 2; + EXPECT_FALSE(ArrayEq(a, b)); + EXPECT_FALSE(ArrayEq(a, 1, b)); +} + +TEST(ArrayEqTest, WorksForTwoDimensionalArrays) { + const char a[][3] = { "hi", "lo" }; + const char b[][3] = { "hi", "lo" }; + const char c[][3] = { "hi", "li" }; + + EXPECT_TRUE(ArrayEq(a, b)); + EXPECT_TRUE(ArrayEq(a, 2, b)); + + EXPECT_FALSE(ArrayEq(a, c)); + EXPECT_FALSE(ArrayEq(a, 2, c)); +} + +// Tests ArrayAwareFind(). + +TEST(ArrayAwareFindTest, WorksForOneDimensionalArray) { + const char a[] = "hello"; + EXPECT_EQ(a + 4, ArrayAwareFind(a, a + 5, 'o')); + EXPECT_EQ(a + 5, ArrayAwareFind(a, a + 5, 'x')); +} + +TEST(ArrayAwareFindTest, WorksForTwoDimensionalArray) { + int a[][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 } }; + const int b[2] = { 2, 3 }; + EXPECT_EQ(a + 1, ArrayAwareFind(a, a + 3, b)); + + const int c[2] = { 6, 7 }; + EXPECT_EQ(a + 3, ArrayAwareFind(a, a + 3, c)); +} + +// Tests CopyArray(). + +TEST(CopyArrayTest, WorksForDegeneratedArrays) { + int n = 0; + CopyArray('a', &n); + EXPECT_EQ('a', n); +} + +TEST(CopyArrayTest, WorksForOneDimensionalArrays) { + const char a[3] = "hi"; + int b[3]; +#ifndef __BORLANDC__ // C++Builder cannot compile some array size deductions. + CopyArray(a, &b); + EXPECT_TRUE(ArrayEq(a, b)); +#endif + + int c[3]; + CopyArray(a, 3, c); + EXPECT_TRUE(ArrayEq(a, c)); +} + +TEST(CopyArrayTest, WorksForTwoDimensionalArrays) { + const int a[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; + int b[2][3]; +#ifndef __BORLANDC__ // C++Builder cannot compile some array size deductions. + CopyArray(a, &b); + EXPECT_TRUE(ArrayEq(a, b)); +#endif + + int c[2][3]; + CopyArray(a, 2, c); + EXPECT_TRUE(ArrayEq(a, c)); +} + +// Tests NativeArray. + +TEST(NativeArrayTest, ConstructorFromArrayWorks) { + const int a[3] = { 0, 1, 2 }; + NativeArray na(a, 3, kReference); + EXPECT_EQ(3U, na.size()); + EXPECT_EQ(a, na.begin()); +} + +TEST(NativeArrayTest, CreatesAndDeletesCopyOfArrayWhenAskedTo) { + typedef int Array[2]; + Array* a = new Array[1]; + (*a)[0] = 0; + (*a)[1] = 1; + NativeArray na(*a, 2, kCopy); + EXPECT_NE(*a, na.begin()); + delete[] a; + EXPECT_EQ(0, na.begin()[0]); + EXPECT_EQ(1, na.begin()[1]); + + // We rely on the heap checker to verify that na deletes the copy of + // array. +} + +TEST(NativeArrayTest, TypeMembersAreCorrect) { + StaticAssertTypeEq::value_type>(); + StaticAssertTypeEq::value_type>(); + + StaticAssertTypeEq::const_iterator>(); + StaticAssertTypeEq::const_iterator>(); +} + +TEST(NativeArrayTest, MethodsWork) { + const int a[3] = { 0, 1, 2 }; + NativeArray na(a, 3, kCopy); + ASSERT_EQ(3U, na.size()); + EXPECT_EQ(3, na.end() - na.begin()); + + NativeArray::const_iterator it = na.begin(); + EXPECT_EQ(0, *it); + ++it; + EXPECT_EQ(1, *it); + it++; + EXPECT_EQ(2, *it); + ++it; + EXPECT_EQ(na.end(), it); + + EXPECT_TRUE(na == na); + + NativeArray na2(a, 3, kReference); + EXPECT_TRUE(na == na2); + + const int b1[3] = { 0, 1, 1 }; + const int b2[4] = { 0, 1, 2, 3 }; + EXPECT_FALSE(na == NativeArray(b1, 3, kReference)); + EXPECT_FALSE(na == NativeArray(b2, 4, kCopy)); +} + +TEST(NativeArrayTest, WorksForTwoDimensionalArray) { + const char a[2][3] = { "hi", "lo" }; + NativeArray na(a, 2, kReference); + ASSERT_EQ(2U, na.size()); + EXPECT_EQ(a, na.begin()); +} + +// Tests SkipPrefix(). + +TEST(SkipPrefixTest, SkipsWhenPrefixMatches) { + const char* const str = "hello"; + + const char* p = str; + EXPECT_TRUE(SkipPrefix("", &p)); + EXPECT_EQ(str, p); + + p = str; + EXPECT_TRUE(SkipPrefix("hell", &p)); + EXPECT_EQ(str + 4, p); +} + +TEST(SkipPrefixTest, DoesNotSkipWhenPrefixDoesNotMatch) { + const char* const str = "world"; + + const char* p = str; + EXPECT_FALSE(SkipPrefix("W", &p)); + EXPECT_EQ(str, p); + + p = str; + EXPECT_FALSE(SkipPrefix("world!", &p)); + EXPECT_EQ(str, p); +} diff --git a/external/gtest/test/gtest_xml_outfile1_test_.cc b/external/gtest/test/gtest_xml_outfile1_test_.cc new file mode 100644 index 0000000000..531ced49d4 --- /dev/null +++ b/external/gtest/test/gtest_xml_outfile1_test_.cc @@ -0,0 +1,49 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// gtest_xml_outfile1_test_ writes some xml via TestProperty used by +// gtest_xml_outfiles_test.py + +#include "gtest/gtest.h" + +class PropertyOne : public testing::Test { + protected: + virtual void SetUp() { + RecordProperty("SetUpProp", 1); + } + virtual void TearDown() { + RecordProperty("TearDownProp", 1); + } +}; + +TEST_F(PropertyOne, TestSomeProperties) { + RecordProperty("TestSomeProperty", 1); +} diff --git a/external/gtest/test/gtest_xml_outfile2_test_.cc b/external/gtest/test/gtest_xml_outfile2_test_.cc new file mode 100644 index 0000000000..7b400b2760 --- /dev/null +++ b/external/gtest/test/gtest_xml_outfile2_test_.cc @@ -0,0 +1,49 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// gtest_xml_outfile2_test_ writes some xml via TestProperty used by +// gtest_xml_outfiles_test.py + +#include "gtest/gtest.h" + +class PropertyTwo : public testing::Test { + protected: + virtual void SetUp() { + RecordProperty("SetUpProp", 2); + } + virtual void TearDown() { + RecordProperty("TearDownProp", 2); + } +}; + +TEST_F(PropertyTwo, TestSomeProperties) { + RecordProperty("TestSomeProperty", 2); +} diff --git a/external/gtest/test/gtest_xml_outfiles_test.py b/external/gtest/test/gtest_xml_outfiles_test.py new file mode 100644 index 0000000000..524e437e6c --- /dev/null +++ b/external/gtest/test/gtest_xml_outfiles_test.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test for the gtest_xml_output module.""" + +__author__ = "keith.ray@gmail.com (Keith Ray)" + +import os +from xml.dom import minidom, Node + +import gtest_test_utils +import gtest_xml_test_utils + + +GTEST_OUTPUT_SUBDIR = "xml_outfiles" +GTEST_OUTPUT_1_TEST = "gtest_xml_outfile1_test_" +GTEST_OUTPUT_2_TEST = "gtest_xml_outfile2_test_" + +EXPECTED_XML_1 = """ + + + + + +""" + +EXPECTED_XML_2 = """ + + + + + +""" + + +class GTestXMLOutFilesTest(gtest_xml_test_utils.GTestXMLTestCase): + """Unit test for Google Test's XML output functionality.""" + + def setUp(self): + # We want the trailing '/' that the last "" provides in os.path.join, for + # telling Google Test to create an output directory instead of a single file + # for xml output. + self.output_dir_ = os.path.join(gtest_test_utils.GetTempDir(), + GTEST_OUTPUT_SUBDIR, "") + self.DeleteFilesAndDir() + + def tearDown(self): + self.DeleteFilesAndDir() + + def DeleteFilesAndDir(self): + try: + os.remove(os.path.join(self.output_dir_, GTEST_OUTPUT_1_TEST + ".xml")) + except os.error: + pass + try: + os.remove(os.path.join(self.output_dir_, GTEST_OUTPUT_2_TEST + ".xml")) + except os.error: + pass + try: + os.rmdir(self.output_dir_) + except os.error: + pass + + def testOutfile1(self): + self._TestOutFile(GTEST_OUTPUT_1_TEST, EXPECTED_XML_1) + + def testOutfile2(self): + self._TestOutFile(GTEST_OUTPUT_2_TEST, EXPECTED_XML_2) + + def _TestOutFile(self, test_name, expected_xml): + gtest_prog_path = gtest_test_utils.GetTestExecutablePath(test_name) + command = [gtest_prog_path, "--gtest_output=xml:%s" % self.output_dir_] + p = gtest_test_utils.Subprocess(command, + working_dir=gtest_test_utils.GetTempDir()) + self.assert_(p.exited) + self.assertEquals(0, p.exit_code) + + # TODO(wan@google.com): libtool causes the built test binary to be + # named lt-gtest_xml_outfiles_test_ instead of + # gtest_xml_outfiles_test_. To account for this possibillity, we + # allow both names in the following code. We should remove this + # hack when Chandler Carruth's libtool replacement tool is ready. + output_file_name1 = test_name + ".xml" + output_file1 = os.path.join(self.output_dir_, output_file_name1) + output_file_name2 = 'lt-' + output_file_name1 + output_file2 = os.path.join(self.output_dir_, output_file_name2) + self.assert_(os.path.isfile(output_file1) or os.path.isfile(output_file2), + output_file1) + + expected = minidom.parseString(expected_xml) + if os.path.isfile(output_file1): + actual = minidom.parse(output_file1) + else: + actual = minidom.parse(output_file2) + self.NormalizeXml(actual.documentElement) + self.AssertEquivalentNodes(expected.documentElement, + actual.documentElement) + expected.unlink() + actual.unlink() + + +if __name__ == "__main__": + os.environ["GTEST_STACK_TRACE_DEPTH"] = "0" + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_xml_output_unittest.py b/external/gtest/test/gtest_xml_output_unittest.py new file mode 100644 index 0000000000..f605d4ee2a --- /dev/null +++ b/external/gtest/test/gtest_xml_output_unittest.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test for the gtest_xml_output module""" + +__author__ = 'eefacm@gmail.com (Sean Mcafee)' + +import datetime +import errno +import os +import re +import sys +from xml.dom import minidom, Node + +import gtest_test_utils +import gtest_xml_test_utils + + +GTEST_FILTER_FLAG = '--gtest_filter' +GTEST_LIST_TESTS_FLAG = '--gtest_list_tests' +GTEST_OUTPUT_FLAG = "--gtest_output" +GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml" +GTEST_PROGRAM_NAME = "gtest_xml_output_unittest_" + +SUPPORTS_STACK_TRACES = False + +if SUPPORTS_STACK_TRACES: + STACK_TRACE_TEMPLATE = '\nStack trace:\n*' +else: + STACK_TRACE_TEMPLATE = '' + +EXPECTED_NON_EMPTY_XML = """ + + + + + + + + + + + + + + + + + + + + ]]>%(stack)s]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" % {'stack': STACK_TRACE_TEMPLATE} + +EXPECTED_FILTERED_TEST_XML = """ + + + + +""" + +EXPECTED_EMPTY_XML = """ + +""" + +GTEST_PROGRAM_PATH = gtest_test_utils.GetTestExecutablePath(GTEST_PROGRAM_NAME) + +SUPPORTS_TYPED_TESTS = 'TypedTest' in gtest_test_utils.Subprocess( + [GTEST_PROGRAM_PATH, GTEST_LIST_TESTS_FLAG], capture_stderr=False).output + + +class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase): + """ + Unit test for Google Test's XML output functionality. + """ + + # This test currently breaks on platforms that do not support typed and + # type-parameterized tests, so we don't run it under them. + if SUPPORTS_TYPED_TESTS: + def testNonEmptyXmlOutput(self): + """ + Runs a test program that generates a non-empty XML output, and + tests that the XML output is expected. + """ + self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_NON_EMPTY_XML, 1) + + def testEmptyXmlOutput(self): + """Verifies XML output for a Google Test binary without actual tests. + + Runs a test program that generates an empty XML output, and + tests that the XML output is expected. + """ + + self._TestXmlOutput('gtest_no_test_unittest', EXPECTED_EMPTY_XML, 0) + + def testTimestampValue(self): + """Checks whether the timestamp attribute in the XML output is valid. + + Runs a test program that generates an empty XML output, and checks if + the timestamp attribute in the testsuites tag is valid. + """ + actual = self._GetXmlOutput('gtest_no_test_unittest', [], 0) + date_time_str = actual.documentElement.getAttributeNode('timestamp').value + # datetime.strptime() is only available in Python 2.5+ so we have to + # parse the expected datetime manually. + match = re.match(r'(\d+)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)', date_time_str) + self.assertTrue( + re.match, + 'XML datettime string %s has incorrect format' % date_time_str) + date_time_from_xml = datetime.datetime( + year=int(match.group(1)), month=int(match.group(2)), + day=int(match.group(3)), hour=int(match.group(4)), + minute=int(match.group(5)), second=int(match.group(6))) + + time_delta = abs(datetime.datetime.now() - date_time_from_xml) + # timestamp value should be near the current local time + self.assertTrue(time_delta < datetime.timedelta(seconds=600), + 'time_delta is %s' % time_delta) + actual.unlink() + + def testDefaultOutputFile(self): + """ + Confirms that Google Test produces an XML output file with the expected + default name if no name is explicitly specified. + """ + output_file = os.path.join(gtest_test_utils.GetTempDir(), + GTEST_DEFAULT_OUTPUT_FILE) + gtest_prog_path = gtest_test_utils.GetTestExecutablePath( + 'gtest_no_test_unittest') + try: + os.remove(output_file) + except OSError, e: + if e.errno != errno.ENOENT: + raise + + p = gtest_test_utils.Subprocess( + [gtest_prog_path, '%s=xml' % GTEST_OUTPUT_FLAG], + working_dir=gtest_test_utils.GetTempDir()) + self.assert_(p.exited) + self.assertEquals(0, p.exit_code) + self.assert_(os.path.isfile(output_file)) + + def testSuppressedXmlOutput(self): + """ + Tests that no XML file is generated if the default XML listener is + shut down before RUN_ALL_TESTS is invoked. + """ + + xml_path = os.path.join(gtest_test_utils.GetTempDir(), + GTEST_PROGRAM_NAME + 'out.xml') + if os.path.isfile(xml_path): + os.remove(xml_path) + + command = [GTEST_PROGRAM_PATH, + '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path), + '--shut_down_xml'] + p = gtest_test_utils.Subprocess(command) + if p.terminated_by_signal: + # p.signal is avalable only if p.terminated_by_signal is True. + self.assertFalse( + p.terminated_by_signal, + '%s was killed by signal %d' % (GTEST_PROGRAM_NAME, p.signal)) + else: + self.assert_(p.exited) + self.assertEquals(1, p.exit_code, + "'%s' exited with code %s, which doesn't match " + 'the expected exit code %s.' + % (command, p.exit_code, 1)) + + self.assert_(not os.path.isfile(xml_path)) + + def testFilteredTestXmlOutput(self): + """Verifies XML output when a filter is applied. + + Runs a test program that executes only some tests and verifies that + non-selected tests do not show up in the XML output. + """ + + self._TestXmlOutput(GTEST_PROGRAM_NAME, EXPECTED_FILTERED_TEST_XML, 0, + extra_args=['%s=SuccessfulTest.*' % GTEST_FILTER_FLAG]) + + def _GetXmlOutput(self, gtest_prog_name, extra_args, expected_exit_code): + """ + Returns the xml output generated by running the program gtest_prog_name. + Furthermore, the program's exit code must be expected_exit_code. + """ + xml_path = os.path.join(gtest_test_utils.GetTempDir(), + gtest_prog_name + 'out.xml') + gtest_prog_path = gtest_test_utils.GetTestExecutablePath(gtest_prog_name) + + command = ([gtest_prog_path, '%s=xml:%s' % (GTEST_OUTPUT_FLAG, xml_path)] + + extra_args) + p = gtest_test_utils.Subprocess(command) + if p.terminated_by_signal: + self.assert_(False, + '%s was killed by signal %d' % (gtest_prog_name, p.signal)) + else: + self.assert_(p.exited) + self.assertEquals(expected_exit_code, p.exit_code, + "'%s' exited with code %s, which doesn't match " + 'the expected exit code %s.' + % (command, p.exit_code, expected_exit_code)) + actual = minidom.parse(xml_path) + return actual + + def _TestXmlOutput(self, gtest_prog_name, expected_xml, + expected_exit_code, extra_args=None): + """ + Asserts that the XML document generated by running the program + gtest_prog_name matches expected_xml, a string containing another + XML document. Furthermore, the program's exit code must be + expected_exit_code. + """ + + actual = self._GetXmlOutput(gtest_prog_name, extra_args or [], + expected_exit_code) + expected = minidom.parseString(expected_xml) + self.NormalizeXml(actual.documentElement) + self.AssertEquivalentNodes(expected.documentElement, + actual.documentElement) + expected.unlink() + actual.unlink() + + +if __name__ == '__main__': + os.environ['GTEST_STACK_TRACE_DEPTH'] = '1' + gtest_test_utils.Main() diff --git a/external/gtest/test/gtest_xml_output_unittest_.cc b/external/gtest/test/gtest_xml_output_unittest_.cc new file mode 100644 index 0000000000..48b8771b52 --- /dev/null +++ b/external/gtest/test/gtest_xml_output_unittest_.cc @@ -0,0 +1,181 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: eefacm@gmail.com (Sean Mcafee) + +// Unit test for Google Test XML output. +// +// A user can specify XML output in a Google Test program to run via +// either the GTEST_OUTPUT environment variable or the --gtest_output +// flag. This is used for testing such functionality. +// +// This program will be invoked from a Python unit test. Don't run it +// directly. + +#include "gtest/gtest.h" + +using ::testing::InitGoogleTest; +using ::testing::TestEventListeners; +using ::testing::TestWithParam; +using ::testing::UnitTest; +using ::testing::Test; +using ::testing::Values; + +class SuccessfulTest : public Test { +}; + +TEST_F(SuccessfulTest, Succeeds) { + SUCCEED() << "This is a success."; + ASSERT_EQ(1, 1); +} + +class FailedTest : public Test { +}; + +TEST_F(FailedTest, Fails) { + ASSERT_EQ(1, 2); +} + +class DisabledTest : public Test { +}; + +TEST_F(DisabledTest, DISABLED_test_not_run) { + FAIL() << "Unexpected failure: Disabled test should not be run"; +} + +TEST(MixedResultTest, Succeeds) { + EXPECT_EQ(1, 1); + ASSERT_EQ(1, 1); +} + +TEST(MixedResultTest, Fails) { + EXPECT_EQ(1, 2); + ASSERT_EQ(2, 3); +} + +TEST(MixedResultTest, DISABLED_test) { + FAIL() << "Unexpected failure: Disabled test should not be run"; +} + +TEST(XmlQuotingTest, OutputsCData) { + FAIL() << "XML output: " + ""; +} + +// Helps to test that invalid characters produced by test code do not make +// it into the XML file. +TEST(InvalidCharactersTest, InvalidCharactersInMessage) { + FAIL() << "Invalid characters in brackets [\x1\x2]"; +} + +class PropertyRecordingTest : public Test { + public: + static void SetUpTestCase() { RecordProperty("SetUpTestCase", "yes"); } + static void TearDownTestCase() { RecordProperty("TearDownTestCase", "aye"); } +}; + +TEST_F(PropertyRecordingTest, OneProperty) { + RecordProperty("key_1", "1"); +} + +TEST_F(PropertyRecordingTest, IntValuedProperty) { + RecordProperty("key_int", 1); +} + +TEST_F(PropertyRecordingTest, ThreeProperties) { + RecordProperty("key_1", "1"); + RecordProperty("key_2", "2"); + RecordProperty("key_3", "3"); +} + +TEST_F(PropertyRecordingTest, TwoValuesForOneKeyUsesLastValue) { + RecordProperty("key_1", "1"); + RecordProperty("key_1", "2"); +} + +TEST(NoFixtureTest, RecordProperty) { + RecordProperty("key", "1"); +} + +void ExternalUtilityThatCallsRecordProperty(const std::string& key, int value) { + testing::Test::RecordProperty(key, value); +} + +void ExternalUtilityThatCallsRecordProperty(const std::string& key, + const std::string& value) { + testing::Test::RecordProperty(key, value); +} + +TEST(NoFixtureTest, ExternalUtilityThatCallsRecordIntValuedProperty) { + ExternalUtilityThatCallsRecordProperty("key_for_utility_int", 1); +} + +TEST(NoFixtureTest, ExternalUtilityThatCallsRecordStringValuedProperty) { + ExternalUtilityThatCallsRecordProperty("key_for_utility_string", "1"); +} + +// Verifies that the test parameter value is output in the 'value_param' +// XML attribute for value-parameterized tests. +class ValueParamTest : public TestWithParam {}; +TEST_P(ValueParamTest, HasValueParamAttribute) {} +TEST_P(ValueParamTest, AnotherTestThatHasValueParamAttribute) {} +INSTANTIATE_TEST_CASE_P(Single, ValueParamTest, Values(33, 42)); + +#if GTEST_HAS_TYPED_TEST +// Verifies that the type parameter name is output in the 'type_param' +// XML attribute for typed tests. +template class TypedTest : public Test {}; +typedef testing::Types TypedTestTypes; +TYPED_TEST_CASE(TypedTest, TypedTestTypes); +TYPED_TEST(TypedTest, HasTypeParamAttribute) {} +#endif + +#if GTEST_HAS_TYPED_TEST_P +// Verifies that the type parameter name is output in the 'type_param' +// XML attribute for type-parameterized tests. +template class TypeParameterizedTestCase : public Test {}; +TYPED_TEST_CASE_P(TypeParameterizedTestCase); +TYPED_TEST_P(TypeParameterizedTestCase, HasTypeParamAttribute) {} +REGISTER_TYPED_TEST_CASE_P(TypeParameterizedTestCase, HasTypeParamAttribute); +typedef testing::Types TypeParameterizedTestCaseTypes; +INSTANTIATE_TYPED_TEST_CASE_P(Single, + TypeParameterizedTestCase, + TypeParameterizedTestCaseTypes); +#endif + +int main(int argc, char** argv) { + InitGoogleTest(&argc, argv); + + if (argc > 1 && strcmp(argv[1], "--shut_down_xml") == 0) { + TestEventListeners& listeners = UnitTest::GetInstance()->listeners(); + delete listeners.Release(listeners.default_xml_generator()); + } + testing::Test::RecordProperty("ad_hoc_property", "42"); + return RUN_ALL_TESTS(); +} diff --git a/external/gtest/test/gtest_xml_test_utils.py b/external/gtest/test/gtest_xml_test_utils.py new file mode 100644 index 0000000000..3d0c3b2c2d --- /dev/null +++ b/external/gtest/test/gtest_xml_test_utils.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Unit test utilities for gtest_xml_output""" + +__author__ = 'eefacm@gmail.com (Sean Mcafee)' + +import re +from xml.dom import minidom, Node + +import gtest_test_utils + + +GTEST_OUTPUT_FLAG = '--gtest_output' +GTEST_DEFAULT_OUTPUT_FILE = 'test_detail.xml' + +class GTestXMLTestCase(gtest_test_utils.TestCase): + """ + Base class for tests of Google Test's XML output functionality. + """ + + + def AssertEquivalentNodes(self, expected_node, actual_node): + """ + Asserts that actual_node (a DOM node object) is equivalent to + expected_node (another DOM node object), in that either both of + them are CDATA nodes and have the same value, or both are DOM + elements and actual_node meets all of the following conditions: + + * It has the same tag name as expected_node. + * It has the same set of attributes as expected_node, each with + the same value as the corresponding attribute of expected_node. + Exceptions are any attribute named "time", which needs only be + convertible to a floating-point number and any attribute named + "type_param" which only has to be non-empty. + * It has an equivalent set of child nodes (including elements and + CDATA sections) as expected_node. Note that we ignore the + order of the children as they are not guaranteed to be in any + particular order. + """ + + if expected_node.nodeType == Node.CDATA_SECTION_NODE: + self.assertEquals(Node.CDATA_SECTION_NODE, actual_node.nodeType) + self.assertEquals(expected_node.nodeValue, actual_node.nodeValue) + return + + self.assertEquals(Node.ELEMENT_NODE, actual_node.nodeType) + self.assertEquals(Node.ELEMENT_NODE, expected_node.nodeType) + self.assertEquals(expected_node.tagName, actual_node.tagName) + + expected_attributes = expected_node.attributes + actual_attributes = actual_node .attributes + self.assertEquals( + expected_attributes.length, actual_attributes.length, + 'attribute numbers differ in element %s:\nExpected: %r\nActual: %r' % ( + actual_node.tagName, expected_attributes.keys(), + actual_attributes.keys())) + for i in range(expected_attributes.length): + expected_attr = expected_attributes.item(i) + actual_attr = actual_attributes.get(expected_attr.name) + self.assert_( + actual_attr is not None, + 'expected attribute %s not found in element %s' % + (expected_attr.name, actual_node.tagName)) + self.assertEquals( + expected_attr.value, actual_attr.value, + ' values of attribute %s in element %s differ: %s vs %s' % + (expected_attr.name, actual_node.tagName, + expected_attr.value, actual_attr.value)) + + expected_children = self._GetChildren(expected_node) + actual_children = self._GetChildren(actual_node) + self.assertEquals( + len(expected_children), len(actual_children), + 'number of child elements differ in element ' + actual_node.tagName) + for child_id, child in expected_children.iteritems(): + self.assert_(child_id in actual_children, + '<%s> is not in <%s> (in element %s)' % + (child_id, actual_children, actual_node.tagName)) + self.AssertEquivalentNodes(child, actual_children[child_id]) + + identifying_attribute = { + 'testsuites': 'name', + 'testsuite': 'name', + 'testcase': 'name', + 'failure': 'message', + } + + def _GetChildren(self, element): + """ + Fetches all of the child nodes of element, a DOM Element object. + Returns them as the values of a dictionary keyed by the IDs of the + children. For , and elements, the ID + is the value of their "name" attribute; for elements, it is + the value of the "message" attribute; CDATA sections and non-whitespace + text nodes are concatenated into a single CDATA section with ID + "detail". An exception is raised if any element other than the above + four is encountered, if two child elements with the same identifying + attributes are encountered, or if any other type of node is encountered. + """ + + children = {} + for child in element.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + self.assert_(child.tagName in self.identifying_attribute, + 'Encountered unknown element <%s>' % child.tagName) + childID = child.getAttribute(self.identifying_attribute[child.tagName]) + self.assert_(childID not in children) + children[childID] = child + elif child.nodeType in [Node.TEXT_NODE, Node.CDATA_SECTION_NODE]: + if 'detail' not in children: + if (child.nodeType == Node.CDATA_SECTION_NODE or + not child.nodeValue.isspace()): + children['detail'] = child.ownerDocument.createCDATASection( + child.nodeValue) + else: + children['detail'].nodeValue += child.nodeValue + else: + self.fail('Encountered unexpected node type %d' % child.nodeType) + return children + + def NormalizeXml(self, element): + """ + Normalizes Google Test's XML output to eliminate references to transient + information that may change from run to run. + + * The "time" attribute of , and + elements is replaced with a single asterisk, if it contains + only digit characters. + * The "timestamp" attribute of elements is replaced with a + single asterisk, if it contains a valid ISO8601 datetime value. + * The "type_param" attribute of elements is replaced with a + single asterisk (if it sn non-empty) as it is the type name returned + by the compiler and is platform dependent. + * The line info reported in the first line of the "message" + attribute and CDATA section of elements is replaced with the + file's basename and a single asterisk for the line number. + * The directory names in file paths are removed. + * The stack traces are removed. + """ + + if element.tagName == 'testsuites': + timestamp = element.getAttributeNode('timestamp') + timestamp.value = re.sub(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d$', + '*', timestamp.value) + if element.tagName in ('testsuites', 'testsuite', 'testcase'): + time = element.getAttributeNode('time') + time.value = re.sub(r'^\d+(\.\d+)?$', '*', time.value) + type_param = element.getAttributeNode('type_param') + if type_param and type_param.value: + type_param.value = '*' + elif element.tagName == 'failure': + source_line_pat = r'^.*[/\\](.*:)\d+\n' + # Replaces the source line information with a normalized form. + message = element.getAttributeNode('message') + message.value = re.sub(source_line_pat, '\\1*\n', message.value) + for child in element.childNodes: + if child.nodeType == Node.CDATA_SECTION_NODE: + # Replaces the source line information with a normalized form. + cdata = re.sub(source_line_pat, '\\1*\n', child.nodeValue) + # Removes the actual stack trace. + child.nodeValue = re.sub(r'\nStack trace:\n(.|\n)*', + '', cdata) + for child in element.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + self.NormalizeXml(child) diff --git a/external/gtest/test/production.cc b/external/gtest/test/production.cc new file mode 100644 index 0000000000..8b8a40b44e --- /dev/null +++ b/external/gtest/test/production.cc @@ -0,0 +1,36 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This is part of the unit test for include/gtest/gtest_prod.h. + +#include "production.h" + +PrivateCode::PrivateCode() : x_(0) {} diff --git a/external/gtest/test/production.h b/external/gtest/test/production.h new file mode 100644 index 0000000000..98fd5e476c --- /dev/null +++ b/external/gtest/test/production.h @@ -0,0 +1,55 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This is part of the unit test for include/gtest/gtest_prod.h. + +#ifndef GTEST_TEST_PRODUCTION_H_ +#define GTEST_TEST_PRODUCTION_H_ + +#include "gtest/gtest_prod.h" + +class PrivateCode { + public: + // Declares a friend test that does not use a fixture. + FRIEND_TEST(PrivateCodeTest, CanAccessPrivateMembers); + + // Declares a friend test that uses a fixture. + FRIEND_TEST(PrivateCodeFixtureTest, CanAccessPrivateMembers); + + PrivateCode(); + + int x() const { return x_; } + private: + void set_x(int an_x) { x_ = an_x; } + int x_; +}; + +#endif // GTEST_TEST_PRODUCTION_H_ diff --git a/external/gtest/xcode/Config/DebugProject.xcconfig b/external/gtest/xcode/Config/DebugProject.xcconfig new file mode 100644 index 0000000000..3d68157d5d --- /dev/null +++ b/external/gtest/xcode/Config/DebugProject.xcconfig @@ -0,0 +1,30 @@ +// +// DebugProject.xcconfig +// +// These are Debug Configuration project settings for the gtest framework and +// examples. It is set in the "Based On:" dropdown in the "Project" info +// dialog. +// This file is based on the Xcode Configuration files in: +// http://code.google.com/p/google-toolbox-for-mac/ +// + +#include "General.xcconfig" + +// No optimization +GCC_OPTIMIZATION_LEVEL = 0 + +// Deployment postprocessing is what triggers Xcode to strip, turn it off +DEPLOYMENT_POSTPROCESSING = NO + +// Dead code stripping off +DEAD_CODE_STRIPPING = NO + +// Debug symbols should be on obviously +GCC_GENERATE_DEBUGGING_SYMBOLS = YES + +// Define the DEBUG macro in all debug builds +OTHER_CFLAGS = $(OTHER_CFLAGS) -DDEBUG=1 + +// These are turned off to avoid STL incompatibilities with client code +// // Turns on special C++ STL checks to "encourage" good STL use +// GCC_PREPROCESSOR_DEFINITIONS = $(GCC_PREPROCESSOR_DEFINITIONS) _GLIBCXX_DEBUG_PEDANTIC _GLIBCXX_DEBUG _GLIBCPP_CONCEPT_CHECKS diff --git a/external/gtest/xcode/Config/FrameworkTarget.xcconfig b/external/gtest/xcode/Config/FrameworkTarget.xcconfig new file mode 100644 index 0000000000..357b1c8fbf --- /dev/null +++ b/external/gtest/xcode/Config/FrameworkTarget.xcconfig @@ -0,0 +1,17 @@ +// +// FrameworkTarget.xcconfig +// +// These are Framework target settings for the gtest framework and examples. It +// is set in the "Based On:" dropdown in the "Target" info dialog. +// This file is based on the Xcode Configuration files in: +// http://code.google.com/p/google-toolbox-for-mac/ +// + +// Dynamic libs need to be position independent +GCC_DYNAMIC_NO_PIC = NO + +// Dynamic libs should not have their external symbols stripped. +STRIP_STYLE = non-global + +// Let the user install by specifying the $DSTROOT with xcodebuild +SKIP_INSTALL = NO diff --git a/external/gtest/xcode/Config/General.xcconfig b/external/gtest/xcode/Config/General.xcconfig new file mode 100644 index 0000000000..f23e322272 --- /dev/null +++ b/external/gtest/xcode/Config/General.xcconfig @@ -0,0 +1,41 @@ +// +// General.xcconfig +// +// These are General configuration settings for the gtest framework and +// examples. +// This file is based on the Xcode Configuration files in: +// http://code.google.com/p/google-toolbox-for-mac/ +// + +// Build for PPC and Intel, 32- and 64-bit +ARCHS = i386 x86_64 ppc ppc64 + +// Zerolink prevents link warnings so turn it off +ZERO_LINK = NO + +// Prebinding considered unhelpful in 10.3 and later +PREBINDING = NO + +// Strictest warning policy +WARNING_CFLAGS = -Wall -Werror -Wendif-labels -Wnewline-eof -Wno-sign-compare -Wshadow + +// Work around Xcode bugs by using external strip. See: +// http://lists.apple.com/archives/Xcode-users/2006/Feb/msg00050.html +SEPARATE_STRIP = YES + +// Force C99 dialect +GCC_C_LANGUAGE_STANDARD = c99 + +// not sure why apple defaults this on, but it's pretty risky +ALWAYS_SEARCH_USER_PATHS = NO + +// Turn on position dependent code for most cases (overridden where appropriate) +GCC_DYNAMIC_NO_PIC = YES + +// Default SDK and minimum OS version is 10.4 +SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk +MACOSX_DEPLOYMENT_TARGET = 10.4 +GCC_VERSION = 4.0 + +// VERSIONING BUILD SETTINGS (used in Info.plist) +GTEST_VERSIONINFO_ABOUT = © 2008 Google Inc. diff --git a/external/gtest/xcode/Config/ReleaseProject.xcconfig b/external/gtest/xcode/Config/ReleaseProject.xcconfig new file mode 100644 index 0000000000..5349f0a04a --- /dev/null +++ b/external/gtest/xcode/Config/ReleaseProject.xcconfig @@ -0,0 +1,32 @@ +// +// ReleaseProject.xcconfig +// +// These are Release Configuration project settings for the gtest framework +// and examples. It is set in the "Based On:" dropdown in the "Project" info +// dialog. +// This file is based on the Xcode Configuration files in: +// http://code.google.com/p/google-toolbox-for-mac/ +// + +#include "General.xcconfig" + +// subconfig/Release.xcconfig + +// Optimize for space and size (Apple recommendation) +GCC_OPTIMIZATION_LEVEL = s + +// Deploment postprocessing is what triggers Xcode to strip +DEPLOYMENT_POSTPROCESSING = YES + +// No symbols +GCC_GENERATE_DEBUGGING_SYMBOLS = NO + +// Dead code strip does not affect ObjC code but can help for C +DEAD_CODE_STRIPPING = YES + +// NDEBUG is used by things like assert.h, so define it for general compat. +// ASSERT going away in release tends to create unused vars. +OTHER_CFLAGS = $(OTHER_CFLAGS) -DNDEBUG=1 -Wno-unused-variable + +// When we strip we want to strip all symbols in release, but save externals. +STRIP_STYLE = all diff --git a/external/gtest/xcode/Config/StaticLibraryTarget.xcconfig b/external/gtest/xcode/Config/StaticLibraryTarget.xcconfig new file mode 100644 index 0000000000..3922fa51d5 --- /dev/null +++ b/external/gtest/xcode/Config/StaticLibraryTarget.xcconfig @@ -0,0 +1,18 @@ +// +// StaticLibraryTarget.xcconfig +// +// These are static library target settings for libgtest.a. It +// is set in the "Based On:" dropdown in the "Target" info dialog. +// This file is based on the Xcode Configuration files in: +// http://code.google.com/p/google-toolbox-for-mac/ +// + +// Static libs can be included in bundles so make them position independent +GCC_DYNAMIC_NO_PIC = NO + +// Static libs should not have their internal globals or external symbols +// stripped. +STRIP_STYLE = debugging + +// Let the user install by specifying the $DSTROOT with xcodebuild +SKIP_INSTALL = NO diff --git a/external/gtest/xcode/Config/TestTarget.xcconfig b/external/gtest/xcode/Config/TestTarget.xcconfig new file mode 100644 index 0000000000..e6652ba859 --- /dev/null +++ b/external/gtest/xcode/Config/TestTarget.xcconfig @@ -0,0 +1,8 @@ +// +// TestTarget.xcconfig +// +// These are Test target settings for the gtest framework and examples. It +// is set in the "Based On:" dropdown in the "Target" info dialog. + +PRODUCT_NAME = $(TARGET_NAME) +HEADER_SEARCH_PATHS = ../include diff --git a/external/gtest/xcode/Resources/Info.plist b/external/gtest/xcode/Resources/Info.plist new file mode 100644 index 0000000000..9dd28ea148 --- /dev/null +++ b/external/gtest/xcode/Resources/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.google.${PRODUCT_NAME} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + GTEST_VERSIONINFO_LONG + CFBundleShortVersionString + GTEST_VERSIONINFO_SHORT + CFBundleGetInfoString + ${PRODUCT_NAME} GTEST_VERSIONINFO_LONG, ${GTEST_VERSIONINFO_ABOUT} + NSHumanReadableCopyright + ${GTEST_VERSIONINFO_ABOUT} + CSResourcesFileMapped + + + diff --git a/external/gtest/xcode/Samples/FrameworkSample/Info.plist b/external/gtest/xcode/Samples/FrameworkSample/Info.plist new file mode 100644 index 0000000000..f3852edea2 --- /dev/null +++ b/external/gtest/xcode/Samples/FrameworkSample/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.google.gtest.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + + + diff --git a/external/gtest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj b/external/gtest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..497617eb68 --- /dev/null +++ b/external/gtest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj @@ -0,0 +1,457 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXAggregateTarget section */ + 4024D162113D7D2400C7059E /* Test */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 4024D169113D7D4600C7059E /* Build configuration list for PBXAggregateTarget "Test" */; + buildPhases = ( + 4024D161113D7D2400C7059E /* ShellScript */, + ); + dependencies = ( + 4024D166113D7D3100C7059E /* PBXTargetDependency */, + ); + name = Test; + productName = TestAndBuild; + }; + 4024D1E9113D83FF00C7059E /* TestAndBuild */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 4024D1F0113D842B00C7059E /* Build configuration list for PBXAggregateTarget "TestAndBuild" */; + buildPhases = ( + ); + dependencies = ( + 4024D1ED113D840900C7059E /* PBXTargetDependency */, + 4024D1EF113D840D00C7059E /* PBXTargetDependency */, + ); + name = TestAndBuild; + productName = TestAndBuild; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 3B7EB1250E5AEE3500C7F239 /* widget.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3B7EB1230E5AEE3500C7F239 /* widget.cc */; }; + 3B7EB1260E5AEE3500C7F239 /* widget.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B7EB1240E5AEE3500C7F239 /* widget.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3B7EB1280E5AEE4600C7F239 /* widget_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3B7EB1270E5AEE4600C7F239 /* widget_test.cc */; }; + 3B7EB1480E5AF3B400C7F239 /* Widget.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D07F2C80486CC7A007CD1D0 /* Widget.framework */; }; + 4024D188113D7D7800C7059E /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4024D185113D7D5500C7059E /* libgtest.a */; }; + 4024D189113D7D7A00C7059E /* libgtest_main.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4024D183113D7D5500C7059E /* libgtest_main.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 3B07BDF00E3F3FAE00647869 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0; + remoteInfo = gTestExample; + }; + 4024D165113D7D3100C7059E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3B07BDE90E3F3F9E00647869; + remoteInfo = WidgetFrameworkTest; + }; + 4024D1EC113D840900C7059E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0; + remoteInfo = WidgetFramework; + }; + 4024D1EE113D840D00C7059E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4024D162113D7D2400C7059E; + remoteInfo = Test; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 3B07BDEA0E3F3F9E00647869 /* WidgetFrameworkTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = WidgetFrameworkTest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B7EB1230E5AEE3500C7F239 /* widget.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = widget.cc; sourceTree = ""; }; + 3B7EB1240E5AEE3500C7F239 /* widget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = widget.h; sourceTree = ""; }; + 3B7EB1270E5AEE4600C7F239 /* widget_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = widget_test.cc; sourceTree = ""; }; + 4024D183113D7D5500C7059E /* libgtest_main.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgtest_main.a; path = /usr/local/lib/libgtest_main.a; sourceTree = ""; }; + 4024D185113D7D5500C7059E /* libgtest.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libgtest.a; path = /usr/local/lib/libgtest.a; sourceTree = ""; }; + 4024D1E2113D838200C7059E /* runtests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = runtests.sh; sourceTree = ""; }; + 8D07F2C70486CC7A007CD1D0 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 8D07F2C80486CC7A007CD1D0 /* Widget.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Widget.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3B07BDE80E3F3F9E00647869 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4024D189113D7D7A00C7059E /* libgtest_main.a in Frameworks */, + 4024D188113D7D7800C7059E /* libgtest.a in Frameworks */, + 3B7EB1480E5AF3B400C7F239 /* Widget.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D07F2C30486CC7A007CD1D0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DDFF38A45A11DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8D07F2C80486CC7A007CD1D0 /* Widget.framework */, + 3B07BDEA0E3F3F9E00647869 /* WidgetFrameworkTest */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* gTestExample */ = { + isa = PBXGroup; + children = ( + 4024D1E1113D836C00C7059E /* Scripts */, + 08FB77ACFE841707C02AAC07 /* Source */, + 089C1665FE841158C02AAC07 /* Resources */, + 3B07BE350E4094E400647869 /* Test */, + 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, + 034768DDFF38A45A11DB9C8B /* Products */, + ); + name = gTestExample; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 4024D183113D7D5500C7059E /* libgtest_main.a */, + 4024D185113D7D5500C7059E /* libgtest.a */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 089C1665FE841158C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + 8D07F2C70486CC7A007CD1D0 /* Info.plist */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77ACFE841707C02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 3B7EB1230E5AEE3500C7F239 /* widget.cc */, + 3B7EB1240E5AEE3500C7F239 /* widget.h */, + ); + name = Source; + sourceTree = ""; + }; + 3B07BE350E4094E400647869 /* Test */ = { + isa = PBXGroup; + children = ( + 3B7EB1270E5AEE4600C7F239 /* widget_test.cc */, + ); + name = Test; + sourceTree = ""; + }; + 4024D1E1113D836C00C7059E /* Scripts */ = { + isa = PBXGroup; + children = ( + 4024D1E2113D838200C7059E /* runtests.sh */, + ); + name = Scripts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8D07F2BD0486CC7A007CD1D0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7EB1260E5AEE3500C7F239 /* widget.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 3B07BDE90E3F3F9E00647869 /* WidgetFrameworkTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3B07BDF40E3F3FB600647869 /* Build configuration list for PBXNativeTarget "WidgetFrameworkTest" */; + buildPhases = ( + 3B07BDE70E3F3F9E00647869 /* Sources */, + 3B07BDE80E3F3F9E00647869 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3B07BDF10E3F3FAE00647869 /* PBXTargetDependency */, + ); + name = WidgetFrameworkTest; + productName = gTestExampleTest; + productReference = 3B07BDEA0E3F3F9E00647869 /* WidgetFrameworkTest */; + productType = "com.apple.product-type.tool"; + }; + 8D07F2BC0486CC7A007CD1D0 /* WidgetFramework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4FADC24208B4156D00ABE55E /* Build configuration list for PBXNativeTarget "WidgetFramework" */; + buildPhases = ( + 8D07F2C10486CC7A007CD1D0 /* Sources */, + 8D07F2C30486CC7A007CD1D0 /* Frameworks */, + 8D07F2BD0486CC7A007CD1D0 /* Headers */, + 8D07F2BF0486CC7A007CD1D0 /* Resources */, + 8D07F2C50486CC7A007CD1D0 /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WidgetFramework; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = gTestExample; + productReference = 8D07F2C80486CC7A007CD1D0 /* Widget.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "WidgetFramework" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 1; + mainGroup = 0867D691FE84028FC02AAC07 /* gTestExample */; + productRefGroup = 034768DDFF38A45A11DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D07F2BC0486CC7A007CD1D0 /* WidgetFramework */, + 3B07BDE90E3F3F9E00647869 /* WidgetFrameworkTest */, + 4024D162113D7D2400C7059E /* Test */, + 4024D1E9113D83FF00C7059E /* TestAndBuild */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D07F2BF0486CC7A007CD1D0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + 8D07F2C50486CC7A007CD1D0 /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4024D161113D7D2400C7059E /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/bash $SRCROOT/runtests.sh $BUILT_PRODUCTS_DIR/WidgetFrameworkTest\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3B07BDE70E3F3F9E00647869 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7EB1280E5AEE4600C7F239 /* widget_test.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D07F2C10486CC7A007CD1D0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B7EB1250E5AEE3500C7F239 /* widget.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 3B07BDF10E3F3FAE00647869 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D07F2BC0486CC7A007CD1D0 /* WidgetFramework */; + targetProxy = 3B07BDF00E3F3FAE00647869 /* PBXContainerItemProxy */; + }; + 4024D166113D7D3100C7059E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3B07BDE90E3F3F9E00647869 /* WidgetFrameworkTest */; + targetProxy = 4024D165113D7D3100C7059E /* PBXContainerItemProxy */; + }; + 4024D1ED113D840900C7059E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D07F2BC0486CC7A007CD1D0 /* WidgetFramework */; + targetProxy = 4024D1EC113D840900C7059E /* PBXContainerItemProxy */; + }; + 4024D1EF113D840D00C7059E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4024D162113D7D2400C7059E /* Test */; + targetProxy = 4024D1EE113D840D00C7059E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 3B07BDEC0E3F3F9F00647869 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = WidgetFrameworkTest; + }; + name = Debug; + }; + 3B07BDED0E3F3F9F00647869 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = WidgetFrameworkTest; + }; + name = Release; + }; + 4024D163113D7D2400C7059E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = TestAndBuild; + }; + name = Debug; + }; + 4024D164113D7D2400C7059E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = TestAndBuild; + }; + name = Release; + }; + 4024D1EA113D83FF00C7059E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = TestAndBuild; + }; + name = Debug; + }; + 4024D1EB113D83FF00C7059E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = TestAndBuild; + }; + name = Release; + }; + 4FADC24308B4156D00ABE55E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + PRODUCT_NAME = Widget; + }; + name = Debug; + }; + 4FADC24408B4156D00ABE55E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + PRODUCT_NAME = Widget; + }; + name = Release; + }; + 4FADC24708B4156D00ABE55E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = 4.0; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Debug; + }; + 4FADC24808B4156D00ABE55E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = 4.0; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3B07BDF40E3F3FB600647869 /* Build configuration list for PBXNativeTarget "WidgetFrameworkTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3B07BDEC0E3F3F9F00647869 /* Debug */, + 3B07BDED0E3F3F9F00647869 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4024D169113D7D4600C7059E /* Build configuration list for PBXAggregateTarget "Test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4024D163113D7D2400C7059E /* Debug */, + 4024D164113D7D2400C7059E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4024D1F0113D842B00C7059E /* Build configuration list for PBXAggregateTarget "TestAndBuild" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4024D1EA113D83FF00C7059E /* Debug */, + 4024D1EB113D83FF00C7059E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4FADC24208B4156D00ABE55E /* Build configuration list for PBXNativeTarget "WidgetFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4FADC24308B4156D00ABE55E /* Debug */, + 4FADC24408B4156D00ABE55E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "WidgetFramework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4FADC24708B4156D00ABE55E /* Debug */, + 4FADC24808B4156D00ABE55E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/external/gtest/xcode/Samples/FrameworkSample/runtests.sh b/external/gtest/xcode/Samples/FrameworkSample/runtests.sh new file mode 100644 index 0000000000..4a0d413e52 --- /dev/null +++ b/external/gtest/xcode/Samples/FrameworkSample/runtests.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Executes the samples and tests for the Google Test Framework. + +# Help the dynamic linker find the path to the libraries. +export DYLD_FRAMEWORK_PATH=$BUILT_PRODUCTS_DIR +export DYLD_LIBRARY_PATH=$BUILT_PRODUCTS_DIR + +# Create some executables. +test_executables=$@ + +# Now execute each one in turn keeping track of how many succeeded and failed. +succeeded=0 +failed=0 +failed_list=() +for test in ${test_executables[*]}; do + "$test" + result=$? + if [ $result -eq 0 ]; then + succeeded=$(( $succeeded + 1 )) + else + failed=$(( failed + 1 )) + failed_list="$failed_list $test" + fi +done + +# Report the successes and failures to the console. +echo "Tests complete with $succeeded successes and $failed failures." +if [ $failed -ne 0 ]; then + echo "The following tests failed:" + echo $failed_list +fi +exit $failed diff --git a/external/gtest/xcode/Samples/FrameworkSample/widget.cc b/external/gtest/xcode/Samples/FrameworkSample/widget.cc new file mode 100644 index 0000000000..bfc4e7fcfd --- /dev/null +++ b/external/gtest/xcode/Samples/FrameworkSample/widget.cc @@ -0,0 +1,63 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: preston.a.jackson@gmail.com (Preston Jackson) +// +// Google Test - FrameworkSample +// widget.cc +// + +// Widget is a very simple class used for demonstrating the use of gtest + +#include "widget.h" + +Widget::Widget(int number, const std::string& name) + : number_(number), + name_(name) {} + +Widget::~Widget() {} + +float Widget::GetFloatValue() const { + return number_; +} + +int Widget::GetIntValue() const { + return static_cast(number_); +} + +std::string Widget::GetStringValue() const { + return name_; +} + +void Widget::GetCharPtrValue(char* buffer, size_t max_size) const { + // Copy the char* representation of name_ into buffer, up to max_size. + strncpy(buffer, name_.c_str(), max_size-1); + buffer[max_size-1] = '\0'; + return; +} diff --git a/external/gtest/xcode/Samples/FrameworkSample/widget.h b/external/gtest/xcode/Samples/FrameworkSample/widget.h new file mode 100644 index 0000000000..0c55cdc8cf --- /dev/null +++ b/external/gtest/xcode/Samples/FrameworkSample/widget.h @@ -0,0 +1,59 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: preston.a.jackson@gmail.com (Preston Jackson) +// +// Google Test - FrameworkSample +// widget.h +// + +// Widget is a very simple class used for demonstrating the use of gtest. It +// simply stores two values a string and an integer, which are returned via +// public accessors in multiple forms. + +#import + +class Widget { + public: + Widget(int number, const std::string& name); + ~Widget(); + + // Public accessors to number data + float GetFloatValue() const; + int GetIntValue() const; + + // Public accessors to the string data + std::string GetStringValue() const; + void GetCharPtrValue(char* buffer, size_t max_size) const; + + private: + // Data members + float number_; + std::string name_; +}; diff --git a/external/gtest/xcode/Samples/FrameworkSample/widget_test.cc b/external/gtest/xcode/Samples/FrameworkSample/widget_test.cc new file mode 100644 index 0000000000..8725994218 --- /dev/null +++ b/external/gtest/xcode/Samples/FrameworkSample/widget_test.cc @@ -0,0 +1,68 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: preston.a.jackson@gmail.com (Preston Jackson) +// +// Google Test - FrameworkSample +// widget_test.cc +// + +// This is a simple test file for the Widget class in the Widget.framework + +#include +#include "gtest/gtest.h" + +#include + +// This test verifies that the constructor sets the internal state of the +// Widget class correctly. +TEST(WidgetInitializerTest, TestConstructor) { + Widget widget(1.0f, "name"); + EXPECT_FLOAT_EQ(1.0f, widget.GetFloatValue()); + EXPECT_EQ(std::string("name"), widget.GetStringValue()); +} + +// This test verifies the conversion of the float and string values to int and +// char*, respectively. +TEST(WidgetInitializerTest, TestConversion) { + Widget widget(1.0f, "name"); + EXPECT_EQ(1, widget.GetIntValue()); + + size_t max_size = 128; + char buffer[max_size]; + widget.GetCharPtrValue(buffer, max_size); + EXPECT_STREQ("name", buffer); +} + +// Use the Google Test main that is linked into the framework. It does something +// like this: +// int main(int argc, char** argv) { +// testing::InitGoogleTest(&argc, argv); +// return RUN_ALL_TESTS(); +// } diff --git a/external/gtest/xcode/Scripts/runtests.sh b/external/gtest/xcode/Scripts/runtests.sh new file mode 100644 index 0000000000..3fc229f1d4 --- /dev/null +++ b/external/gtest/xcode/Scripts/runtests.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Executes the samples and tests for the Google Test Framework. + +# Help the dynamic linker find the path to the libraries. +export DYLD_FRAMEWORK_PATH=$BUILT_PRODUCTS_DIR +export DYLD_LIBRARY_PATH=$BUILT_PRODUCTS_DIR + +# Create some executables. +test_executables=("$BUILT_PRODUCTS_DIR/gtest_unittest-framework" + "$BUILT_PRODUCTS_DIR/gtest_unittest" + "$BUILT_PRODUCTS_DIR/sample1_unittest-framework" + "$BUILT_PRODUCTS_DIR/sample1_unittest-static") + +# Now execute each one in turn keeping track of how many succeeded and failed. +succeeded=0 +failed=0 +failed_list=() +for test in ${test_executables[*]}; do + "$test" + result=$? + if [ $result -eq 0 ]; then + succeeded=$(( $succeeded + 1 )) + else + failed=$(( failed + 1 )) + failed_list="$failed_list $test" + fi +done + +# Report the successes and failures to the console. +echo "Tests complete with $succeeded successes and $failed failures." +if [ $failed -ne 0 ]; then + echo "The following tests failed:" + echo $failed_list +fi +exit $failed diff --git a/external/gtest/xcode/Scripts/versiongenerate.py b/external/gtest/xcode/Scripts/versiongenerate.py new file mode 100644 index 0000000000..81de8c96ac --- /dev/null +++ b/external/gtest/xcode/Scripts/versiongenerate.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# +# Copyright 2008, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""A script to prepare version informtion for use the gtest Info.plist file. + + This script extracts the version information from the configure.ac file and + uses it to generate a header file containing the same information. The + #defines in this header file will be included in during the generation of + the Info.plist of the framework, giving the correct value to the version + shown in the Finder. + + This script makes the following assumptions (these are faults of the script, + not problems with the Autoconf): + 1. The AC_INIT macro will be contained within the first 1024 characters + of configure.ac + 2. The version string will be 3 integers separated by periods and will be + surrounded by squre brackets, "[" and "]" (e.g. [1.0.1]). The first + segment represents the major version, the second represents the minor + version and the third represents the fix version. + 3. No ")" character exists between the opening "(" and closing ")" of + AC_INIT, including in comments and character strings. +""" + +import sys +import re + +# Read the command line argument (the output directory for Version.h) +if (len(sys.argv) < 3): + print "Usage: versiongenerate.py input_dir output_dir" + sys.exit(1) +else: + input_dir = sys.argv[1] + output_dir = sys.argv[2] + +# Read the first 1024 characters of the configure.ac file +config_file = open("%s/configure.ac" % input_dir, 'r') +buffer_size = 1024 +opening_string = config_file.read(buffer_size) +config_file.close() + +# Extract the version string from the AC_INIT macro +# The following init_expression means: +# Extract three integers separated by periods and surrounded by squre +# brackets(e.g. "[1.0.1]") between "AC_INIT(" and ")". Do not be greedy +# (*? is the non-greedy flag) since that would pull in everything between +# the first "(" and the last ")" in the file. +version_expression = re.compile(r"AC_INIT\(.*?\[(\d+)\.(\d+)\.(\d+)\].*?\)", + re.DOTALL) +version_values = version_expression.search(opening_string) +major_version = version_values.group(1) +minor_version = version_values.group(2) +fix_version = version_values.group(3) + +# Write the version information to a header file to be included in the +# Info.plist file. +file_data = """// +// DO NOT MODIFY THIS FILE (but you can delete it) +// +// This file is autogenerated by the versiongenerate.py script. This script +// is executed in a "Run Script" build phase when creating gtest.framework. This +// header file is not used during compilation of C-source. Rather, it simply +// defines some version strings for substitution in the Info.plist. Because of +// this, we are not not restricted to C-syntax nor are we using include guards. +// + +#define GTEST_VERSIONINFO_SHORT %s.%s +#define GTEST_VERSIONINFO_LONG %s.%s.%s + +""" % (major_version, minor_version, major_version, minor_version, fix_version) +version_file = open("%s/Version.h" % output_dir, 'w') +version_file.write(file_data) +version_file.close() diff --git a/external/gtest/xcode/gtest.xcodeproj/project.pbxproj b/external/gtest/xcode/gtest.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..0452a63d0c --- /dev/null +++ b/external/gtest/xcode/gtest.xcodeproj/project.pbxproj @@ -0,0 +1,1135 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 3B238F5F0E828B5400846E11 /* Check */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 3B238FA30E828BB600846E11 /* Build configuration list for PBXAggregateTarget "Check" */; + buildPhases = ( + 3B238F5E0E828B5400846E11 /* ShellScript */, + ); + dependencies = ( + 40899F9D0FFA740F000B29AE /* PBXTargetDependency */, + 40C849F7101A43440083642A /* PBXTargetDependency */, + 4089A0980FFAD34A000B29AE /* PBXTargetDependency */, + 40C849F9101A43490083642A /* PBXTargetDependency */, + ); + name = Check; + productName = Check; + }; + 40C44ADC0E3798F4008FCC51 /* Version Info */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 40C44AE40E379905008FCC51 /* Build configuration list for PBXAggregateTarget "Version Info" */; + buildPhases = ( + 40C44ADB0E3798F4008FCC51 /* Generate Version.h */, + ); + comments = "The generation of Version.h must be performed in its own target. Since the Info.plist is preprocessed before any of the other build phases in gtest, the Version.h file would not be ready if included as a build phase of that target."; + dependencies = ( + ); + name = "Version Info"; + productName = Version.h; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 224A12A30E9EADCC00BD17FD /* gtest-test-part.h in Headers */ = {isa = PBXBuildFile; fileRef = 224A12A20E9EADCC00BD17FD /* gtest-test-part.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3BF6F2A00E79B5AD000F2EEE /* gtest-type-util.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 3BF6F29F0E79B5AD000F2EEE /* gtest-type-util.h */; }; + 3BF6F2A50E79B616000F2EEE /* gtest-typed-test.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BF6F2A40E79B616000F2EEE /* gtest-typed-test.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 404884380E2F799B00CF7658 /* gtest-death-test.h in Headers */ = {isa = PBXBuildFile; fileRef = 404883DB0E2F799B00CF7658 /* gtest-death-test.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 404884390E2F799B00CF7658 /* gtest-message.h in Headers */ = {isa = PBXBuildFile; fileRef = 404883DC0E2F799B00CF7658 /* gtest-message.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4048843A0E2F799B00CF7658 /* gtest-spi.h in Headers */ = {isa = PBXBuildFile; fileRef = 404883DD0E2F799B00CF7658 /* gtest-spi.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4048843B0E2F799B00CF7658 /* gtest.h in Headers */ = {isa = PBXBuildFile; fileRef = 404883DE0E2F799B00CF7658 /* gtest.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4048843C0E2F799B00CF7658 /* gtest_pred_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 404883DF0E2F799B00CF7658 /* gtest_pred_impl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4048843D0E2F799B00CF7658 /* gtest_prod.h in Headers */ = {isa = PBXBuildFile; fileRef = 404883E00E2F799B00CF7658 /* gtest_prod.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 404884500E2F799B00CF7658 /* README in Resources */ = {isa = PBXBuildFile; fileRef = 404883F60E2F799B00CF7658 /* README */; }; + 404884A00E2F7BE600CF7658 /* gtest-death-test-internal.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 404883E20E2F799B00CF7658 /* gtest-death-test-internal.h */; }; + 404884A10E2F7BE600CF7658 /* gtest-filepath.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 404883E30E2F799B00CF7658 /* gtest-filepath.h */; }; + 404884A20E2F7BE600CF7658 /* gtest-internal.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 404883E40E2F799B00CF7658 /* gtest-internal.h */; }; + 404884A30E2F7BE600CF7658 /* gtest-port.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 404883E50E2F799B00CF7658 /* gtest-port.h */; }; + 404884A40E2F7BE600CF7658 /* gtest-string.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 404883E60E2F799B00CF7658 /* gtest-string.h */; }; + 404884AC0E2F7CD900CF7658 /* CHANGES in Resources */ = {isa = PBXBuildFile; fileRef = 404884A90E2F7CD900CF7658 /* CHANGES */; }; + 404884AD0E2F7CD900CF7658 /* CONTRIBUTORS in Resources */ = {isa = PBXBuildFile; fileRef = 404884AA0E2F7CD900CF7658 /* CONTRIBUTORS */; }; + 404884AE0E2F7CD900CF7658 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 404884AB0E2F7CD900CF7658 /* LICENSE */; }; + 40899F3A0FFA70D4000B29AE /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = 224A12A10E9EADA700BD17FD /* gtest-all.cc */; }; + 40899F500FFA7281000B29AE /* gtest-tuple.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 40899F4D0FFA7271000B29AE /* gtest-tuple.h */; }; + 40899F530FFA72A0000B29AE /* gtest_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3B238C120E7FE13C00846E11 /* gtest_unittest.cc */; }; + 4089A0440FFAD1BE000B29AE /* sample1.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4089A02C0FFACF7F000B29AE /* sample1.cc */; }; + 4089A0460FFAD1BE000B29AE /* sample1_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4089A02E0FFACF7F000B29AE /* sample1_unittest.cc */; }; + 40C848FF101A21150083642A /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = 224A12A10E9EADA700BD17FD /* gtest-all.cc */; }; + 40C84915101A21DF0083642A /* gtest_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4048840D0E2F799B00CF7658 /* gtest_main.cc */; }; + 40C84916101A235B0083642A /* libgtest_main.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C8490B101A217E0083642A /* libgtest_main.a */; }; + 40C84921101A23AD0083642A /* libgtest_main.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C8490B101A217E0083642A /* libgtest_main.a */; }; + 40C84978101A36540083642A /* libgtest_main.a in Resources */ = {isa = PBXBuildFile; fileRef = 40C8490B101A217E0083642A /* libgtest_main.a */; }; + 40C84980101A36850083642A /* gtest_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 3B238C120E7FE13C00846E11 /* gtest_unittest.cc */; }; + 40C84982101A36850083642A /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C848FA101A209C0083642A /* libgtest.a */; }; + 40C84983101A36850083642A /* libgtest_main.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C8490B101A217E0083642A /* libgtest_main.a */; }; + 40C8498F101A36A60083642A /* sample1.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4089A02C0FFACF7F000B29AE /* sample1.cc */; }; + 40C84990101A36A60083642A /* sample1_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4089A02E0FFACF7F000B29AE /* sample1_unittest.cc */; }; + 40C84992101A36A60083642A /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C848FA101A209C0083642A /* libgtest.a */; }; + 40C84993101A36A60083642A /* libgtest_main.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C8490B101A217E0083642A /* libgtest_main.a */; }; + 40C849A2101A37050083642A /* gtest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4539C8FF0EC27F6400A70F4C /* gtest.framework */; }; + 40C849A4101A37150083642A /* gtest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4539C8FF0EC27F6400A70F4C /* gtest.framework */; }; + 4539C9340EC280AE00A70F4C /* gtest-param-test.h in Headers */ = {isa = PBXBuildFile; fileRef = 4539C9330EC280AE00A70F4C /* gtest-param-test.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4539C9380EC280E200A70F4C /* gtest-linked_ptr.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 4539C9350EC280E200A70F4C /* gtest-linked_ptr.h */; }; + 4539C9390EC280E200A70F4C /* gtest-param-util-generated.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 4539C9360EC280E200A70F4C /* gtest-param-util-generated.h */; }; + 4539C93A0EC280E200A70F4C /* gtest-param-util.h in Copy Headers Internal */ = {isa = PBXBuildFile; fileRef = 4539C9370EC280E200A70F4C /* gtest-param-util.h */; }; + 4567C8181264FF71007740BE /* gtest-printers.h in Headers */ = {isa = PBXBuildFile; fileRef = 4567C8171264FF71007740BE /* gtest-printers.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 40899F9C0FFA740F000B29AE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40899F420FFA7184000B29AE; + remoteInfo = gtest_unittest; + }; + 4089A0970FFAD34A000B29AE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4089A0120FFACEFC000B29AE; + remoteInfo = sample1_unittest; + }; + 408BEC0F1046CFE900DEF522 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C848F9101A209C0083642A; + remoteInfo = "gtest-static"; + }; + 40C44AE50E379922008FCC51 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C44ADC0E3798F4008FCC51; + remoteInfo = Version.h; + }; + 40C8497C101A36850083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C848F9101A209C0083642A; + remoteInfo = "gtest-static"; + }; + 40C8497E101A36850083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C8490A101A217E0083642A; + remoteInfo = "gtest_main-static"; + }; + 40C8498B101A36A60083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C848F9101A209C0083642A; + remoteInfo = "gtest-static"; + }; + 40C8498D101A36A60083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C8490A101A217E0083642A; + remoteInfo = "gtest_main-static"; + }; + 40C8499B101A36DC0083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C8490A101A217E0083642A; + remoteInfo = "gtest_main-static"; + }; + 40C8499D101A36E50083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0; + remoteInfo = "gtest-framework"; + }; + 40C8499F101A36F10083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0; + remoteInfo = "gtest-framework"; + }; + 40C849F6101A43440083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C8497A101A36850083642A; + remoteInfo = "gtest_unittest-static"; + }; + 40C849F8101A43490083642A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 40C84989101A36A60083642A; + remoteInfo = "sample1_unittest-static"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 404884A50E2F7C0400CF7658 /* Copy Headers Internal */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Headers/internal; + dstSubfolderSpec = 6; + files = ( + 404884A00E2F7BE600CF7658 /* gtest-death-test-internal.h in Copy Headers Internal */, + 404884A10E2F7BE600CF7658 /* gtest-filepath.h in Copy Headers Internal */, + 404884A20E2F7BE600CF7658 /* gtest-internal.h in Copy Headers Internal */, + 4539C9380EC280E200A70F4C /* gtest-linked_ptr.h in Copy Headers Internal */, + 4539C9390EC280E200A70F4C /* gtest-param-util-generated.h in Copy Headers Internal */, + 4539C93A0EC280E200A70F4C /* gtest-param-util.h in Copy Headers Internal */, + 404884A30E2F7BE600CF7658 /* gtest-port.h in Copy Headers Internal */, + 404884A40E2F7BE600CF7658 /* gtest-string.h in Copy Headers Internal */, + 40899F500FFA7281000B29AE /* gtest-tuple.h in Copy Headers Internal */, + 3BF6F2A00E79B5AD000F2EEE /* gtest-type-util.h in Copy Headers Internal */, + ); + name = "Copy Headers Internal"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 224A12A10E9EADA700BD17FD /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = "gtest-all.cc"; sourceTree = ""; }; + 224A12A20E9EADCC00BD17FD /* gtest-test-part.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "gtest-test-part.h"; sourceTree = ""; }; + 3B238C120E7FE13C00846E11 /* gtest_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gtest_unittest.cc; sourceTree = ""; }; + 3B87D2100E96B92E000D1852 /* runtests.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = runtests.sh; sourceTree = ""; }; + 3BF6F29F0E79B5AD000F2EEE /* gtest-type-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-type-util.h"; sourceTree = ""; }; + 3BF6F2A40E79B616000F2EEE /* gtest-typed-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-typed-test.h"; sourceTree = ""; }; + 403EE37C0E377822004BD1E2 /* versiongenerate.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = versiongenerate.py; sourceTree = ""; }; + 404883DB0E2F799B00CF7658 /* gtest-death-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-death-test.h"; sourceTree = ""; }; + 404883DC0E2F799B00CF7658 /* gtest-message.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-message.h"; sourceTree = ""; }; + 404883DD0E2F799B00CF7658 /* gtest-spi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-spi.h"; sourceTree = ""; }; + 404883DE0E2F799B00CF7658 /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gtest.h; sourceTree = ""; }; + 404883DF0E2F799B00CF7658 /* gtest_pred_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gtest_pred_impl.h; sourceTree = ""; }; + 404883E00E2F799B00CF7658 /* gtest_prod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gtest_prod.h; sourceTree = ""; }; + 404883E20E2F799B00CF7658 /* gtest-death-test-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-death-test-internal.h"; sourceTree = ""; }; + 404883E30E2F799B00CF7658 /* gtest-filepath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-filepath.h"; sourceTree = ""; }; + 404883E40E2F799B00CF7658 /* gtest-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-internal.h"; sourceTree = ""; }; + 404883E50E2F799B00CF7658 /* gtest-port.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-port.h"; sourceTree = ""; }; + 404883E60E2F799B00CF7658 /* gtest-string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-string.h"; sourceTree = ""; }; + 404883F60E2F799B00CF7658 /* README */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README; path = ../README; sourceTree = SOURCE_ROOT; }; + 4048840D0E2F799B00CF7658 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gtest_main.cc; sourceTree = ""; }; + 404884A90E2F7CD900CF7658 /* CHANGES */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CHANGES; path = ../CHANGES; sourceTree = SOURCE_ROOT; }; + 404884AA0E2F7CD900CF7658 /* CONTRIBUTORS */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CONTRIBUTORS; path = ../CONTRIBUTORS; sourceTree = SOURCE_ROOT; }; + 404884AB0E2F7CD900CF7658 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = SOURCE_ROOT; }; + 40899F430FFA7184000B29AE /* gtest_unittest-framework */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "gtest_unittest-framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 40899F4D0FFA7271000B29AE /* gtest-tuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-tuple.h"; sourceTree = ""; }; + 40899FB30FFA7567000B29AE /* StaticLibraryTarget.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = StaticLibraryTarget.xcconfig; sourceTree = ""; }; + 4089A0130FFACEFC000B29AE /* sample1_unittest-framework */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "sample1_unittest-framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4089A02C0FFACF7F000B29AE /* sample1.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sample1.cc; sourceTree = ""; }; + 4089A02D0FFACF7F000B29AE /* sample1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sample1.h; sourceTree = ""; }; + 4089A02E0FFACF7F000B29AE /* sample1_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sample1_unittest.cc; sourceTree = ""; }; + 40C848FA101A209C0083642A /* libgtest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgtest.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 40C8490B101A217E0083642A /* libgtest_main.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgtest_main.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 40C84987101A36850083642A /* gtest_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gtest_unittest; sourceTree = BUILT_PRODUCTS_DIR; }; + 40C84997101A36A60083642A /* sample1_unittest-static */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "sample1_unittest-static"; sourceTree = BUILT_PRODUCTS_DIR; }; + 40D4CDF10E30E07400294801 /* DebugProject.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DebugProject.xcconfig; sourceTree = ""; }; + 40D4CDF20E30E07400294801 /* FrameworkTarget.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = FrameworkTarget.xcconfig; sourceTree = ""; }; + 40D4CDF30E30E07400294801 /* General.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = General.xcconfig; sourceTree = ""; }; + 40D4CDF40E30E07400294801 /* ReleaseProject.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ReleaseProject.xcconfig; sourceTree = ""; }; + 40D4CF510E30F5E200294801 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4539C8FF0EC27F6400A70F4C /* gtest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = gtest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4539C9330EC280AE00A70F4C /* gtest-param-test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-param-test.h"; sourceTree = ""; }; + 4539C9350EC280E200A70F4C /* gtest-linked_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-linked_ptr.h"; sourceTree = ""; }; + 4539C9360EC280E200A70F4C /* gtest-param-util-generated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-param-util-generated.h"; sourceTree = ""; }; + 4539C9370EC280E200A70F4C /* gtest-param-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-param-util.h"; sourceTree = ""; }; + 4567C8171264FF71007740BE /* gtest-printers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "gtest-printers.h"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 40899F410FFA7184000B29AE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C849A4101A37150083642A /* gtest.framework in Frameworks */, + 40C84916101A235B0083642A /* libgtest_main.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4089A0110FFACEFC000B29AE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C849A2101A37050083642A /* gtest.framework in Frameworks */, + 40C84921101A23AD0083642A /* libgtest_main.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40C84981101A36850083642A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C84982101A36850083642A /* libgtest.a in Frameworks */, + 40C84983101A36850083642A /* libgtest_main.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40C84991101A36A60083642A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C84992101A36A60083642A /* libgtest.a in Frameworks */, + 40C84993101A36A60083642A /* libgtest_main.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DDFF38A45A11DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 4539C8FF0EC27F6400A70F4C /* gtest.framework */, + 40C848FA101A209C0083642A /* libgtest.a */, + 40C8490B101A217E0083642A /* libgtest_main.a */, + 40899F430FFA7184000B29AE /* gtest_unittest-framework */, + 40C84987101A36850083642A /* gtest_unittest */, + 4089A0130FFACEFC000B29AE /* sample1_unittest-framework */, + 40C84997101A36A60083642A /* sample1_unittest-static */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* gtest */ = { + isa = PBXGroup; + children = ( + 40D4CDF00E30E07400294801 /* Config */, + 08FB77ACFE841707C02AAC07 /* Source */, + 40D4CF4E0E30F5E200294801 /* Resources */, + 403EE37B0E377822004BD1E2 /* Scripts */, + 034768DDFF38A45A11DB9C8B /* Products */, + ); + name = gtest; + sourceTree = ""; + }; + 08FB77ACFE841707C02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 404884A90E2F7CD900CF7658 /* CHANGES */, + 404884AA0E2F7CD900CF7658 /* CONTRIBUTORS */, + 404884AB0E2F7CD900CF7658 /* LICENSE */, + 404883F60E2F799B00CF7658 /* README */, + 404883D90E2F799B00CF7658 /* include */, + 4089A02F0FFACF84000B29AE /* samples */, + 404884070E2F799B00CF7658 /* src */, + 3B238BF00E7FE13B00846E11 /* test */, + ); + name = Source; + sourceTree = ""; + }; + 3B238BF00E7FE13B00846E11 /* test */ = { + isa = PBXGroup; + children = ( + 3B238C120E7FE13C00846E11 /* gtest_unittest.cc */, + ); + name = test; + path = ../test; + sourceTree = SOURCE_ROOT; + }; + 403EE37B0E377822004BD1E2 /* Scripts */ = { + isa = PBXGroup; + children = ( + 403EE37C0E377822004BD1E2 /* versiongenerate.py */, + 3B87D2100E96B92E000D1852 /* runtests.sh */, + ); + path = Scripts; + sourceTree = ""; + }; + 404883D90E2F799B00CF7658 /* include */ = { + isa = PBXGroup; + children = ( + 404883DA0E2F799B00CF7658 /* gtest */, + ); + name = include; + path = ../include; + sourceTree = SOURCE_ROOT; + }; + 404883DA0E2F799B00CF7658 /* gtest */ = { + isa = PBXGroup; + children = ( + 404883E10E2F799B00CF7658 /* internal */, + 224A12A20E9EADCC00BD17FD /* gtest-test-part.h */, + 404883DB0E2F799B00CF7658 /* gtest-death-test.h */, + 404883DC0E2F799B00CF7658 /* gtest-message.h */, + 4539C9330EC280AE00A70F4C /* gtest-param-test.h */, + 4567C8171264FF71007740BE /* gtest-printers.h */, + 404883DD0E2F799B00CF7658 /* gtest-spi.h */, + 404883DE0E2F799B00CF7658 /* gtest.h */, + 404883DF0E2F799B00CF7658 /* gtest_pred_impl.h */, + 404883E00E2F799B00CF7658 /* gtest_prod.h */, + 3BF6F2A40E79B616000F2EEE /* gtest-typed-test.h */, + ); + path = gtest; + sourceTree = ""; + }; + 404883E10E2F799B00CF7658 /* internal */ = { + isa = PBXGroup; + children = ( + 404883E20E2F799B00CF7658 /* gtest-death-test-internal.h */, + 404883E30E2F799B00CF7658 /* gtest-filepath.h */, + 404883E40E2F799B00CF7658 /* gtest-internal.h */, + 4539C9350EC280E200A70F4C /* gtest-linked_ptr.h */, + 4539C9360EC280E200A70F4C /* gtest-param-util-generated.h */, + 4539C9370EC280E200A70F4C /* gtest-param-util.h */, + 404883E50E2F799B00CF7658 /* gtest-port.h */, + 404883E60E2F799B00CF7658 /* gtest-string.h */, + 40899F4D0FFA7271000B29AE /* gtest-tuple.h */, + 3BF6F29F0E79B5AD000F2EEE /* gtest-type-util.h */, + ); + path = internal; + sourceTree = ""; + }; + 404884070E2F799B00CF7658 /* src */ = { + isa = PBXGroup; + children = ( + 224A12A10E9EADA700BD17FD /* gtest-all.cc */, + 4048840D0E2F799B00CF7658 /* gtest_main.cc */, + ); + name = src; + path = ../src; + sourceTree = SOURCE_ROOT; + }; + 4089A02F0FFACF84000B29AE /* samples */ = { + isa = PBXGroup; + children = ( + 4089A02C0FFACF7F000B29AE /* sample1.cc */, + 4089A02D0FFACF7F000B29AE /* sample1.h */, + 4089A02E0FFACF7F000B29AE /* sample1_unittest.cc */, + ); + name = samples; + path = ../samples; + sourceTree = SOURCE_ROOT; + }; + 40D4CDF00E30E07400294801 /* Config */ = { + isa = PBXGroup; + children = ( + 40D4CDF10E30E07400294801 /* DebugProject.xcconfig */, + 40D4CDF20E30E07400294801 /* FrameworkTarget.xcconfig */, + 40D4CDF30E30E07400294801 /* General.xcconfig */, + 40D4CDF40E30E07400294801 /* ReleaseProject.xcconfig */, + 40899FB30FFA7567000B29AE /* StaticLibraryTarget.xcconfig */, + ); + path = Config; + sourceTree = ""; + }; + 40D4CF4E0E30F5E200294801 /* Resources */ = { + isa = PBXGroup; + children = ( + 40D4CF510E30F5E200294801 /* Info.plist */, + ); + path = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8D07F2BD0486CC7A007CD1D0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 404884380E2F799B00CF7658 /* gtest-death-test.h in Headers */, + 404884390E2F799B00CF7658 /* gtest-message.h in Headers */, + 4539C9340EC280AE00A70F4C /* gtest-param-test.h in Headers */, + 4567C8181264FF71007740BE /* gtest-printers.h in Headers */, + 3BF6F2A50E79B616000F2EEE /* gtest-typed-test.h in Headers */, + 4048843A0E2F799B00CF7658 /* gtest-spi.h in Headers */, + 4048843B0E2F799B00CF7658 /* gtest.h in Headers */, + 4048843C0E2F799B00CF7658 /* gtest_pred_impl.h in Headers */, + 4048843D0E2F799B00CF7658 /* gtest_prod.h in Headers */, + 224A12A30E9EADCC00BD17FD /* gtest-test-part.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 40899F420FFA7184000B29AE /* gtest_unittest-framework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40899F4A0FFA71BC000B29AE /* Build configuration list for PBXNativeTarget "gtest_unittest-framework" */; + buildPhases = ( + 40899F400FFA7184000B29AE /* Sources */, + 40899F410FFA7184000B29AE /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 40C849A0101A36F10083642A /* PBXTargetDependency */, + ); + name = "gtest_unittest-framework"; + productName = gtest_unittest; + productReference = 40899F430FFA7184000B29AE /* gtest_unittest-framework */; + productType = "com.apple.product-type.tool"; + }; + 4089A0120FFACEFC000B29AE /* sample1_unittest-framework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4089A0240FFACF01000B29AE /* Build configuration list for PBXNativeTarget "sample1_unittest-framework" */; + buildPhases = ( + 4089A0100FFACEFC000B29AE /* Sources */, + 4089A0110FFACEFC000B29AE /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 40C8499E101A36E50083642A /* PBXTargetDependency */, + ); + name = "sample1_unittest-framework"; + productName = sample1_unittest; + productReference = 4089A0130FFACEFC000B29AE /* sample1_unittest-framework */; + productType = "com.apple.product-type.tool"; + }; + 40C848F9101A209C0083642A /* gtest-static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40C84902101A212E0083642A /* Build configuration list for PBXNativeTarget "gtest-static" */; + buildPhases = ( + 40C848F7101A209C0083642A /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "gtest-static"; + productName = "gtest-static"; + productReference = 40C848FA101A209C0083642A /* libgtest.a */; + productType = "com.apple.product-type.library.static"; + }; + 40C8490A101A217E0083642A /* gtest_main-static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40C84912101A21D20083642A /* Build configuration list for PBXNativeTarget "gtest_main-static" */; + buildPhases = ( + 40C84908101A217E0083642A /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "gtest_main-static"; + productName = "gtest_main-static"; + productReference = 40C8490B101A217E0083642A /* libgtest_main.a */; + productType = "com.apple.product-type.library.static"; + }; + 40C8497A101A36850083642A /* gtest_unittest-static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40C84984101A36850083642A /* Build configuration list for PBXNativeTarget "gtest_unittest-static" */; + buildPhases = ( + 40C8497F101A36850083642A /* Sources */, + 40C84981101A36850083642A /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 40C8497B101A36850083642A /* PBXTargetDependency */, + 40C8497D101A36850083642A /* PBXTargetDependency */, + ); + name = "gtest_unittest-static"; + productName = gtest_unittest; + productReference = 40C84987101A36850083642A /* gtest_unittest */; + productType = "com.apple.product-type.tool"; + }; + 40C84989101A36A60083642A /* sample1_unittest-static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 40C84994101A36A60083642A /* Build configuration list for PBXNativeTarget "sample1_unittest-static" */; + buildPhases = ( + 40C8498E101A36A60083642A /* Sources */, + 40C84991101A36A60083642A /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 40C8498A101A36A60083642A /* PBXTargetDependency */, + 40C8498C101A36A60083642A /* PBXTargetDependency */, + ); + name = "sample1_unittest-static"; + productName = sample1_unittest; + productReference = 40C84997101A36A60083642A /* sample1_unittest-static */; + productType = "com.apple.product-type.tool"; + }; + 8D07F2BC0486CC7A007CD1D0 /* gtest-framework */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4FADC24208B4156D00ABE55E /* Build configuration list for PBXNativeTarget "gtest-framework" */; + buildPhases = ( + 8D07F2C10486CC7A007CD1D0 /* Sources */, + 8D07F2BD0486CC7A007CD1D0 /* Headers */, + 404884A50E2F7C0400CF7658 /* Copy Headers Internal */, + 8D07F2BF0486CC7A007CD1D0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 40C44AE60E379922008FCC51 /* PBXTargetDependency */, + 408BEC101046CFE900DEF522 /* PBXTargetDependency */, + 40C8499C101A36DC0083642A /* PBXTargetDependency */, + ); + name = "gtest-framework"; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = gtest; + productReference = 4539C8FF0EC27F6400A70F4C /* gtest.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + }; + buildConfigurationList = 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "gtest" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + en, + ); + mainGroup = 0867D691FE84028FC02AAC07 /* gtest */; + productRefGroup = 034768DDFF38A45A11DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D07F2BC0486CC7A007CD1D0 /* gtest-framework */, + 40C848F9101A209C0083642A /* gtest-static */, + 40C8490A101A217E0083642A /* gtest_main-static */, + 40899F420FFA7184000B29AE /* gtest_unittest-framework */, + 40C8497A101A36850083642A /* gtest_unittest-static */, + 4089A0120FFACEFC000B29AE /* sample1_unittest-framework */, + 40C84989101A36A60083642A /* sample1_unittest-static */, + 3B238F5F0E828B5400846E11 /* Check */, + 40C44ADC0E3798F4008FCC51 /* Version Info */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D07F2BF0486CC7A007CD1D0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 404884500E2F799B00CF7658 /* README in Resources */, + 404884AC0E2F7CD900CF7658 /* CHANGES in Resources */, + 404884AD0E2F7CD900CF7658 /* CONTRIBUTORS in Resources */, + 404884AE0E2F7CD900CF7658 /* LICENSE in Resources */, + 40C84978101A36540083642A /* libgtest_main.a in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B238F5E0E828B5400846E11 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Remember, this \"Run Script\" build phase will be executed from $SRCROOT\n/bin/bash Scripts/runtests.sh"; + }; + 40C44ADB0E3798F4008FCC51 /* Generate Version.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/Scripts/versiongenerate.py", + "$(SRCROOT)/../configure.ac", + ); + name = "Generate Version.h"; + outputPaths = ( + "$(PROJECT_TEMP_DIR)/Version.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Remember, this \"Run Script\" build phase will be executed from $SRCROOT\n/usr/bin/python Scripts/versiongenerate.py ../ $PROJECT_TEMP_DIR"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 40899F400FFA7184000B29AE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40899F530FFA72A0000B29AE /* gtest_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4089A0100FFACEFC000B29AE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4089A0440FFAD1BE000B29AE /* sample1.cc in Sources */, + 4089A0460FFAD1BE000B29AE /* sample1_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40C848F7101A209C0083642A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C848FF101A21150083642A /* gtest-all.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40C84908101A217E0083642A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C84915101A21DF0083642A /* gtest_main.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40C8497F101A36850083642A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C84980101A36850083642A /* gtest_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 40C8498E101A36A60083642A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40C8498F101A36A60083642A /* sample1.cc in Sources */, + 40C84990101A36A60083642A /* sample1_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D07F2C10486CC7A007CD1D0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 40899F3A0FFA70D4000B29AE /* gtest-all.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 40899F9D0FFA740F000B29AE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40899F420FFA7184000B29AE /* gtest_unittest-framework */; + targetProxy = 40899F9C0FFA740F000B29AE /* PBXContainerItemProxy */; + }; + 4089A0980FFAD34A000B29AE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4089A0120FFACEFC000B29AE /* sample1_unittest-framework */; + targetProxy = 4089A0970FFAD34A000B29AE /* PBXContainerItemProxy */; + }; + 408BEC101046CFE900DEF522 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C848F9101A209C0083642A /* gtest-static */; + targetProxy = 408BEC0F1046CFE900DEF522 /* PBXContainerItemProxy */; + }; + 40C44AE60E379922008FCC51 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C44ADC0E3798F4008FCC51 /* Version Info */; + targetProxy = 40C44AE50E379922008FCC51 /* PBXContainerItemProxy */; + }; + 40C8497B101A36850083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C848F9101A209C0083642A /* gtest-static */; + targetProxy = 40C8497C101A36850083642A /* PBXContainerItemProxy */; + }; + 40C8497D101A36850083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C8490A101A217E0083642A /* gtest_main-static */; + targetProxy = 40C8497E101A36850083642A /* PBXContainerItemProxy */; + }; + 40C8498A101A36A60083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C848F9101A209C0083642A /* gtest-static */; + targetProxy = 40C8498B101A36A60083642A /* PBXContainerItemProxy */; + }; + 40C8498C101A36A60083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C8490A101A217E0083642A /* gtest_main-static */; + targetProxy = 40C8498D101A36A60083642A /* PBXContainerItemProxy */; + }; + 40C8499C101A36DC0083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C8490A101A217E0083642A /* gtest_main-static */; + targetProxy = 40C8499B101A36DC0083642A /* PBXContainerItemProxy */; + }; + 40C8499E101A36E50083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D07F2BC0486CC7A007CD1D0 /* gtest-framework */; + targetProxy = 40C8499D101A36E50083642A /* PBXContainerItemProxy */; + }; + 40C849A0101A36F10083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D07F2BC0486CC7A007CD1D0 /* gtest-framework */; + targetProxy = 40C8499F101A36F10083642A /* PBXContainerItemProxy */; + }; + 40C849F7101A43440083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C8497A101A36850083642A /* gtest_unittest-static */; + targetProxy = 40C849F6101A43440083642A /* PBXContainerItemProxy */; + }; + 40C849F9101A43490083642A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 40C84989101A36A60083642A /* sample1_unittest-static */; + targetProxy = 40C849F8101A43490083642A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 3B238F600E828B5400846E11 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + PRODUCT_NAME = Check; + SDKROOT = macosx; + }; + name = Debug; + }; + 3B238F610E828B5400846E11 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + PRODUCT_NAME = Check; + SDKROOT = macosx; + ZERO_LINK = NO; + }; + name = Release; + }; + 40899F450FFA7185000B29AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ../; + PRODUCT_NAME = "gtest_unittest-framework"; + SDKROOT = macosx; + }; + name = Debug; + }; + 40899F460FFA7185000B29AE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ../; + PRODUCT_NAME = "gtest_unittest-framework"; + SDKROOT = macosx; + }; + name = Release; + }; + 4089A0150FFACEFD000B29AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + PRODUCT_NAME = "sample1_unittest-framework"; + SDKROOT = macosx; + }; + name = Debug; + }; + 4089A0160FFACEFD000B29AE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + PRODUCT_NAME = "sample1_unittest-framework"; + SDKROOT = macosx; + }; + name = Release; + }; + 40C44ADF0E3798F4008FCC51 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_NAME = gtest; + SDKROOT = macosx; + TARGET_NAME = gtest; + }; + name = Debug; + }; + 40C44AE00E3798F4008FCC51 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + MACOSX_DEPLOYMENT_TARGET = 10.7; + PRODUCT_NAME = gtest; + SDKROOT = macosx; + TARGET_NAME = gtest; + }; + name = Release; + }; + 40C848FB101A209D0083642A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40899FB30FFA7567000B29AE /* StaticLibraryTarget.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + ../, + ../include/, + ); + PRODUCT_NAME = gtest; + SDKROOT = macosx; + }; + name = Debug; + }; + 40C848FC101A209D0083642A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40899FB30FFA7567000B29AE /* StaticLibraryTarget.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + ../, + ../include/, + ); + PRODUCT_NAME = gtest; + SDKROOT = macosx; + }; + name = Release; + }; + 40C8490E101A217F0083642A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40899FB30FFA7567000B29AE /* StaticLibraryTarget.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + ../, + ../include/, + ); + PRODUCT_NAME = gtest_main; + SDKROOT = macosx; + }; + name = Debug; + }; + 40C8490F101A217F0083642A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40899FB30FFA7567000B29AE /* StaticLibraryTarget.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + ../, + ../include/, + ); + PRODUCT_NAME = gtest_main; + SDKROOT = macosx; + }; + name = Release; + }; + 40C84985101A36850083642A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ../; + PRODUCT_NAME = gtest_unittest; + SDKROOT = macosx; + }; + name = Debug; + }; + 40C84986101A36850083642A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ../; + PRODUCT_NAME = gtest_unittest; + SDKROOT = macosx; + }; + name = Release; + }; + 40C84995101A36A60083642A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + PRODUCT_NAME = "sample1_unittest-static"; + SDKROOT = macosx; + }; + name = Debug; + }; + 40C84996101A36A60083642A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + PRODUCT_NAME = "sample1_unittest-static"; + SDKROOT = macosx; + }; + name = Release; + }; + 4FADC24308B4156D00ABE55E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40D4CDF20E30E07400294801 /* FrameworkTarget.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + ../, + ../include/, + ); + INFOPLIST_FILE = Resources/Info.plist; + INFOPLIST_PREFIX_HEADER = "$(PROJECT_TEMP_DIR)/Version.h"; + INFOPLIST_PREPROCESS = YES; + PRODUCT_NAME = gtest; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 4FADC24408B4156D00ABE55E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40D4CDF20E30E07400294801 /* FrameworkTarget.xcconfig */; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + HEADER_SEARCH_PATHS = ( + ../, + ../include/, + ); + INFOPLIST_FILE = Resources/Info.plist; + INFOPLIST_PREFIX_HEADER = "$(PROJECT_TEMP_DIR)/Version.h"; + INFOPLIST_PREPROCESS = YES; + PRODUCT_NAME = gtest; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 4FADC24708B4156D00ABE55E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40D4CDF10E30E07400294801 /* DebugProject.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 4FADC24808B4156D00ABE55E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 40D4CDF40E30E07400294801 /* ReleaseProject.xcconfig */; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3B238FA30E828BB600846E11 /* Build configuration list for PBXAggregateTarget "Check" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3B238F600E828B5400846E11 /* Debug */, + 3B238F610E828B5400846E11 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40899F4A0FFA71BC000B29AE /* Build configuration list for PBXNativeTarget "gtest_unittest-framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40899F450FFA7185000B29AE /* Debug */, + 40899F460FFA7185000B29AE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4089A0240FFACF01000B29AE /* Build configuration list for PBXNativeTarget "sample1_unittest-framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4089A0150FFACEFD000B29AE /* Debug */, + 4089A0160FFACEFD000B29AE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40C44AE40E379905008FCC51 /* Build configuration list for PBXAggregateTarget "Version Info" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40C44ADF0E3798F4008FCC51 /* Debug */, + 40C44AE00E3798F4008FCC51 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40C84902101A212E0083642A /* Build configuration list for PBXNativeTarget "gtest-static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40C848FB101A209D0083642A /* Debug */, + 40C848FC101A209D0083642A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40C84912101A21D20083642A /* Build configuration list for PBXNativeTarget "gtest_main-static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40C8490E101A217F0083642A /* Debug */, + 40C8490F101A217F0083642A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40C84984101A36850083642A /* Build configuration list for PBXNativeTarget "gtest_unittest-static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40C84985101A36850083642A /* Debug */, + 40C84986101A36850083642A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 40C84994101A36A60083642A /* Build configuration list for PBXNativeTarget "sample1_unittest-static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 40C84995101A36A60083642A /* Debug */, + 40C84996101A36A60083642A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4FADC24208B4156D00ABE55E /* Build configuration list for PBXNativeTarget "gtest-framework" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4FADC24308B4156D00ABE55E /* Debug */, + 4FADC24408B4156D00ABE55E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "gtest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4FADC24708B4156D00ABE55E /* Debug */, + 4FADC24808B4156D00ABE55E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/include/IMultiWallet.h b/include/IMultiWallet.h new file mode 100755 index 0000000000..1b7f18b06d --- /dev/null +++ b/include/IMultiWallet.h @@ -0,0 +1,101 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace CryptoNote { + +enum class MultiWalletTransactionState : uint8_t { + FAILED +}; + +struct MultiWalletTransaction { + MultiWalletTransactionState state; + uint64_t timestamp; + uint64_t blockHeight; + std::array hash; + bool isBase; + int64_t totalAmount; + uint64_t fee; + uint64_t creationTime; + uint64_t unlockTime; + std::string extra; +}; + +struct MultiWalletTransfer { + std::string address; + uint64_t amount; +}; + +class IMultiWallet { +public: + virtual ~IMultiWallet() {} + + virtual void initialize(const std::string& password) = 0; + virtual void load(std::istream& source, const std::string& password) = 0; + virtual void shutdown() = 0; + + virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) = 0; + + virtual std::size_t getAddressCount() const = 0; + virtual std::string getAddress(std::size_t index) const = 0; + virtual std::string createAddress() = 0; + virtual std::string createAddress(const std::array& spendPublicKey, const std::array& spendSecretKey) = 0; + virtual void deleteAddress(const std::string& address) = 0; + + virtual uint64_t getActualBalance() const = 0; + virtual uint64_t getActualBalance(const std::string& address) const = 0; + virtual uint64_t getPendingBalance() const = 0; + virtual uint64_t getPendingBalance(const std::string& address) const = 0; + + virtual std::size_t getTransactionCount() const = 0; + virtual MultiWalletTransaction getTransaction(std::size_t transactionIndex) const = 0; + virtual std::size_t getTransactionTransferCount(std::size_t transactionIndex) const = 0; + virtual MultiWalletTransfer getTransactionTransfer(std::size_t transactionIndex, std::size_t transferIndex) const = 0; + + virtual std::size_t transfer(const MultiWalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual std::size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual std::size_t transfer(const std::string& sourceAddress, const MultiWalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual std::size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + + virtual void start() = 0; + virtual void stop() = 0; + virtual void refresh() = 0; +}; + +} diff --git a/include/INode.h b/include/INode.h index 364a5d4da3..968834bead 100644 --- a/include/INode.h +++ b/include/INode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -50,8 +50,8 @@ struct OutsForAmount { struct BlockCompleteEntry { crypto::hash blockHash; - cryptonote::blobdata block; - std::list txs; + CryptoNote::blobdata block; + std::list txs; }; class INode { @@ -68,14 +68,16 @@ class INode { virtual size_t getPeerCount() const = 0; virtual uint64_t getLastLocalBlockHeight() const = 0; virtual uint64_t getLastKnownBlockHeight() const = 0; + virtual uint64_t getLocalBlockCount() const = 0; + virtual uint64_t getKnownBlockCount() const = 0; virtual uint64_t getLastLocalBlockTimestamp() const = 0; - virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) = 0; - virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; + virtual void relayTransaction(const Transaction& transaction, const Callback& callback) = 0; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) = 0; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) = 0; }; } diff --git a/include/IObservable.h b/include/IObservable.h index 6694420511..0adc55f11f 100644 --- a/include/IObservable.h +++ b/include/IObservable.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/include/IStreamSerializable.h b/include/IStreamSerializable.h index 0472ec2ab2..f96f37ec12 100644 --- a/include/IStreamSerializable.h +++ b/include/IStreamSerializable.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/include/ITransaction.h b/include/ITransaction.h index bed5de3ab0..36e9490d63 100644 --- a/include/ITransaction.h +++ b/include/ITransaction.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -40,11 +40,6 @@ struct AccountKeys { SecretKey viewSecretKey; }; -struct KeyPair { - PublicKey publicKey; - SecretKey secretKey; -}; - namespace TransactionTypes { enum class InputType : uint8_t { Invalid, Key, Multisignature, Generating }; @@ -92,6 +87,11 @@ namespace TransactionTypes { OutputKeyInfo realOutput; }; + struct KeyPair { + PublicKey publicKey; + SecretKey secretKey; + }; + } // @@ -109,6 +109,7 @@ class ITransactionReader { // extra virtual bool getPaymentId(Hash& paymentId) const = 0; virtual bool getExtraNonce(std::string& nonce) const = 0; + virtual Blob getExtra() const = 0; // inputs virtual size_t getInputCount() const = 0; @@ -154,7 +155,7 @@ class ITransactionWriter { // Inputs/Outputs virtual size_t addInput(const TransactionTypes::InputKey& input) = 0; - virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) = 0; virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0; virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0; @@ -165,7 +166,7 @@ class ITransactionWriter { virtual void setTransactionSecretKey(const SecretKey& key) = 0; // signing - virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0; + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) = 0; virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; }; diff --git a/include/ITransfersContainer.h b/include/ITransfersContainer.h index d6c8408031..21e553daf3 100644 --- a/include/ITransfersContainer.h +++ b/include/ITransfersContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/include/ITransfersSynchronizer.h b/include/ITransfersSynchronizer.h index c9734c4d07..f68d7399d6 100644 --- a/include/ITransfersSynchronizer.h +++ b/include/ITransfersSynchronizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/include/IWallet.h b/include/IWallet.h index b15a627671..c64961d705 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt old mode 100755 new mode 100644 index 9015679249..bd4b2578d4 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,74 +1,67 @@ add_definitions(-DSTATICLIB) -file(GLOB_RECURSE COMMON common/*) -file(GLOB_RECURSE CRYPTO crypto/*) -file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/* cryptonote_config.h) -file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*) -file(GLOB_RECURSE DAEMON daemon/*) -file(GLOB_RECURSE P2P p2p/*) -file(GLOB_RECURSE RPC rpc/*) -file(GLOB_RECURSE SIMPLEWALLET simplewallet/*) -file(GLOB_RECURSE CONN_TOOL connectivity_tool/*) -file(GLOB_RECURSE WALLET wallet/*) -file(GLOB_RECURSE MINER miner/*) -file(GLOB_RECURSE NODE_RPC_PROXY node_rpc_proxy/*) -file(GLOB_RECURSE TRANSFERS transfers/*) +file(GLOB_RECURSE Common Common/*) +file(GLOB_RECURSE ConnectivityTool connectivity_tool/*) +file(GLOB_RECURSE Crypto crypto/*) +file(GLOB_RECURSE CryptoNote CryptoNote/*) +file(GLOB_RECURSE CryptoNoteCore cryptonote_core/* cryptonote_config.h) +file(GLOB_RECURSE CryptoNoteProtocol cryptonote_protocol/*) +file(GLOB_RECURSE Daemon daemon/*) +file(GLOB_RECURSE Http HTTP/*) +file(GLOB_RECURSE InProcessNode InProcessNode/*) +file(GLOB_RECURSE Logging Logging/*) +file(GLOB_RECURSE NodeRpcProxy node_rpc_proxy/*) +file(GLOB_RECURSE P2p p2p/*) +file(GLOB_RECURSE Rpc rpc/*) +file(GLOB_RECURSE Serialization serialization/*) +file(GLOB_RECURSE SimpleWallet simplewallet/*) if(MSVC) -file(GLOB_RECURSE SYSTEM Platform/Windows/System/*) +file(GLOB_RECURSE System System/* Platform/Windows/System/*) elseif(APPLE) -file(GLOB_RECURSE SYSTEM Platform/OSX/System/*) +file(GLOB_RECURSE System System/* Platform/OSX/System/*) else() -file(GLOB_RECURSE SYSTEM Platform/Linux/System/*) +file(GLOB_RECURSE System System/* Platform/Linux/System/*) endif() -file(GLOB_RECURSE SERIALIZATION serialization/*) -file(GLOB_RECURSE LOGGER logger/*) -file(GLOB_RECURSE INPROCESS_NODE inprocess_node/*) -file(GLOB_RECURSE HTTP HTTP/*) +file(GLOB_RECURSE Transfers transfers/*) +file(GLOB_RECURSE Wallet wallet/*) +file(GLOB_RECURSE PaymentService payment_service/*) +source_group("" FILES ${Common} ${ConnectivityTool} ${Crypto} ${CryptoNote} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet}) -source_group(common FILES ${COMMON}) -source_group(crypto FILES ${CRYPTO}) -source_group(cryptonote_core FILES ${CRYPTONOTE_CORE}) -source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) -source_group(daemon FILES ${DAEMON}) -source_group(p2p FILES ${P2P}) -source_group(rpc FILES ${RPC}) -source_group(System FILES ${SYSTEM} ${HTTP}) -source_group(simplewallet FILES ${SIMPLEWALLET}) -source_group(connectivity-tool FILES ${CONN_TOOL}) -source_group(wallet FILES ${WALLET}) -source_group(simpleminer FILES ${MINER}) -source_group(node_rpc_proxy FILES ${NODE_RPC_PROXY}) -source_group(transfers FILES ${TRANSFERS}) -source_group(logger FILES ${LOGGER}) -source_group(inprocess_node FILES ${INPROCESS_NODE}) +add_library(Common ${Common}) +add_library(Crypto ${Crypto}) +add_library(CryptoNote ${CryptoNote}) +add_library(CryptoNoteCore ${CryptoNoteCore}) +add_library(Http ${Http}) +add_library(InProcessNode ${InProcessNode}) +add_library(Logging ${Logging}) +add_library(NodeRpcProxy ${NodeRpcProxy}) +add_library(Rpc ${Rpc}) +add_library(P2P ${CryptoNoteProtocol} ${P2p}) +add_library(Serialization ${Serialization}) +add_library(System ${System}) +add_library(Transfers ${Transfers}) +add_library(Wallet ${Wallet}) -add_library(common ${COMMON}) -add_library(crypto ${CRYPTO}) -add_library(serialization ${SERIALIZATION}) -add_library(cryptonote_core ${CRYPTONOTE_CORE}) -add_library(node_rpc_proxy ${NODE_RPC_PROXY}) -add_library(inprocess_node ${INPROCESS_NODE}) -add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL}) -add_executable(connectivity_tool ${CONN_TOOL}) -add_executable(simpleminer ${MINER}) -target_link_libraries(daemon epee rpc cryptonote_core crypto common upnpc-static serialization ${Boost_LIBRARIES}) -target_link_libraries(connectivity_tool epee rpc cryptonote_core crypto common serialization ${Boost_LIBRARIES}) -target_link_libraries(simpleminer epee cryptonote_core crypto common serialization ${Boost_LIBRARIES}) -add_library(rpc ${RPC}) -add_library(System ${SYSTEM} ${HTTP} System/TcpStream.cpp System/TcpStream.h) -add_library(wallet ${WALLET}) -add_executable(simplewallet ${SIMPLEWALLET} ) -target_link_libraries(simplewallet epee wallet transfers rpc cryptonote_core crypto common upnpc-static node_rpc_proxy serialization ${Boost_LIBRARIES}) -add_library(logger ${LOGGER}) -add_library(transfers ${TRANSFERS}) +add_executable(ConnectivityTool ${ConnectivityTool} p2p/LevinProtocol.cpp p2p/LevinProtocol.h) +add_executable(Daemon ${Daemon}) +add_executable(SimpleWallet ${SimpleWallet}) +add_executable(PaymentGate ${PaymentService}) +target_link_libraries(ConnectivityTool epee Common Crypto Rpc Http System ${Boost_LIBRARIES}) +target_link_libraries(Daemon epee CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(SimpleWallet epee Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(PaymentGate epee Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static ${Boost_LIBRARIES}) -add_dependencies(connectivity_tool version) -add_dependencies(daemon version) -add_dependencies(rpc version) -add_dependencies(simplewallet version) +add_dependencies(Rpc version) -set_property(TARGET common crypto cryptonote_core rpc System wallet node_rpc_proxy serialization logger transfers inprocess_node PROPERTY FOLDER "libs") -set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog") -set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind") +add_dependencies(ConnectivityTool version) +add_dependencies(Daemon version) +add_dependencies(SimpleWallet version) +add_dependencies(PaymentGate version) +add_dependencies(P2P version) + +set_property(TARGET ConnectivityTool PROPERTY OUTPUT_NAME "connectivity_tool") +set_property(TARGET Daemon PROPERTY OUTPUT_NAME "bytecoind") +set_property(TARGET SimpleWallet PROPERTY OUTPUT_NAME "simplewallet") +set_property(TARGET PaymentGate PROPERTY OUTPUT_NAME "walletd") diff --git a/src/Common/ArrayRef.h b/src/Common/ArrayRef.h new file mode 100755 index 0000000000..8e6501fa86 --- /dev/null +++ b/src/Common/ArrayRef.h @@ -0,0 +1,441 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ArrayView.h" +#include + +namespace Common { + +// 'ArrayRef' is a pair of pointer to object of parametrized type and size. +// It is recommended to pass 'ArrayRef' to procedures by value. +// 'ArrayRef' supports 'EMPTY' and 'NIL' representations as follows: +// 'data' == 'nullptr' && 'size' == 0 - EMPTY NIL +// 'data' != 'nullptr' && 'size' == 0 - EMPTY NOTNIL +// 'data' == 'nullptr' && 'size' > 0 - Undefined +// 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL +// For signed integer 'Size', 'ArrayRef' with 'size' < 0 is undefined. +template class ArrayRef { +public: + typedef ObjectType Object; + typedef SizeType Size; + + const static Size INVALID; + const static ArrayRef EMPTY; + const static ArrayRef NIL; + + // Default constructor. + // Leaves object uninitialized. Any usage before initializing it is undefined. + ArrayRef() +#ifndef NDEBUG + : data(nullptr), size(INVALID) // In debug mode, fill in object with invalid state (undefined). +#endif + { + } + + // Direct constructor. + // The behavior is undefined unless 'arrayData' != 'nullptr' || 'arraySize' == 0 + ArrayRef(Object* arrayData, Size arraySize) : data(arrayData), size(arraySize) { + assert(data != nullptr || size == 0); + } + + // Constructor from C array. + // The behavior is undefined unless 'arrayData' != 'nullptr' || 'arraySize' == 0. Input state can be malformed using poiner conversions. + template ArrayRef(Object(&arrayData)[arraySize]) : data(arrayData), size(arraySize) { + assert(data != nullptr || size == 0); + } + + // Copy constructor. + // Performs default action - bitwise copying of source object. + // The behavior is undefined unless 'other' 'ArrayRef' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 + ArrayRef(const ArrayRef& other) : data(other.data), size(other.size) { + assert(data != nullptr || size == 0); + } + + // Destructor. + // No special action is performed. + ~ArrayRef() { + } + + // Copy assignment operator. + // The behavior is undefined unless 'other' 'ArrayRef' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 + ArrayRef& operator=(const ArrayRef& other) { + assert(other.data != nullptr || other.size == 0); + data = other.data; + size = other.size; + return *this; + } + + operator ArrayView() const { + return ArrayView(data, size); + } + + Object* getData() const { + assert(data != nullptr || size == 0); + return data; + } + + Size getSize() const { + assert(data != nullptr || size == 0); + return size; + } + + // Return false if 'ArrayRef' is not EMPTY. + // The behavior is undefined unless 'ArrayRef' was initialized. + bool isEmpty() const { + assert(data != nullptr || size == 0); + return size == 0; + } + + // Return false if 'ArrayRef' is not NIL. + // The behavior is undefined unless 'ArrayRef' was initialized. + bool isNil() const { + assert(data != nullptr || size == 0); + return data == nullptr; + } + + // Get 'ArrayRef' element by index. + // The behavior is undefined unless 'ArrayRef' was initialized and 'index' < 'size'. + Object& operator[](Size index) const { + assert(data != nullptr || size == 0); + assert(index < size); + return *(data + index); + } + + // Get first element. + // The behavior is undefined unless 'ArrayRef' was initialized and 'size' > 0 + Object& first() const { + assert(data != nullptr || size == 0); + assert(size > 0); + return *data; + } + + // Get last element. + // The behavior is undefined unless 'ArrayRef' was initialized and 'size' > 0 + Object& last() const { + assert(data != nullptr || size == 0); + assert(size > 0); + return *(data + (size - 1)); + } + + // Return a pointer to the first element. + // The behavior is undefined unless 'ArrayRef' was initialized. + Object* begin() const { + assert(data != nullptr || size == 0); + return data; + } + + // Return a pointer after the last element. + // The behavior is undefined unless 'ArrayRef' was initialized. + Object* end() const { + assert(data != nullptr || size == 0); + return data + size; + } + + // Compare elements of two arrays, return false if there is a difference. + // EMPTY and NIL arrays are considered equal. + // The behavior is undefined unless both arrays were initialized. + bool operator==(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size == other.getSize()) { + for (Size i = 0;; ++i) { + if (i == size) { + return true; + } + + if (!(*(data + i) == *(other.getData() + i))) { + break; + } + } + } + + return false; + } + + // Compare elements two arrays, return false if there is no difference. + // EMPTY and NIL arrays are considered equal. + // The behavior is undefined unless both arrays were initialized. + bool operator!=(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size == other.getSize()) { + for (Size i = 0;; ++i) { + if (i == size) { + return false; + } + + if (*(data + i) != *(other.getData() + i)) { + break; + } + } + } + + return true; + } + + // Return false if 'ArrayRef' does not contain 'object' at the beginning. + // The behavior is undefined unless 'ArrayRef' was initialized. + bool beginsWith(const Object& object) const { + assert(data != nullptr || size == 0); + if (size == 0) { + return false; + } + + return *data == object; + } + + // Return false if 'ArrayRef' does not contain 'other' at the beginning. + // The behavior is undefined unless both arrays were initialized. + bool beginsWith(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size >= other.getSize()) { + for (Size i = 0;; ++i) { + if (i == other.getSize()) { + return true; + } + + if (!(*(data + i) == *(other.getData() + i))) { + break; + } + } + } + + return false; + } + + // Return false if 'ArrayRef' does not contain 'object'. + // The behavior is undefined unless 'ArrayRef' was initialized. + bool contains(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return true; + } + } + + return false; + } + + // Return false if 'ArrayRef' does not contain 'other'. + // The behavior is undefined unless both arrays were initialized. + bool contains(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.getSize()) { + return true; + } + + if (!(*(data + j + k) == *(other.getData() + k))) { + break; + } + } + } + } + + return false; + } + + // Return false if 'ArrayRef' does not contain 'object' at the end. + // The behavior is undefined unless 'ArrayRef' was initialized. + bool endsWith(const Object& object) const { + assert(data != nullptr || size == 0); + if (size == 0) { + return false; + } + + return *(data + (size - 1)) == object; + } + + // Return false if 'ArrayRef' does not contain 'other' at the end. + // The behavior is undefined unless both arrays were initialized. + bool endsWith(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0;; ++j) { + if (j == other.getSize()) { + return true; + } + + if (!(*(data + i + j) == *(other.getData() + j))) { + break; + } + } + } + + return false; + } + + // Looks for the first occurence of 'object' in 'ArrayRef', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless 'ArrayRef' was initialized. + Size find(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return i; + } + } + + return INVALID; + } + + // Looks for the first occurence of 'other' in 'ArrayRef', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless both arrays were initialized. + Size find(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.getSize()) { + return j; + } + + if (!(*(data + j + k) == *(other.getData() + k))) { + break; + } + } + } + } + + return INVALID; + } + + // Looks for the last occurence of 'object' in 'ArrayRef', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless 'ArrayRef' was initialized. + Size findLast(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + (size - 1 - i)) == object) { + return size - 1 - i; + } + } + + return INVALID; + } + + // Looks for the first occurence of 'other' in 'ArrayRef', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless both arrays were initialized. + Size findLast(ArrayView other) const { + assert(data != nullptr || size == 0); + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.getSize()) { + return i - j; + } + + if (!(*(data + (i - j + k)) == *(other.getData() + k))) { + break; + } + } + } + } + + return INVALID; + } + + // Returns subarray of 'headSize' first elements. + // The behavior is undefined unless 'ArrayRef' was initialized and 'headSize' <= 'size'. + ArrayRef head(Size headSize) const { + assert(data != nullptr || size == 0); + assert(headSize <= size); + return ArrayRef(data, headSize); + } + + // Returns subarray of 'tailSize' last elements. + // The behavior is undefined unless 'ArrayRef' was initialized and 'tailSize' <= 'size'. + ArrayRef tail(Size tailSize) const { + assert(data != nullptr || size == 0); + assert(tailSize <= size); + return ArrayRef(data + (size - tailSize), tailSize); + } + + // Returns 'ArrayRef' without 'headSize' first elements. + // The behavior is undefined unless 'ArrayRef' was initialized and 'headSize' <= 'size'. + ArrayRef unhead(Size headSize) const { + assert(data != nullptr || size == 0); + assert(headSize <= size); + return ArrayRef(data + headSize, size - headSize); + } + + // Returns 'ArrayRef' without 'tailSize' last elements. + // The behavior is undefined unless 'ArrayRef' was initialized and 'tailSize' <= 'size'. + ArrayRef untail(Size tailSize) const { + assert(data != nullptr || size == 0); + assert(tailSize <= size); + return ArrayRef(data, size - tailSize); + } + + // Returns subarray starting at 'startIndex' and contaning 'endIndex' - 'startIndex' elements. + // The behavior is undefined unless 'ArrayRef' was initialized and 'startIndex' <= 'endIndex' and 'endIndex' <= 'size'. + ArrayRef range(Size startIndex, Size endIndex) const { + assert(data != nullptr || size == 0); + assert(startIndex <= endIndex && endIndex <= size); + return ArrayRef(data + startIndex, endIndex - startIndex); + } + + // Returns subarray starting at 'startIndex' and contaning 'sliceSize' elements. + // The behavior is undefined unless 'ArrayRef' was initialized and 'startIndex' <= 'size' and 'startIndex' + 'sliceSize' <= 'size'. + ArrayRef slice(Size startIndex, Size sliceSize) const { + assert(data != nullptr || size == 0); + assert(startIndex <= size && startIndex + sliceSize <= size); + return ArrayRef(data + startIndex, sliceSize); + } + + // Copy 'object' to each element of 'ArrayRef'. + // The behavior is undefined unless 'ArrayRef' was initialized. + const ArrayRef& fill(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + *(data + i) = object; + } + + return *this; + } + + // Reverse 'ArrayRef' elements. + // The behavior is undefined unless 'ArrayRef' was initialized. + const ArrayRef& reverse() const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size / 2; ++i) { + Object object = *(data + i); + *(data + i) = *(data + (size - 1 - i)); + *(data + (size - 1 - i)) = object; + } + + return *this; + } + +protected: + Object* data; + Size size; +}; + +template const Size ArrayRef::INVALID = std::numeric_limits::max(); +template const ArrayRef ArrayRef::EMPTY(reinterpret_cast(1), 0); +template const ArrayRef ArrayRef::NIL(nullptr, 0); + +} diff --git a/src/Common/ArrayView.h b/src/Common/ArrayView.h new file mode 100755 index 0000000000..2fcc22ba6d --- /dev/null +++ b/src/Common/ArrayView.h @@ -0,0 +1,422 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include + +namespace Common { + +// 'ArrayView' is a pair of pointer to constant object of parametrized type and size. +// It is recommended to pass 'ArrayView' to procedures by value. +// 'ArrayView' supports 'EMPTY' and 'NIL' representations as follows: +// 'data' == 'nullptr' && 'size' == 0 - EMPTY NIL +// 'data' != 'nullptr' && 'size' == 0 - EMPTY NOTNIL +// 'data' == 'nullptr' && 'size' > 0 - Undefined +// 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL +// For signed integer 'Size', 'ArrayView' with 'size' < 0 is undefined. +template class ArrayView { +public: + typedef Object ObjectType; + typedef Size SizeType; + + const static Size INVALID; + const static ArrayView EMPTY; + const static ArrayView NIL; + + // Default constructor. + // Leaves object uninitialized. Any usage before initializing it is undefined. + ArrayView() +#ifndef NDEBUG + : data(nullptr), size(INVALID) // In debug mode, fill in object with invalid state (undefined). +#endif + { + } + + // Direct constructor. + // The behavior is undefined unless 'arrayData' != 'nullptr' || 'arraySize' == 0 + ArrayView(const Object* arrayData, Size arraySize) : data(arrayData), size(arraySize) { + assert(data != nullptr || size == 0); + } + + // Constructor from C array. + // The behavior is undefined unless 'arrayData' != 'nullptr' || 'arraySize' == 0. Input state can be malformed using poiner conversions. + template ArrayView(const Object(&arrayData)[arraySize]) : data(arrayData), size(arraySize) { + assert(data != nullptr || size == 0); + } + + // Copy constructor. + // Performs default action - bitwise copying of source object. + // The behavior is undefined unless 'other' 'ArrayView' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 + ArrayView(const ArrayView& other) : data(other.data), size(other.size) { + assert(data != nullptr || size == 0); + } + + // Destructor. + // No special action is performed. + ~ArrayView() { + } + + // Copy assignment operator. + // The behavior is undefined unless 'other' 'ArrayView' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 + ArrayView& operator=(const ArrayView& other) { + assert(other.data != nullptr || other.size == 0); + data = other.data; + size = other.size; + return *this; + } + + const Object* getData() const { + assert(data != nullptr || size == 0); + return data; + } + + Size getSize() const { + assert(data != nullptr || size == 0); + return size; + } + + // Return false if 'ArrayView' is not EMPTY. + // The behavior is undefined unless 'ArrayView' was initialized. + bool isEmpty() const { + assert(data != nullptr || size == 0); + return size == 0; + } + + // Return false if 'ArrayView' is not NIL. + // The behavior is undefined unless 'ArrayView' was initialized. + bool isNil() const { + assert(data != nullptr || size == 0); + return data == nullptr; + } + + // Get 'ArrayView' element by index. + // The behavior is undefined unless 'ArrayView' was initialized and 'index' < 'size'. + const Object& operator[](Size index) const { + assert(data != nullptr || size == 0); + assert(index < size); + return *(data + index); + } + + // Get first element. + // The behavior is undefined unless 'ArrayView' was initialized and 'size' > 0 + const Object& first() const { + assert(data != nullptr || size == 0); + assert(size > 0); + return *data; + } + + // Get last element. + // The behavior is undefined unless 'ArrayView' was initialized and 'size' > 0 + const Object& last() const { + assert(data != nullptr || size == 0); + assert(size > 0); + return *(data + (size - 1)); + } + + // Return a pointer to the first element. + // The behavior is undefined unless 'ArrayView' was initialized. + const Object* begin() const { + assert(data != nullptr || size == 0); + return data; + } + + // Return a pointer after the last element. + // The behavior is undefined unless 'ArrayView' was initialized. + const Object* end() const { + assert(data != nullptr || size == 0); + return data + size; + } + + // Compare elements of two arrays, return false if there is a difference. + // EMPTY and NIL arrays are considered equal. + // The behavior is undefined unless both arrays were initialized. + bool operator==(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size == other.size) { + for (Size i = 0;; ++i) { + if (i == size) { + return true; + } + + if (!(*(data + i) == *(other.data + i))) { + break; + } + } + } + + return false; + } + + // Compare elements two arrays, return false if there is no difference. + // EMPTY and NIL arrays are considered equal. + // The behavior is undefined unless both arrays were initialized. + bool operator!=(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size == other.size) { + for (Size i = 0;; ++i) { + if (i == size) { + return false; + } + + if (*(data + i) != *(other.data + i)) { + break; + } + } + } + + return true; + } + + // Return false if 'ArrayView' does not contain 'object' at the beginning. + // The behavior is undefined unless 'ArrayView' was initialized. + bool beginsWith(const Object& object) const { + assert(data != nullptr || size == 0); + if (size == 0) { + return false; + } + + return *data == object; + } + + // Return false if 'ArrayView' does not contain 'other' at the beginning. + // The behavior is undefined unless both arrays were initialized. + bool beginsWith(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + for (Size i = 0;; ++i) { + if (i == other.size) { + return true; + } + + if (!(*(data + i) == *(other.data + i))) { + break; + } + } + } + + return false; + } + + // Return false if 'ArrayView' does not contain 'object'. + // The behavior is undefined unless 'ArrayView' was initialized. + bool contains(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return true; + } + } + + return false; + } + + // Return false if 'ArrayView' does not contain 'other'. + // The behavior is undefined unless both arrays were initialized. + bool contains(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.size) { + return true; + } + + if (!(*(data + j + k) == *(other.data + k))) { + break; + } + } + } + } + + return false; + } + + // Return false if 'ArrayView' does not contain 'object' at the end. + // The behavior is undefined unless 'ArrayView' was initialized. + bool endsWith(const Object& object) const { + assert(data != nullptr || size == 0); + if (size == 0) { + return false; + } + + return *(data + (size - 1)) == object; + } + + // Return false if 'ArrayView' does not contain 'other' at the end. + // The behavior is undefined unless both arrays were initialized. + bool endsWith(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0;; ++j) { + if (j == other.size) { + return true; + } + + if (!(*(data + i + j) == *(other.data + j))) { + break; + } + } + } + + return false; + } + + // Looks for the first occurence of 'object' in 'ArrayView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless 'ArrayView' was initialized. + Size find(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return i; + } + } + + return INVALID; + } + + // Looks for the first occurence of 'other' in 'ArrayView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless both arrays were initialized. + Size find(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.size) { + return j; + } + + if (!(*(data + j + k) == *(other.data + k))) { + break; + } + } + } + } + + return INVALID; + } + + // Looks for the last occurence of 'object' in 'ArrayView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless 'ArrayView' was initialized. + Size findLast(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + (size - 1 - i)) == object) { + return size - 1 - i; + } + } + + return INVALID; + } + + // Looks for the first occurence of 'other' in 'ArrayView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless both arrays were initialized. + Size findLast(ArrayView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.size) { + return i - j; + } + + if (!(*(data + (i - j + k)) == *(other.data + k))) { + break; + } + } + } + } + + return INVALID; + } + + // Returns subarray of 'headSize' first elements. + // The behavior is undefined unless 'ArrayView' was initialized and 'headSize' <= 'size'. + ArrayView head(Size headSize) const { + assert(data != nullptr || size == 0); + assert(headSize <= size); + return ArrayView(data, headSize); + } + + // Returns subarray of 'tailSize' last elements. + // The behavior is undefined unless 'ArrayView' was initialized and 'tailSize' <= 'size'. + ArrayView tail(Size tailSize) const { + assert(data != nullptr || size == 0); + assert(tailSize <= size); + return ArrayView(data + (size - tailSize), tailSize); + } + + // Returns 'ArrayView' without 'headSize' first elements. + // The behavior is undefined unless 'ArrayView' was initialized and 'headSize' <= 'size'. + ArrayView unhead(Size headSize) const { + assert(data != nullptr || size == 0); + assert(headSize <= size); + return ArrayView(data + headSize, size - headSize); + } + + // Returns 'ArrayView' without 'tailSize' last elements. + // The behavior is undefined unless 'ArrayView' was initialized and 'tailSize' <= 'size'. + ArrayView untail(Size tailSize) const { + assert(data != nullptr || size == 0); + assert(tailSize <= size); + return ArrayView(data, size - tailSize); + } + + // Returns subarray starting at 'startIndex' and contaning 'endIndex' - 'startIndex' elements. + // The behavior is undefined unless 'ArrayView' was initialized and 'startIndex' <= 'endIndex' and 'endIndex' <= 'size'. + ArrayView range(Size startIndex, Size endIndex) const { + assert(data != nullptr || size == 0); + assert(startIndex <= endIndex && endIndex <= size); + return ArrayView(data + startIndex, endIndex - startIndex); + } + + // Returns subarray starting at 'startIndex' and contaning 'sliceSize' elements. + // The behavior is undefined unless 'ArrayView' was initialized and 'startIndex' <= 'size' and 'startIndex' + 'sliceSize' <= 'size'. + ArrayView slice(Size startIndex, Size sliceSize) const { + assert(data != nullptr || size == 0); + assert(startIndex <= size && startIndex + sliceSize <= size); + return ArrayView(data + startIndex, sliceSize); + } + +protected: + const Object* data; + Size size; +}; + +template const Size ArrayView::INVALID = std::numeric_limits::max(); +template const ArrayView ArrayView::EMPTY(reinterpret_cast(1), 0); +template const ArrayView ArrayView::NIL(nullptr, 0); + +} diff --git a/src/common/BlockingQueue.cpp b/src/Common/BlockingQueue.cpp similarity index 92% rename from src/common/BlockingQueue.cpp rename to src/Common/BlockingQueue.cpp index 3778bec4ac..3460308b6d 100644 --- a/src/common/BlockingQueue.cpp +++ b/src/Common/BlockingQueue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/common/BlockingQueue.h b/src/Common/BlockingQueue.h similarity index 97% rename from src/common/BlockingQueue.h rename to src/Common/BlockingQueue.h index 97f55b3776..71dd541864 100644 --- a/src/common/BlockingQueue.h +++ b/src/Common/BlockingQueue.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Common/ConsoleHandler.cpp b/src/Common/ConsoleHandler.cpp new file mode 100644 index 0000000000..1f22eeeb1d --- /dev/null +++ b/src/Common/ConsoleHandler.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ConsoleHandler.h" + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include + +using Common::Console::Color; + +namespace Common { + +///////////////////////////////////////////////////////////////////////////// +// AsyncConsoleReader +///////////////////////////////////////////////////////////////////////////// +AsyncConsoleReader::AsyncConsoleReader() : m_stop(true) { +} + +AsyncConsoleReader::~AsyncConsoleReader() { + stop(); +} + +void AsyncConsoleReader::start() { + m_stop = false; + m_thread = std::thread(std::bind(&AsyncConsoleReader::consoleThread, this)); +} + +bool AsyncConsoleReader::getline(std::string& line) { + return m_queue.pop(line); +} + +void AsyncConsoleReader::stop() { + + if (m_stop) { + return; // already stopping/stopped + } + + m_stop = true; + m_queue.close(); +#ifdef _WIN32 + ::CloseHandle(::GetStdHandle(STD_INPUT_HANDLE)); +#endif + + if (m_thread.joinable()) { + m_thread.join(); + } + + m_thread = std::thread(); +} + +bool AsyncConsoleReader::stopped() const { + return m_stop; +} + +void AsyncConsoleReader::consoleThread() { + + while (waitInput()) { + std::string line; + + if (!std::getline(std::cin, line)) { + break; + } + + if (!m_queue.push(line)) { + break; + } + } +} + +bool AsyncConsoleReader::waitInput() { +#ifndef _WIN32 + int stdin_fileno = ::fileno(stdin); + + while (!m_stop) { + fd_set read_set; + FD_ZERO(&read_set); + FD_SET(stdin_fileno, &read_set); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + + int retval = ::select(stdin_fileno + 1, &read_set, NULL, NULL, &tv); + + if (retval == -1 && errno == EINTR) { + continue; + } + + if (retval < 0) { + return false; + } + + if (retval > 0) { + return true; + } + } +#endif + + return !m_stop; +} + +///////////////////////////////////////////////////////////////////////////// +// ConsoleHandler +///////////////////////////////////////////////////////////////////////////// +ConsoleHandler::~ConsoleHandler() { + stop(); +} + +void ConsoleHandler::start(bool startThread, const std::string& prompt, Console::Color promptColor) { + m_prompt = prompt; + m_promptColor = promptColor; + m_consoleReader.start(); + + if (startThread) { + m_thread = std::thread(std::bind(&ConsoleHandler::handlerThread, this)); + } else { + handlerThread(); + } +} + +void ConsoleHandler::stop() { + requestStop(); + wait(); +} + +void ConsoleHandler::wait() { + + try { + if (m_thread.joinable()) { + m_thread.join(); + } + } catch (std::exception& e) { + std::cerr << "Exception in ConsoleHandler::wait - " << e.what() << std::endl; + } +} + +void ConsoleHandler::requestStop() { + m_consoleReader.stop(); +} + +std::string ConsoleHandler::getUsage() const { + + if (m_handlers.empty()) { + return std::string(); + } + + std::stringstream ss; + + size_t maxlen = std::max_element(m_handlers.begin(), m_handlers.end(), []( + CommandHandlersMap::const_reference& a, CommandHandlersMap::const_reference& b) { + return a.first.size() < b.first.size(); })->first.size(); + + for (auto& x : m_handlers) { + ss << std::left << std::setw(maxlen + 3) << x.first << x.second.second << std::endl; + } + + return ss.str(); +} + +void ConsoleHandler::setHandler(const std::string& command, const ConsoleCommandHandler& handler, const std::string& usage) { + m_handlers[command] = std::make_pair(handler, usage); +} + +bool ConsoleHandler::runCommand(const std::vector& cmdAndArgs) { + if (cmdAndArgs.size() == 0) { + return false; + } + + const auto& cmd = cmdAndArgs.front(); + auto hIter = m_handlers.find(cmd); + + if (hIter == m_handlers.end()) { + std::cout << "Unknown command: " << cmd << std::endl; + return false; + } + + std::vector args(cmdAndArgs.begin() + 1, cmdAndArgs.end()); + hIter->second.first(args); + return true; +} + +void ConsoleHandler::handleCommand(const std::string& cmd) { + std::vector args; + boost::split(args, cmd, boost::is_any_of(" "), boost::token_compress_on); + runCommand(args); +} + +void ConsoleHandler::handlerThread() { + std::string line; + + while(!m_consoleReader.stopped()) { + try { + if (!m_prompt.empty()) { + if (m_promptColor != Color::Default) { + Console::setTextColor(m_promptColor); + } + + std::cout << m_prompt; + std::cout.flush(); + + if (m_promptColor != Color::Default) { + Console::setTextColor(Color::Default); + } + } + + if (!m_consoleReader.getline(line)) { + break; + } + + boost::algorithm::trim(line); + if (!line.empty()) { + handleCommand(line); + } + + } catch (std::exception&) { + // ignore errors + } + } +} + +} diff --git a/src/Common/ConsoleHandler.h b/src/Common/ConsoleHandler.h new file mode 100644 index 0000000000..6ee48b9353 --- /dev/null +++ b/src/Common/ConsoleHandler.h @@ -0,0 +1,86 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "BlockingQueue.h" +#include "ConsoleTools.h" + +namespace Common { + +class AsyncConsoleReader { + +public: + + AsyncConsoleReader(); + ~AsyncConsoleReader(); + + void start(); + bool getline(std::string& line); + void stop(); + bool stopped() const; + +private: + + void consoleThread(); + bool waitInput(); + + std::atomic m_stop; + std::thread m_thread; + BlockingQueue m_queue; +}; + + +class ConsoleHandler { +public: + + ~ConsoleHandler(); + + typedef std::function &)> ConsoleCommandHandler; + + std::string getUsage() const; + void setHandler(const std::string& command, const ConsoleCommandHandler& handler, const std::string& usage = ""); + void requestStop(); + bool runCommand(const std::vector& cmdAndArgs); + + void start(bool startThread = true, const std::string& prompt = "", Console::Color promptColor = Console::Color::Default); + void stop(); + void wait(); + +private: + + typedef std::map> CommandHandlersMap; + + virtual void handleCommand(const std::string& cmd); + + void handlerThread(); + + std::thread m_thread; + std::string m_prompt; + Console::Color m_promptColor = Console::Color::Default; + CommandHandlersMap m_handlers; + AsyncConsoleReader m_consoleReader; +}; + +} diff --git a/src/Common/ConsoleTools.cpp b/src/Common/ConsoleTools.cpp new file mode 100644 index 0000000000..4ccbffc063 --- /dev/null +++ b/src/Common/ConsoleTools.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ConsoleTools.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace Common { namespace Console { + +void setTextColor(Color color) { + + if (color > Color::BrightMagenta) { + color = Color::Default; + } + +#ifdef _WIN32 + + static WORD winColors[] = { + // default + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + // main + FOREGROUND_BLUE, + FOREGROUND_GREEN, + FOREGROUND_RED, + FOREGROUND_RED | FOREGROUND_GREEN, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + FOREGROUND_GREEN | FOREGROUND_BLUE, + FOREGROUND_RED | FOREGROUND_BLUE, + // bright + FOREGROUND_BLUE | FOREGROUND_INTENSITY, + FOREGROUND_GREEN | FOREGROUND_INTENSITY, + FOREGROUND_RED | FOREGROUND_INTENSITY, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY + }; + + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), winColors[static_cast(color)]); + +#else + + static const char* ansiColors[] = { + // default + "\033[0m", + // main + "\033[0;34m", + "\033[0;32m", + "\033[0;31m", + "\033[0;33m", + "\033[0;37m", + "\033[0;36m", + "\033[0;35m", + // bright + "\033[1;34m", + "\033[1;32m", + "\033[1;31m", + "\033[1;33m", + "\033[1;37m", + "\033[1;36m", + "\033[1;35m" + }; + + std::cout << ansiColors[static_cast(color)]; + +#endif + +} + +}} diff --git a/src/Common/ConsoleTools.h b/src/Common/ConsoleTools.h new file mode 100644 index 0000000000..fc334313be --- /dev/null +++ b/src/Common/ConsoleTools.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace Common { namespace Console { + +enum class Color : uint8_t { + Default, + + Blue, + Green, + Red, + Yellow, + White, + Cyan, + Magenta, + + BrightBlue, + BrightGreen, + BrightRed, + BrightYellow, + BrightWhite, + BrightCyan, + BrightMagenta +}; + +void setTextColor(Color color); + +}} diff --git a/src/Platform/Windows/System/InterruptedException.cpp b/src/Common/IInputStream.cpp similarity index 87% rename from src/Platform/Windows/System/InterruptedException.cpp rename to src/Common/IInputStream.cpp index 0e268f4f6f..8396fdbd8e 100755 --- a/src/Platform/Windows/System/InterruptedException.cpp +++ b/src/Common/IInputStream.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,4 +15,4 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "InterruptedException.h" +#include "IInputStream.h" diff --git a/src/Common/IInputStream.h b/src/Common/IInputStream.h new file mode 100755 index 0000000000..2bb867cf5a --- /dev/null +++ b/src/Common/IInputStream.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace Common { + +class IInputStream { +public: + virtual ~IInputStream() { } + virtual std::size_t readSome(void* data, std::size_t size) = 0; +}; + +} diff --git a/src/Platform/Linux/System/InterruptedException.cpp b/src/Common/IOutputStream.cpp similarity index 87% rename from src/Platform/Linux/System/InterruptedException.cpp rename to src/Common/IOutputStream.cpp index 0e268f4f6f..ea53536049 100755 --- a/src/Platform/Linux/System/InterruptedException.cpp +++ b/src/Common/IOutputStream.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,4 +15,4 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "InterruptedException.h" +#include "IOutputStream.h" diff --git a/src/Common/IOutputStream.h b/src/Common/IOutputStream.h new file mode 100755 index 0000000000..1f5ef4f99e --- /dev/null +++ b/src/Common/IOutputStream.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace Common { + +class IOutputStream { +public: + virtual ~IOutputStream() { } + virtual std::size_t writeSome(const void* data, std::size_t size) = 0; +}; + +} diff --git a/src/Common/JsonValue.cpp b/src/Common/JsonValue.cpp new file mode 100644 index 0000000000..e3dd3ff305 --- /dev/null +++ b/src/Common/JsonValue.cpp @@ -0,0 +1,945 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonValue.h" +#include +#include + +namespace Common { + +JsonValue::JsonValue() : type(NIL) { +} + +JsonValue::JsonValue(const JsonValue& other) { + switch (other.type) { + case ARRAY: + new(valueArray)Array(*reinterpret_cast(other.valueArray)); + break; + case BOOL: + valueBool = other.valueBool; + break; + case INTEGER: + valueInteger = other.valueInteger; + break; + case NIL: + break; + case OBJECT: + new(valueObject)Object(*reinterpret_cast(other.valueObject)); + break; + case REAL: + valueReal = other.valueReal; + break; + case STRING: + new(valueString)String(*reinterpret_cast(other.valueString)); + break; + } + + type = other.type; +} + +JsonValue::JsonValue(JsonValue&& other) { + switch (other.type) { + case ARRAY: + new(valueArray)Array(std::move(*reinterpret_cast(other.valueArray))); + reinterpret_cast(other.valueArray)->~Array(); + break; + case BOOL: + valueBool = other.valueBool; + break; + case INTEGER: + valueInteger = other.valueInteger; + break; + case NIL: + break; + case OBJECT: + new(valueObject)Object(std::move(*reinterpret_cast(other.valueObject))); + reinterpret_cast(other.valueObject)->~Object(); + break; + case REAL: + valueReal = other.valueReal; + break; + case STRING: + new(valueString)String(std::move(*reinterpret_cast(other.valueString))); + reinterpret_cast(other.valueString)->~String(); + break; + } + + type = other.type; + other.type = NIL; +} + +JsonValue::JsonValue(Type valueType) { + switch (valueType) { + case ARRAY: + new(valueArray)Array; + break; + case NIL: + break; + case OBJECT: + new(valueObject)Object; + break; + case STRING: + new(valueString)String; + break; + default: + throw std::runtime_error("Invalid JsonValue type for constructor"); + } + + type = valueType; +} + +JsonValue::JsonValue(const Array& value) { + new(valueArray)Array(value); + type = ARRAY; +} + +JsonValue::JsonValue(Array&& value) { + new(valueArray)Array(std::move(value)); + type = ARRAY; +} + +JsonValue::JsonValue(Bool value) : type(BOOL), valueBool(value) { +} + +JsonValue::JsonValue(Integer value) : type(INTEGER), valueInteger(value) { +} + +JsonValue::JsonValue(Nil) : type(NIL) { +} + +JsonValue::JsonValue(const Object& value) { + new(valueObject)Object(value); + type = OBJECT; +} + +JsonValue::JsonValue(Object&& value) { + new(valueObject)Object(std::move(value)); + type = OBJECT; +} + +JsonValue::JsonValue(Real value) : type(REAL), valueReal(value) { +} + +JsonValue::JsonValue(const String& value) { + new(valueString)String(value); + type = STRING; +} + +JsonValue::JsonValue(String&& value) { + new(valueString)String(std::move(value)); + type = STRING; +} + +JsonValue::~JsonValue() { + destructValue(); +} + +JsonValue& JsonValue::operator=(const JsonValue& other) { + if (type != other.type) { + destructValue(); + switch (other.type) { + case ARRAY: + type = NIL; + new(valueArray)Array(*reinterpret_cast(other.valueArray)); + break; + case BOOL: + valueBool = other.valueBool; + break; + case INTEGER: + valueInteger = other.valueInteger; + break; + case NIL: + break; + case OBJECT: + type = NIL; + new(valueObject)Object(*reinterpret_cast(other.valueObject)); + break; + case REAL: + valueReal = other.valueReal; + break; + case STRING: + type = NIL; + new(valueString)String(*reinterpret_cast(other.valueString)); + break; + } + + type = other.type; + } else { + switch (type) { + case ARRAY: + *reinterpret_cast(valueArray) = *reinterpret_cast(other.valueArray); + break; + case BOOL: + valueBool = other.valueBool; + break; + case INTEGER: + valueInteger = other.valueInteger; + break; + case NIL: + break; + case OBJECT: + *reinterpret_cast(valueObject) = *reinterpret_cast(other.valueObject); + break; + case REAL: + valueReal = other.valueReal; + break; + case STRING: + *reinterpret_cast(valueString) = *reinterpret_cast(other.valueString); + break; + } + } + + return *this; +} + +JsonValue& JsonValue::operator=(JsonValue&& other) { + if (type != other.type) { + destructValue(); + switch (other.type) { + case ARRAY: + type = NIL; + new(valueArray)Array(std::move(*reinterpret_cast(other.valueArray))); + reinterpret_cast(other.valueArray)->~Array(); + break; + case BOOL: + valueBool = other.valueBool; + break; + case INTEGER: + valueInteger = other.valueInteger; + break; + case NIL: + break; + case OBJECT: + type = NIL; + new(valueObject)Object(std::move(*reinterpret_cast(other.valueObject))); + reinterpret_cast(other.valueObject)->~Object(); + break; + case REAL: + valueReal = other.valueReal; + break; + case STRING: + type = NIL; + new(valueString)String(std::move(*reinterpret_cast(other.valueString))); + reinterpret_cast(other.valueString)->~String(); + break; + } + + type = other.type; + } else { + switch (type) { + case ARRAY: + *reinterpret_cast(valueArray) = std::move(*reinterpret_cast(other.valueArray)); + reinterpret_cast(other.valueArray)->~Array(); + break; + case BOOL: + valueBool = other.valueBool; + break; + case INTEGER: + valueInteger = other.valueInteger; + break; + case NIL: + break; + case OBJECT: + *reinterpret_cast(valueObject) = std::move(*reinterpret_cast(other.valueObject)); + reinterpret_cast(other.valueObject)->~Object(); + break; + case REAL: + valueReal = other.valueReal; + break; + case STRING: + *reinterpret_cast(valueString) = std::move(*reinterpret_cast(other.valueString)); + reinterpret_cast(other.valueString)->~String(); + break; + } + } + + other.type = NIL; + return *this; +} + +JsonValue& JsonValue::operator=(const Array& value) { + if (type != ARRAY) { + destructValue(); + type = NIL; + new(valueArray)Array(value); + type = ARRAY; + } else { + *reinterpret_cast(valueArray) = value; + } + + return *this; +} + +JsonValue& JsonValue::operator=(Array&& value) { + if (type != ARRAY) { + destructValue(); + type = NIL; + new(valueArray)Array(std::move(value)); + type = ARRAY; + } else { + *reinterpret_cast(valueArray) = std::move(value); + } + + return *this; +} + +//JsonValue& JsonValue::operator=(Bool value) { +// if (type != BOOL) { +// destructValue(); +// type = BOOL; +// } +// +// valueBool = value; +// return *this; +//} + +JsonValue& JsonValue::operator=(Integer value) { + if (type != INTEGER) { + destructValue(); + type = INTEGER; + } + + valueInteger = value; + return *this; +} + +JsonValue& JsonValue::operator=(Nil) { + if (type != NIL) { + destructValue(); + type = NIL; + } + + return *this; +} + +JsonValue& JsonValue::operator=(const Object& value) { + if (type != OBJECT) { + destructValue(); + type = NIL; + new(valueObject)Object(value); + type = OBJECT; + } else { + *reinterpret_cast(valueObject) = value; + } + + return *this; +} + +JsonValue& JsonValue::operator=(Object&& value) { + if (type != OBJECT) { + destructValue(); + type = NIL; + new(valueObject)Object(std::move(value)); + type = OBJECT; + } else { + *reinterpret_cast(valueObject) = std::move(value); + } + + return *this; +} + +JsonValue& JsonValue::operator=(Real value) { + if (type != REAL) { + destructValue(); + type = REAL; + } + + valueReal = value; + return *this; +} + +JsonValue& JsonValue::operator=(const String& value) { + if (type != STRING) { + destructValue(); + type = NIL; + new(valueString)String(value); + type = STRING; + } else { + *reinterpret_cast(valueString) = value; + } + + return *this; +} + +JsonValue& JsonValue::operator=(String&& value) { + if (type != STRING) { + destructValue(); + type = NIL; + new(valueString)String(std::move(value)); + type = STRING; + } else { + *reinterpret_cast(valueString) = std::move(value); + } + + return *this; +} + +bool JsonValue::isArray() const { + return type == ARRAY; +} + +bool JsonValue::isBool() const { + return type == BOOL; +} + +bool JsonValue::isInteger() const { + return type == INTEGER; +} + +bool JsonValue::isNil() const { + return type == NIL; +} + +bool JsonValue::isObject() const { + return type == OBJECT; +} + +bool JsonValue::isReal() const { + return type == REAL; +} + +bool JsonValue::isString() const { + return type == STRING; +} + +JsonValue::Type JsonValue::getType() const { + return type; +} + +JsonValue::Array& JsonValue::getArray() { + if (type != ARRAY) { + throw std::runtime_error("JsonValue type is not ARRAY"); + } + + return *reinterpret_cast(valueArray); +} + +const JsonValue::Array& JsonValue::getArray() const { + if (type != ARRAY) { + throw std::runtime_error("JsonValue type is not ARRAY"); + } + + return *reinterpret_cast(valueArray); +} + +JsonValue::Bool JsonValue::getBool() const { + if (type != BOOL) { + throw std::runtime_error("JsonValue type is not BOOL"); + } + + return valueBool; +} + +JsonValue::Integer JsonValue::getInteger() const { + if (type != INTEGER) { + throw std::runtime_error("JsonValue type is not INTEGER"); + } + + return valueInteger; +} + +JsonValue::Object& JsonValue::getObject() { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return *reinterpret_cast(valueObject); +} + +const JsonValue::Object& JsonValue::getObject() const { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return *reinterpret_cast(valueObject); +} + +JsonValue::Real JsonValue::getReal() const { + if (type != REAL) { + throw std::runtime_error("JsonValue type is not REAL"); + } + + return valueReal; +} + +JsonValue::String& JsonValue::getString() { + if (type != STRING) { + throw std::runtime_error("JsonValue type is not STRING"); + } + + return *reinterpret_cast(valueString); +} + +const JsonValue::String& JsonValue::getString() const { + if (type != STRING) { + throw std::runtime_error("JsonValue type is not STRING"); + } + + return *reinterpret_cast(valueString); +} + +std::size_t JsonValue::size() const { + switch (type) { + case ARRAY: + return reinterpret_cast(valueArray)->size(); + case OBJECT: + return reinterpret_cast(valueObject)->size(); + default: + throw std::runtime_error("JsonValue type is not ARRAY or OBJECT"); + } +} + +JsonValue& JsonValue::operator[](std::size_t index) { + if (type != ARRAY) { + throw std::runtime_error("JsonValue type is not ARRAY"); + } + + return reinterpret_cast(valueArray)->at(index); +} + +const JsonValue& JsonValue::operator[](std::size_t index) const { + if (type != ARRAY) { + throw std::runtime_error("JsonValue type is not ARRAY"); + } + + return reinterpret_cast(valueArray)->at(index); +} + +JsonValue& JsonValue::pushBack(const JsonValue& value) { + if (type != ARRAY) { + throw std::runtime_error("JsonValue type is not ARRAY"); + } + + reinterpret_cast(valueArray)->emplace_back(value); + return reinterpret_cast(valueArray)->back(); +} + +JsonValue& JsonValue::pushBack(JsonValue&& value) { + if (type != ARRAY) { + throw std::runtime_error("JsonValue type is not ARRAY"); + } + + reinterpret_cast(valueArray)->emplace_back(std::move(value)); + return reinterpret_cast(valueArray)->back(); +} + +JsonValue& JsonValue::operator()(const Key& key) { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return reinterpret_cast(valueObject)->at(key); +} + +const JsonValue& JsonValue::operator()(const Key& key) const { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return reinterpret_cast(valueObject)->at(key); +} + +std::size_t JsonValue::count(const Key& key) const { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return reinterpret_cast(valueObject)->count(key); +} + +JsonValue& JsonValue::insert(const Key& key, const JsonValue& value) { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return reinterpret_cast(valueObject)->emplace(key, value).first->second; +} + +JsonValue& JsonValue::insert(const Key& key, JsonValue&& value) { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return reinterpret_cast(valueObject)->emplace(key, std::move(value)).first->second; +} + +std::size_t JsonValue::erase(const Key& key) { + if (type != OBJECT) { + throw std::runtime_error("JsonValue type is not OBJECT"); + } + + return reinterpret_cast(valueObject)->erase(key); +} + +JsonValue JsonValue::fromString(const std::string& source) { + JsonValue jsonValue; + std::istringstream stream(source); + stream >> jsonValue; + if (stream.fail()) { + throw std::runtime_error("Unable to parse JsonValue"); + } + + return jsonValue; +} + +std::string JsonValue::toString() const { + std::ostringstream stream; + stream << *this; + return stream.str(); +} + +std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { + switch (jsonValue.type) { + case JsonValue::ARRAY: { + const JsonValue::Array& array = *reinterpret_cast(jsonValue.valueArray); + out << '['; + if (array.size() > 0) { + out << array[0]; + for (std::size_t i = 1; i < array.size(); ++i) { + out << ',' << array[i]; + } + } + + out << ']'; + break; + } + case JsonValue::BOOL: + out << (jsonValue.valueBool ? "true" : "false"); + break; + case JsonValue::INTEGER: + out << jsonValue.valueInteger; + break; + case JsonValue::NIL: + out << "null"; + break; + case JsonValue::OBJECT: { + const JsonValue::Object& object = *reinterpret_cast(jsonValue.valueObject); + out << '{'; + auto iter = object.begin(); + if (iter != object.end()) { + out << '"' << iter->first << "\":" << iter->second; + ++iter; + for (; iter != object.end(); ++iter) { + out << ",\"" << iter->first << "\":" << iter->second; + } + } + + out << '}'; + break; + } + case JsonValue::REAL: { + std::ostringstream stream; + stream << std::fixed << std::setprecision(11) << jsonValue.valueReal; + std::string value = stream.str(); + while (value.size() > 1 && value[value.size() - 2] != '.' && value[value.size() - 1] == '0') { + value.resize(value.size() - 1); + } + + out << value; + break; + } + case JsonValue::STRING: + out << '"' << *reinterpret_cast(jsonValue.valueString) << '"'; + break; + } + + return out; +} + +std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { + char c; + in >> c; + while (isspace(c)) in >> c; + if (c == '[') { + jsonValue.readArray(in); + } else if (c == 't') { + jsonValue.readTrue(in); + } else if (c == 'f') { + jsonValue.readFalse(in); + } else if ((c == '-') || (c >= '0' && c <= '9')) { + jsonValue.readNumber(in, c); + } else if (c == 'n') { + jsonValue.readNull(in); + } else if (c == '{') { + jsonValue.readObject(in); + } else if (c == '"') { + jsonValue.readString(in); + } else { + throw std::runtime_error("Unable to parse"); + } + + return in; +} + +void JsonValue::destructValue() { + switch (type) { + case ARRAY: + reinterpret_cast(valueArray)->~Array(); + break; + case OBJECT: + reinterpret_cast(valueObject)->~Object(); + break; + case STRING: + reinterpret_cast(valueString)->~String(); + break; + default: + break; + } +} + +void JsonValue::readArray(std::istream& in) { + char c; + JsonValue::Array value; + + c = static_cast(in.peek()); + for (;;) { + if (!isspace(in.peek())) break; + in.read(&c, 1); + } + + if (in.peek() != ']') { + for (;;) { + value.resize(value.size() + 1); + in >> value.back(); + in >> c; + while (isspace(c)) in >> c; + if (c == ']') { + break; + } + + if (c != ',') { + throw std::runtime_error("Unable to parse"); + } + } + } else { + in.read(&c, 1); + } + + if (type != JsonValue::ARRAY) { + destructValue(); + type = JsonValue::NIL; + new(valueArray)JsonValue::Array; + type = JsonValue::ARRAY; + } + + reinterpret_cast(valueArray)->swap(value); +} + +void JsonValue::readTrue(std::istream& in) { + char data[3]; + in.read(data, 3); + if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') { + throw std::runtime_error("Unable to parse"); + } + + if (type != JsonValue::BOOL) { + destructValue(); + type = JsonValue::BOOL; + } + + valueBool = true; +} + +void JsonValue::readFalse(std::istream& in) { + char data[4]; + in.read(data, 4); + if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') { + throw std::runtime_error("Unable to parse"); + } + + if (type != JsonValue::BOOL) { + destructValue(); + type = JsonValue::BOOL; + } + + valueBool = false; +} + +void JsonValue::readNull(std::istream& in) { + char data[3]; + in.read(data, 3); + if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') { + throw std::runtime_error("Unable to parse"); + } + + if (type != JsonValue::NIL) { + destructValue(); + type = JsonValue::NIL; + } +} + +void JsonValue::readNumber(std::istream& in, char c) { + std::string text; + text += c; + std::size_t dots = 0; + for (;;) { + int i = in.peek(); + if (i >= '0' && i <= '9') { + in.read(&c, 1); + text += c; + } else if (i == '.') { + in.read(&c, 1); + text += '.'; + ++dots; + } else { + break; + } + } + + if (dots > 0) { + if (dots > 1) { + throw std::runtime_error("Unable to parse"); + } + + int i = in.peek(); + if (in.peek() == 'e') { + in.read(&c, 1); + text += c; + i = in.peek(); + if (i == '+') { + in.read(&c, 1); + text += c; + i = in.peek(); + } else if (i == '-') { + in.read(&c, 1); + text += c; + i = in.peek(); + } + + if (i < '0' || i > '9') { + throw std::runtime_error("Unable to parse"); + } + + do { + in.read(&c, 1); + text += c; + i = in.peek(); + } while (i >= '0' && i <= '9'); + } + + Real value; + std::istringstream(text) >> value; + if (type != REAL) { + destructValue(); + type = REAL; + } + + valueReal = value; + } else { + if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) { + throw std::runtime_error("Unable to parse"); + } + + Integer value; + std::istringstream(text) >> value; + if (type != INTEGER) { + destructValue(); + type = INTEGER; + } + + valueInteger = value; + } +} + +void JsonValue::readObject(std::istream& in) { + char c; + JsonValue::Object value; + in >> c; + while (isspace(c)) in >> c; + + if (c != '}') { + std::string name; + for (;;) { + if (c != '"') { + throw std::runtime_error("Unable to parse"); + } + + name.clear(); + for (;;) { + in >> c; + if (c == '"') { + break; + } + + if (c == '\\') { + name += c; + in >> c; + } + + name += c; + } + + in >> c; + while (isspace(c)) in >> c; + if (c != ':') { + throw std::runtime_error("Unable to parse"); + } + + in >> value[name]; + in >> c; + while (isspace(c)) in >> c; + if (c == '}') { + break; + } + + if (c != ',') { + throw std::runtime_error("Unable to parse"); + } + in >> c; + while (isspace(c)) in >> c; + } + } + + if (type != JsonValue::OBJECT) { + destructValue(); + type = JsonValue::NIL; + new(valueObject)JsonValue::Object; + type = JsonValue::OBJECT; + } + + reinterpret_cast(valueObject)->swap(value); +} + +void JsonValue::readString(std::istream& in) { + char c; + String value; + + for (;;) { + in.read(&c, 1); + if (c == '"') { + break; + } + + if (c == '\\') { + value += c; + in >> c; + } + + value += c; + } + + if (type != JsonValue::STRING) { + destructValue(); + type = JsonValue::NIL; + new(valueString)String; + type = JsonValue::STRING; + } + + reinterpret_cast(valueString)->swap(value); +} + +} diff --git a/src/Common/JsonValue.h b/src/Common/JsonValue.h new file mode 100644 index 0000000000..41f2202c53 --- /dev/null +++ b/src/Common/JsonValue.h @@ -0,0 +1,156 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +namespace Common { + +class JsonValue { +public: + typedef std::string Key; + + typedef std::vector Array; + typedef bool Bool; + typedef int64_t Integer; + typedef std::nullptr_t Nil; + typedef std::map Object; + typedef double Real; + typedef std::string String; + + enum Type { + ARRAY, + BOOL, + INTEGER, + NIL, + OBJECT, + REAL, + STRING + }; + + JsonValue(); + JsonValue(const JsonValue& other); + JsonValue(JsonValue&& other); + JsonValue(Type valueType); + JsonValue(const Array& value); + JsonValue(Array&& value); + explicit JsonValue(Bool value); + JsonValue(Integer value); + JsonValue(Nil value); + JsonValue(const Object& value); + JsonValue(Object&& value); + JsonValue(Real value); + JsonValue(const String& value); + JsonValue(String&& value); + template JsonValue(const char(&value)[size]) { + new(valueString)String(value, size - 1); + type = STRING; + } + + ~JsonValue(); + + JsonValue& operator=(const JsonValue& other); + JsonValue& operator=(JsonValue&& other); + JsonValue& operator=(const Array& value); + JsonValue& operator=(Array&& value); + //JsonValue& operator=(Bool value); + JsonValue& operator=(Integer value); + JsonValue& operator=(Nil value); + JsonValue& operator=(const Object& value); + JsonValue& operator=(Object&& value); + JsonValue& operator=(Real value); + JsonValue& operator=(const String& value); + JsonValue& operator=(String&& value); + template JsonValue& operator=(const char(&value)[size]) { + if (type != STRING) { + destructValue(); + type = NIL; + new(valueString)String(value, size - 1); + type = STRING; + } else { + reinterpret_cast(valueString)->assign(value, size - 1); + } + + return *this; + } + + bool isArray() const; + bool isBool() const; + bool isInteger() const; + bool isNil() const; + bool isObject() const; + bool isReal() const; + bool isString() const; + + Type getType() const; + Array& getArray(); + const Array& getArray() const; + Bool getBool() const; + Integer getInteger() const; + Object& getObject(); + const Object& getObject() const; + Real getReal() const; + String& getString(); + const String& getString() const; + + std::size_t size() const; + + JsonValue& operator[](std::size_t index); + const JsonValue& operator[](std::size_t index) const; + JsonValue& pushBack(const JsonValue& value); + JsonValue& pushBack(JsonValue&& value); + + JsonValue& operator()(const Key& key); + const JsonValue& operator()(const Key& key) const; + std::size_t count(const Key& key) const; + JsonValue& insert(const Key& key, const JsonValue& value); + JsonValue& insert(const Key& key, JsonValue&& value); + std::size_t erase(const Key& key); + + static JsonValue fromString(const std::string& source); + std::string toString() const; + + friend std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue); + friend std::istream& operator>>(std::istream& in, JsonValue& jsonValue); + +private: + Type type; + union { + uint8_t valueArray[sizeof(Array)]; + Bool valueBool; + Integer valueInteger; + uint8_t valueObject[sizeof(Object)]; + Real valueReal; + uint8_t valueString[sizeof(std::string)]; + }; + + void destructValue(); + void readArray(std::istream& in); + void readTrue(std::istream& in); + void readFalse(std::istream& in); + void readNull(std::istream& in); + void readNumber(std::istream& in, char c); + void readObject(std::istream& in); + void readString(std::istream& in); +}; + +} diff --git a/src/common/ObserverManager.h b/src/Common/ObserverManager.h similarity index 98% rename from src/common/ObserverManager.h rename to src/Common/ObserverManager.h index b1e1e17c49..c383113b5a 100644 --- a/src/common/ObserverManager.h +++ b/src/Common/ObserverManager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Common/PathTools.cpp b/src/Common/PathTools.cpp new file mode 100644 index 0000000000..fbf7da816c --- /dev/null +++ b/src/Common/PathTools.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "PathTools.h" +#include + +namespace { + +const char GENERIC_PATH_SEPARATOR = '/'; + +#ifdef _WIN32 +const char NATIVE_PATH_SEPARATOR = '\\'; +#else +const char NATIVE_PATH_SEPARATOR = '/'; +#endif + + +std::string::size_type findExtensionPosition(const std::string& filename) { + auto pos = filename.rfind('.'); + + if (pos != std::string::npos) { + auto slashPos = filename.rfind(GENERIC_PATH_SEPARATOR); + if (slashPos != std::string::npos && slashPos > pos) { + return std::string::npos; + } + } + + return pos; +} + +} // anonymous namespace + +namespace Common { + +std::string NativePathToGeneric(const std::string& nativePath) { + if (GENERIC_PATH_SEPARATOR == NATIVE_PATH_SEPARATOR) { + return nativePath; + } + std::string genericPath(nativePath); + std::replace(genericPath.begin(), genericPath.end(), NATIVE_PATH_SEPARATOR, GENERIC_PATH_SEPARATOR); + return genericPath; +} + +std::string GetPathDirectory(const std::string& path) { + auto slashPos = path.rfind(GENERIC_PATH_SEPARATOR); + if (slashPos == std::string::npos) { + return std::string(); + } + return path.substr(0, slashPos); +} + +std::string GetPathFilename(const std::string& path) { + auto slashPos = path.rfind(GENERIC_PATH_SEPARATOR); + if (slashPos == std::string::npos) { + return path; + } + return path.substr(slashPos + 1); +} + +void SplitPath(const std::string& path, std::string& directory, std::string& filename) { + directory = GetPathDirectory(path); + filename = GetPathFilename(path); +} + +std::string CombinePath(const std::string& path1, const std::string& path2) { + return path1 + GENERIC_PATH_SEPARATOR + path2; +} + +std::string ReplaceExtenstion(const std::string& path, const std::string& extension) { + return RemoveExtension(path) + extension; +} + +std::string GetExtension(const std::string& path) { + auto pos = findExtensionPosition(path); + if (pos != std::string::npos) { + return path.substr(pos); + } + return std::string(); +} + +std::string RemoveExtension(const std::string& filename) { + auto pos = findExtensionPosition(filename); + + if (pos == std::string::npos) { + return filename; + } + + return filename.substr(0, pos); +} + + +bool HasParentPath(const std::string& path) { + return path.find(GENERIC_PATH_SEPARATOR) != std::string::npos; +} + + +} diff --git a/src/Common/PathTools.h b/src/Common/PathTools.h new file mode 100644 index 0000000000..d8949151b9 --- /dev/null +++ b/src/Common/PathTools.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace Common { + +std::string NativePathToGeneric(const std::string& nativePath); + +std::string GetPathDirectory(const std::string& path); +std::string GetPathFilename(const std::string& path); +void SplitPath(const std::string& path, std::string& directory, std::string& filename); + +std::string CombinePath(const std::string& path1, const std::string& path2); +std::string GetExtension(const std::string& path); +std::string RemoveExtension(const std::string& path); +std::string ReplaceExtenstion(const std::string& path, const std::string& extension); +bool HasParentPath(const std::string& path); + +} diff --git a/src/common/ShuffleGenerator.h b/src/Common/ShuffleGenerator.h similarity index 96% rename from src/common/ShuffleGenerator.h rename to src/Common/ShuffleGenerator.h index 367f47900a..862625ff21 100644 --- a/src/common/ShuffleGenerator.h +++ b/src/Common/ShuffleGenerator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -67,4 +67,3 @@ class ShuffleGenerator { const T N; Gen generator; }; - diff --git a/src/common/SignalHandler.cpp b/src/Common/SignalHandler.cpp similarity index 52% rename from src/common/SignalHandler.cpp rename to src/Common/SignalHandler.cpp index da63a53729..b73735ee19 100644 --- a/src/common/SignalHandler.cpp +++ b/src/Common/SignalHandler.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,37 +18,66 @@ #include "SignalHandler.h" #include -#include +#include -// epee -#include "include_base_utils.h" +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#else +#include +#endif +namespace { + + std::function m_handler; + + void handleSignal() { + static std::mutex m_mutex; + std::unique_lock lock(m_mutex); + m_handler(); + } -namespace tools { - std::function SignalHandler::m_handler; #if defined(WIN32) - BOOL WINAPI SignalHandler::winHandler(DWORD type) { - if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) { - handleSignal(); - return TRUE; - } else { - LOG_PRINT_RED_L0("Got control signal " << type << ". Exiting without saving..."); - return FALSE; - } +BOOL WINAPI winHandler(DWORD type) { + if (CTRL_C_EVENT == type || CTRL_BREAK_EVENT == type) { + handleSignal(); return TRUE; + } else { + std::cerr << "Got control signal " << type << ". Exiting without saving..."; + return FALSE; } + return TRUE; +} #else - void SignalHandler::posixHandler(int /*type*/) { - handleSignal(); - } +void posixHandler(int /*type*/) { + handleSignal(); +} #endif - void SignalHandler::handleSignal() { - static std::mutex m_mutex; - std::unique_lock lock(m_mutex); - m_handler(); +} + + +namespace tools { + + bool SignalHandler::install(std::function t) + { +#if defined(WIN32) + bool r = TRUE == ::SetConsoleCtrlHandler(&winHandler, TRUE); + if (r) { + m_handler = t; + } + return r; +#else + signal(SIGINT, posixHandler); + signal(SIGTERM, posixHandler); + m_handler = t; + return true; +#endif } + } diff --git a/src/Common/SignalHandler.h b/src/Common/SignalHandler.h new file mode 100644 index 0000000000..14033c9a22 --- /dev/null +++ b/src/Common/SignalHandler.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace tools { + + class SignalHandler + { + public: + static bool install(std::function t); + }; +} diff --git a/src/Common/StreamTools.cpp b/src/Common/StreamTools.cpp new file mode 100755 index 0000000000..304c458c3d --- /dev/null +++ b/src/Common/StreamTools.cpp @@ -0,0 +1,250 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StreamTools.h" +#include +#include "IInputStream.h" +#include "IOutputStream.h" + +namespace Common { + +void read(IInputStream& in, void* data, std::size_t size) { + while (size > 0) { + std::size_t readSize = in.readSome(data, size); + if (readSize == 0) { + throw std::runtime_error("Failed to read from IInputStream"); + } + + data = static_cast(data) + readSize; + size -= readSize; + } +} + +void read(IInputStream& in, int8_t& value) { + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, int16_t& value) { + // TODO: Convert from little endian on big endian platforms + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, int32_t& value) { + // TODO: Convert from little endian on big endian platforms + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, int64_t& value) { + // TODO: Convert from little endian on big endian platforms + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, uint8_t& value) { + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, uint16_t& value) { + // TODO: Convert from little endian on big endian platforms + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, uint32_t& value) { + // TODO: Convert from little endian on big endian platforms + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, uint64_t& value) { + // TODO: Convert from little endian on big endian platforms + read(in, &value, sizeof(value)); +} + +void read(IInputStream& in, std::vector& data, std::size_t size) { + data.resize(size); + read(in, data.data(), size); +} + +void read(IInputStream& in, std::string& data, std::size_t size) { + std::vector temp(size); + read(in, temp.data(), size); + data.assign(temp.data(), size); +} + +void readVarint(IInputStream& in, uint8_t& value) { + uint8_t temp = 0; + for (uint8_t shift = 0;; shift += 7) { + uint8_t piece; + read(in, piece); + if (shift >= sizeof(temp) * 8 - 7 && piece >= 1 << (sizeof(temp) * 8 - shift)) { + throw std::runtime_error("readVarint, value overflow"); + } + + temp |= static_cast(piece & 0x7f) << shift; + if ((piece & 0x80) == 0) { + if (piece == 0 && shift != 0) { + throw std::runtime_error("readVarint, invalid value representation"); + } + + break; + } + } + + value = temp; +} + +void readVarint(IInputStream& in, uint16_t& value) { + uint16_t temp = 0; + for (uint8_t shift = 0;; shift += 7) { + uint8_t piece; + read(in, piece); + if (shift >= sizeof(temp) * 8 - 7 && piece >= 1 << (sizeof(temp) * 8 - shift)) { + throw std::runtime_error("readVarint, value overflow"); + } + + temp |= static_cast(piece & 0x7f) << shift; + if ((piece & 0x80) == 0) { + if (piece == 0 && shift != 0) { + throw std::runtime_error("readVarint, invalid value representation"); + } + + break; + } + } + + value = temp; +} + +void readVarint(IInputStream& in, uint32_t& value) { + uint32_t temp = 0; + for (uint8_t shift = 0;; shift += 7) { + uint8_t piece; + read(in, piece); + if (shift >= sizeof(temp) * 8 - 7 && piece >= 1 << (sizeof(temp) * 8 - shift)) { + throw std::runtime_error("readVarint, value overflow"); + } + + temp |= static_cast(piece & 0x7f) << shift; + if ((piece & 0x80) == 0) { + if (piece == 0 && shift != 0) { + throw std::runtime_error("readVarint, invalid value representation"); + } + + break; + } + } + + value = temp; +} + +void readVarint(IInputStream& in, uint64_t& value) { + uint64_t temp = 0; + for (uint8_t shift = 0;; shift += 7) { + uint8_t piece; + read(in, piece); + if (shift >= sizeof(temp) * 8 - 7 && piece >= 1 << (sizeof(temp) * 8 - shift)) { + throw std::runtime_error("readVarint, value overflow"); + } + + temp |= static_cast(piece & 0x7f) << shift; + if ((piece & 0x80) == 0) { + if (piece == 0 && shift != 0) { + throw std::runtime_error("readVarint, invalid value representation"); + } + + break; + } + } + + value = temp; +} + +void write(IOutputStream& out, const void* data, std::size_t size) { + while (size > 0) { + std::size_t writtenSize = out.writeSome(data, size); + if (writtenSize == 0) { + throw std::runtime_error("Failed to write to IOutputStream"); + } + + data = static_cast(data) + writtenSize; + size -= writtenSize; + } +} + +void write(IOutputStream& out, int8_t value) { + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, int16_t value) { + // TODO: Convert to little endian on big endian platforms + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, int32_t value) { + // TODO: Convert to little endian on big endian platforms + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, int64_t value) { + // TODO: Convert to little endian on big endian platforms + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, uint8_t value) { + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, uint16_t value) { + // TODO: Convert to little endian on big endian platforms + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, uint32_t value) { + // TODO: Convert to little endian on big endian platforms + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, uint64_t value) { + // TODO: Convert to little endian on big endian platforms + write(out, &value, sizeof(value)); +} + +void write(IOutputStream& out, const std::vector& data) { + write(out, data.data(), data.size()); +} + +void write(IOutputStream& out, const std::string& data) { + write(out, data.data(), data.size()); +} + +void writeVarint(IOutputStream& out, uint32_t value) { + while (value >= 0x80) { + write(out, static_cast(value | 0x80)); + value >>= 7; + } + + write(out, static_cast(value)); +} + +void writeVarint(IOutputStream& out, uint64_t value) { + while (value >= 0x80) { + write(out, static_cast(value | 0x80)); + value >>= 7; + } + + write(out, static_cast(value)); +} + +} diff --git a/src/Common/StreamTools.h b/src/Common/StreamTools.h new file mode 100755 index 0000000000..578ef33df5 --- /dev/null +++ b/src/Common/StreamTools.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace Common { + +class IInputStream; +class IOutputStream; + +void read(IInputStream& in, void* data, std::size_t size); +void read(IInputStream& in, int8_t& value); +void read(IInputStream& in, int16_t& value); +void read(IInputStream& in, int32_t& value); +void read(IInputStream& in, int64_t& value); +void read(IInputStream& in, uint8_t& value); +void read(IInputStream& in, uint16_t& value); +void read(IInputStream& in, uint32_t& value); +void read(IInputStream& in, uint64_t& value); +void read(IInputStream& in, std::vector& data, std::size_t size); +void read(IInputStream& in, std::string& data, std::size_t size); +void readVarint(IInputStream& in, uint8_t& value); +void readVarint(IInputStream& in, uint16_t& value); +void readVarint(IInputStream& in, uint32_t& value); +void readVarint(IInputStream& in, uint64_t& value); + +void write(IOutputStream& out, const void* data, std::size_t size); +void write(IOutputStream& out, int8_t value); +void write(IOutputStream& out, int16_t value); +void write(IOutputStream& out, int32_t value); +void write(IOutputStream& out, int64_t value); +void write(IOutputStream& out, uint8_t value); +void write(IOutputStream& out, uint16_t value); +void write(IOutputStream& out, uint32_t value); +void write(IOutputStream& out, uint64_t value); +void write(IOutputStream& out, const std::vector& data); +void write(IOutputStream& out, const std::string& data); +void writeVarint(IOutputStream& out, uint64_t value); + +template T read(IInputStream& in) { + T value; + read(in, value); + return value; +} + +template T read(IInputStream& in, std::size_t size) { + T value; + read(in, value, size); + return value; +} + +template T readVarint(IInputStream& in) { + T value; + readVarint(in, value); + return value; +} + +}; diff --git a/src/Common/StringBuffer.h b/src/Common/StringBuffer.h new file mode 100755 index 0000000000..0b7053fbe9 --- /dev/null +++ b/src/Common/StringBuffer.h @@ -0,0 +1,554 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "StringView.h" +#include +#include + +namespace Common { + +// 'StringBuffer' is a string of fixed maximum size. +template class StringBuffer { +public: + typedef char Object; + typedef std::size_t Size; + + const static Size MAXIMUM_SIZE = MAXIMUM_SIZE_VALUE; + const static Size INVALID; + + // Default constructor. + // After construction, 'StringBuffer' is empty, that is 'size' == 0 + StringBuffer() : size(0) { + } + + // Direct constructor. + // Copies string from 'stringData' to 'StringBuffer'. + // The behavior is undefined unless ('stringData' != 'nullptr' || 'stringSize' == 0) && 'stringSize' <= 'MAXIMUM_SIZE'. + StringBuffer(const Object* stringData, Size stringSize) : size(stringSize) { + assert(stringData != nullptr || size == 0); + assert(size <= MAXIMUM_SIZE); + memcpy(data, stringData, size); + } + + // Constructor from C array. + // Copies string from 'stringData' to 'StringBuffer'. + // The behavior is undefined unless ('stringData' != 'nullptr' || 'stringSize' == 0) && 'stringSize' <= 'MAXIMUM_SIZE'. Input state can be malformed using poiner conversions. + template explicit StringBuffer(const Object(&stringData)[stringSize]) : size(stringSize - 1) { + assert(stringData != nullptr || size == 0); + assert(size <= MAXIMUM_SIZE); + memcpy(data, stringData, size); + } + + // Constructor from StringView + // Copies string from 'stringView' to 'StringBuffer'. + // The behavior is undefined unless 'stringView.size()' <= 'MAXIMUM_SIZE'. + explicit StringBuffer(StringView stringView) : size(stringView.getSize()) { + assert(size <= MAXIMUM_SIZE); + memcpy(data, stringView.getData(), size); + } + + // Copy constructor. + // Copies string from 'other' to 'StringBuffer'. + StringBuffer(const StringBuffer& other) : size(other.size) { + memcpy(data, other.data, size); + } + + // Destructor. + // No special action is performed. + ~StringBuffer() { + } + + // Copy assignment operator. + StringBuffer& operator=(const StringBuffer& other) { + size = other.size; + memcpy(data, other.data, size); + return *this; + } + + // StringView assignment operator. + // Copies string from 'stringView' to 'StringBuffer'. + // The behavior is undefined unless 'stringView.size()' <= 'MAXIMUM_SIZE'. + StringBuffer& operator=(StringView stringView) { + assert(stringView.getSize() <= MAXIMUM_SIZE); + memcpy(data, stringView.getData(), stringView.getSize()); + size = stringView.getSize(); + return *this; + } + + operator StringView() const { + return StringView(data, size); + } + + explicit operator std::string() const { + return std::string(data, size); + } + + Object* getData() { + return data; + } + + const Object* getData() const { + return data; + } + + Size getSize() const { + return size; + } + + // Return false if 'StringView' is not EMPTY. + bool isEmpty() const { + return size == 0; + } + + // Get 'StringBuffer' element by index. + // The behavior is undefined unless 'index' < 'size'. + Object& operator[](Size index) { + assert(index < size); + return *(data + index); + } + + // Get 'StringBuffer' element by index. + // The behavior is undefined unless 'index' < 'size'. + const Object& operator[](Size index) const { + assert(index < size); + return *(data + index); + } + + // Get first element. + // The behavior is undefined unless 'size' > 0 + Object& first() { + assert(size > 0); + return *data; + } + + // Get first element. + // The behavior is undefined unless 'size' > 0 + const Object& first() const { + assert(size > 0); + return *data; + } + + // Get last element. + // The behavior is undefined unless 'size' > 0 + Object& last() { + assert(size > 0); + return *(data + (size - 1)); + } + + // Get last element. + // The behavior is undefined unless 'size' > 0 + const Object& last() const { + assert(size > 0); + return *(data + (size - 1)); + } + + // Return a pointer to the first element. + Object* begin() { + return data; + } + + // Return a pointer to the first element. + const Object* begin() const { + return data; + } + + // Return a pointer after the last element. + Object* end() { + return data + size; + } + + // Return a pointer after the last element. + const Object* end() const { + return data + size; + } + + // Compare elements of two strings, return false if there is a difference. + bool operator==(StringView other) const { + if (size == other.getSize()) { + for (Size i = 0;; ++i) { + if (i == size) { + return true; + } + + if (!(*(data + i) == *(other.getData() + i))) { + break; + } + } + } + + return false; + } + + // Compare elements two strings, return false if there is no difference. + bool operator!=(StringView other) const { + return !(*this == other); + } + + // Compare two strings character-wise. + bool operator<(StringView other) const { + assert(data != nullptr || size == 0); + Size count = other.getSize() < size ? other.getSize() : size; + for (Size i = 0; i < count; ++i) { + Object char1 = *(data + i); + Object char2 = *(other.getData() + i); + if (char1 < char2) { + return true; + } + + if (char2 < char1) { + return false; + } + } + + return size < other.getSize(); + } + + // Compare two strings character-wise. + bool operator<=(StringView other) const { + return !(other < *this); + } + + // Compare two strings character-wise. + bool operator>(StringView other) const { + return other < *this; + } + + // Compare two strings character-wise. + bool operator>=(StringView other) const { + return !(*this < other); + } + + // Return false if 'StringView' does not contain 'object' at the beginning. + bool beginsWith(const Object& object) const { + if (size == 0) { + return false; + } + + return *data == object; + } + + // Return false if 'StringView' does not contain 'other' at the beginning. + bool beginsWith(StringView other) const { + if (size >= other.getSize()) { + for (Size i = 0;; ++i) { + if (i == other.getSize()) { + return true; + } + + if (!(*(data + i) == *(other.getData() + i))) { + break; + } + } + } + + return false; + } + + // Return false if 'StringView' does not contain 'object'. + bool contains(const Object& object) const { + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return true; + } + } + + return false; + } + + // Return false if 'StringView' does not contain 'other'. + bool contains(StringView other) const { + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.getSize()) { + return true; + } + + if (!(*(data + j + k) == *(other.getData() + k))) { + break; + } + } + } + } + + return false; + } + + // Return false if 'StringView' does not contain 'object' at the end. + bool endsWith(const Object& object) const { + if (size == 0) { + return false; + } + + return *(data + (size - 1)) == object; + } + + // Return false if 'StringView' does not contain 'other' at the end. + bool endsWith(StringView other) const { + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0;; ++j) { + if (j == other.getSize()) { + return true; + } + + if (!(*(data + i + j) == *(other.getData() + j))) { + break; + } + } + } + + return false; + } + + // Looks for the first occurence of 'object' in 'StringView', + // returns index or INVALID if there are no occurences. + Size find(const Object& object) const { + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return i; + } + } + + return INVALID; + } + + // Looks for the first occurence of 'other' in 'StringView', + // returns index or INVALID if there are no occurences. + Size find(StringView other) const { + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.getSize()) { + return j; + } + + if (!(*(data + j + k) == *(other.getData() + k))) { + break; + } + } + } + } + + return INVALID; + } + + // Looks for the last occurence of 'object' in 'StringView', + // returns index or INVALID if there are no occurences. + Size findLast(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + (size - 1 - i)) == object) { + return size - 1 - i; + } + } + + return INVALID; + } + + // Looks for the first occurence of 'other' in 'StringView', + // returns index or INVALID if there are no occurences. + Size findLast(StringView other) const { + if (size >= other.getSize()) { + Size i = size - other.getSize(); + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.getSize()) { + return i - j; + } + + if (!(*(data + (i - j + k)) == *(other.getData() + k))) { + break; + } + } + } + } + + return INVALID; + } + + // Returns substring of 'headSize' first elements. + // The behavior is undefined unless 'headSize' <= 'size'. + StringView head(Size headSize) const { + assert(headSize <= size); + return StringView(data, headSize); + } + + // Returns substring of 'tailSize' last elements. + // The behavior is undefined unless 'tailSize' <= 'size'. + StringView tail(Size tailSize) const { + assert(tailSize <= size); + return StringView(data + (size - tailSize), tailSize); + } + + // Returns 'StringView' without 'headSize' first elements. + // The behavior is undefined unless 'headSize' <= 'size'. + StringView unhead(Size headSize) const { + assert(headSize <= size); + return StringView(data + headSize, size - headSize); + } + + // Returns 'StringView' without 'tailSize' last elements. + // The behavior is undefined unless 'tailSize' <= 'size'. + StringView untail(Size tailSize) const { + assert(tailSize <= size); + return StringView(data, size - tailSize); + } + + // Returns substring starting at 'startIndex' and contaning 'endIndex' - 'startIndex' elements. + // The behavior is undefined unless 'startIndex' <= 'endIndex' and 'endIndex' <= 'size'. + StringView range(Size startIndex, Size endIndex) const { + assert(startIndex <= endIndex && endIndex <= size); + return StringView(data + startIndex, endIndex - startIndex); + } + + // Returns substring starting at 'startIndex' and contaning 'sliceSize' elements. + // The behavior is undefined unless 'startIndex' <= 'size' and 'sliceSize' <= 'size' - 'startIndex'. + StringView slice(Size startIndex, Size sliceSize) const { + assert(startIndex <= size && sliceSize <= size - startIndex); + return StringView(data + startIndex, sliceSize); + } + + // Appends 'object' to 'StringBuffer'. + // The behavior is undefined unless 1 <= 'MAXIMUM_SIZE' - 'size'. + StringBuffer& append(Object object) { + assert(1 <= MAXIMUM_SIZE - size); + data[size] = object; + ++size; + return *this; + } + + // Appends 'stringView' to 'StringBuffer'. + // The behavior is undefined unless 'stringView.size()' <= 'MAXIMUM_SIZE' - 'size'. + StringBuffer& append(StringView stringView) { + assert(stringView.getSize() <= MAXIMUM_SIZE - size); + if (stringView.getSize() != 0) { + memcpy(data + size, stringView.getData(), stringView.getSize()); + size += stringView.getSize(); + } + + return *this; + } + + // Sets 'StringBuffer' to empty state, that is 'size' == 0 + StringBuffer& clear() { + size = 0; + return *this; + } + + // Removes substring starting at 'startIndex' and contaning 'cutSize' elements. + // The behavior is undefined unless 'startIndex' <= 'size' and 'cutSize' <= 'size' - 'startIndex'. + StringBuffer& cut(Size startIndex, Size cutSize) { + assert(startIndex <= size && cutSize <= size - startIndex); + if (cutSize != 0) { + memcpy(data + startIndex, data + startIndex + cutSize, size - startIndex - cutSize); + size -= cutSize; + } + + return *this; + } + + // Copy 'object' to each element of 'StringBuffer'. + StringBuffer& fill(Object object) { + if (size > 0) { + memset(data, object, size); + } + + return *this; + } + + // Inserts 'object' into 'StringBuffer' at 'index'. + // The behavior is undefined unless 'index' <= 'size' and 1 <= 'MAXIMUM_SIZE' - 'size'. + StringBuffer& insert(Size index, Object object) { + assert(index <= size); + assert(1 <= MAXIMUM_SIZE - size); + memmove(data + index + 1, data + index, size - index); + data[index] = object; + ++size; + return *this; + } + + // Inserts 'stringView' into 'StringBuffer' at 'index'. + // The behavior is undefined unless 'index' <= 'size' and 'stringView.size()' <= 'MAXIMUM_SIZE' - 'size'. + StringBuffer& insert(Size index, StringView stringView) { + assert(index <= size); + assert(stringView.getSize() <= MAXIMUM_SIZE - size); + if (stringView.getSize() != 0) { + memmove(data + index + stringView.getSize(), data + index, size - index); + memcpy(data + index, stringView.getData(), stringView.getSize()); + size += stringView.getSize(); + } + + return *this; + } + + // Overwrites 'StringBuffer' starting at 'index' with 'stringView', possibly expanding 'StringBuffer'. + // The behavior is undefined unless 'index' <= 'size' and 'stringView.size()' <= 'MAXIMUM_SIZE' - 'index'. + StringBuffer& overwrite(Size index, StringView stringView) { + assert(index <= size); + assert(stringView.getSize() <= MAXIMUM_SIZE - index); + memcpy(data + index, stringView.getData(), stringView.getSize()); + if (size < index + stringView.getSize()) { + size = index + stringView.getSize(); + } + + return *this; + } + + // Sets 'size' to 'bufferSize', assigning value '\0' to newly inserted elements. + // The behavior is undefined unless 'bufferSize' <= 'MAXIMUM_SIZE'. + StringBuffer& resize(Size bufferSize) { + assert(bufferSize <= MAXIMUM_SIZE); + if (bufferSize > size) { + memset(data + size, 0, bufferSize - size); + } + + size = bufferSize; + return *this; + } + + // Reverse 'StringBuffer' elements. + StringBuffer& reverse() { + for (Size i = 0; i < size / 2; ++i) { + Object object = *(data + i); + *(data + i) = *(data + (size - 1 - i)); + *(data + (size - 1 - i)) = object; + } + + return *this; + } + + // Sets 'size' to 'bufferSize'. + // The behavior is undefined unless 'bufferSize' <= 'size'. + StringBuffer& shrink(Size bufferSize) { + assert(bufferSize <= size); + size = bufferSize; + return *this; + } + +protected: + Object data[MAXIMUM_SIZE]; + Size size; +}; + +template const typename StringBuffer::Size StringBuffer::INVALID = std::numeric_limits::Size>::max(); + +} diff --git a/src/Common/StringInputStream.cpp b/src/Common/StringInputStream.cpp new file mode 100755 index 0000000000..9ec09498de --- /dev/null +++ b/src/Common/StringInputStream.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StringInputStream.h" +#include + +namespace Common { + +StringInputStream::StringInputStream(const std::string& in) : in(in), offset(0) { +} + +std::size_t StringInputStream::readSome(void* data, std::size_t size) { + if (size > in.size() - offset) { + size = in.size() - offset; + } + + memcpy(data, in.data() + offset, size); + offset += size; + return size; +} + +} diff --git a/src/Common/StringInputStream.h b/src/Common/StringInputStream.h new file mode 100755 index 0000000000..af6cf78e53 --- /dev/null +++ b/src/Common/StringInputStream.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "IInputStream.h" + +namespace Common { + +class StringInputStream : public IInputStream { +public: + StringInputStream(const std::string& in); + std::size_t readSome(void* data, std::size_t size) override; + +private: + const std::string& in; + std::size_t offset; +}; + +} diff --git a/src/Common/StringOutputStream.cpp b/src/Common/StringOutputStream.cpp new file mode 100755 index 0000000000..07c9df906b --- /dev/null +++ b/src/Common/StringOutputStream.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StringOutputStream.h" + +namespace Common { + +StringOutputStream::StringOutputStream(std::string& out) : out(out) { +} + +std::size_t StringOutputStream::writeSome(const void* data, std::size_t size) { + out.append(static_cast(data), size); + return size; +} + +} diff --git a/src/miner/target_helper.h b/src/Common/StringOutputStream.h old mode 100644 new mode 100755 similarity index 68% rename from src/miner/target_helper.h rename to src/Common/StringOutputStream.h index b3d0a2c135..31c8a9c513 --- a/src/miner/target_helper.h +++ b/src/Common/StringOutputStream.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,16 +16,19 @@ // along with Bytecoin. If not, see . #pragma once -#include "cryptonote_core/difficulty.h" +#include +#include "IOutputStream.h" -namespace mining -{ - inline uint32_t get_target_for_difficulty(cryptonote::difficulty_type difficulty) - { - if(!difficulty) - return 0xffffffff; - return 0xffffffff/static_cast(difficulty); - } +namespace Common { + +class StringOutputStream : public IOutputStream { +public: + StringOutputStream(std::string& out); + std::size_t writeSome(const void* data, std::size_t size) override; + +private: + std::string& out; +}; } diff --git a/src/Common/StringTools.cpp b/src/Common/StringTools.cpp new file mode 100755 index 0000000000..750718b17c --- /dev/null +++ b/src/Common/StringTools.cpp @@ -0,0 +1,348 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StringTools.h" +#include + +namespace Common { + +namespace { + +const uint8_t characterValues[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, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +} + +std::string asString(const void* data, std::size_t size) { + return std::string(static_cast(data), size); +} + +std::string asString(const std::vector& data) { + return std::string(reinterpret_cast(data.data()), data.size()); +} + +uint8_t fromHex(char character) { + uint8_t value = characterValues[static_cast(character)]; + if (value > 0x0f) { + throw std::runtime_error("fromHex: invalid character"); + } + + return value; +} + +bool fromHex(char character, uint8_t& value) { + if (characterValues[static_cast(character)] > 0x0f) { + return false; + } + + value = characterValues[static_cast(character)]; + return true; +} + +std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize) { + if ((text.size() & 1) != 0) { + throw std::runtime_error("fromHex: invalid string size"); + } + + if (text.size() >> 1 > bufferSize) { + throw std::runtime_error("fromHex: invalid buffer size"); + } + + for (std::size_t i = 0; i < text.size() >> 1; ++i) { + static_cast(data)[i] = fromHex(text[i << 1]) << 4 | fromHex(text[(i << 1) + 1]); + } + + return text.size() >> 1; +} + +bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::size_t& size) { + if ((text.size() & 1) != 0) { + return false; + } + + if (text.size() >> 1 > bufferSize) { + return false; + } + + for (std::size_t i = 0; i < text.size() >> 1; ++i) { + uint8_t value1; + if (!fromHex(text[i << 1], value1)) { + return false; + } + + uint8_t value2; + if (!fromHex(text[(i << 1) + 1], value2)) { + return false; + } + + static_cast(data)[i] = value1 << 4 | value2; + } + + size = text.size() >> 1; + return true; +} + +std::vector fromHex(const std::string& text) { + if ((text.size() & 1) != 0) { + throw std::runtime_error("fromHex: invalid string size"); + } + + std::vector data(text.size() >> 1); + for (std::size_t i = 0; i < data.size(); ++i) { + data[i] = fromHex(text[i << 1]) << 4 | fromHex(text[(i << 1) + 1]); + } + + return data; +} + +bool fromHex(const std::string& text, std::vector& data) { + if ((text.size() & 1) != 0) { + return false; + } + + for (std::size_t i = 0; i < text.size() >> 1; ++i) { + uint8_t value1; + if (!fromHex(text[i << 1], value1)) { + return false; + } + + uint8_t value2; + if (!fromHex(text[(i << 1) + 1], value2)) { + return false; + } + + data.push_back(value1 << 4 | value2); + } + + return true; +} + +std::string toHex(const void* data, std::size_t size) { + std::string text; + for (std::size_t i = 0; i < size; ++i) { + text += "0123456789abcdef"[static_cast(data)[i] >> 4]; + text += "0123456789abcdef"[static_cast(data)[i] & 15]; + } + + return text; +} + +void toHex(const void* data, std::size_t size, std::string& text) { + for (std::size_t i = 0; i < size; ++i) { + text += "0123456789abcdef"[static_cast(data)[i] >> 4]; + text += "0123456789abcdef"[static_cast(data)[i] & 15]; + } +} + +std::string toHex(const std::vector& data) { + std::string text; + for (std::size_t i = 0; i < data.size(); ++i) { + text += "0123456789abcdef"[data[i] >> 4]; + text += "0123456789abcdef"[data[i] & 15]; + } + + return text; +} + +void toHex(const std::vector& data, std::string& text) { + for (std::size_t i = 0; i < data.size(); ++i) { + text += "0123456789abcdef"[data[i] >> 4]; + text += "0123456789abcdef"[data[i] & 15]; + } +} + +std::string extract(std::string& text, char delimiter) { + std::size_t delimiterPosition = text.find(delimiter); + std::string subText; + if (delimiterPosition != std::string::npos) { + subText = text.substr(0, delimiterPosition); + text = text.substr(delimiterPosition + 1); + } else { + subText.swap(text); + } + + return subText; +} + +std::string extract(const std::string& text, char delimiter, std::size_t& offset) { + std::size_t delimiterPosition = text.find(delimiter, offset); + if (delimiterPosition != std::string::npos) { + offset = delimiterPosition + 1; + return text.substr(offset, delimiterPosition); + } else { + offset = text.size(); + return text.substr(offset); + } +} + +namespace { + +static const std::string base64chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +} + +std::string base64Decode(std::string const& encoded_string) { + size_t in_len = encoded_string.size(); + size_t i = 0; + size_t j = 0; + size_t in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i == 4) { + for (i = 0; i <4; i++) + char_array_4[i] = (unsigned char)base64chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = (unsigned char)base64chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} + + +bool loadFileToString(const std::string& filepath, std::string& buf) { + try { + std::ifstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(filepath, std::ios_base::binary | std::ios_base::in | std::ios::ate); + + size_t fileSize = static_cast(fstream.tellg()); + buf.resize(fileSize); + + if (fileSize > 0) { + fstream.seekg(0, std::ios::beg); + fstream.read(&buf[0], buf.size()); + } + } catch (const std::exception&) { + return false; + } + + return true; +} + +bool saveStringToFile(const std::string& filepath, const std::string& buf) { + try { + std::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(filepath, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + fstream << buf; + } catch (const std::exception&) { + return false; + } + + return true; +} + + +std::string ipAddressToString(uint32_t ip) { + uint8_t bytes[4]; + bytes[0] = ip & 0xFF; + bytes[1] = (ip >> 8) & 0xFF; + bytes[2] = (ip >> 16) & 0xFF; + bytes[3] = (ip >> 24) & 0xFF; + + char buf[16]; + sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); + + return std::string(buf); +} + +bool parseIpAddressAndPort(uint32_t& ip, uint32_t& port, const std::string& addr) { + uint32_t v[4]; + uint32_t localPort; + + if (sscanf(addr.c_str(), "%d.%d.%d.%d:%d", &v[0], &v[1], &v[2], &v[3], &localPort) != 5) { + return false; + } + + for (int i = 0; i < 4; ++i) { + if (v[i] > 0xff) { + return false; + } + } + + ip = (v[3] << 24) | (v[2] << 16) | (v[1] << 8) | v[0]; + port = localPort; + return true; +} + +std::string timeIntervalToString(uint64_t intervalInSeconds) { + auto tail = intervalInSeconds; + + auto days = tail / (60 * 60 * 24); + tail = tail % (60 * 60 * 24); + auto hours = tail / (60 * 60); + tail = tail % (60 * 60); + auto minutes = tail / (60); + tail = tail % (60); + auto seconds = tail; + + return + "d" + std::to_string(days) + + ".h" + std::to_string(hours) + + ".m" + std::to_string(minutes) + + ".s" + std::to_string(seconds); +} + + +} diff --git a/src/Common/StringTools.h b/src/Common/StringTools.h new file mode 100755 index 0000000000..c8e70ccad2 --- /dev/null +++ b/src/Common/StringTools.h @@ -0,0 +1,118 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include + +namespace Common { + +std::string asString(const void* data, std::size_t size); // Does not throw +std::string asString(const std::vector& data); // Does not throw + +uint8_t fromHex(char character); // Returns value of hex 'character', throws on error +bool fromHex(char character, uint8_t& value); // Assigns value of hex 'character' to 'value', returns false on error, does not throw +std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', returns actual data size, throws on error +bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::size_t& size); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', assigns actual data size to 'size', returns false on error, does not throw +std::vector fromHex(const std::string& text); // Returns values of hex 'text', throws on error +bool fromHex(const std::string& text, std::vector& data); // Appends values of hex 'text' to 'data', returns false on error, does not throw + +template +bool podFromHex(const std::string& text, T& val) { + std::size_t outSize; + return fromHex(text, &val, sizeof(val), outSize) && outSize == sizeof(val); +} + +std::string toHex(const void* data, std::size_t size); // Returns hex representation of ('data', 'size'), does not throw +void toHex(const void* data, std::size_t size, std::string& text); // Appends hex representation of ('data', 'size') to 'text', does not throw +std::string toHex(const std::vector& data); // Returns hex representation of 'data', does not throw +void toHex(const std::vector& data, std::string& text); // Appends hex representation of 'data' to 'text', does not throw + +template +std::string podToHex(const T& s) { + return toHex(&s, sizeof(s)); +} + +std::string extract(std::string& text, char delimiter); // Does not throw +std::string extract(const std::string& text, char delimiter, std::size_t& offset); // Does not throw + +template T fromString(const std::string& text) { // Throws on error + T value; + std::istringstream stream(text); + stream >> value; + if (stream.fail()) { + throw std::runtime_error("fromString: unable to parse value"); + } + + return value; +} + +template bool fromString(const std::string& text, T& value) { // Does not throw + std::istringstream stream(text); + stream >> value; + return !stream.fail(); +} + +template std::vector fromDelimitedString(const std::string& source, char delimiter) { // Throws on error + std::vector data; + for (std::size_t offset = 0; offset != source.size();) { + data.emplace_back(fromString(extract(source, delimiter, offset))); + } + + return data; +} + +template bool fromDelimitedString(const std::string& source, char delimiter, std::vector& data) { // Does not throw + for (std::size_t offset = 0; offset != source.size();) { + T value; + if (!fromString(extract(source, delimiter, offset), value)) { + return false; + } + + data.emplace_back(value); + } + + return true; +} + +template std::string toString(const T& value) { // Does not throw + std::ostringstream stream; + stream << value; + return stream.str(); +} + +template void toString(const T& value, std::string& text) { // Does not throw + std::ostringstream stream; + stream << value; + text += stream.str(); +} + +bool loadFileToString(const std::string& filepath, std::string& buf); +bool saveStringToFile(const std::string& filepath, const std::string& buf); + + +std::string base64Decode(std::string const& encoded_string); + +std::string ipAddressToString(uint32_t ip); +bool parseIpAddressAndPort(uint32_t& ip, uint32_t& port, const std::string& addr); + +std::string timeIntervalToString(uint64_t intervalInSeconds); + +} diff --git a/src/Common/StringView.cpp b/src/Common/StringView.cpp new file mode 100755 index 0000000000..68e9d1b54d --- /dev/null +++ b/src/Common/StringView.cpp @@ -0,0 +1,347 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StringView.h" +#include + +namespace Common { + +const StringView::Size StringView::INVALID = std::numeric_limits::max(); +const StringView StringView::EMPTY(reinterpret_cast(1), 0); +const StringView StringView::NIL(nullptr, 0); + +StringView::StringView() +#ifndef NDEBUG + : data(nullptr), size(INVALID) // In debug mode, fill in object with invalid state (undefined). +#endif +{ +} + +StringView::StringView(const Object* stringData, Size stringSize) : data(stringData), size(stringSize) { + assert(data != nullptr || size == 0); +} + +StringView::StringView(const std::string& string) : data(string.data()), size(string.size()) { +} + +StringView::StringView(const StringView& other) : data(other.data), size(other.size) { + assert(data != nullptr || size == 0); +} + +StringView::~StringView() { +} + +StringView& StringView::operator=(const StringView& other) { + assert(other.data != nullptr || other.size == 0); + data = other.data; + size = other.size; + return *this; +} + +StringView::operator std::string() const { + return std::string(data, size); +} + +const StringView::Object* StringView::getData() const { + assert(data != nullptr || size == 0); + return data; +} + +StringView::Size StringView::getSize() const { + assert(data != nullptr || size == 0); + return size; +} + +bool StringView::isEmpty() const { + assert(data != nullptr || size == 0); + return size == 0; +} + +bool StringView::isNil() const { + assert(data != nullptr || size == 0); + return data == nullptr; +} + +const StringView::Object& StringView::operator[](Size index) const { + assert(data != nullptr || size == 0); + assert(index < size); + return *(data + index); +} + +const StringView::Object& StringView::first() const { + assert(data != nullptr || size == 0); + assert(size > 0); + return *data; +} + +const StringView::Object& StringView::last() const { + assert(data != nullptr || size == 0); + assert(size > 0); + return *(data + (size - 1)); +} + +const StringView::Object* StringView::begin() const { + assert(data != nullptr || size == 0); + return data; +} + +const StringView::Object* StringView::end() const { + assert(data != nullptr || size == 0); + return data + size; +} + +bool StringView::operator==(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size == other.size) { + for (Size i = 0;; ++i) { + if (i == size) { + return true; + } + + if (!(*(data + i) == *(other.data + i))) { + break; + } + } + } + + return false; +} + +bool StringView::operator!=(StringView other) const { + return !(*this == other); +} + +bool StringView::operator<(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + Size count = other.size < size ? other.size : size; + for (Size i = 0; i < count; ++i) { + Object char1 = *(data + i); + Object char2 = *(other.data + i); + if (char1 < char2) { + return true; + } + + if (char2 < char1) { + return false; + } + } + + return size < other.size; +} + +bool StringView::operator<=(StringView other) const { + return !(other < *this); +} + +bool StringView::operator>(StringView other) const { + return other < *this; +} + +bool StringView::operator>=(StringView other) const { + return !(*this < other); +} + +bool StringView::beginsWith(const Object& object) const { + assert(data != nullptr || size == 0); + if (size == 0) { + return false; + } + + return *data == object; +} + +bool StringView::beginsWith(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + for (Size i = 0;; ++i) { + if (i == other.size) { + return true; + } + + if (!(*(data + i) == *(other.data + i))) { + break; + } + } + } + + return false; +} + +bool StringView::contains(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return true; + } + } + + return false; +} + +bool StringView::contains(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.size) { + return true; + } + + if (!(*(data + j + k) == *(other.data + k))) { + break; + } + } + } + } + + return false; +} + +bool StringView::endsWith(const Object& object) const { + assert(data != nullptr || size == 0); + if (size == 0) { + return false; + } + + return *(data + (size - 1)) == object; +} + +bool StringView::endsWith(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0;; ++j) { + if (j == other.size) { + return true; + } + + if (!(*(data + i + j) == *(other.data + j))) { + break; + } + } + } + + return false; +} + +StringView::Size StringView::find(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + i) == object) { + return i; + } + } + + return INVALID; +} + +StringView::Size StringView::find(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.size) { + return j; + } + + if (!(*(data + j + k) == *(other.data + k))) { + break; + } + } + } + } + + return INVALID; +} + +StringView::Size StringView::findLast(const Object& object) const { + assert(data != nullptr || size == 0); + for (Size i = 0; i < size; ++i) { + if (*(data + (size - 1 - i)) == object) { + return size - 1 - i; + } + } + + return INVALID; +} + +StringView::Size StringView::findLast(StringView other) const { + assert(data != nullptr || size == 0); + assert(other.data != nullptr || other.size == 0); + if (size >= other.size) { + Size i = size - other.size; + for (Size j = 0; !(i < j); ++j) { + for (Size k = 0;; ++k) { + if (k == other.size) { + return i - j; + } + + if (!(*(data + (i - j + k)) == *(other.data + k))) { + break; + } + } + } + } + + return INVALID; +} + +StringView StringView::head(Size headSize) const { + assert(data != nullptr || size == 0); + assert(headSize <= size); + return StringView(data, headSize); +} + +StringView StringView::tail(Size tailSize) const { + assert(data != nullptr || size == 0); + assert(tailSize <= size); + return StringView(data + (size - tailSize), tailSize); +} + +StringView StringView::unhead(Size headSize) const { + assert(data != nullptr || size == 0); + assert(headSize <= size); + return StringView(data + headSize, size - headSize); +} + +StringView StringView::untail(Size tailSize) const { + assert(data != nullptr || size == 0); + assert(tailSize <= size); + return StringView(data, size - tailSize); +} + +StringView StringView::range(Size startIndex, Size endIndex) const { + assert(data != nullptr || size == 0); + assert(startIndex <= endIndex && endIndex <= size); + return StringView(data + startIndex, endIndex - startIndex); +} + +StringView StringView::slice(Size startIndex, Size sliceSize) const { + assert(data != nullptr || size == 0); + assert(startIndex <= size && startIndex + sliceSize <= size); + return StringView(data + startIndex, sliceSize); +} + +} diff --git a/src/Common/StringView.h b/src/Common/StringView.h new file mode 100755 index 0000000000..6a1eb85b78 --- /dev/null +++ b/src/Common/StringView.h @@ -0,0 +1,204 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace Common { + +// 'StringView' is a pair of pointer to constant char and size. +// It is recommended to pass 'StringView' to procedures by value. +// 'StringView' supports 'EMPTY' and 'NIL' representations as follows: +// 'data' == 'nullptr' && 'size' == 0 - EMPTY NIL +// 'data' != 'nullptr' && 'size' == 0 - EMPTY NOTNIL +// 'data' == 'nullptr' && 'size' > 0 - Undefined +// 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL +class StringView { +public: + typedef char Object; + typedef std::size_t Size; + + const static Size INVALID; + const static StringView EMPTY; + const static StringView NIL; + + // Default constructor. + // Leaves object uninitialized. Any usage before initializing it is undefined. + StringView(); + + // Direct constructor. + // The behavior is undefined unless 'stringData' != 'nullptr' || 'stringSize' == 0 + StringView(const Object* stringData, Size stringSize); + + // Constructor from C array. + // The behavior is undefined unless 'stringData' != 'nullptr' || 'stringSize' == 0. Input state can be malformed using poiner conversions. + template StringView(const Object(&stringData)[stringSize]) : data(stringData), size(stringSize - 1) { + assert(data != nullptr || size == 0); + } + + // Constructor from std::string + StringView(const std::string& string); + + // Copy constructor. + // Performs default action - bitwise copying of source object. + // The behavior is undefined unless 'other' 'StringView' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 + StringView(const StringView& other); + + // Destructor. + // No special action is performed. + ~StringView(); + + // Copy assignment operator. + // The behavior is undefined unless 'other' 'StringView' is in defined state, that is 'data' != 'nullptr' || 'size' == 0 + StringView& operator=(const StringView& other); + + explicit operator std::string() const; + + const Object* getData() const; + + Size getSize() const; + + // Return false if 'StringView' is not EMPTY. + // The behavior is undefined unless 'StringView' was initialized. + bool isEmpty() const; + + // Return false if 'StringView' is not NIL. + // The behavior is undefined unless 'StringView' was initialized. + bool isNil() const; + + // Get 'StringView' element by index. + // The behavior is undefined unless 'StringView' was initialized and 'index' < 'size'. + const Object& operator[](Size index) const; + + // Get first element. + // The behavior is undefined unless 'StringView' was initialized and 'size' > 0 + const Object& first() const; + + // Get last element. + // The behavior is undefined unless 'StringView' was initialized and 'size' > 0 + const Object& last() const; + + // Return a pointer to the first element. + // The behavior is undefined unless 'StringView' was initialized. + const Object* begin() const; + + // Return a pointer after the last element. + // The behavior is undefined unless 'StringView' was initialized. + const Object* end() const; + + // Compare elements of two strings, return false if there is a difference. + // EMPTY and NIL strings are considered equal. + // The behavior is undefined unless both strings were initialized. + bool operator==(StringView other) const; + + // Compare elements two strings, return false if there is no difference. + // EMPTY and NIL strings are considered equal. + // The behavior is undefined unless both strings were initialized. + bool operator!=(StringView other) const; + + // Compare two strings character-wise. + // The behavior is undefined unless both strings were initialized. + bool operator<(StringView other) const; + + // Compare two strings character-wise. + // The behavior is undefined unless both strings were initialized. + bool operator<=(StringView other) const; + + // Compare two strings character-wise. + // The behavior is undefined unless both strings were initialized. + bool operator>(StringView other) const; + + // Compare two strings character-wise. + // The behavior is undefined unless both strings were initialized. + bool operator>=(StringView other) const; + + // Return false if 'StringView' does not contain 'object' at the beginning. + // The behavior is undefined unless 'StringView' was initialized. + bool beginsWith(const Object& object) const; + + // Return false if 'StringView' does not contain 'other' at the beginning. + // The behavior is undefined unless both strings were initialized. + bool beginsWith(StringView other) const; + + // Return false if 'StringView' does not contain 'object'. + // The behavior is undefined unless 'StringView' was initialized. + bool contains(const Object& object) const; + + // Return false if 'StringView' does not contain 'other'. + // The behavior is undefined unless both strings were initialized. + bool contains(StringView other) const; + + // Return false if 'StringView' does not contain 'object' at the end. + // The behavior is undefined unless 'StringView' was initialized. + bool endsWith(const Object& object) const; + + // Return false if 'StringView' does not contain 'other' at the end. + // The behavior is undefined unless both strings were initialized. + bool endsWith(StringView other) const; + + // Looks for the first occurence of 'object' in 'StringView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless 'StringView' was initialized. + Size find(const Object& object) const; + + // Looks for the first occurence of 'other' in 'StringView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless both strings were initialized. + Size find(StringView other) const; + + // Looks for the last occurence of 'object' in 'StringView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless 'StringView' was initialized. + Size findLast(const Object& object) const; + + // Looks for the first occurence of 'other' in 'StringView', + // returns index or INVALID if there are no occurences. + // The behavior is undefined unless both strings were initialized. + Size findLast(StringView other) const; + + // Returns substring of 'headSize' first elements. + // The behavior is undefined unless 'StringView' was initialized and 'headSize' <= 'size'. + StringView head(Size headSize) const; + + // Returns substring of 'tailSize' last elements. + // The behavior is undefined unless 'StringView' was initialized and 'tailSize' <= 'size'. + StringView tail(Size tailSize) const; + + // Returns 'StringView' without 'headSize' first elements. + // The behavior is undefined unless 'StringView' was initialized and 'headSize' <= 'size'. + StringView unhead(Size headSize) const; + + // Returns 'StringView' without 'tailSize' last elements. + // The behavior is undefined unless 'StringView' was initialized and 'tailSize' <= 'size'. + StringView untail(Size tailSize) const; + + // Returns substring starting at 'startIndex' and contaning 'endIndex' - 'startIndex' elements. + // The behavior is undefined unless 'StringView' was initialized and 'startIndex' <= 'endIndex' and 'endIndex' <= 'size'. + StringView range(Size startIndex, Size endIndex) const; + + // Returns substring starting at 'startIndex' and contaning 'sliceSize' elements. + // The behavior is undefined unless 'StringView' was initialized and 'startIndex' <= 'size' and 'startIndex' + 'sliceSize' <= 'size'. + StringView slice(Size startIndex, Size sliceSize) const; + +protected: + const Object* data; + Size size; +}; + +} diff --git a/src/common/base58.cpp b/src/Common/base58.cpp similarity index 99% rename from src/common/base58.cpp rename to src/Common/base58.cpp index 926a56b4ba..cc1a8f4bc5 100644 --- a/src/common/base58.cpp +++ b/src/Common/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/common/base58.h b/src/Common/base58.h similarity index 94% rename from src/common/base58.h rename to src/Common/base58.h index ee947fc65d..b4e95901fa 100644 --- a/src/common/base58.h +++ b/src/Common/base58.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Common/boost_serialization_helper.h b/src/Common/boost_serialization_helper.h new file mode 100644 index 0000000000..696b06c01a --- /dev/null +++ b/src/Common/boost_serialization_helper.h @@ -0,0 +1,106 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#ifdef _WIN32 +#include +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif + +#include + +#include +#include + +namespace tools +{ + template + bool serialize_obj_to_file(t_object& obj, const std::string& file_path) + { + try { +#ifdef _WIN32 + // Need to know HANDLE of file to call FlushFileBuffers + HANDLE data_file_handle = ::CreateFile(file_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == data_file_handle) + return false; + + int data_file_descriptor = _open_osfhandle((intptr_t)data_file_handle, 0); + if (-1 == data_file_descriptor) { + ::CloseHandle(data_file_handle); + return false; + } + + FILE* data_file_file = _fdopen(data_file_descriptor, "wb"); + if (0 == data_file_file) { + // Call CloseHandle is not necessary + _close(data_file_descriptor); + return false; + } + + // HACK: undocumented constructor, this code may not compile + std::ofstream data_file(data_file_file); + if (data_file.fail()) { + // Call CloseHandle and _close are not necessary + fclose(data_file_file); + return false; + } +#else + std::ofstream data_file; + data_file.open(file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); + if (data_file.fail()) + return false; +#endif + + boost::archive::binary_oarchive a(data_file); + a << obj; + if (data_file.fail()) + return false; + + data_file.flush(); +#ifdef _WIN32 + // To make sure the file is fully stored on disk + ::FlushFileBuffers(data_file_handle); + fclose(data_file_file); +#endif + + return true; + } catch (std::exception&) { + return false; + } + } + + template + bool unserialize_obj_from_file(t_object& obj, const std::string& file_path) + { + try { + std::ifstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::in); + if(data_file.fail()) + return false; + boost::archive::binary_iarchive a(data_file); + + a >> obj; + return !data_file.fail(); + } catch (std::exception&) { + return false; + } + } +} diff --git a/src/common/command_line.cpp b/src/Common/command_line.cpp similarity index 94% rename from src/common/command_line.cpp rename to src/Common/command_line.cpp index 92208953f3..dd887a65d0 100644 --- a/src/common/command_line.cpp +++ b/src/Common/command_line.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/common/command_line.h b/src/Common/command_line.h similarity index 90% rename from src/common/command_line.h rename to src/Common/command_line.h index db0c45c961..d992c961ed 100644 --- a/src/common/command_line.h +++ b/src/Common/command_line.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,6 @@ #include #include #include -#include "include_base_utils.h" namespace command_line { @@ -96,9 +95,9 @@ namespace command_line template void add_arg(boost::program_options::options_description& description, const arg_descriptor& arg, bool unique = true) { - if (0 != description.find_nothrow(arg.name, false)) + if (unique && 0 != description.find_nothrow(arg.name, false)) { - CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + std::cerr << "Argument already exists: " << arg.name << std::endl; return; } @@ -108,9 +107,9 @@ namespace command_line template void add_arg(boost::program_options::options_description& description, const arg_descriptor& arg, const T& def, bool unique = true) { - if (0 != description.find_nothrow(arg.name, false)) + if (unique && 0 != description.find_nothrow(arg.name, false)) { - CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + std::cerr << "Argument already exists: " << arg.name << std::endl; return; } @@ -120,9 +119,9 @@ namespace command_line template<> inline void add_arg(boost::program_options::options_description& description, const arg_descriptor& arg, bool unique) { - if (0 != description.find_nothrow(arg.name, false)) + if (unique && 0 != description.find_nothrow(arg.name, false)) { - CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + std::cerr << "Argument already exists: " << arg.name << std::endl; return; } diff --git a/src/common/int-util.h b/src/Common/int-util.h similarity index 99% rename from src/common/int-util.h rename to src/Common/int-util.h index 0ac962ac05..3fdb5dc0c6 100644 --- a/src/common/int-util.h +++ b/src/Common/int-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/common/pod-class.h b/src/Common/pod-class.h similarity index 92% rename from src/common/pod-class.h rename to src/Common/pod-class.h index 6badc12c8a..63972d28af 100644 --- a/src/common/pod-class.h +++ b/src/Common/pod-class.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Platform/Linux/System/InterruptedException.h b/src/Common/static_assert.h similarity index 80% rename from src/Platform/Linux/System/InterruptedException.h rename to src/Common/static_assert.h index 67762ed304..a893d24ad8 100755 --- a/src/Platform/Linux/System/InterruptedException.h +++ b/src/Common/static_assert.h @@ -1,23 +1,26 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -class InterruptedException : public std::exception { -}; +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#ifndef __cplusplus +#ifdef __clang__ + +#define static_assert _Static_assert + +#endif +#endif diff --git a/src/common/unordered_containers_boost_serialization.h b/src/Common/unordered_containers_boost_serialization.h similarity index 96% rename from src/common/unordered_containers_boost_serialization.h rename to src/Common/unordered_containers_boost_serialization.h index 45974dacdd..3aed3ce9cd 100644 --- a/src/common/unordered_containers_boost_serialization.h +++ b/src/Common/unordered_containers_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -46,6 +46,7 @@ namespace boost x.clear(); size_t s = 0; a >> s; + x.reserve(s); for(size_t i = 0; i != s; i++) { h_key k; @@ -75,7 +76,8 @@ namespace boost x.clear(); size_t s = 0; a >> s; - for(size_t i = 0; i != s; i++) + x.reserve(s); + for (size_t i = 0; i != s; i++) { h_key k; hval v; @@ -103,7 +105,8 @@ namespace boost x.clear(); size_t s = 0; a >> s; - for(size_t i = 0; i != s; i++) + x.reserve(s); + for (size_t i = 0; i != s; i++) { hval v; a >> v; diff --git a/src/common/util.cpp b/src/Common/util.cpp similarity index 92% rename from src/common/util.cpp rename to src/Common/util.cpp index 16786d58bf..994929c61e 100644 --- a/src/common/util.cpp +++ b/src/Common/util.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,15 +16,10 @@ // along with Bytecoin. If not, see . #include "util.h" - #include #include -#include "include_base_utils.h" -using namespace epee; - -#include "p2p/p2p_protocol_defs.h" #include "cryptonote_config.h" #ifdef WIN32 @@ -293,12 +288,10 @@ std::string get_nix_version_display_string() namespace fs = boost::filesystem; char psz_path[MAX_PATH] = ""; - if(SHGetSpecialFolderPathA(NULL, psz_path, nfolder, iscreate)) - { + if(SHGetSpecialFolderPathA(NULL, psz_path, nfolder, iscreate)) { return psz_path; } - LOG_ERROR("SHGetSpecialFolderPathA() failed, could not obtain requested path."); return ""; } #endif @@ -313,7 +306,7 @@ std::string get_nix_version_display_string() std::string config_folder; #ifdef WIN32 // Windows - config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + cryptonote::CRYPTONOTE_NAME; + config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CryptoNote::CRYPTONOTE_NAME; #else std::string pathRet; char* pszHome = getenv("HOME"); @@ -324,10 +317,10 @@ std::string get_nix_version_display_string() #ifdef MAC_OSX // Mac pathRet /= "Library/Application Support"; - config_folder = (pathRet + "/" + cryptonote::CRYPTONOTE_NAME); + config_folder = (pathRet + "/" + CryptoNote::CRYPTONOTE_NAME); #else // Unix - config_folder = (pathRet + "/." + cryptonote::CRYPTONOTE_NAME); + config_folder = (pathRet + "/." + CryptoNote::CRYPTONOTE_NAME); #endif #endif @@ -339,22 +332,11 @@ std::string get_nix_version_display_string() namespace fs = boost::filesystem; boost::system::error_code ec; fs::path fs_path(path); - if (fs::is_directory(fs_path, ec)) - { + if (fs::is_directory(fs_path, ec)) { return true; } - bool res = fs::create_directories(fs_path, ec); - if (res) - { - LOG_PRINT_L2("Created directory: " << path); - } - else - { - LOG_PRINT_L2("Can't create directory: " << path << ", err: "<< ec.message()); - } - - return res; + return fs::create_directories(fs_path, ec); } std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name) @@ -377,11 +359,4 @@ std::string get_nix_version_display_string() return std::error_code(code, std::system_category()); } - crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) - { - std::string s; - s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); - s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); - } } diff --git a/src/common/util.h b/src/Common/util.h similarity index 82% rename from src/common/util.h rename to src/Common/util.h index 514bd90f33..e3b6781869 100644 --- a/src/common/util.h +++ b/src/Common/util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,18 +20,10 @@ #include #include -#include "crypto/hash.h" - - -namespace nodetool { - struct proof_of_trust; -} - namespace tools { std::string get_default_data_dir(); std::string get_os_version_string(); bool create_directories_if_necessary(const std::string& path); std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); - crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot); } diff --git a/src/common/varint.h b/src/Common/varint.h similarity index 97% rename from src/common/varint.h rename to src/Common/varint.h index c1e178ece6..fd97b696bd 100644 --- a/src/common/varint.h +++ b/src/Common/varint.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,10 +18,10 @@ #pragma once #include -#include -#include #include #include +#include +#include namespace tools { diff --git a/src/CryptoNote/BaseTransaction.cpp b/src/CryptoNote/BaseTransaction.cpp new file mode 100755 index 0000000000..b4411aa81d --- /dev/null +++ b/src/CryptoNote/BaseTransaction.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BaseTransaction.h" +#include +#include + +namespace CryptoNote { + +BaseTransaction::BaseTransaction( + uint64_t blockIndex, + uint64_t unlockTime, + std::vector&& keyOutputs, + std::vector&& multisignatureOutputs, + std::vector&& extra) : + blockIndex(blockIndex), + unlockTime(unlockTime), + keyOutputs(std::move(keyOutputs)), + multisignatureOutputs(std::move(multisignatureOutputs)), + extra(std::move(extra)) { +} + +BaseTransaction::BaseTransaction(BaseTransaction&& other) : blockIndex(other.blockIndex), unlockTime(other.unlockTime), keyOutputs(std::move(other.keyOutputs)), multisignatureOutputs(std::move(other.multisignatureOutputs)), extra(std::move(other.extra)) { +} + +uint64_t BaseTransaction::getBlockIndex() const { + return blockIndex; +} + +uint64_t BaseTransaction::getUnlockTime() const { + return unlockTime; +} + +uint32_t BaseTransaction::getOutputCount() const { + return static_cast(keyOutputs.size() + multisignatureOutputs.size()); +} + +BaseTransaction::OutputType BaseTransaction::getOutputType(uint32_t index) const { + auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); + if (iterator != keyOutputs.end() && iterator->index == index) { + return KEY_OUTPUT; + } + + return MULTISIGNATURE_OUTPUT; +} + +const KeyOutput& BaseTransaction::getKeyOutput(uint32_t index) const { + auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); + assert(iterator != keyOutputs.end()); + assert(iterator->index == index); + return iterator->output; +} + +const MultisignatureOutput& BaseTransaction::getMultisignatureOutput(uint32_t index) const { + auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); + assert(iterator != multisignatureOutputs.end()); + assert(iterator->index == index); + return iterator->output; +} + +const std::vector& BaseTransaction::getExtra() const { + return extra; +} + +} diff --git a/src/CryptoNote/BaseTransaction.h b/src/CryptoNote/BaseTransaction.h new file mode 100755 index 0000000000..d795cfd9bb --- /dev/null +++ b/src/CryptoNote/BaseTransaction.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "KeyOutput.h" +#include "MultisignatureOutput.h" + +namespace CryptoNote { + +class BaseTransaction { +public: + enum OutputType { + KEY_OUTPUT = 0, + MULTISIGNATURE_OUTPUT = 1 + }; + + struct KeyOutputEntry { + uint32_t index; + KeyOutput output; + }; + + struct MultisignatureOutputEntry { + uint32_t index; + MultisignatureOutput output; + }; + + BaseTransaction(uint64_t blockIndex, uint64_t unlockTime, std::vector&& keyOutputs, std::vector&& multisignatureOutputs, std::vector&& extra); + BaseTransaction(const BaseTransaction& other) = delete; + BaseTransaction(BaseTransaction&& other); + BaseTransaction& operator=(const BaseTransaction& other) = delete; + uint64_t getBlockIndex() const; + uint64_t getUnlockTime() const; + uint32_t getOutputCount() const; + OutputType getOutputType(uint32_t index) const; + const KeyOutput& getKeyOutput(uint32_t index) const; + const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; + const std::vector& getExtra() const; + +private: + uint64_t blockIndex; + uint64_t unlockTime; + std::vector keyOutputs; + std::vector multisignatureOutputs; + std::vector extra; +}; + +} diff --git a/src/CryptoNote/Block.cpp b/src/CryptoNote/Block.cpp new file mode 100755 index 0000000000..38f71359d4 --- /dev/null +++ b/src/CryptoNote/Block.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Block.h" +#include "KeyInput.h" +#include "KeyOutput.h" +#include "MultisignatureInput.h" +#include "MultisignatureOutput.h" +#include "Transaction.h" + +namespace CryptoNote { + +Block::Block( + uint8_t majorVersion, + uint8_t minorVersion, + uint64_t timestamp, + const crypto::hash& previousBlockHash, + BaseTransaction&& baseTransaction, + std::vector&& transactions, + uint8_t parentMajorVersion, + uint8_t parentMinorVersion, + uint32_t nonce, + const crypto::hash& parentPreviousBlockHash, + BaseTransaction&& parentBaseTransaction, + std::vector&& parentBaseTransactionBranch, + uint32_t parentTransactionCount, + std::vector&& branch) : + majorVersion(majorVersion), + minorVersion(minorVersion), + timestamp(timestamp), + previousBlockHash(previousBlockHash), + baseTransaction(std::move(baseTransaction)), + transactions(std::move(transactions)), + parentMajorVersion(parentMajorVersion), + parentMinorVersion(parentMinorVersion), + nonce(nonce), + parentPreviousBlockHash(parentPreviousBlockHash), + parentBaseTransaction(std::move(parentBaseTransaction)), + parentBaseTransactionBranch(std::move(parentBaseTransactionBranch)), + parentTransactionCount(parentTransactionCount), + branch(std::move(branch)) { +} + +uint8_t Block::getMajorVersion() const { + return majorVersion; +} + +uint8_t Block::getMinorVersion() const { + return minorVersion; +} + +uint64_t Block::getTimestamp() const { + return timestamp; +} + +const crypto::hash& Block::getPreviousBlockHash() const { + return previousBlockHash; +} + +const BaseTransaction& Block::getBaseTransaction() const { + return baseTransaction; +} + +uint32_t Block::getTransactionCount() const { + return static_cast(transactions.size()); +} + +const Transaction& Block::getTransaction(uint32_t index) const { + return transactions[index]; +} + +uint8_t Block::getParentMajorVersion() const { + return parentMajorVersion; +} + +uint8_t Block::getParentMinorVersion() const { + return parentMinorVersion; +} + +uint32_t Block::getNonce() const { + return nonce; +} + +const crypto::hash& Block::getParentPreviousBlockHash() const { + return parentPreviousBlockHash; +} + +const BaseTransaction& Block::getParentBaseTransaction() const { + return parentBaseTransaction; +} + +const std::vector& Block::getParentBaseTransactionBranch() const { + return parentBaseTransactionBranch; +} + +uint32_t Block::getParentTransactionCount() const { + return parentTransactionCount; +} + +const std::vector& Block::getBranch() const { + return branch; +} + +} diff --git a/src/CryptoNote/Block.h b/src/CryptoNote/Block.h new file mode 100755 index 0000000000..2aba9bdaf6 --- /dev/null +++ b/src/CryptoNote/Block.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "BaseTransaction.h" + +namespace CryptoNote { + +class Transaction; + +class Block { +public: + Block( + uint8_t majorVersion, + uint8_t minorVersion, + uint64_t timestamp, + const crypto::hash& previousBlockHash, + BaseTransaction&& baseTransaction, + std::vector&& transactions, + uint8_t parentMajorVersion, + uint8_t parentMinorVersion, + uint32_t nonce, + const crypto::hash& parentPreviousBlockHash, + BaseTransaction&& parentBaseTransaction, + std::vector&& parentBaseTransactionBranch, + uint32_t parentTransactionCount, + std::vector&& branch); + Block(const Block& other) = delete; + Block& operator=(const Block& other) = delete; + uint8_t getMajorVersion() const; + uint8_t getMinorVersion() const; + uint64_t getTimestamp() const; + const crypto::hash& getPreviousBlockHash() const; + const BaseTransaction& getBaseTransaction() const; + uint32_t getTransactionCount() const; + const Transaction& getTransaction(uint32_t index) const; + uint8_t getParentMajorVersion() const; + uint8_t getParentMinorVersion() const; + uint32_t getNonce() const; + const crypto::hash& getParentPreviousBlockHash() const; + const BaseTransaction& getParentBaseTransaction() const; + const std::vector& getParentBaseTransactionBranch() const; + uint32_t getParentTransactionCount() const; + const std::vector& getBranch() const; + +private: + uint8_t majorVersion; + uint8_t minorVersion; + uint64_t timestamp; + crypto::hash previousBlockHash; + BaseTransaction baseTransaction; + std::vector transactions; + uint8_t parentMajorVersion; + uint8_t parentMinorVersion; + uint32_t nonce; + crypto::hash parentPreviousBlockHash; + BaseTransaction parentBaseTransaction; + std::vector parentBaseTransactionBranch; + uint32_t parentTransactionCount; + std::vector branch; +}; + +} diff --git a/src/CryptoNote/KeyInput.cpp b/src/CryptoNote/KeyInput.cpp new file mode 100755 index 0000000000..ed5b7f0b40 --- /dev/null +++ b/src/CryptoNote/KeyInput.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "KeyInput.h" + +namespace CryptoNote { + +KeyInput::KeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage) : amount(amount), outputs(std::move(outputs)), keyImage(keyImage) { +} + +uint64_t KeyInput::getAmount() const { + return amount; +} + +uint32_t KeyInput::getOutputCount() const { + return static_cast(outputs.size()); +} + +uint32_t KeyInput::getOutputIndex(uint32_t index) const { + return outputs[index].index; +} + +const crypto::signature& KeyInput::getOutputSignature(uint32_t index) const { + return outputs[index].signature; +} + +const crypto::key_image& KeyInput::getKeyImage() const { + return keyImage; +} + +} diff --git a/src/CryptoNote/KeyInput.h b/src/CryptoNote/KeyInput.h new file mode 100755 index 0000000000..511d491a2d --- /dev/null +++ b/src/CryptoNote/KeyInput.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "../crypto/crypto.h" + +namespace CryptoNote { + +class KeyInput { +public: + struct Output { + uint32_t index; + crypto::signature signature; + }; + + KeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage); + KeyInput(const KeyInput& other) = delete; + KeyInput& operator=(const KeyInput& other) = delete; + uint64_t getAmount() const; + uint32_t getOutputCount() const; + uint32_t getOutputIndex(uint32_t index) const; + const crypto::signature& getOutputSignature(uint32_t index) const; + const crypto::key_image& getKeyImage() const; + +private: + uint64_t amount; + std::vector outputs; + crypto::key_image keyImage; +}; + +} diff --git a/src/CryptoNote/KeyOutput.cpp b/src/CryptoNote/KeyOutput.cpp new file mode 100755 index 0000000000..9a97a1e4bd --- /dev/null +++ b/src/CryptoNote/KeyOutput.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "KeyOutput.h" + +namespace CryptoNote { + +KeyOutput::KeyOutput(uint64_t amount, const crypto::public_key& key) : amount(amount), key(key) { +} + +uint64_t KeyOutput::getAmount() const { + return amount; +} + +const crypto::public_key& KeyOutput::getKey() const { + return key; +} + +} diff --git a/src/Platform/Windows/System/Event.h b/src/CryptoNote/KeyOutput.h similarity index 62% rename from src/Platform/Windows/System/Event.h rename to src/CryptoNote/KeyOutput.h index aab4d1a4f2..7f53420c64 100755 --- a/src/Platform/Windows/System/Event.h +++ b/src/CryptoNote/KeyOutput.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,29 +17,21 @@ #pragma once -namespace System { +#include "../crypto/crypto.h" -class Dispatcher; +namespace CryptoNote { -class Event { +class KeyOutput { public: - Event(); - explicit Event(Dispatcher& dispatcher); - Event(const Event&) = delete; - Event(Event&& other); - ~Event(); - Event& operator=(const Event&) = delete; - Event& operator=(Event&& other); - bool get() const; - void clear(); - void set(); - void wait(); + KeyOutput(uint64_t amount, const crypto::public_key& key); + KeyOutput(const KeyOutput& other) = delete; + KeyOutput& operator=(const KeyOutput& other) = delete; + uint64_t getAmount() const; + const crypto::public_key& getKey() const; private: - Dispatcher* dispatcher; - void* first; - void* last; - bool state; + uint64_t amount; + crypto::public_key key; }; } diff --git a/src/CryptoNote/MultisignatureInput.cpp b/src/CryptoNote/MultisignatureInput.cpp new file mode 100755 index 0000000000..36482e3ade --- /dev/null +++ b/src/CryptoNote/MultisignatureInput.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "MultisignatureInput.h" + +namespace CryptoNote { + +MultisignatureInput::MultisignatureInput(uint64_t amount, uint32_t outputIndex, std::vector&& signatures) : amount(amount), outputIndex(outputIndex), signatures(std::move(signatures)) { +} + +uint64_t MultisignatureInput::getAmount() const { + return amount; +} + +uint32_t MultisignatureInput::getOutputIndex() const { + return outputIndex; +} + +uint32_t MultisignatureInput::getSignatureCount() const { + return static_cast(signatures.size()); +} + +const crypto::signature& MultisignatureInput::getSignature(uint32_t index) const { + return signatures[index]; +} + +} diff --git a/src/CryptoNote/MultisignatureInput.h b/src/CryptoNote/MultisignatureInput.h new file mode 100755 index 0000000000..f6efca4068 --- /dev/null +++ b/src/CryptoNote/MultisignatureInput.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "../crypto/crypto.h" + +namespace CryptoNote { + +class MultisignatureInput { +public: + MultisignatureInput(uint64_t amount, uint32_t outputIndex, std::vector&& signatures); + MultisignatureInput(const MultisignatureInput& other) = delete; + MultisignatureInput& operator=(const MultisignatureInput& other) = delete; + uint64_t getAmount() const; + uint32_t getOutputIndex() const; + uint32_t getSignatureCount() const; + const crypto::signature& getSignature(uint32_t index) const; + +private: + uint64_t amount; + uint32_t outputIndex; + std::vector signatures; +}; + +} diff --git a/src/CryptoNote/MultisignatureOutput.cpp b/src/CryptoNote/MultisignatureOutput.cpp new file mode 100755 index 0000000000..2e346e2fcd --- /dev/null +++ b/src/CryptoNote/MultisignatureOutput.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "MultisignatureOutput.h" +#include + +namespace CryptoNote { + +MultisignatureOutput::MultisignatureOutput(uint64_t amount, std::vector&& keys, uint32_t requiredSignatureCount) : amount(amount), keys(std::move(keys)), requiredSignatureCount(requiredSignatureCount) { + assert(requiredSignatureCount <= keys.size()); +} + +uint64_t MultisignatureOutput::getAmount() const { + return amount; +} + +uint32_t MultisignatureOutput::getKeyCount() const { + return static_cast(keys.size()); +} + +const crypto::public_key& MultisignatureOutput::getKey(uint32_t index) const { + return keys[index]; +} + +uint32_t MultisignatureOutput::getRequiredSignatureCount() const { + return requiredSignatureCount; +} + +} diff --git a/src/CryptoNote/MultisignatureOutput.h b/src/CryptoNote/MultisignatureOutput.h new file mode 100755 index 0000000000..db1006ef37 --- /dev/null +++ b/src/CryptoNote/MultisignatureOutput.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "../crypto/crypto.h" + +namespace CryptoNote { + +class MultisignatureOutput { +public: + MultisignatureOutput(uint64_t amount, std::vector&& keys, uint32_t requiredSignatureCount); + MultisignatureOutput(const MultisignatureOutput& other) = delete; + MultisignatureOutput& operator=(const MultisignatureOutput& other) = delete; + uint64_t getAmount() const; + uint32_t getKeyCount() const; + const crypto::public_key& getKey(uint32_t index) const; + uint32_t getRequiredSignatureCount() const; + +private: + uint64_t amount; + std::vector keys; + uint32_t requiredSignatureCount; +}; + +} diff --git a/src/CryptoNote/Transaction.cpp b/src/CryptoNote/Transaction.cpp new file mode 100755 index 0000000000..6daa4367d4 --- /dev/null +++ b/src/CryptoNote/Transaction.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Transaction.h" +#include +#include + +namespace CryptoNote { + +Transaction::Transaction( + uint64_t unlockTime, + std::vector&& keyInputs, + std::vector&& multisignatureInputs, + std::vector&& keyOutputs, + std::vector&& multisignatureOutputs, + std::vector&& extra) : + unlockTime(unlockTime), + keyInputs(std::move(keyInputs)), + multisignatureInputs(std::move(multisignatureInputs)), + keyOutputs(std::move(keyOutputs)), + multisignatureOutputs(std::move(multisignatureOutputs)), + extra(std::move(extra)) { +} + +Transaction::Transaction( + Transaction&& other) : + unlockTime(other.unlockTime), + keyInputs(std::move(other.keyInputs)), + multisignatureInputs(std::move(other.multisignatureInputs)), + keyOutputs(std::move(other.keyOutputs)), + multisignatureOutputs(std::move(other.multisignatureOutputs)), + extra(std::move(other.extra)) { +} + +uint64_t Transaction::getUnlockTime() const { + return unlockTime; +} + +uint32_t Transaction::getInputCount() const { + return static_cast(keyInputs.size() + multisignatureInputs.size()); +} + +Transaction::InputType Transaction::getInputType(uint32_t index) const { + auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); + if (iterator != keyInputs.end() && iterator->index == index) { + return KEY_INPUT; + } + + return MULTISIGNATURE_INPUT; +} + +const KeyInput& Transaction::getKeyInput(uint32_t index) const { + auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); + assert(iterator != keyInputs.end()); + assert(iterator->index == index); + return iterator->input; +} + +const MultisignatureInput& Transaction::getMultisignatureInput(uint32_t index) const { + auto iterator = std::lower_bound(multisignatureInputs.begin(), multisignatureInputs.end(), index, [](const MultisignatureInputEntry& multisignatureInputEntry, uint32_t index)->bool { return multisignatureInputEntry.index < index; }); + assert(iterator != multisignatureInputs.end()); + assert(iterator->index == index); + return iterator->input; +} + +uint32_t Transaction::getOutputCount() const { + return static_cast(keyOutputs.size() + multisignatureOutputs.size()); +} + +Transaction::OutputType Transaction::getOutputType(uint32_t index) const { + auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); + if (iterator != keyOutputs.end() && iterator->index == index) { + return KEY_OUTPUT; + } + + return MULTISIGNATURE_OUTPUT; +} + +const KeyOutput& Transaction::getKeyOutput(uint32_t index) const { + auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); + assert(iterator != keyOutputs.end()); + assert(iterator->index == index); + return iterator->output; +} + +const MultisignatureOutput& Transaction::getMultisignatureOutput(uint32_t index) const { + auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); + assert(iterator != multisignatureOutputs.end()); + assert(iterator->index == index); + return iterator->output; +} + +const std::vector& Transaction::getExtra() const { + return extra; +} + +} diff --git a/src/CryptoNote/Transaction.h b/src/CryptoNote/Transaction.h new file mode 100755 index 0000000000..be346cea50 --- /dev/null +++ b/src/CryptoNote/Transaction.h @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "KeyInput.h" +#include "KeyOutput.h" +#include "MultisignatureInput.h" +#include "MultisignatureOutput.h" + +namespace CryptoNote { + +class Transaction { +public: + enum InputType { + KEY_INPUT = 0, + MULTISIGNATURE_INPUT = 1 + }; + + enum OutputType { + KEY_OUTPUT = 0, + MULTISIGNATURE_OUTPUT = 1 + }; + + struct KeyInputEntry { + uint32_t index; + KeyInput input; + }; + + struct KeyOutputEntry { + uint32_t index; + KeyOutput output; + }; + + struct MultisignatureInputEntry { + uint32_t index; + MultisignatureInput input; + }; + + struct MultisignatureOutputEntry { + uint32_t index; + MultisignatureOutput output; + }; + + Transaction( + uint64_t unlockTime, + std::vector&& keyInputs, + std::vector&& multisignatureInputs, + std::vector&& keyOutputs, + std::vector&& multisignatureOutputs, + std::vector&& extra); + Transaction(const Transaction& other) = delete; + Transaction(Transaction&& other); + Transaction& operator=(const Transaction& other) = delete; + uint64_t getUnlockTime() const; + uint32_t getInputCount() const; + InputType getInputType(uint32_t index) const; + const KeyInput& getKeyInput(uint32_t index) const; + const MultisignatureInput& getMultisignatureInput(uint32_t index) const; + uint32_t getOutputCount() const; + OutputType getOutputType(uint32_t index) const; + const KeyOutput& getKeyOutput(uint32_t index) const; + const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; + const std::vector& getExtra() const; + +private: + uint64_t unlockTime; + std::vector keyInputs; + std::vector multisignatureInputs; + std::vector keyOutputs; + std::vector multisignatureOutputs; + std::vector extra; +}; + +} diff --git a/src/CryptoNote/UnsignedKeyInput.cpp b/src/CryptoNote/UnsignedKeyInput.cpp new file mode 100755 index 0000000000..72224dec8d --- /dev/null +++ b/src/CryptoNote/UnsignedKeyInput.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "UnsignedKeyInput.h" + +namespace CryptoNote { + +UnsignedKeyInput::UnsignedKeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage) : amount(amount), outputs(std::move(outputs)), keyImage(keyImage) { +} + +uint64_t UnsignedKeyInput::getAmount() const { + return amount; +} + +uint32_t UnsignedKeyInput::getOutputCount() const { + return static_cast(outputs.size()); +} + +uint32_t UnsignedKeyInput::getOutputIndex(uint32_t index) const { + return outputs[index]; +} + +const crypto::key_image& UnsignedKeyInput::getKeyImage() const { + return keyImage; +} + +} diff --git a/src/CryptoNote/UnsignedKeyInput.h b/src/CryptoNote/UnsignedKeyInput.h new file mode 100755 index 0000000000..223c7c9388 --- /dev/null +++ b/src/CryptoNote/UnsignedKeyInput.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "../crypto/crypto.h" + +namespace CryptoNote { + +class UnsignedKeyInput { +public: + UnsignedKeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage); + UnsignedKeyInput(const UnsignedKeyInput& other) = delete; + UnsignedKeyInput& operator=(const UnsignedKeyInput& other) = delete; + uint64_t getAmount() const; + uint32_t getOutputCount() const; + uint32_t getOutputIndex(uint32_t index) const; + const crypto::key_image& getKeyImage() const; + +private: + uint64_t amount; + std::vector outputs; + crypto::key_image keyImage; +}; + +} diff --git a/src/CryptoNote/UnsignedMultisignatureInput.cpp b/src/CryptoNote/UnsignedMultisignatureInput.cpp new file mode 100755 index 0000000000..cfcd734f04 --- /dev/null +++ b/src/CryptoNote/UnsignedMultisignatureInput.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "UnsignedMultisignatureInput.h" + +namespace CryptoNote { + +UnsignedMultisignatureInput::UnsignedMultisignatureInput(uint64_t amount, uint32_t outputIndex) : amount(amount), outputIndex(outputIndex) { +} + +uint64_t UnsignedMultisignatureInput::getAmount() const { + return amount; +} + +uint32_t UnsignedMultisignatureInput::getOutputIndex() const { + return outputIndex; +} + +} diff --git a/src/CryptoNote/UnsignedMultisignatureInput.h b/src/CryptoNote/UnsignedMultisignatureInput.h new file mode 100755 index 0000000000..8f7433e47d --- /dev/null +++ b/src/CryptoNote/UnsignedMultisignatureInput.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace CryptoNote { + +class UnsignedMultisignatureInput { +public: + UnsignedMultisignatureInput(uint64_t amount, uint32_t outputIndex); + UnsignedMultisignatureInput(const UnsignedMultisignatureInput& other) = delete; + UnsignedMultisignatureInput& operator=(const UnsignedMultisignatureInput& other) = delete; + uint64_t getAmount() const; + uint32_t getOutputIndex() const; + +private: + uint64_t amount; + uint32_t outputIndex; +}; + +} diff --git a/src/CryptoNote/UnsignedTransaction.cpp b/src/CryptoNote/UnsignedTransaction.cpp new file mode 100755 index 0000000000..64cfe80cbe --- /dev/null +++ b/src/CryptoNote/UnsignedTransaction.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "UnsignedTransaction.h" +#include +#include + +namespace CryptoNote { + +UnsignedTransaction::UnsignedTransaction( + uint64_t unlockTime, + std::vector&& keyInputs, + std::vector&& multisignatureInputs, + std::vector&& keyOutputs, + std::vector&& multisignatureOutputs, + std::vector&& extra) : + unlockTime(unlockTime), + keyInputs(std::move(keyInputs)), + multisignatureInputs(std::move(multisignatureInputs)), + keyOutputs(std::move(keyOutputs)), + multisignatureOutputs(std::move(multisignatureOutputs)), + extra(std::move(extra)) { +} + +UnsignedTransaction::UnsignedTransaction( + UnsignedTransaction&& other) : + unlockTime(other.unlockTime), + keyInputs(std::move(other.keyInputs)), + multisignatureInputs(std::move(other.multisignatureInputs)), + keyOutputs(std::move(other.keyOutputs)), + multisignatureOutputs(std::move(other.multisignatureOutputs)), + extra(std::move(other.extra)) { +} + +uint64_t UnsignedTransaction::getUnlockTime() const { + return unlockTime; +} + +uint32_t UnsignedTransaction::getInputCount() const { + return static_cast(keyInputs.size() + multisignatureInputs.size()); +} + +UnsignedTransaction::InputType UnsignedTransaction::getInputType(uint32_t index) const { + auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); + if (iterator != keyInputs.end() && iterator->index == index) { + return KEY_INPUT; + } + + return MULTISIGNATURE_INPUT; +} + +const UnsignedKeyInput& UnsignedTransaction::getKeyInput(uint32_t index) const { + auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); + assert(iterator != keyInputs.end()); + assert(iterator->index == index); + return iterator->input; +} + +const UnsignedMultisignatureInput& UnsignedTransaction::getMultisignatureInput(uint32_t index) const { + auto iterator = std::lower_bound(multisignatureInputs.begin(), multisignatureInputs.end(), index, [](const MultisignatureInputEntry& multisignatureInputEntry, uint32_t index)->bool { return multisignatureInputEntry.index < index; }); + assert(iterator != multisignatureInputs.end()); + assert(iterator->index == index); + return iterator->input; +} + +uint32_t UnsignedTransaction::getOutputCount() const { + return static_cast(keyOutputs.size() + multisignatureOutputs.size()); +} + +UnsignedTransaction::OutputType UnsignedTransaction::getOutputType(uint32_t index) const { + auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); + if (iterator != keyOutputs.end() && iterator->index == index) { + return KEY_OUTPUT; + } + + return MULTISIGNATURE_OUTPUT; +} + +const KeyOutput& UnsignedTransaction::getKeyOutput(uint32_t index) const { + auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); + assert(iterator != keyOutputs.end()); + assert(iterator->index == index); + return iterator->output; +} + +const MultisignatureOutput& UnsignedTransaction::getMultisignatureOutput(uint32_t index) const { + auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); + assert(iterator != multisignatureOutputs.end()); + assert(iterator->index == index); + return iterator->output; +} + +const std::vector& UnsignedTransaction::getExtra() const { + return extra; +} + +} diff --git a/src/CryptoNote/UnsignedTransaction.h b/src/CryptoNote/UnsignedTransaction.h new file mode 100755 index 0000000000..b82246177a --- /dev/null +++ b/src/CryptoNote/UnsignedTransaction.h @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "KeyOutput.h" +#include "MultisignatureOutput.h" +#include "UnsignedKeyInput.h" +#include "UnsignedMultisignatureInput.h" + +namespace CryptoNote { + +class UnsignedTransaction { +public: + enum InputType { + KEY_INPUT = 0, + MULTISIGNATURE_INPUT = 1 + }; + + enum OutputType { + KEY_OUTPUT = 0, + MULTISIGNATURE_OUTPUT = 1 + }; + + struct KeyInputEntry { + uint32_t index; + UnsignedKeyInput input; + }; + + struct KeyOutputEntry { + uint32_t index; + KeyOutput output; + }; + + struct MultisignatureInputEntry { + uint32_t index; + UnsignedMultisignatureInput input; + }; + + struct MultisignatureOutputEntry { + uint32_t index; + MultisignatureOutput output; + }; + + UnsignedTransaction( + uint64_t unlockTime, + std::vector&& keyInputs, + std::vector&& multisignatureInputs, + std::vector&& keyOutputs, + std::vector&& multisignatureOutputs, + std::vector&& extra); + UnsignedTransaction(const UnsignedTransaction& other) = delete; + UnsignedTransaction(UnsignedTransaction&& other); + UnsignedTransaction& operator=(const UnsignedTransaction& other) = delete; + uint64_t getUnlockTime() const; + uint32_t getInputCount() const; + InputType getInputType(uint32_t index) const; + const UnsignedKeyInput& getKeyInput(uint32_t index) const; + const UnsignedMultisignatureInput& getMultisignatureInput(uint32_t index) const; + uint32_t getOutputCount() const; + OutputType getOutputType(uint32_t index) const; + const KeyOutput& getKeyOutput(uint32_t index) const; + const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; + const std::vector& getExtra() const; + +private: + uint64_t unlockTime; + std::vector keyInputs; + std::vector multisignatureInputs; + std::vector keyOutputs; + std::vector multisignatureOutputs; + std::vector extra; +}; + +} diff --git a/src/HTTP/HttpParser.cpp b/src/HTTP/HttpParser.cpp index 3e4efd2dfc..455a0cb065 100755 --- a/src/HTTP/HttpParser.cpp +++ b/src/HTTP/HttpParser.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,18 +16,32 @@ // along with Bytecoin. If not, see . #include "HttpParser.h" +#include "HttpParserErrorCodes.h" -#include +namespace { -namespace cryptonote { +void throwIfNotGood(std::istream& stream) { + if (!stream.good()) { + if (stream.eof()) { + throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::END_OF_STREAM)); + } else { + throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::STREAM_NOT_GOOD)); + } + } +} + +} + +namespace CryptoNote { HttpResponse::HTTP_STATUS HttpParser::parseResponseStatusFromString(const std::string& status) { - if (status == "200 OK" || status == "200 Ok") return cryptonote::HttpResponse::STATUS_200; - else if (status == "404 Not Found") return cryptonote::HttpResponse::STATUS_404; - else if (status == "500 Internal Server Error") return cryptonote::HttpResponse::STATUS_500; - else throw std::runtime_error("Unknown HTTP status code is given"); + if (status == "200 OK" || status == "200 Ok") return CryptoNote::HttpResponse::STATUS_200; + else if (status == "404 Not Found") return CryptoNote::HttpResponse::STATUS_404; + else if (status == "500 Internal Server Error") return CryptoNote::HttpResponse::STATUS_500; + else throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::UNEXPECTED_SYMBOL), + "Unknown HTTP status code is given"); - return cryptonote::HttpResponse::STATUS_200; //unaccessible + return CryptoNote::HttpResponse::STATUS_200; //unaccessible } @@ -61,9 +75,7 @@ void HttpParser::receiveResponse(std::istream& stream, HttpResponse& response) { stream.get(c); } - if (!stream.good()) { - throw std::runtime_error("Parser error: stream is not good"); - } + throwIfNotGood(stream); if (c == '\r') { stream.get(c); @@ -109,14 +121,12 @@ void HttpParser::readWord(std::istream& stream, std::string& word) { stream.get(c); } - if (!stream.good()) { - throw std::runtime_error("Parser error: stream is not good"); - } + throwIfNotGood(stream); if (c == '\r') { stream.get(c); if (c != '\n') { - throw std::runtime_error("Parser error: '\\n' symbol is expected"); + throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::UNEXPECTED_SYMBOL)); } } } @@ -146,7 +156,7 @@ bool HttpParser::readHeader(std::istream& stream, std::string& name, std::string } if (name.empty()) { - throw std::runtime_error("Header name must be not empty"); + throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::EMPTY_HEADER)); } if (isName) { @@ -165,20 +175,18 @@ bool HttpParser::readHeader(std::istream& stream, std::string& name, std::string } } - if (!stream.good()) { - throw std::runtime_error("Parser error: stream is not good"); - } + throwIfNotGood(stream); stream.get(c); if (c != '\n') { - throw std::runtime_error("Parser error: '\\n' symbol is expected"); + throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::UNEXPECTED_SYMBOL)); } c = stream.peek(); if (c == '\r') { stream.get(c).get(c); if (c != '\n') { - throw std::runtime_error("Parser error: '\\n' symbol is expected"); + throw std::system_error(make_error_code(CryptoNote::error::HttpParserErrorCodes::UNEXPECTED_SYMBOL)); } return false; //no more headers @@ -205,11 +213,7 @@ void HttpParser::readBody(std::istream& stream, std::string& body, const size_t ++read; } - if (!stream.good()) { - throw std::runtime_error("stream is not good"); - } + throwIfNotGood(stream); } } - - diff --git a/src/HTTP/HttpParser.h b/src/HTTP/HttpParser.h index 759917813b..c6a265fe89 100755 --- a/src/HTTP/HttpParser.h +++ b/src/HTTP/HttpParser.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,7 +24,7 @@ #include "HttpRequest.h" #include "HttpResponse.h" -namespace cryptonote { +namespace CryptoNote { //Blocking HttpParser class HttpParser { @@ -42,6 +42,6 @@ class HttpParser { void readBody(std::istream& stream, std::string& body, const size_t bodyLen); }; -} //namespace cryptonote +} //namespace CryptoNote #endif /* HTTPPARSER_H_ */ diff --git a/src/HTTP/HttpParserErrorCodes.cpp b/src/HTTP/HttpParserErrorCodes.cpp new file mode 100644 index 0000000000..f473b609e5 --- /dev/null +++ b/src/HTTP/HttpParserErrorCodes.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpParserErrorCodes.h" + +namespace CryptoNote { +namespace error { + +HttpParserErrorCategory HttpParserErrorCategory::INSTANCE; + +} //namespace error +} //namespace cryptonote diff --git a/src/HTTP/HttpParserErrorCodes.h b/src/HTTP/HttpParserErrorCodes.h new file mode 100644 index 0000000000..61a53e1a85 --- /dev/null +++ b/src/HTTP/HttpParserErrorCodes.h @@ -0,0 +1,83 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace CryptoNote { +namespace error { + +enum HttpParserErrorCodes { + STREAM_NOT_GOOD = 1, + END_OF_STREAM, + UNEXPECTED_SYMBOL, + EMPTY_HEADER +}; + +// custom category: +class HttpParserErrorCategory : public std::error_category { +public: + static HttpParserErrorCategory INSTANCE; + + virtual const char* name() const throw() { + return "HttpParserErrorCategory"; + } + + virtual std::error_condition default_error_condition(int ev) const throw() { + return std::error_condition(ev, *this); + } + + virtual std::string message(int ev) const { + switch (ev) { + case STREAM_NOT_GOOD: return "The stream is not good"; + case END_OF_STREAM: return "The stream is ended"; + case UNEXPECTED_SYMBOL: return "Unexpected symbol"; + case EMPTY_HEADER: return "The header name is empty"; + default: return "Unknown error"; + } + } + +private: + HttpParserErrorCategory() { + } +}; + +} //namespace error +} //namespace cryptonote + +inline std::error_code make_error_code(CryptoNote::error::HttpParserErrorCodes e) { + return std::error_code(static_cast(e), CryptoNote::error::HttpParserErrorCategory::INSTANCE); +} diff --git a/src/HTTP/HttpRequest.cpp b/src/HTTP/HttpRequest.cpp index 861a918be0..73d3cfa226 100755 --- a/src/HTTP/HttpRequest.cpp +++ b/src/HTTP/HttpRequest.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #include "HttpRequest.h" -namespace cryptonote { +namespace CryptoNote { const std::string& HttpRequest::getMethod() const { return method; @@ -70,4 +70,4 @@ namespace cryptonote { return os; } -} \ No newline at end of file +} diff --git a/src/HTTP/HttpRequest.h b/src/HTTP/HttpRequest.h index 2acb8c2e57..ebeff5fecb 100755 --- a/src/HTTP/HttpRequest.h +++ b/src/HTTP/HttpRequest.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class HttpRequest { public: typedef std::map Headers; @@ -50,4 +50,4 @@ namespace cryptonote { inline std::ostream& operator<<(std::ostream& os, const HttpRequest& resp) { return resp.printHttpRequest(os); } -} \ No newline at end of file +} diff --git a/src/HTTP/HttpResponse.cpp b/src/HTTP/HttpResponse.cpp index bb6ec3db5c..a15b982027 100755 --- a/src/HTTP/HttpResponse.cpp +++ b/src/HTTP/HttpResponse.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,13 +21,13 @@ namespace { -const char* getStatusString(cryptonote::HttpResponse::HTTP_STATUS status) { +const char* getStatusString(CryptoNote::HttpResponse::HTTP_STATUS status) { switch (status) { - case cryptonote::HttpResponse::STATUS_200: + case CryptoNote::HttpResponse::STATUS_200: return "200 OK"; - case cryptonote::HttpResponse::STATUS_404: + case CryptoNote::HttpResponse::STATUS_404: return "404 Not Found"; - case cryptonote::HttpResponse::STATUS_500: + case CryptoNote::HttpResponse::STATUS_500: return "500 Internal Server Error"; default: throw std::runtime_error("Unknown HTTP status code is given"); @@ -36,10 +36,22 @@ const char* getStatusString(cryptonote::HttpResponse::HTTP_STATUS status) { return ""; //unaccessible } +const char* getErrorBody(CryptoNote::HttpResponse::HTTP_STATUS status) { + switch (status) { + case CryptoNote::HttpResponse::STATUS_404: + return "Requested url is not found\n"; + case CryptoNote::HttpResponse::STATUS_500: + return "Internal server error is occured\n"; + default: + throw std::runtime_error("Error body for given status is not available"); + } + + return ""; //unaccessible +} } //namespace -namespace cryptonote { +namespace CryptoNote { HttpResponse::HttpResponse() { status = STATUS_200; @@ -48,6 +60,10 @@ HttpResponse::HttpResponse() { void HttpResponse::setStatus(HTTP_STATUS s) { status = s; + + if (status != HttpResponse::STATUS_200) { + setBody(getErrorBody(status)); + } } void HttpResponse::addHeader(const std::string& name, const std::string& value) { @@ -78,6 +94,4 @@ std::ostream& HttpResponse::printHttpResponse(std::ostream& os) const { return os; } -} //namespace cryptonote - - +} //namespace CryptoNote diff --git a/src/HTTP/HttpResponse.h b/src/HTTP/HttpResponse.h index 3fffd2adea..46403a0c71 100755 --- a/src/HTTP/HttpResponse.h +++ b/src/HTTP/HttpResponse.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class HttpResponse { public: @@ -54,4 +54,4 @@ namespace cryptonote { return resp.printHttpResponse(os); } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/inprocess_node/InProcessNode.cpp b/src/InProcessNode/InProcessNode.cpp similarity index 71% rename from src/inprocess_node/InProcessNode.cpp rename to src/InProcessNode/InProcessNode.cpp index a92257c0fd..ebfd5b60f6 100644 --- a/src/inprocess_node/InProcessNode.cpp +++ b/src/InProcessNode/InProcessNode.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,6 @@ #include #include -#include "cryptonote_core/connection_context.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/verification_context.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" @@ -28,7 +27,7 @@ namespace CryptoNote { -InProcessNode::InProcessNode(cryptonote::ICore& core, cryptonote::ICryptonoteProtocolQuery& protocol) : +InProcessNode::InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : state(NOT_INITIALIZED), core(core), protocol(protocol) @@ -36,14 +35,22 @@ InProcessNode::InProcessNode(cryptonote::ICore& core, cryptonote::ICryptonotePro } InProcessNode::~InProcessNode() { - shutdown(); + doShutdown(); } bool InProcessNode::addObserver(INodeObserver* observer) { + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } + return observerManager.add(observer); } bool InProcessNode::removeObserver(INodeObserver* observer) { + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } + return observerManager.remove(observer); } @@ -52,7 +59,7 @@ void InProcessNode::init(const Callback& callback) { std::error_code ec; if (state != NOT_INITIALIZED) { - ec = make_error_code(cryptonote::error::ALREADY_INITIALIZED); + ec = make_error_code(CryptoNote::error::ALREADY_INITIALIZED); } else { protocol.addObserver(this); core.addObserver(this); @@ -67,6 +74,10 @@ void InProcessNode::init(const Callback& callback) { } bool InProcessNode::shutdown() { + return doShutdown(); +} + +bool InProcessNode::doShutdown() { std::unique_lock lock(mutex); if (state != INITIALIZED) { return false; @@ -84,16 +95,16 @@ bool InProcessNode::shutdown() { } void InProcessNode::workerFunc() { - ioService.run(); + ioService.run(); } -void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, +void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); - callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); return; } @@ -108,7 +119,7 @@ void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::l ); } -void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, +void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { std::error_code ec; @@ -121,24 +132,24 @@ void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, st } //it's always protected with mutex -std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight) { +std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight) { if (state != INITIALIZED) { - return make_error_code(cryptonote::error::NOT_INITIALIZED); + return make_error_code(CryptoNote::error::NOT_INITIALIZED); } try { uint64_t totalHeight; - std::list > > bs; + std::list > > bs; if (!core.find_blockchain_supplement(knownBlockIds, bs, totalHeight, startHeight, 1000)) { - return make_error_code(cryptonote::error::REQUEST_ERROR); + return make_error_code(CryptoNote::error::REQUEST_ERROR); } for (auto& b : bs) { - cryptonote::block_complete_entry be; - be.block = cryptonote::block_to_blob(b.first); + CryptoNote::block_complete_entry be; + be.block = CryptoNote::block_to_blob(b.first); for (auto& t : b.second) { - be.txs.push_back(cryptonote::tx_to_blob(t)); + be.txs.push_back(CryptoNote::tx_to_blob(t)); } newBlocks.push_back(std::move(be)); @@ -146,7 +157,7 @@ std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlo } catch (std::system_error& e) { return e.code(); } catch (std::exception&) { - return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); @@ -158,7 +169,7 @@ void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transact std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); - callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); return; } @@ -187,30 +198,30 @@ void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& tra //it's always protected with mutex std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { if (state != INITIALIZED) { - return make_error_code(cryptonote::error::NOT_INITIALIZED); + return make_error_code(CryptoNote::error::NOT_INITIALIZED); } try { bool r = core.get_tx_outputs_gindexs(transactionHash, outsGlobalIndices); if(!r) { - return make_error_code(cryptonote::error::REQUEST_ERROR); + return make_error_code(CryptoNote::error::REQUEST_ERROR); } } catch (std::system_error& e) { return e.code(); } catch (std::exception&) { - return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } void InProcessNode::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback) + std::vector& result, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); - callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); return; } @@ -226,7 +237,7 @@ void InProcessNode::getRandomOutsByAmounts(std::vector&& amounts, uint } void InProcessNode::getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback) + std::vector& result, const Callback& callback) { std::error_code ec; { @@ -238,38 +249,38 @@ void InProcessNode::getRandomOutsByAmountsAsync(std::vector& amounts, } //it's always protected with mutex -std::error_code InProcessNode::doGetRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result) { +std::error_code InProcessNode::doGetRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result) { if (state != INITIALIZED) { - return make_error_code(cryptonote::error::NOT_INITIALIZED); + return make_error_code(CryptoNote::error::NOT_INITIALIZED); } try { - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res; - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req; req.amounts = amounts; req.outs_count = outsCount; if(!core.get_random_outs_for_amounts(req, res)) { - return make_error_code(cryptonote::error::REQUEST_ERROR); + return make_error_code(CryptoNote::error::REQUEST_ERROR); } result = std::move(res.outs); } catch (std::system_error& e) { return e.code(); } catch (std::exception&) { - return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } -void InProcessNode::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) +void InProcessNode::relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); - callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); return; } @@ -282,7 +293,7 @@ void InProcessNode::relayTransaction(const cryptonote::Transaction& transaction, ); } -void InProcessNode::relayTransactionAsync(const cryptonote::Transaction& transaction, const Callback& callback) { +void InProcessNode::relayTransactionAsync(const CryptoNote::Transaction& transaction, const Callback& callback) { std::error_code ec; { std::unique_lock lock(mutex); @@ -293,35 +304,34 @@ void InProcessNode::relayTransactionAsync(const cryptonote::Transaction& transac } //it's always protected with mutex -std::error_code InProcessNode::doRelayTransaction(const cryptonote::Transaction& transaction) { +std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& transaction) { if (state != INITIALIZED) { - return make_error_code(cryptonote::error::NOT_INITIALIZED); + return make_error_code(CryptoNote::error::NOT_INITIALIZED); } try { - cryptonote::blobdata txBlob = cryptonote::tx_to_blob(transaction); - cryptonote::tx_verification_context tvc = boost::value_initialized(); + CryptoNote::blobdata txBlob = CryptoNote::tx_to_blob(transaction); + CryptoNote::tx_verification_context tvc = boost::value_initialized(); if(!core.handle_incoming_tx(txBlob, tvc, false)) { - return make_error_code(cryptonote::error::REQUEST_ERROR); + return make_error_code(CryptoNote::error::REQUEST_ERROR); } if(tvc.m_verifivation_failed) { - return make_error_code(cryptonote::error::REQUEST_ERROR); + return make_error_code(CryptoNote::error::REQUEST_ERROR); } if(!tvc.m_should_be_relayed) { - return make_error_code(cryptonote::error::REQUEST_ERROR); + return make_error_code(CryptoNote::error::REQUEST_ERROR); } - cryptonote::cryptonote_connection_context fake_context = boost::value_initialized(); - cryptonote::NOTIFY_NEW_TRANSACTIONS::request r; + CryptoNote::NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(txBlob); - core.get_protocol()->relay_transactions(r, fake_context); + core.get_protocol()->relay_transactions(r); } catch (std::system_error& e) { return e.code(); } catch (std::exception&) { - return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); @@ -330,23 +340,50 @@ std::error_code InProcessNode::doRelayTransaction(const cryptonote::Transaction& size_t InProcessNode::getPeerCount() const { std::unique_lock lock(mutex); if (state != INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } return protocol.getPeerCount(); } +uint64_t InProcessNode::getLocalBlockCount() const { + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } + } + + uint64_t lastIndex; + crypto::hash ignore; + + core.get_blockchain_top(lastIndex, ignore); + + return lastIndex + 1; +} + +uint64_t InProcessNode::getKnownBlockCount() const { + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } + } + + return protocol.getObservedHeight(); +} + uint64_t InProcessNode::getLastLocalBlockHeight() const { std::unique_lock lock(mutex); if (state != INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } uint64_t height; crypto::hash ignore; if (!core.get_blockchain_top(height, ignore)) { - throw std::system_error(make_error_code(cryptonote::error::INTERNAL_NODE_ERROR)); + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); } return height; @@ -355,16 +392,24 @@ uint64_t InProcessNode::getLastLocalBlockHeight() const { uint64_t InProcessNode::getLastKnownBlockHeight() const { std::unique_lock lock(mutex); if (state != INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } - return protocol.getObservedHeight(); + return protocol.getObservedHeight() - 1; +} + +void InProcessNode::peerCountUpdated(size_t count) { + observerManager.notify(&INodeObserver::peerCountUpdated, count); +} + +void InProcessNode::lastKnownBlockHeightUpdated(uint64_t height) { + observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); } uint64_t InProcessNode::getLastLocalBlockTimestamp() const { std::unique_lock lock(mutex); if (state != INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } lock.unlock(); @@ -372,25 +417,17 @@ uint64_t InProcessNode::getLastLocalBlockTimestamp() const { crypto::hash hash; if (!core.get_blockchain_top(ignore, hash)) { - throw std::system_error(make_error_code(cryptonote::error::INTERNAL_NODE_ERROR)); + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); } - cryptonote::Block block; + CryptoNote::Block block; if (!core.getBlockByHash(hash, block)) { - throw std::system_error(make_error_code(cryptonote::error::INTERNAL_NODE_ERROR)); + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); } return block.timestamp; } -void InProcessNode::peerCountUpdated(size_t count) { - observerManager.notify(&INodeObserver::peerCountUpdated, count); -} - -void InProcessNode::lastKnownBlockHeightUpdated(uint64_t height) { - observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); -} - void InProcessNode::blockchainUpdated() { uint64_t height; crypto::hash ignore; @@ -409,7 +446,7 @@ void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_ std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); - callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); return; } @@ -440,10 +477,10 @@ void InProcessNode::queryBlocksAsync(std::list& knownBlockIds, uin std::error_code InProcessNode::doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight) { uint64_t currentHeight, fullOffset; - std::list entries; + std::list entries; if (!core.queryBlocks(knownBlockIds, timestamp, startHeight, currentHeight, fullOffset, entries)) { - return make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } for (const auto& entry: entries) { @@ -458,13 +495,13 @@ std::error_code InProcessNode::doQueryBlocks(std::list&& knownBloc return std::error_code(); } -void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, crypto::hash knownBlockId, bool& isBcActual, std::vector& newTxs, +void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, crypto::hash knownBlockId, bool& isBcActual, std::vector& newTxs, std::vector& deletedTxIds, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); - callback(make_error_code(cryptonote::error::NOT_INITIALIZED)); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); return; } @@ -481,13 +518,13 @@ void InProcessNode::getPoolSymmetricDifference(std::vector&& known ); } -void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, +void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { std::error_code ec = std::error_code(); std::unique_lock lock(mutex); if (!core.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, is_bc_actual, new_txs, deleted_tx_ids)) { - ec = make_error_code(cryptonote::error::INTERNAL_NODE_ERROR); + ec = make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } lock.unlock(); @@ -495,4 +532,3 @@ void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& k } } //namespace CryptoNote - diff --git a/src/inprocess_node/InProcessNode.h b/src/InProcessNode/InProcessNode.h similarity index 79% rename from src/inprocess_node/InProcessNode.h rename to src/InProcessNode/InProcessNode.h index 1010443b69..59674e2704 100644 --- a/src/inprocess_node/InProcessNode.h +++ b/src/InProcessNode/InProcessNode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,20 +22,18 @@ #include "cryptonote_protocol/ICryptonoteProtocolObserver.h" #include "cryptonote_core/ICore.h" #include "cryptonote_core/ICoreObserver.h" -#include "common/ObserverManager.h" +#include "Common/ObserverManager.h" #include #include -namespace cryptonote { -class core; -} - namespace CryptoNote { -class InProcessNode : public INode, public cryptonote::ICryptonoteProtocolObserver, public cryptonote::ICoreObserver { +class core; + +class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserver, public CryptoNote::ICoreObserver { public: - InProcessNode(cryptonote::ICore& core, cryptonote::ICryptonoteProtocolQuery& protocol); + InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol); InProcessNode(const InProcessNode&) = delete; InProcessNode(InProcessNode&&) = delete; @@ -54,46 +52,50 @@ class InProcessNode : public INode, public cryptonote::ICryptonoteProtocolObserv virtual size_t getPeerCount() const; virtual uint64_t getLastLocalBlockHeight() const; virtual uint64_t getLastKnownBlockHeight() const; + virtual uint64_t getLocalBlockCount() const override; + virtual uint64_t getKnownBlockCount() const override; virtual uint64_t getLastLocalBlockTimestamp() const override; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback) override; - virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) override; + std::vector& result, const Callback& callback) override; + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override; virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; + private: virtual void peerCountUpdated(size_t count) override; virtual void lastKnownBlockHeightUpdated(uint64_t height) override; virtual void blockchainUpdated() override; virtual void poolUpdated() override; - void getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); - std::error_code doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight); + void getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + std::error_code doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight); void getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); std::error_code doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); void getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback); + std::vector& result, const Callback& callback); std::error_code doGetRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, - std::vector& result); + std::vector& result); - void relayTransactionAsync(const cryptonote::Transaction& transaction, const Callback& callback); - std::error_code doRelayTransaction(const cryptonote::Transaction& transaction); + void relayTransactionAsync(const CryptoNote::Transaction& transaction, const Callback& callback); + std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction); void queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); std::error_code doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight); - void getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, + void getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback); void workerFunc(); + bool doShutdown(); enum State { NOT_INITIALIZED, @@ -101,8 +103,8 @@ class InProcessNode : public INode, public cryptonote::ICryptonoteProtocolObserv }; State state; - cryptonote::ICore& core; - cryptonote::ICryptonoteProtocolQuery& protocol; + CryptoNote::ICore& core; + CryptoNote::ICryptonoteProtocolQuery& protocol; tools::ObserverManager observerManager; boost::asio::io_service ioService; @@ -113,6 +115,3 @@ class InProcessNode : public INode, public cryptonote::ICryptonoteProtocolObserv }; } //namespace CryptoNote - - - diff --git a/src/inprocess_node/InProcessNodeErrors.cpp b/src/InProcessNode/InProcessNodeErrors.cpp similarity index 88% rename from src/inprocess_node/InProcessNodeErrors.cpp rename to src/InProcessNode/InProcessNodeErrors.cpp index 9f63125d12..a58e79c3c2 100644 --- a/src/inprocess_node/InProcessNodeErrors.cpp +++ b/src/InProcessNode/InProcessNodeErrors.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,11 +17,10 @@ #include "InProcessNodeErrors.h" -namespace cryptonote { +namespace CryptoNote { namespace error { InProcessNodeErrorCategory InProcessNodeErrorCategory::INSTANCE; } //namespace error -} //namespace cryptonote - +} //namespace CryptoNote diff --git a/src/inprocess_node/InProcessNodeErrors.h b/src/InProcessNode/InProcessNodeErrors.h similarity index 88% rename from src/inprocess_node/InProcessNodeErrors.h rename to src/InProcessNode/InProcessNodeErrors.h index 4fdf82bcc3..b9b554b197 100644 --- a/src/inprocess_node/InProcessNodeErrors.h +++ b/src/InProcessNode/InProcessNodeErrors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { namespace error { enum InProcessNodeErrorCodes { @@ -62,9 +62,8 @@ class InProcessNodeErrorCategory : public std::error_category { }; } //namespace error -} //namespace cryptonote +} //namespace CryptoNote -inline std::error_code make_error_code(cryptonote::error::InProcessNodeErrorCodes e) { - return std::error_code(static_cast(e), cryptonote::error::InProcessNodeErrorCategory::INSTANCE); +inline std::error_code make_error_code(CryptoNote::error::InProcessNodeErrorCodes e) { + return std::error_code(static_cast(e), CryptoNote::error::InProcessNodeErrorCategory::INSTANCE); } - diff --git a/src/Logging/CommonLogger.cpp b/src/Logging/CommonLogger.cpp new file mode 100755 index 0000000000..005d2e66a7 --- /dev/null +++ b/src/Logging/CommonLogger.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CommonLogger.h" + +namespace Logging { + +namespace { + +std::string formatPattern(const std::string& pattern, const std::string& category, Level level, boost::posix_time::ptime time) { + std::stringstream s; + + for (const char* p = pattern.c_str(); p && *p != 0; ++p) { + if (*p == '%') { + ++p; + switch (*p) { + case 0: + break; + case 'C': + s << category; + break; + case 'D': + s << time.date(); + break; + case 'T': + s << time.time_of_day(); + break; + case 'L': + s << ILogger::LEVEL_NAMES[level]; + break; + default: + s << *p; + } + } else { + s << *p; + } + } + + return s.str(); +} + +} + +void CommonLogger::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { + if (level <= logLevel && disabledCategories.count(category) == 0) { + std::string body2 = body; + if (!pattern.empty()) { + size_t insertPos = 0; + if (!body2.empty() && body2[0] == ILogger::COLOR_DELIMETER) { + size_t delimPos = body2.find(ILogger::COLOR_DELIMETER, 1); + if (delimPos != std::string::npos) { + insertPos = delimPos + 1; + } + } + + body2.insert(insertPos, formatPattern(pattern, category, level, time)); + } + + doLogString(body2); + } +} + +void CommonLogger::setPattern(const std::string& pattern) { + this->pattern = pattern; +} + +void CommonLogger::enableCategory(const std::string& category) { + disabledCategories.erase(category); +} + +void CommonLogger::disableCategory(const std::string& category) { + disabledCategories.insert(category); +} + +void CommonLogger::setMaxLevel(Level level) { + logLevel = level; +} + +CommonLogger::CommonLogger(Level level) : logLevel(level), pattern("%D %T %L [%C] ") { +} + +void CommonLogger::doLogString(const std::string& message) { +} + +} diff --git a/src/Logging/CommonLogger.h b/src/Logging/CommonLogger.h new file mode 100755 index 0000000000..0517314515 --- /dev/null +++ b/src/Logging/CommonLogger.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "ILogger.h" + +namespace Logging { + +class CommonLogger : public ILogger { +public: + + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; + virtual void enableCategory(const std::string& category); + virtual void disableCategory(const std::string& category); + virtual void setMaxLevel(Level level); + + void setPattern(const std::string& pattern); + +protected: + std::set disabledCategories; + Level logLevel; + std::string pattern; + + CommonLogger(Level level); + virtual void doLogString(const std::string& message); +}; + +} diff --git a/src/Logging/ConsoleLogger.cpp b/src/Logging/ConsoleLogger.cpp new file mode 100755 index 0000000000..135d6de67f --- /dev/null +++ b/src/Logging/ConsoleLogger.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ConsoleLogger.h" +#include +#include +#include + + +namespace Logging { + +using Common::Console::Color; + +ConsoleLogger::ConsoleLogger(Level level) : CommonLogger(level) { +} + +void ConsoleLogger::doLogString(const std::string& message) { + std::lock_guard lock(mutex); + bool readingText = true; + bool changedColor = false; + std::string color = ""; + + static std::unordered_map colorMapping = { + { BLUE, Color::Blue }, + { GREEN, Color::Green }, + { RED, Color::Red }, + { YELLOW, Color::Yellow }, + { WHITE, Color::White }, + { CYAN, Color::Cyan }, + { MAGENTA, Color::Magenta }, + + { BRIGHT_BLUE, Color::BrightBlue }, + { BRIGHT_GREEN, Color::BrightGreen }, + { BRIGHT_RED, Color::BrightRed }, + { BRIGHT_YELLOW, Color::BrightYellow }, + { BRIGHT_WHITE, Color::BrightWhite }, + { BRIGHT_CYAN, Color::BrightCyan }, + { BRIGHT_MAGENTA, Color::BrightMagenta }, + + { DEFAULT, Color::Default } + }; + + for (size_t charPos = 0; charPos < message.size(); ++charPos) { + if (message[charPos] == ILogger::COLOR_DELIMETER) { + readingText = !readingText; + color += message[charPos]; + if (readingText) { + auto it = colorMapping.find(color); + Common::Console::setTextColor(it == colorMapping.end() ? Color::Default : it->second); + changedColor = true; + color.clear(); + } + } else if (readingText) { + std::cout << message[charPos]; + } else { + color += message[charPos]; + } + } + + if (changedColor) { + Common::Console::setTextColor(Color::Default); + } +} + +} diff --git a/src/p2p/stdafx.h b/src/Logging/ConsoleLogger.h old mode 100644 new mode 100755 similarity index 68% rename from src/p2p/stdafx.h rename to src/Logging/ConsoleLogger.h index 4ec27541c2..13b355b0ab --- a/src/p2p/stdafx.h +++ b/src/Logging/ConsoleLogger.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,21 +17,20 @@ #pragma once -#include "targetver.h" +#include +#include "CommonLogger.h" +namespace Logging { -#if !defined(__GNUC__) -#define _CRTDBG_MAP_ALLOC -#include -#include -#endif +class ConsoleLogger : public CommonLogger { +public: + ConsoleLogger(Level level = DEBUGGING); +protected: + virtual void doLogString(const std::string& message) override; +private: + std::mutex mutex; +}; -#include - - -#define BOOST_FILESYSTEM_VERSION 3 -#define ENABLE_RELEASE_LOGGING -#include "log_opt_defs.h" -#include "misc_log_ex.h" +} diff --git a/src/Logging/FileLogger.cpp b/src/Logging/FileLogger.cpp new file mode 100755 index 0000000000..75fc3039c2 --- /dev/null +++ b/src/Logging/FileLogger.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "FileLogger.h" + +namespace Logging { + +FileLogger::FileLogger(Level level) : StreamLogger(level) { +} + +void FileLogger::init(const std::string& fileName) { + fileStream.open(fileName, std::ios::app); + StreamLogger::attachToStream(fileStream); +} + +} diff --git a/src/Logging/FileLogger.h b/src/Logging/FileLogger.h new file mode 100755 index 0000000000..fad9d96f89 --- /dev/null +++ b/src/Logging/FileLogger.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "StreamLogger.h" + +namespace Logging { + +class FileLogger : public StreamLogger { +public: + FileLogger(Level level = DEBUGGING); + void init(const std::string& filename); + +private: + std::ofstream fileStream; +}; + +} diff --git a/src/Logging/ILogger.cpp b/src/Logging/ILogger.cpp new file mode 100755 index 0000000000..7aa23f7412 --- /dev/null +++ b/src/Logging/ILogger.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ILogger.h" + +namespace Logging { + +const std::string BLUE = "\x1F""BLUE\x1F"; +const std::string GREEN = "\x1F""GREEN\x1F"; +const std::string RED = "\x1F""RED\x1F"; +const std::string YELLOW = "\x1F""YELLOW\x1F"; +const std::string WHITE = "\x1F""WHITE\x1F"; +const std::string CYAN = "\x1F""CYAN\x1F"; +const std::string MAGENTA = "\x1F""MAGENTA\x1F"; +const std::string BRIGHT_BLUE = "\x1F""BRIGHT_BLUE\x1F"; +const std::string BRIGHT_GREEN = "\x1F""BRIGHT_GREEN\x1F"; +const std::string BRIGHT_RED = "\x1F""BRIGHT_RED\x1F"; +const std::string BRIGHT_YELLOW = "\x1F""BRIGHT_YELLOW\x1F"; +const std::string BRIGHT_WHITE = "\x1F""BRIGHT_WHITE\x1F"; +const std::string BRIGHT_CYAN = "\x1F""BRIGHT_CYAN\x1F"; +const std::string BRIGHT_MAGENTA = "\x1F""BRIGHT_MAGENTA\x1F"; +const std::string DEFAULT = "\x1F""DEFAULT\x1F"; + +const char ILogger::COLOR_DELIMETER = '\x1F'; + +const std::array ILogger::LEVEL_NAMES = { + {"FATAL", + "ERROR", + "WARNING", + "INFO", + "DEBUG", + "TRACE"} +}; + +} diff --git a/src/Logging/ILogger.h b/src/Logging/ILogger.h new file mode 100755 index 0000000000..89f9955823 --- /dev/null +++ b/src/Logging/ILogger.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#undef ERROR + +namespace Logging { + +enum Level { + FATAL = 0, + ERROR = 1, + WARNING = 2, + INFO = 3, + DEBUGGING = 4, + TRACE = 5 +}; + +extern const std::string BLUE; +extern const std::string GREEN; +extern const std::string RED; +extern const std::string YELLOW; +extern const std::string WHITE; +extern const std::string CYAN; +extern const std::string MAGENTA; +extern const std::string BRIGHT_BLUE; +extern const std::string BRIGHT_GREEN; +extern const std::string BRIGHT_RED; +extern const std::string BRIGHT_YELLOW; +extern const std::string BRIGHT_WHITE; +extern const std::string BRIGHT_CYAN; +extern const std::string BRIGHT_MAGENTA; +extern const std::string DEFAULT; + +class ILogger { +public: + const static char COLOR_DELIMETER; + + const static std::array LEVEL_NAMES; + + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) = 0; +}; + +} diff --git a/src/serialization/debug_archive.h b/src/Logging/LoggerGroup.cpp old mode 100644 new mode 100755 similarity index 50% rename from src/serialization/debug_archive.h rename to src/Logging/LoggerGroup.cpp index 7bb4f39058..0eeb32df4c --- a/src/serialization/debug_archive.h +++ b/src/Logging/LoggerGroup.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,27 +15,28 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#pragma once +#include "LoggerGroup.h" +#include -#include "json_archive.h" -#include "variant.h" +namespace Logging { -template -struct debug_archive : public json_archive { - typedef typename json_archive::stream_type stream_type; +LoggerGroup::LoggerGroup(Level level) : CommonLogger(level) { +} - debug_archive(stream_type &s) : json_archive(s) { } -}; +void LoggerGroup::addLogger(ILogger& logger) { + loggers.push_back(&logger); +} -template -struct serializer, T> -{ - static void serialize(debug_archive &ar, T &v) - { - ar.begin_object(); - ar.tag(variant_serialization_traits, T>::get_tag()); - serializer, T>::serialize(ar, v); - ar.end_object(); - ar.stream() << std::endl; +void LoggerGroup::removeLogger(ILogger& logger) { + loggers.erase(std::remove(loggers.begin(), loggers.end(), &logger), loggers.end()); +} + +void LoggerGroup::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { + if (level <= logLevel && disabledCategories.count(category) == 0) { + for (auto& logger : loggers) { + (*logger)(category, level, time, body); + } } -}; +} + +} diff --git a/src/Logging/LoggerGroup.h b/src/Logging/LoggerGroup.h new file mode 100755 index 0000000000..8afe7fcc90 --- /dev/null +++ b/src/Logging/LoggerGroup.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "CommonLogger.h" + +namespace Logging { + +class LoggerGroup : public CommonLogger { +public: + LoggerGroup(Level level = DEBUGGING); + + void addLogger(ILogger& logger); + void removeLogger(ILogger& logger); + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; + +protected: + std::vector loggers; +}; + +} diff --git a/src/Logging/LoggerManager.cpp b/src/Logging/LoggerManager.cpp new file mode 100755 index 0000000000..31eba96725 --- /dev/null +++ b/src/Logging/LoggerManager.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LoggerManager.h" +#include +#include "ConsoleLogger.h" +#include "FileLogger.h" + +namespace Logging { + +using Common::JsonValue; + +LoggerManager::LoggerManager() { +} + +void LoggerManager::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { + std::unique_lock lock(reconfigureLock); + LoggerGroup::operator()(category, level, time, body); +} + +void LoggerManager::configure(const JsonValue& val) { + std::unique_lock lock(reconfigureLock); + loggers.clear(); + LoggerGroup::loggers.clear(); + Level globalLevel; + if (val.count("globalLevel")) { + auto levelVal = val("globalLevel"); + if (levelVal.isInteger()) { + globalLevel = static_cast(levelVal.getInteger()); + } else { + throw std::runtime_error("parameter globalLevel has wrong type"); + } + } else { + globalLevel = TRACE; + } + std::vector globalDisabledCategories; + + if (val.count("globalDisabledCategories")) { + auto globalDisabledCategoriesList = val("globalDisabledCategories"); + if (globalDisabledCategoriesList.isArray()) { + size_t countOfCategories = globalDisabledCategoriesList.size(); + for (size_t i = 0; i < countOfCategories; ++i) { + auto categoryVal = globalDisabledCategoriesList[i]; + if (categoryVal.isString()) { + globalDisabledCategories.push_back(categoryVal.getString()); + } + } + } else { + throw std::runtime_error("parameter globalDisabledCategories has wrong type"); + } + } + + if (val.count("loggers")) { + auto loggersList = val("loggers"); + if (loggersList.isArray()) { + size_t countOfLoggers = loggersList.size(); + for (size_t i = 0; i < countOfLoggers; ++i) { + auto loggerConfiguration = loggersList[i]; + if (!loggerConfiguration.isObject()) { + throw std::runtime_error("loggers element must be objects"); + } + + Level level = INFO; + if (loggerConfiguration.count("level")) { + level = static_cast(loggerConfiguration("level").getInteger()); + } + + std::string type = loggerConfiguration("type").getString(); + std::unique_ptr logger; + + if (type == "console") { + logger.reset(new ConsoleLogger(level)); + } else if (type == "file") { + std::string filename = loggerConfiguration("filename").getString(); + auto fileLogger = new FileLogger(level); + fileLogger->init(filename); + logger.reset(fileLogger); + } else { + throw std::runtime_error("Unknown logger type: " + type); + } + + if (loggerConfiguration.count("pattern")) { + logger->setPattern(loggerConfiguration("pattern").getString()); + } + + std::vector disabledCategories; + if (loggerConfiguration.count("disabledCategories")) { + auto disabledCategoriesVal = loggerConfiguration("disabledCategories"); + size_t countOfCategories = disabledCategoriesVal.size(); + for (size_t i = 0; i < countOfCategories; ++i) { + auto categoryVal = disabledCategoriesVal[i]; + if (categoryVal.isString()) { + logger->disableCategory(categoryVal.getString()); + } + } + } + + loggers.emplace_back(std::move(logger)); + addLogger(*loggers.back()); + } + } else { + throw std::runtime_error("loggers parameter has wrong type"); + } + } else { + throw std::runtime_error("loggers parameter missing"); + } + setMaxLevel(globalLevel); + for (const auto& category : globalDisabledCategories) { + disableCategory(category); + } +} + +} diff --git a/src/Logging/LoggerManager.h b/src/Logging/LoggerManager.h new file mode 100755 index 0000000000..1a874fab9b --- /dev/null +++ b/src/Logging/LoggerManager.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include "../Common/JsonValue.h" +#include "LoggerGroup.h" + +namespace Logging { + +class LoggerManager : public LoggerGroup { +public: + LoggerManager(); + void configure(const Common::JsonValue& val); + virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; + +private: + std::vector> loggers; + std::mutex reconfigureLock; +}; + +} diff --git a/src/logger/LoggerMessage.cpp b/src/Logging/LoggerMessage.cpp similarity index 71% rename from src/logger/LoggerMessage.cpp rename to src/Logging/LoggerMessage.cpp index 5b62d2f1f6..be171cf4db 100755 --- a/src/logger/LoggerMessage.cpp +++ b/src/Logging/LoggerMessage.cpp @@ -1,94 +1,113 @@ -#include "LoggerMessage.h" - -using namespace Log; - -LoggerMessage::LoggerMessage(ILogger& logger, const std::string& category, ILogger::Level level, const std::string& color) - : std::ostream(this) - , std::streambuf() - , logger(logger) - , category(category) - , logLevel(level) - , message(color) - , timestamp(boost::posix_time::microsec_clock::local_time()) - , gotText(false) { -} - -LoggerMessage::~LoggerMessage() { - if (gotText) { - (*this) << std::endl; - } -} - -#ifndef __linux__ -LoggerMessage::LoggerMessage(LoggerMessage&& other) - : std::ostream(std::move(other)) - , std::streambuf(std::move(other)) - , category(other.category) - , logLevel(other.logLevel) - , logger(other.logger) - , message(other.message) - , timestamp(boost::posix_time::microsec_clock::local_time()) - , gotText(false) { - this->set_rdbuf(this); -} -#else -LoggerMessage::LoggerMessage(LoggerMessage&& other) - : std::ostream(nullptr) - , std::streambuf() - , category(other.category) - , logLevel(other.logLevel) - , logger(other.logger) - , message(other.message) - , timestamp(boost::posix_time::microsec_clock::local_time()) - , gotText(false) { - if (this != &other) { - _M_tie = nullptr; - _M_streambuf = nullptr; - - //ios_base swap - std::swap(_M_streambuf_state, other._M_streambuf_state); - std::swap(_M_exception, other._M_exception); - std::swap(_M_flags, other._M_flags); - std::swap(_M_precision, other._M_precision); - std::swap(_M_width, other._M_width); - - std::swap(_M_callbacks, other._M_callbacks); - std::swap(_M_ios_locale, other._M_ios_locale); - //ios_base swap - - //streambuf swap - char *_Pfirst = pbase(); - char *_Pnext = pptr(); - char *_Pend = epptr(); - char *_Gfirst = eback(); - char *_Gnext = gptr(); - char *_Gend = egptr(); - - setp(other.pbase(), other.epptr()); - other.setp(_Pfirst, _Pend); - - setg(other.eback(), other.gptr(), other.egptr()); - other.setg(_Gfirst, _Gnext, _Gend); - - std::swap(_M_buf_locale, other._M_buf_locale); - //streambuf swap - - std::swap(_M_fill, other._M_fill); - std::swap(_M_tie, other._M_tie); - } - _M_streambuf = this; -} -#endif - -int LoggerMessage::sync() { - logger(category, logLevel, timestamp, message); - gotText = false; - message = ILogger::DEFAULT; - return 0; -} - -int LoggerMessage::overflow(int c) { - gotText = true; - message += static_cast(c); - return 0; -} +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LoggerMessage.h" + +namespace Logging { + +LoggerMessage::LoggerMessage(ILogger& logger, const std::string& category, Level level, const std::string& color) + : std::ostream(this) + , std::streambuf() + , logger(logger) + , category(category) + , logLevel(level) + , message(color) + , timestamp(boost::posix_time::microsec_clock::local_time()) + , gotText(false) { +} + +LoggerMessage::~LoggerMessage() { + if (gotText) { + (*this) << std::endl; + } +} + +#ifndef __linux__ +LoggerMessage::LoggerMessage(LoggerMessage&& other) + : std::ostream(std::move(other)) + , std::streambuf(std::move(other)) + , category(other.category) + , logLevel(other.logLevel) + , logger(other.logger) + , message(other.message) + , timestamp(boost::posix_time::microsec_clock::local_time()) + , gotText(false) { + this->set_rdbuf(this); +} +#else +LoggerMessage::LoggerMessage(LoggerMessage&& other) + : std::ostream(nullptr) + , std::streambuf() + , category(other.category) + , logLevel(other.logLevel) + , logger(other.logger) + , message(other.message) + , timestamp(boost::posix_time::microsec_clock::local_time()) + , gotText(false) { + if (this != &other) { + _M_tie = nullptr; + _M_streambuf = nullptr; + + //ios_base swap + std::swap(_M_streambuf_state, other._M_streambuf_state); + std::swap(_M_exception, other._M_exception); + std::swap(_M_flags, other._M_flags); + std::swap(_M_precision, other._M_precision); + std::swap(_M_width, other._M_width); + + std::swap(_M_callbacks, other._M_callbacks); + std::swap(_M_ios_locale, other._M_ios_locale); + //ios_base swap + + //streambuf swap + char *_Pfirst = pbase(); + char *_Pnext = pptr(); + char *_Pend = epptr(); + char *_Gfirst = eback(); + char *_Gnext = gptr(); + char *_Gend = egptr(); + + setp(other.pbase(), other.epptr()); + other.setp(_Pfirst, _Pend); + + setg(other.eback(), other.gptr(), other.egptr()); + other.setg(_Gfirst, _Gnext, _Gend); + + std::swap(_M_buf_locale, other._M_buf_locale); + //streambuf swap + + std::swap(_M_fill, other._M_fill); + std::swap(_M_tie, other._M_tie); + } + _M_streambuf = this; +} +#endif + +int LoggerMessage::sync() { + logger(category, logLevel, timestamp, message); + gotText = false; + message = DEFAULT; + return 0; +} + +int LoggerMessage::overflow(int c) { + gotText = true; + message += static_cast(c); + return 0; +} + +} diff --git a/src/Logging/LoggerMessage.h b/src/Logging/LoggerMessage.h new file mode 100755 index 0000000000..a2d617b843 --- /dev/null +++ b/src/Logging/LoggerMessage.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "ILogger.h" + +namespace Logging { + +class LoggerMessage : public std::ostream, std::streambuf { +public: + LoggerMessage(ILogger& logger, const std::string& category, Level level, const std::string& color); + ~LoggerMessage(); + LoggerMessage(const LoggerMessage&) = delete; + LoggerMessage& operator=(const LoggerMessage&) = delete; + LoggerMessage(LoggerMessage&& other); + +private: + int sync() override; + int overflow(int c) override; + + std::string message; + const std::string category; + Level logLevel; + ILogger& logger; + boost::posix_time::ptime timestamp; + bool gotText; +}; + +} diff --git a/src/Logging/LoggerRef.cpp b/src/Logging/LoggerRef.cpp new file mode 100755 index 0000000000..39768d5014 --- /dev/null +++ b/src/Logging/LoggerRef.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LoggerRef.h" + +namespace Logging { + +LoggerRef::LoggerRef(ILogger& logger, const std::string& category) : logger(&logger), category(category) { +} + +LoggerMessage LoggerRef::operator()(Level level, const std::string& color) const { + return LoggerMessage(*logger, category, level, color); +} + +ILogger& LoggerRef::getLogger() const { + return *logger; +} + +} diff --git a/src/Logging/LoggerRef.h b/src/Logging/LoggerRef.h new file mode 100755 index 0000000000..8afc1b2570 --- /dev/null +++ b/src/Logging/LoggerRef.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ILogger.h" +#include "LoggerMessage.h" + +namespace Logging { + +class LoggerRef { +public: + LoggerRef(ILogger& logger, const std::string& category); + LoggerMessage operator()(Level level = INFO, const std::string& color = DEFAULT) const; + ILogger& getLogger() const; + +private: + ILogger* logger; + std::string category; +}; + +} diff --git a/src/Logging/StreamLogger.cpp b/src/Logging/StreamLogger.cpp new file mode 100755 index 0000000000..0ea0dfdf3b --- /dev/null +++ b/src/Logging/StreamLogger.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StreamLogger.h" +#include +#include + +namespace Logging { + +StreamLogger::StreamLogger(Level level) : CommonLogger(level), stream(nullptr) { +} + +StreamLogger::StreamLogger(std::ostream& stream, Level level) : CommonLogger(level), stream(&stream) { +} + +void StreamLogger::attachToStream(std::ostream& stream) { + this->stream = &stream; +} + +void StreamLogger::doLogString(const std::string& message) { + if (stream != nullptr && stream->good()) { + std::lock_guard lock(mutex); + bool readingText = true; + for (size_t charPos = 0; charPos < message.size(); ++charPos) { + if (message[charPos] == ILogger::COLOR_DELIMETER) { + readingText = !readingText; + } else if (readingText) { + *stream << message[charPos]; + } + } + + *stream << std::flush; + } +} + +} diff --git a/src/Logging/StreamLogger.h b/src/Logging/StreamLogger.h new file mode 100755 index 0000000000..4cdc2cdd3e --- /dev/null +++ b/src/Logging/StreamLogger.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "CommonLogger.h" + +namespace Logging { + +class StreamLogger : public CommonLogger { +public: + StreamLogger(Level level = DEBUGGING); + StreamLogger(std::ostream& stream, Level level = DEBUGGING); + void attachToStream(std::ostream& stream); + +protected: + virtual void doLogString(const std::string& message) override; + +protected: + std::ostream* stream; + +private: + std::mutex mutex; +}; + +} diff --git a/src/Platform/Linux/System/Dispatcher.cpp b/src/Platform/Linux/System/Dispatcher.cpp index 753d612910..e130bfb8b0 100755 --- a/src/Platform/Linux/System/Dispatcher.cpp +++ b/src/Platform/Linux/System/Dispatcher.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,36 +16,56 @@ // along with Bytecoin. If not, see . #include "Dispatcher.h" -#include +#include + +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include - -using namespace System; +#include -void Dispatcher::contextProcedureStatic(void *context) { - reinterpret_cast(context)->contextProcedure(); -} +namespace System { Dispatcher::Dispatcher() { + std::string message; epoll = ::epoll_create1(0); if (epoll == -1) { - std::cerr << "kqueue() fail errno=" << errno << std::endl; + message = "epoll_create1() fail errno=" + std::to_string(errno); } else { currentContext = new ucontext_t; if (getcontext(reinterpret_cast(currentContext)) == -1) { - std::cerr << "getcontext() fail errno=" << errno << std::endl; + message = "getcontext() fail errno=" + std::to_string(errno); } else { - contextCount = 0; - return; + remoteSpawnEvent = eventfd(0, O_NONBLOCK); + if(remoteSpawnEvent == -1) { + message = "eventfd() fail errno=" + std::to_string(errno); + } else { + eventContext.writeContext = nullptr; + eventContext.readContext = nullptr; + + epoll_event remoteSpawnEventEpollEvent; + remoteSpawnEventEpollEvent.events = EPOLLIN; + remoteSpawnEventEpollEvent.data.ptr = &eventContext; + + if (epoll_ctl(epoll, EPOLL_CTL_ADD, remoteSpawnEvent, &remoteSpawnEventEpollEvent) == -1) { + message = "epoll_ctl() failed, errno=" + std::to_string(errno); + } else { + contextCount = 0; + *reinterpret_cast(this->mutex) = pthread_mutex_t(PTHREAD_MUTEX_INITIALIZER); + return; + } + + auto result = close(remoteSpawnEvent); + assert(result == 0); + } } + + auto result = close(epoll); + assert(result == 0); } - throw std::runtime_error("Dispatcher::Dispatcher"); + + throw std::runtime_error("Dispatcher::Dispatcher, "+message); } Dispatcher::~Dispatcher() { @@ -61,33 +81,121 @@ Dispatcher::~Dispatcher() { } while (!timers.empty()) { + int result = ::close(timers.top()); + assert(result == 0); timers.pop(); } - if (-1 == close(epoll)) { - std::cerr << "close() fail errno=" << errno << std::endl; + auto result = close(epoll); + assert(result == 0); + result = close(remoteSpawnEvent); + assert(result == 0); + result = pthread_mutex_destroy(reinterpret_cast(this->mutex)); + assert(result == 0); +} + +void Dispatcher::clear() { + while (!reusableContexts.empty()) { + delete[] allocatedStacks.top(); + allocatedStacks.pop(); + delete static_cast(reusableContexts.top()); + reusableContexts.pop(); + --contextCount; + } + + while (!timers.empty()) { + int result = ::close(timers.top()); + if (result == -1) { + throw std::runtime_error("Dispatcher::clear, close failed, errno=" + std::to_string(errno)); + } + + timers.pop(); } } -void* Dispatcher::getCurrentContext() const { - return currentContext; +void Dispatcher::dispatch() { + void* context; + for (;;) { + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } + + epoll_event event; + int count = epoll_wait(epoll, &event, 1, -1); + if (count == 1) { + ContextPair *contextPair = static_cast(event.data.ptr); + if(((event.events & (EPOLLIN | EPOLLOUT)) != 0) && contextPair->readContext == nullptr && contextPair->writeContext == nullptr) { + uint64_t buf; + auto transferred = read(remoteSpawnEvent, &buf, sizeof buf); + if(transferred == -1) { + throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno)); + } + + pthread_mutex_lock(reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + pthread_mutex_unlock(reinterpret_cast(this->mutex)); + continue; + } + + if ((event.events & EPOLLOUT) != 0) { + context = contextPair->writeContext->context; + contextPair->writeContext->events = event.events; + } else if ((event.events & EPOLLIN) != 0) { + context = contextPair->readContext->context; + contextPair->readContext->events = event.events; + } else { + continue; + } + + assert(context != nullptr); + break; + } + + if (errno != EINTR) { + throw std::runtime_error("Dispatcher::dispatch(), epoll_wait() failed, errno=" + std::to_string(errno)); + } + } + + if (context != currentContext) { + ucontext_t* oldContext = static_cast(currentContext); + currentContext = context; + if (swapcontext(oldContext, static_cast(context)) == -1) { + throw std::runtime_error("Dispatcher::dispatch() swapcontext() failed, errno=" + std::to_string(errno)); + } + } } -int Dispatcher::getEpoll() const { - return epoll; +void* Dispatcher::getCurrentContext() const { + return currentContext; } void Dispatcher::pushContext(void* context) { resumingContexts.push(context); } +void Dispatcher::remoteSpawn(std::function&& procedure) { + pthread_mutex_lock(reinterpret_cast(this->mutex)); + remoteSpawningProcedures.push(std::move(procedure)); + pthread_mutex_unlock(reinterpret_cast(this->mutex)); + uint64_t one = 1; + auto transferred = write(remoteSpawnEvent, &one, sizeof one); + if(transferred == - 1) { + throw std::runtime_error("Dispatcher::remoteSpawn, write() failed errno = " + std::to_string(errno)); + } +} + void Dispatcher::spawn(std::function&& procedure) { ucontext_t *context; if (reusableContexts.empty()) { context = new ucontext_t; if (getcontext(context) == -1) { //makecontext precondition - std::cerr << "getcontext() fail errno=" << errno << std::endl; - throw std::runtime_error("Dispatcher::spawn()"); + throw std::runtime_error("Dispatcher::spawn(), getcontext() fail errno=" + std::to_string(errno)); } auto stackPointer = new uint8_t[64 * 1024]; context->uc_stack.ss_sp = stackPointer; @@ -104,47 +212,82 @@ void Dispatcher::spawn(std::function&& procedure) { spawningProcedures.emplace(std::move(procedure)); } -void Dispatcher::clear() { -//TODO -} - void Dispatcher::yield() { - void* context; - for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); - assert(context); + for(;;){ + epoll_event events[16]; + int count = epoll_wait(epoll, events, 16, 0); + if (count == 0) { break; } - epoll_event event; - int count = epoll_wait(epoll, &event, 1, -1); + if(count > 0) { + for(int i = 0; i < count; ++i) { + ContextPair *contextPair = static_cast(events[i].data.ptr); + if(((events[i].events & (EPOLLIN | EPOLLOUT)) != 0) && contextPair->readContext == nullptr && contextPair->writeContext == nullptr) { + uint64_t buf; + auto transferred = read(remoteSpawnEvent, &buf, sizeof buf); + if(transferred == -1) { + throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno)); + } - if (count == 1) { - if ((event.events & EPOLLOUT) != 0) { - context = static_cast(event.data.ptr)->writeContext; - } else { - context = static_cast(event.data.ptr)->context; + pthread_mutex_lock(reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + pthread_mutex_unlock(reinterpret_cast(this->mutex)); + continue; + } + + if ((events[i].events & EPOLLOUT) != 0) { + resumingContexts.push(contextPair->writeContext->context); + contextPair->writeContext->events = events[i].events; + } else if ((events[i].events & EPOLLIN) != 0) { + resumingContexts.push(contextPair->readContext->context); + contextPair->readContext->events = events[i].events; + } else { + continue; + } + } + } else { + if (errno != EINTR) { + throw std::runtime_error("Dispatcher::dispatch(), epoll_wait() failed, errno=" + std::to_string(errno)); } - assert(context); - break; } + } - if (errno != EINTR) { - std::cerr << "epoll_wait() failed, errno=" << errno << std::endl; - throw std::runtime_error("Dispatcher::yield()"); - } + if(!resumingContexts.empty()){ + resumingContexts.push(getCurrentContext()); + dispatch(); } +} - if (context != currentContext) { - ucontext_t* oldContext = static_cast(currentContext); - currentContext = context; - if (-1 == swapcontext(oldContext, static_cast(context))) { - std::cerr << "swapcontext() failed, errno=" << errno << std::endl; - throw std::runtime_error("Dispatcher::yield()"); +int Dispatcher::getEpoll() const { + return epoll; +} + +int Dispatcher::getTimer() { + int timer; + if (timers.empty()) { + timer = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); + epoll_event timerEvent; + timerEvent.events = 0; + timerEvent.data.ptr = nullptr; + + if (epoll_ctl(getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) { + throw std::runtime_error("Dispatcher::getTimer(), epoll_ctl() failed, errno=" + std::to_string(errno)); } + } else { + timer = timers.top(); + timers.pop(); } + + return timer; +} + +void Dispatcher::pushTimer(int timer) { + timers.push(timer); } void Dispatcher::contextProcedure() { @@ -155,6 +298,12 @@ void Dispatcher::contextProcedure() { spawningProcedures.pop(); procedure(); reusableContexts.push(context); - yield(); + dispatch(); } } + +void Dispatcher::contextProcedureStatic(void *context) { + reinterpret_cast(context)->contextProcedure(); +} + +} diff --git a/src/Platform/Linux/System/Dispatcher.h b/src/Platform/Linux/System/Dispatcher.h index c62a0ff494..31e415f98b 100755 --- a/src/Platform/Linux/System/Dispatcher.h +++ b/src/Platform/Linux/System/Dispatcher.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -29,34 +29,54 @@ class Dispatcher { Dispatcher(const Dispatcher&) = delete; ~Dispatcher(); Dispatcher& operator=(const Dispatcher&) = delete; + void clear(); + void dispatch(); + void* getCurrentContext() const; + void pushContext(void* context); + void remoteSpawn(std::function&& procedure); void spawn(std::function&& procedure); void yield(); - void clear(); - struct ContextExt { + struct OperationContext { void *context; - void *writeContext; //required workaround + bool interrupted; + uint32_t events; + }; + + struct ContextPair { + OperationContext *readContext; + OperationContext *writeContext; }; + + // system-dependent + int getEpoll() const; + int getTimer(); + void pushTimer(int timer); + +#ifdef __x86_64__ +# if __WORDSIZE == 64 + static const int SIZEOF_PTHREAD_MUTEX_T = 40; +# else + static const int SIZEOF_PTHREAD_MUTEX_T = 32 +# endif +#else + static const int SIZEOF_PTHREAD_MUTEX_T = 24 +#endif + private: - friend class Event; - friend class DispatcherAccessor; - friend class TcpConnection; - friend class TcpConnector; - friend class TcpListener; - friend class Timer; - int epoll; - void* currentContext; + std::stack allocatedStacks; std::size_t contextCount; + void* currentContext; + int epoll; + ContextPair eventContext; + uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + int remoteSpawnEvent; + std::queue> remoteSpawningProcedures; std::queue resumingContexts; std::stack reusableContexts; - std::stack allocatedStacks; std::queue> spawningProcedures; std::stack timers; - int getEpoll() const; - void pushContext(void* context); - void* getCurrentContext() const; - void contextProcedure(); static void contextProcedureStatic(void* context); }; diff --git a/src/Platform/Linux/System/Ipv4Resolver.cpp b/src/Platform/Linux/System/Ipv4Resolver.cpp new file mode 100755 index 0000000000..d78195e45c --- /dev/null +++ b/src/Platform/Linux/System/Ipv4Resolver.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Ipv4Resolver.h" +#include +#include +#include + +#include + +#include +#include + +namespace System { + +Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { +} + +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +} + +Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + stopped = other.stopped; + other.dispatcher = nullptr; + } +} + +Ipv4Resolver::~Ipv4Resolver() { +} + +Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + stopped = other.stopped; + other.dispatcher = nullptr; + } + + return *this; +} + +void Ipv4Resolver::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void Ipv4Resolver::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + stopped = true; +} + +Ipv4Address Ipv4Resolver::resolve(const std::string& host) { + assert(dispatcher != nullptr); + if (stopped) { + throw InterruptedException(); + } + + addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; + addrinfo* addressInfos; + int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos); + if (result != 0) { + throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result)); + } + + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::mt19937 generator{ std::random_device()() }; + std::size_t index = std::uniform_int_distribution(0, count - 1)(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + Ipv4Address address(ntohl(reinterpret_cast(addressInfo->ai_addr)->sin_addr.s_addr)); + freeaddrinfo(addressInfo); + return address; +} + +} diff --git a/src/Platform/Linux/System/Ipv4Resolver.h b/src/Platform/Linux/System/Ipv4Resolver.h new file mode 100755 index 0000000000..d59d50e0f2 --- /dev/null +++ b/src/Platform/Linux/System/Ipv4Resolver.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; + +class Ipv4Resolver { +public: + Ipv4Resolver(); + explicit Ipv4Resolver(Dispatcher& dispatcher); + Ipv4Resolver(const Ipv4Resolver&) = delete; + Ipv4Resolver(Ipv4Resolver&& other); + ~Ipv4Resolver(); + Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; + Ipv4Resolver& operator=(Ipv4Resolver&& other); + void start(); + void stop(); + Ipv4Address resolve(const std::string& host); + +private: + Dispatcher* dispatcher; + bool stopped; +}; + +} diff --git a/src/Platform/Linux/System/TcpConnection.cpp b/src/Platform/Linux/System/TcpConnection.cpp index 7ef7cb038e..ecb1ab51f4 100755 --- a/src/Platform/Linux/System/TcpConnection.cpp +++ b/src/Platform/Linux/System/TcpConnection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,73 +16,56 @@ // along with Bytecoin. If not, see . #include "TcpConnection.h" -#include -#include -#include -#include -#include -#include -#include -#include "Dispatcher.h" -#include "InterruptedException.h" +#include -using namespace System; +#include +#include +#include -namespace { +#include +#include -struct ConnectionContext : public Dispatcher::ContextExt { - bool interrupted; -}; - -} +namespace System { TcpConnection::TcpConnection() : dispatcher(nullptr) { } -TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), context(nullptr) { - epoll_event connectionEvent; - connectionEvent.data.fd = connection; - connectionEvent.events = 0; - connectionEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) { - std::cerr << errno << std::endl; - throw std::runtime_error("epoll_ctl() fail"); - } -} - TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { + assert(other.contextPair.writeContext == nullptr); + assert(other.contextPair.readContext == nullptr); connection = other.connection; stopped = other.stopped; - context = other.context; + contextPair = other.contextPair; other.dispatcher = nullptr; } } TcpConnection::~TcpConnection() { if (dispatcher != nullptr) { - assert(context == nullptr); - if (close(connection) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - } + assert(contextPair.readContext == nullptr); + assert(contextPair.writeContext == nullptr); + int result = close(connection); + assert(result != -1); } } TcpConnection& TcpConnection::operator=(TcpConnection&& other) { if (dispatcher != nullptr) { - assert(context == nullptr); + assert(contextPair.readContext == nullptr); + assert(contextPair.writeContext == nullptr); if (close(connection) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpConnection::operator="); + throw std::runtime_error("TcpConnection::operator=, close() failed, errno=" + std::to_string(errno)); } } dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { + assert(other.contextPair.readContext == nullptr); + assert(other.contextPair.writeContext == nullptr); connection = other.connection; stopped = other.stopped; - context = other.context; + contextPair = other.contextPair; other.dispatcher = nullptr; } @@ -98,200 +81,201 @@ void TcpConnection::start() { void TcpConnection::stop() { assert(dispatcher != nullptr); assert(!stopped); - if (context != nullptr) { - ConnectionContext *context2 = static_cast(context); - if (!context2->interrupted) { - - epoll_event connectionEvent; - connectionEvent.data.fd = connection; - connectionEvent.events = 0; - connectionEvent.data.ptr = nullptr; - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - std::cerr << errno << std::endl; - throw std::runtime_error("epoll_ctl() fail"); - } - - context2->interrupted = true; + epoll_event connectionEvent; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; - if (context2->context != nullptr) { - dispatcher->pushContext(context2->context); - } + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail" + std::to_string(errno)); + } - if (context2->writeContext != nullptr) { - dispatcher->pushContext(context2->writeContext); - } - } + if(contextPair.readContext != nullptr) { + contextPair.readContext->interrupted = true; + dispatcher->pushContext(contextPair.readContext->context); } + if(contextPair.writeContext != nullptr) { + contextPair.writeContext->interrupted = true; + dispatcher->pushContext(contextPair.writeContext->context); + } + stopped = true; } size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); - assert(context == nullptr || static_cast(context)->context == nullptr); + assert(contextPair.readContext == nullptr); if (stopped) { throw InterruptedException(); } + std::string message; ssize_t transferred = ::recv(connection, (void *)data, size, 0); if (transferred == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "recv failed, result=" << errno << '.' << std::endl; + message = "recv failed, errno=" + std::to_string(errno); } else { epoll_event connectionEvent; - connectionEvent.data.fd = connection; - - ConnectionContext context2; - if (context == nullptr) { - context2.writeContext = nullptr; - context2.interrupted = false; - context2.context = dispatcher->getCurrentContext(); - context = &context2; - connectionEvent.events = EPOLLIN | EPOLLONESHOT; - } else { - assert(static_cast(context)->writeContext != nullptr); + Dispatcher::OperationContext operationContext; + operationContext.interrupted = false; + operationContext.context = dispatcher->getCurrentContext(); + contextPair.readContext = &operationContext; + connectionEvent.data.ptr = &contextPair; + + if(contextPair.writeContext != nullptr) { connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; + } else { + connectionEvent.events = EPOLLIN | EPOLLONESHOT; } - connectionEvent.data.ptr = context; if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { - dispatcher->yield(); + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - if (static_cast(context)->interrupted) { - context = nullptr; + assert(operationContext.context == dispatcher->getCurrentContext()); + assert(contextPair.readContext == &operationContext); + + if (operationContext.interrupted) { + contextPair.readContext = nullptr; throw InterruptedException(); } - assert(static_cast(context)->context == context2.context); - if (static_cast(context)->writeContext != nullptr) { //write is presented, rearm - static_cast(context)->context = nullptr; - + contextPair.readContext = nullptr; + if(contextPair.writeContext != nullptr) { //write is presented, rearm epoll_event connectionEvent; - connectionEvent.data.fd = connection; connectionEvent.events = EPOLLOUT | EPOLLONESHOT; - connectionEvent.data.ptr = context; + connectionEvent.data.ptr = &contextPair; if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + message = "epoll_ctl() failed, errno=" + std::to_string(errno); throw std::runtime_error("TcpConnection::read"); } - } else { - context = nullptr; + } + + if((operationContext.events & (EPOLLERR | EPOLLHUP)) != 0) { + throw std::runtime_error("TcpConnection::read"); } ssize_t transferred = ::recv(connection, (void *)data, size, 0); if (transferred == -1) { - std::cerr << "recv failed, errno=" << errno << '.' << std::endl; + message = "recv failed, errno=" + std::to_string(errno); } else { - if (transferred == 0) { - std::cerr << "recv return after yield with 0 bytes" << std::endl; - - int retval = -1; - socklen_t retValLen = sizeof(retval); - int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); - if (s == -1) { - std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; - } else { - std::cerr << "recv getsockopt retval = " << retval << std::endl; - } - } - - assert(transferred <= size); + assert(transferred <= static_cast(size)); return transferred; } } } - throw std::runtime_error("TcpConnection::read"); + throw std::runtime_error("TcpConnection::read, "+ message); } - assert(transferred <= size); + assert(transferred <= static_cast(size)); return transferred; } -void TcpConnection::write(const uint8_t* data, size_t size) { +std::size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); - assert(context == nullptr || static_cast(context)->writeContext == nullptr); + assert(contextPair.writeContext == nullptr); if (stopped) { throw InterruptedException(); } - if (size == 0) { - if (shutdown(connection, SHUT_WR) == -1) { - std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpConnection::write"); + std::string message; + if(size == 0) { + if(shutdown(connection, SHUT_WR) == -1) { + throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(errno)); } - return; + return 0; } - ssize_t transferred = ::send(connection, (void *)data, size, 0); + ssize_t transferred = ::send(connection, (void *)data, size, MSG_NOSIGNAL); if (transferred == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "send failed, result=" << errno << '.' << std::endl; + message = "send failed, result=" + std::to_string(errno); } else { epoll_event connectionEvent; - connectionEvent.data.fd = connection; - - ConnectionContext context2; - if (context == nullptr) { - context2.context = nullptr; - context2.interrupted = false; - context2.writeContext = dispatcher->getCurrentContext(); - context = &context2; - connectionEvent.events = EPOLLOUT | EPOLLONESHOT; - } else { - assert(static_cast(context)->context != nullptr); + Dispatcher::OperationContext operationContext; + operationContext.interrupted = false; + operationContext.context = dispatcher->getCurrentContext(); + contextPair.writeContext = &operationContext; + connectionEvent.data.ptr = &contextPair; + + if(contextPair.readContext != nullptr) { connectionEvent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; + } else { + connectionEvent.events = EPOLLOUT | EPOLLONESHOT; } - connectionEvent.data.ptr = context; if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { - dispatcher->yield(); + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.writeContext == dispatcher->getCurrentContext()); - if (static_cast(context)->interrupted) { - context = nullptr; + assert(operationContext.context == dispatcher->getCurrentContext()); + assert(contextPair.writeContext == &operationContext); + + if (operationContext.interrupted) { + contextPair.writeContext = nullptr; throw InterruptedException(); } - assert(static_cast(context)->writeContext == context2.writeContext); - if (static_cast(context)->context != nullptr) { //read is presented, rearm - static_cast(context)->writeContext = nullptr; - + contextPair.writeContext = nullptr; + if(contextPair.readContext != nullptr) { //read is presented, rearm epoll_event connectionEvent; - connectionEvent.data.fd = connection; connectionEvent.events = EPOLLIN | EPOLLONESHOT; - connectionEvent.data.ptr = context; + connectionEvent.data.ptr = &contextPair; if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + message = "epoll_ctl() failed, errno=" + std::to_string(errno); throw std::runtime_error("TcpConnection::write"); } - } else { - context = nullptr; + } + + if((operationContext.events & (EPOLLERR | EPOLLHUP)) != 0) { + throw std::runtime_error("TcpConnection::write"); } ssize_t transferred = ::send(connection, (void *)data, size, 0); if (transferred == -1) { - std::cerr << "send failed, errno=" << errno << '.' << std::endl; + message = "send failed, errno=" + std::to_string(errno); } else { - if (transferred == 0) { - throw std::runtime_error("send transferred 0 bytes."); - } - - assert(transferred == size); - return; + assert(transferred <= static_cast(size)); + return transferred; } } } - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, "+message); } + + assert(transferred <= static_cast(size)); + return transferred; +} + +std::pair TcpConnection::getPeerAddressAndPort() { + sockaddr_in addr; + socklen_t size = sizeof(addr); + if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { + throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(errno)); + } + + assert(size == sizeof(sockaddr_in)); + return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); +} + +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false) { + contextPair.readContext = nullptr; + contextPair.writeContext = nullptr; + epoll_event connectionEvent; + connectionEvent.events = EPOLLONESHOT; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) { + throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl() fail" + std::to_string(errno)); + } +} + } diff --git a/src/Platform/Linux/System/TcpConnection.h b/src/Platform/Linux/System/TcpConnection.h index 9c4993fd26..034d6cccdf 100755 --- a/src/Platform/Linux/System/TcpConnection.h +++ b/src/Platform/Linux/System/TcpConnection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,11 +19,12 @@ #include #include -#include +#include +#include "Dispatcher.h" namespace System { -class Dispatcher; +class Ipv4Address; class TcpConnection { public: @@ -36,18 +37,19 @@ class TcpConnection { void start(); void stop(); std::size_t read(uint8_t* data, std::size_t size); - void write(const uint8_t* data, std::size_t size); + std::size_t write(const uint8_t* data, std::size_t size); + std::pair getPeerAddressAndPort(); private: friend class TcpConnector; friend class TcpListener; - - explicit TcpConnection(Dispatcher& dispatcher, int socket); - + Dispatcher* dispatcher; int connection; bool stopped; - void* context; + Dispatcher::ContextPair contextPair; + + TcpConnection(Dispatcher& dispatcher, int socket); }; } diff --git a/src/Platform/Linux/System/TcpConnector.cpp b/src/Platform/Linux/System/TcpConnector.cpp index 85542efc1d..22a6ddbdd4 100755 --- a/src/Platform/Linux/System/TcpConnector.cpp +++ b/src/Platform/Linux/System/TcpConnector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,31 +16,25 @@ // along with Bytecoin. If not, see . #include "TcpConnector.h" - -#include -#include -#include -#include #include +#include -#include #include +#include #include -#include #include -#include +#include +#include #include "Dispatcher.h" #include "TcpConnection.h" -#include "InterruptedException.h" -using namespace System; +namespace System { namespace { -struct ConnectorContext : public Dispatcher::ContextExt { +struct TcpConnectorContextExt : public Dispatcher::OperationContext { int connection; - bool interrupted; }; } @@ -48,15 +42,14 @@ struct ConnectorContext : public Dispatcher::ContextExt { TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), stopped(false) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { - address = other.address; - port = other.port; + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } @@ -67,10 +60,9 @@ TcpConnector::~TcpConnector() { TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { - address = other.address; - port = other.port; + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -83,131 +75,119 @@ void TcpConnector::start() { stopped = false; } -TcpConnection TcpConnector::connect() { +void TcpConnector::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + TcpConnectorContextExt* connectorContext = static_cast(context); + if (!connectorContext->interrupted) { + if (close(connectorContext->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); + } + + connectorContext->interrupted = true; + dispatcher->pushContext(connectorContext->context); + } + } + + stopped = true; +} + +TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); if (stopped) { throw InterruptedException(); } - std::ostringstream portStream; - portStream << port; - addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; - addrinfo *addressInfos; - int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); - if (result == -1) { - std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl; + std::string message; + int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == -1) { + message = "socket() failed, errno=" + std::to_string(errno); } else { - std::size_t count = 0; - for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { - ++count; - } - - std::random_device randomDevice; - std::mt19937 generator(randomDevice()); - std::uniform_int_distribution distribution(0, count - 1); - std::size_t index = distribution(generator); - addrinfo* addressInfo = addressInfos; - for (std::size_t i = 0; i < index; ++i) { - addressInfo = addressInfo->ai_next; - } - - sockaddr_in addressData = *reinterpret_cast(addressInfo->ai_addr); - freeaddrinfo(addressInfo); - int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (connection == -1) { - std::cerr << "socket failed, errno=" << errno << '.' << std::endl; + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + message = "bind failed, errno=" + std::to_string(errno); } else { - sockaddr_in bindAddress; - bindAddress.sin_family = AF_INET; - bindAddress.sin_port = 0; - bindAddress.sin_addr.s_addr = INADDR_ANY; - if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { - std::cerr << "bind failed, errno=" << errno << '.' << std::endl; + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + message = "fcntl() failed errno=" + std::to_string(errno); } else { - int flags = fcntl(connection, F_GETFL, 0); - if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { - std::cerr << "fcntl() failed errno=" << errno << std::endl; - } else { - int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); - if (result == -1) { - if (errno == EINPROGRESS) { - ConnectorContext context2; - context2.writeContext = dispatcher->getCurrentContext(); - context2.context = nullptr; - context2.interrupted = false; - context2.connection = connection; - context = &context2; - - epoll_event connectEvent; - connectEvent.data.fd = connection; - connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT; - connectEvent.data.ptr = context; - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + sockaddr_in addressData; + addressData.sin_family = AF_INET; + addressData.sin_port = htons(port); + addressData.sin_addr.s_addr = htonl(address.getValue()); + int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); + if (result == -1) { + if (errno == EINPROGRESS) { + + Dispatcher::ContextPair contextPair; + TcpConnectorContextExt connectorContext; + connectorContext.interrupted = false; + connectorContext.context = dispatcher->getCurrentContext(); + connectorContext.connection = connection; + + contextPair.readContext = nullptr; + contextPair.writeContext = &connectorContext; + + epoll_event connectEvent; + connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT; + connectEvent.data.ptr = &contextPair; + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) { + message = "epoll_ctl() failed, errno=" + std::to_string(errno); + } else { + context = &connectorContext; + dispatcher->dispatch(); + assert(dispatcher != nullptr); + assert(connectorContext.context == dispatcher->getCurrentContext()); + assert(contextPair.readContext == nullptr); + assert(context == &connectorContext); + context = nullptr; + connectorContext.context = nullptr; + if (connectorContext.interrupted) { + throw InterruptedException(); + } + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) { + message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { - dispatcher->yield(); - assert(dispatcher != nullptr); - assert(context2.writeContext == dispatcher->getCurrentContext()); - assert(context == &context2); - context = nullptr; - context2.writeContext = nullptr; - if (context2.interrupted) { - if (close(connection) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; - } + if((connectorContext.events & (EPOLLERR | EPOLLHUP)) != 0) { + int result = close(connection); + assert(result != -1); - throw InterruptedException(); + throw std::runtime_error("TcpConnector::connect, connection failed"); } - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + message = "getsockopt() failed, errno=" + std::to_string(errno); } else { - int retval = -1; - socklen_t retValLen = sizeof(retval); - int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); - if (s == -1) { - std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; + if (retval != 0) { + message = "connect failed; getsockopt retval =" + std::to_string(errno); } else { - if (retval != 0) { - std::cerr << "connect failed; getsockopt retval = " << retval << std::endl; - } else { - return TcpConnection(*dispatcher, connection); - } + return TcpConnection(*dispatcher, connection); } } } } - } else { - return TcpConnection(*dispatcher, connection); } + } else { + return TcpConnection(*dispatcher, connection); } } - - if (close(connection) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; - } } - } - throw std::runtime_error("TcpConnector::connect"); -} + int result = close(connection); + assert(result != -1); + } -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - ConnectorContext* context2 = static_cast(context); - if (!context2->interrupted) { - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, context2->connection, NULL) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpConnector::stop"); - } - dispatcher->pushContext(context2->writeContext); - context2->interrupted = true; - } - } + throw std::runtime_error("TcpConnector::connect, "+message); +} - stopped = true; } diff --git a/src/Platform/Linux/System/TcpConnector.h b/src/Platform/Linux/System/TcpConnector.h index 2add7e3dd8..9eef67efd2 100755 --- a/src/Platform/Linux/System/TcpConnector.h +++ b/src/Platform/Linux/System/TcpConnector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,17 +19,17 @@ #include #include -#include namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection; class TcpConnector { public: TcpConnector(); - TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpConnector(Dispatcher& dispatcher); TcpConnector(const TcpConnector&) = delete; TcpConnector(TcpConnector&& other); ~TcpConnector(); @@ -37,14 +37,12 @@ class TcpConnector { TcpConnector& operator=(TcpConnector&& other); void start(); void stop(); - TcpConnection connect(); + TcpConnection connect(const Ipv4Address& address, uint16_t port); private: + void* context; Dispatcher* dispatcher; - std::string address; - uint16_t port; bool stopped; - void* context; }; } diff --git a/src/Platform/Linux/System/TcpListener.cpp b/src/Platform/Linux/System/TcpListener.cpp index 221163d8b6..4a3b750813 100755 --- a/src/Platform/Linux/System/TcpListener.cpp +++ b/src/Platform/Linux/System/TcpListener.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,38 +16,75 @@ // along with Bytecoin. If not, see . #include "TcpListener.h" +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include + #include "Dispatcher.h" #include "TcpConnection.h" -#include "InterruptedException.h" +#include +#include -using namespace System; +namespace System { -namespace { +TcpListener::TcpListener() : dispatcher(nullptr) { +} -struct ListenerContext : public Dispatcher::ContextExt { - bool interrupted; -}; +TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16_t port) : dispatcher(&dispatcher) { + std::string message; + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == -1) { + message = "socket() failed, errno=" + std::to_string(errno); + } else { + int flags = fcntl(listener, F_GETFL, 0); + if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) { + message = "fcntl() failed errno=" + std::to_string(errno); + } else { + int on = 1; + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) { + message = "setsockopt failed, errno=" + std::to_string(errno); + } else { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = htonl( addr.getValue()); + if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { + message = "bind failed, errno=" + std::to_string(errno); + } else if (listen(listener, SOMAXCONN) != 0) { + message = "listen failed, errno=" + std::to_string(errno); + } else { + epoll_event listenEvent; + listenEvent.events = 0; + listenEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) { + message = "epoll_ctl() failed, errno=" + std::to_string(errno); + } else { + stopped = false; + context = nullptr; + return; + } + } + } + } -} + int result = close(listener); + assert(result != -1); + } -TcpListener::TcpListener() : dispatcher(nullptr) { + throw std::runtime_error("TcpListener::TcpListener, " + message); } TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { + assert(other.context == nullptr); listener = other.listener; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } @@ -55,9 +92,8 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { TcpListener::~TcpListener() { if (dispatcher != nullptr) { assert(context == nullptr); - if (close(listener) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - } + int result = close(listener); + assert(result != -1); } } @@ -65,16 +101,16 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (dispatcher != nullptr) { assert(context == nullptr); if (close(listener) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpListener::operator="); + throw std::runtime_error("TcpListener::operator=, close failed, errno=" + std::to_string(errno)); } } dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { + assert(other.context == nullptr); listener = other.listener; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -87,45 +123,26 @@ void TcpListener::start() { stopped = false; } -TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { - listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listener == -1) { - std::cerr << "socket failed, errno=" << errno << std::endl; - } else { - int flags = fcntl(listener, F_GETFL, 0); - if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) { - std::cerr << "fcntl() failed errno=" << errno << std::endl; - } else { - sockaddr_in address; - address.sin_family = AF_INET; - address.sin_port = htons(port); - address.sin_addr.s_addr = INADDR_ANY; - if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { - std::cerr << "bind failed, errno=" << errno << std::endl; - } else if (listen(listener, SOMAXCONN) != 0) { - std::cerr << "listen failed, errno=" << errno << std::endl; - } else { - epoll_event listenEvent; - listenEvent.data.fd = listener; - listenEvent.events = 0; - listenEvent.data.ptr = nullptr; +void TcpListener::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + Dispatcher::OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { + epoll_event listenEvent; + listenEvent.events = 0; + listenEvent.data.ptr = nullptr; - if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; - } else { - stopped = false; - context = nullptr; - return; - } + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { + throw std::runtime_error("TcpListener::stop, epoll_ctl() failed, errno=" + std::to_string(errno) ); } - } - if (close(listener) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); } } - throw std::runtime_error("TcpListener::TcpListener"); + stopped = true; } TcpConnection TcpListener::accept() { @@ -135,73 +152,56 @@ TcpConnection TcpListener::accept() { throw InterruptedException(); } - ListenerContext context2; - context2.context = dispatcher->getCurrentContext(); - context2.writeContext = nullptr; - context2.interrupted = false; + Dispatcher::ContextPair contextPair; + Dispatcher::OperationContext listenerContext; + listenerContext.interrupted = false; + listenerContext.context = dispatcher->getCurrentContext(); + + contextPair.writeContext = nullptr; + contextPair.readContext = &listenerContext; epoll_event listenEvent; - listenEvent.data.fd = listener; listenEvent.events = EPOLLIN | EPOLLONESHOT; - listenEvent.data.ptr = &context2; - + listenEvent.data.ptr = &contextPair; + std::string message; if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; + message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { - context = &context2; - dispatcher->yield(); + context = &listenerContext; + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(context2.writeContext == nullptr); - assert(context == &context2); + assert(listenerContext.context == dispatcher->getCurrentContext()); + assert(contextPair.writeContext == nullptr); + assert(context == &listenerContext); context = nullptr; - context2.context = nullptr; - if (context2.interrupted) { - if (close(listener) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; - } + listenerContext.context = nullptr; + if (listenerContext.interrupted) { throw InterruptedException(); } + if((listenerContext.events & (EPOLLERR | EPOLLHUP)) != 0) { + throw std::runtime_error("TcpListener::accept, accepting failed"); + } + sockaddr inAddr; socklen_t inLen = sizeof(inAddr); int connection = ::accept(listener, &inAddr, &inLen); if (connection == -1) { - std::cerr << "accept() failed, errno=" << errno << '.' << std::endl; + message = "accept() failed, errno=" + std::to_string(errno); } else { int flags = fcntl(connection, F_GETFL, 0); if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { - std::cerr << "fcntl() failed errno=" << errno << std::endl; + message = "fcntl() failed errno=" + std::to_string(errno); } else { return TcpConnection(*dispatcher, connection); } + + int result = close(connection); + assert(result != -1); } } - throw std::runtime_error("TcpListener::accept"); + throw std::runtime_error("TcpListener::accept, " + message); } -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - ListenerContext* context2 = static_cast(context); - if (!context2->interrupted) { - context2->interrupted = true; - - epoll_event listenEvent; - listenEvent.data.fd = listener; - listenEvent.events = 0; - listenEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpListener::stop"); - } - - dispatcher->pushContext(context2->context); - } - } - - stopped = true; } diff --git a/src/Platform/Linux/System/TcpListener.h b/src/Platform/Linux/System/TcpListener.h index 6f2b51ec46..33d625e62c 100755 --- a/src/Platform/Linux/System/TcpListener.h +++ b/src/Platform/Linux/System/TcpListener.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,17 +19,17 @@ #include #include -#include namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection; class TcpListener { public: TcpListener(); - TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uint16_t port); TcpListener(const TcpListener&) = delete; TcpListener(TcpListener&& other); ~TcpListener(); @@ -41,9 +41,9 @@ class TcpListener { private: Dispatcher* dispatcher; + void* context; int listener; bool stopped; - void* context; }; } diff --git a/src/Platform/Linux/System/Timer.cpp b/src/Platform/Linux/System/Timer.cpp index b773ea81b8..8a95a96fbe 100755 --- a/src/Platform/Linux/System/Timer.cpp +++ b/src/Platform/Linux/System/Timer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,70 +16,48 @@ // along with Bytecoin. If not, see . #include "Timer.h" +#include +#include + #include #include -#include #include -#include -#include -#include -#include "Dispatcher.h" -#include "InterruptedException.h" - -using namespace System; - -namespace { -struct TimerContext : public Dispatcher::ContextExt { - Dispatcher* dispatcher; - bool interrupted; -}; +#include "Dispatcher.h" +#include -} +namespace System { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { - timer = timerfd_create(CLOCK_MONOTONIC, 0); - epoll_event timerEvent; - timerEvent.data.fd = timer; - timerEvent.events = 0; - timerEvent.data.ptr = nullptr; - - if (epoll_ctl(this->dispatcher->getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("Timer::Timer"); - } +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), stopped(false), timer(-1) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { + assert(other.context == nullptr); timer = other.timer; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } Timer::~Timer() { - if (dispatcher != nullptr) { - close(timer); - } + assert(dispatcher == nullptr || context == nullptr); } Timer& Timer::operator=(Timer&& other) { - if (dispatcher != nullptr) { - assert(context == nullptr); - close(timer); - } - + assert(dispatcher == nullptr || context == nullptr); dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { + assert(other.context == nullptr); timer = other.timer; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; + other.timer = -1; } return *this; @@ -91,70 +69,87 @@ void Timer::start() { stopped = false; } -void Timer::sleep(std::chrono::milliseconds duration) { - assert(dispatcher != nullptr); - assert(context == nullptr); - if (stopped) { - throw InterruptedException(); - } - - auto seconds = std::chrono::duration_cast(duration); - - itimerspec expires; - expires.it_interval.tv_nsec = expires.it_interval.tv_sec = 0; - expires.it_value.tv_sec = seconds.count(); - expires.it_value.tv_nsec = std::chrono::duration_cast(duration - seconds).count(); - timerfd_settime(timer, 0, &expires, NULL); - - TimerContext context2; - context2.dispatcher = dispatcher; - context2.context = dispatcher->getCurrentContext(); - context2.writeContext = nullptr; - context2.interrupted = false; - - epoll_event timerEvent; - timerEvent.data.fd = timer; - timerEvent.events = EPOLLIN | EPOLLONESHOT; - timerEvent.data.ptr = &context2; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("Timer::sleep"); - } - - context = &context2; - dispatcher->yield(); - assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(context2.writeContext == nullptr); - assert(context == &context2); - context = nullptr; - context2.context = nullptr; - if (context2.interrupted) { - throw InterruptedException(); - } -} - void Timer::stop() { assert(dispatcher != nullptr); assert(!stopped); + if (context != nullptr) { - TimerContext* context2 = reinterpret_cast(context); - if (context2->context != nullptr) { + Dispatcher::OperationContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + + uint64_t value = 0; + if(::read(timer, &value, sizeof value) == -1 ){ + if(errno == EAGAIN || errno == EWOULDBLOCK) { + timerContext->interrupted = true; + dispatcher->pushContext(timerContext->context); + } else { + throw std::runtime_error("Timer::stop, read failed, errno=" + std::to_string(errno)); + } + } else { + assert(value>0); + dispatcher->pushContext(timerContext->context); + } + epoll_event timerEvent; - timerEvent.data.fd = timer; timerEvent.events = 0; timerEvent.data.ptr = nullptr; if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { - std::cerr << "epoll_ctl() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("Timer::sleep"); + throw std::runtime_error("Timer::stop epoll_ctl() failed, errno=" + std::to_string(errno)); } - - dispatcher->pushContext(context2->context); - context2->interrupted = true; } } stopped = true; } + +void Timer::sleep(std::chrono::nanoseconds duration) { + assert(dispatcher != nullptr); + assert(context == nullptr); + if (stopped) { + throw InterruptedException(); + } + + if(duration.count() == 0 ) { + dispatcher->yield(); + } else { + timer = dispatcher->getTimer(); + + auto seconds = std::chrono::duration_cast(duration); + itimerspec expires; + expires.it_interval.tv_nsec = expires.it_interval.tv_sec = 0; + expires.it_value.tv_sec = seconds.count(); + expires.it_value.tv_nsec = std::chrono::duration_cast(duration - seconds).count(); + timerfd_settime(timer, 0, &expires, NULL); + + Dispatcher::ContextPair contextPair; + Dispatcher::OperationContext timerContext; + timerContext.interrupted = false; + timerContext.context = dispatcher->getCurrentContext(); + contextPair.writeContext = nullptr; + contextPair.readContext = &timerContext; + + epoll_event timerEvent; + timerEvent.events = EPOLLIN | EPOLLONESHOT; + timerEvent.data.ptr = &contextPair; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { + throw std::runtime_error("Timer::sleep, epoll_ctl() failed, errno=" + std::to_string(errno)); + } + + context = &timerContext; + dispatcher->dispatch(); + assert(dispatcher != nullptr); + assert(timerContext.context == dispatcher->getCurrentContext()); + assert(contextPair.writeContext == nullptr); + assert(context == &timerContext); + context = nullptr; + timerContext.context = nullptr; + dispatcher->pushTimer(timer); + if (timerContext.interrupted) { + throw InterruptedException(); + } + } +} + +} diff --git a/src/Platform/Linux/System/Timer.h b/src/Platform/Linux/System/Timer.h index 1269281c45..20a0f1a4bd 100755 --- a/src/Platform/Linux/System/Timer.h +++ b/src/Platform/Linux/System/Timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -34,13 +34,13 @@ class Timer { Timer& operator=(Timer&& other); void start(); void stop(); - void sleep(std::chrono::milliseconds duration); + void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; - int timer; - bool stopped; void* context; + bool stopped; + int timer; }; } diff --git a/src/Platform/OSX/System/Dispatcher.cpp b/src/Platform/OSX/System/Dispatcher.cpp index b731b3c24d..ba2aad22f0 100755 --- a/src/Platform/OSX/System/Dispatcher.cpp +++ b/src/Platform/OSX/System/Dispatcher.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,35 +16,51 @@ // along with Bytecoin. If not, see . #include "Dispatcher.h" -#include -#define _XOPEN_SOURCE -#include -#include -#include +#include +#include + +#include +#include #include -#include +#include #include +#include +#include -using namespace System; +#include "context.h" -void Dispatcher::contextProcedureStatic(void *context) { - reinterpret_cast(context)->contextProcedure(); -} +namespace System { Dispatcher::Dispatcher() : lastCreatedTimer(0) { + std::string message; kqueue = ::kqueue(); if (kqueue == -1) { - std::cerr << "kqueue() fail errno=" << errno << std::endl; + message = "kqueue() fail errno=" + std::to_string(errno); } else { - currentContext = new ucontext_t; - if (getcontext(reinterpret_cast(currentContext)) == -1) { - std::cerr << "getcontext() fail errno=" << errno << std::endl; + currentContext = new uctx; + if (getcontext(static_cast(currentContext)) == -1) { + message = "getcontext() fail errno=" + std::to_string(errno); } else { - contextCount = 0; - return; + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD, NOTE_FFNOP, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + message = "kevent() fail errno=" + std::to_string(errno); + } else { + if(pthread_mutex_init(reinterpret_cast(this->mutex), NULL) == -1) { + message = "pthread_mutex_init() fail errno=" + std::to_string(errno); + } else { + remoteSpawned = false; + contextCount = 0; + return; + } + } } + + auto result = close(kqueue); + assert(result == 0); } - throw std::runtime_error("Dispatcher::Dispatcher"); + + throw std::runtime_error("Dispatcher::Dispatcher, " + message); } Dispatcher::~Dispatcher() { @@ -55,16 +71,83 @@ Dispatcher::~Dispatcher() { while (!reusableContexts.empty()) { delete[] allocatedStacks.top(); allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); + delete static_cast(reusableContexts.top()); reusableContexts.pop(); } - while (!timers.empty()) { - timers.pop(); + auto result = close(kqueue); + assert(result != -1); + result = pthread_mutex_destroy(reinterpret_cast(this->mutex)); + assert(result != -1); +} + +void Dispatcher::clear() { + while (!reusableContexts.empty()) { + delete[] allocatedStacks.top(); + allocatedStacks.pop(); + delete static_cast(reusableContexts.top()); + reusableContexts.pop(); + --contextCount; } +} + +void Dispatcher::dispatch() { + void* context; + for (;;) { + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } + + if(remoteSpawned.load() == true) { + pthread_mutex_lock(reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } - if (-1 == close(kqueue)) { - std::cerr << "close() fail errno=" << errno << std::endl; + remoteSpawned = false; + pthread_mutex_unlock(reinterpret_cast(this->mutex)); + continue; + } + + struct kevent event; + int count = kevent(kqueue, NULL, 0, &event, 1, NULL); + if (count == 1) { + if (event.filter == EVFILT_USER && event.ident == 0) { + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_DISABLE, NOTE_FFNOP, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("kevent() fail errno=" + std::to_string(errno)); + } + + continue; + } + + context = static_cast(event.udata)->context; + break; + } + + if (errno != EINTR) { + throw std::runtime_error("Dispatcher::dispatch(), kqueue() fail errno=" + std::to_string(errno)); + } else { + pthread_mutex_lock(reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + pthread_mutex_unlock(reinterpret_cast(this->mutex)); + } + } + + if (context != currentContext) { + uctx* oldContext = static_cast(currentContext); + currentContext = context; + if (swapcontext(oldContext,static_cast(currentContext)) == -1) { + throw std::runtime_error("Dispatcher::dispatch(), swapcontext() failed, errno=" + std::to_string(errno)); + } } } @@ -72,27 +155,36 @@ void* Dispatcher::getCurrentContext() const { return currentContext; } -int Dispatcher::getKqueue() const { - return kqueue; -} - void Dispatcher::pushContext(void* context) { resumingContexts.push(context); } +void Dispatcher::remoteSpawn(std::function&& procedure) { + pthread_mutex_lock(reinterpret_cast(this->mutex)); + remoteSpawningProcedures.push(std::move(procedure)); + if(remoteSpawned == false) { + remoteSpawned = true; + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ONESHOT, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Dispatcher::remoteSpawn(), kevent() fail errno=" + std::to_string(errno)); + }; + } + + pthread_mutex_unlock(reinterpret_cast(this->mutex)); +} + void Dispatcher::spawn(std::function&& procedure) { void* context; if (reusableContexts.empty()) { - context = new ucontext_t; - if (-1 == getcontext(reinterpret_cast(context))) { //makecontext precondition - std::cerr << "getcontext() fail errno=" << errno << std::endl; - throw std::runtime_error("Dispatcher::spawn()"); - } - auto stackPointer = new uint8_t[64 * 1024]; - reinterpret_cast(context)->uc_stack.ss_sp = stackPointer; + context = new uctx; + uint8_t* stackPointer = new uint8_t[64 * 1024]; allocatedStacks.push(stackPointer); - reinterpret_cast(context)->uc_stack.ss_size = 64 * 1024; - makecontext(reinterpret_cast(context), (void(*)())contextProcedureStatic, 1, reinterpret_cast(this)); + + static_cast(context)->uc_stack.ss_sp = stackPointer; + static_cast(context)->uc_stack.ss_size = 64 * 1024; + makecontext(static_cast(context), reinterpret_cast(contextProcedureStatic), reinterpret_cast(this)); + ++contextCount; } else { context = reusableContexts.top(); @@ -103,6 +195,54 @@ void Dispatcher::spawn(std::function&& procedure) { spawningProcedures.emplace(std::move(procedure)); } +void Dispatcher::yield() { + struct timespec zeroTimeout = { 0, 0 }; + for (;;) { + struct kevent events[16]; + int count = kevent(kqueue, NULL, 0, events, 16, &zeroTimeout); + if (count == 0) { + break; + } + + if (count > 0) { + for (int i = 0; i < count; ++i) { + if (events[i].filter == EVFILT_USER && events[i].ident == 0) { + struct kevent event; + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_DISABLE, NOTE_FFNOP, 0, NULL); + if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("kevent() fail errno=" + std::to_string(errno)); + } + + pthread_mutex_lock(reinterpret_cast(this->mutex)); + while (!remoteSpawningProcedures.empty()) { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } + + remoteSpawned = false; + pthread_mutex_unlock(reinterpret_cast(this->mutex)); + continue; + } + + resumingContexts.push(static_cast(events[i].udata)->context); + } + } else { + if (errno != EINTR) { + throw std::runtime_error("Dispatcher::dispatch(), epoll_wait() failed, errno=" + std::to_string(errno)); + } + } + } + + if (!resumingContexts.empty()) { + resumingContexts.push(getCurrentContext()); + dispatch(); + } +} + +int Dispatcher::getKqueue() const { + return kqueue; +} + int Dispatcher::getTimer() { int timer; if (timers.empty()) { @@ -119,43 +259,6 @@ void Dispatcher::pushTimer(int timer) { timers.push(timer); } -void Dispatcher::clear() { -//TODO -} - -void Dispatcher::yield() { - void* context; - for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); - break; - } - - struct kevent event; - int count = kevent(kqueue, NULL, 0, &event, 1, NULL); - - if (count == 1) { - context = static_cast(event.udata)->context; - break; - } - - if (errno != EINTR) { - std::cerr << "kevent() failed, errno=" << errno << std::endl; - throw std::runtime_error("Dispatcher::yield()"); - } - } - - if (context != currentContext) { - ucontext_t* oldContext = static_cast(currentContext); - currentContext = context; - if (-1 == swapcontext(oldContext, static_cast(context))) { - std::cerr << "setcontext() failed, errno=" << errno << std::endl; - throw std::runtime_error("Dispatcher::yield()"); - } - } -} - void Dispatcher::contextProcedure() { void* context = currentContext; for (;;) { @@ -164,6 +267,12 @@ void Dispatcher::contextProcedure() { spawningProcedures.pop(); procedure(); reusableContexts.push(context); - yield(); + dispatch(); } } + +void Dispatcher::contextProcedureStatic(intptr_t context) { + reinterpret_cast(context)->contextProcedure(); +} + +} diff --git a/src/Platform/OSX/System/Dispatcher.h b/src/Platform/OSX/System/Dispatcher.h index 37690d53b4..482dc3c2ca 100755 --- a/src/Platform/OSX/System/Dispatcher.h +++ b/src/Platform/OSX/System/Dispatcher.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,6 +17,7 @@ #pragma once +#include #include #include #include @@ -29,38 +30,45 @@ class Dispatcher { Dispatcher(const Dispatcher&) = delete; ~Dispatcher(); Dispatcher& operator=(const Dispatcher&) = delete; + void clear(); + void dispatch(); + void* getCurrentContext() const; + void pushContext(void* context); + void remoteSpawn(std::function&& procedure); void spawn(std::function&& procedure); void yield(); - void clear(); - - struct ContextExt { + + struct OperationContext { void *context; + bool interrupted; }; + + int getKqueue() const; + int getTimer(); + void pushTimer(int timer); + +#ifdef __LP64__ + static const int SIZEOF_PTHREAD_MUTEX_T = 56 + sizeof(long); +#else + static const int SIZEOF_PTHREAD_MUTEX_T = 40 + sizeof(long); +#endif + private: - friend class Event; - friend class DispatcherAccessor; - friend class TcpConnection; - friend class TcpConnector; - friend class TcpListener; - friend class Timer; - int kqueue; + std::stack allocatedStacks; + std::size_t contextCount; void* currentContext; + int kqueue; int lastCreatedTimer; - std::size_t contextCount; + uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + std::atomic remoteSpawned; + std::queue> remoteSpawningProcedures; std::queue resumingContexts; - std::stack reusableContexts; - std::stack allocatedStacks; std::queue> spawningProcedures; + std::stack reusableContexts; std::stack timers; - - int getKqueue() const; - int getTimer(); - void pushTimer(int timer); - void pushContext(void* context); - void* getCurrentContext() const; - + void contextProcedure(); - static void contextProcedureStatic(void* context); + static void contextProcedureStatic(intptr_t context); }; } diff --git a/src/Platform/OSX/System/Event.cpp b/src/Platform/OSX/System/Event.cpp deleted file mode 100755 index 008bb332b1..0000000000 --- a/src/Platform/OSX/System/Event.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Event.h" -#include -#include -#include "Dispatcher.h" - -using namespace System; - -namespace { - -struct Waiter { - Waiter* next; - void* context; -}; - -} - -Event::Event() : dispatcher(nullptr) { -} - -Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { -} - -Event::Event(Event&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { - first = other.first; - if (other.first != nullptr) { - last = other.last; - } - - state = other.state; - other.dispatcher = nullptr; - } -} - -Event::~Event() { - assert(first == nullptr); -} - -Event& Event::operator=(Event&& other) { - assert(first == nullptr); - dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { - first = other.first; - if (other.first != nullptr) { - last = other.last; - } - - state = other.state; - other.dispatcher = nullptr; - } - - return *this; -} - -bool Event::get() const { - assert(dispatcher != nullptr); - return state; -} - -void Event::clear() { - assert(dispatcher != nullptr); - state = false; -} - -void Event::set() { - assert(dispatcher != nullptr); - state = true; - for (Waiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { - dispatcher->pushContext(waiter->context); - } - - first = nullptr; -} - -void Event::wait() { - assert(dispatcher != nullptr); - if (!state) { - Waiter waiter = { nullptr, dispatcher->getCurrentContext() }; - if (first != nullptr) { - static_cast(last)->next = &waiter; - } else { - first = &waiter; - } - - last = &waiter; - dispatcher->yield(); - assert(dispatcher != nullptr); - } -} diff --git a/src/Platform/OSX/System/Ipv4Resolver.cpp b/src/Platform/OSX/System/Ipv4Resolver.cpp new file mode 100755 index 0000000000..d78195e45c --- /dev/null +++ b/src/Platform/OSX/System/Ipv4Resolver.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Ipv4Resolver.h" +#include +#include +#include + +#include + +#include +#include + +namespace System { + +Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { +} + +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +} + +Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + stopped = other.stopped; + other.dispatcher = nullptr; + } +} + +Ipv4Resolver::~Ipv4Resolver() { +} + +Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + stopped = other.stopped; + other.dispatcher = nullptr; + } + + return *this; +} + +void Ipv4Resolver::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void Ipv4Resolver::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + stopped = true; +} + +Ipv4Address Ipv4Resolver::resolve(const std::string& host) { + assert(dispatcher != nullptr); + if (stopped) { + throw InterruptedException(); + } + + addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; + addrinfo* addressInfos; + int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos); + if (result != 0) { + throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result)); + } + + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::mt19937 generator{ std::random_device()() }; + std::size_t index = std::uniform_int_distribution(0, count - 1)(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + Ipv4Address address(ntohl(reinterpret_cast(addressInfo->ai_addr)->sin_addr.s_addr)); + freeaddrinfo(addressInfo); + return address; +} + +} diff --git a/src/Platform/OSX/System/Ipv4Resolver.h b/src/Platform/OSX/System/Ipv4Resolver.h new file mode 100755 index 0000000000..d59d50e0f2 --- /dev/null +++ b/src/Platform/OSX/System/Ipv4Resolver.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; + +class Ipv4Resolver { +public: + Ipv4Resolver(); + explicit Ipv4Resolver(Dispatcher& dispatcher); + Ipv4Resolver(const Ipv4Resolver&) = delete; + Ipv4Resolver(Ipv4Resolver&& other); + ~Ipv4Resolver(); + Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; + Ipv4Resolver& operator=(Ipv4Resolver&& other); + void start(); + void stop(); + Ipv4Address resolve(const std::string& host); + +private: + Dispatcher* dispatcher; + bool stopped; +}; + +} diff --git a/src/Platform/OSX/System/TcpConnection.cpp b/src/Platform/OSX/System/TcpConnection.cpp index 6410ceb150..dec331eef2 100755 --- a/src/Platform/OSX/System/TcpConnection.cpp +++ b/src/Platform/OSX/System/TcpConnection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,37 +16,31 @@ // along with Bytecoin. If not, see . #include "TcpConnection.h" -#include -#include -#include -#include +#include + +#include #include +#include #include -#include "Dispatcher.h" -#include "InterruptedException.h" - -using namespace System; - -namespace { +#include -struct ConnectionContext : public Dispatcher::ContextExt { - bool interrupted; -}; +#include "Dispatcher.h" +#include +#include -} +namespace System { TcpConnection::TcpConnection() : dispatcher(nullptr) { } -TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { -} - TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { + assert(other.readContext == nullptr); + assert(other.writeContext == nullptr); connection = other.connection; stopped = other.stopped; - readContext = other.readContext; - writeContext = other.writeContext; + readContext = nullptr; + writeContext = nullptr; other.dispatcher = nullptr; } } @@ -55,9 +49,8 @@ TcpConnection::~TcpConnection() { if (dispatcher != nullptr) { assert(readContext == nullptr); assert(writeContext == nullptr); - if (close(connection) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - } + int result = close(connection); + assert(result != -1); } } @@ -66,17 +59,18 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(readContext == nullptr); assert(writeContext == nullptr); if (close(connection) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpConnection::operator="); + throw std::runtime_error("TcpConnection::operator=, close() failed, errno=" + std::to_string(errno)); } } dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { + assert(other.readContext == nullptr); + assert(other.writeContext == nullptr); connection = other.connection; stopped = other.stopped; - readContext = other.readContext; - writeContext = other.writeContext; + readContext = nullptr; + writeContext = nullptr; other.dispatcher = nullptr; } @@ -89,6 +83,42 @@ void TcpConnection::start() { stopped = false; } +void TcpConnection::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (writeContext != nullptr) { + Dispatcher::OperationContext* context = static_cast(writeContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + } + + if (readContext != nullptr) { + Dispatcher::OperationContext* context = static_cast(readContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + } + + stopped = true; +} + size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(readContext == nullptr); @@ -96,152 +126,121 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { throw InterruptedException(); } + std::string message; ssize_t transferred = ::recv(connection, (void *)data, size, 0); if (transferred == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "recv failed, result=" << errno << '.' << std::endl; + message = "recv failed, errno=" + std::to_string(errno); } else { - ConnectionContext context2; - context2.context = dispatcher->getCurrentContext(); - context2.interrupted = false; + Dispatcher::OperationContext context; + context.context = dispatcher->getCurrentContext(); + context.interrupted = false; struct kevent event; - EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context2); + EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + message = "kevent() failed, errno=" + std::to_string(errno); } else { - readContext = &context2; - dispatcher->yield(); + readContext = &context; + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(readContext == &context2); + assert(context.context == dispatcher->getCurrentContext()); + assert(readContext == &context); readContext = nullptr; - context2.context = nullptr; - if (context2.interrupted) { + context.context = nullptr; + if (context.interrupted) { throw InterruptedException(); } ssize_t transferred = ::recv(connection, (void *)data, size, 0); if (transferred == -1) { - std::cerr << "recv failed, errno=" << errno << '.' << std::endl; + message = "recv failed, errno=" + std::to_string(errno); } else { - if (transferred == 0) { - std::cerr << "recv return after yield with 0 bytes" << std::endl; - - int retval = -1; - socklen_t retValLen = sizeof(retval); - int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); - if (s == -1) { - std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; - } else { - std::cerr << "recv getsockopt retval = " << retval << std::endl; - } - } - - assert(transferred <= size); + assert(transferred <= static_cast(size)); return transferred; } } } - throw std::runtime_error("TcpConnection::read"); + throw std::runtime_error("TcpConnection::read, " + message); } - assert(transferred <= size); + assert(transferred <= static_cast(size)); return transferred; } -void TcpConnection::write(const uint8_t* data, size_t size) { +size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(writeContext == nullptr); if (stopped) { throw InterruptedException(); } + std::string message; if (size == 0) { if (shutdown(connection, SHUT_WR) == -1) { - std::cerr << "shutdown failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(errno)); } - return; + return 0; } ssize_t transferred = ::send(connection, (void *)data, size, 0); if (transferred == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { - std::cerr << "send failed, result=" << errno << '.' << std::endl; + message = "send failed, result=" + std::to_string(errno); } else { - ConnectionContext context2; - context2.context = dispatcher->getCurrentContext(); - context2.interrupted = false; + Dispatcher::OperationContext context; + context.context = dispatcher->getCurrentContext(); + context.interrupted = false; struct kevent event; - EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context2); + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + message = "kevent() failed, errno=" + std::to_string(errno); } else { - writeContext = &context2; - dispatcher->yield(); + writeContext = &context; + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(writeContext == &context2); + assert(context.context == dispatcher->getCurrentContext()); + assert(writeContext == &context); writeContext = nullptr; - context2.context = nullptr; - if (context2.interrupted) { + context.context = nullptr; + if (context.interrupted) { throw InterruptedException(); } ssize_t transferred = ::send(connection, (void *)data, size, 0); if (transferred == -1) { - std::cerr << "recv failed, errno=" << errno << '.' << std::endl; + message = "send failed, errno=" + std::to_string(errno); } else { - if (transferred == 0) { - throw std::runtime_error("send transferred 0 bytes."); - } - - assert(transferred == size); - return; + assert(transferred <= static_cast(size)); + return transferred; } } } - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, " + message); } -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (writeContext != nullptr && static_cast(writeContext)->context != nullptr) { - ConnectionContext* context2 = static_cast(writeContext); - if (!context2->interrupted) { - struct kevent event; - EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpListener::stop"); - } + assert(transferred <= static_cast(size)); + return transferred; +} - context2->interrupted = true; - dispatcher->pushContext(context2->context); - } +std::pair TcpConnection::getPeerAddressAndPort() { + sockaddr_in addr; + socklen_t size = sizeof(addr); + if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { + throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(errno)); } - if (readContext != nullptr && static_cast(readContext)->context != nullptr) { - ConnectionContext* context2 = static_cast(readContext); - if (!context2->interrupted) { - struct kevent event; - EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpListener::stop"); - } + assert(size == sizeof(sockaddr_in)); + return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); +} - context2->interrupted = true; - dispatcher->pushContext(context2->context); - } +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { + int val = 1; + if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) { + throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, result=" + std::to_string(errno)); } +} - stopped = true; } diff --git a/src/Platform/OSX/System/TcpConnection.h b/src/Platform/OSX/System/TcpConnection.h index 74b099d58f..c98650f7ad 100755 --- a/src/Platform/OSX/System/TcpConnection.h +++ b/src/Platform/OSX/System/TcpConnection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,10 +19,12 @@ #include #include +#include namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection { public: @@ -35,19 +37,20 @@ class TcpConnection { void start(); void stop(); std::size_t read(uint8_t* data, std::size_t size); - void write(const uint8_t* data, std::size_t size); + std::size_t write(const uint8_t* data, std::size_t size); + std::pair getPeerAddressAndPort(); private: friend class TcpConnector; friend class TcpListener; - explicit TcpConnection(Dispatcher& dispatcher, int socket); - Dispatcher* dispatcher; int connection; bool stopped; void* readContext; void* writeContext; + + TcpConnection(Dispatcher& dispatcher, int socket); }; } diff --git a/src/Platform/OSX/System/TcpConnector.cpp b/src/Platform/OSX/System/TcpConnector.cpp index a5110bc3e0..794ec5152c 100755 --- a/src/Platform/OSX/System/TcpConnector.cpp +++ b/src/Platform/OSX/System/TcpConnector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -13,31 +13,30 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see +// along with Bytecoin. If not, see . #include "TcpConnector.h" #include -#include -#include -#include + +#include +#include +#include #include -#include #include -#include -#include -#include +#include #include -#include "InterruptedException.h" + +#include +#include #include "Dispatcher.h" #include "TcpConnection.h" -using namespace System; +namespace System { namespace { -struct ConnectorContext : public Dispatcher::ContextExt { +struct ConnectorContext : public Dispatcher::OperationContext { int connection; - bool interrupted; }; } @@ -45,29 +44,28 @@ struct ConnectorContext : public Dispatcher::ContextExt { TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { - address = other.address; - port = other.port; + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } TcpConnector::~TcpConnector() { + assert(dispatcher == nullptr || context == nullptr); } TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { - address = other.address; - port = other.port; + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -84,130 +82,103 @@ void TcpConnector::stop() { assert(dispatcher != nullptr); assert(!stopped); if (context != nullptr) { - ConnectorContext* context2 = static_cast(context); - if (!context2->interrupted) { - struct kevent event; - EV_SET(&event, context2->connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpConnector::stop"); + ConnectorContext* connectorContext = static_cast(context); + if (!connectorContext->interrupted) { + if (close(connectorContext->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); } - dispatcher->pushContext(context2->context); - context2->interrupted = true; + dispatcher->pushContext(connectorContext->context); + connectorContext->interrupted = true; } } stopped = true; } -TcpConnection TcpConnector::connect() { +TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); if (stopped) { throw InterruptedException(); } - std::ostringstream portStream; - portStream << port; - addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; - addrinfo *addressInfos; - int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); - if (result == -1) { - std::cerr << "getaddrinfo failed, errno=" << errno << '.' << std::endl; + std::string message; + int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == -1) { + message = "socket() failed, errno=" + std::to_string(errno); } else { - std::size_t count = 0; - for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { - ++count; - } - - std::random_device randomDevice; - std::mt19937 generator(randomDevice()); - std::uniform_int_distribution distribution(0, count - 1); - std::size_t index = distribution(generator); - addrinfo* addressInfo = addressInfos; - for (std::size_t i = 0; i < index; ++i) { - addressInfo = addressInfo->ai_next; - } - - sockaddr_in addressData = *reinterpret_cast(addressInfo->ai_addr); - freeaddrinfo(addressInfo); - int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (connection == -1) { - std::cerr << "socket failed, errno=" << errno << '.' << std::endl; + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + message = "bind failed, errno=" + std::to_string(errno); } else { - sockaddr_in bindAddress; - bindAddress.sin_family = AF_INET; - bindAddress.sin_port = 0; - bindAddress.sin_addr.s_addr = INADDR_ANY; - if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { - std::cerr << "bind failed, errno=" << errno << '.' << std::endl; + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + message = "fcntl() failed errno=" + std::to_string(errno); } else { - int flags = fcntl(connection, F_GETFL, 0); - if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) { - std::cerr << "fcntl() failed errno=" << errno << std::endl; - } else { - int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); - if (result == -1) { - if (errno == EINPROGRESS) { - - ConnectorContext context2; - context2.context = dispatcher->getCurrentContext(); - context2.interrupted = false; - context2.connection = connection; + sockaddr_in addressData; + addressData.sin_family = AF_INET; + addressData.sin_port = htons(port); + addressData.sin_addr.s_addr = htonl(address.getValue()); + int result = ::connect(connection, reinterpret_cast(&addressData), sizeof addressData); + if (result == -1) { + if (errno == EINPROGRESS) { + ConnectorContext connectorContext; + connectorContext.context = dispatcher->getCurrentContext(); + connectorContext.interrupted = false; + connectorContext.connection = connection; + + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &connectorContext); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent() failed, errno=" + std::to_string(errno); + } else { + context = &connectorContext; + dispatcher->dispatch(); + assert(dispatcher != nullptr); + assert(connectorContext.context == dispatcher->getCurrentContext()); + assert(context == &connectorContext); + context = nullptr; + connectorContext.context = nullptr; + if (connectorContext.interrupted) { + throw InterruptedException(); + } struct kevent event; - EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &context2); + EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL); + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + message = "kevent() failed, errno=" + std::to_string(errno); } else { - context = &context2; - dispatcher->yield(); - assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(context == &context2); - context = nullptr; - context2.context = nullptr; - if (context2.interrupted) { - if (close(connection) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; - } - - throw InterruptedException(); - } - struct kevent event; - EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + int retval = -1; + socklen_t retValLen = sizeof(retval); + int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); + if (s == -1) { + message = "getsockopt() failed, errno=" + std::to_string(errno); } else { - int retval = -1; - socklen_t retValLen = sizeof(retval); - int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen); - if (s == -1) { - std::cerr << "getsockopt() failed, errno=" << errno << '.' << std::endl; + if (retval != 0) { + message = "connect failed; getsockopt retval =" + std::to_string(errno); } else { - if (retval != 0) { - std::cerr << "connect failed; getsockopt retval = " << retval << std::endl; - } else { - return TcpConnection(*dispatcher, connection); - } + return TcpConnection(*dispatcher, connection); } } } } - } else { - return TcpConnection(*dispatcher, connection); } + } else { + return TcpConnection(*dispatcher, connection); } } - - if (close(connection) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; - } } + + int result = close(connection); + assert(result != -1);; } - throw std::runtime_error("TcpConnector::connect"); + throw std::runtime_error("TcpConnector::connect, " + message); +} + } diff --git a/src/Platform/OSX/System/TcpConnector.h b/src/Platform/OSX/System/TcpConnector.h index 1f64b2d264..9eef67efd2 100755 --- a/src/Platform/OSX/System/TcpConnector.h +++ b/src/Platform/OSX/System/TcpConnector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,12 +23,13 @@ namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection; class TcpConnector { public: TcpConnector(); - TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpConnector(Dispatcher& dispatcher); TcpConnector(const TcpConnector&) = delete; TcpConnector(TcpConnector&& other); ~TcpConnector(); @@ -36,14 +37,12 @@ class TcpConnector { TcpConnector& operator=(TcpConnector&& other); void start(); void stop(); - TcpConnection connect(); + TcpConnection connect(const Ipv4Address& address, uint16_t port); private: + void* context; Dispatcher* dispatcher; - std::string address; - uint16_t port; bool stopped; - void* context; }; } diff --git a/src/Platform/OSX/System/TcpListener.cpp b/src/Platform/OSX/System/TcpListener.cpp index 63a87462ac..4c9f15bfa5 100755 --- a/src/Platform/OSX/System/TcpListener.cpp +++ b/src/Platform/OSX/System/TcpListener.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,35 +17,77 @@ #include "TcpListener.h" #include -#include -#include +#include + #include +#include +#include #include -#include #include -#include -#include "InterruptedException.h" +#include +#include + #include "Dispatcher.h" #include "TcpConnection.h" +#include +#include -using namespace System; +namespace System { -namespace { +TcpListener::TcpListener() : dispatcher(nullptr) { +} -struct ListenerContext : public Dispatcher::ContextExt { - bool interrupted; -}; +TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16_t port) : dispatcher(&dispatcher) { + std::string message; + listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listener == -1) { + message = "socket() failed, errno=" + std::to_string(errno); + } else { + int flags = fcntl(listener, F_GETFL, 0); + if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) { + message = "fcntl() failed errno=" + std::to_string(errno); + } else { + int on = 1; + if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) { + message = "setsockopt failed, errno=" + std::to_string(errno); + } else { + sockaddr_in address; + address.sin_family = AF_INET; + address.sin_port = htons(port); + address.sin_addr.s_addr = htonl(addr.getValue()); + if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { + message = "bind failed, errno=" + std::to_string(errno); + } else if (listen(listener, SOMAXCONN) != 0) { + message = "listen failed, errno=" + std::to_string(errno); + } else { + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL); + + if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + message = "kevent() failed, errno=" + std::to_string(errno); + } else { + stopped = false; + context = nullptr; + return; + } + } + } + } -} + if (close(listener) == -1) { + message = "close failed, errno=" + std::to_string(errno); + } + } -TcpListener::TcpListener() : dispatcher(nullptr) { + throw std::runtime_error("TcpListener::TcpListener, " + message); } TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { + assert(other.context == nullptr); listener = other.listener; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } @@ -53,9 +95,8 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { TcpListener::~TcpListener() { if (dispatcher != nullptr) { assert(context == nullptr); - if (close(listener) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - } + int result = close(listener); + assert(result != -1); } } @@ -63,16 +104,16 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (dispatcher != nullptr) { assert(context == nullptr); if (close(listener) == -1) { - std::cerr << "close() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpListener::operator="); + throw std::runtime_error("TcpListener::operator=, close failed, errno=" + std::to_string(errno)); } } dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { + assert(other.context == nullptr); listener = other.listener; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -85,43 +126,26 @@ void TcpListener::start() { stopped = false; } -TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { - listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (listener == -1) { - std::cerr << "socket failed, errno=" << errno << std::endl; - } else { - int flags = fcntl(listener, F_GETFL, 0); - if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) { - std::cerr << "fcntl() failed errno=" << errno << std::endl; - } else { - sockaddr_in address; - address.sin_family = AF_INET; - address.sin_port = htons(port); - address.sin_addr.s_addr = INADDR_ANY; - if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { - std::cerr << "bind failed, errno=" << errno << std::endl; - } else if (listen(listener, SOMAXCONN) != 0) { - std::cerr << "listen failed, errno=" << errno << std::endl; - } else { - struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL); +void TcpListener::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + if (context != nullptr) { + Dispatcher::OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { - if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - } else { - stopped = false; - context = nullptr; - return; - } + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); } - } - if (close(listener) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); } } - throw std::runtime_error("TcpListener::TcpListener"); + stopped = true; } TcpConnection TcpListener::accept() { @@ -131,72 +155,42 @@ TcpConnection TcpListener::accept() { throw InterruptedException(); } - ListenerContext context2; - context2.context = dispatcher->getCurrentContext(); - context2.interrupted = false; - + std::string message; + Dispatcher::OperationContext listenerContext; + listenerContext.context = dispatcher->getCurrentContext(); + listenerContext.interrupted = false; struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &context2); + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &listenerContext); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + message = "kevent() failed, errno=" + std::to_string(errno); } else { - context = &context2; - dispatcher->yield(); + context = &listenerContext; + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(context == &context2); + assert(listenerContext.context == dispatcher->getCurrentContext()); + assert(context == &listenerContext); context = nullptr; - context2.context = nullptr; - if (context2.interrupted) { - if (close(listener) == -1) { - std::cerr << "close failed, errno=" << errno << std::endl; - } + listenerContext.context = nullptr; + if (listenerContext.interrupted) { throw InterruptedException(); } - struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, NULL); - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; + sockaddr inAddr; + socklen_t inLen = sizeof(inAddr); + int connection = ::accept(listener, &inAddr, &inLen); + if (connection == -1) { + message = "accept() failed, errno=" + std::to_string(errno); } else { - sockaddr inAddr; - socklen_t inLen = sizeof(inAddr); - int connection = ::accept(listener, &inAddr, &inLen); - if (connection == -1) { - std::cerr << "accept() failed, errno=" << errno << '.' << std::endl; + int flags = fcntl(connection, F_GETFL, 0); + if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) { + message = "fcntl() failed errno=" + std::to_string(errno); } else { - int flags = fcntl(connection, F_GETFL, 0); - if (flags == -1 || (fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1)) { - std::cerr << "fcntl() failed errno=" << errno << std::endl; - } else { - return TcpConnection(*dispatcher, connection); - } + return TcpConnection(*dispatcher, connection); } } } - throw std::runtime_error("TcpListener::accept"); + throw std::runtime_error("TcpListener::accept, " + message); } -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - ListenerContext* context2 = static_cast(context); - if (!context2->interrupted) { - context2->interrupted = true; - - struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("TcpListener::stop"); - } - - dispatcher->pushContext(context2->context); - } - } - - stopped = true; } diff --git a/src/Platform/OSX/System/TcpListener.h b/src/Platform/OSX/System/TcpListener.h index 2fcd6f337f..43fc2b4a35 100755 --- a/src/Platform/OSX/System/TcpListener.h +++ b/src/Platform/OSX/System/TcpListener.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,12 +23,13 @@ namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection; class TcpListener { public: TcpListener(); - TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uint16_t port); TcpListener(const TcpListener&) = delete; TcpListener(TcpListener&& other); ~TcpListener(); diff --git a/src/Platform/OSX/System/Timer.cpp b/src/Platform/OSX/System/Timer.cpp index 63f1941a1e..2160b17d04 100755 --- a/src/Platform/OSX/System/Timer.cpp +++ b/src/Platform/OSX/System/Timer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,60 +17,49 @@ #include "Timer.h" #include -#include +#include +#include + +#include #include #include -#include #include -#include "Dispatcher.h" -#include "InterruptedException.h" - -using namespace System; -namespace { - -struct TimerContext : public Dispatcher::ContextExt { - Dispatcher* dispatcher; - bool interrupted; -}; +#include "Dispatcher.h" +#include -} +namespace System { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { - timer = dispatcher.getTimer(); -} - -Timer::~Timer() { - if (dispatcher != nullptr) { - assert(context == nullptr); - dispatcher->pushTimer(timer); - } +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr), timer(-1) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { + assert(other.context == nullptr); timer = other.timer; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } -Timer& Timer::operator=(Timer&& other) { - if (dispatcher != nullptr) { - assert(context == nullptr); - dispatcher->pushTimer(timer); - } +Timer::~Timer() { + assert(dispatcher == nullptr || context == nullptr); +} +Timer& Timer::operator=(Timer&& other) { + assert(dispatcher == nullptr || context == nullptr); dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { + assert(other.context == nullptr); timer = other.timer; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; + other.timer = -1; } return *this; @@ -82,6 +71,28 @@ void Timer::start() { stopped = false; } +void Timer::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + + if (context != nullptr) { + Dispatcher::OperationContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + dispatcher->pushContext(timerContext->context); + timerContext->interrupted = true; + } + } + + stopped = true; +} + void Timer::sleep(std::chrono::milliseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); @@ -89,49 +100,29 @@ void Timer::sleep(std::chrono::milliseconds duration) { throw InterruptedException(); } - TimerContext context2; - context2.dispatcher = dispatcher; - context2.context = dispatcher->getCurrentContext(); - context2.interrupted = false; + Dispatcher::OperationContext timerContext; + timerContext.context = dispatcher->getCurrentContext(); + timerContext.interrupted = false; + timer = dispatcher->getTimer(); struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count(), &context2); + EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count(), &timerContext); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("Timer::sleep"); + throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); } - context = &context2; - dispatcher->yield(); + context = &timerContext; + dispatcher->dispatch(); assert(dispatcher != nullptr); - assert(context2.context == dispatcher->getCurrentContext()); - assert(context == &context2); + assert(timerContext.context == dispatcher->getCurrentContext()); + assert(context == &timerContext); context = nullptr; - context2.context = nullptr; - if (context2.interrupted) { + timerContext.context = nullptr; + dispatcher->pushTimer(timer); + if (timerContext.interrupted) { throw InterruptedException(); } } -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TimerContext* context2 = reinterpret_cast(context); - if (context2->context != nullptr) { - struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - std::cerr << "kevent() failed, errno=" << errno << '.' << std::endl; - throw std::runtime_error("Timer::stop"); - } - - dispatcher->pushContext(context2->context); - context2->interrupted = true; - } - } - - stopped = true; } diff --git a/src/Platform/OSX/System/Timer.h b/src/Platform/OSX/System/Timer.h index 1269281c45..a9940114ab 100755 --- a/src/Platform/OSX/System/Timer.h +++ b/src/Platform/OSX/System/Timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Platform/OSX/System/asm.s b/src/Platform/OSX/System/asm.s new file mode 100644 index 0000000000..968d564ed6 --- /dev/null +++ b/src/Platform/OSX/System/asm.s @@ -0,0 +1,47 @@ +.globl _setmcontext +_setmcontext: + movq 16(%rdi), %rsi + movq 24(%rdi), %rdx + movq 32(%rdi), %rcx + movq 40(%rdi), %r8 + movq 48(%rdi), %r9 + movq 56(%rdi), %rax + movq 64(%rdi), %rbx + movq 72(%rdi), %rbp + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + movq 184(%rdi), %rsp + pushq 160(%rdi) /* new %eip */ + movq 8(%rdi), %rdi + ret + +.globl _getmcontext +_getmcontext: + movq %rdi, 8(%rdi) + movq %rsi, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rcx, 32(%rdi) + movq %r8, 40(%rdi) + movq %r9, 48(%rdi) + movq $1, 56(%rdi) /* %rax */ + movq %rbx, 64(%rdi) + movq %rbp, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + + movq (%rsp), %rcx /* %rip */ + movq %rcx, 160(%rdi) + leaq 8(%rsp), %rcx /* %rsp */ + movq %rcx, 184(%rdi) + + movq 32(%rdi), %rcx /* restore %rcx */ + movq $0, %rax + ret \ No newline at end of file diff --git a/src/Platform/OSX/System/context.c b/src/Platform/OSX/System/context.c new file mode 100644 index 0000000000..0baea93505 --- /dev/null +++ b/src/Platform/OSX/System/context.c @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include "context.h" + +void +makecontext(uctx *ucp, void (*func)(void), intptr_t arg) +{ + long *sp; + + memset(&ucp->uc_mcontext, 0, sizeof ucp->uc_mcontext); + ucp->uc_mcontext.mc_rdi = (long)arg; + sp = (long*)ucp->uc_stack.ss_sp+ucp->uc_stack.ss_size/sizeof(long); + sp -= 1; + sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ + *--sp = 0; /* return address */ + ucp->uc_mcontext.mc_rip = (long)func; + ucp->uc_mcontext.mc_rsp = (long)sp; +} + +int +swapcontext(uctx *oucp, const uctx *ucp) +{ + if(getcontext(oucp) == 0) + setcontext(ucp); + return 0; +} diff --git a/src/Platform/OSX/System/context.h b/src/Platform/OSX/System/context.h new file mode 100644 index 0000000000..64bf59c8b2 --- /dev/null +++ b/src/Platform/OSX/System/context.h @@ -0,0 +1,102 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#define setcontext(u) setmcontext(&(u)->uc_mcontext) +#define getcontext(u) getmcontext(&(u)->uc_mcontext) + +#ifdef __cplusplus +extern "C" { +#endif +#include + +typedef struct mcontext mctx; +typedef struct ucontext uctx; + +extern int swapcontext(uctx*, const uctx*); +extern void makecontext(uctx*, void(*)(), intptr_t); +extern int getmcontext(mctx*); +extern void setmcontext(const mctx*); + +struct mcontext { + /* + * The first 20 fields must match the definition of + * sigcontext. So that we can support sigcontext + * and ucontext_t at the same time. + */ + long mc_onstack; /* XXX - sigcontext compat. */ + long mc_rdi; /* machine state (struct trapframe) */ + long mc_rsi; + long mc_rdx; + long mc_rcx; + long mc_r8; + long mc_r9; + long mc_rax; + long mc_rbx; + long mc_rbp; + long mc_r10; + long mc_r11; + long mc_r12; + long mc_r13; + long mc_r14; + long mc_r15; + long mc_trapno; + long mc_addr; + long mc_flags; + long mc_err; + long mc_rip; + long mc_cs; + long mc_rflags; + long mc_rsp; + long mc_ss; + + long mc_len; /* sizeof(mcontext_t) */ +#define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ +#define _MC_FPFMT_XMM 0x10002 + long mc_fpformat; +#define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ +#define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ +#define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ + long mc_ownedfp; + /* + * See for the internals of mc_fpstate[]. + */ + long mc_fpstate[64]; + long mc_spare[8]; +}; + +struct ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + mctx uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int __spare__[8]; +}; + +#ifdef __cplusplus +} +#endif diff --git a/src/Platform/Windows/System/Dispatcher.cpp b/src/Platform/Windows/System/Dispatcher.cpp index 54943a3596..cb23e6a8b6 100755 --- a/src/Platform/Windows/System/Dispatcher.cpp +++ b/src/Platform/Windows/System/Dispatcher.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,117 +17,197 @@ #include "Dispatcher.h" #include -#include +#include +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN -#include +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif #include -using namespace System; +namespace System { namespace { -struct OverlappedExt : public OVERLAPPED { +struct DispatcherContext : public OVERLAPPED { void* context; }; } Dispatcher::Dispatcher() { - completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if (completionPort == NULL) { - std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + static_assert(sizeof(CRITICAL_SECTION) == sizeof(Dispatcher::criticalSection), "CRITICAL_SECTION size doesn't fit sizeof(Dispatcher::criticalSection)"); + BOOL result = InitializeCriticalSectionAndSpinCount(reinterpret_cast(criticalSection), 4000); + assert(result != FALSE); + std::string message; + if (ConvertThreadToFiberEx(NULL, 0) == NULL) { + message = "ConvertThreadToFiberEx failed, result=" + std::to_string(GetLastError()); } else { - if (ConvertThreadToFiberEx(NULL, 0) == NULL) { - std::cerr << "ConvertThreadToFiberEx failed, result=" << GetLastError() << '.' << std::endl; + threadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, GetCurrentThreadId()); + if (threadHandle == NULL) { + message = "OpenThread failed, result=" + std::to_string(GetLastError()); } else { - WSADATA wsaData; - int result = WSAStartup(0x0202, &wsaData); - if (result != 0) { - std::cerr << "WSAStartup failed, result=" << result << '.' << std::endl; + completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (completionPort == NULL) { + message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - contextCount = 0; - return; - } + WSADATA wsaData; + int wsaResult = WSAStartup(0x0202, &wsaData); + if (wsaResult != 0) { + message = "WSAStartup failed, result=" + std::to_string(wsaResult); + } else { + contextCount = 0; + remoteNotificationSent = false; + reinterpret_cast(remoteSpawnOverlapped)->hEvent = NULL; + threadId = GetCurrentThreadId(); + return; + } - if (ConvertFiberToThread() != TRUE) { - std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl; + BOOL result = CloseHandle(completionPort); + assert(result == TRUE); } - } - if (CloseHandle(completionPort) != TRUE) { - std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl; + BOOL result = CloseHandle(threadHandle); + assert(result == TRUE); } - } - throw std::runtime_error("Dispatcher::Dispatcher"); + BOOL result = ConvertFiberToThread(); + assert(result == TRUE); + } + + DeleteCriticalSection(reinterpret_cast(criticalSection)); + throw std::runtime_error("Dispatcher::Dispatcher, " + message); } Dispatcher::~Dispatcher() { assert(resumingContexts.empty()); assert(reusableContexts.size() == contextCount); assert(spawningProcedures.empty()); + assert(GetCurrentThreadId() == threadId); while (!reusableContexts.empty()) { DeleteFiber(reusableContexts.top()); reusableContexts.pop(); } - while (!timers.empty()) { - if (CloseHandle(timers.top()) != TRUE) { - std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl; - } + int wsaResult = WSACleanup(); + assert(wsaResult == 0); + BOOL result = CloseHandle(completionPort); + assert(result == TRUE); + result = CloseHandle(threadHandle); + assert(result == TRUE); + result = ConvertFiberToThread(); + assert(result == TRUE); + DeleteCriticalSection(reinterpret_cast(criticalSection)); +} - timers.pop(); +void Dispatcher::clear() { + assert(GetCurrentThreadId() == threadId); + while (!reusableContexts.empty()) { + DeleteFiber(reusableContexts.top()); + --contextCount; + reusableContexts.pop(); } +} - if (WSACleanup() != 0) { - std::cerr << "WSACleanup failed, result=" << WSAGetLastError() << '.' << std::endl; - } +void Dispatcher::dispatch() { + assert(GetCurrentThreadId() == threadId); + void* context; + for (;;) { + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } - if (ConvertFiberToThread() != TRUE) { - std::cerr << "ConvertFiberToThread failed, result=" << GetLastError() << '.' << std::endl; - } + LARGE_INTEGER frequency; + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + QueryPerformanceFrequency(&frequency); + uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); + auto timerContextPair = timers.begin(); + auto end = timers.end(); + while (timerContextPair != end && timerContextPair->first <= currentTime) { + resumingContexts.push(timerContextPair->second); + timerContextPair = timers.erase(timerContextPair); + } - if (CloseHandle(completionPort) != TRUE) { - std::cerr << "CloseHandle failed, result=" << GetLastError() << '.' << std::endl; - } -} + if (!resumingContexts.empty()) { + context = resumingContexts.front(); + resumingContexts.pop(); + break; + } + + DWORD timeout = timers.empty() ? INFINITE : static_cast(std::min(timers.begin()->first - currentTime, static_cast(INFINITE - 1))); + OVERLAPPED_ENTRY entry; + ULONG actual = 0; + if (GetQueuedCompletionStatusEx(completionPort, &entry, 1, &actual, timeout, TRUE) == TRUE) { + if (entry.lpOverlapped == reinterpret_cast(remoteSpawnOverlapped)) { + EnterCriticalSection(reinterpret_cast(criticalSection)); + assert(remoteNotificationSent); + assert(!remoteSpawningProcedures.empty()); + do { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } while (!remoteSpawningProcedures.empty()); -void* Dispatcher::getCompletionPort() const { - return completionPort; -} + remoteNotificationSent = false; + LeaveCriticalSection(reinterpret_cast(criticalSection)); + continue; + } -void* Dispatcher::getTimer() { - void* timer; - if (timers.empty()) { - timer = CreateWaitableTimer(NULL, FALSE, NULL); - if (timer == NULL) { - std::cerr << "CreateWaitableTimer failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("Dispatcher::getTimer"); + context = reinterpret_cast(entry.lpOverlapped)->context; + break; + } + + DWORD lastError = GetLastError(); + if (lastError == WAIT_TIMEOUT) { + continue; + } + + if (lastError != WAIT_IO_COMPLETION) { + throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); } - } else { - timer = timers.top(); - timers.pop(); } - return timer; + if (context != GetCurrentFiber()) { + SwitchToFiber(context); + } } -void Dispatcher::pushTimer(void* timer) { - timers.push(timer); +void* Dispatcher::getCurrentContext() const { + assert(GetCurrentThreadId() == threadId); + return GetCurrentFiber(); } void Dispatcher::pushContext(void* context) { + assert(GetCurrentThreadId() == threadId); resumingContexts.push(context); } +void Dispatcher::remoteSpawn(std::function&& procedure) { + EnterCriticalSection(reinterpret_cast(criticalSection)); + remoteSpawningProcedures.push(std::move(procedure)); + if (!remoteNotificationSent) { + remoteNotificationSent = true; + if (PostQueuedCompletionStatus(completionPort, 0, 0, reinterpret_cast(remoteSpawnOverlapped)) == NULL) { + LeaveCriticalSection(reinterpret_cast(criticalSection)); + throw std::runtime_error("Dispatcher::remoteSpawn, PostQueuedCompletionStatus failed, result=" + std::to_string(GetLastError())); + }; + } + + LeaveCriticalSection(reinterpret_cast(criticalSection)); +} + void Dispatcher::spawn(std::function&& procedure) { + assert(GetCurrentThreadId() == threadId); void* context; if (reusableContexts.empty()) { - context = CreateFiberEx(16384, 65536, 0, contextProcedureStatic, this); + context = CreateFiberEx(16384, 131072, 0, contextProcedureStatic, this); if (context == NULL) { - std::cerr << "CreateFiberEx failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("Dispatcher::spawn"); + throw std::runtime_error("Dispatcher::spawn, CreateFiberEx failed, result=" + std::to_string(GetLastError())); } + ++contextCount; } else { context = reusableContexts.top(); @@ -138,49 +218,94 @@ void Dispatcher::spawn(std::function&& procedure) { spawningProcedures.emplace(std::move(procedure)); } -void Dispatcher::clear() { -//TODO -} - void Dispatcher::yield() { - void* context; + assert(GetCurrentThreadId() == threadId); for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); - break; + LARGE_INTEGER frequency; + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + QueryPerformanceFrequency(&frequency); + uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); + auto timerContextPair = timers.begin(); + auto end = timers.end(); + while (timerContextPair != end && timerContextPair->first <= currentTime) { + resumingContexts.push(timerContextPair->second); + timerContextPair = timers.erase(timerContextPair); } - OVERLAPPED_ENTRY entry; + OVERLAPPED_ENTRY entries[16]; ULONG actual = 0; - if (GetQueuedCompletionStatusEx(completionPort, &entry, 1, &actual, INFINITE, TRUE) == TRUE) { - context = reinterpret_cast(entry.lpOverlapped)->context; - break; - } + if (GetQueuedCompletionStatusEx(completionPort, entries, 16, &actual, 0, TRUE) == TRUE) { + assert(actual > 0); + for (ULONG i = 0; i < actual; ++i) { + if (entries[i].lpOverlapped == reinterpret_cast(remoteSpawnOverlapped)) { + EnterCriticalSection(reinterpret_cast(criticalSection)); + assert(remoteNotificationSent); + assert(!remoteSpawningProcedures.empty()); + do { + spawn(std::move(remoteSpawningProcedures.front())); + remoteSpawningProcedures.pop(); + } while (!remoteSpawningProcedures.empty()); - DWORD lastError = GetLastError(); - if (lastError != WAIT_IO_COMPLETION) { - std::cerr << "GetQueuedCompletionStatusEx failed, result=" << lastError << '.' << std::endl; - throw std::runtime_error("Dispatcher::yield"); + remoteNotificationSent = false; + LeaveCriticalSection(reinterpret_cast(criticalSection)); + continue; + } + + void* context = reinterpret_cast(entries[i].lpOverlapped)->context; + resumingContexts.push(context); + } + } else { + DWORD lastError = GetLastError(); + if (lastError == WAIT_TIMEOUT) { + break; + } else if (lastError != WAIT_IO_COMPLETION) { + throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); + } } } - if (context != GetCurrentFiber()) { - SwitchToFiber(context); + if (!resumingContexts.empty()) { + resumingContexts.push(GetCurrentFiber()); + dispatch(); + } +} + +void Dispatcher::addTimer(uint64_t time, void* context) { + assert(GetCurrentThreadId() == threadId); + timers.insert(std::make_pair(time, context)); +} + +void* Dispatcher::getCompletionPort() const { + return completionPort; +} + +void Dispatcher::interruptTimer(uint64_t time, void* context) { + assert(GetCurrentThreadId() == threadId); + auto range = timers.equal_range(time); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == context) { + resumingContexts.push(context); + timers.erase(it); + break; + } } } void Dispatcher::contextProcedure() { + assert(GetCurrentThreadId() == threadId); for (;;) { assert(!spawningProcedures.empty()); std::function procedure = std::move(spawningProcedures.front()); spawningProcedures.pop(); procedure(); reusableContexts.push(GetCurrentFiber()); - yield(); + dispatch(); } } void __stdcall Dispatcher::contextProcedureStatic(void* context) { static_cast(context)->contextProcedure(); } + +} diff --git a/src/Platform/Windows/System/Dispatcher.h b/src/Platform/Windows/System/Dispatcher.h index 8df6cd9e0e..1d04809b30 100755 --- a/src/Platform/Windows/System/Dispatcher.h +++ b/src/Platform/Windows/System/Dispatcher.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,9 @@ #pragma once +#include #include +#include #include #include @@ -29,29 +31,32 @@ class Dispatcher { Dispatcher(const Dispatcher&) = delete; ~Dispatcher(); Dispatcher& operator=(const Dispatcher&) = delete; + void clear(); + void dispatch(); + void* getCurrentContext() const; + void pushContext(void* context); + void remoteSpawn(std::function&& procedure); void spawn(std::function&& procedure); void yield(); - void clear(); -private: - friend class Event; - friend class DispatcherAccessor; - friend class TcpConnection; - friend class TcpConnector; - friend class TcpListener; - friend class Timer; + // Platform-specific + void addTimer(uint64_t time, void* context); + void* getCompletionPort() const; + void interruptTimer(uint64_t time, void* context); +private: void* completionPort; std::size_t contextCount; + uint8_t criticalSection[2 * sizeof(long) + 4 * sizeof(void*)]; std::queue resumingContexts; + bool remoteNotificationSent; + std::queue> remoteSpawningProcedures; + uint8_t remoteSpawnOverlapped[4 * sizeof(void*)]; std::stack reusableContexts; std::queue> spawningProcedures; - std::stack timers; - - void* getCompletionPort() const; - void* getTimer(); - void pushTimer(void* timer); - void pushContext(void* context); + void* threadHandle; + uint32_t threadId; + std::multimap timers; void contextProcedure(); static void __stdcall contextProcedureStatic(void* context); diff --git a/src/Platform/Windows/System/Event.cpp b/src/Platform/Windows/System/Event.cpp deleted file mode 100755 index 6187378cc8..0000000000 --- a/src/Platform/Windows/System/Event.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Event.h" -#include -#define WIN32_LEAN_AND_MEAN -#include -#include "Dispatcher.h" - -using namespace System; - -namespace { - -struct Waiter { - Waiter* next; - void* context; -}; - -} - -Event::Event() : dispatcher(nullptr) { -} - -Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { -} - -Event::Event(Event&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { - first = other.first; - if (other.first != nullptr) { - last = other.last; - } - - state = other.state; - other.dispatcher = nullptr; - } -} - -Event::~Event() { - assert(first == nullptr); -} - -Event& Event::operator=(Event&& other) { - assert(first == nullptr); - dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { - first = other.first; - if (other.first != nullptr) { - last = other.last; - } - - state = other.state; - other.dispatcher = nullptr; - } - - return *this; -} - -bool Event::get() const { - assert(dispatcher != nullptr); - return state; -} - -void Event::clear() { - assert(dispatcher != nullptr); - state = false; -} - -void Event::set() { - assert(dispatcher != nullptr); - state = true; - for (Waiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { - dispatcher->pushContext(waiter->context); - } - - first = nullptr; -} - -void Event::wait() { - assert(dispatcher != nullptr); - if (!state) { - Waiter waiter = {nullptr, GetCurrentFiber()}; - if (first != nullptr) { - static_cast(last)->next = &waiter; - } else { - first = &waiter; - } - - last = &waiter; - dispatcher->yield(); - assert(dispatcher != nullptr); - } -} diff --git a/src/Platform/Windows/System/Ipv4Resolver.cpp b/src/Platform/Windows/System/Ipv4Resolver.cpp new file mode 100755 index 0000000000..5d48d6275e --- /dev/null +++ b/src/Platform/Windows/System/Ipv4Resolver.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Ipv4Resolver.h" +#include +#include +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + +namespace System { + +Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { +} + +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +} + +Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + stopped = other.stopped; + other.dispatcher = nullptr; + } +} + +Ipv4Resolver::~Ipv4Resolver() { +} + +Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + stopped = other.stopped; + other.dispatcher = nullptr; + } + + return *this; +} + +void Ipv4Resolver::start() { + assert(dispatcher != nullptr); + assert(stopped); + stopped = false; +} + +void Ipv4Resolver::stop() { + assert(dispatcher != nullptr); + assert(!stopped); + stopped = true; +} + +Ipv4Address Ipv4Resolver::resolve(const std::string& host) { + assert(dispatcher != nullptr); + if (stopped) { + throw InterruptedException(); + } + + addrinfo hints = { 0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL }; + addrinfo* addressInfos; + int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos); + if (result != 0) { + throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result)); + } + + std::size_t count = 0; + for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { + ++count; + } + + std::mt19937 generator{ std::random_device()() }; + std::size_t index = std::uniform_int_distribution(0, count - 1)(generator); + addrinfo* addressInfo = addressInfos; + for (std::size_t i = 0; i < index; ++i) { + addressInfo = addressInfo->ai_next; + } + + Ipv4Address address(ntohl(reinterpret_cast(addressInfo->ai_addr)->sin_addr.S_un.S_addr)); + freeaddrinfo(addressInfo); + return address; +} + +} diff --git a/src/Platform/Windows/System/Ipv4Resolver.h b/src/Platform/Windows/System/Ipv4Resolver.h new file mode 100755 index 0000000000..d59d50e0f2 --- /dev/null +++ b/src/Platform/Windows/System/Ipv4Resolver.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace System { + +class Dispatcher; +class Ipv4Address; + +class Ipv4Resolver { +public: + Ipv4Resolver(); + explicit Ipv4Resolver(Dispatcher& dispatcher); + Ipv4Resolver(const Ipv4Resolver&) = delete; + Ipv4Resolver(Ipv4Resolver&& other); + ~Ipv4Resolver(); + Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; + Ipv4Resolver& operator=(Ipv4Resolver&& other); + void start(); + void stop(); + Ipv4Address resolve(const std::string& host); + +private: + Dispatcher* dispatcher; + bool stopped; +}; + +} diff --git a/src/Platform/Windows/System/TcpConnection.cpp b/src/Platform/Windows/System/TcpConnection.cpp index 0f2ea49a5c..c8836f4213 100755 --- a/src/Platform/Windows/System/TcpConnection.cpp +++ b/src/Platform/Windows/System/TcpConnection.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,68 +17,67 @@ #include "TcpConnection.h" #include -#include +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include -#include "InterruptedException.h" +#include +#include +#include #include "Dispatcher.h" -using namespace System; +namespace System { namespace { -struct OverlappedExt : public OVERLAPPED { +struct TcpConnectionContext : public OVERLAPPED { void* context; bool interrupted; }; -struct Context { - Dispatcher* dispatcher; - OverlappedExt* read; - OverlappedExt* write; -}; - } TcpConnection::TcpConnection() : dispatcher(nullptr) { } -TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), context(nullptr) { -} - - TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { + if (dispatcher != nullptr) { + assert(other.readContext == nullptr); + assert(other.writeContext == nullptr); connection = other.connection; stopped = other.stopped; - context = other.context; + readContext = nullptr; + writeContext = nullptr; other.dispatcher = nullptr; } } TcpConnection::~TcpConnection() { if (dispatcher != nullptr) { - assert(context == nullptr); - if (closesocket(connection) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - } + assert(readContext == nullptr); + assert(writeContext == nullptr); + int result = closesocket(connection); + assert(result == 0); } } TcpConnection& TcpConnection::operator=(TcpConnection&& other) { if (dispatcher != nullptr) { - assert(context == nullptr); + assert(readContext == nullptr); + assert(writeContext == nullptr); if (closesocket(connection) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - throw std::runtime_error("TcpConnection::operator="); + throw std::runtime_error("TcpConnection::operator=, closesocket failed, result=" + std::to_string(WSAGetLastError())); } } dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { + if (dispatcher != nullptr) { + assert(other.readContext == nullptr); + assert(other.writeContext == nullptr); connection = other.connection; stopped = other.stopped; - context = other.context; + readContext = nullptr; + writeContext = nullptr; other.dispatcher = nullptr; } @@ -94,24 +93,31 @@ void TcpConnection::start() { void TcpConnection::stop() { assert(dispatcher != nullptr); assert(!stopped); - if (context != nullptr) { - Context* context2 = static_cast(context); - if (context2->read != nullptr && !context2->read->interrupted) { - if (CancelIoEx(reinterpret_cast(connection), context2->read) != TRUE) { - std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("TcpConnection::stop"); + if (readContext != nullptr) { + TcpConnectionContext* context = static_cast(readContext); + if (!context->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } } - context2->read->interrupted = true; + context->interrupted = true; } + } - if (context2->write != nullptr && !context2->write->interrupted) { - if (CancelIoEx(reinterpret_cast(connection), context2->write) != TRUE) { - std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("TcpConnection::stop"); + if (writeContext != nullptr) { + TcpConnectionContext* context = static_cast(writeContext); + if (!context->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } } - context2->write->interrupted = true; + context->interrupted = true; } } @@ -120,54 +126,40 @@ void TcpConnection::stop() { size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); - assert(context == nullptr || static_cast(context)->read == nullptr); + assert(readContext == nullptr); if (stopped) { throw InterruptedException(); } WSABUF buf{static_cast(size), reinterpret_cast(data)}; DWORD flags = 0; - OverlappedExt overlapped; - overlapped.hEvent = NULL; - if (WSARecv(connection, &buf, 1, NULL, &flags, &overlapped, NULL) != 0) { + TcpConnectionContext context; + context.hEvent = NULL; + if (WSARecv(connection, &buf, 1, NULL, &flags, &context, NULL) != 0) { int lastError = WSAGetLastError(); if (lastError != WSA_IO_PENDING) { - std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl; - throw std::runtime_error("TcpConnection::read"); + throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError)); } } assert(flags == 0); - Context context2; - if (context == nullptr) { - context2.dispatcher = dispatcher; - context2.write = nullptr; - context = &context2; - } - - overlapped.context = GetCurrentFiber(); - overlapped.interrupted = false; - static_cast(context)->read = &overlapped; - dispatcher->yield(); + context.context = GetCurrentFiber(); + context.interrupted = false; + readContext = &context; + dispatcher->dispatch(); + assert(context.context == GetCurrentFiber()); assert(dispatcher != nullptr); - assert(overlapped.context == GetCurrentFiber()); - assert(static_cast(context)->read == &overlapped); - if (static_cast(context)->write != nullptr) { - static_cast(context)->read = nullptr; - } else { - context = nullptr; - } - + assert(readContext == &context); + readContext = nullptr; DWORD transferred; - if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) { + if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); - if (lastError == ERROR_OPERATION_ABORTED) { - assert(overlapped.interrupted); - throw InterruptedException(); + if (lastError != ERROR_OPERATION_ABORTED) { + throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError)); } - std::cerr << "WSARecv failed, result=" << lastError << '.' << std::endl; - throw std::runtime_error("TcpConnection::read"); + assert(context.interrupted); + throw InterruptedException(); } assert(transferred <= size); @@ -175,66 +167,68 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { return transferred; } -void TcpConnection::write(const uint8_t* data, size_t size) { +std::size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); - assert(context == nullptr || static_cast(context)->write == nullptr); + assert(writeContext == nullptr); if (stopped) { throw InterruptedException(); } if (size == 0) { if (shutdown(connection, SD_SEND) != 0) { - std::cerr << "shutdown failed, result=" << WSAGetLastError() << '.' << std::endl; - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(WSAGetLastError())); } - return; + return 0; } WSABUF buf{static_cast(size), reinterpret_cast(const_cast(data))}; - OverlappedExt overlapped; - overlapped.hEvent = NULL; - if (WSASend(connection, &buf, 1, NULL, 0, &overlapped, NULL) != 0) { + TcpConnectionContext context; + context.hEvent = NULL; + if (WSASend(connection, &buf, 1, NULL, 0, &context, NULL) != 0) { int lastError = WSAGetLastError(); if (lastError != WSA_IO_PENDING) { - std::cerr << "WSASend failed, result=" << lastError << '.' << std::endl; - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError)); } } - Context context2; - if (context == nullptr) { - context2.dispatcher = dispatcher; - context2.read = nullptr; - context = &context2; - } - - overlapped.context = GetCurrentFiber(); - overlapped.interrupted = false; - static_cast(context)->write = &overlapped; - dispatcher->yield(); + context.context = GetCurrentFiber(); + context.interrupted = false; + writeContext = &context; + dispatcher->dispatch(); + assert(context.context == GetCurrentFiber()); assert(dispatcher != nullptr); - assert(overlapped.context == GetCurrentFiber()); - assert(static_cast(context)->write == &overlapped); - if (static_cast(context)->read != nullptr) { - static_cast(context)->write = nullptr; - } else { - context = nullptr; - } - + assert(writeContext == &context); + writeContext = nullptr; DWORD transferred; DWORD flags; - if (WSAGetOverlappedResult(connection, &overlapped, &transferred, FALSE, &flags) != TRUE) { + if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); - if (lastError == ERROR_OPERATION_ABORTED) { - assert(overlapped.interrupted); - throw InterruptedException(); + if (lastError != ERROR_OPERATION_ABORTED) { + throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError)); } - std::cerr << "WSSend failed, result=" << lastError << '.' << std::endl; - throw std::runtime_error("TcpConnection::write"); + assert(context.interrupted); + throw InterruptedException(); } assert(transferred == size); assert(flags == 0); + return transferred; +} + +std::pair TcpConnection::getPeerAddressAndPort() { + sockaddr_in address; + int size = sizeof(address); + if (getpeername(connection, reinterpret_cast(&address), &size) != 0) { + throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(WSAGetLastError())); + } + + assert(size == sizeof(sockaddr_in)); + return std::make_pair(Ipv4Address(htonl(address.sin_addr.S_un.S_addr)), htons(address.sin_port)); +} + +TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), readContext(nullptr), writeContext(nullptr) { +} + } diff --git a/src/Platform/Windows/System/TcpConnection.h b/src/Platform/Windows/System/TcpConnection.h index 1cf24221ae..cf79c3b96e 100755 --- a/src/Platform/Windows/System/TcpConnection.h +++ b/src/Platform/Windows/System/TcpConnection.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,12 +17,13 @@ #pragma once -#include #include +#include namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection { public: @@ -35,18 +36,20 @@ class TcpConnection { void start(); void stop(); std::size_t read(uint8_t* data, std::size_t size); - void write(const uint8_t* data, std::size_t size); + std::size_t write(const uint8_t* data, std::size_t size); + std::pair getPeerAddressAndPort(); private: friend class TcpConnector; friend class TcpListener; - explicit TcpConnection(Dispatcher& dispatcher, std::size_t connection); - Dispatcher* dispatcher; std::size_t connection; bool stopped; - void* context; + void* readContext; + void* writeContext; + + TcpConnection(Dispatcher& dispatcher, std::size_t connection); }; } diff --git a/src/Platform/Windows/System/TcpConnector.cpp b/src/Platform/Windows/System/TcpConnector.cpp index fb698341e9..dc4e810445 100755 --- a/src/Platform/Windows/System/TcpConnector.cpp +++ b/src/Platform/Windows/System/TcpConnector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,22 +17,21 @@ #include "TcpConnector.h" #include -#include -#include -#include +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include -#include #include -#include "InterruptedException.h" +#include +#include #include "Dispatcher.h" #include "TcpConnection.h" -using namespace System; +namespace System { namespace { -struct Context : public OVERLAPPED { +struct TcpConnectorContext : public OVERLAPPED { void* context; std::size_t connection; bool interrupted; @@ -45,29 +44,29 @@ LPFN_CONNECTEX connectEx = nullptr; TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher), address(address), port(port), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { - address = other.address; - port = other.port; + if (dispatcher != nullptr) { + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } TcpConnector::~TcpConnector() { + assert(dispatcher == nullptr || context == nullptr); } TcpConnector& TcpConnector::operator=(TcpConnector&& other) { + assert(dispatcher == nullptr || context == nullptr); dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { - address = other.address; - port = other.port; + if (dispatcher != nullptr) { + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -84,11 +83,13 @@ void TcpConnector::stop() { assert(dispatcher != nullptr); assert(!stopped); if (context != nullptr) { - Context* context2 = static_cast(context); + TcpConnectorContext* context2 = static_cast(context); if (!context2->interrupted) { if (CancelIoEx(reinterpret_cast(context2->connection), context2) != TRUE) { - std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("TcpConnector::stop"); + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } } context2->interrupted = true; @@ -98,94 +99,78 @@ void TcpConnector::stop() { stopped = true; } -TcpConnection TcpConnector::connect() { +TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); if (stopped) { throw InterruptedException(); } - std::ostringstream portStream; - portStream << port; - addrinfo hints = {0, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; - addrinfo* addressInfos; - int result = getaddrinfo(address.c_str(), portStream.str().c_str(), &hints, &addressInfos); - if (result != 0) { - std::cerr << "getaddrinfo failed, result=" << result << '.' << std::endl; + std::string message; + SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (connection == INVALID_SOCKET) { + message = "socket failed, result=" + std::to_string(WSAGetLastError()); } else { - std::size_t count = 0; - for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { - ++count; - } - - std::random_device randomDevice; - std::mt19937 generator(randomDevice()); - std::uniform_int_distribution distribution(0, count - 1); - std::size_t index = distribution(generator); - addrinfo* addressInfo = addressInfos; - for (std::size_t i = 0; i < index; ++i) { - addressInfo = addressInfo->ai_next; - } - - sockaddr_in addressData = *reinterpret_cast(addressInfo->ai_addr); - freeaddrinfo(addressInfo); - SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (connection == INVALID_SOCKET) { - std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl; + sockaddr_in bindAddress; + bindAddress.sin_family = AF_INET; + bindAddress.sin_port = 0; + bindAddress.sin_addr.s_addr = INADDR_ANY; + if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { + message = "bind failed, result=" + std::to_string(WSAGetLastError()); } else { - sockaddr_in bindAddress; - bindAddress.sin_family = AF_INET; - bindAddress.sin_port = 0; - bindAddress.sin_addr.s_addr = INADDR_ANY; - if (bind(connection, reinterpret_cast(&bindAddress), sizeof bindAddress) != 0) { - std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl; + GUID guidConnectEx = WSAID_CONNECTEX; + DWORD read = sizeof connectEx; + if (connectEx == nullptr && WSAIoctl(connection, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof guidConnectEx, &connectEx, sizeof connectEx, &read, NULL, NULL) != 0) { + message = "WSAIoctl failed, result=" + std::to_string(WSAGetLastError()); } else { - GUID guidConnectEx = WSAID_CONNECTEX; - DWORD read = sizeof connectEx; - if (connectEx == nullptr && WSAIoctl(connection, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof guidConnectEx, &connectEx, sizeof connectEx, &read, NULL, NULL) != 0) { - std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl; + assert(read == sizeof connectEx); + if (CreateIoCompletionPort(reinterpret_cast(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) { + message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - assert(read == sizeof connectEx); - if (CreateIoCompletionPort(reinterpret_cast(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) { - std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + sockaddr_in addressData; + addressData.sin_family = AF_INET; + addressData.sin_port = htons(port); + addressData.sin_addr.S_un.S_addr = htonl(address.getValue()); + TcpConnectorContext context2; + context2.hEvent = NULL; + if (connectEx(connection, reinterpret_cast(&addressData), sizeof addressData, NULL, 0, NULL, &context2) == TRUE) { + message = "ConnectEx returned immediately, which is not supported."; } else { - addressData.sin_port = htons(port); - Context context2; - context2.hEvent = NULL; - if (connectEx(connection, reinterpret_cast(&addressData), sizeof addressData, NULL, 0, NULL, &context2) == TRUE) { - std::cerr << "ConnectEx returned immediately, which is not supported." << std::endl; + int lastError = WSAGetLastError(); + if (lastError != WSA_IO_PENDING) { + message = "ConnectEx failed, result=" + std::to_string(lastError); } else { - int lastError = WSAGetLastError(); - if (lastError != WSA_IO_PENDING) { - std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl; - } else { - context2.context = GetCurrentFiber(); - context2.connection = connection; - context2.interrupted = false; - context = &context2; - dispatcher->yield(); - assert(dispatcher != nullptr); - assert(context2.context == GetCurrentFiber()); - assert(context2.connection == connection); - assert(context == &context2); - context = nullptr; - DWORD transferred; - DWORD flags; - if (WSAGetOverlappedResult(connection, &context2, &transferred, FALSE, &flags) != TRUE) { - lastError = WSAGetLastError(); - if (lastError == ERROR_OPERATION_ABORTED) { - assert(context2.interrupted); - if (closesocket(connection) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - } - + context2.context = GetCurrentFiber(); + context2.connection = connection; + context2.interrupted = false; + context = &context2; + dispatcher->dispatch(); + assert(context2.context == GetCurrentFiber()); + assert(context2.connection == connection); + assert(dispatcher != nullptr); + assert(context == &context2); + context = nullptr; + DWORD transferred; + DWORD flags; + if (WSAGetOverlappedResult(connection, &context2, &transferred, FALSE, &flags) != TRUE) { + lastError = WSAGetLastError(); + if (lastError != ERROR_OPERATION_ABORTED) { + message = "ConnectEx failed, result=" + std::to_string(lastError); + } else { + assert(context2.interrupted); + if (closesocket(connection) != 0) { + throw std::runtime_error("TcpConnector::connect, closesocket failed, result=" + std::to_string(WSAGetLastError())); + } else { throw InterruptedException(); } - - std::cerr << "ConnectEx failed, result=" << lastError << '.' << std::endl; + } + } else { + assert(transferred == 0); + assert(flags == 0); + DWORD value = 1; + if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, reinterpret_cast(&value), sizeof(value)) != 0) { + message = "setsockopt failed, result=" + std::to_string(WSAGetLastError()); } else { - assert(transferred == 0); - assert(flags == 0); return TcpConnection(*dispatcher, connection); } } @@ -193,12 +178,13 @@ TcpConnection TcpConnector::connect() { } } } - - if (closesocket(connection) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - } } + + int result = closesocket(connection); + assert(result == 0); } - throw std::runtime_error("TcpConnector::connect"); + throw std::runtime_error("TcpConnector::connect, " + message); +} + } diff --git a/src/Platform/Windows/System/TcpConnector.h b/src/Platform/Windows/System/TcpConnector.h index 1f64b2d264..562ddfedc6 100755 --- a/src/Platform/Windows/System/TcpConnector.h +++ b/src/Platform/Windows/System/TcpConnector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,12 +23,13 @@ namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection; class TcpConnector { public: TcpConnector(); - TcpConnector(Dispatcher& dispatcher, const std::string& address, uint16_t port); + explicit TcpConnector(Dispatcher& dispatcher); TcpConnector(const TcpConnector&) = delete; TcpConnector(TcpConnector&& other); ~TcpConnector(); @@ -36,12 +37,10 @@ class TcpConnector { TcpConnector& operator=(TcpConnector&& other); void start(); void stop(); - TcpConnection connect(); + TcpConnection connect(const Ipv4Address& address, uint16_t port); private: Dispatcher* dispatcher; - std::string address; - uint16_t port; bool stopped; void* context; }; diff --git a/src/Platform/Windows/System/TcpListener.cpp b/src/Platform/Windows/System/TcpListener.cpp index 020c34a460..d64b3106b0 100755 --- a/src/Platform/Windows/System/TcpListener.cpp +++ b/src/Platform/Windows/System/TcpListener.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,81 +17,76 @@ #include "TcpListener.h" #include -#include +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include #include -#include "InterruptedException.h" +#include +#include #include "Dispatcher.h" #include "TcpConnection.h" -using namespace System; +namespace System { namespace { -struct Context : public OVERLAPPED { +struct TcpListenerContext : public OVERLAPPED { void* context; bool interrupted; }; LPFN_ACCEPTEX acceptEx = nullptr; -LPFN_GETACCEPTEXSOCKADDRS getAcceptExSockaddrs = nullptr; } TcpListener::TcpListener() : dispatcher(nullptr) { } -TcpListener::TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port) : dispatcher(&dispatcher) { +TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uint16_t port) : dispatcher(&dispatcher) { + std::string message; listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listener == INVALID_SOCKET) { - std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl; + message = "socket failed, result=" + std::to_string(WSAGetLastError()); } else { - sockaddr_in address; - address.sin_family = AF_INET; - address.sin_port = htons(port); - address.sin_addr.s_addr = INADDR_ANY; - if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { - std::cerr << "bind failed, result=" << WSAGetLastError() << '.' << std::endl; + sockaddr_in addressData; + addressData.sin_family = AF_INET; + addressData.sin_port = htons(port); + addressData.sin_addr.S_un.S_addr = htonl(address.getValue()); + if (bind(listener, reinterpret_cast(&addressData), sizeof(addressData)) != 0) { + message = "bind failed, result=" + std::to_string(WSAGetLastError()); } else if (listen(listener, SOMAXCONN) != 0) { - std::cerr << "listen failed, result=" << WSAGetLastError() << '.' << std::endl; + message = "listen failed, result=" + std::to_string(WSAGetLastError()); } else { GUID guidAcceptEx = WSAID_ACCEPTEX; DWORD read = sizeof acceptEx; if (acceptEx == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof guidAcceptEx, &acceptEx, sizeof acceptEx, &read, NULL, NULL) != 0) { - std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl; + message = "WSAIoctl failed, result=" + std::to_string(WSAGetLastError()); } else { assert(read == sizeof acceptEx); - GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS; - read = sizeof getAcceptExSockaddrs; - if (getAcceptExSockaddrs == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidGetAcceptExSockaddrs, sizeof guidGetAcceptExSockaddrs, &getAcceptExSockaddrs, sizeof getAcceptExSockaddrs, &read, NULL, NULL) != 0) { - std::cerr << "WSAIoctl failed, result=" << WSAGetLastError() << '.' << std::endl; + if (CreateIoCompletionPort(reinterpret_cast(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) { + message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - assert(read == sizeof getAcceptExSockaddrs); - if (CreateIoCompletionPort(reinterpret_cast(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) { - std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; - } else { - stopped = false; - context = nullptr; - return; - } + stopped = false; + context = nullptr; + return; } } } - if (closesocket(listener) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - } + int result = closesocket(listener); + assert(result == 0); } - throw std::runtime_error("TcpListener::TcpListener"); + throw std::runtime_error("TcpListener::TcpListener, " + message); } TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { + if (dispatcher != nullptr) { + assert(other.context == nullptr); listener = other.listener; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } @@ -99,9 +94,8 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { TcpListener::~TcpListener() { if (dispatcher != nullptr) { assert(context == nullptr); - if (closesocket(listener) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - } + int result = closesocket(listener); + assert(result == 0); } } @@ -109,16 +103,16 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (dispatcher != nullptr) { assert(context == nullptr); if (closesocket(listener) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - throw std::runtime_error("TcpListener::operator="); + throw std::runtime_error("TcpListener::operator=, closesocket failed, result=" + std::to_string(WSAGetLastError())); } } dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { + if (dispatcher != nullptr) { + assert(other.context == nullptr); listener = other.listener; stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -135,11 +129,13 @@ void TcpListener::stop() { assert(dispatcher != nullptr); assert(!stopped); if (context != nullptr) { - Context* context2 = static_cast(context); + TcpListenerContext* context2 = static_cast(context); if (!context2->interrupted) { if (CancelIoEx(reinterpret_cast(listener), context2) != TRUE) { - std::cerr << "CancelIoEx failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("TcpListener::stop"); + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpListener::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } } context2->interrupted = true; @@ -156,60 +152,53 @@ TcpConnection TcpListener::accept() { throw InterruptedException(); } + std::string message; SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (connection == INVALID_SOCKET) { - std::cerr << "socket failed, result=" << WSAGetLastError() << '.' << std::endl; + message = "socket failed, result=" + std::to_string(WSAGetLastError()); } else { uint8_t addresses[sizeof sockaddr_in * 2 + 32]; DWORD received; - Context context2; + TcpListenerContext context2; context2.hEvent = NULL; if (acceptEx(listener, connection, addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, &received, &context2) == TRUE) { - std::cerr << "AcceptEx returned immediately, which is not supported." << std::endl; + message = "AcceptEx returned immediately, which is not supported."; } else { int lastError = WSAGetLastError(); if (lastError != WSA_IO_PENDING) { - std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl; + message = "AcceptEx failed, result=" + std::to_string(lastError); } else { context2.context = GetCurrentFiber(); context2.interrupted = false; context = &context2; - dispatcher->yield(); - assert(dispatcher != nullptr); + dispatcher->dispatch(); assert(context2.context == GetCurrentFiber()); + assert(dispatcher != nullptr); assert(context == &context2); context = nullptr; DWORD transferred; DWORD flags; if (WSAGetOverlappedResult(listener, &context2, &transferred, FALSE, &flags) != TRUE) { lastError = WSAGetLastError(); - if (lastError == ERROR_OPERATION_ABORTED) { + if (lastError != ERROR_OPERATION_ABORTED) { + message = "AcceptEx failed, result=" + std::to_string(lastError); + } else { assert(context2.interrupted); if (closesocket(connection) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; + throw std::runtime_error("TcpListener::accept, closesocket failed, result=" + std::to_string(WSAGetLastError())); + } else { + throw InterruptedException(); } - - throw InterruptedException(); } - - std::cerr << "AcceptEx failed, result=" << lastError << '.' << std::endl; } else { assert(transferred == 0); assert(flags == 0); if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast(&listener), sizeof listener) != 0) { - std::cerr << "setsockopt failed, result=" << WSAGetLastError() << '.' << std::endl; + message = "setsockopt failed, result=" + std::to_string(WSAGetLastError()); } else { if (CreateIoCompletionPort(reinterpret_cast(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) { - std::cerr << "CreateIoCompletionPort failed, result=" << GetLastError() << '.' << std::endl; + message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - //sockaddr_in* local; - //int localSize; - //sockaddr_in* remote; - //int remoteSize; - //static_cast(getAcceptExSockaddrs)(addresses, 0, sizeof(sockaddr_in) + 16, sizeof(sockaddr_in) + 16, reinterpret_cast(&local), &localSize, reinterpret_cast(&remote), &remoteSize); - //assert(localSize == sizeof sockaddr_in); - //assert(remoteSize == sizeof sockaddr_in); - //std::cout << "Client connected from " << static_cast(remote->sin_addr.S_un.S_un_b.s_b1) << '.' << static_cast(remote->sin_addr.S_un.S_un_b.s_b2) << '.' << static_cast(remote->sin_addr.S_un.S_un_b.s_b3) << '.' << static_cast(remote->sin_addr.S_un.S_un_b.s_b4) << ':' << remote->sin_port << std::endl; return TcpConnection(*dispatcher, connection); } } @@ -217,10 +206,11 @@ TcpConnection TcpListener::accept() { } } - if (closesocket(connection) != 0) { - std::cerr << "closesocket failed, result=" << WSAGetLastError() << '.' << std::endl; - } + int result = closesocket(connection); + assert(result == 0); } - throw std::runtime_error("TcpListener::accept"); + throw std::runtime_error("TcpListener::accept, " + message); +} + } diff --git a/src/Platform/Windows/System/TcpListener.h b/src/Platform/Windows/System/TcpListener.h index e48af44424..3ec2f65bfb 100755 --- a/src/Platform/Windows/System/TcpListener.h +++ b/src/Platform/Windows/System/TcpListener.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,12 +23,13 @@ namespace System { class Dispatcher; +class Ipv4Address; class TcpConnection; class TcpListener { public: TcpListener(); - TcpListener(Dispatcher& dispatcher, const std::string& address, uint16_t port); + TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uint16_t port); TcpListener(const TcpListener&) = delete; TcpListener(TcpListener&& other); ~TcpListener(); diff --git a/src/Platform/Windows/System/Timer.cpp b/src/Platform/Windows/System/Timer.cpp index f8fbed2add..66ede19f24 100755 --- a/src/Platform/Windows/System/Timer.cpp +++ b/src/Platform/Windows/System/Timer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,76 +17,52 @@ #include "Timer.h" #include -#include +#include +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif #include -#include "InterruptedException.h" +#include #include "Dispatcher.h" -using namespace System; - namespace System { -class DispatcherAccessor { -public: - DispatcherAccessor(Dispatcher* dispatcher, void* context) { - dispatcher->pushContext(context); - } -}; - -} - namespace { -struct Context { - Dispatcher* dispatcher; +struct TimerContext { + uint64_t time; void* context; bool interrupted; }; -void __stdcall callbackProcedure(void* lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { - Context* context = static_cast(lpArgToCompletionRoutine); - assert(context->context != nullptr); - DispatcherAccessor(context->dispatcher, context->context); - context->context = nullptr; -} - } Timer::Timer() : dispatcher(nullptr) { } Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { - timer = dispatcher.getTimer(); } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { - timer = other.timer; + if (dispatcher != nullptr) { + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } } Timer::~Timer() { - if (dispatcher != nullptr) { - assert(context == nullptr); - dispatcher->pushTimer(timer); - } + assert(dispatcher == nullptr || context == nullptr); } Timer& Timer::operator=(Timer&& other) { - if (dispatcher != nullptr) { - assert(context == nullptr); - dispatcher->pushTimer(timer); - } - + assert(dispatcher == nullptr || context == nullptr); dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { - timer = other.timer; + if (dispatcher != nullptr) { + assert(other.context == nullptr); stopped = other.stopped; - context = other.context; + context = nullptr; other.dispatcher = nullptr; } @@ -103,44 +79,41 @@ void Timer::stop() { assert(dispatcher != nullptr); assert(!stopped); if (context != nullptr) { - Context* context2 = static_cast(context); - if (context2->context != nullptr) { - if (CancelWaitableTimer(timer) != TRUE) { - std::cerr << "CancelWaitableTimer failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("Timer::stop"); - } - - dispatcher->pushContext(context2->context); - context2->context = nullptr; - context2->interrupted = true; + TimerContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + dispatcher->interruptTimer(timerContext->time, timerContext->context); + timerContext->interrupted = true; } } stopped = true; } -void Timer::sleep(std::chrono::nanoseconds duration) { +void Timer::sleep(std::chrono::milliseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); if (stopped) { throw InterruptedException(); } - LARGE_INTEGER duration2; - duration2.QuadPart = static_cast(duration.count() / -100); - Context context2 = {dispatcher, GetCurrentFiber(), false}; - if (SetWaitableTimer(timer, &duration2, 0, callbackProcedure, &context2, FALSE) != TRUE) { - std::cerr << "SetWaitableTimer failed, result=" << GetLastError() << '.' << std::endl; - throw std::runtime_error("Timer::sleep"); - } - - context = &context2; - dispatcher->yield(); + LARGE_INTEGER frequency; + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + QueryPerformanceFrequency(&frequency); + uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); + uint64_t time = currentTime + duration.count(); + void* fiber = GetCurrentFiber(); + TimerContext timerContext{ time, fiber, false }; + context = &timerContext; + dispatcher->addTimer(time, fiber); + dispatcher->dispatch(); + assert(timerContext.context == GetCurrentFiber()); assert(dispatcher != nullptr); - assert(context2.context == nullptr); - assert(context == &context2); + assert(context == &timerContext); context = nullptr; - if (context2.interrupted) { + if (timerContext.interrupted) { throw InterruptedException(); } } + +} diff --git a/src/Platform/Windows/System/Timer.h b/src/Platform/Windows/System/Timer.h index 78af395f34..ed2ab62a5f 100755 --- a/src/Platform/Windows/System/Timer.h +++ b/src/Platform/Windows/System/Timer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -34,11 +34,10 @@ class Timer { Timer& operator=(Timer&& other); void start(); void stop(); - void sleep(std::chrono::nanoseconds duration); + void sleep(std::chrono::milliseconds duration); private: Dispatcher* dispatcher; - void* timer; bool stopped; void* context; }; diff --git a/src/platform/mingw/alloca.h b/src/Platform/mingw/alloca.h similarity index 92% rename from src/platform/mingw/alloca.h rename to src/Platform/mingw/alloca.h index 9e05e581e0..da935abf3d 100644 --- a/src/platform/mingw/alloca.h +++ b/src/Platform/mingw/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/platform/msc/alloca.h b/src/Platform/msc/alloca.h similarity index 89% rename from src/platform/msc/alloca.h rename to src/Platform/msc/alloca.h index 3c05f1c764..c182487427 100644 --- a/src/platform/msc/alloca.h +++ b/src/Platform/msc/alloca.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,4 +17,6 @@ #pragma once +#ifndef __cplusplus #define alloca(size) _alloca(size) +#endif diff --git a/src/platform/msc/stdbool.h b/src/Platform/msc/stdbool.h similarity index 92% rename from src/platform/msc/stdbool.h rename to src/Platform/msc/stdbool.h index f98edb352c..e8bc6aed77 100644 --- a/src/platform/msc/stdbool.h +++ b/src/Platform/msc/stdbool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/platform/msc/sys/param.h b/src/Platform/msc/sys/param.h similarity index 92% rename from src/platform/msc/sys/param.h rename to src/Platform/msc/sys/param.h index 9bf3a26f40..dc1fc4d776 100644 --- a/src/platform/msc/sys/param.h +++ b/src/Platform/msc/sys/param.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Platform/Linux/System/Event.cpp b/src/System/Event.cpp similarity index 61% rename from src/Platform/Linux/System/Event.cpp rename to src/System/Event.cpp index 008bb332b1..809593e6e2 100755 --- a/src/Platform/Linux/System/Event.cpp +++ b/src/System/Event.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,15 +17,14 @@ #include "Event.h" #include -#include -#include "Dispatcher.h" +#include -using namespace System; +namespace System { namespace { -struct Waiter { - Waiter* next; +struct EventWaiter { + EventWaiter* next; void* context; }; @@ -34,35 +33,35 @@ struct Waiter { Event::Event() : dispatcher(nullptr) { } -Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), first(nullptr), state(false) { +Event::Event(Dispatcher& dispatcher) : dispatcher(&dispatcher), state(false), first(nullptr) { } Event::Event(Event&& other) : dispatcher(other.dispatcher) { - if (other.dispatcher != nullptr) { - first = other.first; - if (other.first != nullptr) { - last = other.last; + if (dispatcher != nullptr) { + state = other.state; + if (!state) { + assert(other.first == nullptr); + first = nullptr; } - state = other.state; other.dispatcher = nullptr; } } Event::~Event() { - assert(first == nullptr); + assert(dispatcher == nullptr || state || first == nullptr); } Event& Event::operator=(Event&& other) { - assert(first == nullptr); + assert(dispatcher == nullptr || state || first == nullptr); dispatcher = other.dispatcher; - if (other.dispatcher != nullptr) { - first = other.first; - if (other.first != nullptr) { - last = other.last; + if (dispatcher != nullptr) { + state = other.state; + if (!state) { + assert(other.first == nullptr); + first = nullptr; } - state = other.state; other.dispatcher = nullptr; } @@ -76,31 +75,37 @@ bool Event::get() const { void Event::clear() { assert(dispatcher != nullptr); - state = false; + if (state) { + state = false; + first = nullptr; + } } void Event::set() { assert(dispatcher != nullptr); - state = true; - for (Waiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { - dispatcher->pushContext(waiter->context); + if (!state) { + state = true; + for (EventWaiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + dispatcher->pushContext(waiter->context); + } } - - first = nullptr; } void Event::wait() { assert(dispatcher != nullptr); if (!state) { - Waiter waiter = { nullptr, dispatcher->getCurrentContext() }; + EventWaiter waiter = {nullptr, dispatcher->getCurrentContext()}; if (first != nullptr) { - static_cast(last)->next = &waiter; + static_cast(last)->next = &waiter; } else { first = &waiter; } last = &waiter; - dispatcher->yield(); + dispatcher->dispatch(); + assert(waiter.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); } } + +} diff --git a/src/Platform/OSX/System/Event.h b/src/System/Event.h similarity index 94% rename from src/Platform/OSX/System/Event.h rename to src/System/Event.h index aab4d1a4f2..23cebfd2a1 100755 --- a/src/Platform/OSX/System/Event.h +++ b/src/System/Event.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -37,9 +37,9 @@ class Event { private: Dispatcher* dispatcher; + bool state; void* first; void* last; - bool state; }; } diff --git a/src/System/EventLock.cpp b/src/System/EventLock.cpp new file mode 100644 index 0000000000..22c66764ba --- /dev/null +++ b/src/System/EventLock.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "EventLock.h" +#include + +namespace System { + +EventLock::EventLock(Event& event) : event(event) { + while (!event.get()) { + event.wait(); + } + + event.clear(); +} + +EventLock::~EventLock() { + event.set(); +} + +} diff --git a/src/Platform/OSX/System/InterruptedException.h b/src/System/EventLock.h old mode 100755 new mode 100644 similarity index 80% rename from src/Platform/OSX/System/InterruptedException.h rename to src/System/EventLock.h index aa85c8bd03..e44532bbd3 --- a/src/Platform/OSX/System/InterruptedException.h +++ b/src/System/EventLock.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,11 +17,17 @@ #pragma once -#include - namespace System { -class InterruptedException : public std::exception { +class Event; + +class EventLock { +public: + explicit EventLock(Event& event); + ~EventLock(); + +private: + Event& event; }; } diff --git a/src/Platform/OSX/System/InterruptedException.cpp b/src/System/InterruptedException.cpp similarity index 92% rename from src/Platform/OSX/System/InterruptedException.cpp rename to src/System/InterruptedException.cpp index 0e268f4f6f..4456177f39 100755 --- a/src/Platform/OSX/System/InterruptedException.cpp +++ b/src/System/InterruptedException.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/Platform/Windows/System/InterruptedException.h b/src/System/InterruptedException.h similarity index 92% rename from src/Platform/Windows/System/InterruptedException.h rename to src/System/InterruptedException.h index aa85c8bd03..81a34e0db7 100755 --- a/src/Platform/Windows/System/InterruptedException.h +++ b/src/System/InterruptedException.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/System/Ipv4Address.cpp b/src/System/Ipv4Address.cpp new file mode 100755 index 0000000000..719a6d9244 --- /dev/null +++ b/src/System/Ipv4Address.cpp @@ -0,0 +1,125 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Ipv4Address.h" +#include + +namespace System { + +namespace { + +uint8_t readUint8(const std::string& source, std::size_t& offset) { + if (offset == source.size() || source[offset] < '0' || source[offset] > '9') { + throw std::runtime_error("Unable to read value from string"); + } + + uint8_t value = source[offset] - '0'; + if (offset + 1 == source.size() || source[offset + 1] < '0' || source[offset + 1] > '9') { + offset = offset + 1; + return value; + } + + if (value == 0) { + throw std::runtime_error("Unable to read value from string"); + } + + value = value * 10 + (source[offset + 1] - '0'); + if (offset + 2 == source.size() || source[offset + 2] < '0' || source[offset + 2] > '9') { + offset = offset + 2; + return value; + } + + if ((value == 25 && source[offset + 2] > '5') || value > 25) { + throw std::runtime_error("Unable to read value from string"); + } + + value = value * 10 + (source[offset + 2] - '0'); + offset = offset + 3; + return value; +} + +} + +Ipv4Address::Ipv4Address(uint32_t value) : value(value) { +} + +Ipv4Address::Ipv4Address(const std::string& dottedDecimal) { + std::size_t offset = 0; + value = readUint8(dottedDecimal, offset); + if (offset == dottedDecimal.size() || dottedDecimal[offset] != '.') { + throw std::runtime_error("Invalid Ipv4 address string"); + } + + ++offset; + value = value << 8 | readUint8(dottedDecimal, offset); + if (offset == dottedDecimal.size() || dottedDecimal[offset] != '.') { + throw std::runtime_error("Invalid Ipv4 address string"); + } + + ++offset; + value = value << 8 | readUint8(dottedDecimal, offset); + if (offset == dottedDecimal.size() || dottedDecimal[offset] != '.') { + throw std::runtime_error("Invalid Ipv4 address string"); + } + + ++offset; + value = value << 8 | readUint8(dottedDecimal, offset); + if (offset < dottedDecimal.size()) { + throw std::runtime_error("Invalid Ipv4 address string"); + } +} + +bool Ipv4Address::operator!=(const Ipv4Address& other) const { + return value != other.value; +} + +bool Ipv4Address::operator==(const Ipv4Address& other) const { + return value == other.value; +} + +uint32_t Ipv4Address::getValue() const { + return value; +} + +std::string Ipv4Address::toDottedDecimal() const { + std::string result; + result += std::to_string(value >> 24); + result += '.'; + result += std::to_string(value >> 16 & 255); + result += '.'; + result += std::to_string(value >> 8 & 255); + result += '.'; + result += std::to_string(value & 255); + return result; +} + +bool Ipv4Address::isLoopback() const { + // 127.0.0.0/8 + return (value & 0xff000000) == (127 << 24); +} + +bool Ipv4Address::isPrivate() const { + return + // 10.0.0.0/8 + (value & 0xff000000) == (10 << 24) || + // 172.16.0.0/12 + (value & 0xfff00000) == ((172 << 24) | (16 << 16)) || + // 192.168.0.0/16 + (value & 0xffff0000) == ((192 << 24) | (168 << 16)); +} + +} diff --git a/src/System/Ipv4Address.h b/src/System/Ipv4Address.h new file mode 100755 index 0000000000..43d9f0e0ad --- /dev/null +++ b/src/System/Ipv4Address.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace System { + +class Ipv4Address { +public: + explicit Ipv4Address(uint32_t value); + explicit Ipv4Address(const std::string& dottedDecimal); + bool operator!=(const Ipv4Address& other) const; + bool operator==(const Ipv4Address& other) const; + uint32_t getValue() const; + bool isLoopback() const; + bool isPrivate() const; + std::string toDottedDecimal() const; + +private: + uint32_t value; +}; + +} diff --git a/src/System/Latch.cpp b/src/System/Latch.cpp new file mode 100755 index 0000000000..edc0e513ca --- /dev/null +++ b/src/System/Latch.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Latch.h" +#include +#include + +namespace System { + +namespace { + +struct LatchWaiter { + LatchWaiter* next; + void* context; +}; + +} + +Latch::Latch() : dispatcher(nullptr) { +} + +Latch::Latch(Dispatcher& dispatcher) : dispatcher(&dispatcher), value(0) { +} + +Latch::Latch(Latch&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + value = other.value; + if (value > 0) { + assert(other.first == nullptr); + first = nullptr; + } + + other.dispatcher = nullptr; + } +} + +Latch::~Latch() { + assert(dispatcher == nullptr || value == 0 || first == nullptr); +} + +Latch& Latch::operator=(Latch&& other) { + assert(dispatcher == nullptr || value == 0 || first == nullptr); + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + value = other.value; + if (value > 0) { + assert(other.first == nullptr); + first = nullptr; + } + + other.dispatcher = nullptr; + } + + return *this; +} + +std::size_t Latch::get() const { + assert(dispatcher != nullptr); + return value; +} + +void Latch::increase(std::size_t value) { + assert(dispatcher != nullptr); + if (value > 0) { + if (this->value == 0) { + first = nullptr; + } + + this->value += value; + } +} + +void Latch::decrease(std::size_t value) { + assert(dispatcher != nullptr); + if (value > 0) { + assert(value <= this->value); + if (this->value > 0) { + this->value -= value; + if (this->value == 0) { + for (LatchWaiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + dispatcher->pushContext(waiter->context); + } + } + } + } +} + +void Latch::wait() { + assert(dispatcher != nullptr); + if (value > 0) { + LatchWaiter waiter = {nullptr, dispatcher->getCurrentContext()}; + if (first != nullptr) { + static_cast(last)->next = &waiter; + } else { + first = &waiter; + } + + last = &waiter; + dispatcher->dispatch(); + assert(waiter.context == dispatcher->getCurrentContext()); + assert(dispatcher != nullptr); + } +} + +} diff --git a/src/Platform/Linux/System/Event.h b/src/System/Latch.h similarity index 66% rename from src/Platform/Linux/System/Event.h rename to src/System/Latch.h index aab4d1a4f2..ebe3755821 100755 --- a/src/Platform/Linux/System/Event.h +++ b/src/System/Latch.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,29 +17,31 @@ #pragma once +#include + namespace System { class Dispatcher; -class Event { +class Latch { public: - Event(); - explicit Event(Dispatcher& dispatcher); - Event(const Event&) = delete; - Event(Event&& other); - ~Event(); - Event& operator=(const Event&) = delete; - Event& operator=(Event&& other); - bool get() const; - void clear(); - void set(); + Latch(); + explicit Latch(Dispatcher& dispatcher); + Latch(const Latch&) = delete; + Latch(Latch&& other); + ~Latch(); + Latch& operator=(const Latch&) = delete; + Latch& operator=(Latch&& other); + std::size_t get() const; + void decrease(std::size_t value = 1); + void increase(std::size_t value = 1); void wait(); private: Dispatcher* dispatcher; + std::size_t value; void* first; void* last; - bool state; }; } diff --git a/src/System/LatchGuard.cpp b/src/System/LatchGuard.cpp new file mode 100644 index 0000000000..15ba0a7d2c --- /dev/null +++ b/src/System/LatchGuard.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LatchGuard.h" + +#include "System/Latch.h" + +namespace System { + +LatchGuard::LatchGuard(Latch& latch) : m_latch(latch) { + m_latch.increase(); +} + +LatchGuard::~LatchGuard() { + m_latch.decrease(); +} + +} diff --git a/src/System/LatchGuard.h b/src/System/LatchGuard.h new file mode 100644 index 0000000000..22ee3a26e2 --- /dev/null +++ b/src/System/LatchGuard.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace System { + +class Latch; + +class LatchGuard { +public: + explicit LatchGuard(Latch& latch); + ~LatchGuard(); + +private: + Latch& m_latch; +}; + +} diff --git a/src/System/TcpStream.cpp b/src/System/TcpStream.cpp index 4502fdf2ba..ed97ecb698 100755 --- a/src/System/TcpStream.cpp +++ b/src/System/TcpStream.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,10 +16,9 @@ // along with Bytecoin. If not, see . #include "TcpStream.h" +#include -#include - -using namespace System; +namespace System { TcpStreambuf::TcpStreambuf(TcpConnection& connection) : connection(connection) { setg(&readBuf.front(), &readBuf.front(), &readBuf.front()); @@ -27,17 +26,38 @@ TcpStreambuf::TcpStreambuf(TcpConnection& connection) : connection(connection) { } TcpStreambuf::~TcpStreambuf() { - dumpBuffer(); + dumpBuffer(true); +} + +std::streambuf::int_type TcpStreambuf::overflow(std::streambuf::int_type ch) { + if (ch == traits_type::eof()) { + return traits_type::eof(); + } + + if (pptr() == epptr()) { + if (!dumpBuffer(false)) { + return traits_type::eof(); + } + } + + *pptr() = static_cast(ch); + pbump(1); + return ch; +} + +int TcpStreambuf::sync() { + return dumpBuffer(true) ? 0 : -1; } std::streambuf::int_type TcpStreambuf::underflow() { - if (gptr() < egptr()) + if (gptr() < egptr()) { return traits_type::to_int_type(*gptr()); + } size_t bytesRead; try { bytesRead = connection.read(reinterpret_cast(&readBuf.front()), readBuf.max_size()); - } catch (std::exception& ex) { + } catch (std::exception&) { return traits_type::eof(); } @@ -46,19 +66,36 @@ std::streambuf::int_type TcpStreambuf::underflow() { } setg(&readBuf.front(), &readBuf.front(), &readBuf.front() + bytesRead); - return traits_type::to_int_type(*gptr()); } -int TcpStreambuf::sync() { - return dumpBuffer() ? 0 : -1; -} - -bool TcpStreambuf::dumpBuffer() { +bool TcpStreambuf::dumpBuffer(bool finalize) { try { size_t count = pptr() - pbase(); - connection.write(&writeBuf.front(), count); - pbump(-count); + if(count == 0) { + return true; + } + + size_t transferred = connection.write(&writeBuf.front(), count); + if(transferred == count) { + pbump(-static_cast(count)); + } else { + if(!finalize) { + size_t front = 0; + for (size_t pos = transferred; pos < count; ++pos, ++front) { + writeBuf[front] = writeBuf[pos]; + } + + pbump(-static_cast(transferred)); + } else { + size_t offset = transferred; + while( offset != count) { + offset += connection.write(&writeBuf.front() + offset, count - offset); + } + + pbump(-static_cast(count)); + } + } } catch (std::exception&) { return false; } @@ -66,19 +103,4 @@ bool TcpStreambuf::dumpBuffer() { return true; } -std::streambuf::int_type TcpStreambuf::overflow(std::streambuf::int_type ch) { - if (ch == traits_type::eof()) { - return traits_type::eof(); - } - - if (pptr() == epptr()) { - if (!dumpBuffer()) { - return traits_type::eof(); - } - } - - *pptr() = ch; - pbump(1); - - return ch; } diff --git a/src/System/TcpStream.h b/src/System/TcpStream.h index 359673b3e5..1196a399c2 100755 --- a/src/System/TcpStream.h +++ b/src/System/TcpStream.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,31 +17,30 @@ #pragma once -#include #include - -#include +#include +#include namespace System { +class TcpConnection; + class TcpStreambuf : public std::streambuf { public: - TcpStreambuf(TcpConnection& connection); + explicit TcpStreambuf(TcpConnection& connection); TcpStreambuf(const TcpStreambuf&) = delete; - - virtual ~TcpStreambuf(); + ~TcpStreambuf(); + TcpStreambuf& operator=(const TcpStreambuf&) = delete; private: - std::streambuf::int_type underflow() override; - std::streambuf::int_type overflow(std::streambuf::int_type ch) override; - int sync() override; - - bool dumpBuffer(); - TcpConnection& connection; - std::array readBuf; - std::array writeBuf; + std::array writeBuf; + + std::streambuf::int_type overflow(std::streambuf::int_type ch) override; + int sync() override; + std::streambuf::int_type underflow() override; + bool dumpBuffer(bool finalize); }; } diff --git a/src/common/SignalHandler.h b/src/common/SignalHandler.h deleted file mode 100644 index bfab0b89b6..0000000000 --- a/src/common/SignalHandler.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -#include "misc_os_dependent.h" - -namespace tools { - class SignalHandler - { - public: - template - static bool install(T t) - { -#if defined(WIN32) - bool r = TRUE == ::SetConsoleCtrlHandler(&winHandler, TRUE); - if (r) - { - m_handler = t; - } - return r; -#else - signal(SIGINT, posixHandler); - signal(SIGTERM, posixHandler); - m_handler = t; - return true; -#endif - } - - private: -#if defined(WIN32) - static BOOL WINAPI winHandler(DWORD type); -#else - static void posixHandler(int /*type*/); -#endif - - static void handleSignal(); - - private: - static std::function m_handler; - }; -} diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h deleted file mode 100644 index 7bfac1f8af..0000000000 --- a/src/common/boost_serialization_helper.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#if defined(WIN32) -#include -#endif - -#include - -#include -#include - -// epee -#include "include_base_utils.h" -#include "misc_os_dependent.h" - -namespace tools -{ - template - bool serialize_obj_to_file(t_object& obj, const std::string& file_path) - { - TRY_ENTRY(); -#if defined(_MSC_VER) - // Need to know HANDLE of file to call FlushFileBuffers - HANDLE data_file_handle = ::CreateFile(file_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (INVALID_HANDLE_VALUE == data_file_handle) - return false; - - int data_file_descriptor = _open_osfhandle((intptr_t)data_file_handle, 0); - if (-1 == data_file_descriptor) - { - ::CloseHandle(data_file_handle); - return false; - } - - FILE* data_file_file = _fdopen(data_file_descriptor, "wb"); - if (0 == data_file_file) - { - // Call CloseHandle is not necessary - _close(data_file_descriptor); - return false; - } - - // HACK: undocumented constructor, this code may not compile - std::ofstream data_file(data_file_file); - if (data_file.fail()) - { - // Call CloseHandle and _close are not necessary - fclose(data_file_file); - return false; - } -#else - std::ofstream data_file; - data_file.open(file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); - if (data_file.fail()) - return false; -#endif - - boost::archive::binary_oarchive a(data_file); - a << obj; - if (data_file.fail()) - return false; - - data_file.flush(); -#if defined(_MSC_VER) - // To make sure the file is fully stored on disk - ::FlushFileBuffers(data_file_handle); - fclose(data_file_file); -#endif - - return true; - CATCH_ENTRY_L0("serialize_obj_to_file", false); - } - - template - bool unserialize_obj_from_file(t_object& obj, const std::string& file_path) - { - TRY_ENTRY(); - - std::ifstream data_file; - data_file.open( file_path, std::ios_base::binary | std::ios_base::in); - if(data_file.fail()) - return false; - boost::archive::binary_iarchive a(data_file); - - a >> obj; - return !data_file.fail(); - CATCH_ENTRY_L0("unserialize_obj_from_file", false); - } -} diff --git a/src/common/static_assert.h b/src/common/static_assert.h deleted file mode 100755 index 1ebe4c1c84..0000000000 --- a/src/common/static_assert.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#ifndef __cplusplus -#ifdef __clang__ - -#define static_assert _Static_assert - -#endif -#endif diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp index 68fe82195b..84fee726cd 100644 --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/connectivity_tool/conn_tool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,34 +15,34 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include #include -// epee -#include "include_base_utils.h" -#include "net/http_client.h" -#include "net/levin_client.h" -#include "storages/http_abstract_invoke.h" -#include "storages/levin_abstract_invoke2.h" -#include "storages/portable_storage_template_helper.h" - -#include "common/command_line.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/command_line.h" +#include "Common/StringTools.h" #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "p2p/p2p_protocol_defs.h" +#include "p2p/LevinProtocol.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "rpc/HttpClient.h" #include "version.h" namespace po = boost::program_options; -using namespace cryptonote; -using namespace epee; -using namespace nodetool; +using namespace CryptoNote; -namespace -{ +namespace { const command_line::arg_descriptor arg_ip = {"ip", "set ip"}; - const command_line::arg_descriptor arg_port = {"port", "set port"}; - const command_line::arg_descriptor arg_rpc_port = {"rpc_port", "set rpc port"}; + const command_line::arg_descriptor arg_port = { "port", "set port" }; + const command_line::arg_descriptor arg_rpc_port = {"rpc_port", "set rpc port"}; const command_line::arg_descriptor arg_timeout = {"timeout", "set timeout"}; const command_line::arg_descriptor arg_priv_key = {"private_key", "private key to subscribe debug command", "", true}; const command_line::arg_descriptor arg_peer_id = {"peer_id", "peer_id if known(if not - will be requested)", 0}; @@ -52,83 +52,102 @@ namespace const command_line::arg_descriptor arg_get_daemon_info = {"rpc_get_daemon_info", "request daemon state info vie rpc (--rpc_port option should be set ).", "", true}; } -typedef COMMAND_REQUEST_STAT_INFO_T::stat_info> COMMAND_REQUEST_STAT_INFO; - -struct response_schema -{ +struct response_schema { std::string status; std::string COMMAND_REQUEST_STAT_INFO_status; std::string COMMAND_REQUEST_NETWORK_STATE_status; - enableable si_rsp; - enableable ns_rsp; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(COMMAND_REQUEST_STAT_INFO_status) - KV_SERIALIZE(COMMAND_REQUEST_NETWORK_STATE_status) - KV_SERIALIZE(si_rsp) - KV_SERIALIZE(ns_rsp) - END_KV_SERIALIZE_MAP() + boost::optional si_rsp; + boost::optional ns_rsp; }; - std::string get_response_schema_as_json(response_schema& rs) - { - std::stringstream ss; - ss << "{" << ENDL - << " \"status\": \"" << rs.status << "\"," << ENDL - << " \"COMMAND_REQUEST_NETWORK_STATE_status\": \"" << rs.COMMAND_REQUEST_NETWORK_STATE_status << "\"," << ENDL - << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\""; - if(rs.si_rsp.enabled) - { - ss << "," << ENDL << " \"si_rsp\": " << epee::serialization::store_t_to_json(rs.si_rsp.v, 1); + +template +void withTimeout(System::Dispatcher& dispatcher, SystemObj& obj, unsigned timeout, std::function f) { + System::Event timeoutEvent(dispatcher); + System::Timer timeoutTimer(dispatcher); + + dispatcher.spawn([&](){ + try { + timeoutTimer.sleep(std::chrono::milliseconds(timeout)); + obj.stop(); + } catch (std::exception&) {} + timeoutEvent.set(); + }); + + try { + f(); + } catch (System::InterruptedException&) { + timeoutEvent.wait(); + throw std::runtime_error("Operation timeout"); + } catch (std::exception&) { + timeoutTimer.stop(); + timeoutEvent.wait(); + throw; + } + + timeoutTimer.stop(); + timeoutEvent.wait(); +} + + +std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) { + + ss << "{" << ENDL + << " \"status\": \"" << rs.status << "\"," << ENDL + << " \"COMMAND_REQUEST_NETWORK_STATE_status\": \"" << rs.COMMAND_REQUEST_NETWORK_STATE_status << "\"," << ENDL + << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\""; + + if (rs.si_rsp.is_initialized()) { + ss << "," << ENDL << " \"si_rsp\": " << epee::serialization::store_t_to_json(rs.si_rsp.get(), 1); + } + + if (rs.ns_rsp.is_initialized()) { + const auto& networkState = rs.ns_rsp.get(); + + ss << "," << ENDL << " \"ns_rsp\": {" << ENDL + << " \"local_time\": " << networkState.local_time << "," << ENDL + << " \"my_id\": \"" << networkState.my_id << "\"," << ENDL + << " \"connections_list\": [" << ENDL; + + size_t i = 0; + for (const connection_entry &ce : networkState.connections_list) { + ss << " {\"peer_id\": \"" << ce.id << "\", \"ip\": \"" << Common::ipAddressToString(ce.adr.ip) << "\", \"port\": " << ce.adr.port << ", \"is_income\": " << ce.is_income << "}"; + if (networkState.connections_list.size() - 1 != i) + ss << ","; + ss << ENDL; + i++; } - if(rs.ns_rsp.enabled) - { - ss << "," << ENDL << " \"ns_rsp\": {" << ENDL - << " \"local_time\": " << rs.ns_rsp.v.local_time << "," << ENDL - << " \"my_id\": \"" << rs.ns_rsp.v.my_id << "\"," << ENDL - << " \"connections_list\": [" << ENDL; - - size_t i = 0; - BOOST_FOREACH(const connection_entry& ce, rs.ns_rsp.v.connections_list) - { - ss << " {\"peer_id\": \"" << ce.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(ce.adr.ip) << "\", \"port\": " << ce.adr.port << ", \"is_income\": "<< ce.is_income << "}"; - if(rs.ns_rsp.v.connections_list.size()-1 != i) - ss << ","; - ss << ENDL; - i++; - } - ss << " ]," << ENDL; - ss << " \"local_peerlist_white\": [" << ENDL; - i = 0; - BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_white) - { - ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}"; - if(rs.ns_rsp.v.local_peerlist_white.size()-1 != i) - ss << ","; - ss << ENDL; - i++; - } - ss << " ]," << ENDL; - - ss << " \"local_peerlist_gray\": [" << ENDL; - i = 0; - BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_gray) - { - ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}"; - if(rs.ns_rsp.v.local_peerlist_gray.size()-1 != i) - ss << ","; - ss << ENDL; - i++; - } - ss << " ]" << ENDL << " }" << ENDL; + ss << " ]," << ENDL; + ss << " \"local_peerlist_white\": [" << ENDL; + i = 0; + for (const peerlist_entry &pe : networkState.local_peerlist_white) { + ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; + if (networkState.local_peerlist_white.size() - 1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]," << ENDL; + + ss << " \"local_peerlist_gray\": [" << ENDL; + i = 0; + for (const peerlist_entry &pe : networkState.local_peerlist_gray) { + ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; + if (networkState.local_peerlist_gray.size() - 1 != i) + ss << ","; + ss << ENDL; + i++; } - ss << "}"; - return std::move(ss.str()); + ss << " ]" << ENDL << " }" << ENDL; } + + ss << "}"; + + return ss; +} + //--------------------------------------------------------------------------------------------------------------- -bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response& si) -{ +bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response &si) { std::cout << " ------ COMMAND_REQUEST_STAT_INFO ------ " << ENDL; std::cout << "Version: " << si.version << ENDL; std::cout << "OS Version: " << si.os_version << ENDL; @@ -144,167 +163,176 @@ bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response& return true; } //--------------------------------------------------------------------------------------------------------------- -bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::response& ns) -{ +bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::response &ns) { std::cout << " ------ COMMAND_REQUEST_NETWORK_STATE ------ " << ENDL; std::cout << "Peer id: " << ns.my_id << ENDL; - std::cout << "Active connections:" << ENDL; - BOOST_FOREACH(const connection_entry& ce, ns.connections_list) - { - std::cout << ce.id << "\t" << string_tools::get_ip_string_from_int32(ce.adr.ip) << ":" << ce.adr.port << (ce.is_income ? "(INC)":"(OUT)") << ENDL; + std::cout << "Active connections:" << ENDL; + + for (const connection_entry &ce : ns.connections_list) { + std::cout << ce.id << "\t" << ce.adr << (ce.is_income ? "(INC)" : "(OUT)") << ENDL; } - + std::cout << "Peer list white:" << ns.my_id << ENDL; - BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_white) - { - std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL; + for (const peerlist_entry &pe : ns.local_peerlist_white) { + std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } std::cout << "Peer list gray:" << ns.my_id << ENDL; - BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_gray) - { - std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL; + for (const peerlist_entry &pe : ns.local_peerlist_gray) { + std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } - return true; } //--------------------------------------------------------------------------------------------------------------- -bool handle_get_daemon_info(po::variables_map& vm) -{ - if(!command_line::has_arg(vm, arg_rpc_port)) - { +bool handle_get_daemon_info(po::variables_map& vm) { + if(!command_line::has_arg(vm, arg_rpc_port)) { std::cout << "ERROR: rpc port not set" << ENDL; return false; } - epee::net_utils::http::http_simple_client http_client; + try { + System::Dispatcher dispatcher; + HttpClient httpClient(dispatcher, command_line::get_arg(vm, arg_ip), command_line::get_arg(vm, arg_rpc_port)); - cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res); - std::string daemon_addr = command_line::get_arg(vm, arg_ip) + ":" + std::to_string(command_line::get_arg(vm, arg_rpc_port)); - bool r = net_utils::invoke_http_json_remote_command2(daemon_addr + "/getinfo", req, res, http_client, command_line::get_arg(vm, arg_timeout)); - if(!r) - { - std::cout << "ERROR: failed to invoke request" << ENDL; + CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res); + + invokeJsonCommand(httpClient, "/getinfo", req, res); // TODO: timeout + + std::cout << "OK" << ENDL + << "height: " << res.height << ENDL + << "difficulty: " << res.difficulty << ENDL + << "tx_count: " << res.tx_count << ENDL + << "tx_pool_size: " << res.tx_pool_size << ENDL + << "alt_blocks_count: " << res.alt_blocks_count << ENDL + << "outgoing_connections_count: " << res.outgoing_connections_count << ENDL + << "incoming_connections_count: " << res.incoming_connections_count << ENDL + << "white_peerlist_size: " << res.white_peerlist_size << ENDL + << "grey_peerlist_size: " << res.grey_peerlist_size << ENDL; + + } catch (const std::exception& e) { + std::cout << "ERROR: " << e.what() << std::endl; return false; } - std::cout << "OK" << ENDL - << "height: " << res.height << ENDL - << "difficulty: " << res.difficulty << ENDL - << "tx_count: " << res.tx_count << ENDL - << "tx_pool_size: " << res.tx_pool_size << ENDL - << "alt_blocks_count: " << res.alt_blocks_count << ENDL - << "outgoing_connections_count: " << res.outgoing_connections_count << ENDL - << "incoming_connections_count: " << res.incoming_connections_count << ENDL - << "white_peerlist_size: " << res.white_peerlist_size << ENDL - << "grey_peerlist_size: " << res.grey_peerlist_size << ENDL; return true; } //--------------------------------------------------------------------------------------------------------------- -bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) -{ - - if(!command_line::has_arg(vm, arg_priv_key)) - { +bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { + if(!command_line::has_arg(vm, arg_priv_key)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}"; return false; } + crypto::secret_key prvk = AUTO_VAL_INIT(prvk); - if(!string_tools::hex_to_pod(command_line::get_arg(vm, arg_priv_key) , prvk)) - { + if (!Common::podFromHex(command_line::get_arg(vm, arg_priv_key), prvk)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; return false; } - response_schema rs = AUTO_VAL_INIT(rs); + unsigned timeout = command_line::get_arg(vm, arg_timeout); + + try { + System::Dispatcher dispatcher; + System::TcpConnector connector(dispatcher); + System::Ipv4Resolver resolver(dispatcher); + + std::cout << "Connecting to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << ENDL; + + auto addr = resolver.resolve(command_line::get_arg(vm, arg_ip)); + + System::TcpConnection connection; + + withTimeout(dispatcher, connector, timeout, [&] { + connection = connector.connect(addr, command_line::get_arg(vm, arg_port)); + }); - levin::levin_client_impl2 transport; - if(!transport.connect(command_line::get_arg(vm, arg_ip), static_cast(command_line::get_arg(vm, arg_port)), static_cast(command_line::get_arg(vm, arg_timeout)))) - { - std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}"; - return false; - }else rs.status = "OK"; - if(!peer_id) - { - COMMAND_REQUEST_PEER_ID::request req = AUTO_VAL_INIT(req); - COMMAND_REQUEST_PEER_ID::response rsp = AUTO_VAL_INIT(rsp); - if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_PEER_ID::ID, req, rsp, transport)) - { - std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}"; - return false; - }else - { + LevinProtocol levin(connection); + + if (!peer_id) { + COMMAND_REQUEST_PEER_ID::request req; + COMMAND_REQUEST_PEER_ID::response rsp; + withTimeout(dispatcher, connection, timeout, [&] { + levin.invoke(COMMAND_REQUEST_PEER_ID::ID, req, rsp); + }); peer_id = rsp.my_id; } - } + proof_of_trust pot = AUTO_VAL_INIT(pot); + pot.peer_id = peer_id; + pot.time = time(NULL); + crypto::public_key pubk = AUTO_VAL_INIT(pubk); + Common::podFromHex(P2P_STAT_TRUSTED_PUB_KEY, pubk); + crypto::hash h = get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, prvk, pot.sign); - nodetool::proof_of_trust pot = AUTO_VAL_INIT(pot); - pot.peer_id = peer_id; - pot.time = time(NULL); - crypto::public_key pubk = AUTO_VAL_INIT(pubk); - string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pubk); - crypto::hash h = tools::get_proof_of_trust_hash(pot); - crypto::generate_signature(h, pubk, prvk, pot.sign); - - if(command_line::get_arg(vm, arg_request_stat_info)) - { - COMMAND_REQUEST_STAT_INFO::request req = AUTO_VAL_INIT(req); - req.tr = pot; - if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_STAT_INFO::ID, req, rs.si_rsp.v, transport)) - { - std::stringstream ss; - ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_STAT_INFO to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port); - rs.COMMAND_REQUEST_STAT_INFO_status = ss.str(); - }else - { - rs.si_rsp.enabled = true; - rs.COMMAND_REQUEST_STAT_INFO_status = "OK"; + if (command_line::get_arg(vm, arg_request_stat_info)) { + COMMAND_REQUEST_STAT_INFO::request req = AUTO_VAL_INIT(req); + COMMAND_REQUEST_STAT_INFO::response res = AUTO_VAL_INIT(res); + + req.tr = pot; + + try { + withTimeout(dispatcher, connection, timeout, [&] { + levin.invoke(COMMAND_REQUEST_STAT_INFO::ID, req, res); + }); + rs.si_rsp = std::move(res); + rs.COMMAND_REQUEST_STAT_INFO_status = "OK"; + } catch (const std::exception &e) { + std::stringstream ss; + ss << "ERROR: Failed to invoke remote command COMMAND_REQUEST_STAT_INFO to " + << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) + << " - " << e.what(); + rs.COMMAND_REQUEST_STAT_INFO_status = ss.str(); + } } - } - - if(command_line::get_arg(vm, arg_request_net_state)) - { - ++pot.time; - h = tools::get_proof_of_trust_hash(pot); - crypto::generate_signature(h, pubk, prvk, pot.sign); - COMMAND_REQUEST_NETWORK_STATE::request req = AUTO_VAL_INIT(req); - req.tr = pot; - if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_NETWORK_STATE::ID, req, rs.ns_rsp.v, transport)) - { - std::stringstream ss; - ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_NETWORK_STATE to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port); - rs.COMMAND_REQUEST_NETWORK_STATE_status = ss.str(); - }else - { - rs.ns_rsp.enabled = true; - rs.COMMAND_REQUEST_NETWORK_STATE_status = "OK"; + if (command_line::get_arg(vm, arg_request_net_state)) { + ++pot.time; + h = get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, prvk, pot.sign); + COMMAND_REQUEST_NETWORK_STATE::request req = AUTO_VAL_INIT(req); + COMMAND_REQUEST_NETWORK_STATE::response res = AUTO_VAL_INIT(res); + req.tr = pot; + + try { + withTimeout(dispatcher, connection, timeout, [&] { + levin.invoke(COMMAND_REQUEST_NETWORK_STATE::ID, req, res); + }); + rs.ns_rsp = std::move(res); + rs.COMMAND_REQUEST_NETWORK_STATE_status = "OK"; + } catch (const std::exception &e) { + std::stringstream ss; + ss << "ERROR: Failed to invoke remote command COMMAND_REQUEST_NETWORK_STATE to " + << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) + << " - " << e.what(); + rs.COMMAND_REQUEST_NETWORK_STATE_status = ss.str(); + } } + } catch (const std::exception& e) { + std::cout << "ERROR: " << e.what() << std::endl; + return false; } - std::cout << get_response_schema_as_json(rs); + + get_response_schema_as_json(std::cout, rs) << std::endl; return true; } + //--------------------------------------------------------------------------------------------------------------- -bool generate_and_print_keys() -{ +bool generate_and_print_keys() { crypto::public_key pk = AUTO_VAL_INIT(pk); crypto::secret_key sk = AUTO_VAL_INIT(sk); generate_keys(pk, sk); - std::cout << "PUBLIC KEY: " << epee::string_tools::pod_to_hex(pk) << ENDL - << "PRIVATE KEY: " << epee::string_tools::pod_to_hex(sk); + std::cout << "PUBLIC KEY: " << Common::podToHex(pk) << ENDL + << "PRIVATE KEY: " << Common::podToHex(sk); return true; } -int main(int argc, char* argv[]) -{ - string_tools::set_module_name_and_folder(argv[0]); - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); +int main(int argc, char *argv[]) { // Declare the supported options. po::options_description desc_general("General options"); command_line::add_arg(desc_general, command_line::arg_help); @@ -321,13 +349,11 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_priv_key); command_line::add_arg(desc_params, arg_get_daemon_info); - po::options_description desc_all; desc_all.add(desc_general).add(desc_params); po::variables_map vm; - bool r = command_line::handle_error_helper(desc_all, [&]() - { + bool r = command_line::handle_error_helper(desc_all, [&]() { po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); if (command_line::get_arg(vm, command_line::arg_help)) { @@ -340,26 +366,23 @@ int main(int argc, char* argv[]) return true; }); + if (!r) return 1; - if(command_line::has_arg(vm, arg_request_stat_info) || command_line::has_arg(vm, arg_request_net_state)) - { - return handle_request_stat(vm, command_line::get_arg(vm, arg_peer_id)) ? 0:1; + if (command_line::has_arg(vm, arg_request_stat_info) || command_line::has_arg(vm, arg_request_net_state)) { + return handle_request_stat(vm, command_line::get_arg(vm, arg_peer_id)) ? 0 : 1; } - if(command_line::has_arg(vm, arg_get_daemon_info)) - { - return handle_get_daemon_info(vm) ? 0:1; - } - else if(command_line::has_arg(vm, arg_generate_keys)) - { - return generate_and_print_keys() ? 0:1; - } - else - { - std::cerr << "Not enough arguments." << ENDL; - std::cerr << desc_all << ENDL; + + if (command_line::has_arg(vm, arg_get_daemon_info)) { + return handle_get_daemon_info(vm) ? 0 : 1; + } + + if (command_line::has_arg(vm, arg_generate_keys)) { + return generate_and_print_keys() ? 0 : 1; } + std::cerr << "Not enough arguments." << ENDL; + std::cerr << desc_all << ENDL; return 1; } diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c index 36b42c3d45..92a7f2506a 100644 --- a/src/crypto/blake256.c +++ b/src/crypto/blake256.c @@ -148,7 +148,7 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) { if (datalen > 0) { memcpy((void *) (S->buf + left), (void *) data, datalen >> 3); - S->buflen = (left << 3) + datalen; + S->buflen = (left << 3) + (int)datalen; } else { S->buflen = 0; } diff --git a/src/crypto/chacha8.c b/src/crypto/chacha8.c index df135af594..7cfc88d5ee 100644 --- a/src/crypto/chacha8.c +++ b/src/crypto/chacha8.c @@ -9,8 +9,7 @@ Public domain. #include #include "chacha8.h" -#include "common/int-util.h" -#include "warnings.h" +#include "Common/int-util.h" /* * The following macros are used to obtain exact-width results. @@ -38,8 +37,6 @@ Public domain. static const char sigma[] = "expand 32-byte k"; -DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing) - void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 9319375c80..04ec5d7b7e 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 0267643854..027ee95be8 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -18,11 +18,8 @@ #include #include -#include "warnings.h" #include "crypto-ops.h" -DISABLE_VS_WARNINGS(4146 4244) - /* Predeclarations */ static void fe_mul(fe, const fe, const fe); @@ -190,7 +187,7 @@ static void fe_cmov(fe f, const fe g, unsigned int b) { int32_t x8 = f8 ^ g8; int32_t x9 = f9 ^ g9; assert((((b - 1) & ~b) | ((b - 2) & ~(b - 1))) == (unsigned int) -1); - b = -b; + b = -(int) b; x0 &= b; x1 &= b; x2 &= b; @@ -569,16 +566,16 @@ static void fe_mul(fe h, const fe f, const fe g) { /* |h0| <= 2^25; from now on fits into int32 unchanged */ /* |h1| <= 1.01*2^24 */ - h[0] = h0; - h[1] = h1; - h[2] = h2; - h[3] = h3; - h[4] = h4; - h[5] = h5; - h[6] = h6; - h[7] = h7; - h[8] = h8; - h[9] = h9; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; } /* From fe_neg.c */ @@ -762,16 +759,16 @@ static void fe_sq(fe h, const fe f) { carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; - h[0] = h0; - h[1] = h1; - h[2] = h2; - h[3] = h3; - h[4] = h4; - h[5] = h5; - h[6] = h6; - h[7] = h7; - h[8] = h8; - h[9] = h9; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; } /* From fe_sq2.c */ @@ -921,16 +918,16 @@ static void fe_sq2(fe h, const fe f) { carry0 = (h0 + (int64_t) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; - h[0] = h0; - h[1] = h1; - h[2] = h2; - h[3] = h3; - h[4] = h4; - h[5] = h5; - h[6] = h6; - h[7] = h7; - h[8] = h8; - h[9] = h9; + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; } /* From fe_sub.c */ @@ -1272,16 +1269,16 @@ int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) { carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; - h->Y[0] = h0; - h->Y[1] = h1; - h->Y[2] = h2; - h->Y[3] = h3; - h->Y[4] = h4; - h->Y[5] = h5; - h->Y[6] = h6; - h->Y[7] = h7; - h->Y[8] = h8; - h->Y[9] = h9; + h->Y[0] = (int32_t) h0; + h->Y[1] = (int32_t) h1; + h->Y[2] = (int32_t) h2; + h->Y[3] = (int32_t) h3; + h->Y[4] = (int32_t) h4; + h->Y[5] = (int32_t) h5; + h->Y[6] = (int32_t) h6; + h->Y[7] = (int32_t) h7; + h->Y[8] = (int32_t) h8; + h->Y[9] = (int32_t) h9; /* End fe_frombytes.c */ @@ -1491,7 +1488,7 @@ static unsigned char equal(signed char b, signed char c) { static unsigned char negative(signed char b) { unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ x >>= 63; /* 1: yes; 0: no */ - return x; + return (unsigned char) x; } static void ge_precomp_cmov(ge_precomp *t, const ge_precomp *u, unsigned char b) { @@ -1813,38 +1810,38 @@ void sc_reduce(unsigned char *s) { carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; - s[0] = s0 >> 0; - s[1] = s0 >> 8; - s[2] = (s0 >> 16) | (s1 << 5); - s[3] = s1 >> 3; - s[4] = s1 >> 11; - s[5] = (s1 >> 19) | (s2 << 2); - s[6] = s2 >> 6; - s[7] = (s2 >> 14) | (s3 << 7); - s[8] = s3 >> 1; - s[9] = s3 >> 9; - s[10] = (s3 >> 17) | (s4 << 4); - s[11] = s4 >> 4; - s[12] = s4 >> 12; - s[13] = (s4 >> 20) | (s5 << 1); - s[14] = s5 >> 7; - s[15] = (s5 >> 15) | (s6 << 6); - s[16] = s6 >> 2; - s[17] = s6 >> 10; - s[18] = (s6 >> 18) | (s7 << 3); - s[19] = s7 >> 5; - s[20] = s7 >> 13; - s[21] = s8 >> 0; - s[22] = s8 >> 8; - s[23] = (s8 >> 16) | (s9 << 5); - s[24] = s9 >> 3; - s[25] = s9 >> 11; - s[26] = (s9 >> 19) | (s10 << 2); - s[27] = s10 >> 6; - s[28] = (s10 >> 14) | (s11 << 7); - s[29] = s11 >> 1; - s[30] = s11 >> 9; - s[31] = s11 >> 17; + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); } /* New code */ @@ -2077,16 +2074,16 @@ void ge_fromfe_frombytes_vartime(ge_p2 *r, const unsigned char *s) { carry6 = (h6 + (int64_t) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; carry8 = (h8 + (int64_t) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; - u[0] = h0; - u[1] = h1; - u[2] = h2; - u[3] = h3; - u[4] = h4; - u[5] = h5; - u[6] = h6; - u[7] = h7; - u[8] = h8; - u[9] = h9; + u[0] = (int32_t) h0; + u[1] = (int32_t) h1; + u[2] = (int32_t) h2; + u[3] = (int32_t) h3; + u[4] = (int32_t) h4; + u[5] = (int32_t) h5; + u[6] = (int32_t) h6; + u[7] = (int32_t) h7; + u[8] = (int32_t) h8; + u[9] = (int32_t) h9; /* End fe_frombytes.c */ @@ -2242,38 +2239,38 @@ void sc_reduce32(unsigned char *s) { carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; - s[0] = s0 >> 0; - s[1] = s0 >> 8; - s[2] = (s0 >> 16) | (s1 << 5); - s[3] = s1 >> 3; - s[4] = s1 >> 11; - s[5] = (s1 >> 19) | (s2 << 2); - s[6] = s2 >> 6; - s[7] = (s2 >> 14) | (s3 << 7); - s[8] = s3 >> 1; - s[9] = s3 >> 9; - s[10] = (s3 >> 17) | (s4 << 4); - s[11] = s4 >> 4; - s[12] = s4 >> 12; - s[13] = (s4 >> 20) | (s5 << 1); - s[14] = s5 >> 7; - s[15] = (s5 >> 15) | (s6 << 6); - s[16] = s6 >> 2; - s[17] = s6 >> 10; - s[18] = (s6 >> 18) | (s7 << 3); - s[19] = s7 >> 5; - s[20] = s7 >> 13; - s[21] = s8 >> 0; - s[22] = s8 >> 8; - s[23] = (s8 >> 16) | (s9 << 5); - s[24] = s9 >> 3; - s[25] = s9 >> 11; - s[26] = (s9 >> 19) | (s10 << 2); - s[27] = s10 >> 6; - s[28] = (s10 >> 14) | (s11 << 7); - s[29] = s11 >> 1; - s[30] = s11 >> 9; - s[31] = s11 >> 17; + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); } void sc_add(unsigned char *s, const unsigned char *a, const unsigned char *b) { @@ -2381,38 +2378,38 @@ void sc_add(unsigned char *s, const unsigned char *a, const unsigned char *b) { carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; - s[0] = s0 >> 0; - s[1] = s0 >> 8; - s[2] = (s0 >> 16) | (s1 << 5); - s[3] = s1 >> 3; - s[4] = s1 >> 11; - s[5] = (s1 >> 19) | (s2 << 2); - s[6] = s2 >> 6; - s[7] = (s2 >> 14) | (s3 << 7); - s[8] = s3 >> 1; - s[9] = s3 >> 9; - s[10] = (s3 >> 17) | (s4 << 4); - s[11] = s4 >> 4; - s[12] = s4 >> 12; - s[13] = (s4 >> 20) | (s5 << 1); - s[14] = s5 >> 7; - s[15] = (s5 >> 15) | (s6 << 6); - s[16] = s6 >> 2; - s[17] = s6 >> 10; - s[18] = (s6 >> 18) | (s7 << 3); - s[19] = s7 >> 5; - s[20] = s7 >> 13; - s[21] = s8 >> 0; - s[22] = s8 >> 8; - s[23] = (s8 >> 16) | (s9 << 5); - s[24] = s9 >> 3; - s[25] = s9 >> 11; - s[26] = (s9 >> 19) | (s10 << 2); - s[27] = s10 >> 6; - s[28] = (s10 >> 14) | (s11 << 7); - s[29] = s11 >> 1; - s[30] = s11 >> 9; - s[31] = s11 >> 17; + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); } void sc_sub(unsigned char *s, const unsigned char *a, const unsigned char *b) { @@ -2520,38 +2517,38 @@ void sc_sub(unsigned char *s, const unsigned char *a, const unsigned char *b) { carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; - s[0] = s0 >> 0; - s[1] = s0 >> 8; - s[2] = (s0 >> 16) | (s1 << 5); - s[3] = s1 >> 3; - s[4] = s1 >> 11; - s[5] = (s1 >> 19) | (s2 << 2); - s[6] = s2 >> 6; - s[7] = (s2 >> 14) | (s3 << 7); - s[8] = s3 >> 1; - s[9] = s3 >> 9; - s[10] = (s3 >> 17) | (s4 << 4); - s[11] = s4 >> 4; - s[12] = s4 >> 12; - s[13] = (s4 >> 20) | (s5 << 1); - s[14] = s5 >> 7; - s[15] = (s5 >> 15) | (s6 << 6); - s[16] = s6 >> 2; - s[17] = s6 >> 10; - s[18] = (s6 >> 18) | (s7 << 3); - s[19] = s7 >> 5; - s[20] = s7 >> 13; - s[21] = s8 >> 0; - s[22] = s8 >> 8; - s[23] = (s8 >> 16) | (s9 << 5); - s[24] = s9 >> 3; - s[25] = s9 >> 11; - s[26] = (s9 >> 19) | (s10 << 2); - s[27] = s10 >> 6; - s[28] = (s10 >> 14) | (s11 << 7); - s[29] = s11 >> 1; - s[30] = s11 >> 9; - s[31] = s11 >> 17; + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); } /* @@ -2852,38 +2849,38 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; - s[0] = s0 >> 0; - s[1] = s0 >> 8; - s[2] = (s0 >> 16) | (s1 << 5); - s[3] = s1 >> 3; - s[4] = s1 >> 11; - s[5] = (s1 >> 19) | (s2 << 2); - s[6] = s2 >> 6; - s[7] = (s2 >> 14) | (s3 << 7); - s[8] = s3 >> 1; - s[9] = s3 >> 9; - s[10] = (s3 >> 17) | (s4 << 4); - s[11] = s4 >> 4; - s[12] = s4 >> 12; - s[13] = (s4 >> 20) | (s5 << 1); - s[14] = s5 >> 7; - s[15] = (s5 >> 15) | (s6 << 6); - s[16] = s6 >> 2; - s[17] = s6 >> 10; - s[18] = (s6 >> 18) | (s7 << 3); - s[19] = s7 >> 5; - s[20] = s7 >> 13; - s[21] = s8 >> 0; - s[22] = s8 >> 8; - s[23] = (s8 >> 16) | (s9 << 5); - s[24] = s9 >> 3; - s[25] = s9 >> 11; - s[26] = (s9 >> 19) | (s10 << 2); - s[27] = s10 >> 6; - s[28] = (s10 >> 14) | (s11 << 7); - s[29] = s11 >> 1; - s[30] = s11 >> 9; - s[31] = s11 >> 17; + s[0] = (unsigned char) (s0 >> 0); + s[1] = (unsigned char) (s0 >> 8); + s[2] = (unsigned char) ((s0 >> 16) | (s1 << 5)); + s[3] = (unsigned char) (s1 >> 3); + s[4] = (unsigned char) (s1 >> 11); + s[5] = (unsigned char) ((s1 >> 19) | (s2 << 2)); + s[6] = (unsigned char) (s2 >> 6); + s[7] = (unsigned char) ((s2 >> 14) | (s3 << 7)); + s[8] = (unsigned char) (s3 >> 1); + s[9] = (unsigned char) (s3 >> 9); + s[10] = (unsigned char) ((s3 >> 17) | (s4 << 4)); + s[11] = (unsigned char) (s4 >> 4); + s[12] = (unsigned char) (s4 >> 12); + s[13] = (unsigned char) ((s4 >> 20) | (s5 << 1)); + s[14] = (unsigned char) (s5 >> 7); + s[15] = (unsigned char) ((s5 >> 15) | (s6 << 6)); + s[16] = (unsigned char) (s6 >> 2); + s[17] = (unsigned char) (s6 >> 10); + s[18] = (unsigned char) ((s6 >> 18) | (s7 << 3)); + s[19] = (unsigned char) (s7 >> 5); + s[20] = (unsigned char) (s7 >> 13); + s[21] = (unsigned char) (s8 >> 0); + s[22] = (unsigned char) (s8 >> 8); + s[23] = (unsigned char) ((s8 >> 16) | (s9 << 5)); + s[24] = (unsigned char) (s9 >> 3); + s[25] = (unsigned char) (s9 >> 11); + s[26] = (unsigned char) ((s9 >> 19) | (s10 << 2)); + s[27] = (unsigned char) (s10 >> 6); + s[28] = (unsigned char) ((s10 >> 14) | (s11 << 7)); + s[29] = (unsigned char) (s11 >> 1); + s[30] = (unsigned char) (s11 >> 9); + s[31] = (unsigned char) (s11 >> 17); } /* Assumes that a != INT64_MIN */ @@ -2900,7 +2897,7 @@ int sc_check(const unsigned char *s) { int64_t s5 = load_4(s + 20); int64_t s6 = load_4(s + 24); int64_t s7 = load_4(s + 28); - return (signum(1559614444 - s0) + (signum(1477600026 - s1) << 1) + (signum(2734136534 - s2) << 2) + (signum(350157278 - s3) << 3) + (signum(-s4) << 4) + (signum(-s5) << 5) + (signum(-s6) << 6) + (signum(268435456 - s7) << 7)) >> 8; + return (int) ((signum(1559614444 - s0) + (signum(1477600026 - s1) << 1) + (signum(2734136534 - s2) << 2) + (signum(350157278 - s3) << 3) + (signum(-s4) << 4) + (signum(-s5) << 5) + (signum(-s6) << 6) + (signum(268435456 - s7) << 7)) >> 8); } int sc_isnonzero(const unsigned char *s) { diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 912404e9a4..feb46f2409 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once /* From fe.h */ diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 2664c30a97..298fe1a45b 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include #include #include @@ -24,8 +7,7 @@ #include #include -#include "common/varint.h" -#include "warnings.h" +#include "Common/varint.h" #include "crypto.h" #include "hash.h" @@ -240,15 +222,16 @@ namespace crypto { ge_tobytes(&image, &point2); } -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4200) +#ifdef _MSC_VER +#pragma warning(disable: 4200) +#endif + struct rs_comm { hash h; struct { ec_point a, b; } ab[]; }; -POP_WARNINGS static inline size_t rs_comm_size(size_t pubs_count) { return sizeof(rs_comm) + pubs_count * sizeof(rs_comm().ab[0]); diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 21757e07a4..2dea3321c8 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include @@ -22,7 +5,7 @@ #include #include -#include "common/pod-class.h" +#include "Common/pod-class.h" #include "generic-ops.h" #include "hash.h" diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 5e9e6da66c..6c419acbca 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 9a8f154745..cdd5a2d322 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #if !defined(__cplusplus) @@ -24,9 +7,8 @@ #include #include -#include "../common/static_assert.h" -#include "common/int-util.h" -#include "warnings.h" +#include "../Common/static_assert.h" +#include "Common/int-util.h" static inline void *padd(void *p, size_t i) { return (char *) p + i; @@ -36,17 +18,14 @@ static inline const void *cpadd(const void *p, size_t i) { return (const char *) p + i; } -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4267) static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t must be 4 or 8 bytes long"); static inline void place_length(uint8_t *buffer, size_t bufsize, size_t length) { if (sizeof(size_t) == 4) { - *(uint32_t *) padd(buffer, bufsize - 4) = swap32be(length); + *(uint32_t *) padd(buffer, bufsize - 4) = swap32be((uint32_t) length); } else { *(uint64_t *) padd(buffer, bufsize - 8) = swap64be(length); } } -POP_WARNINGS #pragma pack(push, 1) union hash_state { diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 17031b6b77..9231a58534 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -27,7 +27,7 @@ void hash_permutation(union hash_state *state) { } void hash_process(union hash_state *state, const uint8_t *buf, size_t count) { - keccak1600(buf, count, (uint8_t*)state); + keccak1600(buf, (int)count, (uint8_t*)state); } void cn_fast_hash(const void *data, size_t length, char *hash) { diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 5115e0f95b..041ea6475d 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,25 +1,8 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include -#include "common/pod-class.h" +#include "Common/pod-class.h" #include "generic-ops.h" namespace crypto { diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 9afa713783..44c24c163c 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #if defined(__GNUC__) diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c index f3f2aac8c8..76bcaeb7e7 100644 --- a/src/crypto/oaes_lib.c +++ b/src/crypto/oaes_lib.c @@ -486,9 +486,9 @@ static uint32_t oaes_get_seed(void) ftime (&timer); gmTimer = gmtime( &timer.time ); _test = (char *) calloc( sizeof( char ), timer.millitm ); - _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + + _ret = (uint32_t)(gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + - (uintptr_t) ( _test + timer.millitm ) + getpid(); + (uintptr_t) ( _test + timer.millitm ) + getpid()); if( _test ) free( _test ); @@ -669,7 +669,7 @@ OAES_RET oaes_key_export( OAES_CTX * ctx, // header memcpy( data, oaes_header, OAES_BLOCK_SIZE ); data[5] = 0x01; - data[7] = _ctx->key->data_len; + data[7] = (uint8_t)_ctx->key->data_len; memcpy( data + OAES_BLOCK_SIZE, _ctx->key->data, _ctx->key->data_len ); return OAES_RET_SUCCESS; @@ -1262,7 +1262,7 @@ OAES_RET oaes_encrypt( OAES_CTX * ctx, // insert pad for( _j = 0; _j < OAES_BLOCK_SIZE - _block_size; _j++ ) - _block[ _block_size + _j ] = _j + 1; + _block[ _block_size + _j ] = (uint8_t)(_j + 1); // CBC if( _ctx->options & OAES_OPTION_CBC ) diff --git a/src/crypto/random.h b/src/crypto/random.h index c8eab97fb8..60a670e769 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #if !defined(__cplusplus) diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h index bd47065adf..d37d964dcc 100644 --- a/src/crypto/skein_port.h +++ b/src/crypto/skein_port.h @@ -86,7 +86,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 "Common/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 1454c14e41..cc8594437c 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -30,7 +30,7 @@ #include "aesb.h" #include "initializer.h" -#include "common/int-util.h" +#include "Common/int-util.h" #include "hash-ops.h" #include "oaes_lib.h" diff --git a/src/crypto/slow-hash.cpp b/src/crypto/slow-hash.cpp index 7934203c0c..5d1a204beb 100644 --- a/src/crypto/slow-hash.cpp +++ b/src/crypto/slow-hash.cpp @@ -1,20 +1,3 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include #include "hash.h" diff --git a/src/crypto/slow-hash.inl b/src/crypto/slow-hash.inl index d6b07554ac..7ed0045c52 100644 --- a/src/crypto/slow-hash.inl +++ b/src/crypto/slow-hash.inl @@ -25,7 +25,7 @@ cn_slow_hash_noaesni { #define ctx ((struct cn_ctx *) context) uint8_t ExpandedKey[256]; - size_t i, j; + size_t i; __m128i *longoutput, *expkey, *xmminput, b_x; ALIGNED_DECL(uint64_t a[2], 16); hash_process(&ctx->state.hs, (const uint8_t*) data, length); @@ -50,7 +50,7 @@ cn_slow_hash_noaesni for (i = 0; likely(i < MEMORY); i += INIT_SIZE_BYTE) { #if defined(AESNI) - for(j = 0; j < 10; j++) + for(size_t j = 0; j < 10; j++) { xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); @@ -167,7 +167,7 @@ cn_slow_hash_noaesni xmminput[7] = _mm_xor_si128(longoutput[(i >> 4) + 7], xmminput[7]); #if defined(AESNI) - for(j = 0; j < 10; j++) + for(size_t j = 0; j < 10; j++) { xmminput[0] = _mm_aesenc_si128(xmminput[0], expkey[j]); xmminput[1] = _mm_aesenc_si128(xmminput[1], expkey[j]); diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index a1110c7a66..283b08d570 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,7 +19,7 @@ #include -namespace cryptonote { +namespace CryptoNote { namespace parameters { const uint64_t CRYPTONOTE_MAX_BLOCK_NUMBER = 500000000; @@ -41,8 +41,6 @@ const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = 20000; //size of const size_t CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 = 10000; const size_t CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE = 600; const size_t CRYPTONOTE_DISPLAY_DECIMAL_POINT = 8; -// COIN - number of smallest units in one coin -const uint64_t COIN = UINT64_C(100000000); // pow(10, 8) const uint64_t MINIMUM_FEE = UINT64_C(1000000); // pow(10, 6) const uint64_t DEFAULT_DUST_THRESHOLD = UINT64_C(1000000); // pow(10, 6) @@ -70,7 +68,6 @@ const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_O static_assert(0 < UPGRADE_VOTING_THRESHOLD && UPGRADE_VOTING_THRESHOLD <= 100, "Bad UPGRADE_VOTING_THRESHOLD"); static_assert(UPGRADE_VOTING_WINDOW > 1, "Bad UPGRADE_VOTING_WINDOW"); -const char CRYPTONOTE_BLOCKCHAINDATA_FILENAME[] = "blockchain.bin"; // Obsolete blockchain format const char CRYPTONOTE_BLOCKS_FILENAME[] = "blocks.dat"; const char CRYPTONOTE_BLOCKINDEXES_FILENAME[] = "blockindexes.dat"; const char CRYPTONOTE_BLOCKSCACHE_FILENAME[] = "blockscache.dat"; @@ -152,8 +149,9 @@ const CheckpointData CHECKPOINTS[] = { {645000, "1eeba944c0dd6b9a1228a425a74076fbdbeaf9b657ba7ef02547d99f971de70d"}, {667000, "a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995"}, {689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"}, - {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"} + {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}, + {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"} }; -} // cryptonote +} // CryptoNote #define ALLOW_DEBUG_COMMANDS diff --git a/src/cryptonote_core/AccountKVSerialization.h b/src/cryptonote_core/AccountKVSerialization.h index 31d65f76e8..74fbe6bf47 100644 --- a/src/cryptonote_core/AccountKVSerialization.h +++ b/src/cryptonote_core/AccountKVSerialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ // epee #include "serialization/keyvalue_serialization.h" -namespace cryptonote { +namespace CryptoNote { template struct AccountPublicAddressSerializer; template struct AccountKeysSerializer; template struct AccountBaseSerializer; diff --git a/src/cryptonote_core/BlockIndex.cpp b/src/cryptonote_core/BlockIndex.cpp index 984a8bb689..12e973bdeb 100644 --- a/src/cryptonote_core/BlockIndex.cpp +++ b/src/cryptonote_core/BlockIndex.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/BlockIndex.h b/src/cryptonote_core/BlockIndex.h index 382e6e01c6..d447d1905a 100644 --- a/src/cryptonote_core/BlockIndex.h +++ b/src/cryptonote_core/BlockIndex.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,7 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// multi index #include #include #include diff --git a/src/cryptonote_core/CoreConfig.cpp b/src/cryptonote_core/CoreConfig.cpp index e3a50772e5..b4deb2fbe5 100644 --- a/src/cryptonote_core/CoreConfig.cpp +++ b/src/cryptonote_core/CoreConfig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,10 +17,10 @@ #include "CoreConfig.h" -#include "common/util.h" -#include "common/command_line.h" +#include "Common/util.h" +#include "Common/command_line.h" -namespace cryptonote { +namespace CryptoNote { CoreConfig::CoreConfig() { configFolder = tools::get_default_data_dir(); @@ -32,5 +32,4 @@ void CoreConfig::init(const boost::program_options::variables_map& options) { void CoreConfig::initOptions(boost::program_options::options_description& desc) { } -} //namespace cryptonote - +} //namespace CryptoNote diff --git a/src/cryptonote_core/CoreConfig.h b/src/cryptonote_core/CoreConfig.h index 655e15e33d..2a04edca95 100644 --- a/src/cryptonote_core/CoreConfig.h +++ b/src/cryptonote_core/CoreConfig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include -namespace cryptonote { +namespace CryptoNote { class CoreConfig { public: @@ -33,4 +33,4 @@ class CoreConfig { std::string configFolder; }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/Currency.cpp b/src/cryptonote_core/Currency.cpp index fd7a95ae91..8138ec1fef 100644 --- a/src/cryptonote_core/Currency.cpp +++ b/src/cryptonote_core/Currency.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,424 +16,459 @@ // along with Bytecoin. If not, see . #include "Currency.h" - +#include #include #include +#include "../Common/base58.h" +#include "../Common/int-util.h" +#include "account.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" +#include "UpgradeDetector.h" +#undef ERROR + +using namespace Logging; + +namespace CryptoNote { + +bool Currency::init() { + if (!generateGenesisBlock()) { + logger(ERROR, BRIGHT_RED) << "Failed to generate genesis block"; + return false; + } -// epee -#include "include_base_utils.h" -#include "string_tools.h" - -#include "common/base58.h" -#include "common/int-util.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/UpgradeDetector.h" - -namespace cryptonote { - bool Currency::init() { - bool r; - r = generateGenesisBlock(); - CHECK_AND_ASSERT_MES(r, false, "Failed to generate genesis block"); - - r = get_block_hash(m_genesisBlock, m_genesisBlockHash); - CHECK_AND_ASSERT_MES(r, false, "Failed to get genesis block hash"); - - if (isTestnet()) { - m_upgradeHeight = 0; - m_blocksFileName = "testnet_" + m_blocksFileName; - m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; - m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; - m_txPoolFileName = "testnet_" + m_txPoolFileName; - } + if (!get_block_hash(m_genesisBlock, m_genesisBlockHash)) { + logger(ERROR, BRIGHT_RED) << "Failed to get genesis block hash"; + return false; + } - return true; + if (isTestnet()) { + m_upgradeHeight = 0; + m_blocksFileName = "testnet_" + m_blocksFileName; + m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; + m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; + m_txPoolFileName = "testnet_" + m_txPoolFileName; } - bool Currency::generateGenesisBlock() { - m_genesisBlock = boost::value_initialized(); + return true; +} - //account_public_address ac = boost::value_initialized(); - //std::vector sz; - //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.minerTx); // zero fee in genesis - //blobdata txb = tx_to_blob(m_genesisBlock.minerTx); - //std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); +bool Currency::generateGenesisBlock() { + m_genesisBlock = boost::value_initialized(); - // Hard code coinbase tx in genesis block, because through generating tx use random, but genesis should be always the same - std::string genesisCoinbaseTxHex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + //account_public_address ac = boost::value_initialized(); + //std::vector sz; + //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.minerTx); // zero fee in genesis + //blobdata txb = tx_to_blob(m_genesisBlock.minerTx); + //std::string hex_tx_represent = Common::toHex(txb); - blobdata minerTxBlob; - epee::string_tools::parse_hexstr_to_binbuff(genesisCoinbaseTxHex, minerTxBlob); - bool r = parse_and_validate_tx_from_blob(minerTxBlob, m_genesisBlock.minerTx); - CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); - m_genesisBlock.majorVersion = BLOCK_MAJOR_VERSION_1; - m_genesisBlock.minorVersion = BLOCK_MINOR_VERSION_0; - m_genesisBlock.timestamp = 0; - m_genesisBlock.nonce = 70; - if (m_testnet) { - ++m_genesisBlock.nonce; - } - //miner::find_nonce_for_given_block(bl, 1, 0); + // Hard code coinbase tx in genesis block, because through generating tx use random, but genesis should be always the same + std::string genesisCoinbaseTxHex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + blobdata minerTxBlob; - return true; - } + bool r = + hexToBlob(genesisCoinbaseTxHex, minerTxBlob) && + parse_and_validate_tx_from_blob(minerTxBlob, m_genesisBlock.minerTx); - bool Currency::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, - uint64_t fee, bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const { - assert(alreadyGeneratedCoins <= m_moneySupply); - assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t)); + if (!r) { + logger(ERROR, BRIGHT_RED) << "failed to parse coinbase tx from hard coded blob"; + return false; + } - uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor; + m_genesisBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + m_genesisBlock.minorVersion = BLOCK_MINOR_VERSION_0; + m_genesisBlock.timestamp = 0; + m_genesisBlock.nonce = 70; + if (m_testnet) { + ++m_genesisBlock.nonce; + } + //miner::find_nonce_for_given_block(bl, 1, 0); - size_t blockGrantedFullRewardZone = penalizeFee ? - m_blockGrantedFullRewardZone : - cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - medianSize = std::max(medianSize, blockGrantedFullRewardZone); - if (currentBlockSize > UINT64_C(2) * medianSize) { - LOG_PRINT_L4("Block cumulative size is too big: " << currentBlockSize << ", expected less than " << 2 * medianSize); - return false; - } + return true; +} - uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize); - uint64_t penalizedFee = penalizeFee ? getPenalizedAmount(fee, medianSize, currentBlockSize) : fee; +bool Currency::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, + uint64_t fee, bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const { + assert(alreadyGeneratedCoins <= m_moneySupply); + assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t)); - emissionChange = penalizedBaseReward - (fee - penalizedFee); - reward = penalizedBaseReward + penalizedFee; + uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor; - return true; + size_t blockGrantedFullRewardZone = penalizeFee ? + m_blockGrantedFullRewardZone : + CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + medianSize = std::max(medianSize, blockGrantedFullRewardZone); + if (currentBlockSize > UINT64_C(2) * medianSize) { + logger(TRACE) << "Block cumulative size is too big: " << currentBlockSize << ", expected less than " << 2 * medianSize; + return false; } - size_t Currency::maxBlockCumulativeSize(uint64_t height) const { - assert(height <= std::numeric_limits::max() / m_maxBlockSizeGrowthSpeedNumerator); - size_t maxSize = static_cast(m_maxBlockSizeInitial + - (height * m_maxBlockSizeGrowthSpeedNumerator) / m_maxBlockSizeGrowthSpeedDenominator); - assert(maxSize >= m_maxBlockSizeInitial); - return maxSize; - } + uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize); + uint64_t penalizedFee = penalizeFee ? getPenalizedAmount(fee, medianSize, currentBlockSize) : fee; - bool Currency::constructMinerTx(size_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, - uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, - const blobdata& extraNonce/* = blobdata()*/, size_t maxOuts/* = 1*/, - bool penalizeFee/* = false*/) const { - tx.vin.clear(); - tx.vout.clear(); - tx.extra.clear(); + emissionChange = penalizedBaseReward - (fee - penalizedFee); + reward = penalizedBaseReward + penalizedFee; - KeyPair txkey = KeyPair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - if (!extraNonce.empty()) { - if (!add_extra_nonce_to_tx_extra(tx.extra, extraNonce)) { - return false; - } - } + return true; +} - TransactionInputGenerate in; - in.height = height; +size_t Currency::maxBlockCumulativeSize(uint64_t height) const { + assert(height <= std::numeric_limits::max() / m_maxBlockSizeGrowthSpeedNumerator); + size_t maxSize = static_cast(m_maxBlockSizeInitial + + (height * m_maxBlockSizeGrowthSpeedNumerator) / m_maxBlockSizeGrowthSpeedDenominator); + assert(maxSize >= m_maxBlockSizeInitial); + return maxSize; +} - uint64_t blockReward; - int64_t emissionChange; - if (!getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, blockReward, emissionChange)) { - LOG_PRINT_L0("Block is too big"); - return false; - } +bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, + uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, + const blobdata& extraNonce/* = blobdata()*/, size_t maxOuts/* = 1*/, + bool penalizeFee/* = false*/) const { + tx.vin.clear(); + tx.vout.clear(); + tx.extra.clear(); + + KeyPair txkey = KeyPair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + if (!extraNonce.empty()) { + if (!add_extra_nonce_to_tx_extra(tx.extra, extraNonce)) { + return false; + } + } + + TransactionInputGenerate in; + in.height = height; + + uint64_t blockReward; + int64_t emissionChange; + if (!getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, blockReward, emissionChange)) { + logger(INFO) << "Block is too big"; + return false; + } #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: reward " << blockReward << ", fee " << fee); + logger(DEBUGGING) << "Creating block template: reward " << blockReward << ", fee " << fee; #endif - std::vector outAmounts; - decompose_amount_into_digits(blockReward, m_defaultDustThreshold, - [&outAmounts](uint64_t a_chunk) { outAmounts.push_back(a_chunk); }, - [&outAmounts](uint64_t a_dust) { outAmounts.push_back(a_dust); }); + std::vector outAmounts; + decompose_amount_into_digits(blockReward, m_defaultDustThreshold, + [&outAmounts](uint64_t a_chunk) { outAmounts.push_back(a_chunk); }, + [&outAmounts](uint64_t a_dust) { outAmounts.push_back(a_dust); }); - CHECK_AND_ASSERT_MES(1 <= maxOuts, false, "max_out must be non-zero"); - while (maxOuts < outAmounts.size()) { - outAmounts[outAmounts.size() - 2] += outAmounts.back(); - outAmounts.resize(outAmounts.size() - 1); - } + if (!(1 <= maxOuts)) { logger(ERROR, BRIGHT_RED) << "max_out must be non-zero"; return false; } + while (maxOuts < outAmounts.size()) { + outAmounts[outAmounts.size() - 2] += outAmounts.back(); + outAmounts.resize(outAmounts.size() - 1); + } + + uint64_t summaryAmounts = 0; + for (size_t no = 0; no < outAmounts.size(); no++) { + crypto::key_derivation derivation = boost::value_initialized(); + crypto::public_key outEphemeralPubKey = boost::value_initialized(); + + bool r = crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); - uint64_t summaryAmounts = 0; - for (size_t no = 0; no < outAmounts.size(); no++) { - crypto::key_derivation derivation = boost::value_initialized(); - crypto::public_key outEphemeralPubKey = boost::value_initialized(); - bool r = crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << - minerAddress.m_viewPublicKey << ", " << txkey.sec << ")"); + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "while creating outs: failed to generate_key_derivation(" + << minerAddress.m_viewPublicKey << ", " << txkey.sec << ")"; + return false; + } - r = crypto::derive_public_key(derivation, no, minerAddress.m_spendPublicKey, outEphemeralPubKey); - CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << - no << ", "<< minerAddress.m_spendPublicKey << ")"); + r = crypto::derive_public_key(derivation, no, minerAddress.m_spendPublicKey, outEphemeralPubKey); - TransactionOutputToKey tk; - tk.key = outEphemeralPubKey; + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "while creating outs: failed to derive_public_key(" + << derivation << ", " << no << ", " + << minerAddress.m_spendPublicKey << ")"; + return false; + } - TransactionOutput out; - summaryAmounts += out.amount = outAmounts[no]; - out.target = tk; - tx.vout.push_back(out); - } + TransactionOutputToKey tk; + tk.key = outEphemeralPubKey; - CHECK_AND_ASSERT_MES(summaryAmounts == blockReward, false, - "Failed to construct miner tx, summaryAmounts = " << summaryAmounts << " not equal blockReward = " << blockReward); + TransactionOutput out; + summaryAmounts += out.amount = outAmounts[no]; + out.target = tk; + tx.vout.push_back(out); + } - tx.version = CURRENT_TRANSACTION_VERSION; - //lock - tx.unlockTime = height + m_minedMoneyUnlockWindow; - tx.vin.push_back(in); - return true; + if (!(summaryAmounts == blockReward)) { + logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, summaryAmounts = " << summaryAmounts << " not equal blockReward = " << blockReward; + return false; } - std::string Currency::accountAddressAsString(const account_base& account) const { - return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.get_keys().m_account_address); + tx.version = CURRENT_TRANSACTION_VERSION; + //lock + tx.unlockTime = height + m_minedMoneyUnlockWindow; + tx.vin.push_back(in); + return true; +} + +std::string Currency::accountAddressAsString(const account_base& account) const { + return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.get_keys().m_account_address); +} + +bool Currency::parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const { + uint64_t prefix; + if (!CryptoNote::parseAccountAddressString(prefix, addr, str)) { + return false; } - bool Currency::parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const { - uint64_t prefix; - if (!cryptonote::parseAccountAddressString(prefix, addr, str)) { - return false; - } + if (prefix != m_publicAddressBase58Prefix) { + logger(DEBUGGING) << "Wrong address prefix: " << prefix << ", expected " << m_publicAddressBase58Prefix; + return false; + } - if (prefix != m_publicAddressBase58Prefix) { - LOG_PRINT_L1("Wrong address prefix: " << prefix << ", expected " << m_publicAddressBase58Prefix); - return false; - } + return true; +} - return true; +std::string Currency::formatAmount(uint64_t amount) const { + std::string s = std::to_string(amount); + if (s.size() < m_numberOfDecimalPlaces + 1) { + s.insert(0, m_numberOfDecimalPlaces + 1 - s.size(), '0'); } + s.insert(s.size() - m_numberOfDecimalPlaces, "."); + return s; +} - std::string Currency::formatAmount(uint64_t amount) const { - std::string s = std::to_string(amount); - if (s.size() < m_numberOfDecimalPlaces + 1) { - s.insert(0, m_numberOfDecimalPlaces + 1 - s.size(), '0'); +bool Currency::parseAmount(const std::string& str, uint64_t& amount) const { + std::string strAmount = str; + boost::algorithm::trim(strAmount); + + size_t pointIndex = strAmount.find_first_of('.'); + size_t fractionSize; + if (std::string::npos != pointIndex) { + fractionSize = strAmount.size() - pointIndex - 1; + while (m_numberOfDecimalPlaces < fractionSize && '0' == strAmount.back()) { + strAmount.erase(strAmount.size() - 1, 1); + --fractionSize; } - s.insert(s.size() - m_numberOfDecimalPlaces, "."); - return s; - } - - bool Currency::parseAmount(const std::string& str, uint64_t& amount) const { - std::string strAmount = str; - boost::algorithm::trim(strAmount); - - size_t pointIndex = strAmount.find_first_of('.'); - size_t fractionSize; - if (std::string::npos != pointIndex) { - fractionSize = strAmount.size() - pointIndex - 1; - while (m_numberOfDecimalPlaces < fractionSize && '0' == strAmount.back()) { - strAmount.erase(strAmount.size() - 1, 1); - --fractionSize; - } - if (m_numberOfDecimalPlaces < fractionSize) { - return false; - } - strAmount.erase(pointIndex, 1); - } else { - fractionSize = 0; - } - - if (strAmount.empty()) { + if (m_numberOfDecimalPlaces < fractionSize) { return false; } + strAmount.erase(pointIndex, 1); + } else { + fractionSize = 0; + } - if (fractionSize < m_numberOfDecimalPlaces) { - strAmount.append(m_numberOfDecimalPlaces - fractionSize, '0'); - } + if (strAmount.empty()) { + return false; + } - return epee::string_tools::get_xtype_from_string(amount, strAmount); + if (!std::all_of(strAmount.begin(), strAmount.end(), ::isdigit)) { + return false; } - difficulty_type Currency::nextDifficulty(std::vector timestamps, - std::vector cumulativeDifficulties) const { - assert(m_difficultyWindow >= 2); + if (fractionSize < m_numberOfDecimalPlaces) { + strAmount.append(m_numberOfDecimalPlaces - fractionSize, '0'); + } - if (timestamps.size() > m_difficultyWindow) { - timestamps.resize(m_difficultyWindow); - cumulativeDifficulties.resize(m_difficultyWindow); - } + return Common::fromString(strAmount, amount); +} - size_t length = timestamps.size(); - assert(length == cumulativeDifficulties.size()); - assert(length <= m_difficultyWindow); - if (length <= 1) { - return 1; - } +difficulty_type Currency::nextDifficulty(std::vector timestamps, + std::vector cumulativeDifficulties) const { + assert(m_difficultyWindow >= 2); - sort(timestamps.begin(), timestamps.end()); + if (timestamps.size() > m_difficultyWindow) { + timestamps.resize(m_difficultyWindow); + cumulativeDifficulties.resize(m_difficultyWindow); + } - size_t cutBegin, cutEnd; - assert(2 * m_difficultyCut <= m_difficultyWindow - 2); - if (length <= m_difficultyWindow - 2 * m_difficultyCut) { - cutBegin = 0; - cutEnd = length; - } else { - cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2; - cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut); - } - assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length); - uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin]; - if (timeSpan == 0) { - timeSpan = 1; - } + size_t length = timestamps.size(); + assert(length == cumulativeDifficulties.size()); + assert(length <= m_difficultyWindow); + if (length <= 1) { + return 1; + } - difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin]; - assert(totalWork > 0); + sort(timestamps.begin(), timestamps.end()); - uint64_t low, high; - low = mul128(totalWork, m_difficultyTarget, &high); - if (high != 0 || low + timeSpan - 1 < low) { - return 0; - } + size_t cutBegin, cutEnd; + assert(2 * m_difficultyCut <= m_difficultyWindow - 2); + if (length <= m_difficultyWindow - 2 * m_difficultyCut) { + cutBegin = 0; + cutEnd = length; + } else { + cutBegin = (length - (m_difficultyWindow - 2 * m_difficultyCut) + 1) / 2; + cutEnd = cutBegin + (m_difficultyWindow - 2 * m_difficultyCut); + } + assert(/*cut_begin >= 0 &&*/ cutBegin + 2 <= cutEnd && cutEnd <= length); + uint64_t timeSpan = timestamps[cutEnd - 1] - timestamps[cutBegin]; + if (timeSpan == 0) { + timeSpan = 1; + } - return (low + timeSpan - 1) / timeSpan; + difficulty_type totalWork = cumulativeDifficulties[cutEnd - 1] - cumulativeDifficulties[cutBegin]; + assert(totalWork > 0); + + uint64_t low, high; + low = mul128(totalWork, m_difficultyTarget, &high); + if (high != 0 || low + timeSpan - 1 < low) { + return 0; } - bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, - crypto::hash& proofOfWork) const { - if (BLOCK_MAJOR_VERSION_1 != block.majorVersion) { - return false; - } + return (low + timeSpan - 1) / timeSpan; +} - if (!get_block_longhash(context, block, proofOfWork)) { - return false; - } +bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + crypto::hash& proofOfWork) const { + if (BLOCK_MAJOR_VERSION_1 != block.majorVersion) { + return false; + } - return check_hash(proofOfWork, currentDiffic); + if (!get_block_longhash(context, block, proofOfWork)) { + return false; } - bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, - crypto::hash& proofOfWork) const { - if (BLOCK_MAJOR_VERSION_2 != block.majorVersion) { - return false; - } + return check_hash(proofOfWork, currentDiffic); +} - if (!get_block_longhash(context, block, proofOfWork)) { - return false; - } +bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + crypto::hash& proofOfWork) const { + if (BLOCK_MAJOR_VERSION_2 != block.majorVersion) { + return false; + } - if (!check_hash(proofOfWork, currentDiffic)) { - return false; - } + if (!get_block_longhash(context, block, proofOfWork)) { + return false; + } - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mmTag)) { - LOG_ERROR("merge mining tag wasn't found in extra of the parent block miner transaction"); - return false; - } + if (!check_hash(proofOfWork, currentDiffic)) { + return false; + } - if (8 * sizeof(m_genesisBlockHash) < block.parentBlock.blockchainBranch.size()) { - return false; - } + tx_extra_merge_mining_tag mmTag; + if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mmTag)) { + logger(ERROR) << "merge mining tag wasn't found in extra of the parent block miner transaction"; + return false; + } - crypto::hash auxBlockHeaderHash; - if (!get_aux_block_header_hash(block, auxBlockHeaderHash)) { - return false; - } + if (8 * sizeof(m_genesisBlockHash) < block.parentBlock.blockchainBranch.size()) { + return false; + } - crypto::hash auxBlocksMerkleRoot; - crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), - auxBlockHeaderHash, &m_genesisBlockHash, auxBlocksMerkleRoot); - CHECK_AND_NO_ASSERT_MES(auxBlocksMerkleRoot == mmTag.merkle_root, false, "Aux block hash wasn't found in merkle tree"); + crypto::hash auxBlockHeaderHash; + if (!get_aux_block_header_hash(block, auxBlockHeaderHash)) { + return false; + } + + crypto::hash auxBlocksMerkleRoot; + crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), + auxBlockHeaderHash, &m_genesisBlockHash, auxBlocksMerkleRoot); - return true; + if (auxBlocksMerkleRoot != mmTag.merkle_root) { + logger(ERROR, BRIGHT_YELLOW) << "Aux block hash wasn't found in merkle tree"; + return false; } - bool Currency::checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const { - switch (block.majorVersion) { - case BLOCK_MAJOR_VERSION_1: return checkProofOfWorkV1(context, block, currentDiffic, proofOfWork); - case BLOCK_MAJOR_VERSION_2: return checkProofOfWorkV2(context, block, currentDiffic, proofOfWork); - } + return true; +} - CHECK_AND_ASSERT_MES(false, false, "Unknown block major version: " << block.majorVersion << "." << block.minorVersion); +bool Currency::checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const { + switch (block.majorVersion) { + case BLOCK_MAJOR_VERSION_1: return checkProofOfWorkV1(context, block, currentDiffic, proofOfWork); + case BLOCK_MAJOR_VERSION_2: return checkProofOfWorkV2(context, block, currentDiffic, proofOfWork); } - CurrencyBuilder::CurrencyBuilder() { - maxBlockNumber(parameters::CRYPTONOTE_MAX_BLOCK_NUMBER); - maxBlockBlobSize(parameters::CRYPTONOTE_MAX_BLOCK_BLOB_SIZE); - maxTxSize(parameters::CRYPTONOTE_MAX_TX_SIZE); - publicAddressBase58Prefix(parameters::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); - minedMoneyUnlockWindow(parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + logger(ERROR, BRIGHT_RED) << "Unknown block major version: " << block.majorVersion << "." << block.minorVersion; + return false; +} - timestampCheckWindow(parameters::BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW); - blockFutureTimeLimit(parameters::CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT); +CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) { + maxBlockNumber(parameters::CRYPTONOTE_MAX_BLOCK_NUMBER); + maxBlockBlobSize(parameters::CRYPTONOTE_MAX_BLOCK_BLOB_SIZE); + maxTxSize(parameters::CRYPTONOTE_MAX_TX_SIZE); + publicAddressBase58Prefix(parameters::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + minedMoneyUnlockWindow(parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); - moneySupply(parameters::MONEY_SUPPLY); - emissionSpeedFactor(parameters::EMISSION_SPEED_FACTOR); + timestampCheckWindow(parameters::BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW); + blockFutureTimeLimit(parameters::CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT); - rewardBlocksWindow(parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW); - blockGrantedFullRewardZone(parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); - minerTxBlobReservedSize(parameters::CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + moneySupply(parameters::MONEY_SUPPLY); + emissionSpeedFactor(parameters::EMISSION_SPEED_FACTOR); - numberOfDecimalPlaces(parameters::CRYPTONOTE_DISPLAY_DECIMAL_POINT); + rewardBlocksWindow(parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW); + blockGrantedFullRewardZone(parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); + minerTxBlobReservedSize(parameters::CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); - mininumFee(parameters::MINIMUM_FEE); - defaultDustThreshold(parameters::DEFAULT_DUST_THRESHOLD); + numberOfDecimalPlaces(parameters::CRYPTONOTE_DISPLAY_DECIMAL_POINT); - difficultyTarget(parameters::DIFFICULTY_TARGET); - difficultyWindow(parameters::DIFFICULTY_WINDOW); - difficultyLag(parameters::DIFFICULTY_LAG); - difficultyCut(parameters::DIFFICULTY_CUT); + mininumFee(parameters::MINIMUM_FEE); + defaultDustThreshold(parameters::DEFAULT_DUST_THRESHOLD); - maxBlockSizeInitial(parameters::MAX_BLOCK_SIZE_INITIAL); - maxBlockSizeGrowthSpeedNumerator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR); - maxBlockSizeGrowthSpeedDenominator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR); + difficultyTarget(parameters::DIFFICULTY_TARGET); + difficultyWindow(parameters::DIFFICULTY_WINDOW); + difficultyLag(parameters::DIFFICULTY_LAG); + difficultyCut(parameters::DIFFICULTY_CUT); - lockedTxAllowedDeltaSeconds(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS); - lockedTxAllowedDeltaBlocks(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS); + maxBlockSizeInitial(parameters::MAX_BLOCK_SIZE_INITIAL); + maxBlockSizeGrowthSpeedNumerator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_NUMERATOR); + maxBlockSizeGrowthSpeedDenominator(parameters::MAX_BLOCK_SIZE_GROWTH_SPEED_DENOMINATOR); - mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME); - mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); + lockedTxAllowedDeltaSeconds(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS); + lockedTxAllowedDeltaBlocks(parameters::CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS); - upgradeHeight(parameters::UPGRADE_HEIGHT); - upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); - upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); - upgradeWindow(parameters::UPGRADE_WINDOW); + mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME); + mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); - blocksFileName(parameters::CRYPTONOTE_BLOCKS_FILENAME); - blocksCacheFileName(parameters::CRYPTONOTE_BLOCKSCACHE_FILENAME); - blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME); - txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME); + upgradeHeight(parameters::UPGRADE_HEIGHT); + upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); + upgradeVotingWindow(parameters::UPGRADE_VOTING_WINDOW); + upgradeWindow(parameters::UPGRADE_WINDOW); - testnet(false); - } + blocksFileName(parameters::CRYPTONOTE_BLOCKS_FILENAME); + blocksCacheFileName(parameters::CRYPTONOTE_BLOCKSCACHE_FILENAME); + blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME); + txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME); - CurrencyBuilder& CurrencyBuilder::emissionSpeedFactor(unsigned int val) { - if (val <= 0 || val > 8 * sizeof(uint64_t)) { - throw std::invalid_argument("val at emissionSpeedFactor()"); - } + testnet(false); +} - m_currency.m_emissionSpeedFactor = val; - return *this; +CurrencyBuilder& CurrencyBuilder::emissionSpeedFactor(unsigned int val) { + if (val <= 0 || val > 8 * sizeof(uint64_t)) { + throw std::invalid_argument("val at emissionSpeedFactor()"); } - CurrencyBuilder& CurrencyBuilder::numberOfDecimalPlaces(size_t val) { - m_currency.m_numberOfDecimalPlaces = val; - m_currency.m_coin = 1; - for (size_t i = 0; i < m_currency.m_numberOfDecimalPlaces; ++i) { - m_currency.m_coin *= 10; - } + m_currency.m_emissionSpeedFactor = val; + return *this; +} - return *this; +CurrencyBuilder& CurrencyBuilder::numberOfDecimalPlaces(size_t val) { + m_currency.m_numberOfDecimalPlaces = val; + m_currency.m_coin = 1; + for (size_t i = 0; i < m_currency.m_numberOfDecimalPlaces; ++i) { + m_currency.m_coin *= 10; } - CurrencyBuilder& CurrencyBuilder::difficultyWindow(size_t val) { - if (val < 2) { - throw std::invalid_argument("val at difficultyWindow()"); - } - m_currency.m_difficultyWindow = val; - return *this; + return *this; +} + +CurrencyBuilder& CurrencyBuilder::difficultyWindow(size_t val) { + if (val < 2) { + throw std::invalid_argument("val at difficultyWindow()"); } + m_currency.m_difficultyWindow = val; + return *this; +} - CurrencyBuilder& CurrencyBuilder::upgradeVotingThreshold(unsigned int val) { - if (val <= 0 || val > 100) { - throw std::invalid_argument("val at upgradeVotingThreshold()"); - } - m_currency.m_upgradeVotingThreshold = val; - return *this; +CurrencyBuilder& CurrencyBuilder::upgradeVotingThreshold(unsigned int val) { + if (val <= 0 || val > 100) { + throw std::invalid_argument("val at upgradeVotingThreshold()"); } - CurrencyBuilder& CurrencyBuilder::upgradeWindow(size_t val) { - if (val <= 0) { - throw std::invalid_argument("val at upgradeWindow()"); - } - m_currency.m_upgradeWindow = val; - return *this; + m_currency.m_upgradeVotingThreshold = val; + return *this; +} + +CurrencyBuilder& CurrencyBuilder::upgradeWindow(size_t val) { + if (val <= 0) { + throw std::invalid_argument("val at upgradeWindow()"); } + + m_currency.m_upgradeWindow = val; + return *this; +} + } diff --git a/src/cryptonote_core/Currency.h b/src/cryptonote_core/Currency.h index bbdffd3980..c939409fd4 100644 --- a/src/cryptonote_core/Currency.h +++ b/src/cryptonote_core/Currency.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,219 +20,221 @@ #include #include #include - #include +#include "../cryptonote_config.h" +#include "../crypto/hash.h" +#include "../cryptonote_protocol/blobdatatype.h" +#include "../Logging/LoggerRef.h" +#include "cryptonote_basic.h" +#include "difficulty.h" -#include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_config.h" -#include "cryptonote_protocol/blobdatatype.h" +namespace CryptoNote { -namespace cryptonote -{ - class Currency { - public: - uint64_t maxBlockHeight() const { return m_maxBlockHeight; } - size_t maxBlockBlobSize() const { return m_maxBlockBlobSize; } - size_t maxTxSize() const { return m_maxTxSize; } - uint64_t publicAddressBase58Prefix() const { return m_publicAddressBase58Prefix; } - size_t minedMoneyUnlockWindow() const { return m_minedMoneyUnlockWindow; } +class Currency { +public: + uint64_t maxBlockHeight() const { return m_maxBlockHeight; } + size_t maxBlockBlobSize() const { return m_maxBlockBlobSize; } + size_t maxTxSize() const { return m_maxTxSize; } + uint64_t publicAddressBase58Prefix() const { return m_publicAddressBase58Prefix; } + size_t minedMoneyUnlockWindow() const { return m_minedMoneyUnlockWindow; } - size_t timestampCheckWindow() const { return m_timestampCheckWindow; } - uint64_t blockFutureTimeLimit() const { return m_blockFutureTimeLimit; } + size_t timestampCheckWindow() const { return m_timestampCheckWindow; } + uint64_t blockFutureTimeLimit() const { return m_blockFutureTimeLimit; } - uint64_t moneySupply() const { return m_moneySupply; } - unsigned int emissionSpeedFactor() const { return m_emissionSpeedFactor; } + uint64_t moneySupply() const { return m_moneySupply; } + unsigned int emissionSpeedFactor() const { return m_emissionSpeedFactor; } - size_t rewardBlocksWindow() const { return m_rewardBlocksWindow; } - size_t blockGrantedFullRewardZone() const { return m_blockGrantedFullRewardZone; } - size_t minerTxBlobReservedSize() const { return m_minerTxBlobReservedSize; } + size_t rewardBlocksWindow() const { return m_rewardBlocksWindow; } + size_t blockGrantedFullRewardZone() const { return m_blockGrantedFullRewardZone; } + size_t minerTxBlobReservedSize() const { return m_minerTxBlobReservedSize; } - size_t numberOfDecimalPlaces() const { return m_numberOfDecimalPlaces; } - uint64_t coin() const { return m_coin; } + size_t numberOfDecimalPlaces() const { return m_numberOfDecimalPlaces; } + uint64_t coin() const { return m_coin; } - uint64_t minimumFee() const { return m_mininumFee; } - uint64_t defaultDustThreshold() const { return m_defaultDustThreshold; } + uint64_t minimumFee() const { return m_mininumFee; } + uint64_t defaultDustThreshold() const { return m_defaultDustThreshold; } - uint64_t difficultyTarget() const { return m_difficultyTarget; } - size_t difficultyWindow() const { return m_difficultyWindow; } - size_t difficultyLag() const { return m_difficultyLag; } - size_t difficultyCut() const { return m_difficultyCut; } - size_t difficultyBlocksCount() const { return m_difficultyWindow + m_difficultyLag; } + uint64_t difficultyTarget() const { return m_difficultyTarget; } + size_t difficultyWindow() const { return m_difficultyWindow; } + size_t difficultyLag() const { return m_difficultyLag; } + size_t difficultyCut() const { return m_difficultyCut; } + size_t difficultyBlocksCount() const { return m_difficultyWindow + m_difficultyLag; } - size_t maxBlockSizeInitial() const { return m_maxBlockSizeInitial; } - uint64_t maxBlockSizeGrowthSpeedNumerator() const { return m_maxBlockSizeGrowthSpeedNumerator; } - uint64_t maxBlockSizeGrowthSpeedDenominator() const { return m_maxBlockSizeGrowthSpeedDenominator; } + size_t maxBlockSizeInitial() const { return m_maxBlockSizeInitial; } + uint64_t maxBlockSizeGrowthSpeedNumerator() const { return m_maxBlockSizeGrowthSpeedNumerator; } + uint64_t maxBlockSizeGrowthSpeedDenominator() const { return m_maxBlockSizeGrowthSpeedDenominator; } - uint64_t lockedTxAllowedDeltaSeconds() const { return m_lockedTxAllowedDeltaSeconds; } - size_t lockedTxAllowedDeltaBlocks() const { return m_lockedTxAllowedDeltaBlocks; } + uint64_t lockedTxAllowedDeltaSeconds() const { return m_lockedTxAllowedDeltaSeconds; } + size_t lockedTxAllowedDeltaBlocks() const { return m_lockedTxAllowedDeltaBlocks; } - uint64_t mempoolTxLiveTime() const { return m_mempoolTxLiveTime; } - uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; } + uint64_t mempoolTxLiveTime() const { return m_mempoolTxLiveTime; } + uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; } - uint64_t upgradeHeight() const { return m_upgradeHeight; } - unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; } - size_t upgradeVotingWindow() const { return m_upgradeVotingWindow; } - size_t upgradeWindow() const { return m_upgradeWindow; } - size_t minNumberVotingBlocks() const { return (m_upgradeVotingWindow * m_upgradeVotingThreshold + 99) / 100; } - uint64_t maxUpgradeDistance() const { return static_cast(m_upgradeWindow); } - uint64_t calculateUpgradeHeight(uint64_t voteCompleteHeight) const { return voteCompleteHeight + m_upgradeWindow; } + uint64_t upgradeHeight() const { return m_upgradeHeight; } + unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; } + size_t upgradeVotingWindow() const { return m_upgradeVotingWindow; } + size_t upgradeWindow() const { return m_upgradeWindow; } + size_t minNumberVotingBlocks() const { return (m_upgradeVotingWindow * m_upgradeVotingThreshold + 99) / 100; } + uint64_t maxUpgradeDistance() const { return static_cast(m_upgradeWindow); } + uint64_t calculateUpgradeHeight(uint64_t voteCompleteHeight) const { return voteCompleteHeight + m_upgradeWindow; } - const std::string& blocksFileName() const { return m_blocksFileName; } - const std::string& blocksCacheFileName() const { return m_blocksCacheFileName; } - const std::string& blockIndexesFileName() const { return m_blockIndexesFileName; } - const std::string& txPoolFileName() const { return m_txPoolFileName; } + const std::string& blocksFileName() const { return m_blocksFileName; } + const std::string& blocksCacheFileName() const { return m_blocksCacheFileName; } + const std::string& blockIndexesFileName() const { return m_blockIndexesFileName; } + const std::string& txPoolFileName() const { return m_txPoolFileName; } - bool isTestnet() const { return m_testnet; } + bool isTestnet() const { return m_testnet; } - const Block& genesisBlock() const { return m_genesisBlock; } - const crypto::hash& genesisBlockHash() const { return m_genesisBlockHash; } + const Block& genesisBlock() const { return m_genesisBlock; } + const crypto::hash& genesisBlockHash() const { return m_genesisBlockHash; } - bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const; - size_t maxBlockCumulativeSize(uint64_t height) const; + bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const; + size_t maxBlockCumulativeSize(uint64_t height) const; - bool constructMinerTx(size_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, - uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, - const blobdata& extraNonce = blobdata(), size_t maxOuts = 1, bool penalizeFee = false) const; + bool constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, + uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, + const blobdata& extraNonce = blobdata(), size_t maxOuts = 1, bool penalizeFee = false) const; - std::string accountAddressAsString(const account_base& account) const; - bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; + std::string accountAddressAsString(const account_base& account) const; + bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; - std::string formatAmount(uint64_t amount) const; - bool parseAmount(const std::string& str, uint64_t& amount) const; + std::string formatAmount(uint64_t amount) const; + bool parseAmount(const std::string& str, uint64_t& amount) const; - difficulty_type nextDifficulty(std::vector timestamps, std::vector cumulativeDifficulties) const; + difficulty_type nextDifficulty(std::vector timestamps, std::vector cumulativeDifficulties) const; - bool checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - bool checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - bool checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - private: - Currency() { - } +private: + Currency(Logging::ILogger& log) : logger(log, "currency") { + } - bool init(); + bool init(); - bool generateGenesisBlock(); + bool generateGenesisBlock(); - private: - uint64_t m_maxBlockHeight; - size_t m_maxBlockBlobSize; - size_t m_maxTxSize; - uint64_t m_publicAddressBase58Prefix; - size_t m_minedMoneyUnlockWindow; +private: + uint64_t m_maxBlockHeight; + size_t m_maxBlockBlobSize; + size_t m_maxTxSize; + uint64_t m_publicAddressBase58Prefix; + size_t m_minedMoneyUnlockWindow; - size_t m_timestampCheckWindow; - uint64_t m_blockFutureTimeLimit; + size_t m_timestampCheckWindow; + uint64_t m_blockFutureTimeLimit; - uint64_t m_moneySupply; - unsigned int m_emissionSpeedFactor; + uint64_t m_moneySupply; + unsigned int m_emissionSpeedFactor; - size_t m_rewardBlocksWindow; - size_t m_blockGrantedFullRewardZone; - size_t m_minerTxBlobReservedSize; + size_t m_rewardBlocksWindow; + size_t m_blockGrantedFullRewardZone; + size_t m_minerTxBlobReservedSize; - size_t m_numberOfDecimalPlaces; - uint64_t m_coin; + size_t m_numberOfDecimalPlaces; + uint64_t m_coin; - uint64_t m_mininumFee; - uint64_t m_defaultDustThreshold; + uint64_t m_mininumFee; + uint64_t m_defaultDustThreshold; - uint64_t m_difficultyTarget; - size_t m_difficultyWindow; - size_t m_difficultyLag; - size_t m_difficultyCut; + uint64_t m_difficultyTarget; + size_t m_difficultyWindow; + size_t m_difficultyLag; + size_t m_difficultyCut; - size_t m_maxBlockSizeInitial; - uint64_t m_maxBlockSizeGrowthSpeedNumerator; - uint64_t m_maxBlockSizeGrowthSpeedDenominator; + size_t m_maxBlockSizeInitial; + uint64_t m_maxBlockSizeGrowthSpeedNumerator; + uint64_t m_maxBlockSizeGrowthSpeedDenominator; - uint64_t m_lockedTxAllowedDeltaSeconds; - size_t m_lockedTxAllowedDeltaBlocks; + uint64_t m_lockedTxAllowedDeltaSeconds; + size_t m_lockedTxAllowedDeltaBlocks; - uint64_t m_mempoolTxLiveTime; - uint64_t m_mempoolTxFromAltBlockLiveTime; + uint64_t m_mempoolTxLiveTime; + uint64_t m_mempoolTxFromAltBlockLiveTime; - uint64_t m_upgradeHeight; - unsigned int m_upgradeVotingThreshold; - size_t m_upgradeVotingWindow; - size_t m_upgradeWindow; + uint64_t m_upgradeHeight; + unsigned int m_upgradeVotingThreshold; + size_t m_upgradeVotingWindow; + size_t m_upgradeWindow; - std::string m_blocksFileName; - std::string m_blocksCacheFileName; - std::string m_blockIndexesFileName; - std::string m_txPoolFileName; + std::string m_blocksFileName; + std::string m_blocksCacheFileName; + std::string m_blockIndexesFileName; + std::string m_txPoolFileName; - bool m_testnet; + bool m_testnet; - Block m_genesisBlock; - crypto::hash m_genesisBlockHash; + Block m_genesisBlock; + crypto::hash m_genesisBlockHash; - friend class CurrencyBuilder; - }; + Logging::LoggerRef logger; - class CurrencyBuilder : boost::noncopyable { - public: - CurrencyBuilder(); + friend class CurrencyBuilder; +}; - Currency currency() { - if (!m_currency.init()) { - throw std::runtime_error("Failed to initialize currency object"); - } - return m_currency; +class CurrencyBuilder : boost::noncopyable { +public: + CurrencyBuilder(Logging::ILogger& log); + + Currency currency() { + if (!m_currency.init()) { + throw std::runtime_error("Failed to initialize currency object"); } + return m_currency; + } + + CurrencyBuilder& maxBlockNumber(uint64_t val) { m_currency.m_maxBlockHeight = val; return *this; } + CurrencyBuilder& maxBlockBlobSize(size_t val) { m_currency.m_maxBlockBlobSize = val; return *this; } + CurrencyBuilder& maxTxSize(size_t val) { m_currency.m_maxTxSize = val; return *this; } + CurrencyBuilder& publicAddressBase58Prefix(uint64_t val) { m_currency.m_publicAddressBase58Prefix = val; return *this; } + CurrencyBuilder& minedMoneyUnlockWindow(size_t val) { m_currency.m_minedMoneyUnlockWindow = val; return *this; } - CurrencyBuilder& maxBlockNumber(uint64_t val) { m_currency.m_maxBlockHeight = val; return *this; } - CurrencyBuilder& maxBlockBlobSize(size_t val) { m_currency.m_maxBlockBlobSize = val; return *this; } - CurrencyBuilder& maxTxSize(size_t val) { m_currency.m_maxTxSize = val; return *this; } - CurrencyBuilder& publicAddressBase58Prefix(uint64_t val) { m_currency.m_publicAddressBase58Prefix = val; return *this; } - CurrencyBuilder& minedMoneyUnlockWindow(size_t val) { m_currency.m_minedMoneyUnlockWindow = val; return *this; } + CurrencyBuilder& timestampCheckWindow(size_t val) { m_currency.m_timestampCheckWindow = val; return *this; } + CurrencyBuilder& blockFutureTimeLimit(uint64_t val) { m_currency.m_blockFutureTimeLimit = val; return *this; } - CurrencyBuilder& timestampCheckWindow(size_t val) { m_currency.m_timestampCheckWindow = val; return *this; } - CurrencyBuilder& blockFutureTimeLimit(uint64_t val) { m_currency.m_blockFutureTimeLimit = val; return *this; } + CurrencyBuilder& moneySupply(uint64_t val) { m_currency.m_moneySupply = val; return *this; } + CurrencyBuilder& emissionSpeedFactor(unsigned int val); - CurrencyBuilder& moneySupply(uint64_t val) { m_currency.m_moneySupply = val; return *this; } - CurrencyBuilder& emissionSpeedFactor(unsigned int val); + CurrencyBuilder& rewardBlocksWindow(size_t val) { m_currency.m_rewardBlocksWindow = val; return *this; } + CurrencyBuilder& blockGrantedFullRewardZone(size_t val) { m_currency.m_blockGrantedFullRewardZone = val; return *this; } + CurrencyBuilder& minerTxBlobReservedSize(size_t val) { m_currency.m_minerTxBlobReservedSize = val; return *this; } - CurrencyBuilder& rewardBlocksWindow(size_t val) { m_currency.m_rewardBlocksWindow = val; return *this; } - CurrencyBuilder& blockGrantedFullRewardZone(size_t val) { m_currency.m_blockGrantedFullRewardZone = val; return *this; } - CurrencyBuilder& minerTxBlobReservedSize(size_t val) { m_currency.m_minerTxBlobReservedSize = val; return *this; } + CurrencyBuilder& numberOfDecimalPlaces(size_t val); - CurrencyBuilder& numberOfDecimalPlaces(size_t val); + CurrencyBuilder& mininumFee(uint64_t val) { m_currency.m_mininumFee = val; return *this; } + CurrencyBuilder& defaultDustThreshold(uint64_t val) { m_currency.m_defaultDustThreshold = val; return *this; } - CurrencyBuilder& mininumFee(uint64_t val) { m_currency.m_mininumFee = val; return *this; } - CurrencyBuilder& defaultDustThreshold(uint64_t val) { m_currency.m_defaultDustThreshold = val; return *this; } + CurrencyBuilder& difficultyTarget(uint64_t val) { m_currency.m_difficultyTarget = val; return *this; } + CurrencyBuilder& difficultyWindow(size_t val); + CurrencyBuilder& difficultyLag(size_t val) { m_currency.m_difficultyLag = val; return *this; } + CurrencyBuilder& difficultyCut(size_t val) { m_currency.m_difficultyCut = val; return *this; } - CurrencyBuilder& difficultyTarget(uint64_t val) { m_currency.m_difficultyTarget = val; return *this; } - CurrencyBuilder& difficultyWindow(size_t val); - CurrencyBuilder& difficultyLag(size_t val) { m_currency.m_difficultyLag = val; return *this; } - CurrencyBuilder& difficultyCut(size_t val) { m_currency.m_difficultyCut = val; return *this; } + CurrencyBuilder& maxBlockSizeInitial(size_t val) { m_currency.m_maxBlockSizeInitial = val; return *this; } + CurrencyBuilder& maxBlockSizeGrowthSpeedNumerator(uint64_t val) { m_currency.m_maxBlockSizeGrowthSpeedNumerator = val; return *this; } + CurrencyBuilder& maxBlockSizeGrowthSpeedDenominator(uint64_t val) { m_currency.m_maxBlockSizeGrowthSpeedDenominator = val; return *this; } - CurrencyBuilder& maxBlockSizeInitial(size_t val) { m_currency.m_maxBlockSizeInitial = val; return *this; } - CurrencyBuilder& maxBlockSizeGrowthSpeedNumerator(uint64_t val) { m_currency.m_maxBlockSizeGrowthSpeedNumerator = val; return *this; } - CurrencyBuilder& maxBlockSizeGrowthSpeedDenominator(uint64_t val) { m_currency.m_maxBlockSizeGrowthSpeedDenominator = val; return *this; } + CurrencyBuilder& lockedTxAllowedDeltaSeconds(uint64_t val) { m_currency.m_lockedTxAllowedDeltaSeconds = val; return *this; } + CurrencyBuilder& lockedTxAllowedDeltaBlocks(size_t val) { m_currency.m_lockedTxAllowedDeltaBlocks = val; return *this; } - CurrencyBuilder& lockedTxAllowedDeltaSeconds(uint64_t val) { m_currency.m_lockedTxAllowedDeltaSeconds = val; return *this; } - CurrencyBuilder& lockedTxAllowedDeltaBlocks(size_t val) { m_currency.m_lockedTxAllowedDeltaBlocks = val; return *this; } + CurrencyBuilder& mempoolTxLiveTime(uint64_t val) { m_currency.m_mempoolTxLiveTime = val; return *this; } + CurrencyBuilder& mempoolTxFromAltBlockLiveTime(uint64_t val) { m_currency.m_mempoolTxFromAltBlockLiveTime = val; return *this; } - CurrencyBuilder& mempoolTxLiveTime(uint64_t val) { m_currency.m_mempoolTxLiveTime = val; return *this; } - CurrencyBuilder& mempoolTxFromAltBlockLiveTime(uint64_t val) { m_currency.m_mempoolTxFromAltBlockLiveTime = val; return *this; } + CurrencyBuilder& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; } + CurrencyBuilder& upgradeVotingThreshold(unsigned int val); + CurrencyBuilder& upgradeVotingWindow(size_t val) { m_currency.m_upgradeVotingWindow = val; return *this; } + CurrencyBuilder& upgradeWindow(size_t val); - CurrencyBuilder& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; } - CurrencyBuilder& upgradeVotingThreshold(unsigned int val); - CurrencyBuilder& upgradeVotingWindow(size_t val) { m_currency.m_upgradeVotingWindow = val; return *this; } - CurrencyBuilder& upgradeWindow(size_t val); + CurrencyBuilder& blocksFileName(const std::string& val) { m_currency.m_blocksFileName = val; return *this; } + CurrencyBuilder& blocksCacheFileName(const std::string& val) { m_currency.m_blocksCacheFileName = val; return *this; } + CurrencyBuilder& blockIndexesFileName(const std::string& val) { m_currency.m_blockIndexesFileName = val; return *this; } + CurrencyBuilder& txPoolFileName(const std::string& val) { m_currency.m_txPoolFileName = val; return *this; } - CurrencyBuilder& blocksFileName(const std::string& val) { m_currency.m_blocksFileName = val; return *this; } - CurrencyBuilder& blocksCacheFileName(const std::string& val) { m_currency.m_blocksCacheFileName = val; return *this; } - CurrencyBuilder& blockIndexesFileName(const std::string& val) { m_currency.m_blockIndexesFileName = val; return *this; } - CurrencyBuilder& txPoolFileName(const std::string& val) { m_currency.m_txPoolFileName = val; return *this; } + CurrencyBuilder& testnet(bool val) { m_currency.m_testnet = val; return *this; } - CurrencyBuilder& testnet(bool val) { m_currency.m_testnet = val; return *this; } +private: + Currency m_currency; +}; - private: - Currency m_currency; - }; } diff --git a/src/cryptonote_core/IBlockchainStorageObserver.h b/src/cryptonote_core/IBlockchainStorageObserver.h index 9136732a21..5707bb4cce 100644 --- a/src/cryptonote_core/IBlockchainStorageObserver.h +++ b/src/cryptonote_core/IBlockchainStorageObserver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,7 +15,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -namespace cryptonote { +#pragma once + +namespace CryptoNote { class IBlockchainStorageObserver { public: virtual ~IBlockchainStorageObserver() { diff --git a/src/cryptonote_core/ICore.h b/src/cryptonote_core/ICore.h index 38bddcadff..4674c30dc9 100755 --- a/src/cryptonote_core/ICore.h +++ b/src/cryptonote_core/ICore.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,17 +24,22 @@ #include "crypto/hash.h" #include "cryptonote_protocol/blobdatatype.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" + +namespace CryptoNote { -namespace cryptonote { struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; -struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; + struct Block; struct Transaction; struct i_cryptonote_protocol; struct tx_verification_context; +struct block_verification_context; +struct core_stat_info; struct BlockFullInfo; class ICoreObserver; +class Currency; class ICore { public: @@ -43,6 +48,17 @@ class ICore { virtual bool addObserver(ICoreObserver* observer) = 0; virtual bool removeObserver(ICoreObserver* observer) = 0; + virtual bool have_block(const crypto::hash& id) = 0; + virtual bool get_short_chain_history(std::list& ids) = 0; + virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) = 0; + virtual bool on_idle() = 0; + virtual void pause_mining() = 0; + virtual void update_block_template_and_resume_mining() = 0; + virtual bool handle_incoming_block_blob(const CryptoNote::blobdata& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; + virtual bool handle_get_objects(CryptoNote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) = 0; + virtual void on_synchronized() = 0; + virtual bool is_ready() = 0; + virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) = 0; virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) = 0; @@ -58,4 +74,4 @@ class ICore { virtual bool getBlockByHash(const crypto::hash &h, Block &blk) = 0; }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/ICoreObserver.h b/src/cryptonote_core/ICoreObserver.h index 9e8a2b3531..1392943a68 100644 --- a/src/cryptonote_core/ICoreObserver.h +++ b/src/cryptonote_core/ICoreObserver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #pragma once -namespace cryptonote { +namespace CryptoNote { class ICoreObserver { public: @@ -26,4 +26,4 @@ class ICoreObserver { virtual void poolUpdated() {}; }; -} //namespace cryptonote +} diff --git a/src/cryptonote_core/ITimeProvider.cpp b/src/cryptonote_core/ITimeProvider.cpp index 52d0481a56..fb8255ee65 100644 --- a/src/cryptonote_core/ITimeProvider.cpp +++ b/src/cryptonote_core/ITimeProvider.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/ITimeProvider.h b/src/cryptonote_core/ITimeProvider.h index fa650796c5..31bc34bde0 100644 --- a/src/cryptonote_core/ITimeProvider.h +++ b/src/cryptonote_core/ITimeProvider.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/ITransactionValidator.h b/src/cryptonote_core/ITransactionValidator.h index e0b974f5a3..825ed009a9 100644 --- a/src/cryptonote_core/ITransactionValidator.h +++ b/src/cryptonote_core/ITransactionValidator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -31,11 +31,11 @@ namespace CryptoNote { void clear() { height = 0; - id = cryptonote::null_hash; + id = CryptoNote::null_hash; } bool empty() const { - return id == cryptonote::null_hash; + return id == CryptoNote::null_hash; } }; @@ -43,9 +43,9 @@ namespace CryptoNote { public: virtual ~ITransactionValidator() {} - virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) = 0; - virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) = 0; - virtual bool haveSpentKeyImages(const cryptonote::Transaction& tx) = 0; + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) = 0; + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) = 0; + virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) = 0; }; } diff --git a/src/cryptonote_core/ITxPoolObserver.h b/src/cryptonote_core/ITxPoolObserver.h index 2cdf2caf77..5f0cad9ee8 100755 --- a/src/cryptonote_core/ITxPoolObserver.h +++ b/src/cryptonote_core/ITxPoolObserver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,7 +15,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -namespace cryptonote { +#pragma once + +namespace CryptoNote { class ITxPoolObserver { public: virtual ~ITxPoolObserver() { diff --git a/src/cryptonote_core/MinerConfig.cpp b/src/cryptonote_core/MinerConfig.cpp index a524cf8864..7290f3d0fa 100644 --- a/src/cryptonote_core/MinerConfig.cpp +++ b/src/cryptonote_core/MinerConfig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,9 +17,9 @@ #include "MinerConfig.h" -#include "common/command_line.h" +#include "Common/command_line.h" -namespace cryptonote { +namespace CryptoNote { namespace { const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; @@ -52,4 +52,3 @@ void MinerConfig::init(const boost::program_options::variables_map& options) { } } //namespace cryptonote - diff --git a/src/cryptonote_core/MinerConfig.h b/src/cryptonote_core/MinerConfig.h index ae9675d424..a220dd29e3 100644 --- a/src/cryptonote_core/MinerConfig.h +++ b/src/cryptonote_core/MinerConfig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include -namespace cryptonote { +namespace CryptoNote { class MinerConfig { public: diff --git a/src/cryptonote_core/OnceInInterval.h b/src/cryptonote_core/OnceInInterval.h new file mode 100644 index 0000000000..4663e22ef6 --- /dev/null +++ b/src/cryptonote_core/OnceInInterval.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace CryptoNote +{ + +class OnceInInterval { +public: + + OnceInInterval(unsigned interval, bool startNow = true) + : m_interval(interval), m_lastCalled(startNow ? 0 : time(nullptr)) {} + + template + bool call(F func) { + time_t currentTime = time(nullptr); + + if (currentTime - m_lastCalled > m_interval) { + bool res = func(); + time(&m_lastCalled); + return res; + } + + return true; + } + +private: + time_t m_lastCalled; + time_t m_interval; +}; + +} diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/cryptonote_core/SwappedMap.cpp index 2a1775be98..f7664278b9 100755 --- a/src/cryptonote_core/SwappedMap.cpp +++ b/src/cryptonote_core/SwappedMap.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/SwappedMap.h b/src/cryptonote_core/SwappedMap.h index ca45b59ea4..e9082c8657 100755 --- a/src/cryptonote_core/SwappedMap.h +++ b/src/cryptonote_core/SwappedMap.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/cryptonote_core/SwappedVector.cpp index 57d3eb7d87..44a24553c7 100755 --- a/src/cryptonote_core/SwappedVector.cpp +++ b/src/cryptonote_core/SwappedVector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/SwappedVector.h b/src/cryptonote_core/SwappedVector.h index c5fcc6ad4b..82e4318036 100755 --- a/src/cryptonote_core/SwappedVector.h +++ b/src/cryptonote_core/SwappedVector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/Transaction.cpp b/src/cryptonote_core/Transaction.cpp index ad15b0f3ca..cba7f5a915 100644 --- a/src/cryptonote_core/Transaction.cpp +++ b/src/cryptonote_core/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -27,7 +27,6 @@ namespace { - using namespace cryptonote; using namespace CryptoNote; void derivePublicKey(const AccountAddress& to, const crypto::secret_key& txKey, size_t outputIndex, crypto::public_key& ephemeralKey) { @@ -36,7 +35,7 @@ namespace { crypto::derive_public_key(derivation, outputIndex, *reinterpret_cast(&to.spendPublicKey), ephemeralKey); } - bool checkInputsKeyimagesDiff(const cryptonote::Transaction& tx) { + bool checkInputsKeyimagesDiff(const CryptoNote::Transaction& tx) { std::unordered_set ki; for (const auto& in : tx.vin) { if (in.type() == typeid(TransactionInputToKey)) { @@ -83,14 +82,14 @@ namespace { return TransactionTypes::InputType::Invalid; } - const TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index) { + const TransactionInput& getInputChecked(const CryptoNote::Transaction& transaction, size_t index) { if (transaction.vin.size() <= index) { throw std::runtime_error("Transaction input index out of range"); } return transaction.vin[index]; } - const TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { + const TransactionInput& getInputChecked(const CryptoNote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { const auto& input = getInputChecked(transaction, index); if (getTransactionInputType(input) != type) { throw std::runtime_error("Unexpected transaction input type"); @@ -110,14 +109,14 @@ namespace { return TransactionTypes::OutputType::Invalid; } - const TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index) { + const TransactionOutput& getOutputChecked(const CryptoNote::Transaction& transaction, size_t index) { if (transaction.vout.size() <= index) { throw std::runtime_error("Transaction output index out of range"); } return transaction.vout[index]; } - const TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { + const TransactionOutput& getOutputChecked(const CryptoNote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { const auto& output = getOutputChecked(transaction, index); if (getTransactionOutputType(output.target) != type) { throw std::runtime_error("Unexpected transaction output target type"); @@ -135,11 +134,11 @@ namespace CryptoNote { // class Transaction declaration //////////////////////////////////////////////////////////////////////// - class Transaction : public ITransaction { + class TransactionImpl : public ITransaction { public: - Transaction(); - Transaction(const Blob& txblob); - Transaction(const cryptonote::Transaction& tx); + TransactionImpl(); + TransactionImpl(const Blob& txblob); + TransactionImpl(const CryptoNote::Transaction& tx); // ITransactionReader virtual Hash getTransactionHash() const override; @@ -148,6 +147,7 @@ namespace CryptoNote { virtual uint64_t getUnlockTime() const override; virtual bool getPaymentId(Hash& hash) const override; virtual bool getExtraNonce(std::string& nonce) const override; + virtual Blob getExtra() const override; // inputs virtual size_t getInputCount() const override; @@ -182,13 +182,13 @@ namespace CryptoNote { // Inputs/Outputs virtual size_t addInput(const TransactionTypes::InputKey& input) override; - virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) override; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) override; virtual size_t addInput(const TransactionTypes::InputMultisignature& input) override; virtual size_t addOutput(uint64_t amount, const AccountAddress& to) override; virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) override; - virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) override; + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) override; virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) override; // secret key @@ -197,6 +197,8 @@ namespace CryptoNote { private: + void invalidateHash(); + std::vector& getSignatures(size_t input); const crypto::secret_key& txSecretKey() const { @@ -206,20 +208,15 @@ namespace CryptoNote { return *secretKey; } - cryptonote::Transaction constructFinalTransaction() const { - cryptonote::Transaction tx(transaction); - tx.extra = extra.serialize(); - return tx; - } - void checkIfSigning() const { if (!transaction.signatures.empty()) { throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); } } - cryptonote::Transaction transaction; + CryptoNote::Transaction transaction; boost::optional secretKey; + mutable boost::optional transactionHash; TransactionExtra extra; }; @@ -229,68 +226,80 @@ namespace CryptoNote { //////////////////////////////////////////////////////////////////////// std::unique_ptr createTransaction() { - return std::unique_ptr(new Transaction()); + return std::unique_ptr(new TransactionImpl()); } std::unique_ptr createTransaction(const Blob& transactionBlob) { - return std::unique_ptr(new Transaction(transactionBlob)); + return std::unique_ptr(new TransactionImpl(transactionBlob)); } - std::unique_ptr createTransaction(const cryptonote::Transaction& tx) { - return std::unique_ptr(new Transaction(tx)); + std::unique_ptr createTransaction(const CryptoNote::Transaction& tx) { + return std::unique_ptr(new TransactionImpl(tx)); } - Transaction::Transaction() { - cryptonote::KeyPair txKeys(cryptonote::KeyPair::generate()); - - transaction.version = CURRENT_TRANSACTION_VERSION; - transaction.unlockTime = 0; + TransactionImpl::TransactionImpl() { + CryptoNote::KeyPair txKeys(CryptoNote::KeyPair::generate()); tx_extra_pub_key pk = { txKeys.pub }; extra.set(pk); + transaction.version = CURRENT_TRANSACTION_VERSION; + transaction.unlockTime = 0; + transaction.extra = extra.serialize(); + secretKey = txKeys.sec; } - Transaction::Transaction(const Blob& data) { - cryptonote::blobdata blob(reinterpret_cast(data.data()), data.size()); - if (!cryptonote::parse_and_validate_tx_from_blob(blob, transaction)) { + TransactionImpl::TransactionImpl(const Blob& data) { + CryptoNote::blobdata blob(reinterpret_cast(data.data()), data.size()); + if (!parse_and_validate_tx_from_blob(blob, transaction)) { throw std::runtime_error("Invalid transaction data"); } - + extra.parse(transaction.extra); + transactionHash = get_blob_hash(blob); // avoid serialization if we already have blob } - Transaction::Transaction(const cryptonote::Transaction& tx) : transaction(tx) { + TransactionImpl::TransactionImpl(const CryptoNote::Transaction& tx) : transaction(tx) { extra.parse(transaction.extra); } - Hash Transaction::getTransactionHash() const { - auto hash = get_transaction_hash(constructFinalTransaction()); - return reinterpret_cast(hash); + void TransactionImpl::invalidateHash() { + if (transactionHash.is_initialized()) { + transactionHash = decltype(transactionHash)(); + } + } + + Hash TransactionImpl::getTransactionHash() const { + if (!transactionHash.is_initialized()) { + transactionHash = get_transaction_hash(transaction); + } + + return reinterpret_cast(transactionHash.get()); } - Hash Transaction::getTransactionPrefixHash() const { - auto hash = get_transaction_prefix_hash(constructFinalTransaction()); + Hash TransactionImpl::getTransactionPrefixHash() const { + auto hash = get_transaction_prefix_hash(transaction); return reinterpret_cast(hash); } - PublicKey Transaction::getTransactionPublicKey() const { + PublicKey TransactionImpl::getTransactionPublicKey() const { crypto::public_key pk(null_pkey); extra.getPublicKey(pk); return reinterpret_cast(pk); } - uint64_t Transaction::getUnlockTime() const { + uint64_t TransactionImpl::getUnlockTime() const { return transaction.unlockTime; } - void Transaction::setUnlockTime(uint64_t unlockTime) { + void TransactionImpl::setUnlockTime(uint64_t unlockTime) { checkIfSigning(); transaction.unlockTime = unlockTime; + invalidateHash(); } - bool Transaction::getTransactionSecretKey(SecretKey& key) const { + bool TransactionImpl::getTransactionSecretKey(SecretKey& key) const { if (!secretKey) { return false; } @@ -298,7 +307,7 @@ namespace CryptoNote { return true; } - void Transaction::setTransactionSecretKey(const SecretKey& key) { + void TransactionImpl::setTransactionSecretKey(const SecretKey& key) { const auto& sk = reinterpret_cast(key); crypto::public_key pk; crypto::public_key txPubKey; @@ -313,14 +322,15 @@ namespace CryptoNote { secretKey = reinterpret_cast(key); } - size_t Transaction::addInput(const InputKey& input) { + size_t TransactionImpl::addInput(const InputKey& input) { checkIfSigning(); TransactionInputToKey inKey = { input.amount, input.keyOffsets, *reinterpret_cast(&input.keyImage) }; transaction.vin.emplace_back(inKey); + invalidateHash(); return transaction.vin.size() - 1; } - size_t Transaction::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) { + size_t TransactionImpl::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) { checkIfSigning(); InputKey input; input.amount = info.amount; @@ -329,53 +339,64 @@ namespace CryptoNote { reinterpret_cast(senderKeys), reinterpret_cast(info.realOutput.transactionPublicKey), info.realOutput.outputInTransaction, - reinterpret_cast(ephKeys), + reinterpret_cast(ephKeys), reinterpret_cast(input.keyImage)); // fill outputs array and use relative offsets for (const auto& out : info.outputs) { input.keyOffsets.push_back(out.outputIndex); } - input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); + input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); return addInput(input); } - size_t Transaction::addInput(const InputMultisignature& input) { + size_t TransactionImpl::addInput(const InputMultisignature& input) { checkIfSigning(); + TransactionInputMultisignature inMsig; inMsig.amount = input.amount; inMsig.outputIndex = input.outputIndex; inMsig.signatures = input.signatures; transaction.vin.push_back(inMsig); + invalidateHash(); + return transaction.vin.size() - 1; } - size_t Transaction::addOutput(uint64_t amount, const AccountAddress& to) { + size_t TransactionImpl::addOutput(uint64_t amount, const AccountAddress& to) { checkIfSigning(); + TransactionOutputToKey outKey; derivePublicKey(to, txSecretKey(), transaction.vout.size(), outKey.key); TransactionOutput out = { amount, outKey }; transaction.vout.emplace_back(out); + invalidateHash(); + return transaction.vout.size() - 1; } - size_t Transaction::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { + size_t TransactionImpl::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { checkIfSigning(); + const auto& txKey = txSecretKey(); size_t outputIndex = transaction.vout.size(); TransactionOutputMultisignature outMsig; outMsig.requiredSignatures = requiredSignatures; outMsig.keys.resize(to.size()); + for (int i = 0; i < to.size(); ++i) { derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); } + TransactionOutput out = { amount, outMsig }; transaction.vout.emplace_back(out); + invalidateHash(); + return outputIndex; } - void Transaction::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) { + void TransactionImpl::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) { const auto& input = boost::get(getInputChecked(transaction, index, InputType::Key)); Hash prefixHash = getTransactionPrefixHash(); @@ -397,9 +418,10 @@ namespace CryptoNote { signatures.data()); getSignatures(index) = signatures; + invalidateHash(); } - void Transaction::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { + void TransactionImpl::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { crypto::key_derivation derivation; crypto::public_key ephemeralPublicKey; crypto::secret_key ephemeralSecretKey; @@ -421,9 +443,10 @@ namespace CryptoNote { ephemeralPublicKey, ephemeralSecretKey, signature); getSignatures(index).push_back(signature); + invalidateHash(); } - std::vector& Transaction::getSignatures(size_t input) { + std::vector& TransactionImpl::getSignatures(size_t input) { // update signatures container size if needed if (transaction.signatures.size() < transaction.vin.size()) { transaction.signatures.resize(transaction.vin.size()); @@ -436,18 +459,18 @@ namespace CryptoNote { return transaction.signatures[input]; } - std::vector Transaction::getTransactionData() const { - return stringToVector(t_serializable_object_to_blob(constructFinalTransaction())); + std::vector TransactionImpl::getTransactionData() const { + return stringToVector(t_serializable_object_to_blob(transaction)); } - void Transaction::setPaymentId(const Hash& hash) { + void TransactionImpl::setPaymentId(const Hash& hash) { checkIfSigning(); blobdata paymentIdBlob; set_payment_id_to_tx_extra_nonce(paymentIdBlob, reinterpret_cast(hash)); setExtraNonce(paymentIdBlob); } - bool Transaction::getPaymentId(Hash& hash) const { + bool TransactionImpl::getPaymentId(Hash& hash) const { blobdata nonce; if (getExtraNonce(nonce)) { crypto::hash paymentId; @@ -459,13 +482,15 @@ namespace CryptoNote { return false; } - void Transaction::setExtraNonce(const std::string& nonce) { + void TransactionImpl::setExtraNonce(const std::string& nonce) { checkIfSigning(); tx_extra_nonce extraNonce = { nonce }; extra.set(extraNonce); + transaction.extra = extra.serialize(); + invalidateHash(); } - bool Transaction::getExtraNonce(std::string& nonce) const { + bool TransactionImpl::getExtraNonce(std::string& nonce) const { tx_extra_nonce extraNonce; if (extra.get(extraNonce)) { nonce = extraNonce.nonce; @@ -474,54 +499,58 @@ namespace CryptoNote { return false; } - size_t Transaction::getInputCount() const { + Blob TransactionImpl::getExtra() const { + return transaction.extra; + } + + size_t TransactionImpl::getInputCount() const { return transaction.vin.size(); } - uint64_t Transaction::getInputTotalAmount() const { + uint64_t TransactionImpl::getInputTotalAmount() const { return std::accumulate(transaction.vin.begin(), transaction.vin.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { return val + getTransactionInputAmount(in); }); } - TransactionTypes::InputType Transaction::getInputType(size_t index) const { + TransactionTypes::InputType TransactionImpl::getInputType(size_t index) const { return getTransactionInputType(getInputChecked(transaction, index)); } - void Transaction::getInput(size_t index, InputKey& input) const { + void TransactionImpl::getInput(size_t index, InputKey& input) const { const auto& k = boost::get(getInputChecked(transaction, index, InputType::Key)); input.amount = k.amount; input.keyImage = reinterpret_cast(k.keyImage); input.keyOffsets = k.keyOffsets; } - void Transaction::getInput(size_t index, InputMultisignature& input) const { + void TransactionImpl::getInput(size_t index, InputMultisignature& input) const { const auto& m = boost::get(getInputChecked(transaction, index, InputType::Multisignature)); input.amount = m.amount; input.outputIndex = m.outputIndex; input.signatures = m.signatures; } - size_t Transaction::getOutputCount() const { + size_t TransactionImpl::getOutputCount() const { return transaction.vout.size(); } - uint64_t Transaction::getOutputTotalAmount() const { + uint64_t TransactionImpl::getOutputTotalAmount() const { return std::accumulate(transaction.vout.begin(), transaction.vout.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { return val + out.amount; }); } - TransactionTypes::OutputType Transaction::getOutputType(size_t index) const { + TransactionTypes::OutputType TransactionImpl::getOutputType(size_t index) const { return getTransactionOutputType(getOutputChecked(transaction, index).target); } - void Transaction::getOutput(size_t index, OutputKey& output) const { + void TransactionImpl::getOutput(size_t index, OutputKey& output) const { const auto& out = getOutputChecked(transaction, index, OutputType::Key); const auto& k = boost::get(out.target); output.amount = out.amount; output.key = reinterpret_cast(k.key); } - void Transaction::getOutput(size_t index, OutputMultisignature& output) const { + void TransactionImpl::getOutput(size_t index, OutputMultisignature& output) const { const auto& out = getOutputChecked(transaction, index, OutputType::Multisignature); const auto& m = boost::get(out.target); output.amount = out.amount; @@ -535,7 +564,7 @@ namespace CryptoNote { return pk == outKey; } - bool Transaction::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { + bool TransactionImpl::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { account_keys keys; keys.m_account_address = reinterpret_cast(addr); // only view secret key is used, spend key is not needed @@ -574,11 +603,11 @@ namespace CryptoNote { return true; } - size_t Transaction::getRequiredSignaturesCount(size_t index) const { + size_t TransactionImpl::getRequiredSignaturesCount(size_t index) const { return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); } - bool Transaction::validateInputs() const { + bool TransactionImpl::validateInputs() const { return check_inputs_types_supported(transaction) && check_inputs_overflow(transaction) && @@ -586,13 +615,13 @@ namespace CryptoNote { checkMultisignatureInputsDiff(transaction); } - bool Transaction::validateOutputs() const { + bool TransactionImpl::validateOutputs() const { return check_outs_valid(transaction) && check_outs_overflow(transaction); } - bool Transaction::validateSignatures() const { + bool TransactionImpl::validateSignatures() const { if (transaction.signatures.size() < transaction.vin.size()) { return false; } diff --git a/src/cryptonote_core/TransactionApi.h b/src/cryptonote_core/TransactionApi.h index 7af07943d9..c52eadf7fa 100644 --- a/src/cryptonote_core/TransactionApi.h +++ b/src/cryptonote_core/TransactionApi.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,12 +20,12 @@ #include #include "ITransaction.h" -namespace cryptonote { +namespace CryptoNote { struct Transaction; } namespace CryptoNote { std::unique_ptr createTransaction(); std::unique_ptr createTransaction(const Blob& transactionBlob); - std::unique_ptr createTransaction(const cryptonote::Transaction& tx); + std::unique_ptr createTransaction(const CryptoNote::Transaction& tx); } diff --git a/src/cryptonote_core/TransactionExtra.h b/src/cryptonote_core/TransactionExtra.h index 34fc7e0eeb..7b607bb963 100644 --- a/src/cryptonote_core/TransactionExtra.h +++ b/src/cryptonote_core/TransactionExtra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -37,7 +37,7 @@ namespace CryptoNote { bool parse(const std::vector& extra) { fields.clear(); - return cryptonote::parse_tx_extra(extra, fields); + return CryptoNote::parse_tx_extra(extra, fields); } template @@ -61,7 +61,7 @@ namespace CryptoNote { } bool getPublicKey(crypto::public_key& pk) const { - cryptonote::tx_extra_pub_key extraPk; + CryptoNote::tx_extra_pub_key extraPk; if (!get(extraPk)) { return false; } @@ -73,22 +73,22 @@ namespace CryptoNote { std::ostringstream out; binary_archive ar(out); for (const auto& f : fields) { - ::do_serialize(ar, const_cast(f)); + ::do_serialize(ar, const_cast(f)); } return stringToVector(out.str()); } private: - std::vector::const_iterator find(const std::type_info& t) const { - return std::find_if(fields.begin(), fields.end(), [&t](const cryptonote::tx_extra_field& f) { return t == f.type(); }); + std::vector::const_iterator find(const std::type_info& t) const { + return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::tx_extra_field& f) { return t == f.type(); }); } - std::vector::iterator find(const std::type_info& t) { - return std::find_if(fields.begin(), fields.end(), [&t](const cryptonote::tx_extra_field& f) { return t == f.type(); }); + std::vector::iterator find(const std::type_info& t) { + return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::tx_extra_field& f) { return t == f.type(); }); } - std::vector fields; + std::vector fields; }; -} \ No newline at end of file +} diff --git a/src/cryptonote_core/UpgradeDetector.cpp b/src/cryptonote_core/UpgradeDetector.cpp index c42dbd42a9..3776d02d0b 100644 --- a/src/cryptonote_core/UpgradeDetector.cpp +++ b/src/cryptonote_core/UpgradeDetector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_core/UpgradeDetector.h b/src/cryptonote_core/UpgradeDetector.h index 060fdcf0af..6661783f77 100644 --- a/src/cryptonote_core/UpgradeDetector.h +++ b/src/cryptonote_core/UpgradeDetector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,13 +21,11 @@ #include #include -// epee -#include "include_base_utils.h" - #include "cryptonote_core/Currency.h" #include "cryptonote_config.h" +#include -namespace cryptonote { +namespace CryptoNote { class UpgradeDetectorBase { public: enum : uint64_t { @@ -35,17 +33,17 @@ namespace cryptonote { }; }; - static_assert(cryptonote::UpgradeDetectorBase::UNDEF_HEIGHT == UINT64_C(0xFFFFFFFFFFFFFFFF), "UpgradeDetectorBase::UNDEF_HEIGHT has invalid value"); + static_assert(CryptoNote::UpgradeDetectorBase::UNDEF_HEIGHT == UINT64_C(0xFFFFFFFFFFFFFFFF), "UpgradeDetectorBase::UNDEF_HEIGHT has invalid value"); template class BasicUpgradeDetector : public UpgradeDetectorBase { public: - BasicUpgradeDetector(const Currency& currency, BC& blockchain, uint8_t targetVersion) : + BasicUpgradeDetector(const Currency& currency, BC& blockchain, uint8_t targetVersion, Logging::ILogger& log) : m_currency(currency), m_blockchain(blockchain), m_targetVersion(targetVersion), - m_votingCompleteHeight(UNDEF_HEIGHT) { - } + m_votingCompleteHeight(UNDEF_HEIGHT), + logger(log, "upgrade") { } bool init() { if (m_currency.upgradeHeight() == UNDEF_HEIGHT) { @@ -58,31 +56,26 @@ namespace cryptonote { } else if (m_targetVersion <= m_blockchain.back().bl.majorVersion) { auto it = std::lower_bound(m_blockchain.begin(), m_blockchain.end(), m_targetVersion, [](const typename BC::value_type& b, uint8_t v) { return b.bl.majorVersion < v; }); - CHECK_AND_ASSERT_MES(it != m_blockchain.end() && it->bl.majorVersion == m_targetVersion, false, - "Internal error: upgrade height isn't found"); + if (!(it != m_blockchain.end() && it->bl.majorVersion == m_targetVersion)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: upgrade height isn't found"; return false; } uint64_t upgradeHeight = it - m_blockchain.begin(); m_votingCompleteHeight = findVotingCompleteHeight(upgradeHeight); - CHECK_AND_ASSERT_MES(m_votingCompleteHeight != UNDEF_HEIGHT, false, - "Internal error: voting complete height isn't found, upgrade height = " << upgradeHeight); + if (!(m_votingCompleteHeight != UNDEF_HEIGHT)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: voting complete height isn't found, upgrade height = " << upgradeHeight; return false; } } else { m_votingCompleteHeight = UNDEF_HEIGHT; } } else if (!m_blockchain.empty()) { if (m_blockchain.size() <= m_currency.upgradeHeight() + 1) { - CHECK_AND_ASSERT_MES(m_blockchain.back().bl.majorVersion == m_targetVersion - 1, false, - "Internal error: block at height " << (m_blockchain.size() - 1) << " has invalid version " << - static_cast(m_blockchain.back().bl.majorVersion) << ", expected " << static_cast(m_targetVersion)); + if (!(m_blockchain.back().bl.majorVersion == m_targetVersion - 1)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << (m_blockchain.size() - 1) << " has invalid version " << + static_cast(m_blockchain.back().bl.majorVersion) << ", expected " << static_cast(m_targetVersion); return false; } } else { int blockVersionAtUpgradeHeight = m_blockchain[m_currency.upgradeHeight()].bl.majorVersion; - CHECK_AND_ASSERT_MES(blockVersionAtUpgradeHeight == m_targetVersion - 1, false, - "Internal error: block at height " << m_currency.upgradeHeight() << " has invalid version " << - blockVersionAtUpgradeHeight << ", expected " << static_cast(m_targetVersion - 1)); + if (!(blockVersionAtUpgradeHeight == m_targetVersion - 1)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << m_currency.upgradeHeight() << " has invalid version " << + blockVersionAtUpgradeHeight << ", expected " << static_cast(m_targetVersion - 1); return false; } int blockVersionAfterUpgradeHeight = m_blockchain[m_currency.upgradeHeight() + 1].bl.majorVersion; - CHECK_AND_ASSERT_MES(blockVersionAfterUpgradeHeight == m_targetVersion, false, - "Internal error: block at height " << (m_currency.upgradeHeight() + 1) << " has invalid version " << - blockVersionAfterUpgradeHeight << ", expected " << static_cast(m_targetVersion)); + if (!(blockVersionAfterUpgradeHeight == m_targetVersion)) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: block at height " << (m_currency.upgradeHeight() + 1) << " has invalid version " << + blockVersionAfterUpgradeHeight << ", expected " << static_cast(m_targetVersion); return false; } } } @@ -117,13 +110,13 @@ namespace cryptonote { assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); if (m_blockchain.size() % (60 * 60 / m_currency.difficultyTarget()) == 0) { - LOG_PRINT_GREEN("###### UPGRADE is going to happen after height " << upgradeHeight() << "!", LOG_LEVEL_2); + logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE is going to happen after height " << upgradeHeight() << "!"; } } else if (m_blockchain.size() == upgradeHeight() + 1) { assert(m_blockchain.back().bl.majorVersion == m_targetVersion - 1); - LOG_PRINT_GREEN("###### UPGRADE has happened! Starting from height " << (upgradeHeight() + 1) << - " blocks with major version below " << static_cast(m_targetVersion) << " will be rejected!", LOG_LEVEL_2); + logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE has happened! Starting from height " << (upgradeHeight() + 1) << + " blocks with major version below " << static_cast(m_targetVersion) << " will be rejected!"; } else { assert(m_blockchain.back().bl.majorVersion == m_targetVersion); } @@ -132,8 +125,8 @@ namespace cryptonote { uint64_t lastBlockHeight = m_blockchain.size() - 1; if (isVotingComplete(lastBlockHeight)) { m_votingCompleteHeight = lastBlockHeight; - LOG_PRINT_GREEN("###### UPGRADE voting complete at height " << m_votingCompleteHeight << - "! UPGRADE is going to happen after height " << upgradeHeight() << "!", LOG_LEVEL_2); + logger(Logging::TRACE, Logging::BRIGHT_GREEN) << "###### UPGRADE voting complete at height " << m_votingCompleteHeight << + "! UPGRADE is going to happen after height " << upgradeHeight() << "!"; } } } @@ -143,7 +136,7 @@ namespace cryptonote { assert(m_currency.upgradeHeight() == UNDEF_HEIGHT); if (m_blockchain.size() == m_votingCompleteHeight) { - LOG_PRINT_YELLOW("###### UPGRADE after height " << upgradeHeight() << " has been cancelled!", LOG_LEVEL_2); + logger(Logging::TRACE, Logging::BRIGHT_YELLOW) << "###### UPGRADE after height " << upgradeHeight() << " has been cancelled!"; m_votingCompleteHeight = UNDEF_HEIGHT; } else { assert(m_blockchain.size() > m_votingCompleteHeight); @@ -185,6 +178,7 @@ namespace cryptonote { } private: + Logging::LoggerRef logger; const Currency& m_currency; BC& m_blockchain; uint8_t m_targetVersion; diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index c4ada894e5..4f530b2c47 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,16 +17,7 @@ #include "account.h" -#include -#include -#include - -#include "include_base_utils.h" -#include "warnings.h" - -DISABLE_VS_WARNINGS(4244 4345) - -namespace cryptonote +namespace CryptoNote { //----------------------------------------------------------------- account_base::account_base() diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index 62594eb4ea..171ec91e67 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include "cryptonote_core/cryptonote_basic.h" #include "crypto/crypto.h" -namespace cryptonote { +namespace CryptoNote { template struct AccountBaseSerializer; struct account_keys { diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_core/account_boost_serialization.h index e391b39b6c..8b242a4ccf 100644 --- a/src/cryptonote_core/account_boost_serialization.h +++ b/src/cryptonote_core/account_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,13 +20,13 @@ #include "account.h" #include "cryptonote_core/cryptonote_boost_serialization.h" -//namespace cryptonote { +//namespace CryptoNote { namespace boost { namespace serialization { template - inline void serialize(Archive &a, cryptonote::account_keys &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::account_keys &x, const boost::serialization::version_type ver) { a & x.m_account_address; a & x.m_spend_secret_key; @@ -34,7 +34,7 @@ namespace boost } template - inline void serialize(Archive &a, cryptonote::AccountPublicAddress &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::AccountPublicAddress &x, const boost::serialization::version_type ver) { a & x.m_spendPublicKey; a & x.m_viewPublicKey; diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index fee5dcbf2a..b942f16330 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,7 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include #include "blockchain_storage.h" #include @@ -23,273 +22,164 @@ #include #include +#include +#include +#include -// epee -#include "file_io_utils.h" -#include "misc_language.h" -#include "profile_tools.h" -#include "time_helper.h" +#include "Common/boost_serialization_helper.h" +#include "Common/ShuffleGenerator.h" +#include "Common/StringTools.h" -#include "common/boost_serialization_helper.h" -#include "common/ShuffleGenerator.h" #include "cryptonote_format_utils.h" -#include "cryptonote_boost_serialization.h" #include "rpc/core_rpc_server_commands_defs.h" +using namespace Logging; -//namespace { -// std::string hashHex(const crypto::hash& hash) { -// std::string result; -// for (size_t i = 0; i < crypto::HASH_SIZE; ++i) { -// result += "0123456789ABCDEF"[static_cast(hash.data[i]) >> 4]; -// result += "0123456789ABCDEF"[static_cast(hash.data[i]) & 15]; -// } -// -// return result; -// } -//} +#ifdef ENDL +#undef ENDL +#define ENDL '\n' +#endif namespace { - std::string appendPath(const std::string& path, const std::string& fileName) { - std::string result = path; - if (!result.empty()) { - result += '/'; - } - - result += fileName; - return result; +std::string appendPath(const std::string& path, const std::string& fileName) { + std::string result = path; + if (!result.empty()) { + result += '/'; } + + result += fileName; + return result; } -namespace std { - bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) { - return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0; - } +template +type_vec_type medianValue(std::vector &v) +{ + if (v.empty()) + return boost::value_initialized(); + + if (v.size() == 1) + return v[0]; - bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) { - return memcmp(&keyImage1, &keyImage2, 32) < 0; + size_t n = (v.size()) / 2; + std::sort(v.begin(), v.end()); + //nth_element(v.begin(), v.begin()+n-1, v.end()); + if (v.size() % 2) + {//1, 3, 5... + return v[n]; + } else + {//2, 4, 6... + return (v[n - 1] + v[n]) / 2; } } -using namespace cryptonote; - -DISABLE_VS_WARNINGS(4267) - -namespace cryptonote { - struct transaction_chain_entry { - Transaction tx; - uint64_t m_keeper_block_height; - size_t m_blob_size; - std::vector m_global_output_indexes; - template void serialize(archive_t & ar, unsigned int version); - }; +} - struct block_extended_info { - Block bl; - uint64_t height; - size_t block_cumulative_size; - difficulty_type cumulative_difficulty; - uint64_t already_generated_coins; +namespace std { +bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) { + return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0; +} - template void serialize(archive_t & ar, unsigned int version); - }; +bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) { + return memcmp(&keyImage1, &keyImage2, 32) < 0; +} +} - template void transaction_chain_entry::serialize(archive_t & ar, unsigned int version) { - ar & tx; - ar & m_keeper_block_height; - ar & m_blob_size; - ar & m_global_output_indexes; - } +#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1 - template void block_extended_info::serialize(archive_t & ar, unsigned int version) { - ar & bl; - ar & height; - ar & cumulative_difficulty; - ar & block_cumulative_size; - ar & already_generated_coins; - } +namespace CryptoNote { +class BlockCacheSerializer; } -template void cryptonote::blockchain_storage::TransactionEntry::serialize(Archive& archive, unsigned int version) { - archive & tx; -} +BOOST_CLASS_VERSION(CryptoNote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER); -template void cryptonote::blockchain_storage::BlockEntry::serialize(Archive& archive, unsigned int version) { - archive & bl; - archive & height; - archive & block_cumulative_size; - archive & cumulative_difficulty; - archive & already_generated_coins; - archive & transactions; -} +namespace CryptoNote +{ -template void cryptonote::blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { +template +void blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { archive & block; archive & transaction; } -template void cryptonote::blockchain_storage::MultisignatureOutputUsage::serialize(Archive& archive, unsigned int version) { +template +void blockchain_storage::MultisignatureOutputUsage::serialize(Archive& archive, unsigned int version) { archive & transactionIndex; archive & outputIndex; archive & isUsed; } -namespace cryptonote { -#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 13 - - template void blockchain_storage::serialize(archive_t & ar, const unsigned int version) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if (version < 12) { - LOG_PRINT_L0("Detected blockchain of unsupported version, migration is not possible."); - return; - } - - LOG_PRINT_L0("Blockchain of previous version detected, migrating. This may take several minutes, please be patient..."); - - std::vector blocks; - ar & blocks; - - { - std::unordered_map blocks_index; - ar & blocks_index; - } - - std::unordered_map transactions; - ar & transactions; - - { - std::unordered_set spent_keys; - ar & spent_keys; - } - - { - std::unordered_map alternative_chains; - ar & alternative_chains; - } - - { - std::map>> outputs; - ar & outputs; - } - - { - std::unordered_map invalid_blocks; - ar & invalid_blocks; - } - - size_t current_block_cumul_sz_limit; - ar & current_block_cumul_sz_limit; - LOG_PRINT_L0("Old blockchain storage:" << ENDL << - "blocks: " << blocks.size() << ENDL << - "transactions: " << transactions.size() << ENDL << - "current_block_cumul_sz_limit: " << current_block_cumul_sz_limit); - - BlockEntry block; - TransactionEntry transaction; - for (uint32_t b = 0; b < blocks.size(); ++b) { - block.bl = blocks[b].bl; - block.height = b; - block.block_cumulative_size = blocks[b].block_cumulative_size; - block.cumulative_difficulty = blocks[b].cumulative_difficulty; - block.already_generated_coins = blocks[b].already_generated_coins; - block.transactions.resize(1 + blocks[b].bl.txHashes.size()); - block.transactions[0].tx = blocks[b].bl.minerTx; - TransactionIndex transactionIndex = { b, 0 }; - pushTransaction(block, get_transaction_hash(blocks[b].bl.minerTx), transactionIndex); - for (uint32_t t = 0; t < blocks[b].bl.txHashes.size(); ++t) { - block.transactions[1 + t].tx = transactions[blocks[b].bl.txHashes[t]].tx; - transactionIndex.transaction = 1 + t; - pushTransaction(block, blocks[b].bl.txHashes[t], transactionIndex); - } +class BlockCacheSerializer { - pushBlock(block); - } - - update_next_comulative_size_limit(); - if (m_current_block_cumul_sz_limit != current_block_cumul_sz_limit) { - LOG_ERROR("Migration was unsuccessful."); - } +public: + BlockCacheSerializer(blockchain_storage& bs, const crypto::hash lastBlockHash, ILogger& logger) : + m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockCacheSerializer") { } -} -BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) - -namespace cryptonote -{ + template void serialize(Archive& ar, unsigned int version) { -#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1 - - class BlockCacheSerializer { - - public: - BlockCacheSerializer(blockchain_storage& bs, const crypto::hash lastBlockHash) : - m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false) {} + // ignore old versions, do rebuild + if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) + return; - template void serialize(Archive& ar, unsigned int version) { + std::string operation; + if (Archive::is_loading::value) { + operation = "- loading "; + crypto::hash blockHash; + ar & blockHash; - // ignore old versions, do rebuild - if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) + if (blockHash != m_lastBlockHash) { return; - - std::string operation; - if (Archive::is_loading::value) { - operation = "- loading "; - crypto::hash blockHash; - ar & blockHash; - - if (blockHash != m_lastBlockHash) { - return; - } - - } else { - operation = "- saving "; - ar & m_lastBlockHash; } - LOG_PRINT_L0(operation << "block index..."); - ar & m_bs.m_blockIndex; + } else { + operation = "- saving "; + ar & m_lastBlockHash; + } + + logger(INFO) << operation << "block index..."; + ar & m_bs.m_blockIndex; - LOG_PRINT_L0(operation << "transaction map..."); - ar & m_bs.m_transactionMap; + logger(INFO) << operation << "transaction map..."; + ar & m_bs.m_transactionMap; - LOG_PRINT_L0(operation << "spend keys..."); - ar & m_bs.m_spent_keys; + logger(INFO) << operation << "spend keys..."; + ar & m_bs.m_spent_keys; - LOG_PRINT_L0(operation << "outputs..."); - ar & m_bs.m_outputs; + logger(INFO) << operation << "outputs..."; + ar & m_bs.m_outputs; - LOG_PRINT_L0(operation << "multi-signature outputs..."); - ar & m_bs.m_multisignatureOutputs; + logger(INFO) << operation << "multi-signature outputs..."; + ar & m_bs.m_multisignatureOutputs; - m_loaded = true; - } + m_loaded = true; + } - bool loaded() const { - return m_loaded; - } + bool loaded() const { + return m_loaded; + } - private: +private: - bool m_loaded; - blockchain_storage& m_bs; - crypto::hash m_lastBlockHash; - }; -} + LoggerRef logger; + bool m_loaded; + blockchain_storage& m_bs; + crypto::hash m_lastBlockHash; +}; -BOOST_CLASS_VERSION(cryptonote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) +blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) : +logger(logger, "blockchain_storage"), +m_currency(currency), +m_tx_pool(tx_pool), +m_current_block_cumul_sz_limit(0), +m_is_in_checkpoint_zone(false), +m_is_blockchain_storing(false), +m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger), +m_checkpoints(logger) { -blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool): - m_currency(currency), - m_tx_pool(tx_pool), - m_current_block_cumul_sz_limit(0), - m_is_in_checkpoint_zone(false), - m_is_blockchain_storing(false), - m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2) { m_outputs.set_deleted_key(0); - - crypto::key_image nullImage = AUTO_VAL_INIT(nullImage); + crypto::key_image nullImage = boost::value_initialized(); m_spent_keys.set_deleted_key(nullImage); } @@ -301,11 +191,11 @@ bool blockchain_storage::removeObserver(IBlockchainStorageObserver* observer) { return m_observerManager.remove(observer); } -bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) { +bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) { return check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id); } -bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { +bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { BlockInfo tail; @@ -316,13 +206,12 @@ bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& t if (!lastFailed.empty() && get_current_blockchain_height() > lastFailed.height && get_block_id_by_height(lastFailed.height) == lastFailed.id) { return false; //we already sure that this tx is broken for this height } - + if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { lastFailed = tail; return false; } - } - else { + } else { if (maxUsedBlock.height >= get_current_blockchain_height()) { return false; } @@ -344,29 +233,29 @@ bool blockchain_storage::checkTransactionInputs(const cryptonote::Transaction& t return true; } -bool blockchain_storage::haveSpentKeyImages(const cryptonote::Transaction& tx) { +bool blockchain_storage::haveSpentKeyImages(const CryptoNote::Transaction& tx) { return this->have_tx_keyimges_as_spent(tx); } bool blockchain_storage::have_tx(const crypto::hash &id) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_transactionMap.find(id) != m_transactionMap.end(); } bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_spent_keys.find(key_im) != m_spent_keys.end(); } uint64_t blockchain_storage::get_current_blockchain_height() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_blocks.size(); } bool blockchain_storage::init(const std::string& config_folder, bool load_existing) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { - LOG_ERROR("Failed to create data directory: " << m_config_folder); + logger(ERROR, BRIGHT_RED) << "Failed to create data directory: " << m_config_folder; return false; } @@ -376,83 +265,85 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi return false; } - if (load_existing) { - LOG_PRINT_L0("Loading blockchain..."); - - if (m_blocks.empty()) { - const std::string filename = appendPath(m_config_folder, cryptonote::parameters::CRYPTONOTE_BLOCKCHAINDATA_FILENAME); - if (!tools::unserialize_obj_from_file(*this, filename)) { - LOG_PRINT_L0("Can't load blockchain storage from file."); - } - } else { - BlockCacheSerializer loader(*this, get_block_hash(m_blocks.back().bl)); - tools::unserialize_obj_from_file(loader, appendPath(config_folder, m_currency.blocksCacheFileName())); - - if (!loader.loaded()) { - LOG_PRINT_L0("No actual blockchain cache found, rebuilding internal structures..."); - std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); - m_blockIndex.clear(); - m_transactionMap.clear(); - m_spent_keys.clear(); - m_outputs.clear(); - m_multisignatureOutputs.clear(); - for (uint32_t b = 0; b < m_blocks.size(); ++b) { - if (b % 1000 == 0) { - std::cout << "Height " << b << " of " << m_blocks.size() << '\r'; - } - const BlockEntry& block = m_blocks[b]; - crypto::hash blockHash = get_block_hash(block.bl); - m_blockIndex.push(blockHash); - for (uint16_t t = 0; t < block.transactions.size(); ++t) { - const TransactionEntry& transaction = block.transactions[t]; - crypto::hash transactionHash = get_transaction_hash(transaction.tx); - TransactionIndex transactionIndex = { b, t }; - m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); - - // process inputs - for (auto& i : transaction.tx.vin) { - if (i.type() == typeid(TransactionInputToKey)) { - m_spent_keys.insert(::boost::get(i).keyImage); - } else if (i.type() == typeid(TransactionInputMultisignature)) { - auto out = ::boost::get(i); - m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; - } + if (load_existing && !m_blocks.empty()) { + logger(INFO, BRIGHT_WHITE) << "Loading blockchain..."; + BlockCacheSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger()); + tools::unserialize_obj_from_file(loader, appendPath(config_folder, m_currency.blocksCacheFileName())); + + if (!loader.loaded()) { + logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain cache found, rebuilding internal structures..."; + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + m_blockIndex.clear(); + m_transactionMap.clear(); + m_spent_keys.clear(); + m_outputs.clear(); + m_multisignatureOutputs.clear(); + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); + } + const BlockEntry& block = m_blocks[b]; + crypto::hash blockHash = get_block_hash(block.bl); + m_blockIndex.push(blockHash); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const TransactionEntry& transaction = block.transactions[t]; + crypto::hash transactionHash = get_transaction_hash(transaction.tx); + TransactionIndex transactionIndex = {b, t}; + m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + + // process inputs + for (auto& i : transaction.tx.vin) { + if (i.type() == typeid(TransactionInputToKey)) { + m_spent_keys.insert(::boost::get(i).keyImage); + } else if (i.type() == typeid(TransactionInputMultisignature)) { + auto out = ::boost::get(i); + m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; } + } - // process outputs - for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { - const auto& out = transaction.tx.vout[o]; - if(out.target.type() == typeid(TransactionOutputToKey)) { - m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); - } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { - MultisignatureOutputUsage usage = { transactionIndex, o, false }; - m_multisignatureOutputs[out.amount].push_back(usage); - } + // process outputs + for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { + const auto& out = transaction.tx.vout[o]; + if (out.target.type() == typeid(TransactionOutputToKey)) { + m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); + } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { + MultisignatureOutputUsage usage = {transactionIndex, o, false}; + m_multisignatureOutputs[out.amount].push_back(usage); } } } - - std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; - LOG_PRINT_L0("Rebuilding internal structures took: " << duration.count()); } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count(); } } else { m_blocks.clear(); } if (m_blocks.empty()) { - LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); - block_verification_context bvc = boost::value_initialized(); + logger(INFO, BRIGHT_WHITE) + << "Blockchain not loaded, generating genesis block."; + block_verification_context bvc = + boost::value_initialized(); add_new_block(m_currency.genesisBlock(), bvc); - CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); + if (bvc.m_verifivation_failed) { + logger(ERROR, BRIGHT_RED) << "Failed to add genesis block to blockchain"; + return false; + } } else { crypto::hash firstBlockHash = get_block_hash(m_blocks[0].bl); - CHECK_AND_ASSERT_MES(firstBlockHash == m_currency.genesisBlockHash(), false, - "Failed to init: genesis block mismatch. Probably you set --testnet flag with data dir with non-test blockchain or another network."); + if (!(firstBlockHash == m_currency.genesisBlockHash())) { + logger(ERROR, BRIGHT_RED) << "Failed to init: genesis block mismatch. " + "Probably you set --testnet flag with data " + "dir with non-test blockchain or another " + "network."; + return false; + } } if (!m_upgradeDetector.init()) { - LOG_ERROR("Failed to initialize upgrade detector"); + logger(ERROR, BRIGHT_RED) << "Failed to initialize upgrade detector"; return false; } @@ -463,17 +354,20 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi timestamp_diff = time(NULL) - 1341378000; } - LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) + << "Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " + << Common::timeIntervalToString(timestamp_diff) + << " time ago, current difficulty: " << get_difficulty_for_next_block(); return true; } bool blockchain_storage::storeCache() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); - LOG_PRINT_L0("Saving blockchain..."); - BlockCacheSerializer ser(*this, get_tail_id()); + logger(INFO, BRIGHT_WHITE) << "Saving blockchain..."; + BlockCacheSerializer ser(*this, get_tail_id(), logger.getLogger()); if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { - LOG_ERROR("Failed to save blockchain cache"); + logger(ERROR, BRIGHT_RED) << "Failed to save blockchain cache"; return false; } @@ -486,7 +380,7 @@ bool blockchain_storage::deinit() { } bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); m_blocks.clear(); m_blockIndex.clear(); m_transactionMap.clear(); @@ -501,19 +395,21 @@ bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { } crypto::hash blockchain_storage::get_tail_id(uint64_t& height) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); height = get_current_blockchain_height() - 1; return get_tail_id(); } crypto::hash blockchain_storage::get_tail_id() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_blockIndex.getTailId(); } bool blockchain_storage::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector& new_txs, std::vector& deleted_tx_ids) { - CRITICAL_REGION_LOCAL1(m_tx_pool); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + + std::lock_guard txLock(m_tx_pool); + std::lock_guard bcLock(m_blockchain_lock); + if (known_block_id != get_tail_id()) { return false; } @@ -528,17 +424,17 @@ bool blockchain_storage::getPoolSymmetricDifference(const std::vector& ids) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_blockIndex.getShortChainHistory(ids); } crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_blockIndex.getBlockId(height); } bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& b) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); uint64_t height = 0; @@ -557,7 +453,7 @@ bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& } difficulty_type blockchain_storage::get_difficulty_for_next_block() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(m_currency.difficultyBlocksCount())); @@ -574,7 +470,7 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block() { } uint64_t blockchain_storage::getCoinsInCirculation() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (m_blocks.empty()) { return 0; } else { @@ -586,40 +482,50 @@ uint8_t blockchain_storage::get_block_major_version_for_height(uint64_t height) return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1; } -bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - //remove failed subchain - for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) - { +bool blockchain_storage::rollback_blockchain_switching(std::list &original_chain, size_t rollback_height) { + std::lock_guard lk(m_blockchain_lock); + // remove failed subchain + for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) { popBlock(get_block_hash(m_blocks.back().bl)); - //bool r = pop_block_from_blockchain(); - //CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); } - //return back original chain - BOOST_FOREACH(auto& bl, original_chain) - { - block_verification_context bvc = boost::value_initialized(); + + // return back original chain + for(auto &bl : original_chain) { + block_verification_context bvc = + boost::value_initialized(); bool r = pushBlock(bl, bvc); - CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC!!! failed to add (again) block while chain switching during the rollback!"); + if (!(r && bvc.m_added_to_main_chain)) { + logger(ERROR, BRIGHT_RED) << "PANIC!!! failed to add (again) block while " + "chain switching during the rollback!"; + return false; + } } - LOG_PRINT_L0("Rollback success."); + logger(INFO, BRIGHT_WHITE) << "Rollback success."; return true; } bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); + std::lock_guard lk(m_blockchain_lock); + + if (!(alt_chain.size())) { + logger(ERROR, BRIGHT_RED) << "switch_to_alternative_blockchain: empty chain passed"; + return false; + } size_t split_height = alt_chain.front()->second.height; - CHECK_AND_ASSERT_MES(m_blocks.size() > split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height"); + + if (!(m_blocks.size() > split_height)) { + logger(ERROR, BRIGHT_RED) << "switch_to_alternative_blockchain: blockchain size is lower than split height"; + return false; + } //disconnecting old chain std::list disconnected_chain; for (size_t i = m_blocks.size() - 1; i >= split_height; i--) { Block b = m_blocks[i].bl; popBlock(get_block_hash(b)); - //CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); + //if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to remove block on chain switching"; return false; } disconnected_chain.push_front(b); } @@ -629,10 +535,10 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list(); bool r = pushBlock(ch_ent->second.bl, bvc); if (!r || !bvc.m_added_to_main_chain) { - LOG_PRINT_L0("Failed to switch to alternative blockchain"); + logger(INFO, BRIGHT_WHITE) << "Failed to switch to alternative blockchain"; rollback_blockchain_switching(disconnected_chain, split_height); //add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); - LOG_PRINT_L0("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); + logger(INFO, BRIGHT_WHITE) << "The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl); m_alternative_chains.erase(ch_ent); for (auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) { @@ -651,7 +557,7 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list(); bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); if (!r) { - LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); + logger(ERROR, BRIGHT_RED) << ("Failed to push ex-main chain blocks to alternative chain "); rollback_blockchain_switching(disconnected_chain, split_height); return false; } @@ -663,7 +569,7 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list timestamps; std::vector commulative_difficulties; if (alt_chain.size() < m_currency.difficultyBlocksCount()) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; size_t main_chain_count = m_currency.difficultyBlocksCount() - std::min(m_currency.difficultyBlocksCount(), alt_chain.size()); main_chain_count = std::min(main_chain_count, main_chain_stop_offset); @@ -684,9 +590,10 @@ difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(co commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); } - CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= m_currency.difficultyBlocksCount(), false, - "Internal error, alt_chain.size()[" << alt_chain.size() << "] + timestamps.size()[" << timestamps.size() << - "] NOT <= m_currency.difficultyBlocksCount()[" << m_currency.difficultyBlocksCount() << ']'); + if (!((alt_chain.size() + timestamps.size()) <= m_currency.difficultyBlocksCount())) { + logger(ERROR, BRIGHT_RED) << "Internal error, alt_chain.size()[" << alt_chain.size() << "] + timestamps.size()[" << timestamps.size() << + "] NOT <= m_currency.difficultyBlocksCount()[" << m_currency.difficultyBlocksCount() << ']'; return false; + } for (auto it : alt_chain) { timestamps.push_back(it->second.bl.timestamp); commulative_difficulties.push_back(it->second.cumulative_difficulty); @@ -710,21 +617,35 @@ difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(co } bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t height) { - CHECK_AND_ASSERT_MES(b.minerTx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); - CHECK_AND_ASSERT_MES(b.minerTx.vin[0].type() == typeid(TransactionInputGenerate), false, - "coinbase transaction in the block has the wrong type"); + + if (!(b.minerTx.vin.size() == 1)) { + logger(ERROR, BRIGHT_RED) + << "coinbase transaction in the block has no inputs"; + return false; + } + + if (!(b.minerTx.vin[0].type() == typeid(TransactionInputGenerate))) { + logger(ERROR, BRIGHT_RED) + << "coinbase transaction in the block has the wrong type"; + return false; + } + if (boost::get(b.minerTx.vin[0]).height != height) { - LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << - boost::get(b.minerTx.vin[0]).height << ", expected: " << height); + logger(INFO, BRIGHT_RED) << "The miner transaction in block has invalid height: " << + boost::get(b.minerTx.vin[0]).height << ", expected: " << height; return false; } - CHECK_AND_ASSERT_MES(b.minerTx.unlockTime == height + m_currency.minedMoneyUnlockWindow(), - false, - "coinbase transaction transaction have wrong unlock time=" << b.minerTx.unlockTime << ", expected " << height + m_currency.minedMoneyUnlockWindow()); + if (!(b.minerTx.unlockTime == height + m_currency.minedMoneyUnlockWindow())) { + logger(ERROR, BRIGHT_RED) + << "coinbase transaction transaction have wrong unlock time=" + << b.minerTx.unlockTime << ", expected " + << height + m_currency.minedMoneyUnlockWindow(); + return false; + } if (!check_outs_overflow(b.minerTx)) { - LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); + logger(INFO, BRIGHT_RED) << "miner transaction have money overflow in block " << get_block_hash(b); return false; } @@ -732,8 +653,8 @@ bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t } bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, - uint64_t alreadyGeneratedCoins, uint64_t fee, - uint64_t& reward, int64_t& emissionChange) { + uint64_t alreadyGeneratedCoins, uint64_t fee, + uint64_t& reward, int64_t& emissionChange) { uint64_t minerReward = 0; for (auto& o : b.minerTx.vout) { minerReward += o.amount; @@ -741,21 +662,21 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei std::vector lastBlocksSizes; get_last_n_blocks_sizes(lastBlocksSizes, m_currency.rewardBlocksWindow()); - size_t blocksSizeMedian = epee::misc_utils::median(lastBlocksSizes); + size_t blocksSizeMedian = medianValue(lastBlocksSizes); bool penalizeFee = get_block_major_version_for_height(height) > BLOCK_MAJOR_VERSION_1; if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange)) { - LOG_PRINT_L0("block size " << cumulativeBlockSize << " is bigger than allowed for this blockchain"); + logger(INFO, BRIGHT_WHITE) << "block size " << cumulativeBlockSize << " is bigger than allowed for this blockchain"; return false; } - + if (minerReward > reward) { - LOG_ERROR("Coinbase transaction spend too much money: " << m_currency.formatAmount(minerReward) << - ", block reward is " << m_currency.formatAmount(reward)); + logger(ERROR, BRIGHT_RED) << "Coinbase transaction spend too much money: " << m_currency.formatAmount(minerReward) << + ", block reward is " << m_currency.formatAmount(reward); return false; } else if (minerReward < reward) { - LOG_ERROR("Coinbase transaction doesn't use full amount of block reward: spent " << - m_currency.formatAmount(minerReward) << ", block reward is " << m_currency.formatAmount(reward)); + logger(ERROR, BRIGHT_RED) << "Coinbase transaction doesn't use full amount of block reward: spent " << + m_currency.formatAmount(minerReward) << ", block reward is " << m_currency.formatAmount(reward); return false; } @@ -763,8 +684,13 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei } bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size()); + std::lock_guard lk(m_blockchain_lock); + if (!(from_height < m_blocks.size())) { + logger(ERROR, BRIGHT_RED) + << "Internal error: get_backward_blocks_sizes called with from_height=" + << from_height << ", blockchain height = " << m_blocks.size(); + return false; + } size_t start_offset = (from_height + 1) - std::min((from_height + 1), count); for (size_t i = start_offset; i != from_height + 1; i++) { sz.push_back(m_blocks[i].block_cumulative_size); @@ -774,7 +700,7 @@ bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vect } bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (!m_blocks.size()) { return true; } @@ -786,43 +712,49 @@ uint64_t blockchain_storage::get_current_comulative_blocksize_limit() { return m_current_block_cumul_sz_limit; } -bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) { +bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) { size_t median_size; uint64_t already_generated_coins; - CRITICAL_REGION_BEGIN(m_blockchain_lock); - height = m_blocks.size(); - diffic = get_difficulty_for_next_block(); - CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); + { + std::lock_guard lk(m_blockchain_lock); + height = static_cast(m_blocks.size()); + diffic = get_difficulty_for_next_block(); + if (!(diffic)) { + logger(ERROR, BRIGHT_RED) << "difficulty overhead."; + return false; + } - b = boost::value_initialized(); - b.majorVersion = get_block_major_version_for_height(height); + b = boost::value_initialized(); + b.majorVersion = get_block_major_version_for_height(height); - if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_1; - } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_0; + if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_1; + } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_0; - b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; - b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; - b.parentBlock.numberOfTransactions = 1; - tx_extra_merge_mining_tag mm_tag = AUTO_VAL_INIT(mm_tag); - bool r = append_mm_tag_to_extra(b.parentBlock.minerTx.extra, mm_tag); - CHECK_AND_ASSERT_MES(r, false, "Failed to append merge mining tag to extra of the parent block miner transaction"); - } + b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; + b.parentBlock.numberOfTransactions = 1; + tx_extra_merge_mining_tag mm_tag = boost::value_initialized(); - b.prevId = get_tail_id(); - b.timestamp = time(NULL); + if (!append_mm_tag_to_extra(b.parentBlock.minerTx.extra, mm_tag)) { + logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction"; + return false; + } + } - median_size = m_current_block_cumul_sz_limit / 2; - already_generated_coins = m_blocks.back().already_generated_coins; + b.prevId = get_tail_id(); + b.timestamp = time(NULL); - CRITICAL_REGION_END(); + median_size = m_current_block_cumul_sz_limit / 2; + already_generated_coins = m_blocks.back().already_generated_coins; + } size_t txs_size; uint64_t fee; if (!m_tx_pool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, - txs_size, fee)) { + txs_size, fee)) { return false; } @@ -833,34 +765,34 @@ bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddr for (crypto::hash &cur_hash : b.txHashes) { auto cur_res = m_tx_pool.m_transactions.find(cur_hash); if (cur_res == m_tx_pool.m_transactions.end()) { - LOG_ERROR("Creating block template: error: transaction not found"); + logger(ERROR, BRIGHT_RED) << "Creating block template: error: transaction not found"; continue; } tx_memory_pool::tx_details &cur_tx = cur_res->second; real_txs_size += cur_tx.blob_size; real_fee += cur_tx.fee; if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) { - LOG_ERROR("Creating block template: error: invalid transaction size"); + logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid transaction size"; } uint64_t inputs_amount; if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) { - LOG_ERROR("Creating block template: error: cannot get inputs amount"); + logger(ERROR, BRIGHT_RED) << "Creating block template: error: cannot get inputs amount"; } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) { - LOG_ERROR("Creating block template: error: invalid fee"); + logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid fee"; } } if (txs_size != real_txs_size) { - LOG_ERROR("Creating block template: error: wrongly calculated transaction size"); + logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated transaction size"; } if (fee != real_fee) { - LOG_ERROR("Creating block template: error: wrongly calculated fee"); + logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated fee"; } CRITICAL_REGION_END(); - LOG_PRINT_L1("Creating block template: height " << height << + logger(DEBUGGING) << "Creating block template: height " << height << ", median size " << median_size << ", already generated coins " << already_generated_coins << ", transaction size " << txs_size << - ", fee " << fee); + ", fee " << fee; #endif /* @@ -870,22 +802,26 @@ bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddr //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); - CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); + if (!r) { + logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, first chance"; + return false; + } + size_t cumulative_size = txs_size + get_object_blobsize(b.minerTx); #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << get_object_blobsize(b.minerTx) << - ", cumulative size " << cumulative_size); + logger(DEBUGGING) << "Creating block template: miner tx size " << get_object_blobsize(b.minerTx) << + ", cumulative size " << cumulative_size; #endif for (size_t try_count = 0; try_count != 10; ++try_count) { r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); - CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, second chance"; return false; } size_t coinbase_blob_size = get_object_blobsize(b.minerTx); if (coinbase_blob_size > cumulative_size - txs_size) { cumulative_size = txs_size + coinbase_blob_size; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is greater then before"); + logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << + ", cumulative size " << cumulative_size << " is greater then before"; #endif continue; } @@ -893,33 +829,36 @@ bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddr if (coinbase_blob_size < cumulative_size - txs_size) { size_t delta = cumulative_size - txs_size - coinbase_blob_size; #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << + logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << ", cumulative size " << txs_size + coinbase_blob_size << - " is less then before, adding " << delta << " zero bytes"); + " is less then before, adding " << delta << " zero bytes"; #endif b.minerTx.extra.insert(b.minerTx.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.minerTx)) { - CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.minerTx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx)); + if (!(cumulative_size + 1 == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; } b.minerTx.extra.resize(b.minerTx.extra.size() - 1); if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) { //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size - LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1, LOG_LEVEL_2); + logger(TRACE, BRIGHT_RED) << + "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; cumulative_size += delta - 1; continue; } - LOG_PRINT_GREEN("Setting extra for block: " << b.minerTx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); + logger(DEBUGGING, BRIGHT_GREEN) << + "Setting extra for block: " << b.minerTx.extra.size() << ", try_count=" << try_count; } } - CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.minerTx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx)); + if (!(cumulative_size == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; } #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - LOG_PRINT_L1("Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is now good"); + logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << + ", cumulative size " << cumulative_size << " is now good"; #endif return true; } - LOG_ERROR("Failed to create_block_template with " << 10 << " tries"); + logger(ERROR, BRIGHT_RED) << + "Failed to create_block_template with " << 10 << " tries"; return false; } @@ -927,12 +866,11 @@ bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, s if (timestamps.size() >= m_currency.timestampCheckWindow()) return true; - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); size_t need_elements = m_currency.timestampCheckWindow() - timestamps.size(); - CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); + if (!(start_top_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size(); return false; } size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; - do - { + do { timestamps.push_back(m_blocks[start_top_height].bl.timestamp); if (start_top_height == 0) break; @@ -942,19 +880,20 @@ bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, s } bool blockchain_storage::handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); uint64_t block_height = get_block_height(b); if (block_height == 0) { - LOG_ERROR("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction"); + logger(ERROR, BRIGHT_RED) << + "Block with id: " << Common::podToHex(id) << " (as alternative) have wrong miner transaction"; bvc.m_verifivation_failed = true; return false; } if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { - LOG_PRINT_L2("Block with id: " << id << std::endl << + logger(TRACE) << "Block with id: " << id << std::endl << " can't be accepted for alternative chain, block height: " << block_height << std::endl << - " blockchain height: " << get_current_blockchain_height()); + " blockchain height: " << get_current_blockchain_height(); bvc.m_verifivation_failed = true; return false; } @@ -971,7 +910,7 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: size_t cumulativeSize; if (!getBlockCumulativeSize(b, cumulativeSize)) { - LOG_PRINT_L2("Block with id: " << id << " has at least one unknown transaction. Cumulative size is calculated imprecisely"); + logger(TRACE) << "Block with id: " << id << " has at least one unknown transaction. Cumulative size is calculated imprecisely"; } if (!checkCumulativeBlockSize(id, cumulativeSize, block_height)) { @@ -1000,20 +939,21 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: if (alt_chain.size()) { //make sure that it has right connection to main chain - CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); + if (!(m_blocks.size() > alt_chain.front()->second.height)) { logger(ERROR, BRIGHT_RED) << "main blockchain wrong height"; return false; } crypto::hash h = null_hash; get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); - CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prevId, false, "alternative chain have wrong connection to main chain"); + if (!(h == alt_chain.front()->second.bl.prevId)) { logger(ERROR, BRIGHT_RED) << "alternative chain have wrong connection to main chain"; return false; } complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); } else { - CHECK_AND_ASSERT_MES(mainPrev, false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); + if (!(mainPrev)) { logger(ERROR, BRIGHT_RED) << "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"; return false; } complete_timestamps_vector(mainPrevHeight, timestamps); } //check timestamp correct if (!check_block_timestamp(timestamps, b)) { - LOG_PRINT_RED_L0("Block with id: " << id - << ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp); + logger(INFO, BRIGHT_RED) << + "Block with id: " << id + << ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp; //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed bvc.m_verifivation_failed = true; return false; @@ -1025,7 +965,8 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: bool is_a_checkpoint; if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint)) { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + logger(ERROR, BRIGHT_RED) << + "CHECKPOINT VALIDATION FAILED"; bvc.m_verifivation_failed = true; return false; } @@ -1033,18 +974,20 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: // Always check PoW for alternative blocks m_is_in_checkpoint_zone = false; difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); - CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); + if (!(current_diff)) { logger(ERROR, BRIGHT_RED) << "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"; return false; } crypto::hash proof_of_work = null_hash; if (!m_currency.checkProofOfWork(m_cn_context, bei.bl, current_diff, proof_of_work)) { - LOG_PRINT_RED_L0("Block with id: " << id + logger(INFO, BRIGHT_RED) << + "Block with id: " << id << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work - << ENDL << " expected difficulty: " << current_diff); + << ENDL << " expected difficulty: " << current_diff; bvc.m_verifivation_failed = true; return false; } if (!prevalidate_miner_transaction(b, bei.height)) { - LOG_PRINT_RED_L0("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) have wrong miner transaction."); + logger(INFO, BRIGHT_RED) << + "Block with id: " << Common::podToHex(id) << " (as alternative) have wrong miner transaction."; bvc.m_verifivation_failed = true; return false; } @@ -1054,17 +997,18 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: #ifdef _DEBUG auto i_dres = m_alternative_chains.find(id); - CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist"); + if (!(i_dres == m_alternative_chains.end())) { logger(ERROR, BRIGHT_RED) << "insertion of new alternative block returned as it already exist"; return false; } #endif auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); - CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); + if (!(i_res.second)) { logger(ERROR, BRIGHT_RED) << "insertion of new alternative block returned as it already exist"; return false; } alt_chain.push_back(i_res.first); if (is_a_checkpoint) { //do reorganize! - LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << - ", checkpoint is found in alternative chain on height " << bei.height, LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << + "###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << + ", checkpoint is found in alternative chain on height " << bei.height; bool r = switch_to_alternative_blockchain(alt_chain, true); if (r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; @@ -1072,45 +1016,47 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: } else if (m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain { //do reorganize! - LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty - << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << + "###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty + << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty; bool r = switch_to_alternative_blockchain(alt_chain, false); if (r) bvc.m_added_to_main_chain = true; else bvc.m_verifivation_failed = true; return r; } else { - LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height + logger(INFO, BRIGHT_BLUE) << + "----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height << ENDL << "id:\t" << id << ENDL << "PoW:\t" << proof_of_work - << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); + << ENDL << "difficulty:\t" << current_diff; return true; } - } else { + } else { //block orphaned bvc.m_marked_as_orphaned = true; - LOG_PRINT_RED_L0("Block recognized as orphaned and rejected, id = " << id); + logger(INFO, BRIGHT_RED) << + "Block recognized as orphaned and rejected, id = " << id; } return true; -} + } bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (start_offset >= m_blocks.size()) return false; - for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) - { + for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); std::list missed_ids; get_transactions(m_blocks[i].bl.txHashes, txs, missed_ids); - CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "have missed transactions in own block in main blockchain"); + if (!(!missed_ids.size())) { logger(ERROR, BRIGHT_RED) << "have missed transactions in own block in main blockchain"; return false; } } return true; } bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (start_offset >= m_blocks.size()) { return false; } @@ -1123,7 +1069,7 @@ bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::li } bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); rsp.current_blockchain_height = get_current_blockchain_height(); std::list blocks; get_blocks(arg.blocks, blocks, rsp.missed_ids); @@ -1132,7 +1078,7 @@ bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& std::list missed_tx_id; std::list txs; get_transactions(bl.txHashes, txs, rsp.missed_ids); - CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl)); + if (!(!missed_tx_id.size())) { logger(ERROR, BRIGHT_RED) << "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl); return false; } rsp.blocks.push_back(block_complete_entry()); block_complete_entry& e = rsp.blocks.back(); //pack block @@ -1155,7 +1101,7 @@ bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& } bool blockchain_storage::get_alternative_blocks(std::list& blocks) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); for (auto& alt_bl : m_alternative_chains) { blocks.push_back(alt_bl.second.bl); } @@ -1164,16 +1110,18 @@ bool blockchain_storage::get_alternative_blocks(std::list& blocks) { } size_t blockchain_storage::get_alternative_blocks_count() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_alternative_chains.size(); } bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); const Transaction& tx = transactionByIndex(amount_outs[i].first).tx; - CHECK_AND_ASSERT_MES(tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" - << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx)); - CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(TransactionOutputToKey), false, "unknown tx out type"); + if (!(tx.vout.size() > amount_outs[i].second)) { + logger(ERROR, BRIGHT_RED) << "internal error: in global outs index, transaction out index=" + << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx); return false; + } + if (!(tx.vout[amount_outs[i].second].target.type() == typeid(TransactionOutputToKey))) { logger(ERROR, BRIGHT_RED) << "unknown tx out type"; return false; } //check if transaction is unlocked if (!is_tx_spendtime_unlocked(tx.unlockTime)) @@ -1186,7 +1134,7 @@ bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (amount_outs.empty()) { return 0; } @@ -1203,14 +1151,15 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector lk(m_blockchain_lock); for (uint64_t amount : req.amounts) { 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; auto it = m_outputs.find(amount); if (it == m_outputs.end()) { - LOG_ERROR("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist"); + logger(ERROR, BRIGHT_RED) << + "COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist"; continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist } @@ -1218,7 +1167,7 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split //lets find upper bound of not fresh outs size_t up_index_limit = find_end_of_allowed_index(amount_outs); - CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); + if (!(up_index_limit <= amount_outs.size())) { logger(ERROR, BRIGHT_RED) << "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size(); return false; } if (up_index_limit > 0) { ShuffleGenerator> generator(up_index_limit); @@ -1230,21 +1179,20 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO return true; } -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) { + std::lock_guard lk(m_blockchain_lock); - if (!qblock_ids.size() /*|| !req.m_total_height*/) - { - LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); + if (!qblock_ids.size() /*|| !req.m_total_height*/) { + logger(ERROR, BRIGHT_RED) << + "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"; return false; } //check genesis match - if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) - { - LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " + if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) { + logger(ERROR, BRIGHT_RED) << + "Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) - << "," << ENDL << " dropping connection"); + << "," << ENDL << " dropping connection"; return false; } @@ -1254,59 +1202,61 @@ bool blockchain_storage::find_blockchain_supplement(const std::list lk(m_blockchain_lock); + if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"; return false; } if (i == 0) return m_blocks[i].cumulative_difficulty; return m_blocks[i].cumulative_difficulty - m_blocks[i - 1].cumulative_difficulty; } -void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) -{ +void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) { std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); - if (start_index >= m_blocks.size()) - { - LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size() - 1); + std::lock_guard lk(m_blockchain_lock); + if (start_index >= m_blocks.size()) { + logger(INFO, BRIGHT_WHITE) << + "Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size() - 1; return; } - for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) - { + for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) { ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size << "\nid\t\t" << get_block_hash(m_blocks[i].bl) << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.txHashes.size() << ENDL; } - LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str()); - LOG_PRINT_L0("Blockchain printed with log level 1"); + logger(DEBUGGING) << + "Current blockchain:" << ENDL << ss.str(); + logger(INFO, BRIGHT_WHITE) << + "Blockchain printed with log level 1"; } void blockchain_storage::print_blockchain_index() { std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); std::list blockIds; m_blockIndex.getBlockIds(0, std::numeric_limits::max(), blockIds); - LOG_PRINT_L0("Current blockchain index:" << ENDL); + logger(INFO, BRIGHT_WHITE) << + "Current blockchain index:" << ENDL; size_t height = 0; for (auto i = blockIds.begin(); i != blockIds.end(); ++i, ++height) { - LOG_PRINT_L0("id\t\t" << *i << " height" << height); + logger(INFO, BRIGHT_WHITE) << + "id\t\t" << *i << " height" << height; } } void blockchain_storage::print_blockchain_outs(const std::string& file) { std::stringstream ss; - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); for (const outputs_container::value_type& v : m_outputs) { const std::vector>& vals = v.second; if (!vals.empty()) { @@ -1317,15 +1267,17 @@ void blockchain_storage::print_blockchain_outs(const std::string& file) { } } - if (epee::file_io_utils::save_string_to_file(file, ss.str())) { - LOG_PRINT_L0("Current outputs index writen to file: " << file); + if (Common::saveStringToFile(file, ss.str())) { + logger(INFO, BRIGHT_WHITE) << + "Current outputs index writen to file: " << file; } else { - LOG_PRINT_L0("Failed to write current outputs index to file: " << file); + logger(WARNING, BRIGHT_YELLOW) << + "Failed to write current outputs index to file: " << file; } } bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (!find_blockchain_supplement(qblock_ids, resp.start_height)) return false; @@ -1336,7 +1288,7 @@ bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (!find_blockchain_supplement(qblock_ids, start_height)) { return false; } @@ -1348,15 +1300,14 @@ bool blockchain_storage::find_blockchain_supplement(const std::list mis; get_transactions(m_blocks[i].bl.txHashes, blocks.back().second, mis); - CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); + if (!(!mis.size())) { logger(ERROR, BRIGHT_RED) << "internal error, transaction from block not found"; return false; } } return true; } -bool blockchain_storage::have_block(const crypto::hash& id) -{ - CRITICAL_REGION_LOCAL(m_blockchain_lock); +bool blockchain_storage::have_block(const crypto::hash& id) { + std::lock_guard lk(m_blockchain_lock); if (m_blockIndex.hasBlock(id)) return true; @@ -1367,20 +1318,20 @@ bool blockchain_storage::have_block(const crypto::hash& id) } size_t blockchain_storage::get_total_transactions() { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_transactionMap.size(); } bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); auto it = m_transactionMap.find(tx_id); if (it == m_transactionMap.end()) { - LOG_PRINT_RED_L0("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); + logger(WARNING, YELLOW) << "warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id; return false; } const TransactionEntry& tx = transactionByIndex(it->second); - CHECK_AND_ASSERT_MES(tx.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); + if (!(tx.m_global_output_indexes.size())) { logger(ERROR, BRIGHT_RED) << "internal error: global indexes for transaction " << tx_id << " is empty"; return false; } indexs.resize(tx.m_global_output_indexes.size()); for (size_t i = 0; i < tx.m_global_output_indexes.size(); ++i) { indexs[i] = tx.m_global_output_indexes[i]; @@ -1390,14 +1341,14 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std:: } bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); if (tail) tail->id = get_tail_id(tail->height); bool res = check_tx_inputs(tx, &max_used_block_height); if (!res) return false; - CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); + if (!(max_used_block_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size(); return false; } get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); return true; } @@ -1430,15 +1381,17 @@ bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::ha assert(inputIndex < tx.signatures.size()); if (txin.type() == typeid(TransactionInputToKey)) { const TransactionInputToKey& in_to_key = boost::get(txin); - CHECK_AND_ASSERT_MES(!in_to_key.keyOffsets.empty(), false, "empty in_to_key.keyOffsets in transaction with id " << get_transaction_hash(tx)); + if (!(!in_to_key.keyOffsets.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.keyOffsets in transaction with id " << get_transaction_hash(tx); return false; } if (have_tx_keyimg_as_spent(in_to_key.keyImage)) { - LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.keyImage)); + logger(DEBUGGING) << + "Key image already spent in blockchain: " << Common::podToHex(in_to_key.keyImage); return false; } if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[inputIndex], pmax_used_block_height)) { - LOG_PRINT_L0("Failed to check ring signature for tx " << transactionHash); + logger(INFO, BRIGHT_WHITE) << + "Failed to check ring signature for tx " << transactionHash; return false; } @@ -1450,7 +1403,8 @@ bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::ha ++inputIndex; } else { - LOG_PRINT_L0("Transaction << " << transactionHash << " contains input of unsupported type."); + logger(INFO, BRIGHT_WHITE) << + "Transaction << " << transactionHash << " contains input of unsupported type."; return false; } } @@ -1478,24 +1432,26 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { } bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); - struct outputs_visitor - { + struct outputs_visitor { std::vector& m_results_collector; blockchain_storage& m_bch; - outputs_visitor(std::vector& results_collector, blockchain_storage& bch) :m_results_collector(results_collector), m_bch(bch) - {} + LoggerRef logger; + outputs_visitor(std::vector& results_collector, blockchain_storage& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") { + } + bool handle_output(const Transaction& tx, const TransactionOutput& out) { //check tx unlock time if (!m_bch.is_tx_spendtime_unlocked(tx.unlockTime)) { - LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlockTime = " << tx.unlockTime); + logger(INFO, BRIGHT_WHITE) << + "One of outputs for one of inputs have wrong tx.unlockTime = " << tx.unlockTime; return false; } - if (out.target.type() != typeid(TransactionOutputToKey)) - { - LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); + if (out.target.type() != typeid(TransactionOutputToKey)) { + logger(INFO, BRIGHT_WHITE) << + "Output have wrong type id, which=" << out.target.which(); return false; } @@ -1506,19 +1462,21 @@ bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const //check ring signature std::vector output_keys; - outputs_visitor vi(output_keys, *this); + outputs_visitor vi(output_keys, *this, logger.getLogger()); if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) { - LOG_PRINT_L0("Failed to get output keys for tx with amount = " << m_currency.formatAmount(txin.amount) << - " and count indexes " << txin.keyOffsets.size()); + logger(INFO, BRIGHT_WHITE) << + "Failed to get output keys for tx with amount = " << m_currency.formatAmount(txin.amount) << + " and count indexes " << txin.keyOffsets.size(); return false; } if (txin.keyOffsets.size() != output_keys.size()) { - LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.keyOffsets.size() << " returned wrong keys count " << output_keys.size()); + logger(INFO, BRIGHT_WHITE) << + "Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.keyOffsets.size() << " returned wrong keys count " << output_keys.size(); return false; } - CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + if (!(sig.size() == output_keys.size())) { logger(ERROR, BRIGHT_RED) << "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size(); return false; } if (m_is_in_checkpoint_zone) { return true; } @@ -1533,7 +1491,8 @@ uint64_t blockchain_storage::get_adjusted_time() { bool blockchain_storage::check_block_timestamp_main(const Block& b) { if (b.timestamp > get_adjusted_time() + m_currency.blockFutureTimeLimit()) { - LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); + logger(INFO, BRIGHT_WHITE) << + "Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"; return false; } @@ -1551,11 +1510,12 @@ bool blockchain_storage::check_block_timestamp(std::vector timestamps, return true; } - uint64_t median_ts = epee::misc_utils::median(timestamps); + uint64_t median_ts = medianValue(timestamps); if (b.timestamp < median_ts) { - LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << - ", less than median of last " << m_currency.timestampCheckWindow() << " blocks, " << median_ts); + logger(INFO, BRIGHT_WHITE) << + "Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << + ", less than median of last " << m_currency.timestampCheckWindow() << " blocks, " << median_ts; return false; } @@ -1566,8 +1526,8 @@ bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& b uint64_t height = get_block_height(b); const uint8_t expectedBlockVersion = get_block_major_version_for_height(height); if (b.majorVersion != expectedBlockVersion) { - LOG_PRINT_L2("Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << - ", at height " << height << " expected version is " << static_cast(expectedBlockVersion)); + logger(TRACE) << "Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << + ", at height " << height << " expected version is " << static_cast(expectedBlockVersion); return false; } @@ -1579,13 +1539,15 @@ bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash auto serializer = makeParentBlockSerializer(b, false, false); size_t parentBlockSize; if (!get_object_blobsize(serializer, parentBlockSize)) { - LOG_ERROR("Block " << blockHash << ": failed to determine parent block size"); + logger(ERROR, BRIGHT_RED) << + "Block " << blockHash << ": failed to determine parent block size"; return false; } if (parentBlockSize > 2 * 1024) { - LOG_PRINT_L0("Block " << blockHash << " contains too big parent block: " << parentBlockSize << - " bytes, expected no more than " << 2 * 1024 << " bytes"); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << " contains too big parent block: " << parentBlockSize << + " bytes, expected no more than " << 2 * 1024 << " bytes"; return false; } } @@ -1596,8 +1558,9 @@ bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height) { size_t maxBlockCumulativeSize = m_currency.maxBlockCumulativeSize(height); if (cumulativeBlockSize > maxBlockCumulativeSize) { - LOG_PRINT_L0("Block " << blockId << " is too big: " << cumulativeBlockSize << " bytes, " << - "exptected no more than " << maxBlockCumulativeSize << " bytes"); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockId << " is too big: " << cumulativeBlockSize << " bytes, " << + "exptected no more than " << maxBlockCumulativeSize << " bytes"; return false; } @@ -1628,7 +1591,7 @@ bool blockchain_storage::update_next_comulative_size_limit() { std::vector sz; get_last_n_blocks_sizes(sz, m_currency.rewardBlocksWindow()); - uint64_t median = epee::misc_utils::median(sz); + uint64_t median = medianValue(sz); if (median <= nextBlockGrantedFullRewardZone) { median = nextBlockGrantedFullRewardZone; } @@ -1642,30 +1605,33 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont Block bl = bl_; crypto::hash id; if (!get_block_hash(bl, id)) { - LOG_ERROR("Failed to get block hash, possible block has invalid format"); + logger(ERROR, BRIGHT_RED) << + "Failed to get block hash, possible block has invalid format"; bvc.m_verifivation_failed = true; return false; } bool add_result; - CRITICAL_REGION_BEGIN(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process - CRITICAL_REGION_BEGIN1(m_blockchain_lock); - if (have_block(id)) { - LOG_PRINT_L3("block with id = " << id << " already exists"); - bvc.m_already_exists = true; - return false; - } - //check that block refers to chain tail - if (!(bl.prevId == get_tail_id())) { - //chain switching or wrong block - bvc.m_added_to_main_chain = false; - add_result = handle_alternative_block(bl, id, bvc); - } else { - add_result = pushBlock(bl, bvc); + { //to avoid deadlock lets lock tx_pool for whole add/reorganize process + std::lock_guard poolLock(m_tx_pool); + std::lock_guard bcLock(m_blockchain_lock); + + if (have_block(id)) { + logger(TRACE) << "block with id = " << id << " already exists"; + bvc.m_already_exists = true; + return false; + } + + //check that block refers to chain tail + if (!(bl.prevId == get_tail_id())) { + //chain switching or wrong block + bvc.m_added_to_main_chain = false; + add_result = handle_alternative_block(bl, id, bvc); + } else { + add_result = pushBlock(bl, bvc); + } } - CRITICAL_REGION_END(); - CRITICAL_REGION_END(); if (add_result && bvc.m_added_to_main_chain) { m_observerManager.notify(&IBlockchainStorageObserver::blockchainUpdated); @@ -1679,13 +1645,15 @@ const blockchain_storage::TransactionEntry& blockchain_storage::transactionByInd } bool blockchain_storage::pushBlock(const Block& blockData, block_verification_context& bvc) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - TIME_MEASURE_START(block_processing_time); + std::lock_guard lk(m_blockchain_lock); + + auto blockProcessingStart = std::chrono::steady_clock::now(); crypto::hash blockHash = get_block_hash(blockData); if (m_blockIndex.hasBlock(blockHash)) { - LOG_ERROR("Block " << blockHash << " already exists in blockchain."); + logger(ERROR, BRIGHT_RED) << + "Block " << blockHash << " already exists in blockchain."; bvc.m_verifivation_failed = true; return false; } @@ -1701,42 +1669,52 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co } if (blockData.prevId != get_tail_id()) { - LOG_PRINT_L0("Block " << blockHash << " has wrong prevId: " << blockData.prevId << ", expected: " << get_tail_id()); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << " has wrong prevId: " << blockData.prevId << ", expected: " << get_tail_id(); bvc.m_verifivation_failed = true; return false; } if (!check_block_timestamp_main(blockData)) { - LOG_PRINT_L0("Block " << blockHash << " has invalid timestamp: " << blockData.timestamp); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << " has invalid timestamp: " << blockData.timestamp; bvc.m_verifivation_failed = true; return false; } - TIME_MEASURE_START(target_calculating_time); + auto targetTimeStart = std::chrono::steady_clock::now(); difficulty_type currentDifficulty = get_difficulty_for_next_block(); - TIME_MEASURE_FINISH(target_calculating_time); - CHECK_AND_ASSERT_MES(currentDifficulty, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); + auto target_calculating_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - targetTimeStart).count(); + + if (!(currentDifficulty)) { + logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!"; + return false; + } - TIME_MEASURE_START(longhash_calculating_time); + + auto longhashTimeStart = std::chrono::steady_clock::now(); crypto::hash proof_of_work = null_hash; if (m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) { if (!m_checkpoints.check_block(get_current_blockchain_height(), blockHash)) { - LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + logger(ERROR, BRIGHT_RED) << + "CHECKPOINT VALIDATION FAILED"; bvc.m_verifivation_failed = true; return false; } } else { if (!m_currency.checkProofOfWork(m_cn_context, blockData, currentDifficulty, proof_of_work)) { - LOG_PRINT_L0("Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << ", has too weak proof of work: " << proof_of_work << ", expected difficulty: " << currentDifficulty; bvc.m_verifivation_failed = true; return false; } } - TIME_MEASURE_FINISH(longhash_calculating_time); + auto longhash_calculating_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - longhashTimeStart).count(); if (!prevalidate_miner_transaction(blockData, m_blocks.size())) { - LOG_PRINT_L0("Block " << blockHash << " failed to pass prevalidation"); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << " failed to pass prevalidation"; bvc.m_verifivation_failed = true; return false; } @@ -1746,8 +1724,8 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co BlockEntry block; block.bl = blockData; block.transactions.resize(1); - block.transactions[0].tx = blockData.minerTx; - TransactionIndex transactionIndex = { static_cast(m_blocks.size()), static_cast(0) }; + block.transactions[0].tx = blockData.minerTx; + TransactionIndex transactionIndex = {static_cast(m_blocks.size()), static_cast(0)}; pushTransaction(block, minerTransactionHash, transactionIndex); size_t coinbase_blob_size = get_object_blobsize(blockData.minerTx); @@ -1758,20 +1736,23 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co size_t blob_size = 0; uint64_t fee = 0; if (!m_tx_pool.take_tx(tx_id, block.transactions.back().tx, blob_size, fee)) { - LOG_PRINT_L0("Block " << blockHash << " has at least one unknown transaction: " << tx_id); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << " has at least one unknown transaction: " << tx_id; bvc.m_verifivation_failed = true; - tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + tx_verification_context tvc = boost::value_initialized(); block.transactions.pop_back(); popTransactions(block, minerTransactionHash); return false; } if (!check_tx_inputs(block.transactions.back().tx)) { - LOG_PRINT_L0("Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id); + logger(INFO, BRIGHT_WHITE) << + "Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id; bvc.m_verifivation_failed = true; - tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + tx_verification_context tvc = boost::value_initialized();; if (!m_tx_pool.add_tx(block.transactions.back().tx, tvc, true)) { - LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); + logger(ERROR, BRIGHT_RED) << + "Cannot move transaction from blockchain to transaction pool."; } block.transactions.pop_back(); @@ -1795,7 +1776,7 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co uint64_t reward = 0; uint64_t already_generated_coins = m_blocks.empty() ? 0 : m_blocks.back().already_generated_coins; if (!validate_miner_transaction(blockData, m_blocks.size(), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) { - LOG_PRINT_L0("Block " << blockHash << " has invalid miner transaction"); + logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " has invalid miner transaction"; bvc.m_verifivation_failed = true; popTransactions(block, minerTransactionHash); return false; @@ -1810,13 +1791,16 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co } pushBlock(block); - TIME_MEASURE_FINISH(block_processing_time); - LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash + + auto block_processing_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - blockProcessingStart).count(); + + logger(DEBUGGING) << + "+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << blockHash << ENDL << "PoW:\t" << proof_of_work << ENDL << "HEIGHT " << block.height << ", difficulty:\t" << currentDifficulty << ENDL << "block reward: " << m_currency.formatAmount(reward) << ", fee = " << m_currency.formatAmount(fee_summary) << ", coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size - << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); + << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"; bvc.m_added_to_main_chain = true; @@ -1839,7 +1823,8 @@ bool blockchain_storage::pushBlock(BlockEntry& block) { void blockchain_storage::popBlock(const crypto::hash& blockHash) { if (m_blocks.empty()) { - LOG_ERROR("Attempt to pop block from empty blockchain."); + logger(ERROR, BRIGHT_RED) << + "Attempt to pop block from empty blockchain."; return; } @@ -1855,14 +1840,16 @@ void blockchain_storage::popBlock(const crypto::hash& blockHash) { bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); if (!result.second) { - LOG_ERROR("Duplicate transaction was pushed to blockchain."); + logger(ERROR, BRIGHT_RED) << + "Duplicate transaction was pushed to blockchain."; return false; } TransactionEntry& transaction = block.transactions[transactionIndex.transaction]; if (!checkMultisignatureInputsDiff(transaction.tx)) { - LOG_ERROR("Double spending transaction was pushed to blockchain."); + logger(ERROR, BRIGHT_RED) << + "Double spending transaction was pushed to blockchain."; m_transactionMap.erase(transactionHash); return false; } @@ -1871,7 +1858,8 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& if (transaction.tx.vin[i].type() == typeid(TransactionInputToKey)) { auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).keyImage); if (!result.second) { - LOG_ERROR("Double spending transaction was pushed to blockchain."); + logger(ERROR, BRIGHT_RED) << + "Double spending transaction was pushed to blockchain."; for (size_t j = 0; j < i; ++j) { m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).keyImage); } @@ -1894,12 +1882,12 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) { if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputToKey)) { auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; - transaction.m_global_output_indexes[output] = amountOutputs.size(); + transaction.m_global_output_indexes[output] = static_cast(amountOutputs.size()); amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); } else if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputMultisignature)) { auto& amountOutputs = m_multisignatureOutputs[transaction.tx.vout[output].amount]; - transaction.m_global_output_indexes[output] = amountOutputs.size(); - MultisignatureOutputUsage outputUsage = { transactionIndex, output, false }; + transaction.m_global_output_indexes[output] = static_cast(amountOutputs.size()); + MultisignatureOutputUsage outputUsage = {transactionIndex, output, false}; amountOutputs.push_back(outputUsage); } } @@ -1914,22 +1902,26 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr if (output.target.type() == typeid(TransactionOutputToKey)) { auto amountOutputs = m_outputs.find(output.amount); if (amountOutputs == m_outputs.end()) { - LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - cannot find specific amount in outputs map."; continue; } if (amountOutputs->second.empty()) { - LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - output array for specific amount is empty."; continue; } if (amountOutputs->second.back().first.block != transactionIndex.block || amountOutputs->second.back().first.transaction != transactionIndex.transaction) { - LOG_ERROR("Blockchain consistency broken - invalid transaction index."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - invalid transaction index."; continue; } if (amountOutputs->second.back().second != transaction.vout.size() - 1 - outputIndex) { - LOG_ERROR("Blockchain consistency broken - invalid output index."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - invalid output index."; continue; } @@ -1940,27 +1932,32 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } else if (output.target.type() == typeid(TransactionOutputMultisignature)) { auto amountOutputs = m_multisignatureOutputs.find(output.amount); if (amountOutputs == m_multisignatureOutputs.end()) { - LOG_ERROR("Blockchain consistency broken - cannot find specific amount in outputs map."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - cannot find specific amount in outputs map."; continue; } if (amountOutputs->second.empty()) { - LOG_ERROR("Blockchain consistency broken - output array for specific amount is empty."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - output array for specific amount is empty."; continue; } if (amountOutputs->second.back().isUsed) { - LOG_ERROR("Blockchain consistency broken - attempting to remove used output."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - attempting to remove used output."; continue; } if (amountOutputs->second.back().transactionIndex.block != transactionIndex.block || amountOutputs->second.back().transactionIndex.transaction != transactionIndex.transaction) { - LOG_ERROR("Blockchain consistency broken - invalid transaction index."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - invalid transaction index."; continue; } if (amountOutputs->second.back().outputIndex != transaction.vout.size() - 1 - outputIndex) { - LOG_ERROR("Blockchain consistency broken - invalid output index."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - invalid output index."; continue; } @@ -1975,13 +1972,15 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr if (input.type() == typeid(TransactionInputToKey)) { size_t count = m_spent_keys.erase(::boost::get(input).keyImage); if (count != 1) { - LOG_ERROR("Blockchain consistency broken - cannot find spent key."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - cannot find spent key."; } } else if (input.type() == typeid(TransactionInputMultisignature)) { const TransactionInputMultisignature& in = ::boost::get(input); auto& amountOutputs = m_multisignatureOutputs[in.amount]; if (!amountOutputs[in.outputIndex].isUsed) { - LOG_ERROR("Blockchain consistency broken - multisignature output not marked as used."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - multisignature output not marked as used."; } amountOutputs[in.outputIndex].isUsed = false; @@ -1990,16 +1989,18 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr size_t count = m_transactionMap.erase(transactionHash); if (count != 1) { - LOG_ERROR("Blockchain consistency broken - cannot find transaction by hash."); + logger(ERROR, BRIGHT_RED) << + "Blockchain consistency broken - cannot find transaction by hash."; } } void blockchain_storage::popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash) { for (size_t i = 0; i < block.transactions.size() - 1; ++i) { popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.txHashes[block.transactions.size() - 2 - i]); - tx_verification_context tvc = ::AUTO_VAL_INIT(tvc); + tx_verification_context tvc = boost::value_initialized(); if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) { - LOG_ERROR("Cannot move transaction from blockchain to transaction pool."); + logger(ERROR, BRIGHT_RED) << + "Cannot move transaction from blockchain to transaction pool."; } } @@ -2010,24 +2011,28 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp assert(input.signatures == transactionSignatures.size()); MultisignatureOutputsContainer::const_iterator amountOutputs = m_multisignatureOutputs.find(input.amount); if (amountOutputs == m_multisignatureOutputs.end()) { - LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid amount."); + logger(DEBUGGING) << + "Transaction << " << transactionHash << " contains multisignature input with invalid amount."; return false; } if (input.outputIndex >= amountOutputs->second.size()) { - LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid outputIndex."); + logger(DEBUGGING) << + "Transaction << " << transactionHash << " contains multisignature input with invalid outputIndex."; return false; } const MultisignatureOutputUsage& outputIndex = amountOutputs->second[input.outputIndex]; if (outputIndex.isUsed) { - LOG_PRINT_L1("Transaction << " << transactionHash << " contains double spending multisignature input."); + logger(DEBUGGING) << + "Transaction << " << transactionHash << " contains double spending multisignature input."; return false; } const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx; if (!is_tx_spendtime_unlocked(outputTransaction.unlockTime)) { - LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input which points to a locked transaction."); + logger(DEBUGGING) << + "Transaction << " << transactionHash << " contains multisignature input which points to a locked transaction."; return false; } @@ -2035,7 +2040,8 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp assert(outputTransaction.vout[outputIndex.outputIndex].target.type() == typeid(TransactionOutputMultisignature)); const TransactionOutputMultisignature& output = ::boost::get(outputTransaction.vout[outputIndex.outputIndex].target); if (input.signatures != output.requiredSignatures) { - LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid signature count."); + logger(DEBUGGING) << + "Transaction << " << transactionHash << " contains multisignature input with invalid signature count."; return false; } @@ -2043,7 +2049,8 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp std::size_t outputKeyIndex = 0; while (inputSignatureIndex < input.signatures) { if (outputKeyIndex == output.keys.size()) { - LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid signatures."); + logger(DEBUGGING) << + "Transaction << " << transactionHash << " contains multisignature input with invalid signatures."; return false; } @@ -2058,8 +2065,8 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp } bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); - + std::lock_guard lk(m_blockchain_lock); + if (startOffset >= m_blocks.size()) { return false; } @@ -2076,6 +2083,8 @@ bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, } bool blockchain_storage::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); return m_blockIndex.getBlockIds(startHeight, maxCount, items); } + +} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index 29d38e977a..ba0e29e788 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,8 +22,8 @@ #include "google/sparse_hash_set" #include "google/sparse_hash_map" -#include "common/ObserverManager.h" -#include "common/util.h" +#include "Common/ObserverManager.h" +#include "Common/util.h" #include "cryptonote_core/BlockIndex.h" #include "cryptonote_core/checkpoints.h" #include "cryptonote_core/Currency.h" @@ -35,7 +35,11 @@ #include "cryptonote_core/tx_pool.h" -namespace cryptonote { +#include + +#undef ERROR + +namespace CryptoNote { struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; struct NOTIFY_REQUEST_GET_OBJECTS_request; struct NOTIFY_RESPONSE_GET_OBJECTS_request; @@ -46,15 +50,15 @@ namespace cryptonote { using CryptoNote::BlockInfo; class blockchain_storage : public CryptoNote::ITransactionValidator { public: - blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool); + blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, Logging::ILogger& logger); bool addObserver(IBlockchainStorageObserver* observer); bool removeObserver(IBlockchainStorageObserver* observer); // ITransactionValidator - virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock); - virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed); - virtual bool haveSpentKeyImages(const cryptonote::Transaction& tx); + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock); + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed); + virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx); bool init() { return init(tools::get_default_data_dir(), true); } bool init(const std::string& config_folder, bool load_existing); @@ -84,7 +88,7 @@ namespace cryptonote { uint8_t get_block_major_version_for_height(uint64_t height) const; bool add_new_block(const Block& bl_, block_verification_context& bvc); bool reset_and_set_genesis_block(const Block& b); - bool create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + bool create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& di, uint32_t& height, const blobdata& ex_nonce); bool have_block(const crypto::hash& id); size_t get_total_transactions(); bool get_short_chain_history(std::list& ids); @@ -104,15 +108,15 @@ namespace cryptonote { template bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); for (const auto& bl_id : block_ids) { uint64_t height = 0; if (!m_blockIndex.getBlockHeight(bl_id, height)) { missed_bs.push_back(bl_id); } else { - CHECK_AND_ASSERT_MES(height < m_blocks.size(), false, "Internal error: bl_id=" << epee::string_tools::pod_to_hex(bl_id) - << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size()); + if (!(height < m_blocks.size())) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: bl_id=" << Common::podToHex(bl_id) + << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size(); return false; } blocks.push_back(m_blocks[height].bl); } } @@ -122,7 +126,7 @@ namespace cryptonote { template void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); for (const auto& tx_id : txs_ids) { auto it = m_transactionMap.find(tx_id); @@ -200,7 +204,7 @@ namespace cryptonote { const Currency& m_currency; tx_memory_pool& m_tx_pool; - epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock + std::recursive_mutex m_blockchain_lock; // TODO: add here reader/writer lock crypto::cn_context m_cn_context; tools::ObserverManager m_observerManager; @@ -227,6 +231,8 @@ namespace cryptonote { MultisignatureOutputsContainer m_multisignatureOutputs; UpgradeDetector m_upgradeDetector; + Logging::LoggerRef logger; + bool storeCache(); template bool scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); @@ -278,11 +284,11 @@ namespace cryptonote { private: blockchain_storage& m_bc; - epee::critical_region_t m_lock; + std::lock_guard m_lock; }; template bool blockchain_storage::scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { - CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::lock_guard lk(m_blockchain_lock); auto it = m_outputs.find(tx_in_to_key.amount); if (it == m_outputs.end() || !tx_in_to_key.keyOffsets.size()) return false; @@ -292,18 +298,25 @@ namespace cryptonote { size_t count = 0; for (uint64_t i : absolute_offsets) { if(i >= amount_outs_vec.size() ) { - LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1); + logger(Logging::INFO) << "Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1; return false; } //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); - //CHECK_AND_ASSERT_MES(tx_it != m_transactionMap.end(), false, "Wrong transaction id in output indexes: " << epee::string_tools::pod_to_hex(amount_outs_vec[i].first)); + //if (!(tx_it != m_transactionMap.end())) { logger(ERROR, BRIGHT_RED) << "Wrong transaction id in output indexes: " << Common::podToHex(amount_outs_vec[i].first); return false; } const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first); - CHECK_AND_ASSERT_MES(amount_outs_vec[i].second < tx.tx.vout.size(), false, - "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx.tx.vout.size()); + + if (!(amount_outs_vec[i].second < tx.tx.vout.size())) { + logger(Logging::ERROR, Logging::BRIGHT_RED) + << "Wrong index in transaction outputs: " + << amount_outs_vec[i].second << ", expected less then " + << tx.tx.vout.size(); + return false; + } + if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second])) { - LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); + logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i; return false; } diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h index 5036e82118..af5261170f 100644 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,7 +24,7 @@ namespace boost template - void serialize(archive_t & ar, cryptonote::blockchain_storage::transaction_chain_entry& te, const unsigned int version) + void serialize(archive_t & ar, CryptoNote::blockchain_storage::transaction_chain_entry& te, const unsigned int version) { ar & te.tx; ar & te.m_keeper_block_height; @@ -33,7 +33,7 @@ namespace boost } template - void serialize(archive_t & ar, cryptonote::blockchain_storage::block_extended_info& ei, const unsigned int version) + void serialize(archive_t & ar, CryptoNote::blockchain_storage::block_extended_info& ei, const unsigned int version) { ar & ei.bl; ar & ei.height; diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp index 2ba058a53d..4b48bbb4d9 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/cryptonote_core/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,69 +15,72 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "include_base_utils.h" -using namespace epee; - #include "checkpoints.h" +#include "Common/StringTools.h" -namespace cryptonote -{ - //--------------------------------------------------------------------------- - checkpoints::checkpoints() - { - } - //--------------------------------------------------------------------------- - bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) - { - crypto::hash h = null_hash; - bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); - CHECK_AND_ASSERT_MES(r, false, "WRONG HASH IN CHECKPOINTS!!!"); - CHECK_AND_ASSERT_MES(0 == m_points.count(height), false, "WRONG HASH IN CHECKPOINTS!!!"); - m_points[height] = h; - return true; - } - //--------------------------------------------------------------------------- - bool checkpoints::is_in_checkpoint_zone(uint64_t height) const - { - return !m_points.empty() && (height <= (--m_points.end())->first); - } - //--------------------------------------------------------------------------- - bool checkpoints::check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const - { - auto it = m_points.find(height); - is_a_checkpoint = it != m_points.end(); - if(!is_a_checkpoint) - return true; +using namespace Logging; - if(it->second == h) - { - LOG_PRINT_GREEN("CHECKPOINT PASSED FOR HEIGHT " << height << " " << h, LOG_LEVEL_0); - return true; - }else - { - LOG_ERROR("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH: " << it->second << ", FETCHED HASH: " << h); - return false; - } +namespace CryptoNote { +//--------------------------------------------------------------------------- +checkpoints::checkpoints(Logging::ILogger &log) : logger(log, "checkpoints") {} +//--------------------------------------------------------------------------- +bool checkpoints::add_checkpoint(uint64_t height, const std::string &hash_str) { + crypto::hash h = null_hash; + + if (!Common::podFromHex(hash_str, h)) { + logger(ERROR) << "WRONG HASH IN CHECKPOINTS!!!"; + return false; } - //--------------------------------------------------------------------------- - bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const - { - bool ignored; - return check_block(height, h, ignored); + + if (!(0 == m_points.count(height))) { + logger(ERROR) << "WRONG HASH IN CHECKPOINTS!!!"; + return false; } - //--------------------------------------------------------------------------- - bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const - { - if (0 == block_height) - return false; - auto it = m_points.upper_bound(blockchain_height); - // Is blockchain_height before the first checkpoint? - if (it == m_points.begin()) - return true; + m_points[height] = h; + return true; +} +//--------------------------------------------------------------------------- +bool checkpoints::is_in_checkpoint_zone(uint64_t height) const { + return !m_points.empty() && (height <= (--m_points.end())->first); +} +//--------------------------------------------------------------------------- +bool checkpoints::check_block(uint64_t height, const crypto::hash &h, + bool &is_a_checkpoint) const { + auto it = m_points.find(height); + is_a_checkpoint = it != m_points.end(); + if (!is_a_checkpoint) + return true; - --it; - uint64_t checkpoint_height = it->first; - return checkpoint_height < block_height; + if (it->second == h) { + logger(Logging::INFO, Logging::GREEN) + << "CHECKPOINT PASSED FOR HEIGHT " << height << " " << h; + return true; + } else { + logger(Logging::ERROR) << "CHECKPOINT FAILED FOR HEIGHT " << height + << ". EXPECTED HASH: " << it->second + << ", FETCHED HASH: " << h; + return false; } } +//--------------------------------------------------------------------------- +bool checkpoints::check_block(uint64_t height, const crypto::hash &h) const { + bool ignored; + return check_block(height, h, ignored); +} +//--------------------------------------------------------------------------- +bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, + uint64_t block_height) const { + if (0 == block_height) + return false; + + auto it = m_points.upper_bound(blockchain_height); + // Is blockchain_height before the first checkpoint? + if (it == m_points.begin()) + return true; + + --it; + uint64_t checkpoint_height = it->first; + return checkpoint_height < block_height; +} +} diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h index 1121711500..0778f1a28a 100644 --- a/src/cryptonote_core/checkpoints.h +++ b/src/cryptonote_core/checkpoints.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,14 +18,15 @@ #pragma once #include #include "cryptonote_basic_impl.h" +#include - -namespace cryptonote +namespace CryptoNote { class checkpoints { public: - checkpoints(); + checkpoints(Logging::ILogger& log); + bool add_checkpoint(uint64_t height, const std::string& hash_str); bool is_in_checkpoint_zone(uint64_t height) const; bool check_block(uint64_t height, const crypto::hash& h) const; @@ -34,5 +35,6 @@ namespace cryptonote private: std::map m_points; + Logging::LoggerRef logger; }; } diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_core/connection_context.h deleted file mode 100644 index f18272af18..0000000000 --- a/src/cryptonote_core/connection_context.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include - -#include "net/net_utils_base.h" -#include "copyable_atomic.h" - -#include "crypto/hash.h" - -namespace cryptonote -{ - - struct cryptonote_connection_context: public epee::net_utils::connection_context_base - { - - enum state - { - state_befor_handshake = 0, //default state - state_synchronizing, - state_idle, - state_normal - }; - - state m_state; - std::list m_needed_objects; - std::unordered_set m_requested_objects; - uint64_t m_remote_blockchain_height; - uint64_t m_last_response_height; - epee::copyable_atomic m_callback_request_count; //in debug purpose: problem with double callback rise - //size_t m_score; TODO: add score calculations - }; - - inline std::string get_protocol_state_string(cryptonote_connection_context::state s) - { - switch (s) - { - case cryptonote_connection_context::state_befor_handshake: - return "state_befor_handshake"; - case cryptonote_connection_context::state_synchronizing: - return "state_synchronizing"; - case cryptonote_connection_context::state_idle: - return "state_idle"; - case cryptonote_connection_context::state_normal: - return "state_normal"; - default: - return "unknown"; - } - } - -} diff --git a/src/cryptonote_core/cryptonote_basic.cpp b/src/cryptonote_core/cryptonote_basic.cpp new file mode 100755 index 0000000000..81d1234715 --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic.cpp @@ -0,0 +1,321 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "cryptonote_basic.h" +#include "../Common/StreamTools.h" +#include "../Common/StringTools.h" + +using Common::IInputStream; +using Common::IOutputStream; +using Common::read; +using Common::readVarint; +using Common::toString; + +namespace CryptoNote { + +void TransactionInputGenerate::serialize(IOutputStream& out) const { + writeVarint(out, height); +} + +TransactionInputGenerate TransactionInputGenerate::deserialize(IInputStream& in) { + TransactionInputGenerate input; + readVarint(in, input.height); + return input; +} + +void TransactionInputToKey::serialize(IOutputStream& out) const { + writeVarint(out, amount); + writeVarint(out, keyOffsets.size()); + for (uint64_t outputIndex : keyOffsets) { + writeVarint(out, outputIndex); + } + + write(out, &keyImage, sizeof(keyImage)); +} + +TransactionInputToKey TransactionInputToKey::deserialize(IInputStream& in) { + TransactionInputToKey input; + readVarint(in, input.amount); + input.keyOffsets.resize(readVarint(in)); + for (uint64_t& outputIndex : input.keyOffsets) { + readVarint(in, outputIndex); + } + + read(in, &input.keyImage, sizeof(input.keyImage)); + return input; +} + +void TransactionInputMultisignature::serialize(IOutputStream& out) const { + writeVarint(out, amount); + writeVarint(out, signatures); + writeVarint(out, outputIndex); +} + +TransactionInputMultisignature TransactionInputMultisignature::deserialize(IInputStream& in) { + TransactionInputMultisignature input; + readVarint(in, input.amount); + readVarint(in, input.signatures); + readVarint(in, input.outputIndex); + return input; +} + +void TransactionOutputToKey::serialize(IOutputStream& out) const { + write(out, &key, sizeof(key)); +} + +TransactionOutputToKey TransactionOutputToKey::deserialize(IInputStream& in) { + TransactionOutputToKey output; + read(in, &output.key, sizeof(output.key)); + return output; +} + +void TransactionOutputMultisignature::serialize(IOutputStream& out) const { + writeVarint(out, keys.size()); + for (const crypto::public_key& key : keys) { + write(out, &key, sizeof(key)); + } + + writeVarint(out, requiredSignatures); +} + +TransactionOutputMultisignature TransactionOutputMultisignature::deserialize(IInputStream& in) { + TransactionOutputMultisignature output; + output.keys.resize(readVarint(in)); + for (crypto::public_key& key : output.keys) { + read(in, &key, sizeof(key)); + } + + readVarint(in, output.requiredSignatures); + if (output.requiredSignatures > output.keys.size()) { + throw std::runtime_error("TransactionOutputMultisignature::deserialize"); + } + + return output; +} + +void TransactionOutput::serialize(IOutputStream& out) const { + writeVarint(out, amount); + if (target.type() == typeid(TransactionOutputToKey)) { + write(out, static_cast(2)); + boost::get(target).serialize(out); + } else { + write(out, static_cast(3)); + boost::get(target).serialize(out); + } +} + +TransactionOutput TransactionOutput::deserialize(IInputStream& in) { + TransactionOutput output; + readVarint(in, output.amount); + uint8_t targetType = read(in); + if (targetType == 2) { + output.target = TransactionOutputToKey::deserialize(in); + } else if (targetType == 3) { + output.target = TransactionOutputMultisignature::deserialize(in); + } else { + throw std::runtime_error("TransactionOutput::deserialize"); + } + + return output; +} + +void Transaction::serialize(IOutputStream& out) const { + writeVarint(out, version); + writeVarint(out, unlockTime); + writeVarint(out, vin.size()); + for (const TransactionInput& input : vin) { + if (input.type() == typeid(TransactionInputGenerate)) { + write(out, static_cast(255)); + boost::get(input).serialize(out); + } else if (input.type() == typeid(TransactionInputToKey)) { + write(out, static_cast(2)); + boost::get(input).serialize(out); + } else { + write(out, static_cast(3)); + boost::get(input).serialize(out); + } + } + + writeVarint(out, vout.size()); + for (const TransactionOutput& output : vout) { + output.serialize(out); + } + + writeVarint(out, extra.size()); + write(out, extra); + std::size_t signatureCount = 0; + for (const std::vector& inputSignatures : signatures) { + signatureCount += inputSignatures.size(); + } + + for (const std::vector& inputSignatures : signatures) { + for (const crypto::signature& signature : inputSignatures) { + write(out, &signature, sizeof(signature)); + } + } +} + +Transaction Transaction::deserialize(IInputStream& in) { + Transaction transaction; + readVarint(in, transaction.version); + if (transaction.version != CURRENT_TRANSACTION_VERSION) { + throw std::runtime_error("Transaction::deserialize"); + } + + readVarint(in, transaction.unlockTime); + transaction.vin.resize(readVarint(in)); + for (TransactionInput& input : transaction.vin) { + uint8_t inputType = read(in); + if (inputType == 255) { + input = TransactionInputGenerate::deserialize(in); + } else if (inputType == 2) { + input = TransactionInputToKey::deserialize(in); + } else if (inputType == 3) { + input = TransactionInputMultisignature::deserialize(in); + } else { + throw std::runtime_error("Transaction::deserialize"); + } + } + + transaction.vout.resize(readVarint(in)); + for (TransactionOutput& output : transaction.vout) { + output = TransactionOutput::deserialize(in); + } + + transaction.extra.resize(readVarint(in)); + read(in, transaction.extra.data(), transaction.extra.size()); + transaction.signatures.resize(transaction.vin.size()); + for (std::size_t i = 0; i < transaction.vin.size(); ++i) { + std::size_t signatureCount; + if (transaction.vin[i].type() == typeid(TransactionInputGenerate)) { + signatureCount = 0; + } else if (transaction.vin[i].type() == typeid(TransactionInputToKey)) { + signatureCount = boost::get(transaction.vin[i]).keyOffsets.size(); + } else { + signatureCount = boost::get(transaction.vin[i]).signatures; + } + + transaction.signatures[i].resize(signatureCount); + for (crypto::signature& signature : transaction.signatures[i]) { + read(in, &signature, sizeof(signature)); + } + } + + return transaction; +} + +void Block::serialize(IOutputStream& out) const { + writeVarint(out, majorVersion); + writeVarint(out, minorVersion); + if (majorVersion == BLOCK_MAJOR_VERSION_1) { + writeVarint(out, timestamp); + write(out, &prevId, sizeof(prevId)); + write(out, nonce); + } else { + write(out, &prevId, sizeof(prevId)); + writeVarint(out, parentBlock.majorVersion); + writeVarint(out, parentBlock.minorVersion); + writeVarint(out, timestamp); + write(out, &parentBlock.prevId, sizeof(parentBlock.prevId)); + write(out, nonce); + writeVarint(out, parentBlock.numberOfTransactions); + for (const crypto::hash& hash : parentBlock.minerTxBranch) { + write(out, &hash, sizeof(hash)); + } + + parentBlock.minerTx.serialize(out); + for (const crypto::hash& hash : parentBlock.blockchainBranch) { + write(out, &hash, sizeof(hash)); + } + } + + minerTx.serialize(out); + writeVarint(out, txHashes.size()); + for (const crypto::hash& hash : txHashes) { + write(out, &hash, sizeof(hash)); + } +} + +Block Block::deserialize(IInputStream& in) { + Block block; + readVarint(in, block.majorVersion); + if (block.majorVersion == BLOCK_MAJOR_VERSION_1) { + readVarint(in, block.minorVersion); + if (block.minorVersion != BLOCK_MINOR_VERSION_0 && block.minorVersion != BLOCK_MINOR_VERSION_1) { + throw std::runtime_error("Invalid block minor version (" + toString(static_cast(block.minorVersion)) + ") for major version 1"); + } + + readVarint(in, block.timestamp); + read(in, &block.prevId, sizeof(block.prevId)); + read(in, block.nonce); + } else if (block.majorVersion == BLOCK_MAJOR_VERSION_2) { + readVarint(in, block.minorVersion); + if (block.minorVersion != BLOCK_MINOR_VERSION_0) { + throw std::runtime_error("Invalid block minor version (" + toString(static_cast(block.minorVersion)) + ") for major version 2"); + } + + read(in, &block.prevId, sizeof(block.prevId)); + readVarint(in, block.parentBlock.majorVersion); + if (block.parentBlock.majorVersion != BLOCK_MAJOR_VERSION_1) { + throw std::runtime_error("Invalid parent block major version (" + toString(static_cast(block.parentBlock.majorVersion)) + ')'); + } + + readVarint(in, block.parentBlock.minorVersion); + if (block.parentBlock.minorVersion != BLOCK_MINOR_VERSION_0) { + throw std::runtime_error("Invalid parent block minor version (" + toString(static_cast(block.parentBlock.minorVersion)) + ')'); + } + + + readVarint(in, block.timestamp); + read(in, &block.parentBlock.prevId, sizeof(block.parentBlock.prevId)); + read(in, block.nonce); + readVarint(in, block.parentBlock.numberOfTransactions); + + block.parentBlock.minerTxBranch.resize(crypto::tree_depth(block.parentBlock.numberOfTransactions)); + for (crypto::hash& hash : block.parentBlock.minerTxBranch) { + read(in, &hash, sizeof(hash)); + } + + block.parentBlock.minerTx = Transaction::deserialize(in); + tx_extra_merge_mining_tag mergedMiningTag; + if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mergedMiningTag)) { + throw std::runtime_error("Cannot get merged mining tag"); + } + + if (mergedMiningTag.depth > 8 * sizeof(crypto::hash)) { + throw std::runtime_error("Invalid merged mining tag depth (" + toString(mergedMiningTag.depth) + ')'); + } + + + block.parentBlock.blockchainBranch.resize(mergedMiningTag.depth); + for (crypto::hash& hash : block.parentBlock.blockchainBranch) { + read(in, &hash, sizeof(hash)); + } + } else { + throw std::runtime_error("Invalid block major version (" + toString(static_cast(block.majorVersion)) + ')'); + } + + block.minerTx = Transaction::deserialize(in); + block.txHashes.resize(readVarint(in)); + for (crypto::hash& hash : block.txHashes) { + read(in, &hash, sizeof(hash)); + } + + return block; +} + +} diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 9e804c0b80..99ef0be05d 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -29,13 +29,17 @@ #include "serialization/binary_archive.h" #include "serialization/crypto.h" #include "serialization/keyvalue_serialization.h" // eepe named serialization -#include "serialization/debug_archive.h" #include "serialization/json_archive.h" #include "serialization/serialization.h" #include "serialization/variant.h" #include "cryptonote_config.h" -namespace cryptonote { +namespace Common { +class IInputStream; +class IOutputStream; +} + +namespace CryptoNote { class account_base; struct account_keys; struct Block; @@ -52,11 +56,14 @@ namespace cryptonote { /* inputs */ struct TransactionInputGenerate { - size_t height; + uint32_t height; BEGIN_SERIALIZE_OBJECT() VARINT_FIELD(height); END_SERIALIZE() + + void serialize(Common::IOutputStream& out) const; + static TransactionInputGenerate deserialize(Common::IInputStream& in); }; struct TransactionInputToKey { @@ -68,7 +75,10 @@ namespace cryptonote { VARINT_FIELD(amount); FIELD(keyOffsets); FIELD(keyImage); - END_SERIALIZE() + END_SERIALIZE() + + void serialize(Common::IOutputStream& out) const; + static TransactionInputToKey deserialize(Common::IInputStream& in); }; struct TransactionInputMultisignature { @@ -81,6 +91,9 @@ namespace cryptonote { VARINT_FIELD(signatures); VARINT_FIELD(outputIndex); END_SERIALIZE() + + void serialize(Common::IOutputStream& out) const; + static TransactionInputMultisignature deserialize(Common::IInputStream& in); }; /* outputs */ @@ -89,6 +102,9 @@ namespace cryptonote { TransactionOutputToKey() { } TransactionOutputToKey(const crypto::public_key &_key) : key(_key) { } crypto::public_key key; + + void serialize(Common::IOutputStream& out) const; + static TransactionOutputToKey deserialize(Common::IInputStream& in); }; struct TransactionOutputMultisignature { @@ -99,6 +115,9 @@ namespace cryptonote { FIELD(keys); VARINT_FIELD(requiredSignatures); END_SERIALIZE() + + void serialize(Common::IOutputStream& out) const; + static TransactionOutputMultisignature deserialize(Common::IInputStream& in); }; struct TransactionInputToScript { @@ -142,11 +161,14 @@ namespace cryptonote { VARINT_FIELD(amount); FIELD(target); END_SERIALIZE() + + void serialize(Common::IOutputStream& out) const; + static TransactionOutput deserialize(Common::IInputStream& in); }; struct TransactionPrefix { // tx information - size_t version; + uint8_t version; uint64_t unlockTime; //number of block (or time), used as a limitation like: spend this tx not early then block/time std::vector vin; @@ -231,13 +253,16 @@ namespace cryptonote { return boost::apply_visitor(txin_signature_size_visitor(), input); } + + void serialize(Common::IOutputStream& out) const; + static Transaction deserialize(Common::IInputStream& in); }; struct ParentBlock { uint8_t majorVersion; uint8_t minorVersion; crypto::hash prevId; - size_t numberOfTransactions; + uint16_t numberOfTransactions; std::vector minerTxBranch; Transaction minerTx; std::vector blockchainBranch; @@ -368,6 +393,9 @@ namespace cryptonote { FIELD(minerTx); FIELD(txHashes); END_SERIALIZE() + + void serialize(Common::IOutputStream& out) const; + static Block deserialize(Common::IInputStream& in); }; inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly) { @@ -397,40 +425,28 @@ namespace cryptonote { }; } -BLOB_SERIALIZER(cryptonote::TransactionOutputToKey); - -VARIANT_TAG(binary_archive, cryptonote::TransactionInputGenerate, 0xff); -VARIANT_TAG(binary_archive, cryptonote::TransactionInputToScript, 0x0); -VARIANT_TAG(binary_archive, cryptonote::TransactionInputToScriptHash, 0x1); -VARIANT_TAG(binary_archive, cryptonote::TransactionInputToKey, 0x2); -VARIANT_TAG(binary_archive, cryptonote::TransactionInputMultisignature, 0x3); -VARIANT_TAG(binary_archive, cryptonote::TransactionOutputToScript, 0x0); -VARIANT_TAG(binary_archive, cryptonote::TransactionOutputToScriptHash, 0x1); -VARIANT_TAG(binary_archive, cryptonote::TransactionOutputToKey, 0x2); -VARIANT_TAG(binary_archive, cryptonote::TransactionOutputMultisignature, 0x3); -VARIANT_TAG(binary_archive, cryptonote::Transaction, 0xcc); -VARIANT_TAG(binary_archive, cryptonote::Block, 0xbb); - -VARIANT_TAG(json_archive, cryptonote::TransactionInputGenerate, "generate"); -VARIANT_TAG(json_archive, cryptonote::TransactionInputToScript, "script"); -VARIANT_TAG(json_archive, cryptonote::TransactionInputToScriptHash, "scripthash"); -VARIANT_TAG(json_archive, cryptonote::TransactionInputToKey, "key"); -VARIANT_TAG(json_archive, cryptonote::TransactionInputMultisignature, "multisignature"); -VARIANT_TAG(json_archive, cryptonote::TransactionOutputToScript, "script"); -VARIANT_TAG(json_archive, cryptonote::TransactionOutputToScriptHash, "scripthash"); -VARIANT_TAG(json_archive, cryptonote::TransactionOutputToKey, "key"); -VARIANT_TAG(json_archive, cryptonote::TransactionOutputMultisignature, "multisignature"); -VARIANT_TAG(json_archive, cryptonote::Transaction, "Transaction"); -VARIANT_TAG(json_archive, cryptonote::Block, "Block"); - -VARIANT_TAG(debug_archive, cryptonote::TransactionInputGenerate, "generate"); -VARIANT_TAG(debug_archive, cryptonote::TransactionInputToScript, "script"); -VARIANT_TAG(debug_archive, cryptonote::TransactionInputToScriptHash, "scripthash"); -VARIANT_TAG(debug_archive, cryptonote::TransactionInputToKey, "key"); -VARIANT_TAG(debug_archive, cryptonote::TransactionInputMultisignature, "multisignature"); -VARIANT_TAG(debug_archive, cryptonote::TransactionOutputToScript, "script"); -VARIANT_TAG(debug_archive, cryptonote::TransactionOutputToScriptHash, "scripthash"); -VARIANT_TAG(debug_archive, cryptonote::TransactionOutputToKey, "key"); -VARIANT_TAG(debug_archive, cryptonote::TransactionOutputMultisignature, "multisignature"); -VARIANT_TAG(debug_archive, cryptonote::Transaction, "Transaction"); -VARIANT_TAG(debug_archive, cryptonote::Block, "Block"); +BLOB_SERIALIZER(CryptoNote::TransactionOutputToKey); + +VARIANT_TAG(binary_archive, CryptoNote::TransactionInputGenerate, 0xff); +VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToScript, 0x0); +VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToScriptHash, 0x1); +VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToKey, 0x2); +VARIANT_TAG(binary_archive, CryptoNote::TransactionInputMultisignature, 0x3); +VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToScript, 0x0); +VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToScriptHash, 0x1); +VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToKey, 0x2); +VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputMultisignature, 0x3); +VARIANT_TAG(binary_archive, CryptoNote::Transaction, 0xcc); +VARIANT_TAG(binary_archive, CryptoNote::Block, 0xbb); + +VARIANT_TAG(json_archive, CryptoNote::TransactionInputGenerate, "generate"); +VARIANT_TAG(json_archive, CryptoNote::TransactionInputToScript, "script"); +VARIANT_TAG(json_archive, CryptoNote::TransactionInputToScriptHash, "scripthash"); +VARIANT_TAG(json_archive, CryptoNote::TransactionInputToKey, "key"); +VARIANT_TAG(json_archive, CryptoNote::TransactionInputMultisignature, "multisignature"); +VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToScript, "script"); +VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToScriptHash, "scripthash"); +VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToKey, "key"); +VARIANT_TAG(json_archive, CryptoNote::TransactionOutputMultisignature, "multisignature"); +VARIANT_TAG(json_archive, CryptoNote::Transaction, "Transaction"); +VARIANT_TAG(json_archive, CryptoNote::Block, "Block"); diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp index b2952a560c..567333a536 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,21 +15,15 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "include_base_utils.h" -using namespace epee; - #include "cryptonote_basic_impl.h" -#include "string_tools.h" #include "serialization/binary_utils.h" #include "serialization/vector.h" #include "cryptonote_format_utils.h" -#include "cryptonote_config.h" -#include "misc_language.h" -#include "common/base58.h" +#include "Common/base58.h" #include "crypto/hash.h" -#include "common/int-util.h" +#include "Common/int-util.h" -namespace cryptonote { +namespace CryptoNote { /************************************************************************/ /* Cryptonote helper functions */ @@ -84,46 +78,28 @@ namespace cryptonote { //----------------------------------------------------------------------- bool parseAccountAddressString(uint64_t& prefix, AccountPublicAddress& adr, const std::string& str) { blobdata data; - if (!tools::base58::decode_addr(str, prefix, data)) { - LOG_PRINT_L1("Invalid address format"); - return false; - } - - if (!::serialization::parse_binary(data, adr)) { - LOG_PRINT_L1("Account public address keys can't be parsed"); - return false; - } - if (!crypto::check_key(adr.m_spendPublicKey) || !crypto::check_key(adr.m_viewPublicKey)) { - LOG_PRINT_L1("Failed to validate address keys"); - return false; - } - - return true; + return + tools::base58::decode_addr(str, prefix, data) && + ::serialization::parse_binary(data, adr) && + crypto::check_key(adr.m_spendPublicKey) && + crypto::check_key(adr.m_viewPublicKey); } //----------------------------------------------------------------------- - bool operator ==(const cryptonote::Transaction& a, const cryptonote::Transaction& b) { - return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); + bool operator ==(const CryptoNote::Transaction& a, const CryptoNote::Transaction& b) { + return CryptoNote::get_transaction_hash(a) == CryptoNote::get_transaction_hash(b); } //----------------------------------------------------------------------- - bool operator ==(const cryptonote::Block& a, const cryptonote::Block& b) { - return cryptonote::get_block_hash(a) == cryptonote::get_block_hash(b); + bool operator ==(const CryptoNote::Block& a, const CryptoNote::Block& b) { + return CryptoNote::get_block_hash(a) == CryptoNote::get_block_hash(b); } } //-------------------------------------------------------------------------------- -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); - if (!res || buf.size() != sizeof(crypto::hash)) - { +bool parse_hash256(const std::string& str_hash, crypto::hash& hash) { + if (!Common::podFromHex(str_hash, hash)) { std::cout << "invalid hash format: <" << str_hash << '>' << std::endl; return false; } - else - { - buf.copy(reinterpret_cast(&hash), sizeof(crypto::hash)); - return true; - } + return true; } diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h index 793df885b4..ed7b936994 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,15 +17,13 @@ #pragma once -//epee -#include "string_tools.h" - +#include "Common/StringTools.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_basic.h" -namespace cryptonote { +namespace CryptoNote { /************************************************************************/ /* */ /************************************************************************/ @@ -46,16 +44,16 @@ namespace cryptonote { bool parseAccountAddressString(uint64_t& prefix, AccountPublicAddress& adr, const std::string& str); bool is_coinbase(const Transaction& tx); - bool operator ==(const cryptonote::Transaction& a, const cryptonote::Transaction& b); - bool operator ==(const cryptonote::Block& a, const cryptonote::Block& b); + bool operator ==(const CryptoNote::Transaction& a, const CryptoNote::Transaction& b); + bool operator ==(const CryptoNote::Block& a, const CryptoNote::Block& b); } template std::ostream &print256(std::ostream &o, const T &v) { - return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; + return o << "<" << Common::podToHex(v) << ">"; } -bool parse_hash256(const std::string str_hash, crypto::hash& hash); +bool parse_hash256(const std::string& str_hash, crypto::hash& hash); namespace crypto { inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index d1a7b5d75f..91b02cd894 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -25,10 +25,10 @@ #include #include #include "cryptonote_basic.h" -#include "common/unordered_containers_boost_serialization.h" +#include "Common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" -//namespace cryptonote { +//namespace CryptoNote { namespace boost { namespace serialization @@ -67,47 +67,47 @@ namespace boost a & reinterpret_cast(x); } - template void serialize(Archive& archive, cryptonote::TransactionInputToScript&, unsigned int version) { + template void serialize(Archive& archive, CryptoNote::TransactionInputToScript&, unsigned int version) { assert(false); } - template void serialize(Archive& archive, cryptonote::TransactionInputToScriptHash&, unsigned int version) { + template void serialize(Archive& archive, CryptoNote::TransactionInputToScriptHash&, unsigned int version) { assert(false); } - template void serialize(Archive& archive, cryptonote::TransactionOutputToScript&, unsigned int version) { + template void serialize(Archive& archive, CryptoNote::TransactionOutputToScript&, unsigned int version) { assert(false); } - template void serialize(Archive& archive, cryptonote::TransactionOutputToScriptHash&, unsigned int version) { + template void serialize(Archive& archive, CryptoNote::TransactionOutputToScriptHash&, unsigned int version) { assert(false); } - template void serialize(Archive& archive, cryptonote::TransactionInputMultisignature &output, unsigned int version) { + template void serialize(Archive& archive, CryptoNote::TransactionInputMultisignature &output, unsigned int version) { archive & output.amount; archive & output.signatures; archive & output.outputIndex; } - template void serialize(Archive& archive, cryptonote::TransactionOutputMultisignature &output, unsigned int version) { + template void serialize(Archive& archive, CryptoNote::TransactionOutputMultisignature &output, unsigned int version) { archive & output.keys; archive & output.requiredSignatures; } template - inline void serialize(Archive &a, cryptonote::TransactionOutputToKey &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::TransactionOutputToKey &x, const boost::serialization::version_type ver) { a & x.key; } template - inline void serialize(Archive &a, cryptonote::TransactionInputGenerate &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::TransactionInputGenerate &x, const boost::serialization::version_type ver) { a & x.height; } template - inline void serialize(Archive &a, cryptonote::TransactionInputToKey &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::TransactionInputToKey &x, const boost::serialization::version_type ver) { a & x.amount; a & x.keyOffsets; @@ -115,7 +115,7 @@ namespace boost } template - inline void serialize(Archive &a, cryptonote::TransactionOutput &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::TransactionOutput &x, const boost::serialization::version_type ver) { a & x.amount; a & x.target; @@ -123,7 +123,7 @@ namespace boost template - inline void serialize(Archive &a, cryptonote::Transaction &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::Transaction &x, const boost::serialization::version_type ver) { a & x.version; a & x.unlockTime; @@ -135,7 +135,7 @@ namespace boost template - inline void serialize(Archive &a, cryptonote::Block &b, const boost::serialization::version_type ver) + inline void serialize(Archive &a, CryptoNote::Block &b, const boost::serialization::version_type ver) { a & b.majorVersion; a & b.minorVersion; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp old mode 100755 new mode 100644 index 58917762be..ceea5b819f --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,544 +16,488 @@ // along with Bytecoin. If not, see . #include "cryptonote_core.h" - #include #include - -#include "storages/portable_storage_template_helper.h" -#include "include_base_utils.h" -#include "misc_log_ex.h" -#include "misc_language.h" -#include "warnings.h" - -#include "common/command_line.h" -#include "common/util.h" -#include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_stat_info.h" -#include "cryptonote_core/miner.h" +#include "../cryptonote_config.h" +#include "../Common/command_line.h" +#include "../Common/util.h" +#include "../crypto/crypto.h" +#include "../cryptonote_protocol/cryptonote_protocol_defs.h" +#include "../Logging/LoggerRef.h" +#include "../rpc/core_rpc_server_commands_defs.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_stat_info.h" +#include "miner.h" +#undef ERROR + +using namespace Logging; #include "cryptonote_core/CoreConfig.h" -#include "cryptonote_config.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "rpc/core_rpc_server_commands_defs.h" - - -using namespace epee; - -DISABLE_VS_WARNINGS(4355) -namespace cryptonote -{ - - //----------------------------------------------------------------------------------------------- - core::core(const Currency& currency, i_cryptonote_protocol* pprotocol): - m_currency(currency), - m_mempool(currency, m_blockchain_storage, m_timeProvider), - m_blockchain_storage(currency, m_mempool), - m_miner(new miner(currency, this)), - m_starter_message_showed(false) - { - set_cryptonote_protocol(pprotocol); - m_blockchain_storage.addObserver(this); +namespace CryptoNote { + +core::core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger) : +m_currency(currency), +logger(logger, "core"), +m_mempool(currency, m_blockchain_storage, m_timeProvider, logger), +m_blockchain_storage(currency, m_mempool, logger), +m_miner(new miner(currency, *this, logger)), +m_starter_message_showed(false) { + set_cryptonote_protocol(pprotocol); + m_blockchain_storage.addObserver(this); m_mempool.addObserver(this); } //----------------------------------------------------------------------------------------------- core::~core() { - m_blockchain_storage.removeObserver(this); - } - //----------------------------------------------------------------------------------------------- - void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) - { - if(pprotocol) - m_pprotocol = pprotocol; - else - m_pprotocol = &m_protocol_stub; - } - //----------------------------------------------------------------------------------- - void core::set_checkpoints(checkpoints&& chk_pts) - { - m_blockchain_storage.set_checkpoints(std::move(chk_pts)); - } - //----------------------------------------------------------------------------------- - void core::init_options(boost::program_options::options_description& /*desc*/) - { - } - //----------------------------------------------------------------------------------------------- - bool core::handle_command_line(const boost::program_options::variables_map& vm) - { - m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); - return true; - } - //----------------------------------------------------------------------------------------------- - uint64_t core::get_current_blockchain_height() - { - return m_blockchain_storage.get_current_blockchain_height(); - } - //----------------------------------------------------------------------------------------------- - bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) - { - top_id = m_blockchain_storage.get_tail_id(height); - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) - { - 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) - { - return m_blockchain_storage.get_blocks(start_offset, count, blocks); - } //----------------------------------------------------------------------------------------------- - void core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) - { - m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); - } - //----------------------------------------------------------------------------------------------- - bool core::get_alternative_blocks(std::list& blocks) - { - return m_blockchain_storage.get_alternative_blocks(blocks); - } - //----------------------------------------------------------------------------------------------- - size_t core::get_alternative_blocks_count() - { - return m_blockchain_storage.get_alternative_blocks_count(); - } - //----------------------------------------------------------------------------------------------- - bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { - m_config_folder = config.configFolder; - bool r = m_mempool.init(m_config_folder); - CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); + m_blockchain_storage.removeObserver(this); +} - r = m_blockchain_storage.init(m_config_folder, load_existing); - CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); +void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { + if (pprotocol) + m_pprotocol = pprotocol; + else + m_pprotocol = &m_protocol_stub; +} +//----------------------------------------------------------------------------------- +void core::set_checkpoints(checkpoints&& chk_pts) { + m_blockchain_storage.set_checkpoints(std::move(chk_pts)); +} +//----------------------------------------------------------------------------------- +void core::init_options(boost::program_options::options_description& /*desc*/) { +} - r = m_miner->init(minerConfig); - CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); +bool core::handle_command_line(const boost::program_options::variables_map& vm) { + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + return true; +} - return load_state_data(); - } - //----------------------------------------------------------------------------------------------- - bool core::set_genesis_block(const Block& b) - { - return m_blockchain_storage.reset_and_set_genesis_block(b); - } - //----------------------------------------------------------------------------------------------- - bool core::load_state_data() - { - // may be some code later - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::deinit() - { - m_miner->stop(); - m_mempool.deinit(); - m_blockchain_storage.deinit(); - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) - { - tvc = boost::value_initialized(); - //want to process all transactions sequentially - CRITICAL_REGION_LOCAL(m_incoming_tx_lock); - - if(tx_blob.size() > m_currency.maxTxSize()) - { - LOG_PRINT_L0("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"); - tvc.m_verifivation_failed = true; - return false; - } +bool core::is_ready() { + return !m_blockchain_storage.is_storing_blockchain(); +} - crypto::hash tx_hash = null_hash; - crypto::hash tx_prefixt_hash = null_hash; - Transaction tx; - if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) - { - LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to parse, rejected"); - tvc.m_verifivation_failed = true; - return false; - } - //std::cout << "!"<< tx.vin.size() << std::endl; +uint64_t core::get_current_blockchain_height() { + return m_blockchain_storage.get_current_blockchain_height(); +} - if(!check_tx_syntax(tx)) - { - LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"); - tvc.m_verifivation_failed = true; - return false; - } +bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { + top_id = m_blockchain_storage.get_tail_id(height); + return true; +} - if(!check_tx_semantic(tx, keeped_by_block)) - { - LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); - tvc.m_verifivation_failed = true; - return false; - } +bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { + return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); +} - bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); - if(tvc.m_verifivation_failed) { - if (!tvc.m_tx_fee_too_small) { - LOG_PRINT_RED_L0("Transaction verification failed: " << tx_hash); - } else { - LOG_PRINT_L0("Transaction verification failed: " << tx_hash); - } - } else if(tvc.m_verifivation_impossible) { - LOG_PRINT_RED_L0("Transaction verification impossible: " << tx_hash); - } +bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { + return m_blockchain_storage.get_blocks(start_offset, count, blocks); +} +void core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) { + m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); +} - if (tvc.m_added_to_pool) { - LOG_PRINT_L1("tx added: " << tx_hash); - poolUpdated(); - } +bool core::get_alternative_blocks(std::list& blocks) { + return m_blockchain_storage.get_alternative_blocks(blocks); +} - return r; +size_t core::get_alternative_blocks_count() { + return m_blockchain_storage.get_alternative_blocks_count(); } //----------------------------------------------------------------------------------------------- - bool core::get_stat_info(core_stat_info& st_inf) - { - st_inf.mining_speed = m_miner->get_speed(); - st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); - st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - st_inf.tx_pool_size = m_mempool.get_transactions_count(); - st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id()); - return true; - } + bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { + m_config_folder = config.configFolder; + bool r = m_mempool.init(m_config_folder); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize memory pool"; return false; } - //----------------------------------------------------------------------------------------------- - bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) - { - if(!tx.vin.size()) - { - LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx)); - return false; - } + r = m_blockchain_storage.init(m_config_folder, load_existing); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } - if(!check_inputs_types_supported(tx)) - { - LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx)); - return false; - } + r = m_miner->init(minerConfig); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } - if(!check_outs_valid(tx)) - { - LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx)); - return false; - } + return load_state_data(); +} - if(!check_money_overflow(tx)) - { - LOG_PRINT_RED_L0("tx have money overflow, rejected for tx id= " << get_transaction_hash(tx)); - return false; - } +bool core::set_genesis_block(const Block& b) { + return m_blockchain_storage.reset_and_set_genesis_block(b); +} - uint64_t amount_in = 0; - get_inputs_money_amount(tx, amount_in); - uint64_t amount_out = get_outs_money_amount(tx); +bool core::load_state_data() { + // may be some code later + return true; +} - if(amount_in <= amount_out) - { - LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx)); - return false; - } +bool core::deinit() { + m_miner->stop(); + m_mempool.deinit(); + m_blockchain_storage.deinit(); + return true; +} - if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) - { - LOG_PRINT_RED_L0("transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << - (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize())); - return false; - } +bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) { + tvc = boost::value_initialized(); + //want to process all transactions sequentially + std::lock_guard lk(m_incoming_tx_lock); - //check if tx use different key images - if(!check_tx_inputs_keyimages_diff(tx)) - { - LOG_PRINT_RED_L0("tx has a few inputs with identical keyimages"); - return false; - } + if (tx_blob.size() > m_currency.maxTxSize()) { + logger(INFO) << "WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"; + tvc.m_verifivation_failed = true; + return false; + } - if (!checkMultisignatureInputsDiff(tx)) { - LOG_PRINT_RED_L0("tx has a few multisignature inputs with identical output indexes"); - return false; - } + crypto::hash tx_hash = null_hash; + crypto::hash tx_prefixt_hash = null_hash; + Transaction tx; - return true; + if (!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to parse, rejected"; + tvc.m_verifivation_failed = true; + return false; } - //----------------------------------------------------------------------------------------------- - bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) - { - std::unordered_set ki; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (!ki.insert(boost::get(in).keyImage).second) - return false; - } - } - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(const Transaction& tx, tx_verification_context& tvc, bool keeped_by_block) - { - crypto::hash tx_hash = get_transaction_hash(tx); - 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); + //std::cout << "!"<< tx.vin.size() << std::endl; + + if (!check_tx_syntax(tx)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"; + tvc.m_verifivation_failed = true; + return false; } - //----------------------------------------------------------------------------------------------- - size_t core::get_blockchain_total_transactions() - { - return m_blockchain_storage.get_total_transactions(); + + if (!check_tx_semantic(tx, keeped_by_block)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"; + tvc.m_verifivation_failed = true; + return false; } - //----------------------------------------------------------------------------------------------- - //bool core::get_outs(uint64_t amount, std::list& pkeys) - //{ - // return m_blockchain_storage.get_outs(amount, pkeys); - //} - //----------------------------------------------------------------------------------------------- - bool core::add_new_tx(const 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) { - if (m_blockchain_storage.have_tx(tx_hash)) { - LOG_PRINT_L2("tx " << tx_hash << " is already in blockchain"); - return true; - } - // It's not very good to lock on m_mempool here, because it's very hard to understand the order of locking - // tx_memory_pool::m_transactions_lock, blockchain_storage::m_blockchain_lock, and core::m_incoming_tx_lock - CRITICAL_REGION_LOCAL(m_mempool); - if (m_mempool.have_tx(tx_hash)) { - LOG_PRINT_L2("tx " << tx_hash << " is already in transaction pool"); - return true; + bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); + if (tvc.m_verifivation_failed) { + if (!tvc.m_tx_fee_too_small) { + logger(ERROR) << "Transaction verification failed: " << tx_hash; + } else { + logger(INFO) << "Transaction verification failed: " << tx_hash; } - - return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); - } - //----------------------------------------------------------------------------------------------- - bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) - { - return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); + } else if (tvc.m_verifivation_impossible) { + logger(ERROR) << "Transaction verification impossible: " << tx_hash; } - //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) - { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); + + if (tvc.m_added_to_pool) { + logger(DEBUGGING) << "tx added: " << tx_hash; + poolUpdated(); } - //----------------------------------------------------------------------------------------------- - bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) - { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); + + return r; +} + +bool core::get_stat_info(core_stat_info& st_inf) { + st_inf.mining_speed = m_miner->get_speed(); + st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); + st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + st_inf.tx_pool_size = m_mempool.get_transactions_count(); + st_inf.top_block_id_str = Common::podToHex(m_blockchain_storage.get_tail_id()); + return true; +} + + +bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { + if (!tx.vin.size()) { + logger(ERROR) << "tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx); + return false; } - //----------------------------------------------------------------------------------------------- - void core::print_blockchain(uint64_t start_index, uint64_t end_index) - { - m_blockchain_storage.print_blockchain(start_index, end_index); + + if (!check_inputs_types_supported(tx)) { + logger(ERROR) << "unsupported input types for tx id= " << get_transaction_hash(tx); + return false; } - //----------------------------------------------------------------------------------------------- - void core::print_blockchain_index() - { - m_blockchain_storage.print_blockchain_index(); + + std::string errmsg; + if (!check_outs_valid(tx, &errmsg)) { + logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx) << ": " << errmsg; + return false; } - //----------------------------------------------------------------------------------------------- - void core::print_blockchain_outs(const std::string& file) - { - m_blockchain_storage.print_blockchain_outs(file); + + if (!check_money_overflow(tx)) { + logger(ERROR) << "tx have money overflow, rejected for tx id= " << get_transaction_hash(tx); + return false; } - //----------------------------------------------------------------------------------------------- - 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) - { - return m_blockchain_storage.get_random_outs_for_amounts(req, res); + + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + if (amount_in <= amount_out) { + logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx); + return false; } - //----------------------------------------------------------------------------------------------- - bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) - { - return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); + + if (!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { + logger(ERROR) << "transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << + (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()); + return false; } - //----------------------------------------------------------------------------------------------- - void core::pause_mining() { - m_miner->pause(); + + //check if tx use different key images + if (!check_tx_inputs_keyimages_diff(tx)) { + logger(ERROR) << "tx has a few inputs with identical keyimages"; + return false; } - //----------------------------------------------------------------------------------------------- - void core::update_block_template_and_resume_mining() { - update_miner_block_template(); - m_miner->resume(); + + if (!checkMultisignatureInputsDiff(tx)) { + logger(ERROR) << "tx has a few multisignature inputs with identical output indexes"; + return false; } - //----------------------------------------------------------------------------------------------- - bool core::handle_block_found(Block& b) { - block_verification_context bvc = boost::value_initialized(); - handle_incoming_block(b, bvc, true, true); - if (bvc.m_verifivation_failed) { - LOG_ERROR("mined block failed verification"); - } + return true; +} - return bvc.m_added_to_main_chain; - } - //----------------------------------------------------------------------------------------------- - void core::on_synchronized() - { - m_miner->on_synchronized(); +bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { + std::unordered_set ki; + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } } - //----------------------------------------------------------------------------------------------- - bool core::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { - isBcActual = m_blockchain_storage.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, new_txs, deleted_tx_ids); + return true; +} + +bool core::add_new_tx(const Transaction& tx, tx_verification_context& tvc, bool keeped_by_block) { + crypto::hash tx_hash = get_transaction_hash(tx); + 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); +} + +size_t core::get_blockchain_total_transactions() { + return m_blockchain_storage.get_total_transactions(); +} + +//bool core::get_outs(uint64_t amount, std::list& pkeys) +//{ +// return m_blockchain_storage.get_outs(amount, pkeys); +//} + +bool core::add_new_tx(const 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) { + if (m_blockchain_storage.have_tx(tx_hash)) { + logger(TRACE) << "tx " << tx_hash << " is already in blockchain"; return true; } - //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { - if (block_blob.size() > m_currency.maxBlockBlobSize()) { - LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); - bvc.m_verifivation_failed = true; - return false; - } - Block b = AUTO_VAL_INIT(b); - if (!parse_and_validate_block_from_blob(block_blob, b)) { - LOG_PRINT_L0("Failed to parse and validate new block"); - bvc.m_verifivation_failed = true; - return false; - } + // It's not very good to lock on m_mempool here, because it's very hard to understand the order of locking + // tx_memory_pool::m_transactions_lock, blockchain_storage::m_blockchain_lock, and core::m_incoming_tx_lock + std::lock_guard lk(m_mempool); - return handle_incoming_block(b, bvc, control_miner, relay_block); + if (m_mempool.have_tx(tx_hash)) { + logger(TRACE) << "tx " << tx_hash << " is already in transaction pool"; + return true; } - //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { - if (control_miner) { - pause_mining(); - } - m_blockchain_storage.add_new_block(b, bvc); + return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); +} - if (control_miner) { - update_block_template_and_resume_mining(); - } +bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) { + return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); +} - if (relay_block && bvc.m_added_to_main_chain) { - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(b.txHashes, txs, missed_txs); - if (!missed_txs.empty() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { - LOG_PRINT_L0("Block added, but it seems that reorganize just happened after that, do not relay this block"); - } else { - CHECK_AND_ASSERT_MES(txs.size() == b.txHashes.size() && missed_txs.empty(), false, "can't find some transactions in found block:" << - get_block_hash(b) << " txs.size()=" << txs.size() << ", b.txHashes.size()=" << b.txHashes.size() << ", missed_txs.size()" << missed_txs.size()); - - NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - bool r = block_to_blob(b, arg.b.block); - CHECK_AND_ASSERT_MES(r, false, "failed to serialize block"); - for (auto& tx : txs) { - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); - } +bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); +} - cryptonote_connection_context exclude_context = boost::value_initialized(); - m_pprotocol->relay_block(arg, exclude_context); - } - } +bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); +} - return true; - } - //----------------------------------------------------------------------------------------------- - crypto::hash core::get_tail_id() - { - return m_blockchain_storage.get_tail_id(); - } - //----------------------------------------------------------------------------------------------- - size_t core::get_pool_transactions_count() - { - return m_mempool.get_transactions_count(); - } - //----------------------------------------------------------------------------------------------- - bool core::have_block(const crypto::hash& id) - { - return m_blockchain_storage.have_block(id); +void core::print_blockchain(uint64_t start_index, uint64_t end_index) { + m_blockchain_storage.print_blockchain(start_index, end_index); +} + +void core::print_blockchain_index() { + m_blockchain_storage.print_blockchain_index(); +} + +void core::print_blockchain_outs(const std::string& file) { + m_blockchain_storage.print_blockchain_outs(file); +} + +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) { + return m_blockchain_storage.get_random_outs_for_amounts(req, res); +} + +bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { + return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); +} + +void core::pause_mining() { + m_miner->pause(); +} + +void core::update_block_template_and_resume_mining() { + update_miner_block_template(); + m_miner->resume(); +} + +bool core::handle_block_found(Block& b) { + block_verification_context bvc = boost::value_initialized(); + handle_incoming_block(b, bvc, true, true); + + if (bvc.m_verifivation_failed) { + logger(ERROR) << "mined block failed verification"; } - //----------------------------------------------------------------------------------------------- - bool core::parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) - { - return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); + + return bvc.m_added_to_main_chain; +} + +void core::on_synchronized() { + m_miner->on_synchronized(); } //----------------------------------------------------------------------------------------------- - bool core::check_tx_syntax(const Transaction& tx) - { + bool core::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { + isBcActual = m_blockchain_storage.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, new_txs, deleted_tx_ids); return true; } //----------------------------------------------------------------------------------------------- - void core::get_pool_transactions(std::list& txs) - { - m_mempool.get_transactions(txs); - } - //----------------------------------------------------------------------------------------------- - bool core::get_short_chain_history(std::list& ids) - { - return m_blockchain_storage.get_short_chain_history(ids); - } - //----------------------------------------------------------------------------------------------- - bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context) - { - return m_blockchain_storage.handle_get_objects(arg, rsp); - } - //----------------------------------------------------------------------------------------------- - bool core::getBlockByHash(const crypto::hash &h, Block &blk) { - return core::get_block_by_hash(h, blk); + bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (block_blob.size() > m_currency.maxBlockBlobSize()) { + logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; + bvc.m_verifivation_failed = true; + return false; } - crypto::hash core::get_block_id_by_height(uint64_t height) - { - return m_blockchain_storage.get_block_id_by_height(height); - } - //----------------------------------------------------------------------------------------------- - bool core::get_block_by_hash(const crypto::hash &h, Block &blk) { - return m_blockchain_storage.get_block_by_hash(h, blk); + Block b; + if (!parse_and_validate_block_from_blob(block_blob, b)) { + logger(INFO) << "Failed to parse and validate new block"; + bvc.m_verifivation_failed = true; + return false; } - //----------------------------------------------------------------------------------------------- - //void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { - // m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); - //} - //----------------------------------------------------------------------------------------------- - std::string core::print_pool(bool short_format) - { - return m_mempool.print_pool(short_format); - } - //----------------------------------------------------------------------------------------------- - bool core::update_miner_block_template() - { - m_miner->on_block_chain_update(); - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::on_idle() - { - if(!m_starter_message_showed) - { - LOG_PRINT_L0(ENDL << "**********************************************************************" << ENDL - << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL - << ENDL - << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL - << ENDL - << "Use \"help\" command to see the list of available commands." << ENDL - << ENDL - << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL - << "**********************************************************************"); - m_starter_message_showed = true; - } - m_miner->on_idle(); - m_mempool.on_idle(); - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::addObserver(ICoreObserver* observer) { - return m_observerManager.add(observer); + return handle_incoming_block(b, bvc, control_miner, relay_block); +} + +bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (control_miner) { + pause_mining(); } - bool core::removeObserver(ICoreObserver* observer) { - return m_observerManager.remove(observer); + m_blockchain_storage.add_new_block(b, bvc); + + if (control_miner) { + update_block_template_and_resume_mining(); } - void core::blockchainUpdated() { - m_observerManager.notify(&ICoreObserver::blockchainUpdated); + if (relay_block && bvc.m_added_to_main_chain) { + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(b.txHashes, txs, missed_txs); + if (!missed_txs.empty() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { + logger(INFO) << "Block added, but it seems that reorganize just happened after that, do not relay this block"; + } else { + if (!(txs.size() == b.txHashes.size() && missed_txs.empty())) { + logger(ERROR, BRIGHT_RED) << "can't find some transactions in found block:" << + get_block_hash(b) << " txs.size()=" << txs.size() << ", b.txHashes.size()=" << b.txHashes.size() << ", missed_txs.size()" << missed_txs.size(); return false; + } + + NOTIFY_NEW_BLOCK::request arg; + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + bool r = block_to_blob(b, arg.b.block); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to serialize block"; return false; } + for (auto& tx : txs) { + arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + } + + m_pprotocol->relay_block(arg); + } } + return true; +} + +crypto::hash core::get_tail_id() { + return m_blockchain_storage.get_tail_id(); +} + +size_t core::get_pool_transactions_count() { + return m_mempool.get_transactions_count(); +} + +bool core::have_block(const crypto::hash& id) { + return m_blockchain_storage.have_block(id); +} + +bool core::parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) { + return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); +} + +bool core::check_tx_syntax(const Transaction& tx) { + return true; +} + +void core::get_pool_transactions(std::list& txs) { + m_mempool.get_transactions(txs); +} + +bool core::get_short_chain_history(std::list& ids) { + return m_blockchain_storage.get_short_chain_history(ids); +} + +bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { + return m_blockchain_storage.handle_get_objects(arg, rsp); +} + +bool core::getBlockByHash(const crypto::hash &h, Block &blk) { + return core::get_block_by_hash(h, blk); +} + +crypto::hash core::get_block_id_by_height(uint64_t height) { + return m_blockchain_storage.get_block_id_by_height(height); +} + +bool core::get_block_by_hash(const crypto::hash &h, Block &blk) { + return m_blockchain_storage.get_block_by_hash(h, blk); +} + +//void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { +// m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); +//} + +std::string core::print_pool(bool short_format) { + return m_mempool.print_pool(short_format); +} + +bool core::update_miner_block_template() { + m_miner->on_block_chain_update(); + return true; +} + +bool core::on_idle() { + if (!m_starter_message_showed) { + logger(INFO) << ENDL << "**********************************************************************" << ENDL + << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL + << ENDL + << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << ENDL + << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL + << "**********************************************************************"; + m_starter_message_showed = true; + } + + m_miner->on_idle(); + m_mempool.on_idle(); + return true; +} + +bool core::addObserver(ICoreObserver* observer) { + return m_observerManager.add(observer); +} + +bool core::removeObserver(ICoreObserver* observer) { + return m_observerManager.remove(observer); +} + +void core::blockchainUpdated() { + m_observerManager.notify(&ICoreObserver::blockchainUpdated); +} + void core::txDeletedFromPool() { poolUpdated(); } @@ -563,68 +507,69 @@ namespace cryptonote } bool core::queryBlocks(const std::list& knownBlockIds, uint64_t timestamp, - uint64_t& resStartHeight, uint64_t& resCurrentHeight, uint64_t& resFullOffset, std::list& entries) { + uint64_t& resStartHeight, uint64_t& resCurrentHeight, uint64_t& resFullOffset, std::list& entries) { - LockedBlockchainStorage lbs(m_blockchain_storage); + LockedBlockchainStorage lbs(m_blockchain_storage); - uint64_t currentHeight = lbs->get_current_blockchain_height(); - uint64_t startOffset = 0; + uint64_t currentHeight = lbs->get_current_blockchain_height(); + uint64_t startOffset = 0; - if (!lbs->find_blockchain_supplement(knownBlockIds, startOffset)) { - return false; - } + if (!lbs->find_blockchain_supplement(knownBlockIds, startOffset)) { + return false; + } - uint64_t startFullOffset = 0; + uint64_t startFullOffset = 0; - if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) - startFullOffset = startOffset; + if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) + startFullOffset = startOffset; - resFullOffset = startFullOffset; + resFullOffset = startFullOffset; - if (startOffset != startFullOffset) { - std::list blockIds; - if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { - return false; - } + if (startOffset != startFullOffset) { + std::list blockIds; + if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { + return false; + } - for (const auto& id : blockIds) { - entries.push_back(BlockFullInfo()); - entries.back().block_id = id; - } + for (const auto& id : blockIds) { + entries.push_back(BlockFullInfo()); + entries.back().block_id = id; } + } - auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); + auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); - if (blocksLeft) { - std::list blocks; - lbs->get_blocks(startFullOffset, blocksLeft, blocks); + if (blocksLeft) { + std::list blocks; + lbs->get_blocks(startFullOffset, blocksLeft, blocks); - for (auto& b : blocks) { - BlockFullInfo item; + for (auto& b : blocks) { + BlockFullInfo item; - item.block_id = get_block_hash(b); + item.block_id = get_block_hash(b); - if (b.timestamp >= timestamp) { - // query transactions - std::list txs; - std::list missedTxs; - lbs->get_transactions(b.txHashes, txs, missedTxs); + if (b.timestamp >= timestamp) { + // query transactions + std::list txs; + std::list missedTxs; + lbs->get_transactions(b.txHashes, txs, missedTxs); - // fill data - block_complete_entry& completeEntry = item; - completeEntry.block = block_to_blob(b); - for (auto& tx : txs) { - completeEntry.txs.push_back(tx_to_blob(tx)); - } + // fill data + block_complete_entry& completeEntry = item; + completeEntry.block = block_to_blob(b); + for (auto& tx : txs) { + completeEntry.txs.push_back(tx_to_blob(tx)); } - - entries.push_back(std::move(item)); } + + entries.push_back(std::move(item)); } + } - resCurrentHeight = currentHeight; - resStartHeight = startOffset; + resCurrentHeight = currentHeight; + resStartHeight = startOffset; + + return true; +} - return true; - } } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 8704350117..dd558bf2f6 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -27,35 +27,32 @@ #include "blockchain_storage.h" #include "cryptonote_core/i_miner_handler.h" #include "cryptonote_core/MinerConfig.h" -#include "connection_context.h" -#include "warnings.h" #include "crypto/hash.h" #include "ICore.h" #include "ICoreObserver.h" -#include "common/ObserverManager.h" +#include "Common/ObserverManager.h" +#include -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4355) +namespace CryptoNote { -namespace cryptonote { struct core_stat_info; class miner; class CoreConfig; class core : public ICore, public i_miner_handler, public IBlockchainStorageObserver, public ITxPoolObserver { public: - core(const Currency& currency, i_cryptonote_protocol* pprotocol); + core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger); ~core(); - bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp, cryptonote_connection_context& context); + bool on_idle(); virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); bool handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); - const Currency& currency() const { return m_currency; } virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + const Currency& currency() const { return m_currency; } //-------------------- i_miner_handler ----------------------- virtual bool handle_block_found(Block& b); - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce); bool addObserver(ICoreObserver* observer); bool removeObserver(ICoreObserver* observer); @@ -65,7 +62,16 @@ namespace cryptonote { bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing); bool set_genesis_block(const Block& b); bool deinit(); + + // ICore + virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) override; + uint64_t get_current_blockchain_height(); + bool have_block(const crypto::hash& id); + bool get_short_chain_history(std::list& ids); + void on_synchronized(); + bool is_ready() override; + virtual bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); @@ -93,8 +99,6 @@ namespace cryptonote { size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); //bool get_outs(uint64_t amount, std::list& pkeys); - bool have_block(const crypto::hash& id); - bool get_short_chain_history(std::list& ids); virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool get_stat_info(core_stat_info& st_inf); @@ -110,7 +114,6 @@ namespace cryptonote { void print_blockchain_index(); std::string print_pool(bool short_format); void print_blockchain_outs(const std::string& file); - void on_synchronized(); virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; private: @@ -138,18 +141,17 @@ namespace cryptonote { void poolUpdated(); const Currency& m_currency; + Logging::LoggerRef logger; CryptoNote::RealTimeProvider m_timeProvider; tx_memory_pool m_mempool; blockchain_storage m_blockchain_storage; i_cryptonote_protocol* m_pprotocol; - epee::critical_section m_incoming_tx_lock; + std::mutex m_incoming_tx_lock; std::unique_ptr m_miner; std::string m_config_folder; cryptonote_protocol_stub m_protocol_stub; friend class tx_validate_inputs; std::atomic m_starter_message_showed; - tools::ObserverManager m_observerManager; + tools::ObserverManager m_observerManager; }; } - -POP_WARNINGS diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ac20cf8286..3fac4aa217 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,767 +15,741 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "cryptonote_format_utils.h" #include +#include "../Logging/LoggerRef.h" +#include "account.h" +#include "cryptonote_basic_impl.h" -// epee -#include "include_base_utils.h" -#include "misc_language.h" +using namespace Logging; +using namespace epee; -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "serialization/binary_utils.h" -#include "cryptonote_config.h" +namespace CryptoNote { -using namespace epee; +void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h) { + std::ostringstream s; + binary_archive a(s); + ::serialization::serialize(a, const_cast(tx)); + crypto::cn_fast_hash(s.str().data(), s.str().size(), h); +} -namespace cryptonote -{ - //--------------------------------------------------------------- - void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h) - { - std::ostringstream s; - binary_archive a(s); - ::serialization::serialize(a, const_cast(tx)); - crypto::cn_fast_hash(s.str().data(), s.str().size(), h); - } - //--------------------------------------------------------------- - crypto::hash get_transaction_prefix_hash(const TransactionPrefix& tx) - { - crypto::hash h = null_hash; - get_transaction_prefix_hash(tx, h); - return h; - } - //--------------------------------------------------------------- - bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx) - { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, tx); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction 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; - ss << tx_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, tx); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); - //TODO: validate tx - - crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); - get_transaction_prefix_hash(tx, tx_prefix_hash); - return true; - } - //--------------------------------------------------------------- - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki) - { - crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); - bool r = crypto::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 << ")"); +crypto::hash get_transaction_prefix_hash(const TransactionPrefix& tx) { + crypto::hash h = null_hash; + get_transaction_prefix_hash(tx, h); + return h; +} - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spendPublicKey, in_ephemeral.pub); - CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spendPublicKey << ")"); +bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx) { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + return ::serialization::serialize(ba, tx); +} - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); +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; + ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, tx); - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); - return true; + if (!r) { + return false; } - //--------------------------------------------------------------- - uint64_t power_integral(uint64_t a, uint64_t b) - { - if(b == 0) - return 1; - uint64_t total = a; - for(uint64_t i = 1; i != b; i++) - total *= a; - return total; - } - //--------------------------------------------------------------- - bool get_tx_fee(const Transaction& tx, uint64_t & fee) - { - uint64_t amount_in = 0; - uint64_t amount_out = 0; - - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - amount_in += boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount_in += boost::get(in).amount; - } - } - for (const auto& o : tx.vout) { - amount_out += o.amount; - } + //TODO: validate tx + crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + get_transaction_prefix_hash(tx, tx_prefix_hash); + return true; +} - CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <& tx_extra, std::vector& tx_extra_fields) - { - tx_extra_fields.clear(); - - if(tx_extra.empty()) - 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; - while (!eof) { - tx_extra_field field; - bool r = ::do_serialize(ar, field); - if (!r) { - LOG_PRINT_L4("failed to deserialize extra field. extra = " << - string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); - return false; - } - tx_extra_fields.push_back(field); - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } - - if (!::serialization::check_stream_state(ar)) { - LOG_PRINT_L4("failed to deserialize extra field. extra = " << - string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast(tx_extra.data()), tx_extra.size()))); - return false; - } + r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spendPublicKey, in_ephemeral.pub); - return true; + assert(r && "key image helper: failed to derive_public_key"); + + if (!r) { + return false; } - //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra) - { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - tx_extra_pub_key pub_key_field; - if(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) - return null_pkey; - - return pub_key_field.pub_key; - } - //--------------------------------------------------------------- - crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx) - { - return get_tx_pub_key_from_extra(tx.extra); - } - //--------------------------------------------------------------- - bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key) - { - tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); - tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; - *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; - return true; + + crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); + crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); + return true; +} + +uint64_t power_integral(uint64_t a, uint64_t b) { + if (b == 0) + return 1; + uint64_t total = a; + for (uint64_t i = 1; i != b; i++) + total *= a; + return total; +} + +bool get_tx_fee(const Transaction& tx, uint64_t & fee) { + uint64_t amount_in = 0; + uint64_t amount_out = 0; + + for (const auto& in : tx.vin) { + if (in.type() == typeid(TransactionInputToKey)) { + amount_in += boost::get(in).amount; + } else if (in.type() == typeid(TransactionInputMultisignature)) { + amount_in += boost::get(in).amount; + } } - //--------------------------------------------------------------- - bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce) - { - CHECK_AND_ASSERT_MES(extra_nonce.size() <= TX_EXTRA_NONCE_MAX_COUNT, false, "extra nonce could be 255 bytes max"); - size_t start_pos = tx_extra.size(); - tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); - //write tag - tx_extra[start_pos] = TX_EXTRA_NONCE; - //write len - ++start_pos; - tx_extra[start_pos] = static_cast(extra_nonce.size()); - //write data - ++start_pos; - memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); - return true; + + for (const auto& o : tx.vout) { + amount_out += o.amount; } - //--------------------------------------------------------------- - bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) { - blobdata blob; - if (!t_serializable_object_to_blob(mm_tag, blob)) { - return false; - } - tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); - std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); - return true; + if (!(amount_in >= amount_out)) { + return false; } - //--------------------------------------------------------------- - bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - return find_tx_extra_field_by_type(tx_extra_fields, mm_tag); - } - //--------------------------------------------------------------- - void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) - { - extra_nonce.clear(); - extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); - const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); - std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); - } - //--------------------------------------------------------------- - bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) - { - if(sizeof(crypto::hash) + 1 != extra_nonce.size()) - return false; - if(TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) - return false; - payment_id = *reinterpret_cast(extra_nonce.data() + 1); + + fee = amount_in - amount_out; + return true; +} + +uint64_t get_tx_fee(const Transaction& tx) { + uint64_t r = 0; + if (!get_tx_fee(tx, r)) + return 0; + return r; +} + +bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields) { + tx_extra_fields.clear(); + + if (tx_extra.empty()) return true; - } - bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId) { - cryptonote::blobdata binData; - if (!epee::string_tools::parse_hexstr_to_binbuff(paymentIdString, binData)) { - return false; - } + std::string extra_str(reinterpret_cast(tx_extra.data()), tx_extra.size()); + std::istringstream iss(extra_str); + binary_archive ar(iss); - if (sizeof(crypto::hash) != binData.size()) { + bool eof = false; + while (!eof) { + tx_extra_field field; + bool r = ::do_serialize(ar, field); + if (!r) { return false; } + tx_extra_fields.push_back(field); - paymentId = *reinterpret_cast(binData.data()); - return true; + std::ios_base::iostate state = iss.rdstate(); + eof = (EOF == iss.peek()); + iss.clear(state); } + if (!::serialization::check_stream_state(ar)) { + return false; + } - bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { - crypto::hash paymentIdBin; + return true; +} - if (!parsePaymentId(paymentIdString, paymentIdBin)) { - return false; - } +crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra) { + std::vector tx_extra_fields; + parse_tx_extra(tx_extra, tx_extra_fields); - std::string extraNonce; - cryptonote::set_payment_id_to_tx_extra_nonce(extraNonce, paymentIdBin); + tx_extra_pub_key pub_key_field; + if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) + return null_pkey; - if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extraNonce)) { - return false; - } + return pub_key_field.pub_key; +} - return true; - } +crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx) { + return get_tx_pub_key_from_extra(tx.extra); +} - bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId) { - std::vector tx_extra_fields; - if(!parse_tx_extra(extra, tx_extra_fields)) { - return false; - } +bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key) { + tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); + tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; + return true; +} - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentId)) { - return false; - } - } else { - return false; - } +bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce) { + if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { + return false; + } + + size_t start_pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); + //write tag + tx_extra[start_pos] = TX_EXTRA_NONCE; + //write len + ++start_pos; + tx_extra[start_pos] = static_cast(extra_nonce.size()); + //write data + ++start_pos; + memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; +} - return true; +bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) { + blobdata blob; + if (!t_serializable_object_to_blob(mm_tag, blob)) { + return false; } - bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, Transaction& tx, uint64_t unlock_time) - { - tx.vin.clear(); - tx.vout.clear(); - tx.signatures.clear(); + tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); + std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); + return true; +} - tx.version = CURRENT_TRANSACTION_VERSION; - tx.unlockTime = unlock_time; +bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) { + std::vector tx_extra_fields; + parse_tx_extra(tx_extra, tx_extra_fields); - tx.extra = extra; - KeyPair txkey = KeyPair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); + return find_tx_extra_field_by_type(tx_extra_fields, mm_tag); +} - struct input_generation_context_data - { - KeyPair in_ephemeral; - }; - std::vector in_contexts; +void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) { + extra_nonce.clear(); + extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); + const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); + std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); +} +bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) { + if (sizeof(crypto::hash) + 1 != extra_nonce.size()) + return false; + if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) + return false; + payment_id = *reinterpret_cast(extra_nonce.data() + 1); + return true; +} - uint64_t summary_inputs_money = 0; - //fill inputs - for (const tx_source_entry& src_entr : sources) - { - if(src_entr.real_output >= src_entr.outputs.size()) - { - LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); - return false; - } - summary_inputs_money += src_entr.amount; +bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId) { + return Common::podFromHex(paymentIdString, paymentId); +} - //key_derivation recv_derivation; - in_contexts.push_back(input_generation_context_data()); - KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; - crypto::key_image img; - if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) - return false; +bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { + crypto::hash paymentIdBin; - //check that derivated key is equal with real output key - if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) ) - { - LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:" - << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); - return false; - } + if (!parsePaymentId(paymentIdString, paymentIdBin)) { + return false; + } - //put key image into tx input - TransactionInputToKey input_to_key; - input_to_key.amount = src_entr.amount; - input_to_key.keyImage = img; + std::string extraNonce; + CryptoNote::set_payment_id_to_tx_extra_nonce(extraNonce, paymentIdBin); - //fill outputs array and use relative offsets - for (const tx_source_entry::output_entry& out_entry : src_entr.outputs) { - input_to_key.keyOffsets.push_back(out_entry.first); - } + if (!CryptoNote::add_extra_nonce_to_tx_extra(extra, extraNonce)) { + return false; + } + + return true; +} - input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); - tx.vin.push_back(input_to_key); +bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId) { + std::vector tx_extra_fields; + if (!parse_tx_extra(extra, tx_extra_fields)) { + return false; + } + + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { + if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentId)) { + return false; } + } else { + return false; + } - // "Shuffle" outs - std::vector shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); - - uint64_t summary_outs_money = 0; - //fill outputs - size_t output_index = 0; - for (const tx_destination_entry& dst_entr : shuffled_dsts) { - CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); - crypto::key_derivation derivation; - crypto::public_key out_eph_public_key; - bool r = crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, txkey.sec, derivation); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_viewPublicKey << ", " << txkey.sec << ")"); - - r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spendPublicKey, out_eph_public_key); - CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spendPublicKey << ")"); - - TransactionOutput out; - out.amount = dst_entr.amount; - TransactionOutputToKey tk; - tk.key = out_eph_public_key; - out.target = tk; - tx.vout.push_back(out); - output_index++; - summary_outs_money += dst_entr.amount; + return true; +} + +bool construct_tx( + const account_keys& sender_account_keys, + const std::vector& sources, + const std::vector& destinations, + std::vector extra, + Transaction& tx, + uint64_t unlock_time, + Logging::ILogger& log) { + LoggerRef logger(log, "construct_tx"); + + tx.vin.clear(); + tx.vout.clear(); + tx.signatures.clear(); + + tx.version = CURRENT_TRANSACTION_VERSION; + tx.unlockTime = unlock_time; + + tx.extra = extra; + KeyPair txkey = KeyPair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + + struct input_generation_context_data { + KeyPair in_ephemeral; + }; + + std::vector in_contexts; + uint64_t summary_inputs_money = 0; + //fill inputs + for (const tx_source_entry& src_entr : sources) { + if (src_entr.real_output >= src_entr.outputs.size()) { + logger(ERROR) << "real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size(); + return false; } + summary_inputs_money += src_entr.amount; + + //key_derivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; + crypto::key_image img; + if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + return false; - //check money - if(summary_outs_money > summary_inputs_money ) - { - LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); + //check that derivated key is equal with real output key + if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second)) { + logger(ERROR) << "derived public key missmatch with output public key! " << ENDL << "derived_key:" + << Common::podToHex(in_ephemeral.pub) << ENDL << "real output_public_key:" + << Common::podToHex(src_entr.outputs[src_entr.real_output].second); return false; } + //put key image into tx input + TransactionInputToKey input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.keyImage = img; - //generate ring signatures - crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); + //fill outputs array and use relative offsets + for (const tx_source_entry::output_entry& out_entry : src_entr.outputs) { + input_to_key.keyOffsets.push_back(out_entry.first); + } - std::stringstream ss_ring_s; - size_t i = 0; - for (const tx_source_entry& src_entr : sources) { - ss_ring_s << "pub_keys:" << ENDL; - std::vector keys_ptrs; - for (const tx_source_entry::output_entry& o : src_entr.outputs) { - keys_ptrs.push_back(&o.second); - ss_ring_s << o.second << ENDL; - } + input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); + tx.vin.push_back(input_to_key); + } - tx.signatures.push_back(std::vector()); - std::vector& sigs = tx.signatures.back(); - sigs.resize(src_entr.outputs.size()); - crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).keyImage, keys_ptrs, - in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); - ss_ring_s << "signatures:" << ENDL; - std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); - ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << - ENDL << "real_output: " << src_entr.real_output; - i++; + // "Shuffle" outs + std::vector shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; }); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + for (const tx_destination_entry& dst_entr : shuffled_dsts) { + if (!(dst_entr.amount > 0)) { + logger(ERROR, BRIGHT_RED) << "Destination with wrong amount: " << dst_entr.amount; + return false; } + crypto::key_derivation derivation; + crypto::public_key out_eph_public_key; + bool r = crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, txkey.sec, derivation); - LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3); + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "at creation outs: failed to generate_key_derivation(" + << dst_entr.addr.m_viewPublicKey << ", " << txkey.sec << ")"; + return false; + } - return true; + r = crypto::derive_public_key(derivation, output_index, + dst_entr.addr.m_spendPublicKey, + out_eph_public_key); + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "at creation outs: failed to derive_public_key(" << derivation + << ", " << output_index << ", " << dst_entr.addr.m_spendPublicKey + << ")"; + return false; + } + + TransactionOutput out; + out.amount = dst_entr.amount; + TransactionOutputToKey tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.vout.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; } - //--------------------------------------------------------------- - bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) - { - money = 0; - for (const auto& in : tx.vin) { - uint64_t amount = 0; + //check money + if (summary_outs_money > summary_inputs_money) { + logger(ERROR) << "Transaction inputs money (" << summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"; + return false; + } - if (in.type() == typeid(TransactionInputToKey)) { - amount = boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount = boost::get(in).amount; - } + //generate ring signatures + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); - money += amount; + size_t i = 0; + for (const tx_source_entry& src_entr : sources) { + std::vector keys_ptrs; + for (const tx_source_entry::output_entry& o : src_entr.outputs) { + keys_ptrs.push_back(&o.second); } - return true; + + tx.signatures.push_back(std::vector()); + std::vector& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).keyImage, keys_ptrs, + in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + i++; } - //--------------------------------------------------------------- - uint64_t get_block_height(const Block& b) - { - CHECK_AND_ASSERT_MES(b.minerTx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.minerTx.vin.size() != 1"); - CHECKED_GET_SPECIFIC_VARIANT(b.minerTx.vin[0], const TransactionInputGenerate, coinbase_in, 0); - return coinbase_in.height; - } - //--------------------------------------------------------------- - bool check_inputs_types_supported(const Transaction& tx) { - for (const auto& in : tx.vin) { - if (in.type() != typeid(TransactionInputToKey) && in.type() != typeid(TransactionInputMultisignature)) { - LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains inputs with invalid type."); - return false; - } + + return true; +} + +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { + money = 0; + + for (const auto& in : tx.vin) { + uint64_t amount = 0; + + if (in.type() == typeid(TransactionInputToKey)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(TransactionInputMultisignature)) { + amount = boost::get(in).amount; } - return true; + money += amount; } - //----------------------------------------------------------------------------------------------- - bool check_outs_valid(const Transaction& tx) { - for (const TransactionOutput& out : tx.vout) { - //assert(out.target.type() == typeid(TransactionOutputToKey) || out.target.type() == typeid(TransactionOutputMultisignature)); - if (out.target.type() == typeid(TransactionOutputToKey)) { - CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx)); + return true; +} - if (!check_key(boost::get(out.target).key)) { - return false; - } - } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { - const TransactionOutputMultisignature& multisignatureOutput = ::boost::get(out.target); - if (multisignatureOutput.requiredSignatures > multisignatureOutput.keys.size()) { - LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains multisignature output with invalid required signature count."); - return false; - } +uint32_t get_block_height(const Block& b) { + if (b.minerTx.vin.size() != 1) { + return 0; + } + const auto& in = b.minerTx.vin[0]; + if (in.type() != typeid(TransactionInputGenerate)) { + return 0; + } + return boost::get(in).height; +} - for (const crypto::public_key& key : multisignatureOutput.keys) { - if (!check_key(key)) { - LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains multisignature output with invalid public keys."); - return false; - } +bool check_inputs_types_supported(const Transaction& tx) { + for (const auto& in : tx.vin) { + if (in.type() != typeid(TransactionInputToKey) && in.type() != typeid(TransactionInputMultisignature)) { + return false; + } + } + + return true; +} + +bool check_outs_valid(const Transaction& tx, std::string* error) { + for (const TransactionOutput& out : tx.vout) { + if (out.target.type() == typeid(TransactionOutputToKey)) { + if (out.amount == 0) { + if (error) { + *error = "Zero amount ouput"; } - } else { - LOG_PRINT_L1("Transaction << " << get_transaction_hash(tx) << " contains outputs with invalid type."); return false; } - } - - return true; - } - //----------------------------------------------------------------------------------------------- - bool checkMultisignatureInputsDiff(const Transaction& tx) { - std::set> inputsUsage; - for (const auto& inv : tx.vin) { - if (inv.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(inv); - if (!inputsUsage.insert(std::make_pair(in.amount, static_cast(in.outputIndex))).second) { + if (!check_key(boost::get(out.target).key)) { + if (error) { + *error = "Output with invalid key"; + } + return false; + } + } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { + const TransactionOutputMultisignature& multisignatureOutput = ::boost::get(out.target); + if (multisignatureOutput.requiredSignatures > multisignatureOutput.keys.size()) { + if (error) { + *error = "Multisignature output with invalid required signature count"; + } + return false; + } + for (const crypto::public_key& key : multisignatureOutput.keys) { + if (!check_key(key)) { + if (error) { + *error = "Multisignature output with invalid public key"; + } return false; } } + } else { + if (error) { + *error = "Output with invalid type"; + } + return false; } - return true; } - //----------------------------------------------------------------------------------------------- - bool check_money_overflow(const Transaction& tx) - { - return check_inputs_overflow(tx) && check_outs_overflow(tx); + return true; +} + +bool checkMultisignatureInputsDiff(const Transaction& tx) { + std::set> inputsUsage; + for (const auto& inv : tx.vin) { + if (inv.type() == typeid(TransactionInputMultisignature)) { + const TransactionInputMultisignature& in = ::boost::get(inv); + if (!inputsUsage.insert(std::make_pair(in.amount, static_cast(in.outputIndex))).second) { + return false; + } + } } - //--------------------------------------------------------------- - bool check_inputs_overflow(const Transaction& tx) - { - uint64_t money = 0; + return true; +} - for (const auto& in : tx.vin) { - uint64_t amount = 0; +bool check_money_overflow(const Transaction &tx) { + return check_inputs_overflow(tx) && check_outs_overflow(tx); +} - if (in.type() == typeid(TransactionInputToKey)) { - amount = boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount = boost::get(in).amount; - } +bool check_inputs_overflow(const Transaction &tx) { + uint64_t money = 0; - if (money > amount + money) - return false; + for (const auto &in : tx.vin) { + uint64_t amount = 0; - money += amount; + if (in.type() == typeid(TransactionInputToKey)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(TransactionInputMultisignature)) { + amount = boost::get(in).amount; } - return true; + + if (money > amount + money) + return false; + + money += amount; } - //--------------------------------------------------------------- - bool check_outs_overflow(const Transaction& tx) - { - uint64_t money = 0; - for (const auto& o : tx.vout) { - if(money > o.amount + money) - return false; - money += o.amount; - } - return true; + return true; +} + +bool check_outs_overflow(const Transaction& tx) { + uint64_t money = 0; + for (const auto& o : tx.vout) { + if (money > o.amount + money) + return false; + money += o.amount; } - //--------------------------------------------------------------- - uint64_t get_outs_money_amount(const Transaction& tx) - { - uint64_t outputs_amount = 0; - for (const auto& o : tx.vout) { - outputs_amount += o.amount; - } - return outputs_amount; + return true; +} + +uint64_t get_outs_money_amount(const Transaction& tx) { + uint64_t outputs_amount = 0; + for (const auto& o : tx.vout) { + outputs_amount += o.amount; } - //--------------------------------------------------------------- - std::string short_hash_str(const crypto::hash& h) - { - std::string res = string_tools::pod_to_hex(h); - CHECK_AND_ASSERT_MES(res.size() == 64, res, "wrong hash256 with string_tools::pod_to_hex conversion"); + return outputs_amount; +} + +std::string short_hash_str(const crypto::hash& h) { + std::string res = Common::podToHex(h); + + if (res.size() == 64) { auto erased_pos = res.erase(8, 48); res.insert(8, "...."); - return res; } - //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex) - { - crypto::public_key pk; - derive_public_key(derivation, keyIndex, acc.m_account_address.m_spendPublicKey, pk); - return pk == out_key.key; - } + return res; +} - //--------------------------------------------------------------- - bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex) - { - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - return is_out_to_acc(acc, out_key, derivation, keyIndex); - } +bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex) { + crypto::public_key pk; + derive_public_key(derivation, keyIndex, acc.m_account_address.m_spendPublicKey, pk); + return pk == out_key.key; +} - //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) - { - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if(null_pkey == tx_pub_key) - return false; - return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); - } - //--------------------------------------------------------------- - bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) - { - money_transfered = 0; - size_t keyIndex = 0; - size_t outputIndex = 0; +bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex) { + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + return is_out_to_acc(acc, out_key, derivation, keyIndex); +} - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - - for (const TransactionOutput& o : tx.vout) { - assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); - if (o.target.type() == typeid(TransactionOutputToKey)) { - if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { - outs.push_back(outputIndex); - money_transfered += o.amount; - } +bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) { + crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); + if (null_pkey == tx_pub_key) + return false; + return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); +} - ++keyIndex; - } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { - keyIndex += boost::get(o.target).keys.size(); +bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) { + money_transfered = 0; + size_t keyIndex = 0; + size_t outputIndex = 0; + + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + + for (const TransactionOutput& o : tx.vout) { + assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); + if (o.target.type() == typeid(TransactionOutputToKey)) { + if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { + outs.push_back(outputIndex); + money_transfered += o.amount; } - ++outputIndex; + ++keyIndex; + } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { + keyIndex += boost::get(o.target).keys.size(); } - return true; + + ++outputIndex; } - //--------------------------------------------------------------- - void get_blob_hash(const blobdata& blob, crypto::hash& res) - { - cn_fast_hash(blob.data(), blob.size(), res); - } - //--------------------------------------------------------------- - crypto::hash get_blob_hash(const blobdata& blob) - { - crypto::hash h = null_hash; - get_blob_hash(blob, h); - return h; - } - //--------------------------------------------------------------- - crypto::hash get_transaction_hash(const Transaction& t) - { - crypto::hash h = null_hash; - size_t blob_size = 0; - get_object_hash(t, h, blob_size); - return h; - } - //--------------------------------------------------------------- - bool get_transaction_hash(const Transaction& t, crypto::hash& res) - { - size_t blob_size = 0; - return get_object_hash(t, res, blob_size); - } - //--------------------------------------------------------------- - bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size) - { - return get_object_hash(t, res, blob_size); - } - //--------------------------------------------------------------- - bool get_block_hashing_blob(const Block& b, blobdata& blob) { - if (!t_serializable_object_to_blob(static_cast(b), blob)) { - return false; - } - crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); - blob.append(tools::get_varint_data(b.txHashes.size() + 1)); + return true; +} - return true; +void get_blob_hash(const blobdata& blob, crypto::hash& res) { + cn_fast_hash(blob.data(), blob.size(), res); +} + +crypto::hash get_blob_hash(const blobdata& blob) { + crypto::hash h = null_hash; + get_blob_hash(blob, h); + return h; +} + +crypto::hash get_transaction_hash(const Transaction& t) { + crypto::hash h = null_hash; + size_t blob_size = 0; + get_object_hash(t, h, blob_size); + return h; +} + +bool get_transaction_hash(const Transaction& t, crypto::hash& res) { + size_t blob_size = 0; + return get_object_hash(t, res, blob_size); +} + +bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size) { + return get_object_hash(t, res, blob_size); +} + +bool get_block_hashing_blob(const Block& b, blobdata& blob) { + if (!t_serializable_object_to_blob(static_cast(b), blob)) { + return false; } - //--------------------------------------------------------------- - bool get_parent_block_hashing_blob(const Block& b, blobdata& blob) { - auto serializer = makeParentBlockSerializer(b, true, true); - return t_serializable_object_to_blob(serializer, blob); + crypto::hash tree_root_hash = get_tx_tree_hash(b); + blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); + blob.append(tools::get_varint_data(b.txHashes.size() + 1)); + + return true; +} + +bool get_parent_block_hashing_blob(const Block& b, blobdata& blob) { + auto serializer = makeParentBlockSerializer(b, true, true); + return t_serializable_object_to_blob(serializer, blob); +} + +bool get_block_hash(const Block& b, crypto::hash& res) { + blobdata blob; + if (!get_block_hashing_blob(b, blob)) { + return false; } - //--------------------------------------------------------------- - bool get_block_hash(const Block& b, crypto::hash& res) { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) { + + if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { + blobdata parent_blob; + auto serializer = makeParentBlockSerializer(b, true, false); + if (!t_serializable_object_to_blob(serializer, parent_blob)) return false; - } - if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { - blobdata parent_blob; - auto serializer = makeParentBlockSerializer(b, true, false); - if (!t_serializable_object_to_blob(serializer, parent_blob)) - return false; + blob.append(parent_blob); + } - blob.append(parent_blob); - } + return get_object_hash(blob, res); +} - return get_object_hash(blob, res); - } - //--------------------------------------------------------------- - crypto::hash get_block_hash(const Block& b) { - crypto::hash p = null_hash; - get_block_hash(b, p); - return p; +crypto::hash get_block_hash(const Block& b) { + crypto::hash p = null_hash; + get_block_hash(b, p); + return p; +} + +bool get_aux_block_header_hash(const Block& b, crypto::hash& res) { + blobdata blob; + if (!get_block_hashing_blob(b, blob)) { + return false; } - //--------------------------------------------------------------- - bool get_aux_block_header_hash(const Block& b, crypto::hash& res) { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) { + + return get_object_hash(blob, res); +} + +bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res) { + blobdata bd; + if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { + if (!get_block_hashing_blob(b, bd)) { return false; } - - return get_object_hash(blob, res); - } - //--------------------------------------------------------------- - bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res) { - blobdata bd; - if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { - if (!get_block_hashing_blob(b, bd)) { - return false; - } - } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { - if (!get_parent_block_hashing_blob(b, bd)) { - return false; - } - } else { + } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { + if (!get_parent_block_hashing_blob(b, bd)) { return false; } - crypto::cn_slow_hash(context, bd.data(), bd.size(), res); - return true; - } - //--------------------------------------------------------------- - std::vector relative_output_offsets_to_absolute(const std::vector& off) - { - std::vector res = off; - for(size_t i = 1; i < res.size(); i++) - res[i] += res[i-1]; - return res; + } else { + return false; } - //--------------------------------------------------------------- - std::vector absolute_output_offsets_to_relative(const std::vector& off) - { - std::vector res = off; - if(!off.size()) - return res; - std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted - for(size_t i = res.size()-1; i != 0; i--) - res[i] -= res[i-1]; + crypto::cn_slow_hash(context, bd.data(), bd.size(), res); + return true; +} + +std::vector relative_output_offsets_to_absolute(const std::vector& off) { + std::vector res = off; + for (size_t i = 1; i < res.size(); i++) + res[i] += res[i - 1]; + return res; +} +std::vector absolute_output_offsets_to_relative(const std::vector& off) { + std::vector res = off; + if (!off.size()) return res; + std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + for (size_t i = res.size() - 1; i != 0; i--) + res[i] -= res[i - 1]; + + return res; +} + +bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b) { + std::stringstream ss; + ss << b_blob; + binary_archive ba(ss); + return ::serialization::serialize(ba, b); +} + +blobdata block_to_blob(const Block& b) { + return t_serializable_object_to_blob(b); +} + +bool block_to_blob(const Block& b, blobdata& b_blob) { + return t_serializable_object_to_blob(b, b_blob); +} + +blobdata tx_to_blob(const Transaction& tx) { + return t_serializable_object_to_blob(tx); +} + +bool tx_to_blob(const Transaction& tx, blobdata& b_blob) { + return t_serializable_object_to_blob(tx, b_blob); +} + +void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) { + tree_hash(tx_hashes.data(), tx_hashes.size(), h); +} + +crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) { + crypto::hash h = null_hash; + get_tx_tree_hash(tx_hashes, h); + return h; +} + +crypto::hash get_tx_tree_hash(const Block& b) { + std::vector txs_ids; + crypto::hash h = null_hash; + size_t bl_sz = 0; + get_transaction_hash(b.minerTx, h, bl_sz); + txs_ids.push_back(h); + for (auto& th : b.txHashes) { + txs_ids.push_back(th); } - //--------------------------------------------------------------- - bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b) - { - std::stringstream ss; - ss << b_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, b); - CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); - return true; - } - //--------------------------------------------------------------- - blobdata block_to_blob(const Block& b) - { - return t_serializable_object_to_blob(b); - } - //--------------------------------------------------------------- - bool block_to_blob(const Block& b, blobdata& b_blob) - { - return t_serializable_object_to_blob(b, b_blob); - } - //--------------------------------------------------------------- - blobdata tx_to_blob(const Transaction& tx) - { - return t_serializable_object_to_blob(tx); - } - //--------------------------------------------------------------- - bool tx_to_blob(const Transaction& tx, blobdata& b_blob) - { - return t_serializable_object_to_blob(tx, b_blob); - } - //--------------------------------------------------------------- - void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) - { - tree_hash(tx_hashes.data(), tx_hashes.size(), h); - } - //--------------------------------------------------------------- - crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) - { - crypto::hash h = null_hash; - get_tx_tree_hash(tx_hashes, h); - return h; - } - //--------------------------------------------------------------- - crypto::hash get_tx_tree_hash(const Block& b) - { - std::vector txs_ids; - crypto::hash h = null_hash; - size_t bl_sz = 0; - get_transaction_hash(b.minerTx, h, bl_sz); - txs_ids.push_back(h); - for (auto& th : b.txHashes) { - txs_ids.push_back(th); - } - return get_tx_tree_hash(txs_ids); - } - //--------------------------------------------------------------- + return get_tx_tree_hash(txs_ids); +} + } diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index 0296fb4cad..47899aa7a6 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,222 +17,206 @@ #pragma once -#include +#include "../cryptonote_protocol/blobdatatype.h" +#include "cryptonote_basic.h" -#include +namespace Logging { +class ILogger; +} -#include "include_base_utils.h" +namespace CryptoNote { -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_protocol/blobdatatype.h" +void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h); +crypto::hash get_transaction_prefix_hash(const TransactionPrefix& 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); +struct tx_source_entry { + typedef std::pair output_entry; -namespace cryptonote -{ - //--------------------------------------------------------------- - void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h); - crypto::hash get_transaction_prefix_hash(const TransactionPrefix& 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); + std::vector outputs; //index + key + size_t real_output; //index in outputs vector of real output_entry + crypto::public_key real_out_tx_key; //incoming real tx public key + size_t real_output_in_tx_index; //index in transaction outputs vector + uint64_t amount; //money +}; - struct tx_source_entry - { - typedef std::pair output_entry; +struct tx_destination_entry { + uint64_t amount; //money + AccountPublicAddress addr; //destination address - std::vector outputs; //index + key - size_t real_output; //index in outputs vector of real output_entry - crypto::public_key real_out_tx_key; //incoming real tx public key - size_t real_output_in_tx_index; //index in transaction outputs vector - uint64_t amount; //money - }; + tx_destination_entry() : amount(0), addr(boost::value_initialized()) { } + tx_destination_entry(uint64_t a, const AccountPublicAddress &ad) : amount(a), addr(ad) { } +}; - struct tx_destination_entry - { - uint64_t amount; //money - AccountPublicAddress addr; //destination address - tx_destination_entry() : amount(0), addr(boost::value_initialized()) { } - tx_destination_entry(uint64_t a, const AccountPublicAddress &ad) : amount(a), addr(ad) { } - }; +bool construct_tx( + const account_keys& sender_account_keys, + const std::vector& sources, + const std::vector& destinations, + std::vector extra, Transaction& tx, uint64_t unlock_time, Logging::ILogger& log); - //--------------------------------------------------------------- - bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, std::vector extra, Transaction& tx, uint64_t unlock_time); +template +bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) { + auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), + [](const tx_extra_field& f) { return typeid(T) == f.type(); }); - template - bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) - { - auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), [](const tx_extra_field& f) { return typeid(T) == f.type(); }); - if(tx_extra_fields.end() == it) - return false; + if (tx_extra_fields.end() == it) + return false; - field = boost::get(*it); - return true; - } + field = boost::get(*it); + return true; +} - bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); - crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra); - crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx); - bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key); - bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); - void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); - bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); - bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag); - bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag); - bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex); - bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex); - bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); - bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); - bool get_tx_fee(const Transaction& tx, uint64_t & fee); - uint64_t get_tx_fee(const Transaction& tx); - bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki); - void get_blob_hash(const blobdata& blob, crypto::hash& res); - crypto::hash get_blob_hash(const blobdata& blob); - std::string short_hash_str(const crypto::hash& h); - bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); - //returns false if payment id is not found or parse error - bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId); - bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId); - - crypto::hash get_transaction_hash(const Transaction& t); - bool get_transaction_hash(const Transaction& t, crypto::hash& res); - bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size); - bool get_block_hashing_blob(const Block& b, blobdata& blob); - bool get_parent_block_hashing_blob(const Block& b, blobdata& blob); - bool get_aux_block_header_hash(const Block& b, crypto::hash& res); - bool get_block_hash(const Block& b, crypto::hash& res); - crypto::hash get_block_hash(const Block& b); - bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res); - bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b); - bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); - uint64_t get_outs_money_amount(const Transaction& tx); - bool check_inputs_types_supported(const Transaction& tx); - bool check_outs_valid(const Transaction& tx); - bool checkMultisignatureInputsDiff(const Transaction& tx); - - bool check_money_overflow(const Transaction& tx); - bool check_outs_overflow(const Transaction& tx); - bool check_inputs_overflow(const Transaction& tx); - uint64_t get_block_height(const Block& b); - std::vector relative_output_offsets_to_absolute(const std::vector& off); - std::vector absolute_output_offsets_to_relative(const std::vector& off); - //--------------------------------------------------------------- - template - bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) - { - std::stringstream ss; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, const_cast(to)); - b_blob = ss.str(); - return r; - } - //--------------------------------------------------------------- - template - blobdata t_serializable_object_to_blob(const t_object& to) - { - blobdata b; - t_serializable_object_to_blob(to, b); - return b; - } - //--------------------------------------------------------------- - template - bool get_object_hash(const t_object& o, crypto::hash& res) - { - get_blob_hash(t_serializable_object_to_blob(o), res); - return true; - } - //--------------------------------------------------------------- - template - bool get_object_blobsize(const t_object& o, size_t& size) { - blobdata blob; - if (!t_serializable_object_to_blob(o, blob)) { - size = (std::numeric_limits::max)(); - return false; - } - size = blob.size(); - return true; - } - //--------------------------------------------------------------- - template - size_t get_object_blobsize(const t_object& o) - { - size_t size; - get_object_blobsize(o, size); - return size; +bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); +crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra); +crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx); +bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key); +bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); +void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); +bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); +bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag); +bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag); +bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex); +bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex); +bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); +bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); +bool get_tx_fee(const Transaction& tx, uint64_t & fee); +uint64_t get_tx_fee(const Transaction& tx); +bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki); +void get_blob_hash(const blobdata& blob, crypto::hash& res); +crypto::hash get_blob_hash(const blobdata& blob); +std::string short_hash_str(const crypto::hash& h); +bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); +//returns false if payment id is not found or parse error +bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId); +bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId); + +crypto::hash get_transaction_hash(const Transaction& t); +bool get_transaction_hash(const Transaction& t, crypto::hash& res); +bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size); +bool get_block_hashing_blob(const Block& b, blobdata& blob); +bool get_parent_block_hashing_blob(const Block& b, blobdata& blob); +bool get_aux_block_header_hash(const Block& b, crypto::hash& res); +bool get_block_hash(const Block& b, crypto::hash& res); +crypto::hash get_block_hash(const Block& b); +bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res); +bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b); +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); +uint64_t get_outs_money_amount(const Transaction& tx); +bool check_inputs_types_supported(const Transaction& tx); +bool check_outs_valid(const Transaction& tx, std::string* error = 0); +bool checkMultisignatureInputsDiff(const Transaction& tx); + +bool check_money_overflow(const Transaction& tx); +bool check_outs_overflow(const Transaction& tx); +bool check_inputs_overflow(const Transaction& tx); +uint32_t get_block_height(const Block& b); +std::vector relative_output_offsets_to_absolute(const std::vector& off); +std::vector absolute_output_offsets_to_relative(const std::vector& off); + +template +bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) { + std::stringstream ss; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, const_cast(to)); + b_blob = ss.str(); + return r; +} + +template +blobdata t_serializable_object_to_blob(const t_object& to) { + blobdata b; + t_serializable_object_to_blob(to, b); + return b; +} + +template +bool get_object_hash(const t_object& o, crypto::hash& res) { + get_blob_hash(t_serializable_object_to_blob(o), res); + return true; +} + +template +bool get_object_blobsize(const t_object& o, size_t& size) { + blobdata blob; + if (!t_serializable_object_to_blob(o, blob)) { + size = (std::numeric_limits::max)(); + return false; } - //--------------------------------------------------------------- - template - bool get_object_hash(const t_object& o, crypto::hash& res, size_t& blob_size) - { - blobdata bl = t_serializable_object_to_blob(o); - blob_size = bl.size(); - get_blob_hash(bl, res); - return true; + size = blob.size(); + return true; +} + +template +size_t get_object_blobsize(const t_object& o) { + size_t size; + get_object_blobsize(o, size); + return size; +} + +template +bool get_object_hash(const t_object& o, crypto::hash& res, size_t& blob_size) { + blobdata bl = t_serializable_object_to_blob(o); + blob_size = bl.size(); + get_blob_hash(bl, res); + return true; +} + +template +std::string obj_to_json_str(const T& obj) { + std::stringstream ss; + json_archive ar(ss, true); + bool r = ::serialization::serialize(ar, *const_cast(&obj)); + if (!r) { + return ""; } - //--------------------------------------------------------------- - template - std::string obj_to_json_str(const T& obj) { - std::stringstream ss; - json_archive ar(ss, true); - bool r = ::serialization::serialize(ar, *const_cast(&obj)); - CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false"); - return ss.str(); + return ss.str(); +} + +// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold +template +void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) { + if (0 == amount) { + return; } - //--------------------------------------------------------------- - // 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold - template - void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) - { - if (0 == amount) - { - return; - } - bool is_dust_handled = false; - uint64_t dust = 0; - uint64_t order = 1; - while (0 != amount) - { - uint64_t chunk = (amount % 10) * order; - amount /= 10; - order *= 10; - - if (dust + chunk <= dust_threshold) - { - dust += chunk; + bool is_dust_handled = false; + uint64_t dust = 0; + uint64_t order = 1; + while (0 != amount) { + uint64_t chunk = (amount % 10) * order; + amount /= 10; + order *= 10; + + if (dust + chunk <= dust_threshold) { + dust += chunk; + } else { + if (!is_dust_handled && 0 != dust) { + dust_handler(dust); + is_dust_handled = true; } - else - { - if (!is_dust_handled && 0 != dust) - { - dust_handler(dust); - is_dust_handled = true; - } - if (0 != chunk) - { - chunk_handler(chunk); - } + if (0 != chunk) { + chunk_handler(chunk); } } + } - if (!is_dust_handled && 0 != dust) - { - dust_handler(dust); - } + if (!is_dust_handled && 0 != dust) { + dust_handler(dust); } - //--------------------------------------------------------------- - blobdata block_to_blob(const Block& b); - bool block_to_blob(const Block& b, blobdata& b_blob); - blobdata tx_to_blob(const Transaction& b); - bool tx_to_blob(const Transaction& b, blobdata& b_blob); - void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h); - crypto::hash get_tx_tree_hash(const std::vector& tx_hashes); - crypto::hash get_tx_tree_hash(const Block& b); +} + +blobdata block_to_blob(const Block& b); +bool block_to_blob(const Block& b, blobdata& b_blob); +blobdata tx_to_blob(const Transaction& b); +bool tx_to_blob(const Transaction& b, blobdata& b_blob); +void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h); +crypto::hash get_tx_tree_hash(const std::vector& tx_hashes); +crypto::hash get_tx_tree_hash(const Block& b); #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ - CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ + if(variant_var.type() != typeid(specific_type)) { return fail_return_val; } \ specific_type& variable_name = boost::get(variant_var); } diff --git a/src/cryptonote_core/cryptonote_serialization.cpp b/src/cryptonote_core/cryptonote_serialization.cpp index 8a4b530e38..93317d8f28 100644 --- a/src/cryptonote_core/cryptonote_serialization.cpp +++ b/src/cryptonote_core/cryptonote_serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -33,64 +33,64 @@ namespace { struct BinaryVariantTagGetter: boost::static_visitor { - uint8_t operator()(const cryptonote::TransactionInputGenerate) { return 0xff; } - uint8_t operator()(const cryptonote::TransactionInputToScript) { return 0x0; } - uint8_t operator()(const cryptonote::TransactionInputToScriptHash) { return 0x1; } - uint8_t operator()(const cryptonote::TransactionInputToKey) { return 0x2; } - uint8_t operator()(const cryptonote::TransactionInputMultisignature) { return 0x3; } - uint8_t operator()(const cryptonote::TransactionOutputToScript) { return 0x0; } - uint8_t operator()(const cryptonote::TransactionOutputToScriptHash) { return 0x1; } - uint8_t operator()(const cryptonote::TransactionOutputToKey) { return 0x2; } - uint8_t operator()(const cryptonote::TransactionOutputMultisignature) { return 0x3; } - uint8_t operator()(const cryptonote::Transaction) { return 0xcc; } - uint8_t operator()(const cryptonote::Block) { return 0xbb; } + uint8_t operator()(const CryptoNote::TransactionInputGenerate) { return 0xff; } + uint8_t operator()(const CryptoNote::TransactionInputToScript) { return 0x0; } + uint8_t operator()(const CryptoNote::TransactionInputToScriptHash) { return 0x1; } + uint8_t operator()(const CryptoNote::TransactionInputToKey) { return 0x2; } + uint8_t operator()(const CryptoNote::TransactionInputMultisignature) { return 0x3; } + uint8_t operator()(const CryptoNote::TransactionOutputToScript) { return 0x0; } + uint8_t operator()(const CryptoNote::TransactionOutputToScriptHash) { return 0x1; } + uint8_t operator()(const CryptoNote::TransactionOutputToKey) { return 0x2; } + uint8_t operator()(const CryptoNote::TransactionOutputMultisignature) { return 0x3; } + uint8_t operator()(const CryptoNote::Transaction) { return 0xcc; } + uint8_t operator()(const CryptoNote::Block) { return 0xbb; } }; struct VariantSerializer : boost::static_visitor<> { - VariantSerializer(cryptonote::ISerializer& serializer, const std::string& name) : s(serializer), name(name) {} - - void operator() (cryptonote::TransactionInputGenerate& param) { s(param, name); } - void operator() (cryptonote::TransactionInputToScript& param) { s(param, name); } - void operator() (cryptonote::TransactionInputToScriptHash& param) { s(param, name); } - void operator() (cryptonote::TransactionInputToKey& param) { s(param, name); } - void operator() (cryptonote::TransactionInputMultisignature& param) { s(param, name); } - void operator() (cryptonote::TransactionOutputToScript& param) { s(param, name); } - void operator() (cryptonote::TransactionOutputToScriptHash& param) { s(param, name); } - void operator() (cryptonote::TransactionOutputToKey& param) { s(param, name); } - void operator() (cryptonote::TransactionOutputMultisignature& param) { s(param, name); } - - cryptonote::ISerializer& s; + VariantSerializer(CryptoNote::ISerializer& serializer, const std::string& name) : s(serializer), name(name) {} + + void operator() (CryptoNote::TransactionInputGenerate& param) { s(param, name); } + void operator() (CryptoNote::TransactionInputToScript& param) { s(param, name); } + void operator() (CryptoNote::TransactionInputToScriptHash& param) { s(param, name); } + void operator() (CryptoNote::TransactionInputToKey& param) { s(param, name); } + void operator() (CryptoNote::TransactionInputMultisignature& param) { s(param, name); } + void operator() (CryptoNote::TransactionOutputToScript& param) { s(param, name); } + void operator() (CryptoNote::TransactionOutputToScriptHash& param) { s(param, name); } + void operator() (CryptoNote::TransactionOutputToKey& param) { s(param, name); } + void operator() (CryptoNote::TransactionOutputMultisignature& param) { s(param, name); } + + CryptoNote::ISerializer& s; const std::string& name; }; -void getVariantValue(cryptonote::ISerializer& serializer, uint8_t tag, cryptonote::TransactionInput& in) { +void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNote::TransactionInput& in) { switch(tag) { case 0xff: { - cryptonote::TransactionInputGenerate v; + CryptoNote::TransactionInputGenerate v; serializer(v, "data"); in = v; break; } case 0x0: { - cryptonote::TransactionInputToScript v; + CryptoNote::TransactionInputToScript v; serializer(v, "data"); in = v; break; } case 0x1: { - cryptonote::TransactionInputToScriptHash v; + CryptoNote::TransactionInputToScriptHash v; serializer(v, "data"); in = v; break; } case 0x2: { - cryptonote::TransactionInputToKey v; + CryptoNote::TransactionInputToKey v; serializer(v, "data"); in = v; break; } case 0x3: { - cryptonote::TransactionInputMultisignature v; + CryptoNote::TransactionInputMultisignature v; serializer(v, "data"); in = v; break; @@ -100,28 +100,28 @@ void getVariantValue(cryptonote::ISerializer& serializer, uint8_t tag, cryptonot } } -void getVariantValue(cryptonote::ISerializer& serializer, uint8_t tag, cryptonote::TransactionOutputTarget& out) { +void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNote::TransactionOutputTarget& out) { switch(tag) { case 0x0: { - cryptonote::TransactionOutputToScript v; + CryptoNote::TransactionOutputToScript v; serializer(v, "data"); out = v; break; } case 0x1: { - cryptonote::TransactionOutputToScriptHash v; + CryptoNote::TransactionOutputToScriptHash v; serializer(v, "data"); out = v; break; } case 0x2: { - cryptonote::TransactionOutputToKey v; + CryptoNote::TransactionOutputToKey v; serializer(v, "data"); out = v; break; } case 0x3: { - cryptonote::TransactionOutputMultisignature v; + CryptoNote::TransactionOutputMultisignature v; serializer(v, "data"); out = v; break; @@ -132,11 +132,11 @@ void getVariantValue(cryptonote::ISerializer& serializer, uint8_t tag, cryptonot } template -void serializePod(T& v, const std::string& name, cryptonote::ISerializer& serializer) { +void serializePod(T& v, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.binary(&v, sizeof(v), name); } -void serializeVarintVector(std::vector& vector, cryptonote::ISerializer& serializer, const std::string& name) { +void serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, const std::string& name) { std::size_t size = vector.size(); serializer.beginArray(size, name); vector.resize(size); @@ -152,35 +152,33 @@ void serializeVarintVector(std::vector& vector, cryptonote::ISerialize namespace crypto { -void serialize(public_key& pubKey, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(public_key& pubKey, const std::string& name, CryptoNote::ISerializer& serializer) { serializePod(pubKey, name, serializer); } -void serialize(secret_key& secKey, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(secret_key& secKey, const std::string& name, CryptoNote::ISerializer& serializer) { serializePod(secKey, name, serializer); } -void serialize(hash& h, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(hash& h, const std::string& name, CryptoNote::ISerializer& serializer) { serializePod(h, name, serializer); } -void serialize(key_image& keyImage, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(key_image& keyImage, const std::string& name, CryptoNote::ISerializer& serializer) { serializePod(keyImage, name, serializer); } -void serialize(chacha8_iv& chacha, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(chacha8_iv& chacha, const std::string& name, CryptoNote::ISerializer& serializer) { serializePod(chacha, name, serializer); } } -namespace cryptonote { +namespace CryptoNote { void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& serializer) { serializer.beginObject(name); - uint64_t version = static_cast(txP.version); - serializer(version, "version"); - txP.version = static_cast(version); + serializer(txP.version, "version"); serializer(txP.unlockTime, "unlock_time"); serializer(txP.vin, "vin"); serializer(txP.vout, "vout"); @@ -191,10 +189,7 @@ void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& ser void serialize(Transaction& tx, const std::string& name, ISerializer& serializer) { serializer.beginObject(name); - uint64_t version = static_cast(tx.version); - serializer(version, "version"); - tx.version = static_cast(version); - //TODO: make version. check version here + serializer(tx.version, "version"); serializer(tx.unlockTime, "unlock_time"); serializer(tx.vin, "vin"); serializer(tx.vout, "vout"); @@ -259,9 +254,7 @@ void serialize(TransactionInput& in, const std::string& name, ISerializer& seria void serialize(TransactionInputGenerate& gen, const std::string& name, ISerializer& serializer) { serializer.beginObject(name); - uint64_t height = static_cast(gen.height); - serializer(height, "height"); - gen.height = static_cast(height); + serializer(gen.height, "height"); serializer.endObject(); } @@ -369,7 +362,7 @@ void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& uint64_t txNum = static_cast(pbs.m_parentBlock.numberOfTransactions); serializer(txNum, "numberOfTransactions"); - pbs.m_parentBlock.numberOfTransactions = static_cast(txNum); + pbs.m_parentBlock.numberOfTransactions = static_cast(txNum); if (pbs.m_parentBlock.numberOfTransactions < 1) { throw std::runtime_error("Wrong transactions number"); } @@ -498,4 +491,4 @@ void serialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISeriali serializer.endObject(); } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/cryptonote_serialization.h b/src/cryptonote_core/cryptonote_serialization.h index 16e569273d..9b72638e56 100644 --- a/src/cryptonote_core/cryptonote_serialization.h +++ b/src/cryptonote_core/cryptonote_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,21 +19,21 @@ #include "cryptonote_basic.h" -namespace cryptonote { +namespace CryptoNote { class ISerializer; } namespace crypto { -void serialize(public_key& pubKey, const std::string& name, cryptonote::ISerializer& enumerator); -void serialize(secret_key& secKey, const std::string& name, cryptonote::ISerializer& enumerator); -void serialize(hash& h, const std::string& name, cryptonote::ISerializer& enumerator); -void serialize(chacha8_iv& chacha, const std::string& name, cryptonote::ISerializer& enumerator); -void serialize(key_image& keyImage, const std::string& name, cryptonote::ISerializer& enumerator); +void serialize(public_key& pubKey, const std::string& name, CryptoNote::ISerializer& enumerator); +void serialize(secret_key& secKey, const std::string& name, CryptoNote::ISerializer& enumerator); +void serialize(hash& h, const std::string& name, CryptoNote::ISerializer& enumerator); +void serialize(chacha8_iv& chacha, const std::string& name, CryptoNote::ISerializer& enumerator); +void serialize(key_image& keyImage, const std::string& name, CryptoNote::ISerializer& enumerator); } //namespace crypto -namespace cryptonote { +namespace CryptoNote { void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& serializer); void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& serializer); void serialize(Transaction& tx, const std::string& name, ISerializer& serializer); @@ -59,4 +59,4 @@ void serialize(Block& block, const std::string& name, ISerializer& serializer); void serialize(AccountPublicAddress& address, const std::string& name, ISerializer& serializer); void serialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer); -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_core/cryptonote_stat_info.h index 6129dd3a82..7405221875 100644 --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/cryptonote_core/cryptonote_stat_info.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,7 +19,7 @@ #include "serialization/keyvalue_serialization.h" -namespace cryptonote +namespace CryptoNote { struct core_stat_info { diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp index eed5e00489..bc7e8bc4c0 100644 --- a/src/cryptonote_core/difficulty.cpp +++ b/src/cryptonote_core/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,12 +21,12 @@ #include #include -#include "common/int-util.h" +#include "Common/int-util.h" #include "crypto/hash.h" #include "cryptonote_config.h" #include "difficulty.h" -namespace cryptonote { +namespace CryptoNote { using std::size_t; using std::uint64_t; diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h index f10937c060..103c86e7cd 100644 --- a/src/cryptonote_core/difficulty.h +++ b/src/cryptonote_core/difficulty.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include "crypto/hash.h" -namespace cryptonote +namespace CryptoNote { typedef std::uint64_t difficulty_type; diff --git a/src/cryptonote_core/i_miner_handler.h b/src/cryptonote_core/i_miner_handler.h index ff3d20fb84..8cf205986a 100644 --- a/src/cryptonote_core/i_miner_handler.h +++ b/src/cryptonote_core/i_miner_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,10 +20,10 @@ #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/difficulty.h" -namespace cryptonote { +namespace CryptoNote { struct i_miner_handler { virtual bool handle_block_found(Block& b) = 0; - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) = 0; protected: ~i_miner_handler(){}; diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 71a142a207..809200bd59 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,43 +15,38 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include +#include "miner.h" + +#include #include +#include +#include #include #include -#include +#include #include #include -#include "misc_language.h" -#include "include_base_utils.h" -#include "cryptonote_basic_impl.h" #include "cryptonote_format_utils.h" -#include "file_io_utils.h" -#include "common/command_line.h" -#include "crypto/hash.h" -#include "crypto/random.h" -#include "string_coding.h" -#include "storages/portable_storage_template_helper.h" +#include "Common/command_line.h" -using namespace epee; +// epee +#include "storages/portable_storage_template_helper.h" -#include "miner.h" -#include -#include +using namespace Logging; -namespace cryptonote +namespace CryptoNote { - miner::miner(const Currency& currency, i_miner_handler* phandler): + miner::miner(const Currency& currency, i_miner_handler& handler, Logging::ILogger& log) : m_currency(currency), - m_stop(1), + logger(log, "miner"), + m_stop(true), m_template(boost::value_initialized()), m_template_no(0), m_diffic(0), - m_thread_index(0), - m_phandler(phandler), + m_handler(handler), m_pausers_count(0), m_threads_total(0), m_starter_nonce(0), @@ -59,7 +54,9 @@ namespace cryptonote m_hashes(0), m_do_print_hashrate(false), m_do_mining(false), - m_current_hash_rate(0) + m_current_hash_rate(0), + m_update_block_template_interval(5), + m_update_merge_hr_interval(2) { } //----------------------------------------------------------------------------------------------------- @@ -68,18 +65,19 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------------- bool miner::set_block_template(const Block& bl, const difficulty_type& di) { - CRITICAL_REGION_LOCAL(m_template_lock); + std::lock_guard lk(m_template_lock); + m_template = bl; if (BLOCK_MAJOR_VERSION_2 == m_template.majorVersion) { - cryptonote::tx_extra_merge_mining_tag mm_tag; + CryptoNote::tx_extra_merge_mining_tag mm_tag; mm_tag.depth = 0; - if (!cryptonote::get_aux_block_header_hash(m_template, mm_tag.merkle_root)) { + if (!CryptoNote::get_aux_block_header_hash(m_template, mm_tag.merkle_root)) { return false; } m_template.parentBlock.minerTx.extra.clear(); - if (!cryptonote::append_mm_tag_to_extra(m_template.parentBlock.minerTx.extra, mm_tag)) { + if (!CryptoNote::append_mm_tag_to_extra(m_template.parentBlock.minerTx.extra, mm_tag)) { return false; } } @@ -99,32 +97,33 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------------- bool miner::request_block_template() { - Block bl = AUTO_VAL_INIT(bl); - difficulty_type di = AUTO_VAL_INIT(di); - uint64_t height; - cryptonote::blobdata extra_nonce; - if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) - { + Block bl = boost::value_initialized(); + difficulty_type di = 0; + uint32_t height; + CryptoNote::blobdata extra_nonce; + + if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) { extra_nonce = m_extra_messages[m_config.current_extra_message_index]; } - if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce)) - { - LOG_ERROR("Failed to get_block_template(), stopping mining"); + if(!m_handler.get_block_template(bl, m_mine_address, di, height, extra_nonce)) { + logger(ERROR) << "Failed to get_block_template(), stopping mining"; return false; } + set_block_template(bl, di); return true; } //----------------------------------------------------------------------------------------------------- bool miner::on_idle() { - m_update_block_template_interval.do_call([&](){ - if(is_mining())request_block_template(); + m_update_block_template_interval.call([&](){ + if(is_mining()) + request_block_template(); return true; }); - m_update_merge_hr_interval.do_call([&](){ + m_update_merge_hr_interval.call([&](){ merge_hr(); return true; }); @@ -136,47 +135,55 @@ namespace cryptonote { m_do_print_hashrate = do_hr; } + + uint64_t millisecondsSinceEpoch() { + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); + } + //----------------------------------------------------------------------------------------------------- void miner::merge_hr() { - if(m_last_hr_merge_time && is_mining()) - { - m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); - CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); + if(m_last_hr_merge_time && is_mining()) { + m_current_hash_rate = m_hashes * 1000 / (millisecondsSinceEpoch() - m_last_hr_merge_time + 1); + std::lock_guard lk(m_last_hash_rates_lock); m_last_hash_rates.push_back(m_current_hash_rate); if(m_last_hash_rates.size() > 19) m_last_hash_rates.pop_front(); - if(m_do_print_hashrate) - { - uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); + + if(m_do_print_hashrate) { + uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), static_cast(0)); float hr = static_cast(total_hr)/static_cast(m_last_hash_rates.size()); std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; } } - m_last_hr_merge_time = misc_utils::get_tick_count(); + + m_last_hr_merge_time = millisecondsSinceEpoch(); m_hashes = 0; } bool miner::init(const MinerConfig& config) { if (!config.extraMessages.empty()) { std::string buff; - bool r = file_io_utils::load_file_to_string(config.extraMessages, buff); - CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << config.extraMessages); + if (!Common::loadFileToString(config.extraMessages, buff)) { + logger(ERROR, BRIGHT_RED) << "Failed to load file with extra messages: " << config.extraMessages; + return false; + } std::vector extra_vec; boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); m_extra_messages.resize(extra_vec.size()); for(size_t i = 0; i != extra_vec.size(); i++) { - string_tools::trim(extra_vec[i]); + boost::algorithm::trim(extra_vec[i]); if(!extra_vec[i].size()) continue; - std::string buff = string_encoding::base64_decode(extra_vec[i]); + std::string buff = Common::base64Decode(extra_vec[i]); if(buff != "0") m_extra_messages[i] = buff; } m_config_folder_path = boost::filesystem::path(config.extraMessages).parent_path().string(); - m_config = AUTO_VAL_INIT(m_config); - epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + cryptonote::parameters::MINER_CONFIG_FILE_NAME); - LOG_PRINT_L0("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); + m_config = boost::value_initialized(); + epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + CryptoNote::parameters::MINER_CONFIG_FILE_NAME); + logger(INFO) << "Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index; } if(!config.startMining.empty()) { @@ -200,37 +207,37 @@ namespace cryptonote } //----------------------------------------------------------------------------------------------------- bool miner::start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs) - { - m_mine_address = adr; - m_threads_total = static_cast(threads_count); - m_starter_nonce = crypto::rand(); - CRITICAL_REGION_LOCAL(m_threads_lock); - if(is_mining()) - { - LOG_ERROR("Starting miner but it's already started"); + { + if (is_mining()) { + logger(ERROR) << "Starting miner but it's already started"; return false; } - if(!m_threads.empty()) - { - LOG_ERROR("Unable to start miner because there are active mining threads"); + std::lock_guard lk(m_threads_lock); + + if(!m_threads.empty()) { + logger(ERROR) << "Unable to start miner because there are active mining threads"; return false; } - if(!m_template_no) - request_block_template();//lets update block template + m_mine_address = adr; + m_threads_total = static_cast(threads_count); + m_starter_nonce = crypto::rand(); + + if (!m_template_no) { + request_block_template(); //lets update block template + } - boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); - boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); + m_stop = false; - for(size_t i = 0; i != threads_count; i++) - { - m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this))); + for (uint32_t i = 0; i != threads_count; i++) { + m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this, i))); } - LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ) + logger(INFO) << "Mining has started with " << threads_count << " threads, good luck!"; return true; } + //----------------------------------------------------------------------------------------------------- uint64_t miner::get_speed() { @@ -239,22 +246,25 @@ namespace cryptonote else return 0; } + //----------------------------------------------------------------------------------------------------- - void miner::send_stop_signal() + void miner::send_stop_signal() { - boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); + m_stop = true; } + //----------------------------------------------------------------------------------------------------- bool miner::stop() { send_stop_signal(); - CRITICAL_REGION_LOCAL(m_threads_lock); + std::lock_guard lk(m_threads_lock); - BOOST_FOREACH(boost::thread& th, m_threads) + for (auto& th : m_threads) { th.join(); + } m_threads.clear(); - LOG_PRINT_L0("Mining has been stopped, " << m_threads.size() << " finished" ); + logger(INFO) << "Mining has been stopped, " << m_threads.size() << " finished" ; return true; } //----------------------------------------------------------------------------------------------------- @@ -329,65 +339,63 @@ namespace cryptonote //----------------------------------------------------------------------------------------------------- void miner::pause() { - CRITICAL_REGION_LOCAL(m_miners_count_lock); + std::lock_guard lk(m_miners_count_lock); ++m_pausers_count; if(m_pausers_count == 1 && is_mining()) - LOG_PRINT_L2("MINING PAUSED"); + logger(TRACE) << "MINING PAUSED"; } //----------------------------------------------------------------------------------------------------- void miner::resume() { - CRITICAL_REGION_LOCAL(m_miners_count_lock); + std::lock_guard lk(m_miners_count_lock); --m_pausers_count; if(m_pausers_count < 0) { m_pausers_count = 0; - LOG_PRINT_RED_L0("Unexpected miner::resume() called"); + logger(ERROR) << "Unexpected miner::resume() called"; } if(!m_pausers_count && is_mining()) - LOG_PRINT_L2("MINING RESUMED"); + logger(TRACE) << "MINING RESUMED"; } //----------------------------------------------------------------------------------------------------- - bool miner::worker_thread() + bool miner::worker_thread(uint32_t th_local_index) { - uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); - LOG_PRINT_L0("Miner thread was started ["<< th_local_index << "]"); - log_space::log_singletone::set_thread_log_prefix(std::string("[miner ") + std::to_string(th_local_index) + "]"); + logger(INFO) << "Miner thread was started ["<< th_local_index << "]"; uint32_t nonce = m_starter_nonce + th_local_index; difficulty_type local_diff = 0; uint32_t local_template_ver = 0; crypto::cn_context context; Block b; + while(!m_stop) { - if(m_pausers_count)//anti split workaround + if(m_pausers_count) //anti split workaround { - misc_utils::sleep_no_w(100); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } - if(local_template_ver != m_template_no) - { - - CRITICAL_REGION_BEGIN(m_template_lock); + if(local_template_ver != m_template_no) { + std::unique_lock lk(m_template_lock); b = m_template; local_diff = m_diffic; - CRITICAL_REGION_END(); + lk.unlock(); + local_template_ver = m_template_no; nonce = m_starter_nonce + th_local_index; } if(!local_template_ver)//no any set_block_template call { - LOG_PRINT_L2("Block template not set yet"); - epee::misc_utils::sleep_no_w(1000); + logger(TRACE) << "Block template not set yet"; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); continue; } b.nonce = nonce; crypto::hash h; if (!m_stop && !get_block_longhash(context, b, h)) { - LOG_ERROR("Failed to get block long hash"); + logger(ERROR) << "Failed to get block long hash"; m_stop = true; } @@ -395,21 +403,21 @@ namespace cryptonote { //we lucky! ++m_config.current_extra_message_index; - LOG_PRINT_GREEN("Found block for difficulty: " << local_diff, LOG_LEVEL_0); - if(!m_phandler->handle_block_found(b)) - { + + logger(INFO, GREEN) << "Found block for difficulty: " << local_diff; + + if(!m_handler.handle_block_found(b)) { --m_config.current_extra_message_index; - }else - { + } else { //success update, lets update config - epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + cryptonote::parameters::MINER_CONFIG_FILE_NAME); + epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + CryptoNote::parameters::MINER_CONFIG_FILE_NAME); } } - nonce+=m_threads_total; + nonce += m_threads_total; ++m_hashes; } - LOG_PRINT_L0("Miner thread stopped ["<< th_local_index << "]"); + logger(INFO) << "Miner thread stopped ["<< th_local_index << "]"; return true; } //----------------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index f5de9e701e..e2c3ba06bd 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,21 +20,24 @@ #include #include +#include // epee #include "serialization/keyvalue_serialization.h" -#include "math_helper.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/Currency.h" #include "cryptonote_core/difficulty.h" #include "cryptonote_core/i_miner_handler.h" #include "cryptonote_core/MinerConfig.h" +#include "cryptonote_core/OnceInInterval.h" -namespace cryptonote { +#include + +namespace CryptoNote { class miner { public: - miner(const Currency& currency, i_miner_handler* phandler); + miner(const Currency& currency, i_miner_handler& handler, Logging::ILogger& log); ~miner(); bool init(const MinerConfig& config); @@ -54,7 +57,7 @@ namespace cryptonote { void do_print_hashrate(bool do_hr); private: - bool worker_thread(); + bool worker_thread(uint32_t th_local_index); bool request_block_template(); void merge_hr(); @@ -69,30 +72,36 @@ namespace cryptonote { const Currency& m_currency; - volatile uint32_t m_stop; - epee::critical_section m_template_lock; + Logging::LoggerRef logger; + + std::atomic m_stop; + std::mutex m_template_lock; Block m_template; std::atomic m_template_no; std::atomic m_starter_nonce; difficulty_type m_diffic; - volatile uint32_t m_thread_index; - volatile uint32_t m_threads_total; + + // volatile uint32_t m_thread_index; + std::atomic m_threads_total; std::atomic m_pausers_count; - epee::critical_section m_miners_count_lock; + std::mutex m_miners_count_lock; std::list m_threads; - epee::critical_section m_threads_lock; - i_miner_handler* m_phandler; + std::mutex m_threads_lock; + i_miner_handler& m_handler; AccountPublicAddress m_mine_address; - epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; - epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; + //epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; + //epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; + OnceInInterval m_update_block_template_interval; + OnceInInterval m_update_merge_hr_interval; + std::vector m_extra_messages; miner_config m_config; std::string m_config_folder_path; std::atomic m_last_hr_merge_time; std::atomic m_hashes; std::atomic m_current_hash_rate; - epee::critical_section m_last_hash_rates_lock; + std::mutex m_last_hash_rates_lock; std::list m_last_hash_rates; bool m_do_print_hashrate; bool m_do_mining; diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h index 325bc2a884..e7d3c14ded 100644 --- a/src/cryptonote_core/tx_extra.h +++ b/src/cryptonote_core/tx_extra.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -34,7 +34,7 @@ #define TX_EXTRA_NONCE_PAYMENT_ID 0x00 -namespace cryptonote +namespace CryptoNote { struct tx_extra_padding { @@ -114,7 +114,7 @@ namespace cryptonote BEGIN_SERIALIZE() VARINT_FIELD_N("depth", mm_tag.depth) - FIELD_N("merkle_root", mm_tag.merkle_root) + FIELD_N("merkle_root", mm_tag.merkle_root); END_SERIALIZE() }; @@ -126,8 +126,9 @@ namespace cryptonote bool do_serialize(Archive& ar) { std::string field; - if(!::do_serialize(ar, field)) + if(!::do_serialize(ar, field)) { return false; + } std::istringstream iss(field); binary_archive iar(iss); @@ -157,7 +158,7 @@ namespace cryptonote typedef boost::variant tx_extra_field; } -VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING); -VARIANT_TAG(binary_archive, cryptonote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY); -VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE); -VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG); +VARIANT_TAG(binary_archive, CryptoNote::tx_extra_padding, TX_EXTRA_TAG_PADDING); +VARIANT_TAG(binary_archive, CryptoNote::tx_extra_pub_key, TX_EXTRA_TAG_PUBKEY); +VARIANT_TAG(binary_archive, CryptoNote::tx_extra_nonce, TX_EXTRA_NONCE); +VARIANT_TAG(binary_archive, CryptoNote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG); diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 0f5a3897f0..36040c3616 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,23 +24,17 @@ #include -// epee -#include "misc_language.h" -#include "misc_log_ex.h" -#include "warnings.h" - -#include "common/boost_serialization_helper.h" -#include "common/int-util.h" -#include "common/util.h" +#include "Common/boost_serialization_helper.h" +#include "Common/int-util.h" +#include "Common/util.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_boost_serialization.h" #include "cryptonote_config.h" +using namespace Logging; -DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated - -namespace cryptonote { +namespace CryptoNote { //--------------------------------------------------------------------------------- // BlockTemplate @@ -99,12 +93,17 @@ namespace cryptonote { using CryptoNote::BlockInfo; //--------------------------------------------------------------------------------- - tx_memory_pool::tx_memory_pool(const cryptonote::Currency& currency, CryptoNote::ITransactionValidator& validator, CryptoNote::ITimeProvider& timeProvider) : + tx_memory_pool::tx_memory_pool( + const CryptoNote::Currency& currency, + CryptoNote::ITransactionValidator& validator, + CryptoNote::ITimeProvider& timeProvider, + Logging::ILogger& log) : m_currency(currency), m_validator(validator), m_timeProvider(timeProvider), m_txCheckInterval(60, timeProvider), - m_fee_index(boost::get<1>(m_transactions)) { + m_fee_index(boost::get<1>(m_transactions)), + logger(log, "txpool") { } //--------------------------------------------------------------------------------- @@ -123,16 +122,16 @@ namespace cryptonote { uint64_t outputs_amount = get_outs_money_amount(tx); if (outputs_amount >= inputs_amount) { - LOG_PRINT_L0("transaction use more money then it has: use " << m_currency.formatAmount(outputs_amount) << - ", have " << m_currency.formatAmount(inputs_amount)); + logger(INFO) << "transaction use more money then it has: use " << m_currency.formatAmount(outputs_amount) << + ", have " << m_currency.formatAmount(inputs_amount); tvc.m_verifivation_failed = true; return false; } const uint64_t fee = inputs_amount - outputs_amount; if (!keptByBlock && fee < m_currency.minimumFee()) { - LOG_PRINT_L0("transaction fee is not enought: " << m_currency.formatAmount(fee) << - ", minumim fee: " << m_currency.formatAmount(m_currency.minimumFee())); + logger(INFO) << "transaction fee is not enought: " << m_currency.formatAmount(fee) << + ", minumim fee: " << m_currency.formatAmount(m_currency.minimumFee()); tvc.m_verifivation_failed = true; tvc.m_tx_fee_too_small = true; return false; @@ -140,9 +139,9 @@ namespace cryptonote { //check key images for transaction if it is not kept by block if (!keptByBlock) { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); if (haveSpentInputs(tx)) { - LOG_PRINT_L0("Transaction with id= " << id << " used already spent inputs"); + logger(INFO) << "Transaction with id= " << id << " used already spent inputs"; tvc.m_verifivation_failed = true; return false; } @@ -155,7 +154,7 @@ namespace cryptonote { if (!inputsValid) { if (!keptByBlock) { - LOG_PRINT_L0("tx used wrong inputs, rejected"); + logger(INFO) << "tx used wrong inputs, rejected"; tvc.m_verifivation_failed = true; return false; } @@ -164,7 +163,7 @@ namespace cryptonote { tvc.m_verifivation_impossible = true; } - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); // add to pool { @@ -181,7 +180,7 @@ namespace cryptonote { txd.lastFailedBlock.clear(); auto txd_p = m_transactions.insert(std::move(txd)); - CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); + if (!(txd_p.second)) { logger(ERROR, BRIGHT_RED) << "transaction already exists at inserting in memory pool"; return false; } } tvc.m_added_to_pool = true; @@ -207,7 +206,7 @@ namespace cryptonote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee) { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); auto it = m_transactions.find(id); if (it == m_transactions.end()) { return false; @@ -224,19 +223,19 @@ namespace cryptonote { } //--------------------------------------------------------------------------------- size_t tx_memory_pool::get_transactions_count() const { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); return m_transactions.size(); } //--------------------------------------------------------------------------------- void tx_memory_pool::get_transactions(std::list& txs) const { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); for (const auto& tx_vt : m_transactions) { txs.push_back(tx_vt.tx); } } //--------------------------------------------------------------------------------- void tx_memory_pool::get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); std::unordered_set ready_tx_ids; for (const auto& tx : m_transactions) { TransactionCheckInfo checkInfo(tx); @@ -270,7 +269,7 @@ namespace cryptonote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::have_tx(const crypto::hash &id) const { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); if (m_transactions.count(id)) { return true; } @@ -300,7 +299,7 @@ namespace cryptonote { //--------------------------------------------------------------------------------- std::string tx_memory_pool::print_pool(bool short_format) const { std::stringstream ss; - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); for (const auto& txd : m_fee_index) { ss << "id: " << txd.id << std::endl; if (!short_format) { @@ -321,7 +320,7 @@ namespace cryptonote { //--------------------------------------------------------------------------------- bool tx_memory_pool::fill_block_template(Block& bl, size_t median_size, size_t maxCumulativeSize, uint64_t already_generated_coins, size_t& total_size, uint64_t& fee) { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); total_size = 0; fee = 0; @@ -357,7 +356,7 @@ namespace cryptonote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::init(const std::string& config_folder) { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); m_config_folder = config_folder; std::string state_file_path = config_folder + "/" + m_currency.txPoolFileName(); @@ -367,7 +366,7 @@ namespace cryptonote { } bool res = tools::unserialize_obj_from_file(*this, state_file_path); if (!res) { - LOG_ERROR("Failed to load memory pool from file " << state_file_path); + logger(ERROR) << "Failed to load memory pool from file " << state_file_path; m_transactions.clear(); m_spent_key_images.clear(); @@ -379,14 +378,14 @@ namespace cryptonote { //--------------------------------------------------------------------------------- bool tx_memory_pool::deinit() { if (!tools::create_directories_if_necessary(m_config_folder)) { - LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + logger(INFO) << "Failed to create data directory: " << m_config_folder; return false; } std::string state_file_path = m_config_folder + "/" + m_currency.txPoolFileName(); bool res = tools::serialize_obj_to_file(*this, state_file_path); if (!res) { - LOG_PRINT_L0("Failed to serialize memory pool to file " << state_file_path); + logger(INFO) << "Failed to serialize memory pool to file " << state_file_path; } return true; } @@ -400,23 +399,23 @@ namespace cryptonote { bool tx_memory_pool::removeExpiredTransactions() { bool somethingRemoved = false; { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); - auto now = m_timeProvider.now(); + auto now = m_timeProvider.now(); - for (auto it = m_transactions.begin(); it != m_transactions.end();) { - uint64_t txAge = now - it->receiveTime; - bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); + for (auto it = m_transactions.begin(); it != m_transactions.end();) { + uint64_t txAge = now - it->receiveTime; + bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); - if (remove) { - LOG_PRINT_L2("Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge); - it = removeTransaction(it); + if (remove) { + logger(TRACE) << "Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge; + it = removeTransaction(it); somethingRemoved = true; - } else { - ++it; - } + } else { + ++it; } } + } if (somethingRemoved) { m_observerManager.notify(&ITxPoolObserver::txDeletedFromPool); @@ -435,15 +434,15 @@ namespace cryptonote { if (in.type() == typeid(TransactionInputToKey)) { const auto& txin = boost::get(in); auto it = m_spent_key_images.find(txin.keyImage); - CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.keyImage << std::endl - << "transaction id = " << tx_id); + if (!(it != m_spent_key_images.end())) { logger(ERROR, BRIGHT_RED) << "failed to find transaction input in key images. img=" << txin.keyImage << std::endl + << "transaction id = " << tx_id; return false; } std::unordered_set& key_image_set = it->second; - CHECK_AND_ASSERT_MES(!key_image_set.empty(), false, "empty key_image set, img=" << txin.keyImage << std::endl - << "transaction id = " << tx_id); + if (!(!key_image_set.empty())) { logger(ERROR, BRIGHT_RED) << "empty key_image set, img=" << txin.keyImage << std::endl + << "transaction id = " << tx_id; return false; } auto it_in_set = key_image_set.find(tx_id); - CHECK_AND_ASSERT_MES(it_in_set != key_image_set.end(), false, "transaction id not found in key_image set, img=" << txin.keyImage << std::endl - << "transaction id = " << tx_id); + if (!(it_in_set != key_image_set.end())) { logger(ERROR, BRIGHT_RED) << "transaction id not found in key_image set, img=" << txin.keyImage << std::endl + << "transaction id = " << tx_id; return false; } key_image_set.erase(it_in_set); if (key_image_set.empty()) { //it is now empty hash container for this key_image @@ -469,11 +468,18 @@ namespace cryptonote { if (in.type() == typeid(TransactionInputToKey)) { const auto& txin = boost::get(in); std::unordered_set& kei_image_set = m_spent_key_images[txin.keyImage]; - CHECK_AND_ASSERT_MES(keptByBlock || kei_image_set.size() == 0, false, "internal error: keptByBlock=" << keptByBlock - << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.keyImage=" << txin.keyImage << ENDL - << "tx_id=" << id); + if (!(keptByBlock || kei_image_set.size() == 0)) { + logger(ERROR, BRIGHT_RED) + << "internal error: keptByBlock=" << keptByBlock + << ", kei_image_set.size()=" << kei_image_set.size() << ENDL + << "txin.keyImage=" << txin.keyImage << ENDL << "tx_id=" << id; + return false; + } 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"); + if (!(ins_res.second)) { + logger(ERROR, BRIGHT_RED) << "internal error: try to insert duplicate iterator in key_image set"; + return false; + } } else if (in.type() == typeid(TransactionInputMultisignature)) { if (!keptByBlock) { const auto& msig = boost::get(in); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 78a22f92ca..7276f2716c 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,7 +16,6 @@ // along with Bytecoin. If not, see . #pragma once -#include "include_base_utils.h" #include #include @@ -31,14 +30,9 @@ #include #include -// epee -#include "math_helper.h" -#include "string_tools.h" -#include "syncobj.h" - -#include "common/util.h" -#include "common/int-util.h" -#include "common/ObserverManager.h" +#include "Common/util.h" +#include "Common/int-util.h" +#include "Common/ObserverManager.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/Currency.h" @@ -47,8 +41,9 @@ #include "cryptonote_core/ITxPoolObserver.h" #include "cryptonote_core/verification_context.h" +#include -namespace cryptonote { +namespace CryptoNote { class OnceInTimeInterval { @@ -85,8 +80,11 @@ namespace cryptonote { /************************************************************************/ class tx_memory_pool: boost::noncopyable { public: - tx_memory_pool(const cryptonote::Currency& currency, CryptoNote::ITransactionValidator& validator, - CryptoNote::ITimeProvider& timeProvider); + tx_memory_pool( + const CryptoNote::Currency& currency, + CryptoNote::ITransactionValidator& validator, + CryptoNote::ITimeProvider& timeProvider, + Logging::ILogger& log); bool addObserver(ITxPoolObserver* observer); bool removeObserver(ITxPoolObserver* observer); @@ -117,7 +115,7 @@ namespace cryptonote { template void getTransactions(const t_ids_container& txsIds, t_tx_container& txs, t_missed_container& missedTxs) { - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); for (const auto& id : txsIds) { auto it = m_transactions.find(id); @@ -137,7 +135,7 @@ namespace cryptonote { return; } - CRITICAL_REGION_LOCAL(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); a & m_transactions; a & m_spent_key_images; a & m_spentOutputs; @@ -202,10 +200,9 @@ namespace cryptonote { bool is_transaction_ready_to_go(const Transaction& tx, TransactionCheckInfo& txd) const; tools::ObserverManager m_observerManager; - - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; OnceInTimeInterval m_txCheckInterval; - mutable epee::critical_section m_transactions_lock; + mutable std::recursive_mutex m_transactions_lock; key_images_container m_spent_key_images; GlobalOutputsContainer m_spentOutputs; @@ -216,6 +213,8 @@ namespace cryptonote { tx_container_t m_transactions; tx_container_t::nth_index<1>::type& m_fee_index; + Logging::LoggerRef logger; + #if defined(DEBUG_CREATE_BLOCK_TEMPLATE) friend class blockchain_storage; #endif @@ -225,7 +224,7 @@ namespace cryptonote { namespace boost { namespace serialization { template - void serialize(archive_t & ar, cryptonote::tx_memory_pool::TransactionDetails& td, const unsigned int version) { + void serialize(archive_t & ar, CryptoNote::tx_memory_pool::TransactionDetails& td, const unsigned int version) { ar & td.id; ar & td.blobSize; ar & td.fee; @@ -240,4 +239,4 @@ namespace boost { } } -BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) +BOOST_CLASS_VERSION(CryptoNote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index 52fd1cc118..70cc1b544c 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,7 +16,7 @@ // along with Bytecoin. If not, see . #pragma once -namespace cryptonote +namespace CryptoNote { /************************************************************************/ /* */ diff --git a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h b/src/cryptonote_protocol/ICryptonoteProtocolObserver.h index a573773cb4..3f90904897 100644 --- a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h +++ b/src/cryptonote_protocol/ICryptonoteProtocolObserver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class ICryptonoteProtocolObserver { public: @@ -29,4 +29,4 @@ class ICryptonoteProtocolObserver { virtual void lastKnownBlockHeightUpdated(uint64_t height) {} }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h b/src/cryptonote_protocol/ICryptonoteProtocolQuery.h index 9d853c9bc5..1eb79ec94a 100644 --- a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h +++ b/src/cryptonote_protocol/ICryptonoteProtocolQuery.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,7 +19,7 @@ #include -namespace cryptonote { +namespace CryptoNote { class ICryptonoteProtocolObserver; class ICryptonoteProtocolQuery { @@ -31,4 +31,4 @@ class ICryptonoteProtocolQuery { virtual size_t getPeerCount() const = 0; }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h index a47e9d34f3..e6faa3bd26 100644 --- a/src/cryptonote_protocol/blobdatatype.h +++ b/src/cryptonote_protocol/blobdatatype.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,23 @@ #pragma once -namespace cryptonote +#include "Common/StringTools.h" + +namespace CryptoNote { typedef std::string blobdata; + + inline bool hexToBlob(const std::string& hexStr, blobdata& blob) { + std::vector data; + if (Common::fromHex(hexStr, data)) { + blob = Common::asString(data); + return true; + } + return false; + } + + inline std::string blobToHex(const blobdata& blob) { + return Common::toHex(blob.data(), blob.size()); + } + } diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index b52e7d4ffc..5745881343 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,12 +21,12 @@ #include "serialization/keyvalue_serialization.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_protocol/blobdatatype.h" -namespace cryptonote -{ -#define BC_COMMANDS_POOL_BASE 2000 +namespace CryptoNote +{ +#define BC_COMMANDS_POOL_BASE 2000 /************************************************************************/ /* */ @@ -133,18 +133,6 @@ namespace cryptonote typedef NOTIFY_RESPONSE_GET_OBJECTS_request request; }; - - struct CORE_SYNC_DATA - { - uint64_t current_height; - crypto::hash top_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(current_height) - KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) - END_KV_SERIALIZE_MAP() - }; - struct NOTIFY_REQUEST_CHAIN { const static int ID = BC_COMMANDS_POOL_BASE + 6; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler.cpp new file mode 100644 index 0000000000..018656f14d --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.cpp @@ -0,0 +1,630 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "cryptonote_protocol_handler.h" + +#include +#include +#include + +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/verification_context.h" +#include "p2p/LevinProtocol.h" + +using namespace Logging; + +namespace CryptoNote { + +namespace { + +template +bool post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, cryptonote_connection_context& context) { + return p2p.invoke_notify_to_peer(t_parametr::ID, LevinProtocol::encode(arg), context); +} + +template +void relay_post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, const net_connection_id* excludeConnection = nullptr) { + p2p.relay_notify_to_all(t_parametr::ID, LevinProtocol::encode(arg), excludeConnection); +} + +} + +cryptonote_protocol_handler::cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log) : + m_dispatcher(dispatcher), + m_currency(currency), + m_core(rcore), + m_p2p(p_net_layout), + m_synchronized(false), + m_stop(false), + m_observedHeight(0), + m_peersCount(0), + logger(log, "protocol") { + + if (!m_p2p) { + m_p2p = &m_p2p_stub; + } +} + +size_t cryptonote_protocol_handler::getPeerCount() const { + return m_peersCount; +} + +void cryptonote_protocol_handler::set_p2p_endpoint(i_p2p_endpoint* p2p) { + if (p2p) + m_p2p = p2p; + else + m_p2p = &m_p2p_stub; +} + +void cryptonote_protocol_handler::onConnectionOpened(cryptonote_connection_context& context) { +} + +void cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_context& context) { + bool updated = false; + { + std::lock_guard lock(m_observedHeightMutex); + uint64_t prevHeight = m_observedHeight; + recalculateMaxObservedHeight(context); + if (prevHeight != m_observedHeight) { + updated = true; + } + } + + if (updated) { + logger(TRACE) << "Observed height updated: " << m_observedHeight; + m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + } + + if (context.m_state != cryptonote_connection_context::state_befor_handshake) { + m_peersCount--; + m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + } +} + +void cryptonote_protocol_handler::stop() { + m_stop = true; +} + +bool cryptonote_protocol_handler::start_sync(cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "Starting synchronization"; + + if (context.m_state == cryptonote_connection_context::state_synchronizing) { + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); + m_core.get_short_chain_history(r.block_ids); + logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); + post_notify(*m_p2p, r, context); + } + + return true; +} + +bool cryptonote_protocol_handler::get_stat_info(core_stat_info& stat_inf) { + return m_core.get_stat_info(stat_inf); +} + +void cryptonote_protocol_handler::log_connections() { + std::stringstream ss; + + ss << std::setw(25) << std::left << "Remote Host" + << std::setw(20) << "Peer id" + << std::setw(25) << "Recv/Sent (inactive,sec)" + << std::setw(25) << "State" + << std::setw(20) << "Livetime(seconds)" << ENDL; + + m_p2p->for_each_connection([&](const cryptonote_connection_context& cntxt, peerid_type peer_id) { + ss << std::setw(25) << std::left << std::string(cntxt.m_is_income ? "[INC]" : "[OUT]") + + Common::ipAddressToString(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) + << std::setw(20) << std::hex << peer_id + // << std::setw(25) << std::to_string(cntxt.m_recv_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" + << std::setw(25) << get_protocol_state_string(cntxt.m_state) + << std::setw(20) << std::to_string(time(NULL) - cntxt.m_started) << ENDL; + }); + logger(INFO) << "Connections: " << ENDL << ss.str(); +} + +uint64_t cryptonote_protocol_handler::get_current_blockchain_height() { + uint64_t height; + crypto::hash blockId; + m_core.get_blockchain_top(height, blockId); + return height; +} + +bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital) { + if (context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) + return true; + + if (context.m_state == cryptonote_connection_context::state_synchronizing) { + } else if (m_core.have_block(hshd.top_id)) { + context.m_state = cryptonote_connection_context::state_normal; + if (is_inital) + on_connection_synchronized(); + } else { + int64_t diff = static_cast(hshd.current_height) - static_cast(get_current_blockchain_height()); + + logger(diff >= 0 ? (is_inital ? Logging::INFO : Logging::DEBUGGING) : Logging::TRACE, Logging::BRIGHT_YELLOW) << context << + "Sync data returned unknown top block: " << get_current_blockchain_height() << " -> " << hshd.current_height + << " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_currency.difficultyTarget()) << " days) " + << (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl << "SYNCHRONIZATION started"; + + logger(Logging::DEBUGGING) << "Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id; + //let the socket to send response to handshake, but request callback, to let send request data after response + logger(Logging::TRACE) << context << "requesting synchronization"; + context.m_state = cryptonote_connection_context::state_sync_required; + } + + updateObservedHeight(hshd.current_height, context); + context.m_remote_blockchain_height = hshd.current_height; + + if (is_inital) { + m_peersCount++; + m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + } + + return true; +} + +bool cryptonote_protocol_handler::get_payload_sync_data(CORE_SYNC_DATA& hshd) { + m_core.get_blockchain_top(hshd.current_height, hshd.top_id); + hshd.current_height += 1; + return true; +} + + +template +int notifyAdaptor(const std::string& reqBuf, cryptonote_connection_context& ctx, Handler handler) { + + typedef typename Command::request Request; + int command = Command::ID; + + Request req = boost::value_initialized(); + if (!LevinProtocol::decode(reqBuf, req)) { + throw std::runtime_error("Failed to load_from_binary in command " + std::to_string(command)); + } + + return handler(command, req, ctx); +} + +#define HANDLE_NOTIFY(CMD, Handler) case CMD::ID: { ret = notifyAdaptor(in, ctx, boost::bind(Handler, this, _1, _2, _3)); break; } + +int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, const std::string& in, std::string& out, cryptonote_connection_context& ctx, bool& handled) { + int ret = 0; + handled = true; + + switch (command) { + HANDLE_NOTIFY(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) + HANDLE_NOTIFY(NOTIFY_NEW_TRANSACTIONS, &cryptonote_protocol_handler::handle_notify_new_transactions) + HANDLE_NOTIFY(NOTIFY_REQUEST_GET_OBJECTS, &cryptonote_protocol_handler::handle_request_get_objects) + HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) + HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) + HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) + + default: + handled = false; + } + + return ret; +} + +#undef HANDLE_NOTIFY + +int cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"; + + updateObservedHeight(arg.current_blockchain_height, context); + + context.m_remote_blockchain_height = arg.current_blockchain_height; + + if (context.m_state != cryptonote_connection_context::state_normal) { + return 1; + } + + for (auto tx_blob_it = arg.b.txs.begin(); tx_blob_it != arg.b.txs.end(); tx_blob_it++) { + CryptoNote::tx_verification_context tvc = boost::value_initialized(); + m_core.handle_incoming_tx(*tx_blob_it, tvc, true); + if (tvc.m_verifivation_failed) { + logger(Logging::INFO) << context << "Block verification failed: transaction verification failed, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + } + + block_verification_context bvc = boost::value_initialized(); + m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); + if (bvc.m_verifivation_failed) { + logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + if (bvc.m_added_to_main_chain) { + ++arg.hop; + //TODO: Add here announce protocol usage + relay_post_notify(*m_p2p, arg, &context.m_connection_id); + // relay_block(arg, context); + } else if (bvc.m_marked_as_orphaned) { + context.m_state = cryptonote_connection_context::state_synchronizing; + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); + m_core.get_short_chain_history(r.block_ids); + logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); + post_notify(*m_p2p, r, context); + } + + return 1; +} + +int cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_NEW_TRANSACTIONS"; + if (context.m_state != cryptonote_connection_context::state_normal) + return 1; + + for (auto tx_blob_it = arg.txs.begin(); tx_blob_it != arg.txs.end();) { + CryptoNote::tx_verification_context tvc = boost::value_initialized(); + m_core.handle_incoming_tx(*tx_blob_it, tvc, false); + if (tvc.m_verifivation_failed) { + logger(Logging::INFO) << context << "Tx verification failed, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + if (tvc.m_should_be_relayed) + ++tx_blob_it; + else + arg.txs.erase(tx_blob_it++); + } + + if (arg.txs.size()) { + //TODO: add announce usage here + relay_post_notify(*m_p2p, arg, &context.m_connection_id); + } + + return true; +} + +int cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_REQUEST_GET_OBJECTS"; + NOTIFY_RESPONSE_GET_OBJECTS::request rsp; + if (!m_core.handle_get_objects(arg, rsp)) { + logger(Logging::ERROR) << context << "failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + } + logger(Logging::TRACE) << context << "-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() + << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size(); + post_notify(*m_p2p, rsp, context); + return 1; +} + +int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_RESPONSE_GET_OBJECTS"; + + if (context.m_last_response_height > arg.current_blockchain_height) { + logger(Logging::ERROR) << context << "sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height + << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + updateObservedHeight(arg.current_blockchain_height, context); + + context.m_remote_blockchain_height = arg.current_blockchain_height; + + size_t count = 0; + for (const block_complete_entry& block_entry : arg.blocks) { + ++count; + Block b; + if (!parse_and_validate_block_from_blob(block_entry.block, b)) { + logger(Logging::ERROR) << context << "sent wrong block: failed to parse and validate block: \r\n" + << blobToHex(block_entry.block) << "\r\n dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + //to avoid concurrency in core between connections, suspend connections which delivered block later then first one + if (count == 2) { + if (m_core.have_block(get_block_hash(b))) { + context.m_state = cryptonote_connection_context::state_idle; + context.m_needed_objects.clear(); + context.m_requested_objects.clear(); + logger(Logging::DEBUGGING) << context << "Connection set to idle state."; + return 1; + } + } + + auto req_it = context.m_requested_objects.find(get_block_hash(b)); + if (req_it == context.m_requested_objects.end()) { + logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(get_blob_hash(block_entry.block)) + << " wasn't requested, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + if (b.txHashes.size() != block_entry.txs.size()) { + logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(get_blob_hash(block_entry.block)) + << ", txHashes.size()=" << b.txHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + context.m_requested_objects.erase(req_it); + } + + if (context.m_requested_objects.size()) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << context << + "returned not all requested objects (context.m_requested_objects.size()=" + << context.m_requested_objects.size() << "), dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + { + m_core.pause_mining(); + + BOOST_SCOPE_EXIT_ALL(this) { m_core.update_block_template_and_resume_mining(); }; + + auto currentContext = m_dispatcher.getCurrentContext(); + + auto resultFuture = std::async(std::launch::async, [&]{ + int result = processObjects(context, arg.blocks); + m_dispatcher.remoteSpawn([&] { + m_dispatcher.pushContext(currentContext); + }); + + return result; + }); + + m_dispatcher.dispatch(); + int result = resultFuture.get(); + if (result != 0) { + return result; + } + } + + uint64_t height; + crypto::hash top; + m_core.get_blockchain_top(height, top); + logger(INFO, BRIGHT_GREEN) << "Local blockchain updated, new height = " << height; + + if (!m_stop && context.m_state == cryptonote_connection_context::state_synchronizing) { + request_missing_objects(context, true); + } + + return 1; +} + +int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& context, const std::list& blocks) { + + for (const block_complete_entry& block_entry : blocks) { + if (m_stop) { + break; + } + + //process transactions + for (auto& tx_blob : block_entry.txs) { + tx_verification_context tvc = boost::value_initialized(); + m_core.handle_incoming_tx(tx_blob, tvc, true); + if (tvc.m_verifivation_failed) { + logger(Logging::ERROR) << context << "transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " + << Common::podToHex(get_blob_hash(tx_blob)) << ", dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + } + + // process block + block_verification_context bvc = boost::value_initialized(); + m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); + + if (bvc.m_verifivation_failed) { + logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } else if (bvc.m_marked_as_orphaned) { + logger(Logging::INFO) << context << "Block received at sync phase was marked as orphaned, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } else if (bvc.m_already_exists) { + logger(Logging::DEBUGGING) << context << "Block already exists, switching to idle state"; + context.m_state = cryptonote_connection_context::state_idle; + return 1; + } + } + + return 0; + +} + + +bool cryptonote_protocol_handler::on_idle() { + return m_core.on_idle(); +} + +int cryptonote_protocol_handler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size(); + NOTIFY_RESPONSE_CHAIN_ENTRY::request r; + if (!m_core.find_blockchain_supplement(arg.block_ids, r)) { + logger(Logging::ERROR) << context << "Failed to handle NOTIFY_REQUEST_CHAIN."; + return 1; + } + logger(Logging::TRACE) << context << "-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size(); + post_notify(*m_p2p, r, context); + return 1; +} + +bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks) { + if (context.m_needed_objects.size()) { + //we know objects that we need, request this objects + NOTIFY_REQUEST_GET_OBJECTS::request req; + size_t count = 0; + auto it = context.m_needed_objects.begin(); + + while (it != context.m_needed_objects.end() && count < BLOCKS_SYNCHRONIZING_DEFAULT_COUNT) { + if (!(check_having_blocks && m_core.have_block(*it))) { + req.blocks.push_back(*it); + ++count; + context.m_requested_objects.insert(*it); + } + context.m_needed_objects.erase(it++); + } + logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size(); + post_notify(*m_p2p, req, context); + } else if (context.m_last_response_height < context.m_remote_blockchain_height - 1) {//we have to fetch more objects ids, request blockchain entry + + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); + m_core.get_short_chain_history(r.block_ids); + logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); + post_notify(*m_p2p, r, context); + } else { + if (!(context.m_last_response_height == + context.m_remote_blockchain_height - 1 && + !context.m_needed_objects.size() && + !context.m_requested_objects.size())) { + logger(Logging::ERROR, Logging::BRIGHT_RED) + << "request_missing_blocks final condition failed!" + << "\r\nm_last_response_height=" << context.m_last_response_height + << "\r\nm_remote_blockchain_height=" << context.m_remote_blockchain_height + << "\r\nm_needed_objects.size()=" << context.m_needed_objects.size() + << "\r\nm_requested_objects.size()=" << context.m_requested_objects.size() + << "\r\non connection [" << context << "]"; + return false; + } + + context.m_state = cryptonote_connection_context::state_normal; + logger(Logging::INFO, Logging::BRIGHT_GREEN) << context << "SYNCHRONIZED OK"; + on_connection_synchronized(); + } + return true; +} + +bool cryptonote_protocol_handler::on_connection_synchronized() { + bool val_expected = false; + if (m_synchronized.compare_exchange_strong(val_expected, true)) { + logger(Logging::INFO) << ENDL << "**********************************************************************" << ENDL + << "You are now synchronized with the network. You may now start simplewallet." << ENDL + << ENDL + << "Please note, that the blockchain will be saved only after you quit the daemon with \"exit\" command or if you use \"save\" command." << ENDL + << "Otherwise, you will possibly need to synchronize the blockchain again." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << "**********************************************************************"; + m_core.on_synchronized(); + } + return true; +} + +int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() + << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height; + + if (!arg.m_block_ids.size()) { + logger(Logging::ERROR) << context << "sent empty m_block_ids, dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + if (!m_core.have_block(arg.m_block_ids.front())) { + logger(Logging::ERROR) + << context << "sent m_block_ids starting from unknown id: " + << Common::podToHex(arg.m_block_ids.front()) + << " , dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + context.m_remote_blockchain_height = arg.total_height; + context.m_last_response_height = arg.start_height + arg.m_block_ids.size() - 1; + + if (context.m_last_response_height > context.m_remote_blockchain_height) { + logger(Logging::ERROR) + << context + << "sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" + << arg.total_height << "\r\nm_start_height=" << arg.start_height + << "\r\nm_block_ids.size()=" << arg.m_block_ids.size(); + context.m_state = cryptonote_connection_context::state_shutdown; + } + + for (auto& bl_id : arg.m_block_ids) { + if (!m_core.have_block(bl_id)) + context.m_needed_objects.push_back(bl_id); + } + + request_missing_objects(context, false); + return 1; +} + +void cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg) { + auto buf = LevinProtocol::encode(arg); + m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_BLOCK::ID, buf); +} + +void cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) { + auto buf = LevinProtocol::encode(arg); + m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_TRANSACTIONS::ID, buf); +} + +void cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context) { + bool updated = false; + { + std::lock_guard lock(m_observedHeightMutex); + + uint64_t height = m_observedHeight; + if (peerHeight > context.m_remote_blockchain_height) { + m_observedHeight = std::max(m_observedHeight, peerHeight); + if (m_observedHeight != height) { + updated = true; + } + } else if (context.m_remote_blockchain_height == m_observedHeight) { + //the client switched to alternative chain and had maximum observed height. need to recalculate max height + recalculateMaxObservedHeight(context); + if (m_observedHeight != height) { + updated = true; + } + } + } + + if (updated) { + logger(TRACE) << "Observed height updated: " << m_observedHeight; + m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + } +} + +void cryptonote_protocol_handler::recalculateMaxObservedHeight(const cryptonote_connection_context& context) { + //should be locked outside + uint64_t peerHeight = 0; + m_p2p->for_each_connection([&peerHeight, &context](const cryptonote_connection_context& ctx, peerid_type peerId) { + if (ctx.m_connection_id != context.m_connection_id) { + peerHeight = std::max(peerHeight, ctx.m_remote_blockchain_height); + } + }); + + uint64_t localHeight = 0; + crypto::hash ignore; + m_core.get_blockchain_top(localHeight, ignore); + m_observedHeight = std::max(peerHeight, localHeight); +} + +uint64_t cryptonote_protocol_handler::getObservedHeight() const { + std::lock_guard lock(m_observedHeightMutex); + return m_observedHeight; +}; + +bool cryptonote_protocol_handler::addObserver(ICryptonoteProtocolObserver* observer) { + return m_observerManager.add(observer); +} + +bool cryptonote_protocol_handler::removeObserver(ICryptonoteProtocolObserver* observer) { + return m_observerManager.remove(observer); +} + +}; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index fc5e75ec1d..1e2cd8aaae 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,65 +20,58 @@ #include #include -#include +#include -// epee -#include "storages/levin_abstract_invoke2.h" -#include "warnings.h" +#include "cryptonote_core/ICore.h" -#include "cryptonote_core/connection_context.h" -#include "cryptonote_core/cryptonote_stat_info.h" -#include "cryptonote_core/verification_context.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" #include "cryptonote_protocol/ICryptonoteProtocolObserver.h" #include "cryptonote_protocol/ICryptonoteProtocolQuery.h" +#include "p2p/p2p_protocol_defs.h" +#include "p2p/net_node_common.h" +#include "p2p/connection_context.h" + +#include + PUSH_WARNINGS DISABLE_VS_WARNINGS(4355) -namespace cryptonote { +namespace System { + class Dispatcher; +} + +namespace CryptoNote +{ + class Currency; - template - class t_cryptonote_protocol_handler : public i_cryptonote_protocol, public ICryptonoteProtocolQuery + class cryptonote_protocol_handler : + public i_cryptonote_protocol, + public ICryptonoteProtocolQuery { public: - typedef cryptonote_connection_context connection_context; - typedef core_stat_info stat_info; - typedef t_cryptonote_protocol_handler cryptonote_protocol_handler; - typedef CORE_SYNC_DATA payload_type; - - t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint* p_net_layout); - - BEGIN_INVOKE_MAP2(cryptonote_protocol_handler) - HANDLE_NOTIFY_T2(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) - HANDLE_NOTIFY_T2(NOTIFY_NEW_TRANSACTIONS, &cryptonote_protocol_handler::handle_notify_new_transactions) - HANDLE_NOTIFY_T2(NOTIFY_REQUEST_GET_OBJECTS, &cryptonote_protocol_handler::handle_request_get_objects) - HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) - HANDLE_NOTIFY_T2(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) - HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) - END_INVOKE_MAP2() - bool init(); - bool deinit(); + cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log); virtual bool addObserver(ICryptonoteProtocolObserver* observer); virtual bool removeObserver(ICryptonoteProtocolObserver* observer); - void set_p2p_endpoint(nodetool::i_p2p_endpoint* p2p); - t_core& get_core() { return m_core; } + void set_p2p_endpoint(i_p2p_endpoint* p2p); + // ICore& get_core() { return m_core; } bool is_synchronized() const { return m_synchronized; } void log_connections(); // Interface t_payload_net_handler, where t_payload_net_handler is template argument of nodetool::node_server void stop(); - bool on_callback(cryptonote_connection_context& context); + bool start_sync(cryptonote_connection_context& context); bool on_idle(); void onConnectionOpened(cryptonote_connection_context& context); void onConnectionClosed(cryptonote_connection_context& context); bool get_stat_info(core_stat_info& stat_inf); bool get_payload_sync_data(CORE_SYNC_DATA& hshd); bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); + int handleCommand(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, cryptonote_connection_context& context, bool& handled); virtual size_t getPeerCount() const; virtual uint64_t getObservedHeight() const; @@ -92,39 +85,26 @@ namespace cryptonote { int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); //----------------- i_cryptonote_protocol ---------------------------------- - virtual void relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) override; - virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) override; - //---------------------------------------------------------------------------------- + virtual void relay_block(NOTIFY_NEW_BLOCK::request& arg) override; + virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) override; + //---------------------------------------------------------------------------------- + uint64_t get_current_blockchain_height(); bool request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks); - size_t get_synchronizing_connections_count(); bool on_connection_synchronized(); void updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context); void recalculateMaxObservedHeight(const cryptonote_connection_context& context); - - template - bool post_notify(typename t_parametr::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); - std::string blob; - epee::serialization::store_t_to_binary(arg, blob); - return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); - } - - template - void relay_post_notify(typename t_parametr::request& arg, cryptonote_connection_context& exlude_context) - { - LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(exlude_context) << "] post relay " << typeid(t_parametr).name() << " -->"); - std::string arg_buff; - epee::serialization::store_t_to_binary(arg, arg_buff); - m_p2p->relay_notify_to_all(t_parametr::ID, arg_buff, exlude_context); - } + int processObjects(cryptonote_connection_context& context, const std::list& blocks); + Logging::LoggerRef logger; private: - t_core& m_core; - nodetool::p2p_endpoint_stub m_p2p_stub; - nodetool::i_p2p_endpoint* m_p2p; + System::Dispatcher& m_dispatcher; + ICore& m_core; + const Currency& m_currency; + + p2p_endpoint_stub m_p2p_stub; + i_p2p_endpoint* m_p2p; std::atomic m_synchronized; std::atomic m_stop; @@ -136,6 +116,4 @@ namespace cryptonote { }; } -#include "cryptonote_protocol_handler.inl" - POP_WARNINGS diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl deleted file mode 100644 index 4326f03279..0000000000 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ /dev/null @@ -1,613 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -// epee -#include "profile_tools.h" - -#include "cryptonote_core/cryptonote_format_utils.h" - -namespace cryptonote -{ - //----------------------------------------------------------------------------------------------------------------------- - template - t_cryptonote_protocol_handler::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint* p_net_layout) : - m_core(rcore), - m_p2p(p_net_layout), - m_synchronized(false), - m_stop(false), - m_observedHeight(0) { - if (!m_p2p) { - m_p2p = &m_p2p_stub; - } - } - - template - bool t_cryptonote_protocol_handler::init() { - m_peersCount = 0; - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::deinit() - { - return true; - } - - template - size_t t_cryptonote_protocol_handler::getPeerCount() const { - return m_peersCount; - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::set_p2p_endpoint(nodetool::i_p2p_endpoint* p2p) - { - if(p2p) - m_p2p = p2p; - else - m_p2p = &m_p2p_stub; - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::onConnectionOpened(cryptonote_connection_context& context) { - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_context& context) { - bool updated = false; - { - std::lock_guard lock(m_observedHeightMutex); - uint64_t prevHeight = m_observedHeight; - recalculateMaxObservedHeight(context); - if (prevHeight != m_observedHeight) { - updated = true; - } - } - - if (updated) { - LOG_PRINT_L2("Observed height updated: " << m_observedHeight); - m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); - } - - if (context.m_state != cryptonote_connection_context::state_befor_handshake) { - m_peersCount--; - m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); - } - } - - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::stop() { - m_stop = true; - } - - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::on_callback(cryptonote_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("callback fired"); - CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); - --context.m_callback_request_count; - - if(context.m_state == cryptonote_connection_context::state_synchronizing) - { - NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); - post_notify(r, context); - } - - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::get_stat_info(core_stat_info& stat_inf) - { - return m_core.get_stat_info(stat_inf); - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::log_connections() - { - std::stringstream ss; - - ss << std::setw(25) << std::left << "Remote Host" - << std::setw(20) << "Peer id" - << std::setw(25) << "Recv/Sent (inactive,sec)" - << std::setw(25) << "State" - << std::setw(20) << "Livetime(seconds)" << ENDL; - - m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) - { - ss << std::setw(25) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + - epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) - << std::setw(20) << std::hex << peer_id - << std::setw(25) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" - << std::setw(25) << get_protocol_state_string(cntxt.m_state) - << std::setw(20) << std::to_string(time(NULL) - cntxt.m_started) << ENDL; - return true; - }); - LOG_PRINT_L0("Connections: " << ENDL << ss.str()); - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital) - { - if(context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) - return true; - - if(context.m_state == cryptonote_connection_context::state_synchronizing) { - } else if(m_core.have_block(hshd.top_id)) { - context.m_state = cryptonote_connection_context::state_normal; - if(is_inital) - on_connection_synchronized(); - } else { - int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); - LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height - << " [" << std::abs(diff) << " blocks (" << std::abs(diff) / (24 * 60 * 60 / m_core.currency().difficultyTarget()) << " days) " - << (diff >= 0 ? std::string("behind") : std::string("ahead")) << "] " << std::endl << - "SYNCHRONIZATION started", (diff >= 0 ? (is_inital ? LOG_LEVEL_0 : LOG_LEVEL_1) : LOG_LEVEL_2)); - LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id); - context.m_state = cryptonote_connection_context::state_synchronizing; - - //let the socket to send response to handshake, but request callback, to let send request data after response - LOG_PRINT_CCONTEXT_L2("requesting callback"); - ++context.m_callback_request_count; - m_p2p->request_callback(context); - } - - updateObservedHeight(hshd.current_height, context); - context.m_remote_blockchain_height = hshd.current_height; - - if (is_inital) { - m_peersCount++; - m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); - } - - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::get_payload_sync_data(CORE_SYNC_DATA& hshd) - { - m_core.get_blockchain_top(hshd.current_height, hshd.top_id); - hshd.current_height +=1; - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"); - - updateObservedHeight(arg.current_blockchain_height, context); - - context.m_remote_blockchain_height = arg.current_blockchain_height; - - if (context.m_state != cryptonote_connection_context::state_normal) { - return 1; - } - - for (auto tx_blob_it = arg.b.txs.begin(); tx_blob_it != arg.b.txs.end(); tx_blob_it++) { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(*tx_blob_it, tvc, true); - if (tvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L0("Block verification failed: transaction verification failed, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - } - - block_verification_context bvc = boost::value_initialized(); - m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); - if (bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - if (bvc.m_added_to_main_chain) { - ++arg.hop; - //TODO: Add here announce protocol usage - relay_block(arg, context); - } else if (bvc.m_marked_as_orphaned) { - context.m_state = cryptonote_connection_context::state_synchronizing; - NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size()); - post_notify(r, context); - } - - return 1; - } - //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_TRANSACTIONS"); - if(context.m_state != cryptonote_connection_context::state_normal) - return 1; - - for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end();) - { - cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(*tx_blob_it, tvc, false); - if(tvc.m_verifivation_failed) - { - LOG_PRINT_CCONTEXT_L0("Tx verification failed, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - if(tvc.m_should_be_relayed) - ++tx_blob_it; - else - arg.txs.erase(tx_blob_it++); - } - - if(arg.txs.size()) - { - //TODO: add announce usage here - relay_transactions(arg, context); - } - - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_GET_OBJECTS"); - NOTIFY_RESPONSE_GET_OBJECTS::request rsp; - if(!m_core.handle_get_objects(arg, rsp, context)) - { - LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"); - m_p2p->drop_connection(context); - } - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() - << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size()); - post_notify(rsp, context); - return 1; - } - //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_GET_OBJECTS"); - if(context.m_last_response_height > arg.current_blockchain_height) - { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height - << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - - updateObservedHeight(arg.current_blockchain_height, context); - - context.m_remote_blockchain_height = arg.current_blockchain_height; - - size_t count = 0; - for (const block_complete_entry& block_entry : arg.blocks) - { - ++count; - Block b; - if(!parse_and_validate_block_from_blob(block_entry.block, b)) - { - LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" - << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - //to avoid concurrency in core between connections, suspend connections which delivered block later then first one - if(count == 2) - { - if(m_core.have_block(get_block_hash(b))) - { - context.m_state = cryptonote_connection_context::state_idle; - context.m_needed_objects.clear(); - context.m_requested_objects.clear(); - LOG_PRINT_CCONTEXT_L1("Connection set to idle state."); - return 1; - } - } - - auto req_it = context.m_requested_objects.find(get_block_hash(b)); - if(req_it == context.m_requested_objects.end()) - { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) - << " wasn't requested, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - if (b.txHashes.size() != block_entry.txs.size()) - { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) - << ", txHashes.size()=" << b.txHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - - context.m_requested_objects.erase(req_it); - } - - if(context.m_requested_objects.size()) - { - LOG_PRINT_CCONTEXT_RED("returned not all requested objects (context.m_requested_objects.size()=" - << context.m_requested_objects.size() << "), dropping connection", LOG_LEVEL_0); - m_p2p->drop_connection(context); - return 1; - } - - { - m_core.pause_mining(); - epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler( - std::bind(&t_core::update_block_template_and_resume_mining, &m_core)); - - for (const block_complete_entry& block_entry : arg.blocks) { - if (m_stop) { - break; - } - - //process transactions - TIME_MEASURE_START(transactions_process_time); - for (auto& tx_blob : block_entry.txs) { - tx_verification_context tvc = AUTO_VAL_INIT(tvc); - m_core.handle_incoming_tx(tx_blob, tvc, true); - if (tvc.m_verifivation_failed) { - LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " - << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - } - TIME_MEASURE_FINISH(transactions_process_time); - - //process block - TIME_MEASURE_START(block_process_time); - block_verification_context bvc = boost::value_initialized(); - m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); - - if (bvc.m_verifivation_failed) { - LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } else if (bvc.m_marked_as_orphaned) { - LOG_PRINT_CCONTEXT_L0("Block received at sync phase was marked as orphaned, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - - TIME_MEASURE_FINISH(block_process_time); - LOG_PRINT_CCONTEXT_L2("Block process time: " << block_process_time + transactions_process_time << - " (" << transactions_process_time << " / " << block_process_time << ") ms"); - } - } - - if (!m_stop) { - request_missing_objects(context, true); - } - - return 1; - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::on_idle() - { - return m_core.on_idle(); - } - //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size()); - NOTIFY_RESPONSE_CHAIN_ENTRY::request r; - if(!m_core.find_blockchain_supplement(arg.block_ids, r)) - { - LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); - return 1; - } - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size()); - post_notify(r, context); - return 1; - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks) - { - if(context.m_needed_objects.size()) - { - //we know objects that we need, request this objects - NOTIFY_REQUEST_GET_OBJECTS::request req; - size_t count = 0; - auto it = context.m_needed_objects.begin(); - - while(it != context.m_needed_objects.end() && count < BLOCKS_SYNCHRONIZING_DEFAULT_COUNT) - { - if( !(check_having_blocks && m_core.have_block(*it))) - { - req.blocks.push_back(*it); - ++count; - context.m_requested_objects.insert(*it); - } - context.m_needed_objects.erase(it++); - } - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size()); - post_notify(req, context); - }else if(context.m_last_response_height < context.m_remote_blockchain_height-1) - {//we have to fetch more objects ids, request blockchain entry - - NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); - LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); - post_notify(r, context); - }else - { - CHECK_AND_ASSERT_MES(context.m_last_response_height == context.m_remote_blockchain_height-1 - && !context.m_needed_objects.size() - && !context.m_requested_objects.size(), false, "request_missing_blocks final condition failed!" - << "\r\nm_last_response_height=" << context.m_last_response_height - << "\r\nm_remote_blockchain_height=" << context.m_remote_blockchain_height - << "\r\nm_needed_objects.size()=" << context.m_needed_objects.size() - << "\r\nm_requested_objects.size()=" << context.m_requested_objects.size() - << "\r\non connection [" << epee::net_utils::print_connection_context_short(context)<< "]"); - - context.m_state = cryptonote_connection_context::state_normal; - LOG_PRINT_CCONTEXT_GREEN(" SYNCHRONIZED OK", LOG_LEVEL_0); - on_connection_synchronized(); - } - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - bool t_cryptonote_protocol_handler::on_connection_synchronized() - { - bool val_expected = false; - if(m_synchronized.compare_exchange_strong(val_expected, true)) - { - LOG_PRINT_L0(ENDL << "**********************************************************************" << ENDL - << "You are now synchronized with the network. You may now start simplewallet." << ENDL - << ENDL - << "Please note, that the blockchain will be saved only after you quit the daemon with \"exit\" command or if you use \"save\" command." << ENDL - << "Otherwise, you will possibly need to synchronize the blockchain again." << ENDL - << ENDL - << "Use \"help\" command to see the list of available commands." << ENDL - << "**********************************************************************"); - m_core.on_synchronized(); - } - return true; - } - //------------------------------------------------------------------------------------------------------------------------ - template - size_t t_cryptonote_protocol_handler::get_synchronizing_connections_count() - { - size_t count = 0; - m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id)->bool{ - if(context.m_state == cryptonote_connection_context::state_synchronizing) - ++count; - return true; - }); - return count; - } - //------------------------------------------------------------------------------------------------------------------------ - template - int t_cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() - << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height); - - if(!arg.m_block_ids.size()) - { - LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - - if(!m_core.have_block(arg.m_block_ids.front())) - { - LOG_ERROR_CCONTEXT("sent m_block_ids starting from unknown id: " - << epee::string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); - m_p2p->drop_connection(context); - return 1; - } - - context.m_remote_blockchain_height = arg.total_height; - context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1; - if(context.m_last_response_height > context.m_remote_blockchain_height) - { - LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height - << "\r\nm_start_height=" << arg.start_height - << "\r\nm_block_ids.size()=" << arg.m_block_ids.size()); - m_p2p->drop_connection(context); - } - - for (auto& bl_id : arg.m_block_ids) { - if(!m_core.have_block(bl_id)) - context.m_needed_objects.push_back(bl_id); - } - - request_missing_objects(context, false); - return 1; - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) - { - relay_post_notify(arg, exclude_context); - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) - { - relay_post_notify(arg, exclude_context); - } - //------------------------------------------------------------------------------------------------------------------------ - template - void t_cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context) { - bool updated = false; - { - std::lock_guard lock(m_observedHeightMutex); - - uint64_t height = m_observedHeight; - if (peerHeight > context.m_remote_blockchain_height) { - m_observedHeight = std::max(m_observedHeight, peerHeight); - if (m_observedHeight != height) { - updated = true; - } - } else if (context.m_remote_blockchain_height == m_observedHeight) { - //the client switched to alternative chain and had maximum observed height. need to recalculate max height - recalculateMaxObservedHeight(context); - if (m_observedHeight != height) { - updated = true; - } - } - } - - if (updated) { - LOG_PRINT_L2("Observed height updated: " << m_observedHeight); - m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); - } - } - - template - void t_cryptonote_protocol_handler::recalculateMaxObservedHeight(const cryptonote_connection_context& context) { - //should be locked outside - uint64_t peerHeight = 0; - m_p2p->for_each_connection([&peerHeight, &context](const cryptonote_connection_context& ctx, nodetool::peerid_type peerId) { - if (ctx.m_connection_id != context.m_connection_id) { - peerHeight = std::max(peerHeight, ctx.m_remote_blockchain_height); - } - return true; - }); - - uint64_t localHeight = 0; - crypto::hash ignore; - m_core.get_blockchain_top(localHeight, ignore); - m_observedHeight = std::max(peerHeight, localHeight); - } - - template - uint64_t t_cryptonote_protocol_handler::getObservedHeight() const { - std::lock_guard lock(m_observedHeightMutex); - return m_observedHeight; - }; - - template - bool t_cryptonote_protocol_handler::addObserver(ICryptonoteProtocolObserver* observer) { - return m_observerManager.add(observer); - } - - template - bool t_cryptonote_protocol_handler::removeObserver(ICryptonoteProtocolObserver* observer) { - return m_observerManager.remove(observer); - } -}; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h index 7e24444ea6..e6b1008915 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,10 +17,7 @@ #pragma once -#include "p2p/net_node_common.h" -#include "cryptonote_core/connection_context.h" - -namespace cryptonote +namespace CryptoNote { struct NOTIFY_NEW_BLOCK_request; struct NOTIFY_NEW_TRANSACTIONS_request; @@ -29,19 +26,15 @@ namespace cryptonote /* */ /************************************************************************/ struct i_cryptonote_protocol { - virtual void relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context)=0; - virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context)=0; - //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; + virtual void relay_block(NOTIFY_NEW_BLOCK_request& arg) = 0; + virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg) = 0; }; /************************************************************************/ /* */ /************************************************************************/ struct cryptonote_protocol_stub: public i_cryptonote_protocol { - virtual void relay_block(NOTIFY_NEW_BLOCK_request& arg, cryptonote_connection_context& exclude_context) override { - } - - virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg, cryptonote_connection_context& exclude_context) override { - } + virtual void relay_block(NOTIFY_NEW_BLOCK_request& arg) override {} + virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS_request& arg) override {} }; } diff --git a/src/daemon/DaemonCommandsHandler.h b/src/daemon/DaemonCommandsHandler.h new file mode 100644 index 0000000000..bd483cb07d --- /dev/null +++ b/src/daemon/DaemonCommandsHandler.h @@ -0,0 +1,72 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "Common/ConsoleHandler.h" + +#include +#include + +namespace CryptoNote { +class core; +class node_server; +} + +class DaemonCommandsHandler +{ +public: + DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::node_server& srv, Logging::LoggerManager& log); + + bool start_handling() { + m_consoleHandler.start(); + return true; + } + + void stop_handling() { + m_consoleHandler.stop(); + } + +private: + + Common::ConsoleHandler m_consoleHandler; + CryptoNote::core& m_core; + CryptoNote::node_server& m_srv; + Logging::LoggerRef logger; + Logging::LoggerManager& m_logManager; + + std::string get_commands_str(); + bool print_block_by_height(uint64_t height); + bool print_block_by_hash(const std::string& arg); + + bool exit(const std::vector& args); + bool help(const std::vector& args); + bool print_pl(const std::vector& args); + bool show_hr(const std::vector& args); + bool hide_hr(const std::vector& args); + bool print_bc_outs(const std::vector& args); + bool print_cn(const std::vector& args); + bool print_bc(const std::vector& args); + bool print_bci(const std::vector& args); + bool set_log(const std::vector& args); + bool print_block(const std::vector& args); + bool print_tx(const std::vector& args); + bool print_pool(const std::vector& args); + bool print_pool_sh(const std::vector& args); + bool start_mining(const std::vector& args); + bool stop_mining(const std::vector& args); +}; diff --git a/src/daemon/DeamonCommandsHandler.cpp b/src/daemon/DeamonCommandsHandler.cpp new file mode 100644 index 0000000000..3f81544bfb --- /dev/null +++ b/src/daemon/DeamonCommandsHandler.cpp @@ -0,0 +1,320 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "DaemonCommandsHandler.h" + +#include "p2p/net_node.h" +#include "cryptonote_core/miner.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" + +#include "version.h" + +namespace { + template + static bool print_as_json(const T& obj) { + std::cout << CryptoNote::obj_to_json_str(obj) << ENDL; + return true; + } +} + + +DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::node_server& srv, Logging::LoggerManager& log) : + m_core(core), m_srv(srv), logger(log, "daemon"), m_logManager(log) { + m_consoleHandler.setHandler("exit", boost::bind(&DaemonCommandsHandler::exit, this, _1), "Shutdown the daemon"); + m_consoleHandler.setHandler("help", boost::bind(&DaemonCommandsHandler::help, this, _1), "Show this help"); + m_consoleHandler.setHandler("print_pl", boost::bind(&DaemonCommandsHandler::print_pl, this, _1), "Print peer list"); + m_consoleHandler.setHandler("print_cn", boost::bind(&DaemonCommandsHandler::print_cn, this, _1), "Print connections"); + m_consoleHandler.setHandler("print_bc", boost::bind(&DaemonCommandsHandler::print_bc, this, _1), "Print blockchain info in a given blocks range, print_bc []"); + //m_consoleHandler.setHandler("print_bci", boost::bind(&DaemonCommandsHandler::print_bci, this, _1)); + //m_consoleHandler.setHandler("print_bc_outs", boost::bind(&DaemonCommandsHandler::print_bc_outs, this, _1)); + m_consoleHandler.setHandler("print_block", boost::bind(&DaemonCommandsHandler::print_block, this, _1), "Print block, print_block | "); + m_consoleHandler.setHandler("print_tx", boost::bind(&DaemonCommandsHandler::print_tx, this, _1), "Print transaction, print_tx "); + m_consoleHandler.setHandler("start_mining", boost::bind(&DaemonCommandsHandler::start_mining, this, _1), "Start mining for specified address, start_mining [threads=1]"); + m_consoleHandler.setHandler("stop_mining", boost::bind(&DaemonCommandsHandler::stop_mining, this, _1), "Stop mining"); + m_consoleHandler.setHandler("print_pool", boost::bind(&DaemonCommandsHandler::print_pool, this, _1), "Print transaction pool (long format)"); + m_consoleHandler.setHandler("print_pool_sh", boost::bind(&DaemonCommandsHandler::print_pool_sh, this, _1), "Print transaction pool (short format)"); + m_consoleHandler.setHandler("show_hr", boost::bind(&DaemonCommandsHandler::show_hr, this, _1), "Start showing hash rate"); + m_consoleHandler.setHandler("hide_hr", boost::bind(&DaemonCommandsHandler::hide_hr, this, _1), "Stop showing hash rate"); + m_consoleHandler.setHandler("set_log", boost::bind(&DaemonCommandsHandler::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); +} + +//-------------------------------------------------------------------------------- +std::string DaemonCommandsHandler::get_commands_str() +{ + std::stringstream ss; + ss << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; + ss << "Commands: " << ENDL; + std::string usage = m_consoleHandler.getUsage(); + boost::replace_all(usage, "\n", "\n "); + usage.insert(0, " "); + ss << usage << ENDL; + return ss.str(); +} + +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::exit(const std::vector& args) { + m_consoleHandler.requestStop(); + m_srv.send_stop_signal(); + return true; +} + +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::help(const std::vector& args) { + std::cout << get_commands_str() << ENDL; + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_pl(const std::vector& args) { + m_srv.log_peerlist(); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::show_hr(const std::vector& args) +{ + if (!m_core.get_miner().is_mining()) + { + std::cout << "Mining is not started. You need start mining before you can see hash rate." << ENDL; + } else + { + m_core.get_miner().do_print_hashrate(true); + } + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::hide_hr(const std::vector& args) +{ + m_core.get_miner().do_print_hashrate(false); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_bc_outs(const std::vector& args) +{ + if (args.size() != 1) + { + std::cout << "need file path as parameter" << ENDL; + return true; + } + m_core.print_blockchain_outs(args[0]); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_cn(const std::vector& args) +{ + m_srv.get_payload_object().log_connections(); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_bc(const std::vector &args) { + if (!args.size()) { + std::cout << "need block index parameter" << ENDL; + return false; + } + + uint64_t start_index = 0; + uint64_t end_index = 0; + uint64_t end_block_parametr = m_core.get_current_blockchain_height(); + if (!Common::fromString(args[0], start_index)) { + std::cout << "wrong starter block index parameter" << ENDL; + return false; + } + + if (args.size() > 1 && !Common::fromString(args[1], end_index)) { + std::cout << "wrong end block index parameter" << ENDL; + return false; + } + + if (end_index == 0) { + end_index = end_block_parametr; + } + + if (end_index > end_block_parametr) { + std::cout << "end block index parameter shouldn't be greater than " << end_block_parametr << ENDL; + return false; + } + + if (end_index <= start_index) { + std::cout << "end block index should be greater than starter block index" << ENDL; + return false; + } + + m_core.print_blockchain(start_index, end_index); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_bci(const std::vector& args) +{ + m_core.print_blockchain_index(); + return true; +} + +bool DaemonCommandsHandler::set_log(const std::vector& args) +{ + if (args.size() != 1) { + std::cout << "use: set_log " << ENDL; + return true; + } + + uint16_t l = 0; + if (!Common::fromString(args[0], l)) { + std::cout << "wrong number format, use: set_log " << ENDL; + return true; + } + + ++l; + + if (l > Logging::TRACE) { + std::cout << "wrong number range, use: set_log " << ENDL; + return true; + } + + m_logManager.setMaxLevel(static_cast(l)); + return true; +} + +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_block_by_height(uint64_t height) +{ + std::list blocks; + m_core.get_blocks(height, 1, blocks); + + if (1 == blocks.size()) { + std::cout << "block_id: " << get_block_hash(blocks.front()) << ENDL; + print_as_json(blocks.front()); + } else { + uint64_t current_height; + crypto::hash top_id; + m_core.get_blockchain_top(current_height, top_id); + std::cout << "block wasn't found. Current block chain height: " << current_height << ", requested: " << height << std::endl; + return false; + } + + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_block_by_hash(const std::string& arg) +{ + crypto::hash block_hash; + if (!parse_hash256(arg, block_hash)) { + return false; + } + + std::list block_ids; + block_ids.push_back(block_hash); + std::list blocks; + std::list missed_ids; + m_core.get_blocks(block_ids, blocks, missed_ids); + + if (1 == blocks.size()) + { + print_as_json(blocks.front()); + } else + { + std::cout << "block wasn't found: " << arg << std::endl; + return false; + } + + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_block(const std::vector &args) { + if (args.empty()) { + std::cout << "expected: print_block ( | )" << std::endl; + return true; + } + + const std::string &arg = args.front(); + try { + uint64_t height = boost::lexical_cast(arg); + print_block_by_height(height); + } catch (boost::bad_lexical_cast &) { + print_block_by_hash(arg); + } + + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_tx(const std::vector& args) +{ + if (args.empty()) { + std::cout << "expected: print_tx " << std::endl; + return true; + } + + const std::string &str_hash = args.front(); + crypto::hash tx_hash; + if (!parse_hash256(str_hash, tx_hash)) { + return true; + } + + std::vector tx_ids; + tx_ids.push_back(tx_hash); + std::list txs; + std::list missed_ids; + m_core.get_transactions(tx_ids, txs, missed_ids); + + if (1 == txs.size()) { + print_as_json(txs.front()); + } else { + std::cout << "transaction wasn't found: <" << str_hash << '>' << std::endl; + } + + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_pool(const std::vector& args) +{ + logger(Logging::INFO) << "Pool state: " << ENDL << m_core.print_pool(false); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::print_pool_sh(const std::vector& args) +{ + logger(Logging::INFO) << "Pool state: " << ENDL << m_core.print_pool(true); + return true; +} +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::start_mining(const std::vector &args) { + if (!args.size()) { + std::cout << "Please, specify wallet address to mine for: start_mining [threads=1]" << std::endl; + return true; + } + + CryptoNote::AccountPublicAddress adr; + if (!m_core.currency().parseAccountAddressString(args.front(), adr)) { + std::cout << "target account address has wrong format" << std::endl; + return true; + } + + size_t threads_count = 1; + if (args.size() > 1) { + bool ok = Common::fromString(args[1], threads_count); + threads_count = (ok && 0 < threads_count) ? threads_count : 1; + } + + boost::thread::attributes attrs; + attrs.set_stack_size(CryptoNote::THREAD_STACK_SIZE); + + m_core.get_miner().start(adr, threads_count, attrs); + return true; +} + +//-------------------------------------------------------------------------------- +bool DaemonCommandsHandler::stop_mining(const std::vector& args) { + m_core.get_miner().stop(); + return true; +} diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 52f765db82..697f20e7d0 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,270 +15,281 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// node.cpp : Defines the entry point for the console application. -// - - -#include "include_base_utils.h" #include "version.h" -using namespace epee; - #include -// epee -#include "console_handler.h" +#include "DaemonCommandsHandler.h" -#include "common/SignalHandler.h" +#include "Common/SignalHandler.h" +#include "Common/PathTools.h" #include "crypto/hash.h" #include "cryptonote_core/cryptonote_core.h" #include "cryptonote_core/CoreConfig.h" #include "cryptonote_core/Currency.h" #include "cryptonote_core/MinerConfig.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "daemon/daemon_commands_handler.h" #include "p2p/net_node.h" #include "p2p/NetNodeConfig.h" -#include "rpc/core_rpc_server.h" +#include "rpc/RpcServer.h" +#include "rpc/RpcServerConfig.h" #include "version.h" +#include + #if defined(WIN32) #include #endif +using Common::JsonValue; +using namespace CryptoNote; +using namespace Logging; + namespace po = boost::program_options; namespace { - const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(cryptonote::CRYPTONOTE_NAME) + ".conf"}; + const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(CryptoNote::CRYPTONOTE_NAME) + ".conf"}; const command_line::arg_descriptor arg_os_version = {"os-version", ""}; const command_line::arg_descriptor arg_log_file = {"log-file", "", ""}; - const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; + const command_line::arg_descriptor arg_log_level = {"log-level", "", 2}; // info level const command_line::arg_descriptor arg_console = {"no-console", "Disable daemon console commands"}; const command_line::arg_descriptor arg_testnet_on = {"testnet", "Used to deploy test nets. Checkpoints and hardcoded seeds are ignored, " "network id is changed. Use it with --data-dir flag. The wallet must be launched with --testnet flag.", false}; } -bool command_line_preprocessor(const boost::program_options::variables_map& vm); +bool command_line_preprocessor(const boost::program_options::variables_map& vm, LoggerRef& logger); + +JsonValue buildLoggerConfiguration(Level level, const std::string& logfile) { + JsonValue loggerConfiguration(JsonValue::OBJECT); + loggerConfiguration.insert("globalLevel", static_cast(level)); + + JsonValue& cfgLoggers = loggerConfiguration.insert("loggers", JsonValue::ARRAY); + + JsonValue& fileLogger = cfgLoggers.pushBack(JsonValue::OBJECT); + fileLogger.insert("type", "file"); + fileLogger.insert("filename", logfile); + fileLogger.insert("level", static_cast(TRACE)); + + JsonValue& consoleLogger = cfgLoggers.pushBack(JsonValue::OBJECT); + consoleLogger.insert("type", "console"); + consoleLogger.insert("level", static_cast(TRACE)); + consoleLogger.insert("pattern", "%T %L "); + + return loggerConfiguration; +} + int main(int argc, char* argv[]) { - string_tools::set_module_name_and_folder(argv[0]); #ifdef WIN32 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - LOG_PRINT_L0("Starting..."); - - TRY_ENTRY(); - po::options_description desc_cmd_only("Command line options"); - po::options_description desc_cmd_sett("Command line options and settings options"); + LoggerManager logManager; + LoggerRef logger(logManager, "daemon"); - command_line::add_arg(desc_cmd_only, command_line::arg_help); - command_line::add_arg(desc_cmd_only, command_line::arg_version); - command_line::add_arg(desc_cmd_only, arg_os_version); - // tools::get_default_data_dir() can't be called during static initialization - command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, tools::get_default_data_dir()); - command_line::add_arg(desc_cmd_only, arg_config_file); + try { - command_line::add_arg(desc_cmd_sett, arg_log_file); - command_line::add_arg(desc_cmd_sett, arg_log_level); - command_line::add_arg(desc_cmd_sett, arg_console); - command_line::add_arg(desc_cmd_sett, arg_testnet_on); + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); - cryptonote::core_rpc_server::init_options(desc_cmd_sett); + command_line::add_arg(desc_cmd_only, command_line::arg_help); + command_line::add_arg(desc_cmd_only, command_line::arg_version); + command_line::add_arg(desc_cmd_only, arg_os_version); + // tools::get_default_data_dir() can't be called during static initialization + command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc_cmd_only, arg_config_file); - cryptonote::CoreConfig::initOptions(desc_cmd_sett); - nodetool::NetNodeConfig::initOptions(desc_cmd_sett); - cryptonote::MinerConfig::initOptions(desc_cmd_sett); + command_line::add_arg(desc_cmd_sett, arg_log_file); + command_line::add_arg(desc_cmd_sett, arg_log_level); + command_line::add_arg(desc_cmd_sett, arg_console); + command_line::add_arg(desc_cmd_sett, arg_testnet_on); - po::options_description desc_options("Allowed options"); - desc_options.add(desc_cmd_only).add(desc_cmd_sett); + RpcServerConfig::initOptions(desc_cmd_sett); + CoreConfig::initOptions(desc_cmd_sett); + NetNodeConfig::initOptions(desc_cmd_sett); + MinerConfig::initOptions(desc_cmd_sett); - po::variables_map vm; - bool r = command_line::handle_error_helper(desc_options, [&]() - { - po::store(po::parse_command_line(argc, argv, desc_options), vm); + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); - if (command_line::get_arg(vm, command_line::arg_help)) + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() { - std::cout << cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; - std::cout << desc_options << std::endl; - return false; + po::store(po::parse_command_line(argc, argv, desc_options), vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; + std::cout << desc_options << std::endl; + return false; + } + + std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir); + std::string config = command_line::get_arg(vm, arg_config_file); + + boost::filesystem::path data_dir_path(data_dir); + boost::filesystem::path config_path(config); + if (!config_path.has_parent_path()) { + config_path = data_dir_path / config_path; + } + + boost::system::error_code ec; + if (boost::filesystem::exists(config_path, ec)) { + po::store(po::parse_config_file(config_path.string().c_str(), desc_cmd_sett), vm); + } + po::notify(vm); + return true; + }); + + if (!r) + return 1; + + auto modulePath = Common::NativePathToGeneric(argv[0]); + auto cfgLogFile = Common::NativePathToGeneric(command_line::get_arg(vm, arg_log_file)); + + if (cfgLogFile.empty()) { + cfgLogFile = Common::ReplaceExtenstion(modulePath, ".log"); + } else { + if (!Common::HasParentPath(cfgLogFile)) { + cfgLogFile = Common::CombinePath(Common::GetPathDirectory(modulePath), cfgLogFile); + } } - std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir); - std::string config = command_line::get_arg(vm, arg_config_file); + Level cfgLogLevel = static_cast(static_cast(Logging::ERROR) + command_line::get_arg(vm, arg_log_level)); - boost::filesystem::path data_dir_path(data_dir); - boost::filesystem::path config_path(config); - if (!config_path.has_parent_path()) - { - config_path = data_dir_path / config_path; - } + // configure logging + logManager.configure(buildLoggerConfiguration(cfgLogLevel, cfgLogFile)); - boost::system::error_code ec; - if (boost::filesystem::exists(config_path, ec)) - { - po::store(po::parse_config_file(config_path.string().c_str(), desc_cmd_sett), vm); - } - po::notify(vm); + logger(INFO) << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG; - return true; - }); - if (!r) - return 1; + if (command_line_preprocessor(vm, logger)) { + return 0; + } - //set up logging options - boost::filesystem::path log_file_path(command_line::get_arg(vm, arg_log_file)); - if (log_file_path.empty()) - log_file_path = log_space::log_singletone::get_default_log_file(); - std::string log_dir; - log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder(); + logger(INFO) << "Module folder: " << argv[0]; - log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str()); - LOG_PRINT_L0(cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG); + bool testnet_mode = command_line::get_arg(vm, arg_testnet_on); + if (testnet_mode) { + logger(INFO) << "Starting in testnet mode!"; + } - if (command_line_preprocessor(vm)) - { - return 0; - } + //create objects and link them + CryptoNote::CurrencyBuilder currencyBuilder(logManager); + currencyBuilder.testnet(testnet_mode); + CryptoNote::Currency currency = currencyBuilder.currency(); + CryptoNote::core ccore(currency, nullptr, logManager); - LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0); + CryptoNote::checkpoints checkpoints(logManager); + for (const auto& cp : CryptoNote::CHECKPOINTS) { + checkpoints.add_checkpoint(cp.height, cp.blockId); + } - bool testnet_mode = command_line::get_arg(vm, arg_testnet_on); - if (testnet_mode) { - LOG_PRINT_L0("Starting in testnet mode!"); - } + if (!testnet_mode) { + ccore.set_checkpoints(std::move(checkpoints)); + } - //create objects and link them - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.testnet(testnet_mode); - cryptonote::Currency currency = currencyBuilder.currency(); - cryptonote::core ccore(currency, NULL); + CoreConfig coreConfig; + coreConfig.init(vm); + NetNodeConfig netNodeConfig; + netNodeConfig.init(vm); + MinerConfig minerConfig; + minerConfig.init(vm); + RpcServerConfig rpcConfig; + rpcConfig.init(vm); + + System::Dispatcher dispatcher; + + CryptoNote::cryptonote_protocol_handler cprotocol(currency, dispatcher, ccore, nullptr, logManager); + CryptoNote::node_server p2psrv(dispatcher, cprotocol, logManager); + CryptoNote::RpcServer rpcServer(dispatcher, logManager, ccore, p2psrv); + + cprotocol.set_p2p_endpoint(&p2psrv); + ccore.set_cryptonote_protocol(&cprotocol); + DaemonCommandsHandler dch(ccore, p2psrv, logManager); + + // initialize objects + logger(INFO) << "Initializing p2p server..."; + if (!p2psrv.init(netNodeConfig, testnet_mode)) { + logger(ERROR, BRIGHT_RED) << "Failed to initialize p2p server."; + return 1; + } + logger(INFO) << "P2p server initialized OK"; + + //logger(INFO) << "Initializing core rpc server..."; + //if (!rpc_server.init(vm)) { + // logger(ERROR, BRIGHT_RED) << "Failed to initialize core rpc server."; + // return 1; + //} + // logger(INFO, BRIGHT_GREEN) << "Core rpc server initialized OK on port: " << rpc_server.get_binded_port(); + + // initialize core here + logger(INFO) << "Initializing core..."; + if (!ccore.init(coreConfig, minerConfig, true)) { + logger(ERROR, BRIGHT_RED) << "Failed to initialize core"; + return 1; + } + logger(INFO) << "Core initialized OK"; - cryptonote::checkpoints checkpoints; - for (const auto& cp : cryptonote::CHECKPOINTS) { - checkpoints.add_checkpoint(cp.height, cp.blockId); - } + // start components + if (!command_line::has_arg(vm, arg_console)) { + dch.start_handling(); + } - if (!testnet_mode) { - ccore.set_checkpoints(std::move(checkpoints)); - } + logger(INFO) << "Starting core rpc server on address " << rpcConfig.getBindAddress(); + rpcServer.start(rpcConfig.bindIp, rpcConfig.bindPort); + logger(INFO) << "Core rpc server started ok"; - cryptonote::CoreConfig coreConfig; - coreConfig.init(vm); - nodetool::NetNodeConfig netNodeConfig; - netNodeConfig.init(vm); - cryptonote::MinerConfig minerConfig; - minerConfig.init(vm); - - cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); - nodetool::node_server > p2psrv(cprotocol); - cryptonote::core_rpc_server rpc_server(ccore, p2psrv); - cprotocol.set_p2p_endpoint(&p2psrv); - ccore.set_cryptonote_protocol(&cprotocol); - daemon_cmmands_handler dch(p2psrv); - - //initialize objects - LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(netNodeConfig, testnet_mode); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); - LOG_PRINT_L0("P2p server initialized OK"); - - LOG_PRINT_L0("Initializing cryptonote protocol..."); - res = cprotocol.init(); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol."); - LOG_PRINT_L0("Cryptonote protocol initialized OK"); - - LOG_PRINT_L0("Initializing core rpc server..."); - res = rpc_server.init(vm); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); - LOG_PRINT_GREEN("Core rpc server initialized OK on port: " << rpc_server.get_binded_port(), LOG_LEVEL_0); - - //initialize core here - LOG_PRINT_L0("Initializing core..."); - res = ccore.init(coreConfig, minerConfig, true); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); - LOG_PRINT_L0("Core initialized OK"); - - // start components - if(!command_line::has_arg(vm, arg_console)) - { - dch.start_handling(); - } + tools::SignalHandler::install([&dch, &p2psrv] { + dch.stop_handling(); + p2psrv.send_stop_signal(); + }); - LOG_PRINT_L0("Starting core rpc server..."); - res = rpc_server.run(2, false); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); - LOG_PRINT_L0("Core rpc server started ok"); + logger(INFO) << "Starting p2p net loop..."; + p2psrv.run(); + logger(INFO) << "p2p net loop stopped"; - tools::SignalHandler::install([&dch, &p2psrv] { dch.stop_handling(); - p2psrv.send_stop_signal(); - }); - - LOG_PRINT_L0("Starting p2p net loop..."); - p2psrv.run(); - LOG_PRINT_L0("p2p net loop stopped"); - //stop components - LOG_PRINT_L0("Stopping core rpc server..."); - rpc_server.send_stop_signal(); - rpc_server.timed_wait_server_stop(5000); + //stop components + logger(INFO) << "Stopping core rpc server..."; + rpcServer.stop(); - //deinitialize components - LOG_PRINT_L0("Deinitializing core..."); - ccore.deinit(); - LOG_PRINT_L0("Deinitializing rpc server ..."); - rpc_server.deinit(); - LOG_PRINT_L0("Deinitializing cryptonote_protocol..."); - cprotocol.deinit(); - LOG_PRINT_L0("Deinitializing p2p..."); - p2psrv.deinit(); + //deinitialize components + logger(INFO) << "Deinitializing core..."; + ccore.deinit(); + logger(INFO) << "Deinitializing p2p..."; + p2psrv.deinit(); + ccore.set_cryptonote_protocol(NULL); + cprotocol.set_p2p_endpoint(NULL); - ccore.set_cryptonote_protocol(NULL); - cprotocol.set_p2p_endpoint(NULL); + } catch (const std::exception& e) { + logger(ERROR, BRIGHT_RED) << "Exception: " << e.what(); + return 1; + } - LOG_PRINT("Node stopped.", LOG_LEVEL_0); + logger(INFO) << "Node stopped."; return 0; - - CATCH_ENTRY_L0("main", 1); } -bool command_line_preprocessor(const boost::program_options::variables_map& vm) -{ +bool command_line_preprocessor(const boost::program_options::variables_map &vm, LoggerRef &logger) { bool exit = false; - if (command_line::get_arg(vm, command_line::arg_version)) - { - std::cout << cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; + + if (command_line::get_arg(vm, command_line::arg_version)) { + std::cout << CryptoNote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; exit = true; } - if (command_line::get_arg(vm, arg_os_version)) - { + if (command_line::get_arg(vm, arg_os_version)) { std::cout << "OS: " << tools::get_os_version_string() << ENDL; exit = true; } - if (exit) - { + if (exit) { return true; } - int new_log_level = command_line::get_arg(vm, arg_log_level); - if(new_log_level < LOG_LEVEL_MIN || new_log_level > LOG_LEVEL_MAX) - { - LOG_PRINT_L0("Wrong log level value: "); - } - else if (log_space::get_set_log_detalisation_level(false) != new_log_level) - { - log_space::get_set_log_detalisation_level(true, new_log_level); - LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level); - } - return false; } diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h deleted file mode 100644 index 837839aafe..0000000000 --- a/src/daemon/daemon_commands_handler.h +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include "console_handler.h" -#include "p2p/net_node.h" -#include "cryptonote_core/miner.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "common/util.h" -#include "crypto/hash.h" -#include "version.h" - - -class daemon_cmmands_handler -{ - nodetool::node_server >& m_srv; -public: - daemon_cmmands_handler(nodetool::node_server >& srv):m_srv(srv) - { - m_cmd_binder.set_handler("help", boost::bind(&daemon_cmmands_handler::help, this, _1), "Show this help"); - m_cmd_binder.set_handler("print_pl", boost::bind(&daemon_cmmands_handler::print_pl, this, _1), "Print peer list"); - m_cmd_binder.set_handler("print_cn", boost::bind(&daemon_cmmands_handler::print_cn, this, _1), "Print connections"); - m_cmd_binder.set_handler("print_bc", boost::bind(&daemon_cmmands_handler::print_bc, this, _1), "Print blockchain info in a given blocks range, print_bc []"); - //m_cmd_binder.set_handler("print_bci", boost::bind(&daemon_cmmands_handler::print_bci, this, _1)); - //m_cmd_binder.set_handler("print_bc_outs", boost::bind(&daemon_cmmands_handler::print_bc_outs, this, _1)); - m_cmd_binder.set_handler("print_block", boost::bind(&daemon_cmmands_handler::print_block, this, _1), "Print block, print_block | "); - m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_cmmands_handler::print_tx, this, _1), "Print transaction, print_tx "); - m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_cmmands_handler::start_mining, this, _1), "Start mining for specified address, start_mining [threads=1]"); - m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_cmmands_handler::stop_mining, this, _1), "Stop mining"); - m_cmd_binder.set_handler("print_pool", boost::bind(&daemon_cmmands_handler::print_pool, this, _1), "Print transaction pool (long format)"); - m_cmd_binder.set_handler("print_pool_sh", boost::bind(&daemon_cmmands_handler::print_pool_sh, this, _1), "Print transaction pool (short format)"); - m_cmd_binder.set_handler("show_hr", boost::bind(&daemon_cmmands_handler::show_hr, this, _1), "Start showing hash rate"); - m_cmd_binder.set_handler("hide_hr", boost::bind(&daemon_cmmands_handler::hide_hr, this, _1), "Stop showing hash rate"); - m_cmd_binder.set_handler("set_log", boost::bind(&daemon_cmmands_handler::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); - } - - bool start_handling() - { - m_cmd_binder.start_handling(&m_srv, "", ""); - return true; - } - - void stop_handling() - { - m_cmd_binder.stop_handling(); - } - -private: - epee::srv_console_handlers_binder > > m_cmd_binder; - - //-------------------------------------------------------------------------------- - std::string get_commands_str() - { - std::stringstream ss; - ss << cryptonote::CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL; - ss << "Commands: " << ENDL; - std::string usage = m_cmd_binder.get_usage(); - boost::replace_all(usage, "\n", "\n "); - usage.insert(0, " "); - ss << usage << ENDL; - return ss.str(); - } - //-------------------------------------------------------------------------------- - bool help(const std::vector& args) - { - std::cout << get_commands_str() << ENDL; - return true; - } - //-------------------------------------------------------------------------------- - bool print_pl(const std::vector& args) - { - m_srv.log_peerlist(); - return true; - } - //-------------------------------------------------------------------------------- - bool show_hr(const std::vector& args) - { - if(!m_srv.get_payload_object().get_core().get_miner().is_mining()) - { - std::cout << "Mining is not started. You need start mining before you can see hash rate." << ENDL; - } else - { - m_srv.get_payload_object().get_core().get_miner().do_print_hashrate(true); - } - return true; - } - //-------------------------------------------------------------------------------- - bool hide_hr(const std::vector& args) - { - m_srv.get_payload_object().get_core().get_miner().do_print_hashrate(false); - return true; - } - //-------------------------------------------------------------------------------- - bool print_bc_outs(const std::vector& args) - { - if(args.size() != 1) - { - std::cout << "need file path as parameter" << ENDL; - return true; - } - m_srv.get_payload_object().get_core().print_blockchain_outs(args[0]); - return true; - } - //-------------------------------------------------------------------------------- - bool print_cn(const std::vector& args) - { - m_srv.get_payload_object().log_connections(); - return true; - } - //-------------------------------------------------------------------------------- - bool print_bc(const std::vector& args) - { - if(!args.size()) - { - std::cout << "need block index parameter" << ENDL; - return false; - } - uint64_t start_index = 0; - uint64_t end_index = 0; - uint64_t end_block_parametr = m_srv.get_payload_object().get_core().get_current_blockchain_height(); - if(!string_tools::get_xtype_from_string(start_index, args[0])) - { - std::cout << "wrong starter block index parameter" << ENDL; - return false; - } - if(args.size() >1 && !string_tools::get_xtype_from_string(end_index, args[1])) - { - std::cout << "wrong end block index parameter" << ENDL; - return false; - } - if (end_index == 0) - { - end_index = end_block_parametr; - } - if (end_index > end_block_parametr) - { - std::cout << "end block index parameter shouldn't be greater than " << end_block_parametr << ENDL; - return false; - } - if (end_index <= start_index) - { - std::cout << "end block index should be greater than starter block index" << ENDL; - return false; - } - - m_srv.get_payload_object().get_core().print_blockchain(start_index, end_index); - return true; - } - //-------------------------------------------------------------------------------- - bool print_bci(const std::vector& args) - { - m_srv.get_payload_object().get_core().print_blockchain_index(); - return true; - } - - bool set_log(const std::vector& args) - { - if(args.size() != 1) - { - std::cout << "use: set_log " << ENDL; - return true; - } - - uint16_t l = 0; - if(!string_tools::get_xtype_from_string(l, args[0])) - { - std::cout << "wrong number format, use: set_log " << ENDL; - return true; - } - - if(LOG_LEVEL_4 < l) - { - std::cout << "wrong number range, use: set_log " << ENDL; - return true; - } - - log_space::log_singletone::get_set_log_detalisation_level(true, l); - - return true; - } - - //-------------------------------------------------------------------------------- - template - static bool print_as_json(const T& obj) { - std::cout << cryptonote::obj_to_json_str(obj) << ENDL; - return true; - } - //-------------------------------------------------------------------------------- - bool print_block_by_height(uint64_t height) - { - std::list blocks; - m_srv.get_payload_object().get_core().get_blocks(height, 1, blocks); - - if (1 == blocks.size()) - { - std::cout << "block_id: " << get_block_hash(blocks.front()) << ENDL; - print_as_json(blocks.front()); - } - else - { - uint64_t current_height; - crypto::hash top_id; - m_srv.get_payload_object().get_core().get_blockchain_top(current_height, top_id); - std::cout << "block wasn't found. Current block chain height: " << current_height << ", requested: " << height << std::endl; - return false; - } - - return true; - } - //-------------------------------------------------------------------------------- - bool print_block_by_hash(const std::string& arg) - { - crypto::hash block_hash; - if (!parse_hash256(arg, block_hash)) - { - return false; - } - - std::list block_ids; - block_ids.push_back(block_hash); - std::list blocks; - std::list missed_ids; - m_srv.get_payload_object().get_core().get_blocks(block_ids, blocks, missed_ids); - - if (1 == blocks.size()) - { - print_as_json(blocks.front()); - } - else - { - std::cout << "block wasn't found: " << arg << std::endl; - return false; - } - - return true; - } - //-------------------------------------------------------------------------------- - bool print_block(const std::vector& args) - { - if (args.empty()) - { - std::cout << "expected: print_block ( | )" << std::endl; - return true; - } - - const std::string& arg = args.front(); - try - { - uint64_t height = boost::lexical_cast(arg); - print_block_by_height(height); - } - catch (boost::bad_lexical_cast&) - { - print_block_by_hash(arg); - } - - return true; - } - //-------------------------------------------------------------------------------- - bool print_tx(const std::vector& args) - { - if (args.empty()) - { - std::cout << "expected: print_tx " << std::endl; - return true; - } - - const std::string& str_hash = args.front(); - crypto::hash tx_hash; - if (!parse_hash256(str_hash, tx_hash)) - { - return true; - } - - std::vector tx_ids; - tx_ids.push_back(tx_hash); - std::list txs; - std::list missed_ids; - m_srv.get_payload_object().get_core().get_transactions(tx_ids, txs, missed_ids); - - if (1 == txs.size()) - { - print_as_json(txs.front()); - } - else - { - std::cout << "transaction wasn't found: <" << str_hash << '>' << std::endl; - } - - return true; - } - //-------------------------------------------------------------------------------- - bool print_pool(const std::vector& args) - { - LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(false)); - return true; - } - //-------------------------------------------------------------------------------- - bool print_pool_sh(const std::vector& args) - { - LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(true)); - return true; - } //-------------------------------------------------------------------------------- - bool start_mining(const std::vector& args) - { - if(!args.size()) - { - std::cout << "Please, specify wallet address to mine for: start_mining [threads=1]" << std::endl; - return true; - } - - cryptonote::AccountPublicAddress adr; - if(!m_srv.get_payload_object().get_core().currency().parseAccountAddressString(args.front(), adr)) - { - std::cout << "target account address has wrong format" << std::endl; - return true; - } - size_t threads_count = 1; - if(args.size() > 1) - { - bool ok = string_tools::get_xtype_from_string(threads_count, args[1]); - threads_count = (ok && 0 < threads_count) ? threads_count : 1; - } - - boost::thread::attributes attrs; - attrs.set_stack_size(cryptonote::THREAD_STACK_SIZE); - - m_srv.get_payload_object().get_core().get_miner().start(adr, threads_count, attrs); - return true; - } - //-------------------------------------------------------------------------------- - bool stop_mining(const std::vector& args) - { - m_srv.get_payload_object().get_core().get_miner().stop(); - return true; - } -}; diff --git a/src/logger/CommonLogger.cpp b/src/logger/CommonLogger.cpp deleted file mode 100755 index eb1eb9b622..0000000000 --- a/src/logger/CommonLogger.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "CommonLogger.h" - -#include - -using namespace Log; - -CommonLogger::CommonLogger(ILogger::Level level) : logLevel(level) { -} - -void CommonLogger::doLogString(Level level, boost::posix_time::ptime time, const std::string& message) { -} - -void CommonLogger::enableCategory(const std::string& category) { - disabledCategories.erase(category); -} - -void CommonLogger::disableCategory(const std::string& category) { - disabledCategories.insert(category); -} - -void CommonLogger::setMaxLevel(Level level) { - logLevel = level; -} - -void CommonLogger::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { - if (level > logLevel) { - return; - } - - if (disabledCategories.count(category) != 0) { - return; - } - - doLogString(level, time, body); -} diff --git a/src/logger/CommonLogger.h b/src/logger/CommonLogger.h deleted file mode 100755 index 2643c29805..0000000000 --- a/src/logger/CommonLogger.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include "ILogger.h" - -namespace Log { - -class CommonLogger : public ILogger { -public: - virtual void enableCategory(const std::string& category) override; - virtual void disableCategory(const std::string& category) override; - virtual void setMaxLevel(Level level) override; - virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; - -protected: - std::set disabledCategories; - Level logLevel; - - CommonLogger(ILogger::Level level); - virtual void doLogString(Level level, boost::posix_time::ptime time, const std::string& message); -}; - -} diff --git a/src/logger/ConsoleLogger.cpp b/src/logger/ConsoleLogger.cpp deleted file mode 100755 index d8e82b8ab4..0000000000 --- a/src/logger/ConsoleLogger.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "ConsoleLogger.h" -#include -#include -#include -#if defined(_WIN32) -#include -#include -#else -#include -#endif - - -using namespace Log; - -ConsoleLogger::ConsoleLogger(ILogger::Level level) : CommonLogger(level) { -} - -void ConsoleLogger::doLogString(Level level, boost::posix_time::ptime time, const std::string& message) { - std::vector > coloredStrings; - { - std::stringstream ss(message); - char c; - std::string color = ""; - std::string text = ""; - ss.read(&c, 1); - while (!ss.eof()) { - if (c == ILogger::COLOR_DELIMETER) { - coloredStrings.push_back(std::make_pair(color, text)); - color.clear(); - text.clear(); - color += COLOR_DELIMETER; - ss.read(&c, 1); - while (c != ILogger::COLOR_DELIMETER) { - color += c; - ss.read(&c, 1); - } - color += COLOR_DELIMETER; - } else { - text += c; - } - ss.read(&c, 1); - } - coloredStrings.push_back(std::make_pair(color, text)); - coloredStrings[0].first = coloredStrings[1].first; - coloredStrings[0].second = boost::posix_time::to_simple_string(time) + ILogger::LEVEL_NAMES[level]; - } - - std::lock_guard lock(mutex); - for (size_t stringNumber = 0 ; stringNumber < coloredStrings.size(); ++stringNumber) { - if (coloredStrings[stringNumber].second.empty()) continue; - std::string color = coloredStrings[stringNumber].first; - - if (color == BLUE) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE); -#else - std::cout << "\033[0;34m"; -#endif - } else if (color == GREEN) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN); -#else - std::cout << "\033[0;32m"; -#endif - } else if (color == RED) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED); -#else - std::cout << "\033[0;31m"; -#endif - } else if (color == YELLOW) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN); -#else - std::cout << "\033[0;33m"; -#endif - } else if (color == WHITE) { -#ifdef _WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); -#else - std::cout << "\033[0;37m"; -#endif - } else if (color == CYAN) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE); -#else - std::cout << "\033[0;36m"; -#endif - } else if (color == MAGENTA) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_BLUE); -#else - std::cout << "\033[0;35m"; -#endif - } else if (color == BRIGHT_BLUE) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;34m"; -#endif - } else if (color == BRIGHT_GREEN) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;32m"; -#endif - } else if (color == BRIGHT_RED) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;31m"; -#endif - } else if (color == BRIGHT_YELLOW) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;33m"; -#endif - } else if (color == BRIGHT_WHITE) { -#ifdef _WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;37m"; -#endif - } else if (color == BRIGHT_CYAN) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;36m"; -#endif - } else if (color == BRIGHT_MAGENTA) { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); -#else - std::cout << "\033[1;35m"; -#endif - } else { -#ifdef _WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); -#else - std::cout << "\033[0m"; -#endif - } - - std::cout << coloredStrings[stringNumber].second; - } -} diff --git a/src/logger/ConsoleLogger.h b/src/logger/ConsoleLogger.h deleted file mode 100755 index bfa672eceb..0000000000 --- a/src/logger/ConsoleLogger.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "CommonLogger.h" - -namespace Log { - -class ConsoleLogger : public CommonLogger { -public: - ConsoleLogger(ILogger::Level level = ILogger::DEBUGGING); - -protected: - virtual void doLogString(Level level, boost::posix_time::ptime time, const std::string& message) override; - -private: - std::mutex mutex; -}; - -} diff --git a/src/logger/ILogger.cpp b/src/logger/ILogger.cpp deleted file mode 100755 index 47cc3a052c..0000000000 --- a/src/logger/ILogger.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "ILogger.h" - -using namespace Log; - -const std::string ILogger::BLUE = "\x1F""BLUE\x1F"; -const std::string ILogger::GREEN = "\x1F""GREEN\x1F"; -const std::string ILogger::RED = "\x1F""RED\x1F"; -const std::string ILogger::YELLOW = "\x1F""YELLOW\x1F"; -const std::string ILogger::WHITE = "\x1F""WHITE\x1F"; -const std::string ILogger::CYAN = "\x1F""CYAN\x1F"; -const std::string ILogger::MAGENTA = "\x1F""MAGENTA\x1F"; -const std::string ILogger::BRIGHT_BLUE = "\x1F""BRIGHT_BLUE\x1F"; -const std::string ILogger::BRIGHT_GREEN = "\x1F""BRIGHT_GREEN\x1F"; -const std::string ILogger::BRIGHT_RED = "\x1F""BRIGHT_RED\x1F"; -const std::string ILogger::BRIGHT_YELLOW = "\x1F""BRIGHT_YELLOW\x1F"; -const std::string ILogger::BRIGHT_WHITE = "\x1F""BRIGHT_WHITE\x1F"; -const std::string ILogger::BRIGHT_CYAN = "\x1F""BRIGHT_CYAN\x1F"; -const std::string ILogger::BRIGHT_MAGENTA = "\x1F""BRIGHT_MAGENTA\x1F"; -const std::string ILogger::DEFAULT = "\x1F""DEFAULT\x1F"; - -const char ILogger::COLOR_DELIMETER = '\x1F'; - -const std::vector ILogger::LEVEL_NAMES = { - " [FATAL] ", - " [ERROR] ", - " [WARNING] ", - " [INFO] ", - " [DEBUG] ", - " [TRACE] " -}; diff --git a/src/logger/ILogger.h b/src/logger/ILogger.h deleted file mode 100755 index 649e2de094..0000000000 --- a/src/logger/ILogger.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include -#include - -#undef ERROR - -namespace Log { - -class ILogger { -public: - typedef std::size_t Level; - - const static Level FATAL = 0; - const static Level ERROR = 1; - const static Level WARNING = 2; - const static Level INFO = 3; - const static Level DEBUGGING = 4; - const static Level TRACE = 5; - - const static std::string BLUE; - const static std::string GREEN; - const static std::string RED; - const static std::string YELLOW; - const static std::string WHITE; - const static std::string CYAN; - const static std::string MAGENTA; - const static std::string BRIGHT_BLUE; - const static std::string BRIGHT_GREEN; - const static std::string BRIGHT_RED; - const static std::string BRIGHT_YELLOW; - const static std::string BRIGHT_WHITE; - const static std::string BRIGHT_CYAN; - const static std::string BRIGHT_MAGENTA; - const static std::string DEFAULT; - - const static char COLOR_DELIMETER; - - const static std::vector LEVEL_NAMES; - - virtual void enableCategory(const std::string& category) = 0; - virtual void disableCategory(const std::string& category) = 0; - virtual void setMaxLevel(Level level) = 0; - virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) = 0; -}; - -} diff --git a/src/logger/LoggerGroup.cpp b/src/logger/LoggerGroup.cpp deleted file mode 100755 index f89926cbd2..0000000000 --- a/src/logger/LoggerGroup.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "LoggerGroup.h" -#include - -using namespace Log; - -LoggerGroup::LoggerGroup(ILogger::Level level) : CommonLogger(level) { -} - -void LoggerGroup::addLogger(ILogger& logger) { - loggers.push_back(&logger); -} - -void LoggerGroup::removeLogger(ILogger& logger) { - loggers.erase(std::remove(loggers.begin(), loggers.end(), &logger), loggers.end()); -} - -void LoggerGroup::operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) { - if (level > logLevel) { - return; - } - - if (disabledCategories.count(category) != 0) { - return; - } - - for (auto& logger: loggers) { - (*logger)(category, level, time, body); - } -} diff --git a/src/logger/LoggerGroup.h b/src/logger/LoggerGroup.h deleted file mode 100755 index 7cb4e3319f..0000000000 --- a/src/logger/LoggerGroup.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include "CommonLogger.h" - -namespace Log { - -class LoggerGroup : public CommonLogger { -public: - LoggerGroup(ILogger::Level level = DEBUGGING); - - void addLogger(ILogger& logger); - void removeLogger(ILogger& logger); - virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) override; - -protected: - std::vector loggers; -}; - -} diff --git a/src/logger/LoggerMessage.h b/src/logger/LoggerMessage.h deleted file mode 100755 index 08879d50d3..0000000000 --- a/src/logger/LoggerMessage.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include "ILogger.h" - -namespace Log { - -class LoggerMessage : public std::ostream, std::streambuf { -public: - LoggerMessage(ILogger& logger, const std::string& category, ILogger::Level level, const std::string& color); - ~LoggerMessage(); - LoggerMessage(const LoggerMessage&) = delete; - LoggerMessage& operator=(const LoggerMessage&) = delete; - LoggerMessage(LoggerMessage&& other); - -private: - int sync() override; - int overflow(int c) override; - - std::string message; - const std::string category; - ILogger::Level logLevel; - ILogger& logger; - boost::posix_time::ptime timestamp; - bool gotText; -}; - -} diff --git a/src/logger/LoggerRef.cpp b/src/logger/LoggerRef.cpp deleted file mode 100755 index 29fc035d5a..0000000000 --- a/src/logger/LoggerRef.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "LoggerRef.h" - -using namespace Log; - -LoggerRef::LoggerRef(ILogger& logger, const std::string& category) : logger(logger), category(category) { -} - -LoggerRef::LoggerRef(const LoggerRef& other) : logger(other.logger), category(other.category) { -} - -LoggerRef::LoggerRef(const LoggerRef& other, const std::string& category) : logger(other.logger), category(category) { -} - -LoggerMessage LoggerRef::operator()(const std::string& category, ILogger::Level level, const std::string& color) { - return LoggerMessage(logger, category, level, color); -} - -LoggerMessage LoggerRef::operator()(ILogger::Level level, const std::string& color) { - return LoggerMessage(logger, category, level, color); -} diff --git a/src/logger/LoggerRef.h b/src/logger/LoggerRef.h deleted file mode 100755 index 39099e026e..0000000000 --- a/src/logger/LoggerRef.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "ILogger.h" -#include "LoggerMessage.h" - -namespace Log { - -class LoggerRef { -public: - LoggerRef(const LoggerRef& other); - LoggerRef(const LoggerRef& other, const std::string& category); - LoggerRef(ILogger& logger, const std::string& category); - LoggerMessage operator()(const std::string& category, ILogger::Level level, const std::string& color = ILogger::DEFAULT); - LoggerMessage operator()(ILogger::Level level = ILogger::INFO, const std::string& color = ILogger::DEFAULT); - -private: - ILogger& logger; - std::string category; -}; - -} diff --git a/src/logger/StreamLogger.cpp b/src/logger/StreamLogger.cpp deleted file mode 100755 index 344e499fe7..0000000000 --- a/src/logger/StreamLogger.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "StreamLogger.h" -#include -#include - -using namespace Log; - -StreamLogger::StreamLogger(std::ostream& stream, ILogger::Level level) : CommonLogger(level), stream(stream) { -} - -void StreamLogger::doLogString(Level level, boost::posix_time::ptime time, const std::string& message) { - std::string result; - std::stringstream ss(message); - char c; - bool readingText = true; - while (ss.read(&c, 1)) { - if (c == ILogger::COLOR_DELIMETER) { - readingText = !readingText; - continue; - } - - if (readingText) { - result += c; - } - } - - std::lock_guard lock(mutex); - stream << boost::posix_time::to_iso_extended_string(time) << ILogger::LEVEL_NAMES[level] << result << std::flush; -} diff --git a/src/logger/StreamLogger.h b/src/logger/StreamLogger.h deleted file mode 100755 index 72c7fddcae..0000000000 --- a/src/logger/StreamLogger.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include "CommonLogger.h" - -namespace Log { - -class StreamLogger : public CommonLogger { -public: - StreamLogger(std::ostream& stream, ILogger::Level level = ILogger::DEBUGGING); - -protected: - virtual void doLogString(Level level, boost::posix_time::ptime time, const std::string& message) override; - -private: - std::mutex mutex; - std::ostream& stream; -}; - -} diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp deleted file mode 100644 index 260c2f5184..0000000000 --- a/src/miner/simpleminer.cpp +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "common/command_line.h" -#include "misc_log_ex.h" -#include "simpleminer.h" -#include "target_helper.h" -#include "net/http_server_handlers_map2.h" -#include "simpleminer_protocol_defs.h" -#include "storages/http_abstract_invoke.h" -#include "string_tools.h" -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_format_utils.h" - -using namespace epee; -namespace po = boost::program_options; - -int main(int argc, char** argv) -{ - string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_4); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str()); - - po::options_description desc("Allowed options"); - command_line::add_arg(desc, command_line::arg_help); - mining::simpleminer::init_options(desc); - - //uint32_t t = mining::get_target_for_difficulty(700000); - - po::variables_map vm; - bool r = command_line::handle_error_helper(desc, [&]() - { - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - if (command_line::get_arg(vm, command_line::arg_help)) - { - std::cout << desc << std::endl; - return false; - } - - return true; - }); - if (!r) - return 1; - - mining::simpleminer miner; - r = miner.init(vm); - r = r && miner.run(); // Never returns... - - return 0; -} - - - -namespace mining -{ - const command_line::arg_descriptor arg_pool_addr = {"pool-addr", ""}; - const command_line::arg_descriptor arg_login = {"login", ""}; - const command_line::arg_descriptor arg_pass = {"pass", ""}; - - //----------------------------------------------------------------------------------------------------- - void simpleminer::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_pool_addr); - command_line::add_arg(desc, arg_login); - command_line::add_arg(desc, arg_pass); - } - bool simpleminer::init(const boost::program_options::variables_map& vm) - { - std::string pool_addr = command_line::get_arg(vm, arg_pool_addr); - //parse ip and address - std::string::size_type p = pool_addr.find(':'); - CHECK_AND_ASSERT_MES(p != std::string::npos && (p + 1 != pool_addr.size()), false, "Wrong srv address syntax"); - m_pool_ip = pool_addr.substr(0, p); - m_pool_port = pool_addr.substr(p + 1, pool_addr.size()); - m_login = command_line::get_arg(vm, arg_login); - m_pass = command_line::get_arg(vm, arg_pass); - return true; - } - - bool simpleminer::text_job_details_to_native_job_details(const job_details& job, simpleminer::job_details_native& native_details) - { - bool r = epee::string_tools::parse_hexstr_to_binbuff(job.blob, native_details.blob); - CHECK_AND_ASSERT_MES(r, false, "wrong buffer sent from pool server"); - r = epee::string_tools::parse_tpod_from_hex_string(job.target, native_details.target); - CHECK_AND_ASSERT_MES(r, false, "wrong buffer sent from pool server"); - native_details.job_id = job.job_id; - return true; - } - - bool simpleminer::run() - { - std::string pool_session_id; - simpleminer::job_details_native job = AUTO_VAL_INIT(job); - uint64_t last_job_ticks = 0; - crypto::cn_context context; - - while(true) - { - //----------------- - last_job_ticks = epee::misc_utils::get_tick_count(); - if(!m_http_client.is_connected()) - { - LOG_PRINT_L0("Connecting " << m_pool_ip << ":" << m_pool_port << "...."); - if(!m_http_client.connect(m_pool_ip, m_pool_port, 20000)) - { - LOG_PRINT_L0("Failed to connect " << m_pool_ip << ":" << m_pool_port << ", sleep...."); - epee::misc_utils::sleep_no_w(1000); - continue; - } - //DO AUTH - LOG_PRINT_L0("Connected " << m_pool_ip << ":" << m_pool_port << " OK"); - COMMAND_RPC_LOGIN::request req = AUTO_VAL_INIT(req); - req.login = m_login; - req.pass = m_pass; - req.agent = "simpleminer/0.1"; - COMMAND_RPC_LOGIN::response resp = AUTO_VAL_INIT(resp); - if(!epee::net_utils::invoke_http_json_rpc("/", req, resp, m_http_client)) - { - LOG_PRINT_L0("Failed to invoke login " << m_pool_ip << ":" << m_pool_port << ", disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - continue; - } - if(resp.status != "OK" || resp.id.empty()) - { - LOG_PRINT_L0("Failed to login " << m_pool_ip << ":" << m_pool_port << ", disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - continue; - } - pool_session_id = resp.id; - //78 - if (resp.job.blob.empty() && resp.job.target.empty() && resp.job.job_id.empty()) - { - LOG_PRINT_L0("Job didn't change"); - continue; - } - else if(!text_job_details_to_native_job_details(resp.job, job)) - { - LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - continue; - } - last_job_ticks = epee::misc_utils::get_tick_count(); - - } - while(epee::misc_utils::get_tick_count() - last_job_ticks < 20000) - { - //uint32_t c = (*((uint32_t*)&job.blob.data()[39])); - ++(*((uint32_t*)&job.blob.data()[39])); - crypto::hash h = cryptonote::null_hash; - crypto::cn_slow_hash(context, job.blob.data(), job.blob.size(), h); - if( ((uint32_t*)&h)[7] < job.target ) - { - //found! - - COMMAND_RPC_SUBMITSHARE::request submit_request = AUTO_VAL_INIT(submit_request); - COMMAND_RPC_SUBMITSHARE::response submit_response = AUTO_VAL_INIT(submit_response); - submit_request.id = pool_session_id; - submit_request.job_id = job.job_id; - submit_request.nonce = epee::string_tools::pod_to_hex((*((uint32_t*)&job.blob.data()[39]))); - submit_request.result = epee::string_tools::pod_to_hex(h); - LOG_PRINT_L0("Share found: nonce=" << submit_request.nonce << " for job=" << job.job_id << ", submitting..."); - if(!epee::net_utils::invoke_http_json_rpc("/", submit_request, submit_response, m_http_client)) - { - LOG_PRINT_L0("Failed to submit share! disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - break; - } - if(submit_response.status != "OK") - { - LOG_PRINT_L0("Failed to submit share! (submitted share rejected) disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - break; - } - LOG_PRINT_GREEN("Share submitted successfully!", LOG_LEVEL_0); - break; - } - } - //get next job - COMMAND_RPC_GETJOB::request getjob_request = AUTO_VAL_INIT(getjob_request); - COMMAND_RPC_GETJOB::response getjob_response = AUTO_VAL_INIT(getjob_response); - getjob_request.id = pool_session_id; - LOG_PRINT_L0("Getting next job..."); - if(!epee::net_utils::invoke_http_json_rpc("/", getjob_request, getjob_response, m_http_client)) - { - LOG_PRINT_L0("Can't get new job! Disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - continue; - } - if (getjob_response.blob.empty() && getjob_response.target.empty() && getjob_response.job_id.empty()) - { - LOG_PRINT_L0("Job didn't change"); - continue; - } - else if(!text_job_details_to_native_job_details(getjob_response, job)) - { - LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); - m_http_client.disconnect(); - epee::misc_utils::sleep_no_w(1000); - continue; - } - last_job_ticks = epee::misc_utils::get_tick_count(); - } - - return true; - - } -} diff --git a/src/miner/simpleminer.h b/src/miner/simpleminer.h deleted file mode 100644 index 70c7c40a94..0000000000 --- a/src/miner/simpleminer.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once -#include "net/http_client.h" -#include "cryptonote_protocol/blobdatatype.h" -#include "simpleminer_protocol_defs.h" -namespace mining -{ - class simpleminer - { - public: - static void init_options(boost::program_options::options_description& desc); - bool init(const boost::program_options::variables_map& vm); - bool run(); - private: - struct job_details_native - { - cryptonote::blobdata blob; - uint32_t target; - std::string job_id; - }; - - static bool text_job_details_to_native_job_details(const job_details& job, job_details_native& native_details); - - std::string m_pool_ip; - std::string m_pool_port; - std::string m_login; - std::string m_pass; - epee::net_utils::http::http_simple_client m_http_client; - }; -} diff --git a/src/miner/simpleminer_protocol_defs.h b/src/miner/simpleminer_protocol_defs.h deleted file mode 100644 index 5db5d91058..0000000000 --- a/src/miner/simpleminer_protocol_defs.h +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "crypto/hash.h" -#include "net/rpc_method_name.h" - -namespace mining -{ - //----------------------------------------------- -#define CORE_RPC_STATUS_OK "OK" - - - struct job_details - { - std::string blob; - std::string target; - std::string job_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(blob) - KV_SERIALIZE(target) - KV_SERIALIZE(job_id) - END_KV_SERIALIZE_MAP() - }; - - - struct COMMAND_RPC_LOGIN - { - RPC_METHOD_NAME("login"); - - struct request - { - std::string login; - std::string pass; - std::string agent; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(login) - KV_SERIALIZE(pass) - KV_SERIALIZE(agent) - END_KV_SERIALIZE_MAP() - }; - - - struct response - { - std::string status; - std::string id; - job_details job; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(id) - KV_SERIALIZE(job) - END_KV_SERIALIZE_MAP() - }; - }; - - struct COMMAND_RPC_GETJOB - { - RPC_METHOD_NAME("getjob"); - - struct request - { - std::string id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(id) - END_KV_SERIALIZE_MAP() - }; - - typedef job_details response; - }; - - struct COMMAND_RPC_SUBMITSHARE - { - RPC_METHOD_NAME("submit"); - - struct request - { - std::string id; - std::string nonce; - std::string result; - std::string job_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(id) - KV_SERIALIZE(nonce) - KV_SERIALIZE(result) - KV_SERIALIZE(job_id) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - }; -} diff --git a/src/node_rpc_proxy/InitState.h b/src/node_rpc_proxy/InitState.h index 40d166c944..61e98dd0fb 100644 --- a/src/node_rpc_proxy/InitState.h +++ b/src/node_rpc_proxy/InitState.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,8 +19,6 @@ #include -#include "include_base_utils.h" - namespace tools { class InitState { @@ -35,7 +33,6 @@ class InitState { bool beginInit() volatile { State state = STATE_NOT_INITIALIZED; if (!m_state.compare_exchange_strong(state, STATE_INITIALIZING, std::memory_order_seq_cst)) { - LOG_ERROR("object has been already initialized"); return false; } return true; @@ -44,7 +41,6 @@ class InitState { bool endInit() volatile { State expectedState = STATE_INITIALIZING; if (!m_state.compare_exchange_strong(expectedState, STATE_INITIALIZED, std::memory_order_seq_cst)) { - LOG_ERROR("Unexpected state: " << expectedState); return false; } return true; @@ -56,17 +52,14 @@ class InitState { if (STATE_NOT_INITIALIZED == state) { return true; } else if (STATE_INITIALIZING == state) { - LOG_ERROR("Object is being initialized"); return false; } else if (STATE_INITIALIZED == state) { if (m_state.compare_exchange_strong(state, STATE_SHUTTING_DOWN, std::memory_order_seq_cst)) { return true; } } else if (STATE_SHUTTING_DOWN == state) { - LOG_ERROR("Object is being shutting down"); return false; } else { - LOG_ERROR("Unknown state " << state); return false; } } @@ -75,7 +68,6 @@ class InitState { bool endShutdown() volatile { State expectedState = STATE_SHUTTING_DOWN; if (!m_state.compare_exchange_strong(expectedState, STATE_NOT_INITIALIZED, std::memory_order_seq_cst)) { - LOG_ERROR("Unexpected state: " << expectedState); return false; } return true; diff --git a/src/node_rpc_proxy/NodeErrors.cpp b/src/node_rpc_proxy/NodeErrors.cpp index a36696822c..a83f387e33 100644 --- a/src/node_rpc_proxy/NodeErrors.cpp +++ b/src/node_rpc_proxy/NodeErrors.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #include "NodeErrors.h" -namespace cryptonote { +namespace CryptoNote { namespace error { NodeErrorCategory NodeErrorCategory::INSTANCE; diff --git a/src/node_rpc_proxy/NodeErrors.h b/src/node_rpc_proxy/NodeErrors.h index e1c4bda4f4..d030b211c3 100644 --- a/src/node_rpc_proxy/NodeErrors.h +++ b/src/node_rpc_proxy/NodeErrors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { namespace error { // custom error conditions enum type: @@ -66,6 +66,6 @@ class NodeErrorCategory : public std::error_category { } } -inline std::error_code make_error_code(cryptonote::error::NodeErrorCodes e) { - return std::error_code(static_cast(e), cryptonote::error::NodeErrorCategory::INSTANCE); +inline std::error_code make_error_code(CryptoNote::error::NodeErrorCodes e) { + return std::error_code(static_cast(e), CryptoNote::error::NodeErrorCategory::INSTANCE); } diff --git a/src/node_rpc_proxy/NodeRpcProxy.cpp b/src/node_rpc_proxy/NodeRpcProxy.cpp index 82605b0269..4d88d3844d 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.cpp +++ b/src/node_rpc_proxy/NodeRpcProxy.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,39 +16,105 @@ // along with Bytecoin. If not, see . #include "NodeRpcProxy.h" +#include "NodeErrors.h" #include #include #include +#include +#include +#include + +#include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "storages/http_abstract_invoke.h" -#include "NodeErrors.h" - -namespace cryptonote { +#include "rpc/HttpClient.h" +#include "rpc/JsonRpc.h" -using namespace CryptoNote; +namespace CryptoNote { namespace { - std::error_code interpretJsonRpcResponse(bool ok, const std::string& status) { - if (!ok) { - return make_error_code(error::NETWORK_ERROR); - } else if (CORE_RPC_STATUS_BUSY == status) { - return make_error_code(error::NODE_BUSY); - } else if (CORE_RPC_STATUS_OK != status) { - return make_error_code(error::INTERNAL_NODE_ERROR); +std::error_code interpretResponseStatus(const std::string& status) { + if (CORE_RPC_STATUS_BUSY == status) { + return make_error_code(error::NODE_BUSY); + } else if (CORE_RPC_STATUS_OK != status) { + return make_error_code(error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +template +std::error_code binaryCommand(HttpClient& client, const std::string& url, const Request& req, Response& res) { + std::error_code ec; + + try { + invokeBinaryCommand(client, url, req, res); + ec = interpretResponseStatus(res.status); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +template +std::error_code jsonCommand(HttpClient& client, const std::string& url, const Request& req, Response& res) { + std::error_code ec; + + try { + invokeJsonCommand(client, url, req, res); + ec = interpretResponseStatus(res.status); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +template +std::error_code jsonRpcCommand(HttpClient& client, const std::string& method, const Request& req, Response& res) { + std::error_code ec = make_error_code(error::INTERNAL_NODE_ERROR); + + try { + JsonRpc::JsonRpcRequest jsReq; + + jsReq.setMethod(method); + jsReq.setParams(req); + + HttpRequest httpReq; + HttpResponse httpRes; + + httpReq.setUrl("/json_rpc"); + httpReq.setBody(jsReq.getBody()); + + client.request(httpReq, httpRes); + + JsonRpc::JsonRpcResponse jsRes; + + if (httpRes.getStatus() == HttpResponse::STATUS_200) { + jsRes.parse(httpRes.getBody()); + if (jsRes.getResult(res)) { + ec = interpretResponseStatus(res.status); + } } - return std::error_code(); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); } + + return ec; } -NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort) - : m_nodeAddress("http://" + nodeHost + ":" + std::to_string(nodePort)) - , m_rpcTimeout(10000) - , m_pullTimer(m_ioService) - , m_pullInterval(10000) - , m_lastLocalBlockTimestamp(0) { + +} + +NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort) : + m_rpcTimeout(10000), + m_pullTimer(m_ioService), + m_pullInterval(10000), + m_nodeHost(nodeHost), + m_nodePort(nodePort), + m_lastLocalBlockTimestamp(0) { resetInternalState(); } @@ -61,7 +127,7 @@ void NodeRpcProxy::resetInternalState() { m_peerCount = 0; m_nodeHeight = 0; m_networkHeight = 0; - m_lastKnowHash = cryptonote::null_hash; + m_lastKnowHash = CryptoNote::null_hash; } void NodeRpcProxy::init(const INode::Callback& callback) { @@ -95,6 +161,10 @@ void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) { return; } + System::Dispatcher dispatcher; + HttpClient httpClient(dispatcher, m_nodeHost, m_nodePort); + m_httpClient = &httpClient; + initialized_callback(std::error_code()); pullNodeStatusAndScheduleTheNext(); @@ -116,14 +186,14 @@ void NodeRpcProxy::pullNodeStatusAndScheduleTheNext() { } void NodeRpcProxy::updateNodeStatus() { - cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp = AUTO_VAL_INIT(rsp); - bool r = epee::net_utils::invoke_http_json_rpc(m_nodeAddress + "/json_rpc", "getlastblockheader", req, rsp, m_httpClient, m_rpcTimeout); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp = AUTO_VAL_INIT(rsp); + + std::error_code ec = jsonRpcCommand(*m_httpClient, "getlastblockheader", req, rsp); + if (!ec) { crypto::hash blockHash; if (!parse_hash256(rsp.block_header.hash, blockHash)) { - LOG_ERROR("Invalid block hash format: " << rsp.block_header.hash); return; } @@ -140,26 +210,24 @@ void NodeRpcProxy::updateNodeStatus() { //} m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight); } - } else { - LOG_PRINT_L2("Failed to invoke getlastblockheader: " << ec.message() << ':' << ec.value()); } updatePeerCount(); } void NodeRpcProxy::updatePeerCount() { - cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp); - bool r = epee::net_utils::invoke_http_json_remote_command2(m_nodeAddress + "/getinfo", req, rsp, m_httpClient, m_rpcTimeout); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + + CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp); + + std::error_code ec = jsonCommand(*m_httpClient, "/getinfo", req, rsp); + if (!ec) { size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count; if (peerCount != m_peerCount) { m_peerCount = peerCount; m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount); } - } else { - LOG_PRINT_L2("Failed to invoke getinfo: " << ec.message() << ':' << ec.value()); } } @@ -183,11 +251,19 @@ uint64_t NodeRpcProxy::getLastKnownBlockHeight() const { return m_networkHeight; } +uint64_t NodeRpcProxy::getLocalBlockCount() const { + return m_nodeHeight; +} + +uint64_t NodeRpcProxy::getKnownBlockCount() const { + return m_networkHeight; +} + uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const { return m_lastLocalBlockTimestamp; } -void NodeRpcProxy::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { +void NodeRpcProxy::relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { if (!m_initState.initialized()) { callback(make_error_code(error::NOT_INITIALIZED)); return; @@ -206,7 +282,7 @@ void NodeRpcProxy::getRandomOutsByAmounts(std::vector&& amounts, uint6 m_ioService.post(std::bind(&NodeRpcProxy::doGetRandomOutsByAmounts, this, std::move(amounts), outsCount, std::ref(outs), callback)); } -void NodeRpcProxy::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { +void NodeRpcProxy::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { if (!m_initState.initialized()) { callback(make_error_code(error::NOT_INITIALIZED)); return; @@ -233,12 +309,11 @@ void NodeRpcProxy::queryBlocks(std::list&& knownBlockIds, uint64_t m_ioService.post(std::bind(&NodeRpcProxy::doQueryBlocks, this, std::move(knownBlockIds), timestamp, std::ref(newBlocks), std::ref(startHeight), callback)); } -void NodeRpcProxy::doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) { +void NodeRpcProxy::doRelayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { COMMAND_RPC_SEND_RAW_TX::request req; COMMAND_RPC_SEND_RAW_TX::response rsp; - req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(cryptonote::tx_to_blob(transaction)); - bool r = epee::net_utils::invoke_http_json_remote_command2(m_nodeAddress + "/sendrawtransaction", req, rsp, m_httpClient, m_rpcTimeout); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + req.tx_as_hex = blobToHex(CryptoNote::tx_to_blob(transaction)); + std::error_code ec = jsonCommand(*m_httpClient, "/sendrawtransaction", req, rsp); callback(ec); } @@ -247,50 +322,53 @@ void NodeRpcProxy::doGetRandomOutsByAmounts(std::vector& amounts, uint COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response rsp = AUTO_VAL_INIT(rsp); req.amounts = std::move(amounts); req.outs_count = outsCount; - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/getrandom_outs.bin", req, rsp, m_httpClient, m_rpcTimeout); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + + std::error_code ec = binaryCommand(*m_httpClient, "/getrandom_outs.bin", req, rsp); + if (!ec) { outs = std::move(rsp.outs); } callback(ec); } -void NodeRpcProxy::doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { - cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response rsp = AUTO_VAL_INIT(rsp); +void NodeRpcProxy::doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST::response rsp = AUTO_VAL_INIT(rsp); req.block_ids = std::move(knownBlockIds); - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/getblocks.bin", req, rsp, m_httpClient, m_rpcTimeout); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + + std::error_code ec = binaryCommand(*m_httpClient, "/getblocks.bin", req, rsp); + if (!ec) { newBlocks = std::move(rsp.blocks); startHeight = rsp.start_height; } + callback(ec); } void NodeRpcProxy::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { - cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response rsp = AUTO_VAL_INIT(rsp); + CryptoNote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response rsp = AUTO_VAL_INIT(rsp); req.txid = transactionHash; - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/get_o_indexes.bin", req, rsp, m_httpClient, m_rpcTimeout); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); + + std::error_code ec = binaryCommand(*m_httpClient, "/get_o_indexes.bin", req, rsp); + if (!ec) { outsGlobalIndices = std::move(rsp.o_indexes); } + callback(ec); } void NodeRpcProxy::doQueryBlocks(const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { - cryptonote::COMMAND_RPC_QUERY_BLOCKS::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_QUERY_BLOCKS::response rsp = AUTO_VAL_INIT(rsp); - + CryptoNote::COMMAND_RPC_QUERY_BLOCKS::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_QUERY_BLOCKS::response rsp = AUTO_VAL_INIT(rsp); + req.block_ids = knownBlockIds; req.timestamp = timestamp; - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_nodeAddress + "/queryblocks.bin", req, rsp, m_httpClient, m_rpcTimeout); + std::error_code ec = binaryCommand(*m_httpClient, "/queryblocks.bin", req, rsp); - std::error_code ec = interpretJsonRpcResponse(r, rsp.status); - if (!ec) { for (auto& item : rsp.items) { BlockCompleteEntry entry; @@ -307,7 +385,7 @@ void NodeRpcProxy::doQueryBlocks(const std::list& knownBlockIds, u callback(ec); } -void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { +void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { is_bc_actual = true; callback(std::error_code()); }; diff --git a/src/node_rpc_proxy/NodeRpcProxy.h b/src/node_rpc_proxy/NodeRpcProxy.h index b255de4134..e8dd64a647 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.h +++ b/src/node_rpc_proxy/NodeRpcProxy.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,14 +21,15 @@ #include #include +#include -#include "common/ObserverManager.h" -#include "include_base_utils.h" -#include "net/http_client.h" +#include "Common/ObserverManager.h" #include "InitState.h" #include "INode.h" -namespace cryptonote { +namespace CryptoNote { + +class HttpClient; class NodeRpcProxy : public CryptoNote::INode { public: @@ -44,14 +45,17 @@ class NodeRpcProxy : public CryptoNote::INode { virtual size_t getPeerCount() const; virtual uint64_t getLastLocalBlockHeight() const; virtual uint64_t getLastKnownBlockHeight() const; + virtual uint64_t getLocalBlockCount() const override; + virtual uint64_t getKnownBlockCount() const override; virtual uint64_t getLastLocalBlockTimestamp() const override; - virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; + unsigned int rpcTimeout() const { return m_rpcTimeout; } void rpcTimeout(unsigned int val) { m_rpcTimeout = val; } @@ -64,9 +68,9 @@ class NodeRpcProxy : public CryptoNote::INode { void updateNodeStatus(); void updatePeerCount(); - void doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); + void doRelayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); void doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); - void doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + void doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); void doQueryBlocks(const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); @@ -76,9 +80,10 @@ class NodeRpcProxy : public CryptoNote::INode { boost::asio::io_service m_ioService; tools::ObserverManager m_observerManager; - std::string m_nodeAddress; + const std::string m_nodeHost; + const unsigned short m_nodePort; unsigned int m_rpcTimeout; - epee::net_utils::http::http_simple_client m_httpClient; + HttpClient* m_httpClient = nullptr; boost::asio::deadline_timer m_pullTimer; uint64_t m_pullInterval; diff --git a/src/p2p/LevinProtocol.cpp b/src/p2p/LevinProtocol.cpp new file mode 100644 index 0000000000..9e0f660112 --- /dev/null +++ b/src/p2p/LevinProtocol.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LevinProtocol.h" +#include + +using namespace CryptoNote; + +namespace { + +const uint64_t LEVIN_SIGNATURE = 0x0101010101012101LL; //Bender's nightmare +const uint32_t LEVIN_PACKET_REQUEST = 0x00000001; +const uint32_t LEVIN_PACKET_RESPONSE = 0x00000002; +const uint32_t LEVIN_DEFAULT_MAX_PACKET_SIZE = 100000000; //100MB by default +const uint32_t LEVIN_PROTOCOL_VER_1 = 1; + +#pragma pack(push) +#pragma pack(1) +struct bucket_head2 +{ + uint64_t m_signature; + uint64_t m_cb; + bool m_have_to_return_data; + uint32_t m_command; + int32_t m_return_code; + uint32_t m_flags; + uint32_t m_protocol_version; +}; +#pragma pack(pop) + +} + +LevinProtocol::LevinProtocol(System::TcpConnection& connection) + : m_conn(connection) {} + +std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, bool needResponse, bool readResponse) { + bucket_head2 head = { 0 }; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = out.size(); + head.m_have_to_return_data = needResponse; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + + // write header and body in one operation + std::string writeBuffer; + writeBuffer.reserve(sizeof(head) + out.size()); + writeBuffer.append(reinterpret_cast(&head), sizeof(head)); + writeBuffer.append(out); + m_conn.write(reinterpret_cast(writeBuffer.data()), writeBuffer.size()); + + std::string response; + + if (readResponse) { + m_conn.read(reinterpret_cast(&head), sizeof(head)); + + if (head.m_signature != LEVIN_SIGNATURE) { + throw std::runtime_error("Levin signature mismatch"); + } + + if (head.m_cb > LEVIN_DEFAULT_MAX_PACKET_SIZE) { + throw std::runtime_error("Levin packet size is too big"); + } + + response.resize(head.m_cb); + + if (response.size()) { + readStrict(&response[0], head.m_cb); + } + } + + return response; +} + +bool LevinProtocol::readCommand(Command& cmd) { + bucket_head2 head = { 0 }; + + if (!readStrict(&head, sizeof(head))) { + return false; + } + + if (head.m_signature != LEVIN_SIGNATURE) { + throw std::runtime_error("Levin signature mismatch"); + } + + if (head.m_cb > LEVIN_DEFAULT_MAX_PACKET_SIZE) { + throw std::runtime_error("Levin packet size is too big"); + } + + std::string buf; + buf.resize(head.m_cb); + + if (!buf.empty()) { + if (!readStrict(&buf[0], head.m_cb)) { + return false; + } + } + + cmd.command = head.m_command; + cmd.buf = std::move(buf); + cmd.isNotify = !head.m_have_to_return_data; + cmd.isResponse = (head.m_flags & LEVIN_PACKET_RESPONSE) == LEVIN_PACKET_RESPONSE; + + return true; +} + +void LevinProtocol::sendReply(uint32_t command, const std::string& out, int32_t returnCode) { + bucket_head2 head = { 0 }; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = out.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_RESPONSE; + head.m_return_code = returnCode; + + m_conn.write(reinterpret_cast(&head), sizeof(head)); + if (out.size() > 0) { + m_conn.write(reinterpret_cast(out.data()), out.size()); + } +} + +bool LevinProtocol::readStrict(void* ptr, size_t size) { + char* pos = reinterpret_cast(ptr); + size_t offset = 0; + + while (offset < size) { + size_t read = m_conn.read(reinterpret_cast(pos + offset), size - offset); + if (read == 0) { + return false; + } + offset += read; + } + + return true; +} diff --git a/src/p2p/LevinProtocol.h b/src/p2p/LevinProtocol.h new file mode 100644 index 0000000000..523e7ac206 --- /dev/null +++ b/src/p2p/LevinProtocol.h @@ -0,0 +1,97 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "misc_log_ex.h" +#include "storages/portable_storage.h" +#include "storages/portable_storage_from_bin.h" +#include "storages/portable_storage_to_bin.h" + +namespace System { +class TcpConnection; +} + +namespace CryptoNote { + +enum class LevinError: int32_t { + OK = 0, + ERROR_CONNECTION = -1, + ERROR_CONNECTION_NOT_FOUND = -2, + ERROR_CONNECTION_DESTROYED = -3, + ERROR_CONNECTION_TIMEDOUT = -4, + ERROR_CONNECTION_NO_DUPLEX_PROTOCOL = -5, + ERROR_CONNECTION_HANDLER_NOT_DEFINED = -6, + ERROR_FORMAT = -7, +}; + +class LevinProtocol { +public: + + LevinProtocol(System::TcpConnection& connection); + + template + void invoke(uint32_t command, const Req& req, Resp& resp, bool readResponse = true) { + decode(sendBuf(command, encode(req), true, readResponse), resp); + } + + template + void notify(uint32_t command, const Req& req, int) { + sendBuf(command, encode(req), false, false); + } + + struct Command { + uint32_t command; + bool isNotify; + bool isResponse; + std::string buf; + + bool needReply() const { + return !(isNotify || isResponse); + } + }; + + bool readCommand(Command& cmd); + + std::string sendBuf(uint32_t command, const std::string& out, bool needResponse, bool readResponse = false); + void sendReply(uint32_t command, const std::string& out, int32_t returnCode); + + template + static bool decode(const std::string& buf, T& value) { + epee::serialization::portable_storage stg; + if (!stg.load_from_binary(buf)) { + return false; + } + return value.load(stg); + } + + template + static std::string encode(const T& value) { + std::string buf; + epee::serialization::portable_storage stg; + value.store(stg); + stg.store_to_binary(buf); + return buf; + } + +private: + + bool readStrict(void* ptr, size_t size); + System::TcpConnection& m_conn; +}; + +} diff --git a/src/p2p/NetNodeConfig.cpp b/src/p2p/NetNodeConfig.cpp index 11fbd51544..2ab6d8d389 100644 --- a/src/p2p/NetNodeConfig.cpp +++ b/src/p2p/NetNodeConfig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,17 +15,21 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include #include "NetNodeConfig.h" +#include + +#include +#include "Common/command_line.h" +#include "Common/StringTools.h" +#include "crypto/crypto.h" #include "cryptonote_config.h" -#include "common/command_line.h" -namespace nodetool { +namespace CryptoNote { namespace { const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; -const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(cryptonote::P2P_DEFAULT_PORT)}; +const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(P2P_DEFAULT_PORT)}; const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; @@ -35,18 +39,17 @@ const command_line::arg_descriptor > arg_p2p_add_exclus const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; -bool parsePeerFromString(nodetool::net_address& pe, const std::string& node_addr) { - return epee::string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); +bool parsePeerFromString(net_address& pe, const std::string& node_addr) { + return Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); } bool parsePeersAndAddToContainer(const boost::program_options::variables_map& vm, - const command_line::arg_descriptor>& arg, - std::vector& container) + const command_line::arg_descriptor>& arg, std::vector& container) { std::vector peers = command_line::get_arg(vm, arg); for(const std::string& str: peers) { - nodetool::net_address na = boost::value_initialized(); + net_address na = boost::value_initialized(); if (!parsePeerFromString(na, str)) { return false; } @@ -72,7 +75,7 @@ void NetNodeConfig::initOptions(boost::program_options::options_description& des NetNodeConfig::NetNodeConfig() { bindIp = "0.0.0.0"; - bindPort = std::to_string(cryptonote::P2P_DEFAULT_PORT); + bindPort = std::to_string(P2P_DEFAULT_PORT); externalPort = 0; allowLocalIp = false; hideMyPort = false; @@ -92,7 +95,7 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm) std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); for(const std::string& pr_str: perrs) { - nodetool::peerlist_entry pe = boost::value_initialized(); + peerlist_entry pe = boost::value_initialized(); pe.id = crypto::rand(); if (!parsePeerFromString(pe.adr, pr_str)) { return false; @@ -123,4 +126,3 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm) } } //namespace nodetool - diff --git a/src/p2p/NetNodeConfig.h b/src/p2p/NetNodeConfig.h index d2a846b4fe..4593ac0ea6 100644 --- a/src/p2p/NetNodeConfig.h +++ b/src/p2p/NetNodeConfig.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,9 +22,9 @@ #include #include -#include "p2p_protocol_defs.h" +#include "p2p_protocol_types.h" -namespace nodetool { +namespace CryptoNote { class NetNodeConfig { public: diff --git a/src/p2p/PeerListManager.cpp b/src/p2p/PeerListManager.cpp new file mode 100644 index 0000000000..70f31eb2b9 --- /dev/null +++ b/src/p2p/PeerListManager.cpp @@ -0,0 +1,222 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "PeerListManager.h" + +#include +#include +#include + +using namespace CryptoNote; + +//-------------------------------------------------------------------------------------------------- +bool peerlist_manager::init(bool allow_local_ip) +{ + m_allow_local_ip = allow_local_ip; + return true; +} + +//-------------------------------------------------------------------------------------------------- + void peerlist_manager::trim_white_peerlist() +{ + while (m_peers_gray.size() > CryptoNote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) + { + peers_indexed::index::type& sorted_index = m_peers_gray.get(); + sorted_index.erase(sorted_index.begin()); + } +} +//-------------------------------------------------------------------------------------------------- + void peerlist_manager::trim_gray_peerlist() +{ + while (m_peers_white.size() > CryptoNote::P2P_LOCAL_WHITE_PEERLIST_LIMIT) + { + peers_indexed::index::type& sorted_index = m_peers_white.get(); + sorted_index.erase(sorted_index.begin()); + } +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::merge_peerlist(const std::list& outer_bs) +{ + for(const peerlist_entry& be : outer_bs) { + append_with_peer_gray(be); + } + + // delete extra elements + trim_gray_peerlist(); + return true; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::get_white_peer_by_index(peerlist_entry& p, size_t i) +{ + if (i >= m_peers_white.size()) + return false; + + peers_indexed::index::type& by_time_index = m_peers_white.get(); + + auto it = by_time_index.rbegin(); + std::advance(it, i); + p = *it; + + return true; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::get_gray_peer_by_index(peerlist_entry& p, size_t i) +{ + if (i >= m_peers_gray.size()) + return false; + + peers_indexed::index::type& by_time_index = m_peers_gray.get(); + + auto it = by_time_index.rbegin(); + std::advance(it, i); + p = *it; + + return true; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::is_ip_allowed(uint32_t ip) +{ + System::Ipv4Address addr(networkToHost(ip)); + + //never allow loopback ip + if (addr.isLoopback()) { + return false; + } + + if (!m_allow_local_ip && addr.isPrivate()) { + return false; + } + + return true; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::get_peerlist_head(std::list& bs_head, uint32_t depth) +{ + peers_indexed::index::type& by_time_index = m_peers_white.get(); + uint32_t cnt = 0; + + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index) + { + if (!vl.last_seen) + continue; + bs_head.push_back(vl); + if (cnt++ > depth) + break; + } + return true; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) +{ + peers_indexed::index::type& by_time_index_gr = m_peers_gray.get(); + peers_indexed::index::type& by_time_index_wt = m_peers_white.get(); + + std::copy(by_time_index_gr.rbegin(), by_time_index_gr.rend(), std::back_inserter(pl_gray)); + std::copy(by_time_index_wt.rbegin(), by_time_index_wt.rend(), std::back_inserter(pl_white)); + + return true; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port) +{ + net_address addr; + addr.ip = ip; + addr.port = port; + return set_peer_just_seen(peer, addr); +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) +{ + try { + //find in white list + peerlist_entry ple; + ple.adr = addr; + ple.id = peer; + ple.last_seen = time(NULL); + return append_with_peer_white(ple); + } catch (std::exception&) { + } + + return false; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) +{ + try { + if (!is_ip_allowed(ple.adr.ip)) + return true; + + //find in white list + auto by_addr_it_wt = m_peers_white.get().find(ple.adr); + if (by_addr_it_wt == m_peers_white.get().end()) { + //put new record into white list + m_peers_white.insert(ple); + trim_white_peerlist(); + } else { + //update record in white list + m_peers_white.replace(by_addr_it_wt, ple); + } + //remove from gray list, if need + auto by_addr_it_gr = m_peers_gray.get().find(ple.adr); + if (by_addr_it_gr != m_peers_gray.get().end()) { + m_peers_gray.erase(by_addr_it_gr); + } + return true; + } catch (std::exception&) { + } + return false; +} +//-------------------------------------------------------------------------------------------------- + +bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) +{ + try { + if (!is_ip_allowed(ple.adr.ip)) + return true; + + //find in white list + auto by_addr_it_wt = m_peers_white.get().find(ple.adr); + if (by_addr_it_wt != m_peers_white.get().end()) + return true; + + //update gray list + auto by_addr_it_gr = m_peers_gray.get().find(ple.adr); + if (by_addr_it_gr == m_peers_gray.get().end()) + { + //put new record into white list + m_peers_gray.insert(ple); + trim_gray_peerlist(); + } else + { + //update record in white list + m_peers_gray.replace(by_addr_it_gr, ple); + } + return true; + } catch (std::exception&) { + } + return false; +} +//-------------------------------------------------------------------------------------------------- diff --git a/src/p2p/PeerListManager.h b/src/p2p/PeerListManager.h new file mode 100644 index 0000000000..17863f0b1d --- /dev/null +++ b/src/p2p/PeerListManager.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include +#include +#include +#include + +#include "p2p_protocol_types.h" +#include "cryptonote_config.h" + +namespace CryptoNote { + +/************************************************************************/ +/* */ +/************************************************************************/ +class peerlist_manager +{ +public: + bool init(bool allow_local_ip); + size_t get_white_peers_count(){ return m_peers_white.size(); } + size_t get_gray_peers_count(){ return m_peers_gray.size(); } + bool merge_peerlist(const std::list& outer_bs); + bool get_peerlist_head(std::list& bs_head, uint32_t depth = CryptoNote::P2P_DEFAULT_PEERS_IN_HANDSHAKE); + bool get_peerlist_full(std::list& pl_gray, std::list& 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); + bool append_with_peer_gray(const peerlist_entry& pr); + bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); + bool set_peer_just_seen(peerid_type peer, const net_address& addr); + bool set_peer_unreachable(const peerlist_entry& pr); + bool is_ip_allowed(uint32_t ip); + void trim_white_peerlist(); + void trim_gray_peerlist(); + +private: + + struct by_time{}; + struct by_id{}; + struct by_addr{}; + + typedef boost::multi_index_container< + peerlist_entry, + boost::multi_index::indexed_by< + // access by peerlist_entry::net_adress + boost::multi_index::ordered_unique, boost::multi_index::member >, + // sort by peerlist_entry::last_seen< + boost::multi_index::ordered_non_unique, boost::multi_index::member > + > + > peers_indexed; + +public: + + template + void serialize(Archive &a, const t_version_type ver) + { + if (ver < 4) + return; + + a & m_peers_white; + a & m_peers_gray; + } + +private: + + friend class boost::serialization::access; + std::string m_config_folder; + bool m_allow_local_ip; + peers_indexed m_peers_gray; + peers_indexed m_peers_white; +}; + +} + +BOOST_CLASS_VERSION(CryptoNote::peerlist_manager, 4) diff --git a/src/p2p/connection_context.h b/src/p2p/connection_context.h new file mode 100644 index 0000000000..02cced1b94 --- /dev/null +++ b/src/p2p/connection_context.h @@ -0,0 +1,79 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include +#include "Common/StringTools.h" +#include "crypto/hash.h" + +namespace CryptoNote { + +struct cryptonote_connection_context { + boost::uuids::uuid m_connection_id; + uint32_t m_remote_ip = 0; + uint32_t m_remote_port = 0; + bool m_is_income = false; + time_t m_started = 0; + + enum state { + state_befor_handshake = 0, //default state + state_synchronizing, + state_idle, + state_normal, + state_sync_required, + state_shutdown + }; + + state m_state = state_befor_handshake; + std::list m_needed_objects; + std::unordered_set m_requested_objects; + uint64_t m_remote_blockchain_height = 0; + uint64_t m_last_response_height = 0; +}; + +inline std::string get_protocol_state_string(cryptonote_connection_context::state s) { + switch (s) { + case cryptonote_connection_context::state_befor_handshake: + return "state_befor_handshake"; + case cryptonote_connection_context::state_synchronizing: + return "state_synchronizing"; + case cryptonote_connection_context::state_idle: + return "state_idle"; + case cryptonote_connection_context::state_normal: + return "state_normal"; + case cryptonote_connection_context::state_sync_required: + return "state_sync_required"; + case cryptonote_connection_context::state_shutdown: + return "state_shutdown"; + default: + return "unknown"; + } +} + +} + +namespace std { +inline std::ostream& operator << (std::ostream& s, const CryptoNote::cryptonote_connection_context& context) { + return s << "[" << Common::ipAddressToString(context.m_remote_ip) << ":" << + context.m_remote_port << (context.m_is_income ? " INC" : " OUT") << "] "; +} +} diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp new file mode 100644 index 0000000000..80cdffe920 --- /dev/null +++ b/src/p2p/net_node.cpp @@ -0,0 +1,1335 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "net_node.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "Common/util.h" +#include "crypto/crypto.h" + +#include "p2p_protocol_defs.h" +#include "net_peerlist_boost_serialization.h" +#include "connection_context.h" +#include "LevinProtocol.h" + +using namespace Logging; +using namespace CryptoNote; + +namespace { + +size_t get_random_index_with_fixed_probability(size_t max_index) { + //divide by zero workaround + if (!max_index) + return 0; + size_t x = crypto::rand() % (max_index + 1); + return (x*x*x) / (max_index*max_index); //parabola \/ +} + + +void addPortMapping(Logging::LoggerRef& logger, uint32_t port) { + // Add UPnP port mapping + logger(INFO) << "Attempting to add IGD port mapping."; + int result; + UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); + UPNPUrls urls; + IGDdatas igdData; + char lanAddress[64]; + result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); + freeUPNPDevlist(deviceList); + if (result != 0) { + if (result == 1) { + std::ostringstream portString; + portString << port; + if (UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), + portString.str().c_str(), lanAddress, CryptoNote::CRYPTONOTE_NAME, "TCP", 0, "0") != 0) { + logger(ERROR) << "UPNP_AddPortMapping failed."; + } else { + logger(INFO, BRIGHT_GREEN) << "Added IGD port mapping."; + } + } else if (result == 2) { + logger(INFO) << "IGD was found but reported as not connected."; + } else if (result == 3) { + logger(INFO) << "UPnP device was found but not recoginzed as IGD."; + } else { + logger(ERROR) << "UPNP_GetValidIGD returned an unknown result code."; + } + + FreeUPNPUrls(&urls); + } else { + logger(INFO) << "No IGD was found."; + } +} + +bool parse_peer_from_string(net_address& pe, const std::string& node_addr) { + return Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); +} + +} + + +namespace CryptoNote +{ + namespace + { + const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; + const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(CryptoNote::P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; + const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; + const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; + const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; + const command_line::arg_descriptor > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only." + " If this option is given the options add-priority-node and seed-node are ignored"}; + const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; + const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + } + + std::string print_peerlist_to_string(const std::list& pl) { + time_t now_time = 0; + time(&now_time); + std::stringstream ss; + ss << std::setfill('0') << std::setw(8) << std::hex << std::noshowbase; + for (const auto& pe : pl) { + ss << pe.id << "\t" << pe.adr << " \tlast_seen: " << Common::timeIntervalToString(now_time - pe.last_seen) << std::endl; + } + return ss.str(); + } + + + template + int invokeAdaptor(const std::string& reqBuf, std::string& resBuf, p2p_connection_context& ctx, Handler handler) { + typedef typename Command::request Request; + typedef typename Command::response Response; + int command = Command::ID; + + Request req = boost::value_initialized(); + + if (!LevinProtocol::decode(reqBuf, req)) { + throw std::runtime_error("Failed to load_from_binary in command " + std::to_string(command)); + } + + Response res = boost::value_initialized(); + int ret = handler(command, req, res, ctx); + resBuf = LevinProtocol::encode(res); + return ret; + } + + node_server::node_server(System::Dispatcher& dispatcher, CryptoNote::cryptonote_protocol_handler& payload_handler, Logging::ILogger& log) : + m_dispatcher(dispatcher), + m_payload_handler(payload_handler), + m_allow_local_ip(false), + m_hide_my_port(false), + m_network_id(BYTECOIN_NETWORK), + logger(log, "node_server"), + m_stopEvent(m_dispatcher), + m_shutdownCompleteEvent(m_dispatcher), + m_idleTimer(m_dispatcher), + m_timedSyncTimer(m_dispatcher), + m_spawnCount(0), + m_stop(false), + // intervals + // m_peer_handshake_idle_maker_interval(CryptoNote::P2P_DEFAULT_HANDSHAKE_INTERVAL), + m_connections_maker_interval(1), + m_peerlist_store_interval(60*30, false) { + } + + +#define INVOKE_HANDLER(CMD, Handler) case CMD::ID: { ret = invokeAdaptor(cmd.buf, out, ctx, boost::bind(Handler, this, _1, _2, _3, _4)); break; } + + int node_server::handleCommand(const LevinProtocol::Command& cmd, std::string& out, p2p_connection_context& ctx, bool& handled) { + int ret = 0; + handled = true; + + if (cmd.isResponse && cmd.command == COMMAND_TIMED_SYNC::ID) { + if (!handleTimedSyncResponse(cmd.buf, ctx)) { + // invalid response, close connection + ctx.m_state = cryptonote_connection_context::state_shutdown; + } + return 0; + } + + switch (cmd.command) { + INVOKE_HANDLER(COMMAND_HANDSHAKE, &node_server::handle_handshake) + INVOKE_HANDLER(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) + INVOKE_HANDLER(COMMAND_PING, &node_server::handle_ping) +#ifdef ALLOW_DEBUG_COMMANDS + INVOKE_HANDLER(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) + INVOKE_HANDLER(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) + INVOKE_HANDLER(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) +#endif + default: { + handled = false; + ret = m_payload_handler.handleCommand(cmd.isNotify, cmd.command, cmd.buf, out, ctx, handled); + } + } + + return ret; + } + +#undef INVOKE_HANDLER + + //----------------------------------------------------------------------------------- + + void node_server::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_p2p_bind_ip); + command_line::add_arg(desc, arg_p2p_bind_port); + command_line::add_arg(desc, arg_p2p_external_port); + command_line::add_arg(desc, arg_p2p_allow_local_ip); + command_line::add_arg(desc, arg_p2p_add_peer); + command_line::add_arg(desc, arg_p2p_add_priority_node); + command_line::add_arg(desc, arg_p2p_add_exclusive_node); + command_line::add_arg(desc, arg_p2p_seed_node); + command_line::add_arg(desc, arg_p2p_hide_my_port); + } + //----------------------------------------------------------------------------------- + + bool node_server::init_config() { + try { + std::string state_file_path = m_config_folder + "/" + CryptoNote::parameters::P2P_NET_DATA_FILENAME; + std::ifstream p2p_data; + p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::in); + + if (!p2p_data.fail()) { + boost::archive::binary_iarchive a(p2p_data); + a >> *this; + } else { + make_default_config(); + } + + //at this moment we have hardcoded config + m_config.m_net_config.handshake_interval = CryptoNote::P2P_DEFAULT_HANDSHAKE_INTERVAL; + m_config.m_net_config.connections_count = CryptoNote::P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.packet_max_size = CryptoNote::P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit + m_config.m_net_config.config_id = 0; // initial config + m_config.m_net_config.connection_timeout = CryptoNote::P2P_DEFAULT_CONNECTION_TIMEOUT; + m_config.m_net_config.ping_connection_timeout = CryptoNote::P2P_DEFAULT_PING_CONNECTION_TIMEOUT; + m_config.m_net_config.send_peerlist_sz = CryptoNote::P2P_DEFAULT_PEERS_IN_HANDSHAKE; + + m_first_connection_maker_call = true; + } catch (const std::exception& e) { + logger(ERROR) << "init_config failed: " << e.what(); + return false; + } + return true; + } + + //----------------------------------------------------------------------------------- + void node_server::for_each_connection(std::function f) + { + for (auto& ctx : m_connections) { + f(ctx.second, ctx.second.peer_id); + } + } + + //----------------------------------------------------------------------------------- + void node_server::externalRelayNotifyToAll(int command, const std::string& data_buff) { + m_dispatcher.remoteSpawn([this, command, data_buff] { + relay_notify_to_all(command, data_buff, nullptr); + }); + } + + //----------------------------------------------------------------------------------- + bool node_server::make_default_config() + { + m_config.m_peer_id = crypto::rand(); + return true; + } + + //----------------------------------------------------------------------------------- + + bool node_server::handle_command_line(const boost::program_options::variables_map& vm) + { + m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); + m_port = command_line::get_arg(vm, arg_p2p_bind_port); + m_external_port = command_line::get_arg(vm, arg_p2p_external_port); + m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); + + if (command_line::has_arg(vm, arg_p2p_add_peer)) + { + std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); + for(const std::string& pr_str: perrs) + { + peerlist_entry pe = AUTO_VAL_INIT(pe); + pe.id = crypto::rand(); + bool r = parse_peer_from_string(pe.adr, pr_str); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; } + m_command_line_peers.push_back(pe); + } + } + + if (command_line::has_arg(vm,arg_p2p_add_exclusive_node)) { + if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers)) + return false; + } + if (command_line::has_arg(vm, arg_p2p_add_priority_node)) { + if (!parse_peers_and_add_to_container(vm, arg_p2p_add_priority_node, m_priority_peers)) + return false; + } + if (command_line::has_arg(vm, arg_p2p_seed_node)) { + if (!parse_peers_and_add_to_container(vm, arg_p2p_seed_node, m_seed_nodes)) + return false; + } + + if (command_line::has_arg(vm, arg_p2p_hide_my_port)) { + m_hide_my_port = true; + } + + return true; + } + + bool node_server::handleConfig(const NetNodeConfig& config) { + m_bind_ip = config.bindIp; + m_port = config.bindPort; + m_external_port = config.externalPort; + m_allow_local_ip = config.allowLocalIp; + + std::copy(config.peers.begin(), config.peers.end(), std::back_inserter(m_command_line_peers)); + std::copy(config.exclusiveNodes.begin(), config.exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); + std::copy(config.priorityNodes.begin(), config.priorityNodes.end(), std::back_inserter(m_priority_peers)); + std::copy(config.seedNodes.begin(), config.seedNodes.end(), std::back_inserter(m_seed_nodes)); + + m_hide_my_port = config.hideMyPort; + return true; + } + + bool node_server::append_net_address(std::vector& nodes, const std::string& addr) { + size_t pos = addr.find_last_of(':'); + if (!(std::string::npos != pos && addr.length() - 1 != pos && 0 != pos)) { + logger(ERROR, BRIGHT_RED) << "Failed to parse seed address from string: '" << addr << '\''; + return false; + } + + std::string host = addr.substr(0, pos); + + try { + uint32_t port = Common::fromString(addr.substr(pos + 1)); + + System::Ipv4Resolver resolver(m_dispatcher); + auto addr = resolver.resolve(host); + nodes.push_back(net_address{hostToNetwork(addr.getValue()), port}); + + logger(TRACE) << "Added seed node: " << nodes.back() << " (" << host << ")"; + + } catch (const std::exception& e) { + logger(ERROR, BRIGHT_YELLOW) << "Failed to resolve host name '" << host << "': " << e.what(); + return false; + } + + return true; + } + + + //----------------------------------------------------------------------------------- + + bool node_server::init(const NetNodeConfig& config, bool testnet) { + if (!testnet) { + for (auto seed : CryptoNote::SEED_NODES) { + append_net_address(m_seed_nodes, seed); + } + } else { + m_network_id.data[0] += 1; + } + + if (!handleConfig(config)) { + logger(ERROR, BRIGHT_RED) << "Failed to handle command line"; + return false; + } + m_config_folder = config.configFolder; + + if (!init_config()) { + logger(ERROR, BRIGHT_RED) << "Failed to init config."; + return false; + } + + if (!m_peerlist.init(m_allow_local_ip)) { + logger(ERROR, BRIGHT_RED) << "Failed to init peerlist."; + return false; + } + + for(auto& p: m_command_line_peers) + m_peerlist.append_with_peer_white(p); + + //only in case if we really sure that we have external visible ip + m_have_address = true; + m_ip_address = 0; + m_last_stat_request_time = 0; + + //configure self + // m_net_server.get_config_object().m_pcommands_handler = this; + // m_net_server.get_config_object().m_invoke_timeout = CryptoNote::P2P_DEFAULT_INVOKE_TIMEOUT; + + //try to bind + logger(INFO) << "Binding on " << m_bind_ip << ":" << m_port; + m_listeningPort = Common::fromString(m_port); + + m_listener = System::TcpListener(m_dispatcher, System::Ipv4Address(m_bind_ip), static_cast(m_listeningPort)); + + logger(INFO, BRIGHT_GREEN) << "Net service binded on " << m_bind_ip << ":" << m_listeningPort; + + if(m_external_port) + logger(INFO) << "External port defined as " << m_external_port; + + addPortMapping(logger, m_listeningPort); + + return true; + } + //----------------------------------------------------------------------------------- + + CryptoNote::cryptonote_protocol_handler& node_server::get_payload_object() + { + return m_payload_handler; + } + //----------------------------------------------------------------------------------- + + bool node_server::run() { + logger(INFO) << "Starting node_server"; + + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&node_server::acceptLoop, this)); + + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&node_server::onIdle, this)); + + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&node_server::timedSyncLoop, this)); + + m_stopEvent.wait(); + + logger(INFO) << "Stopping node_server..."; + + m_listener.stop(); + m_idleTimer.stop(); + m_timedSyncTimer.stop(); + + logger(INFO) << "Stopping " << m_connections.size() << " connections"; + + for (auto& conn : m_connections) { + conn.second.connection.stop(); + } + + m_shutdownCompleteEvent.wait(); + + logger(INFO) << "net_service loop stopped"; + return true; + } + + //----------------------------------------------------------------------------------- + + uint64_t node_server::get_connections_count() { + return m_connections.size(); + } + //----------------------------------------------------------------------------------- + + bool node_server::deinit() { + return store_config(); + } + + //----------------------------------------------------------------------------------- + + bool node_server::store_config() + { + try { + if (!tools::create_directories_if_necessary(m_config_folder)) { + logger(INFO) << "Failed to create data directory: " << m_config_folder; + return false; + } + + std::string state_file_path = m_config_folder + "/" + CryptoNote::parameters::P2P_NET_DATA_FILENAME; + std::ofstream p2p_data; + p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (p2p_data.fail()) { + logger(INFO) << "Failed to save config to file " << state_file_path; + return false; + }; + + boost::archive::binary_oarchive a(p2p_data); + a << *this; + return true; + } catch (const std::exception& e) { + logger(WARNING) << "store_config failed: " << e.what(); + } + + return false; + } + //----------------------------------------------------------------------------------- + + bool node_server::send_stop_signal() { + m_stop = true; + + m_dispatcher.remoteSpawn([this] { + m_stopEvent.set(); + m_payload_handler.stop(); + }); + + logger(INFO, BRIGHT_YELLOW) << "Stop signal sent"; + return true; + } + + //----------------------------------------------------------------------------------- + bool node_server::handshake(CryptoNote::LevinProtocol& proto, p2p_connection_context& context, bool just_take_peerlist) { + COMMAND_HANDSHAKE::request arg; + COMMAND_HANDSHAKE::response rsp; + get_local_node_data(arg.node_data); + m_payload_handler.get_payload_sync_data(arg.payload_data); + + proto.invoke(COMMAND_HANDSHAKE::ID, arg, rsp); + + if (rsp.node_data.network_id != m_network_id) { + logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE Failed, wrong network! (" << rsp.node_data.network_id << "), closing connection."; + return false; + } + + if (!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) { + logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."; + return false; + } + + if (just_take_peerlist) { + return true; + } + + if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, true)) { + logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE invoked, but process_payload_sync_data returned false, dropping connection."; + return false; + } + + context.peer_id = rsp.node_data.peer_id; + m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); + + if (rsp.node_data.peer_id == m_config.m_peer_id) { + logger(Logging::TRACE) << context << "Connection to self detected, dropping connection"; + return false; + } + + logger(Logging::DEBUGGING) << context << "COMMAND_HANDSHAKE INVOKED OK"; + return true; + } + + + bool node_server::timedSync() { + COMMAND_TIMED_SYNC::request arg = AUTO_VAL_INIT(arg); + m_payload_handler.get_payload_sync_data(arg.payload_data); + auto cmdBuf = LevinProtocol::encode(arg); + + forEachConnection([&](p2p_connection_context& conn) { + if (conn.peer_id && + (conn.m_state == cryptonote_connection_context::state_normal || + conn.m_state == cryptonote_connection_context::state_idle)) { + try { + System::LatchGuard latch(conn.writeLatch); + System::EventLock lock(conn.connectionEvent); + LevinProtocol(conn.connection).sendBuf(COMMAND_TIMED_SYNC::ID, cmdBuf, true, false); + } catch (std::exception&) { + logger(DEBUGGING) << conn << "Failed to send COMMAND_TIMED_SYNC"; + } + } + }); + + return true; + } + + bool node_server::handleTimedSyncResponse(const std::string& in, p2p_connection_context& context) { + COMMAND_TIMED_SYNC::response rsp; + if (!LevinProtocol::decode(in, rsp)) { + return false; + } + + if (!handle_remote_peerlist(rsp.local_peerlist, rsp.local_time, context)) { + logger(Logging::ERROR) << context << "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."; + return false; + } + + if (!context.m_is_income) { + m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); + } + + if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false)) { + return false; + } + + return true; + } + + void node_server::forEachConnection(std::function action) { + + // create copy of connection ids because the list can be changed during action + std::vector connectionIds; + connectionIds.reserve(m_connections.size()); + for (const auto& c : m_connections) { + connectionIds.push_back(c.first); + } + + for (const auto& connId : connectionIds) { + auto it = m_connections.find(connId); + if (it != m_connections.end()) { + action(it->second); + } + } + } + + //----------------------------------------------------------------------------------- + bool node_server::is_peer_used(const peerlist_entry& peer) { + if(m_config.m_peer_id == peer.id) + return true; //dont make connections to ourself + + for (const auto& kv : m_connections) { + const auto& cntxt = kv.second; + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) { + return true; + } + } + return false; + } + //----------------------------------------------------------------------------------- + + bool node_server::is_addr_connected(const net_address& peer) { + for (const auto& conn : m_connections) { + if (!conn.second.m_is_income && peer.ip == conn.second.m_remote_ip && peer.port == conn.second.m_remote_port) { + return true; + } + } + return false; + } + + + bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) { + + logger(DEBUGGING) << "Connecting to " << na << " (white=" << white << ", last_seen: " + << (last_seen_stamp ? Common::timeIntervalToString(time(NULL) - last_seen_stamp) : "never") << ")..."; + + try { + System::TcpConnector connector(m_dispatcher); + + System::Event timeoutEvent(m_dispatcher); + System::Timer timeoutTimer(m_dispatcher); + + m_dispatcher.spawn([&](){ + try { + timeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + connector.stop(); + } catch (std::exception&) {} + timeoutEvent.set(); + }); + + System::TcpConnection connection; + + try { + connection = connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); + } catch (System::InterruptedException&) { + timeoutEvent.wait(); + return false; + } catch (std::exception&) { + timeoutTimer.stop(); + timeoutEvent.wait(); + throw; + } + + p2p_connection_context ctx(m_dispatcher, std::move(connection)); + + timeoutTimer.stop(); + timeoutEvent.wait(); + + // p2p_connection_context ctx(m_dispatcher, std::move(connector.connect())); + + ctx.m_connection_id = boost::uuids::random_generator()(); + ctx.m_remote_ip = na.ip; + ctx.m_remote_port = na.port; + ctx.m_is_income = false; + ctx.m_started = time(nullptr); + + CryptoNote::LevinProtocol proto(ctx.connection); + + if (!handshake(proto, ctx, just_take_peerlist)) { + logger(WARNING) << "Failed to HANDSHAKE with peer " << na; + return false; + } + + if (just_take_peerlist) { + logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << ctx << "CONNECTION HANDSHAKED OK AND CLOSED."; + return true; + } + + peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); + pe_local.adr = na; + pe_local.id = ctx.peer_id; + time(&pe_local.last_seen); + m_peerlist.append_with_peer_white(pe_local); + + if (m_stop) { + throw System::InterruptedException(); + } + + auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, iter)); + + return true; + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "Connection process interrupted"; + throw; + } catch (const std::exception& e) { + logger(DEBUGGING) << "Connection to " << na << " failed: " << e.what(); + } + + return false; + } + + //----------------------------------------------------------------------------------- + bool node_server::make_new_connection_from_peerlist(bool use_white_list) + { + size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); + if(!local_peers_count) + return false;//no peers + + size_t max_random_index = std::min(local_peers_count -1, 20); + + std::set tried_peers; + + size_t try_count = 0; + size_t rand_count = 0; + while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_stop) { + ++rand_count; + size_t random_index = get_random_index_with_fixed_probability(max_random_index); + if (!(random_index < local_peers_count)) { logger(ERROR, BRIGHT_RED) << "random_starter_index < peers_local.size() failed!!"; return false; } + + if(tried_peers.count(random_index)) + continue; + + tried_peers.insert(random_index); + peerlist_entry pe = AUTO_VAL_INIT(pe); + bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to get random peer from peerlist(white:" << use_white_list << ")"; return false; } + + ++try_count; + + if(is_peer_used(pe)) + continue; + + logger(DEBUGGING) << "Selected peer: " << pe.id << " " << pe.adr << " [white=" << use_white_list + << "] last_seen: " << (pe.last_seen ? Common::timeIntervalToString(time(NULL) - pe.last_seen) : "never"); + + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) + continue; + + return true; + } + return false; + } + //----------------------------------------------------------------------------------- + + bool node_server::connections_maker() + { + if (!connect_to_peerlist(m_exclusive_peers)) { + return false; + } + + if (!m_exclusive_peers.empty()) { + return true; + } + + if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) { + size_t try_count = 0; + size_t current_index = crypto::rand()%m_seed_nodes.size(); + + while(true) { + if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) + break; + + if(++try_count > m_seed_nodes.size()) { + logger(ERROR) << "Failed to connect to any of seed peers, continuing without seeds"; + break; + } + if(++current_index >= m_seed_nodes.size()) + current_index = 0; + } + } + + if (!connect_to_peerlist(m_priority_peers)) return false; + + size_t expected_white_connections = (m_config.m_net_config.connections_count * CryptoNote::P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT) / 100; + + size_t conn_count = get_outgoing_connections_count(); + if(conn_count < m_config.m_net_config.connections_count) + { + if(conn_count < expected_white_connections) + { + //start from white list + if(!make_expected_connections_count(true, expected_white_connections)) + return false; + //and then do grey list + if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + return false; + }else + { + //start from grey list + if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + return false; + //and then do white list + if(!make_expected_connections_count(true, m_config.m_net_config.connections_count)) + return false; + } + } + + return true; + } + //----------------------------------------------------------------------------------- + + bool node_server::make_expected_connections_count(bool white_list, size_t expected_connections) + { + size_t conn_count = get_outgoing_connections_count(); + //add new connections from white peers + while(conn_count < expected_connections) + { + if(m_stopEvent.get()) + return false; + + if(!make_new_connection_from_peerlist(white_list)) + break; + conn_count = get_outgoing_connections_count(); + } + return true; + } + + //----------------------------------------------------------------------------------- + size_t node_server::get_outgoing_connections_count() { + size_t count = 0; + for (const auto& cntxt : m_connections) { + if (!cntxt.second.m_is_income) + ++count; + } + return count; + } + + //----------------------------------------------------------------------------------- + bool node_server::idle_worker() { + m_connections_maker_interval.call(std::bind(&node_server::connections_maker, this)); + m_peerlist_store_interval.call(std::bind(&node_server::store_config, this)); + return true; + } + + //----------------------------------------------------------------------------------- + bool node_server::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) + { + //fix time delta + time_t now = 0; + time(&now); + delta = now - local_time; + + BOOST_FOREACH(peerlist_entry& be, local_peerlist) + { + if(be.last_seen > local_time) + { + logger(ERROR) << "FOUND FUTURE peerlist for entry " << be.adr << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time; + return false; + } + be.last_seen += delta; + } + return true; + } + + //----------------------------------------------------------------------------------- + + bool node_server::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const cryptonote_connection_context& context) + { + int64_t delta = 0; + std::list peerlist_ = peerlist; + if(!fix_time_delta(peerlist_, local_time, delta)) + return false; + logger(Logging::TRACE) << context << "REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size(); + logger(Logging::TRACE) << context << "REMOTE PEERLIST: " << print_peerlist_to_string(peerlist_); + return m_peerlist.merge_peerlist(peerlist_); + } + //----------------------------------------------------------------------------------- + + bool node_server::get_local_node_data(basic_node_data& node_data) + { + time_t local_time; + time(&local_time); + node_data.local_time = local_time; + node_data.peer_id = m_config.m_peer_id; + if(!m_hide_my_port) + node_data.my_port = m_external_port ? m_external_port : m_listeningPort; + else + node_data.my_port = 0; + node_data.network_id = m_network_id; + return true; + } + //----------------------------------------------------------------------------------- +#ifdef ALLOW_DEBUG_COMMANDS + + bool node_server::check_trust(const proof_of_trust& tr) + { + uint64_t local_time = time(NULL); + uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; + if(time_delata > 24*60*60 ) + { + logger(ERROR) << "check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time; + return false; + } + if(m_last_stat_request_time >= tr.time ) + { + logger(ERROR) << "check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time; + return false; + } + if(m_config.m_peer_id != tr.peer_id) + { + logger(ERROR) << "check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"; + return false; + } + crypto::public_key pk = AUTO_VAL_INIT(pk); + Common::podFromHex(CryptoNote::P2P_STAT_TRUSTED_PUB_KEY, pk); + crypto::hash h = get_proof_of_trust_hash(tr); + if(!crypto::check_signature(h, pk, tr.sign)) + { + logger(ERROR) << "check_trust failed: sign check failed"; + return false; + } + //update last request time + m_last_stat_request_time = tr.time; + return true; + } + //----------------------------------------------------------------------------------- + + int node_server::handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) + { + if(!check_trust(arg.tr)) { + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + rsp.connections_count = get_connections_count(); + rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); + rsp.version = PROJECT_VERSION_LONG; + rsp.os_version = tools::get_os_version_string(); + m_payload_handler.get_stat_info(rsp.payload_info); + return 1; + } + //----------------------------------------------------------------------------------- + + int node_server::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) + { + if(!check_trust(arg.tr)) { + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + for (const auto& cntxt : m_connections) { + connection_entry ce; + ce.adr.ip = cntxt.second.m_remote_ip; + ce.adr.port = cntxt.second.m_remote_port; + ce.id = cntxt.second.peer_id; + ce.is_income = cntxt.second.m_is_income; + rsp.connections_list.push_back(ce); + } + + m_peerlist.get_peerlist_full(rsp.local_peerlist_gray, rsp.local_peerlist_white); + rsp.my_id = m_config.m_peer_id; + rsp.local_time = time(NULL); + return 1; + } + //----------------------------------------------------------------------------------- + + int node_server::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) + { + rsp.my_id = m_config.m_peer_id; + return 1; + } +#endif + + //----------------------------------------------------------------------------------- + + void node_server::relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) { + net_connection_id excludeId = excludeConnection ? *excludeConnection : boost::value_initialized(); + + forEachConnection([&](p2p_connection_context& conn) { + if (conn.peer_id && conn.m_connection_id != excludeId) { + try { + logger(TRACE) << conn << "Relay command " << command; + System::LatchGuard latch(conn.writeLatch); + System::EventLock lock(conn.connectionEvent); + LevinProtocol(conn.connection).sendBuf(command, data_buff, false); + } catch (const std::exception& e) { + logger(DEBUGGING) << conn << "Failed to relay notification id=" << command << ": " << e.what(); + } + } + }); + } + + //----------------------------------------------------------------------------------- + bool node_server::invoke_notify_to_peer(int command, const std::string& req_buff, const cryptonote_connection_context& context) { + auto it = m_connections.find(context.m_connection_id); + if (it == m_connections.end()) { + return false; + } + + try { + System::LatchGuard latch(it->second.writeLatch); + System::EventLock lock(it->second.connectionEvent); + LevinProtocol(it->second.connection).sendBuf(command, req_buff, false); + } catch (const std::exception& e) { + logger(DEBUGGING) << it->second << "Failed to invoke notification id=" << command << ": " << e.what(); + } + + return true; + } + + //----------------------------------------------------------------------------------- + bool node_server::try_ping(basic_node_data& node_data, p2p_connection_context& context) + { + if(!node_data.my_port) + return false; + + uint32_t actual_ip = context.m_remote_ip; + if(!m_peerlist.is_ip_allowed(actual_ip)) + return false; + + std::string ip = Common::ipAddressToString(actual_ip); + auto port = node_data.my_port; + peerid_type pr = node_data.peer_id; + + try { + System::TcpConnector connector(m_dispatcher); + System::TcpConnection conn = connector.connect(System::Ipv4Address(ip), static_cast(port)); + + LevinProtocol proto(conn); + + COMMAND_PING::request req; + COMMAND_PING::response rsp; + proto.invoke(COMMAND_PING::ID, req, rsp); + + if (rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) { + logger(Logging::DEBUGGING) << context << "back ping invoke wrong response \"" << rsp.status << "\" from" << ip << ":" << port << ", hsh_peer_id=" << pr << ", rsp.peer_id=" << rsp.peer_id; + return false; + } + + return true; + + } catch (std::exception& e) { + logger(Logging::DEBUGGING) << context << "back ping to " << ip << ":" << port << " failed: " << e.what(); + } + + return false; + } + + //----------------------------------------------------------------------------------- + int node_server::handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) + { + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) { + logger(Logging::ERROR) << context << "Failed to process_payload_sync_data(), dropping connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + //fill response + rsp.local_time = time(NULL); + m_peerlist.get_peerlist_head(rsp.local_peerlist); + m_payload_handler.get_payload_sync_data(rsp.payload_data); + logger(Logging::TRACE) << context << "COMMAND_TIMED_SYNC"; + return 1; + } + //----------------------------------------------------------------------------------- + + int node_server::handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + { + if(arg.node_data.network_id != m_network_id) { + logger(Logging::INFO) << context << "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + if(!context.m_is_income) { + logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came not from incoming connection"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + if(context.peer_id) { + logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { + logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."; + context.m_state = cryptonote_connection_context::state_shutdown; + return 1; + } + //associate peer_id with this connection + context.peer_id = arg.node_data.peer_id; + + if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) { + peerid_type peer_id_l = arg.node_data.peer_id; + uint32_t port_l = arg.node_data.my_port; + + if (try_ping(arg.node_data, context)) { + //called only(!) if success pinged, update local peerlist + peerlist_entry pe; + pe.adr.ip = context.m_remote_ip; + pe.adr.port = port_l; + time(&pe.last_seen); + pe.id = peer_id_l; + m_peerlist.append_with_peer_white(pe); + + logger(Logging::TRACE) << context << "PING SUCCESS " << Common::ipAddressToString(context.m_remote_ip) << ":" << port_l; + } + } + + //fill response + m_peerlist.get_peerlist_head(rsp.local_peerlist); + get_local_node_data(rsp.node_data); + m_payload_handler.get_payload_sync_data(rsp.payload_data); + + logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << "COMMAND_HANDSHAKE"; + return 1; + } + //----------------------------------------------------------------------------------- + + int node_server::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) + { + logger(Logging::TRACE) << context << "COMMAND_PING"; + rsp.status = PING_OK_RESPONSE_STATUS_TEXT; + rsp.peer_id = m_config.m_peer_id; + return 1; + } + //----------------------------------------------------------------------------------- + + bool node_server::log_peerlist() + { + std::list pl_wite; + std::list pl_gray; + m_peerlist.get_peerlist_full(pl_gray, pl_wite); + logger(INFO) << ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_wite) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ; + return true; + } + //----------------------------------------------------------------------------------- + + bool node_server::log_connections() { + logger(INFO) << "Connections: \r\n" << print_connections_container() ; + return true; + } + //----------------------------------------------------------------------------------- + + std::string node_server::print_connections_container() { + + std::stringstream ss; + + for (const auto& cntxt : m_connections) { + ss << Common::ipAddressToString(cntxt.second.m_remote_ip) << ":" << cntxt.second.m_remote_port + << " \t\tpeer_id " << cntxt.second.peer_id + << " \t\tconn_id " << cntxt.second.m_connection_id << (cntxt.second.m_is_income ? " INC" : " OUT") + << std::endl; + } + + return ss.str(); + } + //----------------------------------------------------------------------------------- + + void node_server::on_connection_new(p2p_connection_context& context) + { + logger(TRACE) << context << "NEW CONNECTION"; + m_payload_handler.onConnectionOpened(context); + } + //----------------------------------------------------------------------------------- + + void node_server::on_connection_close(p2p_connection_context& context) + { + logger(TRACE) << context << "CLOSE CONNECTION"; + m_payload_handler.onConnectionClosed(context); + } + + bool node_server::is_priority_node(const net_address& na) + { + return + (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || + (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); + } + + bool node_server::connect_to_peerlist(const std::vector& peers) + { + for(const auto& na: peers) { + if (!is_addr_connected(na)) { + try_to_connect_and_handshake_with_new_peer(na); + } + } + + return true; + } + + bool node_server::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor > & arg, std::vector& container) + { + std::vector perrs = command_line::get_arg(vm, arg); + + for(const std::string& pr_str: perrs) { + net_address na = AUTO_VAL_INIT(na); + if (!parse_peer_from_string(na, pr_str)) { + logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; + return false; + } + container.push_back(na); + } + + return true; + } + + void node_server::acceptLoop() { + try { + for (;;) { + p2p_connection_context ctx(m_dispatcher, m_listener.accept()); + ctx.m_connection_id = boost::uuids::random_generator()(); + ctx.m_is_income = true; + ctx.m_started = time(nullptr); + + auto addressAndPort = ctx.connection.getPeerAddressAndPort(); + ctx.m_remote_ip = hostToNetwork(addressAndPort.first.getValue()); + ctx.m_remote_port = addressAndPort.second; + + auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, iter)); + } + } catch (System::InterruptedException&) { + } catch (const std::exception& e) { + logger(WARNING) << "Exception in acceptLoop: " << e.what(); + } + + logger(DEBUGGING) << "acceptLoop finished"; + + if (--m_spawnCount == 0) { + m_shutdownCompleteEvent.set(); + } + } + + void node_server::onIdle() { + logger(DEBUGGING) << "onIdle started"; + + try { + while (!m_stop) { + idle_worker(); + m_payload_handler.on_idle(); + m_idleTimer.sleep(std::chrono::seconds(1)); + } + } catch (System::InterruptedException&) { + } catch (std::exception& e) { + logger(WARNING) << "Exception in onIdle: " << e.what(); + } + + logger(DEBUGGING) << "onIdle finished"; + + if (--m_spawnCount == 0) { + m_shutdownCompleteEvent.set(); + } + } + + void node_server::timedSyncLoop() { + try { + for (;;) { + m_timedSyncTimer.sleep(std::chrono::seconds(P2P_DEFAULT_HANDSHAKE_INTERVAL)); + timedSync(); + } + } catch (System::InterruptedException&) { + } catch (std::exception& e) { + logger(WARNING) << "Exception in timedSyncLoop: " << e.what(); + } + + logger(DEBUGGING) << "timedSyncLoop finished"; + + if (--m_spawnCount == 0) { + m_shutdownCompleteEvent.set(); + } + } + + void node_server::connectionHandler(ConnectionIterator connIter) { + + try { + auto& ctx = connIter->second; + on_connection_new(ctx); + + LevinProtocol proto(ctx.connection); + LevinProtocol::Command cmd; + + for (;;) { + + if (ctx.m_state == cryptonote_connection_context::state_sync_required) { + ctx.m_state = cryptonote_connection_context::state_synchronizing; + m_payload_handler.start_sync(ctx); + } + + if (!proto.readCommand(cmd)) { + break; + } + + std::string response; + bool handled = false; + auto retcode = handleCommand(cmd, response, ctx, handled); + + // send response + if (cmd.needReply()) { + System::LatchGuard latch(ctx.writeLatch); + System::EventLock lock(ctx.connectionEvent); + if (handled) { + proto.sendReply(cmd.command, response, retcode); + } else { + proto.sendReply(cmd.command, std::string(), static_cast(LevinError::ERROR_CONNECTION_HANDLER_NOT_DEFINED)); + } + } + + if (ctx.m_state == cryptonote_connection_context::state_shutdown) { + break; + } + } + + } catch (System::InterruptedException&) { + logger(TRACE) << "Closing connection..."; + } catch (std::exception& e) { + logger(WARNING) << "Exception in connectionHandler: " << e.what(); + } + + connIter->second.writeLatch.wait(); + + on_connection_close(connIter->second); + m_connections.erase(connIter); + + if (--m_spawnCount == 0) { + m_shutdownCompleteEvent.set(); + } + + } + + +} diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index d54c71bcae..fc6bd13f6c 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,164 +16,167 @@ // along with Bytecoin. If not, see . #pragma once + +#include +#include + #include #include #include -#include -#include -#include -#include -#include -#include #include #include #include -#include "warnings.h" -#include "net/levin_server_cp2.h" -#include "p2p_protocol_defs.h" -#include "storages/levin_abstract_invoke2.h" -#include "net_peerlist.h" -#include "p2p_networks.h" -#include "math_helper.h" +#include +#include +#include +#include +#include +#include + +#include "cryptonote_core/OnceInInterval.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "Common/command_line.h" +#include "Logging/LoggerRef.h" + +#include "connection_context.h" +#include "LevinProtocol.h" #include "net_node_common.h" -#include "common/command_line.h" #include "NetNodeConfig.h" +#include "p2p_protocol_defs.h" +#include "p2p_networks.h" +#include "PeerListManager.h" -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4355) +namespace System { +class TcpConnection; +} -namespace nodetool +namespace CryptoNote { - template - struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base - { - peerid_type peer_id; - }; + class LevinProtocol; - template - class node_server: public epee::levin::levin_commands_handler >, - public i_p2p_endpoint - { - struct by_conn_id{}; - struct by_peer_id{}; - struct by_addr{}; + std::string print_peerlist_to_string(const std::list& pl); - typedef p2p_connection_context_t p2p_connection_context; + struct p2p_connection_context : public cryptonote_connection_context { + + p2p_connection_context(System::Dispatcher& dispatcher, System::TcpConnection&& conn) : + peer_id(0), connectionEvent(dispatcher), writeLatch(dispatcher), connection(std::move(conn)) { + connectionEvent.set(); + } + + p2p_connection_context(p2p_connection_context&& ctx) : + cryptonote_connection_context(std::move(ctx)) { + connection = std::move(ctx.connection); + connectionEvent = std::move(ctx.connectionEvent); + writeLatch = std::move(ctx.writeLatch); + peer_id = ctx.peer_id; + } - typedef COMMAND_HANDSHAKE_T COMMAND_HANDSHAKE; - typedef COMMAND_TIMED_SYNC_T COMMAND_TIMED_SYNC; + peerid_type peer_id; + System::TcpConnection connection; + System::Event connectionEvent; + System::Latch writeLatch; + }; + class node_server : public i_p2p_endpoint + { public: - typedef t_payload_net_handler payload_net_handler; - // Some code - node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false), m_network_id(BYTECOIN_NETWORK) - {} static void init_options(boost::program_options::options_description& desc); + node_server(System::Dispatcher& dispatcher, CryptoNote::cryptonote_protocol_handler& payload_handler, Logging::ILogger& log); + bool run(); bool init(const NetNodeConfig& config, bool testnet); bool deinit(); bool send_stop_signal(); - uint32_t get_this_peer_port(){return m_listenning_port;} - t_payload_net_handler& get_payload_object(); + uint32_t get_this_peer_port(){return m_listeningPort;} + CryptoNote::cryptonote_protocol_handler& get_payload_object(); template - void serialize(Archive &a, const t_version_type ver) - { + void serialize(Archive &a, const t_version_type ver) { a & m_peerlist; a & m_config.m_peer_id; } + // debug functions bool log_peerlist(); bool log_connections(); virtual uint64_t get_connections_count(); size_t get_outgoing_connections_count(); - peerlist_manager& get_peerlist_manager(){return m_peerlist;} - private: - typedef COMMAND_REQUEST_STAT_INFO_T COMMAND_REQUEST_STAT_INFO; - CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map - CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing + CryptoNote::peerlist_manager& get_peerlist_manager() { return m_peerlist; } - BEGIN_INVOKE_MAP2(node_server) - HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake) - HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) - HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping) -#ifdef ALLOW_DEBUG_COMMANDS - HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) - HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) - HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) -#endif - CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) - END_INVOKE_MAP2() + private: + + int handleCommand(const LevinProtocol::Command& cmd, std::string& buff_out, p2p_connection_context& context, bool& handled); //----------------- commands handlers ---------------------------------------------- - int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); - int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); + int handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); + int handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context); #ifdef ALLOW_DEBUG_COMMANDS - int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); + int handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); #endif + bool init_config(); bool make_default_config(); bool store_config(); bool check_trust(const proof_of_trust& tr); void initUpnp(); + bool handshake(CryptoNote::LevinProtocol& proto, p2p_connection_context& context, bool just_take_peerlist = false); + bool timedSync(); + bool handleTimedSyncResponse(const std::string& in, p2p_connection_context& context); + void forEachConnection(std::function action); + + void on_connection_new(p2p_connection_context& context); + void on_connection_close(p2p_connection_context& context); - //----------------- levin_commands_handler ------------------------------------------------------------- - virtual void on_connection_new(p2p_connection_context& context) override; - virtual void on_connection_close(p2p_connection_context& context) override; - virtual void callback(p2p_connection_context& context) override; //----------------- i_p2p_endpoint ------------------------------------------------------------- - virtual void relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) override; - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) override; - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) override; - virtual bool drop_connection(const epee::net_utils::connection_context_base& context) override; - virtual void request_callback(const epee::net_utils::connection_context_base& context) override; - virtual void for_each_connection(std::function f) override; + virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) override; + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const cryptonote_connection_context& context) override; + virtual void for_each_connection(std::function f) override; + virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) override; + //----------------------------------------------------------------------------------------------- - bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); bool handle_command_line(const boost::program_options::variables_map& vm); bool handleConfig(const NetNodeConfig& config); + bool append_net_address(std::vector& nodes, const std::string& addr); 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::list& peerlist, time_t local_time, const cryptonote_connection_context& 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 connections_maker(); - bool peer_sync_idle_maker(); - bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); - bool do_peer_timed_sync(const epee::net_utils::connection_context_base& context, peerid_type peer_id); - bool make_new_connection_from_peerlist(bool use_white_list); bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); - size_t get_random_index_with_fixed_probability(size_t max_index); bool is_peer_used(const peerlist_entry& peer); bool is_addr_connected(const net_address& peer); - template - bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); + bool try_ping(basic_node_data& node_data, p2p_connection_context& context); bool make_expected_connections_count(bool white_list, size_t expected_connections); bool is_priority_node(const net_address& na); - template - bool connect_to_peerlist(const Container& peers); + bool connect_to_peerlist(const std::vector& peers); - template - bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor > & arg, Container& container); + bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor > & arg, std::vector& container); //debug functions std::string print_connections_container(); + typedef std::unordered_map> ConnectionContainer; + typedef ConnectionContainer::iterator ConnectionIterator; + ConnectionContainer m_connections; - typedef epee::net_utils::boosted_tcp_server > net_server; + void acceptLoop(); + void connectionHandler(ConnectionIterator connIter); + void onIdle(); + void timedSyncLoop(); struct config { @@ -191,38 +194,39 @@ namespace nodetool bool m_have_address; bool m_first_connection_maker_call; - uint32_t m_listenning_port; + uint32_t m_listeningPort; uint32_t m_external_port; uint32_t m_ip_address; bool m_allow_local_ip; bool m_hide_my_port; - //critical_section m_connections_lock; - //connections_indexed_container m_connections; + System::Dispatcher& m_dispatcher; + System::Event m_stopEvent; + System::Event m_shutdownCompleteEvent; + System::Timer m_idleTimer; + System::TcpListener m_listener; + Logging::LoggerRef logger; + size_t m_spawnCount; + std::atomic m_stop; - t_payload_net_handler& m_payload_handler; + cryptonote_protocol_handler& m_payload_handler; peerlist_manager m_peerlist; - epee::math_helper::once_a_time_seconds m_peer_handshake_idle_maker_interval; - 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; + // OnceInInterval m_peer_handshake_idle_maker_interval; + OnceInInterval m_connections_maker_interval; + OnceInInterval m_peerlist_store_interval; + System::Timer m_timedSyncTimer; std::string m_bind_ip; std::string m_port; #ifdef ALLOW_DEBUG_COMMANDS uint64_t m_last_stat_request_time; #endif - std::list m_priority_peers; + std::vector m_priority_peers; std::vector m_exclusive_peers; std::vector m_seed_nodes; - std::list m_command_line_peers; + std::list m_command_line_peers; uint64_t m_peer_livetime; - //keep connections to initiate some interactions - net_server m_net_server; boost::uuids::uuid m_network_id; }; } - -#include "net_node.inl" - -POP_WARNINGS diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl deleted file mode 100644 index f2347cd669..0000000000 --- a/src/p2p/net_node.inl +++ /dev/null @@ -1,1091 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include "version.h" -#include "string_tools.h" -#include "common/util.h" -#include "net/net_helper.h" -#include "math_helper.h" -#include "p2p_protocol_defs.h" -#include "net_peerlist_boost_serialization.h" -#include "net/local_ip.h" -#include "crypto/crypto.h" -#include "storages/levin_abstract_invoke2.h" -#include -#include - -#define NET_MAKE_IP(b1,b2,b3,b4) ((LPARAM)(((DWORD)(b1)<<24)+((DWORD)(b2)<<16)+((DWORD)(b3)<<8)+((DWORD)(b4)))) - - -namespace nodetool -{ - template - bool node_server::init_config() - { - // - TRY_ENTRY(); - std::string state_file_path = m_config_folder + "/" + cryptonote::parameters::P2P_NET_DATA_FILENAME; - std::ifstream p2p_data; - p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::in); - if(!p2p_data.fail()) - { - boost::archive::binary_iarchive a(p2p_data); - a >> *this; - }else - { - make_default_config(); - } - - //at this moment we have hardcoded config - m_config.m_net_config.handshake_interval = cryptonote::P2P_DEFAULT_HANDSHAKE_INTERVAL; - m_config.m_net_config.connections_count = cryptonote::P2P_DEFAULT_CONNECTIONS_COUNT; - m_config.m_net_config.packet_max_size = cryptonote::P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit - m_config.m_net_config.config_id = 0; // initial config - m_config.m_net_config.connection_timeout = cryptonote::P2P_DEFAULT_CONNECTION_TIMEOUT; - m_config.m_net_config.ping_connection_timeout = cryptonote::P2P_DEFAULT_PING_CONNECTION_TIMEOUT; - m_config.m_net_config.send_peerlist_sz = cryptonote::P2P_DEFAULT_PEERS_IN_HANDSHAKE; - - m_first_connection_maker_call = true; - CATCH_ENTRY_L0("node_server::init_config", false); - return true; - } - //----------------------------------------------------------------------------------- - template - void node_server::for_each_connection(std::function f) - { - m_net_server.get_config_object().foreach_connection([&](p2p_connection_context& cntx){ - return f(cntx, cntx.peer_id); - }); - } - //----------------------------------------------------------------------------------- - template - bool node_server::make_default_config() - { - m_config.m_peer_id = crypto::rand(); - return true; - } - //----------------------------------------------------------------------------------- - namespace - { - template - bool append_net_address(T& nodes, const std::string& addr) - { - using namespace boost::asio; - - size_t pos = addr.find_last_of(':'); - CHECK_AND_ASSERT_MES(std::string::npos != pos && addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\''); - std::string host = addr.substr(0, pos); - std::string port = addr.substr(pos + 1); - - io_service io_srv; - ip::tcp::resolver resolver(io_srv); - ip::tcp::resolver::query query(host, port); - boost::system::error_code ec; - ip::tcp::resolver::iterator i = resolver.resolve(query, ec); - CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to resolve host name '" << host << "': " << ec.message() << ':' << ec.value()); - - ip::tcp::resolver::iterator iend; - for (; i != iend; ++i) - { - ip::tcp::endpoint endpoint = *i; - if (endpoint.address().is_v4()) - { - nodetool::net_address na; - na.ip = boost::asio::detail::socket_ops::host_to_network_long(endpoint.address().to_v4().to_ulong()); - na.port = endpoint.port(); - nodes.push_back(na); - LOG_PRINT_L4("Added seed node: " << endpoint.address().to_v4().to_string(ec) << ':' << na.port); - } - else - { - LOG_PRINT_L2("IPv6 doesn't supported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); - } - } - - return true; - } - } - - template - void node_server::initUpnp() { - // Add UPnP port mapping - LOG_PRINT_L0("Attempting to add IGD port mapping."); - int result; - UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result); - UPNPUrls urls; - IGDdatas igdData; - char lanAddress[64]; - result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress); - freeUPNPDevlist(deviceList); - if (result != 0) { - if (result == 1) { - std::ostringstream portString; - portString << m_listenning_port; - if (UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), - portString.str().c_str(), lanAddress, cryptonote::CRYPTONOTE_NAME, "TCP", 0, "0") != 0) { - LOG_ERROR("UPNP_AddPortMapping failed."); - } else { - LOG_PRINT_GREEN("Added IGD port mapping.", LOG_LEVEL_0); - } - } else if (result == 2) { - LOG_PRINT_L0("IGD was found but reported as not connected."); - } else if (result == 3) { - LOG_PRINT_L0("UPnP device was found but not recoginzed as IGD."); - } else { - LOG_ERROR("UPNP_GetValidIGD returned an unknown result code."); - } - - FreeUPNPUrls(&urls); - } else { - LOG_PRINT_L0("No IGD was found."); - } - } - - template - bool node_server::handleConfig(const NetNodeConfig& config) { - m_bind_ip = config.bindIp; - m_port = config.bindPort; - m_external_port = config.externalPort; - m_allow_local_ip = config.allowLocalIp; - - std::copy(config.peers.begin(), config.peers.end(), std::back_inserter(m_command_line_peers)); - std::copy(config.exclusiveNodes.begin(), config.exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); - std::copy(config.priorityNodes.begin(), config.priorityNodes.end(), std::back_inserter(m_priority_peers)); - std::copy(config.seedNodes.begin(), config.seedNodes.end(), std::back_inserter(m_seed_nodes)); - - m_hide_my_port = config.hideMyPort; - return true; - } - - template - bool node_server::init(const NetNodeConfig& config, bool testnet) { - if (!testnet) { - for (auto seed : cryptonote::SEED_NODES) { - append_net_address(m_seed_nodes, seed); - } - } else { - m_network_id.data[0] += 1; - } - - bool res = handleConfig(config); - CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); - - m_config_folder = config.configFolder; - - res = init_config(); - CHECK_AND_ASSERT_MES(res, false, "Failed to init config."); - - res = m_peerlist.init(m_allow_local_ip); - CHECK_AND_ASSERT_MES(res, false, "Failed to init peerlist."); - - - for(auto& p: m_command_line_peers) - m_peerlist.append_with_peer_white(p); - - //only in case if we really sure that we have external visible ip - m_have_address = true; - m_ip_address = 0; - m_last_stat_request_time = 0; - - //configure self - m_net_server.set_threads_prefix("P2P"); - m_net_server.get_config_object().m_pcommands_handler = this; - m_net_server.get_config_object().m_invoke_timeout = cryptonote::P2P_DEFAULT_INVOKE_TIMEOUT; - - //try to bind - LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); - res = m_net_server.init_server(m_port, m_bind_ip); - CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); - - m_listenning_port = m_net_server.get_binded_port(); - LOG_PRINT_GREEN("Net service binded on " << m_bind_ip << ":" << m_listenning_port, LOG_LEVEL_0); - if(m_external_port) - LOG_PRINT_L0("External port defined as " << m_external_port); - - initUpnp(); - - return res; - } - //----------------------------------------------------------------------------------- - template - typename node_server::payload_net_handler& node_server::get_payload_object() - { - return m_payload_handler; - } - //----------------------------------------------------------------------------------- - template - bool node_server::run() - { - //here you can set worker threads count - int thrds_count = 10; - - m_net_server.add_idle_handler(boost::bind(&node_server::idle_worker, this), 1000); - m_net_server.add_idle_handler(boost::bind(&t_payload_net_handler::on_idle, &m_payload_handler), 1000); - - boost::thread::attributes attrs; - attrs.set_stack_size(cryptonote::THREAD_STACK_SIZE); - - //go to loop - LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); - if(!m_net_server.run_server(thrds_count, true, attrs)) - { - LOG_ERROR("Failed to run net tcp server!"); - } - - LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); - return true; - } - - //----------------------------------------------------------------------------------- - template - uint64_t node_server::get_connections_count() - { - return m_net_server.get_config_object().get_connections_count(); - } - //----------------------------------------------------------------------------------- - template - bool node_server::deinit() - { - m_peerlist.deinit(); - m_net_server.deinit_server(); - return store_config(); - } - //----------------------------------------------------------------------------------- - template - bool node_server::store_config() - { - - TRY_ENTRY(); - if (!tools::create_directories_if_necessary(m_config_folder)) - { - LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); - return false; - } - - std::string state_file_path = m_config_folder + "/" + cryptonote::parameters::P2P_NET_DATA_FILENAME; - std::ofstream p2p_data; - p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); - if(p2p_data.fail()) - { - LOG_PRINT_L0("Failed to save config to file " << state_file_path); - return false; - }; - - boost::archive::binary_oarchive a(p2p_data); - a << *this; - return true; - CATCH_ENTRY_L0("blockchain_storage::save", false); - } - //----------------------------------------------------------------------------------- - template - bool node_server::send_stop_signal() - { - m_net_server.send_stop_signal(); - m_payload_handler.stop(); - LOG_PRINT_L0("[node] Stop signal sent"); - return true; - } - - template - bool node_server::do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context_, bool just_take_peerlist) - { - typename COMMAND_HANDSHAKE::request arg; - typename COMMAND_HANDSHAKE::response rsp; - get_local_node_data(arg.node_data); - m_payload_handler.get_payload_sync_data(arg.payload_data); - - epee::simple_event ev; - std::atomic hsh_result(false); - - bool r = epee::net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, m_net_server.get_config_object(), - [this, &pi, &ev, &hsh_result, &just_take_peerlist](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) - { - epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ev.raise();}); - - if(code < 0) - { - LOG_PRINT_CC_RED(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")", LOG_LEVEL_1); - return; - } - - if(rsp.node_data.network_id != m_network_id) - { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); - return; - } - - if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) - { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); - return; - } - hsh_result = true; - if(!just_take_peerlist) - { - if(!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, true)) - { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE invoked, but process_payload_sync_data returned false, dropping connection."); - hsh_result = false; - return; - } - - pi = context.peer_id = rsp.node_data.peer_id; - m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); - - if(rsp.node_data.peer_id == m_config.m_peer_id) - { - LOG_PRINT_CCONTEXT_L2("Connection to self detected, dropping connection"); - hsh_result = false; - return; - } - LOG_PRINT_CCONTEXT_L1(" COMMAND_HANDSHAKE INVOKED OK"); - }else - { - LOG_PRINT_CCONTEXT_L1(" COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); - } - }, cryptonote::P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); - - if(r) - { - ev.wait(); - } - - if(!hsh_result) - { - LOG_PRINT_CC_L1(context_, "COMMAND_HANDSHAKE Failed"); - m_net_server.get_config_object().close(context_.m_connection_id); - } - - return hsh_result; - } - //----------------------------------------------------------------------------------- - template - bool node_server::do_peer_timed_sync(const epee::net_utils::connection_context_base& context_, peerid_type peer_id) - { - typename COMMAND_TIMED_SYNC::request arg = AUTO_VAL_INIT(arg); - m_payload_handler.get_payload_sync_data(arg.payload_data); - - bool r = epee::net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, m_net_server.get_config_object(), - [this](int code, const typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) - { - if(code < 0) - { - LOG_PRINT_CC_RED(context, "COMMAND_TIMED_SYNC invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")", LOG_LEVEL_1); - return; - } - - if(!handle_remote_peerlist(rsp.local_peerlist, rsp.local_time, context)) - { - LOG_ERROR_CCONTEXT("COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); - m_net_server.get_config_object().close(context.m_connection_id ); - } - if(!context.m_is_income) - m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); - m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); - }); - - if(!r) - { - LOG_PRINT_CC_L2(context_, "COMMAND_TIMED_SYNC Failed"); - return false; - } - return true; - } - //----------------------------------------------------------------------------------- - template - size_t node_server::get_random_index_with_fixed_probability(size_t max_index) - { - //divide by zero workaround - if(!max_index) - return 0; - - size_t x = crypto::rand()%(max_index+1); - size_t res = (x*x*x)/(max_index*max_index); //parabola \/ - LOG_PRINT_L3("Random connection index=" << res << "(x="<< x << ", max_index=" << max_index << ")"); - return res; - } - //----------------------------------------------------------------------------------- - template - bool node_server::is_peer_used(const peerlist_entry& peer) - { - - if(m_config.m_peer_id == peer.id) - return true;//dont make connections to ourself - - bool used = false; - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) - { - used = true; - return false;//stop enumerating - } - return true; - }); - - return used; - } - //----------------------------------------------------------------------------------- - template - bool node_server::is_addr_connected(const net_address& peer) - { - bool connected = false; - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - if(!cntxt.m_is_income && peer.ip == cntxt.m_remote_ip && peer.port == cntxt.m_remote_port) - { - connected = true; - return false;//stop enumerating - } - return true; - }); - - return connected; - } - -#define LOG_PRINT_CC_PRIORITY_NODE(priority, con, msg) \ - do { \ - if (priority) {\ - LOG_PRINT_CC_L0(con, msg); \ - } else {\ - LOG_PRINT_CC_L1(con, msg); \ - } \ - } while(0) - - template - bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) - { - LOG_PRINT_L1("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" - << epee::string_tools::num_to_string_fast(na.port) << "(white=" << white << ", last_seen: " - << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") - << ")..."); - - typename net_server::t_connection_context con = AUTO_VAL_INIT(con); - bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(na.ip), - epee::string_tools::num_to_string_fast(na.port), - m_config.m_net_config.connection_timeout, - con); - - if(!res) - { - bool is_priority = is_priority_node(na); - LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " - << epee::string_tools::get_ip_string_from_int32(na.ip) - << ":" << epee::string_tools::num_to_string_fast(na.port) - /*<< ", try " << try_count*/); - //m_peerlist.set_peer_unreachable(pe); - return false; - } - - peerid_type pi = AUTO_VAL_INIT(pi); - res = do_handshake_with_peer(pi, con, just_take_peerlist); - - if(!res) - { - bool is_priority = is_priority_node(na); - LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Failed to HANDSHAKE with peer " - << epee::string_tools::get_ip_string_from_int32(na.ip) - << ":" << epee::string_tools::num_to_string_fast(na.port) - /*<< ", try " << try_count*/); - return false; - } - - if(just_take_peerlist) - { - m_net_server.get_config_object().close(con.m_connection_id); - LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK AND CLOSED.", LOG_LEVEL_2); - return true; - } - - peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); - pe_local.adr = na; - pe_local.id = pi; - time(&pe_local.last_seen); - m_peerlist.append_with_peer_white(pe_local); - //update last seen and push it to peerlist manager - - LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK.", LOG_LEVEL_2); - return true; - } - -#undef LOG_PRINT_CC_PRIORITY_NODE - - //----------------------------------------------------------------------------------- - template - bool node_server::make_new_connection_from_peerlist(bool use_white_list) - { - size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); - if(!local_peers_count) - return false;//no peers - - size_t max_random_index = std::min(local_peers_count -1, 20); - - std::set tried_peers; - - size_t try_count = 0; - size_t rand_count = 0; - while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_net_server.is_stop_signal_sent()) - { - ++rand_count; - size_t random_index = get_random_index_with_fixed_probability(max_random_index); - CHECK_AND_ASSERT_MES(random_index < local_peers_count, false, "random_starter_index < peers_local.size() failed!!"); - - if(tried_peers.count(random_index)) - continue; - - tried_peers.insert(random_index); - peerlist_entry pe = AUTO_VAL_INIT(pe); - bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); - CHECK_AND_ASSERT_MES(r, false, "Failed to get random peer from peerlist(white:" << use_white_list << ")"); - - ++try_count; - - if(is_peer_used(pe)) - continue; - - LOG_PRINT_L1("Selected peer: " << pe.id << " " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) - << ":" << boost::lexical_cast(pe.adr.port) - << "[white=" << use_white_list - << "] last_seen: " << (pe.last_seen ? epee::misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); - - if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) - continue; - - return true; - } - return false; - } - //----------------------------------------------------------------------------------- - template - bool node_server::connections_maker() - { - if (!connect_to_peerlist(m_exclusive_peers)) return false; - - if (!m_exclusive_peers.empty()) return true; - - if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) - { - size_t try_count = 0; - size_t current_index = crypto::rand()%m_seed_nodes.size(); - while(true) - { - if(m_net_server.is_stop_signal_sent()) - return false; - - if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) - break; - if(++try_count > m_seed_nodes.size()) - { - LOG_PRINT_RED_L0("Failed to connect to any of seed peers, continuing without seeds"); - break; - } - if(++current_index >= m_seed_nodes.size()) - current_index = 0; - } - } - - if (!connect_to_peerlist(m_priority_peers)) return false; - - size_t expected_white_connections = (m_config.m_net_config.connections_count * cryptonote::P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT) / 100; - - size_t conn_count = get_outgoing_connections_count(); - if(conn_count < m_config.m_net_config.connections_count) - { - if(conn_count < expected_white_connections) - { - //start from white list - if(!make_expected_connections_count(true, expected_white_connections)) - return false; - //and then do grey list - if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) - return false; - }else - { - //start from grey list - if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) - return false; - //and then do white list - if(!make_expected_connections_count(true, m_config.m_net_config.connections_count)) - return false; - } - } - - return true; - } - //----------------------------------------------------------------------------------- - template - bool node_server::make_expected_connections_count(bool white_list, size_t expected_connections) - { - size_t conn_count = get_outgoing_connections_count(); - //add new connections from white peers - while(conn_count < expected_connections) - { - if(m_net_server.is_stop_signal_sent()) - return false; - - if(!make_new_connection_from_peerlist(white_list)) - break; - conn_count = get_outgoing_connections_count(); - } - return true; - } - - //----------------------------------------------------------------------------------- - template - size_t node_server::get_outgoing_connections_count() - { - size_t count = 0; - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - if(!cntxt.m_is_income) - ++count; - return true; - }); - - return count; - } - //----------------------------------------------------------------------------------- - template - bool node_server::idle_worker() - { - m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server::peer_sync_idle_maker, this)); - m_connections_maker_interval.do_call(boost::bind(&node_server::connections_maker, this)); - m_peerlist_store_interval.do_call(boost::bind(&node_server::store_config, this)); - return true; - } - //----------------------------------------------------------------------------------- - template - bool node_server::peer_sync_idle_maker() - { - LOG_PRINT_L2("STARTED PEERLIST IDLE HANDSHAKE"); - typedef std::list > local_connects_type; - local_connects_type cncts; - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - if(cntxt.peer_id) - cncts.push_back(local_connects_type::value_type(cntxt, cntxt.peer_id));//do idle sync only with handshaked connections - return true; - }); - - std::for_each(cncts.begin(), cncts.end(), [&](const typename local_connects_type::value_type& vl){do_peer_timed_sync(vl.first, vl.second);}); - - LOG_PRINT_L2("FINISHED PEERLIST IDLE HANDSHAKE"); - return true; - } - //----------------------------------------------------------------------------------- - template - bool node_server::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) - { - //fix time delta - time_t now = 0; - time(&now); - delta = now - local_time; - - BOOST_FOREACH(peerlist_entry& be, local_peerlist) - { - if(be.last_seen > local_time) - { - LOG_PRINT_RED_L0("FOUND FUTURE peerlist for entry " << epee::string_tools::get_ip_string_from_int32(be.adr.ip) << ":" << be.adr.port << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time); - return false; - } - be.last_seen += delta; - } - return true; - } - //----------------------------------------------------------------------------------- - template - bool node_server::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context) - { - int64_t delta = 0; - std::list peerlist_ = peerlist; - if(!fix_time_delta(peerlist_, local_time, delta)) - return false; - LOG_PRINT_CCONTEXT_L2("REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size()); - LOG_PRINT_CCONTEXT_L3("REMOTE PEERLIST: " << print_peerlist_to_string(peerlist_)); - return m_peerlist.merge_peerlist(peerlist_); - } - //----------------------------------------------------------------------------------- - template - bool node_server::get_local_node_data(basic_node_data& node_data) - { - time_t local_time; - time(&local_time); - node_data.local_time = local_time; - node_data.peer_id = m_config.m_peer_id; - if(!m_hide_my_port) - node_data.my_port = m_external_port ? m_external_port : m_listenning_port; - else - node_data.my_port = 0; - node_data.network_id = m_network_id; - return true; - } - //----------------------------------------------------------------------------------- -#ifdef ALLOW_DEBUG_COMMANDS - template - bool node_server::check_trust(const proof_of_trust& tr) - { - uint64_t local_time = time(NULL); - uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; - if(time_delata > 24*60*60 ) - { - LOG_ERROR("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); - return false; - } - if(m_last_stat_request_time >= tr.time ) - { - LOG_ERROR("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); - return false; - } - if(m_config.m_peer_id != tr.peer_id) - { - LOG_ERROR("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"); - return false; - } - crypto::public_key pk = AUTO_VAL_INIT(pk); - epee::string_tools::hex_to_pod(cryptonote::P2P_STAT_TRUSTED_PUB_KEY, pk); - crypto::hash h = tools::get_proof_of_trust_hash(tr); - if(!crypto::check_signature(h, pk, tr.sign)) - { - LOG_ERROR("check_trust failed: sign check failed"); - return false; - } - //update last request time - m_last_stat_request_time = tr.time; - return true; - } - //----------------------------------------------------------------------------------- - template - int node_server::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) - { - if(!check_trust(arg.tr)) - { - drop_connection(context); - return 1; - } - rsp.connections_count = m_net_server.get_config_object().get_connections_count(); - rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); - rsp.version = PROJECT_VERSION_LONG; - rsp.os_version = tools::get_os_version_string(); - m_payload_handler.get_stat_info(rsp.payload_info); - return 1; - } - //----------------------------------------------------------------------------------- - template - int node_server::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) - { - if(!check_trust(arg.tr)) - { - drop_connection(context); - return 1; - } - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - connection_entry ce; - ce.adr.ip = cntxt.m_remote_ip; - ce.adr.port = cntxt.m_remote_port; - ce.id = cntxt.peer_id; - ce.is_income = cntxt.m_is_income; - rsp.connections_list.push_back(ce); - return true; - }); - - m_peerlist.get_peerlist_full(rsp.local_peerlist_gray, rsp.local_peerlist_white); - rsp.my_id = m_config.m_peer_id; - rsp.local_time = time(NULL); - return 1; - } - //----------------------------------------------------------------------------------- - template - int node_server::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) - { - rsp.my_id = m_config.m_peer_id; - return 1; - } -#endif - //----------------------------------------------------------------------------------- - template - void node_server::request_callback(const epee::net_utils::connection_context_base& context) - { - m_net_server.get_config_object().request_callback(context.m_connection_id); - } - //----------------------------------------------------------------------------------- - template - void node_server::relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) - { - std::list connections; - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - if(cntxt.peer_id && context.m_connection_id != cntxt.m_connection_id) - connections.push_back(cntxt.m_connection_id); - return true; - }); - - BOOST_FOREACH(const auto& c_id, connections) - { - m_net_server.get_config_object().notify(command, data_buff, c_id); - } - } - //----------------------------------------------------------------------------------- - template - void node_server::callback(p2p_connection_context& context) - { - m_payload_handler.on_callback(context); - } - //----------------------------------------------------------------------------------- - template - bool node_server::invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) - { - int res = m_net_server.get_config_object().notify(command, req_buff, context.m_connection_id); - return res > 0; - } - //----------------------------------------------------------------------------------- - template - bool node_server::invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) - { - int res = m_net_server.get_config_object().invoke(command, req_buff, resp_buff, context.m_connection_id); - return res > 0; - } - //----------------------------------------------------------------------------------- - template - bool node_server::drop_connection(const epee::net_utils::connection_context_base& context) - { - m_net_server.get_config_object().close(context.m_connection_id); - return true; - } - //----------------------------------------------------------------------------------- - template template - bool node_server::try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb) - { - if(!node_data.my_port) - return false; - - uint32_t actual_ip = context.m_remote_ip; - if(!m_peerlist.is_ip_allowed(actual_ip)) - return false; - std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip); - std::string port = epee::string_tools::num_to_string_fast(node_data.my_port); - peerid_type pr = node_data.peer_id; - bool r = m_net_server.connect_async(ip, port, m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ ip, port, pr, this]( - const typename net_server::t_connection_context& ping_context, - const boost::system::error_code& ec)->bool - { - if(ec) - { - LOG_PRINT_CC_L2(ping_context, "back ping connect failed to " << ip << ":" << port); - return false; - } - COMMAND_PING::request req; - COMMAND_PING::response rsp; - //vc2010 workaround - /*std::string ip_ = ip; - std::string port_=port; - peerid_type pr_ = pr; - auto cb_ = cb;*/ - bool inv_call_res = epee::net_utils::async_invoke_remote_command2(ping_context.m_connection_id, COMMAND_PING::ID, req, m_net_server.get_config_object(), - [=](int code, const COMMAND_PING::response& rsp, p2p_connection_context& context) - { - if(code <= 0) - { - LOG_PRINT_CC_L2(ping_context, "Failed to invoke COMMAND_PING to " << ip << ":" << port << "(" << code << ", " << epee::levin::get_err_descr(code) << ")"); - return; - } - - if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) - { - LOG_PRINT_CC_L2(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << ip << ":" << port << ", hsh_peer_id=" << pr << ", rsp.peer_id=" << rsp.peer_id); - return; - } - m_net_server.get_config_object().close(ping_context.m_connection_id); - cb(); - }); - - if(!inv_call_res) - { - LOG_PRINT_CC_L2(ping_context, "back ping invoke failed to " << ip << ":" << port); - m_net_server.get_config_object().close(ping_context.m_connection_id); - return false; - } - return true; - }); - if(!r) - { - LOG_ERROR("Failed to call connect_async, network error."); - } - return r; - } - //----------------------------------------------------------------------------------- - template - int node_server::handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) - { - if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) - { - LOG_ERROR_CCONTEXT("Failed to process_payload_sync_data(), dropping connection"); - drop_connection(context); - return 1; - } - - //fill response - rsp.local_time = time(NULL); - m_peerlist.get_peerlist_head(rsp.local_peerlist); - m_payload_handler.get_payload_sync_data(rsp.payload_data); - LOG_PRINT_CCONTEXT_L2("COMMAND_TIMED_SYNC"); - return 1; - } - //----------------------------------------------------------------------------------- - template - int node_server::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) - { - if(arg.node_data.network_id != m_network_id) - { - LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << epee::string_tools::get_str_from_guid_a(arg.node_data.network_id)); - drop_connection(context); - return 1; - } - - if(!context.m_is_income) - { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came not from incoming connection"); - drop_connection(context); - return 1; - } - - if(context.peer_id) - { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"); - drop_connection(context); - return 1; - } - - if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) - { - LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); - drop_connection(context); - return 1; - } - //associate peer_id with this connection - context.peer_id = arg.node_data.peer_id; - - if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) - { - peerid_type peer_id_l = arg.node_data.peer_id; - uint32_t port_l = arg.node_data.my_port; - //try ping to be sure that we can add this peer to peer_list - try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() - { - //called only(!) if success pinged, update local peerlist - peerlist_entry pe; - pe.adr.ip = context.m_remote_ip; - pe.adr.port = port_l; - time(&pe.last_seen); - pe.id = peer_id_l; - this->m_peerlist.append_with_peer_white(pe); - LOG_PRINT_CCONTEXT_L2("PING SUCCESS " << epee::string_tools::get_ip_string_from_int32(context.m_remote_ip) << ":" << port_l); - }); - } - - //fill response - m_peerlist.get_peerlist_head(rsp.local_peerlist); - get_local_node_data(rsp.node_data); - m_payload_handler.get_payload_sync_data(rsp.payload_data); - LOG_PRINT_CCONTEXT_GREEN("COMMAND_HANDSHAKE", LOG_LEVEL_1); - return 1; - } - //----------------------------------------------------------------------------------- - template - int node_server::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) - { - LOG_PRINT_CCONTEXT_L2("COMMAND_PING"); - rsp.status = PING_OK_RESPONSE_STATUS_TEXT; - rsp.peer_id = m_config.m_peer_id; - return 1; - } - //----------------------------------------------------------------------------------- - template - bool node_server::log_peerlist() - { - std::list pl_wite; - std::list pl_gray; - m_peerlist.get_peerlist_full(pl_gray, pl_wite); - LOG_PRINT_L0(ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_wite) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ); - return true; - } - //----------------------------------------------------------------------------------- - template - bool node_server::log_connections() - { - LOG_PRINT_L0("Connections: \r\n" << print_connections_container() ); - return true; - } - //----------------------------------------------------------------------------------- - template - std::string node_server::print_connections_container() - { - - std::stringstream ss; - m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) - { - ss << epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) << ":" << cntxt.m_remote_port - << " \t\tpeer_id " << cntxt.peer_id - << " \t\tconn_id " << epee::string_tools::get_str_from_guid_a(cntxt.m_connection_id) << (cntxt.m_is_income ? " INC":" OUT") - << std::endl; - return true; - }); - std::string s = ss.str(); - return s; - } - //----------------------------------------------------------------------------------- - template - void node_server::on_connection_new(p2p_connection_context& context) - { - LOG_PRINT_L2("["<< epee::net_utils::print_connection_context(context) << "] NEW CONNECTION"); - m_payload_handler.onConnectionOpened(context); - } - //----------------------------------------------------------------------------------- - template - void node_server::on_connection_close(p2p_connection_context& context) - { - LOG_PRINT_L2("["<< epee::net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); - m_payload_handler.onConnectionClosed(context); - } - - template - bool node_server::is_priority_node(const net_address& na) - { - return (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); - } - - template template - bool node_server::connect_to_peerlist(const Container& peers) - { - for(const net_address& na: peers) - { - if(m_net_server.is_stop_signal_sent()) - return false; - - if(is_addr_connected(na)) - continue; - - try_to_connect_and_handshake_with_new_peer(na); - } - - return true; - } -} diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 9aa4d05fc8..054caa034c 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,56 +17,26 @@ #pragma once -#include -#include "net/net_utils_base.h" +#include "p2p_protocol_types.h" -namespace nodetool -{ +namespace CryptoNote { - typedef boost::uuids::uuid uuid; - typedef boost::uuids::uuid net_connection_id; - typedef uint64_t peerid_type; + struct cryptonote_connection_context; - template - struct i_p2p_endpoint - { - virtual void relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context)=0; - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context)=0; - virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; - virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; + struct i_p2p_endpoint { + virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) = 0; + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const CryptoNote::cryptonote_connection_context& context) = 0; virtual uint64_t get_connections_count()=0; - virtual void for_each_connection(std::function f)=0; + virtual void for_each_connection(std::function f) = 0; + // can be called from external threads + virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) = 0; }; - template - struct p2p_endpoint_stub: public i_p2p_endpoint - { - virtual void relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) - { - } - virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) - { - return false; - } - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) - { - return true; - } - virtual bool drop_connection(const epee::net_utils::connection_context_base& context) - { - return false; - } - virtual void request_callback(const epee::net_utils::connection_context_base& context) - { - } - virtual void for_each_connection(std::function f) - { - } - - virtual uint64_t get_connections_count() - { - return false; - } + struct p2p_endpoint_stub: public i_p2p_endpoint { + virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) {} + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const CryptoNote::cryptonote_connection_context& context) { return true; } + virtual void for_each_connection(std::function f) {} + virtual uint64_t get_connections_count() { return 0; } + virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) {} }; } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h deleted file mode 100644 index 8388e70af2..0000000000 --- a/src/p2p/net_peerlist.h +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include -#include -//#include -//#include -#include -#include -#include - -#include -#include -#include -#include - - -#include "syncobj.h" -#include "net/local_ip.h" -#include "p2p_protocol_defs.h" -#include "cryptonote_config.h" -#include "net_peerlist_boost_serialization.h" - - - -namespace nodetool -{ - - - /************************************************************************/ - /* */ - /************************************************************************/ - class peerlist_manager - { - public: - bool init(bool allow_local_ip); - 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 = cryptonote::P2P_DEFAULT_PEERS_IN_HANDSHAKE); - bool get_peerlist_full(std::list& pl_gray, std::list& 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); - bool append_with_peer_gray(const peerlist_entry& pr); - bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); - bool set_peer_just_seen(peerid_type peer, const net_address& addr); - bool set_peer_unreachable(const peerlist_entry& pr); - bool is_ip_allowed(uint32_t ip); - void trim_white_peerlist(); - void trim_gray_peerlist(); - - - private: - struct by_time{}; - struct by_id{}; - struct by_addr{}; - - struct modify_all_but_id - { - modify_all_but_id(const peerlist_entry& ple):m_ple(ple){} - void operator()(peerlist_entry& e) - { - e.id = m_ple.id; - } - private: - const peerlist_entry& m_ple; - }; - - struct modify_all - { - modify_all(const peerlist_entry& ple):m_ple(ple){} - void operator()(peerlist_entry& e) - { - e = m_ple; - } - private: - const peerlist_entry& m_ple; - }; - - typedef boost::multi_index_container< - peerlist_entry, - boost::multi_index::indexed_by< - // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, - // sort by peerlist_entry::last_seen< - boost::multi_index::ordered_non_unique, boost::multi_index::member > - > - > peers_indexed; - - typedef boost::multi_index_container< - peerlist_entry, - boost::multi_index::indexed_by< - // access by peerlist_entry::id< - boost::multi_index::ordered_unique, boost::multi_index::member >, - // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, - // sort by peerlist_entry::last_seen< - boost::multi_index::ordered_non_unique, boost::multi_index::member > - > - > peers_indexed_old; - public: - - template - void serialize(Archive &a, const t_version_type ver) - { - if(ver < 3) - return; - CRITICAL_REGION_LOCAL(m_peerlist_lock); - if(ver < 4) - { - //loading data from old storage - peers_indexed_old pio; - a & pio; - peers_indexed_from_old(pio, m_peers_white); - return; - } - a & m_peers_white; - a & m_peers_gray; - } - - private: - bool peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi); - - friend class boost::serialization::access; - epee::critical_section m_peerlist_lock; - std::string m_config_folder; - bool m_allow_local_ip; - - - peers_indexed m_peers_gray; - peers_indexed m_peers_white; - }; - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::init(bool allow_local_ip) - { - m_allow_local_ip = allow_local_ip; - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::deinit() - { - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi) - { - for(auto x: pio) - { - auto by_addr_it = pi.get().find(x.adr); - if(by_addr_it == pi.get().end()) - { - pi.insert(x); - } - } - - return true; - } - //-------------------------------------------------------------------------------------------------- - inline void peerlist_manager::trim_white_peerlist() - { - while(m_peers_gray.size() > cryptonote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) - { - peers_indexed::index::type& sorted_index=m_peers_gray.get(); - sorted_index.erase(sorted_index.begin()); - } - } - //-------------------------------------------------------------------------------------------------- - inline void peerlist_manager::trim_gray_peerlist() - { - while(m_peers_white.size() > cryptonote::P2P_LOCAL_WHITE_PEERLIST_LIMIT) - { - peers_indexed::index::type& sorted_index=m_peers_white.get(); - sorted_index.erase(sorted_index.begin()); - } - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::merge_peerlist(const std::list& outer_bs) - { - CRITICAL_REGION_LOCAL(m_peerlist_lock); - BOOST_FOREACH(const peerlist_entry& be, outer_bs) - { - append_with_peer_gray(be); - } - // delete extra elements - trim_gray_peerlist(); - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::get_white_peer_by_index(peerlist_entry& p, size_t i) - { - CRITICAL_REGION_LOCAL(m_peerlist_lock); - if(i >= m_peers_white.size()) - return false; - - peers_indexed::index::type& by_time_index = m_peers_white.get(); - p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i); - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::get_gray_peer_by_index(peerlist_entry& p, size_t i) - { - CRITICAL_REGION_LOCAL(m_peerlist_lock); - if(i >= m_peers_gray.size()) - return false; - - peers_indexed::index::type& by_time_index = m_peers_gray.get(); - p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i); - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::is_ip_allowed(uint32_t ip) - { - //never allow loopback ip - if(epee::net_utils::is_ip_loopback(ip)) - return false; - - if(!m_allow_local_ip && epee::net_utils::is_ip_local(ip)) - return false; - - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::get_peerlist_head(std::list& 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; - BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index) - { - if(!vl.last_seen) - continue; - bs_head.push_back(vl); - if(cnt++ > depth) - break; - } - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) - { - CRITICAL_REGION_LOCAL(m_peerlist_lock); - peers_indexed::index::type& by_time_index_gr=m_peers_gray.get(); - BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index_gr) - { - pl_gray.push_back(vl); - } - - peers_indexed::index::type& by_time_index_wt=m_peers_white.get(); - BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index_wt) - { - pl_white.push_back(vl); - } - - return true; - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port) - { - net_address addr; - addr.ip = ip; - addr.port = port; - return set_peer_just_seen(peer, addr); - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) - { - TRY_ENTRY(); - CRITICAL_REGION_LOCAL(m_peerlist_lock); - //find in white list - peerlist_entry ple; - ple.adr = addr; - ple.id = peer; - ple.last_seen = time(NULL); - return append_with_peer_white(ple); - CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false); - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) - { - TRY_ENTRY(); - if(!is_ip_allowed(ple.adr.ip)) - return true; - - CRITICAL_REGION_LOCAL(m_peerlist_lock); - //find in white list - auto by_addr_it_wt = m_peers_white.get().find(ple.adr); - if(by_addr_it_wt == m_peers_white.get().end()) - { - //put new record into white list - m_peers_white.insert(ple); - trim_white_peerlist(); - }else - { - //update record in white list - m_peers_white.replace(by_addr_it_wt, ple); - } - //remove from gray list, if need - auto by_addr_it_gr = m_peers_gray.get().find(ple.adr); - if(by_addr_it_gr != m_peers_gray.get().end()) - { - m_peers_gray.erase(by_addr_it_gr); - } - return true; - CATCH_ENTRY_L0("peerlist_manager::append_with_peer_white()", false); - } - //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) - { - TRY_ENTRY(); - if(!is_ip_allowed(ple.adr.ip)) - return true; - - CRITICAL_REGION_LOCAL(m_peerlist_lock); - //find in white list - auto by_addr_it_wt = m_peers_white.get().find(ple.adr); - if(by_addr_it_wt != m_peers_white.get().end()) - return true; - - //update gray list - auto by_addr_it_gr = m_peers_gray.get().find(ple.adr); - if(by_addr_it_gr == m_peers_gray.get().end()) - { - //put new record into white list - m_peers_gray.insert(ple); - trim_gray_peerlist(); - }else - { - //update record in white list - m_peers_gray.replace(by_addr_it_gr, ple); - } - return true; - CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false); - return true; - } - //-------------------------------------------------------------------------------------------------- -} - -BOOST_CLASS_VERSION(nodetool::peerlist_manager, 4) diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h index cc6961da5c..f051b8150c 100644 --- a/src/p2p/net_peerlist_boost_serialization.h +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,7 @@ namespace boost { //BOOST_CLASS_VERSION(odetool::net_adress, 1) template - inline void serialize(Archive &a, nodetool::net_address& na, const ver_type ver) + inline void serialize(Archive &a, CryptoNote::net_address& na, const ver_type ver) { a & na.ip; a & na.port; @@ -31,7 +31,7 @@ namespace boost template - inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver) + inline void serialize(Archive &a, CryptoNote::peerlist_entry& pl, const ver_type ver) { a & pl.adr; a & pl.id; diff --git a/src/p2p/p2p_networks.h b/src/p2p/p2p_networks.h index 884c13051b..a1790e8c49 100644 --- a/src/p2p/p2p_networks.h +++ b/src/p2p/p2p_networks.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #pragma once -namespace nodetool +namespace CryptoNote { const static boost::uuids::uuid BYTECOIN_NETWORK = { { 0x11 ,0x10, 0x01, 0x11 , 0x11, 0x00 , 0x01, 0x01, 0x10, 0x11, 0x00, 0x12, 0x10, 0x11, 0x01, 0x10} }; //Bender's nightmare } diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 46213dfb5b..198533b2e2 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,71 +17,17 @@ #pragma once -#include +#include "p2p_protocol_types.h" -#include "serialization/keyvalue_serialization.h" -#include "misc_language.h" -#include "string_tools.h" -#include "time_helper.h" - -#include "cryptonote_config.h" #include "crypto/crypto.h" +#include "cryptonote_config.h" +#include "cryptonote_core/cryptonote_stat_info.h" -namespace nodetool -{ - typedef boost::uuids::uuid uuid; - typedef uint64_t peerid_type; - -#pragma pack (push, 1) - - struct net_address - { - uint32_t ip; - uint32_t port; - }; - - struct peerlist_entry - { - net_address adr; - peerid_type id; - time_t last_seen; - }; - - struct connection_entry - { - net_address adr; - peerid_type id; - bool is_income; - }; - -#pragma pack(pop) - - inline - bool operator < (const net_address& a, const net_address& b) - { - return epee::misc_utils::is_less_as_pod(a, b); - } - - inline - bool operator == (const net_address& a, const net_address& b) - { - return memcmp(&a, &b, sizeof(a)) == 0; - } - inline - std::string print_peerlist_to_string(const std::list& pl) - { - time_t now_time = 0; - time(&now_time); - std::stringstream ss; - ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; - BOOST_FOREACH(const peerlist_entry& pe, pl) - { - ss << pe.id << "\t" << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; - } - return ss.str(); - } - +// epee +#include "serialization/keyvalue_serialization.h" +namespace CryptoNote +{ struct network_config { BEGIN_KV_SERIALIZE_MAP() @@ -115,21 +61,30 @@ namespace nodetool END_KV_SERIALIZE_MAP() }; + struct CORE_SYNC_DATA + { + uint64_t current_height; + crypto::hash top_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(current_height) + KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) + END_KV_SERIALIZE_MAP() + }; #define P2P_COMMANDS_POOL_BASE 1000 /************************************************************************/ /* */ /************************************************************************/ - template - struct COMMAND_HANDSHAKE_T - { - const static int ID = P2P_COMMANDS_POOL_BASE + 1; + struct COMMAND_HANDSHAKE + { + const static int ID = P2P_COMMANDS_POOL_BASE + 1; struct request { basic_node_data node_data; - t_playload_type payload_data; + CORE_SYNC_DATA payload_data; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(node_data) @@ -140,7 +95,7 @@ namespace nodetool struct response { basic_node_data node_data; - t_playload_type payload_data; + CORE_SYNC_DATA payload_data; std::list local_peerlist; BEGIN_KV_SERIALIZE_MAP() @@ -149,20 +104,19 @@ namespace nodetool KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) END_KV_SERIALIZE_MAP() }; - }; + }; /************************************************************************/ /* */ /************************************************************************/ - template - struct COMMAND_TIMED_SYNC_T + struct COMMAND_TIMED_SYNC { const static int ID = P2P_COMMANDS_POOL_BASE + 2; struct request { - t_playload_type payload_data; + CORE_SYNC_DATA payload_data; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(payload_data) END_KV_SERIALIZE_MAP() @@ -171,7 +125,7 @@ namespace nodetool struct response { uint64_t local_time; - t_playload_type payload_data; + CORE_SYNC_DATA payload_data; std::list local_peerlist; BEGIN_KV_SERIALIZE_MAP() @@ -235,9 +189,14 @@ namespace nodetool END_KV_SERIALIZE_MAP() }; + inline crypto::hash get_proof_of_trust_hash(const proof_of_trust& pot) { + std::string s; + s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); + s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); + return crypto::cn_fast_hash(s.data(), s.size()); + } - template - struct COMMAND_REQUEST_STAT_INFO_T + struct COMMAND_REQUEST_STAT_INFO { const static int ID = P2P_COMMANDS_POOL_BASE + 4; @@ -255,7 +214,7 @@ namespace nodetool std::string os_version; uint64_t connections_count; uint64_t incoming_connections_count; - payload_stat_info payload_info; + core_stat_info payload_info; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(version) diff --git a/src/p2p/p2p_protocol_types.h b/src/p2p/p2p_protocol_types.h new file mode 100644 index 0000000000..27a5510022 --- /dev/null +++ b/src/p2p/p2p_protocol_types.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include "Common/StringTools.h" + +namespace CryptoNote +{ + typedef boost::uuids::uuid uuid; + typedef boost::uuids::uuid net_connection_id; + typedef uint64_t peerid_type; + +#pragma pack (push, 1) + + struct net_address + { + uint32_t ip; + uint32_t port; + }; + + struct peerlist_entry + { + net_address adr; + peerid_type id; + time_t last_seen; + }; + + struct connection_entry + { + net_address adr; + peerid_type id; + bool is_income; + }; + +#pragma pack(pop) + + inline bool operator < (const net_address& a, const net_address& b) { + return std::tie(a.ip, a.port) < std::tie(b.ip, b.port); + } + + inline bool operator == (const net_address& a, const net_address& b) { + return memcmp(&a, &b, sizeof(a)) == 0; + } + + inline std::ostream& operator << (std::ostream& s, const net_address& na) { + return s << Common::ipAddressToString(na.ip) << ":" << std::to_string(na.port); + } + + inline uint32_t hostToNetwork(uint32_t n) { + return (n << 24) | (n & 0xff00) << 8 | (n & 0xff0000) >> 8 | (n >> 24); + } + + inline uint32_t networkToHost(uint32_t n) { + return hostToNetwork(n); // the same + } + +} diff --git a/src/payment_service/ConfigurationManager.cpp b/src/payment_service/ConfigurationManager.cpp new file mode 100644 index 0000000000..75ed1ee7e9 --- /dev/null +++ b/src/payment_service/ConfigurationManager.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ConfigurationManager.h" + +#include +#include + +#include "Common/command_line.h" +#include "Common/util.h" + +namespace PaymentService { + +namespace po = boost::program_options; + +ConfigurationManager::ConfigurationManager() { + startInprocess = false; +} + +bool ConfigurationManager::init(int argc, char** argv) { + po::options_description cmdGeneralOptions("Common Options"); + + cmdGeneralOptions.add_options() + ("config,c", po::value(), "configuration file"); + + po::options_description confGeneralOptions; + confGeneralOptions.add(cmdGeneralOptions).add_options() + ("testnet", po::value(), "") + ("local", po::value(), ""); + + cmdGeneralOptions.add_options() + ("help,h", "produce this help message and exit") + ("local", "start with local node (remote is default)") + ("testnet", "testnet mode"); + + command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, tools::get_default_data_dir()); + + Configuration::initOptions(cmdGeneralOptions); + Configuration::initOptions(confGeneralOptions); + + po::options_description netNodeOptions("Local Node Options"); + CryptoNote::NetNodeConfig::initOptions(netNodeOptions); + CryptoNote::CoreConfig::initOptions(netNodeOptions); + + po::options_description remoteNodeOptions("Remote Node Options"); + RpcNodeConfiguration::initOptions(remoteNodeOptions); + + po::options_description cmdOptionsDesc; + cmdOptionsDesc.add(cmdGeneralOptions).add(remoteNodeOptions).add(netNodeOptions); + + po::options_description confOptionsDesc; + confOptionsDesc.add(confGeneralOptions).add(remoteNodeOptions).add(netNodeOptions); + + po::variables_map cmdOptions; + po::store(po::parse_command_line(argc, argv, cmdOptionsDesc), cmdOptions); + po::notify(cmdOptions); + + if (cmdOptions.count("help")) { + std::cout << cmdOptionsDesc << std::endl; + return false; + } + + if (cmdOptions.count("config")) { + std::ifstream confStream(cmdOptions["config"].as(), std::ifstream::in); + if (!confStream.good()) { + throw ConfigurationError("Cannot open configuration file"); + } + + po::variables_map confOptions; + po::store(po::parse_config_file(confStream, confOptionsDesc), confOptions); + po::notify(confOptions); + + gateConfiguration.init(confOptions); + netNodeConfig.init(confOptions); + coreConfig.init(confOptions); + remoteNodeConfig.init(confOptions); + + if (confOptions.count("local")) { + startInprocess = confOptions["local"].as(); + } + } + + //command line options should override options from config file + gateConfiguration.init(cmdOptions); + netNodeConfig.init(cmdOptions); + coreConfig.init(cmdOptions); + remoteNodeConfig.init(cmdOptions); + + if (cmdOptions.count("local")) { + startInprocess = true; + } + + return true; +} + +} //namespace PaymentService diff --git a/src/payment_service/ConfigurationManager.h b/src/payment_service/ConfigurationManager.h new file mode 100644 index 0000000000..a372a56d12 --- /dev/null +++ b/src/payment_service/ConfigurationManager.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/CoreConfig.h" +#include "PaymentServiceConfiguration.h" +#include "p2p/NetNodeConfig.h" +#include "RpcNodeConfiguration.h" + +namespace PaymentService { + +class ConfigurationManager { +public: + ConfigurationManager(); + bool init(int argc, char** argv); + + bool startInprocess; + Configuration gateConfiguration; + CryptoNote::NetNodeConfig netNodeConfig; + CryptoNote::CoreConfig coreConfig; + RpcNodeConfiguration remoteNodeConfig; +}; + +} //namespace PaymentService diff --git a/src/payment_service/JsonRpcMessages.cpp b/src/payment_service/JsonRpcMessages.cpp new file mode 100644 index 0000000000..7edac46673 --- /dev/null +++ b/src/payment_service/JsonRpcMessages.cpp @@ -0,0 +1,248 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonRpcMessages.h" +#include "serialization/SerializationOverloads.h" + +namespace PaymentService { + +namespace { + +void throwIfRequiredParamsMissing(CryptoNote::ISerializer& serializer, const std::vector& names) { + bool r = true; + for (const auto name: names) { + r &= serializer.hasObject(name); + } + + if (!r) { + throw RequestSerializationError(); + } +} + +void throwIfRequiredParamsMissing(CryptoNote::ISerializer& serializer, const char* name) { + throwIfRequiredParamsMissing(serializer, std::vector{name}); +} + +} + +void TransferDestination::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + throwIfRequiredParamsMissing(serializer, {"amount", "address"}); + serializer(amount, "amount"); + serializer(address, "address"); + serializer.endObject(); +} + +void SendTransactionRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + throwIfRequiredParamsMissing(serializer, {"destinations", "fee", "mixin"}); + + serializer.beginObject(name); + + size_t size = destinations.size(); + serializer.beginArray(size, "destinations"); + destinations.resize(size); + + auto it = destinations.begin(); + for (size_t i = 0; i < size; ++i, ++it) { + it->serialize(serializer, ""); + } + serializer.endArray(); + + serializer(fee, "fee"); + serializer(mixin, "mixin"); + + if (serializer.hasObject("unlock_time")) { + serializer(unlockTime, "unlock_time"); + } + + if (serializer.hasObject("payment_id")) { + serializer(paymentId, "payment_id"); + } + + serializer.endObject(); +} + +void SendTransactionResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(transactionId, "transaction_id"); + serializer.endObject(); +} + +void GetAddressResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(address, "address"); + serializer.endObject(); +} + +void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(actualBalance, "actual_balance"); + serializer.endObject(); +} + +void GetPendingBalanceResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(pendingBalance, "pending_balance"); + serializer.endObject(); +} + +void GetTransactionsCountResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(transactionsCount, "transactions_count"); + serializer.endObject(); +} + +void GetTransfersCountResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(transfersCount, "transfers_count"); + serializer.endObject(); +} + +void GetTransactionIdByTransferIdRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + throwIfRequiredParamsMissing(serializer, "transfer_id"); + + serializer.beginObject(name); + serializer(transferId, "transfer_id"); + serializer.endObject(); +} + +void GetTransactionIdByTransferIdResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(transactionid, "transaction_id"); + serializer.endObject(); +} + +void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + throwIfRequiredParamsMissing(serializer, "transaction_id"); + + serializer.beginObject(name); + serializer(transactionId, "transaction_id"); + serializer.endObject(); +} + +void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + + serializer(firstTransferId, "first_transfer_id"); + serializer(transferCount, "transfer_count"); + serializer(totalAmount, "total_amount"); + serializer(fee, "fee"); + serializer(hash, "hash"); + serializer(isCoinbase, "is_coin_base"); + serializer(blockHeight, "block_height"); + serializer(timestamp, "timestamp"); + serializer(extra, "extra"); + + serializer.endObject(); +} + +void GetTransactionResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + + serializer(found, "found"); + + if (!found) { + serializer.endObject(); + return; + } + + transactionInfo.serialize(serializer, "transaction_info"); + + serializer.endObject(); +} + +void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(address, "address"); + serializer(amount, "amount"); + serializer.endObject(); +} + +void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + throwIfRequiredParamsMissing(serializer, "transfer_id"); + + serializer.beginObject(name); + serializer(transferId, "transfer_id"); + serializer.endObject(); +} + +void GetTransferResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(found, "found"); + + if (!found) { + serializer.endObject(); + return; + } + + transferInfo.serialize(serializer, "transfer_info"); + + serializer.endObject(); +} + +void GetIncomingPaymentsRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + throwIfRequiredParamsMissing(serializer, "payments"); + + serializer.beginObject(name); + serializer(payments, "payments"); + serializer.endObject(); +} + +void PaymentsById::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + + serializer(id, "id"); + serializer(payments, "payments"); + + serializer.endObject(); +} + +void GetIncomingPaymentsResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + + serializer(payments, "payments"); + + serializer.endObject(); +} + +void PaymentDetails::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { + serializer.beginObject(name); + serializer(txHash, "tx_hash"); + serializer(amount, "amount"); + serializer(blockHeight, "block_height"); + serializer(unlockTime, "unlock_time"); + serializer.endObject(); +} + +} diff --git a/src/payment_service/JsonRpcMessages.h b/src/payment_service/JsonRpcMessages.h new file mode 100644 index 0000000000..098e75ccb4 --- /dev/null +++ b/src/payment_service/JsonRpcMessages.h @@ -0,0 +1,188 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "serialization/ISerializer.h" +#include +#include + +namespace PaymentService { + +class RequestSerializationError: public std::exception { +public: + virtual const char* what() const throw() override { return "Request error"; } +}; + +struct TransferDestination { + uint64_t amount; + std::string address; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct SendTransactionRequest { + SendTransactionRequest() : unlockTime(0) {} + std::vector destinations; + uint64_t fee; + uint64_t mixin; + uint64_t unlockTime; + std::string paymentId; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct SendTransactionResponse { + uint64_t transactionId; + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetAddressResponse { + std::string address; + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetActualBalanceResponse { + uint64_t actualBalance; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetPendingBalanceResponse { + uint64_t pendingBalance; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransactionsCountResponse { + uint64_t transactionsCount; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransfersCountResponse { + uint64_t transfersCount; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransactionIdByTransferIdRequest { + uint64_t transferId; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransactionIdByTransferIdResponse { + uint64_t transactionid; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransactionRequest { + uint64_t transactionId; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct TransactionRpcInfo { + uint64_t firstTransferId; + uint64_t transferCount; + int64_t totalAmount; + uint64_t fee; + std::string hash; + bool isCoinbase; + uint64_t blockHeight; + uint64_t timestamp; + std::string extra; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransactionResponse { + bool found; + TransactionRpcInfo transactionInfo; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct TransferRpcInfo { + std::string address; + int64_t amount; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransferRequest { + uint64_t transferId; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetTransferResponse { + bool found; + TransferRpcInfo transferInfo; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetIncomingPaymentsRequest { + std::vector payments; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct PaymentDetails +{ + std::string txHash; + uint64_t amount; + uint64_t blockHeight; + uint64_t unlockTime; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct PaymentsById { + std::string id; + std::vector payments; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +struct GetIncomingPaymentsResponse { + std::vector payments; + + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); +}; + +} //namespace PaymentService diff --git a/src/payment_service/JsonRpcServer.cpp b/src/payment_service/JsonRpcServer.cpp new file mode 100644 index 0000000000..fbd604f5bd --- /dev/null +++ b/src/payment_service/JsonRpcServer.cpp @@ -0,0 +1,451 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonRpcServer.h" + +#include +#include +#include +#include +#include +#include "HTTP/HttpParserErrorCodes.h" + +#include +#include +#include +#include +#include +#include "HTTP/HttpParser.h" +#include "HTTP/HttpResponse.h" +#include "JsonRpcMessages.h" +#include "WalletService.h" +#include "WalletServiceErrorCodes.h" + +#include "Common/JsonValue.h" +#include "serialization/JsonInputValueSerializer.h" +#include "serialization/JsonOutputStreamSerializer.h" + +namespace PaymentService { + +JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) : + system(sys), + stopEvent(stopEvent), + service(service), + logger(loggerGroup, "JsonRpcServer") +{ +} + +void JsonRpcServer::start(const Configuration& config) { + logger(Logging::INFO) << "Starting server on " << config.bindAddress << ":" << config.bindPort; + + try { + System::TcpListener listener(system, System::Ipv4Address(config.bindAddress), config.bindPort); + system.spawn([this, &listener] () {this->stopEvent.wait(); listener.stop(); }); + for (;;) { + System::TcpConnection connection = listener.accept(); + system.spawn(std::bind(&JsonRpcServer::sessionProcedure, this, new System::TcpConnection(std::move(connection)))); + } + } catch (System::InterruptedException&) { + logger(Logging::DEBUGGING) << "Server is stopped"; + } catch (std::exception& ex) { + logger(Logging::FATAL) << ex.what(); + } +} + +void JsonRpcServer::sessionProcedure(System::TcpConnection* tcpConnection) { + logger(Logging::DEBUGGING) << "new connection has been accepted"; + std::unique_ptr connection(tcpConnection); + + System::TcpStreambuf streambuf(*connection); + std::iostream stream(&streambuf); + + CryptoNote::HttpParser parser; + + try { + for (;;) { + CryptoNote::HttpRequest req; + CryptoNote::HttpResponse resp; + + parser.receiveRequest(stream, req); + processHttpRequest(req, resp); + + stream << resp; + stream.flush(); + } + } catch (std::system_error& e) { + //todo: write error conditions + if (e.code().category() == CryptoNote::error::HttpParserErrorCategory::INSTANCE) { + if (e.code().value() == CryptoNote::error::END_OF_STREAM) { + logger(Logging::DEBUGGING) << "The client is disconnected"; + return; + } + } + logger(Logging::WARNING) << e.code().message(); + } catch (std::exception& e) { + logger(Logging::WARNING) << e.what(); + } +} + +void JsonRpcServer::processHttpRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { + try { + logger(Logging::TRACE) << "HTTP request came: \n" << req; + + if (req.getUrl() == "/json_rpc") { + std::stringstream jsonInputStream(req.getBody()); + Common::JsonValue jsonRpcRequest; + Common::JsonValue jsonRpcResponse(Common::JsonValue::OBJECT); + + try { + jsonInputStream >> jsonRpcRequest; + } catch (std::runtime_error&) { + logger(Logging::WARNING) << "Couldn't parse request: \"" << req.getBody() << "\""; + makeJsonParsingErrorResponse(jsonRpcResponse); + resp.setStatus(CryptoNote::HttpResponse::STATUS_200); + resp.setBody(jsonRpcResponse.toString()); + return; + } + + processJsonRpcRequest(jsonRpcRequest, jsonRpcResponse); + + std::stringstream jsonOutputStream; + jsonOutputStream << jsonRpcResponse; + + resp.setStatus(CryptoNote::HttpResponse::STATUS_200); + resp.setBody(jsonOutputStream.str()); + + } else { + logger(Logging::WARNING) << "Requested url \"" << req.getUrl() << "\" is not found"; + resp.setStatus(CryptoNote::HttpResponse::STATUS_404); + return; + } + } catch (std::exception& e) { + logger(Logging::WARNING) << "Error while processing http request: " << e.what(); + resp.setStatus(CryptoNote::HttpResponse::STATUS_500); + } +} + +void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { + try { + prepareJsonResponse(req, resp); + + std::string method = req("method").getString(); + + CryptoNote::JsonInputValueSerializer inputSerializer; + CryptoNote::JsonOutputStreamSerializer outputSerializer; + + inputSerializer.setJsonValue(&req("params")); + + if (method == "send_transaction") { + SendTransactionRequest sendReq; + SendTransactionResponse sendResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + sendReq.serialize(inputSerializer, ""); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.sendTransaction(sendReq, sendResp); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + sendResp.serialize(outputSerializer, ""); + } else if (method == "get_address") { + GetAddressResponse getAddrResp; + + std::error_code ec = service.getAddress(getAddrResp.address); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + getAddrResp.serialize(outputSerializer, ""); + } else if (method == "get_actual_balance") { + GetActualBalanceResponse actualResp; + + std::error_code ec = service.getActualBalance(actualResp.actualBalance); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + actualResp.serialize(outputSerializer, ""); + } else if (method == "get_pending_balance") { + GetPendingBalanceResponse pendingResp; + + std::error_code ec = service.getPendingBalance(pendingResp.pendingBalance); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + pendingResp.serialize(outputSerializer, ""); + } else if (method == "get_transactions_count") { + GetTransactionsCountResponse txResp; + + std::error_code ec = service.getTransactionsCount(txResp.transactionsCount); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + txResp.serialize(outputSerializer, ""); + } else if (method == "get_transfers_count") { + GetTransfersCountResponse trResp; + + std::error_code ec = service.getTransfersCount(trResp.transfersCount); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + trResp.serialize(outputSerializer, ""); + } else if (method == "get_transaction_id_by_transfer_id") { + GetTransactionIdByTransferIdRequest getReq; + GetTransactionIdByTransferIdResponse getResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + getReq.serialize(inputSerializer, ""); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + CryptoNote::TransactionId txId; + std::error_code ec = service.getTransactionByTransferId(getReq.transferId, txId); + getResp.transactionid = txId; + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + getResp.serialize(outputSerializer, ""); + } else if (method == "get_transaction") { + GetTransactionRequest getReq; + GetTransactionResponse getResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + getReq.serialize(inputSerializer, ""); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.getTransaction(getReq.transactionId, getResp.found, getResp.transactionInfo); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + getResp.serialize(outputSerializer, ""); + } else if (method == "get_transfer") { + GetTransferRequest getReq; + GetTransferResponse getResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + getReq.serialize(inputSerializer, ""); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.getTransfer(getReq.transferId, getResp.found, getResp.transferInfo); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + getResp.serialize(outputSerializer, ""); + } else if (method == "get_incoming_payments") { + GetIncomingPaymentsRequest getReq; + GetIncomingPaymentsResponse getResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + getReq.serialize(inputSerializer, ""); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + WalletService::IncomingPayments payments; + std::error_code ec = service.getIncomingPayments(getReq.payments, payments); + if (ec) { + if (ec == make_error_code(PaymentService::error::REQUEST_ERROR)) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + } else { + makeErrorResponse(ec, resp); + } + + return; + } + + for (auto p: payments) { + PaymentsById pbid; + pbid.id = std::move(p.first); + pbid.payments = std::move(p.second); + + getResp.payments.push_back(std::move(pbid)); + } + + getResp.serialize(outputSerializer, ""); + } else { + logger(Logging::DEBUGGING) << "Requested method not found: " << method; + makeMethodNotFoundResponse(resp); + return; + } + + Common::JsonValue v = outputSerializer.getJsonValue(); + fillJsonResponse(v, resp); + + } catch (RequestSerializationError&) { + logger(Logging::WARNING) << "Wrong request came"; + makeGenericErrorReponse(resp, "Invalid Request", -32600); + } catch (std::exception& e) { + logger(Logging::WARNING) << "Error occured while processing JsonRpc request"; + makeGenericErrorReponse(resp, e.what()); + } +} + +void JsonRpcServer::prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp) { + using Common::JsonValue; + + if (req.count("id")) { + JsonValue id = req("id"); + resp.insert("id", id); + } + + JsonValue jsonRpc; + jsonRpc = "2.0"; + + resp.insert("jsonrpc", jsonRpc); +} + +void JsonRpcServer::makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32000); //Application specific error code + + JsonValue message; + message = ec.message(); + + JsonValue data(JsonValue::OBJECT); + JsonValue appCode; + appCode = static_cast(ec.value()); + data.insert("application_code", appCode); + + error.insert("code", code); + error.insert("message", message); + error.insert("data", data); + + resp.insert("error", error); +} + +void JsonRpcServer::makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(errorCode); + + std::string msg; + if (what) { + msg = what; + } else { + msg = "Unknown application error"; + } + + JsonValue message; + message = msg; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); + +} + +void JsonRpcServer::makeMethodNotFoundResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32601); //ambigous declaration of JsonValue::operator= (between int and JsonValue) + + JsonValue message; + message = "Method not found"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + +void JsonRpcServer::fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp) { + resp.insert("result", v); +} + +void JsonRpcServer::makeJsonParsingErrorResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + resp = JsonValue(JsonValue::OBJECT); + resp.insert("jsonrpc", "2.0"); + resp.insert("id", nullptr); + + JsonValue error(JsonValue::OBJECT); + JsonValue code; + code = static_cast(-32700); //ambigous declaration of JsonValue::operator= (between int and JsonValue) + + JsonValue message; + message = "Parse error"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + +} diff --git a/src/payment_service/JsonRpcServer.h b/src/payment_service/JsonRpcServer.h new file mode 100644 index 0000000000..63a66117c6 --- /dev/null +++ b/src/payment_service/JsonRpcServer.h @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "PaymentServiceConfiguration.h" +#include +#include +#include "Logging/ILogger.h" +#include "Logging/LoggerRef.h" + +#include + +namespace CryptoNote { +class HttpResponse; +class HttpRequest; +} + +namespace Common { +class JsonValue; +} + +namespace System { +class TcpConnection; +} + +namespace PaymentService { + +class WalletService; + +class JsonRpcServer { +public: + JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); + JsonRpcServer(const JsonRpcServer&) = delete; + + void start(const Configuration& config); + +private: + void sessionProcedure(System::TcpConnection* tcpConnection); + + void processHttpRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp); + void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp); + void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); + + void makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp); + void makeMethodNotFoundResponse(Common::JsonValue& resp); + void makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode = -32001); + void makeJsonParsingErrorResponse(Common::JsonValue& resp); + + void fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp); + + System::Dispatcher& system; + System::Event& stopEvent; + WalletService& service; + Logging::LoggerRef logger; +}; + +} //namespace PaymentService diff --git a/src/payment_service/NodeFactory.cpp b/src/payment_service/NodeFactory.cpp new file mode 100644 index 0000000000..a7e86b72ab --- /dev/null +++ b/src/payment_service/NodeFactory.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "NodeFactory.h" + +#include "node_rpc_proxy/NodeRpcProxy.h" +#include +#include + +namespace PaymentService { + +class NodeRpcStub: public CryptoNote::INode { +public: + virtual ~NodeRpcStub() {} + virtual bool addObserver(CryptoNote::INodeObserver* observer) { return true; } + virtual bool removeObserver(CryptoNote::INodeObserver* observer) { return true; } + + virtual void init(const Callback& callback) { } + virtual bool shutdown() { return true; } + + virtual size_t getPeerCount() const { return 0; } + virtual uint64_t getLastLocalBlockHeight() const { return 0; } + virtual uint64_t getLastKnownBlockHeight() const { return 0; } + virtual uint64_t getLocalBlockCount() const override { return 0; } + virtual uint64_t getKnownBlockCount() const override { return 0; } + virtual uint64_t getLastLocalBlockTimestamp() const { return 0; } + + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { callback(std::error_code()); } + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, + std::vector& result, const Callback& callback) { callback(std::error_code()); } + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { callback(std::error_code()); } + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); } + + virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, + uint64_t& startHeight, const CryptoNote::INode::Callback& callback) { startHeight = 0; callback(std::error_code()); } + + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, + bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, + const Callback& callback) { callback(std::error_code()); } + +}; + + +class NodeInitObserver { +public: + NodeInitObserver() {} + + void initCompleted(std::error_code result) { + initPromise.set_value(result); + } + + void waitForInitEnd() { + auto future = initPromise.get_future(); + + std::error_code ec = future.get(); + if (ec) { + throw std::system_error(ec); + } + return; + } + +private: + std::promise initPromise; +}; + +NodeFactory::NodeFactory() { +} + +NodeFactory::~NodeFactory() { +} + +CryptoNote::INode* NodeFactory::createNode(const std::string& daemonAddress, uint16_t daemonPort) { + std::unique_ptr node(new CryptoNote::NodeRpcProxy(daemonAddress, daemonPort)); + + NodeInitObserver initObserver; + node->init(std::bind(&NodeInitObserver::initCompleted, &initObserver, std::placeholders::_1)); + initObserver.waitForInitEnd(); + + return node.release(); +} + +CryptoNote::INode* NodeFactory::createNodeStub() { + return new NodeRpcStub(); +} + +} diff --git a/src/payment_service/NodeFactory.h b/src/payment_service/NodeFactory.h new file mode 100644 index 0000000000..fb50c3ff2c --- /dev/null +++ b/src/payment_service/NodeFactory.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "INode.h" + +#include + +namespace PaymentService { + +class NodeFactory { +public: + static CryptoNote::INode* createNode(const std::string& daemonAddress, uint16_t daemonPort); + static CryptoNote::INode* createNodeStub(); +private: + NodeFactory(); + ~NodeFactory(); + + CryptoNote::INode* getNode(const std::string& daemonAddress, uint16_t daemonPort); + + static NodeFactory factory; +}; + +} //namespace PaymentService diff --git a/src/payment_service/PaymentServiceConfiguration.cpp b/src/payment_service/PaymentServiceConfiguration.cpp new file mode 100644 index 0000000000..71d51f1ba4 --- /dev/null +++ b/src/payment_service/PaymentServiceConfiguration.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "PaymentServiceConfiguration.h" + +#include +#include +#include + +#include "Logging/ILogger.h" + +namespace po = boost::program_options; + +namespace PaymentService { + +Configuration::Configuration() { + generateNewWallet = false; + daemonize = false; + registerService = false; + unregisterService = false; + logFile = "payment_gate.log"; + testnet = false; + logLevel = Logging::INFO; +} + +void Configuration::initOptions(boost::program_options::options_description& desc) { + desc.add_options() + ("bind-address", po::value()->default_value("0.0.0.0"), "payment service bind address") + ("bind-port", po::value()->default_value(8070), "payment service bind port") + ("wallet-file,w", po::value(), "wallet file") + ("wallet-password,p", po::value(), "wallet password") + ("generate-wallet,g", "generate new wallet file and exit") + ("daemon,d", "run as daemon in Unix or as service in Windows") + ("register-service", "register service and exit (Windows only)") + ("unregister-service", "unregister service and exit (Windows only)") + ("import-keys,i", po::value(), "import legacy keys file and exit") + ("log-file,l", po::value(), "log file") + ("server-root", po::value(), "server root. The service will use it as working directory. Don't set it if don't want to change it") + ("log-level", po::value(), "log level"); +} + +void Configuration::init(const boost::program_options::variables_map& options) { + if (options.count("daemon")) { + daemonize = true; + } + + if (options.count("register-service")) { + registerService = true; + } + + if (options.count("unregister-service")) { + unregisterService = true; + } + + if (registerService && unregisterService) { + throw ConfigurationError("It's impossible to use both \"register-service\" and \"unregister-service\" at the same time"); + } + + if (options.count("testnet")) { + testnet = true; + } + + if (options.count("log-file")) { + logFile = options["log-file"].as(); + } + + if (options.count("log-level")) { + logLevel = options["log-level"].as(); + if (logLevel > Logging::TRACE) { + std::string error = "log-level option must be in " + std::to_string(Logging::FATAL) + ".." + std::to_string(Logging::TRACE) + " interval"; + throw ConfigurationError(error.c_str()); + } + } + + if (options.count("server-root")) { + serverRoot = options["server-root"].as(); + } + + if (options.count("bind-address")) { + bindAddress = options["bind-address"].as(); + } + + if (options.count("bind-port")) { + bindPort = options["bind-port"].as(); + } + + if (options.count("wallet-file")) { + walletFile = options["wallet-file"].as(); + } + + if (options.count("wallet-password")) { + walletPassword = options["wallet-password"].as(); + } + + if (options.count("generate-wallet")) { + generateNewWallet = true; + } + + if (options.count("import-keys")) { + importKeys = options["import-keys"].as(); + } + + if (!importKeys.empty() && generateNewWallet) { + throw ConfigurationError("It's impossible to use both \"import\" and \"generate-wallet\" at the same time"); + } + + if (!registerService && !unregisterService) { + if (walletFile.empty() || walletPassword.empty()) { + throw ConfigurationError("Both wallet-file and wallet-password parameters are required"); + } + } +} + +} //namespace PaymentService diff --git a/src/payment_service/PaymentServiceConfiguration.h b/src/payment_service/PaymentServiceConfiguration.h new file mode 100644 index 0000000000..eda15e7ec1 --- /dev/null +++ b/src/payment_service/PaymentServiceConfiguration.h @@ -0,0 +1,74 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include + +namespace PaymentService { + +class ConfigurationError : public std::runtime_error { +public: + ConfigurationError(const char* desc) : std::runtime_error(desc) {} +}; + +struct Configuration { + Configuration(); + + void init(const boost::program_options::variables_map& options); + static void initOptions(boost::program_options::options_description& desc); + + std::string bindAddress; + uint16_t bindPort; + + std::string walletFile; + std::string walletPassword; + std::string importKeys; + std::string logFile; + std::string serverRoot; + + bool generateNewWallet; + bool daemonize; + bool registerService; + bool unregisterService; + bool testnet; + + std::size_t logLevel; +}; + +} //namespace PaymentService diff --git a/src/payment_service/RpcNodeConfiguration.cpp b/src/payment_service/RpcNodeConfiguration.cpp new file mode 100644 index 0000000000..98e5d9a6c2 --- /dev/null +++ b/src/payment_service/RpcNodeConfiguration.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "RpcNodeConfiguration.h" + +namespace PaymentService { + +namespace po = boost::program_options; + +RpcNodeConfiguration::RpcNodeConfiguration() { + daemonHost = "127.0.0.1"; + daemonPort = 8081; +} + +void RpcNodeConfiguration::initOptions(boost::program_options::options_description& desc) { + desc.add_options() + ("daemon-address", po::value()->default_value("localhost"), "bytecoind address") + ("daemon-port", po::value()->default_value(8081), "bytecoind port"); +} + +void RpcNodeConfiguration::init(const boost::program_options::variables_map& options) { + if (options.count("daemon-address")) { + daemonHost = options["daemon-address"].as(); + } + + if (options.count("daemon-port")) { + daemonPort = options["daemon-port"].as(); + } +} + +} //namespace PaymentService diff --git a/src/payment_service/RpcNodeConfiguration.h b/src/payment_service/RpcNodeConfiguration.h new file mode 100644 index 0000000000..02fd6fbac3 --- /dev/null +++ b/src/payment_service/RpcNodeConfiguration.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace PaymentService { + +class RpcNodeConfiguration { +public: + RpcNodeConfiguration(); + + static void initOptions(boost::program_options::options_description& desc); + void init(const boost::program_options::variables_map& options); + + std::string daemonHost; + uint16_t daemonPort; +}; + +} //namespace PaymentService diff --git a/src/payment_service/WalletFactory.cpp b/src/payment_service/WalletFactory.cpp new file mode 100644 index 0000000000..9b6d660a00 --- /dev/null +++ b/src/payment_service/WalletFactory.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletFactory.h" + +#include "node_rpc_proxy/NodeRpcProxy.h" +#include "wallet/Wallet.h" +#include "cryptonote_core/Currency.h" + +#include +#include + +namespace PaymentService { + +WalletFactory WalletFactory::factory; + +WalletFactory::WalletFactory() { +} + +WalletFactory::~WalletFactory() { +} + +CryptoNote::IWallet* WalletFactory::createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node) { + CryptoNote::Wallet* wallet = new CryptoNote::Wallet(currency, node); + return wallet; +} + +} diff --git a/src/payment_service/WalletFactory.h b/src/payment_service/WalletFactory.h new file mode 100644 index 0000000000..850c12db09 --- /dev/null +++ b/src/payment_service/WalletFactory.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" +#include "INode.h" + +#include +#include + +namespace CryptoNote { +class Currency; +} + +namespace PaymentService { + +class WalletFactory { +public: + static CryptoNote::IWallet* createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node); +private: + WalletFactory(); + ~WalletFactory(); + + static WalletFactory factory; +}; + +} //namespace PaymentService diff --git a/src/payment_service/WalletObservers.cpp b/src/payment_service/WalletObservers.cpp new file mode 100644 index 0000000000..b9a62d54d1 --- /dev/null +++ b/src/payment_service/WalletObservers.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletObservers.h" + +#include + +namespace PaymentService { + +void WalletLoadObserver::initCompleted(std::error_code result) { + loadPromise.set_value(result); +} + +void WalletLoadObserver::waitForLoadEnd() { + auto future = loadPromise.get_future(); + + std::error_code ec = future.get(); + if (ec) { + throw std::system_error(ec); + } + return; +} + +void WalletSaveObserver::saveCompleted(std::error_code result) { + savePromise.set_value(result); +} + +void WalletSaveObserver::waitForSaveEnd() { + auto future = savePromise.get_future(); + + std::error_code ec = future.get(); + if (ec) { + throw std::system_error(ec); + } + return; +} + +void WalletTransactionSendObserver::sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) { + std::lock_guard lock(finishedTransactionsLock); + finishedTransactions.insert(std::make_pair(transactionId, result)); +} + +void WalletTransactionSendObserver::waitForTransactionFinished(CryptoNote::TransactionId transactionId, std::error_code& result) { + while (true) { + { + std::lock_guard lock(finishedTransactionsLock); + + auto it = finishedTransactions.find(transactionId); + if (it != finishedTransactions.end()) { + result = it->second; + break; + } + } + + timer.sleep(std::chrono::milliseconds(10)); + } +} + +} //namespace PaymentService diff --git a/src/payment_service/WalletObservers.h b/src/payment_service/WalletObservers.h new file mode 100644 index 0000000000..d083d3e7c7 --- /dev/null +++ b/src/payment_service/WalletObservers.h @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" + +#include +#include + +#include +#include +#include + +namespace PaymentService { + +class WalletLoadObserver : public CryptoNote::IWalletObserver { +public: + WalletLoadObserver() {} + virtual ~WalletLoadObserver() {} + + virtual void initCompleted(std::error_code result); + + void waitForLoadEnd(); +private: + std::promise loadPromise; +}; + +class WalletSaveObserver : public CryptoNote::IWalletObserver { +public: + WalletSaveObserver() {} + virtual ~WalletSaveObserver() {} + + virtual void saveCompleted(std::error_code result); + + void waitForSaveEnd(); + +private: + std::promise savePromise; +}; + +class WalletTransactionSendObserver : public CryptoNote::IWalletObserver { +public: + WalletTransactionSendObserver(System::Dispatcher& sys) : system(sys), timer(system) {} + ~WalletTransactionSendObserver() { timer.stop(); } + + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result); + + void waitForTransactionFinished(CryptoNote::TransactionId transactionId, std::error_code& result); +private: + std::map finishedTransactions; + std::mutex finishedTransactionsLock; + + System::Dispatcher& system; + System::Timer timer; +}; + +} //namespace PaymentService diff --git a/src/payment_service/WalletService.cpp b/src/payment_service/WalletService.cpp new file mode 100644 index 0000000000..fdef45abab --- /dev/null +++ b/src/payment_service/WalletService.cpp @@ -0,0 +1,583 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletService.h" + +#include "WalletServiceErrorCodes.h" +#include "JsonRpcMessages.h" +#include "WalletFactory.h" +#include "NodeFactory.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "crypto/crypto.h" +#include "wallet/LegacyKeysImporter.h" +#include "Common/util.h" + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#endif + +namespace { + +void addPaymentIdToExtra(const std::string& paymentId, std::string& extra) { + std::vector extraVector; + if (!CryptoNote::createTxExtraWithPaymentId(paymentId, extraVector)) { + throw std::runtime_error("Couldn't add payment id to extra"); + } + + std::copy(extraVector.begin(), extraVector.end(), std::back_inserter(extra)); +} + +bool checkPaymentId(const std::string& paymentId) { + if (paymentId.size() != 64) { + return false; + } + + return std::all_of(paymentId.begin(), paymentId.end(), [] (const char c) { + if (c >= '0' && c <= '9') { + return true; + } + + if (c >= 'a' && c <= 'f') { + return true; + } + + if (c >= 'A' && c <= 'F') { + return true; + } + + return false; + }); +} + +bool createOutputBinaryFile(const std::string& filename, std::fstream& file) { + file.open(filename.c_str(), std::fstream::in | std::fstream::out | std::ofstream::binary); + if (file) { + file.close(); + return false; + } + + file.open(filename.c_str(), std::fstream::out | std::fstream::binary); + return true; +} + +std::string createTemporaryFile(const std::string& path, std::fstream& tempFile) { + bool created = false; + std::string temporaryName; + + for (size_t i = 1; i < 100; i++) { + temporaryName = path + "." + std::to_string(i++); + + if (createOutputBinaryFile(temporaryName, tempFile)) { + created = true; + break; + } + } + + if (!created) { + throw std::runtime_error("Couldn't create temporary file: " + temporaryName); + } + + return temporaryName; +} + +//returns true on success +bool deleteFile(const std::string& filename) { +#ifdef WIN32 + return DeleteFile(filename.c_str()) != 0; +#else + return unlink(filename.c_str()) == 0; +#endif +} + +void replaceWalletFiles(const std::string &path, const std::string &tempFilePath) { + tools::replace_file(tempFilePath, path); +} + +} + +namespace PaymentService { + +void createWalletFile(std::fstream& walletFile, const std::string& filename) { + walletFile.open(filename.c_str(), std::fstream::in | std::fstream::out | std::fstream::binary); + if (walletFile) { + walletFile.close(); + throw std::runtime_error("Wallet file already exists"); + } + + walletFile.open(filename.c_str(), std::fstream::out); + walletFile.close(); + + walletFile.open(filename.c_str(), std::fstream::in | std::fstream::out | std::fstream::binary); +} + +void saveWallet(CryptoNote::IWallet* wallet, std::fstream& walletFile, bool saveDetailed = true, bool saveCache = true) { + WalletSaveObserver saveObserver; + wallet->addObserver(&saveObserver); + wallet->save(walletFile, saveDetailed, saveCache); + saveObserver.waitForSaveEnd(); + wallet->removeObserver(&saveObserver); + + walletFile.flush(); +} + +void secureSaveWallet(CryptoNote::IWallet* wallet, const std::string& path, bool saveDetailed = true, bool saveCache = true) { + std::fstream tempFile; + std::string tempFilePath = createTemporaryFile(path, tempFile); + + try { + saveWallet(wallet, tempFile, saveDetailed, saveCache); + } catch (std::exception&) { + deleteFile(tempFilePath); + tempFile.close(); + throw; + } + tempFile.close(); + + replaceWalletFiles(path, tempFilePath); +} + +void generateNewWallet(CryptoNote::Currency ¤cy, const Configuration &conf, Logging::ILogger& logger) { + Logging::LoggerRef log(logger, "generateNewWallet"); + + CryptoNote::INode* nodeStub = NodeFactory::createNodeStub(); + std::unique_ptr nodeGuard(nodeStub); + + CryptoNote::IWallet* wallet = WalletFactory::createWallet(currency, *nodeStub); + std::unique_ptr walletGuard(wallet); + + log(Logging::INFO) << "Generating new wallet"; + + std::fstream walletFile; + createWalletFile(walletFile, conf.walletFile); + + WalletLoadObserver loadObserver; + wallet->addObserver(&loadObserver); + + wallet->initAndGenerate(conf.walletPassword); + + loadObserver.waitForLoadEnd(); + wallet->removeObserver(&loadObserver); + + log(Logging::INFO) << "New wallet is generated. Address: " << wallet->getAddress(); + + saveWallet(wallet, walletFile, false, false); + log(Logging::INFO) << "Wallet is saved"; +} + +void importLegacyKeys(const Configuration& conf) { + std::stringstream archive; + + CryptoNote::importLegacyKeys(conf.importKeys, conf.walletPassword, archive); + + std::fstream walletFile; + createWalletFile(walletFile, conf.walletFile); + + archive.flush(); + walletFile << archive.rdbuf(); + walletFile.flush(); +} + +WalletService::WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, + const Configuration& conf, Logging::ILogger& logger) : + config(conf), + inited(false), + sendObserver(sys), + logger(logger, "WaleltService"), + txIdIndex(boost::get<0>(paymentsCache)), + paymentIdIndex(boost::get<1>(paymentsCache)) +{ + wallet.reset(WalletFactory::createWallet(currency, node)); +} + +WalletService::~WalletService() { + if (wallet) { + if (inited) { + wallet->removeObserver(&sendObserver); + wallet->removeObserver(this); + wallet->shutdown(); + } + } +} + +void WalletService::init() { + loadWallet(); + loadPaymentsCache(); + + wallet->addObserver(&sendObserver); + wallet->addObserver(this); + + inited = true; +} + +void WalletService::saveWallet() { + PaymentService::secureSaveWallet(wallet.get(), config.walletFile, true, true); + logger(Logging::INFO) << "Wallet is saved"; +} + +void WalletService::loadWallet() { + std::ifstream inputWalletFile; + inputWalletFile.open(config.walletFile.c_str(), std::fstream::in | std::fstream::binary); + if (!inputWalletFile) { + throw std::runtime_error("Couldn't open wallet file"); + } + + logger(Logging::INFO) << "Loading wallet"; + + WalletLoadObserver loadObserver; + wallet->addObserver(&loadObserver); + + wallet->initAndLoad(inputWalletFile, config.walletPassword); + + loadObserver.waitForLoadEnd(); + + wallet->removeObserver(&loadObserver); + + logger(Logging::INFO) << "Wallet loading is finished. Address: " << wallet->getAddress(); +} + +void WalletService::loadPaymentsCache() { + size_t txCount = wallet->getTransactionCount(); + + logger(Logging::DEBUGGING) << "seeking for payments among " << txCount << " transactions"; + + for (size_t id = 0; id < txCount; ++id) { + CryptoNote::TransactionInfo tx; + if (!wallet->getTransaction(id, tx)) { + logger(Logging::DEBUGGING) << "tx " << id << " doesn't exist"; + continue; + } + + if (tx.totalAmount < 0) { + logger(Logging::DEBUGGING) << "tx " << id << " has negative amount"; + continue; + } + + std::vector extraVector(tx.extra.begin(), tx.extra.end()); + + crypto::hash paymentId; + if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { + logger(Logging::DEBUGGING) << "tx " << id << " has no payment id"; + continue; + } + + logger(Logging::DEBUGGING) << "transaction " << id << " has been inserted with payment id " << paymentId; + insertTransaction(id, paymentId); + } +} + +std::error_code WalletService::sendTransaction(const SendTransactionRequest& req, SendTransactionResponse& resp) { + assert(wallet); + logger(Logging::DEBUGGING) << "Send transaction request came"; + + try { + std::vector transfers; + makeTransfers(req.destinations, transfers); + + std::string extra; + if (!req.paymentId.empty()) { + addPaymentIdToExtra(req.paymentId, extra); + } + + CryptoNote::TransactionId txId = wallet->sendTransaction(transfers, req.fee, extra, req.mixin, req.unlockTime); + if (txId == CryptoNote::INVALID_TRANSACTION_ID) { + logger(Logging::WARNING) << "Unable to send transaction"; + throw std::runtime_error("Error occured while sending transaction"); + } + + std::error_code ec; + sendObserver.waitForTransactionFinished(txId, ec); + + if (ec) { + return ec; + } + + resp.transactionId = txId; + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Error while sending transaction: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +void WalletService::makeTransfers(const std::vector& destinations, std::vector& transfers) { + transfers.reserve(destinations.size()); + + for (auto dest: destinations) { + transfers.push_back( { dest.address, static_cast(dest.amount) } ); + } +} + +std::error_code WalletService::getAddress(std::string& address) { + logger(Logging::DEBUGGING) << "Get address request came"; + + try { + address = wallet->getAddress(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Error while getting address: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getActualBalance(uint64_t& actualBalance) { + logger(Logging::DEBUGGING) << "Get actual balance request came"; + + try { + actualBalance = wallet->actualBalance(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get actual balance: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getPendingBalance(uint64_t& pendingBalance) { + logger(Logging::DEBUGGING) << "Get pending balance request came"; + + try { + pendingBalance = wallet->pendingBalance(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get pending balance: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getTransactionsCount(uint64_t& txCount) { + logger(Logging::DEBUGGING) << "Get get transactions count request came"; + + try { + txCount = wallet->getTransactionCount(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get transactions count: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getTransfersCount(uint64_t& trCount) { + logger(Logging::DEBUGGING) << "Get get transfers count request came"; + + try { + trCount = wallet->getTransferCount(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get transfers count: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction) { + logger(Logging::DEBUGGING) << "getTransactionByTransferId request came"; + + try { + transaction = wallet->findTransactionByTransferId(transfer); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get transaction id by transfer id count: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo) { + logger(Logging::DEBUGGING) << "getTransaction request came"; + + try { + CryptoNote::TransactionInfo txInfo; + + found = wallet->getTransaction(txId, txInfo); + if (!found) { + return std::error_code(); + } + + fillTransactionRpcInfo(txInfo, rpcInfo); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get transaction: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +void WalletService::fillTransactionRpcInfo(const CryptoNote::TransactionInfo& txInfo, TransactionRpcInfo& rpcInfo) { + rpcInfo.firstTransferId = txInfo.firstTransferId; + rpcInfo.transferCount = txInfo.transferCount; + rpcInfo.totalAmount = txInfo.totalAmount; + rpcInfo.fee = txInfo.fee; + rpcInfo.isCoinbase = txInfo.isCoinbase; + rpcInfo.blockHeight = txInfo.blockHeight; + rpcInfo.timestamp = txInfo.timestamp; + rpcInfo.extra = Common::toHex(txInfo.extra.data(), txInfo.extra.size()); + rpcInfo.hash = Common::podToHex(txInfo.hash); +} + +std::error_code WalletService::getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo) { + logger(Logging::DEBUGGING) << "getTransfer request came"; + + try { + CryptoNote::Transfer transfer; + + found = wallet->getTransfer(txId, transfer); + if (!found) { + return std::error_code(); + } + + fillTransferRpcInfo(transfer, rpcInfo); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get transfer: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +void WalletService::fillTransferRpcInfo(const CryptoNote::Transfer& transfer, TransferRpcInfo& rpcInfo) { + rpcInfo.address = transfer.address; + rpcInfo.amount = transfer.amount; +} + +std::error_code WalletService::getIncomingPayments(const std::vector& payments, IncomingPayments& result) { + logger(Logging::DEBUGGING) << "getIncomingPayments request came"; + + for (const std::string& payment: payments) { + if (!checkPaymentId(payment)) { + return make_error_code(error::REQUEST_ERROR); + } + + std::string paymentString = payment; + std::transform(paymentString.begin(), paymentString.end(), paymentString.begin(), ::tolower); + + auto pair = paymentIdIndex.equal_range(paymentString); + + for (auto it = pair.first; it != pair.second; ++it) { + CryptoNote::TransactionInfo tx; + if (!wallet->getTransaction(it->transactionId, tx)) { + continue; + } + + std::string hashString = Common::podToHex(tx.hash); + + PaymentDetails details; + details.txHash = std::move(hashString); + details.amount = static_cast(tx.totalAmount); + details.blockHeight = tx.blockHeight; + details.unlockTime = 0; //TODO: this is stub. fix it when wallet api allows to retrieve it + + result[it->paymentId].push_back(std::move(details)); + } + } + + return std::error_code(); +} + +void WalletService::externalTransactionCreated(CryptoNote::TransactionId transactionId) { + logger(Logging::DEBUGGING) << "external transaction created " << transactionId; + CryptoNote::TransactionInfo tx; + if (!wallet->getTransaction(transactionId, tx)) { + return; + } + + if (tx.totalAmount < 0) { + return; + } + + logger(Logging::DEBUGGING) << "external transaction created " << transactionId << " extra size: " << tx.extra.size(); + std::vector extraVector(tx.extra.begin(), tx.extra.end()); + crypto::hash paymentId; + if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; + return; + } + + insertTransaction(transactionId, paymentId); + + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been added to payments cache"; +} + +void WalletService::transactionUpdated(CryptoNote::TransactionId transactionId) { + CryptoNote::TransactionInfo tx; + if (!wallet->getTransaction(transactionId, tx)) { + return; + } + + if (tx.totalAmount < 0) { + return; + } + + if (tx.blockHeight != CryptoNote::UNCONFIRMED_TRANSACTION_HEIGHT) { + auto it = txIdIndex.find(transactionId); + if (it != txIdIndex.end()) { + return; + } + + //insert confirmed transaction + std::vector extraVector(tx.extra.begin(), tx.extra.end()); + crypto::hash paymentId; + if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; + return; + } + + insertTransaction(transactionId, paymentId); + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been inserted to payments cache"; + } else { + auto it = txIdIndex.find(transactionId); + if (it != txIdIndex.end()) { + txIdIndex.erase(it); + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been erased from payments cache"; + } + } +} + +void WalletService::insertTransaction(CryptoNote::TransactionId id, const crypto::hash& paymentIdBin) { + paymentsCache.insert(PaymentItem{ Common::podToHex(paymentIdBin), id }); +} + +} //namespace PaymentService diff --git a/src/payment_service/WalletService.h b/src/payment_service/WalletService.h new file mode 100644 index 0000000000..b88dbf47b9 --- /dev/null +++ b/src/payment_service/WalletService.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "PaymentServiceConfiguration.h" +#include "IWallet.h" +#include "INode.h" +#include "WalletObservers.h" +#include "cryptonote_core/Currency.h" +#include "JsonRpcMessages.h" +#undef ERROR //TODO: workaround for windows build. fix it +#include "Logging/LoggerRef.h" + +#include +#include +#include +#include +#include + +namespace PaymentService { + +struct SendTransactionRequest; +struct SendTransactionResponse; +struct TransferDestination; +struct TransactionRpcInfo; +struct TransferRpcInfo; + +void importLegacyKeys(const Configuration& conf); +void generateNewWallet (CryptoNote::Currency ¤cy, const Configuration &conf, Logging::ILogger &logger); + +class WalletService : public CryptoNote::IWalletObserver { +public: + typedef std::map > IncomingPayments; + + explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const Configuration& conf, Logging::ILogger& logger); + virtual ~WalletService(); + + void init(); + void saveWallet(); + + std::error_code sendTransaction(const SendTransactionRequest& req, SendTransactionResponse& resp); + std::error_code getIncomingPayments(const std::vector& payments, IncomingPayments& result); + std::error_code getAddress(std::string& address); + std::error_code getActualBalance(uint64_t& actualBalance); + std::error_code getPendingBalance(uint64_t& pendingBalance); + std::error_code getTransactionsCount(uint64_t& txCount); + std::error_code getTransfersCount(uint64_t& trCount); + std::error_code getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction); + std::error_code getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo); + std::error_code getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo); + +private: + void loadWallet(); + void loadPaymentsCache(); + void insertTransaction(CryptoNote::TransactionId id, const crypto::hash& paymentIdBin); + + void makeTransfers(const std::vector& destinations, std::vector& transfers); + void fillTransactionRpcInfo(const CryptoNote::TransactionInfo& txInfo, TransactionRpcInfo& rpcInfo); + void fillTransferRpcInfo(const CryptoNote::Transfer& transfer, TransferRpcInfo& rpcInfo); + + virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId); + virtual void transactionUpdated(CryptoNote::TransactionId transactionId); + + struct PaymentItem { + std::string paymentId; + CryptoNote::TransactionId transactionId; + }; + + typedef boost::multi_index::hashed_unique TxIdIndex; + typedef boost::multi_index::hashed_non_unique PaymentIndex; + typedef boost::multi_index::multi_index_container< + PaymentItem, + boost::multi_index::indexed_by< + TxIdIndex, + PaymentIndex + > + > PaymentsContainer; + + std::unique_ptr wallet; + CryptoNote::INode* node; + const Configuration& config; + bool inited; + WalletTransactionSendObserver sendObserver; + Logging::LoggerRef logger; + + PaymentsContainer paymentsCache; + PaymentsContainer::nth_index<0>::type& txIdIndex; + PaymentsContainer::nth_index<1>::type& paymentIdIndex; +}; + +} //namespace PaymentService diff --git a/src/payment_service/WalletServiceErrorCodes.cpp b/src/payment_service/WalletServiceErrorCodes.cpp new file mode 100644 index 0000000000..2ca1ff4125 --- /dev/null +++ b/src/payment_service/WalletServiceErrorCodes.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletServiceErrorCodes.h" + +namespace PaymentService { +namespace error { + +WalletServiceErrorCategory WalletServiceErrorCategory::INSTANCE; + +} //namespace error +} //namespace PaymentService diff --git a/src/payment_service/WalletServiceErrorCodes.h b/src/payment_service/WalletServiceErrorCodes.h new file mode 100644 index 0000000000..f8e7456c7b --- /dev/null +++ b/src/payment_service/WalletServiceErrorCodes.h @@ -0,0 +1,77 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace PaymentService { +namespace error { + +enum WalletServiceErrorCodes { + REQUEST_ERROR = 1, +}; + +// custom category: +class WalletServiceErrorCategory : public std::error_category { +public: + static WalletServiceErrorCategory INSTANCE; + + virtual const char* name() const throw() { + return "WalletServiceErrorCategory"; + } + + virtual std::error_condition default_error_condition(int ev) const throw() { + return std::error_condition(ev, *this); + } + + virtual std::string message(int ev) const { + switch (ev) { + case REQUEST_ERROR: return "Request error"; + default: return "Unknown error"; + } + } + +private: + WalletServiceErrorCategory() { + } +}; + +} +} + +inline std::error_code make_error_code(PaymentService::error::WalletServiceErrorCodes e) { + return std::error_code(static_cast(e), PaymentService::error::WalletServiceErrorCategory::INSTANCE); +} diff --git a/src/payment_service/main.cpp b/src/payment_service/main.cpp new file mode 100644 index 0000000000..8182e94ad1 --- /dev/null +++ b/src/payment_service/main.cpp @@ -0,0 +1,546 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include + +#include + +#include "PaymentServiceConfiguration.h" +#include "JsonRpcServer.h" +#include "WalletService.h" +#include "cryptonote_core/Currency.h" +#include "Common/SignalHandler.h" +#include "Logging/LoggerGroup.h" +#include "Logging/ConsoleLogger.h" +#include "Logging/LoggerRef.h" +#include "Logging/StreamLogger.h" +#include "NodeFactory.h" + +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#include "ConfigurationManager.h" +#include "cryptonote_core/CoreConfig.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" +#include "InProcessNode/InProcessNode.h" +#include "version.h" + +#define SERVICE_NAME "Payment Gate" + +PaymentService::ConfigurationManager config; +System::Dispatcher systemService; +System::Event stopEvent(systemService); +PaymentService::WalletService* service; +std::unique_ptr currencyBuilder; +Logging::LoggerGroup logger; +CryptoNote::node_server * gP2pNode = nullptr; + +#ifdef WIN32 +SERVICE_STATUS_HANDLE serviceStatusHandle; +#endif + +void run(); + +void stopSignalHandler() { + Logging::LoggerRef log(logger, "StopSignalHandler"); + log(Logging::INFO) << "Stop signal caught"; + + try { + if (service) { + service->saveWallet(); + } + } catch (std::exception& ex) { + log(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); + } + + + if (gP2pNode != nullptr) { + gP2pNode->send_stop_signal(); + } else { + stopEvent.set(); + } +} + +#ifdef WIN32 +std::string GetLastErrorMessage(DWORD errorMessageID) +{ + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, 0, (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + LocalFree(messageBuffer); + + return message; +} + +void __stdcall serviceHandler(DWORD fdwControl) { + if (fdwControl == SERVICE_CONTROL_STOP) { + Logging::LoggerRef log(logger, "serviceHandler"); + log(Logging::INFO) << "Stop signal caught"; + + SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_STOP_PENDING, 0, NO_ERROR, 0, 0, 0 }; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + try { + if (service) { + log(Logging::INFO) << "Saving wallet"; + service->saveWallet(); + } + } catch (std::exception& ex) { + log(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); + } + + log(Logging::INFO) << "Stopping service"; + stopEvent.set(); + } +} + +void __stdcall serviceMain(DWORD dwArgc, char **lpszArgv) { + Logging::LoggerRef logRef(logger, "WindowsService"); + + serviceStatusHandle = RegisterServiceCtrlHandler("PaymentGate", serviceHandler); + if (serviceStatusHandle == NULL) { + logRef(Logging::FATAL) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError()); + return; + } + + SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, 0, NO_ERROR, 0, 1, 3000 }; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { + logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + return; + } + + serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_RUNNING, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 }; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { + logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + return; + } + + try { + run(); + } catch (std::exception& ex) { + logRef(Logging::FATAL) << "Error occured: " << ex.what(); + } + + serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_STOPPED, 0, NO_ERROR, 0, 0, 0 }; + SetServiceStatus(serviceStatusHandle, &serviceStatus); +} +#else +int daemonize() { + pid_t pid; + pid = fork(); + + if (pid < 0) + return pid; + + if (pid > 0) + return pid; + + if (setsid() < 0) + return -1; + + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + pid = fork(); + + if (pid < 0) + return pid; + + if (pid > 0) + return pid; + + umask(0); + + return 0; +} +#endif + +int runDaemon() { +#ifdef WIN32 + + SERVICE_TABLE_ENTRY serviceTable[] { + { "PaymentGate", serviceMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher(serviceTable) != TRUE) { + return 1; + } + + return 0; + +#else + + int daemonResult = daemonize(); + if (daemonResult > 0) { + //parent + return 0; + } else if (daemonResult < 0) { + //error occured + return 1; + } + + run(); + return 0; + +#endif +} + +int registerService() { +#ifdef WIN32 + Logging::LoggerRef logRef(logger, "ServiceRegistrator"); + + char pathBuff[MAX_PATH]; + std::string modulePath; + SC_HANDLE scManager = NULL; + SC_HANDLE scService = NULL; + int ret = 0; + + for (;;) { + if (GetModuleFileName(NULL, pathBuff, ARRAYSIZE(pathBuff)) == 0) { + logRef(Logging::FATAL) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + modulePath.assign(pathBuff); + + std::string moduleDir = modulePath.substr(0, modulePath.find_last_of('\\') + 1); + modulePath += " --config=" + moduleDir + "payment_service.conf -d"; + + scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); + if (scManager == NULL) { + logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + scService = CreateService(scManager, SERVICE_NAME, NULL, SERVICE_QUERY_STATUS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, modulePath.c_str(), NULL, NULL, NULL, NULL, NULL); + + if (scService == NULL) { + logRef(Logging::FATAL) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + logRef(Logging::INFO) << "Service is registered successfully"; + logRef(Logging::INFO) << "Please make sure " << moduleDir + "payment_service.conf" << " exists"; + break; + } + + if (scManager) { + CloseServiceHandle(scManager); + } + + if (scService) { + CloseServiceHandle(scService); + } + + return ret; +#else + return 0; +#endif +} + +int unregisterService() { +#ifdef WIN32 + Logging::LoggerRef logRef(logger, "ServiceDeregistrator"); + + SC_HANDLE scManager = NULL; + SC_HANDLE scService = NULL; + SERVICE_STATUS ssSvcStatus = { }; + int ret = 0; + + for (;;) { + scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (scManager == NULL) { + logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + scService = OpenService(scManager, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE); + if (scService == NULL) { + logRef(Logging::FATAL) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + if (ControlService(scService, SERVICE_CONTROL_STOP, &ssSvcStatus)) { + logRef(Logging::INFO) << "Stopping " << SERVICE_NAME; + Sleep(1000); + + while (QueryServiceStatus(scService, &ssSvcStatus)) { + if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { + logRef(Logging::INFO) << "Waiting..."; + Sleep(1000); + } else { + break; + } + } + + std::cout << std::endl; + if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { + logRef(Logging::INFO) << SERVICE_NAME << " is stopped"; + } else { + logRef(Logging::FATAL) << SERVICE_NAME << " failed to stop" << std::endl; + } + } + + if (!DeleteService(scService)) { + logRef(Logging::FATAL) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + logRef(Logging::INFO) << SERVICE_NAME << " is removed"; + break; + } + + if (scManager) { + CloseServiceHandle(scManager); + } + + if (scService) { + CloseServiceHandle(scService); + } + + return ret; +#else + return 0; +#endif +} + +void changeDirectory(const std::string& path) { +#ifndef WIN32 + //unix + if (chdir(path.c_str())) { + throw std::runtime_error("Couldn't change directory to \'" + path + "\': " + strerror(errno)); + } +#else + if (!SetCurrentDirectory(path.c_str())) { + throw std::runtime_error("Couldn't change directory to \'" + path + "\': " + GetLastErrorMessage(GetLastError())); + } +#endif +} + +void runInProcess() { + Logging::LoggerRef(logger, "run")(Logging::INFO) << "Starting Payment Gate with local node"; + + CryptoNote::Currency currency = currencyBuilder->currency(); + CryptoNote::core core(currency, NULL, logger); + + CryptoNote::cryptonote_protocol_handler protocol(currency, systemService, core, NULL, logger); + CryptoNote::node_server p2pNode(systemService, protocol, logger); + gP2pNode = &p2pNode; + + protocol.set_p2p_endpoint(&p2pNode); + core.set_cryptonote_protocol(&protocol); + + std::unique_ptr node; + + Logging::LoggerRef(logger, "run")(Logging::INFO) << "initializing p2pNode"; + if (!p2pNode.init(config.netNodeConfig, config.gateConfiguration.testnet)) { + throw std::runtime_error("Failed to init p2pNode"); + } + + Logging::LoggerRef(logger, "run")(Logging::INFO) << "initializing core"; + CryptoNote::MinerConfig emptyMiner; + core.init(config.coreConfig, emptyMiner, true); + + std::promise initPromise; + auto initFuture = initPromise.get_future(); + + node.reset(new CryptoNote::InProcessNode(core, protocol)); + node->init([&initPromise](std::error_code ec) { + if (ec) { + Logging::LoggerRef(logger, "run")(Logging::INFO) << "Failed to init node: " << ec.message(); + } else { + Logging::LoggerRef(logger, "run")(Logging::INFO) << "node is inited successfully"; + } + + initPromise.set_value(ec); + }); + + auto ec = initFuture.get(); + if (ec) { + throw std::system_error(ec); + } + + Logging::LoggerRef(logger, "run")(Logging::INFO) << "Starting p2p server"; + + System::Event p2pStarted(systemService); + + systemService.spawn([&]() { + p2pStarted.set(); + p2pNode.run(); + stopEvent.set(); + }); + + p2pStarted.wait(); + Logging::LoggerRef(logger, "run")(Logging::INFO) << "p2p server is started"; + + service = new PaymentService::WalletService(currency, systemService, *node, config.gateConfiguration, logger); + std::unique_ptr serviceGuard(service); + + service->init(); + + PaymentService::JsonRpcServer rpcServer(systemService, stopEvent, *service, logger); + + rpcServer.start(config.gateConfiguration); + + serviceGuard.reset(); + node->shutdown(); + core.deinit(); + p2pNode.deinit(); + gP2pNode = nullptr; +} + +void runRpcProxy() { + Logging::LoggerRef(logger, "run")(Logging::INFO) << "Starting Payment Gate with remote node"; + CryptoNote::Currency currency = currencyBuilder->currency(); + std::unique_ptr node; + + node.reset(PaymentService::NodeFactory::createNode(config.remoteNodeConfig.daemonHost, config.remoteNodeConfig.daemonPort)); + + service = new PaymentService::WalletService(currency, systemService, *node, config.gateConfiguration, logger); + std::unique_ptr serviceGuard(service); + + service->init(); + + PaymentService::JsonRpcServer rpcServer(systemService, stopEvent, *service, logger); + + rpcServer.start(config.gateConfiguration); +} + +void run() { + tools::SignalHandler::install(stopSignalHandler); + + if (config.startInprocess) { + runInProcess(); + } else { + runRpcProxy(); + } +} + +int main(int argc, char** argv) { + try { + if (!config.init(argc, argv)) { + return 0; //help message requested or so + } + + Logging::ConsoleLogger consoleLogger(static_cast(config.gateConfiguration.logLevel)); + logger.addLogger(consoleLogger); + + currencyBuilder.reset(new CryptoNote::CurrencyBuilder(logger)); + + Logging::LoggerRef(logger, "main")(Logging::INFO) << "PaymentService " << " v" << PROJECT_VERSION_LONG; + + if (config.gateConfiguration.testnet) { + Logging::LoggerRef(logger, "main")(Logging::INFO) << "Starting in testnet mode"; + currencyBuilder->testnet(true); + } + + if (!config.gateConfiguration.serverRoot.empty()) { + changeDirectory(config.gateConfiguration.serverRoot); + Logging::LoggerRef(logger, "main")(Logging::INFO) << "Current working directory now is " << config.gateConfiguration.serverRoot; + } + + std::ofstream fileStream(config.gateConfiguration.logFile, std::ofstream::app); + if (!fileStream) { + throw std::runtime_error("Couldn't open log file"); + } + + Logging::StreamLogger fileLogger(fileStream, static_cast(config.gateConfiguration.logLevel)); + logger.addLogger(fileLogger); + + if (config.gateConfiguration.generateNewWallet) { + CryptoNote::Currency currency = currencyBuilder->currency(); + generateNewWallet(currency, config.gateConfiguration, logger); + return 0; + } + + if (!config.gateConfiguration.importKeys.empty()) { + importLegacyKeys(config.gateConfiguration); + Logging::LoggerRef(logger, "KeysImporter")(Logging::INFO) << "Keys have been imported successfully"; + return 0; + } + + if (config.gateConfiguration.registerService) { + return registerService(); + } + + if (config.gateConfiguration.unregisterService) { + return unregisterService(); + } + + if (config.gateConfiguration.daemonize) { + logger.removeLogger(consoleLogger); + if (runDaemon() != 0) { + throw std::runtime_error("Failed to start daemon"); + } + } else { + run(); + } + + } catch (PaymentService::ConfigurationError& ex) { + std::cerr << "Configuration error: " << ex.what() << std::endl; + return 1; + } catch (std::exception& ex) { + std::cerr << "Fatal error: " << ex.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/rpc/HttpClient.cpp b/src/rpc/HttpClient.cpp new file mode 100644 index 0000000000..36b6f3473e --- /dev/null +++ b/src/rpc/HttpClient.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpClient.h" + +#include +#include +#include +#include + +namespace CryptoNote { + +HttpClient::HttpClient(System::Dispatcher& dispatcher, const std::string& address, uint16_t port) : + m_dispatcher(dispatcher), m_address(address), m_port(port) { +} + +void HttpClient::request(const HttpRequest &req, HttpResponse &res) { + if (!m_connected) { + connect(); + } + + try { + std::iostream stream(m_streamBuf.get()); + HttpParser parser; + stream << req; + stream.flush(); + parser.receiveResponse(stream, res); + } catch (const std::exception &) { + disconnect(); + throw; + } +} + +void HttpClient::connect() { + auto ipAddr = System::Ipv4Resolver(m_dispatcher).resolve(m_address); + m_connection = System::TcpConnector(m_dispatcher).connect(ipAddr, m_port); + m_streamBuf.reset(new System::TcpStreambuf(m_connection)); + m_connected = true; +} + +void HttpClient::disconnect() { + m_streamBuf.reset(); + m_connection = System::TcpConnection(); + m_connected = false; +} + +} diff --git a/src/rpc/HttpClient.h b/src/rpc/HttpClient.h new file mode 100644 index 0000000000..3d7842bba8 --- /dev/null +++ b/src/rpc/HttpClient.h @@ -0,0 +1,85 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include +#include +#include +#include + +// epee serialization +#include "misc_log_ex.h" +#include "storages/portable_storage_template_helper.h" + +namespace CryptoNote { + +class HttpClient { +public: + + HttpClient(System::Dispatcher& dispatcher, const std::string& address, uint16_t port); + void request(const HttpRequest& req, HttpResponse& res); + +private: + + void connect(); + void disconnect(); + + const std::string m_address; + const uint16_t m_port; + + bool m_connected = false; + System::Dispatcher& m_dispatcher; + System::TcpConnection m_connection; + std::unique_ptr m_streamBuf; +}; + +template +void invokeJsonCommand(HttpClient& client, const std::string& url, const Request& req, Response& res) { + HttpRequest hreq; + HttpResponse hres; + + hreq.setUrl(url); + hreq.setBody(epee::serialization::store_t_to_json(req)); + client.request(hreq, hres); + + if (hres.getStatus() != HttpResponse::STATUS_200) { + throw std::runtime_error("HTTP status: " + std::to_string(hres.getStatus())); + } + + if (!epee::serialization::load_t_from_json(res, hres.getBody())) { + throw std::runtime_error("Failed to parse JSON response"); + } +} + +template +void invokeBinaryCommand(HttpClient& client, const std::string& url, const Request& req, Response& res) { + HttpRequest hreq; + HttpResponse hres; + + hreq.setUrl(url); + hreq.setBody(epee::serialization::store_t_to_binary(req)); + client.request(hreq, hres); + + if (!epee::serialization::load_t_from_binary(res, hres.getBody())) { + throw std::runtime_error("Failed to parse binary response"); + } +} + +} diff --git a/src/rpc/HttpServer.cpp b/src/rpc/HttpServer.cpp new file mode 100644 index 0000000000..9386fea199 --- /dev/null +++ b/src/rpc/HttpServer.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpServer.h" +#include + +#include +#include +#include +#include + +using namespace Logging; + +namespace CryptoNote { + +HttpServer::HttpServer(System::Dispatcher& dispatcher, Logging::ILogger& log) + : m_dispatcher(dispatcher), logger(log, "HttpServer"), m_shutdownCompleteEvent(dispatcher) { + +} + +void HttpServer::start(const std::string& address, uint16_t port) { + m_listener = System::TcpListener(m_dispatcher, System::Ipv4Address(address), port); + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&HttpServer::acceptLoop, this)); +} + +void HttpServer::stop() { + m_listener.stop(); + for (auto connPtr : m_connections) { + connPtr->stop(); + } + + if (m_spawnCount) { + m_shutdownCompleteEvent.wait(); + } +} + +void HttpServer::acceptLoop() { + try { + + System::TcpConnection connection; + bool accepted = false; + + while (!accepted) { + try { + connection = m_listener.accept(); + accepted = true; + } catch (System::InterruptedException&) { + throw; + } catch (std::exception&) { + // try again + } + } + + m_connections.insert(&connection); + BOOST_SCOPE_EXIT_ALL(this, &connection) { + m_connections.erase(&connection); }; + + auto addr = connection.getPeerAddressAndPort(); + + logger(DEBUGGING) << "Incoming connection from " << addr.first.toDottedDecimal() << ":" << addr.second; + + ++m_spawnCount; + m_dispatcher.spawn(std::bind(&HttpServer::acceptLoop, this)); + + System::TcpStreambuf streambuf(connection); + std::iostream stream(&streambuf); + HttpParser parser; + + for (;;) { + HttpRequest req; + HttpResponse resp; + + parser.receiveRequest(stream, req); + processRequest(req, resp); + + stream << resp; + stream.flush(); + } + + logger(DEBUGGING) << "Closing connection from " << addr.first.toDottedDecimal() << ":" << addr.second << " total=" << m_connections.size(); + + } catch (System::InterruptedException&) { + } catch (std::exception& e) { + logger(WARNING) << "Connection error: " << e.what(); + } + + if (--m_spawnCount == 0) { + m_shutdownCompleteEvent.set(); + } +} + +} diff --git a/src/rpc/HttpServer.h b/src/rpc/HttpServer.h new file mode 100644 index 0000000000..6139e090e6 --- /dev/null +++ b/src/rpc/HttpServer.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +namespace CryptoNote { + +class HttpServer { + +public: + + HttpServer(System::Dispatcher& dispatcher, Logging::ILogger& log); + + void start(const std::string& address, uint16_t port); + void stop(); + + virtual void processRequest(const HttpRequest& request, HttpResponse& response) = 0; + +protected: + + System::Dispatcher& m_dispatcher; + +private: + + void acceptLoop(); + void connectionHandler(System::TcpConnection&& conn); + + Logging::LoggerRef logger; + System::TcpListener m_listener; + System::Event m_shutdownCompleteEvent; + size_t m_spawnCount = 0; + std::unordered_set m_connections; +}; + +} diff --git a/src/rpc/JsonRpc.cpp b/src/rpc/JsonRpc.cpp new file mode 100644 index 0000000000..f45675080e --- /dev/null +++ b/src/rpc/JsonRpc.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonRpc.h" +#include "rpc/HttpClient.h" + +namespace CryptoNote { + +namespace JsonRpc { + +JsonRpcError::JsonRpcError() : code(0) {} + +JsonRpcError::JsonRpcError(int c) : code(c) { + switch (c) { + case errParseError: message = "Parse error"; break; + case errInvalidRequest: message = "Invalid request"; break; + case errMethodNotFound: message = "Method not found"; break; + case errInvalidParams: message = "Invalid params"; break; + case errInternalError: message = "Internal error"; break; + default: message = "Unknown error"; break; + } +} + +JsonRpcError::JsonRpcError(int c, const std::string& msg) : code(c), message(msg) { +} + +void invokeJsonRpcCommand(HttpClient& httpClient, JsonRpcRequest& jsReq, JsonRpcResponse& jsRes) { + HttpRequest httpReq; + HttpResponse httpRes; + + httpReq.setUrl("/json_rpc"); + httpReq.setBody(jsReq.getBody()); + + httpClient.request(httpReq, httpRes); + + if (httpRes.getStatus() != HttpResponse::STATUS_200) { + throw std::runtime_error("JSON-RPC call failed, HTTP status = " + std::to_string(httpRes.getStatus())); + } + + jsRes.parse(httpRes.getBody()); + + JsonRpcError err; + if (jsRes.getError(err)) { + throw err; + } +} + + +} +} diff --git a/src/rpc/JsonRpc.h b/src/rpc/JsonRpc.h new file mode 100644 index 0000000000..525e9023f3 --- /dev/null +++ b/src/rpc/JsonRpc.h @@ -0,0 +1,223 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include "misc_log_ex.h" +#include "storages/portable_storage_template_helper.h" +#include "serialization/enableable.h" +#include "serialization/keyvalue_serialization_overloads.h" + +namespace CryptoNote { + +class HttpClient; + +namespace JsonRpc { + +const int errParseError = -32700; +const int errInvalidRequest = -32600; +const int errMethodNotFound = -32601; +const int errInvalidParams = -32602; +const int errInternalError = -32603; + +class JsonRpcError: public std::exception { +public: + JsonRpcError(); + JsonRpcError(int c); + JsonRpcError(int c, const std::string& msg); + +#ifdef _MSC_VER + virtual const char* what() const override { +#else + virtual const char* what() const noexcept override { +#endif + return message.c_str(); + } + + int code; + std::string message; +}; + +typedef boost::optional OptionalId; + +class JsonRpcRequest { +public: + + bool parseRequest(const std::string& requestBody) { + if (!psReq.load_from_json(requestBody)) { + throw JsonRpcError(errParseError); + } + + OptionalId::value_type idValue; + if (psReq.get_value("id", idValue, nullptr)) { + id = idValue; + } + + if (!psReq.get_value("method", method, nullptr)) { + throw JsonRpcError(errInvalidRequest); + } + + return true; + } + + template + bool loadParams(T& v) const { + return epee::serialization::kv_unserialize(v, + const_cast(psReq), nullptr, "params"); + } + + template + bool setParams(const T& v) { + return epee::serialization::kv_serialize(v, psReq, nullptr, "params"); + } + + const std::string& getMethod() const { + return method; + } + + void setMethod(const std::string& m) { + method = m; + } + + const OptionalId& getId() const { + return id; + } + + std::string getBody() { + std::string reqBody; + psReq.set_value("jsonrpc", std::string("2.0"), nullptr); + psReq.set_value("method", method, nullptr); + psReq.dump_as_json(reqBody); + return reqBody; + } + +private: + + epee::serialization::portable_storage psReq; + OptionalId id; + std::string method; +}; + + +class JsonRpcResponse { +public: + + void parse(const std::string& resonseBody) { + if (!psResp.load_from_json(resonseBody)) { + throw JsonRpcError(errParseError); + } + } + + void setId(const OptionalId& id) { + if (id.is_initialized()) { + psResp.set_value("id", id.get(), nullptr); + } + } + + void setError(const JsonRpcError& err) { + auto errorSection = psResp.open_section("error", nullptr, true); + psResp.set_value("code", err.code, errorSection); + psResp.set_value("message", err.message, errorSection); + } + + bool getError(JsonRpcError& err) { + auto errorSection = psResp.open_section("error", nullptr, false); + if (!errorSection) { + return false; + } + + psResp.get_value("code", err.code, errorSection); + psResp.get_value("message", err.message, errorSection); + return true; + } + + std::string getBody() { + std::string responseBody; + psResp.set_value("jsonrpc", std::string("2.0"), nullptr); + psResp.dump_as_json(responseBody); + return responseBody; + } + + template + bool setResult(const T& v) { + return epee::serialization::kv_serialize(v, psResp, nullptr, "result"); + } + + template + bool getResult(T& v) const { + return epee::serialization::kv_unserialize(v, + const_cast(psResp), nullptr, "result"); + } + +private: + epee::serialization::portable_storage psResp; +}; + + +void invokeJsonRpcCommand(HttpClient& httpClient, JsonRpcRequest& req, JsonRpcResponse& res); + +template +void invokeJsonRpcCommand(HttpClient& httpClient, const std::string& method, const Request& req, Response& res) { + JsonRpcRequest jsReq; + JsonRpcResponse jsRes; + + jsReq.setMethod(method); + jsReq.setParams(req); + + invokeJsonRpcCommand(httpClient, jsReq, jsRes); + + jsRes.getResult(res); +} + +template +bool invokeMethod(const JsonRpcRequest& jsReq, JsonRpcResponse& jsRes, Handler handler) { + Request req; + Response res; + + if (!jsReq.loadParams(req)) { + throw JsonRpcError(JsonRpc::errInvalidParams); + } + + bool result = handler(req, res); + + if (result) { + if (!jsRes.setResult(res)) { + throw JsonRpcError(JsonRpc::errInternalError); + } + } + return result; +} + +typedef std::function JsonMemberMethod; + +template +JsonMemberMethod makeMemberMethod(bool (Class::*handler)(const Params&, Result&)) { + return [handler](void* obj, const JsonRpcRequest& req, JsonRpcResponse& res) { + return JsonRpc::invokeMethod( + req, res, std::bind(handler, static_cast(obj), std::placeholders::_1, std::placeholders::_2)); + }; +} + + +} + + +} diff --git a/src/rpc/RpcServer.cpp b/src/rpc/RpcServer.cpp new file mode 100644 index 0000000000..285efe85c1 --- /dev/null +++ b/src/rpc/RpcServer.cpp @@ -0,0 +1,606 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "RpcServer.h" + +#include +#include + +// CryptoNote +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/miner.h" +#include "p2p/net_node.h" + +#include "core_rpc_server_error_codes.h" +#include "JsonRpc.h" + +#undef ERROR + +using namespace Logging; + +namespace CryptoNote { + +namespace { + +template +RpcServer::HandlerFunction binMethod(bool (RpcServer::*handler)(typename Command::request const&, typename Command::response&)) { + return [handler](RpcServer* obj, const HttpRequest& request, HttpResponse& response) { + + boost::value_initialized req; + boost::value_initialized res; + + if (!epee::serialization::load_t_from_binary(static_cast(req), request.getBody())) { + return false; + } + + bool result = (obj->*handler)(req, res); + response.setBody(epee::serialization::store_t_to_binary(res.data())); + return result; + }; +} + +template +RpcServer::HandlerFunction jsonMethod(bool (RpcServer::*handler)(typename Command::request const&, typename Command::response&)) { + return [handler](RpcServer* obj, const HttpRequest& request, HttpResponse& response) { + + boost::value_initialized req; + boost::value_initialized res; + + if (!epee::serialization::load_t_from_json(static_cast(req), request.getBody())) { + return false; + } + + bool result = (obj->*handler)(req, res); + response.setBody(epee::serialization::store_t_to_json(res.data())); + return result; + }; +} + +} + +std::unordered_map RpcServer::s_handlers = { + + // binary handlers + { "/getblocks.bin", binMethod(&RpcServer::on_get_blocks) }, + { "/queryblocks.bin", binMethod(&RpcServer::on_query_blocks) }, + { "/get_o_indexes.bin", binMethod(&RpcServer::on_get_indexes) }, + { "/getrandom_outs.bin", binMethod(&RpcServer::on_get_random_outs) }, + + // json handlers + { "/getinfo", jsonMethod(&RpcServer::on_get_info) }, + { "/getheight", jsonMethod(&RpcServer::on_get_height) }, + { "/gettransactions", jsonMethod(&RpcServer::on_get_transactions)}, + { "/sendrawtransaction", jsonMethod(&RpcServer::on_send_raw_tx) }, + { "/start_mining", jsonMethod(&RpcServer::on_start_mining) }, + { "/stop_mining", jsonMethod(&RpcServer::on_stop_mining) }, + { "/stop_daemon", jsonMethod(&RpcServer::on_stop_daemon) }, + + // json rpc + { "/json_rpc", std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) } +}; + +RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, node_server& p2p) : + HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(p2p) { +} + +void RpcServer::processRequest(const HttpRequest& request, HttpResponse& response) { + auto url = request.getUrl(); + + auto it = s_handlers.find(url); + if (it == s_handlers.end()) { + response.setStatus(HttpResponse::STATUS_404); + return; + } + + if (url != "/json_rpc" && !checkCoreReady()) { + response.setStatus(HttpResponse::STATUS_500); + response.setBody("Core is busy"); + return; + } + + it->second(this, request, response); +} + +bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& response) { + + using namespace JsonRpc; + + response.addHeader("Content-Type", "application/json"); + + JsonRpcRequest jsonRequest; + JsonRpcResponse jsonResponse; + + try { + + jsonRequest.parseRequest(request.getBody()); + jsonResponse.setId(jsonRequest.getId()); // copy id + + static std::unordered_map jsonRpcHandlers = { + { "getblockcount", makeMemberMethod(&RpcServer::on_getblockcount) }, + { "on_getblockhash", makeMemberMethod(&RpcServer::on_getblockhash) }, + { "getblocktemplate", makeMemberMethod(&RpcServer::on_getblocktemplate) }, + { "getcurrencyid", makeMemberMethod(&RpcServer::on_get_currency_id) }, + { "submitblock", makeMemberMethod(&RpcServer::on_submitblock) }, + { "getlastblockheader", makeMemberMethod(&RpcServer::on_get_last_block_header) }, + { "getblockheaderbyhash", makeMemberMethod(&RpcServer::on_get_block_header_by_hash) }, + { "getblockheaderbyheight", makeMemberMethod(&RpcServer::on_get_block_header_by_height) } + }; + + auto it = jsonRpcHandlers.find(jsonRequest.getMethod()); + if (it == jsonRpcHandlers.end()) { + throw JsonRpcError(JsonRpc::errMethodNotFound); + } + + if (jsonRequest.getMethod() != "getcurrencyid" && !checkCoreReady()) { + throw JsonRpcError(CORE_RPC_ERROR_CODE_CORE_BUSY, "Core is busy"); + } + + it->second(this, jsonRequest, jsonResponse); + + } catch (const JsonRpcError& err) { + jsonResponse.setError(err); + } + + response.setBody(jsonResponse.getBody()); + return true; +} + +#define CHECK_CORE_READY() + +bool RpcServer::checkCoreReady() { + return m_core.is_ready() && m_p2p.get_payload_object().is_synchronized(); +} + +// +// Binary handlers +// + +bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { + + std::list>> bs; + if (!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { + res.status = "Failed"; + return false; + } + + for (auto& b : bs) { + res.blocks.resize(res.blocks.size() + 1); + res.blocks.back().block = block_to_blob(b.first); + for (auto& t : b.second) { + res.blocks.back().txs.push_back(tx_to_blob(t)); + } + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res) { + CHECK_CORE_READY(); + + if (!m_core.queryBlocks(req.block_ids, req.timestamp, res.start_height, res.current_height, res.full_offset, res.items)) { + res.status = "Failed to perform query"; + return false; + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { + CHECK_CORE_READY(); + + if (!m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes)) { + res.status = "Failed"; + return true; + } + + res.status = CORE_RPC_STATUS_OK; + logger(TRACE) << "COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"; + return true; +} + +bool RpcServer::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { + CHECK_CORE_READY(); + res.status = "Failed"; + 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 << "]:"; + + assert(ofa.outs.size() && "internal error: ofa.outs.size() is empty"); + + std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + }); + std::string s = ss.str(); + logger(TRACE) << "COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s; + res.status = CORE_RPC_STATUS_OK; + return true; +} + +// +// JSON handlers +// + +bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { + res.height = m_core.get_current_blockchain_height(); + res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + 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.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.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { + CHECK_CORE_READY(); + res.height = m_core.get_current_blockchain_height(); + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { + CHECK_CORE_READY(); + std::vector vh; + for (const auto& tx_hex_str : req.txs_hashes) { + blobdata b; + if (!hexToBlob(tx_hex_str, b)) + { + res.status = "Failed to parse hex representation of transaction hash"; + return true; + } + if (b.size() != sizeof(crypto::hash)) + { + res.status = "Failed, size of data mismatch"; + } + vh.push_back(*reinterpret_cast(b.data())); + } + std::list missed_txs; + std::list txs; + m_core.get_transactions(vh, txs, missed_txs); + + for (auto& tx : txs) { + blobdata blob = t_serializable_object_to_blob(tx); + res.txs_as_hex.push_back(blobToHex(blob)); + } + + for (const auto& miss_tx : missed_txs) { + res.missed_tx.push_back(Common::podToHex(miss_tx)); + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { + CHECK_CORE_READY(); + + std::string tx_blob; + if (!hexToBlob(req.tx_as_hex, tx_blob)) + { + logger(INFO) << "[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex; + res.status = "Failed"; + return true; + } + + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + if (!m_core.handle_incoming_tx(tx_blob, tvc, false)) + { + logger(INFO) << "[on_send_raw_tx]: Failed to process tx"; + res.status = "Failed"; + return true; + } + + if (tvc.m_verifivation_failed) + { + logger(INFO) << "[on_send_raw_tx]: tx verification failed"; + res.status = "Failed"; + return true; + } + + if (!tvc.m_should_be_relayed) + { + logger(INFO) << "[on_send_raw_tx]: tx accepted, but not relayed"; + res.status = "Not relayed"; + return true; + } + + + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(tx_blob); + m_core.get_protocol()->relay_transactions(r); + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) { + CHECK_CORE_READY(); + AccountPublicAddress adr; + if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { + res.status = "Failed, wrong address"; + return true; + } + + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + + if (!m_core.get_miner().start(adr, static_cast(req.threads_count), attrs)) { + res.status = "Failed, mining not started"; + return true; + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { + CHECK_CORE_READY(); + if (!m_core.get_miner().stop()) { + res.status = "Failed, mining not stopped"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { + CHECK_CORE_READY(); + if (m_core.currency().isTestnet()) { + m_p2p.send_stop_signal(); + res.status = CORE_RPC_STATUS_OK; + } else { + res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + return false; + } + return true; +} + +//------------------------------------------------------------------------------------------------------------------------------ +// JSON RPC methods +//------------------------------------------------------------------------------------------------------------------------------ +bool RpcServer::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { + res.count = m_core.get_current_blockchain_height(); + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res) { + if (req.size() != 1) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong parameters, expected height" }; + } + + uint64_t h = req[0]; + if (m_core.get_current_blockchain_height() <= h) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) + }; + } + + res = Common::podToHex(m_core.get_block_id_by_height(h)); + return true; +} + +namespace { + uint64_t slow_memmem(void* start_buff, size_t buflen, void* pat, size_t patlen) + { + void* buf = start_buff; + void* end = (char*)buf + buflen - patlen; + while ((buf = memchr(buf, ((char*)pat)[0], buflen))) + { + if (buf>end) + return 0; + if (memcmp(buf, pat, patlen) == 0) + return (char*)buf - (char*)start_buff; + buf = (char*)buf + 1; + } + return 0; + } +} + +bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res) { + if (req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE, "To big reserved size, maximum 255" }; + } + + CryptoNote::AccountPublicAddress acc = AUTO_VAL_INIT(acc); + + if (!req.wallet_address.size() || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS, "Failed to parse wallet address" }; + } + + Block b = AUTO_VAL_INIT(b); + CryptoNote::blobdata blob_reserve; + blob_reserve.resize(req.reserve_size, 0); + if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { + logger(ERROR) << "Failed to create block template"; + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; + } + + blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = CryptoNote::get_tx_pub_key_from_extra(b.minerTx); + if (tx_pub_key == null_pkey) { + logger(ERROR) << "Failed to find tx pub key in coinbase extra"; + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to find tx pub key in coinbase extra" }; + } + + if (0 < req.reserve_size) { + res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if (!res.reserved_offset) { + logger(ERROR) << "Failed to find tx pub key in blockblob"; + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; + } + res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if (res.reserved_offset + req.reserve_size > block_blob.size()) { + logger(ERROR) << "Failed to calculate offset for reserved bytes"; + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; + } + } else { + res.reserved_offset = 0; + } + + res.blocktemplate_blob = blobToHex(block_blob); + res.status = CORE_RPC_STATUS_OK; + + return true; +} + +bool RpcServer::on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& /*req*/, COMMAND_RPC_GET_CURRENCY_ID::response& res) { + crypto::hash currencyId = m_core.currency().genesisBlockHash(); + blobdata blob = t_serializable_object_to_blob(currencyId); + res.currency_id_blob = blobToHex(blob); + return true; +} + +bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res) { + if (req.size() != 1) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param" }; + } + + blobdata blockblob; + if (!hexToBlob(req[0], blockblob)) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob" }; + } + + CryptoNote::block_verification_context bvc = AUTO_VAL_INIT(bvc); + + System::Event event(m_dispatcher); + auto resultFuture = std::async(std::launch::async, [this, &event, &bvc, &blockblob]{ + m_core.handle_incoming_block_blob(blockblob, bvc, true, true); + m_dispatcher.remoteSpawn([&event]() { event.set(); }); + }); + + event.wait(); + resultFuture.get(); + + if (!bvc.m_added_to_main_chain) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, "Block not accepted" }; + } + + res.status = CORE_RPC_STATUS_OK; + return true; +} + + +namespace { + uint64_t get_block_reward(const Block& blk) { + uint64_t reward = 0; + for (const TransactionOutput& out : blk.minerTx.vout) { + reward += out.amount; + } + return reward; + } +} + +void RpcServer::fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce) { + responce.major_version = blk.majorVersion; + responce.minor_version = blk.minorVersion; + responce.timestamp = blk.timestamp; + responce.prev_hash = Common::podToHex(blk.prevId); + responce.nonce = blk.nonce; + responce.orphan_status = orphan_status; + responce.height = height; + responce.depth = m_core.get_current_blockchain_height() - height - 1; + responce.hash = Common::podToHex(hash); + responce.difficulty = m_core.get_blockchain_storage().block_difficulty(height); + responce.reward = get_block_reward(blk); +} + +bool RpcServer::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res) { + uint64_t last_block_height; + crypto::hash last_block_hash; + + if (!m_core.get_blockchain_top(last_block_height, last_block_hash)) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; + } + + Block last_block; + if (!m_core.get_block_by_hash(last_block_hash, last_block)) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; + } + + fill_block_header_responce(last_block, false, last_block_height, last_block_hash, res.block_header); + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::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) { + crypto::hash block_hash; + + if (!parse_hash256(req.hash, block_hash)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_WRONG_PARAM, + "Failed to parse hex representation of block hash. Hex = " + req.hash + '.' }; + } + + Block blk; + if (!m_core.get_block_by_hash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by hash. Hash = " + req.hash + '.' }; + } + + if (blk.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) { + throw JsonRpc::JsonRpcError{ + CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: coinbase transaction in the block has the wrong type" }; + } + + uint64_t block_height = boost::get(blk.minerTx.vin.front()).height; + fill_block_header_responce(blk, false, block_height, block_hash, res.block_header); + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::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) { + if (m_core.get_current_blockchain_height() <= req.height) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, + std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; + } + + crypto::hash block_hash = m_core.get_block_id_by_height(req.height); + Block blk; + if (!m_core.get_block_by_hash(block_hash, blk)) { + throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, + "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.' }; + } + + fill_block_header_responce(blk, false, req.height, block_hash, res.block_header); + res.status = CORE_RPC_STATUS_OK; + return true; +} + + +} diff --git a/src/rpc/RpcServer.h b/src/rpc/RpcServer.h new file mode 100644 index 0000000000..35b85cbced --- /dev/null +++ b/src/rpc/RpcServer.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "HttpServer.h" + +#include +#include + +#include +#include "core_rpc_server_commands_defs.h" + +namespace CryptoNote { + +class core; +class node_server; + +class RpcServer : public HttpServer { +public: + RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, node_server& p2p); + + typedef std::function HandlerFunction; + +private: + + typedef void (RpcServer::*HandlerPtr)(const HttpRequest& request, HttpResponse& response); + static std::unordered_map s_handlers; + + virtual void processRequest(const HttpRequest& request, HttpResponse& response) override; + bool processJsonRpcRequest(const HttpRequest& request, HttpResponse& response); + bool checkCoreReady(); + + // binary handlers + bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); + bool on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res); + bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::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); + + // json handlers + bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); + bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res); + bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res); + bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res); + 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_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res); + + // json rpc + bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); + bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res); + bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res); + bool on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& req, COMMAND_RPC_GET_CURRENCY_ID::response& res); + bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res); + bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res); + 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); + 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); + + void fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce); + + Logging::LoggerRef logger; + core& m_core; + node_server& m_p2p; +}; + +} diff --git a/src/rpc/RpcServerConfig.cpp b/src/rpc/RpcServerConfig.cpp new file mode 100644 index 0000000000..770d2ca5c6 --- /dev/null +++ b/src/rpc/RpcServerConfig.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "RpcServerConfig.h" +#include "Common/command_line.h" +#include "cryptonote_config.h" + +namespace CryptoNote { + + namespace { + + const std::string DEFAULT_RPC_IP = "127.0.0.1"; + const uint16_t DEFAULT_RPC_PORT = RPC_DEFAULT_PORT; + + const command_line::arg_descriptor arg_rpc_bind_ip = { "rpc-bind-ip", "", DEFAULT_RPC_IP }; + const command_line::arg_descriptor arg_rpc_bind_port = { "rpc-bind-port", "", DEFAULT_RPC_PORT }; + } + + + RpcServerConfig::RpcServerConfig() : bindIp(DEFAULT_RPC_IP), bindPort(DEFAULT_RPC_PORT) { + } + + std::string RpcServerConfig::getBindAddress() const { + return bindIp + ":" + std::to_string(bindPort); + } + + void RpcServerConfig::initOptions(boost::program_options::options_description& desc) { + command_line::add_arg(desc, arg_rpc_bind_ip); + command_line::add_arg(desc, arg_rpc_bind_port); + } + + void RpcServerConfig::init(const boost::program_options::variables_map& vm) { + bindIp = command_line::get_arg(vm, arg_rpc_bind_ip); + bindPort = command_line::get_arg(vm, arg_rpc_bind_port); + } + +} diff --git a/src/rpc/RpcServerConfig.h b/src/rpc/RpcServerConfig.h new file mode 100644 index 0000000000..2f6fc6f752 --- /dev/null +++ b/src/rpc/RpcServerConfig.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +namespace CryptoNote { + +class RpcServerConfig { +public: + + RpcServerConfig(); + + static void initOptions(boost::program_options::options_description& desc); + void init(const boost::program_options::variables_map& options); + + std::string getBindAddress() const; + + std::string bindIp; + uint16_t bindPort; +}; + +} diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp deleted file mode 100644 index 3f3de41315..0000000000 --- a/src/rpc/core_rpc_server.cpp +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "core_rpc_server.h" - -#include "include_base_utils.h" -#include "misc_language.h" - -#include "common/command_line.h" -#include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/miner.h" -#include "rpc/core_rpc_server_error_codes.h" - -using namespace epee; - -namespace cryptonote -{ - namespace - { - const command_line::arg_descriptor arg_rpc_bind_ip = {"rpc-bind-ip", "", "127.0.0.1"}; - const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "", std::to_string(RPC_DEFAULT_PORT)}; - } - - //----------------------------------------------------------------------------------- - void core_rpc_server::init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_rpc_bind_ip); - command_line::add_arg(desc, arg_rpc_bind_port); - } - //------------------------------------------------------------------------------------------------------------------------------ - core_rpc_server::core_rpc_server(core& cr, nodetool::node_server >& p2p):m_core(cr), m_p2p(p2p) - {} - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) - { - m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); - m_port = command_line::get_arg(vm, arg_rpc_bind_port); - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::init(const boost::program_options::variables_map& vm) - { - m_net_server.set_threads_prefix("RPC"); - bool r = handle_command_line(vm); - CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); - return epee::http_server_impl_base::init(m_port, m_bind_ip); - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::check_core_ready() - { - if(!m_p2p.get_payload_object().is_synchronized()) - { - return false; - } - if(m_p2p.get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) - { - return false; - } - return true; - } -#define CHECK_CORE_READY() if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} - - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - res.height = m_core.get_current_blockchain_height(); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - res.height = m_core.get_current_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); - 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.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.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - std::list > > bs; - if(!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) - { - res.status = "Failed"; - return false; - } - - for (auto& b : bs) { - res.blocks.resize(res.blocks.size()+1); - res.blocks.back().block = block_to_blob(b.first); - for (auto& t : b.second) { - res.blocks.back().txs.push_back(tx_to_blob(t)); - } - } - - res.status = CORE_RPC_STATUS_OK; - return true; - } - - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - - if (!m_core.queryBlocks(req.block_ids, req.timestamp, res.start_height, res.current_height, res.full_offset, res.items)) { - res.status = "Failed to perform query"; - return false; - } - - res.status = CORE_RPC_STATUS_OK; - 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, connection_context& cntx) - { - CHECK_CORE_READY(); - res.status = "Failed"; - 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_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); - if(!r) - { - res.status = "Failed"; - return true; - } - res.status = CORE_RPC_STATUS_OK; - LOG_PRINT_L2("COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"); - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - std::vector vh; - for (const auto& tx_hex_str : req.txs_hashes) { - blobdata b; - if(!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) - { - res.status = "Failed to parse hex representation of transaction hash"; - return true; - } - if(b.size() != sizeof(crypto::hash)) - { - res.status = "Failed, size of data mismatch"; - } - vh.push_back(*reinterpret_cast(b.data())); - } - std::list missed_txs; - std::list txs; - m_core.get_transactions(vh, txs, missed_txs); - - for (auto& tx : txs) { - blobdata blob = t_serializable_object_to_blob(tx); - res.txs_as_hex.push_back(string_tools::buff_to_hex_nodelimer(blob)); - } - - for (const auto& miss_tx : missed_txs) { - res.missed_tx.push_back(string_tools::pod_to_hex(miss_tx)); - } - - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - - std::string tx_blob; - if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob)) - { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex); - res.status = "Failed"; - return true; - } - - cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); - tx_verification_context tvc = AUTO_VAL_INIT(tvc); - if(!m_core.handle_incoming_tx(tx_blob, tvc, false)) - { - LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); - res.status = "Failed"; - return true; - } - - if(tvc.m_verifivation_failed) - { - LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); - res.status = "Failed"; - return true; - } - - if(!tvc.m_should_be_relayed) - { - LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); - res.status = "Not relayed"; - return true; - } - - - NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(tx_blob); - m_core.get_protocol()->relay_transactions(r, fake_context); - //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - AccountPublicAddress adr; - if(!m_core.currency().parseAccountAddressString(req.miner_address, adr)) - { - res.status = "Failed, wrong address"; - return true; - } - - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - if(!m_core.get_miner().start(adr, static_cast(req.threads_count), attrs)) - { - res.status = "Failed, mining not started"; - return true; - } - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - if(!m_core.get_miner().stop()) - { - res.status = "Failed, mining not stopped"; - return true; - } - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, connection_context& cntx) { - CHECK_CORE_READY(); - if (m_core.currency().isTestnet()) { - m_p2p.send_stop_signal(); - res.status = CORE_RPC_STATUS_OK; - } else { - res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - return false; - } - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx) - { - CHECK_CORE_READY(); - res.count = m_core.get_current_blockchain_height(); - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) - { - if(!check_core_ready()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy"; - return false; - } - if(req.size() != 1) - { - error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; - error_resp.message = "Wrong parameters, expected height"; - return false; - } - uint64_t h = req[0]; - if(m_core.get_current_blockchain_height() <= h) - { - error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; - error_resp.message = std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); - } - res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h)); - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - uint64_t slow_memmem(void* start_buff, size_t buflen,void* pat,size_t patlen) - { - void* buf = start_buff; - void* end=(char*)buf+buflen-patlen; - while((buf=memchr(buf,((char*)pat)[0],buflen))) - { - if(buf>end) - return 0; - if(memcmp(buf,pat,patlen)==0) - return (char*)buf - (char*)start_buff; - buf=(char*)buf+1; - } - return 0; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) - { - if(!check_core_ready()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy"; - return false; - } - - if(req.reserve_size > TX_EXTRA_NONCE_MAX_COUNT) - { - error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE; - error_resp.message = "To big reserved size, maximum 255"; - return false; - } - - cryptonote::AccountPublicAddress acc = AUTO_VAL_INIT(acc); - - if(!req.wallet_address.size() || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) - { - error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS; - error_resp.message = "Failed to parse wallet address"; - return false; - } - - Block b = AUTO_VAL_INIT(b); - cryptonote::blobdata blob_reserve; - blob_reserve.resize(req.reserve_size, 0); - if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to create block template"); - return false; - } - - blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.minerTx); - if(tx_pub_key == null_pkey) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to find tx pub key in coinbase extra"); - return false; - } - - if(0 < req.reserve_size) - { - res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); - if(!res.reserved_offset) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to find tx pub key in blockblob"); - return false; - } - res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - if(res.reserved_offset + req.reserve_size > block_blob.size()) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: failed to create block template"; - LOG_ERROR("Failed to calculate offset for reserved bytes"); - return false; - } - } - else - { - res.reserved_offset = 0; - } - - res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); - res.status = CORE_RPC_STATUS_OK; - - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& /*req*/, COMMAND_RPC_GET_CURRENCY_ID::response& res, epee::json_rpc::error& error_resp, connection_context& /*cntx*/) - { - crypto::hash currencyId = m_core.currency().genesisBlockHash(); - blobdata blob = t_serializable_object_to_blob(currencyId); - res.currency_id_blob = string_tools::buff_to_hex_nodelimer(blob); - - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { - CHECK_CORE_READY(); - if (req.size() != 1) { - error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; - error_resp.message = "Wrong param"; - return false; - } - - blobdata blockblob; - if (!string_tools::parse_hexstr_to_binbuff(req[0], blockblob)) { - error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; - error_resp.message = "Wrong block blob"; - return false; - } - - cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - m_core.handle_incoming_block_blob(blockblob, bvc, true, true); - if (!bvc.m_added_to_main_chain) { - error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED; - error_resp.message = "Block not accepted"; - return false; - } - - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - namespace { - uint64_t get_block_reward(const Block& blk) { - uint64_t reward = 0; - for (const TransactionOutput& out : blk.minerTx.vout) { - reward += out.amount; - } - return reward; - } - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce) - { - responce.major_version = blk.majorVersion; - responce.minor_version = blk.minorVersion; - responce.timestamp = blk.timestamp; - responce.prev_hash = string_tools::pod_to_hex(blk.prevId); - responce.nonce = blk.nonce; - responce.orphan_status = orphan_status; - responce.height = height; - responce.depth = m_core.get_current_blockchain_height() - height - 1; - responce.hash = string_tools::pod_to_hex(hash); - responce.difficulty = m_core.get_blockchain_storage().block_difficulty(height); - responce.reward = get_block_reward(blk); - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::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, connection_context& cntx) - { - if(!check_core_ready()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } - uint64_t last_block_height; - crypto::hash last_block_hash; - bool have_last_block_hash = m_core.get_blockchain_top(last_block_height, last_block_hash); - if (!have_last_block_hash) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't get last block hash."; - return false; - } - Block last_block; - bool have_last_block = m_core.get_block_by_hash(last_block_hash, last_block); - if (!have_last_block) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't get last block."; - return false; - } - bool responce_filled = fill_block_header_responce(last_block, false, last_block_height, last_block_hash, res.block_header); - if (!responce_filled) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't produce valid response."; - return false; - } - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::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, connection_context& cntx){ - if(!check_core_ready()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } - crypto::hash block_hash; - bool hash_parsed = parse_hash256(req.hash, block_hash); - if(!hash_parsed) - { - error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; - error_resp.message = "Failed to parse hex representation of block hash. Hex = " + req.hash + '.'; - return false; - } - Block blk; - bool have_block = m_core.get_block_by_hash(block_hash, blk); - if (!have_block) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.'; - return false; - } - if (blk.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: coinbase transaction in the block has the wrong type"; - return false; - } - uint64_t block_height = boost::get(blk.minerTx.vin.front()).height; - bool responce_filled = fill_block_header_responce(blk, false, block_height, block_hash, res.block_header); - if (!responce_filled) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't produce valid response."; - return false; - } - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ - bool core_rpc_server::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, connection_context& cntx){ - if(!check_core_ready()) - { - error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; - error_resp.message = "Core is busy."; - return false; - } - if(m_core.get_current_blockchain_height() <= req.height) - { - error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; - error_resp.message = std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); - return false; - } - crypto::hash block_hash = m_core.get_block_id_by_height(req.height); - Block blk; - bool have_block = m_core.get_block_by_hash(block_hash, blk); - if (!have_block) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.'; - return false; - } - bool responce_filled = fill_block_header_responce(blk, false, req.height, block_hash, res.block_header); - if (!responce_filled) - { - error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; - error_resp.message = "Internal error: can't produce valid response."; - return false; - } - res.status = CORE_RPC_STATUS_OK; - return true; - } - //------------------------------------------------------------------------------------------------------------------------------ -} diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h deleted file mode 100644 index 3f2765356f..0000000000 --- a/src/rpc/core_rpc_server.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -#include "net/http_server_impl_base.h" -#include "core_rpc_server_commands_defs.h" -#include "cryptonote_core/cryptonote_core.h" -#include "p2p/net_node.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" - -namespace cryptonote -{ - /************************************************************************/ - /* */ - /************************************************************************/ - class core_rpc_server: public epee::http_server_impl_base - { - public: - typedef epee::net_utils::connection_context_base connection_context; - - core_rpc_server(core& cr, nodetool::node_server >& p2p); - - static void init_options(boost::program_options::options_description& desc); - bool init(const boost::program_options::variables_map& vm); - private: - - CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map - - BEGIN_URI_MAP2() - MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT) - MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST) - MAP_URI_AUTO_BIN2("/queryblocks.bin", on_query_blocks, COMMAND_RPC_QUERY_BLOCKS) - MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) - MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) - MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) - MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) - MAP_URI_AUTO_JON2("/start_mining", on_start_mining, COMMAND_RPC_START_MINING) - MAP_URI_AUTO_JON2("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING) - MAP_URI_AUTO_JON2("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON) - MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) - BEGIN_JSON_RPC_MAP("/json_rpc") - MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) - MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) - MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) - MAP_JON_RPC_WE("getcurrencyid", on_get_currency_id, COMMAND_RPC_GET_CURRENCY_ID) - MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK) - MAP_JON_RPC_WE("getlastblockheader", on_get_last_block_header, COMMAND_RPC_GET_LAST_BLOCK_HEADER) - MAP_JON_RPC_WE("getblockheaderbyhash", on_get_block_header_by_hash, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH) - MAP_JON_RPC_WE("getblockheaderbyheight", on_get_block_header_by_height, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT) - END_JSON_RPC_MAP() - END_URI_MAP2() - - bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, connection_context& cntx); - bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx); - bool on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res, connection_context& cntx); - bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx); - bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx); - bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx); - bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx); - bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx); - bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, connection_context& cntx); - bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx); - bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx); - - //json_rpc - bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, connection_context& cntx); - bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& req, COMMAND_RPC_GET_CURRENCY_ID::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, connection_context& cntx); - 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, connection_context& cntx); - 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, connection_context& cntx); - 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, connection_context& cntx); - //----------------------- - bool handle_command_line(const boost::program_options::variables_map& vm); - bool check_core_ready(); - - //utils - bool fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce); - - core& m_core; - nodetool::node_server >& m_p2p; - std::string m_port; - std::string m_bind_ip; - }; -} diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 2c6afa77e9..c42b4a376a 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include "cryptonote_core/difficulty.h" #include "crypto/hash.h" -namespace cryptonote +namespace CryptoNote { //----------------------------------------------- #define CORE_RPC_STATUS_OK "OK" @@ -340,7 +340,7 @@ namespace cryptonote struct response { uint64_t difficulty; - uint64_t height; + uint32_t height; uint64_t reserved_offset; blobdata blocktemplate_blob; std::string status; diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h index edb619b656..0b7a3ce4ae 100644 --- a/src/rpc/core_rpc_server_error_codes.h +++ b/src/rpc/core_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/serialization/BinaryInputStreamSerializer.cpp b/src/serialization/BinaryInputStreamSerializer.cpp index 5b981cfaf3..ed24bbf5a0 100644 --- a/src/serialization/BinaryInputStreamSerializer.cpp +++ b/src/serialization/BinaryInputStreamSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -62,7 +62,7 @@ void readVarintAs(std::istream& s, T &i) { } -namespace cryptonote { +namespace CryptoNote { ISerializer::SerializerType BinaryInputStreamSerializer::type() const { return ISerializer::INPUT; @@ -111,7 +111,7 @@ ISerializer& BinaryInputStreamSerializer::operator()(uint64_t& value, const std: } ISerializer& BinaryInputStreamSerializer::operator()(bool& value, const std::string& name) { - value = static_cast(stream.get()); + value = stream.get() != 0; return *this; } diff --git a/src/serialization/BinaryInputStreamSerializer.h b/src/serialization/BinaryInputStreamSerializer.h index 46707df8da..dc956b7334 100644 --- a/src/serialization/BinaryInputStreamSerializer.h +++ b/src/serialization/BinaryInputStreamSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include -namespace cryptonote { +namespace CryptoNote { class BinaryInputStreamSerializer : public ISerializer { public: diff --git a/src/serialization/BinaryOutputStreamSerializer.cpp b/src/serialization/BinaryOutputStreamSerializer.cpp index 5365c01dc2..7b8b55ff5d 100644 --- a/src/serialization/BinaryOutputStreamSerializer.cpp +++ b/src/serialization/BinaryOutputStreamSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -38,7 +38,7 @@ writeVarint(std::ostream& s, T i) { } -namespace cryptonote { +namespace CryptoNote { ISerializer::SerializerType BinaryOutputStreamSerializer::type() const { return ISerializer::OUTPUT; diff --git a/src/serialization/BinaryOutputStreamSerializer.h b/src/serialization/BinaryOutputStreamSerializer.h index cd70464db7..1de71c0420 100644 --- a/src/serialization/BinaryOutputStreamSerializer.h +++ b/src/serialization/BinaryOutputStreamSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include -namespace cryptonote { +namespace CryptoNote { class BinaryOutputStreamSerializer : public ISerializer { public: diff --git a/src/serialization/ISerializer.h b/src/serialization/ISerializer.h index a30a7f903c..d610d4fc31 100644 --- a/src/serialization/ISerializer.h +++ b/src/serialization/ISerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class ISerializer { public: diff --git a/src/serialization/IStream.h b/src/serialization/IStream.h index 6269c6e302..c979ac462c 100644 --- a/src/serialization/IStream.h +++ b/src/serialization/IStream.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class IInputStream { public: diff --git a/src/serialization/JsonInputStreamSerializer.cpp b/src/serialization/JsonInputStreamSerializer.cpp index 124b172a4c..37aa12f69d 100644 --- a/src/serialization/JsonInputStreamSerializer.cpp +++ b/src/serialization/JsonInputStreamSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) { stream >> root; @@ -30,4 +30,4 @@ JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) { JsonInputStreamSerializer::~JsonInputStreamSerializer() { } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/serialization/JsonInputStreamSerializer.h b/src/serialization/JsonInputStreamSerializer.h index ff67f95163..ce8ea94597 100644 --- a/src/serialization/JsonInputStreamSerializer.h +++ b/src/serialization/JsonInputStreamSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,12 +20,10 @@ #include #include #include +#include "../Common/JsonValue.h" +#include "JsonInputValueSerializer.h" -//#include "serialization/Enumerator.h" -#include "serialization/JsonInputValueSerializer.h" -#include "serialization/JsonValue.h" - -namespace cryptonote { +namespace CryptoNote { //deserialization class JsonInputStreamSerializer : public JsonInputValueSerializer { @@ -34,7 +32,7 @@ class JsonInputStreamSerializer : public JsonInputValueSerializer { virtual ~JsonInputStreamSerializer(); private: - JsonValue root; + Common::JsonValue root; }; } diff --git a/src/serialization/JsonInputValueSerializer.cpp b/src/serialization/JsonInputValueSerializer.cpp index d8f841024b..60c37307d5 100644 --- a/src/serialization/JsonInputValueSerializer.cpp +++ b/src/serialization/JsonInputValueSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,12 +15,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "serialization/JsonInputValueSerializer.h" - +#include "JsonInputValueSerializer.h" #include #include +#include -namespace cryptonote { +using Common::JsonValue; +using namespace CryptoNote; JsonInputValueSerializer::JsonInputValueSerializer() : root(nullptr) { } @@ -115,7 +116,7 @@ ISerializer& JsonInputValueSerializer::operator()(uint64_t& value, const std::st ISerializer& JsonInputValueSerializer::operator()(double& value, const std::string& name) { assert(root); - value = getValue(name).getDouble(); + value = getValue(name).getReal(); return *this; } @@ -138,20 +139,27 @@ ISerializer& JsonInputValueSerializer::operator()(bool& value, const std::string } bool JsonInputValueSerializer::hasObject(const std::string& name) { - return chain.back()->count(name) != 0; + assert(root); + + const Common::JsonValue* value; + if (chain.empty()) { + value = root; + } else { + value = chain.back(); + } + + return value->count(name) != 0; } ISerializer& JsonInputValueSerializer::binary(void* value, std::size_t size, const std::string& name) { - assert(false); - throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); - + auto str = getValue(name).getString(); + Common::fromHex(str, value, size); return *this; } ISerializer& JsonInputValueSerializer::binary(std::string& value, const std::string& name) { - assert(false); - throw std::runtime_error("JsonInputValueSerializer doesn't support this type of serialization"); - + auto str = getValue(name).getString(); + value = Common::asString(Common::fromHex(str)); return *this; } @@ -161,8 +169,5 @@ JsonValue JsonInputValueSerializer::getValue(const std::string& name) { } int64_t JsonInputValueSerializer::getNumber(const std::string& name) { - return getValue(name).getNumber(); -} - - + return getValue(name).getInteger(); } diff --git a/src/serialization/JsonInputValueSerializer.h b/src/serialization/JsonInputValueSerializer.h index 8bf269ce71..29e535232c 100644 --- a/src/serialization/JsonInputValueSerializer.h +++ b/src/serialization/JsonInputValueSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,10 +17,10 @@ #pragma once -#include "serialization/ISerializer.h" -#include "serialization/JsonValue.h" +#include "../Common/JsonValue.h" +#include "ISerializer.h" -namespace cryptonote { +namespace CryptoNote { //deserialization class JsonInputValueSerializer : public ISerializer { @@ -28,7 +28,7 @@ class JsonInputValueSerializer : public ISerializer { JsonInputValueSerializer(); virtual ~JsonInputValueSerializer(); - void setJsonValue(const JsonValue* value); + void setJsonValue(const Common::JsonValue* value); SerializerType type() const; virtual ISerializer& beginObject(const std::string& name) override; @@ -57,13 +57,12 @@ class JsonInputValueSerializer : public ISerializer { } private: + const Common::JsonValue* root; + std::vector chain; + std::vector idxs; - JsonValue getValue(const std::string& name); + Common::JsonValue getValue(const std::string& name); int64_t getNumber(const std::string& name); - - const JsonValue* root; - std::vector chain; - std::vector idxs; }; } diff --git a/src/serialization/JsonOutputStreamSerializer.cpp b/src/serialization/JsonOutputStreamSerializer.cpp index dfd62cd7e2..f3f2703677 100644 --- a/src/serialization/JsonOutputStreamSerializer.cpp +++ b/src/serialization/JsonOutputStreamSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,24 +15,25 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "serialization/JsonOutputStreamSerializer.h" - -#include "string_tools.h" - +#include "JsonOutputStreamSerializer.h" #include #include +#include "Common/StringTools.h" -namespace cryptonote { +using Common::JsonValue; +using namespace CryptoNote; -JsonOutputStreamSerializer::JsonOutputStreamSerializer() : root(JsonValue::OBJECT) { +namespace CryptoNote { +std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& enumerator) { + out << enumerator.root; + return out; +} } -JsonOutputStreamSerializer::~JsonOutputStreamSerializer() { +JsonOutputStreamSerializer::JsonOutputStreamSerializer() : root(JsonValue::OBJECT) { } -std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& enumerator) { - out << enumerator.root; - return out; +JsonOutputStreamSerializer::~JsonOutputStreamSerializer() { } JsonValue JsonOutputStreamSerializer::getJsonValue() const { @@ -140,26 +141,22 @@ ISerializer& JsonOutputStreamSerializer::operator()(uint8_t& value, const std::s ISerializer& JsonOutputStreamSerializer::operator()(bool& value, const std::string& name) { JsonValue* val = chain.back(); - JsonValue v; - v = static_cast(value); if (val->isArray()) { - val->pushBack(v); + val->pushBack(JsonValue(value)); } else { - val->insert(name, v); + val->insert(name, JsonValue(value)); } return *this; } ISerializer& JsonOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { - auto str = static_cast(value); - std::string tmpbuf(str, str + size); - return binary(tmpbuf, name); + auto hex = Common::toHex(value, size); + return (*this)(hex, name); } ISerializer& JsonOutputStreamSerializer::binary(std::string& value, const std::string& name) { - std::string hex = epee::string_tools::buff_to_hex(value); - return (*this)(hex, name); + return binary(const_cast(value.data()), value.size(), name); } bool JsonOutputStreamSerializer::hasObject(const std::string& name) { @@ -168,5 +165,3 @@ bool JsonOutputStreamSerializer::hasObject(const std::string& name) { return false; } - -} diff --git a/src/serialization/JsonOutputStreamSerializer.h b/src/serialization/JsonOutputStreamSerializer.h index 26d444f860..665350b76f 100644 --- a/src/serialization/JsonOutputStreamSerializer.h +++ b/src/serialization/JsonOutputStreamSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,19 +17,18 @@ #pragma once -#include "serialization/ISerializer.h" -#include "serialization/JsonValue.h" - #include +#include "../Common/JsonValue.h" +#include "ISerializer.h" -namespace cryptonote { +namespace CryptoNote { class JsonOutputStreamSerializer : public ISerializer { public: JsonOutputStreamSerializer(); virtual ~JsonOutputStreamSerializer(); - JsonValue getJsonValue() const; + Common::JsonValue getJsonValue() const; SerializerType type() const; virtual ISerializer& beginObject(const std::string& name) override; @@ -60,8 +59,8 @@ class JsonOutputStreamSerializer : public ISerializer { friend std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& enumerator); private: - JsonValue root; - std::vector chain; + Common::JsonValue root; + std::vector chain; }; -} // namespace cryptonote +} diff --git a/src/serialization/JsonSerializationDispatcher.h b/src/serialization/JsonSerializationDispatcher.h deleted file mode 100644 index 94f26e2ed2..0000000000 --- a/src/serialization/JsonSerializationDispatcher.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include - -#include "serialization/JsonOutputStreamSerializer.h" -#include "serialization/JsonInputStreamSerializer.h" -#include "storages/portable_storage_template_helper.h" - -namespace { -BOOST_TTI_HAS_MEMBER_FUNCTION(serialize) -} //namespace - -namespace cryptonote { - -template -inline typename std::enable_if::value, void>::type SerializeToJson(T& obj, std::string& jsonBuff) { - std::stringstream stream; - JsonOutputStreamSerializer serializer; - - obj.serialize(serializer, ""); - - stream << serializer; - jsonBuff = stream.str(); -} - -template -inline typename std::enable_if::value, void>::type LoadFromJson(T& obj, const std::string& jsonBuff) { - std::stringstream stream(jsonBuff); - JsonInputStreamSerializer serializer(stream); - - obj.serialize(serializer, ""); -} - -//old epee serialization - -template -inline typename std::enable_if::value, void>::type SerializeToJson(T& obj, std::string& jsonBuff) { - epee::serialization::store_t_to_json(obj, jsonBuff); -} - -template -inline typename std::enable_if::value, void>::type LoadFromJson(T& obj, const std::string& jsonBuff) { - epee::serialization::load_t_from_json(obj, jsonBuff); -} - -} //namespace cryptonote diff --git a/src/serialization/JsonValue.cpp b/src/serialization/JsonValue.cpp deleted file mode 100644 index bd517679bd..0000000000 --- a/src/serialization/JsonValue.cpp +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "JsonValue.h" -#include -#include -#include - -namespace cryptonote { - -JsonValue::JsonValue() : d_type(NIL) { -} - -JsonValue::JsonValue(JsonValue::Type type) { - switch(type) { - case OBJECT: - new(d_valueObject)JsonValue::Object(); - break; - case ARRAY: - new(d_valueArray)JsonValue::Array(); - break; - default: - throw std::runtime_error("Wrong JsonValue type. Object or Array are possible only"); - } - - d_type = type; -} - -JsonValue::JsonValue(const JsonValue& other) : d_type(other.d_type) { - switch (d_type) { - case ARRAY: - new(d_valueArray)JsonValue::Array(*reinterpret_cast(other.d_valueArray)); - break; - case BOOL: - d_valueBool = other.d_valueBool; - break; - case INT64: - d_valueInt64 = other.d_valueInt64; - break; - case NIL: - break; - case OBJECT: - new(d_valueObject)JsonValue::Object(*reinterpret_cast(other.d_valueObject)); - break; - case DOUBLE: - d_valueDouble = other.d_valueDouble; - break; - case STRING: - new(d_valueString)std::string(*reinterpret_cast(other.d_valueString)); - break; - default: - throw(std::runtime_error("Invalid type")); - } -} - -JsonValue::~JsonValue() { - destructValue(); -} - -bool JsonValue::isArray() const { - return d_type == ARRAY; -} - -bool JsonValue::isBool() const { - return d_type == BOOL; -} - -bool JsonValue::isInt64() const { - return d_type == INT64; -} - -bool JsonValue::isNil() const { - return d_type == NIL; -} - -bool JsonValue::isObject() const { - return d_type == OBJECT; -} - -bool JsonValue::isDouble() const { - return d_type == DOUBLE; -} - -bool JsonValue::isString() const { - return d_type == STRING; -} - -bool JsonValue::getBool() const { - assert(d_type == BOOL); - if (d_type != BOOL) { - throw(std::runtime_error("Value type is not BOOL")); - } - - return d_valueBool; -} - -int64_t JsonValue::getNumber() const { - assert(d_type == INT64); - if (d_type != INT64) { - throw(std::runtime_error("Value type is not INT64")); - } - - return d_valueInt64; -} - -const JsonValue::Object& JsonValue::getObject() const { - assert(d_type == OBJECT); - if (d_type != OBJECT) { - throw(std::runtime_error("Value type is not OBJECT")); - } - - return *reinterpret_cast(d_valueObject); -} - -double JsonValue::getDouble() const { - assert(d_type == DOUBLE); - if (d_type != DOUBLE) { - throw(std::runtime_error("Value type is not DOUBLE")); - } - - return d_valueDouble; -} - -std::string JsonValue::getString() const { - assert(d_type == STRING); - if (d_type != STRING) { - throw(std::runtime_error("Value type is not STRING")); - } - - return *reinterpret_cast(d_valueString); -} - -const JsonValue& JsonValue::operator()(const std::string& name) const { - assert(d_type == OBJECT); - assert(reinterpret_cast(d_valueObject)->count(name) > 0); - if (d_type != OBJECT) { - throw(std::runtime_error("Value type is not OBJECT")); - } - - return reinterpret_cast(d_valueObject)->at(name); -} - -size_t JsonValue::count(const std::string& name) const { - assert(d_type == OBJECT); - if (d_type != OBJECT) { - throw(std::runtime_error("Value type is not OBJECT")); - } - - return reinterpret_cast(d_valueObject)->count(name); -} - -const JsonValue& JsonValue::operator[](size_t index) const { - assert(d_type == ARRAY); - if (d_type != ARRAY) { - throw(std::runtime_error("Value type is not ARRAY")); - } - - return reinterpret_cast(d_valueArray)->at(index); -} - -size_t JsonValue::size() const { - assert(d_type == ARRAY || d_type == OBJECT); - switch (d_type) { - case OBJECT: - return reinterpret_cast(d_valueString)->size(); - case ARRAY: - return reinterpret_cast(d_valueString)->size(); - default: - throw(std::runtime_error("Value type is not ARRAY or OBJECT")); - } -} - -JsonValue::Array::const_iterator JsonValue::begin() const { - assert(d_type == ARRAY); - if (d_type != ARRAY) { - throw(std::runtime_error("Value type is not ARRAY")); - } - - return reinterpret_cast(d_valueArray)->begin(); -} - -JsonValue::Array::const_iterator JsonValue::end() const { - assert(d_type == ARRAY); - if (d_type != ARRAY) { - throw(std::runtime_error("Value type is not ARRAY")); - } - - return reinterpret_cast(d_valueArray)->end(); -} - -void JsonValue::readArray(std::istream& in) { - char c; - JsonValue::Array value; - - c = in.peek(); - while (true) { - if (!isspace(in.peek())) break; - in.read(&c, 1); - } - - if (in.peek() != ']') { - for (;;) { - value.resize(value.size() + 1); - in >> value.back(); - in >> c; - while (isspace(c)) in >> c; - if (c == ']') { - break; - } - - if (c != ',') { - throw(std::runtime_error("Unable to parse")); - } - } - } else { - in.read(&c, 1); - } - - if (d_type != JsonValue::ARRAY) { - destructValue(); - d_type = JsonValue::NIL; - new(d_valueArray)JsonValue::Array; - d_type = JsonValue::ARRAY; - } - - reinterpret_cast(d_valueArray)->swap(value); -} - -void JsonValue::readTrue(std::istream& in) { - char data[3]; - in.read(data, 3); - if (data[0] != 'r' || data[1] != 'u' || data[2] != 'e') { - throw(std::runtime_error("Unable to parse")); - } - - if (d_type != JsonValue::BOOL) { - destructValue(); - d_type = JsonValue::BOOL; - } - - d_valueBool = true; -} - -void JsonValue::readFalse(std::istream& in) { - char data[4]; - in.read(data, 4); - if (data[0] != 'a' || data[1] != 'l' || data[2] != 's' || data[3] != 'e') { - throw(std::runtime_error("Unable to parse")); - } - - if (d_type != JsonValue::BOOL) { - destructValue(); - d_type = JsonValue::BOOL; - } - - d_valueBool = false; -} - -void JsonValue::readNumber(std::istream& in, char c) { - std::string text; - text += c; - size_t dots = 0; - for (;;) { - int i = in.peek(); - if (i >= '0' && i <= '9') { - in.read(&c, 1); - text += c; - } else if (i == '.') { - in.read(&c, 1); - text += '.'; - ++dots; - } else { - break; - } - } - - if (dots > 0) { - if (dots > 1) { - throw(std::runtime_error("Unable to parse")); - } - - int i = in.peek(); - if (in.peek() == 'e') { - in.read(&c, 1); - text += c; - i = in.peek(); - if (i == '+') { - in.read(&c, 1); - text += c; - i = in.peek(); - } else if (i == '-') { - in.read(&c, 1); - text += c; - i = in.peek(); - } - - if (i < '0' || i > '9') { - throw(std::runtime_error("Unable to parse")); - } - - do { - in.read(&c, 1); - text += c; - i = in.peek(); - } while (i >= '0' && i <= '9'); - } - - double value; - std::istringstream(text) >> value; - if (d_type != JsonValue::DOUBLE) { - destructValue(); - d_type = JsonValue::DOUBLE; - } - - d_valueDouble = value; - } else { - if (text.size() > 1 && ((text[0] == '0') || (text[0] == '-' && text[1] == '0'))) { - throw(std::runtime_error("Unable to parse")); - } - - int64_t value; - std::istringstream(text) >> value; - if (d_type != JsonValue::INT64) { - destructValue(); - d_type = JsonValue::INT64; - } - - d_valueInt64 = value; - } -} - -void JsonValue::readNull(std::istream& in) { - char data[3]; - in.read(data, 3); - if (data[0] != 'u' || data[1] != 'l' || data[2] != 'l') { - throw(std::runtime_error("Unable to parse")); - } - - if (d_type != JsonValue::NIL) { - destructValue(); - d_type = JsonValue::NIL; - } -} - -void JsonValue::readObject(std::istream& in) { - char c; - JsonValue::Object value; - in >> c; - while (isspace(c)) in >> c; - - if (c != '}') { - std::string name; - for (;;) { - if (c != '"') { - throw(std::runtime_error("Unable to parse")); - } - - name.clear(); - for (;;) { - in >> c; - if (c == '"') { - break; - } - - if (c == '\\') { - name += c; - in >> c; - } - - name += c; - } - - in >> c; - while (isspace(c)) in >> c; - if (c != ':') { - throw(std::runtime_error("Unable to parse")); - } - - in >> value[name]; - in >> c; - while (isspace(c)) in >> c; - if (c == '}') { - break; - } - - if (c != ',') { - throw(std::runtime_error("Unable to parse")); - } - in >> c; - while (isspace(c)) in >> c; - } - } - - if (d_type != JsonValue::OBJECT) { - destructValue(); - d_type = JsonValue::NIL; - new(d_valueObject)JsonValue::Object; - d_type = JsonValue::OBJECT; - } - - reinterpret_cast(d_valueObject)->swap(value); -} - -void JsonValue::readString(std::istream& in) { - char c; - std::string value; - - for (;;) { - in.read(&c, 1); - if (c == '"') { - break; - } - - if (c == '\\') { - value += c; - in >> c; - } - - value += c; - } - - if (d_type != JsonValue::STRING) { - destructValue(); - d_type = JsonValue::NIL; - new(d_valueString)std::string; - d_type = JsonValue::STRING; - } - - reinterpret_cast(d_valueString)->swap(value); -} - -std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { - char c; - in >> c; - while (isspace(c)) in >> c; - if (c == '[') { - jsonValue.readArray(in); - } else if (c == 't') { - jsonValue.readTrue(in); - } else if (c == 'f') { - jsonValue.readFalse(in); - } else if ((c == '-') || (c >= '0' && c <= '9')) { - jsonValue.readNumber(in, c); - } else if (c == 'n') { - jsonValue.readNull(in); - } else if (c == '{') { - jsonValue.readObject(in); - } else if (c == '"') { - jsonValue.readString(in); - } else { - throw(std::runtime_error("Unable to parse")); - } - - return in; -} - -std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { - if (jsonValue.d_type == JsonValue::ARRAY) { - const JsonValue::Array& array = *reinterpret_cast(jsonValue.d_valueArray); - out << '['; - if (array.size() > 0) { - out << array[0]; - for (size_t i = 1; i < array.size(); ++i) { - out << ',' << array[i]; - } - } - - out << ']'; - } else if (jsonValue.d_type == JsonValue::BOOL) { - out << (jsonValue.d_valueBool ? "true" : "false"); - } else if (jsonValue.d_type == JsonValue::INT64) { - out << jsonValue.d_valueInt64; - } else if (jsonValue.d_type == JsonValue::NIL) { - out << "null"; - } else if (jsonValue.d_type == JsonValue::OBJECT) { - const JsonValue::Object& object = *reinterpret_cast(jsonValue.d_valueObject); - out << '{'; - auto iter = object.begin(); - if (iter != object.end()) { - out << '"' << iter->first << "\":" << iter->second; - ++iter; - for (; iter != object.end(); ++iter) { - out << ",\"" << iter->first << "\":" << iter->second; - } - } - - out << '}'; - } else if (jsonValue.d_type == JsonValue::DOUBLE) { - std::ostringstream stream; - stream << std::fixed << std::setprecision(11) << jsonValue.d_valueDouble; - std::string value = stream.str(); - while (value.size() > 1 && value[value.size() - 2] != '.' && value[value.size() - 1] == '0') { - value.resize(value.size() - 1); - } - - out << value; - } else if (jsonValue.d_type == JsonValue::STRING) { - out << '"' << *reinterpret_cast(jsonValue.d_valueString) << '"'; - } else { - throw(std::runtime_error("Invalid type")); - } - - return out; -} - -void JsonValue::destructValue() { - switch (d_type) { - case ARRAY: - reinterpret_cast(d_valueArray)->~Array(); - break; - case OBJECT: - reinterpret_cast(d_valueObject)->~Object(); - break; - case STRING: - reinterpret_cast(d_valueString)->~basic_string(); - break; - default: - break; - } -} - -JsonValue& JsonValue::pushBack(const JsonValue& val) { - if (d_type != ARRAY) { - throw std::runtime_error("JsonValue error. pushBack is only possible for arrays"); - } - - Array* array = reinterpret_cast(d_valueArray); - array->push_back(val); - - return array->back(); -} - -JsonValue& JsonValue::insert(const std::string& key, const JsonValue& value) { - if (d_type != OBJECT) { - throw std::runtime_error("JsonValue error. insert is only possible for objects"); - } - - Object* obj = reinterpret_cast(d_valueObject); - - auto res = obj->insert(std::make_pair(key, value)); - return res.first->second; -} - -JsonValue& JsonValue::operator=(bool value) { - if (d_type != BOOL) { - destructValue(); - d_type = BOOL; - } - - d_valueBool = value; - - return *this; -} - -JsonValue& JsonValue::operator=(int64_t value) { - if (d_type != INT64) { - destructValue(); - d_type = INT64; - } - - d_valueInt64 = value; - - return *this; -} - -//JsonValue& JsonValue::operator=(NilType value) { -// if (d_type != NIL) { -// destructValue(); -// d_type = NIL; -// } -//} - -JsonValue& JsonValue::operator=(double value) { - if (d_type != DOUBLE) { - destructValue(); - d_type = DOUBLE; - } - - d_valueDouble = value; - - return *this; -} - -JsonValue& JsonValue::operator=(const std::string& value) { - if (d_type != STRING) { - destructValue(); - new(d_valueString)std::string; - d_type = STRING; - } - - reinterpret_cast(d_valueString)->assign(value.data(), value.size()); - - return *this; -} - -JsonValue& JsonValue::operator=(const char* value) { - return operator=(std::string(value)); -} - -} //namespace cryptonote diff --git a/src/serialization/JsonValue.h b/src/serialization/JsonValue.h deleted file mode 100644 index b44c86a62a..0000000000 --- a/src/serialization/JsonValue.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include - -#include - -namespace cryptonote { - -class JsonValue { -public: - typedef std::vector Array; - typedef std::map Object; - - enum Type { - ARRAY, - BOOL, - INT64, - NIL, - OBJECT, - DOUBLE, - STRING - }; - - JsonValue(); - JsonValue(Type type); - JsonValue(const JsonValue& other); - ~JsonValue(); - JsonValue& operator=(const JsonValue& other) = delete; - bool isArray() const; - bool isBool() const; - bool isInt64() const; - bool isNil() const; - bool isObject() const; - bool isDouble() const; - bool isString() const; - bool getBool() const; - int64_t getNumber() const; - const Object& getObject() const; - double getDouble() const; - std::string getString() const; - const JsonValue& operator()(const std::string& name) const; - size_t count(const std::string& name) const; - const JsonValue& operator[](size_t index) const; - size_t size() const; - Array::const_iterator begin() const; - Array::const_iterator end() const; - - JsonValue& pushBack(const JsonValue& val); - JsonValue& insert(const std::string& key, const JsonValue& value); - - JsonValue& operator=(bool value); - JsonValue& operator=(int64_t value); -// JsonValue& operator=(NilType value); - JsonValue& operator=(double value); - JsonValue& operator=(const std::string& value); - JsonValue& operator=(const char* value); - - - friend std::istream& operator>>(std::istream& in, JsonValue& jsonValue); - friend std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue); - -private: - size_t d_type; - union { - uint8_t d_valueArray[sizeof(Array)]; - bool d_valueBool; - int64_t d_valueInt64; - uint8_t d_valueObject[sizeof(Object)]; - double d_valueDouble; - uint8_t d_valueString[sizeof(std::string)]; - }; - - void destructValue(); - - void readArray(std::istream& in); - void readTrue(std::istream& in); - void readFalse(std::istream& in); - void readNumber(std::istream& in, char c); - void readNull(std::istream& in); - void readObject(std::istream& in); - void readString(std::istream& in); -}; - -} //namespace cryptonote diff --git a/src/serialization/KVBinaryCommon.h b/src/serialization/KVBinaryCommon.h index 21b1bcbb05..fe4ea0fc48 100644 --- a/src/serialization/KVBinaryCommon.h +++ b/src/serialization/KVBinaryCommon.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/serialization/KVBinaryInputStreamSerializer.cpp b/src/serialization/KVBinaryInputStreamSerializer.cpp index 6dd24ce1ea..58ca3f79d6 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.cpp +++ b/src/serialization/KVBinaryInputStreamSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,17 +16,14 @@ // along with Bytecoin. If not, see . #include "KVBinaryInputStreamSerializer.h" -#include "KVBinaryCommon.h" - -#include "JsonValue.h" - #include #include -#include #include +#include +#include "KVBinaryCommon.h" +using Common::JsonValue; using namespace CryptoNote; -using namespace cryptonote; namespace { @@ -38,16 +35,16 @@ T readPod(std::istream& s) { } template -cryptonote::JsonValue readPodJson(std::istream& s) { +JsonValue readPodJson(std::istream& s) { T v; s.read(reinterpret_cast(&v), sizeof(T)); - cryptonote::JsonValue jv; + JsonValue jv; jv = static_cast(v); return jv; } template -cryptonote::JsonValue readIntegerJson(std::istream& s) { +JsonValue readIntegerJson(std::istream& s) { return readPodJson(s); } @@ -98,9 +95,6 @@ void readName(std::istream& s, std::string& name) { } - -namespace cryptonote { - void KVBinaryInputStreamSerializer::parse() { auto hdr = readPod(stream); @@ -164,7 +158,7 @@ JsonValue KVBinaryInputStreamSerializer::loadValue(uint8_t type) { case BIN_KV_SERIALIZE_TYPE_UINT16: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_UINT8: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_DOUBLE: return readPodJson(stream); - case BIN_KV_SERIALIZE_TYPE_BOOL: return readPodJson(stream); + case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(stream.get() != 0); case BIN_KV_SERIALIZE_TYPE_STRING: return readStringJson(stream); case BIN_KV_SERIALIZE_TYPE_OBJECT: return loadSection(); case BIN_KV_SERIALIZE_TYPE_ARRAY: return loadArray(type); @@ -195,6 +189,3 @@ JsonValue KVBinaryInputStreamSerializer::loadArray(uint8_t itemType) { return arr; } - - -} diff --git a/src/serialization/KVBinaryInputStreamSerializer.h b/src/serialization/KVBinaryInputStreamSerializer.h index 41d83c34c5..ee922b4174 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.h +++ b/src/serialization/KVBinaryInputStreamSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,16 +17,14 @@ #pragma once -#include "ISerializer.h" -#include "SerializationOverloads.h" - -#include "JsonValue.h" -#include "JsonInputValueSerializer.h" - #include #include +#include "../Common/JsonValue.h" +#include "ISerializer.h" +#include "JsonInputValueSerializer.h" +#include "SerializationOverloads.h" -namespace cryptonote { +namespace CryptoNote { class KVBinaryInputStreamSerializer : public JsonInputValueSerializer { public: @@ -39,14 +37,13 @@ class KVBinaryInputStreamSerializer : public JsonInputValueSerializer { virtual ISerializer& binary(std::string& value, const std::string& name) override; private: - - JsonValue loadSection(); - JsonValue loadEntry(); - JsonValue loadValue(uint8_t type); - JsonValue loadArray(uint8_t itemType); - - std::unique_ptr root; + std::unique_ptr root; std::istream& stream; + + Common::JsonValue loadSection(); + Common::JsonValue loadEntry(); + Common::JsonValue loadValue(uint8_t type); + Common::JsonValue loadArray(uint8_t itemType); }; } diff --git a/src/serialization/KVBinaryOutputStreamSerializer.cpp b/src/serialization/KVBinaryOutputStreamSerializer.cpp index c275661b4d..479ddfd1f0 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.cpp +++ b/src/serialization/KVBinaryOutputStreamSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,6 @@ #include using namespace CryptoNote; -using namespace cryptonote; namespace { @@ -65,7 +64,7 @@ size_t writeArraySize(IOutputStream& s, size_t val) { } -namespace cryptonote { +namespace CryptoNote { using namespace CryptoNote; diff --git a/src/serialization/KVBinaryOutputStreamSerializer.h b/src/serialization/KVBinaryOutputStreamSerializer.h index 553c375074..86d3eec048 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.h +++ b/src/serialization/KVBinaryOutputStreamSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,7 +26,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class KVBinaryOutputStreamSerializer : public ISerializer { public: diff --git a/src/serialization/MemoryStream.cpp b/src/serialization/MemoryStream.cpp index 99bc74ad05..da10cd689d 100644 --- a/src/serialization/MemoryStream.cpp +++ b/src/serialization/MemoryStream.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,4 +16,3 @@ // along with Bytecoin. If not, see . #include "MemoryStream.h" - diff --git a/src/serialization/MemoryStream.h b/src/serialization/MemoryStream.h index 52f87e3280..1b995221c8 100644 --- a/src/serialization/MemoryStream.h +++ b/src/serialization/MemoryStream.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,7 @@ #include #include // memcpy -namespace cryptonote { +namespace CryptoNote { class MemoryStream: public IInputStream, @@ -79,4 +79,3 @@ class MemoryStream: }; } - diff --git a/src/serialization/SerializationOverloads.cpp b/src/serialization/SerializationOverloads.cpp index d5a7e16adb..a344e532a1 100644 --- a/src/serialization/SerializationOverloads.cpp +++ b/src/serialization/SerializationOverloads.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,9 +19,9 @@ #include -namespace cryptonote { +namespace CryptoNote { -//void readVarint(uint64_t& value, cryptonote::ISerializer& serializer) { +//void readVarint(uint64_t& value, CryptoNote::ISerializer& serializer) { // const int bits = std::numeric_limits::digits; // // uint64_t v = 0; @@ -46,7 +46,7 @@ namespace cryptonote { // value = v; //} // -//void writeVarint(uint64_t& value, cryptonote::ISerializer& serializer) { +//void writeVarint(uint64_t& value, CryptoNote::ISerializer& serializer) { // uint64_t v = value; // // while (v >= 0x80) { @@ -60,10 +60,10 @@ namespace cryptonote { //} // // -//void serializeVarint(uint64_t& value, const std::string& name, cryptonote::ISerializer& serializer) { +//void serializeVarint(uint64_t& value, const std::string& name, CryptoNote::ISerializer& serializer) { // serializer.tag(name); // -// if (serializer.type() == cryptonote::ISerializer::INPUT) { +// if (serializer.type() == CryptoNote::ISerializer::INPUT) { // readVarint(value, serializer); // } else { // writeVarint(value, serializer); @@ -72,7 +72,7 @@ namespace cryptonote { // serializer.endTag(); //} // -//void serializeVarint(uint32_t& value, const std::string& name, cryptonote::ISerializer& serializer) { +//void serializeVarint(uint32_t& value, const std::string& name, CryptoNote::ISerializer& serializer) { // uint64_t v = value; // serializeVarint(v, name, serializer); // value = static_cast(v); diff --git a/src/serialization/SerializationOverloads.h b/src/serialization/SerializationOverloads.h index 4c98a8456c..921e9e5271 100644 --- a/src/serialization/SerializationOverloads.h +++ b/src/serialization/SerializationOverloads.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,17 +19,18 @@ #include "ISerializer.h" +#include +#include +#include #include #include #include -#include -#include -namespace cryptonote { +namespace CryptoNote { template typename std::enable_if::value>::type -serializeAsBinary(std::vector& value, const std::string& name, cryptonote::ISerializer& serializer) { +serializeAsBinary(std::vector& value, const std::string& name, CryptoNote::ISerializer& serializer) { std::string blob; if (serializer.type() == ISerializer::INPUT) { serializer.binary(blob, name); @@ -46,26 +47,40 @@ serializeAsBinary(std::vector& value, const std::string& name, cryptonote::IS } template -void serialize(std::vector& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(std::vector& value, const std::string& name, CryptoNote::ISerializer& serializer) { std::size_t size = value.size(); serializer.beginArray(size, name); value.resize(size); - for (size_t i = 0; i < size; ++i) { - serializer(value[i], ""); + for (auto& item : value) { + serializer(item, ""); } serializer.endArray(); } +template +void serialize(std::list& value, const std::string& name, CryptoNote::ISerializer& serializer) { + std::size_t size = value.size(); + serializer.beginArray(size, name); + value.resize(size); + + for (auto& item : value) { + serializer(item, ""); + } + + serializer.endArray(); +} + + template -void serialize(std::unordered_map& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(std::unordered_map& value, const std::string& name, CryptoNote::ISerializer& serializer) { std::size_t size; size = value.size(); serializer.beginArray(size, name); - if (serializer.type() == cryptonote::ISerializer::INPUT) { + if (serializer.type() == CryptoNote::ISerializer::INPUT) { value.reserve(size); for (size_t i = 0; i < size; ++i) { @@ -93,7 +108,7 @@ void serialize(std::unordered_map& value, const std::string& name, c } template -void serialize(std::array& value, const std::string& name, cryptonote::ISerializer& s) { +void serialize(std::array& value, const std::string& name, CryptoNote::ISerializer& s) { s.binary(value.data(), value.size(), name); } diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 02474c0578..d3dc59d952 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,9 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -/* binary_archive.h - * - * Portable (low-endian) binary archive */ #pragma once #include @@ -25,11 +22,7 @@ #include #include -#include "common/varint.h" -#include "warnings.h" - -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4244) +#include "Common/varint.h" //TODO: fix size_t warning in x32 platform @@ -178,5 +171,3 @@ struct binary_archive : public binary_archive_base serialize_int(t); } }; - -POP_WARNINGS diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h index d5ef26939d..da042f4c99 100644 --- a/src/serialization/binary_utils.h +++ b/src/serialization/binary_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 2ac4fd0a51..6dc433a0c8 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,6 @@ #include #include "serialization.h" -#include "debug_archive.h" #include "crypto/chacha8.h" #include "crypto/crypto.h" #include "crypto/hash.h" @@ -71,9 +70,3 @@ BLOB_SERIALIZER(crypto::secret_key); BLOB_SERIALIZER(crypto::key_derivation); BLOB_SERIALIZER(crypto::key_image); BLOB_SERIALIZER(crypto::signature); -VARIANT_TAG(debug_archive, crypto::hash, "hash"); -VARIANT_TAG(debug_archive, crypto::public_key, "public_key"); -VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key"); -VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation"); -VARIANT_TAG(debug_archive, crypto::key_image, "key_image"); -VARIANT_TAG(debug_archive, crypto::signature, "signature"); diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h index 2107f49b63..8cfdc461c0 100644 --- a/src/serialization/json_archive.h +++ b/src/serialization/json_archive.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,10 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -/* json_archive.h - * - * JSON archive */ - #pragma once #include "serialization.h" diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h index 9f129a9ff3..b469e5152e 100644 --- a/src/serialization/json_utils.h +++ b/src/serialization/json_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h index d9c7f1c83e..3a234ce304 100644 --- a/src/serialization/serialization.h +++ b/src/serialization/serialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,10 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -/* serialization.h - * - * Simple templated serialization API */ - #pragma once #include #include diff --git a/src/serialization/string.h b/src/serialization/string.h index 044de8c522..a0424a77d0 100644 --- a/src/serialization/string.h +++ b/src/serialization/string.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/serialization/variant.h b/src/serialization/variant.h index 1471c30b92..244aacac34 100644 --- a/src/serialization/variant.h +++ b/src/serialization/variant.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/serialization/vector.h b/src/serialization/vector.h index a0a03b4c30..1b418607d3 100644 --- a/src/serialization/vector.h +++ b/src/serialization/vector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/simplewallet/password_container.cpp b/src/simplewallet/password_container.cpp index 9f345aaf25..9a574c7649 100644 --- a/src/simplewallet/password_container.cpp +++ b/src/simplewallet/password_container.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/simplewallet/password_container.h b/src/simplewallet/password_container.h index 0157b9c677..7be4ccf407 100644 --- a/src/simplewallet/password_container.h +++ b/src/simplewallet/password_container.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 106c9023f8..61d095ac48 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,44 +15,45 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "simplewallet.h" + +#include #include #include -#include #include + #include -#include #include #include +#include -// epee -#include "include_base_utils.h" -#include "storages/http_abstract_invoke.h" - -#include "common/command_line.h" -#include "common/SignalHandler.h" -#include "common/util.h" +#include "Common/command_line.h" +#include "Common/SignalHandler.h" +#include "Common/PathTools.h" +#include "Common/util.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "p2p/net_node.h" +#include "node_rpc_proxy/NodeRpcProxy.h" #include "rpc/core_rpc_server_commands_defs.h" -#include "simplewallet.h" +#include "rpc/HttpClient.h" + #include "wallet/wallet_rpc_server.h" -#include "version.h" -#include "wallet/WalletHelper.h" #include "wallet/Wallet.h" -#include "wallet/wallet_errors.h" -#include "node_rpc_proxy/NodeRpcProxy.h" #include "wallet/LegacyKeysImporter.h" +#include "wallet/WalletHelper.h" + +#include "version.h" + +#include #if defined(WIN32) #include #endif -using namespace std; -using namespace epee; -using namespace cryptonote; using namespace CryptoNote; -using boost::lexical_cast; +using namespace Logging; +using Common::JsonValue; + namespace po = boost::program_options; #define EXTENDED_LOGS_FILE "wallet_details.log" @@ -65,12 +66,39 @@ const command_line::arg_descriptor arg_generate_new_wallet = { "gen const command_line::arg_descriptor arg_daemon_address = { "daemon-address", "Use daemon instance at :", "" }; const command_line::arg_descriptor arg_daemon_host = { "daemon-host", "Use daemon instance at host instead of localhost", "" }; const command_line::arg_descriptor arg_password = { "password", "Wallet password", "", true }; -const command_line::arg_descriptor arg_daemon_port = { "daemon-port", "Use daemon instance at port instead of 8081", 0 }; -const command_line::arg_descriptor arg_log_level = { "set_log", "", 0, true }; +const command_line::arg_descriptor arg_daemon_port = { "daemon-port", "Use daemon instance at port instead of 8081", 0 }; +const command_line::arg_descriptor arg_log_level = { "set_log", "", INFO, true }; const command_line::arg_descriptor arg_testnet = { "testnet", "Used to deploy test nets. The daemon must be launched with --testnet flag", false }; - const command_line::arg_descriptor< std::vector > arg_command = { "command", "" }; + +bool parseUrlAddress(const std::string& url, std::string& address, uint16_t& port) { + + auto pos = url.find("://"); + size_t addrStart = 0; + + if (pos == std::string::npos) { + pos = 0; + } else { + addrStart = pos + 3; + } + + auto addrEnd = url.find(':', addrStart); + + if (addrEnd != std::string::npos) { + auto portEnd = url.find('/', addrEnd); + port = Common::fromString(url.substr( + addrEnd + 1, portEnd == std::string::npos ? std::string::npos : portEnd - addrEnd - 1)); + } else { + addrEnd = url.find('/'); + port = 80; + } + + address = url.substr(addrStart, addrEnd - addrStart); + return true; +} + + inline std::string interpret_rpc_response(bool ok, const std::string& status) { std::string err; if (ok) { @@ -85,75 +113,6 @@ inline std::string interpret_rpc_response(bool ok, const std::string& status) { return err; } -class message_writer { -public: - message_writer(epee::log_space::console_colors color = epee::log_space::console_color_default, bool bright = false, - std::string&& prefix = std::string(), int log_level = LOG_LEVEL_2) - : m_flush(true) - , m_color(color) - , m_bright(bright) - , m_log_level(log_level) { - m_oss << prefix; - } - - message_writer(message_writer&& rhs) - : m_flush(std::move(rhs.m_flush)) -#if defined(_MSC_VER) - , m_oss(std::move(rhs.m_oss)) -#else - // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 - , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate) -#endif - , m_color(std::move(rhs.m_color)) - , m_log_level(std::move(rhs.m_log_level)) { - rhs.m_flush = false; - } - - template - std::ostream& operator<<(const T& val) { - m_oss << val; - return m_oss; - } - - ~message_writer() { - if (m_flush) { - m_flush = false; - - LOG_PRINT(m_oss.str(), m_log_level) - - if (epee::log_space::console_color_default == m_color) { - std::cout << m_oss.str(); - } else { - epee::log_space::set_console_color(m_color, m_bright); - std::cout << m_oss.str(); - epee::log_space::reset_console_color(); - } - std::cout << std::endl; - } - } - -private: - message_writer(message_writer& rhs); - message_writer& operator=(message_writer& rhs); - message_writer& operator=(message_writer&& rhs); - -private: - bool m_flush; - std::stringstream m_oss; - epee::log_space::console_colors m_color; - bool m_bright; - int m_log_level; -}; - -message_writer success_msg_writer(bool color = false) { - return message_writer(color ? epee::log_space::console_color_green : epee::log_space::console_color_default, false, std::string(), LOG_LEVEL_2); -} - -message_writer fail_msg_writer() { - return message_writer(epee::log_space::console_color_red, true, std::string("Error: "), LOG_LEVEL_0); -} - - template class ArgumentReader { public: @@ -182,17 +141,17 @@ class ArgumentReader { }; struct TransferCommand { - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; size_t fake_outs_count; - vector dsts; + std::vector dsts; std::vector extra; uint64_t fee; - TransferCommand(const cryptonote::Currency& currency) : + TransferCommand(const CryptoNote::Currency& currency) : m_currency(currency), fake_outs_count(0), fee(currency.minimumFee()) { } - bool parseArguments(const std::vector &args) { + bool parseArguments(LoggerRef& logger, const std::vector &args) { ArgumentReader::const_iterator> ar(args.begin(), args.end()); @@ -200,8 +159,8 @@ struct TransferCommand { auto mixin_str = ar.next(); - if (!epee::string_tools::get_xtype_from_string(fake_outs_count, mixin_str)) { - fail_msg_writer() << "mixin_count should be non-negative integer, got " << mixin_str; + if (!Common::fromString(mixin_str, fake_outs_count)) { + logger(ERROR, BRIGHT_RED) << "mixin_count should be non-negative integer, got " << mixin_str; return false; } @@ -215,31 +174,31 @@ struct TransferCommand { if (arg == "-p") { if (!createTxExtraWithPaymentId(value, extra)) { - fail_msg_writer() << "payment ID has invalid format: \"" << value << "\", expected 64-character string"; + logger(ERROR, BRIGHT_RED) << "payment ID has invalid format: \"" << value << "\", expected 64-character string"; return false; } } else if (arg == "-f") { bool ok = m_currency.parseAmount(value, fee); if (!ok) { - fail_msg_writer() << "Fee value is invalid: " << value; + logger(ERROR, BRIGHT_RED) << "Fee value is invalid: " << value; return false; } if (fee < m_currency.minimumFee()) { - fail_msg_writer() << "Fee value is less than minimum: " << m_currency.minimumFee(); + logger(ERROR, BRIGHT_RED) << "Fee value is less than minimum: " << m_currency.minimumFee(); return false; } } } else { Transfer destination; - cryptonote::tx_destination_entry de; + CryptoNote::tx_destination_entry de; if (!m_currency.parseAccountAddressString(arg, de.addr)) { crypto::hash paymentId; - if (cryptonote::parsePaymentId(arg, paymentId)) { - fail_msg_writer() << "Invalid payment ID usage. Please, use -p . See help for details."; + if (CryptoNote::parsePaymentId(arg, paymentId)) { + logger(ERROR, BRIGHT_RED) << "Invalid payment ID usage. Please, use -p . See help for details."; } else { - fail_msg_writer() << "Wrong address: " << arg; + logger(ERROR, BRIGHT_RED) << "Wrong address: " << arg; } return false; @@ -248,7 +207,7 @@ struct TransferCommand { auto value = ar.next(); bool ok = m_currency.parseAmount(value, de.amount); if (!ok || 0 == de.amount) { - fail_msg_writer() << "amount is wrong: " << arg << ' ' << value << + logger(ERROR, BRIGHT_RED) << "amount is wrong: " << arg << ' ' << value << ", expected number from 0 to " << m_currency.formatAmount(std::numeric_limits::max()); return false; } @@ -260,11 +219,11 @@ struct TransferCommand { } if (dsts.empty()) { - fail_msg_writer() << "At least one destination address is required"; + logger(ERROR, BRIGHT_RED) << "At least one destination address is required"; return false; } } catch (const std::exception& e) { - fail_msg_writer() << e.what(); + logger(ERROR, BRIGHT_RED) << e.what(); return false; } @@ -272,19 +231,37 @@ struct TransferCommand { } }; +JsonValue buildLoggerConfiguration(Level level, const std::string& logfile) { + JsonValue loggerConfiguration(JsonValue::OBJECT); + loggerConfiguration.insert("globalLevel", static_cast(level)); + + JsonValue& cfgLoggers = loggerConfiguration.insert("loggers", JsonValue::ARRAY); + + JsonValue& consoleLogger = cfgLoggers.pushBack(JsonValue::OBJECT); + consoleLogger.insert("type", "console"); + consoleLogger.insert("level", static_cast(TRACE)); + consoleLogger.insert("pattern", ""); + + JsonValue& fileLogger = cfgLoggers.pushBack(JsonValue::OBJECT); + fileLogger.insert("type", "file"); + fileLogger.insert("filename", logfile); + fileLogger.insert("level", static_cast(TRACE)); + + return loggerConfiguration; +} + std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, const std::string& password) { WalletHelper::InitWalletResultObserver initObserver; std::future f_initError = initObserver.initResult.get_future(); - wallet.addObserver(&initObserver); + WalletHelper::IWalletRemoveObserverGuard removeGuard(wallet, initObserver); wallet.initAndLoad(walletFile, password); auto initError = f_initError.get(); - wallet.removeObserver(&initObserver); return initError; } -std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { +std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { std::string keys_file, walletFileName; WalletHelper::prepareFileNames(walletFile, keys_file, walletFileName); @@ -301,7 +278,7 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c } if (walletExists) { - LOG_PRINT_L0("Loading wallet..."); + logger(INFO) << "Loading wallet..."; std::ifstream walletFile; walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::in); if (walletFile.fail()) { @@ -314,7 +291,7 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c if (initError) { //bad password, or legacy format if (keysExists) { std::stringstream ss; - cryptonote::importLegacyKeys(keys_file, password, ss); + CryptoNote::importLegacyKeys(keys_file, password, ss); boost::filesystem::rename(keys_file, keys_file + ".back"); boost::filesystem::rename(walletFileName, walletFileName + ".back"); @@ -323,24 +300,16 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c throw std::runtime_error("failed to load wallet: " + initError.message()); } - LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) { - throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); - } - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile, false, false); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + logger(INFO) << "Storing wallet..."; + + try { + CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName); + } catch (std::exception& e) { + logger(ERROR, BRIGHT_RED) << "Failed to store wallet: " << e.what(); throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); } - LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "Stored ok"; return walletFileName; } else { // no keys, wallet error loading throw std::runtime_error("can't load wallet file '" + walletFileName + "', check password"); @@ -350,43 +319,40 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c } } else if (keysExists) { //wallet not exists but keys presented std::stringstream ss; - cryptonote::importLegacyKeys(keys_file, password, ss); + CryptoNote::importLegacyKeys(keys_file, password, ss); boost::filesystem::rename(keys_file, keys_file + ".back"); WalletHelper::InitWalletResultObserver initObserver; std::future f_initError = initObserver.initResult.get_future(); - wallet->addObserver(&initObserver); + + WalletHelper::IWalletRemoveObserverGuard removeGuard(*wallet, initObserver); + wallet->initAndLoad(ss, password); auto initError = f_initError.get(); - wallet->removeObserver(&initObserver); + + removeGuard.removeObserver(); + if (initError) { throw std::runtime_error("failed to load wallet: " + initError.message()); } - LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) { - throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); - } - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile, false, false); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + logger(INFO) << "Storing wallet..."; + + try { + CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName); + } catch(std::exception& e) { + logger(ERROR, BRIGHT_RED) << "Failed to store wallet: " << e.what(); throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); } - LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "Stored ok"; return walletFileName; } else { //no wallet no keys throw std::runtime_error("wallet file '" + walletFileName + "' is not found"); } } + } @@ -394,7 +360,7 @@ std::string simple_wallet::get_commands_str() { std::stringstream ss; ss << "Commands: " << ENDL; - std::string usage = m_cmd_binder.get_usage(); + std::string usage = m_consoleHandler.getUsage(); boost::replace_all(usage, "\n", "\n "); usage.insert(0, " "); ss << usage << ENDL; @@ -407,30 +373,38 @@ bool simple_wallet::help(const std::vector &args/* = std::vector &args) { + m_consoleHandler.requestStop(); + return true; +} + +simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log) : m_daemon_port(0) , m_currency(currency) + , logManager(log) + , m_dispatcher(dispatcher) + , logger(log, "simplewallet") , m_refresh_progress_reporter(*this) - , m_saveResultPromise(nullptr) , m_initResultPromise(nullptr) { - m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); - m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); - //m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); - m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); - m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers"); - m_cmd_binder.set_handler("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers"); - m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); - m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); - m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), + m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); + m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); + //m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); + m_consoleHandler.setHandler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); + m_consoleHandler.setHandler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers"); + m_consoleHandler.setHandler("list_transfers", boost::bind(&simple_wallet::listTransfers, this, _1), "Show all known transfers"); + m_consoleHandler.setHandler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments [ ... ] - Show payments , ... "); + m_consoleHandler.setHandler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); + m_consoleHandler.setHandler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [ ... ] [-p payment_id] [-f fee]" " - Transfer ,... to ,... , respectively. " " is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); - m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); - m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); - m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); - m_cmd_binder.set_handler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start"); - m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help"); + m_consoleHandler.setHandler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); + m_consoleHandler.setHandler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); + m_consoleHandler.setHandler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); + m_consoleHandler.setHandler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start"); + m_consoleHandler.setHandler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help"); + m_consoleHandler.setHandler("exit", boost::bind(&simple_wallet::exit, this, _1), "Close wallet"); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::set_log(const std::vector &args) @@ -441,18 +415,19 @@ bool simple_wallet::set_log(const std::vector &args) return true; } uint16_t l = 0; - if (!epee::string_tools::get_xtype_from_string(l, args[0])) + if (!Common::fromString(args[0], l)) { fail_msg_writer() << "wrong number format, use: set_log "; return true; } - if (LOG_LEVEL_4 < l) + + if (l > Logging::TRACE) { fail_msg_writer() << "wrong number range, use: set_log "; return true; } - log_space::log_singletone::get_set_log_detalisation_level(true, l); + logManager.setMaxLevel(static_cast(l)); return true; } //---------------------------------------------------------------------------------------------------- @@ -488,7 +463,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) do { std::cout << "Wallet file name: "; std::getline(std::cin, userInput); - userInput = string_tools::trim(userInput); + boost::algorithm::trim(userInput); } while (userInput.empty()); if (c == 'g' || c == 'G') { @@ -518,22 +493,22 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_daemon_host = "localhost"; if (!m_daemon_port) m_daemon_port = RPC_DEFAULT_PORT; - if (m_daemon_address.empty()) + + if (!m_daemon_address.empty()) { + if (!parseUrlAddress(m_daemon_address, m_daemon_host, m_daemon_port)) { + fail_msg_writer() << "failed to parse daemon address: " << m_daemon_address; + return false; + } + } else { m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port); + } tools::password_container pwd_container; - if (command_line::has_arg(vm, arg_password)) - { + if (command_line::has_arg(vm, arg_password)) { pwd_container.password(command_line::get_arg(vm, arg_password)); - } - else - { - bool r = pwd_container.read_password(); - if (!r) - { - fail_msg_writer() << "failed to read wallet password"; - return false; - } + } else if (!pwd_container.read_password()) { + fail_msg_writer() << "failed to read wallet password"; + return false; } this->m_node.reset(new NodeRpcProxy(m_daemon_host, m_daemon_port)); @@ -551,14 +526,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (!m_generate_new.empty()) { bool r = new_wallet(walletFileName, pwd_container.password()); - CHECK_AND_ASSERT_MES(r, false, "account creation failed"); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "account creation failed"; return false; } } else { m_wallet.reset(new Wallet(m_currency, *m_node)); try { - m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(m_wallet, m_wallet_file_arg, pwd_container.password()); + m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(logger, m_wallet, m_wallet_file_arg, pwd_container.password()); } catch (const std::exception& e) { fail_msg_writer() << "failed to load wallet: " << e.what(); return false; @@ -567,7 +542,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_wallet->addObserver(this); m_node->addObserver(this); - message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << m_wallet->getAddress(); + logger(INFO, BRIGHT_WHITE) << "Opened wallet: " << m_wallet->getAddress(); success_msg_writer() << "**********************************************************************\n" << @@ -583,9 +558,7 @@ bool simple_wallet::deinit() if (!m_wallet.get()) return true; - bool r = close_wallet(); - m_wallet->shutdown(); - return r; + return close_wallet(); } //---------------------------------------------------------------------------------------------------- void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm) @@ -597,7 +570,7 @@ void simple_wallet::handle_command_line(const boost::program_options::variables_ m_daemon_port = command_line::get_arg(vm, arg_daemon_port); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password) +bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password) { m_wallet_file = wallet_file; @@ -615,26 +588,20 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas fail_msg_writer() << "failed to generate new wallet: " << initError.message(); return false; } - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << "failed to save new wallet: " << saveError.message(); - return false; + + try { + CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); + } catch (std::exception& e) { + fail_msg_writer() << "failed to save new wallet: " << e.what(); + throw; } WalletAccountKeys keys; m_wallet->getAccountKeys(keys); - message_writer(epee::log_space::console_color_white, true) << + logger(INFO, BRIGHT_WHITE) << "Generated new wallet: " << m_wallet->getAddress() << std::endl << - "view key: " << epee::string_tools::pod_to_hex(keys.viewSecretKey); + "view key: " << Common::podToHex(keys.viewSecretKey); } catch (const std::exception& e) { @@ -657,46 +624,26 @@ bool simple_wallet::close_wallet() { try { - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << saveError.message(); - return false; - } + CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); } catch (const std::exception& e) { fail_msg_writer() << e.what(); return false; } + m_wallet->removeObserver(this); + m_wallet->shutdown(); + return true; } + //---------------------------------------------------------------------------------------------------- bool simple_wallet::save(const std::vector &args) { try { - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << saveError.message(); - return false; - } + CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); success_msg_writer() << "Wallet data saved"; } catch (const std::exception& e) @@ -727,7 +674,7 @@ bool simple_wallet::start_mining(const std::vector& args) else if (1 == args.size()) { uint16_t num = 1; - ok = string_tools::get_xtype_from_string(num, args[0]); + ok = Common::fromString(args[0], num); ok = ok && (1 <= num && num <= max_mining_threads_count); req.threads_count = num; } @@ -743,29 +690,44 @@ bool simple_wallet::start_mining(const std::vector& args) return true; } + COMMAND_RPC_START_MINING::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client); - std::string err = interpret_rpc_response(r, res.status); - if (err.empty()) - success_msg_writer() << "Mining started in daemon"; - else - fail_msg_writer() << "mining has NOT been started: " << err; + + try { + HttpClient httpClient(m_dispatcher, m_daemon_host, m_daemon_port); + + invokeJsonCommand(httpClient, "/start_mining", req, res); + + std::string err = interpret_rpc_response(true, res.status); + if (err.empty()) + success_msg_writer() << "Mining started in daemon"; + else + fail_msg_writer() << "mining has NOT been started: " << err; + + } catch (const std::exception& e) { + fail_msg_writer() << "Failed to invoke rpc method: " << e.what(); + } + return true; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::stop_mining(const std::vector& args) { - /* if (!try_connect_to_daemon()) - return true;*/ - COMMAND_RPC_STOP_MINING::request req; COMMAND_RPC_STOP_MINING::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client); - std::string err = interpret_rpc_response(r, res.status); - if (err.empty()) - success_msg_writer() << "Mining stopped in daemon"; - else - fail_msg_writer() << "mining has NOT been stopped: " << err; + + try { + HttpClient httpClient(m_dispatcher, m_daemon_host, m_daemon_port); + invokeJsonCommand(httpClient, "/stop_mining", req, res); + std::string err = interpret_rpc_response(true, res.status); + if (err.empty()) + success_msg_writer() << "Mining stopped in daemon"; + else + fail_msg_writer() << "mining has NOT been stopped: " << err; + } catch (const std::exception& e) { + fail_msg_writer() << "Failed to invoke rpc method: " << e.what(); + } + return true; } //---------------------------------------------------------------------------------------------------- @@ -775,12 +737,6 @@ void simple_wallet::initCompleted(std::error_code result) { } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::saveCompleted(std::error_code result) { - if (m_saveResultPromise.get() != nullptr) { - m_saveResultPromise->set_value(result); - } -} -//---------------------------------------------------------------------------------------------------- void simple_wallet::localBlockchainUpdated(uint64_t height) { m_refresh_progress_reporter.update(height, false); @@ -792,16 +748,15 @@ void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transac m_wallet->getTransaction(transactionId, txInfo); if (txInfo.totalAmount >= 0) { - message_writer(epee::log_space::console_color_green, false) << - "Height " << txInfo.blockHeight << - ", transaction " << epee::string_tools::pod_to_hex(txInfo.hash) << - ", received " << m_currency.formatAmount(static_cast(txInfo.totalAmount)); + logger(INFO, GREEN) << + "Height " << txInfo.blockHeight << ", transaction " << Common::podToHex(txInfo.hash) << + ", received " << m_currency.formatAmount(txInfo.totalAmount); } else { - message_writer(epee::log_space::console_color_magenta, false) << - "Height " << txInfo.blockHeight << - ", transaction " << epee::string_tools::pod_to_hex(txInfo.hash) << + logger(INFO, MAGENTA) << + "Height " << txInfo.blockHeight << ", transaction " << Common::podToHex(txInfo.hash) << ", spent " << m_currency.formatAmount(static_cast(-txInfo.totalAmount)); } + m_refresh_progress_reporter.update(txInfo.blockHeight, true); } //---------------------------------------------------------------------------------------------------- @@ -809,6 +764,7 @@ bool simple_wallet::show_balance(const std::vector& args/* = std::v { success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) << ", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance()); + return true; } //---------------------------------------------------------------------------------------------------- @@ -821,9 +777,9 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.totalAmount < 0) continue; hasTransfers = true; - message_writer() << " amount \t tx id"; - message_writer( epee::log_space::console_color_green, false) << // spent magenta - std::setw(21) << m_currency.formatAmount(txInfo.totalAmount) << '\t' << epee::string_tools::pod_to_hex(txInfo.hash); + logger(INFO) << " amount \t tx id"; + logger(INFO, GREEN) << // spent - magenta + std::setw(21) << m_currency.formatAmount(txInfo.totalAmount) << '\t' << Common::podToHex(txInfo.hash); } if (!hasTransfers) success_msg_writer() << "No incoming transfers"; @@ -845,9 +801,9 @@ bool simple_wallet::listTransfers(const std::vector& args) { std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); crypto::hash paymentId; - paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? epee::string_tools::pod_to_hex(paymentId) : ""); + paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? Common::podToHex(paymentId) : ""); - std::string address = ""; + std::string address = "-"; if (txInfo.totalAmount < 0) { if (txInfo.transferCount > 0) { @@ -857,13 +813,13 @@ bool simple_wallet::listTransfers(const std::vector& args) { } } - message_writer(txInfo.totalAmount < 0 ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) + logger(INFO, txInfo.totalAmount < 0 ? MAGENTA : GREEN) << txInfo.timestamp << ", " << (txInfo.totalAmount < 0 ? "OUTPUT" : "INPUT") - << ", " << epee::string_tools::pod_to_hex(txInfo.hash) - << ", " << (txInfo.totalAmount < 0 ? "-" : "") << m_currency.formatAmount(abs(txInfo.totalAmount)) + << ", " << Common::podToHex(txInfo.hash) + << ", " << (txInfo.totalAmount < 0 ? "-" : "") << m_currency.formatAmount(std::abs(txInfo.totalAmount)) << ", " << m_currency.formatAmount(txInfo.fee) - << ", " << paymentIdStr + << ", " << (paymentIdStr.empty() ? std::string("-") : paymentIdStr) << ", " << address << ", " << txInfo.blockHeight << ", " << txInfo.unlockTime; @@ -881,7 +837,7 @@ bool simple_wallet::show_payments(const std::vector &args) return true; } - message_writer() << " payment \t" << + logger(INFO) << " payment \t" << " transaction \t" << " height\t amount "; @@ -889,9 +845,8 @@ bool simple_wallet::show_payments(const std::vector &args) for (const std::string& arg: args) { crypto::hash expectedPaymentId; - if (cryptonote::parsePaymentId(arg, expectedPaymentId)) + if (CryptoNote::parsePaymentId(arg, expectedPaymentId)) { - size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { TransactionInfo txInfo; @@ -902,24 +857,21 @@ bool simple_wallet::show_payments(const std::vector &args) std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); crypto::hash paymentId; - if (cryptonote::getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { + if (CryptoNote::getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { payments_found = true; success_msg_writer(true) << paymentId << "\t\t" << - epee::string_tools::pod_to_hex(txInfo.hash) << + Common::podToHex(txInfo.hash) << std::setw(8) << txInfo.blockHeight << '\t' << std::setw(21) << m_currency.formatAmount(txInfo.totalAmount);// << '\t' << } } - if (!payments_found) - { + if (!payments_found) { success_msg_writer() << "No payments with id " << expectedPaymentId; continue; } - } - else - { + } else { fail_msg_writer() << "payment ID has invalid format: \"" << arg << "\", expected 64-character string"; } } @@ -945,24 +897,24 @@ bool simple_wallet::transfer(const std::vector &args) { TransferCommand cmd(m_currency); - if (!cmd.parseArguments(args)) + if (!cmd.parseArguments(logger, args)) return false; - cryptonote::WalletHelper::SendCompleteResultObserver sent; - std::promise txId; - sent.expectedTxID = txId.get_future(); - std::future f_sendError = sent.sendResult.get_future(); + CryptoNote::WalletHelper::SendCompleteResultObserver sent; + std::string extraString; std::copy(cmd.extra.begin(), cmd.extra.end(), std::back_inserter(extraString)); - m_wallet->addObserver(&sent); + WalletHelper::IWalletRemoveObserverGuard removeGuard(*m_wallet, sent); + CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0); if (tx == INVALID_TRANSACTION_ID) { fail_msg_writer() << "Can't send money"; return true; } - txId.set_value(tx); - std::error_code sendError = f_sendError.get(); - m_wallet->removeObserver(&sent); + + std::error_code sendError = sent.wait(tx); + removeGuard.removeObserver(); + if (sendError) { fail_msg_writer() << sendError.message(); return true; @@ -970,24 +922,10 @@ bool simple_wallet::transfer(const std::vector &args) CryptoNote::TransactionInfo txInfo; m_wallet->getTransaction(tx, txInfo); - success_msg_writer(true) << "Money successfully sent, transaction " << epee::string_tools::pod_to_hex(txInfo.hash); + success_msg_writer(true) << "Money successfully sent, transaction " << Common::podToHex(txInfo.hash); try { - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) { - fail_msg_writer() << "cant open " << m_wallet_file << " for save"; - return true; - } - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << saveError.message(); - return true; - } + CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); } catch (const std::exception& e) { fail_msg_writer() << e.what(); return true; @@ -1009,28 +947,25 @@ bool simple_wallet::transfer(const std::vector &args) return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::run() -{ +bool simple_wallet::run() { std::string addr_start = m_wallet->getAddress().substr(0, 6); - return m_cmd_binder.run_handling("[wallet " + addr_start + "]: ", ""); + m_consoleHandler.start(false, "[wallet " + addr_start + "]: ", Common::Console::Color::BrightYellow); + return true; } //---------------------------------------------------------------------------------------------------- -void simple_wallet::stop() -{ - m_cmd_binder.stop_handling(); +void simple_wallet::stop() { + m_consoleHandler.requestStop(); } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::print_address(const std::vector &args/* = std::vector()*/) -{ +bool simple_wallet::print_address(const std::vector &args/* = std::vector()*/) { success_msg_writer() << m_wallet->getAddress(); return true; } //---------------------------------------------------------------------------------------------------- -bool simple_wallet::process_command(const std::vector &args) -{ - return m_cmd_binder.process_command_vec(args); +bool simple_wallet::process_command(const std::vector &args) { + return m_consoleHandler.runCommand(args); } -//---------------------------------------------------------------------------------------------------- + int main(int argc, char* argv[]) { @@ -1038,8 +973,6 @@ int main(int argc, char* argv[]) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif - string_tools::set_module_name_and_folder(argv[0]); - po::options_description desc_general("General options"); command_line::add_arg(desc_general, command_line::arg_help); command_line::add_arg(desc_general, command_line::arg_version); @@ -1061,23 +994,26 @@ int main(int argc, char* argv[]) po::options_description desc_all; desc_all.add(desc_general).add(desc_params); - cryptonote::Currency tmp_currency = cryptonote::CurrencyBuilder().currency(); - cryptonote::simple_wallet tmp_wallet(tmp_currency); + + Logging::LoggerManager logManager; + Logging::LoggerRef logger(logManager, "simplewallet"); + System::Dispatcher dispatcher; + po::variables_map vm; - bool r = command_line::handle_error_helper(desc_all, [&]() - { + + bool r = command_line::handle_error_helper(desc_all, [&]() { po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); - if (command_line::get_arg(vm, command_line::arg_help)) - { - success_msg_writer() << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; - success_msg_writer() << "Usage: simplewallet [--wallet-file=|--generate-new-wallet=] [--daemon-address=:] []"; - success_msg_writer() << desc_all << '\n' << tmp_wallet.get_commands_str(); + if (command_line::get_arg(vm, command_line::arg_help)) { + CryptoNote::Currency tmp_currency = CryptoNote::CurrencyBuilder(logManager).currency(); + CryptoNote::simple_wallet tmp_wallet(dispatcher, tmp_currency, logManager); + + std::cout << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG << std::endl; + std::cout << "Usage: simplewallet [--wallet-file=|--generate-new-wallet=] [--daemon-address=:] []"; + std::cout << desc_all << '\n' << tmp_wallet.get_commands_str(); return false; - } - else if (command_line::get_arg(vm, command_line::arg_version)) - { - success_msg_writer() << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; + } else if (command_line::get_arg(vm, command_line::arg_version)) { + std::cout << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; return false; } @@ -1086,45 +1022,39 @@ int main(int argc, char* argv[]) po::notify(vm); return true; }); + if (!r) return 1; //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - //log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str(), LOG_LEVEL_4); - - message_writer(epee::log_space::console_color_white, true) << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; + Level logLevel = DEBUGGING; - if (command_line::has_arg(vm, arg_log_level)) - { - LOG_PRINT_L0("Setting log level = " << command_line::get_arg(vm, arg_log_level)); - log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); + if (command_line::has_arg(vm, arg_log_level)) { + logLevel = static_cast(command_line::get_arg(vm, arg_log_level)); } - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.testnet(command_line::get_arg(vm, arg_testnet)); - cryptonote::Currency currency = currencyBuilder.currency(); + logManager.configure(buildLoggerConfiguration(logLevel, Common::ReplaceExtenstion(argv[0], ".log"))); - if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) - { - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); + logger(INFO, BRIGHT_WHITE) << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG; + + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logManager). + testnet(command_line::get_arg(vm, arg_testnet)).currency(); + + if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { //runs wallet with rpc interface if (!command_line::has_arg(vm, arg_wallet_file)) { - fail_msg_writer() << "Wallet file not set."; + logger(ERROR, BRIGHT_RED) << "Wallet file not set."; return 1; } if (!command_line::has_arg(vm, arg_daemon_address)) { - fail_msg_writer() << "Daemon address not set."; + logger(ERROR, BRIGHT_RED) << "Daemon address not set."; return 1; } if (!command_line::has_arg(vm, arg_password)) { - fail_msg_writer() << "Wallet password not set."; + logger(ERROR, BRIGHT_RED) << "Wallet password not set."; return 1; } @@ -1137,81 +1067,76 @@ int main(int argc, char* argv[]) daemon_host = "localhost"; if (!daemon_port) daemon_port = RPC_DEFAULT_PORT; - if (daemon_address.empty()) - daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); + if (!daemon_address.empty()) { + uint16_t port = 0; + if (!parseUrlAddress(daemon_address, daemon_host, port)) { + logger(ERROR, BRIGHT_RED) << "failed to parse daemon address: " << daemon_address; + return 1; + } - std::unique_ptr node; + daemon_port = port; + } - node.reset(new NodeRpcProxy(daemon_host, daemon_port)); + std::unique_ptr node(new NodeRpcProxy(daemon_host, daemon_port)); std::promise errorPromise; std::future error = errorPromise.get_future(); auto callback = [&errorPromise](std::error_code e) {errorPromise.set_value(e); }; node->init(callback); if (error.get()) { - fail_msg_writer() << ("failed to init NodeRPCProxy"); + logger(ERROR, BRIGHT_RED) << ("failed to init NodeRPCProxy"); return 1; } - std::unique_ptr wallet; + std::unique_ptr wallet(new Wallet(currency, *node.get())); - wallet.reset(new Wallet(currency, *node.get())); std::string walletFileName; - try - { - walletFileName = ::tryToOpenWalletOrLoadKeysOrThrow(wallet, wallet_file, wallet_password); - LOG_PRINT_L1("available balance: " << currency.formatAmount(wallet->actualBalance()) << - ", locked amount: " << currency.formatAmount(wallet->pendingBalance())); - LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0); - } - catch (const std::exception& e) - { - fail_msg_writer() << "Wallet initialize failed: " << e.what(); + try { + walletFileName = ::tryToOpenWalletOrLoadKeysOrThrow(logger, wallet, wallet_file, wallet_password); + + logger(INFO) << "available balance: " << currency.formatAmount(wallet->actualBalance()) << + ", locked amount: " << currency.formatAmount(wallet->pendingBalance()); + + logger(INFO, BRIGHT_GREEN) << "Loaded ok"; + } catch (const std::exception& e) { + logger(ERROR, BRIGHT_RED) << "Wallet initialize failed: " << e.what(); return 1; } - tools::wallet_rpc_server wrpc(*wallet, *node, currency, walletFileName); - wrpc.init(vm); - CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server"); + tools::wallet_rpc_server wrpc(dispatcher, logManager, *wallet, *node, currency, walletFileName); + + if (!wrpc.init(vm)) { + logger(ERROR, BRIGHT_RED) << "Failed to initialize wallet rpc server"; + return 1; + } tools::SignalHandler::install([&wrpc, &wallet] { wrpc.send_stop_signal(); }); - LOG_PRINT_L0("Starting wallet rpc server"); + + logger(INFO) << "Starting wallet rpc server"; wrpc.run(); - LOG_PRINT_L0("Stopped wallet rpc server"); - try - { - LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); - return 1; - } - LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); + logger(INFO) << "Stopped wallet rpc server"; + + try { + CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName); + + logger(INFO, BRIGHT_GREEN) << "Stored ok"; } catch (const std::exception& e) { - fail_msg_writer() << "Failed to store wallet: " << e.what(); + logger(ERROR, BRIGHT_RED) << "Failed to store wallet: " << e.what(); return 1; } - } - else - { + } else { //runs wallet with console interface - cryptonote::simple_wallet wal(currency); - r = wal.init(vm); - CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet"); + CryptoNote::simple_wallet wal(dispatcher, currency, logManager); + + if (!wal.init(vm)) { + logger(ERROR, BRIGHT_RED) << "Failed to initialize wallet"; + return 1; + } std::vector command = command_line::get_arg(vm, arg_command); if (!command.empty()) @@ -1220,11 +1145,14 @@ int main(int argc, char* argv[]) tools::SignalHandler::install([&wal] { wal.stop(); }); + wal.run(); if (!wal.deinit()) { - fail_msg_writer() << "Failed to close wallet"; - } + logger(ERROR, BRIGHT_RED) << "Failed to close wallet"; + } else { + logger(INFO) << "Wallet closed"; + } } return 1; //CATCH_ENTRY_L0("main", 1); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index ff61848014..a2d6ba9d1f 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,16 +22,22 @@ #include -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "console_handler.h" -#include "password_container.h" #include "IWallet.h" #include "INode.h" +#include "password_container.h" + +#include "Common/ConsoleHandler.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/Currency.h" #include "wallet/WalletHelper.h" -#include "net/http_client.h" -namespace cryptonote +#include +#include + +#include +#include + +namespace CryptoNote { /************************************************************************/ /* */ @@ -39,21 +45,30 @@ namespace cryptonote class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletObserver { public: - typedef std::vector command_type; + simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log); - simple_wallet(const cryptonote::Currency& currency); bool init(const boost::program_options::variables_map& vm); bool deinit(); bool run(); void stop(); - //wallet *create_wallet(); bool process_command(const std::vector &args); std::string get_commands_str(); - const cryptonote::Currency& currency() const { return m_currency; } + const CryptoNote::Currency& currency() const { return m_currency; } private: + + Logging::LoggerMessage success_msg_writer(bool color = false) { + return logger(Logging::INFO, color ? Logging::GREEN : Logging::DEFAULT); + } + + Logging::LoggerMessage fail_msg_writer() { + auto msg = logger(Logging::ERROR, Logging::BRIGHT_RED); + msg << "Error: "; + return msg; + } + void handle_command_line(const boost::program_options::variables_map& vm); bool run_console_handler(); @@ -63,9 +78,9 @@ namespace cryptonote bool close_wallet(); bool help(const std::vector &args = std::vector()); + bool exit(const std::vector &args); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); - //bool refresh(const std::vector &args = std::vector()); bool show_balance(const std::vector &args = std::vector()); bool show_incoming_transfers(const std::vector &args); bool show_payments(const std::vector &args); @@ -77,19 +92,10 @@ namespace cryptonote bool reset(const std::vector &args); bool set_log(const std::vector &args); - //uint64_t get_daemon_blockchain_height(std::string& err); - //bool try_connect_to_daemon(); bool ask_wallet_create_if_needed(); - ////----------------- i_wallet2_callback --------------------- - //virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index); - //virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx); - //virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx); - ////---------------------------------------------------------- - //---------------- IWalletObserver ------------------------- virtual void initCompleted(std::error_code result) override; - virtual void saveCompleted(std::error_code result) override; virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override; //---------------------------------------------------------- @@ -102,7 +108,7 @@ namespace cryptonote class refresh_progress_reporter_t { public: - refresh_progress_reporter_t(cryptonote::simple_wallet& simple_wallet) + refresh_progress_reporter_t(CryptoNote::simple_wallet& simple_wallet) : m_simple_wallet(simple_wallet) , m_blockchain_height(0) , m_blockchain_height_update_time() @@ -121,7 +127,7 @@ namespace cryptonote if (std::chrono::milliseconds(1) < current_time - m_print_time || force) { - LOG_PRINT_L0("Height " << height << " of " << m_blockchain_height << '\r'); + std::cout << "Height " << height << " of " << m_blockchain_height << '\r'; m_print_time = current_time; } } @@ -138,12 +144,12 @@ namespace cryptonote } else { - LOG_ERROR("Failed to get current blockchain height: " << err); + std::cerr << "Failed to get current blockchain height: " << err; } } private: - cryptonote::simple_wallet& m_simple_wallet; + CryptoNote::simple_wallet& m_simple_wallet; uint64_t m_blockchain_height; std::chrono::system_clock::time_point m_blockchain_height_update_time; std::chrono::system_clock::time_point m_print_time; @@ -156,20 +162,19 @@ namespace cryptonote std::string m_daemon_address; std::string m_daemon_host; - int m_daemon_port; + uint16_t m_daemon_port; std::string m_wallet_file; - std::unique_ptr> m_initResultPromise; - std::unique_ptr> m_saveResultPromise; - - epee::console_handlers_binder m_cmd_binder; - - const cryptonote::Currency& m_currency; + Common::ConsoleHandler m_consoleHandler; + const CryptoNote::Currency& m_currency; + Logging::LoggerManager& logManager; + System::Dispatcher& m_dispatcher; + Logging::LoggerRef logger; std::unique_ptr m_node; std::unique_ptr m_wallet; - epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; + std::unique_ptr> m_initResultPromise; }; } diff --git a/src/transfers/BlockchainSynchronizer.cpp b/src/transfers/BlockchainSynchronizer.cpp index 19c7c524a2..cadf8a46f0 100644 --- a/src/transfers/BlockchainSynchronizer.cpp +++ b/src/transfers/BlockchainSynchronizer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -51,7 +51,7 @@ void BlockchainSynchronizer::addConsumer(IBlockchainConsumer* consumer) { if (!(checkIfStopped() && checkIfShouldStop())) { throw std::runtime_error("Can't add consumer, because BlockchainSynchronizer isn't stopped"); } - + m_consumers.insert(std::make_pair(consumer, std::make_shared(m_genesisBlockHash))); shouldSyncConsumersPool = true; } @@ -100,8 +100,8 @@ void BlockchainSynchronizer::load(std::istream& in) { bool BlockchainSynchronizer::setFutureState(State s) { return setFutureStateIf(s, std::bind( - [](State futureState, State s) -> bool { - return s > futureState; + [](State futureState, State s) -> bool { + return s > futureState; }, std::ref(m_futureState), s)); } @@ -177,7 +177,7 @@ void BlockchainSynchronizer::start() { if (!setFutureStateIf(State::blockchainSync, std::bind( [](State currentState, State futureState) -> bool { return currentState == State::stopped && futureState == State::stopped; - }, std::ref(m_currentState), std::ref(m_futureState))) ) { + }, std::ref(m_currentState), std::ref(m_futureState)))) { throw std::runtime_error("BlockchainSynchronizer already started"); } @@ -188,7 +188,7 @@ void BlockchainSynchronizer::start() { void BlockchainSynchronizer::stop() { setFutureState(State::stopped); - + // wait for previous processing to end if (workingThread.get() != nullptr && workingThread->joinable()) { workingThread->join(); @@ -232,15 +232,15 @@ BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getIntersectedPoo GetPoolRequest request; { std::unique_lock lk(m_consumersMutex); - auto it = m_consumers.begin(); + auto it = m_consumers.begin(); it->first->getKnownPoolTxIds(request.knownTxIds); ++it; - + for (; it != m_consumers.end(); ++it) { //iterate over consumers std::vector consumerKnownIds; it->first->getKnownPoolTxIds(consumerKnownIds); - for (auto itReq = request.knownTxIds.begin(); itReq != request.knownTxIds.end(); ) { //iterate over intersection + for (auto itReq = request.knownTxIds.begin(); itReq != request.knownTxIds.end();) { //iterate over intersection if (std::count(consumerKnownIds.begin(), consumerKnownIds.end(), *itReq) == 0) { //consumer doesn't contain id from intersection, so delete this id from intersection itReq = request.knownTxIds.erase(itReq); } else { @@ -275,7 +275,7 @@ BlockchainSynchronizer::GetBlocksRequest BlockchainSynchronizer::getCommonHistor syncStart.height = std::min(syncStart.height, consumerStart.height); } - request.knownBlocks = shortest->second->getShortHistory(); + request.knownBlocks = shortest->second->getShortHistory(m_node.getLastLocalBlockHeight()); request.syncStart = syncStart; return request; } @@ -295,19 +295,19 @@ void BlockchainSynchronizer::startBlockchainSync() { std::error_code ec = asyncOperationWaitFuture.get(); if (ec) { + setFutureStateIf(State::idle, std::bind( + [](State futureState) -> bool { + return futureState != State::stopped; + }, std::ref(m_futureState))); m_observerManager.notify( &IBlockchainSynchronizerObserver::synchronizationCompleted, ec); - setFutureStateIf(State::idle, std::bind( - [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); } else { processBlocks(response); } } } catch (std::exception& e) { - std::cout << e.what()<< std::endl; + std::cout << e.what() << std::endl; setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { return futureState != State::stopped; @@ -338,8 +338,8 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { completeBlock.blockHash = block.blockHash; interval.blocks.push_back(completeBlock.blockHash); if (!block.block.empty()) { - cryptonote::Block parsedBlock; - if (!cryptonote::parse_and_validate_block_from_blob(block.block, parsedBlock)) { + Block parsedBlock; + if (!parse_and_validate_block_from_blob(block.block, parsedBlock)) { setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { return futureState != State::stopped; @@ -347,7 +347,7 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { m_observerManager.notify( &IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument)); - return; + return; } completeBlock.block = std::move(parsedBlock); @@ -392,24 +392,24 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { break; case UpdateConsumersResult::nothingChanged: - if (m_node.getLastKnownBlockHeight() > newHeight) { + if (m_node.getLastKnownBlockHeight() != m_node.getLastLocalBlockHeight()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } else { break; } case UpdateConsumersResult::addedNewBlocks: + setFutureState(State::blockchainSync); m_observerManager.notify( &IBlockchainSynchronizerObserver::synchronizationProgressUpdated, newHeight, - m_node.getLastKnownBlockHeight()); - setFutureState(State::blockchainSync); + std::max(m_node.getKnownBlockCount(), m_node.getLocalBlockCount())); break; } if (!blocks.empty()) { lastBlockId = blocks.back().blockHash; } - } + } if (checkIfShouldStop()) { //Sic! m_observerManager.notify( @@ -444,8 +444,8 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons smthChanged = true; } else { return UpdateConsumersResult::errorOccured; - } - } + } + } } return smthChanged ? UpdateConsumersResult::addedNewBlocks : UpdateConsumersResult::nothingChanged; @@ -466,13 +466,13 @@ void BlockchainSynchronizer::startPoolSync() { std::error_code ec = asyncOperationWaitFuture.get(); if (ec) { - m_observerManager.notify( - &IBlockchainSynchronizerObserver::synchronizationCompleted, - ec); setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { return futureState != State::stopped; }, std::ref(m_futureState))); + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + ec); } else { //get union ok if (!unionResponse.isLastKnownBlockActual) { //bc outdated setFutureState(State::blockchainSync); @@ -496,20 +496,20 @@ void BlockchainSynchronizer::startPoolSync() { std::error_code ec2 = asyncOperationWaitFuture.get(); if (ec2) { - m_observerManager.notify( - &IBlockchainSynchronizerObserver::synchronizationCompleted, - ec2); setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { return futureState != State::stopped; }, std::ref(m_futureState))); + m_observerManager.notify( + &IBlockchainSynchronizerObserver::synchronizationCompleted, + ec2); } else { //get intersection ok if (!intersectionResponse.isLastKnownBlockActual) { //bc outdated setFutureState(State::blockchainSync); } else { intersectionResponse.deletedTxIds.assign(unionResponse.deletedTxIds.begin(), unionResponse.deletedTxIds.end()); std::error_code ec3 = processPoolTxs(intersectionResponse); - + //notify about error, or success m_observerManager.notify( &IBlockchainSynchronizerObserver::synchronizationCompleted, diff --git a/src/transfers/BlockchainSynchronizer.h b/src/transfers/BlockchainSynchronizer.h index ab8af4f57f..2ec1413cb1 100644 --- a/src/transfers/BlockchainSynchronizer.h +++ b/src/transfers/BlockchainSynchronizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -72,7 +72,7 @@ class BlockchainSynchronizer : struct GetPoolResponse { bool isLastKnownBlockActual; - std::vector newTxs; + std::vector newTxs; std::vector deletedTxIds; }; diff --git a/src/transfers/CommonTypes.h b/src/transfers/CommonTypes.h index e96aef14ba..f349c8ead6 100644 --- a/src/transfers/CommonTypes.h +++ b/src/transfers/CommonTypes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -35,7 +35,7 @@ struct BlockchainInterval { struct CompleteBlock { crypto::hash blockHash; - boost::optional block; + boost::optional block; // first transaction is always coinbase std::list> transactions; }; diff --git a/src/transfers/IBlockchainSynchronizer.h b/src/transfers/IBlockchainSynchronizer.h index 92ba1f3297..6842ed5422 100644 --- a/src/transfers/IBlockchainSynchronizer.h +++ b/src/transfers/IBlockchainSynchronizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -44,7 +44,7 @@ class IBlockchainConsumer { virtual void getKnownPoolTxIds(std::vector& ids) = 0; virtual void onBlockchainDetach(uint64_t height) = 0; virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) = 0; - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) = 0; + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) = 0; }; diff --git a/src/transfers/IObservableImpl.h b/src/transfers/IObservableImpl.h index 13f048b057..64868b1b69 100644 --- a/src/transfers/IObservableImpl.h +++ b/src/transfers/IObservableImpl.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #pragma once -#include "common/ObserverManager.h" +#include "Common/ObserverManager.h" namespace CryptoNote { diff --git a/src/transfers/SerializationHelpers.h b/src/transfers/SerializationHelpers.h index c44b1efbca..1f0a0d7151 100644 --- a/src/transfers/SerializationHelpers.h +++ b/src/transfers/SerializationHelpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,7 +19,7 @@ #include "serialization/ISerializer.h" -namespace cryptonote { +namespace CryptoNote { template void writeSequence(Iterator begin, Iterator end, const std::string& name, ISerializer& s) { diff --git a/src/transfers/SynchronizationState.cpp b/src/transfers/SynchronizationState.cpp index 0f48286d8b..f537cf2764 100644 --- a/src/transfers/SynchronizationState.cpp +++ b/src/transfers/SynchronizationState.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,12 +23,12 @@ namespace CryptoNote { -SynchronizationState::ShortHistory SynchronizationState::getShortHistory() const { +SynchronizationState::ShortHistory SynchronizationState::getShortHistory(size_t localHeight) const { ShortHistory history; size_t i = 0; size_t current_multiplier = 1; - size_t sz = m_blockchain.size(); + size_t sz = std::min(m_blockchain.size(), localHeight + 1); if (!sz) return history; @@ -101,16 +101,16 @@ uint64_t SynchronizationState::getHeight() const { } void SynchronizationState::save(std::ostream& os) { - cryptonote::BinaryOutputStreamSerializer s(os); + CryptoNote::BinaryOutputStreamSerializer s(os); serialize(s, "state"); } void SynchronizationState::load(std::istream& in) { - cryptonote::BinaryInputStreamSerializer s(in); + CryptoNote::BinaryInputStreamSerializer s(in); serialize(s, "state"); } -cryptonote::ISerializer& SynchronizationState::serialize(cryptonote::ISerializer& s, const std::string& name) { +CryptoNote::ISerializer& SynchronizationState::serialize(CryptoNote::ISerializer& s, const std::string& name) { s.beginObject(name); s(m_blockchain, "blockchain"); s.endObject(); diff --git a/src/transfers/SynchronizationState.h b/src/transfers/SynchronizationState.h index 9cfc8ad4ec..9e55345afb 100644 --- a/src/transfers/SynchronizationState.h +++ b/src/transfers/SynchronizationState.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -41,7 +41,7 @@ class SynchronizationState : public IStreamSerializable { m_blockchain.push_back(genesisBlockHash); } - ShortHistory getShortHistory() const; + ShortHistory getShortHistory(size_t localHeight) const; CheckResult checkInterval(const BlockchainInterval& interval) const; void detach(uint64_t height); @@ -53,7 +53,7 @@ class SynchronizationState : public IStreamSerializable { virtual void load(std::istream& in) override; // serialization - cryptonote::ISerializer& serialize(cryptonote::ISerializer& s, const std::string& name); + CryptoNote::ISerializer& serialize(CryptoNote::ISerializer& s, const std::string& name); private: diff --git a/src/transfers/TransfersConsumer.cpp b/src/transfers/TransfersConsumer.cpp index bc12d64af4..ab2b08f71b 100644 --- a/src/transfers/TransfersConsumer.cpp +++ b/src/transfers/TransfersConsumer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,7 +18,7 @@ #include "TransfersConsumer.h" #include "CommonTypes.h" -#include "common/BlockingQueue.h" +#include "Common/BlockingQueue.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/TransactionApi.h" @@ -95,7 +95,7 @@ void findMyOutputs( namespace CryptoNote { -TransfersConsumer::TransfersConsumer(const cryptonote::Currency& currency, INode& node, const SecretKey& viewSecret) : +TransfersConsumer::TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const SecretKey& viewSecret) : m_node(node), m_viewSecret(viewSecret), m_currency(currency) { updateSyncStart(); } @@ -201,7 +201,8 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH for (const auto& tx : blocks[i].transactions) { auto pubKey = tx->getTransactionPublicKey(); - if (*reinterpret_cast(&pubKey) == cryptonote::null_pkey) { + if (*reinterpret_cast(&pubKey) == CryptoNote::null_pkey) { + ++blockInfo.transactionIndex; continue; } @@ -247,7 +248,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH } } catch (const std::system_error& e) { processingError = e.code(); - } catch (const std::exception& e) { + } catch (const std::exception&) { processingError = std::make_error_code(std::errc::operation_canceled); } } @@ -264,14 +265,14 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH break; } } - } + } if (processingError) { forEachSubscription([&](TransfersSubscription& sub) { sub.onError(processingError, startHeight); }); return false; - } + } auto newHeight = startHeight + count; forEachSubscription([newHeight](TransfersSubscription& sub) { @@ -281,7 +282,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH return true; } -std::error_code TransfersConsumer::onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) { +std::error_code TransfersConsumer::onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) { BlockInfo unconfirmedBlockInfo; unconfirmedBlockInfo.timestamp = 0; unconfirmedBlockInfo.height = UNCONFIRMED_TRANSACTION_HEIGHT; @@ -307,6 +308,7 @@ std::error_code TransfersConsumer::onPoolUpdated(const std::vectordeleteUnconfirmedTransaction(*reinterpret_cast(&deletedTxHash)); } } + return std::error_code(); } @@ -322,14 +324,92 @@ void TransfersConsumer::getKnownPoolTxIds(std::vector& ids) { ids.assign(knownIds.begin(), knownIds.end()); } + +std::error_code createTransfers( + const AccountKeys& account, + const BlockInfo& blockInfo, + const ITransactionReader& tx, + const std::vector& outputs, + const std::vector& globalIdxs, + std::vector& transfers) { + + auto txPubKey = tx.getTransactionPublicKey(); + + for (auto idx : outputs) { + + if (idx >= tx.getOutputCount()) { + return std::make_error_code(std::errc::argument_out_of_domain); + } + + auto outType = tx.getOutputType(size_t(idx)); + + if ( + outType != TransactionTypes::OutputType::Key && + outType != TransactionTypes::OutputType::Multisignature) { + continue; + } + + TransactionOutputInformationIn info; + + info.type = outType; + info.transactionPublicKey = txPubKey; + info.outputInTransaction = idx; + info.globalOutputIndex = (blockInfo.height == UNCONFIRMED_TRANSACTION_HEIGHT) ? + UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx]; + + if (outType == TransactionTypes::OutputType::Key) { + TransactionTypes::OutputKey out; + tx.getOutput(idx, out); + + CryptoNote::KeyPair in_ephemeral; + CryptoNote::generate_key_image_helper( + reinterpret_cast(account), + reinterpret_cast(txPubKey), + idx, + in_ephemeral, + reinterpret_cast(info.keyImage)); + + assert(out.key == reinterpret_cast(in_ephemeral.pub)); + + info.amount = out.amount; + info.outputKey = out.key; + + } else if (outType == TransactionTypes::OutputType::Multisignature) { + TransactionTypes::OutputMultisignature out; + tx.getOutput(idx, out); + + info.amount = out.amount; + info.requiredSignatures = out.requiredSignatures; + } + + transfers.push_back(info); + } + + return std::error_code(); +} + std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info) { - findMyOutputs(tx, m_viewSecret, m_spendKeys, info.outputs); + std::unordered_map> outputs; + findMyOutputs(tx, m_viewSecret, m_spendKeys, outputs); + + if (outputs.empty()) { + return std::error_code(); + } std::error_code errorCode; - if (!info.outputs.empty()) { - auto txHash = tx.getTransactionHash(); - if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { - errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); + auto txHash = tx.getTransactionHash(); + if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); + if (errorCode) { + return errorCode; + } + } + + for (const auto& kv : outputs) { + auto it = m_subscriptions.find(kv.first); + if (it != m_subscriptions.end()) { + auto& transfers = info.outputs[kv.first]; + errorCode = createTransfers(it->second->getKeys(), blockInfo, tx, kv.second, info.globalIdxs, transfers); if (errorCode) { return errorCode; } @@ -352,7 +432,7 @@ std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) { std::error_code errorCode; - static std::vector emptyOutputs; + std::vector emptyOutputs; for (auto& kv : m_subscriptions) { auto it = info.outputs.find(kv.first); auto& subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it->second; @@ -365,8 +445,10 @@ std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo return std::error_code(); } + + std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, - const ITransactionReader& tx, const std::vector& outputs, const std::vector& globalIdxs) { + const ITransactionReader& tx, const std::vector& transfers, const std::vector& globalIdxs) { if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { TransactionInformation subscribtionTxInfo; @@ -384,64 +466,7 @@ std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, Tr } } - std::vector transfers; - - auto txPubKey = tx.getTransactionPublicKey(); - - for (auto idx : outputs) { - - if (idx >= tx.getOutputCount()) { - return std::make_error_code(std::errc::argument_out_of_domain); - } - - auto outType = tx.getOutputType(size_t(idx)); - - if ( - outType != TransactionTypes::OutputType::Key && - outType != TransactionTypes::OutputType::Multisignature) { - continue; - } - - TransactionOutputInformationIn info; - - info.type = outType; - info.transactionPublicKey = txPubKey; - info.outputInTransaction = idx; - info.globalOutputIndex = - (blockInfo.height == UNCONFIRMED_TRANSACTION_HEIGHT) ? - UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : - globalIdxs[idx]; - - if (outType == TransactionTypes::OutputType::Key) { - TransactionTypes::OutputKey out; - tx.getOutput(idx, out); - - cryptonote::KeyPair in_ephemeral; - cryptonote::generate_key_image_helper( - reinterpret_cast(sub.getKeys()), - reinterpret_cast(txPubKey), - idx, - in_ephemeral, - reinterpret_cast(info.keyImage)); - - assert(out.key == reinterpret_cast(in_ephemeral.pub)); - - info.amount = out.amount; - info.outputKey = out.key; - - } else if (outType == TransactionTypes::OutputType::Multisignature) { - TransactionTypes::OutputMultisignature out; - tx.getOutput(idx, out); - - info.amount = out.amount; - info.requiredSignatures = out.requiredSignatures; - } - - transfers.push_back(info); - } - sub.addTransaction(blockInfo, tx, transfers); - return std::error_code(); } @@ -452,7 +477,7 @@ std::error_code TransfersConsumer::getGlobalIndices(const crypto::hash& transact INode::Callback cb = [&prom](std::error_code ec) { std::promise p(std::move(prom)); - p.set_value(std::move(ec)); + p.set_value(ec); }; outsGlobalIndices.clear(); diff --git a/src/transfers/TransfersConsumer.h b/src/transfers/TransfersConsumer.h index 638994d376..bff3423874 100644 --- a/src/transfers/TransfersConsumer.h +++ b/src/transfers/TransfersConsumer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -36,7 +36,7 @@ class TransfersConsumer : public IBlockchainConsumer { public: - TransfersConsumer(const cryptonote::Currency& currency, INode& node, const SecretKey& viewSecret); + TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const SecretKey& viewSecret); ITransfersSubscription& addSubscription(const AccountSubscription& subscription); // returns true if no subscribers left @@ -48,7 +48,7 @@ class TransfersConsumer : public IBlockchainConsumer { virtual SynchronizationStart getSyncStart() override; virtual void onBlockchainDetach(uint64_t height) override; virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override; - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override; + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override; virtual void getKnownPoolTxIds(std::vector& ids) override; private: @@ -61,7 +61,7 @@ class TransfersConsumer : public IBlockchainConsumer { } struct PreprocessInfo { - std::unordered_map> outputs; + std::unordered_map> outputs; std::vector globalIdxs; }; @@ -69,7 +69,7 @@ class TransfersConsumer : public IBlockchainConsumer { std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx); std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info); std::error_code processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, - const std::vector& outputs, const std::vector& globalIdxs); + const std::vector& outputs, const std::vector& globalIdxs); std::error_code getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); @@ -82,7 +82,7 @@ class TransfersConsumer : public IBlockchainConsumer { std::unordered_set m_spendKeys; INode& m_node; - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; }; } diff --git a/src/transfers/TransfersContainer.cpp b/src/transfers/TransfersContainer.cpp index e36ad2464e..4a0a827db7 100644 --- a/src/transfers/TransfersContainer.cpp +++ b/src/transfers/TransfersContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,7 +24,7 @@ namespace CryptoNote { -void serialize(TransactionInformation& ti, const std::string& name, cryptonote::ISerializer& s) { +void serialize(TransactionInformation& ti, const std::string& name, CryptoNote::ISerializer& s) { s(ti.transactionHash, ""); s(ti.publicKey, ""); s(ti.blockHeight, ""); @@ -159,14 +159,14 @@ size_t SpentOutputDescriptor::hash() const { } -TransfersContainer::TransfersContainer(const cryptonote::Currency& currency, size_t transactionSpendableAge) : +TransfersContainer::TransfersContainer(const Currency& currency, size_t transactionSpendableAge) : m_currentHeight(0), m_currency(currency), m_transactionSpendableAge(transactionSpendableAge) { } bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransactionReader& tx, - const std::vector& transfers) { + const std::vector& transfers) { std::unique_lock lock(m_mutex); if (block.height < m_currentHeight) { @@ -205,6 +205,7 @@ void TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti txInfo.publicKey = tx.getTransactionPublicKey(); txInfo.totalAmountIn = tx.getInputTotalAmount(); txInfo.totalAmountOut = tx.getOutputTotalAmount(); + txInfo.extra = tx.getExtra(); if (!tx.getPaymentId(txInfo.paymentId)) { txInfo.paymentId.fill(0); @@ -246,11 +247,11 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr assert(result.second); } else { if (info.type == TransactionTypes::OutputType::Multisignature) { - SpentOutputDescriptor descriptor(transfer); - if (m_availableTransfers.get().count(descriptor) > 0 || - m_spentTransfers.get().count(descriptor) > 0) { - throw std::runtime_error("Transfer already exists"); - } + SpentOutputDescriptor descriptor(transfer); + if (m_availableTransfers.get().count(descriptor) > 0 || + m_spentTransfers.get().count(descriptor) > 0) { + throw std::runtime_error("Transfer already exists"); + } } auto result = m_availableTransfers.emplace(std::move(info)); @@ -330,10 +331,10 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra outputDescriptorIndex.erase(availableOutputIt); inputsAdded = true; - } + } } else { assert(inputType == TransactionTypes::InputType::Generating); - } + } } return inputsAdded; @@ -350,7 +351,7 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas } else { deleteTransactionTransfers(it->transactionHash); m_transactions.erase(it); - return true; + return true; } } @@ -390,12 +391,12 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const transfer.globalOutputIndex = globalIndices[transfer.outputInTransaction]; if (transfer.type == TransactionTypes::OutputType::Multisignature) { - SpentOutputDescriptor descriptor(transfer); - if (m_availableTransfers.get().count(descriptor) > 0 || - m_spentTransfers.get().count(descriptor) > 0) { - // This exception breaks TransfersContainer consistency - throw std::runtime_error("Transfer already exists"); - } + SpentOutputDescriptor descriptor(transfer); + if (m_availableTransfers.get().count(descriptor) > 0 || + m_spentTransfers.get().count(descriptor) > 0) { + // This exception breaks TransfersContainer consistency + throw std::runtime_error("Transfer already exists"); + } } auto result = m_availableTransfers.emplace(std::move(transfer)); @@ -405,7 +406,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const if (transfer.type == TransactionTypes::OutputType::Key) { updateTransfersVisibility(transfer.keyImage); - } + } } auto& spendingTransactionIndex = m_spentTransfers.get(); @@ -418,7 +419,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const spendingTransactionIndex.replace(transferIt, transfer); } - return true; + return true; } /** @@ -456,11 +457,11 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) for (auto it = transactionTransfersRange.first; it != transactionTransfersRange.second;) { if (it->type == TransactionTypes::OutputType::Key) { KeyImage keyImage = it->keyImage; - it = transactionTransfersIndex.erase(it); + it = transactionTransfersIndex.erase(it); updateTransfersVisibility(keyImage); } else { it = transactionTransfersIndex.erase(it); - } + } } } @@ -501,8 +502,8 @@ std::vector TransfersContainer::detach(uint64_t height) { if (spentTransferIt->blockHeight >= height) { doDelete = true; break; - } - } + } + } } else if (it->blockHeight >= height) { doDelete = true; } else { @@ -513,7 +514,7 @@ std::vector TransfersContainer::detach(uint64_t height) { deleteTransactionTransfers(it->transactionHash); deletedTransactions.emplace_back(it->transactionHash); it = blockHeightIndex.erase(it); - } + } } // TODO: notification on detach @@ -575,7 +576,7 @@ bool TransfersContainer::advanceHeight(uint64_t height) { std::lock_guard lk(m_mutex); if (m_currentHeight <= height) { - m_currentHeight = height; + m_currentHeight = height; return true; } @@ -729,20 +730,20 @@ std::vector TransfersContainer::getSpentOutpu void TransfersContainer::save(std::ostream& os) { std::lock_guard lk(m_mutex); - cryptonote::BinaryOutputStreamSerializer s(os); + CryptoNote::BinaryOutputStreamSerializer s(os); s(const_cast(TRANSFERS_CONTAINER_STORAGE_VERSION), "version"); s(m_currentHeight, "height"); - cryptonote::writeSequence(m_transactions.begin(), m_transactions.end(), "transactions", s); - cryptonote::writeSequence(m_unconfirmedTransfers.begin(), m_unconfirmedTransfers.end(), "unconfirmedTransfers", s); - cryptonote::writeSequence(m_availableTransfers.begin(), m_availableTransfers.end(), "availableTransfers", s); - cryptonote::writeSequence(m_spentTransfers.begin(), m_spentTransfers.end(), "spentTransfers", s); + writeSequence(m_transactions.begin(), m_transactions.end(), "transactions", s); + writeSequence(m_unconfirmedTransfers.begin(), m_unconfirmedTransfers.end(), "unconfirmedTransfers", s); + writeSequence(m_availableTransfers.begin(), m_availableTransfers.end(), "availableTransfers", s); + writeSequence(m_spentTransfers.begin(), m_spentTransfers.end(), "spentTransfers", s); } void TransfersContainer::load(std::istream& in) { std::lock_guard lk(m_mutex); - cryptonote::BinaryInputStreamSerializer s(in); + CryptoNote::BinaryInputStreamSerializer s(in); uint32_t version = 0; s(version, "version"); @@ -758,10 +759,10 @@ void TransfersContainer::load(std::istream& in) { SpentTransfersMultiIndex spentTransfers; s(currentHeight, "height"); - cryptonote::readSequence(std::inserter(transactions, transactions.end()), "transactions", s); - cryptonote::readSequence(std::inserter(unconfirmedTransfers, unconfirmedTransfers.end()), "unconfirmedTransfers", s); - cryptonote::readSequence(std::inserter(availableTransfers, availableTransfers.end()), "availableTransfers", s); - cryptonote::readSequence(std::inserter(spentTransfers, spentTransfers.end()), "spentTransfers", s); + readSequence(std::inserter(transactions, transactions.end()), "transactions", s); + readSequence(std::inserter(unconfirmedTransfers, unconfirmedTransfers.end()), "unconfirmedTransfers", s); + readSequence(std::inserter(availableTransfers, availableTransfers.end()), "availableTransfers", s); + readSequence(std::inserter(spentTransfers, spentTransfers.end()), "spentTransfers", s); m_currentHeight = currentHeight; m_transactions = std::move(transactions); diff --git a/src/transfers/TransfersContainer.h b/src/transfers/TransfersContainer.h index da7cd97c3e..fba0b269e9 100644 --- a/src/transfers/TransfersContainer.h +++ b/src/transfers/TransfersContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -86,7 +86,7 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn { SpentOutputDescriptor getSpentOutputDescriptor() const { return SpentOutputDescriptor(*this); } const Hash& getTransactionHash() const { return transactionHash; } - void serialize(cryptonote::ISerializer& s, const std::string& name) { + void serialize(CryptoNote::ISerializer& s, const std::string& name) { s(reinterpret_cast(type), "type"); s(amount, ""); s(globalOutputIndex, ""); @@ -112,7 +112,7 @@ struct BlockInfo { uint64_t timestamp; uint32_t transactionIndex; - void serialize(cryptonote::ISerializer& s, const std::string& name) { + void serialize(ISerializer& s, const std::string& name) { s(height, "height"); s(timestamp, "timestamp"); s(transactionIndex, "transactionIndex"); @@ -128,7 +128,7 @@ struct SpentTransactionOutput : TransactionOutputInformationEx { return spendingTransactionHash; } - void serialize(cryptonote::ISerializer& s, const std::string& name) { + void serialize(ISerializer& s, const std::string& name) { TransactionOutputInformationEx::serialize(s, name); s(spendingBlock, "spendingBlock"); s(spendingTransactionHash, "spendingTransactionHash"); @@ -151,7 +151,7 @@ class TransfersContainer : public ITransfersContainer { public: - TransfersContainer(const cryptonote::Currency& currency, size_t transactionSpendableAge); + TransfersContainer(const CryptoNote::Currency& currency, size_t transactionSpendableAge); bool addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers); bool deleteUnconfirmedTransaction(const Hash& transactionHash); @@ -235,7 +235,7 @@ class TransfersContainer : public ITransfersContainer { boost::multi_index::hashed_unique< boost::multi_index::tag, boost::multi_index::const_mem_fun< - TransactionOutputInformationEx, + TransactionOutputInformationEx, SpentOutputDescriptor, &TransactionOutputInformationEx::getSpentOutputDescriptor>, SpentOutputDescriptorHasher @@ -279,7 +279,7 @@ class TransfersContainer : public ITransfersContainer { uint64_t m_currentHeight; // current height is needed to check if a transfer is unlocked size_t m_transactionSpendableAge; - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; std::mutex m_mutex; }; diff --git a/src/transfers/TransfersSubscription.cpp b/src/transfers/TransfersSubscription.cpp old mode 100755 new mode 100644 index 8ea2216d78..12dcbeaf8b --- a/src/transfers/TransfersSubscription.cpp +++ b/src/transfers/TransfersSubscription.cpp @@ -1,81 +1,81 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "TransfersSubscription.h" -#include "IWallet.h" - -namespace CryptoNote { - -TransfersSubscription::TransfersSubscription(const cryptonote::Currency& currency, const AccountSubscription& sub) - : m_currency(currency), m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} - - -SynchronizationStart TransfersSubscription::getSyncStart() { - return m_subscription.syncStart; -} - -void TransfersSubscription::onBlockchainDetach(uint64_t height) { - std::vector deletedTransactions = m_transfers.detach(height); - for (auto& hash : deletedTransactions) { - m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, hash); - } -} - -void TransfersSubscription::onError(const std::error_code& ec, uint64_t height) { - if (height != UNCONFIRMED_TRANSACTION_HEIGHT) { - m_transfers.detach(height); - } - m_observerManager.notify(&ITransfersObserver::onError, this, height, ec); -} - -bool TransfersSubscription::advanceHeight(uint64_t height) { - return m_transfers.advanceHeight(height); -} - -const AccountKeys& TransfersSubscription::getKeys() const { - return m_subscription.keys; -} - -void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, - const std::vector& transfers) { - - bool added = m_transfers.addTransaction(blockInfo, tx, transfers); - if (added) { - m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); - } -} - -AccountAddress TransfersSubscription::getAddress() { - return m_subscription.keys.address; -} - -ITransfersContainer& TransfersSubscription::getContainer() { - return m_transfers; -} - -void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) { - m_transfers.deleteUnconfirmedTransaction(transactionHash); - m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); -} - -void TransfersSubscription::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, - const std::vector& globalIndices) { - m_transfers.markTransactionConfirmed(block, transactionHash, globalIndices); - m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash); -} - -} +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransfersSubscription.h" +#include "IWallet.h" + +namespace CryptoNote { + +TransfersSubscription::TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub) + : m_currency(currency), m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} + + +SynchronizationStart TransfersSubscription::getSyncStart() { + return m_subscription.syncStart; +} + +void TransfersSubscription::onBlockchainDetach(uint64_t height) { + std::vector deletedTransactions = m_transfers.detach(height); + for (auto& hash : deletedTransactions) { + m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, hash); + } +} + +void TransfersSubscription::onError(const std::error_code& ec, uint64_t height) { + if (height != UNCONFIRMED_TRANSACTION_HEIGHT) { + m_transfers.detach(height); + } + m_observerManager.notify(&ITransfersObserver::onError, this, height, ec); +} + +bool TransfersSubscription::advanceHeight(uint64_t height) { + return m_transfers.advanceHeight(height); +} + +const AccountKeys& TransfersSubscription::getKeys() const { + return m_subscription.keys; +} + +void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, + const std::vector& transfers) { + + bool added = m_transfers.addTransaction(blockInfo, tx, transfers); + if (added) { + m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); + } +} + +AccountAddress TransfersSubscription::getAddress() { + return m_subscription.keys.address; +} + +ITransfersContainer& TransfersSubscription::getContainer() { + return m_transfers; +} + +void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) { + m_transfers.deleteUnconfirmedTransaction(transactionHash); + m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); +} + +void TransfersSubscription::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, + const std::vector& globalIndices) { + m_transfers.markTransactionConfirmed(block, transactionHash, globalIndices); + m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash); +} + +} diff --git a/src/transfers/TransfersSubscription.h b/src/transfers/TransfersSubscription.h index 98520f9245..67ff9076cf 100644 --- a/src/transfers/TransfersSubscription.h +++ b/src/transfers/TransfersSubscription.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,7 +26,7 @@ namespace CryptoNote { class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITransfersSubscription > { public: - TransfersSubscription(const cryptonote::Currency& currency, const AccountSubscription& sub); + TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub); SynchronizationStart getSyncStart(); void onBlockchainDetach(uint64_t height); @@ -47,7 +47,7 @@ class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITran TransfersContainer m_transfers; AccountSubscription m_subscription; - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; }; } diff --git a/src/transfers/TransfersSynchronizer.cpp b/src/transfers/TransfersSynchronizer.cpp index be9927887f..ea5aaabc3d 100644 --- a/src/transfers/TransfersSynchronizer.cpp +++ b/src/transfers/TransfersSynchronizer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,7 @@ namespace CryptoNote { -void serialize(AccountAddress& acc, const std::string& name, cryptonote::ISerializer& s) { +void serialize(AccountAddress& acc, const std::string& name, CryptoNote::ISerializer& s) { s.beginObject(name); s(acc.spendPublicKey, "spendKey"); s(acc.viewPublicKey, "viewKey"); @@ -32,7 +32,7 @@ void serialize(AccountAddress& acc, const std::string& name, cryptonote::ISerial const uint32_t TRANSFERS_STORAGE_ARCHIVE_VERSION = 0; -TransfersSyncronizer::TransfersSyncronizer(const cryptonote::Currency& currency, IBlockchainSynchronizer& sync, INode& node) : +TransfersSyncronizer::TransfersSyncronizer(const CryptoNote::Currency& currency, IBlockchainSynchronizer& sync, INode& node) : m_currency(currency), m_sync(sync), m_node(node) { } @@ -83,7 +83,7 @@ ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountAddre void TransfersSyncronizer::save(std::ostream& os) { m_sync.save(os); - cryptonote::BinaryOutputStreamSerializer s(os); + CryptoNote::BinaryOutputStreamSerializer s(os); s(const_cast(TRANSFERS_STORAGE_ARCHIVE_VERSION), "version"); size_t subscriptionCount = m_consumers.size(); @@ -146,7 +146,7 @@ void setObjectState(IStreamSerializable& obj, const std::string& state) { void TransfersSyncronizer::load(std::istream& is) { m_sync.load(is); - cryptonote::BinaryInputStreamSerializer s(is); + CryptoNote::BinaryInputStreamSerializer s(is); uint32_t version = 0; s(version, "version"); diff --git a/src/transfers/TransfersSynchronizer.h b/src/transfers/TransfersSynchronizer.h index 14c95b5138..27f1ff75ec 100644 --- a/src/transfers/TransfersSynchronizer.h +++ b/src/transfers/TransfersSynchronizer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -14,6 +14,7 @@ // // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . + #pragma once #include "ITransfersSynchronizer.h" @@ -24,7 +25,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { class Currency; } @@ -36,7 +37,7 @@ class INode; class TransfersSyncronizer : public ITransfersSynchronizer { public: - TransfersSyncronizer(const cryptonote::Currency& currency, IBlockchainSynchronizer& sync, INode& node); + TransfersSyncronizer(const CryptoNote::Currency& currency, IBlockchainSynchronizer& sync, INode& node); ~TransfersSyncronizer(); // ITransfersSynchronizer @@ -57,7 +58,7 @@ class TransfersSyncronizer : public ITransfersSynchronizer { // std::unordered_map> m_subscriptions; IBlockchainSynchronizer& m_sync; INode& m_node; - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; }; } diff --git a/src/transfers/TypeHelpers.h b/src/transfers/TypeHelpers.h index dd7afdb0cc..5b95fd71a9 100644 --- a/src/transfers/TypeHelpers.h +++ b/src/transfers/TypeHelpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/version.h.in b/src/version.h.in index cec6af9b10..117aada244 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.3" -#define PROJECT_VERSION_BUILD_NO "387" +#define PROJECT_VERSION "1.0.4" +#define PROJECT_VERSION_BUILD_NO "461" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/KeysStorage.cpp b/src/wallet/KeysStorage.cpp index 0fb7eafb9a..503a4df127 100644 --- a/src/wallet/KeysStorage.cpp +++ b/src/wallet/KeysStorage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include "serialization/SerializationOverloads.h" #include "cryptonote_core/cryptonote_serialization.h" -namespace cryptonote { +namespace CryptoNote { void KeysStorage::serialize(ISerializer& serializer, const std::string& name) { serializer.beginObject(name); diff --git a/src/wallet/KeysStorage.h b/src/wallet/KeysStorage.h index 423e506c32..f7275f2bc8 100644 --- a/src/wallet/KeysStorage.h +++ b/src/wallet/KeysStorage.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include -namespace cryptonote { +namespace CryptoNote { class ISerializer; @@ -37,4 +37,4 @@ struct KeysStorage { void serialize(ISerializer& serializer, const std::string& name); }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/wallet/LegacyKeysImporter.cpp b/src/wallet/LegacyKeysImporter.cpp index 8cdc22d29a..336053136f 100755 --- a/src/wallet/LegacyKeysImporter.cpp +++ b/src/wallet/LegacyKeysImporter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,16 +20,16 @@ #include #include +#include "Common/StringTools.h" + #include "cryptonote_core/Currency.h" #include "cryptonote_core/account.h" #include "cryptonote_core/AccountKVSerialization.h" -#include "file_io_utils.h" #include "serialization/binary_utils.h" #include "storages/portable_storage.h" #include "storages/portable_storage_template_helper.h" -#include "wallet/wallet_errors.h" #include "wallet/WalletSerializer.h" #include "wallet/WalletUserTransactionsCache.h" #include "wallet/WalletErrors.h" @@ -43,7 +43,7 @@ struct keys_file_data { BEGIN_SERIALIZE_OBJECT() FIELD(iv) FIELD(account_data) - END_SERIALIZE() + END_SERIALIZE() }; bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { @@ -52,13 +52,17 @@ bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expect return r && expected_pub == pub; } -void loadKeysFromFile(const std::string& filename, const std::string& password, cryptonote::account_base& account) { +void loadKeysFromFile(const std::string& filename, const std::string& password, CryptoNote::account_base& account) { keys_file_data keys_file_data; std::string buf; - bool r = epee::file_io_utils::load_file_to_string(filename, buf); - THROW_WALLET_EXCEPTION_IF(!r, tools::error::file_read_error, filename); - r = ::serialization::parse_binary(buf, keys_file_data); - THROW_WALLET_EXCEPTION_IF(!r, tools::error::wallet_internal_error, "internal error: failed to deserialize \"" + filename + '\"'); + + if (!Common::loadFileToString(filename, buf)) { + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to load \"" + filename + '\"'); + } + + if (!::serialization::parse_binary(buf, keys_file_data)) { + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to deserialize \"" + filename + '\"'); + } crypto::chacha8_key key; crypto::cn_context cn_context; @@ -67,26 +71,25 @@ void loadKeysFromFile(const std::string& filename, const std::string& password, account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - const ::cryptonote::account_keys& keys = account.get_keys(); - cryptonote::AccountBaseSerializer accountSerializer(account); - r = epee::serialization::load_t_from_binary(accountSerializer, account_data); + const ::CryptoNote::account_keys& keys = account.get_keys(); + CryptoNote::AccountBaseSerializer accountSerializer(account); + bool r = epee::serialization::load_t_from_binary(accountSerializer, account_data); r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey); r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey); - THROW_WALLET_EXCEPTION_IF(!r, tools::error::invalid_password); + + if (!r) { + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); + } } } -namespace cryptonote { +namespace CryptoNote { void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination) { - cryptonote::account_base account; + CryptoNote::account_base account; - try { - loadKeysFromFile(legacyKeysFilename, password, account); - } catch (tools::error::invalid_password&) { - throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); - } + loadKeysFromFile(legacyKeysFilename, password, account); CryptoNote::WalletUserTransactionsCache transactionsCache; std::string cache; @@ -94,4 +97,4 @@ void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& importer.serialize(destination, password, false, cache); } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/wallet/LegacyKeysImporter.h b/src/wallet/LegacyKeysImporter.h index d201397d5b..510359104e 100755 --- a/src/wallet/LegacyKeysImporter.h +++ b/src/wallet/LegacyKeysImporter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,8 +20,8 @@ #include #include -namespace cryptonote { +namespace CryptoNote { void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination); -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/wallet/MultiWallet.h b/src/wallet/MultiWallet.h new file mode 100644 index 0000000000..59840c4f3e --- /dev/null +++ b/src/wallet/MultiWallet.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IMultiWallet.h" + +#include + +namespace CryptoNote { + +class MultiWallet : public IMultiWallet { +public: + virtual ~MultiWallet(); + + virtual void start() override; + virtual void stop() override; + virtual void refresh() override; + +private: + enum MultiWalletState { + NOT_INITIALIZED = 0, + INITIALIZED + }; + + MultiWalletState m_state; + std::string m_password; +}; + +} //namespace CryptoNote diff --git a/src/wallet/SyncWallet.cpp b/src/wallet/SyncWallet.cpp new file mode 100644 index 0000000000..a7a84a7c39 --- /dev/null +++ b/src/wallet/SyncWallet.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "SyncWallet.h" +#include + +#include + +namespace CryptoNote { + + +SyncWallet::SyncWallet(IWallet& wallet) : m_wallet(wallet), m_promise(nullptr) { + m_wallet.addObserver(this); +} + +SyncWallet::~SyncWallet() { + m_wallet.removeObserver(this); +} + +std::error_code SyncWallet::callWallet(std::function f) { + assert(m_promise == nullptr); + + std::promise prom; + m_promise = &prom; + + f(); + + auto result = prom.get_future().get(); + m_promise = nullptr; + + return result; +} + +void SyncWallet::passResult(std::error_code result) { + if (m_promise != nullptr) { + m_promise->set_value(result); + } +} + +std::error_code SyncWallet::syncInitAndLoad(std::istream& source, const std::string& password) { + return callWallet([&]{ m_wallet.initAndLoad(source, password); }); +} + +std::error_code SyncWallet::syncSave(std::ostream& destination, bool saveDetailed, bool saveCache) { + return callWallet([&]{ m_wallet.save(destination, saveDetailed, saveCache); }); +} + +void SyncWallet::initCompleted(std::error_code result) { + passResult(result); +} + +void SyncWallet::saveCompleted(std::error_code result) { + passResult(result); +} + +} diff --git a/src/wallet/SyncWallet.h b/src/wallet/SyncWallet.h new file mode 100644 index 0000000000..1488269b27 --- /dev/null +++ b/src/wallet/SyncWallet.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" +#include + +namespace CryptoNote { + +// not thread-safe! (sync* methods should be called from one thread) +class SyncWallet: IWalletObserver { + +public: + + SyncWallet(IWallet& wallet); + ~SyncWallet(); + + std::error_code syncInitAndLoad(std::istream& source, const std::string& password); + std::error_code syncSave(std::ostream& destination, bool saveDetailed = true, bool saveCache = true); + +private: + + std::error_code callWallet(std::function f); + void passResult(std::error_code result); + + virtual void initCompleted(std::error_code result) override; + virtual void saveCompleted(std::error_code result) override; + + IWallet& m_wallet; + std::promise* m_promise; +}; + + +} diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index dd7522f5f0..a25dc275e6 100755 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,10 +16,7 @@ // along with Bytecoin. If not, see . #include "Wallet.h" -#include "wallet_errors.h" -#include "string_tools.h" #include "serialization/binary_utils.h" -#include "storages/portable_storage_template_helper.h" #include "WalletUtils.h" #include "WalletSerializer.h" @@ -30,6 +27,8 @@ namespace { +const uint64_t ACCOUN_CREATE_TIME_ACCURACY = 24 * 60 * 60; + void throwNotDefined() { throw std::runtime_error("The behavior is not defined!"); } @@ -42,7 +41,7 @@ bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expecte void throwIfKeysMissmatch(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { if (!verifyKeys(sec, expected_pub)) - throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); } class ContextCounterHolder @@ -112,11 +111,13 @@ class SyncStarter : public CryptoNote::IWalletObserver { BlockchainSynchronizer& m_sync; }; -Wallet::Wallet(const cryptonote::Currency& currency, INode& node) : +Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : m_state(NOT_INITIALIZED), m_currency(currency), m_node(node), m_isStopping(false), + m_lastNotifiedActualBalance(0), + m_lastNotifiedPendingBalance(0), m_blockchainSync(node, currency.genesisBlockHash()), m_transfersSync(currency, m_blockchainSync, node), m_transferDetails(nullptr), @@ -157,7 +158,7 @@ void Wallet::initAndGenerate(const std::string& password) { std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::ALREADY_INITIALIZED)); + throw std::system_error(make_error_code(error::ALREADY_INITIALIZED)); } m_account.generate(); @@ -174,10 +175,10 @@ void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::strin std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::ALREADY_INITIALIZED)); + throw std::system_error(make_error_code(error::ALREADY_INITIALIZED)); } - cryptonote::account_keys keys; + account_keys keys; std::copy(accountKeys.spendPublicKey.begin(), accountKeys.spendPublicKey.end(), @@ -196,7 +197,7 @@ void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::strin reinterpret_cast(&keys.m_view_secret_key)); m_account.set_keys(keys); - m_account.set_createtime(0); + m_account.set_createtime(ACCOUN_CREATE_TIME_ACCURACY); m_password = password; initSync(); @@ -209,7 +210,7 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::ALREADY_INITIALIZED)); + throw std::system_error(make_error_code(error::ALREADY_INITIALIZED)); } m_password = password; @@ -225,7 +226,7 @@ void Wallet::initSync() { sub.keys = reinterpret_cast(m_account.get_keys()); sub.transactionSpendableAge = 1; sub.syncStart.height = 0; - sub.syncStart.timestamp = m_account.get_createtime() - (60 * 60 * 24); + sub.syncStart.timestamp = m_account.get_createtime() - ACCOUN_CREATE_TIME_ACCURACY; auto& subObject = m_transfersSync.addSubscription(sub); m_transferDetails = &subObject.getContainer(); @@ -247,13 +248,13 @@ void Wallet::doLoad(std::istream& source) { initSync(); try { - if (!cache.empty()) { - std::stringstream stream(cache); - m_transfersSync.load(stream); - } + if (!cache.empty()) { + std::stringstream stream(cache); + m_transfersSync.load(stream); + } } catch (const std::exception&) { // ignore cache loading errors - } + } } catch (std::system_error& e) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); @@ -262,23 +263,13 @@ void Wallet::doLoad(std::istream& source) { } catch (std::exception&) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); } -void Wallet::decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password) { - crypto::chacha8_key key; - crypto::cn_context context; - crypto::generate_chacha8_key(context, password, key); - - plain.resize(cipher.size()); - - crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]); -} - void Wallet::shutdown() { { std::unique_lock lock(m_cacheMutex); @@ -316,14 +307,14 @@ void Wallet::reset() { std::stringstream ss; try { - save(ss, false, false); + save(ss, false, false); auto saveError = saveWaiter.waitSave(); - if (!saveError) { - shutdown(); - initAndLoad(ss, m_password); + if (!saveError) { + shutdown(); + initAndLoad(ss, m_password); initWaiter.waitInit(); - } + } } catch (std::exception&) { } @@ -333,14 +324,14 @@ void Wallet::reset() { void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { if(m_isStopping) { - m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::OPERATION_CANCELLED)); + m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(CryptoNote::error::OPERATION_CANCELLED)); return; } { std::unique_lock lock(m_cacheMutex); - throwIf(m_state != INITIALIZED, cryptonote::error::WRONG_STATE); + throwIf(m_state != INITIALIZED, CryptoNote::error::WRONG_STATE); m_state = SAVING; } @@ -370,7 +361,7 @@ void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache m_state = INITIALIZED; m_blockchainSync.start(); //XXX: start can throw. what to do in this case? - } + } catch (std::system_error& e) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); m_observerManager.notify(&IWalletObserver::saveCompleted, e.code()); @@ -378,33 +369,20 @@ void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache } catch (std::exception&) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } m_observerManager.notify(&IWalletObserver::saveCompleted, std::error_code()); } -crypto::chacha8_iv Wallet::encrypt(const std::string& plain, std::string& cipher) { - crypto::chacha8_key key; - crypto::cn_context context; - crypto::generate_chacha8_key(context, m_password, key); - - cipher.resize(plain.size()); - - crypto::chacha8_iv iv = crypto::rand(); - crypto::chacha8(plain.data(), plain.size(), key, iv, &cipher[0]); - - return iv; -} - std::error_code Wallet::changePassword(const std::string& oldPassword, const std::string& newPassword) { std::unique_lock passLock(m_cacheMutex); throwIfNotInitialised(); if (m_password.compare(oldPassword)) - return make_error_code(cryptonote::error::WRONG_PASSWORD); + return make_error_code(CryptoNote::error::WRONG_PASSWORD); //we don't let the user to change the password while saving m_password = newPassword; @@ -536,7 +514,7 @@ void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::erro } std::error_code Wallet::cancelTransaction(size_t transactionId) { - return make_error_code(cryptonote::error::TX_CANCEL_IMPOSSIBLE); + return make_error_code(CryptoNote::error::TX_CANCEL_IMPOSSIBLE); } void Wallet::synchronizationProgressUpdated(uint64_t current, uint64_t total) { @@ -576,7 +554,7 @@ void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& tr std::shared_ptr event; { - std::unique_lock lock(m_cacheMutex); + std::unique_lock lock(m_cacheMutex); event = m_transactionsCache.onTransactionDeleted(transactionHash); } @@ -586,8 +564,9 @@ void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& tr } void Wallet::throwIfNotInitialised() { - if (m_state == NOT_INITIALIZED || m_state == LOADING) - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + if (m_state == NOT_INITIALIZED || m_state == LOADING) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } assert(m_transferDetails); } @@ -618,10 +597,10 @@ void Wallet::notifyIfBalanceChanged() { void Wallet::getAccountKeys(WalletAccountKeys& keys) { if (m_state == NOT_INITIALIZED) { - throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } - const cryptonote::account_keys& accountKeys = m_account.get_keys(); + const CryptoNote::account_keys& accountKeys = m_account.get_keys(); std::copy(reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey), reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey) + sizeof(crypto::public_key), keys.spendPublicKey.begin()); diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index c6c27fac56..6080012782 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -27,7 +27,7 @@ #include "INode.h" #include "WalletErrors.h" #include "WalletAsyncContextCounter.h" -#include "common/ObserverManager.h" +#include "Common/ObserverManager.h" #include "cryptonote_core/tx_extra.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/Currency.h" @@ -50,7 +50,7 @@ class Wallet : ITransfersObserver { public: - Wallet(const cryptonote::Currency& currency, INode& node); + Wallet(const CryptoNote::Currency& currency, INode& node); virtual ~Wallet(); virtual void addObserver(IWalletObserver* observer); @@ -101,9 +101,6 @@ class Wallet : void doSave(std::ostream& destination, bool saveDetailed, bool saveCache); void doLoad(std::istream& source); - crypto::chacha8_iv encrypt(const std::string& plain, std::string& cipher); - void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password); - void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec); void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec); void notifyClients(std::deque >& events); @@ -119,9 +116,9 @@ class Wallet : WalletState m_state; std::mutex m_cacheMutex; - cryptonote::account_base m_account; + CryptoNote::account_base m_account; std::string m_password; - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; INode& m_node; bool m_isStopping; diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/wallet/WalletAsyncContextCounter.cpp index 75d3514855..2ddbf05435 100644 --- a/src/wallet/WalletAsyncContextCounter.cpp +++ b/src/wallet/WalletAsyncContextCounter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/wallet/WalletAsyncContextCounter.h b/src/wallet/WalletAsyncContextCounter.h index 5bf34d9661..1b3f1bd9f1 100644 --- a/src/wallet/WalletAsyncContextCounter.h +++ b/src/wallet/WalletAsyncContextCounter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/wallet/WalletErrors.cpp b/src/wallet/WalletErrors.cpp index 926eca98de..38bb09adf9 100644 --- a/src/wallet/WalletErrors.cpp +++ b/src/wallet/WalletErrors.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #include "WalletErrors.h" -namespace cryptonote { +namespace CryptoNote { namespace error { WalletErrorCategory WalletErrorCategory::INSTANCE; diff --git a/src/wallet/WalletErrors.h b/src/wallet/WalletErrors.h index baf33464e8..445f21053e 100644 --- a/src/wallet/WalletErrors.h +++ b/src/wallet/WalletErrors.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ #include #include -namespace cryptonote { +namespace CryptoNote { namespace error { // custom error conditions enum type: @@ -83,6 +83,6 @@ class WalletErrorCategory : public std::error_category { } } -inline std::error_code make_error_code(cryptonote::error::WalletErrorCodes e) { - return std::error_code(static_cast(e), cryptonote::error::WalletErrorCategory::INSTANCE); +inline std::error_code make_error_code(CryptoNote::error::WalletErrorCodes e) { + return std::error_code(static_cast(e), CryptoNote::error::WalletErrorCategory::INSTANCE); } diff --git a/src/wallet/WalletEvent.h b/src/wallet/WalletEvent.h index 3c21fbb807..faf64e034e 100644 --- a/src/wallet/WalletEvent.h +++ b/src/wallet/WalletEvent.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,7 +18,7 @@ #pragma once #include "IWallet.h" -#include "common/ObserverManager.h" +#include "Common/ObserverManager.h" namespace CryptoNote { diff --git a/src/wallet/WalletHelper.cpp b/src/wallet/WalletHelper.cpp index 6e5c286eaa..86fc754839 100755 --- a/src/wallet/WalletHelper.cpp +++ b/src/wallet/WalletHelper.cpp @@ -1,22 +1,129 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include "WalletHelper.h" + +#include #include -#include "string_tools.h" +#include "Common/PathTools.h" #include "cryptonote_protocol/blobdatatype.h" -using namespace cryptonote; -using namespace epee; +using namespace CryptoNote; + +namespace { +void openOutputFileStream(const std::string& filename, std::ofstream& file) { + file.open(filename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (file.fail()) { + throw std::runtime_error("error opening file: " + filename); + } +} + +std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { + CryptoNote::WalletHelper::SaveWalletResultObserver o; + + std::error_code e; + try { + std::future f = o.saveResult.get_future(); + wallet.addObserver(&o); + wallet.save(file, saveDetailes, saveCache); + e = f.get(); + } catch (std::exception&) { + wallet.removeObserver(&o); + return make_error_code(std::errc::invalid_argument); + } + + wallet.removeObserver(&o); + return e; +} + +} void WalletHelper::prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { - if (string_tools::get_extension(file_path) == "wallet") { - keys_file = string_tools::cut_off_extension(file_path) + ".keys"; + if (Common::GetExtension(file_path) == ".wallet") { + keys_file = Common::RemoveExtension(file_path) + ".keys"; wallet_file = file_path; - } else if (string_tools::get_extension(file_path) == "keys") { + } else if (Common::GetExtension(file_path) == ".keys") { keys_file = file_path; - wallet_file = string_tools::cut_off_extension(file_path) + ".wallet"; + wallet_file = Common::RemoveExtension(file_path) + ".wallet"; } else { keys_file = file_path + ".keys"; wallet_file = file_path + ".wallet"; } } + +void WalletHelper::SendCompleteResultObserver::sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) { + std::lock_guard lock(m_mutex); + m_finishedTransactions[transactionId] = result; + m_condition.notify_one(); +} + +std::error_code WalletHelper::SendCompleteResultObserver::wait(CryptoNote::TransactionId transactionId) { + std::unique_lock lock(m_mutex); + m_condition.wait(lock, [this, &transactionId] { return m_finishedTransactions.find(transactionId) != m_finishedTransactions.end(); }); + return m_finishedTransactions.find(transactionId)->second; +} + +WalletHelper::IWalletRemoveObserverGuard::IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer) : + m_wallet(wallet), + m_observer(observer), + m_removed(false) { + m_wallet.addObserver(&m_observer); +} + +WalletHelper::IWalletRemoveObserverGuard::~IWalletRemoveObserverGuard() { + if (!m_removed) { + m_wallet.removeObserver(&m_observer); + } +} + +void WalletHelper::IWalletRemoveObserverGuard::removeObserver() { + m_wallet.removeObserver(&m_observer); + m_removed = true; +} + +void WalletHelper::storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename) { + boost::filesystem::path tempFile = boost::filesystem::unique_path(walletFilename + ".tmp.%%%%-%%%%"); + + if (boost::filesystem::exists(walletFilename)) { + boost::filesystem::rename(walletFilename, tempFile); + } + + std::ofstream file; + try { + openOutputFileStream(walletFilename, file); + } catch (std::exception&) { + if (boost::filesystem::exists(tempFile)) { + boost::filesystem::rename(tempFile, walletFilename); + } + throw; + } + + std::error_code saveError = walletSaveWrapper(wallet, file, true, true); + if (saveError) { + file.close(); + boost::filesystem::remove(walletFilename); + boost::filesystem::rename(tempFile, walletFilename); + throw std::system_error(saveError); + } + + file.close(); + + boost::system::error_code ignore; + boost::filesystem::remove(tempFile, ignore); +} diff --git a/src/wallet/WalletHelper.h b/src/wallet/WalletHelper.h index ac1cc9c61a..4a4d87058b 100755 --- a/src/wallet/WalletHelper.h +++ b/src/wallet/WalletHelper.h @@ -1,12 +1,31 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #include +#include +#include #include "crypto/hash.h" #include "IWallet.h" -namespace cryptonote { +namespace CryptoNote { namespace WalletHelper { class SaveWalletResultObserver : public CryptoNote::IWalletObserver { @@ -23,13 +42,28 @@ class InitWalletResultObserver : public CryptoNote::IWalletObserver { class SendCompleteResultObserver : public CryptoNote::IWalletObserver { public: - std::future expectedTxID; - std::promise sendResult; - virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { - if (transactionId == expectedTxID.get()) sendResult.set_value(result); - } + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override; + std::error_code wait(CryptoNote::TransactionId transactionId); + +private: + std::mutex m_mutex; + std::condition_variable m_condition; + std::map m_finishedTransactions; +}; + +class IWalletRemoveObserverGuard { +public: + IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer); + ~IWalletRemoveObserverGuard(); + + void removeObserver(); +private: + CryptoNote::IWallet& m_wallet; + CryptoNote::IWalletObserver& m_observer; + bool m_removed; }; void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file); +void storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename); } } diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h index 2ec27d4c80..c87264533b 100644 --- a/src/wallet/WalletRequest.h +++ b/src/wallet/WalletRequest.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -63,7 +63,7 @@ class WalletGetRandomOutsByAmountsRequest: public WalletRequest class WalletRelayTransactionRequest: public WalletRequest { public: - WalletRelayTransactionRequest(const cryptonote::Transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {}; + WalletRelayTransactionRequest(const CryptoNote::Transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {}; virtual ~WalletRelayTransactionRequest() {}; virtual void perform(INode& node, std::function cb) @@ -72,7 +72,7 @@ class WalletRelayTransactionRequest: public WalletRequest } private: - cryptonote::Transaction m_tx; + CryptoNote::Transaction m_tx; Callback m_cb; }; diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h index db40d86474..2fe65d8fd8 100644 --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/wallet/WalletSendTransactionContext.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -30,16 +30,16 @@ struct TxDustPolicy { uint64_t dustThreshold; bool addToFee; - cryptonote::AccountPublicAddress addrForDust; + CryptoNote::AccountPublicAddress addrForDust; - TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::AccountPublicAddress an_addr_for_dust = cryptonote::AccountPublicAddress()) + TxDustPolicy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, CryptoNote::AccountPublicAddress an_addr_for_dust = CryptoNote::AccountPublicAddress()) : dustThreshold(a_dust_threshold), addToFee(an_add_to_fee), addrForDust(an_addr_for_dust) {} }; struct SendTransactionContext { TransactionId transactionId; - std::vector outs; + std::vector outs; uint64_t foundMoney; std::list selectedTransfers; TxDustPolicy dustPolicy; diff --git a/src/wallet/WalletSerialization.cpp b/src/wallet/WalletSerialization.cpp index c207eb741d..a79ac6cc85 100644 --- a/src/wallet/WalletSerialization.cpp +++ b/src/wallet/WalletSerialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -25,7 +25,7 @@ namespace CryptoNote { -void serialize(UnconfirmedTransferDetails& utd, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(UnconfirmedTransferDetails& utd, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer(utd.tx, "transaction"); serializer(utd.amount, "amount"); @@ -39,7 +39,7 @@ void serialize(UnconfirmedTransferDetails& utd, const std::string& name, crypton serializer.endObject(); } -void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(TransactionInfo& txi, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); uint64_t trId = static_cast(txi.firstTransferId); @@ -62,7 +62,7 @@ void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISeria serializer.endObject(); } -void serialize(Transfer& tr, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(Transfer& tr, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer(tr.address, "address"); serializer(tr.amount, "amount"); diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h index 613ac73659..aef7e9b974 100755 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,7 @@ #include "IWallet.h" -namespace cryptonote { +namespace CryptoNote { class ISerializer; } @@ -33,9 +33,8 @@ struct UnconfirmedTransferDetails; struct TransactionInfo; struct Transfer; -void serialize(UnconfirmedTransferDetails& utd, const std::string& name, cryptonote::ISerializer& serializer); -void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISerializer& serializer); -void serialize(Transfer& tr, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(UnconfirmedTransferDetails& utd, const std::string& name, CryptoNote::ISerializer& serializer); +void serialize(TransactionInfo& txi, const std::string& name, CryptoNote::ISerializer& serializer); +void serialize(Transfer& tr, const std::string& name, CryptoNote::ISerializer& serializer); } - diff --git a/src/wallet/WalletSerializer.cpp b/src/wallet/WalletSerializer.cpp index d8559716d3..61f20242be 100644 --- a/src/wallet/WalletSerializer.cpp +++ b/src/wallet/WalletSerializer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -37,14 +37,14 @@ bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expecte void throwIfKeysMissmatch(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { if (!verifyKeys(sec, expected_pub)) - throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); } } namespace CryptoNote { -WalletSerializer::WalletSerializer(cryptonote::account_base& account, WalletUserTransactionsCache& transactionsCache) : +WalletSerializer::WalletSerializer(CryptoNote::account_base& account, WalletUserTransactionsCache& transactionsCache) : account(account), transactionsCache(transactionsCache), walletSerializationVersion(1) @@ -53,7 +53,7 @@ WalletSerializer::WalletSerializer(cryptonote::account_base& account, WalletUser void WalletSerializer::serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache) { std::stringstream plainArchive; - cryptonote::BinaryOutputStreamSerializer serializer(plainArchive); + CryptoNote::BinaryOutputStreamSerializer serializer(plainArchive); saveKeys(serializer); serializer(saveDetailed, "has_details"); @@ -70,17 +70,19 @@ void WalletSerializer::serialize(std::ostream& stream, const std::string& passwo crypto::chacha8_iv iv = encrypt(plain, password, cipher); uint32_t version = walletSerializationVersion; - cryptonote::BinaryOutputStreamSerializer s(stream); + CryptoNote::BinaryOutputStreamSerializer s(stream); s.beginObject("wallet"); s(version, "version"); s(iv, "iv"); s(cipher, "data"); s.endObject(); + + stream.flush(); } -void WalletSerializer::saveKeys(cryptonote::ISerializer& serializer) { - cryptonote::KeysStorage keys; - cryptonote::account_keys acc = account.get_keys(); +void WalletSerializer::saveKeys(CryptoNote::ISerializer& serializer) { + CryptoNote::KeysStorage keys; + CryptoNote::account_keys acc = account.get_keys(); keys.creationTimestamp = account.get_createtime(); keys.spendPublicKey = acc.m_account_address.m_spendPublicKey; @@ -106,7 +108,7 @@ crypto::chacha8_iv WalletSerializer::encrypt(const std::string& plain, const std void WalletSerializer::deserialize(std::istream& stream, const std::string& password, std::string& cache) { - cryptonote::BinaryInputStreamSerializer serializerEncrypted(stream); + CryptoNote::BinaryInputStreamSerializer serializerEncrypted(stream); serializerEncrypted.beginObject("wallet"); @@ -126,7 +128,7 @@ void WalletSerializer::deserialize(std::istream& stream, const std::string& pass std::stringstream decryptedStream(plain); - cryptonote::BinaryInputStreamSerializer serializer(decryptedStream); + CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); try { @@ -135,7 +137,7 @@ void WalletSerializer::deserialize(std::istream& stream, const std::string& pass throwIfKeysMissmatch(account.get_keys().m_spend_secret_key, account.get_keys().m_account_address.m_spendPublicKey); } catch (std::exception&) { - throw std::system_error(make_error_code(cryptonote::error::WRONG_PASSWORD)); + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); } bool detailsSaved; @@ -159,12 +161,12 @@ void WalletSerializer::decrypt(const std::string& cipher, std::string& plain, cr crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]); } -void WalletSerializer::loadKeys(cryptonote::ISerializer& serializer) { - cryptonote::KeysStorage keys; +void WalletSerializer::loadKeys(CryptoNote::ISerializer& serializer) { + CryptoNote::KeysStorage keys; keys.serialize(serializer, "keys"); - cryptonote::account_keys acc; + CryptoNote::account_keys acc; acc.m_account_address.m_spendPublicKey = keys.spendPublicKey; acc.m_spend_secret_key = keys.spendSecretKey; acc.m_account_address.m_viewPublicKey = keys.viewPublicKey; @@ -175,5 +177,3 @@ void WalletSerializer::loadKeys(cryptonote::ISerializer& serializer) { } } - - diff --git a/src/wallet/WalletSerializer.h b/src/wallet/WalletSerializer.h index 2fe1137310..aec3eac975 100644 --- a/src/wallet/WalletSerializer.h +++ b/src/wallet/WalletSerializer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,7 +24,7 @@ #include "crypto/hash.h" #include "crypto/chacha8.h" -namespace cryptonote { +namespace CryptoNote { class account_base; class ISerializer; } @@ -35,19 +35,19 @@ class WalletUserTransactionsCache; class WalletSerializer { public: - WalletSerializer(cryptonote::account_base& account, WalletUserTransactionsCache& transactionsCache); + WalletSerializer(CryptoNote::account_base& account, WalletUserTransactionsCache& transactionsCache); void serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache); void deserialize(std::istream& stream, const std::string& password, std::string& cache); private: - void saveKeys(cryptonote::ISerializer& serializer); - void loadKeys(cryptonote::ISerializer& serializer); + void saveKeys(CryptoNote::ISerializer& serializer); + void loadKeys(CryptoNote::ISerializer& serializer); crypto::chacha8_iv encrypt(const std::string& plain, const std::string& password, std::string& cipher); void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password); - cryptonote::account_base& account; + CryptoNote::account_base& account; WalletUserTransactionsCache& transactionsCache; const uint32_t walletSerializationVersion; }; diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index bfc61d3d34..95dec268b0 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,9 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// epee -#include "misc_language.h" - #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" @@ -26,6 +23,8 @@ #include "cryptonote_core/cryptonote_basic_impl.h" +#include + #include namespace { @@ -35,36 +34,38 @@ using namespace CryptoNote; uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { uint64_t needed_money = fee; for (auto& transfer: transfers) { - CryptoNote::throwIf(transfer.amount == 0, cryptonote::error::ZERO_DESTINATION); - CryptoNote::throwIf(transfer.amount < 0, cryptonote::error::WRONG_AMOUNT); + CryptoNote::throwIf(transfer.amount == 0, CryptoNote::error::ZERO_DESTINATION); + CryptoNote::throwIf(transfer.amount < 0, CryptoNote::error::WRONG_AMOUNT); needed_money += transfer.amount; - CryptoNote::throwIf(static_cast(needed_money) < transfer.amount, cryptonote::error::SUM_OVERFLOW); + CryptoNote::throwIf(static_cast(needed_money) < transfer.amount, CryptoNote::error::SUM_OVERFLOW); } return needed_money; } -void createChangeDestinations(const cryptonote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, cryptonote::tx_destination_entry& changeDts) { +void createChangeDestinations(const CryptoNote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, CryptoNote::tx_destination_entry& changeDts) { if (neededMoney < foundMoney) { changeDts.addr = address; changeDts.amount = foundMoney - neededMoney; } } -void constructTx(const cryptonote::account_keys keys, const std::vector& sources, const std::vector& splittedDests, - const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, cryptonote::Transaction& tx) { +void constructTx(const CryptoNote::account_keys keys, const std::vector& sources, const std::vector& splittedDests, + const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, CryptoNote::Transaction& tx) { std::vector extraVec; extraVec.reserve(extra.size()); std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);}); - bool r = cryptonote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp); - CryptoNote::throwIf(!r, cryptonote::error::INTERNAL_WALLET_ERROR); - CryptoNote::throwIf(cryptonote::get_object_blobsize(tx) >= sizeLimit, cryptonote::error::TRANSACTION_SIZE_TOO_BIG); + Logging::LoggerGroup nullLog; + bool r = CryptoNote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp, nullLog); + + CryptoNote::throwIf(!r, CryptoNote::error::INTERNAL_WALLET_ERROR); + CryptoNote::throwIf(CryptoNote::get_object_blobsize(tx) >= sizeLimit, CryptoNote::error::TRANSACTION_SIZE_TOO_BIG); } -void fillTransactionHash(const cryptonote::Transaction& tx, CryptoNote::TransactionHash& hash) { - crypto::hash h = cryptonote::get_transaction_hash(tx); +void fillTransactionHash(const CryptoNote::Transaction& tx, CryptoNote::TransactionHash& hash) { + crypto::hash h = CryptoNote::get_transaction_hash(tx); memcpy(hash.data(), reinterpret_cast(&h), hash.size()); } @@ -77,7 +78,7 @@ std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& tran namespace CryptoNote { -WalletTransactionSender::WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, cryptonote::account_keys keys, ITransfersContainer& transfersContainer) : +WalletTransactionSender::WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer) : m_currency(currency), m_transactionsCache(transactionsCache), m_isStoping(false), @@ -90,14 +91,14 @@ void WalletTransactionSender::stop() { } bool WalletTransactionSender::validateDestinationAddress(const std::string& address) { - cryptonote::AccountPublicAddress ignore; + CryptoNote::AccountPublicAddress ignore; return m_currency.parseAccountAddressString(address, ignore); } void WalletTransactionSender::validateTransfersAddresses(const std::vector& transfers) { for (const Transfer& tr: transfers) { if (!validateDestinationAddress(tr.address)) { - throw std::system_error(make_error_code(cryptonote::error::BAD_ADDRESS)); + throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); } } } @@ -105,16 +106,16 @@ void WalletTransactionSender::validateTransfersAddresses(const std::vector WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { - using namespace cryptonote; + using namespace CryptoNote; - throwIf(transfers.empty(), cryptonote::error::ZERO_DESTINATION); + throwIf(transfers.empty(), CryptoNote::error::ZERO_DESTINATION); validateTransfersAddresses(transfers); uint64_t neededMoney = countNeededMoney(fee, transfers); std::shared_ptr context = std::make_shared(); context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); - throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); + throwIf(context->foundMoney < neededMoney, CryptoNote::error::WRONG_AMOUNT); transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, extra, transfers, unlockTimestamp); context->transactionId = transactionId; @@ -144,7 +145,7 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< boost::optional >& nextRequest, std::error_code ec) { if (m_isStoping) { - ec = make_error_code(cryptonote::error::TX_CANCELLED); + ec = make_error_code(CryptoNote::error::TX_CANCELLED); } if (ec) { @@ -153,10 +154,10 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< } auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), - [&] (cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); + [&] (CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); if (scanty_it != context->outs.end()) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::MIXIN_COUNT_TOO_BIG))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG))); return; } @@ -167,7 +168,7 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque >& events) { if (m_isStoping) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::TX_CANCELLED))); return std::shared_ptr(); } @@ -175,17 +176,18 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s { TransactionInfo& transaction = m_transactionsCache.getTransaction(context->transactionId); - std::vector sources; + std::vector sources; prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); - cryptonote::tx_destination_entry changeDts = AUTO_VAL_INIT(changeDts); + CryptoNote::tx_destination_entry changeDts; + changeDts.amount = 0; uint64_t totalAmount = -transaction.totalAmount; createChangeDestinations(m_keys.m_account_address, totalAmount, context->foundMoney, changeDts); - std::vector splittedDests; + std::vector splittedDests; splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); - cryptonote::Transaction tx; + CryptoNote::Transaction tx; constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx); fillTransactionHash(tx, transaction.hash); @@ -201,7 +203,7 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); } catch(std::exception&) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR))); } return std::shared_ptr(); @@ -217,64 +219,64 @@ void WalletTransactionSender::relayTransactionCallback(std::shared_ptr& splittedDests) { +void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests) { uint64_t dust = 0; digitSplitStrategy(firstTransferId, transfersCount, changeDts, dustPolicy.dustThreshold, splittedDests, dust); - throwIf(dustPolicy.dustThreshold < dust, cryptonote::error::INTERNAL_WALLET_ERROR); + throwIf(dustPolicy.dustThreshold < dust, CryptoNote::error::INTERNAL_WALLET_ERROR); if (0 != dust && !dustPolicy.addToFee) { - splittedDests.push_back(cryptonote::tx_destination_entry(dust, dustPolicy.addrForDust)); + splittedDests.push_back(CryptoNote::tx_destination_entry(dust, dustPolicy.addrForDust)); } } void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, - const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust) { + const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust) { splitted_dsts.clear(); dust = 0; for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) { Transfer& de = m_transactionsCache.getTransfer(idx); - cryptonote::AccountPublicAddress addr; + CryptoNote::AccountPublicAddress addr; if (!m_currency.parseAccountAddressString(de.address, addr)) { - throw std::system_error(make_error_code(cryptonote::error::BAD_ADDRESS)); + throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); } - cryptonote::decompose_amount_into_digits(de.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, addr)); }, - [&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, addr)); } ); + CryptoNote::decompose_amount_into_digits(de.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, addr)); }, + [&](uint64_t a_dust) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(a_dust, addr)); } ); } - cryptonote::decompose_amount_into_digits(change_dst.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr)); }, + CryptoNote::decompose_amount_into_digits(change_dst.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, change_dst.addr)); }, [&](uint64_t a_dust) { dust = a_dust; } ); } void WalletTransactionSender::prepareInputs( const std::list& selectedTransfers, - std::vector& outs, - std::vector& sources, uint64_t mixIn) { + std::vector& outs, + std::vector& sources, uint64_t mixIn) { size_t i = 0; for (const auto& td: selectedTransfers) { sources.resize(sources.size()+1); - cryptonote::tx_source_entry& src = sources.back(); + CryptoNote::tx_source_entry& src = sources.back(); src.amount = td.amount; //paste mixin transaction if(outs.size()) { - outs[i].outs.sort([](const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); + outs[i].outs.sort([](const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); for (auto& daemon_oe: outs[i].outs) { if(td.globalOutputIndex == daemon_oe.global_amount_index) continue; - cryptonote::tx_source_entry::output_entry oe; + CryptoNote::tx_source_entry::output_entry oe; oe.first = daemon_oe.global_amount_index; oe.second = daemon_oe.out_key; src.outputs.push_back(oe); @@ -284,9 +286,9 @@ void WalletTransactionSender::prepareInputs( } //paste real transaction to the random index - auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const cryptonote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; }); + auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const CryptoNote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; }); - cryptonote::tx_source_entry::output_entry real_oe; + CryptoNote::tx_source_entry::output_entry real_oe; real_oe.first = td.globalOutputIndex; real_oe.second = reinterpret_cast(td.outputKey); diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h index 187ef65b22..4ea01a59e7 100644 --- a/src/wallet/WalletTransactionSender.h +++ b/src/wallet/WalletTransactionSender.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -33,9 +33,9 @@ namespace CryptoNote { class WalletTransactionSender { public: - WalletTransactionSender(const cryptonote::Currency& currency, WalletUserTransactionsCache& transactionsCache, cryptonote::account_keys keys, ITransfersContainer& transfersContainer); + WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer); - void init(cryptonote::account_keys keys, ITransfersContainer& transfersContainer); + void init(CryptoNote::account_keys keys, ITransfersContainer& transfersContainer); void stop(); std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, @@ -44,12 +44,12 @@ class WalletTransactionSender private: std::shared_ptr makeGetRandomOutsRequest(std::shared_ptr context); std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque >& events); - void prepareInputs(const std::list& selectedTransfers, std::vector& outs, - std::vector& sources, uint64_t mixIn); - void splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts, - const TxDustPolicy& dustPolicy, std::vector& splittedDests); - void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust); + void prepareInputs(const std::list& selectedTransfers, std::vector& outs, + std::vector& sources, uint64_t mixIn); + void splitDestinations(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests); + void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust); void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, boost::optional >& nextRequest, std::error_code ec); void relayTransactionCallback(std::shared_ptr context, std::deque >& events, @@ -61,8 +61,8 @@ class WalletTransactionSender uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); - const cryptonote::Currency& m_currency; - cryptonote::account_keys m_keys; + const CryptoNote::Currency& m_currency; + CryptoNote::account_keys m_keys; WalletUserTransactionsCache& m_transactionsCache; uint64_t m_upperTransactionSizeLimit; diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index f322ffee0e..a96ae36e44 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -28,12 +28,12 @@ inline TransactionOutputId getOutputId(const TransactionOutputInformation& out) return std::make_pair(out.transactionPublicKey, out.outputInTransaction); } -void WalletUnconfirmedTransactions::serialize(cryptonote::ISerializer& s, const std::string& name) { +void WalletUnconfirmedTransactions::serialize(CryptoNote::ISerializer& s, const std::string& name) { s.beginObject(name); s(m_unconfirmedTxs, "transactions"); s.endObject(); - if (s.type() == cryptonote::ISerializer::INPUT) { + if (s.type() == CryptoNote::ISerializer::INPUT) { collectUsedOutputs(); } } @@ -60,10 +60,10 @@ void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { m_unconfirmedTxs.erase(it); } -void WalletUnconfirmedTransactions::add(const cryptonote::Transaction& tx, TransactionId transactionId, +void WalletUnconfirmedTransactions::add(const CryptoNote::Transaction& tx, TransactionId transactionId, uint64_t amount, const std::list& usedOutputs) { - auto cryptoHash = cryptonote::get_transaction_hash(tx); + auto cryptoHash = CryptoNote::get_transaction_hash(tx); TransactionHash hash = reinterpret_cast(cryptoHash); UnconfirmedTransferDetails& utd = m_unconfirmedTxs[hash]; diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index f21f86b28c..e36b8bbfb7 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -28,7 +28,7 @@ #include "crypto/hash.h" #include "cryptonote_core/cryptonote_basic.h" -namespace cryptonote { +namespace CryptoNote { class ISerializer; } @@ -41,7 +41,7 @@ struct UnconfirmedTransferDetails { UnconfirmedTransferDetails() : amount(0), sentTime(0), transactionId(INVALID_TRANSACTION_ID) {} - cryptonote::Transaction tx; + CryptoNote::Transaction tx; uint64_t amount; uint64_t outsAmount; time_t sentTime; @@ -53,11 +53,11 @@ class WalletUnconfirmedTransactions { public: - void serialize(cryptonote::ISerializer& s, const std::string& name); + void serialize(CryptoNote::ISerializer& s, const std::string& name); bool findTransactionId(const TransactionHash& hash, TransactionId& id); void erase(const TransactionHash& hash); - void add(const cryptonote::Transaction& tx, TransactionId transactionId, + void add(const CryptoNote::Transaction& tx, TransactionId transactionId, uint64_t amount, const std::list& usedOutputs); void updateTransactionId(const TransactionHash& hash, TransactionId id); diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index b958c4f045..961419ac3d 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,9 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// epee -#include "misc_log_ex.h" - #include "WalletErrors.h" #include "WalletUserTransactionsCache.h" #include "WalletSerialization.h" @@ -30,10 +27,10 @@ namespace CryptoNote { -void WalletUserTransactionsCache::serialize(cryptonote::ISerializer& s, const std::string& name) { +void WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s, const std::string& name) { s.beginObject(name); - if (s.type() == cryptonote::ISerializer::INPUT) { + if (s.type() == CryptoNote::ISerializer::INPUT) { s(m_transactions, "transactions"); s(m_transfers, "transfers"); s(m_unconfirmedTransactions, "unconfirmed"); @@ -88,14 +85,17 @@ TransactionId WalletUserTransactionsCache::addNewTransaction( } void WalletUserTransactionsCache::updateTransaction( - TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::list& usedOutputs) { + TransactionId transactionId, const CryptoNote::Transaction& tx, uint64_t amount, const std::list& usedOutputs) { + // update extra field from created transaction + auto& txInfo = m_transactions.at(transactionId); + txInfo.extra.assign(tx.extra.begin(), tx.extra.end()); m_unconfirmedTransactions.add(tx, transactionId, amount, usedOutputs); } void WalletUserTransactionsCache::updateTransactionSendingState(TransactionId transactionId, std::error_code ec) { auto& txInfo = m_transactions.at(transactionId); if (ec) { - txInfo.state = ec.value() == cryptonote::error::TX_CANCELLED ? TransactionState::Cancelled : TransactionState::Failed; + txInfo.state = ec.value() == error::TX_CANCELLED ? TransactionState::Cancelled : TransactionState::Failed; m_unconfirmedTransactions.erase(txInfo.hash); } else { txInfo.sentTime = time(nullptr); // update sending time diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index edfd2ce40e..259c0b747b 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,7 +24,7 @@ #include "WalletEvent.h" #include "WalletUnconfirmedTransactions.h" -namespace cryptonote { +namespace CryptoNote { class ISerializer; } @@ -35,7 +35,7 @@ class WalletUserTransactionsCache public: WalletUserTransactionsCache() {} - void serialize(cryptonote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer, const std::string& name); uint64_t unconfirmedTransactionsAmount() const; uint64_t unconfrimedOutsAmount() const; @@ -43,7 +43,7 @@ class WalletUserTransactionsCache size_t getTransferCount() const; TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime); - void updateTransaction(TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::list& usedOutputs); + void updateTransaction(TransactionId transactionId, const CryptoNote::Transaction& tx, uint64_t amount, const std::list& usedOutputs); void updateTransactionSendingState(TransactionId transactionId, std::error_code ec); std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); diff --git a/src/wallet/WalletUtils.h b/src/wallet/WalletUtils.h index 5705c9958a..65c9dfb467 100644 --- a/src/wallet/WalletUtils.h +++ b/src/wallet/WalletUtils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,7 +26,7 @@ namespace CryptoNote { -inline void throwIf(bool expr, cryptonote::error::WalletErrorCodes ec) +inline void throwIf(bool expr, CryptoNote::error::WalletErrorCodes ec) { if (expr) throw std::system_error(make_error_code(ec)); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h deleted file mode 100644 index 47b1a5812c..0000000000 --- a/src/wallet/wallet_errors.h +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include - -#include "cryptonote_core/cryptonote_format_utils.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "include_base_utils.h" - - -namespace tools -{ - namespace error - { - // std::exception - // std::runtime_error - // wallet_runtime_error * - // wallet_internal_error - // unexpected_txin_type - // std::logic_error - // wallet_logic_error * - // file_exists - // file_not_found - // file_read_error - // file_save_error - // invalid_password - // refresh_error * - // acc_outs_lookup_error - // block_parse_error - // get_blocks_error - // get_out_indexes_error - // tx_parse_error - // transfer_error * - // get_random_outs_general_error - // not_enough_money - // not_enough_outs_to_mix - // tx_not_constructed - // tx_rejected - // tx_sum_overflow - // tx_too_big - // zero_destination - // wallet_rpc_error * - // daemon_busy - // no_connection_to_daemon - // wallet_files_doesnt_correspond - // - // * - class with protected ctor - - //---------------------------------------------------------------------------------------------------- - template - struct wallet_error_base : public Base - { - // This is necessary to compile with g++ 4.7.3, because of ~std::string() (m_loc) can throw an exception - ~wallet_error_base() throw() { } - - const std::string& location() const { return m_loc; } - - std::string to_string() const - { - std::ostringstream ss; - ss << m_loc << ':' << typeid(*this).name() << ": " << Base::what(); - return ss.str(); - } - - protected: - wallet_error_base(std::string&& loc, const std::string& message) - : Base(message) - , m_loc(loc) - { - } - - private: - std::string m_loc; - }; - //---------------------------------------------------------------------------------------------------- - const char* const failed_rpc_request_messages[] = { - "failed to get blocks", - "failed to get out indices", - "failed to get random outs" - }; - enum failed_rpc_request_message_indices - { - get_blocks_error_message_index, - get_out_indices_error_message_index, - get_random_outs_error_message_index - }; - - template - struct failed_rpc_request : public Base - { - explicit failed_rpc_request(std::string&& loc, const std::string& status) - : Base(std::move(loc), failed_rpc_request_messages[msg_index]) - , m_status(status) - { - } - - ~failed_rpc_request() throw() { } - - const std::string& status() const { return m_status; } - - std::string to_string() const - { - std::ostringstream ss; - ss << Base::to_string() << ", status = " << status(); - return ss.str(); - } - - private: - std::string m_status; - }; - //---------------------------------------------------------------------------------------------------- - typedef wallet_error_base wallet_logic_error; - typedef wallet_error_base wallet_runtime_error; - //---------------------------------------------------------------------------------------------------- - struct wallet_internal_error : public wallet_runtime_error - { - explicit wallet_internal_error(std::string&& loc, const std::string& message) - : wallet_runtime_error(std::move(loc), message) - { - } - }; - //---------------------------------------------------------------------------------------------------- - struct unexpected_txin_type : public wallet_internal_error - { - explicit unexpected_txin_type(std::string&& loc, const cryptonote::Transaction& tx) - : wallet_internal_error(std::move(loc), "one of tx inputs has unexpected type") - , m_tx(tx) - { - } - - ~unexpected_txin_type() throw() { } - - const cryptonote::Transaction& tx() const { return m_tx; } - - std::string to_string() const - { - std::ostringstream ss; - ss << wallet_internal_error::to_string() << ", tx:\n" << cryptonote::obj_to_json_str(m_tx); - return ss.str(); - } - - private: - cryptonote::Transaction m_tx; - }; - //---------------------------------------------------------------------------------------------------- - const char* const file_error_messages[] = { - "file already exists", - "file not found", - "failed to read file", - "failed to save file" - }; - enum file_error_message_indices - { - file_exists_message_index, - file_not_found_message_index, - file_read_error_message_index, - file_save_error_message_index - }; - - template - struct file_error_base : public wallet_logic_error - { - explicit file_error_base(std::string&& loc, const std::string& file) - : wallet_logic_error(std::move(loc), std::string(file_error_messages[msg_index]) + " \"" + file + '\"') - , m_file(file) - { - } - - ~file_error_base() throw() { } - - const std::string& file() const { return m_file; } - - std::string to_string() const { return wallet_logic_error::to_string(); } - - private: - std::string m_file; - }; - //---------------------------------------------------------------------------------------------------- - typedef file_error_base file_exists; - typedef file_error_base file_not_found; - typedef file_error_base file_not_found; - typedef file_error_base file_read_error; - typedef file_error_base file_save_error; - //---------------------------------------------------------------------------------------------------- - struct invalid_password : public wallet_logic_error - { - explicit invalid_password(std::string&& loc) - : wallet_logic_error(std::move(loc), "invalid password") - { - } - - std::string to_string() const { return wallet_logic_error::to_string(); } - }; - //---------------------------------------------------------------------------------------------------- - struct refresh_error : public wallet_logic_error - { - protected: - explicit refresh_error(std::string&& loc, const std::string& message) - : wallet_logic_error(std::move(loc), message) - { - } - }; - //---------------------------------------------------------------------------------------------------- - struct acc_outs_lookup_error : public refresh_error - { - explicit acc_outs_lookup_error(std::string&& loc, const cryptonote::Transaction& tx, - const crypto::public_key& tx_pub_key, const cryptonote::account_keys& acc_keys) - : refresh_error(std::move(loc), "account outs lookup error") - , m_tx(tx) - , m_tx_pub_key(tx_pub_key) - , m_acc_keys(acc_keys) - { - } - - ~acc_outs_lookup_error() throw() { } - - const cryptonote::Transaction& tx() const { return m_tx; } - const crypto::public_key& tx_pub_key() const { return m_tx_pub_key; } - const cryptonote::account_keys& acc_keys() const { return m_acc_keys; } - - std::string to_string() const - { - std::ostringstream ss; - ss << refresh_error::to_string() << ", tx: " << cryptonote::obj_to_json_str(m_tx); - return ss.str(); - } - - private: - const cryptonote::Transaction m_tx; - const crypto::public_key m_tx_pub_key; - const cryptonote::account_keys m_acc_keys; - }; - //---------------------------------------------------------------------------------------------------- - struct block_parse_error : public refresh_error - { - explicit block_parse_error(std::string&& loc, const cryptonote::blobdata& block_data) - : refresh_error(std::move(loc), "block parse error") - , m_block_blob(block_data) - { - } - - ~block_parse_error() throw() { } - - const cryptonote::blobdata& block_blob() const { return m_block_blob; } - - std::string to_string() const { return refresh_error::to_string(); } - - private: - cryptonote::blobdata m_block_blob; - }; - //---------------------------------------------------------------------------------------------------- - typedef failed_rpc_request get_blocks_error; - //---------------------------------------------------------------------------------------------------- - typedef failed_rpc_request get_out_indices_error; - //---------------------------------------------------------------------------------------------------- - struct tx_parse_error : public refresh_error - { - explicit tx_parse_error(std::string&& loc, const cryptonote::blobdata& tx_blob) - : refresh_error(std::move(loc), "transaction parse error") - , m_tx_blob(tx_blob) - { - } - - ~tx_parse_error() throw() { } - - const cryptonote::blobdata& tx_blob() const { return m_tx_blob; } - - std::string to_string() const { return refresh_error::to_string(); } - - private: - cryptonote::blobdata m_tx_blob; - }; - //---------------------------------------------------------------------------------------------------- - struct transfer_error : public wallet_logic_error - { - protected: - explicit transfer_error(std::string&& loc, const std::string& message) - : wallet_logic_error(std::move(loc), message) - { - } - }; - //---------------------------------------------------------------------------------------------------- - typedef failed_rpc_request get_random_outs_error; - //---------------------------------------------------------------------------------------------------- - struct not_enough_money : public transfer_error - { - explicit not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) - : transfer_error(std::move(loc), "not enough money") - , m_available(availbable) - , m_tx_amount(tx_amount) - , m_fee(fee) - { - } - - uint64_t available() const { return m_available; } - uint64_t tx_amount() const { return m_tx_amount; } - uint64_t fee() const { return m_fee; } - - std::string to_string() const - { - std::ostringstream ss; - ss << transfer_error::to_string() << - ", available = " << m_available << - ", tx_amount = " << m_tx_amount << - ", fee = " << m_fee; - return ss.str(); - } - - private: - uint64_t m_available; - uint64_t m_tx_amount; - uint64_t m_fee; - }; - //---------------------------------------------------------------------------------------------------- - struct not_enough_outs_to_mix : public transfer_error - { - typedef std::vector scanty_outs_t; - - explicit not_enough_outs_to_mix(std::string&& loc, const scanty_outs_t& scanty_outs, size_t mixin_count) - : transfer_error(std::move(loc), "not enough outputs to mix") - , m_scanty_outs(scanty_outs) - , m_mixin_count(mixin_count) - { - } - - ~not_enough_outs_to_mix() throw() { } - - const scanty_outs_t& scanty_outs() const { return m_scanty_outs; } - size_t mixin_count() const { return m_mixin_count; } - - std::string to_string() const - { - std::ostringstream ss; - ss << transfer_error::to_string() << ", mixin_count = " << m_mixin_count << ", scanty_outs:"; - for (const auto& outs_for_amount : m_scanty_outs) - { - ss << '\n' << outs_for_amount.amount << " - " << outs_for_amount.outs.size(); - } - return ss.str(); - } - - private: - scanty_outs_t m_scanty_outs; - size_t m_mixin_count; - }; - //---------------------------------------------------------------------------------------------------- - struct tx_not_constructed : public transfer_error - { - typedef std::vector sources_t; - typedef std::vector destinations_t; - - explicit tx_not_constructed(std::string&& loc, uint64_t addressPrefix, const sources_t& sources, - const destinations_t& destinations, uint64_t unlock_time) - : transfer_error(std::move(loc), "transaction was not constructed") - , m_addressPrefix(addressPrefix) - , m_sources(sources) - , m_destinations(destinations) - , m_unlock_time(unlock_time) - { - } - - ~tx_not_constructed() throw() { } - - const sources_t& sources() const { return m_sources; } - const destinations_t& destinations() const { return m_destinations; } - uint64_t unlock_time() const { return m_unlock_time; } - - std::string to_string() const - { - std::ostringstream ss; - ss << transfer_error::to_string(); - ss << "\nSources:"; - for (size_t i = 0; i < m_sources.size(); ++i) - { - const cryptonote::tx_source_entry& src = m_sources[i]; - ss << "\n source " << i << ":"; - ss << "\n amount: " << src.amount; - // It's not good, if logs will contain such much data - //ss << "\n real_output: " << src.real_output; - //ss << "\n real_output_in_tx_index: " << src.real_output_in_tx_index; - //ss << "\n real_out_tx_key: " << epee::string_tools::pod_to_hex(src.real_out_tx_key); - //ss << "\n outputs:"; - //for (size_t j = 0; j < src.outputs.size(); ++j) - //{ - // const cryptonote::tx_source_entry::output_entry& out = src.outputs[j]; - // ss << "\n " << j << ": " << out.first << ", " << epee::string_tools::pod_to_hex(out.second); - //} - } - - ss << "\nDestinations:"; - for (size_t i = 0; i < m_destinations.size(); ++i) - { - const cryptonote::tx_destination_entry& dst = m_destinations[i]; - ss << "\n " << i << ": " << getAccountAddressAsStr(m_addressPrefix, dst.addr) << ' ' << dst.amount; - } - - ss << "\nunlock_time: " << m_unlock_time; - - return ss.str(); - } - - private: - uint64_t m_addressPrefix; - sources_t m_sources; - destinations_t m_destinations; - uint64_t m_unlock_time; - }; - //---------------------------------------------------------------------------------------------------- - struct tx_rejected : public transfer_error - { - explicit tx_rejected(std::string&& loc, const cryptonote::Transaction& tx, const std::string& status) - : transfer_error(std::move(loc), "transaction was rejected by daemon") - , m_tx(tx) - , m_status(status) - { - } - - ~tx_rejected() throw() { } - - const cryptonote::Transaction& tx() const { return m_tx; } - const std::string& status() const { return m_status; } - - std::string to_string() const - { - std::ostringstream ss; - ss << transfer_error::to_string() << ", status = " << m_status << ", tx:\n"; - ss << cryptonote::obj_to_json_str(m_tx); - return ss.str(); - } - - private: - cryptonote::Transaction m_tx; - std::string m_status; - }; - //---------------------------------------------------------------------------------------------------- - struct tx_sum_overflow : public transfer_error - { - typedef std::vector destinations_t; - - explicit tx_sum_overflow(std::string&& loc, uint64_t addressPrefix, const destinations_t& destinations, uint64_t fee) - : transfer_error(std::move(loc), "transaction sum + fee exceeds " + std::to_string(std::numeric_limits::max())) - , m_addressPrefix(addressPrefix) - , m_destinations(destinations) - , m_fee(fee) - { - } - - ~tx_sum_overflow() throw() { } - - const std::vector& destinations() const { return m_destinations; } - uint64_t fee() const { return m_fee; } - - std::string to_string() const - { - std::ostringstream ss; - ss << transfer_error::to_string() << - ", fee = " << m_fee << - ", destinations:"; - for (const auto& dst : m_destinations) - { - ss << '\n' << dst.amount << " -> " << getAccountAddressAsStr(m_addressPrefix, dst.addr); - } - return ss.str(); - } - - private: - uint64_t m_addressPrefix; - destinations_t m_destinations; - uint64_t m_fee; - }; - //---------------------------------------------------------------------------------------------------- - struct tx_too_big : public transfer_error - { - explicit tx_too_big(std::string&& loc, const cryptonote::Transaction& tx, uint64_t tx_size_limit) - : transfer_error(std::move(loc), "transaction is too big") - , m_tx(tx) - , m_tx_size_limit(tx_size_limit) - { - } - - ~tx_too_big() throw() { } - - const cryptonote::Transaction& tx() const { return m_tx; } - uint64_t tx_size_limit() const { return m_tx_size_limit; } - - std::string to_string() const - { - std::ostringstream ss; - ss << transfer_error::to_string() << - ", tx_size_limit = " << m_tx_size_limit << - ", tx size = " << get_object_blobsize(m_tx) << - ", tx:\n" << cryptonote::obj_to_json_str(m_tx); - return ss.str(); - } - - private: - cryptonote::Transaction m_tx; - uint64_t m_tx_size_limit; - }; - //---------------------------------------------------------------------------------------------------- - struct zero_destination : public transfer_error - { - explicit zero_destination(std::string&& loc) - : transfer_error(std::move(loc), "destination amount is zero") - { - } - }; - //---------------------------------------------------------------------------------------------------- - struct wallet_rpc_error : public wallet_logic_error - { - ~wallet_rpc_error() throw() { } - - const std::string& request() const { return m_request; } - - std::string to_string() const - { - std::ostringstream ss; - ss << wallet_logic_error::to_string() << ", request = " << m_request; - return ss.str(); - } - - protected: - explicit wallet_rpc_error(std::string&& loc, const std::string& message, const std::string& request) - : wallet_logic_error(std::move(loc), message) - , m_request(request) - { - } - - private: - std::string m_request; - }; - //---------------------------------------------------------------------------------------------------- - struct daemon_busy : public wallet_rpc_error - { - explicit daemon_busy(std::string&& loc, const std::string& request) - : wallet_rpc_error(std::move(loc), "daemon is busy", request) - { - } - }; - //---------------------------------------------------------------------------------------------------- - struct no_connection_to_daemon : public wallet_rpc_error - { - explicit no_connection_to_daemon(std::string&& loc, const std::string& request) - : wallet_rpc_error(std::move(loc), "no connection to daemon", request) - { - } - }; - //---------------------------------------------------------------------------------------------------- - struct wallet_files_doesnt_correspond : public wallet_logic_error - { - explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file) - : wallet_logic_error(std::move(loc), "file " + wallet_file + " does not correspond to " + keys_file) - { - } - - ~wallet_files_doesnt_correspond() throw() { } - - const std::string& keys_file() const { return m_keys_file; } - const std::string& wallet_file() const { return m_wallet_file; } - - std::string to_string() const { return wallet_logic_error::to_string(); } - - private: - std::string m_keys_file; - std::string m_wallet_file; - }; - //---------------------------------------------------------------------------------------------------- - -#if !defined(_MSC_VER) - - template - void throw_wallet_ex(std::string&& loc, const TArgs&... args) - { - TException e(std::move(loc), args...); - LOG_PRINT_L0(e.to_string()); - throw e; - } - -#else - #include - #include - #include - - template - void throw_wallet_ex(std::string&& loc) - { - TException e(std::move(loc)); - LOG_PRINT_L0(e.to_string()); - throw e; - } - -#define GEN_throw_wallet_ex(z, n, data) \ - template \ - void throw_wallet_ex(std::string&& loc, BOOST_PP_ENUM_BINARY_PARAMS(n, const TArg, &arg)) \ - { \ - TException e(std::move(loc), BOOST_PP_ENUM_PARAMS(n, arg)); \ - LOG_PRINT_L0(e.to_string()); \ - throw e; \ - } - - BOOST_PP_REPEAT_FROM_TO(1, 6, GEN_throw_wallet_ex, ~) -#endif - } -} - -#define STRINGIZE_DETAIL(x) #x -#define STRINGIZE(x) STRINGIZE_DETAIL(x) - -#define THROW_WALLET_EXCEPTION_IF(cond, err_type, ...) \ - if (cond) \ - { \ - LOG_ERROR(#cond << ". THROW EXCEPTION: " << #err_type); \ - tools::error::throw_wallet_ex(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ## __VA_ARGS__); \ - } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 3af0b8ad45..bf03f2a3e8 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,25 +15,26 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "include_base_utils.h" -using namespace epee; - #include "wallet_rpc_server.h" -#include "common/command_line.h" + +#include + +#include "Common/command_line.h" +#include "Common/StringTools.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/account.h" -#include "misc_language.h" -#include "string_tools.h" #include "crypto/hash.h" #include "WalletHelper.h" -#include "wallet_errors.h" +// #include "wallet_errors.h" +#include "rpc/JsonRpc.h" +using namespace Logging; using namespace CryptoNote; -using namespace cryptonote; + namespace tools { -//----------------------------------------------------------------------------------- -const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", "", true }; + +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", 0, true }; const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = { "rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1" }; void wallet_rpc_server::init_options(boost::program_options::options_description& desc) { @@ -41,19 +42,39 @@ void wallet_rpc_server::init_options(boost::program_options::options_description command_line::add_arg(desc, arg_rpc_bind_port); } //------------------------------------------------------------------------------------------------------------------------------ -wallet_rpc_server::wallet_rpc_server(CryptoNote::IWallet&w, CryptoNote::INode& n, cryptonote::Currency& currency, const std::string& walletFile) :m_wallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile), m_saveResultPromise(nullptr) { +wallet_rpc_server::wallet_rpc_server( + System::Dispatcher& dispatcher, + Logging::ILogger& log, + CryptoNote::IWallet&w, + CryptoNote::INode& n, + CryptoNote::Currency& currency, + const std::string& walletFile) + : + HttpServer(dispatcher, log), + logger(log, "WalletRpc"), + m_dispatcher(dispatcher), + m_stopComplete(dispatcher), + m_wallet(w), + m_syncWallet(w), + m_node(n), + m_currency(currency), + m_walletFilename(walletFile) { } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { - //DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING - return epee::http_server_impl_base::run(1, true); + start(m_bind_ip, m_port); + m_stopComplete.wait(); + return true; } -//---------------------------------------------------------------------------------------------------- -void wallet_rpc_server::saveCompleted(std::error_code result) { - if (m_saveResultPromise.get() != nullptr) { - m_saveResultPromise->set_value(result); - } + +void wallet_rpc_server::send_stop_signal() { + m_dispatcher.remoteSpawn([this] { + std::cout << "wallet_rpc_server::send_stop_signal()" << std::endl; + stop(); + m_stopComplete.set(); + }); } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); @@ -62,25 +83,59 @@ bool wallet_rpc_server::handle_command_line(const boost::program_options::variab } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) { - m_net_server.set_threads_prefix("RPC"); - bool r = handle_command_line(vm); - CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); - return epee::http_server_impl_base::init(m_port, m_bind_ip); + if (!handle_command_line(vm)) { + logger(ERROR) << "Failed to process command line in wallet_rpc_server"; + return false; + } + + return true; } -//------------------------------------------------------------------------------------------------------------------------------ -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, connection_context& cntx) { + +void wallet_rpc_server::processRequest(const CryptoNote::HttpRequest& request, CryptoNote::HttpResponse& response) { + + using namespace CryptoNote::JsonRpc; + + JsonRpcRequest jsonRequest; + JsonRpcResponse jsonResponse; + try { - res.balance = m_wallet.pendingBalance(); - res.unlocked_balance = m_wallet.pendingBalance(); - } catch (std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); - return false; + jsonRequest.parseRequest(request.getBody()); + jsonResponse.setId(jsonRequest.getId()); + + static std::unordered_map s_methods = { + { "getbalance", makeMemberMethod(&wallet_rpc_server::on_getbalance) }, + { "transfer", makeMemberMethod(&wallet_rpc_server::on_transfer) }, + { "store", makeMemberMethod(&wallet_rpc_server::on_store) }, + { "get_payments", makeMemberMethod(&wallet_rpc_server::on_get_payments) }, + { "get_transfers", makeMemberMethod(&wallet_rpc_server::on_get_transfers) }, + { "get_height", makeMemberMethod(&wallet_rpc_server::on_get_height) }, + { "reset", makeMemberMethod(&wallet_rpc_server::on_reset) } + }; + + auto it = s_methods.find(jsonRequest.getMethod()); + if (it == s_methods.end()) { + throw JsonRpcError(errMethodNotFound); + } + + it->second(this, jsonRequest, jsonResponse); + + } catch (const JsonRpcError& err) { + jsonResponse.setError(err); + } catch (const std::exception& e) { + jsonResponse.setError(JsonRpcError(WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR, e.what())); } + + response.setBody(jsonResponse.getBody()); +} + +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res) { + res.locked_amount = m_wallet.pendingBalance(); + res.available_balance = m_wallet.actualBalance(); return true; } //------------------------------------------------------------------------------------------------------------------------------ -bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) { +bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res) { std::vector transfers; for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { CryptoNote::Transfer transfer; @@ -94,102 +149,67 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ std::string payment_id_str = req.payment_id; crypto::hash payment_id; - if (!cryptonote::parsePaymentId(payment_id_str, payment_id)) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"; - return false; + if (!CryptoNote::parsePaymentId(payment_id_str, payment_id)) { + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, + "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"); } std::string extra_nonce; - cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"; - return false; + CryptoNote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); + if (!CryptoNote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, + "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"); } } std::string extraString; std::copy(extra.begin(), extra.end(), std::back_inserter(extraString)); try { - cryptonote::WalletHelper::SendCompleteResultObserver sent; - std::promise txId; - sent.expectedTxID = txId.get_future(); - std::future f_sendError = sent.sendResult.get_future(); + CryptoNote::WalletHelper::SendCompleteResultObserver sent; + WalletHelper::IWalletRemoveObserverGuard removeGuard(m_wallet, sent); - m_wallet.addObserver(&sent); CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time); - txId.set_value(tx); - std::error_code sendError = f_sendError.get(); - m_wallet.removeObserver(&sent); + if (tx == INVALID_TRANSACTION_ID) { + throw std::runtime_error("Couldn't send transaction"); + } + + std::error_code sendError = sent.wait(tx); + removeGuard.removeObserver(); + if (sendError) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = sendError.message(); - return false; + throw std::system_error(sendError); } CryptoNote::TransactionInfo txInfo; m_wallet.getTransaction(tx, txInfo); + res.tx_hash = Common::podToHex(txInfo.hash); - std::string hexHash; - std::copy(txInfo.hash.begin(), txInfo.hash.end(), std::back_inserter(hexHash)); - res.tx_hash = epee::string_tools::buff_to_hex_nodelimer(hexHash); - return true; - } catch (const tools::error::daemon_busy& e) { - er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; - er.message = e.what(); - return false; } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; - er.message = e.what(); - return false; - } catch (...) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; - return false; + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR, e.what()); } return true; } //------------------------------------------------------------------------------------------------------------------------------ -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, connection_context& cntx) { +bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res) { try { - std::ofstream walletFile; - walletFile.open(m_walletFilename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_wallet.addObserver(this); - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet.save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = saveError.message(); - return false; - } - m_wallet.removeObserver(this); + WalletHelper::storeWallet(m_wallet, m_walletFilename); } catch (std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); - return false; + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR, std::string("Couldn't save wallet: ") + e.what()); } + return true; } //------------------------------------------------------------------------------------------------------------------------------ -bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) { +bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res) { crypto::hash expectedPaymentId; - cryptonote::blobdata payment_id_blob; - if (!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob)) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment ID has invald format"; - return false; + CryptoNote::blobdata payment_id_blob; + + if (!hexToBlob(req.payment_id, payment_id_blob)) { + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment ID has invald format"); } if (sizeof(expectedPaymentId) != payment_id_blob.size()) { - er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; - er.message = "Payment ID has invalid size"; - return false; + throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment ID has invalid size"); } expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); @@ -209,9 +229,9 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN crypto::hash paymentId; if (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { wallet_rpc::payment_details rpc_payment; - rpc_payment.tx_hash = epee::string_tools::pod_to_hex(txInfo.hash); + rpc_payment.tx_hash = Common::podToHex(txInfo.hash); rpc_payment.amount = txInfo.totalAmount; - rpc_payment.block_height = txInfo.totalAmount; + rpc_payment.block_height = txInfo.blockHeight; rpc_payment.unlock_time = txInfo.unlockTime; res.payments.push_back(rpc_payment); } @@ -220,7 +240,7 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN return true; } -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, connection_context& cntx) { +bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res) { res.transfers.clear(); size_t transactionsCount = m_wallet.getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { @@ -242,8 +262,8 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS wallet_rpc::Transfer transfer; transfer.time = txInfo.timestamp; transfer.output = txInfo.totalAmount < 0; - transfer.transactionHash = epee::string_tools::pod_to_hex(txInfo.hash); - transfer.amount = txInfo.totalAmount; + transfer.transactionHash = Common::podToHex(txInfo.hash); + transfer.amount = std::abs(txInfo.totalAmount); transfer.fee = txInfo.fee; transfer.address = address; transfer.blockIndex = txInfo.blockHeight; @@ -255,7 +275,7 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); crypto::hash paymentId; - transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? epee::string_tools::pod_to_hex(paymentId) : ""); + transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? Common::podToHex(paymentId) : ""); res.transfers.push_back(transfer); } @@ -263,12 +283,12 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS return true; } -bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx) { +bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res) { res.height = m_node.getLastLocalBlockHeight(); return true; } -bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) { +bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res) { m_wallet.reset(); return true; } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 3011732677..26b80f236f 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,66 +20,66 @@ #include #include #include -#include "net/http_server_impl_base.h" #include "wallet_rpc_server_commans_defs.h" #include "Wallet.h" -#include "common/command_line.h" +#include "SyncWallet.h" +#include "Common/command_line.h" +#include "rpc/HttpServer.h" + +#include + namespace tools { /************************************************************************/ /* */ /************************************************************************/ - class wallet_rpc_server: public epee::http_server_impl_base, public CryptoNote::IWalletObserver + class wallet_rpc_server : CryptoNote::HttpServer { public: - typedef epee::net_utils::connection_context_base connection_context; - - wallet_rpc_server(CryptoNote::IWallet &w, CryptoNote::INode &n, cryptonote::Currency& currency, const std::string& walletFilename); - const static command_line::arg_descriptor arg_rpc_bind_port; - const static command_line::arg_descriptor arg_rpc_bind_ip; + wallet_rpc_server( + System::Dispatcher& dispatcher, + Logging::ILogger& log, + CryptoNote::IWallet &w, + CryptoNote::INode &n, + CryptoNote::Currency& currency, + const std::string& walletFilename); - //---------------- IWalletObserver ------------------------- - virtual void saveCompleted(std::error_code result) override; - //---------------------------------------------------------- static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); + bool run(); - private: + void send_stop_signal(); + + static const command_line::arg_descriptor arg_rpc_bind_port; + static const command_line::arg_descriptor arg_rpc_bind_ip; - CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map + private: - BEGIN_URI_MAP2() - BEGIN_JSON_RPC_MAP("/json_rpc") - MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) - MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) - MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) - MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) - MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS) - MAP_JON_RPC_WE("get_height", on_get_height, wallet_rpc::COMMAND_RPC_GET_HEIGHT) - MAP_JON_RPC_WE("reset", on_reset, wallet_rpc::COMMAND_RPC_RESET) - END_JSON_RPC_MAP() - END_URI_MAP2() + virtual void processRequest(const CryptoNote::HttpRequest& request, CryptoNote::HttpResponse& response) override; - //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, connection_context& cntx); - bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool 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, connection_context& cntx); - bool on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er, connection_context& cntx); - bool on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx); + //json_rpc + bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res); + bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res); + bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res); + bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res); + bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res); + bool on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res); + bool on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res); - bool handle_command_line(const boost::program_options::variables_map& vm); + bool handle_command_line(const boost::program_options::variables_map& vm); - CryptoNote::IWallet& m_wallet; - CryptoNote::INode& m_node; - std::string m_port; - std::string m_bind_ip; - cryptonote::Currency& m_currency; - const std::string m_walletFilename; + Logging::LoggerRef logger; + CryptoNote::IWallet& m_wallet; + CryptoNote::SyncWallet m_syncWallet; + CryptoNote::INode& m_node; + uint16_t m_port; + std::string m_bind_ip; + CryptoNote::Currency& m_currency; + const std::string m_walletFilename; - std::unique_ptr> m_saveResultPromise; + System::Dispatcher& m_dispatcher; + System::Event m_stopComplete; }; } diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index 8ef30557c7..d8144d209f 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -37,12 +37,12 @@ namespace wallet_rpc struct response { - uint64_t balance; - uint64_t unlocked_balance; + uint64_t locked_amount; + uint64_t available_balance; BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(balance) - KV_SERIALIZE(unlocked_balance) + KV_SERIALIZE(locked_amount) + KV_SERIALIZE(available_balance) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index 0f2f9ab27e..8edf4b8d97 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt old mode 100755 new mode 100644 index fb56e3ba3e..efa35703c1 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,85 +1,100 @@ add_definitions(-DSTATICLIB) -add_subdirectory(gtest) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR} ../version) -file(GLOB_RECURSE CORE_TESTS core_tests/*) -file(GLOB_RECURSE CRYPTO_TESTS crypto/*) -file(GLOB_RECURSE PERFORMANCE_TESTS performance_tests/*) -file(GLOB_RECURSE CORE_PROXY core_proxy/*) -file(GLOB_RECURSE TEST_GENERATOR TestGenerator/*) -file(GLOB_RECURSE UNIT_TESTS unit_tests/*) -file(GLOB_RECURSE INTEGRATION_TEST_LIB integration_test_lib/*) -file(GLOB_RECURSE INTEGRATION_TESTS integration_tests/*) -file(GLOB_RECURSE TRANSFERS_TESTS transfers_tests/*) - - - -source_group(core_tests FILES ${CORE_TESTS}) -source_group(crypto_tests FILES ${CRYPTO_TESTS}) -source_group(performance_tests FILES ${PERFORMANCE_TESTS}) -source_group(core_proxy FILES ${CORE_PROXY}) -source_group(TestGenerator FILES ${TEST_GENERATOR}) -source_group(unit_tests FILES ${UNIT_TESTS}) -source_group(integration_test_lib FILES ${INTEGRATION_TEST_LIB}) -source_group(integration_tests FILES ${INTEGRATION_TESTS}) -source_group(transfers_tests FILES ${TRANSFERS_TESTS}) - - -# add_subdirectory(daemon_tests) - -add_library(TestGenerator ${TEST_GENERATOR}) -add_library(integration_test_lib ${INTEGRATION_TEST_LIB}) - - -add_executable(coretests ${CORE_TESTS}) -add_executable(crypto-tests ${CRYPTO_TESTS}) -add_executable(difficulty-tests difficulty/difficulty.cpp) -add_executable(hash-tests hash/main.cpp) -add_executable(hash-target-tests hash-target.cpp) -add_executable(performance_tests ${PERFORMANCE_TESTS}) -add_executable(core_proxy ${CORE_PROXY} ../src/p2p/NetNodeConfig.cpp) -add_executable(unit_tests ${UNIT_TESTS}) -add_executable(net_load_tests_clt net_load_tests/clt.cpp) -add_executable(net_load_tests_srv net_load_tests/srv.cpp) -add_executable(integration_tests ${INTEGRATION_TESTS} ../src/p2p/NetNodeConfig.cpp) -add_executable(transfers_tests ${TRANSFERS_TESTS} ../src/p2p/NetNodeConfig.cpp ../src/cryptonote_core/MinerConfig.cpp ../src/cryptonote_core/CoreConfig.cpp) - - -target_link_libraries(core_proxy epee cryptonote_core common crypto upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(coretests epee cryptonote_core common crypto TestGenerator ${Boost_LIBRARIES}) -target_link_libraries(difficulty-tests epee cryptonote_core common crypto ${Boost_LIBRARIES}) -target_link_libraries(hash-tests crypto) -target_link_libraries(hash-target-tests epee crypto cryptonote_core) -target_link_libraries(performance_tests epee cryptonote_core common crypto ${Boost_LIBRARIES}) -target_link_libraries(unit_tests epee wallet TestGenerator cryptonote_core common crypto gtest_main transfers serialization inprocess_node ${Boost_LIBRARIES}) -target_link_libraries(net_load_tests_clt epee cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) -target_link_libraries(net_load_tests_srv epee cryptonote_core common crypto gtest_main ${Boost_LIBRARIES}) -target_link_libraries(integration_tests integration_test_lib epee wallet node_rpc_proxy rpc transfers cryptonote_core crypto common upnpc-static serialization System inprocess_node ${Boost_LIBRARIES}) -target_link_libraries(transfers_tests integration_test_lib epee node_rpc_proxy rpc upnpc-static transfers System gtest_main inprocess_node wallet serialization cryptonote_core crypto common ${Boost_LIBRARIES}) - - - -file(GLOB_RECURSE NODE_RPC_PROXY_TEST node_rpc_proxy_test/*) -source_group(node_rpc_proxy_test FILES ${NODE_RPC_PROXY_TEST}) -add_executable(node_rpc_proxy_test ${NODE_RPC_PROXY_TEST}) -target_link_libraries(node_rpc_proxy_test epee rpc node_rpc_proxy cryptonote_core common crypto serialization ${Boost_LIBRARIES}) +file(GLOB_RECURSE CoreTests core_tests/*) +file(GLOB_RECURSE CryptoTests crypto/*) +file(GLOB_RECURSE FunctionalTests functional_tests/*) +file(GLOB_RECURSE IntegrationTestLibrary integration_test_lib/*) +file(GLOB_RECURSE IntegrationTests integration_tests/*) +file(GLOB_RECURSE NodeRpcProxyTests node_rpc_proxy_test/*) +file(GLOB_RECURSE PerformanceTests performance_tests/*) +file(GLOB_RECURSE SystemTests System/*) +file(GLOB_RECURSE TestGenerator TestGenerator/*) +file(GLOB_RECURSE TransfersTests transfers_tests/*) +file(GLOB_RECURSE UnitTests unit_tests/*) + +source_group("" FILES ${CoreTests} ${CryptoTests} ${FunctionalTests} ${IntegrationTestLibrary} ${IntegrationTests} ${NodeRpcProxyTests} ${PerformanceTests} ${SystemTests} ${TestGenerator} ${TransfersTests} ${UnitTests}) +source_group("" FILES ${CryptoNoteProtocol} ${P2p}) + +add_library(IntegrationTestLibrary ${IntegrationTestLibrary}) +add_library(TestGenerator ${TestGenerator}) + +add_executable(CoreTests ${CoreTests}) +add_executable(CryptoTests ${CryptoTests}) +add_executable(IntegrationTests ${IntegrationTests}) +add_executable(NodeRpcProxyTests ${NodeRpcProxyTests}) +add_executable(PerformanceTests ${PerformanceTests}) +add_executable(SystemTests ${SystemTests}) +add_executable(TransfersTests ${TransfersTests}) +add_executable(UnitTests ${UnitTests}) + +add_executable(DifficultyTests difficulty/difficulty.cpp) +add_executable(HashTargetTests hash-target.cpp) +add_executable(HashTests hash/main.cpp) + +target_link_libraries(CoreTests epee TestGenerator CryptoNoteCore Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(IntegrationTests epee IntegrationTestLibrary Wallet NodeRpcProxy InProcessNode P2P Rpc Http Transfers Serialization System CryptoNoteCore Logging Common Crypto gtest upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(NodeRpcProxyTests epee NodeRpcProxy CryptoNoteCore Rpc Http Serialization System Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(PerformanceTests epee CryptoNoteCore Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(SystemTests System gtest_main) +if (MSVC) + target_link_libraries(SystemTests ws2_32) +endif () + +target_link_libraries(TransfersTests IntegrationTestLibrary Wallet epee gtest_main CryptoNoteCore InProcessNode NodeRpcProxy P2P Rpc Http Serialization System Transfers Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(UnitTests epee gtest_main Wallet TestGenerator CryptoNoteCore InProcessNode Transfers Serialization System Logging Common Crypto ${Boost_LIBRARIES}) + +target_link_libraries(DifficultyTests epee CryptoNoteCore Crypto Logging Common ${Boost_LIBRARIES}) +target_link_libraries(HashTargetTests epee CryptoNoteCore Crypto) +target_link_libraries(HashTests Crypto) if(NOT MSVC) - set_property(TARGET gtest gtest_main unit_tests net_load_tests_clt net_load_tests_srv TestGenerator integration_test_lib integration_tests APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") + set_property(TARGET gtest gtest_main IntegrationTestLibrary IntegrationTests TestGenerator UnitTests APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-sign-compare") endif() -add_custom_target(tests DEPENDS coretests difficulty hash performance_tests core_proxy unit_tests node_rpc_proxy_test integration_tests transfers_tests) -set_property(TARGET coretests crypto-tests difficulty-tests gtest gtest_main hash-tests hash-target-tests performance_tests core_proxy unit_tests tests net_load_tests_clt net_load_tests_srv node_rpc_proxy_test TestGenerator integration_test_lib integration_tests PROPERTY FOLDER "tests") -set_property(TARGET transfers_tests PROPERTY FOLDER "tests") - -add_dependencies(core_proxy version) - -add_test(coretests coretests --generate_and_play_test_data) -add_test(crypto crypto-tests ${CMAKE_CURRENT_SOURCE_DIR}/crypto/tests.txt) -add_test(difficulty difficulty-tests ${CMAKE_CURRENT_SOURCE_DIR}/difficulty/data.txt) +add_custom_target(tests DEPENDS CoreTests IntegrationTests NodeRpcProxyTests PerformanceTests SystemTests TransfersTests UnitTests DifficultyTests HashTargetTests) + +set_property(TARGET + tests + + IntegrationTestLibrary + TestGenerator + + CoreTests + CryptoTests + IntegrationTests + NodeRpcProxyTests + PerformanceTests + SystemTests + TransfersTests + UnitTests + + DifficultyTests + HashTargetTests + HashTests +PROPERTY FOLDER "tests") + +add_dependencies(IntegrationTestLibrary version) + +set_property(TARGET CoreTests PROPERTY OUTPUT_NAME "core_tests") +set_property(TARGET CryptoTests PROPERTY OUTPUT_NAME "crypto_tests") +set_property(TARGET IntegrationTests PROPERTY OUTPUT_NAME "integration_tests") +set_property(TARGET NodeRpcProxyTests PROPERTY OUTPUT_NAME "node_rpc_proxy_tests") +set_property(TARGET PerformanceTests PROPERTY OUTPUT_NAME "performance_tests") +set_property(TARGET SystemTests PROPERTY OUTPUT_NAME "system_tests") +set_property(TARGET TransfersTests PROPERTY OUTPUT_NAME "transfers_tests") +set_property(TARGET UnitTests PROPERTY OUTPUT_NAME "unit_tests") +set_property(TARGET DifficultyTests PROPERTY OUTPUT_NAME "difficulty_tests") +set_property(TARGET HashTargetTests PROPERTY OUTPUT_NAME "hash_target_tests") +set_property(TARGET HashTests PROPERTY OUTPUT_NAME "hash_tests") + +add_test(CoreTests core_tests --generate_and_play_test_data) +add_test(CryptoTests crypto_tests ${CMAKE_CURRENT_SOURCE_DIR}/crypto/tests.txt) +add_test(DifficultyTests difficulty_tests ${CMAKE_CURRENT_SOURCE_DIR}/difficulty/data.txt) foreach(hash IN ITEMS fast slow tree extra-blake extra-groestl extra-jh extra-skein) - add_test(hash-${hash} hash-tests ${hash} ${CMAKE_CURRENT_SOURCE_DIR}/hash/tests-${hash}.txt) + add_test(hash-${hash} hash_tests ${hash} ${CMAKE_CURRENT_SOURCE_DIR}/hash/tests-${hash}.txt) endforeach(hash) -add_test(hash-target hash-target-tests) -add_test(unit_tests unit_tests) +add_test(HashTargetTests hash_target_tests) +add_test(SystemTests system_tests) +add_test(UnitTests unit_tests) diff --git a/tests/System/DispatcherTests.cpp b/tests/System/DispatcherTests.cpp new file mode 100755 index 0000000000..3614d297da --- /dev/null +++ b/tests/System/DispatcherTests.cpp @@ -0,0 +1,405 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include + +using namespace System; + +TEST(DispatcherTests, clearRemainsDispatcherWorkable) { + Dispatcher dispatcher; + dispatcher.clear(); + bool spawnDone = false; + dispatcher.spawn([&]() { + spawnDone = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawnDone); +} + +TEST(DispatcherTests, clearRemainsDispatcherWorkableAfterAsyncOperation) { + Dispatcher dispatcher; + bool spawn1Done = false; + bool spawn2Done = false; + dispatcher.spawn([&]() { + spawn1Done = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawn1Done); + dispatcher.clear(); + dispatcher.spawn([&]() { + spawn2Done = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawn2Done); +} + +TEST(DispatcherTests, clearCalledFromSpawnRemainsDispatcherWorkable) { + Dispatcher dispatcher; + bool spawn1Done = false; + bool spawn2Done = false; + dispatcher.spawn([&]() { + dispatcher.clear(); + spawn1Done = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawn1Done); + dispatcher.spawn([&]() { + spawn2Done = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawn2Done); +} + +TEST(DispatcherTests, timerIsHandledOnlyAfterAllSpawnedTasksAreHandled) { + Dispatcher dispatcher; + Event event1(dispatcher); + Event event2(dispatcher); + dispatcher.spawn([&]() { + event1.set(); + Timer(dispatcher).sleep(std::chrono::milliseconds(1)); + event2.set(); + }); + + dispatcher.yield(); + ASSERT_TRUE(event1.get()); + ASSERT_FALSE(event2.get()); + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + dispatcher.pushContext(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + ASSERT_FALSE(event2.get()); + dispatcher.yield(); + ASSERT_TRUE(event2.get()); +} + +TEST(DispatcherTests, dispatchKeepsSpawnOrder) { + Dispatcher dispatcher; + std::deque executionOrder; + std::deque expectedOrder = { 1, 2 }; + dispatcher.spawn([&]() { + executionOrder.push_back(1); + }); + + dispatcher.spawn([&]() { + executionOrder.push_back(2); + }); + + dispatcher.pushContext(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + ASSERT_EQ(executionOrder, expectedOrder); +} + +TEST(DispatcherTests, dispatchKeepsSpawnOrderWithNesting) { + Dispatcher dispatcher; + std::deque executionOrder; + std::deque expectedOrder = { 1, 2, 3, 4 }; + auto mainContext = dispatcher.getCurrentContext(); + dispatcher.spawn([&]() { + executionOrder.push_back(1); + dispatcher.spawn([&]() { + executionOrder.push_back(3); + }); + }); + + dispatcher.spawn([&]() { + executionOrder.push_back(2); + dispatcher.spawn([&]() { + executionOrder.push_back(4); + dispatcher.pushContext(mainContext); + }); + }); + + dispatcher.dispatch(); + ASSERT_EQ(executionOrder, expectedOrder); +} + +TEST(DispatcherTests, dispatchKeepsSpawnResumingOrder) { + Dispatcher dispatcher; + std::deque executionOrder; + std::deque expectedOrder = { 1, 2, 3, 4 }; + std::vector contexts; + dispatcher.spawn([&]() { + executionOrder.push_back(1); + contexts.push_back(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + executionOrder.push_back(3); + }); + + dispatcher.spawn([&]() { + executionOrder.push_back(2); + contexts.push_back(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + executionOrder.push_back(4); + }); + + dispatcher.pushContext(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + for (auto& ctx : contexts) { + dispatcher.pushContext(ctx); + } + + dispatcher.pushContext(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + ASSERT_EQ(executionOrder, expectedOrder); +} + +TEST(DispatcherTests, getCurrentContextDiffersForParallelSpawn) { + Dispatcher dispatcher; + void* ctx1 = nullptr; + void* ctx2 = nullptr; + dispatcher.spawn([&]() { + ctx1 = dispatcher.getCurrentContext(); + }); + + dispatcher.spawn([&]() { + ctx2 = dispatcher.getCurrentContext(); + }); + + dispatcher.yield(); + ASSERT_NE(ctx1, nullptr); + ASSERT_NE(ctx2, nullptr); + ASSERT_NE(ctx1, ctx2); +} + +TEST(DispatcherTests, getCurrentContextSameForSequentialSpawn) { + Dispatcher dispatcher; + void* ctx1 = nullptr; + void* ctx2 = nullptr; + dispatcher.spawn([&]() { + ctx1 = dispatcher.getCurrentContext(); + dispatcher.yield(); + ctx2 = dispatcher.getCurrentContext(); + }); + + dispatcher.yield(); + dispatcher.yield(); + ASSERT_NE(ctx1, nullptr); + ASSERT_EQ(ctx1, ctx2); +} + +TEST(DispatcherTests, pushedContextMustGoOn) { + Dispatcher dispatcher; + bool spawnDone = false; + dispatcher.spawn([&]() { + spawnDone = true; + }); + + dispatcher.pushContext(dispatcher.getCurrentContext()); + dispatcher.dispatch(); + ASSERT_TRUE(spawnDone); +} + +TEST(DispatcherTests, pushedContextMustGoOnFromNestedSpawns) { + Dispatcher dispatcher; + bool spawnDone = false; + auto mainContext = dispatcher.getCurrentContext(); + dispatcher.spawn([&]() { + spawnDone = true; + dispatcher.pushContext(mainContext); + }); + + dispatcher.dispatch(); + ASSERT_TRUE(spawnDone); +} + +TEST(DispatcherTests, remoteSpawnActuallySpawns) { + Dispatcher dispatcher; + Event remoteSpawnDone(dispatcher); + auto remoteSpawnThread = std::thread([&] { + dispatcher.remoteSpawn([&]() { + remoteSpawnDone.set(); + }); + }); + + if (remoteSpawnThread.joinable()) { + remoteSpawnThread.join(); + } + + dispatcher.yield(); + ASSERT_TRUE(remoteSpawnDone.get()); +} + +TEST(DispatcherTests, remoteSpawnActuallySpawns2) { + Dispatcher dispatcher; + Event remoteSpawnDone(dispatcher); + auto remoteSpawnThread = std::thread([&] { + dispatcher.remoteSpawn([&]() { + remoteSpawnDone.set(); + }); + }); + + if (remoteSpawnThread.joinable()) { + remoteSpawnThread.join(); + } + + Timer(dispatcher).sleep(std::chrono::milliseconds(1)); + ASSERT_TRUE(remoteSpawnDone.get()); +} + +TEST(DispatcherTests, remoteSpawnActuallySpawns3) { + Dispatcher dispatcher; + Event remoteSpawnDone(dispatcher); + auto mainCtx = dispatcher.getCurrentContext(); + auto remoteSpawnThread = std::thread([&] { + std::this_thread::sleep_for(std::chrono::seconds(1)); + dispatcher.remoteSpawn([&]() { + remoteSpawnDone.set(); + dispatcher.pushContext(mainCtx); + }); + }); + + dispatcher.dispatch(); + ASSERT_TRUE(remoteSpawnDone.get()); + if (remoteSpawnThread.joinable()) { + remoteSpawnThread.join(); + } +} + +TEST(DispatcherTests, remoteSpawnSpawnsProcedureInDispatcherThread) { + Dispatcher dispatcher; + Event remoteSpawnDone(dispatcher); + auto mainSpawnThrId = std::this_thread::get_id(); + decltype(mainSpawnThrId) remoteSpawnThrId; + auto remoteSpawnThread = std::thread([&] { + dispatcher.remoteSpawn([&]() { + remoteSpawnThrId = std::this_thread::get_id(); + remoteSpawnDone.set(); + }); + }); + + remoteSpawnDone.wait(); + if (remoteSpawnThread.joinable()) { + remoteSpawnThread.join(); + } + + ASSERT_EQ(mainSpawnThrId, remoteSpawnThrId); +} + +TEST(DispatcherTests, remoteSpawnSpawnsProcedureAndKeepsOrder) { + Dispatcher dispatcher; + Event remoteSpawnDone(dispatcher); + std::deque executionOrder; + std::deque expectedOrder = { 1, 2 }; + auto remoteSpawnThread = std::thread([&] { + dispatcher.remoteSpawn([&]() { + executionOrder.push_back(1); + }); + + dispatcher.remoteSpawn([&]() { + executionOrder.push_back(2); + remoteSpawnDone.set(); + }); + }); + + if (remoteSpawnThread.joinable()) { + remoteSpawnThread.join(); + } + + remoteSpawnDone.wait(); + ASSERT_EQ(executionOrder, expectedOrder); +} + +TEST(DispatcherTests, remoteSpawnActuallyWorksParallel) { + Dispatcher dispatcher; + Event remoteSpawnDone(dispatcher); + auto remoteSpawnThread = std::thread([&] { + dispatcher.remoteSpawn([&]() { + remoteSpawnDone.set(); + }); + }); + + Timer(dispatcher).sleep(std::chrono::milliseconds(100)); + ASSERT_TRUE(remoteSpawnDone.get()); + + if (remoteSpawnThread.joinable()) { + remoteSpawnThread.join(); + } +} + +TEST(DispatcherTests, spawnActuallySpawns) { + Dispatcher dispatcher; + bool spawnDone = false; + dispatcher.spawn([&]() { + spawnDone = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawnDone); +} + +TEST(DispatcherTests, spawnJustSpawns) { + Dispatcher dispatcher; + bool spawnDone = false; + dispatcher.spawn([&]() { + spawnDone = true; + }); + + ASSERT_FALSE(spawnDone); + dispatcher.yield(); + ASSERT_TRUE(spawnDone); +} + +TEST(DispatcherTests, yieldReturnsIfNothingToSpawn) { + Dispatcher dispatcher; + dispatcher.yield(); +} + +TEST(DispatcherTests, yieldReturnsAfterExecutionOfSpawnedProcedures) { + Dispatcher dispatcher; + bool spawnDone = false; + dispatcher.spawn([&]() { + spawnDone = true; + }); + + dispatcher.yield(); + ASSERT_TRUE(spawnDone); +} + +TEST(DispatcherTests, yieldReturnsAfterExecutionOfIO) { + Dispatcher dispatcher; + dispatcher.spawn([&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + dispatcher.yield(); + }); + + Timer(dispatcher).sleep(std::chrono::milliseconds(1)); + dispatcher.yield(); + SUCCEED(); +} + +TEST(DispatcherTests, yieldExecutesIoOnItsFront) { + Dispatcher dispatcher; + bool spawnDone = false; + dispatcher.spawn([&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + dispatcher.yield(); + spawnDone = true; + }); + + Timer(dispatcher).sleep(std::chrono::milliseconds(1)); + ASSERT_FALSE(spawnDone); + dispatcher.yield(); + ASSERT_TRUE(spawnDone); +} diff --git a/tests/System/EventLockTests.cpp b/tests/System/EventLockTests.cpp new file mode 100755 index 0000000000..4766679a3d --- /dev/null +++ b/tests/System/EventLockTests.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include + +using namespace System; + +TEST(EventLockTests, eventLockIsLocking) { + Dispatcher dispatcher; + Event event(dispatcher); + bool done = false; + dispatcher.spawn([&]() { + EventLock lock(event); + done = true; + }); + + ASSERT_FALSE(done); + dispatcher.yield(); + ASSERT_FALSE(done); + event.set(); + dispatcher.yield(); + ASSERT_TRUE(done); +} + +TEST(EventLockTests, eventLockIsNotLocking) { + Dispatcher dispatcher; + Event event(dispatcher); + event.set(); + bool done = false; + dispatcher.spawn([&]() { + EventLock lock(event); + done = true; + }); + + ASSERT_FALSE(done); + dispatcher.yield(); + ASSERT_TRUE(done); +} + +TEST(EventLockTests, eventLockIsUnlockOnlyOnce) { + Dispatcher dispatcher; + Event event(dispatcher); + auto i = 0; + dispatcher.spawn([&]() { + EventLock lock(event); + i++; + dispatcher.yield(); + i++; + }); + + dispatcher.spawn([&]() { + EventLock lock(event); + i += 2; + dispatcher.yield(); + i += 2; + }); + + event.set(); + dispatcher.yield(); + ASSERT_EQ(i, 1); + dispatcher.yield(); + ASSERT_EQ(i, 2); + dispatcher.yield(); + ASSERT_EQ(i, 4); + dispatcher.yield(); + ASSERT_EQ(i, 6); +} diff --git a/tests/System/EventTests.cpp b/tests/System/EventTests.cpp new file mode 100755 index 0000000000..3ef44e9a9c --- /dev/null +++ b/tests/System/EventTests.cpp @@ -0,0 +1,299 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include + +using namespace System; + +TEST(EventTests, newEventIsNotSet) { + Dispatcher dispatcher; + Event event(dispatcher); + ASSERT_FALSE(event.get()); +} + +TEST(EventTests, eventIsWorking) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + ASSERT_TRUE(event.get()); +} + +TEST(EventTests, movedEventIsWorking) { + Dispatcher dispatcher; + Event event(std::move(Event(dispatcher))); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + ASSERT_TRUE(event.get()); +} + +TEST(EventTests, movedEventKeepsState) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + Event event2(std::move(event)); + ASSERT_TRUE(event2.get()); +} + +TEST(EventTests, movedEventIsWorking2) { + Dispatcher dispatcher; + Event srcEvent(dispatcher); + Event event; + event = std::move(srcEvent); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + ASSERT_TRUE(event.get()); +} + +TEST(EventTests, movedEventKeepsState2) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + Event dstEvent; + dstEvent = std::move(event); + ASSERT_TRUE(dstEvent.get()); +} + +TEST(EventTests, moveClearsEventState) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + ASSERT_TRUE(event.get()); + Event srcEvent(dispatcher); + event = std::move(srcEvent); + ASSERT_FALSE(event.get()); +} + +TEST(EventTests, movedEventIsTheSame) { + Dispatcher dispatcher; + Event event(dispatcher); + auto eventPtr1 = &event; + Event srcEvent(dispatcher); + event = std::move(srcEvent); + auto eventPtr2 = &event; + ASSERT_EQ(eventPtr1, eventPtr2); +} + +TEST(EventTests, eventIsWorkingAfterClear) { + Dispatcher dispatcher; + Event event(dispatcher); + event.clear(); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + ASSERT_TRUE(event.get()); +} + +TEST(EventTests, eventIsWorkingAfterClearOnWaiting) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.clear(); + event.set(); + }); + + event.wait(); + ASSERT_TRUE(event.get()); +} + +TEST(EventTests, eventIsReusableAfterClear) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.set(); + dispatcher.yield(); + event.set(); + }); + + event.wait(); + event.clear(); + event.wait(); + SUCCEED(); +} + +TEST(EventTests, eventSetIsWorkingOnNewEvent) { + Dispatcher dispatcher; + Event event(dispatcher); + event.set(); + ASSERT_TRUE(event.get()); +} + +TEST(EventTests, setActuallySets) { + Dispatcher dispatcher; + Event event(dispatcher); + dispatcher.spawn([&]() { + event.set(); + }); + + event.wait(); + SUCCEED(); +} + +TEST(EventTests, setJustSets) { + Dispatcher dispatcher; + Event event(dispatcher); + bool done = false; + dispatcher.spawn([&]() { + event.wait(); + done = true; + }); + + dispatcher.yield(); + ASSERT_FALSE(done); + event.set(); + ASSERT_FALSE(done); + dispatcher.yield(); + ASSERT_TRUE(done); +} + +TEST(EventTests, setSetsOnlyOnce) { + Dispatcher dispatcher; + Event event(dispatcher); + auto i = 0; + dispatcher.spawn([&]() { + event.set(); + event.set(); + event.set(); + dispatcher.yield(); + i++; + }); + + event.wait(); + i++; + event.wait(); + ASSERT_EQ(i, 1); + dispatcher.yield(); + ASSERT_EQ(i, 2); +} + +TEST(EventTests, waitIsWaiting) { + Dispatcher dispatcher; + Event event(dispatcher); + bool done = false; + dispatcher.spawn([&]() { + event.wait(); + done = true; + }); + + dispatcher.yield(); + ASSERT_FALSE(done); + event.set(); + dispatcher.yield(); + ASSERT_TRUE(done); +} + +TEST(EventTests, setEventIsNotWaiting) { + Dispatcher dispatcher; + Event event(dispatcher); + auto i = 0; + dispatcher.spawn([&]() { + event.set(); + dispatcher.yield(); + i++; + }); + + event.wait(); + i++; + ASSERT_EQ(i, 1); + event.wait(); + ASSERT_EQ(i, 1); + dispatcher.yield(); + ASSERT_EQ(i, 2); +} + +TEST(EventTests, waitIsParallel) { + Dispatcher dispatcher; + Event event(dispatcher); + auto i = 0; + dispatcher.spawn([&]() { + i++; + event.set(); + }); + + ASSERT_EQ(i, 0); + event.wait(); + ASSERT_EQ(i, 1); +} + +TEST(EventTests, waitIsMultispawn) { + Dispatcher dispatcher; + Event event(dispatcher); + auto i = 0; + dispatcher.spawn([&]() { + event.wait(); + i++; + }); + + dispatcher.spawn([&]() { + event.wait(); + i++; + }); + + ASSERT_EQ(i, 0); + dispatcher.yield(); + ASSERT_EQ(i, 0); + event.set(); + dispatcher.yield(); + ASSERT_EQ(i, 2); +} + +TEST(EventTests, setEventInPastUnblocksWaitersEvenAfterClear) { + Dispatcher dispatcher; + Event event(dispatcher); + auto i = 0; + dispatcher.spawn([&]() { + event.wait(); + i++; + }); + + dispatcher.spawn([&]() { + event.wait(); + i++; + }); + + dispatcher.yield(); + ASSERT_EQ(i, 0); + event.set(); + event.clear(); + dispatcher.yield(); + ASSERT_EQ(i, 2); +} diff --git a/tests/System/Ipv4AddressTests.cpp b/tests/System/Ipv4AddressTests.cpp new file mode 100755 index 0000000000..e3fff4eb8c --- /dev/null +++ b/tests/System/Ipv4AddressTests.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include + +using namespace System; + +TEST(Ipv4AddressTest, value) { + Ipv4Address address1(0x00000000); + ASSERT_EQ(0x00000000, address1.getValue()); + Ipv4Address address2(0xfefdfcfb); + ASSERT_EQ(0xfefdfcfb, address2.getValue()); + Ipv4Address address3 = address1; + ASSERT_EQ(0x00000000, address3.getValue()); + Ipv4Address address4 = address2; + ASSERT_EQ(0xfefdfcfb, address4.getValue()); + address3 = address2; + ASSERT_EQ(0xfefdfcfb, address3.getValue()); + address4 = address1; + ASSERT_EQ(0x00000000, address4.getValue()); +} + +TEST(Ipv4AddressTest, dottedDecimal) { + ASSERT_EQ(0x00000000, Ipv4Address("0.0.0.0").getValue()); + ASSERT_EQ(0x01020304, Ipv4Address("1.2.3.4").getValue()); + ASSERT_EQ(0x7f000001, Ipv4Address("127.0.0.1").getValue()); + ASSERT_EQ(0xfefdfcfb, Ipv4Address("254.253.252.251").getValue()); + ASSERT_EQ(0xffffffff, Ipv4Address("255.255.255.255").getValue()); + ASSERT_EQ("0.0.0.0", Ipv4Address(0x00000000).toDottedDecimal()); + ASSERT_EQ("1.2.3.4", Ipv4Address(0x01020304).toDottedDecimal()); + ASSERT_EQ("127.0.0.1", Ipv4Address(0x7f000001).toDottedDecimal()); + ASSERT_EQ("254.253.252.251", Ipv4Address(0xfefdfcfb).toDottedDecimal()); + ASSERT_EQ("255.255.255.255", Ipv4Address(0xffffffff).toDottedDecimal()); + ASSERT_THROW(Ipv4Address(".0.0.0.0"), std::runtime_error); + ASSERT_THROW(Ipv4Address("0..0.0.0"), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0"), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0."), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0.0."), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0.0.0"), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0.00"), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0.01"), std::runtime_error); + ASSERT_THROW(Ipv4Address("0.0.0.256"), std::runtime_error); + ASSERT_THROW(Ipv4Address("00.0.0.0"), std::runtime_error); + ASSERT_THROW(Ipv4Address("01.0.0.0"), std::runtime_error); + ASSERT_THROW(Ipv4Address("256.0.0.0"), std::runtime_error); +} + +TEST(Ipv4AddressTest, isLoopback) { + // 127.0.0.0/8 + ASSERT_TRUE(Ipv4Address("127.0.0.1").isLoopback()); + ASSERT_TRUE(Ipv4Address("127.1.1.1").isLoopback()); + ASSERT_TRUE(Ipv4Address("127.1.0.0").isLoopback()); + ASSERT_TRUE(Ipv4Address("127.255.255.255").isLoopback()); + + ASSERT_FALSE(Ipv4Address("255.0.0.0").isLoopback()); + ASSERT_FALSE(Ipv4Address("255.255.255.255").isLoopback()); + ASSERT_FALSE(Ipv4Address("128.1.0.0").isLoopback()); + ASSERT_FALSE(Ipv4Address("192.168.1.1").isLoopback()); + ASSERT_FALSE(Ipv4Address("10.0.0.1").isLoopback()); +} + +TEST(Ipv4AddressTest, isPrivate) { + // 10.0.0.0/8 + ASSERT_TRUE(Ipv4Address("10.0.0.0").isPrivate()); + ASSERT_TRUE(Ipv4Address("10.0.0.1").isPrivate()); + ASSERT_TRUE(Ipv4Address("10.0.0.255").isPrivate()); + ASSERT_TRUE(Ipv4Address("10.255.255.255").isPrivate()); + + ASSERT_FALSE(Ipv4Address("11.0.0.255").isPrivate()); + ASSERT_FALSE(Ipv4Address("9.0.0.0").isPrivate()); + ASSERT_FALSE(Ipv4Address("138.0.0.1").isPrivate()); + ASSERT_FALSE(Ipv4Address("255.255.255.255").isPrivate()); + + // 172.16.0.0/12 + ASSERT_TRUE(Ipv4Address("172.16.0.255").isPrivate()); + ASSERT_TRUE(Ipv4Address("172.17.0.0").isPrivate()); + ASSERT_TRUE(Ipv4Address("172.19.1.1").isPrivate()); + ASSERT_TRUE(Ipv4Address("172.31.255.255").isPrivate()); + + ASSERT_FALSE(Ipv4Address("172.32.0.0").isPrivate()); + ASSERT_FALSE(Ipv4Address("172.32.0.1").isPrivate()); + ASSERT_FALSE(Ipv4Address("172.15.0.0").isPrivate()); + ASSERT_FALSE(Ipv4Address("172.15.255.255").isPrivate()); + + // 192.168.0.0/16 + ASSERT_TRUE(Ipv4Address("192.168.0.0").isPrivate()); + ASSERT_TRUE(Ipv4Address("192.168.1.1").isPrivate()); + ASSERT_TRUE(Ipv4Address("192.168.100.100").isPrivate()); + ASSERT_TRUE(Ipv4Address("192.168.255.255").isPrivate()); + + ASSERT_FALSE(Ipv4Address("192.167.255.255").isPrivate()); + ASSERT_FALSE(Ipv4Address("191.168.255.255").isPrivate()); + ASSERT_FALSE(Ipv4Address("192.169.255.255").isPrivate()); + ASSERT_FALSE(Ipv4Address("192.169.0.0").isPrivate()); + + ASSERT_FALSE(Ipv4Address("255.255.255.255").isPrivate()); + +} diff --git a/tests/System/Ipv4ResolverTests.cpp b/tests/System/Ipv4ResolverTests.cpp new file mode 100755 index 0000000000..384c1b5fe6 --- /dev/null +++ b/tests/System/Ipv4ResolverTests.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include + +using namespace System; + +TEST(Ipv4ResolverTest, start) { + Dispatcher dispatcher; + Ipv4Resolver resolver(dispatcher); + resolver.stop(); + resolver.start(); + ASSERT_NO_THROW(resolver.resolve("localhost")); +} + +TEST(Ipv4ResolverTest, stop) { + Dispatcher dispatcher; + Ipv4Resolver resolver(dispatcher); + resolver.stop(); + ASSERT_THROW(resolver.resolve("localhost"), InterruptedException); +} + +TEST(Ipv4ResolverTest, resolve) { + Dispatcher dispatcher; + Ipv4Resolver resolver(dispatcher); + ASSERT_EQ(Ipv4Address("0.0.0.0"), resolver.resolve("0.0.0.0")); + ASSERT_EQ(Ipv4Address("1.2.3.4"), resolver.resolve("1.2.3.4")); + ASSERT_EQ(Ipv4Address("127.0.0.1"), resolver.resolve("127.0.0.1")); + ASSERT_EQ(Ipv4Address("254.253.252.251"), resolver.resolve("254.253.252.251")); + ASSERT_EQ(Ipv4Address("255.255.255.255"), resolver.resolve("255.255.255.255")); + ASSERT_EQ(Ipv4Address("127.0.0.1"), resolver.resolve("localhost")); +//ASSERT_EQ(Ipv4Address("93.184.216.34"), resolver.resolve("example.com")); + ASSERT_THROW(resolver.resolve(".0.0.0.0"), std::runtime_error); + ASSERT_THROW(resolver.resolve("0..0.0.0"), std::runtime_error); +//ASSERT_THROW(resolver.resolve("0.0.0"), std::runtime_error); + ASSERT_THROW(resolver.resolve("0.0.0."), std::runtime_error); +//ASSERT_THROW(resolver.resolve("0.0.0.0."), std::runtime_error); + ASSERT_THROW(resolver.resolve("0.0.0.0.0"), std::runtime_error); +//ASSERT_THROW(resolver.resolve("0.0.0.00"), std::runtime_error); +//ASSERT_THROW(resolver.resolve("0.0.0.01"), std::runtime_error); + ASSERT_THROW(resolver.resolve("0.0.0.256"), std::runtime_error); +//ASSERT_THROW(resolver.resolve("00.0.0.0"), std::runtime_error); +//ASSERT_THROW(resolver.resolve("01.0.0.0"), std::runtime_error); + ASSERT_THROW(resolver.resolve("256.0.0.0"), std::runtime_error); + ASSERT_THROW(resolver.resolve("invalid"), std::runtime_error); +} diff --git a/tests/System/LatchTests.cpp b/tests/System/LatchTests.cpp new file mode 100755 index 0000000000..ce91670dda --- /dev/null +++ b/tests/System/LatchTests.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include + +using namespace System; + +TEST(LatchTest, latch1) { + Dispatcher dispatcher; + Latch latch(dispatcher); + ASSERT_EQ(0, latch.get()); + latch.increase(10); + ASSERT_EQ(10, latch.get()); + latch.decrease(10); + ASSERT_EQ(0, latch.get()); + latch.wait(); +} diff --git a/tests/System/TcpConnectionTests.cpp b/tests/System/TcpConnectionTests.cpp new file mode 100755 index 0000000000..fa1274e3f9 --- /dev/null +++ b/tests/System/TcpConnectionTests.cpp @@ -0,0 +1,260 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace System; + +namespace { + +const Ipv4Address LISTEN_ADDRESS("127.0.0.1"); +const uint16_t LISTEN_PORT = 6666; + +void fillRandomBuf(std::vector& buf) { + for (size_t i = 0; i < buf.size(); ++i) { + buf[i] = static_cast(rand() & 0xff); + } +} + +void fillRandomString(std::string& buf) { + for (size_t i = 0; i < buf.size(); ++i) { + buf[i] = static_cast(rand() & 0xff); + } +} + +std::string removePort(const std::string& address) { + std::size_t colonPosition = address.rfind(':'); + if (colonPosition == std::string::npos) { + throw std::runtime_error("removePort"); + } + + return address.substr(0, colonPosition); +} + +} + +class TcpConnectionTest : public testing::Test { +public: + TcpConnectionTest() : + listener(dispatcher, LISTEN_ADDRESS, LISTEN_PORT) { + } + + void connect() { + connection1 = TcpConnector(dispatcher).connect(LISTEN_ADDRESS, LISTEN_PORT); + connection2 = listener.accept(); + } + +protected: + Dispatcher dispatcher; + TcpListener listener; + TcpConnection connection1; + TcpConnection connection2; +}; + +TEST_F(TcpConnectionTest, sendAndClose) { + connect(); + ASSERT_EQ(LISTEN_ADDRESS, connection1.getPeerAddressAndPort().first); + ASSERT_EQ(LISTEN_ADDRESS, connection2.getPeerAddressAndPort().first); + connection1.write(reinterpret_cast("Test"), 4); + uint8_t data[1024]; + std::size_t size = connection2.read(data, 1024); + ASSERT_EQ(4, size); + ASSERT_EQ(0, memcmp(data, "Test", 4)); + connection1 = TcpConnection(); + size = connection2.read(data, 1024); + ASSERT_EQ(0, size); +} + +TEST_F(TcpConnectionTest, stoppedState) { + connect(); + connection1.stop(); + bool stopped = false; + try { + uint8_t data[1024]; + std::size_t size = connection1.read(data, 1024); + } catch (InterruptedException&) { + stopped = true; + } + + ASSERT_TRUE(stopped); + stopped = false; + try { + connection1.write(reinterpret_cast("Test"), 4); + } catch (InterruptedException&) { + stopped = true; + } + + ASSERT_TRUE(stopped); +} + +TEST_F(TcpConnectionTest, interruptRead) { + connect(); + Event event(dispatcher); + dispatcher.spawn([&]() { + Timer(dispatcher).sleep(std::chrono::milliseconds(10)); + connection1.stop(); + event.set(); + }); + + bool stopped = false; + try { + uint8_t data[1024]; + std::size_t size = connection1.read(data, 1024); + } catch (InterruptedException&) { + stopped = true; + } + + event.wait(); + ASSERT_TRUE(stopped); +} + +TEST_F(TcpConnectionTest, sendBigChunk) { + connect(); + + const size_t bufsize = 15* 1024 * 1024; // 15MB + std::vector buf; + buf.resize(bufsize); + fillRandomBuf(buf); + + std::vector incoming; + Event readComplete(dispatcher); + + dispatcher.spawn([&]{ + uint8_t readBuf[1024]; + size_t readSize; + while ((readSize = connection2.read(readBuf, sizeof(readBuf))) > 0) { + incoming.insert(incoming.end(), readBuf, readBuf + readSize); + } + + readComplete.set(); + }); + + dispatcher.spawn([&]{ + uint8_t* bufPtr = &buf[0]; + size_t left = bufsize; + while(left > 0) { + auto transferred = connection1.write(bufPtr, std::min(left, size_t(666))); + left -= transferred; + bufPtr += transferred; + } + + connection1 = TcpConnection(); // close connection + }); + + readComplete.wait(); + + ASSERT_EQ(bufsize, incoming.size()); + ASSERT_EQ(buf, incoming); +} + +TEST_F(TcpConnectionTest, writeWhenReadWaiting) { + connect(); + + Event readStarted(dispatcher); + Event readCompleted(dispatcher); + Event writeCompleted(dispatcher); + + size_t writeSize = 0; + bool readStopped = false; + + dispatcher.spawn([&]{ + try { + uint8_t readBuf[1024]; + size_t readSize; + readStarted.set(); + while ((readSize = connection2.read(readBuf, sizeof(readBuf))) > 0) { + } + } catch (InterruptedException&) { + readStopped = true; + } + connection2 = TcpConnection(); + readCompleted.set(); + }); + + readStarted.wait(); + + dispatcher.spawn([&]{ + uint8_t writeBuf[1024]; + for (int i = 0; i < 100; ++i) { + writeSize += connection2.write(writeBuf, sizeof(writeBuf)); + } + connection2.stop(); + writeCompleted.set(); + }); + + uint8_t readBuf[100]; + size_t readSize; + size_t totalRead = 0; + while ((readSize = connection1.read(readBuf, sizeof(readBuf))) > 0) { + totalRead += readSize; + } + + ASSERT_EQ(writeSize, totalRead); + readCompleted.wait(); + ASSERT_TRUE(readStopped); + writeCompleted.wait(); +} + +TEST_F(TcpConnectionTest, sendBigChunkThruTcpStream) { + connect(); + const size_t bufsize = 15 * 1024 * 1024; // 15MB + std::string buf; + buf.resize(bufsize); + fillRandomString(buf); + + std::string incoming; + Event readComplete(dispatcher); + + dispatcher.spawn([&]{ + uint8_t readBuf[1024]; + size_t readSize; + while ((readSize = connection2.read(readBuf, sizeof(readBuf))) > 0) { + incoming.insert(incoming.end(), readBuf, readBuf + readSize); + } + + readComplete.set(); + }); + + + dispatcher.spawn([&]{ + TcpStreambuf streambuf(connection1); + std::iostream stream(&streambuf); + + stream << buf; + stream.flush(); + + connection1 = TcpConnection(); // close connection + }); + + readComplete.wait(); + + ASSERT_EQ(bufsize, incoming.size()); + + //ASSERT_EQ(buf, incoming); + for (size_t i = 0; i < bufsize; ++i) { + ASSERT_EQ(buf[i], incoming[i]); //for better output. + } +} diff --git a/tests/System/TcpConnectorTests.cpp b/tests/System/TcpConnectorTests.cpp new file mode 100755 index 0000000000..42492297ac --- /dev/null +++ b/tests/System/TcpConnectorTests.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace System; + +TEST(TcpConnectorTest, tcpConnector1) { + Dispatcher dispatcher; + Event event(dispatcher); + TcpListener listener(dispatcher, Ipv4Address("127.0.0.1"), 6666); + dispatcher.spawn([&]() { + listener.accept(); + event.set(); + }); + + TcpConnector connector(dispatcher); + connector.stop(); + connector.start(); + connector.connect(Ipv4Address("127.0.0.1"), 6666); + connector.stop(); + connector.start(); + event.wait(); + dispatcher.yield(); +} + +TEST(TcpConnectorTest, tcpConnector2) { + Dispatcher dispatcher; + TcpConnector connector(dispatcher); + connector.stop(); + connector.start(); + connector.stop(); + ASSERT_THROW(connector.connect(Ipv4Address("127.0.0.1"), 6666), InterruptedException); +} + +TEST(TcpConnectorTest, tcpConnector3) { + Dispatcher dispatcher; + TcpConnector connector(dispatcher); + Event event(dispatcher); + dispatcher.spawn([&]() { + Timer(dispatcher).sleep(std::chrono::milliseconds(10)); + connector.stop(); + event.set(); + }); + + ASSERT_THROW(connector.connect(Ipv4Address("10.255.255.1"), 6666), InterruptedException); + event.wait(); +} + +TEST(TcpConnectorTest, bindToTheSameAddressFails) { + Dispatcher dispatcher; + TcpListener listener1(dispatcher, Ipv4Address("127.0.0.1"), 6666); + ASSERT_THROW(TcpListener listener2(dispatcher, Ipv4Address("127.0.0.1"), 6666), std::runtime_error); +} diff --git a/tests/System/TcpListenerTests.cpp b/tests/System/TcpListenerTests.cpp new file mode 100755 index 0000000000..2ac0bfa869 --- /dev/null +++ b/tests/System/TcpListenerTests.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace System; + +TEST(TcpListenerTest, tcpListener1) { + Dispatcher dispatcher; + Event event(dispatcher); + TcpListener listener(dispatcher, Ipv4Address("127.0.0.1"), 6666); + dispatcher.spawn([&]() { + TcpConnector connector(dispatcher); + connector.connect(Ipv4Address("127.0.0.1"), 6666); + event.set(); + }); + + listener.stop(); + listener.start(); + listener.accept(); + listener.stop(); + listener.start(); + event.wait(); +} + +TEST(TcpListenerTest, tcpListener2) { + bool stopped = false; + Dispatcher dispatcher; + TcpListener listener(dispatcher, Ipv4Address("127.0.0.1"), 6666); + listener.stop(); + listener.start(); + listener.stop(); + + try { + listener.accept(); + } catch (InterruptedException&) { + stopped = true; + } + + ASSERT_TRUE(stopped); +} + +TEST(TcpListenerTest, tcpListener3) { + bool stopped = false; + Dispatcher dispatcher; + Event event(dispatcher); + TcpListener listener(dispatcher, Ipv4Address("127.0.0.1"), 6666); + dispatcher.spawn([&]() { + Timer(dispatcher).sleep(std::chrono::milliseconds(10)); + listener.stop(); + event.set(); + }); + + try { + listener.accept(); + } catch (InterruptedException&) { + stopped = true; + } + + event.wait(); + ASSERT_TRUE(stopped); +} diff --git a/tests/System/TimerTests.cpp b/tests/System/TimerTests.cpp new file mode 100755 index 0000000000..c0dfe90a4a --- /dev/null +++ b/tests/System/TimerTests.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include +#include +#include +#include + +using namespace System; + +TEST(TimerTests, timerIsWorking) { + Dispatcher dispatcher; + bool done = false; + dispatcher.spawn([&]() { + done = true; + }); + + ASSERT_FALSE(done); + Timer(dispatcher).sleep(std::chrono::milliseconds(10)); + ASSERT_TRUE(done); +} + +TEST(TimerTests, movedTimerIsWorking) { + Dispatcher dispatcher; + Timer t(std::move(Timer(dispatcher))); + bool done = false; + dispatcher.spawn([&]() { + done = true; + }); + + ASSERT_FALSE(done); + t.sleep(std::chrono::milliseconds(10)); + ASSERT_TRUE(done); +} + +TEST(TimerTests, movedAndStoopedTimerIsWorking) { + Dispatcher dispatcher; + Timer src(dispatcher); + src.stop(); + Timer t(std::move(src)); + + ASSERT_ANY_THROW(t.sleep(std::chrono::milliseconds(1))); +} + +TEST(TimerTests, movedTimerIsWorking2) { + Dispatcher dispatcher; + Timer t(dispatcher); + t = std::move(Timer(dispatcher)); + bool done = false; + dispatcher.spawn([&]() { + done = true; + }); + + ASSERT_FALSE(done); + t.sleep(std::chrono::milliseconds(10)); + ASSERT_TRUE(done); +} + +TEST(TimerTests, movedAndStoopedTimerIsWorking2) { + Dispatcher dispatcher; + Timer src(dispatcher); + src.stop(); + Timer t(dispatcher); + t = std::move(src); + + ASSERT_ANY_THROW(t.sleep(std::chrono::milliseconds(1))); +} + +TEST(TimerTests, movedTimerIsTheSame) { + Dispatcher dispatcher; + Timer timer(dispatcher); + auto timerPtr1 = &timer; + Timer srcEvent(dispatcher); + timer = std::move(srcEvent); + auto timerPtr2 = &timer; + ASSERT_EQ(timerPtr1, timerPtr2); +} + +TEST(TimerTests, timerStartIsWorking) { + Dispatcher dispatcher; + Timer t(dispatcher); + t.stop(); + ASSERT_ANY_THROW(t.sleep(std::chrono::milliseconds(1))); + t.start(); + ASSERT_NO_THROW(t.sleep(std::chrono::milliseconds(1))); +} + +TEST(TimerTests, timerStopBeforeSleep) { + Dispatcher dispatcher; + Timer t(dispatcher); + t.stop(); + ASSERT_THROW(t.sleep(std::chrono::milliseconds(1)), InterruptedException); + ASSERT_THROW(t.sleep(std::chrono::milliseconds(1)), InterruptedException); +} + +TEST(TimerTests, timerIsCancelable) { + Dispatcher dispatcher; + Timer t(dispatcher); + dispatcher.spawn([&]() { + t.stop(); + }); + + ASSERT_THROW(t.sleep(std::chrono::milliseconds(100)), InterruptedException); +} + +TEST(TimerTests, DISABLED_sleepThrowsOnlyIfTimerIsStoppedBeforeTime) { + Dispatcher dispatcher; + Timer t(dispatcher); + dispatcher.spawn([&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + t.stop(); + }); + + ASSERT_NO_THROW(t.sleep(std::chrono::milliseconds(1))); + ASSERT_THROW(t.sleep(std::chrono::milliseconds(1)), InterruptedException); +} + +TEST(TimerTests, sleepIsSleepingAtLeastTakenTime) { + Dispatcher dispatcher; + Timer t(dispatcher); + auto timepoint1 = std::chrono::high_resolution_clock::now(); + t.sleep(std::chrono::milliseconds(100)); + auto timepoint2 = std::chrono::high_resolution_clock::now(); + + ASSERT_LE(100, std::chrono::duration_cast(timepoint2 - timepoint1).count()); +} + +TEST(TimerTests, timerIsReusable) { + Dispatcher dispatcher; + Timer t(dispatcher); + ASSERT_NO_THROW(t.sleep(std::chrono::milliseconds(1))); + ASSERT_NO_THROW(t.sleep(std::chrono::milliseconds(1))); +} + +TEST(TimerTests, timerWithZeroTimeIsYielding) { + Dispatcher dispatcher; + bool done = false; + dispatcher.spawn([&]() { + done = true; + }); + + ASSERT_FALSE(done); + Timer(dispatcher).sleep(std::chrono::milliseconds(0)); + ASSERT_TRUE(done); +} diff --git a/tests/System/main.cpp b/tests/System/main.cpp new file mode 100755 index 0000000000..71d5a20480 --- /dev/null +++ b/tests/System/main.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/TestGenerator/TestGenerator.cpp b/tests/TestGenerator/TestGenerator.cpp index 87711a2234..ed6bfc22de 100644 --- a/tests/TestGenerator/TestGenerator.cpp +++ b/tests/TestGenerator/TestGenerator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,7 +26,7 @@ using namespace std; using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; void test_generator::getBlockchain(std::vector& blockchain, const crypto::hash& head, size_t n) const { @@ -61,13 +61,13 @@ uint64_t test_generator::getAlreadyGeneratedCoins(const crypto::hash& blockId) c return it->second.alreadyGeneratedCoins; } -uint64_t test_generator::getAlreadyGeneratedCoins(const cryptonote::Block& blk) const { +uint64_t test_generator::getAlreadyGeneratedCoins(const CryptoNote::Block& blk) const { crypto::hash blkHash; get_block_hash(blk, blkHash); return getAlreadyGeneratedCoins(blkHash); } -void test_generator::addBlock(const cryptonote::Block& blk, size_t tsxSize, uint64_t fee, +void test_generator::addBlock(const CryptoNote::Block& blk, size_t tsxSize, uint64_t fee, std::vector& blockSizes, uint64_t alreadyGeneratedCoins) { const size_t blockSize = tsxSize + get_object_blobsize(blk.minerTx); int64_t emissionChange; @@ -78,9 +78,9 @@ void test_generator::addBlock(const cryptonote::Block& blk, size_t tsxSize, uint m_blocksInfo[get_block_hash(blk)] = BlockInfo(blk.prevId, alreadyGeneratedCoins + emissionChange, blockSize); } -bool test_generator::constructBlock(cryptonote::Block& blk, uint64_t height, const crypto::hash& prevId, - const cryptonote::account_base& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins, - std::vector& blockSizes, const std::list& txList) { +bool test_generator::constructBlock(CryptoNote::Block& blk, uint32_t height, const crypto::hash& prevId, + const CryptoNote::account_base& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins, + std::vector& blockSizes, const std::list& txList) { blk.majorVersion = defaultMajorVersion; blk.minorVersion = defaultMinorVersion; blk.timestamp = timestamp; @@ -143,14 +143,14 @@ bool test_generator::constructBlock(cryptonote::Block& blk, uint64_t height, con blk.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; blk.parentBlock.numberOfTransactions = 1; - cryptonote::tx_extra_merge_mining_tag mmTag; + CryptoNote::tx_extra_merge_mining_tag mmTag; mmTag.depth = 0; - if (!cryptonote::get_aux_block_header_hash(blk, mmTag.merkle_root)) { + if (!CryptoNote::get_aux_block_header_hash(blk, mmTag.merkle_root)) { return false; } blk.parentBlock.minerTx.extra.clear(); - if (!cryptonote::append_mm_tag_to_extra(blk.parentBlock.minerTx.extra, mmTag)) { + if (!CryptoNote::append_mm_tag_to_extra(blk.parentBlock.minerTx.extra, mmTag)) { return false; } } @@ -167,16 +167,16 @@ bool test_generator::constructBlock(cryptonote::Block& blk, uint64_t height, con return true; } -bool test_generator::constructBlock(cryptonote::Block& blk, const cryptonote::account_base& minerAcc, uint64_t timestamp) { +bool test_generator::constructBlock(CryptoNote::Block& blk, const CryptoNote::account_base& minerAcc, uint64_t timestamp) { std::vector blockSizes; - std::list txList; + std::list txList; return constructBlock(blk, 0, null_hash, minerAcc, timestamp, 0, blockSizes, txList); } -bool test_generator::constructBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, - const cryptonote::account_base& minerAcc, - const std::list& txList/* = std::list()*/) { - uint64_t height = boost::get(blkPrev.minerTx.vin.front()).height + 1; +bool test_generator::constructBlock(CryptoNote::Block& blk, const CryptoNote::Block& blkPrev, + const CryptoNote::account_base& minerAcc, + const std::list& txList/* = std::list()*/) { + uint32_t height = boost::get(blkPrev.minerTx.vin.front()).height + 1; crypto::hash prevId = get_block_hash(blkPrev); // Keep difficulty unchanged uint64_t timestamp = blkPrev.timestamp + m_currency.difficultyTarget(); @@ -200,7 +200,7 @@ bool test_generator::constructBlockManually(Block& blk, const Block& prevBlock, blk.prevId = actualParams & bf_prev_id ? prevId : get_block_hash(prevBlock); blk.txHashes = actualParams & bf_tx_hashes ? txHashes : std::vector(); - size_t height = get_block_height(prevBlock) + 1; + uint32_t height = get_block_height(prevBlock) + 1; uint64_t alreadyGeneratedCoins = getAlreadyGeneratedCoins(prevBlock); std::vector blockSizes; getLastNBlockSizes(blockSizes, get_block_hash(prevBlock), m_currency.rewardBlocksWindow()); @@ -220,14 +220,14 @@ bool test_generator::constructBlockManually(Block& blk, const Block& prevBlock, blk.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; blk.parentBlock.numberOfTransactions = 1; - cryptonote::tx_extra_merge_mining_tag mmTag; + CryptoNote::tx_extra_merge_mining_tag mmTag; mmTag.depth = 0; - if (!cryptonote::get_aux_block_header_hash(blk, mmTag.merkle_root)) { + if (!CryptoNote::get_aux_block_header_hash(blk, mmTag.merkle_root)) { return false; } blk.parentBlock.minerTx.extra.clear(); - if (!cryptonote::append_mm_tag_to_extra(blk.parentBlock.minerTx.extra, mmTag)) { + if (!CryptoNote::append_mm_tag_to_extra(blk.parentBlock.minerTx.extra, mmTag)) { return false; } } @@ -242,24 +242,24 @@ bool test_generator::constructBlockManually(Block& blk, const Block& prevBlock, return true; } -bool test_generator::constructBlockManuallyTx(cryptonote::Block& blk, const cryptonote::Block& prevBlock, - const cryptonote::account_base& minerAcc, +bool test_generator::constructBlockManuallyTx(CryptoNote::Block& blk, const CryptoNote::Block& prevBlock, + const CryptoNote::account_base& minerAcc, const std::vector& txHashes, size_t txsSize) { return constructBlockManually(blk, prevBlock, minerAcc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, Transaction(), txHashes, txsSize); } -bool test_generator::constructMaxSizeBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, - const cryptonote::account_base& minerAccount, +bool test_generator::constructMaxSizeBlock(CryptoNote::Block& blk, const CryptoNote::Block& blkPrev, + const CryptoNote::account_base& minerAccount, size_t medianBlockCount/* = 0*/, - const std::list& txList/* = std::list()*/) { + const std::list& txList/* = std::list()*/) { std::vector blockSizes; medianBlockCount = medianBlockCount == 0 ? m_currency.rewardBlocksWindow() : medianBlockCount; getLastNBlockSizes(blockSizes, get_block_hash(blkPrev), medianBlockCount); size_t median = misc_utils::median(blockSizes); size_t blockGrantedFullRewardZone = defaultMajorVersion <= BLOCK_MAJOR_VERSION_1 ? - cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : + CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : m_currency.blockGrantedFullRewardZone(); median = std::max(median, blockGrantedFullRewardZone); @@ -287,7 +287,7 @@ bool test_generator::constructMaxSizeBlock(cryptonote::Block& blk, const crypton 0, 0, 0, crypto::hash(), 0, minerTx, txHashes, txsSize, totalFee); } -void fillNonce(cryptonote::Block& blk, const difficulty_type& diffic) { +void fillNonce(CryptoNote::Block& blk, const difficulty_type& diffic) { blk.nonce = 0; crypto::cn_context context; while (!miner::find_nonce_for_given_block(context, blk, diffic)) { @@ -295,7 +295,7 @@ void fillNonce(cryptonote::Block& blk, const difficulty_type& diffic) { } } -bool constructMinerTxManually(const cryptonote::Currency& currency, size_t height, uint64_t alreadyGeneratedCoins, +bool constructMinerTxManually(const CryptoNote::Currency& currency, uint32_t height, uint64_t alreadyGeneratedCoins, const AccountPublicAddress& minerAddress, Transaction& tx, uint64_t fee, KeyPair* pTxKey/* = 0*/) { KeyPair txkey; @@ -334,12 +334,12 @@ bool constructMinerTxManually(const cryptonote::Currency& currency, size_t heigh return true; } -bool constructMinerTxBySize(const cryptonote::Currency& currency, cryptonote::Transaction& minerTx, uint64_t height, - uint64_t alreadyGeneratedCoins, const cryptonote::AccountPublicAddress& minerAddress, +bool constructMinerTxBySize(const CryptoNote::Currency& currency, CryptoNote::Transaction& minerTx, uint32_t height, + uint64_t alreadyGeneratedCoins, const CryptoNote::AccountPublicAddress& minerAddress, std::vector& blockSizes, size_t targetTxSize, size_t targetBlockSize, uint64_t fee/* = 0*/, bool penalizeFee/* = false*/) { if (!currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, targetBlockSize, - fee, minerAddress, minerTx, cryptonote::blobdata(), 1, penalizeFee)) { + fee, minerAddress, minerTx, CryptoNote::blobdata(), 1, penalizeFee)) { return false; } diff --git a/tests/TestGenerator/TestGenerator.h b/tests/TestGenerator/TestGenerator.h index e7b4658504..9e10a70006 100644 --- a/tests/TestGenerator/TestGenerator.h +++ b/tests/TestGenerator/TestGenerator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -62,8 +62,8 @@ class test_generator bf_diffic = 1 << 6 }; - test_generator(const cryptonote::Currency& currency, uint8_t majorVersion = cryptonote::BLOCK_MAJOR_VERSION_1, - uint8_t minorVersion = cryptonote::BLOCK_MINOR_VERSION_0) + test_generator(const CryptoNote::Currency& currency, uint8_t majorVersion = CryptoNote::BLOCK_MAJOR_VERSION_1, + uint8_t minorVersion = CryptoNote::BLOCK_MINOR_VERSION_0) : m_currency(currency), defaultMajorVersion(majorVersion), defaultMinorVersion(minorVersion) { } @@ -71,45 +71,45 @@ class test_generator uint8_t defaultMajorVersion; uint8_t defaultMinorVersion; - const cryptonote::Currency& currency() const { return m_currency; } + const CryptoNote::Currency& currency() const { return m_currency; } void getBlockchain(std::vector& blockchain, const crypto::hash& head, size_t n) const; void getLastNBlockSizes(std::vector& blockSizes, const crypto::hash& head, size_t n) const; uint64_t getAlreadyGeneratedCoins(const crypto::hash& blockId) const; - uint64_t getAlreadyGeneratedCoins(const cryptonote::Block& blk) const; + uint64_t getAlreadyGeneratedCoins(const CryptoNote::Block& blk) const; - void addBlock(const cryptonote::Block& blk, size_t tsxSize, uint64_t fee, std::vector& blockSizes, + void addBlock(const CryptoNote::Block& blk, size_t tsxSize, uint64_t fee, std::vector& blockSizes, uint64_t alreadyGeneratedCoins); - bool constructBlock(cryptonote::Block& blk, uint64_t height, const crypto::hash& prevId, - const cryptonote::account_base& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins, - std::vector& blockSizes, const std::list& txList); - bool constructBlock(cryptonote::Block& blk, const cryptonote::account_base& minerAcc, uint64_t timestamp); - bool constructBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, const cryptonote::account_base& minerAcc, - const std::list& txList = std::list()); - - bool constructBlockManually(cryptonote::Block& blk, const cryptonote::Block& prevBlock, - const cryptonote::account_base& minerAcc, int actualParams = bf_none, uint8_t majorVer = 0, + bool constructBlock(CryptoNote::Block& blk, uint32_t height, const crypto::hash& prevId, + const CryptoNote::account_base& minerAcc, uint64_t timestamp, uint64_t alreadyGeneratedCoins, + std::vector& blockSizes, const std::list& txList); + bool constructBlock(CryptoNote::Block& blk, const CryptoNote::account_base& minerAcc, uint64_t timestamp); + bool constructBlock(CryptoNote::Block& blk, const CryptoNote::Block& blkPrev, const CryptoNote::account_base& minerAcc, + const std::list& txList = std::list()); + + bool constructBlockManually(CryptoNote::Block& blk, const CryptoNote::Block& prevBlock, + const CryptoNote::account_base& minerAcc, int actualParams = bf_none, uint8_t majorVer = 0, uint8_t minorVer = 0, uint64_t timestamp = 0, const crypto::hash& prevId = crypto::hash(), - const cryptonote::difficulty_type& diffic = 1, const cryptonote::Transaction& minerTx = cryptonote::Transaction(), + const CryptoNote::difficulty_type& diffic = 1, const CryptoNote::Transaction& minerTx = CryptoNote::Transaction(), const std::vector& txHashes = std::vector(), size_t txsSizes = 0, uint64_t fee = 0); - bool constructBlockManuallyTx(cryptonote::Block& blk, const cryptonote::Block& prevBlock, - const cryptonote::account_base& minerAcc, const std::vector& txHashes, size_t txsSize); - bool constructMaxSizeBlock(cryptonote::Block& blk, const cryptonote::Block& blkPrev, - const cryptonote::account_base& minerAccount, size_t medianBlockCount = 0, - const std::list& txList = std::list()); + bool constructBlockManuallyTx(CryptoNote::Block& blk, const CryptoNote::Block& prevBlock, + const CryptoNote::account_base& minerAcc, const std::vector& txHashes, size_t txsSize); + bool constructMaxSizeBlock(CryptoNote::Block& blk, const CryptoNote::Block& blkPrev, + const CryptoNote::account_base& minerAccount, size_t medianBlockCount = 0, + const std::list& txList = std::list()); private: - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; std::unordered_map m_blocksInfo; }; -inline cryptonote::difficulty_type getTestDifficulty() { return 1; } -void fillNonce(cryptonote::Block& blk, const cryptonote::difficulty_type& diffic); +inline CryptoNote::difficulty_type getTestDifficulty() { return 1; } +void fillNonce(CryptoNote::Block& blk, const CryptoNote::difficulty_type& diffic); -bool constructMinerTxManually(const cryptonote::Currency& currency, size_t height, uint64_t alreadyGeneratedCoins, - const cryptonote::AccountPublicAddress& minerAddress, cryptonote::Transaction& tx, uint64_t fee, - cryptonote::KeyPair* pTxKey = 0); -bool constructMinerTxBySize(const cryptonote::Currency& currency, cryptonote::Transaction& minerTx, uint64_t height, - uint64_t alreadyGeneratedCoins, const cryptonote::AccountPublicAddress& minerAddress, +bool constructMinerTxManually(const CryptoNote::Currency& currency, uint32_t height, uint64_t alreadyGeneratedCoins, + const CryptoNote::AccountPublicAddress& minerAddress, CryptoNote::Transaction& tx, uint64_t fee, + CryptoNote::KeyPair* pTxKey = 0); +bool constructMinerTxBySize(const CryptoNote::Currency& currency, CryptoNote::Transaction& minerTx, uint32_t height, + uint64_t alreadyGeneratedCoins, const CryptoNote::AccountPublicAddress& minerAddress, std::vector& blockSizes, size_t targetTxSize, size_t targetBlockSize, uint64_t fee = 0, bool penalizeFee = false); diff --git a/tests/core_proxy/core_proxy.cpp b/tests/core_proxy/core_proxy.cpp deleted file mode 100644 index 55bd3acf7f..0000000000 --- a/tests/core_proxy/core_proxy.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -// node.cpp : Defines the entry point for the console application. -// - - -#include "include_base_utils.h" -#include "version.h" - -using namespace epee; - -#include -#include -using namespace std; - -#include -#include "cryptonote_core/CoreConfig.h" - -#include "common/command_line.h" -#include "console_handler.h" -#include "p2p/net_node.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "core_proxy.h" -#include "version.h" - -#if defined(WIN32) -#include -#endif - -namespace po = boost::program_options; -using namespace cryptonote; -using namespace crypto; - - -BOOST_CLASS_VERSION(nodetool::node_server >, 1); - -int main(int argc, char* argv[]) -{ - -#ifdef WIN32 - _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif - - TRY_ENTRY(); - - - string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - //log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str()); - - - po::options_description desc("Allowed options"); - // tools::get_default_data_dir() can't be called during static initialization - command_line::add_arg(desc, command_line::arg_data_dir, tools::get_default_data_dir()); - - cryptonote::CoreConfig::initOptions(desc); - nodetool::NetNodeConfig::initOptions(desc); - - po::variables_map vm; - bool r = command_line::handle_error_helper(desc, [&]() - { - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - return true; - }); - if (!r) - return 1; - - LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0); - LOG_PRINT("Node starting ...", LOG_LEVEL_0); - - - //create objects and link them - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - tests::proxy_core pr_core(currency); - cryptonote::t_cryptonote_protocol_handler cprotocol(pr_core, NULL); - nodetool::node_server > p2psrv(cprotocol); - cprotocol.set_p2p_endpoint(&p2psrv); - //pr_core.set_cryptonote_protocol(&cprotocol); - //daemon_cmmands_handler dch(p2psrv); - - //initialize objects - cryptonote::CoreConfig coreConfig; - coreConfig.init(vm); - nodetool::NetNodeConfig netNodeConfig; - netNodeConfig.init(vm); - - LOG_PRINT_L0("Initializing p2p server..."); - bool res = p2psrv.init(netNodeConfig, false); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); - LOG_PRINT_L0("P2p server initialized OK"); - - LOG_PRINT_L0("Initializing cryptonote protocol..."); - res = cprotocol.init(); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol."); - LOG_PRINT_L0("Cryptonote protocol initialized OK"); - - //initialize core here - LOG_PRINT_L0("Initializing proxy core..."); - res = pr_core.init(vm); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); - LOG_PRINT_L0("Core initialized OK"); - - LOG_PRINT_L0("Starting p2p net loop..."); - p2psrv.run(); - LOG_PRINT_L0("p2p net loop stopped"); - - //deinitialize components - LOG_PRINT_L0("Deinitializing core..."); - pr_core.deinit(); - LOG_PRINT_L0("Deinitializing cryptonote_protocol..."); - cprotocol.deinit(); - LOG_PRINT_L0("Deinitializing p2p..."); - p2psrv.deinit(); - - - //pr_core.set_cryptonote_protocol(NULL); - cprotocol.set_p2p_endpoint(NULL); - - - LOG_PRINT("Node stopped.", LOG_LEVEL_0); - return 0; - - CATCH_ENTRY_L0("main", 1); -} - -/* -string tx2str(const cryptonote::transaction& tx, const cryptonote::hash256& tx_hash, const cryptonote::hash256& tx_prefix_hash, const cryptonote::blobdata& blob) { - stringstream ss; - - ss << "{" << endl; - ss << "\tversion:" << tx.version << endl; - ss << "\tunlock_time:" << tx.unlockTime << endl; - ss << "\t" - - return ss.str(); -}*/ - -bool tests::proxy_core::handle_incoming_tx(const cryptonote::blobdata& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block) { - if (!keeped_by_block) - return true; - - crypto::hash tx_hash = null_hash; - crypto::hash tx_prefix_hash = null_hash; - Transaction tx; - - if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, tx_prefix_hash)) { - cerr << "WRONG TRANSACTION BLOB, Failed to parse, rejected" << endl; - return false; - } - - cout << "TX " << endl << endl; - cout << tx_hash << endl; - cout << tx_prefix_hash << endl; - cout << tx_blob.size() << endl; - //cout << string_tools::buff_to_hex_nodelimer(tx_blob) << endl << endl; - cout << obj_to_json_str(tx) << endl; - cout << endl << "ENDTX" << endl; - - return true; -} - -bool tests::proxy_core::handle_incoming_block_blob(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool control_miner, bool relay_block) { - Block b = AUTO_VAL_INIT(b); - - if (!parse_and_validate_block_from_blob(block_blob, b)) { - cerr << "Failed to parse and validate new block" << endl; - return false; - } - - crypto::hash h; - crypto::hash lh; - if (!get_block_longhash(m_cn_context, b, lh)) { - return false; - } - - cout << "BLOCK" << endl << endl; - cout << (h = get_block_hash(b)) << endl; - cout << lh << endl; - cout << get_transaction_hash(b.minerTx) << endl; - cout << ::get_object_blobsize(b.minerTx) << endl; - //cout << string_tools::buff_to_hex_nodelimer(block_blob) << endl; - cout << obj_to_json_str(b) << endl; - cout << endl << "ENDBLOCK" << endl << endl; - - return add_block(h, lh, b, block_to_blob(b)); -} - -bool tests::proxy_core::get_short_chain_history(std::list& ids) { - build_short_history(ids, m_lastblk); - return true; -} - -bool tests::proxy_core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { - height = 0; - top_id = get_block_hash(m_genesis); - return true; -} - -bool tests::proxy_core::init(const boost::program_options::variables_map& /*vm*/) { - m_genesis = m_currency.genesisBlock(); - crypto::hash h = m_currency.genesisBlockHash(); - crypto::hash lh; - if (!get_block_longhash(m_cn_context, m_genesis, lh)) { - return false; - } - add_block(h, lh, m_genesis, block_to_blob(m_genesis)); - return true; -} - -bool tests::proxy_core::have_block(const crypto::hash& id) { - if (m_hash2blkidx.end() == m_hash2blkidx.find(id)) - return false; - return true; -} - -void tests::proxy_core::build_short_history(std::list &m_history, const crypto::hash &m_start) { - m_history.push_front(get_block_hash(m_genesis)); - /*std::unordered_map::const_iterator cit = m_hash2blkidx.find(m_lastblk); - - do { - m_history.push_front(cit->first); - - size_t n = 1 << m_history.size(); - while (m_hash2blkidx.end() != cit && cryptonote::null_hash != cit->second.blk.prevId && n > 0) { - n--; - cit = m_hash2blkidx.find(cit->second.blk.prevId); - } - } while (m_hash2blkidx.end() != cit && get_block_hash(cit->second.blk) != cit->first);*/ -} - -bool tests::proxy_core::add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::Block &_blk, const cryptonote::blobdata &_blob) { - size_t height = 0; - - if (cryptonote::null_hash != _blk.prevId) { - std::unordered_map::const_iterator cit = m_hash2blkidx.find(_blk.prevId); - if (m_hash2blkidx.end() == cit) { - cerr << "ERROR: can't find previous block with id \"" << _blk.prevId << "\"" << endl; - return false; - } - - height = cit->second.height + 1; - } - - m_known_block_list.push_back(_id); - - block_index bi(height, _id, _longhash, _blk, _blob, txes); - m_hash2blkidx.insert(std::make_pair(_id, bi)); - txes.clear(); - m_lastblk = _id; - - return true; -} diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h deleted file mode 100644 index 785b2e40d0..0000000000 --- a/tests/core_proxy/core_proxy.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include - -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/verification_context.h" - -namespace tests -{ - struct block_index { - size_t height; - crypto::hash id; - crypto::hash longhash; - cryptonote::Block blk; - cryptonote::blobdata blob; - std::list txes; - - block_index() : height(0), id(cryptonote::null_hash), longhash(cryptonote::null_hash) { } - block_index(size_t _height, const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::Block &_blk, const cryptonote::blobdata &_blob, const std::list &_txes) - : height(_height), id(_id), longhash(_longhash), blk(_blk), blob(_blob), txes(_txes) { } - }; - - class proxy_core - { - const cryptonote::Currency& m_currency; - cryptonote::Block m_genesis; - std::list m_known_block_list; - std::unordered_map m_hash2blkidx; - - crypto::hash m_lastblk; - std::list txes; - - crypto::cn_context m_cn_context; - - bool add_block(const crypto::hash &_id, const crypto::hash &_longhash, const cryptonote::Block &_blk, const cryptonote::blobdata &_blob); - void build_short_history(std::list &m_history, const crypto::hash &m_start); - - - public: - proxy_core(const cryptonote::Currency& currency) : m_currency(currency) { - } - - void on_synchronized(){} - uint64_t get_current_blockchain_height(){return 1;} - const cryptonote::Currency& currency() const { return m_currency; } - bool init(const boost::program_options::variables_map& vm); - bool deinit(){return true;} - bool get_short_chain_history(std::list& ids); - bool get_stat_info(cryptonote::core_stat_info& st_inf){return true;} - bool have_block(const crypto::hash& id); - bool 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 handle_incoming_block_blob(const cryptonote::blobdata& block_blob, cryptonote::block_verification_context& bvc, bool control_miner, bool relay_block); - void pause_mining(){} - void update_block_template_and_resume_mining(){} - bool on_idle(){return true;} - bool find_blockchain_supplement(const std::list& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp){return true;} - bool handle_get_objects(cryptonote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote::cryptonote_connection_context& context){return true;} - }; -} diff --git a/tests/core_tests/TestGenerator.h b/tests/core_tests/TestGenerator.h index ee6274268e..c61a75f389 100644 --- a/tests/core_tests/TestGenerator.h +++ b/tests/core_tests/TestGenerator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,10 +21,13 @@ #include "cryptonote_core/Currency.h" #include "TransactionBuilder.h" +#include class TestGenerator { public: - TestGenerator(const cryptonote::Currency& currency, std::vector& eventsRef) : + TestGenerator( + const CryptoNote::Currency& currency, + std::vector& eventsRef) : generator(currency), events(eventsRef) { minerAccount.generate(); @@ -33,17 +36,17 @@ class TestGenerator { lastBlock = genesisBlock; } - const cryptonote::Currency& currency() const { return generator.currency(); } + const CryptoNote::Currency& currency() const { return generator.currency(); } - void makeNextBlock(const std::list& txs = std::list()) { - cryptonote::Block block; + void makeNextBlock(const std::list& txs = std::list()) { + CryptoNote::Block block; generator.constructBlock(block, lastBlock, minerAccount, txs); events.push_back(block); lastBlock = block; } - void makeNextBlock(const cryptonote::Transaction& tx) { - std::list txs; + void makeNextBlock(const CryptoNote::Transaction& tx) { + std::list txs; txs.push_back(tx); makeNextBlock(txs); } @@ -52,19 +55,19 @@ class TestGenerator { generateBlocks(currency().minedMoneyUnlockWindow()); } - void generateBlocks(size_t count, uint8_t majorVersion = cryptonote::BLOCK_MAJOR_VERSION_1) { + void generateBlocks(size_t count, uint8_t majorVersion = CryptoNote::BLOCK_MAJOR_VERSION_1) { while (count--) { - cryptonote::Block next; + CryptoNote::Block next; generator.constructBlockManually(next, lastBlock, minerAccount, test_generator::bf_major_ver, majorVersion); lastBlock = next; events.push_back(next); } } - TransactionBuilder createTxBuilder(const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee) { + TransactionBuilder createTxBuilder(const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee) { - std::vector sources; - std::vector destinations; + std::vector sources; + std::vector destinations; fillTxSourcesAndDestinations(sources, destinations, from, to, amount, fee); @@ -77,20 +80,20 @@ class TestGenerator { } void fillTxSourcesAndDestinations( - std::vector& sources, - std::vector& destinations, - const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix = 0) { + std::vector& sources, + std::vector& destinations, + const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix = 0) { fill_tx_sources_and_destinations(events, lastBlock, from, to, amount, fee, nmix, sources, destinations); } void constructTxToKey( - cryptonote::Transaction& tx, - const cryptonote::account_base& from, - const cryptonote::account_base& to, + CryptoNote::Transaction& tx, + const CryptoNote::account_base& from, + const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix = 0) { - construct_tx_to_key(events, tx, lastBlock, from, to, amount, fee, nmix); + construct_tx_to_key(logger, events, tx, lastBlock, from, to, amount, fee, nmix); } void addEvent(const test_event_entry& e) { @@ -111,9 +114,10 @@ class TestGenerator { addCallback("check_block_purged"); } + Logging::LoggerGroup logger; test_generator generator; - cryptonote::Block genesisBlock; - cryptonote::Block lastBlock; - cryptonote::account_base minerAccount; + CryptoNote::Block genesisBlock; + CryptoNote::Block lastBlock; + CryptoNote::account_base minerAccount; std::vector& events; }; diff --git a/tests/core_tests/TransactionBuilder.cpp b/tests/core_tests/TransactionBuilder.cpp index 6565b47750..1526198336 100644 --- a/tests/core_tests/TransactionBuilder.cpp +++ b/tests/core_tests/TransactionBuilder.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,23 +17,23 @@ #include "TransactionBuilder.h" -using namespace cryptonote; +using namespace CryptoNote; -TransactionBuilder::TransactionBuilder(const cryptonote::Currency& currency, uint64_t unlockTime) - : m_currency(currency), m_version(cryptonote::CURRENT_TRANSACTION_VERSION), m_unlockTime(unlockTime), m_txKey(KeyPair::generate()) {} +TransactionBuilder::TransactionBuilder(const CryptoNote::Currency& currency, uint64_t unlockTime) + : m_currency(currency), m_version(CryptoNote::CURRENT_TRANSACTION_VERSION), m_unlockTime(unlockTime), m_txKey(KeyPair::generate()) {} TransactionBuilder& TransactionBuilder::newTxKeys() { m_txKey = KeyPair::generate(); return *this; } -TransactionBuilder& TransactionBuilder::setTxKeys(const cryptonote::KeyPair& txKeys) { +TransactionBuilder& TransactionBuilder::setTxKeys(const CryptoNote::KeyPair& txKeys) { m_txKey = txKeys; return *this; } -TransactionBuilder& TransactionBuilder::setInput(const std::vector& sources, const cryptonote::account_keys& senderKeys) { +TransactionBuilder& TransactionBuilder::setInput(const std::vector& sources, const CryptoNote::account_keys& senderKeys) { m_sources = sources; m_senderKeys = senderKeys; return *this; @@ -44,12 +44,12 @@ TransactionBuilder& TransactionBuilder::addMultisignatureInput(const Multisignat return *this; } -TransactionBuilder& TransactionBuilder::setOutput(const std::vector& destinations) { +TransactionBuilder& TransactionBuilder::setOutput(const std::vector& destinations) { m_destinations = destinations; return *this; } -TransactionBuilder& TransactionBuilder::addOutput(const cryptonote::tx_destination_entry& dest) { +TransactionBuilder& TransactionBuilder::addOutput(const CryptoNote::tx_destination_entry& dest) { m_destinations.push_back(dest); return *this; } @@ -73,10 +73,10 @@ Transaction TransactionBuilder::build() const { Transaction tx; add_tx_pub_key_to_extra(tx, m_txKey.pub); - tx.version = m_version; + tx.version = static_cast(m_version); tx.unlockTime = m_unlockTime; - std::vector contexts; + std::vector contexts; fillInputs(tx, contexts); fillOutputs(tx); @@ -88,7 +88,7 @@ Transaction TransactionBuilder::build() const { return tx; } -void TransactionBuilder::fillInputs(Transaction& tx, std::vector& contexts) const { +void TransactionBuilder::fillInputs(Transaction& tx, std::vector& contexts) const { for (const tx_source_entry& src_entr : m_sources) { contexts.push_back(KeyPair()); KeyPair& in_ephemeral = contexts.back(); @@ -153,7 +153,7 @@ void TransactionBuilder::fillOutputs(Transaction& tx) const { } -void TransactionBuilder::signSources(const crypto::hash& prefixHash, const std::vector& contexts, Transaction& tx) const { +void TransactionBuilder::signSources(const crypto::hash& prefixHash, const std::vector& contexts, Transaction& tx) const { tx.signatures.clear(); diff --git a/tests/core_tests/TransactionBuilder.h b/tests/core_tests/TransactionBuilder.h index be77b2ca55..355548c20e 100644 --- a/tests/core_tests/TransactionBuilder.h +++ b/tests/core_tests/TransactionBuilder.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,42 +24,42 @@ class TransactionBuilder { public: - typedef std::vector KeysVector; + typedef std::vector KeysVector; typedef std::vector SignatureVector; typedef std::vector SignatureMultivector; struct MultisignatureSource { - cryptonote::TransactionInputMultisignature input; + CryptoNote::TransactionInputMultisignature input; KeysVector keys; crypto::public_key srcTxPubKey; size_t srcOutputIndex; }; - TransactionBuilder(const cryptonote::Currency& currency, uint64_t unlockTime = 0); + TransactionBuilder(const CryptoNote::Currency& currency, uint64_t unlockTime = 0); // regenerate transaction keys TransactionBuilder& newTxKeys(); - TransactionBuilder& setTxKeys(const cryptonote::KeyPair& txKeys); + TransactionBuilder& setTxKeys(const CryptoNote::KeyPair& txKeys); // inputs - TransactionBuilder& setInput(const std::vector& sources, const cryptonote::account_keys& senderKeys); + TransactionBuilder& setInput(const std::vector& sources, const CryptoNote::account_keys& senderKeys); TransactionBuilder& addMultisignatureInput(const MultisignatureSource& source); // outputs - TransactionBuilder& setOutput(const std::vector& destinations); - TransactionBuilder& addOutput(const cryptonote::tx_destination_entry& dest); + TransactionBuilder& setOutput(const std::vector& destinations); + TransactionBuilder& addOutput(const CryptoNote::tx_destination_entry& dest); TransactionBuilder& addMultisignatureOut(uint64_t amount, const KeysVector& keys, uint32_t required); - cryptonote::Transaction build() const; + CryptoNote::Transaction build() const; - std::vector m_sources; - std::vector m_destinations; + std::vector m_sources; + std::vector m_destinations; private: - void fillInputs(cryptonote::Transaction& tx, std::vector& contexts) const; - void fillOutputs(cryptonote::Transaction& tx) const; - void signSources(const crypto::hash& prefixHash, const std::vector& contexts, cryptonote::Transaction& tx) const; + void fillInputs(CryptoNote::Transaction& tx, std::vector& contexts) const; + void fillOutputs(CryptoNote::Transaction& tx) const; + void signSources(const crypto::hash& prefixHash, const std::vector& contexts, CryptoNote::Transaction& tx) const; struct MultisignatureDestination { uint64_t amount; @@ -67,13 +67,13 @@ class TransactionBuilder { KeysVector keys; }; - cryptonote::account_keys m_senderKeys; + CryptoNote::account_keys m_senderKeys; std::vector m_msigSources; std::vector m_msigDestinations; size_t m_version; uint64_t m_unlockTime; - cryptonote::KeyPair m_txKey; - const cryptonote::Currency& m_currency; + CryptoNote::KeyPair m_txKey; + const CryptoNote::Currency& m_currency; }; diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 99a370adde..98c6a33124 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include "misc_language.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; namespace { @@ -53,7 +53,7 @@ namespace gen_block_reward::gen_block_reward() : m_invalid_block_index(0) { - cryptonote::CurrencyBuilder currencyBuilder; + CryptoNote::CurrencyBuilder currencyBuilder(m_logger); currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); m_currency = currencyBuilder.currency(); @@ -116,21 +116,21 @@ bool gen_block_reward::generate(std::vector& events) const return false; // Test: fee increases block reward - Transaction tx_0(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * m_currency.minimumFee())); + Transaction tx_0(construct_tx_with_fee(m_logger, events, blk_5, miner_account, bob_account, MK_COINS(1), 3 * m_currency.minimumFee())); MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_account, tx_0); DO_CALLBACK(events, "mark_checked_block"); // Test: fee from all block transactions increase block reward std::list txs_0; - txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * m_currency.minimumFee())); - txs_0.push_back(construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * m_currency.minimumFee())); + txs_0.push_back(construct_tx_with_fee(m_logger, events, blk_5, miner_account, bob_account, MK_COINS(1), 5 * m_currency.minimumFee())); + txs_0.push_back(construct_tx_with_fee(m_logger, events, blk_5, miner_account, bob_account, MK_COINS(1), 7 * m_currency.minimumFee())); MAKE_NEXT_BLOCK_TX_LIST(events, blk_7, blk_6, miner_account, txs_0); DO_CALLBACK(events, "mark_checked_block"); // Test: block reward == transactions fee { - Transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * m_currency.minimumFee()); - Transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * m_currency.minimumFee()); + Transaction tx_1 = construct_tx_with_fee(m_logger, events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * m_currency.minimumFee()); + Transaction tx_2 = construct_tx_with_fee(m_logger, events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * m_currency.minimumFee()); size_t txs_1_size = get_object_blobsize(tx_1) + get_object_blobsize(tx_2); uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2); @@ -161,7 +161,7 @@ bool gen_block_reward::generate(std::vector& events) const return true; } -bool gen_block_reward::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/) +bool gen_block_reward::check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& /*blk*/) { if (m_invalid_block_index == event_idx) { @@ -174,19 +174,19 @@ bool gen_block_reward::check_block_verification_context(const cryptonote::block_ } } -bool gen_block_reward::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +bool gen_block_reward::mark_invalid_block(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_invalid_block_index = ev_index + 1; return true; } -bool gen_block_reward::mark_checked_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +bool gen_block_reward::mark_checked_block(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_checked_blocks_indices.push_back(ev_index - 1); return true; } -bool gen_block_reward::check_block_rewards(cryptonote::core& /*c*/, size_t /*ev_index*/, const std::vector& events) +bool gen_block_reward::check_block_rewards(CryptoNote::core& /*c*/, size_t /*ev_index*/, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_block_reward_without_txs::check_block_rewards"); diff --git a/tests/core_tests/block_reward.h b/tests/core_tests/block_reward.h index 564fbd6144..b3b7b608e2 100644 --- a/tests/core_tests/block_reward.h +++ b/tests/core_tests/block_reward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,11 +24,11 @@ struct gen_block_reward : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& blk); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& blk); - bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool mark_checked_block(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_block_rewards(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool mark_checked_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_block_rewards(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: size_t m_invalid_block_index; diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index d87dffe30c..e7d5b502e3 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,23 +19,23 @@ #include "TestGenerator.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; #define BLOCK_VALIDATION_INIT_GENERATE() \ GENERATE_ACCOUNT(miner_account); \ MAKE_GENESIS_BLOCK(events, blk_0, miner_account, 1338224400); namespace { - bool lift_up_difficulty(const cryptonote::Currency& currency, std::vector& events, + bool lift_up_difficulty(const CryptoNote::Currency& currency, std::vector& events, std::vector& timestamps, - std::vector& cummulative_difficulties, test_generator& generator, - size_t new_block_count, const cryptonote::Block blk_last, - const cryptonote::account_base& miner_account, uint8_t block_major_version) { - cryptonote::difficulty_type commulative_diffic = cummulative_difficulties.empty() ? 0 : cummulative_difficulties.back(); - cryptonote::Block blk_prev = blk_last; + std::vector& cummulative_difficulties, test_generator& generator, + size_t new_block_count, const CryptoNote::Block blk_last, + const CryptoNote::account_base& miner_account, uint8_t block_major_version) { + CryptoNote::difficulty_type commulative_diffic = cummulative_difficulties.empty() ? 0 : cummulative_difficulties.back(); + CryptoNote::Block blk_prev = blk_last; for (size_t i = 0; i < new_block_count; ++i) { - cryptonote::Block blk_next; - cryptonote::difficulty_type diffic = currency.nextDifficulty(timestamps, cummulative_difficulties); + CryptoNote::Block blk_next; + CryptoNote::difficulty_type diffic = currency.nextDifficulty(timestamps, cummulative_difficulties); if (!generator.constructBlockManually(blk_next, blk_prev, miner_account, test_generator::bf_major_ver | test_generator::bf_timestamp | test_generator::bf_diffic, block_major_version, 0, blk_prev.timestamp, crypto::hash(), diffic)) { @@ -57,16 +57,16 @@ namespace { return true; } - bool getParentBlockSize(const cryptonote::Block& block, size_t& size) { - auto serializer = cryptonote::makeParentBlockSerializer(block, false, false); - if (!cryptonote::get_object_blobsize(serializer, size)) { + bool getParentBlockSize(const CryptoNote::Block& block, size_t& size) { + auto serializer = CryptoNote::makeParentBlockSerializer(block, false, false); + if (!CryptoNote::get_object_blobsize(serializer, size)) { LOG_ERROR("Failed to get size of parent block"); return false; } return true; } - bool adjustParentBlockSize(cryptonote::Block& block, size_t targetSize) { + bool adjustParentBlockSize(CryptoNote::Block& block, size_t targetSize) { size_t parentBlockSize; if (!getParentBlockSize(block, parentBlockSize)) { return false; @@ -130,7 +130,7 @@ bool TestBlockMajorVersionRejected::generate(std::vector& even bool TestBlockBigMinorVersion::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - cryptonote::Block blk_1; + CryptoNote::Block blk_1; generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver, m_blockMajorVersion, BLOCK_MINOR_VERSION_0 + 1); @@ -223,7 +223,7 @@ bool gen_block_invalid_prev_id::generate(std::vector& events) return true; } -bool gen_block_invalid_prev_id::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/) +bool gen_block_invalid_prev_id::check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& /*blk*/) { if (1 == event_idx) return bvc.m_marked_as_orphaned && !bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; @@ -428,7 +428,7 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector& events destinations.push_back(de); Transaction tmp_tx; - if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tmp_tx, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tmp_tx, 0, m_logger)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_0); @@ -473,7 +473,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector destinations.push_back(de); Transaction tmp_tx; - if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tmp_tx, 0)) + if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tmp_tx, 0, m_logger)) return false; MAKE_MINER_TX_MANUALLY(miner_tx, blk_1); @@ -635,8 +635,8 @@ bool TestBlockCumulativeSizeExceedsLimit::generate(std::vector gen_block_invalid_binary_format::gen_block_invalid_binary_format(uint8_t blockMajorVersion) : m_corrupt_blocks_begin_idx(0), m_blockMajorVersion(blockMajorVersion) { - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : 0); + CryptoNote::CurrencyBuilder currencyBuilder(m_logger); + currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : 0); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK("check_all_blocks_purged", gen_block_invalid_binary_format::check_all_blocks_purged); @@ -710,8 +710,8 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev return true; } -bool gen_block_invalid_binary_format::check_block_verification_context(const cryptonote::block_verification_context& bvc, - size_t event_idx, const cryptonote::Block& blk) +bool gen_block_invalid_binary_format::check_block_verification_context(const CryptoNote::block_verification_context& bvc, + size_t event_idx, const CryptoNote::Block& blk) { if (0 == m_corrupt_blocks_begin_idx || event_idx < m_corrupt_blocks_begin_idx) { @@ -723,13 +723,13 @@ bool gen_block_invalid_binary_format::check_block_verification_context(const cry } } -bool gen_block_invalid_binary_format::corrupt_blocks_boundary(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_block_invalid_binary_format::corrupt_blocks_boundary(CryptoNote::core& c, size_t ev_index, const std::vector& events) { m_corrupt_blocks_begin_idx = ev_index + 1; return true; } -bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_block_invalid_binary_format::check_all_blocks_purged(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_block_invalid_binary_format::check_all_blocks_purged"); @@ -742,7 +742,7 @@ bool gen_block_invalid_binary_format::check_all_blocks_purged(cryptonote::core& bool TestMaxSizeOfParentBlock::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - cryptonote::Block blk_1; + CryptoNote::Block blk_1; generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, BLOCK_MAJOR_VERSION_2); if (!adjustParentBlockSize(blk_1, 2 * 1024)) { return false; @@ -757,7 +757,7 @@ bool TestMaxSizeOfParentBlock::generate(std::vector& events) c bool TestBigParentBlock::generate(std::vector& events) const { BLOCK_VALIDATION_INIT_GENERATE(); - cryptonote::Block blk_1; + CryptoNote::Block blk_1; generator.constructBlockManually(blk_1, blk_0, miner_account, test_generator::bf_major_ver, BLOCK_MAJOR_VERSION_2); if (!adjustParentBlockSize(blk_1, 2 * 1024 + 1)) { return false; @@ -773,10 +773,10 @@ bool TestBigParentBlock::generate(std::vector& events) const { namespace { template - bool GenerateAndMutateBlockV2(const cryptonote::Currency& currency, std::vector& events, const std::string& callback, MutateFunc mf) { + bool GenerateAndMutateBlockV2(const CryptoNote::Currency& currency, std::vector& events, const std::string& callback, MutateFunc mf) { TestGenerator bg(currency, events); - cryptonote::Block blk_1; + CryptoNote::Block blk_1; bg.generator.constructBlockManually( blk_1, bg.lastBlock, bg.minerAccount, test_generator::bf_major_ver, BLOCK_MAJOR_VERSION_2); @@ -790,21 +790,21 @@ namespace } bool TestBlock2ExtraEmpty::generate(std::vector& events) const { - return GenerateAndMutateBlockV2(m_currency, events, "check_block_purged", [](cryptonote::Block& blk) { + return GenerateAndMutateBlockV2(m_currency, events, "check_block_purged", [](CryptoNote::Block& blk) { blk.parentBlock.minerTx.extra.clear(); }); } bool TestBlock2ExtraWithoutMMTag::generate(std::vector& events) const { - return GenerateAndMutateBlockV2(m_currency, events, "check_block_purged", [](cryptonote::Block& blk) { + return GenerateAndMutateBlockV2(m_currency, events, "check_block_purged", [](CryptoNote::Block& blk) { blk.parentBlock.minerTx.extra.clear(); - cryptonote::add_extra_nonce_to_tx_extra(blk.parentBlock.minerTx.extra, "0xdeadbeef"); + CryptoNote::add_extra_nonce_to_tx_extra(blk.parentBlock.minerTx.extra, "0xdeadbeef"); }); } bool TestBlock2ExtraWithGarbage::generate(std::vector& events) const { - return GenerateAndMutateBlockV2(m_currency, events, "check_block_accepted", [](cryptonote::Block& blk) { - cryptonote::add_extra_nonce_to_tx_extra(blk.parentBlock.minerTx.extra, "0xdeadbeef"); + return GenerateAndMutateBlockV2(m_currency, events, "check_block_accepted", [](CryptoNote::Block& blk) { + CryptoNote::add_extra_nonce_to_tx_extra(blk.parentBlock.minerTx.extra, "0xdeadbeef"); blk.parentBlock.minerTx.extra.push_back(0xde); blk.parentBlock.minerTx.extra.push_back(0xad); blk.parentBlock.minerTx.extra.push_back(0xbe); diff --git a/tests/core_tests/block_validation.h b/tests/core_tests/block_validation.h index eed583b5e3..1eb57db0a6 100644 --- a/tests/core_tests/block_validation.h +++ b/tests/core_tests/block_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,24 +19,24 @@ #include "chaingen.h" -const uint64_t UNDEF_HEIGHT = static_cast(cryptonote::UpgradeDetectorBase::UNDEF_HEIGHT); +const uint64_t UNDEF_HEIGHT = static_cast(CryptoNote::UpgradeDetectorBase::UNDEF_HEIGHT); class CheckBlockPurged : public test_chain_unit_base { public: CheckBlockPurged(size_t invalidBlockIdx, uint8_t blockMajorVersion) : m_invalidBlockIdx(invalidBlockIdx), m_blockMajorVersion(blockMajorVersion) { - assert(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_2); + assert(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_2); - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + CryptoNote::CurrencyBuilder currencyBuilder(m_logger); + currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK("check_block_purged", CheckBlockPurged::check_block_purged); REGISTER_CALLBACK("markInvalidBlock", CheckBlockPurged::markInvalidBlock); } - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t eventIdx, const cryptonote::Block& /*blk*/) { + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t eventIdx, const CryptoNote::Block& /*blk*/) { if (m_invalidBlockIdx == eventIdx) { return bvc.m_verifivation_failed; } else { @@ -44,7 +44,7 @@ class CheckBlockPurged : public test_chain_unit_base { } } - bool check_block_purged(cryptonote::core& c, size_t eventIdx, const std::vector& events) { + bool check_block_purged(CryptoNote::core& c, size_t eventIdx, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("CheckBlockPurged::check_block_purged"); CHECK_TEST_CONDITION(m_invalidBlockIdx < eventIdx); @@ -54,7 +54,7 @@ class CheckBlockPurged : public test_chain_unit_base { return true; } - bool markInvalidBlock(cryptonote::core& c, size_t eventIdx, const std::vector& events) { + bool markInvalidBlock(CryptoNote::core& c, size_t eventIdx, const std::vector& events) { m_invalidBlockIdx = eventIdx + 1; return true; } @@ -69,16 +69,16 @@ struct CheckBlockAccepted : public test_chain_unit_base { CheckBlockAccepted(size_t expectedBlockchainHeight, uint8_t blockMajorVersion) : m_expectedBlockchainHeight(expectedBlockchainHeight), m_blockMajorVersion(blockMajorVersion) { - assert(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_2); + assert(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 || blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_2); - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + CryptoNote::CurrencyBuilder currencyBuilder(m_logger); + currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK("check_block_accepted", CheckBlockAccepted::check_block_accepted); } - bool check_block_accepted(cryptonote::core& c, size_t /*eventIdx*/, const std::vector& /*events*/) { + bool check_block_accepted(CryptoNote::core& c, size_t /*eventIdx*/, const std::vector& /*events*/) { DEFINE_TESTS_ERROR_CONTEXT("CheckBlockAccepted::check_block_accepted"); CHECK_EQ(0, c.get_pool_transactions_count()); @@ -159,7 +159,7 @@ struct gen_block_invalid_prev_id : public CheckBlockPurged : CheckBlockPurged(1, blockMajorVersion) {} bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& /*blk*/); }; struct gen_block_invalid_nonce : public CheckBlockPurged @@ -298,8 +298,8 @@ struct gen_block_is_too_big : public CheckBlockPurged { gen_block_is_too_big(uint8_t blockMajorVersion) : CheckBlockPurged(1, blockMajorVersion) { - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.upgradeHeight(blockMajorVersion == cryptonote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); + CryptoNote::CurrencyBuilder currencyBuilder(m_logger); + currencyBuilder.upgradeHeight(blockMajorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1 ? UNDEF_HEIGHT : UINT64_C(0)); currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); m_currency = currencyBuilder.currency(); } @@ -320,9 +320,9 @@ struct gen_block_invalid_binary_format : public test_chain_unit_base gen_block_invalid_binary_format(uint8_t blockMajorVersion); bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*blk*/); - bool check_all_blocks_purged(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool corrupt_blocks_boundary(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& /*blk*/); + bool check_all_blocks_purged(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool corrupt_blocks_boundary(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: const uint8_t m_blockMajorVersion; @@ -330,14 +330,14 @@ struct gen_block_invalid_binary_format : public test_chain_unit_base }; struct TestMaxSizeOfParentBlock : public CheckBlockAccepted { - TestMaxSizeOfParentBlock() : CheckBlockAccepted(2, cryptonote::BLOCK_MAJOR_VERSION_2) { + TestMaxSizeOfParentBlock() : CheckBlockAccepted(2, CryptoNote::BLOCK_MAJOR_VERSION_2) { } bool generate(std::vector& events) const; }; struct TestBigParentBlock : public CheckBlockPurged { - TestBigParentBlock() : CheckBlockPurged(1, cryptonote::BLOCK_MAJOR_VERSION_2) { + TestBigParentBlock() : CheckBlockPurged(1, CryptoNote::BLOCK_MAJOR_VERSION_2) { } bool generate(std::vector& events) const; @@ -345,21 +345,21 @@ struct TestBigParentBlock : public CheckBlockPurged { struct TestBlock2ExtraEmpty : public CheckBlockPurged { - TestBlock2ExtraEmpty() : CheckBlockPurged(1, cryptonote::BLOCK_MAJOR_VERSION_2) {} + TestBlock2ExtraEmpty() : CheckBlockPurged(1, CryptoNote::BLOCK_MAJOR_VERSION_2) {} bool generate(std::vector& events) const; }; struct TestBlock2ExtraWithoutMMTag : public CheckBlockPurged { - TestBlock2ExtraWithoutMMTag() : CheckBlockPurged(1, cryptonote::BLOCK_MAJOR_VERSION_2) {} + TestBlock2ExtraWithoutMMTag() : CheckBlockPurged(1, CryptoNote::BLOCK_MAJOR_VERSION_2) {} bool generate(std::vector& events) const; }; struct TestBlock2ExtraWithGarbage : public CheckBlockAccepted { - TestBlock2ExtraWithGarbage() : CheckBlockAccepted(2, cryptonote::BLOCK_MAJOR_VERSION_2) {} + TestBlock2ExtraWithGarbage() : CheckBlockAccepted(2, CryptoNote::BLOCK_MAJOR_VERSION_2) {} bool generate(std::vector& events) const; }; diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index 28719f42a1..f721029d2d 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ using namespace std; using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; gen_simple_chain_split_1::gen_simple_chain_split_1() @@ -146,99 +146,99 @@ bool gen_simple_chain_split_1::generate(std::vector &events) c return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_mempool_2(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_mempool_2(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_mempool_2"); CHECK_TEST_CONDITION(c.get_pool_transactions_count() == 2); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_mempool_1(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_mempool_1(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_mempool_1"); CHECK_TEST_CONDITION(c.get_pool_transactions_count() == 3); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_split_not_switched(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched"); //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 2); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_split_not_switched2(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_split_not_switched2(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched2"); //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 9); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 9); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[8]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_split_switched(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched"); //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 10); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 10); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[14]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[14]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 3); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_split_not_switched_back(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_split_not_switched_back(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_not_switched_back"); //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 14); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 14); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[19]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[19]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_split_switched_back_1(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_split_switched_back_1(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched_back_1"); //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height()== 15); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 15); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[26]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[26]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8); return true; }//----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_split_switched_back_2(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_split_switched_back_2(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_split_switched_back_2"); //check height CHECK_TEST_CONDITION(c.get_current_blockchain_height() == 16); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 16); - CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[28]))); + CHECK_TEST_CONDITION(c.get_tail_id() == get_block_hash(boost::get(events[28]))); CHECK_TEST_CONDITION(c.get_alternative_blocks_count() == 8); return true; } //----------------------------------------------------------------------------------------------------- /* -bool gen_simple_chain_split_1::check_orphaned_chain_1(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_chain_1(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_1"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 2); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_switched_to_alternative(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_switched_to_alternative(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_switched_to_alternative"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 0); @@ -247,14 +247,14 @@ bool gen_simple_chain_split_1::check_orphaned_switched_to_alternative(cryptonote return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_chain_2(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_chain_2(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_2"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 4); return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_switched_to_main(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_switched_to_main(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_switched_to_main"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 0); @@ -263,7 +263,7 @@ bool gen_simple_chain_split_1::check_orphaned_switched_to_main(cryptonote::core& return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_chain_38(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_chain_38(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_38"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 6); @@ -272,7 +272,7 @@ bool gen_simple_chain_split_1::check_orphaned_chain_38(cryptonote::core& c, size return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_chain_39(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_chain_39(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_39"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 4); @@ -281,7 +281,7 @@ bool gen_simple_chain_split_1::check_orphaned_chain_39(cryptonote::core& c, size return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_chain_40(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_chain_40(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_40"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 5); @@ -290,7 +290,7 @@ bool gen_simple_chain_split_1::check_orphaned_chain_40(cryptonote::core& c, size return true; } //----------------------------------------------------------------------------------------------------- -bool gen_simple_chain_split_1::check_orphaned_chain_41(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_split_1::check_orphaned_chain_41(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("gen_simple_chain_split_1::check_orphaned_chain_41"); CHECK_TEST_CONDITION(c.get_orphaned_by_prev_blocks_count() == 0); @@ -299,4 +299,4 @@ bool gen_simple_chain_split_1::check_orphaned_chain_41(cryptonote::core& c, size return true; }*/ -//----------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------------------------------- diff --git a/tests/core_tests/chain_split_1.h b/tests/core_tests/chain_split_1.h index 5d128ab1e3..cfd61fddbe 100644 --- a/tests/core_tests/chain_split_1.h +++ b/tests/core_tests/chain_split_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,21 +26,21 @@ class gen_simple_chain_split_1 : public test_chain_unit_base public: gen_simple_chain_split_1(); bool generate(std::vector &events) const; - bool check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_split_not_switched2(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_split_not_switched_back(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_split_switched_back_1(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_split_switched_back_2(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_mempool_1(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_mempool_2(cryptonote::core& c, size_t ev_index, const std::vector &events); - /*bool check_orphaned_chain_1(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_switched_to_alternative(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_chain_2(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_switched_to_main(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_chain_38(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_chain_39(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_chain_40(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool check_orphaned_chain_41(cryptonote::core& c, size_t ev_index, const std::vector &events); */ + bool check_split_not_switched(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_split_not_switched2(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_split_switched(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_split_not_switched_back(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_split_switched_back_1(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_split_switched_back_2(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_mempool_1(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_mempool_2(CryptoNote::core& c, size_t ev_index, const std::vector &events); + /*bool check_orphaned_chain_1(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_switched_to_alternative(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_chain_2(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_switched_to_main(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_chain_38(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_chain_39(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_chain_40(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool check_orphaned_chain_41(CryptoNote::core& c, size_t ev_index, const std::vector &events); */ private: }; diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index c82b865441..3c2b5ae50a 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,7 +18,7 @@ #include "chain_switch_1.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; gen_chain_switch_1::gen_chain_switch_1() @@ -105,7 +105,7 @@ bool gen_chain_switch_1::generate(std::vector& events) const //----------------------------------------------------------------------------------------------------- -bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_chain_switch_1::check_split_not_switched(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_chain_switch_1::check_split_not_switched"); @@ -122,7 +122,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev CHECK_EQ(2, c.get_alternative_blocks_count()); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -147,7 +147,7 @@ bool gen_chain_switch_1::check_split_not_switched(cryptonote::core& c, size_t ev } //----------------------------------------------------------------------------------------------------- -bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_chain_switch_1::check_split_switched(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_chain_switch_1::check_split_switched"); @@ -171,7 +171,7 @@ bool gen_chain_switch_1::check_split_switched(cryptonote::core& c, size_t ev_ind CHECK_TEST_CONDITION(m_chain_1.end() != std::find(m_chain_1.begin(), m_chain_1.end(), b)); } - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 6a46754396..33b06bfc36 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -28,16 +28,16 @@ class gen_chain_switch_1 : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_split_not_switched(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_split_switched(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_split_not_switched(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_split_switched(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: - std::list m_chain_1; + std::list 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; + 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::list m_tx_pool; }; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 58ba58b6e3..e60fcb8afd 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -28,7 +28,7 @@ #include "include_base_utils.h" #include "misc_language.h" -#include "common/command_line.h" +#include "Common/command_line.h" #include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_basic_impl.h" @@ -41,21 +41,20 @@ using namespace std; using namespace epee; -using namespace cryptonote; - +using namespace CryptoNote; struct output_index { - const cryptonote::TransactionOutputTarget out; + const CryptoNote::TransactionOutputTarget out; uint64_t amount; size_t blk_height; // block height size_t tx_no; // index of transaction in block size_t out_no; // index of out in transaction size_t idx; bool spent; - const cryptonote::Block *p_blk; - const cryptonote::Transaction *p_tx; + const CryptoNote::Block *p_blk; + const CryptoNote::Transaction *p_tx; - output_index(const cryptonote::TransactionOutputTarget &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const cryptonote::Block *_pb, const cryptonote::Transaction *_pt) + output_index(const CryptoNote::TransactionOutputTarget &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const CryptoNote::Block *_pb, const CryptoNote::Transaction *_pt) : out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { } output_index(const output_index &other) @@ -100,7 +99,7 @@ namespace } } -bool init_output_indices(map_output_idx_t& outs, std::map >& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) { +bool init_output_indices(map_output_idx_t& outs, std::map >& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const CryptoNote::account_base& from) { BOOST_FOREACH (const Block& blk, blockchain) { vector vtx; @@ -143,7 +142,7 @@ bool init_output_indices(map_output_idx_t& outs, std::map& blockchain, const map_hash2tx_t& mtx, const cryptonote::account_base& from) { +bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector& blockchain, const map_hash2tx_t& mtx, const CryptoNote::account_base& from) { for (const map_output_t::value_type& o: outs_mine) { for (size_t i = 0; i < o.second.size(); ++i) { @@ -209,12 +208,12 @@ bool fill_output_entries(std::vector& out_indices, size_t sender_o } bool fill_tx_sources(std::vector& sources, const std::vector& events, - const Block& blk_head, const cryptonote::account_base& from, uint64_t amount, size_t nmix) + const Block& blk_head, const CryptoNote::account_base& from, uint64_t amount, size_t nmix) { map_output_idx_t outs; map_output_t outs_mine; - std::vector blockchain; + std::vector blockchain; map_hash2tx_t mtx; if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head))) return false; @@ -237,7 +236,7 @@ bool fill_tx_sources(std::vector& sources, const std::vector& sources, const std::vector& events, const Block& blk_head, - const cryptonote::account_base& from, const cryptonote::account_base& to, + const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix, std::vector& sources, std::vector& destinations) { @@ -292,27 +291,27 @@ void fill_tx_sources_and_destinations(const std::vector& event } } -bool construct_tx_to_key(const std::vector& events, cryptonote::Transaction& tx, const Block& blk_head, - const cryptonote::account_base& from, const cryptonote::account_base& to, uint64_t amount, +bool construct_tx_to_key(Logging::ILogger& logger, const std::vector& events, CryptoNote::Transaction& tx, const Block& blk_head, + const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix) { vector sources; vector destinations; fill_tx_sources_and_destinations(events, blk_head, from, to, amount, fee, nmix, sources, destinations); - return construct_tx(from.get_keys(), sources, destinations, std::vector(), tx, 0); + return construct_tx(from.get_keys(), sources, destinations, std::vector(), tx, 0, logger); } -Transaction construct_tx_with_fee(std::vector& events, const Block& blk_head, +Transaction construct_tx_with_fee(Logging::ILogger& logger, std::vector& events, const Block& blk_head, const account_base& acc_from, const account_base& acc_to, uint64_t amount, uint64_t fee) { Transaction tx; - construct_tx_to_key(events, tx, blk_head, acc_from, acc_to, amount, fee, 0); + construct_tx_to_key(logger, events, tx, blk_head, acc_from, acc_to, amount, fee, 0); events.push_back(tx); return tx; } -uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx) { +uint64_t get_balance(const CryptoNote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx) { uint64_t res = 0; std::map > outs; std::map > outs_mine; @@ -338,7 +337,7 @@ uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs) +void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs) { std::unordered_set confirmed_hashes; for (const Block& blk : blockchain) @@ -358,7 +357,7 @@ void get_confirmed_txs(const std::vector& blockchain, const m } } -bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { +bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head) { std::unordered_map block_index; BOOST_FOREACH(const test_event_entry& ev, events) { @@ -392,7 +391,7 @@ bool find_block_chain(const std::vector& events, std::vector &events) +bool test_chain_unit_base::verify(const std::string& cb_name, CryptoNote::core& c, size_t ev_index, const std::vector &events) { auto cb_it = m_callbacks.find(cb_name); if(cb_it == m_callbacks.end()) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 8744efd95e..3251489cb2 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,14 +21,17 @@ #include #include "cryptonote_core/CoreConfig.h" -#include "common/boost_serialization_helper.h" -#include "common/command_line.h" +#include "Common/boost_serialization_helper.h" +#include "Common/command_line.h" #include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_core.h" #include "../TestGenerator/TestGenerator.h" +#include +#include + namespace concolor { inline std::basic_ostream >& bright_white(std::basic_ostream >& ostr) @@ -91,12 +94,12 @@ struct serialized_object { serialized_object() { } - serialized_object(const cryptonote::blobdata& a_data) + serialized_object(const CryptoNote::blobdata& a_data) : data(a_data) { } - cryptonote::blobdata data; + CryptoNote::blobdata data; BEGIN_SERIALIZE_OBJECT() FIELD(data) END_SERIALIZE() @@ -111,8 +114,8 @@ struct serialized_object } }; -typedef serialized_object serialized_block; -typedef serialized_object serialized_transaction; +typedef serialized_object serialized_block; +typedef serialized_object serialized_transaction; struct event_visitor_settings { @@ -142,62 +145,64 @@ struct event_visitor_settings }; VARIANT_TAG(binary_archive, callback_entry, 0xcb); -VARIANT_TAG(binary_archive, cryptonote::account_base, 0xcc); +VARIANT_TAG(binary_archive, CryptoNote::account_base, 0xcc); 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 std::unordered_map map_hash2tx_t; +typedef boost::variant test_event_entry; +typedef std::unordered_map map_hash2tx_t; class test_chain_unit_base: boost::noncopyable { public: test_chain_unit_base() : - m_currency(cryptonote::CurrencyBuilder().currency()) { + m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()) { } - typedef std::function &events)> verify_callback; + typedef std::function &events)> verify_callback; typedef std::map callbacks_map; - const cryptonote::Currency& currency() const; + const CryptoNote::Currency& currency() const; void register_callback(const std::string& cb_name, verify_callback cb); - bool verify(const std::string& cb_name, cryptonote::core& c, size_t ev_index, const std::vector &events); + bool verify(const std::string& cb_name, CryptoNote::core& c, size_t ev_index, const std::vector &events); protected: - cryptonote::Currency m_currency; + + mutable Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; private: callbacks_map m_callbacks; }; -bool construct_tx_to_key(const std::vector& events, cryptonote::Transaction& tx, - const cryptonote::Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, +bool construct_tx_to_key(Logging::ILogger& logger, const std::vector& events, CryptoNote::Transaction& tx, + const CryptoNote::Block& blk_head, const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix); -cryptonote::Transaction construct_tx_with_fee(std::vector& events, const cryptonote::Block& blk_head, - const cryptonote::account_base& acc_from, const cryptonote::account_base& acc_to, +CryptoNote::Transaction construct_tx_with_fee(Logging::ILogger& logger, std::vector& events, const CryptoNote::Block& blk_head, + const CryptoNote::account_base& acc_from, const CryptoNote::account_base& acc_to, uint64_t amount, uint64_t fee); -void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); -bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); -void fill_tx_sources_and_destinations(const std::vector& events, const cryptonote::Block& blk_head, - const cryptonote::account_base& from, const cryptonote::account_base& to, +void get_confirmed_txs(const std::vector& blockchain, const map_hash2tx_t& mtx, map_hash2tx_t& confirmed_txs); +bool find_block_chain(const std::vector& events, std::vector& blockchain, map_hash2tx_t& mtx, const crypto::hash& head); +void fill_tx_sources_and_destinations(const std::vector& events, const CryptoNote::Block& blk_head, + const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, size_t nmix, - std::vector& sources, - std::vector& destinations); -uint64_t get_balance(const cryptonote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx); + std::vector& sources, + std::vector& destinations); +uint64_t get_balance(const CryptoNote::account_base& addr, const std::vector& blockchain, const map_hash2tx_t& mtx); //-------------------------------------------------------------------------- template -auto do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::Transaction& tx, t_test_class& validator, int) +auto do_check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_index, const CryptoNote::Transaction& tx, t_test_class& validator, int) -> decltype(validator.check_tx_verification_context(tvc, tx_added, event_index, tx)) { return validator.check_tx_verification_context(tvc, tx_added, event_index, tx); } //-------------------------------------------------------------------------- template -bool do_check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t /*event_index*/, const cryptonote::Transaction& /*tx*/, t_test_class&, long) +bool do_check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t /*event_index*/, const CryptoNote::Transaction& /*tx*/, t_test_class&, long) { // Default block verification context check if (tvc.m_verifivation_failed) @@ -206,21 +211,21 @@ bool do_check_tx_verification_context(const cryptonote::tx_verification_context& } //-------------------------------------------------------------------------- template -bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_index, const cryptonote::Transaction& tx, t_test_class& validator) +bool check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_index, const CryptoNote::Transaction& tx, t_test_class& validator) { // SFINAE in action return do_check_tx_verification_context(tvc, tx_added, event_index, tx, 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) +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)) { return validator.check_block_verification_context(bvc, event_index, blk); } //-------------------------------------------------------------------------- template -bool do_check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t /*event_index*/, const cryptonote::Block& /*blk*/, t_test_class&, long) +bool do_check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t /*event_index*/, const CryptoNote::Block& /*blk*/, t_test_class&, long) { // Default block verification context check if (bvc.m_verifivation_failed) @@ -229,7 +234,7 @@ bool do_check_block_verification_context(const cryptonote::block_verification_co } //-------------------------------------------------------------------------- template -bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_index, const cryptonote::Block& blk, t_test_class& validator) +bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_index, const CryptoNote::Block& blk, t_test_class& validator) { // SFINAE in action return do_check_block_verification_context(bvc, event_index, blk, validator, 0); @@ -242,7 +247,7 @@ template struct push_core_event_visitor: public boost::static_visitor { private: - cryptonote::core& m_c; + CryptoNote::core& m_c; const std::vector& m_events; t_test_class& m_validator; size_t m_ev_index; @@ -250,7 +255,7 @@ struct push_core_event_visitor: public boost::static_visitor bool m_txs_keeped_by_block; public: - push_core_event_visitor(cryptonote::core& c, const std::vector& events, t_test_class& validator) + push_core_event_visitor(CryptoNote::core& c, const std::vector& events, t_test_class& validator) : m_c(c) , m_events(events) , m_validator(validator) @@ -276,11 +281,11 @@ struct push_core_event_visitor: public boost::static_visitor return true; } - bool operator()(const cryptonote::Transaction& tx) const + bool operator()(const CryptoNote::Transaction& tx) const { - log_event("cryptonote::Transaction"); + log_event("CryptoNote::Transaction"); - cryptonote::tx_verification_context tvc = boost::value_initialized(); + CryptoNote::tx_verification_context tvc = boost::value_initialized(); size_t pool_size = m_c.get_pool_transactions_count(); m_c.handle_incoming_tx(t_serializable_object_to_blob(tx), tvc, m_txs_keeped_by_block); bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count(); @@ -289,11 +294,11 @@ struct push_core_event_visitor: public boost::static_visitor return true; } - bool operator()(const cryptonote::Block& b) const + bool operator()(const CryptoNote::Block& b) const { - log_event("cryptonote::Block"); + log_event("CryptoNote::Block"); - cryptonote::block_verification_context bvc = boost::value_initialized(); + CryptoNote::block_verification_context bvc = boost::value_initialized(); m_c.handle_incoming_block_blob(t_serializable_object_to_blob(b), bvc, false, false); bool r = check_block_verification_context(bvc, m_ev_index, b, m_validator); CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed"); @@ -306,9 +311,9 @@ struct push_core_event_visitor: public boost::static_visitor return m_validator.verify(cb.callback_name, m_c, m_ev_index, m_events); } - bool operator()(const cryptonote::account_base& ab) const + bool operator()(const CryptoNote::account_base& ab) const { - log_event("cryptonote::account_base"); + log_event("CryptoNote::account_base"); return true; } @@ -316,17 +321,17 @@ struct push_core_event_visitor: public boost::static_visitor { log_event("serialized_block"); - cryptonote::block_verification_context bvc = boost::value_initialized(); + CryptoNote::block_verification_context bvc = boost::value_initialized(); m_c.handle_incoming_block_blob(sr_block.data, bvc, false, false); - cryptonote::Block blk; + CryptoNote::Block blk; std::stringstream ss; ss << sr_block.data; binary_archive ba(ss); ::serialization::serialize(ba, blk); if (!ss.good()) { - blk = cryptonote::Block(); + blk = CryptoNote::Block(); } bool r = check_block_verification_context(bvc, m_ev_index, blk, m_validator); CHECK_AND_NO_ASSERT_MES(r, false, "block verification context check failed"); @@ -337,19 +342,19 @@ struct push_core_event_visitor: public boost::static_visitor { log_event("serialized_transaction"); - cryptonote::tx_verification_context tvc = boost::value_initialized();; + CryptoNote::tx_verification_context tvc = boost::value_initialized();; size_t pool_size = m_c.get_pool_transactions_count(); m_c.handle_incoming_tx(sr_tx.data, tvc, m_txs_keeped_by_block); bool tx_added = pool_size + 1 == m_c.get_pool_transactions_count(); - cryptonote::Transaction tx; + CryptoNote::Transaction tx; std::stringstream ss; ss << sr_tx.data; binary_archive ba(ss); ::serialization::serialize(ba, tx); if (!ss.good()) { - tx = cryptonote::Transaction(); + tx = CryptoNote::Transaction(); } bool r = check_tx_verification_context(tvc, tx_added, m_ev_index, tx, m_validator); @@ -365,14 +370,14 @@ struct push_core_event_visitor: public boost::static_visitor }; //-------------------------------------------------------------------------- template -inline bool replay_events_through_core(cryptonote::core& cr, const std::vector& events, t_test_class& validator) +inline bool replay_events_through_core(CryptoNote::core& cr, const std::vector& events, t_test_class& validator) { TRY_ENTRY(); //init core here - CHECK_AND_ASSERT_MES(typeid(cryptonote::Block) == events[0].type(), false, "First event must be genesis block creation"); - cr.set_genesis_block(boost::get(events[0])); + CHECK_AND_ASSERT_MES(typeid(CryptoNote::Block) == events[0].type(), false, "First event must be genesis block creation"); + cr.set_genesis_block(boost::get(events[0])); bool r = true; push_core_event_visitor visitor(cr, events, validator); @@ -391,7 +396,7 @@ template inline bool do_replay_events(std::vector& events, t_test_class& validator) { boost::program_options::options_description desc("Allowed options"); - cryptonote::CoreConfig::initOptions(desc); + CryptoNote::CoreConfig::initOptions(desc); command_line::add_arg(desc, command_line::arg_data_dir); boost::program_options::variables_map vm; bool r = command_line::handle_error_helper(desc, [&]() @@ -403,12 +408,13 @@ inline bool do_replay_events(std::vector& events, t_test_class if (!r) return false; - cryptonote::CoreConfig coreConfig; + Logging::ConsoleLogger logger; + CryptoNote::CoreConfig coreConfig; coreConfig.init(vm); - cryptonote::MinerConfig emptyMinerConfig; + CryptoNote::MinerConfig emptyMinerConfig; - cryptonote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects - cryptonote::core c(validator.currency(), &pr); + CryptoNote::cryptonote_protocol_stub pr; //TODO: stub only for this kind of test, make real validation of relayed objects + CryptoNote::core c(validator.currency(), &pr, logger); if (!c.init(coreConfig, emptyMinerConfig, false)) { std::cout << concolor::magenta << "Failed to init core" << concolor::normal << std::endl; @@ -432,11 +438,11 @@ inline bool do_replay_file(const std::string& filename) } //-------------------------------------------------------------------------- #define GENERATE_ACCOUNT(account) \ - cryptonote::account_base account; \ + CryptoNote::account_base account; \ account.generate(); #define MAKE_ACCOUNT(VEC_EVENTS, account) \ - cryptonote::account_base account; \ + CryptoNote::account_base account; \ account.generate(); \ VEC_EVENTS.push_back(account); @@ -455,33 +461,33 @@ inline bool do_replay_file(const std::string& filename) #define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \ test_generator generator(this->m_currency); \ - cryptonote::Block BLK_NAME; \ + CryptoNote::Block BLK_NAME; \ generator.constructBlock(BLK_NAME, MINER_ACC, TS); \ VEC_EVENTS.push_back(BLK_NAME); #define MAKE_NEXT_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \ - cryptonote::Block BLK_NAME; \ + CryptoNote::Block BLK_NAME; \ generator.constructBlock(BLK_NAME, PREV_BLOCK, MINER_ACC); \ VEC_EVENTS.push_back(BLK_NAME); #define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \ - cryptonote::Block BLK_NAME; \ + CryptoNote::Block BLK_NAME; \ { \ - std::list tx_list; \ + std::list tx_list; \ tx_list.push_back(TX1); \ generator.constructBlock(BLK_NAME, PREV_BLOCK, MINER_ACC, tx_list); \ } \ VEC_EVENTS.push_back(BLK_NAME); #define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \ - cryptonote::Block BLK_NAME; \ + CryptoNote::Block BLK_NAME; \ generator.constructBlock(BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \ VEC_EVENTS.push_back(BLK_NAME); #define REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, COUNT) \ - cryptonote::Block BLK_NAME; \ + CryptoNote::Block BLK_NAME; \ { \ - cryptonote::Block blk_last = PREV_BLOCK; \ + CryptoNote::Block blk_last = PREV_BLOCK; \ for (size_t i = 0; i < COUNT; ++i) \ { \ MAKE_NEXT_BLOCK(VEC_EVENTS, blk, blk_last, MINER_ACC); \ @@ -494,16 +500,16 @@ inline bool do_replay_file(const std::string& filename) REWIND_BLOCKS_N(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, this->m_currency.minedMoneyUnlockWindow()) #define MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ - cryptonote::Transaction TX_NAME; \ - construct_tx_to_key(VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, this->m_currency.minimumFee(), NMIX); \ + CryptoNote::Transaction TX_NAME; \ + construct_tx_to_key(this->m_logger, VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, this->m_currency.minimumFee(), NMIX); \ VEC_EVENTS.push_back(TX_NAME); #define MAKE_TX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, 0, HEAD) #define MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD) \ { \ - cryptonote::Transaction t; \ - construct_tx_to_key(VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, this->m_currency.minimumFee(), NMIX); \ + CryptoNote::Transaction t; \ + construct_tx_to_key(this->m_logger, VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, this->m_currency.minimumFee(), NMIX); \ SET_NAME.push_back(t); \ VEC_EVENTS.push_back(t); \ } @@ -511,7 +517,7 @@ inline bool do_replay_file(const std::string& filename) #define MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) MAKE_TX_MIX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, 0, HEAD) #define MAKE_TX_LIST_START(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD) \ - std::list SET_NAME; \ + std::list SET_NAME; \ MAKE_TX_LIST(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, HEAD); #define MAKE_MINER_TX_AND_KEY_MANUALLY(TX, BLK, KEY) \ @@ -617,9 +623,21 @@ bool GenerateAndPlay(const char* testname, GenClassT&& g) { } \ } +template +struct Pow10 { + static const uint64_t value = 10 * Pow10::value; +}; + +template<> +struct Pow10<0> { + static const uint64_t value = 1; +}; + +const uint64_t COIN = Pow10::value; + #define QUOTEME(x) #x #define DEFINE_TESTS_ERROR_CONTEXT(text) const char* perr_context = text; #define CHECK_TEST_CONDITION(cond) CHECK_AND_ASSERT_MES(cond, false, "[" << perr_context << "] failed: \"" << QUOTEME(cond) << "\"") #define CHECK_EQ(v1, v2) CHECK_AND_ASSERT_MES(v1 == v2, false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " == " << QUOTEME(v2) << "\", " << v1 << " != " << v2) #define CHECK_NOT_EQ(v1, v2) CHECK_AND_ASSERT_MES(!(v1 == v2), false, "[" << perr_context << "] failed: \"" << QUOTEME(v1) << " != " << QUOTEME(v2) << "\", " << v1 << " == " << v2) -#define MK_COINS(amount) (UINT64_C(amount) * cryptonote::parameters::COIN) +#define MK_COINS(amount) (UINT64_C(amount) * COIN) diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index d32a01acb1..e115005592 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ using namespace std; using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; //////// // class one_block; @@ -41,20 +41,20 @@ bool one_block::generate(std::vector &events) return true; } -bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool one_block::verify_1(CryptoNote::core& c, size_t ev_index, const std::vector &events) { DEFINE_TESTS_ERROR_CONTEXT("one_block::verify_1"); - alice = boost::get(events[1]); + alice = boost::get(events[1]); // check balances - //std::vector chain; + //std::vector chain; //map_hash2tx_t mtx; - //CHECK_TEST_CONDITION(find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[1])))); + //CHECK_TEST_CONDITION(find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[1])))); //CHECK_TEST_CONDITION(get_block_reward(0) == get_balance(alice, events, chain, mtx)); // check height - std::list blocks; + std::list blocks; std::list outs; bool r = c.get_blocks(0, 100, blocks); //c.get_outs(100, outs); @@ -62,7 +62,7 @@ bool one_block::verify_1(cryptonote::core& c, size_t ev_index, const std::vector CHECK_TEST_CONDITION(blocks.size() == 1); //CHECK_TEST_CONDITION(outs.size() == blocks.size()); CHECK_TEST_CONDITION(c.get_blockchain_total_transactions() == 1); - CHECK_TEST_CONDITION(blocks.back() == boost::get(events[0])); + CHECK_TEST_CONDITION(blocks.back() == boost::get(events[0])); return true; } @@ -90,9 +90,9 @@ bool gen_simple_chain_001::generate(std::vector &events) MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner); //MAKE_TX(events, tx_0, first_miner_account, alice, 151, blk_2); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; - /*bool r = */find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[3]))); + /*bool r = */find_block_chain(events, chain, mtx, get_block_hash(boost::get(events[3]))); std::cout << "BALANCE = " << get_balance(miner, chain, mtx) << std::endl; REWIND_BLOCKS(events, blk_2r, blk_2, miner); @@ -116,7 +116,7 @@ bool gen_simple_chain_001::generate(std::vector &events) //MAKE_BLOCK_TX1(events, blk_3, 3, get_block_hash(blk_0), get_test_target(), first_miner_account, ts_start + 10, tx_0); //DO_CALLBACK(events, "verify_callback_2"); -/* std::vector chain; +/* std::vector chain; map_hash2tx_t mtx; if (!find_block_chain(events, chain, mtx, get_block_hash(blk_6))) throw; @@ -126,12 +126,12 @@ bool gen_simple_chain_001::generate(std::vector &events) return true; } -bool gen_simple_chain_001::verify_callback_1(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_001::verify_callback_1(CryptoNote::core& c, size_t ev_index, const std::vector &events) { return true; } -bool gen_simple_chain_001::verify_callback_2(cryptonote::core& c, size_t ev_index, const std::vector &events) +bool gen_simple_chain_001::verify_callback_2(CryptoNote::core& c, size_t ev_index, const std::vector &events) { return true; } diff --git a/tests/core_tests/chaingen001.h b/tests/core_tests/chaingen001.h index 63de1d74f2..9d73049367 100644 --- a/tests/core_tests/chaingen001.h +++ b/tests/core_tests/chaingen001.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,15 +23,15 @@ class gen_simple_chain_001: public test_chain_unit_base public: gen_simple_chain_001(); bool generate(std::vector &events); - bool verify_callback_1(cryptonote::core& c, size_t ev_index, const std::vector &events); - bool verify_callback_2(cryptonote::core& c, size_t ev_index, const std::vector &events); + bool verify_callback_1(CryptoNote::core& c, size_t ev_index, const std::vector &events); + bool verify_callback_2(CryptoNote::core& c, size_t ev_index, const std::vector &events); }; class one_block: public test_chain_unit_base { - cryptonote::account_base alice; + CryptoNote::account_base alice; public: one_block(); bool generate(std::vector &events); - bool verify_1(cryptonote::core& c, size_t ev_index, const std::vector &events); + bool verify_1(CryptoNote::core& c, size_t ev_index, const std::vector &events); }; diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 49ff96f8f1..1700d0b67e 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #include "chaingen.h" -#include "common/command_line.h" +#include "Common/command_line.h" #include "block_reward.h" #include "block_validation.h" @@ -46,7 +46,7 @@ namespace int main(int argc, char* argv[]) { TRY_ENTRY(); - epee::string_tools::set_module_name_and_folder(argv[0]); + // epee::string_tools::set_module_name_and_folder(argv[0]); //set up logging options epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3); @@ -94,8 +94,8 @@ int main(int argc, char* argv[]) else if (command_line::get_arg(vm, arg_generate_and_play_test_data)) { #define GENERATE_AND_PLAY_EX_2VER(TestCase) \ - GENERATE_AND_PLAY_EX(TestCase(cryptonote::BLOCK_MAJOR_VERSION_1)) \ - GENERATE_AND_PLAY_EX(TestCase(cryptonote::BLOCK_MAJOR_VERSION_2)) + GENERATE_AND_PLAY_EX(TestCase(CryptoNote::BLOCK_MAJOR_VERSION_1)) \ + GENERATE_AND_PLAY_EX(TestCase(CryptoNote::BLOCK_MAJOR_VERSION_2)) GENERATE_AND_PLAY(gen_simple_chain_001); GENERATE_AND_PLAY(gen_simple_chain_split_1); @@ -107,9 +107,9 @@ int main(int argc, char* argv[]) // Block verification tests GENERATE_AND_PLAY_EX_2VER(TestBlockMajorVersionAccepted); - GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(cryptonote::BLOCK_MAJOR_VERSION_1, cryptonote::BLOCK_MAJOR_VERSION_2)); - GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(cryptonote::BLOCK_MAJOR_VERSION_2, cryptonote::BLOCK_MAJOR_VERSION_1)); - GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(cryptonote::BLOCK_MAJOR_VERSION_2, cryptonote::BLOCK_MAJOR_VERSION_2 + 1)); + GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(CryptoNote::BLOCK_MAJOR_VERSION_1, CryptoNote::BLOCK_MAJOR_VERSION_2)); + GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(CryptoNote::BLOCK_MAJOR_VERSION_2, CryptoNote::BLOCK_MAJOR_VERSION_1)); + GENERATE_AND_PLAY_EX(TestBlockMajorVersionRejected(CryptoNote::BLOCK_MAJOR_VERSION_2, CryptoNote::BLOCK_MAJOR_VERSION_2 + 1)); GENERATE_AND_PLAY_EX_2VER(TestBlockBigMinorVersion); GENERATE_AND_PLAY_EX_2VER(gen_block_ts_not_checked); GENERATE_AND_PLAY_EX_2VER(gen_block_ts_in_past); @@ -134,7 +134,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_EX_2VER(gen_block_has_invalid_tx); GENERATE_AND_PLAY_EX_2VER(gen_block_is_too_big); GENERATE_AND_PLAY_EX_2VER(TestBlockCumulativeSizeExceedsLimit); - GENERATE_AND_PLAY_EX_2VER(gen_block_invalid_binary_format); // Takes up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10 + //GENERATE_AND_PLAY_EX_2VER(gen_block_invalid_binary_format); // Takes up to 30 minutes, if CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW == 10 GENERATE_AND_PLAY(TestMaxSizeOfParentBlock); GENERATE_AND_PLAY(TestBigParentBlock); diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 400925a0a2..7a23917cd4 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,7 +19,7 @@ #include "TestGenerator.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; //====================================================================================================================== @@ -56,7 +56,7 @@ bool gen_double_spend_in_different_chains::generate(std::vector& events) +bool gen_double_spend_in_different_chains::check_double_spend(CryptoNote::core& c, size_t /*ev_index*/, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_double_spend_in_different_chains::check_double_spend"); @@ -70,10 +70,10 @@ bool gen_double_spend_in_different_chains::check_double_spend(cryptonote::core& CHECK_EQ(1, c.get_pool_transactions_count()); CHECK_EQ(1, c.get_alternative_blocks_count()); - cryptonote::account_base bob_account = boost::get(events[1]); - cryptonote::account_base alice_account = boost::get(events[2]); + CryptoNote::account_base bob_account = boost::get(events[1]); + CryptoNote::account_base alice_account = boost::get(events[2]); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -102,7 +102,7 @@ DoubleSpendBase::DoubleSpendBase() : REGISTER_CALLBACK_METHOD(DoubleSpendBase, check_double_spend); } -bool DoubleSpendBase::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& /*tx*/) +bool DoubleSpendBase::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; @@ -110,7 +110,7 @@ bool DoubleSpendBase::check_tx_verification_context(const cryptonote::tx_verific return !tvc.m_verifivation_failed && tx_added; } -bool DoubleSpendBase::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*block*/) +bool DoubleSpendBase::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; @@ -118,25 +118,25 @@ bool DoubleSpendBase::check_block_verification_context(const cryptonote::block_v return !bvc.m_verifivation_failed; } -bool DoubleSpendBase::mark_last_valid_block(cryptonote::core& c, size_t /*ev_index*/, const std::vector& /*events*/) +bool DoubleSpendBase::mark_last_valid_block(CryptoNote::core& c, size_t /*ev_index*/, const std::vector& /*events*/) { m_last_valid_block = c.get_blockchain_storage().get_tail_id(); return true; } -bool DoubleSpendBase::mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +bool DoubleSpendBase::mark_invalid_tx(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_invalid_tx_index = ev_index + 1; return true; } -bool DoubleSpendBase::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +bool DoubleSpendBase::mark_invalid_block(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_invalid_block_index = ev_index + 1; return true; } -bool DoubleSpendBase::check_double_spend(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events) +bool DoubleSpendBase::check_double_spend(CryptoNote::core& c, size_t /*ev_index*/, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("DoubleSpendBase::check_double_spend"); CHECK_EQ(m_last_valid_block, c.get_blockchain_storage().get_tail_id()); diff --git a/tests/core_tests/double_spend.h b/tests/core_tests/double_spend.h index 7a87beba86..9e52b66182 100644 --- a/tests/core_tests/double_spend.h +++ b/tests/core_tests/double_spend.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -30,16 +30,16 @@ class gen_double_spend_base : public test_chain_unit_base gen_double_spend_base(); - bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); + bool check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const CryptoNote::Transaction& tx); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& block); - bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool mark_invalid_tx(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_last_valid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_tx(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_double_spend(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: - cryptonote::Block m_last_valid_block; + CryptoNote::Block m_last_valid_block; size_t m_invalid_tx_index; size_t m_invalid_block_index; }; @@ -128,7 +128,7 @@ class gen_double_spend_in_different_chains : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_double_spend(CryptoNote::core& c, size_t ev_index, const std::vector& events); }; @@ -144,13 +144,13 @@ class DoubleSpendBase : public test_chain_unit_base DoubleSpendBase(); - bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); + bool check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const CryptoNote::Transaction& tx); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& block); - bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool mark_invalid_tx(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool mark_invalid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_double_spend(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_last_valid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_tx(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool mark_invalid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_double_spend(CryptoNote::core& c, size_t ev_index, const std::vector& events); TestGenerator prepare(std::vector& events) const; TransactionBuilder createBobToAliceTx() const; @@ -158,9 +158,9 @@ class DoubleSpendBase : public test_chain_unit_base protected: - cryptonote::account_base m_bob_account; - cryptonote::account_base m_alice_account; - cryptonote::KeyPair m_outputTxKey; + CryptoNote::account_base m_bob_account; + CryptoNote::account_base m_alice_account; + CryptoNote::KeyPair m_outputTxKey; private: @@ -204,11 +204,11 @@ struct MultiSigTx_DoubleSpendAltChainSameBlock : public DoubleSpendBase MultiSigTx_DoubleSpendAltChainSameBlock(bool txsKeepedByBlock); - bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx) { + bool check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const CryptoNote::Transaction& tx) { return true; } - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block) { + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& block) { return true; } diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index aa9c2320fe..5e50f41ee9 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + // Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. @@ -31,7 +48,7 @@ gen_double_spend_base::gen_double_spend_base() } template -bool gen_double_spend_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& /*tx*/) +bool gen_double_spend_base::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; @@ -40,7 +57,7 @@ bool gen_double_spend_base::check_tx_verification_context(const c } template -bool gen_double_spend_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*block*/) +bool gen_double_spend_base::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; @@ -49,9 +66,9 @@ 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*/) +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::list 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(); @@ -59,21 +76,21 @@ bool gen_double_spend_base::mark_last_valid_block(cryptonote::cor } template -bool gen_double_spend_base::mark_invalid_tx(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +bool gen_double_spend_base::mark_invalid_tx(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_invalid_tx_index = ev_index + 1; return true; } template -bool gen_double_spend_base::mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) +bool gen_double_spend_base::mark_invalid_block(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_invalid_block_index = ev_index + 1; return true; } template -bool gen_double_spend_base::check_double_spend(cryptonote::core& c, size_t /*ev_index*/, const std::vector& events) +bool gen_double_spend_base::check_double_spend(CryptoNote::core& c, size_t /*ev_index*/, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_double_spend_base::check_double_spend"); @@ -83,19 +100,19 @@ 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::list block_list; bool r = c.get_blocks(0, 100 + 2 * this->m_currency.minedMoneyUnlockWindow(), block_list); CHECK_TEST_CONDITION(r); CHECK_TEST_CONDITION(m_last_valid_block == block_list.back()); CHECK_EQ(concrete_test::expected_pool_txs_count, c.get_pool_transactions_count()); - cryptonote::account_base bob_account = boost::get(events[1]); - cryptonote::account_base alice_account = boost::get(events[2]); + CryptoNote::account_base bob_account = boost::get(events[1]); + CryptoNote::account_base alice_account = boost::get(events[2]); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; - std::vector blocks(block_list.begin(), block_list.end()); + std::vector blocks(block_list.begin(), block_list.end()); r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); CHECK_EQ(concrete_test::expected_bob_balance, get_balance(bob_account, blocks, mtx)); @@ -112,10 +129,10 @@ bool gen_double_spend_in_tx::generate(std::vector sources; - cryptonote::tx_source_entry se; + std::vector sources; + CryptoNote::tx_source_entry se; se.amount = tx_0.vout[0].amount; - se.outputs.push_back(std::make_pair(0, boost::get(tx_0.vout[0].target).key)); + se.outputs.push_back(std::make_pair(0, boost::get(tx_0.vout[0].target).key)); se.real_output = 0; se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0); se.real_output_in_tx_index = 0; @@ -123,14 +140,14 @@ bool gen_double_spend_in_tx::generate(std::vectorm_currency.minimumFee(); - std::vector destinations; + std::vector destinations; destinations.push_back(de); - cryptonote::Transaction tx_1; - if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector(), tx_1, 0)) + CryptoNote::Transaction tx_1; + if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector(), tx_1, 0, this->m_logger)) return false; SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); @@ -152,7 +169,7 @@ bool gen_double_spend_in_the_same_block::generate(std::vect SET_EVENT_VISITOR_SETT(events, event_visitor_settings::set_txs_keeped_by_block, txs_keeped_by_block); MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); - cryptonote::Transaction tx_1 = txs_1.front(); + CryptoNote::Transaction tx_1 = txs_1.front(); auto tx_1_idx = events.size() - 1; // Remove tx_1, it is being inserted back a little later events.pop_back(); @@ -214,7 +231,7 @@ bool gen_double_spend_in_alt_chain_in_the_same_block::gener // Alt chain MAKE_TX_LIST_START(events, txs_1, bob_account, alice_account, send_amount - this->m_currency.minimumFee(), blk_1); - cryptonote::Transaction tx_1 = txs_1.front(); + CryptoNote::Transaction tx_1 = txs_1.front(); auto tx_1_idx = events.size() - 1; // Remove tx_1, it is being inserted back a little later events.pop_back(); diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index e1ddeb6f48..4815ccc74a 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,7 +18,7 @@ #include "integer_overflow.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; namespace { @@ -41,11 +41,11 @@ namespace miner_tx.vout.push_back(out2); } - void append_tx_source_entry(std::vector& sources, const Transaction& tx, size_t out_idx) + void append_tx_source_entry(std::vector& sources, const Transaction& tx, size_t out_idx) { - cryptonote::tx_source_entry se; + CryptoNote::tx_source_entry se; se.amount = tx.vout[out_idx].amount; - se.outputs.push_back(std::make_pair(0, boost::get(tx.vout[out_idx].target).key)); + se.outputs.push_back(std::make_pair(0, boost::get(tx.vout[out_idx].target).key)); se.real_output = 0; se.real_out_tx_key = get_tx_pub_key_from_extra(tx); se.real_output_in_tx_index = out_idx; @@ -62,17 +62,17 @@ gen_uint_overflow_base::gen_uint_overflow_base() REGISTER_CALLBACK_METHOD(gen_uint_overflow_1, mark_last_valid_block); } -bool gen_uint_overflow_base::check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& /*tx*/) +bool gen_uint_overflow_base::check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const CryptoNote::Transaction& /*tx*/) { return m_last_valid_block_event_idx < event_idx ? !tx_added && tvc.m_verifivation_failed : tx_added && !tvc.m_verifivation_failed; } -bool gen_uint_overflow_base::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*block*/) +bool gen_uint_overflow_base::check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& /*block*/) { return m_last_valid_block_event_idx < event_idx ? bvc.m_verifivation_failed | bvc.m_marked_as_orphaned : !bvc.m_verifivation_failed; } -bool gen_uint_overflow_base::mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_uint_overflow_base::mark_last_valid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events) { m_last_valid_block_event_idx = ev_index - 1; return true; @@ -113,10 +113,10 @@ bool gen_uint_overflow_1::generate(std::vector& events) const REWIND_BLOCKS(events, blk_3r, blk_3, miner_account); // Problem 2. total_fee overflow, block_reward overflow - std::list txs_1; + std::list txs_1; // Create txs with huge fee - txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), m_currency.moneySupply() - MK_COINS(1))); - txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_COINS(1), m_currency.moneySupply() - MK_COINS(1))); + txs_1.push_back(construct_tx_with_fee(m_logger, events, blk_3, bob_account, alice_account, MK_COINS(1), m_currency.moneySupply() - MK_COINS(1))); + txs_1.push_back(construct_tx_with_fee(m_logger, events, blk_3, bob_account, alice_account, MK_COINS(1), m_currency.moneySupply() - MK_COINS(1))); MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_1); return true; @@ -136,7 +136,7 @@ bool gen_uint_overflow_2::generate(std::vector& events) const DO_CALLBACK(events, "mark_last_valid_block"); // Problem 1. Regular tx outputs overflow - std::vector sources; + std::vector sources; for (size_t i = 0; i < blk_0.minerTx.vout.size(); ++i) { if (m_currency.minimumFee() < blk_0.minerTx.vout[i].amount) @@ -150,15 +150,15 @@ bool gen_uint_overflow_2::generate(std::vector& events) const return false; } - std::vector destinations; + std::vector destinations; const AccountPublicAddress& bob_addr = bob_account.get_keys().m_account_address; destinations.push_back(tx_destination_entry(m_currency.moneySupply(), bob_addr)); destinations.push_back(tx_destination_entry(m_currency.moneySupply() - 1, bob_addr)); // sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + m_currency.minimumFee() destinations.push_back(tx_destination_entry(sources.front().amount - m_currency.moneySupply() - m_currency.moneySupply() + 1 - m_currency.minimumFee(), bob_addr)); - cryptonote::Transaction tx_1; - if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tx_1, 0)) + CryptoNote::Transaction tx_1; + if (!construct_tx(miner_account.get_keys(), sources, destinations, std::vector(), tx_1, 0, m_logger)) return false; events.push_back(tx_1); @@ -177,14 +177,14 @@ bool gen_uint_overflow_2::generate(std::vector& events) const } destinations.clear(); - cryptonote::tx_destination_entry de; + CryptoNote::tx_destination_entry de; de.addr = alice_account.get_keys().m_account_address; de.amount = m_currency.moneySupply() - m_currency.minimumFee(); destinations.push_back(de); destinations.push_back(de); - cryptonote::Transaction tx_2; - if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector(), tx_2, 0)) + CryptoNote::Transaction tx_2; + if (!construct_tx(bob_account.get_keys(), sources, destinations, std::vector(), tx_2, 0, m_logger)) return false; events.push_back(tx_2); diff --git a/tests/core_tests/integer_overflow.h b/tests/core_tests/integer_overflow.h index 368ac4de18..0d8b3baea5 100644 --- a/tests/core_tests/integer_overflow.h +++ b/tests/core_tests/integer_overflow.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,10 +22,10 @@ struct gen_uint_overflow_base : public test_chain_unit_base { gen_uint_overflow_base(); - bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); + bool check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const CryptoNote::Transaction& tx); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& block); - bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool mark_last_valid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: size_t m_last_valid_block_event_idx; diff --git a/tests/core_tests/random_outs.cpp b/tests/core_tests/random_outs.cpp index 55a61330da..a858420728 100644 --- a/tests/core_tests/random_outs.cpp +++ b/tests/core_tests/random_outs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -54,21 +54,21 @@ bool GetRandomOutputs::generate(std::vector& events) const { return true; } -bool GetRandomOutputs::request(cryptonote::core& c, uint64_t amount, size_t mixin, cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp) { - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request req; +bool GetRandomOutputs::request(CryptoNote::core& c, uint64_t amount, size_t mixin, CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp) { + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request req; req.amounts.push_back(amount); req.outs_count = mixin; - resp = boost::value_initialized(); + resp = boost::value_initialized(); return c.get_random_outs_for_amounts(req, resp); } #define CHECK(cond) if((cond) == false) { LOG_ERROR("Condition "#cond" failed"); return false; } -bool GetRandomOutputs::checkHalfUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events) { - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response resp; +bool GetRandomOutputs::checkHalfUnlocked(CryptoNote::core& c, size_t ev_index, const std::vector& events) { + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response resp; auto amount = MK_COINS(1); auto unlocked = m_currency.minedMoneyUnlockWindow() / 2 + 1; @@ -91,8 +91,8 @@ bool GetRandomOutputs::checkHalfUnlocked(cryptonote::core& c, size_t ev_index, c return true; } -bool GetRandomOutputs::checkFullyUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events) { - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response resp; +bool GetRandomOutputs::checkFullyUnlocked(CryptoNote::core& c, size_t ev_index, const std::vector& events) { + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response resp; auto amount = MK_COINS(1); auto unlocked = m_currency.minedMoneyUnlockWindow() + 1; diff --git a/tests/core_tests/random_outs.h b/tests/core_tests/random_outs.h index 08ae2e3b8a..193342a2c2 100644 --- a/tests/core_tests/random_outs.h +++ b/tests/core_tests/random_outs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,18 +23,18 @@ struct GetRandomOutputs : public test_chain_unit_base { GetRandomOutputs(); - // bool check_tx_verification_context(const cryptonote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const cryptonote::Transaction& tx); - // bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& block); - // bool mark_last_valid_block(cryptonote::core& c, size_t ev_index, const std::vector& events); + // bool check_tx_verification_context(const CryptoNote::tx_verification_context& tvc, bool tx_added, size_t event_idx, const CryptoNote::Transaction& tx); + // bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t event_idx, const CryptoNote::Block& block); + // bool mark_last_valid_block(CryptoNote::core& c, size_t ev_index, const std::vector& events); bool generate(std::vector& events) const; private: - bool checkHalfUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool checkFullyUnlocked(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool checkHalfUnlocked(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool checkFullyUnlocked(CryptoNote::core& c, size_t ev_index, const std::vector& events); - bool request(cryptonote::core& c, uint64_t amount, size_t mixin, cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp); + bool request(CryptoNote::core& c, uint64_t amount, size_t mixin, CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp); }; diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index b30598ff52..1002b8a378 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,7 +18,7 @@ #include "ring_signature_1.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; //////// @@ -80,7 +80,7 @@ bool gen_ring_signature_1::generate(std::vector& events) const return true; } -bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_ring_signature_1::check_balances_1(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_1"); @@ -91,7 +91,7 @@ bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -101,7 +101,7 @@ bool gen_ring_signature_1::check_balances_1(cryptonote::core& c, size_t ev_index return true; } -bool gen_ring_signature_1::check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_ring_signature_1::check_balances_2(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_1::check_balances_2"); @@ -109,7 +109,7 @@ bool gen_ring_signature_1::check_balances_2(cryptonote::core& c, size_t ev_index bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -161,7 +161,7 @@ bool gen_ring_signature_2::generate(std::vector& events) const return true; } -bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_ring_signature_2::check_balances_1(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_1"); @@ -172,7 +172,7 @@ bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -182,7 +182,7 @@ bool gen_ring_signature_2::check_balances_1(cryptonote::core& c, size_t ev_index return true; } -bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_ring_signature_2::check_balances_2(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_2::check_balances_2"); @@ -190,7 +190,7 @@ bool gen_ring_signature_2::check_balances_2(cryptonote::core& c, size_t ev_index bool r = c.get_blocks(0, 100 + 2 * m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -256,7 +256,7 @@ bool gen_ring_signature_big::generate(std::vector& events) con MAKE_NEXT_BLOCK_TX_LIST(events, blk_i, blocks.back(), miner_account, txs_blk_i); blocks.push_back(blk_i); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; bool r = find_block_chain(events, chain, mtx, get_block_hash(blk_i)); CHECK_AND_NO_ASSERT_MES(r, false, "failed to call find_block_chain"); @@ -271,7 +271,7 @@ bool gen_ring_signature_big::generate(std::vector& events) con return true; } -bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_ring_signature_big::check_balances_1(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_1"); @@ -282,7 +282,7 @@ bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_ind bool r = c.get_blocks(0, 2 * m_test_size + m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); @@ -299,7 +299,7 @@ bool gen_ring_signature_big::check_balances_1(cryptonote::core& c, size_t ev_ind return true; } -bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector& events) +bool gen_ring_signature_big::check_balances_2(CryptoNote::core& c, size_t ev_index, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_ring_signature_big::check_balances_2"); @@ -307,7 +307,7 @@ bool gen_ring_signature_big::check_balances_2(cryptonote::core& c, size_t ev_ind bool r = c.get_blocks(0, 2 * m_test_size + m_currency.minedMoneyUnlockWindow(), blocks); CHECK_TEST_CONDITION(r); - std::vector chain; + std::vector chain; map_hash2tx_t mtx; r = find_block_chain(events, chain, mtx, get_block_hash(blocks.back())); CHECK_TEST_CONDITION(r); diff --git a/tests/core_tests/ring_signature_1.h b/tests/core_tests/ring_signature_1.h index b7f4c38a93..9389a3f41c 100644 --- a/tests/core_tests/ring_signature_1.h +++ b/tests/core_tests/ring_signature_1.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -28,12 +28,12 @@ class gen_ring_signature_1 : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_balances_1(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_balances_2(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: - cryptonote::account_base m_bob_account; - cryptonote::account_base m_alice_account; + CryptoNote::account_base m_bob_account; + CryptoNote::account_base m_alice_account; }; @@ -47,12 +47,12 @@ class gen_ring_signature_2 : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_balances_1(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_balances_2(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: - cryptonote::account_base m_bob_account; - cryptonote::account_base m_alice_account; + CryptoNote::account_base m_bob_account; + CryptoNote::account_base m_alice_account; }; @@ -66,13 +66,13 @@ class gen_ring_signature_big : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_balances_1(cryptonote::core& c, size_t ev_index, const std::vector& events); - bool check_balances_2(cryptonote::core& c, size_t ev_index, const std::vector& events); + bool check_balances_1(CryptoNote::core& c, size_t ev_index, const std::vector& events); + bool check_balances_2(CryptoNote::core& c, size_t ev_index, const std::vector& events); private: size_t m_test_size; uint64_t m_tx_amount; - cryptonote::account_base m_bob_account; - cryptonote::account_base m_alice_account; + CryptoNote::account_base m_bob_account; + CryptoNote::account_base m_alice_account; }; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index 5085aa6f6a..a904e67b7e 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,11 +22,14 @@ #include "cryptonote_core/Currency.h" #include "misc_language.h" -using namespace cryptonote; +#include "chaingen.h" + +using namespace CryptoNote; bool test_transaction_generation_and_ring_signature() { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + Logging::ConsoleLogger logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).currency(); account_base miner_acc1; miner_acc1.generate(); @@ -92,7 +95,7 @@ bool test_transaction_generation_and_ring_signature() oe.second = boost::get(tx_mine_6.vout[0].target).key; src.outputs.push_back(oe); - src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2); + src.real_out_tx_key = CryptoNote::get_tx_pub_key_from_extra(tx_mine_2); src.real_output = 1; src.real_output_in_tx_index = 0; } @@ -104,7 +107,7 @@ bool test_transaction_generation_and_ring_signature() destinations.push_back(td); Transaction tx_rc1; - bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, std::vector(), tx_rc1, 0); + bool r = construct_tx(miner_acc2.get_keys(), sources, destinations, std::vector(), tx_rc1, 0, logger); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); crypto::hash pref_hash = get_transaction_prefix_hash(tx_rc1); @@ -134,9 +137,11 @@ bool test_transaction_generation_and_ring_signature() bool test_block_creation() { + Logging::ConsoleLogger logger; + uint64_t vszs[] = {80,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,472,476,476,475,475,474,475,474,474,475,9391,476,476,475,475,474,475,8819,8301,475,472,4302,5316,14347,16620,19583,19403,19728,19442,19852,19015,19000,19016,19795,19749,18087,19787,19704,19750,19267,19006,19050,19445,19407,19522,19546,19788,19369,19486,19329,19370,18853,19600,19110,19320,19746,19474,19474,19743,19494,19755,19715,19769,19620,19368,19839,19532,23424,28287,30707}; std::vector szs(&vszs[0], &vszs[90]); - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).currency(); AccountPublicAddress adr; bool r = currency.parseAccountAddressString("272xWzbWsP4cfNFfxY5ETN5moU8x81PKfWPwynrrqsNGDBQGLmD1kCkKCvPeDUXu5XfmZkCrQ53wsWmdfvHBGLNjGcRiDcK", adr); diff --git a/tests/core_tests/transaction_tests.h b/tests/core_tests/transaction_tests.h index 40303a3f9a..d9ae00754a 100644 --- a/tests/core_tests/transaction_tests.h +++ b/tests/core_tests/transaction_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 38cc5c14d8..572e384f47 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,7 +20,7 @@ using namespace epee; using namespace crypto; -using namespace cryptonote; +using namespace CryptoNote; namespace { @@ -32,7 +32,7 @@ namespace m_tx.vout.clear(); m_tx.signatures.clear(); - m_tx.version = version; + m_tx.version = static_cast(version); m_tx.unlockTime = unlock_time; m_tx_key = KeyPair::generate(); @@ -116,7 +116,7 @@ namespace }; Transaction make_simple_tx_with_unlock_time(const std::vector& events, - const cryptonote::Block& blk_head, const cryptonote::account_base& from, const cryptonote::account_base& to, + const CryptoNote::Block& blk_head, const CryptoNote::account_base& from, const CryptoNote::account_base& to, uint64_t amount, uint64_t fee, uint64_t unlock_time) { std::vector sources; diff --git a/tests/core_tests/tx_validation.h b/tests/core_tests/tx_validation.h index 50b29aba28..ee40fbb126 100644 --- a/tests/core_tests/tx_validation.h +++ b/tests/core_tests/tx_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -28,7 +28,7 @@ struct get_tx_validation_base : public test_chain_unit_base REGISTER_CALLBACK_METHOD(get_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*/) + 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; @@ -36,7 +36,7 @@ struct get_tx_validation_base : public test_chain_unit_base return !tvc.m_verifivation_failed && tx_added; } - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t event_idx, const cryptonote::Block& /*block*/) + 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; @@ -44,13 +44,13 @@ struct get_tx_validation_base : public test_chain_unit_base return !bvc.m_verifivation_failed; } - bool mark_invalid_block(cryptonote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) + 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*/) + bool mark_invalid_tx(CryptoNote::core& /*c*/, size_t ev_index, const std::vector& /*events*/) { m_invalid_tx_index = ev_index + 1; return true; @@ -154,7 +154,7 @@ struct MultiSigTx_OutputSignatures : public get_tx_validation_base { const size_t m_givenKeys; const uint32_t m_requiredSignatures; const bool m_shouldSucceed; - std::vector m_outputAccounts; + std::vector m_outputAccounts; }; struct MultiSigTx_InvalidOutputSignature : public get_tx_validation_base { diff --git a/tests/core_tests/upgrade.cpp b/tests/core_tests/upgrade.cpp index 0d4196c33f..fe13f561bf 100644 --- a/tests/core_tests/upgrade.cpp +++ b/tests/core_tests/upgrade.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,15 +18,15 @@ #include "upgrade.h" using namespace epee; -using namespace cryptonote; +using namespace CryptoNote; namespace { bool makeBlocks(std::vector& events, test_generator& generator, Block& lastBlock, - const Block& parentBlock, const cryptonote::account_base& minerAcc, size_t count, + const Block& parentBlock, const CryptoNote::account_base& minerAcc, size_t count, uint8_t majorVersion, uint8_t minorVersion) { - cryptonote::Block prevBlock = parentBlock; + CryptoNote::Block prevBlock = parentBlock; for (size_t i = 0; i < count; ++i) { - cryptonote::Block b; + CryptoNote::Block b; bool r = generator.constructBlockManually(b, prevBlock, minerAcc, test_generator::bf_major_ver | test_generator::bf_minor_ver, majorVersion, minorVersion); if (!r) { @@ -45,8 +45,9 @@ namespace { gen_upgrade::gen_upgrade() : m_invalidBlockIndex(0), m_checkBlockTemplateVersionCallCounter(0), m_coinsInCirculationBeforeUpgrade(0), m_coinsInCirculationAfterUpgrade(0) { - cryptonote::CurrencyBuilder currencyBuilder; + CryptoNote::CurrencyBuilder currencyBuilder(m_logger); currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); + currencyBuilder.upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK_METHOD(gen_upgrade, markInvalidBlock); @@ -126,7 +127,7 @@ bool gen_upgrade::generate(std::vector& events) const { } bool gen_upgrade::checkBeforeUpgrade(std::vector& events, test_generator& generator, - const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc, + const CryptoNote::Block& parentBlock, const CryptoNote::account_base& minerAcc, bool checkReward) const { // Checking 1: get_block_templare returns block with major version 1 DO_CALLBACK(events, "checkBlockTemplateVersionIsV1"); @@ -151,7 +152,7 @@ bool gen_upgrade::checkBeforeUpgrade(std::vector& events, test } bool gen_upgrade::checkAfterUpgrade(std::vector& events, test_generator& generator, - const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc) const { + const CryptoNote::Block& parentBlock, const CryptoNote::account_base& minerAcc) const { // Checking 1: get_block_templare returns block with major version 2 DO_CALLBACK(events, "checkBlockTemplateVersionIsV2"); @@ -178,7 +179,7 @@ bool gen_upgrade::checkAfterUpgrade(std::vector& events, test_ return makeBlocks(events, generator, badBlock, parentBlock, minerAcc, 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); } -bool gen_upgrade::check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t eventIdx, const cryptonote::Block& /*blk*/) { +bool gen_upgrade::check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t eventIdx, const CryptoNote::Block& /*blk*/) { if (m_invalidBlockIndex == eventIdx) { m_invalidBlockIndex = 0; return bvc.m_verifivation_failed; @@ -187,24 +188,24 @@ bool gen_upgrade::check_block_verification_context(const cryptonote::block_verif } } -bool gen_upgrade::markInvalidBlock(cryptonote::core& /*c*/, size_t evIndex, const std::vector& /*events*/) { +bool gen_upgrade::markInvalidBlock(CryptoNote::core& /*c*/, size_t evIndex, const std::vector& /*events*/) { m_invalidBlockIndex = evIndex + 1; return true; } -bool gen_upgrade::checkBlockTemplateVersionIsV1(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { +bool gen_upgrade::checkBlockTemplateVersionIsV1(CryptoNote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockTemplateVersionIsV1"); CHECK_TEST_CONDITION(checkBlockTemplateVersion(c, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1)); return true; } -bool gen_upgrade::checkBlockTemplateVersionIsV2(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { +bool gen_upgrade::checkBlockTemplateVersionIsV2(CryptoNote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockTemplateVersionIsV2"); CHECK_TEST_CONDITION(checkBlockTemplateVersion(c, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0)); return true; } -bool gen_upgrade::checkBlockTemplateVersion(cryptonote::core& c, uint8_t expectedMajorVersion, uint8_t expectedMinorVersion) { +bool gen_upgrade::checkBlockTemplateVersion(CryptoNote::core& c, uint8_t expectedMajorVersion, uint8_t expectedMinorVersion) { DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockTemplateVersion"); account_base account; @@ -212,7 +213,7 @@ bool gen_upgrade::checkBlockTemplateVersion(cryptonote::core& c, uint8_t expecte Block b; difficulty_type diff; - uint64_t height; + uint32_t height; CHECK_TEST_CONDITION(c.get_block_template(b, account.get_keys().m_account_address, diff, height, blobdata())); CHECK_EQ(b.majorVersion, expectedMajorVersion); CHECK_EQ(b.minorVersion, expectedMinorVersion); @@ -220,7 +221,7 @@ bool gen_upgrade::checkBlockTemplateVersion(cryptonote::core& c, uint8_t expecte return true; } -bool gen_upgrade::checkBlockRewardEqFee(cryptonote::core& c, size_t evIndex, const std::vector& events) { +bool gen_upgrade::checkBlockRewardEqFee(CryptoNote::core& c, size_t evIndex, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockRewardEqFee"); Block blk = boost::get(events[evIndex - 1]); @@ -232,7 +233,7 @@ bool gen_upgrade::checkBlockRewardEqFee(cryptonote::core& c, size_t evIndex, con return true; } -bool gen_upgrade::checkBlockRewardIsZero(cryptonote::core& c, size_t evIndex, const std::vector& events) { +bool gen_upgrade::checkBlockRewardIsZero(CryptoNote::core& c, size_t evIndex, const std::vector& events) { DEFINE_TESTS_ERROR_CONTEXT("gen_upgrade::checkBlockRewardIsZero"); Block blk = boost::get(events[evIndex - 1]); @@ -244,12 +245,12 @@ bool gen_upgrade::checkBlockRewardIsZero(cryptonote::core& c, size_t evIndex, co return true; } -bool gen_upgrade::rememberCoinsInCirculationBeforeUpgrade(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { +bool gen_upgrade::rememberCoinsInCirculationBeforeUpgrade(CryptoNote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { m_coinsInCirculationBeforeUpgrade = c.get_blockchain_storage().getCoinsInCirculation(); return true; } -bool gen_upgrade::rememberCoinsInCirculationAfterUpgrade(cryptonote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { +bool gen_upgrade::rememberCoinsInCirculationAfterUpgrade(CryptoNote::core& c, size_t /*evIndex*/, const std::vector& /*events*/) { m_coinsInCirculationAfterUpgrade = c.get_blockchain_storage().getCoinsInCirculation(); return true; } diff --git a/tests/core_tests/upgrade.h b/tests/core_tests/upgrade.h index 1d5f5af27e..15b2505456 100644 --- a/tests/core_tests/upgrade.h +++ b/tests/core_tests/upgrade.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,22 +24,22 @@ struct gen_upgrade : public test_chain_unit_base bool generate(std::vector& events) const; - bool check_block_verification_context(const cryptonote::block_verification_context& bvc, size_t eventIdx, const cryptonote::Block& blk); + bool check_block_verification_context(const CryptoNote::block_verification_context& bvc, size_t eventIdx, const CryptoNote::Block& blk); - bool markInvalidBlock(cryptonote::core& c, size_t evIndex, const std::vector& events); - bool checkBlockTemplateVersionIsV1(cryptonote::core& c, size_t evIndex, const std::vector& events); - bool checkBlockTemplateVersionIsV2(cryptonote::core& c, size_t evIndex, const std::vector& events); - bool checkBlockRewardEqFee(cryptonote::core& c, size_t evIndex, const std::vector& events); - bool checkBlockRewardIsZero(cryptonote::core& c, size_t evIndex, const std::vector& events); - bool rememberCoinsInCirculationBeforeUpgrade(cryptonote::core& c, size_t evIndex, const std::vector& events); - bool rememberCoinsInCirculationAfterUpgrade(cryptonote::core& c, size_t evIndex, const std::vector& events); + bool markInvalidBlock(CryptoNote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockTemplateVersionIsV1(CryptoNote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockTemplateVersionIsV2(CryptoNote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockRewardEqFee(CryptoNote::core& c, size_t evIndex, const std::vector& events); + bool checkBlockRewardIsZero(CryptoNote::core& c, size_t evIndex, const std::vector& events); + bool rememberCoinsInCirculationBeforeUpgrade(CryptoNote::core& c, size_t evIndex, const std::vector& events); + bool rememberCoinsInCirculationAfterUpgrade(CryptoNote::core& c, size_t evIndex, const std::vector& events); private: bool checkBeforeUpgrade(std::vector& events, test_generator& generator, - const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc, bool checkReward) const; + const CryptoNote::Block& parentBlock, const CryptoNote::account_base& minerAcc, bool checkReward) const; bool checkAfterUpgrade(std::vector& events, test_generator& generator, - const cryptonote::Block& parentBlock, const cryptonote::account_base& minerAcc) const; - bool checkBlockTemplateVersion(cryptonote::core& c, uint8_t expectedMajorVersion, uint8_t expectedMinorVersion); + const CryptoNote::Block& parentBlock, const CryptoNote::account_base& minerAcc) const; + bool checkBlockTemplateVersion(CryptoNote::core& c, uint8_t expectedMajorVersion, uint8_t expectedMinorVersion); private: size_t m_invalidBlockIndex; diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index 4180a134d9..e31d95d6bb 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + // Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index d965a29497..3affc129e6 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + // Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. diff --git a/tests/crypto/crypto-tests.h b/tests/crypto/crypto-tests.h index 5d85f65f7b..62699c174d 100644 --- a/tests/crypto/crypto-tests.h +++ b/tests/crypto/crypto-tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/crypto/crypto.cpp b/tests/crypto/crypto.cpp index 3a3b04d3d4..70639b852b 100644 --- a/tests/crypto/crypto.cpp +++ b/tests/crypto/crypto.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index 17d751abac..f3663896b4 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + // Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. diff --git a/tests/crypto/main.cpp b/tests/crypto/main.cpp index 4f4e12ad59..75337df351 100644 --- a/tests/crypto/main.cpp +++ b/tests/crypto/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,6 @@ #include #include -#include "warnings.h" #include "crypto/crypto.h" #include "crypto/hash.h" #include "crypto-tests.h" @@ -47,8 +46,6 @@ bool operator !=(const key_derivation &a, const key_derivation &b) { return 0 != memcmp(&a, &b, sizeof(key_derivation)); } -DISABLE_GCC_WARNING(maybe-uninitialized) - int main(int argc, char *argv[]) { fstream input; string cmd; diff --git a/tests/crypto/random.c b/tests/crypto/random.c index b6968c73c4..d3625d3aab 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + // Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. diff --git a/tests/daemon_tests/CMakeLists.txt b/tests/daemon_tests/CMakeLists.txt deleted file mode 100644 index 68ca3a87e9..0000000000 --- a/tests/daemon_tests/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_executable(transfers transfers.cpp) -target_link_libraries(transfers useragent rpc cryptonote_core crypto common epee gtest_main ${Boost_LIBRARIES}) - -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_transfers) -add_custom_target(test_transfers COMMAND transfers WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_transfers) diff --git a/tests/daemon_tests/transfers.cpp b/tests/daemon_tests/transfers.cpp deleted file mode 100644 index b558c9f4ee..0000000000 --- a/tests/daemon_tests/transfers.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "gtest/gtest.h" -#include -#include "wallet/wallet.h" -#include "rpc/core_rpc_server.h" -#include "cryptonote_core/account.h" -#include "net/http_client_abstract_invoke.h" -using namespace std; -using namespace epee::misc_utils; -using namespace cryptonote; - -string daemon_address = "http://localhost:23400"; - -#define ACCS 5 - -TEST(Transfers, Transfers) -{ - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - - cout << "TESTING: transfers" << endl; - - net_utils::http::http_simple_client http_client; - wallet miner, accs[100], receiver; - miner.generate(); - ASSERT_TRUE(miner.init()); - ASSERT_TRUE(miner.store("miner.b2wallet")); - cout << "miner: " << miner.get_account().get_public_address_str() << endl; - - for (int i = 0; i < ACCS; i++) { - ostringstream s; - s << "acc" << setw(2) << setfill('0') << i << ".b2wallet"; - accs[i].generate(); - assert(accs[i].init()); - assert(accs[i].store(s.str())); - } - receiver.generate(); - assert(receiver.init()); - receiver.store("receiver.b2wallet"); - - { - COMMAND_RPC_START_MINE::request req; - req.miner_address = miner.get_account().get_public_address_str(); - req.threads_count = 1; - COMMAND_RPC_START_MINE::response res; - bool r = net_utils::http::invoke_http_json_remote_command(daemon_address + "/start_mine", req, res, http_client); - ASSERT_TRUE(r); - } - - string s; - //getline(cin, s); - sleep_no_w(1000); - ASSERT_TRUE(miner.refresh()); - cout << "miner balance: " << miner.balance() << endl; - - vector> d_accs; - for (int i = 0; i < ACCS; i++) - d_accs.push_back(make_pair(accs[i].get_account().get_keys().m_account_address, 1)); - ASSERT_TRUE(miner.transfer(d_accs)); - - //getline(cin, s); - sleep_no_w(1000); - for (int i = 0; i < ACCS; i++) { - ASSERT_TRUE(accs[i].refresh()); - ASSERT_TRUE(accs[i].transfer(receiver.get_account().get_keys().m_account_address, 1)); - } - - //getline(cin, s); - cout << "wait for block" << endl; - sleep_no_w(10000); - receiver.refresh(); - ASSERT_TRUE(receiver.balance() == ACCS); - cout << "OK" << endl; -} diff --git a/tests/difficulty/difficulty.cpp b/tests/difficulty/difficulty.cpp index dba4fc85d5..50bcc9a732 100644 --- a/tests/difficulty/difficulty.cpp +++ b/tests/difficulty/difficulty.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -25,6 +25,7 @@ #include "cryptonote_config.h" #include "cryptonote_core/difficulty.h" #include "cryptonote_core/Currency.h" +#include "Logging/ConsoleLogger.h" using namespace std; @@ -33,12 +34,13 @@ int main(int argc, char *argv[]) { cerr << "Wrong arguments" << endl; return 1; } - cryptonote::CurrencyBuilder currencyBuilder; + Logging::ConsoleLogger logger; + CryptoNote::CurrencyBuilder currencyBuilder(logger); currencyBuilder.difficultyTarget(120); currencyBuilder.difficultyWindow(720); currencyBuilder.difficultyCut(60); currencyBuilder.difficultyLag(15); - cryptonote::Currency currency = currencyBuilder.currency(); + CryptoNote::Currency currency = currencyBuilder.currency(); vector timestamps, cumulative_difficulties; fstream data(argv[1], fstream::in); data.exceptions(fstream::badbit); diff --git a/tests/gtest/include/gtest/internal/gtest-string.h b/tests/gtest/include/gtest/internal/gtest-string.h deleted file mode 100644 index dc3a07be88..0000000000 --- a/tests/gtest/include/gtest/internal/gtest-string.h +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2005, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) -// -// The Google C++ Testing Framework (Google Test) -// -// This header file declares the String class and functions used internally by -// Google Test. They are subject to change without notice. They should not used -// by code external to Google Test. -// -// This header file is #included by . -// It should not be #included by other files. - -#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ -#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ - -#ifdef __BORLANDC__ -// string.h is not guaranteed to provide strcpy on C++ Builder. -# include -#endif - -#include -#include "gtest/internal/gtest-port.h" - -#include - -namespace testing { -namespace internal { - -// String - a UTF-8 string class. -// -// For historic reasons, we don't use std::string. -// -// TODO(wan@google.com): replace this class with std::string or -// implement it in terms of the latter. -// -// Note that String can represent both NULL and the empty string, -// while std::string cannot represent NULL. -// -// NULL and the empty string are considered different. NULL is less -// than anything (including the empty string) except itself. -// -// This class only provides minimum functionality necessary for -// implementing Google Test. We do not intend to implement a full-fledged -// string class here. -// -// Since the purpose of this class is to provide a substitute for -// std::string on platforms where it cannot be used, we define a copy -// constructor and assignment operators such that we don't need -// conditional compilation in a lot of places. -// -// In order to make the representation efficient, the d'tor of String -// is not virtual. Therefore DO NOT INHERIT FROM String. -class GTEST_API_ String { - public: - // Static utility methods - - // Returns the input enclosed in double quotes if it's not NULL; - // otherwise returns "(null)". For example, "\"Hello\"" is returned - // for input "Hello". - // - // This is useful for printing a C string in the syntax of a literal. - // - // Known issue: escape sequences are not handled yet. - static String ShowCStringQuoted(const char* c_str); - - // Clones a 0-terminated C string, allocating memory using new. The - // caller is responsible for deleting the return value using - // delete[]. Returns the cloned string, or NULL if the input is - // NULL. - // - // This is different from strdup() in string.h, which allocates - // memory using malloc(). - static const char* CloneCString(const char* c_str); - -#if GTEST_OS_WINDOWS_MOBILE - // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be - // able to pass strings to Win32 APIs on CE we need to convert them - // to 'Unicode', UTF-16. - - // Creates a UTF-16 wide string from the given ANSI string, allocating - // memory using new. The caller is responsible for deleting the return - // value using delete[]. Returns the wide string, or NULL if the - // input is NULL. - // - // The wide string is created using the ANSI codepage (CP_ACP) to - // match the behaviour of the ANSI versions of Win32 calls and the - // C runtime. - static LPCWSTR AnsiToUtf16(const char* c_str); - - // Creates an ANSI string from the given wide string, allocating - // memory using new. The caller is responsible for deleting the return - // value using delete[]. Returns the ANSI string, or NULL if the - // input is NULL. - // - // The returned string is created using the ANSI codepage (CP_ACP) to - // match the behaviour of the ANSI versions of Win32 calls and the - // C runtime. - static const char* Utf16ToAnsi(LPCWSTR utf16_str); -#endif - - // Compares two C strings. Returns true iff they have the same content. - // - // Unlike strcmp(), this function can handle NULL argument(s). A - // NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool CStringEquals(const char* lhs, const char* rhs); - - // Converts a wide C string to a String using the UTF-8 encoding. - // NULL will be converted to "(null)". If an error occurred during - // the conversion, "(failed to convert from wide string)" is - // returned. - static String ShowWideCString(const wchar_t* wide_c_str); - - // Similar to ShowWideCString(), except that this function encloses - // the converted string in double quotes. - static String ShowWideCStringQuoted(const wchar_t* wide_c_str); - - // Compares two wide C strings. Returns true iff they have the same - // content. - // - // Unlike wcscmp(), this function can handle NULL argument(s). A - // NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); - - // Compares two C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike strcasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL C string, - // including the empty string. - static bool CaseInsensitiveCStringEquals(const char* lhs, - const char* rhs); - - // Compares two wide C strings, ignoring case. Returns true iff they - // have the same content. - // - // Unlike wcscasecmp(), this function can handle NULL argument(s). - // A NULL C string is considered different to any non-NULL wide C string, - // including the empty string. - // NB: The implementations on different platforms slightly differ. - // On windows, this method uses _wcsicmp which compares according to LC_CTYPE - // environment variable. On GNU platform this method uses wcscasecmp - // which compares according to LC_CTYPE category of the current locale. - // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the - // current locale. - static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, - const wchar_t* rhs); - - // Formats a list of arguments to a String, using the same format - // spec string as for printf. - // - // We do not use the StringPrintf class as it is not universally - // available. - // - // The result is limited to 4096 characters (including the tailing - // 0). If 4096 characters are not enough to format the input, - // "" is returned. - static String Format(const char* format, ...); - - // C'tors - - // The default c'tor constructs a NULL string. - String() : c_str_(NULL), length_(0) {} - - // Constructs a String by cloning a 0-terminated C string. - String(const char* a_c_str) { // NOLINT - if (a_c_str == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(a_c_str, strlen(a_c_str)); - } - } - - // Constructs a String by copying a given number of chars from a - // buffer. E.g. String("hello", 3) creates the string "hel", - // String("a\0bcd", 4) creates "a\0bc", String(NULL, 0) creates "", - // and String(NULL, 1) results in access violation. - String(const char* buffer, size_t a_length) { - ConstructNonNull(buffer, a_length); - } - - // The copy c'tor creates a new copy of the string. The two - // String objects do not share content. - String(const String& str) : c_str_(NULL), length_(0) { *this = str; } - - // D'tor. String is intended to be a final class, so the d'tor - // doesn't need to be virtual. - ~String() { delete[] c_str_; } - - // Allows a String to be implicitly converted to an ::std::string or - // ::string, and vice versa. Converting a String containing a NULL - // pointer to ::std::string or ::string is undefined behavior. - // Converting a ::std::string or ::string containing an embedded NUL - // character to a String will result in the prefix up to the first - // NUL character. - String(const ::std::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::std::string() const { return ::std::string(c_str(), length()); } - -#if GTEST_HAS_GLOBAL_STRING - String(const ::string& str) { - ConstructNonNull(str.c_str(), str.length()); - } - - operator ::string() const { return ::string(c_str(), length()); } -#endif // GTEST_HAS_GLOBAL_STRING - - // Returns true iff this is an empty string (i.e. ""). - bool empty() const { return (c_str() != NULL) && (length() == 0); } - - // Compares this with another String. - // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 - // if this is greater than rhs. - int Compare(const String& rhs) const; - - // Returns true iff this String equals the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator==(const char* a_c_str) const { return Compare(a_c_str) == 0; } - - // Returns true iff this String is less than the given String. A - // NULL string is considered less than "". - bool operator<(const String& rhs) const { return Compare(rhs) < 0; } - - // Returns true iff this String doesn't equal the given C string. A NULL - // string and a non-NULL string are considered not equal. - bool operator!=(const char* a_c_str) const { return !(*this == a_c_str); } - - // Returns true iff this String ends with the given suffix. *Any* - // String is considered to end with a NULL or empty suffix. - bool EndsWith(const char* suffix) const; - - // Returns true iff this String ends with the given suffix, not considering - // case. Any String is considered to end with a NULL or empty suffix. - bool EndsWithCaseInsensitive(const char* suffix) const; - - // Returns the length of the encapsulated string, or 0 if the - // string is NULL. - size_t length() const { return length_; } - - // Gets the 0-terminated C string this String object represents. - // The String object still owns the string. Therefore the caller - // should NOT delete the return value. - const char* c_str() const { return c_str_; } - - // Assigns a C string to this object. Self-assignment works. - const String& operator=(const char* a_c_str) { - return *this = String(a_c_str); - } - - // Assigns a String object to this object. Self-assignment works. - const String& operator=(const String& rhs) { - if (this != &rhs) { - delete[] c_str_; - if (rhs.c_str() == NULL) { - c_str_ = NULL; - length_ = 0; - } else { - ConstructNonNull(rhs.c_str(), rhs.length()); - } - } - - return *this; - } - - private: - // Constructs a non-NULL String from the given content. This - // function can only be called when c_str_ has not been allocated. - // ConstructNonNull(NULL, 0) results in an empty string (""). - // ConstructNonNull(NULL, non_zero) is undefined behavior. - void ConstructNonNull(const char* buffer, size_t a_length) { - char* const str = new char[a_length + 1]; - memcpy(str, buffer, a_length); - str[a_length] = '\0'; - c_str_ = str; - length_ = a_length; - } - - const char* c_str_; - size_t length_; -}; // class String - -// Streams a String to an ostream. Each '\0' character in the String -// is replaced with "\\0". -inline ::std::ostream& operator<<(::std::ostream& os, const String& str) { - if (str.c_str() == NULL) { - os << "(null)"; - } else { - const char* const c_str = str.c_str(); - for (size_t i = 0; i != str.length(); i++) { - if (c_str[i] == '\0') { - os << "\\0"; - } else { - os << c_str[i]; - } - } - } - return os; -} - -// Gets the content of the stringstream's buffer as a String. Each '\0' -// character in the buffer is replaced with "\\0". -GTEST_API_ String StringStreamToString(::std::stringstream* stream); - -// Converts a streamable value to a String. A NULL pointer is -// converted to "(null)". When the input value is a ::string, -// ::std::string, ::wstring, or ::std::wstring object, each NUL -// character in it is replaced with "\\0". - -// Declared here but defined in gtest.h, so that it has access -// to the definition of the Message class, required by the ARM -// compiler. -template -String StreamableToString(const T& streamable); - -} // namespace internal -} // namespace testing - -#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/tests/hash-target.cpp b/tests/hash-target.cpp index 8379323390..aff8ce68e8 100644 --- a/tests/hash-target.cpp +++ b/tests/hash-target.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,7 @@ #include "cryptonote_core/difficulty.h" using namespace std; -using cryptonote::check_hash; +using CryptoNote::check_hash; int main(int argc, char *argv[]) { crypto::hash h; diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index ed3d6044a3..025f61faed 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,6 @@ #include #include -#include "warnings.h" #include "crypto/hash.h" #include "../io.h" @@ -32,16 +31,16 @@ typedef crypto::hash chash; cn_context *context; extern "C" { +#ifdef _MSC_VER +#pragma warning(disable: 4297) +#endif -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4297) static void hash_tree(const void *data, size_t length, char *hash) { if ((length & 31) != 0) { throw ios_base::failure("Invalid input length for tree_hash"); } tree_hash((const char (*)[32]) data, length >> 5, hash); } -POP_WARNINGS static void slow_hash(const void *data, size_t length, char *hash) { cn_slow_hash(*context, data, length, *reinterpret_cast(hash)); diff --git a/tests/integration_test_lib/BaseFunctionalTest.cpp b/tests/integration_test_lib/BaseFunctionalTest.cpp index cfeedea607..83a33a5c48 100755 --- a/tests/integration_test_lib/BaseFunctionalTest.cpp +++ b/tests/integration_test_lib/BaseFunctionalTest.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,15 +24,19 @@ #include #include -#include #include +#include +#include +#include + #include "p2p/NetNodeConfig.h" #include "cryptonote_core/CoreConfig.h" +#include "wallet/Wallet.h" +#include "InProcTestNode.h" #include "RPCTestNode.h" -#include "wallet/Wallet.h" -#include "Logger.h" + #if defined __linux__ #include @@ -40,242 +44,249 @@ #include #endif +#include "Logger.h" + +#ifdef _WIN32 +const std::string DAEMON_FILENAME = "bytecoind.exe"; +#else +const std::string DAEMON_FILENAME = "bytecoind"; +#endif + using namespace Tests::Common; +using namespace Tests; void BaseFunctionalTest::launchTestnet(size_t count, Topology t) { - if (count < 1) LOG_WARNING("Testnet has no nodes"); - for (uint16_t i = 0; i < count; ++i) { - std::string dataDirPath = m_dataDir + "/node"; - dataDirPath += boost::lexical_cast(i); - boost::filesystem::create_directory(dataDirPath); - - std::ofstream config(dataDirPath + "/daemon.conf", std::ios_base::trunc | std::ios_base::out); - - uint16_t rpcPort = RPC_FIRST_PORT + i; - uint16_t p2pPort = P2P_FIRST_PORT + i; - - config - << "rpc-bind-port=" << rpcPort << std::endl - << "p2p-bind-port=" << p2pPort << std::endl - << "log-level=2" << std::endl - << "log-file=test_bytecoind_" << i + 1 << ".log" << std::endl; - - switch (t) { - case Line: - if (i != count - 1) config << "add-exclusive-node=127.0.0.1:" << p2pPort + 1 << std::endl; - if (i != 0) config << "add-exclusive-node=127.0.0.1:" << p2pPort - 1 << std::endl; - break; - case Ring: { - uint16_t p2pExternalPort = P2P_FIRST_PORT + (i + 1) % count; - config << "add-exclusive-node=127.0.0.1:" << p2pExternalPort + 1 << std::endl; - } - break; - case Star: - if (i == 0) { - for (size_t node = 1; node < count; ++node) - config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT + node << std::endl; - } - else { - config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT << std::endl; - } - break; - } - config.close(); -#if defined WIN32 - std::string commandLine = "start /MIN \"bytecoind\" \"" + m_daemonDir + "\\bytecoind.exe\" --testnet --data-dir=\"" + dataDirPath + "\" --config-file=daemon.conf"; - LOG_DEBUG(commandLine); - system(commandLine.c_str()); -#elif defined __linux__ - auto pid = fork(); - if( pid == 0 ) { - std::string pathToDaemon = "" + m_daemonDir + "/bytecoind"; - close(1); - close(2); - std::string dataDir = "--data-dir=" + dataDirPath + ""; - if(execl(pathToDaemon.c_str(), "bytecoind", "--testnet", dataDir.c_str(), "--config-file=daemon.conf", NULL) == -1) { - LOG_ERROR(TO_STRING(errno)); - } - throw std::runtime_error("failed to start daemon"); - } else if(pid > 0) { - pids.push_back(pid); - } -#else + if (count < 1) { + LOG_WARNING("Testnet has no nodes"); + } -#endif + m_testnetSize = count; + m_topology = t; - nodeDaemons.push_back( - std::unique_ptr(new RPCTestNode(rpcPort, m_dispatcher)) - ); + nodeDaemons.resize(m_testnetSize); + + for (size_t i = 0; i < m_testnetSize; ++i) { + startNode(i); + } + + waitDaemonsReady(); + + nodeDaemons[0]->makeINode(mainNode); + makeWallet(workingWallet, mainNode); +} + +void BaseFunctionalTest::launchInprocTestnet(size_t count, Topology t) { + m_testnetSize = count; + m_topology = t; + + for (size_t i = 0; i < m_testnetSize; ++i) { + auto cfg = createNodeConfiguration(i); + nodeDaemons.emplace_back(new InProcTestNode(cfg, m_currency)); } - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); //for initial update + + waitDaemonsReady(); + nodeDaemons[0]->makeINode(mainNode); makeWallet(workingWallet, mainNode); } void BaseFunctionalTest::launchTestnetWithInprocNode(size_t count, Topology t) { - if (count < 1) LOG_WARNING("Testnet has no nodes"); - for (uint16_t i = 0; i < count-1; ++i) { - std::string dataDirPath = m_dataDir + "/node"; - dataDirPath += boost::lexical_cast(i); - boost::filesystem::create_directory(dataDirPath); - - std::ofstream config(dataDirPath + "/daemon.conf", std::ios_base::trunc | std::ios_base::out); - - uint16_t rpcPort = RPC_FIRST_PORT + i; - uint16_t p2pPort = P2P_FIRST_PORT + i; - - config - << "rpc-bind-port=" << rpcPort << std::endl - << "p2p-bind-port=" << p2pPort << std::endl - << "log-level=2" << std::endl - << "log-file=test_bytecoind_" << i + 1 << ".log" << std::endl; - - switch (t) { - case Line: - config << "add-exclusive-node=127.0.0.1:" << p2pPort + 1 << std::endl; - if (i != 0) config << "add-exclusive-node=127.0.0.1:" << p2pPort - 1 << std::endl; - break; - case Ring: { - uint16_t p2pExternalPort = P2P_FIRST_PORT + (i + 1) % count; - config << "add-exclusive-node=127.0.0.1:" << p2pExternalPort + 1 << std::endl; - } - break; - case Star: - if (i == 0) { - for (size_t node = 1; node < count; ++node) - config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT + node << std::endl; - } else { - config << "add-exclusive-node=127.0.0.1:" << P2P_FIRST_PORT << std::endl; - } - break; + if (count < 1) { + LOG_WARNING("Testnet has no nodes"); + } + + m_testnetSize = count; + m_topology = t; + + nodeDaemons.resize(m_testnetSize); + + for (size_t i = 0; i < m_testnetSize - 1; ++i) { + startNode(i); + } + + auto cfg = createNodeConfiguration(m_testnetSize - 1); + nodeDaemons[m_testnetSize - 1].reset(new InProcTestNode(cfg, m_currency)); + + waitDaemonsReady(); + + nodeDaemons[0]->makeINode(mainNode); + makeWallet(workingWallet, mainNode); +} + + +Tests::TestNodeConfiguration BaseFunctionalTest::createNodeConfiguration(size_t index) { + Tests::TestNodeConfiguration cfg; + + std::string dataDirPath = m_dataDir + "/node" + std::to_string(index); + boost::filesystem::create_directory(dataDirPath); + + cfg.dataDir = dataDirPath; + + uint16_t rpcPort = static_cast(RPC_FIRST_PORT + index); + uint16_t p2pPort = static_cast(P2P_FIRST_PORT + index); + + cfg.p2pPort = p2pPort; + cfg.rpcPort = rpcPort; + + switch (m_topology) { + case Line: + if (index != 0) { + cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pPort - 1)); } - config.close(); -#if defined WIN32 - std::string commandLine = "start /MIN \"bytecoind\" \"" + m_daemonDir + "\\bytecoind.exe\" --testnet --data-dir=\"" + dataDirPath + "\" --config-file=daemon.conf"; - LOG_DEBUG(commandLine); - system(commandLine.c_str()); -#elif defined __linux__ - auto pid = fork(); - if (pid == 0) { - std::string pathToDaemon = "" + m_daemonDir + "/bytecoind"; - close(1); - close(2); - std::string dataDir = "--data-dir=" + dataDirPath + ""; - if (execl(pathToDaemon.c_str(), "bytecoind", "--testnet", dataDir.c_str(), "--config-file=daemon.conf", NULL) == -1) { - LOG_ERROR(TO_STRING(errno)); + break; + + case Ring: { + uint16_t p2pExternalPort = static_cast(P2P_FIRST_PORT + (index + 1) % m_testnetSize); + cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pExternalPort + 1)); + break; + } + + case Star: + if (index == 0) { + for (size_t node = 1; node < m_testnetSize; ++node) { + cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(P2P_FIRST_PORT + node)); } - throw std::runtime_error("failed to start daemon"); - } else if (pid > 0) { - pids.push_back(pid); } -#else + break; + } -#endif + return cfg; +} - nodeDaemons.push_back( - std::unique_ptr(new RPCTestNode(rpcPort, m_dispatcher)) - ); - } - - this->core.reset(new cryptonote::core(m_currency, NULL)); - this->protocol.reset(new cryptonote::t_cryptonote_protocol_handler(*core, NULL)); - this->p2pNode.reset(new nodetool::node_server>(*protocol)); - protocol->set_p2p_endpoint(p2pNode.get()); - core->set_cryptonote_protocol(protocol.get()); - - std::string dataDirPath = m_dataDir + "/node"; - dataDirPath += boost::lexical_cast(count - 1); +void BaseFunctionalTest::startNode(size_t index) { + std::string dataDirPath = m_dataDir + "/node" + std::to_string(index); boost::filesystem::create_directory(dataDirPath); - uint16_t p2pPort = P2P_FIRST_PORT + static_cast(count) - 1; - - nodetool::NetNodeConfig p2pConfig; - p2pConfig.bindIp = "127.0.0.1"; - p2pConfig.bindPort = boost::lexical_cast(p2pPort); - nodetool::net_address addr; - addr.ip = 0x7f000001; + std::ofstream config(dataDirPath + "/daemon.conf", std::ios_base::trunc | std::ios_base::out); - p2pConfig.externalPort = 0; - p2pConfig.allowLocalIp = false; - p2pConfig.hideMyPort = false; - p2pConfig.configFolder = dataDirPath; + uint16_t rpcPort = static_cast(RPC_FIRST_PORT + index); + uint16_t p2pPort = static_cast(P2P_FIRST_PORT + index); + config + << "rpc-bind-port=" << rpcPort << std::endl + << "p2p-bind-port=" << p2pPort << std::endl + << "log-level=4" << std::endl + << "log-file=test_bytecoind_" << index << ".log" << std::endl; - switch (t) { + switch (m_topology) { case Line: - addr.port = p2pPort - 1; - p2pConfig.exclusiveNodes.push_back(addr); + if (index != 0) { + config << "add-exclusive-node=127.0.0.1:" << p2pPort - 1 << std::endl; + } break; - case Ring: - addr.port = p2pPort - 1; - p2pConfig.exclusiveNodes.push_back(addr); - addr.port = P2P_FIRST_PORT; - p2pConfig.exclusiveNodes.push_back(addr); + + case Ring: { + uint16_t p2pExternalPort = static_cast(P2P_FIRST_PORT + (index + 1) % m_testnetSize); + config << "add-exclusive-node=127.0.0.1:" << (p2pExternalPort + 1) << std::endl; break; + } case Star: - addr.port = P2P_FIRST_PORT; - p2pConfig.exclusiveNodes.push_back(addr); + if (index == 0) { + for (size_t node = 1; node < m_testnetSize; ++node) { + config << "add-exclusive-node=127.0.0.1:" << (P2P_FIRST_PORT + node) << std::endl; + } + } break; } + config.close(); - if (!p2pNode->init(p2pConfig, true)) { - throw std::runtime_error("Failed to init p2pNode"); + boost::filesystem::path daemonPath = index < m_config.daemons.size() ? + boost::filesystem::path(m_config.daemons[index]) : (boost::filesystem::path(m_daemonDir) / DAEMON_FILENAME); + boost::system::error_code ignoredEc; + if (!boost::filesystem::exists(daemonPath, ignoredEc)) { + throw std::runtime_error("daemon binary wasn't found"); } - protocol->init(); - - cryptonote::MinerConfig emptyMiner; - cryptonote::CoreConfig coreConfig; - coreConfig.configFolder = dataDirPath; - core->init(coreConfig, emptyMiner, true); +#if defined WIN32 + std::string commandLine = "start /MIN \"bytecoind" + std::to_string(index) + "\" \"" + daemonPath.string() + + "\" --testnet --data-dir=\"" + dataDirPath + "\" --config-file=daemon.conf"; + LOG_DEBUG(commandLine); + system(commandLine.c_str()); +#elif defined __linux__ + auto pid = fork(); + if (pid == 0) { + std::string pathToDaemon = daemonPath.string(); + close(1); + close(2); + std::string dataDir = "--data-dir=" + dataDirPath + ""; + LOG_TRACE(pathToDaemon); + if (execl(pathToDaemon.c_str(), "bytecoind", "--testnet", dataDir.c_str(), "--config-file=daemon.conf", NULL) == -1) { + LOG_ERROR(TO_STRING(errno)); + } + abort(); +// throw std::runtime_error("failed to start daemon"); + } else if (pid > 0) { + pids.resize(m_testnetSize, 0); + assert(pids[index] == 0); + pids[index] = pid; + } +#else + assert(false); +#endif - inprocNode.reset(new CryptoNote::InProcessNode(*core, *protocol)); - std::promise p; - auto future = p.get_future(); - inprocNode->init([&p](std::error_code ec) { - p.set_value(); - if (ec) { - std::cout << ec.message() << std::endl; - } - }); + assert(nodeDaemons.size() > index); + nodeDaemons[index] = std::unique_ptr(new RPCTestNode(rpcPort, m_dispatcher)); +} - future.get(); - - std::thread serverThread( - std::bind( - &nodetool::node_server>::run, - p2pNode.get() - ) - ); - serverThread.detach(); +void BaseFunctionalTest::stopNode(size_t index) { + assert(nodeDaemons[index].get() != nullptr); + bool ok = nodeDaemons[index]->stopDaemon(); + assert(ok); + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + nodeDaemons[index].release(); +#ifdef __linux__ + int status; + assert(pids[index] != 0); + while (-1 == waitpid(pids[index], &status, 0)); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + std::cerr << "Process " << " (pid " << pids[index] << ") failed" << std::endl; + exit(1); + } + pids[index] = 0; +#endif +} - std::this_thread::sleep_for(std::chrono::milliseconds(10000)); //for initial update - nodeDaemons[0]->makeINode(mainNode); - makeWallet(workingWallet, mainNode); +bool BaseFunctionalTest::waitDaemonsReady() { + for (size_t i = 0; i < nodeDaemons.size(); ++i) { + bool ok = waitDaemonReady(i); + if (!ok) { + return false; + } + } + return true; +} +bool BaseFunctionalTest::waitDaemonReady(size_t nodeIndex) { + assert(nodeIndex < nodeDaemons.size() && nodeDaemons[nodeIndex].get() != nullptr); + for (size_t i = 0; ; ++i) { + if (nodeDaemons[nodeIndex]->getLocalHeight() > 0) { + break; + } else if (i < 2 * 60) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } else { + return false; + } + } + return true; } - BaseFunctionalTest::~BaseFunctionalTest() { if (mainNode) { mainNode->shutdown(); } - if (inprocNode) { - inprocNode->shutdown(); - } + stopTestnet(); - if (p2pNode) { - p2pNode->send_stop_signal(); + for (size_t i = 0; i < m_testnetSize; ++i) { + boost::system::error_code ignoredErrorCode; + auto nodeDataDir = boost::filesystem::path(m_dataDir) / boost::filesystem::path("node" + std::to_string(i)); + boost::filesystem::remove_all(nodeDataDir, ignoredErrorCode); } - - std::this_thread::sleep_for(std::chrono::seconds(2)); - stopTestnet(); } namespace { @@ -292,19 +303,71 @@ namespace { }; } -bool BaseFunctionalTest::mineBlock(std::unique_ptr& wallet) { - if (nodeDaemons.empty() || !wallet) return false; - if (!nodeDaemons.front()->stopMining()) return false; +bool BaseFunctionalTest::mineBlocks(TestNode& node, const CryptoNote::AccountPublicAddress& address, size_t blockCount) { + for (size_t i = 0; i < blockCount; ++i) { + Block blockTemplate; + uint64_t difficulty; + + account_base accBase; + account_keys accKeys; + accKeys.m_account_address = address; + accBase.set_keys(accKeys); + + if (!node.getBlockTemplate(m_currency.accountAddressAsString(accBase), blockTemplate, difficulty)) { + return false; + } + + if (difficulty != 1) { + return false; + } + + blockTemplate.timestamp = m_nextTimestamp; + m_nextTimestamp += 2 * m_currency.difficultyTarget(); + + if (blockTemplate.majorVersion == BLOCK_MAJOR_VERSION_2) { + blockTemplate.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + blockTemplate.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; + blockTemplate.parentBlock.numberOfTransactions = 1; + + CryptoNote::tx_extra_merge_mining_tag mmTag; + mmTag.depth = 0; + if (!CryptoNote::get_aux_block_header_hash(blockTemplate, mmTag.merkle_root)) { + return false; + } + + blockTemplate.parentBlock.minerTx.extra.clear(); + if (!CryptoNote::append_mm_tag_to_extra(blockTemplate.parentBlock.minerTx.extra, mmTag)) { + return false; + } + } + + blobdata blockBlob = block_to_blob(blockTemplate); + if (!node.submitBlock(::Common::toHex(blockBlob.data(), blockBlob.size()))) { + return false; + } + } + + return true; +} + +bool BaseFunctionalTest::mineBlock(std::unique_ptr &wallet) { + if (nodeDaemons.empty() || !wallet) + return false; + if (!nodeDaemons.front()->stopMining()) + return false; std::this_thread::sleep_for(std::chrono::milliseconds(10000)); Semaphore gotReward; WaitForCoinBaseObserver cbo(gotReward, *wallet.get()); wallet->addObserver(&cbo); - if(!nodeDaemons.front()->startMining(1, wallet->getAddress())) return false; + if (!nodeDaemons.front()->startMining(1, wallet->getAddress())) + return false; gotReward.wait(); - if (!nodeDaemons.front()->stopMining()) return false; + if (!nodeDaemons.front()->stopMining()) + return false; wallet->removeObserver(&cbo); return true; } + bool BaseFunctionalTest::mineBlock() { return mineBlock(workingWallet); } @@ -328,18 +391,181 @@ bool BaseFunctionalTest::makeWallet(std::unique_ptr & walle } void BaseFunctionalTest::stopTestnet() { - for (auto& Daemon : nodeDaemons) { - Daemon->stopDaemon(); + if (nodeDaemons.empty()) { + return; } - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + + // WORKAROUND: Make sure all contexts, that use daemons, are finished before these daemons will be destroyed + // TODO: There is should be used context groups + m_dispatcher.yield(); + + for (auto& daemon : nodeDaemons) { + if (daemon) { + daemon->stopDaemon(); + } + } + + // std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + + nodeDaemons.clear(); + #ifdef __linux__ for (auto& pid : pids) { + if (pid != 0) { int status; while (-1 == waitpid(pid, &status, 0)); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - std::cerr << "Process " << " (pid " << pid << ") failed" << std::endl; - exit(1); + std::cerr << "Process " << " (pid " << pid << ") failed" << std::endl; + exit(1); } + } } + + pids.clear(); #endif } + +namespace { + struct PeerCountWaiter : CryptoNote::INodeObserver { + System::Dispatcher& m_dispatcher; + System::Event m_event; + System::Timer m_timer; + bool m_timedout = false; + bool m_waiting = false; + size_t m_expectedPeerCount; + + PeerCountWaiter(System::Dispatcher& dispatcher) : m_dispatcher(dispatcher), m_event(m_dispatcher), m_timer(m_dispatcher) { + } + + void wait(size_t expectedPeerCount) { + m_waiting = true; + m_expectedPeerCount = expectedPeerCount; + m_dispatcher.spawn([this] { + try { + m_timer.sleep(std::chrono::minutes(2)); + m_timedout = true; + m_event.set(); + } catch (System::InterruptedException&) { + } + }); + m_event.wait(); + m_timer.stop(); + m_waiting = false; + } + + virtual void peerCountUpdated(size_t count) override { + m_dispatcher.remoteSpawn([this, count]() { + if (m_waiting && count == m_expectedPeerCount) { + m_event.set(); + } + }); + } + }; +} + +bool BaseFunctionalTest::waitForPeerCount(CryptoNote::INode& node, size_t expectedPeerCount) { + PeerCountWaiter peerCountWaiter(m_dispatcher); + node.addObserver(&peerCountWaiter); + if (node.getPeerCount() != expectedPeerCount) { + peerCountWaiter.wait(expectedPeerCount); + } + node.removeObserver(&peerCountWaiter); + // TODO workaround: make sure ObserverManager doesn't have local pointers to peerCountWaiter, so it can be destroyed + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // Run all spawned handlers from PeerCountWaiter::peerCountUpdated + m_dispatcher.yield(); + + return !peerCountWaiter.m_timedout; +} + +namespace { + struct PoolUpdateWaiter : public INodeObserver { + System::Dispatcher& m_dispatcher; + System::Event& m_event; + + PoolUpdateWaiter(System::Dispatcher& dispatcher, System::Event& event) : m_dispatcher(dispatcher), m_event(event) { + } + + virtual void poolChanged() override { + m_dispatcher.remoteSpawn([this]() { m_event.set(); }); + } + }; +} + +bool BaseFunctionalTest::waitForPoolSize(size_t nodeIndex, CryptoNote::INode& node, size_t expectedPoolSize, + std::vector& txPool) { + System::Event event(m_dispatcher); + PoolUpdateWaiter poolUpdateWaiter(m_dispatcher, event); + node.addObserver(&poolUpdateWaiter); + + bool ok; + for (size_t i = 0; ; ++i) { + ok = getNodeTransactionPool(nodeIndex, node, txPool); + if (!ok) { + break; + } + if (txPool.size() == expectedPoolSize) { + break; + } + + // TODO NodeRpcProxy doesn't send poolChanged() notification!!! + //event.wait(); + //event.clear(); + // WORKAROUND + if (i < 3 * P2P_DEFAULT_HANDSHAKE_INTERVAL) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } else { + ok = false; + break; + } + } + + node.removeObserver(&poolUpdateWaiter); + // TODO workaround: make sure ObserverManager doesn't have local pointers to poolUpdateWaiter, so it can be destroyed + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // Run all spawned handlers from PoolUpdateWaiter::poolChanged + m_dispatcher.yield(); + + return ok; +} + +bool BaseFunctionalTest::getNodeTransactionPool(size_t nodeIndex, CryptoNote::INode& node, + std::vector& txPool) { + assert(nodeIndex < nodeDaemons.size() && nodeDaemons[nodeIndex].get() != nullptr); + auto& daemon = *nodeDaemons[nodeIndex]; + + crypto::hash tailBlockId; + bool updateTailBlockId = true; + while (true) { + if (updateTailBlockId) { + if (!daemon.getTailBlockId(tailBlockId)) { + return false; + } + updateTailBlockId = false; + } + + System::Event poolReceivedEvent(m_dispatcher); + std::error_code ec; + bool isTailBlockActual; + std::vector addedTxs; + std::vector deletedTxsIds; + node.getPoolSymmetricDifference(std::vector(), tailBlockId, isTailBlockActual, addedTxs, deletedTxsIds, + [this, &poolReceivedEvent, &ec](std::error_code result) { + ec = result; + m_dispatcher.remoteSpawn([&poolReceivedEvent]() { poolReceivedEvent.set(); }); + } + ); + poolReceivedEvent.wait(); + + if (ec) { + return false; + } else if (!isTailBlockActual) { + updateTailBlockId = true; + } else { + txPool = std::move(addedTxs); + break; + } + } + + return true; +} diff --git a/tests/integration_test_lib/BaseFunctionalTest.h b/tests/integration_test_lib/BaseFunctionalTest.h index fbd5505dc9..ed5a25faf1 100755 --- a/tests/integration_test_lib/BaseFunctionalTest.h +++ b/tests/integration_test_lib/BaseFunctionalTest.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -27,17 +27,14 @@ #include #include -#include "TestNode.h" #include -#include "cryptonote_core/Currency.h" -#include "inprocess_node/InProcessNode.h" - -#include "../../cryptonote_core/cryptonote_core.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "p2p/net_node.h" +#include +#include "cryptonote_core/Currency.h" #include "IWallet.h" #include "INode.h" +#include "TestNode.h" +#include "NetworkConfiguration.h" namespace Tests { namespace Common { @@ -72,8 +69,8 @@ namespace Tests { } }; - const uint16_t P2P_FIRST_PORT = 8000; - const uint16_t RPC_FIRST_PORT = 8200; + const uint16_t P2P_FIRST_PORT = 9000; + const uint16_t RPC_FIRST_PORT = 9200; class BaseFunctionalTestConfig { @@ -83,7 +80,8 @@ namespace Tests { void init(po::options_description& desc) { desc.add_options() ("daemon-dir,d", po::value()->default_value("."), "path to bytecoind.exe") - ("data-dir,n", po::value()->default_value("."), "path to daemon's data directory"); + ("data-dir,n", po::value()->default_value("."), "path to daemon's data directory") + ("add-daemons,a", po::value>()->multitoken(), "add daemon to topology"); } bool handleCommandLine(const po::variables_map& vm) { @@ -94,27 +92,39 @@ namespace Tests { if (vm.count("data-dir")) { dataDir = vm["data-dir"].as(); } - return true; - } + if (vm.count("add-daemons")) { + daemons = vm["add-daemons"].as>(); + } - protected: - friend class BaseFunctionalTest; + return true; + } std::string daemonDir; std::string dataDir; + std::vector daemons; }; class BaseFunctionalTest : boost::noncopyable { public: - BaseFunctionalTest(const cryptonote::Currency& currency, System::Dispatcher& d, const BaseFunctionalTestConfig& config) : m_currency(currency), m_dataDir(config.dataDir), m_daemonDir(config.daemonDir), m_dispatcher(d), inprocNode(nullptr) { - if (m_dataDir.empty()) m_dataDir = "."; - if (m_daemonDir.empty()) m_daemonDir = "."; + BaseFunctionalTest(const CryptoNote::Currency& currency, System::Dispatcher& d, const BaseFunctionalTestConfig& config) : + m_dispatcher(d), + m_currency(currency), + m_nextTimestamp(time(nullptr) - 365 * 24 * 60 * 60), + m_config(config), + m_dataDir(config.dataDir), + m_daemonDir(config.daemonDir) { + if (m_dataDir.empty()) { + m_dataDir = "."; + } + if (m_daemonDir.empty()) { + m_daemonDir = "."; + } }; - ~BaseFunctionalTest(); + ~BaseFunctionalTest(); enum Topology { Ring, @@ -122,39 +132,53 @@ namespace Tests { Star }; - private: - std::unique_ptr core; - std::unique_ptr> protocol; - std::unique_ptr>> p2pNode; - protected: + + TestNodeConfiguration createNodeConfiguration(size_t i); + std::vector< std::unique_ptr > nodeDaemons; System::Dispatcher& m_dispatcher; - const cryptonote::Currency& m_currency; - std::unique_ptr inprocNode; + const CryptoNote::Currency& m_currency; void launchTestnet(size_t count, Topology t = Line); void launchTestnetWithInprocNode(size_t count, Topology t = Line); + void launchInprocTestnet(size_t count, Topology t = Line); void stopTestnet(); + + void startNode(size_t index); + void stopNode(size_t index); + bool makeWallet(std::unique_ptr & wallet, std::unique_ptr& node, const std::string& password = "pass"); + bool mineBlocks(TestNode& node, const CryptoNote::AccountPublicAddress& address, size_t blockCount); bool mineBlock(std::unique_ptr& wallet); bool mineBlock(); bool startMining(size_t threads); bool stopMining(); + bool getNodeTransactionPool(size_t nodeIndex, CryptoNote::INode& node, std::vector& txPool); + + bool waitDaemonsReady(); + bool waitDaemonReady(size_t nodeIndex); + bool waitForPeerCount(CryptoNote::INode& node, size_t expectedPeerCount); + bool waitForPoolSize(size_t nodeIndex, CryptoNote::INode& node, size_t expectedPoolSize, + std::vector& txPool); + private: #ifdef __linux__ std::vector<__pid_t> pids; #endif - cryptonote::CurrencyBuilder currencyBuilder; + Logging::ConsoleLogger logger; std::unique_ptr mainNode; std::unique_ptr workingWallet; - + uint64_t m_nextTimestamp; + Topology m_topology; + size_t m_testnetSize; + BaseFunctionalTestConfig m_config; std::string m_dataDir; std::string m_daemonDir; - uint16_t m_mainDaemonRPCPort; + uint16_t m_mainDaemonRPCPort; }; } } diff --git a/tests/integration_test_lib/CoreRpcSerialization.cpp b/tests/integration_test_lib/CoreRpcSerialization.cpp index b3ef07a483..14bce8c2a1 100755 --- a/tests/integration_test_lib/CoreRpcSerialization.cpp +++ b/tests/integration_test_lib/CoreRpcSerialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,41 +17,41 @@ #include "CoreRpcSerialization.h" -namespace cryptonote { +namespace CryptoNote { -void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer(value.miner_address, "miner_address"); serializer(value.threads_count, "threads_count"); serializer.endObject(); } -void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer(value.status, "status"); serializer.endObject(); } -void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer.endObject(); } -void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer(value.status, "status"); serializer.endObject(); } -void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer.endObject(); } -void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, cryptonote::ISerializer& serializer) { +void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, CryptoNote::ISerializer& serializer) { serializer.beginObject(name); serializer(value.status, "status"); serializer.endObject(); } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/tests/integration_test_lib/CoreRpcSerialization.h b/tests/integration_test_lib/CoreRpcSerialization.h index 17cdb527c5..ed4f92b8aa 100755 --- a/tests/integration_test_lib/CoreRpcSerialization.h +++ b/tests/integration_test_lib/CoreRpcSerialization.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,15 +22,15 @@ #include "../../src/serialization/ISerializer.h" #include "../../src/rpc/core_rpc_server_commands_defs.h" -namespace cryptonote { +namespace CryptoNote { -void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer); -void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer); +void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, cryptonote::ISerializer& serializer); -void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer); +void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, cryptonote::ISerializer& serializer); -void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, CryptoNote::ISerializer& serializer); +void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, CryptoNote::ISerializer& serializer); -} //namespace cryptonote +} //namespace CryptoNote diff --git a/tests/integration_test_lib/InProcTestNode.cpp b/tests/integration_test_lib/InProcTestNode.cpp new file mode 100644 index 0000000000..00bfbf3bfe --- /dev/null +++ b/tests/integration_test_lib/InProcTestNode.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "InProcTestNode.h" + +#include + +#include +#include + +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/CoreConfig.h" +#include "cryptonote_core/miner.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "p2p/net_node.h" +#include "InProcessNode/InProcessNode.h" + +using namespace CryptoNote; + +#undef ERROR + +namespace Tests { + +namespace { +bool parse_peer_from_string(net_address &pe, const std::string &node_addr) { + return ::Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); +} +} + + +InProcTestNode::InProcTestNode(const TestNodeConfiguration& cfg, const CryptoNote::Currency& currency) : + m_cfg(cfg), m_currency(currency) { + + std::promise initPromise; + std::future initFuture = initPromise.get_future(); + + m_thread = std::thread(std::bind(&InProcTestNode::workerThread, this, std::ref(initPromise))); + auto initError = initFuture.get(); + + if (!initError.empty()) { + m_thread.join(); + throw std::runtime_error(initError); + } +} + +InProcTestNode::~InProcTestNode() { + if (m_thread.joinable()) { + m_thread.join(); + } +} + +void InProcTestNode::workerThread(std::promise& initPromise) { + + System::Dispatcher dispatcher; + Logging::ConsoleLogger log; + + Logging::LoggerRef logger(log, "InProcTestNode"); + + try { + + core.reset(new CryptoNote::core(m_currency, NULL, log)); + protocol.reset(new CryptoNote::cryptonote_protocol_handler(m_currency, dispatcher, *core, NULL, log)); + p2pNode.reset(new CryptoNote::node_server(dispatcher, *protocol, log)); + protocol->set_p2p_endpoint(p2pNode.get()); + core->set_cryptonote_protocol(protocol.get()); + + CryptoNote::NetNodeConfig p2pConfig; + + p2pConfig.bindIp = "127.0.0.1"; + p2pConfig.bindPort = std::to_string(m_cfg.p2pPort); + p2pConfig.externalPort = 0; + p2pConfig.allowLocalIp = false; + p2pConfig.hideMyPort = false; + p2pConfig.configFolder = m_cfg.dataDir; + + for (const auto& en : m_cfg.exclusiveNodes) { + net_address na; + parse_peer_from_string(na, en); + p2pConfig.exclusiveNodes.push_back(na); + } + + if (!p2pNode->init(p2pConfig, true)) { + throw std::runtime_error("Failed to init p2pNode"); + } + + CryptoNote::MinerConfig emptyMiner; + CryptoNote::CoreConfig coreConfig; + + coreConfig.configFolder = m_cfg.dataDir; + + if (!core->init(coreConfig, emptyMiner, true)) { + throw std::runtime_error("Core failed to initialize"); + } + + initPromise.set_value(std::string()); + + } catch (std::exception& e) { + logger(Logging::ERROR) << "Failed to initialize: " << e.what(); + initPromise.set_value(e.what()); + return; + } + + try { + p2pNode->run(); + } catch (std::exception& e) { + logger(Logging::ERROR) << "exception in p2p::run: " << e.what(); + } + + core->deinit(); + p2pNode->deinit(); + core->set_cryptonote_protocol(NULL); + protocol->set_p2p_endpoint(NULL); + + p2pNode.reset(); + protocol.reset(); + core.reset(); +} + +bool InProcTestNode::startMining(size_t threadsCount, const std::string &address) { + assert(core.get()); + AccountPublicAddress addr; + m_currency.parseAccountAddressString(address, addr); + return core->get_miner().start(addr, threadsCount, boost::thread_attributes()); +} + +bool InProcTestNode::stopMining() { + assert(core.get()); + return core->get_miner().stop(); +} + +bool InProcTestNode::stopDaemon() { + if (!p2pNode.get()) { + return false; + } + + p2pNode->send_stop_signal(); + m_thread.join(); + return true; +} + +bool InProcTestNode::getBlockTemplate(const std::string &minerAddress, CryptoNote::Block &blockTemplate, uint64_t &difficulty) { + AccountPublicAddress addr; + m_currency.parseAccountAddressString(minerAddress, addr); + uint32_t height = 0; + return core->get_block_template(blockTemplate, addr, difficulty, height, blobdata()); +} + +bool InProcTestNode::submitBlock(const std::string &block) { + block_verification_context bvc = boost::value_initialized(); + core->handle_incoming_block_blob(block, bvc, true, true); + return bvc.m_added_to_main_chain; +} + +bool InProcTestNode::getTailBlockId(crypto::hash &tailBlockId) { + tailBlockId = core->get_tail_id(); + return true; +} + +bool InProcTestNode::makeINode(std::unique_ptr &node) { + + std::unique_ptr inprocNode(new CryptoNote::InProcessNode(*core, *protocol)); + + std::promise p; + auto future = p.get_future(); + + inprocNode->init([&p](std::error_code ec) { + std::promise localPromise(std::move(p)); + localPromise.set_value(ec); + }); + + auto ec = future.get(); + + if (!ec) { + node = std::move(inprocNode); + return true; + } + + return false; +} + +uint64_t InProcTestNode::getLocalHeight() { + return core->get_current_blockchain_height(); +} + +} diff --git a/tests/integration_test_lib/InProcTestNode.h b/tests/integration_test_lib/InProcTestNode.h new file mode 100644 index 0000000000..fa389d0162 --- /dev/null +++ b/tests/integration_test_lib/InProcTestNode.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "TestNode.h" +#include "NetworkConfiguration.h" + +#include +#include +#include + +#include + + +namespace CryptoNote { +class core; +class cryptonote_protocol_handler; +class node_server; +class Currency; +} + +namespace Tests { + +class InProcTestNode : public TestNode { +public: + InProcTestNode(const TestNodeConfiguration& cfg, const CryptoNote::Currency& currency); + ~InProcTestNode(); + + virtual bool startMining(size_t threadsCount, const std::string &address) override; + virtual bool stopMining() override; + virtual bool stopDaemon() override; + virtual bool getBlockTemplate(const std::string &minerAddress, CryptoNote::Block &blockTemplate, uint64_t &difficulty) override; + virtual bool submitBlock(const std::string &block) override; + virtual bool getTailBlockId(crypto::hash &tailBlockId) override; + virtual bool makeINode(std::unique_ptr &node) override; + virtual uint64_t getLocalHeight() override; + +private: + + void workerThread(std::promise& initPromise); + + std::unique_ptr core; + std::unique_ptr protocol; + std::unique_ptr p2pNode; + + std::thread m_thread; + const CryptoNote::Currency& m_currency; + TestNodeConfiguration m_cfg; +}; + +} diff --git a/tests/integration_test_lib/Logger.cpp b/tests/integration_test_lib/Logger.cpp index 71937558e1..b37a99aebf 100755 --- a/tests/integration_test_lib/Logger.cpp +++ b/tests/integration_test_lib/Logger.cpp @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include "Logger.h" #include @@ -28,4 +45,4 @@ void CLogger::Log(const std::string & log_info, LOG_LEVEL log_lvl, int indent_in if (indent_inc>0)indent+=indent_inc; (log_lvl. + #pragma once #ifdef LOG_ERROR @@ -53,4 +70,3 @@ class CLogger CLogger(const CLogger& root); CLogger& operator=(const CLogger&); }; - diff --git a/tests/integration_test_lib/NetworkConfiguration.h b/tests/integration_test_lib/NetworkConfiguration.h new file mode 100644 index 0000000000..8a3c34cc99 --- /dev/null +++ b/tests/integration_test_lib/NetworkConfiguration.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace Tests { + +enum class NodeType { + RPC, + InProcess +}; + +struct TestNodeConfiguration { + NodeType nodeType = NodeType::RPC; + bool testnet = true; + uint16_t p2pPort; + uint16_t rpcPort; + + std::string dataDir; + std::string blockchainLocation; // copy blockchain from this path + std::string logFile; + std::string daemonPath; // only for rpc node + bool cleanupDataDir = true; + + std::vector exclusiveNodes; +}; + +} diff --git a/tests/integration_test_lib/NodeObserver.h b/tests/integration_test_lib/NodeObserver.h new file mode 100644 index 0000000000..d9afb6378a --- /dev/null +++ b/tests/integration_test_lib/NodeObserver.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "INode.h" +#include +#include + +namespace CryptoNote { + + +template +class ObservableValue { +public: + ObservableValue(const T defaultValue = 0) : + m_prev(defaultValue), m_value(defaultValue) { + } + + void init(T value) { + std::lock_guard lk(m_mutex); + m_value = m_prev = value; + } + + void set(T value) { + std::lock_guard lk(m_mutex); + m_value = value; + m_cv.notify_all(); + } + + T get() { + std::lock_guard lk(m_mutex); + return m_value; + } + + bool waitFor(std::chrono::milliseconds ms, T& value) { + std::unique_lock lk(m_mutex); + + if (m_cv.wait_for(lk, ms, [this] { return m_prev != m_value; })) { + value = m_prev = m_value; + return true; + } + + return false; + } + + T wait() { + std::unique_lock lk(m_mutex); + + m_cv.wait(lk, [this] { return m_prev != m_value; }); + m_prev = m_value; + return m_value; + } + +private: + + std::mutex m_mutex; + std::condition_variable m_cv; + + T m_prev; + T m_value; +}; + +class NodeObserver: public INodeObserver { +public: + + NodeObserver(INode& node) : m_node(node) { + m_knownHeight.init(node.getLastKnownBlockHeight()); + node.addObserver(this); + } + + ~NodeObserver() { + m_node.removeObserver(this); + } + + virtual void lastKnownBlockHeightUpdated(uint64_t height) override { + m_knownHeight.set(height); + } + + virtual void localBlockchainUpdated(uint64_t height) override { + m_localHeight.set(height); + } + + virtual void peerCountUpdated(size_t count) override { + m_peerCount.set(count); + } + + bool waitLastKnownBlockHeightUpdated(std::chrono::milliseconds ms, uint64_t& value) { + return m_knownHeight.waitFor(ms, value); + } + + bool waitLocalBlockchainUpdated(std::chrono::milliseconds ms, uint64_t& value) { + return m_localHeight.waitFor(ms, value); + } + + uint64_t waitLastKnownBlockHeightUpdated() { + return m_knownHeight.wait(); + } + + ObservableValue m_knownHeight; + ObservableValue m_localHeight; + ObservableValue m_peerCount; + +private: + + INode& m_node; +}; + + +} diff --git a/tests/integration_test_lib/Process.cpp b/tests/integration_test_lib/Process.cpp new file mode 100644 index 0000000000..cdbe3c1a55 --- /dev/null +++ b/tests/integration_test_lib/Process.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Process.h" + +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#endif + +namespace Tests { + + void Process::startChild(const std::string& executablePath, const std::vector& args) { + +#if defined WIN32 + std::stringstream ss; + ss << "start /MIN " << executablePath; + + for (const auto& arg: args) { + ss << " \"" << arg << "\""; + } + + auto cmdline = ss.str(); + system(cmdline.c_str()); + +#else + std::vector cargs; + cargs.push_back(executablePath.c_str()); + for (const auto& arg : args) { + cargs.push_back(arg.c_str()); + } + + cargs.push_back(nullptr); + + auto pid = fork(); + + if (pid == 0) { + if (execv(executablePath.c_str(), (char**)&cargs[0]) == -1) { + printf("Failed to start %s: %d\n", executablePath.c_str(), errno); + exit(404); + } + } else if (pid > 0) { + m_pid = pid; + } else if (pid < 0) { + throw std::runtime_error("fork() failed"); + } +#endif + + } + + void Process::wait() { +#ifndef _WIN32 + if (m_pid == 0) { + return; + } + + int status; + waitpid(m_pid, &status, 0); + m_pid = 0; +#endif + } + +} diff --git a/tests/integration_test_lib/Process.h b/tests/integration_test_lib/Process.h new file mode 100644 index 0000000000..e876be40c7 --- /dev/null +++ b/tests/integration_test_lib/Process.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace Tests { + + class Process { + public: + + void startChild(const std::string& executablePath, const std::vector& args = {}); + void wait(); + + private: + + size_t m_pid = 0; + + }; +} diff --git a/tests/integration_test_lib/RPCTestNode.cpp b/tests/integration_test_lib/RPCTestNode.cpp index b7ccb1b8b6..96a5263e27 100755 --- a/tests/integration_test_lib/RPCTestNode.cpp +++ b/tests/integration_test_lib/RPCTestNode.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,150 +17,126 @@ #include "RPCTestNode.h" +#include #include #include -#include "rpc/core_rpc_server_commands_defs.h" +#include "Common/StringTools.h" +#include "cryptonote_core/cryptonote_format_utils.h" #include "node_rpc_proxy/NodeRpcProxy.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "rpc/HttpClient.h" +#include "rpc/JsonRpc.h" -#include "serialization/JsonOutputStreamSerializer.h" -#include "serialization/JsonInputStreamSerializer.h" -#include "storages/portable_storage_base.h" -#include "storages/portable_storage_template_helper.h" - -#include "../contrib/epee/include/net/jsonrpc_structs.h" - -#include -#include -#include -#include "HTTP/HttpParser.h" - -#include "CoreRpcSerialization.h" #include "Logger.h" -using namespace Tests; -using namespace cryptonote; +using namespace CryptoNote; using namespace System; -void RPCTestNode::prepareRequest(HttpRequest& httpReq, const std::string& method, const std::string& params){ - httpReq.setUrl(method); - httpReq.addHeader("Host", "127.0.0.1:" + boost::lexical_cast(m_rpcPort)); - httpReq.addHeader("Content-Type", "application/json-rpc"); - httpReq.setBody(params); -} +namespace Tests { -void RPCTestNode::sendRequest(const HttpRequest& httpReq, HttpResponse& httpResp) { - TcpConnector connector(m_dispatcher, "127.0.0.1", m_rpcPort); - TcpConnection connection = connector.connect(); - TcpStreambuf streambuf(connection); - std::iostream connectionStream(&streambuf); - LOG_DEBUG("invoke rpc:" + httpReq.getMethod() + " " + httpReq.getBody()); - connectionStream << httpReq; - connectionStream.flush(); - HttpParser parser; - parser.receiveResponse(connectionStream, httpResp); +RPCTestNode::RPCTestNode(uint16_t port, System::Dispatcher& d) : + m_rpcPort(port), m_dispatcher(d), m_httpClient(d, "127.0.0.1", port) { } bool RPCTestNode::startMining(size_t threadsCount, const std::string& address) { LOG_DEBUG("startMining()"); - using namespace cryptonote; - COMMAND_RPC_START_MINING::request req; - COMMAND_RPC_START_MINING::response resp; - req.miner_address = address; - req.threads_count = threadsCount; - std::stringstream requestStream; - JsonOutputStreamSerializer enumerator; - enumerator(req, ""); - requestStream << enumerator; - HttpRequest httpReq; - prepareRequest(httpReq, "/start_mining", requestStream.str()); - HttpResponse httpResp; - sendRequest(httpReq, httpResp); - if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; - std::stringstream responseStream(httpResp.getBody()); - JsonInputStreamSerializer en(responseStream); - en(resp, ""); - if (resp.status != CORE_RPC_STATUS_OK) { - std::cout << "startMining() RPC call fail: " << resp.status; + + try { + COMMAND_RPC_START_MINING::request req; + COMMAND_RPC_START_MINING::response resp; + req.miner_address = address; + req.threads_count = threadsCount; + + invokeJsonCommand(m_httpClient, "/start_mining", req, resp); + if (resp.status != CORE_RPC_STATUS_OK) { + throw std::runtime_error(resp.status); + } + } catch (std::exception& e) { + std::cout << "startMining() RPC call fail: " << e.what(); return false; } return true; } -bool RPCTestNode::submitBlock(const std::string& block) { - HttpRequest httpReq; - httpReq.setUrl("/json_rpc"); - httpReq.addHeader("Host", "127.0.0.1:" + boost::lexical_cast(m_rpcPort)); - httpReq.addHeader("Content-Type", "application/json-rpc"); - JsonValue request(cryptonote::JsonValue::OBJECT); - JsonValue jsonRpc; - jsonRpc = "2.0"; - request.insert("jsonrpc", jsonRpc); - JsonValue methodString; - methodString = "submitblock"; - request.insert("method", methodString); - JsonValue id; - id = "sync"; - request.insert("id", id); - JsonValue params(JsonValue::ARRAY); - JsonValue blockstr; - blockstr = block.c_str(); - params.pushBack(blockstr); - request.insert("params", params); - std::stringstream jsonOutputStream; - jsonOutputStream << request; - httpReq.setBody(jsonOutputStream.str()); - TcpConnector connector(m_dispatcher, "127.0.0.1", m_rpcPort); - TcpConnection connection = connector.connect(); - TcpStreambuf streambuf(connection); - std::iostream connectionStream(&streambuf); - LOG_DEBUG("invoke json-rpc: " + httpReq.getBody()); - connectionStream << httpReq; - connectionStream.flush(); - HttpResponse httpResp; - HttpParser parser; - parser.receiveResponse(connectionStream, httpResp); - connectionStream.flush(); - if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; - - epee::serialization::portable_storage ps; - if (!ps.load_from_json(httpResp.getBody())) { - LOG_ERROR("cannot parse response from daemon: " + httpResp.getBody()); +bool RPCTestNode::getBlockTemplate(const std::string& minerAddress, CryptoNote::Block& blockTemplate, uint64_t& difficulty) { + LOG_DEBUG("getBlockTemplate()"); + + try { + COMMAND_RPC_GETBLOCKTEMPLATE::request req; + COMMAND_RPC_GETBLOCKTEMPLATE::response rsp; + req.wallet_address = minerAddress; + req.reserve_size = 0; + + JsonRpc::invokeJsonRpcCommand(m_httpClient, "getblocktemplate", req, rsp); + if (rsp.status != CORE_RPC_STATUS_OK) { + throw std::runtime_error(rsp.status); + } + + difficulty = rsp.difficulty; + + CryptoNote::blobdata blockBlob = ::Common::asString(::Common::fromHex(rsp.blocktemplate_blob)); + return CryptoNote::parse_and_validate_block_from_blob(blockBlob, blockTemplate); + } catch (std::exception& e) { + LOG_ERROR("JSON-RPC call startMining() failed: " + std::string(e.what())); return false; } - epee::json_rpc::response jsonRpcResponse; - jsonRpcResponse.load(ps); + return true; +} - if (jsonRpcResponse.error.code || jsonRpcResponse.error.message.size()) { - LOG_ERROR("RPC call of submit_block returned error: " + TO_STRING(jsonRpcResponse.error.code) + ", message: " + jsonRpcResponse.error.message); +bool RPCTestNode::submitBlock(const std::string& block) { + LOG_DEBUG("submitBlock()"); + + try { + COMMAND_RPC_SUBMITBLOCK::request req; + COMMAND_RPC_SUBMITBLOCK::response res; + req.push_back(block); + JsonRpc::invokeJsonRpcCommand(m_httpClient, "submitblock", req, res); + if (res.status != CORE_RPC_STATUS_OK) { + throw std::runtime_error(res.status); + } + } catch (std::exception& e) { + LOG_ERROR("RPC call of submit_block returned error: " + std::string(e.what())); return false; } - - if (jsonRpcResponse.result.status != CORE_RPC_STATUS_OK) return false; + return true; } bool RPCTestNode::stopMining() { LOG_DEBUG("stopMining()"); - using namespace cryptonote; - COMMAND_RPC_STOP_MINING::request req; - COMMAND_RPC_STOP_MINING::response resp; - std::stringstream requestStream; - JsonOutputStreamSerializer enumerator; - enumerator(req, ""); - requestStream << enumerator; - HttpRequest httpReq; - prepareRequest(httpReq, "/stop_mining", requestStream.str()); - HttpResponse httpResp; - sendRequest(httpReq, httpResp); - if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; - std::stringstream responseStream(httpResp.getBody()); - JsonInputStreamSerializer en(responseStream); - en(resp, ""); - if (resp.status != CORE_RPC_STATUS_OK) { - std::cout << "stopMining() RPC call fail: " << resp.status; + + try { + COMMAND_RPC_STOP_MINING::request req; + COMMAND_RPC_STOP_MINING::response resp; + invokeJsonCommand(m_httpClient, "/stop_mining", req, resp); + if (resp.status != CORE_RPC_STATUS_OK) { + throw std::runtime_error(resp.status); + } + } catch (std::exception& e) { + std::cout << "stopMining() RPC call fail: " << e.what(); + return false; + } + + return true; +} + +bool RPCTestNode::getTailBlockId(crypto::hash& tailBlockId) { + LOG_DEBUG("getTailBlockId()"); + + try { + COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req; + COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp; + JsonRpc::invokeJsonRpcCommand(m_httpClient, "getlastblockheader", req, rsp); + if (rsp.status != CORE_RPC_STATUS_OK) { + throw std::runtime_error(rsp.status); + } + + return ::Common::podFromHex(rsp.block_header.hash, tailBlockId); + } catch (std::exception& e) { + LOG_ERROR("JSON-RPC call getTailBlockId() failed: " + std::string(e.what())); return false; } @@ -168,41 +144,60 @@ bool RPCTestNode::stopMining() { } bool RPCTestNode::makeINode(std::unique_ptr& node) { - node.reset(new cryptonote::NodeRpcProxy("127.0.0.1", m_rpcPort)); - node->init([&](std::error_code ec) { + std::unique_ptr newNode(new CryptoNote::NodeRpcProxy("127.0.0.1", m_rpcPort)); + + std::promise prom; + std::future fut(prom.get_future()); + + newNode->init([&](std::error_code ec) { + std::promise localProm(std::move(prom)); + if (ec) { LOG_ERROR("init error: " + ec.message() + ':' + TO_STRING(ec.value())); } else { LOG_DEBUG("NodeRPCProxy on port " + TO_STRING(m_rpcPort) + " initialized"); } + + localProm.set_value(ec); }); - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); //for initial update - return true; -} + if (!fut.get()) { + node = std::move(newNode); + return true; + } + return false; +} bool RPCTestNode::stopDaemon() { - LOG_DEBUG("stopDaemon()"); - using namespace cryptonote; - COMMAND_RPC_STOP_DAEMON::request req; - COMMAND_RPC_STOP_DAEMON::response resp; - std::stringstream requestStream; - JsonOutputStreamSerializer enumerator; - enumerator(req, ""); - requestStream << enumerator; - HttpRequest httpReq; - prepareRequest(httpReq, "/stop_daemon", requestStream.str()); - HttpResponse httpResp; - sendRequest(httpReq, httpResp); - if (httpResp.getStatus() != HttpResponse::STATUS_200) return false; - std::stringstream responseStream(httpResp.getBody()); - JsonInputStreamSerializer en(responseStream); - en(resp, ""); - if (resp.status != CORE_RPC_STATUS_OK) { - std::cout << "stopDaemon() RPC call fail: " << resp.status; + try { + LOG_DEBUG("stopDaemon()"); + COMMAND_RPC_STOP_DAEMON::request req; + COMMAND_RPC_STOP_DAEMON::response resp; + invokeJsonCommand(m_httpClient, "/stop_daemon", req, resp); + if (resp.status != CORE_RPC_STATUS_OK) { + throw std::runtime_error(resp.status); + } + } catch (std::exception& e) { + std::cout << "stopDaemon() RPC call fail: " << e.what(); return false; } return true; -} \ No newline at end of file +} + +uint64_t RPCTestNode::getLocalHeight() { + try { + CryptoNote::COMMAND_RPC_GET_INFO::request req; + CryptoNote::COMMAND_RPC_GET_INFO::response rsp; + invokeJsonCommand(m_httpClient, "/getinfo", req, rsp); + if (rsp.status == CORE_RPC_STATUS_OK) { + return rsp.height; + } + } catch (std::exception&) { + } + + return 0; +} + +} diff --git a/tests/integration_test_lib/RPCTestNode.h b/tests/integration_test_lib/RPCTestNode.h index 706c1bd81c..7e185807a3 100755 --- a/tests/integration_test_lib/RPCTestNode.h +++ b/tests/integration_test_lib/RPCTestNode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,28 +21,34 @@ #include #include "HTTP/HttpRequest.h" #include "HTTP/HttpResponse.h" - +#include "rpc/HttpClient.h" #include "TestNode.h" -using namespace cryptonote; namespace Tests { - class RPCTestNode : public Common::TestNode { - public: - RPCTestNode(uint16_t port, System::Dispatcher& d) : m_rpcPort(port), m_dispatcher(d) {} - virtual bool startMining(size_t threadsCount, const std::string& address) override; - virtual bool stopMining() override; - virtual bool stopDaemon() override; - virtual bool submitBlock(const std::string& block) override; - virtual bool makeINode(std::unique_ptr& node) override; - virtual ~RPCTestNode() { } - - private: - void prepareRequest(HttpRequest& httpReq, const std::string& method, const std::string& params); - void sendRequest(const HttpRequest& httpReq, HttpResponse& httpResp); - - uint16_t m_rpcPort; - System::Dispatcher& m_dispatcher; - }; + +using namespace CryptoNote; + +class RPCTestNode : public TestNode { +public: + RPCTestNode(uint16_t port, System::Dispatcher &d); + + virtual bool startMining(size_t threadsCount, const std::string &address) override; + virtual bool stopMining() override; + virtual bool stopDaemon() override; + virtual bool getBlockTemplate(const std::string &minerAddress, CryptoNote::Block &blockTemplate, uint64_t &difficulty) override; + virtual bool submitBlock(const std::string &block) override; + virtual bool getTailBlockId(crypto::hash &tailBlockId) override; + virtual bool makeINode(std::unique_ptr &node) override; + virtual uint64_t getLocalHeight() override; + + virtual ~RPCTestNode() {} + +private: + uint16_t m_rpcPort; + System::Dispatcher &m_dispatcher; + CryptoNote::HttpClient m_httpClient; +}; + } diff --git a/tests/integration_test_lib/TestNetwork.cpp b/tests/integration_test_lib/TestNetwork.cpp new file mode 100644 index 0000000000..838da0e55f --- /dev/null +++ b/tests/integration_test_lib/TestNetwork.cpp @@ -0,0 +1,261 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TestNetwork.h" + +#include + +#include "InProcTestNode.h" +#include "RPCTestNode.h" + +#ifdef _WIN32 +const std::string bytecoinDaemon = "bytecoind.exe"; +#else +const std::string bytecoinDaemon = "bytecoind"; +#endif + +namespace { + +using namespace Tests; + +void writeConfiguration(const std::string& confFile, const TestNodeConfiguration& cfg) { + std::ofstream config(confFile, std::ios_base::trunc | std::ios_base::out); + + config + << "rpc-bind-port=" << cfg.rpcPort << std::endl + << "p2p-bind-port=" << cfg.p2pPort << std::endl + << "log-level=4" << std::endl + << "log-file=" << cfg.logFile << std::endl; + + for (const auto& ex : cfg.exclusiveNodes) { + config << "add-exclusive-node=" << ex << std::endl; + } +} + +bool waitDaemonReady(TestNode& node) { + for (size_t i = 0;; ++i) { + if (node.getLocalHeight() > 0) { + break; + } else if (i < 2 * 60) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } else { + return false; + } + } + return true; +} + +void copyBlockchainFiles(bool testnet, const std::string& from, const std::string& to) { + boost::filesystem::path fromPath(from); + boost::filesystem::path toPath(to); + + auto files = { + std::make_pair("blockindexes.dat", true), + std::make_pair("blocks.dat", true), + std::make_pair("blockscache.dat", false), + std::make_pair("blockchainindices.dat", false) + }; + + for (const auto& item : files) { + try { + boost::filesystem::path filePath = std::string(testnet ? "testnet_" : "") + item.first; + boost::filesystem::copy(fromPath / filePath, toPath / filePath); + } catch (...) { + if (item.second) { + // if file is required, the rethrow error + throw; + } + } + } +} + +} + + +namespace Tests { + + +TestNetworkBuilder::TestNetworkBuilder(size_t nodeCount, Topology topology, uint16_t rpcBasePort, uint16_t p2pBasePort) : + nodeCount(nodeCount), + topology(topology), + rpcBasePort(rpcBasePort), + p2pBasePort(p2pBasePort), + baseDataDir("."), + testnet(true) +{} + +std::vector TestNetworkBuilder::build() { + std::vector cfg; + + for (size_t i = 0; i < nodeCount; ++i) { + cfg.push_back(buildNodeConfiguration(i)); + } + + return cfg; +} + +TestNetworkBuilder& TestNetworkBuilder::setDataDirectory(const std::string& dataDir) { + baseDataDir = dataDir; + return *this; +} + +TestNetworkBuilder& TestNetworkBuilder::setTestnet(bool isTestnet) { + testnet = isTestnet; + return *this; +} + +TestNodeConfiguration TestNetworkBuilder::buildNodeConfiguration(size_t index) { + TestNodeConfiguration cfg; + + if (!baseDataDir.empty()) { + cfg.dataDir = baseDataDir + "/node" + std::to_string(index); + } + + cfg.daemonPath = bytecoinDaemon; // default + cfg.testnet = testnet; + cfg.logFile = "test_bytecoind" + std::to_string(index) + ".log"; + + uint16_t rpcPort = static_cast(rpcBasePort + index); + uint16_t p2pPort = static_cast(p2pBasePort + index); + + cfg.p2pPort = p2pPort; + cfg.rpcPort = rpcPort; + + if (nodeCount > 1) { + switch (topology) { + case Topology::Line: + if (index != 0) { + cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pPort - 1)); + } + break; + + case Topology::Ring: { + uint16_t p2pExternalPort = static_cast(p2pBasePort + (index + 1) % nodeCount); + cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pExternalPort)); + break; + } + + case Topology::Star: + if (index == 0) { + for (size_t node = 1; node < nodeCount; ++node) { + cfg.exclusiveNodes.push_back("127.0.0.1:" + std::to_string(p2pBasePort + node)); + } + } + break; + } + } + + return cfg; +} + +TestNetwork::TestNetwork(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency) : + m_dispatcher(dispatcher), + m_currency(currency) { +} + +void TestNetwork::addNodes(const std::vector& nodes) { + for (const auto& n : nodes) { + addNode(n); + } +} + +void TestNetwork::addNode(const TestNodeConfiguration& cfg) { + std::unique_ptr node; + + boost::system::error_code ec; + boost::filesystem::remove_all(cfg.dataDir, ec); + boost::filesystem::create_directory(cfg.dataDir); + + if (!cfg.blockchainLocation.empty()) { + copyBlockchainFiles(cfg.testnet, cfg.blockchainLocation, cfg.dataDir); + } + + switch (cfg.nodeType) { + case NodeType::InProcess: + node.reset(new InProcTestNode(cfg, m_currency)); + break; + case NodeType::RPC: + node = startDaemon(cfg); + break; + } + + nodes.push_back(std::make_pair(std::move(node), cfg)); +} + +void TestNetwork::waitNodesReady() { + for (auto& node : nodes) { + if (!waitDaemonReady(*node.first)) { + throw std::runtime_error("Daemon startup failure (timeout)"); + } + } +} + +TestNode& TestNetwork::getNode(size_t index) { + if (index >= nodes.size()) { + throw std::runtime_error("Invalid node index"); + } + + return *nodes[index].first; +} + +void TestNetwork::shutdown() { + for (auto& node : nodes) { + node.first->stopDaemon(); + } + + for (auto& daemon : m_daemons) { + daemon.wait(); + } + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + for (auto& node : nodes) { + if (node.second.cleanupDataDir) { + boost::filesystem::remove_all(node.second.dataDir); + } + } + +} + + +std::unique_ptr TestNetwork::startDaemon(const TestNodeConfiguration& cfg) { + if (!boost::filesystem::exists(cfg.daemonPath)) { + throw std::runtime_error("daemon binary wasn't found"); + } + + writeConfiguration(cfg.dataDir + "/daemon.conf", cfg); + + Process process; + std::vector daemonArgs = { "--data-dir=" + cfg.dataDir, "--config-file=daemon.conf" }; + + if (cfg.testnet) { + daemonArgs.emplace_back("--testnet"); + } + + process.startChild(cfg.daemonPath, daemonArgs); + + std::unique_ptr node(new RPCTestNode(cfg.rpcPort, m_dispatcher)); + m_daemons.push_back(process); + + return node; +} + + + + + +} diff --git a/tests/integration_test_lib/TestNetwork.h b/tests/integration_test_lib/TestNetwork.h new file mode 100644 index 0000000000..050e475314 --- /dev/null +++ b/tests/integration_test_lib/TestNetwork.h @@ -0,0 +1,87 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "NetworkConfiguration.h" +#include "Process.h" +#include "TestNode.h" + +namespace System { +class Dispatcher; +} + +namespace CryptoNote { +class Currency; +} + +namespace Tests { + +enum class Topology { + Ring, + Line, + Star +}; + + +class TestNetworkBuilder { +public: + + TestNetworkBuilder(size_t nodeCount, Topology topology = Topology::Line, uint16_t rpcBasePort = 9200, uint16_t p2pBasePort = 9000); + + TestNetworkBuilder& setDataDirectory(const std::string& dataDir); + TestNetworkBuilder& setTestnet(bool isTestnet); + + std::vector build(); + +private: + + TestNodeConfiguration buildNodeConfiguration(size_t index); + + uint16_t rpcBasePort; + uint16_t p2pBasePort; + Topology topology; + size_t nodeCount; + std::string baseDataDir; + bool testnet; +}; + + +class TestNetwork { + +public: + + TestNetwork(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency); + + void addNodes(const std::vector& nodes); + void addNode(const TestNodeConfiguration& cfg); + void waitNodesReady(); + void shutdown(); + + TestNode& getNode(size_t index); + +private: + + std::unique_ptr startDaemon(const TestNodeConfiguration& cfg); + + std::vector, TestNodeConfiguration>> nodes; + System::Dispatcher& m_dispatcher; + const CryptoNote::Currency& m_currency; + std::vector m_daemons; +}; + +} diff --git a/tests/integration_test_lib/TestNode.h b/tests/integration_test_lib/TestNode.h index 5e6f1582e9..0d8a56b21e 100755 --- a/tests/integration_test_lib/TestNode.h +++ b/tests/integration_test_lib/TestNode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -20,18 +20,20 @@ #include #include - namespace Tests { - namespace Common { - class TestNode { - public: - virtual bool startMining(size_t threadsCount, const std::string& address) = 0; - virtual bool stopMining() = 0; - virtual bool stopDaemon() = 0; - virtual bool submitBlock(const std::string& block) = 0; - virtual bool makeINode(std::unique_ptr& node) = 0; - virtual ~TestNode() { } - }; - } -} \ No newline at end of file +class TestNode { +public: + virtual bool startMining(size_t threadsCount, const std::string &address) = 0; + virtual bool stopMining() = 0; + virtual bool stopDaemon() = 0; + virtual bool getBlockTemplate(const std::string &minerAddress, CryptoNote::Block &blockTemplate, uint64_t &difficulty) = 0; + virtual bool submitBlock(const std::string &block) = 0; + virtual bool getTailBlockId(crypto::hash &tailBlockId) = 0; + virtual bool makeINode(std::unique_ptr &node) = 0; + virtual uint64_t getLocalHeight() = 0; + + virtual ~TestNode() {} +}; + +} diff --git a/tests/integration_test_lib/TestWallet.cpp b/tests/integration_test_lib/TestWallet.cpp new file mode 100644 index 0000000000..25bbb57cec --- /dev/null +++ b/tests/integration_test_lib/TestWallet.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TestWallet.h" + +namespace Tests { +namespace Common { + +using namespace CryptoNote; + +const std::string TEST_PASSWORD = "password"; + +TestWallet::TestWallet(System::Dispatcher& dispatcher, const Currency& currency, INode& node) : + m_dispatcher(dispatcher), + m_synchronizationCompleted(dispatcher), + m_someTransactionUpdated(dispatcher), + m_currency(currency), + m_node(node), + m_wallet(new CryptoNote::Wallet(currency, node)), + m_currentHeight(0) { + m_wallet->addObserver(this); +} + +TestWallet::~TestWallet() { + m_wallet->removeObserver(this); + // Make sure all remote spawns are executed + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + m_dispatcher.yield(); +} + +std::error_code TestWallet::init() { + CryptoNote::account_base walletAccount; + walletAccount.generate(); + + WalletAccountKeys walletKeys; + walletKeys.spendPublicKey = reinterpret_cast(walletAccount.get_keys().m_account_address.m_spendPublicKey); + walletKeys.spendSecretKey = reinterpret_cast(walletAccount.get_keys().m_spend_secret_key); + walletKeys.viewPublicKey = reinterpret_cast(walletAccount.get_keys().m_account_address.m_viewPublicKey); + walletKeys.viewSecretKey = reinterpret_cast(walletAccount.get_keys().m_view_secret_key); + + m_wallet->initWithKeys(walletKeys, TEST_PASSWORD); + m_synchronizationCompleted.wait(); + return m_lastSynchronizationResult; +} + +namespace { + struct TransactionSendingWaiter : public IWalletObserver { + System::Dispatcher& m_dispatcher; + System::Event m_event; + bool m_waiting = false; + TransactionId m_expectedTxId; + std::error_code m_result; + + TransactionSendingWaiter(System::Dispatcher& dispatcher) : m_dispatcher(dispatcher), m_event(dispatcher) { + } + + void wait(TransactionId expectedTxId) { + m_waiting = true; + m_expectedTxId = expectedTxId; + m_event.wait(); + m_waiting = false; + } + + virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) { + m_dispatcher.remoteSpawn([this, transactionId, result]() { + if (m_waiting && m_expectedTxId == transactionId) { + m_result = result; + m_event.set(); + } + }); + } + }; +} + +std::error_code TestWallet::sendTransaction(const std::string& address, uint64_t amount, TransactionHash& txHash) { + TransactionSendingWaiter transactionSendingWaiter(m_dispatcher); + m_wallet->addObserver(&transactionSendingWaiter); + + Transfer transfer{ address, static_cast(amount) }; + auto txId = m_wallet->sendTransaction(transfer, m_currency.minimumFee()); + transactionSendingWaiter.wait(txId); + m_wallet->removeObserver(&transactionSendingWaiter); + // TODO workaround: make sure ObserverManager doesn't have local pointers to transactionSendingWaiter, so it can be destroyed + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // Run all spawned handlers from TransactionSendingWaiter::sendTransactionCompleted + m_dispatcher.yield(); + + TransactionInfo txInfo; + if (!m_wallet->getTransaction(txId, txInfo)) { + return std::make_error_code(std::errc::identifier_removed); + } + + txHash = txInfo.hash; + return transactionSendingWaiter.m_result; +} + +void TestWallet::waitForSynchronizationToHeight(uint32_t height) { + while (m_synchronizedHeight < height) { + m_synchronizationCompleted.wait(); + } +} + +IWallet* TestWallet::wallet() { + return m_wallet.get(); +} + +AccountPublicAddress TestWallet::address() const { + std::string addressString = m_wallet->getAddress(); + AccountPublicAddress address; + bool ok = m_currency.parseAccountAddressString(addressString, address); + assert(ok); + return address; +} + +void TestWallet::synchronizationCompleted(std::error_code result) { + m_dispatcher.remoteSpawn([this, result]() { + m_lastSynchronizationResult = result; + m_synchronizedHeight = m_currentHeight; + m_synchronizationCompleted.set(); + m_synchronizationCompleted.clear(); + }); +} + +void TestWallet::synchronizationProgressUpdated(uint64_t current, uint64_t total) { + m_dispatcher.remoteSpawn([this, current]() { + m_currentHeight = static_cast(current); + }); +} + +} +} diff --git a/tests/integration_test_lib/TestWallet.h b/tests/integration_test_lib/TestWallet.h new file mode 100644 index 0000000000..b969462117 --- /dev/null +++ b/tests/integration_test_lib/TestWallet.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "cryptonote_core/Currency.h" +#include "INode.h" +#include "IWallet.h" +#include "System/Dispatcher.h" +#include "System/Event.h" +#include "wallet/Wallet.h" + +namespace Tests { +namespace Common { + +class TestWallet : private CryptoNote::IWalletObserver { +public: + TestWallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, CryptoNote::INode& node); + ~TestWallet(); + + std::error_code init(); + std::error_code sendTransaction(const std::string& address, uint64_t amount, CryptoNote::TransactionHash& txHash); + void waitForSynchronizationToHeight(uint32_t height); + CryptoNote::IWallet* wallet(); + CryptoNote::AccountPublicAddress address() const; + +protected: + virtual void synchronizationCompleted(std::error_code result) override; + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override; + +private: + System::Dispatcher& m_dispatcher; + System::Event m_synchronizationCompleted; + System::Event m_someTransactionUpdated; + + CryptoNote::INode& m_node; + const CryptoNote::Currency& m_currency; + std::unique_ptr m_wallet; + std::unique_ptr m_walletObserver; + uint32_t m_currentHeight; + uint32_t m_synchronizedHeight; + std::error_code m_lastSynchronizationResult; +}; + +} +} diff --git a/tests/integration_tests/BlockchainInfo.h b/tests/integration_tests/BlockchainInfo.h new file mode 100644 index 0000000000..2171d98275 --- /dev/null +++ b/tests/integration_tests/BlockchainInfo.h @@ -0,0 +1,69 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "serialization/SerializationOverloads.h" +#include "cryptonote_core/cryptonote_serialization.h" + +namespace CryptoNote { + void serialize(BlockCompleteEntry& v, const std::string& name, ISerializer& s) { + s.beginObject(name); + s(v.blockHash, "hash"); + s.binary(v.block, "block"); + s(v.txs, "transactions"); + s.endObject(); + } + + bool operator == (const BlockCompleteEntry& a, const BlockCompleteEntry& b) { + return + a.blockHash == b.blockHash && + a.block == b.block && + a.txs == b.txs; + } + + struct BlockchainInfo { + std::vector blocks; + std::unordered_map> globalOutputs; + + bool operator == (const BlockchainInfo& other) const { + return blocks == other.blocks && globalOutputs == other.globalOutputs; + } + + void serialize(ISerializer& s, const std::string& name) { + s.beginObject(name); + s(blocks, "blocks"); + s(globalOutputs, "outputs"); + s.endObject(); + } + }; + + void storeBlockchainInfo(const std::string& filename, BlockchainInfo& bc) { + JsonOutputStreamSerializer s; + s(bc, ""); + std::ofstream jsonBlocks(filename, std::ios::trunc); + jsonBlocks << s; + } + + void loadBlockchainInfo(const std::string& filename, BlockchainInfo& bc) { + std::ifstream jsonBlocks(filename); + JsonInputStreamSerializer s(jsonBlocks); + s(bc, ""); + } + + +} diff --git a/tests/integration_tests/IntegrationTests.cpp b/tests/integration_tests/IntegrationTests.cpp new file mode 100644 index 0000000000..e45be84feb --- /dev/null +++ b/tests/integration_tests/IntegrationTests.cpp @@ -0,0 +1,232 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" +#include + +#include "../integration_test_lib/BaseFunctionalTest.h" +#include "../integration_test_lib/NodeObserver.h" + +#include "wallet/Wallet.h" +#include "WalletObserver.h" + +using namespace CryptoNote; +using namespace Logging; + +extern Tests::Common::BaseFunctionalTestConfig baseCfg; +extern System::Dispatcher globalDispatcher; + +struct TotalWalletBalance { + + TotalWalletBalance(uint64_t actual_ = 0, uint64_t pending_ = 0) + : actual(actual_), pending(pending_) {} + + TotalWalletBalance(IWallet& wallet) + : TotalWalletBalance(wallet.actualBalance(), wallet.pendingBalance()) {} + + uint64_t actual = 0; + uint64_t pending = 0; + + uint64_t total() const { + return actual + pending; + } +}; + +class IntegrationTest : public Tests::Common::BaseFunctionalTest, public ::testing::Test { +public: + + IntegrationTest() : + currency(CryptoNote::CurrencyBuilder(log).testnet(true).currency()), + BaseFunctionalTest(currency, globalDispatcher, baseCfg), + logger(log, "IntegrationTest") { + } + + ~IntegrationTest() { + wallets.clear(); + inodes.clear(); + + stopTestnet(); + } + + void makeINodes() { + for (auto& n : nodeDaemons) { + std::unique_ptr node; + n->makeINode(node); + inodes.push_back(std::move(node)); + } + } + + void makeWallets() { + for (auto& n: inodes) { + std::unique_ptr wallet(new CryptoNote::Wallet(m_currency, *n)); + std::unique_ptr observer(new WalletObserver()); + + wallet->initAndGenerate(walletPassword); + wallet->addObserver(observer.get()); + + wallets.push_back(std::move(wallet)); + walletObservers.push_back(std::move(observer)); + } + } + + void mineBlocksFor(size_t node, const std::string& address, size_t blockCount) { + auto prevHeight = nodeDaemons[node]->getLocalHeight(); + nodeDaemons[node]->startMining(1, address); + + do { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } while (prevHeight + blockCount < nodeDaemons[node]->getLocalHeight()); + + nodeDaemons[node]->stopMining(); + } + + void printWalletBalances() { + for (auto& w: wallets) { + logger(INFO) << "Wallet " << w->getAddress().substr(0, 6); + logger(INFO) << " " << currency.formatAmount(w->actualBalance()) << " actual / " << currency.formatAmount(w->pendingBalance()) << " pending"; + } + } + + void mineEmptyBlocks(size_t nodeNum, size_t blocksCount) { + } + + void mineMoneyForWallet(size_t nodeNum, size_t walletNum) { + auto& wallet = *wallets[walletNum]; + auto& node = *nodeDaemons[nodeNum]; + + node.startMining(1, wallet.getAddress()); + walletObservers[walletNum]->waitActualBalanceChange(); + node.stopMining(); + + while (node.getLocalHeight() > walletObservers[walletNum]->getCurrentHeight()) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + std::error_code transferMoney(size_t srcWallet, size_t dstWallet, uint64_t amount, uint64_t fee) { + logger(INFO) + << "Transferring from " << wallets[srcWallet]->getAddress().substr(0, 6) + << " to " << wallets[dstWallet]->getAddress().substr(0, 6) << " " << currency.formatAmount(amount); + + CryptoNote::Transfer tr; + tr.address = wallets[dstWallet]->getAddress(); + tr.amount = amount; + std::error_code result; + + auto txId = wallets[srcWallet]->sendTransaction(tr, fee); + + logger(DEBUGGING) << "Transaction id = " << txId; + + return walletObservers[srcWallet]->waitSendResult(txId); + } + + void checkIncomingTransfer(size_t dstWallet, uint64_t amount) { + startMining(1); + + auto txId = walletObservers[dstWallet]->waitExternalTransaction(); + + stopMining(); + + TransactionInfo txInfo; + + ASSERT_TRUE(wallets[dstWallet]->getTransaction(txId, txInfo)); + ASSERT_EQ(txInfo.totalAmount, amount); + } + + std::string walletPassword = "pass"; + CryptoNote::Currency currency; + Logging::ConsoleLogger log; + Logging::LoggerRef logger; + + std::vector> inodes; + std::vector> wallets; + std::vector> walletObservers; +}; + + + +TEST_F(IntegrationTest, Wallet2Wallet) { + const uint64_t FEE = 1000000; + + launchTestnet(2); + + logger(INFO) << "Testnet launched"; + + makeINodes(); + makeWallets(); + + logger(INFO) << "Created wallets"; + + mineMoneyForWallet(0, 0); + + logger(INFO) << "Mined money"; + + printWalletBalances(); + + TotalWalletBalance w0pre(*wallets[0]); + TotalWalletBalance w1pre(*wallets[1]); + + auto sendAmount = w0pre.actual / 2; + + ASSERT_TRUE(!transferMoney(0, 1, sendAmount, currency.minimumFee())); + ASSERT_NO_FATAL_FAILURE(checkIncomingTransfer(1, sendAmount)); + + printWalletBalances(); + + TotalWalletBalance w0after(*wallets[0]); + TotalWalletBalance w1after(*wallets[1]); + + // check total + ASSERT_EQ(w0pre.total() + w1pre.total() - currency.minimumFee(), w0after.total() + w1after.total()); + + // check diff + ASSERT_EQ(sendAmount, w1after.total() - w1pre.total()); +} + +TEST_F(IntegrationTest, BlockPropagationSpeed) { + + launchTestnet(3, Line); + makeINodes(); + + { + std::unique_ptr& localNode = inodes.front(); + std::unique_ptr& remoteNode = inodes.back(); + + std::unique_ptr wallet; + makeWallet(wallet, localNode); + + NodeObserver localObserver(*localNode); + NodeObserver remoteObserver(*remoteNode); + + const size_t BLOCKS_COUNT = 10; + + nodeDaemons.front()->startMining(1, wallet->getAddress()); + + for (size_t blockNumber = 0; blockNumber < BLOCKS_COUNT; ++blockNumber) { + uint64_t localHeight = localObserver.waitLastKnownBlockHeightUpdated(); + uint64_t remoteHeight = 0; + + while (remoteHeight != localHeight) { + ASSERT_TRUE(remoteObserver.waitLastKnownBlockHeightUpdated(std::chrono::milliseconds(5000), remoteHeight)); + } + + logger(INFO) << "Iteration " << blockNumber + 1 << ": " << "height = " << localHeight; + } + + nodeDaemons.front()->stopMining(); + } +} diff --git a/tests/integration_tests/MultiVersion.cpp b/tests/integration_tests/MultiVersion.cpp new file mode 100644 index 0000000000..b5da360335 --- /dev/null +++ b/tests/integration_tests/MultiVersion.cpp @@ -0,0 +1,250 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include + +#include + +#include "cryptonote_core/account.h" +#include "WalletObserver.h" +#include "../integration_test_lib/BaseFunctionalTest.h" + +#undef ERROR + +using namespace CryptoNote; +using namespace Logging; + +inline std::string shortAddress(const std::string& addr) { + return addr.substr(0, 6); +} + +extern Tests::Common::BaseFunctionalTestConfig baseCfg; + + +class MultiVersionTest : Tests::Common::BaseFunctionalTest { +public: + + MultiVersionTest(const CryptoNote::Currency& currency, System::Dispatcher& d, const Tests::Common::BaseFunctionalTestConfig& config, Logging::ILogger& log) : + BaseFunctionalTest(currency, d, config), m_config(config), m_nodeCount(config.daemons.size()), logger(log, "MultiVersion") {} + + + void run() { + if (m_config.daemons.empty()) { + logger(ERROR, BRIGHT_RED) << "No daemons configured, exiting"; + return; + } + + launchTestnet(m_nodeCount, Tests::Common::BaseFunctionalTest::Line); + + createWallets(); + + miningTest(); + + // create some address for mining + CryptoNote::account_base stashAddress; + stashAddress.generate(); + auto stashAddressStr = m_currency.accountAddressAsString(stashAddress); + + unlockMoney(stashAddressStr); + + std::vector balances; + for (auto& o : m_observers) { + balances.push_back(o->totalBalance()); + } + + printBalances(); + + // transfer money from each wallet to each other + for (size_t i = 0; i < m_nodeCount; ++i) { + auto& srcWallet = *m_wallets[i]; + for (size_t wi = 0; wi < m_nodeCount; ++wi) { + if (i != wi) { + CryptoNote::Transfer transfer; + transfer.address = m_wallets[wi]->getAddress(); + transfer.amount = (i * 1000 + wi * 100) * m_currency.coin(); + logger(INFO, BRIGHT_YELLOW) << "Sending from " << shortAddress(srcWallet.getAddress()) << " to " << shortAddress(transfer.address) << " amount = " << m_currency.formatAmount(transfer.amount); + auto txid = srcWallet.sendTransaction(transfer, m_currency.minimumFee()); + + balances[i] -= transfer.amount + m_currency.minimumFee(); + balances[wi] += transfer.amount; + + auto res = m_observers[i]->waitSendResult(txid); + + if (res) { + logger(ERROR, BRIGHT_RED) << "Failed to send transaction: " << res.message(); + throw std::runtime_error("Failed to send transaction: " + res.message()); + } + + logger(INFO) << "Sent successfully"; + } + } + } + + nodeDaemons[0]->startMining(1, stashAddressStr); + + for (size_t i = 0; i < m_nodeCount; ++i) { + uint64_t total; + logger(INFO) << i << " Expected target balance: " << m_currency.formatAmount(balances[i]); + + while ((total = m_wallets[i]->pendingBalance() + m_wallets[i]->actualBalance()) != balances[i]) { + logger(INFO) << i << " - total: " << m_currency.formatAmount(total) << ", waiting"; + m_observers[i]->waitTotalBalanceChange(); + } + } + + nodeDaemons[0]->stopMining(); + + printBalances(); + } + + void miningTest() { + auto prevHeight = nodeDaemons[0]->getLocalHeight(); + + // mine block from each node to each wallet + for (size_t i = 0; i < m_nodeCount; ++i) { + for (size_t shift = 0; shift < m_nodeCount; ++shift) { + logger(INFO, BRIGHT_YELLOW) << "Starting mining from node " << i << " -> wallet at node " << shift; + + while (nodeDaemons[i]->getLocalHeight() != prevHeight) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + logger(INFO) << "Starting mining at height " << prevHeight; + nodeDaemons[i]->startMining(1, m_wallets[shift]->getAddress()); + + uint64_t newHeight = 0; + + while ((newHeight = nodeDaemons[i]->getLocalHeight()) == prevHeight) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + nodeDaemons[i]->stopMining(); + logger(INFO) << "Block mined, new height = " << newHeight; + + prevHeight = nodeDaemons[i]->getLocalHeight(); + + logger(INFO, BRIGHT_YELLOW) << "Waiting for balance to change"; + auto res = m_observers[shift]->waitPendingBalanceChangeFor(std::chrono::seconds(m_currency.difficultyTarget() * 5)); + + if (!res.first) { + logger(ERROR, BRIGHT_RED) << "Timeout waiting for balance to change!"; + throw std::runtime_error("Timeout"); + } + } + } + } + + void unlockMoney(const std::string& miningAddress) { + logger(INFO, BRIGHT_YELLOW) << "Starting to mine blocks to unlock money"; + + // unlock money + nodeDaemons[0]->startMining(1, miningAddress); + for (auto& o : m_observers) { + o->waitActualBalanceChange(); + } + nodeDaemons[0]->stopMining(); + logger(INFO, BRIGHT_YELLOW) << "Unlocked all, waiting for all daemons to sync blockchain"; + + auto minerHeight = nodeDaemons[0]->getLocalHeight(); + logger(INFO) << "Miner height: " << minerHeight; + + bool unsynced = true; + + while (unsynced) { + unsynced = false; + for (auto& o : m_observers) { + if (o->getCurrentHeight() < minerHeight) { + unsynced = true; + break; + } + } + + if (unsynced) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + logger(INFO) << "OK"; + } + + void printBalances() { + for (auto& w : m_wallets) { + auto pending = w->pendingBalance(); + auto actual = w->actualBalance(); + + logger(INFO, BRIGHT_GREEN) << + "Wallet " << shortAddress(w->getAddress()) << + ": " << m_currency.formatAmount(actual) << + " / " << m_currency.formatAmount(pending) << + " total = " << m_currency.formatAmount(pending + actual); + } + } + + void createWallets() { + for (auto& daemon : nodeDaemons) { + std::unique_ptr node; + std::unique_ptr wallet; + + daemon->makeINode(node); + makeWallet(wallet, node); + + std::unique_ptr observer(new WalletObserver); + + wallet->addObserver(observer.get()); + + m_nodes.push_back(std::move(node)); + m_wallets.push_back(std::move(wallet)); + m_observers.push_back(std::move(observer)); + } + } + + void startShiftedMining(size_t shift) { + for (size_t i = 0; i < m_nodeCount; ++i) { + nodeDaemons[i]->startMining(1, m_wallets[(i + shift) % m_nodeCount]->getAddress()); + } + } + + void waitAllPendingBalancesChange() { + for (auto& o : m_observers) { + o->waitPendingBalanceChange(); + } + } + + void stopAllMining() { + for (auto& n : nodeDaemons) { + n->stopMining(); + } + } + +private: + + const size_t m_nodeCount; + const Tests::Common::BaseFunctionalTestConfig& m_config; + + std::vector> m_nodes; + std::vector> m_wallets; + std::vector> m_observers; + + Logging::LoggerRef logger; +}; + + +void testMultiVersion(const CryptoNote::Currency& currency, System::Dispatcher& d, const Tests::Common::BaseFunctionalTestConfig& config) { + Logging::ConsoleLogger log; + MultiVersionTest test(currency, d, config, log); + test.run(); +} diff --git a/tests/integration_tests/Node.cpp b/tests/integration_tests/Node.cpp new file mode 100644 index 0000000000..619db4c59a --- /dev/null +++ b/tests/integration_tests/Node.cpp @@ -0,0 +1,469 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include "cryptonote_core/Currency.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "wallet/MultiWallet.h" + +#include "../integration_test_lib/TestNetwork.h" +#include "../integration_test_lib/NodeObserver.h" + +#include "BlockchainInfo.h" + +using namespace Tests; +using namespace CryptoNote; + +extern System::Dispatcher globalDispatcher; + +class NodeCallback { +public: + + INode::Callback callback() { + prom = std::promise(); // reset std::promise + return [this](std::error_code ec) { + prom.set_value(ec); + }; + } + + std::error_code get() { + return prom.get_future().get(); + } + +private: + std::promise prom; +}; + + +class NodeTest: public testing::Test { +public: + + NodeTest() : + currency(CryptoNote::CurrencyBuilder(logger).testnet(true).currency()), + network(globalDispatcher, currency) { + } + +protected: + + virtual void TearDown() override { + network.shutdown(); + } + + void startNetworkWithBlockchain(const std::string& sourcePath, size_t nodes = 2); + void readBlockchainInfo(INode& node, BlockchainInfo& bc); + void dumpBlockchainInfo(INode& node); + + Logging::ConsoleLogger logger; + CryptoNote::Currency currency; + TestNetwork network; +}; + +void NodeTest::startNetworkWithBlockchain(const std::string& sourcePath, size_t nodes) { + auto networkCfg = TestNetworkBuilder(nodes, Topology::Ring).build(); + + for (auto& node : networkCfg) { + node.blockchainLocation = sourcePath; + } + + network.addNodes(networkCfg); + network.waitNodesReady(); +} + +void NodeTest::readBlockchainInfo(INode& node, BlockchainInfo& bc) { + + std::vector history = { currency.genesisBlockHash() }; + uint64_t timestamp = 0; + uint64_t startHeight = 0; + size_t itemsAdded = 0; + NodeCallback cb; + + bc.blocks = { + BlockCompleteEntry{ currency.genesisBlockHash(), block_to_blob(currency.genesisBlock()) } + }; + + do { + itemsAdded = 0; + std::list blocks; + node.queryBlocks(std::list(history.rbegin(), history.rend()), timestamp, blocks, startHeight, cb.callback()); + + ASSERT_TRUE(cb.get() == std::error_code()); + + uint64_t currentHeight = startHeight; + + for (auto& entry : blocks) { + + if (currentHeight < history.size()) { + // detach no expected + ASSERT_EQ(entry.blockHash, history[currentHeight]); + } else { + + CryptoNote::Block block; + CryptoNote::parse_and_validate_block_from_blob(entry.block, block); + + auto txHash = get_transaction_hash(block.minerTx); + + std::vector globalIndices; + node.getTransactionOutsGlobalIndices(txHash, globalIndices, cb.callback()); + + ASSERT_TRUE(!cb.get()); + + bc.globalOutputs.insert(std::make_pair(txHash, std::move(globalIndices))); + + bc.blocks.push_back(entry); + history.push_back(entry.blockHash); + ++itemsAdded; + } + + ++currentHeight; + } + } while (itemsAdded > 0); +} + +void NodeTest::dumpBlockchainInfo(INode& node) { + BlockchainInfo bc; + ASSERT_NO_FATAL_FAILURE(readBlockchainInfo(node, bc)); + storeBlockchainInfo("blocks.js", bc); +} + + +//TEST_F(NodeTest, generateBlockchain) { +// +// auto networkCfg = TestNetworkBuilder(2, Topology::Ring).build(); +// networkCfg[0].cleanupDataDir = false; +// network.addNodes(networkCfg); +// network.waitNodesReady(); +// +// auto& daemon = network.getNode(0); +// +// { +// std::unique_ptr mainNode; +// ASSERT_TRUE(daemon.makeINode(mainNode)); +// +// std::string password = "pass"; +// CryptoNote::MultiWallet wallet(globalDispatcher, currency, *mainNode); +// +// wallet.initialize(password); +// +// std::string minerAddress = wallet.createAddress(); +// daemon.startMining(1, minerAddress); +// +// System::Timer timer(globalDispatcher); +// +// while (daemon.getLocalHeight() < 300) { +// std::cout << "Waiting for block..." << std::endl; +// timer.sleep(std::chrono::seconds(10)); +// } +// +// daemon.stopMining(); +// +// std::ofstream walletFile("wallet.bin", std::ios::binary | std::ios::trunc); +// wallet.save(walletFile); +// wallet.shutdown(); +// +// dumpBlockchainInfo(*mainNode); +// } +//} +// +// +//TEST_F(NodeTest, addMoreBlocks) { +// auto networkCfg = TestNetworkBuilder(2, Topology::Ring).build(); +// networkCfg[0].cleanupDataDir = false; +// networkCfg[0].blockchainLocation = "testnet_300"; +// networkCfg[1].blockchainLocation = "testnet_300"; +// network.addNodes(networkCfg); +// network.waitNodesReady(); +// +// auto& daemon = network.getNode(0); +// +// { +// std::unique_ptr mainNode; +// ASSERT_TRUE(daemon.makeINode(mainNode)); +// +// auto startHeight = daemon.getLocalHeight(); +// +// std::string password = "pass"; +// CryptoNote::MultiWallet wallet(globalDispatcher, currency, *mainNode); +// +// { +// std::ifstream walletFile("wallet.bin", std::ios::binary); +// wallet.load(walletFile, password); +// } +// +// std::string minerAddress = wallet.getAddress(0); +// daemon.startMining(1, minerAddress); +// +// System::Timer timer(globalDispatcher); +// +// while (daemon.getLocalHeight() <= startHeight + 3) { +// std::cout << "Waiting for block..." << std::endl; +// timer.sleep(std::chrono::seconds(1)); +// } +// +// daemon.stopMining(); +// +// std::ofstream walletFile("wallet.bin", std::ios::binary | std::ios::trunc); +// wallet.save(walletFile); +// wallet.shutdown(); +// +// dumpBlockchainInfo(*mainNode); +// } +//} + +//TEST_F(NodeTest, dumpBlockchain) { +// startNetworkWithBlockchain("testnet_300", 2); +// auto& daemon = network.getNode(0); +// std::unique_ptr mainNode; +// ASSERT_TRUE(daemon.makeINode(mainNode)); +// dumpBlockchainInfo(*mainNode); +//} + + +class QueryBlocksTest : public NodeTest { +public: + + virtual void SetUp() override { + NodeTest::SetUp(); + + loadBlockchainInfo("blocks.js", knownBc); + + startNetworkWithBlockchain("testnet_300", 2); + auto& daemon = network.getNode(0); + // check full sync + ASSERT_TRUE(daemon.makeINode(mainNode)); + } + + virtual void TearDown() override { + mainNode.reset(); + NodeTest::TearDown(); + } + + BlockchainInfo knownBc; + std::unique_ptr mainNode; +}; + +TEST_F(QueryBlocksTest, fullSync) { + BlockchainInfo nodeBc; + ASSERT_NO_FATAL_FAILURE(readBlockchainInfo(*mainNode, nodeBc)); + ASSERT_EQ(knownBc, nodeBc); +} + +TEST_F(QueryBlocksTest, queryByTimestamp) { + size_t pivotBlockIndex = knownBc.blocks.size() / 3 * 2; + Block block; + + auto iter = knownBc.blocks.begin(); + std::advance(iter, pivotBlockIndex); + + parse_and_validate_block_from_blob(iter->block, block); + auto timestamp = block.timestamp - 1; + uint64_t startHeight = 0; + std::list blocks; + + std::cout << "Requesting timestamp: " << timestamp << std::endl; + + NodeCallback cb; + + std::list history = { currency.genesisBlockHash() }; + + mainNode->queryBlocks(std::list(history), timestamp, blocks, startHeight, cb.callback()); + ASSERT_TRUE(!cb.get()); + + EXPECT_EQ(0, startHeight); + EXPECT_EQ(knownBc.blocks.begin()->blockHash, blocks.begin()->blockHash); + EXPECT_EQ(knownBc.blocks.size(), blocks.size()); + + auto startBlockIter = std::find_if(blocks.begin(), blocks.end(), [](const BlockCompleteEntry& e) { return !e.block.empty(); }); + ASSERT_TRUE(startBlockIter != blocks.end()); + + Block startBlock; + ASSERT_TRUE(parse_and_validate_block_from_blob(startBlockIter->block, startBlock)); + + std::cout << "Starting block timestamp: " << startBlock.timestamp << std::endl; + auto startFullIndex = std::distance(blocks.begin(), startBlockIter); + + auto it = blocks.begin(); + for (const auto& item : knownBc.blocks) { + ASSERT_EQ(item.blockHash, it->blockHash); + ++it; + } + + ASSERT_EQ(pivotBlockIndex, startFullIndex); +} + +TEST_F(QueryBlocksTest, queryHistory) { + NodeCallback cb; + uint64_t startHeight = 0; + std::list blocks; + + // random genesis block hash -> error + auto randomHash = crypto::rand(); + mainNode->queryBlocks({ randomHash }, 0, blocks, startHeight, cb.callback()); + ASSERT_FALSE(!cb.get()); + + // unknown block - start from first known + mainNode->queryBlocks({ randomHash, currency.genesisBlockHash() }, 0, blocks, startHeight, cb.callback()); + ASSERT_TRUE(!cb.get()); + ASSERT_EQ(0, startHeight); + ASSERT_GT(blocks.size(), 1); + ASSERT_EQ(currency.genesisBlockHash(), blocks.begin()->blockHash); + + for (size_t idx = 10; idx <= 100; idx += 10) { + blocks.clear(); + startHeight = 0; + + const auto& knownBlockHash = knownBc.blocks[idx].blockHash; + + std::list history = { knownBlockHash, currency.genesisBlockHash() }; + mainNode->queryBlocks(std::list(history), 0, blocks, startHeight, cb.callback()); + + ASSERT_TRUE(!cb.get()); + EXPECT_EQ(idx, startHeight); + EXPECT_EQ(knownBlockHash, blocks.begin()->blockHash); + } +} + + +TEST_F(NodeTest, queryBlocks) { + BlockchainInfo knownBc, nodeBc; + + loadBlockchainInfo("blocks.js", knownBc); + + startNetworkWithBlockchain("testnet_300", 2); + auto& daemon = network.getNode(0); + std::unique_ptr mainNode; + + // check full sync + + ASSERT_TRUE(daemon.makeINode(mainNode)); + ASSERT_NO_FATAL_FAILURE(readBlockchainInfo(*mainNode, nodeBc)); + ASSERT_EQ(knownBc, nodeBc); + + // check query with timestamp + + size_t pivotBlockIndex = knownBc.blocks.size() / 3 * 2; + Block block; + + auto iter = knownBc.blocks.begin(); + std::advance(iter, pivotBlockIndex); + + parse_and_validate_block_from_blob(iter->block, block); + auto timestamp = block.timestamp - 1; + uint64_t startHeight = 0; + std::list blocks; + + std::cout << "Requesting timestamp: " << timestamp << std::endl; + + NodeCallback cb; + + std::list history = { currency.genesisBlockHash() }; + + mainNode->queryBlocks(std::list(history), timestamp, blocks, startHeight, cb.callback()); + ASSERT_TRUE(!cb.get()); + + EXPECT_EQ(0, startHeight); + EXPECT_EQ(knownBc.blocks.begin()->blockHash, blocks.begin()->blockHash); + EXPECT_EQ(knownBc.blocks.size(), blocks.size()); + + auto startBlockIter = std::find_if(blocks.begin(), blocks.end(), [](const BlockCompleteEntry& e) { return !e.block.empty(); }); + ASSERT_TRUE(startBlockIter != blocks.end()); + + Block startBlock; + ASSERT_TRUE(parse_and_validate_block_from_blob(startBlockIter->block, startBlock)); + + std::cout << "Starting block timestamp: " << startBlock.timestamp << std::endl; + auto startFullIndex = std::distance(blocks.begin(), startBlockIter); + + auto it = blocks.begin(); + for (const auto& item : knownBc.blocks) { + ASSERT_EQ(item.blockHash, it->blockHash); + ++it; + } + + ASSERT_EQ(pivotBlockIndex, startFullIndex); +} + + +TEST_F(NodeTest, observerHeightNotifications) { + BlockchainInfo extraBlocks; + loadBlockchainInfo("blocks_extra.js", extraBlocks); + + startNetworkWithBlockchain("testnet_300"); + + auto& daemon = network.getNode(0); + + { + std::unique_ptr mainNode; + daemon.makeINode(mainNode); + + NodeObserver observer(*mainNode); + + std::chrono::seconds timeout(10); + uint64_t knownHeight = 0; + uint64_t localHeight = 0; + size_t peerCount = 0; + + EXPECT_TRUE(observer.m_localHeight.waitFor(timeout, localHeight)); + EXPECT_TRUE(observer.m_knownHeight.waitFor(timeout, knownHeight)); + EXPECT_TRUE(observer.m_peerCount.waitFor(timeout, peerCount)); + + EXPECT_GT(localHeight, 0); + EXPECT_GT(knownHeight, 0); + EXPECT_GT(peerCount, 0); + + std::cout << "Local height = " << localHeight << std::endl; + std::cout << "Known height = " << knownHeight << std::endl; + std::cout << "Peer count = " << peerCount << std::endl; + + EXPECT_EQ(localHeight, mainNode->getLastLocalBlockHeight()); + EXPECT_EQ(knownHeight, mainNode->getLastKnownBlockHeight()); + + // submit 1 block and check observer + + uint64_t newKnownHeight = 0; + uint64_t newLocalHeight = 0; + + auto blockData = extraBlocks.blocks.begin()->block; + ASSERT_TRUE(daemon.submitBlock(Common::toHex(blockData.data(), blockData.size()))); + + ASSERT_TRUE(observer.m_localHeight.waitFor(timeout, newLocalHeight)); + ASSERT_TRUE(observer.m_knownHeight.waitFor(timeout, newKnownHeight)); + + size_t blocksSubmitted = 1; + + EXPECT_EQ(localHeight + blocksSubmitted, newLocalHeight); + EXPECT_EQ(knownHeight + blocksSubmitted, newKnownHeight); + + EXPECT_EQ(newLocalHeight, mainNode->getLastLocalBlockHeight()); + EXPECT_EQ(newKnownHeight, mainNode->getLastKnownBlockHeight()); + + std::cout << "Local height = " << newLocalHeight << std::endl; + std::cout << "Known height = " << newKnownHeight << std::endl; + } +} diff --git a/tests/integration_tests/WalletObserver.h b/tests/integration_tests/WalletObserver.h new file mode 100644 index 0000000000..96600e34cd --- /dev/null +++ b/tests/integration_tests/WalletObserver.h @@ -0,0 +1,166 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" +#include +#include +#include +#include +#include + +namespace CryptoNote { + +class WalletObserver: public IWalletObserver { +public: + + WalletObserver() : + m_actualBalance(0), + m_actualBalancePrev(0), + m_pendingBalance(0), + m_pendingBalancePrev(0), + m_syncCount(0) {} + + virtual void actualBalanceUpdated(uint64_t actualBalance) { + std::unique_lock lk(m_mutex); + m_actualBalance = actualBalance; + lk.unlock(); + m_cv.notify_all(); + } + + virtual void pendingBalanceUpdated(uint64_t pendingBalance) { + std::unique_lock lk(m_mutex); + m_pendingBalance = pendingBalance; + lk.unlock(); + m_cv.notify_all(); + } + + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) { + std::unique_lock lk(m_mutex); + m_sendResults[transactionId] = result; + m_cv.notify_all(); + } + + virtual void synchronizationCompleted(std::error_code result) { + std::unique_lock lk(m_mutex); + ++m_syncCount; + m_cv.notify_all(); + } + + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) { + std::unique_lock lk(m_mutex); + m_currentHeight = current; + m_cv.notify_all(); + } + + virtual void externalTransactionCreated(TransactionId transactionId) override { + std::unique_lock lk(m_mutex); + m_externalTransactions.push_back(transactionId); + m_cv.notify_all(); + } + + uint64_t getCurrentHeight() { + std::unique_lock lk(m_mutex); + return m_currentHeight; + } + + uint64_t waitPendingBalanceChange() { + std::unique_lock lk(m_mutex); + while (m_pendingBalance == m_pendingBalancePrev) { + m_cv.wait(lk); + } + m_pendingBalancePrev = m_pendingBalance; + return m_pendingBalance; + } + + uint64_t waitTotalBalanceChange() { + std::unique_lock lk(m_mutex); + while (m_pendingBalance == m_pendingBalancePrev && m_actualBalance == m_actualBalancePrev) { + m_cv.wait(lk); + } + + m_actualBalancePrev = m_actualBalance; + m_pendingBalancePrev = m_pendingBalance; + + return m_actualBalance + m_pendingBalance; + } + + CryptoNote::TransactionId waitExternalTransaction() { + std::unique_lock lk(m_mutex); + + while (m_externalTransactions.empty()) { + m_cv.wait(lk); + } + + CryptoNote::TransactionId txId = m_externalTransactions.front(); + m_externalTransactions.pop_front(); + return txId; + } + + template + std::pair waitPendingBalanceChangeFor(const std::chrono::duration& timePeriod) { + std::unique_lock lk(m_mutex); + bool result = m_cv.wait_for(lk, timePeriod, [&] { return m_pendingBalance != m_pendingBalancePrev; }); + m_pendingBalancePrev = m_pendingBalance; + return std::make_pair(result, m_pendingBalance); + } + + uint64_t waitActualBalanceChange() { + std::unique_lock lk(m_mutex); + while (m_actualBalance == m_actualBalancePrev) { + m_cv.wait(lk); + } + m_actualBalancePrev = m_actualBalance; + return m_actualBalance; + } + + std::error_code waitSendResult(CryptoNote::TransactionId txid) { + std::unique_lock lk(m_mutex); + + std::unordered_map::iterator it; + + while ((it = m_sendResults.find(txid)) == m_sendResults.end()) { + m_cv.wait(lk); + } + + return it->second; + } + + uint64_t totalBalance() { + std::unique_lock lk(m_mutex); + m_pendingBalancePrev = m_pendingBalance; + m_actualBalancePrev = m_actualBalance; + return m_pendingBalance + m_actualBalance; + } + +private: + + std::mutex m_mutex; + std::condition_variable m_cv; + uint64_t m_actualBalance; + uint64_t m_actualBalancePrev; + uint64_t m_pendingBalance; + uint64_t m_pendingBalancePrev; + size_t m_syncCount; + uint64_t m_currentHeight; + + std::unordered_map m_sendResults; + std::deque m_externalTransactions; +}; + +} diff --git a/tests/integration_tests/main.cpp b/tests/integration_tests/main.cpp index 0084748ee0..8a0806f658 100755 --- a/tests/integration_tests/main.cpp +++ b/tests/integration_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,20 +15,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . +#include "gtest/gtest.h" + #include #include #include #include #include -#include "boost/lexical_cast.hpp" +#include #include + #include "cryptonote_core/cryptonote_format_utils.h" -#include "string_tools.h" #include "../integration_test_lib/BaseFunctionalTest.h" #include "../integration_test_lib/Logger.h" +#include "Logging/ConsoleLogger.h" + #ifndef CHECK_AND_ASSERT_MES #define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_ERROR(message); return fail_ret_val;};}while(0) #endif @@ -37,9 +41,10 @@ #define CHECK_AND_ASSERT_MES_NON_FATAL(expr, fail_ret_val, message) do{if(!(expr)) {LOG_WARNING(message); };}while(0) #endif +Tests::Common::BaseFunctionalTestConfig baseCfg; +System::Dispatcher globalDispatcher; - - +void testMultiVersion(const CryptoNote::Currency& currency, System::Dispatcher& d, const Tests::Common::BaseFunctionalTestConfig& config); namespace po = boost::program_options; namespace { @@ -53,9 +58,10 @@ struct Configuration : public Tests::Common::BaseFunctionalTestConfig { init(); } - bool handleCommandLine(int argc, char** argv) { + bool handleCommandLine(int argc, char **argv) { po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); + po::store(po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(), vm); + // po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); BaseFunctionalTestConfig::handleCommandLine(vm); if (vm.count("help")) { @@ -65,9 +71,11 @@ struct Configuration : public Tests::Common::BaseFunctionalTestConfig { if (vm.count("test-type")) { auto testType = vm["test-type"].as(); - if (testType<1 || testType>6) throw ConfigurationError("Incorrect test type."); + if (testType < 1 || testType >= TESTLAST) + throw ConfigurationError("Incorrect test type."); _testType = (TestType)testType; - } else throw ConfigurationError("Missing test type."); + } else + throw ConfigurationError("Missing test type."); return true; } @@ -76,7 +84,9 @@ struct Configuration : public Tests::Common::BaseFunctionalTestConfig { BLOCKTHRUDAEMONS = 3, RELAYBLOCKTHRUDAEMONS = 4, TESTPOOLANDINPROCNODE = 5, - TESTPOOLDELETION = 6 + TESTPOOLDELETION = 6, + TESTMULTIVERSION = 7, + TESTLAST } _testType; po::options_description desc; @@ -85,7 +95,14 @@ struct Configuration : public Tests::Common::BaseFunctionalTestConfig { void init() { desc.add_options() ("help,h", "produce this help message and exit") - ("test-type,t", po::value()->default_value(1), "test type:\r\n1 - wallet to wallet test,\r\n3 - block thru daemons test\r\n4 - relay block thru daemons\r\n5 - test tx pool and inproc node\r\n6 - deleting tx from pool due to timeout"); + ("test-type,t", po::value()->default_value(1), + "test type:\r\n" + "1 - wallet to wallet test,\r\n" + "3 - block thru daemons test\r\n" + "4 - relay block thru daemons\r\n" + "5 - test tx pool and inproc node\r\n" + "6 - deleting tx from pool due to timeout\r\n" + "7 - multiple daemons interoperability test (use -a option to specify daemons)\r\n"); BaseFunctionalTestConfig::init(desc); } }; @@ -95,7 +112,8 @@ struct Configuration : public Tests::Common::BaseFunctionalTestConfig { class SimpleTest : public Tests::Common::BaseFunctionalTest { public: - SimpleTest(const cryptonote::Currency& currency, System::Dispatcher& system, const Configuration& config) : BaseFunctionalTest(currency, system, config) {} + SimpleTest(const CryptoNote::Currency& currency, System::Dispatcher& system, const Tests::Common::BaseFunctionalTestConfig& config) + : BaseFunctionalTest(currency, system, config) {} class WaitForActualGrowObserver : public CryptoNote::IWalletObserver { Tests::Common::Semaphore& m_GotActual; @@ -201,7 +219,6 @@ class SimpleTest : public Tests::Common::BaseFunctionalTest { bool perform1() { using namespace Tests::Common; using namespace CryptoNote; - using namespace cryptonote; const uint64_t FEE = 1000000; launchTestnet(2); LOG_TRACE("STEP 1 PASSED"); @@ -506,12 +523,14 @@ class SimpleTest : public Tests::Common::BaseFunctionalTest { bool perform5() { using namespace Tests::Common; using namespace CryptoNote; - using namespace cryptonote; const uint64_t FEE = 1000000; launchTestnetWithInprocNode(2); std::unique_ptr node1; + std::unique_ptr inprocNode; + nodeDaemons.front()->makeINode(node1); + nodeDaemons.back()->makeINode(inprocNode); while (node1->getLastLocalBlockHeight() != inprocNode->getLastLocalBlockHeight()) { LOG_TRACE("Syncing..."); @@ -563,7 +582,6 @@ class SimpleTest : public Tests::Common::BaseFunctionalTest { CryptoNote::Transfer tr; tr.address = wallet2->getAddress(); tr.amount = wallet1ActualBeforeTransaction / 2; - TransactionId sendTransaction; std::error_code result; Semaphore w2GotPending; WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction); @@ -654,13 +672,15 @@ class SimpleTest : public Tests::Common::BaseFunctionalTest { bool perform6() { using namespace Tests::Common; using namespace CryptoNote; - using namespace cryptonote; const uint64_t FEE = 1000000; launchTestnetWithInprocNode(2); std::unique_ptr node1; nodeDaemons.front()->makeINode(node1); + std::unique_ptr inprocNode; + nodeDaemons.back()->makeINode(inprocNode); + while (node1->getLastLocalBlockHeight() != inprocNode->getLastLocalBlockHeight()) { LOG_TRACE("Syncing..."); std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -711,7 +731,6 @@ class SimpleTest : public Tests::Common::BaseFunctionalTest { CryptoNote::Transfer tr; tr.address = wallet2->getAddress(); tr.amount = wallet1ActualBeforeTransaction / 2; - TransactionId sendTransaction; std::error_code result; Semaphore w2GotPending; WaitForPendingGrowObserver pgo1(w2GotPending, wallet2PendingBeforeTransaction); @@ -781,38 +800,58 @@ class SimpleTest : public Tests::Common::BaseFunctionalTest { }; +class SimpleTestCase : public ::testing::Test { + +public: + + SimpleTestCase() : + currency(CryptoNote::CurrencyBuilder(logger).testnet(true).currency()), + test(currency, globalDispatcher, baseCfg) { + } + + Logging::ConsoleLogger logger; + CryptoNote::Currency currency; + SimpleTest test; +}; + +TEST_F(SimpleTestCase, WALLET2WALLET) { + ASSERT_TRUE(test.perform1()); +} + +TEST_F(SimpleTestCase, BLOCKTHRUDAEMONS) { + ASSERT_TRUE(test.perform2()); +} + +TEST_F(SimpleTestCase, RELAYBLOCKTHRUDAEMONS) { + ASSERT_TRUE(test.perform4()); +} + +TEST_F(SimpleTestCase, TESTPOOLANDINPROCNODE) { + ASSERT_TRUE(test.perform5()); +} + +TEST_F(SimpleTestCase, TESTPOOLDELETION) { + currency = CryptoNote::CurrencyBuilder(logger).testnet(true).mempoolTxLiveTime(60).currency(); + ASSERT_TRUE(test.perform6()); +} + +TEST_F(SimpleTestCase, MULTIVERSION) { + ASSERT_NO_THROW(testMultiVersion(currency, globalDispatcher, baseCfg)); +} int main(int argc, char** argv) { CLogger::Instance().init(CLogger::DEBUG); + try { ::Configuration config; if (!config.handleCommandLine(argc, argv)) { return 0; //help message requested or so } - cryptonote::Currency currency = cryptonote::CurrencyBuilder().testnet(true).currency(); - if (config._testType == Configuration::TESTPOOLDELETION) { - currency = cryptonote::CurrencyBuilder().testnet(true).mempoolTxLiveTime(60).currency(); - } - - System::Dispatcher system; - SimpleTest t(currency, system, config); - bool success = false; - switch (config._testType) - { - case Configuration::WALLET2WALLET: success = t.perform1(); break; - case Configuration::BLOCKTHRUDAEMONS: success = t.perform2(); break; - case Configuration::RELAYBLOCKTHRUDAEMONS: success = t.perform4(); break; - case Configuration::TESTPOOLANDINPROCNODE: success = t.perform5(); break; - case Configuration::TESTPOOLDELETION: success = t.perform6(); break; - default: throw std::runtime_error("Oh snap! Serious crap happened..."); - }; - std::this_thread::sleep_for(std::chrono::milliseconds(5000)); - if (!success) { - LOG_ERROR("TEST FAILED"); - return 1; - } - LOG_TRACE("TEST PASSED"); + baseCfg = config; + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } catch (::ConfigurationError& ex) { std::cerr << "Configuration error: " << ex.what() << std::endl; @@ -822,5 +861,6 @@ int main(int argc, char** argv) { LOG_ERROR("Fatal error: " + std::string(ex.what())); return 1; } + return 0; -} \ No newline at end of file +} diff --git a/tests/io.h b/tests/io.h index bbec48535e..7af91ae234 100644 --- a/tests/io.h +++ b/tests/io.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/net_load_tests/clt.cpp b/tests/net_load_tests/clt.cpp deleted file mode 100644 index c7a76da098..0000000000 --- a/tests/net_load_tests/clt.cpp +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include -#include -#include -#include -#include -#include - -#include - -#include "gtest/gtest.h" - -#include "include_base_utils.h" -#include "misc_language.h" -#include "misc_log_ex.h" -#include "storages/levin_abstract_invoke2.h" - -#include "net_load_tests.h" - -using namespace net_load_tests; - -namespace -{ - const size_t CONNECTION_COUNT = 100000; - const size_t CONNECTION_TIMEOUT = 10000; - const size_t DEFAULT_OPERATION_TIMEOUT = 30000; - const size_t RESERVED_CONN_CNT = 1; - - template - bool busy_wait_for(size_t timeout_ms, const t_predicate& predicate, size_t sleep_ms = 10) - { - for (size_t i = 0; i < timeout_ms / sleep_ms; ++i) - { - if (predicate()) - return true; - //std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); - epee::misc_utils::sleep_no_w(static_cast(sleep_ms)); - } - return false; - } - - class t_connection_opener_1 - { - public: - t_connection_opener_1(test_tcp_server& tcp_server, size_t open_request_target) - : m_tcp_server(tcp_server) - , m_open_request_target(open_request_target) - , m_next_id(0) - , m_error_count(0) - , m_connections(open_request_target) - { - for (auto& conn_id : m_connections) - conn_id = boost::uuids::nil_uuid(); - } - - bool open() - { - size_t id = m_next_id.fetch_add(1, std::memory_order_relaxed); - if (m_open_request_target <= id) - return false; - - bool r = m_tcp_server.connect_async("127.0.0.1", srv_port, CONNECTION_TIMEOUT, [=](const test_connection_context& context, const boost::system::error_code& ec) { - if (!ec) - { - m_connections[id] = context.m_connection_id; - } - else - { - m_error_count.fetch_add(1, std::memory_order_relaxed); - } - }); - - if (!r) - { - m_error_count.fetch_add(1, std::memory_order_relaxed); - } - - return true; - } - - bool close(size_t id) - { - if (!m_connections[id].is_nil()) - { - m_tcp_server.get_config_object().close(m_connections[id]); - return true; - } - else - { - return false; - } - } - - size_t error_count() const { return m_error_count.load(std::memory_order_relaxed); } - - private: - test_tcp_server& m_tcp_server; - size_t m_open_request_target; - std::atomic m_next_id; - std::atomic m_error_count; - std::vector m_connections; - }; - - class t_connection_opener_2 - { - public: - t_connection_opener_2(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_open_request_count(0) - , m_error_count(0) - , m_open_close_test_helper(tcp_server, open_request_target, max_opened_connection_count) - { - } - - bool open_and_close() - { - size_t req_count = m_open_request_count.fetch_add(1, std::memory_order_relaxed); - if (m_open_request_target <= req_count) - return false; - - bool r = m_tcp_server.connect_async("127.0.0.1", srv_port, CONNECTION_TIMEOUT, [=](const test_connection_context& context, const boost::system::error_code& ec) { - if (!ec) - { - m_open_close_test_helper.handle_new_connection(context.m_connection_id); - } - else - { - m_error_count.fetch_add(1, std::memory_order_relaxed); - } - }); - - if (!r) - { - m_error_count.fetch_add(1, std::memory_order_relaxed); - } - - return true; - } - - void close_remaining_connections() - { - m_open_close_test_helper.close_remaining_connections(); - } - - size_t opened_connection_count() const { return m_open_close_test_helper.opened_connection_count(); } - size_t error_count() const { return m_error_count.load(std::memory_order_relaxed); } - - private: - test_tcp_server& m_tcp_server; - size_t m_open_request_target; - std::atomic m_open_request_count; - std::atomic m_error_count; - open_close_test_helper m_open_close_test_helper; - }; - - class net_load_test_clt : public ::testing::Test - { - protected: - virtual void SetUp() - { - m_thread_count = (std::max)(min_thread_count, std::thread::hardware_concurrency() / 2); - - m_tcp_server.get_config_object().m_pcommands_handler = &m_commands_handler; - m_tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT; - - ASSERT_TRUE(m_tcp_server.init_server(clt_port, "127.0.0.1")); - ASSERT_TRUE(m_tcp_server.run_server(m_thread_count, false)); - - // Connect to server - std::atomic conn_status(0); - m_cmd_conn_id = boost::uuids::nil_uuid(); - ASSERT_TRUE(m_tcp_server.connect_async("127.0.0.1", srv_port, CONNECTION_TIMEOUT, [&](const test_connection_context& context, const boost::system::error_code& ec) { - if (!ec) - { - m_cmd_conn_id = context.m_connection_id; - } - else - { - LOG_ERROR("Connection error: " << ec.message()); - } - conn_status.store(1, std::memory_order_seq_cst); - })); - - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != conn_status.load(std::memory_order_seq_cst); })) << "connect_async timed out"; - ASSERT_EQ(1, conn_status.load(std::memory_order_seq_cst)); - ASSERT_FALSE(m_cmd_conn_id.is_nil()); - - conn_status.store(0, std::memory_order_seq_cst); - CMD_RESET_STATISTICS::request req; - ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2(m_cmd_conn_id, CMD_RESET_STATISTICS::ID, req, - m_tcp_server.get_config_object(), [&](int code, const CMD_RESET_STATISTICS::response& rsp, const test_connection_context&) { - conn_status.store(code, std::memory_order_seq_cst); - })); - - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != conn_status.load(std::memory_order_seq_cst); })) << "reset statistics timed out"; - ASSERT_LT(0, conn_status.load(std::memory_order_seq_cst)); - } - - virtual void TearDown() - { - m_tcp_server.send_stop_signal(); - ASSERT_TRUE(m_tcp_server.timed_wait_server_stop(DEFAULT_OPERATION_TIMEOUT)); - } - - static void TearDownTestCase() - { - // Stop server - test_levin_commands_handler commands_handler; - test_tcp_server tcp_server; - tcp_server.get_config_object().m_pcommands_handler = &commands_handler; - tcp_server.get_config_object().m_invoke_timeout = CONNECTION_TIMEOUT; - - if (!tcp_server.init_server(clt_port, "127.0.0.1")) return; - if (!tcp_server.run_server(2, false)) return; - - // Connect to server and invoke shutdown command - std::atomic conn_status(0); - boost::uuids::uuid cmd_conn_id = boost::uuids::nil_uuid(); - tcp_server.connect_async("127.0.0.1", srv_port, CONNECTION_TIMEOUT, [&](const test_connection_context& context, const boost::system::error_code& ec) { - cmd_conn_id = context.m_connection_id; - conn_status.store(!ec ? 1 : -1, std::memory_order_seq_cst); - }); - - if (!busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != conn_status.load(std::memory_order_seq_cst); })) return; - if (1 != conn_status.load(std::memory_order_seq_cst)) return; - - epee::net_utils::notify_remote_command2(cmd_conn_id, CMD_SHUTDOWN::ID, CMD_SHUTDOWN::request(), tcp_server.get_config_object()); - - busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != commands_handler.close_connection_counter(); }); - } - - template - static auto call_func(size_t /*thread_index*/, const Func& func, int) -> decltype(func()) - { - func(); - } - - template - static auto call_func(size_t thread_index, const Func& func, long) -> decltype(func(thread_index)) - { - func(thread_index); - } - - template - void parallel_exec(const Func& func) - { - unit_test::call_counter properly_finished_threads; - std::vector threads(m_thread_count); - for (size_t i = 0; i < threads.size(); ++i) - { - threads[i] = std::thread([&, i] { - call_func(i, func, 0); - properly_finished_threads.inc(); - }); - } - - for (auto& th : threads) - th.join(); - - ASSERT_EQ(properly_finished_threads.get(), m_thread_count); - } - - void get_server_statistics(CMD_GET_STATISTICS::response& statistics) - { - std::atomic req_status(0); - CMD_GET_STATISTICS::request req; - ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2(m_cmd_conn_id, CMD_GET_STATISTICS::ID, req, - m_tcp_server.get_config_object(), [&](int code, const CMD_GET_STATISTICS::response& rsp, const test_connection_context&) { - if (0 < code) - { - statistics = rsp; - } - else - { - LOG_ERROR("Get server statistics error: " << code); - } - req_status.store(0 < code ? 1 : -1, std::memory_order_seq_cst); - })); - - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 0 != req_status.load(std::memory_order_seq_cst); })) << "get_server_statistics timed out"; - ASSERT_EQ(1, req_status.load(std::memory_order_seq_cst)); - } - - template - bool busy_wait_for_server_statistics(CMD_GET_STATISTICS::response& statistics, const t_predicate& predicate) - { - for (size_t i = 0; i < 30; ++i) - { - get_server_statistics(statistics); - if (predicate(statistics)) - { - return true; - } - - //std::this_thread::sleep_for(std::chrono::seconds(1)); - epee::misc_utils::sleep_no_w(1000); - } - - return false; - } - - void ask_for_data_requests(size_t request_size = 0) - { - CMD_SEND_DATA_REQUESTS::request req; - req.request_size = request_size; - epee::net_utils::notify_remote_command2(m_cmd_conn_id, CMD_SEND_DATA_REQUESTS::ID, req, m_tcp_server.get_config_object()); - } - - protected: - test_tcp_server m_tcp_server; - test_levin_commands_handler m_commands_handler; - size_t m_thread_count; - boost::uuids::uuid m_cmd_conn_id; - }; -} - -TEST_F(net_load_test_clt, a_lot_of_client_connections_and_connections_closed_by_client) -{ - // Open connections - t_connection_opener_1 connection_opener(m_tcp_server, CONNECTION_COUNT); - parallel_exec([&] { - while (connection_opener.open()); - }); - - // Wait for all open requests to complete - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return CONNECTION_COUNT + RESERVED_CONN_CNT <= m_commands_handler.new_connection_counter() + connection_opener.error_count(); })); - LOG_PRINT_L0("number of opened connections / fails (total): " << m_commands_handler.new_connection_counter() << - " / " << connection_opener.error_count() << " (" << (m_commands_handler.new_connection_counter() + connection_opener.error_count()) << ")"); - - // Check - ASSERT_GT(m_commands_handler.new_connection_counter(), RESERVED_CONN_CNT); - ASSERT_EQ(m_commands_handler.new_connection_counter() + connection_opener.error_count(), CONNECTION_COUNT + RESERVED_CONN_CNT); - ASSERT_EQ(m_commands_handler.new_connection_counter() - m_commands_handler.close_connection_counter(), m_tcp_server.get_config_object().get_connections_count()); - - // Close connections - parallel_exec([&](size_t thread_idx) { - for (size_t i = thread_idx; i < CONNECTION_COUNT; i += m_thread_count) - { - connection_opener.close(i); - } - }); - - // Wait for all opened connections to close - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT <= m_commands_handler.close_connection_counter(); })); - LOG_PRINT_L0("number of opened / closed connections: " << m_tcp_server.get_config_object().get_connections_count() << - " / " << m_commands_handler.close_connection_counter()); - - // Check all connections are closed - ASSERT_EQ(m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT, m_commands_handler.close_connection_counter()); - ASSERT_EQ(RESERVED_CONN_CNT, m_tcp_server.get_config_object().get_connections_count()); - - // Wait for server to handle all open and close requests - CMD_GET_STATISTICS::response srv_stat; - busy_wait_for_server_statistics(srv_stat, [](const CMD_GET_STATISTICS::response& stat) { return stat.new_connection_counter - RESERVED_CONN_CNT <= stat.close_connection_counter; }); - LOG_PRINT_L0("server statistics: " << srv_stat.to_string()); - - // Check server status - // It's OK, if server didn't close all opened connections, because of it could receive not all FIN packets - ASSERT_LE(srv_stat.close_connection_counter, srv_stat.new_connection_counter - RESERVED_CONN_CNT); - ASSERT_LE(RESERVED_CONN_CNT, srv_stat.opened_connections_count); - - // Request data from server, it causes to close rest connections - ask_for_data_requests(); - - // Wait for server to close rest connections - busy_wait_for_server_statistics(srv_stat, [](const CMD_GET_STATISTICS::response& stat) { return stat.new_connection_counter - RESERVED_CONN_CNT <= stat.close_connection_counter; }); - LOG_PRINT_L0("server statistics: " << srv_stat.to_string()); - - // Check server status. All connections should be closed - ASSERT_EQ(srv_stat.close_connection_counter, srv_stat.new_connection_counter - RESERVED_CONN_CNT); - ASSERT_EQ(RESERVED_CONN_CNT, srv_stat.opened_connections_count); -} - -TEST_F(net_load_test_clt, a_lot_of_client_connections_and_connections_closed_by_server) -{ - // Open connections - t_connection_opener_1 connection_opener(m_tcp_server, CONNECTION_COUNT); - parallel_exec([&] { - while (connection_opener.open()); - }); - - // Wait for all open requests to complete - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return CONNECTION_COUNT + RESERVED_CONN_CNT <= m_commands_handler.new_connection_counter() + connection_opener.error_count(); })); - LOG_PRINT_L0("number of opened connections / fails (total): " << m_commands_handler.new_connection_counter() << - " / " << connection_opener.error_count() << " (" << (m_commands_handler.new_connection_counter() + connection_opener.error_count()) << ")"); - - // Check - ASSERT_GT(m_commands_handler.new_connection_counter(), RESERVED_CONN_CNT); - ASSERT_EQ(m_commands_handler.new_connection_counter() + connection_opener.error_count(), CONNECTION_COUNT + RESERVED_CONN_CNT); - ASSERT_EQ(m_commands_handler.new_connection_counter() - m_commands_handler.close_connection_counter(), m_tcp_server.get_config_object().get_connections_count()); - - // Wait for server accepts all connections - CMD_GET_STATISTICS::response srv_stat; - int last_new_connection_counter = -1; - busy_wait_for_server_statistics(srv_stat, [&last_new_connection_counter](const CMD_GET_STATISTICS::response& stat) { - if (last_new_connection_counter == static_cast(stat.new_connection_counter)) return true; - else { last_new_connection_counter = static_cast(stat.new_connection_counter); return false; } - }); - - // Close connections - CMD_CLOSE_ALL_CONNECTIONS::request req; - ASSERT_TRUE(epee::net_utils::notify_remote_command2(m_cmd_conn_id, CMD_CLOSE_ALL_CONNECTIONS::ID, req, m_tcp_server.get_config_object())); - - // Wait for all opened connections to close - busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT <= m_commands_handler.close_connection_counter(); }); - LOG_PRINT_L0("number of opened / closed connections: " << m_tcp_server.get_config_object().get_connections_count() << - " / " << m_commands_handler.close_connection_counter()); - - // It's OK, if server didn't close all connections, because it could accept not all our connections - ASSERT_LE(m_commands_handler.close_connection_counter(), m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT); - ASSERT_LE(RESERVED_CONN_CNT, m_tcp_server.get_config_object().get_connections_count()); - - // Wait for server to handle all open and close requests - busy_wait_for_server_statistics(srv_stat, [](const CMD_GET_STATISTICS::response& stat) { return stat.new_connection_counter - RESERVED_CONN_CNT <= stat.close_connection_counter; }); - LOG_PRINT_L0("server statistics: " << srv_stat.to_string()); - - // Check server status - ASSERT_EQ(srv_stat.close_connection_counter, srv_stat.new_connection_counter - RESERVED_CONN_CNT); - ASSERT_EQ(RESERVED_CONN_CNT, srv_stat.opened_connections_count); - - // Close rest connections - m_tcp_server.get_config_object().foreach_connection([&](test_connection_context& ctx) { - if (ctx.m_connection_id != m_cmd_conn_id) - { - CMD_DATA_REQUEST::request req; - bool r = epee::net_utils::async_invoke_remote_command2(ctx.m_connection_id, CMD_DATA_REQUEST::ID, req, - m_tcp_server.get_config_object(), [=](int code, const CMD_DATA_REQUEST::response& rsp, const test_connection_context&) { - if (code <= 0) - { - LOG_PRINT_L0("Failed to invoke CMD_DATA_REQUEST. code = " << code); - } - }); - if (!r) - LOG_PRINT_L0("Failed to invoke CMD_DATA_REQUEST"); - } - return true; - }); - - // Wait for all opened connections to close - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT <= m_commands_handler.close_connection_counter(); })); - LOG_PRINT_L0("number of opened / closed connections: " << m_tcp_server.get_config_object().get_connections_count() << - " / " << m_commands_handler.close_connection_counter()); - - // Check - ASSERT_EQ(m_commands_handler.close_connection_counter(), m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT); - ASSERT_EQ(RESERVED_CONN_CNT, m_tcp_server.get_config_object().get_connections_count()); -} - -TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_client) -{ - static const size_t MAX_OPENED_CONN_COUNT = 100; - - // Open/close connections - t_connection_opener_2 connection_opener(m_tcp_server, CONNECTION_COUNT, MAX_OPENED_CONN_COUNT); - parallel_exec([&] { - while (connection_opener.open_and_close()); - }); - - // Wait for all open requests to complete - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return CONNECTION_COUNT + RESERVED_CONN_CNT <= m_commands_handler.new_connection_counter() + connection_opener.error_count(); })); - LOG_PRINT_L0("number of opened connections / fails (total): " << m_commands_handler.new_connection_counter() << - " / " << connection_opener.error_count() << " (" << (m_commands_handler.new_connection_counter() + connection_opener.error_count()) << ")"); - - // Check - ASSERT_GT(m_commands_handler.new_connection_counter(), RESERVED_CONN_CNT); - ASSERT_EQ(m_commands_handler.new_connection_counter() + connection_opener.error_count(), CONNECTION_COUNT + RESERVED_CONN_CNT); - - // Wait for all close requests to complete - EXPECT_TRUE(busy_wait_for(4 * DEFAULT_OPERATION_TIMEOUT, [&](){ return connection_opener.opened_connection_count() <= MAX_OPENED_CONN_COUNT; })); - LOG_PRINT_L0("actual number of opened connections: " << connection_opener.opened_connection_count()); - - // Check - ASSERT_EQ(MAX_OPENED_CONN_COUNT, connection_opener.opened_connection_count()); - - connection_opener.close_remaining_connections(); - - // Wait for all close requests to complete - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() <= m_commands_handler.close_connection_counter() + RESERVED_CONN_CNT; })); - LOG_PRINT_L0("actual number of opened connections: " << connection_opener.opened_connection_count()); - - ASSERT_EQ(m_commands_handler.new_connection_counter(), m_commands_handler.close_connection_counter() + RESERVED_CONN_CNT); - ASSERT_EQ(0, connection_opener.opened_connection_count()); - ASSERT_EQ(RESERVED_CONN_CNT, m_tcp_server.get_config_object().get_connections_count()); - - // Wait for server to handle all open and close requests - CMD_GET_STATISTICS::response srv_stat; - busy_wait_for_server_statistics(srv_stat, [](const CMD_GET_STATISTICS::response& stat) { return stat.new_connection_counter - RESERVED_CONN_CNT <= stat.close_connection_counter; }); - LOG_PRINT_L0("server statistics: " << srv_stat.to_string()); - - // Check server status - // It's OK, if server didn't close all opened connections, because of it could receive not all FIN packets - ASSERT_LE(srv_stat.close_connection_counter, srv_stat.new_connection_counter - RESERVED_CONN_CNT); - ASSERT_LE(RESERVED_CONN_CNT, srv_stat.opened_connections_count); - - // Request data from server, it causes to close rest connections - ask_for_data_requests(); - - // Wait for server to close rest connections - busy_wait_for_server_statistics(srv_stat, [](const CMD_GET_STATISTICS::response& stat) { return stat.new_connection_counter - RESERVED_CONN_CNT <= stat.close_connection_counter; }); - LOG_PRINT_L0("server statistics: " << srv_stat.to_string()); - - // Check server status. All connections should be closed - ASSERT_EQ(srv_stat.close_connection_counter, srv_stat.new_connection_counter - RESERVED_CONN_CNT); - ASSERT_EQ(RESERVED_CONN_CNT, srv_stat.opened_connections_count); -} - -TEST_F(net_load_test_clt, permament_open_and_close_and_connections_closed_by_server) -{ - static const size_t MAX_OPENED_CONN_COUNT = 100; - - // Init test - std::atomic test_state(0); - CMD_START_OPEN_CLOSE_TEST::request req_start; - req_start.open_request_target = CONNECTION_COUNT; - req_start.max_opened_conn_count = MAX_OPENED_CONN_COUNT; - ASSERT_TRUE(epee::net_utils::async_invoke_remote_command2(m_cmd_conn_id, CMD_START_OPEN_CLOSE_TEST::ID, req_start, - m_tcp_server.get_config_object(), [&](int code, const CMD_START_OPEN_CLOSE_TEST::response&, const test_connection_context&) { - test_state.store(0 < code ? 1 : -1, std::memory_order_seq_cst); - })); - - // Wait for server response - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&]{ return 1 == test_state.load(std::memory_order_seq_cst); })); - ASSERT_EQ(1, test_state.load(std::memory_order_seq_cst)); - - // Open connections - t_connection_opener_1 connection_opener(m_tcp_server, CONNECTION_COUNT); - parallel_exec([&] { - while (connection_opener.open()); - }); - - // Wait for all open requests to complete - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return CONNECTION_COUNT + RESERVED_CONN_CNT <= m_commands_handler.new_connection_counter() + connection_opener.error_count(); })); - LOG_PRINT_L0("number of opened connections / fails (total): " << m_commands_handler.new_connection_counter() << - " / " << connection_opener.error_count() << " (" << (m_commands_handler.new_connection_counter() + connection_opener.error_count()) << ")"); - LOG_PRINT_L0("actual number of opened connections: " << m_tcp_server.get_config_object().get_connections_count()); - - ASSERT_GT(m_commands_handler.new_connection_counter(), RESERVED_CONN_CNT); - ASSERT_EQ(m_commands_handler.new_connection_counter() + connection_opener.error_count(), CONNECTION_COUNT + RESERVED_CONN_CNT); - - // Wait for server accepts all connections - CMD_GET_STATISTICS::response srv_stat; - int last_new_connection_counter = -1; - busy_wait_for_server_statistics(srv_stat, [&last_new_connection_counter](const CMD_GET_STATISTICS::response& stat) { - if (last_new_connection_counter == static_cast(stat.new_connection_counter)) return true; - else { last_new_connection_counter = static_cast(stat.new_connection_counter); return false; } - }); - - // Ask server to close rest connections - CMD_CLOSE_ALL_CONNECTIONS::request req; - ASSERT_TRUE(epee::net_utils::notify_remote_command2(m_cmd_conn_id, CMD_CLOSE_ALL_CONNECTIONS::ID, req, m_tcp_server.get_config_object())); - - // Wait for almost all connections to be closed by server - busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() <= m_commands_handler.close_connection_counter() + RESERVED_CONN_CNT; }); - - // It's OK, if there are opened connections, because server could accept not all our connections - ASSERT_LE(m_commands_handler.close_connection_counter() + RESERVED_CONN_CNT, m_commands_handler.new_connection_counter()); - ASSERT_LE(RESERVED_CONN_CNT, m_tcp_server.get_config_object().get_connections_count()); - - // Wait for server to handle all open and close requests - busy_wait_for_server_statistics(srv_stat, [](const CMD_GET_STATISTICS::response& stat) { return stat.new_connection_counter - RESERVED_CONN_CNT <= stat.close_connection_counter; }); - LOG_PRINT_L0("server statistics: " << srv_stat.to_string()); - - // Check server status - ASSERT_EQ(srv_stat.close_connection_counter, srv_stat.new_connection_counter - RESERVED_CONN_CNT); - ASSERT_EQ(RESERVED_CONN_CNT, srv_stat.opened_connections_count); - - // Close rest connections - m_tcp_server.get_config_object().foreach_connection([&](test_connection_context& ctx) { - if (ctx.m_connection_id != m_cmd_conn_id) - { - CMD_DATA_REQUEST::request req; - bool r = epee::net_utils::async_invoke_remote_command2(ctx.m_connection_id, CMD_DATA_REQUEST::ID, req, - m_tcp_server.get_config_object(), [=](int code, const CMD_DATA_REQUEST::response& rsp, const test_connection_context&) { - if (code <= 0) - { - LOG_PRINT_L0("Failed to invoke CMD_DATA_REQUEST. code = " << code); - } - }); - if (!r) - LOG_PRINT_L0("Failed to invoke CMD_DATA_REQUEST"); - } - return true; - }); - - // Wait for all opened connections to close - EXPECT_TRUE(busy_wait_for(DEFAULT_OPERATION_TIMEOUT, [&](){ return m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT <= m_commands_handler.close_connection_counter(); })); - LOG_PRINT_L0("number of opened / closed connections: " << m_tcp_server.get_config_object().get_connections_count() << - " / " << m_commands_handler.close_connection_counter()); - - // Check - ASSERT_EQ(m_commands_handler.close_connection_counter(), m_commands_handler.new_connection_counter() - RESERVED_CONN_CNT); - ASSERT_EQ(RESERVED_CONN_CNT, m_tcp_server.get_config_object().get_connections_count()); -} - -int main(int argc, char** argv) -{ - epee::debug::get_set_enable_assert(true, false); - //set up logging options - epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/net_load_tests/net_load_tests.h b/tests/net_load_tests/net_load_tests.h deleted file mode 100644 index dcb0091956..0000000000 --- a/tests/net_load_tests/net_load_tests.h +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include -#include - -#include "include_base_utils.h" -#include "string_tools.h" -#include "net/levin_protocol_handler_async.h" -#include "net/abstract_tcp_server2.h" -#include "serialization/keyvalue_serialization.h" - -#include "../unit_tests/unit_tests_utils.h" - -namespace net_load_tests -{ - struct test_connection_context : epee::net_utils::connection_context_base - { - volatile bool m_closed; - }; - - typedef epee::levin::async_protocol_handler test_levin_protocol_handler; - typedef epee::levin::async_protocol_handler_config test_levin_protocol_handler_config; - typedef epee::net_utils::connection test_connection; - typedef epee::net_utils::boosted_tcp_server test_tcp_server; - - struct test_levin_commands_handler : public epee::levin::levin_commands_handler - { - test_levin_commands_handler() - //: m_return_code(LEVIN_OK) - //, m_last_command(-1) - { - } - - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_connection_context& context) - { - //m_invoke_counter.inc(); - //std::unique_lock lock(m_mutex); - //m_last_command = command; - //m_last_in_buf = in_buff; - //buff_out = m_invoke_out_buf; - //return m_return_code; - return LEVIN_OK; - } - - virtual int notify(int command, const std::string& in_buff, test_connection_context& context) - { - //m_notify_counter.inc(); - //std::unique_lock lock(m_mutex); - //m_last_command = command; - //m_last_in_buf = in_buff; - //return m_return_code; - return LEVIN_OK; - } - - virtual void callback(test_connection_context& context) - { - //m_callback_counter.inc(); - //std::cout << "test_levin_commands_handler::callback()" << std::endl; - } - - virtual void on_connection_new(test_connection_context& context) - { - m_new_connection_counter.inc(); - //std::cout << "test_levin_commands_handler::on_connection_new()" << std::endl; - } - - virtual void on_connection_close(test_connection_context& context) - { - m_close_connection_counter.inc(); - //std::cout << "test_levin_commands_handler::on_connection_close()" << std::endl; - } - - //size_t invoke_counter() const { return m_invoke_counter.get(); } - //size_t notify_counter() const { return m_notify_counter.get(); } - //size_t callback_counter() const { return m_callback_counter.get(); } - size_t new_connection_counter() const { return m_new_connection_counter.get(); } - size_t close_connection_counter() const { return m_close_connection_counter.get(); } - - //int return_code() const { return m_return_code; } - //void return_code(int v) { m_return_code = v; } - - //const std::string& invoke_out_buf() const { return m_invoke_out_buf; } - //void invoke_out_buf(const std::string& v) { m_invoke_out_buf = v; } - - //int last_command() const { return m_last_command; } - //const std::string& last_in_buf() const { return m_last_in_buf; } - - protected: - //unit_test::call_counter m_invoke_counter; - //unit_test::call_counter m_notify_counter; - //unit_test::call_counter m_callback_counter; - unit_test::call_counter m_new_connection_counter; - unit_test::call_counter m_close_connection_counter; - - //std::mutex m_mutex; - - //int m_return_code; - //std::string m_invoke_out_buf; - - //int m_last_command; - //std::string m_last_in_buf; - }; - - class open_close_test_helper - { - 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) - , m_next_closed_conn_idx(0) - , m_connections(open_request_target) - { - for (auto& conn_id : m_connections) - conn_id = boost::uuids::nil_uuid(); - } - - bool handle_new_connection(const boost::uuids::uuid& connection_id, bool ignore_close_fails = false) - { - size_t idx = m_next_opened_conn_idx.fetch_add(1, std::memory_order_relaxed); - m_connections[idx] = connection_id; - - size_t prev_connection_count = m_opened_connection_count.fetch_add(1, std::memory_order_relaxed); - if (m_max_opened_connection_count <= prev_connection_count) - { - return close_next_connection(ignore_close_fails); - } - - return true; - } - - void close_remaining_connections() - { - while (close_next_connection(false)); - } - - bool close_next_connection(bool ignore_close_fails) - { - size_t idx = m_next_closed_conn_idx.fetch_add(1, std::memory_order_relaxed); - if (m_next_opened_conn_idx.load(std::memory_order_relaxed) <= idx) - { - LOG_PRINT_L0("Not enough opened connections"); - return false; - } - if (m_connections[idx].is_nil()) - { - LOG_PRINT_L0("Connection isn't opened"); - return false; - } - if (!m_tcp_server.get_config_object().close(m_connections[idx])) - { - LOG_PRINT_L0("Close connection error: " << m_connections[idx]); - if (!ignore_close_fails) - { - return false; - } - } - - m_connections[idx] = boost::uuids::nil_uuid(); - m_opened_connection_count.fetch_sub(1, std::memory_order_relaxed); - return true; - } - - size_t opened_connection_count() const { return m_opened_connection_count.load(std::memory_order_relaxed); } - - 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; - std::atomic m_next_closed_conn_idx; - std::vector m_connections; - }; - - const unsigned int min_thread_count = 2; - const std::string clt_port("36230"); - const std::string srv_port("36231"); - - enum command_ids - { - cmd_close_all_connections_id = 73564, - cmd_start_open_close_test_id, - cmd_get_statistics_id, - cmd_reset_statistics_id, - cmd_shutdown_id, - cmd_send_data_requests_id, - cmd_data_request_id - }; - - struct CMD_CLOSE_ALL_CONNECTIONS - { - const static int ID = cmd_close_all_connections_id; - - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - }; - - struct CMD_START_OPEN_CLOSE_TEST - { - const static int ID = cmd_start_open_close_test_id; - - struct request - { - uint64_t open_request_target; - uint64_t max_opened_conn_count; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(open_request_target) - KV_SERIALIZE(max_opened_conn_count) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - }; - - struct CMD_GET_STATISTICS - { - const static int ID = cmd_get_statistics_id; - - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - uint64_t opened_connections_count; - uint64_t new_connection_counter; - uint64_t close_connection_counter; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(opened_connections_count) - KV_SERIALIZE(new_connection_counter) - KV_SERIALIZE(close_connection_counter) - END_KV_SERIALIZE_MAP() - - std::string to_string() const - { - std::stringstream ss; - ss << "opened_connections_count = " << opened_connections_count << - ", new_connection_counter = " << new_connection_counter << - ", close_connection_counter = " << close_connection_counter; - return ss.str(); - } - }; - }; - - struct CMD_RESET_STATISTICS - { - const static int ID = cmd_reset_statistics_id; - - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - }; - - struct CMD_SHUTDOWN - { - const static int ID = cmd_shutdown_id; - - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - }; - - struct CMD_SEND_DATA_REQUESTS - { - const static int ID = cmd_send_data_requests_id; - - struct request - { - uint64_t request_size; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(request_size) - END_KV_SERIALIZE_MAP() - }; - }; - - struct CMD_DATA_REQUEST - { - const static int ID = cmd_data_request_id; - - struct request - { - std::string data; - uint64_t response_size; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(data) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string data; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(data) - END_KV_SERIALIZE_MAP() - }; - }; -} diff --git a/tests/net_load_tests/srv.cpp b/tests/net_load_tests/srv.cpp deleted file mode 100644 index 8fb1364010..0000000000 --- a/tests/net_load_tests/srv.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include -#include -#include - -#include - -#include "include_base_utils.h" -#include "misc_log_ex.h" -#include "storages/levin_abstract_invoke2.h" - -#include "net_load_tests.h" - -using namespace net_load_tests; - -#define EXIT_ON_ERROR(cond) { if (!(cond)) { LOG_PRINT_L0("ERROR: " << #cond); exit(1); } else {} } - -namespace -{ - struct srv_levin_commands_handler : public test_levin_commands_handler - { - srv_levin_commands_handler(test_tcp_server& tcp_server) - : m_tcp_server(tcp_server) - , m_open_close_test_conn_id(boost::uuids::nil_uuid()) - { - } - - virtual void on_connection_new(test_connection_context& context) - { - test_levin_commands_handler::on_connection_new(context); - context.m_closed = false; - - //std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - std::unique_lock lock(m_open_close_test_mutex); - if (!m_open_close_test_conn_id.is_nil()) - { - EXIT_ON_ERROR(m_open_close_test_helper->handle_new_connection(context.m_connection_id, true)); - } - } - - virtual void on_connection_close(test_connection_context& context) - { - test_levin_commands_handler::on_connection_close(context); - - std::unique_lock lock(m_open_close_test_mutex); - if (context.m_connection_id == m_open_close_test_conn_id) - { - LOG_PRINT_L0("Stop open/close test"); - m_open_close_test_conn_id = boost::uuids::nil_uuid(); - m_open_close_test_helper.reset(0); - } - } - - CHAIN_LEVIN_INVOKE_MAP2(test_connection_context); - CHAIN_LEVIN_NOTIFY_MAP2(test_connection_context); - - BEGIN_INVOKE_MAP2(srv_levin_commands_handler) - HANDLE_NOTIFY_T2(CMD_CLOSE_ALL_CONNECTIONS, &srv_levin_commands_handler::handle_close_all_connections) - HANDLE_NOTIFY_T2(CMD_SHUTDOWN, &srv_levin_commands_handler::handle_shutdown) - HANDLE_NOTIFY_T2(CMD_SEND_DATA_REQUESTS, &srv_levin_commands_handler::handle_send_data_requests) - HANDLE_INVOKE_T2(CMD_GET_STATISTICS, &srv_levin_commands_handler::handle_get_statistics) - HANDLE_INVOKE_T2(CMD_RESET_STATISTICS, &srv_levin_commands_handler::handle_reset_statistics) - HANDLE_INVOKE_T2(CMD_START_OPEN_CLOSE_TEST, &srv_levin_commands_handler::handle_start_open_close_test) - END_INVOKE_MAP2() - - int handle_close_all_connections(int command, const CMD_CLOSE_ALL_CONNECTIONS::request& req, test_connection_context& context) - { - close_connections(context.m_connection_id); - return 1; - } - - int handle_get_statistics(int command, const CMD_GET_STATISTICS::request&, CMD_GET_STATISTICS::response& rsp, test_connection_context& /*context*/) - { - rsp.opened_connections_count = m_tcp_server.get_config_object().get_connections_count(); - rsp.new_connection_counter = new_connection_counter(); - rsp.close_connection_counter = close_connection_counter(); - LOG_PRINT_L0("Statistics: " << rsp.to_string()); - return 1; - } - - int handle_reset_statistics(int command, const CMD_RESET_STATISTICS::request&, CMD_RESET_STATISTICS::response& /*rsp*/, test_connection_context& /*context*/) - { - m_new_connection_counter.reset(); - m_new_connection_counter.inc(); - m_close_connection_counter.reset(); - return 1; - } - - int handle_start_open_close_test(int command, const CMD_START_OPEN_CLOSE_TEST::request& req, CMD_START_OPEN_CLOSE_TEST::response&, test_connection_context& context) - { - std::unique_lock lock(m_open_close_test_mutex); - if (0 == m_open_close_test_helper.get()) - { - LOG_PRINT_L0("Start open/close test (" << req.open_request_target << ", " << req.max_opened_conn_count << ")"); - - m_open_close_test_conn_id = context.m_connection_id; - m_open_close_test_helper.reset(new open_close_test_helper(m_tcp_server, req.open_request_target, req.max_opened_conn_count)); - return 1; - } - else - { - return -1; - } - } - - int handle_shutdown(int command, const CMD_SHUTDOWN::request& req, test_connection_context& /*context*/) - { - LOG_PRINT_L0("Got shutdown requst. Shutting down..."); - m_tcp_server.send_stop_signal(); - return 1; - } - - int handle_send_data_requests(int /*command*/, const CMD_SEND_DATA_REQUESTS::request& req, test_connection_context& context) - { - boost::uuids::uuid cmd_conn_id = context.m_connection_id; - m_tcp_server.get_config_object().foreach_connection([&](test_connection_context& ctx) { - if (ctx.m_connection_id != cmd_conn_id) - { - CMD_DATA_REQUEST::request req2; - req2.data.resize(req.request_size); - - bool r = epee::net_utils::async_invoke_remote_command2(ctx.m_connection_id, CMD_DATA_REQUEST::ID, req2, - m_tcp_server.get_config_object(), [=](int code, const CMD_DATA_REQUEST::response& rsp, const test_connection_context&) { - if (code <= 0) - { - LOG_PRINT_L0("Failed to invoke CMD_DATA_REQUEST. code = " << code); - } - }); - if (!r) - LOG_PRINT_L0("Failed to invoke CMD_DATA_REQUEST"); - } - return true; - }); - - return 1; - } - - private: - void close_connections(boost::uuids::uuid cmd_conn_id) - { - LOG_PRINT_L0("Closing connections. Number of opened connections: " << m_tcp_server.get_config_object().get_connections_count()); - - size_t count = 0; - bool r = m_tcp_server.get_config_object().foreach_connection([&](test_connection_context& ctx) { - if (ctx.m_connection_id != cmd_conn_id) - { - ++count; - if (!ctx.m_closed) - { - ctx.m_closed = true; - m_tcp_server.get_config_object().close(ctx.m_connection_id); - } - else - { - LOG_PRINT_L0(count << " connection already closed"); - } - } - return true; - }); - - if (0 < count) - { - // Perhaps not all connections were closed, try to close it after 7 seconds - boost::shared_ptr sh_deadline(new boost::asio::deadline_timer(m_tcp_server.get_io_service(), boost::posix_time::seconds(7))); - sh_deadline->async_wait([=](const boost::system::error_code& ec) - { - boost::shared_ptr t = sh_deadline; // Capture sh_deadline - if (!ec) - { - close_connections(cmd_conn_id); - } - else - { - LOG_PRINT_L0("ERROR: " << ec.message() << ':' << ec.value()); - } - }); - } - } - - private: - test_tcp_server& m_tcp_server; - - boost::uuids::uuid m_open_close_test_conn_id; - std::mutex m_open_close_test_mutex; - std::unique_ptr m_open_close_test_helper; - }; -} - -int main(int argc, char** argv) -{ - //set up logging options - epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - - size_t thread_count = (std::max)(min_thread_count, std::thread::hardware_concurrency() / 2); - - test_tcp_server tcp_server; - if (!tcp_server.init_server(srv_port, "127.0.0.1")) - return 1; - - srv_levin_commands_handler commands_handler(tcp_server); - tcp_server.get_config_object().m_pcommands_handler = &commands_handler; - tcp_server.get_config_object().m_invoke_timeout = 10000; - //tcp_server.get_config_object().m_max_packet_size = max_packet_size; - - if (!tcp_server.run_server(thread_count, true)) - return 2; - - return 0; -} diff --git a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp index 3ff889c3bc..c006ffe088 100644 --- a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp +++ b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include "node_rpc_proxy/NodeRpcProxy.h" -using namespace cryptonote; +using namespace CryptoNote; using namespace CryptoNote; @@ -119,7 +119,7 @@ int main(int argc, const char** argv) { LOG_PRINT_RED_L0("shutdown error"); } - cryptonote::Transaction tx; + CryptoNote::Transaction tx; nodeProxy.relayTransaction(tx, [](std::error_code ec) { if (!ec) { LOG_PRINT_L0("relayTransaction called successfully"); diff --git a/tests/performance_tests/check_ring_signature.h b/tests/performance_tests/check_ring_signature.h index b50726478b..e34f93996a 100644 --- a/tests/performance_tests/check_ring_signature.h +++ b/tests/performance_tests/check_ring_signature.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -39,7 +39,7 @@ class test_check_ring_signature : private multi_tx_test_base bool init() { - using namespace cryptonote; + using namespace CryptoNote; if (!base_class::init()) return false; @@ -49,7 +49,7 @@ class test_check_ring_signature : private multi_tx_test_base std::vector destinations; destinations.push_back(tx_destination_entry(this->m_source_amount, m_alice.get_keys().m_account_address)); - if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), m_tx, 0)) + if (!construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), m_tx, 0, this->m_logger)) return false; get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); @@ -59,12 +59,12 @@ class test_check_ring_signature : private multi_tx_test_base bool test() { - const cryptonote::TransactionInputToKey& txin = boost::get(m_tx.vin[0]); + const CryptoNote::TransactionInputToKey& txin = boost::get(m_tx.vin[0]); return crypto::check_ring_signature(m_tx_prefix_hash, txin.keyImage, this->m_public_key_ptrs, ring_size, m_tx.signatures[0].data()); } private: - cryptonote::account_base m_alice; - cryptonote::Transaction m_tx; + CryptoNote::account_base m_alice; + CryptoNote::Transaction m_tx; crypto::hash m_tx_prefix_hash; }; diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index fc6d95f245..ea452409f3 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,36 +17,36 @@ #pragma once +#include "Common/StringTools.h" #include "crypto/crypto.h" #include "cryptonote_core/cryptonote_basic.h" -class test_cn_slow_hash -{ +class test_cn_slow_hash { public: static const size_t loop_count = 10; #pragma pack(push, 1) - struct data_t - { + struct data_t { char data[13]; }; #pragma pack(pop) static_assert(13 == sizeof(data_t), "Invalid structure size"); - bool init() - { - if (!epee::string_tools::hex_to_pod("63617665617420656d70746f72", m_data)) + bool init() { + std::size_t size; + if (!Common::fromHex("63617665617420656d70746f72", &m_data, sizeof(m_data), size) || size != sizeof(m_data)) { return false; + } - if (!epee::string_tools::hex_to_pod("bbec2cacf69866a8e740380fe7b818fc78f8571221742d729d9d02d7f8989b87", m_expected_hash)) + if (!Common::fromHex("bbec2cacf69866a8e740380fe7b818fc78f8571221742d729d9d02d7f8989b87", &m_expected_hash, sizeof(m_expected_hash), size) || size != sizeof(m_expected_hash)) { return false; + } return true; } - bool test() - { + bool test() { crypto::hash hash; crypto::cn_slow_hash(m_context, &m_data, sizeof(m_data), hash); return hash == m_expected_hash; diff --git a/tests/performance_tests/construct_tx.h b/tests/performance_tests/construct_tx.h index ac4f146382..0dae78ab9e 100644 --- a/tests/performance_tests/construct_tx.h +++ b/tests/performance_tests/construct_tx.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -38,7 +38,7 @@ class test_construct_tx : private multi_tx_test_base bool init() { - using namespace cryptonote; + using namespace CryptoNote; if (!base_class::init()) return false; @@ -55,11 +55,11 @@ class test_construct_tx : private multi_tx_test_base bool test() { - return cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, m_destinations, std::vector(), m_tx, 0); + return CryptoNote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, m_destinations, std::vector(), m_tx, 0, this->m_logger); } private: - cryptonote::account_base m_alice; - std::vector m_destinations; - cryptonote::Transaction m_tx; + CryptoNote::account_base m_alice; + std::vector m_destinations; + CryptoNote::Transaction m_tx; }; diff --git a/tests/performance_tests/derive_public_key.h b/tests/performance_tests/derive_public_key.h index d456d4fad8..d3ffb8e9b1 100644 --- a/tests/performance_tests/derive_public_key.h +++ b/tests/performance_tests/derive_public_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -40,7 +40,7 @@ class test_derive_public_key : public single_tx_test_base bool test() { - cryptonote::KeyPair in_ephemeral; + CryptoNote::KeyPair in_ephemeral; crypto::derive_public_key(m_key_derivation, 0, m_spend_public_key, in_ephemeral.pub); return true; } diff --git a/tests/performance_tests/derive_secret_key.h b/tests/performance_tests/derive_secret_key.h index cde62a0a2e..0d088ecc74 100644 --- a/tests/performance_tests/derive_secret_key.h +++ b/tests/performance_tests/derive_secret_key.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -40,7 +40,7 @@ class test_derive_secret_key : public single_tx_test_base bool test() { - cryptonote::KeyPair in_ephemeral; + CryptoNote::KeyPair in_ephemeral; crypto::derive_secret_key(m_key_derivation, 0, m_spend_secret_key, in_ephemeral.sec); return true; } diff --git a/tests/performance_tests/generate_key_derivation.h b/tests/performance_tests/generate_key_derivation.h index a645dd52f0..9035fd9f0f 100644 --- a/tests/performance_tests/generate_key_derivation.h +++ b/tests/performance_tests/generate_key_derivation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/performance_tests/generate_key_image.h b/tests/performance_tests/generate_key_image.h index 29106b4b39..77f5bc72c5 100644 --- a/tests/performance_tests/generate_key_image.h +++ b/tests/performance_tests/generate_key_image.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -29,7 +29,7 @@ class test_generate_key_image : public single_tx_test_base bool init() { - using namespace cryptonote; + using namespace CryptoNote; if (!single_tx_test_base::init()) return false; @@ -53,5 +53,5 @@ class test_generate_key_image : public single_tx_test_base } private: - cryptonote::KeyPair m_in_ephemeral; + CryptoNote::KeyPair m_in_ephemeral; }; diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index 121f20b0cb..4cc808bd03 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -29,8 +29,8 @@ class test_generate_key_image_helper : public single_tx_test_base bool test() { - cryptonote::KeyPair in_ephemeral; + CryptoNote::KeyPair in_ephemeral; crypto::key_image ki; - return cryptonote::generate_key_image_helper(m_bob.get_keys(), m_tx_pub_key, 0, in_ephemeral, ki); + return CryptoNote::generate_key_image_helper(m_bob.get_keys(), m_tx_pub_key, 0, in_ephemeral, ki); } }; diff --git a/tests/performance_tests/is_out_to_acc.h b/tests/performance_tests/is_out_to_acc.h index 8ea73941fc..500500efd5 100644 --- a/tests/performance_tests/is_out_to_acc.h +++ b/tests/performance_tests/is_out_to_acc.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -29,7 +29,7 @@ class test_is_out_to_acc : public single_tx_test_base bool test() { - const cryptonote::TransactionOutputToKey& tx_out = boost::get(m_tx.vout[0].target); - return cryptonote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, 0); + const CryptoNote::TransactionOutputToKey& tx_out = boost::get(m_tx.vout[0].target); + return CryptoNote::is_out_to_acc(m_bob.get_keys(), tx_out, m_tx_pub_key, 0); } }; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index 05d48ee06e..c635443a61 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/performance_tests/multi_tx_test_base.h b/tests/performance_tests/multi_tx_test_base.h index ef3e912f99..582813310f 100644 --- a/tests/performance_tests/multi_tx_test_base.h +++ b/tests/performance_tests/multi_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -25,6 +25,8 @@ #include "cryptonote_core/Currency.h" #include "crypto/crypto.h" +#include "Logging/ConsoleLogger.h" + template class multi_tx_test_base { @@ -36,9 +38,9 @@ class multi_tx_test_base bool init() { - using namespace cryptonote; + using namespace CryptoNote; - Currency currency = CurrencyBuilder().currency(); + Currency currency = CurrencyBuilder(m_logger).currency(); std::vector output_entries; for (size_t i = 0; i < ring_size; ++i) @@ -69,11 +71,12 @@ class multi_tx_test_base } protected: - cryptonote::account_base m_miners[ring_size]; - cryptonote::Transaction m_miner_txs[ring_size]; + CryptoNote::account_base m_miners[ring_size]; + CryptoNote::Transaction m_miner_txs[ring_size]; uint64_t m_source_amount; + Logging::ConsoleLogger m_logger; - std::vector m_sources; + std::vector m_sources; crypto::public_key m_public_keys[ring_size]; const crypto::public_key* m_public_key_ptrs[ring_size]; }; diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 8ff767ffeb..0fa25d88ee 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/performance_tests/performance_utils.h b/tests/performance_tests/performance_utils.h index 2ae780032f..9d2e72cf34 100644 --- a/tests/performance_tests/performance_utils.h +++ b/tests/performance_tests/performance_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/performance_tests/single_tx_test_base.h b/tests/performance_tests/single_tx_test_base.h index 7a670e0a50..22ac04e3f3 100644 --- a/tests/performance_tests/single_tx_test_base.h +++ b/tests/performance_tests/single_tx_test_base.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,14 +21,16 @@ #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include + class single_tx_test_base { public: bool init() { - using namespace cryptonote; + using namespace CryptoNote; - Currency currency = CurrencyBuilder().currency(); + Currency currency = CurrencyBuilder(m_nullLog).currency(); m_bob.generate(); if (!currency.constructMinerTx(0, 0, 0, 2, 0, m_bob.get_keys().m_account_address, m_tx)) @@ -39,7 +41,9 @@ class single_tx_test_base } protected: - cryptonote::account_base m_bob; - cryptonote::Transaction m_tx; + + Logging::LoggerGroup m_nullLog; + CryptoNote::account_base m_bob; + CryptoNote::Transaction m_tx; crypto::public_key m_tx_pub_key; }; diff --git a/tests/transfers_tests/globals.h b/tests/transfers_tests/globals.h index 74db915284..6c2033b620 100644 --- a/tests/transfers_tests/globals.h +++ b/tests/transfers_tests/globals.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include "gtest/gtest.h" extern System::Dispatcher globalSystem; -extern cryptonote::Currency currency; +extern CryptoNote::Currency currency; extern Tests::Common::BaseFunctionalTestConfig config; class TransfersTest : @@ -33,4 +33,3 @@ class TransfersTest : TransfersTest() : BaseFunctionalTest(currency, globalSystem, config) { } }; - diff --git a/tests/transfers_tests/main.cpp b/tests/transfers_tests/main.cpp index 876204ae3c..2fbde2ee7c 100644 --- a/tests/transfers_tests/main.cpp +++ b/tests/transfers_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,8 +18,11 @@ #include "gtest/gtest.h" #include "globals.h" +#include + +Logging::ConsoleLogger logger; System::Dispatcher globalSystem; -cryptonote::Currency currency = cryptonote::CurrencyBuilder().testnet(true).currency(); +CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).testnet(true).currency(); Tests::Common::BaseFunctionalTestConfig config; @@ -45,4 +48,4 @@ int main(int argc, char** argv) { LOG_ERROR("Fatal error: " + std::string(ex.what())); return 1; } -} \ No newline at end of file +} diff --git a/tests/transfers_tests/tests.cpp b/tests/transfers_tests/tests.cpp index e7d35d3fd1..f789b30f85 100644 --- a/tests/transfers_tests/tests.cpp +++ b/tests/transfers_tests/tests.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -96,7 +96,7 @@ class TransactionConsumer : public IBlockchainConsumer { return true; } - std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { + std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { //stub return std::error_code(); } @@ -128,7 +128,8 @@ class TransfersObserver : public ITransfersObserver { std::lock_guard lk(m_mutex); m_transfers.push_back(transactionHash); - auto address = epee::string_tools::pod_to_hex(object->getAddress().spendPublicKey); + auto key = object->getAddress().spendPublicKey; + std::string address = Common::toHex(&key, sizeof(key)); LOG_DEBUG("Transfer to " + address); } m_cv.notify_all(); @@ -175,7 +176,7 @@ class AccountGroup { m_sync(sync) {} void generateAccounts(size_t count) { - cryptonote::account_base acc; + CryptoNote::account_base acc; while (count--) { acc.generate(); @@ -237,7 +238,7 @@ TEST_F(TransfersTest, base) { nodeDaemons[0]->makeINode(node1); nodeDaemons[1]->makeINode(node2); - cryptonote::account_base dstAcc; + CryptoNote::account_base dstAcc; dstAcc.generate(); AccountKeys dstKeys = reinterpret_cast(dstAcc.get_keys()); @@ -306,7 +307,7 @@ std::unique_ptr createTransferToMultisignature( auto tx = createTransaction(); - std::vector> inputs; + std::vector> inputs; uint64_t foundMoney = 0; @@ -324,7 +325,7 @@ std::unique_ptr createTransferToMultisignature( info.realOutput.transactionIndex = 0; info.realOutput.transactionPublicKey = t.transactionPublicKey; - KeyPair kp; + TransactionTypes::KeyPair kp; tx->addInput(senderKeys, info, kp); inputs.push_back(std::make_pair(info, kp)); @@ -355,9 +356,9 @@ std::unique_ptr createTransferToMultisignature( std::error_code submitTransaction(INode& node, ITransactionReader& tx) { auto data = tx.getTransactionData(); - cryptonote::blobdata txblob(data.data(), data.data() + data.size()); - cryptonote::Transaction outTx; - cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + CryptoNote::blobdata txblob(data.data(), data.data() + data.size()); + CryptoNote::Transaction outTx; + CryptoNote::parse_and_validate_tx_from_blob(txblob, outTx); LOG_DEBUG("Submitting transaction " + bin2str(tx.getTransactionHash())); diff --git a/tests/unit_tests/ArrayRefTests.cpp b/tests/unit_tests/ArrayRefTests.cpp new file mode 100755 index 0000000000..525a0cad9f --- /dev/null +++ b/tests/unit_tests/ArrayRefTests.cpp @@ -0,0 +1,344 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include + +using namespace Common; + +TEST(ArrayRefTests, representations) { + ASSERT_NE(nullptr, ArrayRef<>::EMPTY.getData()); + ASSERT_EQ(0, ArrayRef<>::EMPTY.getSize()); + ASSERT_EQ(nullptr, ArrayRef<>::NIL.getData()); + ASSERT_EQ(0, ArrayRef<>::NIL.getSize()); +} + +TEST(ArrayRefTests, directConstructor) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(data, ArrayRef<>(data, 4).getData()); + ASSERT_EQ(4, ArrayRef<>(data, 4).getSize()); +} + +TEST(ArrayRefTests, arrayConstructor) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(data, ArrayRef<>(data).getData()); + ASSERT_EQ(4, ArrayRef<>(data).getSize()); +} + +TEST(ArrayRefTests, copyConstructor) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref(data); + ASSERT_EQ(ref.getData(), ArrayRef<>(ref).getData()); + ASSERT_EQ(ref.getSize(), ArrayRef<>(ref).getSize()); +} + +TEST(ArrayRefTests, copyAssignment) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref1(data); + ArrayRef<> ref2; + ref2 = ref1; + ASSERT_EQ(ref1.getData(), ref2.getData()); + ASSERT_EQ(ref1.getSize(), ref2.getSize()); +} + +TEST(ArrayRefTests, arrayView) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref(data); + ArrayView<> view = ref; + ASSERT_EQ(ref.getData(), view.getData()); + ASSERT_EQ(ref.getSize(), view.getSize()); +} + +TEST(ArrayRefTests, emptyNil) { + ASSERT_TRUE(ArrayRef<>::EMPTY.isEmpty()); + ASSERT_FALSE(ArrayRef<>::EMPTY.isNil()); + ASSERT_TRUE(ArrayRef<>::NIL.isEmpty()); + ASSERT_TRUE(ArrayRef<>::NIL.isNil()); + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_TRUE(ArrayRef<>(data, 0).isEmpty()); + ASSERT_FALSE(ArrayRef<>(data, 0).isNil()); + ASSERT_FALSE(ArrayRef<>(data).isEmpty()); + ASSERT_FALSE(ArrayRef<>(data).isNil()); +} + +TEST(ArrayRefTests, squareBrackets) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref(data); + ASSERT_EQ(data + 0, &ref[0]); + ASSERT_EQ(data + 1, &ref[1]); + ASSERT_EQ(data + 2, &ref[2]); + ASSERT_EQ(data + 3, &ref[3]); +} + +TEST(ArrayRefTests, firstLast) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref(data); + ASSERT_EQ(data + 0, &ref.first()); + ASSERT_EQ(data + 3, &ref.last()); +} + +TEST(ArrayRefTests, beginEnd) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(nullptr, ArrayRef<>::NIL.begin()); + ASSERT_EQ(nullptr, ArrayRef<>::NIL.end()); + ASSERT_EQ(data, ArrayRef<>(data).begin()); + ASSERT_EQ(data + 4, ArrayRef<>(data).end()); + std::size_t offset = 0; + for (uint8_t& value : ArrayRef<>(data)) { + ASSERT_EQ(data[offset], value); + ++offset; + } +} + +TEST(ArrayRefTests, comparisons) { + uint8_t data1[3] = {2, 3, 5}; + uint8_t data2[4] = {2, 3, 5, 7}; + uint8_t data3[4] = {2, 3, 5, 7}; + uint8_t data4[5] = {2, 3, 5, 7, 11}; + uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_TRUE(ArrayRef<>::EMPTY == ArrayView<>::EMPTY); + ASSERT_TRUE(ArrayRef<>::EMPTY == ArrayView<>::NIL); + ASSERT_FALSE(ArrayRef<>::EMPTY == ArrayView<>(data1)); + ASSERT_TRUE(ArrayRef<>::NIL == ArrayView<>::EMPTY); + ASSERT_TRUE(ArrayRef<>::NIL == ArrayView<>::NIL); + ASSERT_FALSE(ArrayRef<>::NIL == ArrayView<>(data1)); + ASSERT_FALSE(ArrayRef<>(data2) == ArrayView<>::EMPTY); + ASSERT_FALSE(ArrayRef<>(data2) == ArrayView<>::NIL); + ASSERT_FALSE(ArrayRef<>(data2) == ArrayView<>(data1)); + ASSERT_TRUE(ArrayRef<>(data2) == ArrayView<>(data2)); + ASSERT_TRUE(ArrayRef<>(data2) == ArrayView<>(data3)); + ASSERT_FALSE(ArrayRef<>(data2) == ArrayView<>(data4)); + ASSERT_FALSE(ArrayRef<>(data2) == ArrayView<>(data5)); + ASSERT_FALSE(ArrayRef<>::EMPTY != ArrayView<>::EMPTY); + ASSERT_FALSE(ArrayRef<>::EMPTY != ArrayView<>::NIL); + ASSERT_TRUE(ArrayRef<>::EMPTY != ArrayView<>(data1)); + ASSERT_FALSE(ArrayRef<>::NIL != ArrayView<>::EMPTY); + ASSERT_FALSE(ArrayRef<>::NIL != ArrayView<>::NIL); + ASSERT_TRUE(ArrayRef<>::NIL != ArrayView<>(data1)); + ASSERT_TRUE(ArrayRef<>(data2) != ArrayView<>::EMPTY); + ASSERT_TRUE(ArrayRef<>(data2) != ArrayView<>::NIL); + ASSERT_TRUE(ArrayRef<>(data2) != ArrayView<>(data1)); + ASSERT_FALSE(ArrayRef<>(data2) != ArrayView<>(data2)); + ASSERT_FALSE(ArrayRef<>(data2) != ArrayView<>(data3)); + ASSERT_TRUE(ArrayRef<>(data2) != ArrayView<>(data4)); + ASSERT_TRUE(ArrayRef<>(data2) != ArrayView<>(data5)); +} + +TEST(ArrayRefTests, beginsWith) { + uint8_t data1[3] = {2, 3, 5}; + uint8_t data2[4] = {2, 3, 5, 7}; + uint8_t data3[4] = {2, 3, 5, 7}; + uint8_t data4[5] = {2, 3, 5, 7, 11}; + uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_FALSE(ArrayRef<>::EMPTY.beginsWith(data1[0])); + ASSERT_TRUE(ArrayRef<>::EMPTY.beginsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>::EMPTY.beginsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayRef<>::EMPTY.beginsWith(ArrayView<>(data1))); + ASSERT_FALSE(ArrayRef<>::NIL.beginsWith(data1[0])); + ASSERT_TRUE(ArrayRef<>::NIL.beginsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>::NIL.beginsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayRef<>::NIL.beginsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayRef<>(data2).beginsWith(data1[0])); + ASSERT_FALSE(ArrayRef<>(data2).beginsWith(data5[0])); + ASSERT_TRUE(ArrayRef<>(data2).beginsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>(data2).beginsWith(ArrayView<>::NIL)); + ASSERT_TRUE(ArrayRef<>(data2).beginsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayRef<>(data2).beginsWith(ArrayView<>(data2))); + ASSERT_TRUE(ArrayRef<>(data2).beginsWith(ArrayView<>(data3))); + ASSERT_FALSE(ArrayRef<>(data2).beginsWith(ArrayView<>(data4))); + ASSERT_FALSE(ArrayRef<>(data2).beginsWith(ArrayView<>(data5))); +} + +TEST(ArrayRefTests, contains) { + uint8_t data1[2] = {3, 5}; + uint8_t data2[4] = {2, 3, 5, 7}; + uint8_t data3[4] = {2, 3, 5, 7}; + uint8_t data4[5] = {2, 3, 5, 7, 11}; + uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_FALSE(ArrayRef<>::EMPTY.contains(data1[1])); + ASSERT_TRUE(ArrayRef<>::EMPTY.contains(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>::EMPTY.contains(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayRef<>::EMPTY.contains(ArrayView<>(data1))); + ASSERT_FALSE(ArrayRef<>::NIL.contains(data1[1])); + ASSERT_TRUE(ArrayRef<>::NIL.contains(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>::NIL.contains(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayRef<>::NIL.contains(ArrayView<>(data1))); + ASSERT_TRUE(ArrayRef<>(data2).contains(data1[1])); + ASSERT_FALSE(ArrayRef<>(data2).contains(data5[1])); + ASSERT_TRUE(ArrayRef<>(data2).contains(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>(data2).contains(ArrayView<>::NIL)); + ASSERT_TRUE(ArrayRef<>(data2).contains(ArrayView<>(data1))); + ASSERT_TRUE(ArrayRef<>(data2).contains(ArrayView<>(data2))); + ASSERT_TRUE(ArrayRef<>(data2).contains(ArrayView<>(data3))); + ASSERT_FALSE(ArrayRef<>(data2).contains(ArrayView<>(data4))); + ASSERT_FALSE(ArrayRef<>(data2).contains(ArrayView<>(data5))); +} + +TEST(ArrayRefTests, endsWith) { + uint8_t data1[3] = {3, 5, 7}; + uint8_t data2[4] = {2, 3, 5, 7}; + uint8_t data3[4] = {2, 3, 5, 7}; + uint8_t data4[5] = {2, 3, 5, 7, 11}; + uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_FALSE(ArrayRef<>::EMPTY.endsWith(data1[2])); + ASSERT_TRUE(ArrayRef<>::EMPTY.endsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>::EMPTY.endsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayRef<>::EMPTY.endsWith(ArrayView<>(data1))); + ASSERT_FALSE(ArrayRef<>::NIL.endsWith(data1[2])); + ASSERT_TRUE(ArrayRef<>::NIL.endsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>::NIL.endsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayRef<>::NIL.endsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayRef<>(data2).endsWith(data1[2])); + ASSERT_FALSE(ArrayRef<>(data2).endsWith(data5[3])); + ASSERT_TRUE(ArrayRef<>(data2).endsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayRef<>(data2).endsWith(ArrayView<>::NIL)); + ASSERT_TRUE(ArrayRef<>(data2).endsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayRef<>(data2).endsWith(ArrayView<>(data2))); + ASSERT_TRUE(ArrayRef<>(data2).endsWith(ArrayView<>(data3))); + ASSERT_FALSE(ArrayRef<>(data2).endsWith(ArrayView<>(data4))); + ASSERT_FALSE(ArrayRef<>(data2).endsWith(ArrayView<>(data5))); +} + +TEST(ArrayRefTests, find) { + uint8_t data1[2] = {3, 5}; + uint8_t data2[6] = {2, 3, 5, 3, 5, 7}; + uint8_t data3[6] = {2, 3, 5, 3, 5, 7}; + uint8_t data4[7] = {2, 3, 5, 3, 5, 7, 11}; + uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::EMPTY.find(data1[0])); + ASSERT_EQ(0, ArrayRef<>::EMPTY.find(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayRef<>::EMPTY.find(ArrayView<>::NIL)); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::EMPTY.find(ArrayView<>(data1))); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::NIL.find(data1[0])); + ASSERT_EQ(0, ArrayRef<>::NIL.find(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayRef<>::NIL.find(ArrayView<>::NIL)); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::NIL.find(ArrayView<>(data1))); + ASSERT_EQ(1, ArrayRef<>(data2).find(data1[0])); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>(data2).find(data5[1])); + ASSERT_EQ(0, ArrayRef<>(data2).find(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayRef<>(data2).find(ArrayView<>::NIL)); + ASSERT_EQ(1, ArrayRef<>(data2).find(ArrayView<>(data1))); + ASSERT_EQ(0, ArrayRef<>(data2).find(ArrayView<>(data2))); + ASSERT_EQ(0, ArrayRef<>(data2).find(ArrayView<>(data3))); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>(data2).find(ArrayView<>(data4))); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>(data2).find(ArrayView<>(data5))); +} + +TEST(ArrayRefTests, findLast) { + uint8_t data1[2] = {3, 5}; + uint8_t data2[6] = {2, 3, 5, 3, 5, 7}; + uint8_t data3[6] = {2, 3, 5, 3, 5, 7}; + uint8_t data4[7] = {2, 3, 5, 3, 5, 7, 11}; + uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::EMPTY.findLast(data1[0])); + ASSERT_EQ(0, ArrayRef<>::EMPTY.findLast(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayRef<>::EMPTY.findLast(ArrayView<>::NIL)); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::EMPTY.findLast(ArrayView<>(data1))); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::NIL.findLast(data1[0])); + ASSERT_EQ(0, ArrayRef<>::NIL.findLast(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayRef<>::NIL.findLast(ArrayView<>::NIL)); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>::NIL.findLast(ArrayView<>(data1))); + ASSERT_EQ(3, ArrayRef<>(data2).findLast(data1[0])); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>(data2).findLast(data5[1])); + ASSERT_EQ(6, ArrayRef<>(data2).findLast(ArrayView<>::EMPTY)); + ASSERT_EQ(6, ArrayRef<>(data2).findLast(ArrayView<>::NIL)); + ASSERT_EQ(3, ArrayRef<>(data2).findLast(ArrayView<>(data1))); + ASSERT_EQ(0, ArrayRef<>(data2).findLast(ArrayView<>(data2))); + ASSERT_EQ(0, ArrayRef<>(data2).findLast(ArrayView<>(data3))); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>(data2).findLast(ArrayView<>(data4))); + ASSERT_EQ(ArrayRef<>::INVALID, ArrayRef<>(data2).findLast(ArrayView<>(data5))); +} + +TEST(ArrayRefTests, head) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayRef<>::EMPTY.head(0).getSize()); + ASSERT_EQ(ArrayRef<>(nullptr, 0), ArrayRef<>::NIL.head(0)); + ASSERT_EQ(ArrayRef<>(data, 0), ArrayRef<>(data).head(0)); + ASSERT_EQ(ArrayRef<>(data, 2), ArrayRef<>(data).head(2)); + ASSERT_EQ(ArrayRef<>(data, 4), ArrayRef<>(data).head(4)); +} + +TEST(ArrayRefTests, tail) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayRef<>::EMPTY.tail(0).getSize()); + ASSERT_EQ(ArrayRef<>(nullptr, 0), ArrayRef<>::NIL.tail(0)); + ASSERT_EQ(ArrayRef<>(data + 4, 0), ArrayRef<>(data).tail(0)); + ASSERT_EQ(ArrayRef<>(data + 2, 2), ArrayRef<>(data).tail(2)); + ASSERT_EQ(ArrayRef<>(data, 4), ArrayRef<>(data).tail(4)); +} + +TEST(ArrayRefTests, unhead) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayRef<>::EMPTY.unhead(0).getSize()); + ASSERT_EQ(ArrayRef<>(nullptr, 0), ArrayRef<>::NIL.unhead(0)); + ASSERT_EQ(ArrayRef<>(data, 4), ArrayRef<>(data).unhead(0)); + ASSERT_EQ(ArrayRef<>(data + 2, 2), ArrayRef<>(data).unhead(2)); + ASSERT_EQ(ArrayRef<>(data + 4, 0), ArrayRef<>(data).unhead(4)); +} + +TEST(ArrayRefTests, untail) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayRef<>::EMPTY.untail(0).getSize()); + ASSERT_EQ(ArrayRef<>(nullptr, 0), ArrayRef<>::NIL.untail(0)); + ASSERT_EQ(ArrayRef<>(data, 4), ArrayRef<>(data).untail(0)); + ASSERT_EQ(ArrayRef<>(data, 2), ArrayRef<>(data).untail(2)); + ASSERT_EQ(ArrayRef<>(data, 0), ArrayRef<>(data).untail(4)); +} + +TEST(ArrayRefTests, range) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayRef<>::EMPTY.range(0, 0).getSize()); + ASSERT_EQ(ArrayRef<>(nullptr, 0), ArrayRef<>::NIL.range(0, 0)); + ASSERT_EQ(ArrayRef<>(data + 0, 0), ArrayRef<>(data).range(0, 0)); + ASSERT_EQ(ArrayRef<>(data + 0, 2), ArrayRef<>(data).range(0, 2)); + ASSERT_EQ(ArrayRef<>(data + 0, 4), ArrayRef<>(data).range(0, 4)); + ASSERT_EQ(ArrayRef<>(data + 2, 0), ArrayRef<>(data).range(2, 2)); + ASSERT_EQ(ArrayRef<>(data + 2, 2), ArrayRef<>(data).range(2, 4)); + ASSERT_EQ(ArrayRef<>(data + 4, 0), ArrayRef<>(data).range(4, 4)); +} + +TEST(ArrayRefTests, slice) { + uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayRef<>::EMPTY.slice(0, 0).getSize()); + ASSERT_EQ(ArrayRef<>(nullptr, 0), ArrayRef<>::NIL.slice(0, 0)); + ASSERT_EQ(ArrayRef<>(data + 0, 0), ArrayRef<>(data).slice(0, 0)); + ASSERT_EQ(ArrayRef<>(data + 0, 2), ArrayRef<>(data).slice(0, 2)); + ASSERT_EQ(ArrayRef<>(data + 0, 4), ArrayRef<>(data).slice(0, 4)); + ASSERT_EQ(ArrayRef<>(data + 2, 0), ArrayRef<>(data).slice(2, 0)); + ASSERT_EQ(ArrayRef<>(data + 2, 2), ArrayRef<>(data).slice(2, 2)); + ASSERT_EQ(ArrayRef<>(data + 4, 0), ArrayRef<>(data).slice(4, 0)); +} + +TEST(ArrayRefTests, fill) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref(data); + ASSERT_EQ(ArrayRef<>(data), ref.fill(11)); + ASSERT_EQ(11, data[0]); + ASSERT_EQ(11, data[1]); + ASSERT_EQ(11, data[2]); + ASSERT_EQ(11, data[3]); +} + +TEST(ArrayRefTests, reverse) { + uint8_t data[4] = {2, 3, 5, 7}; + const ArrayRef<> ref(data); + ASSERT_EQ(ArrayRef<>(data), ref.reverse()); + ASSERT_EQ(7, data[0]); + ASSERT_EQ(5, data[1]); + ASSERT_EQ(3, data[2]); + ASSERT_EQ(2, data[3]); +} diff --git a/tests/unit_tests/ArrayViewTests.cpp b/tests/unit_tests/ArrayViewTests.cpp new file mode 100755 index 0000000000..e31709231c --- /dev/null +++ b/tests/unit_tests/ArrayViewTests.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include + +using namespace Common; + +TEST(ArrayViewTests, representations) { + ASSERT_NE(nullptr, ArrayView<>::EMPTY.getData()); + ASSERT_EQ(0, ArrayView<>::EMPTY.getSize()); + ASSERT_EQ(nullptr, ArrayView<>::NIL.getData()); + ASSERT_EQ(0, ArrayView<>::NIL.getSize()); +} + +TEST(ArrayViewTests, directConstructor) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(data, ArrayView<>(data, 4).getData()); + ASSERT_EQ(4, ArrayView<>(data, 4).getSize()); +} + +TEST(ArrayViewTests, arrayConstructor) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(data, ArrayView<>(data).getData()); + ASSERT_EQ(4, ArrayView<>(data).getSize()); +} + +TEST(ArrayViewTests, copyConstructor) { + const uint8_t data[4] = {2, 3, 5, 7}; + const ArrayView<> view(data); + ASSERT_EQ(view.getData(), ArrayView<>(view).getData()); + ASSERT_EQ(view.getSize(), ArrayView<>(view).getSize()); +} + +TEST(ArrayViewTests, copyAssignment) { + const uint8_t data[4] = {2, 3, 5, 7}; + const ArrayView<> view1(data); + ArrayView<> view2; + view2 = view1; + ASSERT_EQ(view1.getData(), view2.getData()); + ASSERT_EQ(view1.getSize(), view2.getSize()); +} + +TEST(ArrayViewTests, emptyNil) { + ASSERT_TRUE(ArrayView<>::EMPTY.isEmpty()); + ASSERT_FALSE(ArrayView<>::EMPTY.isNil()); + ASSERT_TRUE(ArrayView<>::NIL.isEmpty()); + ASSERT_TRUE(ArrayView<>::NIL.isNil()); + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_TRUE(ArrayView<>(data, 0).isEmpty()); + ASSERT_FALSE(ArrayView<>(data, 0).isNil()); + ASSERT_FALSE(ArrayView<>(data).isEmpty()); + ASSERT_FALSE(ArrayView<>(data).isNil()); +} + +TEST(ArrayViewTests, squareBrackets) { + const uint8_t data[4] = {2, 3, 5, 7}; + const ArrayView<> view(data); + ASSERT_EQ(data + 0, &view[0]); + ASSERT_EQ(data + 1, &view[1]); + ASSERT_EQ(data + 2, &view[2]); + ASSERT_EQ(data + 3, &view[3]); +} + +TEST(ArrayViewTests, firstLast) { + const uint8_t data[4] = {2, 3, 5, 7}; + const ArrayView<> view(data); + ASSERT_EQ(data + 0, &view.first()); + ASSERT_EQ(data + 3, &view.last()); +} + +TEST(ArrayViewTests, beginEnd) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(nullptr, ArrayView<>::NIL.begin()); + ASSERT_EQ(nullptr, ArrayView<>::NIL.end()); + ASSERT_EQ(data, ArrayView<>(data).begin()); + ASSERT_EQ(data + 4, ArrayView<>(data).end()); + std::size_t offset = 0; + for (const uint8_t& value : ArrayView<>(data)) { + ASSERT_EQ(data[offset], value); + ++offset; + } +} + +TEST(ArrayViewTests, comparisons) { + const uint8_t data1[3] = {2, 3, 5}; + const uint8_t data2[4] = {2, 3, 5, 7}; + const uint8_t data3[4] = {2, 3, 5, 7}; + const uint8_t data4[5] = {2, 3, 5, 7, 11}; + const uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_TRUE(ArrayView<>::EMPTY == ArrayView<>::EMPTY); + ASSERT_TRUE(ArrayView<>::EMPTY == ArrayView<>::NIL); + ASSERT_FALSE(ArrayView<>::EMPTY == ArrayView<>(data1)); + ASSERT_TRUE(ArrayView<>::NIL == ArrayView<>::EMPTY); + ASSERT_TRUE(ArrayView<>::NIL == ArrayView<>::NIL); + ASSERT_FALSE(ArrayView<>::NIL == ArrayView<>(data1)); + ASSERT_FALSE(ArrayView<>(data2) == ArrayView<>::EMPTY); + ASSERT_FALSE(ArrayView<>(data2) == ArrayView<>::NIL); + ASSERT_FALSE(ArrayView<>(data2) == ArrayView<>(data1)); + ASSERT_TRUE(ArrayView<>(data2) == ArrayView<>(data2)); + ASSERT_TRUE(ArrayView<>(data2) == ArrayView<>(data3)); + ASSERT_FALSE(ArrayView<>(data2) == ArrayView<>(data4)); + ASSERT_FALSE(ArrayView<>(data2) == ArrayView<>(data5)); + ASSERT_FALSE(ArrayView<>::EMPTY != ArrayView<>::EMPTY); + ASSERT_FALSE(ArrayView<>::EMPTY != ArrayView<>::NIL); + ASSERT_TRUE(ArrayView<>::EMPTY != ArrayView<>(data1)); + ASSERT_FALSE(ArrayView<>::NIL != ArrayView<>::EMPTY); + ASSERT_FALSE(ArrayView<>::NIL != ArrayView<>::NIL); + ASSERT_TRUE(ArrayView<>::NIL != ArrayView<>(data1)); + ASSERT_TRUE(ArrayView<>(data2) != ArrayView<>::EMPTY); + ASSERT_TRUE(ArrayView<>(data2) != ArrayView<>::NIL); + ASSERT_TRUE(ArrayView<>(data2) != ArrayView<>(data1)); + ASSERT_FALSE(ArrayView<>(data2) != ArrayView<>(data2)); + ASSERT_FALSE(ArrayView<>(data2) != ArrayView<>(data3)); + ASSERT_TRUE(ArrayView<>(data2) != ArrayView<>(data4)); + ASSERT_TRUE(ArrayView<>(data2) != ArrayView<>(data5)); +} + +TEST(ArrayViewTests, beginsWith) { + const uint8_t data1[3] = {2, 3, 5}; + const uint8_t data2[4] = {2, 3, 5, 7}; + const uint8_t data3[4] = {2, 3, 5, 7}; + const uint8_t data4[5] = {2, 3, 5, 7, 11}; + const uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_FALSE(ArrayView<>::EMPTY.beginsWith(data1[0])); + ASSERT_TRUE(ArrayView<>::EMPTY.beginsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>::EMPTY.beginsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayView<>::EMPTY.beginsWith(ArrayView<>(data1))); + ASSERT_FALSE(ArrayView<>::NIL.beginsWith(data1[0])); + ASSERT_TRUE(ArrayView<>::NIL.beginsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>::NIL.beginsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayView<>::NIL.beginsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayView<>(data2).beginsWith(data1[0])); + ASSERT_FALSE(ArrayView<>(data2).beginsWith(data5[0])); + ASSERT_TRUE(ArrayView<>(data2).beginsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>(data2).beginsWith(ArrayView<>::NIL)); + ASSERT_TRUE(ArrayView<>(data2).beginsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayView<>(data2).beginsWith(ArrayView<>(data2))); + ASSERT_TRUE(ArrayView<>(data2).beginsWith(ArrayView<>(data3))); + ASSERT_FALSE(ArrayView<>(data2).beginsWith(ArrayView<>(data4))); + ASSERT_FALSE(ArrayView<>(data2).beginsWith(ArrayView<>(data5))); +} + +TEST(ArrayViewTests, contains) { + const uint8_t data1[2] = {3, 5}; + const uint8_t data2[4] = {2, 3, 5, 7}; + const uint8_t data3[4] = {2, 3, 5, 7}; + const uint8_t data4[5] = {2, 3, 5, 7, 11}; + const uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_FALSE(ArrayView<>::EMPTY.contains(data1[1])); + ASSERT_TRUE(ArrayView<>::EMPTY.contains(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>::EMPTY.contains(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayView<>::EMPTY.contains(ArrayView<>(data1))); + ASSERT_FALSE(ArrayView<>::NIL.contains(data1[1])); + ASSERT_TRUE(ArrayView<>::NIL.contains(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>::NIL.contains(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayView<>::NIL.contains(ArrayView<>(data1))); + ASSERT_TRUE(ArrayView<>(data2).contains(data1[1])); + ASSERT_FALSE(ArrayView<>(data2).contains(data5[1])); + ASSERT_TRUE(ArrayView<>(data2).contains(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>(data2).contains(ArrayView<>::NIL)); + ASSERT_TRUE(ArrayView<>(data2).contains(ArrayView<>(data1))); + ASSERT_TRUE(ArrayView<>(data2).contains(ArrayView<>(data2))); + ASSERT_TRUE(ArrayView<>(data2).contains(ArrayView<>(data3))); + ASSERT_FALSE(ArrayView<>(data2).contains(ArrayView<>(data4))); + ASSERT_FALSE(ArrayView<>(data2).contains(ArrayView<>(data5))); +} + +TEST(ArrayViewTests, endsWith) { + const uint8_t data1[3] = {3, 5, 7}; + const uint8_t data2[4] = {2, 3, 5, 7}; + const uint8_t data3[4] = {2, 3, 5, 7}; + const uint8_t data4[5] = {2, 3, 5, 7, 11}; + const uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_FALSE(ArrayView<>::EMPTY.endsWith(data1[2])); + ASSERT_TRUE(ArrayView<>::EMPTY.endsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>::EMPTY.endsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayView<>::EMPTY.endsWith(ArrayView<>(data1))); + ASSERT_FALSE(ArrayView<>::NIL.endsWith(data1[2])); + ASSERT_TRUE(ArrayView<>::NIL.endsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>::NIL.endsWith(ArrayView<>::NIL)); + ASSERT_FALSE(ArrayView<>::NIL.endsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayView<>(data2).endsWith(data1[2])); + ASSERT_FALSE(ArrayView<>(data2).endsWith(data5[3])); + ASSERT_TRUE(ArrayView<>(data2).endsWith(ArrayView<>::EMPTY)); + ASSERT_TRUE(ArrayView<>(data2).endsWith(ArrayView<>::NIL)); + ASSERT_TRUE(ArrayView<>(data2).endsWith(ArrayView<>(data1))); + ASSERT_TRUE(ArrayView<>(data2).endsWith(ArrayView<>(data2))); + ASSERT_TRUE(ArrayView<>(data2).endsWith(ArrayView<>(data3))); + ASSERT_FALSE(ArrayView<>(data2).endsWith(ArrayView<>(data4))); + ASSERT_FALSE(ArrayView<>(data2).endsWith(ArrayView<>(data5))); +} + +TEST(ArrayViewTests, find) { + const uint8_t data1[2] = {3, 5}; + const uint8_t data2[6] = {2, 3, 5, 3, 5, 7}; + const uint8_t data3[6] = {2, 3, 5, 3, 5, 7}; + const uint8_t data4[7] = {2, 3, 5, 3, 5, 7, 11}; + const uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::EMPTY.find(data1[0])); + ASSERT_EQ(0, ArrayView<>::EMPTY.find(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayView<>::EMPTY.find(ArrayView<>::NIL)); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::EMPTY.find(ArrayView<>(data1))); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::NIL.find(data1[0])); + ASSERT_EQ(0, ArrayView<>::NIL.find(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayView<>::NIL.find(ArrayView<>::NIL)); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::NIL.find(ArrayView<>(data1))); + ASSERT_EQ(1, ArrayView<>(data2).find(data1[0])); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>(data2).find(data5[1])); + ASSERT_EQ(0, ArrayView<>(data2).find(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayView<>(data2).find(ArrayView<>::NIL)); + ASSERT_EQ(1, ArrayView<>(data2).find(ArrayView<>(data1))); + ASSERT_EQ(0, ArrayView<>(data2).find(ArrayView<>(data2))); + ASSERT_EQ(0, ArrayView<>(data2).find(ArrayView<>(data3))); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>(data2).find(ArrayView<>(data4))); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>(data2).find(ArrayView<>(data5))); +} + +TEST(ArrayViewTests, findLast) { + const uint8_t data1[2] = {3, 5}; + const uint8_t data2[6] = {2, 3, 5, 3, 5, 7}; + const uint8_t data3[6] = {2, 3, 5, 3, 5, 7}; + const uint8_t data4[7] = {2, 3, 5, 3, 5, 7, 11}; + const uint8_t data5[4] = {13, 17, 19, 23}; + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::EMPTY.findLast(data1[0])); + ASSERT_EQ(0, ArrayView<>::EMPTY.findLast(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayView<>::EMPTY.findLast(ArrayView<>::NIL)); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::EMPTY.findLast(ArrayView<>(data1))); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::NIL.findLast(data1[0])); + ASSERT_EQ(0, ArrayView<>::NIL.findLast(ArrayView<>::EMPTY)); + ASSERT_EQ(0, ArrayView<>::NIL.findLast(ArrayView<>::NIL)); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>::NIL.findLast(ArrayView<>(data1))); + ASSERT_EQ(3, ArrayView<>(data2).findLast(data1[0])); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>(data2).findLast(data5[1])); + ASSERT_EQ(6, ArrayView<>(data2).findLast(ArrayView<>::EMPTY)); + ASSERT_EQ(6, ArrayView<>(data2).findLast(ArrayView<>::NIL)); + ASSERT_EQ(3, ArrayView<>(data2).findLast(ArrayView<>(data1))); + ASSERT_EQ(0, ArrayView<>(data2).findLast(ArrayView<>(data2))); + ASSERT_EQ(0, ArrayView<>(data2).findLast(ArrayView<>(data3))); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>(data2).findLast(ArrayView<>(data4))); + ASSERT_EQ(ArrayView<>::INVALID, ArrayView<>(data2).findLast(ArrayView<>(data5))); +} + +TEST(ArrayViewTests, head) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayView<>::EMPTY.head(0).getSize()); + ASSERT_EQ(ArrayView<>(nullptr, 0), ArrayView<>::NIL.head(0)); + ASSERT_EQ(ArrayView<>(data, 0), ArrayView<>(data).head(0)); + ASSERT_EQ(ArrayView<>(data, 2), ArrayView<>(data).head(2)); + ASSERT_EQ(ArrayView<>(data, 4), ArrayView<>(data).head(4)); +} + +TEST(ArrayViewTests, tail) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayView<>::EMPTY.tail(0).getSize()); + ASSERT_EQ(ArrayView<>(nullptr, 0), ArrayView<>::NIL.tail(0)); + ASSERT_EQ(ArrayView<>(data + 4, 0), ArrayView<>(data).tail(0)); + ASSERT_EQ(ArrayView<>(data + 2, 2), ArrayView<>(data).tail(2)); + ASSERT_EQ(ArrayView<>(data, 4), ArrayView<>(data).tail(4)); +} + +TEST(ArrayViewTests, unhead) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayView<>::EMPTY.unhead(0).getSize()); + ASSERT_EQ(ArrayView<>(nullptr, 0), ArrayView<>::NIL.unhead(0)); + ASSERT_EQ(ArrayView<>(data, 4), ArrayView<>(data).unhead(0)); + ASSERT_EQ(ArrayView<>(data + 2, 2), ArrayView<>(data).unhead(2)); + ASSERT_EQ(ArrayView<>(data + 4, 0), ArrayView<>(data).unhead(4)); +} + +TEST(ArrayViewTests, untail) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayView<>::EMPTY.untail(0).getSize()); + ASSERT_EQ(ArrayView<>(nullptr, 0), ArrayView<>::NIL.untail(0)); + ASSERT_EQ(ArrayView<>(data, 4), ArrayView<>(data).untail(0)); + ASSERT_EQ(ArrayView<>(data, 2), ArrayView<>(data).untail(2)); + ASSERT_EQ(ArrayView<>(data, 0), ArrayView<>(data).untail(4)); +} + +TEST(ArrayViewTests, range) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayView<>::EMPTY.range(0, 0).getSize()); + ASSERT_EQ(ArrayView<>(nullptr, 0), ArrayView<>::NIL.range(0, 0)); + ASSERT_EQ(ArrayView<>(data + 0, 0), ArrayView<>(data).range(0, 0)); + ASSERT_EQ(ArrayView<>(data + 0, 2), ArrayView<>(data).range(0, 2)); + ASSERT_EQ(ArrayView<>(data + 0, 4), ArrayView<>(data).range(0, 4)); + ASSERT_EQ(ArrayView<>(data + 2, 0), ArrayView<>(data).range(2, 2)); + ASSERT_EQ(ArrayView<>(data + 2, 2), ArrayView<>(data).range(2, 4)); + ASSERT_EQ(ArrayView<>(data + 4, 0), ArrayView<>(data).range(4, 4)); +} + +TEST(ArrayViewTests, slice) { + const uint8_t data[4] = {2, 3, 5, 7}; + ASSERT_EQ(0, ArrayView<>::EMPTY.slice(0, 0).getSize()); + ASSERT_EQ(ArrayView<>(nullptr, 0), ArrayView<>::NIL.slice(0, 0)); + ASSERT_EQ(ArrayView<>(data + 0, 0), ArrayView<>(data).slice(0, 0)); + ASSERT_EQ(ArrayView<>(data + 0, 2), ArrayView<>(data).slice(0, 2)); + ASSERT_EQ(ArrayView<>(data + 0, 4), ArrayView<>(data).slice(0, 4)); + ASSERT_EQ(ArrayView<>(data + 2, 0), ArrayView<>(data).slice(2, 0)); + ASSERT_EQ(ArrayView<>(data + 2, 2), ArrayView<>(data).slice(2, 2)); + ASSERT_EQ(ArrayView<>(data + 4, 0), ArrayView<>(data).slice(4, 0)); +} diff --git a/tests/unit_tests/BlockingQueue.cpp b/tests/unit_tests/BlockingQueue.cpp index df673387fe..6fe8abd60f 100644 --- a/tests/unit_tests/BlockingQueue.cpp +++ b/tests/unit_tests/BlockingQueue.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -16,7 +16,7 @@ // along with Bytecoin. If not, see . #include -#include "common/BlockingQueue.h" +#include "Common/BlockingQueue.h" #include #include @@ -192,9 +192,9 @@ TEST(BlockingQueue, CloseAndWait) f1.get(); f2.get(); - ASSERT_EQ(queueSize, itemsPopped.load()); - p.join(); + + ASSERT_EQ(queueSize, itemsPopped.load()); } TEST(BlockingQueue, AllowsMoveOnly) diff --git a/tests/unit_tests/EventWaiter.cpp b/tests/unit_tests/EventWaiter.cpp index 3fedc762f1..d578293545 100644 --- a/tests/unit_tests/EventWaiter.cpp +++ b/tests/unit_tests/EventWaiter.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -34,4 +34,4 @@ bool EventWaiter::wait_for(const std::chrono::milliseconds& rel_time) { auto result = cv.wait_for(lck, rel_time, [this]() { return available; }); available = false; return result; -} \ No newline at end of file +} diff --git a/tests/unit_tests/EventWaiter.h b/tests/unit_tests/EventWaiter.h index a1a8f14538..8f25ac2383 100644 --- a/tests/unit_tests/EventWaiter.h +++ b/tests/unit_tests/EventWaiter.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -32,4 +32,4 @@ class EventWaiter { void notify(); void wait(); bool wait_for(const std::chrono::milliseconds& rel_time); -}; \ No newline at end of file +}; diff --git a/tests/unit_tests/ICoreStub.cpp b/tests/unit_tests/ICoreStub.cpp index 248249a3fc..7ef0890c7f 100755 --- a/tests/unit_tests/ICoreStub.cpp +++ b/tests/unit_tests/ICoreStub.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,11 +17,11 @@ #include "ICoreStub.h" -bool ICoreStub::addObserver(cryptonote::ICoreObserver* observer) { +bool ICoreStub::addObserver(CryptoNote::ICoreObserver* observer) { return true; } -bool ICoreStub::removeObserver(cryptonote::ICoreObserver* observer) { +bool ICoreStub::removeObserver(CryptoNote::ICoreObserver* observer) { return true; } @@ -31,18 +31,18 @@ bool ICoreStub::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { return topResult; } -bool ICoreStub::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, +bool ICoreStub::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { return true; } -bool ICoreStub::find_blockchain_supplement(const std::list& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp) { +bool ICoreStub::find_blockchain_supplement(const std::list& qblock_ids, CryptoNote::NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp) { return true; } -bool ICoreStub::get_random_outs_for_amounts(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) { +bool ICoreStub::get_random_outs_for_amounts(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res) { res = randomOuts; return randomOutsResult; } @@ -52,11 +52,11 @@ bool ICoreStub::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs, bool re globalIndicesResult = result; } -void ICoreStub::set_random_outs(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result) { +void ICoreStub::set_random_outs(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result) { randomOuts = resp; randomOutsResult = result; } -bool ICoreStub::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { +bool ICoreStub::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { return true; } bool ICoreStub::queryBlocks(const std::list& block_ids, uint64_t timestamp, - uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) { + uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) { //stub return true; } -bool ICoreStub::getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) { +bool ICoreStub::getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) { //stub return true; } - diff --git a/tests/unit_tests/ICoreStub.h b/tests/unit_tests/ICoreStub.h old mode 100755 new mode 100644 index 70bcb179bf..740f4d6c37 --- a/tests/unit_tests/ICoreStub.h +++ b/tests/unit_tests/ICoreStub.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -25,30 +25,41 @@ #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" -class ICoreStub: public cryptonote::ICore { +class ICoreStub: public CryptoNote::ICore { public: ICoreStub() : topHeight(0), topResult(false), globalIndicesResult(false), randomOutsResult(false) {}; - virtual bool addObserver(cryptonote::ICoreObserver* observer); - virtual bool removeObserver(cryptonote::ICoreObserver* observer); + virtual bool addObserver(CryptoNote::ICoreObserver* observer); + virtual bool removeObserver(CryptoNote::ICoreObserver* observer); virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id); - virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, + virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - virtual bool find_blockchain_supplement(const std::list& qblock_ids, cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); - virtual bool get_random_outs_for_amounts(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); + virtual bool find_blockchain_supplement(const std::list& qblock_ids, CryptoNote::NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); + virtual bool get_random_outs_for_amounts(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - virtual cryptonote::i_cryptonote_protocol* get_protocol(); - virtual bool handle_incoming_tx(cryptonote::blobdata const& tx_blob, cryptonote::tx_verification_context& tvc, bool keeped_by_block); - virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; + virtual CryptoNote::i_cryptonote_protocol* get_protocol(); + virtual bool handle_incoming_tx(CryptoNote::blobdata const& tx_blob, CryptoNote::tx_verification_context& tvc, bool keeped_by_block); + virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, - uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); + uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); - virtual bool getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) override; + virtual bool getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) override; + + virtual bool have_block(const crypto::hash& id) override { return false; } + virtual bool get_short_chain_history(std::list& ids) override { return false; } + virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) override { return false; } + virtual bool on_idle() override { return false; } + virtual void pause_mining() override {} + virtual void update_block_template_and_resume_mining() override {} + virtual bool handle_incoming_block_blob(const CryptoNote::blobdata& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) override { return false; } + virtual bool handle_get_objects(CryptoNote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) override { return false; } + virtual void on_synchronized() override {} + virtual bool is_ready() override { return true; } void set_blockchain_top(uint64_t height, const crypto::hash& top_id, bool result); void set_outputs_gindexs(const std::vector& indexs, bool result); - void set_random_outs(const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result); + void set_random_outs(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result); private: uint64_t topHeight; @@ -58,6 +69,6 @@ class ICoreStub: public cryptonote::ICore { std::vector globalIndices; bool globalIndicesResult; - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response randomOuts; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response randomOuts; bool randomOutsResult; }; diff --git a/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp b/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp index 6211389ad7..b3c242fbe3 100644 --- a/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp +++ b/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,11 +17,11 @@ #include "ICryptonoteProtocolQueryStub.h" -bool ICryptonoteProtocolQueryStub::addObserver(cryptonote::ICryptonoteProtocolObserver* observer) { +bool ICryptonoteProtocolQueryStub::addObserver(CryptoNote::ICryptonoteProtocolObserver* observer) { return false; } -bool ICryptonoteProtocolQueryStub::removeObserver(cryptonote::ICryptonoteProtocolObserver* observer) { +bool ICryptonoteProtocolQueryStub::removeObserver(CryptoNote::ICryptonoteProtocolObserver* observer) { return false; } diff --git a/tests/unit_tests/ICryptonoteProtocolQueryStub.h b/tests/unit_tests/ICryptonoteProtocolQueryStub.h index 88ae43e75d..fbf55ff2b9 100644 --- a/tests/unit_tests/ICryptonoteProtocolQueryStub.h +++ b/tests/unit_tests/ICryptonoteProtocolQueryStub.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,12 +22,12 @@ #include "cryptonote_protocol/ICryptonoteProtocolObserver.h" #include "cryptonote_protocol/ICryptonoteProtocolQuery.h" -class ICryptonoteProtocolQueryStub: public cryptonote::ICryptonoteProtocolQuery { +class ICryptonoteProtocolQueryStub: public CryptoNote::ICryptonoteProtocolQuery { public: ICryptonoteProtocolQueryStub() : peers(0), observedHeight(0) {} - virtual bool addObserver(cryptonote::ICryptonoteProtocolObserver* observer); - virtual bool removeObserver(cryptonote::ICryptonoteProtocolObserver* observer); + virtual bool addObserver(CryptoNote::ICryptonoteProtocolObserver* observer); + virtual bool removeObserver(CryptoNote::ICryptonoteProtocolObserver* observer); virtual uint64_t getObservedHeight() const; virtual size_t getPeerCount() const; void setPeerCount(uint32_t count); @@ -37,4 +37,3 @@ class ICryptonoteProtocolQueryStub: public cryptonote::ICryptonoteProtocolQuery size_t peers; uint64_t observedHeight; }; - diff --git a/tests/unit_tests/INodeStubs.cpp b/tests/unit_tests/INodeStubs.cpp index 9f07f91741..87981944ee 100644 --- a/tests/unit_tests/INodeStubs.cpp +++ b/tests/unit_tests/INodeStubs.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -53,30 +53,36 @@ bool INodeDummyStub::removeObserver(CryptoNote::INodeObserver* observer) { return observerManager.remove(observer); } -void INodeTrivialRefreshStub::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) +void INodeTrivialRefreshStub::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { m_asyncCounter.addAsyncContext(); - std::thread task(std::bind(&INodeTrivialRefreshStub::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), std::ref(startHeight), callback)); + + std::unique_lock lock(m_multiWalletLock); + auto blockchain = m_blockchainGenerator.getBlockchainCopy(); + lock.unlock(); + + std::thread task(std::bind(&INodeTrivialRefreshStub::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), + std::ref(startHeight), std::move(blockchain), callback)); task.detach(); } -void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) +void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, + uint64_t& startHeight, std::vector blockchain, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_multiWalletLock); - auto& blockchain = m_blockchainGenerator.getBlockchain(); - - std::vector::iterator start = blockchain.end(); + std::vector::iterator start = blockchain.end(); for (const auto& id : knownBlockIds) { start = std::find_if(blockchain.begin(), blockchain.end(), - [&id](cryptonote::Block& block) { return get_block_hash(block) == id; }); + [&id](CryptoNote::Block& block) { return get_block_hash(block) == id; }); if (start != blockchain.end()) break; } if (start == blockchain.end()) { + lock.unlock(); callback(std::error_code()); return; } @@ -86,12 +92,12 @@ void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockI for (; m_lastHeight < blockchain.size(); ++m_lastHeight) { - cryptonote::block_complete_entry e; - e.block = cryptonote::t_serializable_object_to_blob(blockchain[m_lastHeight]); + CryptoNote::block_complete_entry e; + e.block = CryptoNote::t_serializable_object_to_blob(blockchain[m_lastHeight]); for (auto hash : blockchain[m_lastHeight].txHashes) { - cryptonote::Transaction tx; + CryptoNote::Transaction tx; if (!m_blockchainGenerator.getTransactionByHash(hash, tx)) continue; @@ -108,6 +114,7 @@ void INodeTrivialRefreshStub::doGetNewBlocks(std::list knownBlockI m_lastHeight = startHeight + newBlocks.size(); // m_lastHeight = startHeight + blockchain.size() - 1; + lock.unlock(); callback(std::error_code()); } @@ -125,7 +132,7 @@ void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const crypto::ha ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_multiWalletLock); - cryptonote::Transaction tx; + CryptoNote::Transaction tx; if (m_blockchainGenerator.getTransactionByHash(transactionHash, tx)) { outsGlobalIndices.resize(tx.vout.size()); @@ -133,17 +140,18 @@ void INodeTrivialRefreshStub::doGetTransactionOutsGlobalIndices(const crypto::ha outsGlobalIndices.resize(20); //random } + lock.unlock(); callback(std::error_code()); } -void INodeTrivialRefreshStub::relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) +void INodeTrivialRefreshStub::relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task(&INodeTrivialRefreshStub::doRelayTransaction, this, transaction, callback); task.detach(); } -void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) +void INodeTrivialRefreshStub::doRelayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_multiWalletLock); @@ -151,35 +159,38 @@ void INodeTrivialRefreshStub::doRelayTransaction(const cryptonote::Transaction& if (m_nextTxError) { m_nextTxError = false; - callback(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR)); + lock.unlock(); + callback(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } if (m_nextTxToPool) { m_nextTxToPool = false; m_blockchainGenerator.putTxToPool(transaction); + lock.unlock(); callback(std::error_code()); return; } m_blockchainGenerator.addTxToBlockchain(transaction); + lock.unlock(); callback(std::error_code()); } -void INodeTrivialRefreshStub::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) +void INodeTrivialRefreshStub::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task(&INodeTrivialRefreshStub::doGetRandomOutsByAmounts, this, amounts, outsCount, std::ref(result), callback); task.detach(); } -void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback) +void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_multiWalletLock); for (uint64_t amount: amounts) { - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount out; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount out; out.amount = amount; for (uint64_t i = 0; i < outsCount; ++i) @@ -188,32 +199,35 @@ void INodeTrivialRefreshStub::doGetRandomOutsByAmounts(std::vector amo crypto::secret_key sk; generate_keys(key, sk); - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry e; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry e; e.global_amount_index = i; e.out_key = key; out.outs.push_back(e); } + + result.push_back(std::move(out)); } + lock.unlock(); callback(std::error_code()); } void INodeTrivialRefreshStub::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { - auto resultHolder = std::make_shared>(); + auto resultHolder = std::make_shared>(); getNewBlocks(std::move(knownBlockIds), *resultHolder, startHeight, [resultHolder, callback, &startHeight, &newBlocks](std::error_code ec) { - if (ec == std::error_code()) { + if (!ec) { for (const auto& item : *resultHolder) { CryptoNote::BlockCompleteEntry entry; - cryptonote::Block block; + CryptoNote::Block block; - cryptonote::parse_and_validate_block_from_blob(item.block, block); + CryptoNote::parse_and_validate_block_from_blob(item.block, block); - entry.blockHash = cryptonote::get_block_hash(block); + entry.blockHash = CryptoNote::get_block_hash(block); entry.block = item.block; entry.txs = std::move(item.txs); @@ -228,16 +242,7 @@ void INodeTrivialRefreshStub::queryBlocks(std::list&& knownBlockId void INodeTrivialRefreshStub::startAlternativeChain(uint64_t height) { - std::vector& blockchain = m_blockchainGenerator.getBlockchain(); - - assert(height < blockchain.size()); - //assert(height > m_lastHeight); - - auto it = blockchain.begin(); - std::advance(it, height); - - blockchain.erase(it, blockchain.end()); - + m_blockchainGenerator.cutBlockchain(height); m_lastHeight = height; } @@ -251,7 +256,7 @@ void INodeTrivialRefreshStub::setNextTransactionToPool() { } void INodeTrivialRefreshStub::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { m_asyncCounter.addAsyncContext(); std::thread task( @@ -268,12 +273,13 @@ void INodeTrivialRefreshStub::getPoolSymmetricDifference(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { ContextCounterHolder counterHolder(m_asyncCounter); std::unique_lock lock(m_multiWalletLock); m_blockchainGenerator.getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids); + lock.unlock(); callback(std::error_code()); } diff --git a/tests/unit_tests/INodeStubs.h b/tests/unit_tests/INodeStubs.h index ed688fd146..03ed9cec8c 100644 --- a/tests/unit_tests/INodeStubs.h +++ b/tests/unit_tests/INodeStubs.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,7 +23,7 @@ #include "INode.h" #include "cryptonote_core/cryptonote_basic.h" #include "TestBlockchainGenerator.h" -#include "common/ObserverManager.h" +#include "Common/ObserverManager.h" #include "wallet/WalletAsyncContextCounter.h" @@ -39,14 +39,16 @@ class INodeDummyStub : public CryptoNote::INode virtual size_t getPeerCount() const { return 0; }; virtual uint64_t getLastLocalBlockHeight() const { return 0; }; virtual uint64_t getLastKnownBlockHeight() const { return 0; }; + virtual uint64_t getLocalBlockCount() const override { return 0; }; + virtual uint64_t getKnownBlockCount() const override { return 0; }; virtual uint64_t getLastLocalBlockTimestamp() const override { return 0; } - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) {callback(std::error_code());}; + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) {callback(std::error_code());}; - virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) {callback(std::error_code());}; - virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) {callback(std::error_code());}; + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) {callback(std::error_code());}; + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) {callback(std::error_code());}; virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); }; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { is_bc_actual = true; callback(std::error_code()); }; + virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { is_bc_actual = true; callback(std::error_code()); }; virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { callback(std::error_code()); }; void updateObservers(); @@ -66,14 +68,14 @@ class INodeTrivialRefreshStub : public INodeDummyStub virtual uint64_t getLastLocalBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; }; virtual uint64_t getLastKnownBlockHeight() const { return m_blockchainGenerator.getBlockchain().size() - 1; }; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); - virtual void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); - virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; virtual void startAlternativeChain(uint64_t height); void setNextTransactionError(); @@ -84,13 +86,14 @@ class INodeTrivialRefreshStub : public INodeDummyStub virtual ~INodeTrivialRefreshStub(); -private: - void doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); +protected: + void doGetNewBlocks(std::list knownBlockIds, std::list& newBlocks, + uint64_t& startHeight, std::vector blockchain, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); - void doRelayTransaction(const cryptonote::Transaction& transaction, const Callback& callback); - void doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + void doRelayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); + void doGetRandomOutsByAmounts(std::vector amounts, uint64_t outsCount, std::vector& result, const Callback& callback); void doGetPoolSymmetricDifference(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback); + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback); size_t m_getMaxBlocks; uint64_t m_lastHeight; diff --git a/tests/unit_tests/StringBufferTests.cpp b/tests/unit_tests/StringBufferTests.cpp new file mode 100755 index 0000000000..14d1228b7d --- /dev/null +++ b/tests/unit_tests/StringBufferTests.cpp @@ -0,0 +1,441 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include + +using namespace Common; + +TEST(StringBufferTests, defaultConstructor) { + const StringBuffer<16> buffer; + static_assert(buffer.MAXIMUM_SIZE == 16, "Wrong MAXIMUM_SIZE"); + ASSERT_LE(static_cast(&buffer), static_cast(buffer.getData())); + ASSERT_GE(static_cast(&buffer + 1), static_cast(buffer.getData() + 16)); + ASSERT_EQ(0, buffer.getSize()); +} + +TEST(StringBufferTests, directConstructor) { + const StringView view("ABCD"); + const StringBuffer<16> buffer(view.getData(), 4); + ASSERT_LE(static_cast(&buffer), static_cast(buffer.getData())); + ASSERT_GE(static_cast(&buffer + 1), static_cast(buffer.getData() + 16)); + ASSERT_EQ(0, memcmp(buffer.getData(), view.getData(), 4)); + ASSERT_EQ(4, buffer.getSize()); +} + +TEST(StringBufferTests, arrayConstructor) { + const char data[] = "ABCD"; + const StringBuffer<16> buffer(data); + ASSERT_LE(static_cast(&buffer), static_cast(buffer.getData())); + ASSERT_GE(static_cast(&buffer + 1), static_cast(buffer.getData() + 16)); + ASSERT_EQ(0, memcmp(buffer.getData(), data, 4)); + ASSERT_EQ(4, buffer.getSize()); +} + +TEST(StringBufferTests, viewConstructor) { + const StringView view("ABCD"); + const StringBuffer<16> buffer(view); + ASSERT_LE(static_cast(&buffer), static_cast(buffer.getData())); + ASSERT_GE(static_cast(&buffer + 1), static_cast(buffer.getData() + 16)); + ASSERT_EQ(0, memcmp(buffer.getData(), view.getData(), 4)); + ASSERT_EQ(4, buffer.getSize()); +} + +TEST(StringBufferTests, copyConstructor) { + const StringBuffer<16> buffer1("ABCD"); + const StringBuffer<16> buffer2 = buffer1; + ASSERT_LE(static_cast(&buffer2), static_cast(buffer2.getData())); + ASSERT_GE(static_cast(&buffer2 + 1), static_cast(buffer2.getData() + 16)); + ASSERT_EQ(0, memcmp(buffer2.getData(), buffer1.getData(), 4)); + ASSERT_EQ(4, buffer2.getSize()); +} + +TEST(StringBufferTests, copyAssignment) { + const StringBuffer<16> buffer1("ABCD"); + StringBuffer<16> buffer2; + buffer2 = buffer1; + ASSERT_LE(static_cast(&buffer2), static_cast(buffer2.getData())); + ASSERT_GE(static_cast(&buffer2 + 1), static_cast(buffer2.getData() + 16)); + ASSERT_EQ(0, memcmp(buffer2.getData(), buffer1.getData(), 4)); + ASSERT_EQ(4, buffer2.getSize()); +} + +TEST(StringBufferTests, viewAssignment) { + const StringView view("ABCD"); + StringBuffer<16> buffer; + buffer = view; + ASSERT_LE(static_cast(&buffer), static_cast(buffer.getData())); + ASSERT_GE(static_cast(&buffer + 1), static_cast(buffer.getData() + 16)); + ASSERT_EQ(0, memcmp(buffer.getData(), view.getData(), 4)); + ASSERT_EQ(4, view.getSize()); +} + +TEST(StringBufferTests, view) { + const StringBuffer<16> buffer("ABCD"); + const StringView view = buffer; + ASSERT_EQ(buffer.getData(), view.getData()); + ASSERT_EQ(4, view.getSize()); +} + +TEST(StringBufferTests, getData) { + StringBuffer<16> buffer1("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + const StringBuffer<16> buffer2("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); +} + +TEST(StringBufferTests, empty) { + const StringBuffer<16> buffer; + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_TRUE(buffer.isEmpty()); + ASSERT_FALSE(StringBuffer<16>("ABCD").isEmpty()); +} + +TEST(StringBufferTests, squareBrackets) { + StringBuffer<16> buffer1("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_EQ(buffer1.getData(), &buffer1[0]); + ASSERT_EQ(buffer1.getData() + 3, &buffer1[3]); + const StringBuffer<16> buffer2("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_EQ(buffer2.getData(), &buffer2[0]); + ASSERT_EQ(buffer2.getData() + 3, &buffer2[3]); +} + +TEST(StringBufferTests, firstLast) { + StringBuffer<16> buffer1("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_EQ(buffer1.getData(), &buffer1.first()); + ASSERT_EQ(buffer1.getData() + 3, &buffer1.last()); + const StringBuffer<16> buffer2("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_EQ(buffer2.getData(), &buffer2.first()); + ASSERT_EQ(buffer2.getData() + 3, &buffer2.last()); +} + +TEST(StringBufferTests, beginEnd) { + StringBuffer<16> buffer1("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_EQ(buffer1.getData(), buffer1.begin()); + ASSERT_EQ(buffer1.getData() + 4, buffer1.end()); + const StringBuffer<16> buffer2("ABCD"); + static_assert(std::is_same::value, "Wrong operation result type"); + static_assert(std::is_same::value, "Wrong operation result type"); + ASSERT_EQ(buffer2.getData(), buffer2.begin()); + ASSERT_EQ(buffer2.getData() + 4, buffer2.end()); +} + +TEST(StringBufferTests, comparisons) { + const StringView view1("ABC"); + const StringView view2("ABCD"); + const StringView view3("ABCD"); + const StringView view4("ABCDE"); + const StringView view5("FGHI"); + ASSERT_FALSE(StringBuffer<16>(view2) == StringView::EMPTY); + ASSERT_FALSE(StringBuffer<16>(view2) == StringView::NIL); + ASSERT_FALSE(StringBuffer<16>(view2) == view1); + ASSERT_TRUE(StringBuffer<16>(view2) == view2); + ASSERT_TRUE(StringBuffer<16>(view2) == view3); + ASSERT_FALSE(StringBuffer<16>(view2) == view4); + ASSERT_FALSE(StringBuffer<16>(view2) == view5); + ASSERT_TRUE(StringBuffer<16>(view2) != StringView::EMPTY); + ASSERT_TRUE(StringBuffer<16>(view2) != StringView::NIL); + ASSERT_TRUE(StringBuffer<16>(view2) != view1); + ASSERT_FALSE(StringBuffer<16>(view2) != view2); + ASSERT_FALSE(StringBuffer<16>(view2) != view3); + ASSERT_TRUE(StringBuffer<16>(view2) != view4); + ASSERT_TRUE(StringBuffer<16>(view2) != view5); + ASSERT_FALSE(StringBuffer<16>(view2) < StringView::EMPTY); + ASSERT_FALSE(StringBuffer<16>(view2) < StringView::NIL); + ASSERT_FALSE(StringBuffer<16>(view2) < view1); + ASSERT_FALSE(StringBuffer<16>(view2) < view2); + ASSERT_FALSE(StringBuffer<16>(view2) < view3); + ASSERT_TRUE(StringBuffer<16>(view2) < view4); + ASSERT_TRUE(StringBuffer<16>(view2) < view5); + ASSERT_FALSE(StringBuffer<16>(view2) <= StringView::EMPTY); + ASSERT_FALSE(StringBuffer<16>(view2) <= StringView::NIL); + ASSERT_FALSE(StringBuffer<16>(view2) <= view1); + ASSERT_TRUE(StringBuffer<16>(view2) <= view2); + ASSERT_TRUE(StringBuffer<16>(view2) <= view3); + ASSERT_TRUE(StringBuffer<16>(view2) <= view4); + ASSERT_TRUE(StringBuffer<16>(view2) <= view5); + ASSERT_TRUE(StringBuffer<16>(view2) > StringView::EMPTY); + ASSERT_TRUE(StringBuffer<16>(view2) > StringView::NIL); + ASSERT_TRUE(StringBuffer<16>(view2) > view1); + ASSERT_FALSE(StringBuffer<16>(view2) > view2); + ASSERT_FALSE(StringBuffer<16>(view2) > view3); + ASSERT_FALSE(StringBuffer<16>(view2) > view4); + ASSERT_FALSE(StringBuffer<16>(view2) > view5); + ASSERT_TRUE(StringBuffer<16>(view2) >= StringView::EMPTY); + ASSERT_TRUE(StringBuffer<16>(view2) >= StringView::NIL); + ASSERT_TRUE(StringBuffer<16>(view2) >= view1); + ASSERT_TRUE(StringBuffer<16>(view2) >= view2); + ASSERT_TRUE(StringBuffer<16>(view2) >= view3); + ASSERT_FALSE(StringBuffer<16>(view2) >= view4); + ASSERT_FALSE(StringBuffer<16>(view2) >= view5); +} + +TEST(StringBufferTests, beginsWith) { + const StringView view1("ABC"); + const StringView view2("ABCD"); + const StringView view3("ABCD"); + const StringView view4("ABCDE"); + const StringView view5("FGHI"); + ASSERT_TRUE(StringBuffer<16>(view2).beginsWith(view1[0])); + ASSERT_FALSE(StringBuffer<16>(view2).beginsWith(view5[0])); + ASSERT_TRUE(StringBuffer<16>(view2).beginsWith(StringView::EMPTY)); + ASSERT_TRUE(StringBuffer<16>(view2).beginsWith(StringView::NIL)); + ASSERT_TRUE(StringBuffer<16>(view2).beginsWith(view1)); + ASSERT_TRUE(StringBuffer<16>(view2).beginsWith(view2)); + ASSERT_TRUE(StringBuffer<16>(view2).beginsWith(view3)); + ASSERT_FALSE(StringBuffer<16>(view2).beginsWith(view4)); + ASSERT_FALSE(StringBuffer<16>(view2).beginsWith(view5)); +} + +TEST(StringBufferTests, contains) { + const StringView view1("BC"); + const StringView view2("ABCD"); + const StringView view3("ABCD"); + const StringView view4("ABCDE"); + const StringView view5("FGHI"); + ASSERT_TRUE(StringBuffer<16>(view2).contains(view1[1])); + ASSERT_FALSE(StringBuffer<16>(view2).contains(view5[1])); + ASSERT_TRUE(StringBuffer<16>(view2).contains(StringView::EMPTY)); + ASSERT_TRUE(StringBuffer<16>(view2).contains(StringView::NIL)); + ASSERT_TRUE(StringBuffer<16>(view2).contains(view1)); + ASSERT_TRUE(StringBuffer<16>(view2).contains(view2)); + ASSERT_TRUE(StringBuffer<16>(view2).contains(view3)); + ASSERT_FALSE(StringBuffer<16>(view2).contains(view4)); + ASSERT_FALSE(StringBuffer<16>(view2).contains(view5)); +} + +TEST(StringBufferTests, endsWith) { + const StringView view1("BCD"); + const StringView view2("ABCD"); + const StringView view3("ABCD"); + const StringView view4("ABCDE"); + const StringView view5("FGHI"); + ASSERT_TRUE(StringBuffer<16>(view2).endsWith(view1[2])); + ASSERT_FALSE(StringBuffer<16>(view2).endsWith(view5[3])); + ASSERT_TRUE(StringBuffer<16>(view2).endsWith(StringView::EMPTY)); + ASSERT_TRUE(StringBuffer<16>(view2).endsWith(StringView::NIL)); + ASSERT_TRUE(StringBuffer<16>(view2).endsWith(view1)); + ASSERT_TRUE(StringBuffer<16>(view2).endsWith(view2)); + ASSERT_TRUE(StringBuffer<16>(view2).endsWith(view3)); + ASSERT_FALSE(StringBuffer<16>(view2).endsWith(view4)); + ASSERT_FALSE(StringBuffer<16>(view2).endsWith(view5)); +} + +TEST(StringBufferTests, find) { + const StringView view1("BC"); + const StringView view2("ABCBCD"); + const StringView view3("ABCBCD"); + const StringView view4("ABCBCDE"); + const StringView view5("FGHI"); + ASSERT_EQ(1, StringBuffer<16>(view2).find(view1[0])); + ASSERT_EQ(StringBuffer<16>::INVALID, StringBuffer<16>(view2).find(view5[1])); + ASSERT_EQ(0, StringBuffer<16>(view2).find(StringView::EMPTY)); + ASSERT_EQ(0, StringBuffer<16>(view2).find(StringView::NIL)); + ASSERT_EQ(1, StringBuffer<16>(view2).find(view1)); + ASSERT_EQ(0, StringBuffer<16>(view2).find(view2)); + ASSERT_EQ(0, StringBuffer<16>(view2).find(view3)); + ASSERT_EQ(StringBuffer<16>::INVALID, StringBuffer<16>(view2).find(view4)); + ASSERT_EQ(StringBuffer<16>::INVALID, StringBuffer<16>(view2).find(view5)); +} + +TEST(StringBufferTests, findLast) { + const StringView view1("BC"); + const StringView view2("ABCBCD"); + const StringView view3("ABCBCD"); + const StringView view4("ABCBCDE"); + const StringView view5("FGHI"); + ASSERT_EQ(3, StringBuffer<16>(view2).findLast(view1[0])); + ASSERT_EQ(StringBuffer<16>::INVALID, StringBuffer<16>(view2).findLast(view5[1])); + ASSERT_EQ(6, StringBuffer<16>(view2).findLast(StringView::EMPTY)); + ASSERT_EQ(6, StringBuffer<16>(view2).findLast(StringView::NIL)); + ASSERT_EQ(3, StringBuffer<16>(view2).findLast(view1)); + ASSERT_EQ(0, StringBuffer<16>(view2).findLast(view2)); + ASSERT_EQ(0, StringBuffer<16>(view2).findLast(view3)); + ASSERT_EQ(StringBuffer<16>::INVALID, StringBuffer<16>(view2).findLast(view4)); + ASSERT_EQ(StringBuffer<16>::INVALID, StringBuffer<16>(view2).findLast(view5)); +} + +TEST(StringBufferTests, head) { + const StringBuffer<16> buffer("ABCD"); + ASSERT_EQ(StringView(buffer.getData(), 0), buffer.head(0)); + ASSERT_EQ(StringView(buffer.getData(), 2), buffer.head(2)); + ASSERT_EQ(StringView(buffer.getData(), 4), buffer.head(4)); +} + +TEST(StringBufferTests, tail) { + const StringBuffer<16> buffer("ABCD"); + ASSERT_EQ(StringView(buffer.getData() + 4, 0), buffer.tail(0)); + ASSERT_EQ(StringView(buffer.getData() + 2, 2), buffer.tail(2)); + ASSERT_EQ(StringView(buffer.getData(), 4), buffer.tail(4)); +} + +TEST(StringBufferTests, unhead) { + const StringBuffer<16> buffer("ABCD"); + ASSERT_EQ(StringView(buffer.getData(), 4), buffer.unhead(0)); + ASSERT_EQ(StringView(buffer.getData() + 2, 2), buffer.unhead(2)); + ASSERT_EQ(StringView(buffer.getData() + 4, 0), buffer.unhead(4)); +} + +TEST(StringBufferTests, untail) { + const StringBuffer<16> buffer("ABCD"); + ASSERT_EQ(StringView(buffer.getData(), 4), buffer.untail(0)); + ASSERT_EQ(StringView(buffer.getData(), 2), buffer.untail(2)); + ASSERT_EQ(StringView(buffer.getData(), 0), buffer.untail(4)); +} + +TEST(StringBufferTests, range) { + const StringBuffer<16> buffer("ABCD"); + ASSERT_EQ(StringView(buffer.getData() + 0, 0), buffer.range(0, 0)); + ASSERT_EQ(StringView(buffer.getData() + 0, 2), buffer.range(0, 2)); + ASSERT_EQ(StringView(buffer.getData() + 0, 4), buffer.range(0, 4)); + ASSERT_EQ(StringView(buffer.getData() + 2, 0), buffer.range(2, 2)); + ASSERT_EQ(StringView(buffer.getData() + 2, 2), buffer.range(2, 4)); + ASSERT_EQ(StringView(buffer.getData() + 4, 0), buffer.range(4, 4)); +} + +TEST(StringBufferTests, slice) { + const StringBuffer<16> buffer("ABCD"); + ASSERT_EQ(StringView(buffer.getData() + 0, 0), buffer.slice(0, 0)); + ASSERT_EQ(StringView(buffer.getData() + 0, 2), buffer.slice(0, 2)); + ASSERT_EQ(StringView(buffer.getData() + 0, 4), buffer.slice(0, 4)); + ASSERT_EQ(StringView(buffer.getData() + 2, 0), buffer.slice(2, 0)); + ASSERT_EQ(StringView(buffer.getData() + 2, 2), buffer.slice(2, 2)); + ASSERT_EQ(StringView(buffer.getData() + 4, 0), buffer.slice(4, 0)); +} + +TEST(StringBufferTests, append) { + ASSERT_EQ(StringView("E"), StringBuffer<16>("").append('E')); + ASSERT_EQ(StringView(""), StringBuffer<16>("").append("")); + ASSERT_EQ(StringView("EF"), StringBuffer<16>("").append("EF")); + ASSERT_EQ(StringView("EFGH"), StringBuffer<16>("").append("EFGH")); + ASSERT_EQ(StringView("ABE"), StringBuffer<16>("AB").append('E')); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").append("")); + ASSERT_EQ(StringView("ABEF"), StringBuffer<16>("AB").append("EF")); + ASSERT_EQ(StringView("ABEFGH"), StringBuffer<16>("AB").append("EFGH")); + ASSERT_EQ(StringView("ABCDE"), StringBuffer<16>("ABCD").append('E')); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").append("")); + ASSERT_EQ(StringView("ABCDEF"), StringBuffer<16>("ABCD").append("EF")); + ASSERT_EQ(StringView("ABCDEFGH"), StringBuffer<16>("ABCD").append("EFGH")); +} + +TEST(StringBufferTests, clear) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").clear()); + ASSERT_EQ(StringView(""), StringBuffer<16>("AB").clear()); + ASSERT_EQ(StringView(""), StringBuffer<16>("ABCD").clear()); +} + +TEST(StringBufferTests, cut) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").cut(0, 0)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").cut(0, 0)); + ASSERT_EQ(StringView(""), StringBuffer<16>("AB").cut(0, 2)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").cut(2, 0)); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").cut(0, 0)); + ASSERT_EQ(StringView("CD"), StringBuffer<16>("ABCD").cut(0, 2)); + ASSERT_EQ(StringView(""), StringBuffer<16>("ABCD").cut(0, 4)); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").cut(2, 0)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("ABCD").cut(2, 2)); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").cut(4, 0)); +} + +TEST(StringBufferTests, fill) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").fill('E')); + ASSERT_EQ(StringView("EE"), StringBuffer<16>("AB").fill('E')); + ASSERT_EQ(StringView("EEEE"), StringBuffer<16>("ABCD").fill('E')); +} + +TEST(StringBufferTests, insert) { + ASSERT_EQ(StringView("E"), StringBuffer<16>("").insert(0, 'E')); + ASSERT_EQ(StringView(""), StringBuffer<16>("").insert(0, "")); + ASSERT_EQ(StringView("EF"), StringBuffer<16>("").insert(0, "EF")); + ASSERT_EQ(StringView("EFGH"), StringBuffer<16>("").insert(0, "EFGH")); + ASSERT_EQ(StringView("EAB"), StringBuffer<16>("AB").insert(0, 'E')); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").insert(0, "")); + ASSERT_EQ(StringView("EFAB"), StringBuffer<16>("AB").insert(0, "EF")); + ASSERT_EQ(StringView("EFGHAB"), StringBuffer<16>("AB").insert(0, "EFGH")); + ASSERT_EQ(StringView("ABE"), StringBuffer<16>("AB").insert(2, 'E')); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").insert(2, "")); + ASSERT_EQ(StringView("ABEF"), StringBuffer<16>("AB").insert(2, "EF")); + ASSERT_EQ(StringView("ABEFGH"), StringBuffer<16>("AB").insert(2, "EFGH")); + ASSERT_EQ(StringView("EABCD"), StringBuffer<16>("ABCD").insert(0, 'E')); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").insert(0, "")); + ASSERT_EQ(StringView("EFABCD"), StringBuffer<16>("ABCD").insert(0, "EF")); + ASSERT_EQ(StringView("EFGHABCD"), StringBuffer<16>("ABCD").insert(0, "EFGH")); + ASSERT_EQ(StringView("ABECD"), StringBuffer<16>("ABCD").insert(2, 'E')); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").insert(2, "")); + ASSERT_EQ(StringView("ABEFCD"), StringBuffer<16>("ABCD").insert(2, "EF")); + ASSERT_EQ(StringView("ABEFGHCD"), StringBuffer<16>("ABCD").insert(2, "EFGH")); + ASSERT_EQ(StringView("ABCDE"), StringBuffer<16>("ABCD").insert(4, 'E')); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").insert(4, "")); + ASSERT_EQ(StringView("ABCDEF"), StringBuffer<16>("ABCD").insert(4, "EF")); + ASSERT_EQ(StringView("ABCDEFGH"), StringBuffer<16>("ABCD").insert(4, "EFGH")); +} + +TEST(StringBufferTests, overwrite) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").overwrite(0, "")); + ASSERT_EQ(StringView("EF"), StringBuffer<16>("").overwrite(0, "EF")); + ASSERT_EQ(StringView("EFGH"), StringBuffer<16>("").overwrite(0, "EFGH")); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").overwrite(0, "")); + ASSERT_EQ(StringView("EF"), StringBuffer<16>("AB").overwrite(0, "EF")); + ASSERT_EQ(StringView("EFGH"), StringBuffer<16>("AB").overwrite(0, "EFGH")); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").overwrite(2, "")); + ASSERT_EQ(StringView("ABEF"), StringBuffer<16>("AB").overwrite(2, "EF")); + ASSERT_EQ(StringView("ABEFGH"), StringBuffer<16>("AB").overwrite(2, "EFGH")); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").overwrite(0, "")); + ASSERT_EQ(StringView("EFCD"), StringBuffer<16>("ABCD").overwrite(0, "EF")); + ASSERT_EQ(StringView("EFGH"), StringBuffer<16>("ABCD").overwrite(0, "EFGH")); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").overwrite(2, "")); + ASSERT_EQ(StringView("ABEF"), StringBuffer<16>("ABCD").overwrite(2, "EF")); + ASSERT_EQ(StringView("ABEFGH"), StringBuffer<16>("ABCD").overwrite(2, "EFGH")); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").overwrite(4, "")); + ASSERT_EQ(StringView("ABCDEF"), StringBuffer<16>("ABCD").overwrite(4, "EF")); + ASSERT_EQ(StringView("ABCDEFGH"), StringBuffer<16>("ABCD").overwrite(4, "EFGH")); +} + +TEST(StringBufferTests, resize) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").resize(0)); + ASSERT_EQ(StringView("\0\0"), StringBuffer<16>("").resize(2)); + ASSERT_EQ(StringView("\0\0\0\0"), StringBuffer<16>("").resize(4)); + ASSERT_EQ(StringView(""), StringBuffer<16>("AB").resize(0)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").resize(2)); + ASSERT_EQ(StringView("AB\0\0"), StringBuffer<16>("AB").resize(4)); + ASSERT_EQ(StringView(""), StringBuffer<16>("ABCD").resize(0)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("ABCD").resize(2)); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").resize(4)); +} + +TEST(StringBufferTests, reverse) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").reverse()); + ASSERT_EQ(StringView("BA"), StringBuffer<16>("AB").reverse()); + ASSERT_EQ(StringView("DCBA"), StringBuffer<16>("ABCD").reverse()); +} + +TEST(StringBufferTests, shrink) { + ASSERT_EQ(StringView(""), StringBuffer<16>("").shrink(0)); + ASSERT_EQ(StringView(""), StringBuffer<16>("AB").shrink(0)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("AB").shrink(2)); + ASSERT_EQ(StringView(""), StringBuffer<16>("ABCD").shrink(0)); + ASSERT_EQ(StringView("AB"), StringBuffer<16>("ABCD").shrink(2)); + ASSERT_EQ(StringView("ABCD"), StringBuffer<16>("ABCD").shrink(4)); +} diff --git a/tests/unit_tests/StringViewTests.cpp b/tests/unit_tests/StringViewTests.cpp new file mode 100755 index 0000000000..fdcb6d49e1 --- /dev/null +++ b/tests/unit_tests/StringViewTests.cpp @@ -0,0 +1,399 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include + +using namespace Common; + +TEST(StringViewTests, representations) { + ASSERT_NE(nullptr, StringView::EMPTY.getData()); + ASSERT_EQ(0, StringView::EMPTY.getSize()); + ASSERT_EQ(nullptr, StringView::NIL.getData()); + ASSERT_EQ(0, StringView::NIL.getSize()); +} + +TEST(StringViewTests, directConstructor) { + const char data[] = "ABCD"; + ASSERT_EQ(data, StringView(data, 4).getData()); + ASSERT_EQ(4, StringView(data, 4).getSize()); +} + +TEST(StringViewTests, arrayConstructor) { + const char data[] = "ABCD"; + const StringView view = data; + ASSERT_EQ(data, view.getData()); + ASSERT_EQ(4, view.getSize()); +} + +TEST(StringViewTests, stdStringConstructor) { + std::string string("ABCD"); + const StringView view = string; + ASSERT_EQ(string.data(), view.getData()); + ASSERT_EQ(string.size(), view.getSize()); +} + +TEST(StringViewTests, copyConstructor) { + const char data[] = "ABCD"; + const StringView view(data); + ASSERT_EQ(view.getData(), StringView(view).getData()); + ASSERT_EQ(view.getSize(), StringView(view).getSize()); +} + +TEST(StringViewTests, copyAssignment) { + const char data[] = "ABCD"; + const StringView view1(data); + StringView view2; + view2 = view1; + ASSERT_EQ(view1.getData(), view2.getData()); + ASSERT_EQ(view1.getSize(), view2.getSize()); +} + +TEST(ArrayRefTests, stdString) { + const char data[] = "ABCD"; + const StringView view(data); + std::string string(view); + ASSERT_EQ(*view.getData(), *string.data()); + ASSERT_EQ(*(view.getData() + 1), *(string.data() + 1)); + ASSERT_EQ(*(view.getData() + 2), *(string.data() + 2)); + ASSERT_EQ(*(view.getData() + 3), *(string.data() + 3)); + ASSERT_EQ(view.getSize(), string.size()); +} + +TEST(StringViewTests, emptyNil) { + ASSERT_TRUE(StringView::EMPTY.isEmpty()); + ASSERT_FALSE(StringView::EMPTY.isNil()); + ASSERT_TRUE(StringView::NIL.isEmpty()); + ASSERT_TRUE(StringView::NIL.isNil()); + const char data[] = "ABCD"; + ASSERT_TRUE(StringView(data, 0).isEmpty()); + ASSERT_FALSE(StringView(data, 0).isNil()); + ASSERT_FALSE(StringView(data).isEmpty()); + ASSERT_FALSE(StringView(data).isNil()); +} + +TEST(StringViewTests, squareBrackets) { + const char data[] = "ABCD"; + const StringView view(data); + ASSERT_EQ(data + 0, &view[0]); + ASSERT_EQ(data + 1, &view[1]); + ASSERT_EQ(data + 2, &view[2]); + ASSERT_EQ(data + 3, &view[3]); +} + +TEST(StringViewTests, firstLast) { + const char data[] = "ABCD"; + const StringView view(data); + ASSERT_EQ(data + 0, &view.first()); + ASSERT_EQ(data + 3, &view.last()); +} + +TEST(StringViewTests, beginEnd) { + const char data[] = "ABCD"; + ASSERT_EQ(nullptr, StringView::NIL.begin()); + ASSERT_EQ(nullptr, StringView::NIL.end()); + ASSERT_EQ(data, StringView(data).begin()); + ASSERT_EQ(data + 4, StringView(data).end()); + std::size_t offset = 0; + for (const char& value : StringView(data)) { + ASSERT_EQ(data[offset], value); + ++offset; + } +} + +TEST(StringViewTests, comparisons) { + const char data1[] = "ABC"; + const char data2[] = "ABCD"; + const char data3[] = "ABCD"; + const char data4[] = "ABCDE"; + const char data5[] = "FGHI"; + ASSERT_TRUE(StringView::EMPTY == StringView::EMPTY); + ASSERT_TRUE(StringView::EMPTY == StringView::NIL); + ASSERT_FALSE(StringView::EMPTY == StringView(data1)); + ASSERT_TRUE(StringView::NIL == StringView::EMPTY); + ASSERT_TRUE(StringView::NIL == StringView::NIL); + ASSERT_FALSE(StringView::NIL == StringView(data1)); + ASSERT_FALSE(StringView(data2) == StringView::EMPTY); + ASSERT_FALSE(StringView(data2) == StringView::NIL); + ASSERT_FALSE(StringView(data2) == StringView(data1)); + ASSERT_TRUE(StringView(data2) == StringView(data2)); + ASSERT_TRUE(StringView(data2) == StringView(data3)); + ASSERT_FALSE(StringView(data2) == StringView(data4)); + ASSERT_FALSE(StringView(data2) == StringView(data5)); + ASSERT_FALSE(StringView::EMPTY != StringView::EMPTY); + ASSERT_FALSE(StringView::EMPTY != StringView::NIL); + ASSERT_TRUE(StringView::EMPTY != StringView(data1)); + ASSERT_FALSE(StringView::NIL != StringView::EMPTY); + ASSERT_FALSE(StringView::NIL != StringView::NIL); + ASSERT_TRUE(StringView::NIL != StringView(data1)); + ASSERT_TRUE(StringView(data2) != StringView::EMPTY); + ASSERT_TRUE(StringView(data2) != StringView::NIL); + ASSERT_TRUE(StringView(data2) != StringView(data1)); + ASSERT_FALSE(StringView(data2) != StringView(data2)); + ASSERT_FALSE(StringView(data2) != StringView(data3)); + ASSERT_TRUE(StringView(data2) != StringView(data4)); + ASSERT_TRUE(StringView(data2) != StringView(data5)); + ASSERT_FALSE(StringView::EMPTY < StringView::EMPTY); + ASSERT_FALSE(StringView::EMPTY < StringView::NIL); + ASSERT_TRUE(StringView::EMPTY < StringView(data1)); + ASSERT_FALSE(StringView::NIL < StringView::EMPTY); + ASSERT_FALSE(StringView::NIL < StringView::NIL); + ASSERT_TRUE(StringView::NIL < StringView(data1)); + ASSERT_FALSE(StringView(data2) < StringView::EMPTY); + ASSERT_FALSE(StringView(data2) < StringView::NIL); + ASSERT_FALSE(StringView(data2) < StringView(data1)); + ASSERT_FALSE(StringView(data2) < StringView(data2)); + ASSERT_FALSE(StringView(data2) < StringView(data3)); + ASSERT_TRUE(StringView(data2) < StringView(data4)); + ASSERT_TRUE(StringView(data2) < StringView(data5)); + ASSERT_TRUE(StringView::EMPTY <= StringView::EMPTY); + ASSERT_TRUE(StringView::EMPTY <= StringView::NIL); + ASSERT_TRUE(StringView::EMPTY <= StringView(data1)); + ASSERT_TRUE(StringView::NIL <= StringView::EMPTY); + ASSERT_TRUE(StringView::NIL <= StringView::NIL); + ASSERT_TRUE(StringView::NIL <= StringView(data1)); + ASSERT_FALSE(StringView(data2) <= StringView::EMPTY); + ASSERT_FALSE(StringView(data2) <= StringView::NIL); + ASSERT_FALSE(StringView(data2) <= StringView(data1)); + ASSERT_TRUE(StringView(data2) <= StringView(data2)); + ASSERT_TRUE(StringView(data2) <= StringView(data3)); + ASSERT_TRUE(StringView(data2) <= StringView(data4)); + ASSERT_TRUE(StringView(data2) <= StringView(data5)); + ASSERT_FALSE(StringView::EMPTY > StringView::EMPTY); + ASSERT_FALSE(StringView::EMPTY > StringView::NIL); + ASSERT_FALSE(StringView::EMPTY > StringView(data1)); + ASSERT_FALSE(StringView::NIL > StringView::EMPTY); + ASSERT_FALSE(StringView::NIL > StringView::NIL); + ASSERT_FALSE(StringView::NIL > StringView(data1)); + ASSERT_TRUE(StringView(data2) > StringView::EMPTY); + ASSERT_TRUE(StringView(data2) > StringView::NIL); + ASSERT_TRUE(StringView(data2) > StringView(data1)); + ASSERT_FALSE(StringView(data2) > StringView(data2)); + ASSERT_FALSE(StringView(data2) > StringView(data3)); + ASSERT_FALSE(StringView(data2) > StringView(data4)); + ASSERT_FALSE(StringView(data2) > StringView(data5)); + ASSERT_TRUE(StringView::EMPTY >= StringView::EMPTY); + ASSERT_TRUE(StringView::EMPTY >= StringView::NIL); + ASSERT_FALSE(StringView::EMPTY >= StringView(data1)); + ASSERT_TRUE(StringView::NIL >= StringView::EMPTY); + ASSERT_TRUE(StringView::NIL >= StringView::NIL); + ASSERT_FALSE(StringView::NIL >= StringView(data1)); + ASSERT_TRUE(StringView(data2) >= StringView::EMPTY); + ASSERT_TRUE(StringView(data2) >= StringView::NIL); + ASSERT_TRUE(StringView(data2) >= StringView(data1)); + ASSERT_TRUE(StringView(data2) >= StringView(data2)); + ASSERT_TRUE(StringView(data2) >= StringView(data3)); + ASSERT_FALSE(StringView(data2) >= StringView(data4)); + ASSERT_FALSE(StringView(data2) >= StringView(data5)); +} + +TEST(StringViewTests, beginsWith) { + const char data1[] = "ABC"; + const char data2[] = "ABCD"; + const char data3[] = "ABCD"; + const char data4[] = "ABCDE"; + const char data5[] = "FGHI"; + ASSERT_FALSE(StringView::EMPTY.beginsWith(data1[0])); + ASSERT_TRUE(StringView::EMPTY.beginsWith(StringView::EMPTY)); + ASSERT_TRUE(StringView::EMPTY.beginsWith(StringView::NIL)); + ASSERT_FALSE(StringView::EMPTY.beginsWith(StringView(data1))); + ASSERT_FALSE(StringView::NIL.beginsWith(data1[0])); + ASSERT_TRUE(StringView::NIL.beginsWith(StringView::EMPTY)); + ASSERT_TRUE(StringView::NIL.beginsWith(StringView::NIL)); + ASSERT_FALSE(StringView::NIL.beginsWith(StringView(data1))); + ASSERT_TRUE(StringView(data2).beginsWith(data1[0])); + ASSERT_FALSE(StringView(data2).beginsWith(data5[0])); + ASSERT_TRUE(StringView(data2).beginsWith(StringView::EMPTY)); + ASSERT_TRUE(StringView(data2).beginsWith(StringView::NIL)); + ASSERT_TRUE(StringView(data2).beginsWith(StringView(data1))); + ASSERT_TRUE(StringView(data2).beginsWith(StringView(data2))); + ASSERT_TRUE(StringView(data2).beginsWith(StringView(data3))); + ASSERT_FALSE(StringView(data2).beginsWith(StringView(data4))); + ASSERT_FALSE(StringView(data2).beginsWith(StringView(data5))); +} + +TEST(StringViewTests, contains) { + const char data1[] = "BC"; + const char data2[] = "ABCD"; + const char data3[] = "ABCD"; + const char data4[] = "ABCDE"; + const char data5[] = "FGHI"; + ASSERT_FALSE(StringView::EMPTY.contains(data1[1])); + ASSERT_TRUE(StringView::EMPTY.contains(StringView::EMPTY)); + ASSERT_TRUE(StringView::EMPTY.contains(StringView::NIL)); + ASSERT_FALSE(StringView::EMPTY.contains(StringView(data1))); + ASSERT_FALSE(StringView::NIL.contains(data1[1])); + ASSERT_TRUE(StringView::NIL.contains(StringView::EMPTY)); + ASSERT_TRUE(StringView::NIL.contains(StringView::NIL)); + ASSERT_FALSE(StringView::NIL.contains(StringView(data1))); + ASSERT_TRUE(StringView(data2).contains(data1[1])); + ASSERT_FALSE(StringView(data2).contains(data5[1])); + ASSERT_TRUE(StringView(data2).contains(StringView::EMPTY)); + ASSERT_TRUE(StringView(data2).contains(StringView::NIL)); + ASSERT_TRUE(StringView(data2).contains(StringView(data1))); + ASSERT_TRUE(StringView(data2).contains(StringView(data2))); + ASSERT_TRUE(StringView(data2).contains(StringView(data3))); + ASSERT_FALSE(StringView(data2).contains(StringView(data4))); + ASSERT_FALSE(StringView(data2).contains(StringView(data5))); +} + +TEST(StringViewTests, endsWith) { + const char data1[] = "BCD"; + const char data2[] = "ABCD"; + const char data3[] = "ABCD"; + const char data4[] = "ABCDE"; + const char data5[] = "FGHI"; + ASSERT_FALSE(StringView::EMPTY.endsWith(data1[2])); + ASSERT_TRUE(StringView::EMPTY.endsWith(StringView::EMPTY)); + ASSERT_TRUE(StringView::EMPTY.endsWith(StringView::NIL)); + ASSERT_FALSE(StringView::EMPTY.endsWith(StringView(data1))); + ASSERT_FALSE(StringView::NIL.endsWith(data1[2])); + ASSERT_TRUE(StringView::NIL.endsWith(StringView::EMPTY)); + ASSERT_TRUE(StringView::NIL.endsWith(StringView::NIL)); + ASSERT_FALSE(StringView::NIL.endsWith(StringView(data1))); + ASSERT_TRUE(StringView(data2).endsWith(data1[2])); + ASSERT_FALSE(StringView(data2).endsWith(data5[3])); + ASSERT_TRUE(StringView(data2).endsWith(StringView::EMPTY)); + ASSERT_TRUE(StringView(data2).endsWith(StringView::NIL)); + ASSERT_TRUE(StringView(data2).endsWith(StringView(data1))); + ASSERT_TRUE(StringView(data2).endsWith(StringView(data2))); + ASSERT_TRUE(StringView(data2).endsWith(StringView(data3))); + ASSERT_FALSE(StringView(data2).endsWith(StringView(data4))); + ASSERT_FALSE(StringView(data2).endsWith(StringView(data5))); +} + +TEST(StringViewTests, find) { + const char data1[] = "BC"; + const char data2[] = "ABCBCD"; + const char data3[] = "ABCBCD"; + const char data4[] = "ABCBCDE"; + const char data5[] = "FGHI"; + ASSERT_EQ(StringView::INVALID, StringView::EMPTY.find(data1[0])); + ASSERT_EQ(0, StringView::EMPTY.find(StringView::EMPTY)); + ASSERT_EQ(0, StringView::EMPTY.find(StringView::NIL)); + ASSERT_EQ(StringView::INVALID, StringView::EMPTY.find(StringView(data1))); + ASSERT_EQ(StringView::INVALID, StringView::NIL.find(data1[0])); + ASSERT_EQ(0, StringView::NIL.find(StringView::EMPTY)); + ASSERT_EQ(0, StringView::NIL.find(StringView::NIL)); + ASSERT_EQ(StringView::INVALID, StringView::NIL.find(StringView(data1))); + ASSERT_EQ(1, StringView(data2).find(data1[0])); + ASSERT_EQ(StringView::INVALID, StringView(data2).find(data5[1])); + ASSERT_EQ(0, StringView(data2).find(StringView::EMPTY)); + ASSERT_EQ(0, StringView(data2).find(StringView::NIL)); + ASSERT_EQ(1, StringView(data2).find(StringView(data1))); + ASSERT_EQ(0, StringView(data2).find(StringView(data2))); + ASSERT_EQ(0, StringView(data2).find(StringView(data3))); + ASSERT_EQ(StringView::INVALID, StringView(data2).find(StringView(data4))); + ASSERT_EQ(StringView::INVALID, StringView(data2).find(StringView(data5))); +} + +TEST(StringViewTests, findLast) { + const char data1[] = "BC"; + const char data2[] = "ABCBCD"; + const char data3[] = "ABCBCD"; + const char data4[] = "ABCBCDE"; + const char data5[] = "FGHI"; + ASSERT_EQ(StringView::INVALID, StringView::EMPTY.findLast(data1[0])); + ASSERT_EQ(0, StringView::EMPTY.findLast(StringView::EMPTY)); + ASSERT_EQ(0, StringView::EMPTY.findLast(StringView::NIL)); + ASSERT_EQ(StringView::INVALID, StringView::EMPTY.findLast(StringView(data1))); + ASSERT_EQ(StringView::INVALID, StringView::NIL.findLast(data1[0])); + ASSERT_EQ(0, StringView::NIL.findLast(StringView::EMPTY)); + ASSERT_EQ(0, StringView::NIL.findLast(StringView::NIL)); + ASSERT_EQ(StringView::INVALID, StringView::NIL.findLast(StringView(data1))); + ASSERT_EQ(3, StringView(data2).findLast(data1[0])); + ASSERT_EQ(StringView::INVALID, StringView(data2).findLast(data5[1])); + ASSERT_EQ(6, StringView(data2).findLast(StringView::EMPTY)); + ASSERT_EQ(6, StringView(data2).findLast(StringView::NIL)); + ASSERT_EQ(3, StringView(data2).findLast(StringView(data1))); + ASSERT_EQ(0, StringView(data2).findLast(StringView(data2))); + ASSERT_EQ(0, StringView(data2).findLast(StringView(data3))); + ASSERT_EQ(StringView::INVALID, StringView(data2).findLast(StringView(data4))); + ASSERT_EQ(StringView::INVALID, StringView(data2).findLast(StringView(data5))); +} + +TEST(StringViewTests, head) { + const char data[] = "ABCD"; + ASSERT_EQ(0, StringView::EMPTY.head(0).getSize()); + ASSERT_EQ(StringView(nullptr, 0), StringView::NIL.head(0)); + ASSERT_EQ(StringView(data, 0), StringView(data).head(0)); + ASSERT_EQ(StringView(data, 2), StringView(data).head(2)); + ASSERT_EQ(StringView(data, 4), StringView(data).head(4)); +} + +TEST(StringViewTests, tail) { + const char data[] = "ABCD"; + ASSERT_EQ(0, StringView::EMPTY.tail(0).getSize()); + ASSERT_EQ(StringView(nullptr, 0), StringView::NIL.tail(0)); + ASSERT_EQ(StringView(data + 4, 0), StringView(data).tail(0)); + ASSERT_EQ(StringView(data + 2, 2), StringView(data).tail(2)); + ASSERT_EQ(StringView(data, 4), StringView(data).tail(4)); +} + +TEST(StringViewTests, unhead) { + const char data[] = "ABCD"; + ASSERT_EQ(0, StringView::EMPTY.unhead(0).getSize()); + ASSERT_EQ(StringView(nullptr, 0), StringView::NIL.unhead(0)); + ASSERT_EQ(StringView(data, 4), StringView(data).unhead(0)); + ASSERT_EQ(StringView(data + 2, 2), StringView(data).unhead(2)); + ASSERT_EQ(StringView(data + 4, 0), StringView(data).unhead(4)); +} + +TEST(StringViewTests, untail) { + const char data[] = "ABCD"; + ASSERT_EQ(0, StringView::EMPTY.untail(0).getSize()); + ASSERT_EQ(StringView(nullptr, 0), StringView::NIL.untail(0)); + ASSERT_EQ(StringView(data, 4), StringView(data).untail(0)); + ASSERT_EQ(StringView(data, 2), StringView(data).untail(2)); + ASSERT_EQ(StringView(data, 0), StringView(data).untail(4)); +} + +TEST(StringViewTests, range) { + const char data[] = "ABCD"; + ASSERT_EQ(0, StringView::EMPTY.range(0, 0).getSize()); + ASSERT_EQ(StringView(nullptr, 0), StringView::NIL.range(0, 0)); + ASSERT_EQ(StringView(data + 0, 0), StringView(data).range(0, 0)); + ASSERT_EQ(StringView(data + 0, 2), StringView(data).range(0, 2)); + ASSERT_EQ(StringView(data + 0, 4), StringView(data).range(0, 4)); + ASSERT_EQ(StringView(data + 2, 0), StringView(data).range(2, 2)); + ASSERT_EQ(StringView(data + 2, 2), StringView(data).range(2, 4)); + ASSERT_EQ(StringView(data + 4, 0), StringView(data).range(4, 4)); +} + +TEST(StringViewTests, slice) { + const char data[] = "ABCD"; + ASSERT_EQ(0, StringView::EMPTY.slice(0, 0).getSize()); + ASSERT_EQ(StringView(nullptr, 0), StringView::NIL.slice(0, 0)); + ASSERT_EQ(StringView(data + 0, 0), StringView(data).slice(0, 0)); + ASSERT_EQ(StringView(data + 0, 2), StringView(data).slice(0, 2)); + ASSERT_EQ(StringView(data + 0, 4), StringView(data).slice(0, 4)); + ASSERT_EQ(StringView(data + 2, 0), StringView(data).slice(2, 0)); + ASSERT_EQ(StringView(data + 2, 2), StringView(data).slice(2, 2)); + ASSERT_EQ(StringView(data + 4, 0), StringView(data).slice(4, 0)); +} + +TEST(StringViewTests, set) { + std::set set; + set.insert("AB"); + set.insert("ABC"); + set.insert("ABCD"); + ASSERT_EQ(0, set.count(std::string(StringView("A")))); + ASSERT_EQ(1, set.count(std::string(StringView("AB")))); + ASSERT_EQ(1, set.count(std::string(StringView("ABC")))); + ASSERT_EQ(1, set.count(std::string(StringView("ABCD")))); + ASSERT_EQ(0, set.count(std::string(StringView("ABCDE")))); +} diff --git a/tests/unit_tests/TestBlockchainGenerator.cpp b/tests/unit_tests/TestBlockchainGenerator.cpp index d62314d21d..d9bc485793 100644 --- a/tests/unit_tests/TestBlockchainGenerator.cpp +++ b/tests/unit_tests/TestBlockchainGenerator.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,6 +24,8 @@ #include "../performance_tests/multi_tx_test_base.h" +using namespace CryptoNote; + class TransactionForAddressCreator : public multi_tx_test_base<5> { typedef multi_tx_test_base<5> base_class; @@ -35,49 +37,71 @@ class TransactionForAddressCreator : public multi_tx_test_base<5> return base_class::init(); } - void generate(const cryptonote::AccountPublicAddress& address, cryptonote::Transaction& tx) + void generate(const AccountPublicAddress& address, Transaction& tx) { - std::vector destinations; + std::vector destinations; - cryptonote::decompose_amount_into_digits(this->m_source_amount, 0, - [&](uint64_t chunk) { destinations.push_back(cryptonote::tx_destination_entry(chunk, address)); }, - [&](uint64_t a_dust) { destinations.push_back(cryptonote::tx_destination_entry(a_dust, address)); }); + CryptoNote::decompose_amount_into_digits(this->m_source_amount, 0, + [&](uint64_t chunk) { destinations.push_back(CryptoNote::tx_destination_entry(chunk, address)); }, + [&](uint64_t a_dust) { destinations.push_back(CryptoNote::tx_destination_entry(a_dust, address)); }); - cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0); + CryptoNote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0, m_logger); } - void generateSingleOutputTx(const cryptonote::AccountPublicAddress& address, uint64_t amount, cryptonote::Transaction& tx) { - std::vector destinations; - - destinations.push_back(cryptonote::tx_destination_entry(amount, address)); - - cryptonote::construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0); + void generateSingleOutputTx(const AccountPublicAddress& address, uint64_t amount, Transaction& tx) { + std::vector destinations; + destinations.push_back(tx_destination_entry(amount, address)); + construct_tx(this->m_miners[this->real_source_idx].get_keys(), this->m_sources, destinations, std::vector(), tx, 0, m_logger); } }; -TestBlockchainGenerator::TestBlockchainGenerator(const cryptonote::Currency& currency) : +TestBlockchainGenerator::TestBlockchainGenerator(const CryptoNote::Currency& currency) : m_currency(currency), generator(currency) { + std::unique_lock lock(m_mutex); + miner_acc.generate(); addGenesisBlock(); addMiningBlock(); } -std::vector& TestBlockchainGenerator::getBlockchain() +std::vector& TestBlockchainGenerator::getBlockchain() { + std::unique_lock lock(m_mutex); return m_blockchain; } -bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, cryptonote::Transaction& tx) +std::vector TestBlockchainGenerator::getBlockchainCopy() { + std::unique_lock lock(m_mutex); + + std::vector blockchain(m_blockchain); + return blockchain; +} + +bool TestBlockchainGenerator::getTransactionByHash(const crypto::hash& hash, CryptoNote::Transaction& tx, bool checkTxPool) { + std::unique_lock lock(m_mutex); + auto it = m_txs.find(hash); - if (it == m_txs.end()) - return false; + if (it != m_txs.end()) { + tx = it->second; + return true; + } else if (checkTxPool) { + auto poolIt = m_txPool.find(hash); + if (poolIt != m_txPool.end()) { + tx = poolIt->second; + return true; + } + } - tx = it->second; - return true; + return false; +} + +const CryptoNote::account_base& TestBlockchainGenerator::getMinerAccount() const { + std::unique_lock lock(m_mutex); + return miner_acc; } void TestBlockchainGenerator::addGenesisBlock() { @@ -87,7 +111,7 @@ void TestBlockchainGenerator::addGenesisBlock() { } void TestBlockchainGenerator::addMiningBlock() { - cryptonote::Block block; + CryptoNote::Block block; uint64_t timestamp = time(NULL); generator.constructBlock(block, miner_acc, timestamp); m_blockchain.push_back(block); @@ -95,51 +119,76 @@ void TestBlockchainGenerator::addMiningBlock() { void TestBlockchainGenerator::generateEmptyBlocks(size_t count) { + std::unique_lock lock(m_mutex); + for (size_t i = 0; i < count; ++i) { - cryptonote::Block& prev_block = m_blockchain.back(); - cryptonote::Block block; + CryptoNote::Block& prev_block = m_blockchain.back(); + CryptoNote::Block block; generator.constructBlock(block, prev_block, miner_acc); m_blockchain.push_back(block); } } -void TestBlockchainGenerator::addTxToBlockchain(const cryptonote::Transaction& transaction) +void TestBlockchainGenerator::addTxToBlockchain(const CryptoNote::Transaction& transaction) { - crypto::hash txHash = cryptonote::get_transaction_hash(transaction); + std::unique_lock lock(m_mutex); + + crypto::hash txHash = CryptoNote::get_transaction_hash(transaction); m_txs[txHash] = transaction; - std::list txs; + std::list txs; txs.push_back(transaction); - cryptonote::Block& prev_block = m_blockchain.back(); - cryptonote::Block block; + CryptoNote::Block& prev_block = m_blockchain.back(); + CryptoNote::Block block; generator.constructBlock(block, prev_block, miner_acc, txs); m_blockchain.push_back(block); } -bool TestBlockchainGenerator::getBlockRewardForAddress(const cryptonote::AccountPublicAddress& address) +bool TestBlockchainGenerator::getBlockRewardForAddress(const CryptoNote::AccountPublicAddress& address) { + std::unique_lock lock(m_mutex); + + doGenerateTransactionsInOneBlock(address, 1); + return true; +} + +bool TestBlockchainGenerator::generateTransactionsInOneBlock(const CryptoNote::AccountPublicAddress& address, size_t n) { + std::unique_lock lock(m_mutex); + + return doGenerateTransactionsInOneBlock(address, n); +} + +bool TestBlockchainGenerator::doGenerateTransactionsInOneBlock(const AccountPublicAddress &address, size_t n) { + assert(n > 0); + TransactionForAddressCreator creator; if (!creator.init()) return false; - cryptonote::Transaction tx; - creator.generate(address, tx); - tx.unlockTime = 10; //default unlock time for coinbase transactions + std::vector txs; + for (size_t i = 0; i < n; ++i) { + Transaction tx; + creator.generate(address, tx); + tx.unlockTime = 10; //default unlock time for coinbase transactions + txs.push_back(tx); + } - addToBlockchain(tx); + addToBlockchain(txs); return true; } -bool TestBlockchainGenerator::getSingleOutputTransaction(const cryptonote::AccountPublicAddress& address, uint64_t amount) { +bool TestBlockchainGenerator::getSingleOutputTransaction(const CryptoNote::AccountPublicAddress& address, uint64_t amount) { + std::unique_lock lock(m_mutex); + TransactionForAddressCreator creator; if (!creator.init()) return false; - cryptonote::Transaction tx; + CryptoNote::Transaction tx; creator.generateSingleOutputTx(address, amount, tx); addToBlockchain(tx); @@ -147,24 +196,33 @@ bool TestBlockchainGenerator::getSingleOutputTransaction(const cryptonote::Accou return true; } -void TestBlockchainGenerator::addToBlockchain(const cryptonote::Transaction& tx) { - crypto::hash txHash = get_transaction_hash(tx); - m_txs[txHash] = tx; +void TestBlockchainGenerator::addToBlockchain(const CryptoNote::Transaction& tx) { + addToBlockchain(std::vector {tx}); +} - std::list txs; - txs.push_back(tx); +void TestBlockchainGenerator::addToBlockchain(const std::vector& txs) { + std::list txsToBlock; - cryptonote::Block& prev_block = m_blockchain.back(); - cryptonote::Block block; + for (const auto& tx: txs) { + crypto::hash txHash = get_transaction_hash(tx); + m_txs[txHash] = tx; - generator.constructBlock(block, prev_block, miner_acc, txs); + txsToBlock.push_back(tx); + } + + CryptoNote::Block& prev_block = m_blockchain.back(); + CryptoNote::Block block; + + generator.constructBlock(block, prev_block, miner_acc, txsToBlock); m_blockchain.push_back(block); } void TestBlockchainGenerator::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids) + std::vector& new_txs, std::vector& deleted_tx_ids) { - if (known_block_id != cryptonote::get_block_hash(m_blockchain.back())) { + std::unique_lock lock(m_mutex); + + if (known_block_id != CryptoNote::get_block_hash(m_blockchain.back())) { is_bc_actual = false; return; } @@ -192,21 +250,25 @@ void TestBlockchainGenerator::getPoolSymmetricDifference(std::vector lock(m_mutex); + + crypto::hash txHash = CryptoNote::get_transaction_hash(tx); m_txPool[txHash] = tx; } void TestBlockchainGenerator::putTxPoolToBlockchain() { - std::list txs; + std::unique_lock lock(m_mutex); + + std::list txs; for (const auto& kv: m_txPool) { m_txs[kv.first] = kv.second; txs.push_back(kv.second); } - cryptonote::Block& prev_block = m_blockchain.back(); - cryptonote::Block block; + CryptoNote::Block& prev_block = m_blockchain.back(); + CryptoNote::Block block; generator.constructBlock(block, prev_block, miner_acc, txs); m_blockchain.push_back(block); @@ -214,5 +276,21 @@ void TestBlockchainGenerator::putTxPoolToBlockchain() { } void TestBlockchainGenerator::clearTxPool() { + std::unique_lock lock(m_mutex); + m_txPool.clear(); } + +void TestBlockchainGenerator::cutBlockchain(size_t height) { + std::unique_lock lock(m_mutex); + + assert(height < m_blockchain.size()); + //assert(height > m_lastHeight); + + auto it = m_blockchain.begin(); + std::advance(it, height); + + m_blockchain.erase(it, m_blockchain.end()); + + //TODO: delete transactions from m_txs +} diff --git a/tests/unit_tests/TestBlockchainGenerator.h b/tests/unit_tests/TestBlockchainGenerator.h index 1e6303a80e..582180aec7 100644 --- a/tests/unit_tests/TestBlockchainGenerator.h +++ b/tests/unit_tests/TestBlockchainGenerator.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -30,33 +30,42 @@ class TestBlockchainGenerator { public: - TestBlockchainGenerator(const cryptonote::Currency& currency); + TestBlockchainGenerator(const CryptoNote::Currency& currency); - std::vector& getBlockchain(); + //TODO: get rid of this method + std::vector& getBlockchain(); + std::vector getBlockchainCopy(); void generateEmptyBlocks(size_t count); - bool getBlockRewardForAddress(const cryptonote::AccountPublicAddress& address); - bool getSingleOutputTransaction(const cryptonote::AccountPublicAddress& address, uint64_t amount); - void addTxToBlockchain(const cryptonote::Transaction& transaction); - bool getTransactionByHash(const crypto::hash& hash, cryptonote::Transaction& tx); - const cryptonote::account_base& getMinerAccount() const { return miner_acc; } + bool getBlockRewardForAddress(const CryptoNote::AccountPublicAddress& address); + bool generateTransactionsInOneBlock(const CryptoNote::AccountPublicAddress& address, size_t n); + bool getSingleOutputTransaction(const CryptoNote::AccountPublicAddress& address, uint64_t amount); + void addTxToBlockchain(const CryptoNote::Transaction& transaction); + bool getTransactionByHash(const crypto::hash& hash, CryptoNote::Transaction& tx, bool checkTxPool = false); + const CryptoNote::account_base& getMinerAccount() const; - void putTxToPool(const cryptonote::Transaction& tx); + void putTxToPool(const CryptoNote::Transaction& tx); void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids); + std::vector& new_txs, std::vector& deleted_tx_ids); void putTxPoolToBlockchain(); void clearTxPool(); + void cutBlockchain(size_t height); + private: void addGenesisBlock(); void addMiningBlock(); - const cryptonote::Currency& m_currency; + const CryptoNote::Currency& m_currency; test_generator generator; - cryptonote::account_base miner_acc; - std::vector m_blockchain; - std::unordered_map m_txs; - std::unordered_map m_txPool; + CryptoNote::account_base miner_acc; + std::vector m_blockchain; + std::unordered_map m_txs; + std::unordered_map m_txPool; + mutable std::mutex m_mutex; + + void addToBlockchain(const CryptoNote::Transaction& tx); + void addToBlockchain(const std::vector& txs); - void addToBlockchain(cryptonote::Transaction const& tx); + bool doGenerateTransactionsInOneBlock(CryptoNote::AccountPublicAddress const &address, size_t n); }; diff --git a/tests/unit_tests/TestUpgradeDetector.cpp b/tests/unit_tests/TestUpgradeDetector.cpp index a8146a64a9..a66211e885 100644 --- a/tests/unit_tests/TestUpgradeDetector.cpp +++ b/tests/unit_tests/TestUpgradeDetector.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,27 +22,41 @@ #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/UpgradeDetector.h" +#include "Logging/ConsoleLogger.h" + namespace { - using cryptonote::BLOCK_MAJOR_VERSION_1; - using cryptonote::BLOCK_MAJOR_VERSION_2; - using cryptonote::BLOCK_MINOR_VERSION_0; - using cryptonote::BLOCK_MINOR_VERSION_1; + using CryptoNote::BLOCK_MAJOR_VERSION_1; + using CryptoNote::BLOCK_MAJOR_VERSION_2; + using CryptoNote::BLOCK_MINOR_VERSION_0; + using CryptoNote::BLOCK_MINOR_VERSION_1; struct BlockEx { - cryptonote::Block bl; + CryptoNote::Block bl; }; typedef std::vector BlockVector; - typedef cryptonote::BasicUpgradeDetector UpgradeDetector; - - cryptonote::Currency createCurrency(uint64_t upgradeHeight = UpgradeDetector::UNDEF_HEIGHT) { - cryptonote::CurrencyBuilder currencyBuilder; - currencyBuilder.upgradeVotingThreshold(90); - currencyBuilder.upgradeVotingWindow(720); - currencyBuilder.upgradeWindow(720); - currencyBuilder.upgradeHeight(upgradeHeight); - return currencyBuilder.currency(); - } + typedef CryptoNote::BasicUpgradeDetector UpgradeDetector; + + class UpgradeTest : public ::testing::Test { + public: + + CryptoNote::Currency createCurrency(uint64_t upgradeHeight = UpgradeDetector::UNDEF_HEIGHT) { + CryptoNote::CurrencyBuilder currencyBuilder(logger); + currencyBuilder.upgradeVotingThreshold(90); + currencyBuilder.upgradeVotingWindow(720); + currencyBuilder.upgradeWindow(720); + currencyBuilder.upgradeHeight(upgradeHeight); + return currencyBuilder.currency(); + } + + protected: + + Logging::ConsoleLogger logger; + }; + + class UpgradeDetector_voting_init : public UpgradeTest {}; + class UpgradeDetector_upgradeHeight_init : public UpgradeTest {}; + class UpgradeDetector_voting : public UpgradeTest {}; void createBlocks(BlockVector& blockchain, size_t count, uint8_t majorVersion, uint8_t minorVersion) { for (size_t i = 0; i < count; ++i) { @@ -71,60 +85,59 @@ namespace { upgradeDetector.blockPopped(); } } - - - TEST(UpgradeDetector_voting_init, handlesEmptyBlockchain) { - cryptonote::Currency currency = createCurrency(); + + TEST_F(UpgradeDetector_voting_init, handlesEmptyBlockchain) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_voting_init, votingIsNotCompleteDueShortBlockchain) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting_init, votingIsNotCompleteDueShortBlockchain) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; createBlocks(blocks, currency.upgradeVotingWindow() - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfBlocks) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfBlocks) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), currency.upgradeVotingWindow() - 1); } - TEST(UpgradeDetector_voting_init, votingIsNotCompleteDueLackOfVoices) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting_init, votingIsNotCompleteDueLackOfVoices) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); createBlocks(blocks, currency.minNumberVotingBlocks() - 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfVoices) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting_init, votingIsCompleteAfterMinimumNumberOfVoices) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); createBlocks(blocks, currency.minNumberVotingBlocks(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1); } - TEST(UpgradeDetector_voting_init, handlesOneCompleteUpgrade) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting_init, handlesOneCompleteUpgrade) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; createBlocks(blocks, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); uint64_t upgradeHeight = currency.calculateUpgradeHeight(blocks.size() - 1); @@ -132,14 +145,14 @@ namespace { // Upgrade is here createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), currency.upgradeVotingWindow() - 1); ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); } - TEST(UpgradeDetector_voting_init, handlesAFewCompleteUpgrades) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting_init, handlesAFewCompleteUpgrades) { + CryptoNote::Currency currency = createCurrency(); const uint8_t BLOCK_V3 = BLOCK_MAJOR_VERSION_2 + 1; const uint8_t BLOCK_V4 = BLOCK_MAJOR_VERSION_2 + 2; @@ -166,73 +179,73 @@ namespace { // Upgrade to v4 is here createBlocks(blocks, 1, BLOCK_V4, BLOCK_MINOR_VERSION_0); - UpgradeDetector upgradeDetectorV2(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetectorV2(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetectorV2.init()); ASSERT_EQ(upgradeDetectorV2.votingCompleteHeight(), votingCompleteHeigntV2); ASSERT_EQ(upgradeDetectorV2.upgradeHeight(), upgradeHeightV2); - UpgradeDetector upgradeDetectorV3(currency, blocks, BLOCK_V3); + UpgradeDetector upgradeDetectorV3(currency, blocks, BLOCK_V3, logger); ASSERT_TRUE(upgradeDetectorV3.init()); ASSERT_EQ(upgradeDetectorV3.votingCompleteHeight(), votingCompleteHeigntV3); ASSERT_EQ(upgradeDetectorV3.upgradeHeight(), upgradeHeightV3); - UpgradeDetector upgradeDetectorV4(currency, blocks, BLOCK_V4); + UpgradeDetector upgradeDetectorV4(currency, blocks, BLOCK_V4, logger); ASSERT_TRUE(upgradeDetectorV4.init()); ASSERT_EQ(upgradeDetectorV4.votingCompleteHeight(), votingCompleteHeigntV4); ASSERT_EQ(upgradeDetectorV4.upgradeHeight(), upgradeHeightV4); } - TEST(UpgradeDetector_upgradeHeight_init, handlesEmptyBlockchain) { + TEST_F(UpgradeDetector_upgradeHeight_init, handlesEmptyBlockchain) { const uint64_t upgradeHeight = 17; - cryptonote::Currency currency = createCurrency(upgradeHeight); + CryptoNote::Currency currency = createCurrency(upgradeHeight); BlockVector blocks; - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_upgradeHeight_init, handlesBlockchainBeforeUpgrade) { + TEST_F(UpgradeDetector_upgradeHeight_init, handlesBlockchainBeforeUpgrade) { const uint64_t upgradeHeight = 17; - cryptonote::Currency currency = createCurrency(upgradeHeight); + CryptoNote::Currency currency = createCurrency(upgradeHeight); BlockVector blocks; createBlocks(blocks, upgradeHeight, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_upgradeHeight_init, handlesBlockchainAtUpgrade) { + TEST_F(UpgradeDetector_upgradeHeight_init, handlesBlockchainAtUpgrade) { const uint64_t upgradeHeight = 17; - cryptonote::Currency currency = createCurrency(upgradeHeight); + CryptoNote::Currency currency = createCurrency(upgradeHeight); BlockVector blocks; createBlocks(blocks, upgradeHeight + 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_upgradeHeight_init, handlesBlockchainAfterUpgrade) { + TEST_F(UpgradeDetector_upgradeHeight_init, handlesBlockchainAfterUpgrade) { const uint64_t upgradeHeight = 17; - cryptonote::Currency currency = createCurrency(upgradeHeight); + CryptoNote::Currency currency = createCurrency(upgradeHeight); BlockVector blocks; createBlocks(blocks, upgradeHeight + 1, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); createBlocks(blocks, 1, BLOCK_MAJOR_VERSION_2, BLOCK_MINOR_VERSION_0); - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); ASSERT_EQ(upgradeDetector.upgradeHeight(), upgradeHeight); ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_voting, handlesVotingCompleteStartingEmptyBlockchain) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting, handlesVotingCompleteStartingEmptyBlockchain) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); @@ -240,13 +253,13 @@ namespace { ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1); } - TEST(UpgradeDetector_voting, handlesVotingCompleteStartingNonEmptyBlockchain) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting, handlesVotingCompleteStartingNonEmptyBlockchain) { + CryptoNote::Currency currency = createCurrency(); assert(currency.minNumberVotingBlocks() >= 2); const uint64_t portion = currency.minNumberVotingBlocks() - currency.minNumberVotingBlocks() / 2; BlockVector blocks; - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); createBlocks(blocks, upgradeDetector, currency.minNumberVotingBlocks() - portion, BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_1); @@ -256,10 +269,10 @@ namespace { ASSERT_EQ(upgradeDetector.votingCompleteHeight(), blocks.size() - 1); } - TEST(UpgradeDetector_voting, handlesVotingCancelling) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting, handlesVotingCancelling) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); @@ -278,10 +291,10 @@ namespace { ASSERT_EQ(upgradeDetector.votingCompleteHeight(), UpgradeDetector::UNDEF_HEIGHT); } - TEST(UpgradeDetector_voting, handlesVotingAndUpgradeCancelling) { - cryptonote::Currency currency = createCurrency(); + TEST_F(UpgradeDetector_voting, handlesVotingAndUpgradeCancelling) { + CryptoNote::Currency currency = createCurrency(); BlockVector blocks; - UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2); + UpgradeDetector upgradeDetector(currency, blocks, BLOCK_MAJOR_VERSION_2, logger); ASSERT_TRUE(upgradeDetector.init()); createBlocks(blocks, upgradeDetector, currency.upgradeVotingWindow(), BLOCK_MAJOR_VERSION_1, BLOCK_MINOR_VERSION_0); diff --git a/tests/unit_tests/TransactionApi.cpp b/tests/unit_tests/TransactionApi.cpp index 9709fe99cd..12e53599e8 100644 --- a/tests/unit_tests/TransactionApi.cpp +++ b/tests/unit_tests/TransactionApi.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -68,12 +68,13 @@ namespace { virtual void SetUp() override { sender = generateAccountKeys(); tx = createTransaction(); + txHash = tx->getTransactionHash(); } TransactionTypes::InputKeyInfo createInputInfo(uint64_t amount) { TransactionTypes::InputKeyInfo info; - cryptonote::KeyPair srcTxKeys = cryptonote::KeyPair::generate(); + CryptoNote::KeyPair srcTxKeys = CryptoNote::KeyPair::generate(); PublicKey targetKey; @@ -91,17 +92,32 @@ namespace { return info; } + void checkHashChanged() { + auto txNewHash = tx->getTransactionHash(); + EXPECT_NE(txHash, txNewHash); + txHash = txNewHash; + } + + void checkHashUnchanged() { + EXPECT_EQ(txHash, tx->getTransactionHash()); + } + + AccountKeys sender; std::unique_ptr tx; + Hash txHash; }; } TEST_F(TransactionApi, createEmptyReload) { + auto hash = tx->getTransactionHash(); auto pk = tx->getTransactionPublicKey(); checkTxReload(tx); // transaction key should not change on reload - ASSERT_EQ(pk, reloadedTx(tx)->getTransactionPublicKey()); + auto reloaded = reloadedTx(tx); + ASSERT_EQ(pk, reloaded->getTransactionPublicKey()); + ASSERT_EQ(hash, reloaded->getTransactionHash()); } TEST_F(TransactionApi, addAndSignInput) { @@ -109,7 +125,7 @@ TEST_F(TransactionApi, addAndSignInput) { ASSERT_EQ(0, tx->getInputTotalAmount()); TransactionTypes::InputKeyInfo info = createInputInfo(1000); - KeyPair ephKeys; + TransactionTypes::KeyPair ephKeys; size_t index = tx->addInput(sender, info, ephKeys); ASSERT_EQ(0, index); @@ -127,6 +143,8 @@ TEST_F(TransactionApi, addAndSignInput) { auto txBlob = tx->getTransactionData(); ASSERT_FALSE(txBlob.empty()); + + EXPECT_NO_FATAL_FAILURE(checkHashChanged()); } TEST_F(TransactionApi, addAndSignInputMsig) { @@ -159,6 +177,7 @@ TEST_F(TransactionApi, addAndSignInputMsig) { auto txBlob = tx->getTransactionData(); ASSERT_FALSE(txBlob.empty()); + EXPECT_NO_FATAL_FAILURE(checkHashChanged()); } TEST_F(TransactionApi, addOutputKey) { @@ -171,6 +190,7 @@ TEST_F(TransactionApi, addOutputKey) { ASSERT_EQ(1, tx->getOutputCount()); ASSERT_EQ(1000, tx->getOutputTotalAmount()); ASSERT_EQ(TransactionTypes::OutputType::Key, tx->getOutputType(index)); + EXPECT_NO_FATAL_FAILURE(checkHashChanged()); } TEST_F(TransactionApi, addOutputMsig) { @@ -189,6 +209,7 @@ TEST_F(TransactionApi, addOutputMsig) { ASSERT_EQ(1, tx->getOutputCount()); ASSERT_EQ(1000, tx->getOutputTotalAmount()); ASSERT_EQ(TransactionTypes::OutputType::Multisignature, tx->getOutputType(index)); + EXPECT_NO_FATAL_FAILURE(checkHashChanged()); } TEST_F(TransactionApi, secretKey) { @@ -244,6 +265,8 @@ TEST_F(TransactionApi, setGetPaymentId) { tx->setPaymentId(paymentId); + EXPECT_NO_FATAL_FAILURE(checkHashChanged()); + Hash paymentId2; ASSERT_TRUE(tx->getPaymentId(paymentId2)); ASSERT_EQ(paymentId, paymentId2); @@ -274,7 +297,7 @@ TEST_F(TransactionApi, setExtraNonce) { TEST_F(TransactionApi, doubleSpendInTransactionKey) { TransactionTypes::InputKeyInfo info = createInputInfo(1000); - KeyPair ephKeys; + TransactionTypes::KeyPair ephKeys; tx->addInput(sender, info, ephKeys); ASSERT_TRUE(tx->validateInputs()); // now, add the same output again @@ -308,10 +331,13 @@ TEST_F(TransactionApi, unableToModifySignedTransaction) { // from now on, we cannot modify transaction prefix ASSERT_ANY_THROW(tx->addInput(inputMsig)); ASSERT_ANY_THROW(tx->addOutput(500, sender.address)); + Hash paymentId; ASSERT_ANY_THROW(tx->setPaymentId(paymentId)); ASSERT_ANY_THROW(tx->setExtraNonce("smth")); // but can add more signatures tx->signInputMultisignature(index, srcTxKey, 0, generateAccountKeys()); + + EXPECT_NO_FATAL_FAILURE(checkHashChanged()); } diff --git a/tests/unit_tests/TransactionApiHelpers.h b/tests/unit_tests/TransactionApiHelpers.h index 3b777b34f5..793c2e1364 100644 --- a/tests/unit_tests/TransactionApiHelpers.h +++ b/tests/unit_tests/TransactionApiHelpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,15 +26,17 @@ namespace { using namespace CryptoNote; - KeyPair generateKeys() { - KeyPair kp; + inline TransactionTypes::KeyPair generateKeys() { + TransactionTypes::KeyPair kp; crypto::generate_keys( reinterpret_cast(kp.publicKey), reinterpret_cast(kp.secretKey)); return kp; } - AccountKeys accountKeysFromKeypairs(const KeyPair& viewKeys, const KeyPair& spendKeys) { + inline AccountKeys accountKeysFromKeypairs( + const TransactionTypes::KeyPair& viewKeys, + const TransactionTypes::KeyPair& spendKeys) { AccountKeys ak; ak.address.spendPublicKey = spendKeys.publicKey; ak.address.viewPublicKey = viewKeys.publicKey; @@ -43,7 +45,7 @@ namespace { return ak; } - AccountKeys generateAccountKeys() { + inline AccountKeys generateAccountKeys() { return accountKeysFromKeypairs(generateKeys(), generateKeys()); } @@ -53,9 +55,9 @@ namespace { KeyImage generateKeyImage(const AccountKeys& keys, size_t idx, const PublicKey& txPubKey) { KeyImage keyImage; - cryptonote::KeyPair in_ephemeral; - cryptonote::generate_key_image_helper( - reinterpret_cast(keys), + CryptoNote::KeyPair in_ephemeral; + CryptoNote::generate_key_image_helper( + reinterpret_cast(keys), reinterpret_cast(txPubKey), idx, in_ephemeral, diff --git a/tests/unit_tests/TransfersObserver.h b/tests/unit_tests/TransfersObserver.h index 0787200a98..4682ec982c 100644 --- a/tests/unit_tests/TransfersObserver.h +++ b/tests/unit_tests/TransfersObserver.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -44,4 +44,3 @@ class TransfersObserver : public ITransfersObserver { } - diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 4de6fcdf55..d3e0aa820d 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,10 +19,11 @@ #include -#include "common/base58.cpp" +#include "Common/base58.cpp" #include "cryptonote_core/cryptonote_basic_impl.h" #include "serialization/binary_utils.h" #include "cryptonote_core/Currency.h" +#include using namespace tools; @@ -460,17 +461,17 @@ namespace TEST(getAccountAddressAsStr, works_correctly) { - cryptonote::AccountPublicAddress addr; + CryptoNote::AccountPublicAddress addr; ASSERT_TRUE(serialization::parse_binary(test_serialized_keys, addr)); - std::string addr_str = cryptonote::getAccountAddressAsStr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, addr); + std::string addr_str = CryptoNote::getAccountAddressAsStr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, addr); ASSERT_EQ(addr_str, test_keys_addr_str); } TEST(parseAccountAddressString, handles_valid_address) { uint64_t prefix; - cryptonote::AccountPublicAddress addr; - ASSERT_TRUE(cryptonote::parseAccountAddressString(prefix, addr, test_keys_addr_str)); + CryptoNote::AccountPublicAddress addr; + ASSERT_TRUE(CryptoNote::parseAccountAddressString(prefix, addr, test_keys_addr_str)); ASSERT_EQ(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, prefix); std::string blob; @@ -484,17 +485,18 @@ TEST(parseAccountAddressString, fails_on_invalid_address_format) addr_str[0] = '0'; uint64_t prefix; - cryptonote::AccountPublicAddress addr; - ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); + CryptoNote::AccountPublicAddress addr; + ASSERT_FALSE(CryptoNote::parseAccountAddressString(prefix, addr, addr_str)); } TEST(parseAccountAddressString, fails_on_invalid_address_prefix) { std::string addr_str = base58::encode_addr(0, test_serialized_keys); - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); + Logging::LoggerGroup logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).currency(); - cryptonote::AccountPublicAddress addr; + CryptoNote::AccountPublicAddress addr; ASSERT_FALSE(currency.parseAccountAddressString(addr_str, addr)); } @@ -504,8 +506,8 @@ TEST(parseAccountAddressString, fails_on_invalid_address_content) std::string addr_str = base58::encode_addr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, test_serialized_keys.substr(1)); uint64_t prefix; - cryptonote::AccountPublicAddress addr; - ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); + CryptoNote::AccountPublicAddress addr; + ASSERT_FALSE(CryptoNote::parseAccountAddressString(prefix, addr, addr_str)); } TEST(parseAccountAddressString, fails_on_invalid_address_spend_key) @@ -515,8 +517,8 @@ TEST(parseAccountAddressString, fails_on_invalid_address_spend_key) std::string addr_str = base58::encode_addr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); uint64_t prefix; - cryptonote::AccountPublicAddress addr; - ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); + CryptoNote::AccountPublicAddress addr; + ASSERT_FALSE(CryptoNote::parseAccountAddressString(prefix, addr, addr_str)); } TEST(parseAccountAddressString, fails_on_invalid_address_view_key) @@ -526,6 +528,6 @@ TEST(parseAccountAddressString, fails_on_invalid_address_view_key) std::string addr_str = base58::encode_addr(TEST_PUBLIC_ADDRESS_BASE58_PREFIX, serialized_keys_copy); uint64_t prefix; - cryptonote::AccountPublicAddress addr; - ASSERT_FALSE(cryptonote::parseAccountAddressString(prefix, addr, addr_str)); + CryptoNote::AccountPublicAddress addr; + ASSERT_FALSE(CryptoNote::parseAccountAddressString(prefix, addr, addr_str)); } diff --git a/tests/unit_tests/binary_serialization_compatibility.cpp b/tests/unit_tests/binary_serialization_compatibility.cpp index f0edee0675..c8d267edbf 100644 --- a/tests/unit_tests/binary_serialization_compatibility.cpp +++ b/tests/unit_tests/binary_serialization_compatibility.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -15,12 +15,10 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . - #include "gtest/gtest.h" #include #include -#include #include "serialization/BinaryOutputStreamSerializer.h" #include "serialization/BinaryInputStreamSerializer.h" @@ -39,7 +37,7 @@ void checkEqualBinary(Struct& original) { std::stringstream newStream; std::stringstream oldStream; - cryptonote::BinaryOutputStreamSerializer binarySerializer(newStream); + CryptoNote::BinaryOutputStreamSerializer binarySerializer(newStream); binarySerializer(original, ""); binary_archive ba(oldStream); @@ -53,7 +51,7 @@ template void checkEnumeratorToLegacy(Struct& original) { std::stringstream archive; - cryptonote::BinaryOutputStreamSerializer binarySerializer(archive); + CryptoNote::BinaryOutputStreamSerializer binarySerializer(archive); binarySerializer(original, ""); //std::cout << "enumerated string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; @@ -78,7 +76,7 @@ void checkLegacyToEnumerator(Struct& original) { Struct restored; - cryptonote::BinaryInputStreamSerializer binarySerializer(archive); + CryptoNote::BinaryInputStreamSerializer binarySerializer(archive); binarySerializer(restored, ""); ASSERT_EQ(original, restored); @@ -88,11 +86,11 @@ template void checkEnumeratorToEnumerator(Struct& original) { std::stringstream archive; - cryptonote::BinaryOutputStreamSerializer output(archive); + CryptoNote::BinaryOutputStreamSerializer output(archive); output(original, ""); Struct restored; - cryptonote::BinaryInputStreamSerializer input(archive); + CryptoNote::BinaryInputStreamSerializer input(archive); input(restored, ""); ASSERT_EQ(original, restored); @@ -128,7 +126,7 @@ void fillSignature(crypto::signature& sig, char startByte = 120) { fillData(reinterpret_cast(&sig), sizeof(crypto::signature), startByte); } -void fillTransactionOutputMultisignature(cryptonote::TransactionOutputMultisignature& s) { +void fillTransactionOutputMultisignature(CryptoNote::TransactionOutputMultisignature& s) { crypto::public_key key; fillPublicKey(key, 0); s.keys.push_back(key); @@ -153,29 +151,29 @@ void fillTransactionOutputMultisignature(cryptonote::TransactionOutputMultisigna s.requiredSignatures = 12; } -void fillTransaction(cryptonote::Transaction& tx) { +void fillTransaction(CryptoNote::Transaction& tx) { tx.version = 1; tx.unlockTime = 0x7f1234560089ABCD; - cryptonote::TransactionInputGenerate gen; - gen.height = 0xABCD123456EF; + CryptoNote::TransactionInputGenerate gen; + gen.height = 0xABCDEF12; tx.vin.push_back(gen); - cryptonote::TransactionInputToKey key; + CryptoNote::TransactionInputToKey key; key.amount = 500123; key.keyOffsets = {12,3323,0x7f0000000000, std::numeric_limits::max(), 0}; fillKeyImage(key.keyImage); tx.vin.push_back(key); - cryptonote::TransactionInputMultisignature multisig; + CryptoNote::TransactionInputMultisignature multisig; multisig.amount = 490000000; multisig.outputIndex = 424242; multisig.signatures = 4; tx.vin.push_back(multisig); - cryptonote::TransactionOutput txOutput; + CryptoNote::TransactionOutput txOutput; txOutput.amount = 0xfff000ffff778822; - cryptonote::TransactionOutputToKey out; + CryptoNote::TransactionOutputToKey out; fillPublicKey(out.key); txOutput.target = out; tx.vout.push_back(txOutput); @@ -184,20 +182,20 @@ void fillTransaction(cryptonote::Transaction& tx) { tx.signatures.resize(3); - for (size_t i = 0; i < boost::get(tx.vin[1]).keyOffsets.size(); ++i) { + for (size_t i = 0; i < boost::get(tx.vin[1]).keyOffsets.size(); ++i) { crypto::signature sig; - fillSignature(sig, i); + fillSignature(sig, static_cast(i)); tx.signatures[1].push_back(sig); } - for (size_t i = 0; i < boost::get(tx.vin[2]).signatures; ++i) { + for (size_t i = 0; i < boost::get(tx.vin[2]).signatures; ++i) { crypto::signature sig; - fillSignature(sig, i+120); + fillSignature(sig, static_cast(i + 120)); tx.signatures[2].push_back(sig); } } -void fillParentBlock(cryptonote::ParentBlock& pb) { +void fillParentBlock(CryptoNote::ParentBlock& pb) { pb.majorVersion = 1; pb.minorVersion = 1; @@ -207,29 +205,29 @@ void fillParentBlock(cryptonote::ParentBlock& pb) { size_t branchSize = crypto::tree_depth(pb.numberOfTransactions); for (size_t i = 0; i < branchSize; ++i) { crypto::hash hash; - fillHash(hash, i); + fillHash(hash, static_cast(i)); pb.minerTxBranch.push_back(hash); } fillTransaction(pb.minerTx); - cryptonote::tx_extra_merge_mining_tag mmTag; + CryptoNote::tx_extra_merge_mining_tag mmTag; mmTag.depth = 10; fillHash(mmTag.merkle_root); pb.minerTx.extra.clear(); - cryptonote::append_mm_tag_to_extra(pb.minerTx.extra, mmTag); + CryptoNote::append_mm_tag_to_extra(pb.minerTx.extra, mmTag); std::string my; std::copy(pb.minerTx.extra.begin(), pb.minerTx.extra.end(), std::back_inserter(my)); for (size_t i = 0; i < mmTag.depth; ++i) { crypto::hash hash; - fillHash(hash, i); + fillHash(hash, static_cast(i)); pb.blockchainBranch.push_back(hash); } } -void fillBlockHeaderVersion1(cryptonote::BlockHeader& header) { +void fillBlockHeaderVersion1(CryptoNote::BlockHeader& header) { header.majorVersion = 1; header.minorVersion = 1; header.nonce = 0x807F00AB; @@ -237,13 +235,13 @@ void fillBlockHeaderVersion1(cryptonote::BlockHeader& header) { fillHash(header.prevId); } -void fillBlockHeaderVersion2(cryptonote::BlockHeader& header) { +void fillBlockHeaderVersion2(CryptoNote::BlockHeader& header) { fillBlockHeaderVersion1(header); header.majorVersion = 2; } TEST(BinarySerializationCompatibility, TransactionOutputMultisignature) { - cryptonote::TransactionOutputMultisignature s; + CryptoNote::TransactionOutputMultisignature s; fillTransactionOutputMultisignature(s); @@ -251,11 +249,11 @@ TEST(BinarySerializationCompatibility, TransactionOutputMultisignature) { } TEST(BinarySerializationCompatibility, TransactionInputGenerate) { - cryptonote::TransactionInputGenerate s; - s.height = 0x8000000000000001; + CryptoNote::TransactionInputGenerate s; + s.height = 0x80000001; checkCompatibility(s); - s.height = 0x7FFFFFFFFFFFFFFF; + s.height = 0x7FFFFFFF; checkCompatibility(s); s.height = 0; @@ -263,7 +261,7 @@ TEST(BinarySerializationCompatibility, TransactionInputGenerate) { }; TEST(BinarySerializationCompatibility, TransactionInputToKey) { - cryptonote::TransactionInputToKey s; + CryptoNote::TransactionInputToKey s; s.amount = 123456987032; s.keyOffsets = {12,3323,0x7f00000000000000, std::numeric_limits::max(), 0}; @@ -273,7 +271,7 @@ TEST(BinarySerializationCompatibility, TransactionInputToKey) { } TEST(BinarySerializationCompatibility, TransactionInputMultisignature) { - cryptonote::TransactionInputMultisignature s; + CryptoNote::TransactionInputMultisignature s; s.amount = 0xfff000ffff778822; s.signatures = 0x7f259200; s.outputIndex = 0; @@ -282,10 +280,10 @@ TEST(BinarySerializationCompatibility, TransactionInputMultisignature) { } TEST(BinarySerializationCompatibility, TransactionOutput_TransactionOutputToKey) { - cryptonote::TransactionOutput s; + CryptoNote::TransactionOutput s; s.amount = 0xfff000ffff778822; - cryptonote::TransactionOutputToKey out; + CryptoNote::TransactionOutputToKey out; fillPublicKey(out.key); s.target = out; @@ -293,10 +291,10 @@ TEST(BinarySerializationCompatibility, TransactionOutput_TransactionOutputToKey) } TEST(BinarySerializationCompatibility, TransactionOutput_TransactionOutputMultisignature) { - cryptonote::TransactionOutput s; + CryptoNote::TransactionOutput s; s.amount = 0xfff000ffff778822; - cryptonote::TransactionOutputMultisignature out; + CryptoNote::TransactionOutputMultisignature out; fillTransactionOutputMultisignature(out); s.target = out; @@ -304,14 +302,14 @@ TEST(BinarySerializationCompatibility, TransactionOutput_TransactionOutputMultis } TEST(BinarySerializationCompatibility, Transaction) { - cryptonote::Transaction tx; + CryptoNote::Transaction tx; fillTransaction(tx); checkCompatibility(tx); } -void compareParentBlocks(cryptonote::ParentBlock& pb, cryptonote::ParentBlock& restoredPb, bool headerOnly) { +void compareParentBlocks(CryptoNote::ParentBlock& pb, CryptoNote::ParentBlock& restoredPb, bool headerOnly) { EXPECT_EQ(pb.majorVersion, restoredPb.majorVersion); EXPECT_EQ(pb.minorVersion, restoredPb.minorVersion); EXPECT_EQ(pb.prevId, restoredPb.prevId); @@ -326,18 +324,18 @@ void compareParentBlocks(cryptonote::ParentBlock& pb, cryptonote::ParentBlock& r EXPECT_EQ(pb.blockchainBranch, restoredPb.blockchainBranch); } -void checkEnumeratorToLegacy(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { +void checkEnumeratorToLegacy(CryptoNote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { std::stringstream archive; - cryptonote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); - cryptonote::BinaryOutputStreamSerializer output(archive); + CryptoNote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); + CryptoNote::BinaryOutputStreamSerializer output(archive); output(original, ""); - cryptonote::ParentBlock restoredPb; + CryptoNote::ParentBlock restoredPb; uint64_t restoredTs; uint32_t restoredNonce; - cryptonote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); + CryptoNote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); binary_archive ba(archive); bool r = ::serialization::serialize(ba, restored); ASSERT_TRUE(r); @@ -348,21 +346,21 @@ void checkEnumeratorToLegacy(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t ASSERT_NO_FATAL_FAILURE(compareParentBlocks(pb, restoredPb, headerOnly)); } -void checkLegacyToEnumerator(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { +void checkLegacyToEnumerator(CryptoNote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { std::stringstream archive; - cryptonote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); + CryptoNote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); binary_archive ba(archive); bool r = ::serialization::serialize(ba, original); ASSERT_TRUE(r); - cryptonote::ParentBlock restoredPb; + CryptoNote::ParentBlock restoredPb; uint64_t restoredTs; uint32_t restoredNonce; - cryptonote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); + CryptoNote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); - cryptonote::BinaryInputStreamSerializer input(archive); + CryptoNote::BinaryInputStreamSerializer input(archive); input(restored, ""); EXPECT_EQ(nonce, restoredNonce); @@ -371,20 +369,20 @@ void checkLegacyToEnumerator(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t ASSERT_NO_FATAL_FAILURE(compareParentBlocks(pb, restoredPb, headerOnly)); } -void checkEnumeratorToEnumerator(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { +void checkEnumeratorToEnumerator(CryptoNote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { std::stringstream archive; - cryptonote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); - cryptonote::BinaryOutputStreamSerializer output(archive); + CryptoNote::ParentBlockSerializer original(pb, ts, nonce, hashingSerialization, headerOnly); + CryptoNote::BinaryOutputStreamSerializer output(archive); output(original, ""); - cryptonote::ParentBlock restoredPb; + CryptoNote::ParentBlock restoredPb; uint64_t restoredTs; uint32_t restoredNonce; - cryptonote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); + CryptoNote::ParentBlockSerializer restored(restoredPb, restoredTs, restoredNonce, hashingSerialization, headerOnly); - cryptonote::BinaryInputStreamSerializer input(archive); + CryptoNote::BinaryInputStreamSerializer input(archive); input(restored, ""); EXPECT_EQ(nonce, restoredNonce); @@ -393,14 +391,14 @@ void checkEnumeratorToEnumerator(cryptonote::ParentBlock& pb, uint64_t ts, uint3 ASSERT_NO_FATAL_FAILURE(compareParentBlocks(pb, restoredPb, headerOnly)); } -void checkCompatibility(cryptonote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { +void checkCompatibility(CryptoNote::ParentBlock& pb, uint64_t ts, uint32_t nonce, bool hashingSerialization, bool headerOnly) { ASSERT_NO_FATAL_FAILURE(checkEnumeratorToEnumerator(pb, ts, nonce, hashingSerialization, headerOnly)); ASSERT_NO_FATAL_FAILURE(checkEnumeratorToLegacy(pb, ts, nonce, hashingSerialization, headerOnly)); ASSERT_NO_FATAL_FAILURE(checkLegacyToEnumerator(pb, ts, nonce, hashingSerialization, headerOnly)); } TEST(BinarySerializationCompatibility, ParentBlockSerializer) { - cryptonote::ParentBlock pb; + CryptoNote::ParentBlock pb; fillParentBlock(pb); uint64_t timestamp = 1408106672; uint32_t nonce = 1234567; @@ -410,14 +408,14 @@ TEST(BinarySerializationCompatibility, ParentBlockSerializer) { checkCompatibility(pb, timestamp, nonce, false, true); } -void compareBlocks(cryptonote::Block& block, cryptonote::Block& restoredBlock) { +void compareBlocks(CryptoNote::Block& block, CryptoNote::Block& restoredBlock) { ASSERT_EQ(block.majorVersion, restoredBlock.majorVersion); ASSERT_EQ(block.minorVersion, restoredBlock.minorVersion); - if (block.majorVersion == cryptonote::BLOCK_MAJOR_VERSION_1) { + if (block.majorVersion == CryptoNote::BLOCK_MAJOR_VERSION_1) { ASSERT_EQ(block.timestamp, restoredBlock.timestamp); ASSERT_EQ(block.prevId, restoredBlock.prevId); ASSERT_EQ(block.nonce, restoredBlock.nonce); - } else if (block.majorVersion == cryptonote::BLOCK_MAJOR_VERSION_2) { + } else if (block.majorVersion == CryptoNote::BLOCK_MAJOR_VERSION_2) { ASSERT_EQ(block.prevId, restoredBlock.prevId); ASSERT_NO_FATAL_FAILURE(compareParentBlocks(block.parentBlock, restoredBlock.parentBlock, false)); } else { @@ -427,13 +425,13 @@ void compareBlocks(cryptonote::Block& block, cryptonote::Block& restoredBlock) { ASSERT_EQ(block.txHashes, restoredBlock.txHashes); } -void checkEnumeratorToLegacy(cryptonote::Block& block) { +void checkEnumeratorToLegacy(CryptoNote::Block& block) { std::stringstream archive; - cryptonote::BinaryOutputStreamSerializer output(archive); + CryptoNote::BinaryOutputStreamSerializer output(archive); output(block, ""); - cryptonote::Block restoredBlock; + CryptoNote::Block restoredBlock; binary_archive ba(archive); bool r = ::serialization::serialize(ba, restoredBlock); @@ -442,52 +440,52 @@ void checkEnumeratorToLegacy(cryptonote::Block& block) { ASSERT_NO_FATAL_FAILURE(compareBlocks(block, restoredBlock)); } -void checkLegacyToEnumerator(cryptonote::Block& block) { +void checkLegacyToEnumerator(CryptoNote::Block& block) { std::stringstream archive; binary_archive ba(archive); bool r = ::serialization::serialize(ba, block); ASSERT_TRUE(r); - cryptonote::Block restoredBlock; + CryptoNote::Block restoredBlock; - cryptonote::BinaryInputStreamSerializer output(archive); + CryptoNote::BinaryInputStreamSerializer output(archive); output(restoredBlock, ""); ASSERT_NO_FATAL_FAILURE(compareBlocks(block, restoredBlock)); } -void checkEnumeratorToEnumerator(cryptonote::Block& block) { +void checkEnumeratorToEnumerator(CryptoNote::Block& block) { std::stringstream archive; - cryptonote::BinaryOutputStreamSerializer output(archive); + CryptoNote::BinaryOutputStreamSerializer output(archive); output(block, ""); - cryptonote::Block restoredBlock; + CryptoNote::Block restoredBlock; // std::cout << "enumerated string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; - cryptonote::BinaryInputStreamSerializer input(archive); + CryptoNote::BinaryInputStreamSerializer input(archive); input(restoredBlock, ""); ASSERT_NO_FATAL_FAILURE(compareBlocks(block, restoredBlock)); } -void checkCompatibility(cryptonote::Block& block) { +void checkCompatibility(CryptoNote::Block& block) { ASSERT_NO_FATAL_FAILURE(checkEnumeratorToEnumerator(block)); ASSERT_NO_FATAL_FAILURE(checkEnumeratorToLegacy(block)); ASSERT_NO_FATAL_FAILURE(checkLegacyToEnumerator(block)); } TEST(BinarySerializationCompatibility, BlockVersion1) { - cryptonote::Block block; + CryptoNote::Block block; fillBlockHeaderVersion1(block); fillParentBlock(block.parentBlock); fillTransaction(block.minerTx); for (size_t i = 0; i < 7; ++i) { crypto::hash hash; - fillHash(hash, 0x7F + i); + fillHash(hash, static_cast(0x7F + i)); block.txHashes.push_back(hash); } @@ -495,14 +493,14 @@ TEST(BinarySerializationCompatibility, BlockVersion1) { } TEST(BinarySerializationCompatibility, BlockVersion2) { - cryptonote::Block block; + CryptoNote::Block block; fillBlockHeaderVersion2(block); fillParentBlock(block.parentBlock); fillTransaction(block.minerTx); for (size_t i = 0; i < 7; ++i) { crypto::hash hash; - fillHash(hash, 0x7F + i); + fillHash(hash, static_cast(0x7F + i)); block.txHashes.push_back(hash); } @@ -510,16 +508,16 @@ TEST(BinarySerializationCompatibility, BlockVersion2) { } TEST(BinarySerializationCompatibility, account_public_address) { - cryptonote::AccountPublicAddress addr; + CryptoNote::AccountPublicAddress addr; - fillPublicKey(addr.m_spendPublicKey, 0x50); - fillPublicKey(addr.m_viewPublicKey, 0xAA); + fillPublicKey(addr.m_spendPublicKey, '\x50'); + fillPublicKey(addr.m_viewPublicKey, '\xAA'); checkCompatibility(addr); } TEST(BinarySerializationCompatibility, tx_extra_merge_mining_tag) { - cryptonote::tx_extra_merge_mining_tag tag; + CryptoNote::tx_extra_merge_mining_tag tag; tag.depth = 0xdeadbeef; fillHash(tag.merkle_root); @@ -527,17 +525,17 @@ TEST(BinarySerializationCompatibility, tx_extra_merge_mining_tag) { } TEST(BinarySerializationCompatibility, readFromEmptyStream) { - cryptonote::TransactionOutput t; + CryptoNote::TransactionOutput t; std::stringstream emptyStream; - cryptonote::BinaryInputStreamSerializer s(emptyStream); + CryptoNote::BinaryInputStreamSerializer s(emptyStream); ASSERT_ANY_THROW(s(t, "")); } TEST(BinarySerializationCompatibility, writeToBadStream) { - cryptonote::TransactionOutput t; + CryptoNote::TransactionOutput t; std::stringstream badStream; - cryptonote::BinaryOutputStreamSerializer s(badStream); + CryptoNote::BinaryOutputStreamSerializer s(badStream); badStream.setstate(std::ios::badbit); ASSERT_ANY_THROW(s(t, "")); diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index 929364ad84..c4b04c6ee9 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,8 +19,9 @@ #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/Currency.h" +#include -using namespace cryptonote; +using namespace CryptoNote; namespace { @@ -33,7 +34,7 @@ namespace public: getBlockReward_and_already_generated_coins() : ::testing::Test(), - m_currency(cryptonote::CurrencyBuilder(). + m_currency(CryptoNote::CurrencyBuilder(m_logger). blockGrantedFullRewardZone(TEST_GRANTED_FULL_REWARD_ZONE). moneySupply(TEST_MONEY_SUPPLY). emissionSpeedFactor(TEST_EMISSION_SPEED_FACTOR). @@ -43,7 +44,8 @@ namespace protected: static const size_t currentBlockSize = TEST_GRANTED_FULL_REWARD_ZONE / 2; - cryptonote::Currency m_currency; + Logging::LoggerGroup m_logger; + CryptoNote::Currency m_currency; bool m_blockTooBig; int64_t m_emissionChange; uint64_t m_blockReward; @@ -81,7 +83,7 @@ namespace public: getBlockReward_and_median_and_blockSize() : ::testing::Test(), - m_currency(cryptonote::CurrencyBuilder(). + m_currency(CryptoNote::CurrencyBuilder(m_logger). blockGrantedFullRewardZone(TEST_GRANTED_FULL_REWARD_ZONE). moneySupply(TEST_MONEY_SUPPLY). emissionSpeedFactor(TEST_EMISSION_SPEED_FACTOR). @@ -103,7 +105,8 @@ namespace m_blockReward, m_emissionChange); } - cryptonote::Currency m_currency; + Logging::LoggerGroup m_logger; + CryptoNote::Currency m_currency; bool m_blockTooBig; int64_t m_emissionChange; uint64_t m_blockReward; @@ -176,7 +179,7 @@ namespace public: getBlockReward_and_currentBlockSize() : ::testing::Test(), - m_currency(cryptonote::CurrencyBuilder(). + m_currency(CryptoNote::CurrencyBuilder(m_logger). blockGrantedFullRewardZone(TEST_GRANTED_FULL_REWARD_ZONE). moneySupply(TEST_MONEY_SUPPLY). emissionSpeedFactor(TEST_EMISSION_SPEED_FACTOR). @@ -200,7 +203,8 @@ namespace m_blockReward, m_emissionChange); } - cryptonote::Currency m_currency; + Logging::LoggerGroup m_logger; + CryptoNote::Currency m_currency; bool m_blockTooBig; int64_t m_emissionChange; uint64_t m_blockReward; @@ -269,7 +273,7 @@ namespace } //-------------------------------------------------------------------------------------------------------------------- const unsigned int testEmissionSpeedFactor = 4; - const size_t testGrantedFullRewardZone = cryptonote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + const size_t testGrantedFullRewardZone = CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; const size_t testMedian = testGrantedFullRewardZone; const size_t testBlockSize = testMedian + testMedian * 8 / 10; // expected penalty 0.64 * reward const uint64_t testPenalty = 64; // percentage @@ -281,7 +285,7 @@ namespace public: getBlockReward_fee_and_penalizeFee_test() : ::testing::Test(), - m_currency(cryptonote::CurrencyBuilder(). + m_currency(CryptoNote::CurrencyBuilder(m_logger). blockGrantedFullRewardZone(testGrantedFullRewardZone). moneySupply(testMoneySupply). emissionSpeedFactor(testEmissionSpeedFactor). @@ -305,7 +309,8 @@ namespace m_blockReward, m_emissionChange); } - cryptonote::Currency m_currency; + Logging::LoggerGroup m_logger; + CryptoNote::Currency m_currency; bool m_blockTooBig; int64_t m_emissionChange; uint64_t m_blockReward; diff --git a/tests/unit_tests/chacha8.cpp b/tests/unit_tests/chacha8.cpp index 86654e6f11..3344e95a49 100644 --- a/tests/unit_tests/chacha8.cpp +++ b/tests/unit_tests/chacha8.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/unit_tests/checkpoints.cpp b/tests/unit_tests/checkpoints.cpp index 86d42d8f86..fb315be599 100644 --- a/tests/unit_tests/checkpoints.cpp +++ b/tests/unit_tests/checkpoints.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,14 +17,15 @@ #include "gtest/gtest.h" -#include "cryptonote_core/checkpoints.cpp" - -using namespace cryptonote; +#include "cryptonote_core/checkpoints.h" +#include +using namespace CryptoNote; TEST(checkpoints_is_alternative_block_allowed, handles_empty_checkpoins) { - checkpoints cp; + Logging::LoggerGroup logger; + checkpoints cp(logger); ASSERT_FALSE(cp.is_alternative_block_allowed(0, 0)); @@ -35,7 +36,8 @@ TEST(checkpoints_is_alternative_block_allowed, handles_empty_checkpoins) TEST(checkpoints_is_alternative_block_allowed, handles_one_checkpoint) { - checkpoints cp; + Logging::LoggerGroup logger; + checkpoints cp(logger); cp.add_checkpoint(5, "0000000000000000000000000000000000000000000000000000000000000000"); ASSERT_FALSE(cp.is_alternative_block_allowed(0, 0)); @@ -73,7 +75,8 @@ TEST(checkpoints_is_alternative_block_allowed, handles_one_checkpoint) TEST(checkpoints_is_alternative_block_allowed, handles_two_and_more_checkpoints) { - checkpoints cp; + Logging::LoggerGroup logger; + checkpoints cp(logger); cp.add_checkpoint(5, "0000000000000000000000000000000000000000000000000000000000000000"); cp.add_checkpoint(9, "0000000000000000000000000000000000000000000000000000000000000000"); diff --git a/tests/unit_tests/decompose_amount_into_digits.cpp b/tests/unit_tests/decompose_amount_into_digits.cpp index 23d9d329cb..e6cd4948e1 100644 --- a/tests/unit_tests/decompose_amount_into_digits.cpp +++ b/tests/unit_tests/decompose_amount_into_digits.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -70,7 +70,7 @@ namespace TEST_F(decompose_amount_into_digits_test, is_correct_0) { std::vector expected_chunks; - cryptonote::decompose_amount_into_digits(0, 0, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(0, 0, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_has_dust, false); } @@ -78,7 +78,7 @@ TEST_F(decompose_amount_into_digits_test, is_correct_0) TEST_F(decompose_amount_into_digits_test, is_correct_1) { std::vector expected_chunks; - cryptonote::decompose_amount_into_digits(0, 10, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(0, 10, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_has_dust, false); } @@ -87,7 +87,7 @@ TEST_F(decompose_amount_into_digits_test, is_correct_2) { uint64_t expected_chunks_arr[] = {10}; VEC_FROM_ARR(expected_chunks); - cryptonote::decompose_amount_into_digits(10, 0, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(10, 0, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_has_dust, false); } @@ -96,7 +96,7 @@ TEST_F(decompose_amount_into_digits_test, is_correct_3) { std::vector expected_chunks; uint64_t expected_dust = 10; - cryptonote::decompose_amount_into_digits(10, 10, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(10, 10, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_dust, expected_dust); } @@ -105,7 +105,7 @@ TEST_F(decompose_amount_into_digits_test, is_correct_4) { uint64_t expected_dust = 8100; std::vector expected_chunks; - cryptonote::decompose_amount_into_digits(8100, 1000000, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(8100, 1000000, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_dust, expected_dust); } @@ -114,7 +114,7 @@ TEST_F(decompose_amount_into_digits_test, is_correct_5) { uint64_t expected_chunks_arr[] = {100, 900000, 8000000}; VEC_FROM_ARR(expected_chunks); - cryptonote::decompose_amount_into_digits(8900100, 10, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(8900100, 10, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_has_dust, false); } @@ -124,7 +124,7 @@ TEST_F(decompose_amount_into_digits_test, is_correct_6) uint64_t expected_chunks_arr[] = {900000, 8000000}; VEC_FROM_ARR(expected_chunks); uint64_t expected_dust = 100; - cryptonote::decompose_amount_into_digits(8900100, 1000, m_chunk_handler, m_dust_handler); + CryptoNote::decompose_amount_into_digits(8900100, 1000, m_chunk_handler, m_dust_handler); ASSERT_EQ(m_chunk_handler.m_chunks, expected_chunks); ASSERT_EQ(m_dust_handler.m_dust, expected_dust); } diff --git a/tests/unit_tests/epee_boosted_tcp_server.cpp b/tests/unit_tests/epee_boosted_tcp_server.cpp deleted file mode 100644 index 4c74bba9e7..0000000000 --- a/tests/unit_tests/epee_boosted_tcp_server.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include -#include -#include -#include - -#include "gtest/gtest.h" - -#include "include_base_utils.h" -#include "string_tools.h" -#include "net/abstract_tcp_server2.h" - -namespace -{ - const uint32_t test_server_port = 5626; - const std::string test_server_host("127.0.0.1"); - - struct test_connection_context : public epee::net_utils::connection_context_base - { - }; - - struct test_protocol_handler_config - { - }; - - struct test_protocol_handler - { - typedef test_connection_context connection_context; - typedef test_protocol_handler_config config_type; - - test_protocol_handler(epee::net_utils::i_service_endpoint* /*psnd_hndlr*/, config_type& /*config*/, connection_context& /*conn_context*/) - { - } - - void after_init_connection() - { - } - - void handle_qued_callback() - { - } - - bool release_protocol() - { - return true; - } - - bool handle_recv(const void* /*data*/, size_t /*size*/) - { - return false; - } - }; - - typedef epee::net_utils::boosted_tcp_server test_tcp_server; -} - -TEST(boosted_tcp_server, worker_threads_are_exception_resistant) -{ - test_tcp_server srv; - ASSERT_TRUE(srv.init_server(test_server_port, test_server_host)); - - std::mutex mtx; - std::condition_variable cond; - int counter = 0; - - auto counter_incrementer = [&counter, &cond, &mtx]() - { - std::unique_lock lock(mtx); - ++counter; - if (4 <= counter) - { - cond.notify_one(); - } - }; - - // 2 theads, but 4 exceptions - ASSERT_TRUE(srv.run_server(2, false)); - ASSERT_TRUE(srv.async_call([&counter_incrementer]() { counter_incrementer(); throw std::runtime_error("test 1"); })); - ASSERT_TRUE(srv.async_call([&counter_incrementer]() { counter_incrementer(); throw std::string("test 2"); })); - ASSERT_TRUE(srv.async_call([&counter_incrementer]() { counter_incrementer(); throw "test 3"; })); - ASSERT_TRUE(srv.async_call([&counter_incrementer]() { counter_incrementer(); throw 4; })); - - { - std::unique_lock lock(mtx); - ASSERT_NE(std::cv_status::timeout, cond.wait_for(lock, std::chrono::seconds(5))); - ASSERT_EQ(4, counter); - } - - // Check if threads are alive - counter = 0; - //auto counter_incrementer = [&counter]() { counter.fetch_add(1); epee::misc_utils::sleep_no_w(counter.load() * 10); }; - ASSERT_TRUE(srv.async_call(counter_incrementer)); - ASSERT_TRUE(srv.async_call(counter_incrementer)); - ASSERT_TRUE(srv.async_call(counter_incrementer)); - ASSERT_TRUE(srv.async_call(counter_incrementer)); - - { - std::unique_lock lock(mtx); - ASSERT_NE(std::cv_status::timeout, cond.wait_for(lock, std::chrono::seconds(5))); - ASSERT_EQ(4, counter); - } - - srv.send_stop_signal(); - ASSERT_TRUE(srv.timed_wait_server_stop(5 * 1000)); - ASSERT_TRUE(srv.deinit_server()); -} diff --git a/tests/unit_tests/epee_levin_protocol_handler_async.cpp b/tests/unit_tests/epee_levin_protocol_handler_async.cpp deleted file mode 100644 index 8eb20eac6f..0000000000 --- a/tests/unit_tests/epee_levin_protocol_handler_async.cpp +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include -#include - -#include "gtest/gtest.h" - -#include "include_base_utils.h" -#include "string_tools.h" -#include "net/levin_protocol_handler_async.h" -#include "net/net_utils_base.h" -#include "unit_tests_utils.h" - -namespace -{ - struct test_levin_connection_context : public epee::net_utils::connection_context_base - { - }; - - typedef epee::levin::async_protocol_handler_config test_levin_protocol_handler_config; - typedef epee::levin::async_protocol_handler test_levin_protocol_handler; - - struct test_levin_commands_handler : public epee::levin::levin_commands_handler - { - test_levin_commands_handler() - : m_return_code(LEVIN_OK) - , m_last_command(-1) - { - } - - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, test_levin_connection_context& context) - { - m_invoke_counter.inc(); - std::unique_lock lock(m_mutex); - m_last_command = command; - m_last_in_buf = in_buff; - buff_out = m_invoke_out_buf; - return m_return_code; - } - - virtual int notify(int command, const std::string& in_buff, test_levin_connection_context& context) - { - m_notify_counter.inc(); - std::unique_lock lock(m_mutex); - m_last_command = command; - m_last_in_buf = in_buff; - return m_return_code; - } - - virtual void callback(test_levin_connection_context& context) - { - m_callback_counter.inc(); - //std::cout << "test_levin_commands_handler::callback()" << std::endl; - } - - virtual void on_connection_new(test_levin_connection_context& context) - { - m_new_connection_counter.inc(); - //std::cout << "test_levin_commands_handler::on_connection_new()" << std::endl; - } - - virtual void on_connection_close(test_levin_connection_context& context) - { - m_close_connection_counter.inc(); - //std::cout << "test_levin_commands_handler::on_connection_close()" << std::endl; - } - - size_t invoke_counter() const { return m_invoke_counter.get(); } - size_t notify_counter() const { return m_notify_counter.get(); } - size_t callback_counter() const { return m_callback_counter.get(); } - size_t new_connection_counter() const { return m_new_connection_counter.get(); } - size_t close_connection_counter() const { return m_close_connection_counter.get(); } - - int return_code() const { return m_return_code; } - void return_code(int v) { m_return_code = v; } - - const std::string& invoke_out_buf() const { return m_invoke_out_buf; } - void invoke_out_buf(const std::string& v) { m_invoke_out_buf = v; } - - int last_command() const { return m_last_command; } - const std::string& last_in_buf() const { return m_last_in_buf; } - - private: - unit_test::call_counter m_invoke_counter; - unit_test::call_counter m_notify_counter; - unit_test::call_counter m_callback_counter; - unit_test::call_counter m_new_connection_counter; - unit_test::call_counter m_close_connection_counter; - - std::mutex m_mutex; - - int m_return_code; - std::string m_invoke_out_buf; - - int m_last_command; - std::string m_last_in_buf; - }; - - class test_connection : public epee::net_utils::i_service_endpoint - { - public: - test_connection(boost::asio::io_service& io_service, test_levin_protocol_handler_config& protocol_config) - : m_io_service(io_service) - , m_protocol_handler(this, protocol_config, m_context) - , m_send_return(true) - { - } - - void start() - { - ASSERT_TRUE(m_protocol_handler.after_init_connection()); - } - - // Implement epee::net_utils::i_service_endpoint interface - virtual bool do_send(const void* ptr, size_t cb) - { - //std::cout << "test_connection::do_send()" << std::endl; - m_send_counter.inc(); - std::unique_lock lock(m_mutex); - m_last_send_data.append(reinterpret_cast(ptr), cb); - return m_send_return; - } - - virtual bool close() { /*std::cout << "test_connection::close()" << 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; } - virtual bool add_ref() { std::cout << "test_connection::add_ref()" << std::endl; return true; } - virtual bool release() { std::cout << "test_connection::release()" << std::endl; return true; } - - size_t send_counter() const { return m_send_counter.get(); } - - const std::string& last_send_data() const { return m_last_send_data; } - void reset_last_send_data() { std::unique_lock lock(m_mutex); m_last_send_data.clear(); } - - bool send_return() const { return m_send_return; } - void send_return(bool v) { m_send_return = v; } - - public: - test_levin_protocol_handler m_protocol_handler; - - private: - boost::asio::io_service& m_io_service; - test_levin_connection_context m_context; - - unit_test::call_counter m_send_counter; - std::mutex m_mutex; - - std::string m_last_send_data; - - bool m_send_return; - }; - - class async_protocol_handler_test : public ::testing::Test - { - public: - const static uint64_t invoke_timeout = 5 * 1000; - const static size_t max_packet_size = 10 * 1024 * 1024; - - typedef std::unique_ptr test_connection_ptr; - - async_protocol_handler_test() - { - m_handler_config.m_pcommands_handler = &m_commands_handler; - m_handler_config.m_invoke_timeout = invoke_timeout; - m_handler_config.m_max_packet_size = max_packet_size; - } - - virtual void SetUp() - { - } - - protected: - test_connection_ptr create_connection(bool start = true) - { - test_connection_ptr conn(new test_connection(m_io_service, m_handler_config)); - if (start) - { - conn->start(); - } - return conn; - } - - protected: - boost::asio::io_service m_io_service; - test_levin_protocol_handler_config m_handler_config; - test_levin_commands_handler m_commands_handler; - }; - - class positive_test_connection_to_levin_protocol_handler_calls : public async_protocol_handler_test - { - }; - - class test_levin_protocol_handler__hanle_recv_with_invalid_data : public async_protocol_handler_test - { - public: - static const int expected_command = 5615871; - static const int expected_return_code = 782546; - - test_levin_protocol_handler__hanle_recv_with_invalid_data() - : m_expected_invoke_out_buf(512, 'y') - { - } - - virtual void SetUp() - { - async_protocol_handler_test::SetUp(); - - m_conn = create_connection(); - - m_in_data.assign(256, 't'); - - m_req_head.m_signature = LEVIN_SIGNATURE; - m_req_head.m_cb = m_in_data.size(); - m_req_head.m_have_to_return_data = true; - m_req_head.m_command = expected_command; - m_req_head.m_flags = LEVIN_PACKET_REQUEST; - m_req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - - m_commands_handler.return_code(expected_return_code); - m_commands_handler.invoke_out_buf(m_expected_invoke_out_buf); - } - - protected: - void prepare_buf() - { - m_buf.assign(reinterpret_cast(&m_req_head), sizeof(m_req_head)); - m_buf += m_in_data; - } - - protected: - test_connection_ptr m_conn; - epee::levin::bucket_head2 m_req_head; - std::string m_in_data; - std::string m_buf; - std::string m_expected_invoke_out_buf; - }; -} - -TEST_F(positive_test_connection_to_levin_protocol_handler_calls, new_handler_is_not_initialized) -{ - test_connection_ptr conn = create_connection(false); - ASSERT_FALSE(conn->m_protocol_handler.m_connection_initialized); - ASSERT_EQ(0, m_handler_config.get_connections_count()); - ASSERT_EQ(0, m_commands_handler.new_connection_counter()); - conn.reset(); - ASSERT_EQ(0, m_handler_config.get_connections_count()); - ASSERT_EQ(0, m_commands_handler.close_connection_counter()); -} - -TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_initialization_and_destruction_is_correct) -{ - test_connection_ptr conn = create_connection(); - ASSERT_TRUE(conn->m_protocol_handler.m_connection_initialized); - ASSERT_EQ(1, m_handler_config.get_connections_count()); - ASSERT_EQ(1, m_commands_handler.new_connection_counter()); - conn.reset(); - ASSERT_EQ(0, m_handler_config.get_connections_count()); - ASSERT_EQ(1, m_commands_handler.close_connection_counter()); -} - -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]() - { - std::vector connections(connection_count); - for (size_t i = 0; i < connection_count; ++i) - { - connections[i] = create_connection(); - } - - for (size_t i = 0; i < connection_count; ++i) - { - connections[i].reset(); - } - }; - - const size_t thread_count = std::thread::hardware_concurrency(); - std::vector threads(thread_count); - for (std::thread& th : threads) - { - th = std::thread(create_and_destroy_connections); - } - - for (std::thread& th : threads) - { - th.join(); - } - - ASSERT_EQ(0, m_handler_config.get_connections_count()); - ASSERT_EQ(connection_count * thread_count, m_commands_handler.new_connection_counter()); - ASSERT_EQ(connection_count * thread_count, m_commands_handler.close_connection_counter()); -} - -TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_processes_handle_read_as_invoke) -{ - // Setup - const int expected_command = 2634981; - const int expected_return_code = 6732; - const std::string expected_out_data(128, 'w'); - - test_connection_ptr conn = create_connection(); - - std::string in_data(256, 'q'); - - epee::levin::bucket_head2 req_head; - req_head.m_signature = LEVIN_SIGNATURE; - req_head.m_cb = in_data.size(); - req_head.m_have_to_return_data = true; - req_head.m_command = expected_command; - req_head.m_flags = LEVIN_PACKET_REQUEST; - req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - - std::string buf(reinterpret_cast(&req_head), sizeof(req_head)); - buf += in_data; - - m_commands_handler.invoke_out_buf(expected_out_data); - m_commands_handler.return_code(expected_return_code); - - // Test - ASSERT_TRUE(conn->m_protocol_handler.handle_recv(buf.data(), buf.size())); - - // - // Check - // - - // Check connection and levin_commands_handler states - ASSERT_EQ(1, m_commands_handler.invoke_counter()); - ASSERT_EQ(0, m_commands_handler.notify_counter()); - ASSERT_EQ(expected_command, m_commands_handler.last_command()); - ASSERT_EQ(in_data, m_commands_handler.last_in_buf()); - ASSERT_LE(1, conn->send_counter()); - - // Parse send data - std::string send_data = conn->last_send_data(); - epee::levin::bucket_head2 resp_head; - resp_head = *reinterpret_cast(send_data.data()); - ASSERT_LT(sizeof(resp_head), send_data.size()); - std::string out_data = send_data.substr(sizeof(resp_head)); - - // Check sent response - ASSERT_EQ(expected_out_data, out_data); - ASSERT_EQ(LEVIN_SIGNATURE, resp_head.m_signature); - ASSERT_EQ(expected_command, resp_head.m_command); - ASSERT_EQ(expected_return_code, resp_head.m_return_code); - ASSERT_EQ(expected_out_data.size(), resp_head.m_cb); - ASSERT_FALSE(resp_head.m_have_to_return_data); - ASSERT_EQ(LEVIN_PROTOCOL_VER_1, resp_head.m_protocol_version); - ASSERT_TRUE(0 != (resp_head.m_flags | LEVIN_PACKET_RESPONSE)); -} - -TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_processes_handle_read_as_notify) -{ - // Setup - const int expected_command = 4673261; - - test_connection_ptr conn = create_connection(); - - std::string in_data(256, 'e'); - - epee::levin::bucket_head2 req_head; - req_head.m_signature = LEVIN_SIGNATURE; - req_head.m_cb = in_data.size(); - req_head.m_have_to_return_data = false; - req_head.m_command = expected_command; - req_head.m_flags = LEVIN_PACKET_REQUEST; - req_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - - std::string buf(reinterpret_cast(&req_head), sizeof(req_head)); - buf += in_data; - - // Test - ASSERT_TRUE(conn->m_protocol_handler.handle_recv(buf.data(), buf.size())); - - // Check connection and levin_commands_handler states - ASSERT_EQ(1, m_commands_handler.notify_counter()); - ASSERT_EQ(0, m_commands_handler.invoke_counter()); - ASSERT_EQ(expected_command, m_commands_handler.last_command()); - ASSERT_EQ(in_data, m_commands_handler.last_in_buf()); - ASSERT_LE(0, conn->send_counter()); - ASSERT_TRUE(conn->last_send_data().empty()); -} - -TEST_F(positive_test_connection_to_levin_protocol_handler_calls, handler_processes_qued_callback) -{ - test_connection_ptr conn = create_connection(); - - conn->m_protocol_handler.handle_qued_callback(); - conn->m_protocol_handler.handle_qued_callback(); - conn->m_protocol_handler.handle_qued_callback(); - - ASSERT_EQ(3, m_commands_handler.callback_counter()); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_big_packet_1) -{ - std::string buf("yyyyyy"); - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(buf.data(), max_packet_size + 1)); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_big_packet_2) -{ - prepare_buf(); - const size_t first_packet_size = sizeof(m_req_head) - 1; - - m_buf.resize(first_packet_size); - ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); - - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), max_packet_size - m_buf.size() + 1)); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_invalid_signature_for_full_header) -{ - m_req_head.m_signature = LEVIN_SIGNATURE ^ 1; - prepare_buf(); - - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_invalid_signature_for_partial_header) -{ - m_req_head.m_signature = LEVIN_SIGNATURE ^ 1; - prepare_buf(); - m_buf.resize(sizeof(m_req_head.m_signature)); - - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_big_cb) -{ - m_req_head.m_cb = max_packet_size + 1; - prepare_buf(); - - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, does_not_handle_data_after_close) -{ - prepare_buf(); - - ASSERT_TRUE(m_conn->m_protocol_handler.close()); - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_network_error) -{ - prepare_buf(); - - m_conn->send_return(false); - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_chunked_header) -{ - prepare_buf(); - - size_t buf1_size = sizeof(m_req_head) / 2; - - std::string buf1 = m_buf.substr(0, buf1_size); - std::string buf2 = m_buf.substr(buf1_size); - ASSERT_EQ(m_buf, buf1 + buf2); - - ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(buf1.data(), buf1.size())); - ASSERT_EQ(0, m_commands_handler.invoke_counter()); - - ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(buf2.data(), buf2.size())); - ASSERT_EQ(1, m_commands_handler.invoke_counter()); -} - - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_chunked_body) -{ - prepare_buf(); - - size_t buf1_size = sizeof(m_req_head) + (m_buf.size() - sizeof(m_req_head)) / 2; - - std::string buf1 = m_buf.substr(0, buf1_size); - std::string buf2 = m_buf.substr(buf1_size); - ASSERT_EQ(m_buf, buf1 + buf2); - - ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(buf1.data(), buf1.size())); - ASSERT_EQ(0, m_commands_handler.invoke_counter()); - - ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(buf2.data(), buf2.size())); - ASSERT_EQ(1, m_commands_handler.invoke_counter()); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_two_requests_at_once) -{ - prepare_buf(); - m_buf.append(m_buf); - - ASSERT_TRUE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); - ASSERT_EQ(2, m_commands_handler.invoke_counter()); -} - -TEST_F(test_levin_protocol_handler__hanle_recv_with_invalid_data, handles_unexpected_response) -{ - m_req_head.m_flags = LEVIN_PACKET_RESPONSE; - prepare_buf(); - - ASSERT_FALSE(m_conn->m_protocol_handler.handle_recv(m_buf.data(), m_buf.size())); -} diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp index 28d0c5dab2..1b8e5dd933 100644 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ b/tests/unit_tests/get_xtype_from_string.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index 28ac4ab337..a9e2e005b7 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,15 +17,12 @@ #include "gtest/gtest.h" -#include "include_base_utils.h" - int main(int argc, char** argv) { - epee::debug::get_set_enable_assert(true, false); - + // epee::debug::get_set_enable_assert(true, false); //set up logging options - epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + // epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + // epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/tests/unit_tests/mul_div.cpp b/tests/unit_tests/mul_div.cpp index 129e33f29b..5cd6ac0a8d 100644 --- a/tests/unit_tests/mul_div.cpp +++ b/tests/unit_tests/mul_div.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,7 +17,7 @@ #include "gtest/gtest.h" -#include "common/int-util.h" +#include "Common/int-util.h" namespace { diff --git a/tests/unit_tests/parse_amount.cpp b/tests/unit_tests/parse_amount.cpp index e1516b612c..439495502d 100644 --- a/tests/unit_tests/parse_amount.cpp +++ b/tests/unit_tests/parse_amount.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,8 +19,9 @@ #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/Currency.h" +#include -using namespace cryptonote; +using namespace CryptoNote; namespace { @@ -28,7 +29,8 @@ namespace void do_pos_test(uint64_t expected, const std::string& str) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().numberOfDecimalPlaces(TEST_NUMBER_OF_DECIMAL_PLACES).currency(); + Logging::LoggerGroup logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).numberOfDecimalPlaces(TEST_NUMBER_OF_DECIMAL_PLACES).currency(); uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); @@ -39,7 +41,8 @@ namespace void do_neg_test(const std::string& str) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().numberOfDecimalPlaces(TEST_NUMBER_OF_DECIMAL_PLACES).currency(); + Logging::LoggerGroup logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).numberOfDecimalPlaces(TEST_NUMBER_OF_DECIMAL_PLACES).currency(); uint64_t val; std::string number_str = str; std::replace(number_str.begin(), number_str.end(), '_', '.'); diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index bc239a8575..0f5c621bd5 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,7 +26,6 @@ #include "serialization/serialization.h" #include "serialization/binary_archive.h" #include "serialization/json_archive.h" -#include "serialization/debug_archive.h" #include "serialization/variant.h" #include "serialization/vector.h" #include "serialization/binary_utils.h" @@ -92,9 +91,6 @@ VARIANT_TAG(binary_archive, Struct, 0xe0); VARIANT_TAG(binary_archive, int, 0xe1); VARIANT_TAG(json_archive, Struct, "struct"); VARIANT_TAG(json_archive, int, "int"); -VARIANT_TAG(debug_archive, Struct1, "struct1"); -VARIANT_TAG(debug_archive, Struct, "struct"); -VARIANT_TAG(debug_archive, int, "int"); BLOB_SERIALIZER(Blob); @@ -288,7 +284,7 @@ namespace TEST(Serialization, serializes_transacion_signatures_correctly) { - using namespace cryptonote; + using namespace CryptoNote; Transaction tx; Transaction tx1; diff --git a/tests/unit_tests/serialization_kv.cpp b/tests/unit_tests/serialization_kv.cpp index 166f9f27a5..7cb8595680 100644 --- a/tests/unit_tests/serialization_kv.cpp +++ b/tests/unit_tests/serialization_kv.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -30,9 +30,9 @@ #include -using namespace cryptonote; +using namespace CryptoNote; -namespace cryptonote { +namespace CryptoNote { template diff --git a/tests/unit_tests/serialization_structs_comparators.h b/tests/unit_tests/serialization_structs_comparators.h index 3021802e04..94308b6d71 100644 --- a/tests/unit_tests/serialization_structs_comparators.h +++ b/tests/unit_tests/serialization_structs_comparators.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -25,13 +25,13 @@ class Comparator { static bool compare(const T& t1, const T& t2) { return t1 == t2; } }; -namespace cryptonote { +namespace CryptoNote { -bool operator==(const cryptonote::TransactionOutputToScript& t1, const cryptonote::TransactionOutputToScript& t2) { +bool operator==(const CryptoNote::TransactionOutputToScript& t1, const CryptoNote::TransactionOutputToScript& t2) { return true; } -bool operator==(const cryptonote::TransactionOutputMultisignature& t1, const cryptonote::TransactionOutputMultisignature& t2) { +bool operator==(const CryptoNote::TransactionOutputMultisignature& t1, const CryptoNote::TransactionOutputMultisignature& t2) { if (t1.keys != t2.keys) { return false; } @@ -39,19 +39,19 @@ bool operator==(const cryptonote::TransactionOutputMultisignature& t1, const cry return t1.requiredSignatures == t2.requiredSignatures; } -bool operator==(const cryptonote::TransactionInputGenerate& t1, const cryptonote::TransactionInputGenerate& t2) { +bool operator==(const CryptoNote::TransactionInputGenerate& t1, const CryptoNote::TransactionInputGenerate& t2) { return t1.height == t2.height; } -bool operator==(const cryptonote::TransactionInputToScript& t1, const cryptonote::TransactionInputToScript& t2) { +bool operator==(const CryptoNote::TransactionInputToScript& t1, const CryptoNote::TransactionInputToScript& t2) { return true; } -bool operator==(const cryptonote::TransactionInputToScriptHash& t1, const cryptonote::TransactionInputToScriptHash& t2) { +bool operator==(const CryptoNote::TransactionInputToScriptHash& t1, const CryptoNote::TransactionInputToScriptHash& t2) { return true; } -bool operator==(const cryptonote::TransactionInputToKey& t1, const cryptonote::TransactionInputToKey& t2) { +bool operator==(const CryptoNote::TransactionInputToKey& t1, const CryptoNote::TransactionInputToKey& t2) { if (t1.amount != t2.amount) { return false; } @@ -63,7 +63,7 @@ bool operator==(const cryptonote::TransactionInputToKey& t1, const cryptonote::T return t1.keyImage == t2.keyImage; } -bool operator==(const cryptonote::TransactionInputMultisignature& t1, const cryptonote::TransactionInputMultisignature& t2) { +bool operator==(const CryptoNote::TransactionInputMultisignature& t1, const CryptoNote::TransactionInputMultisignature& t2) { if (t1.amount != t2.amount) { return false; } @@ -75,15 +75,15 @@ bool operator==(const cryptonote::TransactionInputMultisignature& t1, const cryp return t1.outputIndex == t2.outputIndex; } -bool operator==(const cryptonote::TransactionOutputToScriptHash& t1, const cryptonote::TransactionOutputToScriptHash& t2) { +bool operator==(const CryptoNote::TransactionOutputToScriptHash& t1, const CryptoNote::TransactionOutputToScriptHash& t2) { return true; } -bool operator==(const cryptonote::TransactionOutputToKey& t1, const cryptonote::TransactionOutputToKey& t2) { +bool operator==(const CryptoNote::TransactionOutputToKey& t1, const CryptoNote::TransactionOutputToKey& t2) { return t1.key == t2.key; } -bool operator==(const cryptonote::TransactionOutput& t1, const cryptonote::TransactionOutput& t2) { +bool operator==(const CryptoNote::TransactionOutput& t1, const CryptoNote::TransactionOutput& t2) { if (t1.amount != t2.amount) { return false; } @@ -91,7 +91,7 @@ bool operator==(const cryptonote::TransactionOutput& t1, const cryptonote::Trans return t1.target == t2.target; } -bool operator==(const cryptonote::ParentBlock& t1, const cryptonote::ParentBlock& t2) { +bool operator==(const CryptoNote::ParentBlock& t1, const CryptoNote::ParentBlock& t2) { if (t1.majorVersion != t2.majorVersion) { return false; } @@ -119,7 +119,7 @@ bool operator==(const cryptonote::ParentBlock& t1, const cryptonote::ParentBlock return t1.blockchainBranch == t2.blockchainBranch; } -bool operator==(const cryptonote::AccountPublicAddress& t1, const cryptonote::AccountPublicAddress& t2) { +bool operator==(const CryptoNote::AccountPublicAddress& t1, const CryptoNote::AccountPublicAddress& t2) { if (t1.m_spendPublicKey != t2.m_spendPublicKey) { return false; } @@ -127,7 +127,7 @@ bool operator==(const cryptonote::AccountPublicAddress& t1, const cryptonote::Ac return t1.m_viewPublicKey == t2.m_viewPublicKey; } -bool operator==(const cryptonote::tx_extra_merge_mining_tag& t1, const cryptonote::tx_extra_merge_mining_tag& t2) { +bool operator==(const CryptoNote::tx_extra_merge_mining_tag& t1, const CryptoNote::tx_extra_merge_mining_tag& t2) { if (t1.depth != t2.depth) { return false; } diff --git a/tests/unit_tests/shuffle.cpp b/tests/unit_tests/shuffle.cpp index e5b818728c..ad20e843b9 100644 --- a/tests/unit_tests/shuffle.cpp +++ b/tests/unit_tests/shuffle.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -21,7 +21,7 @@ #include #include "crypto/crypto.h" -#include "common/ShuffleGenerator.h" +#include "Common/ShuffleGenerator.h" class ShuffleTest : public ::testing::Test { public: @@ -75,4 +75,3 @@ TEST_F(ShuffleTest, correctness_fractionalSize) { TEST_F(ShuffleTest, cryptoGenerator) { checkEngine(ITERATIONS * 3, ITERATIONS, false); } - diff --git a/tests/unit_tests/test_BcS.cpp b/tests/unit_tests/test_BcS.cpp index 5a3f6deb00..855323fbc5 100755 --- a/tests/unit_tests/test_BcS.cpp +++ b/tests/unit_tests/test_BcS.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,6 +22,7 @@ #include "cryptonote_core/TransactionApi.h" #include "cryptonote_core/cryptonote_format_utils.h" +#include "Logging/ConsoleLogger.h" #include "INodeStubs.h" #include "TestBlockchainGenerator.h" @@ -30,12 +31,12 @@ using namespace CryptoNote; namespace { -cryptonote::Transaction createTx(ITransactionReader& tx) { +CryptoNote::Transaction createTx(ITransactionReader& tx) { auto data = tx.getTransactionData(); - cryptonote::blobdata txblob(data.data(), data.data() + data.size()); - cryptonote::Transaction outTx; - cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + CryptoNote::blobdata txblob(data.data(), data.data() + data.size()); + CryptoNote::Transaction outTx; + CryptoNote::parse_and_validate_tx_from_blob(txblob, outTx); return outTx; } @@ -52,7 +53,7 @@ class INodeNonTrivialRefreshStub : public INodeTrivialRefreshStub { } virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { poolWasQueried = true; INodeTrivialRefreshStub::getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback); } @@ -72,7 +73,7 @@ class INodeFunctorialStub : public INodeNonTrivialRefreshStub { INodeFunctorialStub(TestBlockchainGenerator& generator) : INodeNonTrivialRefreshStub(generator) , queryBlocksFunctor([](const std::list&, uint64_t, std::list&, uint64_t&, const Callback&)->bool {return true; }) - , getPoolSymmetricDifferenceFunctor([](const std::vector&, crypto::hash, bool&, std::vector&, std::vector&, const Callback&)->bool {return true; }) { + , getPoolSymmetricDifferenceFunctor([](const std::vector&, crypto::hash, bool&, std::vector&, std::vector&, const Callback&)->bool {return true; }) { } virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override { @@ -82,14 +83,14 @@ class INodeFunctorialStub : public INodeNonTrivialRefreshStub { } virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, - std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { + std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { if (getPoolSymmetricDifferenceFunctor(known_pool_tx_ids, known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback)) { INodeNonTrivialRefreshStub::getPoolSymmetricDifference(std::move(known_pool_tx_ids), known_block_id, is_bc_actual, new_txs, deleted_tx_ids, callback); } } std::function&, uint64_t, std::list&, uint64_t&, const Callback&)> queryBlocksFunctor; - std::function&, crypto::hash, bool&, std::vector&, std::vector&, const Callback&)> getPoolSymmetricDifferenceFunctor; + std::function&, crypto::hash, bool&, std::vector&, std::vector&, const Callback&)> getPoolSymmetricDifferenceFunctor; }; @@ -147,15 +148,15 @@ class ConsumerStub : public IBlockchainConsumer { virtual void getKnownPoolTxIds(std::vector& ids) override { ids.clear(); for (auto& tx : m_pool) { - ids.push_back(cryptonote::get_transaction_hash(tx)); + ids.push_back(CryptoNote::get_transaction_hash(tx)); } } - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { m_pool.insert(m_pool.end(), addedTransactions.begin(), addedTransactions.end()); for (auto& hash : deletedTransactions) { - auto pos = std::find_if(m_pool.begin(), m_pool.end(), [&hash](const cryptonote::Transaction& t)->bool { return hash == cryptonote::get_transaction_hash(t); }); + auto pos = std::find_if(m_pool.begin(), m_pool.end(), [&hash](const CryptoNote::Transaction& t)->bool { return hash == CryptoNote::get_transaction_hash(t); }); if (pos != m_pool.end()) { m_pool.erase(pos); } @@ -165,14 +166,14 @@ class ConsumerStub : public IBlockchainConsumer { } private: - std::vector m_pool; + std::vector m_pool; std::vector m_blockchain; }; class BcSTest : public ::testing::Test, public IBlockchainSynchronizerObserver { public: BcSTest() : - m_currency(cryptonote::CurrencyBuilder().currency()), + m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()), generator(m_currency), m_node(generator), m_sync(m_node, m_currency.genesisBlockHash()) { @@ -193,7 +194,7 @@ class BcSTest : public ::testing::Test, public IBlockchainSynchronizerObserver { generator.getBlockchain().begin(), generator.getBlockchain().end(), std::back_inserter(generatorBlockchain), - [](const cryptonote::Block& b) { return cryptonote::get_block_hash(b); }); + [](const CryptoNote::Block& b) { return CryptoNote::get_block_hash(b); }); for (const auto& consumer : m_consumers) { ASSERT_EQ(consumer->getBlockchain(), generatorBlockchain); @@ -224,7 +225,8 @@ class BcSTest : public ::testing::Test, public IBlockchainSynchronizerObserver { } protected: - cryptonote::Currency m_currency; + Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; TestBlockchainGenerator generator; INodeFunctorialStub m_node; @@ -488,12 +490,12 @@ class FunctorialPoolConsumerStub : public ConsumerStub { getKnownPoolTxIdsFunctor(ids); } - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { + virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override { return onPoolUpdatedFunctor(addedTransactions, deletedTransactions); } std::function&)> getKnownPoolTxIdsFunctor; - std::function&, const std::vector&)> onPoolUpdatedFunctor; + std::function&, const std::vector&)> onPoolUpdatedFunctor; }; TEST_F(BcSTest, firstPoolSynchronizationCheck) { @@ -505,9 +507,9 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) { auto tx2 = ::createTx(*tx2ptr.get()); auto tx3 = ::createTx(*tx3ptr.get()); - auto tx1hash = cryptonote::get_transaction_hash(tx1); - auto tx2hash = cryptonote::get_transaction_hash(tx2); - auto tx3hash = cryptonote::get_transaction_hash(tx3); + auto tx1hash = CryptoNote::get_transaction_hash(tx1); + auto tx2hash = CryptoNote::get_transaction_hash(tx2); + auto tx3hash = CryptoNote::get_transaction_hash(tx3); std::vector consumer1Pool = { tx1hash, tx2hash }; std::vector consumer2Pool = { tx2hash, tx3hash }; @@ -515,7 +517,7 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) { std::unordered_set secondExpectedPool = { tx2hash }; std::vector expectedDeletedPoolAnswer = { tx3hash }; - std::vector expectedNewPoolAnswer = { tx1 }; + std::vector expectedNewPoolAnswer = { tx1 }; FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); @@ -525,17 +527,17 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) { std::vector c1ResponseDeletedPool; std::vector c2ResponseDeletedPool; - std::vector c1ResponseNewPool; - std::vector c2ResponseNewPool; + std::vector c1ResponseNewPool; + std::vector c2ResponseNewPool; - c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { c1ResponseDeletedPool.assign(deleted.begin(), deleted.end()); c1ResponseNewPool.assign(new_txs.begin(), new_txs.end()); return std::error_code(); }; - c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { c2ResponseDeletedPool.assign(deleted.begin(), deleted.end()); c2ResponseNewPool.assign(new_txs.begin(), new_txs.end()); return std::error_code(); @@ -549,7 +551,7 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) { std::unordered_set secondKnownPool; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -596,7 +598,7 @@ TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) { int requestsCount = 0; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -630,7 +632,7 @@ TEST_F(BcSTest, firstPoolSynchronizationCheckGetPoolErr) { int requestsCount = 0; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -676,7 +678,7 @@ TEST_F(BcSTest, poolSynchronizationCheckActual) { int requestsCount = 0; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -714,7 +716,7 @@ TEST_F(BcSTest, poolSynchronizationCheckError) { int requestsCount = 0; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -741,9 +743,9 @@ TEST_F(BcSTest, poolSynchronizationCheckError) { TEST_F(BcSTest, poolSynchronizationCheckTxAdded) { auto tx1ptr = CryptoNote::createTransaction(); auto tx1 = ::createTx(*tx1ptr.get()); - auto tx1hash = cryptonote::get_transaction_hash(tx1); + auto tx1hash = CryptoNote::get_transaction_hash(tx1); - std::vector newPoolAnswer = { tx1 }; + std::vector newPoolAnswer = { tx1 }; std::vector expectedKnownPoolHashes = { tx1hash }; @@ -762,7 +764,7 @@ TEST_F(BcSTest, poolSynchronizationCheckTxAdded) { int requestsCount = 0; std::vector knownPool; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -795,9 +797,9 @@ TEST_F(BcSTest, poolSynchronizationCheckTxAdded) { TEST_F(BcSTest, poolSynchronizationCheckTxDeleted) { auto tx1ptr = CryptoNote::createTransaction(); auto tx1 = ::createTx(*tx1ptr.get()); - auto tx1hash = cryptonote::get_transaction_hash(tx1); + auto tx1hash = CryptoNote::get_transaction_hash(tx1); - std::vector newPoolAnswer = { tx1 }; + std::vector newPoolAnswer = { tx1 }; std::vector deletedPoolAnswer = { tx1hash }; std::vector expectedKnownPoolHashes = {}; @@ -817,7 +819,7 @@ TEST_F(BcSTest, poolSynchronizationCheckTxDeleted) { int requestsCount = 0; std::vector knownPool; - m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { + m_node.getPoolSymmetricDifferenceFunctor = [&](const std::vector& known, crypto::hash last, bool& is_actual, std::vector& new_txs, std::vector& deleted, const INode::Callback& callback) { is_actual = true; requestsCount++; @@ -877,12 +879,12 @@ TEST_F(BcSTest, poolSynchronizationCheckConsumersNotififcation) { bool c1Notified = false; bool c2Notified = false; - c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { c1Notified = true; return std::error_code(); }; - c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { c2Notified = true; return std::error_code(); }; @@ -915,12 +917,12 @@ TEST_F(BcSTest, poolSynchronizationCheckConsumerReturnError) { bool c1Notified = false; bool c2Notified = false; - c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c1.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { c1Notified = true; return std::make_error_code(std::errc::invalid_argument); }; - c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { + c2.onPoolUpdatedFunctor = [&](const std::vector& new_txs, const std::vector& deleted)->std::error_code { c2Notified = true; return std::make_error_code(std::errc::invalid_argument); }; @@ -1059,8 +1061,8 @@ TEST_F(BcSTest, checkINodeReturnBadTx) { CryptoNote::BlockCompleteEntry bce; auto last_block = generator.getBlockchain().back(); - bce.blockHash = cryptonote::get_block_hash(last_block); - bce.block = cryptonote::block_to_blob(last_block); + bce.blockHash = CryptoNote::get_block_hash(last_block); + bce.block = CryptoNote::block_to_blob(last_block); bce.txs.push_back("badtx"); @@ -1263,7 +1265,7 @@ TEST_F(BcSTest, checkStatePreservingBetweenSynchronizations) { generator.generateEmptyBlocks(20); - crypto::hash lastBlockHash = cryptonote::get_block_hash(generator.getBlockchain().back()); + crypto::hash lastBlockHash = CryptoNote::get_block_hash(generator.getBlockchain().back()); m_sync.addObserver(&o1); m_sync.start(); @@ -1377,9 +1379,9 @@ TEST_F(BcSTest, checkTxOrder) { auto tx2 = ::createTx(*tx2ptr.get()); auto tx3 = ::createTx(*tx3ptr.get()); - auto tx1hash = cryptonote::get_transaction_hash(tx1); - auto tx2hash = cryptonote::get_transaction_hash(tx2); - auto tx3hash = cryptonote::get_transaction_hash(tx3); + auto tx1hash = CryptoNote::get_transaction_hash(tx1); + auto tx2hash = CryptoNote::get_transaction_hash(tx2); + auto tx3hash = CryptoNote::get_transaction_hash(tx3); generator.generateEmptyBlocks(2); @@ -1387,14 +1389,14 @@ TEST_F(BcSTest, checkTxOrder) { CryptoNote::BlockCompleteEntry bce; auto last_block = generator.getBlockchain().back(); - bce.blockHash = cryptonote::get_block_hash(last_block); - bce.block = cryptonote::block_to_blob(last_block); - bce.txs.push_back(cryptonote::tx_to_blob(tx1)); - bce.txs.push_back(cryptonote::tx_to_blob(tx2)); - bce.txs.push_back(cryptonote::tx_to_blob(tx3)); + bce.blockHash = CryptoNote::get_block_hash(last_block); + bce.block = CryptoNote::block_to_blob(last_block); + bce.txs.push_back(CryptoNote::tx_to_blob(tx1)); + bce.txs.push_back(CryptoNote::tx_to_blob(tx2)); + bce.txs.push_back(CryptoNote::tx_to_blob(tx3)); - std::vector expectedTxHashes = { cryptonote::get_transaction_hash(last_block.minerTx), tx1hash, tx2hash, tx3hash }; + std::vector expectedTxHashes = { CryptoNote::get_transaction_hash(last_block.minerTx), tx1hash, tx2hash, tx3hash }; int requestNumber = 0; diff --git a/tests/unit_tests/test_TransfersConsumer.cpp b/tests/unit_tests/test_TransfersConsumer.cpp index f6bca31d8d..6cea3e557f 100644 --- a/tests/unit_tests/test_TransfersConsumer.cpp +++ b/tests/unit_tests/test_TransfersConsumer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -18,6 +18,7 @@ #include "gtest/gtest.h" #include "cryptonote_core/TransactionApi.h" +#include "Logging/ConsoleLogger.h" #include "transfers/TransfersConsumer.h" #include @@ -40,7 +41,7 @@ AccountSubscription getAccountSubscription(const AccountKeys& accountKeys) { } AccountKeys getAccountKeysWithViewKey(const PublicKey& publicViewKey, const SecretKey& secretViewKey) { - KeyPair viewKp; + TransactionTypes::KeyPair viewKp; viewKp.publicKey = publicViewKey; viewKp.secretKey = secretViewKey; AccountKeys accountKeys = accountKeysFromKeypairs(viewKp, generateKeys()); @@ -48,11 +49,11 @@ AccountKeys getAccountKeysWithViewKey(const PublicKey& publicViewKey, const Secr return accountKeys; } -cryptonote::Transaction convertTx(ITransactionReader& tx) { +Transaction convertTx(ITransactionReader& tx) { auto blob = tx.getTransactionData(); - cryptonote::blobdata data(reinterpret_cast(blob.data()), blob.size()); - cryptonote::Transaction oldTx; - cryptonote::parse_and_validate_tx_from_blob(data, oldTx); // ignore return code + blobdata data(reinterpret_cast(blob.data()), blob.size()); + Transaction oldTx; + parse_and_validate_tx_from_blob(data, oldTx); // ignore return code return oldTx; } @@ -88,7 +89,8 @@ class TransfersConsumerTest : public ::testing::Test { return getAccountKeysWithViewKey(m_accountKeys.address.viewPublicKey, m_accountKeys.viewSecretKey); } - cryptonote::Currency m_currency; + Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; TestBlockchainGenerator m_generator; INodeTrivialRefreshStub m_node; AccountKeys m_accountKeys; @@ -96,7 +98,7 @@ class TransfersConsumerTest : public ::testing::Test { }; TransfersConsumerTest::TransfersConsumerTest() : - m_currency(cryptonote::CurrencyBuilder().currency()), + m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()), m_generator(m_currency), m_node(m_generator), m_accountKeys(generateAccountKeys()), @@ -321,14 +323,14 @@ TEST_F(TransfersConsumerTest, onBlockchainDetach) { addTestKeyOutput(*tx1, 50, 1, keys); CompleteBlock blocks[3]; - blocks[0].block = cryptonote::Block(); + blocks[0].block = CryptoNote::Block(); blocks[0].block->timestamp = 1233; - blocks[1].block = cryptonote::Block(); + blocks[1].block = CryptoNote::Block(); blocks[1].block->timestamp = 1234; blocks[1].transactions.push_back(tx1); - blocks[2].block = cryptonote::Block(); + blocks[2].block = CryptoNote::Block(); blocks[2].block->timestamp = 1235; blocks[2].transactions.push_back(tx2); @@ -359,7 +361,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_OneEmptyBlockOneFilled) { CompleteBlock blocks[2]; blocks[0].transactions.push_back(ignoredTx); - blocks[1].block = cryptonote::Block(); + blocks[1].block = CryptoNote::Block(); blocks[1].block->timestamp = 1235; blocks[1].transactions.push_back(tx); @@ -390,10 +392,10 @@ TEST_F(TransfersConsumerTest, onNewBlocks_DifferentTimestamps) { CompleteBlock blocks[2]; blocks[0].transactions.push_back(ignoredTx); - blocks[0].block = cryptonote::Block(); + blocks[0].block = CryptoNote::Block(); blocks[0].block->timestamp = subscription.syncStart.timestamp - 1; - blocks[1].block = cryptonote::Block(); + blocks[1].block = CryptoNote::Block(); blocks[1].block->timestamp = subscription.syncStart.timestamp; blocks[1].transactions.push_back(tx); @@ -428,7 +430,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesError) addTestKeyOutput(*tx, 900, 2, m_accountKeys); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = subscription.syncStart.timestamp; block.transactions.push_back(tx); @@ -449,7 +451,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) { addTestKeyOutput(*tx, 900, 0, m_accountKeys); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = subscription.syncStart.timestamp; block.transactions.push_back(tx); @@ -458,7 +460,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_updateHeight) { std::unique_ptr blocks(new CompleteBlock[subscription.transactionSpendableAge]); for (size_t i = 0; i < subscription.transactionSpendableAge; ++i) { - blocks[i].block = cryptonote::Block(); + blocks[i].block = CryptoNote::Block(); auto tr = createTransaction(); addTestInput(*tr, 1000); addTestKeyOutput(*tr, 100, i + 1, generateAccountKeys()); @@ -484,7 +486,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_DifferentSubscribers) { addTestKeyOutput(*tx, amount2, 1, keys); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = 0; block.transactions.push_back(tx); @@ -514,7 +516,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_MultisignatureTransaction) { tx->addOutput(800, { keys.address, keys2.address, keys3.address }, 3); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = 0; block.transactions.push_back(tx); @@ -550,7 +552,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesIsPrope addTestKeyOutput(*tx, 900, 2, m_accountKeys); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = 0; block.transactions.push_back(tx); @@ -588,7 +590,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_getTransactionOutsGlobalIndicesIsNotCa addTestKeyOutput(*tx, 900, 2, generateAccount()); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = 0; block.transactions.push_back(tx); ASSERT_TRUE(consumer.onNewBlocks(&block, 1, 1)); @@ -611,10 +613,10 @@ TEST_F(TransfersConsumerTest, onNewBlocks_markTransactionConfirmed) { ASSERT_EQ(10000, lockedOuts[0].amount); CompleteBlock blocks[2]; - blocks[0].block = cryptonote::Block(); + blocks[0].block = CryptoNote::Block(); blocks[0].block->timestamp = 0; blocks[0].transactions.push_back(tx); - blocks[1].block = cryptonote::Block(); + blocks[1].block = CryptoNote::Block(); blocks[1].block->timestamp = 0; blocks[1].transactions.push_back(createTransaction()); ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 2)); @@ -651,7 +653,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionOutputInformation) { auto out = addTestKeyOutput(*tx, 10000, index, m_accountKeys); CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = 0; block.transactions.push_back(tx); ASSERT_TRUE(consumer.onNewBlocks(&block, 0, 1)); @@ -681,18 +683,18 @@ TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionOutputInformationMulti std::shared_ptr tx(createTransaction()); addTestInput(*tx, 10000); - uint32_t txIndex = tx->addOutput(300, { m_accountKeys.address, generateAccountKeys().address}, 2); + std::size_t txIndex = tx->addOutput(300, { m_accountKeys.address, generateAccountKeys().address}, 2); TransactionOutputInformation expectedOut; expectedOut.type = TransactionTypes::OutputType::Multisignature; expectedOut.amount = 300; expectedOut.globalOutputIndex = index; - expectedOut.outputInTransaction = txIndex; + expectedOut.outputInTransaction = static_cast(txIndex); expectedOut.transactionPublicKey = tx->getTransactionPublicKey(); expectedOut.requiredSignatures = 2; CompleteBlock block; - block.block = cryptonote::Block(); + block.block = CryptoNote::Block(); block.block->timestamp = 0; block.transactions.push_back(tx); ASSERT_TRUE(consumer.onNewBlocks(&block, 0, 1)); @@ -721,11 +723,11 @@ TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionInformation) { tx->setUnlockTime(unlockTime); CompleteBlock blocks[2]; - blocks[0].block = cryptonote::Block(); + blocks[0].block = CryptoNote::Block(); blocks[0].block->timestamp = 0; blocks[0].transactions.push_back(createTransaction()); - blocks[1].block = cryptonote::Block(); + blocks[1].block = CryptoNote::Block(); blocks[1].block->timestamp = 11; blocks[1].transactions.push_back(tx); @@ -760,7 +762,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_manyBlocks) { size_t blockIdx = 0; for (auto& b : blocks) { - b.block = cryptonote::Block(); + b.block = Block(); b.block->timestamp = timestamp++; if (++blockIdx % 10 == 0) { @@ -902,3 +904,135 @@ TEST_F(TransfersConsumerTest, getKnownPoolTxIds_returnsUnconfirmed) { ASSERT_TRUE(std::find(ids.begin(), ids.end(), reinterpret_cast(txhash)) != ids.end()); } } + + +class AutoTimer { +public: + + AutoTimer(bool startNow = true) { + if (startNow) { + start(); + } + } + + void start() { + startTime = std::chrono::steady_clock::now(); + } + + std::chrono::duration getSeconds() { + return std::chrono::steady_clock::now() - startTime; + } + +private: + + std::chrono::steady_clock::time_point startTime; + +}; + +class AutoPrintTimer : AutoTimer { +public: + ~AutoPrintTimer() { + std::cout << "Running time: " << getSeconds().count() << "s" << std::endl; + } +}; + + +class TransfersConsumerPerformanceTest : public TransfersConsumerTest { +public: + + void addAndSubscribeAccounts(size_t count) { + std::cout << "Creating " << count << " accounts" << std::endl; + for (size_t i = 0; i < count; ++i) { + recipients.push_back(generateAccount()); + addSubscription(recipients.back()); + } + } + + size_t generateBlocks(size_t blocksCount, size_t txPerBlock, size_t eachNTx = 3) { + std::cout << "Generating " << blocksCount << " blocks, " << blocksCount*txPerBlock << " transactions" << std::endl; + + blocks.resize(blocksCount); + + uint64_t timestamp = 10000; + uint64_t expectedAmount = 0; + size_t totalTransactions = 0; + size_t expectedTransactions = 0; + uint64_t globalOut = 0; + size_t blockIdx = 0; + + for (auto& b : blocks) { + b.transactions.clear(); + b.block = Block(); + b.block->timestamp = timestamp++; + + for (size_t i = 0; i < txPerBlock; ++i) { + auto tx = createTransaction(); + addTestInput(*tx, 10000); + if ((totalTransactions % eachNTx) == 0) { + + auto& account = recipients[rand() % recipients.size()]; + + addTestKeyOutput(*tx, 1000, ++globalOut, account); + addTestKeyOutput(*tx, 2000, ++globalOut, account); + addTestKeyOutput(*tx, 3000, ++globalOut, account); + expectedAmount += 6000; + ++expectedTransactions; + } + tx->getTransactionHash(); + b.transactions.push_back(std::move(tx)); + ++totalTransactions; + } + } + + return expectedTransactions; + } + + std::vector recipients; + std::vector blocks; +}; + +TEST_F(TransfersConsumerPerformanceTest, DISABLED_memory) { + + addAndSubscribeAccounts(10000); + size_t txcount = generateBlocks(1000, 50, 1); + + std::cout << "Blocks generated, calling onNewBlocks" << std::endl; + + { + AutoPrintTimer t; + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size())); + } + + blocks.clear(); + blocks.shrink_to_fit(); + + std::cout << "Transactions to accounts: " << txcount << std::endl; + + char c; + std::cin >> c; +} + + +TEST_F(TransfersConsumerPerformanceTest, DISABLED_performanceTest) { + + const size_t numAccounts = 1000; + const size_t blocksCount = 1000; + const size_t txPerBlock = 10; + + addAndSubscribeAccounts(1000); + + auto expectedTransactions = generateBlocks(blocksCount, txPerBlock, 3); + auto start = std::chrono::steady_clock::now(); + + std::cout << "Calling onNewBlocks" << std::endl; + + ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, blocks.size())); + + auto end = std::chrono::steady_clock::now(); + std::chrono::duration dur = end - start; + + std::cout << "Total transactions sent: " << blocksCount * txPerBlock << std::endl; + std::cout << "Transactions sent to accounts: " << expectedTransactions << std::endl; + std::cout << "Running time: " << dur.count() << "s" << std::endl; + std::cout << "Finish" << std::endl; +} diff --git a/tests/unit_tests/test_TransfersContainer.cpp b/tests/unit_tests/test_TransfersContainer.cpp index 6269bb4c02..665ff0593e 100644 --- a/tests/unit_tests/test_TransfersContainer.cpp +++ b/tests/unit_tests/test_TransfersContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,13 +23,12 @@ #include "cryptonote_core/account.h" #include "cryptonote_core/Currency.h" #include "cryptonote_core/TransactionApi.h" +#include "Logging/ConsoleLogger.h" #include "transfers/TransfersContainer.h" #include "TransactionApiHelpers.h" using namespace CryptoNote; -using namespace cryptonote; - namespace { const size_t TEST_TRANSACTION_SPENDABLE_AGE = 1; @@ -44,7 +43,7 @@ namespace { } void addInput(ITransaction& tx, const AccountKeys& senderKeys, const TransactionOutputInformation& t) { - CryptoNote::KeyPair kp; + TransactionTypes::KeyPair kp; TransactionTypes::InputKeyInfo info; info.amount = t.amount; @@ -92,7 +91,7 @@ namespace { }; TransfersContainerTest() : - currency(CurrencyBuilder().currency()), + currency(CurrencyBuilder(logger).currency()), container(currency, TEST_TRANSACTION_SPENDABLE_AGE), account(generateAccountKeys()) { } @@ -147,6 +146,7 @@ namespace { return tx; } + Logging::ConsoleLogger logger; Currency currency; TransfersContainer container; AccountKeys account; diff --git a/tests/unit_tests/test_TransfersContainerKeyImage.cpp b/tests/unit_tests/test_TransfersContainerKeyImage.cpp index 6c7b696c00..6743f88265 100644 --- a/tests/unit_tests/test_TransfersContainerKeyImage.cpp +++ b/tests/unit_tests/test_TransfersContainerKeyImage.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -23,12 +23,12 @@ #include "cryptonote_core/account.h" #include "cryptonote_core/Currency.h" #include "cryptonote_core/TransactionApi.h" +#include "Logging/ConsoleLogger.h" #include "transfers/TransfersContainer.h" #include "TransactionApiHelpers.h" using namespace CryptoNote; -using namespace cryptonote; namespace { @@ -46,7 +46,7 @@ namespace { } void addInput(ITransaction& tx, const AccountKeys& senderKeys, const TransactionOutputInformation& t) { - CryptoNote::KeyPair kp; + TransactionTypes::KeyPair kp; TransactionTypes::InputKeyInfo info; info.amount = t.amount; @@ -91,7 +91,7 @@ namespace { public: TransfersContainerKeyImage() : - currency(CurrencyBuilder().currency()), + currency(CurrencyBuilder(logger).currency()), container(currency, TEST_TRANSACTION_SPENDABLE_AGE), account(generateAccountKeys()), txTemplate(createTransaction()) { @@ -179,6 +179,7 @@ namespace { return tx; } + Logging::ConsoleLogger logger; Currency currency; TransfersContainer container; AccountKeys account; diff --git a/tests/unit_tests/test_TransfersSubscription.cpp b/tests/unit_tests/test_TransfersSubscription.cpp index 2ed3051520..3cf4ef2530 100644 --- a/tests/unit_tests/test_TransfersSubscription.cpp +++ b/tests/unit_tests/test_TransfersSubscription.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -19,6 +19,7 @@ #include #include "cryptonote_core/TransactionApi.h" +#include "Logging/ConsoleLogger.h" #include "transfers/TransfersSubscription.h" #include "transfers/TypeHelpers.h" #include "ITransfersContainer.h" @@ -27,7 +28,6 @@ #include "TransfersObserver.h" using namespace CryptoNote; -using namespace cryptonote; namespace { @@ -42,7 +42,7 @@ class TransfersSubscriptionTest : public ::testing::Test { public: TransfersSubscriptionTest() : - currency(CurrencyBuilder().currency()), + currency(CurrencyBuilder(m_logger).currency()), account(generateAccountKeys()), syncStart(SynchronizationStart{ 0, 0 }), sub(currency, AccountSubscription{ account, syncStart, 10 }) { @@ -58,6 +58,7 @@ class TransfersSubscriptionTest : public ::testing::Test { return tx; } + Logging::ConsoleLogger m_logger; Currency currency; AccountKeys account; SynchronizationStart syncStart; @@ -153,4 +154,3 @@ TEST_F(TransfersSubscriptionTest, deleteUnconfirmedTransaction) { ASSERT_EQ(1, observer.deleted.size()); ASSERT_EQ(txHash, observer.deleted[0]); } - diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index f00e153021..fd5fb77a85 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,17 +22,18 @@ // epee #include "misc_language.h" -#include "common/util.h" +#include "Common/util.h" #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/Currency.h" +#include TEST(parse_tx_extra, handles_empty_extra) { std::vector extra;; - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_TRUE(tx_extra_fields.empty()); } @@ -40,47 +41,47 @@ TEST(parse_tx_extra, handles_padding_only_size_1) { const uint8_t extra_arr[] = {0}; std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_EQ(1, tx_extra_fields.size()); - ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[0].type()); - ASSERT_EQ(1, boost::get(tx_extra_fields[0]).size); + ASSERT_EQ(typeid(CryptoNote::tx_extra_padding), tx_extra_fields[0].type()); + ASSERT_EQ(1, boost::get(tx_extra_fields[0]).size); } TEST(parse_tx_extra, handles_padding_only_size_2) { const uint8_t extra_arr[] = {0, 0}; std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_EQ(1, tx_extra_fields.size()); - ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[0].type()); - ASSERT_EQ(2, boost::get(tx_extra_fields[0]).size); + ASSERT_EQ(typeid(CryptoNote::tx_extra_padding), tx_extra_fields[0].type()); + ASSERT_EQ(2, boost::get(tx_extra_fields[0]).size); } TEST(parse_tx_extra, handles_padding_only_max_size) { std::vector extra(TX_EXTRA_NONCE_MAX_COUNT, 0); - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_EQ(1, tx_extra_fields.size()); - ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[0].type()); - ASSERT_EQ(TX_EXTRA_NONCE_MAX_COUNT, boost::get(tx_extra_fields[0]).size); + ASSERT_EQ(typeid(CryptoNote::tx_extra_padding), tx_extra_fields[0].type()); + ASSERT_EQ(TX_EXTRA_NONCE_MAX_COUNT, boost::get(tx_extra_fields[0]).size); } TEST(parse_tx_extra, handles_padding_only_exceed_max_size) { std::vector extra(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); - std::vector tx_extra_fields; - ASSERT_FALSE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_FALSE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); } TEST(parse_tx_extra, handles_invalid_padding_only) { std::vector extra(2, 0); extra[1] = 42; - std::vector tx_extra_fields; - ASSERT_FALSE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_FALSE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); } TEST(parse_tx_extra, handles_pub_key_only) @@ -88,21 +89,21 @@ TEST(parse_tx_extra, handles_pub_key_only) 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)); - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_EQ(1, tx_extra_fields.size()); - ASSERT_EQ(typeid(cryptonote::tx_extra_pub_key), tx_extra_fields[0].type()); + ASSERT_EQ(typeid(CryptoNote::tx_extra_pub_key), tx_extra_fields[0].type()); } TEST(parse_tx_extra, handles_extra_nonce_only) { const uint8_t extra_arr[] = {2, 1, 42}; std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_EQ(1, tx_extra_fields.size()); - ASSERT_EQ(typeid(cryptonote::tx_extra_nonce), tx_extra_fields[0].type()); - cryptonote::tx_extra_nonce extra_nonce = boost::get(tx_extra_fields[0]); + ASSERT_EQ(typeid(CryptoNote::tx_extra_nonce), tx_extra_fields[0].type()); + CryptoNote::tx_extra_nonce extra_nonce = boost::get(tx_extra_fields[0]); ASSERT_EQ(1, extra_nonce.nonce.size()); ASSERT_EQ(42, extra_nonce.nonce[0]); } @@ -114,45 +115,48 @@ TEST(parse_tx_extra, handles_pub_key_and_padding) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; std::vector extra(&extra_arr[0], &extra_arr[0] + sizeof(extra_arr)); - std::vector tx_extra_fields; - ASSERT_TRUE(cryptonote::parse_tx_extra(extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_TRUE(CryptoNote::parse_tx_extra(extra, tx_extra_fields)); ASSERT_EQ(2, tx_extra_fields.size()); - ASSERT_EQ(typeid(cryptonote::tx_extra_pub_key), tx_extra_fields[0].type()); - ASSERT_EQ(typeid(cryptonote::tx_extra_padding), tx_extra_fields[1].type()); + ASSERT_EQ(typeid(CryptoNote::tx_extra_pub_key), tx_extra_fields[0].type()); + ASSERT_EQ(typeid(CryptoNote::tx_extra_padding), tx_extra_fields[1].type()); } TEST(parse_and_validate_tx_extra, is_valid_tx_extra_parsed) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - cryptonote::Transaction tx = AUTO_VAL_INIT(tx); - cryptonote::account_base acc; + Logging::LoggerGroup logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).currency(); + CryptoNote::Transaction tx = AUTO_VAL_INIT(tx); + CryptoNote::account_base acc; acc.generate(); - cryptonote::blobdata b = "dsdsdfsdfsf"; + CryptoNote::blobdata b = "dsdsdfsdfsf"; ASSERT_TRUE(currency.constructMinerTx(0, 0, 10000000000000, 1000, currency.minimumFee(), acc.get_keys().m_account_address, tx, b, 1)); - crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(tx); - ASSERT_NE(tx_pub_key, cryptonote::null_pkey); + crypto::public_key tx_pub_key = CryptoNote::get_tx_pub_key_from_extra(tx); + ASSERT_NE(tx_pub_key, CryptoNote::null_pkey); } TEST(parse_and_validate_tx_extra, fails_on_big_extra_nonce) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - cryptonote::Transaction tx = AUTO_VAL_INIT(tx); - cryptonote::account_base acc; + Logging::LoggerGroup logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).currency(); + CryptoNote::Transaction tx = AUTO_VAL_INIT(tx); + CryptoNote::account_base acc; acc.generate(); - cryptonote::blobdata b(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); + CryptoNote::blobdata b(TX_EXTRA_NONCE_MAX_COUNT + 1, 0); ASSERT_FALSE(currency.constructMinerTx(0, 0, 10000000000000, 1000, currency.minimumFee(), acc.get_keys().m_account_address, tx, b, 1)); } TEST(parse_and_validate_tx_extra, fails_on_wrong_size_in_extra_nonce) { - cryptonote::Transaction tx = AUTO_VAL_INIT(tx); + CryptoNote::Transaction tx = AUTO_VAL_INIT(tx); tx.extra.resize(20, 0); tx.extra[0] = TX_EXTRA_NONCE; tx.extra[1] = 255; - std::vector tx_extra_fields; - ASSERT_FALSE(cryptonote::parse_tx_extra(tx.extra, tx_extra_fields)); + std::vector tx_extra_fields; + ASSERT_FALSE(CryptoNote::parse_tx_extra(tx.extra, tx_extra_fields)); } TEST(validate_parse_amount_case, validate_parse_amount) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().numberOfDecimalPlaces(8).currency(); + Logging::LoggerGroup logger; + CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logger).numberOfDecimalPlaces(8).currency(); uint64_t res = 0; bool r = currency.parseAmount("0.0001", res); ASSERT_TRUE(r); diff --git a/tests/unit_tests/test_inprocess_node.cpp b/tests/unit_tests/test_inprocess_node.cpp index f70f6d101f..4794f0b2ba 100644 --- a/tests/unit_tests/test_inprocess_node.cpp +++ b/tests/unit_tests/test_inprocess_node.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -22,7 +22,7 @@ #include "EventWaiter.h" #include "ICoreStub.h" #include "ICryptonoteProtocolQueryStub.h" -#include "inprocess_node/InProcessNode.h" +#include "InProcessNode/InProcessNode.h" struct CallbackStatus { CallbackStatus() {} @@ -107,7 +107,7 @@ TEST_F(InProcessNode, getLastLocalBlockHeightFailure) { TEST_F(InProcessNode, getLastKnownBlockHeightSuccess) { protocolQueryStub.setObservedHeight(10); - ASSERT_EQ(10, node.getLastKnownBlockHeight()); + ASSERT_EQ(10, node.getLastKnownBlockHeight() + 1); } TEST_F(InProcessNode, getTransactionOutsGlobalIndicesSuccess) { @@ -144,14 +144,14 @@ TEST_F(InProcessNode, getRandomOutsByAmountsSuccess) { crypto::secret_key ignoredSectetKey; crypto::generate_keys(ignoredPublicKey, ignoredSectetKey); - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response expectedResp; - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount out; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response expectedResp; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount out; out.amount = 10; out.outs.push_back({ 11, ignoredPublicKey }); expectedResp.outs.push_back(out); coreStub.set_random_outs(expectedResp, true); - std::vector outs; + std::vector outs; CallbackStatus status; node.getRandomOutsByAmounts({1,2,3}, 1, outs, [&status] (std::error_code ec) { status.setStatus(ec); }); @@ -164,10 +164,10 @@ TEST_F(InProcessNode, getRandomOutsByAmountsSuccess) { } TEST_F(InProcessNode, getRandomOutsByAmountsFailure) { - cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response expectedResp; + CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response expectedResp; coreStub.set_random_outs(expectedResp, false); - std::vector outs; + std::vector outs; CallbackStatus status; node.getRandomOutsByAmounts({1,2,3}, 1, outs, [&status] (std::error_code ec) { status.setStatus(ec); }); @@ -193,7 +193,7 @@ TEST_F(InProcessNode, getLastKnownBlockHeightUninitialized) { TEST_F(InProcessNode, getNewBlocksUninitialized) { CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); std::list knownBlockIds; - std::list newBlocks; + std::list newBlocks; uint64_t startHeight; CallbackStatus status; @@ -214,7 +214,7 @@ TEST_F(InProcessNode, getTransactionOutsGlobalIndicesUninitialized) { TEST_F(InProcessNode, getRandomOutsByAmountsUninitialized) { CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); - std::vector outs; + std::vector outs; CallbackStatus status; newNode.getRandomOutsByAmounts({1,2,3}, 1, outs, [&] (std::error_code ec) { status.setStatus(ec); }); @@ -226,7 +226,7 @@ TEST_F(InProcessNode, relayTransactionUninitialized) { CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); CallbackStatus status; - newNode.relayTransaction(cryptonote::Transaction(), [&] (std::error_code ec) { status.setStatus(ec); }); + newNode.relayTransaction(CryptoNote::Transaction(), [&] (std::error_code ec) { status.setStatus(ec); }); ASSERT_TRUE(status.wait()); ASSERT_NE(std::error_code(), status.getStatus()); } @@ -239,7 +239,7 @@ TEST_F(InProcessNode, getLastLocalBlockTimestamp) { return true; } - virtual bool getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) override { + virtual bool getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) override { blk.timestamp = timestamp; return true; } @@ -267,7 +267,7 @@ TEST_F(InProcessNode, getLastLocalBlockTimestampError) { return true; } - virtual bool getBlockByHash(const crypto::hash &h, cryptonote::Block &blk) override { + virtual bool getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) override { return false; } }; diff --git a/tests/unit_tests/test_path.cpp b/tests/unit_tests/test_path.cpp new file mode 100644 index 0000000000..e12e8484e8 --- /dev/null +++ b/tests/unit_tests/test_path.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" +#include "Common/PathTools.h" + +TEST(PathTools, NativePathToGeneric) { + +#ifdef _WIN32 + const std::string input = "C:\\Windows\\System\\etc\\file.exe"; + const std::string output = "C:/Windows/System/etc/file.exe"; +#else + const std::string input = "/var/tmp/file.tmp"; + const std::string output = input; + +#endif + + auto path = Common::NativePathToGeneric(input); + ASSERT_EQ(output, path); +} + +TEST(PathTools, GetExtension) { + ASSERT_EQ("", Common::GetExtension("")); + ASSERT_EQ(".ext", Common::GetExtension(".ext")); + + ASSERT_EQ("", Common::GetExtension("test")); + ASSERT_EQ(".ext", Common::GetExtension("test.ext")); + ASSERT_EQ(".ext2", Common::GetExtension("test.ext.ext2")); + + ASSERT_EQ(".ext", Common::GetExtension("/path/file.ext")); + ASSERT_EQ(".yyy", Common::GetExtension("/path.xxx/file.yyy")); + ASSERT_EQ("", Common::GetExtension("/path.ext/file")); +} + +TEST(PathTools, RemoveExtension) { + + ASSERT_EQ("", Common::RemoveExtension("")); + ASSERT_EQ("", Common::RemoveExtension(".ext")); + + ASSERT_EQ("test", Common::RemoveExtension("test")); + ASSERT_EQ("test", Common::RemoveExtension("test.ext")); + ASSERT_EQ("test.ext", Common::RemoveExtension("test.ext.ext2")); + + ASSERT_EQ("/path/file", Common::RemoveExtension("/path/file.ext")); + ASSERT_EQ("/path.ext/file", Common::RemoveExtension("/path.ext/file.ext")); + ASSERT_EQ("/path.ext/file", Common::RemoveExtension("/path.ext/file")); +} + +TEST(PathTools, SplitPath) { + std::string dir; + std::string file; + + Common::SplitPath("/path/more/file", dir, file); + + ASSERT_EQ("/path/more", dir); + ASSERT_EQ("file", file); + + Common::SplitPath("file.ext", dir, file); + + ASSERT_EQ("", dir); + ASSERT_EQ("file.ext", file); + + Common::SplitPath("/path/more/", dir, file); + + ASSERT_EQ("/path/more", dir); + ASSERT_EQ("", file); +} diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index 1ef556af9d..32eee076cf 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,18 +17,24 @@ #include "gtest/gtest.h" -#include "common/util.h" -#include "p2p/net_peerlist.h" -#include "net/net_utils_base.h" +#include "Common/util.h" + +#include "p2p/PeerListManager.h" +#include "p2p/PeerListManager.cpp" + +using namespace CryptoNote; + +#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) + TEST(peer_list, peer_list_general) { - nodetool::peerlist_manager plm; + CryptoNote::peerlist_manager plm; plm.init(false); -#define ADD_GRAY_NODE(ip_, port_, id_, last_seen_) { nodetool::peerlist_entry ple; ple.last_seen=last_seen_;ple.adr.ip = ip_; ple.adr.port = port_; ple.id = id_;plm.append_with_peer_gray(ple);} -#define ADD_WHITE_NODE(ip_, port_, id_, last_seen_) { nodetool::peerlist_entry ple;ple.last_seen=last_seen_; ple.adr.ip = ip_; ple.adr.port = port_; ple.id = id_;plm.append_with_peer_white(ple);} +#define ADD_GRAY_NODE(ip_, port_, id_, last_seen_) { peerlist_entry ple; ple.last_seen=last_seen_;ple.adr.ip = ip_; ple.adr.port = port_; ple.id = id_;plm.append_with_peer_gray(ple);} +#define ADD_WHITE_NODE(ip_, port_, id_, last_seen_) { peerlist_entry ple;ple.last_seen=last_seen_; ple.adr.ip = ip_; ple.adr.port = port_; 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::list bs_head; bool r = plm.get_peerlist_head(bs_head, 100);std::cout << "step " << step << ": " << bs_head.size() << std::endl;} ADD_GRAY_NODE(MAKE_IP(123,43,12,1), 8080, 121241, 34345); ADD_GRAY_NODE(MAKE_IP(123,43,12,2), 8080, 121241, 34345); @@ -44,7 +50,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::list bs_head; bool r = plm.get_peerlist_head(bs_head, 100); std::cout << bs_head.size() << std::endl; ASSERT_TRUE(r); @@ -62,10 +68,10 @@ TEST(peer_list, merge_peer_lists) { //([^ \t]*)\t([^ \t]*):([^ \t]*) \tlast_seen: d(\d+)\.h(\d+)\.m(\d+)\.s(\d+)\n //ADD_NODE_TO_PL("\2", \3, 0x\1, (1353346618 -(\4*60*60*24+\5*60*60+\6*60+\7 )));\n - nodetool::peerlist_manager plm; + peerlist_manager plm; plm.init(false); - std::list 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);} + std::list outer_bs; +#define ADD_NODE_TO_PL(ip_, port_, id_, timestamp_) { 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);} } diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index 7ca59dcf73..d3f0600553 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -26,7 +26,7 @@ TEST(protocol_pack, protocol_pack_command) { std::string buff; - cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r; + CryptoNote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r; r.start_height = 1; r.total_height = 3; for(int i = 1; i < 10000; i += i*10) @@ -35,7 +35,7 @@ TEST(protocol_pack, protocol_pack_command) bool res = epee::serialization::store_t_to_binary(r, buff); ASSERT_TRUE(res); - cryptonote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r2; + CryptoNote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r2; res = epee::serialization::load_t_from_binary(r2, buff); ASSERT_TRUE(r.m_block_ids.size() == i); ASSERT_TRUE(r.start_height == 1); diff --git a/tests/unit_tests/test_transfers.cpp b/tests/unit_tests/test_transfers.cpp index 678a9ad8cf..299e6a5ffa 100644 --- a/tests/unit_tests/test_transfers.cpp +++ b/tests/unit_tests/test_transfers.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -30,6 +30,8 @@ #include #include +#include + using namespace CryptoNote; class TransfersObserver : public ITransfersObserver { @@ -48,7 +50,7 @@ class TransfersApi : public ::testing::Test, public IBlockchainSynchronizerObser public: TransfersApi() : - m_currency(cryptonote::CurrencyBuilder().currency()), + m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()), generator(m_currency), m_node(generator), m_sync(m_node, m_currency.genesisBlockHash()), @@ -117,15 +119,15 @@ class TransfersApi : public ::testing::Test, public IBlockchainSynchronizerObser void generateMoneyForAccount(size_t idx) { generator.getBlockRewardForAddress( - reinterpret_cast(m_accounts[idx].address)); + reinterpret_cast(m_accounts[idx].address)); } std::error_code submitTransaction(ITransactionReader& tx) { auto data = tx.getTransactionData(); - cryptonote::blobdata txblob(data.data(), data.data() + data.size()); - cryptonote::Transaction outTx; - cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + CryptoNote::blobdata txblob(data.data(), data.data() + data.size()); + CryptoNote::Transaction outTx; + CryptoNote::parse_and_validate_tx_from_blob(txblob, outTx); std::promise result; m_node.relayTransaction(outTx, [&result](std::error_code ec) { @@ -141,7 +143,8 @@ class TransfersApi : public ::testing::Test, public IBlockchainSynchronizerObser std::vector m_accounts; std::vector m_subscriptions; - cryptonote::Currency m_currency; + Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; TestBlockchainGenerator generator; INodeTrivialRefreshStub m_node; BlockchainSynchronizer m_sync; @@ -188,7 +191,7 @@ TEST_F(TransfersApi, syncOneBlock) { addAccounts(2); subscribeAccounts(); - generator.getBlockRewardForAddress(reinterpret_cast(m_accounts[0].address)); + generator.getBlockRewardForAddress(reinterpret_cast(m_accounts[0].address)); generator.generateEmptyBlocks(15); startSync(); @@ -221,57 +224,57 @@ namespace { uint64_t amount, uint64_t fee, const AccountKeys& senderKeys, - const AccountAddress& reciever, - ITransfersContainer& tc) { + const AccountAddress& reciever, + ITransfersContainer& tc) { - std::vector transfers; - tc.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); + std::vector transfers; + tc.getOutputs(transfers, ITransfersContainer::IncludeAllUnlocked); - auto tx = createTransaction(); + auto tx = createTransaction(); - std::vector> inputs; + std::vector> inputs; - uint64_t foundMoney = 0; + uint64_t foundMoney = 0; - for (const auto& t : transfers) { - TransactionTypes::InputKeyInfo info; + for (const auto& t : transfers) { + TransactionTypes::InputKeyInfo info; - info.amount = t.amount; + info.amount = t.amount; - TransactionTypes::GlobalOutput globalOut; - globalOut.outputIndex = t.globalOutputIndex; - globalOut.targetKey = t.outputKey; - info.outputs.push_back(globalOut); + TransactionTypes::GlobalOutput globalOut; + globalOut.outputIndex = t.globalOutputIndex; + globalOut.targetKey = t.outputKey; + info.outputs.push_back(globalOut); - info.realOutput.outputInTransaction = t.outputInTransaction; - info.realOutput.transactionIndex = 0; - info.realOutput.transactionPublicKey = t.transactionPublicKey; + info.realOutput.outputInTransaction = t.outputInTransaction; + info.realOutput.transactionIndex = 0; + info.realOutput.transactionPublicKey = t.transactionPublicKey; - KeyPair kp; - tx->addInput(senderKeys, info, kp); + TransactionTypes::KeyPair kp; + tx->addInput(senderKeys, info, kp); - inputs.push_back(std::make_pair(info, kp)); + inputs.push_back(std::make_pair(info, kp)); - foundMoney += info.amount; + foundMoney += info.amount; - if (foundMoney >= amount + fee) { - break; - } + if (foundMoney >= amount + fee) { + break; + } } - // output to reciever - tx->addOutput(amount, reciever); - // change - uint64_t change = foundMoney - amount - fee; - if (change) { - tx->addOutput(change, senderKeys.address); - } + // output to reciever + tx->addOutput(amount, reciever); + // change + uint64_t change = foundMoney - amount - fee; + if (change) { + tx->addOutput(change, senderKeys.address); + } - for (size_t inputIdx = 0; inputIdx < inputs.size(); ++inputIdx) { - tx->signInputKey(inputIdx, inputs[inputIdx].first, inputs[inputIdx].second); - } + for (size_t inputIdx = 0; inputIdx < inputs.size(); ++inputIdx) { + tx->signInputKey(inputIdx, inputs[inputIdx].first, inputs[inputIdx].second); + } - return tx; + return tx; } } @@ -283,7 +286,7 @@ TEST_F(TransfersApi, moveMoney) { generator.generateEmptyBlocks(2 * m_currency.minedMoneyUnlockWindow()); // sendAmount is an even number - uint64_t sendAmount = (cryptonote::get_outs_money_amount(generator.getBlockchain()[1].minerTx) / 4) * 2; + uint64_t sendAmount = (get_outs_money_amount(generator.getBlockchain()[1].minerTx) / 4) * 2; auto fee = m_currency.minimumFee(); startSync(); @@ -426,4 +429,3 @@ TEST_F(TransfersApi, sameTrackingKey) { } } - diff --git a/tests/unit_tests/test_tx_pool_detach.cpp b/tests/unit_tests/test_tx_pool_detach.cpp index 55f3f60637..f553e93a1f 100755 --- a/tests/unit_tests/test_tx_pool_detach.cpp +++ b/tests/unit_tests/test_tx_pool_detach.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -17,6 +17,8 @@ #include "gtest/gtest.h" +#include "EventWaiter.h" +#include "Logging/ConsoleLogger.h" #include "transfers/BlockchainSynchronizer.h" #include "transfers/TransfersSynchronizer.h" @@ -53,21 +55,25 @@ class INodeStubWithPoolTx : public INodeTrivialRefreshStub { public: INodeStubWithPoolTx(TestBlockchainGenerator& generator) : INodeTrivialRefreshStub(generator), detached(false) {} - void relayTransaction(const cryptonote::Transaction& transaction, const Callback& callback) override { + void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override { + std::unique_lock lk(mutex); relayedTxs.push_back(std::make_pair(this->getLastLocalBlockHeight(), transaction)); + lk.unlock(); INodeTrivialRefreshStub::relayTransaction(transaction, callback); } void startAlternativeChain(uint64_t height) override { + std::unique_lock lk(mutex); INodeTrivialRefreshStub::startAlternativeChain(height); detachHeight = height; detached = true; } - void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override + void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { - std::sort(relayedTxs.begin(), relayedTxs.end(), [](const std::pair& val1, const std::pair& val2)->bool {return val1.first < val2.first; }); + std::unique_lock lk(mutex); + std::sort(relayedTxs.begin(), relayedTxs.end(), [](const std::pair& val1, const std::pair& val2)->bool {return val1.first < val2.first; }); is_bc_actual = true; if (detached) { @@ -83,22 +89,42 @@ class INodeStubWithPoolTx : public INodeTrivialRefreshStub { } } + lk.unlock(); callback(std::error_code()); }; - std::vector> relayedTxs; + std::vector> relayedTxs; uint64_t detachHeight; bool detached; - + std::mutex mutex; }; +class WalletSendObserver : public CryptoNote::IWalletObserver +{ +public: + WalletSendObserver() {} + + bool waitForSendEnd(std::error_code& ec) { + if (!sent.wait_for(std::chrono::milliseconds(5000))) return false; + ec = sendResult; + return true; + } + + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { + sendResult = result; + sent.notify(); + } + + std::error_code sendResult; + EventWaiter sent; +}; class DetachTest : public ::testing::Test, public IBlockchainSynchronizerObserver { public: DetachTest() : - m_currency(cryptonote::CurrencyBuilder().currency()), + m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()), generator(m_currency), m_node(generator), m_sync(m_node, m_currency.genesisBlockHash()), @@ -137,19 +163,25 @@ class DetachTest : public ::testing::Test, public IBlockchainSynchronizerObserve void generateMoneyForAccount(size_t idx) { generator.getBlockRewardForAddress( - reinterpret_cast(m_accounts[idx].address)); + reinterpret_cast(m_accounts[idx].address)); } std::error_code submitTransaction(ITransactionReader& tx) { auto data = tx.getTransactionData(); - cryptonote::blobdata txblob(data.data(), data.data() + data.size()); - cryptonote::Transaction outTx; - cryptonote::parse_and_validate_tx_from_blob(txblob, outTx); + CryptoNote::blobdata txblob(data.data(), data.data() + data.size()); + CryptoNote::Transaction outTx; + CryptoNote::parse_and_validate_tx_from_blob(txblob, outTx); std::promise result; - m_node.relayTransaction(outTx, [&result](std::error_code ec) { result.set_value(ec); }); - return result.get_future().get(); + std::future future = result.get_future(); + + m_node.relayTransaction(outTx, [&result](std::error_code ec) { + std::promise promise = std::move(result); + promise.set_value(ec); + }); + + return future.get(); } void synchronizationCompleted(std::error_code result) override { @@ -162,7 +194,8 @@ class DetachTest : public ::testing::Test, public IBlockchainSynchronizerObserve std::vector m_accounts; std::vector m_subscriptions; - cryptonote::Currency m_currency; + Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; TestBlockchainGenerator generator; INodeStubWithPoolTx m_node; BlockchainSynchronizer m_sync; @@ -197,7 +230,7 @@ namespace { auto tx = createTransaction(); - std::vector> inputs; + std::vector> inputs; uint64_t foundMoney = 0; @@ -215,7 +248,7 @@ namespace { info.realOutput.transactionIndex = 0; info.realOutput.transactionPublicKey = t.transactionPublicKey; - KeyPair kp; + TransactionTypes::KeyPair kp; tx->addInput(senderKeys, info, kp); inputs.push_back(std::make_pair(info, kp)); @@ -309,7 +342,8 @@ TEST_F(DetachTest, testBlockchainDetach) { struct CompletionWalletObserver : public IWalletObserver { virtual void synchronizationCompleted(std::error_code result) override { - syncCompleted.set_value(result); + decltype(syncCompleted) detachedPromise = std::move(syncCompleted); + detachedPromise.set_value(result); } std::promise syncCompleted; @@ -323,7 +357,8 @@ struct WaitForExternalTransactionObserver : public CryptoNote::IWalletObserver { std::promise promise; virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override { - promise.set_value(transactionId); + decltype(promise) detachedPromise = std::move(promise); + detachedPromise.set_value(transactionId); } }; @@ -350,7 +385,7 @@ TEST_F(DetachTest, testDetachWithWallet) { Bob.removeObserver(&BobCompleted); - cryptonote::AccountPublicAddress AliceAddr; + CryptoNote::AccountPublicAddress AliceAddr; WalletAccountKeys AliceKeys; Alice.getAccountKeys(AliceKeys); AliceAddr.m_spendPublicKey = *reinterpret_cast(&AliceKeys.spendPublicKey); @@ -383,7 +418,13 @@ TEST_F(DetachTest, testDetachWithWallet) { tr.amount = Alice.actualBalance() / 2; tr.address = Bob.getAddress(); + WalletSendObserver wso; + Alice.addObserver(&wso); Alice.sendTransaction(tr, fee); + std::error_code sendError; + wso.waitForSendEnd(sendError); + Alice.removeObserver(&wso); + ASSERT_FALSE(sendError); WaitForExternalTransactionObserver etxo; auto externalTxFuture = etxo.promise.get_future(); diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index 9ad57005fa..4a5b829727 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -29,7 +29,7 @@ #include "INodeStubs.h" #include "TestBlockchainGenerator.h" - +#include class TrivialWalletObserver : public CryptoNote::IWalletObserver { @@ -59,8 +59,8 @@ class TrivialWalletObserver : public CryptoNote::IWalletObserver } virtual void synchronizationCompleted(std::error_code result) override { - synced.notify(); - } + synced.notify(); + } virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { sendResult = result; @@ -152,7 +152,7 @@ void WaitWalletLoad(TrivialWalletObserver* observer) { class WalletApi : public ::testing::Test { public: - WalletApi() : m_currency(cryptonote::CurrencyBuilder().currency()), generator(m_currency) { + WalletApi() : m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()), generator(m_currency) { } void SetUp(); @@ -167,7 +167,9 @@ class WalletApi : public ::testing::Test void TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = ""); void performTransferWithErrorTx(const std::array& amounts, uint64_t fee); - cryptonote::Currency m_currency; + + Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; TestBlockchainGenerator generator; @@ -216,7 +218,7 @@ void WalletApi::prepareCarolWallet() { } void WalletApi::GetOneBlockReward(CryptoNote::Wallet& wallet) { - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(wallet.getAddress(), address)); generator.getBlockRewardForAddress(address); } @@ -266,7 +268,7 @@ void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mix ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); ASSERT_NO_FATAL_FAILURE(GetOneBlockReward(*alice)); - //unblock Alice's money + //unlock Alice's money generator.generateEmptyBlocks(10); uint64_t expectedBalance = TEST_BLOCK_REWARD; @@ -283,8 +285,8 @@ void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mix bob->initAndGenerate("pass2"); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); - ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, 0, "")); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); generator.generateEmptyBlocks(10); @@ -324,7 +326,7 @@ TEST_F(WalletApi, refreshWithMoney) { ASSERT_EQ(alice->actualBalance(), 0); ASSERT_EQ(alice->pendingBalance(), 0); - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getBlockRewardForAddress(address); @@ -349,7 +351,7 @@ TEST_F(WalletApi, initWithMoney) { ASSERT_EQ(alice->actualBalance(), 0); ASSERT_EQ(alice->pendingBalance(), 0); - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); alice->shutdown(); @@ -678,7 +680,7 @@ TEST_F(WalletApi, wrongPassword) { std::error_code result; ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); - EXPECT_EQ(result.value(), cryptonote::error::WRONG_PASSWORD); + EXPECT_EQ(result.value(), CryptoNote::error::WRONG_PASSWORD); } TEST_F(WalletApi, detachBlockchain) { @@ -837,7 +839,7 @@ TEST_F(WalletApi, mineSaveNoCacheNoDetailsRefresh) { ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getBlockRewardForAddress(address); generator.getBlockRewardForAddress(address); @@ -868,7 +870,7 @@ TEST_F(WalletApi, sendMoneyToMyself) { ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getBlockRewardForAddress(address); generator.generateEmptyBlocks(10); @@ -878,6 +880,8 @@ TEST_F(WalletApi, sendMoneyToMyself) { CryptoNote::TransactionId txId = TransferMoney(*alice, *alice, 100000000, 100); ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + generator.generateEmptyBlocks(10); aliceNode->updateObservers(); @@ -1035,7 +1039,7 @@ TEST_F(WalletApi, checkChange) { uint64_t sendAmount = 50000; uint64_t fee = m_currency.minimumFee(); - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getSingleOutputTransaction(address, banknote); generator.generateEmptyBlocks(10); @@ -1065,7 +1069,7 @@ TEST_F(WalletApi, checkBalanceAfterSend) { uint64_t banknote = 1000000000; - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); //Once wallet takes outputs in random fashion we don't know for sure which outputs will be taken. @@ -1099,7 +1103,7 @@ TEST_F(WalletApi, moneyInPoolDontAffectActualBalance) { uint64_t banknote = 1000000000; - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getSingleOutputTransaction(address, banknote); generator.generateEmptyBlocks(10); @@ -1135,7 +1139,7 @@ TEST_F(WalletApi, balanceAfterTransactionsPlacedInBlockchain) { uint64_t banknote = 1000000000; - cryptonote::AccountPublicAddress address; + CryptoNote::AccountPublicAddress address; ASSERT_TRUE(m_currency.parseAccountAddressString(alice->getAddress(), address)); generator.getSingleOutputTransaction(address, banknote); generator.generateEmptyBlocks(10); @@ -1284,7 +1288,7 @@ TEST_F(WalletApi, sendAfterFailedTransaction) { alice->shutdown(); } -TEST_F(WalletApi, loadingBrokenCache) { +TEST_F(WalletApi, DISABLED_loadingBrokenCache) { alice->initAndGenerate("pass"); std::error_code result; diff --git a/tests/unit_tests/tx_pool.cpp b/tests/unit_tests/tx_pool.cpp index f13facc61f..3381b43c6e 100644 --- a/tests/unit_tests/tx_pool.cpp +++ b/tests/unit_tests/tx_pool.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // @@ -24,19 +24,22 @@ #include "cryptonote_core/Currency.h" #include "cryptonote_core/tx_pool.h" -using namespace cryptonote; +#include +#include + +using namespace CryptoNote; using namespace CryptoNote; class TransactionValidator : public CryptoNote::ITransactionValidator { - virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock) { + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) { return true; } - virtual bool checkTransactionInputs(const cryptonote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { return true; } - virtual bool haveSpentKeyImages(const cryptonote::Transaction& tx) { + virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) { return false; } }; @@ -55,7 +58,7 @@ class TestTransactionGenerator { public: - TestTransactionGenerator(const cryptonote::Currency& currency, size_t ringSize) : + TestTransactionGenerator(const CryptoNote::Currency& currency, size_t ringSize) : m_currency(currency), m_ringSize(ringSize), m_miners(ringSize), @@ -110,7 +113,7 @@ class TestTransactionGenerator { destinations.push_back(tx_destination_entry(amountPerOut, rv_acc.get_keys().m_account_address)); } - construct_tx(m_realSenderKeys, m_sources, destinations, std::vector(), tx, 0); + construct_tx(m_realSenderKeys, m_sources, destinations, std::vector(), tx, 0, m_logger); } std::vector m_miners; @@ -119,20 +122,30 @@ class TestTransactionGenerator { std::vector m_public_keys; std::vector m_public_key_ptrs; - const cryptonote::Currency& m_currency; + Logging::LoggerGroup m_logger; + const CryptoNote::Currency& m_currency; const size_t m_ringSize; account_keys m_realSenderKeys; uint64_t m_source_amount; account_base rv_acc; }; +class tx_pool : public ::testing::Test { +public: + tx_pool() : + currency(CryptoNote::CurrencyBuilder(logger).currency()) {} + +protected: + Logging::ConsoleLogger logger; + CryptoNote::Currency currency; +}; namespace { static const size_t textMaxCumulativeSize = std::numeric_limits::max(); - void GenerateTransaction(const cryptonote::Currency& currency, Transaction& tx, uint64_t fee, size_t outputs) { + void GenerateTransaction(const CryptoNote::Currency& currency, Transaction& tx, uint64_t fee, size_t outputs) { TestTransactionGenerator txGenerator(currency, 1); txGenerator.createSources(); txGenerator.construct(txGenerator.m_source_amount, fee, outputs, tx); @@ -145,16 +158,16 @@ namespace Validator validator; TimeProvider timeProvider; - TestPool(const cryptonote::Currency& m_currency) : - tx_memory_pool(m_currency, validator, timeProvider) {} + TestPool(const CryptoNote::Currency& currency, Logging::ILogger& logger) : + tx_memory_pool(currency, validator, timeProvider, logger) {} }; class TxTestBase { public: TxTestBase(size_t ringSize) : - m_currency(cryptonote::CurrencyBuilder().currency()), + m_currency(CryptoNote::CurrencyBuilder(m_logger).currency()), txGenerator(m_currency, ringSize), - pool(m_currency, validator, m_time) + pool(m_currency, validator, m_time, m_logger) { txGenerator.createSources(); } @@ -163,7 +176,8 @@ namespace txGenerator.construct(txGenerator.m_source_amount, fee, outputs, tx); } - cryptonote::Currency m_currency; + Logging::ConsoleLogger m_logger; + CryptoNote::Currency m_currency; CryptoNote::RealTimeProvider m_time; TestTransactionGenerator txGenerator; TransactionValidator validator; @@ -180,7 +194,7 @@ namespace } -TEST(tx_pool, add_one_tx) +TEST_F(tx_pool, add_one_tx) { TxTestBase test(1); Transaction tx; @@ -193,7 +207,7 @@ TEST(tx_pool, add_one_tx) ASSERT_FALSE(tvc.m_verifivation_failed); }; -TEST(tx_pool, take_tx) +TEST_F(tx_pool, take_tx) { TxTestBase test(1); Transaction tx; @@ -217,7 +231,7 @@ TEST(tx_pool, take_tx) }; -TEST(tx_pool, double_spend_tx) +TEST_F(tx_pool, double_spend_tx) { TxTestBase test(1); Transaction tx, tx_double; @@ -237,10 +251,9 @@ TEST(tx_pool, double_spend_tx) } -TEST(tx_pool, fillblock_same_fee) +TEST_F(tx_pool, fillblock_same_fee) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - TestPool pool(currency); + TestPool pool(currency, logger); uint64_t fee = currency.minimumFee(); std::unordered_map> transactions; @@ -292,10 +305,9 @@ TEST(tx_pool, fillblock_same_fee) } -TEST(tx_pool, fillblock_same_size) +TEST_F(tx_pool, fillblock_same_size) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - TestPool pool(currency); + TestPool pool(currency, logger); const uint64_t fee = currency.minimumFee(); const size_t totalTransactions = 50; @@ -352,10 +364,9 @@ TEST(tx_pool, fillblock_same_size) } -TEST(tx_pool, cleanup_stale_tx) +TEST_F(tx_pool, cleanup_stale_tx) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - TestPool pool(currency); + TestPool pool(currency, logger); const uint64_t fee = currency.minimumFee(); time_t startTime = pool.timeProvider.now(); @@ -396,10 +407,9 @@ TEST(tx_pool, cleanup_stale_tx) ASSERT_EQ(3, pool.get_transactions_count()); } -TEST(tx_pool, add_tx_after_cleanup) +TEST_F(tx_pool, add_tx_after_cleanup) { - cryptonote::Currency currency = cryptonote::CurrencyBuilder().currency(); - TestPool pool(currency); + TestPool pool(currency, logger); const uint64_t fee = currency.minimumFee(); time_t startTime = pool.timeProvider.now(); diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index ee95ed881e..80fb5cacbb 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // From 0e37d9f8c5c56c495b8c2e1ad0b6a7ce4586a9de Mon Sep 17 00:00:00 2001 From: DigitalNote Date: Sun, 7 Jun 2015 23:45:04 +0200 Subject: [PATCH 25/59] Checkpoints updates 132000 block (1 XDN year) checkpoint update. --- src/cryptonote_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index a7881d40bd..f9a6a5f6e7 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -140,7 +140,7 @@ const std::initializer_list CHECKPOINTS = { { 80550, "169e6b813b8ee072735bf7f7dc45b9b712b89a1317d1a4e672f6bba785a564fc" }, { 99000, "2a83ce4fbd12ccb2eb60869d11d6b4212e8a810ab33408a2feaa3066d2853d9f" }, { 122000, "926e915d84af28a8908809ae94f75bdea50d99d2d1a67fd5598bb91ccdf62c83" }, - { 128600, "4b67fd3bc0422f8fee358225df849bec01945b298257c6a09b74c905a5b896cb" } + { 132000, "b58a6b387d3120ea11061642a6a78a9a4b7800b77a44fae7ec1c73b60f2e375f" } }; } // cryptonote From 8e27da2c4cef993302e7c2c9255fa3b99196aeb7 Mon Sep 17 00:00:00 2001 From: xdn-project Date: Mon, 8 Jun 2015 17:14:44 +0300 Subject: [PATCH 26/59] Wallet RPC server improvements --- src/version.h.in | 4 ++-- src/wallet/wallet_rpc_server.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/version.h.in b/src/version.h.in index 479bcc517e..5858c51abe 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.0-beta" -#define PROJECT_VERSION_BUILD_NO "1420" +#define PROJECT_VERSION "2.0.1-beta" +#define PROJECT_VERSION_BUILD_NO "1436" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 745a5f13b0..1384369b6d 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -59,7 +59,7 @@ bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) { 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, connection_context& cntx) { try { res.balance = m_wallet.pendingBalance(); - res.unlocked_balance = m_wallet.pendingBalance(); + res.unlocked_balance = m_wallet.actualBalance(); } catch (std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); From f53761c79ae70f4a904a1ae58c759546ddb95df4 Mon Sep 17 00:00:00 2001 From: xdn-project Date: Thu, 11 Jun 2015 14:12:03 +0000 Subject: [PATCH 27/59] Wallet improvements --- src/cryptonote_core/Transaction.cpp | 208 +++++++++++--------- src/simplewallet/simplewallet.cpp | 145 ++++---------- src/simplewallet/simplewallet.h | 11 -- src/transfers/TransfersContainer.cpp | 1 + src/version.h.in | 4 +- src/wallet/WalletHelper.cpp | 101 +++++++++- src/wallet/WalletHelper.h | 29 ++- src/wallet/wallet_rpc_server.cpp | 50 ++--- src/wallet/wallet_rpc_server.h | 6 - src/wallet/wallet_rpc_server_commans_defs.h | 8 +- 10 files changed, 297 insertions(+), 266 deletions(-) diff --git a/src/cryptonote_core/Transaction.cpp b/src/cryptonote_core/Transaction.cpp index 4b109466de..f6382679c9 100644 --- a/src/cryptonote_core/Transaction.cpp +++ b/src/cryptonote_core/Transaction.cpp @@ -7,6 +7,7 @@ #include "TransactionExtra.h" #include "cryptonote_format_utils.h" +#include "cryptonote_config.h" #include "account.h" #include @@ -15,7 +16,6 @@ namespace { - using namespace cryptonote; using namespace CryptoNote; void derivePublicKey(const AccountAddress& to, const crypto::secret_key& txKey, size_t outputIndex, crypto::public_key& ephemeralKey) { @@ -24,11 +24,11 @@ namespace { crypto::derive_public_key(derivation, outputIndex, *reinterpret_cast(&to.spendPublicKey), ephemeralKey); } - bool checkInputsKeyimagesDiff(const cryptonote::Transaction& tx) { + bool checkInputsKeyimagesDiff(const cryptonote::Transaction& tx) { std::unordered_set ki; for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (!ki.insert(boost::get(in).keyImage).second) + if (in.type() == typeid(cryptonote::TransactionInputToKey)) { + if (!ki.insert(boost::get(in).keyImage).second) return false; } } @@ -38,48 +38,48 @@ namespace { // TransactionInput helper functions - size_t getRequiredSignaturesCount(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return boost::get(in).keyOffsets.size(); + size_t getRequiredSignaturesCount(const cryptonote::TransactionInput& in) { + if (in.type() == typeid(cryptonote::TransactionInputToKey)) { + return boost::get(in).keyOffsets.size(); } - if (in.type() == typeid(TransactionInputMultisignature)) { - return boost::get(in).signatures; + if (in.type() == typeid(cryptonote::TransactionInputMultisignature)) { + return boost::get(in).signatures; } return 0; } - uint64_t getTransactionInputAmount(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return boost::get(in).amount; + uint64_t getTransactionInputAmount(const cryptonote::TransactionInput& in) { + if (in.type() == typeid(cryptonote::TransactionInputToKey)) { + return boost::get(in).amount; } - if (in.type() == typeid(TransactionInputMultisignature)) { + if (in.type() == typeid(cryptonote::TransactionInputMultisignature)) { // TODO calculate interest - return boost::get(in).amount; + return boost::get(in).amount; } return 0; } - TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { + CryptoNote::TransactionTypes::InputType getTransactionInputType(const cryptonote::TransactionInput& in) { + if (in.type() == typeid(cryptonote::TransactionInputToKey)) { return TransactionTypes::InputType::Key; } - if (in.type() == typeid(TransactionInputMultisignature)) { + if (in.type() == typeid(cryptonote::TransactionInputMultisignature)) { return TransactionTypes::InputType::Multisignature; } - if (in.type() == typeid(TransactionInputGenerate)) { + if (in.type() == typeid(cryptonote::TransactionInputGenerate)) { return TransactionTypes::InputType::Generating; } return TransactionTypes::InputType::Invalid; } - const TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index) { + const cryptonote::TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index) { if (transaction.vin.size() <= index) { throw std::runtime_error("Transaction input index out of range"); } return transaction.vin[index]; } - const TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { + const cryptonote::TransactionInput& getInputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { const auto& input = getInputChecked(transaction, index); if (getTransactionInputType(input) != type) { throw std::runtime_error("Unexpected transaction input type"); @@ -89,24 +89,24 @@ namespace { // TransactionOutput helper functions - TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { - if (out.type() == typeid(TransactionOutputToKey)) { + TransactionTypes::OutputType getTransactionOutputType(const cryptonote::TransactionOutputTarget& out) { + if (out.type() == typeid(cryptonote::TransactionOutputToKey)) { return TransactionTypes::OutputType::Key; } - if (out.type() == typeid(TransactionOutputMultisignature)) { + if (out.type() == typeid(cryptonote::TransactionOutputMultisignature)) { return TransactionTypes::OutputType::Multisignature; } return TransactionTypes::OutputType::Invalid; } - const TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index) { + const cryptonote::TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index) { if (transaction.vout.size() <= index) { throw std::runtime_error("Transaction output index out of range"); } return transaction.vout[index]; } - const TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { + const cryptonote::TransactionOutput& getOutputChecked(const cryptonote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { const auto& output = getOutputChecked(transaction, index); if (getTransactionOutputType(output.target) != type) { throw std::runtime_error("Unexpected transaction output target type"); @@ -118,17 +118,18 @@ namespace { namespace CryptoNote { + using namespace cryptonote; using namespace TransactionTypes; //////////////////////////////////////////////////////////////////////// // class Transaction declaration //////////////////////////////////////////////////////////////////////// - class Transaction : public ITransaction { + class TransactionImpl : public ITransaction { public: - Transaction(); - Transaction(const Blob& txblob); - Transaction(const cryptonote::Transaction& tx); + TransactionImpl(); + TransactionImpl(const Blob& txblob); + TransactionImpl(const cryptonote::Transaction& tx); // ITransactionReader virtual Hash getTransactionHash() const override; @@ -187,6 +188,8 @@ namespace CryptoNote { private: + void invalidateHash(); + std::vector& getSignatures(size_t input); const crypto::secret_key& txSecretKey() const { @@ -196,12 +199,6 @@ namespace CryptoNote { return *secretKey; } - cryptonote::Transaction constructFinalTransaction() const { - cryptonote::Transaction tx(transaction); - tx.extra = extra.serialize(); - return tx; - } - void checkIfSigning() const { if (!transaction.signatures.empty()) { throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); @@ -210,6 +207,7 @@ namespace CryptoNote { cryptonote::Transaction transaction; boost::optional secretKey; + mutable boost::optional transactionHash; TransactionExtra extra; }; @@ -219,68 +217,80 @@ namespace CryptoNote { //////////////////////////////////////////////////////////////////////// std::unique_ptr createTransaction() { - return std::unique_ptr(new Transaction()); + return std::unique_ptr(new TransactionImpl()); } std::unique_ptr createTransaction(const Blob& transactionBlob) { - return std::unique_ptr(new Transaction(transactionBlob)); + return std::unique_ptr(new TransactionImpl(transactionBlob)); } std::unique_ptr createTransaction(const cryptonote::Transaction& tx) { - return std::unique_ptr(new Transaction(tx)); + return std::unique_ptr(new TransactionImpl(tx)); } - Transaction::Transaction() { + TransactionImpl::TransactionImpl() { cryptonote::KeyPair txKeys(cryptonote::KeyPair::generate()); + cryptonote::tx_extra_pub_key pk = { txKeys.pub }; + extra.set(pk); + transaction.version = TRANSACTION_VERSION_1; transaction.unlockTime = 0; - - tx_extra_pub_key pk = { txKeys.pub }; - extra.set(pk); + transaction.extra = extra.serialize(); secretKey = txKeys.sec; } - Transaction::Transaction(const Blob& data) { + TransactionImpl::TransactionImpl(const Blob& data) { cryptonote::blobdata blob(reinterpret_cast(data.data()), data.size()); - if (!cryptonote::parse_and_validate_tx_from_blob(blob, transaction)) { + if (!parse_and_validate_tx_from_blob(blob, transaction)) { throw std::runtime_error("Invalid transaction data"); } - + extra.parse(transaction.extra); + transactionHash = get_blob_hash(blob); // avoid serialization if we already have blob } - Transaction::Transaction(const cryptonote::Transaction& tx) : transaction(tx) { + TransactionImpl::TransactionImpl(const cryptonote::Transaction& tx) : transaction(tx) { extra.parse(transaction.extra); } - Hash Transaction::getTransactionHash() const { - auto hash = get_transaction_hash(constructFinalTransaction()); - return reinterpret_cast(hash); + void TransactionImpl::invalidateHash() { + if (transactionHash.is_initialized()) { + transactionHash = decltype(transactionHash)(); + } } - Hash Transaction::getTransactionPrefixHash() const { - auto hash = get_transaction_prefix_hash(constructFinalTransaction()); + Hash TransactionImpl::getTransactionHash() const { + if (!transactionHash.is_initialized()) { + transactionHash = get_transaction_hash(transaction); + } + + return reinterpret_cast(transactionHash.get()); + } + + Hash TransactionImpl::getTransactionPrefixHash() const { + auto hash = get_transaction_prefix_hash(transaction); return reinterpret_cast(hash); } - PublicKey Transaction::getTransactionPublicKey() const { + PublicKey TransactionImpl::getTransactionPublicKey() const { crypto::public_key pk(null_pkey); extra.getPublicKey(pk); return reinterpret_cast(pk); } - uint64_t Transaction::getUnlockTime() const { + uint64_t TransactionImpl::getUnlockTime() const { return transaction.unlockTime; } - void Transaction::setUnlockTime(uint64_t unlockTime) { + void TransactionImpl::setUnlockTime(uint64_t unlockTime) { checkIfSigning(); transaction.unlockTime = unlockTime; + invalidateHash(); } - bool Transaction::getTransactionSecretKey(SecretKey& key) const { + bool TransactionImpl::getTransactionSecretKey(SecretKey& key) const { if (!secretKey) { return false; } @@ -288,7 +298,7 @@ namespace CryptoNote { return true; } - void Transaction::setTransactionSecretKey(const SecretKey& key) { + void TransactionImpl::setTransactionSecretKey(const SecretKey& key) { const auto& sk = reinterpret_cast(key); crypto::public_key pk; crypto::public_key txPubKey; @@ -303,14 +313,15 @@ namespace CryptoNote { secretKey = reinterpret_cast(key); } - size_t Transaction::addInput(const InputKey& input) { + size_t TransactionImpl::addInput(const InputKey& input) { checkIfSigning(); TransactionInputToKey inKey = { input.amount, input.keyOffsets, *reinterpret_cast(&input.keyImage) }; transaction.vin.emplace_back(inKey); + invalidateHash(); return transaction.vin.size() - 1; } - size_t Transaction::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) { + size_t TransactionImpl::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) { checkIfSigning(); InputKey input; input.amount = info.amount; @@ -326,13 +337,14 @@ namespace CryptoNote { for (const auto& out : info.outputs) { input.keyOffsets.push_back(out.outputIndex); } - input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); + input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); return addInput(input); } - size_t Transaction::addInput(const InputMultisignature& input) { + size_t TransactionImpl::addInput(const InputMultisignature& input) { checkIfSigning(); + TransactionInputMultisignature inMsig; inMsig.amount = input.amount; inMsig.outputIndex = input.outputIndex; @@ -340,36 +352,46 @@ namespace CryptoNote { inMsig.term = input.term; transaction.vin.push_back(inMsig); transaction.version = TRANSACTION_VERSION_2; + invalidateHash(); + return transaction.vin.size() - 1; } - size_t Transaction::addOutput(uint64_t amount, const AccountAddress& to) { + size_t TransactionImpl::addOutput(uint64_t amount, const AccountAddress& to) { checkIfSigning(); + TransactionOutputToKey outKey; derivePublicKey(to, txSecretKey(), transaction.vout.size(), outKey.key); TransactionOutput out = { amount, outKey }; transaction.vout.emplace_back(out); + invalidateHash(); + return transaction.vout.size() - 1; } - size_t Transaction::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures, uint32_t term) { + size_t TransactionImpl::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures, uint32_t term) { checkIfSigning(); + const auto& txKey = txSecretKey(); size_t outputIndex = transaction.vout.size(); TransactionOutputMultisignature outMsig; outMsig.requiredSignatures = requiredSignatures; outMsig.keys.resize(to.size()); outMsig.term = term; - for (int i = 0; i < to.size(); ++i) { + + for (size_t i = 0; i < to.size(); ++i) { derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); } + TransactionOutput out = { amount, outMsig }; transaction.vout.emplace_back(out); transaction.version = TRANSACTION_VERSION_2; + invalidateHash(); + return outputIndex; } - void Transaction::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) { + void TransactionImpl::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) { const auto& input = boost::get(getInputChecked(transaction, index, InputType::Key)); Hash prefixHash = getTransactionPrefixHash(); @@ -391,9 +413,10 @@ namespace CryptoNote { signatures.data()); getSignatures(index) = signatures; + invalidateHash(); } - void Transaction::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { + void TransactionImpl::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { crypto::key_derivation derivation; crypto::public_key ephemeralPublicKey; crypto::secret_key ephemeralSecretKey; @@ -415,9 +438,10 @@ namespace CryptoNote { ephemeralPublicKey, ephemeralSecretKey, signature); getSignatures(index).push_back(signature); + invalidateHash(); } - std::vector& Transaction::getSignatures(size_t input) { + std::vector& TransactionImpl::getSignatures(size_t input) { // update signatures container size if needed if (transaction.signatures.size() < transaction.vin.size()) { transaction.signatures.resize(transaction.vin.size()); @@ -430,26 +454,22 @@ namespace CryptoNote { return transaction.signatures[input]; } - std::vector Transaction::getTransactionData() const { - return stringToVector(t_serializable_object_to_blob(constructFinalTransaction())); + std::vector TransactionImpl::getTransactionData() const { + return stringToVector(t_serializable_object_to_blob(transaction)); } - void Transaction::setPaymentId(const Hash& hash) { + void TransactionImpl::setPaymentId(const Hash& hash) { checkIfSigning(); blobdata paymentIdBlob; set_payment_id_to_tx_extra_nonce(paymentIdBlob, reinterpret_cast(hash)); setExtraNonce(paymentIdBlob); } - std::vector Transaction::getExtra() const { - if (transaction.signatures.empty()) { - return extra.serialize(); - } else { - return transaction.extra; - } + std::vector TransactionImpl::getExtra() const { + return transaction.extra; } - bool Transaction::getPaymentId(Hash& hash) const { + bool TransactionImpl::getPaymentId(Hash& hash) const { blobdata nonce; if (getExtraNonce(nonce)) { crypto::hash paymentId; @@ -461,13 +481,15 @@ namespace CryptoNote { return false; } - void Transaction::setExtraNonce(const std::string& nonce) { + void TransactionImpl::setExtraNonce(const std::string& nonce) { checkIfSigning(); tx_extra_nonce extraNonce = { nonce }; extra.set(extraNonce); + transaction.extra = extra.serialize(); + invalidateHash(); } - bool Transaction::getExtraNonce(std::string& nonce) const { + bool TransactionImpl::getExtraNonce(std::string& nonce) const { tx_extra_nonce extraNonce; if (extra.get(extraNonce)) { nonce = extraNonce.nonce; @@ -476,27 +498,27 @@ namespace CryptoNote { return false; } - size_t Transaction::getInputCount() const { + size_t TransactionImpl::getInputCount() const { return transaction.vin.size(); } - uint64_t Transaction::getInputTotalAmount() const { + uint64_t TransactionImpl::getInputTotalAmount() const { return std::accumulate(transaction.vin.begin(), transaction.vin.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { return val + getTransactionInputAmount(in); }); } - TransactionTypes::InputType Transaction::getInputType(size_t index) const { + TransactionTypes::InputType TransactionImpl::getInputType(size_t index) const { return getTransactionInputType(getInputChecked(transaction, index)); } - void Transaction::getInput(size_t index, InputKey& input) const { + void TransactionImpl::getInput(size_t index, InputKey& input) const { const auto& k = boost::get(getInputChecked(transaction, index, InputType::Key)); input.amount = k.amount; input.keyImage = reinterpret_cast(k.keyImage); input.keyOffsets = k.keyOffsets; } - void Transaction::getInput(size_t index, InputMultisignature& input) const { + void TransactionImpl::getInput(size_t index, InputMultisignature& input) const { const auto& m = boost::get(getInputChecked(transaction, index, InputType::Multisignature)); input.amount = m.amount; input.outputIndex = m.outputIndex; @@ -504,27 +526,27 @@ namespace CryptoNote { input.term = m.term; } - size_t Transaction::getOutputCount() const { + size_t TransactionImpl::getOutputCount() const { return transaction.vout.size(); } - uint64_t Transaction::getOutputTotalAmount() const { + uint64_t TransactionImpl::getOutputTotalAmount() const { return std::accumulate(transaction.vout.begin(), transaction.vout.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { return val + out.amount; }); } - TransactionTypes::OutputType Transaction::getOutputType(size_t index) const { + TransactionTypes::OutputType TransactionImpl::getOutputType(size_t index) const { return getTransactionOutputType(getOutputChecked(transaction, index).target); } - void Transaction::getOutput(size_t index, OutputKey& output) const { + void TransactionImpl::getOutput(size_t index, OutputKey& output) const { const auto& out = getOutputChecked(transaction, index, OutputType::Key); const auto& k = boost::get(out.target); output.amount = out.amount; output.key = reinterpret_cast(k.key); } - void Transaction::getOutput(size_t index, OutputMultisignature& output) const { + void TransactionImpl::getOutput(size_t index, OutputMultisignature& output) const { const auto& out = getOutputChecked(transaction, index, OutputType::Multisignature); const auto& m = boost::get(out.target); output.amount = out.amount; @@ -539,7 +561,7 @@ namespace CryptoNote { return pk == outKey; } - bool Transaction::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { + bool TransactionImpl::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { account_keys keys; keys.m_account_address = reinterpret_cast(addr); // only view secret key is used, spend key is not needed @@ -578,11 +600,11 @@ namespace CryptoNote { return true; } - size_t Transaction::getRequiredSignaturesCount(size_t index) const { + size_t TransactionImpl::getRequiredSignaturesCount(size_t index) const { return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); } - bool Transaction::validateInputs() const { + bool TransactionImpl::validateInputs() const { return check_inputs_types_supported(transaction) && check_inputs_overflow(transaction) && @@ -590,13 +612,13 @@ namespace CryptoNote { checkMultisignatureInputsDiff(transaction); } - bool Transaction::validateOutputs() const { + bool TransactionImpl::validateOutputs() const { return check_outs_valid(transaction) && check_outs_overflow(transaction); } - bool Transaction::validateSignatures() const { + bool TransactionImpl::validateSignatures() const { if (transaction.signatures.size() < transaction.vin.size()) { return false; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a5830dc1f1..8e72c9289f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -264,10 +265,9 @@ std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, con WalletHelper::InitWalletResultObserver initObserver; std::future f_initError = initObserver.initResult.get_future(); - wallet.addObserver(&initObserver); + WalletHelper::IWalletRemoveObserverGuard removeGuard(wallet, initObserver); wallet.initAndLoad(walletFile, password); auto initError = f_initError.get(); - wallet.removeObserver(&initObserver); return initError; } @@ -312,19 +312,11 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c } LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) { - throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); - } - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile, false, false); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + + try { + WalletHelper::storeWallet(*wallet, walletFileName); + } catch (std::exception& e) { + fail_msg_writer() << "Failed to store wallet: " << e.what(); throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); } @@ -343,28 +335,21 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c WalletHelper::InitWalletResultObserver initObserver; std::future f_initError = initObserver.initResult.get_future(); - wallet->addObserver(&initObserver); + + WalletHelper::IWalletRemoveObserverGuard removeGuard(*wallet, initObserver); wallet->initAndLoad(ss, password); auto initError = f_initError.get(); - wallet->removeObserver(&initObserver); + removeGuard.removeObserver(); if (initError) { throw std::runtime_error("failed to load wallet: " + initError.message()); } LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) { - throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); - } - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile, false, false); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); + + try { + WalletHelper::storeWallet(*wallet, walletFileName); + } catch(std::exception& e) { + fail_msg_writer() << "Failed to store wallet: " << e.what(); throw std::runtime_error("error saving wallet file '" + walletFileName + "'"); } @@ -399,7 +384,6 @@ simple_wallet::simple_wallet(const cryptonote::Currency& currency) : m_daemon_port(0) , m_currency(currency) , m_refresh_progress_reporter(*this) - , m_saveResultPromise(nullptr) , m_initResultPromise(nullptr) { m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); @@ -603,18 +587,12 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas fail_msg_writer() << "failed to generate new wallet: " << initError.message(); return false; } - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << "failed to save new wallet: " << saveError.message(); - return false; + + try { + WalletHelper::storeWallet(*m_wallet, m_wallet_file); + } catch (std::exception& e) { + fail_msg_writer() << "failed to save new wallet: " << e.what(); + throw; } WalletAccountKeys keys; @@ -645,19 +623,7 @@ bool simple_wallet::close_wallet() { try { - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << saveError.message(); - return false; - } + WalletHelper::storeWallet(*m_wallet, m_wallet_file); } catch (const std::exception& e) { @@ -672,19 +638,7 @@ bool simple_wallet::save(const std::vector &args) { try { - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << saveError.message(); - return false; - } + WalletHelper::storeWallet(*m_wallet, m_wallet_file); success_msg_writer() << "Wallet data saved"; } catch (const std::exception& e) @@ -763,12 +717,6 @@ void simple_wallet::initCompleted(std::error_code result) { } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::saveCompleted(std::error_code result) { - if (m_saveResultPromise.get() != nullptr) { - m_saveResultPromise->set_value(result); - } -} -//---------------------------------------------------------------------------------------------------- void simple_wallet::localBlockchainUpdated(uint64_t height) { m_refresh_progress_reporter.update(height, false); @@ -936,21 +884,20 @@ bool simple_wallet::transfer(const std::vector &args) if (!cmd.parseArguments(args)) return false; cryptonote::WalletHelper::SendCompleteResultObserver sent; - std::promise txId; - sent.expectedTxID = txId.get_future(); - std::future f_sendError = sent.sendResult.get_future(); + std::string extraString; std::copy(cmd.extra.begin(), cmd.extra.end(), std::back_inserter(extraString)); - m_wallet->addObserver(&sent); + WalletHelper::IWalletRemoveObserverGuard removeGuard(*m_wallet, sent); + CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0); if (tx == INVALID_TRANSACTION_ID) { fail_msg_writer() << "Can't send money"; return true; } - txId.set_value(tx); - std::error_code sendError = f_sendError.get(); - m_wallet->removeObserver(&sent); + + std::error_code sendError = sent.wait(tx); + removeGuard.removeObserver(); if (sendError) { fail_msg_writer() << sendError.message(); return true; @@ -961,21 +908,7 @@ bool simple_wallet::transfer(const std::vector &args) success_msg_writer(true) << "Money successfully sent, transaction " << epee::string_tools::pod_to_hex(txInfo.hash); try { - std::ofstream walletFile; - walletFile.open(m_wallet_file, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) { - fail_msg_writer() << "cant open " << m_wallet_file << " for save"; - return true; - } - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet->save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - fail_msg_writer() << saveError.message(); - return true; - } + WalletHelper::storeWallet(*m_wallet, m_wallet_file); } catch (const std::exception& e) { fail_msg_writer() << e.what(); return true; @@ -1121,6 +1054,7 @@ int main(int argc, char* argv[]) std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); int daemon_port = command_line::get_arg(vm, arg_daemon_port); + if (daemon_host.empty()) daemon_host = "localhost"; if (!daemon_port) @@ -1172,20 +1106,9 @@ int main(int argc, char* argv[]) try { LOG_PRINT_L0("Storing wallet..."); - std::ofstream walletFile; - walletFile.open(walletFileName, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - WalletHelper::SaveWalletResultObserver saveObserver; - std::future f_saveError = saveObserver.saveResult.get_future(); - wallet->addObserver(&saveObserver); - wallet->save(walletFile); - auto saveError = f_saveError.get(); - wallet->removeObserver(&saveObserver); - if (saveError) { - fail_msg_writer() << "Failed to store wallet: " << saveError.message(); - return 1; - } + + WalletHelper::storeWallet(*wallet, walletFileName); + LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0); } catch (const std::exception& e) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 0fe5d207f0..fb0e280037 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -53,7 +53,6 @@ namespace cryptonote bool help(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); - //bool refresh(const std::vector &args = std::vector()); bool show_balance(const std::vector &args = std::vector()); bool show_incoming_transfers(const std::vector &args); bool show_payments(const std::vector &args); @@ -65,19 +64,10 @@ namespace cryptonote bool reset(const std::vector &args); bool set_log(const std::vector &args); - //uint64_t get_daemon_blockchain_height(std::string& err); - //bool try_connect_to_daemon(); bool ask_wallet_create_if_needed(); - ////----------------- i_wallet2_callback --------------------- - //virtual void on_money_received(uint64_t height, const cryptonote::Transaction& tx, size_t out_index); - //virtual void on_money_spent(uint64_t height, const cryptonote::Transaction& in_tx, size_t out_index, const cryptonote::Transaction& spend_tx); - //virtual void on_skip_transaction(uint64_t height, const cryptonote::Transaction& tx); - ////---------------------------------------------------------- - //---------------- IWalletObserver ------------------------- virtual void initCompleted(std::error_code result) override; - virtual void saveCompleted(std::error_code result) override; virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override; //---------------------------------------------------------- @@ -149,7 +139,6 @@ namespace cryptonote std::string m_wallet_file; std::unique_ptr> m_initResultPromise; - std::unique_ptr> m_saveResultPromise; epee::console_handlers_binder m_cmd_binder; diff --git a/src/transfers/TransfersContainer.cpp b/src/transfers/TransfersContainer.cpp index 3a18b105ba..2d9cfcb189 100644 --- a/src/transfers/TransfersContainer.cpp +++ b/src/transfers/TransfersContainer.cpp @@ -195,6 +195,7 @@ void TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti txInfo.publicKey = tx.getTransactionPublicKey(); txInfo.totalAmountIn = tx.getInputTotalAmount(); txInfo.totalAmountOut = tx.getOutputTotalAmount(); + txInfo.extra = tx.getExtra(); txInfo.messages = std::move(messages); if (!tx.getPaymentId(txInfo.paymentId)) { diff --git a/src/version.h.in b/src/version.h.in index 5858c51abe..33730550ea 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.1-beta" -#define PROJECT_VERSION_BUILD_NO "1436" +#define PROJECT_VERSION "2.0.2-beta" +#define PROJECT_VERSION_BUILD_NO "1487" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/WalletHelper.cpp b/src/wallet/WalletHelper.cpp index 7704a29dd2..b05b84f584 100644 --- a/src/wallet/WalletHelper.cpp +++ b/src/wallet/WalletHelper.cpp @@ -4,16 +4,49 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "WalletHelper.h" + +#include +#include #include #include "string_tools.h" #include "cryptonote_protocol/blobdatatype.h" -using namespace cryptonote; using namespace epee; +namespace cryptonote { +namespace WalletHelper { + +namespace { + +void openOutputFileStream(const std::string& filename, std::ofstream& file) { + file.open(filename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (file.fail()) { + throw std::runtime_error("error opening file: " + filename); + } +} + +std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { + cryptonote::WalletHelper::SaveWalletResultObserver o; + + std::error_code e; + try { + std::future f = o.saveResult.get_future(); + wallet.addObserver(&o); + wallet.save(file, saveDetailes, saveCache); + e = f.get(); + } catch (std::exception&) { + wallet.removeObserver(&o); + return make_error_code(std::errc::invalid_argument); + } + + wallet.removeObserver(&o); + return e; +} + +} -void WalletHelper::prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { +void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { if (string_tools::get_extension(file_path) == "wallet") { keys_file = string_tools::cut_off_extension(file_path) + ".keys"; wallet_file = file_path; @@ -25,3 +58,67 @@ void WalletHelper::prepareFileNames(const std::string& file_path, std::string& k wallet_file = file_path + ".wallet"; } } + +void storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename) { + boost::filesystem::path tempFile = boost::filesystem::unique_path(walletFilename + ".tmp.%%%%-%%%%"); + + if (boost::filesystem::exists(walletFilename)) { + boost::filesystem::rename(walletFilename, tempFile); + } + + std::ofstream file; + try { + openOutputFileStream(walletFilename, file); + } catch (std::exception&) { + if (boost::filesystem::exists(tempFile)) { + boost::filesystem::rename(tempFile, walletFilename); + } + throw; + } + + std::error_code saveError = walletSaveWrapper(wallet, file, true, true); + if (saveError) { + file.close(); + boost::filesystem::remove(walletFilename); + boost::filesystem::rename(tempFile, walletFilename); + throw std::system_error(saveError); + } + + file.close(); + + boost::system::error_code ignore; + boost::filesystem::remove(tempFile, ignore); +} + +void SendCompleteResultObserver::sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) { + std::lock_guard lock(m_mutex); + m_finishedTransactions[transactionId] = result; + m_condition.notify_one(); +} + +std::error_code SendCompleteResultObserver::wait(CryptoNote::TransactionId transactionId) { + std::unique_lock lock(m_mutex); + m_condition.wait(lock, [this, &transactionId] { return m_finishedTransactions.find(transactionId) != m_finishedTransactions.end(); }); + return m_finishedTransactions.find(transactionId)->second; +} + +IWalletRemoveObserverGuard::IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer) : + m_wallet(wallet), + m_observer(observer), + m_removed(false) { + m_wallet.addObserver(&m_observer); +} + +IWalletRemoveObserverGuard::~IWalletRemoveObserverGuard() { + if (!m_removed) { + m_wallet.removeObserver(&m_observer); + } +} + +void IWalletRemoveObserverGuard::removeObserver() { + m_wallet.removeObserver(&m_observer); + m_removed = true; +} + +} +} diff --git a/src/wallet/WalletHelper.h b/src/wallet/WalletHelper.h index 831b5f2512..f799fe2c7b 100644 --- a/src/wallet/WalletHelper.h +++ b/src/wallet/WalletHelper.h @@ -6,11 +6,12 @@ #pragma once #include +#include +#include #include "crypto/hash.h" #include "IWallet.h" - namespace cryptonote { namespace WalletHelper { @@ -28,13 +29,29 @@ class InitWalletResultObserver : public CryptoNote::IWalletObserver { class SendCompleteResultObserver : public CryptoNote::IWalletObserver { public: - std::future expectedTxID; - std::promise sendResult; - virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override { - if (transactionId == expectedTxID.get()) sendResult.set_value(result); - } + virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override; + + std::error_code wait(CryptoNote::TransactionId transactionId); + +private: + std::mutex m_mutex; + std::condition_variable m_condition; + std::map m_finishedTransactions; +}; + +class IWalletRemoveObserverGuard { +public: + IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer); + ~IWalletRemoveObserverGuard(); + + void removeObserver(); +private: + CryptoNote::IWallet& m_wallet; + CryptoNote::IWalletObserver& m_observer; + bool m_removed; }; void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file); +void storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename); } } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 1384369b6d..e8b0f66bb8 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -29,19 +29,13 @@ void wallet_rpc_server::init_options(boost::program_options::options_description command_line::add_arg(desc, arg_rpc_bind_port); } //------------------------------------------------------------------------------------------------------------------------------ -wallet_rpc_server::wallet_rpc_server(CryptoNote::IWallet&w, CryptoNote::INode& n, cryptonote::Currency& currency, const std::string& walletFile) :m_wallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile), m_saveResultPromise(nullptr) { +wallet_rpc_server::wallet_rpc_server(CryptoNote::IWallet&w, CryptoNote::INode& n, cryptonote::Currency& currency, const std::string& walletFile) :m_wallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile) { } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { //DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING return epee::http_server_impl_base::run(1, true); } -//---------------------------------------------------------------------------------------------------- -void wallet_rpc_server::saveCompleted(std::error_code result) { - if (m_saveResultPromise.get() != nullptr) { - m_saveResultPromise->set_value(result); - } -} //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); @@ -58,8 +52,10 @@ bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) { //------------------------------------------------------------------------------------------------------------------------------ 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, connection_context& cntx) { try { - res.balance = m_wallet.pendingBalance(); - res.unlocked_balance = m_wallet.actualBalance(); + res.locked_amount = m_wallet.pendingBalance(); + res.available_balance = m_wallet.actualBalance(); + res.balance = res.locked_amount + res.available_balance; + res.unlocked_balance = res.available_balance; } catch (std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -106,15 +102,17 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ std::copy(extra.begin(), extra.end(), std::back_inserter(extraString)); try { cryptonote::WalletHelper::SendCompleteResultObserver sent; - std::promise txId; - sent.expectedTxID = txId.get_future(); - std::future f_sendError = sent.sendResult.get_future(); + WalletHelper::IWalletRemoveObserverGuard removeGuard(m_wallet, sent); - m_wallet.addObserver(&sent); CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time, messages); - txId.set_value(tx); - std::error_code sendError = f_sendError.get(); - m_wallet.removeObserver(&sent); + if (tx == INVALID_TRANSACTION_ID) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + + std::error_code sendError = sent.wait(tx); + removeGuard.removeObserver(); if (sendError) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = sendError.message(); @@ -146,27 +144,13 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ //------------------------------------------------------------------------------------------------------------------------------ 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, connection_context& cntx) { try { - std::ofstream walletFile; - walletFile.open(m_walletFilename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); - if (walletFile.fail()) - return false; - m_wallet.addObserver(this); - m_saveResultPromise.reset(new std::promise()); - std::future f_saveError = m_saveResultPromise->get_future(); - m_wallet.save(walletFile); - auto saveError = f_saveError.get(); - m_saveResultPromise.reset(nullptr); - if (saveError) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = saveError.message(); - return false; - } - m_wallet.removeObserver(this); + WalletHelper::storeWallet(m_wallet, m_walletFilename); } catch (std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); return false; } + return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -204,7 +188,7 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN wallet_rpc::payment_details rpc_payment; rpc_payment.tx_hash = epee::string_tools::pod_to_hex(txInfo.hash); rpc_payment.amount = txInfo.totalAmount; - rpc_payment.block_height = txInfo.totalAmount; + rpc_payment.block_height = txInfo.blockHeight; rpc_payment.unlock_time = txInfo.unlockTime; res.payments.push_back(rpc_payment); } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 621e61a0d6..c1ba3ea8fc 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -27,10 +27,6 @@ namespace tools const static command_line::arg_descriptor arg_rpc_bind_port; const static command_line::arg_descriptor arg_rpc_bind_ip; - //---------------- IWalletObserver ------------------------- - virtual void saveCompleted(std::error_code result) override; - //---------------------------------------------------------- - static void init_options(boost::program_options::options_description& desc); bool init(const boost::program_options::variables_map& vm); bool run(); @@ -67,7 +63,5 @@ namespace tools std::string m_bind_ip; cryptonote::Currency& m_currency; const std::string m_walletFilename; - - std::unique_ptr> m_saveResultPromise; }; } diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index ab517dcae2..e5cc35592e 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -25,10 +25,14 @@ namespace wallet_rpc struct response { - uint64_t balance; - uint64_t unlocked_balance; + uint64_t locked_amount; + uint64_t available_balance; + uint64_t balance; // Date: Wed, 17 Jun 2015 16:31:40 +0000 Subject: [PATCH 28/59] Wallet RPC server improvements --- src/version.h.in | 4 ++-- src/wallet/wallet_rpc_server.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/version.h.in b/src/version.h.in index 33730550ea..8045c85664 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.2-beta" -#define PROJECT_VERSION_BUILD_NO "1487" +#define PROJECT_VERSION "2.0.3-beta" +#define PROJECT_VERSION_BUILD_NO "1490" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e8b0f66bb8..b96f3eb597 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -220,7 +220,7 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS transfer.time = txInfo.timestamp; transfer.output = txInfo.totalAmount < 0; transfer.transactionHash = epee::string_tools::pod_to_hex(txInfo.hash); - transfer.amount = txInfo.totalAmount; + transfer.amount = std::abs(txInfo.totalAmount); transfer.fee = txInfo.fee; transfer.address = address; transfer.blockIndex = txInfo.blockHeight; From 6ee9db0ee05483664a1e961e5b5d7aa1203fa725 Mon Sep 17 00:00:00 2001 From: xdn-project Date: Fri, 19 Jun 2015 12:36:12 +0000 Subject: [PATCH 29/59] Update checkpoints --- src/cryptonote_config.h | 6 ++++-- src/version.h.in | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index f9a6a5f6e7..e581d93b31 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -58,7 +58,7 @@ const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS = DIFFICULTY_TARGET const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = (60 * 60 * 14); //seconds, 14 hours const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = (60 * 60 * 24); //seconds, one day -const uint64_t UPGRADE_HEIGHT = static_cast(-1); +const uint64_t UPGRADE_HEIGHT = 136212; const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent const size_t UPGRADE_VOTING_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks const size_t UPGRADE_WINDOW = EXPECTED_NUMBER_OF_BLOCKS_PER_DAY; // blocks @@ -140,7 +140,9 @@ const std::initializer_list CHECKPOINTS = { { 80550, "169e6b813b8ee072735bf7f7dc45b9b712b89a1317d1a4e672f6bba785a564fc" }, { 99000, "2a83ce4fbd12ccb2eb60869d11d6b4212e8a810ab33408a2feaa3066d2853d9f" }, { 122000, "926e915d84af28a8908809ae94f75bdea50d99d2d1a67fd5598bb91ccdf62c83" }, - { 132000, "b58a6b387d3120ea11061642a6a78a9a4b7800b77a44fae7ec1c73b60f2e375f" } + { 132000, "b58a6b387d3120ea11061642a6a78a9a4b7800b77a44fae7ec1c73b60f2e375f" }, + { 136212, "5a935b048194d8b6ffb33b744c73cbe632da4f3c4d5e4c4488967d9431ba2a36" }, + { 136213, "336b687fdb96457cf4060072f76fc9e4e9281744822e0892c9ea128445bbebc7" }, }; } // cryptonote diff --git a/src/version.h.in b/src/version.h.in index 8045c85664..1ea48a574a 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.3-beta" -#define PROJECT_VERSION_BUILD_NO "1490" +#define PROJECT_VERSION "2.0.4-beta" +#define PROJECT_VERSION_BUILD_NO "1491" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" From b7b965382988fddda098324321ea52132dc68163 Mon Sep 17 00:00:00 2001 From: xdn-project Date: Tue, 23 Jun 2015 09:47:10 +0000 Subject: [PATCH 30/59] Simplewallet and Wallet RPC server reset fix --- src/cryptonote_config.h | 1 + src/simplewallet/simplewallet.cpp | 57 +++++++++++++++++++++---------- src/simplewallet/simplewallet.h | 1 + src/version.h.in | 4 +-- src/wallet/WalletHelper.cpp | 35 +++++++++++-------- src/wallet/WalletHelper.h | 2 ++ src/wallet/wallet_rpc_server.cpp | 56 +++++++++++++++++++++++------- src/wallet/wallet_rpc_server.h | 7 ++-- 8 files changed, 114 insertions(+), 49 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e581d93b31..4fdabb4198 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -143,6 +143,7 @@ const std::initializer_list CHECKPOINTS = { { 132000, "b58a6b387d3120ea11061642a6a78a9a4b7800b77a44fae7ec1c73b60f2e375f" }, { 136212, "5a935b048194d8b6ffb33b744c73cbe632da4f3c4d5e4c4488967d9431ba2a36" }, { 136213, "336b687fdb96457cf4060072f76fc9e4e9281744822e0892c9ea128445bbebc7" }, + { 137000, "ae73be718076ab00371f81fa5f604c9e020f25abcb48f85b631bde0cabaff048" }, }; } // cryptonote diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 8e72c9289f..5e0b689d95 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -261,17 +261,6 @@ struct TransferCommand { } }; -std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, const std::string& password) { - WalletHelper::InitWalletResultObserver initObserver; - std::future f_initError = initObserver.initResult.get_future(); - - WalletHelper::IWalletRemoveObserverGuard removeGuard(wallet, initObserver); - wallet.initAndLoad(walletFile, password); - auto initError = f_initError.get(); - - return initError; -} - std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { std::string keys_file, walletFileName; WalletHelper::prepareFileNames(walletFile, keys_file, walletFileName); @@ -296,7 +285,7 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c throw std::runtime_error("error opening wallet file '" + walletFileName + "'"); } - auto initError = initAndLoadWallet(*wallet, walletFile, password); + auto initError = WalletHelper::initAndLoadWallet(*wallet, walletFile, password); walletFile.close(); if (initError) { //bad password, or legacy format @@ -306,7 +295,7 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(std::unique_ptr& wallet, c boost::filesystem::rename(keys_file, keys_file + ".back"); boost::filesystem::rename(walletFileName, walletFileName + ".back"); - initError = initAndLoadWallet(*wallet, ss, password); + initError = WalletHelper::initAndLoadWallet(*wallet, ss, password); if (initError) { throw std::runtime_error("failed to load wallet: " + initError.message()); } @@ -493,7 +482,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if (m_daemon_address.empty()) m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port); - tools::password_container pwd_container; if (command_line::has_arg(vm, arg_password)) { pwd_container.password(command_line::get_arg(vm, arg_password)); @@ -650,7 +638,38 @@ bool simple_wallet::save(const std::vector &args) } bool simple_wallet::reset(const std::vector &args) { - m_wallet->reset(); + if (pwd_container.empty()) { + fail_msg_writer() << "password not set"; + return false; + } + + // die on exception + try { + std::stringstream stream; + // save without cache + auto error = WalletHelper::walletSaveWrapper(*m_wallet, stream, false, false); + if (error) { + fail_msg_writer() << "failed to save wallet to stream"; + return false; + } + + m_wallet->removeObserver(this); + m_wallet->shutdown(); + + m_wallet.reset(new Wallet(m_currency, *m_node)); + error = WalletHelper::initAndLoadWallet(*m_wallet, stream, pwd_container.password()); + if (error) { + throw std::runtime_error("failed to reinitialize wallet"); + } + + m_wallet->addObserver(this); + + } catch (std::exception& e) { + // kill simple wallet if something is wrong + fail_msg_writer() << "failed to reset wallet: " << e.what(); + std::abort(); + } + success_msg_writer(true) << "Reset is complete successfully"; return true; } @@ -1050,7 +1069,7 @@ int main(int argc, char* argv[]) } std::string wallet_file = command_line::get_arg(vm, arg_wallet_file); - std::string wallet_password = command_line::get_arg(vm, arg_password); + tools::password_container pass(command_line::get_arg(vm, arg_password)); std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); int daemon_port = command_line::get_arg(vm, arg_daemon_port); @@ -1082,7 +1101,7 @@ int main(int argc, char* argv[]) std::string walletFileName; try { - walletFileName = ::tryToOpenWalletOrLoadKeysOrThrow(wallet, wallet_file, wallet_password); + walletFileName = ::tryToOpenWalletOrLoadKeysOrThrow(wallet, wallet_file, pass.password()); LOG_PRINT_L1("available balance: " << currency.formatAmount(wallet->actualBalance()) << ", locked amount: " << currency.formatAmount(wallet->pendingBalance())); LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0); @@ -1093,7 +1112,9 @@ int main(int argc, char* argv[]) return 1; } - tools::wallet_rpc_server wrpc(*wallet, *node, currency, walletFileName); + // as long as this server is always single threaded it is safe to pass and use refernce to wallet pointer here + // without syncronization + tools::wallet_rpc_server wrpc(wallet, *node, currency, walletFileName, pass); wrpc.init(vm); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server"); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index fb0e280037..2c0d08d771 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -137,6 +137,7 @@ namespace cryptonote int m_daemon_port; std::string m_wallet_file; + tools::password_container pwd_container; std::unique_ptr> m_initResultPromise; diff --git a/src/version.h.in b/src/version.h.in index 1ea48a574a..1be3a41187 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.4-beta" -#define PROJECT_VERSION_BUILD_NO "1491" +#define PROJECT_VERSION "2.0.5-beta" +#define PROJECT_VERSION_BUILD_NO "1502" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/WalletHelper.cpp b/src/wallet/WalletHelper.cpp index b05b84f584..5b6c71fc0c 100644 --- a/src/wallet/WalletHelper.cpp +++ b/src/wallet/WalletHelper.cpp @@ -26,24 +26,31 @@ void openOutputFileStream(const std::string& filename, std::ofstream& file) { } } -std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { - cryptonote::WalletHelper::SaveWalletResultObserver o; +} - std::error_code e; - try { - std::future f = o.saveResult.get_future(); - wallet.addObserver(&o); - wallet.save(file, saveDetailes, saveCache); - e = f.get(); - } catch (std::exception&) { - wallet.removeObserver(&o); - return make_error_code(std::errc::invalid_argument); - } +std::error_code initAndLoadWallet(CryptoNote::IWallet& wallet, std::istream& stream, const std::string& password) { + WalletHelper::InitWalletResultObserver initObserver; + auto f_initError = initObserver.initResult.get_future(); - wallet.removeObserver(&o); - return e; + WalletHelper::IWalletRemoveObserverGuard removeGuard(wallet, initObserver); + wallet.initAndLoad(stream, password); + auto initError = f_initError.get(); + + return initError; } +std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ostream& stream, bool saveDetailes, bool saveCache) { + std::error_code err; + cryptonote::WalletHelper::SaveWalletResultObserver observer; + try { + auto future = observer.saveResult.get_future(); + WalletHelper::IWalletRemoveObserverGuard guard(wallet, observer); + wallet.save(stream, saveDetailes, saveCache); + err = future.get(); + } catch (std::exception&) { + err = make_error_code(std::errc::invalid_argument); + } + return err; } void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file) { diff --git a/src/wallet/WalletHelper.h b/src/wallet/WalletHelper.h index f799fe2c7b..02149c7043 100644 --- a/src/wallet/WalletHelper.h +++ b/src/wallet/WalletHelper.h @@ -53,5 +53,7 @@ class IWalletRemoveObserverGuard { void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file); void storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename); +std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ostream& stream, bool saveDetailes, bool saveCache); +std::error_code initAndLoadWallet(CryptoNote::IWallet& wallet, std::istream& stream, const std::string& password); } } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index b96f3eb597..eca3ec6fd1 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -29,7 +29,8 @@ void wallet_rpc_server::init_options(boost::program_options::options_description command_line::add_arg(desc, arg_rpc_bind_port); } //------------------------------------------------------------------------------------------------------------------------------ -wallet_rpc_server::wallet_rpc_server(CryptoNote::IWallet&w, CryptoNote::INode& n, cryptonote::Currency& currency, const std::string& walletFile) :m_wallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile) { +wallet_rpc_server::wallet_rpc_server(std::unique_ptr& w, CryptoNote::INode& n, cryptonote::Currency& currency, const std::string& walletFile, const password_container& pass) : m_wallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile), m_pass(pass) { + assert(m_wallet.get() != nullptr); } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run() { @@ -52,8 +53,8 @@ bool wallet_rpc_server::init(const boost::program_options::variables_map& vm) { //------------------------------------------------------------------------------------------------------------------------------ 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, connection_context& cntx) { try { - res.locked_amount = m_wallet.pendingBalance(); - res.available_balance = m_wallet.actualBalance(); + res.locked_amount = m_wallet->pendingBalance(); + res.available_balance = m_wallet->actualBalance(); res.balance = res.locked_amount + res.available_balance; res.unlocked_balance = res.available_balance; } catch (std::exception& e) { @@ -102,9 +103,9 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ std::copy(extra.begin(), extra.end(), std::back_inserter(extraString)); try { cryptonote::WalletHelper::SendCompleteResultObserver sent; - WalletHelper::IWalletRemoveObserverGuard removeGuard(m_wallet, sent); + WalletHelper::IWalletRemoveObserverGuard removeGuard(*m_wallet, sent); - CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time, messages); + CryptoNote::TransactionId tx = m_wallet->sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time, messages); if (tx == INVALID_TRANSACTION_ID) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; @@ -120,7 +121,7 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ } CryptoNote::TransactionInfo txInfo; - m_wallet.getTransaction(tx, txInfo); + m_wallet->getTransaction(tx, txInfo); std::string hexHash; std::copy(txInfo.hash.begin(), txInfo.hash.end(), std::back_inserter(hexHash)); @@ -144,7 +145,7 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ //------------------------------------------------------------------------------------------------------------------------------ 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, connection_context& cntx) { try { - WalletHelper::storeWallet(m_wallet, m_walletFilename); + WalletHelper::storeWallet(*m_wallet, m_walletFilename); } catch (std::exception& e) { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = e.what(); @@ -170,10 +171,10 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN } expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); - size_t transactionsCount = m_wallet.getTransactionCount(); + size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { TransactionInfo txInfo; - m_wallet.getTransaction(trantransactionNumber, txInfo); + m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -199,10 +200,10 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN 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, connection_context& cntx) { res.transfers.clear(); - size_t transactionsCount = m_wallet.getTransactionCount(); + size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { TransactionInfo txInfo; - m_wallet.getTransaction(trantransactionNumber, txInfo); + m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -211,7 +212,7 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS if (txInfo.totalAmount < 0) { if (txInfo.transferCount > 0) { Transfer tr; - m_wallet.getTransfer(txInfo.firstTransferId, tr); + m_wallet->getTransfer(txInfo.firstTransferId, tr); address = tr.address; } } @@ -246,7 +247,36 @@ bool wallet_rpc_server::on_get_height(const wallet_rpc::COMMAND_RPC_GET_HEIGHT:: } bool wallet_rpc_server::on_reset(const wallet_rpc::COMMAND_RPC_RESET::request& req, wallet_rpc::COMMAND_RPC_RESET::response& res, epee::json_rpc::error& er, connection_context& cntx) { - m_wallet.reset(); + if (m_pass.empty()) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "password not set"; + return false; + } + + // die on exception + try { + std::stringstream stream; + auto error = WalletHelper::walletSaveWrapper(*m_wallet, stream, false, false); + if (error) { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "failed to save wallet"; + return false; + } + + m_wallet->shutdown(); + + m_wallet.reset(new Wallet(m_currency, m_node)); + error = WalletHelper::initAndLoadWallet(*m_wallet, stream, m_pass.password()); + if (error) { + throw std::runtime_error("failed to reinitialize wallet"); + } + + } catch (std::exception& e) { + // kill simple wallet if something is wrong + std::cerr << "reset rpc request failed" << std::endl; + std::abort(); + } + return true; } diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index c1ba3ea8fc..aebdb00964 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -12,6 +12,7 @@ #include "wallet_rpc_server_commans_defs.h" #include "Wallet.h" #include "common/command_line.h" +#include "simplewallet/password_container.h" namespace tools { /************************************************************************/ @@ -22,7 +23,8 @@ namespace tools public: typedef epee::net_utils::connection_context_base connection_context; - wallet_rpc_server(CryptoNote::IWallet &w, CryptoNote::INode &n, cryptonote::Currency& currency, const std::string& walletFilename); + wallet_rpc_server(std::unique_ptr& w, CryptoNote::INode& n, cryptonote::Currency& currency, + const std::string& walletFilename, const password_container& pass); const static command_line::arg_descriptor arg_rpc_bind_port; const static command_line::arg_descriptor arg_rpc_bind_ip; @@ -57,11 +59,12 @@ namespace tools bool handle_command_line(const boost::program_options::variables_map& vm); - CryptoNote::IWallet& m_wallet; + std::unique_ptr& m_wallet; CryptoNote::INode& m_node; std::string m_port; std::string m_bind_ip; cryptonote::Currency& m_currency; const std::string m_walletFilename; + const tools::password_container& m_pass; }; } From da084d6e4a866650295de2ad3df3a160a1f4e748 Mon Sep 17 00:00:00 2001 From: xdn-project Date: Fri, 26 Jun 2015 17:11:17 +0000 Subject: [PATCH 31/59] get_payments RPC method improvement --- CMakeLists.txt | 3 + include/IWallet.h | 10 +++ src/simplewallet/simplewallet.cpp | 88 +++++++++---------- src/version.h.in | 4 +- src/wallet/Wallet.cpp | 4 + src/wallet/Wallet.h | 1 + src/wallet/WalletUserTransactionsCache.cpp | 84 +++++++++++++++++- src/wallet/WalletUserTransactionsCache.h | 12 ++- src/wallet/wallet_rpc_server.cpp | 36 +++----- tests/core_tests/upgrade.cpp | 1 + .../test_WalletUserTransactionsCache.cpp | 83 +++++++++++++++++ tests/unit_tests/test_wallet.cpp | 70 +++++++++++++++ 12 files changed, 323 insertions(+), 73 deletions(-) create mode 100644 tests/unit_tests/test_WalletUserTransactionsCache.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 102399c4cc..98ea1577d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,9 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes") if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_HAS_TR1_TUPLE=0") + else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8)) set(DEBUG_FLAGS "-g3 -Og") diff --git a/include/IWallet.h b/include/IWallet.h index fc5859439b..62893106e5 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace CryptoNote { @@ -82,6 +83,14 @@ struct WalletAccountKeys { WalletSecretKey spendSecretKey; }; +using PaymentId = std::array; +struct Payments { + PaymentId paymentId; + std::vector transactions; +}; + +static_assert(std::is_move_constructible::value, "Payments is not move constructible"); + class IWalletObserver { public: virtual ~IWalletObserver() {} @@ -125,6 +134,7 @@ class IWallet { virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) = 0; virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0; + virtual std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const = 0; virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()) = 0; virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()) = 0; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5e0b689d95..0f7e04eb15 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -3,11 +3,12 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include -#include +#include #include +#include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include "p2p/net_node.h" #include "rpc/core_rpc_server_commands_defs.h" #include "simplewallet.h" +#include "transfers/TypeHelpers.h" #include "wallet/wallet_rpc_server.h" #include "version.h" #include "wallet/WalletHelper.h" @@ -790,7 +792,7 @@ bool simple_wallet::listTransfers(const std::vector& args) { for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { TransactionInfo txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active) { + if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -828,55 +830,53 @@ bool simple_wallet::listTransfers(const std::vector& args) { return true; } -bool simple_wallet::show_payments(const std::vector &args) -{ - if (args.empty()) - { +bool simple_wallet::show_payments(const std::vector &args) { + if (args.empty()) { fail_msg_writer() << "expected at least one payment ID"; return true; } - message_writer() << " payment \t" << - " transaction \t" << - " height\t amount "; - - bool payments_found = false; - for (const std::string& arg: args) - { - crypto::hash expectedPaymentId; - if (cryptonote::parsePaymentId(arg, expectedPaymentId)) - { - - size_t transactionsCount = m_wallet->getTransactionCount(); - for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; - m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.totalAmount < 0) continue; - std::vector extraVec; - extraVec.reserve(txInfo.extra.size()); - std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - - crypto::hash paymentId; - if (cryptonote::getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { - payments_found = true; - success_msg_writer(true) << - paymentId << "\t\t" << - epee::string_tools::pod_to_hex(txInfo.hash) << - std::setw(8) << txInfo.blockHeight << '\t' << - std::setw(21) << m_currency.formatAmount(txInfo.totalAmount);// << '\t' << - } + try { + auto hashes = args; + std::sort(std::begin(hashes), std::end(hashes)); + hashes.erase(std::unique(std::begin(hashes), std::end(hashes)), std::end(hashes)); + std::vector paymentIds; + paymentIds.reserve(hashes.size()); + std::transform(std::begin(hashes), std::end(hashes), std::back_inserter(paymentIds), [](const std::string& arg) { + crypto::hash expectedPaymentId; + if (!cryptonote::parsePaymentId(arg, expectedPaymentId)) { + throw std::runtime_error("payment ID has invalid format: \"" + arg + "\", expected 64-character string"); + } + + PaymentId paymentId; + static_assert(sizeof(PaymentId) == sizeof(crypto::hash), "size of PaymentId and crypto::hash doesn't match"); + std::copy_n(reinterpret_cast(&expectedPaymentId), sizeof(expectedPaymentId), std::begin(paymentId)); + return paymentId; + }); + + message_writer() << " payment \t" << + " transaction \t" << + " height\t amount "; + + auto payments = m_wallet->getTransactionsByPaymentIds(paymentIds); + + for (auto& payment : payments) { + for (auto& transaction : payment.transactions) { + success_msg_writer(true) << + epee::string_tools::pod_to_hex(payment.paymentId) << '\t' << + epee::string_tools::pod_to_hex(transaction.hash) << '\t' << + std::setw(8) << transaction.blockHeight << '\t' << + std::setw(21) << m_currency.formatAmount(transaction.totalAmount); // << '\t' << } - if (!payments_found) - { - success_msg_writer() << "No payments with id " << expectedPaymentId; - continue; + if (payment.transactions.empty()) { + success_msg_writer() << "No payments with id " << epee::string_tools::pod_to_hex(payment.paymentId); } + } - else - { - fail_msg_writer() << "payment ID has invalid format: \"" << arg << "\", expected 64-character string"; - } + + } catch (std::exception& e) { + fail_msg_writer() << "show_payments exception: " << e.what(); } return true; diff --git a/src/version.h.in b/src/version.h.in index 1be3a41187..934925d249 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.5-beta" -#define PROJECT_VERSION_BUILD_NO "1502" +#define PROJECT_VERSION "2.0.6-beta" +#define PROJECT_VERSION_BUILD_NO "1507" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index 077bc73b39..424801ceed 100644 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -321,6 +321,10 @@ void Wallet::reset() { removeObserver(&initWaiter); } +std::vector Wallet::getTransactionsByPaymentIds(const std::vector& paymentIds) const { + return m_transactionsCache.getTransactionsByPaymentIds(paymentIds); +} + void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { if(m_isStopping) { m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(cryptonote::error::OPERATION_CANCELLED)); diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index 7594ffab1f..63b53f82b3 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -66,6 +66,7 @@ class Wallet : virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction); virtual bool getTransfer(TransferId transferId, Transfer& transfer); + virtual std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const override; virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()); virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()); diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index 743e23bc9a..647f46625c 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -15,6 +15,7 @@ #include "serialization/ISerializer.h" #include "serialization/SerializationOverloads.h" #include +#include namespace CryptoNote { @@ -28,11 +29,63 @@ void WalletUserTransactionsCache::serialize(cryptonote::ISerializer& s, const st if (s.type() == cryptonote::ISerializer::INPUT) { updateUnconfirmedTransactions(); + rebuildPaymentsIndex(); } s.endObject(); } +bool paymentIdIsSet(const PaymentId& paymentId) { + return std::all_of(std::begin(paymentId), std::end(paymentId), [](PaymentId::value_type v) { return v != 0; }); +} + +bool canInsertTransactionToIndex(const TransactionInfo& info) { + return info.state == TransactionState::Active && info.blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT && + info.totalAmount > 0 && !info.extra.empty(); +} + +bool extractPaymentId(const std::vector& extra, PaymentId& paymentId) { + crypto::hash hash; + if (!cryptonote::getPaymentIdFromTxExtra(extra, hash)) { + return false; + } + std::copy_n(reinterpret_cast(&hash), sizeof(crypto::hash), std::begin(paymentId)); + return true; +} + +void WalletUserTransactionsCache::pushToPaymentsIndex(const PaymentId& paymentId, Offset distance) { + m_paymentsIndex[paymentId].push_back(distance); +} + +void WalletUserTransactionsCache::popFromPaymentsIndex(const PaymentId& paymentId, Offset distance) { + auto it = m_paymentsIndex.find(paymentId); + if (it == m_paymentsIndex.end()) { + return; + } + + auto toErase = std::lower_bound(it->second.begin(), it->second.end(), distance); + if (toErase == it->second.end() || *toErase != distance) { + return; + } + + it->second.erase(toErase); +} + +void WalletUserTransactionsCache::rebuildPaymentsIndex() { + auto begin = std::begin(m_transactions); + auto end = std::end(m_transactions); + std::vector extra; + for (auto it = begin; it != end; ++it) { + PaymentId paymentId; + extra.insert(extra.begin(), it->extra.begin(), it->extra.end()); + if (canInsertTransactionToIndex(*it) && extractPaymentId(extra, paymentId)) { + pushToPaymentsIndex(paymentId, std::distance(begin, it)); + } + extra.clear(); + } + +} + uint64_t WalletUserTransactionsCache::unconfirmedTransactionsAmount() const { return m_unconfirmedTransactions.countUnconfirmedTransactionsAmount(); } @@ -55,7 +108,7 @@ size_t WalletUserTransactionsCache::getDepositCount() const { TransactionId WalletUserTransactionsCache::addNewTransaction( uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime, const std::vector& messages) { - + TransactionInfo transaction; transaction.firstTransferId = insertTransfers(transfers); @@ -138,10 +191,34 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c // notification event event = std::make_shared(id); } + + if (canInsertTransactionToIndex(getTransaction(id)) && paymentIdIsSet(txInfo.paymentId)) { + pushToPaymentsIndex(txInfo.paymentId, id); + } return event; } +std::vector WalletUserTransactionsCache::getTransactionsByPaymentIds(const std::vector& paymentIds) const { + std::vector payments(paymentIds.size()); + auto payment = payments.begin(); + for (auto& key : paymentIds) { + payment->paymentId = key; + auto it = m_paymentsIndex.find(key); + if (it != m_paymentsIndex.end()) { + std::transform(it->second.begin(), it->second.end(), std::back_inserter(payment->transactions), + [this](decltype(it->second)::value_type val) { + assert(val < m_transactions.size()); + return m_transactions[val]; + }); + } + + ++payment; + } + + return payments; +} + std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const TransactionHash& transactionHash) { TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; if (m_unconfirmedTransactions.findTransactionId(transactionHash, id)) { @@ -155,6 +232,11 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c std::shared_ptr event; if (id != CryptoNote::INVALID_TRANSACTION_ID) { TransactionInfo& tr = getTransaction(id); + std::vector extra(tr.extra.begin(), tr.extra.end()); + PaymentId paymentId; + if (extractPaymentId(extra, paymentId)) { + popFromPaymentsIndex(paymentId, id); + } tr.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; tr.timestamp = 0; tr.state = TransactionState::Deleted; diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index 498f7381e1..c7a0c10755 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -8,6 +8,7 @@ #include "crypto/hash.h" #include "IWallet.h" #include "ITransfersContainer.h" +#include #include "WalletEvent.h" #include "WalletUnconfirmedTransactions.h" @@ -49,8 +50,9 @@ class WalletUserTransactionsCache bool isUsed(const TransactionOutputInformation& out) const; -private: + std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const; +private: TransactionId findTransactionByHash(const TransactionHash& hash); TransactionId insertTransaction(TransactionInfo&& Transaction); TransferId insertTransfers(const std::vector& transfers); @@ -59,11 +61,19 @@ class WalletUserTransactionsCache typedef std::vector UserTransfers; typedef std::vector UserTransactions; typedef std::vector UserDeposits; + using Offset = UserTransactions::size_type; + using UserPaymentIndex = std::unordered_map>; + + void rebuildPaymentsIndex(); + void pushToPaymentsIndex(const PaymentId& paymentId, Offset distance); + void pushToPaymentsIndexInternal(Offset distance, const TransactionInfo& info, std::vector& extra); + void popFromPaymentsIndex(const PaymentId& paymentId, Offset distance); UserTransactions m_transactions; UserTransfers m_transfers; UserDeposits m_deposits; WalletUnconfirmedTransactions m_unconfirmedTransactions; + UserPaymentIndex m_paymentsIndex; }; } //namespace CryptoNote diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index eca3ec6fd1..586293b0bf 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -156,7 +156,7 @@ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& r } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx) { - crypto::hash expectedPaymentId; + PaymentId expectedPaymentId; cryptonote::blobdata payment_id_blob; if (!epee::string_tools::parse_hexstr_to_binbuff(req.payment_id, payment_id_blob)) { er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID; @@ -170,31 +170,17 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN return false; } - expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); - size_t transactionsCount = m_wallet->getTransactionCount(); - for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; - m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { - continue; - } - - if (txInfo.totalAmount < 0) continue; - std::vector extraVec; - extraVec.reserve(txInfo.extra.size()); - std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - - crypto::hash paymentId; - if (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { - wallet_rpc::payment_details rpc_payment; - rpc_payment.tx_hash = epee::string_tools::pod_to_hex(txInfo.hash); - rpc_payment.amount = txInfo.totalAmount; - rpc_payment.block_height = txInfo.blockHeight; - rpc_payment.unlock_time = txInfo.unlockTime; - res.payments.push_back(rpc_payment); - } + std::copy(std::begin(payment_id_blob), std::end(payment_id_blob), reinterpret_cast(&expectedPaymentId)); // no UB, char can alias any type + auto payments = m_wallet->getTransactionsByPaymentIds({expectedPaymentId}); + assert(payments.size() == 1); + for (auto& transaction : payments[0].transactions) { + wallet_rpc::payment_details rpc_payment; + rpc_payment.tx_hash = epee::string_tools::pod_to_hex(transaction.hash); + rpc_payment.amount = transaction.totalAmount; + rpc_payment.block_height = transaction.blockHeight; + rpc_payment.unlock_time = transaction.unlockTime; + res.payments.push_back(rpc_payment); } - return true; } diff --git a/tests/core_tests/upgrade.cpp b/tests/core_tests/upgrade.cpp index e35bf90373..3987c2ca17 100644 --- a/tests/core_tests/upgrade.cpp +++ b/tests/core_tests/upgrade.cpp @@ -84,6 +84,7 @@ gen_upgrade::gen_upgrade() : m_invalidBlockIndex(0), m_checkBlockTemplateVersion m_coinsInCirculationBeforeUpgrade(0), m_coinsInCirculationAfterUpgrade(0) { cryptonote::CurrencyBuilder currencyBuilder; currencyBuilder.maxBlockSizeInitial(std::numeric_limits::max() / 2); + currencyBuilder.upgradeHeight(UpgradeDetectorBase::UNDEF_HEIGHT); m_currency = currencyBuilder.currency(); REGISTER_CALLBACK_METHOD(gen_upgrade, markInvalidBlock); diff --git a/tests/unit_tests/test_WalletUserTransactionsCache.cpp b/tests/unit_tests/test_WalletUserTransactionsCache.cpp new file mode 100644 index 0000000000..5b919c58ac --- /dev/null +++ b/tests/unit_tests/test_WalletUserTransactionsCache.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Copyright (c) 2014-2015 XDN developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "gtest/gtest.h" + +#include +#include +#include + +using namespace CryptoNote; + +class WalletUserTransactionsCacheTest : public testing::Test { +public: + WalletUserTransactionsCacheTest() { + cryptonote::createTxExtraWithPaymentId(stringPaymentId, rawExtra); + crypto::hash hash; + if (!cryptonote::getPaymentIdFromTxExtra(rawExtra, hash)) { + assert(false); + } + std::copy_n(reinterpret_cast(&hash), sizeof(hash), paymentId.begin()); + } + + TransactionInfo buildTransactionInfo() { + TransactionInfo info; + info.state = TransactionState::Active; + info.blockHeight = 1; + info.totalAmount = 1000; + info.extra.assign(rawExtra.begin(), rawExtra.end()); + info.hash = paymentId; + return info; + } + + TransactionInformation buildTransactionInformation() { + TransactionInformation info; + info.blockHeight = 1; + info.extra.assign(rawExtra.begin(), rawExtra.end()); + info.paymentId = paymentId; + info.transactionHash = paymentId; + + return info; + } + + std::string stringPaymentId = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + WalletUserTransactionsCache cache; + PaymentId paymentId; + std::vector rawExtra; + crypto::hash hash; + TransactionId id = 0; +}; + +TEST_F(WalletUserTransactionsCacheTest, TransactionIsAddedToIndexWhenItIsConfirmed) { + cache.onTransactionUpdated(buildTransactionInformation(), 1000); + ASSERT_EQ(1, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); + ASSERT_EQ(paymentId, cache.getTransactionsByPaymentIds({paymentId})[0].transactions[0].hash); +} + +TEST_F(WalletUserTransactionsCacheTest, TransactionWithInvalidHeightIsNotAdded) { + auto tx = buildTransactionInformation(); + tx.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + cache.onTransactionUpdated(tx, 1000); + ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); +} + +TEST_F(WalletUserTransactionsCacheTest, TransactionWithEmptyExtraIsNotAdded) { + auto tx = buildTransactionInformation(); + tx.extra.clear(); + cache.onTransactionUpdated(tx, 1000); + ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); +} + +TEST_F(WalletUserTransactionsCacheTest, TransactionWithInvalidAmountIsNotAdded) { + cache.onTransactionUpdated(buildTransactionInformation(), 0); + ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); +} + +TEST_F(WalletUserTransactionsCacheTest, TransactionIsRemovedFromIndexWhenItIsRemovedFromCache) { + cache.onTransactionUpdated(buildTransactionInformation(), 1000); + ASSERT_EQ(1, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); + cache.onTransactionDeleted(cache.getTransaction(id).hash); + ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); +} diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index f50bc3efa9..bc87c3eba0 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -1435,3 +1435,73 @@ TEST_F(WalletApi, sendBulkOfMessages) { bob->shutdown(); carol->shutdown(); } + +TEST_F(WalletApi, PaymentIdIndexWorks) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + for (int i = 0; i < 5; ++i) { + GetOneBlockReward(*alice); + } + + generator.generateEmptyBlocks(10); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + uint64_t sendAmount = 100000; + + CryptoNote::Transfer tr; + tr.address = bob->getAddress(); + tr.amount = sendAmount; + + std::string extra; + std::vector rawExtra; + ASSERT_TRUE(cryptonote::createTxExtraWithPaymentId("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", rawExtra)); + std::copy(rawExtra.begin(), rawExtra.end(), std::back_inserter(extra)); + CryptoNote::PaymentId paymentId; + crypto::hash id; + ASSERT_TRUE(cryptonote::getPaymentIdFromTxExtra(rawExtra, id)); + std::copy_n(reinterpret_cast(&id), sizeof(CryptoNote::PaymentId), std::begin(paymentId)); + ASSERT_EQ(0, bob->getTransactionCount()); + ASSERT_EQ(0, bob->getTransactionsByPaymentIds({paymentId})[0].transactions.size()); + aliceNode->setNextTransactionToPool(); + ASSERT_FALSE(extra.empty()); + auto txId = alice->sendTransaction(tr, m_currency.minimumFee(), extra, 1, 0); + + bobNode->setNextTransactionToPool(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + ASSERT_NE(txId, CryptoNote::INVALID_TRANSACTION_ID); + aliceNode->includeTransactionsFromPoolToBlock(); + ASSERT_EQ(0, bob->getTransactionsByPaymentIds({paymentId})[0].transactions.size()); + + bobNode->includeTransactionsFromPoolToBlock(); + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + ASSERT_EQ(1, bob->getTransactionCount()); + bobNode->includeTransactionsFromPoolToBlock(); + //ASSERT_EQ(0, bob->unconfirmedTransactionAmount()); + + CryptoNote::TransactionInfo info; + ASSERT_TRUE(bob->getTransaction(0, info)); + //CryptoNote::ITransfersObserver& obeserver = *bob; + //observer.onTransactionUpdated(nullptr, info.hash); + generator.generateEmptyBlocks(10); + + { + auto payments = bob->getTransactionsByPaymentIds({paymentId}); + + ASSERT_EQ(1, payments[0].transactions.size()); + ASSERT_EQ(sendAmount, payments[0].transactions[0].totalAmount); + } + { + auto payments = alice->getTransactionsByPaymentIds({paymentId}); + + ASSERT_EQ(0, payments[0].transactions.size()); + } +} From b1b8502c62d6fca5370c7a7fb42fe7b94a91edbc Mon Sep 17 00:00:00 2001 From: xdn-project Date: Tue, 30 Jun 2015 11:39:02 +0000 Subject: [PATCH 32/59] Added 'print_stat' command to daemon --- src/cryptonote_core/DepositIndex.cpp | 121 +++++++++ src/cryptonote_core/DepositIndex.h | 50 ++++ src/cryptonote_core/blockchain_storage.cpp | 81 +++++- src/cryptonote_core/blockchain_storage.h | 12 +- src/cryptonote_core/cryptonote_core.cpp | 28 ++ src/cryptonote_core/cryptonote_core.h | 7 + src/daemon/daemon_commands_handler.h | 33 ++- src/rpc/core_rpc_server.cpp | 2 + src/rpc/core_rpc_server_commands_defs.h | 4 + src/version.h.in | 4 +- tests/core_tests/chaingen_main.cpp | 10 +- tests/core_tests/deposit.cpp | 290 +++++++++++++++++++++ tests/core_tests/deposit.h | 77 +++++- tests/unit_tests/test_DepositIndex.cpp | 187 +++++++++++++ 14 files changed, 895 insertions(+), 11 deletions(-) create mode 100644 src/cryptonote_core/DepositIndex.cpp create mode 100644 src/cryptonote_core/DepositIndex.h create mode 100644 tests/unit_tests/test_DepositIndex.cpp diff --git a/src/cryptonote_core/DepositIndex.cpp b/src/cryptonote_core/DepositIndex.cpp new file mode 100644 index 0000000000..2d6b3e10f8 --- /dev/null +++ b/src/cryptonote_core/DepositIndex.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Copyright (c) 2014-2015 XDN developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include + +namespace CryptoNote { + +DepositIndex::DepositIndex() { + index.push_back({0, 0, 0}); + height = 0; +} + +DepositIndex::DepositIndex(DepositHeight expectedHeight) { + index.reserve(expectedHeight + 1); + index.push_back({0, 0, 0}); + height = 0; +} + +void DepositIndex::reserve(DepositHeight expectedHeight) { + index.reserve(expectedHeight + 1); +} + +auto DepositIndex::fullDepositAmount() const -> DepositAmount { + assert(!index.empty()); + return index.back().amount; +} + +auto DepositIndex::fullInterestAmount() const -> DepositInterest { + assert(!index.empty()); + return index.back().interest; +} + +static inline bool sumWillOverflow(int64_t x, int64_t y) { + if (y > 0 && x > std::numeric_limits::max() - y) { + return true; + } + + if (y < 0 && x < std::numeric_limits::min() - y) { + return true; + } + + return false; +} + +static inline bool sumWillOverflow(uint64_t x, uint64_t y) { + if (x > std::numeric_limits::max() - y) { + return true; + } + + return false; +} + +void DepositIndex::pushBlock(DepositAmount amount, DepositInterest interest) { + auto lastAmount = index.back().amount; + auto lastInterest = index.back().interest; + assert(!sumWillOverflow(interest, lastInterest)); + assert(!sumWillOverflow(amount, lastAmount)); + assert(amount + lastAmount >= 0); + ++height; + if (amount != 0 || interest > 0) { + index.push_back({height, amount + lastAmount, interest + lastInterest}); + } +} + +void DepositIndex::popBlock() { + assert(!index.empty()); + assert(height > 0); + if (index.back().height == height) { + assert(index.size() > 1); + index.pop_back(); + } + + --height; +} + +auto DepositIndex::lastHeight() const -> DepositHeight { + return height; +} + +auto DepositIndex::elementAt(DepositHeight height) const -> IndexType::const_iterator { + return std::upper_bound( + index.cbegin(), index.cend(), height, + [] (DepositHeight height, const DepositIndexEntry& left) { return height < left.height; }) - 1; +} + +size_t DepositIndex::popBlocks(DepositHeight from) { + from = from == 0 ? 1 : from; + if (from > height) { + return 0; + } + + IndexType::iterator it = index.begin(); + std::advance(it, std::distance(index.cbegin(), elementAt(from))); + if (it->height < from) { + ++it; + } + + auto diff = height - from + 1; + index.erase(it, index.end()); + height -= diff; + return diff; +} + +auto DepositIndex::depositAmountAtHeight(DepositHeight height) const -> DepositAmount { + assert(!index.empty()); + return elementAt(height)->amount; +} + +auto DepositIndex::depositInterestAtHeight(DepositHeight height) const -> DepositInterest { + assert(!index.empty()); + return elementAt(height)->interest; +} +} diff --git a/src/cryptonote_core/DepositIndex.h b/src/cryptonote_core/DepositIndex.h new file mode 100644 index 0000000000..2dd7d643e8 --- /dev/null +++ b/src/cryptonote_core/DepositIndex.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Copyright (c) 2014-2015 XDN developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include + +namespace CryptoNote { +class DepositIndex { +public: + using DepositAmount = int64_t; + using DepositInterest = uint64_t; + using DepositHeight = uint32_t; + DepositIndex(); + explicit DepositIndex(DepositHeight expectedHeight); + void pushBlock(DepositAmount amount, DepositInterest interest); + void popBlock(); + void reserve(DepositHeight expectedHeight); + size_t popBlocks(DepositHeight from); + DepositAmount depositAmountAtHeight(DepositHeight height) const; + DepositAmount fullDepositAmount() const; + DepositInterest depositInterestAtHeight(DepositHeight height) const; + DepositInterest fullInterestAmount() const; + DepositHeight lastHeight() const; + template void serialize(Archive& ar, const unsigned int version) { + ar & index; + } + +private: + struct DepositIndexEntry { + DepositHeight height; + DepositAmount amount; + DepositInterest interest; + template void serialize(Archive& ar, const unsigned int version) { + ar & height; + ar & amount; + ar & interest; + } + }; + + using IndexType = std::vector; + IndexType::const_iterator elementAt(DepositHeight height) const; + IndexType index; + DepositHeight height; +}; +} diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index d168684a52..abf9a58a82 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -81,7 +81,7 @@ template void cryptonote::blockchain_storage::MultisignatureOutpu namespace cryptonote { -#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1 +#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 2 class BlockCacheSerializer { @@ -125,6 +125,9 @@ namespace cryptonote LOG_PRINT_L0(operation << "multi-signature outputs..."); ar & m_bs.m_multisignatureOutputs; + LOG_PRINT_L0(operation << "deposit index..."); + ar & m_bs.m_depositIndex; + m_loaded = true; } @@ -263,6 +266,7 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi const BlockEntry& block = m_blocks[b]; crypto::hash blockHash = get_block_hash(block.bl); m_blockIndex.push(blockHash); + uint64_t interest = 0; for (uint16_t t = 0; t < block.transactions.size(); ++t) { const TransactionEntry& transaction = block.transactions[t]; crypto::hash transactionHash = get_transaction_hash(transaction.tx); @@ -289,7 +293,11 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi m_multisignatureOutputs[out.amount].push_back(usage); } } + + interest += m_currency.calculateTotalTransactionInterest(transaction.tx); } + + pushToDepositIndex(block, interest); } std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; @@ -396,6 +404,11 @@ crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { CRITICAL_REGION_LOCAL(m_blockchain_lock); return m_blockIndex.getBlockId(height); } + +bool blockchain_storage::getBlockHeight(const crypto::hash& blockHash, uint64_t& height) const { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_blockIndex.getBlockHeight(blockHash, height); +} bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& b) { CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -441,6 +454,22 @@ uint64_t blockchain_storage::getCoinsInCirculation() { return m_blocks.back().already_generated_coins; } } + +uint64_t blockchain_storage::coinsEmittedAtHeight(uint64_t height) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + const auto& block = m_blocks[height]; + return block.already_generated_coins; +} + +difficulty_type blockchain_storage::difficultyAtHeight(uint64_t height) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + const auto& current = m_blocks[height]; + if (height < 1) { + return current.cumulative_difficulty; + } + const auto& previous = m_blocks[height - 1]; + return current.cumulative_difficulty - previous.cumulative_difficulty; +} uint8_t blockchain_storage::get_block_major_version_for_height(uint64_t height) const { return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1; @@ -1179,7 +1208,6 @@ bool blockchain_storage::find_blockchain_supplement(const std::list(in); + if (multisign.term > 0) { + deposit -= multisign.amount; + } + } + } + for (const auto& out : tx.tx.vout) { + if (out.target.type() == typeid(TransactionOutputMultisignature)) { + auto& multisign = boost::get(out.target); + if (multisign.term > 0) { + deposit += out.amount; + } + } + } + } + m_depositIndex.pushBlock(deposit, interest); +} bool blockchain_storage::pushBlock(BlockEntry& block) { crypto::hash blockHash = get_block_hash(block.bl); @@ -1707,6 +1779,7 @@ void blockchain_storage::popBlock(const crypto::hash& blockHash) { } popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.minerTx)); + m_depositIndex.popBlock(); m_blocks.pop_back(); m_blockIndex.pop(); @@ -1912,8 +1985,8 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp return false; } - std::size_t inputSignatureIndex = 0; - std::size_t outputKeyIndex = 0; + size_t inputSignatureIndex = 0; + size_t outputKeyIndex = 0; while (inputSignatureIndex < input.signatures) { if (outputKeyIndex == output.keys.size()) { LOG_PRINT_L1("Transaction << " << transactionHash << " contains multisignature input with invalid signatures."); diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index b18ff5011b..b88faaf320 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -21,6 +21,7 @@ #include "cryptonote_core/UpgradeDetector.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/tx_pool.h" +#include "cryptonote_core/DepositIndex.h" namespace cryptonote { @@ -58,6 +59,7 @@ namespace cryptonote { size_t get_alternative_blocks_count(); crypto::hash get_block_id_by_height(uint64_t height); bool get_block_by_hash(const crypto::hash &h, Block &blk); + bool getBlockHeight(const crypto::hash& blockHash, uint64_t& height) const; bool have_tx(const crypto::hash &id); bool have_tx_keyimges_as_spent(const Transaction &tx); @@ -86,6 +88,12 @@ namespace cryptonote { bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector& new_txs, std::vector& deleted_tx_ids); + uint64_t fullDepositAmount() const; + uint64_t depositAmountAtHeight(size_t height) const; + uint64_t fullDepositInterest() const; + uint64_t depositInterestAtHeight(size_t height) const; + uint64_t coinsEmittedAtHeight(uint64_t height); + uint64_t difficultyAtHeight(uint64_t height); template @@ -182,7 +190,7 @@ namespace cryptonote { const Currency& m_currency; tx_memory_pool& m_tx_pool; - epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock + mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock crypto::cn_context m_cn_context; tools::ObserverManager m_observerManager; @@ -205,6 +213,7 @@ namespace cryptonote { Blocks m_blocks; CryptoNote::BlockIndex m_blockIndex; + CryptoNote::DepositIndex m_depositIndex; TransactionMap m_transactionMap; MultisignatureOutputsContainer m_multisignatureOutputs; UpgradeDetector m_upgradeDetector; @@ -214,6 +223,7 @@ namespace cryptonote { bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); bool handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc); difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); + void pushToDepositIndex(const BlockEntry& block, uint64_t interest); bool prevalidate_miner_transaction(const Block& b, uint64_t height); bool validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange); bool validate_transaction(const Block& b, uint64_t height, const Transaction& tx); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index add7da3cbc..d9e65865d8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -212,6 +212,22 @@ namespace cryptonote uint64_t core::getCoinsInCirculation() { return m_blockchain_storage.getCoinsInCirculation(); } + + uint64_t core::fullDepositAmount() const { + return m_blockchain_storage.fullDepositAmount(); + } + + uint64_t core::depositAmountAtHeight(size_t height) const { + return m_blockchain_storage.depositAmountAtHeight(height); + } + + uint64_t core::fullDepositInterest() const { + return m_blockchain_storage.fullDepositInterest(); + } + + uint64_t core::depositInterestAtHeight(size_t height) const { + return m_blockchain_storage.depositInterestAtHeight(height); + } //----------------------------------------------------------------------------------------------- bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) @@ -487,6 +503,18 @@ namespace cryptonote bool core::getBlockByHash(const crypto::hash &h, Block &blk) { return core::get_block_by_hash(h, blk); } + + bool core::getBlockHeight(const crypto::hash &h, uint64_t& height) const { + return m_blockchain_storage.getBlockHeight(h, height); + } + + uint64_t core::coinsEmittedAtHeight(uint64_t height) { + return m_blockchain_storage.coinsEmittedAtHeight(height); + } + + uint64_t core::difficultyAtHeight(uint64_t height) { + return {m_blockchain_storage.difficultyAtHeight(height)}; + } crypto::hash core::get_block_id_by_height(uint64_t height) { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 93d6f6179a..0b4d4e85d6 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -67,12 +67,15 @@ namespace cryptonote { crypto::hash get_block_id_by_height(uint64_t height); void get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); bool get_block_by_hash(const crypto::hash &h, Block &blk); + bool getBlockHeight(const crypto::hash &h, uint64_t& height) const; //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); virtual bool getBlockByHash(const crypto::hash &h, Block &blk) override; bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); + uint64_t coinsEmittedAtHeight(uint64_t height); + uint64_t difficultyAtHeight(uint64_t height); void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_checkpoints(checkpoints&& chk_pts); @@ -101,6 +104,10 @@ namespace cryptonote { void on_synchronized(); virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; uint64_t getCoinsInCirculation(); + uint64_t fullDepositAmount() const; + uint64_t fullDepositInterest() const; + uint64_t depositAmountAtHeight(size_t height) const; + uint64_t depositInterestAtHeight(size_t height) const; private: bool add_new_tx(const 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); diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index af968a123e..ab40c1ec9c 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -9,13 +9,13 @@ #include "console_handler.h" #include "p2p/net_node.h" +#include "cryptonote_core/Currency.h" #include "cryptonote_core/miner.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "common/util.h" #include "crypto/hash.h" #include "version.h" - class daemon_cmmands_handler { nodetool::node_server >& m_srv; @@ -29,6 +29,7 @@ class daemon_cmmands_handler //m_cmd_binder.set_handler("print_bci", boost::bind(&daemon_cmmands_handler::print_bci, this, _1)); //m_cmd_binder.set_handler("print_bc_outs", boost::bind(&daemon_cmmands_handler::print_bc_outs, this, _1)); m_cmd_binder.set_handler("print_block", boost::bind(&daemon_cmmands_handler::print_block, this, _1), "Print block, print_block | "); + m_cmd_binder.set_handler("print_stat", boost::bind(&daemon_cmmands_handler::print_stat, this, _1), "Print statistics, print_stat | | "); m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_cmmands_handler::print_tx, this, _1), "Print transaction, print_tx "); m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_cmmands_handler::start_mining, this, _1), "Start mining for specified address, start_mining [threads=1]"); m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_cmmands_handler::stop_mining, this, _1), "Stop mining"); @@ -240,6 +241,36 @@ class daemon_cmmands_handler return true; } //-------------------------------------------------------------------------------- + bool print_stat(const std::vector& args) { + uint64_t height = 0; + auto& core = m_srv.get_payload_object().get_core(); + uint64_t maxHeight = core.get_current_blockchain_height() - 1; + if (args.empty()) { + height = maxHeight; + } else { + try { + height = boost::lexical_cast(args.front()); + } catch (boost::bad_lexical_cast&) { + crypto::hash block_hash; + if (!parse_hash256(args.front(), block_hash) || !core.getBlockHeight(block_hash, height)) { + return false; + } + } + if (height > maxHeight) { + std::cout << "printing for last available block: " << maxHeight << std::endl; + height = maxHeight; + } + } + + std::cout << "Block height: " << height << std::endl; + std::cout << "Block difficulty: " << core.difficultyAtHeight(height) << std::endl; + std::cout << "Total coins in network: " << core.currency().formatAmount(core.coinsEmittedAtHeight(height)) << std::endl; + std::cout << "Total coins on deposits: " << core.currency().formatAmount(core.depositAmountAtHeight(height)) << std::endl; + std::cout << "Total interest paid: " << core.currency().formatAmount(core.depositInterestAtHeight(height)) << std::endl; + + return true; + } + //-------------------------------------------------------------------------------- bool print_block(const std::vector& args) { if (args.empty()) diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 0c9f918962..33d5cc6830 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -86,6 +86,8 @@ namespace cryptonote res.incoming_connections_count = total_conn - res.outgoing_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.full_deposit_amount = m_core.fullDepositAmount(); + res.full_deposit_interest = m_core.fullDepositInterest(); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 8a2436d556..c763a523e2 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -229,6 +229,8 @@ namespace cryptonote uint64_t incoming_connections_count; uint64_t white_peerlist_size; uint64_t grey_peerlist_size; + uint64_t full_deposit_amount; + uint64_t full_deposit_interest; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -241,6 +243,8 @@ namespace cryptonote KV_SERIALIZE(incoming_connections_count) KV_SERIALIZE(white_peerlist_size) KV_SERIALIZE(grey_peerlist_size) + KV_SERIALIZE(full_deposit_amount) + KV_SERIALIZE(full_deposit_interest) END_KV_SERIALIZE_MAP() }; }; diff --git a/src/version.h.in b/src/version.h.in index 934925d249..83122b794c 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.6-beta" -#define PROJECT_VERSION_BUILD_NO "1507" +#define PROJECT_VERSION "2.0.7-beta" +#define PROJECT_VERSION_BUILD_NO "1510" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 4ded30d1c8..5ff9e23ddf 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -86,6 +86,15 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_EX(TestCase(cryptonote::BLOCK_MAJOR_VERSION_1)) \ GENERATE_AND_PLAY_EX(TestCase(cryptonote::BLOCK_MAJOR_VERSION_2)) + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositUnrolesPartOfAmountAfterSwitchToAlternativeChain); + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositExtendsTotalDeposit); + GENERATE_AND_PLAY(DepositTests::TransactionWithMultipleDepositOutsExtendsTotalDeposit); + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositUpdatesInterestAfterDepositUnlock); + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositUnrolesInterestAfterSwitchToAlternativeChain); + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositUnrolesAmountAfterSwitchToAlternativeChain); + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositIsClearedAfterInputSpend); + GENERATE_AND_PLAY(DepositTests::TransactionWithDepositUpdatesInterestAfterDepositUnlockMultiple); + GENERATE_AND_PLAY(DepositTests::BlocksOfFirstTypeCantHaveTransactionsOfTypeTwo); GENERATE_AND_PLAY(DepositTests::BlocksOfSecondTypeCanHaveTransactionsOfTypeOne); GENERATE_AND_PLAY(DepositTests::BlocksOfSecondTypeCanHaveTransactionsOfTypeTwo); @@ -111,7 +120,6 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(DepositTests::TransactionWithDepositExtendsEmission); GENERATE_AND_PLAY(DepositTests::TransactionWithDepositRestorsEmissionOnAlternativeChain); - GENERATE_AND_PLAY(gen_simple_chain_001); GENERATE_AND_PLAY(gen_simple_chain_split_1); GENERATE_AND_PLAY(one_block); diff --git a/tests/core_tests/deposit.cpp b/tests/core_tests/deposit.cpp index a8ce52e9d0..355848a4ab 100644 --- a/tests/core_tests/deposit.cpp +++ b/tests/core_tests/deposit.cpp @@ -695,5 +695,295 @@ bool TransactionWithAmountThatHasAlreadyFinishedWillBeAccepted::generate(std::ve return true; } +bool TransactionWithDepositExtendsTotalDeposit::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + generator.addCallback("amountZero"); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + generator.addCallback("amountOneMinimal"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + return true; +} + +bool TransactionWithMultipleDepositOutsExtendsTotalDeposit::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + generator.addCallback("amountZero"); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, 0); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + generator.addCallback("amountThreeMinimal"); + generator.generateBlocks(2, BLOCK_MAJOR_VERSION_2); + return true; +} + +bool TransactionWithDepositIsClearedAfterInputSpend::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + generator.addCallback("amountZero"); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + generator.generateBlocks(m_currency.depositMinTerm() - 1, BLOCK_MAJOR_VERSION_2); + + generator.addCallback("amountOneMinimal"); + { + TransactionBuilder builder(m_currency); + auto src = createSource(m_currency.depositMinTerm(), key); + src.input.term = m_currency.depositMinTerm(); + builder.addMultisignatureInput(src); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + generator.addCallback("amountZero"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + + return true; +} + +bool TransactionWithDepositUpdatesInterestAfterDepositUnlock::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + generator.generateBlocks(m_currency.depositMinTerm() - 1, BLOCK_MAJOR_VERSION_2); + + generator.addCallback("interestZero"); + { + TransactionBuilder builder(m_currency); + auto src = createSource(m_currency.depositMinTerm(), key); + src.input.term = m_currency.depositMinTerm(); + builder.addMultisignatureInput(src); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + generator.addCallback("interestOneMinimal"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + } + + return true; +} + +bool TransactionWithDepositUpdatesInterestAfterDepositUnlockMultiple::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + generator.generateBlocks(m_currency.depositMinTerm() - 1, BLOCK_MAJOR_VERSION_2); + + generator.addCallback("interestZero"); + { + TransactionBuilder builder(m_currency); + auto src1 = createSource(m_currency.depositMinTerm(), key); + auto src2 = createSource(m_currency.depositMinTerm(), key); + src1.input.term = m_currency.depositMinTerm(); + src2.input.term = m_currency.depositMinTerm(); + src2.input.outputIndex = 1; + src2.srcOutputIndex = 1; + builder.addMultisignatureInput(src1); + builder.addMultisignatureInput(src2); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + generator.addCallback("interestTwoMininmal"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + } + + return true; +} + +bool TransactionWithDepositUnrolesInterestAfterSwitchToAlternativeChain::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + generator.generateBlocks(m_currency.depositMinTerm() - 1, BLOCK_MAJOR_VERSION_2); + auto lastBlock = generator.lastBlock; + + generator.addCallback("interestZero"); + { + TransactionBuilder builder(m_currency); + auto src = createSource(m_currency.depositMinTerm(), key); + src.input.term = m_currency.depositMinTerm(); + builder.addMultisignatureInput(src); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + generator.addCallback("interestOneMinimal"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + } + + generator.lastBlock = lastBlock; + generator.generateBlocks(4, BLOCK_MAJOR_VERSION_2); + generator.addCallback("interestZero"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + + return true; +} + +bool TransactionWithDepositUnrolesAmountAfterSwitchToAlternativeChain::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow(), BLOCK_MAJOR_VERSION_2); + auto lastBlock = generator.lastBlock; + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount() + 100, m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + generator.addCallback("amountOneMinimal"); + generator.generateBlocks(m_currency.depositMinTerm(), BLOCK_MAJOR_VERSION_2); + + generator.addCallback("amountOneMinimal"); + generator.lastBlock = lastBlock; + generator.generateBlocks(m_currency.depositMinTerm() + 4, BLOCK_MAJOR_VERSION_2); + generator.addCallback("amountZero"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + + return true; +} + +bool TransactionWithDepositUnrolesPartOfAmountAfterSwitchToAlternativeChain::generate(std::vector& events) { + TestGenerator generator(m_currency, events); + generator.generator.defaultMajorVersion = BLOCK_MAJOR_VERSION_2; + generator.generateBlocks(m_currency.minedMoneyUnlockWindow() + 3, BLOCK_MAJOR_VERSION_2); + cryptonote::KeyPair key; + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount(), m_currency.minimumFee()); + builder.m_destinations.clear(); + + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + key = builder.getTxKeys(); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + } + + auto lastBlock = generator.lastBlock; + generator.addCallback("amountOneMinimal"); + generator.generateBlocks(m_currency.depositMinTerm(), BLOCK_MAJOR_VERSION_2); + + { + auto builder = generator.createTxBuilder(generator.minerAccount, from, m_currency.depositMinAmount(), m_currency.minimumFee()); + builder.m_sources.clear(); + builder.m_destinations.clear(); + TransactionBuilder::KeysVector kv; + kv.push_back(from.get_keys()); + auto src1 = createSource(m_currency.depositMinTerm(), key); + src1.input.term = m_currency.depositMinTerm(); + builder.addMultisignatureInput(src1); + builder.addMultisignatureOut(m_currency.depositMinAmount(), kv, 1, m_currency.depositMinTerm()); + auto tx = builder.build(); + generator.addEvent(tx); + generator.makeNextBlock(tx); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + } + + generator.lastBlock = lastBlock; + generator.generateBlocks(m_currency.depositMinTerm() + 4, BLOCK_MAJOR_VERSION_2); + generator.addCallback("amountOneMinimal"); + generator.generateBlocks(1, BLOCK_MAJOR_VERSION_2); + + return true; +} } diff --git a/tests/core_tests/deposit.h b/tests/core_tests/deposit.h index 6dcbe6af33..2a5320540c 100644 --- a/tests/core_tests/deposit.h +++ b/tests/core_tests/deposit.h @@ -57,6 +57,46 @@ struct DepositTestsBase : public test_chain_unit_base { std::size_t emission = 0; }; +struct DepositIndexTest : public DepositTestsBase { + using Block = cryptonote::Block; + using Core = cryptonote::core; + using Events = std::vector; + DepositIndexTest() { + m_currency = + cryptonote::CurrencyBuilder().upgradeHeight(0).depositMinTerm(10).depositMinTotalRateFactor(100).mininumFee(1000).currency(); + REGISTER_CALLBACK_METHOD(DepositIndexTest, interestZero); + REGISTER_CALLBACK_METHOD(DepositIndexTest, interestOneMinimal); + REGISTER_CALLBACK_METHOD(DepositIndexTest, interestTwoMininmal); + REGISTER_CALLBACK_METHOD(DepositIndexTest, amountZero); + REGISTER_CALLBACK_METHOD(DepositIndexTest, amountOneMinimal); + REGISTER_CALLBACK_METHOD(DepositIndexTest, amountThreeMinimal); + } + + bool amountZero(const Core& c, std::size_t ev_index, const Events& events) { + return c.fullDepositAmount() == 0; + } + + bool amountOneMinimal(const Core& c, std::size_t ev_index, const Events& events) { + return c.fullDepositAmount() == m_currency.depositMinAmount(); + } + + bool amountThreeMinimal(const Core& c, std::size_t ev_index, const Events& events) { + return c.fullDepositAmount() == 3 * m_currency.depositMinAmount(); + } + + bool interestZero(const Core& c, std::size_t ev_index, const Events& events) { + return c.fullDepositInterest() == 0; + } + + bool interestOneMinimal(const Core& c, std::size_t ev_index, const Events& events) { + return c.fullDepositInterest() == m_currency.calculateInterest(m_currency.depositMinAmount(), m_currency.depositMinTerm()); + } + + bool interestTwoMininmal(const Core& c, std::size_t ev_index, const Events& events) { + return c.fullDepositInterest() == 2 * m_currency.calculateInterest(m_currency.depositMinAmount(), m_currency.depositMinTerm()); + } +}; + struct EmissionTest : public DepositTestsBase { EmissionTest() { m_currency = @@ -77,12 +117,12 @@ struct EmissionTest : public DepositTestsBase { bool save_emission_before(cryptonote::core& c, std::size_t /*ev_index*/, const std::vector& /*events*/) { emission_before = c.getCoinsInCirculation(); - return true; + return emission_before > 0; } bool save_emission_after(cryptonote::core& c, std::size_t ev_index, const std::vector& /*events*/) { emission_after = c.getCoinsInCirculation(); - return true; + return emission_after > 0; } TestGenerator prepare(std::vector& events) const; std::size_t emission_before = 0; @@ -198,4 +238,37 @@ struct TransactionThatTriesToSpendOutputWhosTermHasntFinishedWillBeRejected : pu struct TransactionWithAmountThatHasAlreadyFinishedWillBeAccepted : public DepositTestsBase { bool generate(std::vector& events); }; + +struct TransactionWithDepositExtendsTotalDeposit : public DepositIndexTest { + bool generate(std::vector& events); +}; + +struct TransactionWithMultipleDepositOutsExtendsTotalDeposit : public DepositIndexTest { + bool generate(std::vector& events); +}; + +struct TransactionWithDepositIsClearedAfterInputSpend : public DepositIndexTest { + bool generate(std::vector& events); +}; + +struct TransactionWithDepositUpdatesInterestAfterDepositUnlock : public DepositIndexTest { + bool generate(std::vector& events); +}; + +struct TransactionWithDepositUnrolesInterestAfterSwitchToAlternativeChain : public DepositIndexTest{ + bool generate(std::vector& events); +}; + +struct TransactionWithDepositUnrolesAmountAfterSwitchToAlternativeChain : public DepositIndexTest { + bool generate(std::vector& events); +}; + +struct TransactionWithDepositUpdatesInterestAfterDepositUnlockMultiple : public DepositIndexTest { + bool generate(std::vector& events); +}; + +struct TransactionWithDepositUnrolesPartOfAmountAfterSwitchToAlternativeChain : public DepositIndexTest { + bool generate(std::vector& events); +}; + } diff --git a/tests/unit_tests/test_DepositIndex.cpp b/tests/unit_tests/test_DepositIndex.cpp new file mode 100644 index 0000000000..86b4fefc17 --- /dev/null +++ b/tests/unit_tests/test_DepositIndex.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2011-2015 The Cryptonote developers +// Copyright (c) 2014-2015 XDN developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "gtest/gtest.h" + +#include + +using namespace CryptoNote; + +class DepositIndexTest : public ::testing::Test { +public: + const std::size_t DEFAULT_HEIGHT = 10; + DepositIndexTest() : index(DEFAULT_HEIGHT) { + } + DepositIndex index; +}; + +TEST_F(DepositIndexTest, EmptyAfterCreate) { + ASSERT_EQ(0, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, AddBlockUpdatesGlobalAmount) { + index.pushBlock(10, 1); + ASSERT_EQ(10, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, RemoveReducesGlobalAmount) { + index.pushBlock(12, 1); + index.popBlock(); + ASSERT_EQ(0, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, AddEmptyBlockIncrementsHeight) { + index.pushBlock(0, 0); + ASSERT_EQ(1, index.lastHeight()); + index.pushBlock(0, 0); + ASSERT_EQ(2, index.lastHeight()); +} + +TEST_F(DepositIndexTest, MultipleRemoves) { + index.pushBlock(10, 1); + index.pushBlock(0, 0); + index.pushBlock(11, 1); + index.pushBlock(0, 0); + index.pushBlock(12, 1); + ASSERT_EQ(5, index.popBlocks(0)); + ASSERT_EQ(0, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, MultipleRemovesDecrementHeight) { + index.pushBlock(10, 1); + index.pushBlock(11, 1); + index.pushBlock(0, 0); + index.pushBlock(12, 1); + ASSERT_EQ(2, index.popBlocks(3)); + ASSERT_EQ(2, index.lastHeight()); +} + +TEST_F(DepositIndexTest, PopBlockReducesFullAmount) { + index.pushBlock(10, 1); + index.pushBlock(12, 1); + index.popBlock(); + ASSERT_EQ(10, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, RemoveDoesntClearGlobalAmount) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.popBlock(); + ASSERT_EQ(9, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, AddEmptyBlockDoesntChangeAmount) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + ASSERT_EQ(9, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, AddEmptyBlockDoesntChangeInterest) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + ASSERT_EQ(1, index.fullInterestAmount()); +} + +TEST_F(DepositIndexTest, RemoveDecrementsHeight) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.popBlock(); + ASSERT_EQ(1, index.lastHeight()); +} + +TEST_F(DepositIndexTest, GlobalAmountIsSumOfBlockDeposits) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + ASSERT_EQ(9 + 12, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddle) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(14, 1); + ASSERT_EQ(9 + 12, index.depositAmountAtHeight(2)); +} + +TEST_F(DepositIndexTest, MaxAmountIsReturnedForHeightLargerThanLastBlock) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(14, 1); + ASSERT_EQ(index.depositAmountAtHeight(20), index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddleLooksForLowerBound) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(14, 1); + index.pushBlock(7, 1); + ASSERT_EQ(9 + 12 + 14, index.depositAmountAtHeight(3)); +} + +TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddleIgnoresEmptyBlocks) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + index.pushBlock(12, 1); + index.pushBlock(0, 0); + index.pushBlock(14, 1); + index.pushBlock(0, 0); + index.pushBlock(7, 1); + ASSERT_EQ(9 + 12, index.depositAmountAtHeight(3)); +} + +TEST_F(DepositIndexTest, AmountAtZeroHeightIsZero) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(14, 1); + ASSERT_EQ(0, index.depositAmountAtHeight(0)); +} + +TEST_F(DepositIndexTest, MultiPopZeroChangesNothing) { + ASSERT_EQ(0, index.popBlocks(0)); + ASSERT_EQ(0, index.depositAmountAtHeight(0)); +} + +TEST_F(DepositIndexTest, DepositAmountAtNonExistingHeight) { + ASSERT_EQ(0, index.depositAmountAtHeight(4)); +} + +TEST_F(DepositIndexTest, MultiPopZeroClearsIndex) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(14, 1); + ASSERT_EQ(3, index.popBlocks(0)); + ASSERT_EQ(0, index.depositAmountAtHeight(0)); +} + +TEST_F(DepositIndexTest, GetInterestOnHeight) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(14, 1); + ASSERT_EQ(3, index.depositInterestAtHeight(14)); +} + +TEST_F(DepositIndexTest, CanSubmitNegativeDeposit) { + index.pushBlock(20, 1); + index.pushBlock(-14, 1); +} + +TEST_F(DepositIndexTest, DepositAmountCanBeReduced) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + index.pushBlock(-14, 1); + ASSERT_EQ(9 + 12 - 14, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, PopBlocksZeroReturnsZero) { + ASSERT_EQ(0, index.popBlocks(0)); +} + +TEST_F(DepositIndexTest, PopBlocksZeroRemovesEmptyBlocks) { + index.pushBlock(1, 1); + index.pushBlock(0, 0); + ASSERT_EQ(1, index.popBlocks(2)); + ASSERT_EQ(1, index.lastHeight()); + ASSERT_EQ(1, index.fullDepositAmount()); + ASSERT_EQ(1, index.fullInterestAmount()); +} From 6d45be9ec0fe602eca1fd4c90c23cdd0f173d5f7 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Thu, 9 Jul 2015 15:52:47 +0100 Subject: [PATCH 33/59] Simplewallet improvements --- src/Common/SignalHandler.cpp | 6 +- src/Platform/OSX/System/TcpConnection.cpp | 4 - src/cryptonote_config.h | 3 +- src/p2p/net_node.cpp | 47 ++- src/p2p/net_node.h | 3 +- src/simplewallet/simplewallet.cpp | 2 +- src/transfers/BlockchainSynchronizer.h | 1 - src/version.h.in | 4 +- src/wallet/Wallet.cpp | 71 ++-- src/wallet/WalletUnconfirmedTransactions.cpp | 4 + src/wallet/WalletUnconfirmedTransactions.h | 1 + src/wallet/WalletUserTransactionsCache.cpp | 6 + src/wallet/WalletUserTransactionsCache.h | 1 + tests/unit_tests/test_wallet.cpp | 344 ++++++++++++++++++- 14 files changed, 439 insertions(+), 58 deletions(-) diff --git a/src/Common/SignalHandler.cpp b/src/Common/SignalHandler.cpp index b73735ee19..3795df3d9a 100644 --- a/src/Common/SignalHandler.cpp +++ b/src/Common/SignalHandler.cpp @@ -35,7 +35,10 @@ namespace { void handleSignal() { static std::mutex m_mutex; - std::unique_lock lock(m_mutex); + std::unique_lock lock(m_mutex, std::try_to_lock); + if (!lock.owns_lock()) { + return; + } m_handler(); } @@ -75,6 +78,7 @@ namespace tools { #else signal(SIGINT, posixHandler); signal(SIGTERM, posixHandler); + signal(SIGPIPE, SIG_IGN); m_handler = t; return true; #endif diff --git a/src/Platform/OSX/System/TcpConnection.cpp b/src/Platform/OSX/System/TcpConnection.cpp index dec331eef2..62872f635b 100755 --- a/src/Platform/OSX/System/TcpConnection.cpp +++ b/src/Platform/OSX/System/TcpConnection.cpp @@ -237,10 +237,6 @@ std::pair TcpConnection::getPeerAddressAndPort() { } TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { - int val = 1; - if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) { - throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, result=" + std::to_string(errno)); - } } } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 283b08d570..f4a6642943 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -150,7 +150,8 @@ const CheckpointData CHECKPOINTS[] = { {667000, "a020c8fcaa567845d04b520bb7ebe721e097a9bed2bdb8971081f933b5b42995"}, {689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"}, {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}, - {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"} + {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, + {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"} }; } // CryptoNote diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 80cdffe920..0faf95d4f6 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -435,7 +435,11 @@ namespace CryptoNote m_idleTimer.stop(); m_timedSyncTimer.stop(); - logger(INFO) << "Stopping " << m_connections.size() << " connections"; + logger(INFO) << "Stopping " << m_connections.size() + m_raw_connections.size() << " connections"; + + for (auto& conn : m_raw_connections) { + conn.second.connection.stop(); + } for (auto& conn : m_connections) { conn.second.connection.stop(); @@ -671,32 +675,44 @@ namespace CryptoNote ctx.m_is_income = false; ctx.m_started = time(nullptr); - CryptoNote::LevinProtocol proto(ctx.connection); + auto raw = m_raw_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + try { + CryptoNote::LevinProtocol proto(raw->second.connection); - if (!handshake(proto, ctx, just_take_peerlist)) { - logger(WARNING) << "Failed to HANDSHAKE with peer " << na; - return false; + if (!handshake(proto, raw->second, just_take_peerlist)) { + logger(WARNING) << "Failed to HANDSHAKE with peer " << na; + m_raw_connections.erase(raw); + return false; + } + } catch (...) { + m_raw_connections.erase(raw); + throw; } if (just_take_peerlist) { - logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << ctx << "CONNECTION HANDSHAKED OK AND CLOSED."; + logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << raw->second << "CONNECTION HANDSHAKED OK AND CLOSED."; + m_raw_connections.erase(raw); return true; } peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); pe_local.adr = na; - pe_local.id = ctx.peer_id; + pe_local.id = raw->second.peer_id; time(&pe_local.last_seen); m_peerlist.append_with_peer_white(pe_local); if (m_stop) { + m_raw_connections.erase(raw); throw System::InterruptedException(); } - auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + auto iter = m_connections.emplace(raw->first, std::move(raw->second)).first; + m_raw_connections.erase(raw); + const boost::uuids::uuid& connectionId = iter->first; + p2p_connection_context& connectionContext = iter->second; ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, iter)); + m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connectionContext))); return true; } catch (System::InterruptedException&) { @@ -1219,9 +1235,11 @@ namespace CryptoNote ctx.m_remote_port = addressAndPort.second; auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; + const boost::uuids::uuid& connectionId = iter->first; + p2p_connection_context& connection = iter->second; ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, iter)); + m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connection))); } } catch (System::InterruptedException&) { } catch (const std::exception& e) { @@ -1274,10 +1292,9 @@ namespace CryptoNote } } - void node_server::connectionHandler(ConnectionIterator connIter) { + void node_server::connectionHandler(const boost::uuids::uuid& connectionId, p2p_connection_context& ctx) { try { - auto& ctx = connIter->second; on_connection_new(ctx); LevinProtocol proto(ctx.connection); @@ -1320,10 +1337,10 @@ namespace CryptoNote logger(WARNING) << "Exception in connectionHandler: " << e.what(); } - connIter->second.writeLatch.wait(); + ctx.writeLatch.wait(); - on_connection_close(connIter->second); - m_connections.erase(connIter); + on_connection_close(ctx); + m_connections.erase(connectionId); if (--m_spawnCount == 0) { m_shutdownCompleteEvent.set(); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index fc6bd13f6c..f894606c61 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -171,10 +171,11 @@ namespace CryptoNote typedef std::unordered_map> ConnectionContainer; typedef ConnectionContainer::iterator ConnectionIterator; + ConnectionContainer m_raw_connections; ConnectionContainer m_connections; void acceptLoop(); - void connectionHandler(ConnectionIterator connIter); + void connectionHandler(const boost::uuids::uuid& connectionId, p2p_connection_context& connection); void onIdle(); void timedSyncLoop(); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 61d095ac48..69c0184747 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -791,7 +791,7 @@ bool simple_wallet::listTransfers(const std::vector& args) { for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { TransactionInfo txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active) { + if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } diff --git a/src/transfers/BlockchainSynchronizer.h b/src/transfers/BlockchainSynchronizer.h index 2ec1413cb1..feeb508cfc 100644 --- a/src/transfers/BlockchainSynchronizer.h +++ b/src/transfers/BlockchainSynchronizer.h @@ -125,7 +125,6 @@ class BlockchainSynchronizer : INode& m_node; const crypto::hash m_genesisBlockHash; - std::vector knownTxIds; crypto::hash lastBlockId; State m_currentState; diff --git a/src/version.h.in b/src/version.h.in index 117aada244..7a7edafc61 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.4" -#define PROJECT_VERSION_BUILD_NO "461" +#define PROJECT_VERSION "1.0.4.1" +#define PROJECT_VERSION_BUILD_NO "466" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index a25dc275e6..d1ce67cab7 100755 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -16,14 +16,15 @@ // along with Bytecoin. If not, see . #include "Wallet.h" -#include "serialization/binary_utils.h" -#include "WalletUtils.h" -#include "WalletSerializer.h" -#include #include +#include +#include "serialization/binary_utils.h" +#include "WalletHelper.h" #include "WalletSerialization.h" +#include "WalletSerializer.h" +#include "WalletUtils.h" namespace { @@ -93,6 +94,7 @@ class SaveWaiter : public CryptoNote::IWalletObserver { std::promise promise; std::future future; }; + } //namespace namespace CryptoNote { @@ -125,7 +127,6 @@ Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : m_onInitSyncStarter(new SyncStarter(m_blockchainSync)) { addObserver(m_onInitSyncStarter.get()); - m_blockchainSync.addObserver(this); } Wallet::~Wallet() { @@ -139,10 +140,10 @@ Wallet::~Wallet() { } } - m_blockchainSync.removeObserver(this); - m_blockchainSync.stop(); - m_asyncContextCounter.waitAsyncContextsFinish(); - m_sender.release(); + m_blockchainSync.removeObserver(this); + m_blockchainSync.stop(); + m_asyncContextCounter.waitAsyncContextsFinish(); + m_sender.release(); } void Wallet::addObserver(IWalletObserver* observer) { @@ -215,7 +216,7 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { m_password = password; m_state = LOADING; - + m_asyncContextCounter.addAsyncContext(); std::thread loader(&Wallet::doLoad, this, std::ref(source)); loader.detach(); @@ -234,6 +235,8 @@ void Wallet::initSync() { m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.get_keys(), *m_transferDetails)); m_state = INITIALIZED; + + m_blockchainSync.addObserver(this); } void Wallet::doLoad(std::istream& source) { @@ -248,20 +251,18 @@ void Wallet::doLoad(std::istream& source) { initSync(); try { - if (!cache.empty()) { - std::stringstream stream(cache); - m_transfersSync.load(stream); - } + if (!cache.empty()) { + std::stringstream stream(cache); + m_transfersSync.load(stream); + } } catch (const std::exception&) { // ignore cache loading errors - } - } - catch (std::system_error& e) { + } + } catch (std::system_error& e) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); m_observerManager.notify(&IWalletObserver::initCompleted, e.code()); return; - } - catch (std::exception&) { + } catch (std::exception&) { runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; @@ -295,31 +296,39 @@ void Wallet::shutdown() { std::unique_lock lock(m_cacheMutex); m_isStopping = false; m_state = NOT_INITIALIZED; + + const AccountAddress& accountAddress = reinterpret_cast(m_account.get_keys().m_account_address); + auto subObject = m_transfersSync.getSubscription(accountAddress); + assert(subObject != nullptr); + subObject->removeObserver(this); + m_transfersSync.removeSubscription(accountAddress); + m_transferDetails = nullptr; + + m_transactionsCache.reset(); + m_lastNotifiedActualBalance = 0; + m_lastNotifiedPendingBalance = 0; } } void Wallet::reset() { InitWaiter initWaiter; SaveWaiter saveWaiter; - - addObserver(&initWaiter); - addObserver(&saveWaiter); + WalletHelper::IWalletRemoveObserverGuard initGuarantee(*this, initWaiter); + WalletHelper::IWalletRemoveObserverGuard saveGuarantee(*this, saveWaiter); std::stringstream ss; try { - save(ss, false, false); + save(ss, false, false); auto saveError = saveWaiter.waitSave(); - if (!saveError) { - shutdown(); - initAndLoad(ss, m_password); + if (!saveError) { + shutdown(); + initAndLoad(ss, m_password); initWaiter.waitInit(); + } + } catch (std::exception& e) { + std::cout << "exception in reset: " << e.what() << std::endl; } - } catch (std::exception&) { - } - - removeObserver(&saveWaiter); - removeObserver(&initWaiter); } void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index a96ae36e44..44dde435d7 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -123,5 +123,9 @@ void WalletUnconfirmedTransactions::collectUsedOutputs() { m_usedOutputs = std::move(used); } +void WalletUnconfirmedTransactions::reset() { + m_unconfirmedTxs.clear(); + m_usedOutputs.clear(); +} } /* namespace CryptoNote */ diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index e36b8bbfb7..a5b71152bd 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -64,6 +64,7 @@ class WalletUnconfirmedTransactions uint64_t countUnconfirmedOutsAmount() const; uint64_t countUnconfirmedTransactionsAmount() const; bool isUsed(const TransactionOutputInformation& out) const; + void reset(); private: diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index 961419ac3d..a1f0171cc0 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -293,5 +293,11 @@ void WalletUserTransactionsCache::updateUnconfirmedTransactions() { Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { return m_transfers.at(transferId); } + +void WalletUserTransactionsCache::reset() { + m_transactions.clear(); + m_transfers.clear(); + m_unconfirmedTransactions.reset(); +} } //namespace CryptoNote diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index 259c0b747b..1bdf9f9b12 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -57,6 +57,7 @@ class WalletUserTransactionsCache Transfer& getTransfer(TransferId transferId); bool isUsed(const TransactionOutputInformation& out) const; + void reset(); private: diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index 4a5b829727..57cde04015 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -24,6 +24,7 @@ #include "EventWaiter.h" #include "INode.h" #include "wallet/Wallet.h" +#include "wallet/WalletHelper.h" #include "cryptonote_core/account.h" #include "cryptonote_core/Currency.h" @@ -285,7 +286,7 @@ void WalletApi::TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mix bob->initAndGenerate("pass2"); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); - ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, 0, "")); + ASSERT_NO_FATAL_FAILURE(TransferMoney(*alice, *bob, transferAmount, fee, mixIn, "")); ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); generator.generateEmptyBlocks(10); @@ -1327,3 +1328,344 @@ TEST_F(WalletApi, DISABLED_loadingBrokenCache) { ASSERT_NO_FATAL_FAILURE(WaitWalletLoad(aliceWalletObserver.get(), result)); ASSERT_EQ(result.value(), 0); } + +TEST_F(WalletApi, shutdownAllowsInitializeWalletWithTheSameKeys) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::WalletAccountKeys accountKeys; + alice->getAccountKeys(accountKeys); + + alice->shutdown(); + alice->initWithKeys(accountKeys, "pass"); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransactionCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, shutdownAllowsInitializeWalletWithDifferentKeys) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->shutdown(); + alice->initAndGenerate("pass"); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(0, alice->getTransactionCount()); + + alice->shutdown(); +} + +namespace { +class WalletSynchronizationProgressUpdatedObserver : public CryptoNote::IWalletObserver { +public: + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t /*total*/) override { + m_current = current; + } + + uint64_t m_current = 0; +}; +} + +TEST_F(WalletApi, shutdownDoesNotRemoveObservers) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + WalletSynchronizationProgressUpdatedObserver observer; + CryptoNote::WalletHelper::IWalletRemoveObserverGuard observerGuard(*alice, observer); + + alice->shutdown(); + observer.m_current = 0; + alice->initAndGenerate("pass"); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(5, observer.m_current); + + observerGuard.removeObserver(); + alice->shutdown(); +} + +namespace { +class WalletTransactionEventCounter : public CryptoNote::IWalletObserver { +public: + virtual void externalTransactionCreated(CryptoNote::TransactionId /*transactionId*/) override { + ++m_count; + } + + virtual void transactionUpdated(CryptoNote::TransactionId /*transactionId*/) override { + ++m_count; + } + + size_t m_count = 0; +}; +} + +TEST_F(WalletApi, afterShutdownAndInitWalletDoesNotSendNotificationsRelatedToOldAddress) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + std::string aliceAddress1 = alice->getAddress(); + CryptoNote::WalletAccountKeys accountKeys1; + alice->getAccountKeys(accountKeys1); + + alice->shutdown(); + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + std::string aliceAddress2 = alice->getAddress(); + + alice->shutdown(); + alice->initWithKeys(accountKeys1, "pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + WalletTransactionEventCounter observer; + CryptoNote::WalletHelper::IWalletRemoveObserverGuard observerGuard(*alice, observer); + + prepareBobWallet(); + bob->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + GetOneBlockReward(*bob); + generator.generateEmptyBlocks(10); + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + std::vector transfers; + transfers.push_back({ aliceAddress1, TEST_BLOCK_REWARD / 10 }); + transfers.push_back({ aliceAddress2, TEST_BLOCK_REWARD / 5 }); + bob->sendTransaction(transfers, m_currency.minimumFee()); + std::error_code sendResult; + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(bobWalletObserver.get(), sendResult)); + + generator.generateEmptyBlocks(1); + bobNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(bobWalletObserver.get())); + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, observer.m_count); + + observerGuard.removeObserver(); + bob->shutdown(); + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotChangeAddress) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + auto expectedAddress = alice->getAddress(); + alice->reset(); + ASSERT_EQ(expectedAddress, alice->getAddress()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotChangeAccountKeys) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + CryptoNote::WalletAccountKeys expectedAccountKeys; + alice->getAccountKeys(expectedAccountKeys); + + alice->reset(); + + CryptoNote::WalletAccountKeys actualAccountKeys; + alice->getAccountKeys(actualAccountKeys); + + ASSERT_EQ(expectedAccountKeys.spendPublicKey, actualAccountKeys.spendPublicKey); + ASSERT_EQ(expectedAccountKeys.spendSecretKey, actualAccountKeys.spendSecretKey); + ASSERT_EQ(expectedAccountKeys.viewPublicKey, actualAccountKeys.viewPublicKey); + ASSERT_EQ(expectedAccountKeys.viewSecretKey, actualAccountKeys.viewSecretKey); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotRemoveObservers) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + WalletSynchronizationProgressUpdatedObserver observer; + CryptoNote::WalletHelper::IWalletRemoveObserverGuard observerGuard(*alice, observer); + + alice->reset(); + observer.m_current = 0; + + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(5, observer.m_current); + + observerGuard.removeObserver(); + alice->shutdown(); +} + +TEST_F(WalletApi, resetDoesNotChangePassword) { + std::string password = "password"; + std::string newPassword = "new_password"; + + alice->initAndGenerate(password); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + ASSERT_TRUE(static_cast(alice->changePassword(newPassword, password))); + ASSERT_FALSE(static_cast(alice->changePassword(password, newPassword))); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsPendingBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->pendingBalance()); + alice->reset(); + ASSERT_EQ(0, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsActualBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->actualBalance()); + alice->reset(); + ASSERT_EQ(0, alice->actualBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsTransactionHistory) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransactionCount()); + alice->reset(); + ASSERT_EQ(0, alice->getTransactionCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetClearsTransfersHistory) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->sendTransaction({ alice->getAddress(), 100 }, m_currency.minimumFee()); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransferCount()); + alice->reset(); + ASSERT_EQ(0, alice->getTransferCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncRestorePendingBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncRestoreActualBalance) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(TEST_BLOCK_REWARD, alice->actualBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncRestoreTransactions) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(1, alice->getTransactionCount()); + + alice->shutdown(); +} + +TEST_F(WalletApi, resetAndSyncDoNotRestoreTransfers) { + alice->initAndGenerate("pass"); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + GetOneBlockReward(*alice); + generator.generateEmptyBlocks(10); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + alice->sendTransaction({ alice->getAddress(), 100 }, m_currency.minimumFee()); + ASSERT_NO_FATAL_FAILURE(WaitWalletSend(aliceWalletObserver.get())); + + alice->reset(); + aliceNode->updateObservers(); + ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); + + ASSERT_EQ(0, alice->getTransferCount()); + + alice->shutdown(); +} From 4ceaeaf6f8ad8293e6ac80060fef80eb6bdf515c Mon Sep 17 00:00:00 2001 From: xdn-project Date: Fri, 10 Jul 2015 11:17:26 +0000 Subject: [PATCH 34/59] Wallet deposit support --- include/ITransfersContainer.h | 10 + include/ITransfersSynchronizer.h | 7 + include/IWallet.h | 12 +- src/cryptonote_config.h | 1 + .../cryptonote_format_utils.cpp | 3 +- src/daemon/daemon_commands_handler.h | 18 +- src/serialization/ISerializer.h | 7 + src/transfers/TransfersContainer.cpp | 251 ++++- src/transfers/TransfersContainer.h | 107 +- src/transfers/TransfersSubscription.cpp | 27 +- src/version.h.in | 4 +- src/wallet/Wallet.cpp | 307 +++++- src/wallet/Wallet.h | 28 +- src/wallet/WalletErrors.h | 12 +- src/wallet/WalletEvent.h | 41 + src/wallet/WalletRequest.h | 18 +- src/wallet/WalletSendTransactionContext.h | 3 +- src/wallet/WalletSerialization.cpp | 17 +- src/wallet/WalletSerialization.h | 2 + src/wallet/WalletSerializer.cpp | 10 +- src/wallet/WalletTransactionSender.cpp | 441 +++++++- src/wallet/WalletTransactionSender.h | 45 +- src/wallet/WalletUnconfirmedTransactions.cpp | 94 +- src/wallet/WalletUnconfirmedTransactions.h | 39 +- src/wallet/WalletUserTransactionsCache.cpp | 390 ++++++- src/wallet/WalletUserTransactionsCache.h | 55 +- tests/unit_tests/TestBlockchainGenerator.h | 2 + tests/unit_tests/TransactionApiHelpers.h | 4 +- tests/unit_tests/test_TransfersContainer.cpp | 425 +++++++- .../test_TransfersContainerKeyImage.cpp | 37 +- .../unit_tests/test_TransfersSubscription.cpp | 2 +- .../test_WalletUserTransactionsCache.cpp | 22 +- tests/unit_tests/test_wallet.cpp | 973 +++++++++++++++++- 33 files changed, 3191 insertions(+), 223 deletions(-) diff --git a/include/ITransfersContainer.h b/include/ITransfersContainer.h index c1f1d847ae..3948a861c6 100644 --- a/include/ITransfersContainer.h +++ b/include/ITransfersContainer.h @@ -85,14 +85,24 @@ class ITransfersContainer : public IStreamSerializable { IncludeDefault = IncludeKeyUnlocked }; + enum class TransferState : uint32_t { + TransferUnconfirmed, + TransferLocked, + TransferAvailable, + TransferSpent + }; + virtual size_t transfersCount() = 0; virtual size_t transactionsCount() = 0; virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0; virtual void getOutputs(std::vector& transfers, uint32_t flags = IncludeDefault) = 0; virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; + //only type flags are feasible for this function + virtual std::vector getTransactionInputs(const Hash& transactionHash, uint32_t flags) const = 0; virtual void getUnconfirmedTransactions(std::vector& transactions) = 0; virtual std::vector getSpentOutputs() = 0; + virtual bool getTransfer(const Hash& transactionHash, uint32_t outputInTransaction, TransactionOutputInformation& transfer, TransferState& transferState) const = 0; }; } diff --git a/include/ITransfersSynchronizer.h b/include/ITransfersSynchronizer.h index 34914000e5..5ac9ae5102 100644 --- a/include/ITransfersSynchronizer.h +++ b/include/ITransfersSynchronizer.h @@ -39,6 +39,13 @@ class ITransfersObserver { * for the same \a transactionHash. */ virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { } + + /** + * \note this method MUST be called after appropriate onTransactionUpdated has been called + */ + virtual void onTransfersUnlocked(ITransfersSubscription* object, const std::vector& unlockedTransfers) {} + + virtual void onTransfersLocked(ITransfersSubscription* object, const std::vector& lockedTransfers) {} }; class ITransfersSubscription : public IObservable < ITransfersObserver > { diff --git a/include/IWallet.h b/include/IWallet.h index 62893106e5..66ebd8194c 100644 --- a/include/IWallet.h +++ b/include/IWallet.h @@ -71,6 +71,7 @@ struct Deposit { uint32_t term; uint64_t amount; uint64_t interest; + bool locked; }; typedef std::array WalletPublicKey; @@ -101,14 +102,17 @@ class IWalletObserver { virtual void synchronizationCompleted(std::error_code result) {} virtual void actualBalanceUpdated(uint64_t actualBalance) {} virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} + virtual void actualDepositBalanceUpdated(uint64_t actualDepositBalance) {} + virtual void pendingDepositBalanceUpdated(uint64_t pendingDepositBalance) {} virtual void externalTransactionCreated(TransactionId transactionId) {} virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) {} virtual void transactionUpdated(TransactionId transactionId) {} + virtual void depositsUpdated(const std::vector& depositIds) {} }; class IWallet { public: - virtual ~IWallet() {} ; + virtual ~IWallet() {} virtual void addObserver(IWalletObserver* observer) = 0; virtual void removeObserver(IWalletObserver* observer) = 0; @@ -126,18 +130,24 @@ class IWallet { virtual uint64_t actualBalance() = 0; virtual uint64_t pendingBalance() = 0; + virtual uint64_t actualDepositBalance() = 0; + virtual uint64_t pendingDepositBalance() = 0; virtual size_t getTransactionCount() = 0; virtual size_t getTransferCount() = 0; + virtual size_t getDepositCount() = 0; virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) = 0; virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0; + virtual bool getDeposit(DepositId depositId, Deposit& deposit) = 0; virtual std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const = 0; virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()) = 0; virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()) = 0; + virtual TransactionId deposit(uint32_t term, uint64_t amount, uint64_t fee, uint64_t mixIn = 0) = 0; + virtual TransactionId withdrawDeposits(const std::vector& depositIds, uint64_t fee) = 0; virtual std::error_code cancelTransaction(size_t transferId) = 0; virtual void getAccountKeys(WalletAccountKeys& keys) = 0; diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 4fdabb4198..2948914b7a 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -144,6 +144,7 @@ const std::initializer_list CHECKPOINTS = { { 136212, "5a935b048194d8b6ffb33b744c73cbe632da4f3c4d5e4c4488967d9431ba2a36" }, { 136213, "336b687fdb96457cf4060072f76fc9e4e9281744822e0892c9ea128445bbebc7" }, { 137000, "ae73be718076ab00371f81fa5f604c9e020f25abcb48f85b631bde0cabaff048" }, + { 143000, "2a9dc9638f091078a67d085ae97caa28f1061c0ae088463977f3df6435a9b585" }, }; } // cryptonote diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 83eb68300f..582acbd0d4 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -231,7 +231,8 @@ namespace cryptonote return true; } - bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, const std::vector& messages, const std::vector& extra, uint64_t unlock_time, Transaction& tx) + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, + const std::vector& messages, const std::vector& extra, uint64_t unlock_time, Transaction& tx) { tx.vin.clear(); tx.vout.clear(); diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h index ab40c1ec9c..9ec931ef03 100644 --- a/src/daemon/daemon_commands_handler.h +++ b/src/daemon/daemon_commands_handler.h @@ -241,6 +241,10 @@ class daemon_cmmands_handler return true; } //-------------------------------------------------------------------------------- + uint64_t calculatePercent(const cryptonote::Currency& currency, uint64_t value, uint64_t total) { + return static_cast(100.0 * currency.coin() * static_cast(value) / static_cast(total)); + } + //-------------------------------------------------------------------------------- bool print_stat(const std::vector& args) { uint64_t height = 0; auto& core = m_srv.get_payload_object().get_core(); @@ -262,11 +266,19 @@ class daemon_cmmands_handler } } + uint64_t totalCoinsInNetwork = core.coinsEmittedAtHeight(height); + uint64_t totalCoinsOnDeposits = core.depositAmountAtHeight(height); + uint64_t amountOfActiveCoins = totalCoinsInNetwork - totalCoinsOnDeposits; + + const auto& currency = core.currency(); std::cout << "Block height: " << height << std::endl; std::cout << "Block difficulty: " << core.difficultyAtHeight(height) << std::endl; - std::cout << "Total coins in network: " << core.currency().formatAmount(core.coinsEmittedAtHeight(height)) << std::endl; - std::cout << "Total coins on deposits: " << core.currency().formatAmount(core.depositAmountAtHeight(height)) << std::endl; - std::cout << "Total interest paid: " << core.currency().formatAmount(core.depositInterestAtHeight(height)) << std::endl; + std::cout << "Total coins in network: " << currency.formatAmount(totalCoinsInNetwork) << std::endl; + std::cout << "Total coins on deposits: " << currency.formatAmount(totalCoinsOnDeposits) << + " (" << currency.formatAmount(calculatePercent(currency, totalCoinsOnDeposits, totalCoinsInNetwork)) << "%)" << std::endl; + std::cout << "Amount of active coins: " << currency.formatAmount(amountOfActiveCoins) << + " (" << currency.formatAmount(calculatePercent(currency, amountOfActiveCoins, totalCoinsInNetwork)) << "%)" << std::endl; + std::cout << "Total interest paid: " << currency.formatAmount(core.depositInterestAtHeight(height)) << std::endl; return true; } diff --git a/src/serialization/ISerializer.h b/src/serialization/ISerializer.h index 9cf9f8fc71..76337be9b8 100644 --- a/src/serialization/ISerializer.h +++ b/src/serialization/ISerializer.h @@ -58,4 +58,11 @@ void serialize(T& value, const std::string& name, ISerializer& serializer) { return; } +#ifdef __clang__ +template<> inline +ISerializer& ISerializer::operator()(size_t& value, const std::string& name) { + return operator()(*reinterpret_cast(&value), name); +} +#endif + } diff --git a/src/transfers/TransfersContainer.cpp b/src/transfers/TransfersContainer.cpp index 2d9cfcb189..0d7c51f12e 100644 --- a/src/transfers/TransfersContainer.cpp +++ b/src/transfers/TransfersContainer.cpp @@ -4,6 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "TransfersContainer.h" + +#include + #include "IWallet.h" #include "cryptonote_core/cryptonote_format_utils.h" @@ -25,7 +28,7 @@ void serialize(TransactionInformation& ti, const std::string& name, cryptonote:: s(ti.messages, ""); } -const uint32_t TRANSFERS_CONTAINER_STORAGE_VERSION = 0; +const uint32_t TRANSFERS_CONTAINER_STORAGE_VERSION = 1; namespace { template @@ -80,8 +83,32 @@ namespace { TransferIteratorList createTransferIteratorList(const std::pair& itPair) { return TransferIteratorList(itPair.first, itPair.second); } + + TransferUnlockJob makeTransferUnlockJob(const TransactionOutputInformationEx& output, size_t transactionSpendableAge) { + TransferUnlockJob job; + + uint64_t unlockTime = output.unlockTime == 0 ? output.blockHeight : output.unlockTime; + + if (output.type == TransactionTypes::OutputType::Multisignature && output.term != 0) { + job.unlockHeight = std::max({unlockTime, output.blockHeight + output.term, output.blockHeight + transactionSpendableAge}); + } else { + job.unlockHeight = std::max(unlockTime, output.blockHeight + transactionSpendableAge); + } + + job.transactionOutputId.transactionHash = output.transactionHash; + job.transactionOutputId.outputInTransaction = output.outputInTransaction; + + return job; + } } +size_t TransactionOutputId::hash() const { + size_t hash = 0; + boost::hash_combine(hash, transactionHash); + boost::hash_combine(hash, outputInTransaction); + + return hash; +} SpentOutputDescriptor::SpentOutputDescriptor() : m_type(TransactionTypes::OutputType::Invalid) { @@ -156,7 +183,7 @@ TransfersContainer::TransfersContainer(const cryptonote::Currency& currency, siz bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers, - std::vector&& messages) { + std::vector&& messages, std::vector* unlockingTransfers) { std::unique_lock lock(m_mutex); if (block.height < m_currentHeight) { @@ -175,7 +202,11 @@ bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti } if (block.height != UNCONFIRMED_TRANSACTION_HEIGHT) { - m_currentHeight = block.height; + auto finishedJobs = doAdvanceHeight(block.height); + + if (unlockingTransfers != nullptr) { + *unlockingTransfers = std::move(finishedJobs); + } } return added; @@ -245,6 +276,8 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr } } + addUnlockJob(info); + auto result = m_availableTransfers.emplace(std::move(info)); assert(result.second); } @@ -304,6 +337,7 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra } assert(spendingTransferIt->keyImage == input.keyImage); + deleteUnlockJob(*spendingTransferIt); copyToSpent(block, tx, i, *spendingTransferIt); // erase from available outputs outputDescriptorIndex.erase(spendingTransferIt); @@ -317,6 +351,7 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra auto& outputDescriptorIndex = m_availableTransfers.get(); auto availableOutputIt = outputDescriptorIndex.find(SpentOutputDescriptor(input.amount, input.outputIndex)); if (availableOutputIt != outputDescriptorIndex.end()) { + deleteUnlockJob(*availableOutputIt); copyToSpent(block, tx, i, *availableOutputIt); // erase from available outputs outputDescriptorIndex.erase(availableOutputIt); @@ -390,6 +425,8 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const } } + addUnlockJob(transfer); + auto result = m_availableTransfers.emplace(std::move(transfer)); assert(result.second); @@ -423,8 +460,12 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) assert(it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); assert(it->globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); - auto result = m_availableTransfers.emplace(static_cast(*it)); + const TransactionOutputInformationEx& unspendingTransfer = static_cast(*it); + + addUnlockJob(unspendingTransfer); + auto result = m_availableTransfers.emplace(unspendingTransfer); assert(result.second); + it = spendingTransactionIndex.erase(it); if (result.first->type == TransactionTypes::OutputType::Key) { @@ -446,6 +487,8 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) auto& transactionTransfersIndex = m_availableTransfers.get(); auto transactionTransfersRange = transactionTransfersIndex.equal_range(transactionHash); for (auto it = transactionTransfersRange.first; it != transactionTransfersRange.second;) { + deleteUnlockJob(*it); + if (it->type == TransactionTypes::OutputType::Key) { KeyImage keyImage = it->keyImage; it = transactionTransfersIndex.erase(it); @@ -473,13 +516,12 @@ void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionR assert(result.second); } -std::vector TransfersContainer::detach(uint64_t height) { +void TransfersContainer::detach(uint64_t height, std::vector& deletedTransactions, std::vector& lockedTransfers) { // This method expects that UNCONFIRMED_TRANSACTION_HEIGHT is a big positive number assert(height < UNCONFIRMED_TRANSACTION_HEIGHT); std::lock_guard lk(m_mutex); - std::vector deletedTransactions; auto& spendingTransactionIndex = m_spentTransfers.get(); auto& blockHeightIndex = m_transactions.get<1>(); auto it = blockHeightIndex.end(); @@ -508,10 +550,12 @@ std::vector TransfersContainer::detach(uint64_t height) { } } + uint64_t prevHeight = m_currentHeight; + // TODO: notification on detach m_currentHeight = height == 0 ? 0 : height - 1; - return deletedTransactions; + getLockingTransfers(prevHeight, m_currentHeight, deletedTransactions, lockedTransfers); } namespace { @@ -563,15 +607,24 @@ void TransfersContainer::updateTransfersVisibility(const KeyImage& keyImage) { } } -bool TransfersContainer::advanceHeight(uint64_t height) { +std::vector TransfersContainer::advanceHeight(uint64_t height) { std::lock_guard lk(m_mutex); - if (m_currentHeight <= height) { - m_currentHeight = height; - return true; + return doAdvanceHeight(height); +} + +/** + * \pre m_mutex is locked + */ +std::vector TransfersContainer::doAdvanceHeight(uint64_t height) { + if (height < m_currentHeight) { + throw std::invalid_argument("New height is less then current while advancing height"); } - return false; + uint64_t prevHeight = m_currentHeight; + m_currentHeight = height; + + return getUnlockingTransfers(prevHeight, m_currentHeight); } size_t TransfersContainer::transfersCount() { @@ -686,6 +739,24 @@ std::vector TransfersContainer::getTransactionOutp return result; } +std::vector TransfersContainer::getTransactionInputs(const Hash& transactionHash, uint32_t flags) const { + //only type flags are feasible + assert((flags & IncludeStateAll) == 0); + flags |= IncludeStateUnlocked; + + std::lock_guard lk(m_mutex); + + std::vector result; + auto transactionInputsRange = m_spentTransfers.get().equal_range(transactionHash); + for (auto it = transactionInputsRange.first; it != transactionInputsRange.second; ++it) { + if (isIncluded(*it, IncludeStateUnlocked, flags)) { + result.push_back(*it); + } + } + + return result; +} + void TransfersContainer::getUnconfirmedTransactions(std::vector& transactions) { std::lock_guard lk(m_mutex); transactions.clear(); @@ -719,6 +790,47 @@ std::vector TransfersContainer::getSpentOutpu return spentOutputs; } +bool TransfersContainer::getTransfer(const Hash& transactionHash, uint32_t outputInTransaction, TransactionOutputInformation& transfer, TransferState& transferState) const { + TransactionOutputId transferId { transactionHash, outputInTransaction }; + + std::lock_guard lk(m_mutex); + + auto& availableIndex = m_availableTransfers.get(); + + auto availableIt = availableIndex.find(transferId); + if (availableIt != availableIndex.end()) { + transfer = *availableIt; + + if (!isSpendTimeUnlocked(*availableIt) || m_currentHeight < availableIt->blockHeight + m_transactionSpendableAge) { + transferState = TransferState::TransferLocked; + } else { + transferState = TransferState::TransferAvailable; + } + + return true; + } + + auto& unconfirmedIndex = m_unconfirmedTransfers.get(); + + auto unconfirmedIt = unconfirmedIndex.find(transferId); + if (unconfirmedIt != unconfirmedIndex.end()) { + transfer = *unconfirmedIt; + transferState = TransferState::TransferUnconfirmed; + return true; + } + + auto& spentIndex = m_spentTransfers.get(); + + auto spentIt = spentIndex.find(transferId); + if (spentIt != spentIndex.end()) { + transfer = *unconfirmedIt; + transferState = TransferState::TransferSpent; + return true; + } + + return false; +} + void TransfersContainer::save(std::ostream& os) { std::lock_guard lk(m_mutex); cryptonote::BinaryOutputStreamSerializer s(os); @@ -730,6 +842,7 @@ void TransfersContainer::save(std::ostream& os) { cryptonote::writeSequence(m_unconfirmedTransfers.begin(), m_unconfirmedTransfers.end(), "unconfirmedTransfers", s); cryptonote::writeSequence(m_availableTransfers.begin(), m_availableTransfers.end(), "availableTransfers", s); cryptonote::writeSequence(m_spentTransfers.begin(), m_spentTransfers.end(), "spentTransfers", s); + cryptonote::writeSequence(m_transfersUnlockJobs.begin(), m_transfersUnlockJobs.end(), "transfersUnlockJobs", s); } void TransfersContainer::load(std::istream& in) { @@ -748,6 +861,7 @@ void TransfersContainer::load(std::istream& in) { UnconfirmedTransfersMultiIndex unconfirmedTransfers; AvailableTransfersMultiIndex availableTransfers; SpentTransfersMultiIndex spentTransfers; + TransfersUnlockMultiIndex transfersUnlockJobs; s(currentHeight, "height"); cryptonote::readSequence(std::inserter(transactions, transactions.end()), "transactions", s); @@ -755,11 +869,32 @@ void TransfersContainer::load(std::istream& in) { cryptonote::readSequence(std::inserter(availableTransfers, availableTransfers.end()), "availableTransfers", s); cryptonote::readSequence(std::inserter(spentTransfers, spentTransfers.end()), "spentTransfers", s); + if (version != 0) { + cryptonote::readSequence(std::inserter(transfersUnlockJobs, transfersUnlockJobs.end()), "transfersUnlockJobs", s); + } else { + rebuildTransfersUnlockJobs(transfersUnlockJobs, availableTransfers, spentTransfers); + } + m_currentHeight = currentHeight; m_transactions = std::move(transactions); m_unconfirmedTransfers = std::move(unconfirmedTransfers); m_availableTransfers = std::move(availableTransfers); m_spentTransfers = std::move(spentTransfers); + m_transfersUnlockJobs = std::move(transfersUnlockJobs); +} + +void TransfersContainer::rebuildTransfersUnlockJobs(TransfersUnlockMultiIndex& transfersUnlockJobs, const AvailableTransfersMultiIndex& availableTransfers, + const SpentTransfersMultiIndex& spentTransfers) { + + for (auto it = availableTransfers.begin(); it != availableTransfers.end(); ++it) { + TransferUnlockJob job = makeTransferUnlockJob(*it, m_transactionSpendableAge); + transfersUnlockJobs.emplace(std::move(job)); + } + + for (auto it = spentTransfers.begin(); it != spentTransfers.end(); ++it) { + TransferUnlockJob job = makeTransferUnlockJob(*it, m_transactionSpendableAge); + transfersUnlockJobs.emplace(std::move(job)); + } } bool TransfersContainer::isSpendTimeUnlocked(const TransactionOutputInformationEx& info) const { @@ -806,4 +941,96 @@ bool TransfersContainer::isIncluded(const TransactionOutputInformationEx& output ((flags & state) != 0); } +/** + * \pre m_mutex is locked + */ +void TransfersContainer::addUnlockJob(const TransactionOutputInformationEx& output) { + TransferUnlockJob job = makeTransferUnlockJob(output, m_transactionSpendableAge); + + auto r = m_transfersUnlockJobs.emplace(std::move(job)); + assert(r.second); +} + +void TransfersContainer::deleteUnlockJob(const TransactionOutputInformationEx& output) { + auto& index = m_transfersUnlockJobs.get(); + + auto it = index.find(output.getTransactionOutputId()); + if (it == index.end()) { + return; + } + + index.erase(it); +} + +/** + * \pre m_mutex is locked + */ +std::vector TransfersContainer::getUnlockingTransfers(uint64_t prevHeight, uint64_t currentHeight) { + if (currentHeight < prevHeight) { + assert(false); + throw std::invalid_argument("New height is less then current height"); + } + + auto& index = m_transfersUnlockJobs.get(); + auto start = index.upper_bound(prevHeight); + auto end = index.upper_bound(currentHeight); + + if (start == end) { + //no transfers to unlock + return std::vector(); + } + + std::vector unlockingTransfers; + unlockingTransfers.reserve(std::distance(start, end)); + + for (auto it = start; it != end; ++it) { + TransactionOutputInformation output = getAvailableOutput(it->transactionOutputId); + unlockingTransfers.emplace_back(std::move(output)); + } + + return unlockingTransfers; +} + +/** + * \pre m_mutex is locked + */ +void TransfersContainer::getLockingTransfers(uint64_t prevHeight, uint64_t currentHeight, const std::vector& deletedTransactions, + std::vector& lockingTransfers) { + + if (currentHeight > prevHeight) { + return; + } + + auto& index = m_transfersUnlockJobs.get(); + auto start = index.upper_bound(currentHeight); + auto end = index.upper_bound(prevHeight); + + if (start == end) { + //no transfers to lock + return; + } + + lockingTransfers.reserve(lockingTransfers.size() + std::distance(start, end)); + for (auto it = start; it != end; ++it) { + TransactionOutputInformation output = getAvailableOutput(it->transactionOutputId); + lockingTransfers.emplace_back(std::move(output)); + } +} + +/** + * \pre m_mutex is locked + * \pre requested output must exist + */ +TransactionOutputInformation TransfersContainer::getAvailableOutput(const TransactionOutputId& transactionOutputId) const { + auto& availableIndex = m_availableTransfers.get(); + auto availableIt = availableIndex.find(transactionOutputId); + + assert(availableIt != availableIndex.end()); + if (availableIt == availableIndex.end()) { + throw std::invalid_argument("The output is supposed to be available"); + } + + return *availableIt; +} + } diff --git a/src/transfers/TransfersContainer.h b/src/transfers/TransfersContainer.h index 5d120b97af..56fffb427f 100644 --- a/src/transfers/TransfersContainer.h +++ b/src/transfers/TransfersContainer.h @@ -29,6 +29,35 @@ namespace CryptoNote { struct TransactionOutputInformationIn; +struct TransactionOutputId { + Hash transactionHash; + uint32_t outputInTransaction; + + size_t hash() const; + bool operator==(const TransactionOutputId& rhs) const { + if (transactionHash != rhs.transactionHash) { + return false; + } + + if (outputInTransaction != rhs.outputInTransaction) { + return false; + } + + return true; + } + + void serialize(cryptonote::ISerializer& s, const std::string& name) { + s(transactionHash, "transactionHash"); + s(outputInTransaction, "outputInTransaction"); + } +}; + +struct TransactionOutputIdHasher { + size_t operator() (const TransactionOutputId& outputId) const { + return outputId.hash(); + } +}; + class SpentOutputDescriptor { public: SpentOutputDescriptor(); @@ -74,6 +103,8 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn { SpentOutputDescriptor getSpentOutputDescriptor() const { return SpentOutputDescriptor(*this); } const Hash& getTransactionHash() const { return transactionHash; } + TransactionOutputId getTransactionOutputId() const { return TransactionOutputId {transactionHash, outputInTransaction}; } + void serialize(cryptonote::ISerializer& s, const std::string& name) { s(reinterpret_cast(type), "type"); s(amount, ""); @@ -126,6 +157,18 @@ struct SpentTransactionOutput : TransactionOutputInformationEx { } }; +struct TransferUnlockJob { + uint64_t unlockHeight; + TransactionOutputId transactionOutputId; + + Hash getTransactionHash() const { return transactionOutputId.transactionHash; } + + void serialize(cryptonote::ISerializer& s, const std::string& name) { + s(unlockHeight, "unlockHeight"); + s(transactionOutputId, "transactionOutputId"); + } +}; + enum class KeyImageState { Unconfirmed, Confirmed, @@ -145,12 +188,13 @@ class TransfersContainer : public ITransfersContainer { bool addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers, - std::vector&& messages); + std::vector&& messages, std::vector* unlockingTransfers = nullptr); bool deleteUnconfirmedTransaction(const Hash& transactionHash); bool markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); - std::vector detach(uint64_t height); - bool advanceHeight(uint64_t height); + void detach(uint64_t height, std::vector& deletedTransactions, std::vector& lockedTransfers); + //returns outputs that are being unlocked + std::vector advanceHeight(uint64_t height); // ITransfersContainer virtual size_t transfersCount() override; @@ -159,8 +203,11 @@ class TransfersContainer : public ITransfersContainer { virtual void getOutputs(std::vector& transfers, uint32_t flags) override; virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) override; virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags) override; + //only type flags are feasible for this function + virtual std::vector getTransactionInputs(const Hash& transactionHash, uint32_t flags) const override; virtual void getUnconfirmedTransactions(std::vector& transactions) override; virtual std::vector getSpentOutputs() override; + virtual bool getTransfer(const Hash& transactionHash, uint32_t outputInTransaction, TransactionOutputInformation& transfer, TransferState& transferState) const override; // IStreamSerializable virtual void save(std::ostream& os) override; @@ -170,6 +217,8 @@ class TransfersContainer : public ITransfersContainer { struct ContainingTransactionIndex { }; struct SpendingTransactionIndex { }; struct SpentOutputDescriptorIndex { }; + struct TransferUnlockHeightIndex { }; + struct TransactionOutputIdIndex { }; typedef boost::multi_index_container< TransactionInformation, @@ -196,6 +245,14 @@ class TransfersContainer : public ITransfersContainer { TransactionOutputInformationEx, const Hash&, &TransactionOutputInformationEx::getTransactionHash> + >, + boost::multi_index::hashed_unique < + boost::multi_index::tag, + boost::multi_index::const_mem_fun < + TransactionOutputInformationEx, + TransactionOutputId, + &TransactionOutputInformationEx::getTransactionOutputId>, + TransactionOutputIdHasher > > > UnconfirmedTransfersMultiIndex; @@ -217,6 +274,14 @@ class TransfersContainer : public ITransfersContainer { TransactionOutputInformationEx, const Hash&, &TransactionOutputInformationEx::getTransactionHash> + >, + boost::multi_index::hashed_unique < + boost::multi_index::tag, + boost::multi_index::const_mem_fun < + TransactionOutputInformationEx, + TransactionOutputId, + &TransactionOutputInformationEx::getTransactionOutputId>, + TransactionOutputIdHasher > > > AvailableTransfersMultiIndex; @@ -245,10 +310,33 @@ class TransfersContainer : public ITransfersContainer { SpentTransactionOutput, const Hash&, &SpentTransactionOutput::getSpendingTransactionHash> + >, + boost::multi_index::hashed_unique < + boost::multi_index::tag, + boost::multi_index::const_mem_fun < + TransactionOutputInformationEx, + TransactionOutputId, + &TransactionOutputInformationEx::getTransactionOutputId>, + TransactionOutputIdHasher > > > SpentTransfersMultiIndex; + typedef boost::multi_index_container< + TransferUnlockJob, + boost::multi_index::indexed_by< + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + BOOST_MULTI_INDEX_MEMBER(TransferUnlockJob, uint64_t, unlockHeight) + >, + boost::multi_index::hashed_unique< + boost::multi_index::tag, + BOOST_MULTI_INDEX_MEMBER(TransferUnlockJob, TransactionOutputId, transactionOutputId), + TransactionOutputIdHasher + > + > + > TransfersUnlockMultiIndex; + private: void addTransaction(const BlockInfo& block, const ITransactionReader& tx, std::vector&& messages); bool addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, @@ -259,20 +347,31 @@ class TransfersContainer : public ITransfersContainer { bool isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const; static bool isIncluded(const TransactionOutputInformationEx& output, uint32_t state, uint32_t flags); void updateTransfersVisibility(const KeyImage& keyImage); + void addUnlockJob(const TransactionOutputInformationEx& output); + void deleteUnlockJob(const TransactionOutputInformationEx& output); + std::vector getUnlockingTransfers(uint64_t prevHeight, uint64_t currentHeight); + void getLockingTransfers(uint64_t prevHeight, uint64_t currentHeight, + const std::vector& deletedTransactions, std::vector& lockingTransfers); + TransactionOutputInformation getAvailableOutput(const TransactionOutputId& transactionOutputId) const; void copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output); + void rebuildTransfersUnlockJobs(TransfersUnlockMultiIndex& transfersUnlockJobs, const AvailableTransfersMultiIndex& availableTransfers, + const SpentTransfersMultiIndex& spentTransfers); + std::vector doAdvanceHeight(uint64_t height); + private: TransactionMultiIndex m_transactions; UnconfirmedTransfersMultiIndex m_unconfirmedTransfers; AvailableTransfersMultiIndex m_availableTransfers; SpentTransfersMultiIndex m_spentTransfers; + TransfersUnlockMultiIndex m_transfersUnlockJobs; //std::unordered_map> m_keyImages; uint64_t m_currentHeight; // current height is needed to check if a transfer is unlocked size_t m_transactionSpendableAge; const cryptonote::Currency& m_currency; - std::mutex m_mutex; + mutable std::mutex m_mutex; }; } diff --git a/src/transfers/TransfersSubscription.cpp b/src/transfers/TransfersSubscription.cpp index e9ad4df10a..e32bbe38c7 100644 --- a/src/transfers/TransfersSubscription.cpp +++ b/src/transfers/TransfersSubscription.cpp @@ -17,21 +17,35 @@ SynchronizationStart TransfersSubscription::getSyncStart() { } void TransfersSubscription::onBlockchainDetach(uint64_t height) { - std::vector deletedTransactions = m_transfers.detach(height); + std::vector deletedTransactions; + std::vector lockedTransfers; + m_transfers.detach(height, deletedTransactions, lockedTransfers); + for (auto& hash : deletedTransactions) { m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, hash); } + + if (!lockedTransfers.empty()) { + m_observerManager.notify(&ITransfersObserver::onTransfersLocked, this, lockedTransfers); + } } void TransfersSubscription::onError(const std::error_code& ec, uint64_t height) { if (height != UNCONFIRMED_TRANSACTION_HEIGHT) { - m_transfers.detach(height); + onBlockchainDetach(height); } + m_observerManager.notify(&ITransfersObserver::onError, this, height, ec); } bool TransfersSubscription::advanceHeight(uint64_t height) { - return m_transfers.advanceHeight(height); + std::vector unlockedTransfers = m_transfers.advanceHeight(height); + + if (!unlockedTransfers.empty()) { + m_observerManager.notify(&ITransfersObserver::onTransfersUnlocked, this, unlockedTransfers); + } + + return true; } const AccountKeys& TransfersSubscription::getKeys() const { @@ -41,11 +55,16 @@ const AccountKeys& TransfersSubscription::getKeys() const { void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const std::vector& transfers, std::vector&& messages) { + std::vector unlockedTransfers; - bool added = m_transfers.addTransaction(blockInfo, tx, transfers, std::move(messages)); + bool added = m_transfers.addTransaction(blockInfo, tx, transfers, std::move(messages), &unlockedTransfers); if (added) { m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); } + + if (!unlockedTransfers.empty()) { + m_observerManager.notify(&ITransfersObserver::onTransfersUnlocked, this, unlockedTransfers); + } } AccountAddress TransfersSubscription::getAddress() { diff --git a/src/version.h.in b/src/version.h.in index 83122b794c..7dd66062ce 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.7-beta" -#define PROJECT_VERSION_BUILD_NO "1510" +#define PROJECT_VERSION "2.0.8-beta" +#define PROJECT_VERSION_BUILD_NO "1554" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index 424801ceed..a771210d2d 100644 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -11,6 +11,7 @@ #include "WalletUtils.h" #include "WalletSerializer.h" +#include #include #include @@ -84,6 +85,13 @@ class SaveWaiter : public CryptoNote::IWalletObserver { std::promise promise; std::future future; }; + +uint64_t calculateDepositsAmount(const std::vector& transfers, const cryptonote::Currency& currency) { + return std::accumulate(transfers.begin(), transfers.end(), static_cast(0), [¤cy] (uint64_t sum, const CryptoNote::TransactionOutputInformation& deposit) { + return sum + deposit.amount + currency.calculateInterest(deposit.amount, deposit.term); + }); +} + } //namespace namespace CryptoNote { @@ -107,6 +115,10 @@ Wallet::Wallet(const cryptonote::Currency& currency, INode& node) : m_currency(currency), m_node(node), m_isStopping(false), + m_lastNotifiedActualBalance(0), + m_lastNotifiedPendingBalance(0), + m_lastNotifiedActualDepositBalance(0), + m_lastNotifiedPendingDepositBalance(0), m_blockchainSync(node, currency.genesisBlockHash()), m_transfersSync(currency, m_blockchainSync, node), m_transferDetails(nullptr), @@ -417,16 +429,28 @@ uint64_t Wallet::actualBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); - return m_transferDetails->balance(ITransfersContainer::IncludeKeyUnlocked) - - m_transactionsCache.unconfrimedOutsAmount(); + return calculateActualBalance(); } uint64_t Wallet::pendingBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); - uint64_t change = m_transactionsCache.unconfrimedOutsAmount() - m_transactionsCache.unconfirmedTransactionsAmount(); - return m_transferDetails->balance(ITransfersContainer::IncludeKeyNotUnlocked) + change; + return calculatePendingBalance(); +} + +uint64_t Wallet::actualDepositBalance() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return calculateActualDepositBalance(); +} + +uint64_t Wallet::pendingDepositBalance() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return calculatePendingDepositBalance(); } size_t Wallet::getTransactionCount() { @@ -443,6 +467,13 @@ size_t Wallet::getTransferCount() { return m_transactionsCache.getTransferCount(); } +size_t Wallet::getDepositCount() { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.getDepositCount(); +} + TransactionId Wallet::findTransactionByTransferId(TransferId transferId) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); @@ -464,6 +495,13 @@ bool Wallet::getTransfer(TransferId transferId, Transfer& transfer) { return m_transactionsCache.getTransfer(transferId, transfer); } +bool Wallet::getDeposit(DepositId depositId, Deposit& deposit) { + std::unique_lock lock(m_cacheMutex); + throwIfNotInitialised(); + + return m_transactionsCache.getDeposit(depositId, deposit); +} + TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp, const std::vector& messages) { std::vector transfers; transfers.push_back(transfer); @@ -474,8 +512,8 @@ TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, co TransactionId Wallet::sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp, const std::vector& messages) { TransactionId txId = 0; - std::shared_ptr request; - std::deque > events; + std::unique_ptr request; + std::deque> events; throwIfNotInitialised(); { @@ -493,29 +531,91 @@ TransactionId Wallet::sendTransaction(const std::vector& transfers, ui return txId; } +TransactionId Wallet::deposit(uint32_t term, uint64_t amount, uint64_t fee, uint64_t mixIn) { + throwIfNotInitialised(); + + TransactionId txId = 0; + std::unique_ptr request; + std::deque> events; + + { + std::unique_lock lock(m_cacheMutex); + request = m_sender->makeDepositRequest(txId, events, term, amount, fee, mixIn); + + if (request != nullptr) { + pushBalanceUpdatedEvents(events); + } + } + + notifyClients(events); + + if (request) { + m_asyncContextCounter.addAsyncContext(); + request->perform(m_node, std::bind(&Wallet::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); + } + + return txId; +} + +TransactionId Wallet::withdrawDeposits(const std::vector& depositIds, uint64_t fee) { + throwIfNotInitialised(); + + TransactionId txId = 0; + std::unique_ptr request; + std::deque> events; + + { + std::unique_lock lock(m_cacheMutex); + request = m_sender->makeWithdrawDepositRequest(txId, events, depositIds, fee); + + if (request != nullptr) { + pushBalanceUpdatedEvents(events); + } + } + + notifyClients(events); + + if (request != nullptr) { + m_asyncContextCounter.addAsyncContext(); + request->perform(m_node, std::bind(&Wallet::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); + } + + return txId; +} + void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) { ContextCounterHolder counterHolder(m_asyncContextCounter); - std::deque > events; + std::deque > events; - boost::optional > nextRequest; + std::unique_ptr nextRequest; { std::unique_lock lock(m_cacheMutex); callback(events, nextRequest, ec); + + auto actualDepositBalanceUpdated = getActualDepositBalanceChangedEvent(); + if (actualDepositBalanceUpdated) { + events.push_back(std::move(actualDepositBalanceUpdated)); + } + + auto pendingDepositBalanceUpdated = getPendingDepositBalanceChangedEvent(); + if (pendingDepositBalanceUpdated) { + events.push_back(std::move(pendingDepositBalanceUpdated)); + } } notifyClients(events); if (nextRequest) { m_asyncContextCounter.addAsyncContext(); - (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + nextRequest->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } } void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { ContextCounterHolder counterHolder(m_asyncContextCounter); - std::deque > events; - boost::optional > nextRequest; + std::deque> events; + std::unique_ptr nextRequest; { std::unique_lock lock(m_cacheMutex); callback(events, nextRequest, ec); @@ -525,7 +625,7 @@ void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::erro if (nextRequest) { m_asyncContextCounter.addAsyncContext(); - (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + nextRequest->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } } @@ -552,30 +652,75 @@ void Wallet::synchronizationCompleted(std::error_code result) { } void Wallet::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { - std::shared_ptr event; + std::deque> events; TransactionInformation txInfo; int64_t txBalance; if (m_transferDetails->getTransactionInformation(transactionHash, txInfo, txBalance)) { std::unique_lock lock(m_cacheMutex); - event = m_transactionsCache.onTransactionUpdated(txInfo, txBalance); - } - if (event.get()) { - event->notify(m_observerManager); + auto newDepositOuts = m_transferDetails->getTransactionOutputs(transactionHash, ITransfersContainer::IncludeTypeDeposit | ITransfersContainer::IncludeStateAll); + auto spentDeposits = m_transferDetails->getTransactionInputs(transactionHash, ITransfersContainer::IncludeTypeDeposit); + + events = m_transactionsCache.onTransactionUpdated(txInfo, txBalance, newDepositOuts, spentDeposits, m_currency); + + auto actualDepositBalanceChangedEvent = getActualDepositBalanceChangedEvent(); + auto pendingDepositBalanceChangedEvent = getPendingDepositBalanceChangedEvent(); + + if (actualDepositBalanceChangedEvent) { + events.push_back(std::move(actualDepositBalanceChangedEvent)); + } + + if (pendingDepositBalanceChangedEvent) { + events.push_back(std::move(pendingDepositBalanceChangedEvent)); + } } + + notifyClients(events); } void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { - std::shared_ptr event; + std::deque> events; { std::unique_lock lock(m_cacheMutex); - event = m_transactionsCache.onTransactionDeleted(transactionHash); + events = m_transactionsCache.onTransactionDeleted(transactionHash); + + std::unique_ptr actualDepositBalanceUpdated = getActualDepositBalanceChangedEvent(); + if (actualDepositBalanceUpdated) { + events.push_back(std::move(actualDepositBalanceUpdated)); + } + + std::unique_ptr pendingDepositBalanceUpdated = getPendingDepositBalanceChangedEvent(); + if (pendingDepositBalanceUpdated) { + events.push_back(std::move(pendingDepositBalanceUpdated)); + } } - if (event.get()) { - event->notify(m_observerManager); + notifyClients(events); +} + +void Wallet::onTransfersUnlocked(ITransfersSubscription* object, const std::vector& unlockedTransfers) { + std::unique_lock lock(m_cacheMutex); + auto unlockedDeposits = m_transactionsCache.unlockDeposits(unlockedTransfers); + lock.unlock(); + + if (!unlockedDeposits.empty()) { + m_observerManager.notify(&IWalletObserver::depositsUpdated, unlockedDeposits); + + notifyIfDepositBalanceChanged(); + } +} + +void Wallet::onTransfersLocked(ITransfersSubscription* object, const std::vector& lockedTransfers) { + std::unique_lock lock(m_cacheMutex); + auto lockedDeposits = m_transactionsCache.lockDeposits(lockedTransfers); + lock.unlock(); + + if (!lockedDeposits.empty()) { + m_observerManager.notify(&IWalletObserver::depositsUpdated, lockedDeposits); + + notifyIfDepositBalanceChanged(); } } @@ -585,9 +730,9 @@ void Wallet::throwIfNotInitialised() { assert(m_transferDetails); } -void Wallet::notifyClients(std::deque >& events) { +void Wallet::notifyClients(std::deque>& events) { while (!events.empty()) { - std::shared_ptr event = events.front(); + std::unique_ptr& event = events.front(); event->notify(m_observerManager); events.pop_front(); } @@ -610,6 +755,71 @@ void Wallet::notifyIfBalanceChanged() { } +void Wallet::notifyIfDepositBalanceChanged() { + std::unique_ptr actualEvent = getActualDepositBalanceChangedEvent(); + std::unique_ptr pendingEvent = getPendingDepositBalanceChangedEvent(); + + if (actualEvent) { + actualEvent->notify(m_observerManager); + } + + if (pendingEvent) { + pendingEvent->notify(m_observerManager); + } +} + +std::unique_ptr Wallet::getActualDepositBalanceChangedEvent() { + auto actual = calculateActualDepositBalance(); + auto prevActual = m_lastNotifiedActualDepositBalance.exchange(actual); + + std::unique_ptr event; + + if (actual != prevActual) { + event = std::unique_ptr(new WalletActualDepositBalanceUpdatedEvent(actual)); + } + + return event; +} + +std::unique_ptr Wallet::getPendingDepositBalanceChangedEvent() { + auto pending = calculatePendingDepositBalance(); + auto prevPending = m_lastNotifiedPendingDepositBalance.exchange(pending); + + std::unique_ptr event; + + if (pending != prevPending) { + event = std::unique_ptr(new WalletPendingDepositBalanceUpdatedEvent(pending)); + } + + return event; +} + +std::unique_ptr Wallet::getActualBalanceChangedEvent() { + auto actual = calculateActualBalance(); + auto prevActual = m_lastNotifiedActualBalance.exchange(actual); + + std::unique_ptr event; + + if (actual != prevActual) { + event = std::unique_ptr(new WalletActualBalanceUpdatedEvent(actual)); + } + + return event; +} + +std::unique_ptr Wallet::getPendingBalanceChangedEvent() { + auto pending = calculatePendingBalance(); + auto prevPending = m_lastNotifiedPendingBalance.exchange(pending); + + std::unique_ptr event; + + if (pending != prevPending) { + event = std::unique_ptr(new WalletPendingBalanceUpdatedEvent(pending)); + } + + return event; +} + void Wallet::getAccountKeys(WalletAccountKeys& keys) { if (m_state == NOT_INITIALIZED) { throw std::system_error(make_error_code(cryptonote::error::NOT_INITIALIZED)); @@ -633,4 +843,55 @@ void Wallet::getAccountKeys(WalletAccountKeys& keys) { keys.viewSecretKey.begin()); } +uint64_t Wallet::calculateActualDepositBalance() { + std::vector transfers; + m_transferDetails->getOutputs(transfers, ITransfersContainer::IncludeTypeDeposit | ITransfersContainer::IncludeStateUnlocked); + + return calculateDepositsAmount(transfers, m_currency) - m_transactionsCache.countUnconfirmedSpentDepositsTotalAmount(); +} + +uint64_t Wallet::calculatePendingDepositBalance() { + std::vector transfers; + m_transferDetails->getOutputs(transfers, ITransfersContainer::IncludeTypeDeposit + | ITransfersContainer::IncludeStateLocked + | ITransfersContainer::IncludeStateSoftLocked); + + return calculateDepositsAmount(transfers, m_currency) + m_transactionsCache.countUnconfirmedCreatedDepositsSum(); +} + +uint64_t Wallet::calculateActualBalance() { + return m_transferDetails->balance(ITransfersContainer::IncludeKeyUnlocked) - + m_transactionsCache.unconfrimedOutsAmount(); +} + +uint64_t Wallet::calculatePendingBalance() { + uint64_t change = m_transactionsCache.unconfrimedOutsAmount() - m_transactionsCache.unconfirmedTransactionsAmount(); + uint64_t spentDeposits = m_transactionsCache.countUnconfirmedSpentDepositsProfit(); + uint64_t container = m_transferDetails->balance(ITransfersContainer::IncludeKeyNotUnlocked); + + return container + change + spentDeposits; +} + +void Wallet::pushBalanceUpdatedEvents(std::deque>& eventsQueue) { + auto actualDepositBalanceUpdated = getActualDepositBalanceChangedEvent(); + if (actualDepositBalanceUpdated != nullptr) { + eventsQueue.push_back(std::move(actualDepositBalanceUpdated)); + } + + auto pendingDepositBalanceUpdated = getPendingDepositBalanceChangedEvent(); + if (pendingDepositBalanceUpdated != nullptr) { + eventsQueue.push_back(std::move(pendingDepositBalanceUpdated)); + } + + auto actualBalanceUpdated = getActualBalanceChangedEvent(); + if (actualBalanceUpdated != nullptr) { + eventsQueue.push_back(std::move(actualBalanceUpdated)); + } + + auto pendingBalanceUpdated = getPendingBalanceChangedEvent(); + if (pendingBalanceUpdated != nullptr) { + eventsQueue.push_back(std::move(pendingBalanceUpdated)); + } +} + } //namespace CryptoNote diff --git a/src/wallet/Wallet.h b/src/wallet/Wallet.h index 63b53f82b3..e694f30a55 100644 --- a/src/wallet/Wallet.h +++ b/src/wallet/Wallet.h @@ -58,18 +58,24 @@ class Wallet : virtual uint64_t actualBalance(); virtual uint64_t pendingBalance(); + virtual uint64_t actualDepositBalance() override; + virtual uint64_t pendingDepositBalance() override; virtual size_t getTransactionCount(); virtual size_t getTransferCount(); + virtual size_t getDepositCount() override; virtual TransactionId findTransactionByTransferId(TransferId transferId); virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction); virtual bool getTransfer(TransferId transferId, Transfer& transfer); + virtual bool getDeposit(DepositId depositId, Deposit& deposit) override; virtual std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const override; virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()); virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()); + virtual TransactionId deposit(uint32_t term, uint64_t amount, uint64_t fee, uint64_t mixIn = 0) override; + virtual TransactionId withdrawDeposits(const std::vector& depositIds, uint64_t fee) override; virtual std::error_code cancelTransaction(size_t transactionId); virtual void getAccountKeys(WalletAccountKeys& keys); @@ -83,6 +89,8 @@ class Wallet : // ITransfersObserver virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override; virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) override; + virtual void onTransfersUnlocked(ITransfersSubscription* object, const std::vector& unlockedTransfers) override; + virtual void onTransfersLocked(ITransfersSubscription* object, const std::vector& lockedTransfers) override; void initSync(); void throwIfNotInitialised(); @@ -95,8 +103,23 @@ class Wallet : void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec); void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec); - void notifyClients(std::deque >& events); + void notifyClients(std::deque>& events); void notifyIfBalanceChanged(); + void notifyIfDepositBalanceChanged(); + + std::unique_ptr getActualDepositBalanceChangedEvent(); + std::unique_ptr getPendingDepositBalanceChangedEvent(); + + std::unique_ptr getActualBalanceChangedEvent(); + std::unique_ptr getPendingBalanceChangedEvent(); + + uint64_t calculateActualDepositBalance(); + uint64_t calculatePendingDepositBalance(); + + uint64_t calculateActualBalance(); + uint64_t calculatePendingBalance(); + + void pushBalanceUpdatedEvents(std::deque>& eventsQueue); enum WalletState { @@ -117,6 +140,9 @@ class Wallet : std::atomic m_lastNotifiedActualBalance; std::atomic m_lastNotifiedPendingBalance; + std::atomic m_lastNotifiedActualDepositBalance; + std::atomic m_lastNotifiedPendingDepositBalance; + BlockchainSynchronizer m_blockchainSync; TransfersSyncronizer m_transfersSync; ITransfersContainer* m_transferDetails; diff --git a/src/wallet/WalletErrors.h b/src/wallet/WalletErrors.h index afc896a556..187cf5e5de 100644 --- a/src/wallet/WalletErrors.h +++ b/src/wallet/WalletErrors.h @@ -27,7 +27,12 @@ enum WalletErrorCodes { TX_CANCEL_IMPOSSIBLE, TX_CANCELLED, OPERATION_CANCELLED, - TX_TRANSFER_IMPOSSIBLE + TX_TRANSFER_IMPOSSIBLE, + DEPOSIT_TERM_TOO_SMALL, + DEPOSIT_TERM_TOO_BIG, + DEPOSIT_AMOUNT_TOO_SMALL, + DEPOSIT_DOESNOT_EXIST, + DEPOSIT_LOCKED }; // custom category: @@ -59,6 +64,11 @@ class WalletErrorCategory : public std::error_category { case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later"; case OPERATION_CANCELLED: return "The operation you've requested has been cancelled"; case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible"; + case DEPOSIT_TERM_TOO_SMALL: return "Deposit term is too small"; + case DEPOSIT_TERM_TOO_BIG: return "Deposit term is too big"; + case DEPOSIT_AMOUNT_TOO_SMALL: return "Deposit amount is too small"; + case DEPOSIT_DOESNOT_EXIST: return "Deposit doesn't exist"; + case DEPOSIT_LOCKED: return "Deposit is locked"; default: return "Unknown error"; } } diff --git a/src/wallet/WalletEvent.h b/src/wallet/WalletEvent.h index 1c47f5f6fe..d3b634d7cc 100644 --- a/src/wallet/WalletEvent.h +++ b/src/wallet/WalletEvent.h @@ -64,6 +64,19 @@ class WalletExternalTransactionCreatedEvent : public WalletEvent TransactionId m_id; }; +class WalletDepositsUpdatedEvent : public WalletEvent { +public: + WalletDepositsUpdatedEvent(std::vector&& depositIds) : updatedDeposits(depositIds) {} + + virtual ~WalletDepositsUpdatedEvent() {} + + virtual void notify(tools::ObserverManager& observer) override { + observer.notify(&IWalletObserver::depositsUpdated, updatedDeposits); + } +private: + std::vector updatedDeposits; +}; + class WalletSynchronizationProgressUpdatedEvent : public WalletEvent { public: @@ -121,4 +134,32 @@ class WalletPendingBalanceUpdatedEvent : public WalletEvent uint64_t m_balance; }; +class WalletActualDepositBalanceUpdatedEvent : public WalletEvent +{ +public: + WalletActualDepositBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {} + virtual ~WalletActualDepositBalanceUpdatedEvent() {} + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::actualDepositBalanceUpdated, m_balance); + } +private: + uint64_t m_balance; +}; + +class WalletPendingDepositBalanceUpdatedEvent : public WalletEvent +{ +public: + WalletPendingDepositBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {} + virtual ~WalletPendingDepositBalanceUpdatedEvent() {} + + virtual void notify(tools::ObserverManager& observer) + { + observer.notify(&IWalletObserver::pendingDepositBalanceUpdated, m_balance); + } +private: + uint64_t m_balance; +}; + } /* namespace CryptoNote */ diff --git a/src/wallet/WalletRequest.h b/src/wallet/WalletRequest.h index e00afb7839..71a8563b89 100644 --- a/src/wallet/WalletRequest.h +++ b/src/wallet/WalletRequest.h @@ -21,7 +21,7 @@ namespace CryptoNote { class WalletRequest { public: - typedef std::function >& events, boost::optional >& nextRequest, std::error_code ec)> Callback; + typedef std::function>& events, std::unique_ptr& nextRequest, std::error_code ec)> Callback; virtual ~WalletRequest() {}; @@ -64,4 +64,20 @@ class WalletRelayTransactionRequest: public WalletRequest Callback m_cb; }; +class WalletRelayDepositTransactionRequest final: public WalletRequest +{ +public: + WalletRelayDepositTransactionRequest(const cryptonote::Transaction& tx, Callback cb) : m_tx(tx), m_cb(cb) {} + virtual ~WalletRelayDepositTransactionRequest() {} + + virtual void perform(INode& node, std::function cb) + { + node.relayTransaction(m_tx, std::bind(cb, m_cb, std::placeholders::_1)); + } + +private: + cryptonote::Transaction m_tx; + Callback m_cb; +}; + } //namespace CryptoNote diff --git a/src/wallet/WalletSendTransactionContext.h b/src/wallet/WalletSendTransactionContext.h index d7a0faeada..be19df0625 100644 --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/wallet/WalletSendTransactionContext.h @@ -29,10 +29,11 @@ struct SendTransactionContext TransactionId transactionId; std::vector outs; uint64_t foundMoney; - std::list selectedTransfers; + std::vector selectedTransfers; TxDustPolicy dustPolicy; uint64_t mixIn; std::vector messages; + uint32_t depositTerm; }; } //namespace CryptoNote diff --git a/src/wallet/WalletSerialization.cpp b/src/wallet/WalletSerialization.cpp index 219dbca1cd..0f10b52e42 100644 --- a/src/wallet/WalletSerialization.cpp +++ b/src/wallet/WalletSerialization.cpp @@ -28,6 +28,19 @@ void serialize(UnconfirmedTransferDetails& utd, const std::string& name, crypton serializer.endObject(); } +void serialize(UnconfirmedSpentDepositDetails& details, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + + uint64_t txId = details.transactionId; + serializer(txId, "spendingTransactionId"); + details.transactionId = txId; + + serializer(details.depositsSum, "depositsSum"); + serializer(details.fee, "fee"); + + serializer.endObject(); +} + void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISerializer& serializer) { serializer.beginObject(name); @@ -82,11 +95,13 @@ void serialize(Deposit& deposit, const std::string& name, cryptonote::ISerialize uint64_t spendingTxIx = static_cast(deposit.spendingTransactionId); serializer(spendingTxIx, "spending_transaction_id"); - deposit.creatingTransactionId = static_cast(spendingTxIx); + deposit.spendingTransactionId = static_cast(spendingTxIx); serializer(deposit.term, "term"); serializer(deposit.amount, "amount"); serializer(deposit.interest, "interest"); + serializer(deposit.locked, "locked"); + serializer.endObject(); } diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h index c54330e477..585dcccc1c 100644 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -22,8 +22,10 @@ struct TransactionInfo; struct Transfer; struct DepositInfo; struct Deposit; +struct UnconfirmedSpentDepositDetails; void serialize(UnconfirmedTransferDetails& utd, const std::string& name, cryptonote::ISerializer& serializer); +void serialize(UnconfirmedSpentDepositDetails& details, const std::string& name, cryptonote::ISerializer& serializer); void serialize(TransactionInfo& txi, const std::string& name, cryptonote::ISerializer& serializer); void serialize(Transfer& tr, const std::string& name, cryptonote::ISerializer& serializer); void serialize(DepositInfo& depositInfo, const std::string& name, cryptonote::ISerializer& serializer); diff --git a/src/wallet/WalletSerializer.cpp b/src/wallet/WalletSerializer.cpp index 11d287197d..3fc9631c56 100644 --- a/src/wallet/WalletSerializer.cpp +++ b/src/wallet/WalletSerializer.cpp @@ -17,6 +17,8 @@ namespace { +const uint32_t WALLET_SERIALIZATION_VERSION = 2; + bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { crypto::public_key pub; bool r = crypto::secret_key_to_public_key(sec, pub); @@ -35,7 +37,7 @@ namespace CryptoNote { WalletSerializer::WalletSerializer(cryptonote::account_base& account, WalletUserTransactionsCache& transactionsCache) : account(account), transactionsCache(transactionsCache), - walletSerializationVersion(1) + walletSerializationVersion(WALLET_SERIALIZATION_VERSION) { } @@ -131,7 +133,11 @@ void WalletSerializer::deserialize(std::istream& stream, const std::string& pass serializer(detailsSaved, "has_details"); if (detailsSaved) { - serializer(transactionsCache, "details"); + if (version == 1) { + transactionsCache.deserializeLegacyV1(serializer, "details"); + } else { + serializer(transactionsCache, "details"); + } } serializer.binary(cache, "cache"); diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 587911a5dd..805c64633e 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -13,6 +13,7 @@ #include "WalletUtils.h" #include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/TransactionApi.h" #include @@ -20,6 +21,35 @@ namespace { using namespace CryptoNote; +PublicKey convertPublicKey(const crypto::public_key& publicKey) { + PublicKey result = *reinterpret_cast(&publicKey); + + return result; +} + +SecretKey convertSecretKey(const crypto::secret_key& secretKey) { + SecretKey result = *reinterpret_cast(&secretKey); + + return result; +} + +AccountAddress convertAccountAddress(const cryptonote::AccountPublicAddress& address) { + AccountAddress result; + result.spendPublicKey = convertPublicKey(address.m_spendPublicKey); + result.viewPublicKey = convertPublicKey(address.m_viewPublicKey); + + return result; +} + +AccountKeys convertAccountKeys(const cryptonote::account_keys& keys) { + AccountKeys result; + result.address = convertAccountAddress(keys.m_account_address); + result.spendSecretKey = convertSecretKey(keys.m_spend_secret_key); + result.viewSecretKey = convertSecretKey(keys.m_view_secret_key); + + return result; +} + uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { uint64_t needed_money = fee; for (auto& transfer: transfers) { @@ -33,6 +63,12 @@ uint64_t countNeededMoney(uint64_t fee, const std::vector& return needed_money; } +uint64_t getSumWithOverflowCheck(uint64_t amount, uint64_t fee) { + CryptoNote::throwIf(std::numeric_limits::max() - amount < fee, cryptonote::error::SUM_OVERFLOW); + + return amount + fee; +} + void createChangeDestinations(const cryptonote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, cryptonote::tx_destination_entry& changeDts) { if (neededMoney < foundMoney) { changeDts.addr = address; @@ -56,9 +92,83 @@ void fillTransactionHash(const cryptonote::Transaction& tx, CryptoNote::Transact memcpy(hash.data(), reinterpret_cast(&h), hash.size()); } -std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { +std::unique_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { transactionCache.updateTransactionSendingState(transactionId, ec); - return std::make_shared(transactionId, ec); + return std::unique_ptr(new WalletSendTransactionCompletedEvent(transactionId, ec)); +} + +std::vector convertSources(std::vector&& sources) { + std::vector inputs; + inputs.reserve(sources.size()); + + for (cryptonote::tx_source_entry& source: sources) { + TransactionTypes::InputKeyInfo input; + input.amount = source.amount; + + input.outputs.reserve(source.outputs.size()); + for (const cryptonote::tx_source_entry::output_entry& sourceOutput: source.outputs) { + TransactionTypes::GlobalOutput output; + output.outputIndex = sourceOutput.first; + output.targetKey = convertPublicKey(sourceOutput.second); + + input.outputs.emplace_back(std::move(output)); + } + + input.realOutput.transactionPublicKey = convertPublicKey(source.real_out_tx_key); + input.realOutput.outputInTransaction = source.real_output_in_tx_index; + input.realOutput.transactionIndex = source.real_output; + + inputs.emplace_back(std::move(input)); + } + + return inputs; +} + +std::vector splitAmount(uint64_t amount, uint64_t dustThreshold) { + std::vector amounts; + + cryptonote::decompose_amount_into_digits(amount, dustThreshold, + [&](uint64_t chunk) { amounts.push_back(chunk); }, + [&](uint64_t dust) { amounts.push_back(dust); } ); + + return amounts; +} + +cryptonote::Transaction convertTransaction(const ITransaction& transaction) { + Blob serializedTransaction = transaction.getTransactionData(); + + cryptonote::Transaction result; + if (!parse_and_validate_tx_from_blob(std::string(serializedTransaction.begin(), serializedTransaction.end()), result)) { + throw std::system_error(make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR), "Cannot convert transaction"); + } + + return result; +} + +uint64_t checkDepositsAndCalculateAmount(const std::vector& depositIds, const WalletUserTransactionsCache& transactionsCache) { + uint64_t amount = 0; + + for (const auto& id: depositIds) { + Deposit deposit; + throwIf(!transactionsCache.getDeposit(id, deposit), cryptonote::error::DEPOSIT_DOESNOT_EXIST); + throwIf(deposit.locked, cryptonote::error::DEPOSIT_LOCKED); + + amount += deposit.amount + deposit.interest; + } + + return amount; +} + +void countDepositsTotalSumAndInterestSum(const std::vector& depositIds, WalletUserTransactionsCache& depositsCache, + uint64_t& totalSum, uint64_t& interestsSum) { + totalSum = 0; + interestsSum = 0; + + for (auto id: depositIds) { + Deposit& deposit = depositsCache.getDeposit(id); + totalSum += deposit.amount + deposit.interest; + interestsSum += deposit.interest; + } } } //namespace @@ -90,7 +200,7 @@ void WalletTransactionSender::validateTransfersAddresses(const std::vector WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, +std::unique_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque>& events, const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp, const std::vector& messages) { using namespace cryptonote; @@ -100,6 +210,7 @@ std::shared_ptr WalletTransactionSender::makeSendRequest(Transact uint64_t neededMoney = countNeededMoney(fee, transfers); std::shared_ptr context = std::make_shared(); + context->dustPolicy.dustThreshold = m_currency.defaultDustThreshold(); context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); @@ -118,15 +229,59 @@ std::shared_ptr WalletTransactionSender::makeSendRequest(Transact context->messages.push_back( { message.message, true, address } ); } - if(context->mixIn) { - std::shared_ptr request = makeGetRandomOutsRequest(context); - return request; + if(context->mixIn != 0) { + return makeGetRandomOutsRequest(std::move(context), false); } - return doSendTransaction(context, events); + return doSendTransaction(std::move(context), events); } -std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest(std::shared_ptr context) { +std::unique_ptr WalletTransactionSender::makeDepositRequest(TransactionId& transactionId, std::deque>& events, + uint64_t term, uint64_t amount, uint64_t fee, uint64_t mixIn) { + + throwIf(term < m_currency.depositMinTerm(), cryptonote::error::DEPOSIT_TERM_TOO_SMALL); + throwIf(term > m_currency.depositMaxTerm(), cryptonote::error::DEPOSIT_TERM_TOO_BIG); + throwIf(amount < m_currency.depositMinAmount(), cryptonote::error::DEPOSIT_AMOUNT_TOO_SMALL); + + uint64_t neededMoney = getSumWithOverflowCheck(amount, fee); + std::shared_ptr context = std::make_shared(); + context->dustPolicy.dustThreshold = m_currency.defaultDustThreshold(); + + context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); + + throwIf(context->foundMoney < neededMoney, cryptonote::error::WRONG_AMOUNT); + + transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, std::string(), {}, 0, {}); + context->transactionId = transactionId; + context->mixIn = mixIn; + context->depositTerm = term; + + if(context->mixIn != 0) { + return makeGetRandomOutsRequest(std::move(context), true); + } + + return doSendMultisigTransaction(std::move(context), events); +} + +std::unique_ptr WalletTransactionSender::makeWithdrawDepositRequest(TransactionId& transactionId, std::deque>& events, + const std::vector& depositIds, uint64_t fee) { + + std::shared_ptr context = std::make_shared(); + context->dustPolicy.dustThreshold = m_currency.defaultDustThreshold(); + + context->foundMoney = selectDepositTransfers(depositIds, context->selectedTransfers); + throwIf(context->foundMoney < fee, cryptonote::error::WRONG_AMOUNT); + + transactionId = m_transactionsCache.addNewTransaction(context->foundMoney, fee, std::string(), {}, 0, {}); + context->transactionId = transactionId; + context->mixIn = 0; + + setSpendingTransactionToDeposits(transactionId, depositIds); + + return doSendDepositWithdrawTransaction(std::move(context), events, depositIds); +} + +std::unique_ptr WalletTransactionSender::makeGetRandomOutsRequest(std::shared_ptr&& context, bool isMultisigTransaction) { uint64_t outsCount = context->mixIn + 1;// add one to make possible (if need) to skip real output key std::vector amounts; @@ -134,13 +289,14 @@ std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest amounts.push_back(td.amount); } - return std::make_shared(amounts, outsCount, context, std::bind(&WalletTransactionSender::sendTransactionRandomOutsByAmount, - this, context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + return std::unique_ptr(new WalletGetRandomOutsByAmountsRequest(amounts, outsCount, context, + std::bind(&WalletTransactionSender::sendTransactionRandomOutsByAmount, this, isMultisigTransaction, context, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); } -void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec) { - +void WalletTransactionSender::sendTransactionRandomOutsByAmount(bool isMultisigTransaction, std::shared_ptr context, std::deque>& events, + std::unique_ptr& nextRequest, std::error_code ec) { + if (m_isStoping) { ec = make_error_code(cryptonote::error::TX_CANCELLED); } @@ -150,23 +306,33 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< return; } - auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), - [&] (cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); - - if (scanty_it != context->outs.end()) { + if (!checkIfEnoughMixins(context->outs, context->mixIn)) { events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::MIXIN_COUNT_TOO_BIG))); return; } - std::shared_ptr req = doSendTransaction(context, events); - if (req) - nextRequest = req; + if (isMultisigTransaction) { + nextRequest = doSendMultisigTransaction(std::move(context), events); + } else { + nextRequest = doSendTransaction(std::move(context), events); + } +} + +bool WalletTransactionSender::checkIfEnoughMixins(const std::vector& outs, uint64_t mixIn) { + auto scanty_it = std::find_if(outs.begin(), outs.end(), + [&] (const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < mixIn;}); + + if (scanty_it != outs.end()) { + return false; + } + + return true; } -std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque >& events) { +std::unique_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr&& context, std::deque>& events) { if (m_isStoping) { events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); - return std::shared_ptr(); + return std::unique_ptr(); } try @@ -174,7 +340,7 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s TransactionInfo& transaction = m_transactionsCache.getTransaction(context->transactionId); std::vector sources; - prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); + prepareKeyInputs(context->selectedTransfers, context->outs, sources, context->mixIn); cryptonote::tx_destination_entry changeDts = AUTO_VAL_INIT(changeDts); uint64_t totalAmount = -transaction.totalAmount; @@ -192,8 +358,8 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s notifyBalanceChanged(events); - return std::make_shared(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context, - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + return std::unique_ptr(new WalletRelayTransactionRequest(tx, std::bind(&WalletTransactionSender::relayTransactionCallback, this, context, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); } catch(std::system_error& ec) { events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); @@ -202,11 +368,142 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); } - return std::shared_ptr(); + return std::unique_ptr(); +} + +std::unique_ptr WalletTransactionSender::doSendMultisigTransaction(std::shared_ptr&& context, std::deque>& events) { + if (m_isStoping) { + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); + return std::unique_ptr(); + } + + try { + //TODO decompose this method + TransactionInfo& transactionInfo = m_transactionsCache.getTransaction(context->transactionId); + + std::unique_ptr transaction = createTransaction(); + + uint64_t totalAmount = std::abs(transactionInfo.totalAmount); + std::vector inputs = prepareKeyInputs(context->selectedTransfers, context->outs, context->mixIn); + std::vector decomposedChange = splitAmount(context->foundMoney - totalAmount, context->dustPolicy.dustThreshold); + + AccountAddress myAddress = convertAccountAddress(m_keys.m_account_address); + auto depositIndex = transaction->addOutput(std::abs(transactionInfo.totalAmount) - transactionInfo.fee, + { myAddress }, + 1, + context->depositTerm); + + for (uint64_t changeOut: decomposedChange) { + transaction->addOutput(changeOut, myAddress); + } + + transaction->setUnlockTime(transactionInfo.unlockTime); + + std::vector ephKeys; + ephKeys.reserve(inputs.size()); + + for (const auto& input: inputs) { + KeyPair ephKey; + transaction->addInput(convertAccountKeys(m_keys), input, ephKey); + ephKeys.push_back(std::move(ephKey)); + } + + for (size_t i = 0; i < inputs.size(); ++i) { + transaction->signInputKey(i, inputs[i], ephKeys[i]); + } + + transactionInfo.hash = transaction->getTransactionHash(); + + Deposit deposit; + deposit.amount = std::abs(transactionInfo.totalAmount) - transactionInfo.fee; + deposit.term = context->depositTerm; + deposit.creatingTransactionId = context->transactionId; + deposit.spendingTransactionId = INVALID_TRANSACTION_ID; + deposit.interest = m_currency.calculateInterest(deposit.amount, deposit.term); + deposit.locked = true; + DepositId depositId = m_transactionsCache.insertDeposit(deposit, depositIndex, transaction->getTransactionHash()); + transactionInfo.firstDepositId = depositId; + transactionInfo.depositCount = 1; + + cryptonote::Transaction lowlevelTransaction = convertTransaction(*transaction); + m_transactionsCache.updateTransaction(context->transactionId, lowlevelTransaction, totalAmount, context->selectedTransfers); + m_transactionsCache.addCreatedDeposit(depositId, deposit.amount + deposit.interest); + + notifyBalanceChanged(events); + + std::vector deposits {depositId}; + + return std::unique_ptr(new WalletRelayDepositTransactionRequest(lowlevelTransaction, std::bind(&WalletTransactionSender::relayDepositTransactionCallback, this, context, + deposits, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); + + } catch(std::system_error& ec) { + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); + } catch(std::exception&) { + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + } + + return std::unique_ptr(); +} + +std::unique_ptr WalletTransactionSender::doSendDepositWithdrawTransaction(std::shared_ptr&& context, + std::deque>& events, const std::vector& depositIds) { + if (m_isStoping) { + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::TX_CANCELLED))); + return std::unique_ptr(); + } + + try { + TransactionInfo& transactionInfo = m_transactionsCache.getTransaction(context->transactionId); + + std::unique_ptr transaction = createTransaction(); + std::vector inputs = prepareMultisignatureInputs(context->selectedTransfers); + + std::vector outputAmounts = splitAmount(context->foundMoney - transactionInfo.fee, context->dustPolicy.dustThreshold); + + for (const auto& input: inputs) { + transaction->addInput(input); + } + + AccountAddress myAddress = convertAccountAddress(m_keys.m_account_address); + for (auto amount: outputAmounts) { + transaction->addOutput(amount, myAddress); + } + + transaction->setUnlockTime(transactionInfo.unlockTime); + + assert(inputs.size() == context->selectedTransfers.size()); + AccountKeys myKeys = convertAccountKeys(m_keys); + for (size_t i = 0; i < inputs.size(); ++i) { + transaction->signInputMultisignature(i, context->selectedTransfers[i].transactionPublicKey, context->selectedTransfers[i].outputInTransaction, myKeys); + } + + transactionInfo.hash = transaction->getTransactionHash(); + + cryptonote::Transaction lowlevelTransaction = convertTransaction(*transaction); + + uint64_t interestsSum; + uint64_t totalSum; + countDepositsTotalSumAndInterestSum(depositIds, m_transactionsCache, totalSum, interestsSum); + + UnconfirmedSpentDepositDetails unconfirmed; + unconfirmed.depositsSum = totalSum; + unconfirmed.fee = transactionInfo.fee; + unconfirmed.transactionId = context->transactionId; + m_transactionsCache.addDepositSpendingTransaction(transaction->getTransactionHash(), unconfirmed); + + return std::unique_ptr(new WalletRelayDepositTransactionRequest(lowlevelTransaction, + std::bind(&WalletTransactionSender::relayDepositTransactionCallback, this, context, depositIds, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))); + } catch(std::system_error& ec) { + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); + } catch(std::exception&) { + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(cryptonote::error::INTERNAL_WALLET_ERROR))); + } + + return std::unique_ptr(); } -void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec) { +void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque>& events, + std::unique_ptr& nextRequest, std::error_code ec) { if (m_isStoping) { return; } @@ -214,6 +511,16 @@ void WalletTransactionSender::relayTransactionCallback(std::shared_ptrtransactionId, ec)); } +void WalletTransactionSender::relayDepositTransactionCallback(std::shared_ptr context, std::vector deposits, + std::deque>& events, std::unique_ptr& nextRequest, std::error_code ec) { + + if (m_isStoping) { + return; + } + + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec)); + events.push_back(std::unique_ptr(new WalletDepositsUpdatedEvent(std::move(deposits)))); +} void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts, const TxDustPolicy& dustPolicy, std::vector& splittedDests) { @@ -253,14 +560,16 @@ void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, siz } -void WalletTransactionSender::prepareInputs( - const std::list& selectedTransfers, +void WalletTransactionSender::prepareKeyInputs( + const std::vector& selectedTransfers, std::vector& outs, std::vector& sources, uint64_t mixIn) { size_t i = 0; for (const auto& td: selectedTransfers) { + assert(td.type == TransactionTypes::OutputType::Key); + sources.resize(sources.size()+1); cryptonote::tx_source_entry& src = sources.back(); @@ -297,15 +606,45 @@ void WalletTransactionSender::prepareInputs( } } -void WalletTransactionSender::notifyBalanceChanged(std::deque >& events) { +std::vector WalletTransactionSender::prepareKeyInputs( + const std::vector& selectedTransfers, + std::vector& outs, uint64_t mixIn) { + + std::vector sources; + prepareKeyInputs(selectedTransfers, outs, sources, mixIn); + + return convertSources(std::move(sources)); +} + +std::vector WalletTransactionSender::prepareMultisignatureInputs(const std::vector& selectedTransfers) { + std::vector inputs; + inputs.reserve(selectedTransfers.size()); + + for (const auto& output: selectedTransfers) { + assert(output.type == TransactionTypes::OutputType::Multisignature); + assert(output.requiredSignatures == 1); //Other types are currently unsupported + + TransactionTypes::InputMultisignature input; + input.amount = output.amount; + input.signatures = output.requiredSignatures; + input.outputIndex = output.globalOutputIndex; + input.term = output.term; + + inputs.emplace_back(std::move(input)); + } + + return inputs; +} + +void WalletTransactionSender::notifyBalanceChanged(std::deque>& events) { uint64_t unconfirmedOutsAmount = m_transactionsCache.unconfrimedOutsAmount(); uint64_t change = unconfirmedOutsAmount - m_transactionsCache.unconfirmedTransactionsAmount(); uint64_t actualBalance = m_transferDetails.balance(ITransfersContainer::IncludeKeyUnlocked) - unconfirmedOutsAmount; uint64_t pendingBalance = m_transferDetails.balance(ITransfersContainer::IncludeKeyNotUnlocked) + change; - events.push_back(std::make_shared(actualBalance)); - events.push_back(std::make_shared(pendingBalance)); + events.push_back(std::unique_ptr(new WalletActualBalanceUpdatedEvent(actualBalance))); + events.push_back(std::unique_ptr(new WalletPendingBalanceUpdatedEvent(pendingBalance))); } namespace { @@ -328,9 +667,7 @@ T popRandomValue(URNG& randomGenerator, std::vector& vec) { } - -uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers) { - +uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::vector& selectedTransfers) { std::vector unusedTransfers; std::vector unusedDust; @@ -365,8 +702,40 @@ uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bo } return foundMoney; +} + +uint64_t WalletTransactionSender::selectDepositTransfers(const std::vector& depositIds, std::vector& selectedTransfers) { + uint64_t foundMoney = 0; + for (auto id: depositIds) { + Hash transactionHash; + uint32_t outputInTransaction; + throwIf(m_transactionsCache.getDepositInTransactionInfo(id, transactionHash, outputInTransaction) == false, cryptonote::error::DEPOSIT_DOESNOT_EXIST); + + { + TransactionOutputInformation transfer; + ITransfersContainer::TransferState state; + throwIf(m_transferDetails.getTransfer(transactionHash, outputInTransaction, transfer, state) == false, cryptonote::error::DEPOSIT_DOESNOT_EXIST); + throwIf(state != ITransfersContainer::TransferState::TransferAvailable, cryptonote::error::DEPOSIT_LOCKED); + + selectedTransfers.push_back(std::move(transfer)); + } + + Deposit deposit; + bool r = m_transactionsCache.getDeposit(id, deposit); + assert(r); + + foundMoney += deposit.amount + deposit.interest; + } + + return foundMoney; } +void WalletTransactionSender::setSpendingTransactionToDeposits(TransactionId transactionId, const std::vector& depositIds) { + for (auto id: depositIds) { + Deposit& deposit = m_transactionsCache.getDeposit(id); + deposit.spendingTransactionId = transactionId; + } +} } /* namespace CryptoNote */ diff --git a/src/wallet/WalletTransactionSender.h b/src/wallet/WalletTransactionSender.h index cee8cfcb0d..e06e82820e 100644 --- a/src/wallet/WalletTransactionSender.h +++ b/src/wallet/WalletTransactionSender.h @@ -26,28 +26,51 @@ class WalletTransactionSender void init(cryptonote::account_keys keys, ITransfersContainer& transfersContainer); void stop(); - std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, + std::unique_ptr makeSendRequest(TransactionId& transactionId, std::deque>& events, const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0, const std::vector& messages = std::vector()); + std::unique_ptr makeDepositRequest(TransactionId& transactionId, std::deque>& events, uint64_t term, + uint64_t amount, uint64_t fee, uint64_t mixIn = 0); + + std::unique_ptr makeWithdrawDepositRequest(TransactionId& transactionId, std::deque>& events, + const std::vector& depositIds, uint64_t fee); + private: - std::shared_ptr makeGetRandomOutsRequest(std::shared_ptr context); - std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque >& events); - void prepareInputs(const std::list& selectedTransfers, std::vector& outs, - std::vector& sources, uint64_t mixIn); + std::unique_ptr makeGetRandomOutsRequest(std::shared_ptr&& context, bool isMultisigTransaction); + + std::unique_ptr doSendTransaction(std::shared_ptr&& context, std::deque>& events); + std::unique_ptr doSendMultisigTransaction(std::shared_ptr&& context, std::deque>& events); + std::unique_ptr doSendDepositWithdrawTransaction(std::shared_ptr&& context, + std::deque>& events, const std::vector& depositIds); + + void sendTransactionRandomOutsByAmount(bool isMultisigTransaction, std::shared_ptr context, std::deque>& events, + std::unique_ptr& nextRequest, std::error_code ec); + + void prepareKeyInputs(const std::vector& selectedTransfers, + std::vector& outs, + std::vector& sources, uint64_t mixIn); + std::vector prepareKeyInputs(const std::vector& selectedTransfers, + std::vector& outs, + uint64_t mixIn); + std::vector prepareMultisignatureInputs(const std::vector& selectedTransfers); void splitDestinations(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& changeDts, const TxDustPolicy& dustPolicy, std::vector& splittedDests); void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, std::vector& splitted_dsts, uint64_t& dust); - void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec); - void relayTransactionCallback(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec); - void notifyBalanceChanged(std::deque >& events); + bool checkIfEnoughMixins(const std::vector& outs, uint64_t mixIn); + void relayTransactionCallback(std::shared_ptr context, std::deque>& events, + std::unique_ptr& nextRequest, std::error_code ec); + void relayDepositTransactionCallback(std::shared_ptr context, std::vector deposits, + std::deque>& events, std::unique_ptr& nextRequest, std::error_code ec); + void notifyBalanceChanged(std::deque>& events); void validateTransfersAddresses(const std::vector& transfers); bool validateDestinationAddress(const std::string& address); - uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); + uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::vector& selectedTransfers); + uint64_t selectDepositTransfers(const std::vector& depositIds, std::vector& selectedTransfers); + + void setSpendingTransactionToDeposits(TransactionId transactionId, const std::vector& depositIds); const cryptonote::Currency& m_currency; cryptonote::account_keys m_keys; diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index b2554f03ce..6ba91edfb5 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -4,6 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "WalletUnconfirmedTransactions.h" + +#include + #include "WalletSerialization.h" #include "cryptonote_core/cryptonote_format_utils.h" @@ -12,11 +15,23 @@ namespace CryptoNote { -inline TransactionOutputId getOutputId(const TransactionOutputInformation& out) { +inline std::pair getOutputId(const TransactionOutputInformation& out) { return std::make_pair(out.transactionPublicKey, out.outputInTransaction); } void WalletUnconfirmedTransactions::serialize(cryptonote::ISerializer& s, const std::string& name) { + s.beginObject(name); + s(m_unconfirmedTxs, "transactions"); + s(m_createdDeposits, "unconfirmedCreatedDeposits"); + s(m_spentDeposits, "unconfirmedSpentDeposits"); + s.endObject(); + + if (s.type() == cryptonote::ISerializer::INPUT) { + collectUsedOutputs(); + } +} + +void WalletUnconfirmedTransactions::deserializeV1(cryptonote::ISerializer& s, const std::string& name) { s.beginObject(name); s(m_unconfirmedTxs, "transactions"); s.endObject(); @@ -27,6 +42,10 @@ void WalletUnconfirmedTransactions::serialize(cryptonote::ISerializer& s, const } bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& hash, TransactionId& id) { + return findUnconfirmedTransactionId(hash, id) || findUnconfirmedDepositSpendingTransactionId(hash, id); +} + +bool WalletUnconfirmedTransactions::findUnconfirmedTransactionId(const TransactionHash& hash, TransactionId& id) { auto it = m_unconfirmedTxs.find(hash); if (it == m_unconfirmedTxs.end()) { return false; @@ -36,20 +55,46 @@ bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& has return true; } +bool WalletUnconfirmedTransactions::findUnconfirmedDepositSpendingTransactionId(const TransactionHash& hash, TransactionId& id) { + auto it = m_spentDeposits.find(hash); + if (it == m_spentDeposits.end()) { + return false; + } + + id = it->second.transactionId; + return true; +} + void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { + eraseUnconfirmedTransaction(hash) || eraseDepositSpendingTransaction(hash); +} + +bool WalletUnconfirmedTransactions::eraseUnconfirmedTransaction(const TransactionHash& hash) { auto it = m_unconfirmedTxs.find(hash); if (it == m_unconfirmedTxs.end()) { - return; + return false; } for (const auto& o : it->second.usedOutputs) { m_usedOutputs.erase(o); } + m_unconfirmedTxs.erase(it); + return true; +} + +bool WalletUnconfirmedTransactions::eraseDepositSpendingTransaction(const TransactionHash& hash) { + auto it = m_spentDeposits.find(hash); + if (it == m_spentDeposits.end()) { + return false; + } + + m_spentDeposits.erase(it); + return true; } void WalletUnconfirmedTransactions::add(const cryptonote::Transaction& tx, TransactionId transactionId, - uint64_t amount, const std::list& usedOutputs) { + uint64_t amount, const std::vector& usedOutputs) { auto cryptoHash = cryptonote::get_transaction_hash(tx); TransactionHash hash = reinterpret_cast(cryptoHash); @@ -81,6 +126,49 @@ void WalletUnconfirmedTransactions::updateTransactionId(const TransactionHash& h } } +void WalletUnconfirmedTransactions::addCreatedDeposit(DepositId id, uint64_t totalAmount) { + m_createdDeposits[id] = totalAmount; +} + +void WalletUnconfirmedTransactions::addDepositSpendingTransaction(const Hash& transactionHash, const UnconfirmedSpentDepositDetails& details) { + assert(m_spentDeposits.count(transactionHash) == 0); + m_spentDeposits.emplace(transactionHash, details); +} + +void WalletUnconfirmedTransactions::eraseCreatedDeposit(DepositId id) { + m_createdDeposits.erase(id); +} + +uint64_t WalletUnconfirmedTransactions::countCreatedDepositsSum() const { + uint64_t sum = 0; + + for (const auto& kv: m_createdDeposits) { + sum += kv.second; + } + + return sum; +} + +uint64_t WalletUnconfirmedTransactions::countSpentDepositsProfit() const { + uint64_t sum = 0; + + for (const auto& kv: m_spentDeposits) { + sum += kv.second.depositsSum - kv.second.fee; + } + + return sum; +} + +uint64_t WalletUnconfirmedTransactions::countSpentDepositsTotalAmount() const { + uint64_t sum = 0; + + for (const auto& kv: m_spentDeposits) { + sum += kv.second.depositsSum; + } + + return sum; +} + uint64_t WalletUnconfirmedTransactions::countUnconfirmedOutsAmount() const { uint64_t amount = 0; diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index 688c154c70..16f67b3ea2 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -9,6 +9,7 @@ #include "ITransfersContainer.h" #include +#include #include #include #include @@ -16,14 +17,14 @@ #include "crypto/hash.h" #include "cryptonote_core/cryptonote_basic.h" +#include + namespace cryptonote { class ISerializer; } namespace CryptoNote { -typedef std::pair TransactionOutputId; - struct UnconfirmedTransferDetails { UnconfirmedTransferDetails() : @@ -34,21 +35,36 @@ struct UnconfirmedTransferDetails { uint64_t outsAmount; time_t sentTime; TransactionId transactionId; - std::vector usedOutputs; + std::vector> usedOutputs; +}; + +struct UnconfirmedSpentDepositDetails { + TransactionId transactionId; + uint64_t depositsSum; + uint64_t fee; }; -class WalletUnconfirmedTransactions -{ +class WalletUnconfirmedTransactions { public: void serialize(cryptonote::ISerializer& s, const std::string& name); + void deserializeV1(cryptonote::ISerializer& s, const std::string& name); bool findTransactionId(const TransactionHash& hash, TransactionId& id); void erase(const TransactionHash& hash); void add(const cryptonote::Transaction& tx, TransactionId transactionId, - uint64_t amount, const std::list& usedOutputs); + uint64_t amount, const std::vector& usedOutputs); void updateTransactionId(const TransactionHash& hash, TransactionId id); + void addCreatedDeposit(DepositId id, uint64_t totalAmount); + void addDepositSpendingTransaction(const Hash& transactionHash, const UnconfirmedSpentDepositDetails& details); + + void eraseCreatedDeposit(DepositId id); + + uint64_t countCreatedDepositsSum() const; + uint64_t countSpentDepositsProfit() const; + uint64_t countSpentDepositsTotalAmount() const; + uint64_t countUnconfirmedOutsAmount() const; uint64_t countUnconfirmedTransactionsAmount() const; bool isUsed(const TransactionOutputInformation& out) const; @@ -57,11 +73,20 @@ class WalletUnconfirmedTransactions void collectUsedOutputs(); + bool eraseUnconfirmedTransaction(const TransactionHash& hash); + bool eraseDepositSpendingTransaction(const TransactionHash& hash); + + bool findUnconfirmedTransactionId(const TransactionHash& hash, TransactionId& id); + bool findUnconfirmedDepositSpendingTransactionId(const TransactionHash& hash, TransactionId& id); + typedef std::unordered_map> UnconfirmedTxsContainer; - typedef std::set UsedOutputsContainer; + typedef std::set> UsedOutputsContainer; UnconfirmedTxsContainer m_unconfirmedTxs; UsedOutputsContainer m_usedOutputs; + + std::unordered_map m_createdDeposits; + std::unordered_map m_spentDeposits; }; } // namespace CryptoNote diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index 647f46625c..7388055ece 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -16,9 +16,102 @@ #include "serialization/SerializationOverloads.h" #include #include +#include +#include namespace CryptoNote { +struct LegacyDeposit { + TransactionId creatingTransactionId; + TransactionId spendingTransactionId; + uint32_t term; + uint64_t amount; + uint64_t interest; +}; + +struct LegacyDepositInfo { + Deposit deposit; + uint32_t outputInTransaction; +}; + +void serialize(LegacyDeposit& deposit, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + + uint64_t creatingTxId = static_cast(deposit.creatingTransactionId); + serializer(creatingTxId, "creating_transaction_id"); + deposit.creatingTransactionId = static_cast(creatingTxId); + + uint64_t spendingTxIx = static_cast(deposit.spendingTransactionId); + serializer(spendingTxIx, "spending_transaction_id"); + deposit.creatingTransactionId = static_cast(spendingTxIx); + + serializer(deposit.term, "term"); + serializer(deposit.amount, "amount"); + serializer(deposit.interest, "interest"); + serializer.endObject(); +} + +void serialize(LegacyDepositInfo& depositInfo, const std::string& name, cryptonote::ISerializer& serializer) { + serializer.beginObject(name); + serializer(depositInfo.deposit, "deposit"); + serializer(depositInfo.outputInTransaction, "output_in_transaction"); + serializer.endObject(); +} + +namespace { + +class DepositIdSequenceIterator: public std::iterator { +public: + explicit DepositIdSequenceIterator(DepositId start) : val(start) {} + + DepositId operator *() const { return val; } + + const DepositIdSequenceIterator& operator ++() { ++val; return *this; } + DepositIdSequenceIterator operator ++(int) { DepositIdSequenceIterator copy(*this); ++val; return copy; } + + const DepositIdSequenceIterator& operator --() { --val; return *this; } + DepositIdSequenceIterator operator --(int) { DepositIdSequenceIterator copy(*this); --val; return copy; } + + DepositIdSequenceIterator operator +(const difference_type& n) const { return DepositIdSequenceIterator(val + n); } + DepositIdSequenceIterator operator -(const difference_type& n) const { return DepositIdSequenceIterator(val - n); } + + difference_type operator -(const DepositIdSequenceIterator& other) const { return val - other.val; } + + DepositIdSequenceIterator& operator +=(const difference_type& n) { val += n; return *this; } + DepositIdSequenceIterator& operator -=(const difference_type& n) { val -= n; return *this; } + + bool operator <(const DepositIdSequenceIterator& other) const { return val < other.val; } + bool operator >(const DepositIdSequenceIterator& other) const { return val > other.val; } + + bool operator <=(const DepositIdSequenceIterator& other) const { return !(val > other.val); } + bool operator >=(const DepositIdSequenceIterator& other) const { return !(val < other.val); } + + bool operator ==(const DepositIdSequenceIterator& other) const { return val == other.val; } + bool operator !=(const DepositIdSequenceIterator& other) const { return val != other.val; } + +private: + DepositId val; +}; + +void convertLegacyDeposits(const std::vector& legacyDeposits, UserDeposits& deposits) { + deposits.reserve(legacyDeposits.size()); + + for (const LegacyDepositInfo& legacyDepositInfo: legacyDeposits) { + DepositInfo info; + info.deposit.amount = legacyDepositInfo.deposit.amount; + info.deposit.creatingTransactionId = legacyDepositInfo.deposit.creatingTransactionId; + info.deposit.interest = legacyDepositInfo.deposit.interest; + info.deposit.spendingTransactionId = legacyDepositInfo.deposit.spendingTransactionId; + info.deposit.term = legacyDepositInfo.deposit.term; + info.deposit.locked = true; + info.outputInTransaction = legacyDepositInfo.outputInTransaction; + + deposits.push_back(std::move(info)); + } +} + +} + void WalletUserTransactionsCache::serialize(cryptonote::ISerializer& s, const std::string& name) { s.beginObject(name); @@ -29,12 +122,29 @@ void WalletUserTransactionsCache::serialize(cryptonote::ISerializer& s, const st if (s.type() == cryptonote::ISerializer::INPUT) { updateUnconfirmedTransactions(); + restoreTransactionOutputToDepositIndex(); rebuildPaymentsIndex(); } s.endObject(); } +void WalletUserTransactionsCache::deserializeLegacyV1(cryptonote::ISerializer& s, const std::string& name) { + s.beginObject(name); + + s(m_transactions, "transactions"); + s(m_transfers, "transfers"); + m_unconfirmedTransactions.deserializeV1(s, "unconfirmed"); + + std::vector legacyDeposits; + s(legacyDeposits, "deposits"); + + convertLegacyDeposits(legacyDeposits, m_deposits); + restoreTransactionOutputToDepositIndex(); + + s.endObject(); +} + bool paymentIdIsSet(const PaymentId& paymentId) { return std::all_of(std::begin(paymentId), std::end(paymentId), [](PaymentId::value_type v) { return v != 0; }); } @@ -83,7 +193,6 @@ void WalletUserTransactionsCache::rebuildPaymentsIndex() { } extra.clear(); } - } uint64_t WalletUserTransactionsCache::unconfirmedTransactionsAmount() const { @@ -94,6 +203,18 @@ uint64_t WalletUserTransactionsCache::unconfrimedOutsAmount() const { return m_unconfirmedTransactions.countUnconfirmedOutsAmount(); } +uint64_t WalletUserTransactionsCache::countUnconfirmedCreatedDepositsSum() const { + return m_unconfirmedTransactions.countCreatedDepositsSum(); +} + +uint64_t WalletUserTransactionsCache::countUnconfirmedSpentDepositsProfit() const { + return m_unconfirmedTransactions.countSpentDepositsProfit(); +} + +uint64_t WalletUserTransactionsCache::countUnconfirmedSpentDepositsTotalAmount() const { + return m_unconfirmedTransactions.countSpentDepositsTotalAmount(); +} + size_t WalletUserTransactionsCache::getTransactionCount() const { return m_transactions.size(); } @@ -111,7 +232,12 @@ TransactionId WalletUserTransactionsCache::addNewTransaction( TransactionInfo transaction; - transaction.firstTransferId = insertTransfers(transfers); + if (!transfers.empty()) { + transaction.firstTransferId = insertTransfers(transfers); + } else { + transaction.firstTransferId = INVALID_TRANSFER_ID; + } + transaction.transferCount = transfers.size(); transaction.firstDepositId = INVALID_DEPOSIT_ID; transaction.depositCount = 0; @@ -133,7 +259,7 @@ TransactionId WalletUserTransactionsCache::addNewTransaction( } void WalletUserTransactionsCache::updateTransaction( - TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::list& usedOutputs) { + TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::vector& usedOutputs) { m_unconfirmedTransactions.add(tx, transactionId, amount, usedOutputs); } @@ -148,9 +274,10 @@ void WalletUserTransactionsCache::updateTransactionSendingState(TransactionId tr } } -std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, - int64_t txBalance) { - std::shared_ptr event; +std::deque> WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance, + const std::vector& newDepositOutputs, const std::vector& spentDepositOutputs, + const cryptonote::Currency& currency) { + std::deque> events; TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; @@ -181,45 +308,45 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c transaction.messages = txInfo.messages; id = insertTransaction(std::move(transaction)); - // notification event - event = std::make_shared(id); + + events.push_back(std::unique_ptr(new WalletExternalTransactionCreatedEvent(id))); + + auto updatedDepositIds = createNewDeposits(id, newDepositOutputs, currency); + if (!updatedDepositIds.empty()) { + auto& tx = getTransaction(id); + tx.firstDepositId = updatedDepositIds[0]; + tx.depositCount = updatedDepositIds.size(); + } + + auto spentDepositIds = processSpentDeposits(id, spentDepositOutputs); + updatedDepositIds.insert(updatedDepositIds.end(), spentDepositIds.begin(), spentDepositIds.end()); + + if (!updatedDepositIds.empty()) { + events.push_back(std::unique_ptr(new WalletDepositsUpdatedEvent(std::move(updatedDepositIds)))); + } } else { TransactionInfo& tr = getTransaction(id); tr.blockHeight = txInfo.blockHeight; tr.timestamp = txInfo.timestamp; tr.state = TransactionState::Active; // notification event - event = std::make_shared(id); + events.push_back(std::unique_ptr(new WalletTransactionUpdatedEvent(id))); + + if (tr.firstDepositId != INVALID_DEPOSIT_ID) { + for (auto id = tr.firstDepositId; id < tr.firstDepositId + tr.depositCount; ++id) { + m_unconfirmedTransactions.eraseCreatedDeposit(id); + } + } } if (canInsertTransactionToIndex(getTransaction(id)) && paymentIdIsSet(txInfo.paymentId)) { pushToPaymentsIndex(txInfo.paymentId, id); } - return event; + return events; } -std::vector WalletUserTransactionsCache::getTransactionsByPaymentIds(const std::vector& paymentIds) const { - std::vector payments(paymentIds.size()); - auto payment = payments.begin(); - for (auto& key : paymentIds) { - payment->paymentId = key; - auto it = m_paymentsIndex.find(key); - if (it != m_paymentsIndex.end()) { - std::transform(it->second.begin(), it->second.end(), std::back_inserter(payment->transactions), - [this](decltype(it->second)::value_type val) { - assert(val < m_transactions.size()); - return m_transactions[val]; - }); - } - - ++payment; - } - - return payments; -} - -std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const TransactionHash& transactionHash) { +std::deque> WalletUserTransactionsCache::onTransactionDeleted(const TransactionHash& transactionHash) { TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; if (m_unconfirmedTransactions.findTransactionId(transactionHash, id)) { m_unconfirmedTransactions.erase(transactionHash); @@ -229,7 +356,7 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c id = findTransactionByHash(transactionHash); } - std::shared_ptr event; + std::deque> events; if (id != CryptoNote::INVALID_TRANSACTION_ID) { TransactionInfo& tr = getTransaction(id); std::vector extra(tr.extra.begin(), tr.extra.end()); @@ -241,13 +368,83 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c tr.timestamp = 0; tr.state = TransactionState::Deleted; - event = std::make_shared(id); + events.push_back(std::unique_ptr(new WalletTransactionUpdatedEvent(id))); + + std::vector unspentDeposits = getDepositIdsBySpendingTransaction(id); + + std::for_each(unspentDeposits.begin(), unspentDeposits.end(), [this] (DepositId id) { + Deposit& deposit = getDeposit(id); + deposit.spendingTransactionId = INVALID_TRANSACTION_ID; + }); + + DepositIdSequenceIterator depositIdSequenceStart(tr.firstDepositId); + DepositIdSequenceIterator depositIdSequenceEnd(tr.firstDepositId + tr.depositCount); + + if (depositIdSequenceStart != depositIdSequenceEnd || !unspentDeposits.empty()) { + unspentDeposits.insert(unspentDeposits.end(), depositIdSequenceStart, depositIdSequenceEnd); + events.push_back(std::unique_ptr(new WalletDepositsUpdatedEvent(std::move(unspentDeposits)))); + } } else { LOG_ERROR("Transaction wasn't found: " << transactionHash); assert(false); } - return event; + return events; +} + +std::vector WalletUserTransactionsCache::getTransactionsByPaymentIds(const std::vector& paymentIds) const { + std::vector payments(paymentIds.size()); + auto payment = payments.begin(); + for (auto& key : paymentIds) { + payment->paymentId = key; + auto it = m_paymentsIndex.find(key); + if (it != m_paymentsIndex.end()) { + std::transform(it->second.begin(), it->second.end(), std::back_inserter(payment->transactions), + [this](decltype(it->second)::value_type val) { + assert(val < m_transactions.size()); + return m_transactions[val]; + }); + } + + ++payment; + } + + return payments; +} + +std::vector WalletUserTransactionsCache::unlockDeposits(const std::vector& transfers) { + std::vector unlockedDeposits; + + for (const auto& transfer: transfers) { + auto it = m_transactionOutputToDepositIndex.find(std::tie(transfer.transactionHash, transfer.outputInTransaction)); + if (it == m_transactionOutputToDepositIndex.end()) { + continue; + } + + auto id = it->second; + unlockedDeposits.push_back(id); + + m_deposits[id].deposit.locked = false; + } + + return unlockedDeposits; +} + +std::vector WalletUserTransactionsCache::lockDeposits(const std::vector& transfers) { + std::vector lockedDeposits; + for (const auto& transfer: transfers) { + auto it = m_transactionOutputToDepositIndex.find(std::tie(transfer.transactionHash, transfer.outputInTransaction)); + if (it == m_transactionOutputToDepositIndex.end()) { + continue; + } + + auto id = it->second; + lockedDeposits.push_back(id); + + m_deposits[id].deposit.locked = true; + } + + return lockedDeposits; } TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferId transferId) const @@ -298,6 +495,12 @@ bool WalletUserTransactionsCache::getDeposit(DepositId depositId, Deposit& depos return true; } +Deposit& WalletUserTransactionsCache::getDeposit(DepositId depositId) { + assert(depositId < m_deposits.size()); + + return m_deposits[depositId].deposit; +} + TransactionId WalletUserTransactionsCache::insertTransaction(TransactionInfo&& Transaction) { m_transactions.emplace_back(std::move(Transaction)); return m_transactions.size() - 1; @@ -337,4 +540,123 @@ Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { return m_transfers.at(transferId); } +void WalletUserTransactionsCache::restoreTransactionOutputToDepositIndex() { + m_transactionOutputToDepositIndex.clear(); + + DepositId id = 0; + for (const auto& d: m_deposits) { + TransactionInfo transaction = m_transactions[d.deposit.creatingTransactionId]; + m_transactionOutputToDepositIndex[std::tie(transaction.hash, d.outputInTransaction)] = id; + ++id; + } +} + +DepositId WalletUserTransactionsCache::insertDeposit(const Deposit& deposit, size_t depositIndexInTransaction, const Hash& transactionHash) { + DepositInfo info; + info.deposit = deposit; + info.outputInTransaction = depositIndexInTransaction; + + DepositId id = m_deposits.size(); + m_deposits.push_back(std::move(info)); + + m_transactionOutputToDepositIndex.emplace(std::piecewise_construct, std::forward_as_tuple(transactionHash, static_cast(depositIndexInTransaction)), + std::forward_as_tuple(id)); + + return id; +} + +bool WalletUserTransactionsCache::getDepositInTransactionInfo(DepositId depositId, Hash& transactionHash, uint32_t& outputInTransaction) { + if (depositId >= m_deposits.size()) { + return false; + } + + assert(m_deposits[depositId].deposit.creatingTransactionId < m_transactions.size()); + + outputInTransaction = m_deposits[depositId].outputInTransaction; + transactionHash = m_transactions[m_deposits[depositId].deposit.creatingTransactionId].hash; + + return true; +} + +std::vector WalletUserTransactionsCache::createNewDeposits(TransactionId creatingTransactionId, const std::vector& depositOutputs, + const cryptonote::Currency& currency) { + std::vector deposits; + + for (size_t i = 0; i < depositOutputs.size(); i++) { + auto id = insertNewDeposit(depositOutputs[i], creatingTransactionId, currency); + deposits.push_back(id); + } + return deposits; +} + +DepositId WalletUserTransactionsCache::insertNewDeposit(const TransactionOutputInformation& depositOutput, TransactionId creatingTransactionId, + const cryptonote::Currency& currency) { + assert(depositOutput.type == TransactionTypes::OutputType::Multisignature); + assert(depositOutput.term != 0); + assert(m_transactionOutputToDepositIndex.find(std::tie(depositOutput.transactionHash, depositOutput.outputInTransaction)) == m_transactionOutputToDepositIndex.end()); + + Deposit deposit; + deposit.amount = depositOutput.amount; + deposit.creatingTransactionId = creatingTransactionId; + deposit.term = depositOutput.term; + deposit.spendingTransactionId = INVALID_TRANSACTION_ID; + deposit.interest = currency.calculateInterest(deposit.amount, deposit.term); + deposit.locked = true; + + return insertDeposit(deposit, depositOutput.outputInTransaction, depositOutput.transactionHash); +} + +std::vector WalletUserTransactionsCache::processSpentDeposits(TransactionId spendingTransactionId, const std::vector& spentDepositOutputs) { + std::vector deposits; + deposits.reserve(spentDepositOutputs.size()); + + for (size_t i = 0; i < spentDepositOutputs.size(); i++) { + auto depositId = getDepositId(spentDepositOutputs[i].transactionHash, spentDepositOutputs[i].outputInTransaction); + assert(depositId != INVALID_DEPOSIT_ID); + if (depositId == INVALID_DEPOSIT_ID) { + throw std::invalid_argument("processSpentDeposits error: requested deposit doesn't exist"); + } + + auto& d = m_deposits[depositId]; + d.deposit.spendingTransactionId = spendingTransactionId; + deposits.push_back(depositId); + } + return deposits; +} + +DepositId WalletUserTransactionsCache::getDepositId(const Hash& creatingTransactionHash, uint32_t outputInTransaction) { + auto it = m_transactionOutputToDepositIndex.find(std::tie(creatingTransactionHash, outputInTransaction)); + if (it == m_transactionOutputToDepositIndex.end()) { + return INVALID_DEPOSIT_ID; + } + + return it->second; +} + +std::vector WalletUserTransactionsCache::getDepositIdsBySpendingTransaction(TransactionId transactionId) { + std::vector ids; + + for (DepositId dId = 0; dId < m_deposits.size(); ++dId) { + auto& deposit = m_deposits[dId].deposit; + + if (deposit.spendingTransactionId == transactionId) { + ids.push_back(dId); + } + } + + return ids; +} + +void WalletUserTransactionsCache::addCreatedDeposit(DepositId id, uint64_t totalAmount) { + m_unconfirmedTransactions.addCreatedDeposit(id, totalAmount); +} + +void WalletUserTransactionsCache::addDepositSpendingTransaction(const Hash& transactionHash, const UnconfirmedSpentDepositDetails& details) { + m_unconfirmedTransactions.addDepositSpendingTransaction(transactionHash, details); +} + +void WalletUserTransactionsCache::eraseCreatedDeposit(DepositId id) { + m_unconfirmedTransactions.eraseCreatedDeposit(id); +} + } //namespace CryptoNote diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index c7a0c10755..4a1ed32c21 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -5,9 +5,12 @@ #pragma once +#include + #include "crypto/hash.h" #include "IWallet.h" #include "ITransfersContainer.h" +#include "cryptonote_core/Currency.h" #include #include "WalletEvent.h" @@ -18,27 +21,54 @@ namespace cryptonote { class ISerializer; } +namespace std { + template<> + struct hash> { + size_t operator()(const std::tuple& item) const { + size_t hash = 0; + boost::hash_combine(hash, std::get<0>(item)); + boost::hash_combine(hash, std::get<1>(item)); + return hash; + } + }; +} + namespace CryptoNote { +typedef std::vector UserDeposits; + class WalletUserTransactionsCache { public: WalletUserTransactionsCache() {} void serialize(cryptonote::ISerializer& serializer, const std::string& name); + void deserializeLegacyV1(cryptonote::ISerializer& serializer, const std::string& name); uint64_t unconfirmedTransactionsAmount() const; uint64_t unconfrimedOutsAmount() const; + uint64_t countUnconfirmedCreatedDepositsSum() const; + uint64_t countUnconfirmedSpentDepositsProfit() const; + uint64_t countUnconfirmedSpentDepositsTotalAmount() const; + size_t getTransactionCount() const; size_t getTransferCount() const; size_t getDepositCount() const; TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime, const std::vector& messages); - void updateTransaction(TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::list& usedOutputs); + void updateTransaction(TransactionId transactionId, const cryptonote::Transaction& tx, uint64_t amount, const std::vector& usedOutputs); void updateTransactionSendingState(TransactionId transactionId, std::error_code ec); - std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); - std::shared_ptr onTransactionDeleted(const TransactionHash& transactionHash); + void addCreatedDeposit(DepositId id, uint64_t totalAmount); + void addDepositSpendingTransaction(const Hash& transactionHash, const UnconfirmedSpentDepositDetails& details); + + std::deque> onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance, + const std::vector& newDeposits, const std::vector& spentDeposits, + const cryptonote::Currency& currency); + std::deque> onTransactionDeleted(const TransactionHash& transactionHash); + + std::vector unlockDeposits(const std::vector& transfers); + std::vector lockDeposits(const std::vector& transfers); TransactionId findTransactionByTransferId(TransferId transferId) const; @@ -47,9 +77,13 @@ class WalletUserTransactionsCache bool getTransfer(TransferId transferId, Transfer& transfer) const; Transfer& getTransfer(TransferId transferId); bool getDeposit(DepositId depositId, Deposit& deposit) const; + Deposit& getDeposit(DepositId depositId); bool isUsed(const TransactionOutputInformation& out) const; + DepositId insertDeposit(const Deposit& deposit, size_t depositIndexInTransaction, const Hash& transactionHash); + bool getDepositInTransactionInfo(DepositId depositId, Hash& transactionHash, uint32_t& outputInTransaction); + std::vector getTransactionsByPaymentIds(const std::vector& paymentIds) const; private: @@ -57,13 +91,24 @@ class WalletUserTransactionsCache TransactionId insertTransaction(TransactionInfo&& Transaction); TransferId insertTransfers(const std::vector& transfers); void updateUnconfirmedTransactions(); + void restoreTransactionOutputToDepositIndex(); + std::vector createNewDeposits(TransactionId creatingTransactionId, const std::vector& depositOutputs, + const cryptonote::Currency& currency); + DepositId insertNewDeposit(const TransactionOutputInformation& depositOutput, TransactionId creatingTransactionId, + const cryptonote::Currency& currency); + std::vector processSpentDeposits(TransactionId spendingTransactionId, const std::vector& spentDepositOutputs); + DepositId getDepositId(const Hash& creatingTransactionHash, uint32_t outputInTransaction); + + std::vector getDepositIdsBySpendingTransaction(TransactionId transactionId); + + void eraseCreatedDeposit(DepositId id); typedef std::vector UserTransfers; typedef std::vector UserTransactions; typedef std::vector UserDeposits; using Offset = UserTransactions::size_type; using UserPaymentIndex = std::unordered_map>; - + void rebuildPaymentsIndex(); void pushToPaymentsIndex(const PaymentId& paymentId, Offset distance); void pushToPaymentsIndexInternal(Offset distance, const TransactionInfo& info, std::vector& extra); @@ -73,6 +118,8 @@ class WalletUserTransactionsCache UserTransfers m_transfers; UserDeposits m_deposits; WalletUnconfirmedTransactions m_unconfirmedTransactions; + //tuple -> depositId + std::unordered_map, DepositId> m_transactionOutputToDepositIndex; UserPaymentIndex m_paymentsIndex; }; diff --git a/tests/unit_tests/TestBlockchainGenerator.h b/tests/unit_tests/TestBlockchainGenerator.h index 248b7b8802..8c957e20c9 100644 --- a/tests/unit_tests/TestBlockchainGenerator.h +++ b/tests/unit_tests/TestBlockchainGenerator.h @@ -34,6 +34,8 @@ class TestBlockchainGenerator void putTxPoolToBlockchain(); void clearTxPool(); + uint64_t getCurrentHeight() const { return m_blockchain.size() - 1; } + private: void addGenesisBlock(); diff --git a/tests/unit_tests/TransactionApiHelpers.h b/tests/unit_tests/TransactionApiHelpers.h index 917252c0f3..e4bc71d3f1 100644 --- a/tests/unit_tests/TransactionApiHelpers.h +++ b/tests/unit_tests/TransactionApiHelpers.h @@ -88,7 +88,7 @@ inline bool operator == (const AccountKeys& a, const AccountKeys& b) { return memcmp(&a, &b, sizeof(a)) == 0; } -inline bool operator==(const CryptoNote::TransactionOutputInformation& l, const CryptoNote::TransactionOutputInformation& r) { +inline bool operator==(const TransactionOutputInformation& l, const TransactionOutputInformation& r) { if (l.type != r.type) { return false; } @@ -97,7 +97,7 @@ inline bool operator==(const CryptoNote::TransactionOutputInformation& l, const return false; } - if (l.globalOutputIndex != r.globalOutputIndex ) { + if (l.globalOutputIndex != r.globalOutputIndex) { return false; } diff --git a/tests/unit_tests/test_TransfersContainer.cpp b/tests/unit_tests/test_TransfersContainer.cpp index 037381bb42..94bda7c47d 100644 --- a/tests/unit_tests/test_TransfersContainer.cpp +++ b/tests/unit_tests/test_TransfersContainer.cpp @@ -47,6 +47,16 @@ namespace { tx.addInput(senderKeys, info, kp); } + void addMultisignatureInput(ITransaction& tx, uint64_t amount, uint32_t signatures, uint64_t outputIndex, uint32_t term) { + TransactionTypes::InputMultisignature input; + input.amount = amount; + input.signatures = signatures; + input.outputIndex = outputIndex; + input.term = term; + + tx.addInput(input); + } + TransactionOutputInformationIn addTestMultisignatureOutput(ITransaction& transaction, uint64_t amount, uint64_t globalOutputIndex) { std::vector addresses; @@ -92,17 +102,18 @@ namespace { } std::unique_ptr addTransaction(uint64_t height = UNCONFIRMED_TRANSACTION_HEIGHT, - uint64_t outputAmount = TEST_OUTPUT_AMOUNT) { + uint64_t outputAmount = TEST_OUTPUT_AMOUNT, std::vector* unlockedTransfers = nullptr) { auto tx = createTransaction(); addTestInput(*tx, outputAmount + 1); auto outputIndex = (height == UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; auto outInfo = addTestKeyOutput(*tx, outputAmount, outputIndex, account); std::vector outputs = { outInfo }; - EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, outputs, {})); + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, outputs, {}, unlockedTransfers)); return tx; } - std::unique_ptr addSpendingTransaction(const Hash& sourceTx, uint64_t height, uint64_t outputIndex, uint64_t amount = TEST_OUTPUT_AMOUNT) { + std::unique_ptr addSpendingTransaction(const Hash& sourceTx, uint64_t height, uint64_t outputIndex, uint64_t amount = TEST_OUTPUT_AMOUNT, + std::vector* unlockedTransfers = nullptr) { std::unique_ptr tx; auto outputs = container.getTransactionOutputs(sourceTx, ITransfersContainer::IncludeTypeAll | ITransfersContainer::IncludeStateUnlocked | ITransfersContainer::IncludeStateSoftLocked); @@ -117,7 +128,11 @@ namespace { size_t inputAmount = 0; for (const auto& t : outputs) { inputAmount += t.amount; - addInput(*tx, account, t); + if (t.type == TransactionTypes::OutputType::Key) { + addInput(*tx, account, t); + } else if (t.type == TransactionTypes::OutputType::Multisignature) { + addMultisignatureInput(*tx, t.amount, t.requiredSignatures, t.globalOutputIndex, t.term); + } } EXPECT_GE(inputAmount, amount); @@ -130,11 +145,48 @@ namespace { transfers.emplace_back(addTestKeyOutput(*tx, inputAmount - amount, outputIndex + 1, account)); // change } - EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, transfers, {})); + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, transfers, {}, unlockedTransfers)); return tx; } + TransactionOutputInformationIn addDepositOutput(ITransaction& tx, uint64_t amount, uint32_t term, uint64_t height, uint32_t requiredSignatures = 1) { + auto index = tx.addOutput(amount, {account.address}, requiredSignatures, term); + auto outputIndex = (height == UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; + + TransactionTypes::OutputMultisignature output; + tx.getOutput(index, output); + + TransactionOutputInformationIn outputInfo; + outputInfo.type = TransactionTypes::OutputType::Multisignature; + outputInfo.amount = output.amount; + outputInfo.globalOutputIndex = outputIndex; + outputInfo.outputInTransaction = index; + outputInfo.transactionPublicKey = tx.getTransactionPublicKey(); + outputInfo.transactionHash = tx.getTransactionHash(); + outputInfo.requiredSignatures = requiredSignatures; + outputInfo.term = term; + outputInfo.keyImage = generateKeyImage(account, index, tx.getTransactionPublicKey()); + + return outputInfo; + } + + std::vector detachContainer(uint64_t height) { + std::vector deletedTransactions; + std::vector lockedTransfers; + + container.detach(height, deletedTransactions, lockedTransfers); + return deletedTransactions; + } + + std::vector detachContainerGetLockedTransfers(uint64_t height) { + std::vector deletedTransactions; + std::vector lockedTransfers; + + container.detach(height, deletedTransactions, lockedTransfers); + return lockedTransfers; + } + Currency currency; TransfersContainer container; AccountKeys account; @@ -169,7 +221,7 @@ TEST_F(TransfersContainer_addTransaction, orderIsRequired_unconfirmedAtAnyHeight TEST_F(TransfersContainer_addTransaction, orderIsRequired_afterDetach) { ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT + 1)); - container.detach(TEST_BLOCK_HEIGHT + 1); + detachContainer(TEST_BLOCK_HEIGHT + 1); ASSERT_NO_THROW(addTransaction(TEST_BLOCK_HEIGHT)); } @@ -801,7 +853,9 @@ TEST_F(TransfersContainer_detach, detachConfirmed) { ASSERT_EQ(1, container.transfersCount()); ASSERT_EQ(1, container.transactionsCount()); - container.detach(TEST_BLOCK_HEIGHT); + + detachContainer(TEST_BLOCK_HEIGHT); + ASSERT_EQ(0, container.transfersCount()); ASSERT_EQ(0, container.transactionsCount()); } @@ -818,7 +872,7 @@ TEST_F(TransfersContainer_detach, detachConfirmedSpendingTransaction) { ASSERT_EQ(2, container.transactionsCount()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); - container.detach(TEST_BLOCK_HEIGHT+1); + detachContainer(TEST_BLOCK_HEIGHT+1); ASSERT_EQ(1, container.transfersCount()); ASSERT_EQ(1, container.transactionsCount()); @@ -841,7 +895,8 @@ TEST_F(TransfersContainer_detach, threeRelatedTransactions) { ASSERT_EQ(3, container.transactionsCount()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); - container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + detachContainer(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE*2); ASSERT_EQ(1, container.transfersCount()); @@ -859,7 +914,7 @@ TEST_F(TransfersContainer_detach, detachConfirmedTransactionWithUnrelatedUnconfi ASSERT_EQ(2, container.transactionsCount()); ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAll)); - container.detach(TEST_BLOCK_HEIGHT); + detachContainer(TEST_BLOCK_HEIGHT); ASSERT_EQ(1, container.transfersCount()); ASSERT_EQ(1, container.transactionsCount()); @@ -879,7 +934,7 @@ TEST_F(TransfersContainer_detach, confirmedWithUnconfirmedSpendingTransaction_H1 ASSERT_EQ(2, container.transactionsCount()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); - container.detach(TEST_BLOCK_HEIGHT + 1); + detachContainer(TEST_BLOCK_HEIGHT + 1); ASSERT_EQ(1, container.transfersCount()); ASSERT_EQ(2, container.transactionsCount()); @@ -897,7 +952,7 @@ TEST_F(TransfersContainer_detach, confirmedWithUnconfirmedSpendingTransaction_H0 ASSERT_EQ(2, container.transactionsCount()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); - container.detach(TEST_BLOCK_HEIGHT); + detachContainer(TEST_BLOCK_HEIGHT); ASSERT_EQ(0, container.transfersCount()); ASSERT_EQ(0, container.transactionsCount()); @@ -911,23 +966,23 @@ TEST_F(TransfersContainer_detach, confirmedTwoOfThree) { ASSERT_EQ(3, container.transactionsCount()); - container.detach(TEST_BLOCK_HEIGHT); + detachContainer(TEST_BLOCK_HEIGHT); ASSERT_EQ(1, container.transactionsCount()); ASSERT_EQ(1, container.getTransactionOutputs(txHash, ITransfersContainer::IncludeAll).size()); } TEST_F(TransfersContainer_detach, transactionDetachAfterAdvance) { - container.detach(TEST_BLOCK_HEIGHT); addTransaction(TEST_BLOCK_HEIGHT); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); - container.detach(TEST_BLOCK_HEIGHT); + + detachContainer(TEST_BLOCK_HEIGHT); + ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); } - //--------------------------------------------------------------------------- // TransfersContainer_advanceHeight //--------------------------------------------------------------------------- @@ -936,19 +991,7 @@ class TransfersContainer_advanceHeight : public TransfersContainerTest { TransfersContainer_advanceHeight(){} }; - -TEST_F(TransfersContainer_advanceHeight, advanceFailed) { - ASSERT_TRUE(container.advanceHeight(1000)); - ASSERT_FALSE(container.advanceHeight(999)); // 1000 -> 999 -} - -TEST_F(TransfersContainer_advanceHeight, advanceSucceeded) { - ASSERT_TRUE(container.advanceHeight(1000)); // 1000 -> 1000 - ASSERT_TRUE(container.advanceHeight(1001)); // 1000 -> 1001 -} - TEST_F(TransfersContainer_advanceHeight, advanceUnlocksTransaction) { - container.detach(TEST_BLOCK_HEIGHT); addTransaction(TEST_BLOCK_HEIGHT); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAllUnlocked)); container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); @@ -958,6 +1001,206 @@ TEST_F(TransfersContainer_advanceHeight, advanceUnlocksTransaction) { ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); } +//--------------------------------------------------------------------------- +// TransfersContainer_transfersLockStateNotification +//--------------------------------------------------------------------------- +class TransfersContainer_transfersLockStateNotification : public TransfersContainer_advanceHeight { +public: + TransfersContainer_transfersLockStateNotification(){} + + const uint64_t AMOUNT_1 = 1122; + const uint64_t AMOUNT_2 = 2233; + const uint32_t TERM = 100; + const uint64_t TRANSACTION_HEIGHT_1 = 1; + + struct UnconfirmedTransactionInfo { + std::unique_ptr transaction; + std::vector outputs; + std::vector globalIndices; + + UnconfirmedTransactionInfo() { + } + + UnconfirmedTransactionInfo(UnconfirmedTransactionInfo&& other) : + transaction(std::move(other.transaction)), + outputs(std::move(other.outputs)), + globalIndices(std::move(other.globalIndices)) { + } + }; + + UnconfirmedTransactionInfo addUnconfirmedTransaction(uint64_t outputAmount = TEST_OUTPUT_AMOUNT) { + UnconfirmedTransactionInfo txInfo; + + txInfo.transaction = createTransaction(); + + addTestInput(*txInfo.transaction, outputAmount + 1); + auto outputIndex = UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX; + auto outInfo = addTestKeyOutput(*txInfo.transaction, outputAmount, outputIndex, account); + + txInfo.outputs = { outInfo }; + txInfo.globalIndices = { TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX }; + + std::vector unlockedTransfers; + EXPECT_TRUE(container.addTransaction(blockInfo(UNCONFIRMED_TRANSACTION_HEIGHT), *txInfo.transaction, txInfo.outputs, {}, &unlockedTransfers)); + EXPECT_TRUE(unlockedTransfers.empty()); + + return txInfo; + } + + void confirmTransaction(uint64_t height, const UnconfirmedTransactionInfo& txInfo) { + EXPECT_TRUE(container.markTransactionConfirmed(blockInfo(height), txInfo.transaction->getTransactionHash(), txInfo.globalIndices)); + } + + std::unique_ptr addDepositTransaction(uint64_t height, uint32_t term, uint64_t amount = TEST_OUTPUT_AMOUNT, std::vector* unlockedTransfers = nullptr) { + auto tx = createTransaction(); + auto outInfo = addDepositOutput(*tx, amount, term, height); + + std::vector outputs = { outInfo }; + + EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, outputs, {}, unlockedTransfers)); + + return tx; + } +}; + +TEST_F(TransfersContainer_transfersLockStateNotification, addTransactionReturnsUnlockedTransfers) { + std::vector unlockedTransfers; + + addTransaction(TRANSACTION_HEIGHT_1, AMOUNT_1); + addTransaction(TRANSACTION_HEIGHT_1 + TEST_TRANSACTION_SPENDABLE_AGE, AMOUNT_2, &unlockedTransfers); + + ASSERT_EQ(1, unlockedTransfers.size()); + EXPECT_EQ(AMOUNT_1, unlockedTransfers.front().amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, advanceReturnsUnlockedTransfers) { + std::vector unlockedTransfers; + + addTransaction(TRANSACTION_HEIGHT_1, AMOUNT_1); + unlockedTransfers = container.advanceHeight(TRANSACTION_HEIGHT_1 + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(1, unlockedTransfers.size()); + EXPECT_EQ(AMOUNT_1, unlockedTransfers.front().amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, advanceReturnsNoUnlockedTransfersForUnconfirmedTransaction) { + auto info = addUnconfirmedTransaction(TEST_OUTPUT_AMOUNT); + + auto unlockedTransfers = container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_TRUE(unlockedTransfers.empty()); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, advanceReturnsUnlockedTransfersAfterTransactionConfirmation) { + auto info = addUnconfirmedTransaction(TEST_OUTPUT_AMOUNT); + confirmTransaction(TEST_BLOCK_HEIGHT, info); + + auto unlockedTransfers = container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_EQ(1, unlockedTransfers.size()); + EXPECT_EQ(TEST_OUTPUT_AMOUNT, unlockedTransfers[0].amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, secondAdvanceReturnsNoUnlockedTransfers) { + addTransaction(TEST_BLOCK_HEIGHT, AMOUNT_1); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + auto transfers = container.advanceHeight(TEST_BLOCK_HEIGHT + 2 * TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_TRUE(transfers.empty()); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, advanceHeightLessThenDepositTermReturnsNoTransfers) { + assert(TEST_TRANSACTION_SPENDABLE_AGE < TERM); + assert(TERM > 1); + + addDepositTransaction(TEST_BLOCK_HEIGHT, TERM); + + auto unlocked = container.advanceHeight(TEST_BLOCK_HEIGHT + TERM - 1); + ASSERT_TRUE(unlocked.empty()); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, advanceHeightReturnsUnlockedDeposits) { + addDepositTransaction(TEST_BLOCK_HEIGHT, TERM, TEST_OUTPUT_AMOUNT); + + auto unlocked = container.advanceHeight(TEST_BLOCK_HEIGHT + TERM); + ASSERT_EQ(1, unlocked.size()); + EXPECT_EQ(TEST_OUTPUT_AMOUNT, unlocked[0].amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, detachLocksTransfers) { + addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + std::vector lockedTransfers = detachContainerGetLockedTransfers(TEST_BLOCK_HEIGHT + 1); + + ASSERT_EQ(1, lockedTransfers.size()); + EXPECT_EQ(TEST_OUTPUT_AMOUNT, lockedTransfers[0].amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, detachDoesntLockUnconfirmedTransfers) { + addTransaction(); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + std::vector lockedTransfers = detachContainerGetLockedTransfers(TEST_BLOCK_HEIGHT + 1); + ASSERT_TRUE(lockedTransfers.empty()); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, detachDoesntLockUnlockedTransfersForThisHeight) { + addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 10); + + std::vector lockedTransfers = detachContainerGetLockedTransfers(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1); + ASSERT_TRUE(lockedTransfers.empty()); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, detachLocksDeposits) { + assert(TEST_TRANSACTION_SPENDABLE_AGE <= TERM); + + addDepositTransaction(TEST_BLOCK_HEIGHT, TERM, TEST_OUTPUT_AMOUNT); + + uint64_t unlockHeight = TEST_BLOCK_HEIGHT + TERM; + container.advanceHeight(unlockHeight); + + std::vector lockedTransfers = detachContainerGetLockedTransfers(unlockHeight - 1); + ASSERT_EQ(1, lockedTransfers.size()); + EXPECT_EQ(TEST_OUTPUT_AMOUNT, lockedTransfers[0].amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, advanceHeightDoesntUnlockDeletedTransfers) { + addTransaction(TEST_BLOCK_HEIGHT); + + container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + detachContainer(TEST_BLOCK_HEIGHT); + auto unlocked = container.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + + ASSERT_TRUE(unlocked.empty()); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, restoreContainerAndUnlockConfirmedTransactions) { + addTransaction(TEST_BLOCK_HEIGHT); + + std::stringstream stream; + container.save(stream); + TransfersContainer container2(currency, TEST_TRANSACTION_SPENDABLE_AGE); + container2.load(stream); + + auto unlocked = container2.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_EQ(1, unlocked.size()); + EXPECT_EQ(TEST_OUTPUT_AMOUNT, unlocked[0].amount); +} + +TEST_F(TransfersContainer_transfersLockStateNotification, restoreContainerAndUnlockUnconfirmedTransactions) { + addTransaction(); + + std::stringstream stream; + container.save(stream); + TransfersContainer container2(currency, TEST_TRANSACTION_SPENDABLE_AGE); + container2.load(stream); + + auto unlocked = container2.advanceHeight(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); + ASSERT_TRUE(unlocked.empty()); +} //--------------------------------------------------------------------------- // TransfersContainer_balance @@ -1196,33 +1439,107 @@ TEST_F(TransfersContainer_getOutputs, filtersByStateAndKeySimultaneously) { ASSERT_EQ(AMOUNT_1 + AMOUNT_2, transfers.front().amount); } -class TransfersContainer_depositBalance : public TransfersContainerTest { -protected: +class TransfersContainer_getTransactionInputs : public TransfersContainerTest { +public: + const uint64_t AMOUNT_1 = 1000224; + const uint64_t AMOUNT_2 = 3392922; + const uint64_t AMOUNT_3 = AMOUNT_1 + AMOUNT_2; + const uint64_t TERM = 22231; - const uint64_t HEIGHT = 10; - const uint64_t TERM = 100; + TransfersContainer_getTransactionInputs() { + } - TransactionOutputInformationIn addDepositOutput(std::unique_ptr& tx, uint64_t amount, uint32_t term, uint64_t height, uint32_t requiredSignatures = 1) { - auto index = tx->addOutput(amount, {account.address}, requiredSignatures, term); - auto outputIndex = (height == UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX; + std::vector keyOutputs; + std::vector depositOutputs; - TransactionTypes::OutputMultisignature output; - tx->getOutput(index, output); + std::unique_ptr createGeneratingTransaction() { + auto generatingTransaction = createTransaction(); - TransactionOutputInformationIn outputInfo; - outputInfo.type = TransactionTypes::OutputType::Multisignature; - outputInfo.amount = output.amount; - outputInfo.globalOutputIndex = outputIndex; - outputInfo.outputInTransaction = index; - outputInfo.transactionPublicKey = tx->getTransactionPublicKey(); - outputInfo.transactionHash = tx->getTransactionHash(); - outputInfo.requiredSignatures = requiredSignatures; - outputInfo.term = term; - outputInfo.keyImage = generateKeyImage(account, index, tx->getTransactionPublicKey()); + auto out1 = addTestKeyOutput(*generatingTransaction, AMOUNT_1, 1, account); + auto out2 = addTestKeyOutput(*generatingTransaction, AMOUNT_2, 2, account); + auto out3 = addDepositOutput(*generatingTransaction, AMOUNT_3, TERM, TEST_BLOCK_HEIGHT); - return outputInfo; + std::vector outputs = { out1, out2, out3 }; + container.addTransaction(blockInfo(TEST_BLOCK_HEIGHT), *generatingTransaction, outputs, {}); + container.advanceHeight(TEST_BLOCK_HEIGHT + TERM); + + out1.transactionHash = generatingTransaction->getTransactionHash(); + out2.transactionHash = generatingTransaction->getTransactionHash(); + + keyOutputs.push_back(out1); + keyOutputs.push_back(out2); + depositOutputs.push_back(out3); + + return generatingTransaction; + } + + std::unique_ptr createSpendingTransaction(ITransaction& generatingTransaction) { + auto spendingTransaction = addSpendingTransaction(generatingTransaction.getTransactionHash(), TEST_BLOCK_HEIGHT + TERM + 1, 3, AMOUNT_3); + return spendingTransaction; + } + + void checkKeyInputs(const std::vector& inputs) { + for (const auto& input: keyOutputs) { + EXPECT_NE(inputs.end(), std::find(inputs.begin(), inputs.end(), static_cast(input))); + } + } + + void checkDepositInputs(const std::vector& inputs) { + for (const auto& input: depositOutputs) { + EXPECT_NE(inputs.end(), std::find(inputs.begin(), inputs.end(), static_cast(input))); + } + } + + void checkDepositInputs(ITransaction& spendingTransaction) { + std::vector inputs = container.getTransactionInputs(spendingTransaction.getTransactionHash(), ITransfersContainer::IncludeTypeDeposit); + ASSERT_EQ(depositOutputs.size(), inputs.size()); + + checkDepositInputs(inputs); } + void checkKeyInputs(ITransaction& spendingTransaction) { + std::vector inputs = container.getTransactionInputs(spendingTransaction.getTransactionHash(), ITransfersContainer::IncludeTypeKey); + ASSERT_EQ(keyOutputs.size(), inputs.size()); + + checkKeyInputs(inputs); + } + + void checkAllInputs(ITransaction& spendingTransaction) { + std::vector inputs = container.getTransactionInputs(spendingTransaction.getTransactionHash(), ITransfersContainer::IncludeTypeAll); + + ASSERT_EQ(keyOutputs.size() + depositOutputs.size(), inputs.size()); + checkKeyInputs(inputs); + checkDepositInputs(inputs); + } +}; + +TEST_F(TransfersContainer_getTransactionInputs, returnsAllInputsCorrectly) { + auto generatingTransaction = createGeneratingTransaction(); + auto spendingTransaction = createSpendingTransaction(*generatingTransaction); + + checkAllInputs(*spendingTransaction); +} + +TEST_F(TransfersContainer_getTransactionInputs, returnsDepositInputsCorrectly) { + auto generatingTransaction = createGeneratingTransaction(); + auto spendingTransaction = createSpendingTransaction(*generatingTransaction); + + checkDepositInputs(*spendingTransaction); +} + +TEST_F(TransfersContainer_getTransactionInputs, returnsKeyInputsCorrectly) { + auto generatingTransaction = createGeneratingTransaction(); + auto spendingTransaction = createSpendingTransaction(*generatingTransaction); + + checkKeyInputs(*spendingTransaction); +} + +class TransfersContainer_depositBalance : public TransfersContainer_getTransactionInputs { +protected: + + const uint64_t HEIGHT = 10; + const uint64_t TERM = 100; + void addTransaction(const std::unique_ptr& tx, TransactionOutputInformationIn outInfo, uint64_t height) { std::vector outputs = { outInfo }; EXPECT_TRUE(container.addTransaction(blockInfo(height), *tx, outputs, {})); @@ -1236,7 +1553,7 @@ class TransfersContainer_depositBalance : public TransfersContainerTest { TEST_F(TransfersContainer_depositBalance, depositBalanceLocked) { auto tx = createTransaction(); - TransactionOutputInformationIn outInfo = addDepositOutput(tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); + TransactionOutputInformationIn outInfo = addDepositOutput(*tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); addTransaction(tx, outInfo, HEIGHT); container.advanceHeight(HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE); @@ -1247,7 +1564,7 @@ TEST_F(TransfersContainer_depositBalance, depositBalanceLocked) { TEST_F(TransfersContainer_depositBalance, depositBalanceUnlocked) { auto tx = createTransaction(); - TransactionOutputInformationIn outInfo = addDepositOutput(tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); + TransactionOutputInformationIn outInfo = addDepositOutput(*tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); addTransaction(tx, outInfo, HEIGHT); container.advanceHeight(HEIGHT + TERM); @@ -1258,7 +1575,7 @@ TEST_F(TransfersContainer_depositBalance, depositBalanceUnlocked) { TEST_F(TransfersContainer_depositBalance, spendDepositOutput) { auto tx = createTransaction(); - TransactionOutputInformationIn outInfo = addDepositOutput(tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); + TransactionOutputInformationIn outInfo = addDepositOutput(*tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); addTransaction(tx, outInfo, HEIGHT); container.advanceHeight(HEIGHT + TERM); @@ -1282,7 +1599,7 @@ TEST_F(TransfersContainer_depositBalance, spendDepositOutput) { TEST_F(TransfersContainer_depositBalance, serializeContainer) { auto tx1 = createTransaction(); - TransactionOutputInformationIn out1 = addDepositOutput(tx1, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); + TransactionOutputInformationIn out1 = addDepositOutput(*tx1, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); addTransaction(tx1, out1, HEIGHT); container.advanceHeight(HEIGHT + TERM); @@ -1291,7 +1608,7 @@ TEST_F(TransfersContainer_depositBalance, serializeContainer) { const uint64_t AMOUNT2 = 8832; auto tx2 = createTransaction(); - TransactionOutputInformationIn out2 = addDepositOutput(tx2, AMOUNT2, TERM2, HEIGHT2); + TransactionOutputInformationIn out2 = addDepositOutput(*tx2, AMOUNT2, TERM2, HEIGHT2); addTransaction(tx2, out2, HEIGHT2); auto unlockedBalance = container.balance(ITransfersContainer::IncludeAllUnlocked); @@ -1317,7 +1634,7 @@ TEST_F(TransfersContainer_depositBalance, transactionUnlockTimeGreaterThenDeposi const uint64_t UNLOCK_TIME = TERM + 20; auto tx = createTransaction(); - TransactionOutputInformationIn out = addDepositOutput(tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); + TransactionOutputInformationIn out = addDepositOutput(*tx, TEST_OUTPUT_AMOUNT, TERM, HEIGHT); tx->setUnlockTime(UNLOCK_TIME); addTransaction(tx, out, HEIGHT); diff --git a/tests/unit_tests/test_TransfersContainerKeyImage.cpp b/tests/unit_tests/test_TransfersContainerKeyImage.cpp index e460dc2fbc..280b40ecd9 100644 --- a/tests/unit_tests/test_TransfersContainerKeyImage.cpp +++ b/tests/unit_tests/test_TransfersContainerKeyImage.cpp @@ -645,7 +645,9 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_oneOfThree) { ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); - container.detach(TEST_BLOCK_HEIGHT + 2); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT + 2, deletedTransactions, lockedTransfers); ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); } @@ -655,7 +657,9 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_oneOfTwo) { ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAllUnlocked)); - container.detach(TEST_BLOCK_HEIGHT + 1); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT + 1, deletedTransactions, lockedTransfers); ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeStateSoftLocked | ITransfersContainer::IncludeTypeAll)); } @@ -664,7 +668,10 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_revealsUnconfirmed) { addTransactionWithFixedKey(UNCONFIRMED, 2, TEST_OUTPUT_AMOUNT * 2); ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); - ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT).size()); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT, deletedTransactions, lockedTransfers); + ASSERT_EQ(1, deletedTransactions.size()); ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAll)); ASSERT_EQ(TEST_OUTPUT_AMOUNT * 2, container.balance(ITransfersContainer::IncludeAllLocked)); } @@ -676,7 +683,11 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_twoUnconfirmedHidden) { ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); - ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT).size()); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT, deletedTransactions, lockedTransfers); + + ASSERT_EQ(1, deletedTransactions.size()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); } @@ -686,7 +697,11 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_twoConfirmedOneUnconfirmedHid addTransactionWithFixedKey(UNCONFIRMED, 3, TEST_OUTPUT_AMOUNT * 3); ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); - ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT + 1).size()); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT + 1, deletedTransactions, lockedTransfers); + + ASSERT_EQ(1, deletedTransactions.size()); ASSERT_EQ(TEST_OUTPUT_AMOUNT, container.balance(ITransfersContainer::IncludeAll)); } @@ -699,7 +714,11 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_oneSpentOneConfirmed) { auto tx2 = addTransactionWithFixedKey(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, 2, TEST_OUTPUT_AMOUNT * 2); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); - ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1).size()); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 1, deletedTransactions, lockedTransfers); + + ASSERT_EQ(1, deletedTransactions.size()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); } @@ -714,7 +733,11 @@ TEST_F(TransfersContainerKeyImage, removeConfirmed_oneSpentTwoConfirmed) { ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); ASSERT_EQ(4, container.transactionsCount()); - ASSERT_EQ(1, container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 2).size()); + std::vector deletedTransactions; + std::vector lockedTransfers; + container.detach(TEST_BLOCK_HEIGHT + TEST_TRANSACTION_SPENDABLE_AGE + 2, deletedTransactions, lockedTransfers); + + ASSERT_EQ(1, deletedTransactions.size()); ASSERT_EQ(3, container.transactionsCount()); ASSERT_EQ(0, container.balance(ITransfersContainer::IncludeAll)); } diff --git a/tests/unit_tests/test_TransfersSubscription.cpp b/tests/unit_tests/test_TransfersSubscription.cpp index 0a83008f68..deed5180f3 100644 --- a/tests/unit_tests/test_TransfersSubscription.cpp +++ b/tests/unit_tests/test_TransfersSubscription.cpp @@ -116,7 +116,7 @@ TEST_F(TransfersSubscriptionTest, onError) { TEST_F(TransfersSubscriptionTest, advanceHeight) { ASSERT_TRUE(sub.advanceHeight(10)); - ASSERT_FALSE(sub.advanceHeight(9)); // can't go backwards + ASSERT_ANY_THROW(sub.advanceHeight(9)); // can't go backwards } diff --git a/tests/unit_tests/test_WalletUserTransactionsCache.cpp b/tests/unit_tests/test_WalletUserTransactionsCache.cpp index 5b919c58ac..c221807b52 100644 --- a/tests/unit_tests/test_WalletUserTransactionsCache.cpp +++ b/tests/unit_tests/test_WalletUserTransactionsCache.cpp @@ -6,14 +6,17 @@ #include "gtest/gtest.h" #include + #include +#include "cryptonote_core/Currency.h" #include using namespace CryptoNote; +using namespace cryptonote; class WalletUserTransactionsCacheTest : public testing::Test { public: - WalletUserTransactionsCacheTest() { + WalletUserTransactionsCacheTest() : currency(CurrencyBuilder().currency()) { cryptonote::createTxExtraWithPaymentId(stringPaymentId, rawExtra); crypto::hash hash; if (!cryptonote::getPaymentIdFromTxExtra(rawExtra, hash)) { @@ -42,6 +45,13 @@ class WalletUserTransactionsCacheTest : public testing::Test { return info; } + void updateTransaction(const CryptoNote::TransactionInformation& info, int64_t balance) { + std::vector newDeposits; + std::vector spentDeposits; + cache.onTransactionUpdated(info, balance, newDeposits, spentDeposits, currency); + } + + Currency currency; std::string stringPaymentId = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; WalletUserTransactionsCache cache; PaymentId paymentId; @@ -51,7 +61,7 @@ class WalletUserTransactionsCacheTest : public testing::Test { }; TEST_F(WalletUserTransactionsCacheTest, TransactionIsAddedToIndexWhenItIsConfirmed) { - cache.onTransactionUpdated(buildTransactionInformation(), 1000); + updateTransaction(buildTransactionInformation(), 1000); ASSERT_EQ(1, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); ASSERT_EQ(paymentId, cache.getTransactionsByPaymentIds({paymentId})[0].transactions[0].hash); } @@ -59,24 +69,24 @@ TEST_F(WalletUserTransactionsCacheTest, TransactionIsAddedToIndexWhenItIsConfirm TEST_F(WalletUserTransactionsCacheTest, TransactionWithInvalidHeightIsNotAdded) { auto tx = buildTransactionInformation(); tx.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; - cache.onTransactionUpdated(tx, 1000); + updateTransaction(tx, 1000); ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); } TEST_F(WalletUserTransactionsCacheTest, TransactionWithEmptyExtraIsNotAdded) { auto tx = buildTransactionInformation(); tx.extra.clear(); - cache.onTransactionUpdated(tx, 1000); + updateTransaction(tx, 1000); ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); } TEST_F(WalletUserTransactionsCacheTest, TransactionWithInvalidAmountIsNotAdded) { - cache.onTransactionUpdated(buildTransactionInformation(), 0); + updateTransaction(buildTransactionInformation(), 0); ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); } TEST_F(WalletUserTransactionsCacheTest, TransactionIsRemovedFromIndexWhenItIsRemovedFromCache) { - cache.onTransactionUpdated(buildTransactionInformation(), 1000); + updateTransaction(buildTransactionInformation(), 1000); ASSERT_EQ(1, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); cache.onTransactionDeleted(cache.getTransaction(id).hash); ASSERT_EQ(0, cache.getTransactionsByPaymentIds({paymentId})[0].transactions.size()); diff --git a/tests/unit_tests/test_wallet.cpp b/tests/unit_tests/test_wallet.cpp index bc87c3eba0..0d4886c6a9 100644 --- a/tests/unit_tests/test_wallet.cpp +++ b/tests/unit_tests/test_wallet.cpp @@ -18,6 +18,7 @@ #include "INodeStubs.h" #include "TestBlockchainGenerator.h" +namespace { class TrivialWalletObserver : public CryptoNote::IWalletObserver { @@ -46,6 +47,11 @@ class TrivialWalletObserver : public CryptoNote::IWalletObserver return true; } + bool waitForDepositsUpdated() { + if (!depositsUpdate.wait_for(std::chrono::milliseconds(5000))) return false; + return true; + } + virtual void synchronizationCompleted(std::error_code result) override { synced.notify(); } @@ -75,6 +81,10 @@ class TrivialWalletObserver : public CryptoNote::IWalletObserver this->pendingBalance = pendingBalance; } + virtual void depositsUpdated(const std::vector& depositIds) override { + depositsUpdate.notify(); + } + std::error_code sendResult; std::error_code saveResult; std::error_code loadResult; @@ -86,6 +96,7 @@ class TrivialWalletObserver : public CryptoNote::IWalletObserver EventWaiter saved; EventWaiter loaden; EventWaiter sent; + EventWaiter depositsUpdate; }; struct SaveOnInitWalletObserver: public CryptoNote::IWalletObserver { @@ -139,10 +150,140 @@ void WaitWalletLoad(TrivialWalletObserver* observer) { EXPECT_FALSE(ec); } +class ScopedObserverBase : public CryptoNote::IWalletObserver { +public: + ScopedObserverBase(CryptoNote::IWallet& wallet) : m_wallet(wallet) { + m_wallet.addObserver(this); + } + + ScopedObserverBase(const ScopedObserverBase&) = delete; + ScopedObserverBase(ScopedObserverBase&&) = delete; + + virtual ~ScopedObserverBase() { + m_wallet.removeObserver(this); + } + +protected: + CryptoNote::IWallet& m_wallet; + EventWaiter called; +}; + +class DepositsUpdatedScopedObserver : public ScopedObserverBase { +public: + DepositsUpdatedScopedObserver(CryptoNote::IWallet& wallet) : ScopedObserverBase(wallet) {} + virtual ~DepositsUpdatedScopedObserver() {} + + virtual void depositsUpdated(const std::vector& depositIds) override { + m_updatedDeposits = depositIds; + called.notify(); + } + + std::vector wait() { + if (!called.wait_for(std::chrono::milliseconds(5000))) { + throw std::runtime_error("Operation timeout"); + } + + return m_updatedDeposits; + } + +private: + std::vector m_updatedDeposits; +}; + +class DepositsActualBalanceChangedScopedObserver : public ScopedObserverBase { +public: + DepositsActualBalanceChangedScopedObserver(CryptoNote::IWallet& wallet) : ScopedObserverBase(wallet) {} + virtual ~DepositsActualBalanceChangedScopedObserver() {} + + virtual void actualDepositBalanceUpdated(uint64_t actualDepositBalance) override { + m_actualBalance = actualDepositBalance; + called.notify(); + } + + uint64_t wait() { + if (!called.wait_for(std::chrono::milliseconds(5000))) { + throw std::runtime_error("Operation timeout"); + } + + return m_actualBalance; + } + +private: + uint64_t m_actualBalance; +}; + +class DepositsPendingBalanceChangedScopedObserver : public ScopedObserverBase { +public: + DepositsPendingBalanceChangedScopedObserver(CryptoNote::IWallet& wallet) : ScopedObserverBase(wallet) {} + virtual ~DepositsPendingBalanceChangedScopedObserver() {} + + virtual void pendingDepositBalanceUpdated(uint64_t pendingDepositBalance) override { + m_pendingBalance = pendingDepositBalance; + called.notify(); + } + + uint64_t wait() { + if (!called.wait_for(std::chrono::milliseconds(5000))) { + throw std::runtime_error("Operation timeout"); + } + + return m_pendingBalance; + } + +private: + uint64_t m_pendingBalance; +}; + +class PendingBalanceChangedScopedObserver : public ScopedObserverBase { +public: + PendingBalanceChangedScopedObserver(CryptoNote::IWallet& wallet) : ScopedObserverBase(wallet) {} + virtual ~PendingBalanceChangedScopedObserver() {} + + virtual void pendingBalanceUpdated(uint64_t pendingBalance) override { + m_pendingBalance = pendingBalance; + called.notify(); + } + + uint64_t wait() { + if (!called.wait_for(std::chrono::milliseconds(5000))) { + throw std::runtime_error("Operation timeout"); + } + + return m_pendingBalance; + } + +private: + uint64_t m_pendingBalance; +}; + +class ActualBalanceChangedScopedObserver : public ScopedObserverBase { +public: + ActualBalanceChangedScopedObserver(CryptoNote::IWallet& wallet) : ScopedObserverBase(wallet) {} + virtual ~ActualBalanceChangedScopedObserver() {} + + virtual void actualBalanceUpdated(uint64_t actualBalance) override { + m_actualBalance = actualBalance; + called.notify(); + } + + uint64_t wait() { + if (!called.wait_for(std::chrono::milliseconds(5000))) { + throw std::runtime_error("Operation timeout"); + } + + return m_actualBalance; + } + +private: + uint64_t m_actualBalance; +}; + +} //namespace + class WalletApi : public ::testing::Test { public: - WalletApi() : m_currency(cryptonote::CurrencyBuilder().currency()), generator(m_currency) { + WalletApi() : m_currency(cryptonote::CurrencyBuilder().depositMinTerm(100).depositMinTotalRateFactor(0).defaultDustThreshold(0).currency()), generator(m_currency) { } void SetUp(); @@ -158,6 +299,12 @@ class WalletApi : public ::testing::Test void TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = ""); void performTransferWithErrorTx(const std::array& amounts, uint64_t fee); + CryptoNote::DepositId makeDeposit(uint64_t amount, uint64_t term, uint64_t fee, uint64_t mixin = 0); + void unlockDeposit(uint64_t term); + CryptoNote::DepositId makeDepositAndUnlock(uint64_t amount, uint64_t term, uint64_t fee, uint64_t mixin = 0); + CryptoNote::TransactionId withdrawDeposits(const std::vector& ids, uint64_t fee); + uint64_t calculateTotalDepositAmount(uint64_t amount, uint64_t term); + cryptonote::Currency m_currency; TestBlockchainGenerator generator; @@ -307,6 +454,42 @@ void WaitWalletLoad(TrivialWalletObserver* observer, std::error_code& ec) { ASSERT_TRUE(observer->waitForLoadEnd(ec)); } +CryptoNote::DepositId WalletApi::makeDeposit(uint64_t amount, uint64_t term, uint64_t fee, uint64_t mixin) { + auto txId = alice->deposit(term, amount, fee, mixin); + aliceNode->updateObservers(); + WaitWalletSync(aliceWalletObserver.get()); + + CryptoNote::TransactionInfo txInfo; + alice->getTransaction(txId, txInfo); + + return txInfo.firstDepositId; +} + +void WalletApi::unlockDeposit(uint64_t term) { + generator.generateEmptyBlocks(term - 1); //subtract 1 becaause INodeTrivialRefreshStub->relayTransaction adds new block implicitly + aliceNode->updateObservers(); + WaitWalletSync(aliceWalletObserver.get()); +} + +CryptoNote::DepositId WalletApi::makeDepositAndUnlock(uint64_t amount, uint64_t term, uint64_t fee, uint64_t mixin) { + auto id = makeDeposit(amount, term, fee, mixin); + unlockDeposit(term); + + return id; +} + +CryptoNote::TransactionId WalletApi::withdrawDeposits(const std::vector& ids, uint64_t fee) { + auto txId = alice->withdrawDeposits(ids, fee); + aliceNode->updateObservers(); + WaitWalletSync(aliceWalletObserver.get()); + + return txId; +} + +uint64_t WalletApi::calculateTotalDepositAmount(uint64_t amount, uint64_t term) { + return m_currency.calculateInterest(amount, term) + amount; +} + TEST_F(WalletApi, initAndSave) { SaveOnInitWalletObserver saveOnInit(alice.get()); alice->addObserver(&saveOnInit); @@ -1436,6 +1619,794 @@ TEST_F(WalletApi, sendBulkOfMessages) { carol->shutdown(); } +TEST_F(WalletApi, depositReturnsCorrectDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint32_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + const uint64_t AMOUNT = m_currency.depositMinAmount(); + + auto txId = alice->deposit(TERM, AMOUNT, FEE); + WaitWalletSend(aliceWalletObserver.get()); + + CryptoNote::TransactionInfo info; + ASSERT_TRUE(alice->getTransaction(txId, info)); + + EXPECT_EQ(0, info.firstDepositId); + EXPECT_EQ(1, info.depositCount); + EXPECT_EQ(-static_cast(AMOUNT + FEE), info.totalAmount); + EXPECT_EQ(CryptoNote::INVALID_TRANSFER_ID, info.firstTransferId); + EXPECT_EQ(0, info.transferCount); + EXPECT_EQ(FEE, info.fee); + + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(0, deposit)); + EXPECT_EQ(txId, deposit.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, deposit.spendingTransactionId); + EXPECT_EQ(TERM, deposit.term); + EXPECT_EQ(AMOUNT, deposit.amount); + EXPECT_EQ(m_currency.calculateInterest(deposit.amount, deposit.term), deposit.interest); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositWithMixinReturnsCorrectDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint32_t TERM = m_currency.depositMinTerm(); + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t FEE = m_currency.minimumFee(); + + auto txId = alice->deposit(TERM, AMOUNT, FEE, 3); + WaitWalletSend(aliceWalletObserver.get()); + + CryptoNote::TransactionInfo info; + ASSERT_TRUE(alice->getTransaction(txId, info)); + + EXPECT_EQ(0, info.firstDepositId); + EXPECT_EQ(1, info.depositCount); + EXPECT_EQ(-static_cast(AMOUNT + FEE), info.totalAmount); + EXPECT_EQ(CryptoNote::INVALID_TRANSFER_ID, info.firstTransferId); + EXPECT_EQ(0, info.transferCount); + EXPECT_EQ(FEE, info.fee); + + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(0, deposit)); + EXPECT_EQ(txId, deposit.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, deposit.spendingTransactionId); + EXPECT_EQ(TERM, deposit.term); + EXPECT_EQ(AMOUNT, deposit.amount); + EXPECT_EQ(m_currency.calculateInterest(deposit.amount, deposit.term), deposit.interest); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsUpdatedCallbackCame) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + alice->deposit(m_currency.depositMinTerm(), m_currency.depositMinAmount(), m_currency.minimumFee(), 3); + ASSERT_TRUE(aliceWalletObserver->waitForDepositsUpdated()); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsRestoredAfterSerialization) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT1 = m_currency.depositMinAmount(); + const uint64_t AMOUNT2 = m_currency.depositMinAmount() + 992; + const uint32_t TERM1 = m_currency.depositMinTerm(); + const uint32_t TERM2 = m_currency.depositMinTerm() + 1; + + auto firstTx = alice->deposit(TERM1, AMOUNT1, m_currency.minimumFee()); + WaitWalletSend(aliceWalletObserver.get()); + + auto secondTx = alice->deposit(TERM2, AMOUNT2, m_currency.minimumFee()); + WaitWalletSend(aliceWalletObserver.get()); + + std::stringstream data; + alice->save(data, false, false); + WaitWalletSave(aliceWalletObserver.get()); + alice->shutdown(); + + prepareBobWallet(); + bob->initAndLoad(data, "pass"); + WaitWalletSync(bobWalletObserver.get()); + + ASSERT_EQ(2, bob->getDepositCount()); + + CryptoNote::Deposit deposit1; + ASSERT_TRUE(bob->getDeposit(0, deposit1)); + EXPECT_EQ(AMOUNT1, deposit1.amount); + EXPECT_EQ(TERM1, deposit1.term); + EXPECT_EQ(firstTx, deposit1.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, deposit1.spendingTransactionId); + EXPECT_EQ(m_currency.calculateInterest(deposit1.amount, deposit1.term), deposit1.interest); + + CryptoNote::Deposit deposit2; + ASSERT_TRUE(bob->getDeposit(1, deposit2)); + EXPECT_EQ(AMOUNT2, deposit2.amount); + EXPECT_EQ(TERM2, deposit2.term); + EXPECT_EQ(secondTx, deposit2.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, deposit2.spendingTransactionId); + EXPECT_EQ(m_currency.calculateInterest(deposit2.amount, deposit2.term), deposit2.interest); + + bob->shutdown(); +} + +TEST_F(WalletApi, depositsRestoredFromBlockchain) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t AMOUNT2 = m_currency.depositMinAmount() + 1; + const uint32_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto unlockedDepositId = makeDepositAndUnlock(AMOUNT, TERM, FEE); + auto unlockedDepositCreatingTransactionId = alice->getTransactionCount() - 1; + + auto lockedDepositId = makeDeposit(AMOUNT2, TERM, FEE); + auto lockedDepositCreatingTransactionId = alice->getTransactionCount() - 1; + + std::stringstream data; + alice->save(data, false, false); + WaitWalletSave(aliceWalletObserver.get()); + + alice->shutdown(); + + prepareBobWallet(); + bob->initAndLoad(data, "pass"); + WaitWalletSync(bobWalletObserver.get()); + + ASSERT_EQ(2, bob->getDepositCount()); + + CryptoNote::Deposit unlockedDeposit; + bob->getDeposit(unlockedDepositId, unlockedDeposit); + EXPECT_EQ(AMOUNT, unlockedDeposit.amount); + EXPECT_EQ(TERM, unlockedDeposit.term); + EXPECT_EQ(m_currency.calculateInterest(AMOUNT, TERM), unlockedDeposit.interest); + EXPECT_EQ(unlockedDepositCreatingTransactionId, unlockedDeposit.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, unlockedDeposit.spendingTransactionId); + EXPECT_FALSE(unlockedDeposit.locked); + + CryptoNote::Deposit lockedDeposit; + bob->getDeposit(lockedDepositId, lockedDeposit); + EXPECT_EQ(AMOUNT2, lockedDeposit.amount); + EXPECT_EQ(TERM, lockedDeposit.term); + EXPECT_EQ(m_currency.calculateInterest(AMOUNT2, TERM), lockedDeposit.interest); + EXPECT_EQ(lockedDepositCreatingTransactionId, lockedDeposit.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, lockedDeposit.spendingTransactionId); + EXPECT_TRUE(lockedDeposit.locked); + + bob->shutdown(); +} + +TEST_F(WalletApi, depositsUnlock) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + auto walletActualBalance = alice->actualBalance(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto depositId = makeDepositAndUnlock(AMOUNT, TERM, FEE); + + uint64_t expectedActualDepositBalance = calculateTotalDepositAmount(AMOUNT, TERM); + EXPECT_EQ(expectedActualDepositBalance, alice->actualDepositBalance()); + EXPECT_EQ(0, alice->pendingDepositBalance()); + + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(depositId, deposit)); + EXPECT_FALSE(deposit.locked); + + EXPECT_EQ(walletActualBalance - AMOUNT - FEE, alice->actualBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithTooSmallTerm) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm() - 1; + const uint64_t FEE = m_currency.minimumFee(); + + ASSERT_ANY_THROW(makeDeposit(AMOUNT, TERM, FEE)); + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithTooBigTerm) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMaxTerm() + 1; + const uint64_t FEE = m_currency.minimumFee(); + + ASSERT_ANY_THROW(makeDeposit(AMOUNT, TERM, FEE)); + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithTooSmallAmount) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount() - 1; + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + ASSERT_ANY_THROW(makeDeposit(AMOUNT, TERM, FEE)); + alice->shutdown(); +} + +TEST_F(WalletApi, depositsUpdatedCallbackCalledOnDepositUnlock) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto depositId = makeDeposit(AMOUNT, TERM, FEE); + + DepositsUpdatedScopedObserver depositsUpdatedWaiter(*alice); + + unlockDeposit(TERM); + + auto depositsUpdated = depositsUpdatedWaiter.wait(); + ASSERT_EQ(1, depositsUpdated.size()); + EXPECT_EQ(depositId, depositsUpdated[0]); + + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(depositId, deposit)); + EXPECT_FALSE(deposit.locked); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithdraw) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + const uint64_t FEE2 = m_currency.minimumFee(); + + auto id = makeDepositAndUnlock(AMOUNT, TERM, FEE); + + withdrawDeposits({id}, FEE2); + EXPECT_EQ(calculateTotalDepositAmount(AMOUNT, TERM) - FEE2, alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsCheckSpendingTransactionId) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto id = makeDepositAndUnlock(AMOUNT, TERM, FEE); + auto spendingTxId = withdrawDeposits({id}, FEE); + + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(id, deposit)); + EXPECT_EQ(spendingTxId, deposit.spendingTransactionId); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithdrawTwoDepositsCheckSpendingTransactionId) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t AMOUNT2 = m_currency.depositMinAmount() + 1; + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto depositId1 = makeDeposit(AMOUNT, TERM, FEE); + auto depositId2 = makeDeposit(AMOUNT2, TERM, FEE); + + unlockDeposit(TERM); + + auto spendingTxId = withdrawDeposits({depositId1, depositId2}, FEE); + + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(depositId1, deposit)); + EXPECT_EQ(spendingTxId, deposit.spendingTransactionId); + + CryptoNote::Deposit deposit2; + ASSERT_TRUE(alice->getDeposit(depositId2, deposit2)); + EXPECT_EQ(spendingTxId, deposit2.spendingTransactionId); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithdrawWrongDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + ASSERT_ANY_THROW(withdrawDeposits({3}, m_currency.minimumFee())); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithdrawLockedDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto depositId = makeDeposit(AMOUNT, TERM, FEE); + unlockDeposit(TERM - 1); + + ASSERT_ANY_THROW(withdrawDeposits({depositId}, FEE)); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsWithdrawFeeGreaterThenAmount) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto depositId = makeDeposit(AMOUNT, TERM, FEE); + unlockDeposit(TERM); + + ASSERT_ANY_THROW(withdrawDeposits({depositId}, calculateTotalDepositAmount(AMOUNT, TERM) + 1)); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsUpdatedCallbackCalledOnWithdraw) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t AMOUNT2 = m_currency.depositMinAmount() + 1; + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto depositId1 = makeDeposit(AMOUNT, TERM, FEE); + auto depositId2 = makeDeposit(AMOUNT2, TERM, FEE); + + unlockDeposit(TERM); + + DepositsUpdatedScopedObserver depoUpdated(*alice); + + withdrawDeposits({depositId1, depositId2}, FEE); + + auto updatedDeposits = depoUpdated.wait(); + ASSERT_EQ(2, updatedDeposits.size()); + EXPECT_NE(updatedDeposits.end(), std::find(updatedDeposits.begin(), updatedDeposits.end(), depositId1)); + EXPECT_NE(updatedDeposits.end(), std::find(updatedDeposits.begin(), updatedDeposits.end(), depositId2)); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsBalancesRightAfterMakingDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + auto initialActualBalance = alice->actualBalance(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + DepositsPendingBalanceChangedScopedObserver depositPendingBalanceChanged(*alice); + + alice->deposit(TERM, AMOUNT, FEE); + WaitWalletSend(aliceWalletObserver.get()); + + auto depositPending = depositPendingBalanceChanged.wait(); + + EXPECT_EQ(calculateTotalDepositAmount(AMOUNT, TERM), depositPending); + EXPECT_EQ(0, alice->actualDepositBalance()); + + EXPECT_EQ(initialActualBalance - AMOUNT - FEE, alice->actualBalance() + alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsBalancesAfterUnlockingDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + auto initialTotalBalance = alice->actualBalance() + alice->pendingBalance(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + makeDeposit(AMOUNT, TERM, FEE); + + DepositsPendingBalanceChangedScopedObserver depositPendingBalanceChanged(*alice); + DepositsActualBalanceChangedScopedObserver depositActualBalanceChanged(*alice); + + unlockDeposit(TERM); + + auto depositPending = depositPendingBalanceChanged.wait(); + auto depositActual = depositActualBalanceChanged.wait(); + + EXPECT_EQ(calculateTotalDepositAmount(AMOUNT, TERM), depositActual); + EXPECT_EQ(0, depositPending); + EXPECT_EQ(initialTotalBalance - AMOUNT - FEE, alice->actualBalance() + alice->pendingBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, depositsBalancesAfterWithdrawDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + auto initialActualBalance = alice->actualBalance(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + const uint64_t FEE2 = m_currency.minimumFee() + 10; + + auto depositId = makeDepositAndUnlock(AMOUNT, TERM, FEE); + + DepositsActualBalanceChangedScopedObserver depositActualBalanceChanged(*alice); + PendingBalanceChangedScopedObserver pendingBalanceChanged(*alice); + + alice->withdrawDeposits({depositId}, FEE2); + + auto depositActual = depositActualBalanceChanged.wait(); + auto pendingBalance = pendingBalanceChanged.wait(); + + EXPECT_EQ(0, depositActual); + EXPECT_EQ(0, alice->pendingDepositBalance()); + EXPECT_EQ(calculateTotalDepositAmount(AMOUNT, TERM) - FEE2, pendingBalance); + EXPECT_EQ(initialActualBalance - AMOUNT - FEE, alice->actualBalance()); + + alice->shutdown(); +} + +TEST_F(WalletApi, lockedDepositsRemovedAfterDetach) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + auto initialActualBalance = alice->actualBalance(); + auto initialPendingBalance = alice->pendingBalance(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto detachHeight = generator.getCurrentHeight() - 1; + + auto id = makeDeposit(AMOUNT, TERM, FEE, 0); + + DepositsPendingBalanceChangedScopedObserver depositPendingBalanceChanged(*alice); + DepositsUpdatedScopedObserver depositsUpdatedCalled(*alice); + ActualBalanceChangedScopedObserver actualBalanceChanged(*alice); + + aliceNode->startAlternativeChain(detachHeight); + generator.generateEmptyBlocks(1); + aliceNode->updateObservers(); + WaitWalletSync(aliceWalletObserver.get()); + + auto depositPendingBalance = depositPendingBalanceChanged.wait(); + auto depositsUpdated = depositsUpdatedCalled.wait(); + auto actualBalance = actualBalanceChanged.wait(); + + EXPECT_EQ(initialActualBalance, actualBalance); + EXPECT_EQ(initialPendingBalance, alice->pendingBalance()); + EXPECT_EQ(0, depositPendingBalance); + + ASSERT_EQ(1, depositsUpdated.size()); + EXPECT_EQ(id, depositsUpdated[0]); + + EXPECT_EQ(1, alice->getDepositCount()); + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(id, deposit)); + + CryptoNote::TransactionInfo txInfo; + ASSERT_TRUE(alice->getTransaction(deposit.creatingTransactionId, txInfo)); + + EXPECT_EQ(CryptoNote::TransactionState::Deleted, txInfo.state); + + alice->shutdown(); +} + +TEST_F(WalletApi, unlockedDepositsRemovedAfterDetach) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + auto initialActualBalance = alice->actualBalance(); + auto initialPendingBalance = alice->pendingBalance(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto detachHeight = generator.getCurrentHeight() - 1; + + auto id = makeDepositAndUnlock(AMOUNT, TERM, FEE); + + DepositsActualBalanceChangedScopedObserver depositActualBalanceChanged(*alice); + DepositsUpdatedScopedObserver depositsUpdatedCalled(*alice); + ActualBalanceChangedScopedObserver actualBalanceChanged(*alice); + + aliceNode->startAlternativeChain(detachHeight); + generator.generateEmptyBlocks(1); + aliceNode->updateObservers(); + WaitWalletSync(aliceWalletObserver.get()); + + auto depositActualBalance = depositActualBalanceChanged.wait(); + auto depositsUpdated = depositsUpdatedCalled.wait(); + auto actualBalance = actualBalanceChanged.wait(); + + EXPECT_EQ(initialActualBalance, actualBalance); + EXPECT_EQ(initialPendingBalance, alice->pendingBalance()); + EXPECT_EQ(0, alice->pendingDepositBalance()); + EXPECT_EQ(0, depositActualBalance); + + ASSERT_EQ(1, depositsUpdated.size()); + EXPECT_EQ(id, depositsUpdated[0]); + + EXPECT_EQ(1, alice->getDepositCount()); + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(id, deposit)); + + CryptoNote::TransactionInfo txInfo; + ASSERT_TRUE(alice->getTransaction(deposit.creatingTransactionId, txInfo)); + + EXPECT_EQ(CryptoNote::TransactionState::Deleted, txInfo.state); + + alice->shutdown(); +} + +TEST_F(WalletApi, unlockedDepositsLockedAfterDetach) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + auto id = makeDepositAndUnlock(AMOUNT, TERM, FEE); + + auto detachHeight = generator.getCurrentHeight() - 2; + + DepositsActualBalanceChangedScopedObserver depositActualBalanceChanged(*alice); + DepositsPendingBalanceChangedScopedObserver depositsPendingBalanceChanged(*alice); + DepositsUpdatedScopedObserver depositsUpdatedCalled(*alice); + + aliceNode->startAlternativeChain(detachHeight); + generator.generateEmptyBlocks(1); + aliceNode->updateObservers(); + WaitWalletSync(aliceWalletObserver.get()); + + auto depositActualBalance = depositActualBalanceChanged.wait(); + auto depositPendingBalance = depositsPendingBalanceChanged.wait(); + auto depositsUpdated = depositsUpdatedCalled.wait(); + + EXPECT_EQ(calculateTotalDepositAmount(AMOUNT, TERM), depositPendingBalance); + EXPECT_EQ(0, depositActualBalance); + + ASSERT_EQ(1, depositsUpdated.size()); + EXPECT_EQ(id, depositsUpdated[0]); + + EXPECT_EQ(1, alice->getDepositCount()); + CryptoNote::Deposit deposit; + ASSERT_TRUE(alice->getDeposit(id, deposit)); + EXPECT_TRUE(deposit.locked); + + alice->shutdown(); +} + +TEST_F(WalletApi, serializeLockedDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + makeDeposit(AMOUNT, TERM, FEE); + + std::stringstream data; + alice->save(data); + WaitWalletSave(aliceWalletObserver.get()); + + alice->shutdown(); + + prepareBobWallet(); + bob->initAndLoad(data, "pass"); + WaitWalletSync(bobWalletObserver.get()); + + ASSERT_EQ(1, bob->getDepositCount()); + + CryptoNote::Deposit deposit; + EXPECT_TRUE(bob->getDeposit(0, deposit)); + EXPECT_EQ(1, deposit.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, deposit.spendingTransactionId); + EXPECT_EQ(TERM, deposit.term); + EXPECT_EQ(AMOUNT, deposit.amount); + EXPECT_EQ(m_currency.calculateInterest(AMOUNT, TERM), deposit.interest); + EXPECT_TRUE(deposit.locked); + + bob->shutdown(); +} + +TEST_F(WalletApi, serializeUnlockedDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + makeDepositAndUnlock(AMOUNT, TERM, FEE); + + std::stringstream data; + alice->save(data); + WaitWalletSave(aliceWalletObserver.get()); + + alice->shutdown(); + + prepareBobWallet(); + bob->initAndLoad(data, "pass"); + WaitWalletSync(bobWalletObserver.get()); + + ASSERT_EQ(1, bob->getDepositCount()); + + CryptoNote::Deposit deposit; + EXPECT_TRUE(bob->getDeposit(0, deposit)); + EXPECT_EQ(1, deposit.creatingTransactionId); + EXPECT_EQ(CryptoNote::INVALID_TRANSACTION_ID, deposit.spendingTransactionId); + EXPECT_EQ(TERM, deposit.term); + EXPECT_EQ(AMOUNT, deposit.amount); + EXPECT_EQ(m_currency.calculateInterest(AMOUNT, TERM), deposit.interest); + EXPECT_FALSE(deposit.locked); + + bob->shutdown(); +} + +TEST_F(WalletApi, serializeSpentDeposit) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + const uint64_t FEE2 = m_currency.minimumFee() + 10; + + auto id = makeDepositAndUnlock(AMOUNT, TERM, FEE); + withdrawDeposits({id}, FEE2); + + std::stringstream data; + alice->save(data); + WaitWalletSave(aliceWalletObserver.get()); + + alice->shutdown(); + + prepareBobWallet(); + bob->initAndLoad(data, "pass"); + WaitWalletSync(bobWalletObserver.get()); + + ASSERT_EQ(1, bob->getDepositCount()); + + CryptoNote::Deposit deposit; + EXPECT_TRUE(bob->getDeposit(0, deposit)); + EXPECT_EQ(1, deposit.creatingTransactionId); + EXPECT_EQ(2, deposit.spendingTransactionId); + EXPECT_EQ(TERM, deposit.term); + EXPECT_EQ(AMOUNT, deposit.amount); + EXPECT_EQ(m_currency.calculateInterest(AMOUNT, TERM), deposit.interest); + EXPECT_FALSE(deposit.locked); + + bob->shutdown(); +} + +TEST_F(WalletApi, depositsUnlockAfterLoad) { + alice->initAndGenerate("pass"); + WaitWalletSync(aliceWalletObserver.get()); + + GenerateOneBlockRewardAndUnlock(); + + const uint64_t AMOUNT = m_currency.depositMinAmount(); + const uint64_t TERM = m_currency.depositMinTerm(); + const uint64_t FEE = m_currency.minimumFee(); + + makeDeposit(AMOUNT, TERM, FEE); + + std::stringstream data; + alice->save(data); + WaitWalletSave(aliceWalletObserver.get()); + + alice->shutdown(); + + prepareBobWallet(); + bob->initAndLoad(data, "pass"); + WaitWalletSync(bobWalletObserver.get()); + + generator.generateEmptyBlocks(TERM); + bobNode->updateObservers(); + WaitWalletSync(bobWalletObserver.get()); + + ASSERT_EQ(1, bob->getDepositCount()); + + CryptoNote::Deposit deposit; + EXPECT_TRUE(bob->getDeposit(0, deposit)); + EXPECT_FALSE(deposit.locked); + + bob->shutdown(); +} + TEST_F(WalletApi, PaymentIdIndexWorks) { alice->initAndGenerate("pass"); ASSERT_NO_FATAL_FAILURE(WaitWalletSync(aliceWalletObserver.get())); From f697d93978df59153042a556995f6fb0ba732e02 Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Wed, 15 Jul 2015 13:23:00 +0100 Subject: [PATCH 35/59] Bytecoin v.1.0.5 release --- CMakeLists.txt | 3 +- ReleaseNotes.txt | 8 + contrib/CMakeLists.txt | 7 - contrib/epee/LICENSE.txt | 25 - contrib/epee/README.md | 1 - contrib/epee/demo/.gitignore | 1 - contrib/epee/demo/CMakeLists.txt | 49 - contrib/epee/demo/README.txt | 0 .../demo_http_server/demo_http_server.cpp | 217 --- .../demo/demo_http_server/demo_http_server.h | 103 -- contrib/epee/demo/demo_http_server/stdafx.cpp | 8 - contrib/epee/demo/demo_http_server/stdafx.h | 40 - .../epee/demo/demo_http_server/targetver.h | 13 - .../demo_levin_server/demo_levin_server.cpp | 200 --- .../demo_levin_server/demo_levin_server.h | 76 - .../epee/demo/demo_levin_server/stdafx.cpp | 30 - contrib/epee/demo/demo_levin_server/stdafx.h | 38 - .../epee/demo/demo_levin_server/targetver.h | 13 - contrib/epee/demo/generate_gcc.sh | 4 - contrib/epee/demo/generate_vc_proj.bat | 7 - contrib/epee/demo/iface/transport_defs.h | 221 --- contrib/epee/include/ado_db_helper.h | 1095 ------------ contrib/epee/include/console_handler.h | 430 ----- contrib/epee/include/copyable_atomic.h | 54 - contrib/epee/include/file_io_utils.h | 452 ----- .../epee/include/global_stream_operators.h | 35 - contrib/epee/include/gzip_encoding.h | 227 --- contrib/epee/include/hmac-md5.h | 93 - contrib/epee/include/include_base_utils.h | 34 - contrib/epee/include/math_helper.h | 273 --- contrib/epee/include/md5_l.h | 97 - contrib/epee/include/md5_l.inl | 563 ------ contrib/epee/include/md5global.h | 77 - contrib/epee/include/misc_language.h | 162 -- contrib/epee/include/misc_log_ex.cpp | 1029 ----------- contrib/epee/include/misc_log_ex.h | 507 ------ contrib/epee/include/misc_os_dependent.cpp | 97 - contrib/epee/include/misc_os_dependent.h | 52 - .../epee/include/net/abstract_tcp_server.h | 316 ---- .../epee/include/net/abstract_tcp_server2.h | 276 --- .../epee/include/net/abstract_tcp_server2.inl | 817 --------- .../epee/include/net/abstract_tcp_server_cp.h | 233 --- .../include/net/abstract_tcp_server_cp.inl | 605 ------- contrib/epee/include/net/http_base.h | 184 -- contrib/epee/include/net/http_client.h | 875 --------- .../include/net/http_client_abstract_invoke.h | 98 - contrib/epee/include/net/http_client_base.h | 73 - .../include/net/http_client_via_api_helper.h | 177 -- .../epee/include/net/http_protocol_handler.h | 212 --- .../include/net/http_protocol_handler.inl | 682 ------- contrib/epee/include/net/http_server_cp.h | 48 - contrib/epee/include/net/http_server_cp2.h | 47 - .../include/net/http_server_handlers_map2.h | 230 --- .../epee/include/net/http_server_impl_base.h | 112 -- .../net/http_server_thread_per_connect.h | 48 - .../include/net/jsonrpc_protocol_handler.h | 167 -- .../include/net/jsonrpc_server_handlers_map.h | 86 - .../include/net/jsonrpc_server_impl_base.h | 84 - contrib/epee/include/net/jsonrpc_structs.h | 96 - contrib/epee/include/net/levin_base.h | 125 -- contrib/epee/include/net/levin_client.h | 89 - contrib/epee/include/net/levin_client.inl | 194 -- contrib/epee/include/net/levin_client_async.h | 577 ------ .../epee/include/net/levin_client_async.inl | 0 contrib/epee/include/net/levin_helper.h | 137 -- .../epee/include/net/levin_protocol_handler.h | 178 -- .../net/levin_protocol_handler_async.h | 779 -------- contrib/epee/include/net/levin_server_cp.h | 47 - contrib/epee/include/net/levin_server_cp2.h | 49 - contrib/epee/include/net/local_ip.h | 72 - .../epee/include/net/multiprotocols_server.h | 47 - .../include/net/munin_connection_handler.h | 376 ---- contrib/epee/include/net/munin_node_server.h | 49 - contrib/epee/include/net/net_helper.h | 683 ------- contrib/epee/include/net/net_parse_helpers.h | 169 -- contrib/epee/include/net/net_utils_base.h | 177 -- contrib/epee/include/net/protocol_switcher.h | 121 -- contrib/epee/include/net/rpc_method_name.h | 31 - contrib/epee/include/net/smtp.h | 181 -- contrib/epee/include/net/smtp.inl | 1569 ----------------- contrib/epee/include/net/smtp_helper.h | 88 - contrib/epee/include/pragma_comp_defs.h | 14 - contrib/epee/include/profile_tools.h | 113 -- contrib/epee/include/reg_exp_definer.h | 84 - contrib/epee/include/reg_utils.h | 249 --- .../epee/include/serialization/enableable.h | 53 - .../serialization/keyvalue_serialization.h | 94 - .../keyvalue_serialization_overloads.h | 374 ---- .../include/serialization/serialize_base.h | 2 - contrib/epee/include/service_impl_base.h | 323 ---- contrib/epee/include/sha1.h | 51 - contrib/epee/include/sha1.inl | 179 -- contrib/epee/include/soci_helper.h | 142 -- contrib/epee/include/static_initializer.h | 55 - .../epee/include/storages/activity_notifier.h | 132 -- .../epee/include/storages/crypted_storage.h | 62 - .../include/storages/gzipped_inmemstorage.h | 68 - .../include/storages/http_abstract_invoke.h | 126 -- .../include/storages/levin_abstract_invoke2.h | 291 --- .../include/storages/parserse_base_utils.h | 260 --- .../epee/include/storages/portable_storage.h | 480 ----- .../include/storages/portable_storage_base.h | 160 -- .../storages/portable_storage_from_bin.h | 281 --- .../storages/portable_storage_from_json.h | 384 ---- .../portable_storage_template_helper.h | 123 -- .../storages/portable_storage_to_bin.h | 213 --- .../storages/portable_storage_to_json.h | 181 -- .../portable_storage_val_converters.h | 169 -- contrib/epee/include/string_coding.h | 295 ---- contrib/epee/include/string_tools.cpp | 487 ----- contrib/epee/include/string_tools.h | 360 ---- contrib/epee/include/syncobj.h | 238 --- contrib/epee/include/time_helper.h | 159 -- contrib/epee/include/tiny_ini.h | 75 - contrib/epee/include/to_nonconst_iterator.h | 52 - contrib/epee/include/warnings.h | 30 - contrib/epee/include/winobj.h | 227 --- contrib/epee/include/zlib_helper.h | 139 -- contrib/epee/tests/.gitignore | 1 - .../tests/data/storages/invalid_storage_1.bin | Bin 109577 -> 0 bytes .../tests/data/storages/invalid_storage_2.bin | Bin 20 -> 0 bytes .../tests/data/storages/invalid_storage_3.bin | 1 - .../tests/data/storages/invalid_storage_4.bin | Bin 18 -> 0 bytes .../tests/data/storages/valid_storage.bin | Bin 180345 -> 0 bytes contrib/epee/tests/generate_vc_proj.bat | 5 - contrib/epee/tests/src/CMakeLists.txt | 33 - contrib/epee/tests/src/misc/test_math.h | 82 - contrib/epee/tests/src/net/test_net.h | 403 ----- .../src/storages/portable_storages_test.h | 232 --- .../epee/tests/src/storages/storage_tests.h | 142 -- contrib/epee/tests/src/tests.cpp | 59 - include/BlockchainExplorerData.h | 130 ++ include/IBlockchainExplorer.h | 61 + include/IMultiWallet.h | 17 - include/INode.h | 8 + include/ITransaction.h | 1 + src/BlockchainExplorer/BlockchainExplorer.cpp | 523 ++++++ src/BlockchainExplorer/BlockchainExplorer.h | 88 + .../BlockchainExplorerDataBuilder.cpp | 358 ++++ .../BlockchainExplorerDataBuilder.h | 52 + .../BlockchainExplorerErrors.cpp | 27 + .../BlockchainExplorerErrors.h | 66 + src/CMakeLists.txt | 13 +- src/Common/ConsoleTools.cpp | 16 + src/Common/ConsoleTools.h | 1 + src/Common/JsonValue.cpp | 164 +- src/Common/JsonValue.h | 8 +- src/Common/Math.cpp | 18 + src/Common/Math.h | 43 + src/HTTP/HttpParserErrorCodes.h | 17 - src/InProcessNode/InProcessNode.cpp | 265 ++- src/InProcessNode/InProcessNode.h | 20 + src/Logging/ILogger.h | 4 + src/Logging/LoggerManager.cpp | 12 +- src/Platform/Linux/System/Dispatcher.h | 4 +- src/Platform/OSX/System/Timer.cpp | 4 +- src/Platform/OSX/System/Timer.h | 2 +- src/Platform/Windows/System/TcpConnection.cpp | 2 + src/Platform/Windows/System/Timer.cpp | 4 +- src/Platform/Windows/System/Timer.h | 2 +- src/System/Ipv4Address.cpp | 6 +- src/connectivity_tool/conn_tool.cpp | 32 +- src/crypto/crypto-ops-data.c | 2 +- src/crypto/crypto-ops.c | 2 +- src/crypto/crypto.cpp | 17 + src/crypto/crypto.h | 17 + src/crypto/generic-ops.h | 17 + src/crypto/hash-extra-blake.c | 2 +- src/crypto/hash-extra-groestl.c | 2 +- src/crypto/hash-extra-jh.c | 2 +- src/crypto/hash-extra-skein.c | 2 +- src/crypto/hash-ops.h | 17 + src/crypto/hash.c | 2 +- src/crypto/hash.h | 17 + src/crypto/initializer.h | 17 + src/crypto/random.c | 2 +- src/crypto/random.h | 17 + src/crypto/slow-hash.c | 2 +- src/crypto/slow-hash.cpp | 17 + src/crypto/slow-hash.inl | 2 +- src/crypto/tree-hash.c | 2 +- src/cryptonote_config.h | 4 +- src/cryptonote_core/AccountKVSerialization.h | 113 -- src/cryptonote_core/Currency.cpp | 5 + src/cryptonote_core/Currency.h | 4 + src/cryptonote_core/ICore.h | 38 +- src/cryptonote_core/Transaction.cpp | 9 +- src/cryptonote_core/account.cpp | 57 +- src/cryptonote_core/account.h | 11 +- src/cryptonote_core/blockchain_storage.cpp | 151 +- src/cryptonote_core/blockchain_storage.h | 29 +- src/cryptonote_core/cryptonote_basic.h | 1 - src/cryptonote_core/cryptonote_core.cpp | 97 +- src/cryptonote_core/cryptonote_core.h | 27 +- .../cryptonote_format_utils.cpp | 1 - .../cryptonote_serialization.cpp | 151 +- .../cryptonote_serialization.h | 67 +- src/cryptonote_core/cryptonote_stat_info.h | 16 +- src/cryptonote_core/miner.cpp | 16 +- src/cryptonote_core/miner.h | 16 +- src/cryptonote_core/tx_pool.cpp | 54 +- src/cryptonote_core/tx_pool.h | 4 +- src/cryptonote_core/verification_context.h | 1 + .../ICryptonoteProtocolObserver.h | 1 + .../ICryptonoteProtocolQuery.h | 2 + .../cryptonote_protocol_defs.h | 105 +- .../cryptonote_protocol_handler.cpp | 80 +- .../cryptonote_protocol_handler.h | 9 +- src/daemon/DeamonCommandsHandler.cpp | 2 +- src/daemon/daemon.cpp | 1 + src/node_rpc_proxy/NodeRpcProxy.cpp | 90 +- src/node_rpc_proxy/NodeRpcProxy.h | 19 +- src/p2p/LevinProtocol.h | 26 +- src/p2p/connection_context.h | 2 + src/p2p/net_node.cpp | 64 +- src/p2p/net_node.h | 8 +- src/p2p/p2p_protocol_defs.h | 174 +- src/payment_service/ConfigurationManager.cpp | 17 - src/payment_service/ConfigurationManager.h | 17 - src/payment_service/JsonRpcMessages.cpp | 203 +-- src/payment_service/JsonRpcMessages.h | 70 +- src/payment_service/JsonRpcServer.cpp | 148 +- src/payment_service/JsonRpcServer.h | 30 +- src/payment_service/NodeFactory.cpp | 47 +- src/payment_service/NodeFactory.h | 17 - .../PaymentServiceConfiguration.cpp | 17 - .../PaymentServiceConfiguration.h | 17 - src/payment_service/RpcNodeConfiguration.cpp | 17 - src/payment_service/RpcNodeConfiguration.h | 17 - src/payment_service/WalletFactory.cpp | 17 - src/payment_service/WalletFactory.h | 17 - src/payment_service/WalletObservers.cpp | 17 - src/payment_service/WalletObservers.h | 17 - src/payment_service/WalletService.cpp | 54 +- src/payment_service/WalletService.h | 18 +- .../WalletServiceErrorCodes.cpp | 17 - src/payment_service/WalletServiceErrorCodes.h | 17 - src/payment_service/main.cpp | 203 +-- src/rpc/HttpClient.h | 12 +- src/rpc/JsonRpc.h | 90 +- src/rpc/RpcServer.cpp | 59 +- src/rpc/RpcServer.h | 1 + src/rpc/core_rpc_server_commands_defs.h | 817 ++++----- .../BinaryInputStreamSerializer.cpp | 67 +- .../BinaryInputStreamSerializer.h | 39 +- .../BinaryOutputStreamSerializer.cpp | 60 +- .../BinaryOutputStreamSerializer.h | 37 +- src/serialization/ISerializer.h | 59 +- .../JsonInputStreamSerializer.cpp | 3 +- .../JsonInputValueSerializer.cpp | 169 +- src/serialization/JsonInputValueSerializer.h | 53 +- .../JsonOutputStreamSerializer.cpp | 129 +- .../JsonOutputStreamSerializer.h | 38 +- .../KVBinaryInputStreamSerializer.cpp | 129 +- .../KVBinaryInputStreamSerializer.h | 19 +- .../KVBinaryOutputStreamSerializer.cpp | 88 +- .../KVBinaryOutputStreamSerializer.h | 40 +- src/serialization/SerializationOverloads.h | 66 +- src/serialization/SerializationTools.h | 134 ++ src/simplewallet/simplewallet.cpp | 4 +- src/transfers/TransfersContainer.cpp | 11 +- src/transfers/TransfersContainer.h | 10 +- src/transfers/TransfersSubscription.cpp | 2 +- src/transfers/TransfersSubscription.h | 7 +- src/transfers/TransfersSynchronizer.cpp | 4 +- src/version.h.in | 4 +- src/wallet/LegacyKeysImporter.cpp | 18 +- src/wallet/Wallet.cpp | 5 - src/wallet/WalletSerialization.cpp | 13 +- src/wallet/WalletSerialization.h | 9 +- src/wallet/WalletTransactionSender.cpp | 6 +- src/wallet/WalletUnconfirmedTransactions.cpp | 6 +- src/wallet/WalletUnconfirmedTransactions.h | 2 +- src/wallet/WalletUserTransactionsCache.cpp | 10 +- src/wallet/WalletUserTransactionsCache.h | 2 +- src/wallet/wallet_rpc_server_commans_defs.h | 143 +- tests/CMakeLists.txt | 16 +- tests/TestGenerator/TestGenerator.cpp | 24 +- tests/core_tests/block_reward.cpp | 7 +- tests/core_tests/block_validation.cpp | 1 - tests/core_tests/chain_split_1.cpp | 2 - tests/core_tests/chain_switch_1.cpp | 7 +- tests/core_tests/chain_switch_1.h | 2 +- tests/core_tests/chaingen.cpp | 5 - tests/core_tests/chaingen.h | 58 +- tests/core_tests/chaingen001.cpp | 2 - tests/core_tests/chaingen_main.cpp | 15 +- tests/core_tests/double_spend.cpp | 1 - tests/core_tests/double_spend.inl | 17 - tests/core_tests/integer_overflow.cpp | 1 - tests/core_tests/ring_signature_1.cpp | 1 - tests/core_tests/transaction_tests.cpp | 6 +- tests/core_tests/tx_validation.cpp | 1 - tests/core_tests/upgrade.cpp | 1 - tests/crypto/crypto-ops-data.c | 17 - tests/crypto/crypto-ops.c | 17 - tests/crypto/hash.c | 17 - tests/crypto/random.c | 17 - .../BaseFunctionalTest.cpp | 40 +- .../integration_test_lib/BaseFunctionalTest.h | 2 + .../CoreRpcSerialization.cpp | 57 - .../CoreRpcSerialization.h | 36 - tests/integration_test_lib/TestNetwork.cpp | 1 + tests/integration_tests/BlockchainInfo.h | 8 +- .../node_rpc_proxy_test.cpp | 75 +- tests/transfers_tests/test_TxPoolSync.cpp | 600 +++++++ tests/unit_tests/ICoreStub.cpp | 102 +- tests/unit_tests/ICoreStub.h | 32 +- .../ICryptonoteProtocolQueryStub.cpp | 8 + .../unit_tests/ICryptonoteProtocolQueryStub.h | 8 +- tests/unit_tests/INodeStubs.cpp | 154 ++ tests/unit_tests/INodeStubs.h | 23 +- tests/unit_tests/TransactionApi.cpp | 24 + .../binary_serialization_compatibility.cpp | 7 - tests/unit_tests/get_xtype_from_string.cpp | 152 -- tests/unit_tests/main.cpp | 5 - tests/unit_tests/serialization_kv.cpp | 136 +- tests/unit_tests/test_BlockchainExplorer.cpp | 922 ++++++++++ tests/unit_tests/test_JsonValue.cpp | 67 + tests/unit_tests/test_TransfersConsumer.cpp | 2 +- tests/unit_tests/test_TransfersContainer.cpp | 4 +- tests/unit_tests/test_format_utils.cpp | 5 +- tests/unit_tests/test_inprocess_node.cpp | 396 ++++- tests/unit_tests/test_peerlist.cpp | 1 - tests/unit_tests/test_protocol_pack.cpp | 12 +- tests/unit_tests/tx_pool.cpp | 219 ++- 326 files changed, 6976 insertions(+), 28525 deletions(-) delete mode 100644 contrib/CMakeLists.txt delete mode 100644 contrib/epee/LICENSE.txt delete mode 100644 contrib/epee/README.md delete mode 100644 contrib/epee/demo/.gitignore delete mode 100644 contrib/epee/demo/CMakeLists.txt delete mode 100644 contrib/epee/demo/README.txt delete mode 100644 contrib/epee/demo/demo_http_server/demo_http_server.cpp delete mode 100644 contrib/epee/demo/demo_http_server/demo_http_server.h delete mode 100644 contrib/epee/demo/demo_http_server/stdafx.cpp delete mode 100644 contrib/epee/demo/demo_http_server/stdafx.h delete mode 100644 contrib/epee/demo/demo_http_server/targetver.h delete mode 100644 contrib/epee/demo/demo_levin_server/demo_levin_server.cpp delete mode 100644 contrib/epee/demo/demo_levin_server/demo_levin_server.h delete mode 100644 contrib/epee/demo/demo_levin_server/stdafx.cpp delete mode 100644 contrib/epee/demo/demo_levin_server/stdafx.h delete mode 100644 contrib/epee/demo/demo_levin_server/targetver.h delete mode 100644 contrib/epee/demo/generate_gcc.sh delete mode 100644 contrib/epee/demo/generate_vc_proj.bat delete mode 100644 contrib/epee/demo/iface/transport_defs.h delete mode 100644 contrib/epee/include/ado_db_helper.h delete mode 100644 contrib/epee/include/console_handler.h delete mode 100644 contrib/epee/include/copyable_atomic.h delete mode 100644 contrib/epee/include/file_io_utils.h delete mode 100644 contrib/epee/include/global_stream_operators.h delete mode 100644 contrib/epee/include/gzip_encoding.h delete mode 100644 contrib/epee/include/hmac-md5.h delete mode 100644 contrib/epee/include/include_base_utils.h delete mode 100644 contrib/epee/include/math_helper.h delete mode 100644 contrib/epee/include/md5_l.h delete mode 100644 contrib/epee/include/md5_l.inl delete mode 100644 contrib/epee/include/md5global.h delete mode 100644 contrib/epee/include/misc_language.h delete mode 100644 contrib/epee/include/misc_log_ex.cpp delete mode 100644 contrib/epee/include/misc_log_ex.h delete mode 100644 contrib/epee/include/misc_os_dependent.cpp delete mode 100644 contrib/epee/include/misc_os_dependent.h delete mode 100644 contrib/epee/include/net/abstract_tcp_server.h delete mode 100644 contrib/epee/include/net/abstract_tcp_server2.h delete mode 100644 contrib/epee/include/net/abstract_tcp_server2.inl delete mode 100644 contrib/epee/include/net/abstract_tcp_server_cp.h delete mode 100644 contrib/epee/include/net/abstract_tcp_server_cp.inl delete mode 100644 contrib/epee/include/net/http_base.h delete mode 100644 contrib/epee/include/net/http_client.h delete mode 100644 contrib/epee/include/net/http_client_abstract_invoke.h delete mode 100644 contrib/epee/include/net/http_client_base.h delete mode 100644 contrib/epee/include/net/http_client_via_api_helper.h delete mode 100644 contrib/epee/include/net/http_protocol_handler.h delete mode 100644 contrib/epee/include/net/http_protocol_handler.inl delete mode 100644 contrib/epee/include/net/http_server_cp.h delete mode 100644 contrib/epee/include/net/http_server_cp2.h delete mode 100644 contrib/epee/include/net/http_server_handlers_map2.h delete mode 100644 contrib/epee/include/net/http_server_impl_base.h delete mode 100644 contrib/epee/include/net/http_server_thread_per_connect.h delete mode 100644 contrib/epee/include/net/jsonrpc_protocol_handler.h delete mode 100644 contrib/epee/include/net/jsonrpc_server_handlers_map.h delete mode 100644 contrib/epee/include/net/jsonrpc_server_impl_base.h delete mode 100644 contrib/epee/include/net/jsonrpc_structs.h delete mode 100644 contrib/epee/include/net/levin_base.h delete mode 100644 contrib/epee/include/net/levin_client.h delete mode 100644 contrib/epee/include/net/levin_client.inl delete mode 100644 contrib/epee/include/net/levin_client_async.h delete mode 100644 contrib/epee/include/net/levin_client_async.inl delete mode 100644 contrib/epee/include/net/levin_helper.h delete mode 100644 contrib/epee/include/net/levin_protocol_handler.h delete mode 100644 contrib/epee/include/net/levin_protocol_handler_async.h delete mode 100644 contrib/epee/include/net/levin_server_cp.h delete mode 100644 contrib/epee/include/net/levin_server_cp2.h delete mode 100644 contrib/epee/include/net/local_ip.h delete mode 100644 contrib/epee/include/net/multiprotocols_server.h delete mode 100644 contrib/epee/include/net/munin_connection_handler.h delete mode 100644 contrib/epee/include/net/munin_node_server.h delete mode 100644 contrib/epee/include/net/net_helper.h delete mode 100644 contrib/epee/include/net/net_parse_helpers.h delete mode 100644 contrib/epee/include/net/net_utils_base.h delete mode 100644 contrib/epee/include/net/protocol_switcher.h delete mode 100644 contrib/epee/include/net/rpc_method_name.h delete mode 100644 contrib/epee/include/net/smtp.h delete mode 100644 contrib/epee/include/net/smtp.inl delete mode 100644 contrib/epee/include/net/smtp_helper.h delete mode 100644 contrib/epee/include/pragma_comp_defs.h delete mode 100644 contrib/epee/include/profile_tools.h delete mode 100644 contrib/epee/include/reg_exp_definer.h delete mode 100644 contrib/epee/include/reg_utils.h delete mode 100644 contrib/epee/include/serialization/enableable.h delete mode 100644 contrib/epee/include/serialization/keyvalue_serialization.h delete mode 100644 contrib/epee/include/serialization/keyvalue_serialization_overloads.h delete mode 100644 contrib/epee/include/serialization/serialize_base.h delete mode 100644 contrib/epee/include/service_impl_base.h delete mode 100644 contrib/epee/include/sha1.h delete mode 100644 contrib/epee/include/sha1.inl delete mode 100644 contrib/epee/include/soci_helper.h delete mode 100644 contrib/epee/include/static_initializer.h delete mode 100644 contrib/epee/include/storages/activity_notifier.h delete mode 100644 contrib/epee/include/storages/crypted_storage.h delete mode 100644 contrib/epee/include/storages/gzipped_inmemstorage.h delete mode 100644 contrib/epee/include/storages/http_abstract_invoke.h delete mode 100644 contrib/epee/include/storages/levin_abstract_invoke2.h delete mode 100644 contrib/epee/include/storages/parserse_base_utils.h delete mode 100644 contrib/epee/include/storages/portable_storage.h delete mode 100644 contrib/epee/include/storages/portable_storage_base.h delete mode 100644 contrib/epee/include/storages/portable_storage_from_bin.h delete mode 100644 contrib/epee/include/storages/portable_storage_from_json.h delete mode 100644 contrib/epee/include/storages/portable_storage_template_helper.h delete mode 100644 contrib/epee/include/storages/portable_storage_to_bin.h delete mode 100644 contrib/epee/include/storages/portable_storage_to_json.h delete mode 100644 contrib/epee/include/storages/portable_storage_val_converters.h delete mode 100644 contrib/epee/include/string_coding.h delete mode 100644 contrib/epee/include/string_tools.cpp delete mode 100644 contrib/epee/include/string_tools.h delete mode 100644 contrib/epee/include/syncobj.h delete mode 100644 contrib/epee/include/time_helper.h delete mode 100644 contrib/epee/include/tiny_ini.h delete mode 100644 contrib/epee/include/to_nonconst_iterator.h delete mode 100644 contrib/epee/include/warnings.h delete mode 100644 contrib/epee/include/winobj.h delete mode 100644 contrib/epee/include/zlib_helper.h delete mode 100644 contrib/epee/tests/.gitignore delete mode 100644 contrib/epee/tests/data/storages/invalid_storage_1.bin delete mode 100644 contrib/epee/tests/data/storages/invalid_storage_2.bin delete mode 100644 contrib/epee/tests/data/storages/invalid_storage_3.bin delete mode 100644 contrib/epee/tests/data/storages/invalid_storage_4.bin delete mode 100644 contrib/epee/tests/data/storages/valid_storage.bin delete mode 100644 contrib/epee/tests/generate_vc_proj.bat delete mode 100644 contrib/epee/tests/src/CMakeLists.txt delete mode 100644 contrib/epee/tests/src/misc/test_math.h delete mode 100644 contrib/epee/tests/src/net/test_net.h delete mode 100644 contrib/epee/tests/src/storages/portable_storages_test.h delete mode 100644 contrib/epee/tests/src/storages/storage_tests.h delete mode 100644 contrib/epee/tests/src/tests.cpp create mode 100644 include/BlockchainExplorerData.h create mode 100644 include/IBlockchainExplorer.h create mode 100644 src/BlockchainExplorer/BlockchainExplorer.cpp create mode 100644 src/BlockchainExplorer/BlockchainExplorer.h create mode 100644 src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp create mode 100644 src/BlockchainExplorer/BlockchainExplorerDataBuilder.h create mode 100644 src/BlockchainExplorer/BlockchainExplorerErrors.cpp create mode 100644 src/BlockchainExplorer/BlockchainExplorerErrors.h create mode 100644 src/Common/Math.cpp create mode 100644 src/Common/Math.h delete mode 100644 src/cryptonote_core/AccountKVSerialization.h create mode 100644 src/serialization/SerializationTools.h delete mode 100755 tests/integration_test_lib/CoreRpcSerialization.cpp delete mode 100755 tests/integration_test_lib/CoreRpcSerialization.h create mode 100644 tests/transfers_tests/test_TxPoolSync.cpp delete mode 100644 tests/unit_tests/get_xtype_from_string.cpp create mode 100644 tests/unit_tests/test_BlockchainExplorer.cpp create mode 100644 tests/unit_tests/test_JsonValue.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8893c32b7c..70e7873905 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_SUPPRESS_REGENERATION ON) project(Bytecoin) -include_directories(include src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") +include_directories(include src external "${CMAKE_BINARY_DIR}/version") if(APPLE) include_directories(SYSTEM /usr/include/malloc) enable_language(ASM) @@ -140,7 +140,6 @@ else() endif() endif() -add_subdirectory(contrib) add_subdirectory(external) add_subdirectory(src) add_subdirectory(tests) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index dcda199d29..e3a4f0b20a 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,11 @@ +Release notes 1.0.5 + +- High-level API for blockchain explorer +- Full network layer refactoring +- Transactions pool synchronization +- list_transactions method for RPC Wallet +- Various improvements + Release notes 1.0.4 - Bytecoin RPC Wallet diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt deleted file mode 100644 index 7222cce5ee..0000000000 --- a/contrib/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -file(GLOB_RECURSE EPEE epee/include/*) - -source_group(epee FILES ${EPEE}) - -add_library(epee ${EPEE}) - -set_property(TARGET epee PROPERTY FOLDER "external") diff --git a/contrib/epee/LICENSE.txt b/contrib/epee/LICENSE.txt deleted file mode 100644 index 4a6b529e5d..0000000000 --- a/contrib/epee/LICENSE.txt +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Andrey N. Sabelnikov 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 Andrey N. Sabelnikov 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. \ No newline at end of file diff --git a/contrib/epee/README.md b/contrib/epee/README.md deleted file mode 100644 index a69884f570..0000000000 --- a/contrib/epee/README.md +++ /dev/null @@ -1 +0,0 @@ -epee - is a small library of helpers, wrappers, tools and and so on, used to make my life easier. \ No newline at end of file diff --git a/contrib/epee/demo/.gitignore b/contrib/epee/demo/.gitignore deleted file mode 100644 index d9b4f015d3..0000000000 --- a/contrib/epee/demo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/* diff --git a/contrib/epee/demo/CMakeLists.txt b/contrib/epee/demo/CMakeLists.txt deleted file mode 100644 index b4ac2cc8ba..0000000000 --- a/contrib/epee/demo/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -set(Boost_USE_MULTITHREADED ON) -#set(Boost_DEBUG 1) -find_package(Boost COMPONENTS system filesystem thread date_time chrono regex ) - -include_directories( ${Boost_INCLUDE_DIRS} ) - - -IF (MSVC) - add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /nologo /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /bigobj" ) -ELSE() - # set stuff for other systems - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-reorder -D_GNU_SOURCE") -ENDIF() - - -include_directories(.) -include_directories(../include) -include_directories(iface) - - -# Add folders to filters -file(GLOB_RECURSE LEVIN_GENERAL_SECTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.h - ${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.inl - ${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.cpp) - -file(GLOB_RECURSE HTTP_GENERAL_SECTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.h - ${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.inl - ${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.cpp) - - - -source_group(general FILES ${LEVIN_GENERAL_SECTION} FILES ${HTTP_GENERAL_SECTION}) -#source_group(general FILES ${HTTP_GENERAL_SECTION}) - -add_executable(demo_http_server ${HTTP_GENERAL_SECTION} ) -add_executable(demo_levin_server ${LEVIN_GENERAL_SECTION} ) - -target_link_libraries( demo_http_server ${Boost_LIBRARIES} ) -target_link_libraries( demo_levin_server ${Boost_LIBRARIES} ) - -IF (NOT WIN32) - target_link_libraries (demo_http_server rt) - target_link_libraries (demo_levin_server rt) -ENDIF() - - diff --git a/contrib/epee/demo/README.txt b/contrib/epee/demo/README.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/contrib/epee/demo/demo_http_server/demo_http_server.cpp b/contrib/epee/demo/demo_http_server/demo_http_server.cpp deleted file mode 100644 index 85547e4c9e..0000000000 --- a/contrib/epee/demo/demo_http_server/demo_http_server.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "stdafx.h" -#include "console_handler.h" -#include "demo_http_server.h" -#include "net/http_client.h" -#include "storages/http_abstract_invoke.h" - - -template -bool communicate(const std::string url, t_request& req, t_response& rsp, const std::string& ip, const std::string& port, bool use_json, bool use_jrpc = false) -{ - epee::net_utils::http::http_simple_client http_client; - bool r = http_client.connect(ip, port, 1000); - CHECK_AND_ASSERT_MES(r, false, "failed to connect"); - if(use_json) - { - if(use_jrpc) - { - epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); - req_t.jsonrpc = "2.0"; - req_t.id = epee::serialization::storage_entry(10); - req_t.method = "command_example_1"; - req_t.params = req; - epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); - if(!epee::net_utils::invoke_http_json_remote_command2("/request_json_rpc", req_t, resp_t, http_client)) - { - return false; - } - rsp = resp_t.result; - return true; - }else - return epee::net_utils::invoke_http_json_remote_command2(url, req, rsp, http_client); - } - else - return epee::net_utils::invoke_http_bin_remote_command2(url, req, rsp, http_client); -} - - -int main(int argc, char* argv[]) -{ - TRY_ENTRY(); - string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str()); - - - - LOG_PRINT("Demo server starting ...", LOG_LEVEL_0); - - - demo::demo_http_server srv; - - start_default_console(&srv, "#"); - - std::string bind_param = "0.0.0.0"; - std::string port = "83"; - - if(!srv.init(port, bind_param)) - { - LOG_ERROR("Failed to initialize srv!"); - return 1; - } - - //log loop - srv.run(); - size_t count = 0; - while (!srv.is_stop()) - { - - demo::COMMAND_EXAMPLE_1::request req; - req.sub = demo::get_test_data(); - demo::COMMAND_EXAMPLE_1::response rsp; - bool r = false; - if(count%2) - {//invoke json - r = communicate("/request_api_json_1", req, rsp, "127.0.0.1", port, true, true); - }else{ - r = communicate("/request_api_bin_1", req, rsp, "127.0.0.1", port, false); - } - CHECK_AND_ASSERT_MES(r, false, "failed to invoke http request"); - CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response"); - CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response"); - CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response"); - //misc_utils::sleep_no_w(1000); - ++count; - } - bool r = srv.wait_stop(); - CHECK_AND_ASSERT_MES(r, 1, "failed to wait server stop"); - srv.deinit(); - - LOG_PRINT("Demo server stoped.", LOG_LEVEL_0); - return 1; - - CATCH_ENTRY_L0("main", 1); -} - -/************************************************************************/ -/* */ -/************************************************************************/ -namespace demo -{ - bool demo_http_server::init(const std::string& bind_port, const std::string& bind_ip) - { - - - //set self as callback handler - m_net_server.get_config_object().m_phandler = this; - - //here set folder for hosting reqests - m_net_server.get_config_object().m_folder = ""; - - LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); - return m_net_server.init_server(bind_port, bind_ip); - } - - bool demo_http_server::run() - { - m_stop = false; - //here you can set worker threads count - int thrds_count = 4; - - //go to loop - LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); - if(!m_net_server.run_server(thrds_count, false)) - { - LOG_ERROR("Failed to run net tcp server!"); - } - - return true; - } - - bool demo_http_server::deinit() - { - return m_net_server.deinit_server(); - } - - bool demo_http_server::send_stop_signal() - { - m_stop = true; - m_net_server.send_stop_signal(); - return true; - } - - bool demo_http_server::on_requestr_uri_1(const net_utils::http::http_request_info& query_info, - net_utils::http::http_response_info& response, - const net_utils::connection_context_base& m_conn_context) - { - return true; - } - - - bool demo_http_server::on_requestr_uri_2(const net_utils::http::http_request_info& query_info, - net_utils::http::http_response_info& response, - const net_utils::connection_context_base& m_conn_context) - { - return true; - } - - - bool demo_http_server::on_hosting_request( const net_utils::http::http_request_info& query_info, - net_utils::http::http_response_info& response, - const net_utils::connection_context_base& m_conn_context) - { - //read file from filesystem here - return true; - } - - bool demo_http_server::on_request_api_1(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, connection_context& ctxt) - { - CHECK_AND_ASSERT_MES(req.sub == demo::get_test_data(), false, "wrong request"); - res.m_success = true; - res.subs.push_back(req.sub); - return true; - } - - bool demo_http_server::on_request_api_1_with_error(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, epee::json_rpc::error& error_resp, connection_context& ctxt) - { - error_resp.code = 232432; - error_resp.message = "bla bla bla"; - return false; - } - - bool demo_http_server::on_request_api_2(const COMMAND_EXAMPLE_2::request& req, COMMAND_EXAMPLE_2::response& res, connection_context& ctxt) - { - return true; - } -} diff --git a/contrib/epee/demo/demo_http_server/demo_http_server.h b/contrib/epee/demo/demo_http_server/demo_http_server.h deleted file mode 100644 index 088ead548e..0000000000 --- a/contrib/epee/demo/demo_http_server/demo_http_server.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "net/http_server_cp2.h" -#include "transport_defs.h" -#include "net/http_server_handlers_map2.h" - -using namespace epee; - -namespace demo -{ - - class demo_http_server: public net_utils::http::i_http_server_handler - { - public: - typedef epee::net_utils::connection_context_base connection_context; - - demo_http_server():m_stop(false){} - bool run(); - bool init(const std::string& bind_port = "11112", const std::string& bind_ip = "0.0.0.0"); - bool deinit(); - bool send_stop_signal(); - bool is_stop(){return m_stop;} - bool wait_stop(){return m_net_server.timed_wait_server_stop(100000);} - private: - - - CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map - - BEGIN_URI_MAP2() - MAP_URI2("/requestr_uri_1", on_requestr_uri_1) - MAP_URI2("/requestr_uri_2", on_requestr_uri_1) - //MAP_URI_AUTO_XML2("/request_api_xml_1", on_request_api_1, COMMAND_EXAMPLE_1) - //MAP_URI_AUTO_XML2("/request_api_xml_2", on_request_api_2, COMMAND_EXAMPLE_2) - MAP_URI_AUTO_JON2("/request_api_json_1", on_request_api_1, COMMAND_EXAMPLE_1) - MAP_URI_AUTO_JON2("/request_api_json_2", on_request_api_2, COMMAND_EXAMPLE_2) - MAP_URI_AUTO_BIN2("/request_api_bin_1", on_request_api_1, COMMAND_EXAMPLE_1) - MAP_URI_AUTO_BIN2("/request_api_bin_2", on_request_api_2, COMMAND_EXAMPLE_2) - BEGIN_JSON_RPC_MAP("/request_json_rpc") - MAP_JON_RPC("command_example_1", on_request_api_1, COMMAND_EXAMPLE_1) - MAP_JON_RPC("command_example_2", on_request_api_2, COMMAND_EXAMPLE_2) - MAP_JON_RPC_WE("command_example_1_we", on_request_api_1_with_error, COMMAND_EXAMPLE_1) - END_JSON_RPC_MAP() - CHAIN_URI_MAP2(on_hosting_request) - END_URI_MAP2() - - - - bool on_requestr_uri_1(const net_utils::http::http_request_info& query_info, - net_utils::http::http_response_info& response, - const net_utils::connection_context_base& m_conn_context); - - - bool on_requestr_uri_2(const net_utils::http::http_request_info& query_info, - net_utils::http::http_response_info& response, - const net_utils::connection_context_base& m_conn_context); - - - - - bool on_hosting_request( const net_utils::http::http_request_info& query_info, - net_utils::http::http_response_info& response, - const net_utils::connection_context_base& m_conn_context); - - bool on_request_api_1(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, connection_context& ctxt); - bool on_request_api_2(const COMMAND_EXAMPLE_2::request& req, COMMAND_EXAMPLE_2::response& res, connection_context& ctxt); - - bool on_request_api_1_with_error(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, epee::json_rpc::error& error_resp, connection_context& ctxt); - - net_utils::boosted_http_server_custum_handling m_net_server; - std::atomic m_stop; - }; -} - diff --git a/contrib/epee/demo/demo_http_server/stdafx.cpp b/contrib/epee/demo/demo_http_server/stdafx.cpp deleted file mode 100644 index ecec246572..0000000000 --- a/contrib/epee/demo/demo_http_server/stdafx.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// demo_http_server.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/contrib/epee/demo/demo_http_server/stdafx.h b/contrib/epee/demo/demo_http_server/stdafx.h deleted file mode 100644 index e288832024..0000000000 --- a/contrib/epee/demo/demo_http_server/stdafx.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "targetver.h" - - -#include - - -#define BOOST_FILESYSTEM_VERSION 3 -#define ENABLE_RELEASE_LOGGING -#include "misc_log_ex.h" - - diff --git a/contrib/epee/demo/demo_http_server/targetver.h b/contrib/epee/demo/demo_http_server/targetver.h deleted file mode 100644 index 6fe8eb79e1..0000000000 --- a/contrib/epee/demo/demo_http_server/targetver.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -// The following macros define the minimum required platform. The minimum required platform -// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run -// your application. The macros work by enabling all features available on platform versions up to and -// including the version specified. - -// Modify the following defines if you have to target a platform prior to the ones specified below. -// Refer to MSDN for the latest info on corresponding values for different platforms. -#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. -#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. -#endif - diff --git a/contrib/epee/demo/demo_levin_server/demo_levin_server.cpp b/contrib/epee/demo/demo_levin_server/demo_levin_server.cpp deleted file mode 100644 index a99a1f564c..0000000000 --- a/contrib/epee/demo/demo_levin_server/demo_levin_server.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "stdafx.h" -#include "demo_levin_server.h" -#include "console_handler.h" - - -template -bool communicate(net_utils::boosted_levin_async_server& transport, int id, t_request& req, const std::string& ip, const std::string& port, bool use_async) -{ - if(use_async) - { - //IMPORTANT: do not pass local parameters from stack by reference! connect_async returns immediately, and callback will call in any thread later - transport.connect_async(ip, port, 10000, [&transport, id, req, ip, port](net_utils::connection_context_base& ctx, const boost::system::error_code& ec_) - { - if(!!ec_) - { - LOG_ERROR("Failed to connect to " << ip << ":" << port); - }else - {//connected ok! - - epee::net_utils::async_invoke_remote_command2(ctx.m_connection_id, id, req, transport.get_config_object(), [&transport, ip, port](int res_code, demo::COMMAND_EXAMPLE_1::response& rsp, net_utils::connection_context_base& ctx) - { - if(res_code < 0) - { - LOG_ERROR("Failed to invoke to " << ip << ":" << port); - }else - {//invoked ok - CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response"); - CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response"); - CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response"); - LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 async invoked ok", LOG_LEVEL_0); - } - transport.get_config_object().close(ctx.m_connection_id); - return true; - }); - LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 async invoke requested", LOG_LEVEL_0); - } - }); - }else - { - net_utils::connection_context_base ctx = AUTO_VAL_INIT(ctx); - bool r = transport.connect(ip, port, 10000, ctx); - CHECK_AND_ASSERT_MES(r, false, "failed to connect to " << ip << ":" << port); - demo::COMMAND_EXAMPLE_1::response rsp = AUTO_VAL_INIT(rsp); - LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 sync invoke requested", LOG_LEVEL_0); - r = epee::net_utils::invoke_remote_command2(ctx.m_connection_id, id, req, rsp, transport.get_config_object()); - CHECK_AND_ASSERT_MES(r, false, "failed to invoke levin request"); - CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response"); - CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response"); - CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response"); - transport.get_config_object().close(ctx.m_connection_id); - LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 sync invoked ok", LOG_LEVEL_0); - } - return true; -} - - -int main(int argc, char* argv[]) -{ - TRY_ENTRY(); - string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str()); - - - - LOG_PRINT("Demo server starting ...", LOG_LEVEL_0); - - - demo::demo_levin_server srv; - - start_default_console(&srv, "#"); - - std::string bind_param = "0.0.0.0"; - std::string port = "12345"; - - if(!srv.init(port, bind_param)) - { - LOG_ERROR("Failed to initialize srv!"); - return 1; - } - - srv.run(); - - size_t c = 1; - while (!srv.is_stop()) - { - - demo::COMMAND_EXAMPLE_1::request req; - req.sub = demo::get_test_data(); - bool r = communicate(srv.get_server(), demo::COMMAND_EXAMPLE_1::ID, req, "127.0.0.1", port, (c%2 == 0)); - misc_utils::sleep_no_w(1000); - ++c; - } - bool r = srv.wait_stop(); - CHECK_AND_ASSERT_MES(r, 1, "failed to wait server stop"); - - - srv.deinit(); - - LOG_PRINT("Demo server stoped.", LOG_LEVEL_0); - return 1; - - CATCH_ENTRY_L0("main", 1); -} - -/************************************************************************/ -/* */ -/************************************************************************/ -namespace demo -{ - bool demo_levin_server::init(const std::string& bind_port, const std::string& bind_ip) - { - m_net_server.get_config_object().m_pcommands_handler = this; - LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); - return m_net_server.init_server(bind_port, bind_ip); - } - - bool demo_levin_server::run() - { - m_stop = false; - //here you can set worker threads count - int thrds_count = 4; - m_net_server.get_config_object().m_invoke_timeout = 10000; - m_net_server.get_config_object().m_pcommands_handler = this; - - //go to loop - LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); - if(!m_net_server.run_server(thrds_count, false)) - { - LOG_ERROR("Failed to run net tcp server!"); - } - - LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); - return true; - } - - bool demo_levin_server::deinit() - { - return m_net_server.deinit_server(); - } - - bool demo_levin_server::send_stop_signal() - { - m_net_server.send_stop_signal(); - return true; - } - int demo_levin_server::handle_command_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context) - { - CHECK_AND_ASSERT_MES(arg.sub == demo::get_test_data(), false, "wrong request"); - rsp.m_success = true; - rsp.subs.push_back(arg.sub); - LOG_PRINT_GREEN("Server COMMAND_EXAMPLE_1 ok", LOG_LEVEL_0); - return 1; - } - int demo_levin_server::handle_command_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context) - { - return 1; - } - int demo_levin_server::handle_notify_1(int command, COMMAND_EXAMPLE_1::request& arg, const net_utils::connection_context_base& context) - { - return 1; - } - int demo_levin_server::handle_notify_2(int command, COMMAND_EXAMPLE_2::request& arg, const net_utils::connection_context_base& context) - { - return 1; - } -} diff --git a/contrib/epee/demo/demo_levin_server/demo_levin_server.h b/contrib/epee/demo/demo_levin_server/demo_levin_server.h deleted file mode 100644 index 5a6f68f2d4..0000000000 --- a/contrib/epee/demo/demo_levin_server/demo_levin_server.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "net/levin_server_cp2.h" -#include "transport_defs.h" -#include "storages/levin_abstract_invoke2.h" - -using namespace epee; - -namespace demo -{ - - class demo_levin_server: public levin::levin_commands_handler<> - { - public: - bool run(); - bool init(const std::string& bind_port = "11112", const std::string& bind_ip = "0.0.0.0"); - bool deinit(); - bool send_stop_signal(); - bool is_stop(){return m_stop;} - bool wait_stop(){return m_net_server.timed_wait_server_stop(100000);} - net_utils::boosted_levin_async_server& get_server(){return m_net_server;} - private: - - - CHAIN_LEVIN_INVOKE_MAP(); //move levin_commands_handler interface invoke(...) callbacks into invoke map - CHAIN_LEVIN_NOTIFY_STUB(); //move levin_commands_handler interface notify(...) callbacks into nothing - - BEGIN_INVOKE_MAP2(demo_levin_server) - HANDLE_INVOKE_T2(COMMAND_EXAMPLE_1, &demo_levin_server::handle_command_1) - HANDLE_INVOKE_T2(COMMAND_EXAMPLE_2, &demo_levin_server::handle_command_2) - HANDLE_NOTIFY_T2(COMMAND_EXAMPLE_1, &demo_levin_server::handle_notify_1) - HANDLE_NOTIFY_T2(COMMAND_EXAMPLE_2, &demo_levin_server::handle_notify_2) - END_INVOKE_MAP2() - - //----------------- commands handlers ---------------------------------------------- - int handle_command_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context); - int handle_command_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context); - int handle_notify_1(int command, COMMAND_EXAMPLE_1::request& arg, const net_utils::connection_context_base& context); - int handle_notify_2(int command, COMMAND_EXAMPLE_2::request& arg, const net_utils::connection_context_base& context); - //---------------------------------------------------------------------------------- - net_utils::boosted_levin_async_server m_net_server; - std::atomic m_stop; - - }; -} - diff --git a/contrib/epee/demo/demo_levin_server/stdafx.cpp b/contrib/epee/demo/demo_levin_server/stdafx.cpp deleted file mode 100644 index d6ea1c6f2d..0000000000 --- a/contrib/epee/demo/demo_levin_server/stdafx.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "stdafx.h" - diff --git a/contrib/epee/demo/demo_levin_server/stdafx.h b/contrib/epee/demo/demo_levin_server/stdafx.h deleted file mode 100644 index cc4558434d..0000000000 --- a/contrib/epee/demo/demo_levin_server/stdafx.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "targetver.h" - - -#include - - -#define BOOST_FILESYSTEM_VERSION 3 -#define ENABLE_RELEASE_LOGGING -#include "misc_log_ex.h" diff --git a/contrib/epee/demo/demo_levin_server/targetver.h b/contrib/epee/demo/demo_levin_server/targetver.h deleted file mode 100644 index 6fe8eb79e1..0000000000 --- a/contrib/epee/demo/demo_levin_server/targetver.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -// The following macros define the minimum required platform. The minimum required platform -// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run -// your application. The macros work by enabling all features available on platform versions up to and -// including the version specified. - -// Modify the following defines if you have to target a platform prior to the ones specified below. -// Refer to MSDN for the latest info on corresponding values for different platforms. -#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. -#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. -#endif - diff --git a/contrib/epee/demo/generate_gcc.sh b/contrib/epee/demo/generate_gcc.sh deleted file mode 100644 index fcd0a8a7e7..0000000000 --- a/contrib/epee/demo/generate_gcc.sh +++ /dev/null @@ -1,4 +0,0 @@ -mkdir build -cd build -cmake .. -#cmake -DBOOST_ROOT=/usr/local/proj/boost_1_49_0 -DBOOST_LIBRARYDIR=/usr/local/proj/boost_1_49_0/stage/lib .. diff --git a/contrib/epee/demo/generate_vc_proj.bat b/contrib/epee/demo/generate_vc_proj.bat deleted file mode 100644 index 111405981f..0000000000 --- a/contrib/epee/demo/generate_vc_proj.bat +++ /dev/null @@ -1,7 +0,0 @@ -mkdir build - -cd build - -cmake "-DBoost_USE_STATIC_LIBS=TRUE" -G "Visual Studio 11 Win64" .. -cd .. -pause \ No newline at end of file diff --git a/contrib/epee/demo/iface/transport_defs.h b/contrib/epee/demo/iface/transport_defs.h deleted file mode 100644 index 8463eb903a..0000000000 --- a/contrib/epee/demo/iface/transport_defs.h +++ /dev/null @@ -1,221 +0,0 @@ -#pragma once - -#include "serialization/keyvalue_serialization.h" -#include "storages/portable_storage_base.h" - -namespace demo -{ - - struct some_test_subdata - { - std::string m_str; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_str) - END_KV_SERIALIZE_MAP() - }; - - struct some_test_data - { - std::string m_str; - uint64_t m_uint64; - uint32_t m_uint32; - uint16_t m_uint16; - uint8_t m_uint8; - int64_t m_int64; - int32_t m_int32; - int16_t m_int16; - int8_t m_int8; - double m_double; - bool m_bool; - std::list m_list_of_str; - std::list m_list_of_uint64_t; - std::list m_list_of_uint32_t; - std::list m_list_of_uint16_t; - std::list m_list_of_uint8_t; - std::list m_list_of_int64_t; - std::list m_list_of_int32_t; - std::list m_list_of_int16_t; - std::list m_list_of_int8_t; - std::list m_list_of_double; - std::list m_list_of_bool; - some_test_subdata m_subobj; - std::list m_list_of_self; - epee::serialization::storage_entry m_storage_entry_int; - epee::serialization::storage_entry m_storage_entry_string; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_str) - KV_SERIALIZE(m_uint64) - KV_SERIALIZE(m_uint32) - KV_SERIALIZE(m_uint16) - KV_SERIALIZE(m_uint8) - KV_SERIALIZE(m_int64) - KV_SERIALIZE(m_int32) - KV_SERIALIZE(m_int16) - KV_SERIALIZE(m_int8) - KV_SERIALIZE(m_double) - KV_SERIALIZE(m_bool) - KV_SERIALIZE(m_subobj) - KV_SERIALIZE(m_list_of_str) - KV_SERIALIZE(m_list_of_uint64_t) - KV_SERIALIZE(m_list_of_uint32_t) - KV_SERIALIZE(m_list_of_uint16_t) - KV_SERIALIZE(m_list_of_uint8_t) - KV_SERIALIZE(m_list_of_int64_t) - KV_SERIALIZE(m_list_of_int32_t) - KV_SERIALIZE(m_list_of_int16_t) - KV_SERIALIZE(m_list_of_int8_t) - KV_SERIALIZE(m_list_of_double) - KV_SERIALIZE(m_list_of_bool) - KV_SERIALIZE(m_list_of_self) - KV_SERIALIZE(m_storage_entry_int) - KV_SERIALIZE(m_storage_entry_string) - END_KV_SERIALIZE_MAP() - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - struct COMMAND_EXAMPLE_1 - { - const static int ID = 1000; - - struct request - { - std::string example_string_data; - some_test_data sub; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(example_string_data) - KV_SERIALIZE(sub) - END_KV_SERIALIZE_MAP() - }; - - - struct response - { - bool m_success; - std::list subs; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_success) - KV_SERIALIZE(subs) - END_KV_SERIALIZE_MAP() - }; - }; - - - - struct COMMAND_EXAMPLE_2 - { - const static int ID = 1001; - - struct request - { - std::string example_string_data2; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(example_string_data2) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - bool m_success; - - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_success) - END_KV_SERIALIZE_MAP() - }; - }; - - - //------------------------------------------------------------------------------------- - //------------------------------------------------------------------------------------- - //in debug purpose - bool operator != (const some_test_subdata& a, const some_test_subdata& b) - { - return b.m_str != a.m_str; - } - - bool operator == (const some_test_data& a, const some_test_data& b) - { - if( b.m_str != a.m_str - || b.m_uint64 != a.m_uint64 - || b.m_uint32 != a.m_uint32 - || b.m_uint16 != a.m_uint16 - || b.m_uint8 != a.m_uint8 - || b.m_int64 != a.m_int64 - || b.m_int32 != a.m_int32 - || b.m_int16 != a.m_int16 - || b.m_int8 != a.m_int8 - || b.m_double != a.m_double - || b.m_bool != a.m_bool - || b.m_list_of_str != a.m_list_of_str - || b.m_list_of_uint64_t != a.m_list_of_uint64_t - || b.m_list_of_uint32_t != a.m_list_of_uint32_t - || b.m_list_of_uint16_t != a.m_list_of_uint16_t - || b.m_list_of_uint8_t != a.m_list_of_uint8_t - || b.m_list_of_int64_t != a.m_list_of_int64_t - || b.m_list_of_int32_t != a.m_list_of_int32_t - || b.m_list_of_int16_t != a.m_list_of_int16_t - || b.m_list_of_int8_t != a.m_list_of_int8_t - || b.m_list_of_double != a.m_list_of_double - || b.m_list_of_bool != a.m_list_of_bool - || b.m_subobj != a.m_subobj - || b.m_list_of_self != a.m_list_of_self - || b.m_storage_entry_int.which() != a.m_storage_entry_int.which() - || b.m_storage_entry_string.which() != a.m_storage_entry_string.which() - ) - return false; - return true; - } - - inline some_test_data get_test_data() - { - some_test_data s; - s.m_str = "zuzuzuzuzuz"; - s.m_uint64 = 111111111111111; - s.m_uint32 = 2222222; - s.m_uint16 = 2222; - s.m_uint8 = 22; - s.m_int64 = -111111111111111; - s.m_int32 = -2222222; - s.m_int16 = -2222; - s.m_int8 = -24; - s.m_double = 0.11111; - s.m_bool = true; - s.m_list_of_str.push_back("1112121"); - s.m_list_of_uint64_t.push_back(1111111111); - s.m_list_of_uint64_t.push_back(2222222222); - s.m_list_of_uint32_t.push_back(1111111); - s.m_list_of_uint32_t.push_back(2222222); - s.m_list_of_uint16_t.push_back(1111); - s.m_list_of_uint16_t.push_back(2222); - s.m_list_of_uint8_t.push_back(11); - s.m_list_of_uint8_t.push_back(22); - - - s.m_list_of_int64_t.push_back(-1111111111); - s.m_list_of_int64_t.push_back(-222222222); - s.m_list_of_int32_t.push_back(-1111111); - s.m_list_of_int32_t.push_back(-2222222); - s.m_list_of_int16_t.push_back(-1111); - s.m_list_of_int16_t.push_back(-2222); - s.m_list_of_int8_t.push_back(-11); - s.m_list_of_int8_t.push_back(-22); - - s.m_list_of_double.push_back(0.11111); - s.m_list_of_double.push_back(0.22222); - s.m_list_of_bool.push_back(true); - s.m_list_of_bool.push_back(false); - - s.m_subobj.m_str = "subszzzzzzzz"; - s.m_list_of_self.push_back(s); - s.m_storage_entry_int = epee::serialization::storage_entry(uint64_t(22222));; - s.m_storage_entry_string = epee::serialization::storage_entry(std::string("sdsvsdvs")); - return s; - } -} \ No newline at end of file diff --git a/contrib/epee/include/ado_db_helper.h b/contrib/epee/include/ado_db_helper.h deleted file mode 100644 index ed4e5b30f8..0000000000 --- a/contrib/epee/include/ado_db_helper.h +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _DB_ADO_HELPER_H_ -#define _DB_ADO_HELPER_H_ - -#include -#include -#include "string_coding.h" -#include "math_helper.h" -#include "file_io_utils.h" -#include "global_stream_operators.h" - - - -#define BEGIN_TRY_SECTION() try { - -#define CATCH_TRY_SECTION(ret_val) CATCH_TRY_SECTION_MESS(ret_val, "") - -#define CATCH_TRY_SECTION_MESS(ret_val, mess_where) }\ - catch(const std::exception&ex)\ - {\ - LOG_PRINT_J("DB_ERROR: " << ex.what(), LOG_LEVEL_0);\ - return ret_val;\ - }\ - catch(const _com_error& comm_err)\ - {\ - const TCHAR* pstr = comm_err.Description();\ - std::string descr = string_encoding::convert_to_ansii(pstr?pstr:TEXT(""));\ - const TCHAR* pmessage = comm_err.ErrorMessage();\ - pstr = comm_err.Source();\ - std::string source = string_encoding::convert_to_ansii(pstr?pstr:TEXT(""));\ - LOG_PRINT_J("COM_ERROR " << mess_where << ":\n\tDescriprion:" << descr << ", \n\t Message: " << string_encoding::convert_to_ansii(pmessage) << "\n\t Source: " << source, LOG_LEVEL_0);\ - return ret_val;\ - }\ - catch(...)\ - {\ - LOG_PRINT_J("..._ERROR: Unknown error.", LOG_LEVEL_0);\ - return ret_val;\ - }\ - -namespace epee -{ -namespace ado_db_helper -{ - - struct profile_entry - { - profile_entry():m_call_count(0), m_max_time(0), m_min_time(0) - {} - //std::string m_sql; - math_helper::average m_avrg; - size_t m_call_count; - DWORD m_max_time; - DWORD m_min_time; - }; - - class profiler_manager - { - public: - typedef std::map sqls_map; - profiler_manager(){} - - static bool sort_by_timing(const sqls_map::iterator& a, const sqls_map::iterator& b) - { - return a->second.m_avrg.get_avg() > b->second.m_avrg.get_avg(); - } - - bool flush_log(const std::string& path) - { - CRITICAL_REGION_BEGIN(m_sqls_lock); - std::stringstream strm; - strm << "SQL PROFILE:\r\nStatements: " << m_sqls.size() << "\r\n"; - std::list m_sorted_by_time_sqls; - for(std::map::iterator it = m_sqls.begin();it!=m_sqls.end();it++) - m_sorted_by_time_sqls.push_back(it); - - m_sorted_by_time_sqls.sort(sort_by_timing); - - for(std::list::iterator it = m_sorted_by_time_sqls.begin();it!=m_sorted_by_time_sqls.end();it++) - { - strm << "---------------------------------------------------------------------------------------------------------\r\nSQL: " << (*it)->first << "\r\n"; - strm << "\tavrg: " << (*it)->second.m_avrg.get_avg() << "\r\n\tmax: " << (*it)->second.m_max_time << "\r\n\tmin: " << (*it)->second.m_min_time << "\r\n\tcount: " << (*it)->second.m_call_count << "\r\n"; - } - - return file_io_utils::save_string_to_file(path.c_str(), strm.str()); - CRITICAL_REGION_END(); - } - - bool push_entry(const std::string sql, DWORD time) - { - CRITICAL_REGION_BEGIN(m_sqls_lock); - profile_entry& entry_ref = m_sqls[sql]; - entry_ref.m_avrg.push(time); - entry_ref.m_call_count++; - if(time > entry_ref.m_max_time) entry_ref.m_max_time = time; - if(time < entry_ref.m_min_time || entry_ref.m_min_time == 0) entry_ref.m_min_time = time; - CRITICAL_REGION_END(); - return true; - } - - bool get_entry_avarege(const std::string sql, DWORD& time) - { - CRITICAL_REGION_BEGIN(m_sqls_lock); - sqls_map::iterator it = m_sqls.find(sql); - if(it==m_sqls.end()) - return false; - - time = static_cast(it->second.m_avrg.get_avg()); - CRITICAL_REGION_END(); - return true; - } - - private: - - sqls_map m_sqls; - critical_section m_sqls_lock; - }; -inline - profiler_manager* get_set_profiler(bool need_to_set = false, profiler_manager** pprofiler = NULL) - { - static profiler_manager* pmanager = NULL; - if(need_to_set) - pmanager = *pprofiler; - //else - // *pprofiler = pmanager; - - return pmanager; - } -inline - bool init() // INIT and DEINIT are NOT THREAD SAFE operations, CALL it BEFOR u start using this wrapper. - { - profiler_manager* pmanager = new profiler_manager(); - get_set_profiler(true, &pmanager); - return true; - } -inline - bool deinit() - { - profiler_manager* pmanager = get_set_profiler(); - //get_set_profiler(false, &pmanager); - if(pmanager) - delete pmanager; - return true; - } - inline bool push_timing(const std::string sql, DWORD time) - { - profiler_manager* pmanager = get_set_profiler(); - //get_set_profiler(false, &pmanager); - if(pmanager) - return pmanager->push_entry(sql, time); - return true; - } - - inline bool flush_profiler(const std::string path) - { - profiler_manager* pmanager = get_set_profiler(); - //get_set_profiler(false, &pmanager); - if(pmanager) - return pmanager->flush_log(path); - return true; - } - - class timing_guard - { - DWORD m_start_time; - std::string m_sql; - - public: - timing_guard(const std::string& sql) - { - m_start_time = ::GetTickCount(); - m_sql = sql; - } - - ~timing_guard() - { - DWORD timing = ::GetTickCount() - m_start_time; - push_timing(m_sql, timing); - } - }; -#define PROFILE_SQL(sql) timing_guard local_timing(sql) - - - typedef std::vector > table; - - inline bool add_parametr(ADODB::_CommandPtr cmd, const std::string& parametr) - { - _variant_t param(parametr.c_str()); - ADODB::ADO_LONGPTR size = sizeof(parametr); - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("", ADODB::adVarChar, ADODB::adParamInput, static_cast(parametr.size()+1), param); - cmd->Parameters->Append(param_obj); - return true; - } - - inline bool add_parametr(ADODB::_CommandPtr cmd, const std::wstring& parametr) - { - _variant_t param(parametr.c_str()); - ADODB::ADO_LONGPTR size = sizeof(parametr); - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("", ADODB::adVarWChar, ADODB::adParamInput, static_cast(parametr.size()+2), param); - cmd->Parameters->Append(param_obj); - return true; - } - - inline bool add_parametr(ADODB::_CommandPtr cmd, const __int64 parametr) - { - _variant_t param(parametr); - ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adBigInt, ADODB::adParamInput, static_cast(size), param); - cmd->Parameters->Append(param_obj); - return true; - } - - inline bool add_parametr(ADODB::_CommandPtr cmd, const unsigned __int64 parametr) - { - _variant_t param(parametr); - ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adUnsignedBigInt, ADODB::adParamInput, static_cast(size), param); - cmd->Parameters->Append(param_obj); - return true; - } - - - inline bool add_parametr(ADODB::_CommandPtr cmd, const int parametr) - { - _variant_t param(parametr); - ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adInteger, ADODB::adParamInput, static_cast(size), param); - cmd->Parameters->Append(param_obj); - return true; - } - - inline bool add_parametr(ADODB::_CommandPtr cmd, const unsigned int parametr) - { - _variant_t param(parametr); - ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adUnsignedInt, ADODB::adParamInput, static_cast(size), param); - cmd->Parameters->Append(param_obj); - return true; - } - - inline bool add_parametr(ADODB::_CommandPtr cmd, float parametr) - { - _variant_t param; - param.ChangeType(VT_R4); - param.fltVal = parametr; - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adSingle, ADODB::adParamInput, static_cast(sizeof(float)), param); - cmd->Parameters->Append(param_obj); - return true; - } - - inline bool add_parametr(ADODB::_CommandPtr cmd, bool parametr) - { - _variant_t param; - param = parametr; - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adBoolean, ADODB::adParamInput, sizeof(parametr), param); - cmd->Parameters->Append(param_obj); - return true; - } - - - inline bool add_parametr(ADODB::_CommandPtr cmd, _variant_t parametr) - { - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDBTimeStamp, ADODB::adParamInput, sizeof(parametr), parametr); - cmd->Parameters->Append(param_obj); - return true; - } - - - inline bool add_parametr_as_double(ADODB::_CommandPtr cmd, const DATE parametr) - { - _variant_t param; - param.ChangeType(VT_R8); - param.dblVal = parametr; - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDouble, ADODB::adParamInput, sizeof(float), param); - cmd->Parameters->Append(param_obj); - return true; - } - - template - inline bool add_parametr(ADODB::_CommandPtr cmd, const std::list params) - { - for(std::list::const_iterator it = params.begin(); it!=params.end(); it++) - if(!add_parametr(cmd, *it)) - return false; - return true; - } - - /* - inline bool add_parametr(ADODB::_CommandPtr cmd, const size_t parametr) - { - _variant_t param; - param.ChangeType(VT_I4); - param.intVal = parametr; - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adInteger, ADODB::adParamInput, sizeof(parametr), param); - cmd->Parameters->Append(param_obj); - return true; - }*/ - - - inline bool add_parametr(ADODB::_CommandPtr cmd, const DATE parametr) - { - /*_variant_t param; - param.ChangeType(VT_R8); - param.dblVal = parametr; - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDouble, ADODB::adParamInput, sizeof(float), param); - cmd->Parameters->Append(param_obj);*/ - - _variant_t param; - param.ChangeType(VT_DATE); - param.date = parametr; - ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDBDate, ADODB::adParamInput, sizeof(parametr), param); - cmd->Parameters->Append(param_obj); - - return true; - } - - - inline bool execute_helper(ADODB::_CommandPtr cmd, _variant_t* pcount_processed = NULL) - { - //BEGIN_TRY_SECTION(); - - cmd->Execute(pcount_processed, NULL, ADODB::adExecuteNoRecords); - - - //CATCH_TRY_SECTION(false); - - return true; - } - - - inline bool select_helper(ADODB::_CommandPtr cmd, table& result_vector) - { - result_vector.clear(); - //BEGIN_TRY_SECTION(); - - ADODB::_RecordsetPtr precordset = cmd->Execute(NULL, NULL, NULL); - if(!precordset) - { - LOG_ERROR("DB_ERROR: cmd->Execute returned NULL!!!"); - return false; - } - - //if(precordset->EndOfFile == EOF) - //{ - // return true; - //} - /*try - { - if(precordset->MoveFirst()!= S_OK) - { - LOG_ERROR("DB_ERROR: Filed to move first!!!"); - return false; - } - } - catch (...) - { - return true; - }*/ - - size_t current_record_index = 0; - while(precordset->EndOfFile != EOF) - { - result_vector.push_back(table::value_type()); - size_t fields_count = precordset->Fields->Count; - result_vector[current_record_index].resize(fields_count); - for(size_t current_field_index = 0; current_field_index < fields_count; current_field_index++) - { - _variant_t var; - var.ChangeType(VT_I2); - var.intVal = static_cast(current_field_index); - result_vector[current_record_index][current_field_index] = precordset->Fields->GetItem(var)->Value; - } - precordset->MoveNext(); - current_record_index++; - } - //CATCH_TRY_SECTION(false); - return true; - } - - - template - struct adapter_zero - { - - }; - - template - struct adapter_single - { - TParam1 tparam1; - }; - template - struct adapter_double - { - TParam1 tparam1; - TParam2 tparam2; - }; - - - template - struct adapter_triple - { - TParam1 tparam1; - TParam2 tparam2; - TParam3 tparam3; - }; - - template - struct adapter_quad - { - TParam1 tparam1; - TParam2 tparam2; - TParam3 tparam3; - TParam4 tparam4; - }; - - template - struct adapter_quanto - { - TParam1 tparam1; - TParam2 tparam2; - TParam3 tparam3; - TParam4 tparam4; - TParam5 tparam5; - }; - - template - struct adapter_sixto - { - TParam1 tparam1; - TParam2 tparam2; - TParam3 tparam3; - TParam4 tparam4; - TParam5 tparam5; - TParam6 tparam6; - }; - - template - struct adapter_sevento - { - TParam1 tparam1; - TParam2 tparam2; - TParam3 tparam3; - TParam4 tparam4; - TParam5 tparam5; - TParam6 tparam6; - TParam7 tparam7; - }; - - template - struct adapter_nine - { - TParam1 tparam1; - TParam2 tparam2; - TParam3 tparam3; - TParam4 tparam4; - TParam5 tparam5; - TParam6 tparam6; - TParam7 tparam7; - TParam8 tparam8; - TParam9 tparam9; - }; - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_zero& params) - { - return true; - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_single& params) - { - return add_parametr(cmd, params.tparam1); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_double& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - return add_parametr(cmd, params.tparam2); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_triple& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - if(!add_parametr(cmd, params.tparam2)) return false; - return add_parametr(cmd, params.tparam3); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_quad& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - if(!add_parametr(cmd, params.tparam2)) return false; - if(!add_parametr(cmd, params.tparam3)) return false; - return add_parametr(cmd, params.tparam4); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_quanto& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - if(!add_parametr(cmd, params.tparam2)) return false; - if(!add_parametr(cmd, params.tparam3)) return false; - if(!add_parametr(cmd, params.tparam4)) return false; - return add_parametr(cmd, params.tparam5); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_sixto& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - if(!add_parametr(cmd, params.tparam2)) return false; - if(!add_parametr(cmd, params.tparam3)) return false; - if(!add_parametr(cmd, params.tparam4)) return false; - if(!add_parametr(cmd, params.tparam5)) return false; - return add_parametr(cmd, params.tparam6); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_sevento& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - if(!add_parametr(cmd, params.tparam2)) return false; - if(!add_parametr(cmd, params.tparam3)) return false; - if(!add_parametr(cmd, params.tparam4)) return false; - if(!add_parametr(cmd, params.tparam5)) return false; - if(!add_parametr(cmd, params.tparam6)) return false; - return add_parametr(cmd, params.tparam7); - } - - template - bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_nine& params) - { - if(!add_parametr(cmd, params.tparam1)) return false; - if(!add_parametr(cmd, params.tparam2)) return false; - if(!add_parametr(cmd, params.tparam3)) return false; - if(!add_parametr(cmd, params.tparam4)) return false; - if(!add_parametr(cmd, params.tparam5)) return false; - if(!add_parametr(cmd, params.tparam6)) return false; - if(!add_parametr(cmd, params.tparam7)) return false; - if(!add_parametr(cmd, params.tparam8)) return false; - return add_parametr(cmd, params.tparam9); - } - - template - std::string print_parameters_multi(const adapter_sevento& params) - { - std::stringstream strm; - strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6 << ", " << params.tparam7; - return strm.str(); - } - - template - std::string print_parameters_multi(const adapter_nine& params) - { - std::stringstream strm; - strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6 << ", " << params.tparam7 << ", " << params.tparam8 << ", " << params.tparam9; - return strm.str(); - } - - template - std::string print_parameters_multi(const adapter_sixto& params) - { - std::stringstream strm; - strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6; - return strm.str(); - } - - template - std::string print_parameters_multi(const adapter_quanto& params) - { - std::stringstream strm; - strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5; - return strm.str(); - } - - - template - std::string print_parameters_multi(const adapter_quad& params) - { - std::stringstream strm; - strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4; - return strm.str(); - } - - template - std::string print_parameters_multi(const adapter_triple& params) - { - std::stringstream strm; - strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3; - return strm.str(); - } - - template - std::string get_str_param(const TParam& prm) - { - std::stringstream strm; - strm << prm; - return strm.str(); - } - - template - std::string get_str_param(const std::list& prm_lst) - { - std::stringstream strm; - for(std::list::const_iterator it = prm_lst.begin();it!=prm_lst.end();it++) - strm << get_str_param(*it) << ", "; - return strm.str(); - } - - - template - std::string print_parameters_multi(const adapter_double& params) - { - std::stringstream strm; - strm << get_str_param(params.tparam1) << ", " << get_str_param(params.tparam2); - return strm.str(); - } - - template - std::string print_parameters_multi(const adapter_single& params) - { - std::stringstream strm; - strm << get_str_param(params.tparam1); - return strm.str(); - } - - template - std::string print_parameters_multi(const adapter_zero& params) - { - std::stringstream strm; - strm << "(no parametrs)"; - return strm.str(); - } - - - template - bool execute_helper_multiparam(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, _variant_t* pcount_processed = NULL) - { - PROFILE_SQL(sql_statment); - bool res = false; - BEGIN_TRY_SECTION(); - - ADODB::_CommandPtr cmd; - cmd.CreateInstance(__uuidof(ADODB::Command)); - cmd->CommandText = _bstr_t(sql_statment.c_str()); - - if(!add_parametrs_multi(cmd, parametrs)) - return false; - - cmd->ActiveConnection = pconnection; - res = execute_helper(cmd, pcount_processed); - - CATCH_TRY_SECTION_MESS(false, "while statment: " << sql_statment << " [params]: " << print_parameters_multi(parametrs)); - return res; - } - - - template - inline - bool select_helper_multiparam(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, table& result_vector) - { - PROFILE_SQL(sql_statment); - bool res = false; - BEGIN_TRY_SECTION(); - ADODB::_CommandPtr cmd; - cmd.CreateInstance(__uuidof(ADODB::Command)); - cmd->CommandText = _bstr_t(sql_statment.c_str()); - - - if(!add_parametrs_multi(cmd, parametrs)) - return false; - - cmd->ActiveConnection = pconnection; - res = select_helper(cmd, result_vector); - CATCH_TRY_SECTION_MESS(false, "while statment: " << sql_statment << " [params]: " << print_parameters_multi(parametrs)); - return res; - } - - - template - inline - bool select_helper_param_container(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, table& result_vector) - { - PROFILE_SQL(sql_statment); - bool res = false; - BEGIN_TRY_SECTION(); - ADODB::_CommandPtr cmd; - cmd.CreateInstance(__uuidof(ADODB::Command)); - cmd->CommandText = _bstr_t(sql_statment.c_str()); - - - for(TParams::const_iterator it = parametrs.begin(); it!=parametrs.end(); it++) - { - add_parametr(cmd, *it); - } - - cmd->ActiveConnection = pconnection; - res = select_helper(cmd, result_vector); - - CATCH_TRY_SECTION(false); - return res; - } - - - inline - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, _variant_t* pvt = NULL) - { - adapter_zero params; - return execute_helper_multiparam(pconnection, sql_statment, params, pvt); - } - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam& parametr) - { - adapter_single params; - params.tparam1 = parametr; - return execute_helper_multiparam(pconnection, sql_statment, params); - } - - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2) - { - adapter_double params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - return execute_helper_multiparam(pconnection, sql_statment, params); - - } - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3) - { - adapter_triple params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - return execute_helper_multiparam(pconnection, sql_statment, params); - } - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4) - { - adapter_quad params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - return execute_helper_multiparam(pconnection, sql_statment, params); - } - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5) - { - adapter_quanto params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - return execute_helper_multiparam(pconnection, sql_statment, params); - } - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5, const TParam6& parametr6) - { - adapter_sixto params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - params.tparam6 = parametr6; - return execute_helper_multiparam(pconnection, sql_statment, params); - } - - - template - bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5, const TParam6& parametr6, const TParam7& parametr7) - { - adapter_sevento params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - params.tparam6 = parametr6; - params.tparam7 = parametr7; - return execute_helper_multiparam(pconnection, sql_statment, params); - } - - inline - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, table& result_vector) - { - adapter_zero params; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam& parametr, table& result_vector) - { - adapter_single params; - params.tparam1 = parametr; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, table& result_vector) - { - adapter_double params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - - } - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, table& result_vector) - { - adapter_triple params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - - } - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, table& result_vector) - { - adapter_quad params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, table& result_vector) - { - adapter_quanto params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, table& result_vector) - { - adapter_sixto params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - params.tparam6 = parametr6; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, const TParam7 parametr7, table& result_vector) - { - adapter_sevento params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - params.tparam6 = parametr6; - params.tparam7 = parametr7; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - template - bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, const TParam7 parametr7,const TParam8 parametr8,const TParam9 parametr9, table& result_vector) - { - adapter_nine params; - params.tparam1 = parametr1; - params.tparam2 = parametr2; - params.tparam3 = parametr3; - params.tparam4 = parametr4; - params.tparam5 = parametr5; - params.tparam6 = parametr6; - params.tparam7 = parametr7; - params.tparam8 = parametr8; - params.tparam9 = parametr9; - return select_helper_multiparam(pconnection, sql_statment, params, result_vector); - } - - - - - /************************************************************************/ - /* */ - /************************************************************************/ - - class per_thread_connection_pool - { - public: - bool init(const std::string& connection_string, const std::string& login, const std::string& pass) - { - m_connection_string = connection_string; - m_login = login; - m_password = pass; - if(!get_db_connection().GetInterfacePtr()) - return false; - - return true; - } - - ADODB::_ConnectionPtr& get_db_connection() - { - - //soci::session - - m_db_connections_lock.lock(); - boost::shared_ptr& conn_ptr = m_db_connections[::GetCurrentThreadId()]; - m_db_connections_lock.unlock(); - if(!conn_ptr.get()) - { - conn_ptr.reset(new ADODB::_ConnectionPtr()); - ADODB::_ConnectionPtr& conn = *conn_ptr.get(); - //init new connection - - BEGIN_TRY_SECTION(); - //_bstr_t str = _bstr_t("Provider=SQLOLEDB;Data Source=SRV1;Integrated Security=SSPI;Initial Catalog=dispatcher;"); - - if(S_OK != conn.CreateInstance(__uuidof(ADODB::Connection))) - { - LOG_ERROR("Failed to Create, instance, was CoInitialize called ???!"); - return conn; - } - - HRESULT res = conn->Open(_bstr_t(m_connection_string.c_str()), _bstr_t(m_login.c_str()), _bstr_t(m_password.c_str()), NULL); - if(res != S_OK) - { - LOG_ERROR("Failed to connect do DB, connection str:" << m_connection_string); - return conn; - } - CATCH_TRY_SECTION_MESS(conn, "while creating another connection"); - LOG_PRINT("New DB Connection added for threadid=" << ::GetCurrentThreadId(), LOG_LEVEL_0); - ado_db_helper::execute_helper(conn, "set enable_seqscan=false;"); - return conn; - } - - return *conn_ptr.get(); - } - - //---------------------------------------------------------------------------------------------- - bool check_status() - { - ADODB::_ConnectionPtr& rconn = get_db_connection(); - if(!ado_db_helper::execute_helper(rconn, "SET CLIENT_ENCODING TO 'SQL_ASCII'")) - { - - try{ - HRESULT res = rconn->Close(); - } - catch(...) - { - - }; - BEGIN_TRY_SECTION(); - - HRESULT res = rconn->Open(_bstr_t(m_connection_string.c_str()), _bstr_t(m_login.c_str()), _bstr_t(m_password.c_str()), NULL); - if(res != S_OK) - { - LOG_PRINT("Failed to restore connection to local AI DB", LOG_LEVEL_1); - return false; - } - CATCH_TRY_SECTION(false); - } - - return true; - } - - protected: - private: - std::map > m_db_connections; - critical_section m_db_connections_lock; - std::string m_connection_string; - std::string m_login; - std::string m_password; - }; - - - template - bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, t_conn& c) - { - ado_db_helper::adapter_single params; - params.tparam1 = parametr_1; - return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); - } - - - template - bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, t_conn& c) - { - ado_db_helper::adapter_double params; - params.tparam1 = parametr_1; - params.tparam2 = parametr_2; - return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); - } - - - template - bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, TParam3 parametr_3, t_conn& c) - { - ado_db_helper::adapter_triple params; - params.tparam1 = parametr_1; - params.tparam2 = parametr_2; - params.tparam3 = parametr_3; - return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); - } - - template - bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, TParam3 parametr_3, TParam4 parametr_4, t_conn& c) - { - ado_db_helper::adapter_quad params; - params.tparam1 = parametr_1; - params.tparam2 = parametr_2; - params.tparam3 = parametr_3; - params.tparam4 = parametr_4; - return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); - } - - template - bool find_or_add_t_multiparametred(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParams params, t_conn& c) - { - - //CHECK_CONNECTION(false); - - new_object_added = false; - ado_db_helper::table result_table; - - bool res = select_helper_multiparam(c.get_db_connection(), sql_select_statment, params, result_table); - if(!result_table.size()) - { - res = select_helper_multiparam(c.get_db_connection(), sql_insert_statment, params, result_table); - if(!res || !result_table.size()) - { - //last time try to select - res = select_helper_multiparam(c.get_db_connection(), sql_select_statment, params, result_table); - CHECK_AND_ASSERT_MES(res, false, "Failed to execute statment: " << sql_select_statment); - CHECK_AND_ASSERT_MES(result_table.size(), false, "No records returned from statment: " << sql_select_statment); - }else - { - new_object_added = true; - } - } - - BEGIN_TRY_SECTION() - id = result_table[0][0]; - CATCH_TRY_SECTION_MESS(false, "while converting returned value [find_or_add_t_multiparametred()]"); - - return true; - } - -} -} -#endif //!_DB_HELPER_H_ diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h deleted file mode 100644 index 39bd0ae491..0000000000 --- a/contrib/epee/include/console_handler.h +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#include -#include - -#include "include_base_utils.h" -#include "string_tools.h" - -namespace epee -{ - class async_stdin_reader - { - public: - async_stdin_reader() - : m_run(true) - , m_has_read_request(false) - , m_read_status(state_init) - { - m_reader_thread = std::thread(std::bind(&async_stdin_reader::reader_thread_func, this)); - } - - ~async_stdin_reader() - { - stop(); - } - - // Not thread safe. Only one thread can call this method at once. - bool get_line(std::string& line) - { - if (!start_read()) - return false; - - std::unique_lock lock(m_response_mutex); - while (state_init == m_read_status) - { - m_response_cv.wait(lock); - } - - bool res = false; - if (state_success == m_read_status) - { - line = m_line; - res = true; - } - - m_read_status = state_init; - - return res; - } - - void stop() - { - if (m_run) - { - m_run.store(false, std::memory_order_relaxed); - -#if defined(WIN32) - ::CloseHandle(::GetStdHandle(STD_INPUT_HANDLE)); -#endif - - m_request_cv.notify_one(); - m_reader_thread.join(); - } - } - - private: - bool start_read() - { - std::unique_lock lock(m_request_mutex); - if (!m_run.load(std::memory_order_relaxed) || m_has_read_request) - return false; - - m_has_read_request = true; - m_request_cv.notify_one(); - return true; - } - - bool wait_read() - { - std::unique_lock lock(m_request_mutex); - while (m_run.load(std::memory_order_relaxed) && !m_has_read_request) - { - m_request_cv.wait(lock); - } - - if (m_has_read_request) - { - m_has_read_request = false; - return true; - } - - return false; - } - - bool wait_stdin_data() - { -#if !defined(WIN32) - int stdin_fileno = ::fileno(stdin); - - while (m_run.load(std::memory_order_relaxed)) - { - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(stdin_fileno, &read_set); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100 * 1000; - - int retval = ::select(stdin_fileno + 1, &read_set, NULL, NULL, &tv); - if (retval < 0) - return false; - else if (0 < retval) - return true; - } -#endif - - return true; - } - - void reader_thread_func() - { - while (true) - { - if (!wait_read()) - break; - - std::string line; - bool read_ok = true; - if (wait_stdin_data()) - { - if (m_run.load(std::memory_order_relaxed)) - { - std::getline(std::cin, line); - read_ok = !std::cin.eof() && !std::cin.fail(); - } - } - else - { - read_ok = false; - } - - { - std::unique_lock lock(m_response_mutex); - if (m_run.load(std::memory_order_relaxed)) - { - m_line = std::move(line); - m_read_status = read_ok ? state_success : state_error; - } - else - { - m_read_status = state_cancelled; - } - m_response_cv.notify_one(); - } - } - } - - enum t_state - { - state_init, - state_success, - state_error, - state_cancelled - }; - - private: - std::thread m_reader_thread; - std::atomic m_run; - - std::string m_line; - bool m_has_read_request; - t_state m_read_status; - - std::mutex m_request_mutex; - std::mutex m_response_mutex; - std::condition_variable m_request_cv; - std::condition_variable m_response_cv; - }; - - - template - bool empty_commands_handler(t_server* psrv, const std::string& command) - { - return true; - } - - - class async_console_handler - { - public: - async_console_handler() - { - } - - template - bool run(t_server* psrv, chain_handler ch_handler, const std::string& prompt = "#", const std::string& usage = "") - { - return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(psrv, cmd); }, [&] { psrv->send_stop_signal(); }); - } - - template - bool run(chain_handler ch_handler, const std::string& prompt = "#", const std::string& usage = "") - { - return run(prompt, usage, [&](const std::string& cmd) { return ch_handler(cmd); }, [] { }); - } - - void stop() - { - m_stdin_reader.stop(); - } - - private: - template - bool run(const std::string& prompt, const std::string& usage, const t_cmd_handler& cmd_handler, const t_exit_handler& exit_handler) - { - TRY_ENTRY(); - bool continue_handle = true; - while(continue_handle) - { - if (!prompt.empty()) - { - epee::log_space::set_console_color(epee::log_space::console_color_yellow, true); - std::cout << prompt; - if (' ' != prompt.back()) - std::cout << ' '; - epee::log_space::reset_console_color(); - std::cout.flush(); - } - - std::string command; - if(!m_stdin_reader.get_line(command)) - { - LOG_PRINT("Failed to read line. Stopping...", LOG_LEVEL_0); - continue_handle = false; - break; - } - string_tools::trim(command); - - LOG_PRINT_L2("Read command: " << command); - if(0 == command.compare("exit") || 0 == command.compare("q")) - { - continue_handle = false; - }else if (command.empty()) - { - continue; - } - else if(cmd_handler(command)) - { - continue; - } else - { - std::cout << "unknown command: " << command << std::endl; - std::cout << usage; - } - } - exit_handler(); - return true; - CATCH_ENTRY_L0("console_handler", false); - } - - private: - async_stdin_reader m_stdin_reader; - }; - - - template - bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") - { - std::shared_ptr console_handler = std::make_shared(); - std::thread([=](){console_handler->run(ptsrv, handlr, prompt, usage);}).detach(); - return true; - } - - template - bool start_default_console(t_server* ptsrv, const std::string& prompt, const std::string& usage = "") - { - return start_default_console(ptsrv, empty_commands_handler, prompt, usage); - } - - template - bool no_srv_param_adapter(t_server* ptsrv, const std::string& cmd, t_handler handlr) - { - return handlr(cmd); - } - - template - bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") - { - async_console_handler console_handler; - return console_handler.run(ptsrv, std::bind(no_srv_param_adapter, std::placeholders::_1, std::placeholders::_2, handlr), prompt, usage); - } - - template - bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") - { - std::thread( std::bind(run_default_console_handler_no_srv_param, ptsrv, handlr, prompt, usage) ); - return true; - } - - /************************************************************************/ - /* */ - /************************************************************************/ - class console_handlers_binder - { - typedef std::function &)> console_command_handler; - typedef std::map > command_handlers_map; - std::unique_ptr m_console_thread; - command_handlers_map m_command_handlers; - async_console_handler m_console_handler; - public: - std::string get_usage() - { - std::stringstream ss; - size_t max_command_len = 0; - for(auto& x:m_command_handlers) - if(x.first.size() > max_command_len) - max_command_len = x.first.size(); - - for(auto& x:m_command_handlers) - { - ss.width(max_command_len + 3); - ss << std::left << x.first << x.second.second << ENDL; - } - return ss.str(); - } - void set_handler(const std::string& cmd, const console_command_handler& hndlr, const std::string& usage = "") - { - command_handlers_map::mapped_type & vt = m_command_handlers[cmd]; - vt.first = hndlr; - vt.second = usage; - } - bool process_command_vec(const std::vector& cmd) - { - if(!cmd.size()) - return false; - auto it = m_command_handlers.find(cmd.front()); - if(it == m_command_handlers.end()) - return false; - std::vector cmd_local(cmd.begin()+1, cmd.end()); - return it->second.first(cmd_local); - } - - bool process_command_str(const std::string& cmd) - { - std::vector cmd_v; - boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on); - return process_command_vec(cmd_v); - } - - bool start_handling(const std::string& prompt, const std::string& usage_string = "") - { - m_console_thread.reset(new std::thread(std::bind(&console_handlers_binder::run_handling, this, prompt, usage_string))); - m_console_thread->detach(); - return true; - } - - void stop_handling() - { - m_console_handler.stop(); - } - - bool run_handling(const std::string& prompt, const std::string& usage_string) - { - return m_console_handler.run(std::bind(&console_handlers_binder::process_command_str, this, std::placeholders::_1), prompt, usage_string); - } - }; - - /* work around because of broken boost bind */ - template - class srv_console_handlers_binder: public console_handlers_binder - { - bool process_command_str(t_server* /*psrv*/, const std::string& cmd) - { - return console_handlers_binder::process_command_str(cmd); - } - public: - bool start_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string = "") - { - std::thread(std::bind(&srv_console_handlers_binder::run_handling, this, psrv, prompt, usage_string)).detach(); - return true; - } - - bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string) - { - return m_console_handler.run(psrv, std::bind(&srv_console_handlers_binder::process_command_str, this, - std::placeholders::_1, std::placeholders::_2), prompt, usage_string); - } - - void stop_handling() - { - m_console_handler.stop(); - } - - private: - async_console_handler m_console_handler; - }; -} diff --git a/contrib/epee/include/copyable_atomic.h b/contrib/epee/include/copyable_atomic.h deleted file mode 100644 index 6b5691ab11..0000000000 --- a/contrib/epee/include/copyable_atomic.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -namespace epee -{ - class copyable_atomic: public std::atomic - { - public: - copyable_atomic() - {}; - copyable_atomic(const copyable_atomic& a):std::atomic(a.load()) - {} - copyable_atomic& operator= (const copyable_atomic& a) - { - store(a.load()); - return *this; - } - uint32_t operator++() - { - return std::atomic::operator++(); - } - uint32_t operator++(int fake) - { - return std::atomic::operator++(fake); - } - }; -} \ No newline at end of file diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h deleted file mode 100644 index 01d70db3ef..0000000000 --- a/contrib/epee/include/file_io_utils.h +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _FILE_IO_UTILS_H_ -#define _FILE_IO_UTILS_H_ - -#include -#include -#include - - -#ifndef MAKE64 - #define MAKE64(low,high) ((__int64)(((DWORD)(low)) | ((__int64)((DWORD)(high))) << 32)) -#endif - -#ifdef WINDOWS_PLATFORM -#include -#include -#include -#include - -#endif - - - -namespace epee -{ -namespace file_io_utils -{ -#ifdef WINDOWS_PLATFORM - - inline - std::string get_temp_file_name_a() - { - std::string str_result; - char sz_temp[MAX_PATH*2] = {0}; - if(!::GetTempPathA( sizeof( sz_temp ), sz_temp )) - return str_result; - - char sz_temp_file[MAX_PATH*2] = {0}; - if(!::GetTempFileNameA( sz_temp, "mail", 0, sz_temp_file)) - return str_result; - sz_temp_file[sizeof(sz_temp_file)-1] = 0; //be happy! - str_result = sz_temp_file; - return str_result; - } - - -#ifdef BOOST_LEXICAL_CAST_INCLUDED - inline - bool get_not_used_filename(const std::string& folder, std::string& result_name) - { - DWORD folder_attr = ::GetFileAttributesA(folder.c_str()); - if(folder_attr == INVALID_FILE_ATTRIBUTES) - return false; - if(!(folder_attr&FILE_ATTRIBUTE_DIRECTORY)) - return false; - - - std::string base_name = folder + "\\tmp"; - std::string tmp_name; - bool name_found = false; - int current_index = 0; - tmp_name = base_name + boost::lexical_cast(current_index) + ".tmp"; - while(!name_found) - { - if(INVALID_FILE_ATTRIBUTES == ::GetFileAttributesA(tmp_name.c_str())) - name_found = true; - else - { - current_index++; - tmp_name = base_name + boost::lexical_cast(current_index) + ".tmp"; - } - } - result_name = tmp_name; - return true; - } -#endif - - inline - std::string get_temp_folder_a() - { - std::string str_result; - char sz_temp[MAX_PATH*2] = {0}; - if(!::GetTempPathA( sizeof( sz_temp ), sz_temp )) - return str_result; - sz_temp[(sizeof(sz_temp)/sizeof(sz_temp[0])) -1] = 0; - str_result = sz_temp; - return str_result; - } - - std::string convert_from_device_path_to_standart(const std::string& path) - { - - - STRSAFE_LPSTR pszFilename = (STRSAFE_LPSTR)path.c_str(); - - // Translate path with device name to drive letters. - char szTemp[4000] = {0}; - - if (::GetLogicalDriveStringsA(sizeof(szTemp)-1, szTemp)) - { - char szName[MAX_PATH]; - char szDrive[3] = " :"; - BOOL bFound = FALSE; - char* p = szTemp; - - do - { - // Copy the drive letter to the template string - *szDrive = *p; - - // Look up each device name - if (::QueryDosDeviceA(szDrive, szName, sizeof(szName))) - { - UINT uNameLen = strlen(szName); - - if (uNameLen < MAX_PATH) - { - bFound = _mbsnbicmp((const unsigned char*)pszFilename, (const unsigned char*)szName, - uNameLen) == 0; - - if (bFound) - { - // Reconstruct pszFilename using szTempFile - // Replace device path with DOS path - char szTempFile[MAX_PATH] = {0}; - StringCchPrintfA(szTempFile, - MAX_PATH, - "%s%s", - szDrive, - pszFilename+uNameLen); - return szTempFile; - //::StringCchCopyNA(pszFilename, MAX_PATH+1, szTempFile, strlen(szTempFile)); - } - } - } - - // Go to the next NULL character. - while (*p++); - } while (!bFound && *p); // end of string - } - - return ""; - } - - inline - std::string get_process_path_by_pid(DWORD pid) - { - std::string res; - - HANDLE hprocess = 0; - if( hprocess = ::OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pid) ) - { - char buff[MAX_PATH]= {0}; - if(!::GetModuleFileNameExA( hprocess, 0, buff, MAX_PATH - 1 )) - res = "Unknown_b"; - else - { - buff[MAX_PATH - 1]=0; //be happy! - res = buff; - std::string::size_type a = res.rfind( '\\' ); - if ( a != std::string::npos ) - res.erase( 0, a+1); - - } - ::CloseHandle( hprocess ); - }else - res = "Unknown_a"; - - return res; - } - - - - - - inline - std::wstring get_temp_file_name_w() - { - std::wstring str_result; - wchar_t sz_temp[MAX_PATH*2] = {0}; - if(!::GetTempPathW( sizeof(sz_temp)/sizeof(sz_temp[0]), sz_temp )) - return str_result; - - wchar_t sz_temp_file[MAX_PATH+1] = {0}; - if(!::GetTempFileNameW( sz_temp, L"mail", 0, sz_temp_file)) - return str_result; - - sz_temp_file[(sizeof(sz_temp_file)/sizeof(sz_temp_file[0]))-1] = 0; //be happy! - str_result = sz_temp_file; - return str_result; - } -#endif - inline - bool is_file_exist(const std::string& path) - { - boost::filesystem::path p(path); - return boost::filesystem::exists(p); - } - - /* - inline - bool save_string_to_handle(HANDLE hfile, const std::string& str) - { - - - - if( INVALID_HANDLE_VALUE != hfile ) - { - DWORD dw; - if( !::WriteFile( hfile, str.data(), (DWORD) str.size(), &dw, NULL) ) - { - int err_code = GetLastError(); - //LOG_PRINT("Failed to write to file handle: " << hfile<< " Last error code:" << err_code << " : " << log_space::get_win32_err_descr(err_code), LOG_LEVEL_2); - return false; - } - ::CloseHandle(hfile); - return true; - }else - { - //LOG_WIN32_ERROR(::GetLastError()); - return false; - } - - return false; - }*/ - - - - inline - bool save_string_to_file(const std::string& path_to_file, const std::string& str) - { - - try - { - std::ofstream fstream; - fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - fstream.open(path_to_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); - fstream << str; - fstream.close(); - return true; - } - - catch(...) - { - return false; - } - } - - /* - inline - bool load_form_handle(HANDLE hfile, std::string& str) - { - if( INVALID_HANDLE_VALUE != hfile ) - { - bool res = true; - DWORD dw = 0; - DWORD fsize = ::GetFileSize(hfile, &dw); - if(fsize > 300000000) - { - ::CloseHandle(hfile); - return false; - } - if(fsize) - { - str.resize(fsize); - if(!::ReadFile( hfile, (LPVOID)str.data(), (DWORD)str.size(), &dw, NULL)) - res = false; - } - ::CloseHandle(hfile); - return res; - } - return false; - } - */ - inline - bool get_file_time(const std::string& path_to_file, time_t& ft) - { - boost::system::error_code ec; - ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec); - if(!ec) - return true; - else - return false; - } - - inline - bool set_file_time(const std::string& path_to_file, const time_t& ft) - { - boost::system::error_code ec; - boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ft, ec); - if(!ec) - return true; - else - return false; - } - - - inline - bool load_file_to_string(const std::string& path_to_file, std::string& target_str) - { - try - { - std::ifstream fstream; - fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate); - - std::ifstream::pos_type file_size = fstream.tellg(); - - if(file_size > 1000000000) - return false;//don't go crazy - size_t file_size_t = static_cast(file_size); - - target_str.resize(file_size_t); - - fstream.seekg (0, std::ios::beg); - fstream.read((char*)target_str.data(), target_str.size()); - fstream.close(); - return true; - } - - catch(...) - { - return false; - } - } - - inline - bool append_string_to_file(const std::string& path_to_file, const std::string& str) - { - try - { - std::ofstream fstream; - fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - fstream.open(path_to_file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::app); - fstream << str; - fstream.close(); - return true; - } - - catch(...) - { - return false; - } - } - - /* - bool remove_dir_and_subirs(const char* path_to_dir); - - inline - bool clean_dir(const char* path_to_dir) - { - if(!path_to_dir) - return false; - - std::string folder = path_to_dir; - WIN32_FIND_DATAA find_data = {0}; - HANDLE hfind = ::FindFirstFileA((folder + "\\*.*").c_str(), &find_data); - if(INVALID_HANDLE_VALUE == hfind) - return false; - do{ - if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName))) - continue; - - if(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) - { - if(!remove_dir_and_subirs((folder + "\\" + find_data.cFileName).c_str())) - return false; - }else - { - if(!::DeleteFileA((folder + "\\" + find_data.cFileName).c_str())) - return false; - } - - - }while(::FindNextFileA(hfind, &find_data)); - ::FindClose(hfind); - - return true; - } - */ -#ifdef WINDOWS_PLATFORM - inline bool get_folder_content(const std::string& path, std::list& target_list) - { - WIN32_FIND_DATAA find_data = {0}; - HANDLE hfind = ::FindFirstFileA((path + "\\*.*").c_str(), &find_data); - if(INVALID_HANDLE_VALUE == hfind) - return false; - do{ - if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName))) - continue; - - target_list.push_back(find_data); - - }while(::FindNextFileA(hfind, &find_data)); - ::FindClose(hfind); - - return true; - } -#endif - inline bool get_folder_content(const std::string& path, std::list& target_list, bool only_files = false) - { - try - { - - boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end - for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr ) - { - if ( only_files && boost::filesystem::is_directory(itr->status()) ) - { - continue; - } - target_list.push_back(itr->path().filename().string()); - } - - } - - catch(...) - { - return false; - } - return true; - } -} -} - -#endif //_FILE_IO_UTILS_H_ diff --git a/contrib/epee/include/global_stream_operators.h b/contrib/epee/include/global_stream_operators.h deleted file mode 100644 index 6fbdbc2edb..0000000000 --- a/contrib/epee/include/global_stream_operators.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -std::stringstream& operator<<(std::stringstream& out, const std::wstring& ws) -{ - std::string as = string_encoding::convert_to_ansii(ws); - out << as; - return out; -} diff --git a/contrib/epee/include/gzip_encoding.h b/contrib/epee/include/gzip_encoding.h deleted file mode 100644 index 2be51e77d6..0000000000 --- a/contrib/epee/include/gzip_encoding.h +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _GZIP_ENCODING_H_ -#define _GZIP_ENCODING_H_ -#include "net/http_client_base.h" -#include "zlib/zlib.h" -//#include "http.h" - - -namespace epee -{ -namespace net_utils -{ - - - - class content_encoding_gzip: public i_sub_handler - { - public: - /*! \brief - * Function content_encoding_gzip : Constructor - * - */ - inline - content_encoding_gzip(i_target_handler* powner_filter, bool is_deflate_mode = false):m_powner_filter(powner_filter), - m_is_stream_ended(false), - m_is_deflate_mode(is_deflate_mode), - m_is_first_update_in(true) - { - memset(&m_zstream_in, 0, sizeof(m_zstream_in)); - memset(&m_zstream_out, 0, sizeof(m_zstream_out)); - int ret = 0; - if(is_deflate_mode) - { - ret = inflateInit(&m_zstream_in); - ret = deflateInit(&m_zstream_out, Z_DEFAULT_COMPRESSION); - }else - { - ret = inflateInit2(&m_zstream_in, 0x1F); - ret = deflateInit2(&m_zstream_out, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8, Z_DEFAULT_STRATEGY); - } - } - /*! \brief - * Function content_encoding_gzip : Destructor - * - */ - inline - ~content_encoding_gzip() - { - inflateEnd(& m_zstream_in ); - deflateEnd(& m_zstream_out ); - } - /*! \brief - * Function update_in : Entry point for income data - * - */ - inline - virtual bool update_in( std::string& piece_of_transfer) - { - - bool is_first_time_here = m_is_first_update_in; - m_is_first_update_in = false; - - if(m_pre_decode.size()) - m_pre_decode += piece_of_transfer; - else - m_pre_decode.swap(piece_of_transfer); - piece_of_transfer.clear(); - - std::string decode_summary_buff; - - size_t ungzip_size = m_pre_decode.size() * 0x30; - std::string current_decode_buff(ungzip_size, 'X'); - - //Here the cycle is introduced where we unpack the buffer, the cycle is required - //because of the case where if after unpacking the data will exceed the awaited size, we will not halt with error - bool continue_unpacking = true; - bool first_step = true; - while(m_pre_decode.size() && continue_unpacking) - { - - //fill buffers - m_zstream_in.next_in = (Bytef*)m_pre_decode.data(); - m_zstream_in.avail_in = (uInt)m_pre_decode.size(); - m_zstream_in.next_out = (Bytef*)current_decode_buff.data(); - m_zstream_in.avail_out = (uInt)ungzip_size; - - int flag = Z_SYNC_FLUSH; - int ret = inflate(&m_zstream_in, flag); - CHECK_AND_ASSERT_MES(ret>=0 || m_zstream_in.avail_out ||m_is_deflate_mode, false, "content_encoding_gzip::update_in() Failed to inflate. err = " << ret); - - if(Z_STREAM_END == ret) - m_is_stream_ended = true; - else if(Z_DATA_ERROR == ret && is_first_time_here && m_is_deflate_mode&& first_step) - { - // some servers (notably Apache with mod_deflate) don't generate zlib headers - // insert a dummy header and try again - static char dummy_head[2] = - { - 0x8 + 0x7 * 0x10, - (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, - }; - inflateReset(&m_zstream_in); - m_zstream_in.next_in = (Bytef*) dummy_head; - m_zstream_in.avail_in = sizeof(dummy_head); - - ret = inflate(&m_zstream_in, Z_NO_FLUSH); - if (ret != Z_OK) - { - LOCAL_ASSERT(0); - m_pre_decode.swap(piece_of_transfer); - return false; - } - m_zstream_in.next_in = (Bytef*)m_pre_decode.data(); - m_zstream_in.avail_in = (uInt)m_pre_decode.size(); - - ret = inflate(&m_zstream_in, Z_NO_FLUSH); - if (ret != Z_OK) - { - LOCAL_ASSERT(0); - m_pre_decode.swap(piece_of_transfer); - return false; - } - } - - - //leave only unpacked part in the output buffer to start with it the next time - m_pre_decode.erase(0, m_pre_decode.size()-m_zstream_in.avail_in); - //if decoder gave nothing to return, then everything is ahead, now simply break - if(ungzip_size == m_zstream_in.avail_out) - break; - - //decode_buff currently stores data parts that were unpacked, fix this size - current_decode_buff.resize(ungzip_size - m_zstream_in.avail_out); - if(decode_summary_buff.size()) - decode_summary_buff += current_decode_buff; - else - current_decode_buff.swap(decode_summary_buff); - - current_decode_buff.resize(ungzip_size); - first_step = false; - } - - //Process these data if required - bool res = true; - - res = m_powner_filter->handle_target_data(decode_summary_buff); - - return true; - - } - /*! \brief - * Function stop : Entry point for stop signal and flushing cached data buffer. - * - */ - inline - virtual void stop(std::string& OUT collect_remains) - { - } - protected: - private: - /*! \brief - * Pointer to parent HTTP-parser - */ - i_target_handler* m_powner_filter; - /*! \brief - * ZLIB object for income stream - */ - z_stream m_zstream_in; - /*! \brief - * ZLIB object for outcome stream - */ - z_stream m_zstream_out; - /*! \brief - * Data that could not be unpacked immediately, left to wait for the next packet of data - */ - std::string m_pre_decode; - /*! \brief - * The data are accumulated for a package in the buffer to send the web client - */ - std::string m_pre_encode; - /*! \brief - * Signals that stream looks like ended - */ - bool m_is_stream_ended; - /*! \brief - * If this flag is set, income data is in HTTP-deflate mode - */ - bool m_is_deflate_mode; - /*! \brief - * Marks that it is a first data packet - */ - bool m_is_first_update_in; - }; -} -} - - - -#endif //_GZIP_ENCODING_H_ diff --git a/contrib/epee/include/hmac-md5.h b/contrib/epee/include/hmac-md5.h deleted file mode 100644 index 2a4e0d401a..0000000000 --- a/contrib/epee/include/hmac-md5.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * libEtPan! -- a mail stuff library - * - * Copyright (C) 2001, 2005 - DINH Viet Hoa - * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. - */ - -/* hmac-md5.h -- HMAC_MD5 functions - */ - -/* - * $Id: hmac-md5.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $ - */ - -#ifndef HMAC_MD5_H -#define HMAC_MD5_H 1 - -namespace md5 -{ - - - -#define HMAC_MD5_SIZE 16 - - /* intermediate MD5 context */ - typedef struct HMAC_MD5_CTX_s { - MD5_CTX ictx, octx; - } HMAC_MD5_CTX; - - /* intermediate HMAC state - * values stored in network byte order (Big Endian) - */ - typedef struct HMAC_MD5_STATE_s { - UINT4 istate[4]; - UINT4 ostate[4]; - } HMAC_MD5_STATE; - - /* One step hmac computation - * - * digest may be same as text or key - */ - void hmac_md5(const unsigned char *text, int text_len, - const unsigned char *key, int key_len, - unsigned char digest[HMAC_MD5_SIZE]); - - /* create context from key - */ - void hmac_md5_init(HMAC_MD5_CTX *hmac, - const unsigned char *key, int key_len); - - /* precalculate intermediate state from key - */ - void hmac_md5_precalc(HMAC_MD5_STATE *hmac, - const unsigned char *key, int key_len); - - /* initialize context from intermediate state - */ - void hmac_md5_import(HMAC_MD5_CTX *hmac, HMAC_MD5_STATE *state); - -#define hmac_md5_update(hmac, text, text_len) MD5Update(&(hmac)->ictx, (text), (text_len)) - - /* finish hmac from intermediate result. Intermediate result is zeroed. - */ - void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], - HMAC_MD5_CTX *hmac); - -} - -#endif /* HMAC_MD5_H */ diff --git a/contrib/epee/include/include_base_utils.h b/contrib/epee/include/include_base_utils.h deleted file mode 100644 index 8412a0083c..0000000000 --- a/contrib/epee/include/include_base_utils.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#define BOOST_FILESYSTEM_VERSION 3 -#define ENABLE_RELEASE_LOGGING - -#include "misc_log_ex.h" - - diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h deleted file mode 100644 index 11faa9762f..0000000000 --- a/contrib/epee/include/math_helper.h +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#include "misc_os_dependent.h" -#include "pragma_comp_defs.h" - -namespace epee -{ -namespace math_helper -{ - - template - class average - { - public: - - average() - { - m_base = default_base; - m_last_avg_val = 0; - } - - bool set_base() - { - CRITICAL_REGION_LOCAL(m_lock); - - m_base = default_base; - if(m_list.size() > m_base) - m_list.resize(m_base); - - return true; - } - - typedef val value_type; - - void push(const value_type& vl) - { - CRITICAL_REGION_LOCAL(m_lock); - -//#ifndef DEBUG_STUB - m_list.push_back(vl); - if(m_list.size() > m_base ) - m_list.pop_front(); -//#endif - } - - double update(const value_type& vl) - { - CRITICAL_REGION_LOCAL(m_lock); -//#ifndef DEBUG_STUB - push(vl); -//#endif - - return get_avg(); - } - - double get_avg() - { - CRITICAL_REGION_LOCAL(m_lock); - - value_type vl = std::accumulate(m_list.begin(), m_list.end(), value_type(0)); - if(m_list.size()) - return m_last_avg_val = (double)(vl/m_list.size()); - - return m_last_avg_val = (double)vl; - } - - value_type get_last_val() - { - CRITICAL_REGION_LOCAL(m_lock); - if(m_list.size()) - return m_list.back(); - - return 0; - } - - private: - unsigned int m_base; - double m_last_avg_val; - std::list m_list; - critical_section m_lock; - }; - - -#ifdef WINDOWS_PLATFORM - - /************************************************************************/ - /* */ - /************************************************************************/ - class timing_guard_base - { - public: - virtual ~timing_guard_base(){}; - }; - - template - class timing_guard: public timing_guard_base - { - public: - timing_guard(T& avrg):m_avrg(avrg) - { - m_start_ticks = ::GetTickCount(); - } - - ~timing_guard() - { - m_avrg.push(::GetTickCount()-m_start_ticks); - } - - private: - T& m_avrg; - DWORD m_start_ticks; - }; - - template - timing_guard_base* create_timing_guard(t_timing& timing){return new timing_guard(timing);} - -#define BEGIN_TIMING_ZONE(timing_var) { boost::shared_ptr local_timing_guard_ptr(math_helper::create_timing_guard(timing_var)); -#define END_TIMING_ZONE() } -#endif - -//#ifdef WINDOWS_PLATFORM_EX - template - class speed - { - public: - - speed() - { - m_time_window = default_time_window; - m_last_speed_value = 0; - } - bool chick() - { -#ifndef DEBUG_STUB - uint64_t ticks = misc_utils::get_tick_count(); - CRITICAL_REGION_BEGIN(m_lock); - m_chicks.push_back(ticks); - CRITICAL_REGION_END(); - //flush(ticks); -#endif - return true; - } - - bool chick(size_t count) - { - for(size_t s = 0; s != count; s++) - chick(); - - return true; - } - - - size_t get_speed() - { - flush(misc_utils::get_tick_count()); - return m_last_speed_value = m_chicks.size(); - } - private: - - bool flush(uint64_t ticks) - { - CRITICAL_REGION_BEGIN(m_lock); - std::list::iterator it = m_chicks.begin(); - while(it != m_chicks.end()) - { - if(*it + m_time_window < ticks) - m_chicks.erase(it++); - else - break; - } - CRITICAL_REGION_END(); - return true; - } - - std::list m_chicks; - uint64_t m_time_window; - size_t m_last_speed_value; - critical_section m_lock; - }; -//#endif - - template - void randomize_list(tlist& t_list) - { - for(typename tlist::iterator it = t_list.begin();it!=t_list.end();it++) - { - size_t offset = rand()%t_list.size(); - typename tlist::iterator it_2 = t_list.begin(); - for(size_t local_offset = 0;local_offset!=offset;local_offset++) - it_2++; - if(it_2 == it) - continue; - std::swap(*it_2, *it); - } - - } -PRAGMA_WARNING_PUSH -PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") - inline - uint64_t generated_random_uint64() - { - boost::uuids::uuid id___ = boost::uuids::random_generator()(); - return *reinterpret_cast(&id___.data[0]); //(*reinterpret_cast(&id___.data[0]) ^ *reinterpret_cast(&id___.data[8])); - } -PRAGMA_WARNING_POP - template - class once_a_time_seconds - { - public: - once_a_time_seconds():m_interval(default_interval) - { - m_last_worked_time = 0; - if(!start_immediate) - time(&m_last_worked_time); - } - - template - bool do_call(functor_t functr) - { - time_t current_time = 0; - time(¤t_time); - - if(current_time - m_last_worked_time > m_interval) - { - bool res = functr(); - time(&m_last_worked_time); - return res; - } - return true; - } - - private: - time_t m_last_worked_time; - time_t m_interval; - }; -} -} \ No newline at end of file diff --git a/contrib/epee/include/md5_l.h b/contrib/epee/include/md5_l.h deleted file mode 100644 index fe4c67db62..0000000000 --- a/contrib/epee/include/md5_l.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * libEtPan! -- a mail stuff library - * - * Copyright (C) 2001, 2005 - DINH Viet Hoa - * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. - */ - -/* - * $Id: md5.h,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $ - */ - -/* MD5.H - header file for MD5C.C - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. -These notices must be retained in any copies of any part of this -documentation and/or software. - */ -#ifndef MD5_H -#define MD5_H - - -#include "md5global.h" - -namespace md5 -{ - /* MD5 context. */ - typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ - } MD5_CTX; - - static void MD5Init(MD5_CTX * context); - static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen ); - static void MD5Final ( unsigned char digest[16], MD5_CTX *context ); - static void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest); - - - inline bool md5( unsigned char *input, int ilen, unsigned char output[16] ) - { - MD5_CTX ctx; - - MD5Init( &ctx ); - MD5Update( &ctx, input, ilen ); - MD5Final( output, &ctx); - - memset( &ctx, 0, sizeof( MD5_CTX) ); - return true; - } - - -} - -#include "md5_l.inl" - -#endif diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl deleted file mode 100644 index c3da1a3b07..0000000000 --- a/contrib/epee/include/md5_l.inl +++ /dev/null @@ -1,563 +0,0 @@ -/* -* libEtPan! -- a mail stuff library -* -* Copyright (C) 2001, 2005 - DINH Viet Hoa -* 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. -*/ - -/* -* $Id: md5.c,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $ -*/ - -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm -*/ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. -*/ - -/* do i need all of this just for htonl()? damn. */ -//#include -//#include -//#include -//#include - - - -#include "md5global.h" -#include "md5_l.h" -#include "hmac-md5.h" - -namespace md5 -{ - /* Constants for MD5Transform routine. - */ - -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - - /* - static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); - static void Encode PROTO_LIST - ((unsigned char *, UINT4 *, unsigned int)); - static void Decode PROTO_LIST - ((UINT4 *, unsigned char *, unsigned int)); - static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); - static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); - */ - - static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) - { - unsigned int i; - - for (i = 0; i < len; i++) - output[i] = input[i]; - } - - /* Note: Replace "for loop" with standard memset if possible. - */ - - static void MD5_memset (POINTER output, int value, unsigned int len) - { - unsigned int i; - - for (i = 0; i < len; i++) - ((char *)output)[i] = (char)value; - } - - static void MD5Transform (UINT4 state[4], unsigned char block[64]); - - static unsigned char* PADDING() - { - static unsigned char local_PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - return local_PADDING; - - } - - - - /* F, G, H and I are basic MD5 functions. - - */ -#ifdef I - /* This might be defined via NANA */ -#undef I -#endif - -#define MD5_M_F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define MD5_M_G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define MD5_M_H(x, y, z) ((x) ^ (y) ^ (z)) -#define MD5_M_I(x, y, z) ((y) ^ ((x) | (~z))) - - /* ROTATE_LEFT rotates x left n bits. - - */ - -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - - /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. - Rotation is separate from addition to prevent recomputation. - */ - -#define FF(a, b, c, d, x, s, ac) { (a) += MD5_M_F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } -#define GG(a, b, c, d, x, s, ac) { (a) += MD5_M_G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } -#define HH(a, b, c, d, x, s, ac) { (a) += MD5_M_H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } -#define II(a, b, c, d, x, s, ac) { (a) += MD5_M_I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } - - /* MD5 initialization. Begins an MD5 operation, writing a new context. - */ - - static void MD5Init(MD5_CTX * context) - { - context->count[0] = context->count[1] = 0; - - /* Load magic initialization constants. - - */ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; - } - - /* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the context. - */ - - static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen ) - { - unsigned int i, index, partLen; - - /* Compute number of bytes mod 64 */ - index = (unsigned int)((context->count[0] >> 3) & 0x3F); - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - index; - - /* Transform as many times as possible. - - */ - if (inputLen >= partLen) - { - MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)input, partLen ); - MD5Transform( context->state, context->buffer ); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform (context->state, (unsigned char*)&input[i]); - - index = 0; - } - else - i = 0; - - /* Buffer remaining input */ - MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i ); - - } - - /* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - - */ - - static void Encode (unsigned char *output, UINT4 *input, unsigned int len) - { - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } - } - - /* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - - */ - - static void Decode (UINT4 *output, unsigned char *input, unsigned int len) - { - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) - | (((UINT4)input[j+3]) << 24); - } - - /* MD5 finalization. Ends an MD5 message-digest operation, writing the - the message digest and zeroizing the context. - - */ - - static void MD5Final ( unsigned char digest[16], MD5_CTX *context ) - { - unsigned char bits[8]; - unsigned int index, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); - - /* Pad out to 56 mod 64. - - */ - index = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (index < 56) ? (56 - index) : (120 - index); - MD5Update (context, PADDING(), padLen); - - /* Append length (before padding) */ - MD5Update (context, bits, 8); - - /* Store state in digest */ - Encode (digest, context->state, 16); - - /* Zeroize sensitive information. - - */ - MD5_memset ((POINTER)context, 0, sizeof (*context)); - } - - /* MD5 basic transformation. Transforms state based on block. - - */ - - static void MD5Transform (UINT4 state[4], unsigned char block[64]) - { - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. - */ - MD5_memset ((POINTER)x, 0, sizeof (x)); - } - - /* Note: Replace "for loop" with standard memcpy if possible. - - */ - inline - void hmac_md5_init(HMAC_MD5_CTX *hmac, - const unsigned char *key, - int key_len) - { - unsigned char k_ipad[65]; /* inner padding - - * key XORd with ipad - */ - unsigned char k_opad[65]; /* outer padding - - * key XORd with opad - */ - unsigned char tk[16]; - int i; - /* if key is longer than 64 bytes reset it to key=MD5(key) */ - if (key_len > 64) { - - MD5_CTX tctx; - - MD5Init(&tctx); - MD5Update(&tctx, key, key_len); - MD5Final(tk, &tctx); - - key = tk; - key_len = 16; - } - - /* - * the HMAC_MD5 transform looks like: - * - * MD5(K XOR opad, MD5(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected - */ - - /* start out by storing key in pads */ - MD5_memset(k_ipad, '\0', sizeof k_ipad); - MD5_memset(k_opad, '\0', sizeof k_opad); - MD5_memcpy( k_ipad, (POINTER)key, key_len); - MD5_memcpy( k_opad, (POINTER)key, key_len); - - /* XOR key with ipad and opad values */ - for (i=0; i<64; i++) { - k_ipad[i] ^= 0x36; - k_opad[i] ^= 0x5c; - } - - MD5Init(&hmac->ictx); /* init inner context */ - MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */ - - MD5Init(&hmac->octx); /* init outer context */ - MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ - - /* scrub the pads and key context (if used) */ - MD5_memset( (POINTER)&k_ipad, 0, sizeof(k_ipad)); - MD5_memset( (POINTER)&k_opad, 0, sizeof(k_opad)); - MD5_memset( (POINTER)&tk, 0, sizeof(tk)); - - /* and we're done. */ - } - - /* The precalc and import routines here rely on the fact that we pad - * the key out to 64 bytes and use that to initialize the md5 - * contexts, and that updating an md5 context with 64 bytes of data - * leaves nothing left over; all of the interesting state is contained - * in the state field, and none of it is left over in the count and - * buffer fields. So all we have to do is save the state field; we - * can zero the others when we reload it. Which is why the decision - * was made to pad the key out to 64 bytes in the first place. */ - inline - void hmac_md5_precalc(HMAC_MD5_STATE *state, - const unsigned char *key, - int key_len) - { - HMAC_MD5_CTX hmac; - unsigned lupe; - - hmac_md5_init(&hmac, key, key_len); - for (lupe = 0; lupe < 4; lupe++) { - state->istate[lupe] = htonl(hmac.ictx.state[lupe]); - state->ostate[lupe] = htonl(hmac.octx.state[lupe]); - } - MD5_memset( (POINTER)&hmac, 0, sizeof(hmac)); - } - - - inline - void hmac_md5_import(HMAC_MD5_CTX *hmac, - HMAC_MD5_STATE *state) - { - unsigned lupe; - MD5_memset( (POINTER)hmac, 0, sizeof(HMAC_MD5_CTX)); - for (lupe = 0; lupe < 4; lupe++) { - hmac->ictx.state[lupe] = ntohl(state->istate[lupe]); - hmac->octx.state[lupe] = ntohl(state->ostate[lupe]); - } - /* Init the counts to account for our having applied - * 64 bytes of key; this works out to 0x200 (64 << 3; see - * MD5Update above...) */ - hmac->ictx.count[0] = hmac->octx.count[0] = 0x200; - } - - inline - void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], - HMAC_MD5_CTX *hmac) - { - MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */ - MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */ - MD5Final(digest, &hmac->octx); /* Finalize outer md5 */ - } - - - void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest) - { - MD5_CTX context; - - unsigned char k_ipad[65]; /* inner padding - - * key XORd with ipad - */ - unsigned char k_opad[65]; /* outer padding - - * key XORd with opad - */ - unsigned char tk[16]; - int i; - /* if key is longer than 64 bytes reset it to key=MD5(key) */ - if (key_len > 64) { - - MD5_CTX tctx; - - MD5Init(&tctx); - MD5Update(&tctx, key, key_len); - MD5Final(tk, &tctx); - - key = tk; - key_len = 16; - } - - /* - * the HMAC_MD5 transform looks like: - * - * MD5(K XOR opad, MD5(K XOR ipad, text)) - * - * where K is an n byte key - * ipad is the byte 0x36 repeated 64 times - * opad is the byte 0x5c repeated 64 times - * and text is the data being protected - */ - - /* start out by storing key in pads */ - MD5_memset(k_ipad, '\0', sizeof k_ipad); - MD5_memset(k_opad, '\0', sizeof k_opad); - MD5_memcpy( k_ipad, (POINTER)key, key_len); - MD5_memcpy( k_opad, (POINTER)key, key_len); - - /* XOR key with ipad and opad values */ - for (i=0; i<64; i++) { - k_ipad[i] ^= 0x36; - k_opad[i] ^= 0x5c; - } - /* - * perform inner MD5 - */ - - MD5Init(&context); /* init context for 1st - * pass */ - MD5Update(&context, k_ipad, 64); /* start with inner pad */ - MD5Update(&context, text, text_len); /* then text of datagram */ - MD5Final(digest, &context); /* finish up 1st pass */ - - /* - * perform outer MD5 - */ - MD5Init(&context); /* init context for 2nd - * pass */ - MD5Update(&context, k_opad, 64); /* start with outer pad */ - MD5Update(&context, digest, 16); /* then results of 1st - * hash */ - MD5Final(digest, &context); /* finish up 2nd pass */ - - } -} \ No newline at end of file diff --git a/contrib/epee/include/md5global.h b/contrib/epee/include/md5global.h deleted file mode 100644 index afc2290193..0000000000 --- a/contrib/epee/include/md5global.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * libEtPan! -- a mail stuff library - * - * Copyright (C) 2001, 2005 - DINH Viet Hoa - * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. - */ - -/* - * $Id: md5global.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $ - */ - -/* GLOBAL.H - RSAREF types and constants - */ - -#ifndef MD5GLOBAL_H -#define MD5GLOBAL_H - -namespace md5 -{ - - - /* PROTOTYPES should be set to one if and only if the compiler supports - function argument prototyping. - The following makes PROTOTYPES default to 0 if it has not already - been defined with C compiler flags. - */ -#ifndef PROTOTYPES -#define PROTOTYPES 0 -#endif - - /* POINTER defines a generic pointer type */ - typedef unsigned char *POINTER; - - /* UINT2 defines a two byte word */ - typedef unsigned short int UINT2; - - /* UINT4 defines a four byte word */ - //typedef unsigned long int UINT4; - typedef unsigned int UINT4; - - /* PROTO_LIST is defined depending on how PROTOTYPES is defined above. - If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it - returns an empty list. - */ -#if PROTOTYPES -#define PROTO_LIST(list) list -#else -#define PROTO_LIST(list) () -#endif - -} - -#endif diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h deleted file mode 100644 index d5157365c8..0000000000 --- a/contrib/epee/include/misc_language.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -namespace epee -{ -#define STD_TRY_BEGIN() try { - -#define STD_TRY_CATCH(where_, ret_val) \ - } \ - catch (const std::exception &e) \ - { \ - LOG_ERROR("EXCEPTION: " << where_ << ", mes: "<< e.what()); \ - return ret_val; \ - } \ - catch (...) \ - { \ - LOG_ERROR("EXCEPTION: " << where_ ); \ - return ret_val; \ - } - - - -#define AUTO_VAL_INIT(v) boost::value_initialized() - -namespace misc_utils -{ - template - t_type get_max_t_val(t_type t) - { - return (std::numeric_limits::max)(); - } - - - template - t_iterator move_it_forward(t_iterator it, size_t count) - { - while(count--) - it++; - return it; - } - - template - t_iterator move_it_backward(t_iterator it, size_t count) - { - while(count--) - it--; - return it; - } - - - // TEMPLATE STRUCT less - template - struct less_as_pod - : public std::binary_function<_Ty, _Ty, bool> - { // functor for operator< - bool operator()(const _Ty& _Left, const _Ty& _Right) const - { // apply operator< to operands - return memcmp(&_Left, &_Right, sizeof(_Left)) < 0; - } - }; - - template - bool is_less_as_pod(const _Ty& _Left, const _Ty& _Right) - { // apply operator< to operands - return memcmp(&_Left, &_Right, sizeof(_Left)) < 0; - } - - - inline - bool sleep_no_w(long ms ) - { - boost::this_thread::sleep( - boost::get_system_time() + - boost::posix_time::milliseconds( std::max(ms,0) ) ); - - return true; - } - - template - type_vec_type median(std::vector &v) - { - if(v.empty()) - return boost::value_initialized(); - if(v.size() == 1) - return v[0]; - - size_t n = (v.size()) / 2; - std::sort(v.begin(), v.end()); - //nth_element(v.begin(), v.begin()+n-1, v.end()); - if(v.size()%2) - {//1, 3, 5... - return v[n]; - }else - {//2, 4, 6... - return (v[n-1] + v[n])/2; - } - - } - - /************************************************************************/ - /* */ - /************************************************************************/ - - struct call_befor_die_base - { - virtual ~call_befor_die_base(){} - }; - - typedef boost::shared_ptr auto_scope_leave_caller; - - - template - struct call_befor_die: public call_befor_die_base - { - t_scope_leave_handler m_func; - call_befor_die(t_scope_leave_handler f):m_func(f) - {} - ~call_befor_die() - { - m_func(); - } - }; - - template - auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f) - { - auto_scope_leave_caller slc(new call_befor_die(f)); - return slc; - } - -} -} diff --git a/contrib/epee/include/misc_log_ex.cpp b/contrib/epee/include/misc_log_ex.cpp deleted file mode 100644 index 0c0b441b4f..0000000000 --- a/contrib/epee/include/misc_log_ex.cpp +++ /dev/null @@ -1,1029 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_log_ex.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(WIN32) -#include -#else -#include -#endif - -#include "static_initializer.h" -#include "string_tools.h" -#include "time_helper.h" -#include "misc_os_dependent.h" - -#include "syncobj.h" - - -#define LOG_LEVEL_SILENT -1 -#define LOG_LEVEL_0 0 -#define LOG_LEVEL_1 1 -#define LOG_LEVEL_2 2 -#define LOG_LEVEL_3 3 -#define LOG_LEVEL_4 4 -#define LOG_LEVEL_MIN LOG_LEVEL_SILENT -#define LOG_LEVEL_MAX LOG_LEVEL_4 - - -#define LOGGER_NULL 0 -#define LOGGER_FILE 1 -#define LOGGER_DEBUGGER 2 -#define LOGGER_CONSOLE 3 -#define LOGGER_DUMP 4 - - -#ifndef LOCAL_ASSERT -#include -#if (defined _MSC_VER) -#define LOCAL_ASSERT(expr) {if(epee::debug::get_set_enable_assert()){_ASSERTE(expr);}} -#else -#define LOCAL_ASSERT(expr) -#endif - -#endif - -namespace epee { -namespace log_space { - //---------------------------------------------------------------------------- - bool is_stdout_a_tty() - { - static std::atomic initialized(false); - static std::atomic is_a_tty(false); - - if (!initialized.load(std::memory_order_acquire)) - { -#if defined(WIN32) - is_a_tty.store(0 != _isatty(_fileno(stdout)), std::memory_order_relaxed); -#else - is_a_tty.store(0 != isatty(fileno(stdout)), std::memory_order_relaxed); -#endif - initialized.store(true, std::memory_order_release); - } - - return is_a_tty.load(std::memory_order_relaxed); - } - //---------------------------------------------------------------------------- - void set_console_color(int color, bool bright) - { - if (!is_stdout_a_tty()) - return; - - switch(color) - { - case console_color_default: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;37m"; - else - std::cout << "\033[0m"; -#endif - } - break; - case console_color_white: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;37m"; - else - std::cout << "\033[0;37m"; -#endif - } - break; - case console_color_red: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;31m"; - else - std::cout << "\033[0;31m"; -#endif - } - break; - case console_color_green: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;32m"; - else - std::cout << "\033[0;32m"; -#endif - } - break; - - case console_color_blue: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;34m"; - else - std::cout << "\033[0;34m"; -#endif - } - break; - - case console_color_cyan: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;36m"; - else - std::cout << "\033[0;36m"; -#endif - } - break; - - case console_color_magenta: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;35m"; - else - std::cout << "\033[0;35m"; -#endif - } - break; - - case console_color_yellow: - { -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); -#else - if(bright) - std::cout << "\033[1;33m"; - else - std::cout << "\033[0;33m"; -#endif - } - break; - - } - } - //---------------------------------------------------------------------------- - void reset_console_color() { - if (!is_stdout_a_tty()) - return; - -#ifdef WIN32 - HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); -#else - std::cout << "\033[0m"; - std::cout.flush(); -#endif - } - //---------------------------------------------------------------------------- - bool rotate_log_file(const char* pfile_path) - { -#ifdef _MSC_VER - if(!pfile_path) - return false; - - std::string file_path = pfile_path; - std::string::size_type a = file_path .rfind('.'); - if ( a != std::string::npos ) - file_path .erase( a, file_path .size()); - - ::DeleteFileA( (file_path + ".0").c_str() ); - ::MoveFileA( (file_path + ".log").c_str(), (file_path + ".0").c_str() ); -#else - return false;//not implemented yet -#endif - return true; - } - //---------------------------------------------------------------------------- -#ifdef _MSC_VER - bool debug_output_stream::out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name/* = NULL*/) - { - for ( int i = 0; i < buffer_len; i = i + max_dbg_str_len ) - { - std::string s( buffer + i, buffer_len- i < max_dbg_str_len ? - buffer_len - i : max_dbg_str_len ); - - ::OutputDebugStringA( s.c_str() ); - } - return true; - } -#endif - //---------------------------------------------------------------------------- - console_output_stream::console_output_stream() - { -#ifdef _MSC_VER - - if(!::GetStdHandle(STD_OUTPUT_HANDLE)) - m_have_to_kill_console = true; - else - m_have_to_kill_console = false; - - ::AllocConsole(); -#endif - } - //---------------------------------------------------------------------------- - console_output_stream::~console_output_stream() - { -#ifdef _MSC_VER - if(m_have_to_kill_console) - ::FreeConsole(); -#endif - } - //---------------------------------------------------------------------------- - bool console_output_stream::out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name/* = NULL*/) - { - if(plog_name) - return true; //skip alternative logs from console - - set_console_color(color, log_level < 1); - -#ifdef _MSC_VER - const char* ptarget_buf = NULL; - char* pallocated_buf = NULL; - - // - int i = 0; - for(; i < buffer_len; i++) - if(buffer[i] == '\a') break; - if(i == buffer_len) - ptarget_buf = buffer; - else - { - pallocated_buf = new char[buffer_len]; - ptarget_buf = pallocated_buf; - for(i = 0; i < buffer_len; i++) - { - if(buffer[i] == '\a') - pallocated_buf[i] = '^'; - else - pallocated_buf[i] = buffer[i]; - } - } - - //uint32_t b = 0; - //::WriteConsoleA(::GetStdHandle(STD_OUTPUT_HANDLE), ptarget_buf, buffer_len, (DWORD*)&b, 0); - std::cout << ptarget_buf; - if(pallocated_buf) delete [] pallocated_buf; -#else - std::string buf(buffer, buffer_len); - for(size_t i = 0; i!= buf.size(); i++) - { - if(buf[i] == 7 || buf[i] == -107) - buf[i] = '^'; - } - - std::cout << buf; -#endif - reset_console_color(); - return true; - } - //---------------------------------------------------------------------------- - file_output_stream::file_output_stream(std::string default_log_file_name, std::string log_path) - { - m_default_log_filename = default_log_file_name; - m_max_logfile_size = 0; - m_default_log_path = log_path; - m_pdefault_file_stream = add_new_stream_and_open(default_log_file_name.c_str()); - } - //---------------------------------------------------------------------------- - file_output_stream::~file_output_stream() - { - for(named_log_streams::iterator it = m_log_file_names.begin(); it!=m_log_file_names.end(); it++) - { - if ( it->second->is_open() ) - { - it->second->flush(); - it->second->close(); - } - delete it->second; - } - } - //---------------------------------------------------------------------------- - std::ofstream* file_output_stream::add_new_stream_and_open(const char* pstream_name) - { - //log_space::rotate_log_file((m_default_log_path + "\\" + pstream_name).c_str()); - - std::ofstream* pstream = (m_log_file_names[pstream_name] = new std::ofstream); - std::string target_path = m_default_log_path + "/" + pstream_name; - pstream->open( target_path.c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); - if(pstream->fail()) - return NULL; - return pstream; - } - //---------------------------------------------------------------------------- - bool file_output_stream::set_max_logfile_size(uint64_t max_size) - { - m_max_logfile_size = max_size; - return true; - } - //---------------------------------------------------------------------------- - bool file_output_stream::set_log_rotate_cmd(const std::string& cmd) - { - m_log_rotate_cmd = cmd; - return true; - } - //---------------------------------------------------------------------------- - bool file_output_stream::out_buffer(const char* buffer, int buffer_len, int log_level, int color, const char* plog_name/* = NULL*/) - { - std::ofstream* m_target_file_stream = m_pdefault_file_stream; - if(plog_name) - { //find named stream - named_log_streams::iterator it = m_log_file_names.find(plog_name); - if(it == m_log_file_names.end()) - m_target_file_stream = add_new_stream_and_open(plog_name); - else - m_target_file_stream = it->second; - } - if(!m_target_file_stream || !m_target_file_stream->is_open()) - return false;//TODO: add assert here - - m_target_file_stream->write(buffer, buffer_len ); - m_target_file_stream->flush(); - - if(m_max_logfile_size) - { - std::ofstream::pos_type pt = m_target_file_stream->tellp(); - uint64_t current_sz = pt; - if(current_sz > m_max_logfile_size) - { - std::cout << "current_sz= " << current_sz << " m_max_logfile_size= " << m_max_logfile_size << std::endl; - std::string log_file_name; - if(!plog_name) - log_file_name = m_default_log_filename; - else - log_file_name = plog_name; - - m_target_file_stream->close(); - std::string new_log_file_name = log_file_name; - - time_t tm = 0; - time(&tm); - - int err_count = 0; - boost::system::error_code ec; - do - { - new_log_file_name = string_tools::cut_off_extension(log_file_name); - if(err_count) - new_log_file_name += misc_utils::get_time_str_v2(tm) + "(" + boost::lexical_cast(err_count) + ")" + ".log"; - else - new_log_file_name += misc_utils::get_time_str_v2(tm) + ".log"; - - err_count++; - }while(boost::filesystem::exists(m_default_log_path + "/" + new_log_file_name, ec)); - - std::string new_log_file_path = m_default_log_path + "/" + new_log_file_name; - boost::filesystem::rename(m_default_log_path + "/" + log_file_name, new_log_file_path, ec); - if(ec) - { - std::cout << "Filed to rename, ec = " << ec.message() << std::endl; - } - - if(m_log_rotate_cmd.size()) - { - - std::string m_log_rotate_cmd_local_copy = m_log_rotate_cmd; - //boost::replace_all(m_log_rotate_cmd, "[*SOURCE*]", new_log_file_path); - boost::replace_all(m_log_rotate_cmd_local_copy, "[*TARGET*]", new_log_file_path); - - misc_utils::call_sys_cmd(m_log_rotate_cmd_local_copy); - } - - m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); - if(m_target_file_stream->fail()) - return false; - } - } - - return true; - } - //---------------------------------------------------------------------------- - log_stream_splitter::~log_stream_splitter() - { - //free pointers - std::for_each(m_log_streams.begin(), m_log_streams.end(), delete_ptr()); - } - //---------------------------------------------------------------------------- - bool log_stream_splitter::set_max_logfile_size(uint64_t max_size) - { - for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) - it->first->set_max_logfile_size(max_size); - return true; - } - //---------------------------------------------------------------------------- - bool log_stream_splitter::set_log_rotate_cmd(const std::string& cmd) - { - for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) - it->first->set_log_rotate_cmd(cmd); - return true; - } - //---------------------------------------------------------------------------- - bool log_stream_splitter::do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name/* = NULL*/) - { - std::string str_mess = rlog_mes; - size_t str_len = str_mess.size(); - const char* pstr = str_mess.c_str(); - for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) - if(it->second >= log_level) - it->first->out_buffer(pstr, (int)str_len, log_level, color, plog_name); - return true; - } - //---------------------------------------------------------------------------- - bool log_stream_splitter::add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit/* = LOG_LEVEL_4*/) - { - ibase_log_stream* ls = NULL; - - switch( type ) - { - case LOGGER_FILE: - ls = new file_output_stream( pdefault_file_name, pdefault_log_folder ); - break; - - case LOGGER_DEBUGGER: -#ifdef _MSC_VER - ls = new debug_output_stream( ); -#else - return false;//not implemented yet -#endif - break; - case LOGGER_CONSOLE: - ls = new console_output_stream( ); - break; - } - - if ( ls ) { - m_log_streams.push_back(streams_container::value_type(ls, log_level_limit)); - return true; - } - return ls ? true:false; - } - //---------------------------------------------------------------------------- - bool log_stream_splitter::add_logger(ibase_log_stream* pstream, int log_level_limit/* = LOG_LEVEL_4*/) - { - m_log_streams.push_back(streams_container::value_type(pstream, log_level_limit) ); - return true; - } - //---------------------------------------------------------------------------- - bool log_stream_splitter::remove_logger(int type) - { - streams_container::iterator it = m_log_streams.begin(); - for(;it!=m_log_streams.end(); it++) - { - if(it->first->get_type() == type) - { - delete it->first; - m_log_streams.erase(it); - return true; - } - } - return false; - } - //---------------------------------------------------------------------------- - std::string get_daytime_string2() - { - boost::posix_time::ptime p = boost::posix_time::microsec_clock::local_time(); - return misc_utils::get_time_str_v3(p); - } - //---------------------------------------------------------------------------- - std::string get_day_time_string() - { - return get_daytime_string2(); - //time_t tm = 0; - //time(&tm); - //return misc_utils::get_time_str(tm); - } - //---------------------------------------------------------------------------- - std::string get_time_string() - { - return get_daytime_string2(); - } - //---------------------------------------------------------------------------- -#ifdef _MSC_VER - std::string get_time_string_adv(SYSTEMTIME* pst/* = NULL*/) - { - SYSTEMTIME st = {0}; - if(!pst) - { - pst = &st; - GetSystemTime(&st); - } - std::stringstream str_str; - str_str.fill('0'); - str_str << std::setw(2) << pst->wHour << "_" - << std::setw(2) << pst->wMinute << "_" - << std::setw(2) << pst->wSecond << "_" - << std::setw(3) << pst->wMilliseconds; - return str_str.str(); - } -#endif - //---------------------------------------------------------------------------- - logger::logger() - { - CRITICAL_REGION_BEGIN(m_critical_sec); - init(); - CRITICAL_REGION_END(); - } - //---------------------------------------------------------------------------- - bool logger::set_max_logfile_size(uint64_t max_size) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_log_target.set_max_logfile_size(max_size); - CRITICAL_REGION_END(); - return true; - } - //---------------------------------------------------------------------------- - bool logger::set_log_rotate_cmd(const std::string& cmd) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_log_target.set_log_rotate_cmd(cmd); - CRITICAL_REGION_END(); - return true; - } - //---------------------------------------------------------------------------- - bool logger::take_away_journal(std::list& journal) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_journal.swap(journal); - CRITICAL_REGION_END(); - return true; - } - //---------------------------------------------------------------------------- - bool logger::do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal/* = false*/, const char* plog_name/* = NULL*/) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_log_target.do_log_message(rlog_mes, log_level, color, plog_name); - if(add_to_journal) - m_journal.push_back(rlog_mes); - - return true; - CRITICAL_REGION_END(); - } - //---------------------------------------------------------------------------- - bool logger::add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit/* = LOG_LEVEL_4*/) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - return m_log_target.add_logger( type, pdefault_file_name, pdefault_log_folder, log_level_limit); - CRITICAL_REGION_END(); - } - //---------------------------------------------------------------------------- - bool logger::add_logger( ibase_log_stream* pstream, int log_level_limit/* = LOG_LEVEL_4*/) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - return m_log_target.add_logger(pstream, log_level_limit); - CRITICAL_REGION_END(); - } - //---------------------------------------------------------------------------- - bool logger::remove_logger(int type) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - return m_log_target.remove_logger(type); - CRITICAL_REGION_END(); - } - //---------------------------------------------------------------------------- - bool logger::set_thread_prefix(const std::string& prefix) - { - CRITICAL_REGION_BEGIN(m_critical_sec); - m_thr_prefix_strings[misc_utils::get_thread_string_id()] = prefix; - CRITICAL_REGION_END(); - return true; - } - //---------------------------------------------------------------------------- - bool logger::init() - { - m_process_name = string_tools::get_current_module_name(); - - init_log_path_by_default(); - - //init default set of loggers - init_default_loggers(); - - std::stringstream ss; - ss << get_time_string() << " Init logging. Level=" << get_set_log_detalisation_level() - << " Log path=" << m_default_log_folder << std::endl; - this->do_log_message(ss.str(), console_color_white, LOG_LEVEL_0); - return true; - } - //---------------------------------------------------------------------------- - bool logger::init_default_loggers() - { - return true; - } - //---------------------------------------------------------------------------- - bool logger::init_log_path_by_default() - { - //load process name - m_default_log_folder = string_tools::get_current_module_folder(); - - m_default_log_file = m_process_name; - std::string::size_type a = m_default_log_file.rfind('.'); - if ( a != std::string::npos ) - m_default_log_file.erase( a, m_default_log_file.size()); - m_default_log_file += ".log"; - - return true; - } - //---------------------------------------------------------------------------- - int log_singletone::get_log_detalisation_level() - { - get_or_create_instance();//to initialize logger, if it not initialized - return get_set_log_detalisation_level(); - } - //---------------------------------------------------------------------------- - bool log_singletone::is_filter_error(int error_code) - { - return false; - } - //---------------------------------------------------------------------------- - bool log_singletone::do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name/* = NULL*/) - { - logger* plogger = get_or_create_instance(); - bool res = false; - if(plogger) - res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); - else - { //globally uninitialized, create new logger for each call of do_log_message() and then delete it - plogger = new logger(); - //TODO: some extra initialization - res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); - delete plogger; - plogger = NULL; - } - return res; - } - //---------------------------------------------------------------------------- - bool log_singletone::take_away_journal(std::list& journal) - { - logger* plogger = get_or_create_instance(); - bool res = false; - if(plogger) - res = plogger->take_away_journal(journal); - - return res; - } - //---------------------------------------------------------------------------- - bool log_singletone::set_max_logfile_size(uint64_t file_size) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->set_max_logfile_size(file_size); - } - //---------------------------------------------------------------------------- - bool log_singletone::set_log_rotate_cmd(const std::string& cmd) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->set_log_rotate_cmd(cmd); - } - //---------------------------------------------------------------------------- - bool log_singletone::add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit/* = LOG_LEVEL_4*/) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->add_logger(type, pdefault_file_name, pdefault_log_folder, log_level_limit); - } - //---------------------------------------------------------------------------- - std::string log_singletone::get_default_log_file() - { - logger* plogger = get_or_create_instance(); - if(plogger) - return plogger->get_default_log_file(); - - return ""; - } - //---------------------------------------------------------------------------- - std::string log_singletone::get_default_log_folder() - { - logger* plogger = get_or_create_instance(); - if(plogger) - return plogger->get_default_log_folder(); - - return ""; - } - //---------------------------------------------------------------------------- - bool log_singletone::add_logger(ibase_log_stream* pstream, int log_level_limit/* = LOG_LEVEL_4*/) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->add_logger(pstream, log_level_limit); - } - //---------------------------------------------------------------------------- - bool log_singletone::remove_logger(int type) - { - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->remove_logger(type); - } - //---------------------------------------------------------------------------- -PUSH_WARNINGS -DISABLE_GCC_WARNING(maybe-uninitialized) - int log_singletone::get_set_log_detalisation_level(bool is_need_set/* = false*/, int log_level_to_set/* = LOG_LEVEL_1*/) - { - static int log_detalisation_level = LOG_LEVEL_1; - if(is_need_set) - log_detalisation_level = log_level_to_set; - return log_detalisation_level; - } -POP_WARNINGS - //---------------------------------------------------------------------------- - int log_singletone::get_set_time_level(bool is_need_set/* = false*/, int time_log_level/* = LOG_LEVEL_0*/) - { - static int val_time_log_level = LOG_LEVEL_0; - if(is_need_set) - val_time_log_level = time_log_level; - - return val_time_log_level; - } - //---------------------------------------------------------------------------- - int log_singletone::get_set_process_level(bool is_need_set/* = false*/, int process_log_level/* = LOG_LEVEL_0*/) - { - static int val_process_log_level = LOG_LEVEL_0; - if(is_need_set) - val_process_log_level = process_log_level; - - return val_process_log_level; - } - //---------------------------------------------------------------------------- - bool log_singletone::get_set_need_thread_id(bool is_need_set/* = false*/, bool is_need_val/* = false*/) - { - static bool is_need = false; - if(is_need_set) - is_need = is_need_val; - - return is_need; - } - //---------------------------------------------------------------------------- - bool log_singletone::get_set_need_proc_name(bool is_need_set/* = false*/, bool is_need_val/* = false*/) - { - static bool is_need = true; - if(is_need_set) - is_need = is_need_val; - - return is_need; - } - //---------------------------------------------------------------------------- - uint64_t log_singletone::get_set_err_count(bool is_need_set/* = false*/, uint64_t err_val/* = false*/) - { - static uint64_t err_count = 0; - if(is_need_set) - err_count = err_val; - - return err_count; - } - //---------------------------------------------------------------------------- -#ifdef _MSC_VER - void log_singletone::SetThreadName( DWORD dwThreadID, const char* threadName) - { -#define MS_VC_EXCEPTION 0x406D1388 - -#pragma pack(push,8) - typedef struct tagTHREADNAME_INFO - { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - } THREADNAME_INFO; -#pragma pack(pop) - - Sleep(10); - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = (char*)threadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); - } - __except(EXCEPTION_EXECUTE_HANDLER) - { - } - } -#endif - //---------------------------------------------------------------------------- - bool log_singletone::set_thread_log_prefix(const std::string& prefix) - { -#ifdef _MSC_VER - SetThreadName(-1, prefix.c_str()); -#endif - - logger* plogger = get_or_create_instance(); - if(!plogger) return false; - return plogger->set_thread_prefix(prefix); - } - //---------------------------------------------------------------------------- - std::string log_singletone::get_prefix_entry() - { - std::stringstream str_prefix; - //write time entry - if ( get_set_time_level() <= get_set_log_detalisation_level() ) - str_prefix << get_day_time_string() << " "; - - //write process info - logger* plogger = get_or_create_instance(); - //bool res = false; - if(!plogger) - { //globally uninitialized, create new logger for each call of get_prefix_entry() and then delete it - plogger = new logger(); - } - - //if ( get_set_need_proc_name() && get_set_process_level() <= get_set_log_detalisation_level() ) - // str_prefix << "[" << plogger->m_process_name << " (id=" << GetCurrentProcessId() << ")] "; -//#ifdef _MSC_VER_EX - if ( get_set_need_thread_id() /*&& get_set_tid_level() <= get_set_log_detalisation_level()*/ ) - str_prefix << "tid:" << misc_utils::get_thread_string_id() << " "; -//#endif - - if(plogger->m_thr_prefix_strings.size()) - { - CRITICAL_REGION_LOCAL(plogger->m_critical_sec); - std::string thr_str = misc_utils::get_thread_string_id(); - std::map::iterator it = plogger->m_thr_prefix_strings.find(thr_str); - if(it!=plogger->m_thr_prefix_strings.end()) - { - str_prefix << it->second; - } - } - - if(get_set_is_uninitialized()) - delete plogger; - - return str_prefix.str(); - } - //---------------------------------------------------------------------------- - bool log_singletone::init() - { - return true;/*do nothing here*/ - } - //---------------------------------------------------------------------------- - bool log_singletone::un_init() - { - //delete object - logger* plogger = get_set_instance_internal(); - if(plogger) delete plogger; - //set uninitialized - get_set_is_uninitialized(true, true); - get_set_instance_internal(true, NULL); - return true; - } - //---------------------------------------------------------------------------- - logger* log_singletone::get_or_create_instance() - { - logger* plogger = get_set_instance_internal(); - if(!plogger) - if(!get_set_is_uninitialized()) - get_set_instance_internal(true, plogger = new logger); - - return plogger; - } - //---------------------------------------------------------------------------- - logger* log_singletone::get_set_instance_internal(bool is_need_set/* = false*/, logger* pnew_logger_val/* = NULL*/) - { - static logger* val_plogger = NULL; - - if(is_need_set) - val_plogger = pnew_logger_val; - - return val_plogger; - } - //---------------------------------------------------------------------------- - bool log_singletone::get_set_is_uninitialized(bool is_need_set/* = false*/, bool is_uninitialized/* = false*/) - { - static bool val_is_uninitialized = false; - - if(is_need_set) - val_is_uninitialized = is_uninitialized; - - return val_is_uninitialized; - } - //---------------------------------------------------------------------------- - log_frame::log_frame(const std::string& name, int dlevel/* = LOG_LEVEL_2*/, const char* plog_name/* = NULL*/) - { -#ifdef _MSC_VER - int lasterr=::GetLastError(); -#endif - m_plog_name = plog_name; - if ( dlevel <= log_singletone::get_log_detalisation_level() ) - { - m_name = name; - std::stringstream ss; - ss << log_space::log_singletone::get_prefix_entry() << "-->>" << m_name << std::endl; - log_singletone::do_log_message(ss.str(), dlevel, console_color_default, false, m_plog_name); - } - m_level = dlevel; -#ifdef _MSC_VER - ::SetLastError(lasterr); -#endif - } - //---------------------------------------------------------------------------- - log_frame::~log_frame() - { -#ifdef _MSC_VER - int lasterr=::GetLastError(); -#endif - - if (m_level <= log_singletone::get_log_detalisation_level() ) - { - std::stringstream ss; - ss << log_space::log_singletone::get_prefix_entry() << "<<--" << m_name << std::endl; - log_singletone::do_log_message(ss.str(), m_level, console_color_default, false,m_plog_name); - } -#ifdef _MSC_VER - ::SetLastError(lasterr); -#endif - } - //---------------------------------------------------------------------------- - std::string get_win32_err_descr(int err_no) - { -#ifdef _MSC_VER - LPVOID lpMsgBuf; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - err_no, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (char*) &lpMsgBuf, - 0, NULL ); - - std::string fix_sys_message = "(null)"; - if(lpMsgBuf) fix_sys_message = (char*)lpMsgBuf; - std::string::size_type a; - if ( (a = fix_sys_message.rfind( '\n' )) != std::string::npos ) - fix_sys_message.erase(a); - if ( (a = fix_sys_message.rfind( '\r' )) != std::string::npos ) - fix_sys_message.erase(a); - - LocalFree(lpMsgBuf); - return fix_sys_message; -#else - return "Not implemented yet"; -#endif - } - //---------------------------------------------------------------------------- - bool getwin32_err_text(std::stringstream& ref_message, int error_no) - { - ref_message << "win32 error:" << get_win32_err_descr(error_no); - return true; - } -} -} diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h deleted file mode 100644 index 918bfcf2d2..0000000000 --- a/contrib/epee/include/misc_log_ex.h +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#ifndef _MISC_LOG_EX_H_ -#define _MISC_LOG_EX_H_ - -#if defined(WIN32) -#if !defined(WIN32_LEAN_AND_MEAN) -#define WIN32_LEAN_AND_MEAN -#endif -#endif - -#include -#include -#include -#include -#include - -#include "misc_os_dependent.h" -#include "static_initializer.h" -#include "syncobj.h" -#include "warnings.h" - - -#define LOG_LEVEL_SILENT -1 -#define LOG_LEVEL_0 0 -#define LOG_LEVEL_1 1 -#define LOG_LEVEL_2 2 -#define LOG_LEVEL_3 3 -#define LOG_LEVEL_4 4 -#define LOG_LEVEL_MIN LOG_LEVEL_SILENT -#define LOG_LEVEL_MAX LOG_LEVEL_4 - - -#define LOGGER_NULL 0 -#define LOGGER_FILE 1 -#define LOGGER_DEBUGGER 2 -#define LOGGER_CONSOLE 3 -#define LOGGER_DUMP 4 - - -#ifndef LOCAL_ASSERT -#include -#if (defined _MSC_VER) -#define LOCAL_ASSERT(expr) {if(epee::debug::get_set_enable_assert()){_ASSERTE(expr);}} -#else -#define LOCAL_ASSERT(expr) -#endif - -#endif - -namespace epee -{ -namespace debug -{ - inline bool get_set_enable_assert(bool set = false, bool v = false) - { - static bool e = true; - if(set) - e = v; - return e; - } -} -namespace log_space -{ - class logger; - class log_message; - class log_singletone; - - /************************************************************************/ - /* */ - /************************************************************************/ - enum console_colors - { - console_color_default, - console_color_white, - console_color_red, - console_color_green, - console_color_blue, - console_color_cyan, - console_color_magenta, - console_color_yellow - }; - - - struct ibase_log_stream - { - ibase_log_stream() {} - virtual ~ibase_log_stream() {} - virtual bool out_buffer(const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL)=0; - virtual int get_type() const { return 0; } - - virtual bool set_max_logfile_size(uint64_t max_size) { return true; } - virtual bool set_log_rotate_cmd(const std::string& cmd) { return true; } - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - struct delete_ptr - { - template - void operator() (P p) - { - delete p.first; - } - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - - bool is_stdout_a_tty(); - void set_console_color(int color, bool bright); - void reset_console_color(); - bool rotate_log_file(const char* pfile_path); - - //------------------------------------------------------------------------ -#define max_dbg_str_len 80 -#ifdef _MSC_VER - class debug_output_stream: public ibase_log_stream - { - virtual bool out_buffer(const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) override; - }; -#endif - - class console_output_stream: public ibase_log_stream - { -#ifdef _MSC_VER - bool m_have_to_kill_console; -#endif - - public: - console_output_stream(); - virtual ~console_output_stream(); - - virtual int get_type() const override { return LOGGER_CONSOLE; } - virtual bool out_buffer(const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) override; - }; - - //--------------------------------------------------------------------------// - class file_output_stream : public ibase_log_stream - { - public: - typedef std::map named_log_streams; - - file_output_stream(std::string default_log_file_name, std::string log_path); - ~file_output_stream(); - - private: - named_log_streams m_log_file_names; - std::string m_default_log_path; - std::ofstream* m_pdefault_file_stream; - std::string m_log_rotate_cmd; - std::string m_default_log_filename; - uint64_t m_max_logfile_size; - - virtual int get_type() const override { return LOGGER_FILE; } - virtual bool out_buffer(const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL) override; - virtual bool set_max_logfile_size(uint64_t max_size) override; - virtual bool set_log_rotate_cmd(const std::string& cmd) override; - - std::ofstream* add_new_stream_and_open(const char* pstream_name); - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - class log_stream_splitter - { - public: - typedef std::list > streams_container; - - log_stream_splitter() { } - ~log_stream_splitter(); - - bool set_max_logfile_size(uint64_t max_size); - bool set_log_rotate_cmd(const std::string& cmd); - bool do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name = NULL); - bool add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4); - bool add_logger(ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4); - bool remove_logger(int type); - - private: - streams_container m_log_streams; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); - int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); - bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); - bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); - - std::string get_daytime_string2(); - std::string get_day_time_string(); - std::string get_time_string(); - -#ifdef _MSC_VER - inline std::string get_time_string_adv(SYSTEMTIME* pst = NULL); -#endif - - class logger - { - public: - friend class log_singletone; - - logger(); - ~logger() { } - - bool set_max_logfile_size(uint64_t max_size); - bool set_log_rotate_cmd(const std::string& cmd); - bool take_away_journal(std::list& journal); - bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal = false, const char* plog_name = NULL); - bool add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder , int log_level_limit = LOG_LEVEL_4); - bool add_logger(ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4); - bool remove_logger(int type); - bool set_thread_prefix(const std::string& prefix); - std::string get_default_log_file() { return m_default_log_file; } - std::string get_default_log_folder() { return m_default_log_folder; } - - private: - bool init(); - bool init_default_loggers(); - bool init_log_path_by_default(); - - log_stream_splitter m_log_target; - - std::string m_default_log_folder; - std::string m_default_log_file; - std::string m_process_name; - std::map m_thr_prefix_strings; - std::list m_journal; - critical_section m_critical_sec; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - class log_singletone - { - public: - friend class initializer; - friend class logger; - - static int get_log_detalisation_level(); - static bool is_filter_error(int error_code); - static bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name = NULL); - static bool take_away_journal(std::list& journal); - static bool set_max_logfile_size(uint64_t file_size); - static bool set_log_rotate_cmd(const std::string& cmd); - static bool add_logger(int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4); - static std::string get_default_log_file(); - static std::string get_default_log_folder(); - static bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4); - static bool remove_logger(int type); - -PUSH_WARNINGS -DISABLE_GCC_WARNING(maybe-uninitialized) - static int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); -POP_WARNINGS - - static int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); - static int get_set_process_level(bool is_need_set = false, int process_log_level = LOG_LEVEL_0); - static bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); - static bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); - static uint64_t get_set_err_count(bool is_need_set = false, uint64_t err_val = false); - -#ifdef _MSC_VER - static void SetThreadName( DWORD dwThreadID, const char* threadName); -#endif - - static bool set_thread_log_prefix(const std::string& prefix); - static std::string get_prefix_entry(); - - private: - log_singletone() { } //restric to create an instance - //static initializer m_log_initializer;//must be in one .cpp file (for example main.cpp) via DEFINE_LOGGING macro - - static bool init(); - static bool un_init(); - - static logger* get_or_create_instance(); - static logger* get_set_instance_internal(bool is_need_set = false, logger* pnew_logger_val = NULL); - static bool get_set_is_uninitialized(bool is_need_set = false, bool is_uninitialized = false); - }; - - const static initializer log_initializer; - - class log_frame - { - std::string m_name; - int m_level; - const char* m_plog_name; - - public: - log_frame(const std::string& name, int dlevel = LOG_LEVEL_2 , const char* plog_name = NULL); - ~log_frame(); - }; - - inline int get_set_time_level(bool is_need_set, int time_log_level) - { - return log_singletone::get_set_time_level(is_need_set, time_log_level); - } - inline int get_set_log_detalisation_level(bool is_need_set, int log_level_to_set) - { - return log_singletone::get_set_log_detalisation_level(is_need_set, log_level_to_set); - } - inline std::string get_prefix_entry() - { - return log_singletone::get_prefix_entry(); - } - inline bool get_set_need_thread_id(bool is_need_set, bool is_need_val) - { - return log_singletone::get_set_need_thread_id(is_need_set, is_need_val); - } - inline bool get_set_need_proc_name(bool is_need_set, bool is_need_val ) - { - return log_singletone::get_set_need_proc_name(is_need_set, is_need_val); - } - - inline std::string get_win32_err_descr(int err_no); - inline bool getwin32_err_text(std::stringstream& ref_message, int error_no); -} - -#if defined(_DEBUG) || defined(__GNUC__) - #define ENABLE_LOGGING_INTERNAL -#endif - -#if defined(ENABLE_RELEASE_LOGGING) - #define ENABLE_LOGGING_INTERNAL -#endif - - -#if defined(ENABLE_LOGGING_INTERNAL) - -#define LOG_PRINT_NO_PREFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str() , y, epee::log_space::console_color_default, false, log_name);}} - -#define LOG_PRINT_NO_PREFIX_NO_POSTFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} - - -#define LOG_PRINT_NO_POSTFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} - - -#define LOG_PRINT2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} - -#define LOG_PRINT_COLOR2(log_name, x, y, color) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, color, false, log_name);}} - - -#define LOG_PRINT2_JORNAL(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);}} - - -#define LOG_ERROR2(log_name, x) { \ - std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "ERROR " << __FILE__ << ":" << __LINE__ << " " << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name);LOCAL_ASSERT(0); epee::log_space::log_singletone::get_set_err_count(true, epee::log_space::log_singletone::get_set_err_count()+1);} - -#define LOG_FRAME2(log_name, x, y) epee::log_space::log_frame frame(x, y, log_name) - -#define LOG_WARNING2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ - {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "WARNING " << __FILE__ << ":" << __LINE__ << " " << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_red, true, log_name);LOCAL_ASSERT(0); epee::log_space::log_singletone::get_set_err_count(true, epee::log_space::log_singletone::get_set_err_count()+1);}} - -#else - - -#define LOG_PRINT_NO_PREFIX2(log_name, x, y) - -#define LOG_PRINT_NO_PREFIX_NO_POSTFIX2(log_name, x, y) - -#define LOG_PRINT_NO_POSTFIX2(log_name, x, y) - -#define LOG_PRINT_COLOR2(log_name, x, y, color) - -#define LOG_PRINT2_JORNAL(log_name, x, y) - -#define LOG_PRINT2(log_name, x, y) - -#define LOG_ERROR2(log_name, x) - - -#define LOG_FRAME2(log_name, x, y) - -#define LOG_WARNING2(log_name, x, level) - - -#endif - - -#ifndef LOG_DEFAULT_TARGET - #define LOG_DEFAULT_TARGET NULL -#endif - - -#define LOG_PRINT_NO_POSTFIX(mess, level) LOG_PRINT_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level) -#define LOG_PRINT_NO_PREFIX(mess, level) LOG_PRINT_NO_PREFIX2(LOG_DEFAULT_TARGET, mess, level) -#define LOG_PRINT_NO_PREFIX_NO_POSTFIX(mess, level) LOG_PRINT_NO_PREFIX_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level) -#define LOG_PRINT(mess, level) LOG_PRINT2(LOG_DEFAULT_TARGET, mess, level) - -#define LOG_PRINT_COLOR(mess, level, color) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, color) -#define LOG_PRINT_RED(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_red) -#define LOG_PRINT_GREEN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_green) -#define LOG_PRINT_BLUE(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_blue) -#define LOG_PRINT_YELLOW(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_yellow) -#define LOG_PRINT_CYAN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_cyan) -#define LOG_PRINT_MAGENTA(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_magenta) - -#define LOG_PRINT_RED_L0(mess) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, LOG_LEVEL_0, epee::log_space::console_color_red) - -#define LOG_PRINT_L0(mess) LOG_PRINT(mess, LOG_LEVEL_0) -#define LOG_PRINT_L1(mess) LOG_PRINT(mess, LOG_LEVEL_1) -#define LOG_PRINT_L2(mess) LOG_PRINT(mess, LOG_LEVEL_2) -#define LOG_PRINT_L3(mess) LOG_PRINT(mess, LOG_LEVEL_3) -#define LOG_PRINT_L4(mess) LOG_PRINT(mess, LOG_LEVEL_4) -#define LOG_PRINT_J(mess, level) LOG_PRINT2_JORNAL(LOG_DEFAULT_TARGET, mess, level) - -#define LOG_ERROR(mess) LOG_ERROR2(LOG_DEFAULT_TARGET, mess) -#define LOG_FRAME(mess, level) LOG_FRAME2(LOG_DEFAULT_TARGET, mess, level) -#define LOG_VALUE(mess, level) LOG_VALUE2(LOG_DEFAULT_TARGET, mess, level) -#define LOG_ARRAY(mess, level) LOG_ARRAY2(LOG_DEFAULT_TARGET, mess, level) -//#define LOGWIN_PLATFORM_ERROR(err_no) LOGWINDWOS_PLATFORM_ERROR2(LOG_DEFAULT_TARGET, err_no) -#define LOG_SOCKET_ERROR(err_no) LOG_SOCKET_ERROR2(LOG_DEFAULT_TARGET, err_no) -//#define LOGWIN_PLATFORM_ERROR_UNCRITICAL(mess) LOGWINDWOS_PLATFORM_ERROR_UNCRITICAL2(LOG_DEFAULT_TARGET, mess) -#define LOG_WARNING(mess, level) LOG_WARNING2(LOG_DEFAULT_TARGET, mess, level) - -#define ENDL std::endl - -#define TRY_ENTRY() try { -#define CATCH_ENTRY(location, return_val) } \ - catch(const std::exception& ex) \ -{ \ - (void)(ex); \ - LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \ - return return_val; \ -}\ - catch(...)\ -{\ - LOG_ERROR("Exception at [" << location << "], generic exception \"...\"");\ - return return_val; \ -} - -#define CATCH_ENTRY_L0(lacation, return_val) CATCH_ENTRY(lacation, return_val) -#define CATCH_ENTRY_L1(lacation, return_val) CATCH_ENTRY(lacation, return_val) -#define CATCH_ENTRY_L2(lacation, return_val) CATCH_ENTRY(lacation, return_val) -#define CATCH_ENTRY_L3(lacation, return_val) CATCH_ENTRY(lacation, return_val) -#define CATCH_ENTRY_L4(lacation, return_val) CATCH_ENTRY(lacation, return_val) - - -#define ASSERT_MES_AND_THROW(message) {LOG_ERROR(message); std::stringstream ss; ss << message; throw std::runtime_error(ss.str());} -#define CHECK_AND_ASSERT_THROW_MES(expr, message) {if(!(expr)) ASSERT_MES_AND_THROW(message);} - - -#ifndef CHECK_AND_ASSERT -#define CHECK_AND_ASSERT(expr, fail_ret_val) do{if(!(expr)){LOCAL_ASSERT(expr); return fail_ret_val;};}while(0) -#endif - -#define NOTHING - -#ifndef CHECK_AND_ASSERT_MES -#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_ERROR(message); return fail_ret_val;};}while(0) -#endif - -#ifndef CHECK_AND_NO_ASSERT_MES -#define CHECK_AND_NO_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_PRINT_L0(message); /*LOCAL_ASSERT(expr);*/ return fail_ret_val;};}while(0) -#endif - - -#ifndef CHECK_AND_ASSERT_MES_NO_RET -#define CHECK_AND_ASSERT_MES_NO_RET(expr, message) do{if(!(expr)) {LOG_ERROR(message); return;};}while(0) -#endif - - -#ifndef CHECK_AND_ASSERT_MES2 -#define CHECK_AND_ASSERT_MES2(expr, message) do{if(!(expr)) {LOG_ERROR(message); };}while(0) -#endif - -} -#endif //_MISC_LOG_EX_H_ diff --git a/contrib/epee/include/misc_os_dependent.cpp b/contrib/epee/include/misc_os_dependent.cpp deleted file mode 100644 index 3bff65853c..0000000000 --- a/contrib/epee/include/misc_os_dependent.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_os_dependent.h" - -#include - -#include - -#ifdef __MACH__ -#include -#include -#endif - -namespace epee -{ -namespace misc_utils -{ - - uint64_t get_tick_count() - { -#if defined(_MSC_VER) - return ::GetTickCount64(); -#elif defined(__MACH__) - clock_serv_t cclock; - mach_timespec_t mts; - - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - - return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000); -#else - struct timespec ts; - if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000); -#endif - } - - int call_sys_cmd(const std::string& cmd) - { - std::cout << "# " << cmd << std::endl; - - FILE * fp ; - //char tstCommand[] ="ls *"; - char path[1000] = {0}; -#if !defined(__GNUC__) - fp = _popen(cmd.c_str(), "r"); -#else - fp = popen(cmd.c_str(), "r"); -#endif - while ( fgets( path, 1000, fp ) != NULL ) - std::cout << path; - -#if !defined(__GNUC__) - _pclose(fp); -#else - pclose(fp); -#endif - return 0; - } - - std::string get_thread_string_id() - { -#if defined(_MSC_VER) - return boost::lexical_cast(GetCurrentThreadId()); -#elif defined(__GNUC__) - return boost::lexical_cast(pthread_self()); -#endif - } -} -} diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h deleted file mode 100644 index eb4a3a8a75..0000000000 --- a/contrib/epee/include/misc_os_dependent.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#ifdef WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - - #if !defined(NOMINMAX) - #define NOMINMAX 1 - #endif // !defined(NOMINMAX) - - #include -#endif - -namespace epee -{ -namespace misc_utils -{ - uint64_t get_tick_count(); - int call_sys_cmd(const std::string& cmd); - std::string get_thread_string_id(); -} -} diff --git a/contrib/epee/include/net/abstract_tcp_server.h b/contrib/epee/include/net/abstract_tcp_server.h deleted file mode 100644 index c74444c8e0..0000000000 --- a/contrib/epee/include/net/abstract_tcp_server.h +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _ABSTRACT_TCP_SERVER_H_ -#define _ABSTRACT_TCP_SERVER_H_ - -#include -#include -#include -#include "winobj.h" -//#include "threads_helper.h" -#include "net_utils_base.h" - -#pragma comment(lib, "Ws2_32.lib") - -namespace epee -{ -namespace net_utils -{ - /************************************************************************/ - /* */ - /************************************************************************/ - class soket_sender: public i_service_endpoint - { - public: - soket_sender(SOCKET sock):m_sock(sock){} - private: - virtual bool handle_send(const void* ptr, size_t cb) - { - if(cb != send(m_sock, (char*)ptr, (int)cb, 0)) - { - int sock_err = WSAGetLastError(); - LOG_ERROR("soket_sender: Failed to send " << cb << " bytes, Error=" << sock_err); - return false; - } - return true; - - } - - SOCKET m_sock; - }; - - - - /************************************************************************/ - /* */ - /************************************************************************/ - template - class abstract_tcp_server - { - public: - abstract_tcp_server(); - - bool init_server(int port_no); - bool deinit_server(); - bool run_server(); - bool send_stop_signal(); - - typename THandler::config_type& get_config_object(){return m_config;} - - private: - bool invoke_connection(SOCKET hnew_sock, long ip_from, int post_from); - static unsigned __stdcall ConnectionHandlerProc(void* lpParameter); - - class thread_context; - typedef std::list connections_container; - typedef typename connections_container::iterator connections_iterator; - - struct thread_context - { - HANDLE m_htread; - SOCKET m_socket; - abstract_tcp_server* powner; - connection_context m_context; - typename connections_iterator m_self_it; - }; - - SOCKET m_listen_socket; - int m_port; - bool m_initialized; - volatile LONG m_stop_server; - volatile LONG m_threads_count; - typename THandler::config_type m_config; - connections_container m_connections; - critical_section m_connections_lock; - }; - - template - unsigned __stdcall abstract_tcp_server::ConnectionHandlerProc(void* lpParameter) - { - - thread_context* pthread_context = (thread_context*)lpParameter; - if(!pthread_context) - return 0; - abstract_tcp_server* pthis = pthread_context->powner; - - ::InterlockedIncrement(&pthis->m_threads_count); - - ::CoInitialize(NULL); - - - LOG_PRINT("Handler thread STARTED with socket=" << pthread_context->m_socket, LOG_LEVEL_2); - int res = 0; - - soket_sender sndr(pthread_context->m_socket); - THandler srv(&sndr, pthread_context->powner->m_config, pthread_context->m_context); - - - srv.after_init_connection(); - - char buff[1000] = {0}; - std::string ansver; - while ( (res = recv(pthread_context->m_socket, (char*)buff, 1000, 0)) > 0) - { - LOG_PRINT("Data in, " << res << " bytes", LOG_LEVEL_3); - if(!srv.handle_recv(buff, res)) - break; - } - shutdown(pthread_context->m_socket, SD_BOTH); - closesocket(pthread_context->m_socket); - - abstract_tcp_server* powner = pthread_context->powner; - LOG_PRINT("Handler thread with socket=" << pthread_context->m_socket << " STOPPED", LOG_LEVEL_2); - powner->m_connections_lock.lock(); - ::CloseHandle(pthread_context->m_htread); - pthread_context->powner->m_connections.erase(pthread_context->m_self_it); - powner->m_connections_lock.unlock(); - CoUninitialize(); - ::InterlockedDecrement(&pthis->m_threads_count); - return 1; - } - //---------------------------------------------------------------------------------------- - template - abstract_tcp_server::abstract_tcp_server():m_listen_socket(INVALID_SOCKET), - m_initialized(false), - m_stop_server(0), m_port(0), m_threads_count(0) - { - - } - - //---------------------------------------------------------------------------------------- - template - bool abstract_tcp_server::init_server(int port_no) - { - m_port = port_no; - WSADATA wsad = {0}; - int err = ::WSAStartup(MAKEWORD(2,2), &wsad); - if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 ) - { - LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - - m_initialized = true; - - m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); - if(INVALID_SOCKET == m_listen_socket) - { - err = ::WSAGetLastError(); - LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - - int opt = 1; - setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast(&opt), sizeof(int)); - - sockaddr_in adr = {0}; - adr.sin_family = AF_INET; - adr.sin_addr.s_addr = htonl(INADDR_ANY); - adr.sin_port = (u_short)htons(port_no); - - err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr )); - if(SOCKET_ERROR == err ) - { - err = ::WSAGetLastError(); - LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); - deinit_server(); - return false; - } - - ::InterlockedExchange(&m_stop_server, 0); - - return true; - } - //---------------------------------------------------------------------------------------- - template - bool abstract_tcp_server::deinit_server() - { - - if(!m_initialized) - return true; - - if(INVALID_SOCKET != m_listen_socket) - { - shutdown(m_listen_socket, SD_BOTH); - int res = closesocket(m_listen_socket); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - } - m_listen_socket = INVALID_SOCKET; - } - - int res = ::WSACleanup(); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - } - m_initialized = false; - - return true; - } - //---------------------------------------------------------------------------------------- - template - bool abstract_tcp_server::send_stop_signal() - { - InterlockedExchange(&m_stop_server, 1); - return true; - } - //---------------------------------------------------------------------------------------- - template - bool abstract_tcp_server::run_server() - { - int err = listen(m_listen_socket, 10000); - if(SOCKET_ERROR == err ) - { - err = ::WSAGetLastError(); - LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - - LOG_PRINT("Listening port "<< m_port << "...." , LOG_LEVEL_2); - - while(!m_stop_server) - { - sockaddr_in adr_from = {0}; - int adr_len = sizeof(adr_from); - fd_set read_fs = {0}; - read_fs.fd_count = 1; - read_fs.fd_array[0] = m_listen_socket; - TIMEVAL tv = {0}; - tv.tv_usec = 100; - int select_res = select(0, &read_fs, NULL, NULL, &tv); - if(!select_res) - continue; - SOCKET new_sock = WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, NULL, NULL); - LOG_PRINT("Accepted connection on socket=" << new_sock, LOG_LEVEL_2); - invoke_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port); - } - - deinit_server(); - -#define ABSTR_TCP_SRV_WAIT_COUNT_MAX 5000 -#define ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL 1000 - - int wait_count = 0; - - while(m_threads_count && wait_count*1000 < ABSTR_TCP_SRV_WAIT_COUNT_MAX) - { - ::Sleep(ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL); - wait_count++; - } - LOG_PRINT("abstract_tcp_server exit with wait count=" << wait_count*ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL << "(max=" << ABSTR_TCP_SRV_WAIT_COUNT_MAX <<")", LOG_LEVEL_0); - - return true; - } - //---------------------------------------------------------------------------------------- - template - bool abstract_tcp_server::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from) - { - m_connections_lock.lock(); - m_connections.push_back(thread_context()); - m_connections_lock.unlock(); - m_connections.back().m_socket = hnew_sock; - m_connections.back().powner = this; - m_connections.back().m_self_it = --m_connections.end(); - m_connections.back().m_context.m_remote_ip = ip_from; - m_connections.back().m_context.m_remote_port = post_from; - m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); - - return true; - } - //---------------------------------------------------------------------------------------- - - //---------------------------------------------------------------------------------------- - //---------------------------------------------------------------------------------------- -} -} -#endif //_ABSTRACT_TCP_SERVER_H_ diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h deleted file mode 100644 index b8e291c327..0000000000 --- a/contrib/epee/include/net/abstract_tcp_server2.h +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _ABSTRACT_TCP_SERVER2_H_ -#define _ABSTRACT_TCP_SERVER2_H_ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "net_utils_base.h" -#include "syncobj.h" - - -#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 100 - -namespace epee -{ -namespace net_utils -{ - - struct i_connection_filter - { - virtual bool is_remote_ip_allowed(uint32_t adress)=0; - protected: - virtual ~i_connection_filter(){} - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - /// Represents a single connection from a client. - template - class connection - : public boost::enable_shared_from_this >, - private boost::noncopyable, - public i_service_endpoint - { - public: - typedef typename t_protocol_handler::connection_context t_connection_context; - /// Construct a connection with the given io_service. - explicit connection(boost::asio::io_service& io_service, - typename t_protocol_handler::config_type& config, volatile uint32_t& sock_count, i_connection_filter * &pfilter); - - virtual ~connection(); - /// Get the socket associated with the connection. - boost::asio::ip::tcp::socket& socket(); - - /// Start the first asynchronous operation for the connection. - bool start(bool is_income, bool is_multithreaded); - - void get_context(t_connection_context& context_){context_ = context;} - - void call_back_starter(); - private: - //----------------- i_service_endpoint --------------------- - virtual bool do_send(const void* ptr, size_t cb); - virtual bool close(); - virtual bool call_run_once_service_io(); - virtual bool request_callback(); - virtual boost::asio::io_service& get_io_service(); - virtual bool add_ref(); - virtual bool release(); - //------------------------------------------------------ - boost::shared_ptr > safe_shared_from_this(); - bool shutdown(); - /// Handle completion of a read operation. - void handle_read(const boost::system::error_code& e, - std::size_t bytes_transferred); - - /// Handle completion of a write operation. - void handle_write(const boost::system::error_code& e, size_t cb); - - /// Strand to ensure the connection's handlers are not called concurrently. - boost::asio::io_service::strand strand_; - - /// Socket for the connection. - boost::asio::ip::tcp::socket socket_; - - /// Buffer for incoming data. - boost::array buffer_; - - t_connection_context context; - volatile uint32_t m_want_close_connection; - std::atomic m_was_shutdown; - critical_section m_send_que_lock; - std::list m_send_que; - volatile uint32_t& m_ref_sockets_count; - i_connection_filter* &m_pfilter; - volatile bool m_is_multithreaded; - - //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 - critical_section m_self_refs_lock; - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - template - class boosted_tcp_server - : private boost::noncopyable - { - public: - typedef boost::shared_ptr > connection_ptr; - typedef typename t_protocol_handler::connection_context t_connection_context; - /// Construct the server to listen on the specified TCP address and port, and - /// serve up files from the given directory. - boosted_tcp_server(); - explicit boosted_tcp_server(boost::asio::io_service& external_io_service); - ~boosted_tcp_server(); - - bool init_server(uint32_t port, const std::string address = "0.0.0.0"); - bool init_server(const std::string port, const std::string& address = "0.0.0.0"); - - /// Run the server's io_service loop. - bool run_server(size_t threads_count, bool wait = true, const boost::thread::attributes& attrs = boost::thread::attributes()); - - /// wait for service workers stop - bool timed_wait_server_stop(uint64_t wait_mseconds); - - /// Stop the server. - void send_stop_signal(); - - bool is_stop_signal_sent(); - - void set_threads_prefix(const std::string& prefix_name); - - bool deinit_server(){return true;} - - size_t get_threads_count(){return m_threads_count;} - - void set_connection_filter(i_connection_filter* pfilter); - - bool connect(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0"); - template - bool connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0"); - - typename t_protocol_handler::config_type& get_config_object(){return m_config;} - - int get_binded_port(){return m_port;} - - boost::asio::io_service& get_io_service(){return io_service_;} - - struct idle_callback_conext_base - { - virtual ~idle_callback_conext_base(){} - - virtual bool call_handler(){return true;} - - idle_callback_conext_base(boost::asio::io_service& io_serice): - m_timer(io_serice) - {} - boost::asio::deadline_timer m_timer; - uint64_t m_period; - }; - - template - struct idle_callback_conext: public idle_callback_conext_base - { - idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, uint64_t period): - idle_callback_conext_base(io_serice), - m_handler(h) - {this->m_period = period;} - - t_handler m_handler; - virtual bool call_handler() - { - return m_handler(); - } - }; - - 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)); - //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)); - return true; - } - - 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)); - return true; - } - - template - bool async_call(t_handler t_callback) - { - io_service_.post(t_callback); - return true; - } - - protected: - typename t_protocol_handler::config_type m_config; - - private: - /// Run the server's io_service loop. - bool worker_thread(); - /// Handle completion of an asynchronous accept operation. - void handle_accept(const boost::system::error_code& e); - - bool is_thread_worker(); - - /// The io_service used to perform asynchronous operations. - std::unique_ptr m_io_service_local_instance; - boost::asio::io_service& io_service_; - - /// Acceptor used to listen for incoming connections. - boost::asio::ip::tcp::acceptor acceptor_; - - /// The next connection to be accepted. - connection_ptr new_connection_; - std::atomic m_stop_signal_sent; - uint32_t m_port; - volatile uint32_t m_sockets_count; - std::string m_address; - std::string m_thread_name_prefix; - size_t m_threads_count; - i_connection_filter* m_pfilter; - std::vector > m_threads; - boost::thread::id m_main_thread_id; - critical_section m_threads_lock; - volatile uint32_t m_thread_index; - }; -} -} - -#include "abstract_tcp_server2.inl" - -#endif diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl deleted file mode 100644 index 1b34425b5f..0000000000 --- a/contrib/epee/include/net/abstract_tcp_server2.inl +++ /dev/null @@ -1,817 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "net_utils_base.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "include_base_utils.h" -#include "misc_language.h" -#include "pragma_comp_defs.h" - -PRAGMA_WARNING_PUSH -namespace epee -{ -namespace net_utils -{ - /************************************************************************/ - /* */ - /************************************************************************/ -PRAGMA_WARNING_DISABLE_VS(4355) - - template - connection::connection(boost::asio::io_service& io_service, - typename t_protocol_handler::config_type& config, volatile uint32_t& sock_count, i_connection_filter* &pfilter) - : strand_(io_service), - socket_(io_service), - m_want_close_connection(0), - m_was_shutdown(0), - m_ref_sockets_count(sock_count), - m_pfilter(pfilter), - m_protocol_handler(this, config, context) - { - boost::interprocess::ipcdetail::atomic_inc32(&m_ref_sockets_count); - } -PRAGMA_WARNING_DISABLE_VS(4355) - //--------------------------------------------------------------------------------- - template - connection::~connection() - { - if(!m_was_shutdown) - { - LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed without shutdown."); - shutdown(); - } - - LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed"); - boost::interprocess::ipcdetail::atomic_dec32(&m_ref_sockets_count); - } - //--------------------------------------------------------------------------------- - template - boost::asio::ip::tcp::socket& connection::socket() - { - return socket_; - } - //--------------------------------------------------------------------------------- - template - boost::shared_ptr > connection::safe_shared_from_this() - { - try - { - return connection::shared_from_this(); - } - catch (const boost::bad_weak_ptr&) - { - // It happens when the connection is being deleted - return boost::shared_ptr >(); - } - } - //--------------------------------------------------------------------------------- - template - bool connection::start(bool is_income, bool is_multithreaded) - { - TRY_ENTRY(); - - // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted - auto self = safe_shared_from_this(); - if(!self) - return false; - - m_is_multithreaded = is_multithreaded; - - boost::system::error_code ec; - auto remote_ep = socket_.remote_endpoint(ec); - CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value()); - - auto local_ep = socket_.local_endpoint(ec); - CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value()); - - context = boost::value_initialized(); - long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong()); - - context.set_details(boost::uuids::random_generator()(), ip_, remote_ep.port(), is_income); - LOG_PRINT_L3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << - " to " << local_ep.address().to_string() << ':' << local_ep.port() << - ", total sockets objects " << m_ref_sockets_count); - - if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip)) - { - LOG_PRINT_L2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection"); - close(); - return false; - } - - m_protocol_handler.after_init_connection(); - - socket_.async_read_some(boost::asio::buffer(buffer_), - strand_.wrap( - boost::bind(&connection::handle_read, self, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - - return true; - - CATCH_ENTRY_L0("connection::start()", false); - } - //--------------------------------------------------------------------------------- - template - bool connection::request_callback() - { - TRY_ENTRY(); - LOG_PRINT_L2("[" << print_connection_context_short(context) << "] request_callback"); - // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted - auto self = safe_shared_from_this(); - if(!self) - return false; - - strand_.post(boost::bind(&connection::call_back_starter, self)); - CATCH_ENTRY_L0("connection::request_callback()", false); - return true; - } - //--------------------------------------------------------------------------------- - template - boost::asio::io_service& connection::get_io_service() - { - return socket_.get_io_service(); - } - //--------------------------------------------------------------------------------- - template - bool connection::add_ref() - { - TRY_ENTRY(); - LOG_PRINT_L4("[sock " << socket_.native_handle() << "] add_ref"); - CRITICAL_REGION_LOCAL(m_self_refs_lock); - - // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted - auto self = safe_shared_from_this(); - if(!self) - return false; - if(m_was_shutdown) - return false; - m_self_refs.push_back(self); - return true; - CATCH_ENTRY_L0("connection::add_ref()", false); - } - //--------------------------------------------------------------------------------- - template - bool connection::release() - { - TRY_ENTRY(); - boost::shared_ptr > back_connection_copy; - LOG_PRINT_L4("[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(); - CRITICAL_REGION_END(); - return true; - CATCH_ENTRY_L0("connection::release()", false); - } - //--------------------------------------------------------------------------------- - template - void connection::call_back_starter() - { - TRY_ENTRY(); - LOG_PRINT_L2("[" << print_connection_context_short(context) << "] fired_callback"); - m_protocol_handler.handle_qued_callback(); - CATCH_ENTRY_L0("connection::call_back_starter()", void()); - } - //--------------------------------------------------------------------------------- - template - void connection::handle_read(const boost::system::error_code& e, - std::size_t bytes_transferred) - { - TRY_ENTRY(); - LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async read calledback."); - - if (!e) - { - LOG_PRINT("[sock " << socket_.native_handle() << "] RECV " << bytes_transferred, LOG_LEVEL_4); - context.m_last_recv = time(NULL); - context.m_recv_cnt += bytes_transferred; - bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred); - if(!recv_res) - { - LOG_PRINT("[sock " << socket_.native_handle() << "] protocol_want_close", LOG_LEVEL_4); - - //some error in protocol, protocol handler ask to close connection - boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); - bool do_shutdown = false; - CRITICAL_REGION_BEGIN(m_send_que_lock); - if(!m_send_que.size()) - do_shutdown = true; - CRITICAL_REGION_END(); - if(do_shutdown) - shutdown(); - }else - { - socket_.async_read_some(boost::asio::buffer(buffer_), - strand_.wrap( - boost::bind(&connection::handle_read, connection::shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - LOG_PRINT_L4("[sock " << socket_.native_handle() << "]Async read requested."); - } - }else - { - LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some not success at read: " << e.message() << ':' << e.value()); - if(e.value() != 2) - { - LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value()); - shutdown(); - } - } - // If an error occurs then no new asynchronous operations are started. This - // means that all shared_ptr references to the connection object will - // disappear and the object will be destroyed automatically after this - // handler returns. The connection class's destructor closes the socket. - CATCH_ENTRY_L0("connection::handle_read", void()); - } - //--------------------------------------------------------------------------------- - template - bool connection::call_run_once_service_io() - { - TRY_ENTRY(); - if(!m_is_multithreaded) - { - //single thread model, we can wait in blocked call - size_t cnt = socket_.get_io_service().run_one(); - if(!cnt)//service is going to quit - return false; - }else - { - //multi thread model, we can't(!) wait in blocked call - //so we make non blocking call and releasing CPU by calling sleep(0); - //if no handlers were called - //TODO: Maybe we need to have have critical section + event + callback to upper protocol to - //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); - } - - return true; - CATCH_ENTRY_L0("connection::call_run_once_service_io", false); - } - //--------------------------------------------------------------------------------- - template - bool connection::do_send(const void* ptr, size_t cb) - { - TRY_ENTRY(); - // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted - auto self = safe_shared_from_this(); - if(!self) - return false; - if(m_was_shutdown) - return false; - - LOG_PRINT("[sock " << socket_.native_handle() << "] SEND " << cb, LOG_LEVEL_4); - context.m_last_send = time(NULL); - context.m_send_cnt += cb; - //some data should be wrote to stream - //request complete - - epee::critical_region_t send_guard(m_send_que_lock); - if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) - { - send_guard.unlock(); - LOG_WARNING("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection", LOG_LEVEL_2); - close(); - return false; - } - - m_send_que.resize(m_send_que.size()+1); - m_send_que.back().assign((const char*)ptr, cb); - - if(m_send_que.size() > 1) - { - //active operation should be in progress, nothing to do, just wait last operation callback - }else - { - //no active operation - if(m_send_que.size()!=1) - { - LOG_ERROR("Looks like no active operations, but send que size != 1!!"); - return false; - } - - boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()), - //strand_.wrap( - boost::bind(&connection::handle_write, self, _1, _2) - //) - ); - - LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size()); - } - - return true; - - CATCH_ENTRY_L0("connection::do_send", false); - } - //--------------------------------------------------------------------------------- - template - bool connection::shutdown() - { - // Initiate graceful connection closure. - 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(); - return true; - } - //--------------------------------------------------------------------------------- - template - bool connection::close() - { - TRY_ENTRY(); - LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Que Shutdown called."); - size_t send_que_size = 0; - CRITICAL_REGION_BEGIN(m_send_que_lock); - send_que_size = m_send_que.size(); - CRITICAL_REGION_END(); - boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); - if(!send_que_size) - { - shutdown(); - } - - return true; - CATCH_ENTRY_L0("connection::close", false); - } - //--------------------------------------------------------------------------------- - template - void connection::handle_write(const boost::system::error_code& e, size_t cb) - { - TRY_ENTRY(); - LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send calledback " << cb); - - if (e) - { - LOG_PRINT_L0("[sock " << socket_.native_handle() << "] Some problems at write: " << e.message() << ':' << e.value()); - shutdown(); - return; - } - - bool do_shutdown = false; - CRITICAL_REGION_BEGIN(m_send_que_lock); - if(m_send_que.empty()) - { - LOG_ERROR("[sock " << socket_.native_handle() << "] m_send_que.size() == 0 at handle_write!"); - return; - } - - m_send_que.pop_front(); - if(m_send_que.empty()) - { - if(boost::interprocess::ipcdetail::atomic_read32(&m_want_close_connection)) - { - do_shutdown = true; - } - }else - { - //have more data to send - boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()), - //strand_.wrap( - boost::bind(&connection::handle_write, connection::shared_from_this(), _1, _2)); - //); - } - CRITICAL_REGION_END(); - - if(do_shutdown) - { - shutdown(); - } - CATCH_ENTRY_L0("connection::handle_write", void()); - } - /************************************************************************/ - /* */ - /************************************************************************/ - template - boosted_tcp_server::boosted_tcp_server(): - m_io_service_local_instance(new boost::asio::io_service()), - io_service_(*m_io_service_local_instance.get()), - acceptor_(io_service_), - new_connection_(new connection(io_service_, m_config, m_sockets_count, m_pfilter)), - m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0) - { - m_thread_name_prefix = "NET"; - } - - template - boosted_tcp_server::boosted_tcp_server(boost::asio::io_service& extarnal_io_service): - io_service_(extarnal_io_service), - acceptor_(io_service_), - new_connection_(new connection(io_service_, m_config, m_sockets_count, m_pfilter)), - m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0) - { - m_thread_name_prefix = "NET"; - } - //--------------------------------------------------------------------------------- - template - boosted_tcp_server::~boosted_tcp_server() - { - this->send_stop_signal(); - timed_wait_server_stop(10000); - } - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::init_server(uint32_t port, const std::string address) - { - TRY_ENTRY(); - m_stop_signal_sent = false; - m_port = port; - m_address = address; - // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast(port)); - boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); - acceptor_.open(endpoint.protocol()); - acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - acceptor_.bind(endpoint); - acceptor_.listen(); - boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint(); - m_port = binded_endpoint.port(); - acceptor_.async_accept(new_connection_->socket(), - boost::bind(&boosted_tcp_server::handle_accept, this, - boost::asio::placeholders::error)); - - return true; - CATCH_ENTRY_L0("boosted_tcp_server::init_server", false); - } - //----------------------------------------------------------------------------- -PUSH_WARNINGS -DISABLE_GCC_WARNING(maybe-uninitialized) - template - bool boosted_tcp_server::init_server(const std::string port, const std::string& address) - { - uint32_t p = 0; - - if (port.size() && !string_tools::get_xtype_from_string(p, port)) { - LOG_ERROR("Failed to convert port no = " << port); - return false; - } - return this->init_server(p, address); - } -POP_WARNINGS - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::worker_thread() - { - TRY_ENTRY(); - uint32_t local_thr_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); - std::string thread_name = std::string("[") + m_thread_name_prefix; - thread_name += boost::to_string(local_thr_index) + "]"; - log_space::log_singletone::set_thread_log_prefix(thread_name); - while(!m_stop_signal_sent) - { - try - { - io_service_.run(); - } - catch(const std::exception& ex) - { - LOG_ERROR("Exception at server worker thread, what=" << ex.what()); - } - catch(...) - { - LOG_ERROR("Exception at server worker thread, unknown execption"); - } - } - LOG_PRINT_L4("Worker thread finished"); - return true; - CATCH_ENTRY_L0("boosted_tcp_server::worker_thread", false); - } - //--------------------------------------------------------------------------------- - template - void boosted_tcp_server::set_threads_prefix(const std::string& prefix_name) - { - m_thread_name_prefix = prefix_name; - } - //--------------------------------------------------------------------------------- - template - void boosted_tcp_server::set_connection_filter(i_connection_filter* pfilter) - { - m_pfilter = pfilter; - } - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::run_server(size_t threads_count, bool wait, const boost::thread::attributes& attrs) - { - TRY_ENTRY(); - m_threads_count = threads_count; - m_main_thread_id = boost::this_thread::get_id(); - log_space::log_singletone::set_thread_log_prefix("[SRV_MAIN]"); - while(!m_stop_signal_sent) - { - - // Create a pool of threads to run all of the io_services. - CRITICAL_REGION_BEGIN(m_threads_lock); - for (std::size_t i = 0; i < threads_count; ++i) - { - boost::shared_ptr thread(new boost::thread( - attrs, boost::bind(&boosted_tcp_server::worker_thread, this))); - m_threads.push_back(thread); - } - CRITICAL_REGION_END(); - // Wait for all threads in the pool to exit. - if(wait) - { - for (std::size_t i = 0; i < m_threads.size(); ++i) - m_threads[i]->join(); - m_threads.clear(); - - }else - { - return true; - } - - if(wait && !m_stop_signal_sent) - { - //some problems with the listening socket ?.. - LOG_PRINT_L0("Net service stopped without stop request, restarting..."); - if(!this->init_server(m_port, m_address)) - { - LOG_PRINT_L0("Reiniting service failed, exit."); - return false; - }else - { - LOG_PRINT_L0("Reiniting OK."); - } - } - } - return true; - CATCH_ENTRY_L0("boosted_tcp_server::run_server", false); - } - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::is_thread_worker() - { - TRY_ENTRY(); - CRITICAL_REGION_LOCAL(m_threads_lock); - BOOST_FOREACH(boost::shared_ptr& thp, m_threads) - { - if(thp->get_id() == boost::this_thread::get_id()) - return true; - } - if(m_threads_count == 1 && boost::this_thread::get_id() == m_main_thread_id) - return true; - return false; - CATCH_ENTRY_L0("boosted_tcp_server::is_thread_worker", false); - } - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::timed_wait_server_stop(uint64_t wait_mseconds) - { - TRY_ENTRY(); - boost::chrono::milliseconds ms(wait_mseconds); - for (std::size_t i = 0; i < m_threads.size(); ++i) - { - if(m_threads[i]->joinable() && !m_threads[i]->try_join_for(ms)) - { - LOG_PRINT_L0("Interrupting thread " << m_threads[i]->native_handle()); - m_threads[i]->interrupt(); - } - } - return true; - CATCH_ENTRY_L0("boosted_tcp_server::timed_wait_server_stop", false); - } - //--------------------------------------------------------------------------------- - template - void boosted_tcp_server::send_stop_signal() - { - m_stop_signal_sent = true; - TRY_ENTRY(); - io_service_.stop(); - CATCH_ENTRY_L0("boosted_tcp_server::send_stop_signal()", void()); - } - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::is_stop_signal_sent() - { - return m_stop_signal_sent; - } - //--------------------------------------------------------------------------------- - template - void boosted_tcp_server::handle_accept(const boost::system::error_code& e) - { - TRY_ENTRY(); - if (!e) - { - connection_ptr conn(std::move(new_connection_)); - - new_connection_.reset(new connection(io_service_, m_config, m_sockets_count, m_pfilter)); - acceptor_.async_accept(new_connection_->socket(), - boost::bind(&boosted_tcp_server::handle_accept, this, - boost::asio::placeholders::error)); - - bool r = conn->start(true, 1 < m_threads_count); - if (!r) - LOG_ERROR("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); - }else - { - LOG_ERROR("Some problems at accept: " << e.message() << ", connections_count = " << m_sockets_count); - } - CATCH_ENTRY_L0("boosted_tcp_server::handle_accept", void()); - } - //--------------------------------------------------------------------------------- - template - bool boosted_tcp_server::connect(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip) - { - TRY_ENTRY(); - - connection_ptr new_connection_l(new connection(io_service_, m_config, m_sockets_count, m_pfilter) ); - boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); - - ////////////////////////////////////////////////////////////////////////// - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port); - boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) - { - LOG_ERROR("Failed to resolve " << adr); - return false; - } - ////////////////////////////////////////////////////////////////////////// - - - //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); - - 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); - sock_.bind(local_endpoint); - } - - /* - NOTICE: be careful to make sync connection from event handler: in case if all threads suddenly do sync connect, there will be no thread to dispatch events from io service. - */ - - boost::system::error_code ec = boost::asio::error::would_block; - - //have another free thread(s), work in wait mode, without event handling - struct local_async_context - { - boost::system::error_code ec; - boost::mutex connect_mut; - boost::condition_variable cond; - }; - - boost::shared_ptr local_shared_context(new local_async_context()); - local_shared_context->ec = boost::asio::error::would_block; - boost::unique_lock lock(local_shared_context->connect_mut); - auto connect_callback = [](boost::system::error_code ec_, boost::shared_ptr shared_context) - { - shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->connect_mut.unlock(); shared_context->cond.notify_one(); - }; - - sock_.async_connect(remote_endpoint, boost::bind(connect_callback, _1, local_shared_context)); - while(local_shared_context->ec == boost::asio::error::would_block) - { - bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout)); - if(local_shared_context->ec == boost::asio::error::would_block && !r) - { - //timeout - sock_.close(); - LOG_PRINT_L3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")"); - return false; - } - } - ec = local_shared_context->ec; - - if (ec || !sock_.is_open()) - { - LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3); - return false; - } - - LOG_PRINT_L3("Connected success to " << adr << ':' << port); - - bool r = new_connection_l->start(false, 1 < m_threads_count); - if (r) - { - new_connection_l->get_context(conn_context); - //new_connection_l.reset(new connection(io_service_, m_config, m_sockets_count, m_pfilter)); - } - else - { - LOG_ERROR("[sock " << new_connection_->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); - } - - return r; - - CATCH_ENTRY_L0("boosted_tcp_server::connect", false); - } - //--------------------------------------------------------------------------------- - template template - bool boosted_tcp_server::connect_async(const std::string& adr, const std::string& port, uint32_t conn_timeout, t_callback cb, const std::string& bind_ip) - { - TRY_ENTRY(); - connection_ptr new_connection_l(new connection(io_service_, m_config, m_sockets_count, m_pfilter) ); - boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); - - ////////////////////////////////////////////////////////////////////////// - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port); - boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) - { - LOG_ERROR("Failed to resolve " << adr); - return false; - } - ////////////////////////////////////////////////////////////////////////// - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); - - 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); - sock_.bind(local_endpoint); - } - - boost::shared_ptr sh_deadline(new boost::asio::deadline_timer(io_service_)); - //start deadline - sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)); - sh_deadline->async_wait([=](const boost::system::error_code& error) - { - if(error != boost::asio::error::operation_aborted) - { - LOG_PRINT_L3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")"); - new_connection_l->socket().close(); - } - }); - //start async connect - sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_) - { - t_connection_context conn_context = AUTO_VAL_INIT(conn_context); - boost::system::error_code ignored_ec; - boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec); - if(!ec_) - {//success - if(!sh_deadline->cancel()) - { - cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation - }else - { - LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port << - " from " << lep.address().to_string() << ':' << lep.port()); - bool r = new_connection_l->start(false, 1 < m_threads_count); - if (r) - { - new_connection_l->get_context(conn_context); - cb(conn_context, ec_); - } - else - { - LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port); - cb(conn_context, boost::asio::error::fault); - } - } - }else - { - LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port << - " from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value()); - cb(conn_context, ec_); - } - }); - return true; - CATCH_ENTRY_L0("boosted_tcp_server::connect_async", false); - } -} -} -PRAGMA_WARNING_POP diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.h b/contrib/epee/include/net/abstract_tcp_server_cp.h deleted file mode 100644 index b6410e120f..0000000000 --- a/contrib/epee/include/net/abstract_tcp_server_cp.h +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _LEVIN_CP_SERVER_H_ -#define _LEVIN_CP_SERVER_H_ - -#include -#include -#include -#include -#include - -#include "misc_log_ex.h" -//#include "threads_helper.h" -#include "syncobj.h" -#define ENABLE_PROFILING -#include "profile_tools.h" -#include "net_utils_base.h" -#include "pragma_comp_defs.h" - -#define LEVIN_DEFAULT_DATA_BUFF_SIZE 2000 - -namespace epee -{ -namespace net_utils -{ - - template - class cp_server_impl//: public abstract_handler - { - public: - cp_server_impl(/*abstract_handler* phandler = NULL*/); - virtual ~cp_server_impl(); - - bool init_server(int port_no); - bool deinit_server(); - bool run_server(int threads_count = 0); - bool send_stop_signal(); - bool is_stop_signal(); - virtual bool on_net_idle(){return true;} - size_t get_active_connections_num(); - typename TProtocol::config_type& get_config_object(){return m_config;} - private: - enum overlapped_operation_type - { - op_type_recv, - op_type_send, - op_type_stop - }; - - struct io_data_base - { - OVERLAPPED m_overlapped; - WSABUF DataBuf; - overlapped_operation_type m_op_type; - DWORD TotalBuffBytes; - volatile LONG m_is_in_use; - char Buffer[1]; - }; - -PRAGMA_WARNING_PUSH -PRAGMA_WARNING_DISABLE_VS(4355) - template - struct connection: public net_utils::i_service_endpoint - { - connection(typename TProtocol::config_type& ref_config):m_sock(INVALID_SOCKET), m_tprotocol_handler(this, ref_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) - { - } - - //connection():m_sock(INVALID_SOCKET), m_tprotocol_handler(this, m_dummy_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) - //{ - //} - - connection& operator=(const connection& obj) - { - return *this; - } - - bool init_buffers() - { - m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; - m_psend_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; - m_precv_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; - m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; - return true; - } - - bool query_shutdown() - { - if(!::InterlockedCompareExchange(&m_asked_to_shutdown, 1, 0)) - { - m_psend_data->m_op_type = op_type_stop; - ::PostQueuedCompletionStatus(m_completion_port, 0, (ULONG_PTR)this, &m_psend_data->m_overlapped); - } - return true; - } - - //bool set_config(typename TProtocol::config_type& config) - //{ - // this->~connection(); - // new(this) connection(config); - // return true; - //} - ~connection() - { - if(m_psend_data) - delete m_psend_data; - - if(m_precv_data) - delete m_precv_data; - } - virtual bool handle_send(const void* ptr, size_t cb) - { - PROFILE_FUNC("[handle_send]"); - if(m_psend_data->TotalBuffBytes < cb) - resize_send_buff((DWORD)cb); - - ZeroMemory(&m_psend_data->m_overlapped, sizeof(OVERLAPPED)); - m_psend_data->DataBuf.len = (u_long)cb;//m_psend_data->TotalBuffBytes; - m_psend_data->DataBuf.buf = m_psend_data->Buffer; - memcpy(m_psend_data->DataBuf.buf, ptr, cb); - m_psend_data->m_op_type = op_type_send; - InterlockedExchange(&m_psend_data->m_is_in_use, 1); - DWORD bytes_sent = 0; - DWORD flags = 0; - int res = 0; - { - PROFILE_FUNC("[handle_send] ::WSASend"); - res = ::WSASend(m_sock, &(m_psend_data->DataBuf), 1, &bytes_sent, flags, &(m_psend_data->m_overlapped), NULL); - } - - if(res == SOCKET_ERROR ) - { - int err = ::WSAGetLastError(); - if(WSA_IO_PENDING == err ) - return true; - } - LOG_ERROR("BIG FAIL: WSASend error code not correct, res=" << res << " last_err=" << err); - ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); - query_shutdown(); - //closesocket(m_psend_data); - return false; - }else if(0 == res) - { - ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); - if(!bytes_sent || bytes_sent != cb) - { - int err = ::WSAGetLastError(); - LOG_ERROR("BIG FAIL: WSASend immediatly complete? but bad results, res=" << res << " last_err=" << err); - query_shutdown(); - return false; - }else - { - return true; - } - } - - return true; - } - bool resize_send_buff(DWORD new_size) - { - if(m_psend_data->TotalBuffBytes >= new_size) - return true; - - delete m_psend_data; - m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + new_size-1]; - m_psend_data->TotalBuffBytes = new_size; - LOG_PRINT("Connection buffer resized up to " << new_size, LOG_LEVEL_3); - return true; - } - - - SOCKET m_sock; - net_utils::connection_context_base context; - TProtocol m_tprotocol_handler; - typename TProtocol::config_type m_dummy_config; - io_data_base* m_precv_data; - io_data_base* m_psend_data; - HANDLE m_completion_port; - volatile LONG m_asked_to_shutdown; - volatile LONG m_connection_shutwoned; - }; -PRAGMA_WARNING_POP - - bool worker_thread_member(); - static unsigned CALLBACK worker_thread(void* param); - - bool add_new_connection(SOCKET new_sock, long ip_from, int port_from); - bool shutdown_connection(connection* pconn); - - - typedef std::map > > connections_container; - SOCKET m_listen_socket; - HANDLE m_completion_port; - connections_container m_connections; - critical_section m_connections_lock; - int m_port; - volatile LONG m_stop; - //abstract_handler* m_phandler; - bool m_initialized; - volatile LONG m_worker_thread_counter; - typename TProtocol::config_type m_config; - }; -} -} -#include "abstract_tcp_server_cp.inl" - - -#endif //_LEVIN_SERVER_H_ diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.inl b/contrib/epee/include/net/abstract_tcp_server_cp.inl deleted file mode 100644 index 5673c50be0..0000000000 --- a/contrib/epee/include/net/abstract_tcp_server_cp.inl +++ /dev/null @@ -1,605 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 comment(lib, "Ws2_32.lib") - -namespace epee -{ -namespace net_utils -{ -template -cp_server_impl::cp_server_impl(): - m_port(0), m_stop(false), - m_worker_thread_counter(0), m_listen_socket(INVALID_SOCKET) -{ -} -//------------------------------------------------------------- -template -cp_server_impl::~cp_server_impl() -{ - deinit_server(); -} -//------------------------------------------------------------- -template -bool cp_server_impl::init_server(int port_no) -{ - m_port = port_no; - - WSADATA wsad = {0}; - int err = ::WSAStartup(MAKEWORD(2,2), &wsad); - if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 ) - { - LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - - m_initialized = true; - - m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); - if(INVALID_SOCKET == m_listen_socket) - { - err = ::WSAGetLastError(); - LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - - - int opt = 1; - err = setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast(&opt), sizeof(int)); - if(SOCKET_ERROR == err ) - { - err = ::WSAGetLastError(); - LOG_PRINT("Failed to setsockopt(SO_REUSEADDR), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); - deinit_server(); - return false; - } - - - sockaddr_in adr = {0}; - adr.sin_family = AF_INET; - adr.sin_addr.s_addr = htonl(INADDR_ANY); - adr.sin_port = (u_short)htons(m_port); - - //binding - err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr )); - if(SOCKET_ERROR == err ) - { - err = ::WSAGetLastError(); - LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); - deinit_server(); - return false; - } - - - m_completion_port = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if(INVALID_HANDLE_VALUE == m_completion_port) - { - err = ::WSAGetLastError(); - LOG_PRINT("Failed to CreateIoCompletionPort, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); - deinit_server(); - return false; - } - - - return true; -} -//------------------------------------------------------------- - -//------------------------------------------------------------- -static int CALLBACK CPConditionFunc( - IN LPWSABUF lpCallerId, - IN LPWSABUF lpCallerData, - IN OUT LPQOS lpSQOS, - IN OUT LPQOS lpGQOS, - IN LPWSABUF lpCalleeId, - OUT LPWSABUF lpCalleeData, - OUT GROUP FAR *g, - IN DWORD_PTR dwCallbackData - ) -{ - - /*cp_server_impl* pthis = (cp_server_impl*)dwCallbackData; - if(!pthis) - return CF_REJECT;*/ - /*if(pthis->get_active_connections_num()>=FD_SETSIZE-1) - { - LOG_PRINT("Maximum connections count overfull.", LOG_LEVEL_2); - return CF_REJECT; - }*/ - - return CF_ACCEPT; -} -//------------------------------------------------------------- -template -size_t cp_server_impl::get_active_connections_num() -{ - return m_connections.size(); -} -//------------------------------------------------------------- -template -unsigned CALLBACK cp_server_impl::worker_thread(void* param) -{ - if(!param) - return 0; - - cp_server_impl* pthis = (cp_server_impl*)param; - pthis->worker_thread_member(); - return 1; -} -//------------------------------------------------------------- -template -bool cp_server_impl::worker_thread_member() -{ - LOG_PRINT("Worker thread STARTED", LOG_LEVEL_1); - bool stop_handling = false; - while(!stop_handling) - { - PROFILE_FUNC("[worker_thread]Worker Loop"); - DWORD bytes_transfered = 0; - connection* pconnection = 0; - io_data_base* pio_data = 0; - - { - PROFILE_FUNC("[worker_thread]GetQueuedCompletionStatus"); - BOOL res = ::GetQueuedCompletionStatus (m_completion_port, &bytes_transfered , (PULONG_PTR)&pconnection, (LPOVERLAPPED *)&pio_data, INFINITE); - if (res == 0) - { - // check return code for error - int err = GetLastError(); - LOG_PRINT("GetQueuedCompletionStatus failed with error " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_1); - - if(pio_data) - ::InterlockedExchange(&pio_data->m_is_in_use, 0); - - - continue; - } - } - - if(pio_data) - ::InterlockedExchange(&pio_data->m_is_in_use, 0); - - - - if(!bytes_transfered && !pconnection && !pio_data) - { - //signal to stop - break; - } - if(!pconnection || !pio_data) - { - LOG_PRINT("BIG FAIL: pconnection or pio_data is empty: pconnection=" << pconnection << " pio_data=" << pio_data, LOG_LEVEL_0); - break; - } - - - - if(::InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)) - { - LOG_ERROR("InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)"); - //DebugBreak(); - } - - if(pio_data->m_op_type == op_type_stop) - { - if(!pconnection) - { - LOG_ERROR("op_type=op_type_stop, but pconnection is empty!!!"); - continue; - } - shutdown_connection(pconnection); - continue;// - } - else if(pio_data->m_op_type == op_type_send) - { - continue; - //do nothing, just queuing request - }else if(pio_data->m_op_type == op_type_recv) - { - PROFILE_FUNC("[worker_thread]m_tprotocol_handler.handle_recv"); - if(bytes_transfered) - { - bool res = pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_transfered); - if(!res) - pconnection->query_shutdown(); - } - else - { - pconnection->query_shutdown(); - continue; - } - - } - - //preparing new request, - - { - PROFILE_FUNC("[worker_thread]RECV Request small loop"); - int res = 0; - while(true) - { - LOG_PRINT("Prepearing data for WSARecv....", LOG_LEVEL_3); - ZeroMemory(&pio_data->m_overlapped, sizeof(OVERLAPPED)); - pio_data->DataBuf.len = pio_data->TotalBuffBytes; - pio_data->DataBuf.buf = pio_data->Buffer; - pio_data->m_op_type = op_type_recv; - //calling WSARecv() and go to completion waiting - DWORD bytes_recvd = 0; - DWORD flags = 0; - - LOG_PRINT("Calling WSARecv....", LOG_LEVEL_3); - ::InterlockedExchange(&pio_data->m_is_in_use, 1); - res = WSARecv(pconnection->m_sock, &(pio_data->DataBuf), 1, &bytes_recvd , &flags, &(pio_data->m_overlapped), NULL); - if(res == SOCKET_ERROR ) - { - int err = ::WSAGetLastError(); - if(WSA_IO_PENDING == err ) - {//go pending, ok - LOG_PRINT("WSARecv return WSA_IO_PENDING", LOG_LEVEL_3); - break; - } - LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err); - ::InterlockedExchange(&pio_data->m_is_in_use, 0); - pconnection->query_shutdown(); - break; - } - break; - /*else if(0 == res) - { - if(!bytes_recvd) - { - ::InterlockedExchange(&pio_data->m_is_in_use, 0); - LOG_PRINT("WSARecv return 0, bytes_recvd=0, graceful close.", LOG_LEVEL_3); - int err = ::WSAGetLastError(); - //LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err); - //pconnection->query_shutdown(); - break; - }else - { - LOG_PRINT("WSARecv return immediatily 0, bytes_recvd=" << bytes_recvd, LOG_LEVEL_3); - //pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_recvd); - } - }*/ - } - } - } - - - LOG_PRINT("Worker thread STOPED", LOG_LEVEL_1); - ::InterlockedDecrement(&m_worker_thread_counter); - return true; -} -//------------------------------------------------------------- -template -bool cp_server_impl::shutdown_connection(connection* pconn) -{ - PROFILE_FUNC("[shutdown_connection]"); - - if(!pconn) - { - LOG_ERROR("Attempt to remove null pptr connection!"); - return false; - } - else - { - LOG_PRINT("Shutting down connection ("<< pconn << ")", LOG_LEVEL_3); - } - m_connections_lock.lock(); - connections_container::iterator it = m_connections.find(pconn->m_sock); - m_connections_lock.unlock(); - if(it == m_connections.end()) - { - LOG_ERROR("Failed to find closing socket=" << pconn->m_sock); - return false; - } - SOCKET sock = it->second->m_sock; - { - PROFILE_FUNC("[shutdown_connection] shutdown, close"); - ::shutdown(it->second->m_sock, SD_SEND ); - } - size_t close_sock_wait_count = 0; - { - LOG_PRINT("Entered to 'in_use wait zone'", LOG_LEVEL_3); - PROFILE_FUNC("[shutdown_connection] wait for in_use"); - while(::InterlockedCompareExchange(&it->second->m_precv_data->m_is_in_use, 1, 1)) - { - - Sleep(100); - close_sock_wait_count++; - } - LOG_PRINT("First step to 'in_use wait zone'", LOG_LEVEL_3); - - - while(::InterlockedCompareExchange(&it->second->m_psend_data->m_is_in_use, 1, 1)) - { - Sleep(100); - close_sock_wait_count++; - } - LOG_PRINT("Leaved 'in_use wait zone'", LOG_LEVEL_3); - } - - ::closesocket(it->second->m_sock); - - ::InterlockedExchange(&it->second->m_connection_shutwoned, 1); - m_connections_lock.lock(); - m_connections.erase(it); - m_connections_lock.unlock(); - LOG_PRINT("Socked " << sock << " closed, wait_count=" << close_sock_wait_count, LOG_LEVEL_2); - return true; -} -//------------------------------------------------------------- -template -bool cp_server_impl::run_server(int threads_count = 0) -{ - int err = listen(m_listen_socket, 100); - if(SOCKET_ERROR == err ) - { - err = ::WSAGetLastError(); - LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - - if(!threads_count) - { - SYSTEM_INFO si = {0}; - ::GetSystemInfo(&si); - threads_count = si.dwNumberOfProcessors + 2; - } - for(int i = 0; i != threads_count; i++) - { - boost::thread(boost::bind(&cp_server_impl::worker_thread_member, this)); - //HANDLE h_thread = threads_helper::create_thread(worker_thread, this); - InterlockedIncrement(&m_worker_thread_counter); - //::CloseHandle(h_thread); - } - - LOG_PRINT("Numbers of worker threads started: " << threads_count, LOG_LEVEL_1); - - m_stop = false; - while(!m_stop) - { - PROFILE_FUNC("[run_server] main_loop"); - TIMEVAL tv = {0}; - tv.tv_sec = 0; - tv.tv_usec = 100; - fd_set sock_set; - sock_set.fd_count = 1; - sock_set.fd_array[0] = m_listen_socket; - int select_res = 0; - { - PROFILE_FUNC("[run_server] select"); - select_res = select(0, &sock_set, &sock_set, NULL, &tv); - } - - if(SOCKET_ERROR == select_res) - { - err = ::WSAGetLastError(); - LOG_ERROR("Failed to select, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - return false; - } - if(!select_res) - { - on_net_idle(); - continue; - } - else - { - sockaddr_in adr_from = {0}; - int adr_len = sizeof(adr_from); - SOCKET new_sock = INVALID_SOCKET; - { - PROFILE_FUNC("[run_server] WSAAccept"); - new_sock = ::WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, CPConditionFunc, (DWORD_PTR)this); - } - - if(INVALID_SOCKET == new_sock) - { - if(m_stop) - break; - int err = ::WSAGetLastError(); - LOG_PRINT("Failed to WSAAccept, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); - continue; - } - LOG_PRINT("Accepted connection (new socket=" << new_sock << ")", LOG_LEVEL_2); - { - PROFILE_FUNC("[run_server] Add new connection"); - add_new_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port); - } - - } - - } - LOG_PRINT("Closing connections("<< m_connections.size() << ") and waiting...", LOG_LEVEL_2); - m_connections_lock.lock(); - for(connections_container::iterator it = m_connections.begin(); it != m_connections.end(); it++) - { - ::shutdown(it->second->m_sock, SD_BOTH); - ::closesocket(it->second->m_sock); - } - m_connections_lock.unlock(); - size_t wait_count = 0; - while(m_connections.size() && wait_count < 100) - { - ::Sleep(100); - wait_count++; - } - LOG_PRINT("Connections closed OK (wait_count=" << wait_count << ")", LOG_LEVEL_2); - - - LOG_PRINT("Stopping worker threads("<< m_worker_thread_counter << ").", LOG_LEVEL_2); - for(int i = 0; i > ptr; - ptr.reset(new connection(m_config)); - - connection& conn = *ptr.get(); - m_connections[new_sock] = ptr; - LOG_PRINT("Add new connection zone: leaving lock", LOG_LEVEL_3); - m_connections_lock.unlock(); - conn.init_buffers(); - conn.m_sock = new_sock; - conn.context.m_remote_ip = ip_from; - conn.context.m_remote_port = port_from; - conn.m_completion_port = m_completion_port; - { - PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort"); - ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0); - } - - //if(NULL == ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0)) - //{ - // int err = ::GetLastError(); - // LOG_PRINT("Failed to CreateIoCompletionPort(associate socket and completion port), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); - // return false; - //} - - conn.m_tprotocol_handler.after_init_connection(); - { - PROFILE_FUNC("[add_new_connection] starting loop"); - int res = 0; - while(true)//res!=SOCKET_ERROR) - { - PROFILE_FUNC("[add_new_connection] in loop time"); - conn.m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; - ZeroMemory(&conn.m_precv_data->m_overlapped, sizeof(OVERLAPPED)); - conn.m_precv_data->DataBuf.len = conn.m_precv_data->TotalBuffBytes; - conn.m_precv_data->DataBuf.buf = conn.m_precv_data->Buffer; - conn.m_precv_data->m_op_type = op_type_recv; - InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1); - DWORD bytes_recvd = 0; - DWORD flags = 0; - - ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1); - { - PROFILE_FUNC("[add_new_connection] ::WSARecv"); - res = ::WSARecv(conn.m_sock, &(conn.m_precv_data->DataBuf), 1, &bytes_recvd , &flags, &(conn.m_precv_data->m_overlapped), NULL); - } - if(res == SOCKET_ERROR ) - { - int err = ::WSAGetLastError(); - if(WSA_IO_PENDING == err ) - { - break; - } - LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err << " " << log_space::get_win32_err_descr(err)); - ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0); - conn.query_shutdown(); - //shutdown_connection(&conn); - break; - } - - - break; - /*else if(0 == res) - { - if(!bytes_recvd) - { - PROFILE_FUNC("[add_new_connection] shutdown_connection"); - ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0); - conn.query_shutdown(); - //shutdown_connection(&conn); - break; - }else - { - PROFILE_FUNC("[add_new_connection] handle_recv"); - } - }*/ - } - } - - - - return true; -} -//------------------------------------------------------------- -template -bool cp_server_impl::deinit_server() -{ - if(!m_initialized) - return true; - - if(INVALID_SOCKET != m_listen_socket) - { - shutdown(m_listen_socket, SD_BOTH); - int res = closesocket(m_listen_socket); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - } - m_listen_socket = INVALID_SOCKET; - } - - int res = ::WSACleanup(); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - } - m_initialized = false; - - return true; -} - -//------------------------------------------------------------- -template -bool cp_server_impl::send_stop_signal() -{ - ::InterlockedExchange(&m_stop, 1); - return true; -} -//------------------------------------------------------------- -template -bool cp_server_impl::is_stop_signal() -{ - return m_stop?true:false; -} -//------------------------------------------------------------- -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h deleted file mode 100644 index 49b0839b2b..0000000000 --- a/contrib/epee/include/net/http_base.h +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "string_tools.h" -namespace epee -{ -namespace net_utils -{ - namespace http - { - - enum http_method{ - http_method_get, - http_method_post, - http_method_put, - http_method_head, - http_method_etc, - http_method_unknown - }; - - enum http_content_type - { - http_content_type_text_html, - http_content_type_image_gif, - http_content_type_other, - http_content_type_not_set - }; - - typedef std::list > fields_list; - - inline - std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields) - { - fields_list::const_iterator it = fields.begin(); - for(; it != fields.end(); it++) - if(!string_tools::compare_no_case(param_name, it->first)) - break; - - if(it==fields.end()) - return std::string(); - - return it->second; - } - - - inline - std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri) - { - std::string buff = "([\\?|&])"; - buff += param_name + "=([^&]*)"; - boost::regex match_param(buff.c_str(), boost::regex::icase | boost::regex::normal); - boost::smatch result; - if(boost::regex_search(uri, result, match_param, boost::match_default) && result[0].matched) - { - return result[2]; - } - return std::string(); - } - - - - struct http_header_info - { - std::string m_connection; //"Connection:" - std::string m_referer; //"Referer:" - std::string m_content_length; //"Content-Length:" - std::string m_content_type; //"Content-Type:" - std::string m_transfer_encoding;//"Transfer-Encoding:" - std::string m_content_encoding; //"Content-Encoding:" - std::string m_host; //"Host:" - std::string m_cookie; //"Cookie:" - fields_list m_etc_fields; - - void clear() - { - m_connection.clear(); - m_referer.clear(); - m_content_length.clear(); - m_content_type.clear(); - m_transfer_encoding.clear(); - m_content_encoding.clear(); - m_host.clear(); - m_cookie.clear(); - m_etc_fields.clear(); - } - }; - - struct uri_content - { - std::string m_path; - std::string m_query; - std::string m_fragment; - std::list > m_query_params; - }; - - struct url_content - { - std::string schema; - std::string host; - std::string uri; - uint64_t port; - uri_content m_uri_content; - }; - - - struct http_request_info - { - http_request_info():m_http_method(http_method_unknown), - m_http_ver_hi(0), - m_http_ver_lo(0), - m_have_to_block(false) - {} - - http_method m_http_method; - std::string m_URI; - std::string m_http_method_str; - std::string m_full_request_str; - std::string m_replace_html; - std::string m_request_head; - int m_http_ver_hi; - int m_http_ver_lo; - bool m_have_to_block; - http_header_info m_header_info; - uri_content m_uri_content; - size_t m_full_request_buf_size; - std::string m_body; - - void clear() - { - this->~http_request_info(); - new(this) http_request_info(); - } - }; - - - struct http_response_info - { - int m_response_code; - std::string m_response_comment; - fields_list m_additional_fields; - std::string m_body; - std::string m_mime_tipe; - http_header_info m_header_info; - int m_http_ver_hi;// OUT paramter only - int m_http_ver_lo;// OUT paramter only - - void clear() - { - this->~http_response_info(); - new(this) http_response_info(); - } - }; - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h deleted file mode 100644 index 5897a017e7..0000000000 --- a/contrib/epee/include/net/http_client.h +++ /dev/null @@ -1,875 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -#include -#include - -#include "net_helper.h" -#include "http_client_base.h" - -#ifdef HTTP_ENABLE_GZIP -#include "gzip_encoding.h" -#endif - -#include "string_tools.h" -#include "reg_exp_definer.h" -#include "http_base.h" -#include "to_nonconst_iterator.h" -#include "net_parse_helpers.h" - -//#include "shlwapi.h" - -//#pragma comment(lib, "shlwapi.lib") - -extern epee::critical_section gregexp_lock; - - -namespace epee -{ -namespace net_utils -{ - -using namespace std; - - /*struct url - { - public: - void parse(const std::string& url_s) - { - const string prot_end("://"); - string::const_iterator prot_i = search(url_s.begin(), url_s.end(), - prot_end.begin(), prot_end.end()); - protocol_.reserve(distance(url_s.begin(), prot_i)); - transform(url_s.begin(), prot_i, - back_inserter(protocol_), - ptr_fun(tolower)); // protocol is icase - if( prot_i == url_s.end() ) - return; - advance(prot_i, prot_end.length()); - string::const_iterator path_i = find(prot_i, url_s.end(), '/'); - host_.reserve(distance(prot_i, path_i)); - transform(prot_i, path_i, - back_inserter(host_), - ptr_fun(tolower)); // host is icase - string::const_iterator query_i = find(path_i, url_s.end(), '?'); - path_.assign(path_i, query_i); - if( query_i != url_s.end() ) - ++query_i; - query_.assign(query_i, url_s.end()); - } - - std::string protocol_; - std::string host_; - std::string path_; - std::string query_; - };*/ - - - - - //--------------------------------------------------------------------------- - static inline const char* get_hex_vals() - { - static char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - return hexVals; - } - - static inline const char* get_unsave_chars() - { - //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&"; - static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; - return unsave_chars; - } - - static inline bool is_unsafe(unsigned char compare_char) - { - if(compare_char <= 32 || compare_char >= 123) - return true; - - const char* punsave = get_unsave_chars(); - - for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++) - if(compare_char == punsave[ichar_pos]) - return true; - - return false; - } - - static inline - std::string dec_to_hex(char num, int radix) - { - int temp=0; - std::string csTmp; - int num_char; - - num_char = (int) num; - if (num_char < 0) - num_char = 256 + num_char; - - while (num_char >= radix) - { - temp = num_char % radix; - num_char = (int)floor((float)num_char / (float)radix); - csTmp = get_hex_vals()[temp]; - } - - csTmp += get_hex_vals()[num_char]; - - if(csTmp.size() < 2) - { - csTmp += '0'; - } - - std::reverse(csTmp.begin(), csTmp.end()); - //_mbsrev((unsigned char*)csTmp.data()); - - return csTmp; - } - - static inline std::string convert(char val) - { - std::string csRet; - csRet += "%"; - csRet += dec_to_hex(val, 16); - return csRet; - } - static inline std::string conver_to_url_format(const std::string& uri) - { - - std::string result; - - for(size_t i = 0; i!= uri.size(); i++) - { - if(is_unsafe(uri[i])) - result += convert(uri[i]); - else - result += uri[i]; - - } - - return result; - } - - static inline std::string convert_to_url_format_force_all(const std::string& uri) - { - - std::string result; - - for(size_t i = 0; i!= uri.size(); i++) - { - result += convert(uri[i]); - } - - return result; - } - - - - - - namespace http - { - - class http_simple_client: public i_target_handler - { - public: - - - private: - enum reciev_machine_state - { - reciev_machine_state_header, - reciev_machine_state_body_content_len, - reciev_machine_state_body_connection_close, - reciev_machine_state_body_chunked, - reciev_machine_state_done, - reciev_machine_state_error - }; - - - - enum chunked_state{ - http_chunked_state_chunk_head, - http_chunked_state_chunk_body, - http_chunked_state_done, - http_chunked_state_undefined - }; - - - blocked_mode_client m_net_client; - std::string m_host_buff; - std::string m_port; - unsigned int m_timeout; - std::string m_header_cache; - http_response_info m_response_info; - size_t m_len_in_summary; - size_t m_len_in_remain; - //std::string* m_ptarget_buffer; - boost::shared_ptr m_pcontent_encoding_handler; - reciev_machine_state m_state; - chunked_state m_chunked_state; - std::string m_chunked_cache; - critical_section m_lock; - - public: - void set_host_name(const std::string& name) - { - CRITICAL_REGION_LOCAL(m_lock); - m_host_buff = name; - } - bool connect(const std::string& host, int port, unsigned int timeout) - { - return connect(host, std::to_string(port), timeout); - } - bool connect(const std::string& host, const std::string& port, unsigned int timeout) - { - CRITICAL_REGION_LOCAL(m_lock); - m_host_buff = host; - m_port = port; - m_timeout = timeout; - - return m_net_client.connect(host, port, timeout, timeout); - } - //--------------------------------------------------------------------------- - bool disconnect() - { - CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.disconnect(); - } - //--------------------------------------------------------------------------- - bool is_connected() - { - CRITICAL_REGION_LOCAL(m_lock); - return m_net_client.is_connected(); - } - //--------------------------------------------------------------------------- - virtual bool handle_target_data(std::string& piece_of_transfer) - { - CRITICAL_REGION_LOCAL(m_lock); - m_response_info.m_body += piece_of_transfer; - piece_of_transfer.clear(); - return true; - } - //--------------------------------------------------------------------------- - inline - bool invoke_get(const std::string& uri, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) - { - CRITICAL_REGION_LOCAL(m_lock); - return invoke(uri, "GET", body, ppresponse_info, additional_params); - } - - //--------------------------------------------------------------------------- - inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) - { - CRITICAL_REGION_LOCAL(m_lock); - if(!is_connected()) - { - LOG_PRINT("Reconnecting...", LOG_LEVEL_3); - if(!connect(m_host_buff, m_port, m_timeout)) - { - LOG_PRINT("Failed to connect to " << m_host_buff << ":" << m_port, LOG_LEVEL_3); - return false; - } - } - m_response_info.clear(); - std::string req_buff = method + " "; - req_buff += uri + " HTTP/1.1\r\n" + - "Host: "+ m_host_buff +"\r\n" + "Content-Length: " + boost::lexical_cast(body.size()) + "\r\n"; - - - //handle "additional_params" - for(fields_list::const_iterator it = additional_params.begin(); it!=additional_params.end(); it++) - req_buff += it->first + ": " + it->second + "\r\n"; - req_buff += "\r\n"; - //-- - - bool res = m_net_client.send(req_buff); - CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); - if(body.size()) - res = m_net_client.send(body); - CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); - - if(ppresponse_info) - *ppresponse_info = &m_response_info; - - m_state = reciev_machine_state_header; - return handle_reciev(); - } - //--------------------------------------------------------------------------- - inline bool invoke_post(const std::string& uri, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) - { - CRITICAL_REGION_LOCAL(m_lock); - return invoke(uri, "POST", body, ppresponse_info, additional_params); - } - private: - //--------------------------------------------------------------------------- - inline bool handle_reciev() - { - CRITICAL_REGION_LOCAL(m_lock); - bool keep_handling = true; - bool need_more_data = true; - std::string recv_buffer; - while(keep_handling) - { - if(need_more_data) - { - if(!m_net_client.recv(recv_buffer)) - { - LOG_PRINT("Unexpected reciec fail", LOG_LEVEL_3); - m_state = reciev_machine_state_error; - } - if(!recv_buffer.size()) - { - //connection is going to be closed - if(reciev_machine_state_body_connection_close != m_state) - { - m_state = reciev_machine_state_error; - } - } - need_more_data = false; - } - switch(m_state) - { - case reciev_machine_state_header: - keep_handling = handle_header(recv_buffer, need_more_data); - break; - case reciev_machine_state_body_content_len: - keep_handling = handle_body_content_len(recv_buffer, need_more_data); - break; - case reciev_machine_state_body_connection_close: - keep_handling = handle_body_connection_close(recv_buffer, need_more_data); - break; - case reciev_machine_state_body_chunked: - keep_handling = handle_body_body_chunked(recv_buffer, need_more_data); - break; - case reciev_machine_state_done: - keep_handling = false; - break; - case reciev_machine_state_error: - keep_handling = false; - break; - } - - } - m_header_cache.clear(); - if(m_state != reciev_machine_state_error) - { - if(m_response_info.m_header_info.m_connection.size() && !string_tools::compare_no_case("close", m_response_info.m_header_info.m_connection)) - disconnect(); - - return true; - } - else - { - LOG_PRINT_L3("Returning false because of wrong state machine. state: " << m_state); - return false; - } - } - //--------------------------------------------------------------------------- - inline - bool handle_header(std::string& recv_buff, bool& need_more_data) - { - - CRITICAL_REGION_LOCAL(m_lock); - if(!recv_buff.size()) - { - LOG_ERROR("Connection closed at handle_header"); - m_state = reciev_machine_state_error; - return false; - } - - m_header_cache += recv_buff; - recv_buff.clear(); - std::string::size_type pos = m_header_cache.find("\r\n\r\n"); - if(pos != std::string::npos) - { - recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end()); - m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end()); - - analize_cached_header_and_invoke_state(); - m_header_cache.clear(); - if(!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done)) - need_more_data = true; - - return true; - }else - need_more_data = true; - return true; - } - //--------------------------------------------------------------------------- - inline - bool handle_body_content_len(std::string& recv_buff, bool& need_more_data) - { - CRITICAL_REGION_LOCAL(m_lock); - if(!recv_buff.size()) - { - LOG_PRINT("Warning: Content-Len mode, but connection unexpectedly closed", LOG_LEVEL_3); - m_state = reciev_machine_state_done; - return true; - } - CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()"); - m_len_in_remain -= recv_buff.size(); - m_pcontent_encoding_handler->update_in(recv_buff); - - if(m_len_in_remain == 0) - m_state = reciev_machine_state_done; - else - need_more_data = true; - - return true; - } - //--------------------------------------------------------------------------- - inline - bool handle_body_connection_close(std::string& recv_buff, bool& need_more_data) - { - CRITICAL_REGION_LOCAL(m_lock); - if(!recv_buff.size()) - { - m_state = reciev_machine_state_done; - return true; - } - need_more_data = true; - m_pcontent_encoding_handler->update_in(recv_buff); - - - return true; - } - //--------------------------------------------------------------------------- - inline bool is_hex_symbol(char ch) - { - - if( (ch >= '0' && ch <='9')||(ch >= 'A' && ch <='F')||(ch >= 'a' && ch <='f')) - return true; - else - return false; - } - //--------------------------------------------------------------------------- - inline - bool get_len_from_chunk_head(const std::string &chunk_head, size_t& result_size) - { - std::stringstream str_stream; - str_stream << std::hex; - if(!(str_stream << chunk_head && str_stream >> result_size)) - return false; - - return true; - } - //--------------------------------------------------------------------------- - inline - bool get_chunk_head(std::string& buff, size_t& chunk_size, bool& is_matched) - { - is_matched = false; - size_t offset = 0; - for(std::string::iterator it = buff.begin(); it!= buff.end(); it++, offset++) - { - if(!is_hex_symbol(*it)) - { - if(*it == '\r' || *it == ' ' ) - { - offset--; - continue; - } - else if(*it == '\n') - { - std::string chunk_head = buff.substr(0, offset); - if(!get_len_from_chunk_head(chunk_head, chunk_size)) - return false; - - if(0 == chunk_size) - { - //Here is a small confusion - //In breif - if the chunk is the last one we need to get terminating sequence - //along with the cipher, generally in the "ddd\r\n\r\n" form - - for(it++;it != buff.end(); it++) - { - if('\r' == *it) - continue; - else if('\n' == *it) - break; - else - { - LOG_ERROR("http_stream_filter: Wrong last chunk terminator"); - return false; - } - } - - if(it == buff.end()) - return true; - } - - buff.erase(buff.begin(), ++it); - - is_matched = true; - return true; - } - else - return false; - } - } - - return true; - } - //--------------------------------------------------------------------------- - inline - bool handle_body_body_chunked(std::string& recv_buff, bool& need_more_data) - { - CRITICAL_REGION_LOCAL(m_lock); - if(!recv_buff.size()) - { - LOG_PRINT("Warning: CHUNKED mode, but connection unexpectedly closed", LOG_LEVEL_3); - m_state = reciev_machine_state_done; - return true; - } - m_chunked_cache += recv_buff; - recv_buff.clear(); - bool is_matched = false; - - while(true) - { - if(!m_chunked_cache.size()) - { - need_more_data = true; - break; - } - - switch(m_chunked_state) - { - case http_chunked_state_chunk_head: - if(m_chunked_cache[0] == '\n' || m_chunked_cache[0] == '\r') - { - //optimize a bit - if(m_chunked_cache[0] == '\r' && m_chunked_cache.size()>1 && m_chunked_cache[1] == '\n') - m_chunked_cache.erase(0, 2); - else - m_chunked_cache.erase(0, 1); - break; - } - if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched)) - { - LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache); - m_state = reciev_machine_state_error; - return false; - } - - if(!is_matched) - { - need_more_data = true; - return true; - }else - { - m_chunked_state = http_chunked_state_chunk_body; - if(m_len_in_remain == 0) - {//last chunk, let stop the stream and fix the chunk queue. - m_state = reciev_machine_state_done; - return true; - } - m_chunked_state = http_chunked_state_chunk_body; - break; - } - break; - case http_chunked_state_chunk_body: - { - std::string chunk_body; - if(m_len_in_remain >= m_chunked_cache.size()) - { - m_len_in_remain -= m_chunked_cache.size(); - chunk_body.swap(m_chunked_cache); - }else - { - chunk_body.assign(m_chunked_cache, 0, m_len_in_remain); - m_chunked_cache.erase(0, m_len_in_remain); - m_len_in_remain = 0; - } - - m_pcontent_encoding_handler->update_in(chunk_body); - - if(!m_len_in_remain) - m_chunked_state = http_chunked_state_chunk_head; - } - break; - case http_chunked_state_done: - m_state = reciev_machine_state_done; - return true; - case http_chunked_state_undefined: - default: - LOG_ERROR("http_stream_filter::handle_chunked(): Wrong state" << m_chunked_state); - return false; - } - } - - return true; - } - //--------------------------------------------------------------------------- - inline - bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process) - { - LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_4); - - STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)" - // 12 3 4 5 6 7 8 9 - "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //10 1112 13 - boost::regex::icase | boost::regex::normal); - - boost::smatch result; - std::string::const_iterator it_current_bound = m_cache_to_process.begin(); - std::string::const_iterator it_end_bound = m_cache_to_process.end(); - - - - //lookup all fields and fill well-known fields - while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) - { - const size_t field_val = 12; - //const size_t field_etc_name = 10; - - int i = 2; //start position = 2 - if(result[i++].matched)//"Connection" - body_info.m_connection = result[field_val]; - else if(result[i++].matched)//"Referrer" - body_info.m_referer = result[field_val]; - else if(result[i++].matched)//"Content-Length" - body_info.m_content_length = result[field_val]; - else if(result[i++].matched)//"Content-Type" - body_info.m_content_type = result[field_val]; - else if(result[i++].matched)//"Transfer-Encoding" - body_info.m_transfer_encoding = result[field_val]; - else if(result[i++].matched)//"Content-Encoding" - body_info.m_content_encoding = result[field_val]; - else if(result[i++].matched)//"Host" - { body_info.m_host = result[field_val]; - string_tools::trim(body_info.m_host); - } - else if(result[i++].matched)//"Cookie" - body_info.m_cookie = result[field_val]; - else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - {;} - else - {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<(result[1]); - m_response_info.m_http_ver_lo = boost::lexical_cast(result[2]); - m_response_info.m_response_code = boost::lexical_cast(result[3]); - - m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second)); - return true; - }else - { - LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache); - return false; - } - - } - inline - bool set_reply_content_encoder() - { - STATIC_REGEXP_EXPR_1(rexp_match_gzip, "^.*?((gzip)|(deflate))", boost::regex::icase | boost::regex::normal); - boost::smatch result; // 12 3 - if(boost::regex_search( m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched) - { -#ifdef HTTP_ENABLE_GZIP - m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched)); -#else - m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this)); - LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP"); - return false; -#endif - } - else - { - m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this)); - } - - return true; - } - inline - bool analize_cached_header_and_invoke_state() - { - m_response_info.clear(); - analize_first_response_line(); - std::string fake_str; //gcc error workaround - - bool res = parse_header(m_response_info.m_header_info, m_header_cache); - CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache); - - set_reply_content_encoder(); - - m_len_in_summary = 0; - bool content_len_valid = false; - if(m_response_info.m_header_info.m_content_length.size()) - content_len_valid = string_tools::get_xtype_from_string(m_len_in_summary, m_response_info.m_header_info.m_content_length); - - - - if(!m_len_in_summary && ((m_response_info.m_response_code>=100&&m_response_info.m_response_code<200) - || 204 == m_response_info.m_response_code - || 304 == m_response_info.m_response_code) ) - {//There will be no response body, server will display the local page with error - m_state = reciev_machine_state_done; - return true; - }else if(m_response_info.m_header_info.m_transfer_encoding.size()) - { - string_tools::trim(m_response_info.m_header_info.m_transfer_encoding); - if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked")) - { - LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding); - m_state = reciev_machine_state_error; - return false; - } - m_state = reciev_machine_state_body_chunked; - m_chunked_state = http_chunked_state_chunk_head; - return true; - } - else if(!m_response_info.m_header_info.m_content_length.empty()) - { - //In the response header the length was specified - if(!content_len_valid) - { - LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<(u_c.port), timeout); - CHECK_AND_ASSERT_MES(res, false, "failed to connect " << u_c.host << ":" << u_c.port); - } - - return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params); - } - - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/http_client_abstract_invoke.h b/contrib/epee/include/net/http_client_abstract_invoke.h deleted file mode 100644 index 425a355eee..0000000000 --- a/contrib/epee/include/net/http_client_abstract_invoke.h +++ /dev/null @@ -1,98 +0,0 @@ - -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "storages/serializeble_struct_helper.h" - -namespace epee -{ - namespace net_utils - { - namespace http - { - template - bool invoke_http_json_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET") - { - std::string req_param; - StorageNamed::InMemStorageSpace::json::store_t_to_json(out_struct, req_param); - - const http_response_info* pri = NULL; - if(!invoke_request(url, transport, timeout, &pri, method, req_param)) - { - LOG_PRINT_L1("Failed to invoke http request to " << url); - return false; - } - - if(!pri->m_response_code) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); - return false; - } - - if(pri->m_response_code != 200) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); - return false; - } - - return StorageNamed::InMemStorageSpace::json::load_t_from_json(result_struct, pri->m_body); - } - - - - template - bool invoke_http_bin_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET") - { - std::string req_param; - epee::StorageNamed::save_struct_as_storage_to_buff(out_struct, req_param); - - const http_response_info* pri = NULL; - if(!invoke_request(url, transport, timeout, &pri, method, req_param)) - { - LOG_PRINT_L1("Failed to invoke http request to " << url); - return false; - } - - if(!pri->m_response_code) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); - return false; - } - - if(pri->m_response_code != 200) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); - return false; - } - - return epee::StorageNamed::load_struct_from_storage_buff(result_struct, pri->m_body); - } - - - } - } -} diff --git a/contrib/epee/include/net/http_client_base.h b/contrib/epee/include/net/http_client_base.h deleted file mode 100644 index 571e27f73c..0000000000 --- a/contrib/epee/include/net/http_client_base.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 net_utils - { - struct i_sub_handler - { - virtual ~i_sub_handler(){} - - virtual bool update_in( std::string& piece_of_transfer)=0; - virtual void stop(std::string& OUT collect_remains)=0; - virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed) - { - is_changed = true; - bool res = this->update_in(collect_remains); - if(res) - this->stop(collect_remains); - return res; - } - }; - - - struct i_target_handler - { - virtual ~i_target_handler(){} - virtual bool handle_target_data( std::string& piece_of_transfer)=0; - }; - - - class do_nothing_sub_handler: public i_sub_handler - { - public: - do_nothing_sub_handler(i_target_handler* powner_filter):m_powner_filter(powner_filter) - {} - virtual bool update_in( std::string& piece_of_transfer) - { - return m_powner_filter->handle_target_data(piece_of_transfer); - } - virtual void stop(std::string& OUT collect_remains) - { - - } - i_target_handler* m_powner_filter; - }; - } -} \ No newline at end of file diff --git a/contrib/epee/include/net/http_client_via_api_helper.h b/contrib/epee/include/net/http_client_via_api_helper.h deleted file mode 100644 index 45a70993b3..0000000000 --- a/contrib/epee/include/net/http_client_via_api_helper.h +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -#pragma comment(lib, "Wininet.lib") - -namespace epee -{ -namespace net_utils -{ - inline - bool http_ssl_invoke(const std::string& url, const std::string usr, const std::string psw, std::string& http_response_body, bool use_post = false) - { - bool final_res = false; - - ATL::CUrl url_obj; - BOOL crack_rss = url_obj.CrackUrl(string_encoding::convert_to_t >(url).c_str()); - - HINTERNET hinet = ::InternetOpenA(SHARED_JOBSCOMMON_HTTP_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - if(!hinet) - { - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetOpenA, \nError: " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - return false; - } - - DWORD dwFlags = 0; - DWORD dwBuffLen = sizeof(dwFlags); - - if(usr.size()) - { - dwFlags |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID| - INTERNET_FLAG_PRAGMA_NOCACHE | SECURITY_FLAG_IGNORE_UNKNOWN_CA|INTERNET_FLAG_SECURE; - }else - { - dwFlags |= INTERNET_FLAG_PRAGMA_NOCACHE; - } - - - int port = url_obj.GetPortNumber(); - BOOL res = FALSE; - - HINTERNET hsession = ::InternetConnectA(hinet, string_encoding::convert_to_ansii(url_obj.GetHostName()).c_str(), port/*INTERNET_DEFAULT_HTTPS_PORT*/, usr.c_str(), psw.c_str(), INTERNET_SERVICE_HTTP, dwFlags, NULL); - if(hsession) - { - const std::string uri = string_encoding::convert_to_ansii(url_obj.GetUrlPath()) + string_encoding::convert_to_ansii(url_obj.GetExtraInfo()); - - HINTERNET hrequest = ::HttpOpenRequestA(hsession, use_post?"POST":NULL, uri.c_str(), NULL, NULL,NULL, dwFlags, NULL); - if(hrequest) - { - while(true) - { - res = ::HttpSendRequestA(hrequest, NULL, 0, NULL, 0); - if(!res) - { - //ERROR_INTERNET_INVALID_CA 45 - //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5) - int err = ::GetLastError(); - LOG_PRINT("Failed to call HttpSendRequestA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - break; - } - - DWORD code = 0; - DWORD buf_len = sizeof(code); - DWORD index = 0; - res = ::HttpQueryInfo(hrequest, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &code, &buf_len, &index); - if(!res) - { - //ERROR_INTERNET_INVALID_CA 45 - //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5) - int err = ::GetLastError(); - LOG_PRINT("Failed to call HttpQueryInfo, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - break; - } - if(code < 200 || code > 299) - { - LOG_PRINT("Wrong server response, HttpQueryInfo returned statuse code" << code , LOG_LEVEL_0); - break; - } - - - char buff[100000] = {0}; - DWORD readed = 0; - while(true) - { - res = ::InternetReadFile(hrequest, buff, sizeof(buff), &readed); - if(!res) - { - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetReadFile, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - break; - } - if(readed) - { - http_response_body.append(buff, readed); - } - else - break; - } - - if(!res) - break; - - - //we success - final_res = true; - - res = ::InternetCloseHandle(hrequest); - if(!res) - { - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - } - - break; - } - } - else - { - //ERROR_INTERNET_INVALID_CA - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetOpenUrlA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - return false; - } - - res = ::InternetCloseHandle(hsession); - if(!res) - { - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - } - }else - { - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetConnectA(" << string_encoding::convert_to_ansii(url_obj.GetHostName()) << ", port " << port << " \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - } - - - - res = ::InternetCloseHandle(hinet); - if(!res) - { - int err = ::GetLastError(); - LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); - } - return final_res; - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h deleted file mode 100644 index 72b4b57569..0000000000 --- a/contrib/epee/include/net/http_protocol_handler.h +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _HTTP_SERVER_H_ -#define _HTTP_SERVER_H_ - -#include -#include "include_base_utils.h" -#include "net_utils_base.h" -#include "to_nonconst_iterator.h" -#include "http_base.h" - -namespace epee -{ -namespace net_utils -{ - namespace http - { - - - /************************************************************************/ - /* */ - /************************************************************************/ - struct http_server_config - { - std::string m_folder; - critical_section m_lock; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - template - class simple_http_connection_handler - { - public: - 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); - virtual ~simple_http_connection_handler(){} - - bool release_protocol() - { - return true; - } - - virtual bool thread_init() - { - return true; - } - - virtual bool thread_deinit() - { - return true; - } - bool after_init_connection() - { - return true; - } - virtual bool handle_recv(const void* ptr, size_t cb); - virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response); - - private: - enum machine_state{ - http_state_retriving_comand_line, - http_state_retriving_header, - http_state_retriving_body, - http_state_connection_close, - http_state_error - }; - - enum body_transfer_type{ - http_body_transfer_chunked, - http_body_transfer_measure,//mean "Content-Length" valid - http_body_transfer_chunked_instead_measure, - http_body_transfer_connection_close, - http_body_transfer_multipart, - http_body_transfer_undefined - }; - - bool handle_buff_in(std::string& buf); - - bool analize_cached_request_header_and_invoke_state(size_t pos); - - bool handle_invoke_query_line(); - bool parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos); - std::string::size_type match_end_of_header(const std::string& buf); - bool get_len_from_content_lenght(const std::string& str, size_t& len); - bool handle_retriving_query_body(); - bool handle_query_measure(); - bool set_ready_state(); - bool slash_to_back_slash(std::string& str); - std::string get_file_mime_tipe(const std::string& path); - std::string get_response_header(const http_response_info& response); - - //major function - inline bool handle_request_and_send_response(const http::http_request_info& query_info); - - - std::string get_not_found_response_body(const std::string& URI); - - std::string m_root_path; - std::string m_cache; - machine_state m_state; - body_transfer_type m_body_transfer_type; - bool m_is_stop_handling; - http::http_request_info m_query_info; - size_t m_len_summary, m_len_remain; - config_type& m_config; - bool m_want_close; - protected: - i_service_endpoint* m_psnd_hndlr; - }; - - template - struct i_http_server_handler - { - virtual ~i_http_server_handler(){} - virtual bool handle_http_request(const http_request_info& query_info, - http_response_info& response, - t_connection_context& m_conn_context) = 0; - virtual bool init_server_thread(){return true;} - virtual bool deinit_server_thread(){return true;} - }; - - template - struct custum_handler_config: public http_server_config - { - i_http_server_handler* m_phandler; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - - template - class http_custom_handler: public simple_http_connection_handler - { - public: - 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), - m_config(config), - m_conn_context(conn_context) - {} - inline bool handle_request(const http_request_info& query_info, http_response_info& response) - { - CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!"); - //fill with default values - response.m_mime_tipe = "text/plain"; - response.m_response_code = 200; - response.m_response_comment = "OK"; - response.m_body.clear(); - return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); - } - - virtual bool thread_init() - { - return m_config.m_phandler->init_server_thread();; - } - - virtual bool thread_deinit() - { - return m_config.m_phandler->deinit_server_thread(); - } - void handle_qued_callback() - {} - bool after_init_connection() - { - return true; - } - - private: - //simple_http_connection_handler::config_type m_stub_config; - config_type& m_config; - t_connection_context& m_conn_context; - }; - } -} -} - -#include "http_protocol_handler.inl" - -#endif //_HTTP_SERVER_H_ diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl deleted file mode 100644 index fc091a2126..0000000000 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ /dev/null @@ -1,682 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "http_protocol_handler.h" -#include "include_base_utils.h" -#include "reg_exp_definer.h" -#include "string_tools.h" -#include "time_helper.h" -#include "file_io_utils.h" -#include "net_parse_helpers.h" - -#define HTTP_MAX_URI_LEN 9000 -#define HTTP_MAX_HEADER_LEN 100000 - -namespace epee -{ -namespace net_utils -{ - namespace http - { - - struct multipart_entry - { - std::list > m_etc_header_fields; - std::string m_content_disposition; - std::string m_content_type; - std::string m_body; - }; - - inline - bool match_boundary(const std::string& content_type, std::string& boundary) - { - STATIC_REGEXP_EXPR_1(rexp_match_boundary, "boundary=(.*?)(($)|([;\\s,]))", boost::regex::icase | boost::regex::normal); - // 1 - boost::smatch result; - if(boost::regex_search(content_type, result, rexp_match_boundary, boost::match_default) && result[0].matched) - { - boundary = result[1]; - return true; - } - - return false; - } - - inline - bool parse_header(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry) - { - STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Content-Disposition)|(Content-Type)" - // 12 3 - "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //4 56 7 - boost::regex::icase | boost::regex::normal); - - boost::smatch result; - std::string::const_iterator it_current_bound = it_begin; - std::string::const_iterator it_end_bound = it_end; - - //lookup all fields and fill well-known fields - while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) - { - const size_t field_val = 6; - const size_t field_etc_name = 4; - - int i = 2; //start position = 2 - if(result[i++].matched)//"Content-Disposition" - entry.m_content_disposition = result[field_val]; - else if(result[i++].matched)//"Content-Type" - entry.m_content_type = result[field_val]; - else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - entry.m_etc_header_fields.push_back(std::pair(result[field_etc_name], result[field_val])); - else - { - LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<& out_values) - { - //bool res = file_io_utils::load_file_to_string("C:\\public\\multupart_data", body); - - std::string boundary; - if(!match_boundary(content_type, boundary)) - { - LOG_PRINT("Failed to match boundary in content type: " << content_type, LOG_LEVEL_0); - return false; - } - - boundary+="\r\n"; - bool is_stop = false; - bool first_step = true; - - std::string::const_iterator it_begin = body.begin(); - std::string::const_iterator it_end; - while(!is_stop) - { - std::string::size_type pos = body.find(boundary, std::distance(body.begin(), it_begin)); - - if(std::string::npos == pos) - { - is_stop = true; - boundary.erase(boundary.size()-2, 2); - boundary+= "--"; - pos = body.find(boundary, std::distance(body.begin(), it_begin)); - if(std::string::npos == pos) - { - LOG_PRINT("Error: Filed to match closing multipart tag", LOG_LEVEL_0); - it_end = body.end(); - }else - { - it_end = body.begin() + pos; - } - }else - it_end = body.begin() + pos; - - - if(first_step && !is_stop) - { - first_step = false; - it_begin = it_end + boundary.size(); - std::string temp = "\r\n--"; - boundary = temp + boundary; - continue; - } - - out_values.push_back(multipart_entry()); - if(!handle_part_of_multipart(it_begin, it_end, out_values.back())) - { - LOG_PRINT("Failed to handle_part_of_multipart", LOG_LEVEL_0); - return false; - } - - it_begin = it_end + boundary.size(); - } - - return true; - } - - - - - //-------------------------------------------------------------------------------------------- - template - simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config): - m_state(http_state_retriving_comand_line), - m_body_transfer_type(http_body_transfer_undefined), - m_is_stop_handling(false), - m_len_summary(0), - m_len_remain(0), - m_config(config), - m_want_close(false), - m_psnd_hndlr(psnd_hndlr) - { - - } - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::set_ready_state() - { - m_is_stop_handling = false; - m_state = http_state_retriving_comand_line; - m_body_transfer_type = http_body_transfer_undefined; - m_query_info.clear(); - m_len_summary = 0; - return true; - } - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::handle_recv(const void* ptr, size_t cb) - { - std::string buf((const char*)ptr, cb); - //LOG_PRINT_L0("HTTP_RECV: " << ptr << "\r\n" << buf); - //file_io_utils::save_string_to_file(string_tools::get_current_module_folder() + "/" + boost::lexical_cast(ptr), std::string((const char*)ptr, cb)); - - bool res = handle_buff_in(buf); - if(m_want_close/*m_state == http_state_connection_close || m_state == http_state_error*/) - return false; - return res; - } - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::handle_buff_in(std::string& buf) - { - - if(m_cache.size()) - m_cache += buf; - else - m_cache.swap(buf); - - m_is_stop_handling = false; - while(!m_is_stop_handling) - { - switch(m_state) - { - case http_state_retriving_comand_line: - //The HTTP protocol does not place any a priori limit on the length of a URI. (c)RFC2616 - //but we forebly restirct it len to HTTP_MAX_URI_LEN to make it more safely - if(!m_cache.size()) - break; - - //check_and_handle_fake_response(); - if((m_cache[0] == '\r' || m_cache[0] == '\n')) - { - //some times it could be that before query line cold be few line breaks - //so we have to be calm without panic with assers - m_cache.erase(0, 1); - break; - } - - if(std::string::npos != m_cache.find('\n', 0)) - handle_invoke_query_line(); - else - { - 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"); - m_state = http_state_error; - return false; - } - } - break; - case http_state_retriving_header: - { - std::string::size_type pos = match_end_of_header(m_cache); - if(std::string::npos == pos) - { - 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"); - m_state = http_state_error; - return false; - } - break; - } - analize_cached_request_header_and_invoke_state(pos); - break; - } - case http_state_retriving_body: - return handle_retriving_query_body(); - case http_state_connection_close: - return false; - default: - LOG_ERROR("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!!!"); - return false; - } - - if(!m_cache.size()) - m_is_stop_handling = true; - } - - return true; - } - //-------------------------------------------------------------------------------------------- - 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(result[4].matched) - method = http::http_method_get; - else if(result[5].matched) - method = http::http_method_head; - else if(result[6].matched) - method = http::http_method_post; - else if(result[7].matched) - method = http::http_method_put; - else - method = http::http_method_etc; - - return true; - } - - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::handle_invoke_query_line() - { - LOG_FRAME("simple_http_connection_handler::handle_recognize_protocol_out(*)", LOG_LEVEL_3); - - 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; - 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); - m_query_info.m_URI = result[10]; - parse_uri(m_query_info.m_URI, m_query_info.m_uri_content); - m_query_info.m_http_method_str = result[2]; - m_query_info.m_full_request_str = result[0]; - - m_cache.erase(m_cache.begin(), to_nonsonst_iterator(m_cache, result[0].second)); - - m_state = http_state_retriving_header; - - return true; - }else - { - m_state = http_state_error; - LOG_ERROR("simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache); - return false; - } - - return false; - } - //-------------------------------------------------------------------------------------------- - template - std::string::size_type simple_http_connection_handler::match_end_of_header(const std::string& buf) - { - - //Here we returning head size, including terminating sequence (\r\n\r\n or \n\n) - std::string::size_type res = buf.find("\r\n\r\n"); - if(std::string::npos != res) - return res+4; - res = buf.find("\n\n"); - if(std::string::npos != res) - return res+2; - return res; - } - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::analize_cached_request_header_and_invoke_state(size_t pos) - { - //LOG_PRINT_L4("HTTP HEAD:\r\n" << m_cache.substr(0, pos)); - - LOG_FRAME("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(*)", LOG_LEVEL_3); - - m_query_info.m_full_request_buf_size = pos; - m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos); - - 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); - m_state = http_state_error; - } - - m_cache.erase(0, pos); - - std::string req_command_str = m_query_info.m_full_request_str; - //if we have POST or PUT command, it is very possible tha we will get body - //but now, we suppose than we have body only in case of we have "ContentLength" - if(m_query_info.m_header_info.m_content_length.size()) - { - m_state = http_state_retriving_body; - 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="< - bool simple_http_connection_handler::handle_retriving_query_body() - { - switch(m_body_transfer_type) - { - case http_body_transfer_measure: - return handle_query_measure(); - case http_body_transfer_chunked: - case http_body_transfer_connection_close: - case http_body_transfer_multipart: - case http_body_transfer_undefined: - default: - LOG_ERROR("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; - } - - return true; - } - //----------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::handle_query_measure() - { - - if(m_len_remain >= m_cache.size()) - { - m_len_remain -= m_cache.size(); - m_query_info.m_body += m_cache; - m_cache.clear(); - }else - { - m_query_info.m_body.append(m_cache.begin(), m_cache.begin() + m_len_remain); - m_cache.erase(0, m_len_remain); - m_len_remain = 0; - } - - if(!m_len_remain) - { - if(handle_request_and_send_response(m_query_info)) - set_ready_state(); - else - m_state = http_state_error; - } - return true; - } - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) - { - LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_3); - - STATIC_REGEXP_EXPR_1(rexp_mach_field, - "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)" - // 12 3 4 5 6 7 8 9 - "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", - //10 1112 13 - boost::regex::icase | boost::regex::normal); - - boost::smatch result; - std::string::const_iterator it_current_bound = m_cache_to_process.begin(); - std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos; - - body_info.clear(); - - //lookup all fields and fill well-known fields - while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) - { - const size_t field_val = 12; - const size_t field_etc_name = 10; - - int i = 2; //start position = 2 - if(result[i++].matched)//"Connection" - body_info.m_connection = result[field_val]; - else if(result[i++].matched)//"Referer" - body_info.m_referer = result[field_val]; - else if(result[i++].matched)//"Content-Length" - body_info.m_content_length = result[field_val]; - else if(result[i++].matched)//"Content-Type" - body_info.m_content_type = result[field_val]; - else if(result[i++].matched)//"Transfer-Encoding" - body_info.m_transfer_encoding = result[field_val]; - else if(result[i++].matched)//"Content-Encoding" - body_info.m_content_encoding = result[field_val]; - else if(result[i++].matched)//"Host" - body_info.m_host = result[field_val]; - else if(result[i++].matched)//"Cookie" - body_info.m_cookie = result[field_val]; - else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) - 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:"< - bool simple_http_connection_handler::get_len_from_content_lenght(const std::string& str, size_t& OUT len) - { - STATIC_REGEXP_EXPR_1(rexp_mach_field, "\\d+", boost::regex::normal); - std::string res; - boost::smatch result; - if(!(boost::regex_search( str, result, rexp_mach_field, boost::match_default) && result[0].matched)) - return false; - - len = boost::lexical_cast(result[0]); - return true; - } - //----------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::handle_request_and_send_response(const http::http_request_info& query_info) - { - http_response_info response; - bool res = handle_request(query_info, response); - //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" ); - - std::string response_data = get_response_header(response); - - //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body); - LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); - - m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); - if(response.m_body.size()) - m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); - return res; - } - //----------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::handle_request(const http::http_request_info& query_info, http_response_info& response) - { - - std::string uri_to_path = query_info.m_uri_content.m_path; - if("/" == uri_to_path) - uri_to_path = "/index.html"; - - //slash_to_back_slash(uri_to_path); - m_config.m_lock.lock(); - std::string destination_file_path = m_config.m_folder + uri_to_path; - m_config.m_lock.unlock(); - if(!file_io_utils::load_file_to_string(destination_file_path.c_str(), response.m_body)) - { - LOG_PRINT("URI \""<< query_info.m_full_request_str.substr(0, query_info.m_full_request_str.size()-2) << "\" [" << destination_file_path << "] Not Found (404 )" , LOG_LEVEL_1); - response.m_body = get_not_found_response_body(query_info.m_URI); - response.m_response_code = 404; - response.m_response_comment = "Not found"; - response.m_mime_tipe = "text/html"; - return true; - } - - LOG_PRINT(" -->> " << query_info.m_full_request_str << "\r\n<<--OK" , LOG_LEVEL_3); - response.m_response_code = 200; - response.m_response_comment = "OK"; - response.m_mime_tipe = get_file_mime_tipe(uri_to_path); - - - return true; - } - //----------------------------------------------------------------------------------- - template - std::string simple_http_connection_handler::get_response_header(const http_response_info& response) - { - std::string buf = "HTTP/1.1 "; - buf += boost::lexical_cast(response.m_response_code) + " " + response.m_response_comment + "\r\n" + - "Server: Epee-based\r\n" - "Content-Length: "; - buf += boost::lexical_cast(response.m_body.size()) + "\r\n"; - buf += "Content-Type: "; - buf += response.m_mime_tipe + "\r\n"; - - buf += "Last-Modified: "; - time_t tm; - time(&tm); - buf += misc_utils::get_internet_time_str(tm) + "\r\n"; - buf += "Accept-Ranges: bytes\r\n"; - //Wed, 01 Dec 2010 03:27:41 GMT" - - string_tools::trim(m_query_info.m_header_info.m_connection); - if(m_query_info.m_header_info.m_connection.size()) - { - if(!string_tools::compare_no_case("close", m_query_info.m_header_info.m_connection)) - { - //closing connection after sending - buf += "Connection: close\r\n"; - m_state = http_state_connection_close; - m_want_close = true; - } - } - //add additional fields, if it is - for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) - buf += it->first + ":" + it->second + "\r\n"; - - buf+="\r\n"; - - return buf; - } - //----------------------------------------------------------------------------------- - template - std::string simple_http_connection_handler::get_file_mime_tipe(const std::string& path) - { - std::string result; - std::string ext = string_tools::get_extension(path); - if(!string_tools::compare_no_case(ext, "gif")) - result = "image/gif"; - else if(!string_tools::compare_no_case(ext, "jpg")) - result = "image/jpeg"; - else if(!string_tools::compare_no_case(ext, "html")) - result = "text/html"; - else if(!string_tools::compare_no_case(ext, "htm")) - result = "text/html"; - else if(!string_tools::compare_no_case(ext, "js")) - result = "application/x-javascript"; - else if(!string_tools::compare_no_case(ext, "css")) - result = "text/css"; - else if(!string_tools::compare_no_case(ext, "xml")) - result = "application/xml"; - else if(!string_tools::compare_no_case(ext, "svg")) - result = "image/svg+xml"; - - - return result; - } - //----------------------------------------------------------------------------------- - template - std::string simple_http_connection_handler::get_not_found_response_body(const std::string& URI) - { - std::string body = - "\r\n" - "\r\n" - "404 Not Found\r\n" - "\r\n" - "

Not Found

\r\n" - "

The requested URL \r\n"; - body += URI; - body += "was not found on this server.

\r\n" - "\r\n"; - - return body; - } - //-------------------------------------------------------------------------------------------- - template - bool simple_http_connection_handler::slash_to_back_slash(std::string& str) - { - for(std::string::iterator it = str.begin(); it!=str.end(); it++) - if('/' == *it) - *it = '\\'; - return true; - } - } -} -} - -//-------------------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------------------- -//-------------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/contrib/epee/include/net/http_server_cp.h b/contrib/epee/include/net/http_server_cp.h deleted file mode 100644 index bbb167f9f3..0000000000 --- a/contrib/epee/include/net/http_server_cp.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _HTTP_SERVER_CP_H_ -#define _HTTP_SERVER_CP_H_ - -#include "abstract_tcp_server_cp.h" -#include "http_server.h" -namespace epee -{ -namespace net_utils -{ - typedef cp_server_impl cp_http_server_file_system; - typedef cp_server_impl cp_http_server_custum_handling; -} -} - - - -#endif - - diff --git a/contrib/epee/include/net/http_server_cp2.h b/contrib/epee/include/net/http_server_cp2.h deleted file mode 100644 index 1a503a4de2..0000000000 --- a/contrib/epee/include/net/http_server_cp2.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _HTTP_SERVER_CP2_H_ -#define _HTTP_SERVER_CP2_H_ - -#include "abstract_tcp_server2.h" -#include "http_protocol_handler.h" -namespace epee -{ -namespace net_utils -{ - typedef boosted_tcp_server > boosted_http_server_file_system; - typedef boosted_tcp_server > boosted_http_server_custum_handling; -} -} - - -#endif - - diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h deleted file mode 100644 index 12ad9d9731..0000000000 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "http_base.h" -#include "jsonrpc_structs.h" -#include "misc_os_dependent.h" -#include "storages/portable_storage.h" -#include "storages/portable_storage_template_helper.h" - - -#define CHAIN_HTTP_TO_MAP2(context_type) bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \ - epee::net_utils::http::http_response_info& response, \ - context_type& m_conn_context) \ -{\ - LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << 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)) \ - {response.m_response_code = 404;response.m_response_comment = "Not found";} \ - return true; \ -} - - -#define BEGIN_URI_MAP2() template bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ - epee::net_utils::http::http_response_info& response_info, \ - t_context& m_conn_context) { \ - bool handled = false; \ - if(false) return true; //just a stub to have "else if" - -#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context); - -#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format - -#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \ - else if(query_info.m_URI == s_pattern) \ - { \ - handled = true; \ - uint64_t ticks = epee::misc_utils::get_tick_count(); \ - boost::value_initialized req; \ - bool parse_res = epee::serialization::load_t_from_json(static_cast(req), query_info.m_body); \ - 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;\ - if(!callback_f(static_cast(req), static_cast(resp), m_conn_context)) \ - { \ - LOG_ERROR("Failed to " << #callback_f << "()"); \ - response_info.m_response_code = 500; \ - response_info.m_response_comment = "Internal Server Error"; \ - return true; \ - } \ - uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ - epee::serialization::store_t_to_json(static_cast(resp), response_info.m_body); \ - uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ - response_info.m_mime_tipe = "application/json"; \ - response_info.m_header_info.m_content_type = " application/json"; \ - LOG_PRINT( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ - } - -#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \ - else if(query_info.m_URI == s_pattern) \ - { \ - handled = true; \ - uint64_t ticks = epee::misc_utils::get_tick_count(); \ - boost::value_initialized req; \ - bool parse_res = epee::serialization::load_t_from_binary(static_cast(req), query_info.m_body); \ - CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ - uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ - boost::value_initialized resp;\ - if(!callback_f(static_cast(req), static_cast(resp), m_conn_context)) \ - { \ - LOG_ERROR("Failed to " << #callback_f << "()"); \ - response_info.m_response_code = 500; \ - response_info.m_response_comment = "Internal Server Error"; \ - return true; \ - } \ - uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ - epee::serialization::store_t_to_binary(static_cast(resp), response_info.m_body); \ - uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ - response_info.m_mime_tipe = " application/octet-stream"; \ - response_info.m_header_info.m_content_type = " application/octet-stream"; \ - LOG_PRINT( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ - } - -#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;} - -#define END_URI_MAP2() return handled;} - - -#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \ - { \ - uint64_t ticks = epee::misc_utils::get_tick_count(); \ - epee::serialization::portable_storage ps; \ - if(!ps.load_from_json(query_info.m_body)) \ - { \ - boost::value_initialized rsp; \ - 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); \ - return true; \ - } \ - epee::serialization::storage_entry id_; \ - id_ = epee::serialization::storage_entry(std::string()); \ - ps.get_value("id", id_, nullptr); \ - std::string callback_name; \ - if(!ps.get_value("method", callback_name, nullptr)) \ - { \ - epee::json_rpc::error_response rsp; \ - rsp.jsonrpc = "2.0"; \ - rsp.error.code = -32600; \ - rsp.error.message = "Invalid Request"; \ - epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ - return true; \ - } \ - if(false) return true; //just a stub to have "else if" - - -#define PREPARE_OBJECTS_FROM_JSON(command_type) \ - handled = true; \ - boost::value_initialized > req_; \ - epee::json_rpc::request& req = static_cast&>(req_);\ - if(!req.load(ps)) \ - { \ - epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ - fail_resp.jsonrpc = "2.0"; \ - fail_resp.id = req.id; \ - fail_resp.error.code = -32602; \ - fail_resp.error.message = "Invalid params"; \ - epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ - return true; \ - } \ - uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ - boost::value_initialized > resp_; \ - epee::json_rpc::response& resp = static_cast &>(resp_); \ - resp.jsonrpc = "2.0"; \ - resp.id = req.id; - -#define FINALIZE_OBJECTS_TO_JSON(method_name) \ - uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ - epee::serialization::store_t_to_json(resp, response_info.m_body); \ - uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ - response_info.m_mime_tipe = "application/json"; \ - response_info.m_header_info.m_content_type = " application/json"; \ - LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); - -#define MAP_JON_RPC_WE(method_name, callback_f, command_type) \ - else if(callback_name == method_name) \ -{ \ - PREPARE_OBJECTS_FROM_JSON(command_type) \ - epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ - fail_resp.jsonrpc = "2.0"; \ - fail_resp.id = req.id; \ - if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context)) \ - { \ - epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ - return true; \ - } \ - FINALIZE_OBJECTS_TO_JSON(method_name) \ - return true;\ -} - -#define MAP_JON_RPC_WERI(method_name, callback_f, command_type) \ - else if(callback_name == method_name) \ -{ \ - PREPARE_OBJECTS_FROM_JSON(command_type) \ - epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ - fail_resp.jsonrpc = "2.0"; \ - fail_resp.id = req.id; \ - 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); \ - return true; \ - } \ - FINALIZE_OBJECTS_TO_JSON(method_name) \ - return true;\ -} - -#define MAP_JON_RPC(method_name, callback_f, command_type) \ - else if(callback_name == method_name) \ -{ \ - PREPARE_OBJECTS_FROM_JSON(command_type) \ - if(!callback_f(req.params, resp.result, m_conn_context)) \ - { \ - epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ - fail_resp.jsonrpc = "2.0"; \ - fail_resp.id = req.id; \ - fail_resp.error.code = -32603; \ - fail_resp.error.message = "Internal error"; \ - epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ - return true; \ - } \ - FINALIZE_OBJECTS_TO_JSON(method_name) \ - return true;\ -} - -#define END_JSON_RPC_MAP() \ - epee::json_rpc::error_response rsp; \ - rsp.id = id_; \ - rsp.jsonrpc = "2.0"; \ - rsp.error.code = -32601; \ - rsp.error.message = "Method not found"; \ - epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ - return true; \ -} - - diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h deleted file mode 100644 index c02475c34f..0000000000 --- a/contrib/epee/include/net/http_server_impl_base.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "net/http_server_cp2.h" -#include "net/http_server_handlers_map2.h" - -namespace epee -{ - - template - class http_server_impl_base: public net_utils::http::i_http_server_handler - { - - public: - http_server_impl_base() - : m_net_server() - {} - - explicit http_server_impl_base(boost::asio::io_service& external_io_service) - : m_net_server(external_io_service) - {} - - bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0") - { - - //set self as callback handler - m_net_server.get_config_object().m_phandler = static_cast(this); - - //here set folder for hosting reqests - m_net_server.get_config_object().m_folder = ""; - - LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); - bool res = m_net_server.init_server(bind_port, bind_ip); - if(!res) - { - LOG_ERROR("Failed to bind server"); - return false; - } - return true; - } - - bool run(size_t threads_count, bool wait = true) - { - //go to loop - LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0); - if(!m_net_server.run_server(threads_count, wait)) - { - LOG_ERROR("Failed to run net tcp server!"); - } - - if(wait) - LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); - return true; - } - - bool deinit() - { - return m_net_server.deinit_server(); - } - - bool timed_wait_server_stop(uint64_t ms) - { - return m_net_server.timed_wait_server_stop(ms); - } - - bool send_stop_signal() - { - m_net_server.send_stop_signal(); - return true; - } - - int get_binded_port() - { - return m_net_server.get_binded_port(); - } - - protected: - net_utils::boosted_tcp_server > m_net_server; - }; -} \ No newline at end of file diff --git a/contrib/epee/include/net/http_server_thread_per_connect.h b/contrib/epee/include/net/http_server_thread_per_connect.h deleted file mode 100644 index bec43b726e..0000000000 --- a/contrib/epee/include/net/http_server_thread_per_connect.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _HTTP_SERVER_CP_H_ -#define _HTTP_SERVER_CP_H_ - -#include "abstract_tcp_server.h" -#include "http_server.h" - -namespace epee -{ -namespace net_utils -{ - typedef abstract_tcp_server mt_http_server_file_system; - typedef abstract_tcp_server mt_http_server_custum_handling; - -} -} - - -#endif - - diff --git a/contrib/epee/include/net/jsonrpc_protocol_handler.h b/contrib/epee/include/net/jsonrpc_protocol_handler.h deleted file mode 100644 index b224c34292..0000000000 --- a/contrib/epee/include/net/jsonrpc_protocol_handler.h +++ /dev/null @@ -1,167 +0,0 @@ -#ifndef JSONRPC_PROTOCOL_HANDLER_H -#define JSONRPC_PROTOCOL_HANDLER_H - -#include -#include - -#include "net/net_utils_base.h" -#include "jsonrpc_structs.h" -#include "storages/portable_storage.h" -#include "storages/portable_storage_template_helper.h" - -namespace epee -{ -namespace net_utils -{ - namespace jsonrpc2 - { - inline - std::string& make_error_resp_json(int64_t code, const std::string& message, - std::string& response_data, - const epee::serialization::storage_entry& id = nullptr) - { - epee::json_rpc::error_response rsp; - rsp.id = id; - rsp.jsonrpc = "2.0"; - rsp.error.code = code; - rsp.error.message = message; - epee::serialization::store_t_to_json(static_cast(rsp), response_data, 0, false); - response_data += "\n"; - return response_data; - } - - template - struct i_jsonrpc2_server_handler - { - virtual ~i_jsonrpc2_server_handler() - {} - virtual bool handle_rpc_request(const std::string& req_data, - std::string& resp_data, - t_connection_context& conn_context) = 0; - virtual bool init_server_thread() - { return true; } - virtual bool deinit_server_thread() - { return true; } - }; - - template - struct jsonrpc2_server_config - { - i_jsonrpc2_server_handler* m_phandler; - critical_section m_lock; - }; - - template - class jsonrpc2_connection_handler - { - public: - typedef t_connection_context connection_context; - typedef jsonrpc2_server_config config_type; - - jsonrpc2_connection_handler(i_service_endpoint* psnd_hndlr, - config_type& config, - t_connection_context& conn_context) - : m_psnd_hndlr(psnd_hndlr), - m_config(config), - m_conn_context(conn_context), - m_is_stop_handling(false) - {} - virtual ~jsonrpc2_connection_handler() - {} - - bool release_protocol() - { - return true; - } - virtual bool thread_init() - { - return true; - } - virtual bool thread_deinit() - { - return true; - } - void handle_qued_callback() - {} - bool after_init_connection() - { - return true; - } - virtual bool handle_recv(const void* ptr, size_t cb) - { - std::string buf((const char*)ptr, cb); - LOG_PRINT_L0("JSONRPC2_RECV: " << ptr << "\r\n" << buf); - - bool res = handle_buff_in(buf); - return res; - } - private: - bool handle_buff_in(std::string& buf) - { - if(m_cache.size()) - m_cache += buf; - else - m_cache.swap(buf); - - m_is_stop_handling = false; - while (!m_is_stop_handling) { - std::string::size_type pos = match_end_of_request(m_cache); - if (std::string::npos == pos) { - m_is_stop_handling = true; - if (m_cache.size() > 4096) { - LOG_ERROR("jsonrpc2_connection_handler::handle_buff_in: Too long request"); - return false; - } - break; - } else { - extract_cached_request_and_handle(pos); - } - - if (!m_cache.size()) { - m_is_stop_handling = true; - } - } - - return true; - } - bool extract_cached_request_and_handle(std::string::size_type pos) - { - std::string request_data(m_cache.begin(), m_cache.begin() + pos); - m_cache.erase(0, pos); - return handle_request_and_send_response(request_data); - } - bool handle_request_and_send_response(const std::string& request_data) - { - CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!"); - std::string response_data; - - LOG_PRINT_L3("JSONRPC2_REQUEST: >> \r\n" << request_data); - bool rpc_result = m_config.m_phandler->handle_rpc_request(request_data, response_data, m_conn_context); - LOG_PRINT_L3("JSONRPC2_RESPONSE: << \r\n" << response_data); - - m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); - return rpc_result; - } - std::string::size_type match_end_of_request(const std::string& buf) - { - std::string::size_type res = buf.find("\n"); - if(std::string::npos != res) { - return res + 2; - } - return res; - } - - protected: - i_service_endpoint* m_psnd_hndlr; - - private: - config_type& m_config; - t_connection_context& m_conn_context; - std::string m_cache; - bool m_is_stop_handling; - }; - } -} -} - -#endif /* JSONRPC_PROTOCOL_HANDLER_H */ diff --git a/contrib/epee/include/net/jsonrpc_server_handlers_map.h b/contrib/epee/include/net/jsonrpc_server_handlers_map.h deleted file mode 100644 index 8c747d1af4..0000000000 --- a/contrib/epee/include/net/jsonrpc_server_handlers_map.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef JSONRPC_SERVER_HANDLERS_MAP_H -#define JSONRPC_SERVER_HANDLERS_MAP_H - -#include -#include "serialization/keyvalue_serialization.h" -#include "storages/portable_storage_template_helper.h" -#include "storages/portable_storage_base.h" -#include "jsonrpc_structs.h" -#include "jsonrpc_protocol_handler.h" - -#define BEGIN_JSONRPC2_MAP(t_connection_context) \ -bool handle_rpc_request(const std::string& req_data, \ - std::string& resp_data, \ - t_connection_context& m_conn_context) \ -{ \ - bool handled = false; \ - uint64_t ticks = epee::misc_utils::get_tick_count(); \ - epee::serialization::portable_storage ps; \ - if (!ps.load_from_json(req_data)) \ - { \ - epee::net_utils::jsonrpc2::make_error_resp_json(-32700, "Parse error", resp_data); \ - return true; \ - } \ - epee::serialization::storage_entry id_; \ - id_ = epee::serialization::storage_entry(std::string()); \ - if (!ps.get_value("id", id_, nullptr)) \ - { \ - epee::net_utils::jsonrpc2::make_error_resp_json(-32600, "Invalid Request", resp_data); \ - return true; \ - } \ - std::string callback_name; \ - if (!ps.get_value("method", callback_name, nullptr)) \ - { \ - epee::net_utils::jsonrpc2::make_error_resp_json(-32600, "Invalid Request", resp_data, id_); \ - return true; \ - } \ - if (false) return true; //just a stub to have "else if" - - - -#define PREPARE_JSONRPC2_OBJECTS_FROM_JSON(command_type) \ - handled = true; \ - boost::value_initialized > req_; \ - epee::json_rpc::request& req = static_cast&>(req_);\ - if(!req.load(ps)) \ - { \ - epee::net_utils::jsonrpc2::make_error_resp_json(-32602, "Invalid params", resp_data, req.id); \ - return true; \ - } \ - uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ - boost::value_initialized > resp_; \ - epee::json_rpc::response& resp = static_cast &>(resp_); \ - resp.jsonrpc = "2.0"; \ - resp.id = req.id; - -#define FINALIZE_JSONRPC2_OBJECTS_TO_JSON(method_name) \ - uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ - epee::serialization::store_t_to_json(resp, resp_data, 0, false); \ - resp_data += "\n"; \ - uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ - LOG_PRINT("[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); - - -#define MAP_JSONRPC2_WE(method_name, callback_f, command_type) \ - else if (callback_name == method_name) \ - { \ - PREPARE_JSONRPC2_OBJECTS_FROM_JSON(command_type) \ - epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ - fail_resp.jsonrpc = "2.0"; \ - fail_resp.id = req.id; \ - if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context)) \ - { \ - epee::serialization::store_t_to_json(static_cast(fail_resp), resp_data, 0, false); \ - resp_data += "\n"; \ - return true; \ - } \ - FINALIZE_JSONRPC2_OBJECTS_TO_JSON(method_name) \ - return true; \ - } - -#define END_JSONRPC2_MAP() \ - epee::net_utils::jsonrpc2::make_error_resp_json(-32601, "Method not found", resp_data, id_); \ - return true; \ -} - -#endif /* JSONRPC_SERVER_HANDLERS_MAP_H */ diff --git a/contrib/epee/include/net/jsonrpc_server_impl_base.h b/contrib/epee/include/net/jsonrpc_server_impl_base.h deleted file mode 100644 index 8a5a9a5b68..0000000000 --- a/contrib/epee/include/net/jsonrpc_server_impl_base.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef JSONRPC_SERVER_IMPL_BASE_H -#define JSONRPC_SERVER_IMPL_BASE_H - -#include -#include - -#include "net/jsonrpc_protocol_handler.h" -#include "net/jsonrpc_server_handlers_map.h" -#include "net/abstract_tcp_server2.h" - -namespace epee -{ - -template - class jsonrpc_server_impl_base: public net_utils::jsonrpc2::i_jsonrpc2_server_handler - { - - public: - jsonrpc_server_impl_base() - : m_net_server() - {} - - explicit jsonrpc_server_impl_base(boost::asio::io_service& external_io_service) - : m_net_server(external_io_service) - {} - - bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0") - { - //set self as callback handler - m_net_server.get_config_object().m_phandler = static_cast(this); - - LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); - bool res = m_net_server.init_server(bind_port, bind_ip); - if (!res) - { - LOG_ERROR("Failed to bind server"); - return false; - } - return true; - } - - bool run(size_t threads_count, bool wait = true) - { - //go to loop - LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0); - if(!m_net_server.run_server(threads_count, wait)) - { - LOG_ERROR("Failed to run net tcp server!"); - } - - if(wait) - LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); - return true; - } - - bool deinit() - { - return m_net_server.deinit_server(); - } - - bool timed_wait_server_stop(uint64_t ms) - { - return m_net_server.timed_wait_server_stop(ms); - } - - bool send_stop_signal() - { - m_net_server.send_stop_signal(); - return true; - } - - int get_binded_port() - { - return m_net_server.get_binded_port(); - } - - protected: - net_utils::boosted_tcp_server > m_net_server; - }; - -} - -#endif /* JSONRPC_SERVER_IMPL_BASE_H */ - diff --git a/contrib/epee/include/net/jsonrpc_structs.h b/contrib/epee/include/net/jsonrpc_structs.h deleted file mode 100644 index 9df9e25961..0000000000 --- a/contrib/epee/include/net/jsonrpc_structs.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef JSONRPC_STRUCTS_H -#define JSONRPC_STRUCTS_H - -#include -#include -#include "serialization/keyvalue_serialization.h" -#include "storages/portable_storage_base.h" - -namespace epee -{ - namespace json_rpc - { - template - struct request - { - std::string jsonrpc; - std::string method; - epee::serialization::storage_entry id; - t_param params; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(jsonrpc) - KV_SERIALIZE(id) - KV_SERIALIZE(method) - KV_SERIALIZE(params) - END_KV_SERIALIZE_MAP() - }; - - struct error - { - int64_t code; - std::string message; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(code) - KV_SERIALIZE(message) - END_KV_SERIALIZE_MAP() - }; - - struct dummy_error - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct dummy_result - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - template - struct response - { - std::string jsonrpc; - t_param result; - epee::serialization::storage_entry id; - t_error error; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(jsonrpc) - KV_SERIALIZE(id) - KV_SERIALIZE(result) - KV_SERIALIZE(error) - END_KV_SERIALIZE_MAP() - }; - - template - struct response - { - std::string jsonrpc; - t_param result; - epee::serialization::storage_entry id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(jsonrpc) - KV_SERIALIZE(id) - KV_SERIALIZE(result) - END_KV_SERIALIZE_MAP() - }; - - template - struct response - { - std::string jsonrpc; - t_error error; - epee::serialization::storage_entry id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(jsonrpc) - KV_SERIALIZE(id) - KV_SERIALIZE(error) - END_KV_SERIALIZE_MAP() - }; - - typedef response error_response; - } -} - -#endif /* JSONRPC_STRUCTS_H */ diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h deleted file mode 100644 index d630bff198..0000000000 --- a/contrib/epee/include/net/levin_base.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _LEVIN_BASE_H_ -#define _LEVIN_BASE_H_ - -#include "net_utils_base.h" - -#define LEVIN_SIGNATURE 0x0101010101012101LL //Bender's nightmare - -namespace epee -{ -namespace levin -{ -#pragma pack(push) -#pragma pack(1) - struct bucket_head - { - uint64_t m_signature; - uint64_t m_cb; - bool m_have_to_return_data; - uint32_t m_command; - int32_t m_return_code; - uint32_t m_reservedA; //probably some flags in future - uint32_t m_reservedB; //probably some check sum in future - }; -#pragma pack(pop) - - -#pragma pack(push) -#pragma pack(1) - struct bucket_head2 - { - uint64_t m_signature; - uint64_t m_cb; - bool m_have_to_return_data; - uint32_t m_command; - int32_t m_return_code; - uint32_t m_flags; - uint32_t m_protocol_version; - }; -#pragma pack(pop) - - -#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0 -#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default - -#define LEVIN_PACKET_REQUEST 0x00000001 -#define LEVIN_PACKET_RESPONSE 0x00000002 - - -#define LEVIN_PROTOCOL_VER_0 0 -#define LEVIN_PROTOCOL_VER_1 1 - - template - struct levin_commands_handler - { - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, t_connection_context& context)=0; - virtual int notify(int command, const std::string& in_buff, t_connection_context& context)=0; - virtual void callback(t_connection_context& context){}; - - virtual void on_connection_new(t_connection_context& context){}; - virtual void on_connection_close(t_connection_context& context){}; - - }; - -#define LEVIN_OK 0 -#define LEVIN_ERROR_CONNECTION -1 -#define LEVIN_ERROR_CONNECTION_NOT_FOUND -2 -#define LEVIN_ERROR_CONNECTION_DESTROYED -3 -#define LEVIN_ERROR_CONNECTION_TIMEDOUT -4 -#define LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL -5 -#define LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED -6 -#define LEVIN_ERROR_FORMAT -7 - -#define DESCRIBE_RET_CODE(code) case code: return #code; - inline - const char* get_err_descr(int err) - { - switch(err) - { - DESCRIBE_RET_CODE(LEVIN_OK); - DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION); - DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NOT_FOUND); - DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_DESTROYED); - DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_TIMEDOUT); - DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL); - DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED); - DESCRIBE_RET_CODE(LEVIN_ERROR_FORMAT); - default: - return "unknown code"; - } - } - - -} -} - - -#endif //_LEVIN_BASE_H_ diff --git a/contrib/epee/include/net/levin_client.h b/contrib/epee/include/net/levin_client.h deleted file mode 100644 index 335f6ba02f..0000000000 --- a/contrib/epee/include/net/levin_client.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _LEVIN_CLIENT_H_ -#define _LEVIN_CLIENT_H_ - -#include "net_helper.h" -#include "levin_base.h" - - -#ifndef MAKE_IP -#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) -#endif - -namespace epee -{ -namespace levin -{ - /************************************************************************/ - /* */ - /************************************************************************/ - class levin_client_impl - { - public: - levin_client_impl(); - virtual ~levin_client_impl(); - - bool connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0"); - bool connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0"); - bool is_connected(); - bool disconnect(); - - virtual int invoke(int command, const std::string& in_buff, std::string& buff_out); - virtual int notify(int command, const std::string& in_buff); - - protected: - net_utils::blocked_mode_client m_transport; - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - class levin_client_impl2: public levin_client_impl - { - public: - - int invoke(int command, const std::string& in_buff, std::string& buff_out); - int notify(int command, const std::string& in_buff); - }; - -} -namespace net_utils -{ - typedef levin::levin_client_impl levin_client; - typedef levin::levin_client_impl2 levin_client2; -} -} - -#include "levin_client.inl" - -#endif //_LEVIN_CLIENT_H_ diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl deleted file mode 100644 index ae159da6e5..0000000000 --- a/contrib/epee/include/net/levin_client.inl +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "string_tools.h" -namespace epee -{ -namespace levin -{ -inline -bool levin_client_impl::connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip) -{ - return m_transport.connect(string_tools::get_ip_string_from_int32(ip), port, timeout, timeout, bind_ip); -} -//------------------------------------------------------------------------------ -inline - bool levin_client_impl::connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip) -{ - return m_transport.connect(addr, port, timeout, timeout, bind_ip); -} -//------------------------------------------------------------------------------ -inline -bool levin_client_impl::is_connected() -{ - return m_transport.is_connected(); -} -//------------------------------------------------------------------------------ -inline -bool levin_client_impl::disconnect() -{ - return m_transport.disconnect(); -} -//------------------------------------------------------------------------------ -inline -levin_client_impl::levin_client_impl() -{ -} -//------------------------------------------------------------------------------ -inline -levin_client_impl::~levin_client_impl() -{ - disconnect(); -} -//------------------------------------------------------------------------------ -inline -int levin_client_impl::invoke(int command, const std::string& in_buff, std::string& buff_out) -{ - if(!is_connected()) - return -1; - - bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); - head.m_have_to_return_data = true; - head.m_command = command; - if(!m_transport.send(&head, sizeof(head))) - return -1; - - if(!m_transport.send(in_buff)) - return -1; - - std::string local_buff; - if(!m_transport.recv_n(local_buff, sizeof(bucket_head))) - return -1; - - head = *(bucket_head*)local_buff.data(); - - - if(head.m_signature!=LEVIN_SIGNATURE) - { - LOG_PRINT_L0("Signature missmatch in response"); - return -1; - } - - if(!m_transport.recv_n(buff_out, head.m_cb)) - return -1; - - return head.m_return_code; -} -//------------------------------------------------------------------------------ -inline -int levin_client_impl::notify(int command, const std::string& in_buff) -{ - if(!is_connected()) - return -1; - - bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); - head.m_have_to_return_data = false; - head.m_command = command; - - if(!m_transport.send((const char*)&head, sizeof(head))) - return -1; - - if(!m_transport.send(in_buff)) - return -1; - - return 1; -} - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -inline - int levin_client_impl2::invoke(int command, const std::string& in_buff, std::string& buff_out) -{ - if(!is_connected()) - return -1; - - bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = 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; - if(!m_transport.send(&head, sizeof(head))) - return -1; - - if(!m_transport.send(in_buff)) - return -1; - - std::string local_buff; - if(!m_transport.recv_n(local_buff, sizeof(bucket_head2))) - return -1; - - head = *(bucket_head2*)local_buff.data(); - - - if(head.m_signature!=LEVIN_SIGNATURE) - { - LOG_PRINT_L0("Signature missmatch in response"); - return -1; - } - - if(!m_transport.recv_n(buff_out, head.m_cb)) - return -1; - - return head.m_return_code; -} -//------------------------------------------------------------------------------ -inline - int levin_client_impl2::notify(int command, const std::string& in_buff) -{ - if(!is_connected()) - return -1; - - bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = 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; - - if(!m_transport.send((const char*)&head, sizeof(head))) - return -1; - - if(!m_transport.send(in_buff)) - return -1; - - return 1; -} - -} -} -//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h deleted file mode 100644 index 9e76cd5099..0000000000 --- a/contrib/epee/include/net/levin_client_async.h +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "net_helper.h" -#include "levin_base.h" - - -namespace epee -{ -namespace levin -{ - - /************************************************************************ - * levin_client_async - probably it is not really fast implementation, - * each handler thread could make up to 30 ms latency. - * But, handling events in reader thread will cause dead locks in - * case of recursive call (call invoke() to the same connection - * on reader thread on remote invoke() handler) - ***********************************************************************/ - - - class levin_client_async - { - levin_commands_handler* m_pcommands_handler; - volatile uint32_t m_is_stop; - volatile uint32_t m_threads_count; - ::critical_section m_send_lock; - - std::string m_local_invoke_buff; - ::critical_section m_local_invoke_buff_lock; - volatile int m_invoke_res; - - volatile uint32_t m_invoke_data_ready; - volatile uint32_t m_invoke_is_active; - - boost::mutex m_invoke_event; - boost::condition_variable m_invoke_cond; - size_t m_timeout; - - ::critical_section m_recieved_packets_lock; - struct packet_entry - { - bucket_head m_hd; - std::string m_body; - uint32_t m_connection_index; - }; - std::list m_recieved_packets; - /* - m_current_connection_index needed when some connection was broken and reconnected - in this - case we could have some received packets in que, which shoud not be handled - */ - volatile uint32_t m_current_connection_index; - ::critical_section m_invoke_lock; - ::critical_section m_reciev_packet_lock; - ::critical_section m_connection_lock; - net_utils::blocked_mode_client m_transport; - public: - levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) - {} - levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) - {} - ~levin_client_async() - { - boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1); - disconnect(); - - - while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count)) - ::Sleep(100); - } - - void set_handler(levin_commands_handler* phandler) - { - m_pcommands_handler = phandler; - } - - bool connect(uint32_t ip, uint32_t port, uint32_t timeout) - { - loop_call_guard(); - critical_region cr(m_connection_lock); - - m_timeout = timeout; - bool res = false; - CRITICAL_REGION_BEGIN(m_reciev_packet_lock); - CRITICAL_REGION_BEGIN(m_send_lock); - res = levin_client_impl::connect(ip, port, timeout); - boost::interprocess::ipcdetail::atomic_inc32(&m_current_connection_index); - CRITICAL_REGION_END(); - CRITICAL_REGION_END(); - if(res && !boost::interprocess::ipcdetail::atomic_read32(&m_threads_count) ) - { - //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 0);//m_is_stop = false; - boost::thread( boost::bind(&levin_duplex_client::reciever_thread, this) ); - boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) ); - boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) ); - } - - return res; - } - bool is_connected() - { - loop_call_guard(); - critical_region cr(m_cs); - return levin_client_impl::is_connected(); - } - - inline - bool check_connection() - { - loop_call_guard(); - critical_region cr(m_cs); - - if(!is_connected()) - { - if( !reconnect() ) - { - LOG_ERROR("Reconnect Failed. Failed to invoke() becouse not connected!"); - return false; - } - } - return true; - } - - //------------------------------------------------------------------------------ - inline - bool recv_n(SOCKET s, char* pbuff, size_t cb) - { - while(cb) - { - int res = ::recv(m_socket, pbuff, (int)cb, 0); - - if(SOCKET_ERROR == res) - { - if(!m_connected) - return false; - - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to recv(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - disconnect(); - //reconnect(); - return false; - }else if(res == 0) - { - disconnect(); - //reconnect(); - return false; - } - LOG_PRINT_L4("[" << m_socket <<"] RECV " << res); - cb -= res; - pbuff += res; - } - - return true; - } - - //------------------------------------------------------------------------------ - inline - bool recv_n(SOCKET s, std::string& buff) - { - size_t cb_remain = buff.size(); - char* m_current_ptr = (char*)buff.data(); - return recv_n(s, m_current_ptr, cb_remain); - } - - bool disconnect() - { - //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);//m_is_stop = true; - loop_call_guard(); - critical_region cr(m_cs); - levin_client_impl::disconnect(); - - CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); - m_local_invoke_buff.clear(); - m_invoke_res = LEVIN_ERROR_CONNECTION_DESTROYED; - CRITICAL_REGION_END(); - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true; - m_invoke_cond.notify_all(); - return true; - } - - void loop_call_guard() - { - - } - - void on_leave_invoke() - { - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 0); - } - - int invoke(const GUID& target, int command, const std::string& in_buff, std::string& buff_out) - { - - critical_region cr_invoke(m_invoke_lock); - - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 1); - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 0); - misc_utils::destr_ptr hdlr = misc_utils::add_exit_scope_handler(boost::bind(&levin_duplex_client::on_leave_invoke, this)); - - loop_call_guard(); - - if(!check_connection()) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - - bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); - head.m_have_to_return_data = true; - head.m_id = target; -#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS - ::UuidCreate(&head.m_id); -#endif - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; - LOG_PRINT("[" << m_socket <<"] Sending invoke data", LOG_LEVEL_4); - - CRITICAL_REGION_BEGIN(m_send_lock); - LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head)); - int res = ::send(m_socket, (const char*)&head, sizeof(head), 0); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - disconnect(); - return LEVIN_ERROR_CONNECTION_DESTROYED; - } - LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size()); - res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - disconnect(); - return LEVIN_ERROR_CONNECTION_DESTROYED; - } - CRITICAL_REGION_END(); - LOG_PRINT_L4("LEVIN_PACKET_SENT. [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) << "]"); - - //hard coded timeout in 10 minutes for maximum invoke period. if it happens, it could mean only some real troubles. - boost::system_time timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100); - size_t timeout_count = 0; - boost::unique_lock lock(m_invoke_event); - - while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_data_ready)) - { - if(!m_invoke_cond.timed_wait(lock, timeout)) - { - if(timeout_count < 10) - { - //workaround to avoid freezing at timed_wait called after notify_all. - timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100); - ++timeout_count; - continue; - }else if(timeout_count == 10) - { - //workaround to avoid freezing at timed_wait called after notify_all. - timeout = boost::get_system_time()+ boost::posix_time::minutes(10); - ++timeout_count; - continue; - }else - { - LOG_PRINT("[" << m_socket <<"] Timeout on waiting invoke result. ", LOG_LEVEL_0); - //disconnect(); - return LEVIN_ERROR_CONNECTION_TIMEDOUT; - } - } - } - - - CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); - buff_out.swap(m_local_invoke_buff); - m_local_invoke_buff.clear(); - CRITICAL_REGION_END(); - return m_invoke_res; - } - - int notify(const GUID& target, int command, const std::string& in_buff) - { - if(!check_connection()) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - bucket_head head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = in_buff.size(); - head.m_have_to_return_data = false; - head.m_id = target; -#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS - ::UuidCreate(&head.m_id); -#endif - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; - CRITICAL_REGION_BEGIN(m_send_lock); - LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head)); - int res = ::send(m_socket, (const char*)&head, sizeof(head), 0); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - disconnect(); - return LEVIN_ERROR_CONNECTION_DESTROYED; - } - LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size()); - res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0); - if(SOCKET_ERROR == res) - { - int err = ::WSAGetLastError(); - LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); - disconnect(); - return LEVIN_ERROR_CONNECTION_DESTROYED; - } - CRITICAL_REGION_END(); - LOG_PRINT_L4("LEVIN_PACKET_SENT. [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) << "]"); - - return 1; - } - - - private: - bool have_some_data(SOCKET sock, int interval = 1) - { - fd_set fds; - FD_ZERO(&fds); - FD_SET(sock, &fds); - - fd_set fdse; - FD_ZERO(&fdse); - FD_SET(sock, &fdse); - - - timeval tv; - tv.tv_sec = interval; - tv.tv_usec = 0; - - int sel_res = select(0, &fds, 0, &fdse, &tv); - if(0 == sel_res) - return false; - else if(sel_res == SOCKET_ERROR) - { - if(m_is_stop) - return false; - int err_code = ::WSAGetLastError(); - LOG_ERROR("Filed to call select, err code = " << err_code); - disconnect(); - }else - { - if(fds.fd_array[0]) - {//some read operations was performed - return true; - }else if(fdse.fd_array[0]) - {//some error was at the socket - return true; - } - } - return false; - } - - - bool reciev_and_process_incoming_data() - { - bucket_head head = {0}; - uint32_t conn_index = 0; - bool is_request = false; - std::string local_buff; - CRITICAL_REGION_BEGIN(m_reciev_packet_lock);//to protect from socket reconnect between head and body - - if(!recv_n(m_socket, (char*)&head, sizeof(head))) - { - if(m_is_stop) - return false; - LOG_ERROR("Failed to recv_n"); - return false; - } - - conn_index = boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index); - - if(head.m_signature!=LEVIN_SIGNATURE) - { - LOG_ERROR("Signature missmatch in response"); - return false; - } - - is_request = (head.m_protocol_version == LEVIN_PROTOCOL_VER_1 && head.m_flags&LEVIN_PACKET_REQUEST); - - - local_buff.resize((size_t)head.m_cb); - if(!recv_n(m_socket, local_buff)) - { - if(m_is_stop) - return false; - LOG_ERROR("Filed to reciev"); - return false; - } - 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) << "]"); - - if(is_request) - { - CRITICAL_REGION_BEGIN(m_recieved_packets_lock); - m_recieved_packets.resize(m_recieved_packets.size() + 1); - m_recieved_packets.back().m_hd = head; - m_recieved_packets.back().m_body.swap(local_buff); - m_recieved_packets.back().m_connection_index = conn_index; - CRITICAL_REGION_END(); - /* - - */ - }else - {//this is some response - - CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); - m_local_invoke_buff.swap(local_buff); - m_invoke_res = head.m_return_code; - CRITICAL_REGION_END(); - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true; - m_invoke_cond.notify_all(); - - } - return true; - } - - bool reciever_thread() - { - LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread started.[m_threads_count=" << m_threads_count << "]"); - log_space::log_singletone::set_thread_log_prefix("RECIEVER_WORKER"); - boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count); - - while(!m_is_stop) - { - if(!m_connected) - { - Sleep(100); - continue; - } - - if(have_some_data(m_socket, 1)) - { - if(!reciev_and_process_incoming_data()) - { - if(m_is_stop) - { - break;//boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); - //return true; - } - LOG_ERROR("Failed to reciev_and_process_incoming_data. shutting down"); - //boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); - //disconnect_no_wait(); - //break; - } - } - } - - boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); - LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread stopped.[m_threads_count=" << m_threads_count << "]"); - return true; - } - - bool process_recieved_packet(bucket_head& head, const std::string& local_buff, uint32_t conn_index) - { - - net_utils::connection_context_base conn_context; - conn_context.m_remote_ip = m_ip; - conn_context.m_remote_port = m_port; - if(head.m_have_to_return_data) - { - std::string return_buff; - if(m_pcommands_handler) - head.m_return_code = m_pcommands_handler->invoke(head.m_id, head.m_command, local_buff, return_buff, conn_context); - else - head.m_return_code = LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; - - - - head.m_cb = return_buff.size(); - head.m_have_to_return_data = false; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_RESPONSE; - - std::string send_buff((const char*)&head, sizeof(head)); - send_buff += return_buff; - CRITICAL_REGION_BEGIN(m_send_lock); - if(conn_index != boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index)) - {//there was reconnect, send response back is not allowed - return true; - } - int res = ::send(m_socket, (const char*)send_buff.data(), send_buff.size(), 0); - if(res == SOCKET_ERROR) - { - int err_code = ::WSAGetLastError(); - LOG_ERROR("Failed to send, err = " << err_code); - return false; - } - CRITICAL_REGION_END(); - LOG_PRINT_L4("LEVIN_PACKET_SENT. [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) << "]"); - - } - else - { - if(m_pcommands_handler) - m_pcommands_handler->notify(head.m_id, head.m_command, local_buff, conn_context); - } - - return true; - } - - bool handler_thread() - { - LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread started.[m_threads_count=" << m_threads_count << "]"); - log_space::log_singletone::set_thread_log_prefix("HANDLER_WORKER"); - boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count); - - while(!m_is_stop) - { - bool have_some_work = false; - std::string local_buff; - bucket_head bh = {0}; - uint32_t conn_index = 0; - - CRITICAL_REGION_BEGIN(m_recieved_packets_lock); - if(m_recieved_packets.size()) - { - bh = m_recieved_packets.begin()->m_hd; - conn_index = m_recieved_packets.begin()->m_connection_index; - local_buff.swap(m_recieved_packets.begin()->m_body); - have_some_work = true; - m_recieved_packets.pop_front(); - } - CRITICAL_REGION_END(); - - if(have_some_work) - { - process_recieved_packet(bh, local_buff, conn_index); - }else - { - //Idle when no work - Sleep(30); - } - } - - boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); - LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread stopped.[m_threads_count=" << m_threads_count << "]"); - return true; - } - }; - -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/levin_client_async.inl b/contrib/epee/include/net/levin_client_async.inl deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h deleted file mode 100644 index a8406103ca..0000000000 --- a/contrib/epee/include/net/levin_helper.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "levin_base.h" -#include "serializeble_struct_helper.h" - -namespace epee -{ -namespace levin -{ - template - bool pack_struct_to_levin_message(const t_struct& t, std::string& buff, int command_id) - { - buff.resize(sizeof(levin::bucket_head)); - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - head.m_signature = 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_reservedA = rand(); //probably some flags in future - head.m_reservedB = rand(); //probably some check summ in future - - std::string buff_strg; - if(!StorageNamed::save_struct_as_storage_to_buff_t(t, buff_strg)) - return false; - - head.m_cb = buff_strg.size(); - buff.append(buff_strg); - return true; - } - - - bool pack_data_to_levin_message(const std::string& data, std::string& buff, int command_id) - { - buff.resize(sizeof(levin::bucket_head)); - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - head.m_signature = 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_reservedA = rand(); //probably some flags in future - head.m_reservedB = rand(); //probably some check summ in future - - head.m_cb = data.size(); - buff.append(data); - return true; - } - - bool load_levin_data_from_levin_message(std::string& levin_data, const std::string& buff, int& command) - { - if(buff.size() < sizeof(levin::bucket_head) ) - { - LOG_PRINT_L3("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message"); - return false; - } - - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - if(head.m_signature != LEVIN_SIGNATURE) - { - LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message"); - return false; - } - if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) - { - LOG_PRINT_L3("sizes missmatch, at load_struct_from_levin_message"); - return false; - } - - //std::string buff_strg; - levin_data.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head)); - command = head.m_command; - return true; - } - - template - bool load_struct_from_levin_message(t_struct& t, const std::string& buff, int& command) - { - if(buff.size() < sizeof(levin::bucket_head) ) - { - LOG_ERROR("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message"); - return false; - } - - levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); - if(head.m_signature != LEVIN_SIGNATURE) - { - LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message"); - return false; - } - if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) - { - LOG_ERROR("sizes missmatch, at load_struct_from_levin_message"); - return false; - } - - std::string buff_strg; - buff_strg.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head)); - - if(!StorageNamed::load_struct_from_storage_buff_t(t, buff_strg)) - { - LOG_ERROR("Failed to read storage, at load_struct_from_levin_message"); - return false; - } - command = head.m_command; - return true; - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h deleted file mode 100644 index 512ba1c3ca..0000000000 --- a/contrib/epee/include/net/levin_protocol_handler.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _LEVIN_PROTOCOL_HANDLER_H_ -#define _LEVIN_PROTOCOL_HANDLER_H_ - -#include -#include "levin_base.h" - -namespace epee -{ -namespace levin -{ - template - struct protocl_handler_config - { - levin_commands_handler* m_pcommands_handler; - }; - - template - class protocol_handler - { - public: - typedef t_connection_context connection_context; - typedef protocl_handler_config config_type; - - protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context); - virtual ~protocol_handler(){} - - virtual bool handle_recv(const void* ptr, size_t cb); - - bool after_init_connection(){return true;} - private: - enum connection_data_state - { - conn_state_reading_head, - conn_state_reading_body - }; - - - config_type& m_config; - t_connection_context& m_conn_context; - net_utils::i_service_endpoint* m_psnd_hndlr; - std::string m_cach_in_buffer; - connection_data_state m_state; - bucket_head m_current_head; - }; - - template - protocol_handler::protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context): - m_config(config), - m_conn_context(conn_context), - m_psnd_hndlr(psnd_hndlr), - m_state(conn_state_reading_head), - m_current_head(bucket_head()) - {} - - template - bool protocol_handler::handle_recv(const void* ptr, size_t cb) - { - if(!m_config.m_pcommands_handler) - { - LOG_ERROR("Command handler not set!"); - return false; - } - m_cach_in_buffer.append((const char*)ptr, cb); - - bool is_continue = true; - while(is_continue) - { - switch(m_state) - { - 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) - { - LOG_ERROR("Signature missmatch on accepted connection"); - return false; - } - is_continue = false; - break; - } - { - bucket_head* phead = (bucket_head*)m_cach_in_buffer.data(); - if(LEVIN_SIGNATURE != phead->m_signature) - { - LOG_ERROR("Signature missmatch on accepted connection"); - return false; - } - m_current_head = *phead; - } - m_cach_in_buffer.erase(0, sizeof(bucket_head)); - m_state = conn_state_reading_body; - break; - case conn_state_reading_body: - if(m_cach_in_buffer.size() < m_current_head.m_cb) - { - is_continue = false; - break; - } - { - std::string buff_to_invoke; - if(m_cach_in_buffer.size() == m_current_head.m_cb) - buff_to_invoke.swap(m_cach_in_buffer); - else - { - buff_to_invoke.assign(m_cach_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); - m_cach_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); - } - - - if(m_current_head.m_have_to_return_data) - { - std::string return_buff; - m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(m_current_head.m_command, buff_to_invoke, return_buff, m_conn_context); - m_current_head.m_cb = return_buff.size(); - m_current_head.m_have_to_return_data = false; - std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); - send_buff += return_buff; - - if(!m_psnd_hndlr->do_send(send_buff.data(), send_buff.size())) - return false; - - } - else - m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_conn_context); - } - m_state = conn_state_reading_head; - break; - default: - LOG_ERROR("Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); - return false; - } - } - - return true; - } - - - - - - - -} -} - - - - -#endif //_LEVIN_PROTOCOL_HANDLER_H_ - diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h deleted file mode 100644 index 406d92b285..0000000000 --- a/contrib/epee/include/net/levin_protocol_handler_async.h +++ /dev/null @@ -1,779 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#include "levin_base.h" -#include "misc_language.h" - - -namespace epee -{ -namespace levin -{ - -/************************************************************************/ -/* */ -/************************************************************************/ -template -class async_protocol_handler; - -template -class async_protocol_handler_config -{ - typedef std::map* > connections_map; - critical_section m_connects_lock; - connections_map m_connects; - - void add_connection(async_protocol_handler* pc); - void del_connection(async_protocol_handler* pc); - - async_protocol_handler* find_connection(boost::uuids::uuid connection_id) const; - int find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler*& aph); - - friend class async_protocol_handler; - -public: - typedef t_connection_context connection_context; - levin_commands_handler* m_pcommands_handler; - uint64_t m_max_packet_size; - uint64_t m_invoke_timeout; - - int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id); - template - int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); - - int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id); - bool close(boost::uuids::uuid connection_id); - bool update_connection_context(const t_connection_context& contxt); - bool request_callback(boost::uuids::uuid connection_id); - template - bool foreach_connection(callback_t cb); - size_t get_connections_count(); - - async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) - {} -}; - - -/************************************************************************/ -/* */ -/************************************************************************/ -template -class async_protocol_handler -{ -public: - typedef t_connection_context connection_context; - typedef async_protocol_handler_config config_type; - - enum stream_state - { - stream_state_head, - stream_state_body - }; - - std::atomic m_deletion_initiated; - std::atomic m_protocol_released; - volatile uint32_t m_invoke_buf_ready; - - volatile int m_invoke_result_code; - - critical_section m_local_inv_buff_lock; - std::string m_local_inv_buff; - - critical_section m_send_lock; - critical_section m_call_lock; - - volatile uint32_t m_wait_count; - volatile uint32_t m_close_called; - bucket_head2 m_current_head; - net_utils::i_service_endpoint* m_pservice_endpoint; - config_type& m_config; - t_connection_context& m_connection_context; - - std::string m_cache_in_buffer; - stream_state m_state; - - int32_t m_oponent_protocol_ver; - bool m_connection_initialized; - - struct invoke_response_handler_base - { - virtual bool handle(int res, const std::string& buff, connection_context& context)=0; - virtual bool is_timer_started() const=0; - virtual void cancel()=0; - virtual bool cancel_timer()=0; - }; - template - struct anvoke_handler: invoke_response_handler_base - { - anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command) - :m_cb(cb), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false), - m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command) - { - if(m_con.start_outer_call()) - { - m_timer.expires_from_now(boost::posix_time::milliseconds(timeout)); - m_timer.async_wait([&con, command, cb](const boost::system::error_code& ec) - { - if(ec == boost::asio::error::operation_aborted) - return; - LOG_PRINT_CC(con.get_context_ref(), "Timeout on invoke operation happened, command: " << command, LOG_LEVEL_2); - std::string fake; - cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); - con.close(); - con.finish_outer_call(); - }); - m_timer_started = true; - } - } - virtual ~anvoke_handler() - {} - callback_t m_cb; - async_protocol_handler& m_con; - boost::asio::deadline_timer m_timer; - bool m_timer_started; - bool m_cancel_timer_called; - bool m_timer_cancelled; - int m_command; - virtual bool handle(int res, const std::string& buff, typename async_protocol_handler::connection_context& context) - { - if(!cancel_timer()) - return false; - m_cb(res, buff, context); - m_con.finish_outer_call(); - return true; - } - virtual bool is_timer_started() const - { - return m_timer_started; - } - virtual void cancel() - { - if(cancel_timer()) - { - std::string fake; - m_cb(LEVIN_ERROR_CONNECTION_DESTROYED, fake, m_con.get_context_ref()); - m_con.finish_outer_call(); - } - } - virtual bool cancel_timer() - { - if(!m_cancel_timer_called) - { - m_cancel_timer_called = true; - boost::system::error_code ignored_ec; - m_timer_cancelled = 1 == m_timer.cancel(ignored_ec); - } - return m_timer_cancelled; - } - }; - critical_section m_invoke_response_handlers_lock; - std::list > m_invoke_response_handlers; - - template - bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command) - { - CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock); - boost::shared_ptr handler(boost::make_shared>(cb, timeout, con, command)); - m_invoke_response_handlers.push_back(handler); - return handler->is_timer_started(); - } - template friend struct anvoke_handler; -public: - async_protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, - config_type& config, - t_connection_context& conn_context): - m_current_head(bucket_head2()), - m_pservice_endpoint(psnd_hndlr), - m_config(config), - m_connection_context(conn_context), - m_state(stream_state_head) - { - m_close_called = 0; - m_deletion_initiated = false; - m_protocol_released = false; - m_wait_count = 0; - m_oponent_protocol_ver = 0; - m_connection_initialized = false; - } - virtual ~async_protocol_handler() - { - m_deletion_initiated = true; - if(m_connection_initialized) - { - m_config.del_connection(this); - } - - for (size_t i = 0; i < 60 * 1000 / 100 && 0 != boost::interprocess::ipcdetail::atomic_read32(&m_wait_count); ++i) - { - misc_utils::sleep_no_w(100); - } - 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); - - LOG_PRINT_CC(m_connection_context, "~async_protocol_handler()", LOG_LEVEL_4); - } - - bool start_outer_call() - { - LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] -->> start_outer_call"); - if(!m_pservice_endpoint->add_ref()) - { - LOG_PRINT_CC_RED(m_connection_context, "[levin_protocol] -->> start_outer_call failed", LOG_LEVEL_4); - return false; - } - boost::interprocess::ipcdetail::atomic_inc32(&m_wait_count); - return true; - } - bool finish_outer_call() - { - LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] <<-- finish_outer_call"); - boost::interprocess::ipcdetail::atomic_dec32(&m_wait_count); - m_pservice_endpoint->release(); - return true; - } - - bool release_protocol() - { - decltype(m_invoke_response_handlers) local_invoke_response_handlers; - CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock); - local_invoke_response_handlers.swap(m_invoke_response_handlers); - m_protocol_released = true; - CRITICAL_REGION_END(); - - // Never call callback inside critical section, that can cause deadlock. Callback can be called when - // invoke_response_handler_base is cancelled - std::for_each(local_invoke_response_handlers.begin(), local_invoke_response_handlers.end(), [](const boost::shared_ptr& pinv_resp_hndlr) { - pinv_resp_hndlr->cancel(); - }); - - return true; - } - - bool close() - { - boost::interprocess::ipcdetail::atomic_inc32(&m_close_called); - - m_pservice_endpoint->close(); - return true; - } - - void update_connection_context(const connection_context& contxt) - { - m_connection_context = contxt; - } - - void request_callback() - { - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( - boost::bind(&async_protocol_handler::finish_outer_call, this)); - - m_pservice_endpoint->request_callback(); - } - - void handle_qued_callback() - { - m_config.m_pcommands_handler->callback(m_connection_context); - } - - virtual bool handle_recv(const void* ptr, size_t cb) - { - if(boost::interprocess::ipcdetail::atomic_read32(&m_close_called)) - return false; //closing connections - - if(!m_config.m_pcommands_handler) - { - LOG_ERROR_CC(m_connection_context, "Commands handler not set!"); - return false; - } - - if(m_cache_in_buffer.size() + cb > m_config.m_max_packet_size) - { - LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size - << ", packet received " << m_cache_in_buffer.size() + cb - << ", connection will be closed."); - return false; - } - - m_cache_in_buffer.append((const char*)ptr, cb); - - bool is_continue = true; - while(is_continue) - { - switch(m_state) - { - case stream_state_body: - if(m_cache_in_buffer.size() < m_current_head.m_cb) - { - is_continue = false; - break; - } - { - std::string buff_to_invoke; - if(m_cache_in_buffer.size() == m_current_head.m_cb) - buff_to_invoke.swap(m_cache_in_buffer); - else - { - buff_to_invoke.assign(m_cache_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); - m_cache_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); - } - - bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); - - LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_RECIEVED. [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 - << ", v=" << m_current_head.m_protocol_version); - - if(is_response) - {//response to some invoke - - epee::critical_region_t invoke_response_handlers_guard(m_invoke_response_handlers_lock); - if(!m_invoke_response_handlers.empty()) - {//async call scenario - boost::shared_ptr response_handler = m_invoke_response_handlers.front(); - bool timer_cancelled = response_handler->cancel_timer(); - // Don't pop handler, to avoid destroying it - if(timer_cancelled) - m_invoke_response_handlers.pop_front(); - invoke_response_handlers_guard.unlock(); - - if(timer_cancelled) - response_handler->handle(m_current_head.m_command, buff_to_invoke, m_connection_context); - } - else - { - invoke_response_handlers_guard.unlock(); - //use sync call scenario - if(!boost::interprocess::ipcdetail::atomic_read32(&m_wait_count) && !boost::interprocess::ipcdetail::atomic_read32(&m_close_called)) - { - LOG_ERROR_CC(m_connection_context, "no active invoke when response came, wtf?"); - return false; - }else - { - CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); - buff_to_invoke.swap(m_local_inv_buff); - buff_to_invoke.clear(); - m_invoke_result_code = m_current_head.m_return_code; - CRITICAL_REGION_END(); - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 1); - } - } - }else - { - if(m_current_head.m_have_to_return_data) - { - std::string return_buff; - m_current_head.m_return_code = m_config.m_pcommands_handler->invoke( - m_current_head.m_command, - buff_to_invoke, - return_buff, - m_connection_context); - m_current_head.m_cb = return_buff.size(); - 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; - std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); - send_buff += return_buff; - CRITICAL_REGION_BEGIN(m_send_lock); - if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size())) - return false; - CRITICAL_REGION_END(); - LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [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 - << ", ver=" << m_current_head.m_protocol_version); - } - else - m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context); - } - } - m_state = stream_state_head; - break; - case stream_state_head: - { - 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) - { - LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); - return false; - } - is_continue = false; - break; - } - - bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data(); - 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_cache_in_buffer.erase(0, sizeof(bucket_head2)); - m_state = stream_state_body; - m_oponent_protocol_ver = m_current_head.m_protocol_version; - if(m_current_head.m_cb > m_config.m_max_packet_size) - { - LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size - << ", packet header received " << m_current_head.m_cb - << ", connection will be closed."); - return false; - } - } - break; - default: - LOG_ERROR_CC(m_connection_context, "Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); - return false; - } - } - - return true; - } - - bool after_init_connection() - { - if (!m_connection_initialized) - { - m_connection_initialized = true; - m_config.add_connection(this); - } - return true; - } - - template - bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) - { - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( - boost::bind(&async_protocol_handler::finish_outer_call, this)); - - if(timeout == LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) - timeout = m_config.m_invoke_timeout; - - int err_code = LEVIN_OK; - do - { - if(m_deletion_initiated) - { - err_code = LEVIN_ERROR_CONNECTION_DESTROYED; - break; - } - - CRITICAL_REGION_LOCAL(m_call_lock); - - if(m_deletion_initiated) - { - err_code = LEVIN_ERROR_CONNECTION_DESTROYED; - break; - } - - bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = 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; - - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); - CRITICAL_REGION_BEGIN(m_send_lock); - CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock); - if(!m_pservice_endpoint->do_send(&head, sizeof(head))) - { -// LOG_ERROR_CC(m_connection_context, "Failed to do_send"); - err_code = LEVIN_ERROR_CONNECTION; - break; - } - - if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) - { - LOG_ERROR_CC(m_connection_context, "Failed to do_send"); - err_code = LEVIN_ERROR_CONNECTION; - break; - } - - if(!add_invoke_response_handler(cb, timeout, *this, command)) - { - err_code = LEVIN_ERROR_CONNECTION_DESTROYED; - break; - } - CRITICAL_REGION_END(); - } while (false); - - if (LEVIN_OK != err_code) - { - std::string stub_buff; - // Never call callback inside critical section, that can cause deadlock - cb(err_code, stub_buff, m_connection_context); - return false; - } - - return true; - } - - int invoke(int command, const std::string& in_buff, std::string& buff_out) - { - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( - boost::bind(&async_protocol_handler::finish_outer_call, this)); - - if(m_deletion_initiated) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - CRITICAL_REGION_LOCAL(m_call_lock); - - if(m_deletion_initiated) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_cb = 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; - - boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); - CRITICAL_REGION_BEGIN(m_send_lock); - if(!m_pservice_endpoint->do_send(&head, sizeof(head))) - { - LOG_ERROR_CC(m_connection_context, "Failed to do_send"); - return LEVIN_ERROR_CONNECTION; - } - - if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) - { - LOG_ERROR_CC(m_connection_context, "Failed to do_send"); - return LEVIN_ERROR_CONNECTION; - } - CRITICAL_REGION_END(); - - LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb - << ", f=" << head.m_flags - << ", r?=" << head.m_have_to_return_data - << ", cmd = " << head.m_command - << ", ver=" << head.m_protocol_version); - - uint64_t ticks_start = misc_utils::get_tick_count(); - - while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_buf_ready) && !m_deletion_initiated && !m_protocol_released) - { - if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout) - { - LOG_PRINT_CC_L2(m_connection_context, "invoke timeout (" << m_config.m_invoke_timeout << "), closing connection "); - close(); - return LEVIN_ERROR_CONNECTION_TIMEDOUT; - } - if(!m_pservice_endpoint->call_run_once_service_io()) - return LEVIN_ERROR_CONNECTION_DESTROYED; - } - - if(m_deletion_initiated || m_protocol_released) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); - buff_out.swap(m_local_inv_buff); - m_local_inv_buff.clear(); - CRITICAL_REGION_END(); - - return m_invoke_result_code; - } - - int notify(int command, const std::string& in_buff) - { - misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( - boost::bind(&async_protocol_handler::finish_outer_call, this)); - - if(m_deletion_initiated) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - CRITICAL_REGION_LOCAL(m_call_lock); - - if(m_deletion_initiated) - return LEVIN_ERROR_CONNECTION_DESTROYED; - - bucket_head2 head = {0}; - head.m_signature = LEVIN_SIGNATURE; - head.m_have_to_return_data = false; - head.m_cb = in_buff.size(); - - head.m_command = command; - head.m_protocol_version = LEVIN_PROTOCOL_VER_1; - head.m_flags = LEVIN_PACKET_REQUEST; - CRITICAL_REGION_BEGIN(m_send_lock); - if(!m_pservice_endpoint->do_send(&head, sizeof(head))) - { -// LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); - return -1; - } - - if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) - { - LOG_ERROR("Failed to do_send()"); - return -1; - } - CRITICAL_REGION_END(); - LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb << - ", f=" << head.m_flags << - ", r?=" << head.m_have_to_return_data << - ", cmd = " << head.m_command << - ", ver=" << head.m_protocol_version); - - return 1; - } - //------------------------------------------------------------------------------------------ - boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;} - //------------------------------------------------------------------------------------------ - t_connection_context& get_context_ref() {return m_connection_context;} -}; -//------------------------------------------------------------------------------------------ -template -void async_protocol_handler_config::del_connection(async_protocol_handler* pconn) -{ - CRITICAL_REGION_BEGIN(m_connects_lock); - m_connects.erase(pconn->get_connection_id()); - CRITICAL_REGION_END(); - m_pcommands_handler->on_connection_close(pconn->m_connection_context); -} -//------------------------------------------------------------------------------------------ -template -void async_protocol_handler_config::add_connection(async_protocol_handler* pconn) -{ - CRITICAL_REGION_BEGIN(m_connects_lock); - m_connects[pconn->get_connection_id()] = pconn; - CRITICAL_REGION_END(); - m_pcommands_handler->on_connection_new(pconn->m_connection_context); -} -//------------------------------------------------------------------------------------------ -template -async_protocol_handler* async_protocol_handler_config::find_connection(boost::uuids::uuid connection_id) const -{ - auto it = m_connects.find(connection_id); - return it == m_connects.end() ? 0 : it->second; -} -//------------------------------------------------------------------------------------------ -template -int async_protocol_handler_config::find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler*& aph) -{ - CRITICAL_REGION_LOCAL(m_connects_lock); - aph = find_connection(connection_id); - if(0 == aph) - return LEVIN_ERROR_CONNECTION_NOT_FOUND; - if(!aph->start_outer_call()) - return LEVIN_ERROR_CONNECTION_DESTROYED; - return LEVIN_OK; -} -//------------------------------------------------------------------------------------------ -template -int async_protocol_handler_config::invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id) -{ - async_protocol_handler* aph; - int r = find_and_lock_connection(connection_id, aph); - return LEVIN_OK == r ? aph->invoke(command, in_buff, buff_out) : r; -} -//------------------------------------------------------------------------------------------ -template template -int async_protocol_handler_config::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout) -{ - async_protocol_handler* aph; - int r = find_and_lock_connection(connection_id, aph); - return LEVIN_OK == r ? aph->async_invoke(command, in_buff, cb, timeout) : r; -} -//------------------------------------------------------------------------------------------ -template template -bool async_protocol_handler_config::foreach_connection(callback_t cb) -{ - CRITICAL_REGION_LOCAL(m_connects_lock); - for(auto& c: m_connects) - { - async_protocol_handler* aph = c.second; - if(!cb(aph->get_context_ref())) - return false; - } - return true; -} -//------------------------------------------------------------------------------------------ -template -size_t async_protocol_handler_config::get_connections_count() -{ - CRITICAL_REGION_LOCAL(m_connects_lock); - return m_connects.size(); -} -//------------------------------------------------------------------------------------------ -template -int async_protocol_handler_config::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id) -{ - async_protocol_handler* aph; - int r = find_and_lock_connection(connection_id, aph); - return LEVIN_OK == r ? aph->notify(command, in_buff) : r; -} -//------------------------------------------------------------------------------------------ -template -bool async_protocol_handler_config::close(boost::uuids::uuid connection_id) -{ - CRITICAL_REGION_LOCAL(m_connects_lock); - async_protocol_handler* aph = find_connection(connection_id); - return 0 != aph ? aph->close() : false; -} -//------------------------------------------------------------------------------------------ -template -bool async_protocol_handler_config::update_connection_context(const t_connection_context& contxt) -{ - CRITICAL_REGION_LOCAL(m_connects_lock); - async_protocol_handler* aph = find_connection(contxt.m_connection_id); - if(0 == aph) - return false; - aph->update_connection_context(contxt); - return true; -} -//------------------------------------------------------------------------------------------ -template -bool async_protocol_handler_config::request_callback(boost::uuids::uuid connection_id) -{ - async_protocol_handler* aph; - int r = find_and_lock_connection(connection_id, aph); - if(LEVIN_OK == r) - { - aph->request_callback(); - return true; - } - else - { - return false; - } -} -} -} diff --git a/contrib/epee/include/net/levin_server_cp.h b/contrib/epee/include/net/levin_server_cp.h deleted file mode 100644 index 8ece350595..0000000000 --- a/contrib/epee/include/net/levin_server_cp.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _HTTP_SERVER_CP_H_ -#define _HTTP_SERVER_CP_H_ - -#include "abstract_tcp_server_cp.h" -#include "levin_protocol_handler.h" -namespace epee -{ -namespace net_utils -{ - typedef cp_server_impl cp_levin_server; -} -} - - - -#endif - - diff --git a/contrib/epee/include/net/levin_server_cp2.h b/contrib/epee/include/net/levin_server_cp2.h deleted file mode 100644 index b29d49bf8f..0000000000 --- a/contrib/epee/include/net/levin_server_cp2.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _HTTP_SERVER_CP_H_ -#define _HTTP_SERVER_CP_H_ - -#include "abstract_tcp_server2.h" -#include "levin_protocol_handler.h" -#include "levin_protocol_handler_async.h" - -namespace epee -{ -namespace net_utils -{ - typedef boosted_tcp_server > boosted_levin_server; - typedef boosted_tcp_server > boosted_levin_async_server; -} -} - - - -#endif - - diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h deleted file mode 100644 index 0d458963c6..0000000000 --- a/contrib/epee/include/net/local_ip.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 net_utils - { - inline - bool is_ip_local(uint32_t ip) - { - /* - local ip area - 10.0.0.0 — 10.255.255.255 - 172.16.0.0 — 172.31.255.255 - 192.168.0.0 — 192.168.255.255 - */ - if( (ip | 0xffffff00) == 0xffffff0a) - return true; - - if( (ip | 0xffff0000) == 0xffffa8c0) - return true; - - if( (ip | 0xffffff00) == 0xffffffac) - { - uint32_t second_num = (ip << 8) & 0xff000000; - if(second_num >= 16 && second_num <= 31 ) - return true; - } - return false; - } - inline - bool is_ip_loopback(uint32_t ip) - { - if( (ip | 0xffffff00) == 0xffffff7f) - return true; - //MAKE_IP - /* - loopback ip - 127.0.0.0 — 127.255.255.255 - */ - return false; - } - - } -} - diff --git a/contrib/epee/include/net/multiprotocols_server.h b/contrib/epee/include/net/multiprotocols_server.h deleted file mode 100644 index 4807a44218..0000000000 --- a/contrib/epee/include/net/multiprotocols_server.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _MULTIPROTOCOLS_SERVER_H_ -#define _MULTIPROTOCOLS_SERVER_H_ - -//#include "abstract_tcp_server_cp.h" -#include "protocol_switcher.h" -#include "abstract_tcp_server2.h" - -namespace epee -{ -namespace net_utils -{ - //typedef cp_server_impl multiprotocol_server; - typedef boosted_tcp_server boosted_multiprotocol_server; -} -} - - -#endif //_MULTIPROTOCOLS_SERVER_H_ - diff --git a/contrib/epee/include/net/munin_connection_handler.h b/contrib/epee/include/net/munin_connection_handler.h deleted file mode 100644 index 8579339c57..0000000000 --- a/contrib/epee/include/net/munin_connection_handler.h +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _MUNIN_CONNECTION_HANDLER_H_ -#define _MUNIN_CONNECTION_HANDLER_H_ - -#include -#include "net_utils_base.h" -#include "to_nonconst_iterator.h" -#include "http_base.h" -#include "reg_exp_definer.h" - -#define MUNIN_ARGS_DEFAULT(vertial_lable_str) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " \n" -#define MUNIN_ARGS_FORCE_AUPPER_LIMIT(vertial_lable_str, limit) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " --rigid --upper-limit " limit " \n" -#define MUNIN_TITLE(title_str) "graph_title " title_str "\n" -#define MUNIN_CATEGORY(category_str) "graph_category " category_str "\n" -#define MUNIN_INFO(info_str) "graph_info " info_str "\n" -#define MUNIN_ENTRY(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" -#define MUNIN_ENTRY_AREA(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" #var_name".draw AREASTACK\n" -#define MUNIN_ENTRY_ALIAS(var_name, alias) #var_name".label " #alias"\n" #var_name".info "#alias".\n" -#define BEGIN_MUNIN_SERVICE(servivece_name_str) if(servivece_name_str == pservice->m_service_name) { -#define END_MUNIN_SERVICE() } -#define MUNIN_SERVICE_PARAM(munin_var_name_str, variable) paramters_text += std::string() + munin_var_name_str ".value " + boost::lexical_cast(variable) + "\n" - - - - -namespace epee -{ -namespace net_utils -{ - namespace munin - { - - - /************************************************************************/ - /* */ - /************************************************************************/ - struct munin_service; - - struct munin_service_data_provider - { - virtual bool update_service_data(munin_service* pservice, std::string& paramters_text)=0; - }; - - struct munin_service - { - std::string m_service_name; - std::string m_service_config_string; - munin_service_data_provider* m_pdata_provider; - }; - - struct node_server_config - { - std::list m_services; - //TODO: - }; - - struct fake_send_handler: public i_service_endpoint - { - virtual bool do_send(const void* ptr, size_t cb) - { - m_cache += std::string((const char*)ptr, cb); - return true; - } - public: - - std::string m_cache; - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - class munin_node_server_connection_handler - { - public: - typedef node_server_config config_type; - typedef connection_context_base connection_context; - - munin_node_server_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, const connection_context_base& context):m_psnd_hndlr(psnd_hndlr), - m_machine_state(http_state_retriving_comand_line), - m_config(config) - { - init(); - } - virtual ~munin_node_server_connection_handler() - { - - } - - bool release_protocol() - { - return true; - } - bool after_init_connection() - { - std::string hello_str = "# munin node at "; - hello_str += m_host_name + "\n"; - send_hook(hello_str); - return true; - } - - virtual bool thread_init() - { - return true; - } - - virtual bool thread_deinit() - { - return true; - } - - void handle_qued_callback() - { - - } - - virtual bool handle_recv(const void* ptr, size_t cb) - { - - const char* pbuff = (const char*)ptr; - std::string recvd_buff(pbuff, cb); - LOG_PRINT("munin_recv: \n" << recvd_buff, LOG_LEVEL_3); - - m_cache += recvd_buff; - - bool stop_handling = false; - while(!stop_handling) - { - switch(m_machine_state) - { - case http_state_retriving_comand_line: - { - - std::string::size_type fpos = m_cache.find('\n'); - if(std::string::npos != fpos ) - { - bool res = handle_command(m_cache); - if(!res) - return false; - m_cache.erase(0, fpos+1); - continue; - } - stop_handling = true; - } - break; - case http_state_error: - stop_handling = true; - return false; - default: - LOG_ERROR("Error in munin state machine! Unkonwon state=" << m_machine_state); - stop_handling = true; - m_machine_state = http_state_error; - return false; - } - - } - - return true; - } - - private: - - - bool init() - { - char hostname[64] = {0}; - int res = gethostname(hostname, 64); - hostname[63] = 0;//be happy - m_host_name = hostname; - return true; - } - bool handle_command(const std::string& command) - { - // list, nodes, config, fetch, version or quit - STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^((list)|(nodes)|(config)|(fetch)|(version)|(quit))(\\s+(\\S+))?", boost::regex::icase | boost::regex::normal); - // 12 3 4 5 6 7 8 9 - size_t match_len = 0; - boost::smatch result; - if(boost::regex_search(command, result, rexp_match_command_line, boost::match_default) && result[0].matched) - { - if(result[2].matched) - {//list command - return handle_list_command(); - }else if(result[3].matched) - {//nodes command - return handle_nodes_command(); - }else if(result[4].matched) - {//config command - if(result[9].matched) - return handle_config_command(result[9]); - else - { - send_hook("Unknown service\n"); - } - }else if(result[5].matched) - {//fetch command - if(result[9].matched) - return handle_fetch_command(result[9]); - else - { - send_hook("Unknown service\n"); - } - }else if(result[6].matched) - {//version command - return handle_version_command(); - }else if(result[7].matched) - {//quit command - return handle_quit_command(); - } - else - return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n"); - } - - return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");; - } - - bool handle_list_command() - { - std::string buff_to_send; - for(std::list::const_iterator it = m_config.m_services.begin(); it!=m_config.m_services.end();it++) - { - buff_to_send += it->m_service_name + " "; - } - buff_to_send+='\n'; - return send_hook(buff_to_send); - } - bool handle_nodes_command() - { - //supports only one node - host name - send_hook(m_host_name + "\n.\n"); - return true; - } - bool handle_config_command(const std::string& service_name) - { - munin_service* psrv = get_service_by_name(service_name); - if(!psrv) - return send_hook(std::string() + "Unknown service\n"); - - - return send_hook(psrv->m_service_config_string + ".\n"); - } - - bool handle_fetch_command(const std::string& service_name) - { - munin_service* psrv = get_service_by_name(service_name); - if(!psrv) - return send_hook(std::string() + "Unknown service\n"); - - std::string buff; - psrv->m_pdata_provider->update_service_data(psrv, buff); - - buff += ".\n"; - return send_hook(buff); - } - bool handle_version_command() - { - return send_hook("Munin node component by Andrey Sabelnikov\n"); - } - bool handle_quit_command() - { - return false; - } - - bool send_hook(const std::string& buff) - { - LOG_PRINT("munin_send: \n" << buff, LOG_LEVEL_3); - - if(m_psnd_hndlr) - return m_psnd_hndlr->do_send(buff.data(), buff.size()); - else - return false; - } - - - munin_service* get_service_by_name(const std::string& srv_name) - { - std::list::iterator it = m_config.m_services.begin(); - for(; it!=m_config.m_services.end(); it++) - if(it->m_service_name == srv_name) - break; - - if(it==m_config.m_services.end()) - return NULL; - - return &(*it); - } - - enum machine_state{ - http_state_retriving_comand_line, - http_state_error - }; - - - config_type& m_config; - machine_state m_machine_state; - std::string m_cache; - std::string m_host_name; - protected: - i_service_endpoint* m_psnd_hndlr; - }; - - - inline bool test_self() - { - /*WSADATA w; - ::WSAStartup(MAKEWORD(1, 1), &w); - node_server_config sc; - sc.m_services.push_back(munin_service()); - sc.m_services.back().m_service_name = "test_service"; - - sc.m_services.back().m_service_config_string = - "graph_args --base 1000 -l 0 --vertical-label N --upper-limit 329342976\n" - "graph_title REPORTS STATICTICS\n" - "graph_category bind\n" - "graph_info This graph shows how many reports came in fixed time period.\n" - "graph_order apps free swap\n" - "apps.label apps\n" - "apps.draw AREA\n" - "apps.info Memory used by user-space applications.\n" - "swap.label swap\n" - "swap.draw STACK\n" - "swap.info Swap space used.\n" - "free.label unused\n" - "free.draw STACK\n" - "free.info Wasted memory. Memory that is not used for anything at all.\n" - "committed.label committed\n" - "committed.draw LINE2\n" - "committed.warn 625410048\n" - "committed.info The amount of memory that would be used if all the memory that's been allocated were to be used.\n"; - - - sc.m_services.push_back(munin_service()); - sc.m_services.back().m_service_name = "test_service1"; - fake_send_handler fh; - munin_node_server_connection_handler mh(&fh, sc); - - std::string buff = "list\n"; - mh.handle_recv(buff.data(), buff.size()); - - - buff = "nodes\n"; - mh.handle_recv(buff.data(), buff.size()); -*/ - return true; - } - - } -} -} -#endif//!_MUNIN_CONNECTION_HANDLER_H_ \ No newline at end of file diff --git a/contrib/epee/include/net/munin_node_server.h b/contrib/epee/include/net/munin_node_server.h deleted file mode 100644 index 07637f550d..0000000000 --- a/contrib/epee/include/net/munin_node_server.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _MUNIN_NODE_SERVER_H_ -#define _MUNIN_NODE_SERVER_H_ - -#include -//#include "net_utils_base.h" -#include "munin_connection_handler.h" -//#include "abstract_tcp_server.h" -//#include "abstract_tcp_server_cp.h" -#include "abstract_tcp_server2.h" -namespace epee -{ -namespace net_utils -{ - namespace munin - { - typedef boosted_tcp_server munin_node_server; - //typedef cp_server_impl munin_node_cp_server; - } -} -} -#endif//!_MUNIN_NODE_SERVER_H_ \ No newline at end of file diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h deleted file mode 100644 index 4f7ebfa044..0000000000 --- a/contrib/epee/include/net/net_helper.h +++ /dev/null @@ -1,683 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -#include -#include -#include -#include -#include -#include -#include -#include "net/net_utils_base.h" -#include "misc_language.h" -//#include "profile_tools.h" -#include "../string_tools.h" - -#ifndef MAKE_IP -#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) -#endif - - -namespace epee -{ -namespace net_utils -{ - - class blocked_mode_client - { - - - struct handler_obj - { - handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred) - {} - handler_obj(const handler_obj& other_obj):ref_error(other_obj.ref_error), ref_bytes_transferred(other_obj.ref_bytes_transferred) - {} - - boost::system::error_code& ref_error; - size_t& ref_bytes_transferred; - - void operator()(const boost::system::error_code& error, // Result of operation. - std::size_t bytes_transferred // Number of bytes read. - ) - { - ref_error = error; - ref_bytes_transferred = bytes_transferred; - } - }; - - public: - inline - blocked_mode_client():m_socket(m_io_service), - m_initialized(false), - m_connected(false), - m_deadline(m_io_service), - m_shutdowned(0) - { - - - m_initialized = true; - - - // No deadline is required until the first socket operation is started. We - // set the deadline to positive infinity so that the actor takes no action - // until a specific deadline is set. - m_deadline.expires_at(boost::posix_time::pos_infin); - - // Start the persistent actor that checks for deadline expiry. - check_deadline(); - - } - inline - ~blocked_mode_client() - { - //profile_tools::local_coast lc("~blocked_mode_client()", 3); - shutdown(); - } - - inline void set_recv_timeout(int reciev_timeout) - { - m_reciev_timeout = reciev_timeout; - } - - inline - bool connect(const std::string& addr, int port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0") - { - return connect(addr, std::to_string(port), connect_timeout, reciev_timeout, bind_ip); - } - - inline - bool connect(const std::string& addr, const std::string& port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0") - { - m_connect_timeout = connect_timeout; - m_reciev_timeout = reciev_timeout; - m_connected = false; - if(!m_reciev_timeout) - m_reciev_timeout = m_connect_timeout; - - try - { - m_socket.close(); - // Get a list of endpoints corresponding to the server name. - - - ////////////////////////////////////////////////////////////////////////// - - boost::asio::ip::tcp::resolver resolver(m_io_service); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port); - boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); - boost::asio::ip::tcp::resolver::iterator end; - if(iterator == end) - { - LOG_ERROR("Failed to resolve " << addr); - return false; - } - - ////////////////////////////////////////////////////////////////////////// - - - //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); - boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); - - - m_socket.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(addr.c_str()), 0); - m_socket.bind(local_endpoint); - } - - - m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_timeout)); - - - boost::system::error_code ec = boost::asio::error::would_block; - - //m_socket.connect(remote_endpoint); - m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); - while (ec == boost::asio::error::would_block) - { - m_io_service.run_one(); - } - - if (!ec && m_socket.is_open()) - { - m_connected = true; - m_deadline.expires_at(boost::posix_time::pos_infin); - return true; - }else - { - LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3); - return false; - } - - } - catch(const boost::system::system_error& er) - { - LOG_PRINT("Some problems at connect, message: " << er.what(), LOG_LEVEL_4); - return false; - } - catch(...) - { - LOG_PRINT("Some fatal problems.", LOG_LEVEL_4); - return false; - } - - return true; - } - - - inline - bool disconnect() - { - try - { - if(m_connected) - { - m_connected = false; - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); - - } - } - - catch(const boost::system::system_error& /*er*/) - { - //LOG_ERROR("Some problems at disconnect, message: " << er.what()); - return false; - } - catch(...) - { - //LOG_ERROR("Some fatal problems."); - return false; - } - return true; - } - - - inline - bool send(const std::string& buff) - { - - try - { - m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); - - // Set up the variable that receives 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. - boost::system::error_code ec = boost::asio::error::would_block; - - // Start the asynchronous operation itself. The boost::lambda function - // object is used as a callback and will update the ec variable when the - // operation completes. The blocking_udp_client.cpp example shows how you - // can use boost::bind rather than boost::lambda. - boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1); - - // Block until the asynchronous operation has completed. - while (ec == boost::asio::error::would_block) - { - m_io_service.run_one(); - } - - if (ec) - { - LOG_PRINT_L3("Problems at write: " << ec.message()); - m_connected = false; - return false; - }else - { - m_deadline.expires_at(boost::posix_time::pos_infin); - } - } - - catch(const boost::system::system_error& er) - { - LOG_ERROR("Some problems at connect, message: " << er.what()); - return false; - } - catch(...) - { - LOG_ERROR("Some fatal problems."); - return false; - } - - return true; - } - - inline - bool send(const void* data, size_t sz) - { - try - { - /* - m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); - - // Set up the variable that receives 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. - boost::system::error_code ec = boost::asio::error::would_block; - - // Start the asynchronous operation itself. The boost::lambda function - // object is used as a callback and will update the ec variable when the - // operation completes. The blocking_udp_client.cpp example shows how you - // can use boost::bind rather than boost::lambda. - boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); - - // Block until the asynchronous operation has completed. - while (ec == boost::asio::error::would_block) - { - m_io_service.run_one(); - } - */ - boost::system::error_code ec; - - size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); - - - - if (!writen || ec) - { - LOG_PRINT_L3("Problems at write: " << ec.message()); - m_connected = false; - return false; - }else - { - m_deadline.expires_at(boost::posix_time::pos_infin); - } - } - - catch(const boost::system::system_error& er) - { - LOG_ERROR("Some problems at send, message: " << er.what()); - m_connected = false; - return false; - } - catch(...) - { - LOG_ERROR("Some fatal problems."); - return false; - } - - return true; - } - - bool is_connected() - { - return m_connected && m_socket.is_open(); - //TRY_ENTRY() - //return m_socket.is_open(); - //CATCH_ENTRY_L0("is_connected", false) - } - - inline - bool recv(std::string& buff) - { - - try - { - // Set a deadline for the asynchronous operation. Since this function uses - // a composed operation (async_read_until), the deadline applies to the - // entire operation, rather than individual reads from the socket. - m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); - - // Set up the variable that receives 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. - //boost::system::error_code ec = boost::asio::error::would_block; - - // Start the asynchronous operation itself. The boost::lambda function - // object is used as a callback and will update the ec variable when the - // operation completes. The blocking_udp_client.cpp example shows how you - // can use boost::bind rather than boost::lambda. - - boost::system::error_code ec = boost::asio::error::would_block; - size_t bytes_transfered = 0; - - handler_obj hndlr(ec, bytes_transfered); - - char local_buff[10000] = {0}; - //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr); - boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr); - - // Block until the asynchronous operation has completed. - while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) - { - m_io_service.run_one(); - } - - - if (ec) - { - LOG_PRINT_L4("READ ENDS: Connection err_code " << ec.value()); - if(ec == boost::asio::error::eof) - { - LOG_PRINT_L4("Connection err_code eof."); - //connection closed there, empty - return true; - } - - LOG_PRINT_L3("Problems at read: " << ec.message()); - m_connected = false; - return false; - }else - { - LOG_PRINT_L4("READ ENDS: Success. bytes_tr: " << bytes_transfered); - m_deadline.expires_at(boost::posix_time::pos_infin); - } - - /*if(!bytes_transfered) - return false;*/ - - buff.assign(local_buff, bytes_transfered); - return true; - } - - catch(const boost::system::system_error& er) - { - LOG_ERROR("Some problems at read, message: " << er.what()); - m_connected = false; - return false; - } - catch(...) - { - LOG_ERROR("Some fatal problems at read."); - return false; - } - - - - return false; - - } - - inline bool recv_n(std::string& buff, int64_t sz) - { - - try - { - // Set a deadline for the asynchronous operation. Since this function uses - // a composed operation (async_read_until), the deadline applies to the - // entire operation, rather than individual reads from the socket. - m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); - - // Set up the variable that receives 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. - //boost::system::error_code ec = boost::asio::error::would_block; - - // Start the asynchronous operation itself. The boost::lambda function - // object is used as a callback and will update the ec variable when the - // operation completes. The blocking_udp_client.cpp example shows how you - // can use boost::bind rather than boost::lambda. - - buff.resize(static_cast(sz)); - boost::system::error_code ec = boost::asio::error::would_block; - size_t bytes_transfered = 0; - - - handler_obj hndlr(ec, bytes_transfered); - - //char local_buff[10000] = {0}; - boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr); - - // Block until the asynchronous operation has completed. - while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) - { - m_io_service.run_one(); - } - - if (ec) - { - LOG_PRINT_L3("Problems at read: " << ec.message()); - m_connected = false; - return false; - }else - { - m_deadline.expires_at(boost::posix_time::pos_infin); - } - - if(bytes_transfered != buff.size()) - { - LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size()); - return false; - } - - return true; - } - - catch(const boost::system::system_error& er) - { - LOG_ERROR("Some problems at read, message: " << er.what()); - m_connected = false; - return false; - } - catch(...) - { - LOG_ERROR("Some fatal problems at read."); - return false; - } - - - - return false; - } - - bool shutdown() - { - m_deadline.cancel(); - boost::system::error_code ignored_ec; - m_socket.cancel(ignored_ec); - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); - m_socket.close(ignored_ec); - boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1); - m_connected = false; - return true; - } - - void set_connected(bool connected) - { - m_connected = connected; - } - boost::asio::io_service& get_io_service() - { - return m_io_service; - } - - boost::asio::ip::tcp::socket& get_socket() - { - return m_socket; - } - - private: - - void check_deadline() - { - // 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 socket is closed so that any outstanding - // asynchronous operations are cancelled. This allows the blocked - // connect(), read_line() or write_line() functions to return. - LOG_PRINT_L3("Timed out socket"); - m_connected = false; - m_socket.close(); - - // 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(&blocked_mode_client::check_deadline, this)); - } - - - - protected: - boost::asio::io_service m_io_service; - boost::asio::ip::tcp::socket m_socket; - int m_connect_timeout; - int m_reciev_timeout; - bool m_initialized; - bool m_connected; - boost::asio::deadline_timer m_deadline; - volatile uint32_t m_shutdowned; - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - class async_blocked_mode_client: public blocked_mode_client - { - public: - async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service) - { - - // No deadline is required until the first socket operation is started. We - // set the deadline to positive infinity so that the actor takes no action - // until a specific deadline is set. - m_send_deadline.expires_at(boost::posix_time::pos_infin); - - // Start the persistent actor that checks for deadline expiry. - check_send_deadline(); - } - ~async_blocked_mode_client() - { - m_send_deadline.cancel(); - } - - bool shutdown() - { - blocked_mode_client::shutdown(); - m_send_deadline.cancel(); - return true; - } - - inline - bool send(const void* data, size_t sz) - { - try - { - /* - m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); - - // Set up the variable that receives 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. - boost::system::error_code ec = boost::asio::error::would_block; - - // Start the asynchronous operation itself. The boost::lambda function - // object is used as a callback and will update the ec variable when the - // operation completes. The blocking_udp_client.cpp example shows how you - // can use boost::bind rather than boost::lambda. - boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); - - // Block until the asynchronous operation has completed. - while(ec == boost::asio::error::would_block) - { - m_io_service.run_one(); - }*/ - - boost::system::error_code ec; - - size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); - - if (!writen || ec) - { - LOG_PRINT_L3("Problems at write: " << ec.message()); - return false; - }else - { - m_send_deadline.expires_at(boost::posix_time::pos_infin); - } - } - - catch(const boost::system::system_error& er) - { - LOG_ERROR("Some problems at connect, message: " << er.what()); - return false; - } - catch(...) - { - LOG_ERROR("Some fatal problems."); - return false; - } - - return true; - } - - - private: - - boost::asio::deadline_timer m_send_deadline; - - void check_send_deadline() - { - // 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_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) - { - // The deadline has passed. The socket is closed so that any outstanding - // asynchronous operations are cancelled. This allows the blocked - // connect(), read_line() or write_line() functions to return. - LOG_PRINT_L3("Timed out socket"); - m_socket.close(); - - // 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_send_deadline.expires_at(boost::posix_time::pos_infin); - } - - // Put the actor back to sleep. - m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this)); - } - }; -} -} diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h deleted file mode 100644 index 40c3d935d2..0000000000 --- a/contrib/epee/include/net/net_parse_helpers.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "http_base.h" -#include "include_base_utils.h" -#include "reg_exp_definer.h" - - -namespace epee -{ -namespace net_utils -{ - - inline bool parse_uri_query(const std::string& query, std::list >& params) - { - enum state - { - st_param_name, - st_param_val - }; - state st = st_param_name; - std::string::const_iterator start_it = query.begin(); - std::pair e; - for(std::string::const_iterator it = query.begin(); it != query.end(); it++) - { - switch(st) - { - case st_param_name: - if(*it == '=') - { - e.first.assign(start_it, it); - start_it = it;++start_it; - st = st_param_val; - } - break; - case st_param_val: - if(*it == '&') - { - e.second.assign(start_it, it); - start_it = it;++start_it; - params.push_back(e); - e.first.clear();e.second.clear(); - st = st_param_name; - } - break; - default: - LOG_ERROR("Unknown state " << (int)st); - return false; - } - } - if(st == st_param_name) - { - if(start_it != query.end()) - { - e.first.assign(start_it, query.end()); - params.push_back(e); - } - }else - { - if(start_it != query.end()) - e.second.assign(start_it, query.end()); - - if(e.first.size()) - params.push_back(e); - } - return true; - } - - inline - bool parse_uri(const std::string uri, http::uri_content& content) - { - - ///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= - content.m_query_params.clear(); - STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); - - boost::smatch result; - if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched) - { - LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << uri); - content.m_path = uri; - return true; - } - if(result[1].matched) - { - content.m_path = result[1]; - } - if(result[3].matched) - { - content.m_query = result[3]; - } - if(result[5].matched) - { - content.m_fragment = result[5]; - } - if(content.m_query.size()) - { - parse_uri_query(content.m_query, content.m_query_params); - } - return true; - } - - - inline - bool parse_url(const std::string url_str, http::url_content& content) - { - - ///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= - //STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); - STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal); - // 12 34 5 6 7 - content.port = 0; - boost::smatch result; - if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched) - { - LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << rexp_match_uri); - //content.m_path = uri; - return true; - } - if(result[2].matched) - { - content.schema = result[2]; - } - if(result[4].matched) - { - content.host = result[4]; - } - if(result[6].matched) - { - content.port = boost::lexical_cast(result[6]); - } - if(result[7].matched) - { - content.uri = result[7]; - return parse_uri(result[7], content.m_uri_content); - } - - return true; - } - -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h deleted file mode 100644 index b0e44f5dd0..0000000000 --- a/contrib/epee/include/net/net_utils_base.h +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _NET_UTILS_BASE_H_ -#define _NET_UTILS_BASE_H_ - -#include -#include "string_tools.h" - -#ifndef MAKE_IP -#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) -#endif - -namespace boost { - namespace asio { - class io_service; - } -} - -namespace epee -{ -namespace net_utils -{ - /************************************************************************/ - /* */ - /************************************************************************/ - struct connection_context_base - { - const boost::uuids::uuid m_connection_id; - const uint32_t m_remote_ip; - const uint32_t m_remote_port; - const bool m_is_income; - const time_t m_started; - time_t m_last_recv; - time_t m_last_send; - uint64_t m_recv_cnt; - uint64_t m_send_cnt; - - connection_context_base(boost::uuids::uuid connection_id, - long remote_ip, int remote_port, bool is_income, - time_t last_recv = 0, time_t last_send = 0, - uint64_t recv_cnt = 0, uint64_t send_cnt = 0): - m_connection_id(connection_id), - m_remote_ip(remote_ip), - m_remote_port(remote_port), - m_is_income(is_income), - m_started(time(NULL)), - m_last_recv(last_recv), - m_last_send(last_send), - m_recv_cnt(recv_cnt), - m_send_cnt(send_cnt) - {} - - connection_context_base(): m_connection_id(), - m_remote_ip(0), - m_remote_port(0), - m_is_income(false), - m_started(time(NULL)), - m_last_recv(0), - m_last_send(0), - m_recv_cnt(0), - m_send_cnt(0) - {} - - connection_context_base& operator=(const connection_context_base& a) - { - set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income); - return *this; - } - - private: - template - friend class connection; - void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income) - { - this->~connection_context_base(); - new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income); - } - - }; - - /************************************************************************/ - /* */ - /************************************************************************/ - struct i_service_endpoint - { - virtual bool do_send(const void* ptr, size_t cb)=0; - virtual bool close()=0; - virtual bool call_run_once_service_io()=0; - virtual bool request_callback()=0; - virtual boost::asio::io_service& get_io_service()=0; - //protect from deletion connection object(with protocol instance) during external call "invoke" - virtual bool add_ref()=0; - virtual bool release()=0; - protected: - virtual ~i_service_endpoint(){} - }; - - - //some helpers - - - inline - std::string print_connection_context(const connection_context_base& ctx) - { - std::stringstream ss; - ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); - return ss.str(); - } - - inline - std::string print_connection_context_short(const connection_context_base& ctx) - { - std::stringstream ss; - ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT"); - return ss.str(); - } - -#define LOG_PRINT_CC(ct, message, log_level) LOG_PRINT("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_PRINT_CC_YELLOW(ct, message, log_level) LOG_PRINT_YELLOW("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_PRINT_CC_CYAN(ct, message, log_level) LOG_PRINT_CYAN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_PRINT_CC_MAGENTA(ct, message, log_level) LOG_PRINT_MAGENTA("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) -#define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) - -#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) -#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) -#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) -#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) -#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) - -#define LOG_PRINT_CCONTEXT_L0(message) LOG_PRINT_CC_L0(context, message) -#define LOG_PRINT_CCONTEXT_L1(message) LOG_PRINT_CC_L1(context, message) -#define LOG_PRINT_CCONTEXT_L2(message) LOG_PRINT_CC_L2(context, message) -#define LOG_PRINT_CCONTEXT_L3(message) LOG_PRINT_CC_L3(context, message) -#define LOG_ERROR_CCONTEXT(message) LOG_ERROR_CC(context, message) - -#define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level) -#define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level) -#define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level) -#define LOG_PRINT_CCONTEXT_YELLOW(message, log_level) LOG_PRINT_CC_YELLOW(context, message, log_level) -#define LOG_PRINT_CCONTEXT_CYAN(message, log_level) LOG_PRINT_CC_CYAN(context, message, log_level) -#define LOG_PRINT_CCONTEXT_MAGENTA(message, log_level) LOG_PRINT_CC_MAGENTA(context, message, log_level) - -#define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message) - -} -} - -#endif //_NET_UTILS_BASE_H_ diff --git a/contrib/epee/include/net/protocol_switcher.h b/contrib/epee/include/net/protocol_switcher.h deleted file mode 100644 index ca0ce6f97e..0000000000 --- a/contrib/epee/include/net/protocol_switcher.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _PROTOCOL_SWITCHER_H_ -#define _PROTOCOL_SWITCHER_H_ - -#include "levin_base.h" -#include "http_server.h" -#include "levin_protocol_handler.h" -//#include "abstract_tcp_server.h" - -namespace epee -{ -namespace net_utils -{ - struct protocl_switcher_config - { - http::http_custom_handler::config_type m_http_config; - levin::protocol_handler::config_type m_levin_config; - }; - - - struct i_protocol_handler - { - virtual bool handle_recv(const void* ptr, size_t cb)=0; - }; - - template - class t_protocol_handler: public i_protocol_handler - { - public: - typedef t t_type; - t_protocol_handler(i_service_endpoint* psnd_hndlr, typename t_type::config_type& config, const connection_context& conn_context):m_hadler(psnd_hndlr, config, conn_context) - {} - private: - bool handle_recv(const void* ptr, size_t cb) - { - return m_hadler.handle_recv(ptr, cb); - } - t_type m_hadler; - }; - - - class protocol_switcher - { - public: - typedef protocl_switcher_config config_type; - - protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context); - virtual ~protocol_switcher(){} - - virtual bool handle_recv(const void* ptr, size_t cb); - - bool after_init_connection(){return true;} - private: - t_protocol_handler m_http_handler; - t_protocol_handler m_levin_handler; - i_protocol_handler* pcurrent_handler; - - std::string m_cached_buff; - }; - - protocol_switcher::protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):m_http_handler(psnd_hndlr, config.m_http_config, conn_context), m_levin_handler(psnd_hndlr, config.m_levin_config, conn_context), pcurrent_handler(NULL) - {} - - bool protocol_switcher::handle_recv(const void* ptr, size_t cb) - { - if(pcurrent_handler) - return pcurrent_handler->handle_recv(ptr, cb); - else - { - m_cached_buff.append((const char*)ptr, cb); - if(m_cached_buff.size() < sizeof(uint64_t)) - return true; - - if(*((uint64_t*)&m_cached_buff[0]) == LEVIN_SIGNATURE) - { - pcurrent_handler = &m_levin_handler; - return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size()); - } - if(m_cached_buff.substr(0, 4) == "GET " || m_cached_buff.substr(0, 4) == "POST") - { - pcurrent_handler = &m_http_handler; - return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size()); - }else - { - LOG_ERROR("Wrong protocol accepted on port..."); - return false; - } - } - - return true; - } -} -} -#endif //_PROTOCOL_SWITCHER_H_ \ No newline at end of file diff --git a/contrib/epee/include/net/rpc_method_name.h b/contrib/epee/include/net/rpc_method_name.h deleted file mode 100644 index c226639c4b..0000000000 --- a/contrib/epee/include/net/rpc_method_name.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - - -#define RPC_METHOD_NAME(name) static inline const char* methodname(){return name;} \ No newline at end of file diff --git a/contrib/epee/include/net/smtp.h b/contrib/epee/include/net/smtp.h deleted file mode 100644 index d2e8598fd7..0000000000 --- a/contrib/epee/include/net/smtp.h +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -#include -#include -#include -#include -#include - - -namespace epee -{ -namespace net_utils -{ - namespace smtp - { - - using boost::asio::ip::tcp; - using namespace boost::archive::iterators; - typedef base64_from_binary > base64_text; - - /************************************************************************/ - /* */ - /************************************************************************/ - class smtp_client - { - public: - smtp_client(std::string pServer,unsigned int pPort,std::string pUser,std::string pPassword): - mServer(pServer),mPort(pPort),mUserName(pUser),mPassword(pPassword),mSocket(mIOService),mResolver(mIOService) - { - tcp::resolver::query qry(mServer,boost::lexical_cast( mPort )); - mResolver.async_resolve(qry,boost::bind(&smtp_client::handleResolve,this,boost::asio::placeholders::error, - boost::asio::placeholders::iterator)); - } - bool Send(std::string pFrom,std::string pTo,std::string pSubject,std::string pMessage) - { - mHasError = true; - mFrom=pFrom; - mTo=pTo; - mSubject=pSubject; - mMessage=pMessage; - mIOService.run(); - return !mHasError; - } - private: - std::string encodeBase64(std::string pData) - { - std::stringstream os; - size_t sz=pData.size(); - std::copy(base64_text(pData.c_str()),base64_text(pData.c_str()+sz),std::ostream_iterator(os)); - return os.str(); - } - void handleResolve(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator) - { - if(!err) - { - tcp::endpoint endpoint=*endpoint_iterator; - mSocket.async_connect(endpoint, - boost::bind(&smtp_client::handleConnect,this,boost::asio::placeholders::error,++endpoint_iterator)); - } - else - { - mHasError=true; - mErrorMsg= err.message(); - } - } - void writeLine(std::string pData) - { - std::ostream req_strm(&mRequest); - req_strm << pData << "\r\n"; - boost::asio::write(mSocket,mRequest); - req_strm.clear(); - } - void readLine(std::string& pData) - { - boost::asio::streambuf response; - boost::asio::read_until(mSocket, response, "\r\n"); - std::istream response_stream(&response); - response_stream >> pData; - } - void handleConnect(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator) - { - if (!err) - { - std::string read_buff; - // The connection was successful. Send the request. - std::ostream req_strm(&mRequest); - writeLine("EHLO "+mServer); - readLine(read_buff);//220 - writeLine("AUTH LOGIN"); - readLine(read_buff);// - writeLine(encodeBase64(mUserName)); - readLine(read_buff); - writeLine(encodeBase64(mPassword)); - readLine(read_buff); - writeLine( "MAIL FROM:<"+mFrom+">"); - writeLine( "RCPT TO:<"+mTo+">"); - writeLine( "DATA"); - writeLine( "SUBJECT:"+mSubject); - writeLine( "From:"+mFrom); - writeLine( "To:"+mTo); - writeLine( ""); - writeLine( mMessage ); - writeLine( "\r\n.\r\n"); - readLine(read_buff); - if(read_buff == "250") - mHasError = false; - writeLine( "QUIT"); - } - else - { - mHasError=true; - mErrorMsg= err.message(); - } - } - std::string mServer; - std::string mUserName; - std::string mPassword; - std::string mFrom; - std::string mTo; - std::string mSubject; - std::string mMessage; - unsigned int mPort; - boost::asio::io_service mIOService; - tcp::resolver mResolver; - tcp::socket mSocket; - boost::asio::streambuf mRequest; - boost::asio::streambuf mResponse; - bool mHasError; - std::string mErrorMsg; - }; - - - bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_email, /*"STIL CRAWLER",*/ - const std::string& maillist, const std::string& subject, const std::string& body) - { - STD_TRY_BEGIN(); - //smtp_client mailc("yoursmtpserver.com",25,"user@yourdomain.com","password"); - //mailc.Send("from@yourdomain.com","to@somewhere.com","subject","Hello from C++ SMTP Client!"); - smtp_client mailc(server,port,login,pass); - return mailc.Send(from_email,maillist,subject,body); - STD_TRY_CATCH("at send_mail", false); - } - - } -} -} - -//#include "smtp.inl" \ No newline at end of file diff --git a/contrib/epee/include/net/smtp.inl b/contrib/epee/include/net/smtp.inl deleted file mode 100644 index d42c8b950c..0000000000 --- a/contrib/epee/include/net/smtp.inl +++ /dev/null @@ -1,1569 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "md5.h" - -namespace epee -{ -namespace net_utils -{ - namespace smtp - { - - - ////////////////////////////////////////////////////////////////////////// - inline char * convert_hex( unsigned char *in, int len ) - { - static char hex[] = "0123456789abcdef"; - char * out; - int i; - - out = (char *) malloc(len * 2 + 1); - if (out == NULL) - return NULL; - - for (i = 0; i < len; i++) { - out[i * 2] = hex[in[i] >> 4]; - out[i * 2 + 1] = hex[in[i] & 15]; - } - - out[i*2] = 0; - - return out; - } - - ////////////////////////////////////////////////////////////////////////// - inline char * hash_md5(const char * sec_key, const char * data, int len) - { - char key[65], digest[24]; - char * hash_hex; - - int sec_len, i; - - sec_len = strlen(sec_key); - - if (sec_len < 64) { - memcpy(key, sec_key, sec_len); - for (i = sec_len; i < 64; i++) { - key[i] = 0; - } - } else { - memcpy(key, sec_key, 64); - } - - md5::hmac_md5( (const unsigned char*)data, len, (const unsigned char*)key, 64, (unsigned char*)digest ); - hash_hex = convert_hex( (unsigned char*)digest, 16 ); - - return hash_hex; - } - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - inline CSMTPClient::CSMTPClient(void) - { - m_dwSupportedAuthModesCount = 0; - m_bConnected = FALSE; - m_hSocket = INVALID_SOCKET; - m_pErrorText = NULL; - - // Initialize WinSock - WORD wVer = MAKEWORD( 2, 2 ); - if ( WSAStartup( wVer, &m_wsaData ) != NO_ERROR ) - { - SetErrorText( "WSAStartup.", WSAGetLastError() ); - throw; - } - if ( LOBYTE( m_wsaData.wVersion ) != 2 || HIBYTE( m_wsaData.wVersion ) != 2 ) - { - SetErrorText( "Can't find a useable WinSock DLL." ); - WSACleanup(); - throw; - } - } - - ////////////////////////////////////////////////////////////////////////// - inline CSMTPClient::~CSMTPClient(void) - { - if ( m_pErrorText ) - { - free( m_pErrorText ); - m_pErrorText = NULL; - } - - if ( m_bConnected ) - ServerDisconnect(); - - // Cleanup - WSACleanup(); - } - - ////////////////////////////////////////////////////////////////////////// - inline void CSMTPClient::SetErrorText( LPCSTR szErrorText, DWORD dwErrorCode ) - { - if ( m_pErrorText ) - { - free( m_pErrorText ); - m_pErrorText = NULL; - } - - LPVOID lpMsgBuf = NULL; - if ( dwErrorCode ) - { - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - dwErrorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR) &lpMsgBuf, - 0, NULL ); - } - - if ( szErrorText && strlen( szErrorText ) ) - { - m_pErrorText = (LPBYTE)malloc( strlen( szErrorText ) + 1 ); - strcpy( (char*)m_pErrorText, szErrorText ); - - if ( lpMsgBuf ) - { - strcat( (char*)m_pErrorText, " " ); - strcpy( (char*)m_pErrorText, (char*)lpMsgBuf ); - - LocalFree( lpMsgBuf ); - } - } - } - - inline void CSMTPClient::SetErrorText( PBYTE szErrorText, DWORD dwErrorCode ) - { - SetErrorText( (LPCSTR)szErrorText, dwErrorCode ); - } - - ////////////////////////////////////////////////////////////////////////// - inline char* CSMTPClient::GetLastErrorText() - { - return (char*)m_pErrorText; - } - - ////////////////////////////////////////////////////////////////////////// - inline DWORD CSMTPClient::ReceiveData( SOCKET hSocket, PBYTE pReceiveBuffer, DWORD dwReceiveBufferSize ) - { - DWORD dwReceivedDataSize = 0; - - if ( hSocket != INVALID_SOCKET && pReceiveBuffer && dwReceiveBufferSize ) - { - int iReceived = 0; - int iLength = 0; - - iLength = recv( hSocket, (LPSTR)pReceiveBuffer + iReceived, dwReceiveBufferSize - iReceived, - NO_FLAGS ); - - if ( iLength != 0 && iLength != SOCKET_ERROR ) - iReceived += iLength; - - dwReceivedDataSize = iReceived; - - pReceiveBuffer[ iReceived ] = 0; - } - - return dwReceivedDataSize; - } - - inline ////////////////////////////////////////////////////////////////////////// - DWORD CSMTPClient::SendData( SOCKET hSocket, PBYTE pSendBuffer, DWORD dwSendBufferSize ) - { - DWORD dwSended = 0; - - if ( hSocket != INVALID_SOCKET && pSendBuffer && dwSendBufferSize ) - { - int iSended = 0; - int iLength = 0; - - while ( iLength != SOCKET_ERROR && dwSendBufferSize - iSended > 0 ) - { - iLength = send( hSocket, (LPSTR)pSendBuffer + iSended, dwSendBufferSize - iSended, - NO_FLAGS ); - - if ( iLength != 0 && iLength != SOCKET_ERROR ) - iSended += iLength; - } - - dwSended = iSended; - } - - //if ( dwSended ) - // printf( "C: %s", pSendBuffer ); - - return dwSended; - } - - ////////////////////////////////////////////////////////////////////////// - inline unsigned short CSMTPClient::GetResponseCode( LPBYTE pBuffer, DWORD dwBufferSize ) - { - unsigned short iCode = 0; - - if ( dwBufferSize >= 3 ) - { - CHAR szResponseCode[ 4 ] = { 0 }; - memcpy( szResponseCode, pBuffer, 3 ); - szResponseCode[ 3 ] = 0; - iCode = atoi( szResponseCode ); - } - - return iCode; - } - - ////////////////////////////////////////////////////////////////////////// - inline void CSMTPClient::ParseESMTPExtensions( LPBYTE pBuffer, DWORD dwBufferSize ) - { - const char *szSubstring = strstr( (const char*)pBuffer, "250-AUTH " ); - if ( !szSubstring ) - { - szSubstring = strstr( (const char*)pBuffer, "250 AUTH " ); - } - - if ( szSubstring ) - { - const char *szSubstringEnd = strstr( (const char*)szSubstring, "\r\n" ); - if ( szSubstringEnd ) - { - szSubstring += 9; - char szAuthMode[ 256 ] = { 0 }; - for ( ; szSubstring < szSubstringEnd + 1 ; szSubstring++ ) - { - if ( *szSubstring == ' ' || *szSubstring == '\r' ) - { - if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_PLAIN ) == 0 ) - { - m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_PLAIN; - m_dwSupportedAuthModesCount++; - } - else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_LOGIN ) == 0 ) - { - m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_LOGIN; - m_dwSupportedAuthModesCount++; - } - else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_CRAM_MD5 ) == 0 ) - { - m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_CRAM_MD5; - m_dwSupportedAuthModesCount++; - } - - szAuthMode[ 0 ] = 0; - - if ( m_dwSupportedAuthModesCount == MAX_AUTH_MODES_COUND ) - break; - } - else - { - szAuthMode[ strlen( szAuthMode ) + 1 ] = 0; - szAuthMode[ strlen( szAuthMode ) ] = *szSubstring; - } - } - } - } - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber ) - { - if ( m_bConnected ) - ServerDisconnect(); - - m_bConnected = FALSE; - m_hSocket = INVALID_SOCKET; - - m_hSocket = _connectServerSocket( szServerAddress, iPortNumber ); - - if ( m_hSocket != INVALID_SOCKET ) - { - DWORD dwReceiveBufferSize = 1024*16; - PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); - if ( pReceiveBuffer ) - { - // Connected. Wait server hello string. - DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - // Check 220 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 220 ) - { - SetErrorText( pReceiveBuffer ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - } - else - { - SetErrorText( "ReceiveData error. ", WSAGetLastError() ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - - // EHLO / HELO - BYTE szHelloBuffer[ 256 ]; - sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_EHLO, (char*)szServerAddress ); - if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - // Check 250 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode == 500 ) - { - SetErrorText( pReceiveBuffer ); - - sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_HELO, (char*)szServerAddress ); - if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - - iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 250 ) - { - SetErrorText( pReceiveBuffer ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - } - else if ( iResponseCode != 250 ) - { - SetErrorText( pReceiveBuffer ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - - // Parse AUTH supported modes - ParseESMTPExtensions( pReceiveBuffer, iReceived ); - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - ServerDisconnect(); - return FALSE; - } - - free( pReceiveBuffer ); - } - } - else - { - return FALSE; - } - - m_bConnected = TRUE; - - return TRUE; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber, LPCSTR szUsername, LPCSTR szPassword ) - { - BOOL bSuccess = FALSE; - - bSuccess = ServerConnect( szServerAddress, iPortNumber ); - if ( bSuccess ) - { - if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) ) - { - ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 ); - } - else - if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) ) - { - ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN ); - } - else - if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) ) - { - ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN ); - } - } - - return bSuccess; - } - - ////////////////////////////////////////////////////////////////////////// - inline SOCKET CSMTPClient::_connectServerSocket( LPCSTR szServerAddress, const unsigned short iPortNumber ) - { - int nConnect; - short nProtocolPort = iPortNumber; - LPHOSTENT lpHostEnt; - SOCKADDR_IN sockAddr; - - SOCKET hServerSocket = INVALID_SOCKET; - - lpHostEnt = gethostbyname( szServerAddress ); - if (lpHostEnt) - { - hServerSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - if (hServerSocket != INVALID_SOCKET) - { - sockAddr.sin_family = AF_INET; - sockAddr.sin_port = htons( nProtocolPort ); - sockAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list); - - nConnect = connect( hServerSocket, (PSOCKADDR)&sockAddr, - sizeof(sockAddr) ); - - if ( nConnect != 0 ) - { - SetErrorText( "connect error.", WSAGetLastError() ); - hServerSocket = INVALID_SOCKET; - } - } - else - { - SetErrorText( "Invalid socket." ); - throw; - } - } - else - { - SetErrorText( "Error retrieving host by name.", WSAGetLastError() ); - } - - return hServerSocket ; - } - - ////////////////////////////////////////////////////////////////////////// - inline void CSMTPClient::ServerDisconnect() - { - if ( m_hSocket != INVALID_SOCKET ) - { - if ( SendData( m_hSocket, (PBYTE)SMTP_COMMAND_QUIT, strlen( SMTP_COMMAND_QUIT ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - return; - } - - DWORD dwReceiveBufferSize = 1024*16; - PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); - if ( pReceiveBuffer ) - { - DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - - if ( iReceived ) - SetErrorText( pReceiveBuffer ); - - free( pReceiveBuffer ); - } - - m_hSocket = INVALID_SOCKET; - } - - m_bConnected = FALSE; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::GetAuthModeIsSupported( int iMode ) - { - BOOL bSupported = FALSE; - - for ( int i = 0 ; i < m_dwSupportedAuthModesCount ; i++ ) - { - if ( m_aSupportedAuthModes[ i ] == iMode ) - { - bSupported = TRUE; - break; - } - } - - return bSupported; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword, int iAuthMode ) - { - BOOL bSuccess = FALSE; - - if ( iAuthMode == AUTH_MODE_PLAIN ) - { - bSuccess = ServerLoginMethodPlain( szUsername, szPassword ); - } - else if ( iAuthMode == AUTH_MODE_LOGIN ) - { - bSuccess = ServerLoginMethodLogin( szUsername, szPassword ); - } - else if ( iAuthMode == AUTH_MODE_CRAM_MD5 ) - { - bSuccess = ServerLoginMethodCramMD5( szUsername, szPassword ); - } - - return bSuccess; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword ) - { - BOOL bSuccess = FALSE; - - if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) ) - { - bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 ); - } - else - if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) ) - { - bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN ); - } - else - if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) ) - { - bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN ); - } - - return bSuccess; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerLoginMethodPlain( LPCSTR szUsername, LPCSTR szPassword ) - { - BOOL bSuccess = FALSE; - - BYTE szCommandBuffer[ 256 ]; - sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_PLAIN ); - if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - return FALSE; - } - - DWORD dwReceiveBufferSize = 1024*16; - PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); - if ( pReceiveBuffer ) - { - // Connected. Wait server hello string. - DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 334 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 334 ) - { - free( pReceiveBuffer ); - return FALSE; - } - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - - // Encode. - DWORD dwLoginBuffer = strlen( szUsername ) + strlen( szPassword ) + 3; - char *pLoginBuffer = (char*)malloc( dwLoginBuffer ); - if ( pLoginBuffer ) - { - ZeroMemory( pLoginBuffer, dwLoginBuffer ); - strcpy( pLoginBuffer + 1, szUsername ); - strcpy( pLoginBuffer + 1 + strlen( szUsername ) + 1, szPassword ); - - Base64Coder coder; - coder.Encode( (const PBYTE)pLoginBuffer, dwLoginBuffer - 1 ); - LPCSTR szLoginBufferEncoded = coder.EncodedMessage(); - - if ( szLoginBufferEncoded && strlen( szLoginBufferEncoded ) > 0 ) - { - DWORD dwSendBufferSize = strlen( szLoginBufferEncoded ) + 4; - char* pSendBuffer = (char*)malloc( dwSendBufferSize ); - if ( pSendBuffer ) - { - strcpy( pSendBuffer, szLoginBufferEncoded ); - strcat( pSendBuffer, "\r\n" ); - - if ( SendData( m_hSocket, (PBYTE)pSendBuffer, strlen( (const char*)pSendBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pSendBuffer ); - free( pLoginBuffer ); - free( pReceiveBuffer ); - return FALSE; - } - - free( pSendBuffer ); - } - } - - free( pLoginBuffer ); - - // check result - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 235 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 235 ) - { - free( pReceiveBuffer ); - return FALSE; - } - - bSuccess = TRUE; - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - } - - free( pReceiveBuffer ); - } - - return bSuccess; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerLoginMethodLogin( LPCSTR szUsername, LPCSTR szPassword ) - { - BOOL bSuccess = FALSE; - - BYTE szCommandBuffer[ 256 ]; - sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_LOGIN ); - if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - return FALSE; - } - - DWORD dwReceiveBufferSize = 1024*16; - PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); - if ( pReceiveBuffer ) - { - DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 334 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 334 ) - { - free( pReceiveBuffer ); - return FALSE; - } - - // Check request - if ( iReceived > 6 ) - { - Base64Coder coder; - coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); - LPCSTR szRequest = coder.DecodedMessage(); - if ( szRequest && strlen( szRequest ) > 0 ) - { - if ( strcmpi( szRequest, "Username:" ) == 0 ) - { - coder.Encode( (const PBYTE)szUsername, strlen( szUsername ) ); - LPCSTR szUsernameEncoded = coder.EncodedMessage(); - - char* szLoginUsernameBuffer = (char*)malloc( strlen( szUsernameEncoded ) + 4 ); - if ( szLoginUsernameBuffer ) - { - strcpy( szLoginUsernameBuffer, szUsernameEncoded ); - strcat( szLoginUsernameBuffer, "\r\n" ); - - if ( SendData( m_hSocket, (PBYTE)szLoginUsernameBuffer, strlen( (const char*)szLoginUsernameBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - - free( szLoginUsernameBuffer ); - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 334 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 334 ) - { - free( pReceiveBuffer ); - return FALSE; - } - - // Check request - if ( iReceived > 6 ) - { - coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); - LPCSTR szRequest2 = coder.DecodedMessage(); - if ( szRequest2 && strlen( szRequest2 ) > 0 ) - { - if ( strcmpi( szRequest2, "Password:" ) == 0 ) - { - coder.Encode( (const PBYTE)szPassword, strlen( szPassword ) ); - LPCSTR szPasswordEncoded = coder.EncodedMessage(); - - char* szLoginPasswordBuffer = (char*)malloc( strlen( szPasswordEncoded ) + 4 ); - if ( szLoginPasswordBuffer ) - { - strcpy( szLoginPasswordBuffer, szPasswordEncoded ); - strcat( szLoginPasswordBuffer, "\r\n" ); - - if ( SendData( m_hSocket, (PBYTE)szLoginPasswordBuffer, strlen( (const char*)szLoginPasswordBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - - free( szLoginPasswordBuffer ); - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 235 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 235 ) - { - free( pReceiveBuffer ); - return FALSE; - } - - bSuccess = TRUE; - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - } - } - } - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - } - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - - free( pReceiveBuffer ); - } - - return bSuccess; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::ServerLoginMethodCramMD5( LPCSTR szUsername, LPCSTR szPassword ) - { - BOOL bSuccess = FALSE; - - BYTE szCommandBuffer[ 256 ]; - sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_CRAM_MD5 ); - if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - return FALSE; - } - - DWORD dwReceiveBufferSize = 1024*16; - PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); - if ( pReceiveBuffer ) - { - // Connected. Wait server hello string. - DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 334 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 334 ) - { - free( pReceiveBuffer ); - return FALSE; - } - - // Check request - if ( iReceived > 6 ) - { - Base64Coder coder; - coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); - LPCSTR szResponse = coder.DecodedMessage(); - if ( szResponse && strlen( szResponse ) > 0 ) - { - char *auth_hex = hash_md5( szPassword, szResponse, strlen(szResponse) ); - if ( !auth_hex ) - { - free( pReceiveBuffer ); - return FALSE; - } - - char *szCommand = (char*)malloc( strlen( szUsername ) + strlen( auth_hex ) + 5 ); - if ( szCommand ) - { - sprintf( szCommand, "%s %s", szUsername, auth_hex ); - - free( auth_hex ); - - coder.Encode( (const PBYTE)szCommand, strlen( szCommand ) ); - - free( szCommand ); - - LPCSTR szAuthEncoded = coder.EncodedMessage(); - if ( szAuthEncoded == NULL ) - { - free( pReceiveBuffer ); - return FALSE; - } - - char *szAuthCommand = (char*)malloc( strlen( szAuthEncoded ) + 4 ); - if ( szAuthCommand ) - { - strcpy( szAuthCommand, szAuthEncoded ); - strcat( szAuthCommand, "\r\n" ); - - // Send auth data - if ( SendData( m_hSocket, (PBYTE)szAuthCommand, strlen( (const char*)szAuthCommand ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( szAuthCommand ); - free( pReceiveBuffer ); - return FALSE; - } - - // Check response - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 235 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 235 ) - { - free( pReceiveBuffer ); - return FALSE; - } - - bSuccess = TRUE; - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - - free( szAuthCommand ); - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - } - else - { - free( auth_hex ); - free( pReceiveBuffer ); - return FALSE; - } - } - else - { - free( pReceiveBuffer ); - return FALSE; - } - } - - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - return FALSE; - } - - free( pReceiveBuffer ); - } - else - { - SetErrorText( "malloc() failed.", GetLastError() ); - } - - return bSuccess; - } - - ////////////////////////////////////////////////////////////////////////// - inline BOOL CSMTPClient::SendMessage( LPCSTR szFromAddress, LPCSTR szFromName, LPCSTR szToAddresses, LPCSTR szSubject, LPCSTR szXMailer, LPBYTE pBodyBuffer, DWORD dwBodySize ) - { - BOOL bSuccess = FALSE; - - // Format Header - if ( !szFromAddress ) - { - SetErrorText( "SendMessage. Invalid Parameters!" ); - return NULL; - } - - char *szHeaderBuffer = (char*)malloc( 1024 * 16 ); - if ( szHeaderBuffer ) - { - // get the current date and time - char szDate[ 500 ]; - char sztTime[ 500 ]; - - SYSTEMTIME st = { 0 }; - ::GetSystemTime(&st); - - ::GetDateFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, &st, "ddd',' dd MMM yyyy", szDate , sizeof( szDate ) ); - ::GetTimeFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), TIME_FORCE24HOURFORMAT, &st, "HH':'mm':'ss", sztTime, sizeof( sztTime ) ); - - sprintf( szHeaderBuffer, "DATE: %s %s\r\n", szDate, sztTime ); - - // X-Mailer Field - if ( szXMailer && strlen( szXMailer ) ) - { - strcat( szHeaderBuffer, "X-Mailer: " ); - strcat( szHeaderBuffer, szXMailer ); - strcat( szHeaderBuffer, "\r\n" ); - } - - // From: - strcat( szHeaderBuffer, "From: " ); - if ( szFromName ) - { - strcat( szHeaderBuffer, "\"" ); - strcat( szHeaderBuffer, szFromName ); - strcat( szHeaderBuffer, "\" <" ); - strcat( szHeaderBuffer, szFromAddress ); - strcat( szHeaderBuffer, ">\r\n" ); - } - else - { - strcat( szHeaderBuffer, "<" ); - strcat( szHeaderBuffer, szFromAddress ); - strcat( szHeaderBuffer, ">\r\n" ); - } - - // Subject: - if ( szSubject && strlen( szSubject ) ) - { - strcat( szHeaderBuffer, "Subject: " ); - strcat( szHeaderBuffer, szSubject ); - strcat( szHeaderBuffer, "\r\n" ); - } - - // To Fields - strcat( szHeaderBuffer, "To: " ); - strcat( szHeaderBuffer, szToAddresses ); - strcat( szHeaderBuffer, "\r\n" ); - - // MIME - strcat( szHeaderBuffer, "MIME-Version: 1.0\r\nContent-type: text/plain; charset=US-ASCII\r\n" ); - - // End Header - strcat( szHeaderBuffer, "\r\n" ); - } - else - { - SetErrorText( "malloc error.", GetLastError() ); - return FALSE; - } - - - BYTE szCommandBuffer[ 256 ]; - sprintf( (char*)szCommandBuffer, "MAIL FROM:<%s> SIZE=%u\r\n", (char*)szFromAddress, strlen( szHeaderBuffer ) + dwBodySize + 2 ); - if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( szHeaderBuffer ); - return FALSE; - } - - DWORD dwReceiveBufferSize = 1024*16; - PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); - if ( pReceiveBuffer ) - { - DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 250 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 250 ) - { - free( szHeaderBuffer ); - free( pReceiveBuffer ); - return FALSE; - } - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( szHeaderBuffer ); - free( pReceiveBuffer ); - return FALSE; - } - - // Post "RCTP TO:" - char *szCurrentAddr = (char*)malloc( strlen( szToAddresses ) + 1 ); - if ( !szCurrentAddr ) - { - SetErrorText( "malloc error.", GetLastError() ); - free( szHeaderBuffer ); - free( pReceiveBuffer ); - return FALSE; - } - - const char* szToOffset = szToAddresses; - char* szZap = NULL; - - BOOL bRCPTAccepted = FALSE; - do - { - strcpy( szCurrentAddr, szToOffset ); - char *szExtractedAdress = szCurrentAddr; - szZap = strchr( szCurrentAddr, ',' ); - - if ( szZap ) - { - *szZap = 0; - szToOffset = szZap + 1; - } - - char *pSkobka1 = strchr( szCurrentAddr, '<' ); - char *pSkobka2 = strchr( szCurrentAddr, '>' ); - - if ( pSkobka1 && pSkobka2 && pSkobka2 > pSkobka1 ) - { - szExtractedAdress = pSkobka1 + 1; - *pSkobka2 = NULL; - } - - if ( szExtractedAdress && strlen( szExtractedAdress ) > 0 ) - { - sprintf( (char*)szCommandBuffer, "RCPT TO:<%s>\r\n", (char*)szExtractedAdress ); - if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( szCurrentAddr ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 250 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode == 250 ) - { - bRCPTAccepted = TRUE; - } - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( szCurrentAddr ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - } - - } while( szZap ); - - free( szCurrentAddr ); - - if ( bRCPTAccepted ) - { - sprintf( (char*)szCommandBuffer, "DATA\r\n" ); - if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 354 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode != 354 ) - { - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - - // Send message data (header + body + .) - if ( SendData( m_hSocket, (PBYTE)szHeaderBuffer, strlen( (const char*)szHeaderBuffer ) ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - - if ( SendData( m_hSocket, (PBYTE)pBodyBuffer, dwBodySize ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - - if ( SendData( m_hSocket, (PBYTE)"\r\n.\r\n", 5 ) == 0 ) - { - SetErrorText( "SendData error.", WSAGetLastError() ); - free( pReceiveBuffer ); - free( szHeaderBuffer ); - return FALSE; - } - - iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); - if ( iReceived ) - { - SetErrorText( pReceiveBuffer ); - - // Check 250 - int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); - if ( iResponseCode == 250 ) - { - bSuccess = TRUE; - } - } - else - { - SetErrorText( "ReceiveData error.", WSAGetLastError() ); - } - } - - free( pReceiveBuffer ); - } - else - { - SetErrorText( "malloc error.", GetLastError() ); - } - - if ( szHeaderBuffer ) - free( szHeaderBuffer ); - - return bSuccess; - } - - - - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - - -#ifndef PAGESIZE -#define PAGESIZE 4096 -#endif - -#ifndef ROUNDTOPAGE -#define ROUNDTOPAGE(a) (((a/4096)+1)*4096) -#endif - - ////////////////////////////////////////////////////////////////////// - // Construction/Destruction - ////////////////////////////////////////////////////////////////////// - - inline Base64Coder::Base64Coder() - : m_pDBuffer(NULL), - m_pEBuffer(NULL), - m_nDBufLen(0), - m_nEBufLen(0) - { - - } - - inline Base64Coder::~Base64Coder() - { - if(m_pDBuffer != NULL) - delete [] m_pDBuffer; - - if(m_pEBuffer != NULL) - delete [] m_pEBuffer; - } - - inline LPCSTR Base64Coder::DecodedMessage() const - { - return (LPCSTR) m_pDBuffer; - } - - inline LPCSTR Base64Coder::EncodedMessage() const - { - return (LPCSTR) m_pEBuffer; - } - - inline void Base64Coder::AllocEncode(DWORD nSize) - { - if(m_nEBufLen < nSize) - { - if(m_pEBuffer != NULL) - delete [] m_pEBuffer; - - m_nEBufLen = ROUNDTOPAGE(nSize); - m_pEBuffer = new BYTE[m_nEBufLen]; - } - - ::ZeroMemory(m_pEBuffer, m_nEBufLen); - m_nEDataLen = 0; - } - - inline void Base64Coder::AllocDecode(DWORD nSize) - { - if(m_nDBufLen < nSize) - { - if(m_pDBuffer != NULL) - delete [] m_pDBuffer; - - m_nDBufLen = ROUNDTOPAGE(nSize); - m_pDBuffer = new BYTE[m_nDBufLen]; - } - - ::ZeroMemory(m_pDBuffer, m_nDBufLen); - m_nDDataLen = 0; - } - - inline void Base64Coder::SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen) - { - DWORD i = 0; - - AllocEncode(nBufLen); - while(i < nBufLen) - { - if(!_IsBadMimeChar(pBuffer[i])) - { - m_pEBuffer[m_nEDataLen] = pBuffer[i]; - m_nEDataLen++; - } - - i++; - } - } - - inline void Base64Coder::SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen) - { - AllocDecode(nBufLen); - ::CopyMemory(m_pDBuffer, pBuffer, nBufLen); - m_nDDataLen = nBufLen; - } - - inline void Base64Coder::Encode(const PBYTE pBuffer, DWORD nBufLen) - { - SetDecodeBuffer(pBuffer, nBufLen); - AllocEncode(nBufLen * 2); - - TempBucket Raw; - DWORD nIndex = 0; - - while((nIndex + 3) <= nBufLen) - { - Raw.Clear(); - ::CopyMemory(&Raw, m_pDBuffer + nIndex, 3); - Raw.nSize = 3; - _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); - nIndex += 3; - m_nEDataLen += 4; - } - - if(nBufLen > nIndex) - { - Raw.Clear(); - Raw.nSize = (BYTE) (nBufLen - nIndex); - ::CopyMemory(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex); - _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); - m_nEDataLen += 4; - } - } - - inline void Base64Coder::Encode(LPCSTR szMessage) - { - if(szMessage != NULL) - Base64Coder::Encode((const PBYTE)szMessage, strlen( (const char*)szMessage)); - } - - inline void Base64Coder::Decode(const PBYTE pBuffer, DWORD dwBufLen) - { - if(is_init()) - _Init(); - - SetEncodeBuffer(pBuffer, dwBufLen); - - AllocDecode(dwBufLen); - - TempBucket Raw; - - DWORD nIndex = 0; - - while((nIndex + 4) <= m_nEDataLen) - { - Raw.Clear(); - Raw.nData[0] = DecodeTable()[m_pEBuffer[nIndex]]; - Raw.nData[1] = DecodeTable()[m_pEBuffer[nIndex + 1]]; - Raw.nData[2] = DecodeTable()[m_pEBuffer[nIndex + 2]]; - Raw.nData[3] = DecodeTable()[m_pEBuffer[nIndex + 3]]; - - if(Raw.nData[2] == 255) - Raw.nData[2] = 0; - if(Raw.nData[3] == 255) - Raw.nData[3] = 0; - - Raw.nSize = 4; - _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); - nIndex += 4; - m_nDDataLen += 3; - } - - // If nIndex < m_nEDataLen, then we got a decode message without padding. - // We may want to throw some kind of warning here, but we are still required - // to handle the decoding as if it was properly padded. - if(nIndex < m_nEDataLen) - { - Raw.Clear(); - for(DWORD i = nIndex; i < m_nEDataLen; i++) - { - Raw.nData[i - nIndex] = DecodeTable()[m_pEBuffer[i]]; - Raw.nSize++; - if(Raw.nData[i - nIndex] == 255) - Raw.nData[i - nIndex] = 0; - } - - _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); - m_nDDataLen += (m_nEDataLen - nIndex); - } - } - - inline void Base64Coder::Decode(LPCSTR szMessage) - { - if(szMessage != NULL) - Base64Coder::Decode((const PBYTE)szMessage, strlen((const char*)szMessage)); - } - - inline DWORD Base64Coder::_DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) - { - TempBucket Data; - DWORD nCount = 0; - - _DecodeRaw(Data, Decode); - - for(int i = 0; i < 3; i++) - { - pBuffer[i] = Data.nData[i]; - if(pBuffer[i] != 255) - nCount++; - } - - return nCount; - } - - - inline void Base64Coder::_EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) - { - TempBucket Data; - - _EncodeRaw(Data, Decode); - - for(int i = 0; i < 4; i++) - pBuffer[i] = Base64Digits()[Data.nData[i]]; - - switch(Decode.nSize) - { - case 1: - pBuffer[2] = '='; - case 2: - pBuffer[3] = '='; - } - } - - inline void Base64Coder::_DecodeRaw(TempBucket &Data, const TempBucket &Decode) - { - BYTE nTemp; - - Data.nData[0] = Decode.nData[0]; - Data.nData[0] <<= 2; - - nTemp = Decode.nData[1]; - nTemp >>= 4; - nTemp &= 0x03; - Data.nData[0] |= nTemp; - - Data.nData[1] = Decode.nData[1]; - Data.nData[1] <<= 4; - - nTemp = Decode.nData[2]; - nTemp >>= 2; - nTemp &= 0x0F; - Data.nData[1] |= nTemp; - - Data.nData[2] = Decode.nData[2]; - Data.nData[2] <<= 6; - nTemp = Decode.nData[3]; - nTemp &= 0x3F; - Data.nData[2] |= nTemp; - } - - inline void Base64Coder::_EncodeRaw(TempBucket &Data, const TempBucket &Decode) - { - BYTE nTemp; - - Data.nData[0] = Decode.nData[0]; - Data.nData[0] >>= 2; - - Data.nData[1] = Decode.nData[0]; - Data.nData[1] <<= 4; - nTemp = Decode.nData[1]; - nTemp >>= 4; - Data.nData[1] |= nTemp; - Data.nData[1] &= 0x3F; - - Data.nData[2] = Decode.nData[1]; - Data.nData[2] <<= 2; - - nTemp = Decode.nData[2]; - nTemp >>= 6; - - Data.nData[2] |= nTemp; - Data.nData[2] &= 0x3F; - - Data.nData[3] = Decode.nData[2]; - Data.nData[3] &= 0x3F; - } - - inline BOOL Base64Coder::_IsBadMimeChar(BYTE nData) - { - switch(nData) - { - case '\r': case '\n': case '\t': case ' ' : - case '\b': case '\a': case '\f': case '\v': - return TRUE; - default: - return FALSE; - } - } - - inline void Base64Coder::_Init() - { // Initialize Decoding table. - - int i; - - for(i = 0; i < 256; i++) - DecodeTable()[i] = -2; - - for(i = 0; i < 64; i++) - { - DecodeTable()[Base64Digits()[i]] = i; - DecodeTable()[Base64Digits()[i]|0x80] = i; - } - - DecodeTable()['='] = -1; - DecodeTable()['='|0x80] = -1; - - is_init() = TRUE; - } - - - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/net/smtp_helper.h b/contrib/epee/include/net/smtp_helper.h deleted file mode 100644 index b8252e1cff..0000000000 --- a/contrib/epee/include/net/smtp_helper.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "smtp.h" - -namespace epee -{ -namespace net_utils -{ - namespace smtp - { - - inline bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_addres, const std::string& from_name, const std::string& maillist, const std::string& subject, const std::string& mail_body) - { - net_utils::smtp::CSMTPClient smtp; - - if ( !smtp.ServerConnect( server.c_str(), port ) ) - { - LOG_PRINT("Reporting: Failed to connect to server " << server <<":"<< port, LOG_LEVEL_0); - return false; - } - - if(login.size() && pass.size()) - { - if ( !smtp.ServerLogin( login.c_str(), pass.c_str()) ) - { - LOG_PRINT("Reporting: Failed to auth on server " << server <<":"<< port, LOG_LEVEL_0); - return false; - - } - } - - if ( !smtp.SendMessage( from_addres.c_str(), - from_name.c_str(), - maillist.c_str(), - subject.c_str(), - "bicycle-client", - (LPBYTE)mail_body.data(), - mail_body.size())) - { - char *szErrorText = smtp.GetLastErrorText(); - if ( szErrorText ) - { - LOG_PRINT("Failed to send message, error text: " << szErrorText, LOG_LEVEL_0); - } - else - { - LOG_PRINT("Failed to send message, error text: null", LOG_LEVEL_0); - } - return false; - } - - smtp.ServerDisconnect(); - - return true; - - - } - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/pragma_comp_defs.h b/contrib/epee/include/pragma_comp_defs.h deleted file mode 100644 index f4ef7057e4..0000000000 --- a/contrib/epee/include/pragma_comp_defs.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#if defined(__GNUC__) - #define PRAGMA_WARNING_PUSH _Pragma("GCC diagnostic push") - #define PRAGMA_WARNING_POP _Pragma("GCC diagnostic pop") - #define PRAGMA_WARNING_DISABLE_VS(w) - #define PRAGMA_GCC(w) _Pragma(w) -#elif defined(_MSC_VER) - #define PRAGMA_WARNING_PUSH __pragma(warning( push )) - #define PRAGMA_WARNING_POP __pragma(warning( pop )) - #define PRAGMA_WARNING_DISABLE_VS(w) __pragma( warning ( disable: w )) - //#define PRAGMA_WARNING_DISABLE_GCC(w) - #define PRAGMA_GCC(w) -#endif diff --git a/contrib/epee/include/profile_tools.h b/contrib/epee/include/profile_tools.h deleted file mode 100644 index 49180c6a3c..0000000000 --- a/contrib/epee/include/profile_tools.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _PROFILE_TOOLS_H_ -#define _PROFILE_TOOLS_H_ - -#include - -namespace epee -{ - -#ifdef ENABLE_PROFILING -#define PROFILE_FUNC(immortal_ptr_str) static profile_tools::local_call_account lcl_acc(immortal_ptr_str); \ - profile_tools::call_frame cf(lcl_acc); - -#define PROFILE_FUNC_SECOND(immortal_ptr_str) static profile_tools::local_call_account lcl_acc2(immortal_ptr_str); \ - profile_tools::call_frame cf2(lcl_acc2); - -#define PROFILE_FUNC_THIRD(immortal_ptr_str) static profile_tools::local_call_account lcl_acc3(immortal_ptr_str); \ - profile_tools::call_frame cf3(lcl_acc3); - -#define PROFILE_FUNC_ACC(acc) \ - profile_tools::call_frame cf(acc); - - -#else -#define PROFILE_FUNC(immortal_ptr_str) -#define PROFILE_FUNC_SECOND(immortal_ptr_str) -#define PROFILE_FUNC_THIRD(immortal_ptr_str) -#endif - -#define START_WAY_POINTS() uint64_t _____way_point_time = epee::misc_utils::get_tick_count(); -#define WAY_POINT(name) {uint64_t delta = epee::misc_utils::get_tick_count()-_____way_point_time; LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();} -#define WAY_POINT2(name, avrg_obj) {uint64_t delta = epee::misc_utils::get_tick_count()-_____way_point_time; avrg_obj.push(delta); LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();} - - -#define TIME_MEASURE_START(var_name) uint64_t var_name = epee::misc_utils::get_tick_count(); -#define TIME_MEASURE_FINISH(var_name) var_name = epee::misc_utils::get_tick_count() - var_name; - -namespace profile_tools -{ - struct local_call_account - { - local_call_account(const char* pstr):m_count_of_call(0), m_summary_time_used(0),m_pname(pstr) - {} - ~local_call_account() - { - LOG_PRINT2("profile_details.log", "PROFILE "< - - -namespace epee -{ - class global_regexp_critical_section - { - private: - mutable critical_section regexp_lock; - public: - global_regexp_critical_section(){} - critical_section& get_lock()const {return regexp_lock;} - }; - - const static global_regexp_critical_section gregexplock; - -#define STATIC_REGEXP_EXPR_1(var_name, xpr_text, reg_exp_flags) \ - static volatile uint32_t regexp_initialized_1 = 0;\ - volatile uint32_t local_is_initialized_1 = regexp_initialized_1;\ - if(!local_is_initialized_1)\ - gregexplock.get_lock().lock();\ - static const boost::regex var_name(xpr_text , reg_exp_flags);\ - if(!local_is_initialized_1)\ -{\ - boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_1, 1);\ - gregexplock.get_lock().unlock();\ -} - -#define STATIC_REGEXP_EXPR_2(var_name, xpr_text, reg_exp_flags) \ - static volatile uint32_t regexp_initialized_2 = 0;\ - volatile uint32_t local_is_initialized_2 = regexp_initialized_2;\ - if(!local_is_initialized_2)\ - gregexplock.get_lock().lock().lock();\ - static const boost::regex var_name(xpr_text , reg_exp_flags);\ - if(!local_is_initialized_2)\ -{\ - boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_2, 1);\ - gregexplock.get_lock().lock().unlock();\ -} - -#define STATIC_REGEXP_EXPR_3(var_name, xpr_text, reg_exp_flags) \ - static volatile uint32_t regexp_initialized_3 = 0;\ - volatile uint32_t local_is_initialized_3 = regexp_initialized_3;\ - if(!local_is_initialized_3)\ - gregexplock.get_lock().lock().lock();\ - static const boost::regex var_name(xpr_text , reg_exp_flags);\ - if(!local_is_initialized_3)\ -{\ - boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_3, 1);\ - gregexplock.get_lock().lock().unlock();\ -} -} - -#endif //_REG_EXP_DEFINER_H_ diff --git a/contrib/epee/include/reg_utils.h b/contrib/epee/include/reg_utils.h deleted file mode 100644 index 22227a9b27..0000000000 --- a/contrib/epee/include/reg_utils.h +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _MUSC_UTILS_EX_H_ -#define _MUSC_UTILS_EX_H_ - -namespace epee -{ -namespace reg_utils -{ - //----------------------------------------------------------------------------------------------------------------------------------- - template - bool RegSetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const T& valToSave, bool force_create = true) - { - HKEY hRegKey = 0; - DWORD dw = 0; - - if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) - if(force_create && (::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS) ) - return false; - - - DWORD val_type = (sizeof(valToSave) == sizeof(DWORD)) ? REG_DWORD:REG_BINARY; - - BOOL res = ::RegSetValueExA( hRegKey, pValName, 0, val_type, (LPBYTE)&valToSave, sizeof(valToSave)) == ERROR_SUCCESS; - - ::RegCloseKey(hRegKey); - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - template - bool RegGetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, T& valToSave) - { - HKEY hRegKey = 0; - LONG res = 0; - - - if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) - { - DWORD dwType, lSize = 0; - res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize); - if(ERROR_SUCCESS!=res || (sizeof(valToSave) < lSize) ) - { - ::RegCloseKey(hRegKey); - return false; - } - res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)&valToSave, &lSize); - } - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - inline - bool RegSetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& strToSave) - { - HKEY hRegKey = 0; - DWORD dw = 0; - DWORD res_ = 0; - if( (res_ = ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw)) != ERROR_SUCCESS ) - if( (res_= ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey)) != ERROR_SUCCESS ) - return false; - - DWORD valType = REG_SZ; - const char* pStr = strToSave.c_str(); - DWORD sizeOfStr = (DWORD)strToSave.size()+1; - LSTATUS res = ::RegSetValueExA(hRegKey, pValName, 0, valType, (LPBYTE)pStr, sizeOfStr); - - ::RegCloseKey(hRegKey); - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - inline - bool RegGetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& strToSave) - { - HKEY hRegKey = 0; - LONG res = 0; - - - if((res = ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey)) == ERROR_SUCCESS ) - { - DWORD dwType, lSize = 0; - res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize); - if(ERROR_SUCCESS!=res) - { - - ::RegCloseKey(hRegKey); - return false; - } - char* pTmpStr = new char[lSize+2]; - memset(pTmpStr, 0, lSize+2); - res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)pTmpStr, &lSize); - pTmpStr[lSize+1] = 0; //be happy ;) - strToSave = pTmpStr; - delete [] pTmpStr; - ::RegCloseKey(hRegKey); - } - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - template - bool RegSetRAWValue(HKEY hKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY) - { - LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.get(0), (DWORD)valToSave.get_size()); - - return ERROR_SUCCESS==res ? true:false; - } - //---------------------------------------------------------------------------------------------------------------------------------- - bool RegSetRAWValue(HKEY hKey, const char* pValName, const std::string & valToSave, DWORD valType = REG_BINARY) - { - LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.data(), (DWORD)valToSave.size()); - - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - template - bool RegGetRAWValue(HKEY hKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType) - { - DWORD dwType, lSize = 0; - LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize); - if(ERROR_SUCCESS!=res || 0 >= lSize) - { - valToSave.release(); - return false; - } - if(valToSave.get_size() < lSize) - valToSave.alloc_buff(lSize); - res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.get(0), &lSize); - if(pRegType) *pRegType = dwType; - - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - bool RegGetRAWValue(HKEY hKey, const char* pValName, std::string& valToSave, DWORD* pRegType) - { - DWORD dwType, lSize = 0; - LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize); - if(ERROR_SUCCESS!=res || 0 >= lSize) - { - return false; - } - - valToSave.resize(lSize); - res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.data(), &lSize); - if(pRegType) *pRegType = dwType; - - return ERROR_SUCCESS==res ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - template - bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY) - { - HKEY hRegKey = 0; - DWORD dw = 0; - bool res = false; - - if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS ) - if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) - return false; - - res = RegSetRAWValue(hRegKey, pValName, valToSave, valType); - - ::RegCloseKey(hRegKey); - return res; - } - //----------------------------------------------------------------------------------------------------------------------------------- - bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& valToSave, DWORD valType = REG_BINARY) - { - HKEY hRegKey = 0; - DWORD dw = 0; - bool res = false; - - if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS ) - if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) - return false; - - res = RegSetRAWValue(hRegKey, pValName, valToSave, valType); - - ::RegCloseKey(hRegKey); - return res; - } - //----------------------------------------------------------------------------------------------------------------------------------- - template - bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType) - { - HKEY hRegKey = 0; - bool res = false; - - if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) - { - res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType); - ::RegCloseKey(hRegKey); - } - return res; - } - //----------------------------------------------------------------------------------------------------------------------------------- - inline - bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& valToSave, DWORD* pRegType) - { - HKEY hRegKey = 0; - bool res = false; - - if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) - { - res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType); - ::RegCloseKey(hRegKey); - } - return res; - } - //----------------------------------------------------------------------------------------------------------------------------------- - inline - bool RegRemoveValue(HKEY hParentKey, const char* pValName) - { - //CHECK_AND_ASSERT(hParentKey&&pValName, false); - return ::RegDeleteValueA(hParentKey, pValName)==ERROR_SUCCESS ? true:false; - } - //----------------------------------------------------------------------------------------------------------------------------------- - inline - bool RegRemoveKey(HKEY hParentKey, const char* pKeyName) - { - //CHECK_AND_ASSERT(hParentKey&&pKeyName, false); - return ::RegDeleteKeyA(hParentKey, pKeyName)==ERROR_SUCCESS ? true:false; - } - -} -} -#endif //_MUSC_UTILS_EX_H_ diff --git a/contrib/epee/include/serialization/enableable.h b/contrib/epee/include/serialization/enableable.h deleted file mode 100644 index ab1d799e64..0000000000 --- a/contrib/epee/include/serialization/enableable.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -{ - - template - struct enableable - { - t_obj v; - bool enabled; - - enableable() - : v(t_obj()), enabled(true) - { // construct from defaults - } - - enableable(const t_obj& _v) - : v(_v), enabled(true) - { // construct from specified values - } - - enableable(const enableable& _v) - : v(_v.v), enabled(_v.enabled) - { // construct from specified values - } - }; -} \ No newline at end of file diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h deleted file mode 100644 index bf2c8dacd0..0000000000 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_log_ex.h" -#include "enableable.h" -#include "keyvalue_serialization_overloads.h" -#include "serialization/serialization.h" - -namespace epee -{ - /************************************************************************/ - /* Serialize map declarations */ - /************************************************************************/ -#define BEGIN_KV_SERIALIZE_MAP() \ -public: \ - template \ - bool store( t_storage& st, typename t_storage::hsection hparent_section = nullptr) const\ - {\ - return serialize_map(*this, st, hparent_section);\ - }\ - template \ - bool _load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\ - {\ - return serialize_map(*this, stg, hparent_section);\ - }\ - template \ - bool load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\ - {\ - try{\ - return serialize_map(*this, stg, hparent_section);\ - }\ - catch(const std::exception& err) \ - { \ - (void)(err); \ - LOG_ERROR("Exception on unserializing: " << err.what());\ - return false; \ - }\ - }\ - template \ - static bool serialize_map(this_type& this_ref, t_storage& stg, typename t_storage::hsection hparent_section) \ - { - -#define KV_SERIALIZE_N(varialble, val_name) \ - epee::serialization::selector::serialize(this_ref.varialble, stg, hparent_section, val_name); - -#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ - epee::serialization::selector::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); - -#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \ - 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_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); - -#define END_KV_SERIALIZE_MAP() return true;} - -#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_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) - -} - - - - diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h deleted file mode 100644 index d53f167fb0..0000000000 --- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -namespace epee -{ - namespace serialization - { - - //------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_t_val(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return stg.set_value(pname, d, hparent_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_t_val(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return stg.get_value(pname, d, hparent_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - std::string blob((const char *)&d, sizeof(d)); - return stg.set_value(pname, blob, hparent_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - std::string blob; - if(!stg.get_value(pname, blob, hparent_section)) - return false; - CHECK_AND_ASSERT_MES(blob.size() == sizeof(d), false, "unserialize_t_val_as_blob: size of " << typeid(t_type).name() << " = " << sizeof(t_type) << ", but stored blod size = " << blob.size() << ", value name = " << pname); - d = *(const t_type*)blob.data(); - return true; - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_t_obj(const serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); - CHECK_AND_ASSERT_MES(hchild_section, false, "serialize_t_obj: failed to open/create section " << pname); - return obj.store(stg, hchild_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); - if(!hchild_section) return false; - return obj._load(stg, hchild_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_t_obj(enableable& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - if(!obj.enabled) - return true; - return serialize_t_obj(obj.v, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_t_obj(enableable& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - obj.enabled = false; - typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); - if(!hchild_section) return false; - obj.enabled = true; - return obj.v._load(stg, hchild_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - if(!container.size()) return true; - typename stl_container::const_iterator it = container.begin(); - typename t_storage::harray hval_array = stg.insert_first_value(pname, *it, hparent_section); - CHECK_AND_ASSERT_MES(hval_array, false, "failed to insert first value to storage"); - it++; - for(;it!= container.end();it++) - stg.insert_next_value(hval_array, *it); - - return true; - } - //-------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_stl_container_t_val(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - container.clear(); - typename stl_container::value_type exchange_val; - typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section); - if(!hval_array) return false; - container.push_back(std::move(exchange_val)); - while(stg.get_next_value(hval_array, exchange_val)) - container.push_back(std::move(exchange_val)); - return true; - }//-------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_stl_container_pod_val_as_blob(const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - if(!container.size()) return true; - typename stl_container::const_iterator it = container.begin(); - std::string mb; - mb.resize(sizeof(typename stl_container::value_type)*container.size()); - typename stl_container::value_type* p_elem = (typename stl_container::value_type*)mb.data(); - BOOST_FOREACH(const typename stl_container::value_type& v, container) - { - *p_elem = v; - p_elem++; - } - return stg.set_value(pname, mb, hparent_section); - } - //-------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_stl_container_pod_val_as_blob(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - container.clear(); - std::string buff; - bool res = stg.get_value(pname, buff, hparent_section); - if(res) - { - size_t loaded_size = buff.size(); - 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_t count = (loaded_size/sizeof(typename stl_container::value_type)); - for(size_t i = 0; i < count; i++) - container.push_back(*(pelem++)); - } - return res; - } - //-------------------------------------------------------------------------------------------------------------------- - template - static bool serialize_stl_container_t_obj (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - bool res = false; - if(!container.size()) return true; - typename stl_container::const_iterator it = container.begin(); - typename t_storage::hsection hchild_section = nullptr; - typename t_storage::harray hsec_array = stg.insert_first_section(pname, hchild_section, hparent_section); - CHECK_AND_ASSERT_MES(hsec_array && hchild_section, false, "failed to insert first section with section name " << pname); - res = it->store(stg, hchild_section); - it++; - for(;it!= container.end();it++) - { - stg.insert_next_section(hsec_array, hchild_section); - res |= it->store(stg, hchild_section); - } - return res; - } - //-------------------------------------------------------------------------------------------------------------------- - template - static bool unserialize_stl_container_t_obj(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - bool res = false; - container.clear(); - typename stl_container::value_type val = typename stl_container::value_type(); - typename t_storage::hsection hchild_section = nullptr; - typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section); - if(!hsec_array || !hchild_section) return false; - res = val._load(stg, hchild_section); - container.push_back(val); - while(stg.get_next_section(hsec_array, hchild_section)) - { - typename stl_container::value_type val_l = typename stl_container::value_type(); - res |= val_l._load(stg, hchild_section); - container.push_back(std::move(val_l)); - } - return res; - } - //-------------------------------------------------------------------------------------------------------------------- - template - struct kv_serialization_overloads_impl_is_base_serializable_types; - - template<> - struct kv_serialization_overloads_impl_is_base_serializable_types - { - template - static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return stg.set_value(pname, d, hparent_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return stg.get_value(pname, d, hparent_section); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_serialize(const std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return serialize_stl_container_t_val(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_unserialize(std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return unserialize_stl_container_t_val(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return serialize_stl_container_t_val(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_unserialize(std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return unserialize_stl_container_t_val(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - }; - template<> - struct kv_serialization_overloads_impl_is_base_serializable_types - { - template - static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return serialize_t_obj(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return unserialize_t_obj(d, stg, hparent_section, pname); - } - - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_serialize(const std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return serialize_stl_container_t_obj(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_unserialize(std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return serialize_stl_container_t_obj(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - static bool kv_unserialize(std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); - } - }; - template - struct base_serializable_types: public boost::mpl::vector::type - {}; - //------------------------------------------------------------------------------------------------------------------- - template struct selector; - template<> - struct selector - { - template - static bool serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialize(d, stg, hparent_section, pname); - } - - template - static bool serialize_stl_container_pod_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return epee::serialization::serialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname); - } - - template - static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return epee::serialization::serialize_t_val_as_blob(d, stg, hparent_section, pname); - } - - - }; - template<> - struct selector - { - template - static bool serialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_unserialize(d, stg, hparent_section, pname); - } - template - static bool serialize_stl_container_pod_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return epee::serialization::unserialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname); - } - - template - static bool serialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return epee::serialization::unserialize_t_val_as_blob(d, stg, hparent_section, pname); - } - }; - - template - bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_serialize(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - bool kv_serialize(const std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_serialize(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - bool kv_unserialize(std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_serialize(d, stg, hparent_section, pname); - } - //------------------------------------------------------------------------------------------------------------------- - template - bool kv_unserialize(std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) - { - return kv_serialization_overloads_impl_is_base_serializable_types, typename std::remove_const::type>::value>::kv_unserialize(d, stg, hparent_section, pname); - } - } -} diff --git a/contrib/epee/include/serialization/serialize_base.h b/contrib/epee/include/serialization/serialize_base.h deleted file mode 100644 index 84a1624cb6..0000000000 --- a/contrib/epee/include/serialization/serialize_base.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once - diff --git a/contrib/epee/include/service_impl_base.h b/contrib/epee/include/service_impl_base.h deleted file mode 100644 index 6e9aefc461..0000000000 --- a/contrib/epee/include/service_impl_base.h +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _SERVICE_IMPL_BASE_H_ -#define _SERVICE_IMPL_BASE_H_ - -#pragma comment(lib, "advapi32.lib") - - -namespace epee -{ -class service_impl_base { - public: - service_impl_base(); - virtual ~service_impl_base(); - - virtual const char *get_name() = 0; - virtual const char *get_caption() = 0; - virtual const char *get_description() = 0; - - bool run_service(); - virtual bool install(); - virtual bool remove(); - virtual bool init(); - void set_control_accepted(unsigned controls); - void set_status(unsigned state, unsigned pending = 0); - unsigned get_control_accepted(); - - private: - virtual void service_main() = 0; - virtual unsigned service_handler(unsigned control, unsigned event_code, - void *pdata) = 0; - //------------------------------------------------------------------------- - static service_impl_base*& instance(); - //------------------------------------------------------------------------- - static DWORD __stdcall _service_handler(DWORD control, DWORD event, - void *pdata, void *pcontext); - static void __stdcall service_entry(DWORD argc, char **pargs); - virtual SERVICE_FAILURE_ACTIONSA* get_failure_actions(); - - private: - SC_HANDLE m_manager; - SC_HANDLE m_service; - SERVICE_STATUS_HANDLE m_status_handle; - DWORD m_accepted_control; -}; - -inline service_impl_base::service_impl_base() { - m_manager = 0; - m_service = 0; - m_status_handle = 0; - m_accepted_control = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN - | SERVICE_ACCEPT_PAUSE_CONTINUE; - - instance() = this; -} -//----------------------------------------------------------------------------- -inline service_impl_base::~service_impl_base() { - if (m_service) { - ::CloseServiceHandle(m_service); - } - m_service = 0; - if (m_manager) { - ::CloseServiceHandle(m_manager); - } - m_manager = 0; - instance() = 0; -} -//----------------------------------------------------------------------------- -inline service_impl_base*& service_impl_base::instance() { - static service_impl_base *pservice = NULL; - return pservice; -} -//----------------------------------------------------------------------------- -inline -bool service_impl_base::install() { - CHECK_AND_ASSERT(!m_service, false); - const char *psz_descr = get_description(); - SERVICE_FAILURE_ACTIONSA* fail_acts = get_failure_actions(); - - char sz_path[MAX_PATH]; - ::GetModuleFileNameA(0, sz_path, sizeof(sz_path)); - ::GetShortPathNameA(sz_path, sz_path, sizeof(sz_path)); - - while (TRUE) { - if (!m_manager) { - m_manager = ::OpenSCManager(NULL, NULL, GENERIC_ALL); - if (!m_manager) { - int err = GetLastError(); - LOG_ERROR( - "Failed to OpenSCManager(), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - } - m_service = ::CreateServiceA(m_manager, get_name(), get_caption(), - SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, - SERVICE_ERROR_IGNORE, sz_path, 0, 0, 0, 0, 0); - if (!m_service) { - int err = GetLastError(); - LOG_ERROR( - "Failed to CreateService(), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - - if (psz_descr) { - SERVICE_DESCRIPTIONA sd = { (char*) psz_descr }; - if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_DESCRIPTION, - &sd)) { - int err = GetLastError(); - LOG_ERROR( - "Failed to ChangeServiceConfig2(SERVICE_CONFIG_DESCRIPTION), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - } - - if (fail_acts) { - if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_FAILURE_ACTIONS, - fail_acts)) { - int err = GetLastError(); - LOG_ERROR( - "Failed to ChangeServiceConfig2(SERVICE_CONFIG_FAILURE_ACTIONS), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - } - LOG_PRINT("Installed succesfully.", LOG_LEVEL_0); - return true; - } - LOG_PRINT("Failed to install.", LOG_LEVEL_0); - return false; -} -//----------------------------------------------------------------------------- -inline -bool service_impl_base::remove() { - CHECK_AND_ASSERT(!m_service, false); - - while (TRUE) { - if (!m_manager) { - m_manager = ::OpenSCManager(0, 0, GENERIC_ALL); - if (!m_manager) { - int err = GetLastError(); - LOG_ERROR( - "Failed to OpenSCManager(), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - } - - if (!m_service) { - m_service = ::OpenServiceA(m_manager, get_name(), SERVICE_STOP | DELETE); - if (!m_service) { - int err = GetLastError(); - LOG_ERROR( - "Failed to OpenService(), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - } - - SERVICE_STATUS status = { }; - if (!::ControlService(m_service, SERVICE_CONTROL_STOP, &status)) { - int err = ::GetLastError(); - if (err == ERROR_SHUTDOWN_IN_PROGRESS) - continue; - else if (err != ERROR_SERVICE_NOT_ACTIVE) { - LOG_ERROR( - "Failed to ControlService(SERVICE_CONTROL_STOP), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - } - - if (!::DeleteService(m_service)) { - int err = ::GetLastError(); - LOG_ERROR( - "Failed to ControlService(SERVICE_CONTROL_STOP), last err=" - << log_space::get_win32_err_descr(err)); - break; - } - - LOG_PRINT("Removed successfully.", LOG_LEVEL_0); - break; - } - - return true; -} -//----------------------------------------------------------------------------- -inline -bool service_impl_base::init() { - return true; -} -//----------------------------------------------------------------------------- -inline -bool service_impl_base::run_service() { - CHECK_AND_ASSERT(!m_service, false); - - long error_code = 0; - - SERVICE_TABLE_ENTRYA service_table[2]; - ZeroMemory(&service_table, sizeof(service_table)); - - service_table->lpServiceName = (char*) get_name(); - service_table->lpServiceProc = service_entry; - - LOG_PRINT("[+] Start service control dispatcher for \"" << get_name() << "\"", - LOG_LEVEL_1); - - error_code = 1; - BOOL res = ::StartServiceCtrlDispatcherA(service_table); - if (!res) { - int err = GetLastError(); - LOG_PRINT( - "[+] Error starting service control dispatcher, err=" - << log_space::get_win32_err_descr(err), LOG_LEVEL_1); - return false; - } else { - LOG_PRINT("[+] End service control dispatcher for \"" << get_name() << "\"", - LOG_LEVEL_1); - } - return true; -} -//----------------------------------------------------------------------------- -inline DWORD __stdcall service_impl_base::_service_handler(DWORD control, - DWORD event, void *pdata, void *pcontext) { - CHECK_AND_ASSERT(pcontext, ERROR_CALL_NOT_IMPLEMENTED); - - service_impl_base *pservice = (service_impl_base*) pcontext; - return pservice->service_handler(control, event, pdata); -} -//----------------------------------------------------------------------------- -inline -void __stdcall service_impl_base::service_entry(DWORD argc, char **pargs) { - service_impl_base *pme = instance(); - LOG_PRINT("instance: " << pme, LOG_LEVEL_4); - if (!pme) { - LOG_ERROR("Error: at service_entry() pme = NULL"); - return; - } - pme->m_status_handle = ::RegisterServiceCtrlHandlerExA(pme->get_name(), - _service_handler, pme); - - pme->set_status(SERVICE_RUNNING); - pme->service_main(); - pme->set_status(SERVICE_STOPPED); -} -//----------------------------------------------------------------------------- -inline -void service_impl_base::set_status(unsigned state, unsigned pending) { - if (!m_status_handle) - return; - - SERVICE_STATUS status = { 0 }; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; - status.dwCurrentState = state; - status.dwControlsAccepted = m_accepted_control; - /*status.dwWin32ExitCode = NO_ERROR; - status.dwServiceSpecificExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - status.dwCheckPoint = 0; - status.dwWaitHint = 0; - - status.dwCurrentState = state;*/ - - if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING - || state == SERVICE_CONTINUE_PENDING || state == SERVICE_PAUSE_PENDING) { - status.dwWaitHint = 2000; - status.dwCheckPoint = pending; - } - ::SetServiceStatus(m_status_handle, &status); -} -//----------------------------------------------------------------------------------------- -inline -void service_impl_base::set_control_accepted(unsigned controls) { - m_accepted_control = controls; -} -//----------------------------------------------------------------------------------------- -inline -unsigned service_impl_base::get_control_accepted() { - return m_accepted_control; -} -//----------------------------------------------------------------------------------------- -inline SERVICE_FAILURE_ACTIONSA* service_impl_base::get_failure_actions() { - // first 3 failures in 30 minutes. Service will be restarted. - // do nothing for next failures - static SC_ACTION sa[] = { { SC_ACTION_RESTART, 3 * 1000 }, { - SC_ACTION_RESTART, 3 * 1000 }, { SC_ACTION_RESTART, 3 * 1000 }, { - SC_ACTION_NONE, 0 } }; - - static SERVICE_FAILURE_ACTIONSA sfa = { 1800, // interval for failures counter - 30 min - "", NULL, 4, (SC_ACTION*) &sa }; - - // TODO: refactor this code, really unsafe! - return &sfa; -} -} - -#endif //_SERVICE_IMPL_BASE_H_ diff --git a/contrib/epee/include/sha1.h b/contrib/epee/include/sha1.h deleted file mode 100644 index ce42082f8e..0000000000 --- a/contrib/epee/include/sha1.h +++ /dev/null @@ -1,51 +0,0 @@ - -/* - Copyright (c) 2011, Micael Hildenborg - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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 SHA1_DEFINED -#define SHA1_DEFINED - -namespace sha1 { - - /** - @param src points to any kind of data to be hashed. - @param bytelength the number of bytes to hash from the src pointer. - @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in. - */ - void calc(const void* src, const int bytelength, unsigned char* hash); - - /** - @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function. - @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string. - */ - void toHexString(const unsigned char* hash, char* hexstring); - -} // namespace sha1 - -#include "sha1.inl" - -#endif // SHA1_DEFINED diff --git a/contrib/epee/include/sha1.inl b/contrib/epee/include/sha1.inl deleted file mode 100644 index d332027240..0000000000 --- a/contrib/epee/include/sha1.inl +++ /dev/null @@ -1,179 +0,0 @@ - -/* - Copyright (c) 2011, Micael Hildenborg - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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. - */ - -/* - Contributors: - Gustav - Several members in the gamedev.se forum. - Gregory Petrosyan - */ - -#include "sha1.h" - -namespace sha1 { -namespace {// local -// Rotate an integer value to left. -inline const unsigned int rol(const unsigned int value, - const unsigned int steps) { - return ((value << steps) | (value >> (32 - steps))); -} - -// Sets the first 16 integers in the buffert to zero. -// Used for clearing the W buffert. -inline void clearWBuffert(unsigned int* buffert) { - for (int pos = 16; --pos >= 0;) - { - buffert[pos] = 0; - } -} - -inline -void innerHash(unsigned int* result, unsigned int* w) { - unsigned int a = result[0]; - unsigned int b = result[1]; - unsigned int c = result[2]; - unsigned int d = result[3]; - unsigned int e = result[4]; - - int round = 0; - -#define sha1macro(func,val) \ - { \ - const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ - e = d; \ - d = c; \ - c = rol(b, 30); \ - b = a; \ - a = t; \ - } - - while (round < 16) { - sha1macro((b & c) | (~b & d), 0x5a827999) - ++round; - } - while (round < 20) { - w[round] = rol( - (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro((b & c) | (~b & d), 0x5a827999) - ++round; - } - while (round < 40) { - w[round] = rol( - (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro(b ^ c ^ d, 0x6ed9eba1) - ++round; - } - while (round < 60) { - w[round] = rol( - (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) - ++round; - } - while (round < 80) { - w[round] = rol( - (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); - sha1macro(b ^ c ^ d, 0xca62c1d6) - ++round; - } - -#undef sha1macro - - result[0] += a; - result[1] += b; - result[2] += c; - result[3] += d; - result[4] += e; -} -} // namespace - -inline -void calc(const void* src, const int bytelength, unsigned char* hash) { - // Init the result array. - unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, - 0xc3d2e1f0 }; - - // Cast the void src pointer to be the byte array we can work with. - const unsigned char* sarray = (const unsigned char*) src; - - // The reusable round buffer - unsigned int w[80]; - - // Loop through all complete 64byte blocks. - const int endOfFullBlocks = bytelength - 64; - int endCurrentBlock; - int currentBlock(0); - - while (currentBlock <= endOfFullBlocks) { - endCurrentBlock = currentBlock + 64; - - // Init the round buffer with the 64 byte block data. - for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) - { - // This line will swap endian on big endian and keep endian on little endian. - w[roundPos++] = (unsigned int) sarray[currentBlock + 3] - | (((unsigned int) sarray[currentBlock + 2]) << 8) - | (((unsigned int) sarray[currentBlock + 1]) << 16) - | (((unsigned int) sarray[currentBlock]) << 24); - } - innerHash(result, w); - } - - // Handle the last and not full 64 byte block if existing. - endCurrentBlock = bytelength - currentBlock; - clearWBuffert(w); - int lastBlockBytes = 0; - for (; lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { - w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes - + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); - } - w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); - if (endCurrentBlock >= 56) { - innerHash(result, w); - clearWBuffert(w); - } - w[15] = bytelength << 3; - innerHash(result, w); - - // Store hash in result pointer, and make sure we get in in the correct order on both endian models. - for (int hashByte = 20; --hashByte >= 0;) { - hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) - & 0xff; - } -} -inline -void toHexString(const unsigned char* hash, char* hexstring) { - const char hexDigits[] = { "0123456789abcdef" }; - - for (int hashByte = 20; --hashByte >= 0;) - { - hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf]; - hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf]; - } - hexstring[40] = 0; -} -} // namespace sha1 diff --git a/contrib/epee/include/soci_helper.h b/contrib/epee/include/soci_helper.h deleted file mode 100644 index 813edc1fcf..0000000000 --- a/contrib/epee/include/soci_helper.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "soci.h" -#include "soci-postgresql.h" - -using namespace epee; -namespace soci -{ - - template <> - struct type_conversion - { - typedef long long base_type; - - static void from_base(base_type a_, indicator ind, uint64_t & mi) - { - if (ind == i_null) - { - mi = 0; - //throw soci_error("Null value not allowed for this type"); - } - mi = (uint64_t)a_; - //mi.set(i); - } - - static void to_base(const uint64_t & mi, base_type & i, indicator & ind) - { - i = (base_type)mi; - ind = i_ok; - } - }; - - - - template <> - struct type_conversion - { - typedef int base_type; - - static void from_base(base_type a_, indicator ind, bool& mi) - { - if (ind == i_null) - { - mi = false; - //throw soci_error("Null value not allowed for this type"); - } - mi = a_? true:false; - //mi.set(i); - } - - static void to_base(const bool & mi, base_type & i, indicator & ind) - { - i = mi? 1:0; - ind = i_ok; - } - }; - - - - class per_thread_session - { - public: - bool init(const std::string& connection_string) - { - m_connection_string = connection_string; - - return true; - } - - soci::session& get() - { - - //soci::session - - m_db_connections_lock.lock(); - boost::shared_ptr& conn_ptr = m_db_connections[epee::misc_utils::get_thread_string_id()]; - m_db_connections_lock.unlock(); - if(!conn_ptr.get()) - { - conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string)); - } - //init new connection - return *conn_ptr.get(); - } - - bool reopen() - { - //soci::session - - m_db_connections_lock.lock(); - boost::shared_ptr& conn_ptr = m_db_connections[misc_utils::get_thread_string_id()]; - m_db_connections_lock.unlock(); - if(conn_ptr.get()) - { - conn_ptr->close(); - conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string)); - } - - //init new connection - return true; - } - - //---------------------------------------------------------------------------------------------- - bool check_status() - { - return true; - } - - protected: - private: - std::map > m_db_connections; - epee::critical_section m_db_connections_lock; - std::string m_connection_string; - }; -} -/*}*/ \ No newline at end of file diff --git a/contrib/epee/include/static_initializer.h b/contrib/epee/include/static_initializer.h deleted file mode 100644 index 1510805c25..0000000000 --- a/contrib/epee/include/static_initializer.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _STATIC_INITIALIZER_H_ -#define _STATIC_INITIALIZER_H_ - - -namespace epee -{ -/*********************************************************************** -class initializer - useful to initialize some static classes - which have init() and un_init() static members -************************************************************************/ - -template -class initializer -{ -public: - initializer() - { - to_initialize::init(); - } - ~initializer() - { - to_initialize::un_init(); - } -}; - -} -#endif //_STATIC_INITIALIZER_H_ diff --git a/contrib/epee/include/storages/activity_notifier.h b/contrib/epee/include/storages/activity_notifier.h deleted file mode 100644 index 14b6ebbfbc..0000000000 --- a/contrib/epee/include/storages/activity_notifier.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "inmemtoxml.h" - -//#include "levin/levin_server.h" - -namespace epee -{ - -class activity_printer_base -{ -public: - activity_printer_base(){} - virtual ~activity_printer_base(){} -}; - -template -class notify_activity_printer: public activity_printer_base -{ -public: - notify_activity_printer(int level, A& arg, bool is_notify_mode = true):m_ref_arg(arg), m_level(level), m_is_notify_mode(is_notify_mode) - { - m_command_name = typeid(m_ref_arg).name(); - m_command_name.erase(0, 7); - m_command_name.erase(m_command_name.size()-10, m_command_name.size()-1); - if(level == log_space::get_set_log_detalisation_level()) - { - LOG_PRINT(m_command_name, level); - } - else if(level+1 == log_space::get_set_log_detalisation_level()) - { - LOG_PRINT(" -->>" << m_command_name, level); - } - else if(level+2 == log_space::get_set_log_detalisation_level()) - { - LOG_PRINT(" -->>" << m_command_name << "\n" << StorageNamed::xml::get_t_as_xml(m_ref_arg), level); - } - } - - virtual ~notify_activity_printer() - { - if(m_is_notify_mode) - { - if(m_level+1 == log_space::get_set_log_detalisation_level()) - { - LOG_PRINT(" <<--" << m_command_name, m_level); - } - } - } -protected: - std::string m_command_name; - A& m_ref_arg; - int m_level; - bool m_is_notify_mode; -}; - -template -class command_activity_printer: public notify_activity_printer
-{ -public: - command_activity_printer(int level, A& arg, R& rsp):notify_activity_printer(level, arg, false), m_ref_rsp(rsp) - { - } - - virtual ~command_activity_printer() - { - if(m_level+1 == log_space::get_set_log_detalisation_level()) - { - LOG_PRINT(" <<--" << m_command_name, m_level); - } - else if(m_level+2 == log_space::get_set_log_detalisation_level()) - { - LOG_PRINT(" <<--" << m_command_name << "\n" << StorageNamed::trace_as_xml(m_ref_rsp), m_level); - } - } -private: - R& m_ref_rsp; -}; - -template -activity_printer_base* create_activity_printer(int level, A& arg, R& rsp) -{ - return new command_activity_printer(level, arg, rsp); -} - -template -activity_printer_base* create_activity_printer(int level, A& arg) -{ - return new notify_activity_printer(level, arg); -} - -} - -#define PRINT_COMMAND_ACTIVITY(level) boost::shared_ptr local_activity_printer(create_activity_printer(level, in_struct, out_struct)); -#define PRINT_NOTIFY_ACTIVITY(level) boost::shared_ptr local_activity_printer(create_activity_printer(level, in_struct)); - -#define PRINT_ACTIVITY(level) \ -{std::string some_str = typeid(in_struct).name(); \ - some_str.erase(0, 7); \ - some_str.erase(some_str.size()-10, some_str.size()-1); \ - LOG_PRINT(some_str, level);} - -} - diff --git a/contrib/epee/include/storages/crypted_storage.h b/contrib/epee/include/storages/crypted_storage.h deleted file mode 100644 index d6e6edcba8..0000000000 --- a/contrib/epee/include/storages/crypted_storage.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _CRYPTED_STORAGE_H_ -#define _CRYPTED_STORAGE_H_ - -#include "cryptopp_helper.h" - -namespace epee -{ -template -class crypted_storage: public t_base_storage -{ -public: - size_t PackToSolidBuffer(std::string& targetObj) - { - size_t res = t_base_storage::PackToSolidBuffer(targetObj); - if(res <= 0) - return res; - - if(!crypt_provider::encrypt(targetObj, t_key_provider::get_storage_default_key())) - return 0; - - return targetObj.size(); - } - - size_t LoadFromSolidBuffer(const std::string& pTargetObj) - { - std::string buff_to_decrypt = pTargetObj; - if(crypt_provider::decrypt(buff_to_decrypt, t_key_provider::get_storage_default_key())) - return t_base_storage::LoadFromSolidBuffer(buff_to_decrypt); - - return 0; - } -}; -} - -#endif //_CRYPTED_STORAGE_H_ \ No newline at end of file diff --git a/contrib/epee/include/storages/gzipped_inmemstorage.h b/contrib/epee/include/storages/gzipped_inmemstorage.h deleted file mode 100644 index 5c53fffa7b..0000000000 --- a/contrib/epee/include/storages/gzipped_inmemstorage.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _GZIPPED_INMEMSTORAGE_H_ -#define _GZIPPED_INMEMSTORAGE_H_ - -#include "zlib_helper.h" -namespace epee -{ -namespace StorageNamed -{ - - template - class gziped_storage: public t_base_storage - { - public: - size_t PackToSolidBuffer(std::string& targetObj) - { - size_t res = t_base_storage::PackToSolidBuffer(targetObj); - if(res <= 0) - return res; - - if(!zlib_helper::pack(targetObj)) - return 0; - - return targetObj.size(); - } - - size_t LoadFromSolidBuffer(const std::string& pTargetObj) - { - std::string buff_to_ungzip = pTargetObj; - if(zlib_helper::unpack(buff_to_ungzip)) - return t_base_storage::LoadFromSolidBuffer(buff_to_ungzip); - - return 0; - } - - private: - }; - -} -} - -#endif \ No newline at end of file diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h deleted file mode 100644 index 00ee8a4ad3..0000000000 --- a/contrib/epee/include/storages/http_abstract_invoke.h +++ /dev/null @@ -1,126 +0,0 @@ - -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "portable_storage_template_helper.h" -#include "net/http_base.h" -#include "net/http_server_handlers_map2.h" - -namespace epee -{ - namespace net_utils - { - template - bool invoke_http_json_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET") - { - std::string req_param; - if(!serialization::store_t_to_json(out_struct, req_param)) - return false; - - const http::http_response_info* pri = NULL; - if(!invoke_request(url, transport, timeout, &pri, method, req_param)) - { - LOG_PRINT_L1("Failed to invoke http request to " << url); - return false; - } - - if(!pri->m_response_code) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); - return false; - } - - if(pri->m_response_code != 200) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); - return false; - } - - return serialization::load_t_from_json(result_struct, pri->m_body); - } - - - - template - bool invoke_http_bin_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET") - { - std::string req_param; - if(!serialization::store_t_to_binary(out_struct, req_param)) - return false; - - const http::http_response_info* pri = NULL; - if(!invoke_request(url, transport, timeout, &pri, method, req_param)) - { - LOG_PRINT_L1("Failed to invoke http request to " << url); - return false; - } - - if(!pri->m_response_code) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); - return false; - } - - if(pri->m_response_code != 200) - { - LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); - return false; - } - - return serialization::load_t_from_binary(result_struct, pri->m_body); - } - - template - bool invoke_http_json_rpc(const std::string& url, const std::string& method_name, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") - { - epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); - req_t.jsonrpc = "2.0"; - req_t.id = req_id; - req_t.method = method_name; - req_t.params = out_struct; - epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); - if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method)) - { - return false; - } - if(resp_t.error.code || resp_t.error.message.size()) - { - LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); - return false; - } - result_struct = resp_t.result; - return true; - } - - template - bool invoke_http_json_rpc(const std::string& url, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") - { - return invoke_http_json_rpc(url, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); - } - - } -} diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h deleted file mode 100644 index da12c10607..0000000000 --- a/contrib/epee/include/storages/levin_abstract_invoke2.h +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "portable_storage_template_helper.h" -#include -#include "net/levin_base.h" - -namespace epee -{ - namespace net_utils - { - template - bool invoke_remote_command2(int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) - { - if(!transport.is_connected()) - return false; - - serialization::portable_storage stg; - out_struct.store(stg); - std::string buff_to_send, buff_to_recv; - stg.store_to_binary(buff_to_send); - - int res = transport.invoke(command, buff_to_send, buff_to_recv); - if( res <=0 ) - { - LOG_PRINT_RED("Failed to invoke command " << command << " return code " << res, LOG_LEVEL_1); - return false; - } - serialization::portable_storage stg_ret; - if(!stg_ret.load_from_binary(buff_to_recv)) - { - LOG_ERROR("Failed to load_from_binary on command " << command); - return false; - } - result_struct.load(stg_ret); - return true; - } - - template - bool notify_remote_command2(int command, const t_arg& out_struct, t_transport& transport) - { - if(!transport.is_connected()) - return false; - - serialization::portable_storage stg; - out_struct.store(&stg); - std::string buff_to_send; - stg.store_to_binary(buff_to_send); - - int res = transport.notify(command, buff_to_send); - if(res <=0 ) - { - LOG_ERROR("Failed to notify command " << command << " return code " << res); - return false; - } - return true; - } - - template - bool invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) - { - - typename serialization::portable_storage stg; - out_struct.store(stg); - std::string buff_to_send, buff_to_recv; - stg.store_to_binary(buff_to_send); - - int res = transport.invoke(command, buff_to_send, buff_to_recv, conn_id); - if( res <=0 ) - { - LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); - return false; - } - typename serialization::portable_storage stg_ret; - if(!stg_ret.load_from_binary(buff_to_recv)) - { - LOG_ERROR("Failed to load_from_binary on command " << command); - return false; - } - result_struct.load(stg_ret); - - return true; - } - - template - bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, callback_t cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) - { - typename serialization::portable_storage stg; - const_cast(out_struct).store(stg);//TODO: add true const support to searilzation - std::string buff_to_send, buff_to_recv; - stg.store_to_binary(buff_to_send); - - int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool - { - t_result result_struct = AUTO_VAL_INIT(result_struct); - if( code <=0 ) - { - LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code); - cb(code, result_struct, context); - return false; - } - serialization::portable_storage stg_ret; - if(!stg_ret.load_from_binary(buff)) - { - LOG_ERROR("Failed to load_from_binary on command " << command); - cb(LEVIN_ERROR_FORMAT, result_struct, context); - return false; - } - result_struct.load(stg_ret); - - cb(code, result_struct, context); - return true; - }, inv_timeout); - if( res <=0 ) - { - LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); - return false; - } - return true; - } - - template - bool notify_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport) - { - - serialization::portable_storage stg; - out_struct.store(stg); - std::string buff_to_send, buff_to_recv; - stg.store_to_binary(buff_to_send); - - int res = transport.notify(command, buff_to_send, conn_id); - if(res <=0 ) - { - LOG_PRINT_RED_L0("Failed to notify command " << command << " return code " << res); - return false; - } - return true; - } - //---------------------------------------------------------------------------------------------------- - //---------------------------------------------------------------------------------------------------- - template - int buff_to_t_adapter(int command, const std::string& in_buff, std::string& buff_out, callback_t cb, t_context& context ) - { - serialization::portable_storage strg; - if(!strg.load_from_binary(in_buff)) - { - LOG_ERROR("Failed to load_from_binary in command " << command); - return -1; - } - boost::value_initialized in_struct; - boost::value_initialized out_struct; - - static_cast(in_struct).load(strg); - int res = cb(command, static_cast(in_struct), static_cast(out_struct), context); - serialization::portable_storage strg_out; - static_cast(out_struct).store(strg_out); - - if(!strg_out.store_to_binary(buff_out)) - { - LOG_ERROR("Failed to store_to_binary in command" << command); - return -1; - } - - return res; - }; - - template - int buff_to_t_adapter(t_owner* powner, int command, const std::string& in_buff, callback_t cb, t_context& context) - { - serialization::portable_storage strg; - if(!strg.load_from_binary(in_buff)) - { - LOG_ERROR("Failed to load_from_binary in notify " << command); - return -1; - } - boost::value_initialized in_struct; - static_cast(in_struct).load(strg); - return cb(command, in_struct, context); - }; - -#define CHAIN_LEVIN_INVOKE_MAP2(context_type) \ - int invoke(int command, const std::string& in_buff, std::string& buff_out, context_type& context) \ - { \ - bool handled = false; \ - return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ - } - -#define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \ - int notify(int command, const std::string& in_buff, context_type& context) \ - { \ - bool handled = false; std::string fake_str;\ - return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ - } - - -#define CHAIN_LEVIN_INVOKE_MAP() \ - int invoke(int command, const std::string& in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \ - { \ - bool handled = false; \ - return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ - } - -#define CHAIN_LEVIN_NOTIFY_MAP() \ - int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ - { \ - bool handled = false; std::string fake_str;\ - return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ - } - -#define CHAIN_LEVIN_NOTIFY_STUB() \ - int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ - { \ - return -1; \ - } - -#define BEGIN_INVOKE_MAP2(owner_type) \ - template int handle_invoke_map(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, t_context& context, bool& handled) \ - { \ - typedef owner_type internal_owner_type_name; - -#define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \ - if(!is_notify && command_id == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} - -#define HANDLE_INVOKE_T2(COMMAND, func) \ - if(!is_notify && COMMAND::ID == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter(command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} - - -#define HANDLE_NOTIFY2(command_id, func, type_name_in) \ - if(is_notify && command_id == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} - -#define HANDLE_NOTIFY_T2(NOTIFY, func) \ - if(is_notify && NOTIFY::ID == command) \ - {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} - - -#define CHAIN_INVOKE_MAP2(func) \ - { \ - int res = func(is_notify, command, in_buff, buff_out, context, handled); \ - if(handled) \ - return res; \ - } - -#define CHAIN_INVOKE_MAP_TO_OBJ2(obj) \ - { \ - int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, context, handled); \ - if(handled) \ - return res; \ - } - -#define CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(obj, context_type) \ - { \ - int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, static_cast(context), handled); \ - if(handled) return res; \ - } - - -#define END_INVOKE_MAP2() \ - LOG_ERROR("Unkonown command:" << command); \ - return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \ - } - } -} - diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h deleted file mode 100644 index baafb56230..0000000000 --- a/contrib/epee/include/storages/parserse_base_utils.h +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 misc_utils -{ - namespace parse - { - inline std::string transform_to_escape_sequence(const std::string& src) - { - //std::stringstream res; - std::string res; - for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it) - { - switch(*it) - { - case '\b': //Backspace (ascii code 08) - res+="\\b"; break; - case '\f': //Form feed (ascii code 0C) - res+="\\f"; break; - case '\n': //New line - res+="\\n"; break; - case '\r': //Carriage return - res+="\\r"; break; - case '\t': //Tab - res+="\\t"; break; - case '\v': //Vertical tab - res+="\\v"; break; - //case '\'': //Apostrophe or single quote - // res+="\\'"; break; - case '"': //Double quote - res+="\\\""; break; - case '\\': //Backslash caracter - res+="\\\\"; break; - case '/': //Backslash caracter - res+="\\/"; break; - default: - res.push_back(*it); - } - } - return res; - } - /* - - \b Backspace (ascii code 08) - \f Form feed (ascii code 0C) - \n New line - \r Carriage return - \t Tab - \v Vertical tab - \' Apostrophe or single quote - \" Double quote - \\ Backslash character - - */ - inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) - { - val.clear(); - bool escape_mode = false; - std::string::const_iterator it = star_end_string; - ++it; - for(;it != buf_end;it++) - { - if(escape_mode/*prev_ch == '\\'*/) - { - switch(*it) - { - case 'b': //Backspace (ascii code 08) - val.push_back(0x08);break; - case 'f': //Form feed (ascii code 0C) - val.push_back(0x0C);break; - case 'n': //New line - val.push_back('\n');break; - case 'r': //Carriage return - val.push_back('\r');break; - case 't': //Tab - val.push_back('\t');break; - case 'v': //Vertical tab - val.push_back('\v');break; - case '\'': //Apostrophe or single quote - val.push_back('\'');break; - case '"': //Double quote - val.push_back('"');break; - case '\\': //Backslash character - val.push_back('\\');break; - case '/': //Slash character - val.push_back('/');break; - default: - val.push_back(*it); - LOG_PRINT_L0("Unknown escape sequence :\"\\" << *it << "\""); - } - escape_mode = false; - }else if(*it == '"') - { - star_end_string = it; - return; - }else if(*it == '\\') - { - escape_mode = true; - } - else - { - val.push_back(*it); - } - } - ASSERT_MES_AND_THROW("Failed to match string in json entry: " << std::string(star_end_string, buf_end)); - } - inline bool match_string(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) - { - try - { - - match_string2(star_end_string, buf_end, val); - return true; - } - catch(...) - { - return false; - } - } - inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val, bool& is_float_val, bool& is_signed_val) - { - val.clear(); - is_float_val = false; - for(std::string::const_iterator it = star_end_string;it != buf_end;it++) - { - if(isdigit(*it) || (it == star_end_string && *it == '-') || (val.size() && *it == '.' ) || (is_float_val && (*it == 'e' || *it == 'E' || *it == '-' || *it == '+' )) ) - { - if(!val.size() && *it == '-') - is_signed_val = true; - if(*it == '.' ) - is_float_val = true; - val.push_back(*it); - } - else - { - if(val.size()) - { - star_end_string = --it; - return; - } - else - ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); - } - } - ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); - } - inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) - { - try - { - bool is_v_float = false;bool is_signed_val = false; - match_number2(star_end_string, buf_end, val, is_v_float, is_signed_val); - return !is_v_float; - } - catch(...) - { - return false; - } - } - inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) - { - val.clear(); - - for(std::string::const_iterator it = star_end_string;it != buf_end;it++) - { - if(!isalpha(*it)) - { - val.assign(star_end_string, it); - if(val.size()) - { - star_end_string = --it; - return; - }else - ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); - } - } - ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); - } - inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) - { - try - { - match_word2(star_end_string, buf_end, val); - return true; - } - catch(...) - { - return false; - } - } - inline bool match_word_with_extrasymb(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) - { - val.clear(); - - for(std::string::const_iterator it = star_end_string;it != buf_end;it++) - { - if(!isalnum(*it) && *it != '-' && *it != '_') - { - val.assign(star_end_string, it); - if(val.size()) - { - star_end_string = --it; - return true; - }else - return false; - } - } - return false; - } - inline bool match_word_til_equal_mark(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string::const_iterator& word_end) - { - word_end = star_end_string; - - for(std::string::const_iterator it = star_end_string;it != buf_end;it++) - { - if(isspace(*it)) - { - - continue; - }else if( *it == '=' ) - { - star_end_string = it; - word_end = it; - return true; - } - } - return false; - } - } -} -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h deleted file mode 100644 index 87c6edb2b9..0000000000 --- a/contrib/epee/include/storages/portable_storage.h +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_language.h" -#include "portable_storage_base.h" -#include "portable_storage_to_bin.h" -#include "portable_storage_from_bin.h" -#include "portable_storage_to_json.h" -#include "portable_storage_from_json.h" -#include "portable_storage_val_converters.h" - -namespace epee -{ - namespace serialization - { - /************************************************************************/ - /* */ - /************************************************************************/ - class portable_storage - { - public: - typedef epee::serialization::hsection hsection; - typedef epee::serialization::harray harray; - typedef storage_entry meta_entry; - - portable_storage(){} - virtual ~portable_storage(){} - hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false); - template - bool get_value(const std::string& value_name, t_value& val, hsection hparent_section); - bool get_value(const std::string& value_name, storage_entry& val, hsection hparent_section); - template - bool set_value(const std::string& value_name, const t_value& target, hsection hparent_section); - - //serial access for arrays of values -------------------------------------- - //values - template - harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section); - template - bool get_next_value(harray hval_array, t_value& target); - template - harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section); - template - bool insert_next_value(harray hval_array, const t_value& target); - //sections - harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); - bool get_next_section(harray hSecArray, hsection& h_child_section); - harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section); - bool insert_next_section(harray hSecArray, hsection& hinserted_childsection); - //------------------------------------------------------------------------ - //delete entry (section, value or array) - bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr); - - //------------------------------------------------------------------------------- - bool store_to_binary(binarybuffer& target); - bool load_from_binary(const binarybuffer& target); - template - bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); - bool dump_as_json(std::string& targetObj, size_t indent = 0, bool insert_newlines = true); - bool load_from_json(const std::string& source); - - private: - section m_root; - hsection get_root_section() {return &m_root;} - storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection); - template - storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry); - - hsection insert_new_section(const std::string& pentry_name, hsection psection); - -#pragma pack(push) -#pragma pack(1) - struct storage_block_header - { - uint32_t m_signature_a; - uint32_t m_signature_b; - uint8_t m_ver; - }; -#pragma pack(pop) - }; - inline - bool portable_storage::dump_as_json(std::string& buff, size_t indent, bool insert_newlines) - { - TRY_ENTRY(); - std::stringstream ss; - epee::serialization::dump_as_json(ss, m_root, indent, insert_newlines); - buff = ss.str(); - return true; - CATCH_ENTRY("portable_storage::dump_as_json", false) - } - inline - bool portable_storage::load_from_json(const std::string& source) - { - TRY_ENTRY(); - return json::load_from_json(source, *this); - CATCH_ENTRY("portable_storage::load_from_json", false) - } - - template - bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name) - { - return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format - } - - inline - bool portable_storage::store_to_binary(binarybuffer& target) - { - 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_ver = PORTABLE_STORAGE_FORMAT_VER; - ss.write((const char*)&sbh, sizeof(storage_block_header)); - pack_entry_to_buff(ss, m_root); - target = ss.str(); - return true; - CATCH_ENTRY("portable_storage::store_to_binary", false) - } - inline - bool portable_storage::load_from_binary(const binarybuffer& source) - { - m_root.m_entries.clear(); - if(source.size() < sizeof(storage_block_header)) - { - LOG_WARNING("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" - << sizeof(storage_block_header), LOG_LEVEL_2) - 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 - ) - { - LOG_WARNING("portable_storage: wrong binary format - signature missmatch", LOG_LEVEL_2); - return false; - } - if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER) - { - LOG_WARNING("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver, LOG_LEVEL_2); - return false; - } - TRY_ENTRY(); - throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header)); - buf_reader.read(m_root); - return true;//TODO: - CATCH_ENTRY("portable_storage::load_from_binary", false); - } - //--------------------------------------------------------------------------------------------------------------- - inline - hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist) - { - TRY_ENTRY(); - hparent_section = hparent_section ? hparent_section:&m_root; - storage_entry* pentry = find_storage_entry(section_name, hparent_section); - if(!pentry) - { - if(!create_if_notexist) - return nullptr; - return insert_new_section(section_name, hparent_section); - } - CHECK_AND_ASSERT(pentry , nullptr); - //check that section_entry we find is real "CSSection" - if(pentry->type() != typeid(section)) - { - if(create_if_notexist) - *pentry = storage_entry(section());//replace - else - return nullptr; - } - return &boost::get
(*pentry); - CATCH_ENTRY("portable_storage::open_section", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - template - struct get_value_visitor: boost::static_visitor - { - to_type& m_target; - get_value_visitor(to_type& target):m_target(target){} - template - void operator()(const from_type& v){convert_t(v, m_target);} - }; - - template - bool portable_storage::get_value(const std::string& value_name, t_value& val, hsection hparent_section) - { - BOOST_MPL_ASSERT(( boost::mpl::contains )); - //TRY_ENTRY(); - if(!hparent_section) hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(value_name, hparent_section); - if(!pentry) - return false; - - get_value_visitor gvv(val); - boost::apply_visitor(gvv, *pentry); - return true; - //CATCH_ENTRY("portable_storage::template<>get_value", false); - } - //--------------------------------------------------------------------------------------------------------------- - inline - bool portable_storage::get_value(const std::string& value_name, storage_entry& val, hsection hparent_section) - { - //TRY_ENTRY(); - if(!hparent_section) hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(value_name, hparent_section); - if(!pentry) - return false; - - val = *pentry; - return true; - //CATCH_ENTRY("portable_storage::template<>get_value", false); - } - //--------------------------------------------------------------------------------------------------------------- - template - bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section) - { - BOOST_MPL_ASSERT(( boost::mpl::contains::type, t_value> )); - TRY_ENTRY(); - if(!hparent_section) - hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(value_name, hparent_section); - if(!pentry) - { - pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, v); - if(!pentry) - return false; - return true; - } - *pentry = storage_entry(v); - return true; - CATCH_ENTRY("portable_storage::template<>set_value", false); - } - //--------------------------------------------------------------------------------------------------------------- - inline - storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection) - { - TRY_ENTRY(); - CHECK_AND_ASSERT(psection, nullptr); - auto it = psection->m_entries.find(pentry_name); - if(it == psection->m_entries.end()) - return nullptr; - - return &it->second; - CATCH_ENTRY("portable_storage::find_storage_entry", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - template - storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry) - { - TRY_ENTRY(); - CHECK_AND_ASSERT(psection, nullptr); - auto ins_res = psection->m_entries.insert(std::pair(pentry_name, entry)); - return &ins_res.first->second; - CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - inline - hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection) - { - TRY_ENTRY(); - storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section()); - if(!pse) return nullptr; - return &boost::get
(*pse); - CATCH_ENTRY("portable_storage::insert_new_section", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - template - struct get_first_value_visitor: boost::static_visitor - { - to_type& m_target; - get_first_value_visitor(to_type& target):m_target(target){} - template - bool operator()(const array_entry_t& a) - { - const from_type* pv = a.get_first_val(); - if(!pv) - return false; - convert_t(*pv, m_target); - return true; - } - }; - //--------------------------------------------------------------------------------------------------------------- - template - harray portable_storage::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section) - { - BOOST_MPL_ASSERT(( boost::mpl::contains )); - //TRY_ENTRY(); - if(!hparent_section) hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(value_name, hparent_section); - if(!pentry) - return nullptr; - if(pentry->type() != typeid(array_entry)) - return nullptr; - array_entry& ar_entry = boost::get(*pentry); - - get_first_value_visitor gfv(target); - if(!boost::apply_visitor(gfv, ar_entry)) - return nullptr; - return &ar_entry; - //CATCH_ENTRY("portable_storage::get_first_value", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - template - struct get_next_value_visitor: boost::static_visitor - { - to_type& m_target; - get_next_value_visitor(to_type& target):m_target(target){} - template - bool operator()(const array_entry_t& a) - { - //TODO: optimize code here: work without get_next_val function - const from_type* pv = a.get_next_val(); - if(!pv) - return false; - convert_t(*pv, m_target); - return true; - } - }; - - - template - bool portable_storage::get_next_value(harray hval_array, t_value& target) - { - BOOST_MPL_ASSERT(( boost::mpl::contains )); - //TRY_ENTRY(); - CHECK_AND_ASSERT(hval_array, false); - array_entry& ar_entry = *hval_array; - get_next_value_visitor gnv(target); - if(!boost::apply_visitor(gnv, ar_entry)) - return false; - return true; - //CATCH_ENTRY("portable_storage::get_next_value", false); - } - //--------------------------------------------------------------------------------------------------------------- - template - harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section) - { - TRY_ENTRY(); - if(!hparent_section) hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(value_name, hparent_section); - if(!pentry) - { - pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t())); - if(!pentry) - return nullptr; - } - if(pentry->type() != typeid(array_entry)) - *pentry = storage_entry(array_entry(array_entry_t())); - - array_entry& arr = boost::get(*pentry); - if(arr.type() != typeid(array_entry_t)) - arr = array_entry(array_entry_t()); - - array_entry_t& arr_typed = boost::get >(arr); - arr_typed.insert_first_val(target); - return &arr; - CATCH_ENTRY("portable_storage::insert_first_value", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - template - bool portable_storage::insert_next_value(harray hval_array, const t_value& target) - { - TRY_ENTRY(); - CHECK_AND_ASSERT(hval_array, false); - - CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t), - false, "unexpected type in insert_next_value: " << typeid(array_entry_t).name()); - - array_entry_t& arr_typed = boost::get >(*hval_array); - arr_typed.insert_next_value(target); - return true; - CATCH_ENTRY("portable_storage::insert_next_value", false); - } - //--------------------------------------------------------------------------------------------------------------- - //sections - inline - harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section) - { - TRY_ENTRY(); - if(!hparent_section) hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(sec_name, hparent_section); - if(!pentry) - return nullptr; - if(pentry->type() != typeid(array_entry)) - return nullptr; - array_entry& ar_entry = boost::get(*pentry); - if(ar_entry.type() != typeid(array_entry_t
)) - return nullptr; - array_entry_t
& sec_array = boost::get>(ar_entry); - section* psec = sec_array.get_first_val(); - if(!psec) - return nullptr; - h_child_section = psec; - return &ar_entry; - CATCH_ENTRY("portable_storage::get_first_section", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - inline - bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section) - { - TRY_ENTRY(); - CHECK_AND_ASSERT(hsec_array, false); - if(hsec_array->type() != typeid(array_entry_t
)) - return nullptr; - array_entry_t
& sec_array = boost::get>(*hsec_array); - h_child_section = sec_array.get_next_val(); - if(!h_child_section) - return false; - return true; - CATCH_ENTRY("portable_storage::get_next_section", false); - } - //--------------------------------------------------------------------------------------------------------------- - inline - harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section) - { - TRY_ENTRY(); - if(!hparent_section) hparent_section = &m_root; - storage_entry* pentry = find_storage_entry(sec_name, hparent_section); - if(!pentry) - { - pentry = insert_new_entry_get_storage_entry(sec_name, hparent_section, array_entry(array_entry_t
())); - if(!pentry) - return nullptr; - } - if(pentry->type() != typeid(array_entry)) - *pentry = storage_entry(array_entry(array_entry_t
())); - - array_entry& ar_entry = boost::get(*pentry); - if(ar_entry.type() != typeid(array_entry_t
)) - ar_entry = array_entry(array_entry_t
()); - - array_entry_t
& sec_array = boost::get>(ar_entry); - hinserted_childsection = &sec_array.insert_first_val(section()); - return &ar_entry; - CATCH_ENTRY("portable_storage::insert_first_section", nullptr); - } - //--------------------------------------------------------------------------------------------------------------- - inline - bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection) - { - TRY_ENTRY(); - CHECK_AND_ASSERT(hsec_array, false); - CHECK_AND_ASSERT_MES(hsec_array->type() == typeid(array_entry_t
), - false, "unexpected type(not 'section') in insert_next_section, type: " << hsec_array->type().name()); - - array_entry_t
& sec_array = boost::get>(*hsec_array); - hinserted_childsection = &sec_array.insert_next_value(section()); - return true; - CATCH_ENTRY("portable_storage::insert_next_section", false); - } - //--------------------------------------------------------------------------------------------------------------- - } -} diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h deleted file mode 100644 index 3f16375388..0000000000 --- a/contrib/epee/include/storages/portable_storage_base.h +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#define PORTABLE_STORAGE_SIGNATUREA 0x01011101 -#define PORTABLE_STORAGE_SIGNATUREB 0x01020101 // bender's nightmare -#define PORTABLE_STORAGE_FORMAT_VER 1 - -#define PORTABLE_RAW_SIZE_MARK_MASK 0x03 -#define PORTABLE_RAW_SIZE_MARK_BYTE 0 -#define PORTABLE_RAW_SIZE_MARK_WORD 1 -#define PORTABLE_RAW_SIZE_MARK_DWORD 2 -#define PORTABLE_RAW_SIZE_MARK_INT64 3 - -#ifndef MAX_STRING_LEN_POSSIBLE -#define MAX_STRING_LEN_POSSIBLE 2000000000 //do not let string be so big -#endif - -//data types -#define SERIALIZE_TYPE_INT64 1 -#define SERIALIZE_TYPE_INT32 2 -#define SERIALIZE_TYPE_INT16 3 -#define SERIALIZE_TYPE_INT8 4 -#define SERIALIZE_TYPE_UINT64 5 -#define SERIALIZE_TYPE_UINT32 6 -#define SERIALIZE_TYPE_UINT16 7 -#define SERIALIZE_TYPE_UINT8 8 -#define SERIALIZE_TYPE_DUOBLE 9 -#define SERIALIZE_TYPE_STRING 10 -#define SERIALIZE_TYPE_BOOL 11 -#define SERIALIZE_TYPE_OBJECT 12 -#define SERIALIZE_TYPE_ARRAY 13 - -#define SERIALIZE_FLAG_ARRAY 0x80 - - -namespace epee -{ - namespace serialization - { - struct section; - - /************************************************************************/ - /* */ - /************************************************************************/ - template - struct array_entry_t - { - array_entry_t():m_it(m_array.end()){} - - const t_entry_type* get_first_val() const - { - m_it = m_array.begin(); - return get_next_val(); - } - - t_entry_type* get_first_val() - { - m_it = m_array.begin(); - return get_next_val(); - } - - - const t_entry_type* get_next_val() const - { - if(m_it == m_array.end()) - return nullptr; - return &(*(m_it++)); - } - - t_entry_type* get_next_val() - { - if(m_it == m_array.end()) - return nullptr; - return (t_entry_type*)&(*(m_it++));//fuckoff - } - - t_entry_type& insert_first_val(const t_entry_type& v) - { - m_array.clear(); - m_it = m_array.end(); - return insert_next_value(v); - } - - t_entry_type& insert_next_value(const t_entry_type& v) - { - m_array.push_back(v); - return m_array.back(); - } - - std::list m_array; - mutable typename std::list::const_iterator m_it; - }; - - - typedef boost::make_recursive_variant< - array_entry_t
, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t, - array_entry_t
, - array_entry_t - >::type array_entry; - - typedef boost::variant storage_entry; - - typedef std::string binarybuffer;//it's ok - - /************************************************************************/ - /* */ - /************************************************************************/ - struct section - { - std::map m_entries; - }; - - //handle-like aliases - typedef section* hsection; - typedef array_entry* harray; - } -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h deleted file mode 100644 index e9b7e2e6f3..0000000000 --- a/contrib/epee/include/storages/portable_storage_from_bin.h +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_language.h" -#include "portable_storage_base.h" - -#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT -#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT -#else -#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 -#endif - -namespace epee -{ - namespace serialization - { - struct throwable_buffer_reader - { - throwable_buffer_reader(const void* ptr, size_t sz); - void read(void* target, size_t count); - void read_sec_name(std::string& sce_name); - template - void read(t_pod_type& pod_val); - template - t_type read(); - template - storage_entry read_ae(); - storage_entry load_storage_array_entry(uint8_t type); - size_t read_varint(); - template - storage_entry read_se(); - storage_entry load_storage_entry(); - void read(section& sec); - void read(std::string& str); - private: - struct recursuion_limitation_guard - { - size_t& m_counter_ref; - recursuion_limitation_guard(size_t& counter):m_counter_ref(counter) - { - ++m_counter_ref; - CHECK_AND_ASSERT_THROW_MES(m_counter_ref < EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL, "Wrong blob data in portable storage: recursion limitation (" << EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL << ") exceeded"); - } - ~recursuion_limitation_guard() - { - CHECK_AND_ASSERT_THROW_MES(m_counter_ref != 0, "Internal error: m_counter_ref == 0 while ~recursuion_limitation_guard()"); - --m_counter_ref; - } - }; -#define RECURSION_LIMITATION() recursuion_limitation_guard rl(m_recursion_count) - - const uint8_t* m_ptr; - size_t m_count; - size_t m_recursion_count; - }; - - inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) - { - if(!ptr) - throw std::runtime_error("throwable_buffer_reader: ptr==nullptr"); - if(!sz) - throw std::runtime_error("throwable_buffer_reader: sz==0"); - m_ptr = (uint8_t*)ptr; - m_count = sz; - m_recursion_count = 0; - } - inline - void throwable_buffer_reader::read(void* target, size_t count) - { - RECURSION_LIMITATION(); - CHECK_AND_ASSERT_THROW_MES(m_count >= count, " attempt to read " << count << " bytes from buffer with " << m_count << " bytes remained"); - memcpy(target, m_ptr, count); - m_ptr += count; - m_count -= count; - } - inline - void throwable_buffer_reader::read_sec_name(std::string& sce_name) - { - RECURSION_LIMITATION(); - uint8_t name_len = 0; - read(name_len); - sce_name.resize(name_len); - read((void*)sce_name.data(), name_len); - } - - template - void throwable_buffer_reader::read(t_pod_type& pod_val) - { - RECURSION_LIMITATION(); - read(&pod_val, sizeof(pod_val)); - } - - template - t_type throwable_buffer_reader::read() - { - RECURSION_LIMITATION(); - t_type v; - read(v); - return v; - } - - - template - storage_entry throwable_buffer_reader::read_ae() - { - RECURSION_LIMITATION(); - //for pod types - array_entry_t sa; - size_t size = read_varint(); - //TODO: add some optimization here later - while(size--) - sa.m_array.push_back(read()); - return storage_entry(array_entry(sa)); - } - - inline - storage_entry throwable_buffer_reader::load_storage_array_entry(uint8_t type) - { - RECURSION_LIMITATION(); - type &= ~SERIALIZE_FLAG_ARRAY; - switch(type) - { - case SERIALIZE_TYPE_INT64: return read_ae(); - case SERIALIZE_TYPE_INT32: return read_ae(); - case SERIALIZE_TYPE_INT16: return read_ae(); - case SERIALIZE_TYPE_INT8: return read_ae(); - case SERIALIZE_TYPE_UINT64: return read_ae(); - case SERIALIZE_TYPE_UINT32: return read_ae(); - case SERIALIZE_TYPE_UINT16: return read_ae(); - case SERIALIZE_TYPE_UINT8: return read_ae(); - case SERIALIZE_TYPE_DUOBLE: return read_ae(); - case SERIALIZE_TYPE_BOOL: return read_ae(); - case SERIALIZE_TYPE_STRING: return read_ae(); - case SERIALIZE_TYPE_OBJECT: return read_ae
(); - case SERIALIZE_TYPE_ARRAY: return read_ae(); - default: - CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << type); - } - } - - inline - size_t throwable_buffer_reader::read_varint() - { - RECURSION_LIMITATION(); - CHECK_AND_ASSERT_THROW_MES(m_count >= 1, "empty buff, expected place for varint"); - size_t v = 0; - uint8_t size_mask = (*(uint8_t*)m_ptr) &PORTABLE_RAW_SIZE_MARK_MASK; - switch (size_mask) - { - case PORTABLE_RAW_SIZE_MARK_BYTE: v = read();break; - case PORTABLE_RAW_SIZE_MARK_WORD: v = read();break; - case PORTABLE_RAW_SIZE_MARK_DWORD: v = read();break; - case PORTABLE_RAW_SIZE_MARK_INT64: v = read();break; - default: - CHECK_AND_ASSERT_THROW_MES(false, "unknown varint size_mask = " << size_mask); - } - v >>= 2; - return v; - } - - template - storage_entry throwable_buffer_reader::read_se() - { - RECURSION_LIMITATION(); - t_type v; - read(v); - return storage_entry(v); - } - - template<> - inline storage_entry throwable_buffer_reader::read_se() - { - RECURSION_LIMITATION(); - return storage_entry(read()); - } - - - template<> - inline storage_entry throwable_buffer_reader::read_se
() - { - RECURSION_LIMITATION(); - section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio - storage_entry se(s); - section& section_entry = boost::get
(se); - read(section_entry); - return se; - } - - template<> - inline storage_entry throwable_buffer_reader::read_se() - { - RECURSION_LIMITATION(); - uint8_t ent_type = 0; - read(ent_type); - CHECK_AND_ASSERT_THROW_MES(ent_type&SERIALIZE_FLAG_ARRAY, "wrong type sequenses"); - return load_storage_array_entry(ent_type); - } - - inline - storage_entry throwable_buffer_reader::load_storage_entry() - { - RECURSION_LIMITATION(); - uint8_t ent_type = 0; - read(ent_type); - if(ent_type&SERIALIZE_FLAG_ARRAY) - return load_storage_array_entry(ent_type); - - switch(ent_type) - { - case SERIALIZE_TYPE_INT64: return read_se(); - case SERIALIZE_TYPE_INT32: return read_se(); - case SERIALIZE_TYPE_INT16: return read_se(); - case SERIALIZE_TYPE_INT8: return read_se(); - case SERIALIZE_TYPE_UINT64: return read_se(); - case SERIALIZE_TYPE_UINT32: return read_se(); - case SERIALIZE_TYPE_UINT16: return read_se(); - case SERIALIZE_TYPE_UINT8: return read_se(); - case SERIALIZE_TYPE_DUOBLE: return read_se(); - case SERIALIZE_TYPE_BOOL: return read_se(); - case SERIALIZE_TYPE_STRING: return read_se(); - case SERIALIZE_TYPE_OBJECT: return read_se
(); - case SERIALIZE_TYPE_ARRAY: return read_se(); - default: - CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << ent_type); - } - } - inline - void throwable_buffer_reader::read(section& sec) - { - RECURSION_LIMITATION(); - sec.m_entries.clear(); - size_t count = read_varint(); - while(count--) - { - //read section name string - std::string sec_name; - read_sec_name(sec_name); - sec.m_entries.insert(std::make_pair(sec_name, load_storage_entry())); - } - } - inline - void throwable_buffer_reader::read(std::string& str) - { - RECURSION_LIMITATION(); - size_t len = read_varint(); - CHECK_AND_ASSERT_THROW_MES(len < MAX_STRING_LEN_POSSIBLE, "to big string len value in storage: " << len); - CHECK_AND_ASSERT_THROW_MES(m_count >= len, "string len count value " << len << " goes out of remain storage len " << m_count); - //do this manually to avoid double memory write in huge strings (first time at resize, second at read) - str.assign((const char*)m_ptr, len); - m_ptr+=len; - m_count -= len; - } - } -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h deleted file mode 100644 index 3ca61c02de..0000000000 --- a/contrib/epee/include/storages/portable_storage_from_json.h +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "parserse_base_utils.h" -#include "file_io_utils.h" - -namespace epee -{ - using namespace misc_utils::parse; - namespace serialization - { - namespace json - { -#define CHECK_ISSPACE() if(!isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));} - - /*inline void parse_error() - { - 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) - { - - std::string::const_iterator sub_element_start; - std::string name; - typename t_storage::harray h_array = nullptr; - enum match_state - { - match_state_lookup_for_section_start, - match_state_lookup_for_name, - match_state_waiting_separator, - match_state_wonder_after_separator, - match_state_wonder_after_value, - match_state_wonder_array, - match_state_array_after_value, - match_state_array_waiting_value, - match_state_error - }; - - enum array_mode - { - array_mode_undifined = 0, - array_mode_sections, - array_mode_string, - array_mode_numbers, - array_mode_booleans - }; - - match_state state = match_state_lookup_for_section_start; - array_mode array_md = array_mode_undifined; - std::string::const_iterator it = sec_buf_begin; - for(;it != buf_end;it++) - { - switch (state) - { - case match_state_lookup_for_section_start: - if(*it == '{') - state = match_state_lookup_for_name; - else CHECK_ISSPACE(); - break; - case match_state_lookup_for_name: - switch(*it) - { - case '"': - match_string2(it, buf_end, name); - state = match_state_waiting_separator; - break; - case '}': - //this is it! section ends here. - //seems that it is empty section - sec_buf_begin = it; - return; - default: - CHECK_ISSPACE(); - } - break; - case match_state_waiting_separator: - if(*it == ':') - state = match_state_wonder_after_separator; - else CHECK_ISSPACE(); - break; - case match_state_wonder_after_separator: - if(*it == '"') - {//just a named string value started - std::string val; - match_string2(it, buf_end, val); - //insert text value - stg.set_value(name, val, current_section); - state = match_state_wonder_after_value; - }else if (isdigit(*it) || *it == '-') - {//just a named number value started - std::string val; - bool is_v_float = false;bool is_signed = false; - match_number2(it, buf_end, val, is_v_float, is_signed); - if(!is_v_float) - { - if(is_signed) - { - int64_t nval = boost::lexical_cast(val); - stg.set_value(name, nval, current_section); - }else - { - uint64_t nval = boost::lexical_cast(val); - stg.set_value(name, nval, current_section); - } - }else - { - double nval = boost::lexical_cast(val); - stg.set_value(name, nval, current_section); - } - state = match_state_wonder_after_value; - }else if(isalpha(*it) ) - {// could be null, true or false - std::string word; - match_word2(it, buf_end, word); - if(boost::iequals(word, "null")) - { - state = match_state_wonder_after_value; - //just skip this, - }else if(boost::iequals(word, "true")) - { - stg.set_value(name, true, current_section); - state = match_state_wonder_after_value; - }else if(boost::iequals(word, "false")) - { - stg.set_value(name, false, current_section); - state = match_state_wonder_after_value; - }else ASSERT_MES_AND_THROW("Unknown value keyword " << word); - }else if(*it == '{') - { - //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); - state = match_state_wonder_after_value; - }else if(*it == '[') - {//array of something - state = match_state_wonder_array; - }else CHECK_ISSPACE(); - break; - case match_state_wonder_after_value: - if(*it == ',') - state = match_state_lookup_for_name; - else if(*it == '}') - { - //this is it! section ends here. - sec_buf_begin = it; - return; - }else CHECK_ISSPACE(); - break; - case match_state_wonder_array: - if(*it == '[') - { - ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry"); - //mean array of array - } - if(*it == '{') - { - //mean array of sections - 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); - state = match_state_array_after_value; - array_md = array_mode_sections; - }else if(*it == '"') - { - //mean array of strings - std::string val; - match_string2(it, buf_end, val); - h_array = stg.insert_first_value(name, val, current_section); - CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry"); - state = match_state_array_after_value; - array_md = array_mode_string; - }else if (isdigit(*it) || *it == '-') - {//array of numbers value started - std::string val; - bool is_v_float = false;bool is_signed_val = false; - 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); - 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); - h_array = stg.insert_first_value(name, nval, current_section); - CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); - } - - state = match_state_array_after_value; - array_md = array_mode_numbers; - }else if(*it == ']')//empty array - { - array_md = array_mode_undifined; - state = match_state_wonder_after_value; - }else if(isalpha(*it) ) - {// array of booleans - std::string word; - match_word2(it, buf_end, word); - if(boost::iequals(word, "true")) - { - h_array = stg.insert_first_value(name, true, current_section); - CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); - state = match_state_array_after_value; - array_md = array_mode_booleans; - }else if(boost::iequals(word, "false")) - { - h_array = stg.insert_first_value(name, false, current_section); - CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); - state = match_state_array_after_value; - array_md = array_mode_booleans; - - }else ASSERT_MES_AND_THROW("Unknown value keyword " << word) - }else CHECK_ISSPACE(); - break; - case match_state_array_after_value: - if(*it == ',') - state = match_state_array_waiting_value; - else if(*it == ']') - { - h_array = nullptr; - array_md = array_mode_undifined; - state = match_state_wonder_after_value; - }else CHECK_ISSPACE(); - break; - case match_state_array_waiting_value: - switch(array_md) - { - case array_mode_sections: - if(*it == '{') - { - 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); - state = match_state_array_after_value; - }else CHECK_ISSPACE(); - break; - case array_mode_string: - if(*it == '"') - { - std::string val; - match_string2(it, buf_end, val); - bool res = stg.insert_next_value(h_array, val); - CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values"); - state = match_state_array_after_value; - }else CHECK_ISSPACE(); - break; - case array_mode_numbers: - if (isdigit(*it) || *it == '-') - {//array of numbers value started - std::string val; - bool is_v_float = false;bool is_signed_val = false; - match_number2(it, buf_end, val, is_v_float, is_signed_val); - 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); - - }else - { - //TODO: optimize here if need - double nval = boost::lexical_cast(val); //string_tools::string_to_num_fast(val, nval); - insert_res = stg.insert_next_value(h_array, nval); - } - CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); - state = match_state_array_after_value; - array_md = array_mode_numbers; - }else CHECK_ISSPACE(); - break; - case array_mode_booleans: - if(isalpha(*it) ) - {// array of booleans - std::string word; - match_word2(it, buf_end, word); - if(boost::iequals(word, "true")) - { - bool r = stg.insert_next_value(h_array, true); - CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); - state = match_state_array_after_value; - }else if(boost::iequals(word, "false")) - { - bool r = stg.insert_next_value(h_array, false); - CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); - state = match_state_array_after_value; - } - else ASSERT_MES_AND_THROW("Unknown value keyword " << word); - }else CHECK_ISSPACE(); - break; - case array_mode_undifined: - default: - ASSERT_MES_AND_THROW("Bad array state"); - } - break; - case match_state_error: - default: - ASSERT_MES_AND_THROW("WRONG JSON STATE"); - } - } - } -/* -{ - "firstName": "John", - "lastName": "Smith", - "age": 25, - "address": { - "streetAddress": "21 2nd Street", - "city": "New York", - "state": "NY", - "postalCode": -10021, - "have_boobs": true, - "have_balls": false - }, - "phoneNumber": [ - { - "type": "home", - "number": "212 555-1234" - }, - { - "type": "fax", - "number": "646 555-4567" - } - ], - "phoneNumbers": [ - "812 123-1234", - "916 123-4567" - ] -} -*/ - template - inline bool load_from_json(const std::string& buff_json, t_storage& stg) - { - std::string::const_iterator sec_buf_begin = buff_json.begin(); - try - { - run_handler(nullptr, sec_buf_begin, buff_json.end(), stg); - return true; - } - catch(const std::exception& ex) - { - (void)(ex); - LOG_PRINT_RED_L0("Failed to parse json, what: " << ex.what()); - return false; - } - catch(...) - { - LOG_PRINT_RED_L0("Failed to parse json"); - return false; - } - } - } - } -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h deleted file mode 100644 index 008f443211..0000000000 --- a/contrib/epee/include/storages/portable_storage_template_helper.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "parserse_base_utils.h" -#include "portable_storage.h" -#include "file_io_utils.h" - -namespace epee -{ - namespace serialization - { - //----------------------------------------------------------------------------------------------------------- - template - bool load_t_from_json(t_struct& out, const std::string& json_buff) - { - portable_storage ps; - bool rs = ps.load_from_json(json_buff); - if(!rs) - return false; - - return out.load(ps); - } - //----------------------------------------------------------------------------------------------------------- - template - bool load_t_from_json_file(t_struct& out, const std::string& json_file) - { - std::string f_buff; - if(!file_io_utils::load_file_to_string(json_file, f_buff)) - return false; - - return load_t_from_json(out, f_buff); - } - //----------------------------------------------------------------------------------------------------------- - template - bool store_t_to_json(t_struct& str_in, std::string& json_buff, size_t indent = 0, bool insert_newlines = true) - { - portable_storage ps; - str_in.store(ps); - ps.dump_as_json(json_buff, indent, insert_newlines); - return true; - } - //----------------------------------------------------------------------------------------------------------- - template - std::string store_t_to_json(t_struct& str_in, size_t indent = 0, bool insert_newlines = true) - { - std::string json_buff; - store_t_to_json(str_in, json_buff, indent, insert_newlines); - return std::move(json_buff); - } - //----------------------------------------------------------------------------------------------------------- - template - bool store_t_to_json_file(t_struct& str_in, const std::string& fpath) - { - std::string json_buff; - store_t_to_json(str_in, json_buff); - return file_io_utils::save_string_to_file(fpath, json_buff); - } - //----------------------------------------------------------------------------------------------------------- - template - bool load_t_from_binary(t_struct& out, const std::string& binary_buff) - { - portable_storage ps; - bool rs = ps.load_from_binary(binary_buff); - if(!rs) - return false; - - return out.load(ps); - } - //----------------------------------------------------------------------------------------------------------- - template - bool load_t_from_binary_file(t_struct& out, const std::string& binary_file) - { - std::string f_buff; - if(!file_io_utils::load_file_to_string(binary_file, f_buff)) - return false; - - return load_t_from_binary(out, f_buff); - } - //----------------------------------------------------------------------------------------------------------- - template - bool store_t_to_binary(t_struct& str_in, std::string& binary_buff, size_t indent = 0) - { - portable_storage ps; - str_in.store(ps); - return ps.store_to_binary(binary_buff); - } - //----------------------------------------------------------------------------------------------------------- - template - std::string store_t_to_binary(t_struct& str_in, size_t indent = 0) - { - std::string binary_buff; - store_t_to_binary(str_in, binary_buff, indent); - return std::move(binary_buff); - } - } -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h deleted file mode 100644 index 6743a60879..0000000000 --- a/contrib/epee/include/storages/portable_storage_to_bin.h +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_language.h" -#include "portable_storage_base.h" -#include "pragma_comp_defs.h" - -namespace epee -{ - namespace serialization - { - - template - size_t pack_varint_t(t_stream& strm, uint8_t type_or, size_t& pv) - { - pack_value v = (*((pack_value*)&pv)) << 2; - v |= type_or; - strm.write((const char*)&v, sizeof(pack_value)); - return sizeof(pack_value); - } - - PRAGMA_WARNING_PUSH - PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") - template - size_t pack_varint(t_stream& strm, size_t val) - { //the first two bits always reserved for size information - - if(val <= 63) - {//mean enough one byte - return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_BYTE, val); - } - else if(val <= 16383) - {//mean need word - return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_WORD, val); - }else if(val <= 1073741823) - {//mean need dword - return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_DWORD, val); - }else - { - CHECK_AND_ASSERT_THROW_MES(val <= 4611686018427387903, "failed to pack varint - too big amount = " << val); - return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_INT64, val); - } - } - PRAGMA_WARNING_POP - - template - bool put_string(t_stream& strm, const std::string& v) - { - pack_varint(strm, v.size()); - if(v.size()) - strm.write((const char*)v.data(), v.size()); - return true; - } - - template - struct array_entry_store_visitor: public boost::static_visitor - { - t_stream& m_strm; - - template - bool pack_pod_array_type(uint8_t contained_type, const array_entry_t& arr_pod) - { - uint8_t type = contained_type|SERIALIZE_FLAG_ARRAY; - m_strm.write((const char*)&type, 1); - pack_varint(m_strm, arr_pod.m_array.size()); - for(const t_pod_type& x: arr_pod.m_array) - m_strm.write((const char*)&x, sizeof(t_pod_type)); - return true; - } - - array_entry_store_visitor(t_stream& strm):m_strm(strm){} - bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT64, v);} - bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT32, v);} - bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT16, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_UINT8, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT64, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT32, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT16, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT8, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_DUOBLE, v);} - bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_BOOL, v);} - bool operator()(const array_entry_t& arr_str) - { - uint8_t type = SERIALIZE_TYPE_STRING|SERIALIZE_FLAG_ARRAY; - m_strm.write((const char*)&type, 1); - pack_varint(m_strm, arr_str.m_array.size()); - for(const std::string& s: arr_str.m_array) - put_string(m_strm, s); - return true; - } - bool operator()(const array_entry_t
& arr_sec) - { - uint8_t type = SERIALIZE_TYPE_OBJECT|SERIALIZE_FLAG_ARRAY; - m_strm.write((const char*)&type, 1); - pack_varint(m_strm, arr_sec.m_array.size()); - for(const section& s: arr_sec.m_array) - pack_entry_to_buff(m_strm, s); - return true; - } - bool operator()(const array_entry_t& arra_ar) - { - uint8_t type = SERIALIZE_TYPE_ARRAY|SERIALIZE_FLAG_ARRAY; - m_strm.write((const char*)&type, 1); - pack_varint(m_strm, arra_ar.m_array.size()); - for(const array_entry& s: arra_ar.m_array) - pack_entry_to_buff(m_strm, s); - return true; - } - }; - - template - struct storage_entry_store_visitor: public boost::static_visitor - { - t_stream& m_strm; - storage_entry_store_visitor(t_stream& strm):m_strm(strm){} - template - bool pack_pod_type(uint8_t type, const pod_type& v) - { - m_strm.write((const char*)&type, 1); - m_strm.write((const char*)&v, sizeof(pod_type)); - return true; - } - //section, array_entry - bool operator()(const uint64_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT64, v);} - bool operator()(const uint32_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT32, v);} - bool operator()(const uint16_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT16, v);} - bool operator()(const uint8_t& v) { return pack_pod_type(SERIALIZE_TYPE_UINT8, v);} - bool operator()(const int64_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT64, v);} - bool operator()(const int32_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT32, v);} - bool operator()(const int16_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT16, v);} - bool operator()(const int8_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT8, v);} - bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DUOBLE, v);} - bool operator()(const bool& v) { return pack_pod_type(SERIALIZE_TYPE_BOOL, v);} - bool operator()(const std::string& v) - { - uint8_t type = SERIALIZE_TYPE_STRING; - m_strm.write((const char*)&type, 1); - put_string(m_strm, v); - return true; - } - bool operator()(const section& v) - { - uint8_t type = SERIALIZE_TYPE_OBJECT; - m_strm.write((const char*)&type, 1); - return pack_entry_to_buff(m_strm, v); - } - - bool operator()(const array_entry& v) - { - //uint8_t type = SERIALIZE_TYPE_ARRAY; - //m_strm.write((const char*)&type, 1); - return pack_entry_to_buff(m_strm, v); - } - }; - - template - bool pack_entry_to_buff(t_stream& strm, const array_entry& ae) - { - array_entry_store_visitor aesv(strm); - return boost::apply_visitor(aesv, ae); - } - - template - bool pack_entry_to_buff(t_stream& strm, const storage_entry& se) - { - storage_entry_store_visitor sv(strm); - return boost::apply_visitor(sv, se); - } - - template - bool pack_entry_to_buff(t_stream& strm, const section& sec) - { - typedef std::map::value_type section_pair; - pack_varint(strm, sec.m_entries.size()); - for(const section_pair& se: sec.m_entries) - { - CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first); - uint8_t len = static_cast(se.first.size()); - strm.write((const char*)&len, sizeof(len)); - strm.write(se.first.data(), size_t(len)); - pack_entry_to_buff(strm, se.second); - } - return true; - } - } -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_to_json.h b/contrib/epee/include/storages/portable_storage_to_json.h deleted file mode 100644 index e3fdcec29c..0000000000 --- a/contrib/epee/include/storages/portable_storage_to_json.h +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_language.h" -#include "portable_storage_base.h" -#include "parserse_base_utils.h" - -namespace epee -{ - namespace serialization - { - - template - void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const std::string& v, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const bool& v, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const t_type& v, size_t indent, bool insert_newlines); - template - void dump_as_json(t_stream& strm, const section& sec, size_t indent, bool insert_newlines); - - - inline std::string make_indent(size_t indent) - { - return std::string(indent*2, ' '); - } - - template - struct array_entry_store_to_json_visitor: public boost::static_visitor - { - t_stream& m_strm; - size_t m_indent; - bool m_insert_newlines; - array_entry_store_to_json_visitor(t_stream& strm, size_t indent, - bool insert_newlines = true) - : m_strm(strm), m_indent(indent), m_insert_newlines(insert_newlines) - {} - - template - void operator()(const array_entry_t& a) - { - m_strm << "["; - if(a.m_array.size()) - { - auto last_it = --a.m_array.end(); - for(auto it = a.m_array.begin(); it != a.m_array.end(); it++) - { - dump_as_json(m_strm, *it, m_indent, m_insert_newlines); - if(it != last_it) - m_strm << ","; - } - } - m_strm << "]"; - } - }; - - template - struct storage_entry_store_to_json_visitor: public boost::static_visitor - { - t_stream& m_strm; - size_t m_indent; - bool m_insert_newlines; - storage_entry_store_to_json_visitor(t_stream& strm, size_t indent, - bool insert_newlines = true) - : m_strm(strm), m_indent(indent), m_insert_newlines(insert_newlines) - {} - //section, array_entry - template - void operator()(const visited_type& v) - { - dump_as_json(m_strm, v, m_indent, m_insert_newlines); - } - }; - - template - void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent, bool insert_newlines) - { - array_entry_store_to_json_visitor aesv(strm, indent, insert_newlines); - boost::apply_visitor(aesv, ae); - } - - template - void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent, bool insert_newlines) - { - storage_entry_store_to_json_visitor sv(strm, indent, insert_newlines); - boost::apply_visitor(sv, se); - } - - template - void dump_as_json(t_stream& strm, const std::string& v, size_t indent, bool insert_newlines) - { - strm << "\"" << misc_utils::parse::transform_to_escape_sequence(v) << "\""; - } - - template - void dump_as_json(t_stream& strm, const int8_t& v, size_t indent, bool insert_newlines) - { - strm << static_cast(v); - } - - template - void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent, bool insert_newlines) - { - strm << static_cast(v); - } - - template - void dump_as_json(t_stream& strm, const bool& v, size_t indent, bool insert_newlines) - { - if(v) - strm << "true"; - else - strm << "false"; - } - - - - template - void dump_as_json(t_stream& strm, const t_type& v, size_t indent, bool insert_newlines) - { - strm << v; - } - - template - void dump_as_json(t_stream& strm, const section& sec, size_t indent, bool insert_newlines) - { - size_t local_indent = indent + 1; - std::string newline = insert_newlines ? "\r\n" : ""; - strm << "{" << newline; - std::string indent_str = make_indent(local_indent); - if(sec.m_entries.size()) - { - auto it_last = --sec.m_entries.end(); - for(auto it = sec.m_entries.begin(); it!= sec.m_entries.end();it++) - { - strm << indent_str << "\"" << misc_utils::parse::transform_to_escape_sequence(it->first) << "\"" << ": "; - dump_as_json(strm, it->second, local_indent, insert_newlines); - if(it_last != it) - strm << ","; - strm << newline; - } - } - strm << make_indent(indent) << "}"; - } - } -} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h deleted file mode 100644 index 73d339f5d0..0000000000 --- a/contrib/epee/include/storages/portable_storage_val_converters.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "misc_language.h" -#include "portable_storage_base.h" -#include "warnings.h" - -namespace epee -{ - namespace serialization - { -#define ASSERT_AND_THROW_WRONG_CONVERSION() ASSERT_MES_AND_THROW("WRONG DATA CONVERSION: from type=" << typeid(from).name() << " to type " << typeid(to).name()) - - template - void convert_int_to_uint(const from_type& from, to_type& to) - { -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4018) - CHECK_AND_ASSERT_THROW_MES(from >=0, "unexpected int value with signed storage value less than 0, and unsigned receiver value"); -DISABLE_GCC_AND_CLANG_WARNING(sign-compare) - CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); - to = static_cast(from); -POP_WARNINGS - } - template - void convert_int_to_int(const from_type& from, to_type& to) - { - CHECK_AND_ASSERT_THROW_MES(from >= boost::numeric::bounds::lowest(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with lowest possible value = " << boost::numeric::bounds::lowest()); -PUSH_WARNINGS -DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) - CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); -POP_WARNINGS - to = static_cast(from); - } - template - void convert_uint_to_any_int(const from_type& from, to_type& to) - { -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4018) -DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) - CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "uint value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); - to = static_cast(from); -POP_WARNINGS - } - - template //is from signed, is from to signed - struct convert_to_signed_unsigned; - - template - struct convert_to_signed_unsigned - { - static void convert(const from_type& from, to_type& to) - {//from signed to signed - convert_int_to_int(from, to); - } - }; - - template - struct convert_to_signed_unsigned - { - static void convert(const from_type& from, to_type& to) - {//from signed to unsigned - convert_int_to_uint(from, to); - } - }; - - template - struct convert_to_signed_unsigned - { - static void convert(const from_type& from, to_type& to) - {//from unsigned to signed - convert_uint_to_any_int(from, to); - } - }; - - template - struct convert_to_signed_unsigned - { - static void convert(const from_type& from, to_type& to) - { - //from unsigned to unsigned - convert_uint_to_any_int(from, to); - } - }; - - template - struct convert_to_integral; - - template - struct convert_to_integral - { - static void convert(const from_type& from, to_type& to) - { - convert_to_signed_unsigned::value, std::is_signed::value>::convert(from, to); - } - }; - - template - struct convert_to_integral - { - static void convert(const from_type& from, to_type& to) - { - ASSERT_AND_THROW_WRONG_CONVERSION(); - } - }; - - template - struct is_convertable: std::integral_constant::value && - std::is_integral::value && - !std::is_same::value && - !std::is_same::value > {}; - - template - struct convert_to_same; - - template - struct convert_to_same - { - static void convert(const from_type& from, to_type& to) - { - to = from; - } - }; - - template - struct convert_to_same - { - static void convert(const from_type& from, to_type& to) - { - convert_to_integral::value>::convert(from, to);// ASSERT_AND_THROW_WRONG_CONVERSION(); - } - }; - - - template - void convert_t(const from_type& from, to_type& to) - { - convert_to_same::value>::convert(from, to); - } - } -} \ No newline at end of file diff --git a/contrib/epee/include/string_coding.h b/contrib/epee/include/string_coding.h deleted file mode 100644 index a2e3d6eb2d..0000000000 --- a/contrib/epee/include/string_coding.h +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _STRING_CODING_H_ -#define _STRING_CODING_H_ - -#include -//#include "md5_l.h" -namespace epee -{ -namespace string_encoding -{ - inline std::string convert_to_ansii(const std::wstring& str_from) - { - - std::string res(str_from.begin(), str_from.end()); - return res; - /* - std::string result; - std::locale loc; - for(unsigned int i= 0; i < str_from.size(); ++i) - { - result += std::use_facet >(loc).narrow(str_from[i]); - } - return result; - */ - - //return boost::lexical_cast(str_from); - /* - std::string str_trgt; - if(!str_from.size()) - return str_trgt; - int cb = ::WideCharToMultiByte( code_page, 0, str_from.data(), (__int32)str_from.size(), 0, 0, 0, 0 ); - if(!cb) - return str_trgt; - str_trgt.resize(cb); - ::WideCharToMultiByte( code_page, 0, str_from.data(), (int)str_from.size(), - (char*)str_trgt.data(), (int)str_trgt.size(), 0, 0); - return str_trgt;*/ - } -#ifdef WINDOWS_PLATFORM_EX - inline std::string convert_to_ansii_win(const std::wstring& str_from) - { - - int code_page = CP_ACP; - std::string str_trgt; - if(!str_from.size()) - return str_trgt; - int cb = ::WideCharToMultiByte( code_page, 0, str_from.data(), (__int32)str_from.size(), 0, 0, 0, 0 ); - if(!cb) - return str_trgt; - str_trgt.resize(cb); - ::WideCharToMultiByte( code_page, 0, str_from.data(), (int)str_from.size(), - (char*)str_trgt.data(), (int)str_trgt.size(), 0, 0); - return str_trgt; - } -#endif - - inline std::string convert_to_ansii(const std::string& str_from) - { - return str_from; - } - - inline std::wstring convert_to_unicode(const std::string& str_from) - { - std::wstring result; - std::locale loc; - for(unsigned int i= 0; i < str_from.size(); ++i) - { - result += std::use_facet >(loc).widen(str_from[i]); - } - return result; - - //return boost::lexical_cast(str_from); - /* - std::wstring str_trgt; - if(!str_from.size()) - return str_trgt; - - int cb = ::MultiByteToWideChar( code_page, 0, str_from.data(), (int)str_from.size(), 0, 0 ); - if(!cb) - return str_trgt; - - str_trgt.resize(cb); - ::MultiByteToWideChar( code_page, 0, str_from.data(),(int)str_from.size(), - (wchar_t*)str_trgt.data(),(int)str_trgt.size()); - return str_trgt;*/ - } - inline std::wstring convert_to_unicode(const std::wstring& str_from) - { - return str_from; - } - - template - inline target_string convert_to_t(const std::wstring& str_from); - - template<> - inline std::string convert_to_t(const std::wstring& str_from) - { - return convert_to_ansii(str_from); - } - - template<> - inline std::wstring convert_to_t(const std::wstring& str_from) - { - return str_from; - } - - template - inline target_string convert_to_t(const std::string& str_from); - - template<> - inline std::string convert_to_t(const std::string& str_from) - { - return str_from; - } - - template<> - inline std::wstring convert_to_t(const std::string& str_from) - { - return convert_to_unicode(str_from); - } - - inline - std::string& base64_chars() - { - - static std::string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - return chars; - - } - - inline - std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len) { - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; - - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for(i = 0; (i <4) ; i++) - ret += base64_chars()[char_array_4[i]]; - i = 0; - } - } - - if (i) - { - for(j = i; j < 3; j++) - char_array_3[j] = '\0'; - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (j = 0; (j < i + 1); j++) - ret += base64_chars()[char_array_4[j]]; - - while((i++ < 3)) - ret += '='; - - } - - return ret; - - } - - inline - std::string base64_encode(const std::string& str) - { - return base64_encode((unsigned char const* )str.data(), str.size()); - } - - inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); - } - - - inline - std::string base64_decode(std::string const& encoded_string) { - size_t in_len = encoded_string.size(); - size_t i = 0; - size_t j = 0; - size_t in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++) - char_array_4[i] = (unsigned char)base64_chars().find(char_array_4[i]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) - ret += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (j = i; j <4; j++) - char_array_4[j] = 0; - - for (j = 0; j <4; j++) - char_array_4[j] = (unsigned char)base64_chars().find(char_array_4[j]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - - return ret; - } - - //md5 -#ifdef MD5_H - inline - std::string get_buf_as_hex_string(const void* pbuf, size_t len) - { - std::ostringstream result; - - const unsigned char* p_buff = (const unsigned char*)pbuf; - - for(unsigned int i=0;i(len), output); - return get_buf_as_hex_string(output, sizeof(output)); - } - - inline - std::string get_md5_as_hexstring(const std::string& src) - { - return get_md5_as_hexstring(src.data(), src.size()); - } -#endif - - -} -} - -#endif //_STRING_CODING_H_ diff --git a/contrib/epee/include/string_tools.cpp b/contrib/epee/include/string_tools.cpp deleted file mode 100644 index 8181bc1021..0000000000 --- a/contrib/epee/include/string_tools.cpp +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "string_tools.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -#ifdef WINDOWS_PLATFORM -#pragma comment (lib, "Rpcrt4.lib") -#endif - -namespace epee -{ -namespace string_tools -{ - std::wstring get_str_from_guid(const boost::uuids::uuid& rid) - { - return boost::lexical_cast(rid); - } - //---------------------------------------------------------------------------- - std::string get_str_from_guid_a(const boost::uuids::uuid& rid) - { - return boost::lexical_cast(rid); - } - //---------------------------------------------------------------------------- - bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id) - { - if(str_id.size() < 36) - return false; - - if('{' == *str_id.begin()) - str_id.erase(0, 1); - - if('}' == *(--str_id.end())) - str_id.erase(--str_id.end()); - - try - { - inetifer = boost::lexical_cast(str_id); - return true; - } - catch(...) - { - return false; - } - } - //---------------------------------------------------------------------------- - bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id) - { - std::string local_str_id = str_id; - if(local_str_id.size() < 36) - return false; - - if('{' == *local_str_id.begin()) - local_str_id.erase(0, 1); - - if('}' == *(--local_str_id.end())) - local_str_id.erase(--local_str_id.end()); - - try - { - inetifer = boost::lexical_cast(local_str_id); - return true; - } - catch(...) - { - return false; - } - } - //---------------------------------------------------------------------------- -//#ifdef _WINSOCK2API_ - std::string get_ip_string_from_int32(uint32_t ip) - { - in_addr adr; - adr.s_addr = ip; - const char* pbuf = inet_ntoa(adr); - if(pbuf) - return pbuf; - else - return "[failed]"; - } - //---------------------------------------------------------------------------- - bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str) - { - ip = inet_addr(ip_str.c_str()); - if(INADDR_NONE == ip) - return false; - - return true; - } - //---------------------------------------------------------------------------- - bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) - { - //parse ip and address - std::string::size_type p = addres.find(':'); - if(p == std::string::npos) - { - return false; - } - std::string ip_str = addres.substr(0, p); - std::string port_str = addres.substr(p+1, addres.size()); - - if(!get_ip_int32_from_string(ip, ip_str)) - { - return false; - } - - if(!get_xtype_from_string(port, port_str)) - { - return false; - } - return true; - } - - //---------------------------------------------------------------------------- - std::string num_to_string_fast(int64_t val) - { - /* - char buff[30] = {0}; - i64toa_s(val, buff, sizeof(buff)-1, 10); - return buff;*/ - return boost::lexical_cast(val); - } - //---------------------------------------------------------------------------- - bool string_to_num_fast(const std::string& buff, int64_t& val) - { - //return get_xtype_from_string(val, buff); -#if (defined _MSC_VER) - val = _atoi64(buff.c_str()); -#else - val = atoll(buff.c_str()); -#endif - /* - * val = atoi64(buff.c_str()); - */ - if(buff != "0" && val == 0) - return false; - return true; - } - //---------------------------------------------------------------------------- - bool string_to_num_fast(const std::string& buff, int& val) - { - val = atoi(buff.c_str()); - if(buff != "0" && val == 0) - return false; - - return true; - } - //---------------------------------------------------------------------------- -#ifdef WINDOWS_PLATFORM - std::string system_time_to_string(const SYSTEMTIME& st) - { - - /* - TIME_ZONE_INFORMATION tzi; - GetTimeZoneInformation(&tzi); - SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal); - */ - - char szTime[25], szDate[25]; - ::GetTimeFormatA( - LOCALE_USER_DEFAULT, // locale - TIME_FORCE24HOURFORMAT, // options - &st, // time - NULL, // time format string - szTime, // formatted string buffer - 25 // size of string buffer - ); - - ::GetDateFormatA( - LOCALE_USER_DEFAULT, // locale - NULL, // options - &st, // date - NULL, // date format - szDate, // formatted string buffer - 25 // size of buffer - ); - szTime[24] = szDate[24] = 0; //be happy :) - - std::string res = szDate; - (res += " " )+= szTime; - return res; - - } -#endif - //---------------------------------------------------------------------------- - - bool compare_no_case(const std::string& str1, const std::string& str2) - { - - return !boost::iequals(str1, str2); - } - //---------------------------------------------------------------------------- - bool compare_no_case(const std::wstring& str1, const std::wstring& str2) - { - return !boost::iequals(str1, str2); - } - //---------------------------------------------------------------------------- - bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix) - { - if(prefix.size()>str1.size()) - return false; - - if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) - return true; - else - return false; - } - //---------------------------------------------------------------------------- - bool is_match_prefix(const std::string& str1, const std::string& prefix) - { - if(prefix.size()>str1.size()) - return false; - - if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) - return true; - else - return false; - } - //---------------------------------------------------------------------------- - std::string& get_current_module_name() - { - static std::string module_name; - return module_name; - } - //---------------------------------------------------------------------------- - std::string& get_current_module_folder() - { - static std::string module_folder; - return module_folder; - } - //---------------------------------------------------------------------------- -#ifdef _WIN32 - std::string get_current_module_path() - { - char pname [5000] = {0}; - GetModuleFileNameA( NULL, pname, sizeof(pname)); - pname[sizeof(pname)-1] = 0; //be happy ;) - return pname; - } -#endif - //---------------------------------------------------------------------------- - bool set_module_name_and_folder(const std::string& path_to_process_) - { - std::string path_to_process = path_to_process_; -#ifdef _WIN32 - path_to_process = get_current_module_path(); -#endif - std::string::size_type a = path_to_process.rfind( '\\' ); - if(a == std::string::npos ) - { - a = path_to_process.rfind( '/' ); - } - if ( a != std::string::npos ) - { - get_current_module_name() = path_to_process.substr(a+1, path_to_process.size()); - get_current_module_folder() = path_to_process.substr(0, a); - return true; - }else - return false; - - } - - //---------------------------------------------------------------------------- - bool trim_left(std::string& str) - { - for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast(*it));) - str.erase(str.begin()); - - return true; - } - //---------------------------------------------------------------------------- - bool trim_right(std::string& str) - { - - for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast(*it));) - str.erase( --((it++).base())); - - return true; - } - //---------------------------------------------------------------------------- - std::string& trim(std::string& str) - { - - trim_left(str); - trim_right(str); - return str; - } - //---------------------------------------------------------------------------- - std::string trim(const std::string& str_) - { - std::string str = str_; - trim_left(str); - trim_right(str); - return str; - } - //---------------------------------------------------------------------------- - std::string get_extension(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('.'); - if(std::string::npos == pos) - return res; - - res = str.substr(pos+1, str.size()-pos); - return res; - } - //---------------------------------------------------------------------------- - std::string get_filename_from_path(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('\\'); - if(std::string::npos == pos) - return str; - - res = str.substr(pos+1, str.size()-pos); - return res; - } - //---------------------------------------------------------------------------- - - - - std::string cut_off_extension(const std::string& str) - { - std::string res; - std::string::size_type pos = str.rfind('.'); - if(std::string::npos == pos) - return str; - - res = str.substr(0, pos); - return res; - } - - //---------------------------------------------------------------------------- -#ifdef _WININET_ - std::string get_string_from_systemtime(const SYSTEMTIME& sys_time) - { - std::string result_string; - - char buff[100] = {0}; - BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99); - if(!res) - { - LOG_ERROR("Failed to load SytemTime to string"); - } - - result_string = buff; - return result_string; - - } - //------------------------------------------------------------------------------------- - SYSTEMTIME get_systemtime_from_string(const std::string& buff) - { - SYSTEMTIME result_time = {0}; - - BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL); - if(!res) - { - LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes"); - } - - return result_time; - } -#endif - -#ifdef WINDOWS_PLATFORM - const wchar_t* get_pc_name() - { - static wchar_t info[INFO_BUFFER_SIZE]; - static DWORD bufCharCount = INFO_BUFFER_SIZE; - static bool init = false; - - if (!init) { - if (!GetComputerNameW( info, &bufCharCount )) - info[0] = 0; - else - init = true; - } - - return info; - } - - const wchar_t* get_user_name() - { - static wchar_t info[INFO_BUFFER_SIZE]; - static DWORD bufCharCount = INFO_BUFFER_SIZE; - static bool init = false; - - if (!init) { - if (!GetUserNameW( info, &bufCharCount )) - info[0] = 0; - else - init = true; - } - - return info; - } -#endif - -#ifdef _LM_ - const wchar_t* get_domain_name() - { - static wchar_t info[INFO_BUFFER_SIZE]; - static DWORD bufCharCount = 0; - static bool init = false; - - if (!init) { - LPWSTR domain( NULL ); - NETSETUP_JOIN_STATUS status; - info[0] = 0; - - if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status )) - { - LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result)); - } else - { - StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain ); - NetApiBufferFree((void*)domain); - init = true; - } - } - - return info; - } -#endif -#ifdef WINDOWS_PLATFORM - inline - std::string load_resource_string_a(int id, const char* pmodule_name = NULL) - { - //slow realization - HMODULE h = ::GetModuleHandleA( pmodule_name ); - - char buff[2000] = {0}; - - ::LoadStringA( h, id, buff, sizeof(buff)); - buff[sizeof(buff)-1] = 0; //be happy :) - return buff; - } - inline - std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL) - { - //slow realization - HMODULE h = ::GetModuleHandleA( pmodule_name ); - - wchar_t buff[2000] = {0}; - - ::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) ); - buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :) - return buff; - } -#endif -} -} diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h deleted file mode 100644 index c5d09421b5..0000000000 --- a/contrib/epee/include/string_tools.h +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 - -#ifndef _STRING_TOOLS_H_ -#define _STRING_TOOLS_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "warnings.h" - -#ifndef OUT - #define OUT -#endif - -#ifdef WINDOWS_PLATFORM -#pragma comment (lib, "Rpcrt4.lib") -#endif - -// Don't include lexical_cast.hpp, to reduce compilation time -//#include - -namespace boost { - namespace uuids { - struct uuid; - } - - template - inline Target lexical_cast(const Source &arg); -} - -namespace epee -{ -namespace string_tools -{ - std::wstring get_str_from_guid(const boost::uuids::uuid& rid); - std::string get_str_from_guid_a(const boost::uuids::uuid& rid); - bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id); - bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id); - //---------------------------------------------------------------------------- - template - std::basic_string buff_to_hex(const std::basic_string& s) - { - std::basic_stringstream hexStream; - hexStream << std::hex << std::noshowbase << std::setw(2); - - for(typename std::basic_string::const_iterator it = s.begin(); it != s.end(); it++) - { - hexStream << "0x"<< static_cast(static_cast(*it)) << " "; - } - return hexStream.str(); - } - //---------------------------------------------------------------------------- - template - std::basic_string buff_to_hex_nodelimer(const std::basic_string& s) - { - std::basic_stringstream hexStream; - hexStream << std::hex << std::noshowbase; - - for(typename std::basic_string::const_iterator it = s.begin(); it != s.end(); it++) - { - hexStream << std::setw(2) << std::setfill('0') << static_cast(static_cast(*it)); - } - return hexStream.str(); - } - //---------------------------------------------------------------------------- - template - bool parse_hexstr_to_binbuff(const std::basic_string& s, std::basic_string& res) - { - res.clear(); - try - { - long v = 0; - for(size_t i = 0; i < (s.size() + 1) / 2; i++) - { - 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)); - } - - return true; - }catch(...) - { - return false; - } - } - //---------------------------------------------------------------------------- - template - bool parse_tpod_from_hex_string(const std::string& str_hash, t_pod_type& t_pod) - { - std::string buf; - bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); - if (!res || buf.size() != sizeof(t_pod_type)) - { - return false; - } - else - { - buf.copy(reinterpret_cast(&t_pod), sizeof(t_pod_type)); - return true; - } - } - //---------------------------------------------------------------------------- -PUSH_WARNINGS -DISABLE_GCC_WARNING(maybe-uninitialized) - template - inline bool get_xtype_from_string(OUT XType& val, const std::string& str_id) - { - if (std::is_integral::value && !std::numeric_limits::is_signed && !std::is_same::value) - { - for (char c : str_id) - { - if (!std::isdigit(c)) - return false; - } - } - - try - { - val = boost::lexical_cast(str_id); - return true; - } - catch(std::exception& /*e*/) - { - //const char* pmsg = e.what(); - return false; - } - catch(...) - { - return false; - } - - return true; - } -POP_WARNINGS - //--------------------------------------------------- - template - bool get_xnum_from_hex_string(const std::string str, int_t& res ) - { - try - { - std::stringstream ss; - ss << std::hex << str; - ss >> res; - return true; - } - catch(...) - { - return false; - } - } - //---------------------------------------------------------------------------- - template - inline bool xtype_to_string(const XType& val, std::string& str) - { - try - { - str = boost::lexical_cast(val); - } - catch(...) - { - return false; - } - - return true; - } - - typedef std::map command_line_params_a; - typedef std::map command_line_params_w; - - template - bool parse_commandline(std::map& res, int argc, char** argv) - { - t_string key; - for(int i = 1; i < argc; i++) - { - if(!argv[i]) - break; - t_string s = argv[i]; - std::string::size_type p = s.find('='); - if(std::string::npos == p) - { - res[s] = ""; - }else - { - std::string ss; - t_string nm = s.substr(0, p); - t_string vl = s.substr(p+1, s.size()); - res[nm] = vl; - } - } - return true; - } - - template - bool get_xparam_from_command_line(const std::map& res, const t_string & key, t_type& val) - { - typename std::map::const_iterator it = res.find(key); - if(it == res.end()) - return false; - - if(it->second.size()) - { - return get_xtype_from_string(val, it->second); - } - - return true; - } - - template - t_type get_xparam_from_command_line(const std::map& res, const t_string & key, const t_type& default_value) - { - typename std::map::const_iterator it = res.find(key); - if(it == res.end()) - return default_value; - - if(it->second.size()) - { - t_type s; - get_xtype_from_string(s, it->second); - return s; - } - - return default_value; - } - - template - bool have_in_command_line(const std::map& res, const std::basic_string& key) - { - typename std::map::const_iterator it = res.find(key); - if(it == res.end()) - return false; - - return true; - } - - //---------------------------------------------------------------------------- - std::string get_ip_string_from_int32(uint32_t ip); - bool get_ip_int32_from_string(uint32_t& ip, const std::string& ip_str); - bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres); - //---------------------------------------------------------------------------- - template - inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8) - { - std::stringstream ss; - ss << std::setfill ('0') << std::setw (w) << std::hex << std::noshowbase; - ss << v; - return ss.str(); - } - //---------------------------------------------------------------------------- - std::string num_to_string_fast(int64_t val); - bool string_to_num_fast(const std::string& buff, int64_t& val); - bool string_to_num_fast(const std::string& buff, int& val); -#ifdef WINDOWS_PLATFORM - std::string system_time_to_string(const SYSTEMTIME& st); -#endif - bool compare_no_case(const std::string& str1, const std::string& str2); - bool compare_no_case(const std::wstring& str1, const std::wstring& str2); - bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix); - bool is_match_prefix(const std::string& str1, const std::string& prefix); - std::string& get_current_module_name(); - std::string& get_current_module_folder(); -#ifdef _WIN32 - std::string get_current_module_path(); -#endif - bool set_module_name_and_folder(const std::string& path_to_process_); - bool trim_left(std::string& str); - bool trim_right(std::string& str); - std::string& trim(std::string& str); - std::string trim(const std::string& str_); - //---------------------------------------------------------------------------- - template - std::string pod_to_hex(const t_pod_type& s) - { - std::string buff; - buff.assign(reinterpret_cast(&s), sizeof(s)); - return buff_to_hex_nodelimer(buff); - } - //---------------------------------------------------------------------------- - template - bool hex_to_pod(const std::string& hex_str, t_pod_type& s) - { - 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; - } - //---------------------------------------------------------------------------- - std::string get_extension(const std::string& str); - std::string get_filename_from_path(const std::string& str); - std::string cut_off_extension(const std::string& str); -#ifdef _WININET_ - std::string get_string_from_systemtime(const SYSTEMTIME& sys_time); - SYSTEMTIME get_systemtime_from_string(const std::string& buff); -#endif - -#ifdef WINDOWS_PLATFORM - const DWORD INFO_BUFFER_SIZE = 10000; - - const wchar_t* get_pc_name(); - const wchar_t* get_user_name(); -#endif - -#ifdef _LM_ - const wchar_t* get_domain_name(); -#endif -#ifdef WINDOWS_PLATFORM - std::string load_resource_string_a(int id, const char* pmodule_name = NULL); - std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL); -#endif -} -} -#endif //_STRING_TOOLS_H_ diff --git a/contrib/epee/include/syncobj.h b/contrib/epee/include/syncobj.h deleted file mode 100644 index 52cb70e609..0000000000 --- a/contrib/epee/include/syncobj.h +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 __WINH_OBJ_H__ -#define __WINH_OBJ_H__ - -#include -#include - -namespace epee -{ - struct simple_event - { - simple_event() : m_rised(false) - { - } - - void raise() - { - std::unique_lock lock(m_mx); - m_rised = true; - m_cond_var.notify_one(); - } - - void wait() - { - std::unique_lock lock(m_mx); - while (!m_rised) - m_cond_var.wait(lock); - m_rised = false; - } - - private: - std::mutex m_mx; - std::condition_variable m_cond_var; - bool m_rised; - }; - - class critical_region; - - class critical_section - { - std::recursive_mutex m_section; - - public: - //to make copy fake! - critical_section(const critical_section& section) - { - } - - critical_section() - { - } - - ~critical_section() - { - } - - void lock() - { - m_section.lock(); - } - - void unlock() - { - m_section.unlock(); - } - - bool tryLock() - { - return m_section.try_lock(); - } - - // to make copy fake - critical_section& operator=(const critical_section& section) - { - return *this; - } - }; - - - template - class critical_region_t - { - t_lock& m_locker; - bool m_unlocked; - - critical_region_t(const critical_region_t&) {} - - public: - critical_region_t(t_lock& cs): m_locker(cs), m_unlocked(false) - { - m_locker.lock(); - } - - ~critical_region_t() - { - unlock(); - } - - void unlock() - { - if (!m_unlocked) - { - m_locker.unlock(); - m_unlocked = true; - } - } - }; - - -#if defined(WINDWOS_PLATFORM) - class shared_critical_section - { - public: - shared_critical_section() - { - ::InitializeSRWLock(&m_srw_lock); - } - ~shared_critical_section() - {} - - bool lock_shared() - { - AcquireSRWLockShared(&m_srw_lock); - return true; - } - bool unlock_shared() - { - ReleaseSRWLockShared(&m_srw_lock); - return true; - } - bool lock_exclusive() - { - ::AcquireSRWLockExclusive(&m_srw_lock); - return true; - } - bool unlock_exclusive() - { - ::ReleaseSRWLockExclusive(&m_srw_lock); - return true; - } - private: - SRWLOCK m_srw_lock; - }; - - - class shared_guard - { - public: - shared_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) - { - m_ref_sec.lock_shared(); - } - - ~shared_guard() - { - m_ref_sec.unlock_shared(); - } - - private: - shared_critical_section& m_ref_sec; - }; - - - class exclusive_guard - { - public: - exclusive_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) - { - m_ref_sec.lock_exclusive(); - } - - ~exclusive_guard() - { - m_ref_sec.unlock_exclusive(); - } - - private: - shared_critical_section& m_ref_sec; - }; -#endif - -#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x) -#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x) - -#define CRITICAL_REGION_LOCAL(x) epee::critical_region_t critical_region_var(x) -#define CRITICAL_REGION_BEGIN(x) { epee::critical_region_t critical_region_var(x) -#define CRITICAL_REGION_LOCAL1(x) epee::critical_region_t critical_region_var1(x) -#define CRITICAL_REGION_BEGIN1(x) { epee::critical_region_t critical_region_var1(x) - -#define CRITICAL_REGION_END() } - - -#if defined(WINDWOS_PLATFORM) - inline const char* get_wait_for_result_as_text(DWORD res) - { - switch(res) - { - case WAIT_ABANDONED: return "WAIT_ABANDONED"; - case WAIT_TIMEOUT: return "WAIT_TIMEOUT"; - case WAIT_OBJECT_0: return "WAIT_OBJECT_0"; - case WAIT_OBJECT_0+1: return "WAIT_OBJECT_1"; - case WAIT_OBJECT_0+2: return "WAIT_OBJECT_2"; - default: return "UNKNOWN CODE"; - } - } -#endif - -} - -#endif diff --git a/contrib/epee/include/time_helper.h b/contrib/epee/include/time_helper.h deleted file mode 100644 index 958176da6f..0000000000 --- a/contrib/epee/include/time_helper.h +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "pragma_comp_defs.h" - -namespace epee -{ -namespace misc_utils -{ - -#ifdef __ATLTIME_H__ - - inline - bool get_time_t_from_ole_date(DATE src, time_t& res) - { - SYSTEMTIME st = {0}; - if(TRUE != ::VariantTimeToSystemTime(src, &st)) - return false; - ATL::CTime ss(st); - res = ss.GetTime(); - return true; - } -#endif - inline - std::string get_time_str(const time_t& time_) - { - - - char tmpbuf[200] = {0}; - tm* pt = NULL; -PRAGMA_WARNING_PUSH -PRAGMA_WARNING_DISABLE_VS(4996) - pt = localtime(&time_); -PRAGMA_WARNING_POP - - if(pt) - strftime( tmpbuf, 199, "%d.%m.%Y %H:%M:%S", pt ); - else - { - std::stringstream strs; - strs << "[wrong_time: " << std::hex << time_ << "]"; - return strs.str(); - } - return tmpbuf; - } - - inline - std::string get_time_str_v2(const time_t& time_) - { - - char tmpbuf[200] = {0}; - tm* pt = NULL; -PRAGMA_WARNING_PUSH -PRAGMA_WARNING_DISABLE_VS(4996) - pt = localtime(&time_); -PRAGMA_WARNING_POP - - if(pt) - strftime( tmpbuf, 199, "%Y_%m_%d %H_%M_%S", pt ); - else - { - std::stringstream strs; - strs << "[wrong_time: " << std::hex << time_ << "]"; - return strs.str(); - } - return tmpbuf; - } - - inline - std::string get_time_str_v3(const boost::posix_time::ptime& time_) - { - return boost::posix_time::to_simple_string(time_); - } - - - - inline std::string get_internet_time_str(const time_t& time_) - { - char tmpbuf[200] = {0}; - tm* pt = NULL; -PRAGMA_WARNING_PUSH -PRAGMA_WARNING_DISABLE_VS(4996) - pt = gmtime(&time_); -PRAGMA_WARNING_POP - strftime( tmpbuf, 199, "%a, %d %b %Y %H:%M:%S GMT", pt ); - return tmpbuf; - } - - inline std::string get_time_interval_string(const time_t& time_) - { - std::string res; - time_t tail = time_; -PRAGMA_WARNING_PUSH -PRAGMA_WARNING_DISABLE_VS(4244) - int days = tail/(60*60*24); - tail = tail%(60*60*24); - int hours = tail/(60*60); - tail = tail%(60*60); - int minutes = tail/(60); - tail = tail%(60); - int seconds = tail; -PRAGMA_WARNING_POP - res = std::string() + "d" + boost::lexical_cast(days) + ".h" + boost::lexical_cast(hours) + ".m" + boost::lexical_cast(minutes) + ".s" + boost::lexical_cast(seconds); - return res; - } - -#ifdef __SQLEXT - inline - bool odbc_time_to_oledb_taime(const SQL_TIMESTAMP_STRUCT& odbc_timestamp, DATE& oledb_date) - { - - SYSTEMTIME st = {0}; - st.wYear = odbc_timestamp.year; - st.wDay = odbc_timestamp.day; - st.wHour = odbc_timestamp.hour ; - st.wMilliseconds = (WORD)odbc_timestamp.fraction ; - st.wMinute = odbc_timestamp.minute ; - st.wMonth = odbc_timestamp.month ; - st.wSecond = odbc_timestamp.second ; - - if(TRUE != ::SystemTimeToVariantTime(&st, &oledb_date)) - return false; - return true; - } - -#endif -} -} \ No newline at end of file diff --git a/contrib/epee/include/tiny_ini.h b/contrib/epee/include/tiny_ini.h deleted file mode 100644 index 2bc71fc1a4..0000000000 --- a/contrib/epee/include/tiny_ini.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _TINY_INI_H_ -#define _TINY_INI_H_ - -#include -#include -#include "string_tools.h" - -namespace epee -{ -namespace tiny_ini -{ - - inline - bool get_param_value(const std::string& param_name, const std::string& ini_entry, std::string& res) - { - std::string expr_str = std::string() + "^("+ param_name +") *=(.*?)$"; - const boost::regex match_ini_entry( expr_str, boost::regex::icase | boost::regex::normal); - boost::smatch result; - if(!boost::regex_search(ini_entry, result, match_ini_entry, boost::match_default)) - return false; - res = result[2]; - string_tools::trim(res); - return true; - } - inline - std::string get_param_value(const std::string& param_name, const std::string& ini_entry) - { - std::string buff; - get_param_value(param_name, ini_entry, buff); - return buff; - } - - template - bool get_param_value_as_t(const std::string& param_name, const std::string& ini_entry, T& res) - { - std::string str_res = get_param_value(param_name, ini_entry); - - string_tools::trim(str_res); - if(!str_res.size()) - return false; - - return string_tools::get_xtype_from_string(res, str_res); - } - -} -} - -#endif //_TINY_INI_H_ diff --git a/contrib/epee/include/to_nonconst_iterator.h b/contrib/epee/include/to_nonconst_iterator.h deleted file mode 100644 index 729b0e8b29..0000000000 --- a/contrib/epee/include/to_nonconst_iterator.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _TO_NONCONST_ITERATOR_H_ -#define _TO_NONCONST_ITERATOR_H_ - -namespace epee -{ - -template -typename Type::iterator to_nonsonst_iterator(Type& obj, typename Type::const_iterator it) -{ - typename Type::difference_type dist = std::distance(static_cast(obj.begin()), it); - typename Type::iterator res_it = obj.begin()+dist; - return res_it; -} - - -template -typename Type::iterator to_nonsonst_iterator(typename Type::iterator base_it, typename Type::const_iterator it) -{ - typename Type::difference_type dist = std::distance(static_cast(base_it), it); - typename Type::iterator res_it = base_it+dist; - return res_it; -} -}//namespace epee -#endif //_TO_NONCONST_ITERATOR_H_ diff --git a/contrib/epee/include/warnings.h b/contrib/epee/include/warnings.h deleted file mode 100644 index 37d7a29004..0000000000 --- a/contrib/epee/include/warnings.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#if defined(_MSC_VER) - -#define PUSH_WARNINGS __pragma(warning(push)) -#define POP_WARNINGS __pragma(warning(pop)) -#define DISABLE_VS_WARNINGS(w) __pragma(warning(disable: w)) -#define DISABLE_GCC_WARNING(w) -#define DISABLE_CLANG_WARNING(w) -#define DISABLE_GCC_AND_CLANG_WARNING(w) - -#else - -#include - -#define PUSH_WARNINGS _Pragma("GCC diagnostic push") -#define POP_WARNINGS _Pragma("GCC diagnostic pop") -#define DISABLE_VS_WARNINGS(w) - -#if defined(__clang__) -#define DISABLE_GCC_WARNING(w) -#define DISABLE_CLANG_WARNING DISABLE_GCC_AND_CLANG_WARNING -#else -#define DISABLE_GCC_WARNING DISABLE_GCC_AND_CLANG_WARNING -#define DISABLE_CLANG_WARNING(w) -#endif - -#define DISABLE_GCC_AND_CLANG_WARNING(w) _Pragma(BOOST_PP_STRINGIZE(GCC diagnostic ignored BOOST_PP_STRINGIZE(-W##w))) - -#endif \ No newline at end of file diff --git a/contrib/epee/include/winobj.h b/contrib/epee/include/winobj.h deleted file mode 100644 index 3279cdac6e..0000000000 --- a/contrib/epee/include/winobj.h +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 __WINH_OBJ_H__ -#define __WINH_OBJ_H__ - -#include - -namespace epee -{ -class critical_region; - -class critical_section { - - boost::mutex m_section; - -public: - - critical_section( const critical_section& section ) { - InitializeCriticalSection( &m_section ); - } - - critical_section() { - InitializeCriticalSection( &m_section ); - } - - ~critical_section() { - DeleteCriticalSection( &m_section ); - } - - void lock() { - EnterCriticalSection( &m_section ); - } - - void unlock() { - LeaveCriticalSection( &m_section ); - } - - bool tryLock() { - return TryEnterCriticalSection( &m_section )? true:false; - } - - critical_section& operator=( const critical_section& section ) - { - return *this; - } - - -}; - -class critical_region { - - ::critical_section *m_locker; - - critical_region( const critical_region& ){} - -public: - - critical_region(critical_section &cs ) { - m_locker = &cs; - cs.lock(); - } - - ~critical_region() - { - m_locker->unlock(); - } -}; - - -class shared_critical_section -{ -public: - shared_critical_section() - { - ::InitializeSRWLock(&m_srw_lock); - } - ~shared_critical_section() - {} - - bool lock_shared() - { - AcquireSRWLockShared(&m_srw_lock); - return true; - } - bool unlock_shared() - { - ReleaseSRWLockShared(&m_srw_lock); - return true; - } - bool lock_exclusive() - { - ::AcquireSRWLockExclusive(&m_srw_lock); - return true; - } - bool unlock_exclusive() - { - ::ReleaseSRWLockExclusive(&m_srw_lock); - return true; - } -private: - SRWLOCK m_srw_lock; - -}; - - -class shared_guard -{ -public: - shared_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) - { - m_ref_sec.lock_shared(); - } - - ~shared_guard() - { - m_ref_sec.unlock_shared(); - } - -private: - shared_critical_section& m_ref_sec; -}; - - -class exclusive_guard -{ -public: - exclusive_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) - { - m_ref_sec.lock_exclusive(); - } - - ~exclusive_guard() - { - m_ref_sec.unlock_exclusive(); - } - -private: - shared_critical_section& m_ref_sec; -}; - - -class event -{ -public: - event() - { - m_hevent = ::CreateEvent(NULL, FALSE, FALSE, NULL); - } - ~event() - { - ::CloseHandle(m_hevent); - - } - - bool set() - { - return ::SetEvent(m_hevent) ? true:false; - } - - bool reset() - { - return ::ResetEvent(m_hevent) ? true:false; - } - - HANDLE get_handle() - { - return m_hevent; - } -private: - HANDLE m_hevent; - -}; - - -#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x) -#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x) - - - -#define CRITICAL_REGION_LOCAL(x) critical_region critical_region_var(x) -#define CRITICAL_REGION_BEGIN(x) { critical_region critical_region_var(x) -#define CRITICAL_REGION_END() } - - - inline const char* get_wait_for_result_as_text(DWORD res) - { - switch(res) - { - case WAIT_ABANDONED: return "WAIT_ABANDONED"; - case WAIT_TIMEOUT: return "WAIT_TIMEOUT"; - case WAIT_OBJECT_0: return "WAIT_OBJECT_0"; - case WAIT_OBJECT_0+1: return "WAIT_OBJECT_1"; - case WAIT_OBJECT_0+2: return "WAIT_OBJECT_2"; - default: - return "UNKNOWN CODE"; - } - - } - -}// namespace epee - -#endif diff --git a/contrib/epee/include/zlib_helper.h b/contrib/epee/include/zlib_helper.h deleted file mode 100644 index 46c7f48e61..0000000000 --- a/contrib/epee/include/zlib_helper.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 -extern "C" { -#include "zlib/zlib.h" -} -#pragma comment(lib, "zlibstat.lib") - -namespace epee -{ -namespace zlib_helper -{ - inline - bool pack(std::string& target){ - std::string result_packed_buff; - - z_stream zstream = {0}; - int ret = deflateInit(&zstream, Z_DEFAULT_COMPRESSION); - if(target.size()) - { - - - result_packed_buff.resize(target.size()*2, 'X'); - - zstream.next_in = (Bytef*)target.data(); - zstream.avail_in = (uInt)target.size(); - zstream.next_out = (Bytef*)result_packed_buff.data(); - zstream.avail_out = (uInt)result_packed_buff.size(); - - ret = deflate(&zstream, Z_FINISH); - CHECK_AND_ASSERT_MES(ret>=0, false, "Failed to deflate. err = " << ret); - - if(result_packed_buff.size() != zstream.avail_out) - result_packed_buff.resize(result_packed_buff.size()-zstream.avail_out); - - - result_packed_buff.erase(0, 2); - target.swap(result_packed_buff); - } - - deflateEnd(& zstream ); - return true; - } - - inline bool unpack(std::string& target) - { - z_stream zstream = {0}; - int ret = inflateInit(&zstream);// - - std::string decode_summary_buff; - size_t ungzip_buff_size = target.size() * 0x30; - std::string current_decode_buff(ungzip_buff_size, 'X'); - - while(target.size()) - { - - - zstream.next_out = (Bytef*)current_decode_buff.data(); - zstream.avail_out = (uInt)ungzip_buff_size; - - int flag = Z_SYNC_FLUSH; - - static char dummy_head[2] = - { - 0x8 + 0x7 * 0x10, - (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, - }; - zstream.next_in = (Bytef*) dummy_head; - zstream.avail_in = sizeof(dummy_head); - ret = inflate(&zstream, Z_NO_FLUSH); - if (ret != Z_OK) - { - LOCAL_ASSERT(0); - return false; - } - - zstream.next_in = (Bytef*)target.data(); - zstream.avail_in = (uInt)target.size(); - - ret = inflate(&zstream, Z_SYNC_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) - { - LOCAL_ASSERT(0); - return false; - } - - - target.erase(0, target.size()-zstream.avail_in); - - - if(ungzip_buff_size == zstream.avail_out) - { - LOG_ERROR("Can't unpack buffer"); - return false; - } - - - current_decode_buff.resize(ungzip_buff_size - zstream.avail_out); - if(decode_summary_buff.size()) - decode_summary_buff += current_decode_buff; - else - current_decode_buff.swap(decode_summary_buff); - - current_decode_buff.resize(ungzip_buff_size); - } - - inflateEnd(&zstream ); - - decode_summary_buff.swap(target); - return 1; - } - -}; -}//namespace epee diff --git a/contrib/epee/tests/.gitignore b/contrib/epee/tests/.gitignore deleted file mode 100644 index d9b4f015d3..0000000000 --- a/contrib/epee/tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build/* diff --git a/contrib/epee/tests/data/storages/invalid_storage_1.bin b/contrib/epee/tests/data/storages/invalid_storage_1.bin deleted file mode 100644 index fac7b3e97c6efbf757593b29c2f2c0653c501d69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109577 zcmeF))pu4`qv&A*v{0ulZK>k!?(Xg`0fIwtcXxMpcXxMpcXy|*C(pjw`~lxSWA~iJ z#WMmI$(!HB9Bay&%r08;&7~h^3=9d06&_l*MT=&UB8Nl`2@h@7xJ}#877aohH*Xs$ zYRIW*F(O6$i4lUxh=Qni2k#;pqT@Zhj}P!6KElWN1ioR6Pw^Q(M@)QyFYy&(;cI+@ zZ}ArwF7(v7jj`-*?K7n5%#;5oUpCcx|z?b+6vG6s%!MFGh-{S|w zMjXUNJj6!=Bt#-4MiL}NG9*U|q(myDMjE6=I;2MiWJD%pMiyj6He^Q*=HB?6p)I=?WqBiQFF2Ya` z_0a$g(Fl#v1WnNl;b@K)Xo*&6jW%eDc4&_d=!j0}j4tSkZs?94=!stFjXvm$e&~+@ z7>Gd_j3F3`VHl1P7>Q9BjWHODaTt#Yn21T3j47CkX_$@~n2A}KjX9W$d6pfzIEhm@jWallb2yI+xQI)*j4QZ`Yq*XZxQSc1jXSuDd$^AWc!)=Mj3;=CXLybm zc!^hdjW-ee_i);#ezOLVqC^de`Ty`>8zYecH!+eRDUu;MQXnN#AvMw@+Ar!Sy2XzsKdZ>>EXoyB=j3#J`W(Y@fv_MO=LTj`^TeL%abU;URLT7YA zS9C*n^gvJaLT~gzU-UzN48TAP!e9)+Pz=LxjKD~Y!f1@aSd7DXOu$4;!emUrR7}Hk z%)m^{!fedJT+G9KEWko6!eT7JQY^!AtiVdF!fLF+TCBr*Y`{ir!e(s2R&2v|?7&X! z!fx!rUhKnu9Kb;w!eJc2Q5?f@oWMz(!fBkrS)9XpT);(K!ev~+Rb0b$+`vuT!fo8a zUEITcJitRd!eczaQ#`|SyueGm!fU+2kN62c;}`sj-|##Bz@PXFf8!rS3W*>Dkr4$^ z@ebZaG(^XHcpo3&Lwtmf@d;w!Q+$Tc5ffkFOMHb`_!{5fTYQJ_@dIKb4&ovn;v)eP zA`ucJ36dfik|PCDA{A024bmbV(jx;hA`>zr3$h{`vLgp_A{TNa5Aq@(@}mF>q7VwB z2#TT@ilYQdq7+J_49cP$%A*1*q7o{j3aX+Ss-p&Kq836?8+A|@VW@}tXn=-jgvMxs zrf7z6G)D`xL@TsL8?;3`v_}VYL??7c7j#88bVm>LL@)G4AM`~(^v3`U#2^gD5DdjI z495tJ#3+o$7>va@jK>5_#3W3{6imf5OvenOCl9L&W$%*O&O#3C%l5-i0sEXNA0 z#44=D8mz@Stj7jy#3pRU7Hq{fY{w4l#4hZ{9_+L8U{D|^5EKXs1Ooks_Mg9M_)qnjR{C=e6~3j8Yz1oMLb&9k=Pvjqi$0zrYGKu{nk5EKXs1O@)@ z6bR-8|93hEKP4y-6bK3g1%d)WfuKN8ASm#^xxjyRUhx0)`})Bvq!DkCRP!cWb<{vj z)Ium~qYmmK4E0bS4bTvc&=^h76wMHh=4gSIXoc2jgSKdg_UM3)=!DMbg0AR>?&yJ@ z=!M?sgTCm8{uqFP7=*zXf}t3O;TVCD7=_UogRvNg@tA;#n1sogf~lB>>6n3;n1$Jx zgSnW8`B;F3ScJt`f~8o7$riNxP{xegS)tg`*?td zc!bAzf~R=HB?6p)I=?WqBiQFF2Ya`_0a$g(Fl#v1WnNl;b@K) zXo*&6jW%eDc4&_d=!j0}j4tSkZs?94=!stFjXvm$e&~+@7>Gd_j3F3`VHl1P7>Q9B zjWHODaTt#Yn21T3j47CkX_$@~n2A}KjX9W$d6pfzIEhm@jWallb2yI+ zxQI)*j4QZ`Yq*XZxQSc1jXSuDd$^AWc!)=Mj3;=CXLybmc!^hdjW-dz^$O{KT^kZ3 zxHiPdo(idv25FHF>5%~$kqMcR1zC{|*^vV|kqfzz2YHbX`B4A`Q3!=m1VvE{#Zdw! zQ3|C|24ztW)9hGPUqViZPW z48~#{#$y5|ViG1}3Z`Njreg+XVism&4(4JW=3@aCVi6W&36^3RmSY80Vii_n4c1~E z)?))UViPuF3$|h#wqpl&Vi$H}5B6do_TvB!;t&qw2#(?yj^hMQ;uKEf49?;l&f@|u z;u0?73a;WBuHy!7;udb>4({R}?&AR-;t?L>37+B^p5p~x;uT)w4SvK=_!+<8SNw+G z@dy6IU-%pUAW}#KA&879h>CacE}|hi-oyL&03YHbe2h;J1E1nEe2$p-0$<`Q#KPD3 z2H)a4e2*Ux8*va9@em&gkPwNG7)g*6$&ef=kP@ko8flOg>5v{7kP(@X8Cj4O*^nJM zkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq38f8!xR;36*JGOpk%uHiav;3jV2Htygq?%_Tj;2|F2 zF`nQlp5ZxO;3Zz+HQq$<)-zvlZOFg!Sor_uCFtO@1qFfvL4lw^P#`D}6bK6Z&lL!^ z`y-YWJeC< zL@wk;9^^$nXpau)h)(E?F6fGG=#C!fiC*Z9 zKIn^n=#K#yh(Q>PAsC8b7>*GbiBTAhF&K++7>@~Th(~yg zCwPiyc#ao%iC1`yH~0}h;b;7UU-27$#~=6;f8lTZgGeC}gdj4aAS&L$yNHJ9cn|O6 z1AK^&@G(9?419{u@Ht}Q3w(*M5DQ=98+?oJ@I8J&Y{Wra#6x@}Ktd!!VkALQBtvqf zKuV-SYNSD0q(gdSKt^OjW@JHDWJ7l3Ku+XBZsb8;8KuMHB zX_P@(ltXz`Kt)tSWmG{`R6}*tKuy#_C~Bh)>LLvFP#+D@5RK3nP0$q05RT?(ftF~6 z)@XyaXovRbfR5;d&gg=!=!Wj-fu87v-spqA=!gCofPol3~(fsq)6(HMiV z7>DtgfQgud$(Vwvn1<Q~(IEVANfQz_< z%eaE8xQ6Svft$F6+qi?fxQF|AfQNX5$9RILc!uYAftPrN*LV}bTT8#e{}PhIpq>(` zkQ!-_7U_^48ITc~kQrH!71@v-Igk^%kQ;fB7x|DM1yB%$P#8r}6va>+B~TKjP#R@W z7UfVL6;KhCP#INF71dB3HBb|^5Q^HUgSrSqJ=8}7G(;mbMiVqeGlZi#TA(Fbp*7l| zE!v?yI-nyup)xVVK??*FZN+S4&WdT;V_QiD30McPT(X?;WW0)oCB8x|e2s7LExyC| z_yMsI2XPS(@sR)tkqC*A1WAz$$&msnkqW7i25FHF>5%~$kqMcR1zC{|*^vV|kqfzz z2YHbX`B4A`Q3!=m1VvE{#Zdw!Q3|C|24ztW)9hGPUqViZPW48~#{#$y5|ViG1}3Z`Njreg+XVism&4(4JW=3@aC zVi6W&36^3RmSY80Vii_n4c1~E)?))UViPuF3$|h#wqpl&Vi$H}5B6do_TvB!;t&qw z2#(?yj^hMQ;uKEf49?;l&f@|u;u0?73a;WBuHy!7;udb>4({R}?&AR-;t?L>37+B^ zp5p~x;uT)wO$2W}^99@e!FGR8ASe(N2nqxRf&xK-pg>UIUsK>eyWJnm3;yR0W(40k zC=e6~3Iqj$0zrYGKu{nk@IP1JKRYi7ZvC?Hn+mCs25FHF>5%~$kqMcR1zC{|*^vV| zkqfzz2YHbX`B4A`Q3!=m1VvE{#Zdw!Q3|C|24ztW)9hGPUqViZPW48~#{#$y5|ViG1}3Z`Njreg+XVism&4(4JW z=3@aCVi6W&36^3RmSY80Vii_n4c1~E)?))UViPuF3$|h#wqpl&Vi$H}5B6do_TvB! z;t&qw2#(?yj^hMQ;uKEf49?;l&f@|u;u0?73a;WBuHy!7;udb>4({R}?&AR-;t?L> z37+B^p5p~x;uT)w4SvK=_!+<8SNw+G@dy6IU-%pUAW}#KA&879h>CacE}|hi-oyL& z03YHbe2h;J1E1nEe2$p-0$<`Q#KPD32H)a4e2*Ux8*va9@em&gkPwNG7)g*6$&ef= zkP@ko8flOg>5v{7kP(@X8Cj4O*^nJMkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq3 z8f8!xR;36*J zGOpk%uHiav;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO;3Zz+HQq$<*3$2P9RrRY-1=o? zPleP-gS1G8^vHmW$b`(uf~?4f?8t$f$c5af~u&7>ZpO5sD)6}Mjg~e80w)u8lWK>p)s1EDViZ1&Cvoa(F(26 z25r#}?a=`p(FvW=1zph%-O&R*(F?uN2Yt~G{V@OoF$jY(1Vb?l!!ZIQF$$wG24gV} z<1qmfF$t3~1yeB%(=h`xF$=RX2XiqG^RWO6u?UN?1WU0D%drA0u?nlP25Yen>#+eF zu?d^81zWKV+pz;Xu?xGg2Yay(`*8pVaR`TT1V?cU$8iEDaSEq#24`^&=WziSaS4}k z1y^wm*Kq?kaSOL`2X}D~_wfJ^@d%Ic1W)k{&+!5;@d~f;20!8_{ET1lD}KZ8_yd39 zFZ_*v5Gf>r5JW~4M8!LJ7ts(M@8NxXfDiEzKE@}Aflu)nK1WP^fiLkDV&Q9igKzO2 zzQ+%UjW~#lc!-Y#NQgv8j3h{kWJrz_NQqQPjWkG$bV!d3$cRkHj4a5CY{-rr$cbFY zjXcPUe8`UiD2PHRj3OwCVknLhD2Y-ijWQ^Uawv}qsEA6aj4G&#YN(DHsEJw#MQzkU zU4)??>Z1V~q7fRS37VoA!qFTp&=RfC8g0-P?a&?_&=H-`8C}p7-OwF9&=bAT8-36h z{m>r+Fc5<<7(*}=!!R5pFcPCM8e=dP<1ii*FcFh58B;J7(=Z(~FcY&d8*?xh^DrL^ zun>!|7)!7e%di|PuoA1V8f&l?>#!ahuo0WE8C$Rw+prxwuoJtm8+))9`>-Dea1e)Z z7)Njv$8a1ca1y6*8fS18=Wreua1obq8CP%>*Ki#-a1*z18+ULQ_i!H%@DPvi7*FsN z&+r^C@Di`^8gC+a>zOY&2K=u%^6@{nXdHa!pg>R{C=e6~3Iqj$0zrX)OMzg!KVs>_ zs!VLeL0rT`d?Y|ZBtl{&K~f|`a-={?q(W+>L0Y6kdSpOGWI|?SK~`i#cH}@#kb<{vj)Ium~qYmmK z4E0bS4bTvc&=^h76wMHh=4gSIXoc2jgSKdg_UM3)=!DMbg0AR>?&yJ@=!M?sgTCm8 z{uqFP7=*zXf}t3O;TVCD7=_UogRvNg@tA;#n1sogf~lB>>6n3;n1$JxgSnW8`B;F3 zScJt`f~8o7$riNxP{xegS)tg`*?tdc!bAzf~R=HB?6p)I=?WqBiQFF2Ya`_0a$g(Fl#v1WnNl;b@K)Xo*&6jW%eD zc4&_d=!j0}j4tSkZs?94=!stFjXvm$e&~+@7>Gd_j3F3`VHl1P7>Q9BjWHODaTt#Y zn21T3j47CkX_$@~n2A}KjX9W$d6pfzIEhm@jWallb2yI+xQI)*j4QZ` zYq*XZxQSc1jXSuDd$^AWc!)=Mj3;=CXLybmc!^hdjW-dzwe%a@`jx_5v{7kP(@X8Cj4O*^nJMkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq38f8!xR;36*JGOpk%uHiav z;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO;3Zz+HQwMy{DhzJ3x36K_#J=XPyB_y@ed+} zL=b|=h=Qni2k#;pqT@Zhj}P!6KElWN1TpX_KEvmTi7)UazCtW~jc@QRzQgzU0kIJW zaS;#kkpKyi2#JvdNs$c6kpd}^3aOC>X^{@;kpUTz37L@vS&CfiG($L=qXk-`6{x}qDpqX&AT7kZ-)`l28DV*mzX z5C&rihGH0oV+2NG6h>nV#$p`CV*(~(5+-8`reYeVV+Lko7G`4(=3*Y^V*wUo5f)60>c7ISHC=e6~3Iqj$0zrYGKv3XcQ{X?l-5<;g{w>Emg3lQg2nqxR zf&xK-pg>R{C=e8QyFf56c)LdMbx5v{7kP(@X8Cj4O*^nJMkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq38f8!x zR;36*JGOpk% zuHiav;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO;3Zz+HQwMy{DhzJ3x36K_#J=XPyB_y z@ed+}L=b|=h=Qni2k#;pqT@Zhj}P!6KElWN1TpX_KEvmTi7)UazCtW~jc@QRzQgzU z0kIJWaS;#kkpKyi2#JvdNs$c6kpd}^3aOC>X^{@;kpUTz37L@vS&CfiG($L=qXk-`6{x}qDpqX&AT7kZ-)`l28D zV*mzX5C&rihGH0oV+2NG6h>nV#$p`CV*(~(5+-8`reYeVV+Lko7G`4(=3*Y^V*wUo z5f)8KuMHBX_P@(ltXz`Kt)tSWmG{`R6}*tKuy#_C~Bh)>LLvF zP#+D@5RK3nP0$q05RT?(ftF~6)@XyaXovRbfR5;d&gg=!=!Wj-fu87v-spqA=!gCo zfPol3~(fsq)6(HMiV7>DtgfQgud$(Vwvn1<Q~(IEVANfQz_<%eaE8xQ6Svft$F6+qi?fxQF|AfQNX5$9RILc!uYA zftPrN*LZ^;@e_W=FZdO|;dlIjKk*m-#y^M@5rL0K0d&Q_y`~4 z6U4x$_za&TCceOz_zJP`HNL^O_zvIW2gF7k#6>*BM*<{7A|yrCS*nyWJNY)M-JpfF62fYArwXt6h$!Yy&dP!ILd01eRyjnM>6(G1~ejuvQ%R%nejXp44e zj}GXFPUws-=!$OWjvnZVUg(WJ=!<^nj{z8nK^Tl77>Z#Sju9A%Q5cOe7>jWjj|rHF zNtlc&n2Kqbjv1JVS(uGEn2ULsj|EtWMOcg_Sc+v>julvmRalKRSc`R7j}6#}P1uYr z*otk~jvd&EUD%C1*o%GGj{`V}LpY2hIErI9juSYEQ#g$?IE!;Qj|;enOSp_HxQc7I zjvKg%TeyuoxQlzZj|X^&M|g}Uc#3Cuju&`|S9py#5xn)x7rY+$uQ>|-|L1~s@LLB3 zf&xK-pg>R{C=e6~3cOt)*zS*5`mib!8*va9@em&gkPwNG7)g*6$&ef=kP@ko8flOg z>5v{7kP(@X8Cj4O*^nJMkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq38f8!xR;36*JGOpk%uHiav z;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO;3Zz+HQwMy{DhzJ3x36K_#J=XPyB_y@ed+} zL=b|=h=Qni2k#;pqT@Zhj}P!6KElWN1TpX_KEvmTi7)UazCtW~jc@QRzQgzU0kIJW zaS;#kkpKyi2#JvdNs$c6kpd}^3aOC>X^{@;kpUTz37L@vS&CfiG($L=qXk-`6{x}qDpqX&AT7kZ-)`l28DV*mzX z5C&rihGH0oV+2NG6h>nV#$p`CV*(~(5+-8`reYeVV+Lko7G`4(=3*Y^V*wUo5f)CS*nyWJNY)M-JpfF62fY zArwXt6h$!Yy&dP!ILd01eRy zjnM>6(G1~ejuvQ%R%nejXp44ej}GXFPUws-=!$OWjvnZVUg(WJ=!<^nj{z8nK^Tl7 z7>Z#Sju9A%Q5cOe7>jWjj|rHFNtlc&n2Kqbjv1JVS(uGEn2ULsj|EtWMOcg_Sc+v> zjulvmRalKRSc`R7j}6#}P1uYr*otk~jvd&EUD%C1*o%GGj{`V}LpY2hIErI9juSYE zQ#g$?IE!;Qj|;enOSp_HxQc7IjvKg%TeyuoxQlzZj|X^&M|g}Uc#3Cuju&`|S9py# z_z^$hXZ(U+@f&`}ANUi0;cxtdNFfn~ATpvLD&E1nh=%BR5AWjxe29Ie2UNT zIbz}qe2K3R3t!_Ke2ee!J$^uJ#6eudLwqDaLL@?BBtcRnLvo}*N~A(+q(NGwLwaOD zMr1-}WIt^6hToGLvfTqNt8lqltEdPLwQs{MN~p% zR6$i#Lv_?ZP1HgtYNHP7A`JCV9}UnDjnEiP&=k!Oj^=2AmS~06XoI$BhxX`zj_8EW z=z^~3hVJNrp6G?%=!3rKhyECVff$6r7=ob~hT#~2kr;*17=y7Ghw+$ziI{}Rn1ZR8 zhUu7rnV5yyn1i{Phxu55g;<2eSc0WkhUHj+l~{$;hy6H!gE)l4ID(@%hT}MalQ@ObID@k|hx53Ai@1c#xPq&=hU>V2o4AGB zxP!a6hx>Sdhj@g?c!H;RhUa*Jmw1KOcoV@}&wRmlf3V#j6bK3g1%d)WfuKN8ASe(N z_}3Kp&u;ez^MbdJQUqTI1%d)WfuKN8ASe(N2nqxR{w)Rmv-5)B)-M~ssgN3JkQV8X z9vP4knUEP-kQLdG9XXH_xsV%qkQe!o9|celg-{qpP!z>b93@Z^rBE7WP!{D-9u-g# zl~5T~P!-is9W_uBwGfKhsDru)Lp{_-12jYIa;74TA?-Cpe@>=JvyKx zI-xVVpewqeJ9?ledZ9P^pfCENKL%hR24OIUU?_%RI7VP3MqxC@U@XRAJSJcwCSfwB zU@E3zI%Z%dW??qwU@qoiJ{Djh7GW`#U@4YiIaXjLR$(>PU@g{RJvLw?HeoZiU@Nv^ zJ9c0vc40U6U@!JzKMvp^4&gA4;3$saI8NXsPT@4p;4IGJJTBlOF5xn+;3}@+I&R=5 zZs9iW;4bdrJ|5s99^o;b;3=NrIbPr;Ug0&~;79y~pYaQR#c%i>f8bC2g}?C+B85Z{ zg2;%1sCWnOA{wIOJ-m+(@F70J$M^&>@F_mS=ZJ|f@Fl)NEPRb`@GZW>_xJ&^5eIP* z5Al%z36ThikpxMR49SrKDUk}Pkp^jz4(X8r8IcK@kp)?i4cU6bB~c2cQ3hpE4&_k+6;TP5Q3X{|4b@QtHBk$psEs)aV-40~9oAz5HewStV+*!o8@6Kyc48NHV-NOXANJz_4&o3F;|Px87>?rv zPT~|!;|$K?9M0ncF5(g{;|i|g8m{98ZsHbh;|}iP9`54-9^w%m;|ZSP8J^<>Ug8yA z<4pu_E&cx2G2rOItzSm=R7j09NQ-nxj||9&OvsEZ$ck*pjvUB|T*!?)$cuc)j{+!& zLMV(PD2iezjuI$|QYeiwD2s9^j|!-WN~nw~sETT+jvAvC9|JHDgD@CF zFciZu93wCiqc9p{Fc#x59uqJTlQ0=mFcs4<9WyW!voITTFc4JD1)*nhw`X^il~Ij zsDi4fhU%z+ny7_P)J7fDMHuR#J{q7Q8lf?opedRm9L>=JEzt_C(FSeN4(-ta9nlG$ z(FI-64c*ZLJ<$uj(Fc9e5B)I!12G7LF$6;~48t)3BQXl2F$QBX4&yNa6EO*sF$GgG z4bw3LGcgOZF$Z%o5A(4A3$X}`u>?!849l?sE3pczu?B0g4(qW28?gzSu?1VP4coB; zJFyG9u?Ksx5BqTd2XP38aRf(k499T-Cvgg=aRz5`4(D+J7jX%faRpa#4cBo4H*pKM zaR+yC5BKo^5Ag_(@dQut4A1cbFYyYm@g{<|p80}f!2g;fAODsm=-_h(1%d)WfuKN8 zASe(N2nzhq6$rNbBbGj_%EU$-#6>*BM*<{7A|yr zCS*nyWJNY)M-JpfF62fYArwXt6h$!Yy&dP!ILd01eRyjnM>6(G1~ejuvQ%R%nejXp44ej}GXFPUws- z=!$OWjvnZVUg(WJ=!<^nj{z8nK^Tl77>Z#Sju9A%Q5cOe7>jWjj|rHFNtlc&n2Kqb zjv1JVS(uGEn2ULsj|EtWMOcg_Sc+v>julvmRalKRSc`R7j}6#}P1uYr*otk~jvd&E zUD%C1*o%GGj{`V}LpY2hIErI9juSYEQ#g$?IE!;Qj|;enOSp_HxQc7IjvKg%Teyuo zxQlzZj|X^&M|g}Uc#3Cuju&`|S9py#_z^$hXZ(U+@f&`}ANUi0;cxtdNFfn~ATpvL zD&E1nh=%BR5AWjxe29Ie2UNTIbz}qe2K3R3t!_Ke2ee!J$^uJ#6eudLwqDa zLL@?BBtcRnLvo}*N~A(+q(NGwLwaODMr1-}WIt^ z6hToGLvfTqNt8lqltEdPLwQs{MN~p%R6$i#Lv_?ZP1HgtYNHP7A`JCV9}UnDjnEiP z&=k!Oj^=2AmS~06XoI$BhxX`zj_8EW=z^~3hVJNrp6G?%=!3rKhyECVff$6r7=ob~ zhT#~2kr;*17=y7Ghw+$ziI{}Rn1ZR8hUu7rnV5yyn1i{Phxu55g;<2eSc0WkhUHj+ zl~{$;hy6H!gE)l4ID(@%hT}MalQ@Ob zID@k|hx53Ai@1c#xPq&=hU>V2o4AGBxP!a6hx>Sdhj@g?c!H;RhUa*Jmw1KOcoV@} zOTWRbUnvagDUk}Pkp^jz4(X8r8IcK@kp)?i4cU6bB~c2cQ3hpE4&_k+6;TP5Q3X{|4b@QtHBk$psEs)a zV-40~9oAz5HewStV+*!o8@6Kyc48NHV-NOXANJz_4&o3F;|Px87>?rvPT~|!;|$K? z9M0ncF5(g{;|i|g8m{98ZsHbh;|}iP9`54-9^w%m;|ZSP8J^<>Ug8yA;|+eqPxu+X z;8*;H-|+|j#9#Ou{~%IG1R;owD2R%8@GhbuI^M(k_y8Z`BYccc5Cfm$GklJi_yS+z zE5yRr_y*tNJA98H5F2q27x54u36KzpkQhmj6v>brDUcGWkQ!-_7U_^48ITc~kQrH! z71@v-Igk^%kQ;fB7x|DM1yB%$P#8r}6va>+B~TKjP#R@W7UfVL6;KhCP#INF71dB3 zHBb|^5Q^HUgSrSqJ=8}7G(;mbMiVqeGlZi#TA(Fbp*7l|E!v?yI-nyup)xVVK??* zFZN+S4&WdT;V_QiD30McPT(X?;WWOQBC63IN2mavQ2i$nCz27a!gLiIk_ZLa!qc@J(-h7@=RXI zJNYEvC4b+1p8Gxb6Cgl<009C72oPu^uTFFW zK!5-N0t5&UAkam?UeM*HoRa_n0t5&UAV7dXoq)Zd&emiC1PBlyK!5-N0$l{`1zm2+ VISCLTK!5-N0t5)u39P#p`~YH9P(uI! diff --git a/contrib/epee/tests/data/storages/invalid_storage_2.bin b/contrib/epee/tests/data/storages/invalid_storage_2.bin deleted file mode 100644 index a8c29f155ab0634fedda8b5a8424f4611c6dbf1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20 bcmZ4F>HGPn-24tkMv2_`r2PCG1|}{5SEUB% diff --git a/contrib/epee/tests/data/storages/invalid_storage_3.bin b/contrib/epee/tests/data/storages/invalid_storage_3.bin deleted file mode 100644 index b5c31aa058..0000000000 --- a/contrib/epee/tests/data/storages/invalid_storage_3.bin +++ /dev/null @@ -1 +0,0 @@ -¢IMóÙŸˆm_bo \ No newline at end of file diff --git a/contrib/epee/tests/data/storages/invalid_storage_4.bin b/contrib/epee/tests/data/storages/invalid_storage_4.bin deleted file mode 100644 index 4f8372d1978598fcbcbc6a73a5d76ee90c3d6936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18 ZcmZ4F>HGPn-24tkMv2_`r2PCG1^`Ll2HXGu diff --git a/contrib/epee/tests/data/storages/valid_storage.bin b/contrib/epee/tests/data/storages/valid_storage.bin deleted file mode 100644 index e13f780b17a5a6253dfbf52560d240339ea91b50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180345 zcmeF)Rg6{HqNrgC=!SN>n|8t7-QC??3Md>3E8N}P-QC^Y-QC@tcD?oOm)kGr~kg$U&2a&T7M;jVQbdxVx`_(`hLd1kdQCKL+iC{**r?rkmw=dq0O5_v<+?9FtkaF zwo#&ooQe@EO60FtA&81-h>o}LHew(q-od+g5AWjxe29noP;vqf~AR!VVF_IuDk|8-#ASF^EHPRq0(jh%EAR{s%GqNBnvLQQi zASZGmH}W7a@*zJ8pdbpNFp8ikilI14pd?D6G|HeX%Aq_epdu=vGOC~|s-Ze+peAae zHbPMcbx{vtsE-C{h(>6PCTNOgXpV5SKufejYqUWG+M*rWqXRmk6FQ>{x}qDpqX&AT z7kZ-)`l28DV*mzX5C&rihGH0oV+2NG6h>nV#$p`CV*(~(5+-8`reYeVV+Lko7G`4( z=3*Y^V*wUo5f)B>4j<@hOVjw2o z!Mk`5@8bh}h>u__jrB1;!Ka9g&+s|Ez?b+6U*j8mi|_C~;vg>KAwCiyArc`mk{~IP zAvsbYB~l?Z(jYC;Aw4o6BQhZ~vLGw6Av=dVjRX}0w!V-CSwYwVj8An24-RwW@8TKVjkvW0TyBr7GnvPVi}fW1y*7eR$~p; zVjb3F12$q4He(C6VjH$&2XxV zVK??*FZN+S4&WdT;V_QiD30McPT(X?;WW zcnfbM24dnJyo>knK0d&Q_z1D^F+Rbkh>g$iIljP`_zGX+8+?oJ@IB%nF5)3R5+ETG zAu*C5DUu;MQXnN#AvMw@+p*BKM2X#>oVW^J=XoyB=j3#J` zW@wIZv_MO=LTj`^1lpn<+M@$Hq7yo!3%a5kx}yhrq8ECj5Bj1X`eOhFVh{#n2!>)9 zhGPUqViZPW48~#{#$y5|ViG1}3Z`Njreg+XVism&4(4JW=3@aCVi6W&36^3RmSY80 zVii_n4c1~E)?))UViPuF3$|h#wqpl&Vi$H}5B6do_TvB!;t&qw2#(?yj^hMQ;uKEf z49?;l&f@|u;u0?73a;WBuHy!7;udb>4({R}?&AR-;t?L>37+B^p5p~x;uT)wO(g%A z^R;agC3@6TF=9tVgoV`)3R{C=e6~3IqlII~NEKjfkAVM~!;5ZdkLr z;q}9kGz)JK)*w7AlCbc)VUfQH4-2nRw@D-|8~k5e|Lc|3EgOV~wsV*tH6+&8BceqK zj{9x!q(&N~MLMKM24qAgWJVTbMK)wd4&+2GOR7Mq4MKx4M4b(&})J7=kpf2hm4E50f4bcdV(F9G=49yXa7HEl9 zXpJ_AKwGp!dvriYbV6rzL05D`cl1C{^g?g+L0|Mke+L0Y6kdSpOGWI|?SK~`i#cH}@# zkb<{vj)Ix29 zq7Le!9>P!`4bTvc&=^h76wS~a;b?)DXoc2jg9x-mJG4g!bVMg~Mi+ENH*`l2^h7W8 zMj!M=KlH}{48$M|#t;m}Fbu~CjKnC6#u$vnIE=>xOvEHi#uQA&G)%_~%)~6r#vIJW zJj}-eEW{!##u6;WGAzdmti&p;#u}`}I;_VAY{VvP#ujYFHf+ZZ?8GkY#vbg&KJ3Q< z9K<0U#t|IFF&xJUoWv=d#u=Q&Ih@A@T*M_@#uZ$}HC)FH+{7*1#vR16wJj5eB z#uGfnGd#x&yu>TK#+yj~vGx0}6PCTNOgXpV5SKufejYqUWG+M*rWqXRmk6FQ>{x}qDpqX&AT7kZ-) z`l28DV*mzX5C&rihGH0oV+2NG6h>nV#$p`CV*(~(5+-8`reYeVV+Lko7G`4(=3*Y^ zV*wUo5f)B>4j<@hOVjw2o!Mk`5 z@8bh}h>s8pALA2zirDxJpW_RBiLdZAzQMQn4&NgV;vyd6BLNa35fUQ_k|G(BBLz|- z6;dM&(jpzwBLgxb6EY(UvLYL@BL{LK7jh#H@**GdqW}t`5DKFRilP{bqXbH#6iTBE z%Ay>~qXH_T5-Ot#s-haIqXufC7HT6Dbx;@e5Qh3_fQD#<#%O}3XoltpM+>w>E3`%% zM4&C&p*=dFBRZiox}Yn%p*wn@Cwieb`k*iRp+5#-AO>MDhF~a$VK_!$Bt~I0#$YVQ zVLT>aA|_!nreG?jVLE1DCT3wa=3p-7VLldMAr@gVmS8ECVL4V{C01cI)?h8xVLdir zBQ{|(wqPr^VLNtUCw5^s_FymeVLuMwAP(U$j^HSc;W$pMCT`(2?%*!&;XWSVAs*o|p5Q5-;W=L5C0^k*-bC_`IbU$xA6#+>3Iqj$ z0zrYGKu{nk5EKXs{Kpjd_a65L>w^EzS382QEhrEa2nqxRf&xK-pg>R{DDYnu`1h_0 zf@{DIep4e2(jpzwBLgxb6EY(UvLYL@BL{LK7jh#H@**GdqW}t`5DKFRilP{bqXbH# z6iTBE%Ay>~qXH_T5-Ot#s-haIqXufC7HT6Dbx;@e5Qh3_fQD#<#%O}3XoltpM+>w> zE3`%%M4&C&p*=dFBRZiox}Yn%p*wn@Cwieb`k*iRp+5#-AO>MDhF~a$VK_!$Bt~I0 z#$YVQVLT>aA|_!nreG?jVLE1DCT3wa=3p-7VLldMAr@gVmS8ECVL4V{C01cI)?h8x zVLdirBQ{|(wqPr^VLNtUCw5^s_FymeVLuMwAP(U$j^HSc;W$pMCT`(2?%*!&;XWSVAs*o|p5Q5-;W=L5C0^k*-rxuPh@bE?e!;K! z4Zq_L{E5HtH==|@5`w6RhUj<;ZzBd`;vKw;_wYVGz=!wKAwCiyArc`mk{~IPAvsbYB~l?Z(jYC;Aw4o6BQhZ~vLGw6Av=dVjRX}0w!V-CSwYwVj8An24-RwW@8TK zVjkvW0TyBr7GnvPVi}fW1y*7eR$~p;Vjb3F12$q4He(C6VjH$&2XkLSLL@)G4 zAM`~(^v3`U#2^gD5DdjI495tJ#3+o$7>va@jK>5_#3W3{6imf5OvenOCl9L&W$ z%*O&O#3C%l5-i0sEXNA0#44=D8mz@Stj7jy#3pRU7Hq{fY{w4l#4hZ{9_+WJeCLCpE(Ett62#wJMP0KWK6+SOv7}{z)Z}-Y|O!2%)@*vz(Op-Vl2T@EW>iFz)GybYOKLptiyV2 zz(#DsW^BP$Y{Pc!z)tMKZtTHc?8AN>z(E|sVI09x9K&&(z)76KX`I1XoWprsz(rib zWn95kT*GzTz)jr3ZQQ|K+{1l5z(YL3V?4oAJi~Lmz)QTsYrKi%A9KFo8t{M2m5={w z%QtvEC=e6~3Iqj$0zrYGKv3X+Pl4dLKXU8Cu1p-nMLfhu0whEtBt{Y>MKUBu3Zz6T zq(&N~MLMKM24qAgWJVTbMK)wd4&+2GOR7Mq4MKx4M4b(&})J7=kpf2hm4E50f4bcdV(F9G=49yXa7HEl9XpJ_A zKwGp!dvriYbV6rzL05D`cl1C{^g?g+L0|Mke+L0Y6kdSpOGWI|?SK~`i#cH}@#kb<{vj)Ix29q7Le! z9>P!`4bTvc&=^h76wS~a;b?)DXoc2jg9x-mJG4g!bVMg~Mi+ENH*`l2^h7W8Mj!M= zKlH}{48$M|#t;m}Fbu~CjKnC6#u$vnIE=>xOvEHi#uQA&G)%_~%)~6r#vIJWJj}-e zEW{!##u6;WGAzdmti&p;#u}`}I;_VAY{VvP#ujYFHf+ZZ?8GkY#vbg&KJ3Q<9K<0U z#t|IFF&xJUoWv=d#u=Q&Ih@A@T*M_@#uZ$}HC)FH+{7*1#vR16wJj5eB#uGfn zGd#x&yu>TK#+yj~vGp4~^(&=8Jrzzr3$h{`vLgp_A{TNa5Aq@( z@}mF>q7VwB2#TT@ilYQdq7+J_49cP$%A*1*q7o{j3aX+Ss-p&Kq84f+6m?J+^$>>o zXn=-jgvMxsrf7!d2uBOFL@TsL8$_Tj+MzuR;36*JGOpk%uHiav;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO z;3Zz+HQwL{{D`0MGk(FZ_zl0~5B!P0@He7_L=u9ih=%BR3vVL^V&WaVi}&z8KEQ|g z2(j=nKEbDmjnD8ozQC9G3SZ+Je2ee!J>noP;vqf~AR!VVF_IuDk|8-#ASF^EHPRq0 z(jh%EAR{s%GqNBnvLQQiASZGmH}W7a@*zJ8pdbpNFp8ikilI14pd?D6G|HeX%Aq_e zpdu=vGOC~|s-Ze+peAaeHbPMcbx{vtsE-C{h(>6PCTNOgXpV5SKufejYqUWG+M*rW zqXRmk6FQ>{x}qDpqX&AT7kZ-)`l28DV*mzX5C&rihGH0oV+2NG6h>nV#$p`CV*(~( z5+-8`reYeVV+Lko7G`4(=3*Y^V*wUo5f)$N61;_otaeq)CC=e6~3Iqj$ z0zrYGKv3X6rog}VxIb7I{O`Fa61>x(Ku{nk5EKXs1Ow@5Ez%GQQMjE6=I;2MiWJD%pMiyj6He^Q*=HB?6p)I=@RMkwl_ zF6to+_0a$g(Fl#v1WnNl%@K|kXo*&6jW&otTeL%abU;URLT7YAS9C*n^gvJaLT~gz zU-UzN48TAP!e9)+Pz=LxjKD~Y!f1@aSd7DXOu$4;!emUrR7}Hk%)m^{!fedJT+G9K zEWko6!eT7JQY^!AtiVdF!fLF+TCBr*Y`{ir!e(s2R&2v|?7&X!!fx!rUhKnu9Kb;w z!eJc2Q5?f@oWMz(!fBkrS)9XpT);(K!ev~+Rb0b$+`vuT!fo8aUEITcJitRd!ecza zQ#`|SyueGm!fU+25BL#3;b;7UU-27$#~=6;f8lRL35g^GQ4tN%@fO}j48+7cco*;C zeSClq@eyL-V|;>75gVW3b9{j>@fE(tH~1Fc;d{hET*O0sBtSwWLSiIAQY1riq(Dlf zLTaQzTBJjIWI#q_LS|$^R%AnVkLSLL@)G4AM`~(^v3`U#2^gD5DdjI495tJ#3+o$7>va@ zjK>5_#3W3{6imf5OvenOCl9L&W$%*O&O#3C%l5-i0sEXNA0#44=D8mz@Stj7jy z#3pRU7Hq{fY{w4l#4hZ{9_+6PCTNOgXpV5SKufejYqUWG z+M*rWqXRmk6FQ>{x}qDpqX&AT7kZ-)`l28DV*mzX5C&rihGH0oV+2NG6h>nV#$p`C zV*(~(5+-8`reYeVV+Lko7G`4(=3*Y^V*wUo5f)B>4j<@hOVjw2o!Mk`5@8bh}h>s8pALA2zirDxJpW_RBiLdZAzQMQn z4&NgV;vyd6BLNa35fUQ_k|G(BBLz|-6;dM&(jpzwBLgxb6EY(UvLYL@BL{LK7jh#H z@**GdqW}t`5DKFRilP{bqXbH#6iTBE%Ay>~qXH_T5-Ot#s-haIqXufC7HT6Dbx;@e z5Qh3_fQD#<#%O}3XoltpM+>w>E3`%%M4&C&p*=dFBRZiox}Yn%p*wn@Cwieb`k*iR zp+5#-AO>MDhF~a$VK_!$Bt~I0#$YVQVLT>aA|_!nreG?jVLE1DCT3wa=3p-7VLldM zAr@gVmS8ECVL4V{C01cI)?h8xVLdirBQ{|(wqPr^VLNtUCw5^s_FymeVLuMwAP(U$ zj^HSc;W$pMCT`(2?%*!&;XWSVAs*o|p5Q5- z;W=L5C0^k*-bC_`IbZPc!2g)5;QwzM!oi;k3Iqj$0zrYGKu{nk5ES^IR3JF+kKFpO zD-#EC5fAZ^011%@iID_JkqpU^0x6LSsgVY0kq+sR0U41AnUMuqkqz0A138fkxseBX zkq`M%00mJ9g;4}WQ4GaV0wqxjrBMcDQ4Zx%0TodRl~Dy%Q4Q5m12s_#wGoOssEc|C zLwz(rLo`BTG(l4|Lvw_q1zMsNTB8jj&=&2`9v#pTozNLw&=uX#9X-$!z0ezd&=>vC z9|JHDgD@CFFciZu93wCiqc9p{Fc#x59uqJTlQ0=mFcs4<9WyW!voITTFcdKK~zLTbi9SP5d$&t4&KFkcpo3& zLwtl-_!ytyQ^dw+_#9v0OMHc|@eRJkclaK05Etb93@Z^rBE7WP!{D- z9u-g#l~5T~P!-is9W_uBwNM+OsDrwwhcMJf12jY?!849l?sE3pczu?B0g4(qW28?gzS zu?1VP4coB;JFyG9u?Ksx5BqTd2XP38aRf(k499T-Cvgg=aRz5`4(D+J7jX%faRpa# z4cBo4H*pKMaR+yC5BKo^5Ag_(@dQut4A1cbFYyYm@g|agZ2bmL15RmBPleP-gS1G8 z^vHmW$b`(uf~?4f?8t$f$c5af~u&7>ZpO5sD;`HMIF>dJ%ph?8lWK>p)s1EDVm`Gd_j3F3`VHl1P7>Q9BjWHODaTt#Yn21T3 zj47CkX_$@~n2A}KjX9W$d6pfzIEhm@jWallb2yI+xQI)*j4QZ`Yq*XZ zxQSc1jXSuDd$^AWc!)=Mj3;=CXLybmc!^hdjW_rKKjJ6+j9>68e#7th1ApQ#{Ea9f zk%S;Bq9Hoo!rO>}n0N>8;yt{N5AY#ALM(iYPw**X<1>7YFYqP4!q@l)-{L!bk2r{n zc!-Y#NQgv8j3h{kWJrz_NQqQPjWkG$bV!d3$cRkHj4a5CY{-rr$cbFYjXcPUe8`Ui zD2PHRj3OwCVknLhD2Y-ijWQ^Uawv}qsEA6aj4G&#YN(DHsEJyrjZoAZ1V~ zq7fRS37VoAnj;)7&=RfC8f_4PwrGd;=zxysgwE)KuIPsD=z*T-h2H3czUYVk7=VEo zguxhsp%{kY7=e)(h0z#;u^5N(n1G3xgvpqKshEc8n1Pv?h1r;cxtNFfSb&9CgvD5b zrC5gLSb>#Th1FPtwOEJs*no}Lgw5E3t=NX`*nyqch27YLz1WBSIDmsVgu^(3qd11+ zIDwNmh0{2Lvp9$IxPXhegv+>stGI^ixPhCvh1f~u&7>ZpO5sD;`HMIF>dJ%ph? z8lWK>p)s1EDVm`Gd_j3F3`VHl1P7>Q9BjWHODaTt#Yn21T3j47CkX_$@~n2A}KjX9W$d6pfzIEhm@jWallb2yI+xQI)*j4QZ`Yq*XZxQSc1jXSuDd$^AWc!)=Mj3;=CXLybm zc!^hdjW_rKKjJ6+j9>68e#7th1ApQ#{Ea9fk%S;Bq9Hoo!rO>}n0N>8;yt{N5AY#A zLM(iYPw**X<1>7YFYqP4!q@l)-{L!bk2r{nc!-Y#NQgv8j3h{kWJrz_NQqQPjWkG$ zbV!d3$cRkHj4a5CY{-rr$cbFYjXcPUe8`UiD2PHRj3OwCVknLhD2Y-ijWQ^Uawv}q zsEA6aj4G&#YN(DHsEJyrjZoAZ1V~q7fRS37VoAnj;)7&=RfC8f_4PwrGd; z=zxysgwE)KuIPsD=z*T-h2H3czUYVk7=VEoguxhsp%{kY7=e)(h0z#;u^5N(n1G3x zgvpqKshEc8n1Pv?h1r;cxtNFfSb&9CgvD5brC5gLSb>#Th1FPtwOEJs*no}Lgw5E3 zt=NX`*nyqch27YLz1WBSIDmsVgu^(3qd11+IDwNmh0{2Lvp9$IxPXhegv+>stGI^i zxPhCvh1kb<{vj)Ix29q7Le!9>P!`4bTvc&=^h76wS~a;b?)DXoc2jg9x-mJG4g! zbVMg~Mi+ENH*`l2^h7W8Mj!M=KlH}{48$M|#t;m}Fbu~CjKnC6#u$vnIE=>xOvEHi z#uQA&G)%_~%)~6r#vIJWJj}-eEW{!##u6;WGAzdmti&p;#u}`}I;_VAY{VvP#ujYF zHf+ZZ?8GkY#vbg&KJ3Q<9K<0U#t|IFF&xJUoWv=d#u=Q&Ih@A@T*M_@#uZ$}HC)FH z+{7*1#vR16wJj5eB#uGfnGd#x&yu>TK#vA;AAMq1@#xM94zu|ZMfj{vV{zjCL zNJ0=5(GVSP;cdh~OuU14@gCmC2lx;lAr?NyC-@Yx@fkkH7x)ri;cI+@Z}A=HB?6p)I=@RMkwl_F6to+_0a$g z(Fl#v1WnNl%@K|kXo*&6jW&otTeL%abU;URLT7YAS9C*n^gvJaLT~gzU-UzN48TAP z!e9)+Pz=LxjKD~Y!f1@aSd7DXOu$4;!emUrR7}Hk%)m^{!fedJT+G9KEWko6!eT7J zQY^!AtiVdF!fLF+TCBr*Y`{ir!e(s2R&2v|?7&X!!fx!rUhKnu9Kb;w!eJc2Q5?f@ zoWMz(!fBkrS)9XpT);(K!ev~+Rb0b$+`vuT!fo8aUEITcJitRd!eczaQ#`|SyueGm z!fU*V6v_fmNK?K^O9onM)aV-40~9oAz5HewStV+*!o8@6Kyc48NHV-NOXANJz_4&o3F;|Px87>?rv zPT~|!;|$K?9M0ncF5(g{;|i|g8m{98ZsHbh;|}iP9`54-9^w%m;|ZSP8J^<>Ug8yA z;|+emkN62c;}`sj-|##Bz@PXFeojcn|O61AK^&5DOpU z6MTx;_za)p3w(*M@HM`{xA+d$k7>c6=N}?1>qYTQT9Ll2tDxwl9 zqYA2`8mglPYN8fuBNTN|7xfT^`e=ZLXoSXSf~IJO<_Jd%v_vbkMjJ$+E!v?yI-nyu zp)xVVK??*FZN+S4&WdT;V_QiD30McPT(X?;WW5v{7kP(@X z8Cj4O*^nJMkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq38f8!xcFP2#c`vcx3ahaOYq1XNu>l*g37fG6Td@t>u>(7? z3%jugd$AAuaR3K#2#0Y5M{x|taRMiC3a4=fXK@baaRC=`372sNS8)y3aRWDT3%79x zcX1E*@c<9;2#@guPw@=T@d7XL3a{}7Kj26FgrD&Xe#LM29e?0Y{Dr>}B_xs%L`5`2 z$6I(CF%T2);9b0j_wfNf#7BsQkMRjUMQnVA&+!Gm#8>zl-{4z(hwl*waS;#kkpKyi z2#JvdNs$c6kpd}^3aOC>X^{@;kpUTz37L@vS&Cfi zG(&TQqXk-`6kJo5uMN(UCcO{6TQ$Ieb5*E&>sUZ5Q8unLogJ> zFdQQ=5~DC0V=xxuFdh>y5tA?(Q!o|NFdZ{66SFWIb1)b4FdqxB5R0%FORyBnupBF} z605KpYp@pUupS$*5u30XTd)<|upK+F6T7e*d$1S#upb9-5QlIWM{pF!a2zLa5~pw) zXK)tha2^+M5tncoS8x^Aa2+>r6Sr_1cW@W?a32rw5RdQ}Pw*7a@EkAj60h(YZzB1} zoG&=;503kT0zrYGKu{nk5EKXs1O6bB~c2cQ3hpE4&_k+6;TP5Q3X{|4b@QtHBk$-5sEsfi+Tt{ zeKbHrG(uxEK~pqCbA+P>TA~$NqYWa^7VXd;9ncY-&>3CO72VJsJMZw7yZy5 z127PSFc?EH6vHqaBQO%9FdAbp7UM7;6EG2zFd0)Y71J;sGcXggFdK6)7xOS53$PH2 zuoz3Q6w9z2E3gu)uo`Qy7VEGc8?X_Zuo+vh72B{KJFpYGup4`@7yGau2XGLFa2Q8$ z6vuEJCvXy{a2jWD7Uyst7jO}ma2Z!{71wYbH*gcTa2t1U7x!=<5AYC=@EA|<6wmM+ zFYpqt@EULM1AfF$_!+<8SNw+G@dy6IU-%nQLLv!4R768`yoI+B12ORq-o<-(A0OaD ze1ur|7@y!%#Kvd%9ADr|e1)&^4Zg*9_#SZ(7x54u36KzpkQhmj6v>brDUcGWkQ!-_ z7U_^48ITc~kQrH!71@v-Igk^%kQ;fB7x|DM1yB%$P#8r}6va>+B~TKjP#R@W7UfVL z6;KhCP#INF71dB3HBb|^P#dABgSx1PFw{o_G(;mbMiVqeGc-pyTA(Fbp*7kd0&UR_ z?a=`p(FvW=1zph%-O&R*(F?uN2Yt~G{V@OoF$jY(1Vb?l!!ZIQF$$wG24gV}<1qmf zF$t3~1yeB%(=h`xF$=RX2XiqG^RWO6u?UN?1WU0D%drA0u?nlP25Yen>#+eFu?d^8 z1zWKV+pz;Xu?xGg2Yay(`*8pVaR`TT1V?cU$8iEDaSEq#24`^&=WziSaS4}k1y^wm z*Kq?kaSOL`2X}D~_wfJ^@d%Ic1W)k{&+!5;@d~f;CX#<#75~>`Lt+I#CB(>{8flOg z>5v{7kP(@X8Cj4O*^nJMkQ2F(8+niy`H&w4P!NSs7)4MN#ZVk2P!gq38f8!xcFP2#c`vcx3ahaOYq1XNu>l*g37fG6 zTd@t>u>(7?3%jugd$AAuaR3K#2#0Y5M{x|taRMiC3a4=fXK@baaRC=`372sNS8)y3 zaRWDT3%79xcX1E*@c<9;2#@guPw@=T@d7XL3a{}7Kj26FgrD&Xe#LM29e?0Y{Dr>} zB_xs%L`5`2$6I(CF%T2);9b0j_wfNf#7BsQkMRjUMQnWbf7-jJCe5yf0mGSal1!Y8 zZQHhO+ctYVJ+^Jzwr$(?#G0?K?`VEND)pvnpInFi?7F*a*V3~Z5fKTI5d~2Z4bc$; zF%b)~5eIP*5Al%z36ThikpxMR49SrKDUk}Pkp^jz4(X8r8IcK@kp)?i4cU6bB~c2cQ3hpE4&_k+6;TP5Q3X{|4b@QtHBk$-Q3rKV z5B1Ri4bcdV(F9G=49(F3Ezt_C(FSeN4(-ta9nlG$(FI-64I$``9_Wc)=#4(;i+%`2 ze+6f95Nq|I5qH!LtPgf&xK-pg>R{C=e6~3j8|-g6;mWr4Oqz zu@D<^5Etb93@Z^rBE7WP!{D-9u-g#l~5T~P!-is9W_uBwNM*%P#5)3 z9}UnDjnEiP&=k$k94*iitcP#W|eE1zf}>T*eh##Wh^V4cx>n+{PW;#Xa1|13bhdJjN3|#WOs| z3%tZDyv7^+h@bE?e!;K!4Zq_L{E5HtH{RmCaAAbQ`}hDK;v;;FPY@oT;xl}X2>1eD z;wyZOZ}2U?!}s_B5fKTI5d~2Z4bc$;F%b)~5eIP*5Al%z36ThikpxMR49SrKDUk}P zkp^jz4(X8r8IcK@kp)?i4cU6bB~c2cQ3hpE z4&_k+6;TP5Q3X{|4b@QtHBk$-Q3rKV5B1Ri4bcdV(F9G=49(F3Ezt_C(FSeN4(-ta z9nlG$(FI-64I$``9_Wc)=#4(;i+%`2e+ zfQqPu%BX^>sD|pOftsj=+NguNsE7J!fQD#<#%O}3Xolu!ftF~6)@XyaXovRbfR5;d z&gg=!=!OtMDhF~a$VK_!$Bt~I0#$YVQVLT>aA|_!n zreG?jVLE1DCT3wa=3p-7VLldMAr@gVmS8ECVL4V{C01cI)?h8xVLdirBQ{|(wqPr^ zVLNtUCw5^s_FymeVLuMwAP(U$j^HSc;W$pM zCT`(2?%*!&;XWSVAs*o|p5Q5-;W=L5C0^k*-rz_4grD&Xe#LM29e?0Y{Dr^q7Vm`% zBOKnx2lx;l;bVM)@c0y;;d4a57x)ri;cI+@Z}AB>4ju?oEScr`{ zh>LiLj|51FL`aMzNQz`gjuc3VR7j09NQ-nxj||9&OvsEZ$ck*pjvUB|T*!?)$cuc) zj{+!&LMV(PD2iezjuI$|QYeiwD2s9^j|!-WN~nw~sETT+jvA^g&=dVjRX}0w!V-CSwYwVj8An24-RwW@8TKVjkvW0TyBr7GnvP zVi}fW1y*7eR$~p;Vjb3F12$q4He(C6VjH$&2XR{DDa;t@Xv1d2lIk|=a5G5DM5jtKu{nk z5EKXs1O+B~TKjP#R@W7UfVL6;KhCP#INF71dB3HBb|^P#bkn7xhpd z4bTvc&=^h76wS~aEzlCJ&>C&f7VXd;9ncY-&>3CO72Obm?&yJ@=!M?sgTCm8Q1r(D z48$M|#t;m}Fbu~CjKnC6#u$vnIE=>xOvEHi#uQA&G)%_~%)~6r#vIJWJj}-eEW{!# z#u6;WGAzdmti&p;#u}`}I;_VAY{VvP#ujYFHf+ZZ?8GkY#vbg&KJ3Q<9K<0U#t|IF zF&xJUoWv=d#u=Q&Ih@A@T*M_@#uZ$}HC)FH+{7*1#vR16wJj5eB#uGfnGd#x& zyu>TK#vA;IpYSt&!LRrYzvB=5iNEkS-r~J*VT8l`_y8Z`BYccc5FVf6GklH+_yS+z zD}0S_@GZW>_xJ%35ebnI1yK5%~$kqMcR1zC{|*^vV|kqfzz2YHbX`B4A`Q3!=m1VvE{#Zdw!Q3|C|24ztW zo_0a$g(Fl#v1WnNl&Cvoa(F(2625r#}?a=`p z(FvW=1zph%A?S`C=!stFjXvm$eh5W>48TAP!e9)+Pz=LxjKD~Y!f1@aSd7DXOu$4; z!emUrR7}Hk%)m^{!fedJT+G9KEWko6!eT7JQY^!AtiVdF!fLF+TCBr*Y`{ir!e(s2 zR&2v|?7&X!!fx!rUhKnu9Kb;w!eJc2Q5?f@oWMz(!fBkrS)9XpT);(K!ev~+Rb0b$ z+`vuT!fo8aUEITcJitRd!eczaQ#`|SyueGm!fU(<{5`AvMw< zEz%)9G9V)|Av3ZdE3zRwav&#iAvf|MFY+Nj3ZNhgp)iV|D2kytN}wc4p)|^%EXtug zDxe}Np)#tVDypG6YM>@+p*HHEF6yB^8lWK>p)s1EDVm`B-O&R*(F?uN2Yt~Gq3DkR7>Gd_j3F3`VHl1P7>Q9BjWHODaTt#Yn21T3 zj47CkX_$@~n2A}KjX9W$d6pfzIEhm@jWallb2yI+xQI)*j4QZ`Yq*XZ zxQSc1jXSuDd$^AWc!)=Mj3;=CXLybmc!^hdjW_raKjCNmf?x3)e#am96Mx}vyv2Lr z!U%`=@c}->NB9_@AUrzr3$h{`vLgp_A{TNa5Aq@( z@}mF>q7VwB2#TT@ilYQdq7+J_49cP$%A*1*q7o{j3aX+Ss-p&Kq84hS4(g&F>Z1V~ zq7fRS37VoAnxh3;q7_=B4cej|+M@$Hq7yo!3%a5kLeL#O&=bAT8-36h{Sb=&7=VEo zguxhsp%{kY7=e)(h0z#;u^5N(n1G3xgvpqKshEc8n1Pv?h1r;cxtNFfSb&9CgvD5b zrC5gLSb>#Th1FPtwOEJs*no}Lgw5E3t=NX`*nyqch27YLz1WBSIDmsVgu^(3qd11+ zIDwNmh0{2Lvp9$IxPXhegv+>stGI^ixPhCvh1UXy#6}#% zMLfhu0whEtBt{Y>MKUBu3Zz6Tq(&N~MLMKM24qAgWJVTbMK)wd4&+2GOR7Mq4MKx4M4b(&})J7fDMLpC<12jY< zG)5CNMKd%<3$#Qlv_>1WMLV=d2XsUybVe6+MK^??J9?ledZ9P^pfCC%6#X#(12G7L zF$6;~48t)3BQXl2F$QBX4&yNa6EO*sF$GgG4bw3LGcgOZF$Z%o5A(4A3$X}`u>?!8 z49l?sE3pczu?B0g4(qW28?gzSu?1VP4coB;JFyG9u?Ksx5BqTd2XP38aRf(k499T- zCvgg=aRz5`4(D+J7jX%faRpa#4cBo4H*pKMaR+yC5BKo^5Ag_(@dQut4A1cbFYyYm z@diKQC;W_G@GE}9@Aw0M;xGJ-w|FmH7~$|fKEQ|g2p{7UgvY1&44)$czQC9G3SZ+J ze2ee!J$^t$L_%alK~zLTbi_bR#6oPuL0rT`d?Y|ZBtl{&K~f|`a-={?q(W+>L0Y6k zdSpOGWI|?SK~`i#cH}@#kb<{vj)Ix34L0!~CeKbHrG(uxEK~pqCbF@H9v_fmNL0hy#dvriYbV6rz zL05D`2)d&OdZHJ4qYwI`A41U|127PSFc?EH6vHqaBQO%9FdAbp7UM7;6EG2zFd0)Y z71J;sGcXggFdK6)7xOS53$PH2uoz3Q6w9z2E3gu)uo`Qy7VEGc8?X_Zuo+vh72B{K zJFpYGup4`@7yGau2XGLFa2Q8$6vuEJCvXy{a2jWD7Uyst7jO}ma2Z!{71wYbH*gcT za2t1U7x!=<5AYC=@EA|<6wmM+FYpqt@EUKzcxUN1xb-W!mwF1ML@K048l*)!q(=s1 zL?&cL7Gy;>WJeCcFP2#c`vcx3ahaOYq1XNu>l*g37fG6Td@t>u>(7? z3%jugd$AAuaR3K#2#0Y5M{x|taRMiC3a4=fXK@baaRC=`372sNS8)y3aRWDT3%79x zcX1E*@c<9;2#@guPw@=T@d7XL3a{}7KjJ6+j9>68e#7th1ApQ#{EfGGFI*Vm@IF4k zhxiB|;}e9(r}zw?BLcp_m-q@_;~RX7@9;f-Ktx1BWJEz!L_>7MKup9!Y{Wra#6x@} zKtd!!VkALQBtvqfKuV-SYNSD0q(gdSKt^OjW@JHDWJ7l3Ku+XBZsb8;8KuMHBX_P@(ltXz`Kt)tSWmG{`R6}*tKuy#_ZPYCfi zG(&T=KufejYqUXIv_pGzKu2^!XLLbVbVCTbqX&AT7kZ-)`l25~(H{da5Q8unLogJ> zFdQQ=5~DC0V=xxuFdh>y5tA?(Q!o|NFdZ{66SFWIb1)b4FdqxB5R0%FORyBnupBF} z605KpYp@pUupS$*5u30XTd)<|upK+F6T7e*d$1S#upb9-5QlIWM{pF!a2zLa5~pw) zXK)tha2^+M5tncoS8x^Aa2+>r6Sr_1cW@W?a32rw5RdQ}Pw*7a@EkAj60h(YZ^C$I z%ol9;2iyHYfuKN8ASe(N2nqxRf&xK-|4e~@cDp~A7yK8Gd<4%N6bK3g1%d)WfuKN8 zASe(N2<8RB-#}0xC=e6~3Iqj$0zrYGKu{nk@XyW*g4=){2u+DpNR2c|i*!hj49JK~ z$c!w=ifqV^9LR}W$c;S6i+sqB0w{<=D2yT~iee~^5-5pMD2*~Gi*hKB3aE%msEjJ8 zifX8i8mNg{sEs)aV-40~9oAz5HewStV+*!o8@6Kyc48NH zV-NOXANJz_4&o3F;|Px87>?rvPT~|!;|$K?9M0ncF5(g{;|i|g8m{98ZsHbh;|}iP z9`54-9^w%m;|ZSP8J^<>Ug8yA;|+eqPxu+X;8*;H-|+|j#9#OuZ}DEZFv8(|e1H$} z5kAHz2#-(k89qk@e1R|V6~4wd_!i&cd;EZih=j<9f~bgw=!k)sh=tgQgSd!?_(*_+ zNQA^lf}}`>f~u&7>ZpO5sD;|7gSx1P`e=ZLXoSXSf~IJO z=4gSIXoc2jgSKdg_UM3)=!DMbg0AR>5OhZm^h7W8Mj!M=KZK$`24EltVK9bZD28D; zMqngHVKl~IEXH9xCSW2aVKSy*DyCsNW?&{}VK(MqF6LoA7GNP3VKJ6qDVAY5R$wJo zVKvrZE!JT@Hee$*VKcU1E4E=fc3>xVVK??*FZN+S4&WdT;V_QiD30McPT(X?;WW5h1|%4yvT?AD1d?}gu*C- zq9}&qD1nkFh0-X4vM7i0sDO&7gvzLbs;GwQsDYZOh1#ftx~PZxXn=-jgvMxsrf7!d zXn~e!h1O_;wrGd;=zxysgwE)KuIPpkbVm>LL@)G4AM`~(grYwNU?2uzFos|#hG95H zU?fIiG{#^o#$h}rU?L`AGNxcEreQi}U?yf^Hs)Y1=3zb-U?CP^F_vH{mSH(oU?o;z zHP&D))?qz1U?VnRGqzwWwqZMVU?+BAH}+sJ_F+E`;2;j+Fpl6Tj^Q{?;3Q7rG|u2G z&fz>R;36*JGOpk%uHiav;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO;3Zz+HQwMy{DhzJ z3x36K_#J=XPyB_y@fPoe3nLuf#|QWjAK_zsg7EkhpW$;vz!&%uU*T(fgKzO2zQ+%U zh)9TxD2R$^h>jSDiCBn@IEagQh>rwFh(t(?BuI*6NRAXpiBw39G)RkdNRJH2h)l?g zEXay%$c`MyiCoByJjjcD$d3Xjh(aigA}EStD2@^+iBc$yGAN63D31!Lh)Sr8DyWKT zsE!(_iCU6PCTNOgXpRXpau)h)(E?F6fGG2tjxB zKu`2SZ}dT5^g}56V*mzX5C&rihGH0oV+2NG6h>nV#$p`CV*(~(5+-8`reYeVV+Lko z7G`4(=3*Y^V*wUo5f)R{C=e6~3Iqj$0zrXb zyFYB{!>UXy#6}#%MLfhu0whEtBt{Y>MKUBu3Zz6Tq(&N~MLMKM24qAgWJVTbMK)wd z4&+2GOR7Mq4MKx4M4b(&} z)J7fDMLpC<12jY1WMLV=d2XsUybVe6+MK^??J9?ledZ9P^ zpfCC%6#X#(12G7LF$6;~48t)3BQXl2F$QBX4&yNa6EO*sF$GgG4bw3LGcgOZF$Z%o z5A(4A3$X}`u>?!849l?sE3pczu?B0g4(qW28?gzSu?1VP4coB;JFyG9u?Ksx5BqTd z2XP38aRf(k499T-Cvgg=aRz5`4(D+J7jX%faRpa#4cBo4H*pKMaR+yC5BKo^5Ag_( z@dQut4A1cbFYyYm@diKQC;W_G@GE}9@Aw0M;xGJ-w|FmH7~$|fKEQ|g2p{7UgvY1& z44)$czQC9G3SZ+Je2ee!J$^t$L_%alK~zLTbi_bR#6oPuL0rT`d?Y|ZBtl{&K~f|` za-={?q(W+>L0Y6kdSpOGWI|?SK~`i#cH}@#kb<{vj)Ix34L0!~CeKbHrG(uxEK~pqCbF@H9v_fmN zL0hy#dvriYbV6rzL05D`2)d&OdZHJ4qYwI`A41U|127PSFc?EH6vHqaBQO%9FdAbp z7UM7;6EG2zFd0)Y71J;sGcXggFdK6)7xOS53$PH2uoz3Q6w9z2E3gu)uo`Qy7VEGc z8?X_Zuo+vh72B{KJFpYGup4`@7yGau2XGLFa2Q8$6vuEJCvXy{a2jWD7Uyst7jO}m za2Z!{71wYbH*gcTa2t1U7x!=<5AYC=@EA|<6wmM+FYpqt@EUKzcxUN1xD7bDmwF1M zL@K048l*)!q(=s1L?&cL7Gy;>WJeCcFP2#c`vcx3ahaOYq1XNu>l*g z37fG6Td@t>u>(7?3%jugd$AAuaR3K#2#0Y5M{x|taRMiC3a4=fXK@baaRC=`372sN zS8)y3aRWDT3%79xcX1E*@c<9;2#@guPw@=T@d7XL3a{}7KjJ6+j9>68e#7th1ApQ# z{EfGGFI*Vm@IF4khxiB|;}e9(r}zw?BLcp_m-q@_;~RX7@9;f-Ktx1BWJEz!L_>7M zKup9!Y{Wra#6x@}Ktd!!VkALQBtvqfKuV-SYNSD0q(gdSKt^OjW@JHDWJ7l3Ku+XB zZsb8;8KuMHBX_P@(ltXz`Kt)tSWmG{`R6}*tKuy#_ZPYCfiG(&T=KufejYqUXIv_pGzKu2^!XLLbVbVCTbqX&AT7kZ-)`l25~ z(H{da5Q8unLogJ>FdQQ=5~DC0V=xxuFdh>y5tA?(Q!o|NFdZ{66SFWIb1)b4FdqxB z5R0%FORyBnupBF}605KpYp@pUupS$*5u30XTd)<|upK+F6T7e*d$1S#upb9-5QlIW zM{pF!a2zLa5~pw)XK)tha2^+M5tncoS8x^Aa2+>r6Sr_1cW@W?a32rw5RdQ}Pw*7a z@EkAj60h(YZ^C$I%ol9;2iyHYfuKN8ASe(N2nqxRf&xK-|4e~@cDp~A7X$}Lf&xK- zpg>R{C=e6~3Iqj$0{_JV|LnXVxb@4%Z%U*>YNSD0q(gdSKt^OjW@JHDWJ7l3Ku+XB zZsb8;8KuMHBX_P@(ltXz`Kt)tSWmG{`R6}*tKuy#_ZPYCfiG(&T=KufejYqUXIv_pGzKu2^!XLLbVbVCTbqX&AT7kZ-)`l25~ z(H{da5Q8unLogJ>FdQQ=5~DC0V=xxuFdh>y5tA?(Q!o|NFdZ{66SFWIb1)b4FdqxB z5R0%FORyBnupBF}605KpYp@pUupS$*5u30XTd)<|upK+F6T7e*d$1S#upb9-5QlIW zM{pF!a2zLa5~pw)XK)tha2^+M5tncoS8x^Aa2+>r6Sr_1cW@W?a32rw5RdQ}Pw*7a z@EkAj60h(YZ}202!q4~xzv4Iijz91x{=(mQi}%8X5f1O;1AK^&@G(9?czlY_@Hryj z3w(*M@HM`{xA+d<;|D}UBt%9OL`5`2M-0S7EW}0}#6>*BM*<{7A|yrCS*nyWJNY)M-JpfF62fYArwXt6h$!w>E3`%%v_(6# zM+bC7Cv-*^bVWCWpgVe?Cwieb`k*iRAr$>F00S`ygE0g{F$}{o0wXaBqcH|!F%IJ~ z0TVF^lQ9KTF%8o(12ZuTvoQyAF%R>x01L4Qi?IYtu?)+x0xPi!tFZ=au@3980UNOi zo3RC3u?^d?13R$`yRip*u@C!k00(ghhj9c)aSX?C0w-|_r*Q^naSrEk0T*!zmvIGG zaShjT12=ICw{Zt|aS!+L01xp9kMRUg@eI%L0x$6jukj{~cb0zt{}^!i;MOm1_LNA4 z)JTK0NQd;ufQ-n5%*cYQ$cF65ft<*N+{lBx$cOwWfPyH5!YG2GD2C!Ffs!bN(kO$n zD2MW>fQqPu%BX^>sD|pOftsj=+NguNsE7J!fQD#<#%O}3Xolu!ftF~6)@XyaXovRb zfR5;d&gg=!=!OtMDhF~a$VK_!$Bt~I0#$YVQVLT>a zA|_!nreG?jVLE1DCT3wa=3p-7VLldMAr@gVmS8ECVL4V{C01cI)?h8xVLdirBQ{|( zwqPr^VLNtUCw5^s_FymeVLuMwAP(U$j^HSc;W$pMCT`(2?%*!&;XWSVAs*o|p5Q5-;W=L5C0^k*-rz_4grD&Xe#LM29e?0Y{Dr^q z7Vm`%BOKnx2lx;l;bVM)@c0y;;d4a57x)ri;cI+@Z}AB>4ju?oE zScr`{h>LiLj|51FL`aMzNQz`gjuc3VR7j09NQ-nxj||9&OvsEZ$ck*pjvUB|T*!?) z$cuc)j{+!&LMV(PD2iezjuI$|QYeiwD2s9^j|!-WN~nw~sETT+jvA^g&=dVjRX}0w!V-CSwYwVj8An24-RwW@8TKVjkvW0TyBr z7GnvPVi}fW1y*7eR$~p;Vjb3F12$q4He(C6VjH$&2X=sR{C=e6~3IqlI*#g0Kf7sH8Rhd|b zjW~#lc!-Y#NQgv8j3h{kWJrz_NQqQPjWkG$bV!d3$cRkHj4a5CY{-rr$cbFYjXcPU ze8`UiD2PHRj3OwCVknLhD2Y-ijWQ^Uawv}qsEA6aj4G&#YN(DHsEJyrjXJ1{dZ>>E zXoyB=j3#J`W@wHUXo*&6jW%eDc4&_d=!j0}j4tSkZU{kl^gvJaLT~gzU-UyL`eOhF zVh{#n2!>)9hGPUqViZPW48~#{#$y5|ViG1}3Z`Njreg+XVism&4(4JW=3@aCVi6W& z36^3RmSY80Vii_n4c1~E)?))UViPuF3$|h#wqpl&Vi$H}5B6do_TvB!;t&qw2#(?y zj^hMQ;uKEf49?;l&f@|u;u0?73a;WBuHy!7;udb>4({R}?&AR-;t?L>37+B^p5p~x z;uT)w4SvK=_!+<8SNw+G@dy6IU-%nu@m{zv!r^^h=G`hh1iILxQK`NNPvV$gv3aKq)3M3NP(0{h15uc zv`B~a$bgK5h1|%4yvT?AD1d?}gu*C-q9}&qD1nkFh0-X4vM7i0 zsDO&7gvzLbs;GwQsDYZOh1#ftx~PZxXn=-jgvMxsrf7!dXn~e!h1O_;wrGd;=zxys zgwE)KuIPpkbVm>LL@)G4AM`~(grYwNU?2uzFos|#hG95HU?fIiG{#^o#$h}rU?L`A zGNxcEreQi}U?yf^Hs)Y1=3zb-U?CP^F_vH{mSH(oU?o;zHP&D))?qz1U?VnRGqzwW zwqZMVU?+BAH}+sJ_F+E`;2;j+Fpl6Tj^Q{?;3Q7rG|u2G&fz>R;36*JGOpk%uHiav z;3jV2Htygq?%_Tj;2|F2F`nQlp5ZxO;3Zz+HQt2r&eCsi>sN9w^%O{nR7j09NQ-nx zj||9&OvsEZ$ck*pjvUB|T*!?)$cuc)j{+!&LMV(PD2iezjuI$|QYeiwD2s9^j|!-W zN~nw~sETT+jvA^g&=dVjRX}0w!V-CSwYw zVj8An24-RwW@8TKVjkvW0TyBr7GnvPVi}fW1y*7eR$~p;Vjb3F12$q4He(C6VjH$& z2X4JD1)*nhw`X^il~IjsDi4fhU%z+ny7`^sDrwwhx%xMhG>Mw zXo99_hURF2mS~06XoI$BhxX`zj_8EW=z^~3h7fc|5A;MY^hO`_ML&e1KL%hR24OIU zU?_%RI7VP3MqxC@U@XRAJSJcwCSfwBU@E3zI%Z%dW??qwU@qoiJ{Djh7GW`#U@4Yi zIaXjLR$(>PU@g{RJvLw?HeoZiU@Nv^J9c0vc40U6U@!JzKMvp^4&gA4;3$saI8NXs zPT@4p;4IGJJTBlOF5xn+;3}@+I&R=5Zs9iW;4bdrJ|5s99^o;b;3=NrIbPr;Ug0&~ zgz?UpFWBx6w)=wuL4lw^P#`D}6bK3g1%d+qnF9apc7HH0_-7Ae1V1e(5EKXs1O&xo#5Gm b0zrYGKu{nk5EKXs1O sz; - size_t m = misc_utils::median(sz); - CHECK_AND_ASSERT_MES(m == 0, false, "test failed"); - sz.push_back(1); - m = misc_utils::median(sz); - CHECK_AND_ASSERT_MES(m == 1, false, "test failed"); - sz.push_back(10); - m = misc_utils::median(sz); - CHECK_AND_ASSERT_MES(m == 5, false, "test failed"); - - sz.clear(); - sz.resize(3); - sz[0] = 0; - sz[1] = 9; - sz[2] = 3; - m = misc_utils::median(sz); - CHECK_AND_ASSERT_MES(m == 3, false, "test failed"); - - sz.clear(); - sz.resize(4); - sz[0] = 77; - sz[1] = 9; - sz[2] = 22; - sz[3] = 60; - m = misc_utils::median(sz); - CHECK_AND_ASSERT_MES(m == 41, false, "test failed"); - - - - sz.clear(); - sz.resize(5); - sz[0] = 77; - sz[1] = 9; - sz[2] = 22; - sz[3] = 60; - sz[4] = 11; - m = misc_utils::median(sz); - CHECK_AND_ASSERT_MES(m == 22, false, "test failed"); - return true; - } - } -} - diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h deleted file mode 100644 index 0b6dc1f73b..0000000000 --- a/contrib/epee/tests/src/net/test_net.h +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "net/levin_server_cp2.h" -#include "storages/abstract_invoke.h" - -namespace epee -{ -namespace StorageNamed -{ - typedef CInMemStorage DefaultStorageType; -} -namespace tests -{ - struct some_subdata - { - - std::string str1; - std::list array_of_id; - - BEGIN_NAMED_SERIALIZE_MAP() - SERIALIZE_STL_ANSI_STRING(str1) - SERIALIZE_STL_CONTAINER_POD(array_of_id) - END_NAMED_SERIALIZE_MAP() - }; - - - /************************************************************************/ - /* */ - /************************************************************************/ - struct COMMAND_EXAMPLE_1 - { - const static int ID = 1000; - - struct request - { - - std::string example_string_data; - uint64_t example_id_data; - some_subdata sub; - - BEGIN_NAMED_SERIALIZE_MAP() - SERIALIZE_STL_ANSI_STRING(example_string_data) - SERIALIZE_POD(example_id_data) - SERIALIZE_T(sub) - END_NAMED_SERIALIZE_MAP() - }; - - - struct response - { - bool m_success; - uint64_t example_id_data; - std::list subs; - - BEGIN_NAMED_SERIALIZE_MAP() - SERIALIZE_POD(m_success) - SERIALIZE_POD(example_id_data) - SERIALIZE_STL_CONTAINER_T(subs) - END_NAMED_SERIALIZE_MAP() - }; - }; - - struct COMMAND_EXAMPLE_2 - { - const static int ID = 1001; - - struct request - { - std::string example_string_data2; - uint64_t example_id_data; - - BEGIN_NAMED_SERIALIZE_MAP() - SERIALIZE_POD(example_id_data) - SERIALIZE_STL_ANSI_STRING(example_string_data2) - END_NAMED_SERIALIZE_MAP() - }; - - struct response - { - bool m_success; - uint64_t example_id_data; - - BEGIN_NAMED_SERIALIZE_MAP() - SERIALIZE_POD(example_id_data) - SERIALIZE_POD(m_success) - END_NAMED_SERIALIZE_MAP() - }; - }; - typedef boost::uuids::uuid uuid; - - class test_levin_server: public levin::levin_commands_handler<> - { - test_levin_server(const test_levin_server&){} - public: - test_levin_server(){} - void set_thread_prefix(const std::string& pref) - { - m_net_server.set_threads_prefix(pref); - } - template - bool connect_async(const std::string adr, const std::string& port, uint32_t conn_timeot, calback_t cb, const std::string& bind_ip = "0.0.0.0") - { - return m_net_server.connect_async(adr, port, conn_timeot, cb, bind_ip); - } - - bool connect(const std::string adr, const std::string& port, uint32_t conn_timeot, net_utils::connection_context_base& cn, const std::string& bind_ip = "0.0.0.0") - { - return m_net_server.connect(adr, port, conn_timeot, cn, bind_ip); - } - void close(net_utils::connection_context_base& cn) - { - m_net_server.get_config_object().close(cn.m_connection_id); - } - - template - bool invoke(uuid con_id, int command, t_request& req, t_response& resp) - { - return invoke_remote_command(con_id, command, req, resp, m_net_server.get_config_object()); - } - - template< class t_response, class t_request, class callback_t> - bool invoke_async(uuid con_id, int command, t_request& req, callback_t cb) - { - return async_invoke_remote_command(con_id, command, req, m_net_server.get_config_object(), cb); - } - - bool init(const std::string& bind_port = "", const std::string& bind_ip = "0.0.0.0") - { - m_net_server.get_config_object().m_pcommands_handler = this; - m_net_server.get_config_object().m_invoke_timeout = 1000; - LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); - return m_net_server.init_server(bind_port, bind_ip); - } - - bool run() - { - //here you can set worker threads count - int thrds_count = 4; - - //go to loop - LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); - if(!m_net_server.run_server(thrds_count)) - { - LOG_ERROR("Failed to run net tcp server!"); - } - - LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); - return true; - } - - bool deinit() - { - return m_net_server.deinit_server(); - } - - bool send_stop_signal() - { - m_net_server.send_stop_signal(); - return true; - } - - uint32_t get_binded_port() - { - return m_net_server.get_binded_port(); - } - private: - - - CHAIN_LEVIN_INVOKE_TO_MAP(); //move levin_commands_handler interface invoke(...) callbacks into invoke map - CHAIN_LEVIN_NOTIFY_TO_STUB(); //move levin_commands_handler interface notify(...) callbacks into nothing - - BEGIN_INVOKE_MAP(test_levin_server) - HANDLE_INVOKE_T(COMMAND_EXAMPLE_1, &test_levin_server::handle_1) - HANDLE_INVOKE_T(COMMAND_EXAMPLE_2, &test_levin_server::handle_2) - END_INVOKE_MAP() - - //----------------- commands handlers ---------------------------------------------- - int handle_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context) - { - LOG_PRINT_L0("on_command_1: id " << arg.example_id_data << "---->>"); - COMMAND_EXAMPLE_2::request arg_ = AUTO_VAL_INIT(arg_); - arg_.example_id_data = arg.example_id_data; - COMMAND_EXAMPLE_2::response rsp_ = AUTO_VAL_INIT(rsp_); - invoke_async(context.m_connection_id, COMMAND_EXAMPLE_2::ID, arg_, [](int code, const COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context) - { - if(code < 0) - {LOG_PRINT_RED_L0("on_command_1: command_2 failed to invoke");} - else - {LOG_PRINT_L0("on_command_1: command_2 response " << rsp.example_id_data);} - }); - rsp.example_id_data = arg.example_id_data; - LOG_PRINT_L0("on_command_1: id " << arg.example_id_data << "<<----"); - return true; - } - int handle_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context) - { - LOG_PRINT_L0("on_command_2: id "<< arg.example_id_data); - rsp.example_id_data = arg.example_id_data; - //misc_utils::sleep_no_w(6000); - return true; - } - //---------------------------------------------------------------------------------- - net_utils::boosted_levin_async_server m_net_server; - }; - - - inline - bool do_run_test_server() - { - - test_levin_server srv1, srv2; - - - std::string bind_param = "0.0.0.0"; - std::string port = ""; - - if(!srv1.init(port, bind_param)) - { - LOG_ERROR("Failed to initialize srv!"); - return 1; - } - - if(!srv2.init(port, bind_param)) - { - LOG_ERROR("Failed to initialize srv!"); - return 1; - } - - srv1.set_thread_prefix("SRV_A"); - srv2.set_thread_prefix("SRV_B"); - - boost::thread th1( boost::bind(&test_levin_server::run, &srv1)); - boost::thread th2( boost::bind(&test_levin_server::run, &srv2)); - - LOG_PRINT_L0("Initalized servers, waiting for worker threads started..."); - misc_utils::sleep_no_w(1000); - - - LOG_PRINT_L0("Connecting to each other..."); - uint32_t port1 = srv1.get_binded_port(); - uint32_t port2 = srv2.get_binded_port(); - - COMMAND_EXAMPLE_1::request arg; - COMMAND_EXAMPLE_1::request resp; - - net_utils::connection_context_base cntxt_1; - bool r = srv1.connect("127.0.0.1", string_tools::num_to_string_fast(port2), 5000, cntxt_1); - CHECK_AND_ASSERT_MES(r, false, "connect to server failed"); - - net_utils::connection_context_base cntxt_2; - r = srv2.connect("127.0.0.1", string_tools::num_to_string_fast(port1), 5000, cntxt_2); - CHECK_AND_ASSERT_MES(r, false, "connect to server failed"); - - while(true) - { - LOG_PRINT_L0("Invoking from A to B..."); - int r = srv1.invoke(cntxt_1.m_connection_id, COMMAND_EXAMPLE_1::ID, arg, resp); - if(r<=0) - { - LOG_ERROR("Failed tp invoke A to B"); - break; - } - - LOG_PRINT_L0("Invoking from B to A..."); - r = srv2.invoke(cntxt_2.m_connection_id, COMMAND_EXAMPLE_1::ID, arg, resp); - if(r<=0) - { - LOG_ERROR("Failed tp invoke B to A"); - break; - } - } - srv1.send_stop_signal(); - srv2.send_stop_signal(); - th1.join(); - th1.join(); - - return true; - } - - - - inline bool do_test2_work_with_srv(test_levin_server& srv, int port) - { - uint64_t i = 0; - boost::mutex wait_event; - wait_event.lock(); - while(true) - { - net_utils::connection_context_base cntxt_local = AUTO_VAL_INIT(cntxt_local); - bool r = srv.connect_async("127.0.0.1", string_tools::num_to_string_fast(port), 5000, [&srv, &port, &wait_event, &i, &cntxt_local](const net_utils::connection_context_base& cntxt, const boost::system::error_code& ec) - { - CHECK_AND_ASSERT_MES(!ec, void(), "Some problems at connect, message: " << ec.message() ); - cntxt_local = cntxt; - LOG_PRINT_L0("Invoking command 1 to " << port); - COMMAND_EXAMPLE_1::request arg = AUTO_VAL_INIT(arg); - arg.example_id_data = i; - /*vc2010 workaround*/ - int port_ = port; - boost::mutex& wait_event_ = wait_event; - int r = srv.invoke_async(cntxt.m_connection_id, COMMAND_EXAMPLE_1::ID, arg, [port_, &wait_event_](int code, const COMMAND_EXAMPLE_1::request& rsp, const net_utils::connection_context_base& cntxt) - { - CHECK_AND_ASSERT_MES(code > 0, void(), "Failed to invoke"); - LOG_PRINT_L0("command 1 invoke to " << port_ << " OK."); - wait_event_.unlock(); - }); - }); - wait_event.lock(); - srv.close(cntxt_local); - ++i; - } - return true; - } - - inline - bool do_run_test_server_async_connect() - { - test_levin_server srv1, srv2; - - - std::string bind_param = "0.0.0.0"; - std::string port = ""; - - if(!srv1.init(port, bind_param)) - { - LOG_ERROR("Failed to initialize srv!"); - return 1; - } - - if(!srv2.init(port, bind_param)) - { - LOG_ERROR("Failed to initialize srv!"); - return 1; - } - - srv1.set_thread_prefix("SRV_A"); - srv2.set_thread_prefix("SRV_B"); - - boost::thread thmain1( boost::bind(&test_levin_server::run, &srv1)); - boost::thread thmain2( boost::bind(&test_levin_server::run, &srv2)); - - LOG_PRINT_L0("Initalized servers, waiting for worker threads started..."); - misc_utils::sleep_no_w(1000); - - - LOG_PRINT_L0("Connecting to each other..."); - uint32_t port1 = srv1.get_binded_port(); - uint32_t port2 = srv2.get_binded_port(); - - COMMAND_EXAMPLE_1::request arg; - COMMAND_EXAMPLE_1::request resp; - - - boost::thread work_1( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); - boost::thread work_2( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); - boost::thread work_3( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); - boost::thread work_4( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); - boost::thread work_5( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); - boost::thread work_6( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); - boost::thread work_7( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); - boost::thread work_8( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); - - - work_1.join(); - work_2.join(); - srv1.send_stop_signal(); - srv2.send_stop_signal(); - thmain1.join(); - thmain2.join(); - - return true; - } - -} -} \ No newline at end of file diff --git a/contrib/epee/tests/src/storages/portable_storages_test.h b/contrib/epee/tests/src/storages/portable_storages_test.h deleted file mode 100644 index ecded8dada..0000000000 --- a/contrib/epee/tests/src/storages/portable_storages_test.h +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "storages/serializeble_struct_helper.h" -#include "serialization/keyvalue_serialization.h" -#include "storages/portable_storage.h" -#include "storages/portable_storage_template_helper.h" - -namespace epee -{ - namespace tests - { - - struct port_test_struct_sub - { - std::string m_str; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL(m_str) - END_KV_SERIALIZE_MAP() - }; - -#pragma pack (push, 1) - struct some_pod_struct - { - uint64_t a; - int32_t b; - }; -#pragma pack(pop) - - struct port_test_struct - { - std::string m_str; - uint64_t m_uint64; - uint32_t m_uint32; - uint16_t m_uint16; - uint8_t m_uint8; - int64_t m_int64; - int32_t m_int32; - int16_t m_int16; - int8_t m_int8; - double m_double; - bool m_bool; - some_pod_struct m_pod; - std::list m_list_of_str; - std::list m_list_of_uint64_t; - std::list m_list_of_uint32_t; - std::list m_list_of_uint16_t; - std::list m_list_of_uint8_t; - std::list m_list_of_int64_t; - std::list m_list_of_int32_t; - std::list m_list_of_int16_t; - std::list m_list_of_int8_t; - std::list m_list_of_double; - std::list m_list_of_bool; - port_test_struct_sub m_subobj; - std::list m_list_of_self; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL(m_str) - KV_SERIALIZE_VAL(m_uint64) - KV_SERIALIZE_VAL(m_uint32) - KV_SERIALIZE_VAL(m_uint16) - KV_SERIALIZE_VAL(m_uint8) - KV_SERIALIZE_VAL(m_int64) - KV_SERIALIZE_VAL(m_int32) - KV_SERIALIZE_VAL(m_int16) - KV_SERIALIZE_VAL(m_int8) - KV_SERIALIZE_VAL(m_double) - KV_SERIALIZE_VAL(m_bool) - KV_SERIALIZE_VAL_POD_AS_BLOB(m_pod) - KV_SERIALIZE_OBJ(m_subobj) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_str) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint64_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint32_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint16_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint8_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_int64_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_int32_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_int16_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_int8_t) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_double) - KV_SERIALIZE_CONTAINER_VAL(m_list_of_bool) - KV_SERIALIZE_CONTAINER_OBJ(m_list_of_self) - END_KV_SERIALIZE_MAP() - }; - - bool operator != (const port_test_struct_sub& a, const port_test_struct_sub& b) - { - return b.m_str != a.m_str; - } - - bool operator == (const port_test_struct& a, const port_test_struct& b) - { - if( b.m_str != a.m_str - || b.m_uint64 != a.m_uint64 - || b.m_uint32 != a.m_uint32 - || b.m_uint16 != a.m_uint16 - || b.m_uint8 != a.m_uint8 - || b.m_int64 != a.m_int64 - || b.m_int32 != a.m_int32 - || b.m_int16 != a.m_int16 - || b.m_int8 != a.m_int8 - || b.m_double != a.m_double - || b.m_bool != a.m_bool - || b.m_pod.a != a.m_pod.a - || b.m_pod.b != a.m_pod.b - || b.m_list_of_str != a.m_list_of_str - || b.m_list_of_uint64_t != a.m_list_of_uint64_t - || b.m_list_of_uint32_t != a.m_list_of_uint32_t - || b.m_list_of_uint16_t != a.m_list_of_uint16_t - || b.m_list_of_uint8_t != a.m_list_of_uint8_t - || b.m_list_of_int64_t != a.m_list_of_int64_t - || b.m_list_of_int32_t != a.m_list_of_int32_t - || b.m_list_of_int16_t != a.m_list_of_int16_t - || b.m_list_of_int8_t != a.m_list_of_int8_t - || b.m_list_of_double != a.m_list_of_double - || b.m_list_of_bool != a.m_list_of_bool - || b.m_subobj != a.m_subobj - || b.m_list_of_self != a.m_list_of_self - ) - return false; - return true; - } - - void fill_struct_with_test_values(port_test_struct& s) - { - s.m_str = "zuzuzuzuzuz"; - s.m_uint64 = 111111111111111; - s.m_uint32 = 2222222; - s.m_uint16 = 2222; - s.m_uint8 = 22; - s.m_int64 = -111111111111111; - s.m_int32 = -2222222; - s.m_int16 = -2222; - s.m_int8 = -24; - s.m_double = 0.11111; - s.m_bool = true; - s.m_pod.a = 32342342342342; - s.m_pod.b = -342342; - s.m_list_of_str.push_back("1112121"); - s.m_list_of_uint64_t.push_back(1111111111); - s.m_list_of_uint64_t.push_back(2222222222); - s.m_list_of_uint32_t.push_back(1111111); - s.m_list_of_uint32_t.push_back(2222222); - s.m_list_of_uint16_t.push_back(1111); - s.m_list_of_uint16_t.push_back(2222); - s.m_list_of_uint8_t.push_back(11); - s.m_list_of_uint8_t.push_back(22); - - - s.m_list_of_int64_t.push_back(-1111111111); - s.m_list_of_int64_t.push_back(-222222222); - s.m_list_of_int32_t.push_back(-1111111); - s.m_list_of_int32_t.push_back(-2222222); - s.m_list_of_int16_t.push_back(-1111); - s.m_list_of_int16_t.push_back(-2222); - s.m_list_of_int8_t.push_back(-11); - s.m_list_of_int8_t.push_back(-22); - - s.m_list_of_double.push_back(0.11111); - s.m_list_of_double.push_back(0.22222); - s.m_list_of_bool.push_back(true); - s.m_list_of_bool.push_back(false); - - s.m_subobj.m_str = "subszzzzzzzz"; - s.m_list_of_self.push_back(s); - } - - bool test_portable_storages(const std::string& tests_folder) - { - serialization::portable_storage ps, ps2; - port_test_struct s1, s2; - fill_struct_with_test_values(s1); - - s1.store(ps); - std::string binbuf; - bool r = ps.store_to_binary(binbuf); - - ps2.load_from_binary(binbuf); - s2.load(ps2); - if(!(s1 == s2)) - { - LOG_ERROR("Portable storage test failed!"); - return false; - } - - - port_test_struct ss1, ss2; - fill_struct_with_test_values(ss1); - std::string json_buff = epee::serialization::store_t_to_json(ss1); - epee::serialization::load_t_from_json(ss2, json_buff); - if(!(ss1 == ss2)) - { - LOG_ERROR("Portable storage test failed!"); - return false; - } - - return true; - } - - } -} \ No newline at end of file diff --git a/contrib/epee/tests/src/storages/storage_tests.h b/contrib/epee/tests/src/storages/storage_tests.h deleted file mode 100644 index 522e589c4b..0000000000 --- a/contrib/epee/tests/src/storages/storage_tests.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the Andrey N. Sabelnikov nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 "storages/serializeble_struct_helper.h" -#include "storages/portable_storage.h" - -namespace epee -{ - namespace tests - { - - - struct test_struct - { - - std::string m_str; - unsigned int m_uint; - bool m_bool; - std::list m_list_of_str; - std::list m_list_of_int; - std::list m_list_of_self; - - - BEGIN_NAMED_SERIALIZE_MAP() - SERIALIZE_STL_ANSI_STRING(m_str) - SERIALIZE_POD(m_uint) - SERIALIZE_POD(m_bool) - SERIALIZE_STL_CONTAINER_ANSII_STRING(m_list_of_str) - SERIALIZE_STL_CONTAINER_POD(m_list_of_int) - SERIALIZE_STL_CONTAINER_T(m_list_of_self) - END_NAMED_SERIALIZE_MAP() - - }; - - - bool operator == (const test_struct& a, const test_struct& b) - { - if( b.m_str != a.m_str - || b.m_uint != a.m_uint - || b.m_bool != a.m_bool - || b.m_list_of_str != a.m_list_of_str - || b.m_list_of_int != a.m_list_of_int - || b.m_list_of_self != a.m_list_of_self - ) - return false; - return true; - } - - inline test_struct get_test_struct() - { - test_struct t = boost::value_initialized(); - t.m_bool = true; - t.m_str = "ackamdc'kmecemcececmacmecmcm[aicm[oeicm[oeicm[qaicm[qoe"; - t.m_uint = 233242; - for(int i = 0; i!=500; i++) - t.m_list_of_int.push_back(i); - - for(int i = 0; i!=500; i++) - t.m_list_of_str.push_back("ssccd"); - - for(int i = 0; i!=5; i++) - { - t.m_list_of_self.push_back(t); - } - return t; - } - - bool test_storages(const std::string& tests_folder) - { - - epee::serialization::portable_storage ps; - auto s = ps.open_section("zzz", nullptr); - uint64_t i = 0; - ps.get_value("afdsdf", i, s); - - - LOG_PRINT_L0("Generating test struct..."); - boost::filesystem::path storage_folder = tests_folder; - storage_folder /= "storages"; - - - test_struct t = get_test_struct(); - - LOG_PRINT_L0("Loading test struct from storage..."); - test_struct t2; - bool res = epee::StorageNamed::load_struct_from_storage_file(t2, (storage_folder /+ "valid_storage.bin").string()); - CHECK_AND_ASSERT_MES(res, false, "Failed to load valid_storage.bin"); - - LOG_PRINT_L0("Comparing generated and loaded test struct..."); - if(!(t == t2)) - return false; - - LOG_PRINT_L0("Loading broken archive 1..."); - test_struct t3; - res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_1.bin").string()); - CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_1.bin loaded, but should not "); - - - LOG_PRINT_L0("Loading broken archive 2..."); - res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_2.bin").string()); - CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_2.bin loaded, but should not "); - - LOG_PRINT_L0("Loading broken archive 3..."); - res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_3.bin").string()); - CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_3.bin loaded, but should not "); - - LOG_PRINT_L0("Loading broken archive 4..."); - res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_4.bin").string()); - CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_3.bin loaded, but should not "); - - return true; - } - } -} - diff --git a/contrib/epee/tests/src/tests.cpp b/contrib/epee/tests/src/tests.cpp deleted file mode 100644 index ed045d8331..0000000000 --- a/contrib/epee/tests/src/tests.cpp +++ /dev/null @@ -1,59 +0,0 @@ - -#include "include_base_utils.h" -#include "storages/storage_tests.h" -#include "misc/test_math.h" -#include "storages/portable_storages_test.h" -#include "net/test_net.h" - -using namespace epee; - -int main(int argc, char* argv[]) -{ - - string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - log_space::log_singletone::add_logger(LOGGER_FILE, - log_space::log_singletone::get_default_log_file().c_str(), - log_space::log_singletone::get_default_log_folder().c_str()); - - - string_tools::command_line_params_a start_params; - string_tools::parse_commandline(start_params, argc, argv); - std::string tests_data_path; - string_tools::get_xparam_from_command_line(start_params, std::string("/tests_folder"), tests_data_path); - - if(string_tools::have_in_command_line(start_params, std::string("/run_net_tests"))) - { - if(!tests::do_run_test_server()) - { - LOG_ERROR("net tests failed"); - return 1; - } - if(!tests::do_run_test_server_async_connect() ) - { - LOG_ERROR("net tests failed"); - return 1; - } - }else if(string_tools::have_in_command_line(start_params, std::string("/run_unit_tests"))) - { - if(!tests::test_median()) - { - LOG_ERROR("median test failed"); - return 1; - } - - - if(!tests::test_storages(tests_data_path)) - { - LOG_ERROR("storage test failed"); - return 1; - } - }else if(string_tools::have_in_command_line(start_params, std::string("/run_portable_storage_test"))) - { - tests::test_portable_storages(tests_data_path); - } - return 1; -} \ No newline at end of file diff --git a/include/BlockchainExplorerData.h b/include/BlockchainExplorerData.h new file mode 100644 index 0000000000..6e476da6f1 --- /dev/null +++ b/include/BlockchainExplorerData.h @@ -0,0 +1,130 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include + +namespace CryptoNote { + +enum class TransactionRemoveReason : uint8_t +{ + INCLUDED_IN_BLOCK = 0, + TIMEOUT = 1 +}; + +struct TransactionOutputToKeyDetails { + std::array txOutKey; +}; + +struct TransactionOutputMultisignatureDetails { + std::vector> keys; + uint32_t requiredSignatures; +}; + +struct TransactionOutputDetails { + uint64_t amount; + uint64_t globalIndex; + + boost::variant< + TransactionOutputToKeyDetails, + TransactionOutputMultisignatureDetails> output; +}; + +struct TransactionOutputReferenceDetails { + std::array transactionHash; + size_t number; +}; + +struct TransactionInputGenerateDetails { + uint64_t height; +}; + +struct TransactionInputToKeyDetails { + std::vector keyOffsets; + std::array keyImage; + uint64_t mixin; + TransactionOutputReferenceDetails output; +}; + +struct TransactionInputMultisignatureDetails { + uint32_t signatures; + TransactionOutputReferenceDetails output; +}; + +struct TransactionInputDetails { + uint64_t amount; + + boost::variant< + TransactionInputGenerateDetails, + TransactionInputToKeyDetails, + TransactionInputMultisignatureDetails> input; +}; + +struct TransactionExtraDetails { + std::vector padding; + std::vector> publicKey; + std::vector nonce; + std::vector raw; +}; + +struct TransactionDetails { + std::array hash; + uint64_t size; + uint64_t fee; + uint64_t totalInputsAmount; + uint64_t totalOutputsAmount; + uint64_t mixin; + uint64_t unlockTime; + uint64_t timestamp; + std::array paymentId; + bool inBlockchain; + std::array blockHash; + uint64_t blockHeight; + TransactionExtraDetails extra; + std::vector>> signatures; + std::vector inputs; + std::vector outputs; +}; + +struct BlockDetails { + uint8_t majorVersion; + uint8_t minorVersion; + uint64_t timestamp; + std::array prevBlockHash; + uint32_t nonce; + bool isOrphaned; + uint64_t height; + std::array hash; + uint64_t difficulty; + uint64_t reward; + uint64_t baseReward; + uint64_t blockSize; + uint64_t transactionsCumulativeSize; + uint64_t alreadyGeneratedCoins; + uint64_t alreadyGeneratedTransactions; + uint64_t sizeMedian; + double penalty; + uint64_t totalFeeAmount; + std::vector transactions; +}; + +} diff --git a/include/IBlockchainExplorer.h b/include/IBlockchainExplorer.h new file mode 100644 index 0000000000..3fa656812e --- /dev/null +++ b/include/IBlockchainExplorer.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "BlockchainExplorerData.h" + +namespace CryptoNote { + +class IBlockchainObserver { +public: + virtual ~IBlockchainObserver() {} + + virtual void blockchainUpdated(const std::vector& newBlocks, const std::vector& orphanedBlocks) {} + virtual void poolUpdated(const std::vector& newTransactions, const std::vector, TransactionRemoveReason>>& removedTransactions) {} + + virtual void blockchainSynchronized(const BlockDetails& topBlock) {} +}; + +class IBlockchainExplorer { +public: + virtual ~IBlockchainExplorer() {}; + + virtual bool addObserver(IBlockchainObserver* observer) = 0; + virtual bool removeObserver(IBlockchainObserver* observer) = 0; + + virtual void init() = 0; + virtual void shutdown() = 0; + + virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) = 0; + virtual bool getBlocks(const std::vector>& blockHashes, std::vector& blocks) = 0; + + virtual bool getBlockchainTop(BlockDetails& topBlock) = 0; + + virtual bool getTransactions(const std::vector>& transactionHashes, std::vector& transactions) = 0; + virtual bool getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) = 0; + + virtual uint64_t getRewardBlocksWindow() = 0; + virtual uint64_t getFullRewardMaxBlockSize(uint8_t majorVersion) = 0; + + virtual bool isSynchronized() = 0; +}; + +} diff --git a/include/IMultiWallet.h b/include/IMultiWallet.h index 1b7f18b06d..a6eb2c879f 100755 --- a/include/IMultiWallet.h +++ b/include/IMultiWallet.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/include/INode.h b/include/INode.h index 968834bead..9c53317f32 100644 --- a/include/INode.h +++ b/include/INode.h @@ -27,6 +27,8 @@ #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "BlockchainExplorerData.h" + namespace CryptoNote { class INodeObserver { @@ -36,6 +38,7 @@ class INodeObserver { virtual void localBlockchainUpdated(uint64_t height) {} virtual void lastKnownBlockHeightUpdated(uint64_t height) {} virtual void poolChanged() {} + virtual void blockchainSynchronized(uint64_t topHeight) {} }; struct OutEntry { @@ -78,6 +81,11 @@ class INode { virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) = 0; + + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) = 0; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) = 0; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) = 0; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) = 0; }; } diff --git a/include/ITransaction.h b/include/ITransaction.h index 36e9490d63..9c9fe77362 100644 --- a/include/ITransaction.h +++ b/include/ITransaction.h @@ -152,6 +152,7 @@ class ITransactionWriter { // extra virtual void setPaymentId(const Hash& paymentId) = 0; virtual void setExtraNonce(const std::string& nonce) = 0; + virtual void appendExtra(const Blob& extraData) = 0; // Inputs/Outputs virtual size_t addInput(const TransactionTypes::InputKey& input) = 0; diff --git a/src/BlockchainExplorer/BlockchainExplorer.cpp b/src/BlockchainExplorer/BlockchainExplorer.cpp new file mode 100644 index 0000000000..e3ec4b5b32 --- /dev/null +++ b/src/BlockchainExplorer/BlockchainExplorer.cpp @@ -0,0 +1,523 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockchainExplorer.h" + +#include +#include +#include + +#include "cryptonote_core/cryptonote_format_utils.h" +#include "BlockchainExplorerErrors.h" + +using namespace Logging; + +namespace CryptoNote { + +class NodeRequest { +public: + + NodeRequest(const std::function& request) : requestFunc(request) {} + + std::error_code performBlocking() { + requestFunc(std::bind(&NodeRequest::completeionCallback, this, std::placeholders::_1)); + return promise.get_future().get(); + } + + void performAsync(const INode::Callback& callback) { + requestFunc(callback); + } + +private: + void completeionCallback(std::error_code ec) { + promise.set_value(ec); + } + + std::promise promise; + const std::function requestFunc; +}; + +BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) : node(node), logger(logger, "BlockchainExplorer"), state(NOT_INITIALIZED) {} + +BlockchainExplorer::~BlockchainExplorer() {} + +bool BlockchainExplorer::addObserver(IBlockchainObserver* observer) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + return observerManager.add(observer); +} + +bool BlockchainExplorer::removeObserver(IBlockchainObserver* observer) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + return observerManager.remove(observer); +} + +void BlockchainExplorer::init() { + if (state.load() != NOT_INITIALIZED) { + logger(ERROR) << "Init called on already initialized BlockchainExplorer."; + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::ALREADY_INITIALIZED)); + } + if (node.addObserver(this)) { + state.store(INITIALIZED); + } else { + logger(ERROR) << "Can't add observer to node."; + state.store(NOT_INITIALIZED); + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::INTERNAL_ERROR)); + } + if (getBlockchainTop(knownBlockchainTop)) { + knownBlockchainTopHeight = knownBlockchainTop.height; + } else { + logger(ERROR) << "Can't get blockchain top."; + state.store(NOT_INITIALIZED); + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::INTERNAL_ERROR)); + } +} + +void BlockchainExplorer::shutdown() { + if (state.load() != INITIALIZED) { + logger(ERROR) << "Shutdown called on not initialized BlockchainExplorer."; + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + node.removeObserver(this); + state.store(NOT_INITIALIZED); +} + +bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, std::vector>& blocks) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get blocks by height request came."; + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + const std::vector&, + std::vector>&, + const INode::Callback& + ) + >(&INode::getBlocks), + std::ref(node), + std::cref(blockHeights), + std::ref(blocks), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get blocks by height: " << ec.message(); + throw std::system_error(ec); + } + assert(blocks.size() == blockHeights.size()); + return true; +} + +bool BlockchainExplorer::getBlocks(const std::vector>& blockHashes, std::vector& blocks) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get blocks by hash request came."; + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + const std::vector&, + std::vector&, + const INode::Callback& + ) + >(&INode::getBlocks), + std::ref(node), + std::cref(reinterpret_cast&>(blockHashes)), + std::ref(blocks), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get blocks by hash: " << ec.message(); + throw std::system_error(ec); + } + assert(blocks.size() == blockHashes.size()); + return true; +} + +bool BlockchainExplorer::getBlockchainTop(BlockDetails& topBlock) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get blockchain top request came."; + uint64_t lastHeight = node.getLastLocalBlockHeight(); + + std::vector heights; + heights.push_back(std::move(lastHeight)); + + std::vector> blocks; + if (!getBlocks(heights, blocks)) { + logger(ERROR) << "Can't get blockchain top."; + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::INTERNAL_ERROR)); + } + assert(blocks.size() == heights.size() && blocks.size() == 1); + + bool gotMainchainBlock = false; + for (const BlockDetails& block : blocks.back()) { + if (!block.isOrphaned) { + topBlock = block; + gotMainchainBlock = true; + break; + } + } + + if (!gotMainchainBlock) { + logger(ERROR) << "Can't get blockchain top: all blocks on height " << lastHeight << " are orphaned."; + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::INTERNAL_ERROR)); + } + return true; +} + +bool BlockchainExplorer::getTransactions(const std::vector>& transactionHashes, std::vector& transactions) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get transactions request came."; + NodeRequest request( + std::bind( + &INode::getTransactions, + std::ref(node), + std::cref(reinterpret_cast&>(transactionHashes)), + std::ref(transactions), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get transactions: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + +bool BlockchainExplorer::getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTopHash, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get pool state request came."; + std::vector rawNewTransactions; + + NodeRequest request( + [&](const INode::Callback& callback) { + std::vector hashes; + for (const std::array& hash : knownPoolTransactionHashes) { + hashes.push_back(std::move(reinterpret_cast(hash))); + } + node.getPoolSymmetricDifference( + std::move(hashes), + reinterpret_cast(knownBlockchainTopHash), + isBlockchainActual, + rawNewTransactions, + reinterpret_cast&>(removedTransactions), + callback + ); + } + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get pool state: " << ec.message(); + throw std::system_error(ec); + } + + std::vector> newTransactionsHashes; + for (const Transaction& rawTransaction : rawNewTransactions) { + crypto::hash transactionHash = get_transaction_hash(rawTransaction); + newTransactionsHashes.push_back(std::move(reinterpret_cast&>(transactionHash))); + } + + return getTransactions(newTransactionsHashes, newTransactions); +} + +uint64_t BlockchainExplorer::getRewardBlocksWindow() { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + return parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW; +} + +uint64_t BlockchainExplorer::getFullRewardMaxBlockSize(uint8_t majorVersion) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + if (majorVersion > 1) { + return parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + } else { + return parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + } +} + +bool BlockchainExplorer::isSynchronized() { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Synchronization status request came."; + bool syncStatus = false; + NodeRequest request( + std::bind( + &INode::isSynchronized, + std::ref(node), + std::ref(syncStatus), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get synchronization status: " << ec.message(); + throw std::system_error(ec); + } + return syncStatus; +} + +void BlockchainExplorer::poolChanged() { + logger(DEBUGGING) << "Got poolChanged notification."; + + std::unique_lock lock(mutex); + + std::shared_ptr> rawNewTransactionsPtr = std::make_shared>(); + std::shared_ptr> removedTransactionsPtr = std::make_shared>(); + std::shared_ptr isBlockchainActualPtr = std::make_shared(false); + + NodeRequest request( + [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](const INode::Callback& callback) { + std::vector hashes; + for (const crypto::hash& hash : knownPoolState) { + hashes.push_back(std::move(hash)); + } + node.getPoolSymmetricDifference( + std::move(hashes), + reinterpret_cast(knownBlockchainTop.hash), + *isBlockchainActualPtr, + *rawNewTransactionsPtr, + *removedTransactionsPtr, + callback + ); + } + ); + request.performAsync( + [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](std::error_code ec) { + if (ec) { + logger(ERROR) << "Can't send poolChanged notification because can't get pool symmetric difference: " << ec.message(); + return; + } + + if (!*isBlockchainActualPtr) { + logger(WARNING) << "Blockchain not actual."; + } + + std::unique_lock lock(mutex); + + std::shared_ptr> newTransactionsHashesPtr = std::make_shared>(); + for (const Transaction& rawTransaction : *rawNewTransactionsPtr) { + crypto::hash transactionHash = get_transaction_hash(rawTransaction); + bool inserted = knownPoolState.emplace(transactionHash).second; + if (inserted) { + newTransactionsHashesPtr->push_back(std::move(transactionHash)); + } + } + + std::shared_ptr, TransactionRemoveReason>>> removedTransactionsHashesPtr = std::make_shared, TransactionRemoveReason>>>(); + for (const crypto::hash hash : *removedTransactionsPtr) { + auto iter = knownPoolState.find(hash); + if (iter != knownPoolState.end()) { + removedTransactionsHashesPtr->push_back( + std::move(std::make_pair( + reinterpret_cast&>(hash), + TransactionRemoveReason::INCLUDED_IN_BLOCK //Can't have real reason here. + )) + ); + knownPoolState.erase(iter); + } + } + + std::shared_ptr> newTransactionsPtr = std::make_shared>(); + NodeRequest request( + std::bind( + &INode::getTransactions, + std::ref(node), + std::cref(*newTransactionsHashesPtr), + std::ref(*newTransactionsPtr), + std::placeholders::_1 + ) + ); + request.performAsync( + [this, newTransactionsHashesPtr, newTransactionsPtr, removedTransactionsHashesPtr](std::error_code ec) { + if (ec) { + logger(ERROR) << "Can't send poolChanged notification because can't get transactions: " << ec.message(); + return; + } + if (!newTransactionsPtr->empty() || !removedTransactionsHashesPtr->empty()) { + observerManager.notify(&IBlockchainObserver::poolUpdated, *newTransactionsPtr, *removedTransactionsHashesPtr); + logger(DEBUGGING) << "poolUpdated notification was successfully sent."; + } + } + ); + } + ); + +} + +void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { + logger(DEBUGGING) << "Got blockchainSynchronized notification."; + + std::shared_ptr> blockHeightsPtr = std::make_shared>(); + std::shared_ptr>> blocksPtr = std::make_shared>>(); + + blockHeightsPtr->push_back(topHeight); + + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + const std::vector&, + std::vector>&, + const INode::Callback& + ) + >(&INode::getBlocks), + std::ref(node), + std::cref(*blockHeightsPtr), + std::ref(*blocksPtr), + std::placeholders::_1 + ) + ); + + request.performAsync( + [this, blockHeightsPtr, blocksPtr, topHeight](std::error_code ec) { + if (ec) { + logger(ERROR) << "Can't send blockchainSynchronized notification because can't get blocks by height: " << ec.message(); + return; + } + assert(blocksPtr->size() == blockHeightsPtr->size() && blocksPtr->size() == 1); + + BlockDetails topMainchainBlock; + bool gotMainchainBlock = false; + for (const BlockDetails& block : blocksPtr->back()) { + if (!block.isOrphaned) { + topMainchainBlock = block; + gotMainchainBlock = true; + break; + } + } + + if (!gotMainchainBlock) { + logger(ERROR) << "Can't send blockchainSynchronized notification because can't get blockchain top: all blocks on height " << topHeight << " are orphaned."; + return; + } + + observerManager.notify(&IBlockchainObserver::blockchainSynchronized, topMainchainBlock); + logger(DEBUGGING) << "blockchainSynchronized notification was successfully sent."; + } + ); +} + +void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { + logger(DEBUGGING) << "Got localBlockchainUpdated notification."; + + std::unique_lock lock(mutex); + + assert(height >= knownBlockchainTopHeight); + + std::shared_ptr> blockHeightsPtr = std::make_shared>(); + std::shared_ptr>> blocksPtr = std::make_shared>>(); + + for (size_t i = knownBlockchainTopHeight; i <= height; ++i) { + blockHeightsPtr->push_back(i); + } + + knownBlockchainTopHeight = height; + + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + const std::vector&, + std::vector>&, + const INode::Callback& + ) + >(&INode::getBlocks), + std::ref(node), + std::cref(*blockHeightsPtr), + std::ref(*blocksPtr), + std::placeholders::_1 + ) + ); + + request.performAsync( + [this, blockHeightsPtr, blocksPtr](std::error_code ec) { + if (ec) { + logger(ERROR) << "Can't send blockchainUpdated notification because can't get blocks by height: " << ec.message(); + return; + } + assert(blocksPtr->size() == blockHeightsPtr->size()); + + std::unique_lock lock(mutex); + + BlockDetails topMainchainBlock; + bool gotTopMainchainBlock = false; + uint64_t topHeight = 0; + + std::vector newBlocks; + std::vector orphanedBlocks; + for (const std::vector& sameHeightBlocks : *blocksPtr) { + for (const BlockDetails& block : sameHeightBlocks) { + if (topHeight < block.height) { + topHeight = block.height; + gotTopMainchainBlock = false; + } + if (block.isOrphaned) { + orphanedBlocks.push_back(block); + } else { + if (block.height > knownBlockchainTop.height || block.hash != knownBlockchainTop.hash) { + newBlocks.push_back(block); + } + if (!gotTopMainchainBlock) { + topMainchainBlock = block; + gotTopMainchainBlock = true; + } + } + } + } + + if (!gotTopMainchainBlock) { + logger(ERROR) << "Can't send localBlockchainUpdated notification because can't get blockchain top: all blocks on height " << topHeight << " are orphaned."; + return; + } + + knownBlockchainTop = topMainchainBlock; + + observerManager.notify(&IBlockchainObserver::blockchainUpdated, newBlocks, orphanedBlocks); + logger(DEBUGGING) << "localBlockchainUpdated notification was successfully sent."; + } + ); +} + +} diff --git a/src/BlockchainExplorer/BlockchainExplorer.h b/src/BlockchainExplorer/BlockchainExplorer.h new file mode 100644 index 0000000000..d3c644d3d4 --- /dev/null +++ b/src/BlockchainExplorer/BlockchainExplorer.h @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include "IBlockchainExplorer.h" +#include "INode.h" + +#include "Common/ObserverManager.h" +#include "BlockchainExplorerErrors.h" + +#include "Logging/LoggerRef.h" + +namespace CryptoNote { + +class BlockchainExplorer : public IBlockchainExplorer, public INodeObserver { +public: + BlockchainExplorer(INode& node, Logging::ILogger& logger); + + BlockchainExplorer(const BlockchainExplorer&) = delete; + BlockchainExplorer(BlockchainExplorer&&) = delete; + + BlockchainExplorer& operator=(const BlockchainExplorer&) = delete; + BlockchainExplorer& operator=(BlockchainExplorer&&) = delete; + + virtual ~BlockchainExplorer(); + + virtual bool addObserver(IBlockchainObserver* observer) override; + virtual bool removeObserver(IBlockchainObserver* observer) override; + + virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) override; + virtual bool getBlocks(const std::vector>& blockHashes, std::vector& blocks) override; + + virtual bool getBlockchainTop(BlockDetails& topBlock) override; + + virtual bool getTransactions(const std::vector>& transactionHashes, std::vector& transactions) override; + virtual bool getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) override; + + virtual uint64_t getRewardBlocksWindow() override; + virtual uint64_t getFullRewardMaxBlockSize(uint8_t majorVersion) override; + + virtual bool isSynchronized() override; + + virtual void init() override; + virtual void shutdown() override; + + virtual void poolChanged() override; + virtual void blockchainSynchronized(uint64_t topHeight) override; + virtual void localBlockchainUpdated(uint64_t height) override; + +private: + enum State { + NOT_INITIALIZED, + INITIALIZED + }; + + BlockDetails knownBlockchainTop; + uint64_t knownBlockchainTopHeight; + std::unordered_set knownPoolState; + + std::atomic state; + tools::ObserverManager observerManager; + + std::mutex mutex; + + INode& node; + Logging::LoggerRef logger; + +}; +} diff --git a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp new file mode 100644 index 0000000000..336b934250 --- /dev/null +++ b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp @@ -0,0 +1,358 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockchainExplorerDataBuilder.h" + +#include +#include + +#include "Common/StringTools.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +namespace CryptoNote { + +BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : + core(core), + protocol(protocol) +{ +} + +bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uint64_t& mixin) { + mixin = 0; + for (const TransactionInput& txin : transaction.vin) { + if (txin.type() != typeid(TransactionInputToKey)) { + continue; + } + uint64_t currentMixin = boost::get(txin).keyOffsets.size(); + if (currentMixin > mixin) { + mixin = currentMixin; + } + } + return true; +} + +bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, crypto::hash& paymentId) { + std::vector txExtraFields; + parse_tx_extra(transaction.extra, txExtraFields); + tx_extra_nonce extraNonce; + if (!find_tx_extra_field_by_type(txExtraFields, extraNonce)) { + return false; + } + return get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); +} + +bool BlockchainExplorerDataBuilder::fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails) { + extraDetails.raw = rawExtra; + std::vector txExtraFields; + parse_tx_extra(rawExtra, txExtraFields); + for (const tx_extra_field& field : txExtraFields) { + if (typeid(tx_extra_padding) == field.type()) { + extraDetails.padding.push_back(std::move(boost::get(field).size)); + } + else if (typeid(tx_extra_pub_key) == field.type()) { + extraDetails.publicKey.push_back(std::move(reinterpret_cast&>(boost::get(field).pub_key))); + } + else if (typeid(tx_extra_nonce) == field.type()) { + extraDetails.nonce.push_back(std::move(Common::toHex(boost::get(field).nonce.data(), boost::get(field).nonce.size()))); + } + } + return true; +} + +size_t BlockchainExplorerDataBuilder::median(std::vector& v) { + if(v.empty()) + return boost::value_initialized(); + if(v.size() == 1) + return v[0]; + + size_t n = (v.size()) / 2; + std::sort(v.begin(), v.end()); + //nth_element(v.begin(), v.begin()+n-1, v.end()); + if(v.size()%2) + {//1, 3, 5... + return v[n]; + }else + {//2, 4, 6... + return (v[n-1] + v[n])/2; + } + +} + +bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDetails& blockDetails) { + crypto::hash hash = get_block_hash(block); + + blockDetails.majorVersion = block.majorVersion; + blockDetails.minorVersion = block.minorVersion; + blockDetails.timestamp = block.timestamp; + blockDetails.prevBlockHash = reinterpret_cast&>(block.prevId); + blockDetails.nonce = block.nonce; + blockDetails.hash = reinterpret_cast&>(hash); + + blockDetails.reward = 0; + for (const TransactionOutput& out : block.minerTx.vout) { + blockDetails.reward += out.amount; + } + + if (block.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) + return false; + blockDetails.height = boost::get(block.minerTx.vin.front()).height; + + crypto::hash tmpHash = core.getBlockIdByHeight(blockDetails.height); + blockDetails.isOrphaned = hash != tmpHash; + + if (!core.getBlockDifficulty(blockDetails.height, blockDetails.difficulty)) { + return false; + } + + std::vector blocksSizes; + if (!core.getBackwardBlocksSizes(blockDetails.height, blocksSizes, parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW)) { + return false; + } + blockDetails.sizeMedian = median(blocksSizes); + + size_t blockSize = 0; + if (!core.getBlockSize(hash, blockSize)) { + return false; + } + blockDetails.transactionsCumulativeSize = blockSize; + + size_t blokBlobSize = get_object_blobsize(block); + size_t minerTxBlobSize = get_object_blobsize(block.minerTx); + blockDetails.blockSize = blokBlobSize + blockDetails.transactionsCumulativeSize - minerTxBlobSize; + + if (!core.getAlreadyGeneratedCoins(hash, blockDetails.alreadyGeneratedCoins)) { + return false; + } + + blockDetails.alreadyGeneratedTransactions = 0; //TODO + + uint64_t prevBlockGeneratedCoins = 0; + if (blockDetails.height > 0) + { + if (!core.getAlreadyGeneratedCoins(block.prevId, prevBlockGeneratedCoins)) { + return false; + } + } + uint64_t maxReward = 0; + uint64_t currentReward = 0; + int64_t emissionChange = 0; + bool penalizeFee = block.majorVersion >= 2; + if(!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange)) + { + return false; + } + if(!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange)) + { + return false; + } + + blockDetails.baseReward = maxReward; + if (maxReward == 0 && currentReward == 0) + { + blockDetails.penalty = static_cast(0); + } + else + { + if (maxReward < currentReward) { + return false; + } + blockDetails.penalty = static_cast(maxReward - currentReward) / static_cast(maxReward); + } + + + blockDetails.transactions.reserve(block.txHashes.size() + 1); + TransactionDetails transactionDetails; + if (!fillTransactionDetails(block.minerTx, transactionDetails, block.timestamp)) { + return false; + } + blockDetails.transactions.push_back(std::move(transactionDetails)); + + std::list found; + std::list missed; + core.getTransactions(block.txHashes, found, missed); + if (found.size() != block.txHashes.size()) { + return false; + } + + blockDetails.totalFeeAmount = 0; + + for (const Transaction& tx : found) { + TransactionDetails transactionDetails; + if (!fillTransactionDetails(tx, transactionDetails, block.timestamp)) { + return false; + } + blockDetails.transactions.push_back(std::move(transactionDetails)); + blockDetails.totalFeeAmount += transactionDetails.fee; + } + return true; +} + +bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& transaction, TransactionDetails& transactionDetails, uint64_t timestamp) { + crypto::hash hash = get_transaction_hash(transaction); + transactionDetails.hash = reinterpret_cast&>(hash); + + transactionDetails.timestamp = timestamp; + + crypto::hash blockHash; + uint64_t blockHeight; + if (!core.getBlockContainingTx(hash, blockHash, blockHeight)) { + transactionDetails.inBlockchain = false; + transactionDetails.blockHeight = boost::value_initialized(); + transactionDetails.blockHash = boost::value_initialized>(); + } else { + transactionDetails.inBlockchain = true; + transactionDetails.blockHeight = blockHeight; + transactionDetails.blockHash = reinterpret_cast&>(blockHash); + if (timestamp == 0) { + Block block; + if (!core.getBlockByHash(blockHash, block)) { + return false; + } + transactionDetails.timestamp = block.timestamp; + } + } + + transactionDetails.size = get_object_blobsize(transaction); + transactionDetails.unlockTime = transaction.unlockTime; + transactionDetails.totalOutputsAmount = get_outs_money_amount(transaction); + + uint64_t inputsAmount; + if (!get_inputs_money_amount(transaction, inputsAmount)) { + return false; + } + transactionDetails.totalInputsAmount = inputsAmount; + + if (transaction.vin.size() > 0 && transaction.vin.front().type() == typeid(TransactionInputGenerate)) { + //It's gen transaction + transactionDetails.fee = 0; + transactionDetails.mixin = 0; + } else { + uint64_t fee; + if (!get_tx_fee(transaction, fee)) { + return false; + } + transactionDetails.fee = fee; + uint64_t mixin; + if (!getMixin(transaction, mixin)) { + return false; + } + transactionDetails.mixin = mixin; + } + + crypto::hash paymentId; + if (getPaymentId(transaction, paymentId)) { + transactionDetails.paymentId = reinterpret_cast&>(paymentId); + } + else { + transactionDetails.paymentId = boost::value_initialized>(); + } + + fillTxExtra(transaction.extra, transactionDetails.extra); + + transactionDetails.signatures.reserve(transaction.signatures.size()); + for (const std::vector& signatures : transaction.signatures) { + std::vector> signaturesDetails; + signaturesDetails.reserve(signatures.size()); + for (const crypto::signature& signature : signatures) { + signaturesDetails.push_back(std::move(reinterpret_cast&>(signature))); + } + transactionDetails.signatures.push_back(std::move(signaturesDetails)); + } + + transactionDetails.inputs.reserve(transaction.vin.size()); + for (const TransactionInput& txIn : transaction.vin) { + TransactionInputDetails txInDetails; + + if (txIn.type() == typeid(TransactionInputGenerate)) { + TransactionInputGenerateDetails txInGenDetails; + txInGenDetails.height = boost::get(txIn).height; + txInDetails.amount = 0; + for (const TransactionOutput& out : transaction.vout) { + txInDetails.amount += out.amount; + } + txInDetails.input = txInGenDetails; + } else if (txIn.type() == typeid(TransactionInputToKey)) { + TransactionInputToKeyDetails txInToKeyDetails; + const TransactionInputToKey& txInToKey = boost::get(txIn); + std::list> outputReferences; + if (!core.scanOutputkeysForIndices(txInToKey, outputReferences)) { + return false; + } + txInDetails.amount = txInToKey.amount; + txInToKeyDetails.keyOffsets = txInToKey.keyOffsets; + txInToKeyDetails.keyImage = reinterpret_cast&>(txInToKey.keyImage); + txInToKeyDetails.mixin = txInToKey.keyOffsets.size(); + txInToKeyDetails.output.number = outputReferences.back().second; + txInToKeyDetails.output.transactionHash = reinterpret_cast&>(outputReferences.back().first); + txInDetails.input = txInToKeyDetails; + } else if (txIn.type() == typeid(TransactionInputMultisignature)) { + TransactionInputMultisignatureDetails txInMultisigDetails; + const TransactionInputMultisignature& txInMultisig = boost::get(txIn); + txInDetails.amount = txInMultisig.amount; + txInMultisigDetails.signatures = txInMultisig.signatures; + std::pair outputReference; + if (!core.getMultisigOutputReference(txInMultisig, outputReference)) { + return false; + } + txInMultisigDetails.output.number = outputReference.second; + txInMultisigDetails.output.transactionHash = reinterpret_cast&>(outputReference.first); + txInDetails.input = txInMultisigDetails; + } else { + return false; + } + transactionDetails.inputs.push_back(std::move(txInDetails)); + } + + transactionDetails.outputs.reserve(transaction.vout.size()); + std::vector globalIndices; + globalIndices.reserve(transaction.vout.size()); + if (!core.get_tx_outputs_gindexs(hash, globalIndices)) { + for (size_t i = 0; i < transaction.vout.size(); ++i) { + globalIndices.push_back(0); + } + } + + typedef boost::tuple outputWithIndex; + auto range = boost::combine(transaction.vout, globalIndices); + for (const outputWithIndex& txOutput : range) { + TransactionOutputDetails txOutDetails; + txOutDetails.amount = txOutput.get<0>().amount; + txOutDetails.globalIndex = txOutput.get<1>(); + + if (txOutput.get<0>().target.type() == typeid(TransactionOutputToKey)) { + TransactionOutputToKeyDetails txOutToKeyDetails; + txOutToKeyDetails.txOutKey = reinterpret_cast&>(boost::get(txOutput.get<0>().target).key); + txOutDetails.output = txOutToKeyDetails; + } else if (txOutput.get<0>().target.type() == typeid(TransactionOutputMultisignature)) { + TransactionOutputMultisignatureDetails txOutMultisigDetails; + TransactionOutputMultisignature txOutMultisig = boost::get(txOutput.get<0>().target); + txOutMultisigDetails.keys.reserve(txOutMultisig.keys.size()); + for (const crypto::public_key& key : txOutMultisig.keys) { + txOutMultisigDetails.keys.push_back(std::move(reinterpret_cast&>(key))); + } + txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatures; + txOutDetails.output = txOutMultisigDetails; + } else { + return false; + } + transactionDetails.outputs.push_back(std::move(txOutDetails)); + } + + return true; +} + +} diff --git a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h new file mode 100644 index 0000000000..c7a3ea65a9 --- /dev/null +++ b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h @@ -0,0 +1,52 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" +#include "cryptonote_core/ICore.h" +#include "BlockchainExplorerData.h" + +namespace CryptoNote { + +class BlockchainExplorerDataBuilder +{ +public: + BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol); + + BlockchainExplorerDataBuilder(const BlockchainExplorerDataBuilder&) = delete; + BlockchainExplorerDataBuilder(BlockchainExplorerDataBuilder&&) = delete; + + BlockchainExplorerDataBuilder& operator=(const BlockchainExplorerDataBuilder&) = delete; + BlockchainExplorerDataBuilder& operator=(BlockchainExplorerDataBuilder&&) = delete; + + bool fillBlockDetails(const Block& block, BlockDetails& blockDetails); + bool fillTransactionDetails(const Transaction &tx, TransactionDetails& txRpcInfo, uint64_t timestamp = 0); + +private: + bool getMixin(const Transaction& transaction, uint64_t& mixin); + bool getPaymentId(const Transaction& transaction, crypto::hash& paymentId); + bool fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails); + size_t median(std::vector& v); + + CryptoNote::ICore& core; + CryptoNote::ICryptonoteProtocolQuery& protocol; +}; +} diff --git a/src/BlockchainExplorer/BlockchainExplorerErrors.cpp b/src/BlockchainExplorer/BlockchainExplorerErrors.cpp new file mode 100644 index 0000000000..e5adeff0e4 --- /dev/null +++ b/src/BlockchainExplorer/BlockchainExplorerErrors.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockchainExplorerErrors.h" + +namespace CryptoNote { +namespace error { + +BlockchainExplorerErrorCategory BlockchainExplorerErrorCategory::INSTANCE; + +} //namespace error +} //namespace CryptoNote + diff --git a/src/BlockchainExplorer/BlockchainExplorerErrors.h b/src/BlockchainExplorer/BlockchainExplorerErrors.h new file mode 100644 index 0000000000..fc02f3171a --- /dev/null +++ b/src/BlockchainExplorer/BlockchainExplorerErrors.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace CryptoNote { +namespace error { + +enum class BlockchainExplorerErrorCodes : int { + NOT_INITIALIZED = 1, + ALREADY_INITIALIZED, + INTERNAL_ERROR, + REQUEST_ERROR +}; + +class BlockchainExplorerErrorCategory : public std::error_category { +public: + static BlockchainExplorerErrorCategory INSTANCE; + + virtual const char* name() const throw() { + return "BlockchainExplorerErrorCategory"; + } + + virtual std::error_condition default_error_condition(int ev) const throw() { + return std::error_condition(ev, *this); + } + + virtual std::string message(int ev) const { + switch (ev) { + case static_cast(BlockchainExplorerErrorCodes::NOT_INITIALIZED): return "Object was not initialized"; + case static_cast(BlockchainExplorerErrorCodes::ALREADY_INITIALIZED): return "Object has been already initialized"; + case static_cast(BlockchainExplorerErrorCodes::INTERNAL_ERROR): return "Internal error"; + case static_cast(BlockchainExplorerErrorCodes::REQUEST_ERROR): return "Error in request parameters"; + default: return "Unknown error"; + } + } + +private: + BlockchainExplorerErrorCategory() { + } +}; + +} //namespace error +} //namespace CryptoNote + +inline std::error_code make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes e) { + return std::error_code(static_cast(e), CryptoNote::error::BlockchainExplorerErrorCategory::INSTANCE); +} + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd4b2578d4..1ebd2854ea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_definitions(-DSTATICLIB) +file(GLOB_RECURSE BlockchainExplorer BlockchainExplorer/*) file(GLOB_RECURSE Common Common/*) file(GLOB_RECURSE ConnectivityTool connectivity_tool/*) file(GLOB_RECURSE Crypto crypto/*) @@ -26,8 +27,10 @@ file(GLOB_RECURSE Transfers transfers/*) file(GLOB_RECURSE Wallet wallet/*) file(GLOB_RECURSE PaymentService payment_service/*) + source_group("" FILES ${Common} ${ConnectivityTool} ${Crypto} ${CryptoNote} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet}) +add_library(BlockchainExplorer ${BlockchainExplorer}) add_library(Common ${Common}) add_library(Crypto ${Crypto}) add_library(CryptoNote ${CryptoNote}) @@ -43,15 +46,15 @@ add_library(System ${System}) add_library(Transfers ${Transfers}) add_library(Wallet ${Wallet}) -add_executable(ConnectivityTool ${ConnectivityTool} p2p/LevinProtocol.cpp p2p/LevinProtocol.h) +add_executable(ConnectivityTool ${ConnectivityTool}) add_executable(Daemon ${Daemon}) add_executable(SimpleWallet ${SimpleWallet}) add_executable(PaymentGate ${PaymentService}) -target_link_libraries(ConnectivityTool epee Common Crypto Rpc Http System ${Boost_LIBRARIES}) -target_link_libraries(Daemon epee CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(SimpleWallet epee Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) -target_link_libraries(PaymentGate epee Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(ConnectivityTool CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES}) +target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(SimpleWallet Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(PaymentGate Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode BlockchainExplorer upnpc-static ${Boost_LIBRARIES}) add_dependencies(Rpc version) diff --git a/src/Common/ConsoleTools.cpp b/src/Common/ConsoleTools.cpp index 4ccbffc063..1cefdd6350 100644 --- a/src/Common/ConsoleTools.cpp +++ b/src/Common/ConsoleTools.cpp @@ -17,15 +17,31 @@ #include "ConsoleTools.h" +#include + #ifdef _WIN32 #include +#include #else #include +#include #endif namespace Common { namespace Console { +bool isConsoleTty() { +#if defined(WIN32) + static bool istty = 0 != _isatty(_fileno(stdout)); +#else + static bool istty = 0 != isatty(fileno(stdout)); +#endif + return istty; +} + void setTextColor(Color color) { + if (!isConsoleTty()) { + return; + } if (color > Color::BrightMagenta) { color = Color::Default; diff --git a/src/Common/ConsoleTools.h b/src/Common/ConsoleTools.h index fc334313be..7a27587876 100644 --- a/src/Common/ConsoleTools.h +++ b/src/Common/ConsoleTools.h @@ -42,5 +42,6 @@ enum class Color : uint8_t { }; void setTextColor(Color color); +bool isConsoleTty(); }} diff --git a/src/Common/JsonValue.cpp b/src/Common/JsonValue.cpp index e3dd3ff305..be656f75f7 100644 --- a/src/Common/JsonValue.cpp +++ b/src/Common/JsonValue.cpp @@ -538,51 +538,37 @@ JsonValue& JsonValue::pushBack(JsonValue&& value) { } JsonValue& JsonValue::operator()(const Key& key) { - if (type != OBJECT) { - throw std::runtime_error("JsonValue type is not OBJECT"); - } - - return reinterpret_cast(valueObject)->at(key); + return getObject().at(key); } const JsonValue& JsonValue::operator()(const Key& key) const { - if (type != OBJECT) { - throw std::runtime_error("JsonValue type is not OBJECT"); - } - - return reinterpret_cast(valueObject)->at(key); + return getObject().at(key); } -std::size_t JsonValue::count(const Key& key) const { - if (type != OBJECT) { - throw std::runtime_error("JsonValue type is not OBJECT"); - } - - return reinterpret_cast(valueObject)->count(key); +bool JsonValue::contains(const Key& key) const { + return getObject().count(key) > 0; } JsonValue& JsonValue::insert(const Key& key, const JsonValue& value) { - if (type != OBJECT) { - throw std::runtime_error("JsonValue type is not OBJECT"); - } - - return reinterpret_cast(valueObject)->emplace(key, value).first->second; + return getObject().emplace(key, value).first->second; } JsonValue& JsonValue::insert(const Key& key, JsonValue&& value) { - if (type != OBJECT) { - throw std::runtime_error("JsonValue type is not OBJECT"); - } + return getObject().emplace(key, std::move(value)).first->second; +} - return reinterpret_cast(valueObject)->emplace(key, std::move(value)).first->second; +JsonValue& JsonValue::set(const Key& key, const JsonValue& value) { + getObject()[key] = value; + return *this; } -std::size_t JsonValue::erase(const Key& key) { - if (type != OBJECT) { - throw std::runtime_error("JsonValue type is not OBJECT"); - } +JsonValue& JsonValue::set(const Key& key, JsonValue&& value) { + getObject()[key] = std::move(value); + return *this; +} - return reinterpret_cast(valueObject)->erase(key); +std::size_t JsonValue::erase(const Key& key) { + return getObject().erase(key); } JsonValue JsonValue::fromString(const std::string& source) { @@ -660,10 +646,57 @@ std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { return out; } -std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { + +namespace { + +char readChar(std::istream& in) { + char c; + + if (!(in >> c)) { + throw std::runtime_error("Unable to parse: unexpected end of stream"); + } + + return c; +} + +char readNonWsChar(std::istream& in) { char c; - in >> c; - while (isspace(c)) in >> c; + + do { + c = readChar(in); + } while (isspace(c)); + + return c; +} + +std::string readStringToken(std::istream& in) { + char c; + std::string value; + + while (in) { + c = readChar(in); + + if (c == '"') { + break; + } + + if (c == '\\') { + value += c; + c = readChar(in); + } + + value += c; + } + + return value; +} + +} + + +std::istream& operator>>(std::istream& in, JsonValue& jsonValue) { + char c = readNonWsChar(in); + if (c == '[') { jsonValue.readArray(in); } else if (c == 't') { @@ -702,21 +735,16 @@ void JsonValue::destructValue() { } void JsonValue::readArray(std::istream& in) { - char c; JsonValue::Array value; + char c = readNonWsChar(in); - c = static_cast(in.peek()); - for (;;) { - if (!isspace(in.peek())) break; - in.read(&c, 1); - } - - if (in.peek() != ']') { + if(c != ']') { + in.putback(c); for (;;) { value.resize(value.size() + 1); in >> value.back(); - in >> c; - while (isspace(c)) in >> c; + c = readNonWsChar(in); + if (c == ']') { break; } @@ -725,8 +753,6 @@ void JsonValue::readArray(std::istream& in) { throw std::runtime_error("Unable to parse"); } } - } else { - in.read(&c, 1); } if (type != JsonValue::ARRAY) { @@ -856,42 +882,27 @@ void JsonValue::readNumber(std::istream& in, char c) { } void JsonValue::readObject(std::istream& in) { - char c; + char c = readNonWsChar(in); JsonValue::Object value; - in >> c; - while (isspace(c)) in >> c; if (c != '}') { std::string name; + for (;;) { if (c != '"') { throw std::runtime_error("Unable to parse"); } - name.clear(); - for (;;) { - in >> c; - if (c == '"') { - break; - } - - if (c == '\\') { - name += c; - in >> c; - } + name = readStringToken(in); + c = readNonWsChar(in); - name += c; - } - - in >> c; - while (isspace(c)) in >> c; if (c != ':') { throw std::runtime_error("Unable to parse"); } in >> value[name]; - in >> c; - while (isspace(c)) in >> c; + c = readNonWsChar(in); + if (c == '}') { break; } @@ -899,8 +910,8 @@ void JsonValue::readObject(std::istream& in) { if (c != ',') { throw std::runtime_error("Unable to parse"); } - in >> c; - while (isspace(c)) in >> c; + + c = readNonWsChar(in); } } @@ -915,22 +926,7 @@ void JsonValue::readObject(std::istream& in) { } void JsonValue::readString(std::istream& in) { - char c; - String value; - - for (;;) { - in.read(&c, 1); - if (c == '"') { - break; - } - - if (c == '\\') { - value += c; - in >> c; - } - - value += c; - } + String value = readStringToken(in); if (type != JsonValue::STRING) { destructValue(); diff --git a/src/Common/JsonValue.h b/src/Common/JsonValue.h index 41f2202c53..0fc6d2b5fd 100644 --- a/src/Common/JsonValue.h +++ b/src/Common/JsonValue.h @@ -121,9 +121,14 @@ class JsonValue { JsonValue& operator()(const Key& key); const JsonValue& operator()(const Key& key) const; - std::size_t count(const Key& key) const; + bool contains(const Key& key) const; JsonValue& insert(const Key& key, const JsonValue& value); JsonValue& insert(const Key& key, JsonValue&& value); + + // sets or creates value, returns reference to self + JsonValue& set(const Key& key, const JsonValue& value); + JsonValue& set(const Key& key, JsonValue&& value); + std::size_t erase(const Key& key); static JsonValue fromString(const std::string& source); @@ -144,6 +149,7 @@ class JsonValue { }; void destructValue(); + void readArray(std::istream& in); void readTrue(std::istream& in); void readFalse(std::istream& in); diff --git a/src/Common/Math.cpp b/src/Common/Math.cpp new file mode 100644 index 0000000000..922a231db2 --- /dev/null +++ b/src/Common/Math.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Math.h" diff --git a/src/Common/Math.h b/src/Common/Math.h new file mode 100644 index 0000000000..92a84cd03c --- /dev/null +++ b/src/Common/Math.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace Common { + +template +T medianValue(std::vector &v) { + if (v.empty()) + return T(); + + if (v.size() == 1) + return v[0]; + + auto n = (v.size()) / 2; + std::sort(v.begin(), v.end()); + //nth_element(v.begin(), v.begin()+n-1, v.end()); + if (v.size() % 2) { //1, 3, 5... + return v[n]; + } else { //2, 4, 6... + return (v[n - 1] + v[n]) / 2; + } +} + +} diff --git a/src/HTTP/HttpParserErrorCodes.h b/src/HTTP/HttpParserErrorCodes.h index 61a53e1a85..ad8475e2ee 100644 --- a/src/HTTP/HttpParserErrorCodes.h +++ b/src/HTTP/HttpParserErrorCodes.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/src/InProcessNode/InProcessNode.cpp b/src/InProcessNode/InProcessNode.cpp index ebfd5b60f6..0602febc5d 100644 --- a/src/InProcessNode/InProcessNode.cpp +++ b/src/InProcessNode/InProcessNode.cpp @@ -20,6 +20,7 @@ #include #include +#include "Common/StringTools.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/verification_context.h" #include "cryptonote_protocol/cryptonote_protocol_handler_common.h" @@ -30,7 +31,8 @@ namespace CryptoNote { InProcessNode::InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : state(NOT_INITIALIZED), core(core), - protocol(protocol) + protocol(protocol), + blockchainExplorerDataBuilder(core, protocol) { } @@ -122,19 +124,17 @@ void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::l void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { - std::error_code ec; - { - std::unique_lock lock(mutex); - ec = doGetNewBlocks(std::move(knownBlockIds), newBlocks, startHeight); - } - + std::error_code ec = doGetNewBlocks(std::move(knownBlockIds), newBlocks, startHeight); callback(ec); } //it's always protected with mutex std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight) { - if (state != INITIALIZED) { - return make_error_code(CryptoNote::error::NOT_INITIALIZED); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + return make_error_code(CryptoNote::error::NOT_INITIALIZED); + } } try { @@ -186,19 +186,17 @@ void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transact void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { - std::error_code ec; - { - std::unique_lock lock(mutex); - ec = doGetTransactionOutsGlobalIndices(transactionHash, outsGlobalIndices); - } - + std::error_code ec = doGetTransactionOutsGlobalIndices(transactionHash, outsGlobalIndices); callback(ec); } //it's always protected with mutex std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { - if (state != INITIALIZED) { - return make_error_code(CryptoNote::error::NOT_INITIALIZED); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + return make_error_code(CryptoNote::error::NOT_INITIALIZED); + } } try { @@ -239,19 +237,17 @@ void InProcessNode::getRandomOutsByAmounts(std::vector&& amounts, uint void InProcessNode::getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) { - std::error_code ec; - { - std::unique_lock lock(mutex); - ec = doGetRandomOutsByAmounts(std::move(amounts), outsCount, result); - } - + std::error_code ec = doGetRandomOutsByAmounts(std::move(amounts), outsCount, result); callback(ec); } //it's always protected with mutex std::error_code InProcessNode::doGetRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result) { - if (state != INITIALIZED) { - return make_error_code(CryptoNote::error::NOT_INITIALIZED); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + return make_error_code(CryptoNote::error::NOT_INITIALIZED); + } } try { @@ -294,19 +290,17 @@ void InProcessNode::relayTransaction(const CryptoNote::Transaction& transaction, } void InProcessNode::relayTransactionAsync(const CryptoNote::Transaction& transaction, const Callback& callback) { - std::error_code ec; - { - std::unique_lock lock(mutex); - ec = doRelayTransaction(transaction); - } - + std::error_code ec = doRelayTransaction(transaction); callback(ec); } //it's always protected with mutex std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& transaction) { - if (state != INITIALIZED) { - return make_error_code(CryptoNote::error::NOT_INITIALIZED); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + return make_error_code(CryptoNote::error::NOT_INITIALIZED); + } } try { @@ -338,9 +332,11 @@ std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& } size_t InProcessNode::getPeerCount() const { - std::unique_lock lock(mutex); - if (state != INITIALIZED) { - throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } } return protocol.getPeerCount(); @@ -374,9 +370,11 @@ uint64_t InProcessNode::getKnownBlockCount() const { } uint64_t InProcessNode::getLastLocalBlockHeight() const { - std::unique_lock lock(mutex); - if (state != INITIALIZED) { - throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } } uint64_t height; @@ -390,9 +388,11 @@ uint64_t InProcessNode::getLastLocalBlockHeight() const { } uint64_t InProcessNode::getLastKnownBlockHeight() const { - std::unique_lock lock(mutex); - if (state != INITIALIZED) { - throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } } return protocol.getObservedHeight() - 1; @@ -440,6 +440,10 @@ void InProcessNode::poolUpdated() { observerManager.notify(&INodeObserver::poolChanged); } +void InProcessNode::blockchainSynchronized(uint64_t topHeight) { + observerManager.notify(&INodeObserver::blockchainSynchronized, topHeight); +} + void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const InProcessNode::Callback& callback) { @@ -465,12 +469,7 @@ void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_ void InProcessNode::queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { - std::error_code ec; - { - std::unique_lock lock(mutex); - ec = doQueryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight); - } - + std::error_code ec = doQueryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight); callback(ec); } @@ -522,13 +521,177 @@ void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& k std::vector& deleted_tx_ids, const Callback& callback) { std::error_code ec = std::error_code(); - std::unique_lock lock(mutex); - if (!core.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, is_bc_actual, new_txs, deleted_tx_ids)) { + is_bc_actual = core.getPoolChanges(known_block_id, known_pool_tx_ids, new_txs, deleted_tx_ids); + if (!is_bc_actual) { ec = make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } - lock.unlock(); callback(ec); } +void InProcessNode::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + static_cast< + void(InProcessNode::*)( + const std::vector&, + std::vector>&, + const Callback& + ) + >(&InProcessNode::getBlocksAsync), + this, + std::cref(blockHeights), + std::ref(blocks), + callback + ) + ); +} + +void InProcessNode::getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + std::error_code ec = doGetBlocks(blockHeights, blocks); + callback(ec); +} + +std::error_code InProcessNode::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks) { + uint64_t topHeight = 0; + crypto::hash topHash = boost::value_initialized(); + if (!core.get_blockchain_top(topHeight, topHash)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + for (const uint64_t& height : blockHeights) { + if (height > topHeight) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + crypto::hash hash = core.getBlockIdByHeight(height); + Block block; + if (!core.getBlockByHash(hash, block)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + BlockDetails blockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + std::vector blocksOnSameHeight; + blocksOnSameHeight.push_back(std::move(blockDetails)); + blocks.push_back(std::move(blocksOnSameHeight)); + } + return std::error_code(); +} + +void InProcessNode::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + static_cast< + void(InProcessNode::*)( + const std::vector&, + std::vector&, + const Callback& + ) + >(&InProcessNode::getBlocksAsync), + this, + std::cref(blockHashes), + std::ref(blocks), + callback + ) + ); +} + +void InProcessNode::getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::error_code ec = doGetBlocks(blockHashes, blocks); + callback(ec); +} + +std::error_code InProcessNode::doGetBlocks(const std::vector& blockHashes, std::vector& blocks) { + for (const crypto::hash& hash : blockHashes) { + Block block; + if (!core.getBlockByHash(hash, block)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + BlockDetails blockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocks.push_back(std::move(blockDetails)); + } + return std::error_code(); +} + +void InProcessNode::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + &InProcessNode::getTransactionsAsync, + this, + std::cref(transactionHashes), + std::ref(transactions), + callback + ) + ); +} + +void InProcessNode::getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::error_code ec= doGetTransactions(transactionHashes, transactions); + callback(ec); +} + +std::error_code InProcessNode::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions) { + std::list txs; + std::list missed_txs; + core.getTransactions(transactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Transaction& tx : txs) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(tx, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); + } + return std::error_code(); +} + +void InProcessNode::isSynchronized(bool& syncStatus, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + &InProcessNode::isSynchronizedAsync, + this, + std::ref(syncStatus), + callback + ) + ); +} + +void InProcessNode::isSynchronizedAsync(bool& syncStatus, const Callback& callback) { + syncStatus = protocol.isSynchronized(); + callback(std::error_code()); +} + } //namespace CryptoNote diff --git a/src/InProcessNode/InProcessNode.h b/src/InProcessNode/InProcessNode.h index 59674e2704..9baed79da9 100644 --- a/src/InProcessNode/InProcessNode.h +++ b/src/InProcessNode/InProcessNode.h @@ -23,6 +23,7 @@ #include "cryptonote_core/ICore.h" #include "cryptonote_core/ICoreObserver.h" #include "Common/ObserverManager.h" +#include "BlockchainExplorer/BlockchainExplorerDataBuilder.h" #include #include @@ -66,10 +67,15 @@ class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserv virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; private: virtual void peerCountUpdated(size_t count) override; virtual void lastKnownBlockHeightUpdated(uint64_t height) override; + virtual void blockchainSynchronized(uint64_t topHeight) override; virtual void blockchainUpdated() override; virtual void poolUpdated() override; @@ -94,6 +100,18 @@ class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserv void getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback); + void getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback); + std::error_code doGetBlocks(const std::vector& blockHeights, std::vector>& blocks); + + void getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback); + std::error_code doGetBlocks(const std::vector& blockHashes, std::vector& blocks); + + void getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback); + std::error_code doGetTransactions(const std::vector& transactionHashes, std::vector& transactions); + + void isSynchronizedAsync(bool& syncStatus, const Callback& callback); + std::error_code doIsSynchronized(bool& syncStatus); + void workerFunc(); bool doShutdown(); @@ -111,6 +129,8 @@ class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserv std::unique_ptr workerThread; std::unique_ptr work; + BlockchainExplorerDataBuilder blockchainExplorerDataBuilder; + mutable std::mutex mutex; }; diff --git a/src/Logging/ILogger.h b/src/Logging/ILogger.h index 89f9955823..4b7af0826f 100755 --- a/src/Logging/ILogger.h +++ b/src/Logging/ILogger.h @@ -59,4 +59,8 @@ class ILogger { virtual void operator()(const std::string& category, Level level, boost::posix_time::ptime time, const std::string& body) = 0; }; +#ifndef ENDL +#define ENDL std::endl +#endif + } diff --git a/src/Logging/LoggerManager.cpp b/src/Logging/LoggerManager.cpp index 31eba96725..ea159ab271 100755 --- a/src/Logging/LoggerManager.cpp +++ b/src/Logging/LoggerManager.cpp @@ -37,7 +37,7 @@ void LoggerManager::configure(const JsonValue& val) { loggers.clear(); LoggerGroup::loggers.clear(); Level globalLevel; - if (val.count("globalLevel")) { + if (val.contains("globalLevel")) { auto levelVal = val("globalLevel"); if (levelVal.isInteger()) { globalLevel = static_cast(levelVal.getInteger()); @@ -49,7 +49,7 @@ void LoggerManager::configure(const JsonValue& val) { } std::vector globalDisabledCategories; - if (val.count("globalDisabledCategories")) { + if (val.contains("globalDisabledCategories")) { auto globalDisabledCategoriesList = val("globalDisabledCategories"); if (globalDisabledCategoriesList.isArray()) { size_t countOfCategories = globalDisabledCategoriesList.size(); @@ -64,7 +64,7 @@ void LoggerManager::configure(const JsonValue& val) { } } - if (val.count("loggers")) { + if (val.contains("loggers")) { auto loggersList = val("loggers"); if (loggersList.isArray()) { size_t countOfLoggers = loggersList.size(); @@ -75,7 +75,7 @@ void LoggerManager::configure(const JsonValue& val) { } Level level = INFO; - if (loggerConfiguration.count("level")) { + if (loggerConfiguration.contains("level")) { level = static_cast(loggerConfiguration("level").getInteger()); } @@ -93,12 +93,12 @@ void LoggerManager::configure(const JsonValue& val) { throw std::runtime_error("Unknown logger type: " + type); } - if (loggerConfiguration.count("pattern")) { + if (loggerConfiguration.contains("pattern")) { logger->setPattern(loggerConfiguration("pattern").getString()); } std::vector disabledCategories; - if (loggerConfiguration.count("disabledCategories")) { + if (loggerConfiguration.contains("disabledCategories")) { auto disabledCategoriesVal = loggerConfiguration("disabledCategories"); size_t countOfCategories = disabledCategoriesVal.size(); for (size_t i = 0; i < countOfCategories; ++i) { diff --git a/src/Platform/Linux/System/Dispatcher.h b/src/Platform/Linux/System/Dispatcher.h index 31e415f98b..c7574f1105 100755 --- a/src/Platform/Linux/System/Dispatcher.h +++ b/src/Platform/Linux/System/Dispatcher.h @@ -57,10 +57,10 @@ class Dispatcher { # if __WORDSIZE == 64 static const int SIZEOF_PTHREAD_MUTEX_T = 40; # else - static const int SIZEOF_PTHREAD_MUTEX_T = 32 + static const int SIZEOF_PTHREAD_MUTEX_T = 32; # endif #else - static const int SIZEOF_PTHREAD_MUTEX_T = 24 + static const int SIZEOF_PTHREAD_MUTEX_T = 24; #endif private: diff --git a/src/Platform/OSX/System/Timer.cpp b/src/Platform/OSX/System/Timer.cpp index 2160b17d04..a8a9e829e6 100755 --- a/src/Platform/OSX/System/Timer.cpp +++ b/src/Platform/OSX/System/Timer.cpp @@ -93,7 +93,7 @@ void Timer::stop() { stopped = true; } -void Timer::sleep(std::chrono::milliseconds duration) { +void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); if (stopped) { @@ -106,7 +106,7 @@ void Timer::sleep(std::chrono::milliseconds duration) { timer = dispatcher->getTimer(); struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count(), &timerContext); + EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count() / 1000000, &timerContext); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); diff --git a/src/Platform/OSX/System/Timer.h b/src/Platform/OSX/System/Timer.h index a9940114ab..75840bed2b 100755 --- a/src/Platform/OSX/System/Timer.h +++ b/src/Platform/OSX/System/Timer.h @@ -34,7 +34,7 @@ class Timer { Timer& operator=(Timer&& other); void start(); void stop(); - void sleep(std::chrono::milliseconds duration); + void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; diff --git a/src/Platform/Windows/System/TcpConnection.cpp b/src/Platform/Windows/System/TcpConnection.cpp index c8836f4213..927036d37a 100755 --- a/src/Platform/Windows/System/TcpConnection.cpp +++ b/src/Platform/Windows/System/TcpConnection.cpp @@ -26,6 +26,8 @@ #include #include "Dispatcher.h" +#pragma comment(lib, "Ws2_32.lib") + namespace System { namespace { diff --git a/src/Platform/Windows/System/Timer.cpp b/src/Platform/Windows/System/Timer.cpp index 66ede19f24..bf3a195ba2 100755 --- a/src/Platform/Windows/System/Timer.cpp +++ b/src/Platform/Windows/System/Timer.cpp @@ -89,7 +89,7 @@ void Timer::stop() { stopped = true; } -void Timer::sleep(std::chrono::milliseconds duration) { +void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); if (stopped) { @@ -101,7 +101,7 @@ void Timer::sleep(std::chrono::milliseconds duration) { QueryPerformanceCounter(&ticks); QueryPerformanceFrequency(&frequency); uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); - uint64_t time = currentTime + duration.count(); + uint64_t time = currentTime + duration.count() / 1000000; void* fiber = GetCurrentFiber(); TimerContext timerContext{ time, fiber, false }; context = &timerContext; diff --git a/src/Platform/Windows/System/Timer.h b/src/Platform/Windows/System/Timer.h index ed2ab62a5f..cf2d19b66e 100755 --- a/src/Platform/Windows/System/Timer.h +++ b/src/Platform/Windows/System/Timer.h @@ -34,7 +34,7 @@ class Timer { Timer& operator=(Timer&& other); void start(); void stop(); - void sleep(std::chrono::milliseconds duration); + void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; diff --git a/src/System/Ipv4Address.cpp b/src/System/Ipv4Address.cpp index 719a6d9244..75e9fcee36 100755 --- a/src/System/Ipv4Address.cpp +++ b/src/System/Ipv4Address.cpp @@ -115,11 +115,11 @@ bool Ipv4Address::isLoopback() const { bool Ipv4Address::isPrivate() const { return // 10.0.0.0/8 - (value & 0xff000000) == (10 << 24) || + (value & UINT32_C(0xff000000)) == (UINT32_C(10) << 24) || // 172.16.0.0/12 - (value & 0xfff00000) == ((172 << 24) | (16 << 16)) || + (value & UINT32_C(0xfff00000)) == ((UINT32_C(172) << 24) | (UINT32_C(16) << 16)) || // 192.168.0.0/16 - (value & 0xffff0000) == ((192 << 24) | (168 << 16)); + (value & UINT32_C(0xffff0000)) == ((UINT32_C(192) << 24) | (UINT32_C(168) << 16)); } } diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp index 84fee726cd..d2b5a2a36d 100644 --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/connectivity_tool/conn_tool.cpp @@ -34,11 +34,16 @@ #include "p2p/LevinProtocol.h" #include "rpc/core_rpc_server_commands_defs.h" #include "rpc/HttpClient.h" +#include "serialization/SerializationTools.h" #include "version.h" namespace po = boost::program_options; using namespace CryptoNote; +#ifndef ENDL +#define ENDL std::endl +#endif + namespace { const command_line::arg_descriptor arg_ip = {"ip", "set ip"}; const command_line::arg_descriptor arg_port = { "port", "set port" }; @@ -98,7 +103,7 @@ std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\""; if (rs.si_rsp.is_initialized()) { - ss << "," << ENDL << " \"si_rsp\": " << epee::serialization::store_t_to_json(rs.si_rsp.get(), 1); + ss << "," << ENDL << " \"si_rsp\": " << storeToJson(rs.si_rsp.get()); } if (rs.ns_rsp.is_initialized()) { @@ -195,8 +200,8 @@ bool handle_get_daemon_info(po::variables_map& vm) { System::Dispatcher dispatcher; HttpClient httpClient(dispatcher, command_line::get_arg(vm, arg_ip), command_line::get_arg(vm, arg_rpc_port)); - CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); - CryptoNote::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res); + CryptoNote::COMMAND_RPC_GET_INFO::request req; + CryptoNote::COMMAND_RPC_GET_INFO::response res; invokeJsonCommand(httpClient, "/getinfo", req, res); // TODO: timeout @@ -225,13 +230,13 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { return false; } - crypto::secret_key prvk = AUTO_VAL_INIT(prvk); + crypto::secret_key prvk; if (!Common::podFromHex(command_line::get_arg(vm, arg_priv_key), prvk)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; return false; } - response_schema rs = AUTO_VAL_INIT(rs); + response_schema rs; unsigned timeout = command_line::get_arg(vm, arg_timeout); try { @@ -262,17 +267,17 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { peer_id = rsp.my_id; } - proof_of_trust pot = AUTO_VAL_INIT(pot); + proof_of_trust pot; pot.peer_id = peer_id; pot.time = time(NULL); - crypto::public_key pubk = AUTO_VAL_INIT(pubk); + crypto::public_key pubk; Common::podFromHex(P2P_STAT_TRUSTED_PUB_KEY, pubk); crypto::hash h = get_proof_of_trust_hash(pot); crypto::generate_signature(h, pubk, prvk, pot.sign); if (command_line::get_arg(vm, arg_request_stat_info)) { - COMMAND_REQUEST_STAT_INFO::request req = AUTO_VAL_INIT(req); - COMMAND_REQUEST_STAT_INFO::response res = AUTO_VAL_INIT(res); + COMMAND_REQUEST_STAT_INFO::request req; + COMMAND_REQUEST_STAT_INFO::response res; req.tr = pot; @@ -295,9 +300,8 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { ++pot.time; h = get_proof_of_trust_hash(pot); crypto::generate_signature(h, pubk, prvk, pot.sign); - COMMAND_REQUEST_NETWORK_STATE::request req = AUTO_VAL_INIT(req); - COMMAND_REQUEST_NETWORK_STATE::response res = AUTO_VAL_INIT(res); - req.tr = pot; + COMMAND_REQUEST_NETWORK_STATE::request req{ pot }; + COMMAND_REQUEST_NETWORK_STATE::response res; try { withTimeout(dispatcher, connection, timeout, [&] { @@ -324,8 +328,8 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { //--------------------------------------------------------------------------------------------------------------- bool generate_and_print_keys() { - crypto::public_key pk = AUTO_VAL_INIT(pk); - crypto::secret_key sk = AUTO_VAL_INIT(sk); + crypto::public_key pk; + crypto::secret_key sk; generate_keys(pk, sk); std::cout << "PUBLIC KEY: " << Common::podToHex(pk) << ENDL << "PRIVATE KEY: " << Common::podToHex(sk); diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index d25a400c89..e6253f4de5 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index 027ee95be8..4ef2c76297 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 298fe1a45b..efa724e1f1 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include #include #include diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 2dea3321c8..9bc0bcd1d1 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #include diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 6c419acbca..4b4b16d909 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #include diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c index 37e74f51d6..d14c9eff51 100644 --- a/src/crypto/hash-extra-blake.c +++ b/src/crypto/hash-extra-blake.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c index ab00dd63ab..50c69ffeea 100644 --- a/src/crypto/hash-extra-groestl.c +++ b/src/crypto/hash-extra-groestl.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c index 2b012c24b0..05018a3d6f 100644 --- a/src/crypto/hash-extra-jh.c +++ b/src/crypto/hash-extra-jh.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c index c0327fbc97..232f9df338 100644 --- a/src/crypto/hash-extra-skein.c +++ b/src/crypto/hash-extra-skein.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index cdd5a2d322..1a2ec16ec5 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #if !defined(__cplusplus) diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 9231a58534..f652255a51 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 041ea6475d..05e2e4595d 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #include diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h index 44c24c163c..7c40256232 100644 --- a/src/crypto/initializer.h +++ b/src/crypto/initializer.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #if defined(__GNUC__) diff --git a/src/crypto/random.c b/src/crypto/random.c index 08604f25c2..fc7497e31d 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/random.h b/src/crypto/random.h index 60a670e769..a07819023d 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #pragma once #if !defined(__cplusplus) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index cc8594437c..31ce6766ee 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/slow-hash.cpp b/src/crypto/slow-hash.cpp index 5d1a204beb..33fa46b79d 100644 --- a/src/crypto/slow-hash.cpp +++ b/src/crypto/slow-hash.cpp @@ -1,3 +1,20 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + #include #include "hash.h" diff --git a/src/crypto/slow-hash.inl b/src/crypto/slow-hash.inl index 7ed0045c52..57c86e069d 100644 --- a/src/crypto/slow-hash.inl +++ b/src/crypto/slow-hash.inl @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c index feee98dd6a..27cd4558fe 100644 --- a/src/crypto/tree-hash.c +++ b/src/crypto/tree-hash.c @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers // // This file is part of Bytecoin. // diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index f4a6642943..6c2086b077 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -60,6 +60,7 @@ const uint64_t CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS = DIFFICULTY_TARGET const uint64_t CRYPTONOTE_MEMPOOL_TX_LIVETIME = 60 * 60 * 24; //seconds, one day const uint64_t CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME = 60 * 60 * 24 * 7; //seconds, one week +const uint64_t CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL = 7; // CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL * CRYPTONOTE_MEMPOOL_TX_LIVETIME = time to forget tx const uint64_t UPGRADE_HEIGHT = 546602; const unsigned UPGRADE_VOTING_THRESHOLD = 90; // percent @@ -151,7 +152,8 @@ const CheckpointData CHECKPOINTS[] = { {689000, "212ec2698c5ebd15d6242d59f36c2d186d11bb47c58054f476dd8e6b1c7f0008"}, {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}, {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, - {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"} + {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"}, + {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"} }; } // CryptoNote diff --git a/src/cryptonote_core/AccountKVSerialization.h b/src/cryptonote_core/AccountKVSerialization.h deleted file mode 100644 index 74fbe6bf47..0000000000 --- a/src/cryptonote_core/AccountKVSerialization.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "cryptonote_core/account.h" - -// epee -#include "serialization/keyvalue_serialization.h" - -namespace CryptoNote { - template struct AccountPublicAddressSerializer; - template struct AccountKeysSerializer; - template struct AccountBaseSerializer; - - template<> - struct AccountPublicAddressSerializer { - const AccountPublicAddress& m_account_address; - - AccountPublicAddressSerializer(const AccountPublicAddress& account_address) : m_account_address(account_address) { - } - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_spendPublicKey, "m_spend_public_key") - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_viewPublicKey, "m_view_public_key") - END_KV_SERIALIZE_MAP() - }; - - template<> - struct AccountPublicAddressSerializer { - AccountPublicAddress& m_account_address; - - AccountPublicAddressSerializer(AccountPublicAddress& account_address) : m_account_address(account_address) { - } - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_spendPublicKey, "m_spend_public_key") - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_account_address.m_viewPublicKey, "m_view_public_key") - END_KV_SERIALIZE_MAP() - }; - - template<> - struct AccountKeysSerializer { - const account_keys& m_keys; - - AccountKeysSerializer(const account_keys& keys) : m_keys(keys) { - } - - BEGIN_KV_SERIALIZE_MAP() - AccountPublicAddressSerializer addressSerializer(this_ref.m_keys.m_account_address); - epee::serialization::selector::serialize(addressSerializer, stg, hparent_section, "m_account_address"); - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_spend_secret_key, "m_spend_secret_key") - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_view_secret_key, "m_view_secret_key") - END_KV_SERIALIZE_MAP() - }; - - template<> - struct AccountKeysSerializer { - account_keys& m_keys; - - AccountKeysSerializer(account_keys& keys) : m_keys(keys) { - } - - BEGIN_KV_SERIALIZE_MAP() - AccountPublicAddressSerializer addressSerializer(this_ref.m_keys.m_account_address); - epee::serialization::selector::serialize(addressSerializer, stg, hparent_section, "m_account_address"); - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_spend_secret_key, "m_spend_secret_key") - KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(m_keys.m_view_secret_key, "m_view_secret_key") - END_KV_SERIALIZE_MAP() - }; - - template<> - struct AccountBaseSerializer { - const account_base& m_account; - - AccountBaseSerializer(const account_base& account) : m_account(account) { - } - - BEGIN_KV_SERIALIZE_MAP() - AccountKeysSerializer keysSerializer(this_ref.m_account.m_keys); - epee::serialization::selector::serialize(keysSerializer, stg, hparent_section, "m_keys"); - KV_SERIALIZE_N(m_account.m_creation_timestamp, "m_creation_timestamp") - END_KV_SERIALIZE_MAP() - }; - - template<> - struct AccountBaseSerializer { - account_base& m_account; - - AccountBaseSerializer(account_base& account) : m_account(account) { - } - - BEGIN_KV_SERIALIZE_MAP() - AccountKeysSerializer keysSerializer(this_ref.m_account.m_keys); - epee::serialization::selector::serialize(keysSerializer, stg, hparent_section, "m_keys"); - KV_SERIALIZE_N(m_account.m_creation_timestamp, "m_creation_timestamp") - END_KV_SERIALIZE_MAP() - }; -} diff --git a/src/cryptonote_core/Currency.cpp b/src/cryptonote_core/Currency.cpp index 8138ec1fef..c75f2b399f 100644 --- a/src/cryptonote_core/Currency.cpp +++ b/src/cryptonote_core/Currency.cpp @@ -209,6 +209,10 @@ std::string Currency::accountAddressAsString(const account_base& account) const return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.get_keys().m_account_address); } +std::string Currency::accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const { + return getAccountAddressAsStr(m_publicAddressBase58Prefix, accountPublicAddress); +} + bool Currency::parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const { uint64_t prefix; if (!CryptoNote::parseAccountAddressString(prefix, addr, str)) { @@ -412,6 +416,7 @@ CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) { mempoolTxLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_LIVETIME); mempoolTxFromAltBlockLiveTime(parameters::CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME); + numberOfPeriodsToForgetTxDeletedFromPool(parameters::CRYPTONOTE_NUMBER_OF_PERIODS_TO_FORGET_TX_DELETED_FROM_POOL); upgradeHeight(parameters::UPGRADE_HEIGHT); upgradeVotingThreshold(parameters::UPGRADE_VOTING_THRESHOLD); diff --git a/src/cryptonote_core/Currency.h b/src/cryptonote_core/Currency.h index c939409fd4..d4009f0fb1 100644 --- a/src/cryptonote_core/Currency.h +++ b/src/cryptonote_core/Currency.h @@ -69,6 +69,7 @@ class Currency { uint64_t mempoolTxLiveTime() const { return m_mempoolTxLiveTime; } uint64_t mempoolTxFromAltBlockLiveTime() const { return m_mempoolTxFromAltBlockLiveTime; } + uint64_t numberOfPeriodsToForgetTxDeletedFromPool() const { return m_numberOfPeriodsToForgetTxDeletedFromPool; } uint64_t upgradeHeight() const { return m_upgradeHeight; } unsigned int upgradeVotingThreshold() const { return m_upgradeVotingThreshold; } @@ -97,6 +98,7 @@ class Currency { const blobdata& extraNonce = blobdata(), size_t maxOuts = 1, bool penalizeFee = false) const; std::string accountAddressAsString(const account_base& account) const; + std::string accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const; bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; std::string formatAmount(uint64_t amount) const; @@ -153,6 +155,7 @@ class Currency { uint64_t m_mempoolTxLiveTime; uint64_t m_mempoolTxFromAltBlockLiveTime; + uint64_t m_numberOfPeriodsToForgetTxDeletedFromPool; uint64_t m_upgradeHeight; unsigned int m_upgradeVotingThreshold; @@ -220,6 +223,7 @@ class CurrencyBuilder : boost::noncopyable { CurrencyBuilder& mempoolTxLiveTime(uint64_t val) { m_currency.m_mempoolTxLiveTime = val; return *this; } CurrencyBuilder& mempoolTxFromAltBlockLiveTime(uint64_t val) { m_currency.m_mempoolTxFromAltBlockLiveTime = val; return *this; } + CurrencyBuilder& numberOfPeriodsToForgetTxDeletedFromPool(uint64_t val) { m_currency.m_numberOfPeriodsToForgetTxDeletedFromPool = val; return *this; } CurrencyBuilder& upgradeHeight(uint64_t val) { m_currency.m_upgradeHeight = val; return *this; } CurrencyBuilder& upgradeVotingThreshold(unsigned int val); diff --git a/src/cryptonote_core/ICore.h b/src/cryptonote_core/ICore.h index 4674c30dc9..5ab1625477 100755 --- a/src/cryptonote_core/ICore.h +++ b/src/cryptonote_core/ICore.h @@ -23,23 +23,28 @@ #include #include "crypto/hash.h" +#include "cryptonote_core/difficulty.h" #include "cryptonote_protocol/blobdatatype.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" namespace CryptoNote { struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; +struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; +struct NOTIFY_RESPONSE_GET_OBJECTS_request; +struct NOTIFY_REQUEST_GET_OBJECTS_request; +class Currency; +class ICoreObserver; struct Block; -struct Transaction; -struct i_cryptonote_protocol; -struct tx_verification_context; struct block_verification_context; -struct core_stat_info; struct BlockFullInfo; -class ICoreObserver; -class Currency; +struct core_stat_info; +struct i_cryptonote_protocol; +struct Transaction; +struct TransactionInputMultisignature; +struct TransactionInputToKey; +struct tx_verification_context; class ICore { public: @@ -55,7 +60,7 @@ class ICore { virtual void pause_mining() = 0; virtual void update_block_template_and_resume_mining() = 0; virtual bool handle_incoming_block_blob(const CryptoNote::blobdata& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; - virtual bool handle_get_objects(CryptoNote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) = 0; + virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; virtual void on_synchronized() = 0; virtual bool is_ready() = 0; @@ -67,11 +72,26 @@ class ICore { virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) = 0; virtual i_cryptonote_protocol* get_protocol() = 0; virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) = 0; - virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) = 0; + virtual std::vector getPoolTransactions() = 0; + virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) = 0; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) = 0; virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) = 0; + virtual crypto::hash getBlockIdByHeight(uint64_t height) = 0; virtual bool getBlockByHash(const crypto::hash &h, Block &blk) = 0; + virtual void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) = 0; + virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) = 0; + virtual bool getBlockSize(const crypto::hash& hash, size_t& size) = 0; + virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) = 0; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) = 0; + virtual bool scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) = 0; + virtual bool getBlockDifficulty(uint64_t height, difficulty_type& difficulty) = 0; + virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) = 0; + virtual bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) = 0; }; } //namespace CryptoNote diff --git a/src/cryptonote_core/Transaction.cpp b/src/cryptonote_core/Transaction.cpp index cba7f5a915..c7dada3281 100644 --- a/src/cryptonote_core/Transaction.cpp +++ b/src/cryptonote_core/Transaction.cpp @@ -179,6 +179,7 @@ namespace CryptoNote { virtual void setUnlockTime(uint64_t unlockTime) override; virtual void setPaymentId(const Hash& hash) override; virtual void setExtraNonce(const std::string& nonce) override; + virtual void appendExtra(const Blob& extraData) override; // Inputs/Outputs virtual size_t addInput(const TransactionTypes::InputKey& input) override; @@ -385,7 +386,7 @@ namespace CryptoNote { outMsig.requiredSignatures = requiredSignatures; outMsig.keys.resize(to.size()); - for (int i = 0; i < to.size(); ++i) { + for (size_t i = 0; i < to.size(); ++i) { derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); } @@ -490,6 +491,12 @@ namespace CryptoNote { invalidateHash(); } + void TransactionImpl::appendExtra(const Blob& extraData) { + checkIfSigning(); + transaction.extra.insert( + transaction.extra.end(), extraData.begin(), extraData.end()); + } + bool TransactionImpl::getExtraNonce(std::string& nonce) const { tx_extra_nonce extraNonce; if (extra.get(extraNonce)) { diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp index 4f530b2c47..e67042aae4 100644 --- a/src/cryptonote_core/account.cpp +++ b/src/cryptonote_core/account.cpp @@ -16,34 +16,35 @@ // along with Bytecoin. If not, see . #include "account.h" +#include "cryptonote_serialization.h" -namespace CryptoNote -{ - //----------------------------------------------------------------- - account_base::account_base() - { - set_null(); - } - //----------------------------------------------------------------- - void account_base::set_null() - { - m_keys = account_keys(); - } - //----------------------------------------------------------------- - void account_base::generate() - { - crypto::generate_keys(m_keys.m_account_address.m_spendPublicKey, m_keys.m_spend_secret_key); - crypto::generate_keys(m_keys.m_account_address.m_viewPublicKey, m_keys.m_view_secret_key); - m_creation_timestamp = time(NULL); - } - //----------------------------------------------------------------- - const account_keys& account_base::get_keys() const - { - return m_keys; - } +namespace CryptoNote { +//----------------------------------------------------------------- +account_base::account_base() { + set_null(); +} +//----------------------------------------------------------------- +void account_base::set_null() { + m_keys = account_keys(); +} +//----------------------------------------------------------------- +void account_base::generate() { + crypto::generate_keys(m_keys.m_account_address.m_spendPublicKey, m_keys.m_spend_secret_key); + crypto::generate_keys(m_keys.m_account_address.m_viewPublicKey, m_keys.m_view_secret_key); + m_creation_timestamp = time(NULL); +} +//----------------------------------------------------------------- +const account_keys &account_base::get_keys() const { + return m_keys; +} - void account_base::set_keys(const account_keys& keys) { - m_keys = keys; - } - //----------------------------------------------------------------- +void account_base::set_keys(const account_keys &keys) { + m_keys = keys; +} +//----------------------------------------------------------------- + +void account_base::serialize(ISerializer &s) { + s(m_keys, "m_keys"); + s(m_creation_timestamp, "m_creation_timestamp"); +} } diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h index 171ec91e67..35939576c2 100644 --- a/src/cryptonote_core/account.h +++ b/src/cryptonote_core/account.h @@ -23,10 +23,12 @@ namespace CryptoNote { template struct AccountBaseSerializer; + class ISerializer; + struct account_keys { AccountPublicAddress m_account_address; - crypto::secret_key m_spend_secret_key; - crypto::secret_key m_view_secret_key; + crypto::secret_key m_spend_secret_key; + crypto::secret_key m_view_secret_key; }; /************************************************************************/ @@ -43,15 +45,14 @@ namespace CryptoNote { uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } - bool load(const std::string& file_path); - bool store(const std::string& file_path); - template inline void serialize(t_archive &a, const unsigned int /*ver*/) { a & m_keys; a & m_creation_timestamp; } + void serialize(ISerializer& s); + private: void set_null(); account_keys m_keys; diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index b942f16330..7c49b1c6eb 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -27,6 +27,7 @@ #include #include "Common/boost_serialization_helper.h" +#include "Common/Math.h" #include "Common/ShuffleGenerator.h" #include "Common/StringTools.h" @@ -35,12 +36,10 @@ using namespace Logging; -#ifdef ENDL -#undef ENDL -#define ENDL '\n' -#endif +#undef ERROR namespace { + std::string appendPath(const std::string& path, const std::string& fileName) { std::string result = path; if (!result.empty()) { @@ -51,28 +50,6 @@ std::string appendPath(const std::string& path, const std::string& fileName) { return result; } -template -type_vec_type medianValue(std::vector &v) -{ - if (v.empty()) - return boost::value_initialized(); - - if (v.size() == 1) - return v[0]; - - size_t n = (v.size()) / 2; - std::sort(v.begin(), v.end()); - //nth_element(v.begin(), v.begin()+n-1, v.end()); - if (v.size() % 2) - {//1, 3, 5... - return v[n]; - } else - {//2, 4, 6... - return (v[n - 1] + v[n]) / 2; - } -} - - } namespace std { @@ -405,22 +382,31 @@ crypto::hash blockchain_storage::get_tail_id() { return m_blockIndex.getTailId(); } -bool blockchain_storage::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector& new_txs, std::vector& deleted_tx_ids) { - - std::lock_guard txLock(m_tx_pool); - std::lock_guard bcLock(m_blockchain_lock); +bool blockchain_storage::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { + // Locks are necessary in order to make sure blockchain isn't changed between get_tail_id() and getPoolChanges() + std::lock_guard txPoolLock(m_tx_pool); + std::lock_guard blockchainLock(m_blockchain_lock); - if (known_block_id != get_tail_id()) { + if (tailBlockId != get_tail_id()) { return false; } - std::vector new_tx_ids; - m_tx_pool.get_difference(known_pool_tx_ids, new_tx_ids, deleted_tx_ids); + getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); + + return true; +} + +void blockchain_storage::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) { + std::lock_guard txPoolLock(m_tx_pool); + + std::vector addedTxsIds; + m_tx_pool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds); std::vector misses; - get_transactions(new_tx_ids, new_txs, misses, true); + m_tx_pool.getTransactions(addedTxsIds, addedTxs, misses); assert(misses.empty()); - return true; } bool blockchain_storage::get_short_chain_history(std::list& ids) { @@ -662,7 +648,7 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei std::vector lastBlocksSizes; get_last_n_blocks_sizes(lastBlocksSizes, m_currency.rewardBlocksWindow()); - size_t blocksSizeMedian = medianValue(lastBlocksSizes); + size_t blocksSizeMedian = Common::medianValue(lastBlocksSizes); bool penalizeFee = get_block_major_version_for_height(height) > BLOCK_MAJOR_VERSION_1; if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange)) { @@ -1010,8 +996,12 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: "###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << ", checkpoint is found in alternative chain on height " << bei.height; bool r = switch_to_alternative_blockchain(alt_chain, true); - if (r) bvc.m_added_to_main_chain = true; - else bvc.m_verifivation_failed = true; + if (r) { + bvc.m_added_to_main_chain = true; + bvc.m_switched_to_alt_chain = true; + } else { + bvc.m_verifivation_failed = true; + } return r; } else if (m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain { @@ -1020,8 +1010,12 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: "###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() - 1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty; bool r = switch_to_alternative_blockchain(alt_chain, false); - if (r) bvc.m_added_to_main_chain = true; - else bvc.m_verifivation_failed = true; + if (r) { + bvc.m_added_to_main_chain = true; + bvc.m_switched_to_alt_chain = true; + } else { + bvc.m_verifivation_failed = true; + } return r; } else { logger(INFO, BRIGHT_BLUE) << @@ -1441,7 +1435,7 @@ bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const outputs_visitor(std::vector& results_collector, blockchain_storage& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") { } - bool handle_output(const Transaction& tx, const TransactionOutput& out) { + bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) { //check tx unlock time if (!m_bch.is_tx_spendtime_unlocked(tx.unlockTime)) { logger(INFO, BRIGHT_WHITE) << @@ -1510,7 +1504,7 @@ bool blockchain_storage::check_block_timestamp(std::vector timestamps, return true; } - uint64_t median_ts = medianValue(timestamps); + uint64_t median_ts = Common::medianValue(timestamps); if (b.timestamp < median_ts) { logger(INFO, BRIGHT_WHITE) << @@ -1591,7 +1585,7 @@ bool blockchain_storage::update_next_comulative_size_limit() { std::vector sz; get_last_n_blocks_sizes(sz, m_currency.rewardBlocksWindow()); - uint64_t median = medianValue(sz); + uint64_t median = Common::medianValue(sz); if (median <= nextBlockGrantedFullRewardZone) { median = nextBlockGrantedFullRewardZone; } @@ -2087,4 +2081,75 @@ bool blockchain_storage::getBlockIds(uint64_t startHeight, size_t maxCount, std: return m_blockIndex.getBlockIds(startHeight, maxCount, items); } +bool blockchain_storage::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { + std::lock_guard lk(m_blockchain_lock); + auto it = m_transactionMap.find(txId); + if(it == m_transactionMap.end()) { + return false; + } else { + blockHeight = m_blocks[it->second.block].height; + blockId = get_block_id_by_height(blockHeight); + return true; + } +} + +bool blockchain_storage::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { + std::lock_guard lk(m_blockchain_lock); + + // try to find block in main chain + uint64_t height = 0; + if (m_blockIndex.getBlockHeight(hash, height)) { + generatedCoins = m_blocks[height].already_generated_coins; + return true; + } + + // try to find block in alternative chain + auto blockByHashIterator = m_alternative_chains.find(hash); + if (blockByHashIterator != m_alternative_chains.end()) { + generatedCoins = blockByHashIterator->second.already_generated_coins; + return true; + } + + logger(DEBUGGING) << "Can't find block with hash " << hash << " to get already generated coins."; + return false; +} + +bool blockchain_storage::getBlockSize(const crypto::hash& hash, size_t& size) { + std::lock_guard lk(m_blockchain_lock); + + // try to find block in main chain + uint64_t height = 0; + if (m_blockIndex.getBlockHeight(hash, height)) { + size = m_blocks[height].block_cumulative_size; + return true; + } + + // try to find block in alternative chain + auto blockByHashIterator = m_alternative_chains.find(hash); + if (blockByHashIterator != m_alternative_chains.end()) { + size = blockByHashIterator->second.block_cumulative_size; + return true; + } + + logger(DEBUGGING) << "Can't find block with hash " << hash << " to get block size."; + return false; +} + +bool blockchain_storage::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { + std::lock_guard lk(m_blockchain_lock); + MultisignatureOutputsContainer::const_iterator amountIter = m_multisignatureOutputs.find(txInMultisig.amount); + if (amountIter == m_multisignatureOutputs.end()) { + logger(DEBUGGING) << "Transaction contains multisignature input with invalid amount."; + return false; + } + if (amountIter->second.size() <= txInMultisig.outputIndex) { + logger(DEBUGGING) << "Transaction contains multisignature input with invalid outputIndex."; + return false; + } + const MultisignatureOutputUsage& outputIndex = amountIter->second[txInMultisig.outputIndex]; + const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx; + outputReference.first = get_transaction_hash(outputTransaction); + outputReference.second = outputIndex.outputIndex; + return true; +} } diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h index ba0e29e788..6b0781dead 100644 --- a/src/cryptonote_core/blockchain_storage.h +++ b/src/cryptonote_core/blockchain_storage.h @@ -103,8 +103,16 @@ namespace CryptoNote { uint64_t get_current_comulative_blocksize_limit(); bool is_storing_blockchain(){return m_is_blockchain_storing;} uint64_t block_difficulty(size_t i); - bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, std::vector& new_txs, std::vector& deleted_tx_ids); + bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds); + void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds); + bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight); + bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins); + bool getBlockSize(const crypto::hash& hash, size_t& size); + bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference); + template bool scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); template bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { @@ -125,8 +133,8 @@ namespace CryptoNote { } template - void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { - std::lock_guard lk(m_blockchain_lock); + void getBlockchainTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { + std::lock_guard bcLock(m_blockchain_lock); for (const auto& tx_id : txs_ids) { auto it = m_transactionMap.find(tx_id); @@ -136,11 +144,21 @@ namespace CryptoNote { txs.push_back(transactionByIndex(it->second).tx); } } + } + + template + void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { + if (checkTxPool){ + std::lock_guard txLock(m_tx_pool); + + getBlockchainTransactions(txs_ids, txs, missed_txs); - if (checkTxPool) { auto poolTxIds = std::move(missed_txs); missed_txs.clear(); m_tx_pool.getTransactions(poolTxIds, txs, missed_txs); + + } else { + getBlockchainTransactions(txs_ids, txs, missed_txs); } } @@ -234,7 +252,6 @@ namespace CryptoNote { Logging::LoggerRef logger; bool storeCache(); - template bool scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); bool handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc); difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); @@ -315,7 +332,7 @@ namespace CryptoNote { return false; } - if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second])) { + if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second], amount_outs_vec[i].second)) { logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i; return false; } diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 99ef0be05d..17d533c569 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -28,7 +28,6 @@ #include "cryptonote_core/tx_extra.h" #include "serialization/binary_archive.h" #include "serialization/crypto.h" -#include "serialization/keyvalue_serialization.h" // eepe named serialization #include "serialization/json_archive.h" #include "serialization/serialization.h" #include "serialization/variant.h" diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index ceea5b819f..de23c8667d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -91,8 +91,8 @@ bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blo bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { return m_blockchain_storage.get_blocks(start_offset, count, blocks); } -void core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) { - m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); +void core::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { + m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs, checkTxPool); } bool core::get_alternative_blocks(std::list& blocks) { @@ -258,14 +258,6 @@ bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { return true; } -bool core::add_new_tx(const Transaction& tx, tx_verification_context& tvc, bool keeped_by_block) { - crypto::hash tx_hash = get_transaction_hash(tx); - 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); -} - size_t core::get_blockchain_total_transactions() { return m_blockchain_storage.get_total_transactions(); } @@ -347,14 +339,19 @@ bool core::handle_block_found(Block& b) { void core::on_synchronized() { m_miner->on_synchronized(); - } - //----------------------------------------------------------------------------------------------- - bool core::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { - isBcActual = m_blockchain_storage.getPoolSymmetricDifference(known_pool_tx_ids, known_block_id, new_txs, deleted_tx_ids); - return true; - } - //----------------------------------------------------------------------------------------------- - bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { +} +//----------------------------------------------------------------------------------------------- +bool core::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { + return m_blockchain_storage.getPoolChanges(tailBlockId, knownTxsIds, addedTxs, deletedTxsIds); +} +//----------------------------------------------------------------------------------------------- +void core::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) { + m_blockchain_storage.getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); +} +//----------------------------------------------------------------------------------------------- +bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { if (block_blob.size() > m_currency.maxBlockBlobSize()) { logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; bvc.m_verifivation_failed = true; @@ -430,8 +427,15 @@ bool core::check_tx_syntax(const Transaction& tx) { return true; } -void core::get_pool_transactions(std::list& txs) { +std::vector core::getPoolTransactions() { + std::list txs; m_mempool.get_transactions(txs); + + std::vector result; + for (auto& tx : txs) { + result.emplace_back(std::move(tx)); + } + return result; } bool core::get_short_chain_history(std::list& ids) { @@ -442,15 +446,11 @@ bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_R return m_blockchain_storage.handle_get_objects(arg, rsp); } -bool core::getBlockByHash(const crypto::hash &h, Block &blk) { - return core::get_block_by_hash(h, blk); -} - -crypto::hash core::get_block_id_by_height(uint64_t height) { +crypto::hash core::getBlockIdByHeight(uint64_t height) { return m_blockchain_storage.get_block_id_by_height(height); } -bool core::get_block_by_hash(const crypto::hash &h, Block &blk) { +bool core::getBlockByHash(const crypto::hash &h, Block &blk) { return m_blockchain_storage.get_block_by_hash(h, blk); } @@ -572,4 +572,51 @@ void core::blockchainUpdated() { return true; } +bool core::getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) { + return m_blockchain_storage.get_backward_blocks_sizes(fromHeight, sizes, count); +} + +bool core::getBlockSize(const crypto::hash& hash, size_t& size) { + return m_blockchain_storage.getBlockSize(hash, size); +} + +bool core::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { + return m_blockchain_storage.getAlreadyGeneratedCoins(hash, generatedCoins); +} + +bool core::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { + return m_currency.getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange); +} + +bool core::scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) { + struct outputs_visitor + { + std::list>& m_resultsCollector; + outputs_visitor(std::list>& resultsCollector):m_resultsCollector(resultsCollector){} + bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) + { + m_resultsCollector.push_back(std::make_pair(get_transaction_hash(tx), transactionOutputIndex)); + return true; + } + }; + + outputs_visitor vi(outputReferences); + + return m_blockchain_storage.scan_outputkeys_for_indexes(txInToKey, vi); +} + +bool core::getBlockDifficulty(uint64_t height, difficulty_type& difficulty) { + difficulty = m_blockchain_storage.block_difficulty(height); + return true; +} + +bool core::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { + return m_blockchain_storage.getBlockContainingTx(txId, blockId, blockHeight); +} + +bool core::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { + return m_blockchain_storage.getMultisigOutputReference(txInMultisig, outputReference); +} + } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index dd558bf2f6..d26891b936 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -65,6 +65,15 @@ namespace CryptoNote { // ICore virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) override; + virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) override; + virtual bool getBlockSize(const crypto::hash& hash, size_t& size) override; + virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) override; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) override; + virtual bool scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) override; + virtual bool getBlockDifficulty(uint64_t height, difficulty_type& difficulty) override; + virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) override; + virtual bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& output_reference) override; uint64_t get_current_blockchain_height(); bool have_block(const crypto::hash& id); @@ -82,12 +91,10 @@ namespace CryptoNote { } virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); - crypto::hash get_block_id_by_height(uint64_t height); - void get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); - bool get_block_by_hash(const crypto::hash &h, Block &blk); - //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - + virtual crypto::hash getBlockIdByHeight(uint64_t height) override; + void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) override; virtual bool getBlockByHash(const crypto::hash &h, Block &blk) override; + //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); bool get_alternative_blocks(std::list& blocks); size_t get_alternative_blocks_count(); @@ -95,14 +102,14 @@ namespace CryptoNote { void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); void set_checkpoints(checkpoints&& chk_pts); - void get_pool_transactions(std::list& txs); + std::vector getPoolTransactions() override; size_t get_pool_transactions_count(); size_t get_blockchain_total_transactions(); //bool get_outs(uint64_t amount, std::list& pkeys); virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); bool get_stat_info(core_stat_info& st_inf); - //bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); + virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); crypto::hash get_tail_id(); virtual 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); @@ -114,11 +121,13 @@ namespace CryptoNote { void print_blockchain_index(); std::string print_pool(bool short_format); void print_blockchain_outs(const std::string& file); - virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; + virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) override; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) override; private: bool add_new_tx(const 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 add_new_tx(const Transaction& tx, tx_verification_context& tvc, bool keeped_by_block); bool load_state_data(); bool parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); bool handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block); diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 3fac4aa217..e24b121ecf 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -22,7 +22,6 @@ #include "cryptonote_basic_impl.h" using namespace Logging; -using namespace epee; namespace CryptoNote { diff --git a/src/cryptonote_core/cryptonote_serialization.cpp b/src/cryptonote_core/cryptonote_serialization.cpp index 93317d8f28..d3c995f6e9 100644 --- a/src/cryptonote_core/cryptonote_serialization.cpp +++ b/src/cryptonote_core/cryptonote_serialization.cpp @@ -16,6 +16,7 @@ // along with Bytecoin. If not, see . #include "cryptonote_serialization.h" +#include "account.h" #include "serialization/ISerializer.h" #include "serialization/SerializationOverloads.h" @@ -132,13 +133,18 @@ void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNot } template -void serializePod(T& v, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.binary(&v, sizeof(v), name); +bool serializePod(T& v, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializer.binary(&v, sizeof(v), name); } -void serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, const std::string& name) { +bool serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, Common::StringView name) { std::size_t size = vector.size(); - serializer.beginArray(size, name); + + if (!serializer.beginArray(size, name)) { + vector.clear(); + return false; + } + vector.resize(size); for (size_t i = 0; i < size; ++i) { @@ -146,49 +152,51 @@ void serializeVarintVector(std::vector& vector, CryptoNote::ISerialize } serializer.endArray(); + return true; } } namespace crypto { -void serialize(public_key& pubKey, const std::string& name, CryptoNote::ISerializer& serializer) { - serializePod(pubKey, name, serializer); +bool serialize(public_key& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(pubKey, name, serializer); } -void serialize(secret_key& secKey, const std::string& name, CryptoNote::ISerializer& serializer) { - serializePod(secKey, name, serializer); +bool serialize(secret_key& secKey, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(secKey, name, serializer); } -void serialize(hash& h, const std::string& name, CryptoNote::ISerializer& serializer) { - serializePod(h, name, serializer); +bool serialize(hash& h, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(h, name, serializer); } -void serialize(key_image& keyImage, const std::string& name, CryptoNote::ISerializer& serializer) { - serializePod(keyImage, name, serializer); +bool serialize(key_image& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(keyImage, name, serializer); } -void serialize(chacha8_iv& chacha, const std::string& name, CryptoNote::ISerializer& serializer) { - serializePod(chacha, name, serializer); +bool serialize(chacha8_iv& chacha, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(chacha, name, serializer); } +bool serialize(signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(sig, name, serializer); +} + + } namespace CryptoNote { -void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionPrefix& txP, ISerializer& serializer) { serializer(txP.version, "version"); serializer(txP.unlockTime, "unlock_time"); serializer(txP.vin, "vin"); serializer(txP.vout, "vout"); serializeAsBinary(txP.extra, "extra", serializer); - serializer.endObject(); } -void serialize(Transaction& tx, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(Transaction& tx, ISerializer& serializer) { serializer(tx.version, "version"); serializer(tx.unlockTime, "unlock_time"); serializer(tx.vin, "vin"); @@ -228,13 +236,9 @@ void serialize(Transaction& tx, const std::string& name, ISerializer& serializer } } // serializer.endArray(); - - serializer.endObject(); } -void serialize(TransactionInput& in, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(TransactionInput& in, ISerializer& serializer) { if (serializer.type() == ISerializer::OUTPUT) { BinaryVariantTagGetter tagGetter; uint8_t tag = boost::apply_visitor(tagGetter, in); @@ -248,52 +252,33 @@ void serialize(TransactionInput& in, const std::string& name, ISerializer& seria getVariantValue(serializer, tag, in); } - - serializer.endObject(); } -void serialize(TransactionInputGenerate& gen, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionInputGenerate& gen, ISerializer& serializer) { serializer(gen.height, "height"); - serializer.endObject(); -} - -void serialize(TransactionInputToScript& script, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - serializer.endObject(); } -void serialize(TransactionInputToScriptHash& scripthash, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - serializer.endObject(); -} +void serialize(TransactionInputToScript& script, ISerializer& serializer) {} +void serialize(TransactionInputToScriptHash& scripthash, ISerializer& serializer) {} -void serialize(TransactionInputToKey& key, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionInputToKey& key, ISerializer& serializer) { serializer(key.amount, "amount"); serializeVarintVector(key.keyOffsets, serializer, "key_offsets"); serializer(key.keyImage, "k_image"); - serializer.endObject(); } -void serialize(TransactionInputMultisignature& multisignature, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionInputMultisignature& multisignature, ISerializer& serializer) { serializer(multisignature.amount, "amount"); serializer(multisignature.signatures, "signatures"); serializer(multisignature.outputIndex, "outputIndex"); - serializer.endObject(); } -void serialize(TransactionOutput& output, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionOutput& output, ISerializer& serializer) { serializer(output.amount, "amount"); serializer(output.target, "target"); - serializer.endObject(); } -void serialize(TransactionOutputTarget& output, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(TransactionOutputTarget& output, ISerializer& serializer) { if (serializer.type() == ISerializer::OUTPUT) { BinaryVariantTagGetter tagGetter; uint8_t tag = boost::apply_visitor(tagGetter, output); @@ -307,36 +292,21 @@ void serialize(TransactionOutputTarget& output, const std::string& name, ISerial getVariantValue(serializer, tag, output); } - - serializer.endObject(); -} - -void serialize(TransactionOutputToScript& script, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - serializer.endObject(); } -void serialize(TransactionOutputToScriptHash& scripthash, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - serializer.endObject(); -} +void serialize(TransactionOutputToScript& script, ISerializer& serializer) {} +void serialize(TransactionOutputToScriptHash& scripthash, ISerializer& serializer) {} -void serialize(TransactionOutputToKey& key, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionOutputToKey& key, ISerializer& serializer) { serializer(key.key, "key"); - serializer.endObject(); } -void serialize(TransactionOutputMultisignature& multisignature, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(TransactionOutputMultisignature& multisignature, ISerializer& serializer) { serializer(multisignature.keys, "keys"); serializer(multisignature.requiredSignatures, "required_signatures"); - serializer.endObject(); } -void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { serializer(pbs.m_parentBlock.majorVersion, "majorVersion"); if (BLOCK_MAJOR_VERSION_1 < pbs.m_parentBlock.majorVersion) { @@ -410,8 +380,6 @@ void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& for (crypto::hash& hash: pbs.m_parentBlock.blockchainBranch) { serializer(hash, ""); } - - serializer.endObject(); } void serializeBlockHeader(BlockHeader& header, ISerializer& serializer) { @@ -432,15 +400,11 @@ void serializeBlockHeader(BlockHeader& header, ISerializer& serializer) { } } -void serialize(BlockHeader& header, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); +void serialize(BlockHeader& header, ISerializer& serializer) { serializeBlockHeader(header, serializer); - serializer.endObject(); } -void serialize(Block& block, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(Block& block, ISerializer& serializer) { serializeBlockHeader(block, serializer); if (block.majorVersion == BLOCK_MAJOR_VERSION_2) { @@ -450,45 +414,40 @@ void serialize(Block& block, const std::string& name, ISerializer& serializer) { serializer(block.minerTx, "miner_tx"); serializer(block.txHashes, "tx_hashes"); - - serializer.endObject(); } -void serialize(AccountPublicAddress& address, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - - serializer(address.m_spendPublicKey, "spend_public_key"); - serializer(address.m_viewPublicKey, "view_public_key"); +void serialize(AccountPublicAddress& address, ISerializer& serializer) { + serializer(address.m_spendPublicKey, "m_spend_public_key"); + serializer(address.m_viewPublicKey, "m_view_public_key"); +} - serializer.endObject(); +void serialize(account_keys& keys, ISerializer& s) { + s(keys.m_account_address, "m_account_address"); + s(keys.m_spend_secret_key, "m_spend_secret_key"); + s(keys.m_view_secret_key, "m_view_secret_key"); } -void doSerialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer) { +void doSerialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer) { uint64_t depth = static_cast(tag.depth); serializer(depth, "depth"); tag.depth = static_cast(depth); serializer(tag.merkle_root, "merkle_root"); } -void serialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer) { if (serializer.type() == ISerializer::OUTPUT) { std::stringstream stream; BinaryOutputStreamSerializer output(stream); - doSerialize(tag, "", output); + doSerialize(tag, output); std::string field = stream.str(); serializer(field, ""); } else { std::string field; serializer(field, ""); - std::stringstream stream(field); BinaryInputStreamSerializer input(stream); - doSerialize(tag, "", input); + doSerialize(tag, input); } - - serializer.endObject(); } } //namespace CryptoNote diff --git a/src/cryptonote_core/cryptonote_serialization.h b/src/cryptonote_core/cryptonote_serialization.h index 9b72638e56..ecf3c8ec12 100644 --- a/src/cryptonote_core/cryptonote_serialization.h +++ b/src/cryptonote_core/cryptonote_serialization.h @@ -18,45 +18,46 @@ #pragma once #include "cryptonote_basic.h" - -namespace CryptoNote { -class ISerializer; -} +#include "serialization/ISerializer.h" namespace crypto { -void serialize(public_key& pubKey, const std::string& name, CryptoNote::ISerializer& enumerator); -void serialize(secret_key& secKey, const std::string& name, CryptoNote::ISerializer& enumerator); -void serialize(hash& h, const std::string& name, CryptoNote::ISerializer& enumerator); -void serialize(chacha8_iv& chacha, const std::string& name, CryptoNote::ISerializer& enumerator); -void serialize(key_image& keyImage, const std::string& name, CryptoNote::ISerializer& enumerator); +bool serialize(public_key& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(secret_key& secKey, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(hash& h, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(chacha8_iv& chacha, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(key_image& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer); } //namespace crypto namespace CryptoNote { -void serialize(ParentBlockSerializer& pbs, const std::string& name, ISerializer& serializer); -void serialize(TransactionPrefix& txP, const std::string& name, ISerializer& serializer); -void serialize(Transaction& tx, const std::string& name, ISerializer& serializer); -void serialize(TransactionInput& in, const std::string& name, ISerializer& serializer); -void serialize(TransactionOutput& in, const std::string& name, ISerializer& serializer); - -void serialize(TransactionInputGenerate& gen, const std::string& name, ISerializer& serializer); -void serialize(TransactionInputToScript& script, const std::string& name, ISerializer& serializer); -void serialize(TransactionInputToScriptHash& scripthash, const std::string& name, ISerializer& serializer); -void serialize(TransactionInputToKey& key, const std::string& name, ISerializer& serializer); -void serialize(TransactionInputMultisignature& multisignature, const std::string& name, ISerializer& serializer); - -void serialize(TransactionOutput& output, const std::string& name, ISerializer& serializer); - -void serialize(TransactionOutputTarget& output, const std::string& name, ISerializer& serializer); - -void serialize(TransactionOutputToScript& script, const std::string& name, ISerializer& serializer); -void serialize(TransactionOutputToScriptHash& scripthash, const std::string& name, ISerializer& serializer); -void serialize(TransactionOutputToKey& key, const std::string& name, ISerializer& serializer); -void serialize(TransactionOutputMultisignature& multisignature, const std::string& name, ISerializer& serializer); -void serialize(BlockHeader& header, const std::string& name, ISerializer& serializer); -void serialize(Block& block, const std::string& name, ISerializer& serializer); -void serialize(AccountPublicAddress& address, const std::string& name, ISerializer& serializer); -void serialize(tx_extra_merge_mining_tag& tag, const std::string& name, ISerializer& serializer); +void serialize(ParentBlockSerializer& pbs, ISerializer& serializer); +void serialize(TransactionPrefix& txP, ISerializer& serializer); +void serialize(Transaction& tx, ISerializer& serializer); +void serialize(TransactionInput& in, ISerializer& serializer); +void serialize(TransactionOutput& in, ISerializer& serializer); + +void serialize(TransactionInputGenerate& gen, ISerializer& serializer); +void serialize(TransactionInputToScript& script, ISerializer& serializer); +void serialize(TransactionInputToScriptHash& scripthash, ISerializer& serializer); +void serialize(TransactionInputToKey& key, ISerializer& serializer); +void serialize(TransactionInputMultisignature& multisignature, ISerializer& serializer); + +void serialize(TransactionOutput& output, ISerializer& serializer); + +void serialize(TransactionOutputTarget& output, ISerializer& serializer); + +void serialize(TransactionOutputToScript& script, ISerializer& serializer); +void serialize(TransactionOutputToScriptHash& scripthash, ISerializer& serializer); +void serialize(TransactionOutputToKey& key, ISerializer& serializer); +void serialize(TransactionOutputMultisignature& multisignature, ISerializer& serializer); +void serialize(BlockHeader& header, ISerializer& serializer); +void serialize(Block& block, ISerializer& serializer); +void serialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer); + +void serialize(AccountPublicAddress& address, ISerializer& serializer); +void serialize(account_keys& keys, ISerializer& s); + } //namespace CryptoNote diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_core/cryptonote_stat_info.h index 7405221875..0c7bec643e 100644 --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/cryptonote_core/cryptonote_stat_info.h @@ -16,8 +16,8 @@ // along with Bytecoin. If not, see . #pragma once -#include "serialization/keyvalue_serialization.h" +#include "serialization/ISerializer.h" namespace CryptoNote { @@ -29,12 +29,12 @@ namespace CryptoNote uint64_t alternative_blocks; std::string top_block_id_str; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_pool_size) - KV_SERIALIZE(blockchain_height) - KV_SERIALIZE(mining_speed) - KV_SERIALIZE(alternative_blocks) - KV_SERIALIZE(top_block_id_str) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(tx_pool_size) + KV_MEMBER(blockchain_height) + KV_MEMBER(mining_speed) + KV_MEMBER(alternative_blocks) + KV_MEMBER(top_block_id_str) + } }; } diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp index 809200bd59..8c7cddd075 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/cryptonote_core/miner.cpp @@ -25,14 +25,13 @@ #include #include #include +#include #include #include #include "cryptonote_format_utils.h" #include "Common/command_line.h" - -// epee -#include "storages/portable_storage_template_helper.h" +#include "serialization/SerializationTools.h" using namespace Logging; @@ -182,13 +181,18 @@ namespace CryptoNote } m_config_folder_path = boost::filesystem::path(config.extraMessages).parent_path().string(); m_config = boost::value_initialized(); - epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + CryptoNote::parameters::MINER_CONFIG_FILE_NAME); + + std::string filebuf; + if (Common::loadFileToString(m_config_folder_path + "/" + CryptoNote::parameters::MINER_CONFIG_FILE_NAME, filebuf)) { + loadFromJson(m_config, filebuf); + } + logger(INFO) << "Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index; } if(!config.startMining.empty()) { if (!m_currency.parseAccountAddressString(config.startMining, m_mine_address)) { - LOG_ERROR("Target account address " << config.startMining << " has wrong format, starting daemon canceled"); + logger(ERROR) << "Target account address " << config.startMining << " has wrong format, starting daemon canceled"; return false; } m_threads_total = 1; @@ -410,7 +414,7 @@ namespace CryptoNote --m_config.current_extra_message_index; } else { //success update, lets update config - epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + CryptoNote::parameters::MINER_CONFIG_FILE_NAME); + Common::saveStringToFile(m_config_folder_path + "/" + CryptoNote::parameters::MINER_CONFIG_FILE_NAME, storeToJson(m_config)); } } diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h index e2c3ba06bd..c52a195e60 100644 --- a/src/cryptonote_core/miner.h +++ b/src/cryptonote_core/miner.h @@ -22,9 +22,6 @@ #include #include -// epee -#include "serialization/keyvalue_serialization.h" - #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/Currency.h" #include "cryptonote_core/difficulty.h" @@ -34,6 +31,8 @@ #include +#include "serialization/ISerializer.h" + namespace CryptoNote { class miner { public: @@ -64,13 +63,11 @@ namespace CryptoNote { struct miner_config { uint64_t current_extra_message_index; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(current_extra_message_index) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(current_extra_message_index) + } }; - const Currency& m_currency; Logging::LoggerRef logger; @@ -81,7 +78,6 @@ namespace CryptoNote { std::atomic m_starter_nonce; difficulty_type m_diffic; - // volatile uint32_t m_thread_index; std::atomic m_threads_total; std::atomic m_pausers_count; std::mutex m_miners_count_lock; @@ -90,8 +86,6 @@ namespace CryptoNote { std::mutex m_threads_lock; i_miner_handler& m_handler; AccountPublicAddress m_mine_address; - //epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval; - //epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; OnceInInterval m_update_block_template_interval; OnceInInterval m_update_merge_hr_interval; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 36040c3616..5f5e8edf81 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -34,6 +34,8 @@ using namespace Logging; +#undef ERROR + namespace CryptoNote { //--------------------------------------------------------------------------------- @@ -130,8 +132,8 @@ namespace CryptoNote { const uint64_t fee = inputs_amount - outputs_amount; if (!keptByBlock && fee < m_currency.minimumFee()) { - logger(INFO) << "transaction fee is not enought: " << m_currency.formatAmount(fee) << - ", minumim fee: " << m_currency.formatAmount(m_currency.minimumFee()); + logger(INFO) << "transaction fee is not enough: " << m_currency.formatAmount(fee) << + ", minimum fee: " << m_currency.formatAmount(m_currency.minimumFee()); tvc.m_verifivation_failed = true; tvc.m_tx_fee_too_small = true; return false; @@ -165,6 +167,14 @@ namespace CryptoNote { std::lock_guard lock(m_transactions_lock); + if (!keptByBlock && m_recentlyDeletedTransactions.find(id) != m_recentlyDeletedTransactions.end()) { + logger(INFO) << "Trying to add recently deleted transaction. Ignore: " << id; + tvc.m_verifivation_failed = false; + tvc.m_should_be_relayed = false; + tvc.m_added_to_pool = false; + return true; + } + // add to pool { TransactionDetails txd; @@ -180,7 +190,10 @@ namespace CryptoNote { txd.lastFailedBlock.clear(); auto txd_p = m_transactions.insert(std::move(txd)); - if (!(txd_p.second)) { logger(ERROR, BRIGHT_RED) << "transaction already exists at inserting in memory pool"; return false; } + if (!(txd_p.second)) { + logger(ERROR, BRIGHT_RED) << "transaction already exists at inserting in memory pool"; + return false; + } } tvc.m_added_to_pool = true; @@ -372,6 +385,9 @@ namespace CryptoNote { m_spent_key_images.clear(); m_spentOutputs.clear(); } + + removeExpiredTransactions(); + // Ignore deserialization error return true; } @@ -399,23 +415,33 @@ namespace CryptoNote { bool tx_memory_pool::removeExpiredTransactions() { bool somethingRemoved = false; { - std::lock_guard lock(m_transactions_lock); + std::lock_guard lock(m_transactions_lock); - auto now = m_timeProvider.now(); + uint64_t now = m_timeProvider.now(); + + for (auto it = m_recentlyDeletedTransactions.begin(); it != m_recentlyDeletedTransactions.end();) { + uint64_t elapsedTimeSinceDeletion = now - it->second; + if (elapsedTimeSinceDeletion > m_currency.numberOfPeriodsToForgetTxDeletedFromPool() * m_currency.mempoolTxLiveTime()) { + it = m_recentlyDeletedTransactions.erase(it); + } else { + ++it; + } + } - for (auto it = m_transactions.begin(); it != m_transactions.end();) { - uint64_t txAge = now - it->receiveTime; - bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); + for (auto it = m_transactions.begin(); it != m_transactions.end();) { + uint64_t txAge = now - it->receiveTime; + bool remove = txAge > (it->keptByBlock ? m_currency.mempoolTxFromAltBlockLiveTime() : m_currency.mempoolTxLiveTime()); - if (remove) { - logger(TRACE) << "Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge; - it = removeTransaction(it); + if (remove) { + logger(TRACE) << "Tx " << it->id << " removed from tx pool due to outdated, age: " << txAge; + m_recentlyDeletedTransactions.emplace(it->id, now); + it = removeTransaction(it); somethingRemoved = true; - } else { - ++it; + } else { + ++it; + } } } - } if (somethingRemoved) { m_observerManager.notify(&ITxPoolObserver::txDeletedFromPool); diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 7276f2716c..d52173a1fd 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -127,7 +127,7 @@ namespace CryptoNote { } } -#define CURRENT_MEMPOOL_ARCHIVE_VER 10 +#define CURRENT_MEMPOOL_ARCHIVE_VER 11 template void serialize(archive_t & a, const unsigned int version) { @@ -139,6 +139,7 @@ namespace CryptoNote { a & m_transactions; a & m_spent_key_images; a & m_spentOutputs; + a & m_recentlyDeletedTransactions; } struct TransactionCheckInfo { @@ -212,6 +213,7 @@ namespace CryptoNote { tx_container_t m_transactions; tx_container_t::nth_index<1>::type& m_fee_index; + std::unordered_map m_recentlyDeletedTransactions; Logging::LoggerRef logger; diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h index 70cc1b544c..346bf05a00 100644 --- a/src/cryptonote_core/verification_context.h +++ b/src/cryptonote_core/verification_context.h @@ -36,5 +36,6 @@ namespace CryptoNote bool m_verifivation_failed; //bad block, should drop connection bool m_marked_as_orphaned; bool m_already_exists; + bool m_switched_to_alt_chain; }; } diff --git a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h b/src/cryptonote_protocol/ICryptonoteProtocolObserver.h index 3f90904897..34c1140215 100644 --- a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h +++ b/src/cryptonote_protocol/ICryptonoteProtocolObserver.h @@ -27,6 +27,7 @@ class ICryptonoteProtocolObserver { public: virtual void peerCountUpdated(size_t count) {} virtual void lastKnownBlockHeightUpdated(uint64_t height) {} + virtual void blockchainSynchronized(uint64_t topHeight) {} }; } //namespace CryptoNote diff --git a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h b/src/cryptonote_protocol/ICryptonoteProtocolQuery.h index 1eb79ec94a..b9d422790b 100644 --- a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h +++ b/src/cryptonote_protocol/ICryptonoteProtocolQuery.h @@ -18,6 +18,7 @@ #pragma once #include +#include namespace CryptoNote { class ICryptonoteProtocolObserver; @@ -29,6 +30,7 @@ class ICryptonoteProtocolQuery { virtual uint64_t getObservedHeight() const = 0; virtual size_t getPeerCount() const = 0; + virtual bool isSynchronized() const = 0; }; } //namespace CryptoNote diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 5745881343..6a1904d914 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -18,10 +18,13 @@ #pragma once #include -#include "serialization/keyvalue_serialization.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_protocol/blobdatatype.h" +// ISerializer-based serialization +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" +#include "cryptonote_core/cryptonote_serialization.h" namespace CryptoNote { @@ -35,21 +38,24 @@ namespace CryptoNote { blobdata block; std::list txs; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(block) - KV_SERIALIZE(txs) - END_KV_SERIALIZE_MAP() + + void serialize(ISerializer& s) { + KV_MEMBER(block); + KV_MEMBER(txs); + } + }; struct BlockFullInfo : public block_complete_entry { crypto::hash block_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB(block_id) - KV_SERIALIZE(block) - KV_SERIALIZE(txs) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(block_id); + KV_MEMBER(block); + KV_MEMBER(txs); + } + }; /************************************************************************/ @@ -61,11 +67,11 @@ namespace CryptoNote uint64_t current_blockchain_height; uint32_t hop; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(b) - KV_SERIALIZE(current_blockchain_height) - KV_SERIALIZE(hop) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(b) + KV_MEMBER(current_blockchain_height) + KV_MEMBER(hop) + } }; struct NOTIFY_NEW_BLOCK @@ -81,9 +87,10 @@ namespace CryptoNote { std::list txs; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(txs); + } + }; struct NOTIFY_NEW_TRANSACTIONS @@ -100,10 +107,10 @@ namespace CryptoNote std::list txs; std::list blocks; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + serializeAsBinary(txs, "txs", s); + serializeAsBinary(blocks, "blocks", s); + } }; struct NOTIFY_REQUEST_GET_OBJECTS @@ -114,17 +121,18 @@ namespace CryptoNote struct NOTIFY_RESPONSE_GET_OBJECTS_request { - std::list txs; - std::list blocks; - std::list missed_ids; - uint64_t current_blockchain_height; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs) - KV_SERIALIZE(blocks) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids) - KV_SERIALIZE(current_blockchain_height) - END_KV_SERIALIZE_MAP() + std::list txs; + std::list blocks; + std::list missed_ids; + uint64_t current_blockchain_height; + + void serialize(ISerializer& s) { + KV_MEMBER(txs) + KV_MEMBER(blocks) + serializeAsBinary(missed_ids, "missed_ids", s); + KV_MEMBER(current_blockchain_height) + } + }; struct NOTIFY_RESPONSE_GET_OBJECTS @@ -141,9 +149,9 @@ namespace CryptoNote { std::list block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + serializeAsBinary(block_ids, "block_ids", s); + } }; }; @@ -153,11 +161,11 @@ namespace CryptoNote uint64_t total_height; std::list m_block_ids; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(start_height) - KV_SERIALIZE(total_height) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(start_height) + KV_MEMBER(total_height) + serializeAsBinary(m_block_ids, "m_block_ids", s); + } }; struct NOTIFY_RESPONSE_CHAIN_ENTRY @@ -166,4 +174,19 @@ namespace CryptoNote typedef NOTIFY_RESPONSE_CHAIN_ENTRY_request request; }; + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_REQUEST_TX_POOL_request { + std::vector txs; + + void serialize(ISerializer& s) { + serializeAsBinary(txs, "txs", s); + } + }; + + struct NOTIFY_REQUEST_TX_POOL { + const static int ID = BC_COMMANDS_POOL_BASE + 8; + typedef NOTIFY_REQUEST_TX_POOL_request request; + }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler.cpp index 018656f14d..0edd5703f0 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include "cryptonote_core/cryptonote_basic_impl.h" @@ -34,7 +35,7 @@ namespace CryptoNote { namespace { template -bool post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, cryptonote_connection_context& context) { +bool post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, const cryptonote_connection_context& context) { return p2p.invoke_notify_to_peer(t_parametr::ID, LevinProtocol::encode(arg), context); } @@ -151,9 +152,12 @@ bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA if (context.m_state == cryptonote_connection_context::state_synchronizing) { } else if (m_core.have_block(hshd.top_id)) { - context.m_state = cryptonote_connection_context::state_normal; - if (is_inital) + if (is_inital) { on_connection_synchronized(); + context.m_state = cryptonote_connection_context::state_pool_sync_required; + } else { + context.m_state = cryptonote_connection_context::state_normal; + } } else { int64_t diff = static_cast(hshd.current_height) - static_cast(get_current_blockchain_height()); @@ -200,7 +204,7 @@ int notifyAdaptor(const std::string& reqBuf, cryptonote_connection_context& ctx, return handler(command, req, ctx); } -#define HANDLE_NOTIFY(CMD, Handler) case CMD::ID: { ret = notifyAdaptor(in, ctx, boost::bind(Handler, this, _1, _2, _3)); break; } +#define HANDLE_NOTIFY(CMD, Handler) case CMD::ID: { ret = notifyAdaptor(in, ctx, std::bind(Handler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); break; } int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, const std::string& in, std::string& out, cryptonote_connection_context& ctx, bool& handled) { int ret = 0; @@ -213,6 +217,7 @@ int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, cons HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) + HANDLE_NOTIFY(NOTIFY_REQUEST_TX_POOL, &cryptonote_protocol_handler::handleRequestTxPool) default: handled = false; @@ -262,6 +267,8 @@ int cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW m_core.get_short_chain_history(r.block_ids); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); + } else if (bvc.m_switched_to_alt_chain) { + requestMissingPoolTransactions(context); } return 1; @@ -276,14 +283,13 @@ int cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOT CryptoNote::tx_verification_context tvc = boost::value_initialized(); m_core.handle_incoming_tx(*tx_blob_it, tvc, false); if (tvc.m_verifivation_failed) { - logger(Logging::INFO) << context << "Tx verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; - return 1; + logger(Logging::INFO) << context << "Tx verification failed"; } - if (tvc.m_should_be_relayed) + if (!tvc.m_verifivation_failed && tvc.m_should_be_relayed) { ++tx_blob_it; - else - arg.txs.erase(tx_blob_it++); + } else { + tx_blob_it = arg.txs.erase(tx_blob_it); + } } if (arg.txs.size()) { @@ -394,7 +400,7 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY uint64_t height; crypto::hash top; m_core.get_blockchain_top(height, top); - logger(INFO, BRIGHT_GREEN) << "Local blockchain updated, new height = " << height; + logger(DEBUGGING, BRIGHT_GREEN) << "Local blockchain updated, new height = " << height; if (!m_stop && context.m_state == cryptonote_connection_context::state_synchronizing) { request_missing_objects(context, true); @@ -475,7 +481,7 @@ bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_ ++count; context.m_requested_objects.insert(*it); } - context.m_needed_objects.erase(it++); + it = context.m_needed_objects.erase(it); } logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size(); post_notify(*m_p2p, req, context); @@ -500,6 +506,8 @@ bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_ return false; } + requestMissingPoolTransactions(context); + context.m_state = cryptonote_connection_context::state_normal; logger(Logging::INFO, Logging::BRIGHT_GREEN) << context << "SYNCHRONIZED OK"; on_connection_synchronized(); @@ -519,6 +527,12 @@ bool cryptonote_protocol_handler::on_connection_synchronized() { << "Use \"help\" command to see the list of available commands." << ENDL << "**********************************************************************"; m_core.on_synchronized(); + + uint64_t height; + crypto::hash hash; + if (m_core.get_blockchain_top(height, hash)) { + m_observerManager.notify(&ICryptonoteProtocolObserver::blockchainSynchronized, height); + } } return true; } @@ -563,6 +577,30 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY return 1; } +int cryptonote_protocol_handler::handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, + cryptonote_connection_context& context) { + logger(Logging::TRACE) << context << "NOTIFY_REQUEST_TX_POOL: txs.size() = " << arg.txs.size(); + + std::vector addedTransactions; + std::vector deletedTransactions; + m_core.getPoolChanges(arg.txs, addedTransactions, deletedTransactions); + + if (!addedTransactions.empty()) { + NOTIFY_NEW_TRANSACTIONS::request notification; + for (auto& tx : addedTransactions) { + notification.txs.push_back(tx_to_blob(tx)); + } + + bool ok = post_notify(*m_p2p, notification, context); + if (!ok) { + logger(Logging::WARNING, Logging::BRIGHT_YELLOW) << "Failed to post notification NOTIFY_NEW_TRANSACTIONS to " << context.m_connection_id; + } + } + + return 1; +} + + void cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg) { auto buf = LevinProtocol::encode(arg); m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_BLOCK::ID, buf); @@ -573,6 +611,24 @@ void cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::re m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_TRANSACTIONS::ID, buf); } +void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonote_connection_context& context) { + if (context.version < P2PProtocolVersion::V1) { + return; + } + + auto poolTxs = m_core.getPoolTransactions(); + + NOTIFY_REQUEST_TX_POOL::request notification; + for (auto& tx : poolTxs) { + notification.txs.emplace_back(get_transaction_hash(tx)); + } + + bool ok = post_notify(*m_p2p, notification, context); + if (!ok) { + logger(Logging::WARNING, Logging::BRIGHT_YELLOW) << "Failed to post notification NOTIFY_REQUEST_TX_POOL to " << context.m_connection_id; + } +} + void cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context) { bool updated = false; { diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 1e2cd8aaae..9fccc75f79 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -35,9 +35,6 @@ #include -PUSH_WARNINGS -DISABLE_VS_WARNINGS(4355) - namespace System { class Dispatcher; } @@ -59,7 +56,7 @@ namespace CryptoNote void set_p2p_endpoint(i_p2p_endpoint* p2p); // ICore& get_core() { return m_core; } - bool is_synchronized() const { return m_synchronized; } + virtual bool isSynchronized() const override { return m_synchronized; } void log_connections(); // Interface t_payload_net_handler, where t_payload_net_handler is template argument of nodetool::node_server @@ -74,6 +71,7 @@ namespace CryptoNote int handleCommand(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, cryptonote_connection_context& context, bool& handled); virtual size_t getPeerCount() const; virtual uint64_t getObservedHeight() const; + void requestMissingPoolTransactions(const cryptonote_connection_context& context); private: //----------------- commands handlers ---------------------------------------------- @@ -83,6 +81,7 @@ namespace CryptoNote int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context); int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); + int handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, cryptonote_connection_context& context); //----------------- i_cryptonote_protocol ---------------------------------- virtual void relay_block(NOTIFY_NEW_BLOCK::request& arg) override; @@ -115,5 +114,3 @@ namespace CryptoNote tools::ObserverManager m_observerManager; }; } - -POP_WARNINGS diff --git a/src/daemon/DeamonCommandsHandler.cpp b/src/daemon/DeamonCommandsHandler.cpp index 3f81544bfb..241424ca02 100644 --- a/src/daemon/DeamonCommandsHandler.cpp +++ b/src/daemon/DeamonCommandsHandler.cpp @@ -265,7 +265,7 @@ bool DaemonCommandsHandler::print_tx(const std::vector& args) tx_ids.push_back(tx_hash); std::list txs; std::list missed_ids; - m_core.get_transactions(tx_ids, txs, missed_ids); + m_core.getTransactions(tx_ids, txs, missed_ids); if (1 == txs.size()) { print_as_json(txs.front()); diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 697f20e7d0..3c5e6957c3 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -17,6 +17,7 @@ #include "version.h" +#include #include #include "DaemonCommandsHandler.h" diff --git a/src/node_rpc_proxy/NodeRpcProxy.cpp b/src/node_rpc_proxy/NodeRpcProxy.cpp index 4d88d3844d..3e3c109527 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.cpp +++ b/src/node_rpc_proxy/NodeRpcProxy.cpp @@ -32,6 +32,10 @@ #include "rpc/HttpClient.h" #include "rpc/JsonRpc.h" +#ifndef AUTO_VAL_INIT +#define AUTO_VAL_INIT(n) boost::value_initialized() +#endif + namespace CryptoNote { namespace { @@ -124,9 +128,9 @@ NodeRpcProxy::~NodeRpcProxy() { void NodeRpcProxy::resetInternalState() { m_ioService.reset(); - m_peerCount = 0; - m_nodeHeight = 0; - m_networkHeight = 0; + m_peerCount.store(0, std::memory_order_relaxed); + m_nodeHeight.store(0, std::memory_order_relaxed); + m_networkHeight.store(0, std::memory_order_relaxed); m_lastKnowHash = CryptoNote::null_hash; } @@ -199,16 +203,16 @@ void NodeRpcProxy::updateNodeStatus() { if (blockHash != m_lastKnowHash) { m_lastKnowHash = blockHash; - m_nodeHeight = rsp.block_header.height; + m_nodeHeight.store(rsp.block_header.height, std::memory_order_relaxed); m_lastLocalBlockTimestamp = rsp.block_header.timestamp; // TODO request and update network height - m_networkHeight = m_nodeHeight; - m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); - //if (m_networkHeight != rsp.block_header.network_height) { - // m_networkHeight = rsp.block_header.network_height; + m_networkHeight.store(rsp.block_header.height, std::memory_order_relaxed); + m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight.load(std::memory_order_relaxed)); + //if (m_networkHeight.load(std::memory_order_relaxed) != rsp.block_header.network_height) { + // m_networkHeight.store(rsp.block_header.height, std::memory_order_relaxed); // m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); //} - m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight); + m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight.load(std::memory_order_relaxed)); } } @@ -226,7 +230,7 @@ void NodeRpcProxy::updatePeerCount() { size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count; if (peerCount != m_peerCount) { m_peerCount = peerCount; - m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount); + m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount.load(std::memory_order_relaxed)); } } } @@ -240,15 +244,15 @@ bool NodeRpcProxy::removeObserver(INodeObserver* observer) { } size_t NodeRpcProxy::getPeerCount() const { - return m_peerCount; + return m_peerCount.load(std::memory_order_relaxed); } uint64_t NodeRpcProxy::getLastLocalBlockHeight() const { - return m_nodeHeight; + return m_nodeHeight.load(std::memory_order_relaxed); } uint64_t NodeRpcProxy::getLastKnownBlockHeight() const { - return m_networkHeight; + return m_networkHeight.load(std::memory_order_relaxed); } uint64_t NodeRpcProxy::getLocalBlockCount() const { @@ -309,6 +313,18 @@ void NodeRpcProxy::queryBlocks(std::list&& knownBlockIds, uint64_t m_ioService.post(std::bind(&NodeRpcProxy::doQueryBlocks, this, std::move(knownBlockIds), timestamp, std::ref(newBlocks), std::ref(startHeight), callback)); } +void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& knownTxsIds, crypto::hash tailBlockId, + bool& isTailBlockActual, std::vector& addedTxs, + std::vector& deletedTxsIds, const Callback& callback) { + if (!m_initState.initialized()) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + m_ioService.post(std::bind(&NodeRpcProxy::doGetPoolSymmetricDifference, this, std::move(knownTxsIds), tailBlockId, + std::ref(isTailBlockActual), std::ref(addedTxs), std::ref(deletedTxsIds), callback)); +} + void NodeRpcProxy::doRelayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { COMMAND_RPC_SEND_RAW_TX::request req; COMMAND_RPC_SEND_RAW_TX::response rsp; @@ -385,9 +401,51 @@ void NodeRpcProxy::doQueryBlocks(const std::list& knownBlockIds, u callback(ec); } -void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) { - is_bc_actual = true; +void NodeRpcProxy::doGetPoolSymmetricDifference(const std::vector& knownTxsIds, + const crypto::hash& tailBlockId, bool& isTailBlockActual, + std::vector& addedTxs, + std::vector& deletedTxsIds, const Callback& callback) { + CryptoNote::COMMAND_RPC_GET_POOL_CHANGES::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_POOL_CHANGES::response rsp = AUTO_VAL_INIT(rsp); + + req.tailBlockId = tailBlockId; + req.knownTxsIds = knownTxsIds; + + std::error_code ec = binaryCommand(*m_httpClient, "/get_pool_changes.bin", req, rsp); + if (!ec) { + isTailBlockActual = rsp.isTailBlockActual; + if (isTailBlockActual) { + deletedTxsIds = std::move(rsp.deletedTxsIds); + + for (auto& txBlob : rsp.addedTxs) { + CryptoNote::Transaction tx; + if (!CryptoNote::parse_and_validate_tx_from_blob(txBlob, tx)) { + ec = make_error_code(error::INTERNAL_NODE_ERROR); + break; + } + + addedTxs.emplace_back(std::move(tx)); + } + } + } + + callback(ec); +} + +void NodeRpcProxy::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { callback(std::error_code()); -}; +} + +void NodeRpcProxy::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + callback(std::error_code()); +} + +void NodeRpcProxy::isSynchronized(bool& syncStatus, const Callback& callback) { + callback(std::error_code()); +} } diff --git a/src/node_rpc_proxy/NodeRpcProxy.h b/src/node_rpc_proxy/NodeRpcProxy.h index e8dd64a647..72c695260b 100644 --- a/src/node_rpc_proxy/NodeRpcProxy.h +++ b/src/node_rpc_proxy/NodeRpcProxy.h @@ -54,8 +54,14 @@ class NodeRpcProxy : public CryptoNote::INode { virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; - + // TODO INodeObserver::poolChanged() notification NOT implemented!!! + virtual void getPoolSymmetricDifference(std::vector&& knownTxsIds, crypto::hash tailBlockId, + bool& isTailBlockActual, std::vector& addedTxs, + std::vector& deletedTxsIds, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; unsigned int rpcTimeout() const { return m_rpcTimeout; } void rpcTimeout(unsigned int val) { m_rpcTimeout = val; } @@ -73,6 +79,9 @@ class NodeRpcProxy : public CryptoNote::INode { void doGetNewBlocks(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); void doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); void doQueryBlocks(const std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); + void doGetPoolSymmetricDifference(const std::vector& knownTxsIds, const crypto::hash& tailBlockId, + bool& isTailBlockActual, std::vector& addedTxs, + std::vector& deletedTxsIds, const Callback& callback); private: tools::InitState m_initState; @@ -89,9 +98,9 @@ class NodeRpcProxy : public CryptoNote::INode { uint64_t m_pullInterval; // Internal state - size_t m_peerCount; - uint64_t m_nodeHeight; - uint64_t m_networkHeight; + std::atomic m_peerCount; + std::atomic m_nodeHeight; + std::atomic m_networkHeight; crypto::hash m_lastKnowHash; uint64_t m_lastLocalBlockTimestamp; }; diff --git a/src/p2p/LevinProtocol.h b/src/p2p/LevinProtocol.h index 523e7ac206..5d335c9e08 100644 --- a/src/p2p/LevinProtocol.h +++ b/src/p2p/LevinProtocol.h @@ -17,10 +17,8 @@ #pragma once -#include "misc_log_ex.h" -#include "storages/portable_storage.h" -#include "storages/portable_storage_from_bin.h" -#include "storages/portable_storage_to_bin.h" +#include "serialization/KVBinaryInputStreamSerializer.h" +#include "serialization/KVBinaryOutputStreamSerializer.h" namespace System { class TcpConnection; @@ -72,20 +70,24 @@ class LevinProtocol { template static bool decode(const std::string& buf, T& value) { - epee::serialization::portable_storage stg; - if (!stg.load_from_binary(buf)) { + try { + std::stringstream stream(buf); + KVBinaryInputStreamSerializer serializer(stream); + serialize(value, serializer); + } catch (std::exception&) { return false; } - return value.load(stg); + + return true; } template static std::string encode(const T& value) { - std::string buf; - epee::serialization::portable_storage stg; - value.store(stg); - stg.store_to_binary(buf); - return buf; + KVBinaryOutputStreamSerializer serializer; + serialize(const_cast(value), serializer); + std::stringstream stream; + serializer.write(stream); + return stream.str(); } private: diff --git a/src/p2p/connection_context.h b/src/p2p/connection_context.h index 02cced1b94..97cf40218c 100644 --- a/src/p2p/connection_context.h +++ b/src/p2p/connection_context.h @@ -28,6 +28,7 @@ namespace CryptoNote { struct cryptonote_connection_context { + uint8_t version; boost::uuids::uuid m_connection_id; uint32_t m_remote_ip = 0; uint32_t m_remote_port = 0; @@ -40,6 +41,7 @@ struct cryptonote_connection_context { state_idle, state_normal, state_sync_required, + state_pool_sync_required, state_shutdown }; diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 0faf95d4f6..128c3e88b2 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -283,7 +283,7 @@ namespace CryptoNote std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); for(const std::string& pr_str: perrs) { - peerlist_entry pe = AUTO_VAL_INIT(pe); + peerlist_entry pe = boost::value_initialized(); pe.id = crypto::rand(); bool r = parse_peer_from_string(pe.adr, pr_str); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; } @@ -512,6 +512,8 @@ namespace CryptoNote proto.invoke(COMMAND_HANDSHAKE::ID, arg, rsp); + context.version = rsp.node_data.version; + if (rsp.node_data.network_id != m_network_id) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE Failed, wrong network! (" << rsp.node_data.network_id << "), closing connection."; return false; @@ -545,7 +547,7 @@ namespace CryptoNote bool node_server::timedSync() { - COMMAND_TIMED_SYNC::request arg = AUTO_VAL_INIT(arg); + COMMAND_TIMED_SYNC::request arg = boost::value_initialized(); m_payload_handler.get_payload_sync_data(arg.payload_data); auto cmdBuf = LevinProtocol::encode(arg); @@ -695,7 +697,7 @@ namespace CryptoNote return true; } - peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); + peerlist_entry pe_local = boost::value_initialized(); pe_local.adr = na; pe_local.id = raw->second.peer_id; time(&pe_local.last_seen); @@ -747,7 +749,7 @@ namespace CryptoNote continue; tried_peers.insert(random_index); - peerlist_entry pe = AUTO_VAL_INIT(pe); + peerlist_entry pe = boost::value_initialized(); bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to get random peer from peerlist(white:" << use_white_list << ")"; return false; } @@ -894,6 +896,7 @@ namespace CryptoNote bool node_server::get_local_node_data(basic_node_data& node_data) { + node_data.version = P2PProtocolVersion::CURRENT; time_t local_time; time(&local_time); node_data.local_time = local_time; @@ -907,34 +910,34 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- #ifdef ALLOW_DEBUG_COMMANDS - - bool node_server::check_trust(const proof_of_trust& tr) - { + + bool node_server::check_trust(const proof_of_trust &tr) { uint64_t local_time = time(NULL); - uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; - if(time_delata > 24*60*60 ) - { - logger(ERROR) << "check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time; + uint64_t time_delata = local_time > tr.time ? local_time - tr.time : tr.time - local_time; + + if (time_delata > 24 * 60 * 60) { + logger(ERROR) << "check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time; return false; } - if(m_last_stat_request_time >= tr.time ) - { - logger(ERROR) << "check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time; + + if (m_last_stat_request_time >= tr.time) { + logger(ERROR) << "check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time; return false; } - if(m_config.m_peer_id != tr.peer_id) - { - logger(ERROR) << "check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"; + + if (m_config.m_peer_id != tr.peer_id) { + logger(ERROR) << "check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id << ")"; return false; } - crypto::public_key pk = AUTO_VAL_INIT(pk); + + crypto::public_key pk; Common::podFromHex(CryptoNote::P2P_STAT_TRUSTED_PUB_KEY, pk); crypto::hash h = get_proof_of_trust_hash(tr); - if(!crypto::check_signature(h, pk, tr.sign)) - { + if (!crypto::check_signature(h, pk, tr.sign)) { logger(ERROR) << "check_trust failed: sign check failed"; return false; } + //update last request time m_last_stat_request_time = tr.time; return true; @@ -1081,7 +1084,9 @@ namespace CryptoNote int node_server::handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) { - if(arg.node_data.network_id != m_network_id) { + context.version = arg.node_data.version; + + if (arg.node_data.network_id != m_network_id) { logger(Logging::INFO) << context << "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id; context.m_state = cryptonote_connection_context::state_shutdown; return 1; @@ -1211,7 +1216,7 @@ namespace CryptoNote std::vector perrs = command_line::get_arg(vm, arg); for(const std::string& pr_str: perrs) { - net_address na = AUTO_VAL_INIT(na); + net_address na; if (!parse_peer_from_string(na, pr_str)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; @@ -1223,8 +1228,8 @@ namespace CryptoNote } void node_server::acceptLoop() { - try { - for (;;) { + for (;;) { + try { p2p_connection_context ctx(m_dispatcher, m_listener.accept()); ctx.m_connection_id = boost::uuids::random_generator()(); ctx.m_is_income = true; @@ -1240,10 +1245,11 @@ namespace CryptoNote ++m_spawnCount; m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connection))); + } catch (System::InterruptedException&) { + break; + } catch (const std::exception& e) { + logger(WARNING) << "Exception in acceptLoop: " << e.what(); } - } catch (System::InterruptedException&) { - } catch (const std::exception& e) { - logger(WARNING) << "Exception in acceptLoop: " << e.what(); } logger(DEBUGGING) << "acceptLoop finished"; @@ -1301,10 +1307,12 @@ namespace CryptoNote LevinProtocol::Command cmd; for (;;) { - if (ctx.m_state == cryptonote_connection_context::state_sync_required) { ctx.m_state = cryptonote_connection_context::state_synchronizing; m_payload_handler.start_sync(ctx); + } else if (ctx.m_state == cryptonote_connection_context::state_pool_sync_required) { + ctx.m_state = cryptonote_connection_context::state_normal; + m_payload_handler.requestMissingPoolTransactions(ctx); } if (!proto.readCommand(cmd)) { diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index f894606c61..cc57c41a35 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -184,10 +184,10 @@ namespace CryptoNote network_config m_net_config; uint64_t m_peer_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(m_net_config) - KV_SERIALIZE(m_peer_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(m_net_config) + KV_MEMBER(m_peer_id) + } }; config m_config; diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 198533b2e2..84619338bd 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -23,19 +23,25 @@ #include "cryptonote_config.h" #include "cryptonote_core/cryptonote_stat_info.h" -// epee -#include "serialization/keyvalue_serialization.h" +// new serialization +#include "serialization/ISerializer.h" +#include "serialization/SerializationOverloads.h" +#include "cryptonote_core/cryptonote_serialization.h" namespace CryptoNote { + inline bool serialize(uuid& v, Common::StringView name, ISerializer& s) { + return s.binary(&v, sizeof(v), name); + } + struct network_config { - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(connections_count) - KV_SERIALIZE(handshake_interval) - KV_SERIALIZE(packet_max_size) - KV_SERIALIZE(config_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(connections_count) + KV_MEMBER(handshake_interval) + KV_MEMBER(packet_max_size) + KV_MEMBER(config_id) + } uint32_t connections_count; uint32_t connection_timeout; @@ -46,19 +52,30 @@ namespace CryptoNote uint32_t send_peerlist_sz; }; + enum P2PProtocolVersion : uint8_t { + V0 = 0, + V1 = 1, + CURRENT = V1 + }; + struct basic_node_data { - uuid network_id; + uuid network_id; + uint8_t version; uint64_t local_time; uint32_t my_port; peerid_type peer_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) - KV_SERIALIZE(peer_id) - KV_SERIALIZE(local_time) - KV_SERIALIZE(my_port) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(network_id) + if (s.type() == ISerializer::INPUT) { + version = 0; + } + KV_MEMBER(version) + KV_MEMBER(peer_id) + KV_MEMBER(local_time) + KV_MEMBER(my_port) + } }; struct CORE_SYNC_DATA @@ -66,10 +83,10 @@ namespace CryptoNote uint64_t current_height; crypto::hash top_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(current_height) - KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(current_height) + KV_MEMBER(top_id) + } }; #define P2P_COMMANDS_POOL_BASE 1000 @@ -86,10 +103,11 @@ namespace CryptoNote basic_node_data node_data; CORE_SYNC_DATA payload_data; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(node_data) - KV_SERIALIZE(payload_data) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(node_data) + KV_MEMBER(payload_data) + } + }; struct response @@ -98,11 +116,11 @@ namespace CryptoNote CORE_SYNC_DATA payload_data; std::list local_peerlist; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(node_data) - KV_SERIALIZE(payload_data) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(node_data) + KV_MEMBER(payload_data) + serializeAsBinary(local_peerlist, "local_peerlist", s); + } }; }; @@ -117,9 +135,11 @@ namespace CryptoNote struct request { CORE_SYNC_DATA payload_data; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payload_data) - END_KV_SERIALIZE_MAP() + + void serialize(ISerializer& s) { + KV_MEMBER(payload_data) + } + }; struct response @@ -128,11 +148,11 @@ namespace CryptoNote CORE_SYNC_DATA payload_data; std::list local_peerlist; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(local_time) - KV_SERIALIZE(payload_data) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(local_time) + KV_MEMBER(payload_data) + serializeAsBinary(local_peerlist, "local_peerlist", s); + } }; }; @@ -154,9 +174,7 @@ namespace CryptoNote struct request { /*actually we don't need to send any real data*/ - - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) {} }; struct response @@ -164,10 +182,10 @@ namespace CryptoNote std::string status; peerid_type peer_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(peer_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(status) + KV_MEMBER(peer_id) + } }; }; @@ -182,11 +200,11 @@ namespace CryptoNote uint64_t time; crypto::signature sign; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(peer_id) - KV_SERIALIZE(time) - KV_SERIALIZE_VAL_POD_AS_BLOB(sign) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(peer_id) + KV_MEMBER(time) + KV_MEMBER(sign) + } }; inline crypto::hash get_proof_of_trust_hash(const proof_of_trust& pot) { @@ -203,9 +221,10 @@ namespace CryptoNote struct request { proof_of_trust tr; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tr) - END_KV_SERIALIZE_MAP() + + void serialize(ISerializer& s) { + KV_MEMBER(tr) + } }; struct response @@ -216,13 +235,13 @@ namespace CryptoNote uint64_t incoming_connections_count; core_stat_info payload_info; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(version) - KV_SERIALIZE(os_version) - KV_SERIALIZE(connections_count) - KV_SERIALIZE(incoming_connections_count) - KV_SERIALIZE(payload_info) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(version) + KV_MEMBER(os_version) + KV_MEMBER(connections_count) + KV_MEMBER(incoming_connections_count) + KV_MEMBER(payload_info) + } }; }; @@ -237,25 +256,27 @@ namespace CryptoNote struct request { proof_of_trust tr; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tr) - END_KV_SERIALIZE_MAP() + + void serialize(ISerializer& s) { + KV_MEMBER(tr) + } }; struct response { - std::list local_peerlist_white; - std::list local_peerlist_gray; - std::list connections_list; + std::list local_peerlist_white; + std::list local_peerlist_gray; + std::list connections_list; peerid_type my_id; - uint64_t local_time; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list) - KV_SERIALIZE(my_id) - KV_SERIALIZE(local_time) - END_KV_SERIALIZE_MAP() + uint64_t local_time; + + void serialize(ISerializer& s) { + serializeAsBinary(local_peerlist_white, "local_peerlist_white", s); + serializeAsBinary(local_peerlist_gray, "local_peerlist_gray", s); + serializeAsBinary(connections_list, "connections_list", s); + KV_MEMBER(my_id) + KV_MEMBER(local_time) + } }; }; @@ -268,17 +289,16 @@ namespace CryptoNote struct request { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) {} }; struct response { peerid_type my_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(my_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(my_id) + } }; }; diff --git a/src/payment_service/ConfigurationManager.cpp b/src/payment_service/ConfigurationManager.cpp index 75ed1ee7e9..70b169b713 100644 --- a/src/payment_service/ConfigurationManager.cpp +++ b/src/payment_service/ConfigurationManager.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "ConfigurationManager.h" #include diff --git a/src/payment_service/ConfigurationManager.h b/src/payment_service/ConfigurationManager.h index a372a56d12..5b3b2c24d2 100644 --- a/src/payment_service/ConfigurationManager.h +++ b/src/payment_service/ConfigurationManager.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include "cryptonote_core/CoreConfig.h" diff --git a/src/payment_service/JsonRpcMessages.cpp b/src/payment_service/JsonRpcMessages.cpp index 7edac46673..ab4b68070e 100644 --- a/src/payment_service/JsonRpcMessages.cpp +++ b/src/payment_service/JsonRpcMessages.cpp @@ -15,145 +15,77 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "JsonRpcMessages.h" #include "serialization/SerializationOverloads.h" namespace PaymentService { -namespace { - -void throwIfRequiredParamsMissing(CryptoNote::ISerializer& serializer, const std::vector& names) { - bool r = true; - for (const auto name: names) { - r &= serializer.hasObject(name); - } +void TransferDestination::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(amount, "amount"); + r &= serializer(address, "address"); if (!r) { - throw RequestSerializationError(); + throw std::runtime_error("Required parameter is missing"); } } -void throwIfRequiredParamsMissing(CryptoNote::ISerializer& serializer, const char* name) { - throwIfRequiredParamsMissing(serializer, std::vector{name}); -} - -} - -void TransferDestination::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); - throwIfRequiredParamsMissing(serializer, {"amount", "address"}); - serializer(amount, "amount"); - serializer(address, "address"); - serializer.endObject(); -} - -void SendTransactionRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - throwIfRequiredParamsMissing(serializer, {"destinations", "fee", "mixin"}); - - serializer.beginObject(name); - - size_t size = destinations.size(); - serializer.beginArray(size, "destinations"); - destinations.resize(size); - - auto it = destinations.begin(); - for (size_t i = 0; i < size; ++i, ++it) { - it->serialize(serializer, ""); - } - serializer.endArray(); - - serializer(fee, "fee"); - serializer(mixin, "mixin"); - - if (serializer.hasObject("unlock_time")) { - serializer(unlockTime, "unlock_time"); - } +void SendTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(destinations, "destinations"); + r &= serializer(fee, "fee"); + r &= serializer(mixin, "mixin"); + serializer(unlockTime, "unlock_time"); + serializer(paymentId, "payment_id"); - if (serializer.hasObject("payment_id")) { - serializer(paymentId, "payment_id"); + if (!r) { + throw std::runtime_error("Required parameter is missing"); } - - serializer.endObject(); } -void SendTransactionResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void SendTransactionResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(transactionId, "transaction_id"); - serializer.endObject(); } -void GetAddressResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetAddressResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(address, "address"); - serializer.endObject(); } -void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(actualBalance, "actual_balance"); - serializer.endObject(); } -void GetPendingBalanceResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetPendingBalanceResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(pendingBalance, "pending_balance"); - serializer.endObject(); } -void GetTransactionsCountResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetTransactionsCountResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(transactionsCount, "transactions_count"); - serializer.endObject(); } -void GetTransfersCountResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetTransfersCountResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(transfersCount, "transfers_count"); - serializer.endObject(); } -void GetTransactionIdByTransferIdRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - throwIfRequiredParamsMissing(serializer, "transfer_id"); +void GetTransactionIdByTransferIdRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(transferId, "transfer_id"); - serializer.beginObject(name); - serializer(transferId, "transfer_id"); - serializer.endObject(); + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } } -void GetTransactionIdByTransferIdResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetTransactionIdByTransferIdResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(transactionid, "transaction_id"); - serializer.endObject(); } -void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - throwIfRequiredParamsMissing(serializer, "transaction_id"); +void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(transactionId, "transaction_id"); - serializer.beginObject(name); - serializer(transactionId, "transaction_id"); - serializer.endObject(); + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } } -void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); - +void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) { serializer(firstTransferId, "first_transfer_id"); serializer(transferCount, "transfer_count"); serializer(totalAmount, "total_amount"); @@ -163,86 +95,71 @@ void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer, const st serializer(blockHeight, "block_height"); serializer(timestamp, "timestamp"); serializer(extra, "extra"); - - serializer.endObject(); } -void GetTransactionResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); - +void GetTransactionResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(found, "found"); if (!found) { - serializer.endObject(); - return; + serializer(transactionInfo, "transaction_info"); } +} - transactionInfo.serialize(serializer, "transaction_info"); +void ListTransactionsRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(startingTransactionId, "starting_transaction_id"); + r &= serializer(maxTransactionCount, "max_transaction_count"); - serializer.endObject(); + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } } -void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void ListTransactionsResponse::serialize(CryptoNote::ISerializer& serializer) { + serializer(transactions, "transactions"); +} + +void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) { serializer(address, "address"); serializer(amount, "amount"); - serializer.endObject(); } -void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - throwIfRequiredParamsMissing(serializer, "transfer_id"); +void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(transferId, "transfer_id"); - serializer.beginObject(name); - serializer(transferId, "transfer_id"); - serializer.endObject(); + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } } -void GetTransferResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void GetTransferResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(found, "found"); - - if (!found) { - serializer.endObject(); - return; + if (found) { + serializer(transferInfo, "transfer_info"); } - - transferInfo.serialize(serializer, "transfer_info"); - - serializer.endObject(); } -void GetIncomingPaymentsRequest::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - throwIfRequiredParamsMissing(serializer, "payments"); +void GetIncomingPaymentsRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(payments, "payments"); - serializer.beginObject(name); - serializer(payments, "payments"); - serializer.endObject(); + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } } -void PaymentsById::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); - +void PaymentsById::serialize(CryptoNote::ISerializer& serializer) { serializer(id, "id"); serializer(payments, "payments"); - - serializer.endObject(); } -void GetIncomingPaymentsResponse::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); - +void GetIncomingPaymentsResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(payments, "payments"); - - serializer.endObject(); } -void PaymentDetails::serialize(CryptoNote::ISerializer& serializer, const std::string& name) { - serializer.beginObject(name); +void PaymentDetails::serialize(CryptoNote::ISerializer& serializer) { serializer(txHash, "tx_hash"); serializer(amount, "amount"); serializer(blockHeight, "block_height"); serializer(unlockTime, "unlock_time"); - serializer.endObject(); } } diff --git a/src/payment_service/JsonRpcMessages.h b/src/payment_service/JsonRpcMessages.h index 098e75ccb4..c3862b1871 100644 --- a/src/payment_service/JsonRpcMessages.h +++ b/src/payment_service/JsonRpcMessages.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include "serialization/ISerializer.h" @@ -49,7 +32,7 @@ struct TransferDestination { uint64_t amount; std::string address; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct SendTransactionRequest { @@ -60,59 +43,59 @@ struct SendTransactionRequest { uint64_t unlockTime; std::string paymentId; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct SendTransactionResponse { uint64_t transactionId; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetAddressResponse { std::string address; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetActualBalanceResponse { uint64_t actualBalance; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetPendingBalanceResponse { uint64_t pendingBalance; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransactionsCountResponse { uint64_t transactionsCount; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransfersCountResponse { uint64_t transfersCount; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransactionIdByTransferIdRequest { uint64_t transferId; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransactionIdByTransferIdResponse { uint64_t transactionid; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransactionRequest { uint64_t transactionId; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct TransactionRpcInfo { @@ -126,40 +109,53 @@ struct TransactionRpcInfo { uint64_t timestamp; std::string extra; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransactionResponse { bool found; TransactionRpcInfo transactionInfo; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct ListTransactionsRequest { + uint32_t startingTransactionId; + uint32_t maxTransactionCount; + + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct ListTransactionsResponse { + std::vector transactions; + + void serialize(CryptoNote::ISerializer& serializer); }; struct TransferRpcInfo { std::string address; int64_t amount; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransferRequest { uint64_t transferId; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetTransferResponse { bool found; TransferRpcInfo transferInfo; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetIncomingPaymentsRequest { std::vector payments; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct PaymentDetails @@ -169,20 +165,20 @@ struct PaymentDetails uint64_t blockHeight; uint64_t unlockTime; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct PaymentsById { std::string id; std::vector payments; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; struct GetIncomingPaymentsResponse { std::vector payments; - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + void serialize(CryptoNote::ISerializer& serializer); }; } //namespace PaymentService diff --git a/src/payment_service/JsonRpcServer.cpp b/src/payment_service/JsonRpcServer.cpp index fbd604f5bd..83cc9debf3 100644 --- a/src/payment_service/JsonRpcServer.cpp +++ b/src/payment_service/JsonRpcServer.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "JsonRpcServer.h" #include @@ -59,65 +42,21 @@ namespace PaymentService { JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) : - system(sys), - stopEvent(stopEvent), - service(service), - logger(loggerGroup, "JsonRpcServer") + HttpServer(sys, loggerGroup), + system(sys), + stopEvent(stopEvent), + service(service), + logger(loggerGroup, "JsonRpcServer") { } void JsonRpcServer::start(const Configuration& config) { - logger(Logging::INFO) << "Starting server on " << config.bindAddress << ":" << config.bindPort; - - try { - System::TcpListener listener(system, System::Ipv4Address(config.bindAddress), config.bindPort); - system.spawn([this, &listener] () {this->stopEvent.wait(); listener.stop(); }); - for (;;) { - System::TcpConnection connection = listener.accept(); - system.spawn(std::bind(&JsonRpcServer::sessionProcedure, this, new System::TcpConnection(std::move(connection)))); - } - } catch (System::InterruptedException&) { - logger(Logging::DEBUGGING) << "Server is stopped"; - } catch (std::exception& ex) { - logger(Logging::FATAL) << ex.what(); - } + HttpServer::start(config.bindAddress, config.bindPort); + stopEvent.wait(); + HttpServer::stop(); } -void JsonRpcServer::sessionProcedure(System::TcpConnection* tcpConnection) { - logger(Logging::DEBUGGING) << "new connection has been accepted"; - std::unique_ptr connection(tcpConnection); - - System::TcpStreambuf streambuf(*connection); - std::iostream stream(&streambuf); - - CryptoNote::HttpParser parser; - - try { - for (;;) { - CryptoNote::HttpRequest req; - CryptoNote::HttpResponse resp; - - parser.receiveRequest(stream, req); - processHttpRequest(req, resp); - - stream << resp; - stream.flush(); - } - } catch (std::system_error& e) { - //todo: write error conditions - if (e.code().category() == CryptoNote::error::HttpParserErrorCategory::INSTANCE) { - if (e.code().value() == CryptoNote::error::END_OF_STREAM) { - logger(Logging::DEBUGGING) << "The client is disconnected"; - return; - } - } - logger(Logging::WARNING) << e.code().message(); - } catch (std::exception& e) { - logger(Logging::WARNING) << e.what(); - } -} - -void JsonRpcServer::processHttpRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { +void JsonRpcServer::processRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { try { logger(Logging::TRACE) << "HTTP request came: \n" << req; @@ -161,18 +100,16 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: std::string method = req("method").getString(); - CryptoNote::JsonInputValueSerializer inputSerializer; + CryptoNote::JsonInputValueSerializer inputSerializer(req("params")); CryptoNote::JsonOutputStreamSerializer outputSerializer; - inputSerializer.setJsonValue(&req("params")); - if (method == "send_transaction") { SendTransactionRequest sendReq; SendTransactionResponse sendResp; //XXX: refactor it when migrate to different exception types in different subsystems! try { - sendReq.serialize(inputSerializer, ""); + serialize(sendReq, inputSerializer); } catch (std::exception&) { makeGenericErrorReponse(resp, "Invalid Request", -32600); return; @@ -184,7 +121,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - sendResp.serialize(outputSerializer, ""); + serialize(sendResp, outputSerializer); } else if (method == "get_address") { GetAddressResponse getAddrResp; @@ -194,7 +131,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - getAddrResp.serialize(outputSerializer, ""); + serialize(getAddrResp, outputSerializer); } else if (method == "get_actual_balance") { GetActualBalanceResponse actualResp; @@ -204,7 +141,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - actualResp.serialize(outputSerializer, ""); + serialize(actualResp, outputSerializer); } else if (method == "get_pending_balance") { GetPendingBalanceResponse pendingResp; @@ -214,7 +151,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - pendingResp.serialize(outputSerializer, ""); + serialize(pendingResp, outputSerializer); } else if (method == "get_transactions_count") { GetTransactionsCountResponse txResp; @@ -224,7 +161,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - txResp.serialize(outputSerializer, ""); + serialize(txResp, outputSerializer); } else if (method == "get_transfers_count") { GetTransfersCountResponse trResp; @@ -234,14 +171,14 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - trResp.serialize(outputSerializer, ""); + serialize(trResp, outputSerializer); } else if (method == "get_transaction_id_by_transfer_id") { GetTransactionIdByTransferIdRequest getReq; GetTransactionIdByTransferIdResponse getResp; //XXX: refactor it when migrate to different exception types in different subsystems! try { - getReq.serialize(inputSerializer, ""); + serialize(getReq, inputSerializer); } catch (std::exception&) { makeGenericErrorReponse(resp, "Invalid Request", -32600); return; @@ -255,14 +192,14 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - getResp.serialize(outputSerializer, ""); + serialize(getResp, outputSerializer); } else if (method == "get_transaction") { GetTransactionRequest getReq; GetTransactionResponse getResp; //XXX: refactor it when migrate to different exception types in different subsystems! try { - getReq.serialize(inputSerializer, ""); + serialize(getReq, inputSerializer); } catch (std::exception&) { makeGenericErrorReponse(resp, "Invalid Request", -32600); return; @@ -274,14 +211,34 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - getResp.serialize(outputSerializer, ""); + serialize(getResp, outputSerializer); + } else if (method == "list_transactions") { + ListTransactionsRequest listReq; + ListTransactionsResponse listResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(listReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.listTransactions(static_cast(listReq.startingTransactionId), + listReq.maxTransactionCount, listResp.transactions); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(listResp, outputSerializer); } else if (method == "get_transfer") { GetTransferRequest getReq; GetTransferResponse getResp; //XXX: refactor it when migrate to different exception types in different subsystems! try { - getReq.serialize(inputSerializer, ""); + serialize(getReq, inputSerializer); } catch (std::exception&) { makeGenericErrorReponse(resp, "Invalid Request", -32600); return; @@ -293,14 +250,14 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - getResp.serialize(outputSerializer, ""); + serialize(getResp, outputSerializer); } else if (method == "get_incoming_payments") { GetIncomingPaymentsRequest getReq; GetIncomingPaymentsResponse getResp; //XXX: refactor it when migrate to different exception types in different subsystems! try { - getReq.serialize(inputSerializer, ""); + serialize(getReq, inputSerializer); } catch (std::exception&) { makeGenericErrorReponse(resp, "Invalid Request", -32600); return; @@ -326,15 +283,14 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: getResp.payments.push_back(std::move(pbid)); } - getResp.serialize(outputSerializer, ""); + serialize(getResp, outputSerializer); } else { logger(Logging::DEBUGGING) << "Requested method not found: " << method; makeMethodNotFoundResponse(resp); return; } - Common::JsonValue v = outputSerializer.getJsonValue(); - fillJsonResponse(v, resp); + fillJsonResponse(outputSerializer.getValue(), resp); } catch (RequestSerializationError&) { logger(Logging::WARNING) << "Wrong request came"; @@ -348,15 +304,11 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: void JsonRpcServer::prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp) { using Common::JsonValue; - if (req.count("id")) { - JsonValue id = req("id"); - resp.insert("id", id); + if (req.contains("id")) { + resp.insert("id", req("id")); } - - JsonValue jsonRpc; - jsonRpc = "2.0"; - - resp.insert("jsonrpc", jsonRpc); + + resp.insert("jsonrpc", "2.0"); } void JsonRpcServer::makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp) { diff --git a/src/payment_service/JsonRpcServer.h b/src/payment_service/JsonRpcServer.h index 63a66117c6..feb133e9c3 100644 --- a/src/payment_service/JsonRpcServer.h +++ b/src/payment_service/JsonRpcServer.h @@ -15,32 +15,18 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once -#include "PaymentServiceConfiguration.h" +#include + #include #include #include "Logging/ILogger.h" #include "Logging/LoggerRef.h" +#include "rpc/HttpServer.h" + +#include "PaymentServiceConfiguration.h" -#include namespace CryptoNote { class HttpResponse; @@ -59,7 +45,7 @@ namespace PaymentService { class WalletService; -class JsonRpcServer { +class JsonRpcServer : CryptoNote::HttpServer { public: JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); JsonRpcServer(const JsonRpcServer&) = delete; @@ -69,7 +55,9 @@ class JsonRpcServer { private: void sessionProcedure(System::TcpConnection* tcpConnection); - void processHttpRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp); + // HttpServer + virtual void processRequest(const CryptoNote::HttpRequest& request, CryptoNote::HttpResponse& response) override; + void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp); void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); diff --git a/src/payment_service/NodeFactory.cpp b/src/payment_service/NodeFactory.cpp index a7e86b72ab..7c468c7f25 100644 --- a/src/payment_service/NodeFactory.cpp +++ b/src/payment_service/NodeFactory.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "NodeFactory.h" #include "node_rpc_proxy/NodeRpcProxy.h" @@ -58,16 +41,36 @@ class NodeRpcStub: public CryptoNote::INode { virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { callback(std::error_code()); } virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback) { callback(std::error_code()); } - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { callback(std::error_code()); } - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { callback(std::error_code()); } + std::vector& result, const Callback& callback) { } + virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + startHeight = 0; + callback(std::error_code()); + } + virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { } virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, - uint64_t& startHeight, const CryptoNote::INode::Callback& callback) { startHeight = 0; callback(std::error_code()); } + uint64_t& startHeight, const CryptoNote::INode::Callback& callback) { + startHeight = 0; + callback(std::error_code()); + } virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, - const Callback& callback) { callback(std::error_code()); } + const Callback& callback) { + is_bc_actual = true; + callback(std::error_code()); + } + + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, + const Callback& callback) override { } + + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, + const Callback& callback) override { } + + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, + const Callback& callback) override { } + + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override { } }; diff --git a/src/payment_service/NodeFactory.h b/src/payment_service/NodeFactory.h index fb50c3ff2c..09de8b4086 100644 --- a/src/payment_service/NodeFactory.h +++ b/src/payment_service/NodeFactory.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include "INode.h" diff --git a/src/payment_service/PaymentServiceConfiguration.cpp b/src/payment_service/PaymentServiceConfiguration.cpp index 71d51f1ba4..f749750bc9 100644 --- a/src/payment_service/PaymentServiceConfiguration.cpp +++ b/src/payment_service/PaymentServiceConfiguration.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "PaymentServiceConfiguration.h" #include diff --git a/src/payment_service/PaymentServiceConfiguration.h b/src/payment_service/PaymentServiceConfiguration.h index eda15e7ec1..42316518e1 100644 --- a/src/payment_service/PaymentServiceConfiguration.h +++ b/src/payment_service/PaymentServiceConfiguration.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/src/payment_service/RpcNodeConfiguration.cpp b/src/payment_service/RpcNodeConfiguration.cpp index 98e5d9a6c2..a0a7a04dfa 100644 --- a/src/payment_service/RpcNodeConfiguration.cpp +++ b/src/payment_service/RpcNodeConfiguration.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "RpcNodeConfiguration.h" namespace PaymentService { diff --git a/src/payment_service/RpcNodeConfiguration.h b/src/payment_service/RpcNodeConfiguration.h index 02fd6fbac3..82cc298f3e 100644 --- a/src/payment_service/RpcNodeConfiguration.h +++ b/src/payment_service/RpcNodeConfiguration.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/src/payment_service/WalletFactory.cpp b/src/payment_service/WalletFactory.cpp index 9b6d660a00..590960fa3b 100644 --- a/src/payment_service/WalletFactory.cpp +++ b/src/payment_service/WalletFactory.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "WalletFactory.h" #include "node_rpc_proxy/NodeRpcProxy.h" diff --git a/src/payment_service/WalletFactory.h b/src/payment_service/WalletFactory.h index 850c12db09..9df4b19e26 100644 --- a/src/payment_service/WalletFactory.h +++ b/src/payment_service/WalletFactory.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include "IWallet.h" diff --git a/src/payment_service/WalletObservers.cpp b/src/payment_service/WalletObservers.cpp index b9a62d54d1..db008960cd 100644 --- a/src/payment_service/WalletObservers.cpp +++ b/src/payment_service/WalletObservers.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "WalletObservers.h" #include diff --git a/src/payment_service/WalletObservers.h b/src/payment_service/WalletObservers.h index d083d3e7c7..e444772283 100644 --- a/src/payment_service/WalletObservers.h +++ b/src/payment_service/WalletObservers.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include "IWallet.h" diff --git a/src/payment_service/WalletService.cpp b/src/payment_service/WalletService.cpp index fdef45abab..0d7b0de0e1 100644 --- a/src/payment_service/WalletService.cpp +++ b/src/payment_service/WalletService.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "WalletService.h" #include "WalletServiceErrorCodes.h" @@ -50,6 +33,9 @@ #include #ifdef WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #else #include @@ -458,6 +444,40 @@ void WalletService::fillTransactionRpcInfo(const CryptoNote::TransactionInfo& tx rpcInfo.hash = Common::podToHex(txInfo.hash); } +std::error_code WalletService::listTransactions(CryptoNote::TransactionId startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo) { + logger(Logging::DEBUGGING) << "listTransactions request came"; + + if (maxTxCount == 0) { + txsRpcInfo.clear(); + return std::error_code(); + } + + try { + CryptoNote::TransactionId endTxId; + if (startingTxId > std::numeric_limits::max() - static_cast(maxTxCount)) { + endTxId = static_cast(wallet->getTransactionCount()); + } else { + endTxId = startingTxId + static_cast(maxTxCount); + endTxId = std::min(endTxId, static_cast(wallet->getTransactionCount())); + } + + txsRpcInfo.resize(endTxId - startingTxId); + + for (auto txId = startingTxId; txId < endTxId; ++txId) { + CryptoNote::TransactionInfo txInfo; + assert(txId < wallet->getTransactionCount()); + wallet->getTransaction(txId, txInfo); + + fillTransactionRpcInfo(txInfo, txsRpcInfo[txId - startingTxId]); + } + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to list transaction: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + std::error_code WalletService::getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo) { logger(Logging::DEBUGGING) << "getTransfer request came"; diff --git a/src/payment_service/WalletService.h b/src/payment_service/WalletService.h index b88dbf47b9..d70175f6be 100644 --- a/src/payment_service/WalletService.h +++ b/src/payment_service/WalletService.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include @@ -80,6 +63,7 @@ class WalletService : public CryptoNote::IWalletObserver { std::error_code getTransfersCount(uint64_t& trCount); std::error_code getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction); std::error_code getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo); + std::error_code listTransactions(CryptoNote::TransactionId startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo); std::error_code getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo); private: diff --git a/src/payment_service/WalletServiceErrorCodes.cpp b/src/payment_service/WalletServiceErrorCodes.cpp index 2ca1ff4125..61ef02f12a 100644 --- a/src/payment_service/WalletServiceErrorCodes.cpp +++ b/src/payment_service/WalletServiceErrorCodes.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "WalletServiceErrorCodes.h" namespace PaymentService { diff --git a/src/payment_service/WalletServiceErrorCodes.h b/src/payment_service/WalletServiceErrorCodes.h index f8e7456c7b..ba2dcbbe49 100644 --- a/src/payment_service/WalletServiceErrorCodes.h +++ b/src/payment_service/WalletServiceErrorCodes.h @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once #include diff --git a/src/payment_service/main.cpp b/src/payment_service/main.cpp index 8182e94ad1..d4e5805dcd 100644 --- a/src/payment_service/main.cpp +++ b/src/payment_service/main.cpp @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include #include #include @@ -52,6 +35,9 @@ #include #ifdef WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif #include #include #else @@ -73,13 +59,19 @@ #define SERVICE_NAME "Payment Gate" -PaymentService::ConfigurationManager config; -System::Dispatcher systemService; -System::Event stopEvent(systemService); -PaymentService::WalletService* service; -std::unique_ptr currencyBuilder; -Logging::LoggerGroup logger; -CryptoNote::node_server * gP2pNode = nullptr; +struct PaymentGate { + PaymentGate() : dispatcher(nullptr), stopEvent(nullptr), config(), service(nullptr), logger(), currencyBuilder(logger) { + } + + System::Dispatcher* dispatcher; + System::Event* stopEvent; + PaymentService::ConfigurationManager config; + PaymentService::WalletService* service; + Logging::LoggerGroup logger; + CryptoNote::CurrencyBuilder currencyBuilder; +}; + +PaymentGate* ppg; #ifdef WIN32 SERVICE_STATUS_HANDLE serviceStatusHandle; @@ -88,23 +80,20 @@ SERVICE_STATUS_HANDLE serviceStatusHandle; void run(); void stopSignalHandler() { - Logging::LoggerRef log(logger, "StopSignalHandler"); + Logging::LoggerRef log(ppg->logger, "StopSignalHandler"); log(Logging::INFO) << "Stop signal caught"; try { - if (service) { - service->saveWallet(); + if (ppg->service) { + ppg->service->saveWallet(); } } catch (std::exception& ex) { log(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); } - - if (gP2pNode != nullptr) { - gP2pNode->send_stop_signal(); - } else { - stopEvent.set(); - } + ppg->dispatcher->remoteSpawn([&]() { + ppg->stopEvent->set(); + }); } #ifdef WIN32 @@ -123,28 +112,34 @@ std::string GetLastErrorMessage(DWORD errorMessageID) void __stdcall serviceHandler(DWORD fdwControl) { if (fdwControl == SERVICE_CONTROL_STOP) { - Logging::LoggerRef log(logger, "serviceHandler"); + Logging::LoggerRef log(ppg->logger, "serviceHandler"); log(Logging::INFO) << "Stop signal caught"; SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_STOP_PENDING, 0, NO_ERROR, 0, 0, 0 }; SetServiceStatus(serviceStatusHandle, &serviceStatus); try { - if (service) { + if (ppg->service) { log(Logging::INFO) << "Saving wallet"; - service->saveWallet(); + ppg->service->saveWallet(); } } catch (std::exception& ex) { log(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); } log(Logging::INFO) << "Stopping service"; - stopEvent.set(); + ppg->dispatcher->remoteSpawn([&]() { + ppg->stopEvent->set(); + }); } } void __stdcall serviceMain(DWORD dwArgc, char **lpszArgv) { - Logging::LoggerRef logRef(logger, "WindowsService"); + System::Dispatcher dispatcher; + System::Event stopEvent(dispatcher); + ppg->dispatcher = &dispatcher; + ppg->stopEvent = &stopEvent; + Logging::LoggerRef logRef(ppg->logger, "WindowsService"); serviceStatusHandle = RegisterServiceCtrlHandler("PaymentGate", serviceHandler); if (serviceStatusHandle == NULL) { @@ -209,7 +204,7 @@ int runDaemon() { #ifdef WIN32 SERVICE_TABLE_ENTRY serviceTable[] { - { "PaymentGate", serviceMain }, + { "Payment Gate", serviceMain }, { NULL, NULL } }; @@ -230,6 +225,10 @@ int runDaemon() { return 1; } + System::Dispatcher dispatcher; + System::Event stopEvent(dispatcher); + ppg->dispatcher = &dispatcher; + ppg->stopEvent = &stopEvent; run(); return 0; @@ -238,7 +237,7 @@ int runDaemon() { int registerService() { #ifdef WIN32 - Logging::LoggerRef logRef(logger, "ServiceRegistrator"); + Logging::LoggerRef logRef(ppg->logger, "ServiceRegistrator"); char pathBuff[MAX_PATH]; std::string modulePath; @@ -295,7 +294,7 @@ int registerService() { int unregisterService() { #ifdef WIN32 - Logging::LoggerRef logRef(logger, "ServiceDeregistrator"); + Logging::LoggerRef logRef(ppg->logger, "ServiceDeregistrator"); SC_HANDLE scManager = NULL; SC_HANDLE scService = NULL; @@ -376,28 +375,27 @@ void changeDirectory(const std::string& path) { } void runInProcess() { - Logging::LoggerRef(logger, "run")(Logging::INFO) << "Starting Payment Gate with local node"; + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "Starting Payment Gate with local node"; - CryptoNote::Currency currency = currencyBuilder->currency(); - CryptoNote::core core(currency, NULL, logger); + CryptoNote::Currency currency = ppg->currencyBuilder.currency(); + CryptoNote::core core(currency, NULL, ppg->logger); - CryptoNote::cryptonote_protocol_handler protocol(currency, systemService, core, NULL, logger); - CryptoNote::node_server p2pNode(systemService, protocol, logger); - gP2pNode = &p2pNode; + CryptoNote::cryptonote_protocol_handler protocol(currency, *ppg->dispatcher, core, NULL, ppg->logger); + CryptoNote::node_server p2pNode(*ppg->dispatcher, protocol, ppg->logger); protocol.set_p2p_endpoint(&p2pNode); core.set_cryptonote_protocol(&protocol); std::unique_ptr node; - Logging::LoggerRef(logger, "run")(Logging::INFO) << "initializing p2pNode"; - if (!p2pNode.init(config.netNodeConfig, config.gateConfiguration.testnet)) { + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "initializing p2pNode"; + if (!p2pNode.init(ppg->config.netNodeConfig, ppg->config.gateConfiguration.testnet)) { throw std::runtime_error("Failed to init p2pNode"); } - Logging::LoggerRef(logger, "run")(Logging::INFO) << "initializing core"; + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "initializing core"; CryptoNote::MinerConfig emptyMiner; - core.init(config.coreConfig, emptyMiner, true); + core.init(ppg->config.coreConfig, emptyMiner, true); std::promise initPromise; auto initFuture = initPromise.get_future(); @@ -405,9 +403,9 @@ void runInProcess() { node.reset(new CryptoNote::InProcessNode(core, protocol)); node->init([&initPromise](std::error_code ec) { if (ec) { - Logging::LoggerRef(logger, "run")(Logging::INFO) << "Failed to init node: " << ec.message(); + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "Failed to init node: " << ec.message(); } else { - Logging::LoggerRef(logger, "run")(Logging::INFO) << "node is inited successfully"; + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "node is inited successfully"; } initPromise.set_value(ec); @@ -418,56 +416,57 @@ void runInProcess() { throw std::system_error(ec); } - Logging::LoggerRef(logger, "run")(Logging::INFO) << "Starting p2p server"; + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "Spawning p2p server"; - System::Event p2pStarted(systemService); + System::Event p2pStopped(*ppg->dispatcher); + System::Event p2pStarted(*ppg->dispatcher); - systemService.spawn([&]() { + ppg->dispatcher->spawn([&]() { p2pStarted.set(); p2pNode.run(); - stopEvent.set(); + p2pStopped.set(); }); - + p2pStarted.wait(); - Logging::LoggerRef(logger, "run")(Logging::INFO) << "p2p server is started"; - - service = new PaymentService::WalletService(currency, systemService, *node, config.gateConfiguration, logger); - std::unique_ptr serviceGuard(service); + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "p2p server is started"; - service->init(); + ppg->service = new PaymentService::WalletService(currency, *ppg->dispatcher, *node, ppg->config.gateConfiguration, ppg->logger); + std::unique_ptr serviceGuard(ppg->service); - PaymentService::JsonRpcServer rpcServer(systemService, stopEvent, *service, logger); + ppg->service->init(); - rpcServer.start(config.gateConfiguration); + PaymentService::JsonRpcServer rpcServer(*ppg->dispatcher, *ppg->stopEvent, *ppg->service, ppg->logger); + rpcServer.start(ppg->config.gateConfiguration); serviceGuard.reset(); + p2pNode.send_stop_signal(); + p2pStopped.wait(); node->shutdown(); core.deinit(); p2pNode.deinit(); - gP2pNode = nullptr; } void runRpcProxy() { - Logging::LoggerRef(logger, "run")(Logging::INFO) << "Starting Payment Gate with remote node"; - CryptoNote::Currency currency = currencyBuilder->currency(); + Logging::LoggerRef(ppg->logger, "run")(Logging::INFO) << "Starting Payment Gate with remote node"; + CryptoNote::Currency currency = ppg->currencyBuilder.currency(); std::unique_ptr node; - node.reset(PaymentService::NodeFactory::createNode(config.remoteNodeConfig.daemonHost, config.remoteNodeConfig.daemonPort)); + node.reset(PaymentService::NodeFactory::createNode(ppg->config.remoteNodeConfig.daemonHost, ppg->config.remoteNodeConfig.daemonPort)); - service = new PaymentService::WalletService(currency, systemService, *node, config.gateConfiguration, logger); - std::unique_ptr serviceGuard(service); + ppg->service = new PaymentService::WalletService(currency, *ppg->dispatcher, *node, ppg->config.gateConfiguration, ppg->logger); + std::unique_ptr serviceGuard(ppg->service); - service->init(); + ppg->service->init(); - PaymentService::JsonRpcServer rpcServer(systemService, stopEvent, *service, logger); + PaymentService::JsonRpcServer rpcServer(*ppg->dispatcher, *ppg->stopEvent, *ppg->service, ppg->logger); - rpcServer.start(config.gateConfiguration); + rpcServer.start(ppg->config.gateConfiguration); } void run() { tools::SignalHandler::install(stopSignalHandler); - if (config.startInprocess) { + if (ppg->config.startInprocess) { runInProcess(); } else { runRpcProxy(); @@ -475,62 +474,66 @@ void run() { } int main(int argc, char** argv) { + PaymentGate pg; + ppg = &pg; try { - if (!config.init(argc, argv)) { + if (!pg.config.init(argc, argv)) { return 0; //help message requested or so } - Logging::ConsoleLogger consoleLogger(static_cast(config.gateConfiguration.logLevel)); - logger.addLogger(consoleLogger); - - currencyBuilder.reset(new CryptoNote::CurrencyBuilder(logger)); + Logging::ConsoleLogger consoleLogger(static_cast(pg.config.gateConfiguration.logLevel)); + pg.logger.addLogger(consoleLogger); - Logging::LoggerRef(logger, "main")(Logging::INFO) << "PaymentService " << " v" << PROJECT_VERSION_LONG; + Logging::LoggerRef(pg.logger, "main")(Logging::INFO) << "PaymentService " << " v" << PROJECT_VERSION_LONG; - if (config.gateConfiguration.testnet) { - Logging::LoggerRef(logger, "main")(Logging::INFO) << "Starting in testnet mode"; - currencyBuilder->testnet(true); + if (pg.config.gateConfiguration.testnet) { + Logging::LoggerRef(pg.logger, "main")(Logging::INFO) << "Starting in testnet mode"; + pg.currencyBuilder.testnet(true); } - if (!config.gateConfiguration.serverRoot.empty()) { - changeDirectory(config.gateConfiguration.serverRoot); - Logging::LoggerRef(logger, "main")(Logging::INFO) << "Current working directory now is " << config.gateConfiguration.serverRoot; + if (!pg.config.gateConfiguration.serverRoot.empty()) { + changeDirectory(pg.config.gateConfiguration.serverRoot); + Logging::LoggerRef(pg.logger, "main")(Logging::INFO) << "Current working directory now is " << pg.config.gateConfiguration.serverRoot; } - std::ofstream fileStream(config.gateConfiguration.logFile, std::ofstream::app); + std::ofstream fileStream(pg.config.gateConfiguration.logFile, std::ofstream::app); if (!fileStream) { throw std::runtime_error("Couldn't open log file"); } - Logging::StreamLogger fileLogger(fileStream, static_cast(config.gateConfiguration.logLevel)); - logger.addLogger(fileLogger); + Logging::StreamLogger fileLogger(fileStream, static_cast(pg.config.gateConfiguration.logLevel)); + pg.logger.addLogger(fileLogger); - if (config.gateConfiguration.generateNewWallet) { - CryptoNote::Currency currency = currencyBuilder->currency(); - generateNewWallet(currency, config.gateConfiguration, logger); + if (pg.config.gateConfiguration.generateNewWallet) { + CryptoNote::Currency currency = pg.currencyBuilder.currency(); + generateNewWallet(currency, pg.config.gateConfiguration, pg.logger); return 0; } - if (!config.gateConfiguration.importKeys.empty()) { - importLegacyKeys(config.gateConfiguration); - Logging::LoggerRef(logger, "KeysImporter")(Logging::INFO) << "Keys have been imported successfully"; + if (!pg.config.gateConfiguration.importKeys.empty()) { + importLegacyKeys(pg.config.gateConfiguration); + Logging::LoggerRef(pg.logger, "KeysImporter")(Logging::INFO) << "Keys have been imported successfully"; return 0; } - if (config.gateConfiguration.registerService) { + if (pg.config.gateConfiguration.registerService) { return registerService(); } - if (config.gateConfiguration.unregisterService) { + if (pg.config.gateConfiguration.unregisterService) { return unregisterService(); } - if (config.gateConfiguration.daemonize) { - logger.removeLogger(consoleLogger); + if (pg.config.gateConfiguration.daemonize) { + pg.logger.removeLogger(consoleLogger); if (runDaemon() != 0) { throw std::runtime_error("Failed to start daemon"); } } else { + System::Dispatcher dispatcher; + System::Event stopEvent(dispatcher); + ppg->dispatcher = &dispatcher; + ppg->stopEvent = &stopEvent; run(); } diff --git a/src/rpc/HttpClient.h b/src/rpc/HttpClient.h index 3d7842bba8..0e351d7fb9 100644 --- a/src/rpc/HttpClient.h +++ b/src/rpc/HttpClient.h @@ -24,9 +24,7 @@ #include #include -// epee serialization -#include "misc_log_ex.h" -#include "storages/portable_storage_template_helper.h" +#include "serialization/SerializationTools.h" namespace CryptoNote { @@ -56,14 +54,14 @@ void invokeJsonCommand(HttpClient& client, const std::string& url, const Request HttpResponse hres; hreq.setUrl(url); - hreq.setBody(epee::serialization::store_t_to_json(req)); + hreq.setBody(storeToJson(req)); client.request(hreq, hres); if (hres.getStatus() != HttpResponse::STATUS_200) { throw std::runtime_error("HTTP status: " + std::to_string(hres.getStatus())); } - if (!epee::serialization::load_t_from_json(res, hres.getBody())) { + if (!loadFromJson(res, hres.getBody())) { throw std::runtime_error("Failed to parse JSON response"); } } @@ -74,10 +72,10 @@ void invokeBinaryCommand(HttpClient& client, const std::string& url, const Reque HttpResponse hres; hreq.setUrl(url); - hreq.setBody(epee::serialization::store_t_to_binary(req)); + hreq.setBody(storeToBinaryKeyValue(req)); client.request(hreq, hres); - if (!epee::serialization::load_t_from_binary(res, hres.getBody())) { + if (!loadFromBinaryKeyValue(res, hres.getBody())) { throw std::runtime_error("Failed to parse binary response"); } } diff --git a/src/rpc/JsonRpc.h b/src/rpc/JsonRpc.h index 525e9023f3..ee1ca60dec 100644 --- a/src/rpc/JsonRpc.h +++ b/src/rpc/JsonRpc.h @@ -21,10 +21,9 @@ #include #include -#include "misc_log_ex.h" -#include "storages/portable_storage_template_helper.h" -#include "serialization/enableable.h" -#include "serialization/keyvalue_serialization_overloads.h" +#include "serialization/ISerializer.h" +#include "serialization/SerializationTools.h" +#include namespace CryptoNote { @@ -52,27 +51,37 @@ class JsonRpcError: public std::exception { return message.c_str(); } + void serialize(ISerializer& s) { + s(code, "code"); + s(message, "message"); + } + int code; std::string message; }; -typedef boost::optional OptionalId; +typedef boost::optional OptionalId; class JsonRpcRequest { public: + + JsonRpcRequest() : psReq(Common::JsonValue::OBJECT) {} bool parseRequest(const std::string& requestBody) { - if (!psReq.load_from_json(requestBody)) { + try { + psReq = Common::JsonValue::fromString(requestBody); + } catch (std::exception&) { throw JsonRpcError(errParseError); } - OptionalId::value_type idValue; - if (psReq.get_value("id", idValue, nullptr)) { - id = idValue; + if (!psReq.contains("method")) { + throw JsonRpcError(errInvalidRequest); } - if (!psReq.get_value("method", method, nullptr)) { - throw JsonRpcError(errInvalidRequest); + method = psReq("method").getString(); + + if (psReq.contains("id")) { + id = psReq("id"); } return true; @@ -80,13 +89,15 @@ class JsonRpcRequest { template bool loadParams(T& v) const { - return epee::serialization::kv_unserialize(v, - const_cast(psReq), nullptr, "params"); + loadFromJsonValue(v, psReq.contains("params") ? + psReq("params") : Common::JsonValue(Common::JsonValue::NIL)); + return true; } template bool setParams(const T& v) { - return epee::serialization::kv_serialize(v, psReq, nullptr, "params"); + psReq.set("params", storeToJsonValue(v)); + return true; } const std::string& getMethod() const { @@ -102,16 +113,14 @@ class JsonRpcRequest { } std::string getBody() { - std::string reqBody; - psReq.set_value("jsonrpc", std::string("2.0"), nullptr); - psReq.set_value("method", method, nullptr); - psReq.dump_as_json(reqBody); - return reqBody; + psReq.set("jsonrpc", std::string("2.0")); + psReq.set("method", method); + return psReq.toString(); } private: - epee::serialization::portable_storage psReq; + Common::JsonValue psReq; OptionalId id; std::string method; }; @@ -120,55 +129,58 @@ class JsonRpcRequest { class JsonRpcResponse { public: - void parse(const std::string& resonseBody) { - if (!psResp.load_from_json(resonseBody)) { + JsonRpcResponse() : psResp(Common::JsonValue::OBJECT) {} + + void parse(const std::string& responseBody) { + try { + psResp = Common::JsonValue::fromString(responseBody); + } catch (std::exception&) { throw JsonRpcError(errParseError); } } void setId(const OptionalId& id) { if (id.is_initialized()) { - psResp.set_value("id", id.get(), nullptr); + psResp.insert("id", id.get()); } } void setError(const JsonRpcError& err) { - auto errorSection = psResp.open_section("error", nullptr, true); - psResp.set_value("code", err.code, errorSection); - psResp.set_value("message", err.message, errorSection); + psResp.set("error", storeToJsonValue(err)); } - bool getError(JsonRpcError& err) { - auto errorSection = psResp.open_section("error", nullptr, false); - if (!errorSection) { + bool getError(JsonRpcError& err) const { + if (!psResp.contains("error")) { return false; } - psResp.get_value("code", err.code, errorSection); - psResp.get_value("message", err.message, errorSection); + loadFromJsonValue(err, psResp("error")); return true; } std::string getBody() { - std::string responseBody; - psResp.set_value("jsonrpc", std::string("2.0"), nullptr); - psResp.dump_as_json(responseBody); - return responseBody; + psResp.set("jsonrpc", std::string("2.0")); + return psResp.toString(); } template bool setResult(const T& v) { - return epee::serialization::kv_serialize(v, psResp, nullptr, "result"); + psResp.set("result", storeToJsonValue(v)); + return true; } template bool getResult(T& v) const { - return epee::serialization::kv_unserialize(v, - const_cast(psResp), nullptr, "result"); + if (!psResp.contains("result")) { + return false; + } + + loadFromJsonValue(v, psResp("result")); + return true; } private: - epee::serialization::portable_storage psResp; + Common::JsonValue psResp; }; diff --git a/src/rpc/RpcServer.cpp b/src/rpc/RpcServer.cpp index 285efe85c1..594b17fae2 100644 --- a/src/rpc/RpcServer.cpp +++ b/src/rpc/RpcServer.cpp @@ -43,12 +43,12 @@ RpcServer::HandlerFunction binMethod(bool (RpcServer::*handler)(typename Command boost::value_initialized req; boost::value_initialized res; - if (!epee::serialization::load_t_from_binary(static_cast(req), request.getBody())) { + if (!loadFromBinaryKeyValue(static_cast(req), request.getBody())) { return false; } bool result = (obj->*handler)(req, res); - response.setBody(epee::serialization::store_t_to_binary(res.data())); + response.setBody(storeToBinaryKeyValue(res.data())); return result; }; } @@ -60,12 +60,12 @@ RpcServer::HandlerFunction jsonMethod(bool (RpcServer::*handler)(typename Comman boost::value_initialized req; boost::value_initialized res; - if (!epee::serialization::load_t_from_json(static_cast(req), request.getBody())) { + if (!loadFromJson(static_cast(req), request.getBody())) { return false; } bool result = (obj->*handler)(req, res); - response.setBody(epee::serialization::store_t_to_json(res.data())); + response.setBody(storeToJson(res.data())); return result; }; } @@ -79,6 +79,7 @@ std::unordered_map RpcServer::s_handler { "/queryblocks.bin", binMethod(&RpcServer::on_query_blocks) }, { "/get_o_indexes.bin", binMethod(&RpcServer::on_get_indexes) }, { "/getrandom_outs.bin", binMethod(&RpcServer::on_get_random_outs) }, + { "/get_pool_changes.bin", binMethod(&RpcServer::onGetPoolChanges) }, // json handlers { "/getinfo", jsonMethod(&RpcServer::on_get_info) }, @@ -125,7 +126,7 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& JsonRpcResponse jsonResponse; try { - + logger(TRACE) << "JSON-RPC request: " << request.getBody(); jsonRequest.parseRequest(request.getBody()); jsonResponse.setId(jsonRequest.getId()); // copy id @@ -153,16 +154,19 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& } catch (const JsonRpcError& err) { jsonResponse.setError(err); + } catch (const std::exception& e) { + jsonResponse.setError(JsonRpcError(JsonRpc::errInternalError, e.what())); } response.setBody(jsonResponse.getBody()); + logger(TRACE) << "JSON-RPC response: " << jsonResponse.getBody(); return true; } #define CHECK_CORE_READY() bool RpcServer::checkCoreReady() { - return m_core.is_ready() && m_p2p.get_payload_object().is_synchronized(); + return m_core.is_ready() && m_p2p.get_payload_object().isSynchronized(); } // @@ -244,6 +248,28 @@ bool RpcServer::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOU return true; } +bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp) { + CHECK_CORE_READY(); + + rsp.status = CORE_RPC_STATUS_OK; + std::vector addedTransactions; + rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds); + if (rsp.isTailBlockActual) { + for (auto& tx : addedTransactions) { + blobdata txBlob; + if (!CryptoNote::tx_to_blob(tx, txBlob)) { + rsp.status = "Internal error"; + break;; + } + + rsp.addedTxs.emplace_back(std::move(txBlob)); + } + } + + return true; +} + + // // JSON handlers // @@ -288,7 +314,7 @@ bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& } std::list missed_txs; std::list txs; - m_core.get_transactions(vh, txs, missed_txs); + m_core.getTransactions(vh, txs, missed_txs); for (auto& tx : txs) { blobdata blob = t_serializable_object_to_blob(tx); @@ -314,7 +340,7 @@ bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMM return true; } - tx_verification_context tvc = AUTO_VAL_INIT(tvc); + tx_verification_context tvc = boost::value_initialized(); if (!m_core.handle_incoming_tx(tx_blob, tvc, false)) { logger(INFO) << "[on_send_raw_tx]: Failed to process tx"; @@ -409,7 +435,7 @@ bool RpcServer::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, CO }; } - res = Common::podToHex(m_core.get_block_id_by_height(h)); + res = Common::podToHex(m_core.getBlockIdByHeight(h)); return true; } @@ -435,13 +461,13 @@ bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE, "To big reserved size, maximum 255" }; } - CryptoNote::AccountPublicAddress acc = AUTO_VAL_INIT(acc); + AccountPublicAddress acc = boost::value_initialized(); if (!req.wallet_address.size() || !m_core.currency().parseAccountAddressString(req.wallet_address, acc)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS, "Failed to parse wallet address" }; } - Block b = AUTO_VAL_INIT(b); + Block b = boost::value_initialized(); CryptoNote::blobdata blob_reserve; blob_reserve.resize(req.reserve_size, 0); if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { @@ -494,8 +520,7 @@ bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMM throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob" }; } - CryptoNote::block_verification_context bvc = AUTO_VAL_INIT(bvc); - + block_verification_context bvc = boost::value_initialized(); System::Event event(m_dispatcher); auto resultFuture = std::async(std::launch::async, [this, &event, &bvc, &blockblob]{ m_core.handle_incoming_block_blob(blockblob, bvc, true, true); @@ -547,7 +572,7 @@ bool RpcServer::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER } Block last_block; - if (!m_core.get_block_by_hash(last_block_hash, last_block)) { + if (!m_core.getBlockByHash(last_block_hash, last_block)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; } @@ -566,7 +591,7 @@ bool RpcServer::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_B } Block blk; - if (!m_core.get_block_by_hash(block_hash, blk)) { + if (!m_core.getBlockByHash(block_hash, blk)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get block by hash. Hash = " + req.hash + '.' }; @@ -590,9 +615,9 @@ bool RpcServer::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; } - crypto::hash block_hash = m_core.get_block_id_by_height(req.height); + crypto::hash block_hash = m_core.getBlockIdByHeight(req.height); Block blk; - if (!m_core.get_block_by_hash(block_hash, blk)) { + if (!m_core.getBlockByHash(block_hash, blk)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.' }; } diff --git a/src/rpc/RpcServer.h b/src/rpc/RpcServer.h index 35b85cbced..6c6ecc2a5f 100644 --- a/src/rpc/RpcServer.h +++ b/src/rpc/RpcServer.h @@ -48,6 +48,7 @@ class RpcServer : public HttpServer { bool on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res); bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::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 onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp); // json handlers bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index c42b4a376a..04e070b7ff 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -16,504 +16,427 @@ // along with Bytecoin. If not, see . #pragma once + #include "cryptonote_protocol/cryptonote_protocol_defs.h" #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/difficulty.h" #include "crypto/hash.h" -namespace CryptoNote -{ - //----------------------------------------------- -#define CORE_RPC_STATUS_OK "OK" -#define CORE_RPC_STATUS_BUSY "BUSY" - - struct COMMAND_RPC_GET_HEIGHT - { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - 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() - }; - }; +#include "serialization/SerializationOverloads.h" - struct COMMAND_RPC_GET_BLOCKS_FAST - { - - struct request - { - 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 */ - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::list blocks; - uint64_t start_height; - uint64_t current_height; - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(blocks) - KV_SERIALIZE(start_height) - KV_SERIALIZE(current_height) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - }; - //----------------------------------------------- - struct COMMAND_RPC_GET_TRANSACTIONS - { - struct request - { - std::list txs_hashes; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs_hashes) - END_KV_SERIALIZE_MAP() - }; - - - struct response - { - std::list txs_as_hex; //transactions blobs as hex - std::list missed_tx; //not found transactions - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(txs_as_hex) - KV_SERIALIZE(missed_tx) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - }; +namespace CryptoNote { +//----------------------------------------------- +#define CORE_RPC_STATUS_OK "OK" +#define CORE_RPC_STATUS_BUSY "BUSY" - //----------------------------------------------- - struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES - { - struct request - { - crypto::hash txid; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_VAL_POD_AS_BLOB(txid) - END_KV_SERIALIZE_MAP() - }; - - - struct response - { - std::vector o_indexes; - std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(o_indexes) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - }; - //----------------------------------------------- - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request - { - std::vector amounts; - uint64_t outs_count; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amounts) - KV_SERIALIZE(outs_count) - END_KV_SERIALIZE_MAP() - }; +struct EMPTY_STRUCT { + void serialize(ISerializer &s) {} +}; -#pragma pack (push, 1) - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry - { - uint64_t global_amount_index; - crypto::public_key out_key; - }; -#pragma pack(pop) +struct STATUS_STRUCT { + std::string status; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount - { - uint64_t amount; - std::list outs; + void serialize(ISerializer &s) { + KV_MEMBER(status) + } +}; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) - END_KV_SERIALIZE_MAP() - }; +struct COMMAND_RPC_GET_HEIGHT { + typedef EMPTY_STRUCT request; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response - { - std::vector outs; + struct response { + uint64_t height; std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(outs) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() + + void serialize(ISerializer &s) { + KV_MEMBER(height) + KV_MEMBER(status) + } }; +}; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS - { - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request request; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response response; +struct COMMAND_RPC_GET_BLOCKS_FAST { - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry out_entry; - typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount outs_for_amount; - }; - //----------------------------------------------- - struct COMMAND_RPC_SEND_RAW_TX - { - struct request - { - std::string tx_as_hex; - - request() {} - explicit request(const Transaction &); - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_as_hex) - END_KV_SERIALIZE_MAP() - }; - - - struct response - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + struct request { + 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 */ + + void serialize(ISerializer &s) { + serializeAsBinary(block_ids, "block_ids", s); + } }; - //----------------------------------------------- - struct COMMAND_RPC_START_MINING - { - struct request - { - std::string miner_address; - uint64_t threads_count; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(miner_address) - KV_SERIALIZE(threads_count) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + + struct response { + std::list blocks; + uint64_t start_height; + uint64_t current_height; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(blocks) + KV_MEMBER(start_height) + KV_MEMBER(current_height) + KV_MEMBER(status) + } }; - //----------------------------------------------- - struct COMMAND_RPC_GET_INFO - { - struct request - { - - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - uint64_t height; - uint64_t difficulty; - uint64_t tx_count; - uint64_t tx_pool_size; - uint64_t alt_blocks_count; - uint64_t outgoing_connections_count; - uint64_t incoming_connections_count; - uint64_t white_peerlist_size; - uint64_t grey_peerlist_size; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(height) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(tx_count) - KV_SERIALIZE(tx_pool_size) - KV_SERIALIZE(alt_blocks_count) - KV_SERIALIZE(outgoing_connections_count) - KV_SERIALIZE(incoming_connections_count) - KV_SERIALIZE(white_peerlist_size) - KV_SERIALIZE(grey_peerlist_size) - END_KV_SERIALIZE_MAP() - }; +}; +//----------------------------------------------- +struct COMMAND_RPC_GET_TRANSACTIONS { + struct request { + std::list txs_hashes; + + void serialize(ISerializer &s) { + KV_MEMBER(txs_hashes) + } }; - - //----------------------------------------------- - struct COMMAND_RPC_STOP_MINING - { - struct request - { + struct response { + std::list txs_as_hex; //transactions blobs as hex + std::list missed_tx; //not found transactions + std::string status; - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + void serialize(ISerializer &s) { + KV_MEMBER(txs_as_hex) + KV_MEMBER(missed_tx) + KV_MEMBER(status) + } + }; +}; +//----------------------------------------------- +struct COMMAND_RPC_GET_POOL_CHANGES { + struct request { + crypto::hash tailBlockId; + std::vector knownTxsIds; + + void serialize(ISerializer &s) { + KV_MEMBER(tailBlockId) + serializeAsBinary(knownTxsIds, "knownTxsIds", s); + } + }; + struct response { + bool isTailBlockActual; + std::vector addedTxs; // Added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions + std::string status; - struct response - { - std::string status; + void serialize(ISerializer &s) { + KV_MEMBER(isTailBlockActual) + KV_MEMBER(addedTxs) + serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); + KV_MEMBER(status) + } + }; +}; +//----------------------------------------------- +struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES { + + struct request { + crypto::hash txid; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + void serialize(ISerializer &s) { + KV_MEMBER(txid) + } }; - //----------------------------------------------- - struct COMMAND_RPC_STOP_DAEMON { - struct request { + struct response { + std::vector o_indexes; + std::string status; - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + void serialize(ISerializer &s) { + KV_MEMBER(o_indexes) + KV_MEMBER(status) + } + }; +}; +//----------------------------------------------- +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request { + std::vector amounts; + uint64_t outs_count; + + void serialize(ISerializer &s) { + KV_MEMBER(amounts) + KV_MEMBER(outs_count) + } +}; + +#pragma pack(push, 1) +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry { + uint64_t global_amount_index; + crypto::public_key out_key; +}; +#pragma pack(pop) +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount { + uint64_t amount; + std::list outs; + + void serialize(ISerializer &s) { + KV_MEMBER(amount) + serializeAsBinary(outs, "outs", s); + } +}; + +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response { + std::vector outs; + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(outs); + KV_MEMBER(status) + } +}; + +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS { + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request request; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response response; + + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry out_entry; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount outs_for_amount; +}; + +//----------------------------------------------- +struct COMMAND_RPC_SEND_RAW_TX { + struct request { + std::string tx_as_hex; + + request() {} + explicit request(const Transaction &); + + void serialize(ISerializer &s) { + KV_MEMBER(tx_as_hex) + } + }; - struct response { - std::string status; + struct response { + std::string status; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + void serialize(ISerializer &s) { + KV_MEMBER(status) + } + }; +}; +//----------------------------------------------- +struct COMMAND_RPC_START_MINING { + struct request { + std::string miner_address; + uint64_t threads_count; + + void serialize(ISerializer &s) { + KV_MEMBER(miner_address) + KV_MEMBER(threads_count) + } }; + struct response { + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(status) + } + }; +}; +//----------------------------------------------- +struct COMMAND_RPC_GET_INFO { + typedef EMPTY_STRUCT request; - // - struct COMMAND_RPC_GETBLOCKCOUNT - { - typedef std::list request; + struct response { + std::string status; + uint64_t height; + uint64_t difficulty; + uint64_t tx_count; + uint64_t tx_pool_size; + uint64_t alt_blocks_count; + uint64_t outgoing_connections_count; + uint64_t incoming_connections_count; + uint64_t white_peerlist_size; + uint64_t grey_peerlist_size; + + void serialize(ISerializer &s) { + KV_MEMBER(status) + KV_MEMBER(height) + KV_MEMBER(difficulty) + KV_MEMBER(tx_count) + KV_MEMBER(tx_pool_size) + KV_MEMBER(alt_blocks_count) + KV_MEMBER(outgoing_connections_count) + KV_MEMBER(incoming_connections_count) + KV_MEMBER(white_peerlist_size) + KV_MEMBER(grey_peerlist_size) + } + }; +}; - struct response - { - uint64_t count; - std::string status; +//----------------------------------------------- +struct COMMAND_RPC_STOP_MINING { + typedef EMPTY_STRUCT request; + typedef STATUS_STRUCT response; +}; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(count) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; +//----------------------------------------------- +struct COMMAND_RPC_STOP_DAEMON { + typedef EMPTY_STRUCT request; + typedef STATUS_STRUCT response; +}; - }; +// +struct COMMAND_RPC_GETBLOCKCOUNT { + typedef std::list request; - struct COMMAND_RPC_GETBLOCKHASH - { - typedef std::vector request; + struct response { + uint64_t count; + std::string status; - typedef std::string response; + void serialize(ISerializer &s) { + KV_MEMBER(count) + KV_MEMBER(status) + } + }; +}; + +struct COMMAND_RPC_GETBLOCKHASH { + typedef std::vector request; + typedef std::string response; +}; + +struct COMMAND_RPC_GETBLOCKTEMPLATE { + struct request { + uint64_t reserve_size; //max 255 bytes + std::string wallet_address; + + void serialize(ISerializer &s) { + KV_MEMBER(reserve_size) + KV_MEMBER(wallet_address) + } }; + struct response { + uint64_t difficulty; + uint32_t height; + uint64_t reserved_offset; + blobdata blocktemplate_blob; + std::string status; - struct COMMAND_RPC_GETBLOCKTEMPLATE - { - struct request - { - uint64_t reserve_size; //max 255 bytes - std::string wallet_address; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(reserve_size) - KV_SERIALIZE(wallet_address) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - uint64_t difficulty; - uint32_t height; - uint64_t reserved_offset; - blobdata blocktemplate_blob; - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(difficulty) - KV_SERIALIZE(height) - KV_SERIALIZE(reserved_offset) - KV_SERIALIZE(blocktemplate_blob) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + void serialize(ISerializer &s) { + KV_MEMBER(difficulty) + KV_MEMBER(height) + KV_MEMBER(reserved_offset) + KV_MEMBER(blocktemplate_blob) + KV_MEMBER(status) + } }; +}; - struct COMMAND_RPC_GET_CURRENCY_ID - { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string currency_id_blob; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(currency_id_blob) - END_KV_SERIALIZE_MAP() - }; - }; +struct COMMAND_RPC_GET_CURRENCY_ID { + typedef EMPTY_STRUCT request; - struct COMMAND_RPC_SUBMITBLOCK - { - typedef std::vector request; - - struct response - { - std::string status; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + struct response { + std::string currency_id_blob; + + void serialize(ISerializer &s) { + KV_MEMBER(currency_id_blob) + } }; - - struct block_header_responce - { - uint8_t major_version; - uint8_t minor_version; - uint64_t timestamp; - std::string prev_hash; - uint32_t nonce; - bool orphan_status; - uint64_t height; - uint64_t depth; - std::string hash; - difficulty_type difficulty; - uint64_t reward; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(major_version) - KV_SERIALIZE(minor_version) - KV_SERIALIZE(timestamp) - KV_SERIALIZE(prev_hash) - KV_SERIALIZE(nonce) - KV_SERIALIZE(orphan_status) - KV_SERIALIZE(height) - KV_SERIALIZE(depth) - KV_SERIALIZE(hash) - KV_SERIALIZE(difficulty) - KV_SERIALIZE(reward) - END_KV_SERIALIZE_MAP() +}; + +struct COMMAND_RPC_SUBMITBLOCK { + typedef std::vector request; + typedef STATUS_STRUCT response; +}; + +struct block_header_responce { + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + std::string prev_hash; + uint32_t nonce; + bool orphan_status; + uint64_t height; + uint64_t depth; + std::string hash; + difficulty_type difficulty; + uint64_t reward; + + void serialize(ISerializer &s) { + KV_MEMBER(major_version) + KV_MEMBER(minor_version) + KV_MEMBER(timestamp) + KV_MEMBER(prev_hash) + KV_MEMBER(nonce) + KV_MEMBER(orphan_status) + KV_MEMBER(height) + KV_MEMBER(depth) + KV_MEMBER(hash) + KV_MEMBER(difficulty) + KV_MEMBER(reward) + } +}; + +struct BLOCK_HEADER_RESPONSE { + std::string status; + block_header_responce block_header; + + void serialize(ISerializer &s) { + KV_MEMBER(block_header) + KV_MEMBER(status) + } +}; + + +struct COMMAND_RPC_GET_LAST_BLOCK_HEADER { + typedef EMPTY_STRUCT request; + typedef BLOCK_HEADER_RESPONSE response; +}; + +struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH { + struct request { + std::string hash; + + void serialize(ISerializer &s) { + KV_MEMBER(hash) + } }; - - struct COMMAND_RPC_GET_LAST_BLOCK_HEADER - { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - block_header_responce block_header; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(block_header) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; - }; - - struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH - { - struct request - { - std::string hash; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(hash) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - block_header_responce block_header; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(block_header) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + typedef BLOCK_HEADER_RESPONSE response; +}; + +struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT { + struct request { + uint64_t height; + void serialize(ISerializer &s) { + KV_MEMBER(height) + } }; - struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT - { - struct request - { - uint64_t height; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - block_header_responce block_header; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(block_header) - KV_SERIALIZE(status) - END_KV_SERIALIZE_MAP() - }; + typedef BLOCK_HEADER_RESPONSE response; +}; +struct COMMAND_RPC_QUERY_BLOCKS { + struct request { + 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 timestamp; + + void serialize(ISerializer &s) { + serializeAsBinary(block_ids, "block_ids", s); + KV_MEMBER(timestamp) + } }; - struct COMMAND_RPC_QUERY_BLOCKS - { - struct request - { - 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 timestamp; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) - KV_SERIALIZE(timestamp) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string status; - uint64_t start_height; - uint64_t current_height; - uint64_t full_offset; - - std::list items; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(status) - KV_SERIALIZE(start_height) - KV_SERIALIZE(current_height) - KV_SERIALIZE(full_offset) - KV_SERIALIZE(items) - END_KV_SERIALIZE_MAP() - }; + struct response { + std::string status; + uint64_t start_height; + uint64_t current_height; + uint64_t full_offset; + std::list items; + + void serialize(ISerializer &s) { + KV_MEMBER(status) + KV_MEMBER(start_height) + KV_MEMBER(current_height) + KV_MEMBER(full_offset) + KV_MEMBER(items) + } }; +}; + } diff --git a/src/serialization/BinaryInputStreamSerializer.cpp b/src/serialization/BinaryInputStreamSerializer.cpp index ed24bbf5a0..bcb80df86b 100644 --- a/src/serialization/BinaryInputStreamSerializer.cpp +++ b/src/serialization/BinaryInputStreamSerializer.cpp @@ -68,54 +68,52 @@ ISerializer::SerializerType BinaryInputStreamSerializer::type() const { return ISerializer::INPUT; } -ISerializer& BinaryInputStreamSerializer::beginObject(const std::string& name) { - return *this; +bool BinaryInputStreamSerializer::beginObject(Common::StringView name) { + return true; } -ISerializer& BinaryInputStreamSerializer::endObject() { - return *this; +void BinaryInputStreamSerializer::endObject() { } -ISerializer& BinaryInputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { +bool BinaryInputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { readVarintAs(stream, size); - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::endArray() { - return *this; +void BinaryInputStreamSerializer::endArray() { } -ISerializer& BinaryInputStreamSerializer::operator()(uint8_t& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(uint8_t& value, Common::StringView name) { readVarint(stream, value); - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::operator()(uint32_t& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { readVarint(stream, value); - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::operator()(int32_t& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(int32_t& value, Common::StringView name) { readVarintAs(stream, value); - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::operator()(int64_t& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(int64_t& value, Common::StringView name) { readVarintAs(stream, value); - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::operator()(uint64_t& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(uint64_t& value, Common::StringView name) { readVarint(stream, value); - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::operator()(bool& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(bool& value, Common::StringView name) { value = stream.get() != 0; - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::operator()(std::string& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(std::string& value, Common::StringView name) { uint64_t size; readVarint(stream, size); @@ -129,39 +127,28 @@ ISerializer& BinaryInputStreamSerializer::operator()(std::string& value, const s value.clear(); } - return *this; + return true; } -ISerializer& BinaryInputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { - stream.read(static_cast(value), size); - return *this; +bool BinaryInputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { + checkedRead(static_cast(value), size); + return true; } -ISerializer& BinaryInputStreamSerializer::binary(std::string& value, const std::string& name) { +bool BinaryInputStreamSerializer::binary(std::string& value, Common::StringView name) { return (*this)(value, name); } - -bool BinaryInputStreamSerializer::hasObject(const std::string& name) { - assert(false); //the method is not supported for this type of serialization - throw std::runtime_error("hasObject method is not supported in BinaryInputStreamSerializer"); - - return false; -} - -ISerializer& BinaryInputStreamSerializer::operator()(double& value, const std::string& name) { +bool BinaryInputStreamSerializer::operator()(double& value, Common::StringView name) { assert(false); //the method is not supported for this type of serialization throw std::runtime_error("double serialization is not supported in BinaryInputStreamSerializer"); - - return *this; + return false; } void BinaryInputStreamSerializer::checkedRead(char* buf, size_t size) { - stream.read(buf, size); - if (!stream) { + if (stream.read(buf, size).gcount() != size) { throw std::runtime_error("Stream read error"); } } - } diff --git a/src/serialization/BinaryInputStreamSerializer.h b/src/serialization/BinaryInputStreamSerializer.h index dc956b7334..002b8024c4 100644 --- a/src/serialization/BinaryInputStreamSerializer.h +++ b/src/serialization/BinaryInputStreamSerializer.h @@ -31,36 +31,31 @@ class BinaryInputStreamSerializer : public ISerializer { virtual ISerializer::SerializerType type() const; - virtual ISerializer& beginObject(const std::string& name) override; - virtual ISerializer& endObject() override; - - virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; - virtual ISerializer& endArray() override; - - virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; - virtual ISerializer& operator()(int32_t& value, const std::string& name) override; - - virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; - virtual ISerializer& operator()(int64_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; - virtual ISerializer& operator()(double& value, const std::string& name) override; - virtual ISerializer& operator()(bool& value, const std::string& name) override; - virtual ISerializer& operator()(std::string& value, const std::string& name) override; - - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; - virtual ISerializer& binary(std::string& value, const std::string& name) override; - - virtual bool hasObject(const std::string& name) override; + virtual bool beginObject(Common::StringView name) override; + virtual void endObject() override; + + virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual void endArray() override; + + virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int32_t& value, Common::StringView name) override; + virtual bool operator()(uint32_t& value, Common::StringView name) override; + virtual bool operator()(int64_t& value, Common::StringView name) override; + virtual bool operator()(uint64_t& value, Common::StringView name) override; + virtual bool operator()(double& value, Common::StringView name) override; + virtual bool operator()(bool& value, Common::StringView name) override; + virtual bool operator()(std::string& value, Common::StringView name) override; + virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(std::string& value, Common::StringView name) override; template - ISerializer& operator()(T& value, const std::string& name) { + bool operator()(T& value, Common::StringView name) { return ISerializer::operator()(value, name); } private: void checkedRead(char* buf, size_t size); - std::istream& stream; }; diff --git a/src/serialization/BinaryOutputStreamSerializer.cpp b/src/serialization/BinaryOutputStreamSerializer.cpp index 7b8b55ff5d..9e9c3e6a33 100644 --- a/src/serialization/BinaryOutputStreamSerializer.cpp +++ b/src/serialization/BinaryOutputStreamSerializer.cpp @@ -44,82 +44,72 @@ ISerializer::SerializerType BinaryOutputStreamSerializer::type() const { return ISerializer::OUTPUT; } -ISerializer& BinaryOutputStreamSerializer::beginObject(const std::string& name) { - return *this; +bool BinaryOutputStreamSerializer::beginObject(Common::StringView name) { + return true; } -ISerializer& BinaryOutputStreamSerializer::endObject() { - return *this; +void BinaryOutputStreamSerializer::endObject() { } -ISerializer& BinaryOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { +bool BinaryOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { writeVarint(stream, size); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::endArray() { - return *this; +void BinaryOutputStreamSerializer::endArray() { } -ISerializer& BinaryOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(uint8_t& value, Common::StringView name) { writeVarint(stream, value); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { writeVarint(stream, value); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(int32_t& value, Common::StringView name) { writeVarint(stream, static_cast(value)); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(int64_t& value, Common::StringView name) { writeVarint(stream, static_cast(value)); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(uint64_t& value, Common::StringView name) { writeVarint(stream, value); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::operator()(bool& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(bool& value, Common::StringView name) { char boolVal = value; checkedWrite(&boolVal, 1); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::operator()(std::string& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(std::string& value, Common::StringView name) { writeVarint(stream, value.size()); checkedWrite(value.data(), value.size()); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { +bool BinaryOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { checkedWrite(static_cast(value), size); - return *this; + return true; } -ISerializer& BinaryOutputStreamSerializer::binary(std::string& value, const std::string& name) { +bool BinaryOutputStreamSerializer::binary(std::string& value, Common::StringView name) { // write as string (with size prefix) return (*this)(value, name); } -bool BinaryOutputStreamSerializer::hasObject(const std::string& name) { - assert(false); //the method is not supported for this type of serialization - throw std::runtime_error("hasObject method is not supported in BinaryOutputStreamSerializer"); - - return false; -} - -ISerializer& BinaryOutputStreamSerializer::operator()(double& value, const std::string& name) { +bool BinaryOutputStreamSerializer::operator()(double& value, Common::StringView name) { assert(false); //the method is not supported for this type of serialization throw std::runtime_error("double serialization is not supported in BinaryOutputStreamSerializer"); - - return *this; + return false; } void BinaryOutputStreamSerializer::checkedWrite(const char* buf, size_t size) { diff --git a/src/serialization/BinaryOutputStreamSerializer.h b/src/serialization/BinaryOutputStreamSerializer.h index 1de71c0420..e1dc350036 100644 --- a/src/serialization/BinaryOutputStreamSerializer.h +++ b/src/serialization/BinaryOutputStreamSerializer.h @@ -31,28 +31,25 @@ class BinaryOutputStreamSerializer : public ISerializer { virtual ISerializer::SerializerType type() const; - virtual ISerializer& beginObject(const std::string& name) override; - virtual ISerializer& endObject() override; - - virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; - virtual ISerializer& endArray() override; - - virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; - virtual ISerializer& operator()(int32_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; - virtual ISerializer& operator()(int64_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; - virtual ISerializer& operator()(double& value, const std::string& name) override; - virtual ISerializer& operator()(bool& value, const std::string& name) override; - virtual ISerializer& operator()(std::string& value, const std::string& name) override; - - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; - virtual ISerializer& binary(std::string& value, const std::string& name) override; - - virtual bool hasObject(const std::string& name) override; + virtual bool beginObject(Common::StringView name) override; + virtual void endObject() override; + + virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual void endArray() override; + + virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int32_t& value, Common::StringView name) override; + virtual bool operator()(uint32_t& value, Common::StringView name) override; + virtual bool operator()(int64_t& value, Common::StringView name) override; + virtual bool operator()(uint64_t& value, Common::StringView name) override; + virtual bool operator()(double& value, Common::StringView name) override; + virtual bool operator()(bool& value, Common::StringView name) override; + virtual bool operator()(std::string& value, Common::StringView name) override; + virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(std::string& value, Common::StringView name) override; template - ISerializer& operator()(T& value, const std::string& name) { + bool operator()(T& value, Common::StringView name) { return ISerializer::operator()(value, name); } diff --git a/src/serialization/ISerializer.h b/src/serialization/ISerializer.h index d610d4fc31..f7c5c5f08b 100644 --- a/src/serialization/ISerializer.h +++ b/src/serialization/ISerializer.h @@ -20,6 +20,8 @@ #include #include +#include + namespace CryptoNote { class ISerializer { @@ -34,40 +36,49 @@ class ISerializer { virtual SerializerType type() const = 0; - virtual ISerializer& beginObject(const std::string& name) = 0; - virtual ISerializer& endObject() = 0; - virtual ISerializer& beginArray(std::size_t& size, const std::string& name) = 0; - virtual ISerializer& endArray() = 0; - - virtual ISerializer& operator()(uint8_t& value, const std::string& name) = 0; - virtual ISerializer& operator()(int32_t& value, const std::string& name) = 0; - virtual ISerializer& operator()(uint32_t& value, const std::string& name) = 0; - virtual ISerializer& operator()(int64_t& value, const std::string& name) = 0; - virtual ISerializer& operator()(uint64_t& value, const std::string& name) = 0; - virtual ISerializer& operator()(double& value, const std::string& name) = 0; - virtual ISerializer& operator()(bool& value, const std::string& name) = 0; - virtual ISerializer& operator()(std::string& value, const std::string& name) = 0; + virtual bool beginObject(Common::StringView name) = 0; + virtual void endObject() = 0; + virtual bool beginArray(std::size_t& size, Common::StringView name) = 0; + virtual void endArray() = 0; + + virtual bool operator()(uint8_t& value, Common::StringView name) = 0; + virtual bool operator()(int32_t& value, Common::StringView name) = 0; + virtual bool operator()(uint32_t& value, Common::StringView name) = 0; + virtual bool operator()(int64_t& value, Common::StringView name) = 0; + virtual bool operator()(uint64_t& value, Common::StringView name) = 0; + virtual bool operator()(double& value, Common::StringView name) = 0; + virtual bool operator()(bool& value, Common::StringView name) = 0; + virtual bool operator()(std::string& value, Common::StringView name) = 0; // read/write binary block - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) = 0; - virtual ISerializer& binary(std::string& value, const std::string& name) = 0; - - virtual bool hasObject(const std::string& name) = 0; + virtual bool binary(void* value, std::size_t size, Common::StringView name) = 0; + virtual bool binary(std::string& value, Common::StringView name) = 0; template - ISerializer& operator()(T& value, const std::string& name); + bool operator()(T& value, Common::StringView name); }; template -ISerializer& ISerializer::operator()(T& value, const std::string& name) { - serialize(value, name, *this); - return *this; +bool ISerializer::operator()(T& value, Common::StringView name) { + return serialize(value, name, *this); +} + +template +bool serialize(T& value, Common::StringView name, ISerializer& serializer) { + if (!serializer.beginObject(name)) { + return false; + } + + serialize(value, serializer); + serializer.endObject(); + return true; } template -void serialize(T& value, const std::string& name, ISerializer& serializer) { - value.serialize(serializer, name); - return; +void serialize(T& value, ISerializer& serializer) { + value.serialize(serializer); } +#define KV_MEMBER(member) s(member, #member); + } diff --git a/src/serialization/JsonInputStreamSerializer.cpp b/src/serialization/JsonInputStreamSerializer.cpp index 37aa12f69d..8cb6efe197 100644 --- a/src/serialization/JsonInputStreamSerializer.cpp +++ b/src/serialization/JsonInputStreamSerializer.cpp @@ -22,9 +22,8 @@ namespace CryptoNote { -JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) { +JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) : JsonInputValueSerializer(root) { stream >> root; - JsonInputValueSerializer::setJsonValue(&root); } JsonInputStreamSerializer::~JsonInputStreamSerializer() { diff --git a/src/serialization/JsonInputValueSerializer.cpp b/src/serialization/JsonInputValueSerializer.cpp index 60c37307d5..35b282dcd6 100644 --- a/src/serialization/JsonInputValueSerializer.cpp +++ b/src/serialization/JsonInputValueSerializer.cpp @@ -23,151 +23,142 @@ using Common::JsonValue; using namespace CryptoNote; -JsonInputValueSerializer::JsonInputValueSerializer() : root(nullptr) { +JsonInputValueSerializer::JsonInputValueSerializer(const Common::JsonValue& value) : root(value) { + chain.push_back(&root); } JsonInputValueSerializer::~JsonInputValueSerializer() { } -void JsonInputValueSerializer::setJsonValue(const JsonValue* value) { - root = value; -} - ISerializer::SerializerType JsonInputValueSerializer::type() const { return ISerializer::INPUT; } -ISerializer& JsonInputValueSerializer::beginObject(const std::string& name) { - assert(root); - - if (chain.size() == 0) { - chain.push_back(root); - return *this; - } - +bool JsonInputValueSerializer::beginObject(Common::StringView name) { const JsonValue* parent = chain.back(); + if (parent->isArray()) { const JsonValue& v = (*parent)[idxs.back()++]; chain.push_back(&v); - } else { - const JsonValue& v = (*parent)(name); + return true; + } + + if (parent->contains(std::string(name))) { + const JsonValue& v = (*parent)(std::string(name)); chain.push_back(&v); + return true; } - return *this; -} + return false; -ISerializer& JsonInputValueSerializer::endObject() { - assert(root); + //if (parent->isArray()) { + // const JsonValue& v = (*parent)[idxs.back()++]; + // chain.push_back(&v); + //} else { + // const JsonValue& v = (*parent)(name); + // chain.push_back(&v); + //} +} +void JsonInputValueSerializer::endObject() { + assert(!chain.empty()); chain.pop_back(); - return *this; } -ISerializer& JsonInputValueSerializer::beginArray(std::size_t& size, const std::string& name) { - assert(root); - +bool JsonInputValueSerializer::beginArray(std::size_t& size, Common::StringView name) { const JsonValue* parent = chain.back(); + std::string strName(name); - if (parent->count(name)) { - const JsonValue& arr = (*parent)(name); + if (parent->contains(strName)) { + const JsonValue& arr = (*parent)(strName); size = arr.size(); chain.push_back(&arr); - } else { - size = 0; - chain.push_back(0); + idxs.push_back(0); + return true; } - - idxs.push_back(0); - return *this; + + size = 0; + return false; } -ISerializer& JsonInputValueSerializer::endArray() { - assert(root); +void JsonInputValueSerializer::endArray() { + assert(!chain.empty()); + assert(!idxs.empty()); chain.pop_back(); idxs.pop_back(); - return *this; } -ISerializer& JsonInputValueSerializer::operator()(uint32_t& value, const std::string& name) { - assert(root); - value = static_cast(getNumber(name)); - return *this; +bool JsonInputValueSerializer::operator()(uint32_t& value, Common::StringView name) { + return getNumber(name, value); } -ISerializer& JsonInputValueSerializer::operator()(int32_t& value, const std::string& name) { - assert(root); - value = static_cast(getNumber(name)); - return *this; +bool JsonInputValueSerializer::operator()(int32_t& value, Common::StringView name) { + return getNumber(name, value); } -ISerializer& JsonInputValueSerializer::operator()(int64_t& value, const std::string& name) { - assert(root); - value = getNumber(name); - return *this; +bool JsonInputValueSerializer::operator()(int64_t& value, Common::StringView name) { + return getNumber(name, value); } -ISerializer& JsonInputValueSerializer::operator()(uint64_t& value, const std::string& name) { - assert(root); - value = static_cast(getNumber(name)); - return *this; +bool JsonInputValueSerializer::operator()(uint64_t& value, Common::StringView name) { + return getNumber(name, value); } -ISerializer& JsonInputValueSerializer::operator()(double& value, const std::string& name) { - assert(root); - value = getValue(name).getReal(); - return *this; +bool JsonInputValueSerializer::operator()(double& value, Common::StringView name) { + return getNumber(name, value); } -ISerializer& JsonInputValueSerializer::operator()(std::string& value, const std::string& name) { - assert(root); - value = getValue(name).getString(); - return *this; +bool JsonInputValueSerializer::operator()(uint8_t& value, Common::StringView name) { + return getNumber(name, value); } -ISerializer& JsonInputValueSerializer::operator()(uint8_t& value, const std::string& name) { - assert(root); - value = static_cast(getNumber(name)); - return *this; +bool JsonInputValueSerializer::operator()(std::string& value, Common::StringView name) { + auto ptr = getValue(name); + if (!ptr) { + return false; + } + value = ptr->getString(); + return true; } -ISerializer& JsonInputValueSerializer::operator()(bool& value, const std::string& name) { - assert(root); - value = getValue(name).getBool(); - return *this; +bool JsonInputValueSerializer::operator()(bool& value, Common::StringView name) { + auto ptr = getValue(name); + if (!ptr) { + return false; + } + value = ptr->getBool(); + return true; } -bool JsonInputValueSerializer::hasObject(const std::string& name) { - assert(root); - - const Common::JsonValue* value; - if (chain.empty()) { - value = root; - } else { - value = chain.back(); +bool JsonInputValueSerializer::binary(void* value, std::size_t size, Common::StringView name) { + auto ptr = getValue(name); + if (ptr == nullptr) { + return false; } - return value->count(name) != 0; + Common::fromHex(ptr->getString(), value, size); + return true; } -ISerializer& JsonInputValueSerializer::binary(void* value, std::size_t size, const std::string& name) { - auto str = getValue(name).getString(); - Common::fromHex(str, value, size); - return *this; -} +bool JsonInputValueSerializer::binary(std::string& value, Common::StringView name) { + auto ptr = getValue(name); + if (ptr == nullptr) { + return false; + } + + std::string valueHex = ptr->getString(); + value = Common::asString(Common::fromHex(valueHex)); -ISerializer& JsonInputValueSerializer::binary(std::string& value, const std::string& name) { - auto str = getValue(name).getString(); - value = Common::asString(Common::fromHex(str)); - return *this; + return true; } -JsonValue JsonInputValueSerializer::getValue(const std::string& name) { +const JsonValue* JsonInputValueSerializer::getValue(Common::StringView name) { const JsonValue& val = *chain.back(); - return val.isArray() ? val[idxs.back()++] : val(name); -} + if (val.isArray()) { + return &val[idxs.back()++]; + } -int64_t JsonInputValueSerializer::getNumber(const std::string& name) { - return getValue(name).getInteger(); + std::string strName(name); + return val.contains(strName) ? &val(strName) : nullptr; } diff --git a/src/serialization/JsonInputValueSerializer.h b/src/serialization/JsonInputValueSerializer.h index 29e535232c..e211b700ba 100644 --- a/src/serialization/JsonInputValueSerializer.h +++ b/src/serialization/JsonInputValueSerializer.h @@ -25,44 +25,51 @@ namespace CryptoNote { //deserialization class JsonInputValueSerializer : public ISerializer { public: - JsonInputValueSerializer(); + JsonInputValueSerializer(const Common::JsonValue& value); virtual ~JsonInputValueSerializer(); - void setJsonValue(const Common::JsonValue* value); SerializerType type() const; - virtual ISerializer& beginObject(const std::string& name) override; - virtual ISerializer& endObject() override; + virtual bool beginObject(Common::StringView name) override; + virtual void endObject() override; - virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; - virtual ISerializer& endArray() override; + virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual void endArray() override; - virtual ISerializer& operator()(int32_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; - virtual ISerializer& operator()(int64_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; - virtual ISerializer& operator()(double& value, const std::string& name) override; - virtual ISerializer& operator()(std::string& value, const std::string& name) override; - virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; - virtual ISerializer& operator()(bool& value, const std::string& name) override; - - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; - virtual ISerializer& binary(std::string& value, const std::string& name) override; - - virtual bool hasObject(const std::string& name) override; + virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int32_t& value, Common::StringView name) override; + virtual bool operator()(uint32_t& value, Common::StringView name) override; + virtual bool operator()(int64_t& value, Common::StringView name) override; + virtual bool operator()(uint64_t& value, Common::StringView name) override; + virtual bool operator()(double& value, Common::StringView name) override; + virtual bool operator()(bool& value, Common::StringView name) override; + virtual bool operator()(std::string& value, Common::StringView name) override; + virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(std::string& value, Common::StringView name) override; template - ISerializer& operator()(T& value, const std::string& name) { + bool operator()(T& value, Common::StringView name) { return ISerializer::operator()(value, name); } private: - const Common::JsonValue* root; + const Common::JsonValue& root; std::vector chain; std::vector idxs; - Common::JsonValue getValue(const std::string& name); - int64_t getNumber(const std::string& name); + const Common::JsonValue* getValue(Common::StringView name); + + template + bool getNumber(Common::StringView name, T& v) { + auto ptr = getValue(name); + + if (!ptr) { + return false; + } + + v = static_cast(ptr->getInteger()); + return true; + } }; } diff --git a/src/serialization/JsonOutputStreamSerializer.cpp b/src/serialization/JsonOutputStreamSerializer.cpp index f3f2703677..3224d4793a 100644 --- a/src/serialization/JsonOutputStreamSerializer.cpp +++ b/src/serialization/JsonOutputStreamSerializer.cpp @@ -30,138 +30,105 @@ std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& en } } -JsonOutputStreamSerializer::JsonOutputStreamSerializer() : root(JsonValue::OBJECT) { +namespace { + +template +void insertOrPush(JsonValue& js, Common::StringView name, const T& value) { + if (js.isArray()) { + js.pushBack(JsonValue(value)); + } else { + js.insert(std::string(name), JsonValue(value)); + } } -JsonOutputStreamSerializer::~JsonOutputStreamSerializer() { } -JsonValue JsonOutputStreamSerializer::getJsonValue() const { - return root; +JsonOutputStreamSerializer::JsonOutputStreamSerializer() : root(JsonValue::OBJECT) { + chain.push_back(&root); +} + +JsonOutputStreamSerializer::~JsonOutputStreamSerializer() { } ISerializer::SerializerType JsonOutputStreamSerializer::type() const { return ISerializer::OUTPUT; } -ISerializer& JsonOutputStreamSerializer::beginObject(const std::string& name) { - if (chain.size() == 0) { - chain.push_back(&root); - return *this; - } - - JsonValue* parent = chain.back(); +bool JsonOutputStreamSerializer::beginObject(Common::StringView name) { + JsonValue& parent = *chain.back(); JsonValue obj(JsonValue::OBJECT); - if (parent->isObject()) { - JsonValue& res = parent->insert(name, obj); - chain.push_back(&res); + if (parent.isObject()) { + chain.push_back(&parent.insert(std::string(name), obj)); } else { - JsonValue& res = parent->pushBack(obj); - chain.push_back(&res); + chain.push_back(&parent.pushBack(obj)); } - return *this; + return true; } -ISerializer& JsonOutputStreamSerializer::endObject() { +void JsonOutputStreamSerializer::endObject() { + assert(!chain.empty()); chain.pop_back(); - return *this; } -ISerializer& JsonOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { +bool JsonOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { JsonValue val(JsonValue::ARRAY); - JsonValue& res = chain.back()->insert(name, val); + JsonValue& res = chain.back()->insert(std::string(name), val); chain.push_back(&res); - return *this; + return true; } -ISerializer& JsonOutputStreamSerializer::endArray() { +void JsonOutputStreamSerializer::endArray() { + assert(!chain.empty()); chain.pop_back(); - return *this; } -ISerializer& JsonOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { +bool JsonOutputStreamSerializer::operator()(uint64_t& value, Common::StringView name) { int64_t v = static_cast(value); return operator()(v, name); } -ISerializer& JsonOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { +bool JsonOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { uint64_t v = static_cast(value); return operator()(v, name); } -ISerializer& JsonOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { +bool JsonOutputStreamSerializer::operator()(int32_t& value, Common::StringView name) { int64_t v = static_cast(value); return operator()(v, name); } -ISerializer& JsonOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { - JsonValue* val = chain.back(); - JsonValue v; - v = static_cast(value); - if (val->isArray()) { - val->pushBack(v); - } else { - val->insert(name, v); - } - return *this; +bool JsonOutputStreamSerializer::operator()(int64_t& value, Common::StringView name) { + insertOrPush(*chain.back(), name, value); + return true; } -ISerializer& JsonOutputStreamSerializer::operator()(double& value, const std::string& name) { - JsonValue* val = chain.back(); - JsonValue v; - v = static_cast(value); - - if (val->isArray()) { - val->pushBack(v); - } else { - val->insert(name, v); - } - return *this; +bool JsonOutputStreamSerializer::operator()(double& value, Common::StringView name) { + insertOrPush(*chain.back(), name, value); + return true; } -ISerializer& JsonOutputStreamSerializer::operator()(std::string& value, const std::string& name) { - JsonValue* val = chain.back(); - JsonValue v; - v = value; - - if (val->isArray()) { - val->pushBack(v); - } else { - val->insert(name, v); - } - return *this; +bool JsonOutputStreamSerializer::operator()(std::string& value, Common::StringView name) { + insertOrPush(*chain.back(), name, value); + return true; } -ISerializer& JsonOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { - uint64_t v = static_cast(value); - return operator()(v, name); +bool JsonOutputStreamSerializer::operator()(uint8_t& value, Common::StringView name) { + insertOrPush(*chain.back(), name, static_cast(value)); + return true; } -ISerializer& JsonOutputStreamSerializer::operator()(bool& value, const std::string& name) { - JsonValue* val = chain.back(); - if (val->isArray()) { - val->pushBack(JsonValue(value)); - } else { - val->insert(name, JsonValue(value)); - } - - return *this; +bool JsonOutputStreamSerializer::operator()(bool& value, Common::StringView name) { + insertOrPush(*chain.back(), name, value); + return true; } -ISerializer& JsonOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { - auto hex = Common::toHex(value, size); +bool JsonOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { + std::string hex = Common::toHex(value, size); return (*this)(hex, name); } -ISerializer& JsonOutputStreamSerializer::binary(std::string& value, const std::string& name) { +bool JsonOutputStreamSerializer::binary(std::string& value, Common::StringView name) { return binary(const_cast(value.data()), value.size(), name); } - -bool JsonOutputStreamSerializer::hasObject(const std::string& name) { - assert(false); - throw std::runtime_error("JsonOutputStreamSerializer doesn't support this type of serialization"); - - return false; -} diff --git a/src/serialization/JsonOutputStreamSerializer.h b/src/serialization/JsonOutputStreamSerializer.h index 665350b76f..bea003d9cb 100644 --- a/src/serialization/JsonOutputStreamSerializer.h +++ b/src/serialization/JsonOutputStreamSerializer.h @@ -28,34 +28,34 @@ class JsonOutputStreamSerializer : public ISerializer { JsonOutputStreamSerializer(); virtual ~JsonOutputStreamSerializer(); - Common::JsonValue getJsonValue() const; SerializerType type() const; - virtual ISerializer& beginObject(const std::string& name) override; - virtual ISerializer& endObject() override; + virtual bool beginObject(Common::StringView name) override; + virtual void endObject() override; - virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; - virtual ISerializer& endArray() override; + virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual void endArray() override; - virtual ISerializer& operator()(int32_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; - virtual ISerializer& operator()(int64_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; - virtual ISerializer& operator()(double& value, const std::string& name) override; - virtual ISerializer& operator()(std::string& value, const std::string& name) override; - virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; - virtual ISerializer& operator()(bool& value, const std::string& name) override; - - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; - virtual ISerializer& binary(std::string& value, const std::string& name) override; - - virtual bool hasObject(const std::string& name) override; + virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int32_t& value, Common::StringView name) override; + virtual bool operator()(uint32_t& value, Common::StringView name) override; + virtual bool operator()(int64_t& value, Common::StringView name) override; + virtual bool operator()(uint64_t& value, Common::StringView name) override; + virtual bool operator()(double& value, Common::StringView name) override; + virtual bool operator()(bool& value, Common::StringView name) override; + virtual bool operator()(std::string& value, Common::StringView name) override; + virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(std::string& value, Common::StringView name) override; template - ISerializer& operator()(T& value, const std::string& name) { + bool operator()(T& value, Common::StringView name) { return ISerializer::operator()(value, name); } + const Common::JsonValue& getValue() const { + return root; + } + friend std::ostream& operator<<(std::ostream& out, const JsonOutputStreamSerializer& enumerator); private: diff --git a/src/serialization/KVBinaryInputStreamSerializer.cpp b/src/serialization/KVBinaryInputStreamSerializer.cpp index 58ca3f79d6..f6e27208d4 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.cpp +++ b/src/serialization/KVBinaryInputStreamSerializer.cpp @@ -27,19 +27,23 @@ using namespace CryptoNote; namespace { +void strictRead(std::istream& s, void* ptr, size_t size) { + if (s.read(reinterpret_cast(ptr), size).gcount() != size) { + throw std::runtime_error("read error"); + } +} + template T readPod(std::istream& s) { T v; - s.read(reinterpret_cast(&v), sizeof(T)); + strictRead(s, &v, sizeof(T)); return v; } template JsonValue readPodJson(std::istream& s) { - T v; - s.read(reinterpret_cast(&v), sizeof(T)); JsonValue jv; - jv = static_cast(v); + jv = static_cast(readPod(s)); return jv; } @@ -77,77 +81,44 @@ std::string readString(std::istream& s) { auto size = readVarint(s); std::string str; str.resize(size); - s.read(&str[0], size); + if (size) { + strictRead(s, &str[0], size); + } return str; } JsonValue readStringJson(std::istream& s) { - JsonValue js; - js = readString(s); - return js; + return JsonValue(readString(s)); } void readName(std::istream& s, std::string& name) { uint8_t len = readPod(s); - name.resize(len); - s.read(&name[0], len); -} - -} - -void KVBinaryInputStreamSerializer::parse() { - auto hdr = readPod(stream); - - if ( - hdr.m_signature_a != PORTABLE_STORAGE_SIGNATUREA || - hdr.m_signature_b != PORTABLE_STORAGE_SIGNATUREB) { - throw std::runtime_error("Invalid binary storage signature"); - } - - if (hdr.m_ver != PORTABLE_STORAGE_FORMAT_VER) { - throw std::runtime_error("Unknown binary storage format version"); + if (len) { + name.resize(len); + strictRead(s, &name[0], len); } - - root.reset(new JsonValue(loadSection())); - setJsonValue(root.get()); } -ISerializer& KVBinaryInputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { - std::string str; +JsonValue loadValue(std::istream& stream, uint8_t type); +JsonValue loadSection(std::istream& stream); +JsonValue loadEntry(std::istream& stream); +JsonValue loadArray(std::istream& stream, uint8_t itemType); - (*this)(str, name); - if (str.size() != size) { - throw std::runtime_error("Binary block size mismatch"); - } - - memcpy(value, str.data(), size); - return *this; -} - -ISerializer& KVBinaryInputStreamSerializer::binary(std::string& value, const std::string& name) { - if (!hasObject(name)) { - value.clear(); - return *this; - } - - return (*this)(value, name); // load as string -} - -JsonValue KVBinaryInputStreamSerializer::loadSection() { +JsonValue loadSection(std::istream& stream) { JsonValue sec(JsonValue::OBJECT); size_t count = readVarint(stream); std::string name; while (count--) { readName(stream, name); - sec.insert(name, loadEntry()); + sec.insert(name, loadEntry(stream)); } return sec; } -JsonValue KVBinaryInputStreamSerializer::loadValue(uint8_t type) { +JsonValue loadValue(std::istream& stream, uint8_t type) { switch (type) { case BIN_KV_SERIALIZE_TYPE_INT64: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_INT32: return readIntegerJson(stream); @@ -160,32 +131,74 @@ JsonValue KVBinaryInputStreamSerializer::loadValue(uint8_t type) { case BIN_KV_SERIALIZE_TYPE_DOUBLE: return readPodJson(stream); case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(stream.get() != 0); case BIN_KV_SERIALIZE_TYPE_STRING: return readStringJson(stream); - case BIN_KV_SERIALIZE_TYPE_OBJECT: return loadSection(); - case BIN_KV_SERIALIZE_TYPE_ARRAY: return loadArray(type); + case BIN_KV_SERIALIZE_TYPE_OBJECT: return loadSection(stream); + case BIN_KV_SERIALIZE_TYPE_ARRAY: return loadArray(stream, type); default: throw std::runtime_error("Unknown data type"); break; } } -JsonValue KVBinaryInputStreamSerializer::loadEntry() { +JsonValue loadEntry(std::istream& stream) { uint8_t type = readPod(stream); if (type & BIN_KV_SERIALIZE_FLAG_ARRAY) { type &= ~BIN_KV_SERIALIZE_FLAG_ARRAY; - return loadArray(type); + return loadArray(stream, type); } - return loadValue(type); + return loadValue(stream, type); } -JsonValue KVBinaryInputStreamSerializer::loadArray(uint8_t itemType) { +JsonValue loadArray(std::istream& stream, uint8_t itemType) { JsonValue arr(JsonValue::ARRAY); size_t count = readVarint(stream); while (count--) { - arr.pushBack(loadValue(itemType)); + arr.pushBack(loadValue(stream, itemType)); } return arr; } + + +JsonValue parseBinary(std::istream& stream) { + auto hdr = readPod(stream); + + if ( + hdr.m_signature_a != PORTABLE_STORAGE_SIGNATUREA || + hdr.m_signature_b != PORTABLE_STORAGE_SIGNATUREB) { + throw std::runtime_error("Invalid binary storage signature"); + } + + if (hdr.m_ver != PORTABLE_STORAGE_FORMAT_VER) { + throw std::runtime_error("Unknown binary storage format version"); + } + + return loadSection(stream); +} + +} + +KVBinaryInputStreamSerializer::KVBinaryInputStreamSerializer(std::istream& strm) : value(parseBinary(strm)), JsonInputValueSerializer(value) { +} + +bool KVBinaryInputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { + std::string str; + + if (!(*this)(str, name)) { + return false; + } + + if (str.size() != size) { + throw std::runtime_error("Binary block size mismatch"); + } + + memcpy(value, str.data(), size); + return true; +} + +bool KVBinaryInputStreamSerializer::binary(std::string& value, Common::StringView name) { + return (*this)(value, name); // load as string +} + diff --git a/src/serialization/KVBinaryInputStreamSerializer.h b/src/serialization/KVBinaryInputStreamSerializer.h index ee922b4174..6fe2962819 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.h +++ b/src/serialization/KVBinaryInputStreamSerializer.h @@ -18,32 +18,21 @@ #pragma once #include -#include -#include "../Common/JsonValue.h" #include "ISerializer.h" #include "JsonInputValueSerializer.h" -#include "SerializationOverloads.h" namespace CryptoNote { class KVBinaryInputStreamSerializer : public JsonInputValueSerializer { public: - KVBinaryInputStreamSerializer(std::istream& strm) : stream(strm) {} - virtual ~KVBinaryInputStreamSerializer() {} + KVBinaryInputStreamSerializer(std::istream& strm); - void parse(); - - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; - virtual ISerializer& binary(std::string& value, const std::string& name) override; + virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(std::string& value, Common::StringView name) override; private: - std::unique_ptr root; - std::istream& stream; - Common::JsonValue loadSection(); - Common::JsonValue loadEntry(); - Common::JsonValue loadValue(uint8_t type); - Common::JsonValue loadArray(uint8_t itemType); + Common::JsonValue value; }; } diff --git a/src/serialization/KVBinaryOutputStreamSerializer.cpp b/src/serialization/KVBinaryOutputStreamSerializer.cpp index 479ddfd1f0..37c77f69b9 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.cpp +++ b/src/serialization/KVBinaryOutputStreamSerializer.cpp @@ -25,6 +25,18 @@ using namespace CryptoNote; namespace { +class StdOutputStream : public IOutputStream { +public: + StdOutputStream(std::ostream& s) : stream(s) {} + + virtual void write(const char* data, std::size_t size) override { + stream.write(data, size); + } + +private: + std::ostream& stream; +}; + template void writePod(IOutputStream& s, const T& value) { s.write((const char*)&value, sizeof(T)); @@ -38,13 +50,14 @@ size_t packVarint(IOutputStream& s, uint8_t type_or, size_t pv) { return sizeof(T); } -void writeElementName(IOutputStream& s, const std::string& name) { - if (name.size() > std::numeric_limits::max()) { +void writeElementName(IOutputStream& s, Common::StringView name) { + if (name.getSize() > std::numeric_limits::max()) { throw std::runtime_error("Element name is too long"); } - uint8_t len = static_cast(name.size()); + + uint8_t len = static_cast(name.getSize()); s.write((const char*)&len, sizeof(len)); - s.write(name.data(), len); + s.write(name.getData(), len); } size_t writeArraySize(IOutputStream& s, size_t val) { @@ -86,6 +99,8 @@ void KVBinaryOutputStreamSerializer::write(std::ostream& target) { hdr.m_ver = PORTABLE_STORAGE_FORMAT_VER; target.write(reinterpret_cast(&hdr), sizeof(hdr)); + StdOutputStream stdstream(target); + writeArraySize(stdstream, m_stack.front().count); target.write(stream().data(), stream().size()); } @@ -93,16 +108,16 @@ ISerializer::SerializerType KVBinaryOutputStreamSerializer::type() const { return ISerializer::OUTPUT; } -ISerializer& KVBinaryOutputStreamSerializer::beginObject(const std::string& name) { +bool KVBinaryOutputStreamSerializer::beginObject(Common::StringView name) { checkArrayPreamble(BIN_KV_SERIALIZE_TYPE_OBJECT); m_stack.push_back(Level(name)); m_objectsStack.push_back(MemoryStream()); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::endObject() { +void KVBinaryOutputStreamSerializer::endObject() { assert(m_objectsStack.size()); auto level = std::move(m_stack.back()); @@ -117,106 +132,95 @@ ISerializer& KVBinaryOutputStreamSerializer::endObject() { writeArraySize(out, level.count); out.write(objStream.data(), objStream.size()); - - return *this; } -ISerializer& KVBinaryOutputStreamSerializer::beginArray(std::size_t& size, const std::string& name) { +bool KVBinaryOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { m_stack.push_back(Level(name, size)); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::endArray() { +void KVBinaryOutputStreamSerializer::endArray() { bool validArray = m_stack.back().state == State::Array; m_stack.pop_back(); if (m_stack.back().state == State::Object && validArray) { ++m_stack.back().count; } - - return *this; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(uint8_t& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(uint8_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT8, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(uint32_t& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT32, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(int32_t& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(int32_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_INT32, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(int64_t& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(int64_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_INT64, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(uint64_t& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(uint64_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT64, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(bool& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(bool& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_BOOL, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(double& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(double& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_DOUBLE, name); writePod(stream(), value); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::operator()(std::string& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::operator()(std::string& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_STRING, name); auto& out = stream(); writeArraySize(out, value.size()); out.write(value.data(), value.size()); - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::binary(void* value, std::size_t size, const std::string& name) { +bool KVBinaryOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { if (size > 0) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_STRING, name); auto& out = stream(); writeArraySize(out, size); out.write(static_cast(value), size); } - return *this; + return true; } -ISerializer& KVBinaryOutputStreamSerializer::binary(std::string& value, const std::string& name) { +bool KVBinaryOutputStreamSerializer::binary(std::string& value, Common::StringView name) { return binary(const_cast(value.data()), value.size(), name); } -bool KVBinaryOutputStreamSerializer::hasObject(const std::string& name) { - assert(false); //the method is not supported for this type of serialization - throw std::runtime_error("hasObject method is not supported in KVBinaryOutputStreamSerializer"); - - return false; -} - -void KVBinaryOutputStreamSerializer::writeElementPrefix(uint8_t type, const std::string& name) { +void KVBinaryOutputStreamSerializer::writeElementPrefix(uint8_t type, Common::StringView name) { assert(m_stack.size()); checkArrayPreamble(type); Level& level = m_stack.back(); if (level.state != State::Array) { - if (!name.empty()) { + if (!name.isEmpty()) { auto& s = stream(); writeElementName(s, name); s.write((const char*)&type, 1); diff --git a/src/serialization/KVBinaryOutputStreamSerializer.h b/src/serialization/KVBinaryOutputStreamSerializer.h index 86d3eec048..70fb43d933 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.h +++ b/src/serialization/KVBinaryOutputStreamSerializer.h @@ -18,7 +18,6 @@ #pragma once #include "ISerializer.h" -#include "SerializationOverloads.h" #include "MemoryStream.h" #include @@ -38,34 +37,31 @@ class KVBinaryOutputStreamSerializer : public ISerializer { virtual ISerializer::SerializerType type() const; - virtual ISerializer& beginObject(const std::string& name) override; - virtual ISerializer& endObject() override; + virtual bool beginObject(Common::StringView name) override; + virtual void endObject() override; - virtual ISerializer& beginArray(std::size_t& size, const std::string& name) override; - virtual ISerializer& endArray() override; + virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual void endArray() override; - virtual ISerializer& operator()(uint8_t& value, const std::string& name) override; - virtual ISerializer& operator()(int32_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint32_t& value, const std::string& name) override; - virtual ISerializer& operator()(int64_t& value, const std::string& name) override; - virtual ISerializer& operator()(uint64_t& value, const std::string& name) override; - virtual ISerializer& operator()(double& value, const std::string& name) override; - virtual ISerializer& operator()(bool& value, const std::string& name) override; - virtual ISerializer& operator()(std::string& value, const std::string& name) override; - - virtual ISerializer& binary(void* value, std::size_t size, const std::string& name) override; - virtual ISerializer& binary(std::string& value, const std::string& name) override; - - virtual bool hasObject(const std::string& name) override; + virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int32_t& value, Common::StringView name) override; + virtual bool operator()(uint32_t& value, Common::StringView name) override; + virtual bool operator()(int64_t& value, Common::StringView name) override; + virtual bool operator()(uint64_t& value, Common::StringView name) override; + virtual bool operator()(double& value, Common::StringView name) override; + virtual bool operator()(bool& value, Common::StringView name) override; + virtual bool operator()(std::string& value, Common::StringView name) override; + virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(std::string& value, Common::StringView name) override; template - ISerializer& operator()(T& value, const std::string& name) { + bool operator()(T& value, Common::StringView name) { return ISerializer::operator()(value, name); } private: - void writeElementPrefix(uint8_t type, const std::string& name); + void writeElementPrefix(uint8_t type, Common::StringView name); void checkArrayPreamble(uint8_t type); void updateState(uint8_t type); MemoryStream& stream(); @@ -82,10 +78,10 @@ class KVBinaryOutputStreamSerializer : public ISerializer { std::string name; size_t count; - Level(const std::string& nm) : + Level(Common::StringView nm) : name(nm), state(State::Object), count(0) {} - Level(const std::string& nm, size_t arraySize) : + Level(Common::StringView nm, size_t arraySize) : name(nm), state(State::ArrayPrefix), count(arraySize) {} Level(Level&& rv) { diff --git a/src/serialization/SerializationOverloads.h b/src/serialization/SerializationOverloads.h index 921e9e5271..71ddf04678 100644 --- a/src/serialization/SerializationOverloads.h +++ b/src/serialization/SerializationOverloads.h @@ -29,8 +29,8 @@ namespace CryptoNote { template -typename std::enable_if::value>::type -serializeAsBinary(std::vector& value, const std::string& name, CryptoNote::ISerializer& serializer) { +typename std::enable_if::value>::type +serializeAsBinary(std::vector& value, Common::StringView name, CryptoNote::ISerializer& serializer) { std::string blob; if (serializer.type() == ISerializer::INPUT) { serializer.binary(blob, name); @@ -47,22 +47,39 @@ serializeAsBinary(std::vector& value, const std::string& name, CryptoNote::IS } template -void serialize(std::vector& value, const std::string& name, CryptoNote::ISerializer& serializer) { - std::size_t size = value.size(); - serializer.beginArray(size, name); - value.resize(size); +typename std::enable_if::value>::type +serializeAsBinary(std::list& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + std::string blob; + if (serializer.type() == ISerializer::INPUT) { + serializer.binary(blob, name); - for (auto& item : value) { - serializer(item, ""); - } + size_t count = blob.size() / sizeof(T); + const T* ptr = reinterpret_cast(blob.data()); - serializer.endArray(); + while (count--) { + value.push_back(*ptr++); + } + } else { + if (!value.empty()) { + blob.resize(value.size() * sizeof(T)); + T* ptr = reinterpret_cast(&blob[0]); + + for (const auto& item : value) { + *ptr++ = item; + } + } + serializer.binary(blob, name); + } } -template -void serialize(std::list& value, const std::string& name, CryptoNote::ISerializer& serializer) { +template +bool serializeContainer(Cont& value, Common::StringView name, CryptoNote::ISerializer& serializer) { std::size_t size = value.size(); - serializer.beginArray(size, name); + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + value.resize(size); for (auto& item : value) { @@ -70,15 +87,29 @@ void serialize(std::list& value, const std::string& name, CryptoNote::ISerial } serializer.endArray(); + return true; +} + +template +bool serialize(std::vector& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeContainer(value, name, serializer); +} + +template +bool serialize(std::list& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeContainer(value, name, serializer); } template -void serialize(std::unordered_map& value, const std::string& name, CryptoNote::ISerializer& serializer) { +bool serialize(std::unordered_map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { std::size_t size; size = value.size(); - serializer.beginArray(size, name); + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } if (serializer.type() == CryptoNote::ISerializer::INPUT) { value.reserve(size); @@ -105,11 +136,12 @@ void serialize(std::unordered_map& value, const std::string& name, C } serializer.endArray(); + return true; } template -void serialize(std::array& value, const std::string& name, CryptoNote::ISerializer& s) { - s.binary(value.data(), value.size(), name); +bool serialize(std::array& value, Common::StringView name, CryptoNote::ISerializer& s) { + return s.binary(value.data(), value.size(), name); } } diff --git a/src/serialization/SerializationTools.h b/src/serialization/SerializationTools.h new file mode 100644 index 0000000000..dc314c7016 --- /dev/null +++ b/src/serialization/SerializationTools.h @@ -0,0 +1,134 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "KVBinaryInputStreamSerializer.h" +#include "KVBinaryOutputStreamSerializer.h" + +#include "JsonInputStreamSerializer.h" +#include "JsonOutputStreamSerializer.h" + +namespace Common { + +template +T getValueAs(const JsonValue& js) { + return js; + //cdstatic_assert(false, "undefined conversion"); +} + +template <> +inline std::string getValueAs(const JsonValue& js) { return js.getString(); } + +template <> +inline uint64_t getValueAs(const JsonValue& js) { return static_cast(js.getInteger()); } + + +} + +namespace CryptoNote { + +template +Common::JsonValue storeToJsonValue(const T& v) { + JsonOutputStreamSerializer s; + serialize(const_cast(v), s); + return s.getValue(); +} + +template +Common::JsonValue storeContainerToJsonValue(const T& cont) { + Common::JsonValue js(Common::JsonValue::ARRAY); + for (const auto& item : cont) { + js.pushBack(item); + } + return js; +} + +template +Common::JsonValue storeToJsonValue(const std::vector& v) { return storeContainerToJsonValue(v); } + +template +Common::JsonValue storeToJsonValue(const std::list& v) { return storeContainerToJsonValue(v); } + +template <> +inline Common::JsonValue storeToJsonValue(const std::string& v) { return Common::JsonValue(v); } + + +template +void loadFromJsonValue(T& v, const Common::JsonValue& js) { + JsonInputValueSerializer s(js); + serialize(v, s); +} + +template +void loadFromJsonValue(std::vector& v, const Common::JsonValue& js) { + for (size_t i = 0; i < js.size(); ++i) { + v.push_back(Common::getValueAs(js[i])); + } +} + +template +void loadFromJsonValue(std::list& v, const Common::JsonValue& js) { + for (size_t i = 0; i < js.size(); ++i) { + v.push_back(Common::getValueAs(js[i])); + } +} + + +template +std::string storeToJson(const T& v) { + return storeToJsonValue(v).toString(); +} + +template +bool loadFromJson(T& v, const std::string& buf) { + try { + if (buf.empty()) { + return true; + } + auto js = Common::JsonValue::fromString(buf); + loadFromJsonValue(v, js); + } catch (std::exception&) { + return false; + } + return true; +} + +template +std::string storeToBinaryKeyValue(const T& v) { + KVBinaryOutputStreamSerializer s; + serialize(const_cast(v), s); + + std::ostringstream stream; + s.write(stream); + return stream.str(); +} + +template +bool loadFromBinaryKeyValue(T& v, const std::string& buf) { + try { + std::istringstream stream(buf); + KVBinaryInputStreamSerializer s(stream); + serialize(v, s); + return stream.good(); + } catch (std::exception&) { + return false; + } +} + + +} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 69c0184747..5dcbdc0460 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -17,11 +17,13 @@ #include "simplewallet.h" +#include #include #include #include #include +#include #include #include #include @@ -57,7 +59,7 @@ using Common::JsonValue; namespace po = boost::program_options; #define EXTENDED_LOGS_FILE "wallet_details.log" - +#undef ERROR namespace { diff --git a/src/transfers/TransfersContainer.cpp b/src/transfers/TransfersContainer.cpp index 4a0a827db7..ed46916544 100644 --- a/src/transfers/TransfersContainer.cpp +++ b/src/transfers/TransfersContainer.cpp @@ -24,7 +24,7 @@ namespace CryptoNote { -void serialize(TransactionInformation& ti, const std::string& name, CryptoNote::ISerializer& s) { +void serialize(TransactionInformation& ti, CryptoNote::ISerializer& s) { s(ti.transactionHash, ""); s(ti.publicKey, ""); s(ti.blockHeight, ""); @@ -99,7 +99,9 @@ SpentOutputDescriptor::SpentOutputDescriptor() : } SpentOutputDescriptor::SpentOutputDescriptor(const TransactionOutputInformationIn& transactionInfo) : - m_type(transactionInfo.type) { + m_type(transactionInfo.type), + m_amount(0), + m_globalOutputIndex(0) { if (m_type == TransactionTypes::OutputType::Key) { m_keyImage = &transactionInfo.keyImage; } else if (m_type == TransactionTypes::OutputType::Multisignature) { @@ -212,6 +214,7 @@ void TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti } auto result = m_transactions.emplace(std::move(txInfo)); + (void)result; // Disable unused warning assert(result.second); } @@ -244,6 +247,7 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr if (transferIsUnconfirmed) { auto result = m_unconfirmedTransfers.emplace(std::move(info)); + (void)result; // Disable unused warning assert(result.second); } else { if (info.type == TransactionTypes::OutputType::Multisignature) { @@ -255,6 +259,7 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr } auto result = m_availableTransfers.emplace(std::move(info)); + (void)result; // Disable unused warning assert(result.second); } @@ -400,6 +405,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const } auto result = m_availableTransfers.emplace(std::move(transfer)); + (void)result; // Disable unused warning assert(result.second); transferIt = m_unconfirmedTransfers.get().erase(transferIt); @@ -479,6 +485,7 @@ void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionR spentOutput.spendingTransactionHash = tx.getTransactionHash(); spentOutput.inputInTransaction = static_cast(inputIndex); auto result = m_spentTransfers.emplace(std::move(spentOutput)); + (void)result; // Disable unused warning assert(result.second); } diff --git a/src/transfers/TransfersContainer.h b/src/transfers/TransfersContainer.h index fba0b269e9..72d4c17a0f 100644 --- a/src/transfers/TransfersContainer.h +++ b/src/transfers/TransfersContainer.h @@ -60,7 +60,7 @@ class SpentOutputDescriptor { TransactionTypes::OutputType m_type; union { const KeyImage* m_keyImage; -struct { + struct { uint64_t m_amount; uint64_t m_globalOutputIndex; }; @@ -86,7 +86,7 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn { SpentOutputDescriptor getSpentOutputDescriptor() const { return SpentOutputDescriptor(*this); } const Hash& getTransactionHash() const { return transactionHash; } - void serialize(CryptoNote::ISerializer& s, const std::string& name) { + void serialize(CryptoNote::ISerializer& s) { s(reinterpret_cast(type), "type"); s(amount, ""); s(globalOutputIndex, ""); @@ -112,7 +112,7 @@ struct BlockInfo { uint64_t timestamp; uint32_t transactionIndex; - void serialize(ISerializer& s, const std::string& name) { + void serialize(ISerializer& s) { s(height, "height"); s(timestamp, "timestamp"); s(transactionIndex, "transactionIndex"); @@ -128,8 +128,8 @@ struct SpentTransactionOutput : TransactionOutputInformationEx { return spendingTransactionHash; } - void serialize(ISerializer& s, const std::string& name) { - TransactionOutputInformationEx::serialize(s, name); + void serialize(ISerializer& s) { + TransactionOutputInformationEx::serialize(s); s(spendingBlock, "spendingBlock"); s(spendingTransactionHash, "spendingTransactionHash"); s(inputInTransaction, "inputInTransaction"); diff --git a/src/transfers/TransfersSubscription.cpp b/src/transfers/TransfersSubscription.cpp index 12dcbeaf8b..cb2309ce8a 100644 --- a/src/transfers/TransfersSubscription.cpp +++ b/src/transfers/TransfersSubscription.cpp @@ -21,7 +21,7 @@ namespace CryptoNote { TransfersSubscription::TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub) - : m_currency(currency), m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} + : m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} SynchronizationStart TransfersSubscription::getSyncStart() { diff --git a/src/transfers/TransfersSubscription.h b/src/transfers/TransfersSubscription.h index 67ff9076cf..da3630dcc2 100644 --- a/src/transfers/TransfersSubscription.h +++ b/src/transfers/TransfersSubscription.h @@ -25,7 +25,6 @@ namespace CryptoNote { class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITransfersSubscription > { public: - TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub); SynchronizationStart getSyncStart(); @@ -33,8 +32,8 @@ class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITran void onError(const std::error_code& ec, uint64_t height); bool advanceHeight(uint64_t height); const AccountKeys& getKeys() const; - void addTransaction(const BlockInfo& blockInfo, - const ITransactionReader& tx, const std::vector& transfers); + void addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, + const std::vector& transfers); void deleteUnconfirmedTransaction(const Hash& transactionHash); void markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); @@ -44,10 +43,8 @@ class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITran virtual ITransfersContainer& getContainer() override; private: - TransfersContainer m_transfers; AccountSubscription m_subscription; - const CryptoNote::Currency& m_currency; }; } diff --git a/src/transfers/TransfersSynchronizer.cpp b/src/transfers/TransfersSynchronizer.cpp index ea5aaabc3d..3dafa027fa 100644 --- a/src/transfers/TransfersSynchronizer.cpp +++ b/src/transfers/TransfersSynchronizer.cpp @@ -23,11 +23,9 @@ namespace CryptoNote { -void serialize(AccountAddress& acc, const std::string& name, CryptoNote::ISerializer& s) { - s.beginObject(name); +void serialize(AccountAddress& acc, CryptoNote::ISerializer& s) { s(acc.spendPublicKey, "spendKey"); s(acc.viewPublicKey, "viewKey"); - s.endObject(); } const uint32_t TRANSFERS_STORAGE_ARCHIVE_VERSION = 0; diff --git a/src/version.h.in b/src/version.h.in index 7a7edafc61..ff69191995 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.4.1" -#define PROJECT_VERSION_BUILD_NO "466" +#define PROJECT_VERSION "1.0.5" +#define PROJECT_VERSION_BUILD_NO "503" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/LegacyKeysImporter.cpp b/src/wallet/LegacyKeysImporter.cpp index 336053136f..44110f3432 100755 --- a/src/wallet/LegacyKeysImporter.cpp +++ b/src/wallet/LegacyKeysImporter.cpp @@ -24,11 +24,9 @@ #include "cryptonote_core/Currency.h" #include "cryptonote_core/account.h" -#include "cryptonote_core/AccountKVSerialization.h" #include "serialization/binary_utils.h" -#include "storages/portable_storage.h" -#include "storages/portable_storage_template_helper.h" +#include "serialization/SerializationTools.h" #include "wallet/WalletSerializer.h" #include "wallet/WalletUserTransactionsCache.h" @@ -71,15 +69,15 @@ void loadKeysFromFile(const std::string& filename, const std::string& password, account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - const ::CryptoNote::account_keys& keys = account.get_keys(); - CryptoNote::AccountBaseSerializer accountSerializer(account); - bool r = epee::serialization::load_t_from_binary(accountSerializer, account_data); - r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey); - r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey); + const CryptoNote::account_keys& keys = account.get_keys(); - if (!r) { - throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); + if (CryptoNote::loadFromBinaryKeyValue(account, account_data) && + verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey) && + verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey)) { + return; } + + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); } } diff --git a/src/wallet/Wallet.cpp b/src/wallet/Wallet.cpp index d1ce67cab7..6d047e591d 100755 --- a/src/wallet/Wallet.cpp +++ b/src/wallet/Wallet.cpp @@ -40,11 +40,6 @@ bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expecte return r && expected_pub == pub; } -void throwIfKeysMissmatch(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { - if (!verifyKeys(sec, expected_pub)) - throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); -} - class ContextCounterHolder { public: diff --git a/src/wallet/WalletSerialization.cpp b/src/wallet/WalletSerialization.cpp index a79ac6cc85..6246c2bde8 100644 --- a/src/wallet/WalletSerialization.cpp +++ b/src/wallet/WalletSerialization.cpp @@ -25,8 +25,7 @@ namespace CryptoNote { -void serialize(UnconfirmedTransferDetails& utd, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); +void serialize(UnconfirmedTransferDetails& utd, CryptoNote::ISerializer& serializer) { serializer(utd.tx, "transaction"); serializer(utd.amount, "amount"); serializer(utd.outsAmount, "outs_amount"); @@ -36,12 +35,9 @@ void serialize(UnconfirmedTransferDetails& utd, const std::string& name, CryptoN uint64_t txId = static_cast(utd.transactionId); serializer(txId, "transaction_id"); utd.transactionId = static_cast(txId); - serializer.endObject(); } -void serialize(TransactionInfo& txi, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - +void serialize(TransactionInfo& txi, CryptoNote::ISerializer& serializer) { uint64_t trId = static_cast(txi.firstTransferId); serializer(trId, "first_transfer_id"); txi.firstTransferId = static_cast(trId); @@ -59,14 +55,11 @@ void serialize(TransactionInfo& txi, const std::string& name, CryptoNote::ISeria serializer(txi.timestamp, "timestamp"); serializer(txi.unlockTime, "unlock_time"); serializer(txi.extra, "extra"); - serializer.endObject(); } -void serialize(Transfer& tr, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); +void serialize(Transfer& tr, CryptoNote::ISerializer& serializer) { serializer(tr.address, "address"); serializer(tr.amount, "amount"); - serializer.endObject(); } } //namespace CryptoNote diff --git a/src/wallet/WalletSerialization.h b/src/wallet/WalletSerialization.h index aef7e9b974..85a0f24051 100755 --- a/src/wallet/WalletSerialization.h +++ b/src/wallet/WalletSerialization.h @@ -25,16 +25,13 @@ namespace CryptoNote { class ISerializer; -} - -namespace CryptoNote { struct UnconfirmedTransferDetails; struct TransactionInfo; struct Transfer; -void serialize(UnconfirmedTransferDetails& utd, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(TransactionInfo& txi, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(Transfer& tr, const std::string& name, CryptoNote::ISerializer& serializer); +void serialize(UnconfirmedTransferDetails& utd, ISerializer& serializer); +void serialize(TransactionInfo& txi, ISerializer& serializer); +void serialize(Transfer& tr, ISerializer& serializer); } diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 95dec268b0..53eee3b64e 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -316,7 +316,11 @@ namespace { template T popRandomValue(URNG& randomGenerator, std::vector& vec) { - CHECK_AND_ASSERT_MES(!vec.empty(), T(), "Vector must be non-empty"); + assert(!vec.empty()); + + if (vec.empty()) { + return T(); + } std::uniform_int_distribution distribution(0, vec.size() - 1); size_t idx = distribution(randomGenerator); diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/wallet/WalletUnconfirmedTransactions.cpp index 44dde435d7..da528c7d87 100644 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/wallet/WalletUnconfirmedTransactions.cpp @@ -28,14 +28,12 @@ inline TransactionOutputId getOutputId(const TransactionOutputInformation& out) return std::make_pair(out.transactionPublicKey, out.outputInTransaction); } -void WalletUnconfirmedTransactions::serialize(CryptoNote::ISerializer& s, const std::string& name) { - s.beginObject(name); +bool WalletUnconfirmedTransactions::serialize(CryptoNote::ISerializer& s) { s(m_unconfirmedTxs, "transactions"); - s.endObject(); - if (s.type() == CryptoNote::ISerializer::INPUT) { collectUsedOutputs(); } + return true; } bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& hash, TransactionId& id) { diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/wallet/WalletUnconfirmedTransactions.h index a5b71152bd..0ccdf1c2cc 100644 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/wallet/WalletUnconfirmedTransactions.h @@ -53,7 +53,7 @@ class WalletUnconfirmedTransactions { public: - void serialize(CryptoNote::ISerializer& s, const std::string& name); + bool serialize(CryptoNote::ISerializer& s); bool findTransactionId(const TransactionHash& hash, TransactionId& id); void erase(const TransactionHash& hash); diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/wallet/WalletUserTransactionsCache.cpp index a1f0171cc0..2772988058 100644 --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/wallet/WalletUserTransactionsCache.cpp @@ -27,9 +27,7 @@ namespace CryptoNote { -void WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s, const std::string& name) { - s.beginObject(name); - +bool WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s) { if (s.type() == CryptoNote::ISerializer::INPUT) { s(m_transactions, "transactions"); s(m_transfers, "transfers"); @@ -45,7 +43,7 @@ void WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s, const st s(m_unconfirmedTransactions, "unconfirmed"); } - s.endObject(); + return true; } uint64_t WalletUserTransactionsCache::unconfirmedTransactionsAmount() const { @@ -151,7 +149,7 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; if (m_unconfirmedTransactions.findTransactionId(transactionHash, id)) { m_unconfirmedTransactions.erase(transactionHash); - LOG_ERROR("Unconfirmed transaction is deleted: id = " << id << ", hash = " << transactionHash); + // LOG_ERROR("Unconfirmed transaction is deleted: id = " << id << ", hash = " << transactionHash); assert(false); } else { id = findTransactionByHash(transactionHash); @@ -166,7 +164,7 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c event = std::make_shared(id); } else { - LOG_ERROR("Transaction wasn't found: " << transactionHash); + // LOG_ERROR("Transaction wasn't found: " << transactionHash); assert(false); } diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/wallet/WalletUserTransactionsCache.h index 1bdf9f9b12..10faeca816 100644 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/wallet/WalletUserTransactionsCache.h @@ -35,7 +35,7 @@ class WalletUserTransactionsCache public: WalletUserTransactionsCache() {} - void serialize(CryptoNote::ISerializer& serializer, const std::string& name); + bool serialize(CryptoNote::ISerializer& serializer); uint64_t unconfirmedTransactionsAmount() const; uint64_t unconfrimedOutsAmount() const; diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h index d8144d209f..8ef7be5acc 100644 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/wallet/wallet_rpc_server_commans_defs.h @@ -20,30 +20,34 @@ #include "cryptonote_core/cryptonote_basic.h" #include "crypto/hash.h" #include "wallet_rpc_server_error_codes.h" + namespace tools { namespace wallet_rpc { + +using CryptoNote::ISerializer; + #define WALLET_RPC_STATUS_OK "OK" #define WALLET_RPC_STATUS_BUSY "BUSY" + struct EMPTY_STRUCT { + void serialize(ISerializer& s) {} + }; + struct COMMAND_RPC_GET_BALANCE { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + typedef EMPTY_STRUCT request; struct response { uint64_t locked_amount; uint64_t available_balance; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(locked_amount) - KV_SERIALIZE(available_balance) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(locked_amount) + KV_MEMBER(available_balance) + } }; }; @@ -51,10 +55,11 @@ namespace wallet_rpc { uint64_t amount; std::string address; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(address) - END_KV_SERIALIZE_MAP() + + void serialize(ISerializer& s) { + KV_MEMBER(amount) + KV_MEMBER(address) + } }; struct COMMAND_RPC_TRANSFER @@ -67,38 +72,29 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(destinations) - KV_SERIALIZE(fee) - KV_SERIALIZE(mixin) - KV_SERIALIZE(unlock_time) - KV_SERIALIZE(payment_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(destinations) + KV_MEMBER(fee) + KV_MEMBER(mixin) + KV_MEMBER(unlock_time) + KV_MEMBER(payment_id) + } }; struct response { std::string tx_hash; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(tx_hash) + } }; }; struct COMMAND_RPC_STORE { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + typedef EMPTY_STRUCT request; + typedef EMPTY_STRUCT response; }; struct payment_details @@ -108,12 +104,12 @@ namespace wallet_rpc uint64_t block_height; uint64_t unlock_time; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(amount) - KV_SERIALIZE(block_height) - KV_SERIALIZE(unlock_time) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(tx_hash) + KV_MEMBER(amount) + KV_MEMBER(block_height) + KV_MEMBER(unlock_time) + } }; struct COMMAND_RPC_GET_PAYMENTS @@ -122,18 +118,18 @@ namespace wallet_rpc { std::string payment_id; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(payment_id) + } }; struct response { std::list payments; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payments) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(payments) + } }; }; @@ -148,59 +144,46 @@ namespace wallet_rpc uint64_t blockIndex; uint64_t unlockTime; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(time) - KV_SERIALIZE(output) - KV_SERIALIZE(transactionHash) - KV_SERIALIZE(amount) - KV_SERIALIZE(fee) - KV_SERIALIZE(paymentId) - KV_SERIALIZE(address) - KV_SERIALIZE(blockIndex) - KV_SERIALIZE(unlockTime) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(time) + KV_MEMBER(output) + KV_MEMBER(transactionHash) + KV_MEMBER(amount) + KV_MEMBER(fee) + KV_MEMBER(paymentId) + KV_MEMBER(address) + KV_MEMBER(blockIndex) + KV_MEMBER(unlockTime) + } }; struct COMMAND_RPC_GET_TRANSFERS { - struct request { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + typedef EMPTY_STRUCT request; struct response { std::list transfers; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(transfers) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(transfers) + } }; }; struct COMMAND_RPC_GET_HEIGHT { - struct request { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + typedef EMPTY_STRUCT request; struct response { uint64_t height; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(height) - END_KV_SERIALIZE_MAP() + void serialize(ISerializer& s) { + KV_MEMBER(height) + } }; }; struct COMMAND_RPC_RESET { - struct request { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; + typedef EMPTY_STRUCT request; + typedef EMPTY_STRUCT response; }; } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index efa35703c1..bad78332c8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,20 +33,20 @@ add_executable(DifficultyTests difficulty/difficulty.cpp) add_executable(HashTargetTests hash-target.cpp) add_executable(HashTests hash/main.cpp) -target_link_libraries(CoreTests epee TestGenerator CryptoNoteCore Logging Common Crypto ${Boost_LIBRARIES}) -target_link_libraries(IntegrationTests epee IntegrationTestLibrary Wallet NodeRpcProxy InProcessNode P2P Rpc Http Transfers Serialization System CryptoNoteCore Logging Common Crypto gtest upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(NodeRpcProxyTests epee NodeRpcProxy CryptoNoteCore Rpc Http Serialization System Logging Common Crypto ${Boost_LIBRARIES}) -target_link_libraries(PerformanceTests epee CryptoNoteCore Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(CoreTests TestGenerator CryptoNoteCore Serialization Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(IntegrationTests IntegrationTestLibrary Wallet NodeRpcProxy InProcessNode P2P Rpc Http Transfers Serialization System CryptoNoteCore Logging Common Crypto BlockchainExplorer gtest upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(NodeRpcProxyTests NodeRpcProxy CryptoNoteCore Rpc Http Serialization System Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(PerformanceTests CryptoNoteCore Serialization Logging Common Crypto ${Boost_LIBRARIES}) target_link_libraries(SystemTests System gtest_main) if (MSVC) target_link_libraries(SystemTests ws2_32) endif () -target_link_libraries(TransfersTests IntegrationTestLibrary Wallet epee gtest_main CryptoNoteCore InProcessNode NodeRpcProxy P2P Rpc Http Serialization System Transfers Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) -target_link_libraries(UnitTests epee gtest_main Wallet TestGenerator CryptoNoteCore InProcessNode Transfers Serialization System Logging Common Crypto ${Boost_LIBRARIES}) +target_link_libraries(TransfersTests IntegrationTestLibrary Wallet gtest_main CryptoNoteCore InProcessNode NodeRpcProxy P2P Rpc Http Serialization System Transfers Logging Common Crypto BlockchainExplorer upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(UnitTests gtest_main Wallet TestGenerator CryptoNoteCore InProcessNode Transfers Serialization System Logging Common Crypto BlockchainExplorer ${Boost_LIBRARIES}) -target_link_libraries(DifficultyTests epee CryptoNoteCore Crypto Logging Common ${Boost_LIBRARIES}) -target_link_libraries(HashTargetTests epee CryptoNoteCore Crypto) +target_link_libraries(DifficultyTests CryptoNoteCore Serialization Crypto Logging Common ${Boost_LIBRARIES}) +target_link_libraries(HashTargetTests CryptoNoteCore Crypto) target_link_libraries(HashTests Crypto) if(NOT MSVC) diff --git a/tests/TestGenerator/TestGenerator.cpp b/tests/TestGenerator/TestGenerator.cpp index ed6bfc22de..83e2487a19 100644 --- a/tests/TestGenerator/TestGenerator.cpp +++ b/tests/TestGenerator/TestGenerator.cpp @@ -17,17 +17,17 @@ #include "TestGenerator.h" -// epee -#include "misc_language.h" - +#include #include "cryptonote_core/account.h" #include "cryptonote_core/miner.h" using namespace std; - -using namespace epee; using namespace CryptoNote; +#ifndef CHECK_AND_ASSERT_MES +#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {std::cerr << message << std::endl; return fail_ret_val;};}while(0) +#endif + void test_generator::getBlockchain(std::vector& blockchain, const crypto::hash& head, size_t n) const { crypto::hash curr = head; @@ -73,7 +73,7 @@ void test_generator::addBlock(const CryptoNote::Block& blk, size_t tsxSize, uint int64_t emissionChange; uint64_t blockReward; bool penalizeFee = blk.majorVersion > BLOCK_MAJOR_VERSION_1; - m_currency.getBlockReward(misc_utils::median(blockSizes), blockSize, alreadyGeneratedCoins, fee, penalizeFee, + m_currency.getBlockReward(Common::medianValue(blockSizes), blockSize, alreadyGeneratedCoins, fee, penalizeFee, blockReward, emissionChange); m_blocksInfo[get_block_hash(blk)] = BlockInfo(blk.prevId, alreadyGeneratedCoins + emissionChange, blockSize); } @@ -103,10 +103,10 @@ bool test_generator::constructBlock(CryptoNote::Block& blk, uint32_t height, con txsSize += get_object_blobsize(tx); } - blk.minerTx = AUTO_VAL_INIT(blk.minerTx); + blk.minerTx = boost::value_initialized(); size_t targetBlockSize = txsSize + get_object_blobsize(blk.minerTx); while (true) { - if (!m_currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, targetBlockSize, + if (!m_currency.constructMinerTx(height, Common::medianValue(blockSizes), alreadyGeneratedCoins, targetBlockSize, totalFee, minerAcc.get_keys().m_account_address, blk.minerTx, blobdata(), 10)) { return false; } @@ -209,7 +209,7 @@ bool test_generator::constructBlockManually(Block& blk, const Block& prevBlock, } else { size_t currentBlockSize = txsSizes + get_object_blobsize(blk.minerTx); // TODO: This will work, until size of constructed block is less then m_currency.blockGrantedFullRewardZone() - if (!m_currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, currentBlockSize, 0, + if (!m_currency.constructMinerTx(height, Common::medianValue(blockSizes), alreadyGeneratedCoins, currentBlockSize, 0, minerAcc.get_keys().m_account_address, blk.minerTx, blobdata(), 1, blk.majorVersion > BLOCK_MAJOR_VERSION_1)) { return false; } @@ -257,7 +257,7 @@ bool test_generator::constructMaxSizeBlock(CryptoNote::Block& blk, const CryptoN medianBlockCount = medianBlockCount == 0 ? m_currency.rewardBlocksWindow() : medianBlockCount; getLastNBlockSizes(blockSizes, get_block_hash(blkPrev), medianBlockCount); - size_t median = misc_utils::median(blockSizes); + size_t median = Common::medianValue(blockSizes); size_t blockGrantedFullRewardZone = defaultMajorVersion <= BLOCK_MAJOR_VERSION_1 ? CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : m_currency.blockGrantedFullRewardZone(); @@ -314,7 +314,7 @@ bool constructMinerTxManually(const CryptoNote::Currency& currency, uint32_t hei int64_t emissionChange; uint64_t blockReward; if (!currency.getBlockReward(0, 0, alreadyGeneratedCoins, fee, false, blockReward, emissionChange)) { - LOG_PRINT_L0("Block is too big"); + std::cerr << "Block is too big" << std::endl; return false; } @@ -338,7 +338,7 @@ bool constructMinerTxBySize(const CryptoNote::Currency& currency, CryptoNote::Tr uint64_t alreadyGeneratedCoins, const CryptoNote::AccountPublicAddress& minerAddress, std::vector& blockSizes, size_t targetTxSize, size_t targetBlockSize, uint64_t fee/* = 0*/, bool penalizeFee/* = false*/) { - if (!currency.constructMinerTx(height, misc_utils::median(blockSizes), alreadyGeneratedCoins, targetBlockSize, + if (!currency.constructMinerTx(height, Common::medianValue(blockSizes), alreadyGeneratedCoins, targetBlockSize, fee, minerAddress, minerTx, CryptoNote::blobdata(), 1, penalizeFee)) { return false; } diff --git a/tests/core_tests/block_reward.cpp b/tests/core_tests/block_reward.cpp index 98c6a33124..60e91d54df 100644 --- a/tests/core_tests/block_reward.cpp +++ b/tests/core_tests/block_reward.cpp @@ -16,11 +16,8 @@ // along with Bytecoin. If not, see . #include "block_reward.h" +#include -// epee -#include "misc_language.h" - -using namespace epee; using namespace CryptoNote; namespace @@ -136,7 +133,7 @@ bool gen_block_reward::generate(std::vector& events) const std::vector block_sizes; generator.getLastNBlockSizes(block_sizes, get_block_hash(blk_7), m_currency.rewardBlocksWindow()); - size_t median = misc_utils::median(block_sizes); + size_t median = Common::medianValue(block_sizes); Transaction miner_tx; bool r = constructMinerTxBySize(m_currency, miner_tx, get_block_height(blk_7) + 1, generator.getAlreadyGeneratedCoins(blk_7), diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index e7d5b502e3..fd308afa40 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -18,7 +18,6 @@ #include "block_validation.h" #include "TestGenerator.h" -using namespace epee; using namespace CryptoNote; #define BLOCK_VALIDATION_INIT_GENERATE() \ diff --git a/tests/core_tests/chain_split_1.cpp b/tests/core_tests/chain_split_1.cpp index f721029d2d..3039e790bc 100644 --- a/tests/core_tests/chain_split_1.cpp +++ b/tests/core_tests/chain_split_1.cpp @@ -18,8 +18,6 @@ #include "chain_split_1.h" using namespace std; - -using namespace epee; using namespace CryptoNote; diff --git a/tests/core_tests/chain_switch_1.cpp b/tests/core_tests/chain_switch_1.cpp index 3c2b5ae50a..1845fc9eb7 100644 --- a/tests/core_tests/chain_switch_1.cpp +++ b/tests/core_tests/chain_switch_1.cpp @@ -17,7 +17,6 @@ #include "chain_switch_1.h" -using namespace epee; using namespace CryptoNote; @@ -131,8 +130,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; - c.get_pool_transactions(tx_pool); + std::vector tx_pool = c.getPoolTransactions(); CHECK_EQ(1, tx_pool.size()); std::vector tx_outs; @@ -180,8 +178,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; - c.get_pool_transactions(tx_pool); + std::vector tx_pool = c.getPoolTransactions(); CHECK_EQ(1, tx_pool.size()); CHECK_TEST_CONDITION(!(tx_pool.front() == m_tx_pool.front())); diff --git a/tests/core_tests/chain_switch_1.h b/tests/core_tests/chain_switch_1.h index 33b06bfc36..e925492000 100644 --- a/tests/core_tests/chain_switch_1.h +++ b/tests/core_tests/chain_switch_1.h @@ -39,5 +39,5 @@ class gen_chain_switch_1 : public test_chain_unit_base 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.cpp b/tests/core_tests/chaingen.cpp index e60fcb8afd..932f976131 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -25,9 +25,6 @@ #include #include -#include "include_base_utils.h" -#include "misc_language.h" - #include "Common/command_line.h" #include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/cryptonote_basic.h" @@ -39,8 +36,6 @@ #include "cryptonote_core/UpgradeDetector.h" using namespace std; - -using namespace epee; using namespace CryptoNote; struct output_index { diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 3251489cb2..2c2cdaf00a 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -23,6 +23,8 @@ #include "Common/boost_serialization_helper.h" #include "Common/command_line.h" +#include "Common/ConsoleTools.h" + #include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_core.h" @@ -32,45 +34,52 @@ #include #include + + namespace concolor { + using namespace Common::Console; + inline std::basic_ostream >& bright_white(std::basic_ostream >& ostr) { - epee::log_space::set_console_color(epee::log_space::console_color_white, true); + setTextColor(Color::BrightWhite); return ostr; } inline std::basic_ostream >& red(std::basic_ostream >& ostr) { - epee::log_space::set_console_color(epee::log_space::console_color_red, true); + setTextColor(Color::BrightRed); return ostr; } inline std::basic_ostream >& green(std::basic_ostream >& ostr) { - epee::log_space::set_console_color(epee::log_space::console_color_green, true); + setTextColor(Color::BrightGreen); return ostr; } inline std::basic_ostream >& magenta(std::basic_ostream >& ostr) { - epee::log_space::set_console_color(epee::log_space::console_color_magenta, true); + setTextColor(Color::BrightMagenta); return ostr; } inline std::basic_ostream >& yellow(std::basic_ostream >& ostr) { - epee::log_space::set_console_color(epee::log_space::console_color_yellow, true); + setTextColor(Color::BrightYellow); return ostr; } inline std::basic_ostream >& normal(std::basic_ostream >& ostr) { - epee::log_space::reset_console_color(); + setTextColor(Color::Default); return ostr; } } +#define LOG_ERROR(msg) std::cout << concolor::red << msg << concolor::normal << std::endl +#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {std::cout << concolor::red << message << concolor::normal << std::endl; return fail_ret_val;};}while(0) +#define CHECK_AND_NO_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {std::cout << concolor::red << message << concolor::normal << std::endl; return fail_ret_val;};}while(0) struct callback_entry { @@ -372,24 +381,23 @@ struct push_core_event_visitor: public boost::static_visitor template inline bool replay_events_through_core(CryptoNote::core& cr, const std::vector& events, t_test_class& validator) { - TRY_ENTRY(); - - //init core here + try { + CHECK_AND_ASSERT_MES(typeid(CryptoNote::Block) == events[0].type(), false, "First event must be genesis block creation"); + cr.set_genesis_block(boost::get(events[0])); - CHECK_AND_ASSERT_MES(typeid(CryptoNote::Block) == events[0].type(), false, "First event must be genesis block creation"); - cr.set_genesis_block(boost::get(events[0])); + bool r = true; + push_core_event_visitor visitor(cr, events, validator); + for (size_t i = 1; i < events.size() && r; ++i) + { + visitor.event_index(i); + r = boost::apply_visitor(visitor, events[i]); + } - bool r = true; - push_core_event_visitor visitor(cr, events, validator); - for(size_t i = 1; i < events.size() && r; ++i) - { - visitor.event_index(i); - r = boost::apply_visitor(visitor, events[i]); + return r; + } catch (std::exception& e) { + std::cout << "replay_events_through_core: " << e.what(); + return false; } - - return r; - - CATCH_ENTRY_L0("replay_events_through_core", false); } //-------------------------------------------------------------------------- template @@ -562,11 +570,11 @@ inline bool do_replay_file(const std::string& filename) } \ catch (const std::exception& ex) \ { \ - LOG_PRINT(#genclass << " generation failed: what=" << ex.what(), 0); \ + std::cout << #genclass << " generation failed: what=" << ex.what(); \ } \ catch (...) \ { \ - LOG_PRINT(#genclass << " generation failed: generic exception", 0); \ + std::cout << #genclass << " generation failed: generic exception"; \ } \ genclass validator; \ if (generated && do_replay_events< genclass >(events, validator)) \ @@ -590,9 +598,9 @@ bool GenerateAndPlay(const char* testname, GenClassT&& g) { try { generated = g.generate(events); } catch (const std::exception& ex) { - LOG_PRINT(testname << " generation failed: what=" << ex.what(), 0); + std::cout << testname << " generation failed: what=" << ex.what(); } catch (...) { - LOG_PRINT(testname << " generation failed: generic exception", 0); + std::cout << testname << " generation failed: generic exception"; } bool succeeded = generated && do_replay_events(events, g); diff --git a/tests/core_tests/chaingen001.cpp b/tests/core_tests/chaingen001.cpp index e115005592..86f4bec2c8 100644 --- a/tests/core_tests/chaingen001.cpp +++ b/tests/core_tests/chaingen001.cpp @@ -18,8 +18,6 @@ #include "chaingen001.h" using namespace std; - -using namespace epee; using namespace CryptoNote; //////// diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 1700d0b67e..9b82daeff0 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -45,16 +45,7 @@ namespace int main(int argc, char* argv[]) { - TRY_ENTRY(); - // epee::string_tools::set_module_name_and_folder(argv[0]); - - //set up logging options - epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_3); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); - - epee::log_space::log_singletone::add_logger(LOGGER_FILE, - epee::log_space::log_singletone::get_default_log_file().c_str(), - epee::log_space::log_singletone::get_default_log_folder().c_str()); + try { po::options_description desc_options("Allowed options"); command_line::add_arg(desc_options, command_line::arg_help); @@ -238,5 +229,7 @@ int main(int argc, char* argv[]) return failed_tests.empty() ? 0 : 1; - CATCH_ENTRY_L0("main", 1); + } catch (std::exception& e) { + std::cout << "Exception in main(): " << e.what() << std::endl; + } } diff --git a/tests/core_tests/double_spend.cpp b/tests/core_tests/double_spend.cpp index 7a23917cd4..d89e719db3 100644 --- a/tests/core_tests/double_spend.cpp +++ b/tests/core_tests/double_spend.cpp @@ -18,7 +18,6 @@ #include "double_spend.h" #include "TestGenerator.h" -using namespace epee; using namespace CryptoNote; //====================================================================================================================== diff --git a/tests/core_tests/double_spend.inl b/tests/core_tests/double_spend.inl index 5e50f41ee9..f3af70ed6d 100644 --- a/tests/core_tests/double_spend.inl +++ b/tests/core_tests/double_spend.inl @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #pragma once //====================================================================================================================== diff --git a/tests/core_tests/integer_overflow.cpp b/tests/core_tests/integer_overflow.cpp index 4815ccc74a..daa88eb99c 100644 --- a/tests/core_tests/integer_overflow.cpp +++ b/tests/core_tests/integer_overflow.cpp @@ -17,7 +17,6 @@ #include "integer_overflow.h" -using namespace epee; using namespace CryptoNote; namespace diff --git a/tests/core_tests/ring_signature_1.cpp b/tests/core_tests/ring_signature_1.cpp index 1002b8a378..98e2382e4e 100644 --- a/tests/core_tests/ring_signature_1.cpp +++ b/tests/core_tests/ring_signature_1.cpp @@ -17,7 +17,6 @@ #include "ring_signature_1.h" -using namespace epee; using namespace CryptoNote; diff --git a/tests/core_tests/transaction_tests.cpp b/tests/core_tests/transaction_tests.cpp index a904e67b7e..5324a977fc 100644 --- a/tests/core_tests/transaction_tests.cpp +++ b/tests/core_tests/transaction_tests.cpp @@ -15,12 +15,12 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "include_base_utils.h" #include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/Currency.h" -#include "misc_language.h" + +#include #include "chaingen.h" @@ -147,7 +147,7 @@ bool test_block_creation() bool r = currency.parseAccountAddressString("272xWzbWsP4cfNFfxY5ETN5moU8x81PKfWPwynrrqsNGDBQGLmD1kCkKCvPeDUXu5XfmZkCrQ53wsWmdfvHBGLNjGcRiDcK", adr); CHECK_AND_ASSERT_MES(r, false, "failed to import"); Block b; - r = currency.constructMinerTx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, b.minerTx, blobdata(), 11); + r = currency.constructMinerTx(90, Common::medianValue(szs), 3553616528562147, 33094, 10000000, adr, b.minerTx, blobdata(), 11); return r; } diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index 572e384f47..d56beee005 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -18,7 +18,6 @@ #include "tx_validation.h" #include "TestGenerator.h" -using namespace epee; using namespace crypto; using namespace CryptoNote; diff --git a/tests/core_tests/upgrade.cpp b/tests/core_tests/upgrade.cpp index fe13f561bf..f6f564ba65 100644 --- a/tests/core_tests/upgrade.cpp +++ b/tests/core_tests/upgrade.cpp @@ -17,7 +17,6 @@ #include "upgrade.h" -using namespace epee; using namespace CryptoNote; namespace { diff --git a/tests/crypto/crypto-ops-data.c b/tests/crypto/crypto-ops-data.c index e31d95d6bb..16d1cc4f6b 100644 --- a/tests/crypto/crypto-ops-data.c +++ b/tests/crypto/crypto-ops-data.c @@ -15,21 +15,4 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "crypto/crypto-ops-data.c" diff --git a/tests/crypto/crypto-ops.c b/tests/crypto/crypto-ops.c index 3affc129e6..ca2404e712 100644 --- a/tests/crypto/crypto-ops.c +++ b/tests/crypto/crypto-ops.c @@ -15,21 +15,4 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "crypto/crypto-ops.c" diff --git a/tests/crypto/hash.c b/tests/crypto/hash.c index f3663896b4..d8bab89b66 100644 --- a/tests/crypto/hash.c +++ b/tests/crypto/hash.c @@ -15,22 +15,5 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "crypto/hash.c" #include "crypto/keccak.c" diff --git a/tests/crypto/random.c b/tests/crypto/random.c index d3625d3aab..68448a5f50 100644 --- a/tests/crypto/random.c +++ b/tests/crypto/random.c @@ -15,23 +15,6 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -// Copyright (c) 2012-2014, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - #include "crypto/random.c" #include "crypto-tests.h" diff --git a/tests/integration_test_lib/BaseFunctionalTest.cpp b/tests/integration_test_lib/BaseFunctionalTest.cpp index 83a33a5c48..96224f8767 100755 --- a/tests/integration_test_lib/BaseFunctionalTest.cpp +++ b/tests/integration_test_lib/BaseFunctionalTest.cpp @@ -321,33 +321,37 @@ bool BaseFunctionalTest::mineBlocks(TestNode& node, const CryptoNote::AccountPub return false; } - blockTemplate.timestamp = m_nextTimestamp; - m_nextTimestamp += 2 * m_currency.difficultyTarget(); + if (!prepareAndSubmitBlock(node, std::move(blockTemplate))) { + return false; + } + } - if (blockTemplate.majorVersion == BLOCK_MAJOR_VERSION_2) { - blockTemplate.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; - blockTemplate.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; - blockTemplate.parentBlock.numberOfTransactions = 1; + return true; +} - CryptoNote::tx_extra_merge_mining_tag mmTag; - mmTag.depth = 0; - if (!CryptoNote::get_aux_block_header_hash(blockTemplate, mmTag.merkle_root)) { - return false; - } +bool BaseFunctionalTest::prepareAndSubmitBlock(TestNode& node, CryptoNote::Block&& blockTemplate) { + blockTemplate.timestamp = m_nextTimestamp; + m_nextTimestamp += 2 * m_currency.difficultyTarget(); - blockTemplate.parentBlock.minerTx.extra.clear(); - if (!CryptoNote::append_mm_tag_to_extra(blockTemplate.parentBlock.minerTx.extra, mmTag)) { - return false; - } + if (blockTemplate.majorVersion == BLOCK_MAJOR_VERSION_2) { + blockTemplate.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + blockTemplate.parentBlock.minorVersion = BLOCK_MINOR_VERSION_0; + blockTemplate.parentBlock.numberOfTransactions = 1; + + CryptoNote::tx_extra_merge_mining_tag mmTag; + mmTag.depth = 0; + if (!CryptoNote::get_aux_block_header_hash(blockTemplate, mmTag.merkle_root)) { + return false; } - blobdata blockBlob = block_to_blob(blockTemplate); - if (!node.submitBlock(::Common::toHex(blockBlob.data(), blockBlob.size()))) { + blockTemplate.parentBlock.minerTx.extra.clear(); + if (!CryptoNote::append_mm_tag_to_extra(blockTemplate.parentBlock.minerTx.extra, mmTag)) { return false; } } - return true; + blobdata blockBlob = block_to_blob(blockTemplate); + return node.submitBlock(::Common::toHex(blockBlob.data(), blockBlob.size())); } bool BaseFunctionalTest::mineBlock(std::unique_ptr &wallet) { diff --git a/tests/integration_test_lib/BaseFunctionalTest.h b/tests/integration_test_lib/BaseFunctionalTest.h index ed5a25faf1..1402b84f75 100755 --- a/tests/integration_test_lib/BaseFunctionalTest.h +++ b/tests/integration_test_lib/BaseFunctionalTest.h @@ -163,6 +163,8 @@ namespace Tests { bool waitForPoolSize(size_t nodeIndex, CryptoNote::INode& node, size_t expectedPoolSize, std::vector& txPool); + bool prepareAndSubmitBlock(TestNode& node, CryptoNote::Block&& blockTemplate); + private: #ifdef __linux__ std::vector<__pid_t> pids; diff --git a/tests/integration_test_lib/CoreRpcSerialization.cpp b/tests/integration_test_lib/CoreRpcSerialization.cpp deleted file mode 100755 index 14bce8c2a1..0000000000 --- a/tests/integration_test_lib/CoreRpcSerialization.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "CoreRpcSerialization.h" - -namespace CryptoNote { - -void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - serializer(value.miner_address, "miner_address"); - serializer(value.threads_count, "threads_count"); - serializer.endObject(); -} - -void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - serializer(value.status, "status"); - serializer.endObject(); -} - -void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - serializer.endObject(); -} - -void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - serializer(value.status, "status"); - serializer.endObject(); -} - -void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - serializer.endObject(); -} - -void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, CryptoNote::ISerializer& serializer) { - serializer.beginObject(name); - serializer(value.status, "status"); - serializer.endObject(); -} - -} //namespace CryptoNote diff --git a/tests/integration_test_lib/CoreRpcSerialization.h b/tests/integration_test_lib/CoreRpcSerialization.h deleted file mode 100755 index ed4f92b8aa..0000000000 --- a/tests/integration_test_lib/CoreRpcSerialization.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include "../../src/serialization/ISerializer.h" -#include "../../src/rpc/core_rpc_server_commands_defs.h" - -namespace CryptoNote { - -void serialize(COMMAND_RPC_START_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(COMMAND_RPC_START_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer); - -void serialize(COMMAND_RPC_STOP_MINING::request& value, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(COMMAND_RPC_STOP_MINING::response& value, const std::string& name, CryptoNote::ISerializer& serializer); - -void serialize(COMMAND_RPC_STOP_DAEMON::request& value, const std::string& name, CryptoNote::ISerializer& serializer); -void serialize(COMMAND_RPC_STOP_DAEMON::response& value, const std::string& name, CryptoNote::ISerializer& serializer); - -} //namespace CryptoNote diff --git a/tests/integration_test_lib/TestNetwork.cpp b/tests/integration_test_lib/TestNetwork.cpp index 838da0e55f..65b953764f 100644 --- a/tests/integration_test_lib/TestNetwork.cpp +++ b/tests/integration_test_lib/TestNetwork.cpp @@ -17,6 +17,7 @@ #include "TestNetwork.h" +#include #include #include "InProcTestNode.h" diff --git a/tests/integration_tests/BlockchainInfo.h b/tests/integration_tests/BlockchainInfo.h index 2171d98275..219412f650 100644 --- a/tests/integration_tests/BlockchainInfo.h +++ b/tests/integration_tests/BlockchainInfo.h @@ -21,12 +21,13 @@ #include "cryptonote_core/cryptonote_serialization.h" namespace CryptoNote { - void serialize(BlockCompleteEntry& v, const std::string& name, ISerializer& s) { + bool serialize(BlockCompleteEntry& v, const Common::StringView& name, ISerializer& s) { s.beginObject(name); s(v.blockHash, "hash"); s.binary(v.block, "block"); s(v.txs, "transactions"); s.endObject(); + return true; } bool operator == (const BlockCompleteEntry& a, const BlockCompleteEntry& b) { @@ -44,11 +45,10 @@ namespace CryptoNote { return blocks == other.blocks && globalOutputs == other.globalOutputs; } - void serialize(ISerializer& s, const std::string& name) { - s.beginObject(name); + ISerializer& serialize(ISerializer& s) { s(blocks, "blocks"); s(globalOutputs, "outputs"); - s.endObject(); + return s; } }; diff --git a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp index c006ffe088..61353ea01b 100644 --- a/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp +++ b/tests/node_rpc_proxy_test/node_rpc_proxy_test.cpp @@ -18,80 +18,83 @@ #include #include -#include "include_base_utils.h" +#include +#include #include "node_rpc_proxy/NodeRpcProxy.h" using namespace CryptoNote; -using namespace CryptoNote; +using namespace Logging; +#undef ERROR class NodeObserver : public INodeObserver { public: - NodeObserver(const std::string& name, NodeRpcProxy& nodeProxy) + NodeObserver(const std::string& name, NodeRpcProxy& nodeProxy, ILogger& log) : m_name(name) - , m_nodeProxy(nodeProxy) { + , m_nodeProxy(nodeProxy) + , logger(log, "NodeObserver:" + name) { } virtual ~NodeObserver() { } virtual void peerCountUpdated(size_t count) { - LOG_PRINT_L0('[' << m_name << "] peerCountUpdated " << count << " = " << m_nodeProxy.getPeerCount()); + logger(INFO) << '[' << m_name << "] peerCountUpdated " << count << " = " << m_nodeProxy.getPeerCount(); } virtual void localBlockchainUpdated(uint64_t height) { - LOG_PRINT_L0('[' << m_name << "] localBlockchainUpdated " << height << " = " << m_nodeProxy.getLastLocalBlockHeight()); + logger(INFO) << '[' << m_name << "] localBlockchainUpdated " << height << " = " << m_nodeProxy.getLastLocalBlockHeight(); std::vector amounts; amounts.push_back(100000000); auto outs = std::make_shared>(); - m_nodeProxy.getRandomOutsByAmounts(std::move(amounts), 10, *outs.get(), [outs](std::error_code ec) { + m_nodeProxy.getRandomOutsByAmounts(std::move(amounts), 10, *outs.get(), [outs, this](std::error_code ec) { if (!ec) { if (1 == outs->size() && 10 == (*outs)[0].outs.size()) { - LOG_PRINT_L0("getRandomOutsByAmounts called successfully"); + logger(INFO) << "getRandomOutsByAmounts called successfully"; } else { - LOG_PRINT_RED_L0("getRandomOutsByAmounts returned invalid result"); + logger(ERROR) << "getRandomOutsByAmounts returned invalid result"; } } else { - LOG_PRINT_RED_L0("failed to call getRandomOutsByAmounts: " << ec.message() << ':' << ec.value()); + logger(ERROR) << "failed to call getRandomOutsByAmounts: " << ec.message() << ':' << ec.value(); } }); } virtual void lastKnownBlockHeightUpdated(uint64_t height) { - LOG_PRINT_L0('[' << m_name << "] lastKnownBlockHeightUpdated " << height << " = " << m_nodeProxy.getLastKnownBlockHeight()); + logger(INFO) << '[' << m_name << "] lastKnownBlockHeightUpdated " << height << " = " << m_nodeProxy.getLastKnownBlockHeight(); } private: + LoggerRef logger; std::string m_name; NodeRpcProxy& m_nodeProxy; }; int main(int argc, const char** argv) { - //set up logging options - epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); - epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + Logging::ConsoleLogger log; + Logging::LoggerRef logger(log, "main"); NodeRpcProxy nodeProxy("127.0.0.1", 18081); - NodeObserver observer1("obs1", nodeProxy); - NodeObserver observer2("obs2", nodeProxy); + NodeObserver observer1("obs1", nodeProxy, log); + NodeObserver observer2("obs2", nodeProxy, log); nodeProxy.addObserver(&observer1); nodeProxy.addObserver(&observer2); - nodeProxy.init([](std::error_code ec) { + nodeProxy.init([&](std::error_code ec) { if (ec) { - LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + logger(ERROR) << "init error: " << ec.message() << ':' << ec.value(); } else { - LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "initialized"; } }); //nodeProxy.init([](std::error_code ec) { // if (ec) { - // LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + // logger(ERROR) << "init error: " << ec.message() << ':' << ec.value(); // } else { // LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); // } @@ -99,49 +102,49 @@ int main(int argc, const char** argv) { std::this_thread::sleep_for(std::chrono::seconds(5)); if (nodeProxy.shutdown()) { - LOG_PRINT_GREEN("shutdown", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "shutdown"; } else { - LOG_PRINT_RED_L0("shutdown error"); + logger(ERROR) << "shutdown error"; } - nodeProxy.init([](std::error_code ec) { + nodeProxy.init([&](std::error_code ec) { if (ec) { - LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + logger(ERROR) << "init error: " << ec.message() << ':' << ec.value(); } else { - LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "initialized"; } }); std::this_thread::sleep_for(std::chrono::seconds(5)); if (nodeProxy.shutdown()) { - LOG_PRINT_GREEN("shutdown", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "shutdown"; } else { - LOG_PRINT_RED_L0("shutdown error"); + logger(ERROR) << "shutdown error"; } CryptoNote::Transaction tx; - nodeProxy.relayTransaction(tx, [](std::error_code ec) { + nodeProxy.relayTransaction(tx, [&](std::error_code ec) { if (!ec) { - LOG_PRINT_L0("relayTransaction called successfully"); + logger(INFO) << "relayTransaction called successfully"; } else { - LOG_PRINT_RED_L0("failed to call relayTransaction: " << ec.message() << ':' << ec.value()); + logger(ERROR) << "failed to call relayTransaction: " << ec.message() << ':' << ec.value(); } }); - nodeProxy.init([](std::error_code ec) { + nodeProxy.init([&](std::error_code ec) { if (ec) { - LOG_PRINT_RED_L0("init error: " << ec.message() << ':' << ec.value()); + logger(ERROR) << "init error: " << ec.message() << ':' << ec.value(); } else { - LOG_PRINT_GREEN("initialized", LOG_LEVEL_0); + logger(INFO, BRIGHT_GREEN) << "initialized"; } }); std::this_thread::sleep_for(std::chrono::seconds(5)); - nodeProxy.relayTransaction(tx, [](std::error_code ec) { + nodeProxy.relayTransaction(tx, [&](std::error_code ec) { if (!ec) { - LOG_PRINT_L0("relayTransaction called successfully"); + logger(INFO) << "relayTransaction called successfully"; } else { - LOG_PRINT_RED_L0("failed to call relayTransaction: " << ec.message() << ':' << ec.value()); + logger(ERROR) << "failed to call relayTransaction: " << ec.message() << ':' << ec.value(); } }); diff --git a/tests/transfers_tests/test_TxPoolSync.cpp b/tests/transfers_tests/test_TxPoolSync.cpp new file mode 100644 index 0000000000..691c5fb801 --- /dev/null +++ b/tests/transfers_tests/test_TxPoolSync.cpp @@ -0,0 +1,600 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include "cryptonote_core/account.h" +#include "cryptonote_core/CoreConfig.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_core/Currency.h" +#include "Logging/LoggerManager.h" +#include "p2p/NetNodeConfig.h" +#include "System/Dispatcher.h" +#include "System/InterruptedException.h" +#include "wallet/Wallet.h" + +#include "../integration_test_lib/BaseFunctionalTest.h" +#include "../integration_test_lib/TestWallet.h" + + +using namespace CryptoNote; + +extern System::Dispatcher globalSystem; + +namespace { + TransactionHash toTransactionHash(const crypto::hash& h) { + TransactionHash result; + std::copy(reinterpret_cast(&h), reinterpret_cast(&h) + sizeof(h), result.begin()); + return result; + } + + class NodeTxPoolSyncTest : public Tests::Common::BaseFunctionalTest, public ::testing::Test { + public: + NodeTxPoolSyncTest() : + BaseFunctionalTest(m_currency, globalSystem, Tests::Common::BaseFunctionalTestConfig()), + m_dispatcher(globalSystem), + m_currency(CurrencyBuilder(m_logManager).testnet(true).currency()) { + } + + protected: + Logging::LoggerManager m_logManager; + System::Dispatcher& m_dispatcher; + CryptoNote::Currency m_currency; + }; + + const std::string TEST_PASSWORD = "password"; + + class TestWallet : private IWalletObserver { + public: + TestWallet(System::Dispatcher& dispatcher, Currency& currency, INode& node) : + m_dispatcher(dispatcher), + m_synchronizationCompleted(dispatcher), + m_someTransactionUpdated(dispatcher), + m_currency(currency), + m_node(node), + m_wallet(new CryptoNote::Wallet(currency, node)), + m_currentHeight(0) { + m_wallet->addObserver(this); + } + + ~TestWallet() { + m_wallet->removeObserver(this); + } + + std::error_code init() { + CryptoNote::account_base walletAccount; + walletAccount.generate(); + + WalletAccountKeys walletKeys; + walletKeys.spendPublicKey = reinterpret_cast(walletAccount.get_keys().m_account_address.m_spendPublicKey); + walletKeys.spendSecretKey = reinterpret_cast(walletAccount.get_keys().m_spend_secret_key); + walletKeys.viewPublicKey = reinterpret_cast(walletAccount.get_keys().m_account_address.m_viewPublicKey); + walletKeys.viewSecretKey = reinterpret_cast(walletAccount.get_keys().m_view_secret_key); + + m_wallet->initWithKeys(walletKeys, TEST_PASSWORD); + m_synchronizationCompleted.wait(); + return m_lastSynchronizationResult; + } + + struct TransactionSendingWaiter : public IWalletObserver { + System::Dispatcher& m_dispatcher; + System::Event m_event; + bool m_waiting = false; + TransactionId m_expectedTxId; + std::error_code m_result; + + TransactionSendingWaiter(System::Dispatcher& dispatcher) : m_dispatcher(dispatcher), m_event(dispatcher) { + } + + void wait(TransactionId expectedTxId) { + m_waiting = true; + m_expectedTxId = expectedTxId; + m_event.wait(); + m_waiting = false; + } + + virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) { + m_dispatcher.remoteSpawn([this, transactionId, result]() { + if (m_waiting && m_expectedTxId == transactionId) { + m_result = result; + m_event.set(); + } + }); + } + }; + + std::error_code sendTransaction(const std::string& address, uint64_t amount, TransactionHash& txHash) { + TransactionSendingWaiter transactionSendingWaiter(m_dispatcher); + m_wallet->addObserver(&transactionSendingWaiter); + + Transfer transfer{ address, static_cast(amount) }; + auto txId = m_wallet->sendTransaction(transfer, m_currency.minimumFee()); + transactionSendingWaiter.wait(txId); + m_wallet->removeObserver(&transactionSendingWaiter); + // TODO workaround: make sure ObserverManager doesn't have local pointers to transactionSendingWaiter, so it can be destroyed + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // Run all spawned handlers from TransactionSendingWaiter::sendTransactionCompleted + m_dispatcher.yield(); + + TransactionInfo txInfo; + if (!m_wallet->getTransaction(txId, txInfo)) { + return std::make_error_code(std::errc::identifier_removed); + } + + txHash = txInfo.hash; + return transactionSendingWaiter.m_result; + } + + void waitForSynchronizationToHeight(uint32_t height) { + while (m_synchronizedHeight < height) { + m_synchronizationCompleted.wait(); + } + } + + IWallet* wallet() { + return m_wallet.get(); + } + + AccountPublicAddress address() const { + std::string addressString = m_wallet->getAddress(); + AccountPublicAddress address; + bool ok = m_currency.parseAccountAddressString(addressString, address); + assert(ok); + return address; + } + + protected: + virtual void synchronizationCompleted(std::error_code result) override { + m_dispatcher.remoteSpawn([this, result]() { + m_lastSynchronizationResult = result; + m_synchronizedHeight = m_currentHeight; + m_synchronizationCompleted.set(); + m_synchronizationCompleted.clear(); + }); + } + + virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override { + m_dispatcher.remoteSpawn([this, current]() { + m_currentHeight = static_cast(current); + }); + } + + private: + System::Dispatcher& m_dispatcher; + System::Event m_synchronizationCompleted; + System::Event m_someTransactionUpdated; + + INode& m_node; + Currency& m_currency; + std::unique_ptr m_wallet; + std::unique_ptr m_walletObserver; + uint32_t m_currentHeight; + uint32_t m_synchronizedHeight; + std::error_code m_lastSynchronizationResult; + }; + + TEST_F(NodeTxPoolSyncTest, TxPoolsAreRequestedRightAfterANodeIsConnectedToAnotherIfTheirBlockchainsAreSynchronized) { + //System::Timer timer(m_dispatcher); + //m_dispatcher.spawn([&m_dispatcher, &timer] { + // try { + // timer.sleep(std::chrono::minutes(5)); + // m_dispatcher. + // } catch (System::InterruptedException&) { + // } + //}); + + const size_t NODE_0 = 0; + const size_t NODE_1 = 1; + const size_t NODE_2 = 2; + const size_t NODE_3 = 3; + + launchTestnet(4, Tests::Common::BaseFunctionalTest::Line); + + std::unique_ptr node0; + std::unique_ptr node1; + std::unique_ptr node2; + std::unique_ptr node3; + + nodeDaemons[NODE_0]->makeINode(node0); + nodeDaemons[NODE_1]->makeINode(node1); + nodeDaemons[NODE_2]->makeINode(node2); + nodeDaemons[NODE_3]->makeINode(node3); + + CryptoNote::account_base minerAccount; + minerAccount.generate(); + + TestWallet wallet1(m_dispatcher, m_currency, *node1); + TestWallet wallet2(m_dispatcher, m_currency, *node2); + + ASSERT_FALSE(static_cast(wallet1.init())); + ASSERT_FALSE(static_cast(wallet2.init())); + + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet1.address(), 1)); + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet2.address(), 1)); + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], minerAccount.get_keys().m_account_address, m_currency.minedMoneyUnlockWindow())); + + wallet1.waitForSynchronizationToHeight(static_cast(m_currency.minedMoneyUnlockWindow()) + 3); + wallet2.waitForSynchronizationToHeight(static_cast(m_currency.minedMoneyUnlockWindow()) + 3); + + stopNode(NODE_2); + // To make sure new transaction won't be received by NODE_2 and NODE_3 + waitForPeerCount(*node1, 1); + + TransactionHash txHash1; + ASSERT_FALSE(static_cast(wallet1.sendTransaction(m_currency.accountAddressAsString(minerAccount), m_currency.coin(), txHash1))); + + stopNode(NODE_1); + // Don't start NODE_2, while NODE_1 doesn't close its connections + waitForPeerCount(*node0, 0); + + startNode(NODE_2); + waitDaemonReady(NODE_2); + waitForPeerCount(*node3, 1); + + TransactionHash txHash2; + ASSERT_FALSE(static_cast(wallet2.sendTransaction(m_currency.accountAddressAsString(minerAccount), m_currency.coin(), txHash2))); + + startNode(NODE_1); + waitDaemonReady(NODE_1); + + std::vector poolTxs1; + std::vector poolTxs2; + ASSERT_TRUE(waitForPoolSize(NODE_1, *node1, 2, poolTxs1)); + ASSERT_TRUE(waitForPoolSize(NODE_2, *node2, 2, poolTxs2)); + + //timer.stop(); + + std::vector poolTxsIds1; + std::vector poolTxsIds2; + + for (auto& tx : poolTxs1) { + TransactionHash txHash = toTransactionHash(CryptoNote::get_transaction_hash(tx)); + poolTxsIds1.emplace_back(std::move(txHash)); + } + for (auto& tx : poolTxs2) { + TransactionHash txHash = toTransactionHash(CryptoNote::get_transaction_hash(tx)); + poolTxsIds2.emplace_back(std::move(txHash)); + } + + ASSERT_TRUE(std::find(poolTxsIds1.begin(), poolTxsIds1.end(), txHash1) != poolTxsIds1.end()); + ASSERT_TRUE(std::find(poolTxsIds1.begin(), poolTxsIds1.end(), txHash2) != poolTxsIds1.end()); + + ASSERT_TRUE(std::find(poolTxsIds2.begin(), poolTxsIds2.end(), txHash1) != poolTxsIds2.end()); + ASSERT_TRUE(std::find(poolTxsIds2.begin(), poolTxsIds2.end(), txHash2) != poolTxsIds2.end()); + } + + TEST_F(NodeTxPoolSyncTest, TxPoolsAreRequestedRightAfterInitialBlockchainsSynchronization) { + //System::Timer timer(m_dispatcher); + //m_dispatcher.spawn([&m_dispatcher, &timer] { + // try { + // timer.sleep(std::chrono::minutes(5)); + // m_dispatcher. + // } catch (System::InterruptedException&) { + // } + //}); + + const size_t NODE_0 = 0; + const size_t NODE_1 = 1; + const size_t NODE_2 = 2; + const size_t NODE_3 = 3; + + launchTestnet(4, Tests::Common::BaseFunctionalTest::Line); + + std::unique_ptr node0; + std::unique_ptr node1; + std::unique_ptr node2; + std::unique_ptr node3; + + nodeDaemons[NODE_0]->makeINode(node0); + nodeDaemons[NODE_1]->makeINode(node1); + nodeDaemons[NODE_2]->makeINode(node2); + nodeDaemons[NODE_3]->makeINode(node3); + + CryptoNote::account_base minerAccount; + minerAccount.generate(); + + TestWallet wallet1(m_dispatcher, m_currency, *node1); + TestWallet wallet2(m_dispatcher, m_currency, *node2); + + ASSERT_FALSE(static_cast(wallet1.init())); + ASSERT_FALSE(static_cast(wallet2.init())); + + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet1.address(), 1)); + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet2.address(), 1)); + + wallet1.waitForSynchronizationToHeight(static_cast(3)); + wallet2.waitForSynchronizationToHeight(static_cast(3)); + + stopNode(NODE_2); + // To make sure new transaction won't be received by NODE_2 and NODE_3 + waitForPeerCount(*node1, 1); + + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], minerAccount.get_keys().m_account_address, m_currency.minedMoneyUnlockWindow())); + wallet1.waitForSynchronizationToHeight(static_cast(m_currency.minedMoneyUnlockWindow()) + 3); + + TransactionHash txHash1; + ASSERT_FALSE(static_cast(wallet1.sendTransaction(m_currency.accountAddressAsString(minerAccount), m_currency.coin(), txHash1))); + + stopNode(NODE_1); + // Don't start NODE_2, while NODE_1 doesn't close its connections + waitForPeerCount(*node0, 0); + + startNode(NODE_2); + waitDaemonReady(NODE_2); + waitForPeerCount(*node3, 1); + + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_3], minerAccount.get_keys().m_account_address, m_currency.minedMoneyUnlockWindow())); + wallet2.waitForSynchronizationToHeight(static_cast(m_currency.minedMoneyUnlockWindow()) + 3); + + TransactionHash txHash2; + ASSERT_FALSE(static_cast(wallet2.sendTransaction(m_currency.accountAddressAsString(minerAccount), m_currency.coin(), txHash2))); + + startNode(NODE_1); + waitDaemonReady(NODE_1); + + std::vector poolTxs1; + std::vector poolTxs2; + ASSERT_TRUE(waitForPoolSize(NODE_1, *node1, 2, poolTxs1)); + ASSERT_TRUE(waitForPoolSize(NODE_2, *node2, 2, poolTxs2)); + + //timer.stop(); + + std::vector poolTxsIds1; + std::vector poolTxsIds2; + + for (auto& tx : poolTxs1) { + TransactionHash txHash = toTransactionHash(CryptoNote::get_transaction_hash(tx)); + poolTxsIds1.emplace_back(std::move(txHash)); + } + for (auto& tx : poolTxs2) { + TransactionHash txHash = toTransactionHash(CryptoNote::get_transaction_hash(tx)); + poolTxsIds2.emplace_back(std::move(txHash)); + } + + ASSERT_TRUE(std::find(poolTxsIds1.begin(), poolTxsIds1.end(), txHash1) != poolTxsIds1.end()); + ASSERT_TRUE(std::find(poolTxsIds1.begin(), poolTxsIds1.end(), txHash2) != poolTxsIds1.end()); + + ASSERT_TRUE(std::find(poolTxsIds2.begin(), poolTxsIds2.end(), txHash1) != poolTxsIds2.end()); + ASSERT_TRUE(std::find(poolTxsIds2.begin(), poolTxsIds2.end(), txHash2) != poolTxsIds2.end()); + } + + TEST_F(NodeTxPoolSyncTest, TxPoolsAreRequestedRightAfterTimedBlockchainsSynchronization) { + //System::Timer timer(m_dispatcher); + //m_dispatcher.spawn([&m_dispatcher, &timer] { + // try { + // timer.sleep(std::chrono::minutes(5)); + // m_dispatcher. + // } catch (System::InterruptedException&) { + // } + //}); + + const size_t NODE_0 = 0; + const size_t NODE_1 = 1; + const size_t NODE_2 = 2; + const size_t NODE_3 = 3; + const size_t NODE_4 = 4; + + launchTestnet(5, Tests::Common::BaseFunctionalTest::Line); + + std::unique_ptr node0; + std::unique_ptr node1; + std::unique_ptr node2; + std::unique_ptr node3; + std::unique_ptr node4; + + nodeDaemons[NODE_0]->makeINode(node0); + nodeDaemons[NODE_1]->makeINode(node1); + nodeDaemons[NODE_2]->makeINode(node2); + nodeDaemons[NODE_3]->makeINode(node3); + nodeDaemons[NODE_4]->makeINode(node4); + + CryptoNote::account_base minerAccount; + minerAccount.generate(); + + TestWallet wallet1(m_dispatcher, m_currency, *node1); + ASSERT_FALSE(static_cast(wallet1.init())); + + stopNode(NODE_4); + waitForPeerCount(*node3, 1); + + stopNode(NODE_3); + waitForPeerCount(*node2, 1); + + stopNode(NODE_2); + waitForPeerCount(*node1, 1); + + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet1.address(), 1)); + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], minerAccount.get_keys().m_account_address, m_currency.minedMoneyUnlockWindow())); + wallet1.waitForSynchronizationToHeight(static_cast(m_currency.minedMoneyUnlockWindow()) + 2); + + TransactionHash txHash1; + ASSERT_FALSE(static_cast(wallet1.sendTransaction(m_currency.accountAddressAsString(minerAccount), m_currency.coin(), txHash1))); + + // Start nodes simultaneously due to them connect each other and decided that they are connected to network + startNode(NODE_4); + startNode(NODE_3); + waitDaemonReady(NODE_4); + waitDaemonReady(NODE_3); + waitForPeerCount(*node4, 1); + waitForPeerCount(*node3, 1); + + //std::this_thread::sleep_for(std::chrono::seconds(5)); + + startNode(NODE_2); + waitDaemonReady(NODE_2); + + // NODE_3 and NODE_4 are synchronized by timer + std::vector poolTxs2; + std::vector poolTxs3; + std::vector poolTxs4; + ASSERT_TRUE(waitForPoolSize(NODE_2, *node2, 1, poolTxs2)); + ASSERT_TRUE(waitForPoolSize(NODE_3, *node3, 1, poolTxs3)); + ASSERT_TRUE(waitForPoolSize(NODE_4, *node4, 1, poolTxs4)); + + //timer.stop(); + + TransactionHash poolTxId2 = toTransactionHash(CryptoNote::get_transaction_hash(poolTxs2.front())); + TransactionHash poolTxId3 = toTransactionHash(CryptoNote::get_transaction_hash(poolTxs3.front())); + TransactionHash poolTxId4 = toTransactionHash(CryptoNote::get_transaction_hash(poolTxs4.front())); + + ASSERT_EQ(txHash1, poolTxId2); + ASSERT_EQ(txHash1, poolTxId3); + ASSERT_EQ(txHash1, poolTxId4); + } + + TEST_F(NodeTxPoolSyncTest, TxPoolsAreRequestedRightAfterSwitchingToAlternativeChain) { + // If this condition isn't true, then test must be rewritten a bit + ASSERT_GT(m_currency.difficultyLag() + m_currency.difficultyCut(), m_currency.minedMoneyUnlockWindow()); + + //System::Timer timer(m_dispatcher); + //m_dispatcher.spawn([&m_dispatcher, &timer] { + // try { + // timer.sleep(std::chrono::minutes(5)); + // m_dispatcher. + // } catch (System::InterruptedException&) { + // } + //}); + + const size_t NODE_0 = 0; + const size_t NODE_1 = 1; + const size_t NODE_2 = 2; + const size_t NODE_3 = 3; + const size_t NODE_4 = 4; + const size_t NODE_5 = 5; + + launchTestnet(6, Tests::Common::BaseFunctionalTest::Line); + + std::unique_ptr node0; + std::unique_ptr node1; + std::unique_ptr node2; + std::unique_ptr node3; + std::unique_ptr node4; + std::unique_ptr node5; + + nodeDaemons[NODE_0]->makeINode(node0); + nodeDaemons[NODE_1]->makeINode(node1); + nodeDaemons[NODE_2]->makeINode(node2); + nodeDaemons[NODE_3]->makeINode(node3); + nodeDaemons[NODE_4]->makeINode(node4); + nodeDaemons[NODE_5]->makeINode(node5); + + TestWallet wallet0(m_dispatcher, m_currency, *node1); + TestWallet wallet1(m_dispatcher, m_currency, *node1); + TestWallet wallet2(m_dispatcher, m_currency, *node2); + TestWallet wallet5(m_dispatcher, m_currency, *node5); + + ASSERT_FALSE(static_cast(wallet0.init())); + ASSERT_FALSE(static_cast(wallet1.init())); + ASSERT_FALSE(static_cast(wallet2.init())); + ASSERT_FALSE(static_cast(wallet5.init())); + + uint32_t blockchainLenght = 1; + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet0.address(), m_currency.difficultyBlocksCount())); + blockchainLenght += static_cast(m_currency.difficultyBlocksCount()); + + wallet1.waitForSynchronizationToHeight(blockchainLenght); + wallet2.waitForSynchronizationToHeight(blockchainLenght); + wallet5.waitForSynchronizationToHeight(blockchainLenght); + + stopNode(NODE_2); + // To make sure new blocks won't be received by NODE_2 + waitForPeerCount(*node1, 1); + + // Generate alternative chain for NODE_1 + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet1.address(), 1)); + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_0], wallet2.address(), m_currency.minedMoneyUnlockWindow())); + blockchainLenght += 1 + static_cast(m_currency.minedMoneyUnlockWindow()); + + wallet1.waitForSynchronizationToHeight(blockchainLenght); + + // This transaction is valid in both alternative chains, it is just an indicator, that shows when NODE_1 and NODE_2 are synchronized + TransactionHash txHash0; + ASSERT_FALSE(static_cast(wallet0.sendTransaction(wallet0.wallet()->getAddress(), m_currency.coin(), txHash0))); + + // This transaction is valid only in alternative chain 1 + TransactionHash txHash1; + ASSERT_FALSE(static_cast(wallet1.sendTransaction(wallet0.wallet()->getAddress(), m_currency.coin(), txHash1))); + + stopNode(NODE_1); + // Don't start NODE_2, while NODE_1 doesn't close its connections + waitForPeerCount(*node0, 0); + + startNode(NODE_2); + waitDaemonReady(NODE_2); + waitForPeerCount(*node3, 1); + + // Generate alternative chain for NODE_2. + // After that it is expected that alternative chains 1 and 2 have the same difficulty, because + // m_currency.minedMoneyUnlockWindow() < m_currency.difficultyLag() + m_currency.difficultyCut() + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_5], wallet2.address(), 1)); + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_5], wallet1.address(), m_currency.minedMoneyUnlockWindow())); + + wallet2.waitForSynchronizationToHeight(blockchainLenght); + wallet5.waitForSynchronizationToHeight(blockchainLenght); + + // Tear connection between NODE_2 and nodes 4 and 5, in order to this nodes doesn't receive new transactions + stopNode(NODE_3); + waitForPeerCount(*node4, 1); + + // This transaction is valid only in alternative chain 2 + TransactionHash txHash2; + ASSERT_FALSE(static_cast(wallet2.sendTransaction(wallet0.wallet()->getAddress(), m_currency.coin(), txHash2))); + + startNode(NODE_1); + waitDaemonReady(NODE_1); + waitForPeerCount(*node2, 1); + + std::vector poolTxs2; + ASSERT_TRUE(waitForPoolSize(NODE_2, *node2, 2, poolTxs2)); + + // Now NODE_1 and NODE_2 are synchronized, but both are on its own alternative chains + // Add block to alternative chain 2, and wait for when NODE_1 switches to alternative chain 2. + ASSERT_TRUE(mineBlocks(*nodeDaemons[NODE_5], wallet1.address(), 1)); + blockchainLenght += 1; + + startNode(NODE_3); + waitDaemonReady(NODE_3); + waitForPeerCount(*node2, 2); + + wallet1.waitForSynchronizationToHeight(blockchainLenght); + wallet2.waitForSynchronizationToHeight(blockchainLenght); + + std::vector poolTxs1; + ASSERT_TRUE(waitForPoolSize(NODE_1, *node1, 2, poolTxs1)); + ASSERT_TRUE(waitForPoolSize(NODE_2, *node2, 2, poolTxs2)); + + //timer.stop(); + + std::vector poolTxsIds1; + std::vector poolTxsIds2; + + for (auto& tx : poolTxs1) { + TransactionHash txHash = toTransactionHash(CryptoNote::get_transaction_hash(tx)); + poolTxsIds1.emplace_back(std::move(txHash)); + } + for (auto& tx : poolTxs2) { + TransactionHash txHash = toTransactionHash(CryptoNote::get_transaction_hash(tx)); + poolTxsIds2.emplace_back(std::move(txHash)); + } + + ASSERT_TRUE(std::find(poolTxsIds1.begin(), poolTxsIds1.end(), txHash0) != poolTxsIds1.end()); + ASSERT_TRUE(std::find(poolTxsIds1.begin(), poolTxsIds1.end(), txHash2) != poolTxsIds1.end()); + + ASSERT_TRUE(std::find(poolTxsIds2.begin(), poolTxsIds2.end(), txHash0) != poolTxsIds2.end()); + ASSERT_TRUE(std::find(poolTxsIds2.begin(), poolTxsIds2.end(), txHash2) != poolTxsIds2.end()); + } +} diff --git a/tests/unit_tests/ICoreStub.cpp b/tests/unit_tests/ICoreStub.cpp index 7ef0890c7f..ddb05fd0d4 100755 --- a/tests/unit_tests/ICoreStub.cpp +++ b/tests/unit_tests/ICoreStub.cpp @@ -17,6 +17,8 @@ #include "ICoreStub.h" +#include "cryptonote_core/cryptonote_format_utils.h" + bool ICoreStub::addObserver(CryptoNote::ICoreObserver* observer) { return true; } @@ -77,17 +79,113 @@ void ICoreStub::set_random_outs(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS randomOutsResult = result; } -bool ICoreStub::getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) { +std::vector ICoreStub::getPoolTransactions() { + return std::vector(); +} + +bool ICoreStub::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { return true; } +void ICoreStub::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) { +} + bool ICoreStub::queryBlocks(const std::list& block_ids, uint64_t timestamp, uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) { //stub return true; } +crypto::hash ICoreStub::getBlockIdByHeight(uint64_t height) { + auto iter = blockHashByHeightIndex.find(height); + if (iter == blockHashByHeightIndex.end()) { + return boost::value_initialized(); + } + return iter->second; +} + bool ICoreStub::getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) { - //stub + auto iter = blocks.find(h); + if (iter == blocks.end()) { + return false; + } + blk = iter->second; + return true; +} + +void ICoreStub::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { + for (const crypto::hash& hash : txs_ids) { + auto iter = transactions.find(hash); + if (iter != transactions.end()) { + txs.push_back(iter->second); + } else { + missed_txs.push_back(hash); + } + } +} + +bool ICoreStub::getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) { + return true; +} + +bool ICoreStub::getBlockSize(const crypto::hash& hash, size_t& size) { + return true; +} + +bool ICoreStub::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { return true; } + +bool ICoreStub::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { + return true; +} + +bool ICoreStub::scanOutputkeysForIndices(const CryptoNote::TransactionInputToKey& txInToKey, std::list>& outputReferences) { + return true; +} + +bool ICoreStub::getBlockDifficulty(uint64_t height, CryptoNote::difficulty_type& difficulty) { + return true; +} + +bool ICoreStub::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { + auto iter = blockHashByTxHashIndex.find(txId); + if (iter == blockHashByTxHashIndex.end()) { + return false; + } + blockId = iter->second; + auto blockIter = blocks.find(blockId); + if (blockIter == blocks.end()) { + return false; + } + blockHeight = boost::get(blockIter->second.minerTx.vin.front()).height; + return true; +} + +bool ICoreStub::getMultisigOutputReference(const CryptoNote::TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { + return true; +} + +void ICoreStub::addBlock(const CryptoNote::Block& block) { + uint64_t height = boost::get(block.minerTx.vin.front()).height; + crypto::hash hash = CryptoNote::get_block_hash(block); + if (height > topHeight) { + topHeight = height; + topId = hash; + } + blocks.emplace(std::make_pair(hash, block)); + blockHashByHeightIndex.emplace(std::make_pair(height, hash)); + + blockHashByTxHashIndex.emplace(std::make_pair(CryptoNote::get_transaction_hash(block.minerTx), hash)); + for (auto txHash : block.txHashes) { + blockHashByTxHashIndex.emplace(std::make_pair(txHash, hash)); + } +} + +void ICoreStub::addTransaction(const CryptoNote::Transaction& tx) { + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + transactions.emplace(std::make_pair(hash, tx)); +} diff --git a/tests/unit_tests/ICoreStub.h b/tests/unit_tests/ICoreStub.h index 740f4d6c37..c3e72e3328 100644 --- a/tests/unit_tests/ICoreStub.h +++ b/tests/unit_tests/ICoreStub.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/ICore.h" @@ -40,12 +41,14 @@ class ICoreStub: public CryptoNote::ICore { virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); virtual CryptoNote::i_cryptonote_protocol* get_protocol(); virtual bool handle_incoming_tx(CryptoNote::blobdata const& tx_blob, CryptoNote::tx_verification_context& tvc, bool keeped_by_block); - virtual bool getPoolSymmetricDifference(const std::vector& known_pool_tx_ids, const crypto::hash& known_block_id, bool& isBcActual, std::vector& new_txs, std::vector& deleted_tx_ids) override; + virtual std::vector getPoolTransactions() override; + virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) override; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) override; virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); - virtual bool getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) override; - virtual bool have_block(const crypto::hash& id) override { return false; } virtual bool get_short_chain_history(std::list& ids) override { return false; } virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) override { return false; } @@ -57,10 +60,26 @@ class ICoreStub: public CryptoNote::ICore { virtual void on_synchronized() override {} virtual bool is_ready() override { return true; } + virtual crypto::hash getBlockIdByHeight(uint64_t height) override; + virtual bool getBlockByHash(const crypto::hash &h, CryptoNote::Block &blk) override; + virtual void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) override; + virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) override; + virtual bool getBlockSize(const crypto::hash& hash, size_t& size) override; + virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) override; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) override; + virtual bool scanOutputkeysForIndices(const CryptoNote::TransactionInputToKey& txInToKey, std::list>& outputReferences) override; + virtual bool getBlockDifficulty(uint64_t height, CryptoNote::difficulty_type& difficulty) override; + virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) override; + virtual bool getMultisigOutputReference(const CryptoNote::TransactionInputMultisignature& txInMultisig, std::pair& outputReference) override; + void set_blockchain_top(uint64_t height, const crypto::hash& top_id, bool result); void set_outputs_gindexs(const std::vector& indexs, bool result); void set_random_outs(const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& resp, bool result); + void addBlock(const CryptoNote::Block& block); + void addTransaction(const CryptoNote::Transaction& tx); + private: uint64_t topHeight; crypto::hash topId; @@ -71,4 +90,11 @@ class ICoreStub: public CryptoNote::ICore { CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response randomOuts; bool randomOutsResult; + + std::unordered_map blocks; + std::unordered_map blockHashByHeightIndex; + std::unordered_map blockHashByTxHashIndex; + + std::unordered_map transactions; + }; diff --git a/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp b/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp index b3c242fbe3..53aa40bffb 100644 --- a/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp +++ b/tests/unit_tests/ICryptonoteProtocolQueryStub.cpp @@ -33,6 +33,10 @@ size_t ICryptonoteProtocolQueryStub::getPeerCount() const { return peers; } +bool ICryptonoteProtocolQueryStub::isSynchronized() const { + return synchronized; +} + void ICryptonoteProtocolQueryStub::setPeerCount(uint32_t count) { peers = count; } @@ -40,3 +44,7 @@ void ICryptonoteProtocolQueryStub::setPeerCount(uint32_t count) { void ICryptonoteProtocolQueryStub::setObservedHeight(uint64_t height) { observedHeight = height; } + +void ICryptonoteProtocolQueryStub::setSynchronizedStatus(bool status) { + synchronized = status; +} diff --git a/tests/unit_tests/ICryptonoteProtocolQueryStub.h b/tests/unit_tests/ICryptonoteProtocolQueryStub.h index fbf55ff2b9..ed8b7742da 100644 --- a/tests/unit_tests/ICryptonoteProtocolQueryStub.h +++ b/tests/unit_tests/ICryptonoteProtocolQueryStub.h @@ -24,16 +24,22 @@ class ICryptonoteProtocolQueryStub: public CryptoNote::ICryptonoteProtocolQuery { public: - ICryptonoteProtocolQueryStub() : peers(0), observedHeight(0) {} + ICryptonoteProtocolQueryStub() : peers(0), observedHeight(0), synchronized(false) {} virtual bool addObserver(CryptoNote::ICryptonoteProtocolObserver* observer); virtual bool removeObserver(CryptoNote::ICryptonoteProtocolObserver* observer); virtual uint64_t getObservedHeight() const; virtual size_t getPeerCount() const; + virtual bool isSynchronized() const; + void setPeerCount(uint32_t count); void setObservedHeight(uint64_t height); + void setSynchronizedStatus(bool status); + private: size_t peers; uint64_t observedHeight; + + bool synchronized; }; diff --git a/tests/unit_tests/INodeStubs.cpp b/tests/unit_tests/INodeStubs.cpp index 87981944ee..24b0868276 100644 --- a/tests/unit_tests/INodeStubs.cpp +++ b/tests/unit_tests/INodeStubs.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include "crypto/crypto.h" namespace { @@ -290,3 +292,155 @@ void INodeTrivialRefreshStub::includeTransactionsFromPoolToBlock() { INodeTrivialRefreshStub::~INodeTrivialRefreshStub() { m_asyncCounter.waitAsyncContextsFinish(); } + +void INodeTrivialRefreshStub::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + m_asyncCounter.addAsyncContext(); + + std::thread task( + std::bind( + static_cast< + void(INodeTrivialRefreshStub::*)( + const std::vector&, + std::vector>&, + const Callback& + ) + >(&INodeTrivialRefreshStub::doGetBlocks), + this, + std::cref(blockHeights), + std::ref(blocks), + callback + ) + ); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + + for (const uint64_t& height: blockHeights) { + if (m_blockchainGenerator.getBlockchain().size() <= height) { + callback(std::error_code(EDOM, std::generic_category())); + return; + } + CryptoNote::BlockDetails b = CryptoNote::BlockDetails(); + b.height = height; + b.isOrphaned = false; + crypto::hash hash = CryptoNote::get_block_hash(m_blockchainGenerator.getBlockchain()[height]); + b.hash = reinterpret_cast&>(hash); + std::vector v; + v.push_back(b); + blocks.push_back(v); + } + + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + m_asyncCounter.addAsyncContext(); + + std::thread task( + std::bind( + static_cast< + void(INodeTrivialRefreshStub::*)( + const std::vector&, + std::vector&, + const Callback& + ) + >(&INodeTrivialRefreshStub::doGetBlocks), + this, + std::cref(blockHashes), + std::ref(blocks), + callback + ) + ); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + + for (const crypto::hash& hash: blockHashes) { + auto iter = std::find_if( + m_blockchainGenerator.getBlockchain().begin(), + m_blockchainGenerator.getBlockchain().end(), + [&hash](const CryptoNote::Block& block) -> bool { + return hash == CryptoNote::get_block_hash(block); + } + ); + if (iter == m_blockchainGenerator.getBlockchain().end()) { + callback(std::error_code(EDOM, std::generic_category())); + return; + } + CryptoNote::BlockDetails b = CryptoNote::BlockDetails(); + crypto::hash actualHash = CryptoNote::get_block_hash(*iter); + b.hash = reinterpret_cast&>(actualHash); + b.isOrphaned = false; + blocks.push_back(b); + } + + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + m_asyncCounter.addAsyncContext(); + + std::thread task( + std::bind( + &INodeTrivialRefreshStub::doGetTransactions, + this, + std::cref(transactionHashes), + std::ref(transactions), + callback + ) + ); + task.detach(); +} + +void INodeTrivialRefreshStub::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + ContextCounterHolder counterHolder(m_asyncCounter); + std::unique_lock lock(m_multiWalletLock); + + for (const crypto::hash& hash : transactionHashes) { + CryptoNote::Transaction tx; + CryptoNote::TransactionDetails txDetails = CryptoNote::TransactionDetails(); + if (m_blockchainGenerator.getTransactionByHash(hash, tx, false)) { + crypto::hash actualHash = CryptoNote::get_transaction_hash(tx); + txDetails.hash = reinterpret_cast&>(actualHash); + txDetails.inBlockchain = true; + } else if (m_blockchainGenerator.getTransactionByHash(hash, tx, true)) { + crypto::hash actualHash = CryptoNote::get_transaction_hash(tx); + txDetails.hash = reinterpret_cast&>(actualHash); + txDetails.inBlockchain = false; + } else { + callback(std::error_code(EDOM, std::generic_category())); + return; + } + + transactions.push_back(txDetails); + } + + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::isSynchronized(bool& syncStatus, const Callback& callback) { + //m_asyncCounter.addAsyncContext(); + syncStatus = m_synchronized; + callback(std::error_code()); +} + +void INodeTrivialRefreshStub::setSynchronizedStatus(bool status) { + m_synchronized = status; + if (m_synchronized) { + observerManager.notify(&CryptoNote::INodeObserver::blockchainSynchronized, getLastLocalBlockHeight()); + } +} + +void INodeTrivialRefreshStub::sendPoolChanged() { + observerManager.notify(&CryptoNote::INodeObserver::poolChanged); +} + +void INodeTrivialRefreshStub::sendLocalBlockchainUpdated(){ + observerManager.notify(&CryptoNote::INodeObserver::localBlockchainUpdated, getLastLocalBlockHeight()); +} diff --git a/tests/unit_tests/INodeStubs.h b/tests/unit_tests/INodeStubs.h index 03ed9cec8c..a55f65fdaf 100644 --- a/tests/unit_tests/INodeStubs.h +++ b/tests/unit_tests/INodeStubs.h @@ -51,6 +51,11 @@ class INodeDummyStub : public CryptoNote::INode virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override { is_bc_actual = true; callback(std::error_code()); }; virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { callback(std::error_code()); }; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { callback(std::error_code()); }; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { callback(std::error_code()); }; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { callback(std::error_code()); }; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) { callback(std::error_code()); }; + void updateObservers(); tools::ObserverManager observerManager; @@ -61,7 +66,7 @@ class INodeTrivialRefreshStub : public INodeDummyStub { public: INodeTrivialRefreshStub(TestBlockchainGenerator& generator) : - m_lastHeight(1), m_blockchainGenerator(generator), m_nextTxError(false), m_getMaxBlocks(std::numeric_limits::max()), m_nextTxToPool(false) {}; + m_lastHeight(1), m_blockchainGenerator(generator), m_nextTxError(false), m_getMaxBlocks(std::numeric_limits::max()), m_nextTxToPool(false), m_synchronized(false) {}; void setGetNewBlocksLimit(size_t maxBlocks) { m_getMaxBlocks = maxBlocks; } @@ -77,11 +82,21 @@ class INodeTrivialRefreshStub : public INodeDummyStub virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; + virtual void startAlternativeChain(uint64_t height); void setNextTransactionError(); void setNextTransactionToPool(); void includeTransactionsFromPoolToBlock(); + void setSynchronizedStatus(bool status); + + void sendPoolChanged(); + void sendLocalBlockchainUpdated(); + std::vector calls_getTransactionOutsGlobalIndices; virtual ~INodeTrivialRefreshStub(); @@ -95,6 +110,10 @@ class INodeTrivialRefreshStub : public INodeDummyStub void doGetPoolSymmetricDifference(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback); + void doGetBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback); + void doGetBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback); + void doGetTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback); + size_t m_getMaxBlocks; uint64_t m_lastHeight; TestBlockchainGenerator& m_blockchainGenerator; @@ -102,4 +121,6 @@ class INodeTrivialRefreshStub : public INodeDummyStub bool m_nextTxToPool; std::mutex m_multiWalletLock; CryptoNote::WalletAsyncContextCounter m_asyncCounter; + + bool m_synchronized; }; diff --git a/tests/unit_tests/TransactionApi.cpp b/tests/unit_tests/TransactionApi.cpp index 12e53599e8..2948acc90b 100644 --- a/tests/unit_tests/TransactionApi.cpp +++ b/tests/unit_tests/TransactionApi.cpp @@ -16,7 +16,10 @@ // along with Bytecoin. If not, see . #include "gtest/gtest.h" + +#include #include + #include "cryptonote_core/TransactionApi.h" #include "cryptonote_core/cryptonote_format_utils.h" // TODO: delete #include "cryptonote_core/account.h" @@ -294,6 +297,27 @@ TEST_F(TransactionApi, setExtraNonce) { ASSERT_EQ(extraNonce, s); } +TEST_F(TransactionApi, appendExtra) { + Blob blob; + + blob.resize(100); + std::iota(blob.begin(), blob.end(), 0); + + auto tx = createTransaction(); + + auto extra = tx->getExtra(); + + ASSERT_FALSE(extra.empty()); + + tx->appendExtra(blob); + + auto newExtra = tx->getExtra(); + + ASSERT_EQ(blob.size() + extra.size(), newExtra.size()); + ASSERT_EQ(0, memcmp(newExtra.data() + extra.size(), blob.data(), blob.size())); +} + + TEST_F(TransactionApi, doubleSpendInTransactionKey) { TransactionTypes::InputKeyInfo info = createInputInfo(1000); diff --git a/tests/unit_tests/binary_serialization_compatibility.cpp b/tests/unit_tests/binary_serialization_compatibility.cpp index c8d267edbf..a0d7f389b9 100644 --- a/tests/unit_tests/binary_serialization_compatibility.cpp +++ b/tests/unit_tests/binary_serialization_compatibility.cpp @@ -30,7 +30,6 @@ #include "serialization_structs_comparators.h" #include -#include "string_tools.h" template void checkEqualBinary(Struct& original) { @@ -54,8 +53,6 @@ void checkEnumeratorToLegacy(Struct& original) { CryptoNote::BinaryOutputStreamSerializer binarySerializer(archive); binarySerializer(original, ""); - //std::cout << "enumerated string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; - Struct restored; binary_archive ba(archive); bool r = ::serialization::serialize(ba, restored); @@ -72,8 +69,6 @@ void checkLegacyToEnumerator(Struct& original) { bool r = ::serialization::serialize(ba, original); ASSERT_TRUE(r); - //std::cout << "legacy string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; - Struct restored; CryptoNote::BinaryInputStreamSerializer binarySerializer(archive); @@ -463,8 +458,6 @@ void checkEnumeratorToEnumerator(CryptoNote::Block& block) { CryptoNote::Block restoredBlock; -// std::cout << "enumerated string: " << epee::string_tools::buff_to_hex_nodelimer(archive.str()) << std::endl; - CryptoNote::BinaryInputStreamSerializer input(archive); input(restoredBlock, ""); diff --git a/tests/unit_tests/get_xtype_from_string.cpp b/tests/unit_tests/get_xtype_from_string.cpp deleted file mode 100644 index 1b8e5dd933..0000000000 --- a/tests/unit_tests/get_xtype_from_string.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "gtest/gtest.h" - -#include - -// epee -#include - -using namespace epee::string_tools; - -namespace -{ - template - void do_pos_test(T expected, const std::string& str) - { - T val; - ASSERT_TRUE(get_xtype_from_string(val, str)); - ASSERT_EQ(expected, val); - } - - template - void do_neg_test(const std::string& str) - { - T val; - ASSERT_FALSE(get_xtype_from_string(val, str)); - } -} - -#define TEST_pos(int_type, expected, str) \ - TEST(get_xtype_from_string, handles_pos_ ## int_type ## _ ## expected) \ - { \ - do_pos_test(expected, str); \ - } - -#define DO_MAKE_NEG_TEST_NAME(prefix, int_type, ln) prefix ## int_type ## _ ## ln -#define MAKE_NEG_TEST_NAME(prefix, int_type, ln) DO_MAKE_NEG_TEST_NAME(prefix, int_type, ln) - -#define TEST_neg(int_type, str) \ - TEST(get_xtype_from_string, MAKE_NEG_TEST_NAME(handles_neg, int_type, __LINE__)) \ - { \ - do_neg_test(str); \ - } - -TEST_pos(uint16_t, 0, "0"); -TEST_pos(uint16_t, 1, "1"); -TEST_pos(uint16_t, 65535, "65535"); - -TEST_neg(uint16_t, ""); -TEST_neg(uint16_t, "+0"); -TEST_neg(uint16_t, "+1"); -TEST_neg(uint16_t, "+65535"); -TEST_neg(uint16_t, "+65536"); - -TEST_neg(uint16_t, "-0"); -TEST_neg(uint16_t, "-1"); -TEST_neg(uint16_t, "-65535"); -TEST_neg(uint16_t, "-65536"); - -TEST_neg(uint16_t, ".0"); -TEST_neg(uint16_t, ".1"); -TEST_neg(uint16_t, "0.0"); -TEST_neg(uint16_t, "0.1"); -TEST_neg(uint16_t, "1.0"); -TEST_neg(uint16_t, "1.1"); - -TEST_neg(uint16_t, "w"); -TEST_neg(uint16_t, "0w"); -TEST_neg(uint16_t, "1w"); -TEST_neg(uint16_t, "1w1"); -TEST_neg(uint16_t, "65535w"); - -TEST_neg(uint16_t, "65536"); -TEST_neg(uint16_t, "4294967296"); -TEST_neg(uint16_t, "18446744073709551616"); - - -TEST_pos(uint32_t, 0, "0"); -TEST_pos(uint32_t, 1, "1"); -TEST_pos(uint32_t, 4294967295, "4294967295"); - -TEST_neg(uint32_t, ""); -TEST_neg(uint32_t, "+0"); -TEST_neg(uint32_t, "+1"); -TEST_neg(uint32_t, "+4294967295"); -TEST_neg(uint32_t, "+4294967296"); - -TEST_neg(uint32_t, "-0"); -TEST_neg(uint32_t, "-1"); -TEST_neg(uint32_t, "-4294967295"); -TEST_neg(uint32_t, "-4294967296"); - -TEST_neg(uint32_t, ".0"); -TEST_neg(uint32_t, ".1"); -TEST_neg(uint32_t, "0.0"); -TEST_neg(uint32_t, "0.1"); -TEST_neg(uint32_t, "1.0"); -TEST_neg(uint32_t, "1.1"); - -TEST_neg(uint32_t, "w"); -TEST_neg(uint32_t, "0w"); -TEST_neg(uint32_t, "1w"); -TEST_neg(uint32_t, "1w1"); -TEST_neg(uint32_t, "4294967295w"); - -TEST_neg(uint32_t, "4294967296"); -TEST_neg(uint32_t, "18446744073709551616"); - -TEST_pos(uint64_t, 0, "0"); -TEST_pos(uint64_t, 1, "1"); -TEST_pos(uint64_t, 18446744073709551615ULL, "18446744073709551615"); - -TEST_neg(uint64_t, ""); -TEST_neg(uint64_t, "+0"); -TEST_neg(uint64_t, "+1"); -TEST_neg(uint64_t, "+18446744073709551615"); -TEST_neg(uint64_t, "+18446744073709551616"); - -TEST_neg(uint64_t, "-0"); -TEST_neg(uint64_t, "-1"); -TEST_neg(uint64_t, "-18446744073709551615"); -TEST_neg(uint64_t, "-18446744073709551616"); - -TEST_neg(uint64_t, ".0"); -TEST_neg(uint64_t, ".1"); -TEST_neg(uint64_t, "0.0"); -TEST_neg(uint64_t, "0.1"); -TEST_neg(uint64_t, "1.0"); -TEST_neg(uint64_t, "1.1"); - -TEST_neg(uint64_t, "w"); -TEST_neg(uint64_t, "0w"); -TEST_neg(uint64_t, "1w"); -TEST_neg(uint64_t, "1w1"); -TEST_neg(uint64_t, "18446744073709551615w"); - -TEST_neg(uint64_t, "18446744073709551616"); diff --git a/tests/unit_tests/main.cpp b/tests/unit_tests/main.cpp index a9e2e005b7..b42fde001f 100644 --- a/tests/unit_tests/main.cpp +++ b/tests/unit_tests/main.cpp @@ -19,11 +19,6 @@ int main(int argc, char** argv) { - // epee::debug::get_set_enable_assert(true, false); - //set up logging options - // epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); - // epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); - ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit_tests/serialization_kv.cpp b/tests/unit_tests/serialization_kv.cpp index 7cb8595680..39d779f7c7 100644 --- a/tests/unit_tests/serialization_kv.cpp +++ b/tests/unit_tests/serialization_kv.cpp @@ -21,12 +21,8 @@ #include "serialization/KVBinaryInputStreamSerializer.h" #include "serialization/KVBinaryOutputStreamSerializer.h" - -#include "serialization/keyvalue_serialization.h" -#include "serialization/keyvalue_serialization_overloads.h" -#include "storages/portable_storage.h" -#include "storages/portable_storage_from_bin.h" -#include "storages/portable_storage_template_helper.h" +#include "serialization/SerializationOverloads.h" +#include "serialization/SerializationTools.h" #include @@ -34,28 +30,6 @@ using namespace CryptoNote; namespace CryptoNote { - -template -void serializeAsPod(Cont& cont, const std::string& name, ISerializer& s) { - - typedef typename Cont::value_type ElementType; - const size_t elementSize = sizeof(ElementType); - std::string buf; - - if (s.type() == ISerializer::INPUT) { - s.binary(buf, name); - const ElementType* ptr = reinterpret_cast(buf.data()); - size_t count = buf.size() / elementSize; - cont.insert(cont.begin(), ptr, ptr + count); - } else { - auto rawSize = cont.size() * elementSize; - auto ptr = reinterpret_cast(cont.data()); - buf.assign(ptr, ptr + rawSize); - s.binary(buf, name); - } -} - - struct TestElement { std::string name; uint32_t nonce; @@ -70,21 +44,11 @@ struct TestElement { u32array == other.u32array; } - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(name) - KV_SERIALIZE(nonce) - KV_SERIALIZE_VAL_POD_AS_BLOB(blob) - KV_SERIALIZE_CONTAINER_POD_AS_BLOB(u32array) - END_KV_SERIALIZE_MAP() - - - void serialize(ISerializer& s, const std::string& nm) { - s.beginObject(nm); + void serialize(ISerializer& s) { s(name, "name"); s(nonce, "nonce"); s.binary(blob.data(), blob.size(), "blob"); - serializeAsPod(u32array, "u32array", s); - s.endObject(); + serializeAsBinary(u32array, "u32array", s); } }; @@ -106,24 +70,13 @@ struct TestStruct { vec2 == other.vec2; } - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(root) - KV_SERIALIZE(vec1) - KV_SERIALIZE(vec2) - KV_SERIALIZE(u8) - KV_SERIALIZE(u32) - KV_SERIALIZE(u64) - END_KV_SERIALIZE_MAP() - - void serialize(ISerializer& s, const std::string& name) { - s.beginObject(name); + void serialize(ISerializer& s) { s(root, "root"); s(vec1, "vec1"); s(vec2, "vec2"); s(u8, "u8"); s(u32, "u32"); s(u64, "u64"); - s.endObject(); } }; @@ -150,7 +103,6 @@ class HiResTimer { TEST(KVSerialize, Simple) { TestElement testData1, testData2; - std::string buf; testData1.name = "hello"; testData1.nonce = 12345; @@ -159,82 +111,8 @@ TEST(KVSerialize, Simple) { testData2.name = "bye"; testData2.nonce = 54321; - epee::serialization::store_t_to_binary(testData1, buf); - - std::stringstream s(buf); - KVBinaryInputStreamSerializer kvInput(s); - kvInput.parse(); - kvInput(testData2, ""); - + std::string buf = CryptoNote::storeToBinaryKeyValue(testData1); + ASSERT_TRUE(CryptoNote::loadFromBinaryKeyValue(testData2, buf)); EXPECT_EQ(testData1, testData2); } - -TEST(KVSerialize, NewWriterOldReader) { - std::string bufOld, bufNew; - TestStruct s1; - TestStruct s2; - - s1.u64 = 0xffULL << 50; - s1.vec1.resize(37); - s1.root.name = "somename"; - s1.root.u32array.resize(128); - - s2.u64 = 13; - s2.vec2.resize(10); - - { - HiResTimer t; - epee::serialization::store_t_to_binary(s1, bufOld); - std::cout << "Old serialization: " << t.duration().count() << std::endl; - } - - { - HiResTimer t; - - KVBinaryOutputStreamSerializer kvOut; - kvOut(s1, ""); - std::stringstream out; - kvOut.write(out); - bufNew = out.str(); - - std::cout << "New serialization: " << t.duration().count() << std::endl; - } - - { - HiResTimer t; - TestStruct outStruct(s2); - - std::stringstream s(bufOld); - KVBinaryInputStreamSerializer kvInput(s); - kvInput.parse(); - kvInput(outStruct, ""); - - std::cout << "New deserialization: " << t.duration().count() << std::endl; - - EXPECT_EQ(s1, outStruct); - } - - - { - HiResTimer t; - TestStruct outStruct(s2); - - bool parseOld = epee::serialization::load_t_from_binary(outStruct, bufOld); - - ASSERT_TRUE(parseOld); - - std::cout << "Old deserialization: " << t.duration().count() << std::endl; - - EXPECT_EQ(s1, outStruct); - } - - { - TestStruct outStruct(s2); - bool parseNew = epee::serialization::load_t_from_binary(outStruct, bufNew); - ASSERT_TRUE(parseNew); - EXPECT_EQ(s1, outStruct); - } - - -} diff --git a/tests/unit_tests/test_BlockchainExplorer.cpp b/tests/unit_tests/test_BlockchainExplorer.cpp new file mode 100644 index 0000000000..79046597a8 --- /dev/null +++ b/tests/unit_tests/test_BlockchainExplorer.cpp @@ -0,0 +1,922 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" + +#include + +#include + +#include "EventWaiter.h" +#include "ICoreStub.h" +#include "ICryptonoteProtocolQueryStub.h" +#include "INodeStubs.h" +#include "cryptonote_core/TransactionApi.h" +#include "TestBlockchainGenerator.h" + +#include "Logging/FileLogger.h" + +#include "BlockchainExplorer/BlockchainExplorer.h" + +namespace { +CryptoNote::Transaction createTx(CryptoNote::ITransactionReader& tx) { + auto data = tx.getTransactionData(); + + CryptoNote::blobdata txblob(data.data(), data.data() + data.size()); + CryptoNote::Transaction outTx; + CryptoNote::parse_and_validate_tx_from_blob(txblob, outTx); + + return outTx; +} +} + +struct CallbackStatus { + CallbackStatus() {} + + bool wait() { return waiter.wait_for(std::chrono::milliseconds(3000)); } + bool ok() { return waiter.wait_for(std::chrono::milliseconds(3000)) && !static_cast(code); } + void setStatus(const std::error_code& ec) { code = ec; waiter.notify(); } + std::error_code getStatus() const { return code; } + + std::error_code code; + EventWaiter waiter; +}; + +class dummyObserver : public CryptoNote::IBlockchainObserver { +public: + virtual ~dummyObserver() {} +}; + +class smartObserver : public CryptoNote::IBlockchainObserver { +public: + virtual ~smartObserver() {} + + virtual void blockchainUpdated(const std::vector& newBlocks, const std::vector& orphanedBlocks) override { + blockchainUpdatedCallback(newBlocks, orphanedBlocks); + } + + virtual void poolUpdated(const std::vector& newTransactions, const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) override { + poolUpdatedCallback(newTransactions, removedTransactions); + } + + virtual void blockchainSynchronized(const CryptoNote::BlockDetails& topBlock) override { + blockchainSynchronizedCallback(topBlock); + } + + void setCallback(const std::function& newBlocks, const std::vector& orphanedBlocks)>& cb) { + blockchainUpdatedCallback = cb; + } + + void setCallback(const std::function& newTransactions, const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions)>& cb) { + poolUpdatedCallback = cb; + } + + void setCallback(std::function& cb) { + blockchainSynchronizedCallback = cb; + } + +private: + std::function& newBlocks, const std::vector& orphanedBlocks)> blockchainUpdatedCallback; + std::function& newTransactions, const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions)> poolUpdatedCallback; + std::function blockchainSynchronizedCallback; +}; + +class BlockchainExplorer : public ::testing::Test { +public: + BlockchainExplorer() : + currency(CryptoNote::CurrencyBuilder(logger).currency()), + generator(currency), + nodeStub(generator), + blockchainExplorer(nodeStub, logger) {} + void SetUp(); + void TearDown(); + +protected: + CryptoNote::Currency currency; + TestBlockchainGenerator generator; + INodeTrivialRefreshStub nodeStub; + Logging::FileLogger logger; + dummyObserver observer; + CryptoNote::BlockchainExplorer blockchainExplorer; +}; + +void BlockchainExplorer::SetUp() { + logger.init("/dev/null"); + ASSERT_NO_THROW(blockchainExplorer.init()); +} + +void BlockchainExplorer::TearDown() { + ASSERT_NO_THROW(blockchainExplorer.shutdown()); +} + +TEST_F(BlockchainExplorer, initOk) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + + ASSERT_NO_THROW(newExplorer.init()); +} + +TEST_F(BlockchainExplorer, shutdownOk) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_NO_THROW(newExplorer.init()); + ASSERT_NO_THROW(newExplorer.shutdown()); +} + +TEST_F(BlockchainExplorer, doubleInit) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_NO_THROW(newExplorer.init()); + ASSERT_ANY_THROW(newExplorer.init()); +} + +TEST_F(BlockchainExplorer, shutdownNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_ANY_THROW(newExplorer.shutdown()); +} + +TEST_F(BlockchainExplorer, addObserver) { + ASSERT_TRUE(blockchainExplorer.addObserver(&observer)); +} + +TEST_F(BlockchainExplorer, addObserverNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_ANY_THROW(newExplorer.addObserver(&observer)); +} + +TEST_F(BlockchainExplorer, removeObserver) { + ASSERT_TRUE(blockchainExplorer.addObserver(&observer)); + ASSERT_TRUE(blockchainExplorer.removeObserver(&observer)); +} + +TEST_F(BlockchainExplorer, removeObserverNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_ANY_THROW(newExplorer.addObserver(&observer)); + ASSERT_ANY_THROW(newExplorer.removeObserver(&observer)); +} + +TEST_F(BlockchainExplorer, removeObserverNotAdded) { + ASSERT_FALSE(blockchainExplorer.removeObserver(&observer)); +} + +TEST_F(BlockchainExplorer, getBlocksByHeightGenesis) { + std::vector blockHeights; + blockHeights.push_back(0); + std::vector> blocks; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlocks(blockHeights, blocks)); + ASSERT_EQ(blocks.size(), 1); + EXPECT_EQ(blockHeights.size(), blocks.size()); + ASSERT_EQ(blocks.front().size(), 1); + EXPECT_EQ(blocks.front().front().height, 0); + EXPECT_FALSE(blocks.front().front().isOrphaned); +} + +TEST_F(BlockchainExplorer, getBlocksByHeightMany) { + const size_t NUMBER_OF_BLOCKS = 10; + std::vector blockHeights; + for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) { + blockHeights.push_back(i); + } + std::vector> blocks; + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_GE(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + + ASSERT_TRUE(blockchainExplorer.getBlocks(blockHeights, blocks)); + EXPECT_EQ(blocks.size(), NUMBER_OF_BLOCKS); + ASSERT_EQ(blockHeights.size(), blocks.size()); + + auto range = boost::combine(blockHeights, blocks); + for (const boost::tuple>& sameHeight : range) { + EXPECT_EQ(sameHeight.get<1>().size(), 1); + for (const CryptoNote::BlockDetails& block : sameHeight.get<1>()) { + EXPECT_EQ(block.height, sameHeight.get<0>()); + EXPECT_FALSE(block.isOrphaned); + } + } +} + +TEST_F(BlockchainExplorer, getBlocksByHeightFail) { + const size_t NUMBER_OF_BLOCKS = 10; + std::vector blockHeights; + for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) { + blockHeights.push_back(i); + } + std::vector> blocks; + + EXPECT_LT(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + + ASSERT_ANY_THROW(blockchainExplorer.getBlocks(blockHeights, blocks)); +} + +TEST_F(BlockchainExplorer, getBlocksByHeightNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + std::vector blockHeights; + blockHeights.push_back(0); + std::vector> blocks; + ASSERT_ANY_THROW(newExplorer.getBlocks(blockHeights, blocks)); +} + +TEST_F(BlockchainExplorer, getBlocksByHashGenesis) { + std::vector> blockHashes; + ASSERT_GE(generator.getBlockchain().size(), 1); + + crypto::hash genesisHash = CryptoNote::get_block_hash(generator.getBlockchain().front()); + blockHashes.push_back(reinterpret_cast&>(genesisHash)); + std::vector blocks; + + ASSERT_TRUE(blockchainExplorer.getBlocks(blockHashes, blocks)); + ASSERT_EQ(blocks.size(), 1); + EXPECT_EQ(blockHashes.size(), blocks.size()); + + std::array expectedHash = reinterpret_cast&>(genesisHash); + EXPECT_EQ(blocks.front().hash, expectedHash); + EXPECT_EQ(blocks.front().hash, blockHashes.front()); + EXPECT_FALSE(blocks.front().isOrphaned); +} + +TEST_F(BlockchainExplorer, getBlocksByHashMany) { + const size_t NUMBER_OF_BLOCKS = 10; + std::vector> blockHashes; + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_GE(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + + for (const auto& block : generator.getBlockchain()) { + if (blockHashes.size() == NUMBER_OF_BLOCKS) { + break; + } + crypto::hash hash = CryptoNote::get_block_hash(block); + blockHashes.push_back(reinterpret_cast&>(hash)); + } + + std::vector blocks; + + ASSERT_TRUE(blockchainExplorer.getBlocks(blockHashes, blocks)); + EXPECT_EQ(blocks.size(), NUMBER_OF_BLOCKS); + ASSERT_EQ(blockHashes.size(), blocks.size()); + + auto range = boost::combine(blockHashes, blocks); + for (const boost::tuple, CryptoNote::BlockDetails>& hashWithBlock : range) { + EXPECT_EQ(hashWithBlock.get<0>(), hashWithBlock.get<1>().hash); + EXPECT_FALSE(hashWithBlock.get<1>().isOrphaned); + } +} + +TEST_F(BlockchainExplorer, getBlocksByHashFail) { + const size_t NUMBER_OF_BLOCKS = 10; + std::vector> blockHashes; + + for (size_t i = 0; i < NUMBER_OF_BLOCKS; ++i) { + blockHashes.push_back(boost::value_initialized>()); + } + + std::vector blocks; + + EXPECT_LT(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + ASSERT_ANY_THROW(blockchainExplorer.getBlocks(blockHashes, blocks)); + +} + +TEST_F(BlockchainExplorer, getBlocksByHashNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + std::vector> blockHashes; + crypto::hash genesisHash = CryptoNote::get_block_hash(generator.getBlockchain().front()); + blockHashes.push_back(reinterpret_cast&>(genesisHash)); + std::vector blocks; + ASSERT_ANY_THROW(newExplorer.getBlocks(blockHashes, blocks)); +} + +TEST_F(BlockchainExplorer, getBlockchainTop) { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); +} + +TEST_F(BlockchainExplorer, getBlockchainTopNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_ANY_THROW(newExplorer.getBlockchainTop(topBlock)); +} + +TEST_F(BlockchainExplorer, getTransactionFromBlockchain) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + generator.addTxToBlockchain(tx); + + ASSERT_GE(generator.getBlockchain().size(), 1); + + std::vector> transactionHashes; + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + transactionHashes.push_back(reinterpret_cast&>(hash)); + + std::vector transactions; + + ASSERT_TRUE(blockchainExplorer.getTransactions(transactionHashes, transactions)); + ASSERT_EQ(transactions.size(), 1); + EXPECT_EQ(transactions.size(), transactionHashes.size()); + + EXPECT_EQ(transactions.front().hash, transactionHashes.front()); + EXPECT_TRUE(transactions.front().inBlockchain); +} + +TEST_F(BlockchainExplorer, getTransactionFromPool) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + generator.putTxToPool(tx); + + ASSERT_GE(generator.getBlockchain().size(), 1); + + std::vector> transactionHashes; + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + transactionHashes.push_back(reinterpret_cast&>(hash)); + + std::vector transactions; + + ASSERT_TRUE(blockchainExplorer.getTransactions(transactionHashes, transactions)); + ASSERT_EQ(transactions.size(), 1); + EXPECT_EQ(transactions.size(), transactionHashes.size()); + + EXPECT_EQ(transactions.front().hash, transactionHashes.front()); + EXPECT_FALSE(transactions.front().inBlockchain); +} + +TEST_F(BlockchainExplorer, getTransactionsMany) { + size_t POOL_TX_NUMBER = 10; + size_t BLOCKCHAIN_TX_NUMBER = 10; + std::vector> poolTxs; + std::vector> blockchainTxs; + + for (size_t i = 0; i < POOL_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + poolTxs.push_back(reinterpret_cast&>(hash)); + generator.putTxToPool(tx); + } + + for (size_t i = 0; i < BLOCKCHAIN_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + blockchainTxs.push_back(reinterpret_cast&>(hash)); + generator.addTxToBlockchain(tx); + } + + ASSERT_GE(generator.getBlockchain().size(), 1); + + std::vector> transactionHashes; + std::copy(poolTxs.begin(), poolTxs.end(), std::back_inserter(transactionHashes)); + std::copy(blockchainTxs.begin(), blockchainTxs.end(), std::back_inserter(transactionHashes)); + + std::vector transactions; + + ASSERT_TRUE(blockchainExplorer.getTransactions(transactionHashes, transactions)); + ASSERT_EQ(transactions.size(), POOL_TX_NUMBER + BLOCKCHAIN_TX_NUMBER); + EXPECT_EQ(transactions.size(), transactionHashes.size()); + + for (const std::array& poolTxHash : poolTxs) { + auto iter = std::find_if( + transactions.begin(), + transactions.end(), + [&poolTxHash](const CryptoNote::TransactionDetails& txDetails) -> bool { + return poolTxHash == txDetails.hash; + } + ); + EXPECT_NE(iter, transactions.end()); + EXPECT_EQ(iter->hash, poolTxHash); + EXPECT_FALSE(iter->inBlockchain); + } + + for (const std::array& blockchainTxHash : blockchainTxs) { + auto iter = std::find_if( + transactions.begin(), + transactions.end(), + [&blockchainTxHash](const CryptoNote::TransactionDetails& txDetails) -> bool { + return blockchainTxHash == txDetails.hash; + } + ); + EXPECT_NE(iter, transactions.end()); + EXPECT_EQ(iter->hash, blockchainTxHash); + EXPECT_TRUE(iter->inBlockchain); + } +} + +TEST_F(BlockchainExplorer, getTransactionsFail) { + size_t POOL_TX_NUMBER = 10; + size_t BLOCKCHAIN_TX_NUMBER = 10; + std::vector> poolTxs; + std::vector> blockchainTxs; + + for (size_t i = 0; i < POOL_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + poolTxs.push_back(reinterpret_cast&>(hash)); + generator.putTxToPool(tx); + } + + for (size_t i = 0; i < BLOCKCHAIN_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + blockchainTxs.push_back(reinterpret_cast&>(hash)); + generator.addTxToBlockchain(tx); + } + + ASSERT_GE(generator.getBlockchain().size(), 1); + + std::vector> transactionHashes; + transactionHashes.push_back(boost::value_initialized>()); + + std::vector transactions; + + ASSERT_ANY_THROW(blockchainExplorer.getTransactions(transactionHashes, transactions)); +} + +TEST_F(BlockchainExplorer, getTransactionsNotInited) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + generator.addTxToBlockchain(tx); + + ASSERT_GE(generator.getBlockchain().size(), 1); + + std::vector> transactionHashes; + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + transactionHashes.push_back(reinterpret_cast&>(hash)); + + std::vector transactions; + + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + + ASSERT_ANY_THROW(newExplorer.getTransactions(transactionHashes, transactions)); +} + +TEST_F(BlockchainExplorer, getPoolStateEmpty) { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + std::vector> knownPoolTransactionHashes; + std::array knownBlockchainTop = topBlock.hash; + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + ASSERT_TRUE(blockchainExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); + EXPECT_TRUE(isBlockchainActual); + + EXPECT_EQ(newTransactions.size(), 0); + EXPECT_EQ(removedTransactions.size(), 0); +} + +TEST_F(BlockchainExplorer, getPoolStateMany) { + size_t POOL_TX_NUMBER = 10; + std::vector> poolTxs; + + for (size_t i = 0; i < POOL_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + poolTxs.push_back(reinterpret_cast&>(hash)); + generator.putTxToPool(tx); + } + + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + std::vector> knownPoolTransactionHashes; + std::array knownBlockchainTop = topBlock.hash; + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + ASSERT_TRUE(blockchainExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); + EXPECT_TRUE(isBlockchainActual); + + EXPECT_EQ(newTransactions.size(), POOL_TX_NUMBER); + EXPECT_EQ(removedTransactions.size(), 0); + + for (const std::array& poolTxHash : poolTxs) { + auto iter = std::find_if( + newTransactions.begin(), + newTransactions.end(), + [&poolTxHash](const CryptoNote::TransactionDetails& txDetails) -> bool { + return poolTxHash == txDetails.hash; + } + ); + EXPECT_NE(iter, newTransactions.end()); + EXPECT_EQ(iter->hash, poolTxHash); + EXPECT_FALSE(iter->inBlockchain); + } + } + + generator.putTxPoolToBlockchain(); + + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + std::vector> knownPoolTransactionHashes; + std::array knownBlockchainTop = topBlock.hash; + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + ASSERT_TRUE(blockchainExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); + EXPECT_TRUE(isBlockchainActual); + + EXPECT_EQ(newTransactions.size(), 0); + EXPECT_EQ(removedTransactions.size(), 0); + } + + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + std::vector> knownPoolTransactionHashes = poolTxs; + std::array knownBlockchainTop = topBlock.hash; + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + ASSERT_TRUE(blockchainExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); + EXPECT_TRUE(isBlockchainActual); + + EXPECT_EQ(newTransactions.size(), 0); + EXPECT_EQ(removedTransactions.size(), POOL_TX_NUMBER); + + for (const std::array& poolTxHash : knownPoolTransactionHashes) { + auto iter = std::find( + removedTransactions.begin(), + removedTransactions.end(), + poolTxHash + ); + EXPECT_NE(iter, removedTransactions.end()); + EXPECT_EQ(*iter, poolTxHash); + } + } + + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + std::array newTxHash = reinterpret_cast&>(hash); + generator.putTxToPool(tx); + + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + std::vector> knownPoolTransactionHashes = poolTxs; + std::array knownBlockchainTop = topBlock.hash; + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + ASSERT_TRUE(blockchainExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); + EXPECT_TRUE(isBlockchainActual); + + ASSERT_EQ(newTransactions.size(), 1); + EXPECT_EQ(newTransactions.front().hash, newTxHash); + EXPECT_EQ(removedTransactions.size(), POOL_TX_NUMBER); + + for (const std::array& poolTxHash : knownPoolTransactionHashes) { + auto iter = std::find( + removedTransactions.begin(), + removedTransactions.end(), + poolTxHash + ); + EXPECT_NE(iter, removedTransactions.end()); + EXPECT_EQ(*iter, poolTxHash); + } + } + + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + std::vector> knownPoolTransactionHashes; + std::array knownBlockchainTop = boost::value_initialized>(); + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + ASSERT_TRUE(blockchainExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); + EXPECT_FALSE(isBlockchainActual); + } +} + +TEST_F(BlockchainExplorer, getPoolStateNotInited) { + + std::vector> knownPoolTransactionHashes; + std::array knownBlockchainTop = boost::value_initialized>(); + bool isBlockchainActual; + + std::vector newTransactions; + std::vector> removedTransactions; + + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + + ASSERT_ANY_THROW(newExplorer.getPoolState(knownPoolTransactionHashes, knownBlockchainTop, isBlockchainActual, newTransactions, removedTransactions)); +} + +TEST_F(BlockchainExplorer, getRewardBlocksWindow) { + ASSERT_EQ(blockchainExplorer.getRewardBlocksWindow(), CryptoNote::parameters::CRYPTONOTE_REWARD_BLOCKS_WINDOW); +} + +TEST_F(BlockchainExplorer, getRewardBlocksWindowNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_ANY_THROW(newExplorer.getRewardBlocksWindow()); +} + +TEST_F(BlockchainExplorer, getFullRewardMaxBlockSize) { + ASSERT_EQ(blockchainExplorer.getFullRewardMaxBlockSize(1), CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); + ASSERT_EQ(blockchainExplorer.getFullRewardMaxBlockSize(2), CryptoNote::parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); +} + +TEST_F(BlockchainExplorer, getFullRewardMaxBlockSizeNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_ANY_THROW(newExplorer.getFullRewardMaxBlockSize(1)); +} + +TEST_F(BlockchainExplorer, isSynchronizedFalse) { + ASSERT_FALSE(blockchainExplorer.isSynchronized()); +} + +TEST_F(BlockchainExplorer, isSynchronizedNotInited) { + CryptoNote::BlockchainExplorer newExplorer(nodeStub, logger); + ASSERT_ANY_THROW(newExplorer.isSynchronized()); +} + +TEST_F(BlockchainExplorer, isSynchronizedNotification) { + smartObserver observer; + CallbackStatus status; + + std::function cb = [&status, this](const CryptoNote::BlockDetails& topBlock){ + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + status.setStatus(std::error_code()); + }; + observer.setCallback(cb); + blockchainExplorer.addObserver(&observer); + + nodeStub.setSynchronizedStatus(true); + ASSERT_TRUE(blockchainExplorer.isSynchronized()); + ASSERT_TRUE(status.wait()); +} + +TEST_F(BlockchainExplorer, blockchainUpdatedEmpty) { + smartObserver observer; + CallbackStatus status; + + std::function< + void(const std::vector& newBlocks, + const std::vector& orphanedBlocks) + > cb = [&status, this](const std::vector& newBlocks, + const std::vector& orphanedBlocks) { + EXPECT_EQ(newBlocks.size(), 0); + EXPECT_EQ(orphanedBlocks.size(), 0); + status.setStatus(std::error_code()); + }; + observer.setCallback(cb); + blockchainExplorer.addObserver(&observer); + + nodeStub.sendLocalBlockchainUpdated(); + ASSERT_TRUE(status.wait()); +} + +TEST_F(BlockchainExplorer, blockchainUpdatedMany) { + const size_t NUMBER_OF_BLOCKS = 10; + std::vector> blockHashes; + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_GE(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + + for (auto iter = generator.getBlockchain().begin() + 2; iter != generator.getBlockchain().end(); iter++) { + if (blockHashes.size() == NUMBER_OF_BLOCKS) { + break; + } + crypto::hash hash = CryptoNote::get_block_hash(*iter); + blockHashes.push_back(reinterpret_cast&>(hash)); + } + + smartObserver observer; + CallbackStatus status; + + std::function< + void(const std::vector& newBlocks, + const std::vector& orphanedBlocks) + > cb = [&status, &blockHashes, this, NUMBER_OF_BLOCKS](const std::vector& newBlocks, + const std::vector& orphanedBlocks) { + EXPECT_EQ(newBlocks.size(), NUMBER_OF_BLOCKS); + EXPECT_EQ(orphanedBlocks.size(), 0); + + auto range = boost::combine(blockHashes, newBlocks); + for (const boost::tuple, CryptoNote::BlockDetails>& hashWithBlock : range) { + EXPECT_EQ(hashWithBlock.get<0>(), hashWithBlock.get<1>().hash); + EXPECT_FALSE(hashWithBlock.get<1>().isOrphaned); + } + + status.setStatus(std::error_code()); + }; + observer.setCallback(cb); + blockchainExplorer.addObserver(&observer); + + nodeStub.sendLocalBlockchainUpdated(); + ASSERT_TRUE(status.wait()); +} + +TEST_F(BlockchainExplorer, poolUpdatedEmpty) { + smartObserver observer; + CallbackStatus status; + + std::function< + void(const std::vector& newTransactions, + const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) + > cb = [&status, this](const std::vector& newTransactions, + const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) { + EXPECT_EQ(newTransactions.size(), 0); + EXPECT_EQ(removedTransactions.size(), 0); + status.setStatus(std::error_code()); + }; + observer.setCallback(cb); + blockchainExplorer.addObserver(&observer); + + nodeStub.sendPoolChanged(); + ASSERT_FALSE(status.wait()); +} + +TEST_F(BlockchainExplorer, poolUpdatedMany) { + size_t POOL_TX_NUMBER = 10; + std::vector> poolTxs; + + for (size_t i = 0; i < POOL_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + crypto::hash hash = CryptoNote::get_transaction_hash(tx); + poolTxs.push_back(reinterpret_cast&>(hash)); + generator.putTxToPool(tx); + } + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + smartObserver observer; + CallbackStatus status; + + std::function< + void(const std::vector& newTransactions, + const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) + > cb = [&status, &poolTxs, this, POOL_TX_NUMBER](const std::vector& newTransactions, + const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) { + EXPECT_EQ(newTransactions.size(), POOL_TX_NUMBER); + EXPECT_EQ(removedTransactions.size(), 0); + + for (const std::array& poolTxHash : poolTxs) { + auto iter = std::find_if( + newTransactions.begin(), + newTransactions.end(), + [&poolTxHash](const CryptoNote::TransactionDetails& txDetails) -> bool { + return poolTxHash == txDetails.hash; + } + ); + EXPECT_NE(iter, newTransactions.end()); + EXPECT_EQ(iter->hash, poolTxHash); + EXPECT_FALSE(iter->inBlockchain); + } + status.setStatus(std::error_code()); + }; + observer.setCallback(cb); + + std::function< + void(const std::vector& newBlocks, + const std::vector& orphanedBlocks) + > cb1 = [&status, this](const std::vector& newBlocks, + const std::vector& orphanedBlocks) {}; + observer.setCallback(cb1); + + nodeStub.sendLocalBlockchainUpdated(); + + blockchainExplorer.addObserver(&observer); + + nodeStub.sendPoolChanged(); + ASSERT_TRUE(status.wait()); + blockchainExplorer.removeObserver(&observer); + } + + generator.putTxPoolToBlockchain(); + + { + CryptoNote::BlockDetails topBlock; + + ASSERT_GE(generator.getBlockchain().size(), 1); + + ASSERT_TRUE(blockchainExplorer.getBlockchainTop(topBlock)); + EXPECT_EQ(topBlock.height, generator.getBlockchain().size() - 1); + EXPECT_FALSE(topBlock.isOrphaned); + + smartObserver observer; + CallbackStatus status; + CallbackStatus status1; + + std::function< + void(const std::vector& newTransactions, + const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) + > cb = [&status, &poolTxs, this, POOL_TX_NUMBER](const std::vector& newTransactions, + const std::vector, CryptoNote::TransactionRemoveReason>>& removedTransactions) { + EXPECT_EQ(newTransactions.size(), 0); + EXPECT_EQ(removedTransactions.size(), POOL_TX_NUMBER); + + for (const std::array& poolTxHash : poolTxs) { + auto iter = std::find_if( + removedTransactions.begin(), + removedTransactions.end(), + [&poolTxHash](const std::pair, CryptoNote::TransactionRemoveReason>& txDetails) -> bool { + return poolTxHash == txDetails.first; + } + ); + EXPECT_NE(iter, removedTransactions.end()); + EXPECT_EQ(iter->first, poolTxHash); + EXPECT_EQ(iter->second, CryptoNote::TransactionRemoveReason::INCLUDED_IN_BLOCK); + } + status.setStatus(std::error_code()); + }; + observer.setCallback(cb); + + std::function< + void(const std::vector& newBlocks, + const std::vector& orphanedBlocks) + > cb1 = [&status1, this](const std::vector& newBlocks, + const std::vector& orphanedBlocks) { + status1.setStatus(std::error_code()); + }; + observer.setCallback(cb1); + + blockchainExplorer.addObserver(&observer); + + nodeStub.sendLocalBlockchainUpdated(); + ASSERT_TRUE(status1.wait()); + + nodeStub.sendPoolChanged(); + ASSERT_TRUE(status.wait()); + blockchainExplorer.removeObserver(&observer); + } + +} diff --git a/tests/unit_tests/test_JsonValue.cpp b/tests/unit_tests/test_JsonValue.cpp new file mode 100644 index 0000000000..85eae1a27c --- /dev/null +++ b/tests/unit_tests/test_JsonValue.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "gtest/gtest.h" +#include + +using Common::JsonValue; + +namespace { + +std::vector goodPatterns{ + "{}", + " {} ", + " { } ", + "100", + "[10,20,30]", + " [ 10 , \n 20 , \n 30 ] ", + "{\"prop\": 100}", + "{\"prop\": 100, \"prop2\": [100, 20, 30] }", + "{\"prop\": 100, \"prop2\": { \"p\":\"test\" } }", + +}; + + +std::vector badPatterns{ + "", + "1..2", + "\n\n", + "{", + "[", + "[100,", + "[[]", + "\"", + "{\"prop: 100 }", + "{\"prop\" 100 }", + "{ prop: 100 }", +}; + +} + +TEST(JsonValue, testGoodPatterns) { + for (const auto& p : goodPatterns) { + std::cout << "Pattern: " << p << std::endl; + ASSERT_NO_THROW(Common::JsonValue::fromString(p)); + } +} + +TEST(JsonValue, testBadPatterns) { + for (const auto& p : badPatterns) { + ASSERT_ANY_THROW(Common::JsonValue::fromString(p)); + } +} + diff --git a/tests/unit_tests/test_TransfersConsumer.cpp b/tests/unit_tests/test_TransfersConsumer.cpp index 6cea3e557f..ed44a74e67 100644 --- a/tests/unit_tests/test_TransfersConsumer.cpp +++ b/tests/unit_tests/test_TransfersConsumer.cpp @@ -843,7 +843,7 @@ TEST_F(TransfersConsumerTest, onPoolUpdated_addTransactionDoesNotGetsGlobalIndic // construct tx auto tx = createTransaction(); addTestInput(*tx, 10000); - auto out = addTestKeyOutput(*tx, 10000, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, m_accountKeys); + addTestKeyOutput(*tx, 10000, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, m_accountKeys); m_consumer.onPoolUpdated({ convertTx(*tx) }, {}); ASSERT_TRUE(m_node.calls_getTransactionOutsGlobalIndices.empty()); diff --git a/tests/unit_tests/test_TransfersContainer.cpp b/tests/unit_tests/test_TransfersContainer.cpp index 665ff0593e..1ff15b3f0b 100644 --- a/tests/unit_tests/test_TransfersContainer.cpp +++ b/tests/unit_tests/test_TransfersContainer.cpp @@ -399,7 +399,7 @@ TEST_F(TransfersContainer_addTransaction, addingEmptyTransactionOuptutsDoesNotCh auto tx = createTransaction(); addTestInput(*tx, TEST_OUTPUT_AMOUNT + 1); - auto outInfo = addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); + addTestKeyOutput(*tx, TEST_OUTPUT_AMOUNT, TEST_TRANSACTION_OUTPUT_GLOBAL_INDEX); std::vector outputs; @@ -615,7 +615,7 @@ TEST_F(TransfersContainer_deleteUnconfirmedTransaction, deleteUnconfirmedSpendin { CryptoNote::BlockInfo blockInfo{ UNCONFIRMED_TRANSACTION_HEIGHT, 1000000 }; addInput(*spendingTx, account, transfers[0]); - auto outInfo = addTestKeyOutput(*spendingTx, TEST_OUTPUT_AMOUNT - 1, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); + addTestKeyOutput(*spendingTx, TEST_OUTPUT_AMOUNT - 1, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); std::vector outputs; ASSERT_TRUE(container.addTransaction(blockInfo, *spendingTx, outputs)); } diff --git a/tests/unit_tests/test_format_utils.cpp b/tests/unit_tests/test_format_utils.cpp index fd5fb77a85..dba54c8e25 100644 --- a/tests/unit_tests/test_format_utils.cpp +++ b/tests/unit_tests/test_format_utils.cpp @@ -19,9 +19,6 @@ #include -// epee -#include "misc_language.h" - #include "Common/util.h" #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" @@ -29,6 +26,8 @@ #include +#define AUTO_VAL_INIT(n) boost::value_initialized() + TEST(parse_tx_extra, handles_empty_extra) { std::vector extra;; diff --git a/tests/unit_tests/test_inprocess_node.cpp b/tests/unit_tests/test_inprocess_node.cpp index 4794f0b2ba..60944531ae 100644 --- a/tests/unit_tests/test_inprocess_node.cpp +++ b/tests/unit_tests/test_inprocess_node.cpp @@ -19,10 +19,15 @@ #include +#include + #include "EventWaiter.h" #include "ICoreStub.h" #include "ICryptonoteProtocolQueryStub.h" #include "InProcessNode/InProcessNode.h" +#include "TestBlockchainGenerator.h" +#include "Logging/FileLogger.h" +#include "cryptonote_core/TransactionApi.h" struct CallbackStatus { CallbackStatus() {} @@ -36,9 +41,24 @@ struct CallbackStatus { EventWaiter waiter; }; +namespace { +CryptoNote::Transaction createTx(CryptoNote::ITransactionReader& tx) { + auto data = tx.getTransactionData(); + + CryptoNote::blobdata txblob(data.data(), data.data() + data.size()); + CryptoNote::Transaction outTx; + CryptoNote::parse_and_validate_tx_from_blob(txblob, outTx); + + return outTx; +} +} + class InProcessNode : public ::testing::Test { public: - InProcessNode() : node(coreStub, protocolQueryStub) {} + InProcessNode() : + node(coreStub, protocolQueryStub), + currency(CryptoNote::CurrencyBuilder(logger).currency()), + generator(currency) {} void SetUp(); protected: @@ -47,9 +67,14 @@ class InProcessNode : public ::testing::Test { ICoreStub coreStub; ICryptonoteProtocolQueryStub protocolQueryStub; CryptoNote::InProcessNode node; + + CryptoNote::Currency currency; + TestBlockchainGenerator generator; + Logging::FileLogger logger; }; void InProcessNode::SetUp() { + logger.init("/dev/null"); initNode(); } @@ -282,6 +307,375 @@ TEST_F(InProcessNode, getLastLocalBlockTimestampError) { ASSERT_THROW(newNode.getLastLocalBlockTimestamp(), std::exception); } +TEST_F(InProcessNode, getBlocksByHeightEmpty) { + std::vector blockHeights; + std::vector> blocks; + ASSERT_EQ(blockHeights.size(), 0); + ASSERT_EQ(blocks.size(), 0); + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + CallbackStatus status; + node.getBlocks(blockHeights, blocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getBlocksByHeightMany) { + const uint64_t NUMBER_OF_BLOCKS = 10; + + std::vector blockHeights; + std::vector> actualBlocks; + + std::vector expectedBlocks; + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_GE(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + + for (auto iter = generator.getBlockchain().begin() + 1; iter != generator.getBlockchain().end(); iter++) { + expectedBlocks.push_back(*iter); + blockHeights.push_back(std::move(boost::get(iter->minerTx.vin.front()).height)); + coreStub.addBlock(*iter); + } + + ASSERT_GE(blockHeights.size(), NUMBER_OF_BLOCKS); + ASSERT_EQ(blockHeights.size(), expectedBlocks.size()); + ASSERT_EQ(actualBlocks.size(), 0); + + CallbackStatus status; + node.getBlocks(blockHeights, actualBlocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); + + ASSERT_EQ(blockHeights.size(), expectedBlocks.size()); + ASSERT_EQ(blockHeights.size(), actualBlocks.size()); + auto range1 = boost::combine(blockHeights, expectedBlocks); + auto range = boost::combine(range1, actualBlocks); + for (const boost::tuple, std::vector>& sameHeight : range) { + EXPECT_EQ(sameHeight.get<1>().size(), 1); + for (const CryptoNote::BlockDetails& block : sameHeight.get<1>()) { + EXPECT_EQ(block.height, sameHeight.get<0>().get<0>()); + crypto::hash expectedCryptoHash = CryptoNote::get_block_hash(sameHeight.get<0>().get<1>()); + std::array expectedHash = reinterpret_cast&>(expectedCryptoHash); + EXPECT_EQ(block.hash, expectedHash); + EXPECT_FALSE(block.isOrphaned); + } + } +} + +TEST_F(InProcessNode, getBlocksByHeightFail) { + const uint64_t NUMBER_OF_BLOCKS = 10; + + std::vector blockHeights; + std::vector> actualBlocks; + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_LT(generator.getBlockchain().size(), NUMBER_OF_BLOCKS * 2); + + for (const CryptoNote::Block& block : generator.getBlockchain()) { + coreStub.addBlock(block); + } + + for (uint64_t i = 0; i < NUMBER_OF_BLOCKS * 2; ++i) { + blockHeights.push_back(std::move(i)); + } + + ASSERT_EQ(actualBlocks.size(), 0); + + CallbackStatus status; + node.getBlocks(blockHeights, actualBlocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getBlocksByHeightNotInited) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + + std::vector blockHeights; + std::vector> blocks; + ASSERT_EQ(blockHeights.size(), 0); + ASSERT_EQ(blocks.size(), 0); + + CallbackStatus status; + newNode.getBlocks(blockHeights, blocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getBlocksByHashEmpty) { + std::vector blockHashes; + std::vector blocks; + ASSERT_EQ(blockHashes.size(), 0); + ASSERT_EQ(blocks.size(), 0); + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + CallbackStatus status; + node.getBlocks(blockHashes, blocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getBlocksByHashMany) { + const uint64_t NUMBER_OF_BLOCKS = 10; + + std::vector blockHashes; + std::vector actualBlocks; + + std::vector expectedBlocks; + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_GE(generator.getBlockchain().size(), NUMBER_OF_BLOCKS); + + for (auto iter = generator.getBlockchain().begin() + 1; iter != generator.getBlockchain().end(); iter++) { + expectedBlocks.push_back(*iter); + blockHashes.push_back(CryptoNote::get_block_hash(*iter)); + coreStub.addBlock(*iter); + } + + ASSERT_GE(blockHashes.size(), NUMBER_OF_BLOCKS); + ASSERT_EQ(blockHashes.size(), expectedBlocks.size()); + ASSERT_EQ(actualBlocks.size(), 0); + + CallbackStatus status; + node.getBlocks(blockHashes, actualBlocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); + + ASSERT_EQ(blockHashes.size(), expectedBlocks.size()); + ASSERT_EQ(blockHashes.size(), actualBlocks.size()); + auto range1 = boost::combine(blockHashes, expectedBlocks); + auto range = boost::combine(range1, actualBlocks); + for (const boost::tuple, CryptoNote::BlockDetails>& sameHeight : range) { + crypto::hash expectedCryptoHash = CryptoNote::get_block_hash(sameHeight.get<0>().get<1>()); + EXPECT_EQ(expectedCryptoHash, sameHeight.get<0>().get<0>()); + std::array expectedHash = reinterpret_cast&>(expectedCryptoHash); + EXPECT_EQ(sameHeight.get<1>().hash, expectedHash); + EXPECT_FALSE(sameHeight.get<1>().isOrphaned); + } +} + +TEST_F(InProcessNode, getBlocksByHashFail) { + const uint64_t NUMBER_OF_BLOCKS = 10; + + std::vector blockHashes; + std::vector actualBlocks; + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + generator.generateEmptyBlocks(NUMBER_OF_BLOCKS); + ASSERT_LT(generator.getBlockchain().size(), NUMBER_OF_BLOCKS * 2); + + for (const CryptoNote::Block& block : generator.getBlockchain()) { + coreStub.addBlock(block); + } + + for (uint64_t i = 0; i < NUMBER_OF_BLOCKS * 2; ++i) { + blockHashes.push_back(boost::value_initialized()); + } + + ASSERT_EQ(actualBlocks.size(), 0); + + CallbackStatus status; + node.getBlocks(blockHashes, actualBlocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getBlocksByHashNotInited) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + + std::vector blockHashes; + std::vector blocks; + ASSERT_EQ(blockHashes.size(), 0); + ASSERT_EQ(blocks.size(), 0); + + CallbackStatus status; + newNode.getBlocks(blockHashes, blocks, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getTxEmpty) { + std::vector transactionHashes; + std::vector transactions; + ASSERT_EQ(transactionHashes.size(), 0); + ASSERT_EQ(transactions.size(), 0); + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + CallbackStatus status; + node.getTransactions(transactionHashes, transactions, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, getTxMany) { + size_t POOL_TX_NUMBER = 10; + size_t BLOCKCHAIN_TX_NUMBER = 10; + + std::vector transactionHashes; + std::vector actualTransactions; + + std::vector> expectedTransactions; + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + size_t prevBlockchainSize = generator.getBlockchain().size(); + for (size_t i = 0; i < BLOCKCHAIN_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + transactionHashes.push_back(CryptoNote::get_transaction_hash(tx)); + generator.addTxToBlockchain(tx); + ASSERT_EQ(generator.getBlockchain().size(), prevBlockchainSize + 1); + prevBlockchainSize = generator.getBlockchain().size(); + coreStub.addBlock(generator.getBlockchain().back()); + coreStub.addTransaction(tx); + expectedTransactions.push_back(std::make_tuple(tx, CryptoNote::get_block_hash(generator.getBlockchain().back()), boost::get(generator.getBlockchain().back().minerTx.vin.front()).height)); + } + + ASSERT_EQ(transactionHashes.size(), BLOCKCHAIN_TX_NUMBER); + ASSERT_EQ(transactionHashes.size(), expectedTransactions.size()); + ASSERT_EQ(actualTransactions.size(), 0); + + for (size_t i = 0; i < POOL_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + transactionHashes.push_back(CryptoNote::get_transaction_hash(tx)); + coreStub.addTransaction(tx); + expectedTransactions.push_back(std::make_tuple(tx, boost::value_initialized(), boost::value_initialized())); + } + + ASSERT_EQ(transactionHashes.size(), BLOCKCHAIN_TX_NUMBER + POOL_TX_NUMBER); + ASSERT_EQ(transactionHashes.size(), expectedTransactions.size()); + ASSERT_EQ(actualTransactions.size(), 0); + + + CallbackStatus status; + node.getTransactions(transactionHashes, actualTransactions, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); + + ASSERT_EQ(transactionHashes.size(), expectedTransactions.size()); + ASSERT_EQ(transactionHashes.size(), actualTransactions.size()); + auto range1 = boost::combine(transactionHashes, actualTransactions); + auto range = boost::combine(range1, expectedTransactions); + for (const boost::tuple, std::tuple>& sameHeight : range) { + crypto::hash expectedCryptoHash = CryptoNote::get_transaction_hash(std::get<0>(sameHeight.get<1>())); + EXPECT_EQ(expectedCryptoHash, sameHeight.get<0>().get<0>()); + std::array expectedHash = reinterpret_cast&>(expectedCryptoHash); + EXPECT_EQ(sameHeight.get<0>().get<1>().hash, expectedHash); + if (std::get<1>(sameHeight.get<1>()) != boost::value_initialized()) { + EXPECT_TRUE(sameHeight.get<0>().get<1>().inBlockchain); + std::array expectedBlockHash = reinterpret_cast&>(std::get<1>(sameHeight.get<1>())); + EXPECT_EQ(sameHeight.get<0>().get<1>().blockHash, expectedBlockHash); + EXPECT_EQ(sameHeight.get<0>().get<1>().blockHeight, std::get<2>(sameHeight.get<1>())); + } else { + EXPECT_FALSE(sameHeight.get<0>().get<1>().inBlockchain); + } + } +} + +TEST_F(InProcessNode, getTxFail) { +size_t POOL_TX_NUMBER = 10; + size_t BLOCKCHAIN_TX_NUMBER = 10; + + std::vector transactionHashes; + std::vector actualTransactions; + + std::vector> expectedTransactions; + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + size_t prevBlockchainSize = generator.getBlockchain().size(); + for (size_t i = 0; i < BLOCKCHAIN_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + transactionHashes.push_back(CryptoNote::get_transaction_hash(tx)); + generator.addTxToBlockchain(tx); + ASSERT_EQ(generator.getBlockchain().size(), prevBlockchainSize + 1); + prevBlockchainSize = generator.getBlockchain().size(); + coreStub.addBlock(generator.getBlockchain().back()); + coreStub.addTransaction(tx); + expectedTransactions.push_back(std::make_tuple(tx, CryptoNote::get_block_hash(generator.getBlockchain().back()), boost::get(generator.getBlockchain().back().minerTx.vin.front()).height)); + } + + ASSERT_EQ(transactionHashes.size(), BLOCKCHAIN_TX_NUMBER); + ASSERT_EQ(transactionHashes.size(), expectedTransactions.size()); + ASSERT_EQ(actualTransactions.size(), 0); + + for (size_t i = 0; i < POOL_TX_NUMBER; ++i) { + auto txptr = CryptoNote::createTransaction(); + auto tx = ::createTx(*txptr.get()); + transactionHashes.push_back(CryptoNote::get_transaction_hash(tx)); + expectedTransactions.push_back(std::make_tuple(tx, boost::value_initialized(), boost::value_initialized())); + } + + ASSERT_EQ(transactionHashes.size(), BLOCKCHAIN_TX_NUMBER + POOL_TX_NUMBER); + ASSERT_EQ(transactionHashes.size(), expectedTransactions.size()); + ASSERT_EQ(actualTransactions.size(), 0); + + + CallbackStatus status; + node.getTransactions(transactionHashes, actualTransactions, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); + +} + +TEST_F(InProcessNode, getTxNotInited) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + + std::vector transactionHashes; + std::vector transactions; + ASSERT_EQ(transactionHashes.size(), 0); + ASSERT_EQ(transactions.size(), 0); + + coreStub.set_blockchain_top(0, boost::value_initialized(), true); + + CallbackStatus status; + newNode.getTransactions(transactionHashes, transactions, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + +TEST_F(InProcessNode, isSynchronized) { + bool syncStatus; + { + CallbackStatus status; + node.isSynchronized(syncStatus, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); + ASSERT_FALSE(syncStatus); + } + + protocolQueryStub.setSynchronizedStatus(true); + + { + CallbackStatus status; + node.isSynchronized(syncStatus, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_EQ(std::error_code(), status.getStatus()); + ASSERT_TRUE(syncStatus); + } +} + +TEST_F(InProcessNode, isSynchronizedNotInited) { + CryptoNote::InProcessNode newNode(coreStub, protocolQueryStub); + bool syncStatus; + + CallbackStatus status; + newNode.isSynchronized(syncStatus, [&status] (std::error_code ec) { status.setStatus(ec); }); + ASSERT_TRUE(status.wait()); + ASSERT_NE(std::error_code(), status.getStatus()); +} + //TODO: make relayTransaction unit test //TODO: make getNewBlocks unit test //TODO: make queryBlocks unit test diff --git a/tests/unit_tests/test_peerlist.cpp b/tests/unit_tests/test_peerlist.cpp index 32eee076cf..704466c829 100644 --- a/tests/unit_tests/test_peerlist.cpp +++ b/tests/unit_tests/test_peerlist.cpp @@ -71,7 +71,6 @@ TEST(peer_list, merge_peer_lists) peerlist_manager plm; plm.init(false); std::list outer_bs; -#define ADD_NODE_TO_PL(ip_, port_, id_, timestamp_) { 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);} } diff --git a/tests/unit_tests/test_protocol_pack.cpp b/tests/unit_tests/test_protocol_pack.cpp index d3f0600553..b68f39ecc3 100644 --- a/tests/unit_tests/test_protocol_pack.cpp +++ b/tests/unit_tests/test_protocol_pack.cpp @@ -18,10 +18,8 @@ #include #include "gtest/gtest.h" - -#include "include_base_utils.h" #include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "storages/portable_storage_template_helper.h" +#include "serialization/SerializationTools.h" TEST(protocol_pack, protocol_pack_command) { @@ -29,14 +27,12 @@ TEST(protocol_pack, protocol_pack_command) CryptoNote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r; r.start_height = 1; r.total_height = 3; - for(int i = 1; i < 10000; i += i*10) - { + for(int i = 1; i < 10000; i += i*10) { r.m_block_ids.resize(i, boost::value_initialized()); - bool res = epee::serialization::store_t_to_binary(r, buff); - ASSERT_TRUE(res); + buff = CryptoNote::storeToBinaryKeyValue(r); CryptoNote::NOTIFY_RESPONSE_CHAIN_ENTRY::request r2; - res = epee::serialization::load_t_from_binary(r2, buff); + bool res = CryptoNote::loadFromBinaryKeyValue(r2, buff); ASSERT_TRUE(r.m_block_ids.size() == i); ASSERT_TRUE(r.start_height == 1); ASSERT_TRUE(r.total_height == 3); diff --git a/tests/unit_tests/tx_pool.cpp b/tests/unit_tests/tx_pool.cpp index 3381b43c6e..db80895833 100644 --- a/tests/unit_tests/tx_pool.cpp +++ b/tests/unit_tests/tx_pool.cpp @@ -19,6 +19,8 @@ #include +#include + #include "cryptonote_core/account.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/Currency.h" @@ -136,9 +138,20 @@ class tx_pool : public ::testing::Test { tx_pool() : currency(CryptoNote::CurrencyBuilder(logger).currency()) {} +protected: + virtual void SetUp() override { + m_configDir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("test_data_%%%%%%%%%%%%"); + } + + virtual void TearDown() override { + boost::system::error_code ignoredErrorCode; + boost::filesystem::remove_all(m_configDir, ignoredErrorCode); + } + protected: Logging::ConsoleLogger logger; CryptoNote::Currency currency; + boost::filesystem::path m_configDir; }; namespace @@ -421,7 +434,11 @@ TEST_F(tx_pool, add_tx_after_cleanup) ASSERT_TRUE(pool.add_tx(tx, tvc, false)); // main chain ASSERT_TRUE(tvc.m_added_to_pool); - pool.timeProvider.timeNow = startTime + currency.mempoolTxLiveTime() + 1; + uint64_t cleanupTime = startTime + currency.mempoolTxLiveTime() + 1; + pool.timeProvider.timeNow = cleanupTime; + pool.on_idle(); + + pool.timeProvider.timeNow = cleanupTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime() + 1; pool.on_idle(); ASSERT_EQ(0, pool.get_transactions_count()); @@ -433,3 +450,203 @@ TEST_F(tx_pool, add_tx_after_cleanup) ASSERT_EQ(1, pool.get_transactions_count()); } + +TEST_F(tx_pool, RecentlyDeletedTransactionCannotBeAddedToTxPoolAgain) { + TestPool pool(currency, logger); + + uint64_t startTime = pool.timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, currency.minimumFee(), 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1; + pool.timeProvider.timeNow = deleteTime; + pool.on_idle(); + ASSERT_EQ(0, pool.get_transactions_count()); + + // Try to add tx again + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_FALSE(tvc.m_added_to_pool); + ASSERT_FALSE(tvc.m_should_be_relayed); + ASSERT_FALSE(tvc.m_verifivation_failed); + ASSERT_FALSE(tvc.m_verifivation_impossible); + + ASSERT_EQ(0, pool.get_transactions_count()); +} + +TEST_F(tx_pool, RecentlyDeletedTransactionCanBeAddedAgainAfterSomeTime) { + TestPool pool(currency, logger); + + uint64_t startTime = pool.timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, currency.minimumFee(), 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1; + pool.timeProvider.timeNow = deleteTime; + pool.on_idle(); + ASSERT_EQ(0, pool.get_transactions_count()); + + uint64_t forgetDeletedTxTime = deleteTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime() + 1; + pool.timeProvider.timeNow = forgetDeletedTxTime; + pool.on_idle(); + + // Try to add tx again + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + ASSERT_TRUE(tvc.m_should_be_relayed); + ASSERT_FALSE(tvc.m_verifivation_failed); + ASSERT_FALSE(tvc.m_verifivation_impossible); + + ASSERT_EQ(1, pool.get_transactions_count()); +} + +TEST_F(tx_pool, RecentlyDeletedTransactionCanBeAddedToTxPoolIfItIsReceivedInBlock) { + TestPool pool(currency, logger); + + uint64_t startTime = pool.timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, currency.minimumFee(), 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool.add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1; + pool.timeProvider.timeNow = deleteTime; + pool.on_idle(); + ASSERT_EQ(0, pool.get_transactions_count()); + + // Try to add tx again + ASSERT_TRUE(pool.add_tx(tx, tvc, true)); + ASSERT_TRUE(tvc.m_added_to_pool); + ASSERT_TRUE(tvc.m_should_be_relayed); + ASSERT_FALSE(tvc.m_verifivation_failed); + ASSERT_FALSE(tvc.m_verifivation_impossible); + + ASSERT_EQ(1, pool.get_transactions_count()); +} + +TEST_F(tx_pool, OldTransactionIsDeletedDuringTxPoolInitialization) { + TransactionValidator validator; + FakeTimeProvider timeProvider; + std::unique_ptr pool(new tx_memory_pool(currency, validator, timeProvider, logger)); + ASSERT_TRUE(pool->init(m_configDir.string())); + + uint64_t startTime = timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, currency.minimumFee(), 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool->add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + ASSERT_TRUE(pool->deinit()); + pool.reset(); + + uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1; + timeProvider.timeNow = deleteTime; + + pool.reset(new tx_memory_pool(currency, validator, timeProvider, logger)); + ASSERT_TRUE(pool->init(m_configDir.string())); + ASSERT_EQ(0, pool->get_transactions_count()); +} + +TEST_F(tx_pool, TransactionThatWasDeletedLongAgoIsForgottenDuringTxPoolInitialization) { + TransactionValidator validator; + FakeTimeProvider timeProvider; + std::unique_ptr pool(new tx_memory_pool(currency, validator, timeProvider, logger)); + ASSERT_TRUE(pool->init(m_configDir.string())); + + uint64_t startTime = timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, currency.minimumFee(), 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool->add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1; + timeProvider.timeNow = deleteTime; + pool->on_idle(); + ASSERT_EQ(0, pool->get_transactions_count()); + + ASSERT_TRUE(pool->deinit()); + pool.reset(); + + uint64_t forgetDeletedTxTime = deleteTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime() + 1; + timeProvider.timeNow = forgetDeletedTxTime; + + pool.reset(new tx_memory_pool(currency, validator, timeProvider, logger)); + ASSERT_TRUE(pool->init(m_configDir.string())); + + // Try to add tx again + ASSERT_TRUE(pool->add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + ASSERT_TRUE(tvc.m_should_be_relayed); + ASSERT_FALSE(tvc.m_verifivation_failed); + ASSERT_FALSE(tvc.m_verifivation_impossible); + + ASSERT_EQ(1, pool->get_transactions_count()); +} + +TEST_F(tx_pool, RecentlyDeletedTxInfoIsSerializedAndDeserialized) { + TransactionValidator validator; + FakeTimeProvider timeProvider; + std::unique_ptr pool(new tx_memory_pool(currency, validator, timeProvider, logger)); + ASSERT_TRUE(pool->init(m_configDir.string())); + + uint64_t startTime = timeProvider.now(); + + Transaction tx; + GenerateTransaction(currency, tx, currency.minimumFee(), 1); + + tx_verification_context tvc = boost::value_initialized(); + ASSERT_TRUE(pool->add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + + uint64_t deleteTime = startTime + currency.mempoolTxLiveTime() + 1; + timeProvider.timeNow = deleteTime; + pool->on_idle(); + ASSERT_EQ(0, pool->get_transactions_count()); + + ASSERT_TRUE(pool->deinit()); + + pool.reset(new tx_memory_pool(currency, validator, timeProvider, logger)); + ASSERT_TRUE(pool->init(m_configDir.string())); + + uint64_t timeBeforeCleanupDeletedTx = deleteTime + currency.numberOfPeriodsToForgetTxDeletedFromPool() * currency.mempoolTxLiveTime(); + timeProvider.timeNow = timeBeforeCleanupDeletedTx; + pool->on_idle(); + + ASSERT_TRUE(pool->add_tx(tx, tvc, false)); + ASSERT_FALSE(tvc.m_added_to_pool); + ASSERT_FALSE(tvc.m_should_be_relayed); + ASSERT_FALSE(tvc.m_verifivation_failed); + ASSERT_FALSE(tvc.m_verifivation_impossible); + + ASSERT_EQ(0, pool->get_transactions_count()); + + timeProvider.timeNow = timeBeforeCleanupDeletedTx + 61; + pool->on_idle(); + + // Try to add tx again + ASSERT_TRUE(pool->add_tx(tx, tvc, false)); + ASSERT_TRUE(tvc.m_added_to_pool); + ASSERT_TRUE(tvc.m_should_be_relayed); + ASSERT_FALSE(tvc.m_verifivation_failed); + ASSERT_FALSE(tvc.m_verifivation_impossible); + + ASSERT_EQ(1, pool->get_transactions_count()); +} From 9f509d93d8171578a52306132fa797a27e80a73c Mon Sep 17 00:00:00 2001 From: xdn-project Date: Wed, 15 Jul 2015 16:30:38 +0000 Subject: [PATCH 36/59] Wallet improvements --- src/cryptonote_config.h | 1 + src/version.h.in | 4 ++-- src/wallet/WalletTransactionSender.cpp | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 2948914b7a..d22dbee2e0 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -145,6 +145,7 @@ const std::initializer_list CHECKPOINTS = { { 136213, "336b687fdb96457cf4060072f76fc9e4e9281744822e0892c9ea128445bbebc7" }, { 137000, "ae73be718076ab00371f81fa5f604c9e020f25abcb48f85b631bde0cabaff048" }, { 143000, "2a9dc9638f091078a67d085ae97caa28f1061c0ae088463977f3df6435a9b585" }, + { 145000, "4b2bbfc07f42ae151119f732e568cba3bee2a0031e3eded2c94958b830e07e5a" }, }; } // cryptonote diff --git a/src/version.h.in b/src/version.h.in index 7dd66062ce..237061affe 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.8-beta" -#define PROJECT_VERSION_BUILD_NO "1554" +#define PROJECT_VERSION "2.0.9-beta" +#define PROJECT_VERSION_BUILD_NO "1555" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/src/wallet/WalletTransactionSender.cpp b/src/wallet/WalletTransactionSender.cpp index 805c64633e..83031411eb 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/wallet/WalletTransactionSender.cpp @@ -134,8 +134,9 @@ std::vector splitAmount(uint64_t amount, uint64_t dustThreshold) { return amounts; } -cryptonote::Transaction convertTransaction(const ITransaction& transaction) { +cryptonote::Transaction convertTransaction(const ITransaction& transaction, size_t upperTransactionSizeLimit) { Blob serializedTransaction = transaction.getTransactionData(); + CryptoNote::throwIf(serializedTransaction.size() >= upperTransactionSizeLimit, cryptonote::error::TRANSACTION_SIZE_TOO_BIG); cryptonote::Transaction result; if (!parse_and_validate_tx_from_blob(std::string(serializedTransaction.begin(), serializedTransaction.end()), result)) { @@ -425,7 +426,7 @@ std::unique_ptr WalletTransactionSender::doSendMultisigTransactio transactionInfo.firstDepositId = depositId; transactionInfo.depositCount = 1; - cryptonote::Transaction lowlevelTransaction = convertTransaction(*transaction); + cryptonote::Transaction lowlevelTransaction = convertTransaction(*transaction, static_cast(m_upperTransactionSizeLimit)); m_transactionsCache.updateTransaction(context->transactionId, lowlevelTransaction, totalAmount, context->selectedTransfers); m_transactionsCache.addCreatedDeposit(depositId, deposit.amount + deposit.interest); @@ -479,7 +480,7 @@ std::unique_ptr WalletTransactionSender::doSendDepositWithdrawTra transactionInfo.hash = transaction->getTransactionHash(); - cryptonote::Transaction lowlevelTransaction = convertTransaction(*transaction); + cryptonote::Transaction lowlevelTransaction = convertTransaction(*transaction, static_cast(m_upperTransactionSizeLimit)); uint64_t interestsSum; uint64_t totalSum; From b3a91f67d74bea3f3ad25665714e0a242cbd12ed Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Mon, 20 Jul 2015 17:52:27 +0100 Subject: [PATCH 37/59] P2P network stability improvement --- src/cryptonote_config.h | 3 +- .../cryptonote_protocol_handler.h | 8 +- src/p2p/LevinProtocol.cpp | 8 +- src/p2p/net_node.cpp | 109 ++++++++++++------ src/p2p/net_node.h | 3 +- src/version.h.in | 4 +- 6 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 6c2086b077..5e5e1e139d 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -153,7 +153,8 @@ const CheckpointData CHECKPOINTS[] = { {713000, "a03f836c4a19f907cd6cac095eb6f56f5279ca2d1303fb7f826750dcb9025495"}, {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"}, - {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"} + {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"}, + {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"} }; } // CryptoNote diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 9fccc75f79..bdd8261d69 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -51,8 +51,8 @@ namespace CryptoNote cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log); - virtual bool addObserver(ICryptonoteProtocolObserver* observer); - virtual bool removeObserver(ICryptonoteProtocolObserver* observer); + virtual bool addObserver(ICryptonoteProtocolObserver* observer) override; + virtual bool removeObserver(ICryptonoteProtocolObserver* observer) override; void set_p2p_endpoint(i_p2p_endpoint* p2p); // ICore& get_core() { return m_core; } @@ -69,8 +69,8 @@ namespace CryptoNote bool get_payload_sync_data(CORE_SYNC_DATA& hshd); bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); int handleCommand(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, cryptonote_connection_context& context, bool& handled); - virtual size_t getPeerCount() const; - virtual uint64_t getObservedHeight() const; + virtual size_t getPeerCount() const override; + virtual uint64_t getObservedHeight() const override; void requestMissingPoolTransactions(const cryptonote_connection_context& context); private: diff --git a/src/p2p/LevinProtocol.cpp b/src/p2p/LevinProtocol.cpp index 9e0f660112..29baaaa117 100644 --- a/src/p2p/LevinProtocol.cpp +++ b/src/p2p/LevinProtocol.cpp @@ -66,7 +66,9 @@ std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, boo std::string response; if (readResponse) { - m_conn.read(reinterpret_cast(&head), sizeof(head)); + if (!readStrict(reinterpret_cast(&head), sizeof(head))) { + throw std::runtime_error("Levin::sendBuf, failed to read header, peer closed connection"); + } if (head.m_signature != LEVIN_SIGNATURE) { throw std::runtime_error("Levin signature mismatch"); @@ -79,7 +81,9 @@ std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, boo response.resize(head.m_cb); if (response.size()) { - readStrict(&response[0], head.m_cb); + if (!readStrict(&response[0], head.m_cb)) { + throw std::runtime_error("Levin::sendBuf, failed to read body, peer closed connection"); + } } } diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 128c3e88b2..c77ad260a0 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -18,6 +18,7 @@ #include "net_node.h" #include +#include #include #include @@ -435,11 +436,7 @@ namespace CryptoNote m_idleTimer.stop(); m_timedSyncTimer.stop(); - logger(INFO) << "Stopping " << m_connections.size() + m_raw_connections.size() << " connections"; - - for (auto& conn : m_raw_connections) { - conn.second.connection.stop(); - } + logger(INFO) << "Stopping " << m_connections.size() << " connections"; for (auto& conn : m_connections) { conn.second.connection.stop(); @@ -639,35 +636,38 @@ namespace CryptoNote try { System::TcpConnector connector(m_dispatcher); - - System::Event timeoutEvent(m_dispatcher); - System::Timer timeoutTimer(m_dispatcher); - m_dispatcher.spawn([&](){ + System::Event connectorTimeoutEvent(m_dispatcher); + System::Timer connectorTimeoutTimer(m_dispatcher); + + m_dispatcher.spawn([&]() { try { - timeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + connectorTimeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); connector.stop(); - } catch (std::exception&) {} - timeoutEvent.set(); + } catch (std::exception&) { + } + + connectorTimeoutEvent.set(); }); System::TcpConnection connection; try { - connection = connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); + connection = + connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); } catch (System::InterruptedException&) { - timeoutEvent.wait(); + connectorTimeoutEvent.wait(); return false; - } catch (std::exception&) { - timeoutTimer.stop(); - timeoutEvent.wait(); + } catch (std::exception& e) { + connectorTimeoutTimer.stop(); + connectorTimeoutEvent.wait(); throw; } p2p_connection_context ctx(m_dispatcher, std::move(connection)); - timeoutTimer.stop(); - timeoutEvent.wait(); + connectorTimeoutTimer.stop(); + connectorTimeoutEvent.wait(); // p2p_connection_context ctx(m_dispatcher, std::move(connector.connect())); @@ -677,39 +677,68 @@ namespace CryptoNote ctx.m_is_income = false; ctx.m_started = time(nullptr); - auto raw = m_raw_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; - try { - CryptoNote::LevinProtocol proto(raw->second.connection); + System::Event handshakeTimeoutFinishedEvent(m_dispatcher); + System::Event handshakeFinishedEvent(m_dispatcher); + System::Timer handshakeTimeoutTimer(m_dispatcher); + m_dispatcher.spawn([&] { + try { + handshakeTimeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + ctx.connection.stop(); + } catch (std::exception& e) { + } - if (!handshake(proto, raw->second, just_take_peerlist)) { - logger(WARNING) << "Failed to HANDSHAKE with peer " << na; - m_raw_connections.erase(raw); - return false; + handshakeTimeoutFinishedEvent.set(); + }); + + CryptoNote::LevinProtocol proto(ctx.connection); + bool error = false; + std::exception_ptr exceptionHolder; + m_dispatcher.spawn([&] { + try { + if (!handshake(proto, ctx, just_take_peerlist)) { + logger(WARNING) << "Failed to HANDSHAKE with peer " << na; + error = true; + } + + } catch (System::InterruptedException&) { + error = true; + handshakeFinishedEvent.set(); + return; + } catch (...) { + exceptionHolder = std::current_exception(); } - } catch (...) { - m_raw_connections.erase(raw); - throw; + + handshakeTimeoutTimer.stop(); + handshakeFinishedEvent.set(); + }); + + handshakeFinishedEvent.wait(); + handshakeTimeoutFinishedEvent.wait(); + if (error) { + return false; + } + + if (exceptionHolder != nullptr) { + std::rethrow_exception(exceptionHolder); } if (just_take_peerlist) { - logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << raw->second << "CONNECTION HANDSHAKED OK AND CLOSED."; - m_raw_connections.erase(raw); + logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << ctx << "CONNECTION HANDSHAKED OK AND CLOSED."; return true; } peerlist_entry pe_local = boost::value_initialized(); pe_local.adr = na; - pe_local.id = raw->second.peer_id; + pe_local.id = ctx.peer_id; time(&pe_local.last_seen); m_peerlist.append_with_peer_white(pe_local); if (m_stop) { - m_raw_connections.erase(raw); throw System::InterruptedException(); } - auto iter = m_connections.emplace(raw->first, std::move(raw->second)).first; - m_raw_connections.erase(raw); + auto key = ctx.m_connection_id; + auto iter = m_connections.emplace(key, std::move(ctx)).first; const boost::uuids::uuid& connectionId = iter->first; p2p_connection_context& connectionContext = iter->second; @@ -784,7 +813,7 @@ namespace CryptoNote size_t try_count = 0; size_t current_index = crypto::rand()%m_seed_nodes.size(); - while(true) { + while(true) { if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) break; @@ -855,8 +884,12 @@ namespace CryptoNote //----------------------------------------------------------------------------------- bool node_server::idle_worker() { - m_connections_maker_interval.call(std::bind(&node_server::connections_maker, this)); - m_peerlist_store_interval.call(std::bind(&node_server::store_config, this)); + try { + m_connections_maker_interval.call(std::bind(&node_server::connections_maker, this)); + m_peerlist_store_interval.call(std::bind(&node_server::store_config, this)); + } catch (std::exception& e) { + logger(DEBUGGING) << "exception in idle_worker: " << e.what(); + } return true; } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index cc57c41a35..a1c1c7073a 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -102,7 +102,7 @@ namespace CryptoNote // debug functions bool log_peerlist(); bool log_connections(); - virtual uint64_t get_connections_count(); + virtual uint64_t get_connections_count() override; size_t get_outgoing_connections_count(); CryptoNote::peerlist_manager& get_peerlist_manager() { return m_peerlist; } @@ -171,7 +171,6 @@ namespace CryptoNote typedef std::unordered_map> ConnectionContainer; typedef ConnectionContainer::iterator ConnectionIterator; - ConnectionContainer m_raw_connections; ConnectionContainer m_connections; void acceptLoop(); diff --git a/src/version.h.in b/src/version.h.in index ff69191995..3818b7acbf 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "1.0.5" -#define PROJECT_VERSION_BUILD_NO "503" +#define PROJECT_VERSION "1.0.5.1" +#define PROJECT_VERSION_BUILD_NO "505" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" From 0ac79982babbdf58e2a0db15b28ef37a33aa54bf Mon Sep 17 00:00:00 2001 From: xdn-project Date: Tue, 21 Jul 2015 10:28:27 +0000 Subject: [PATCH 38/59] 'print_stat' command fixes --- src/cryptonote_config.h | 1 + src/cryptonote_core/DepositIndex.cpp | 82 ++++++----- src/cryptonote_core/DepositIndex.h | 7 +- src/cryptonote_core/blockchain_storage.cpp | 2 +- src/version.h.in | 4 +- tests/unit_tests/test_DepositIndex.cpp | 156 ++++++++++++++++----- 6 files changed, 173 insertions(+), 79 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index d22dbee2e0..4342172ea0 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -146,6 +146,7 @@ const std::initializer_list CHECKPOINTS = { { 137000, "ae73be718076ab00371f81fa5f604c9e020f25abcb48f85b631bde0cabaff048" }, { 143000, "2a9dc9638f091078a67d085ae97caa28f1061c0ae088463977f3df6435a9b585" }, { 145000, "4b2bbfc07f42ae151119f732e568cba3bee2a0031e3eded2c94958b830e07e5a" }, + { 147000, "fe3eaf9a565426fe0498377157b62edd3c139e4b18803a4ca9bb6f78a6c0686b" }, }; } // cryptonote diff --git a/src/cryptonote_core/DepositIndex.cpp b/src/cryptonote_core/DepositIndex.cpp index 2d6b3e10f8..07c120527f 100644 --- a/src/cryptonote_core/DepositIndex.cpp +++ b/src/cryptonote_core/DepositIndex.cpp @@ -13,15 +13,11 @@ namespace CryptoNote { -DepositIndex::DepositIndex() { - index.push_back({0, 0, 0}); - height = 0; +DepositIndex::DepositIndex() : blockCount(0) { } -DepositIndex::DepositIndex(DepositHeight expectedHeight) { +DepositIndex::DepositIndex(DepositHeight expectedHeight) : blockCount(0) { index.reserve(expectedHeight + 1); - index.push_back({0, 0, 0}); - height = 0; } void DepositIndex::reserve(DepositHeight expectedHeight) { @@ -29,13 +25,11 @@ void DepositIndex::reserve(DepositHeight expectedHeight) { } auto DepositIndex::fullDepositAmount() const -> DepositAmount { - assert(!index.empty()); - return index.back().amount; + return index.empty() ? 0 : index.back().amount; } auto DepositIndex::fullInterestAmount() const -> DepositInterest { - assert(!index.empty()); - return index.back().interest; + return index.empty() ? 0 : index.back().interest; } static inline bool sumWillOverflow(int64_t x, int64_t y) { @@ -59,63 +53,79 @@ static inline bool sumWillOverflow(uint64_t x, uint64_t y) { } void DepositIndex::pushBlock(DepositAmount amount, DepositInterest interest) { - auto lastAmount = index.back().amount; - auto lastInterest = index.back().interest; - assert(!sumWillOverflow(interest, lastInterest)); + DepositAmount lastAmount; + DepositInterest lastInterest; + if (index.empty()) { + lastAmount = 0; + lastInterest = 0; + } else { + lastAmount = index.back().amount; + lastInterest = index.back().interest; + } + assert(!sumWillOverflow(amount, lastAmount)); + assert(!sumWillOverflow(interest, lastInterest)); assert(amount + lastAmount >= 0); - ++height; if (amount != 0 || interest > 0) { - index.push_back({height, amount + lastAmount, interest + lastInterest}); + index.push_back({blockCount, amount + lastAmount, interest + lastInterest}); } + + ++blockCount; } void DepositIndex::popBlock() { - assert(!index.empty()); - assert(height > 0); - if (index.back().height == height) { - assert(index.size() > 1); + assert(blockCount > 0); + --blockCount; + if (!index.empty() && index.back().height == blockCount) { index.pop_back(); } - - --height; } -auto DepositIndex::lastHeight() const -> DepositHeight { - return height; +auto DepositIndex::size() const -> DepositHeight { + return blockCount; } -auto DepositIndex::elementAt(DepositHeight height) const -> IndexType::const_iterator { +auto DepositIndex::upperBound(DepositHeight height) const -> IndexType::const_iterator { return std::upper_bound( index.cbegin(), index.cend(), height, - [] (DepositHeight height, const DepositIndexEntry& left) { return height < left.height; }) - 1; + [] (DepositHeight height, const DepositIndexEntry& left) { return height < left.height; }); } size_t DepositIndex::popBlocks(DepositHeight from) { - from = from == 0 ? 1 : from; - if (from > height) { + if (from >= blockCount) { return 0; } IndexType::iterator it = index.begin(); - std::advance(it, std::distance(index.cbegin(), elementAt(from))); - if (it->height < from) { - ++it; + std::advance(it, std::distance(index.cbegin(), upperBound(from))); + if (it != index.begin()) { + --it; + if (it->height != from) { + ++it; + } } - auto diff = height - from + 1; index.erase(it, index.end()); - height -= diff; + auto diff = blockCount - from; + blockCount -= diff; return diff; } auto DepositIndex::depositAmountAtHeight(DepositHeight height) const -> DepositAmount { - assert(!index.empty()); - return elementAt(height)->amount; + if (blockCount == 0) { + return 0; + } else { + auto it = upperBound(height); + return it == index.cbegin() ? 0 : (--it)->amount; + } } auto DepositIndex::depositInterestAtHeight(DepositHeight height) const -> DepositInterest { - assert(!index.empty()); - return elementAt(height)->interest; + if (blockCount == 0) { + return 0; + } else { + auto it = upperBound(height); + return it == index.cbegin() ? 0 : (--it)->interest; + } } } diff --git a/src/cryptonote_core/DepositIndex.h b/src/cryptonote_core/DepositIndex.h index 2dd7d643e8..10a92cf374 100644 --- a/src/cryptonote_core/DepositIndex.h +++ b/src/cryptonote_core/DepositIndex.h @@ -25,9 +25,10 @@ class DepositIndex { DepositAmount fullDepositAmount() const; DepositInterest depositInterestAtHeight(DepositHeight height) const; DepositInterest fullInterestAmount() const; - DepositHeight lastHeight() const; + DepositHeight size() const; template void serialize(Archive& ar, const unsigned int version) { ar & index; + ar & blockCount; } private: @@ -43,8 +44,8 @@ class DepositIndex { }; using IndexType = std::vector; - IndexType::const_iterator elementAt(DepositHeight height) const; + IndexType::const_iterator upperBound(DepositHeight height) const; IndexType index; - DepositHeight height; + DepositHeight blockCount; }; } diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp index abf9a58a82..f309b005eb 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -81,7 +81,7 @@ template void cryptonote::blockchain_storage::MultisignatureOutpu namespace cryptonote { -#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 2 +#define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 3 class BlockCacheSerializer { diff --git a/src/version.h.in b/src/version.h.in index 237061affe..9e18595333 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,4 +1,4 @@ #define BUILD_COMMIT_ID "@VERSION@" -#define PROJECT_VERSION "2.0.9-beta" -#define PROJECT_VERSION_BUILD_NO "1555" +#define PROJECT_VERSION "2.0.10-beta" +#define PROJECT_VERSION_BUILD_NO "1567" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" diff --git a/tests/unit_tests/test_DepositIndex.cpp b/tests/unit_tests/test_DepositIndex.cpp index 86b4fefc17..fad6f37100 100644 --- a/tests/unit_tests/test_DepositIndex.cpp +++ b/tests/unit_tests/test_DepositIndex.cpp @@ -12,13 +12,14 @@ using namespace CryptoNote; class DepositIndexTest : public ::testing::Test { public: const std::size_t DEFAULT_HEIGHT = 10; - DepositIndexTest() : index(DEFAULT_HEIGHT) { + DepositIndexTest() : index(static_cast(DEFAULT_HEIGHT)) { } DepositIndex index; }; TEST_F(DepositIndexTest, EmptyAfterCreate) { ASSERT_EQ(0, index.fullDepositAmount()); + ASSERT_EQ(0, index.fullInterestAmount()); } TEST_F(DepositIndexTest, AddBlockUpdatesGlobalAmount) { @@ -26,20 +27,110 @@ TEST_F(DepositIndexTest, AddBlockUpdatesGlobalAmount) { ASSERT_EQ(10, index.fullDepositAmount()); } +TEST_F(DepositIndexTest, AddBlockUpdatesFullInterest) { + index.pushBlock(10, 1); + ASSERT_EQ(1, index.fullInterestAmount()); +} + +TEST_F(DepositIndexTest, GlobalAmountIsSumOfBlockDeposits) { + index.pushBlock(9, 1); + index.pushBlock(12, 1); + ASSERT_EQ(9 + 12, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, AddEmptyBlockDoesntChangeAmount) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + ASSERT_EQ(9, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, AddEmptyBlockDoesntChangeInterest) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + ASSERT_EQ(1, index.fullInterestAmount()); +} + +TEST_F(DepositIndexTest, FirstBlockPushUpdatesDepositAmountAtHeight0) { + index.pushBlock(9, 1); + ASSERT_EQ(9, index.depositAmountAtHeight(0)); +} + +TEST_F(DepositIndexTest, FirstBlockPushUpdatesDepositInterestAtHeight0) { + index.pushBlock(9, 1); + ASSERT_EQ(1, index.depositInterestAtHeight(0)); +} + +TEST_F(DepositIndexTest, FullDepositAmountEqualsDepositAmountAtLastHeight) { + index.pushBlock(9, 1); + index.pushBlock(12, 2); + ASSERT_EQ(index.fullDepositAmount(), index.depositAmountAtHeight(index.size() - 1)); +} + +TEST_F(DepositIndexTest, FullInterestAmountEqualsDepositInterestAtLastHeight) { + index.pushBlock(9, 1); + index.pushBlock(12, 2); + ASSERT_EQ(index.fullInterestAmount(), index.depositInterestAtHeight(index.size() - 1)); +} + +TEST_F(DepositIndexTest, FullDepositAmountEqualsDepositAmountAtHeightGreaterThanLastHeight) { + index.pushBlock(9, 1); + index.pushBlock(12, 2); + ASSERT_EQ(index.fullDepositAmount(), index.depositAmountAtHeight(index.size())); +} + +TEST_F(DepositIndexTest, FullInterestAmountEqualsInterestAmountAtHeightGreaterThanLastHeight) { + index.pushBlock(9, 1); + index.pushBlock(12, 2); + ASSERT_EQ(index.fullInterestAmount(), index.depositInterestAtHeight(index.size())); +} + TEST_F(DepositIndexTest, RemoveReducesGlobalAmount) { index.pushBlock(12, 1); index.popBlock(); ASSERT_EQ(0, index.fullDepositAmount()); } -TEST_F(DepositIndexTest, AddEmptyBlockIncrementsHeight) { +TEST_F(DepositIndexTest, AddEmptyBlockIncrementsSize) { index.pushBlock(0, 0); - ASSERT_EQ(1, index.lastHeight()); + ASSERT_EQ(1, index.size()); index.pushBlock(0, 0); - ASSERT_EQ(2, index.lastHeight()); + ASSERT_EQ(2, index.size()); } -TEST_F(DepositIndexTest, MultipleRemoves) { +TEST_F(DepositIndexTest, PopEmptyBlockDecrementsSize) { + index.pushBlock(0, 0); + index.popBlock(); + ASSERT_EQ(0, index.size()); +} + +TEST_F(DepositIndexTest, AddNonEmptyBlockIncrementsSize) { + index.pushBlock(9, 1); + ASSERT_EQ(1, index.size()); + index.pushBlock(12, 1); + ASSERT_EQ(2, index.size()); +} + +TEST_F(DepositIndexTest, PopNonEmptyBlockDecrementsSize) { + index.pushBlock(9, 1); + index.popBlock(); + ASSERT_EQ(0, index.size()); +} + +TEST_F(DepositIndexTest, PopLastEmptyBlockDoesNotChangeFullDepositAmount) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + index.popBlock(); + ASSERT_EQ(9, index.fullDepositAmount()); +} + +TEST_F(DepositIndexTest, PopLastEmptyBlockDoesNotChangeFullInterestAmount) { + index.pushBlock(9, 1); + index.pushBlock(0, 0); + index.popBlock(); + ASSERT_EQ(1, index.fullInterestAmount()); +} + +TEST_F(DepositIndexTest, MultipleRemovals) { index.pushBlock(10, 1); index.pushBlock(0, 0); index.pushBlock(11, 1); @@ -49,13 +140,13 @@ TEST_F(DepositIndexTest, MultipleRemoves) { ASSERT_EQ(0, index.fullDepositAmount()); } -TEST_F(DepositIndexTest, MultipleRemovesDecrementHeight) { +TEST_F(DepositIndexTest, MultipleRemovalsDecrementSize) { index.pushBlock(10, 1); index.pushBlock(11, 1); index.pushBlock(0, 0); index.pushBlock(12, 1); - ASSERT_EQ(2, index.popBlocks(3)); - ASSERT_EQ(2, index.lastHeight()); + ASSERT_EQ(1, index.popBlocks(3)); + ASSERT_EQ(4 - 1, index.size()); } TEST_F(DepositIndexTest, PopBlockReducesFullAmount) { @@ -65,43 +156,40 @@ TEST_F(DepositIndexTest, PopBlockReducesFullAmount) { ASSERT_EQ(10, index.fullDepositAmount()); } -TEST_F(DepositIndexTest, RemoveDoesntClearGlobalAmount) { +TEST_F(DepositIndexTest, PopBlockDecrementsSize) { index.pushBlock(9, 1); index.pushBlock(12, 1); + + auto size = index.size(); index.popBlock(); - ASSERT_EQ(9, index.fullDepositAmount()); + ASSERT_EQ(size - 1, index.size()); } -TEST_F(DepositIndexTest, AddEmptyBlockDoesntChangeAmount) { - index.pushBlock(9, 1); - index.pushBlock(0, 0); - ASSERT_EQ(9, index.fullDepositAmount()); +TEST_F(DepositIndexTest, DepositAmountAtAnyHeightIsZeroAfterCreation) { + ASSERT_EQ(0, index.depositAmountAtHeight(10)); } -TEST_F(DepositIndexTest, AddEmptyBlockDoesntChangeInterest) { - index.pushBlock(9, 1); - index.pushBlock(0, 0); - ASSERT_EQ(1, index.fullInterestAmount()); +TEST_F(DepositIndexTest, DepositInterestAtAnyHeightIsZeroAfterCreation) { + ASSERT_EQ(0, index.depositInterestAtHeight(10)); } -TEST_F(DepositIndexTest, RemoveDecrementsHeight) { +TEST_F(DepositIndexTest, DepositAmountIsZeroAtAnyHeightBeforeFirstDeposit) { + index.pushBlock(0, 0); index.pushBlock(9, 1); - index.pushBlock(12, 1); - index.popBlock(); - ASSERT_EQ(1, index.lastHeight()); + ASSERT_EQ(0, index.depositAmountAtHeight(0)); } -TEST_F(DepositIndexTest, GlobalAmountIsSumOfBlockDeposits) { +TEST_F(DepositIndexTest, DepositInterestIsZeroAtAnyHeightBeforeFirstDeposit) { + index.pushBlock(0, 0); index.pushBlock(9, 1); - index.pushBlock(12, 1); - ASSERT_EQ(9 + 12, index.fullDepositAmount()); + ASSERT_EQ(0, index.depositInterestAtHeight(0)); } TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddle) { index.pushBlock(9, 1); index.pushBlock(12, 1); index.pushBlock(14, 1); - ASSERT_EQ(9 + 12, index.depositAmountAtHeight(2)); + ASSERT_EQ(9 + 12, index.depositAmountAtHeight(1)); } TEST_F(DepositIndexTest, MaxAmountIsReturnedForHeightLargerThanLastBlock) { @@ -116,7 +204,7 @@ TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddleLooksForLowerBound) { index.pushBlock(12, 1); index.pushBlock(14, 1); index.pushBlock(7, 1); - ASSERT_EQ(9 + 12 + 14, index.depositAmountAtHeight(3)); + ASSERT_EQ(9 + 12 + 14, index.depositAmountAtHeight(2)); } TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddleIgnoresEmptyBlocks) { @@ -130,13 +218,6 @@ TEST_F(DepositIndexTest, DepositAmountAtHeightInTheMiddleIgnoresEmptyBlocks) { ASSERT_EQ(9 + 12, index.depositAmountAtHeight(3)); } -TEST_F(DepositIndexTest, AmountAtZeroHeightIsZero) { - index.pushBlock(9, 1); - index.pushBlock(12, 1); - index.pushBlock(14, 1); - ASSERT_EQ(0, index.depositAmountAtHeight(0)); -} - TEST_F(DepositIndexTest, MultiPopZeroChangesNothing) { ASSERT_EQ(0, index.popBlocks(0)); ASSERT_EQ(0, index.depositAmountAtHeight(0)); @@ -177,11 +258,12 @@ TEST_F(DepositIndexTest, PopBlocksZeroReturnsZero) { ASSERT_EQ(0, index.popBlocks(0)); } -TEST_F(DepositIndexTest, PopBlocksZeroRemovesEmptyBlocks) { +TEST_F(DepositIndexTest, PopBlocksRemovesEmptyBlocks) { index.pushBlock(1, 1); index.pushBlock(0, 0); - ASSERT_EQ(1, index.popBlocks(2)); - ASSERT_EQ(1, index.lastHeight()); + index.pushBlock(0, 0); + ASSERT_EQ(2, index.popBlocks(1)); + ASSERT_EQ(1, index.size()); ASSERT_EQ(1, index.fullDepositAmount()); ASSERT_EQ(1, index.fullInterestAmount()); } From 50cdbfa42481fb0c6be44eb02fd3cf1fc79725ad Mon Sep 17 00:00:00 2001 From: Antonio Juarez Date: Thu, 30 Jul 2015 16:22:07 +0100 Subject: [PATCH 39/59] Bytecoin v.1.0.6 release --- CMakeLists.txt | 7 +- ReleaseNotes.txt | 6 + include/BlockchainExplorerData.h | 34 +- include/CryptoNote.h | 114 ++ .../json_utils.h => include/CryptoTypes.h | 40 +- include/IBlockchainExplorer.h | 13 +- include/IMultiWallet.h | 84 - include/INode.h | 59 +- include/ITransaction.h | 106 +- include/ITransfersContainer.h | 30 +- include/ITransfersSynchronizer.h | 15 +- include/IWallet.h | 140 +- include/IWalletLegacy.h | 116 ++ src/BlockchainExplorer/BlockchainExplorer.cpp | 261 +++- src/BlockchainExplorer/BlockchainExplorer.h | 29 +- .../BlockchainExplorerDataBuilder.cpp | 245 ++- .../BlockchainExplorerDataBuilder.h | 11 +- src/CMakeLists.txt | 52 +- src/Common/ArrayRef.h | 2 +- src/Common/ArrayView.h | 2 +- src/Common/{base58.cpp => Base58.cpp} | 14 +- src/Common/{base58.h => Base58.h} | 4 +- src/Common/BlockingQueue.cpp | 4 + .../{command_line.cpp => CommandLine.cpp} | 2 +- src/Common/{command_line.h => CommandLine.h} | 0 src/Common/IInputStream.h | 2 +- src/Common/IOutputStream.h | 2 +- src/Common/JsonValue.cpp | 12 +- src/Common/JsonValue.h | 12 +- src/Common/Math.cpp | 4 + .../MemoryInputStream.cpp} | 33 +- .../MemoryInputStream.h} | 29 +- src/Common/ObserverManager.h | 3 +- src/Common/SignalHandler.cpp | 23 +- src/Common/SignalHandler.h | 2 +- src/Common/StdInputStream.cpp | 30 + src/Common/StdInputStream.h | 35 + src/Common/StdOutputStream.cpp | 34 + src/Common/StdOutputStream.h | 35 + src/Common/StreamTools.cpp | 20 +- src/Common/StreamTools.h | 10 +- src/Common/StringBuffer.h | 6 +- src/Common/StringInputStream.cpp | 2 +- src/Common/StringInputStream.h | 4 +- src/Common/StringOutputStream.cpp | 2 +- src/Common/StringOutputStream.h | 2 +- src/Common/StringTools.cpp | 37 +- src/Common/StringTools.h | 19 +- src/Common/StringView.h | 2 +- src/Common/{util.cpp => Util.cpp} | 8 +- src/Common/{util.h => Util.h} | 4 +- src/Common/{varint.h => Varint.h} | 2 +- src/Common/VectorOutputStream.cpp | 30 + src/Common/VectorOutputStream.h | 36 + src/Common/static_assert.h | 52 +- .../ConnectivityTool.cpp} | 85 +- src/CryptoNote/BaseTransaction.cpp | 79 - src/CryptoNote/BaseTransaction.h | 62 - src/CryptoNote/Block.cpp | 118 -- src/CryptoNote/Block.h | 78 - src/CryptoNote/KeyInput.cpp | 45 - src/CryptoNote/KeyInput.h | 46 - src/CryptoNote/MultisignatureInput.cpp | 41 - src/CryptoNote/MultisignatureInput.h | 40 - src/CryptoNote/Transaction.cpp | 111 -- src/CryptoNote/Transaction.h | 89 -- .../UnsignedMultisignatureInput.cpp | 33 - src/CryptoNote/UnsignedTransaction.cpp | 111 -- src/CryptoNote/UnsignedTransaction.h | 89 -- ...cryptonote_config.h => CryptoNoteConfig.h} | 13 +- .../Account.cpp} | 24 +- .../account.h => CryptoNoteCore/Account.h} | 28 +- src/CryptoNoteCore/BlockIndex.cpp | 88 ++ .../BlockIndex.h | 34 +- .../Blockchain.cpp} | 1370 ++++++++++------- src/CryptoNoteCore/Blockchain.h | 382 +++++ src/CryptoNoteCore/BlockchainIndices.cpp | 262 ++++ src/CryptoNoteCore/BlockchainIndices.h | 121 ++ src/CryptoNoteCore/BlockchainMessages.cpp | 109 ++ src/CryptoNoteCore/BlockchainMessages.h | 84 + .../Checkpoints.cpp} | 20 +- .../Checkpoints.h} | 18 +- src/CryptoNoteCore/Core.cpp | 1031 +++++++++++++ src/CryptoNoteCore/Core.h | 194 +++ .../CoreConfig.cpp | 6 +- .../CoreConfig.h | 0 .../CryptoNoteBasic.cpp} | 15 +- src/CryptoNoteCore/CryptoNoteBasic.h | 46 + .../CryptoNoteBasicImpl.cpp} | 47 +- .../CryptoNoteBasicImpl.h} | 24 +- src/CryptoNoteCore/CryptoNoteFormatUtils.cpp | 550 +++++++ src/CryptoNoteCore/CryptoNoteFormatUtils.h | 128 ++ .../CryptoNoteSerialization.cpp} | 274 ++-- .../CryptoNoteSerialization.h} | 50 +- .../CryptoNoteStatInfo.h} | 2 +- src/CryptoNoteCore/CryptoNoteTools.cpp | 79 + src/CryptoNoteCore/CryptoNoteTools.h | 125 ++ .../Currency.cpp | 89 +- .../Currency.h | 28 +- .../Difficulty.cpp} | 21 +- .../Difficulty.h} | 2 +- .../IBlock.cpp} | 15 +- .../KeyOutput.cpp => CryptoNoteCore/IBlock.h} | 22 +- .../IBlockchainStorageObserver.h | 0 src/CryptoNoteCore/ICore.h | 122 ++ .../ICoreObserver.h | 0 .../IMinerHandler.h} | 10 +- .../ITimeProvider.cpp | 0 .../ITimeProvider.h | 0 .../ITransactionValidator.h | 11 +- .../ITxPoolObserver.h | 0 src/CryptoNoteCore/IntrusiveLinkedList.h | 211 +++ src/CryptoNoteCore/MessageQueue.h | 114 ++ .../miner.cpp => CryptoNoteCore/Miner.cpp} | 58 +- .../miner.h => CryptoNoteCore/Miner.h} | 32 +- .../MinerConfig.cpp | 4 +- .../MinerConfig.h | 2 +- .../OnceInInterval.h | 0 .../SwappedMap.cpp | 4 + .../SwappedMap.h | 14 +- .../SwappedVector.cpp | 4 + .../SwappedVector.h | 29 +- src/CryptoNoteCore/Transaction.cpp | 532 +++++++ .../TransactionApi.h | 11 +- .../TransactionApiExtra.h} | 42 +- src/CryptoNoteCore/TransactionExtra.cpp | 247 +++ src/CryptoNoteCore/TransactionExtra.h | 90 ++ .../TransactionPool.cpp} | 198 ++- .../TransactionPool.h} | 88 +- src/CryptoNoteCore/TransactionPrefixImpl.cpp | 227 +++ src/CryptoNoteCore/TransactionUtils.cpp | 164 ++ src/CryptoNoteCore/TransactionUtils.h | 42 + .../UpgradeDetector.cpp | 0 .../UpgradeDetector.h | 4 +- .../VerificationContext.h} | 0 .../CryptoNoteProtocolDefinitions.h} | 62 +- .../CryptoNoteProtocolHandler.cpp} | 278 ++-- .../CryptoNoteProtocolHandler.h} | 75 +- .../CryptoNoteProtocolHandlerCommon.h} | 0 .../ICryptoNoteProtocolObserver.h} | 6 +- .../ICryptoNoteProtocolQuery.h} | 10 +- src/{daemon/daemon.cpp => Daemon/Daemon.cpp} | 35 +- .../DaemonCommandsHandler.cpp} | 53 +- .../DaemonCommandsHandler.h | 8 +- src/HTTP/HttpParserErrorCodes.cpp | 2 +- src/HTTP/HttpParserErrorCodes.h | 2 +- src/HTTP/HttpResponse.cpp | 2 +- src/InProcessNode/InProcessNode.cpp | 601 ++++++-- src/InProcessNode/InProcessNode.h | 95 +- src/JsonRpcServer/JsonRpcServer.cpp | 190 +++ .../JsonRpcServer.h | 39 +- .../NodeErrors.cpp | 0 .../NodeErrors.h | 0 src/NodeRpcProxy/NodeRpcProxy.cpp | 637 ++++++++ src/NodeRpcProxy/NodeRpcProxy.h | 139 ++ .../ConnectionContext.h} | 26 +- src/P2p/IP2pNodeInternal.cpp | 18 + .../IP2pNodeInternal.h} | 25 +- src/{p2p => P2p}/LevinProtocol.cpp | 48 +- src/{p2p => P2p}/LevinProtocol.h | 53 +- src/{p2p/net_node.cpp => P2p/NetNode.cpp} | 718 +++++---- src/P2p/NetNode.h | 273 ++++ .../net_node_common.h => P2p/NetNodeCommon.h} | 25 +- src/{p2p => P2p}/NetNodeConfig.cpp | 126 +- src/P2p/NetNodeConfig.h | 76 + src/P2p/P2pConnectionProxy.cpp | 168 ++ src/P2p/P2pConnectionProxy.h | 59 + src/P2p/P2pContext.cpp | 193 +++ src/P2p/P2pContext.h | 104 ++ .../P2pContextOwner.cpp} | 26 +- .../MultiWallet.h => P2p/P2pContextOwner.h} | 32 +- .../LatchGuard.h => P2p/P2pInterfaces.cpp} | 16 +- .../P2pInterfaces.h} | 30 +- src/{p2p/p2p_networks.h => P2p/P2pNetworks.h} | 0 src/P2p/P2pNode.cpp | 553 +++++++ src/P2p/P2pNode.h | 122 ++ src/P2p/P2pNodeConfig.cpp | 130 ++ src/P2p/P2pNodeConfig.h | 63 + .../P2pProtocolDefinitions.h} | 52 +- .../P2pProtocolTypes.h} | 22 +- src/{p2p => P2p}/PeerListManager.cpp | 161 +- src/{p2p => P2p}/PeerListManager.h | 82 +- .../NodeFactory.cpp | 61 +- .../NodeFactory.h | 0 .../PaymentServiceJsonRpcMessages.cpp} | 55 +- .../PaymentServiceJsonRpcMessages.h} | 58 +- .../PaymentServiceJsonRpcServer.cpp} | 273 ++-- src/PaymentGate/PaymentServiceJsonRpcServer.h | 39 + .../WalletFactory.cpp | 10 +- .../WalletFactory.h | 3 +- .../WalletService.cpp | 419 ++--- .../WalletService.h | 62 +- .../ConfigurationManager.cpp | 8 +- .../ConfigurationManager.h | 4 +- src/PaymentGateService/PaymentGateService.cpp | 242 +++ src/PaymentGateService/PaymentGateService.h | 64 + .../PaymentServiceConfiguration.cpp | 17 +- .../PaymentServiceConfiguration.h | 4 +- .../RpcNodeConfiguration.cpp | 0 .../RpcNodeConfiguration.h | 0 src/PaymentGateService/main.cpp | 335 ++++ src/Platform/Linux/System/Dispatcher.cpp | 299 +++- src/Platform/Linux/System/Dispatcher.h | 75 +- src/Platform/Linux/System/Ipv4Resolver.cpp | 19 +- src/Platform/Linux/System/Ipv4Resolver.h | 3 - src/Platform/Linux/System/TcpConnection.cpp | 91 +- src/Platform/Linux/System/TcpConnection.h | 7 +- src/Platform/Linux/System/TcpConnector.cpp | 47 +- src/Platform/Linux/System/TcpConnector.h | 3 - src/Platform/Linux/System/TcpListener.cpp | 69 +- src/Platform/Linux/System/TcpListener.h | 3 - src/Platform/Linux/System/Timer.cpp | 78 +- src/Platform/Linux/System/Timer.h | 3 - .../OSX/System/{context.c => Context.c} | 0 .../OSX/System/{context.h => Context.h} | 0 src/Platform/OSX/System/Dispatcher.cpp | 297 +++- src/Platform/OSX/System/Dispatcher.h | 62 +- src/Platform/OSX/System/Ipv4Resolver.cpp | 19 +- src/Platform/OSX/System/Ipv4Resolver.h | 3 - src/Platform/OSX/System/TcpConnection.cpp | 96 +- src/Platform/OSX/System/TcpConnection.h | 5 +- src/Platform/OSX/System/TcpConnector.cpp | 47 +- src/Platform/OSX/System/TcpConnector.h | 3 - src/Platform/OSX/System/TcpListener.cpp | 58 +- src/Platform/OSX/System/TcpListener.h | 3 - src/Platform/OSX/System/Timer.cpp | 56 +- src/Platform/OSX/System/Timer.h | 3 - src/Platform/Windows/System/Dispatcher.cpp | 276 +++- src/Platform/Windows/System/Dispatcher.h | 51 +- src/Platform/Windows/System/Ipv4Resolver.cpp | 25 +- src/Platform/Windows/System/Ipv4Resolver.h | 5 +- src/Platform/Windows/System/TcpConnection.cpp | 106 +- src/Platform/Windows/System/TcpConnection.h | 13 +- src/Platform/Windows/System/TcpConnector.cpp | 59 +- src/Platform/Windows/System/TcpConnector.h | 3 - src/Platform/Windows/System/TcpListener.cpp | 56 +- src/Platform/Windows/System/TcpListener.h | 5 +- src/Platform/Windows/System/Timer.cpp | 46 +- src/Platform/Windows/System/Timer.h | 3 - .../CoreRpcServerCommandsDefinitions.h} | 99 +- .../CoreRpcServerErrorCodes.h} | 0 src/{rpc => Rpc}/HttpClient.cpp | 19 +- src/{rpc => Rpc}/HttpClient.h | 3 +- src/{rpc => Rpc}/HttpServer.cpp | 27 +- src/{rpc => Rpc}/HttpServer.h | 4 +- src/{rpc => Rpc}/JsonRpc.cpp | 2 +- src/{rpc => Rpc}/JsonRpc.h | 4 +- src/{rpc => Rpc}/RpcServer.cpp | 197 ++- src/{rpc => Rpc}/RpcServer.h | 12 +- src/{rpc => Rpc}/RpcServerConfig.cpp | 4 +- src/{rpc => Rpc}/RpcServerConfig.h | 0 .../BinaryInputStreamSerializer.cpp | 64 +- .../BinaryInputStreamSerializer.h | 13 +- .../BinaryOutputStreamSerializer.cpp | 38 +- .../BinaryOutputStreamSerializer.h | 13 +- src/Serialization/BinarySerializationTools.h | 89 ++ .../ISerializer.h | 13 +- .../IStream.h | 4 +- .../JsonInputStreamSerializer.cpp | 15 +- .../JsonInputStreamSerializer.h | 3 - .../JsonInputValueSerializer.cpp | 44 +- .../JsonInputValueSerializer.h | 11 +- .../JsonOutputStreamSerializer.cpp | 14 +- .../JsonOutputStreamSerializer.h | 6 +- .../KVBinaryCommon.h | 0 .../KVBinaryInputStreamSerializer.cpp | 82 +- .../KVBinaryInputStreamSerializer.h | 10 +- .../KVBinaryOutputStreamSerializer.cpp | 63 +- .../KVBinaryOutputStreamSerializer.h | 15 +- .../MemoryStream.cpp | 0 .../MemoryStream.h | 37 +- src/Serialization/SerializationOverloads.h | 252 +++ .../SerializationTools.h | 24 +- .../PasswordContainer.cpp} | 22 +- .../PasswordContainer.h} | 12 +- .../SimpleWallet.cpp} | 145 +- .../SimpleWallet.h} | 19 +- src/System/Context.h | 151 ++ src/System/ContextGroup.cpp | 99 ++ src/System/{Latch.h => ContextGroup.h} | 28 +- .../System/ContextGroupTimeout.cpp | 24 +- src/System/ContextGroupTimeout.h | 34 + src/System/Event.cpp | 39 +- src/System/EventLock.cpp | 0 src/System/EventLock.h | 1 + src/System/InterruptedException.cpp | 4 + src/System/InterruptedException.h | 4 + src/System/Ipv4Address.cpp | 10 +- src/System/Latch.cpp | 119 -- .../OperationTimeout.h} | 36 +- src/System/RemoteEventLock.cpp | 58 + src/System/RemoteEventLock.h | 35 + .../BlockchainSynchronizer.cpp | 78 +- .../BlockchainSynchronizer.h | 22 +- src/{transfers => Transfers}/CommonTypes.h | 6 +- .../IBlockchainSynchronizer.h | 14 +- .../IObservableImpl.h | 2 +- .../SynchronizationState.cpp | 43 +- .../SynchronizationState.h | 20 +- .../TransfersConsumer.cpp | 102 +- .../TransfersConsumer.h | 30 +- .../TransfersContainer.cpp | 80 +- .../TransfersContainer.h | 66 +- .../TransfersSubscription.cpp | 41 +- .../TransfersSubscription.h | 16 +- .../TransfersSynchronizer.cpp | 32 +- .../TransfersSynchronizer.h | 8 +- src/{transfers => Transfers}/TypeHelpers.h | 15 +- src/{wallet => Wallet}/LegacyKeysImporter.cpp | 198 +-- src/{wallet => Wallet}/LegacyKeysImporter.h | 0 .../WalletAsyncContextCounter.cpp | 0 .../WalletAsyncContextCounter.h | 0 src/{wallet => Wallet}/WalletErrors.cpp | 0 src/{wallet => Wallet}/WalletErrors.h | 4 +- src/Wallet/WalletGreen.cpp | 1218 +++++++++++++++ src/Wallet/WalletGreen.h | 211 +++ src/Wallet/WalletIndices.h | 124 ++ .../WalletRpcServer.cpp} | 61 +- .../WalletRpcServer.h} | 16 +- .../WalletRpcServerCommandsDefinitions.h} | 8 +- .../WalletRpcServerErrorCodes.h} | 0 src/Wallet/WalletSerialization.cpp | 873 +++++++++++ src/Wallet/WalletSerialization.h | 117 ++ src/{wallet => WalletLegacy}/KeysStorage.cpp | 8 +- src/{wallet => WalletLegacy}/KeysStorage.h | 9 +- src/{wallet => WalletLegacy}/WalletHelper.cpp | 24 +- src/{wallet => WalletLegacy}/WalletHelper.h | 22 +- .../WalletLegacy.cpp} | 218 ++- .../Wallet.h => WalletLegacy/WalletLegacy.h} | 60 +- .../WalletLegacyEvent.h} | 59 +- .../WalletLegacySerialization.cpp} | 36 +- .../WalletLegacySerialization.h} | 10 +- src/WalletLegacy/WalletLegacySerializer.cpp | 186 +++ .../WalletLegacySerializer.h} | 12 +- src/{wallet => WalletLegacy}/WalletRequest.h | 6 +- .../WalletSendTransactionContext.h | 4 +- .../WalletTransactionSender.cpp | 148 +- .../WalletTransactionSender.h | 45 +- .../WalletUnconfirmedTransactions.cpp | 27 +- .../WalletUnconfirmedTransactions.h | 34 +- .../WalletUserTransactionsCache.cpp | 106 +- .../WalletUserTransactionsCache.h | 30 +- src/{wallet => WalletLegacy}/WalletUtils.h | 8 +- src/crypto/chacha8.c | 0 src/crypto/chacha8.h | 13 +- src/crypto/crypto.cpp | 347 +++-- src/crypto/crypto.h | 213 +-- src/crypto/generic-ops.h | 16 +- src/crypto/groestl.c | 2 +- src/crypto/hash-ops.h | 0 src/crypto/hash.h | 30 +- src/crypto/skein.c | 4 +- src/crypto/skein_port.h | 0 src/crypto/slow-hash.c | 0 src/crypto/slow-hash.cpp | 2 +- src/cryptonote_core/BlockIndex.cpp | 87 -- src/cryptonote_core/ICore.h | 97 -- src/cryptonote_core/Transaction.cpp | 644 -------- src/cryptonote_core/blockchain_storage.h | 352 ----- .../blockchain_storage_boost_serialization.h | 47 - src/cryptonote_core/cryptonote_basic.cpp | 321 ---- src/cryptonote_core/cryptonote_basic.h | 451 ------ src/cryptonote_core/cryptonote_core.cpp | 622 -------- src/cryptonote_core/cryptonote_core.h | 166 -- .../cryptonote_format_utils.cpp | 754 --------- src/cryptonote_core/cryptonote_format_utils.h | 222 --- src/cryptonote_core/tx_extra.h | 164 -- src/cryptonote_protocol/blobdatatype.h | 39 - src/node_rpc_proxy/InitState.h | 88 -- src/node_rpc_proxy/NodeRpcProxy.cpp | 451 ------ src/node_rpc_proxy/NodeRpcProxy.h | 108 -- src/p2p/NetNodeConfig.h | 47 - src/p2p/net_node.h | 232 --- src/p2p/net_peerlist_boost_serialization.h | 41 - src/payment_service/WalletObservers.cpp | 73 - src/payment_service/WalletObservers.h | 72 - src/payment_service/WalletServiceErrorCodes.h | 60 - src/payment_service/main.cpp | 549 ------- src/serialization/SerializationOverloads.cpp | 81 - src/serialization/SerializationOverloads.h | 147 -- src/serialization/binary_archive.h | 173 --- src/serialization/binary_utils.h | 43 - src/serialization/crypto.h | 72 - src/serialization/json_archive.h | 154 -- src/serialization/serialization.h | 164 -- src/serialization/string.h | 48 - src/serialization/variant.h | 123 -- src/serialization/vector.h | 94 -- src/transfers/SerializationHelpers.h | 48 - src/version.h.in | 4 +- src/wallet/SyncWallet.cpp | 70 - src/wallet/SyncWallet.h | 49 - src/wallet/WalletSerializer.cpp | 179 --- tests/CMakeLists.txt | 39 +- .../CoreTests/AccountBoostSerialization.h | 16 +- .../BlockReward.cpp} | 27 +- .../BlockReward.h} | 2 +- .../BlockValidation.cpp} | 182 ++- .../BlockValidation.h} | 2 +- .../CoreTests/BoostSerializationHelper.h | 2 +- .../ChainSplit1.cpp} | 2 +- .../ChainSplit1.h} | 2 +- .../ChainSwitch1.cpp} | 14 +- .../ChainSwitch1.h} | 10 +- .../chaingen.cpp => CoreTests/Chaingen.cpp} | 122 +- .../chaingen.h => CoreTests/Chaingen.h} | 93 +- .../Chaingen001.cpp} | 6 +- .../chaingen001.h => CoreTests/Chaingen001.h} | 4 +- .../ChaingenMain.cpp} | 34 +- .../CoreTests/CryptoNoteBoostSerialization.h | 74 +- .../DoubleSpend.cpp} | 24 +- .../DoubleSpend.h} | 8 +- .../IntegerOverflow.cpp} | 60 +- .../IntegerOverflow.h} | 2 +- .../RandomOuts.cpp} | 4 +- .../random_outs.h => CoreTests/RandomOuts.h} | 2 +- .../RingSignature.cpp} | 22 +- .../RingSignature.h} | 14 +- .../{core_tests => CoreTests}/TestGenerator.h | 24 +- .../TransactionBuilder.cpp | 91 +- .../TransactionBuilder.h | 28 +- .../TransactionTests.cpp} | 95 +- .../TransactionTests.h} | 0 .../TransactionValidation.cpp} | 286 ++-- .../TransactionValidation.h} | 4 +- .../UnorderedContainersBoostSerialization.h | 0 .../upgrade.cpp => CoreTests/Upgrade.cpp} | 24 +- .../upgrade.h => CoreTests/Upgrade.h} | 6 +- .../double_spend.inl | 28 +- ...ec48bb477733d70ce5f9b85338a07cb10b849ad8fb | Bin ...73df5e5aaace00fe767c4f09de452838575357ca9f | Bin ...8fe4c0659f6959b2bebb15079cdaed07a442a78486 | Bin ...d2331715a631f5729db284eb1fc6f108aeb7a7f4fe | Bin .../Difficulty.cpp} | 6 +- tests/{difficulty => Difficulty}/data.txt | 0 .../{difficulty => Difficulty}/generate-data | 0 tests/{hash => Hash}/main.cpp | 17 +- tests/{hash => Hash}/tests-extra-blake.txt | 0 tests/{hash => Hash}/tests-extra-groestl.txt | 0 tests/{hash => Hash}/tests-extra-jh.txt | 0 tests/{hash => Hash}/tests-extra-skein.txt | 0 tests/{hash => Hash}/tests-fast.txt | 0 tests/{hash => Hash}/tests-slow.txt | 0 tests/{hash => Hash}/tests-tree.txt | 0 tests/{hash-target.cpp => HashTarget.cpp} | 8 +- .../BaseFunctionalTests.cpp} | 117 +- .../BaseFunctionalTests.h} | 30 +- .../InProcTestNode.cpp | 50 +- .../InProcTestNode.h | 14 +- .../Logger.cpp | 96 +- .../Logger.h | 144 +- .../NetworkConfiguration.h | 4 + tests/IntegrationTestLib/NodeCallback.h | 43 + .../NodeObserver.h | 14 +- tests/IntegrationTestLib/ObservableValue.h | 75 + .../Process.cpp | 0 .../Process.h | 0 .../RPCTestNode.cpp | 45 +- .../RPCTestNode.h | 106 +- .../TestNetwork.cpp | 48 +- .../TestNetwork.h | 2 + .../TestNode.h | 87 +- .../TestWalletLegacy.cpp} | 41 +- .../TestWalletLegacy.h} | 22 +- tests/IntegrationTests/BaseTests.h | 55 + .../IntegrationTests.cpp | 37 +- .../MultiVersion.cpp | 35 +- tests/IntegrationTests/Node.cpp | 380 +++++ .../WalletLegacyObserver.h} | 39 +- tests/IntegrationTests/WalletLegacyTests.cpp | 65 + .../main.cpp | 93 +- tests/{io.h => Io.h} | 0 .../NodeRpcProxyTests.cpp} | 6 +- .../CheckRingSignature.h} | 25 +- .../ConstructTransaction.h} | 16 +- .../CryptoNoteSlowHash.h} | 12 +- .../DerivePublicKey.h} | 14 +- .../DeriveSecretKey.h} | 14 +- .../GenerateKeyDerivation.h} | 8 +- .../GenerateKeyImage.h} | 18 +- .../GenerateKeyImageHelper.h} | 10 +- .../IsOutToAccount.h} | 10 +- .../MultiTransactionTestBase.h} | 36 +- .../PerformanceTests.h} | 0 .../PerformanceUtils.h} | 0 .../SingleTransactionTestBase.h} | 14 +- .../main.cpp | 23 +- tests/System/ContextGroupTests.cpp | 401 +++++ tests/System/ContextGroupTimeoutTests.cpp | 60 + tests/System/ContextTests.cpp | 121 ++ tests/System/DispatcherTests.cpp | 145 +- tests/System/EventLockTests.cpp | 9 +- tests/System/EventTests.cpp | 75 +- tests/System/Ipv4ResolverTests.cpp | 55 +- tests/System/OperationTimeoutTests.cpp | 56 + tests/System/TcpConnectionTests.cpp | 190 ++- tests/System/TcpConnectorTests.cpp | 71 +- tests/System/TcpListenerTests.cpp | 106 +- tests/System/TimerTests.cpp | 269 +++- tests/TestGenerator/TestGenerator.cpp | 185 +-- tests/TestGenerator/TestGenerator.h | 48 +- .../globals.h => TransfersTests/Globals.h} | 10 +- tests/TransfersTests/TestNodeRpcProxy.cpp | 100 ++ .../TestTxPoolSync.cpp} | 350 ++--- .../tests.cpp => TransfersTests/Tests.cpp} | 232 +-- .../main.cpp | 4 +- .../ArrayRefTests.cpp | 2 +- .../ArrayViewTests.cpp | 2 +- .../base58.cpp => UnitTests/Base58.cpp} | 90 +- .../BinarySerializationCompatibility.cpp | 561 +++++++ .../BlockReward.cpp} | 4 +- .../BlockingQueue.cpp | 1 - .../chacha8.cpp => UnitTests/Chacha8.cpp} | 4 +- .../Checkpoints.cpp} | 8 +- .../DecomposeAmountIntoDigits.cpp} | 2 +- .../{unit_tests => UnitTests}/EventWaiter.cpp | 0 tests/{unit_tests => UnitTests}/EventWaiter.h | 0 tests/UnitTests/ICoreStub.cpp | 318 ++++ tests/UnitTests/ICoreStub.h | 125 ++ .../ICryptoNoteProtocolQueryStub.cpp} | 18 +- .../ICryptoNoteProtocolQueryStub.h} | 18 +- tests/UnitTests/INodeStubs.cpp | 693 +++++++++ tests/UnitTests/INodeStubs.h | 150 ++ .../mul_div.cpp => UnitTests/MulDiv.cpp} | 0 .../ParseAmount.cpp} | 4 +- tests/UnitTests/PaymentGateTests.cpp | 266 ++++ tests/UnitTests/Serialization.cpp | 468 ++++++ .../SerializationKV.cpp} | 26 +- .../shuffle.cpp => UnitTests/Shuffle.cpp} | 2 +- .../StringBufferTests.cpp | 0 .../StringViewTests.cpp | 2 +- .../test_BcS.cpp => UnitTests/TestBcS.cpp} | 409 +++-- tests/UnitTests/TestBlockchainExplorer.cpp | 1269 +++++++++++++++ .../TestBlockchainGenerator.cpp | 190 ++- tests/UnitTests/TestBlockchainGenerator.h | 101 ++ .../TestFormatUtils.cpp} | 107 +- .../TestInprocessNode.cpp} | 327 ++-- .../TestJsonValue.cpp} | 0 tests/UnitTests/TestMessageQueue.cpp | 292 ++++ .../test_path.cpp => UnitTests/TestPath.cpp} | 0 .../TestPeerlist.cpp} | 20 +- .../TestProtocolPack.cpp} | 8 +- .../TestTransactionPoolDetach.cpp} | 59 +- .../TestTransfers.cpp} | 36 +- .../TestTransfersConsumer.cpp} | 228 +-- .../TestTransfersContainer.cpp} | 571 +++---- .../TestTransfersContainerKeyImage.cpp} | 221 ++- .../TestTransfersSubscription.cpp} | 24 +- .../TestUpgradeDetector.cpp | 4 +- tests/UnitTests/TestWallet.cpp | 1141 ++++++++++++++ .../TestWalletLegacy.cpp} | 317 ++-- .../TransactionApi.cpp | 76 +- tests/UnitTests/TransactionApiHelpers.cpp | 227 +++ tests/UnitTests/TransactionApiHelpers.h | 178 +++ .../TransactionPool.cpp} | 80 +- .../TransfersObserver.h | 2 +- .../UnitTestsUtils.h} | 0 tests/{unit_tests => UnitTests}/main.cpp | 0 tests/crypto/crypto-tests.h | 10 +- tests/crypto/crypto.cpp | 28 +- tests/crypto/main.cpp | 105 +- tests/integration_tests/BlockchainInfo.h | 69 - tests/integration_tests/Node.cpp | 469 ------ tests/unit_tests/ICoreStub.cpp | 191 --- tests/unit_tests/ICoreStub.h | 100 -- tests/unit_tests/INodeStubs.cpp | 446 ------ tests/unit_tests/INodeStubs.h | 126 -- tests/unit_tests/TestBlockchainGenerator.h | 71 - tests/unit_tests/TransactionApiHelpers.h | 104 -- .../binary_serialization_compatibility.cpp | 535 ------- tests/unit_tests/serialization.cpp | 427 ----- .../serialization_structs_comparators.h | 138 -- tests/unit_tests/test_BlockchainExplorer.cpp | 922 ----------- 573 files changed, 30695 insertions(+), 21213 deletions(-) create mode 100644 include/CryptoNote.h rename src/serialization/json_utils.h => include/CryptoTypes.h (73%) delete mode 100755 include/IMultiWallet.h mode change 100644 => 100755 include/INode.h mode change 100644 => 100755 include/ITransaction.h mode change 100644 => 100755 include/IWallet.h create mode 100644 include/IWalletLegacy.h mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorer.cpp mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorer.h mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp mode change 100644 => 100755 src/BlockchainExplorer/BlockchainExplorerDataBuilder.h rename src/Common/{base58.cpp => Base58.cpp} (96%) rename src/Common/{base58.h => Base58.h} (96%) mode change 100644 => 100755 rename src/Common/{command_line.cpp => CommandLine.cpp} (97%) rename src/Common/{command_line.h => CommandLine.h} (100%) mode change 100644 => 100755 rename src/{CryptoNote/MultisignatureOutput.cpp => Common/MemoryInputStream.cpp} (53%) mode change 100755 => 100644 rename src/{CryptoNote/KeyOutput.h => Common/MemoryInputStream.h} (66%) mode change 100755 => 100644 mode change 100644 => 100755 src/Common/ObserverManager.h mode change 100644 => 100755 src/Common/SignalHandler.cpp mode change 100644 => 100755 src/Common/SignalHandler.h create mode 100644 src/Common/StdInputStream.cpp create mode 100644 src/Common/StdInputStream.h create mode 100644 src/Common/StdOutputStream.cpp create mode 100644 src/Common/StdOutputStream.h rename src/Common/{util.cpp => Util.cpp} (99%) rename src/Common/{util.h => Util.h} (94%) mode change 100644 => 100755 rename src/Common/{varint.h => Varint.h} (99%) mode change 100644 => 100755 create mode 100644 src/Common/VectorOutputStream.cpp create mode 100644 src/Common/VectorOutputStream.h mode change 100755 => 100644 src/Common/static_assert.h rename src/{connectivity_tool/conn_tool.cpp => ConnectivityTool/ConnectivityTool.cpp} (88%) mode change 100644 => 100755 delete mode 100755 src/CryptoNote/BaseTransaction.cpp delete mode 100755 src/CryptoNote/BaseTransaction.h delete mode 100755 src/CryptoNote/Block.cpp delete mode 100755 src/CryptoNote/Block.h delete mode 100755 src/CryptoNote/KeyInput.cpp delete mode 100755 src/CryptoNote/KeyInput.h delete mode 100755 src/CryptoNote/MultisignatureInput.cpp delete mode 100755 src/CryptoNote/MultisignatureInput.h delete mode 100755 src/CryptoNote/Transaction.cpp delete mode 100755 src/CryptoNote/Transaction.h delete mode 100755 src/CryptoNote/UnsignedMultisignatureInput.cpp delete mode 100755 src/CryptoNote/UnsignedTransaction.cpp delete mode 100755 src/CryptoNote/UnsignedTransaction.h rename src/{cryptonote_config.h => CryptoNoteConfig.h} (95%) rename src/{cryptonote_core/account.cpp => CryptoNoteCore/Account.cpp} (70%) mode change 100644 => 100755 rename src/{cryptonote_core/account.h => CryptoNoteCore/Account.h} (73%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/BlockIndex.cpp rename src/{cryptonote_core => CryptoNoteCore}/BlockIndex.h (69%) mode change 100644 => 100755 rename src/{cryptonote_core/blockchain_storage.cpp => CryptoNoteCore/Blockchain.cpp} (59%) create mode 100755 src/CryptoNoteCore/Blockchain.h create mode 100755 src/CryptoNoteCore/BlockchainIndices.cpp create mode 100755 src/CryptoNoteCore/BlockchainIndices.h create mode 100644 src/CryptoNoteCore/BlockchainMessages.cpp create mode 100644 src/CryptoNoteCore/BlockchainMessages.h rename src/{cryptonote_core/checkpoints.cpp => CryptoNoteCore/Checkpoints.cpp} (81%) rename src/{cryptonote_core/checkpoints.h => CryptoNoteCore/Checkpoints.h} (66%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/Core.cpp create mode 100755 src/CryptoNoteCore/Core.h rename src/{cryptonote_core => CryptoNoteCore}/CoreConfig.cpp (91%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/CoreConfig.h (100%) rename src/{payment_service/WalletServiceErrorCodes.cpp => CryptoNoteCore/CryptoNoteBasic.cpp} (79%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/CryptoNoteBasic.h rename src/{cryptonote_core/cryptonote_basic_impl.cpp => CryptoNoteCore/CryptoNoteBasicImpl.cpp} (76%) rename src/{cryptonote_core/cryptonote_basic_impl.h => CryptoNoteCore/CryptoNoteBasicImpl.h} (68%) mode change 100644 => 100755 create mode 100644 src/CryptoNoteCore/CryptoNoteFormatUtils.cpp create mode 100755 src/CryptoNoteCore/CryptoNoteFormatUtils.h rename src/{cryptonote_core/cryptonote_serialization.cpp => CryptoNoteCore/CryptoNoteSerialization.cpp} (55%) rename src/{cryptonote_core/cryptonote_serialization.h => CryptoNoteCore/CryptoNoteSerialization.h} (55%) mode change 100644 => 100755 rename src/{cryptonote_core/cryptonote_stat_info.h => CryptoNoteCore/CryptoNoteStatInfo.h} (96%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/CryptoNoteTools.cpp create mode 100755 src/CryptoNoteCore/CryptoNoteTools.h rename src/{cryptonote_core => CryptoNoteCore}/Currency.cpp (86%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/Currency.h (91%) mode change 100644 => 100755 rename src/{cryptonote_core/difficulty.cpp => CryptoNoteCore/Difficulty.cpp} (91%) mode change 100644 => 100755 rename src/{cryptonote_core/difficulty.h => CryptoNoteCore/Difficulty.h} (93%) mode change 100644 => 100755 rename src/{System/LatchGuard.cpp => CryptoNoteCore/IBlock.cpp} (79%) rename src/{CryptoNote/KeyOutput.cpp => CryptoNoteCore/IBlock.h} (75%) mode change 100755 => 100644 rename src/{cryptonote_core => CryptoNoteCore}/IBlockchainStorageObserver.h (100%) create mode 100755 src/CryptoNoteCore/ICore.h rename src/{cryptonote_core => CryptoNoteCore}/ICoreObserver.h (100%) rename src/{cryptonote_core/i_miner_handler.h => CryptoNoteCore/IMinerHandler.h} (86%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/ITimeProvider.cpp (100%) rename src/{cryptonote_core => CryptoNoteCore}/ITimeProvider.h (100%) rename src/{cryptonote_core => CryptoNoteCore}/ITransactionValidator.h (85%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/ITxPoolObserver.h (100%) create mode 100644 src/CryptoNoteCore/IntrusiveLinkedList.h create mode 100644 src/CryptoNoteCore/MessageQueue.h rename src/{cryptonote_core/miner.cpp => CryptoNoteCore/Miner.cpp} (90%) rename src/{cryptonote_core/miner.h => CryptoNoteCore/Miner.h} (79%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/MinerConfig.cpp (97%) rename src/{cryptonote_core => CryptoNoteCore}/MinerConfig.h (97%) mode change 100644 => 100755 rename src/{cryptonote_core => CryptoNoteCore}/OnceInInterval.h (100%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedMap.cpp (94%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedMap.h (97%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedVector.cpp (94%) rename src/{cryptonote_core => CryptoNoteCore}/SwappedVector.h (93%) create mode 100755 src/CryptoNoteCore/Transaction.cpp rename src/{cryptonote_core => CryptoNoteCore}/TransactionApi.h (69%) mode change 100644 => 100755 rename src/{cryptonote_core/TransactionExtra.h => CryptoNoteCore/TransactionApiExtra.h} (63%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/TransactionExtra.cpp create mode 100755 src/CryptoNoteCore/TransactionExtra.h rename src/{cryptonote_core/tx_pool.cpp => CryptoNoteCore/TransactionPool.cpp} (74%) rename src/{cryptonote_core/tx_pool.h => CryptoNoteCore/TransactionPool.h} (72%) mode change 100644 => 100755 create mode 100755 src/CryptoNoteCore/TransactionPrefixImpl.cpp create mode 100755 src/CryptoNoteCore/TransactionUtils.cpp create mode 100755 src/CryptoNoteCore/TransactionUtils.h rename src/{cryptonote_core => CryptoNoteCore}/UpgradeDetector.cpp (100%) rename src/{cryptonote_core => CryptoNoteCore}/UpgradeDetector.h (99%) mode change 100644 => 100755 rename src/{cryptonote_core/verification_context.h => CryptoNoteCore/VerificationContext.h} (100%) mode change 100644 => 100755 rename src/{cryptonote_protocol/cryptonote_protocol_defs.h => CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h} (77%) mode change 100644 => 100755 rename src/{cryptonote_protocol/cryptonote_protocol_handler.cpp => CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp} (64%) rename src/{cryptonote_protocol/cryptonote_protocol_handler.h => CryptoNoteProtocol/CryptoNoteProtocolHandler.h} (51%) mode change 100644 => 100755 rename src/{cryptonote_protocol/cryptonote_protocol_handler_common.h => CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h} (100%) mode change 100644 => 100755 rename src/{cryptonote_protocol/ICryptonoteProtocolObserver.h => CryptoNoteProtocol/ICryptoNoteProtocolObserver.h} (85%) mode change 100644 => 100755 rename src/{cryptonote_protocol/ICryptonoteProtocolQuery.h => CryptoNoteProtocol/ICryptoNoteProtocolQuery.h} (80%) mode change 100644 => 100755 rename src/{daemon/daemon.cpp => Daemon/Daemon.cpp} (92%) mode change 100644 => 100755 rename src/{daemon/DeamonCommandsHandler.cpp => Daemon/DaemonCommandsHandler.cpp} (90%) mode change 100644 => 100755 rename src/{daemon => Daemon}/DaemonCommandsHandler.h (91%) mode change 100644 => 100755 mode change 100644 => 100755 src/HTTP/HttpParserErrorCodes.cpp mode change 100644 => 100755 src/HTTP/HttpParserErrorCodes.h create mode 100755 src/JsonRpcServer/JsonRpcServer.cpp rename src/{payment_service => JsonRpcServer}/JsonRpcServer.h (61%) mode change 100644 => 100755 rename src/{node_rpc_proxy => NodeRpcProxy}/NodeErrors.cpp (100%) rename src/{node_rpc_proxy => NodeRpcProxy}/NodeErrors.h (100%) create mode 100644 src/NodeRpcProxy/NodeRpcProxy.cpp create mode 100644 src/NodeRpcProxy/NodeRpcProxy.h rename src/{p2p/connection_context.h => P2p/ConnectionContext.h} (72%) mode change 100644 => 100755 create mode 100644 src/P2p/IP2pNodeInternal.cpp rename src/{CryptoNote/UnsignedKeyInput.h => P2p/IP2pNodeInternal.h} (60%) mode change 100755 => 100644 rename src/{p2p => P2p}/LevinProtocol.cpp (73%) rename src/{p2p => P2p}/LevinProtocol.h (60%) mode change 100644 => 100755 rename src/{p2p/net_node.cpp => P2p/NetNode.cpp} (64%) create mode 100644 src/P2p/NetNode.h rename src/{p2p/net_node_common.h => P2p/NetNodeCommon.h} (52%) mode change 100644 => 100755 rename src/{p2p => P2p}/NetNodeConfig.cpp (60%) mode change 100644 => 100755 create mode 100755 src/P2p/NetNodeConfig.h create mode 100644 src/P2p/P2pConnectionProxy.cpp create mode 100644 src/P2p/P2pConnectionProxy.h create mode 100755 src/P2p/P2pContext.cpp create mode 100755 src/P2p/P2pContext.h rename src/{CryptoNote/UnsignedKeyInput.cpp => P2p/P2pContextOwner.cpp} (52%) mode change 100755 => 100644 rename src/{wallet/MultiWallet.h => P2p/P2pContextOwner.h} (65%) rename src/{System/LatchGuard.h => P2p/P2pInterfaces.cpp} (83%) rename src/{CryptoNote/UnsignedMultisignatureInput.h => P2p/P2pInterfaces.h} (66%) mode change 100755 => 100644 rename src/{p2p/p2p_networks.h => P2p/P2pNetworks.h} (100%) mode change 100644 => 100755 create mode 100755 src/P2p/P2pNode.cpp create mode 100755 src/P2p/P2pNode.h create mode 100644 src/P2p/P2pNodeConfig.cpp create mode 100644 src/P2p/P2pNodeConfig.h rename src/{p2p/p2p_protocol_defs.h => P2p/P2pProtocolDefinitions.h} (85%) mode change 100644 => 100755 rename src/{p2p/p2p_protocol_types.h => P2p/P2pProtocolTypes.h} (78%) mode change 100644 => 100755 rename src/{p2p => P2p}/PeerListManager.cpp (59%) mode change 100644 => 100755 rename src/{p2p => P2p}/PeerListManager.h (53%) rename src/{payment_service => PaymentGate}/NodeFactory.cpp (51%) rename src/{payment_service => PaymentGate}/NodeFactory.h (100%) rename src/{payment_service/JsonRpcMessages.cpp => PaymentGate/PaymentServiceJsonRpcMessages.cpp} (79%) mode change 100644 => 100755 rename src/{payment_service/JsonRpcMessages.h => PaymentGate/PaymentServiceJsonRpcMessages.h} (82%) rename src/{payment_service/JsonRpcServer.cpp => PaymentGate/PaymentServiceJsonRpcServer.cpp} (56%) mode change 100644 => 100755 create mode 100644 src/PaymentGate/PaymentServiceJsonRpcServer.h rename src/{payment_service => PaymentGate}/WalletFactory.cpp (79%) mode change 100644 => 100755 rename src/{payment_service => PaymentGate}/WalletFactory.h (91%) rename src/{payment_service => PaymentGate}/WalletService.cpp (52%) mode change 100644 => 100755 rename src/{payment_service => PaymentGate}/WalletService.h (58%) mode change 100644 => 100755 rename src/{payment_service => PaymentGateService}/ConfigurationManager.cpp (96%) mode change 100644 => 100755 rename src/{payment_service => PaymentGateService}/ConfigurationManager.h (94%) mode change 100644 => 100755 create mode 100755 src/PaymentGateService/PaymentGateService.cpp create mode 100644 src/PaymentGateService/PaymentGateService.h rename src/{payment_service => PaymentGateService}/PaymentServiceConfiguration.cpp (89%) rename src/{payment_service => PaymentGateService}/PaymentServiceConfiguration.h (96%) rename src/{payment_service => PaymentGateService}/RpcNodeConfiguration.cpp (100%) rename src/{payment_service => PaymentGateService}/RpcNodeConfiguration.h (100%) create mode 100644 src/PaymentGateService/main.cpp rename src/Platform/OSX/System/{context.c => Context.c} (100%) mode change 100644 => 100755 rename src/Platform/OSX/System/{context.h => Context.h} (100%) mode change 100644 => 100755 rename src/{rpc/core_rpc_server_commands_defs.h => Rpc/CoreRpcServerCommandsDefinitions.h} (77%) mode change 100644 => 100755 rename src/{rpc/core_rpc_server_error_codes.h => Rpc/CoreRpcServerErrorCodes.h} (100%) mode change 100644 => 100755 rename src/{rpc => Rpc}/HttpClient.cpp (84%) rename src/{rpc => Rpc}/HttpClient.h (97%) mode change 100644 => 100755 rename src/{rpc => Rpc}/HttpServer.cpp (83%) mode change 100644 => 100755 rename src/{rpc => Rpc}/HttpServer.h (95%) mode change 100644 => 100755 rename src/{rpc => Rpc}/JsonRpc.cpp (98%) mode change 100644 => 100755 rename src/{rpc => Rpc}/JsonRpc.h (98%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServer.cpp (78%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServer.h (88%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServerConfig.cpp (96%) mode change 100644 => 100755 rename src/{rpc => Rpc}/RpcServerConfig.h (100%) rename src/{serialization => Serialization}/BinaryInputStreamSerializer.cpp (72%) rename src/{serialization => Serialization}/BinaryInputStreamSerializer.h (81%) rename src/{serialization => Serialization}/BinaryOutputStreamSerializer.cpp (82%) rename src/{serialization => Serialization}/BinaryOutputStreamSerializer.h (81%) create mode 100644 src/Serialization/BinarySerializationTools.h rename src/{serialization => Serialization}/ISerializer.h (83%) rename src/{serialization => Serialization}/IStream.h (88%) rename src/{serialization => Serialization}/JsonInputStreamSerializer.cpp (77%) rename src/{serialization => Serialization}/JsonInputStreamSerializer.h (96%) rename src/{serialization => Serialization}/JsonInputValueSerializer.cpp (79%) rename src/{serialization => Serialization}/JsonInputValueSerializer.h (84%) rename src/{serialization => Serialization}/JsonOutputStreamSerializer.cpp (88%) rename src/{serialization => Serialization}/JsonOutputStreamSerializer.h (87%) rename src/{serialization => Serialization}/KVBinaryCommon.h (100%) rename src/{serialization => Serialization}/KVBinaryInputStreamSerializer.cpp (69%) rename src/{serialization => Serialization}/KVBinaryInputStreamSerializer.h (84%) rename src/{serialization => Serialization}/KVBinaryOutputStreamSerializer.cpp (84%) rename src/{serialization => Serialization}/KVBinaryOutputStreamSerializer.h (87%) rename src/{serialization => Serialization}/MemoryStream.cpp (100%) rename src/{serialization => Serialization}/MemoryStream.h (68%) create mode 100644 src/Serialization/SerializationOverloads.h rename src/{serialization => Serialization}/SerializationTools.h (91%) rename src/{simplewallet/password_container.cpp => SimpleWallet/PasswordContainer.cpp} (90%) rename src/{simplewallet/password_container.h => SimpleWallet/PasswordContainer.h} (86%) mode change 100644 => 100755 rename src/{simplewallet/simplewallet.cpp => SimpleWallet/SimpleWallet.cpp} (91%) rename src/{simplewallet/simplewallet.h => SimpleWallet/SimpleWallet.h} (94%) mode change 100644 => 100755 create mode 100755 src/System/Context.h create mode 100755 src/System/ContextGroup.cpp rename src/System/{Latch.h => ContextGroup.h} (67%) rename tests/System/LatchTests.cpp => src/System/ContextGroupTimeout.cpp (64%) create mode 100755 src/System/ContextGroupTimeout.h mode change 100644 => 100755 src/System/EventLock.cpp mode change 100644 => 100755 src/System/EventLock.h delete mode 100755 src/System/Latch.cpp rename src/{CryptoNote/MultisignatureOutput.h => System/OperationTimeout.h} (55%) create mode 100755 src/System/RemoteEventLock.cpp create mode 100755 src/System/RemoteEventLock.h rename src/{transfers => Transfers}/BlockchainSynchronizer.cpp (89%) mode change 100644 => 100755 rename src/{transfers => Transfers}/BlockchainSynchronizer.h (88%) mode change 100644 => 100755 rename src/{transfers => Transfers}/CommonTypes.h (93%) mode change 100644 => 100755 rename src/{transfers => Transfers}/IBlockchainSynchronizer.h (74%) mode change 100644 => 100755 rename src/{transfers => Transfers}/IObservableImpl.h (95%) mode change 100644 => 100755 rename src/{transfers => Transfers}/SynchronizationState.cpp (67%) mode change 100644 => 100755 rename src/{transfers => Transfers}/SynchronizationState.h (75%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersConsumer.cpp (81%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersConsumer.h (66%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersContainer.cpp (92%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersContainer.h (80%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSubscription.cpp (69%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSubscription.h (75%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSynchronizer.cpp (88%) mode change 100644 => 100755 rename src/{transfers => Transfers}/TransfersSynchronizer.h (83%) rename src/{transfers => Transfers}/TypeHelpers.h (71%) rename src/{wallet => Wallet}/LegacyKeysImporter.cpp (60%) rename src/{wallet => Wallet}/LegacyKeysImporter.h (100%) rename src/{wallet => Wallet}/WalletAsyncContextCounter.cpp (100%) rename src/{wallet => Wallet}/WalletAsyncContextCounter.h (100%) rename src/{wallet => Wallet}/WalletErrors.cpp (100%) rename src/{wallet => Wallet}/WalletErrors.h (96%) create mode 100755 src/Wallet/WalletGreen.cpp create mode 100755 src/Wallet/WalletGreen.h create mode 100644 src/Wallet/WalletIndices.h rename src/{wallet/wallet_rpc_server.cpp => Wallet/WalletRpcServer.cpp} (87%) rename src/{wallet/wallet_rpc_server.h => Wallet/WalletRpcServer.h} (92%) mode change 100644 => 100755 rename src/{wallet/wallet_rpc_server_commans_defs.h => Wallet/WalletRpcServerCommandsDefinitions.h} (95%) mode change 100644 => 100755 rename src/{wallet/wallet_rpc_server_error_codes.h => Wallet/WalletRpcServerErrorCodes.h} (100%) mode change 100644 => 100755 create mode 100755 src/Wallet/WalletSerialization.cpp create mode 100755 src/Wallet/WalletSerialization.h rename src/{wallet => WalletLegacy}/KeysStorage.cpp (86%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/KeysStorage.h (84%) rename src/{wallet => WalletLegacy}/WalletHelper.cpp (86%) rename src/{wallet => WalletLegacy}/WalletHelper.h (75%) rename src/{wallet/Wallet.cpp => WalletLegacy/WalletLegacy.cpp} (59%) rename src/{wallet/Wallet.h => WalletLegacy/WalletLegacy.h} (62%) mode change 100644 => 100755 rename src/{wallet/WalletEvent.h => WalletLegacy/WalletLegacyEvent.h} (50%) mode change 100644 => 100755 rename src/{wallet/WalletSerialization.cpp => WalletLegacy/WalletLegacySerialization.cpp} (67%) mode change 100644 => 100755 rename src/{wallet/WalletSerialization.h => WalletLegacy/WalletLegacySerialization.h} (81%) create mode 100755 src/WalletLegacy/WalletLegacySerializer.cpp rename src/{wallet/WalletSerializer.h => WalletLegacy/WalletLegacySerializer.h} (82%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletRequest.h (89%) rename src/{wallet => WalletLegacy}/WalletSendTransactionContext.h (95%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletTransactionSender.cpp (62%) rename src/{wallet => WalletLegacy}/WalletTransactionSender.h (56%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUnconfirmedTransactions.cpp (77%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUnconfirmedTransactions.h (67%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUserTransactionsCache.cpp (70%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUserTransactionsCache.h (66%) mode change 100644 => 100755 rename src/{wallet => WalletLegacy}/WalletUtils.h (88%) mode change 100644 => 100755 mode change 100644 => 100755 src/crypto/chacha8.c mode change 100644 => 100755 src/crypto/crypto.cpp mode change 100644 => 100755 src/crypto/crypto.h mode change 100644 => 100755 src/crypto/hash-ops.h mode change 100644 => 100755 src/crypto/skein_port.h mode change 100644 => 100755 src/crypto/slow-hash.c delete mode 100644 src/cryptonote_core/BlockIndex.cpp delete mode 100755 src/cryptonote_core/ICore.h delete mode 100644 src/cryptonote_core/Transaction.cpp delete mode 100644 src/cryptonote_core/blockchain_storage.h delete mode 100644 src/cryptonote_core/blockchain_storage_boost_serialization.h delete mode 100755 src/cryptonote_core/cryptonote_basic.cpp delete mode 100644 src/cryptonote_core/cryptonote_basic.h delete mode 100644 src/cryptonote_core/cryptonote_core.cpp delete mode 100644 src/cryptonote_core/cryptonote_core.h delete mode 100644 src/cryptonote_core/cryptonote_format_utils.cpp delete mode 100644 src/cryptonote_core/cryptonote_format_utils.h delete mode 100644 src/cryptonote_core/tx_extra.h delete mode 100644 src/cryptonote_protocol/blobdatatype.h delete mode 100644 src/node_rpc_proxy/InitState.h delete mode 100644 src/node_rpc_proxy/NodeRpcProxy.cpp delete mode 100644 src/node_rpc_proxy/NodeRpcProxy.h delete mode 100644 src/p2p/NetNodeConfig.h delete mode 100644 src/p2p/net_node.h delete mode 100644 src/p2p/net_peerlist_boost_serialization.h delete mode 100644 src/payment_service/WalletObservers.cpp delete mode 100644 src/payment_service/WalletObservers.h delete mode 100644 src/payment_service/WalletServiceErrorCodes.h delete mode 100644 src/payment_service/main.cpp delete mode 100644 src/serialization/SerializationOverloads.cpp delete mode 100644 src/serialization/SerializationOverloads.h delete mode 100644 src/serialization/binary_archive.h delete mode 100644 src/serialization/binary_utils.h delete mode 100644 src/serialization/crypto.h delete mode 100644 src/serialization/json_archive.h delete mode 100644 src/serialization/serialization.h delete mode 100644 src/serialization/string.h delete mode 100644 src/serialization/variant.h delete mode 100644 src/serialization/vector.h delete mode 100644 src/transfers/SerializationHelpers.h delete mode 100644 src/wallet/SyncWallet.cpp delete mode 100644 src/wallet/SyncWallet.h delete mode 100644 src/wallet/WalletSerializer.cpp rename src/cryptonote_core/account_boost_serialization.h => tests/CoreTests/AccountBoostSerialization.h (75%) mode change 100644 => 100755 rename tests/{core_tests/block_reward.cpp => CoreTests/BlockReward.cpp} (91%) rename tests/{core_tests/block_reward.h => CoreTests/BlockReward.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/block_validation.cpp => CoreTests/BlockValidation.cpp} (80%) rename tests/{core_tests/block_validation.h => CoreTests/BlockValidation.h} (99%) mode change 100644 => 100755 rename src/Common/boost_serialization_helper.h => tests/CoreTests/BoostSerializationHelper.h (99%) mode change 100644 => 100755 rename tests/{core_tests/chain_split_1.cpp => CoreTests/ChainSplit1.cpp} (99%) rename tests/{core_tests/chain_split_1.h => CoreTests/ChainSplit1.h} (99%) mode change 100644 => 100755 rename tests/{core_tests/chain_switch_1.cpp => CoreTests/ChainSwitch1.cpp} (94%) rename tests/{core_tests/chain_switch_1.h => CoreTests/ChainSwitch1.h} (87%) mode change 100644 => 100755 rename tests/{core_tests/chaingen.cpp => CoreTests/Chaingen.cpp} (72%) rename tests/{core_tests/chaingen.h => CoreTests/Chaingen.h} (91%) mode change 100644 => 100755 rename tests/{core_tests/chaingen001.cpp => CoreTests/Chaingen001.cpp} (97%) rename tests/{core_tests/chaingen001.h => CoreTests/Chaingen001.h} (96%) mode change 100644 => 100755 rename tests/{core_tests/chaingen_main.cpp => CoreTests/ChaingenMain.cpp} (96%) rename src/cryptonote_core/cryptonote_boost_serialization.h => tests/CoreTests/CryptoNoteBoostSerialization.h (53%) mode change 100644 => 100755 rename tests/{core_tests/double_spend.cpp => CoreTests/DoubleSpend.cpp} (94%) rename tests/{core_tests/double_spend.h => CoreTests/DoubleSpend.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/integer_overflow.cpp => CoreTests/IntegerOverflow.cpp} (74%) rename tests/{core_tests/integer_overflow.h => CoreTests/IntegerOverflow.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/random_outs.cpp => CoreTests/RandomOuts.cpp} (98%) rename tests/{core_tests/random_outs.h => CoreTests/RandomOuts.h} (98%) mode change 100644 => 100755 rename tests/{core_tests/ring_signature_1.cpp => CoreTests/RingSignature.cpp} (94%) rename tests/{core_tests/ring_signature_1.h => CoreTests/RingSignature.h} (90%) mode change 100644 => 100755 rename tests/{core_tests => CoreTests}/TestGenerator.h (80%) rename tests/{core_tests => CoreTests}/TransactionBuilder.cpp (57%) rename tests/{core_tests => CoreTests}/TransactionBuilder.h (69%) rename tests/{core_tests/transaction_tests.cpp => CoreTests/TransactionTests.cpp} (54%) rename tests/{core_tests/transaction_tests.h => CoreTests/TransactionTests.h} (100%) mode change 100644 => 100755 rename tests/{core_tests/tx_validation.cpp => CoreTests/TransactionValidation.cpp} (72%) rename tests/{core_tests/tx_validation.h => CoreTests/TransactionValidation.h} (98%) mode change 100644 => 100755 rename src/Common/unordered_containers_boost_serialization.h => tests/CoreTests/UnorderedContainersBoostSerialization.h (100%) mode change 100644 => 100755 rename tests/{core_tests/upgrade.cpp => CoreTests/Upgrade.cpp} (91%) rename tests/{core_tests/upgrade.h => CoreTests/Upgrade.h} (94%) mode change 100644 => 100755 rename tests/{core_tests => CoreTests}/double_spend.inl (91%) rename tests/{data => Data}/account-002bee2f8e16f5de4db0d3b8ce9227c8c0b7f9688348b028e022cb43f210968b40a69cdc8531fd4a2e7c9e144eec48bb477733d70ce5f9b85338a07cb10b849ad8fb (100%) rename tests/{data => Data}/account-007af2d7c5ffd8f69005debae820207820805e28c7d7a16714591143f56fb51e2b91ad0c1a535567e6292b321773df5e5aaace00fe767c4f09de452838575357ca9f (100%) rename tests/{data => Data}/account-009b82d66dfaaba55a581913fa09d6c5bebe179cd73731781265c96e9e630dcd27fd5d20e7f1d0fa42619de9ca8fe4c0659f6959b2bebb15079cdaed07a442a78486 (100%) rename tests/{data => Data}/account-00aff84db50d6a54dd56051379f6c336fdd330d1cb11e7523bbf71f30b1ae760fa47ace8679b6486f79429980fd2331715a631f5729db284eb1fc6f108aeb7a7f4fe (100%) rename tests/{difficulty/difficulty.cpp => Difficulty/Difficulty.cpp} (96%) mode change 100644 => 100755 rename tests/{difficulty => Difficulty}/data.txt (100%) rename tests/{difficulty => Difficulty}/generate-data (100%) rename tests/{hash => Hash}/main.cpp (87%) rename tests/{hash => Hash}/tests-extra-blake.txt (100%) rename tests/{hash => Hash}/tests-extra-groestl.txt (100%) rename tests/{hash => Hash}/tests-extra-jh.txt (100%) rename tests/{hash => Hash}/tests-extra-skein.txt (100%) rename tests/{hash => Hash}/tests-fast.txt (100%) rename tests/{hash => Hash}/tests-slow.txt (100%) rename tests/{hash => Hash}/tests-tree.txt (100%) rename tests/{hash-target.cpp => HashTarget.cpp} (93%) mode change 100644 => 100755 rename tests/{integration_test_lib/BaseFunctionalTest.cpp => IntegrationTestLib/BaseFunctionalTests.cpp} (81%) rename tests/{integration_test_lib/BaseFunctionalTest.h => IntegrationTestLib/BaseFunctionalTests.h} (84%) rename tests/{integration_test_lib => IntegrationTestLib}/InProcTestNode.cpp (78%) rename tests/{integration_test_lib => IntegrationTestLib}/InProcTestNode.h (82%) rename tests/{integration_test_lib => IntegrationTestLib}/Logger.cpp (97%) rename tests/{integration_test_lib => IntegrationTestLib}/Logger.h (96%) rename tests/{integration_test_lib => IntegrationTestLib}/NetworkConfiguration.h (93%) create mode 100644 tests/IntegrationTestLib/NodeCallback.h rename tests/{integration_test_lib => IntegrationTestLib}/NodeObserver.h (89%) create mode 100644 tests/IntegrationTestLib/ObservableValue.h rename tests/{integration_test_lib => IntegrationTestLib}/Process.cpp (100%) rename tests/{integration_test_lib => IntegrationTestLib}/Process.h (100%) rename tests/{integration_test_lib => IntegrationTestLib}/RPCTestNode.cpp (83%) rename tests/{integration_test_lib => IntegrationTestLib}/RPCTestNode.h (53%) rename tests/{integration_test_lib => IntegrationTestLib}/TestNetwork.cpp (89%) rename tests/{integration_test_lib => IntegrationTestLib}/TestNetwork.h (95%) rename tests/{integration_test_lib => IntegrationTestLib}/TestNode.h (66%) rename tests/{integration_test_lib/TestWallet.cpp => IntegrationTestLib/TestWalletLegacy.cpp} (70%) rename tests/{integration_test_lib/TestWallet.h => IntegrationTestLib/TestWalletLegacy.h} (71%) create mode 100644 tests/IntegrationTests/BaseTests.h rename tests/{integration_tests => IntegrationTests}/IntegrationTests.cpp (85%) rename tests/{integration_tests => IntegrationTests}/MultiVersion.cpp (88%) create mode 100644 tests/IntegrationTests/Node.cpp rename tests/{integration_tests/WalletObserver.h => IntegrationTests/WalletLegacyObserver.h} (84%) create mode 100644 tests/IntegrationTests/WalletLegacyTests.cpp rename tests/{integration_tests => IntegrationTests}/main.cpp (94%) rename tests/{io.h => Io.h} (100%) mode change 100644 => 100755 rename tests/{node_rpc_proxy_test/node_rpc_proxy_test.cpp => NodeRpcProxyTests/NodeRpcProxyTests.cpp} (96%) mode change 100644 => 100755 rename tests/{performance_tests/check_ring_signature.h => PerformanceTests/CheckRingSignature.h} (63%) mode change 100644 => 100755 rename tests/{performance_tests/construct_tx.h => PerformanceTests/ConstructTransaction.h} (72%) mode change 100644 => 100755 rename tests/{performance_tests/cn_slow_hash.h => PerformanceTests/CryptoNoteSlowHash.h} (87%) mode change 100644 => 100755 rename tests/{performance_tests/derive_public_key.h => PerformanceTests/DerivePublicKey.h} (71%) mode change 100644 => 100755 rename tests/{performance_tests/derive_secret_key.h => PerformanceTests/DeriveSecretKey.h} (72%) mode change 100644 => 100755 rename tests/{performance_tests/generate_key_derivation.h => PerformanceTests/GenerateKeyDerivation.h} (80%) mode change 100644 => 100755 rename tests/{performance_tests/generate_key_image.h => PerformanceTests/GenerateKeyImage.h} (66%) mode change 100644 => 100755 rename tests/{performance_tests/generate_key_image_helper.h => PerformanceTests/GenerateKeyImageHelper.h} (78%) mode change 100644 => 100755 rename tests/{performance_tests/is_out_to_acc.h => PerformanceTests/IsOutToAccount.h} (73%) mode change 100644 => 100755 rename tests/{performance_tests/multi_tx_test_base.h => PerformanceTests/MultiTransactionTestBase.h} (64%) mode change 100644 => 100755 rename tests/{performance_tests/performance_tests.h => PerformanceTests/PerformanceTests.h} (100%) mode change 100644 => 100755 rename tests/{performance_tests/performance_utils.h => PerformanceTests/PerformanceUtils.h} (100%) mode change 100644 => 100755 rename tests/{performance_tests/single_tx_test_base.h => PerformanceTests/SingleTransactionTestBase.h} (76%) mode change 100644 => 100755 rename tests/{performance_tests => PerformanceTests}/main.cpp (86%) create mode 100755 tests/System/ContextGroupTests.cpp create mode 100644 tests/System/ContextGroupTimeoutTests.cpp create mode 100755 tests/System/ContextTests.cpp create mode 100644 tests/System/OperationTimeoutTests.cpp rename tests/{transfers_tests/globals.h => TransfersTests/Globals.h} (77%) mode change 100644 => 100755 create mode 100755 tests/TransfersTests/TestNodeRpcProxy.cpp rename tests/{transfers_tests/test_TxPoolSync.cpp => TransfersTests/TestTxPoolSync.cpp} (56%) mode change 100644 => 100755 rename tests/{transfers_tests/tests.cpp => TransfersTests/Tests.cpp} (68%) rename tests/{transfers_tests => TransfersTests}/main.cpp (95%) rename tests/{unit_tests => UnitTests}/ArrayRefTests.cpp (99%) rename tests/{unit_tests => UnitTests}/ArrayViewTests.cpp (99%) rename tests/{unit_tests/base58.cpp => UnitTests/Base58.cpp} (90%) mode change 100644 => 100755 create mode 100755 tests/UnitTests/BinarySerializationCompatibility.cpp rename tests/{unit_tests/block_reward.cpp => UnitTests/BlockReward.cpp} (99%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/BlockingQueue.cpp (99%) rename tests/{unit_tests/chacha8.cpp => UnitTests/Chacha8.cpp} (97%) mode change 100644 => 100755 rename tests/{unit_tests/checkpoints.cpp => UnitTests/Checkpoints.cpp} (98%) mode change 100644 => 100755 rename tests/{unit_tests/decompose_amount_into_digits.cpp => UnitTests/DecomposeAmountIntoDigits.cpp} (98%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/EventWaiter.cpp (100%) rename tests/{unit_tests => UnitTests}/EventWaiter.h (100%) create mode 100755 tests/UnitTests/ICoreStub.cpp create mode 100644 tests/UnitTests/ICoreStub.h rename tests/{unit_tests/ICryptonoteProtocolQueryStub.cpp => UnitTests/ICryptoNoteProtocolQueryStub.cpp} (61%) rename tests/{unit_tests/ICryptonoteProtocolQueryStub.h => UnitTests/ICryptoNoteProtocolQueryStub.h} (69%) create mode 100644 tests/UnitTests/INodeStubs.cpp create mode 100644 tests/UnitTests/INodeStubs.h rename tests/{unit_tests/mul_div.cpp => UnitTests/MulDiv.cpp} (100%) mode change 100644 => 100755 rename tests/{unit_tests/parse_amount.cpp => UnitTests/ParseAmount.cpp} (97%) mode change 100644 => 100755 create mode 100644 tests/UnitTests/PaymentGateTests.cpp create mode 100755 tests/UnitTests/Serialization.cpp rename tests/{unit_tests/serialization_kv.cpp => UnitTests/SerializationKV.cpp} (81%) mode change 100644 => 100755 rename tests/{unit_tests/shuffle.cpp => UnitTests/Shuffle.cpp} (97%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/StringBufferTests.cpp (100%) rename tests/{unit_tests => UnitTests}/StringViewTests.cpp (99%) rename tests/{unit_tests/test_BcS.cpp => UnitTests/TestBcS.cpp} (65%) create mode 100755 tests/UnitTests/TestBlockchainExplorer.cpp rename tests/{unit_tests => UnitTests}/TestBlockchainGenerator.cpp (50%) create mode 100644 tests/UnitTests/TestBlockchainGenerator.h rename tests/{unit_tests/test_format_utils.cpp => UnitTests/TestFormatUtils.cpp} (55%) mode change 100644 => 100755 rename tests/{unit_tests/test_inprocess_node.cpp => UnitTests/TestInprocessNode.cpp} (73%) rename tests/{unit_tests/test_JsonValue.cpp => UnitTests/TestJsonValue.cpp} (100%) mode change 100644 => 100755 create mode 100644 tests/UnitTests/TestMessageQueue.cpp rename tests/{unit_tests/test_path.cpp => UnitTests/TestPath.cpp} (100%) mode change 100644 => 100755 rename tests/{unit_tests/test_peerlist.cpp => UnitTests/TestPeerlist.cpp} (73%) mode change 100644 => 100755 rename tests/{unit_tests/test_protocol_pack.cpp => UnitTests/TestProtocolPack.cpp} (84%) mode change 100644 => 100755 rename tests/{unit_tests/test_tx_pool_detach.cpp => UnitTests/TestTransactionPoolDetach.cpp} (88%) rename tests/{unit_tests/test_transfers.cpp => UnitTests/TestTransfers.cpp} (92%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersConsumer.cpp => UnitTests/TestTransfersConsumer.cpp} (83%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersContainer.cpp => UnitTests/TestTransfersContainer.cpp} (67%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersContainerKeyImage.cpp => UnitTests/TestTransfersContainerKeyImage.cpp} (79%) mode change 100644 => 100755 rename tests/{unit_tests/test_TransfersSubscription.cpp => UnitTests/TestTransfersSubscription.cpp} (83%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/TestUpgradeDetector.cpp (99%) create mode 100755 tests/UnitTests/TestWallet.cpp rename tests/{unit_tests/test_wallet.cpp => UnitTests/TestWalletLegacy.cpp} (84%) rename tests/{unit_tests => UnitTests}/TransactionApi.cpp (83%) create mode 100755 tests/UnitTests/TransactionApiHelpers.cpp create mode 100644 tests/UnitTests/TransactionApiHelpers.h rename tests/{unit_tests/tx_pool.cpp => UnitTests/TransactionPool.cpp} (89%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/TransfersObserver.h (95%) rename tests/{unit_tests/unit_tests_utils.h => UnitTests/UnitTestsUtils.h} (100%) mode change 100644 => 100755 rename tests/{unit_tests => UnitTests}/main.cpp (100%) delete mode 100644 tests/integration_tests/BlockchainInfo.h delete mode 100644 tests/integration_tests/Node.cpp delete mode 100755 tests/unit_tests/ICoreStub.cpp delete mode 100644 tests/unit_tests/ICoreStub.h delete mode 100644 tests/unit_tests/INodeStubs.cpp delete mode 100644 tests/unit_tests/INodeStubs.h delete mode 100644 tests/unit_tests/TestBlockchainGenerator.h delete mode 100644 tests/unit_tests/TransactionApiHelpers.h delete mode 100644 tests/unit_tests/binary_serialization_compatibility.cpp delete mode 100644 tests/unit_tests/serialization.cpp delete mode 100644 tests/unit_tests/serialization_structs_comparators.h delete mode 100644 tests/unit_tests/test_BlockchainExplorer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e7873905..8aa392adc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(CMAKE_CONFIGURATION_TYPES Debug RelWithDebInfo Release CACHE TYPE INTERNAL) set(CMAKE_SKIP_INSTALL_RULES ON) set(CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY ON) set(CMAKE_SUPPRESS_REGENERATION ON) -#enable_testing() +enable_testing() project(Bytecoin) @@ -62,11 +62,14 @@ else() else() set(MINGW_FLAG "") endif() + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_C_COMPILER_VERSION VERSION_LESS 5.1)) + set(WARNINGS "${WARNINGS} -Wno-error=odr") + endif() set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes") set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${MINGW_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG} -maes") if(NOT APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG} -maes") if(APPLE) diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index e3a4f0b20a..fa94c0708a 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,9 @@ +Release notes 1.0.6 + +- High-level API update +- Aggregate multi-addresses for Bytecoin RPC Wallet +- Wallet synchronization speed increase + Release notes 1.0.5 - High-level API for blockchain explorer diff --git a/include/BlockchainExplorerData.h b/include/BlockchainExplorerData.h index 6e476da6f1..6ad6d80a82 100644 --- a/include/BlockchainExplorerData.h +++ b/include/BlockchainExplorerData.h @@ -21,6 +21,8 @@ #include #include +#include "CryptoTypes.h" + #include namespace CryptoNote { @@ -32,17 +34,17 @@ enum class TransactionRemoveReason : uint8_t }; struct TransactionOutputToKeyDetails { - std::array txOutKey; + Crypto::PublicKey txOutKey; }; struct TransactionOutputMultisignatureDetails { - std::vector> keys; + std::vector keys; uint32_t requiredSignatures; }; struct TransactionOutputDetails { uint64_t amount; - uint64_t globalIndex; + uint32_t globalIndex; boost::variant< TransactionOutputToKeyDetails, @@ -50,17 +52,17 @@ struct TransactionOutputDetails { }; struct TransactionOutputReferenceDetails { - std::array transactionHash; + Crypto::Hash transactionHash; size_t number; }; struct TransactionInputGenerateDetails { - uint64_t height; + uint32_t height; }; struct TransactionInputToKeyDetails { - std::vector keyOffsets; - std::array keyImage; + std::vector outputIndexes; + Crypto::KeyImage keyImage; uint64_t mixin; TransactionOutputReferenceDetails output; }; @@ -81,13 +83,13 @@ struct TransactionInputDetails { struct TransactionExtraDetails { std::vector padding; - std::vector> publicKey; + std::vector publicKey; std::vector nonce; std::vector raw; }; struct TransactionDetails { - std::array hash; + Crypto::Hash hash; uint64_t size; uint64_t fee; uint64_t totalInputsAmount; @@ -95,12 +97,12 @@ struct TransactionDetails { uint64_t mixin; uint64_t unlockTime; uint64_t timestamp; - std::array paymentId; + Crypto::Hash paymentId; bool inBlockchain; - std::array blockHash; - uint64_t blockHeight; + Crypto::Hash blockHash; + uint32_t blockHeight; TransactionExtraDetails extra; - std::vector>> signatures; + std::vector> signatures; std::vector inputs; std::vector outputs; }; @@ -109,11 +111,11 @@ struct BlockDetails { uint8_t majorVersion; uint8_t minorVersion; uint64_t timestamp; - std::array prevBlockHash; + Crypto::Hash prevBlockHash; uint32_t nonce; bool isOrphaned; - uint64_t height; - std::array hash; + uint32_t height; + Crypto::Hash hash; uint64_t difficulty; uint64_t reward; uint64_t baseReward; diff --git a/include/CryptoNote.h b/include/CryptoNote.h new file mode 100644 index 0000000000..2eecebdf45 --- /dev/null +++ b/include/CryptoNote.h @@ -0,0 +1,114 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include "CryptoTypes.h" + +namespace CryptoNote { + +struct BaseInput { + uint32_t blockIndex; +}; + +struct KeyInput { + uint64_t amount; + std::vector outputIndexes; + Crypto::KeyImage keyImage; +}; + +struct MultisignatureInput { + uint64_t amount; + uint8_t signatureCount; + uint32_t outputIndex; +}; + +struct KeyOutput { + Crypto::PublicKey key; +}; + +struct MultisignatureOutput { + std::vector keys; + uint8_t requiredSignatureCount; +}; + +typedef boost::variant TransactionInput; + +typedef boost::variant TransactionOutputTarget; + +struct TransactionOutput { + uint64_t amount; + TransactionOutputTarget target; +}; + +struct TransactionPrefix { + uint8_t version; + uint64_t unlockTime; + std::vector inputs; + std::vector outputs; + std::vector extra; +}; + +struct Transaction : public TransactionPrefix { + std::vector> signatures; +}; + +struct ParentBlock { + uint8_t majorVersion; + uint8_t minorVersion; + Crypto::Hash previousBlockHash; + uint16_t transactionCount; + std::vector baseTransactionBranch; + Transaction baseTransaction; + std::vector blockchainBranch; +}; + +struct BlockHeader { + uint8_t majorVersion; + uint8_t minorVersion; + uint32_t nonce; + uint64_t timestamp; + Crypto::Hash previousBlockHash; +}; + +struct Block : public BlockHeader { + ParentBlock parentBlock; + Transaction baseTransaction; + std::vector transactionHashes; +}; + +struct AccountPublicAddress { + Crypto::PublicKey spendPublicKey; + Crypto::PublicKey viewPublicKey; +}; + +struct AccountKeys { + AccountPublicAddress address; + Crypto::SecretKey spendSecretKey; + Crypto::SecretKey viewSecretKey; +}; + +struct KeyPair { + Crypto::PublicKey publicKey; + Crypto::SecretKey secretKey; +}; + +using BinaryArray = std::vector; + +} diff --git a/src/serialization/json_utils.h b/include/CryptoTypes.h similarity index 73% rename from src/serialization/json_utils.h rename to include/CryptoTypes.h index b469e5152e..e9b18fb6ef 100644 --- a/src/serialization/json_utils.h +++ b/include/CryptoTypes.h @@ -17,18 +17,32 @@ #pragma once -#include -#include "json_archive.h" - -namespace serialization { - -template -std::string dump_json(T &v) -{ - std::stringstream ostr; - json_archive oar(ostr); - assert(serialization::serialize(oar, v)); - return ostr.str(); +#include + +namespace Crypto { + +struct Hash { + uint8_t data[32]; +}; + +struct PublicKey { + uint8_t data[32]; +}; + +struct SecretKey { + uint8_t data[32]; +}; + +struct KeyDerivation { + uint8_t data[32]; +}; + +struct KeyImage { + uint8_t data[32]; +}; + +struct Signature { + uint8_t data[64]; }; -} // namespace serialization +} diff --git a/include/IBlockchainExplorer.h b/include/IBlockchainExplorer.h index 3fa656812e..b2c8126103 100644 --- a/include/IBlockchainExplorer.h +++ b/include/IBlockchainExplorer.h @@ -29,7 +29,7 @@ class IBlockchainObserver { virtual ~IBlockchainObserver() {} virtual void blockchainUpdated(const std::vector& newBlocks, const std::vector& orphanedBlocks) {} - virtual void poolUpdated(const std::vector& newTransactions, const std::vector, TransactionRemoveReason>>& removedTransactions) {} + virtual void poolUpdated(const std::vector& newTransactions, const std::vector>& removedTransactions) {} virtual void blockchainSynchronized(const BlockDetails& topBlock) {} }; @@ -44,13 +44,16 @@ class IBlockchainExplorer { virtual void init() = 0; virtual void shutdown() = 0; - virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) = 0; - virtual bool getBlocks(const std::vector>& blockHashes, std::vector& blocks) = 0; + virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) = 0; + virtual bool getBlocks(const std::vector& blockHashes, std::vector& blocks) = 0; + virtual bool getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) = 0; virtual bool getBlockchainTop(BlockDetails& topBlock) = 0; - virtual bool getTransactions(const std::vector>& transactionHashes, std::vector& transactions) = 0; - virtual bool getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) = 0; + virtual bool getTransactions(const std::vector& transactionHashes, std::vector& transactions) = 0; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) = 0; + virtual bool getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) = 0; + virtual bool getPoolState(const std::vector& knownPoolTransactionHashes, Crypto::Hash knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector& removedTransactions) = 0; virtual uint64_t getRewardBlocksWindow() = 0; virtual uint64_t getFullRewardMaxBlockSize(uint8_t majorVersion) = 0; diff --git a/include/IMultiWallet.h b/include/IMultiWallet.h deleted file mode 100755 index a6eb2c879f..0000000000 --- a/include/IMultiWallet.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -namespace CryptoNote { - -enum class MultiWalletTransactionState : uint8_t { - FAILED -}; - -struct MultiWalletTransaction { - MultiWalletTransactionState state; - uint64_t timestamp; - uint64_t blockHeight; - std::array hash; - bool isBase; - int64_t totalAmount; - uint64_t fee; - uint64_t creationTime; - uint64_t unlockTime; - std::string extra; -}; - -struct MultiWalletTransfer { - std::string address; - uint64_t amount; -}; - -class IMultiWallet { -public: - virtual ~IMultiWallet() {} - - virtual void initialize(const std::string& password) = 0; - virtual void load(std::istream& source, const std::string& password) = 0; - virtual void shutdown() = 0; - - virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; - virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) = 0; - - virtual std::size_t getAddressCount() const = 0; - virtual std::string getAddress(std::size_t index) const = 0; - virtual std::string createAddress() = 0; - virtual std::string createAddress(const std::array& spendPublicKey, const std::array& spendSecretKey) = 0; - virtual void deleteAddress(const std::string& address) = 0; - - virtual uint64_t getActualBalance() const = 0; - virtual uint64_t getActualBalance(const std::string& address) const = 0; - virtual uint64_t getPendingBalance() const = 0; - virtual uint64_t getPendingBalance(const std::string& address) const = 0; - - virtual std::size_t getTransactionCount() const = 0; - virtual MultiWalletTransaction getTransaction(std::size_t transactionIndex) const = 0; - virtual std::size_t getTransactionTransferCount(std::size_t transactionIndex) const = 0; - virtual MultiWalletTransfer getTransactionTransfer(std::size_t transactionIndex, std::size_t transferIndex) const = 0; - - virtual std::size_t transfer(const MultiWalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual std::size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual std::size_t transfer(const std::string& sourceAddress, const MultiWalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual std::size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - - virtual void start() = 0; - virtual void stop() = 0; - virtual void refresh() = 0; -}; - -} diff --git a/include/INode.h b/include/INode.h old mode 100644 new mode 100755 index 9c53317f32..a8d1d5e6d2 --- a/include/INode.h +++ b/include/INode.h @@ -23,11 +23,12 @@ #include #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "rpc/core_rpc_server_commands_defs.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" #include "BlockchainExplorerData.h" +#include "ITransaction.h" namespace CryptoNote { @@ -35,15 +36,15 @@ class INodeObserver { public: virtual ~INodeObserver() {} virtual void peerCountUpdated(size_t count) {} - virtual void localBlockchainUpdated(uint64_t height) {} - virtual void lastKnownBlockHeightUpdated(uint64_t height) {} + virtual void localBlockchainUpdated(uint32_t height) {} + virtual void lastKnownBlockHeightUpdated(uint32_t height) {} virtual void poolChanged() {} - virtual void blockchainSynchronized(uint64_t topHeight) {} + virtual void blockchainSynchronized(uint32_t topHeight) {} }; struct OutEntry { - uint64_t outGlobalIndex; - crypto::public_key outKey; + uint32_t outGlobalIndex; + Crypto::PublicKey outKey; }; struct OutsForAmount { @@ -51,10 +52,16 @@ struct OutsForAmount { std::vector outs; }; -struct BlockCompleteEntry { - crypto::hash blockHash; - CryptoNote::blobdata block; - std::list txs; +struct TransactionShortInfo { + Crypto::Hash txId; + TransactionPrefix txPrefix; +}; + +struct BlockShortEntry { + Crypto::Hash blockHash; + bool hasBlock; + CryptoNote::Block block; + std::vector txsShortInfo; }; class INode { @@ -69,22 +76,26 @@ class INode { virtual bool shutdown() = 0; virtual size_t getPeerCount() const = 0; - virtual uint64_t getLastLocalBlockHeight() const = 0; - virtual uint64_t getLastKnownBlockHeight() const = 0; - virtual uint64_t getLocalBlockCount() const = 0; - virtual uint64_t getKnownBlockCount() const = 0; + virtual uint32_t getLastLocalBlockHeight() const = 0; + virtual uint32_t getLastKnownBlockHeight() const = 0; + virtual uint32_t getLocalBlockCount() const = 0; + virtual uint32_t getKnownBlockCount() const = 0; virtual uint64_t getLastLocalBlockTimestamp() const = 0; virtual void relayTransaction(const Transaction& transaction, const Callback& callback) = 0; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) = 0; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; - virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) = 0; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, const Callback& callback) = 0; - - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) = 0; - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) = 0; - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) = 0; + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) = 0; + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) = 0; + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) = 0; + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) = 0; + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) = 0; + + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) = 0; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) = 0; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) = 0; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) = 0; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) = 0; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) = 0; virtual void isSynchronized(bool& syncStatus, const Callback& callback) = 0; }; diff --git a/include/ITransaction.h b/include/ITransaction.h old mode 100644 new mode 100755 index 9c9fe77362..e65f422287 --- a/include/ITransaction.h +++ b/include/ITransaction.h @@ -19,64 +19,27 @@ #include #include +#include #include -namespace CryptoNote { - -typedef std::array PublicKey; -typedef std::array SecretKey; -typedef std::array KeyImage; -typedef std::array Hash; -typedef std::vector Blob; - -struct AccountAddress { - PublicKey spendPublicKey; - PublicKey viewPublicKey; -}; +#include "CryptoNote.h" -struct AccountKeys { - AccountAddress address; - SecretKey spendSecretKey; - SecretKey viewSecretKey; -}; +namespace CryptoNote { namespace TransactionTypes { enum class InputType : uint8_t { Invalid, Key, Multisignature, Generating }; enum class OutputType : uint8_t { Invalid, Key, Multisignature }; - struct InputKey { - uint64_t amount; - std::vector keyOffsets; - KeyImage keyImage; // double spending protection - }; - - struct InputMultisignature { - uint64_t amount; - uint32_t signatures; - uint64_t outputIndex; - }; - - struct OutputKey { - uint64_t amount; - PublicKey key; - }; - - struct OutputMultisignature { - uint64_t amount; - std::vector keys; - uint32_t requiredSignatures; - }; - struct GlobalOutput { - PublicKey targetKey; - uint64_t outputIndex; + Crypto::PublicKey targetKey; + uint32_t outputIndex; }; typedef std::vector GlobalOutputsContainer; struct OutputKeyInfo { - PublicKey transactionPublicKey; + Crypto::PublicKey transactionPublicKey; size_t transactionIndex; size_t outputInTransaction; }; @@ -86,12 +49,6 @@ namespace TransactionTypes { GlobalOutputsContainer outputs; OutputKeyInfo realOutput; }; - - struct KeyPair { - PublicKey publicKey; - SecretKey secretKey; - }; - } // @@ -101,33 +58,34 @@ class ITransactionReader { public: virtual ~ITransactionReader() { } - virtual Hash getTransactionHash() const = 0; - virtual Hash getTransactionPrefixHash() const = 0; - virtual PublicKey getTransactionPublicKey() const = 0; + virtual Crypto::Hash getTransactionHash() const = 0; + virtual Crypto::Hash getTransactionPrefixHash() const = 0; + virtual Crypto::PublicKey getTransactionPublicKey() const = 0; + virtual bool getTransactionSecretKey(Crypto::SecretKey& key) const = 0; virtual uint64_t getUnlockTime() const = 0; // extra - virtual bool getPaymentId(Hash& paymentId) const = 0; - virtual bool getExtraNonce(std::string& nonce) const = 0; - virtual Blob getExtra() const = 0; + virtual bool getPaymentId(Crypto::Hash& paymentId) const = 0; + virtual bool getExtraNonce(BinaryArray& nonce) const = 0; + virtual BinaryArray getExtra() const = 0; // inputs virtual size_t getInputCount() const = 0; virtual uint64_t getInputTotalAmount() const = 0; virtual TransactionTypes::InputType getInputType(size_t index) const = 0; - virtual void getInput(size_t index, TransactionTypes::InputKey& input) const = 0; - virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const = 0; + virtual void getInput(size_t index, KeyInput& input) const = 0; + virtual void getInput(size_t index, MultisignatureInput& input) const = 0; // outputs virtual size_t getOutputCount() const = 0; virtual uint64_t getOutputTotalAmount() const = 0; virtual TransactionTypes::OutputType getOutputType(size_t index) const = 0; - virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const = 0; - virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const = 0; + virtual void getOutput(size_t index, KeyOutput& output, uint64_t& amount) const = 0; + virtual void getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const = 0; // signatures virtual size_t getRequiredSignaturesCount(size_t inputIndex) const = 0; - virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const = 0; + virtual bool findOutputsToAccount(const AccountPublicAddress& addr, const Crypto::SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const = 0; // various checks virtual bool validateInputs() const = 0; @@ -135,7 +93,7 @@ class ITransactionReader { virtual bool validateSignatures() const = 0; // serialized transaction - virtual Blob getTransactionData() const = 0; + virtual BinaryArray getTransactionData() const = 0; }; // @@ -150,25 +108,27 @@ class ITransactionWriter { virtual void setUnlockTime(uint64_t unlockTime) = 0; // extra - virtual void setPaymentId(const Hash& paymentId) = 0; - virtual void setExtraNonce(const std::string& nonce) = 0; - virtual void appendExtra(const Blob& extraData) = 0; + virtual void setPaymentId(const Crypto::Hash& paymentId) = 0; + virtual void setExtraNonce(const BinaryArray& nonce) = 0; + virtual void appendExtra(const BinaryArray& extraData) = 0; // Inputs/Outputs - virtual size_t addInput(const TransactionTypes::InputKey& input) = 0; - virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) = 0; - virtual size_t addInput(const TransactionTypes::InputMultisignature& input) = 0; + virtual size_t addInput(const KeyInput& input) = 0; + virtual size_t addInput(const MultisignatureInput& input) = 0; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) = 0; - virtual size_t addOutput(uint64_t amount, const AccountAddress& to) = 0; - virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) = 0; + virtual size_t addOutput(uint64_t amount, const AccountPublicAddress& to) = 0; + virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) = 0; + virtual size_t addOutput(uint64_t amount, const KeyOutput& out) = 0; + virtual size_t addOutput(uint64_t amount, const MultisignatureOutput& out) = 0; // transaction info - virtual bool getTransactionSecretKey(SecretKey& key) const = 0; - virtual void setTransactionSecretKey(const SecretKey& key) = 0; + virtual void setTransactionSecretKey(const Crypto::SecretKey& key) = 0; // signing - virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) = 0; - virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) = 0; + virtual void signInputMultisignature(size_t input, const Crypto::PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) = 0; + virtual void signInputMultisignature(size_t input, const KeyPair& ephemeralKeys) = 0; }; class ITransaction : diff --git a/include/ITransfersContainer.h b/include/ITransfersContainer.h index 21e553daf3..96cbd12c7d 100644 --- a/include/ITransfersContainer.h +++ b/include/ITransfersContainer.h @@ -27,19 +27,19 @@ namespace CryptoNote { -const uint64_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits::max(); +const uint32_t UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX = std::numeric_limits::max(); struct TransactionInformation { // transaction info - Hash transactionHash; - PublicKey publicKey; - uint64_t blockHeight; + Crypto::Hash transactionHash; + Crypto::PublicKey publicKey; + uint32_t blockHeight; uint64_t timestamp; uint64_t unlockTime; uint64_t totalAmountIn; uint64_t totalAmountOut; std::vector extra; - Hash paymentId; + Crypto::Hash paymentId; }; @@ -47,24 +47,24 @@ struct TransactionOutputInformation { // output info TransactionTypes::OutputType type; uint64_t amount; - uint64_t globalOutputIndex; + uint32_t globalOutputIndex; uint32_t outputInTransaction; // transaction info - Hash transactionHash; - PublicKey transactionPublicKey; + Crypto::Hash transactionHash; + Crypto::PublicKey transactionPublicKey; union { - PublicKey outputKey; // Type: Key + Crypto::PublicKey outputKey; // Type: Key uint32_t requiredSignatures; // Type: Multisignature }; }; struct TransactionSpentOutputInformation: public TransactionOutputInformation { - uint64_t spendingBlockHeight; + uint32_t spendingBlockHeight; uint64_t timestamp; - Hash spendingTransactionHash; - KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key + Crypto::Hash spendingTransactionHash; + Crypto::KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key uint32_t inputInTransaction; }; @@ -96,9 +96,9 @@ class ITransfersContainer : public IStreamSerializable { virtual size_t transactionsCount() = 0; virtual uint64_t balance(uint32_t flags = IncludeDefault) = 0; virtual void getOutputs(std::vector& transfers, uint32_t flags = IncludeDefault) = 0; - virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; - virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; - virtual void getUnconfirmedTransactions(std::vector& transactions) = 0; + virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) = 0; + virtual std::vector getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags = IncludeDefault) = 0; + virtual void getUnconfirmedTransactions(std::vector& transactions) = 0; virtual std::vector getSpentOutputs() = 0; }; diff --git a/include/ITransfersSynchronizer.h b/include/ITransfersSynchronizer.h index f68d7399d6..d49da55edf 100644 --- a/include/ITransfersSynchronizer.h +++ b/include/ITransfersSynchronizer.h @@ -42,22 +42,23 @@ class ITransfersSubscription; class ITransfersObserver { public: virtual void onError(ITransfersSubscription* object, - uint64_t height, std::error_code ec) {} + uint32_t height, std::error_code ec) { + } - virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) {} + virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) {} /** * \note The sender must guarantee that onTransactionDeleted() is called only after onTransactionUpdated() is called * for the same \a transactionHash. */ - virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { } + virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) {} }; class ITransfersSubscription : public IObservable < ITransfersObserver > { public: virtual ~ITransfersSubscription() {} - virtual AccountAddress getAddress() = 0; + virtual AccountPublicAddress getAddress() = 0; virtual ITransfersContainer& getContainer() = 0; }; @@ -66,10 +67,10 @@ class ITransfersSynchronizer : public IStreamSerializable { virtual ~ITransfersSynchronizer() {} virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) = 0; - virtual bool removeSubscription(const AccountAddress& acc) = 0; - virtual void getSubscriptions(std::vector& subscriptions) = 0; + virtual bool removeSubscription(const AccountPublicAddress& acc) = 0; + virtual void getSubscriptions(std::vector& subscriptions) = 0; // returns nullptr if address is not found - virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) = 0; + virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) = 0; }; } diff --git a/include/IWallet.h b/include/IWallet.h old mode 100644 new mode 100755 index c64961d705..854f0dfd1e --- a/include/IWallet.h +++ b/include/IWallet.h @@ -17,113 +17,99 @@ #pragma once -#include -#include -#include #include -#include #include -#include #include +#include "CryptoNote.h" namespace CryptoNote { -typedef size_t TransactionId; -typedef size_t TransferId; -typedef std::array TransactionHash; +const size_t WALLET_INVALID_TRANSACTION_ID = std::numeric_limits::max(); +const size_t WALLET_INVALID_TRANSFER_ID = std::numeric_limits::max(); +const uint32_t WALLET_UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); -struct Transfer { - std::string address; - int64_t amount; +enum class WalletTransactionState : uint8_t { + SUCCEEDED = 0, + FAILED, + CANCELLED }; -const TransactionId INVALID_TRANSACTION_ID = std::numeric_limits::max(); -const TransferId INVALID_TRANSFER_ID = std::numeric_limits::max(); -const uint64_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); - -enum class TransactionState : uint8_t { - Active, // --> {Deleted} - Deleted, // --> {Active} +enum WalletEventType { + TRANSACTION_CREATED, + TRANSACTION_UPDATED, + BALANCE_UNLOCKED +}; - Sending, // --> {Active, Cancelled, Failed} - Cancelled, // --> {} - Failed // --> {} +struct WalletTransactionCreatedData { + size_t transactionIndex; }; -struct TransactionInfo { - TransferId firstTransferId; - size_t transferCount; - int64_t totalAmount; - uint64_t fee; - uint64_t sentTime; - uint64_t unlockTime; - TransactionHash hash; - bool isCoinbase; - uint64_t blockHeight; - uint64_t timestamp; - std::string extra; - TransactionState state; +struct WalletTransactionUpdatedData { + size_t transactionIndex; }; -typedef std::array WalletPublicKey; -typedef std::array WalletSecretKey; +struct WalletEvent { + WalletEventType type; + union { + WalletTransactionCreatedData transactionCreated; + WalletTransactionUpdatedData transactionUpdated; + }; +}; -struct WalletAccountKeys { - WalletPublicKey viewPublicKey; - WalletSecretKey viewSecretKey; - WalletPublicKey spendPublicKey; - WalletSecretKey spendSecretKey; +struct WalletTransaction { + WalletTransactionState state; + uint64_t timestamp; + uint32_t blockHeight; + Crypto::Hash hash; + int64_t totalAmount; + uint64_t fee; + uint64_t creationTime; + uint64_t unlockTime; + std::string extra; }; -class IWalletObserver { -public: - virtual ~IWalletObserver() {} - - virtual void initCompleted(std::error_code result) {} - virtual void saveCompleted(std::error_code result) {} - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} - virtual void synchronizationCompleted(std::error_code result) {} - virtual void actualBalanceUpdated(uint64_t actualBalance) {} - virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} - virtual void externalTransactionCreated(TransactionId transactionId) {} - virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) {} - virtual void transactionUpdated(TransactionId transactionId) {} +struct WalletTransfer { + std::string address; + int64_t amount; }; class IWallet { public: - virtual ~IWallet() {} ; - virtual void addObserver(IWalletObserver* observer) = 0; - virtual void removeObserver(IWalletObserver* observer) = 0; + virtual ~IWallet() {} - virtual void initAndGenerate(const std::string& password) = 0; - virtual void initAndLoad(std::istream& source, const std::string& password) = 0; - virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) = 0; + virtual void initialize(const std::string& password) = 0; + virtual void load(std::istream& source, const std::string& password) = 0; virtual void shutdown() = 0; - virtual void reset() = 0; - - virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0; - virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) = 0; - virtual std::string getAddress() = 0; + virtual size_t getAddressCount() const = 0; + virtual std::string getAddress(size_t index) const = 0; + virtual std::string createAddress() = 0; + virtual std::string createAddress(const KeyPair& spendKey) = 0; + virtual void deleteAddress(const std::string& address) = 0; - virtual uint64_t actualBalance() = 0; - virtual uint64_t pendingBalance() = 0; + virtual uint64_t getActualBalance() const = 0; + virtual uint64_t getActualBalance(const std::string& address) const = 0; + virtual uint64_t getPendingBalance() const = 0; + virtual uint64_t getPendingBalance(const std::string& address) const = 0; - virtual size_t getTransactionCount() = 0; - virtual size_t getTransferCount() = 0; + virtual size_t getTransactionCount() const = 0; + virtual WalletTransaction getTransaction(size_t transactionIndex) const = 0; + virtual size_t getTransactionTransferCount(size_t transactionIndex) const = 0; + virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const = 0; - virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; - - virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) = 0; - virtual bool getTransfer(TransferId transferId, Transfer& transfer) = 0; + virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; + virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; - virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; - virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; - virtual std::error_code cancelTransaction(size_t transferId) = 0; + virtual void start() = 0; + virtual void stop() = 0; - virtual void getAccountKeys(WalletAccountKeys& keys) = 0; + //blocks until an event occured + virtual WalletEvent getEvent() = 0; }; } diff --git a/include/IWalletLegacy.h b/include/IWalletLegacy.h new file mode 100644 index 0000000000..a0686a5f93 --- /dev/null +++ b/include/IWalletLegacy.h @@ -0,0 +1,116 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include "CryptoNote.h" + +namespace CryptoNote { + +typedef size_t TransactionId; +typedef size_t TransferId; + +struct WalletLegacyTransfer { + std::string address; + int64_t amount; +}; + +const TransactionId WALLET_LEGACY_INVALID_TRANSACTION_ID = std::numeric_limits::max(); +const TransferId WALLET_LEGACY_INVALID_TRANSFER_ID = std::numeric_limits::max(); +const uint32_t WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); + +enum class WalletLegacyTransactionState : uint8_t { + Active, // --> {Deleted} + Deleted, // --> {Active} + + Sending, // --> {Active, Cancelled, Failed} + Cancelled, // --> {} + Failed // --> {} +}; + +struct WalletLegacyTransaction { + TransferId firstTransferId; + size_t transferCount; + int64_t totalAmount; + uint64_t fee; + uint64_t sentTime; + uint64_t unlockTime; + Crypto::Hash hash; + bool isCoinbase; + uint32_t blockHeight; + uint64_t timestamp; + std::string extra; + WalletLegacyTransactionState state; +}; + +class IWalletLegacyObserver { +public: + virtual ~IWalletLegacyObserver() {} + + virtual void initCompleted(std::error_code result) {} + virtual void saveCompleted(std::error_code result) {} + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) {} + virtual void synchronizationCompleted(std::error_code result) {} + virtual void actualBalanceUpdated(uint64_t actualBalance) {} + virtual void pendingBalanceUpdated(uint64_t pendingBalance) {} + virtual void externalTransactionCreated(TransactionId transactionId) {} + virtual void sendTransactionCompleted(TransactionId transactionId, std::error_code result) {} + virtual void transactionUpdated(TransactionId transactionId) {} +}; + +class IWalletLegacy { +public: + virtual ~IWalletLegacy() {} ; + virtual void addObserver(IWalletLegacyObserver* observer) = 0; + virtual void removeObserver(IWalletLegacyObserver* observer) = 0; + + virtual void initAndGenerate(const std::string& password) = 0; + virtual void initAndLoad(std::istream& source, const std::string& password) = 0; + virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password) = 0; + virtual void shutdown() = 0; + virtual void reset() = 0; + + virtual void save(std::ostream& destination, bool saveDetailed = true, bool saveCache = true) = 0; + + virtual std::error_code changePassword(const std::string& oldPassword, const std::string& newPassword) = 0; + + virtual std::string getAddress() = 0; + + virtual uint64_t actualBalance() = 0; + virtual uint64_t pendingBalance() = 0; + + virtual size_t getTransactionCount() = 0; + virtual size_t getTransferCount() = 0; + + virtual TransactionId findTransactionByTransferId(TransferId transferId) = 0; + + virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) = 0; + virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) = 0; + + virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0) = 0; + virtual std::error_code cancelTransaction(size_t transferId) = 0; + + virtual void getAccountKeys(AccountKeys& keys) = 0; +}; + +} diff --git a/src/BlockchainExplorer/BlockchainExplorer.cpp b/src/BlockchainExplorer/BlockchainExplorer.cpp old mode 100644 new mode 100755 index e3ec4b5b32..bdfe4ee1b4 --- a/src/BlockchainExplorer/BlockchainExplorer.cpp +++ b/src/BlockchainExplorer/BlockchainExplorer.cpp @@ -21,37 +21,69 @@ #include #include -#include "cryptonote_core/cryptonote_format_utils.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteConfig.h" + #include "BlockchainExplorerErrors.h" +#include "ITransaction.h" using namespace Logging; +using namespace Crypto; namespace CryptoNote { +class ContextCounterHolder +{ +public: + ContextCounterHolder(BlockchainExplorer::AsyncContextCounter& counter) : counter(counter) {} + ~ContextCounterHolder() { counter.delAsyncContext(); } + +private: + BlockchainExplorer::AsyncContextCounter& counter; +}; + class NodeRequest { public: NodeRequest(const std::function& request) : requestFunc(request) {} std::error_code performBlocking() { - requestFunc(std::bind(&NodeRequest::completeionCallback, this, std::placeholders::_1)); - return promise.get_future().get(); + std::promise promise; + std::future future = promise.get_future(); + requestFunc([&](std::error_code c){ + blockingCompleteionCallback(std::move(promise), c); + }); + return future.get(); } - void performAsync(const INode::Callback& callback) { - requestFunc(callback); + void performAsync(BlockchainExplorer::AsyncContextCounter& asyncContextCounter, const INode::Callback& callback) { + asyncContextCounter.addAsyncContext(); + requestFunc(std::bind(&NodeRequest::asyncCompleteionCallback, callback, std::ref(asyncContextCounter), std::placeholders::_1)); } private: - void completeionCallback(std::error_code ec) { + void blockingCompleteionCallback(std::promise promise, std::error_code ec) { promise.set_value(ec); } - std::promise promise; + static void asyncCompleteionCallback(const INode::Callback& callback, BlockchainExplorer::AsyncContextCounter& asyncContextCounter, std::error_code ec) { + ContextCounterHolder counterHolder(asyncContextCounter); + try { + callback(ec); + } catch (...) { + return; + } + } + const std::function requestFunc; }; -BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) : node(node), logger(logger, "BlockchainExplorer"), state(NOT_INITIALIZED) {} +BlockchainExplorer::BlockchainExplorer(INode& node, Logging::ILogger& logger) : + node(node), + logger(logger, "BlockchainExplorer"), + state(NOT_INITIALIZED), + synchronized(false), + observersCounter(0) {} BlockchainExplorer::~BlockchainExplorer() {} @@ -59,7 +91,7 @@ bool BlockchainExplorer::addObserver(IBlockchainObserver* observer) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } - + observersCounter.fetch_add(1); return observerManager.add(observer); } @@ -67,7 +99,9 @@ bool BlockchainExplorer::removeObserver(IBlockchainObserver* observer) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } - + if (observersCounter.load() != 0) { + observersCounter.fetch_sub(1); + } return observerManager.remove(observer); } @@ -98,10 +132,11 @@ void BlockchainExplorer::shutdown() { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } node.removeObserver(this); + asyncContextCounter.waitAsyncContextsFinish(); state.store(NOT_INITIALIZED); } -bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, std::vector>& blocks) { +bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, std::vector>& blocks) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } @@ -111,7 +146,7 @@ bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, st std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector>&, const INode::Callback& ) @@ -131,7 +166,7 @@ bool BlockchainExplorer::getBlocks(const std::vector& blockHeights, st return true; } -bool BlockchainExplorer::getBlocks(const std::vector>& blockHashes, std::vector& blocks) { +bool BlockchainExplorer::getBlocks(const std::vector& blockHashes, std::vector& blocks) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } @@ -141,13 +176,13 @@ bool BlockchainExplorer::getBlocks(const std::vector>& b std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector&, const INode::Callback& ) >(&INode::getBlocks), std::ref(node), - std::cref(reinterpret_cast&>(blockHashes)), + std::cref(reinterpret_cast&>(blockHashes)), std::ref(blocks), std::placeholders::_1 ) @@ -161,15 +196,50 @@ bool BlockchainExplorer::getBlocks(const std::vector>& b return true; } +bool BlockchainExplorer::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get blocks by timestamp request came."; + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + uint64_t, + uint64_t, + uint32_t, + std::vector&, + uint32_t&, + const INode::Callback& + ) + >(&INode::getBlocks), + std::ref(node), + timestampBegin, + timestampEnd, + blocksNumberLimit, + std::ref(blocks), + std::ref(blocksNumberWithinTimestamps), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get blocks by timestamp: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + bool BlockchainExplorer::getBlockchainTop(BlockDetails& topBlock) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } logger(DEBUGGING) << "Get blockchain top request came."; - uint64_t lastHeight = node.getLastLocalBlockHeight(); + uint32_t lastHeight = node.getLastLocalBlockHeight(); - std::vector heights; + std::vector heights; heights.push_back(std::move(lastHeight)); std::vector> blocks; @@ -195,49 +265,105 @@ bool BlockchainExplorer::getBlockchainTop(BlockDetails& topBlock) { return true; } -bool BlockchainExplorer::getTransactions(const std::vector>& transactionHashes, std::vector& transactions) { +bool BlockchainExplorer::getTransactions(const std::vector& transactionHashes, std::vector& transactions) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get transactions by hash request came."; + NodeRequest request( + std::bind( + static_cast< + void(INode::*)( + const std::vector&, + std::vector&, + const INode::Callback& + ) + >(&INode::getTransactions), + std::ref(node), + std::cref(reinterpret_cast&>(transactionHashes)), + std::ref(transactions), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get transactions by hash: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + +bool BlockchainExplorer::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { + if (state.load() != INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); + } + + logger(DEBUGGING) << "Get transactions by timestamp request came."; + NodeRequest request( + std::bind( + &INode::getPoolTransactions, + std::ref(node), + timestampBegin, + timestampEnd, + transactionsNumberLimit, + std::ref(transactions), + std::ref(transactionsNumberWithinTimestamps), + std::placeholders::_1 + ) + ); + std::error_code ec = request.performBlocking(); + if (ec) { + logger(ERROR) << "Can't get transactions by timestamp: " << ec.message(); + throw std::system_error(ec); + } + return true; +} + +bool BlockchainExplorer::getTransactionsByPaymentId(const Hash& paymentId, std::vector& transactions) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } - logger(DEBUGGING) << "Get transactions request came."; + logger(DEBUGGING) << "Get transactions by payment id request came."; NodeRequest request( std::bind( - &INode::getTransactions, + &INode::getTransactionsByPaymentId, std::ref(node), - std::cref(reinterpret_cast&>(transactionHashes)), + std::cref(reinterpret_cast(paymentId)), std::ref(transactions), std::placeholders::_1 ) ); std::error_code ec = request.performBlocking(); if (ec) { - logger(ERROR) << "Can't get transactions: " << ec.message(); + logger(ERROR) << "Can't get transactions by payment id: " << ec.message(); throw std::system_error(ec); } return true; } -bool BlockchainExplorer::getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTopHash, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) { +bool BlockchainExplorer::getPoolState(const std::vector& knownPoolTransactionHashes, Hash knownBlockchainTopHash, bool& isBlockchainActual, std::vector& newTransactions, std::vector& removedTransactions) { if (state.load() != INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::BlockchainExplorerErrorCodes::NOT_INITIALIZED)); } logger(DEBUGGING) << "Get pool state request came."; - std::vector rawNewTransactions; + std::vector> rawNewTransactions; NodeRequest request( [&](const INode::Callback& callback) { - std::vector hashes; - for (const std::array& hash : knownPoolTransactionHashes) { - hashes.push_back(std::move(reinterpret_cast(hash))); + std::vector hashes; + for (Hash hash : knownPoolTransactionHashes) { + hashes.push_back(std::move(hash)); } + node.getPoolSymmetricDifference( std::move(hashes), - reinterpret_cast(knownBlockchainTopHash), + reinterpret_cast(knownBlockchainTopHash), isBlockchainActual, rawNewTransactions, - reinterpret_cast&>(removedTransactions), + removedTransactions, callback ); } @@ -248,10 +374,10 @@ bool BlockchainExplorer::getPoolState(const std::vector> throw std::system_error(ec); } - std::vector> newTransactionsHashes; - for (const Transaction& rawTransaction : rawNewTransactions) { - crypto::hash transactionHash = get_transaction_hash(rawTransaction); - newTransactionsHashes.push_back(std::move(reinterpret_cast&>(transactionHash))); + std::vector newTransactionsHashes; + for (const auto& rawTransaction : rawNewTransactions) { + Hash transactionHash = rawTransaction->getTransactionHash(); + newTransactionsHashes.push_back(std::move(transactionHash)); } return getTransactions(newTransactionsHashes, newTransactions); @@ -295,27 +421,32 @@ bool BlockchainExplorer::isSynchronized() { logger(ERROR) << "Can't get synchronization status: " << ec.message(); throw std::system_error(ec); } + synchronized.store(syncStatus); return syncStatus; } void BlockchainExplorer::poolChanged() { logger(DEBUGGING) << "Got poolChanged notification."; + if (!synchronized.load() || observersCounter.load() == 0) { + return; + } + std::unique_lock lock(mutex); - std::shared_ptr> rawNewTransactionsPtr = std::make_shared>(); - std::shared_ptr> removedTransactionsPtr = std::make_shared>(); + std::shared_ptr>> rawNewTransactionsPtr = std::make_shared>>(); + std::shared_ptr> removedTransactionsPtr = std::make_shared>(); std::shared_ptr isBlockchainActualPtr = std::make_shared(false); NodeRequest request( [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](const INode::Callback& callback) { - std::vector hashes; - for (const crypto::hash& hash : knownPoolState) { + std::vector hashes; + for (const Hash& hash : knownPoolState) { hashes.push_back(std::move(hash)); } node.getPoolSymmetricDifference( std::move(hashes), - reinterpret_cast(knownBlockchainTop.hash), + reinterpret_cast(knownBlockchainTop.hash), *isBlockchainActualPtr, *rawNewTransactionsPtr, *removedTransactionsPtr, @@ -323,7 +454,7 @@ void BlockchainExplorer::poolChanged() { ); } ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, rawNewTransactionsPtr, removedTransactionsPtr, isBlockchainActualPtr](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send poolChanged notification because can't get pool symmetric difference: " << ec.message(); @@ -336,22 +467,23 @@ void BlockchainExplorer::poolChanged() { std::unique_lock lock(mutex); - std::shared_ptr> newTransactionsHashesPtr = std::make_shared>(); - for (const Transaction& rawTransaction : *rawNewTransactionsPtr) { - crypto::hash transactionHash = get_transaction_hash(rawTransaction); + std::shared_ptr> newTransactionsHashesPtr = std::make_shared>(); + for (const auto& rawTransaction : *rawNewTransactionsPtr) { + auto hash = rawTransaction->getTransactionHash(); + Hash transactionHash = reinterpret_cast(hash); bool inserted = knownPoolState.emplace(transactionHash).second; if (inserted) { newTransactionsHashesPtr->push_back(std::move(transactionHash)); } } - std::shared_ptr, TransactionRemoveReason>>> removedTransactionsHashesPtr = std::make_shared, TransactionRemoveReason>>>(); - for (const crypto::hash hash : *removedTransactionsPtr) { + std::shared_ptr>> removedTransactionsHashesPtr = std::make_shared>>(); + for (const Hash hash : *removedTransactionsPtr) { auto iter = knownPoolState.find(hash); if (iter != knownPoolState.end()) { removedTransactionsHashesPtr->push_back( std::move(std::make_pair( - reinterpret_cast&>(hash), + hash, TransactionRemoveReason::INCLUDED_IN_BLOCK //Can't have real reason here. )) ); @@ -362,14 +494,20 @@ void BlockchainExplorer::poolChanged() { std::shared_ptr> newTransactionsPtr = std::make_shared>(); NodeRequest request( std::bind( - &INode::getTransactions, + static_cast< + void(INode::*)( + const std::vector&, + std::vector&, + const INode::Callback& + ) + >(&INode::getTransactions), std::ref(node), std::cref(*newTransactionsHashesPtr), std::ref(*newTransactionsPtr), std::placeholders::_1 ) ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, newTransactionsHashesPtr, newTransactionsPtr, removedTransactionsHashesPtr](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send poolChanged notification because can't get transactions: " << ec.message(); @@ -386,10 +524,16 @@ void BlockchainExplorer::poolChanged() { } -void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { +void BlockchainExplorer::blockchainSynchronized(uint32_t topHeight) { logger(DEBUGGING) << "Got blockchainSynchronized notification."; - std::shared_ptr> blockHeightsPtr = std::make_shared>(); + synchronized.store(true); + + if (observersCounter.load() == 0) { + return; + } + + std::shared_ptr> blockHeightsPtr = std::make_shared>(); std::shared_ptr>> blocksPtr = std::make_shared>>(); blockHeightsPtr->push_back(topHeight); @@ -398,7 +542,7 @@ void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector>&, const INode::Callback& ) @@ -410,7 +554,7 @@ void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { ) ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, blockHeightsPtr, blocksPtr, topHeight](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send blockchainSynchronized notification because can't get blocks by height: " << ec.message(); @@ -439,17 +583,22 @@ void BlockchainExplorer::blockchainSynchronized(uint64_t topHeight) { ); } -void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { +void BlockchainExplorer::localBlockchainUpdated(uint32_t height) { logger(DEBUGGING) << "Got localBlockchainUpdated notification."; + if (observersCounter.load() == 0) { + knownBlockchainTopHeight = height; + return; + } + std::unique_lock lock(mutex); assert(height >= knownBlockchainTopHeight); - std::shared_ptr> blockHeightsPtr = std::make_shared>(); + std::shared_ptr> blockHeightsPtr = std::make_shared>(); std::shared_ptr>> blocksPtr = std::make_shared>>(); - for (size_t i = knownBlockchainTopHeight; i <= height; ++i) { + for (uint32_t i = knownBlockchainTopHeight; i <= height; ++i) { blockHeightsPtr->push_back(i); } @@ -459,7 +608,7 @@ void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { std::bind( static_cast< void(INode::*)( - const std::vector&, + const std::vector&, std::vector>&, const INode::Callback& ) @@ -471,7 +620,7 @@ void BlockchainExplorer::localBlockchainUpdated(uint64_t height) { ) ); - request.performAsync( + request.performAsync(asyncContextCounter, [this, blockHeightsPtr, blocksPtr](std::error_code ec) { if (ec) { logger(ERROR) << "Can't send blockchainUpdated notification because can't get blocks by height: " << ec.message(); diff --git a/src/BlockchainExplorer/BlockchainExplorer.h b/src/BlockchainExplorer/BlockchainExplorer.h old mode 100644 new mode 100755 index d3c644d3d4..8ab27420b4 --- a/src/BlockchainExplorer/BlockchainExplorer.h +++ b/src/BlockchainExplorer/BlockchainExplorer.h @@ -27,6 +27,8 @@ #include "Common/ObserverManager.h" #include "BlockchainExplorerErrors.h" +#include "Wallet/WalletAsyncContextCounter.h" + #include "Logging/LoggerRef.h" namespace CryptoNote { @@ -46,13 +48,16 @@ class BlockchainExplorer : public IBlockchainExplorer, public INodeObserver { virtual bool addObserver(IBlockchainObserver* observer) override; virtual bool removeObserver(IBlockchainObserver* observer) override; - virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) override; - virtual bool getBlocks(const std::vector>& blockHashes, std::vector& blocks) override; + virtual bool getBlocks(const std::vector& blockHeights, std::vector>& blocks) override; + virtual bool getBlocks(const std::vector& blockHashes, std::vector& blocks) override; + virtual bool getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) override; virtual bool getBlockchainTop(BlockDetails& topBlock) override; - virtual bool getTransactions(const std::vector>& transactionHashes, std::vector& transactions) override; - virtual bool getPoolState(const std::vector>& knownPoolTransactionHashes, std::array knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector>& removedTransactions) override; + virtual bool getTransactions(const std::vector& transactionHashes, std::vector& transactions) override; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) override; + virtual bool getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) override; + virtual bool getPoolState(const std::vector& knownPoolTransactionHashes, Crypto::Hash knownBlockchainTop, bool& isBlockchainActual, std::vector& newTransactions, std::vector& removedTransactions) override; virtual uint64_t getRewardBlocksWindow() override; virtual uint64_t getFullRewardMaxBlockSize(uint8_t majorVersion) override; @@ -63,8 +68,10 @@ class BlockchainExplorer : public IBlockchainExplorer, public INodeObserver { virtual void shutdown() override; virtual void poolChanged() override; - virtual void blockchainSynchronized(uint64_t topHeight) override; - virtual void localBlockchainUpdated(uint64_t height) override; + virtual void blockchainSynchronized(uint32_t topHeight) override; + virtual void localBlockchainUpdated(uint32_t height) override; + + typedef WalletAsyncContextCounter AsyncContextCounter; private: enum State { @@ -73,16 +80,20 @@ class BlockchainExplorer : public IBlockchainExplorer, public INodeObserver { }; BlockDetails knownBlockchainTop; - uint64_t knownBlockchainTopHeight; - std::unordered_set knownPoolState; + uint32_t knownBlockchainTopHeight; + std::unordered_set knownPoolState; std::atomic state; - tools::ObserverManager observerManager; + std::atomic synchronized; + std::atomic observersCounter; + Tools::ObserverManager observerManager; std::mutex mutex; INode& node; Logging::LoggerRef logger; + + AsyncContextCounter asyncContextCounter; }; } diff --git a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp old mode 100644 new mode 100755 index 336b934250..f0b7b64023 --- a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp +++ b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.cpp @@ -21,23 +21,25 @@ #include #include "Common/StringTools.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/TransactionExtra.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { -BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : - core(core), - protocol(protocol) -{ +BlockchainExplorerDataBuilder::BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol) : +core(core), +protocol(protocol) { } bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uint64_t& mixin) { mixin = 0; - for (const TransactionInput& txin : transaction.vin) { - if (txin.type() != typeid(TransactionInputToKey)) { + for (const TransactionInput& txin : transaction.inputs) { + if (txin.type() != typeid(KeyInput)) { continue; } - uint64_t currentMixin = boost::get(txin).keyOffsets.size(); + uint64_t currentMixin = boost::get(txin).outputIndexes.size(); if (currentMixin > mixin) { mixin = currentMixin; } @@ -45,75 +47,71 @@ bool BlockchainExplorerDataBuilder::getMixin(const Transaction& transaction, uin return true; } -bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, crypto::hash& paymentId) { - std::vector txExtraFields; - parse_tx_extra(transaction.extra, txExtraFields); - tx_extra_nonce extraNonce; - if (!find_tx_extra_field_by_type(txExtraFields, extraNonce)) { +bool BlockchainExplorerDataBuilder::getPaymentId(const Transaction& transaction, Crypto::Hash& paymentId) { + std::vector txExtraFields; + parseTransactionExtra(transaction.extra, txExtraFields); + TransactionExtraNonce extraNonce; + if (!findTransactionExtraFieldByType(txExtraFields, extraNonce)) { return false; } - return get_payment_id_from_tx_extra_nonce(extraNonce.nonce, paymentId); + return getPaymentIdFromTransactionExtraNonce(extraNonce.nonce, paymentId); } bool BlockchainExplorerDataBuilder::fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails) { extraDetails.raw = rawExtra; - std::vector txExtraFields; - parse_tx_extra(rawExtra, txExtraFields); - for (const tx_extra_field& field : txExtraFields) { - if (typeid(tx_extra_padding) == field.type()) { - extraDetails.padding.push_back(std::move(boost::get(field).size)); - } - else if (typeid(tx_extra_pub_key) == field.type()) { - extraDetails.publicKey.push_back(std::move(reinterpret_cast&>(boost::get(field).pub_key))); - } - else if (typeid(tx_extra_nonce) == field.type()) { - extraDetails.nonce.push_back(std::move(Common::toHex(boost::get(field).nonce.data(), boost::get(field).nonce.size()))); + std::vector txExtraFields; + parseTransactionExtra(rawExtra, txExtraFields); + for (const TransactionExtraField& field : txExtraFields) { + if (typeid(TransactionExtraPadding) == field.type()) { + extraDetails.padding.push_back(std::move(boost::get(field).size)); + } else if (typeid(TransactionExtraPublicKey) == field.type()) { + extraDetails.publicKey.push_back(std::move(boost::get(field).publicKey)); + } else if (typeid(TransactionExtraNonce) == field.type()) { + extraDetails.nonce.push_back(std::move(Common::toHex(boost::get(field).nonce.data(), boost::get(field).nonce.size()))); } } return true; } size_t BlockchainExplorerDataBuilder::median(std::vector& v) { - if(v.empty()) + if (v.empty()) return boost::value_initialized(); - if(v.size() == 1) + if (v.size() == 1) return v[0]; size_t n = (v.size()) / 2; std::sort(v.begin(), v.end()); //nth_element(v.begin(), v.begin()+n-1, v.end()); - if(v.size()%2) - {//1, 3, 5... + if (v.size() % 2) {//1, 3, 5... return v[n]; - }else - {//2, 4, 6... - return (v[n-1] + v[n])/2; + } else {//2, 4, 6... + return (v[n - 1] + v[n]) / 2; } } bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDetails& blockDetails) { - crypto::hash hash = get_block_hash(block); - + Crypto::Hash hash = get_block_hash(block); + blockDetails.majorVersion = block.majorVersion; blockDetails.minorVersion = block.minorVersion; blockDetails.timestamp = block.timestamp; - blockDetails.prevBlockHash = reinterpret_cast&>(block.prevId); + blockDetails.prevBlockHash = block.previousBlockHash; blockDetails.nonce = block.nonce; - blockDetails.hash = reinterpret_cast&>(hash); + blockDetails.hash = hash; blockDetails.reward = 0; - for (const TransactionOutput& out : block.minerTx.vout) { + for (const TransactionOutput& out : block.baseTransaction.outputs) { blockDetails.reward += out.amount; } - if (block.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) + if (block.baseTransaction.inputs.front().type() != typeid(BaseInput)) return false; - blockDetails.height = boost::get(block.minerTx.vin.front()).height; - - crypto::hash tmpHash = core.getBlockIdByHeight(blockDetails.height); + blockDetails.height = boost::get(block.baseTransaction.inputs.front()).blockIndex; + + Crypto::Hash tmpHash = core.getBlockIdByHeight(blockDetails.height); blockDetails.isOrphaned = hash != tmpHash; - + if (!core.getBlockDifficulty(blockDetails.height, blockDetails.difficulty)) { return false; } @@ -130,20 +128,21 @@ bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDe } blockDetails.transactionsCumulativeSize = blockSize; - size_t blokBlobSize = get_object_blobsize(block); - size_t minerTxBlobSize = get_object_blobsize(block.minerTx); + size_t blokBlobSize = getObjectBinarySize(block); + size_t minerTxBlobSize = getObjectBinarySize(block.baseTransaction); blockDetails.blockSize = blokBlobSize + blockDetails.transactionsCumulativeSize - minerTxBlobSize; - + if (!core.getAlreadyGeneratedCoins(hash, blockDetails.alreadyGeneratedCoins)) { return false; } - - blockDetails.alreadyGeneratedTransactions = 0; //TODO + + if (!core.getGeneratedTransactionsNumber(blockDetails.height, blockDetails.alreadyGeneratedTransactions)) { + return false; + } uint64_t prevBlockGeneratedCoins = 0; - if (blockDetails.height > 0) - { - if (!core.getAlreadyGeneratedCoins(block.prevId, prevBlockGeneratedCoins)) { + if (blockDetails.height > 0) { + if (!core.getAlreadyGeneratedCoins(block.previousBlockHash, prevBlockGeneratedCoins)) { return false; } } @@ -151,45 +150,40 @@ bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDe uint64_t currentReward = 0; int64_t emissionChange = 0; bool penalizeFee = block.majorVersion >= 2; - if(!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange)) - { + if (!core.getBlockReward(blockDetails.sizeMedian, 0, prevBlockGeneratedCoins, 0, penalizeFee, maxReward, emissionChange)) { return false; } - if(!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange)) - { + if (!core.getBlockReward(blockDetails.sizeMedian, blockDetails.transactionsCumulativeSize, prevBlockGeneratedCoins, 0, penalizeFee, currentReward, emissionChange)) { return false; } blockDetails.baseReward = maxReward; - if (maxReward == 0 && currentReward == 0) - { + if (maxReward == 0 && currentReward == 0) { blockDetails.penalty = static_cast(0); - } - else - { + } else { if (maxReward < currentReward) { return false; } blockDetails.penalty = static_cast(maxReward - currentReward) / static_cast(maxReward); } - - blockDetails.transactions.reserve(block.txHashes.size() + 1); + + blockDetails.transactions.reserve(block.transactionHashes.size() + 1); TransactionDetails transactionDetails; - if (!fillTransactionDetails(block.minerTx, transactionDetails, block.timestamp)) { + if (!fillTransactionDetails(block.baseTransaction, transactionDetails, block.timestamp)) { return false; } blockDetails.transactions.push_back(std::move(transactionDetails)); - + std::list found; - std::list missed; - core.getTransactions(block.txHashes, found, missed); - if (found.size() != block.txHashes.size()) { + std::list missed; + core.getTransactions(block.transactionHashes, found, missed, blockDetails.isOrphaned); + if (found.size() != block.transactionHashes.size()) { return false; } - + blockDetails.totalFeeAmount = 0; - + for (const Transaction& tx : found) { TransactionDetails transactionDetails; if (!fillTransactionDetails(tx, transactionDetails, block.timestamp)) { @@ -202,21 +196,21 @@ bool BlockchainExplorerDataBuilder::fillBlockDetails(const Block &block, BlockDe } bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& transaction, TransactionDetails& transactionDetails, uint64_t timestamp) { - crypto::hash hash = get_transaction_hash(transaction); - transactionDetails.hash = reinterpret_cast&>(hash); - + Crypto::Hash hash = getObjectHash(transaction); + transactionDetails.hash = hash; + transactionDetails.timestamp = timestamp; - - crypto::hash blockHash; - uint64_t blockHeight; + + Crypto::Hash blockHash; + uint32_t blockHeight; if (!core.getBlockContainingTx(hash, blockHash, blockHeight)) { transactionDetails.inBlockchain = false; - transactionDetails.blockHeight = boost::value_initialized(); - transactionDetails.blockHash = boost::value_initialized>(); + transactionDetails.blockHeight = boost::value_initialized(); + transactionDetails.blockHash = boost::value_initialized(); } else { transactionDetails.inBlockchain = true; transactionDetails.blockHeight = blockHeight; - transactionDetails.blockHash = reinterpret_cast&>(blockHash); + transactionDetails.blockHash = blockHash; if (timestamp == 0) { Block block; if (!core.getBlockByHash(blockHash, block)) { @@ -225,8 +219,8 @@ bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& tr transactionDetails.timestamp = block.timestamp; } } - - transactionDetails.size = get_object_blobsize(transaction); + + transactionDetails.size = getObjectBinarySize(transaction); transactionDetails.unlockTime = transaction.unlockTime; transactionDetails.totalOutputsAmount = get_outs_money_amount(transaction); @@ -236,7 +230,7 @@ bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& tr } transactionDetails.totalInputsAmount = inputsAmount; - if (transaction.vin.size() > 0 && transaction.vin.front().type() == typeid(TransactionInputGenerate)) { + if (transaction.inputs.size() > 0 && transaction.inputs.front().type() == typeid(BaseInput)) { //It's gen transaction transactionDetails.fee = 0; transactionDetails.mixin = 0; @@ -252,106 +246,105 @@ bool BlockchainExplorerDataBuilder::fillTransactionDetails(const Transaction& tr } transactionDetails.mixin = mixin; } - - crypto::hash paymentId; + + Crypto::Hash paymentId; if (getPaymentId(transaction, paymentId)) { - transactionDetails.paymentId = reinterpret_cast&>(paymentId); - } - else { - transactionDetails.paymentId = boost::value_initialized>(); + transactionDetails.paymentId = paymentId; + } else { + transactionDetails.paymentId = boost::value_initialized(); } - + fillTxExtra(transaction.extra, transactionDetails.extra); - + transactionDetails.signatures.reserve(transaction.signatures.size()); - for (const std::vector& signatures : transaction.signatures) { - std::vector> signaturesDetails; + for (const std::vector& signatures : transaction.signatures) { + std::vector signaturesDetails; signaturesDetails.reserve(signatures.size()); - for (const crypto::signature& signature : signatures) { - signaturesDetails.push_back(std::move(reinterpret_cast&>(signature))); + for (const Crypto::Signature& signature : signatures) { + signaturesDetails.push_back(std::move(signature)); } transactionDetails.signatures.push_back(std::move(signaturesDetails)); } - - transactionDetails.inputs.reserve(transaction.vin.size()); - for (const TransactionInput& txIn : transaction.vin) { + + transactionDetails.inputs.reserve(transaction.inputs.size()); + for (const TransactionInput& txIn : transaction.inputs) { TransactionInputDetails txInDetails; - if (txIn.type() == typeid(TransactionInputGenerate)) { + if (txIn.type() == typeid(BaseInput)) { TransactionInputGenerateDetails txInGenDetails; - txInGenDetails.height = boost::get(txIn).height; + txInGenDetails.height = boost::get(txIn).blockIndex; txInDetails.amount = 0; - for (const TransactionOutput& out : transaction.vout) { + for (const TransactionOutput& out : transaction.outputs) { txInDetails.amount += out.amount; } txInDetails.input = txInGenDetails; - } else if (txIn.type() == typeid(TransactionInputToKey)) { + } else if (txIn.type() == typeid(KeyInput)) { TransactionInputToKeyDetails txInToKeyDetails; - const TransactionInputToKey& txInToKey = boost::get(txIn); - std::list> outputReferences; + const KeyInput& txInToKey = boost::get(txIn); + std::list> outputReferences; if (!core.scanOutputkeysForIndices(txInToKey, outputReferences)) { return false; } txInDetails.amount = txInToKey.amount; - txInToKeyDetails.keyOffsets = txInToKey.keyOffsets; - txInToKeyDetails.keyImage = reinterpret_cast&>(txInToKey.keyImage); - txInToKeyDetails.mixin = txInToKey.keyOffsets.size(); + txInToKeyDetails.outputIndexes = txInToKey.outputIndexes; + txInToKeyDetails.keyImage = txInToKey.keyImage; + txInToKeyDetails.mixin = txInToKey.outputIndexes.size(); txInToKeyDetails.output.number = outputReferences.back().second; - txInToKeyDetails.output.transactionHash = reinterpret_cast&>(outputReferences.back().first); + txInToKeyDetails.output.transactionHash = outputReferences.back().first; txInDetails.input = txInToKeyDetails; - } else if (txIn.type() == typeid(TransactionInputMultisignature)) { + } else if (txIn.type() == typeid(MultisignatureInput)) { TransactionInputMultisignatureDetails txInMultisigDetails; - const TransactionInputMultisignature& txInMultisig = boost::get(txIn); + const MultisignatureInput& txInMultisig = boost::get(txIn); txInDetails.amount = txInMultisig.amount; - txInMultisigDetails.signatures = txInMultisig.signatures; - std::pair outputReference; + txInMultisigDetails.signatures = txInMultisig.signatureCount; + std::pair outputReference; if (!core.getMultisigOutputReference(txInMultisig, outputReference)) { return false; } txInMultisigDetails.output.number = outputReference.second; - txInMultisigDetails.output.transactionHash = reinterpret_cast&>(outputReference.first); + txInMultisigDetails.output.transactionHash = outputReference.first; txInDetails.input = txInMultisigDetails; } else { return false; } transactionDetails.inputs.push_back(std::move(txInDetails)); } - - transactionDetails.outputs.reserve(transaction.vout.size()); - std::vector globalIndices; - globalIndices.reserve(transaction.vout.size()); - if (!core.get_tx_outputs_gindexs(hash, globalIndices)) { - for (size_t i = 0; i < transaction.vout.size(); ++i) { + + transactionDetails.outputs.reserve(transaction.outputs.size()); + std::vector globalIndices; + globalIndices.reserve(transaction.outputs.size()); + if (!transactionDetails.inBlockchain || !core.get_tx_outputs_gindexs(hash, globalIndices)) { + for (size_t i = 0; i < transaction.outputs.size(); ++i) { globalIndices.push_back(0); } } - typedef boost::tuple outputWithIndex; - auto range = boost::combine(transaction.vout, globalIndices); + typedef boost::tuple outputWithIndex; + auto range = boost::combine(transaction.outputs, globalIndices); for (const outputWithIndex& txOutput : range) { TransactionOutputDetails txOutDetails; txOutDetails.amount = txOutput.get<0>().amount; txOutDetails.globalIndex = txOutput.get<1>(); - if (txOutput.get<0>().target.type() == typeid(TransactionOutputToKey)) { + if (txOutput.get<0>().target.type() == typeid(KeyOutput)) { TransactionOutputToKeyDetails txOutToKeyDetails; - txOutToKeyDetails.txOutKey = reinterpret_cast&>(boost::get(txOutput.get<0>().target).key); + txOutToKeyDetails.txOutKey = boost::get(txOutput.get<0>().target).key; txOutDetails.output = txOutToKeyDetails; - } else if (txOutput.get<0>().target.type() == typeid(TransactionOutputMultisignature)) { + } else if (txOutput.get<0>().target.type() == typeid(MultisignatureOutput)) { TransactionOutputMultisignatureDetails txOutMultisigDetails; - TransactionOutputMultisignature txOutMultisig = boost::get(txOutput.get<0>().target); + MultisignatureOutput txOutMultisig = boost::get(txOutput.get<0>().target); txOutMultisigDetails.keys.reserve(txOutMultisig.keys.size()); - for (const crypto::public_key& key : txOutMultisig.keys) { - txOutMultisigDetails.keys.push_back(std::move(reinterpret_cast&>(key))); + for (const Crypto::PublicKey& key : txOutMultisig.keys) { + txOutMultisigDetails.keys.push_back(std::move(key)); } - txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatures; + txOutMultisigDetails.requiredSignatures = txOutMultisig.requiredSignatureCount; txOutDetails.output = txOutMultisigDetails; } else { return false; } transactionDetails.outputs.push_back(std::move(txOutDetails)); } - + return true; } diff --git a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h old mode 100644 new mode 100755 index c7a3ea65a9..33c3ab7100 --- a/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h +++ b/src/BlockchainExplorer/BlockchainExplorerDataBuilder.h @@ -20,8 +20,8 @@ #include #include -#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" -#include "cryptonote_core/ICore.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" +#include "CryptoNoteCore/ICore.h" #include "BlockchainExplorerData.h" namespace CryptoNote { @@ -29,7 +29,7 @@ namespace CryptoNote { class BlockchainExplorerDataBuilder { public: - BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol); + BlockchainExplorerDataBuilder(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol); BlockchainExplorerDataBuilder(const BlockchainExplorerDataBuilder&) = delete; BlockchainExplorerDataBuilder(BlockchainExplorerDataBuilder&&) = delete; @@ -40,13 +40,14 @@ class BlockchainExplorerDataBuilder bool fillBlockDetails(const Block& block, BlockDetails& blockDetails); bool fillTransactionDetails(const Transaction &tx, TransactionDetails& txRpcInfo, uint64_t timestamp = 0); + static bool getPaymentId(const Transaction& transaction, Crypto::Hash& paymentId); + private: bool getMixin(const Transaction& transaction, uint64_t& mixin); - bool getPaymentId(const Transaction& transaction, crypto::hash& paymentId); bool fillTxExtra(const std::vector& rawExtra, TransactionExtraDetails& extraDetails); size_t median(std::vector& v); CryptoNote::ICore& core; - CryptoNote::ICryptonoteProtocolQuery& protocol; + CryptoNote::ICryptoNoteProtocolQuery& protocol; }; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ebd2854ea..16e89dfe3e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,20 +2,19 @@ add_definitions(-DSTATICLIB) file(GLOB_RECURSE BlockchainExplorer BlockchainExplorer/*) file(GLOB_RECURSE Common Common/*) -file(GLOB_RECURSE ConnectivityTool connectivity_tool/*) +file(GLOB_RECURSE ConnectivityTool ConnectivityTool/*) file(GLOB_RECURSE Crypto crypto/*) -file(GLOB_RECURSE CryptoNote CryptoNote/*) -file(GLOB_RECURSE CryptoNoteCore cryptonote_core/* cryptonote_config.h) -file(GLOB_RECURSE CryptoNoteProtocol cryptonote_protocol/*) -file(GLOB_RECURSE Daemon daemon/*) +file(GLOB_RECURSE CryptoNoteCore CryptoNoteCore/* CryptoNoteConfig.h) +file(GLOB_RECURSE CryptoNoteProtocol CryptoNoteProtocol/*) +file(GLOB_RECURSE Daemon Daemon/*) file(GLOB_RECURSE Http HTTP/*) file(GLOB_RECURSE InProcessNode InProcessNode/*) file(GLOB_RECURSE Logging Logging/*) -file(GLOB_RECURSE NodeRpcProxy node_rpc_proxy/*) -file(GLOB_RECURSE P2p p2p/*) -file(GLOB_RECURSE Rpc rpc/*) -file(GLOB_RECURSE Serialization serialization/*) -file(GLOB_RECURSE SimpleWallet simplewallet/*) +file(GLOB_RECURSE NodeRpcProxy NodeRpcProxy/*) +file(GLOB_RECURSE P2p P2p/*) +file(GLOB_RECURSE Rpc Rpc/*) +file(GLOB_RECURSE Serialization Serialization/*) +file(GLOB_RECURSE SimpleWallet SimpleWallet/*) if(MSVC) file(GLOB_RECURSE System System/* Platform/Windows/System/*) elseif(APPLE) @@ -23,17 +22,20 @@ file(GLOB_RECURSE System System/* Platform/OSX/System/*) else() file(GLOB_RECURSE System System/* Platform/Linux/System/*) endif() -file(GLOB_RECURSE Transfers transfers/*) -file(GLOB_RECURSE Wallet wallet/*) -file(GLOB_RECURSE PaymentService payment_service/*) +file(GLOB_RECURSE Transfers Transfers/*) +file(GLOB_RECURSE Wallet Wallet/*) +file(GLOB_RECURSE WalletLegacy WalletLegacy/*) +file(GLOB_RECURSE JsonRpcServer JsonRpcServer/*) -source_group("" FILES ${Common} ${ConnectivityTool} ${Crypto} ${CryptoNote} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet}) +file(GLOB_RECURSE PaymentGate PaymentGate/*) +file(GLOB_RECURSE PaymentGateService PaymentGateService/*) + +source_group("" FILES $${Common} ${ConnectivityTool} ${Crypto} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${JsonRpcServer} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet} ${WalletLegacy}) add_library(BlockchainExplorer ${BlockchainExplorer}) add_library(Common ${Common}) add_library(Crypto ${Crypto}) -add_library(CryptoNote ${CryptoNote}) add_library(CryptoNoteCore ${CryptoNoteCore}) add_library(Http ${Http}) add_library(InProcessNode ${InProcessNode}) @@ -44,27 +46,35 @@ add_library(P2P ${CryptoNoteProtocol} ${P2p}) add_library(Serialization ${Serialization}) add_library(System ${System}) add_library(Transfers ${Transfers}) -add_library(Wallet ${Wallet}) +add_library(Wallet ${Wallet} ${WalletLegacy}) +add_library(PaymentGate ${PaymentGate}) +add_library(JsonRpcServer ${JsonRpcServer}) add_executable(ConnectivityTool ${ConnectivityTool}) add_executable(Daemon ${Daemon}) add_executable(SimpleWallet ${SimpleWallet}) -add_executable(PaymentGate ${PaymentService}) +add_executable(PaymentGateService ${PaymentGateService}) + target_link_libraries(ConnectivityTool CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES}) -target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) target_link_libraries(SimpleWallet Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) -target_link_libraries(PaymentGate Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode BlockchainExplorer upnpc-static ${Boost_LIBRARIES}) +target_link_libraries(PaymentGateService PaymentGate JsonRpcServer Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) + +if (MSVC) + target_link_libraries(ConnectivityTool ws2_32) + target_link_libraries(SimpleWallet ws2_32) +endif () add_dependencies(Rpc version) add_dependencies(ConnectivityTool version) add_dependencies(Daemon version) add_dependencies(SimpleWallet version) -add_dependencies(PaymentGate version) +add_dependencies(PaymentGateService version) add_dependencies(P2P version) set_property(TARGET ConnectivityTool PROPERTY OUTPUT_NAME "connectivity_tool") set_property(TARGET Daemon PROPERTY OUTPUT_NAME "bytecoind") set_property(TARGET SimpleWallet PROPERTY OUTPUT_NAME "simplewallet") -set_property(TARGET PaymentGate PROPERTY OUTPUT_NAME "walletd") +set_property(TARGET PaymentGateService PROPERTY OUTPUT_NAME "walletd") diff --git a/src/Common/ArrayRef.h b/src/Common/ArrayRef.h index 8e6501fa86..9d5a391ef9 100755 --- a/src/Common/ArrayRef.h +++ b/src/Common/ArrayRef.h @@ -30,7 +30,7 @@ namespace Common { // 'data' == 'nullptr' && 'size' > 0 - Undefined // 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL // For signed integer 'Size', 'ArrayRef' with 'size' < 0 is undefined. -template class ArrayRef { +template class ArrayRef { public: typedef ObjectType Object; typedef SizeType Size; diff --git a/src/Common/ArrayView.h b/src/Common/ArrayView.h index 2fcc22ba6d..444237670e 100755 --- a/src/Common/ArrayView.h +++ b/src/Common/ArrayView.h @@ -32,7 +32,7 @@ namespace Common { // 'data' == 'nullptr' && 'size' > 0 - Undefined // 'data' != 'nullptr' && 'size' > 0 - NOTEMPTY NOTNIL // For signed integer 'Size', 'ArrayView' with 'size' < 0 is undefined. -template class ArrayView { +template class ArrayView { public: typedef Object ObjectType; typedef Size SizeType; diff --git a/src/Common/base58.cpp b/src/Common/Base58.cpp similarity index 96% rename from src/Common/base58.cpp rename to src/Common/Base58.cpp index cc1a8f4bc5..538d38f6e5 100644 --- a/src/Common/base58.cpp +++ b/src/Common/Base58.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "base58.h" +#include "Base58.h" #include #include @@ -23,11 +23,11 @@ #include "crypto/hash.h" #include "int-util.h" -#include "varint.h" +#include "Varint.h" -namespace tools +namespace Tools { - namespace base58 + namespace Base58 { namespace { @@ -227,7 +227,7 @@ namespace tools { std::string buf = get_varint_data(tag); buf += data; - crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size()); + Crypto::Hash hash = Crypto::cn_fast_hash(buf.data(), buf.size()); const char* hash_data = reinterpret_cast(&hash); buf.append(hash_data, addr_checksum_size); return encode(buf); @@ -244,11 +244,11 @@ namespace tools checksum = addr_data.substr(addr_data.size() - addr_checksum_size); addr_data.resize(addr_data.size() - addr_checksum_size); - crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size()); + Crypto::Hash hash = Crypto::cn_fast_hash(addr_data.data(), addr_data.size()); std::string expected_checksum(reinterpret_cast(&hash), addr_checksum_size); if (expected_checksum != checksum) return false; - int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag); + int read = Tools::read_varint(addr_data.begin(), addr_data.end(), tag); if (read <= 0) return false; data = addr_data.substr(read); diff --git a/src/Common/base58.h b/src/Common/Base58.h old mode 100644 new mode 100755 similarity index 96% rename from src/Common/base58.h rename to src/Common/Base58.h index b4e95901fa..f358e2450d --- a/src/Common/base58.h +++ b/src/Common/Base58.h @@ -20,9 +20,9 @@ #include #include -namespace tools +namespace Tools { - namespace base58 + namespace Base58 { std::string encode(const std::string& data); bool decode(const std::string& enc, std::string& data); diff --git a/src/Common/BlockingQueue.cpp b/src/Common/BlockingQueue.cpp index 3460308b6d..af636d156d 100644 --- a/src/Common/BlockingQueue.cpp +++ b/src/Common/BlockingQueue.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "BlockingQueue.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/Common/command_line.cpp b/src/Common/CommandLine.cpp similarity index 97% rename from src/Common/command_line.cpp rename to src/Common/CommandLine.cpp index dd887a65d0..af4b7144fb 100644 --- a/src/Common/command_line.cpp +++ b/src/Common/CommandLine.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "command_line.h" +#include "CommandLine.h" namespace command_line { diff --git a/src/Common/command_line.h b/src/Common/CommandLine.h old mode 100644 new mode 100755 similarity index 100% rename from src/Common/command_line.h rename to src/Common/CommandLine.h diff --git a/src/Common/IInputStream.h b/src/Common/IInputStream.h index 2bb867cf5a..ef8aea6222 100755 --- a/src/Common/IInputStream.h +++ b/src/Common/IInputStream.h @@ -24,7 +24,7 @@ namespace Common { class IInputStream { public: virtual ~IInputStream() { } - virtual std::size_t readSome(void* data, std::size_t size) = 0; + virtual size_t readSome(void* data, size_t size) = 0; }; } diff --git a/src/Common/IOutputStream.h b/src/Common/IOutputStream.h index 1f5ef4f99e..5361c4aa78 100755 --- a/src/Common/IOutputStream.h +++ b/src/Common/IOutputStream.h @@ -24,7 +24,7 @@ namespace Common { class IOutputStream { public: virtual ~IOutputStream() { } - virtual std::size_t writeSome(const void* data, std::size_t size) = 0; + virtual size_t writeSome(const void* data, size_t size) = 0; }; } diff --git a/src/Common/JsonValue.cpp b/src/Common/JsonValue.cpp index be656f75f7..132ed10063 100644 --- a/src/Common/JsonValue.cpp +++ b/src/Common/JsonValue.cpp @@ -492,7 +492,7 @@ const JsonValue::String& JsonValue::getString() const { return *reinterpret_cast(valueString); } -std::size_t JsonValue::size() const { +size_t JsonValue::size() const { switch (type) { case ARRAY: return reinterpret_cast(valueArray)->size(); @@ -503,7 +503,7 @@ std::size_t JsonValue::size() const { } } -JsonValue& JsonValue::operator[](std::size_t index) { +JsonValue& JsonValue::operator[](size_t index) { if (type != ARRAY) { throw std::runtime_error("JsonValue type is not ARRAY"); } @@ -511,7 +511,7 @@ JsonValue& JsonValue::operator[](std::size_t index) { return reinterpret_cast(valueArray)->at(index); } -const JsonValue& JsonValue::operator[](std::size_t index) const { +const JsonValue& JsonValue::operator[](size_t index) const { if (type != ARRAY) { throw std::runtime_error("JsonValue type is not ARRAY"); } @@ -567,7 +567,7 @@ JsonValue& JsonValue::set(const Key& key, JsonValue&& value) { return *this; } -std::size_t JsonValue::erase(const Key& key) { +size_t JsonValue::erase(const Key& key) { return getObject().erase(key); } @@ -595,7 +595,7 @@ std::ostream& operator<<(std::ostream& out, const JsonValue& jsonValue) { out << '['; if (array.size() > 0) { out << array[0]; - for (std::size_t i = 1; i < array.size(); ++i) { + for (size_t i = 1; i < array.size(); ++i) { out << ',' << array[i]; } } @@ -811,7 +811,7 @@ void JsonValue::readNull(std::istream& in) { void JsonValue::readNumber(std::istream& in, char c) { std::string text; text += c; - std::size_t dots = 0; + size_t dots = 0; for (;;) { int i = in.peek(); if (i >= '0' && i <= '9') { diff --git a/src/Common/JsonValue.h b/src/Common/JsonValue.h index 0fc6d2b5fd..5efe007714 100644 --- a/src/Common/JsonValue.h +++ b/src/Common/JsonValue.h @@ -61,7 +61,7 @@ class JsonValue { JsonValue(Real value); JsonValue(const String& value); JsonValue(String&& value); - template JsonValue(const char(&value)[size]) { + template JsonValue(const char(&value)[size]) { new(valueString)String(value, size - 1); type = STRING; } @@ -80,7 +80,7 @@ class JsonValue { JsonValue& operator=(Real value); JsonValue& operator=(const String& value); JsonValue& operator=(String&& value); - template JsonValue& operator=(const char(&value)[size]) { + template JsonValue& operator=(const char(&value)[size]) { if (type != STRING) { destructValue(); type = NIL; @@ -112,10 +112,10 @@ class JsonValue { String& getString(); const String& getString() const; - std::size_t size() const; + size_t size() const; - JsonValue& operator[](std::size_t index); - const JsonValue& operator[](std::size_t index) const; + JsonValue& operator[](size_t index); + const JsonValue& operator[](size_t index) const; JsonValue& pushBack(const JsonValue& value); JsonValue& pushBack(JsonValue&& value); @@ -129,7 +129,7 @@ class JsonValue { JsonValue& set(const Key& key, const JsonValue& value); JsonValue& set(const Key& key, JsonValue&& value); - std::size_t erase(const Key& key); + size_t erase(const Key& key); static JsonValue fromString(const std::string& source); std::string toString() const; diff --git a/src/Common/Math.cpp b/src/Common/Math.cpp index 922a231db2..495e0ef156 100644 --- a/src/Common/Math.cpp +++ b/src/Common/Math.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "Math.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/CryptoNote/MultisignatureOutput.cpp b/src/Common/MemoryInputStream.cpp old mode 100755 new mode 100644 similarity index 53% rename from src/CryptoNote/MultisignatureOutput.cpp rename to src/Common/MemoryInputStream.cpp index 2e346e2fcd..88efbef8cd --- a/src/CryptoNote/MultisignatureOutput.cpp +++ b/src/Common/MemoryInputStream.cpp @@ -15,29 +15,34 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "MultisignatureOutput.h" +#include "MemoryInputStream.h" +#include #include +#include // memcpy -namespace CryptoNote { +namespace Common { -MultisignatureOutput::MultisignatureOutput(uint64_t amount, std::vector&& keys, uint32_t requiredSignatureCount) : amount(amount), keys(std::move(keys)), requiredSignatureCount(requiredSignatureCount) { - assert(requiredSignatureCount <= keys.size()); -} +MemoryInputStream::MemoryInputStream(const void* buffer, size_t bufferSize) : +buffer(static_cast(buffer)), bufferSize(bufferSize), position(0) {} -uint64_t MultisignatureOutput::getAmount() const { - return amount; +size_t MemoryInputStream::getPosition() const { + return position; } -uint32_t MultisignatureOutput::getKeyCount() const { - return static_cast(keys.size()); +bool MemoryInputStream::endOfStream() const { + return position == bufferSize; } -const crypto::public_key& MultisignatureOutput::getKey(uint32_t index) const { - return keys[index]; -} +size_t MemoryInputStream::readSome(void* data, size_t size) { + assert(position <= bufferSize); + size_t readSize = std::min(size, bufferSize - position); -uint32_t MultisignatureOutput::getRequiredSignatureCount() const { - return requiredSignatureCount; + if (readSize > 0) { + memcpy(data, buffer + position, readSize); + position += readSize; + } + + return readSize; } } diff --git a/src/CryptoNote/KeyOutput.h b/src/Common/MemoryInputStream.h old mode 100755 new mode 100644 similarity index 66% rename from src/CryptoNote/KeyOutput.h rename to src/Common/MemoryInputStream.h index 7f53420c64..017dc65580 --- a/src/CryptoNote/KeyOutput.h +++ b/src/Common/MemoryInputStream.h @@ -17,21 +17,22 @@ #pragma once -#include "../crypto/crypto.h" +#include "IInputStream.h" -namespace CryptoNote { +namespace Common { -class KeyOutput { -public: - KeyOutput(uint64_t amount, const crypto::public_key& key); - KeyOutput(const KeyOutput& other) = delete; - KeyOutput& operator=(const KeyOutput& other) = delete; - uint64_t getAmount() const; - const crypto::public_key& getKey() const; - -private: - uint64_t amount; - crypto::public_key key; -}; + class MemoryInputStream : public IInputStream { + public: + MemoryInputStream(const void* buffer, size_t bufferSize); + size_t getPosition() const; + bool endOfStream() const; + + // IInputStream + virtual size_t readSome(void* data, size_t size) override; + private: + const char* buffer; + size_t bufferSize; + size_t position; + }; } diff --git a/src/Common/ObserverManager.h b/src/Common/ObserverManager.h old mode 100644 new mode 100755 index c383113b5a..585041db01 --- a/src/Common/ObserverManager.h +++ b/src/Common/ObserverManager.h @@ -21,7 +21,7 @@ #include #include -namespace tools { +namespace Tools { template class ObserverManager { @@ -39,6 +39,7 @@ class ObserverManager { bool remove(T* observer) { std::unique_lock lock(m_observersMutex); + auto it = std::find(m_observers.begin(), m_observers.end(), observer); if (m_observers.end() == it) { return false; diff --git a/src/Common/SignalHandler.cpp b/src/Common/SignalHandler.cpp old mode 100644 new mode 100755 index 3795df3d9a..c78bf3e34d --- a/src/Common/SignalHandler.cpp +++ b/src/Common/SignalHandler.cpp @@ -27,6 +27,7 @@ #include #else #include +#include #endif namespace { @@ -65,7 +66,7 @@ void posixHandler(int /*type*/) { } -namespace tools { +namespace Tools { bool SignalHandler::install(std::function t) { @@ -76,9 +77,23 @@ namespace tools { } return r; #else - signal(SIGINT, posixHandler); - signal(SIGTERM, posixHandler); - signal(SIGPIPE, SIG_IGN); + struct sigaction newMask; + std::memset(&newMask, 0, sizeof(struct sigaction)); + newMask.sa_handler = posixHandler; + if (sigaction(SIGINT, &newMask, nullptr) != 0) { + return false; + } + + if (sigaction(SIGTERM, &newMask, nullptr) != 0) { + return false; + } + + std::memset(&newMask, 0, sizeof(struct sigaction)); + newMask.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &newMask, nullptr) != 0) { + return false; + } + m_handler = t; return true; #endif diff --git a/src/Common/SignalHandler.h b/src/Common/SignalHandler.h old mode 100644 new mode 100755 index 14033c9a22..1807a63370 --- a/src/Common/SignalHandler.h +++ b/src/Common/SignalHandler.h @@ -19,7 +19,7 @@ #include -namespace tools { +namespace Tools { class SignalHandler { diff --git a/src/Common/StdInputStream.cpp b/src/Common/StdInputStream.cpp new file mode 100644 index 0000000000..3e5dcc085f --- /dev/null +++ b/src/Common/StdInputStream.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StdInputStream.h" + +namespace Common { + +StdInputStream::StdInputStream(std::istream& in) : in(in) { +} + +size_t StdInputStream::readSome(void* data, size_t size) { + in.read(static_cast(data), size); + return in.gcount(); +} + +} diff --git a/src/Common/StdInputStream.h b/src/Common/StdInputStream.h new file mode 100644 index 0000000000..4efb5ea7e8 --- /dev/null +++ b/src/Common/StdInputStream.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "IInputStream.h" + +namespace Common { + +class StdInputStream : public IInputStream { +public: + StdInputStream(std::istream& in); + StdInputStream& operator=(const StdInputStream&) = delete; + size_t readSome(void* data, size_t size) override; + +private: + std::istream& in; +}; + +} diff --git a/src/Common/StdOutputStream.cpp b/src/Common/StdOutputStream.cpp new file mode 100644 index 0000000000..2788c5a011 --- /dev/null +++ b/src/Common/StdOutputStream.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "StdOutputStream.h" + +namespace Common { + +StdOutputStream::StdOutputStream(std::ostream& out) : out(out) { +} + +size_t StdOutputStream::writeSome(const void* data, size_t size) { + out.write(static_cast(data), size); + if (out.bad()) { + return 0; + } + + return size; +} + +} diff --git a/src/Common/StdOutputStream.h b/src/Common/StdOutputStream.h new file mode 100644 index 0000000000..2c0dbc5114 --- /dev/null +++ b/src/Common/StdOutputStream.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "IOutputStream.h" + +namespace Common { + +class StdOutputStream : public IOutputStream { +public: + StdOutputStream(std::ostream& out); + StdOutputStream& operator=(const StdOutputStream&) = delete; + size_t writeSome(const void* data, size_t size) override; + +private: + std::ostream& out; +}; + +} diff --git a/src/Common/StreamTools.cpp b/src/Common/StreamTools.cpp index 304c458c3d..821d7cc261 100755 --- a/src/Common/StreamTools.cpp +++ b/src/Common/StreamTools.cpp @@ -22,9 +22,9 @@ namespace Common { -void read(IInputStream& in, void* data, std::size_t size) { +void read(IInputStream& in, void* data, size_t size) { while (size > 0) { - std::size_t readSize = in.readSome(data, size); + size_t readSize = in.readSome(data, size); if (readSize == 0) { throw std::runtime_error("Failed to read from IInputStream"); } @@ -72,12 +72,12 @@ void read(IInputStream& in, uint64_t& value) { read(in, &value, sizeof(value)); } -void read(IInputStream& in, std::vector& data, std::size_t size) { +void read(IInputStream& in, std::vector& data, size_t size) { data.resize(size); read(in, data.data(), size); } -void read(IInputStream& in, std::string& data, std::size_t size) { +void read(IInputStream& in, std::string& data, size_t size) { std::vector temp(size); read(in, temp.data(), size); data.assign(temp.data(), size); @@ -92,7 +92,7 @@ void readVarint(IInputStream& in, uint8_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -114,7 +114,7 @@ void readVarint(IInputStream& in, uint16_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -136,7 +136,7 @@ void readVarint(IInputStream& in, uint32_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -158,7 +158,7 @@ void readVarint(IInputStream& in, uint64_t& value) { throw std::runtime_error("readVarint, value overflow"); } - temp |= static_cast(piece & 0x7f) << shift; + temp |= static_cast(piece & 0x7f) << shift; if ((piece & 0x80) == 0) { if (piece == 0 && shift != 0) { throw std::runtime_error("readVarint, invalid value representation"); @@ -171,9 +171,9 @@ void readVarint(IInputStream& in, uint64_t& value) { value = temp; } -void write(IOutputStream& out, const void* data, std::size_t size) { +void write(IOutputStream& out, const void* data, size_t size) { while (size > 0) { - std::size_t writtenSize = out.writeSome(data, size); + size_t writtenSize = out.writeSome(data, size); if (writtenSize == 0) { throw std::runtime_error("Failed to write to IOutputStream"); } diff --git a/src/Common/StreamTools.h b/src/Common/StreamTools.h index 578ef33df5..44e25a709c 100755 --- a/src/Common/StreamTools.h +++ b/src/Common/StreamTools.h @@ -26,7 +26,7 @@ namespace Common { class IInputStream; class IOutputStream; -void read(IInputStream& in, void* data, std::size_t size); +void read(IInputStream& in, void* data, size_t size); void read(IInputStream& in, int8_t& value); void read(IInputStream& in, int16_t& value); void read(IInputStream& in, int32_t& value); @@ -35,14 +35,14 @@ void read(IInputStream& in, uint8_t& value); void read(IInputStream& in, uint16_t& value); void read(IInputStream& in, uint32_t& value); void read(IInputStream& in, uint64_t& value); -void read(IInputStream& in, std::vector& data, std::size_t size); -void read(IInputStream& in, std::string& data, std::size_t size); +void read(IInputStream& in, std::vector& data, size_t size); +void read(IInputStream& in, std::string& data, size_t size); void readVarint(IInputStream& in, uint8_t& value); void readVarint(IInputStream& in, uint16_t& value); void readVarint(IInputStream& in, uint32_t& value); void readVarint(IInputStream& in, uint64_t& value); -void write(IOutputStream& out, const void* data, std::size_t size); +void write(IOutputStream& out, const void* data, size_t size); void write(IOutputStream& out, int8_t value); void write(IOutputStream& out, int16_t value); void write(IOutputStream& out, int32_t value); @@ -61,7 +61,7 @@ template T read(IInputStream& in) { return value; } -template T read(IInputStream& in, std::size_t size) { +template T read(IInputStream& in, size_t size) { T value; read(in, value, size); return value; diff --git a/src/Common/StringBuffer.h b/src/Common/StringBuffer.h index 0b7053fbe9..4b29318080 100755 --- a/src/Common/StringBuffer.h +++ b/src/Common/StringBuffer.h @@ -24,10 +24,10 @@ namespace Common { // 'StringBuffer' is a string of fixed maximum size. -template class StringBuffer { +template class StringBuffer { public: typedef char Object; - typedef std::size_t Size; + typedef size_t Size; const static Size MAXIMUM_SIZE = MAXIMUM_SIZE_VALUE; const static Size INVALID; @@ -549,6 +549,6 @@ template class StringBuffer { Size size; }; -template const typename StringBuffer::Size StringBuffer::INVALID = std::numeric_limits::Size>::max(); +template const typename StringBuffer::Size StringBuffer::INVALID = std::numeric_limits::Size>::max(); } diff --git a/src/Common/StringInputStream.cpp b/src/Common/StringInputStream.cpp index 9ec09498de..0aefe35356 100755 --- a/src/Common/StringInputStream.cpp +++ b/src/Common/StringInputStream.cpp @@ -23,7 +23,7 @@ namespace Common { StringInputStream::StringInputStream(const std::string& in) : in(in), offset(0) { } -std::size_t StringInputStream::readSome(void* data, std::size_t size) { +size_t StringInputStream::readSome(void* data, size_t size) { if (size > in.size() - offset) { size = in.size() - offset; } diff --git a/src/Common/StringInputStream.h b/src/Common/StringInputStream.h index af6cf78e53..cb25c68588 100755 --- a/src/Common/StringInputStream.h +++ b/src/Common/StringInputStream.h @@ -25,11 +25,11 @@ namespace Common { class StringInputStream : public IInputStream { public: StringInputStream(const std::string& in); - std::size_t readSome(void* data, std::size_t size) override; + size_t readSome(void* data, size_t size) override; private: const std::string& in; - std::size_t offset; + size_t offset; }; } diff --git a/src/Common/StringOutputStream.cpp b/src/Common/StringOutputStream.cpp index 07c9df906b..ce4ae9946e 100755 --- a/src/Common/StringOutputStream.cpp +++ b/src/Common/StringOutputStream.cpp @@ -22,7 +22,7 @@ namespace Common { StringOutputStream::StringOutputStream(std::string& out) : out(out) { } -std::size_t StringOutputStream::writeSome(const void* data, std::size_t size) { +size_t StringOutputStream::writeSome(const void* data, size_t size) { out.append(static_cast(data), size); return size; } diff --git a/src/Common/StringOutputStream.h b/src/Common/StringOutputStream.h index 31c8a9c513..528a13cfb9 100755 --- a/src/Common/StringOutputStream.h +++ b/src/Common/StringOutputStream.h @@ -25,7 +25,7 @@ namespace Common { class StringOutputStream : public IOutputStream { public: StringOutputStream(std::string& out); - std::size_t writeSome(const void* data, std::size_t size) override; + size_t writeSome(const void* data, size_t size) override; private: std::string& out; diff --git a/src/Common/StringTools.cpp b/src/Common/StringTools.cpp index 750718b17c..200d2cede9 100755 --- a/src/Common/StringTools.cpp +++ b/src/Common/StringTools.cpp @@ -43,7 +43,7 @@ const uint8_t characterValues[256] = { } -std::string asString(const void* data, std::size_t size) { +std::string asString(const void* data, size_t size) { return std::string(static_cast(data), size); } @@ -51,6 +51,11 @@ std::string asString(const std::vector& data) { return std::string(reinterpret_cast(data.data()), data.size()); } +std::vector asBinaryArray(const std::string& data) { + auto dataPtr = reinterpret_cast(data.data()); + return std::vector(dataPtr, dataPtr + data.size()); +} + uint8_t fromHex(char character) { uint8_t value = characterValues[static_cast(character)]; if (value > 0x0f) { @@ -69,7 +74,7 @@ bool fromHex(char character, uint8_t& value) { return true; } -std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize) { +size_t fromHex(const std::string& text, void* data, size_t bufferSize) { if ((text.size() & 1) != 0) { throw std::runtime_error("fromHex: invalid string size"); } @@ -78,14 +83,14 @@ std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize) throw std::runtime_error("fromHex: invalid buffer size"); } - for (std::size_t i = 0; i < text.size() >> 1; ++i) { + for (size_t i = 0; i < text.size() >> 1; ++i) { static_cast(data)[i] = fromHex(text[i << 1]) << 4 | fromHex(text[(i << 1) + 1]); } return text.size() >> 1; } -bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::size_t& size) { +bool fromHex(const std::string& text, void* data, size_t bufferSize, size_t& size) { if ((text.size() & 1) != 0) { return false; } @@ -94,7 +99,7 @@ bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::s return false; } - for (std::size_t i = 0; i < text.size() >> 1; ++i) { + for (size_t i = 0; i < text.size() >> 1; ++i) { uint8_t value1; if (!fromHex(text[i << 1], value1)) { return false; @@ -118,7 +123,7 @@ std::vector fromHex(const std::string& text) { } std::vector data(text.size() >> 1); - for (std::size_t i = 0; i < data.size(); ++i) { + for (size_t i = 0; i < data.size(); ++i) { data[i] = fromHex(text[i << 1]) << 4 | fromHex(text[(i << 1) + 1]); } @@ -130,7 +135,7 @@ bool fromHex(const std::string& text, std::vector& data) { return false; } - for (std::size_t i = 0; i < text.size() >> 1; ++i) { + for (size_t i = 0; i < text.size() >> 1; ++i) { uint8_t value1; if (!fromHex(text[i << 1], value1)) { return false; @@ -147,9 +152,9 @@ bool fromHex(const std::string& text, std::vector& data) { return true; } -std::string toHex(const void* data, std::size_t size) { +std::string toHex(const void* data, size_t size) { std::string text; - for (std::size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { text += "0123456789abcdef"[static_cast(data)[i] >> 4]; text += "0123456789abcdef"[static_cast(data)[i] & 15]; } @@ -157,8 +162,8 @@ std::string toHex(const void* data, std::size_t size) { return text; } -void toHex(const void* data, std::size_t size, std::string& text) { - for (std::size_t i = 0; i < size; ++i) { +void toHex(const void* data, size_t size, std::string& text) { + for (size_t i = 0; i < size; ++i) { text += "0123456789abcdef"[static_cast(data)[i] >> 4]; text += "0123456789abcdef"[static_cast(data)[i] & 15]; } @@ -166,7 +171,7 @@ void toHex(const void* data, std::size_t size, std::string& text) { std::string toHex(const std::vector& data) { std::string text; - for (std::size_t i = 0; i < data.size(); ++i) { + for (size_t i = 0; i < data.size(); ++i) { text += "0123456789abcdef"[data[i] >> 4]; text += "0123456789abcdef"[data[i] & 15]; } @@ -175,14 +180,14 @@ std::string toHex(const std::vector& data) { } void toHex(const std::vector& data, std::string& text) { - for (std::size_t i = 0; i < data.size(); ++i) { + for (size_t i = 0; i < data.size(); ++i) { text += "0123456789abcdef"[data[i] >> 4]; text += "0123456789abcdef"[data[i] & 15]; } } std::string extract(std::string& text, char delimiter) { - std::size_t delimiterPosition = text.find(delimiter); + size_t delimiterPosition = text.find(delimiter); std::string subText; if (delimiterPosition != std::string::npos) { subText = text.substr(0, delimiterPosition); @@ -194,8 +199,8 @@ std::string extract(std::string& text, char delimiter) { return subText; } -std::string extract(const std::string& text, char delimiter, std::size_t& offset) { - std::size_t delimiterPosition = text.find(delimiter, offset); +std::string extract(const std::string& text, char delimiter, size_t& offset) { + size_t delimiterPosition = text.find(delimiter, offset); if (delimiterPosition != std::string::npos) { offset = delimiterPosition + 1; return text.substr(offset, delimiterPosition); diff --git a/src/Common/StringTools.h b/src/Common/StringTools.h index c8e70ccad2..a56203a455 100755 --- a/src/Common/StringTools.h +++ b/src/Common/StringTools.h @@ -24,24 +24,25 @@ namespace Common { -std::string asString(const void* data, std::size_t size); // Does not throw +std::string asString(const void* data, size_t size); // Does not throw std::string asString(const std::vector& data); // Does not throw +std::vector asBinaryArray(const std::string& data); uint8_t fromHex(char character); // Returns value of hex 'character', throws on error bool fromHex(char character, uint8_t& value); // Assigns value of hex 'character' to 'value', returns false on error, does not throw -std::size_t fromHex(const std::string& text, void* data, std::size_t bufferSize); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', returns actual data size, throws on error -bool fromHex(const std::string& text, void* data, std::size_t bufferSize, std::size_t& size); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', assigns actual data size to 'size', returns false on error, does not throw +size_t fromHex(const std::string& text, void* data, size_t bufferSize); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', returns actual data size, throws on error +bool fromHex(const std::string& text, void* data, size_t bufferSize, size_t& size); // Assigns values of hex 'text' to buffer 'data' up to 'bufferSize', assigns actual data size to 'size', returns false on error, does not throw std::vector fromHex(const std::string& text); // Returns values of hex 'text', throws on error bool fromHex(const std::string& text, std::vector& data); // Appends values of hex 'text' to 'data', returns false on error, does not throw template bool podFromHex(const std::string& text, T& val) { - std::size_t outSize; + size_t outSize; return fromHex(text, &val, sizeof(val), outSize) && outSize == sizeof(val); } -std::string toHex(const void* data, std::size_t size); // Returns hex representation of ('data', 'size'), does not throw -void toHex(const void* data, std::size_t size, std::string& text); // Appends hex representation of ('data', 'size') to 'text', does not throw +std::string toHex(const void* data, size_t size); // Returns hex representation of ('data', 'size'), does not throw +void toHex(const void* data, size_t size, std::string& text); // Appends hex representation of ('data', 'size') to 'text', does not throw std::string toHex(const std::vector& data); // Returns hex representation of 'data', does not throw void toHex(const std::vector& data, std::string& text); // Appends hex representation of 'data' to 'text', does not throw @@ -51,7 +52,7 @@ std::string podToHex(const T& s) { } std::string extract(std::string& text, char delimiter); // Does not throw -std::string extract(const std::string& text, char delimiter, std::size_t& offset); // Does not throw +std::string extract(const std::string& text, char delimiter, size_t& offset); // Does not throw template T fromString(const std::string& text) { // Throws on error T value; @@ -72,7 +73,7 @@ template bool fromString(const std::string& text, T& value) { // Doe template std::vector fromDelimitedString(const std::string& source, char delimiter) { // Throws on error std::vector data; - for (std::size_t offset = 0; offset != source.size();) { + for (size_t offset = 0; offset != source.size();) { data.emplace_back(fromString(extract(source, delimiter, offset))); } @@ -80,7 +81,7 @@ template std::vector fromDelimitedString(const std::string& sourc } template bool fromDelimitedString(const std::string& source, char delimiter, std::vector& data) { // Does not throw - for (std::size_t offset = 0; offset != source.size();) { + for (size_t offset = 0; offset != source.size();) { T value; if (!fromString(extract(source, delimiter, offset), value)) { return false; diff --git a/src/Common/StringView.h b/src/Common/StringView.h index 6a1eb85b78..526dd71414 100755 --- a/src/Common/StringView.h +++ b/src/Common/StringView.h @@ -32,7 +32,7 @@ namespace Common { class StringView { public: typedef char Object; - typedef std::size_t Size; + typedef size_t Size; const static Size INVALID; const static StringView EMPTY; diff --git a/src/Common/util.cpp b/src/Common/Util.cpp similarity index 99% rename from src/Common/util.cpp rename to src/Common/Util.cpp index 994929c61e..8c8dec57a2 100644 --- a/src/Common/util.cpp +++ b/src/Common/Util.cpp @@ -15,12 +15,12 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "util.h" +#include "Util.h" #include #include -#include "cryptonote_config.h" +#include "CryptoNoteConfig.h" #ifdef WIN32 #include @@ -31,7 +31,7 @@ #endif -namespace tools +namespace Tools { #ifdef WIN32 std::string get_windows_version_display_string() @@ -296,7 +296,7 @@ std::string get_nix_version_display_string() } #endif - std::string get_default_data_dir() + std::string getDefaultDataDirectory() { //namespace fs = boost::filesystem; // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME diff --git a/src/Common/util.h b/src/Common/Util.h old mode 100644 new mode 100755 similarity index 94% rename from src/Common/util.h rename to src/Common/Util.h index e3b6781869..bf4eac1539 --- a/src/Common/util.h +++ b/src/Common/Util.h @@ -20,9 +20,9 @@ #include #include -namespace tools +namespace Tools { - std::string get_default_data_dir(); + std::string getDefaultDataDirectory(); std::string get_os_version_string(); bool create_directories_if_necessary(const std::string& path); std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); diff --git a/src/Common/varint.h b/src/Common/Varint.h old mode 100644 new mode 100755 similarity index 99% rename from src/Common/varint.h rename to src/Common/Varint.h index fd97b696bd..2d7e4b079d --- a/src/Common/varint.h +++ b/src/Common/Varint.h @@ -23,7 +23,7 @@ #include #include -namespace tools { +namespace Tools { template typename std::enable_if::value && std::is_unsigned::value, void>::type diff --git a/src/Common/VectorOutputStream.cpp b/src/Common/VectorOutputStream.cpp new file mode 100644 index 0000000000..283f5d3506 --- /dev/null +++ b/src/Common/VectorOutputStream.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "VectorOutputStream.h" + +namespace Common { + +VectorOutputStream::VectorOutputStream(std::vector& out) : out(out) { +} + +size_t VectorOutputStream::writeSome(const void* data, size_t size) { + out.insert(out.end(), static_cast(data), static_cast(data) + size); + return size; +} + +} diff --git a/src/Common/VectorOutputStream.h b/src/Common/VectorOutputStream.h new file mode 100644 index 0000000000..564f267e80 --- /dev/null +++ b/src/Common/VectorOutputStream.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include "IOutputStream.h" + +namespace Common { + +class VectorOutputStream : public IOutputStream { +public: + VectorOutputStream(std::vector& out); + VectorOutputStream& operator=(const VectorOutputStream&) = delete; + size_t writeSome(const void* data, size_t size) override; + +private: + std::vector& out; +}; + +} diff --git a/src/Common/static_assert.h b/src/Common/static_assert.h old mode 100755 new mode 100644 index a893d24ad8..e7b4ce0db7 --- a/src/Common/static_assert.h +++ b/src/Common/static_assert.h @@ -1,26 +1,26 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#ifndef __cplusplus -#ifdef __clang__ - -#define static_assert _Static_assert - -#endif -#endif +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#ifndef __cplusplus +#ifdef __clang__ + +#define static_assert _Static_assert + +#endif +#endif diff --git a/src/connectivity_tool/conn_tool.cpp b/src/ConnectivityTool/ConnectivityTool.cpp old mode 100644 new mode 100755 similarity index 88% rename from src/connectivity_tool/conn_tool.cpp rename to src/ConnectivityTool/ConnectivityTool.cpp index d2b5a2a36d..231ef33a7c --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/ConnectivityTool/ConnectivityTool.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -27,14 +29,14 @@ #include #include -#include "Common/command_line.h" +#include "Common/CommandLine.h" #include "Common/StringTools.h" #include "crypto/crypto.h" -#include "p2p/p2p_protocol_defs.h" -#include "p2p/LevinProtocol.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "rpc/HttpClient.h" -#include "serialization/SerializationTools.h" +#include "P2p/P2pProtocolDefinitions.h" +#include "P2p/LevinProtocol.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Rpc/HttpClient.h" +#include "Serialization/SerializationTools.h" #include "version.h" namespace po = boost::program_options; @@ -65,33 +67,26 @@ struct response_schema { boost::optional ns_rsp; }; - -template -void withTimeout(System::Dispatcher& dispatcher, SystemObj& obj, unsigned timeout, std::function f) { - System::Event timeoutEvent(dispatcher); - System::Timer timeoutTimer(dispatcher); - - dispatcher.spawn([&](){ +void withTimeout(System::Dispatcher& dispatcher, unsigned timeout, std::function f) { + std::string result; + System::ContextGroup cg(dispatcher); + System::ContextGroupTimeout cgTimeout(dispatcher, cg, std::chrono::milliseconds(timeout)); + + cg.spawn([&] { try { - timeoutTimer.sleep(std::chrono::milliseconds(timeout)); - obj.stop(); - } catch (std::exception&) {} - timeoutEvent.set(); + f(); + } catch (System::InterruptedException&) { + result = "Operation timeout"; + } catch (std::exception& e) { + result = e.what(); + } }); - try { - f(); - } catch (System::InterruptedException&) { - timeoutEvent.wait(); - throw std::runtime_error("Operation timeout"); - } catch (std::exception&) { - timeoutTimer.stop(); - timeoutEvent.wait(); - throw; - } + cg.wait(); - timeoutTimer.stop(); - timeoutEvent.wait(); + if (!result.empty()) { + throw std::runtime_error(result); + } } @@ -125,7 +120,7 @@ std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) ss << " ]," << ENDL; ss << " \"local_peerlist_white\": [" << ENDL; i = 0; - for (const peerlist_entry &pe : networkState.local_peerlist_white) { + for (const PeerlistEntry &pe : networkState.local_peerlist_white) { ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; if (networkState.local_peerlist_white.size() - 1 != i) ss << ","; @@ -136,7 +131,7 @@ std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) ss << " \"local_peerlist_gray\": [" << ENDL; i = 0; - for (const peerlist_entry &pe : networkState.local_peerlist_gray) { + for (const PeerlistEntry &pe : networkState.local_peerlist_gray) { ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; if (networkState.local_peerlist_gray.size() - 1 != i) ss << ","; @@ -178,12 +173,12 @@ bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::re } std::cout << "Peer list white:" << ns.my_id << ENDL; - for (const peerlist_entry &pe : ns.local_peerlist_white) { + for (const PeerlistEntry &pe : ns.local_peerlist_white) { std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } std::cout << "Peer list gray:" << ns.my_id << ENDL; - for (const peerlist_entry &pe : ns.local_peerlist_gray) { + for (const PeerlistEntry &pe : ns.local_peerlist_gray) { std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } @@ -224,13 +219,13 @@ bool handle_get_daemon_info(po::variables_map& vm) { return true; } //--------------------------------------------------------------------------------------------------------------- -bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { +bool handle_request_stat(po::variables_map& vm, PeerIdType peer_id) { if(!command_line::has_arg(vm, arg_priv_key)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}"; return false; } - crypto::secret_key prvk; + Crypto::SecretKey prvk; if (!Common::podFromHex(command_line::get_arg(vm, arg_priv_key), prvk)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; return false; @@ -250,7 +245,7 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { System::TcpConnection connection; - withTimeout(dispatcher, connector, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { connection = connector.connect(addr, command_line::get_arg(vm, arg_port)); }); @@ -261,7 +256,7 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { if (!peer_id) { COMMAND_REQUEST_PEER_ID::request req; COMMAND_REQUEST_PEER_ID::response rsp; - withTimeout(dispatcher, connection, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_PEER_ID::ID, req, rsp); }); peer_id = rsp.my_id; @@ -270,10 +265,10 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { proof_of_trust pot; pot.peer_id = peer_id; pot.time = time(NULL); - crypto::public_key pubk; + Crypto::PublicKey pubk; Common::podFromHex(P2P_STAT_TRUSTED_PUB_KEY, pubk); - crypto::hash h = get_proof_of_trust_hash(pot); - crypto::generate_signature(h, pubk, prvk, pot.sign); + Crypto::Hash h = get_proof_of_trust_hash(pot); + Crypto::generate_signature(h, pubk, prvk, pot.sign); if (command_line::get_arg(vm, arg_request_stat_info)) { COMMAND_REQUEST_STAT_INFO::request req; @@ -282,7 +277,7 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { req.tr = pot; try { - withTimeout(dispatcher, connection, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_STAT_INFO::ID, req, res); }); rs.si_rsp = std::move(res); @@ -299,12 +294,12 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { if (command_line::get_arg(vm, arg_request_net_state)) { ++pot.time; h = get_proof_of_trust_hash(pot); - crypto::generate_signature(h, pubk, prvk, pot.sign); + Crypto::generate_signature(h, pubk, prvk, pot.sign); COMMAND_REQUEST_NETWORK_STATE::request req{ pot }; COMMAND_REQUEST_NETWORK_STATE::response res; try { - withTimeout(dispatcher, connection, timeout, [&] { + withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_NETWORK_STATE::ID, req, res); }); rs.ns_rsp = std::move(res); @@ -328,8 +323,8 @@ bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) { //--------------------------------------------------------------------------------------------------------------- bool generate_and_print_keys() { - crypto::public_key pk; - crypto::secret_key sk; + Crypto::PublicKey pk; + Crypto::SecretKey sk; generate_keys(pk, sk); std::cout << "PUBLIC KEY: " << Common::podToHex(pk) << ENDL << "PRIVATE KEY: " << Common::podToHex(sk); diff --git a/src/CryptoNote/BaseTransaction.cpp b/src/CryptoNote/BaseTransaction.cpp deleted file mode 100755 index b4411aa81d..0000000000 --- a/src/CryptoNote/BaseTransaction.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "BaseTransaction.h" -#include -#include - -namespace CryptoNote { - -BaseTransaction::BaseTransaction( - uint64_t blockIndex, - uint64_t unlockTime, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra) : - blockIndex(blockIndex), - unlockTime(unlockTime), - keyOutputs(std::move(keyOutputs)), - multisignatureOutputs(std::move(multisignatureOutputs)), - extra(std::move(extra)) { -} - -BaseTransaction::BaseTransaction(BaseTransaction&& other) : blockIndex(other.blockIndex), unlockTime(other.unlockTime), keyOutputs(std::move(other.keyOutputs)), multisignatureOutputs(std::move(other.multisignatureOutputs)), extra(std::move(other.extra)) { -} - -uint64_t BaseTransaction::getBlockIndex() const { - return blockIndex; -} - -uint64_t BaseTransaction::getUnlockTime() const { - return unlockTime; -} - -uint32_t BaseTransaction::getOutputCount() const { - return static_cast(keyOutputs.size() + multisignatureOutputs.size()); -} - -BaseTransaction::OutputType BaseTransaction::getOutputType(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - if (iterator != keyOutputs.end() && iterator->index == index) { - return KEY_OUTPUT; - } - - return MULTISIGNATURE_OUTPUT; -} - -const KeyOutput& BaseTransaction::getKeyOutput(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - assert(iterator != keyOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const MultisignatureOutput& BaseTransaction::getMultisignatureOutput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); - assert(iterator != multisignatureOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const std::vector& BaseTransaction::getExtra() const { - return extra; -} - -} diff --git a/src/CryptoNote/BaseTransaction.h b/src/CryptoNote/BaseTransaction.h deleted file mode 100755 index d795cfd9bb..0000000000 --- a/src/CryptoNote/BaseTransaction.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "KeyOutput.h" -#include "MultisignatureOutput.h" - -namespace CryptoNote { - -class BaseTransaction { -public: - enum OutputType { - KEY_OUTPUT = 0, - MULTISIGNATURE_OUTPUT = 1 - }; - - struct KeyOutputEntry { - uint32_t index; - KeyOutput output; - }; - - struct MultisignatureOutputEntry { - uint32_t index; - MultisignatureOutput output; - }; - - BaseTransaction(uint64_t blockIndex, uint64_t unlockTime, std::vector&& keyOutputs, std::vector&& multisignatureOutputs, std::vector&& extra); - BaseTransaction(const BaseTransaction& other) = delete; - BaseTransaction(BaseTransaction&& other); - BaseTransaction& operator=(const BaseTransaction& other) = delete; - uint64_t getBlockIndex() const; - uint64_t getUnlockTime() const; - uint32_t getOutputCount() const; - OutputType getOutputType(uint32_t index) const; - const KeyOutput& getKeyOutput(uint32_t index) const; - const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; - const std::vector& getExtra() const; - -private: - uint64_t blockIndex; - uint64_t unlockTime; - std::vector keyOutputs; - std::vector multisignatureOutputs; - std::vector extra; -}; - -} diff --git a/src/CryptoNote/Block.cpp b/src/CryptoNote/Block.cpp deleted file mode 100755 index 38f71359d4..0000000000 --- a/src/CryptoNote/Block.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Block.h" -#include "KeyInput.h" -#include "KeyOutput.h" -#include "MultisignatureInput.h" -#include "MultisignatureOutput.h" -#include "Transaction.h" - -namespace CryptoNote { - -Block::Block( - uint8_t majorVersion, - uint8_t minorVersion, - uint64_t timestamp, - const crypto::hash& previousBlockHash, - BaseTransaction&& baseTransaction, - std::vector&& transactions, - uint8_t parentMajorVersion, - uint8_t parentMinorVersion, - uint32_t nonce, - const crypto::hash& parentPreviousBlockHash, - BaseTransaction&& parentBaseTransaction, - std::vector&& parentBaseTransactionBranch, - uint32_t parentTransactionCount, - std::vector&& branch) : - majorVersion(majorVersion), - minorVersion(minorVersion), - timestamp(timestamp), - previousBlockHash(previousBlockHash), - baseTransaction(std::move(baseTransaction)), - transactions(std::move(transactions)), - parentMajorVersion(parentMajorVersion), - parentMinorVersion(parentMinorVersion), - nonce(nonce), - parentPreviousBlockHash(parentPreviousBlockHash), - parentBaseTransaction(std::move(parentBaseTransaction)), - parentBaseTransactionBranch(std::move(parentBaseTransactionBranch)), - parentTransactionCount(parentTransactionCount), - branch(std::move(branch)) { -} - -uint8_t Block::getMajorVersion() const { - return majorVersion; -} - -uint8_t Block::getMinorVersion() const { - return minorVersion; -} - -uint64_t Block::getTimestamp() const { - return timestamp; -} - -const crypto::hash& Block::getPreviousBlockHash() const { - return previousBlockHash; -} - -const BaseTransaction& Block::getBaseTransaction() const { - return baseTransaction; -} - -uint32_t Block::getTransactionCount() const { - return static_cast(transactions.size()); -} - -const Transaction& Block::getTransaction(uint32_t index) const { - return transactions[index]; -} - -uint8_t Block::getParentMajorVersion() const { - return parentMajorVersion; -} - -uint8_t Block::getParentMinorVersion() const { - return parentMinorVersion; -} - -uint32_t Block::getNonce() const { - return nonce; -} - -const crypto::hash& Block::getParentPreviousBlockHash() const { - return parentPreviousBlockHash; -} - -const BaseTransaction& Block::getParentBaseTransaction() const { - return parentBaseTransaction; -} - -const std::vector& Block::getParentBaseTransactionBranch() const { - return parentBaseTransactionBranch; -} - -uint32_t Block::getParentTransactionCount() const { - return parentTransactionCount; -} - -const std::vector& Block::getBranch() const { - return branch; -} - -} diff --git a/src/CryptoNote/Block.h b/src/CryptoNote/Block.h deleted file mode 100755 index 2aba9bdaf6..0000000000 --- a/src/CryptoNote/Block.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "BaseTransaction.h" - -namespace CryptoNote { - -class Transaction; - -class Block { -public: - Block( - uint8_t majorVersion, - uint8_t minorVersion, - uint64_t timestamp, - const crypto::hash& previousBlockHash, - BaseTransaction&& baseTransaction, - std::vector&& transactions, - uint8_t parentMajorVersion, - uint8_t parentMinorVersion, - uint32_t nonce, - const crypto::hash& parentPreviousBlockHash, - BaseTransaction&& parentBaseTransaction, - std::vector&& parentBaseTransactionBranch, - uint32_t parentTransactionCount, - std::vector&& branch); - Block(const Block& other) = delete; - Block& operator=(const Block& other) = delete; - uint8_t getMajorVersion() const; - uint8_t getMinorVersion() const; - uint64_t getTimestamp() const; - const crypto::hash& getPreviousBlockHash() const; - const BaseTransaction& getBaseTransaction() const; - uint32_t getTransactionCount() const; - const Transaction& getTransaction(uint32_t index) const; - uint8_t getParentMajorVersion() const; - uint8_t getParentMinorVersion() const; - uint32_t getNonce() const; - const crypto::hash& getParentPreviousBlockHash() const; - const BaseTransaction& getParentBaseTransaction() const; - const std::vector& getParentBaseTransactionBranch() const; - uint32_t getParentTransactionCount() const; - const std::vector& getBranch() const; - -private: - uint8_t majorVersion; - uint8_t minorVersion; - uint64_t timestamp; - crypto::hash previousBlockHash; - BaseTransaction baseTransaction; - std::vector transactions; - uint8_t parentMajorVersion; - uint8_t parentMinorVersion; - uint32_t nonce; - crypto::hash parentPreviousBlockHash; - BaseTransaction parentBaseTransaction; - std::vector parentBaseTransactionBranch; - uint32_t parentTransactionCount; - std::vector branch; -}; - -} diff --git a/src/CryptoNote/KeyInput.cpp b/src/CryptoNote/KeyInput.cpp deleted file mode 100755 index ed5b7f0b40..0000000000 --- a/src/CryptoNote/KeyInput.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "KeyInput.h" - -namespace CryptoNote { - -KeyInput::KeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage) : amount(amount), outputs(std::move(outputs)), keyImage(keyImage) { -} - -uint64_t KeyInput::getAmount() const { - return amount; -} - -uint32_t KeyInput::getOutputCount() const { - return static_cast(outputs.size()); -} - -uint32_t KeyInput::getOutputIndex(uint32_t index) const { - return outputs[index].index; -} - -const crypto::signature& KeyInput::getOutputSignature(uint32_t index) const { - return outputs[index].signature; -} - -const crypto::key_image& KeyInput::getKeyImage() const { - return keyImage; -} - -} diff --git a/src/CryptoNote/KeyInput.h b/src/CryptoNote/KeyInput.h deleted file mode 100755 index 511d491a2d..0000000000 --- a/src/CryptoNote/KeyInput.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "../crypto/crypto.h" - -namespace CryptoNote { - -class KeyInput { -public: - struct Output { - uint32_t index; - crypto::signature signature; - }; - - KeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage); - KeyInput(const KeyInput& other) = delete; - KeyInput& operator=(const KeyInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputCount() const; - uint32_t getOutputIndex(uint32_t index) const; - const crypto::signature& getOutputSignature(uint32_t index) const; - const crypto::key_image& getKeyImage() const; - -private: - uint64_t amount; - std::vector outputs; - crypto::key_image keyImage; -}; - -} diff --git a/src/CryptoNote/MultisignatureInput.cpp b/src/CryptoNote/MultisignatureInput.cpp deleted file mode 100755 index 36482e3ade..0000000000 --- a/src/CryptoNote/MultisignatureInput.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "MultisignatureInput.h" - -namespace CryptoNote { - -MultisignatureInput::MultisignatureInput(uint64_t amount, uint32_t outputIndex, std::vector&& signatures) : amount(amount), outputIndex(outputIndex), signatures(std::move(signatures)) { -} - -uint64_t MultisignatureInput::getAmount() const { - return amount; -} - -uint32_t MultisignatureInput::getOutputIndex() const { - return outputIndex; -} - -uint32_t MultisignatureInput::getSignatureCount() const { - return static_cast(signatures.size()); -} - -const crypto::signature& MultisignatureInput::getSignature(uint32_t index) const { - return signatures[index]; -} - -} diff --git a/src/CryptoNote/MultisignatureInput.h b/src/CryptoNote/MultisignatureInput.h deleted file mode 100755 index f6efca4068..0000000000 --- a/src/CryptoNote/MultisignatureInput.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "../crypto/crypto.h" - -namespace CryptoNote { - -class MultisignatureInput { -public: - MultisignatureInput(uint64_t amount, uint32_t outputIndex, std::vector&& signatures); - MultisignatureInput(const MultisignatureInput& other) = delete; - MultisignatureInput& operator=(const MultisignatureInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputIndex() const; - uint32_t getSignatureCount() const; - const crypto::signature& getSignature(uint32_t index) const; - -private: - uint64_t amount; - uint32_t outputIndex; - std::vector signatures; -}; - -} diff --git a/src/CryptoNote/Transaction.cpp b/src/CryptoNote/Transaction.cpp deleted file mode 100755 index 6daa4367d4..0000000000 --- a/src/CryptoNote/Transaction.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Transaction.h" -#include -#include - -namespace CryptoNote { - -Transaction::Transaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra) : - unlockTime(unlockTime), - keyInputs(std::move(keyInputs)), - multisignatureInputs(std::move(multisignatureInputs)), - keyOutputs(std::move(keyOutputs)), - multisignatureOutputs(std::move(multisignatureOutputs)), - extra(std::move(extra)) { -} - -Transaction::Transaction( - Transaction&& other) : - unlockTime(other.unlockTime), - keyInputs(std::move(other.keyInputs)), - multisignatureInputs(std::move(other.multisignatureInputs)), - keyOutputs(std::move(other.keyOutputs)), - multisignatureOutputs(std::move(other.multisignatureOutputs)), - extra(std::move(other.extra)) { -} - -uint64_t Transaction::getUnlockTime() const { - return unlockTime; -} - -uint32_t Transaction::getInputCount() const { - return static_cast(keyInputs.size() + multisignatureInputs.size()); -} - -Transaction::InputType Transaction::getInputType(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - if (iterator != keyInputs.end() && iterator->index == index) { - return KEY_INPUT; - } - - return MULTISIGNATURE_INPUT; -} - -const KeyInput& Transaction::getKeyInput(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - assert(iterator != keyInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -const MultisignatureInput& Transaction::getMultisignatureInput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureInputs.begin(), multisignatureInputs.end(), index, [](const MultisignatureInputEntry& multisignatureInputEntry, uint32_t index)->bool { return multisignatureInputEntry.index < index; }); - assert(iterator != multisignatureInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -uint32_t Transaction::getOutputCount() const { - return static_cast(keyOutputs.size() + multisignatureOutputs.size()); -} - -Transaction::OutputType Transaction::getOutputType(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - if (iterator != keyOutputs.end() && iterator->index == index) { - return KEY_OUTPUT; - } - - return MULTISIGNATURE_OUTPUT; -} - -const KeyOutput& Transaction::getKeyOutput(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - assert(iterator != keyOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const MultisignatureOutput& Transaction::getMultisignatureOutput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); - assert(iterator != multisignatureOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const std::vector& Transaction::getExtra() const { - return extra; -} - -} diff --git a/src/CryptoNote/Transaction.h b/src/CryptoNote/Transaction.h deleted file mode 100755 index be346cea50..0000000000 --- a/src/CryptoNote/Transaction.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "KeyInput.h" -#include "KeyOutput.h" -#include "MultisignatureInput.h" -#include "MultisignatureOutput.h" - -namespace CryptoNote { - -class Transaction { -public: - enum InputType { - KEY_INPUT = 0, - MULTISIGNATURE_INPUT = 1 - }; - - enum OutputType { - KEY_OUTPUT = 0, - MULTISIGNATURE_OUTPUT = 1 - }; - - struct KeyInputEntry { - uint32_t index; - KeyInput input; - }; - - struct KeyOutputEntry { - uint32_t index; - KeyOutput output; - }; - - struct MultisignatureInputEntry { - uint32_t index; - MultisignatureInput input; - }; - - struct MultisignatureOutputEntry { - uint32_t index; - MultisignatureOutput output; - }; - - Transaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra); - Transaction(const Transaction& other) = delete; - Transaction(Transaction&& other); - Transaction& operator=(const Transaction& other) = delete; - uint64_t getUnlockTime() const; - uint32_t getInputCount() const; - InputType getInputType(uint32_t index) const; - const KeyInput& getKeyInput(uint32_t index) const; - const MultisignatureInput& getMultisignatureInput(uint32_t index) const; - uint32_t getOutputCount() const; - OutputType getOutputType(uint32_t index) const; - const KeyOutput& getKeyOutput(uint32_t index) const; - const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; - const std::vector& getExtra() const; - -private: - uint64_t unlockTime; - std::vector keyInputs; - std::vector multisignatureInputs; - std::vector keyOutputs; - std::vector multisignatureOutputs; - std::vector extra; -}; - -} diff --git a/src/CryptoNote/UnsignedMultisignatureInput.cpp b/src/CryptoNote/UnsignedMultisignatureInput.cpp deleted file mode 100755 index cfcd734f04..0000000000 --- a/src/CryptoNote/UnsignedMultisignatureInput.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "UnsignedMultisignatureInput.h" - -namespace CryptoNote { - -UnsignedMultisignatureInput::UnsignedMultisignatureInput(uint64_t amount, uint32_t outputIndex) : amount(amount), outputIndex(outputIndex) { -} - -uint64_t UnsignedMultisignatureInput::getAmount() const { - return amount; -} - -uint32_t UnsignedMultisignatureInput::getOutputIndex() const { - return outputIndex; -} - -} diff --git a/src/CryptoNote/UnsignedTransaction.cpp b/src/CryptoNote/UnsignedTransaction.cpp deleted file mode 100755 index 64cfe80cbe..0000000000 --- a/src/CryptoNote/UnsignedTransaction.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "UnsignedTransaction.h" -#include -#include - -namespace CryptoNote { - -UnsignedTransaction::UnsignedTransaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra) : - unlockTime(unlockTime), - keyInputs(std::move(keyInputs)), - multisignatureInputs(std::move(multisignatureInputs)), - keyOutputs(std::move(keyOutputs)), - multisignatureOutputs(std::move(multisignatureOutputs)), - extra(std::move(extra)) { -} - -UnsignedTransaction::UnsignedTransaction( - UnsignedTransaction&& other) : - unlockTime(other.unlockTime), - keyInputs(std::move(other.keyInputs)), - multisignatureInputs(std::move(other.multisignatureInputs)), - keyOutputs(std::move(other.keyOutputs)), - multisignatureOutputs(std::move(other.multisignatureOutputs)), - extra(std::move(other.extra)) { -} - -uint64_t UnsignedTransaction::getUnlockTime() const { - return unlockTime; -} - -uint32_t UnsignedTransaction::getInputCount() const { - return static_cast(keyInputs.size() + multisignatureInputs.size()); -} - -UnsignedTransaction::InputType UnsignedTransaction::getInputType(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - if (iterator != keyInputs.end() && iterator->index == index) { - return KEY_INPUT; - } - - return MULTISIGNATURE_INPUT; -} - -const UnsignedKeyInput& UnsignedTransaction::getKeyInput(uint32_t index) const { - auto iterator = std::lower_bound(keyInputs.begin(), keyInputs.end(), index, [](const KeyInputEntry& keyInputEntry, uint32_t index)->bool { return keyInputEntry.index < index; }); - assert(iterator != keyInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -const UnsignedMultisignatureInput& UnsignedTransaction::getMultisignatureInput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureInputs.begin(), multisignatureInputs.end(), index, [](const MultisignatureInputEntry& multisignatureInputEntry, uint32_t index)->bool { return multisignatureInputEntry.index < index; }); - assert(iterator != multisignatureInputs.end()); - assert(iterator->index == index); - return iterator->input; -} - -uint32_t UnsignedTransaction::getOutputCount() const { - return static_cast(keyOutputs.size() + multisignatureOutputs.size()); -} - -UnsignedTransaction::OutputType UnsignedTransaction::getOutputType(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - if (iterator != keyOutputs.end() && iterator->index == index) { - return KEY_OUTPUT; - } - - return MULTISIGNATURE_OUTPUT; -} - -const KeyOutput& UnsignedTransaction::getKeyOutput(uint32_t index) const { - auto iterator = std::lower_bound(keyOutputs.begin(), keyOutputs.end(), index, [](const KeyOutputEntry& keyOutputEntry, uint32_t index)->bool { return keyOutputEntry.index < index; }); - assert(iterator != keyOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const MultisignatureOutput& UnsignedTransaction::getMultisignatureOutput(uint32_t index) const { - auto iterator = std::lower_bound(multisignatureOutputs.begin(), multisignatureOutputs.end(), index, [](const MultisignatureOutputEntry& multisignatureOutputEntry, uint32_t index)->bool { return multisignatureOutputEntry.index < index; }); - assert(iterator != multisignatureOutputs.end()); - assert(iterator->index == index); - return iterator->output; -} - -const std::vector& UnsignedTransaction::getExtra() const { - return extra; -} - -} diff --git a/src/CryptoNote/UnsignedTransaction.h b/src/CryptoNote/UnsignedTransaction.h deleted file mode 100755 index b82246177a..0000000000 --- a/src/CryptoNote/UnsignedTransaction.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "KeyOutput.h" -#include "MultisignatureOutput.h" -#include "UnsignedKeyInput.h" -#include "UnsignedMultisignatureInput.h" - -namespace CryptoNote { - -class UnsignedTransaction { -public: - enum InputType { - KEY_INPUT = 0, - MULTISIGNATURE_INPUT = 1 - }; - - enum OutputType { - KEY_OUTPUT = 0, - MULTISIGNATURE_OUTPUT = 1 - }; - - struct KeyInputEntry { - uint32_t index; - UnsignedKeyInput input; - }; - - struct KeyOutputEntry { - uint32_t index; - KeyOutput output; - }; - - struct MultisignatureInputEntry { - uint32_t index; - UnsignedMultisignatureInput input; - }; - - struct MultisignatureOutputEntry { - uint32_t index; - MultisignatureOutput output; - }; - - UnsignedTransaction( - uint64_t unlockTime, - std::vector&& keyInputs, - std::vector&& multisignatureInputs, - std::vector&& keyOutputs, - std::vector&& multisignatureOutputs, - std::vector&& extra); - UnsignedTransaction(const UnsignedTransaction& other) = delete; - UnsignedTransaction(UnsignedTransaction&& other); - UnsignedTransaction& operator=(const UnsignedTransaction& other) = delete; - uint64_t getUnlockTime() const; - uint32_t getInputCount() const; - InputType getInputType(uint32_t index) const; - const UnsignedKeyInput& getKeyInput(uint32_t index) const; - const UnsignedMultisignatureInput& getMultisignatureInput(uint32_t index) const; - uint32_t getOutputCount() const; - OutputType getOutputType(uint32_t index) const; - const KeyOutput& getKeyOutput(uint32_t index) const; - const MultisignatureOutput& getMultisignatureOutput(uint32_t index) const; - const std::vector& getExtra() const; - -private: - uint64_t unlockTime; - std::vector keyInputs; - std::vector multisignatureInputs; - std::vector keyOutputs; - std::vector multisignatureOutputs; - std::vector extra; -}; - -} diff --git a/src/cryptonote_config.h b/src/CryptoNoteConfig.h similarity index 95% rename from src/cryptonote_config.h rename to src/CryptoNoteConfig.h index 5e5e1e139d..17b3d14bcf 100644 --- a/src/cryptonote_config.h +++ b/src/CryptoNoteConfig.h @@ -74,6 +74,7 @@ const char CRYPTONOTE_BLOCKINDEXES_FILENAME[] = "blockindexes.dat const char CRYPTONOTE_BLOCKSCACHE_FILENAME[] = "blockscache.dat"; const char CRYPTONOTE_POOLDATA_FILENAME[] = "poolstate.bin"; const char P2P_NET_DATA_FILENAME[] = "p2pstate.bin"; +const char CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME[] = "blockchainindices.dat"; const char MINER_CONFIG_FILE_NAME[] = "miner_conf.json"; } // parameters @@ -95,7 +96,9 @@ const int RPC_DEFAULT_PORT = 8081; const size_t P2P_LOCAL_WHITE_PEERLIST_LIMIT = 1000; const size_t P2P_LOCAL_GRAY_PEERLIST_LIMIT = 5000; +const size_t P2P_CONNECTION_MAX_WRITE_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MB const uint32_t P2P_DEFAULT_CONNECTIONS_COUNT = 8; +const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; const uint32_t P2P_DEFAULT_HANDSHAKE_INTERVAL = 60; // seconds const uint32_t P2P_DEFAULT_PACKET_MAX_SIZE = 50000000; // 50000000 bytes maximum packet size const uint32_t P2P_DEFAULT_PEERS_IN_HANDSHAKE = 250; @@ -103,10 +106,7 @@ const uint32_t P2P_DEFAULT_CONNECTION_TIMEOUT = 5000; // const uint32_t P2P_DEFAULT_PING_CONNECTION_TIMEOUT = 2000; // 2 seconds const uint64_t P2P_DEFAULT_INVOKE_TIMEOUT = 60 * 2 * 1000; // 2 minutes const size_t P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT = 5000; // 5 seconds -const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4"; -const size_t P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT = 70; - -const unsigned THREAD_STACK_SIZE = 5 * 1024 * 1024; +const char P2P_STAT_TRUSTED_PUB_KEY[] = "93467628927eaa0b13a4e52e61864a75aa475e67f6b5748eb3fc1d2fe468aed4"; const char* const SEED_NODES[] = { "seed.bytecoin.org:8080", @@ -122,7 +122,7 @@ const char* const SEED_NODES[] = { }; struct CheckpointData { - uint64_t height; + uint32_t height; const char* blockId; }; @@ -154,7 +154,8 @@ const CheckpointData CHECKPOINTS[] = { {750300, "5117631dbeb5c14748a91127a515ecbf13f6849e14fda7ee03cd55da41f1710c"}, {780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"}, {785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"}, - {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"} + {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"}, + {796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"} }; } // CryptoNote diff --git a/src/cryptonote_core/account.cpp b/src/CryptoNoteCore/Account.cpp old mode 100644 new mode 100755 similarity index 70% rename from src/cryptonote_core/account.cpp rename to src/CryptoNoteCore/Account.cpp index e67042aae4..9cf7e45837 --- a/src/cryptonote_core/account.cpp +++ b/src/CryptoNoteCore/Account.cpp @@ -15,35 +15,35 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "account.h" -#include "cryptonote_serialization.h" +#include "Account.h" +#include "CryptoNoteSerialization.h" namespace CryptoNote { //----------------------------------------------------------------- -account_base::account_base() { - set_null(); +AccountBase::AccountBase() { + setNull(); } //----------------------------------------------------------------- -void account_base::set_null() { - m_keys = account_keys(); +void AccountBase::setNull() { + m_keys = AccountKeys(); } //----------------------------------------------------------------- -void account_base::generate() { - crypto::generate_keys(m_keys.m_account_address.m_spendPublicKey, m_keys.m_spend_secret_key); - crypto::generate_keys(m_keys.m_account_address.m_viewPublicKey, m_keys.m_view_secret_key); +void AccountBase::generate() { + Crypto::generate_keys(m_keys.address.spendPublicKey, m_keys.spendSecretKey); + Crypto::generate_keys(m_keys.address.viewPublicKey, m_keys.viewSecretKey); m_creation_timestamp = time(NULL); } //----------------------------------------------------------------- -const account_keys &account_base::get_keys() const { +const AccountKeys &AccountBase::getAccountKeys() const { return m_keys; } -void account_base::set_keys(const account_keys &keys) { +void AccountBase::setAccountKeys(const AccountKeys &keys) { m_keys = keys; } //----------------------------------------------------------------- -void account_base::serialize(ISerializer &s) { +void AccountBase::serialize(ISerializer &s) { s(m_keys, "m_keys"); s(m_creation_timestamp, "m_creation_timestamp"); } diff --git a/src/cryptonote_core/account.h b/src/CryptoNoteCore/Account.h old mode 100644 new mode 100755 similarity index 73% rename from src/cryptonote_core/account.h rename to src/CryptoNoteCore/Account.h index 35939576c2..2e879210c6 --- a/src/cryptonote_core/account.h +++ b/src/CryptoNoteCore/Account.h @@ -17,33 +17,26 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" #include "crypto/crypto.h" namespace CryptoNote { - template struct AccountBaseSerializer; class ISerializer; - struct account_keys { - AccountPublicAddress m_account_address; - crypto::secret_key m_spend_secret_key; - crypto::secret_key m_view_secret_key; - }; - /************************************************************************/ /* */ /************************************************************************/ - class account_base { + class AccountBase { public: - account_base(); + AccountBase(); void generate(); - const account_keys& get_keys() const; - void set_keys(const account_keys& keys); - + const AccountKeys& getAccountKeys() const; + void setAccountKeys(const AccountKeys& keys); uint64_t get_createtime() const { return m_creation_timestamp; } void set_createtime(uint64_t val) { m_creation_timestamp = val; } + void serialize(ISerializer& s); template inline void serialize(t_archive &a, const unsigned int /*ver*/) { @@ -51,14 +44,9 @@ namespace CryptoNote { a & m_creation_timestamp; } - void serialize(ISerializer& s); - private: - void set_null(); - account_keys m_keys; + void setNull(); + AccountKeys m_keys; uint64_t m_creation_timestamp; - - friend struct AccountBaseSerializer; - friend struct AccountBaseSerializer; }; } diff --git a/src/CryptoNoteCore/BlockIndex.cpp b/src/CryptoNoteCore/BlockIndex.cpp new file mode 100755 index 0000000000..bcd0f39225 --- /dev/null +++ b/src/CryptoNoteCore/BlockIndex.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockIndex.h" + +#include + +#include "CryptoNoteSerialization.h" +#include "Serialization/SerializationOverloads.h" + +namespace CryptoNote { + Crypto::Hash BlockIndex::getBlockId(uint32_t height) const { + assert(height < m_container.size()); + + return m_container[static_cast(height)]; + } + + std::vector BlockIndex::getBlockIds(uint32_t startBlockIndex, uint32_t maxCount) const { + std::vector result; + if (startBlockIndex >= m_container.size()) { + return result; + } + + size_t count = std::min(static_cast(maxCount), m_container.size() - static_cast(startBlockIndex)); + result.reserve(count); + for (size_t i = 0; i < count; ++i) { + result.push_back(m_container[startBlockIndex + i]); + } + + return result; + } + + bool BlockIndex::findSupplement(const std::vector& ids, uint32_t& offset) const { + for (const auto& id : ids) { + if (getBlockHeight(id, offset)) { + return true; + } + } + + return false; + } + + std::vector BlockIndex::buildSparseChain(const Crypto::Hash& startBlockId) const { + assert(m_index.count(startBlockId) > 0); + + uint32_t startBlockHeight; + getBlockHeight(startBlockId, startBlockHeight); + + std::vector result; + size_t sparseChainEnd = static_cast(startBlockHeight + 1); + for (size_t i = 1; i <= sparseChainEnd; i *= 2) { + result.emplace_back(m_container[sparseChainEnd - i]); + } + + if (result.back() != m_container[0]) { + result.emplace_back(m_container[0]); + } + + return result; + } + + Crypto::Hash BlockIndex::getTailId() const { + assert(!m_container.empty()); + return m_container.back(); + } + + void BlockIndex::serialize(ISerializer& s) { + if (s.type() == ISerializer::INPUT) { + readSequence(std::back_inserter(m_container), "index", s); + } else { + writeSequence(m_container.begin(), m_container.end(), "index", s); + } + } +} diff --git a/src/cryptonote_core/BlockIndex.h b/src/CryptoNoteCore/BlockIndex.h old mode 100644 new mode 100755 similarity index 69% rename from src/cryptonote_core/BlockIndex.h rename to src/CryptoNoteCore/BlockIndex.h index d447d1905a..8f2cc942e7 --- a/src/cryptonote_core/BlockIndex.h +++ b/src/CryptoNoteCore/BlockIndex.h @@ -20,10 +20,12 @@ #include #include "crypto/hash.h" -#include +#include namespace CryptoNote { + class ISerializer; + class BlockIndex { public: @@ -36,49 +38,47 @@ namespace CryptoNote } // returns true if new element was inserted, false if already exists - bool push(const crypto::hash& h) { + bool push(const Crypto::Hash& h) { auto result = m_container.push_back(h); return result.second; } - bool hasBlock(const crypto::hash& h) const { + bool hasBlock(const Crypto::Hash& h) const { return m_index.find(h) != m_index.end(); } - bool getBlockHeight(const crypto::hash& h, uint64_t& height) const { + bool getBlockHeight(const Crypto::Hash& h, uint32_t& height) const { auto hi = m_index.find(h); if (hi == m_index.end()) return false; - height = std::distance(m_container.begin(), m_container.project<0>(hi)); + height = static_cast(std::distance(m_container.begin(), m_container.project<0>(hi))); return true; } - size_t size() const { - return m_container.size(); + uint32_t size() const { + return static_cast(m_container.size()); } void clear() { m_container.clear(); } - crypto::hash getBlockId(uint64_t height) const; - bool getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) const; - bool findSupplement(const std::list& ids, uint64_t& offset) const; - bool getShortChainHistory(std::list& ids) const; - crypto::hash getTailId() const; + Crypto::Hash getBlockId(uint32_t height) const; + std::vector getBlockIds(uint32_t startBlockIndex, uint32_t maxCount) const; + bool findSupplement(const std::vector& ids, uint32_t& offset) const; + std::vector buildSparseChain(const Crypto::Hash& startBlockId) const; + Crypto::Hash getTailId() const; - template void serialize(Archive& ar, const unsigned int version) { - ar & m_container; - } + void serialize(ISerializer& s); private: typedef boost::multi_index_container < - crypto::hash, + Crypto::Hash, boost::multi_index::indexed_by< boost::multi_index::random_access<>, - boost::multi_index::hashed_unique> + boost::multi_index::hashed_unique> > > ContainerT; diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/CryptoNoteCore/Blockchain.cpp similarity index 59% rename from src/cryptonote_core/blockchain_storage.cpp rename to src/CryptoNoteCore/Blockchain.cpp index 7c49b1c6eb..f069131e55 100644 --- a/src/cryptonote_core/blockchain_storage.cpp +++ b/src/CryptoNoteCore/Blockchain.cpp @@ -15,28 +15,21 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "blockchain_storage.h" +#include "Blockchain.h" #include #include - -#include -#include #include -#include -#include - -#include "Common/boost_serialization_helper.h" #include "Common/Math.h" #include "Common/ShuffleGenerator.h" -#include "Common/StringTools.h" - -#include "cryptonote_format_utils.h" -#include "rpc/core_rpc_server_commands_defs.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Serialization/BinarySerializationTools.h" +#include "CryptoNoteTools.h" using namespace Logging; - -#undef ERROR +using namespace Common; namespace { @@ -53,57 +46,137 @@ std::string appendPath(const std::string& path, const std::string& fileName) { } namespace std { -bool operator<(const crypto::hash& hash1, const crypto::hash& hash2) { - return memcmp(&hash1, &hash2, crypto::HASH_SIZE) < 0; +bool operator<(const Crypto::Hash& hash1, const Crypto::Hash& hash2) { + return memcmp(&hash1, &hash2, Crypto::HASH_SIZE) < 0; } -bool operator<(const crypto::key_image& keyImage1, const crypto::key_image& keyImage2) { +bool operator<(const Crypto::KeyImage& keyImage1, const Crypto::KeyImage& keyImage2) { return memcmp(&keyImage1, &keyImage2, 32) < 0; } } #define CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER 1 +#define CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER 1 namespace CryptoNote { class BlockCacheSerializer; +class BlockchainIndicesSerializer; +} + +namespace CryptoNote { + +template +bool serialize(google::sparse_hash_map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [&value](size_t size) { value.resize(size); }); +} + +template +bool serialize(google::sparse_hash_set& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + size_t size = value.size(); + if (!serializer.beginArray(size, name)) { + return false; + } + + if (serializer.type() == ISerializer::OUTPUT) { + for (auto& key : value) { + serializer(const_cast(key), ""); + } + } else { + value.resize(size); + while (size--) { + K key; + serializer(key, ""); + value.insert(key); + } + } + + serializer.endArray(); + return true; } -BOOST_CLASS_VERSION(CryptoNote::BlockCacheSerializer, CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER); +// custom serialization to speedup cache loading +bool serialize(std::vector>& value, Common::StringView name, CryptoNote::ISerializer& s) { + const size_t elementSize = sizeof(std::pair); + size_t size = value.size() * elementSize; + + if (!s.beginArray(size, name)) { + return false; + } + + if (s.type() == CryptoNote::ISerializer::INPUT) { + if (size % elementSize != 0) { + throw std::runtime_error("Invalid vector size"); + } + value.resize(size / elementSize); + } -namespace CryptoNote -{ + if (size) { + s.binary(value.data(), size, ""); + } -template -void blockchain_storage::TransactionIndex::serialize(Archive& archive, unsigned int version) { - archive & block; - archive & transaction; + s.endArray(); + return true; } -template -void blockchain_storage::MultisignatureOutputUsage::serialize(Archive& archive, unsigned int version) { - archive & transactionIndex; - archive & outputIndex; - archive & isUsed; +void serialize(Blockchain::TransactionIndex& value, ISerializer& s) { + s(value.block, "block"); + s(value.transaction, "tx"); } class BlockCacheSerializer { public: - BlockCacheSerializer(blockchain_storage& bs, const crypto::hash lastBlockHash, ILogger& logger) : + BlockCacheSerializer(Blockchain& bs, const Crypto::Hash lastBlockHash, ILogger& logger) : m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockCacheSerializer") { } - template void serialize(Archive& ar, unsigned int version) { + void load(const std::string& filename) { + try { + std::ifstream stdStream(filename, std::ios::binary); + if (!stdStream) { + return; + } + + StdInputStream stream(stdStream); + BinaryInputStreamSerializer s(stream); + CryptoNote::serialize(*this, s); + } catch (std::exception& e) { + logger(WARNING) << "loading failed: " << e.what(); + } + } + + bool save(const std::string& filename) { + try { + std::ofstream file(filename, std::ios::binary); + if (!file) { + return false; + } + + StdOutputStream stream(file); + BinaryOutputStreamSerializer s(stream); + CryptoNote::serialize(*this, s); + } catch (std::exception&) { + return false; + } + + return true; + } + + void serialize(ISerializer& s) { + auto start = std::chrono::steady_clock::now(); + + uint8_t version = CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER; + s(version, "version"); // ignore old versions, do rebuild if (version < CURRENT_BLOCKCACHE_STORAGE_ARCHIVE_VER) return; std::string operation; - if (Archive::is_loading::value) { + if (s.type() == ISerializer::INPUT) { operation = "- loading "; - crypto::hash blockHash; - ar & blockHash; + Crypto::Hash blockHash; + s(blockHash, "last_block"); if (blockHash != m_lastBlockHash) { return; @@ -111,23 +184,118 @@ class BlockCacheSerializer { } else { operation = "- saving "; - ar & m_lastBlockHash; + s(m_lastBlockHash, "last_block"); } logger(INFO) << operation << "block index..."; - ar & m_bs.m_blockIndex; + s(m_bs.m_blockIndex, "block_index"); logger(INFO) << operation << "transaction map..."; - ar & m_bs.m_transactionMap; + s(m_bs.m_transactionMap, "transactions"); - logger(INFO) << operation << "spend keys..."; - ar & m_bs.m_spent_keys; + logger(INFO) << operation << "spent keys..."; + s(m_bs.m_spent_keys, "spent_keys"); logger(INFO) << operation << "outputs..."; - ar & m_bs.m_outputs; + s(m_bs.m_outputs, "outputs"); logger(INFO) << operation << "multi-signature outputs..."; - ar & m_bs.m_multisignatureOutputs; + s(m_bs.m_multisignatureOutputs, "multisig_outputs"); + + auto dur = std::chrono::steady_clock::now() - start; + + logger(INFO) << "Serialization time: " << std::chrono::duration_cast(dur).count() << "ms"; + + m_loaded = true; + } + + bool loaded() const { + return m_loaded; + } + +private: + + LoggerRef logger; + bool m_loaded; + Blockchain& m_bs; + Crypto::Hash m_lastBlockHash; +}; + +class BlockchainIndicesSerializer { + +public: + BlockchainIndicesSerializer(Blockchain& bs, const Crypto::Hash lastBlockHash, ILogger& logger) : + m_bs(bs), m_lastBlockHash(lastBlockHash), m_loaded(false), logger(logger, "BlockchainIndicesSerializer") { + } + + void serialize(ISerializer& s) { + + uint8_t version = CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER; + + KV_MEMBER(version); + + // ignore old versions, do rebuild + if (version != CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER) + return; + + std::string operation; + + if (s.type() == ISerializer::INPUT) { + operation = "- loading "; + + Crypto::Hash blockHash; + s(blockHash, "blockHash"); + + if (blockHash != m_lastBlockHash) { + return; + } + + } else { + operation = "- saving "; + s(m_lastBlockHash, "blockHash"); + } + + logger(INFO) << operation << "paymentID index..."; + s(m_bs.m_paymentIdIndex, "paymentIdIndex"); + + logger(INFO) << operation << "timestamp index..."; + s(m_bs.m_timestampIndex, "timestampIndex"); + + logger(INFO) << operation << "generated transactions index..."; + s(m_bs.m_generatedTransactionsIndex, "generatedTransactionsIndex"); + + m_loaded = true; + } + + template void serialize(Archive& ar, unsigned int version) { + + // ignore old versions, do rebuild + if (version < CURRENT_BLOCKCHAININDICES_STORAGE_ARCHIVE_VER) + return; + + std::string operation; + if (Archive::is_loading::value) { + operation = "- loading "; + Crypto::Hash blockHash; + ar & blockHash; + + if (blockHash != m_lastBlockHash) { + return; + } + + } else { + operation = "- saving "; + ar & m_lastBlockHash; + } + + logger(INFO) << operation << "paymentID index..."; + ar & m_bs.m_paymentIdIndex; + + logger(INFO) << operation << "timestamp index..."; + ar & m_bs.m_timestampIndex; + + logger(INFO) << operation << "generated transactions index..."; + ar & m_bs.m_generatedTransactionsIndex; m_loaded = true; } @@ -140,13 +308,13 @@ class BlockCacheSerializer { LoggerRef logger; bool m_loaded; - blockchain_storage& m_bs; - crypto::hash m_lastBlockHash; + Blockchain& m_bs; + Crypto::Hash m_lastBlockHash; }; -blockchain_storage::blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) : -logger(logger, "blockchain_storage"), +Blockchain::Blockchain(const Currency& currency, tx_memory_pool& tx_pool, ILogger& logger) : +logger(logger, "Blockchain"), m_currency(currency), m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), @@ -156,23 +324,23 @@ m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger), m_checkpoints(logger) { m_outputs.set_deleted_key(0); - crypto::key_image nullImage = boost::value_initialized(); + Crypto::KeyImage nullImage = boost::value_initialized(); m_spent_keys.set_deleted_key(nullImage); } -bool blockchain_storage::addObserver(IBlockchainStorageObserver* observer) { +bool Blockchain::addObserver(IBlockchainStorageObserver* observer) { return m_observerManager.add(observer); } -bool blockchain_storage::removeObserver(IBlockchainStorageObserver* observer) { +bool Blockchain::removeObserver(IBlockchainStorageObserver* observer) { return m_observerManager.remove(observer); } -bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) { - return check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id); +bool Blockchain::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) { + return checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id); } -bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { +bool Blockchain::checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) { BlockInfo tail; @@ -180,27 +348,27 @@ bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& t //check is ring_signature already checked ? if (maxUsedBlock.empty()) { //not checked, lets try to check - if (!lastFailed.empty() && get_current_blockchain_height() > lastFailed.height && get_block_id_by_height(lastFailed.height) == lastFailed.id) { + if (!lastFailed.empty() && getCurrentBlockchainHeight() > lastFailed.height && getBlockIdByHeight(lastFailed.height) == lastFailed.id) { return false; //we already sure that this tx is broken for this height } - if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { + if (!checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { lastFailed = tail; return false; } } else { - if (maxUsedBlock.height >= get_current_blockchain_height()) { + if (maxUsedBlock.height >= getCurrentBlockchainHeight()) { return false; } - if (get_block_id_by_height(maxUsedBlock.height) != maxUsedBlock.id) { + if (getBlockIdByHeight(maxUsedBlock.height) != maxUsedBlock.id) { //if we already failed on this height and id, skip actual ring signature check - if (lastFailed.id == get_block_id_by_height(lastFailed.height)) { + if (lastFailed.id == getBlockIdByHeight(lastFailed.height)) { return false; } //check ring signature again, it is possible (with very small chance) that this transaction become again valid - if (!check_tx_inputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { + if (!checkTransactionInputs(tx, maxUsedBlock.height, maxUsedBlock.id, &tail)) { lastFailed = tail; return false; } @@ -210,28 +378,41 @@ bool blockchain_storage::checkTransactionInputs(const CryptoNote::Transaction& t return true; } -bool blockchain_storage::haveSpentKeyImages(const CryptoNote::Transaction& tx) { - return this->have_tx_keyimges_as_spent(tx); +bool Blockchain::haveSpentKeyImages(const CryptoNote::Transaction& tx) { + return this->haveTransactionKeyImagesAsSpent(tx); } -bool blockchain_storage::have_tx(const crypto::hash &id) { +/** +* \pre m_blockchain_lock is locked +*/ +bool Blockchain::checkTransactionSize(size_t blobSize) { + if (blobSize > getCurrentCumulativeBlocksizeLimit() - m_currency.minerTxBlobReservedSize()) { + logger(ERROR) << "transaction is too big " << blobSize << ", maximum allowed size is " << + (getCurrentCumulativeBlocksizeLimit() - m_currency.minerTxBlobReservedSize()); + return false; + } + + return true; +} + +bool Blockchain::haveTransaction(const Crypto::Hash &id) { std::lock_guard lk(m_blockchain_lock); return m_transactionMap.find(id) != m_transactionMap.end(); } -bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) { +bool Blockchain::have_tx_keyimg_as_spent(const Crypto::KeyImage &key_im) { std::lock_guard lk(m_blockchain_lock); return m_spent_keys.find(key_im) != m_spent_keys.end(); } -uint64_t blockchain_storage::get_current_blockchain_height() { +uint32_t Blockchain::getCurrentBlockchainHeight() { std::lock_guard lk(m_blockchain_lock); - return m_blocks.size(); + return static_cast(m_blocks.size()); } -bool blockchain_storage::init(const std::string& config_folder, bool load_existing) { +bool Blockchain::init(const std::string& config_folder, bool load_existing) { std::lock_guard lk(m_blockchain_lock); - if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder)) { + if (!config_folder.empty() && !Tools::create_directories_if_necessary(config_folder)) { logger(ERROR, BRIGHT_RED) << "Failed to create data directory: " << m_config_folder; return false; } @@ -245,55 +426,14 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi if (load_existing && !m_blocks.empty()) { logger(INFO, BRIGHT_WHITE) << "Loading blockchain..."; BlockCacheSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger()); - tools::unserialize_obj_from_file(loader, appendPath(config_folder, m_currency.blocksCacheFileName())); + loader.load(appendPath(config_folder, m_currency.blocksCacheFileName())); if (!loader.loaded()) { logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain cache found, rebuilding internal structures..."; - std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); - m_blockIndex.clear(); - m_transactionMap.clear(); - m_spent_keys.clear(); - m_outputs.clear(); - m_multisignatureOutputs.clear(); - for (uint32_t b = 0; b < m_blocks.size(); ++b) { - if (b % 1000 == 0) { - logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); - } - const BlockEntry& block = m_blocks[b]; - crypto::hash blockHash = get_block_hash(block.bl); - m_blockIndex.push(blockHash); - for (uint16_t t = 0; t < block.transactions.size(); ++t) { - const TransactionEntry& transaction = block.transactions[t]; - crypto::hash transactionHash = get_transaction_hash(transaction.tx); - TransactionIndex transactionIndex = {b, t}; - m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); - - // process inputs - for (auto& i : transaction.tx.vin) { - if (i.type() == typeid(TransactionInputToKey)) { - m_spent_keys.insert(::boost::get(i).keyImage); - } else if (i.type() == typeid(TransactionInputMultisignature)) { - auto out = ::boost::get(i); - m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; - } - } - - // process outputs - for (uint16_t o = 0; o < transaction.tx.vout.size(); ++o) { - const auto& out = transaction.tx.vout[o]; - if (out.target.type() == typeid(TransactionOutputToKey)) { - m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); - } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { - MultisignatureOutputUsage usage = {transactionIndex, o, false}; - m_multisignatureOutputs[out.amount].push_back(usage); - } - } - } - } - - std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; - logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count(); + rebuildCache(); } + + loadBlockchainIndices(); } else { m_blocks.clear(); } @@ -301,15 +441,14 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi if (m_blocks.empty()) { logger(INFO, BRIGHT_WHITE) << "Blockchain not loaded, generating genesis block."; - block_verification_context bvc = - boost::value_initialized(); - add_new_block(m_currency.genesisBlock(), bvc); + block_verification_context bvc = boost::value_initialized(); + pushBlock(m_currency.genesisBlock(), bvc); if (bvc.m_verifivation_failed) { logger(ERROR, BRIGHT_RED) << "Failed to add genesis block to blockchain"; return false; } } else { - crypto::hash firstBlockHash = get_block_hash(m_blocks[0].bl); + Crypto::Hash firstBlockHash = get_block_hash(m_blocks[0].bl); if (!(firstBlockHash == m_currency.genesisBlockHash())) { logger(ERROR, BRIGHT_RED) << "Failed to init: genesis block mismatch. " "Probably you set --testnet flag with data " @@ -334,16 +473,63 @@ bool blockchain_storage::init(const std::string& config_folder, bool load_existi logger(INFO, BRIGHT_GREEN) << "Blockchain initialized. last block: " << m_blocks.size() - 1 << ", " << Common::timeIntervalToString(timestamp_diff) - << " time ago, current difficulty: " << get_difficulty_for_next_block(); + << " time ago, current difficulty: " << getDifficultyForNextBlock(); return true; } -bool blockchain_storage::storeCache() { +void Blockchain::rebuildCache() { + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + m_blockIndex.clear(); + m_transactionMap.clear(); + m_spent_keys.clear(); + m_outputs.clear(); + m_multisignatureOutputs.clear(); + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); + } + const BlockEntry& block = m_blocks[b]; + Crypto::Hash blockHash = get_block_hash(block.bl); + m_blockIndex.push(blockHash); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const TransactionEntry& transaction = block.transactions[t]; + Crypto::Hash transactionHash = getObjectHash(transaction.tx); + TransactionIndex transactionIndex = { b, t }; + m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); + + // process inputs + for (auto& i : transaction.tx.inputs) { + if (i.type() == typeid(KeyInput)) { + m_spent_keys.insert(::boost::get(i).keyImage); + } else if (i.type() == typeid(MultisignatureInput)) { + auto out = ::boost::get(i); + m_multisignatureOutputs[out.amount][out.outputIndex].isUsed = true; + } + } + + // process outputs + for (uint16_t o = 0; o < transaction.tx.outputs.size(); ++o) { + const auto& out = transaction.tx.outputs[o]; + if (out.target.type() == typeid(KeyOutput)) { + m_outputs[out.amount].push_back(std::make_pair<>(transactionIndex, o)); + } else if (out.target.type() == typeid(MultisignatureOutput)) { + MultisignatureOutputUsage usage = { transactionIndex, o, false }; + m_multisignatureOutputs[out.amount].push_back(usage); + } + } + } + } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + logger(INFO, BRIGHT_WHITE) << "Rebuilding internal structures took: " << duration.count(); +} + +bool Blockchain::storeCache() { std::lock_guard lk(m_blockchain_lock); logger(INFO, BRIGHT_WHITE) << "Saving blockchain..."; - BlockCacheSerializer ser(*this, get_tail_id(), logger.getLogger()); - if (!tools::serialize_obj_to_file(ser, appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { + BlockCacheSerializer ser(*this, getTailId(), logger.getLogger()); + if (!ser.save(appendPath(m_config_folder, m_currency.blocksCacheFileName()))) { logger(ERROR, BRIGHT_RED) << "Failed to save blockchain cache"; return false; } @@ -351,12 +537,14 @@ bool blockchain_storage::storeCache() { return true; } -bool blockchain_storage::deinit() { +bool Blockchain::deinit() { storeCache(); + storeBlockchainIndices(); + assert(m_messageQueueList.empty()); return true; } -bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { +bool Blockchain::resetAndSetGenesisBlock(const Block& b) { std::lock_guard lk(m_blockchain_lock); m_blocks.clear(); m_blockIndex.clear(); @@ -366,69 +554,89 @@ bool blockchain_storage::reset_and_set_genesis_block(const Block& b) { m_alternative_chains.clear(); m_outputs.clear(); + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + m_generatedTransactionsIndex.clear(); + m_orthanBlocksIndex.clear(); + block_verification_context bvc = boost::value_initialized(); - add_new_block(b, bvc); + addNewBlock(b, bvc); return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; } -crypto::hash blockchain_storage::get_tail_id(uint64_t& height) { +Crypto::Hash Blockchain::getTailId(uint32_t& height) { + assert(!m_blocks.empty()); std::lock_guard lk(m_blockchain_lock); - height = get_current_blockchain_height() - 1; - return get_tail_id(); + height = getCurrentBlockchainHeight() - 1; + return getTailId(); } -crypto::hash blockchain_storage::get_tail_id() { +Crypto::Hash Blockchain::getTailId() { std::lock_guard lk(m_blockchain_lock); - return m_blockIndex.getTailId(); + return m_blocks.empty() ? NULL_HASH : m_blockIndex.getTailId(); } -bool blockchain_storage::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) { - // Locks are necessary in order to make sure blockchain isn't changed between get_tail_id() and getPoolChanges() - std::lock_guard txPoolLock(m_tx_pool); - std::lock_guard blockchainLock(m_blockchain_lock); +std::vector Blockchain::buildSparseChain() { + std::lock_guard lk(m_blockchain_lock); + assert(m_blockIndex.size() != 0); + return doBuildSparseChain(m_blockIndex.getTailId()); +} - if (tailBlockId != get_tail_id()) { - return false; - } +std::vector Blockchain::buildSparseChain(const Crypto::Hash& startBlockId) { + std::lock_guard lk(m_blockchain_lock); + assert(haveBlock(startBlockId)); + return doBuildSparseChain(startBlockId); +} - getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); +std::vector Blockchain::doBuildSparseChain(const Crypto::Hash& startBlockId) const { + assert(m_blockIndex.size() != 0); - return true; -} + std::vector sparseChain; -void blockchain_storage::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) { - std::lock_guard txPoolLock(m_tx_pool); + if (m_blockIndex.hasBlock(startBlockId)) { + sparseChain = m_blockIndex.buildSparseChain(startBlockId); + } else { + assert(m_alternative_chains.count(startBlockId) > 0); - std::vector addedTxsIds; - m_tx_pool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds); + std::vector alternativeChain; + Crypto::Hash blockchainAncestor; + for (auto it = m_alternative_chains.find(startBlockId); it != m_alternative_chains.end(); it = m_alternative_chains.find(blockchainAncestor)) { + alternativeChain.emplace_back(it->first); + blockchainAncestor = it->second.bl.previousBlockHash; + } - std::vector misses; - m_tx_pool.getTransactions(addedTxsIds, addedTxs, misses); - assert(misses.empty()); -} + for (size_t i = 1; i <= alternativeChain.size(); i *= 2) { + sparseChain.emplace_back(alternativeChain[i - 1]); + } -bool blockchain_storage::get_short_chain_history(std::list& ids) { - std::lock_guard lk(m_blockchain_lock); - return m_blockIndex.getShortChainHistory(ids); + assert(!sparseChain.empty()); + assert(m_blockIndex.hasBlock(blockchainAncestor)); + std::vector sparseMainChain = m_blockIndex.buildSparseChain(blockchainAncestor); + sparseChain.reserve(sparseChain.size() + sparseMainChain.size()); + std::copy(sparseMainChain.begin(), sparseMainChain.end(), std::back_inserter(sparseChain)); + } + + return sparseChain; } -crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) { +Crypto::Hash Blockchain::getBlockIdByHeight(uint32_t height) { std::lock_guard lk(m_blockchain_lock); + assert(height < m_blockIndex.size()); return m_blockIndex.getBlockId(height); } -bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& b) { +bool Blockchain::getBlockByHash(const Crypto::Hash& blockHash, Block& b) { std::lock_guard lk(m_blockchain_lock); - uint64_t height = 0; + uint32_t height = 0; if (m_blockIndex.getBlockHeight(blockHash, height)) { b = m_blocks[height].bl; return true; } + logger(WARNING) << blockHash; + auto blockByHashIterator = m_alternative_chains.find(blockHash); if (blockByHashIterator != m_alternative_chains.end()) { b = blockByHashIterator->second.bl; @@ -438,7 +646,12 @@ bool blockchain_storage::get_block_by_hash(const crypto::hash& blockHash, Block& return false; } -difficulty_type blockchain_storage::get_difficulty_for_next_block() { +bool Blockchain::getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) { + std::lock_guard lock(m_blockchain_lock); + return m_blockIndex.getBlockHeight(blockId, blockHeight); +} + +difficulty_type Blockchain::getDifficultyForNextBlock() { std::lock_guard lk(m_blockchain_lock); std::vector timestamps; std::vector commulative_difficulties; @@ -455,7 +668,7 @@ difficulty_type blockchain_storage::get_difficulty_for_next_block() { return m_currency.nextDifficulty(timestamps, commulative_difficulties); } -uint64_t blockchain_storage::getCoinsInCirculation() { +uint64_t Blockchain::getCoinsInCirculation() { std::lock_guard lk(m_blockchain_lock); if (m_blocks.empty()) { return 0; @@ -464,11 +677,11 @@ uint64_t blockchain_storage::getCoinsInCirculation() { } } -uint8_t blockchain_storage::get_block_major_version_for_height(uint64_t height) const { +uint8_t Blockchain::getBlockMajorVersionForHeight(uint32_t height) const { return height > m_upgradeDetector.upgradeHeight() ? m_upgradeDetector.targetVersion() : BLOCK_MAJOR_VERSION_1; } -bool blockchain_storage::rollback_blockchain_switching(std::list &original_chain, size_t rollback_height) { +bool Blockchain::rollback_blockchain_switching(std::list &original_chain, size_t rollback_height) { std::lock_guard lk(m_blockchain_lock); // remove failed subchain for (size_t i = m_blocks.size() - 1; i >= rollback_height; i--) { @@ -476,7 +689,7 @@ bool blockchain_storage::rollback_blockchain_switching(std::list &origina } // return back original chain - for(auto &bl : original_chain) { + for (auto &bl : original_chain) { block_verification_context bvc = boost::value_initialized(); bool r = pushBlock(bl, bvc); @@ -491,7 +704,7 @@ bool blockchain_storage::rollback_blockchain_switching(std::list &origina return true; } -bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { +bool Blockchain::switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain) { std::lock_guard lk(m_blockchain_lock); if (!(alt_chain.size())) { @@ -525,11 +738,13 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::listsecond, get_block_hash(ch_ent->second.bl)); logger(INFO, BRIGHT_WHITE) << "The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl); + m_orthanBlocksIndex.remove(ch_ent->second.bl); m_alternative_chains.erase(ch_ent); for (auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) { //block_verification_context bvc = boost::value_initialized(); //add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); + m_orthanBlocksIndex.remove((*alt_ch_to_orph_iter)->second.bl); m_alternative_chains.erase(*alt_ch_to_orph_iter); } @@ -541,7 +756,7 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list(); - bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); + bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc, false); if (!r) { logger(ERROR, BRIGHT_RED) << ("Failed to push ex-main chain blocks to alternative chain "); rollback_blockchain_switching(disconnected_chain, split_height); @@ -550,16 +765,24 @@ bool blockchain_storage::switch_to_alternative_blockchain(std::list blocksFromCommonRoot; + blocksFromCommonRoot.reserve(alt_chain.size() + 1); + blocksFromCommonRoot.push_back(alt_chain.front()->second.bl.previousBlockHash); + //removing all_chain entries from alternative chain for (auto ch_ent : alt_chain) { + blocksFromCommonRoot.push_back(get_block_hash(ch_ent->second.bl)); + m_orthanBlocksIndex.remove(ch_ent->second.bl); m_alternative_chains.erase(ch_ent); } + sendMessage(BlockchainMessage(std::move(ChainSwitchMessage(std::move(blocksFromCommonRoot))))); + logger(INFO, BRIGHT_GREEN) << "REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(); return true; } -difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei) { +difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei) { std::vector timestamps; std::vector commulative_difficulties; if (alt_chain.size() < m_currency.difficultyBlocksCount()) { @@ -602,35 +825,35 @@ difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(co return m_currency.nextDifficulty(timestamps, commulative_difficulties); } -bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t height) { +bool Blockchain::prevalidate_miner_transaction(const Block& b, uint32_t height) { - if (!(b.minerTx.vin.size() == 1)) { + if (!(b.baseTransaction.inputs.size() == 1)) { logger(ERROR, BRIGHT_RED) << "coinbase transaction in the block has no inputs"; return false; } - if (!(b.minerTx.vin[0].type() == typeid(TransactionInputGenerate))) { + if (!(b.baseTransaction.inputs[0].type() == typeid(BaseInput))) { logger(ERROR, BRIGHT_RED) << "coinbase transaction in the block has the wrong type"; return false; } - if (boost::get(b.minerTx.vin[0]).height != height) { + if (boost::get(b.baseTransaction.inputs[0]).blockIndex != height) { logger(INFO, BRIGHT_RED) << "The miner transaction in block has invalid height: " << - boost::get(b.minerTx.vin[0]).height << ", expected: " << height; + boost::get(b.baseTransaction.inputs[0]).blockIndex << ", expected: " << height; return false; } - if (!(b.minerTx.unlockTime == height + m_currency.minedMoneyUnlockWindow())) { + if (!(b.baseTransaction.unlockTime == height + m_currency.minedMoneyUnlockWindow())) { logger(ERROR, BRIGHT_RED) << "coinbase transaction transaction have wrong unlock time=" - << b.minerTx.unlockTime << ", expected " + << b.baseTransaction.unlockTime << ", expected " << height + m_currency.minedMoneyUnlockWindow(); return false; } - if (!check_outs_overflow(b.minerTx)) { + if (!check_outs_overflow(b.baseTransaction)) { logger(INFO, BRIGHT_RED) << "miner transaction have money overflow in block " << get_block_hash(b); return false; } @@ -638,11 +861,11 @@ bool blockchain_storage::prevalidate_miner_transaction(const Block& b, uint64_t return true; } -bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, +bool Blockchain::validate_miner_transaction(const Block& b, uint32_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange) { uint64_t minerReward = 0; - for (auto& o : b.minerTx.vout) { + for (auto& o : b.baseTransaction.outputs) { minerReward += o.amount; } @@ -650,7 +873,7 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei get_last_n_blocks_sizes(lastBlocksSizes, m_currency.rewardBlocksWindow()); size_t blocksSizeMedian = Common::medianValue(lastBlocksSizes); - bool penalizeFee = get_block_major_version_for_height(height) > BLOCK_MAJOR_VERSION_1; + bool penalizeFee = getBlockMajorVersionForHeight(height) > BLOCK_MAJOR_VERSION_1; if (!m_currency.getBlockReward(blocksSizeMedian, cumulativeBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange)) { logger(INFO, BRIGHT_WHITE) << "block size " << cumulativeBlockSize << " is bigger than allowed for this blockchain"; return false; @@ -669,7 +892,7 @@ bool blockchain_storage::validate_miner_transaction(const Block& b, uint64_t hei return true; } -bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) { +bool Blockchain::getBackwardBlocksSize(size_t from_height, std::vector& sz, size_t count) { std::lock_guard lk(m_blockchain_lock); if (!(from_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) @@ -685,170 +908,20 @@ bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vect return true; } -bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) { +bool Blockchain::get_last_n_blocks_sizes(std::vector& sz, size_t count) { std::lock_guard lk(m_blockchain_lock); if (!m_blocks.size()) { return true; } - return get_backward_blocks_sizes(m_blocks.size() - 1, sz, count); + return getBackwardBlocksSize(m_blocks.size() - 1, sz, count); } -uint64_t blockchain_storage::get_current_comulative_blocksize_limit() { +uint64_t Blockchain::getCurrentCumulativeBlocksizeLimit() { return m_current_block_cumul_sz_limit; } -bool blockchain_storage::create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) { - size_t median_size; - uint64_t already_generated_coins; - - { - std::lock_guard lk(m_blockchain_lock); - height = static_cast(m_blocks.size()); - diffic = get_difficulty_for_next_block(); - if (!(diffic)) { - logger(ERROR, BRIGHT_RED) << "difficulty overhead."; - return false; - } - - b = boost::value_initialized(); - b.majorVersion = get_block_major_version_for_height(height); - - if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_1; - } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { - b.minorVersion = BLOCK_MINOR_VERSION_0; - - b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; - b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; - b.parentBlock.numberOfTransactions = 1; - tx_extra_merge_mining_tag mm_tag = boost::value_initialized(); - - if (!append_mm_tag_to_extra(b.parentBlock.minerTx.extra, mm_tag)) { - logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction"; - return false; - } - } - - b.prevId = get_tail_id(); - b.timestamp = time(NULL); - - median_size = m_current_block_cumul_sz_limit / 2; - already_generated_coins = m_blocks.back().already_generated_coins; - } - - size_t txs_size; - uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, - txs_size, fee)) { - return false; - } - -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - size_t real_txs_size = 0; - uint64_t real_fee = 0; - CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); - for (crypto::hash &cur_hash : b.txHashes) { - auto cur_res = m_tx_pool.m_transactions.find(cur_hash); - if (cur_res == m_tx_pool.m_transactions.end()) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: transaction not found"; - continue; - } - tx_memory_pool::tx_details &cur_tx = cur_res->second; - real_txs_size += cur_tx.blob_size; - real_fee += cur_tx.fee; - if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid transaction size"; - } - uint64_t inputs_amount; - if (!get_inputs_money_amount(cur_tx.tx, inputs_amount)) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: cannot get inputs amount"; - } else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx)) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: invalid fee"; - } - } - if (txs_size != real_txs_size) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated transaction size"; - } - if (fee != real_fee) { - logger(ERROR, BRIGHT_RED) << "Creating block template: error: wrongly calculated fee"; - } - CRITICAL_REGION_END(); - logger(DEBUGGING) << "Creating block template: height " << height << - ", median size " << median_size << - ", already generated coins " << already_generated_coins << - ", transaction size " << txs_size << - ", 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 - */ - //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size - bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; - bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); - if (!r) { - logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, first chance"; - return false; - } - - size_t cumulative_size = txs_size + get_object_blobsize(b.minerTx); -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << get_object_blobsize(b.minerTx) << - ", cumulative size " << cumulative_size; -#endif - for (size_t try_count = 0; try_count != 10; ++try_count) { - r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.minerTx, ex_nonce, 11, penalizeFee); - - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construc miner tx, second chance"; return false; } - size_t coinbase_blob_size = get_object_blobsize(b.minerTx); - if (coinbase_blob_size > cumulative_size - txs_size) { - cumulative_size = txs_size + coinbase_blob_size; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is greater then before"; -#endif - continue; - } - - if (coinbase_blob_size < cumulative_size - txs_size) { - size_t delta = cumulative_size - txs_size - coinbase_blob_size; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << txs_size + coinbase_blob_size << - " is less then before, adding " << delta << " zero bytes"; -#endif - b.minerTx.extra.insert(b.minerTx.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.minerTx)) { - if (!(cumulative_size + 1 == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; } - b.minerTx.extra.resize(b.minerTx.extra.size() - 1); - if (cumulative_size != txs_size + get_object_blobsize(b.minerTx)) { - //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size - logger(TRACE, BRIGHT_RED) << - "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; - cumulative_size += delta - 1; - continue; - } - logger(DEBUGGING, BRIGHT_GREEN) << - "Setting extra for block: " << b.minerTx.extra.size() << ", try_count=" << try_count; - } - } - if (!(cumulative_size == txs_size + get_object_blobsize(b.minerTx))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.minerTx)=" << get_object_blobsize(b.minerTx); return false; } -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: miner tx size " << coinbase_blob_size << - ", cumulative size " << cumulative_size << " is now good"; -#endif - return true; - } - - logger(ERROR, BRIGHT_RED) << - "Failed to create_block_template with " << 10 << " tries"; - return false; -} - -bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { +bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { if (timestamps.size() >= m_currency.timestampCheckWindow()) return true; @@ -865,10 +938,10 @@ bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, s return true; } -bool blockchain_storage::handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc) { +bool Blockchain::handle_alternative_block(const Block& b, const Crypto::Hash& id, block_verification_context& bvc, bool sendNewAlternativeBlockMessage) { std::lock_guard lk(m_blockchain_lock); - uint64_t block_height = get_block_height(b); + auto block_height = get_block_height(b); if (block_height == 0) { logger(ERROR, BRIGHT_RED) << "Block with id: " << Common::podToHex(id) << " (as alternative) have wrong miner transaction"; @@ -876,10 +949,10 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: return false; } - if (!m_checkpoints.is_alternative_block_allowed(get_current_blockchain_height(), block_height)) { + if (!m_checkpoints.is_alternative_block_allowed(getCurrentBlockchainHeight(), block_height)) { logger(TRACE) << "Block with id: " << id << std::endl << " can't be accepted for alternative chain, block height: " << block_height << std::endl << - " blockchain height: " << get_current_blockchain_height(); + " blockchain height: " << getCurrentBlockchainHeight(); bvc.m_verifivation_failed = true; return false; } @@ -906,9 +979,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: //block is not related with head of main chain //first of all - look in alternative chains container - uint64_t mainPrevHeight = 0; - const bool mainPrev = m_blockIndex.getBlockHeight(b.prevId, mainPrevHeight); - const auto it_prev = m_alternative_chains.find(b.prevId); + uint32_t mainPrevHeight = 0; + const bool mainPrev = m_blockIndex.getBlockHeight(b.previousBlockHash, mainPrevHeight); + const auto it_prev = m_alternative_chains.find(b.previousBlockHash); if (it_prev != m_alternative_chains.end() || mainPrev) { //we have new block in alternative chain @@ -920,15 +993,15 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: while (alt_it != m_alternative_chains.end()) { alt_chain.push_front(alt_it); timestamps.push_back(alt_it->second.bl.timestamp); - alt_it = m_alternative_chains.find(alt_it->second.bl.prevId); + alt_it = m_alternative_chains.find(alt_it->second.bl.previousBlockHash); } if (alt_chain.size()) { //make sure that it has right connection to main chain if (!(m_blocks.size() > alt_chain.front()->second.height)) { logger(ERROR, BRIGHT_RED) << "main blockchain wrong height"; return false; } - crypto::hash h = null_hash; + Crypto::Hash h = NULL_HASH; get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); - if (!(h == alt_chain.front()->second.bl.prevId)) { logger(ERROR, BRIGHT_RED) << "alternative chain have wrong connection to main chain"; return false; } + if (!(h == alt_chain.front()->second.bl.previousBlockHash)) { logger(ERROR, BRIGHT_RED) << "alternative chain have wrong connection to main chain"; return false; } complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); } else { if (!(mainPrev)) { logger(ERROR, BRIGHT_RED) << "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"; return false; } @@ -961,7 +1034,7 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: m_is_in_checkpoint_zone = false; difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); if (!(current_diff)) { logger(ERROR, BRIGHT_RED) << "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"; return false; } - crypto::hash proof_of_work = null_hash; + Crypto::Hash proof_of_work = NULL_HASH; if (!m_currency.checkProofOfWork(m_cn_context, bei.bl, current_diff, proof_of_work)) { logger(INFO, BRIGHT_RED) << "Block with id: " << id @@ -988,6 +1061,9 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); if (!(i_res.second)) { logger(ERROR, BRIGHT_RED) << "insertion of new alternative block returned as it already exist"; return false; } + + m_orthanBlocksIndex.add(bei.bl); + alt_chain.push_back(i_res.first); if (is_a_checkpoint) { @@ -1023,9 +1099,12 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: << ENDL << "id:\t" << id << ENDL << "PoW:\t" << proof_of_work << ENDL << "difficulty:\t" << current_diff; + if (sendNewAlternativeBlockMessage) { + sendMessage(BlockchainMessage(std::move(NewAlternativeBlockMessage(id)))); + } return true; } - } else { + } else { //block orphaned bvc.m_marked_as_orphaned = true; logger(INFO, BRIGHT_RED) << @@ -1033,68 +1112,68 @@ bool blockchain_storage::handle_alternative_block(const Block& b, const crypto:: } return true; - } +} -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { +bool Blockchain::getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs) { std::lock_guard lk(m_blockchain_lock); if (start_offset >= m_blocks.size()) return false; for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); - std::list missed_ids; - get_transactions(m_blocks[i].bl.txHashes, txs, missed_ids); + std::list missed_ids; + getTransactions(m_blocks[i].bl.transactionHashes, txs, missed_ids); if (!(!missed_ids.size())) { logger(ERROR, BRIGHT_RED) << "have missed transactions in own block in main blockchain"; return false; } } return true; } -bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) { +bool Blockchain::getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks) { std::lock_guard lk(m_blockchain_lock); if (start_offset >= m_blocks.size()) { return false; } - for (size_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { + for (uint32_t i = start_offset; i < start_offset + count && i < m_blocks.size(); i++) { blocks.push_back(m_blocks[i].bl); } return true; } -bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { +bool Blockchain::handleGetObjects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. std::lock_guard lk(m_blockchain_lock); - rsp.current_blockchain_height = get_current_blockchain_height(); + rsp.current_blockchain_height = getCurrentBlockchainHeight(); std::list blocks; - get_blocks(arg.blocks, blocks, rsp.missed_ids); + getBlocks(arg.blocks, blocks, rsp.missed_ids); for (const auto& bl : blocks) { - std::list missed_tx_id; + std::list missed_tx_id; std::list txs; - get_transactions(bl.txHashes, txs, rsp.missed_ids); - if (!(!missed_tx_id.size())) { logger(ERROR, BRIGHT_RED) << "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl); return false; } + getTransactions(bl.transactionHashes, txs, rsp.missed_ids); + if (!(!missed_tx_id.size())) { logger(ERROR, BRIGHT_RED) << "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() << ENDL << "for block id = " << get_block_hash(bl); return false; } //WTF??? rsp.blocks.push_back(block_complete_entry()); block_complete_entry& e = rsp.blocks.back(); //pack block - e.block = t_serializable_object_to_blob(bl); + e.block = asString(toBinaryArray(bl)); //pack transactions for (Transaction& tx : txs) { - e.txs.push_back(t_serializable_object_to_blob(tx)); + e.txs.push_back(asString(toBinaryArray(tx))); } } //get another transactions, if need std::list txs; - get_transactions(arg.txs, txs, rsp.missed_ids); + getTransactions(arg.txs, txs, rsp.missed_ids); //pack aside transactions for (const auto& tx : txs) { - rsp.txs.push_back(t_serializable_object_to_blob(tx)); + rsp.txs.push_back(asString(toBinaryArray(tx))); } return true; } -bool blockchain_storage::get_alternative_blocks(std::list& blocks) { +bool Blockchain::getAlternativeBlocks(std::list& blocks) { std::lock_guard lk(m_blockchain_lock); for (auto& alt_bl : m_alternative_chains) { blocks.push_back(alt_bl.second.bl); @@ -1103,31 +1182,31 @@ bool blockchain_storage::get_alternative_blocks(std::list& blocks) { return true; } -size_t blockchain_storage::get_alternative_blocks_count() { +uint32_t Blockchain::getAlternativeBlocksCount() { std::lock_guard lk(m_blockchain_lock); - return m_alternative_chains.size(); + return static_cast(m_alternative_chains.size()); } -bool blockchain_storage::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { +bool Blockchain::add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) { std::lock_guard lk(m_blockchain_lock); const Transaction& tx = transactionByIndex(amount_outs[i].first).tx; - if (!(tx.vout.size() > amount_outs[i].second)) { + if (!(tx.outputs.size() > amount_outs[i].second)) { logger(ERROR, BRIGHT_RED) << "internal error: in global outs index, transaction out index=" - << amount_outs[i].second << " more than transaction outputs = " << tx.vout.size() << ", for tx id = " << get_transaction_hash(tx); return false; + << amount_outs[i].second << " more than transaction outputs = " << tx.outputs.size() << ", for tx id = " << getObjectHash(tx); return false; } - if (!(tx.vout[amount_outs[i].second].target.type() == typeid(TransactionOutputToKey))) { logger(ERROR, BRIGHT_RED) << "unknown tx out type"; return false; } + if (!(tx.outputs[amount_outs[i].second].target.type() == typeid(KeyOutput))) { logger(ERROR, BRIGHT_RED) << "unknown tx out type"; return false; } //check if transaction is unlocked if (!is_tx_spendtime_unlocked(tx.unlockTime)) return false; 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; - oen.out_key = boost::get(tx.vout[amount_outs[i].second].target).key; + oen.global_amount_index = static_cast(i); + oen.out_key = boost::get(tx.outputs[amount_outs[i].second].target).key; return true; } -size_t blockchain_storage::find_end_of_allowed_index(const std::vector>& amount_outs) { +size_t Blockchain::find_end_of_allowed_index(const std::vector>& amount_outs) { std::lock_guard lk(m_blockchain_lock); if (amount_outs.empty()) { return 0; @@ -1136,7 +1215,7 @@ size_t blockchain_storage::find_end_of_allowed_index(const std::vector lk(m_blockchain_lock); for (uint64_t amount : req.amounts) { @@ -1164,7 +1243,7 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO if (!(up_index_limit <= amount_outs.size())) { logger(ERROR, BRIGHT_RED) << "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size(); return false; } if (up_index_limit > 0) { - ShuffleGenerator> generator(up_index_limit); + ShuffleGenerator> generator(up_index_limit); for (uint64_t j = 0; j < up_index_limit && result_outs.outs.size() < req.outs_count; ++j) { add_out_to_get_random_outs(amount_outs, result_outs, amount, generator()); } @@ -1173,44 +1252,27 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO return true; } -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) { - std::lock_guard lk(m_blockchain_lock); +uint32_t Blockchain::findBlockchainSupplement(const std::vector& qblock_ids) { + assert(!qblock_ids.empty()); + assert(qblock_ids.back() == m_blockIndex.getBlockId(0)); - if (!qblock_ids.size() /*|| !req.m_total_height*/) { - logger(ERROR, BRIGHT_RED) << - "Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"; - return false; - } - //check genesis match - if (qblock_ids.back() != get_block_hash(m_blocks[0].bl)) { - logger(ERROR, BRIGHT_RED) << - "Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " - << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) - << "," << ENDL << " dropping connection"; - return false; - } - - /* Figure out what blocks we should request to get state_normal */ - if (m_blockIndex.findSupplement(qblock_ids, starter_offset)) { - return true; - } - - //this should NEVER happen, but, dose of paranoia in such cases is not too bad - logger(ERROR, BRIGHT_RED) << - "Internal error handling connection, can't find split point"; - return false; + std::lock_guard lk(m_blockchain_lock); + uint32_t blockIndex; + // assert above guarantees that method returns true + m_blockIndex.findSupplement(qblock_ids, blockIndex); + return blockIndex; } -uint64_t blockchain_storage::block_difficulty(size_t i) { +uint64_t Blockchain::blockDifficulty(size_t i) { std::lock_guard lk(m_blockchain_lock); - if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"; return false; } + if (!(i < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "wrong block index i = " << i << " at Blockchain::block_difficulty()"; return false; } if (i == 0) return m_blocks[i].cumulative_difficulty; return m_blocks[i].cumulative_difficulty - m_blocks[i - 1].cumulative_difficulty; } -void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) { +void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) { std::stringstream ss; std::lock_guard lk(m_blockchain_lock); if (start_index >= m_blocks.size()) { @@ -1222,7 +1284,7 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind for (size_t i = start_index; i != m_blocks.size() && i != end_index; i++) { ss << "height " << i << ", timestamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size << "\nid\t\t" << get_block_hash(m_blocks[i].bl) - << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.txHashes.size() << ENDL; + << "\ndifficulty\t\t" << blockDifficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.transactionHashes.size() << ENDL; } logger(DEBUGGING) << "Current blockchain:" << ENDL << ss.str(); @@ -1230,25 +1292,21 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind "Blockchain printed with log level 1"; } -void blockchain_storage::print_blockchain_index() { +void Blockchain::print_blockchain_index() { std::stringstream ss; std::lock_guard lk(m_blockchain_lock); - std::list blockIds; - m_blockIndex.getBlockIds(0, std::numeric_limits::max(), blockIds); - - logger(INFO, BRIGHT_WHITE) << - "Current blockchain index:" << ENDL; + std::vector blockIds = m_blockIndex.getBlockIds(0, std::numeric_limits::max()); + logger(INFO, BRIGHT_WHITE) << "Current blockchain index:"; size_t height = 0; for (auto i = blockIds.begin(); i != blockIds.end(); ++i, ++height) { - logger(INFO, BRIGHT_WHITE) << - "id\t\t" << *i << " height" << height; + logger(INFO, BRIGHT_WHITE) << "id\t\t" << *i << " height" << height; } } -void blockchain_storage::print_blockchain_outs(const std::string& file) { +void Blockchain::print_blockchain_outs(const std::string& file) { std::stringstream ss; std::lock_guard lk(m_blockchain_lock); for (const outputs_container::value_type& v : m_outputs) { @@ -1256,7 +1314,7 @@ void blockchain_storage::print_blockchain_outs(const std::string& file) { if (!vals.empty()) { ss << "amount: " << v.first << ENDL; for (size_t i = 0; i != vals.size(); i++) { - ss << "\t" << get_transaction_hash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL; + ss << "\t" << getObjectHash(transactionByIndex(vals[i].first).tx) << ": " << vals[i].second << ENDL; } } } @@ -1270,37 +1328,20 @@ void blockchain_storage::print_blockchain_outs(const std::string& file) { } } -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { - std::lock_guard lk(m_blockchain_lock); - if (!find_blockchain_supplement(qblock_ids, resp.start_height)) - return false; - - resp.total_height = get_current_blockchain_height(); - size_t count = 0; +std::vector Blockchain::findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) { - return m_blockIndex.getBlockIds(resp.start_height, BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT, resp.m_block_ids); -} + assert(!remoteBlockIds.empty()); + assert(remoteBlockIds.back() == m_blockIndex.getBlockId(0)); -bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { std::lock_guard lk(m_blockchain_lock); - if (!find_blockchain_supplement(qblock_ids, start_height)) { - return false; - } + totalBlockCount = getCurrentBlockchainHeight(); + startBlockIndex = findBlockchainSupplement(remoteBlockIds); - total_height = get_current_blockchain_height(); - size_t count = 0; - for (size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) { - blocks.resize(blocks.size() + 1); - blocks.back().first = m_blocks[i].bl; - std::list mis; - get_transactions(m_blocks[i].bl.txHashes, blocks.back().second, mis); - if (!(!mis.size())) { logger(ERROR, BRIGHT_RED) << "internal error, transaction from block not found"; return false; } - } - - return true; + return m_blockIndex.getBlockIds(startBlockIndex, static_cast(maxCount)); } -bool blockchain_storage::have_block(const crypto::hash& id) { +bool Blockchain::haveBlock(const Crypto::Hash& id) { std::lock_guard lk(m_blockchain_lock); if (m_blockIndex.hasBlock(id)) return true; @@ -1311,12 +1352,12 @@ bool blockchain_storage::have_block(const crypto::hash& id) { return false; } -size_t blockchain_storage::get_total_transactions() { +size_t Blockchain::getTotalTransactions() { std::lock_guard lk(m_blockchain_lock); return m_transactionMap.size(); } -bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { +bool Blockchain::getTransactionOutputGlobalIndexes(const Crypto::Hash& tx_id, std::vector& indexs) { std::lock_guard lk(m_blockchain_lock); auto it = m_transactionMap.find(tx_id); if (it == m_transactionMap.end()) { @@ -1334,23 +1375,46 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std:: return true; } -bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail) { +bool Blockchain::get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) { + std::lock_guard lk(m_blockchain_lock); + auto it = m_multisignatureOutputs.find(amount); + if (it == m_multisignatureOutputs.end()) { + return false; + } + + if (it->second.size() <= gindex) { + return false; + } + + auto msigUsage = it->second[gindex]; + auto& targetOut = transactionByIndex(msigUsage.transactionIndex).tx.outputs[msigUsage.outputIndex].target; + if (targetOut.type() != typeid(MultisignatureOutput)) { + return false; + } + + out = boost::get(targetOut); + return true; +} + + + +bool Blockchain::checkTransactionInputs(const Transaction& tx, uint32_t& max_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail) { std::lock_guard lk(m_blockchain_lock); if (tail) - tail->id = get_tail_id(tail->height); + tail->id = getTailId(tail->height); - bool res = check_tx_inputs(tx, &max_used_block_height); + bool res = checkTransactionInputs(tx, &max_used_block_height); if (!res) return false; if (!(max_used_block_height < m_blocks.size())) { logger(ERROR, BRIGHT_RED) << "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size(); return false; } get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); return true; } -bool blockchain_storage::have_tx_keyimges_as_spent(const Transaction &tx) { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (have_tx_keyimg_as_spent(boost::get(in).keyImage)) { +bool Blockchain::haveTransactionKeyImagesAsSpent(const Transaction &tx) { + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (have_tx_keyimg_as_spent(boost::get(in).keyImage)) { return true; } } @@ -1359,23 +1423,23 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const Transaction &tx) { return false; } -bool blockchain_storage::check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height) { - crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); - return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); +bool Blockchain::checkTransactionInputs(const Transaction& tx, uint32_t* pmax_used_block_height) { + Crypto::Hash tx_prefix_hash = getObjectHash(*static_cast(&tx)); + return checkTransactionInputs(tx, tx_prefix_hash, pmax_used_block_height); } -bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) { +bool Blockchain::checkTransactionInputs(const Transaction& tx, const Crypto::Hash& tx_prefix_hash, uint32_t* pmax_used_block_height) { size_t inputIndex = 0; if (pmax_used_block_height) { *pmax_used_block_height = 0; } - crypto::hash transactionHash = get_transaction_hash(tx); - for (const auto& txin : tx.vin) { + Crypto::Hash transactionHash = getObjectHash(tx); + for (const auto& txin : tx.inputs) { assert(inputIndex < tx.signatures.size()); - if (txin.type() == typeid(TransactionInputToKey)) { - const TransactionInputToKey& in_to_key = boost::get(txin); - if (!(!in_to_key.keyOffsets.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.keyOffsets in transaction with id " << get_transaction_hash(tx); return false; } + if (txin.type() == typeid(KeyInput)) { + const KeyInput& in_to_key = boost::get(txin); + if (!(!in_to_key.outputIndexes.empty())) { logger(ERROR, BRIGHT_RED) << "empty in_to_key.outputIndexes in transaction with id " << getObjectHash(tx); return false; } if (have_tx_keyimg_as_spent(in_to_key.keyImage)) { logger(DEBUGGING) << @@ -1390,8 +1454,8 @@ bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::ha } ++inputIndex; - } else if (txin.type() == typeid(TransactionInputMultisignature)) { - if (!validateInput(::boost::get(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) { + } else if (txin.type() == typeid(MultisignatureInput)) { + if (!validateInput(::boost::get(txin), transactionHash, tx_prefix_hash, tx.signatures[inputIndex])) { return false; } @@ -1406,10 +1470,10 @@ bool blockchain_storage::check_tx_inputs(const Transaction& tx, const crypto::ha return true; } -bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { +bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) { if (unlock_time < m_currency.maxBlockHeight()) { //interpret as block index - if (get_current_blockchain_height() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time) + if (getCurrentBlockchainHeight() - 1 + m_currency.lockedTxAllowedDeltaBlocks() >= unlock_time) return true; else return false; @@ -1425,14 +1489,14 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) { return false; } -bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) { +bool Blockchain::check_tx_input(const KeyInput& txin, const Crypto::Hash& tx_prefix_hash, const std::vector& sig, uint32_t* pmax_related_block_height) { std::lock_guard lk(m_blockchain_lock); struct outputs_visitor { - std::vector& m_results_collector; - blockchain_storage& m_bch; + std::vector& m_results_collector; + Blockchain& m_bch; LoggerRef logger; - outputs_visitor(std::vector& results_collector, blockchain_storage& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") { + outputs_visitor(std::vector& results_collector, Blockchain& bch, ILogger& logger) :m_results_collector(results_collector), m_bch(bch), logger(logger, "outputs_visitor") { } bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) { @@ -1443,30 +1507,30 @@ bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const return false; } - if (out.target.type() != typeid(TransactionOutputToKey)) { + if (out.target.type() != typeid(KeyOutput)) { logger(INFO, BRIGHT_WHITE) << "Output have wrong type id, which=" << out.target.which(); return false; } - m_results_collector.push_back(&boost::get(out.target).key); + m_results_collector.push_back(&boost::get(out.target).key); return true; } }; //check ring signature - std::vector output_keys; + std::vector output_keys; outputs_visitor vi(output_keys, *this, logger.getLogger()); - if (!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) { + if (!scanOutputKeysForIndexes(txin, vi, pmax_related_block_height)) { logger(INFO, BRIGHT_WHITE) << "Failed to get output keys for tx with amount = " << m_currency.formatAmount(txin.amount) << - " and count indexes " << txin.keyOffsets.size(); + " and count indexes " << txin.outputIndexes.size(); return false; } - if (txin.keyOffsets.size() != output_keys.size()) { + if (txin.outputIndexes.size() != output_keys.size()) { logger(INFO, BRIGHT_WHITE) << - "Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.keyOffsets.size() << " returned wrong keys count " << output_keys.size(); + "Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.outputIndexes.size() << " returned wrong keys count " << output_keys.size(); return false; } @@ -1475,15 +1539,15 @@ bool blockchain_storage::check_tx_input(const TransactionInputToKey& txin, const return true; } - return crypto::check_ring_signature(tx_prefix_hash, txin.keyImage, output_keys, sig.data()); + return Crypto::check_ring_signature(tx_prefix_hash, txin.keyImage, output_keys, sig.data()); } -uint64_t blockchain_storage::get_adjusted_time() { +uint64_t Blockchain::get_adjusted_time() { //TODO: add collecting median time return time(NULL); } -bool blockchain_storage::check_block_timestamp_main(const Block& b) { +bool Blockchain::check_block_timestamp_main(const Block& b) { if (b.timestamp > get_adjusted_time() + m_currency.blockFutureTimeLimit()) { logger(INFO, BRIGHT_WHITE) << "Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"; @@ -1499,7 +1563,7 @@ bool blockchain_storage::check_block_timestamp_main(const Block& b) { return check_block_timestamp(std::move(timestamps), b); } -bool blockchain_storage::check_block_timestamp(std::vector timestamps, const Block& b) { +bool Blockchain::check_block_timestamp(std::vector timestamps, const Block& b) { if (timestamps.size() < m_currency.timestampCheckWindow()) { return true; } @@ -1516,9 +1580,9 @@ bool blockchain_storage::check_block_timestamp(std::vector timestamps, return true; } -bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& blockHash) { - uint64_t height = get_block_height(b); - const uint8_t expectedBlockVersion = get_block_major_version_for_height(height); +bool Blockchain::checkBlockVersion(const Block& b, const Crypto::Hash& blockHash) { + uint32_t height = get_block_height(b); + const uint8_t expectedBlockVersion = getBlockMajorVersionForHeight(height); if (b.majorVersion != expectedBlockVersion) { logger(TRACE) << "Block " << blockHash << " has wrong major version: " << static_cast(b.majorVersion) << ", at height " << height << " expected version is " << static_cast(expectedBlockVersion); @@ -1528,11 +1592,11 @@ bool blockchain_storage::checkBlockVersion(const Block& b, const crypto::hash& b return true; } -bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash& blockHash) { +bool Blockchain::checkParentBlockSize(const Block& b, const Crypto::Hash& blockHash) { if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { auto serializer = makeParentBlockSerializer(b, false, false); size_t parentBlockSize; - if (!get_object_blobsize(serializer, parentBlockSize)) { + if (!getObjectBinarySize(serializer, parentBlockSize)) { logger(ERROR, BRIGHT_RED) << "Block " << blockHash << ": failed to determine parent block size"; return false; @@ -1549,7 +1613,7 @@ bool blockchain_storage::checkParentBlockSize(const Block& b, const crypto::hash return true; } -bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height) { +bool Blockchain::checkCumulativeBlockSize(const Crypto::Hash& blockId, size_t cumulativeBlockSize, uint64_t height) { size_t maxBlockCumulativeSize = m_currency.maxBlockCumulativeSize(height); if (cumulativeBlockSize > maxBlockCumulativeSize) { logger(INFO, BRIGHT_WHITE) << @@ -1562,22 +1626,22 @@ bool blockchain_storage::checkCumulativeBlockSize(const crypto::hash& blockId, s } // Returns true, if cumulativeSize is calculated precisely, else returns false. -bool blockchain_storage::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) { +bool Blockchain::getBlockCumulativeSize(const Block& block, size_t& cumulativeSize) { std::vector blockTxs; - std::vector missedTxs; - get_transactions(block.txHashes, blockTxs, missedTxs, true); + std::vector missedTxs; + getTransactions(block.transactionHashes, blockTxs, missedTxs, true); - cumulativeSize = get_object_blobsize(block.minerTx); + cumulativeSize = getObjectBinarySize(block.baseTransaction); for (const Transaction& tx : blockTxs) { - cumulativeSize += get_object_blobsize(tx); + cumulativeSize += getObjectBinarySize(tx); } return missedTxs.empty(); } // Precondition: m_blockchain_lock is locked. -bool blockchain_storage::update_next_comulative_size_limit() { - uint8_t nextBlockMajorVersion = get_block_major_version_for_height(m_blocks.size()); +bool Blockchain::update_next_comulative_size_limit() { + uint8_t nextBlockMajorVersion = getBlockMajorVersionForHeight(static_cast(m_blocks.size())); size_t nextBlockGrantedFullRewardZone = nextBlockMajorVersion == BLOCK_MAJOR_VERSION_1 ? parameters::CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : m_currency.blockGrantedFullRewardZone(); @@ -1594,10 +1658,10 @@ bool blockchain_storage::update_next_comulative_size_limit() { return true; } -bool blockchain_storage::add_new_block(const Block& bl_, block_verification_context& bvc) { +bool Blockchain::addNewBlock(const Block& bl_, block_verification_context& bvc) { //copy block here to let modify block.target Block bl = bl_; - crypto::hash id; + Crypto::Hash id; if (!get_block_hash(bl, id)) { logger(ERROR, BRIGHT_RED) << "Failed to get block hash, possible block has invalid format"; @@ -1611,19 +1675,22 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont std::lock_guard poolLock(m_tx_pool); std::lock_guard bcLock(m_blockchain_lock); - if (have_block(id)) { + if (haveBlock(id)) { logger(TRACE) << "block with id = " << id << " already exists"; bvc.m_already_exists = true; return false; } //check that block refers to chain tail - if (!(bl.prevId == get_tail_id())) { + if (!(bl.previousBlockHash == getTailId())) { //chain switching or wrong block bvc.m_added_to_main_chain = false; add_result = handle_alternative_block(bl, id, bvc); } else { add_result = pushBlock(bl, bvc); + if (add_result) { + sendMessage(BlockchainMessage(std::move(NewBlockMessage(id)))); + } } } @@ -1634,16 +1701,31 @@ bool blockchain_storage::add_new_block(const Block& bl_, block_verification_cont return add_result; } -const blockchain_storage::TransactionEntry& blockchain_storage::transactionByIndex(TransactionIndex index) { +const Blockchain::TransactionEntry& Blockchain::transactionByIndex(TransactionIndex index) { return m_blocks[index.block].transactions[index.transaction]; } -bool blockchain_storage::pushBlock(const Block& blockData, block_verification_context& bvc) { +bool Blockchain::pushBlock(const Block& blockData, block_verification_context& bvc) { + std::vector transactions; + if (!loadTransactions(blockData, transactions)) { + bvc.m_verifivation_failed = true; + return false; + } + + if (!pushBlock(blockData, transactions, bvc)) { + saveTransactions(transactions); + return false; + } + + return true; +} + +bool Blockchain::pushBlock(const Block& blockData, const std::vector& transactions, block_verification_context& bvc) { std::lock_guard lk(m_blockchain_lock); auto blockProcessingStart = std::chrono::steady_clock::now(); - crypto::hash blockHash = get_block_hash(blockData); + Crypto::Hash blockHash = get_block_hash(blockData); if (m_blockIndex.hasBlock(blockHash)) { logger(ERROR, BRIGHT_RED) << @@ -1662,9 +1744,9 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co return false; } - if (blockData.prevId != get_tail_id()) { + if (blockData.previousBlockHash != getTailId()) { logger(INFO, BRIGHT_WHITE) << - "Block " << blockHash << " has wrong prevId: " << blockData.prevId << ", expected: " << get_tail_id(); + "Block " << blockHash << " has wrong previousBlockHash: " << blockData.previousBlockHash << ", expected: " << getTailId(); bvc.m_verifivation_failed = true; return false; } @@ -1677,19 +1759,19 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co } auto targetTimeStart = std::chrono::steady_clock::now(); - difficulty_type currentDifficulty = get_difficulty_for_next_block(); + difficulty_type currentDifficulty = getDifficultyForNextBlock(); auto target_calculating_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - targetTimeStart).count(); - if (!(currentDifficulty)) { - logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!"; - return false; + if (!(currentDifficulty)) { + logger(ERROR, BRIGHT_RED) << "!!!!!!!!! difficulty overhead !!!!!!!!!"; + return false; } - + auto longhashTimeStart = std::chrono::steady_clock::now(); - crypto::hash proof_of_work = null_hash; - if (m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) { - if (!m_checkpoints.check_block(get_current_blockchain_height(), blockHash)) { + Crypto::Hash proof_of_work = NULL_HASH; + if (m_checkpoints.is_in_checkpoint_zone(getCurrentBlockchainHeight())) { + if (!m_checkpoints.check_block(getCurrentBlockchainHeight(), blockHash)) { logger(ERROR, BRIGHT_RED) << "CHECKPOINT VALIDATION FAILED"; bvc.m_verifivation_failed = true; @@ -1706,48 +1788,38 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co auto longhash_calculating_time = std::chrono::duration_cast(std::chrono::steady_clock::now() - longhashTimeStart).count(); - if (!prevalidate_miner_transaction(blockData, m_blocks.size())) { + if (!prevalidate_miner_transaction(blockData, static_cast(m_blocks.size()))) { logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " failed to pass prevalidation"; bvc.m_verifivation_failed = true; return false; } - crypto::hash minerTransactionHash = get_transaction_hash(blockData.minerTx); + Crypto::Hash minerTransactionHash = getObjectHash(blockData.baseTransaction); BlockEntry block; block.bl = blockData; block.transactions.resize(1); - block.transactions[0].tx = blockData.minerTx; - TransactionIndex transactionIndex = {static_cast(m_blocks.size()), static_cast(0)}; + block.transactions[0].tx = blockData.baseTransaction; + TransactionIndex transactionIndex = { static_cast(m_blocks.size()), static_cast(0) }; pushTransaction(block, minerTransactionHash, transactionIndex); - size_t coinbase_blob_size = get_object_blobsize(blockData.minerTx); + size_t coinbase_blob_size = getObjectBinarySize(blockData.baseTransaction); size_t cumulative_block_size = coinbase_blob_size; uint64_t fee_summary = 0; - for (const crypto::hash& tx_id : blockData.txHashes) { + for (size_t i = 0; i < transactions.size(); ++i) { + const Crypto::Hash& tx_id = blockData.transactionHashes[i]; block.transactions.resize(block.transactions.size() + 1); size_t blob_size = 0; uint64_t fee = 0; - if (!m_tx_pool.take_tx(tx_id, block.transactions.back().tx, blob_size, fee)) { - logger(INFO, BRIGHT_WHITE) << - "Block " << blockHash << " has at least one unknown transaction: " << tx_id; - bvc.m_verifivation_failed = true; - tx_verification_context tvc = boost::value_initialized(); - block.transactions.pop_back(); - popTransactions(block, minerTransactionHash); - return false; - } + block.transactions.back().tx = transactions[i]; - if (!check_tx_inputs(block.transactions.back().tx)) { + blob_size = toBinaryArray(block.transactions.back().tx).size(); + fee = getInputAmount(block.transactions.back().tx) - getOutputAmount(block.transactions.back().tx); + if (!checkTransactionInputs(block.transactions.back().tx)) { logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " has at least one transaction with wrong inputs: " << tx_id; bvc.m_verifivation_failed = true; - tx_verification_context tvc = boost::value_initialized();; - if (!m_tx_pool.add_tx(block.transactions.back().tx, tvc, true)) { - logger(ERROR, BRIGHT_RED) << - "Cannot move transaction from blockchain to transaction pool."; - } block.transactions.pop_back(); popTransactions(block, minerTransactionHash); @@ -1769,7 +1841,7 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co int64_t emissionChange = 0; uint64_t reward = 0; uint64_t already_generated_coins = m_blocks.empty() ? 0 : m_blocks.back().already_generated_coins; - if (!validate_miner_transaction(blockData, m_blocks.size(), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) { + if (!validate_miner_transaction(blockData, static_cast(m_blocks.size()), cumulative_block_size, already_generated_coins, fee_summary, reward, emissionChange)) { logger(INFO, BRIGHT_WHITE) << "Block " << blockHash << " has invalid miner transaction"; bvc.m_verifivation_failed = true; popTransactions(block, minerTransactionHash); @@ -1804,25 +1876,39 @@ bool blockchain_storage::pushBlock(const Block& blockData, block_verification_co return true; } -bool blockchain_storage::pushBlock(BlockEntry& block) { - crypto::hash blockHash = get_block_hash(block.bl); +bool Blockchain::pushBlock(BlockEntry& block) { + Crypto::Hash blockHash = get_block_hash(block.bl); m_blocks.push_back(block); m_blockIndex.push(blockHash); + m_timestampIndex.add(block.bl.timestamp, blockHash); + m_generatedTransactionsIndex.add(block.bl); + assert(m_blockIndex.size() == m_blocks.size()); return true; } -void blockchain_storage::popBlock(const crypto::hash& blockHash) { +void Blockchain::popBlock(const Crypto::Hash& blockHash) { if (m_blocks.empty()) { logger(ERROR, BRIGHT_RED) << "Attempt to pop block from empty blockchain."; return; } - popTransactions(m_blocks.back(), get_transaction_hash(m_blocks.back().bl.minerTx)); + std::vector transactions(m_blocks.back().transactions.size() - 1); + for (size_t i = 0; i < m_blocks.back().transactions.size() - 1; ++i) { + transactions[i] = m_blocks.back().transactions[1 + i].tx; + } + + saveTransactions(transactions); + + popTransactions(m_blocks.back(), getObjectHash(m_blocks.back().bl.baseTransaction)); + + m_timestampIndex.remove(m_blocks.back().bl.timestamp, blockHash); + m_generatedTransactionsIndex.remove(m_blocks.back().bl); + m_blocks.pop_back(); m_blockIndex.pop(); @@ -1831,7 +1917,7 @@ void blockchain_storage::popBlock(const crypto::hash& blockHash) { m_upgradeDetector.blockPopped(); } -bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex) { +bool Blockchain::pushTransaction(BlockEntry& block, const Crypto::Hash& transactionHash, TransactionIndex transactionIndex) { auto result = m_transactionMap.insert(std::make_pair(transactionHash, transactionIndex)); if (!result.second) { logger(ERROR, BRIGHT_RED) << @@ -1848,14 +1934,14 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& return false; } - for (size_t i = 0; i < transaction.tx.vin.size(); ++i) { - if (transaction.tx.vin[i].type() == typeid(TransactionInputToKey)) { - auto result = m_spent_keys.insert(::boost::get(transaction.tx.vin[i]).keyImage); + for (size_t i = 0; i < transaction.tx.inputs.size(); ++i) { + if (transaction.tx.inputs[i].type() == typeid(KeyInput)) { + auto result = m_spent_keys.insert(::boost::get(transaction.tx.inputs[i]).keyImage); if (!result.second) { logger(ERROR, BRIGHT_RED) << "Double spending transaction was pushed to blockchain."; for (size_t j = 0; j < i; ++j) { - m_spent_keys.erase(::boost::get(transaction.tx.vin[i - 1 - j]).keyImage); + m_spent_keys.erase(::boost::get(transaction.tx.inputs[i - 1 - j]).keyImage); } m_transactionMap.erase(transactionHash); @@ -1864,36 +1950,38 @@ bool blockchain_storage::pushTransaction(BlockEntry& block, const crypto::hash& } } - for (const auto& inv : transaction.tx.vin) { - if (inv.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(inv); + for (const auto& inv : transaction.tx.inputs) { + if (inv.type() == typeid(MultisignatureInput)) { + const MultisignatureInput& in = ::boost::get(inv); auto& amountOutputs = m_multisignatureOutputs[in.amount]; amountOutputs[in.outputIndex].isUsed = true; } } - transaction.m_global_output_indexes.resize(transaction.tx.vout.size()); - for (uint16_t output = 0; output < transaction.tx.vout.size(); ++output) { - if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputToKey)) { - auto& amountOutputs = m_outputs[transaction.tx.vout[output].amount]; + transaction.m_global_output_indexes.resize(transaction.tx.outputs.size()); + for (uint16_t output = 0; output < transaction.tx.outputs.size(); ++output) { + if (transaction.tx.outputs[output].target.type() == typeid(KeyOutput)) { + auto& amountOutputs = m_outputs[transaction.tx.outputs[output].amount]; transaction.m_global_output_indexes[output] = static_cast(amountOutputs.size()); amountOutputs.push_back(std::make_pair<>(transactionIndex, output)); - } else if (transaction.tx.vout[output].target.type() == typeid(TransactionOutputMultisignature)) { - auto& amountOutputs = m_multisignatureOutputs[transaction.tx.vout[output].amount]; + } else if (transaction.tx.outputs[output].target.type() == typeid(MultisignatureOutput)) { + auto& amountOutputs = m_multisignatureOutputs[transaction.tx.outputs[output].amount]; transaction.m_global_output_indexes[output] = static_cast(amountOutputs.size()); - MultisignatureOutputUsage outputUsage = {transactionIndex, output, false}; + MultisignatureOutputUsage outputUsage = { transactionIndex, output, false }; amountOutputs.push_back(outputUsage); } } + m_paymentIdIndex.add(transaction.tx); + return true; } -void blockchain_storage::popTransaction(const Transaction& transaction, const crypto::hash& transactionHash) { +void Blockchain::popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash) { TransactionIndex transactionIndex = m_transactionMap.at(transactionHash); - for (size_t outputIndex = 0; outputIndex < transaction.vout.size(); ++outputIndex) { - const TransactionOutput& output = transaction.vout[transaction.vout.size() - 1 - outputIndex]; - if (output.target.type() == typeid(TransactionOutputToKey)) { + for (size_t outputIndex = 0; outputIndex < transaction.outputs.size(); ++outputIndex) { + const TransactionOutput& output = transaction.outputs[transaction.outputs.size() - 1 - outputIndex]; + if (output.target.type() == typeid(KeyOutput)) { auto amountOutputs = m_outputs.find(output.amount); if (amountOutputs == m_outputs.end()) { logger(ERROR, BRIGHT_RED) << @@ -1913,7 +2001,7 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr continue; } - if (amountOutputs->second.back().second != transaction.vout.size() - 1 - outputIndex) { + if (amountOutputs->second.back().second != transaction.outputs.size() - 1 - outputIndex) { logger(ERROR, BRIGHT_RED) << "Blockchain consistency broken - invalid output index."; continue; @@ -1923,7 +2011,7 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr if (amountOutputs->second.empty()) { m_outputs.erase(amountOutputs); } - } else if (output.target.type() == typeid(TransactionOutputMultisignature)) { + } else if (output.target.type() == typeid(MultisignatureOutput)) { auto amountOutputs = m_multisignatureOutputs.find(output.amount); if (amountOutputs == m_multisignatureOutputs.end()) { logger(ERROR, BRIGHT_RED) << @@ -1949,7 +2037,7 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr continue; } - if (amountOutputs->second.back().outputIndex != transaction.vout.size() - 1 - outputIndex) { + if (amountOutputs->second.back().outputIndex != transaction.outputs.size() - 1 - outputIndex) { logger(ERROR, BRIGHT_RED) << "Blockchain consistency broken - invalid output index."; continue; @@ -1962,15 +2050,15 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } } - for (auto& input : transaction.vin) { - if (input.type() == typeid(TransactionInputToKey)) { - size_t count = m_spent_keys.erase(::boost::get(input).keyImage); + for (auto& input : transaction.inputs) { + if (input.type() == typeid(KeyInput)) { + size_t count = m_spent_keys.erase(::boost::get(input).keyImage); if (count != 1) { logger(ERROR, BRIGHT_RED) << "Blockchain consistency broken - cannot find spent key."; } - } else if (input.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(input); + } else if (input.type() == typeid(MultisignatureInput)) { + const MultisignatureInput& in = ::boost::get(input); auto& amountOutputs = m_multisignatureOutputs[in.amount]; if (!amountOutputs[in.outputIndex].isUsed) { logger(ERROR, BRIGHT_RED) << @@ -1981,6 +2069,8 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } } + m_paymentIdIndex.remove(transaction); + size_t count = m_transactionMap.erase(transactionHash); if (count != 1) { logger(ERROR, BRIGHT_RED) << @@ -1988,21 +2078,16 @@ void blockchain_storage::popTransaction(const Transaction& transaction, const cr } } -void blockchain_storage::popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash) { +void Blockchain::popTransactions(const BlockEntry& block, const Crypto::Hash& minerTransactionHash) { for (size_t i = 0; i < block.transactions.size() - 1; ++i) { - popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.txHashes[block.transactions.size() - 2 - i]); - tx_verification_context tvc = boost::value_initialized(); - if (!m_tx_pool.add_tx(block.transactions[block.transactions.size() - 1 - i].tx, tvc, true)) { - logger(ERROR, BRIGHT_RED) << - "Cannot move transaction from blockchain to transaction pool."; - } + popTransaction(block.transactions[block.transactions.size() - 1 - i].tx, block.bl.transactionHashes[block.transactions.size() - 2 - i]); } - popTransaction(block.bl.minerTx, minerTransactionHash); + popTransaction(block.bl.baseTransaction, minerTransactionHash); } -bool blockchain_storage::validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector& transactionSignatures) { - assert(input.signatures == transactionSignatures.size()); +bool Blockchain::validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector& transactionSignatures) { + assert(input.signatureCount == transactionSignatures.size()); MultisignatureOutputsContainer::const_iterator amountOutputs = m_multisignatureOutputs.find(input.amount); if (amountOutputs == m_multisignatureOutputs.end()) { logger(DEBUGGING) << @@ -2030,25 +2115,25 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp return false; } - assert(outputTransaction.vout[outputIndex.outputIndex].amount == input.amount); - assert(outputTransaction.vout[outputIndex.outputIndex].target.type() == typeid(TransactionOutputMultisignature)); - const TransactionOutputMultisignature& output = ::boost::get(outputTransaction.vout[outputIndex.outputIndex].target); - if (input.signatures != output.requiredSignatures) { + assert(outputTransaction.outputs[outputIndex.outputIndex].amount == input.amount); + assert(outputTransaction.outputs[outputIndex.outputIndex].target.type() == typeid(MultisignatureOutput)); + const MultisignatureOutput& output = ::boost::get(outputTransaction.outputs[outputIndex.outputIndex].target); + if (input.signatureCount != output.requiredSignatureCount) { logger(DEBUGGING) << "Transaction << " << transactionHash << " contains multisignature input with invalid signature count."; return false; } - std::size_t inputSignatureIndex = 0; - std::size_t outputKeyIndex = 0; - while (inputSignatureIndex < input.signatures) { + size_t inputSignatureIndex = 0; + size_t outputKeyIndex = 0; + while (inputSignatureIndex < input.signatureCount) { if (outputKeyIndex == output.keys.size()) { logger(DEBUGGING) << "Transaction << " << transactionHash << " contains multisignature input with invalid signatures."; return false; } - if (crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) { + if (Crypto::check_signature(transactionPrefixHash, output.keys[outputKeyIndex], transactionSignatures[inputSignatureIndex])) { ++inputSignatureIndex; } @@ -2058,12 +2143,10 @@ bool blockchain_storage::validateInput(const TransactionInputMultisignature& inp return true; } -bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height) { +bool Blockchain::getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height) { std::lock_guard lk(m_blockchain_lock); - if (startOffset >= m_blocks.size()) { - return false; - } + assert(startOffset < m_blocks.size()); auto bound = std::lower_bound(m_blocks.begin() + startOffset, m_blocks.end(), timestamp - m_currency.blockFutureTimeLimit(), [](const BlockEntry& b, uint64_t timestamp) { return b.bl.timestamp < timestamp; }); @@ -2072,32 +2155,32 @@ bool blockchain_storage::getLowerBound(uint64_t timestamp, uint64_t startOffset, return false; } - height = std::distance(m_blocks.begin(), bound); + height = static_cast(std::distance(m_blocks.begin(), bound)); return true; } -bool blockchain_storage::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) { +std::vector Blockchain::getBlockIds(uint32_t startHeight, uint32_t maxCount) { std::lock_guard lk(m_blockchain_lock); - return m_blockIndex.getBlockIds(startHeight, maxCount, items); + return m_blockIndex.getBlockIds(startHeight, maxCount); } -bool blockchain_storage::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { +bool Blockchain::getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) { std::lock_guard lk(m_blockchain_lock); auto it = m_transactionMap.find(txId); - if(it == m_transactionMap.end()) { + if (it == m_transactionMap.end()) { return false; } else { blockHeight = m_blocks[it->second.block].height; - blockId = get_block_id_by_height(blockHeight); + blockId = getBlockIdByHeight(blockHeight); return true; } } -bool blockchain_storage::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { +bool Blockchain::getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) { std::lock_guard lk(m_blockchain_lock); // try to find block in main chain - uint64_t height = 0; + uint32_t height = 0; if (m_blockIndex.getBlockHeight(hash, height)) { generatedCoins = m_blocks[height].already_generated_coins; return true; @@ -2114,11 +2197,11 @@ bool blockchain_storage::getAlreadyGeneratedCoins(const crypto::hash& hash, uint return false; } -bool blockchain_storage::getBlockSize(const crypto::hash& hash, size_t& size) { +bool Blockchain::getBlockSize(const Crypto::Hash& hash, size_t& size) { std::lock_guard lk(m_blockchain_lock); // try to find block in main chain - uint64_t height = 0; + uint32_t height = 0; if (m_blockIndex.getBlockHeight(hash, height)) { size = m_blocks[height].block_cumulative_size; return true; @@ -2135,7 +2218,7 @@ bool blockchain_storage::getBlockSize(const crypto::hash& hash, size_t& size) { return false; } -bool blockchain_storage::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { +bool Blockchain::getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) { std::lock_guard lk(m_blockchain_lock); MultisignatureOutputsContainer::const_iterator amountIter = m_multisignatureOutputs.find(txInMultisig.amount); if (amountIter == m_multisignatureOutputs.end()) { @@ -2148,8 +2231,125 @@ bool blockchain_storage::getMultisigOutputReference(const TransactionInputMultis } const MultisignatureOutputUsage& outputIndex = amountIter->second[txInMultisig.outputIndex]; const Transaction& outputTransaction = m_blocks[outputIndex.transactionIndex.block].transactions[outputIndex.transactionIndex.transaction].tx; - outputReference.first = get_transaction_hash(outputTransaction); + outputReference.first = getObjectHash(outputTransaction); outputReference.second = outputIndex.outputIndex; return true; } + +bool Blockchain::storeBlockchainIndices() { + std::lock_guard lk(m_blockchain_lock); + + logger(INFO, BRIGHT_WHITE) << "Saving blockchain indices..."; + BlockchainIndicesSerializer ser(*this, getTailId(), logger.getLogger()); + + if (!storeToBinaryFile(ser, appendPath(m_config_folder, m_currency.blockchinIndicesFileName()))) { + logger(ERROR, BRIGHT_RED) << "Failed to save blockchain indices"; + return false; + } + + return true; +} + +bool Blockchain::loadBlockchainIndices() { + std::lock_guard lk(m_blockchain_lock); + + logger(INFO, BRIGHT_WHITE) << "Loading blockchain indices for BlockchainExplorer..."; + BlockchainIndicesSerializer loader(*this, get_block_hash(m_blocks.back().bl), logger.getLogger()); + + loadFromBinaryFile(loader, appendPath(m_config_folder, m_currency.blockchinIndicesFileName())); + + if (!loader.loaded()) { + logger(WARNING, BRIGHT_YELLOW) << "No actual blockchain indices for BlockchainExplorer found, rebuilding..."; + std::chrono::steady_clock::time_point timePoint = std::chrono::steady_clock::now(); + + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + m_generatedTransactionsIndex.clear(); + + for (uint32_t b = 0; b < m_blocks.size(); ++b) { + if (b % 1000 == 0) { + logger(INFO, BRIGHT_WHITE) << "Height " << b << " of " << m_blocks.size(); + } + const BlockEntry& block = m_blocks[b]; + m_timestampIndex.add(block.bl.timestamp, get_block_hash(block.bl)); + m_generatedTransactionsIndex.add(block.bl); + for (uint16_t t = 0; t < block.transactions.size(); ++t) { + const TransactionEntry& transaction = block.transactions[t]; + m_paymentIdIndex.add(transaction.tx); + } + } + + std::chrono::duration duration = std::chrono::steady_clock::now() - timePoint; + logger(INFO, BRIGHT_WHITE) << "Rebuilding blockchain indices took: " << duration.count(); + } + return true; +} + +bool Blockchain::getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) { + std::lock_guard lk(m_blockchain_lock); + return m_generatedTransactionsIndex.find(height, generatedTransactions); +} + +bool Blockchain::getOrphanBlockIdsByHeight(uint32_t height, std::vector& blockHashes) { + std::lock_guard lk(m_blockchain_lock); + return m_orthanBlocksIndex.find(height, blockHashes); +} + +bool Blockchain::getBlockIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& hashes, uint32_t& blocksNumberWithinTimestamps) { + std::lock_guard lk(m_blockchain_lock); + return m_timestampIndex.find(timestampBegin, timestampEnd, blocksNumberLimit, hashes, blocksNumberWithinTimestamps); +} + +bool Blockchain::getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionHashes) { + std::lock_guard lk(m_blockchain_lock); + return m_paymentIdIndex.find(paymentId, transactionHashes); +} + +bool Blockchain::loadTransactions(const Block& block, std::vector& transactions) { + transactions.resize(block.transactionHashes.size()); + size_t transactionSize; + uint64_t fee; + for (size_t i = 0; i < block.transactionHashes.size(); ++i) { + if (!m_tx_pool.take_tx(block.transactionHashes[i], transactions[i], transactionSize, fee)) { + tx_verification_context context; + for (size_t j = 0; j < i; ++j) { + if (!m_tx_pool.add_tx(transactions[i - 1 - j], context, true)) { + throw std::runtime_error("Blockchain::loadTransactions, failed to add transaction to pool"); + } + } + + return false; + } + } + + return true; +} + +void Blockchain::saveTransactions(const std::vector& transactions) { + tx_verification_context context; + for (size_t i = 0; i < transactions.size(); ++i) { + if (!m_tx_pool.add_tx(transactions[transactions.size() - 1 - i], context, true)) { + throw std::runtime_error("Blockchain::saveTransactions, failed to add transaction to pool"); + } + } +} + +bool Blockchain::addMessageQueue(MessageQueue& messageQueue) { + return m_messageQueueList.insert(messageQueue); +} + +bool Blockchain::removeMessageQueue(MessageQueue& messageQueue) { + return m_messageQueueList.remove(messageQueue); +} + +void Blockchain::sendMessage(const BlockchainMessage& message) { + for (IntrusiveLinkedList>::iterator iter = m_messageQueueList.begin(); iter != m_messageQueueList.end(); ++iter) { + iter->push(message); + } +} + +bool Blockchain::isBlockInMainChain(const Crypto::Hash& blockId) { + return m_blockIndex.hasBlock(blockId); +} + } diff --git a/src/CryptoNoteCore/Blockchain.h b/src/CryptoNoteCore/Blockchain.h new file mode 100755 index 0000000000..f748fbfc52 --- /dev/null +++ b/src/CryptoNoteCore/Blockchain.h @@ -0,0 +1,382 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "google/sparse_hash_set" +#include "google/sparse_hash_map" + +#include "Common/ObserverManager.h" +#include "Common/Util.h" +#include "CryptoNoteCore/BlockIndex.h" +#include "CryptoNoteCore/Checkpoints.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/IBlockchainStorageObserver.h" +#include "CryptoNoteCore/ITransactionValidator.h" +#include "CryptoNoteCore/SwappedVector.h" +#include "CryptoNoteCore/UpgradeDetector.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/TransactionPool.h" +#include "CryptoNoteCore/BlockchainIndices.h" + +#include "CryptoNoteCore/MessageQueue.h" +#include "CryptoNoteCore/BlockchainMessages.h" +#include "CryptoNoteCore/IntrusiveLinkedList.h" + +#include + +#undef ERROR + +namespace CryptoNote { + struct NOTIFY_REQUEST_GET_OBJECTS_request; + struct NOTIFY_RESPONSE_GET_OBJECTS_request; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount; + + using CryptoNote::BlockInfo; + class Blockchain : public CryptoNote::ITransactionValidator { + public: + Blockchain(const Currency& currency, tx_memory_pool& tx_pool, Logging::ILogger& logger); + + bool addObserver(IBlockchainStorageObserver* observer); + bool removeObserver(IBlockchainStorageObserver* observer); + + // ITransactionValidator + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) override; + virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) override; + virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) override; + virtual bool checkTransactionSize(size_t blobSize) override; + + bool init() { return init(Tools::getDefaultDataDirectory(), true); } + bool init(const std::string& config_folder, bool load_existing); + bool deinit(); + + bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t& height); + std::vector getBlockIds(uint32_t startHeight, uint32_t maxCount); + + void setCheckpoints(Checkpoints&& chk_pts) { m_checkpoints = chk_pts; } + bool getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs); + bool getBlocks(uint32_t start_offset, uint32_t count, std::list& blocks); + bool getAlternativeBlocks(std::list& blocks); + uint32_t getAlternativeBlocksCount(); + Crypto::Hash getBlockIdByHeight(uint32_t height); + bool getBlockByHash(const Crypto::Hash &h, Block &blk); + bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight); + + template void serialize(archive_t & ar, const unsigned int version); + + bool haveTransaction(const Crypto::Hash &id); + bool haveTransactionKeyImagesAsSpent(const Transaction &tx); + + uint32_t getCurrentBlockchainHeight(); //TODO rename to getCurrentBlockchainSize + Crypto::Hash getTailId(); + Crypto::Hash getTailId(uint32_t& height); + difficulty_type getDifficultyForNextBlock(); + uint64_t getCoinsInCirculation(); + uint8_t getBlockMajorVersionForHeight(uint32_t height) const; + bool addNewBlock(const Block& bl_, block_verification_context& bvc); + bool resetAndSetGenesisBlock(const Block& b); + bool haveBlock(const Crypto::Hash& id); + size_t getTotalTransactions(); + std::vector buildSparseChain(); + std::vector buildSparseChain(const Crypto::Hash& startBlockId); + uint32_t findBlockchainSupplement(const std::vector& qblock_ids); // !!!! + std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex); + bool handleGetObjects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp); //Deprecated. Should be removed with CryptoNoteProtocolHandler. + bool getRandomOutsByAmount(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response& res); + bool getBackwardBlocksSize(size_t from_height, std::vector& sz, size_t count); + bool getTransactionOutputGlobalIndexes(const Crypto::Hash& tx_id, std::vector& indexs); + bool get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out); + bool checkTransactionInputs(const Transaction& tx, uint32_t& pmax_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail = 0); + uint64_t getCurrentCumulativeBlocksizeLimit(); + bool isStoringBlockchain(){return m_is_blockchain_storing;} + uint64_t blockDifficulty(size_t i); + bool getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight); + bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins); + bool getBlockSize(const Crypto::Hash& hash, size_t& size); + bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference); + bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions); + bool getOrphanBlockIdsByHeight(uint32_t height, std::vector& blockHashes); + bool getBlockIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& hashes, uint32_t& blocksNumberWithinTimestamps); + bool getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionHashes); + bool isBlockInMainChain(const Crypto::Hash& blockId); + + template bool scanOutputKeysForIndexes(const KeyInput& tx_in_to_key, visitor_t& vis, uint32_t* pmax_related_block_height = NULL); + + bool addMessageQueue(MessageQueue& messageQueue); + bool removeMessageQueue(MessageQueue& messageQueue); + + template + bool getBlocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { + std::lock_guard lk(m_blockchain_lock); + + for (const auto& bl_id : block_ids) { + uint32_t height = 0; + if (!m_blockIndex.getBlockHeight(bl_id, height)) { + missed_bs.push_back(bl_id); + } else { + if (!(height < m_blocks.size())) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: bl_id=" << Common::podToHex(bl_id) + << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size(); return false; } + blocks.push_back(m_blocks[height].bl); + } + } + + return true; + } + + template + void getBlockchainTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { + std::lock_guard bcLock(m_blockchain_lock); + + for (const auto& tx_id : txs_ids) { + auto it = m_transactionMap.find(tx_id); + if (it == m_transactionMap.end()) { + missed_txs.push_back(tx_id); + } else { + txs.push_back(transactionByIndex(it->second).tx); + } + } + } + + template + void getTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { + if (checkTxPool){ + std::lock_guard txLock(m_tx_pool); + + getBlockchainTransactions(txs_ids, txs, missed_txs); + + auto poolTxIds = std::move(missed_txs); + missed_txs.clear(); + m_tx_pool.getTransactions(poolTxIds, txs, missed_txs); + + } else { + getBlockchainTransactions(txs_ids, txs, missed_txs); + } + } + + //debug functions + void print_blockchain(uint64_t start_index, uint64_t end_index); + void print_blockchain_index(); + void print_blockchain_outs(const std::string& file); + + struct TransactionIndex { + uint32_t block; + uint16_t transaction; + + void serialize(ISerializer& s) { + s(block, "block"); + s(transaction, "tx"); + } + }; + + private: + + struct MultisignatureOutputUsage { + TransactionIndex transactionIndex; + uint16_t outputIndex; + bool isUsed; + + void serialize(ISerializer& s) { + s(transactionIndex, "txindex"); + s(outputIndex, "outindex"); + s(isUsed, "used"); + } + }; + + struct TransactionEntry { + Transaction tx; + std::vector m_global_output_indexes; + + void serialize(ISerializer& s) { + s(tx, "tx"); + s(m_global_output_indexes, "indexes"); + } + }; + + struct BlockEntry { + Block bl; + uint32_t height; + uint64_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + std::vector transactions; + + void serialize(ISerializer& s) { + s(bl, "block"); + s(height, "height"); + s(block_cumulative_size, "block_cumulative_size"); + s(cumulative_difficulty, "cumulative_difficulty"); + s(already_generated_coins, "already_generated_coins"); + s(transactions, "transactions"); + } + }; + + typedef google::sparse_hash_set key_images_container; + typedef std::unordered_map blocks_ext_by_hash; + typedef google::sparse_hash_map>> outputs_container; //Crypto::Hash - tx hash, size_t - index of out in transaction + typedef google::sparse_hash_map> MultisignatureOutputsContainer; + + const Currency& m_currency; + tx_memory_pool& m_tx_pool; + std::recursive_mutex m_blockchain_lock; // TODO: add here reader/writer lock + Crypto::cn_context m_cn_context; + Tools::ObserverManager m_observerManager; + + key_images_container m_spent_keys; + size_t m_current_block_cumul_sz_limit; + blocks_ext_by_hash m_alternative_chains; // Crypto::Hash -> block_extended_info + outputs_container m_outputs; + + std::string m_config_folder; + Checkpoints m_checkpoints; + std::atomic m_is_in_checkpoint_zone; + std::atomic m_is_blockchain_storing; + + typedef SwappedVector Blocks; + typedef std::unordered_map BlockMap; + typedef std::unordered_map TransactionMap; + typedef BasicUpgradeDetector UpgradeDetector; + + friend class BlockCacheSerializer; + friend class BlockchainIndicesSerializer; + + Blocks m_blocks; + CryptoNote::BlockIndex m_blockIndex; + TransactionMap m_transactionMap; + MultisignatureOutputsContainer m_multisignatureOutputs; + UpgradeDetector m_upgradeDetector; + + PaymentIdIndex m_paymentIdIndex; + TimestampBlocksIndex m_timestampIndex; + GeneratedTransactionsIndex m_generatedTransactionsIndex; + OrphanBlocksIndex m_orthanBlocksIndex; + + IntrusiveLinkedList> m_messageQueueList; + + Logging::LoggerRef logger; + + void rebuildCache(); + bool storeCache(); + bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); + bool handle_alternative_block(const Block& b, const Crypto::Hash& id, block_verification_context& bvc, bool sendNewAlternativeBlockMessage = true); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); + bool prevalidate_miner_transaction(const Block& b, uint32_t height); + bool validate_miner_transaction(const Block& b, uint32_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange); + bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); + bool get_last_n_blocks_sizes(std::vector& sz, size_t count); + bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount& result_outs, uint64_t amount, size_t i); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + size_t find_end_of_allowed_index(const std::vector>& amount_outs); + bool check_block_timestamp_main(const Block& b); + bool check_block_timestamp(std::vector timestamps, const Block& b); + uint64_t get_adjusted_time(); + bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); + bool checkBlockVersion(const Block& b, const Crypto::Hash& blockHash); + bool checkParentBlockSize(const Block& b, const Crypto::Hash& blockHash); + bool checkCumulativeBlockSize(const Crypto::Hash& blockId, size_t cumulativeBlockSize, uint64_t height); + std::vector doBuildSparseChain(const Crypto::Hash& startBlockId) const; + bool getBlockCumulativeSize(const Block& block, size_t& cumulativeSize); + bool update_next_comulative_size_limit(); + bool check_tx_input(const KeyInput& txin, const Crypto::Hash& tx_prefix_hash, const std::vector& sig, uint32_t* pmax_related_block_height = NULL); + bool checkTransactionInputs(const Transaction& tx, const Crypto::Hash& tx_prefix_hash, uint32_t* pmax_used_block_height = NULL); + bool checkTransactionInputs(const Transaction& tx, uint32_t* pmax_used_block_height = NULL); + bool have_tx_keyimg_as_spent(const Crypto::KeyImage &key_im); + const TransactionEntry& transactionByIndex(TransactionIndex index); + bool pushBlock(const Block& blockData, block_verification_context& bvc); + bool pushBlock(const Block& blockData, const std::vector& transactions, block_verification_context& bvc); + bool pushBlock(BlockEntry& block); + void popBlock(const Crypto::Hash& blockHash); + bool pushTransaction(BlockEntry& block, const Crypto::Hash& transactionHash, TransactionIndex transactionIndex); + void popTransaction(const Transaction& transaction, const Crypto::Hash& transactionHash); + void popTransactions(const BlockEntry& block, const Crypto::Hash& minerTransactionHash); + bool validateInput(const MultisignatureInput& input, const Crypto::Hash& transactionHash, const Crypto::Hash& transactionPrefixHash, const std::vector& transactionSignatures); + + bool storeBlockchainIndices(); + bool loadBlockchainIndices(); + + bool loadTransactions(const Block& block, std::vector& transactions); + void saveTransactions(const std::vector& transactions); + + void sendMessage(const BlockchainMessage& message); + + friend class LockedBlockchainStorage; + }; + + class LockedBlockchainStorage: boost::noncopyable { + public: + + LockedBlockchainStorage(Blockchain& bc) + : m_bc(bc), m_lock(bc.m_blockchain_lock) {} + + Blockchain* operator -> () { + return &m_bc; + } + + private: + + Blockchain& m_bc; + std::lock_guard m_lock; + }; + + template bool Blockchain::scanOutputKeysForIndexes(const KeyInput& tx_in_to_key, visitor_t& vis, uint32_t* pmax_related_block_height) { + std::lock_guard lk(m_blockchain_lock); + auto it = m_outputs.find(tx_in_to_key.amount); + if (it == m_outputs.end() || !tx_in_to_key.outputIndexes.size()) + return false; + + std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.outputIndexes); + std::vector>& amount_outs_vec = it->second; + size_t count = 0; + for (uint64_t i : absolute_offsets) { + if(i >= amount_outs_vec.size() ) { + logger(Logging::INFO) << "Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1; + return false; + } + + //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); + //if (!(tx_it != m_transactionMap.end())) { logger(ERROR, BRIGHT_RED) << "Wrong transaction id in output indexes: " << Common::podToHex(amount_outs_vec[i].first); return false; } + + const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first); + + if (!(amount_outs_vec[i].second < tx.tx.outputs.size())) { + logger(Logging::ERROR, Logging::BRIGHT_RED) + << "Wrong index in transaction outputs: " + << amount_outs_vec[i].second << ", expected less then " + << tx.tx.outputs.size(); + return false; + } + + if (!vis.handle_output(tx.tx, tx.tx.outputs[amount_outs_vec[i].second], amount_outs_vec[i].second)) { + logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i; + return false; + } + + if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) { + if (*pmax_related_block_height < amount_outs_vec[i].first.block) { + *pmax_related_block_height = amount_outs_vec[i].first.block; + } + } + } + + return true; + } +} + diff --git a/src/CryptoNoteCore/BlockchainIndices.cpp b/src/CryptoNoteCore/BlockchainIndices.cpp new file mode 100755 index 0000000000..584daa5017 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainIndices.cpp @@ -0,0 +1,262 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "BlockchainIndices.h" + +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "BlockchainExplorer/BlockchainExplorerDataBuilder.h" +#include "CryptoNoteBasicImpl.h" + +namespace CryptoNote { + +bool PaymentIdIndex::add(const Transaction& transaction) { + Crypto::Hash paymentId; + Crypto::Hash transactionHash = getObjectHash(transaction); + if (!BlockchainExplorerDataBuilder::getPaymentId(transaction, paymentId)) { + return false; + } + + index.emplace(paymentId, transactionHash); + + return true; +} + +bool PaymentIdIndex::remove(const Transaction& transaction) { + Crypto::Hash paymentId; + Crypto::Hash transactionHash = getObjectHash(transaction); + if (!BlockchainExplorerDataBuilder::getPaymentId(transaction, paymentId)) { + return false; + } + + auto range = index.equal_range(paymentId); + for (auto iter = range.first; iter != range.second; ++iter){ + if (iter->second == transactionHash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool PaymentIdIndex::find(const Crypto::Hash& paymentId, std::vector& transactionHashes) { + bool found = false; + auto range = index.equal_range(paymentId); + for (auto iter = range.first; iter != range.second; ++iter){ + found = true; + transactionHashes.emplace_back(iter->second); + } + return found; +} + +void PaymentIdIndex::clear() { + index.clear(); +} + + +void PaymentIdIndex::serialize(ISerializer& s) { + s(index, "index"); +} + +bool TimestampBlocksIndex::add(uint64_t timestamp, const Crypto::Hash& hash) { + index.emplace(timestamp, hash); + return true; +} + +bool TimestampBlocksIndex::remove(uint64_t timestamp, const Crypto::Hash& hash) { + auto range = index.equal_range(timestamp); + for (auto iter = range.first; iter != range.second; ++iter) { + if (iter->second == hash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool TimestampBlocksIndex::find(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t hashesNumberLimit, std::vector& hashes, uint32_t& hashesNumberWithinTimestamps) { + uint32_t hashesNumber = 0; + if (timestampBegin > timestampEnd) { + //std::swap(timestampBegin, timestampEnd); + return false; + } + auto begin = index.lower_bound(timestampBegin); + auto end = index.upper_bound(timestampEnd); + + hashesNumberWithinTimestamps = static_cast(std::distance(begin, end)); + + for (auto iter = begin; iter != end && hashesNumber < hashesNumberLimit; ++iter){ + ++hashesNumber; + hashes.emplace_back(iter->second); + } + return hashesNumber > 0; +} + +void TimestampBlocksIndex::clear() { + index.clear(); +} + +void TimestampBlocksIndex::serialize(ISerializer& s) { + s(index, "index"); +} + +bool TimestampTransactionsIndex::add(uint64_t timestamp, const Crypto::Hash& hash) { + index.emplace(timestamp, hash); + return true; +} + +bool TimestampTransactionsIndex::remove(uint64_t timestamp, const Crypto::Hash& hash) { + auto range = index.equal_range(timestamp); + for (auto iter = range.first; iter != range.second; ++iter) { + if (iter->second == hash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool TimestampTransactionsIndex::find(uint64_t timestampBegin, uint64_t timestampEnd, uint64_t hashesNumberLimit, std::vector& hashes, uint64_t& hashesNumberWithinTimestamps) { + uint32_t hashesNumber = 0; + if (timestampBegin > timestampEnd) { + //std::swap(timestampBegin, timestampEnd); + return false; + } + auto begin = index.lower_bound(timestampBegin); + auto end = index.upper_bound(timestampEnd); + + hashesNumberWithinTimestamps = static_cast(std::distance(begin, end)); + + for (auto iter = begin; iter != end && hashesNumber < hashesNumberLimit; ++iter) { + ++hashesNumber; + hashes.emplace_back(iter->second); + } + return hashesNumber > 0; +} + +void TimestampTransactionsIndex::clear() { + index.clear(); +} + +void TimestampTransactionsIndex::serialize(ISerializer& s) { + s(index, "index"); +} + +GeneratedTransactionsIndex::GeneratedTransactionsIndex() : lastGeneratedTxNumber(0) { + +} + +bool GeneratedTransactionsIndex::add(const Block& block) { + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + + if (index.size() != blockHeight) { + return false; + } + + bool status = index.emplace(blockHeight, lastGeneratedTxNumber + block.transactionHashes.size() + 1).second; //Plus miner tx + if (status) { + lastGeneratedTxNumber += block.transactionHashes.size() + 1; + } + return status; +} + +bool GeneratedTransactionsIndex::remove(const Block& block) { + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + + if (blockHeight != index.size() - 1) { + return false; + } + + auto iter = index.find(blockHeight); + assert(iter != index.end()); + index.erase(iter); + + if (blockHeight != 0) { + iter = index.find(blockHeight - 1); + assert(iter != index.end()); + lastGeneratedTxNumber = iter->second; + } else { + lastGeneratedTxNumber = 0; + } + + return true; +} + +bool GeneratedTransactionsIndex::find(uint32_t height, uint64_t& generatedTransactions) { + if (height > std::numeric_limits::max()) { + return false; + } + auto iter = index.find(height); + if (iter == index.end()) { + return false; + } + generatedTransactions = iter->second; + return true; +} + +void GeneratedTransactionsIndex::clear() { + index.clear(); +} + +void GeneratedTransactionsIndex::serialize(ISerializer& s) { + s(index, "index"); + s(lastGeneratedTxNumber, "lastGeneratedTxNumber"); +} + +bool OrphanBlocksIndex::add(const Block& block) { + Crypto::Hash blockHash = get_block_hash(block); + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + index.emplace(blockHeight, blockHash); + return true; +} + +bool OrphanBlocksIndex::remove(const Block& block) { + Crypto::Hash blockHash = get_block_hash(block); + uint32_t blockHeight = boost::get(block.baseTransaction.inputs.front()).blockIndex; + auto range = index.equal_range(blockHeight); + for (auto iter = range.first; iter != range.second; ++iter) { + if (iter->second == blockHash) { + index.erase(iter); + return true; + } + } + + return false; +} + +bool OrphanBlocksIndex::find(uint32_t height, std::vector& blockHashes) { + if (height > std::numeric_limits::max()) { + return false; + } + bool found = false; + auto range = index.equal_range(height); + for (auto iter = range.first; iter != range.second; ++iter) { + found = true; + blockHashes.emplace_back(iter->second); + } + return found; +} + +void OrphanBlocksIndex::clear() { + index.clear(); +} + +} diff --git a/src/CryptoNoteCore/BlockchainIndices.h b/src/CryptoNoteCore/BlockchainIndices.h new file mode 100755 index 0000000000..96d761896b --- /dev/null +++ b/src/CryptoNoteCore/BlockchainIndices.h @@ -0,0 +1,121 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include "crypto/hash.h" +#include "CryptoNoteBasic.h" + +namespace CryptoNote { + +class ISerializer; + +class PaymentIdIndex { +public: + PaymentIdIndex() = default; + + bool add(const Transaction& transaction); + bool remove(const Transaction& transaction); + bool find(const Crypto::Hash& paymentId, std::vector& transactionHashes); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + } +private: + std::unordered_multimap index; +}; + +class TimestampBlocksIndex { +public: + TimestampBlocksIndex() = default; + + bool add(uint64_t timestamp, const Crypto::Hash& hash); + bool remove(uint64_t timestamp, const Crypto::Hash& hash); + bool find(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t hashesNumberLimit, std::vector& hashes, uint32_t& hashesNumberWithinTimestamps); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + } +private: + std::multimap index; +}; + +class TimestampTransactionsIndex { +public: + TimestampTransactionsIndex() = default; + + bool add(uint64_t timestamp, const Crypto::Hash& hash); + bool remove(uint64_t timestamp, const Crypto::Hash& hash); + bool find(uint64_t timestampBegin, uint64_t timestampEnd, uint64_t hashesNumberLimit, std::vector& hashes, uint64_t& hashesNumberWithinTimestamps); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + } +private: + std::multimap index; +}; + +class GeneratedTransactionsIndex { +public: + GeneratedTransactionsIndex(); + + bool add(const Block& block); + bool remove(const Block& block); + bool find(uint32_t height, uint64_t& generatedTransactions); + void clear(); + + void serialize(ISerializer& s); + + template + void serialize(Archive& archive, unsigned int version) { + archive & index; + archive & lastGeneratedTxNumber; + } +private: + std::unordered_map index; + uint64_t lastGeneratedTxNumber; +}; + +class OrphanBlocksIndex { +public: + OrphanBlocksIndex() = default; + + bool add(const Block& block); + bool remove(const Block& block); + bool find(uint32_t height, std::vector& blockHashes); + void clear(); +private: + std::unordered_multimap index; +}; + +} diff --git a/src/CryptoNoteCore/BlockchainMessages.cpp b/src/CryptoNoteCore/BlockchainMessages.cpp new file mode 100644 index 0000000000..7b23cd38d1 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainMessages.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteCore/BlockchainMessages.h" + +namespace CryptoNote { + +NewBlockMessage::NewBlockMessage(const Crypto::Hash& hash) : blockHash(hash) {} + +void NewBlockMessage::get(Crypto::Hash& hash) const { + hash = blockHash; +} + +NewAlternativeBlockMessage::NewAlternativeBlockMessage(const Crypto::Hash& hash) : blockHash(hash) {} + +void NewAlternativeBlockMessage::get(Crypto::Hash& hash) const { + hash = blockHash; +} + +ChainSwitchMessage::ChainSwitchMessage(std::vector&& hashes) : blocksFromCommonRoot(std::move(hashes)) {} + +ChainSwitchMessage::ChainSwitchMessage(const ChainSwitchMessage& other) : blocksFromCommonRoot(other.blocksFromCommonRoot) {} + +void ChainSwitchMessage::get(std::vector& hashes) const { + hashes = blocksFromCommonRoot; +} + +BlockchainMessage::BlockchainMessage(NewBlockMessage&& message) : type(MessageType::NEW_BLOCK_MESSAGE), newBlockMessage(std::move(message)) {} + +BlockchainMessage::BlockchainMessage(NewAlternativeBlockMessage&& message) : type(MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE), newAlternativeBlockMessage(std::move(message)) {} + +BlockchainMessage::BlockchainMessage(ChainSwitchMessage&& message) : type(MessageType::CHAIN_SWITCH_MESSAGE) { + chainSwitchMessage = new ChainSwitchMessage(std::move(message)); +} + +BlockchainMessage::BlockchainMessage(const BlockchainMessage& other) : type(other.type) { + switch (type) { + case MessageType::NEW_BLOCK_MESSAGE: + new (&newBlockMessage) NewBlockMessage(other.newBlockMessage); + break; + case MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE: + new (&newAlternativeBlockMessage) NewAlternativeBlockMessage(other.newAlternativeBlockMessage); + break; + case MessageType::CHAIN_SWITCH_MESSAGE: + chainSwitchMessage = new ChainSwitchMessage(*other.chainSwitchMessage); + break; + } +} + +BlockchainMessage::~BlockchainMessage() { + switch (type) { + case MessageType::NEW_BLOCK_MESSAGE: + newBlockMessage.~NewBlockMessage(); + break; + case MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE: + newAlternativeBlockMessage.~NewAlternativeBlockMessage(); + break; + case MessageType::CHAIN_SWITCH_MESSAGE: + delete chainSwitchMessage; + break; + } +} + +BlockchainMessage::MessageType BlockchainMessage::getType() const { + return type; +} + +bool BlockchainMessage::getNewBlockHash(Crypto::Hash& hash) const { + if (type == MessageType::NEW_BLOCK_MESSAGE) { + newBlockMessage.get(hash); + return true; + } else { + return false; + } +} + +bool BlockchainMessage::getNewAlternativeBlockHash(Crypto::Hash& hash) const { + if (type == MessageType::NEW_ALTERNATIVE_BLOCK_MESSAGE) { + newAlternativeBlockMessage.get(hash); + return true; + } else { + return false; + } +} + +bool BlockchainMessage::getChainSwitch(std::vector& hashes) const { + if (type == MessageType::CHAIN_SWITCH_MESSAGE) { + chainSwitchMessage->get(hashes); + return true; + } else { + return false; + } +} + +} diff --git a/src/CryptoNoteCore/BlockchainMessages.h b/src/CryptoNoteCore/BlockchainMessages.h new file mode 100644 index 0000000000..544f9df4f9 --- /dev/null +++ b/src/CryptoNoteCore/BlockchainMessages.h @@ -0,0 +1,84 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include + +namespace CryptoNote { + +class NewBlockMessage { +public: + NewBlockMessage(const Crypto::Hash& hash); + NewBlockMessage() = default; + void get(Crypto::Hash& hash) const; +private: + Crypto::Hash blockHash; +}; + +class NewAlternativeBlockMessage { +public: + NewAlternativeBlockMessage(const Crypto::Hash& hash); + NewAlternativeBlockMessage() = default; + void get(Crypto::Hash& hash) const; +private: + Crypto::Hash blockHash; +}; + +class ChainSwitchMessage { +public: + ChainSwitchMessage(std::vector&& hashes); + ChainSwitchMessage(const ChainSwitchMessage& other); + void get(std::vector& hashes) const; +private: + std::vector blocksFromCommonRoot; +}; + +class BlockchainMessage { +public: + enum class MessageType { + NEW_BLOCK_MESSAGE, + NEW_ALTERNATIVE_BLOCK_MESSAGE, + CHAIN_SWITCH_MESSAGE + }; + + BlockchainMessage(NewBlockMessage&& message); + BlockchainMessage(NewAlternativeBlockMessage&& message); + BlockchainMessage(ChainSwitchMessage&& message); + + BlockchainMessage(const BlockchainMessage& other); + + ~BlockchainMessage(); + + MessageType getType() const; + + bool getNewBlockHash(Crypto::Hash& hash) const; + bool getNewAlternativeBlockHash(Crypto::Hash& hash) const; + bool getChainSwitch(std::vector& hashes) const; +private: + const MessageType type; + + union { + NewBlockMessage newBlockMessage; + NewAlternativeBlockMessage newAlternativeBlockMessage; + ChainSwitchMessage* chainSwitchMessage; + }; +}; + +} diff --git a/src/cryptonote_core/checkpoints.cpp b/src/CryptoNoteCore/Checkpoints.cpp similarity index 81% rename from src/cryptonote_core/checkpoints.cpp rename to src/CryptoNoteCore/Checkpoints.cpp index 4b48bbb4d9..e58da8637e 100644 --- a/src/cryptonote_core/checkpoints.cpp +++ b/src/CryptoNoteCore/Checkpoints.cpp @@ -15,17 +15,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "checkpoints.h" +#include "Checkpoints.h" #include "Common/StringTools.h" using namespace Logging; namespace CryptoNote { //--------------------------------------------------------------------------- -checkpoints::checkpoints(Logging::ILogger &log) : logger(log, "checkpoints") {} +Checkpoints::Checkpoints(Logging::ILogger &log) : logger(log, "checkpoints") {} //--------------------------------------------------------------------------- -bool checkpoints::add_checkpoint(uint64_t height, const std::string &hash_str) { - crypto::hash h = null_hash; +bool Checkpoints::add_checkpoint(uint32_t height, const std::string &hash_str) { + Crypto::Hash h = NULL_HASH; if (!Common::podFromHex(hash_str, h)) { logger(ERROR) << "WRONG HASH IN CHECKPOINTS!!!"; @@ -41,11 +41,11 @@ bool checkpoints::add_checkpoint(uint64_t height, const std::string &hash_str) { return true; } //--------------------------------------------------------------------------- -bool checkpoints::is_in_checkpoint_zone(uint64_t height) const { +bool Checkpoints::is_in_checkpoint_zone(uint32_t height) const { return !m_points.empty() && (height <= (--m_points.end())->first); } //--------------------------------------------------------------------------- -bool checkpoints::check_block(uint64_t height, const crypto::hash &h, +bool Checkpoints::check_block(uint32_t height, const Crypto::Hash &h, bool &is_a_checkpoint) const { auto it = m_points.find(height); is_a_checkpoint = it != m_points.end(); @@ -64,13 +64,13 @@ bool checkpoints::check_block(uint64_t height, const crypto::hash &h, } } //--------------------------------------------------------------------------- -bool checkpoints::check_block(uint64_t height, const crypto::hash &h) const { +bool Checkpoints::check_block(uint32_t height, const Crypto::Hash &h) const { bool ignored; return check_block(height, h, ignored); } //--------------------------------------------------------------------------- -bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, - uint64_t block_height) const { +bool Checkpoints::is_alternative_block_allowed(uint32_t blockchain_height, + uint32_t block_height) const { if (0 == block_height) return false; @@ -80,7 +80,7 @@ bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, return true; --it; - uint64_t checkpoint_height = it->first; + uint32_t checkpoint_height = it->first; return checkpoint_height < block_height; } } diff --git a/src/cryptonote_core/checkpoints.h b/src/CryptoNoteCore/Checkpoints.h old mode 100644 new mode 100755 similarity index 66% rename from src/cryptonote_core/checkpoints.h rename to src/CryptoNoteCore/Checkpoints.h index 0778f1a28a..0d1f3c4fda --- a/src/cryptonote_core/checkpoints.h +++ b/src/CryptoNoteCore/Checkpoints.h @@ -17,24 +17,24 @@ #pragma once #include -#include "cryptonote_basic_impl.h" +#include "CryptoNoteBasicImpl.h" #include namespace CryptoNote { - class checkpoints + class Checkpoints { public: - checkpoints(Logging::ILogger& log); + Checkpoints(Logging::ILogger& log); - bool add_checkpoint(uint64_t height, const std::string& hash_str); - bool is_in_checkpoint_zone(uint64_t height) const; - bool check_block(uint64_t height, const crypto::hash& h) const; - bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const; - bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const; + bool add_checkpoint(uint32_t height, const std::string& hash_str); + bool is_in_checkpoint_zone(uint32_t height) const; + bool check_block(uint32_t height, const Crypto::Hash& h) const; + bool check_block(uint32_t height, const Crypto::Hash& h, bool& is_a_checkpoint) const; + bool is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const; private: - std::map m_points; + std::map m_points; Logging::LoggerRef logger; }; } diff --git a/src/CryptoNoteCore/Core.cpp b/src/CryptoNoteCore/Core.cpp new file mode 100755 index 0000000000..3e51e7ccfe --- /dev/null +++ b/src/CryptoNoteCore/Core.cpp @@ -0,0 +1,1031 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "Core.h" + +#include +#include +#include "../CryptoNoteConfig.h" +#include "../Common/CommandLine.h" +#include "../Common/Util.h" +#include "../Common/StringTools.h" +#include "../crypto/crypto.h" +#include "../CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "../Logging/LoggerRef.h" +#include "../Rpc/CoreRpcServerCommandsDefinitions.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "CryptoNoteStatInfo.h" +#include "Miner.h" +#include "TransactionExtra.h" +#include "IBlock.h" +#undef ERROR + +using namespace Logging; +#include "CryptoNoteCore/CoreConfig.h" + +using namespace Common; + +namespace CryptoNote { + +class BlockWithTransactions : public IBlock { +public: + virtual const Block& getBlock() const override { + return block; + } + + virtual size_t getTransactionCount() const override { + return transactions.size(); + } + + virtual const Transaction& getTransaction(size_t index) const override { + assert(index < transactions.size()); + return transactions[index]; + } + +private: + Block block; + std::vector transactions; + + friend class core; +}; + +core::core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger) : +m_currency(currency), +logger(logger, "core"), +m_mempool(currency, m_blockchain, m_timeProvider, logger), +m_blockchain(currency, m_mempool, logger), +m_miner(new miner(currency, *this, logger)), +m_starter_message_showed(false) { + set_cryptonote_protocol(pprotocol); + m_blockchain.addObserver(this); + m_mempool.addObserver(this); + } + //----------------------------------------------------------------------------------------------- + core::~core() { + m_blockchain.removeObserver(this); +} + +void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { + if (pprotocol) + m_pprotocol = pprotocol; + else + m_pprotocol = &m_protocol_stub; +} +//----------------------------------------------------------------------------------- +void core::set_checkpoints(Checkpoints&& chk_pts) { + m_blockchain.setCheckpoints(std::move(chk_pts)); +} +//----------------------------------------------------------------------------------- +void core::init_options(boost::program_options::options_description& /*desc*/) { +} + +bool core::handle_command_line(const boost::program_options::variables_map& vm) { + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + return true; +} + +bool core::is_ready() { + return !m_blockchain.isStoringBlockchain(); +} + + +uint32_t core::get_current_blockchain_height() { + return m_blockchain.getCurrentBlockchainHeight(); +} + +void core::get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) { + assert(m_blockchain.getCurrentBlockchainHeight() > 0); + top_id = m_blockchain.getTailId(height); +} + +bool core::get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs) { + return m_blockchain.getBlocks(start_offset, count, blocks, txs); +} + +bool core::get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks) { + return m_blockchain.getBlocks(start_offset, count, blocks); +} +void core::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { + m_blockchain.getTransactions(txs_ids, txs, missed_txs, checkTxPool); +} + +bool core::get_alternative_blocks(std::list& blocks) { + return m_blockchain.getAlternativeBlocks(blocks); +} + +size_t core::get_alternative_blocks_count() { + return m_blockchain.getAlternativeBlocksCount(); + } + //----------------------------------------------------------------------------------------------- + bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { + m_config_folder = config.configFolder; + bool r = m_mempool.init(m_config_folder); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize memory pool"; return false; } + + r = m_blockchain.init(m_config_folder, load_existing); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } + + r = m_miner->init(minerConfig); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } + + return load_state_data(); +} + +bool core::set_genesis_block(const Block& b) { + return m_blockchain.resetAndSetGenesisBlock(b); +} + +bool core::load_state_data() { + // may be some code later + return true; +} + +bool core::deinit() { + m_miner->stop(); + m_mempool.deinit(); + m_blockchain.deinit(); + return true; +} + +size_t core::addChain(const std::vector& chain) { + size_t blocksCounter = 0; + + for (const IBlock* block : chain) { + bool allTransactionsAdded = true; + for (size_t txNumber = 0; txNumber < block->getTransactionCount(); ++txNumber) { + const Transaction& tx = block->getTransaction(txNumber); + + Crypto::Hash txHash = NULL_HASH; + size_t blobSize = 0; + getObjectHash(tx, txHash, blobSize); + tx_verification_context tvc = boost::value_initialized(); + + if (!handleIncomingTransaction(tx, txHash, blobSize, tvc, true)) { + logger(ERROR, BRIGHT_RED) << "core::addChain() failed to handle transaction " << txHash << " from block " << blocksCounter << "/" << chain.size(); + allTransactionsAdded = false; + break; + } + } + + if (!allTransactionsAdded) { + break; + } + + block_verification_context bvc = boost::value_initialized(); + m_blockchain.addNewBlock(block->getBlock(), bvc); + if (bvc.m_marked_as_orphaned || bvc.m_verifivation_failed) { + logger(ERROR, BRIGHT_RED) << "core::addChain() failed to handle incoming block " << get_block_hash(block->getBlock()) << + ", " << blocksCounter << "/" << chain.size(); + break; + } + + ++blocksCounter; + // TODO m_dispatcher.yield()? + } + + return blocksCounter; +} + +bool core::handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. + tvc = boost::value_initialized(); + //want to process all transactions sequentially + + if (tx_blob.size() > m_currency.maxTxSize()) { + logger(INFO) << "WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"; + tvc.m_verifivation_failed = true; + return false; + } + + Crypto::Hash tx_hash = NULL_HASH; + Crypto::Hash tx_prefixt_hash = NULL_HASH; + Transaction tx; + + if (!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to parse, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + //std::cout << "!"<< tx.inputs.size() << std::endl; + + return handleIncomingTransaction(tx, tx_hash, tx_blob.size(), tvc, keeped_by_block); +} + +bool core::get_stat_info(core_stat_info& st_inf) { + st_inf.mining_speed = m_miner->get_speed(); + st_inf.alternative_blocks = m_blockchain.getAlternativeBlocksCount(); + st_inf.blockchain_height = m_blockchain.getCurrentBlockchainHeight(); + st_inf.tx_pool_size = m_mempool.get_transactions_count(); + st_inf.top_block_id_str = Common::podToHex(m_blockchain.getTailId()); + return true; +} + + +bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { + if (!tx.inputs.size()) { + logger(ERROR) << "tx with empty inputs, rejected for tx id= " << getObjectHash(tx); + return false; + } + + if (!check_inputs_types_supported(tx)) { + logger(ERROR) << "unsupported input types for tx id= " << getObjectHash(tx); + return false; + } + + std::string errmsg; + if (!check_outs_valid(tx, &errmsg)) { + logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << getObjectHash(tx) << ": " << errmsg; + return false; + } + + if (!check_money_overflow(tx)) { + logger(ERROR) << "tx have money overflow, rejected for tx id= " << getObjectHash(tx); + return false; + } + + uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + uint64_t amount_out = get_outs_money_amount(tx); + + if (amount_in <= amount_out) { + logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << getObjectHash(tx); + return false; + } + + //check if tx use different key images + if (!check_tx_inputs_keyimages_diff(tx)) { + logger(ERROR) << "tx has a few inputs with identical keyimages"; + return false; + } + + if (!checkMultisignatureInputsDiff(tx)) { + logger(ERROR) << "tx has a few multisignature inputs with identical output indexes"; + return false; + } + + return true; +} + +bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { + std::unordered_set ki; + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } + } + return true; +} + +size_t core::get_blockchain_total_transactions() { + return m_blockchain.getTotalTransactions(); +} + +//bool core::get_outs(uint64_t amount, std::list& pkeys) +//{ +// return m_blockchain.get_outs(amount, pkeys); +//} + +bool core::add_new_tx(const Transaction& tx, const Crypto::Hash& tx_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) { + //Locking on m_mempool and m_blockchain closes possibility to add tx to memory pool which is already in blockchain + std::lock_guard lk(m_mempool); + LockedBlockchainStorage lbs(m_blockchain); + + if (m_blockchain.haveTransaction(tx_hash)) { + logger(TRACE) << "tx " << tx_hash << " is already in blockchain"; + return true; + } + + if (m_mempool.have_tx(tx_hash)) { + logger(TRACE) << "tx " << tx_hash << " is already in transaction pool"; + return true; + } + + return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); +} + +bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) { + size_t median_size; + uint64_t already_generated_coins; + + { + LockedBlockchainStorage blockchainLock(m_blockchain); + height = m_blockchain.getCurrentBlockchainHeight(); + diffic = m_blockchain.getDifficultyForNextBlock(); + if (!(diffic)) { + logger(ERROR, BRIGHT_RED) << "difficulty overhead."; + return false; + } + + b = boost::value_initialized(); + b.majorVersion = m_blockchain.getBlockMajorVersionForHeight(height); + + if (BLOCK_MAJOR_VERSION_1 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_1; + } else if (BLOCK_MAJOR_VERSION_2 == b.majorVersion) { + b.minorVersion = BLOCK_MINOR_VERSION_0; + + b.parentBlock.majorVersion = BLOCK_MAJOR_VERSION_1; + b.parentBlock.majorVersion = BLOCK_MINOR_VERSION_0; + b.parentBlock.transactionCount = 1; + TransactionExtraMergeMiningTag mm_tag = boost::value_initialized(); + + if (!appendMergeMiningTagToExtra(b.parentBlock.baseTransaction.extra, mm_tag)) { + logger(ERROR, BRIGHT_RED) << "Failed to append merge mining tag to extra of the parent block miner transaction"; + return false; + } + } + + b.previousBlockHash = get_tail_id(); + b.timestamp = time(NULL); + + median_size = m_blockchain.getCurrentCumulativeBlocksizeLimit() / 2; + already_generated_coins = m_blockchain.getCoinsInCirculation(); + } + + size_t txs_size; + uint64_t fee; + if (!m_mempool.fill_block_template(b, median_size, m_currency.maxBlockCumulativeSize(height), already_generated_coins, + txs_size, fee)) { + return false; + } + + /* + 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 + */ + //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size + bool penalizeFee = b.majorVersion > BLOCK_MAJOR_VERSION_1; + bool r = m_currency.constructMinerTx(height, median_size, already_generated_coins, txs_size, fee, adr, b.baseTransaction, ex_nonce, 11, penalizeFee); + if (!r) { + logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, first chance"; + return false; + } + + size_t cumulative_size = txs_size + getObjectBinarySize(b.baseTransaction); + for (size_t try_count = 0; try_count != 10; ++try_count) { + r = m_currency.constructMinerTx(height, median_size, already_generated_coins, cumulative_size, fee, adr, b.baseTransaction, ex_nonce, 11, penalizeFee); + + if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to construct miner tx, second chance"; return false; } + size_t coinbase_blob_size = getObjectBinarySize(b.baseTransaction); + if (coinbase_blob_size > cumulative_size - txs_size) { + cumulative_size = txs_size + coinbase_blob_size; + continue; + } + + if (coinbase_blob_size < cumulative_size - txs_size) { + size_t delta = cumulative_size - txs_size - coinbase_blob_size; + b.baseTransaction.extra.insert(b.baseTransaction.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 + getObjectBinarySize(b.baseTransaction)) { + if (!(cumulative_size + 1 == txs_size + getObjectBinarySize(b.baseTransaction))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.baseTransaction)=" << getObjectBinarySize(b.baseTransaction); return false; } + b.baseTransaction.extra.resize(b.baseTransaction.extra.size() - 1); + if (cumulative_size != txs_size + getObjectBinarySize(b.baseTransaction)) { + //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size + logger(TRACE, BRIGHT_RED) << + "Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1; + cumulative_size += delta - 1; + continue; + } + logger(DEBUGGING, BRIGHT_GREEN) << + "Setting extra for block: " << b.baseTransaction.extra.size() << ", try_count=" << try_count; + } + } + if (!(cumulative_size == txs_size + getObjectBinarySize(b.baseTransaction))) { logger(ERROR, BRIGHT_RED) << "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.baseTransaction)=" << getObjectBinarySize(b.baseTransaction); return false; } + return true; + } + + logger(ERROR, BRIGHT_RED) << + "Failed to create_block_template with " << 10 << " tries"; + return false; +} + +std::vector core::findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) { + + assert(!remoteBlockIds.empty()); + assert(remoteBlockIds.back() == m_blockchain.getBlockIdByHeight(0)); + + return m_blockchain.findBlockchainSupplement(remoteBlockIds, maxCount, totalBlockCount, startBlockIndex); +} + +void core::print_blockchain(uint32_t start_index, uint32_t end_index) { + m_blockchain.print_blockchain(start_index, end_index); +} + +void core::print_blockchain_index() { + m_blockchain.print_blockchain_index(); +} + +void core::print_blockchain_outs(const std::string& file) { + m_blockchain.print_blockchain_outs(file); +} + +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) { + return m_blockchain.getRandomOutsByAmount(req, res); +} + +bool core::get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) { + return m_blockchain.getTransactionOutputGlobalIndexes(tx_id, indexs); +} + +bool core::getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) { + return m_blockchain.get_out_by_msig_gindex(amount, gindex, out); +} + +void core::pause_mining() { + m_miner->pause(); +} + +void core::update_block_template_and_resume_mining() { + update_miner_block_template(); + m_miner->resume(); +} + +bool core::handle_block_found(Block& b) { + block_verification_context bvc = boost::value_initialized(); + handle_incoming_block(b, bvc, true, true); + + if (bvc.m_verifivation_failed) { + logger(ERROR) << "mined block failed verification"; + } + + return bvc.m_added_to_main_chain; +} + +void core::on_synchronized() { + m_miner->on_synchronized(); +} + +bool core::getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { + if (tailBlockId != m_blockchain.getTailId()) { + return false; + } + + getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); + return true; +} + +bool core::getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) { + std::vector added; + if (!getPoolChanges(tailBlockId, knownTxsIds, added, deletedTxsIds)) { + return false; + } + + for (const auto& tx: added) { + TransactionPrefixInfo tpi; + tpi.txPrefix = tx; + tpi.txHash = getObjectHash(tx); + + addedTxs.push_back(std::move(tpi)); + } + + return true; +} + +void core::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) { + std::vector addedTxsIds; + m_mempool.get_difference(knownTxsIds, addedTxsIds, deletedTxsIds); + std::vector misses; + m_mempool.getTransactions(addedTxsIds, addedTxs, misses); + assert(misses.empty()); +} + +bool core::handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (block_blob.size() > m_currency.maxBlockBlobSize()) { + logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; + bvc.m_verifivation_failed = true; + return false; + } + + Block b; + if (!fromBinaryArray(b, block_blob)) { + logger(INFO) << "Failed to parse and validate new block"; + bvc.m_verifivation_failed = true; + return false; + } + + return handle_incoming_block(b, bvc, control_miner, relay_block); +} + +bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { + if (control_miner) { + pause_mining(); + } + + m_blockchain.addNewBlock(b, bvc); + + if (control_miner) { + update_block_template_and_resume_mining(); + } + + if (relay_block && bvc.m_added_to_main_chain) { + std::list missed_txs; + std::list txs; + m_blockchain.getTransactions(b.transactionHashes, txs, missed_txs); + if (!missed_txs.empty() && getBlockIdByHeight(get_block_height(b)) != get_block_hash(b)) { + logger(INFO) << "Block added, but it seems that reorganize just happened after that, do not relay this block"; + } else { + if (!(txs.size() == b.transactionHashes.size() && missed_txs.empty())) { + logger(ERROR, BRIGHT_RED) << "can't find some transactions in found block:" << + get_block_hash(b) << " txs.size()=" << txs.size() << ", b.transactionHashes.size()=" << b.transactionHashes.size() << ", missed_txs.size()" << missed_txs.size(); return false; + } + + NOTIFY_NEW_BLOCK::request arg; + arg.hop = 0; + arg.current_blockchain_height = m_blockchain.getCurrentBlockchainHeight(); + BinaryArray blockBa; + bool r = toBinaryArray(b, blockBa); + if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to serialize block"; return false; } + arg.b.block = asString(blockBa); + for (auto& tx : txs) { + arg.b.txs.push_back(asString(toBinaryArray(tx))); + } + + m_pprotocol->relay_block(arg); + } + } + + return true; +} + +Crypto::Hash core::get_tail_id() { + return m_blockchain.getTailId(); +} + +size_t core::get_pool_transactions_count() { + return m_mempool.get_transactions_count(); +} + +bool core::have_block(const Crypto::Hash& id) { + return m_blockchain.haveBlock(id); +} + +bool core::parse_tx_from_blob(Transaction& tx, Crypto::Hash& tx_hash, Crypto::Hash& tx_prefix_hash, const BinaryArray& blob) { + return parseAndValidateTransactionFromBinaryArray(blob, tx, tx_hash, tx_prefix_hash); +} + +bool core::check_tx_syntax(const Transaction& tx) { + return true; +} + +std::vector core::getPoolTransactions() { + std::list txs; + m_mempool.get_transactions(txs); + + std::vector result; + for (auto& tx : txs) { + result.emplace_back(std::move(tx)); + } + return result; +} + +std::vector core::buildSparseChain() { + assert(m_blockchain.getCurrentBlockchainHeight() != 0); + return m_blockchain.buildSparseChain(); +} + +std::vector core::buildSparseChain(const Crypto::Hash& startBlockId) { + LockedBlockchainStorage lbs(m_blockchain); + assert(m_blockchain.haveBlock(startBlockId)); + return m_blockchain.buildSparseChain(startBlockId); +} + +bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { //Deprecated. Should be removed with CryptoNoteProtocolHandler. + return m_blockchain.handleGetObjects(arg, rsp); +} + +Crypto::Hash core::getBlockIdByHeight(uint32_t height) { + LockedBlockchainStorage lbs(m_blockchain); + if (height < m_blockchain.getCurrentBlockchainHeight()) { + return m_blockchain.getBlockIdByHeight(height); + } else { + return NULL_HASH; + } +} + +bool core::getBlockByHash(const Crypto::Hash &h, Block &blk) { + return m_blockchain.getBlockByHash(h, blk); +} + +bool core::getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) { + return m_blockchain.getBlockHeight(blockId, blockHeight); +} + +//void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { +// m_blockchain.get_all_known_block_ids(main, alt, invalid); +//} + +std::string core::print_pool(bool short_format) { + return m_mempool.print_pool(short_format); +} + +bool core::update_miner_block_template() { + m_miner->on_block_chain_update(); + return true; +} + +bool core::on_idle() { + if (!m_starter_message_showed) { + logger(INFO) << ENDL << "**********************************************************************" << ENDL + << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL + << ENDL + << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << ENDL + << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL + << "**********************************************************************"; + m_starter_message_showed = true; + } + + m_miner->on_idle(); + m_mempool.on_idle(); + return true; +} + +bool core::addObserver(ICoreObserver* observer) { + return m_observerManager.add(observer); +} + +bool core::removeObserver(ICoreObserver* observer) { + return m_observerManager.remove(observer); +} + +void core::blockchainUpdated() { + m_observerManager.notify(&ICoreObserver::blockchainUpdated); +} + +void core::txDeletedFromPool() { + poolUpdated(); +} + +void core::poolUpdated() { + m_observerManager.notify(&ICoreObserver::poolUpdated); +} + +bool core::queryBlocks(const std::vector& knownBlockIds, uint64_t timestamp, + uint32_t& resStartHeight, uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) { + + LockedBlockchainStorage lbs(m_blockchain); + + uint32_t currentHeight = lbs->getCurrentBlockchainHeight(); + uint32_t startOffset = 0; + uint32_t startFullOffset = 0; + + if (!findStartAndFullOffsets(knownBlockIds, timestamp, startOffset, startFullOffset)) { + return false; + } + + resFullOffset = startFullOffset; + std::vector blockIds = findIdsForShortBlocks(startOffset, startFullOffset); + entries.reserve(blockIds.size()); + + for (const auto& id : blockIds) { + entries.push_back(BlockFullInfo()); + entries.back().block_id = id; + } + + resCurrentHeight = currentHeight; + resStartHeight = startOffset; + + uint32_t blocksLeft = static_cast(std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT))); + + if (blocksLeft == 0) { + return true; + } + + std::list blocks; + lbs->getBlocks(startFullOffset, blocksLeft, blocks); + + for (auto& b : blocks) { + BlockFullInfo item; + + item.block_id = get_block_hash(b); + + if (b.timestamp >= timestamp) { + // query transactions + std::list txs; + std::list missedTxs; + lbs->getTransactions(b.transactionHashes, txs, missedTxs); + + // fill data + block_complete_entry& completeEntry = item; + completeEntry.block = asString(toBinaryArray(b)); + for (auto& tx : txs) { + completeEntry.txs.push_back(asString(toBinaryArray(tx))); + } + } + + entries.push_back(std::move(item)); + } + + return true; +} + +bool core::findStartAndFullOffsets(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& startOffset, uint32_t& startFullOffset) { + LockedBlockchainStorage lbs(m_blockchain); + + if (knownBlockIds.empty()) { + logger(ERROR, BRIGHT_RED) << "knownBlockIds is empty"; + return false; + } + + if (knownBlockIds.back() != m_blockchain.getBlockIdByHeight(0)) { + logger(ERROR, BRIGHT_RED) << "knownBlockIds doesn't end with genesis block hash: " << knownBlockIds.back(); + return false; + } + + startOffset = lbs->findBlockchainSupplement(knownBlockIds); + if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) { + startFullOffset = startOffset; + } + + return true; +} + +std::vector core::findIdsForShortBlocks(uint32_t startOffset, uint32_t startFullOffset) { + assert(startOffset <= startFullOffset); + + LockedBlockchainStorage lbs(m_blockchain); + + std::vector result; + if (startOffset < startFullOffset) { + result = lbs->getBlockIds(startOffset, std::min(static_cast(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset)); + } + + return result; +} + +bool core::queryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& resStartHeight, + uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) { + LockedBlockchainStorage lbs(m_blockchain); + + resCurrentHeight = lbs->getCurrentBlockchainHeight(); + resStartHeight = 0; + resFullOffset = 0; + + if (!findStartAndFullOffsets(knownBlockIds, timestamp, resStartHeight, resFullOffset)) { + return false; + } + + std::vector blockIds = findIdsForShortBlocks(resStartHeight, resFullOffset); + entries.reserve(blockIds.size()); + + for (const auto& id : blockIds) { + entries.push_back(BlockShortInfo()); + entries.back().blockId = id; + } + + uint32_t blocksLeft = static_cast(std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT))); + + if (blocksLeft == 0) { + return true; + } + + std::list blocks; + lbs->getBlocks(resFullOffset, blocksLeft, blocks); + + for (auto& b : blocks) { + BlockShortInfo item; + + item.blockId = get_block_hash(b); + + if (b.timestamp >= timestamp) { + std::list txs; + std::list missedTxs; + lbs->getTransactions(b.transactionHashes, txs, missedTxs); + + item.block = asString(toBinaryArray(b)); + + for (const auto& tx: txs) { + TransactionPrefixInfo info; + info.txPrefix = tx; + info.txHash = getObjectHash(tx); + + item.txPrefixes.push_back(std::move(info)); + } + } + + entries.push_back(std::move(item)); + } + + return true; +} + +bool core::getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) { + return m_blockchain.getBackwardBlocksSize(fromHeight, sizes, count); +} + +bool core::getBlockSize(const Crypto::Hash& hash, size_t& size) { + return m_blockchain.getBlockSize(hash, size); +} + +bool core::getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) { + return m_blockchain.getAlreadyGeneratedCoins(hash, generatedCoins); +} + +bool core::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { + return m_currency.getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange); +} + +bool core::scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) { + struct outputs_visitor + { + std::list>& m_resultsCollector; + outputs_visitor(std::list>& resultsCollector):m_resultsCollector(resultsCollector){} + bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) + { + m_resultsCollector.push_back(std::make_pair(getObjectHash(tx), transactionOutputIndex)); + return true; + } + }; + + outputs_visitor vi(outputReferences); + + return m_blockchain.scanOutputKeysForIndexes(txInToKey, vi); +} + +bool core::getBlockDifficulty(uint32_t height, difficulty_type& difficulty) { + difficulty = m_blockchain.blockDifficulty(height); + return true; +} + +bool core::getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) { + return m_blockchain.getBlockContainingTransaction(txId, blockId, blockHeight); +} + +bool core::getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) { + return m_blockchain.getMultisigOutputReference(txInMultisig, outputReference); +} + +bool core::getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) { + return m_blockchain.getGeneratedTransactionsNumber(height, generatedTransactions); +} + +bool core::getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) { + std::vector blockHashes; + if (!m_blockchain.getOrphanBlockIdsByHeight(height, blockHashes)) { + return false; + } + for (const Crypto::Hash& hash : blockHashes) { + Block blk; + if (!getBlockByHash(hash, blk)) { + return false; + } + blocks.push_back(std::move(blk)); + } + return true; +} + +bool core::getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { + std::vector blockHashes; + if (!m_blockchain.getBlockIdsByTimestamp(timestampBegin, timestampEnd, blocksNumberLimit, blockHashes, blocksNumberWithinTimestamps)) { + return false; + } + for (const Crypto::Hash& hash : blockHashes) { + Block blk; + if (!getBlockByHash(hash, blk)) { + return false; + } + blocks.push_back(std::move(blk)); + } + return true; +} + +bool core::getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { + std::vector poolTransactionHashes; + if (!m_mempool.getTransactionIdsByTimestamp(timestampBegin, timestampEnd, transactionsNumberLimit, poolTransactionHashes, transactionsNumberWithinTimestamps)) { + return false; + } + std::list txs; + std::list missed_txs; + + getTransactions(poolTransactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return false; + } + + transactions.insert(transactions.end(), txs.begin(), txs.end()); + return true; +} + +bool core::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) { + std::vector blockchainTransactionHashes; + if (!m_blockchain.getTransactionIdsByPaymentId(paymentId, blockchainTransactionHashes)) { + return false; + } + std::vector poolTransactionHashes; + if (!m_mempool.getTransactionIdsByPaymentId(paymentId, poolTransactionHashes)) { + return false; + } + std::list txs; + std::list missed_txs; + blockchainTransactionHashes.insert(blockchainTransactionHashes.end(), poolTransactionHashes.begin(), poolTransactionHashes.end()); + + getTransactions(blockchainTransactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return false; + } + + transactions.insert(transactions.end(), txs.begin(), txs.end()); + return true; +} + +std::error_code core::executeLocked(const std::function& func) { + std::lock_guard lk(m_mempool); + LockedBlockchainStorage lbs(m_blockchain); + + return func(); +} + +uint64_t core::getNextBlockDifficulty() { + return m_blockchain.getDifficultyForNextBlock(); +} + +uint64_t core::getTotalGeneratedAmount() { + return m_blockchain.getCoinsInCirculation(); +} + +bool core::handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { + if (!check_tx_syntax(tx)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " syntax, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + + if (!check_tx_semantic(tx, keptByBlock)) { + logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << txHash << " semantic, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + + bool r = add_new_tx(tx, txHash, blobSize, tvc, keptByBlock); + if (tvc.m_verifivation_failed) { + if (!tvc.m_tx_fee_too_small) { + logger(ERROR) << "Transaction verification failed: " << txHash; + } else { + logger(INFO) << "Transaction verification failed: " << txHash; + } + } else if (tvc.m_verifivation_impossible) { + logger(ERROR) << "Transaction verification impossible: " << txHash; + } + + if (tvc.m_added_to_pool) { + logger(DEBUGGING) << "tx added: " << txHash; + poolUpdated(); + } + + return r; +} + +std::unique_ptr core::getBlock(const Crypto::Hash& blockId) { + std::lock_guard lk(m_mempool); + LockedBlockchainStorage lbs(m_blockchain); + + std::unique_ptr blockPtr(new BlockWithTransactions()); + if (!lbs->getBlockByHash(blockId, blockPtr->block)) { + logger(DEBUGGING) << "Can't find block: " << blockId; + return std::unique_ptr(nullptr); + } + + blockPtr->transactions.reserve(blockPtr->block.transactionHashes.size()); + std::vector missedTxs; + lbs->getTransactions(blockPtr->block.transactionHashes, blockPtr->transactions, missedTxs, true); + assert(missedTxs.empty() || !lbs->isBlockInMainChain(blockId)); //if can't find transaction for blockchain block -> error + + if (!missedTxs.empty()) { + logger(DEBUGGING) << "Can't find transactions for block: " << blockId; + return std::unique_ptr(nullptr); + } + + return std::move(blockPtr); +} + +bool core::addMessageQueue(MessageQueue& messageQueue) { + return m_blockchain.addMessageQueue(messageQueue); +} + +bool core::removeMessageQueue(MessageQueue& messageQueue) { + return m_blockchain.removeMessageQueue(messageQueue); +} + +} diff --git a/src/CryptoNoteCore/Core.h b/src/CryptoNoteCore/Core.h new file mode 100755 index 0000000000..d880fd9bef --- /dev/null +++ b/src/CryptoNoteCore/Core.h @@ -0,0 +1,194 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include "P2p/NetNodeCommon.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h" +#include "Currency.h" +#include "TransactionPool.h" +#include "Blockchain.h" +#include "CryptoNoteCore/IMinerHandler.h" +#include "CryptoNoteCore/MinerConfig.h" +#include "ICore.h" +#include "ICoreObserver.h" +#include "Common/ObserverManager.h" + +#include "System/Dispatcher.h" +#include "CryptoNoteCore/MessageQueue.h" +#include "CryptoNoteCore/BlockchainMessages.h" + +#include + +namespace CryptoNote { + + struct core_stat_info; + class miner; + class CoreConfig; + + class core : public ICore, public IMinerHandler, public IBlockchainStorageObserver, public ITxPoolObserver { + public: + core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger); + ~core(); + + bool on_idle(); + virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block); //Deprecated. Should be removed with CryptoNoteProtocolHandler. + bool handle_incoming_block_blob(const BinaryArray& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); + virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + const Currency& currency() const { return m_currency; } + + //-------------------- IMinerHandler ----------------------- + virtual bool handle_block_found(Block& b); + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce); + + bool addObserver(ICoreObserver* observer); + bool removeObserver(ICoreObserver* observer); + + miner& get_miner() { return *m_miner; } + static void init_options(boost::program_options::options_description& desc); + bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing); + bool set_genesis_block(const Block& b); + bool deinit(); + + // ICore + virtual size_t addChain(const std::vector& chain) override; + virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) override; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + virtual bool getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) override; + virtual bool getBlockSize(const Crypto::Hash& hash, size_t& size) override; + virtual bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) override; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) override; + virtual bool scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) override; + virtual bool getBlockDifficulty(uint32_t height, difficulty_type& difficulty) override; + virtual bool getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) override; + virtual bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& output_reference) override; + virtual bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) override; + virtual bool getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) override; + virtual bool getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) override; + virtual bool getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) override; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) override; + virtual bool getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) override; + virtual std::unique_ptr getBlock(const Crypto::Hash& blocksId) override; + virtual bool handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) override; + virtual std::error_code executeLocked(const std::function& func) override; + + virtual bool addMessageQueue(MessageQueue& messageQueue) override; + virtual bool removeMessageQueue(MessageQueue& messageQueue) override; + + uint32_t get_current_blockchain_height(); + bool have_block(const Crypto::Hash& id); + std::vector buildSparseChain() override; + std::vector buildSparseChain(const Crypto::Hash& startBlockId) override; + void on_synchronized(); + bool is_ready() override; + + virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id); + bool get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks, std::list& txs); + bool get_blocks(uint32_t start_offset, uint32_t count, std::list& blocks); + template + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + { + return m_blockchain.getBlocks(block_ids, blocks, missed_bs); + } + virtual bool queryBlocks(const std::vector& block_ids, uint64_t timestamp, + uint32_t& start_height, uint32_t& current_height, uint32_t& full_offset, std::vector& entries) override; + virtual bool queryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, + uint32_t& resStartHeight, uint32_t& resCurrentHeight, uint32_t& resFullOffset, std::vector& entries) override; + virtual Crypto::Hash getBlockIdByHeight(uint32_t height) override; + void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) override; + virtual bool getBlockByHash(const Crypto::Hash &h, Block &blk) override; + virtual bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) override; + //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + + bool get_alternative_blocks(std::list& blocks); + size_t get_alternative_blocks_count(); + + void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); + void set_checkpoints(Checkpoints&& chk_pts); + + std::vector getPoolTransactions() override; + size_t get_pool_transactions_count(); + size_t get_blockchain_total_transactions(); + //bool get_outs(uint64_t amount, std::list& pkeys); + virtual std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) override; + bool get_stat_info(core_stat_info& st_inf); + + virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs); + Crypto::Hash get_tail_id(); + virtual 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); + void pause_mining(); + void update_block_template_and_resume_mining(); + //Blockchain& get_blockchain_storage(){return m_blockchain;} + //debug functions + void print_blockchain(uint32_t start_index, uint32_t end_index); + void print_blockchain_index(); + std::string print_pool(bool short_format); + void print_blockchain_outs(const std::string& file); + virtual bool getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) override; + virtual bool getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) override; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) override; + + uint64_t getNextBlockDifficulty(); + uint64_t getTotalGeneratedAmount(); + + private: + bool add_new_tx(const Transaction& tx, const Crypto::Hash& tx_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); + bool load_state_data(); + bool parse_tx_from_blob(Transaction& tx, Crypto::Hash& tx_hash, Crypto::Hash& tx_prefix_hash, const BinaryArray& blob); + bool handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block); + + bool check_tx_syntax(const Transaction& tx); + //check correct values, amounts and all lightweight checks not related with database + bool check_tx_semantic(const Transaction& tx, bool keeped_by_block); + //check if tx already in memory pool or in main blockchain + + bool is_key_image_spent(const Crypto::KeyImage& key_im); + + bool check_tx_ring_signature(const KeyInput& tx, const Crypto::Hash& tx_prefix_hash, const std::vector& sig); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool update_miner_block_template(); + bool handle_command_line(const boost::program_options::variables_map& vm); + bool on_update_blocktemplate_interval(); + bool check_tx_inputs_keyimages_diff(const Transaction& tx); + virtual void blockchainUpdated() override; + virtual void txDeletedFromPool() override; + void poolUpdated(); + + bool findStartAndFullOffsets(const std::vector& knownBlockIds, uint64_t timestamp, uint32_t& startOffset, uint32_t& startFullOffset); + std::vector findIdsForShortBlocks(uint32_t startOffset, uint32_t startFullOffset); + + const Currency& m_currency; + Logging::LoggerRef logger; + CryptoNote::RealTimeProvider m_timeProvider; + tx_memory_pool m_mempool; + Blockchain m_blockchain; + i_cryptonote_protocol* m_pprotocol; + std::unique_ptr m_miner; + std::string m_config_folder; + cryptonote_protocol_stub m_protocol_stub; + friend class tx_validate_inputs; + std::atomic m_starter_message_showed; + Tools::ObserverManager m_observerManager; + }; +} diff --git a/src/cryptonote_core/CoreConfig.cpp b/src/CryptoNoteCore/CoreConfig.cpp old mode 100644 new mode 100755 similarity index 91% rename from src/cryptonote_core/CoreConfig.cpp rename to src/CryptoNoteCore/CoreConfig.cpp index b4deb2fbe5..9bcf5239f4 --- a/src/cryptonote_core/CoreConfig.cpp +++ b/src/CryptoNoteCore/CoreConfig.cpp @@ -17,13 +17,13 @@ #include "CoreConfig.h" -#include "Common/util.h" -#include "Common/command_line.h" +#include "Common/Util.h" +#include "Common/CommandLine.h" namespace CryptoNote { CoreConfig::CoreConfig() { - configFolder = tools::get_default_data_dir(); + configFolder = Tools::getDefaultDataDirectory(); } void CoreConfig::init(const boost::program_options::variables_map& options) { diff --git a/src/cryptonote_core/CoreConfig.h b/src/CryptoNoteCore/CoreConfig.h similarity index 100% rename from src/cryptonote_core/CoreConfig.h rename to src/CryptoNoteCore/CoreConfig.h diff --git a/src/payment_service/WalletServiceErrorCodes.cpp b/src/CryptoNoteCore/CryptoNoteBasic.cpp old mode 100644 new mode 100755 similarity index 79% rename from src/payment_service/WalletServiceErrorCodes.cpp rename to src/CryptoNoteCore/CryptoNoteBasic.cpp index 61ef02f12a..6a56862ca1 --- a/src/payment_service/WalletServiceErrorCodes.cpp +++ b/src/CryptoNoteCore/CryptoNoteBasic.cpp @@ -15,12 +15,15 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletServiceErrorCodes.h" +#include "CryptoNoteBasic.h" +#include "crypto/crypto.h" -namespace PaymentService { -namespace error { +namespace CryptoNote { -WalletServiceErrorCategory WalletServiceErrorCategory::INSTANCE; +KeyPair generateKeyPair() { + KeyPair k; + Crypto::generate_keys(k.publicKey, k.secretKey); + return k; +} -} //namespace error -} //namespace PaymentService +} diff --git a/src/CryptoNoteCore/CryptoNoteBasic.h b/src/CryptoNoteCore/CryptoNoteBasic.h new file mode 100755 index 0000000000..2230a30965 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteBasic.h @@ -0,0 +1,46 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +namespace CryptoNote { + const Crypto::Hash NULL_HASH = boost::value_initialized(); + const Crypto::PublicKey NULL_PUBLIC_KEY = boost::value_initialized(); + + KeyPair generateKeyPair(); + + struct ParentBlockSerializer { + ParentBlockSerializer(ParentBlock& parentBlock, uint64_t& timestamp, uint32_t& nonce, bool hashingSerialization, bool headerOnly) : + m_parentBlock(parentBlock), m_timestamp(timestamp), m_nonce(nonce), m_hashingSerialization(hashingSerialization), m_headerOnly(headerOnly) { + } + + ParentBlock& m_parentBlock; + uint64_t& m_timestamp; + uint32_t& m_nonce; + bool m_hashingSerialization; + bool m_headerOnly; + }; + + inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly) { + Block& blockRef = const_cast(b); + return ParentBlockSerializer(blockRef.parentBlock, blockRef.timestamp, blockRef.nonce, hashingSerialization, headerOnly); + } + +} diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/CryptoNoteCore/CryptoNoteBasicImpl.cpp similarity index 76% rename from src/cryptonote_core/cryptonote_basic_impl.cpp rename to src/CryptoNoteCore/CryptoNoteBasicImpl.cpp index 567333a536..8192b26dc0 100644 --- a/src/cryptonote_core/cryptonote_basic_impl.cpp +++ b/src/CryptoNoteCore/CryptoNoteBasicImpl.cpp @@ -15,18 +15,22 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_basic_impl.h" -#include "serialization/binary_utils.h" -#include "serialization/vector.h" -#include "cryptonote_format_utils.h" -#include "Common/base58.h" +#include "CryptoNoteBasicImpl.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "CryptoNoteSerialization.h" + +#include "Common/Base58.h" #include "crypto/hash.h" #include "Common/int-util.h" +using namespace Crypto; +using namespace Common; + namespace CryptoNote { /************************************************************************/ - /* Cryptonote helper functions */ + /* CryptoNote helper functions */ /************************************************************************/ //----------------------------------------------------------------------------------------------- uint64_t getPenalizedAmount(uint64_t amount, size_t medianSize, size_t currentBlockSize) { @@ -58,18 +62,18 @@ namespace CryptoNote { } //----------------------------------------------------------------------- std::string getAccountAddressAsStr(uint64_t prefix, const AccountPublicAddress& adr) { - blobdata blob; - bool r = t_serializable_object_to_blob(adr, blob); + BinaryArray ba; + bool r = toBinaryArray(adr, ba); assert(r); - return tools::base58::encode_addr(prefix, blob); + return Tools::Base58::encode_addr(prefix, Common::asString(ba)); } //----------------------------------------------------------------------- bool is_coinbase(const Transaction& tx) { - if(tx.vin.size() != 1) { + if(tx.inputs.size() != 1) { return false; } - if(tx.vin[0].type() != typeid(TransactionInputGenerate)) { + if(tx.inputs[0].type() != typeid(BaseInput)) { return false; } @@ -77,17 +81,18 @@ namespace CryptoNote { } //----------------------------------------------------------------------- bool parseAccountAddressString(uint64_t& prefix, AccountPublicAddress& adr, const std::string& str) { - blobdata data; + std::string data; return - tools::base58::decode_addr(str, prefix, data) && - ::serialization::parse_binary(data, adr) && - crypto::check_key(adr.m_spendPublicKey) && - crypto::check_key(adr.m_viewPublicKey); + Tools::Base58::decode_addr(str, prefix, data) && + fromBinaryArray(adr, asBinaryArray(data)) && + // ::serialization::parse_binary(data, adr) && + check_key(adr.spendPublicKey) && + check_key(adr.viewPublicKey); } //----------------------------------------------------------------------- bool operator ==(const CryptoNote::Transaction& a, const CryptoNote::Transaction& b) { - return CryptoNote::get_transaction_hash(a) == CryptoNote::get_transaction_hash(b); + return getObjectHash(a) == getObjectHash(b); } //----------------------------------------------------------------------- bool operator ==(const CryptoNote::Block& a, const CryptoNote::Block& b) { @@ -96,10 +101,6 @@ namespace CryptoNote { } //-------------------------------------------------------------------------------- -bool parse_hash256(const std::string& str_hash, crypto::hash& hash) { - if (!Common::podFromHex(str_hash, hash)) { - std::cout << "invalid hash format: <" << str_hash << '>' << std::endl; - return false; - } - return true; +bool parse_hash256(const std::string& str_hash, Crypto::Hash& hash) { + return Common::podFromHex(str_hash, hash); } diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/CryptoNoteCore/CryptoNoteBasicImpl.h old mode 100644 new mode 100755 similarity index 68% rename from src/cryptonote_core/cryptonote_basic_impl.h rename to src/CryptoNoteCore/CryptoNoteBasicImpl.h index ed7b936994..3dce926246 --- a/src/cryptonote_core/cryptonote_basic_impl.h +++ b/src/CryptoNoteCore/CryptoNoteBasicImpl.h @@ -20,7 +20,7 @@ #include "Common/StringTools.h" #include "crypto/crypto.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" namespace CryptoNote { @@ -28,16 +28,16 @@ namespace CryptoNote { /* */ /************************************************************************/ template - struct array_hasher: std::unary_function + struct array_hasher: std::unary_function { - std::size_t operator()(const t_array& val) const + size_t operator()(const t_array& val) const { return boost::hash_range(&val.data[0], &val.data[sizeof(val.data)]); } }; /************************************************************************/ - /* Cryptonote helper functions */ + /* CryptoNote helper functions */ /************************************************************************/ uint64_t getPenalizedAmount(uint64_t amount, size_t medianSize, size_t currentBlockSize); std::string getAccountAddressAsStr(uint64_t prefix, const AccountPublicAddress& adr); @@ -53,13 +53,13 @@ std::ostream &print256(std::ostream &o, const T &v) { return o << "<" << Common::podToHex(v) << ">"; } -bool parse_hash256(const std::string& str_hash, crypto::hash& hash); +bool parse_hash256(const std::string& str_hash, Crypto::Hash& hash); -namespace crypto { - inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } - inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } +namespace Crypto { + inline std::ostream &operator <<(std::ostream &o, const Crypto::PublicKey &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::SecretKey &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::KeyDerivation &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::KeyImage &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::Signature &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const Crypto::Hash &v) { return print256(o, v); } } diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp new file mode 100644 index 0000000000..b60c6a96ea --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.cpp @@ -0,0 +1,550 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteFormatUtils.h" + +#include +#include +#include + +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +#include "Account.h" +#include "CryptoNoteBasicImpl.h" +#include "CryptoNoteSerialization.h" +#include "TransactionExtra.h" +#include "CryptoNoteTools.h" + +#include "CryptoNoteConfig.h" + +using namespace Logging; +using namespace Crypto; +using namespace Common; + +namespace CryptoNote { + +bool parseAndValidateTransactionFromBinaryArray(const BinaryArray& tx_blob, Transaction& tx, Hash& tx_hash, Hash& tx_prefix_hash) { + if (!fromBinaryArray(tx, tx_blob)) { + return false; + } + + //TODO: validate tx + cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + getObjectHash(*static_cast(&tx), tx_prefix_hash); + return true; +} + +bool generate_key_image_helper(const AccountKeys& ack, const PublicKey& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, KeyImage& ki) { + KeyDerivation recv_derivation; + bool r = generate_key_derivation(tx_public_key, ack.viewSecretKey, recv_derivation); + + assert(r && "key image helper: failed to generate_key_derivation"); + + if (!r) { + return false; + } + + r = derive_public_key(recv_derivation, real_output_index, ack.address.spendPublicKey, in_ephemeral.publicKey); + + assert(r && "key image helper: failed to derive_public_key"); + + if (!r) { + return false; + } + + derive_secret_key(recv_derivation, real_output_index, ack.spendSecretKey, in_ephemeral.secretKey); + generate_key_image(in_ephemeral.publicKey, in_ephemeral.secretKey, ki); + return true; +} + +uint64_t power_integral(uint64_t a, uint64_t b) { + if (b == 0) + return 1; + uint64_t total = a; + for (uint64_t i = 1; i != b; i++) + total *= a; + return total; +} + +bool get_tx_fee(const Transaction& tx, uint64_t & fee) { + uint64_t amount_in = 0; + uint64_t amount_out = 0; + + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + amount_in += boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount_in += boost::get(in).amount; + } + } + + for (const auto& o : tx.outputs) { + amount_out += o.amount; + } + + if (!(amount_in >= amount_out)) { + return false; + } + + fee = amount_in - amount_out; + return true; +} + +uint64_t get_tx_fee(const Transaction& tx) { + uint64_t r = 0; + if (!get_tx_fee(tx, r)) + return 0; + return r; +} + + +bool constructTransaction( + const AccountKeys& sender_account_keys, + const std::vector& sources, + const std::vector& destinations, + std::vector extra, + Transaction& tx, + uint64_t unlock_time, + Logging::ILogger& log) { + LoggerRef logger(log, "construct_tx"); + + tx.inputs.clear(); + tx.outputs.clear(); + tx.signatures.clear(); + + tx.version = CURRENT_TRANSACTION_VERSION; + tx.unlockTime = unlock_time; + + tx.extra = extra; + KeyPair txkey = generateKeyPair(); + addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey); + + struct input_generation_context_data { + KeyPair in_ephemeral; + }; + + std::vector in_contexts; + uint64_t summary_inputs_money = 0; + //fill inputs + for (const TransactionSourceEntry& src_entr : sources) { + if (src_entr.realOutput >= src_entr.outputs.size()) { + logger(ERROR) << "real_output index (" << src_entr.realOutput << ")bigger than output_keys.size()=" << src_entr.outputs.size(); + return false; + } + summary_inputs_money += src_entr.amount; + + //KeyDerivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; + KeyImage img; + if (!generate_key_image_helper(sender_account_keys, src_entr.realTransactionPublicKey, src_entr.realOutputIndexInTransaction, in_ephemeral, img)) + return false; + + //check that derived key is equal with real output key + if (!(in_ephemeral.publicKey == src_entr.outputs[src_entr.realOutput].second)) { + logger(ERROR) << "derived public key mismatch with output public key! " << ENDL << "derived_key:" + << Common::podToHex(in_ephemeral.publicKey) << ENDL << "real output_public_key:" + << Common::podToHex(src_entr.outputs[src_entr.realOutput].second); + return false; + } + + //put key image into tx input + KeyInput input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.keyImage = img; + + //fill outputs array and use relative offsets + for (const TransactionSourceEntry::OutputEntry& out_entry : src_entr.outputs) { + input_to_key.outputIndexes.push_back(out_entry.first); + } + + input_to_key.outputIndexes = absolute_output_offsets_to_relative(input_to_key.outputIndexes); + tx.inputs.push_back(input_to_key); + } + + // "Shuffle" outs + std::vector shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const TransactionDestinationEntry& de1, const TransactionDestinationEntry& de2) { return de1.amount < de2.amount; }); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + for (const TransactionDestinationEntry& dst_entr : shuffled_dsts) { + if (!(dst_entr.amount > 0)) { + logger(ERROR, BRIGHT_RED) << "Destination with wrong amount: " << dst_entr.amount; + return false; + } + KeyDerivation derivation; + PublicKey out_eph_public_key; + bool r = generate_key_derivation(dst_entr.addr.viewPublicKey, txkey.secretKey, derivation); + + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "at creation outs: failed to generate_key_derivation(" + << dst_entr.addr.viewPublicKey << ", " << txkey.secretKey << ")"; + return false; + } + + r = derive_public_key(derivation, output_index, + dst_entr.addr.spendPublicKey, + out_eph_public_key); + if (!(r)) { + logger(ERROR, BRIGHT_RED) + << "at creation outs: failed to derive_public_key(" << derivation + << ", " << output_index << ", " << dst_entr.addr.spendPublicKey + << ")"; + return false; + } + + TransactionOutput out; + out.amount = dst_entr.amount; + KeyOutput tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.outputs.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; + } + + //check money + if (summary_outs_money > summary_inputs_money) { + logger(ERROR) << "Transaction inputs money (" << summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"; + return false; + } + + //generate ring signatures + Hash tx_prefix_hash; + getObjectHash(*static_cast(&tx), tx_prefix_hash); + + size_t i = 0; + for (const TransactionSourceEntry& src_entr : sources) { + std::vector keys_ptrs; + for (const TransactionSourceEntry::OutputEntry& o : src_entr.outputs) { + keys_ptrs.push_back(&o.second); + } + + tx.signatures.push_back(std::vector()); + std::vector& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + generate_ring_signature(tx_prefix_hash, boost::get(tx.inputs[i]).keyImage, keys_ptrs, + in_contexts[i].in_ephemeral.secretKey, src_entr.realOutput, sigs.data()); + i++; + } + + return true; +} + +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { + money = 0; + + for (const auto& in : tx.inputs) { + uint64_t amount = 0; + + if (in.type() == typeid(KeyInput)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount = boost::get(in).amount; + } + + money += amount; + } + return true; +} + +uint32_t get_block_height(const Block& b) { + if (b.baseTransaction.inputs.size() != 1) { + return 0; + } + const auto& in = b.baseTransaction.inputs[0]; + if (in.type() != typeid(BaseInput)) { + return 0; + } + return boost::get(in).blockIndex; +} + +bool check_inputs_types_supported(const TransactionPrefix& tx) { + for (const auto& in : tx.inputs) { + if (in.type() != typeid(KeyInput) && in.type() != typeid(MultisignatureInput)) { + return false; + } + } + + return true; +} + +bool check_outs_valid(const TransactionPrefix& tx, std::string* error) { + for (const TransactionOutput& out : tx.outputs) { + if (out.target.type() == typeid(KeyOutput)) { + if (out.amount == 0) { + if (error) { + *error = "Zero amount ouput"; + } + return false; + } + + if (!check_key(boost::get(out.target).key)) { + if (error) { + *error = "Output with invalid key"; + } + return false; + } + } else if (out.target.type() == typeid(MultisignatureOutput)) { + const MultisignatureOutput& multisignatureOutput = ::boost::get(out.target); + if (multisignatureOutput.requiredSignatureCount > multisignatureOutput.keys.size()) { + if (error) { + *error = "Multisignature output with invalid required signature count"; + } + return false; + } + for (const PublicKey& key : multisignatureOutput.keys) { + if (!check_key(key)) { + if (error) { + *error = "Multisignature output with invalid public key"; + } + return false; + } + } + } else { + if (error) { + *error = "Output with invalid type"; + } + return false; + } + } + + return true; +} + +bool checkMultisignatureInputsDiff(const TransactionPrefix& tx) { + std::set> inputsUsage; + for (const auto& inv : tx.inputs) { + if (inv.type() == typeid(MultisignatureInput)) { + const MultisignatureInput& in = ::boost::get(inv); + if (!inputsUsage.insert(std::make_pair(in.amount, in.outputIndex)).second) { + return false; + } + } + } + return true; +} + +bool check_money_overflow(const TransactionPrefix &tx) { + return check_inputs_overflow(tx) && check_outs_overflow(tx); +} + +bool check_inputs_overflow(const TransactionPrefix &tx) { + uint64_t money = 0; + + for (const auto &in : tx.inputs) { + uint64_t amount = 0; + + if (in.type() == typeid(KeyInput)) { + amount = boost::get(in).amount; + } else if (in.type() == typeid(MultisignatureInput)) { + amount = boost::get(in).amount; + } + + if (money > amount + money) + return false; + + money += amount; + } + return true; +} + +bool check_outs_overflow(const TransactionPrefix& tx) { + uint64_t money = 0; + for (const auto& o : tx.outputs) { + if (money > o.amount + money) + return false; + money += o.amount; + } + return true; +} + +uint64_t get_outs_money_amount(const Transaction& tx) { + uint64_t outputs_amount = 0; + for (const auto& o : tx.outputs) { + outputs_amount += o.amount; + } + return outputs_amount; +} + +std::string short_hash_str(const Hash& h) { + std::string res = Common::podToHex(h); + + if (res.size() == 64) { + auto erased_pos = res.erase(8, 48); + res.insert(8, "...."); + } + + return res; +} + +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const KeyDerivation& derivation, size_t keyIndex) { + PublicKey pk; + derive_public_key(derivation, keyIndex, acc.address.spendPublicKey, pk); + return pk == out_key.key; +} + +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const PublicKey& tx_pub_key, size_t keyIndex) { + KeyDerivation derivation; + generate_key_derivation(tx_pub_key, acc.viewSecretKey, derivation); + return is_out_to_acc(acc, out_key, derivation, keyIndex); +} + +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) { + PublicKey transactionPublicKey = getTransactionPublicKeyFromExtra(tx.extra); + if (transactionPublicKey == NULL_PUBLIC_KEY) + return false; + return lookup_acc_outs(acc, tx, transactionPublicKey, outs, money_transfered); +} + +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, const PublicKey& tx_pub_key, std::vector& outs, uint64_t& money_transfered) { + money_transfered = 0; + size_t keyIndex = 0; + size_t outputIndex = 0; + + KeyDerivation derivation; + generate_key_derivation(tx_pub_key, acc.viewSecretKey, derivation); + + for (const TransactionOutput& o : tx.outputs) { + assert(o.target.type() == typeid(KeyOutput) || o.target.type() == typeid(MultisignatureOutput)); + if (o.target.type() == typeid(KeyOutput)) { + if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { + outs.push_back(outputIndex); + money_transfered += o.amount; + } + + ++keyIndex; + } else if (o.target.type() == typeid(MultisignatureOutput)) { + keyIndex += boost::get(o.target).keys.size(); + } + + ++outputIndex; + } + return true; +} + +bool get_block_hashing_blob(const Block& b, BinaryArray& ba) { + if (!toBinaryArray(static_cast(b), ba)) { + return false; + } + + Hash treeRootHash = get_tx_tree_hash(b); + ba.insert(ba.end(), treeRootHash.data, treeRootHash.data + 32); + auto transactionCount = asBinaryArray(Tools::get_varint_data(b.transactionHashes.size() + 1)); + ba.insert(ba.end(), transactionCount.begin(), transactionCount.end()); + return true; +} + +bool get_parent_block_hashing_blob(const Block& b, BinaryArray& blob) { + auto serializer = makeParentBlockSerializer(b, true, true); + return toBinaryArray(serializer, blob); +} + +bool get_block_hash(const Block& b, Hash& res) { + BinaryArray ba; + if (!get_block_hashing_blob(b, ba)) { + return false; + } + + if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { + BinaryArray parent_blob; + auto serializer = makeParentBlockSerializer(b, true, false); + if (!toBinaryArray(serializer, parent_blob)) + return false; + + ba.insert(ba.end(), parent_blob.begin(), parent_blob.end()); + } + + return getObjectHash(ba, res); +} + +Hash get_block_hash(const Block& b) { + Hash p = NULL_HASH; + get_block_hash(b, p); + return p; +} + +bool get_aux_block_header_hash(const Block& b, Hash& res) { + BinaryArray blob; + if (!get_block_hashing_blob(b, blob)) { + return false; + } + + return getObjectHash(blob, res); +} + +bool get_block_longhash(cn_context &context, const Block& b, Hash& res) { + BinaryArray bd; + if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { + if (!get_block_hashing_blob(b, bd)) { + return false; + } + } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { + if (!get_parent_block_hashing_blob(b, bd)) { + return false; + } + } else { + return false; + } + cn_slow_hash(context, bd.data(), bd.size(), res); + return true; +} + +std::vector relative_output_offsets_to_absolute(const std::vector& off) { + std::vector res = off; + for (size_t i = 1; i < res.size(); i++) + res[i] += res[i - 1]; + return res; +} + +std::vector absolute_output_offsets_to_relative(const std::vector& off) { + std::vector res = off; + if (!off.size()) + return res; + std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + for (size_t i = res.size() - 1; i != 0; i--) + res[i] -= res[i - 1]; + + return res; +} + +void get_tx_tree_hash(const std::vector& tx_hashes, Hash& h) { + tree_hash(tx_hashes.data(), tx_hashes.size(), h); +} + +Hash get_tx_tree_hash(const std::vector& tx_hashes) { + Hash h = NULL_HASH; + get_tx_tree_hash(tx_hashes, h); + return h; +} + +Hash get_tx_tree_hash(const Block& b) { + std::vector txs_ids; + Hash h = NULL_HASH; + getObjectHash(b.baseTransaction, h); + txs_ids.push_back(h); + for (auto& th : b.transactionHashes) { + txs_ids.push_back(th); + } + return get_tx_tree_hash(txs_ids); +} + +} diff --git a/src/CryptoNoteCore/CryptoNoteFormatUtils.h b/src/CryptoNoteCore/CryptoNoteFormatUtils.h new file mode 100755 index 0000000000..7809a0d61f --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteFormatUtils.h @@ -0,0 +1,128 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "CryptoNoteBasic.h" +#include "CryptoNoteSerialization.h" + +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +namespace Logging { +class ILogger; +} + +namespace CryptoNote { + +bool parseAndValidateTransactionFromBinaryArray(const BinaryArray& transactionBinaryArray, Transaction& transaction, Crypto::Hash& transactionHash, Crypto::Hash& transactionPrefixHash); + +struct TransactionSourceEntry { + typedef std::pair OutputEntry; + + std::vector outputs; //index + key + size_t realOutput; //index in outputs vector of real output_entry + Crypto::PublicKey realTransactionPublicKey; //incoming real tx public key + size_t realOutputIndexInTransaction; //index in transaction outputs vector + uint64_t amount; //money +}; + +struct TransactionDestinationEntry { + uint64_t amount; //money + AccountPublicAddress addr; //destination address + + TransactionDestinationEntry() : amount(0), addr(boost::value_initialized()) {} + TransactionDestinationEntry(uint64_t amount, const AccountPublicAddress &addr) : amount(amount), addr(addr) {} +}; + + +bool constructTransaction( + const AccountKeys& senderAccountKeys, + const std::vector& sources, + const std::vector& destinations, + std::vector extra, Transaction& transaction, uint64_t unlock_time, Logging::ILogger& log); + + +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const Crypto::PublicKey& tx_pub_key, size_t keyIndex); +bool is_out_to_acc(const AccountKeys& acc, const KeyOutput& out_key, const Crypto::KeyDerivation& derivation, size_t keyIndex); +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, const Crypto::PublicKey& tx_pub_key, std::vector& outs, uint64_t& money_transfered); +bool lookup_acc_outs(const AccountKeys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); +bool get_tx_fee(const Transaction& tx, uint64_t & fee); +uint64_t get_tx_fee(const Transaction& tx); +bool generate_key_image_helper(const AccountKeys& ack, const Crypto::PublicKey& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, Crypto::KeyImage& ki); +std::string short_hash_str(const Crypto::Hash& h); + +bool get_block_hashing_blob(const Block& b, BinaryArray& blob); +bool get_parent_block_hashing_blob(const Block& b, BinaryArray& blob); +bool get_aux_block_header_hash(const Block& b, Crypto::Hash& res); +bool get_block_hash(const Block& b, Crypto::Hash& res); +Crypto::Hash get_block_hash(const Block& b); +bool get_block_longhash(Crypto::cn_context &context, const Block& b, Crypto::Hash& res); +bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); +uint64_t get_outs_money_amount(const Transaction& tx); +bool check_inputs_types_supported(const TransactionPrefix& tx); +bool check_outs_valid(const TransactionPrefix& tx, std::string* error = 0); +bool checkMultisignatureInputsDiff(const TransactionPrefix& tx); + +bool check_money_overflow(const TransactionPrefix& tx); +bool check_outs_overflow(const TransactionPrefix& tx); +bool check_inputs_overflow(const TransactionPrefix& tx); +uint32_t get_block_height(const Block& b); +std::vector relative_output_offsets_to_absolute(const std::vector& off); +std::vector absolute_output_offsets_to_relative(const std::vector& off); + + +// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold +template +void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) { + if (0 == amount) { + return; + } + + bool is_dust_handled = false; + uint64_t dust = 0; + uint64_t order = 1; + while (0 != amount) { + uint64_t chunk = (amount % 10) * order; + amount /= 10; + order *= 10; + + if (dust + chunk <= dust_threshold) { + dust += chunk; + } else { + if (!is_dust_handled && 0 != dust) { + dust_handler(dust); + is_dust_handled = true; + } + if (0 != chunk) { + chunk_handler(chunk); + } + } + } + + if (!is_dust_handled && 0 != dust) { + dust_handler(dust); + } +} + +void get_tx_tree_hash(const std::vector& tx_hashes, Crypto::Hash& h); +Crypto::Hash get_tx_tree_hash(const std::vector& tx_hashes); +Crypto::Hash get_tx_tree_hash(const Block& b); + +} diff --git a/src/cryptonote_core/cryptonote_serialization.cpp b/src/CryptoNoteCore/CryptoNoteSerialization.cpp similarity index 55% rename from src/cryptonote_core/cryptonote_serialization.cpp rename to src/CryptoNoteCore/CryptoNoteSerialization.cpp index d3c995f6e9..cc7e9a4302 100644 --- a/src/cryptonote_core/cryptonote_serialization.cpp +++ b/src/CryptoNoteCore/CryptoNoteSerialization.cpp @@ -15,34 +15,52 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_serialization.h" -#include "account.h" +#include "CryptoNoteSerialization.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" -#include "crypto/crypto.h" -#include "cryptonote_config.h" - -#include #include +#include +#include #include #include +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" + +#include "Common/StringOutputStream.h" +#include "crypto/crypto.h" + +#include "Account.h" +#include "CryptoNoteConfig.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "TransactionExtra.h" + +using namespace Common; + namespace { +using namespace CryptoNote; +using namespace Common; + +size_t getSignaturesCount(const TransactionInput& input) { + struct txin_signature_size_visitor : public boost::static_visitor < size_t > { + size_t operator()(const BaseInput& txin) const { return 0; } + size_t operator()(const KeyInput& txin) const { return txin.outputIndexes.size(); } + size_t operator()(const MultisignatureInput& txin) const { return txin.signatureCount; } + }; + + return boost::apply_visitor(txin_signature_size_visitor(), input); +} + struct BinaryVariantTagGetter: boost::static_visitor { - uint8_t operator()(const CryptoNote::TransactionInputGenerate) { return 0xff; } - uint8_t operator()(const CryptoNote::TransactionInputToScript) { return 0x0; } - uint8_t operator()(const CryptoNote::TransactionInputToScriptHash) { return 0x1; } - uint8_t operator()(const CryptoNote::TransactionInputToKey) { return 0x2; } - uint8_t operator()(const CryptoNote::TransactionInputMultisignature) { return 0x3; } - uint8_t operator()(const CryptoNote::TransactionOutputToScript) { return 0x0; } - uint8_t operator()(const CryptoNote::TransactionOutputToScriptHash) { return 0x1; } - uint8_t operator()(const CryptoNote::TransactionOutputToKey) { return 0x2; } - uint8_t operator()(const CryptoNote::TransactionOutputMultisignature) { return 0x3; } + uint8_t operator()(const CryptoNote::BaseInput) { return 0xff; } + uint8_t operator()(const CryptoNote::KeyInput) { return 0x2; } + uint8_t operator()(const CryptoNote::MultisignatureInput) { return 0x3; } + uint8_t operator()(const CryptoNote::KeyOutput) { return 0x2; } + uint8_t operator()(const CryptoNote::MultisignatureOutput) { return 0x3; } uint8_t operator()(const CryptoNote::Transaction) { return 0xcc; } uint8_t operator()(const CryptoNote::Block) { return 0xbb; } }; @@ -50,49 +68,30 @@ struct BinaryVariantTagGetter: boost::static_visitor { struct VariantSerializer : boost::static_visitor<> { VariantSerializer(CryptoNote::ISerializer& serializer, const std::string& name) : s(serializer), name(name) {} - void operator() (CryptoNote::TransactionInputGenerate& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputToScript& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputToScriptHash& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputToKey& param) { s(param, name); } - void operator() (CryptoNote::TransactionInputMultisignature& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputToScript& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputToScriptHash& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputToKey& param) { s(param, name); } - void operator() (CryptoNote::TransactionOutputMultisignature& param) { s(param, name); } + template + void operator() (T& param) { s(param, name); } CryptoNote::ISerializer& s; - const std::string& name; + std::string name; }; void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNote::TransactionInput& in) { switch(tag) { case 0xff: { - CryptoNote::TransactionInputGenerate v; - serializer(v, "data"); - in = v; - break; - } - case 0x0: { - CryptoNote::TransactionInputToScript v; - serializer(v, "data"); - in = v; - break; - } - case 0x1: { - CryptoNote::TransactionInputToScriptHash v; - serializer(v, "data"); + CryptoNote::BaseInput v; + serializer(v, "value"); in = v; break; } case 0x2: { - CryptoNote::TransactionInputToKey v; - serializer(v, "data"); + CryptoNote::KeyInput v; + serializer(v, "value"); in = v; break; } case 0x3: { - CryptoNote::TransactionInputMultisignature v; - serializer(v, "data"); + CryptoNote::MultisignatureInput v; + serializer(v, "value"); in = v; break; } @@ -103,26 +102,14 @@ void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNot void getVariantValue(CryptoNote::ISerializer& serializer, uint8_t tag, CryptoNote::TransactionOutputTarget& out) { switch(tag) { - case 0x0: { - CryptoNote::TransactionOutputToScript v; - serializer(v, "data"); - out = v; - break; - } - case 0x1: { - CryptoNote::TransactionOutputToScriptHash v; - serializer(v, "data"); - out = v; - break; - } case 0x2: { - CryptoNote::TransactionOutputToKey v; + CryptoNote::KeyOutput v; serializer(v, "data"); out = v; break; } case 0x3: { - CryptoNote::TransactionOutputMultisignature v; + CryptoNote::MultisignatureOutput v; serializer(v, "data"); out = v; break; @@ -137,8 +124,8 @@ bool serializePod(T& v, Common::StringView name, CryptoNote::ISerializer& serial return serializer.binary(&v, sizeof(v), name); } -bool serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, Common::StringView name) { - std::size_t size = vector.size(); +bool serializeVarintVector(std::vector& vector, CryptoNote::ISerializer& serializer, Common::StringView name) { + size_t size = vector.size(); if (!serializer.beginArray(size, name)) { vector.clear(); @@ -157,21 +144,21 @@ bool serializeVarintVector(std::vector& vector, CryptoNote::ISerialize } -namespace crypto { +namespace Crypto { -bool serialize(public_key& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(PublicKey& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(pubKey, name, serializer); } -bool serialize(secret_key& secKey, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(SecretKey& secKey, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(secKey, name, serializer); } -bool serialize(hash& h, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(Hash& h, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(h, name, serializer); } -bool serialize(key_image& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(KeyImage& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(keyImage, name, serializer); } @@ -179,10 +166,17 @@ bool serialize(chacha8_iv& chacha, Common::StringView name, CryptoNote::ISeriali return serializePod(chacha, name, serializer); } -bool serialize(signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer) { +bool serialize(Signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer) { return serializePod(sig, name, serializer); } +bool serialize(EllipticCurveScalar& ecScalar, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(ecScalar, name, serializer); +} + +bool serialize(EllipticCurvePoint& ecPoint, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializePod(ecPoint, name, serializer); +} } @@ -190,31 +184,35 @@ namespace CryptoNote { void serialize(TransactionPrefix& txP, ISerializer& serializer) { serializer(txP.version, "version"); + + if (CURRENT_TRANSACTION_VERSION < txP.version) { + throw std::runtime_error("Wrong transaction version"); + } + serializer(txP.unlockTime, "unlock_time"); - serializer(txP.vin, "vin"); - serializer(txP.vout, "vout"); + serializer(txP.inputs, "vin"); + serializer(txP.outputs, "vout"); serializeAsBinary(txP.extra, "extra", serializer); } void serialize(Transaction& tx, ISerializer& serializer) { - serializer(tx.version, "version"); - serializer(tx.unlockTime, "unlock_time"); - serializer(tx.vin, "vin"); - serializer(tx.vout, "vout"); - serializeAsBinary(tx.extra, "extra", serializer); + serialize(static_cast(tx), serializer); - std::size_t sigSize = tx.vin.size(); + size_t sigSize = tx.inputs.size(); //TODO: make arrays without sizes // serializer.beginArray(sigSize, "signatures"); - tx.signatures.resize(sigSize); + + if (serializer.type() == ISerializer::INPUT) { + tx.signatures.resize(sigSize); + } bool signaturesNotExpected = tx.signatures.empty(); - if (!signaturesNotExpected && tx.vin.size() != tx.signatures.size()) { + if (!signaturesNotExpected && tx.inputs.size() != tx.signatures.size()) { throw std::runtime_error("Serialization error: unexpected signatures size"); } - for (size_t i = 0; i < tx.vin.size(); ++i) { - size_t signatureSize = Transaction::getSignatureSize(tx.vin[i]); + for (size_t i = 0; i < tx.inputs.size(); ++i) { + size_t signatureSize = getSignaturesCount(tx.inputs[i]); if (signaturesNotExpected) { if (signatureSize == 0) { continue; @@ -227,12 +225,18 @@ void serialize(Transaction& tx, ISerializer& serializer) { if (signatureSize != tx.signatures[i].size()) { throw std::runtime_error("Serialization error: unexpected signatures size"); } + + for (Crypto::Signature& sig : tx.signatures[i]) { + serializePod(sig, "", serializer); + } + } else { - tx.signatures[i].resize(signatureSize); - } + std::vector signatures(signatureSize); + for (Crypto::Signature& sig : signatures) { + serializePod(sig, "", serializer); + } - for (crypto::signature& sig: tx.signatures[i]) { - serializePod(sig, "", serializer); + tx.signatures[i] = std::move(signatures); } } // serializer.endArray(); @@ -254,22 +258,19 @@ void serialize(TransactionInput& in, ISerializer& serializer) { } } -void serialize(TransactionInputGenerate& gen, ISerializer& serializer) { - serializer(gen.height, "height"); +void serialize(BaseInput& gen, ISerializer& serializer) { + serializer(gen.blockIndex, "height"); } -void serialize(TransactionInputToScript& script, ISerializer& serializer) {} -void serialize(TransactionInputToScriptHash& scripthash, ISerializer& serializer) {} - -void serialize(TransactionInputToKey& key, ISerializer& serializer) { +void serialize(KeyInput& key, ISerializer& serializer) { serializer(key.amount, "amount"); - serializeVarintVector(key.keyOffsets, serializer, "key_offsets"); + serializeVarintVector(key.outputIndexes, serializer, "key_offsets"); serializer(key.keyImage, "k_image"); } -void serialize(TransactionInputMultisignature& multisignature, ISerializer& serializer) { +void serialize(MultisignatureInput& multisignature, ISerializer& serializer) { serializer(multisignature.amount, "amount"); - serializer(multisignature.signatures, "signatures"); + serializer(multisignature.signatureCount, "signatures"); serializer(multisignature.outputIndex, "outputIndex"); } @@ -294,16 +295,13 @@ void serialize(TransactionOutputTarget& output, ISerializer& serializer) { } } -void serialize(TransactionOutputToScript& script, ISerializer& serializer) {} -void serialize(TransactionOutputToScriptHash& scripthash, ISerializer& serializer) {} - -void serialize(TransactionOutputToKey& key, ISerializer& serializer) { +void serialize(KeyOutput& key, ISerializer& serializer) { serializer(key.key, "key"); } -void serialize(TransactionOutputMultisignature& multisignature, ISerializer& serializer) { +void serialize(MultisignatureOutput& multisignature, ISerializer& serializer) { serializer(multisignature.keys, "keys"); - serializer(multisignature.requiredSignatures, "required_signatures"); + serializer(multisignature.requiredSignatureCount, "required_signatures"); } void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { @@ -315,25 +313,25 @@ void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { serializer(pbs.m_parentBlock.minorVersion, "minorVersion"); serializer(pbs.m_timestamp, "timestamp"); - serializer(pbs.m_parentBlock.prevId, "prevId"); + serializer(pbs.m_parentBlock.previousBlockHash, "prevId"); serializer.binary(&pbs.m_nonce, sizeof(pbs.m_nonce), "nonce"); if (pbs.m_hashingSerialization) { - crypto::hash minerTxHash; - if (!get_transaction_hash(pbs.m_parentBlock.minerTx, minerTxHash)) { + Crypto::Hash minerTxHash; + if (!getObjectHash(pbs.m_parentBlock.baseTransaction, minerTxHash)) { throw std::runtime_error("Get transaction hash error"); } - crypto::hash merkleRoot; - crypto::tree_hash_from_branch(pbs.m_parentBlock.minerTxBranch.data(), pbs.m_parentBlock.minerTxBranch.size(), minerTxHash, 0, merkleRoot); + Crypto::Hash merkleRoot; + Crypto::tree_hash_from_branch(pbs.m_parentBlock.baseTransactionBranch.data(), pbs.m_parentBlock.baseTransactionBranch.size(), minerTxHash, 0, merkleRoot); serializer(merkleRoot, "merkleRoot"); } - uint64_t txNum = static_cast(pbs.m_parentBlock.numberOfTransactions); + uint64_t txNum = static_cast(pbs.m_parentBlock.transactionCount); serializer(txNum, "numberOfTransactions"); - pbs.m_parentBlock.numberOfTransactions = static_cast(txNum); - if (pbs.m_parentBlock.numberOfTransactions < 1) { + pbs.m_parentBlock.transactionCount = static_cast(txNum); + if (pbs.m_parentBlock.transactionCount < 1) { throw std::runtime_error("Wrong transactions number"); } @@ -341,29 +339,29 @@ void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { return; } - size_t branchSize = crypto::tree_depth(pbs.m_parentBlock.numberOfTransactions); + size_t branchSize = Crypto::tree_depth(pbs.m_parentBlock.transactionCount); if (serializer.type() == ISerializer::OUTPUT) { - if (pbs.m_parentBlock.minerTxBranch.size() != branchSize) { + if (pbs.m_parentBlock.baseTransactionBranch.size() != branchSize) { throw std::runtime_error("Wrong miner transaction branch size"); } } else { - pbs.m_parentBlock.minerTxBranch.resize(branchSize); + pbs.m_parentBlock.baseTransactionBranch.resize(branchSize); } -// serializer(m_parentBlock.minerTxBranch, "minerTxBranch"); +// serializer(m_parentBlock.baseTransactionBranch, "baseTransactionBranch"); //TODO: Make arrays with computable size! This code won't work with json serialization! - for (crypto::hash& hash: pbs.m_parentBlock.minerTxBranch) { + for (Crypto::Hash& hash: pbs.m_parentBlock.baseTransactionBranch) { serializer(hash, ""); } - serializer(pbs.m_parentBlock.minerTx, "minerTx"); + serializer(pbs.m_parentBlock.baseTransaction, "minerTx"); - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(pbs.m_parentBlock.minerTx.extra, mmTag)) { + TransactionExtraMergeMiningTag mmTag; + if (!getMergeMiningTagFromExtra(pbs.m_parentBlock.baseTransaction.extra, mmTag)) { throw std::runtime_error("Can't get extra merge mining tag"); } - if (mmTag.depth > 8 * sizeof(crypto::hash)) { + if (mmTag.depth > 8 * sizeof(Crypto::Hash)) { throw std::runtime_error("Wrong merge mining tag depth"); } @@ -377,7 +375,7 @@ void serialize(ParentBlockSerializer& pbs, ISerializer& serializer) { // serializer(m_parentBlock.blockchainBranch, "blockchainBranch"); //TODO: Make arrays with computable size! This code won't work with json serialization! - for (crypto::hash& hash: pbs.m_parentBlock.blockchainBranch) { + for (Crypto::Hash& hash: pbs.m_parentBlock.blockchainBranch) { serializer(hash, ""); } } @@ -391,10 +389,10 @@ void serializeBlockHeader(BlockHeader& header, ISerializer& serializer) { serializer(header.minorVersion, "minor_version"); if (header.majorVersion == BLOCK_MAJOR_VERSION_1) { serializer(header.timestamp, "timestamp"); - serializer(header.prevId, "prev_id"); + serializer(header.previousBlockHash, "prev_id"); serializer.binary(&header.nonce, sizeof(header.nonce), "nonce"); } else if (header.majorVersion == BLOCK_MAJOR_VERSION_2) { - serializer(header.prevId, "prev_id"); + serializer(header.previousBlockHash, "prev_id"); } else { throw std::runtime_error("Wrong major version"); } @@ -412,42 +410,48 @@ void serialize(Block& block, ISerializer& serializer) { serializer(parentBlockSerializer, "parent_block"); } - serializer(block.minerTx, "miner_tx"); - serializer(block.txHashes, "tx_hashes"); + serializer(block.baseTransaction, "miner_tx"); + serializer(block.transactionHashes, "tx_hashes"); } void serialize(AccountPublicAddress& address, ISerializer& serializer) { - serializer(address.m_spendPublicKey, "m_spend_public_key"); - serializer(address.m_viewPublicKey, "m_view_public_key"); + serializer(address.spendPublicKey, "m_spend_public_key"); + serializer(address.viewPublicKey, "m_view_public_key"); } -void serialize(account_keys& keys, ISerializer& s) { - s(keys.m_account_address, "m_account_address"); - s(keys.m_spend_secret_key, "m_spend_secret_key"); - s(keys.m_view_secret_key, "m_view_secret_key"); +void serialize(AccountKeys& keys, ISerializer& s) { + s(keys.address, "m_account_address"); + s(keys.spendSecretKey, "m_spend_secret_key"); + s(keys.viewSecretKey, "m_view_secret_key"); } -void doSerialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer) { +void doSerialize(TransactionExtraMergeMiningTag& tag, ISerializer& serializer) { uint64_t depth = static_cast(tag.depth); serializer(depth, "depth"); tag.depth = static_cast(depth); - serializer(tag.merkle_root, "merkle_root"); + serializer(tag.merkleRoot, "merkle_root"); } -void serialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer) { +void serialize(TransactionExtraMergeMiningTag& tag, ISerializer& serializer) { if (serializer.type() == ISerializer::OUTPUT) { - std::stringstream stream; - BinaryOutputStreamSerializer output(stream); + std::string field; + StringOutputStream os(field); + BinaryOutputStreamSerializer output(os); doSerialize(tag, output); - std::string field = stream.str(); serializer(field, ""); } else { std::string field; serializer(field, ""); - std::stringstream stream(field); + MemoryInputStream stream(field.data(), field.size()); BinaryInputStreamSerializer input(stream); doSerialize(tag, input); } } +void serialize(KeyPair& keyPair, ISerializer& serializer) { + serializer(keyPair.secretKey, "secret_key"); + serializer(keyPair.publicKey, "public_key"); +} + + } //namespace CryptoNote diff --git a/src/cryptonote_core/cryptonote_serialization.h b/src/CryptoNoteCore/CryptoNoteSerialization.h old mode 100644 new mode 100755 similarity index 55% rename from src/cryptonote_core/cryptonote_serialization.h rename to src/CryptoNoteCore/CryptoNoteSerialization.h index ecf3c8ec12..49ee1ad30c --- a/src/cryptonote_core/cryptonote_serialization.h +++ b/src/CryptoNoteCore/CryptoNoteSerialization.h @@ -17,47 +17,51 @@ #pragma once -#include "cryptonote_basic.h" -#include "serialization/ISerializer.h" +#include "CryptoNoteBasic.h" +#include "crypto/chacha8.h" +#include "Serialization/ISerializer.h" +#include "crypto/crypto.h" -namespace crypto { +namespace Crypto { -bool serialize(public_key& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(secret_key& secKey, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(hash& h, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(PublicKey& pubKey, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(SecretKey& secKey, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(Hash& h, Common::StringView name, CryptoNote::ISerializer& serializer); bool serialize(chacha8_iv& chacha, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(key_image& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer); -bool serialize(signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(KeyImage& keyImage, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(Signature& sig, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(EllipticCurveScalar& ecScalar, Common::StringView name, CryptoNote::ISerializer& serializer); +bool serialize(EllipticCurvePoint& ecPoint, Common::StringView name, CryptoNote::ISerializer& serializer); -} //namespace crypto +} namespace CryptoNote { -void serialize(ParentBlockSerializer& pbs, ISerializer& serializer); + +struct AccountKeys; +struct TransactionExtraMergeMiningTag; + void serialize(TransactionPrefix& txP, ISerializer& serializer); void serialize(Transaction& tx, ISerializer& serializer); void serialize(TransactionInput& in, ISerializer& serializer); void serialize(TransactionOutput& in, ISerializer& serializer); -void serialize(TransactionInputGenerate& gen, ISerializer& serializer); -void serialize(TransactionInputToScript& script, ISerializer& serializer); -void serialize(TransactionInputToScriptHash& scripthash, ISerializer& serializer); -void serialize(TransactionInputToKey& key, ISerializer& serializer); -void serialize(TransactionInputMultisignature& multisignature, ISerializer& serializer); +void serialize(BaseInput& gen, ISerializer& serializer); +void serialize(KeyInput& key, ISerializer& serializer); +void serialize(MultisignatureInput& multisignature, ISerializer& serializer); void serialize(TransactionOutput& output, ISerializer& serializer); - void serialize(TransactionOutputTarget& output, ISerializer& serializer); +void serialize(KeyOutput& key, ISerializer& serializer); +void serialize(MultisignatureOutput& multisignature, ISerializer& serializer); -void serialize(TransactionOutputToScript& script, ISerializer& serializer); -void serialize(TransactionOutputToScriptHash& scripthash, ISerializer& serializer); -void serialize(TransactionOutputToKey& key, ISerializer& serializer); -void serialize(TransactionOutputMultisignature& multisignature, ISerializer& serializer); void serialize(BlockHeader& header, ISerializer& serializer); void serialize(Block& block, ISerializer& serializer); -void serialize(tx_extra_merge_mining_tag& tag, ISerializer& serializer); +void serialize(ParentBlockSerializer& pbs, ISerializer& serializer); +void serialize(TransactionExtraMergeMiningTag& tag, ISerializer& serializer); void serialize(AccountPublicAddress& address, ISerializer& serializer); -void serialize(account_keys& keys, ISerializer& s); +void serialize(AccountKeys& keys, ISerializer& s); +void serialize(KeyPair& keyPair, ISerializer& serializer); -} //namespace CryptoNote +} diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/CryptoNoteCore/CryptoNoteStatInfo.h old mode 100644 new mode 100755 similarity index 96% rename from src/cryptonote_core/cryptonote_stat_info.h rename to src/CryptoNoteCore/CryptoNoteStatInfo.h index 0c7bec643e..5fd0ec7b0d --- a/src/cryptonote_core/cryptonote_stat_info.h +++ b/src/CryptoNoteCore/CryptoNoteStatInfo.h @@ -17,7 +17,7 @@ #pragma once -#include "serialization/ISerializer.h" +#include "Serialization/ISerializer.h" namespace CryptoNote { diff --git a/src/CryptoNoteCore/CryptoNoteTools.cpp b/src/CryptoNoteCore/CryptoNoteTools.cpp new file mode 100755 index 0000000000..e1c406eed6 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteTools.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteTools.h" +#include "CryptoNoteFormatUtils.h" + +namespace CryptoNote { +template<> +bool toBinaryArray(const BinaryArray& object, BinaryArray& binaryArray) { + try { + Common::VectorOutputStream stream(binaryArray); + BinaryOutputStreamSerializer serializer(stream); + std::string oldBlob = Common::asString(object); + serializer(oldBlob, ""); + } catch (std::exception&) { + return false; + } + + return true; +} + +void getBinaryArrayHash(const BinaryArray& binaryArray, Crypto::Hash& hash) { + cn_fast_hash(binaryArray.data(), binaryArray.size(), hash); +} + +Crypto::Hash getBinaryArrayHash(const BinaryArray& binaryArray) { + Crypto::Hash hash; + getBinaryArrayHash(binaryArray, hash); + return hash; +} + +uint64_t getInputAmount(const Transaction& transaction) { + uint64_t amount = 0; + for (auto& input : transaction.inputs) { + if (input.type() == typeid(KeyInput)) { + amount += boost::get(input).amount; + } else if (input.type() == typeid(MultisignatureInput)) { + amount += boost::get(input).amount; + } + } + + return amount; +} + +uint64_t getOutputAmount(const Transaction& transaction) { + uint64_t amount = 0; + for (auto& output : transaction.outputs) { + amount += output.amount; + } + + return amount; +} + +void decomposeAmount(uint64_t amount, uint64_t dustThreshold, std::vector& decomposedAmounts) { + decompose_amount_into_digits(amount, dustThreshold, + [&](uint64_t amount) { + decomposedAmounts.push_back(amount); + }, + [&](uint64_t dust) { + decomposedAmounts.push_back(dust); + } + ); +} + +} diff --git a/src/CryptoNoteCore/CryptoNoteTools.h b/src/CryptoNoteCore/CryptoNoteTools.h new file mode 100755 index 0000000000..7985063404 --- /dev/null +++ b/src/CryptoNoteCore/CryptoNoteTools.h @@ -0,0 +1,125 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "Common/MemoryInputStream.h" +#include "Common/StringTools.h" +#include "Common/VectorOutputStream.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "CryptoNoteSerialization.h" + +namespace CryptoNote { + +void getBinaryArrayHash(const BinaryArray& binaryArray, Crypto::Hash& hash); +Crypto::Hash getBinaryArrayHash(const BinaryArray& binaryArray); + +template +bool toBinaryArray(const T& object, BinaryArray& binaryArray) { + try { + ::Common::VectorOutputStream stream(binaryArray); + BinaryOutputStreamSerializer serializer(stream); + serialize(const_cast(object), serializer); + } catch (std::exception&) { + return false; + } + + return true; +} + +template<> +bool toBinaryArray(const BinaryArray& object, BinaryArray& binaryArray); + +template +BinaryArray toBinaryArray(const T& object) { + BinaryArray ba; + toBinaryArray(object, ba); + return ba; +} + +template +bool fromBinaryArray(T& object, const BinaryArray& binaryArray) { + bool result = false; + try { + Common::MemoryInputStream stream(binaryArray.data(), binaryArray.size()); + BinaryInputStreamSerializer serializer(stream); + serialize(object, serializer); + result = stream.endOfStream(); // check that all data was consumed + } catch (std::exception&) { + } + + return result; +} + +template +bool getObjectBinarySize(const T& object, size_t& size) { + BinaryArray ba; + if (!toBinaryArray(object, ba)) { + size = (std::numeric_limits::max)(); + return false; + } + + size = ba.size(); + return true; +} + +template +size_t getObjectBinarySize(const T& object) { + size_t size; + getObjectBinarySize(object, size); + return size; +} + +template +bool getObjectHash(const T& object, Crypto::Hash& hash) { + BinaryArray ba; + if (!toBinaryArray(object, ba)) { + hash = NULL_HASH; + return false; + } + + hash = getBinaryArrayHash(ba); + return true; +} + +template +bool getObjectHash(const T& object, Crypto::Hash& hash, size_t& size) { + BinaryArray ba; + if (!toBinaryArray(object, ba)) { + hash = NULL_HASH; + size = (std::numeric_limits::max)(); + return false; + } + + size = ba.size(); + hash = getBinaryArrayHash(ba); + return true; +} + +template +Crypto::Hash getObjectHash(const T& object) { + Crypto::Hash hash; + getObjectHash(object, hash); + return hash; +} + +uint64_t getInputAmount(const Transaction& transaction); +uint64_t getOutputAmount(const Transaction& transaction); +void decomposeAmount(uint64_t amount, uint64_t dustThreshold, std::vector& decomposedAmounts); +} diff --git a/src/cryptonote_core/Currency.cpp b/src/CryptoNoteCore/Currency.cpp old mode 100644 new mode 100755 similarity index 86% rename from src/cryptonote_core/Currency.cpp rename to src/CryptoNoteCore/Currency.cpp index c75f2b399f..73590e890f --- a/src/cryptonote_core/Currency.cpp +++ b/src/CryptoNoteCore/Currency.cpp @@ -19,15 +19,21 @@ #include #include #include -#include "../Common/base58.h" +#include "../Common/Base58.h" #include "../Common/int-util.h" -#include "account.h" -#include "cryptonote_basic_impl.h" -#include "cryptonote_format_utils.h" +#include "../Common/StringTools.h" + +#include "Account.h" +#include "CryptoNoteBasicImpl.h" +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "TransactionExtra.h" #include "UpgradeDetector.h" + #undef ERROR using namespace Logging; +using namespace Common; namespace CryptoNote { @@ -48,6 +54,7 @@ bool Currency::init() { m_blocksCacheFileName = "testnet_" + m_blocksCacheFileName; m_blockIndexesFileName = "testnet_" + m_blockIndexesFileName; m_txPoolFileName = "testnet_" + m_txPoolFileName; + m_blockchinIndicesFileName = "testnet_" + m_blockchinIndicesFileName; } return true; @@ -58,17 +65,17 @@ bool Currency::generateGenesisBlock() { //account_public_address ac = boost::value_initialized(); //std::vector sz; - //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.minerTx); // zero fee in genesis - //blobdata txb = tx_to_blob(m_genesisBlock.minerTx); + //constructMinerTx(0, 0, 0, 0, 0, ac, m_genesisBlock.baseTransaction); // zero fee in genesis + //BinaryArray txb = toBinaryArray(m_genesisBlock.baseTransaction); //std::string hex_tx_represent = Common::toHex(txb); // Hard code coinbase tx in genesis block, because through generating tx use random, but genesis should be always the same std::string genesisCoinbaseTxHex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; - blobdata minerTxBlob; + BinaryArray minerTxBlob; bool r = - hexToBlob(genesisCoinbaseTxHex, minerTxBlob) && - parse_and_validate_tx_from_blob(minerTxBlob, m_genesisBlock.minerTx); + fromHex(genesisCoinbaseTxHex, minerTxBlob) && + fromBinaryArray(m_genesisBlock.baseTransaction, minerTxBlob); if (!r) { logger(ERROR, BRIGHT_RED) << "failed to parse coinbase tx from hard coded blob"; @@ -122,22 +129,22 @@ size_t Currency::maxBlockCumulativeSize(uint64_t height) const { bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, - const blobdata& extraNonce/* = blobdata()*/, size_t maxOuts/* = 1*/, + const BinaryArray& extraNonce/* = BinaryArray()*/, size_t maxOuts/* = 1*/, bool penalizeFee/* = false*/) const { - tx.vin.clear(); - tx.vout.clear(); + tx.inputs.clear(); + tx.outputs.clear(); tx.extra.clear(); - KeyPair txkey = KeyPair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); + KeyPair txkey = generateKeyPair(); + addTransactionPublicKeyToExtra(tx.extra, txkey.publicKey); if (!extraNonce.empty()) { - if (!add_extra_nonce_to_tx_extra(tx.extra, extraNonce)) { + if (!addExtraNonceToTransactionExtra(tx.extra, extraNonce)) { return false; } } - TransactionInputGenerate in; - in.height = height; + BaseInput in; + in.blockIndex = height; uint64_t blockReward; int64_t emissionChange; @@ -145,9 +152,6 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr logger(INFO) << "Block is too big"; return false; } -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - logger(DEBUGGING) << "Creating block template: reward " << blockReward << ", fee " << fee; -#endif std::vector outAmounts; decompose_amount_into_digits(blockReward, m_defaultDustThreshold, @@ -162,35 +166,35 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr uint64_t summaryAmounts = 0; for (size_t no = 0; no < outAmounts.size(); no++) { - crypto::key_derivation derivation = boost::value_initialized(); - crypto::public_key outEphemeralPubKey = boost::value_initialized(); + Crypto::KeyDerivation derivation = boost::value_initialized(); + Crypto::PublicKey outEphemeralPubKey = boost::value_initialized(); - bool r = crypto::generate_key_derivation(minerAddress.m_viewPublicKey, txkey.sec, derivation); + bool r = Crypto::generate_key_derivation(minerAddress.viewPublicKey, txkey.secretKey, derivation); if (!(r)) { logger(ERROR, BRIGHT_RED) << "while creating outs: failed to generate_key_derivation(" - << minerAddress.m_viewPublicKey << ", " << txkey.sec << ")"; + << minerAddress.viewPublicKey << ", " << txkey.secretKey << ")"; return false; } - r = crypto::derive_public_key(derivation, no, minerAddress.m_spendPublicKey, outEphemeralPubKey); + r = Crypto::derive_public_key(derivation, no, minerAddress.spendPublicKey, outEphemeralPubKey); if (!(r)) { logger(ERROR, BRIGHT_RED) << "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", " - << minerAddress.m_spendPublicKey << ")"; + << minerAddress.spendPublicKey << ")"; return false; } - TransactionOutputToKey tk; + KeyOutput tk; tk.key = outEphemeralPubKey; TransactionOutput out; summaryAmounts += out.amount = outAmounts[no]; out.target = tk; - tx.vout.push_back(out); + tx.outputs.push_back(out); } if (!(summaryAmounts == blockReward)) { @@ -201,12 +205,12 @@ bool Currency::constructMinerTx(uint32_t height, size_t medianSize, uint64_t alr tx.version = CURRENT_TRANSACTION_VERSION; //lock tx.unlockTime = height + m_minedMoneyUnlockWindow; - tx.vin.push_back(in); + tx.inputs.push_back(in); return true; } -std::string Currency::accountAddressAsString(const account_base& account) const { - return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.get_keys().m_account_address); +std::string Currency::accountAddressAsString(const AccountBase& account) const { + return getAccountAddressAsStr(m_publicAddressBase58Prefix, account.getAccountKeys().address); } std::string Currency::accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const { @@ -316,8 +320,8 @@ difficulty_type Currency::nextDifficulty(std::vector timestamps, return (low + timeSpan - 1) / timeSpan; } -bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, - crypto::hash& proofOfWork) const { +bool Currency::checkProofOfWorkV1(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + Crypto::Hash& proofOfWork) const { if (BLOCK_MAJOR_VERSION_1 != block.majorVersion) { return false; } @@ -329,8 +333,8 @@ bool Currency::checkProofOfWorkV1(crypto::cn_context& context, const Block& bloc return check_hash(proofOfWork, currentDiffic); } -bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, - crypto::hash& proofOfWork) const { +bool Currency::checkProofOfWorkV2(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, + Crypto::Hash& proofOfWork) const { if (BLOCK_MAJOR_VERSION_2 != block.majorVersion) { return false; } @@ -343,8 +347,8 @@ bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& bloc return false; } - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mmTag)) { + TransactionExtraMergeMiningTag mmTag; + if (!getMergeMiningTagFromExtra(block.parentBlock.baseTransaction.extra, mmTag)) { logger(ERROR) << "merge mining tag wasn't found in extra of the parent block miner transaction"; return false; } @@ -353,16 +357,16 @@ bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& bloc return false; } - crypto::hash auxBlockHeaderHash; + Crypto::Hash auxBlockHeaderHash; if (!get_aux_block_header_hash(block, auxBlockHeaderHash)) { return false; } - crypto::hash auxBlocksMerkleRoot; - crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), + Crypto::Hash auxBlocksMerkleRoot; + Crypto::tree_hash_from_branch(block.parentBlock.blockchainBranch.data(), block.parentBlock.blockchainBranch.size(), auxBlockHeaderHash, &m_genesisBlockHash, auxBlocksMerkleRoot); - if (auxBlocksMerkleRoot != mmTag.merkle_root) { + if (auxBlocksMerkleRoot != mmTag.merkleRoot) { logger(ERROR, BRIGHT_YELLOW) << "Aux block hash wasn't found in merkle tree"; return false; } @@ -370,7 +374,7 @@ bool Currency::checkProofOfWorkV2(crypto::cn_context& context, const Block& bloc return true; } -bool Currency::checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const { +bool Currency::checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const { switch (block.majorVersion) { case BLOCK_MAJOR_VERSION_1: return checkProofOfWorkV1(context, block, currentDiffic, proofOfWork); case BLOCK_MAJOR_VERSION_2: return checkProofOfWorkV2(context, block, currentDiffic, proofOfWork); @@ -427,6 +431,7 @@ CurrencyBuilder::CurrencyBuilder(Logging::ILogger& log) : m_currency(log) { blocksCacheFileName(parameters::CRYPTONOTE_BLOCKSCACHE_FILENAME); blockIndexesFileName(parameters::CRYPTONOTE_BLOCKINDEXES_FILENAME); txPoolFileName(parameters::CRYPTONOTE_POOLDATA_FILENAME); + blockchinIndicesFileName(parameters::CRYPTONOTE_BLOCKCHAIN_INDICES_FILENAME); testnet(false); } diff --git a/src/cryptonote_core/Currency.h b/src/CryptoNoteCore/Currency.h old mode 100644 new mode 100755 similarity index 91% rename from src/cryptonote_core/Currency.h rename to src/CryptoNoteCore/Currency.h index d4009f0fb1..7394e48f91 --- a/src/cryptonote_core/Currency.h +++ b/src/CryptoNoteCore/Currency.h @@ -21,15 +21,16 @@ #include #include #include -#include "../cryptonote_config.h" +#include "../CryptoNoteConfig.h" #include "../crypto/hash.h" -#include "../cryptonote_protocol/blobdatatype.h" #include "../Logging/LoggerRef.h" -#include "cryptonote_basic.h" -#include "difficulty.h" +#include "CryptoNoteBasic.h" +#include "Difficulty.h" namespace CryptoNote { +class AccountBase; + class Currency { public: uint64_t maxBlockHeight() const { return m_maxBlockHeight; } @@ -83,11 +84,12 @@ class Currency { const std::string& blocksCacheFileName() const { return m_blocksCacheFileName; } const std::string& blockIndexesFileName() const { return m_blockIndexesFileName; } const std::string& txPoolFileName() const { return m_txPoolFileName; } + const std::string& blockchinIndicesFileName() const { return m_blockchinIndicesFileName; } bool isTestnet() const { return m_testnet; } const Block& genesisBlock() const { return m_genesisBlock; } - const crypto::hash& genesisBlockHash() const { return m_genesisBlockHash; } + const Crypto::Hash& genesisBlockHash() const { return m_genesisBlockHash; } bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, bool penalizeFee, uint64_t& reward, int64_t& emissionChange) const; @@ -95,9 +97,9 @@ class Currency { bool constructMinerTx(uint32_t height, size_t medianSize, uint64_t alreadyGeneratedCoins, size_t currentBlockSize, uint64_t fee, const AccountPublicAddress& minerAddress, Transaction& tx, - const blobdata& extraNonce = blobdata(), size_t maxOuts = 1, bool penalizeFee = false) const; + const BinaryArray& extraNonce = BinaryArray(), size_t maxOuts = 1, bool penalizeFee = false) const; - std::string accountAddressAsString(const account_base& account) const; + std::string accountAddressAsString(const AccountBase& account) const; std::string accountAddressAsString(const AccountPublicAddress& accountPublicAddress) const; bool parseAccountAddressString(const std::string& str, AccountPublicAddress& addr) const; @@ -106,9 +108,9 @@ class Currency { difficulty_type nextDifficulty(std::vector timestamps, std::vector cumulativeDifficulties) const; - bool checkProofOfWorkV1(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - bool checkProofOfWorkV2(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; - bool checkProofOfWork(crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, crypto::hash& proofOfWork) const; + bool checkProofOfWorkV1(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; + bool checkProofOfWorkV2(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; + bool checkProofOfWork(Crypto::cn_context& context, const Block& block, difficulty_type currentDiffic, Crypto::Hash& proofOfWork) const; private: Currency(Logging::ILogger& log) : logger(log, "currency") { @@ -166,11 +168,12 @@ class Currency { std::string m_blocksCacheFileName; std::string m_blockIndexesFileName; std::string m_txPoolFileName; + std::string m_blockchinIndicesFileName; bool m_testnet; Block m_genesisBlock; - crypto::hash m_genesisBlockHash; + Crypto::Hash m_genesisBlockHash; Logging::LoggerRef logger; @@ -234,7 +237,8 @@ class CurrencyBuilder : boost::noncopyable { CurrencyBuilder& blocksCacheFileName(const std::string& val) { m_currency.m_blocksCacheFileName = val; return *this; } CurrencyBuilder& blockIndexesFileName(const std::string& val) { m_currency.m_blockIndexesFileName = val; return *this; } CurrencyBuilder& txPoolFileName(const std::string& val) { m_currency.m_txPoolFileName = val; return *this; } - + CurrencyBuilder& blockchinIndicesFileName(const std::string& val) { m_currency.m_blockchinIndicesFileName = val; return *this; } + CurrencyBuilder& testnet(bool val) { m_currency.m_testnet = val; return *this; } private: diff --git a/src/cryptonote_core/difficulty.cpp b/src/CryptoNoteCore/Difficulty.cpp old mode 100644 new mode 100755 similarity index 91% rename from src/cryptonote_core/difficulty.cpp rename to src/CryptoNoteCore/Difficulty.cpp index bc7e8bc4c0..8b37efd41d --- a/src/cryptonote_core/difficulty.cpp +++ b/src/CryptoNoteCore/Difficulty.cpp @@ -23,30 +23,27 @@ #include "Common/int-util.h" #include "crypto/hash.h" -#include "cryptonote_config.h" -#include "difficulty.h" +#include "CryptoNoteConfig.h" +#include "Difficulty.h" namespace CryptoNote { - using std::size_t; using std::uint64_t; using std::vector; -#if defined(_MSC_VER) -#include -#include +#if defined(__SIZEOF_INT128__) static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { - low = mul128(a, b, &high); + typedef unsigned __int128 uint128_t; + uint128_t res = (uint128_t) a * (uint128_t) b; + low = (uint64_t) res; + high = (uint64_t) (res >> 64); } #else static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { - typedef unsigned __int128 uint128_t; - uint128_t res = (uint128_t) a * (uint128_t) b; - low = (uint64_t) res; - high = (uint64_t) (res >> 64); + low = mul128(a, b, &high); } #endif @@ -59,7 +56,7 @@ namespace CryptoNote { return a + b < a || (c && a + b == (uint64_t) -1); } - bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + bool check_hash(const Crypto::Hash &hash, difficulty_type difficulty) { uint64_t low, high, top, cur; // First check the highest word, this will most likely fail for a random hash. mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); diff --git a/src/cryptonote_core/difficulty.h b/src/CryptoNoteCore/Difficulty.h old mode 100644 new mode 100755 similarity index 93% rename from src/cryptonote_core/difficulty.h rename to src/CryptoNoteCore/Difficulty.h index 103c86e7cd..f59336a96b --- a/src/cryptonote_core/difficulty.h +++ b/src/CryptoNoteCore/Difficulty.h @@ -26,5 +26,5 @@ namespace CryptoNote { typedef std::uint64_t difficulty_type; - bool check_hash(const crypto::hash &hash, difficulty_type difficulty); + bool check_hash(const Crypto::Hash &hash, difficulty_type difficulty); } diff --git a/src/System/LatchGuard.cpp b/src/CryptoNoteCore/IBlock.cpp similarity index 79% rename from src/System/LatchGuard.cpp rename to src/CryptoNoteCore/IBlock.cpp index 15ba0a7d2c..a6336e6c1f 100644 --- a/src/System/LatchGuard.cpp +++ b/src/CryptoNoteCore/IBlock.cpp @@ -15,18 +15,9 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "LatchGuard.h" +#include "IBlock.h" -#include "System/Latch.h" - -namespace System { - -LatchGuard::LatchGuard(Latch& latch) : m_latch(latch) { - m_latch.increase(); +namespace CryptoNote { +IBlock::~IBlock() { } - -LatchGuard::~LatchGuard() { - m_latch.decrease(); -} - } diff --git a/src/CryptoNote/KeyOutput.cpp b/src/CryptoNoteCore/IBlock.h old mode 100755 new mode 100644 similarity index 75% rename from src/CryptoNote/KeyOutput.cpp rename to src/CryptoNoteCore/IBlock.h index 9a97a1e4bd..e86cdcd915 --- a/src/CryptoNote/KeyOutput.cpp +++ b/src/CryptoNoteCore/IBlock.h @@ -15,19 +15,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "KeyOutput.h" +#pragma once -namespace CryptoNote { - -KeyOutput::KeyOutput(uint64_t amount, const crypto::public_key& key) : amount(amount), key(key) { -} +#include "CryptoNote.h" -uint64_t KeyOutput::getAmount() const { - return amount; -} - -const crypto::public_key& KeyOutput::getKey() const { - return key; -} +namespace CryptoNote { +class IBlock { +public: + virtual ~IBlock(); + virtual const Block& getBlock() const = 0; + virtual size_t getTransactionCount() const = 0; + virtual const Transaction& getTransaction(size_t index) const = 0; +}; } diff --git a/src/cryptonote_core/IBlockchainStorageObserver.h b/src/CryptoNoteCore/IBlockchainStorageObserver.h similarity index 100% rename from src/cryptonote_core/IBlockchainStorageObserver.h rename to src/CryptoNoteCore/IBlockchainStorageObserver.h diff --git a/src/CryptoNoteCore/ICore.h b/src/CryptoNoteCore/ICore.h new file mode 100755 index 0000000000..72b4460f73 --- /dev/null +++ b/src/CryptoNoteCore/ICore.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include "CryptoNoteCore/Difficulty.h" + +#include "CryptoNoteCore/MessageQueue.h" +#include "CryptoNoteCore/BlockchainMessages.h" + +namespace CryptoNote { + +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; +struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; +struct NOTIFY_RESPONSE_GET_OBJECTS_request; +struct NOTIFY_REQUEST_GET_OBJECTS_request; + +class Currency; +class IBlock; +class ICoreObserver; +struct Block; +struct block_verification_context; +struct BlockFullInfo; +struct BlockShortInfo; +struct core_stat_info; +struct i_cryptonote_protocol; +struct Transaction; +struct MultisignatureInput; +struct KeyInput; +struct TransactionPrefixInfo; +struct tx_verification_context; + +class ICore { +public: + virtual ~ICore() {} + + virtual bool addObserver(ICoreObserver* observer) = 0; + virtual bool removeObserver(ICoreObserver* observer) = 0; + + virtual bool have_block(const Crypto::Hash& id) = 0; + virtual std::vector buildSparseChain() = 0; + virtual std::vector buildSparseChain(const Crypto::Hash& startBlockId) = 0; + virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) = 0; + virtual bool on_idle() = 0; + virtual void pause_mining() = 0; + virtual void update_block_template_and_resume_mining() = 0; + virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; + virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + virtual void on_synchronized() = 0; + virtual bool is_ready() = 0; + virtual size_t addChain(const std::vector& chain) = 0; + + virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) = 0; + virtual std::vector findBlockchainSupplement(const std::vector& remoteBlockIds, size_t maxCount, + uint32_t& totalBlockCount, uint32_t& startBlockIndex) = 0; + virtual 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) = 0; + virtual bool get_tx_outputs_gindexs(const Crypto::Hash& tx_id, std::vector& indexs) = 0; + virtual bool getOutByMSigGIndex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out) = 0; + virtual i_cryptonote_protocol* get_protocol() = 0; + virtual bool handle_incoming_tx(const BinaryArray& tx_blob, tx_verification_context& tvc, bool keeped_by_block) = 0; //Deprecated. Should be removed with CryptoNoteProtocolHandler. + virtual std::vector getPoolTransactions() = 0; + virtual bool getPoolChanges(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) = 0; + virtual bool getPoolChangesLite(const Crypto::Hash& tailBlockId, const std::vector& knownTxsIds, + std::vector& addedTxs, std::vector& deletedTxsIds) = 0; + virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, + std::vector& deletedTxsIds) = 0; + virtual bool queryBlocks(const std::vector& block_ids, uint64_t timestamp, + uint32_t& start_height, uint32_t& current_height, uint32_t& full_offset, std::vector& entries) = 0; + virtual bool queryBlocksLite(const std::vector& block_ids, uint64_t timestamp, + uint32_t& start_height, uint32_t& current_height, uint32_t& full_offset, std::vector& entries) = 0; + + virtual Crypto::Hash getBlockIdByHeight(uint32_t height) = 0; + virtual bool getBlockByHash(const Crypto::Hash &h, Block &blk) = 0; + virtual bool getBlockHeight(const Crypto::Hash& blockId, uint32_t& blockHeight) = 0; + virtual void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) = 0; + virtual bool getBackwardBlocksSizes(uint32_t fromHeight, std::vector& sizes, size_t count) = 0; + virtual bool getBlockSize(const Crypto::Hash& hash, size_t& size) = 0; + virtual bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins) = 0; + virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, + bool penalizeFee, uint64_t& reward, int64_t& emissionChange) = 0; + virtual bool scanOutputkeysForIndices(const KeyInput& txInToKey, std::list>& outputReferences) = 0; + virtual bool getBlockDifficulty(uint32_t height, difficulty_type& difficulty) = 0; + virtual bool getBlockContainingTx(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight) = 0; + virtual bool getMultisigOutputReference(const MultisignatureInput& txInMultisig, std::pair& outputReference) = 0; + + virtual bool getGeneratedTransactionsNumber(uint32_t height, uint64_t& generatedTransactions) = 0; + virtual bool getOrphanBlocksByHeight(uint32_t height, std::vector& blocks) = 0; + virtual bool getBlocksByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) = 0; + virtual bool getPoolTransactionsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) = 0; + virtual bool getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) = 0; + + virtual std::unique_ptr getBlock(const Crypto::Hash& blocksId) = 0; + virtual bool handleIncomingTransaction(const Transaction& tx, const Crypto::Hash& txHash, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) = 0; + virtual std::error_code executeLocked(const std::function& func) = 0; + + virtual bool addMessageQueue(MessageQueue& messageQueue) = 0; + virtual bool removeMessageQueue(MessageQueue& messageQueue) = 0; +}; + +} //namespace CryptoNote diff --git a/src/cryptonote_core/ICoreObserver.h b/src/CryptoNoteCore/ICoreObserver.h similarity index 100% rename from src/cryptonote_core/ICoreObserver.h rename to src/CryptoNoteCore/ICoreObserver.h diff --git a/src/cryptonote_core/i_miner_handler.h b/src/CryptoNoteCore/IMinerHandler.h old mode 100644 new mode 100755 similarity index 86% rename from src/cryptonote_core/i_miner_handler.h rename to src/CryptoNoteCore/IMinerHandler.h index 8cf205986a..0d1eaa7838 --- a/src/cryptonote_core/i_miner_handler.h +++ b/src/CryptoNoteCore/IMinerHandler.h @@ -17,15 +17,15 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/Difficulty.h" namespace CryptoNote { - struct i_miner_handler { + struct IMinerHandler { virtual bool handle_block_found(Block& b) = 0; - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) = 0; + virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const BinaryArray& ex_nonce) = 0; protected: - ~i_miner_handler(){}; + ~IMinerHandler(){}; }; } diff --git a/src/cryptonote_core/ITimeProvider.cpp b/src/CryptoNoteCore/ITimeProvider.cpp similarity index 100% rename from src/cryptonote_core/ITimeProvider.cpp rename to src/CryptoNoteCore/ITimeProvider.cpp diff --git a/src/cryptonote_core/ITimeProvider.h b/src/CryptoNoteCore/ITimeProvider.h similarity index 100% rename from src/cryptonote_core/ITimeProvider.h rename to src/CryptoNoteCore/ITimeProvider.h diff --git a/src/cryptonote_core/ITransactionValidator.h b/src/CryptoNoteCore/ITransactionValidator.h old mode 100644 new mode 100755 similarity index 85% rename from src/cryptonote_core/ITransactionValidator.h rename to src/CryptoNoteCore/ITransactionValidator.h index 825ed009a9..b85ab48704 --- a/src/cryptonote_core/ITransactionValidator.h +++ b/src/CryptoNoteCore/ITransactionValidator.h @@ -17,13 +17,13 @@ #pragma once -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" namespace CryptoNote { struct BlockInfo { - uint64_t height; - crypto::hash id; + uint32_t height; + Crypto::Hash id; BlockInfo() { clear(); @@ -31,11 +31,11 @@ namespace CryptoNote { void clear() { height = 0; - id = CryptoNote::null_hash; + id = CryptoNote::NULL_HASH; } bool empty() const { - return id == CryptoNote::null_hash; + return id == CryptoNote::NULL_HASH; } }; @@ -46,6 +46,7 @@ namespace CryptoNote { virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock) = 0; virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed) = 0; virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx) = 0; + virtual bool checkTransactionSize(size_t blobSize) = 0; }; } diff --git a/src/cryptonote_core/ITxPoolObserver.h b/src/CryptoNoteCore/ITxPoolObserver.h similarity index 100% rename from src/cryptonote_core/ITxPoolObserver.h rename to src/CryptoNoteCore/ITxPoolObserver.h diff --git a/src/CryptoNoteCore/IntrusiveLinkedList.h b/src/CryptoNoteCore/IntrusiveLinkedList.h new file mode 100644 index 0000000000..1f5320f9e1 --- /dev/null +++ b/src/CryptoNoteCore/IntrusiveLinkedList.h @@ -0,0 +1,211 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace CryptoNote { + +//Value must have public method IntrusiveLinkedList::hook& getHook() +template class IntrusiveLinkedList { +public: + class hook { + public: + friend class IntrusiveLinkedList; + + hook(); + private: + Value* prev; + Value* next; + bool used; + }; + + class iterator { + public: + iterator(Value* value); + + bool operator!=(const iterator& other) const; + bool operator==(const iterator& other) const; + iterator& operator++(); + iterator operator++(int); + iterator& operator--(); + iterator operator--(int); + + Value& operator*() const; + Value* operator->() const; + + private: + Value* currentElement; + }; + + IntrusiveLinkedList(); + + bool insert(Value& value); + bool remove(Value& value); + + bool empty() const; + + iterator begin(); + iterator end(); +private: + Value* head; + Value* tail; +}; + +template +IntrusiveLinkedList::IntrusiveLinkedList() : head(nullptr), tail(nullptr) {} + +template +bool IntrusiveLinkedList::insert(Value& value) { + if (!value.getHook().used) { + if (head == nullptr) { + head = &value; + tail = head; + value.getHook().prev = nullptr; + } else { + tail->getHook().next = &value; + value.getHook().prev = tail; + tail = &value; + } + + value.getHook().next = nullptr; + value.getHook().used = true; + return true; + } else { + return false; + } +} + +template +bool IntrusiveLinkedList::remove(Value& value) { + if (value.getHook().used && head != nullptr) { + Value* toRemove = &value; + Value* current = head; + while (current->getHook().next != nullptr) { + if (toRemove == current) { + break; + } + + current = current->getHook().next; + } + + if (toRemove == current) { + if (current->getHook().prev == nullptr) { + assert(current == head); + head = current->getHook().next; + + if (head != nullptr) { + head->getHook().prev = nullptr; + } else { + tail = nullptr; + } + } else { + current->getHook().prev->getHook().next = current->getHook().next; + if (current->getHook().next != nullptr) { + current->getHook().next->getHook().prev = current->getHook().prev; + } else { + tail = current->getHook().prev; + } + } + + current->getHook().prev = nullptr; + current->getHook().next = nullptr; + current->getHook().used = false; + return true; + } else { + return false; + } + } else { + return false; + } +} + +template +bool IntrusiveLinkedList::empty() const { + return head == nullptr; +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::begin() { + return iterator(head); +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::end() { + return iterator(nullptr); +} + +template +IntrusiveLinkedList::hook::hook() : prev(nullptr), next(nullptr), used(false) {} + +template +IntrusiveLinkedList::iterator::iterator(Value* value) : currentElement(value) {} + +template +bool IntrusiveLinkedList::iterator::operator!=(const typename IntrusiveLinkedList::iterator& other) const { + return currentElement != other.currentElement; +} + +template +bool IntrusiveLinkedList::iterator::operator==(const typename IntrusiveLinkedList::iterator& other) const { + return currentElement == other.currentElement; +} + +template +typename IntrusiveLinkedList::iterator& IntrusiveLinkedList::iterator::operator++() { + assert(currentElement != nullptr); + currentElement = currentElement->getHook().next; + return *this; +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::iterator::operator++(int) { + IntrusiveLinkedList::iterator copy = *this; + + assert(currentElement != nullptr); + currentElement = currentElement->getHook().next; + return copy; +} + +template +typename IntrusiveLinkedList::iterator& IntrusiveLinkedList::iterator::operator--() { + assert(currentElement != nullptr); + currentElement = currentElement->getHook().prev; + return *this; +} + +template +typename IntrusiveLinkedList::iterator IntrusiveLinkedList::iterator::operator--(int) { + IntrusiveLinkedList::iterator copy = *this; + + assert(currentElement != nullptr); + currentElement = currentElement->getHook().prev; + return copy; +} + +template +Value& IntrusiveLinkedList::iterator::operator*() const { + assert(currentElement != nullptr); + + return *currentElement; +} + +template +Value* IntrusiveLinkedList::iterator::operator->() const { + return currentElement; +} + +} diff --git a/src/CryptoNoteCore/MessageQueue.h b/src/CryptoNoteCore/MessageQueue.h new file mode 100644 index 0000000000..6be5f38f57 --- /dev/null +++ b/src/CryptoNoteCore/MessageQueue.h @@ -0,0 +1,114 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "IntrusiveLinkedList.h" + +#include "System/Event.h" +#include "System/InterruptedException.h" + +namespace CryptoNote { + +template class MessageQueue { +public: + MessageQueue(System::Dispatcher& dispatcher); + + const MessageType& front(); + void pop(); + void push(const MessageType& message); + + void stop(); + + typename IntrusiveLinkedList>::hook& getHook(); + +private: + void wait(); + std::queue messageQueue; + System::Event event; + bool stopped; + + typename IntrusiveLinkedList>::hook hook; +}; + +template +class MesageQueueGuard { +public: + MesageQueueGuard(MessageQueueContainer& container, MessageQueue& messageQueue) : container(container), messageQueue(messageQueue) { + container.addMessageQueue(messageQueue); + } + + MesageQueueGuard(const MesageQueueGuard& other) = delete; + MesageQueueGuard& operator=(const MesageQueueGuard& other) = delete; + + ~MesageQueueGuard() { + container.removeMessageQueue(messageQueue); + } +private: + MessageQueueContainer& container; + MessageQueue& messageQueue; +}; + +template +MessageQueue::MessageQueue(System::Dispatcher& dispatcher) : event(dispatcher), stopped(false) {} + +template +void MessageQueue::wait() { + if (messageQueue.empty()) { + if (stopped) { + throw System::InterruptedException(); + } + + event.clear(); + while (!event.get()) { + event.wait(); + } + } +} + +template +const MessageType& MessageQueue::front() { + wait(); + return messageQueue.front(); +} + +template +void MessageQueue::pop() { + wait(); + messageQueue.pop(); +} + +template +void MessageQueue::push(const MessageType& message) { + messageQueue.push(message); + event.set(); +} + +template +void MessageQueue::stop() { + stopped = true; + event.set(); +} + +template +typename IntrusiveLinkedList>::hook& MessageQueue::getHook() { + return hook; +} + +} diff --git a/src/cryptonote_core/miner.cpp b/src/CryptoNoteCore/Miner.cpp similarity index 90% rename from src/cryptonote_core/miner.cpp rename to src/CryptoNoteCore/Miner.cpp index 8c7cddd075..e083107593 100644 --- a/src/cryptonote_core/miner.cpp +++ b/src/CryptoNoteCore/Miner.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "miner.h" +#include "Miner.h" #include #include @@ -29,16 +29,20 @@ #include #include -#include "cryptonote_format_utils.h" -#include "Common/command_line.h" -#include "serialization/SerializationTools.h" +#include "crypto/crypto.h" +#include "Common/CommandLine.h" +#include "Common/StringTools.h" +#include "Serialization/SerializationTools.h" + +#include "CryptoNoteFormatUtils.h" +#include "TransactionExtra.h" using namespace Logging; namespace CryptoNote { - miner::miner(const Currency& currency, i_miner_handler& handler, Logging::ILogger& log) : + miner::miner(const Currency& currency, IMinerHandler& handler, Logging::ILogger& log) : m_currency(currency), logger(log, "miner"), m_stop(true), @@ -69,21 +73,21 @@ namespace CryptoNote m_template = bl; if (BLOCK_MAJOR_VERSION_2 == m_template.majorVersion) { - CryptoNote::tx_extra_merge_mining_tag mm_tag; + CryptoNote::TransactionExtraMergeMiningTag mm_tag; mm_tag.depth = 0; - if (!CryptoNote::get_aux_block_header_hash(m_template, mm_tag.merkle_root)) { + if (!CryptoNote::get_aux_block_header_hash(m_template, mm_tag.merkleRoot)) { return false; } - m_template.parentBlock.minerTx.extra.clear(); - if (!CryptoNote::append_mm_tag_to_extra(m_template.parentBlock.minerTx.extra, mm_tag)) { + m_template.parentBlock.baseTransaction.extra.clear(); + if (!CryptoNote::appendMergeMiningTagToExtra(m_template.parentBlock.baseTransaction.extra, mm_tag)) { return false; } } m_diffic = di; ++m_template_no; - m_starter_nonce = crypto::rand(); + m_starter_nonce = Crypto::rand(); return true; } //----------------------------------------------------------------------------------------------------- @@ -99,7 +103,7 @@ namespace CryptoNote Block bl = boost::value_initialized(); difficulty_type di = 0; uint32_t height; - CryptoNote::blobdata extra_nonce; + CryptoNote::BinaryArray extra_nonce; if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) { extra_nonce = m_extra_messages[m_config.current_extra_message_index]; @@ -175,9 +179,9 @@ namespace CryptoNote boost::algorithm::trim(extra_vec[i]); if(!extra_vec[i].size()) continue; - std::string buff = Common::base64Decode(extra_vec[i]); + BinaryArray ba = Common::asBinaryArray(Common::base64Decode(extra_vec[i])); if(buff != "0") - m_extra_messages[i] = buff; + m_extra_messages[i] = ba; } m_config_folder_path = boost::filesystem::path(config.extraMessages).parent_path().string(); m_config = boost::value_initialized(); @@ -210,7 +214,7 @@ namespace CryptoNote return !m_stop; } //----------------------------------------------------------------------------------------------------- - bool miner::start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs) + bool miner::start(const AccountPublicAddress& adr, size_t threads_count) { if (is_mining()) { logger(ERROR) << "Starting miner but it's already started"; @@ -226,7 +230,7 @@ namespace CryptoNote m_mine_address = adr; m_threads_total = static_cast(threads_count); - m_starter_nonce = crypto::rand(); + m_starter_nonce = Crypto::rand(); if (!m_template_no) { request_block_template(); //lets update block template @@ -235,7 +239,7 @@ namespace CryptoNote m_stop = false; for (uint32_t i = 0; i != threads_count; i++) { - m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this, i))); + m_threads.push_back(std::thread(std::bind(&miner::worker_thread, this, i))); } logger(INFO) << "Mining has started with " << threads_count << " threads, good luck!"; @@ -272,7 +276,7 @@ namespace CryptoNote return true; } //----------------------------------------------------------------------------------------------------- - bool miner::find_nonce_for_given_block(crypto::cn_context &context, Block& bl, const difficulty_type& diffic) { + bool miner::find_nonce_for_given_block(Crypto::cn_context &context, Block& bl, const difficulty_type& diffic) { unsigned nthreads = std::thread::hardware_concurrency(); @@ -280,12 +284,12 @@ namespace CryptoNote std::vector> threads(nthreads); std::atomic foundNonce; std::atomic found(false); - uint32_t startNonce = crypto::rand(); + uint32_t startNonce = Crypto::rand(); for (unsigned i = 0; i < nthreads; ++i) { threads[i] = std::async(std::launch::async, [&, i]() { - crypto::cn_context localctx; - crypto::hash h; + Crypto::cn_context localctx; + Crypto::Hash h; Block lb(bl); // copy to local block @@ -316,7 +320,7 @@ namespace CryptoNote return found; } else { for (; bl.nonce != std::numeric_limits::max(); bl.nonce++) { - crypto::hash h; + Crypto::Hash h; if (!get_block_longhash(context, bl, h)) { return false; } @@ -332,12 +336,8 @@ namespace CryptoNote //----------------------------------------------------------------------------------------------------- void miner::on_synchronized() { - if(m_do_mining) - { - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - start(m_mine_address, m_threads_total, attrs); + if(m_do_mining) { + start(m_mine_address, m_threads_total); } } //----------------------------------------------------------------------------------------------------- @@ -368,7 +368,7 @@ namespace CryptoNote uint32_t nonce = m_starter_nonce + th_local_index; difficulty_type local_diff = 0; uint32_t local_template_ver = 0; - crypto::cn_context context; + Crypto::cn_context context; Block b; while(!m_stop) @@ -397,7 +397,7 @@ namespace CryptoNote } b.nonce = nonce; - crypto::hash h; + Crypto::Hash h; if (!m_stop && !get_block_longhash(context, b, h)) { logger(ERROR) << "Failed to get block long hash"; m_stop = true; diff --git a/src/cryptonote_core/miner.h b/src/CryptoNoteCore/Miner.h old mode 100644 new mode 100755 similarity index 79% rename from src/cryptonote_core/miner.h rename to src/CryptoNoteCore/Miner.h index c52a195e60..c40ee42561 --- a/src/cryptonote_core/miner.h +++ b/src/CryptoNoteCore/Miner.h @@ -18,31 +18,31 @@ #pragma once #include +#include +#include +#include -#include -#include - -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_core/i_miner_handler.h" -#include "cryptonote_core/MinerConfig.h" -#include "cryptonote_core/OnceInInterval.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/Difficulty.h" +#include "CryptoNoteCore/IMinerHandler.h" +#include "CryptoNoteCore/MinerConfig.h" +#include "CryptoNoteCore/OnceInInterval.h" #include -#include "serialization/ISerializer.h" +#include "Serialization/ISerializer.h" namespace CryptoNote { class miner { public: - miner(const Currency& currency, i_miner_handler& handler, Logging::ILogger& log); + miner(const Currency& currency, IMinerHandler& handler, Logging::ILogger& log); ~miner(); bool init(const MinerConfig& config); bool set_block_template(const Block& bl, const difficulty_type& diffic); bool on_block_chain_update(); - bool start(const AccountPublicAddress& adr, size_t threads_count, const boost::thread::attributes& attrs); + bool start(const AccountPublicAddress& adr, size_t threads_count); uint64_t get_speed(); void send_stop_signal(); bool stop(); @@ -50,7 +50,7 @@ namespace CryptoNote { bool on_idle(); void on_synchronized(); //synchronous analog (for fast calls) - static bool find_nonce_for_given_block(crypto::cn_context &context, Block& bl, const difficulty_type& diffic); + static bool find_nonce_for_given_block(Crypto::cn_context &context, Block& bl, const difficulty_type& diffic); void pause(); void resume(); void do_print_hashrate(bool do_hr); @@ -82,14 +82,14 @@ namespace CryptoNote { std::atomic m_pausers_count; std::mutex m_miners_count_lock; - std::list m_threads; + std::list m_threads; std::mutex m_threads_lock; - i_miner_handler& m_handler; + IMinerHandler& m_handler; AccountPublicAddress m_mine_address; OnceInInterval m_update_block_template_interval; OnceInInterval m_update_merge_hr_interval; - std::vector m_extra_messages; + std::vector m_extra_messages; miner_config m_config; std::string m_config_folder_path; std::atomic m_last_hr_merge_time; diff --git a/src/cryptonote_core/MinerConfig.cpp b/src/CryptoNoteCore/MinerConfig.cpp similarity index 97% rename from src/cryptonote_core/MinerConfig.cpp rename to src/CryptoNoteCore/MinerConfig.cpp index 7290f3d0fa..2ef6dfc9ef 100644 --- a/src/cryptonote_core/MinerConfig.cpp +++ b/src/CryptoNoteCore/MinerConfig.cpp @@ -17,7 +17,7 @@ #include "MinerConfig.h" -#include "Common/command_line.h" +#include "Common/CommandLine.h" namespace CryptoNote { @@ -51,4 +51,4 @@ void MinerConfig::init(const boost::program_options::variables_map& options) { } } -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/MinerConfig.h b/src/CryptoNoteCore/MinerConfig.h old mode 100644 new mode 100755 similarity index 97% rename from src/cryptonote_core/MinerConfig.h rename to src/CryptoNoteCore/MinerConfig.h index a220dd29e3..698888adc2 --- a/src/cryptonote_core/MinerConfig.h +++ b/src/CryptoNoteCore/MinerConfig.h @@ -36,4 +36,4 @@ class MinerConfig { uint32_t miningThreads; }; -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/cryptonote_core/OnceInInterval.h b/src/CryptoNoteCore/OnceInInterval.h similarity index 100% rename from src/cryptonote_core/OnceInInterval.h rename to src/CryptoNoteCore/OnceInInterval.h diff --git a/src/cryptonote_core/SwappedMap.cpp b/src/CryptoNoteCore/SwappedMap.cpp similarity index 94% rename from src/cryptonote_core/SwappedMap.cpp rename to src/CryptoNoteCore/SwappedMap.cpp index f7664278b9..9f4e97f6a1 100755 --- a/src/cryptonote_core/SwappedMap.cpp +++ b/src/CryptoNoteCore/SwappedMap.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "SwappedMap.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/cryptonote_core/SwappedMap.h b/src/CryptoNoteCore/SwappedMap.h similarity index 97% rename from src/cryptonote_core/SwappedMap.h rename to src/CryptoNoteCore/SwappedMap.h index e9082c8657..aba27ec5c1 100755 --- a/src/cryptonote_core/SwappedMap.h +++ b/src/CryptoNoteCore/SwappedMap.h @@ -109,6 +109,7 @@ template class SwappedMap { std::unordered_map::iterator> m_cacheIterators; uint64_t m_cacheHits; uint64_t m_cacheMisses; + uint64_t descriptorsCounter; std::pair* prepare(const Key& key); const std::pair* load(const Key& key, uint64_t offset); @@ -125,6 +126,7 @@ template bool SwappedMap::open(const std::string& it if (poolSize == 0) { return false; } + descriptorsCounter = 0; m_itemsFile.open(itemFileName, std::ios::in | std::ios::out | std::ios::binary); m_indexesFile.open(indexFileName, std::ios::in | std::ios::out | std::ios::binary); @@ -160,7 +162,7 @@ template bool SwappedMap::open(const std::string& it Descriptor descriptor = { itemsFileSize, i }; descriptors.insert(std::make_pair(key, descriptor)); } - + descriptorsCounter++; itemsFileSize += itemSize; } @@ -233,6 +235,7 @@ template void SwappedMap::clear() { m_items.clear(); m_cache.clear(); m_cacheIterators.clear(); + descriptorsCounter = 0; } template void SwappedMap::erase(const_iterator iterator) { @@ -281,7 +284,7 @@ template std::pair::const_iterat throw std::runtime_error("SwappedMap::insert"); } - m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * m_descriptors.size()); + m_indexesFile.seekp(sizeof(uint64_t) + (sizeof(bool) + sizeof(Key) + sizeof(uint32_t)) * descriptorsCounter); bool valid = true; m_indexesFile.write(reinterpret_cast(&valid), sizeof valid); if (!m_indexesFile) { @@ -300,17 +303,20 @@ template std::pair::const_iterat } m_indexesFile.seekp(0); - uint64_t count = m_descriptors.size() + 1; + uint64_t count = descriptorsCounter + 1; m_indexesFile.write(reinterpret_cast(&count), sizeof count); if (!m_indexesFile) { throw std::runtime_error("SwappedMap::insert"); } + } - Descriptor descriptor = { m_itemsFileSize, m_descriptors.size() }; + Descriptor descriptor = { m_itemsFileSize, descriptorsCounter }; auto descriptorsInsert = m_descriptors.insert(std::make_pair(value.first, descriptor)); m_itemsFileSize = itemsFileSize; + descriptorsCounter++; + T* newItem = &prepare(value.first)->second; *newItem = value.second; return std::make_pair(const_iterator(this, descriptorsInsert.first), true); diff --git a/src/cryptonote_core/SwappedVector.cpp b/src/CryptoNoteCore/SwappedVector.cpp similarity index 94% rename from src/cryptonote_core/SwappedVector.cpp rename to src/CryptoNoteCore/SwappedVector.cpp index 44a24553c7..2fb3778dcc 100755 --- a/src/cryptonote_core/SwappedVector.cpp +++ b/src/CryptoNoteCore/SwappedVector.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "SwappedVector.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/cryptonote_core/SwappedVector.h b/src/CryptoNoteCore/SwappedVector.h similarity index 93% rename from src/cryptonote_core/SwappedVector.h rename to src/CryptoNoteCore/SwappedVector.h index 82e4318036..88df10f83e 100755 --- a/src/cryptonote_core/SwappedVector.h +++ b/src/CryptoNoteCore/SwappedVector.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include #include @@ -25,7 +26,11 @@ #include #include #include -#include "serialization/binary_archive.h" + +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" template class SwappedVector { public: @@ -42,7 +47,7 @@ template class SwappedVector { const_iterator() { } - const_iterator(SwappedVector* swappedVector, std::size_t index) : m_swappedVector(swappedVector), m_index(index) { + const_iterator(SwappedVector* swappedVector, size_t index) : m_swappedVector(swappedVector), m_index(index) { } bool operator!=(const const_iterator& other) const { @@ -129,13 +134,13 @@ template class SwappedVector { return (*m_swappedVector)[m_index + offset]; } - std::size_t index() const { + size_t index() const { return m_index; } private: SwappedVector* m_swappedVector; - std::size_t m_index; + size_t m_index; }; SwappedVector(); @@ -287,10 +292,10 @@ template const T& SwappedVector::operator[](uint64_t index) { m_itemsFile.seekg(m_offsets[index]); T tempItem; - binary_archive archive(m_itemsFile); - if (!do_serialize(archive, tempItem)) { - throw std::runtime_error("SwappedVector::operator[]"); - } + + Common::StdInputStream stream(m_itemsFile); + CryptoNote::BinaryInputStreamSerializer archive(stream); + serialize(tempItem, archive); T* item = prepare(index); std::swap(tempItem, *item); @@ -354,10 +359,10 @@ template void SwappedVector::push_back(const T& item) { } m_itemsFile.seekp(m_itemsFileSize); - binary_archive archive(m_itemsFile); - if (!do_serialize(archive, *const_cast(&item))) { - throw std::runtime_error("SwappedVector::push_back"); - } + + Common::StdOutputStream stream(m_itemsFile); + CryptoNote::BinaryOutputStreamSerializer archive(stream); + serialize(const_cast(item), archive); itemsFileSize = m_itemsFile.tellp(); } diff --git a/src/CryptoNoteCore/Transaction.cpp b/src/CryptoNoteCore/Transaction.cpp new file mode 100755 index 0000000000..3af50e2d86 --- /dev/null +++ b/src/CryptoNoteCore/Transaction.cpp @@ -0,0 +1,532 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ITransaction.h" +#include "TransactionApiExtra.h" +#include "TransactionUtils.h" + +#include "Account.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteConfig.h" + +#include +#include +#include + +using namespace Crypto; + +namespace { + + using namespace CryptoNote; + + void derivePublicKey(const AccountPublicAddress& to, const SecretKey& txKey, size_t outputIndex, PublicKey& ephemeralKey) { + KeyDerivation derivation; + generate_key_derivation(to.viewPublicKey, txKey, derivation); + derive_public_key(derivation, outputIndex, to.spendPublicKey, ephemeralKey); + } + +} + +namespace CryptoNote { + + using namespace Crypto; + + //////////////////////////////////////////////////////////////////////// + // class Transaction declaration + //////////////////////////////////////////////////////////////////////// + + class TransactionImpl : public ITransaction { + public: + TransactionImpl(); + TransactionImpl(const BinaryArray& txblob); + TransactionImpl(const CryptoNote::Transaction& tx); + + // ITransactionReader + virtual Hash getTransactionHash() const override; + virtual Hash getTransactionPrefixHash() const override; + virtual PublicKey getTransactionPublicKey() const override; + virtual uint64_t getUnlockTime() const override; + virtual bool getPaymentId(Hash& hash) const override; + virtual bool getExtraNonce(BinaryArray& nonce) const override; + virtual BinaryArray getExtra() const override; + + // inputs + virtual size_t getInputCount() const override; + virtual uint64_t getInputTotalAmount() const override; + virtual TransactionTypes::InputType getInputType(size_t index) const override; + virtual void getInput(size_t index, KeyInput& input) const override; + virtual void getInput(size_t index, MultisignatureInput& input) const override; + + // outputs + virtual size_t getOutputCount() const override; + virtual uint64_t getOutputTotalAmount() const override; + virtual TransactionTypes::OutputType getOutputType(size_t index) const override; + virtual void getOutput(size_t index, KeyOutput& output, uint64_t& amount) const override; + virtual void getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const override; + + virtual size_t getRequiredSignaturesCount(size_t index) const override; + virtual bool findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; + + // various checks + virtual bool validateInputs() const override; + virtual bool validateOutputs() const override; + virtual bool validateSignatures() const override; + + // get serialized transaction + virtual BinaryArray getTransactionData() const override; + + // ITransactionWriter + + virtual void setUnlockTime(uint64_t unlockTime) override; + virtual void setPaymentId(const Hash& hash) override; + virtual void setExtraNonce(const BinaryArray& nonce) override; + virtual void appendExtra(const BinaryArray& extraData) override; + + // Inputs/Outputs + virtual size_t addInput(const KeyInput& input) override; + virtual size_t addInput(const MultisignatureInput& input) override; + virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) override; + + virtual size_t addOutput(uint64_t amount, const AccountPublicAddress& to) override; + virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) override; + virtual size_t addOutput(uint64_t amount, const KeyOutput& out) override; + virtual size_t addOutput(uint64_t amount, const MultisignatureOutput& out) override; + + virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) override; + virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) override; + virtual void signInputMultisignature(size_t input, const KeyPair& ephemeralKeys) override; + + + // secret key + virtual bool getTransactionSecretKey(SecretKey& key) const override; + virtual void setTransactionSecretKey(const SecretKey& key) override; + + private: + + void invalidateHash(); + + std::vector& getSignatures(size_t input); + + const SecretKey& txSecretKey() const { + if (!secretKey) { + throw std::runtime_error("Operation requires transaction secret key"); + } + return *secretKey; + } + + void checkIfSigning() const { + if (!transaction.signatures.empty()) { + throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); + } + } + + CryptoNote::Transaction transaction; + boost::optional secretKey; + mutable boost::optional transactionHash; + TransactionExtra extra; + }; + + + //////////////////////////////////////////////////////////////////////// + // class Transaction implementation + //////////////////////////////////////////////////////////////////////// + + std::unique_ptr createTransaction() { + return std::unique_ptr(new TransactionImpl()); + } + + std::unique_ptr createTransaction(const BinaryArray& transactionBlob) { + return std::unique_ptr(new TransactionImpl(transactionBlob)); + } + + std::unique_ptr createTransaction(const CryptoNote::Transaction& tx) { + return std::unique_ptr(new TransactionImpl(tx)); + } + + TransactionImpl::TransactionImpl() { + CryptoNote::KeyPair txKeys(CryptoNote::generateKeyPair()); + + TransactionExtraPublicKey pk = { txKeys.publicKey }; + extra.set(pk); + + transaction.version = CURRENT_TRANSACTION_VERSION; + transaction.unlockTime = 0; + transaction.extra = extra.serialize(); + + secretKey = txKeys.secretKey; + } + + TransactionImpl::TransactionImpl(const BinaryArray& ba) { + if (!fromBinaryArray(transaction, ba)) { + throw std::runtime_error("Invalid transaction data"); + } + + extra.parse(transaction.extra); + transactionHash = getBinaryArrayHash(ba); // avoid serialization if we already have blob + } + + TransactionImpl::TransactionImpl(const CryptoNote::Transaction& tx) : transaction(tx) { + extra.parse(transaction.extra); + } + + void TransactionImpl::invalidateHash() { + if (transactionHash.is_initialized()) { + transactionHash = decltype(transactionHash)(); + } + } + + Hash TransactionImpl::getTransactionHash() const { + if (!transactionHash.is_initialized()) { + transactionHash = getObjectHash(transaction); + } + + return transactionHash.get(); + } + + Hash TransactionImpl::getTransactionPrefixHash() const { + return getObjectHash(*static_cast(&transaction)); + } + + PublicKey TransactionImpl::getTransactionPublicKey() const { + PublicKey pk(NULL_PUBLIC_KEY); + extra.getPublicKey(pk); + return pk; + } + + uint64_t TransactionImpl::getUnlockTime() const { + return transaction.unlockTime; + } + + void TransactionImpl::setUnlockTime(uint64_t unlockTime) { + checkIfSigning(); + transaction.unlockTime = unlockTime; + invalidateHash(); + } + + bool TransactionImpl::getTransactionSecretKey(SecretKey& key) const { + if (!secretKey) { + return false; + } + key = reinterpret_cast(secretKey.get()); + return true; + } + + void TransactionImpl::setTransactionSecretKey(const SecretKey& key) { + const auto& sk = reinterpret_cast(key); + PublicKey pk; + PublicKey txPubKey; + + secret_key_to_public_key(sk, pk); + extra.getPublicKey(txPubKey); + + if (txPubKey != pk) { + throw std::runtime_error("Secret transaction key does not match public key"); + } + + secretKey = key; + } + + size_t TransactionImpl::addInput(const KeyInput& input) { + checkIfSigning(); + transaction.inputs.emplace_back(input); + invalidateHash(); + return transaction.inputs.size() - 1; + } + + size_t TransactionImpl::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, KeyPair& ephKeys) { + checkIfSigning(); + KeyInput input; + input.amount = info.amount; + + generate_key_image_helper( + senderKeys, + info.realOutput.transactionPublicKey, + info.realOutput.outputInTransaction, + ephKeys, + input.keyImage); + + // fill outputs array and use relative offsets + for (const auto& out : info.outputs) { + input.outputIndexes.push_back(out.outputIndex); + } + + input.outputIndexes = absolute_output_offsets_to_relative(input.outputIndexes); + return addInput(input); + } + + size_t TransactionImpl::addInput(const MultisignatureInput& input) { + checkIfSigning(); + transaction.inputs.push_back(input); + invalidateHash(); + return transaction.inputs.size() - 1; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const AccountPublicAddress& to) { + checkIfSigning(); + + KeyOutput outKey; + derivePublicKey(to, txSecretKey(), transaction.outputs.size(), outKey.key); + TransactionOutput out = { amount, outKey }; + transaction.outputs.emplace_back(out); + invalidateHash(); + + return transaction.outputs.size() - 1; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { + checkIfSigning(); + + const auto& txKey = txSecretKey(); + size_t outputIndex = transaction.outputs.size(); + MultisignatureOutput outMsig; + outMsig.requiredSignatureCount = requiredSignatures; + outMsig.keys.resize(to.size()); + + for (size_t i = 0; i < to.size(); ++i) { + derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); + } + + TransactionOutput out = { amount, outMsig }; + transaction.outputs.emplace_back(out); + invalidateHash(); + + return outputIndex; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const KeyOutput& out) { + checkIfSigning(); + size_t outputIndex = transaction.outputs.size(); + TransactionOutput realOut = { amount, out }; + transaction.outputs.emplace_back(realOut); + invalidateHash(); + return outputIndex; + } + + size_t TransactionImpl::addOutput(uint64_t amount, const MultisignatureOutput& out) { + checkIfSigning(); + size_t outputIndex = transaction.outputs.size(); + TransactionOutput realOut = { amount, out }; + transaction.outputs.emplace_back(realOut); + invalidateHash(); + return outputIndex; + } + + void TransactionImpl::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const KeyPair& ephKeys) { + const auto& input = boost::get(getInputChecked(transaction, index, TransactionTypes::InputType::Key)); + Hash prefixHash = getTransactionPrefixHash(); + + std::vector signatures; + std::vector keysPtrs; + + for (const auto& o : info.outputs) { + keysPtrs.push_back(reinterpret_cast(&o.targetKey)); + } + + signatures.resize(keysPtrs.size()); + + generate_ring_signature( + reinterpret_cast(prefixHash), + reinterpret_cast(input.keyImage), + keysPtrs, + reinterpret_cast(ephKeys.secretKey), + info.realOutput.transactionIndex, + signatures.data()); + + getSignatures(index) = signatures; + invalidateHash(); + } + + void TransactionImpl::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { + KeyDerivation derivation; + PublicKey ephemeralPublicKey; + SecretKey ephemeralSecretKey; + + generate_key_derivation( + reinterpret_cast(sourceTransactionKey), + reinterpret_cast(accountKeys.viewSecretKey), + derivation); + + derive_public_key(derivation, outputIndex, + reinterpret_cast(accountKeys.address.spendPublicKey), ephemeralPublicKey); + derive_secret_key(derivation, outputIndex, + reinterpret_cast(accountKeys.spendSecretKey), ephemeralSecretKey); + + Signature signature; + auto txPrefixHash = getTransactionPrefixHash(); + + generate_signature(reinterpret_cast(txPrefixHash), + ephemeralPublicKey, ephemeralSecretKey, signature); + + getSignatures(index).push_back(signature); + invalidateHash(); + } + + void TransactionImpl::signInputMultisignature(size_t index, const KeyPair& ephemeralKeys) { + Signature signature; + auto txPrefixHash = getTransactionPrefixHash(); + + generate_signature(txPrefixHash, ephemeralKeys.publicKey, ephemeralKeys.secretKey, signature); + + getSignatures(index).push_back(signature); + invalidateHash(); + } + + std::vector& TransactionImpl::getSignatures(size_t input) { + // update signatures container size if needed + if (transaction.signatures.size() < transaction.inputs.size()) { + transaction.signatures.resize(transaction.inputs.size()); + } + // check range + if (input >= transaction.signatures.size()) { + throw std::runtime_error("Invalid input index"); + } + + return transaction.signatures[input]; + } + + BinaryArray TransactionImpl::getTransactionData() const { + return toBinaryArray(transaction); + } + + void TransactionImpl::setPaymentId(const Hash& hash) { + checkIfSigning(); + BinaryArray paymentIdBlob; + setPaymentIdToTransactionExtraNonce(paymentIdBlob, reinterpret_cast(hash)); + setExtraNonce(paymentIdBlob); + } + + bool TransactionImpl::getPaymentId(Hash& hash) const { + BinaryArray nonce; + if (getExtraNonce(nonce)) { + Hash paymentId; + if (getPaymentIdFromTransactionExtraNonce(nonce, paymentId)) { + hash = reinterpret_cast(paymentId); + return true; + } + } + return false; + } + + void TransactionImpl::setExtraNonce(const BinaryArray& nonce) { + checkIfSigning(); + TransactionExtraNonce extraNonce = { nonce }; + extra.set(extraNonce); + transaction.extra = extra.serialize(); + invalidateHash(); + } + + void TransactionImpl::appendExtra(const BinaryArray& extraData) { + checkIfSigning(); + transaction.extra.insert( + transaction.extra.end(), extraData.begin(), extraData.end()); + } + + bool TransactionImpl::getExtraNonce(BinaryArray& nonce) const { + TransactionExtraNonce extraNonce; + if (extra.get(extraNonce)) { + nonce = extraNonce.nonce; + return true; + } + return false; + } + + BinaryArray TransactionImpl::getExtra() const { + return transaction.extra; + } + + size_t TransactionImpl::getInputCount() const { + return transaction.inputs.size(); + } + + uint64_t TransactionImpl::getInputTotalAmount() const { + return std::accumulate(transaction.inputs.begin(), transaction.inputs.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { + return val + getTransactionInputAmount(in); }); + } + + TransactionTypes::InputType TransactionImpl::getInputType(size_t index) const { + return getTransactionInputType(getInputChecked(transaction, index)); + } + + void TransactionImpl::getInput(size_t index, KeyInput& input) const { + input = boost::get(getInputChecked(transaction, index, TransactionTypes::InputType::Key)); + } + + void TransactionImpl::getInput(size_t index, MultisignatureInput& input) const { + input = boost::get(getInputChecked(transaction, index, TransactionTypes::InputType::Multisignature)); + } + + size_t TransactionImpl::getOutputCount() const { + return transaction.outputs.size(); + } + + uint64_t TransactionImpl::getOutputTotalAmount() const { + return std::accumulate(transaction.outputs.begin(), transaction.outputs.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { + return val + out.amount; }); + } + + TransactionTypes::OutputType TransactionImpl::getOutputType(size_t index) const { + return getTransactionOutputType(getOutputChecked(transaction, index).target); + } + + void TransactionImpl::getOutput(size_t index, KeyOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(transaction, index, TransactionTypes::OutputType::Key); + output = boost::get(out.target); + amount = out.amount; + } + + void TransactionImpl::getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(transaction, index, TransactionTypes::OutputType::Multisignature); + output = boost::get(out.target); + amount = out.amount; + } + + bool TransactionImpl::findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { + return ::CryptoNote::findOutputsToAccount(transaction, addr, viewSecretKey, out, amount); + } + + size_t TransactionImpl::getRequiredSignaturesCount(size_t index) const { + return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); + } + + bool TransactionImpl::validateInputs() const { + return + check_inputs_types_supported(transaction) && + check_inputs_overflow(transaction) && + checkInputsKeyimagesDiff(transaction) && + checkMultisignatureInputsDiff(transaction); + } + + bool TransactionImpl::validateOutputs() const { + return + check_outs_valid(transaction) && + check_outs_overflow(transaction); + } + + bool TransactionImpl::validateSignatures() const { + if (transaction.signatures.size() < transaction.inputs.size()) { + return false; + } + + for (size_t i = 0; i < transaction.inputs.size(); ++i) { + if (getRequiredSignaturesCount(i) > transaction.signatures[i].size()) { + return false; + } + } + + return true; + } +} diff --git a/src/cryptonote_core/TransactionApi.h b/src/CryptoNoteCore/TransactionApi.h old mode 100644 new mode 100755 similarity index 69% rename from src/cryptonote_core/TransactionApi.h rename to src/CryptoNoteCore/TransactionApi.h index c52eadf7fa..2753a2a3c9 --- a/src/cryptonote_core/TransactionApi.h +++ b/src/CryptoNoteCore/TransactionApi.h @@ -20,12 +20,11 @@ #include #include "ITransaction.h" -namespace CryptoNote { - struct Transaction; -} - namespace CryptoNote { std::unique_ptr createTransaction(); - std::unique_ptr createTransaction(const Blob& transactionBlob); - std::unique_ptr createTransaction(const CryptoNote::Transaction& tx); + std::unique_ptr createTransaction(const BinaryArray& transactionBlob); + std::unique_ptr createTransaction(const Transaction& tx); + + std::unique_ptr createTransactionPrefix(const TransactionPrefix& prefix, const Crypto::Hash& transactionHash); + std::unique_ptr createTransactionPrefix(const Transaction& fullTransaction); } diff --git a/src/cryptonote_core/TransactionExtra.h b/src/CryptoNoteCore/TransactionApiExtra.h old mode 100644 new mode 100755 similarity index 63% rename from src/cryptonote_core/TransactionExtra.h rename to src/CryptoNoteCore/TransactionApiExtra.h index 7b607bb963..07af198901 --- a/src/cryptonote_core/TransactionExtra.h +++ b/src/CryptoNoteCore/TransactionApiExtra.h @@ -17,17 +17,11 @@ #pragma once -#include "cryptonote_format_utils.h" +#include "CryptoNoteFormatUtils.h" +#include "TransactionExtra.h" namespace CryptoNote { - inline std::vector stringToVector(const std::string& s) { - std::vector vec( - reinterpret_cast(s.data()), - reinterpret_cast(s.data()) + s.size()); - return vec; - } - class TransactionExtra { public: TransactionExtra() {} @@ -37,7 +31,7 @@ namespace CryptoNote { bool parse(const std::vector& extra) { fields.clear(); - return CryptoNote::parse_tx_extra(extra, fields); + return CryptoNote::parseTransactionExtra(extra, fields); } template @@ -60,35 +54,37 @@ namespace CryptoNote { } } - bool getPublicKey(crypto::public_key& pk) const { - CryptoNote::tx_extra_pub_key extraPk; + template + void append(const T& value) { + fields.push_back(value); + } + + bool getPublicKey(Crypto::PublicKey& pk) const { + CryptoNote::TransactionExtraPublicKey extraPk; if (!get(extraPk)) { return false; } - pk = extraPk.pub_key; + pk = extraPk.publicKey; return true; } std::vector serialize() const { - std::ostringstream out; - binary_archive ar(out); - for (const auto& f : fields) { - ::do_serialize(ar, const_cast(f)); - } - return stringToVector(out.str()); + std::vector extra; + writeTransactionExtra(extra, fields); + return extra; } private: - std::vector::const_iterator find(const std::type_info& t) const { - return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::tx_extra_field& f) { return t == f.type(); }); + std::vector::const_iterator find(const std::type_info& t) const { + return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::TransactionExtraField& f) { return t == f.type(); }); } - std::vector::iterator find(const std::type_info& t) { - return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::tx_extra_field& f) { return t == f.type(); }); + std::vector::iterator find(const std::type_info& t) { + return std::find_if(fields.begin(), fields.end(), [&t](const CryptoNote::TransactionExtraField& f) { return t == f.type(); }); } - std::vector fields; + std::vector fields; }; } diff --git a/src/CryptoNoteCore/TransactionExtra.cpp b/src/CryptoNoteCore/TransactionExtra.cpp new file mode 100755 index 0000000000..cef604280f --- /dev/null +++ b/src/CryptoNoteCore/TransactionExtra.cpp @@ -0,0 +1,247 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransactionExtra.h" + +#include "Common/MemoryInputStream.h" +#include "Common/StreamTools.h" +#include "Common/StringTools.h" +#include "CryptoNoteTools.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +using namespace Crypto; +using namespace Common; + +namespace CryptoNote { + +bool parseTransactionExtra(const std::vector &transactionExtra, std::vector &transactionExtraFields) { + transactionExtraFields.clear(); + + if (transactionExtra.empty()) + return true; + + try { + MemoryInputStream iss(transactionExtra.data(), transactionExtra.size()); + BinaryInputStreamSerializer ar(iss); + + int c = 0; + + while (!iss.endOfStream()) { + c = read(iss); + switch (c) { + case TX_EXTRA_TAG_PADDING: { + size_t size = 1; + for (; !iss.endOfStream() && size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) { + if (read(iss) != 0) { + return false; // all bytes should be zero + } + } + + if (size > TX_EXTRA_PADDING_MAX_COUNT) { + return false; + } + + transactionExtraFields.push_back(TransactionExtraPadding{ size }); + break; + } + + case TX_EXTRA_TAG_PUBKEY: { + TransactionExtraPublicKey extraPk; + ar(extraPk.publicKey, "public_key"); + transactionExtraFields.push_back(extraPk); + break; + } + + case TX_EXTRA_NONCE: { + TransactionExtraNonce extraNonce; + uint8_t size = read(iss); + if (size > 0) { + extraNonce.nonce.resize(size); + read(iss, extraNonce.nonce.data(), extraNonce.nonce.size()); + } + + transactionExtraFields.push_back(extraNonce); + break; + } + + case TX_EXTRA_MERGE_MINING_TAG: { + TransactionExtraMergeMiningTag mmTag; + ar(mmTag, "mm_tag"); + transactionExtraFields.push_back(mmTag); + break; + } + } + } + } catch (std::exception &) { + return false; + } + + return true; +} + +struct ExtraSerializerVisitor : public boost::static_visitor { + std::vector& extra; + + ExtraSerializerVisitor(std::vector& tx_extra) + : extra(tx_extra) {} + + bool operator()(const TransactionExtraPadding& t) { + if (t.size > TX_EXTRA_PADDING_MAX_COUNT) { + return false; + } + extra.insert(extra.end(), t.size, 0); + return true; + } + + bool operator()(const TransactionExtraPublicKey& t) { + return addTransactionPublicKeyToExtra(extra, t.publicKey); + } + + bool operator()(const TransactionExtraNonce& t) { + return addExtraNonceToTransactionExtra(extra, t.nonce); + } + + bool operator()(const TransactionExtraMergeMiningTag& t) { + return appendMergeMiningTagToExtra(extra, t); + } +}; + +bool writeTransactionExtra(std::vector& tx_extra, const std::vector& tx_extra_fields) { + ExtraSerializerVisitor visitor(tx_extra); + + for (const auto& tag : tx_extra_fields) { + if (!boost::apply_visitor(visitor, tag)) { + return false; + } + } + + return true; +} + +PublicKey getTransactionPublicKeyFromExtra(const std::vector& tx_extra) { + std::vector tx_extra_fields; + parseTransactionExtra(tx_extra, tx_extra_fields); + + TransactionExtraPublicKey pub_key_field; + if (!findTransactionExtraFieldByType(tx_extra_fields, pub_key_field)) + return boost::value_initialized(); + + return pub_key_field.publicKey; +} + +bool addTransactionPublicKeyToExtra(std::vector& tx_extra, const PublicKey& tx_pub_key) { + tx_extra.resize(tx_extra.size() + 1 + sizeof(PublicKey)); + tx_extra[tx_extra.size() - 1 - sizeof(PublicKey)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast(&tx_extra[tx_extra.size() - sizeof(PublicKey)]) = tx_pub_key; + return true; +} + + +bool addExtraNonceToTransactionExtra(std::vector& tx_extra, const BinaryArray& extra_nonce) { + if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { + return false; + } + + size_t start_pos = tx_extra.size(); + tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); + //write tag + tx_extra[start_pos] = TX_EXTRA_NONCE; + //write len + ++start_pos; + tx_extra[start_pos] = static_cast(extra_nonce.size()); + //write data + ++start_pos; + memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; +} + +bool appendMergeMiningTagToExtra(std::vector& tx_extra, const TransactionExtraMergeMiningTag& mm_tag) { + BinaryArray blob; + if (!toBinaryArray(mm_tag, blob)) { + return false; + } + + tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); + std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); + return true; +} + +bool getMergeMiningTagFromExtra(const std::vector& tx_extra, TransactionExtraMergeMiningTag& mm_tag) { + std::vector tx_extra_fields; + parseTransactionExtra(tx_extra, tx_extra_fields); + + return findTransactionExtraFieldByType(tx_extra_fields, mm_tag); +} + +void setPaymentIdToTransactionExtraNonce(std::vector& extra_nonce, const Hash& payment_id) { + extra_nonce.clear(); + extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); + const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); + std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); +} + +bool getPaymentIdFromTransactionExtraNonce(const std::vector& extra_nonce, Hash& payment_id) { + if (sizeof(Hash) + 1 != extra_nonce.size()) + return false; + if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) + return false; + payment_id = *reinterpret_cast(extra_nonce.data() + 1); + return true; +} + +bool parsePaymentId(const std::string& paymentIdString, Hash& paymentId) { + return Common::podFromHex(paymentIdString, paymentId); +} + +bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { + Hash paymentIdBin; + + if (!parsePaymentId(paymentIdString, paymentIdBin)) { + return false; + } + + std::vector extraNonce; + CryptoNote::setPaymentIdToTransactionExtraNonce(extraNonce, paymentIdBin); + + if (!CryptoNote::addExtraNonceToTransactionExtra(extra, extraNonce)) { + return false; + } + + return true; +} + +bool getPaymentIdFromTxExtra(const std::vector& extra, Hash& paymentId) { + std::vector tx_extra_fields; + if (!parseTransactionExtra(extra, tx_extra_fields)) { + return false; + } + + TransactionExtraNonce extra_nonce; + if (findTransactionExtraFieldByType(tx_extra_fields, extra_nonce)) { + if (!getPaymentIdFromTransactionExtraNonce(extra_nonce.nonce, paymentId)) { + return false; + } + } else { + return false; + } + + return true; +} + + +} diff --git a/src/CryptoNoteCore/TransactionExtra.h b/src/CryptoNoteCore/TransactionExtra.h new file mode 100755 index 0000000000..66cc14480f --- /dev/null +++ b/src/CryptoNoteCore/TransactionExtra.h @@ -0,0 +1,90 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include + +#define TX_EXTRA_PADDING_MAX_COUNT 255 +#define TX_EXTRA_NONCE_MAX_COUNT 255 + +#define TX_EXTRA_TAG_PADDING 0x00 +#define TX_EXTRA_TAG_PUBKEY 0x01 +#define TX_EXTRA_NONCE 0x02 +#define TX_EXTRA_MERGE_MINING_TAG 0x03 + +#define TX_EXTRA_NONCE_PAYMENT_ID 0x00 + +namespace CryptoNote { + +struct TransactionExtraPadding { + size_t size; +}; + +struct TransactionExtraPublicKey { + Crypto::PublicKey publicKey; +}; + +struct TransactionExtraNonce { + std::vector nonce; +}; + +struct TransactionExtraMergeMiningTag { + size_t depth; + Crypto::Hash merkleRoot; +}; + +// tx_extra_field format, except tx_extra_padding and tx_extra_pub_key: +// varint tag; +// varint size; +// varint data[]; +typedef boost::variant TransactionExtraField; + + + +template +bool findTransactionExtraFieldByType(const std::vector& tx_extra_fields, T& field) { + auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), + [](const TransactionExtraField& f) { return typeid(T) == f.type(); }); + + if (tx_extra_fields.end() == it) + return false; + + field = boost::get(*it); + return true; +} + +bool parseTransactionExtra(const std::vector& tx_extra, std::vector& tx_extra_fields); +bool writeTransactionExtra(std::vector& tx_extra, const std::vector& tx_extra_fields); + +Crypto::PublicKey getTransactionPublicKeyFromExtra(const std::vector& tx_extra); +bool addTransactionPublicKeyToExtra(std::vector& tx_extra, const Crypto::PublicKey& tx_pub_key); +bool addExtraNonceToTransactionExtra(std::vector& tx_extra, const BinaryArray& extra_nonce); +void setPaymentIdToTransactionExtraNonce(BinaryArray& extra_nonce, const Crypto::Hash& payment_id); +bool getPaymentIdFromTransactionExtraNonce(const BinaryArray& extra_nonce, Crypto::Hash& payment_id); +bool appendMergeMiningTagToExtra(std::vector& tx_extra, const TransactionExtraMergeMiningTag& mm_tag); +bool getMergeMiningTagFromExtra(const std::vector& tx_extra, TransactionExtraMergeMiningTag& mm_tag); + +bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); +//returns false if payment id is not found or parse error +bool getPaymentIdFromTxExtra(const std::vector& extra, Crypto::Hash& paymentId); +bool parsePaymentId(const std::string& paymentIdString, Crypto::Hash& paymentId); + +} diff --git a/src/cryptonote_core/tx_pool.cpp b/src/CryptoNoteCore/TransactionPool.cpp similarity index 74% rename from src/cryptonote_core/tx_pool.cpp rename to src/CryptoNoteCore/TransactionPool.cpp index 5f5e8edf81..4c74e070ff 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/CryptoNoteCore/TransactionPool.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "tx_pool.h" +#include "TransactionPool.h" #include #include @@ -24,13 +24,16 @@ #include -#include "Common/boost_serialization_helper.h" #include "Common/int-util.h" -#include "Common/util.h" +#include "Common/Util.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_boost_serialization.h" -#include "cryptonote_config.h" + +#include "Serialization/SerializationTools.h" +#include "Serialization/BinarySerializationTools.h" + +#include "CryptoNoteFormatUtils.h" +#include "CryptoNoteTools.h" +#include "CryptoNoteConfig.h" using namespace Logging; @@ -44,17 +47,17 @@ namespace CryptoNote { class BlockTemplate { public: - bool addTransaction(const crypto::hash& txid, const Transaction& tx) { + bool addTransaction(const Crypto::Hash& txid, const Transaction& tx) { if (!canAdd(tx)) return false; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - auto r = m_keyImages.insert(boost::get(in).keyImage); + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + auto r = m_keyImages.insert(boost::get(in).keyImage); (void)r; //just to make compiler to shut up assert(r.second); - } else if (in.type() == typeid(TransactionInputMultisignature)) { - const auto& msig = boost::get(in); + } else if (in.type() == typeid(MultisignatureInput)) { + const auto& msig = boost::get(in); auto r = m_usedOutputs.insert(std::make_pair(msig.amount, msig.outputIndex)); (void)r; //just to make compiler to shut up assert(r.second); @@ -65,20 +68,20 @@ namespace CryptoNote { return true; } - const std::vector& getTransactions() const { + const std::vector& getTransactions() const { return m_txHashes; } private: bool canAdd(const Transaction& tx) { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (m_keyImages.count(boost::get(in).keyImage)) { + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (m_keyImages.count(boost::get(in).keyImage)) { return false; } - } else if (in.type() == typeid(TransactionInputMultisignature)) { - const auto& msig = boost::get(in); + } else if (in.type() == typeid(MultisignatureInput)) { + const auto& msig = boost::get(in); if (m_usedOutputs.count(std::make_pair(msig.amount, msig.outputIndex))) { return false; } @@ -87,9 +90,9 @@ namespace CryptoNote { return true; } - std::unordered_set m_keyImages; + std::unordered_set m_keyImages; std::set> m_usedOutputs; - std::vector m_txHashes; + std::vector m_txHashes; }; using CryptoNote::BlockInfo; @@ -109,7 +112,7 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::add_tx(const Transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { + bool tx_memory_pool::add_tx(const Transaction &tx, /*const Crypto::Hash& tx_prefix_hash,*/ const Crypto::Hash &id, size_t blobSize, tx_verification_context& tvc, bool keptByBlock) { if (!check_inputs_types_supported(tx)) { tvc.m_verifivation_failed = true; return false; @@ -165,6 +168,15 @@ namespace CryptoNote { tvc.m_verifivation_impossible = true; } + if (!keptByBlock) { + bool sizeValid = m_validator.checkTransactionSize(blobSize); + if (!sizeValid) { + logger(INFO) << "tx too big, rejected"; + tvc.m_verifivation_failed = true; + return false; + } + } + std::lock_guard lock(m_transactions_lock); if (!keptByBlock && m_recentlyDeletedTransactions.find(id) != m_recentlyDeletedTransactions.end()) { @@ -194,6 +206,9 @@ namespace CryptoNote { logger(ERROR, BRIGHT_RED) << "transaction already exists at inserting in memory pool"; return false; } + m_paymentIdIndex.add(txd.tx); + m_timestampIndex.add(txd.receiveTime, txd.id); + } tvc.m_added_to_pool = true; @@ -212,13 +227,13 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::add_tx(const Transaction &tx, tx_verification_context& tvc, bool keeped_by_block) { - crypto::hash h = null_hash; + Crypto::Hash h = NULL_HASH; size_t blobSize = 0; - get_transaction_hash(tx, h, blobSize); + getObjectHash(tx, h, blobSize); return add_tx(tx, h, blobSize, tvc, keeped_by_block); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee) { + bool tx_memory_pool::take_tx(const Crypto::Hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee) { std::lock_guard lock(m_transactions_lock); auto it = m_transactions.find(id); if (it == m_transactions.end()) { @@ -247,9 +262,9 @@ namespace CryptoNote { } } //--------------------------------------------------------------------------------- - void tx_memory_pool::get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const { + void tx_memory_pool::get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const { std::lock_guard lock(m_transactions_lock); - std::unordered_set ready_tx_ids; + std::unordered_set ready_tx_ids; for (const auto& tx : m_transactions) { TransactionCheckInfo checkInfo(tx); if (is_transaction_ready_to_go(tx.tx, checkInfo)) { @@ -257,7 +272,7 @@ namespace CryptoNote { } } - std::unordered_set known_set(known_tx_ids.begin(), known_tx_ids.end()); + std::unordered_set known_set(known_tx_ids.begin(), known_tx_ids.end()); for (auto it = ready_tx_ids.begin(), e = ready_tx_ids.end(); it != e;) { auto known_it = known_set.find(*it); if (known_it != known_set.end()) { @@ -273,15 +288,15 @@ namespace CryptoNote { deleted_tx_ids.assign(known_set.begin(), known_set.end()); } //--------------------------------------------------------------------------------- - bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) { + bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const Crypto::Hash& top_block_id) { return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) { + bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const Crypto::Hash& top_block_id) { return true; } //--------------------------------------------------------------------------------- - bool tx_memory_pool::have_tx(const crypto::hash &id) const { + bool tx_memory_pool::have_tx(const Crypto::Hash &id) const { std::lock_guard lock(m_transactions_lock); if (m_transactions.count(id)) { return true; @@ -315,9 +330,11 @@ namespace CryptoNote { std::lock_guard lock(m_transactions_lock); for (const auto& txd : m_fee_index) { ss << "id: " << txd.id << std::endl; + if (!short_format) { - ss << obj_to_json_str(txd.tx) << std::endl; + ss << storeToJson(txd.tx) << std::endl; } + ss << "blobSize: " << txd.blobSize << std::endl << "fee: " << m_currency.formatAmount(txd.fee) << std::endl << "keptByBlock: " << (txd.keptByBlock ? 'T' : 'F') << std::endl @@ -364,7 +381,7 @@ namespace CryptoNote { } } - bl.txHashes = blockTemplate.getTransactions(); + bl.transactionHashes = blockTemplate.getTransactions(); return true; } //--------------------------------------------------------------------------------- @@ -377,13 +394,18 @@ namespace CryptoNote { if (!boost::filesystem::exists(state_file_path, ec)) { return true; } - bool res = tools::unserialize_obj_from_file(*this, state_file_path); - if (!res) { + + if (!loadFromBinaryFile(*this, state_file_path)) { logger(ERROR) << "Failed to load memory pool from file " << state_file_path; m_transactions.clear(); m_spent_key_images.clear(); m_spentOutputs.clear(); + + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + } else { + buildIndices(); } removeExpiredTransactions(); @@ -393,19 +415,63 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- bool tx_memory_pool::deinit() { - if (!tools::create_directories_if_necessary(m_config_folder)) { + if (!Tools::create_directories_if_necessary(m_config_folder)) { logger(INFO) << "Failed to create data directory: " << m_config_folder; return false; } std::string state_file_path = m_config_folder + "/" + m_currency.txPoolFileName(); - bool res = tools::serialize_obj_to_file(*this, state_file_path); - if (!res) { + + if (!storeToBinaryFile(*this, state_file_path)) { logger(INFO) << "Failed to serialize memory pool to file " << state_file_path; } + + m_paymentIdIndex.clear(); + m_timestampIndex.clear(); + return true; } +#define CURRENT_MEMPOOL_ARCHIVE_VER 1 + + void serialize(CryptoNote::tx_memory_pool::TransactionDetails& td, ISerializer& s) { + s(td.id, "id"); + s(td.blobSize, "blobSize"); + s(td.fee, "fee"); + s(td.tx, "tx"); + s(td.maxUsedBlock.height, "maxUsedBlock.height"); + s(td.maxUsedBlock.id, "maxUsedBlock.id"); + s(td.lastFailedBlock.height, "lastFailedBlock.height"); + s(td.lastFailedBlock.id, "lastFailedBlock.id"); + s(td.keptByBlock, "keptByBlock"); + s(reinterpret_cast(td.receiveTime), "receiveTime"); + } + + //--------------------------------------------------------------------------------- + void tx_memory_pool::serialize(ISerializer& s) { + + uint8_t version = CURRENT_MEMPOOL_ARCHIVE_VER; + + s(version, "version"); + + if (version != CURRENT_MEMPOOL_ARCHIVE_VER) { + return; + } + + std::lock_guard lock(m_transactions_lock); + + if (s.type() == ISerializer::INPUT) { + m_transactions.clear(); + readSequence(std::inserter(m_transactions, m_transactions.end()), "transactions", s); + } else { + writeSequence(m_transactions.begin(), m_transactions.end(), "transactions", s); + } + + KV_MEMBER(m_spent_key_images); + KV_MEMBER(m_spentOutputs); + KV_MEMBER(m_recentlyDeletedTransactions); + } + //--------------------------------------------------------------------------------- void tx_memory_pool::on_idle() { m_txCheckInterval.call([this](){ return removeExpiredTransactions(); }); @@ -452,17 +518,19 @@ namespace CryptoNote { tx_memory_pool::tx_container_t::iterator tx_memory_pool::removeTransaction(tx_memory_pool::tx_container_t::iterator i) { removeTransactionInputs(i->id, i->tx, i->keptByBlock); + m_paymentIdIndex.remove(i->tx); + m_timestampIndex.remove(i->receiveTime, i->id); return m_transactions.erase(i); } - bool tx_memory_pool::removeTransactionInputs(const crypto::hash& tx_id, const Transaction& tx, bool keptByBlock) { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - const auto& txin = boost::get(in); + bool tx_memory_pool::removeTransactionInputs(const Crypto::Hash& tx_id, const Transaction& tx, bool keptByBlock) { + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + const auto& txin = boost::get(in); auto it = m_spent_key_images.find(txin.keyImage); if (!(it != m_spent_key_images.end())) { logger(ERROR, BRIGHT_RED) << "failed to find transaction input in key images. img=" << txin.keyImage << std::endl << "transaction id = " << tx_id; return false; } - std::unordered_set& key_image_set = it->second; + std::unordered_set& key_image_set = it->second; if (!(!key_image_set.empty())) { logger(ERROR, BRIGHT_RED) << "empty key_image set, img=" << txin.keyImage << std::endl << "transaction id = " << tx_id; return false; } @@ -474,9 +542,9 @@ namespace CryptoNote { //it is now empty hash container for this key_image m_spent_key_images.erase(it); } - } else if (in.type() == typeid(TransactionInputMultisignature)) { + } else if (in.type() == typeid(MultisignatureInput)) { if (!keptByBlock) { - const auto& msig = boost::get(in); + const auto& msig = boost::get(in); auto output = GlobalOutput(msig.amount, msig.outputIndex); assert(m_spentOutputs.count(output)); m_spentOutputs.erase(output); @@ -488,12 +556,12 @@ namespace CryptoNote { } //--------------------------------------------------------------------------------- - bool tx_memory_pool::addTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock) { + bool tx_memory_pool::addTransactionInputs(const Crypto::Hash& id, const Transaction& tx, bool keptByBlock) { // should not fail - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - const auto& txin = boost::get(in); - std::unordered_set& kei_image_set = m_spent_key_images[txin.keyImage]; + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + const auto& txin = boost::get(in); + std::unordered_set& kei_image_set = m_spent_key_images[txin.keyImage]; if (!(keptByBlock || kei_image_set.size() == 0)) { logger(ERROR, BRIGHT_RED) << "internal error: keptByBlock=" << keptByBlock @@ -506,9 +574,9 @@ namespace CryptoNote { logger(ERROR, BRIGHT_RED) << "internal error: try to insert duplicate iterator in key_image set"; return false; } - } else if (in.type() == typeid(TransactionInputMultisignature)) { + } else if (in.type() == typeid(MultisignatureInput)) { if (!keptByBlock) { - const auto& msig = boost::get(in); + const auto& msig = boost::get(in); auto r = m_spentOutputs.insert(GlobalOutput(msig.amount, msig.outputIndex)); (void)r; assert(r.second); @@ -521,14 +589,14 @@ namespace CryptoNote { //--------------------------------------------------------------------------------- bool tx_memory_pool::haveSpentInputs(const Transaction& tx) const { - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - const auto& tokey_in = boost::get(in); + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + const auto& tokey_in = boost::get(in); if (m_spent_key_images.count(tokey_in.keyImage)) { return true; } - } else if (in.type() == typeid(TransactionInputMultisignature)) { - const auto& msig = boost::get(in); + } else if (in.type() == typeid(MultisignatureInput)) { + const auto& msig = boost::get(in); if (m_spentOutputs.count(GlobalOutput(msig.amount, msig.outputIndex))) { return true; } @@ -544,4 +612,22 @@ namespace CryptoNote { bool tx_memory_pool::removeObserver(ITxPoolObserver* observer) { return m_observerManager.remove(observer); } + + void tx_memory_pool::buildIndices() { + std::lock_guard lock(m_transactions_lock); + for (auto it = m_transactions.begin(); it != m_transactions.end(); it++) { + m_paymentIdIndex.add(it->tx); + m_timestampIndex.add(it->receiveTime, it->id); + } + } + + bool tx_memory_pool::getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionIds) { + std::lock_guard lock(m_transactions_lock); + return m_paymentIdIndex.find(paymentId, transactionIds); + } + + bool tx_memory_pool::getTransactionIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& hashes, uint64_t& transactionsNumberWithinTimestamps) { + std::lock_guard lock(m_transactions_lock); + return m_timestampIndex.find(timestampBegin, timestampEnd, transactionsNumberLimit, hashes, transactionsNumberWithinTimestamps); + } } diff --git a/src/cryptonote_core/tx_pool.h b/src/CryptoNoteCore/TransactionPool.h old mode 100644 new mode 100755 similarity index 72% rename from src/cryptonote_core/tx_pool.h rename to src/CryptoNoteCore/TransactionPool.h index d52173a1fd..c0301da2cd --- a/src/cryptonote_core/tx_pool.h +++ b/src/CryptoNoteCore/TransactionPool.h @@ -21,7 +21,6 @@ #include #include -#include #include // multi index @@ -30,21 +29,25 @@ #include #include -#include "Common/util.h" +#include "Common/Util.h" #include "Common/int-util.h" #include "Common/ObserverManager.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/ITimeProvider.h" -#include "cryptonote_core/ITransactionValidator.h" -#include "cryptonote_core/ITxPoolObserver.h" -#include "cryptonote_core/verification_context.h" + +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/ITimeProvider.h" +#include "CryptoNoteCore/ITransactionValidator.h" +#include "CryptoNoteCore/ITxPoolObserver.h" +#include "CryptoNoteCore/VerificationContext.h" +#include "CryptoNoteCore/BlockchainIndices.h" #include namespace CryptoNote { + class ISerializer; class OnceInTimeInterval { public: @@ -93,14 +96,14 @@ namespace CryptoNote { bool init(const std::string& config_folder); bool deinit(); - bool have_tx(const crypto::hash &id) const; - bool add_tx(const Transaction &tx, const crypto::hash &id, size_t blobSize, tx_verification_context& tvc, bool keeped_by_block); + bool have_tx(const Crypto::Hash &id) const; + bool add_tx(const Transaction &tx, const Crypto::Hash &id, size_t blobSize, tx_verification_context& tvc, bool keeped_by_block); bool add_tx(const Transaction &tx, tx_verification_context& tvc, bool keeped_by_block); //gets tx and remove it from pool - bool take_tx(const crypto::hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee); + bool take_tx(const Crypto::Hash &id, Transaction &tx, size_t& blobSize, uint64_t& fee); - bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); - bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); + bool on_blockchain_inc(uint64_t new_block_height, const Crypto::Hash& top_block_id); + bool on_blockchain_dec(uint64_t new_block_height, const Crypto::Hash& top_block_id); void lock() const; void unlock() const; @@ -108,11 +111,14 @@ namespace CryptoNote { bool fill_block_template(Block &bl, size_t median_size, size_t maxCumulativeSize, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee); void get_transactions(std::list& txs) const; - void get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const; + void get_difference(const std::vector& known_tx_ids, std::vector& new_tx_ids, std::vector& deleted_tx_ids) const; size_t get_transactions_count() const; std::string print_pool(bool short_format) const; void on_idle(); + bool getTransactionIdsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactionIds); + bool getTransactionIdsByTimestamp(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& hashes, uint64_t& transactionsNumberWithinTimestamps); + template void getTransactions(const t_ids_container& txsIds, t_tx_container& txs, t_missed_container& missedTxs) { std::lock_guard lock(m_transactions_lock); @@ -127,20 +133,7 @@ namespace CryptoNote { } } -#define CURRENT_MEMPOOL_ARCHIVE_VER 11 - - template - void serialize(archive_t & a, const unsigned int version) { - if (version < CURRENT_MEMPOOL_ARCHIVE_VER) { - return; - } - - std::lock_guard lock(m_transactions_lock); - a & m_transactions; - a & m_spent_key_images; - a & m_spentOutputs; - a & m_recentlyDeletedTransactions; - } + void serialize(ISerializer& s); struct TransactionCheckInfo { BlockInfo maxUsedBlock; @@ -148,7 +141,7 @@ namespace CryptoNote { }; struct TransactionDetails : public TransactionCheckInfo { - crypto::hash id; + Crypto::Hash id; Transaction tx; size_t blobSize; uint64_t fee; @@ -179,7 +172,7 @@ namespace CryptoNote { } }; - typedef hashed_unique main_index_t; + typedef hashed_unique main_index_t; typedef ordered_non_unique, TransactionPriorityComparator> fee_index_t; typedef multi_index_container GlobalOutput; typedef std::set GlobalOutputsContainer; - typedef std::unordered_map > key_images_container; + typedef std::unordered_map > key_images_container; // double spending checking - bool addTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock); + bool addTransactionInputs(const Crypto::Hash& id, const Transaction& tx, bool keptByBlock); bool haveSpentInputs(const Transaction& tx) const; - bool removeTransactionInputs(const crypto::hash& id, const Transaction& tx, bool keptByBlock); + bool removeTransactionInputs(const Crypto::Hash& id, const Transaction& tx, bool keptByBlock); tx_container_t::iterator removeTransaction(tx_container_t::iterator i); bool removeExpiredTransactions(); bool is_transaction_ready_to_go(const Transaction& tx, TransactionCheckInfo& txd) const; - tools::ObserverManager m_observerManager; + void buildIndices(); + + Tools::ObserverManager m_observerManager; const CryptoNote::Currency& m_currency; OnceInTimeInterval m_txCheckInterval; mutable std::recursive_mutex m_transactions_lock; @@ -213,32 +208,13 @@ namespace CryptoNote { tx_container_t m_transactions; tx_container_t::nth_index<1>::type& m_fee_index; - std::unordered_map m_recentlyDeletedTransactions; + std::unordered_map m_recentlyDeletedTransactions; Logging::LoggerRef logger; -#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) - friend class blockchain_storage; -#endif + PaymentIdIndex m_paymentIdIndex; + TimestampTransactionsIndex m_timestampIndex; }; } -namespace boost { - namespace serialization { - template - void serialize(archive_t & ar, CryptoNote::tx_memory_pool::TransactionDetails& td, const unsigned int version) { - ar & td.id; - ar & td.blobSize; - ar & td.fee; - ar & td.tx; - ar & td.maxUsedBlock.height; - ar & td.maxUsedBlock.id; - ar & td.lastFailedBlock.height; - ar & td.lastFailedBlock.id; - ar & td.keptByBlock; - ar & td.receiveTime; - } - } -} -BOOST_CLASS_VERSION(CryptoNote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) diff --git a/src/CryptoNoteCore/TransactionPrefixImpl.cpp b/src/CryptoNoteCore/TransactionPrefixImpl.cpp new file mode 100755 index 0000000000..532b768403 --- /dev/null +++ b/src/CryptoNoteCore/TransactionPrefixImpl.cpp @@ -0,0 +1,227 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ITransaction.h" + +#include +#include + +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/TransactionApiExtra.h" +#include "TransactionUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" + +using namespace Crypto; + +namespace CryptoNote { + +class TransactionPrefixImpl : public ITransactionReader { +public: + TransactionPrefixImpl(); + TransactionPrefixImpl(const TransactionPrefix& prefix, const Hash& transactionHash); + + virtual ~TransactionPrefixImpl() { } + + virtual Hash getTransactionHash() const override; + virtual Hash getTransactionPrefixHash() const override; + virtual PublicKey getTransactionPublicKey() const override; + virtual uint64_t getUnlockTime() const override; + + // extra + virtual bool getPaymentId(Hash& paymentId) const override; + virtual bool getExtraNonce(BinaryArray& nonce) const override; + virtual BinaryArray getExtra() const override; + + // inputs + virtual size_t getInputCount() const override; + virtual uint64_t getInputTotalAmount() const override; + virtual TransactionTypes::InputType getInputType(size_t index) const override; + virtual void getInput(size_t index, KeyInput& input) const override; + virtual void getInput(size_t index, MultisignatureInput& input) const override; + + // outputs + virtual size_t getOutputCount() const override; + virtual uint64_t getOutputTotalAmount() const override; + virtual TransactionTypes::OutputType getOutputType(size_t index) const override; + virtual void getOutput(size_t index, KeyOutput& output, uint64_t& amount) const override; + virtual void getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const override; + + // signatures + virtual size_t getRequiredSignaturesCount(size_t inputIndex) const override; + virtual bool findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; + + // various checks + virtual bool validateInputs() const override; + virtual bool validateOutputs() const override; + virtual bool validateSignatures() const override; + + // serialized transaction + virtual BinaryArray getTransactionData() const override; + + virtual bool getTransactionSecretKey(SecretKey& key) const override; + +private: + TransactionPrefix m_txPrefix; + TransactionExtra m_extra; + Hash m_txHash; +}; + +TransactionPrefixImpl::TransactionPrefixImpl() { +} + +TransactionPrefixImpl::TransactionPrefixImpl(const TransactionPrefix& prefix, const Hash& transactionHash) { + m_extra.parse(prefix.extra); + + m_txPrefix = prefix; + m_txHash = transactionHash; +} + +Hash TransactionPrefixImpl::getTransactionHash() const { + return m_txHash; +} + +Hash TransactionPrefixImpl::getTransactionPrefixHash() const { + return getObjectHash(m_txPrefix); +} + +PublicKey TransactionPrefixImpl::getTransactionPublicKey() const { + Crypto::PublicKey pk(NULL_PUBLIC_KEY); + m_extra.getPublicKey(pk); + return pk; +} + +uint64_t TransactionPrefixImpl::getUnlockTime() const { + return m_txPrefix.unlockTime; +} + +bool TransactionPrefixImpl::getPaymentId(Hash& hash) const { + BinaryArray nonce; + + if (getExtraNonce(nonce)) { + Crypto::Hash paymentId; + if (getPaymentIdFromTransactionExtraNonce(nonce, paymentId)) { + hash = reinterpret_cast(paymentId); + return true; + } + } + + return false; +} + +bool TransactionPrefixImpl::getExtraNonce(BinaryArray& nonce) const { + TransactionExtraNonce extraNonce; + + if (m_extra.get(extraNonce)) { + nonce = extraNonce.nonce; + return true; + } + + return false; +} + +BinaryArray TransactionPrefixImpl::getExtra() const { + return m_txPrefix.extra; +} + +size_t TransactionPrefixImpl::getInputCount() const { + return m_txPrefix.inputs.size(); +} + +uint64_t TransactionPrefixImpl::getInputTotalAmount() const { + return std::accumulate(m_txPrefix.inputs.begin(), m_txPrefix.inputs.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { + return val + getTransactionInputAmount(in); }); +} + +TransactionTypes::InputType TransactionPrefixImpl::getInputType(size_t index) const { + return getTransactionInputType(getInputChecked(m_txPrefix, index)); +} + +void TransactionPrefixImpl::getInput(size_t index, KeyInput& input) const { + input = boost::get(getInputChecked(m_txPrefix, index, TransactionTypes::InputType::Key)); +} + +void TransactionPrefixImpl::getInput(size_t index, MultisignatureInput& input) const { + input = boost::get(getInputChecked(m_txPrefix, index, TransactionTypes::InputType::Multisignature)); +} + +size_t TransactionPrefixImpl::getOutputCount() const { + return m_txPrefix.outputs.size(); +} + +uint64_t TransactionPrefixImpl::getOutputTotalAmount() const { + return std::accumulate(m_txPrefix.outputs.begin(), m_txPrefix.outputs.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { + return val + out.amount; }); +} + +TransactionTypes::OutputType TransactionPrefixImpl::getOutputType(size_t index) const { + return getTransactionOutputType(getOutputChecked(m_txPrefix, index).target); +} + +void TransactionPrefixImpl::getOutput(size_t index, KeyOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(m_txPrefix, index, TransactionTypes::OutputType::Key); + output = boost::get(out.target); + amount = out.amount; +} + +void TransactionPrefixImpl::getOutput(size_t index, MultisignatureOutput& output, uint64_t& amount) const { + const auto& out = getOutputChecked(m_txPrefix, index, TransactionTypes::OutputType::Multisignature); + output = boost::get(out.target); + amount = out.amount; +} + +size_t TransactionPrefixImpl::getRequiredSignaturesCount(size_t inputIndex) const { + return ::CryptoNote::getRequiredSignaturesCount(getInputChecked(m_txPrefix, inputIndex)); +} + +bool TransactionPrefixImpl::findOutputsToAccount(const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const { + return ::CryptoNote::findOutputsToAccount(m_txPrefix, addr, viewSecretKey, outs, outputAmount); +} + +bool TransactionPrefixImpl::validateInputs() const { + return check_inputs_types_supported(m_txPrefix) && + check_inputs_overflow(m_txPrefix) && + checkInputsKeyimagesDiff(m_txPrefix) && + checkMultisignatureInputsDiff(m_txPrefix); +} + +bool TransactionPrefixImpl::validateOutputs() const { + return check_outs_valid(m_txPrefix) && + check_outs_overflow(m_txPrefix); +} + +bool TransactionPrefixImpl::validateSignatures() const { + throw std::system_error(std::make_error_code(std::errc::function_not_supported), "Validating signatures is not supported for transaction prefix"); +} + +BinaryArray TransactionPrefixImpl::getTransactionData() const { + return toBinaryArray(m_txPrefix); +} + +bool TransactionPrefixImpl::getTransactionSecretKey(SecretKey& key) const { + return false; +} + + +std::unique_ptr createTransactionPrefix(const TransactionPrefix& prefix, const Hash& transactionHash) { + return std::unique_ptr (new TransactionPrefixImpl(prefix, transactionHash)); +} + +std::unique_ptr createTransactionPrefix(const Transaction& fullTransaction) { + return std::unique_ptr (new TransactionPrefixImpl(fullTransaction, getObjectHash(fullTransaction))); +} + +} diff --git a/src/CryptoNoteCore/TransactionUtils.cpp b/src/CryptoNoteCore/TransactionUtils.cpp new file mode 100755 index 0000000000..31b0061930 --- /dev/null +++ b/src/CryptoNoteCore/TransactionUtils.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "TransactionUtils.h" + +#include + +#include "crypto/crypto.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteFormatUtils.h" +#include "TransactionExtra.h" + +using namespace Crypto; + +namespace CryptoNote { + +bool checkInputsKeyimagesDiff(const CryptoNote::TransactionPrefix& tx) { + std::unordered_set ki; + for (const auto& in : tx.inputs) { + if (in.type() == typeid(KeyInput)) { + if (!ki.insert(boost::get(in).keyImage).second) + return false; + } + } + return true; +} + +// TransactionInput helper functions + +size_t getRequiredSignaturesCount(const TransactionInput& in) { + if (in.type() == typeid(KeyInput)) { + return boost::get(in).outputIndexes.size(); + } + if (in.type() == typeid(MultisignatureInput)) { + return boost::get(in).signatureCount; + } + return 0; +} + +uint64_t getTransactionInputAmount(const TransactionInput& in) { + if (in.type() == typeid(KeyInput)) { + return boost::get(in).amount; + } + if (in.type() == typeid(MultisignatureInput)) { + return boost::get(in).amount; + } + return 0; +} + +TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { + if (in.type() == typeid(KeyInput)) { + return TransactionTypes::InputType::Key; + } + if (in.type() == typeid(MultisignatureInput)) { + return TransactionTypes::InputType::Multisignature; + } + if (in.type() == typeid(BaseInput)) { + return TransactionTypes::InputType::Generating; + } + return TransactionTypes::InputType::Invalid; +} + +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index) { + if (transaction.inputs.size() <= index) { + throw std::runtime_error("Transaction input index out of range"); + } + return transaction.inputs[index]; +} + +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::InputType type) { + const auto& input = getInputChecked(transaction, index); + if (getTransactionInputType(input) != type) { + throw std::runtime_error("Unexpected transaction input type"); + } + return input; +} + +// TransactionOutput helper functions + +TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { + if (out.type() == typeid(KeyOutput)) { + return TransactionTypes::OutputType::Key; + } + if (out.type() == typeid(MultisignatureOutput)) { + return TransactionTypes::OutputType::Multisignature; + } + return TransactionTypes::OutputType::Invalid; +} + +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index) { + if (transaction.outputs.size() <= index) { + throw std::runtime_error("Transaction output index out of range"); + } + return transaction.outputs[index]; +} + +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::OutputType type) { + const auto& output = getOutputChecked(transaction, index); + if (getTransactionOutputType(output.target) != type) { + throw std::runtime_error("Unexpected transaction output target type"); + } + return output; +} + +bool isOutToKey(const Crypto::PublicKey& spendPublicKey, const Crypto::PublicKey& outKey, const Crypto::KeyDerivation& derivation, size_t keyIndex) { + Crypto::PublicKey pk; + derive_public_key(derivation, keyIndex, spendPublicKey, pk); + return pk == outKey; +} + +bool findOutputsToAccount(const CryptoNote::TransactionPrefix& transaction, const AccountPublicAddress& addr, + const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) { + AccountKeys keys; + keys.address = addr; + // only view secret key is used, spend key is not needed + keys.viewSecretKey = viewSecretKey; + + Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(transaction.extra); + + amount = 0; + size_t keyIndex = 0; + uint32_t outputIndex = 0; + + Crypto::KeyDerivation derivation; + generate_key_derivation(txPubKey, keys.viewSecretKey, derivation); + + for (const TransactionOutput& o : transaction.outputs) { + assert(o.target.type() == typeid(KeyOutput) || o.target.type() == typeid(MultisignatureOutput)); + if (o.target.type() == typeid(KeyOutput)) { + if (is_out_to_acc(keys, boost::get(o.target), derivation, keyIndex)) { + out.push_back(outputIndex); + amount += o.amount; + } + ++keyIndex; + } else if (o.target.type() == typeid(MultisignatureOutput)) { + const auto& target = boost::get(o.target); + for (const auto& key : target.keys) { + if (isOutToKey(keys.address.spendPublicKey, key, derivation, static_cast(outputIndex))) { + out.push_back(outputIndex); + } + ++keyIndex; + } + } + ++outputIndex; + } + + return true; +} + +} diff --git a/src/CryptoNoteCore/TransactionUtils.h b/src/CryptoNoteCore/TransactionUtils.h new file mode 100755 index 0000000000..3555c607ba --- /dev/null +++ b/src/CryptoNoteCore/TransactionUtils.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "ITransaction.h" + +namespace CryptoNote { + +bool checkInputsKeyimagesDiff(const CryptoNote::TransactionPrefix& tx); + +// TransactionInput helper functions +size_t getRequiredSignaturesCount(const TransactionInput& in); +uint64_t getTransactionInputAmount(const TransactionInput& in); +TransactionTypes::InputType getTransactionInputType(const TransactionInput& in); +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index); +const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::InputType type); + +bool isOutToKey(const Crypto::PublicKey& spendPublicKey, const Crypto::PublicKey& outKey, const Crypto::KeyDerivation& derivation, size_t keyIndex); + +// TransactionOutput helper functions +TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out); +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index); +const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::OutputType type); + +bool findOutputsToAccount(const CryptoNote::TransactionPrefix& transaction, const AccountPublicAddress& addr, + const Crypto::SecretKey& viewSecretKey, std::vector& out, uint64_t& amount); + +} //namespace CryptoNote diff --git a/src/cryptonote_core/UpgradeDetector.cpp b/src/CryptoNoteCore/UpgradeDetector.cpp similarity index 100% rename from src/cryptonote_core/UpgradeDetector.cpp rename to src/CryptoNoteCore/UpgradeDetector.cpp diff --git a/src/cryptonote_core/UpgradeDetector.h b/src/CryptoNoteCore/UpgradeDetector.h old mode 100644 new mode 100755 similarity index 99% rename from src/cryptonote_core/UpgradeDetector.h rename to src/CryptoNoteCore/UpgradeDetector.h index 6661783f77..e0d2c417aa --- a/src/cryptonote_core/UpgradeDetector.h +++ b/src/CryptoNoteCore/UpgradeDetector.h @@ -21,8 +21,8 @@ #include #include -#include "cryptonote_core/Currency.h" -#include "cryptonote_config.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteConfig.h" #include namespace CryptoNote { diff --git a/src/cryptonote_core/verification_context.h b/src/CryptoNoteCore/VerificationContext.h old mode 100644 new mode 100755 similarity index 100% rename from src/cryptonote_core/verification_context.h rename to src/CryptoNoteCore/VerificationContext.h diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h old mode 100644 new mode 100755 similarity index 77% rename from src/cryptonote_protocol/cryptonote_protocol_defs.h rename to src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h index 6a1904d914..e5b070d602 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h @@ -18,13 +18,12 @@ #pragma once #include -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_protocol/blobdatatype.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" // ISerializer-based serialization -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" namespace CryptoNote { @@ -36,8 +35,8 @@ namespace CryptoNote /************************************************************************/ struct block_complete_entry { - blobdata block; - std::list txs; + std::string block; + std::vector txs; void serialize(ISerializer& s) { KV_MEMBER(block); @@ -48,14 +47,35 @@ namespace CryptoNote struct BlockFullInfo : public block_complete_entry { - crypto::hash block_id; + Crypto::Hash block_id; void serialize(ISerializer& s) { KV_MEMBER(block_id); KV_MEMBER(block); KV_MEMBER(txs); } + }; + + struct TransactionPrefixInfo { + Crypto::Hash txHash; + TransactionPrefix txPrefix; + + void serialize(ISerializer& s) { + KV_MEMBER(txHash); + KV_MEMBER(txPrefix); + } + }; + + struct BlockShortInfo { + Crypto::Hash blockId; + std::string block; + std::vector txPrefixes; + void serialize(ISerializer& s) { + KV_MEMBER(blockId); + KV_MEMBER(block); + KV_MEMBER(txPrefixes); + } }; /************************************************************************/ @@ -64,7 +84,7 @@ namespace CryptoNote struct NOTIFY_NEW_BLOCK_request { block_complete_entry b; - uint64_t current_blockchain_height; + uint32_t current_blockchain_height; uint32_t hop; void serialize(ISerializer& s) { @@ -85,7 +105,7 @@ namespace CryptoNote /************************************************************************/ struct NOTIFY_NEW_TRANSACTIONS_request { - std::list txs; + std::vector txs; void serialize(ISerializer& s) { KV_MEMBER(txs); @@ -104,8 +124,8 @@ namespace CryptoNote /************************************************************************/ struct NOTIFY_REQUEST_GET_OBJECTS_request { - std::list txs; - std::list blocks; + std::vector txs; + std::vector blocks; void serialize(ISerializer& s) { serializeAsBinary(txs, "txs", s); @@ -121,10 +141,10 @@ namespace CryptoNote struct NOTIFY_RESPONSE_GET_OBJECTS_request { - std::list txs; - std::list blocks; - std::list missed_ids; - uint64_t current_blockchain_height; + std::vector txs; + std::vector blocks; + std::vector missed_ids; + uint32_t current_blockchain_height; void serialize(ISerializer& s) { KV_MEMBER(txs) @@ -147,7 +167,7 @@ namespace CryptoNote struct request { - std::list block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + std::vector block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ void serialize(ISerializer& s) { serializeAsBinary(block_ids, "block_ids", s); @@ -157,9 +177,9 @@ namespace CryptoNote struct NOTIFY_RESPONSE_CHAIN_ENTRY_request { - uint64_t start_height; - uint64_t total_height; - std::list m_block_ids; + uint32_t start_height; + uint32_t total_height; + std::vector m_block_ids; void serialize(ISerializer& s) { KV_MEMBER(start_height) @@ -178,7 +198,7 @@ namespace CryptoNote /* */ /************************************************************************/ struct NOTIFY_REQUEST_TX_POOL_request { - std::vector txs; + std::vector txs; void serialize(ISerializer& s) { serializeAsBinary(txs, "txs", s); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp similarity index 64% rename from src/cryptonote_protocol/cryptonote_protocol_handler.cpp rename to src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp index 0edd5703f0..e1d65be312 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.cpp +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.cpp @@ -15,38 +15,40 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_protocol_handler.h" +#include "CryptoNoteProtocolHandler.h" #include #include #include #include -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/verification_context.h" -#include "p2p/LevinProtocol.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/VerificationContext.h" +#include "P2p/LevinProtocol.h" using namespace Logging; +using namespace Common; namespace CryptoNote { namespace { template -bool post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, const cryptonote_connection_context& context) { +bool post_notify(IP2pEndpoint& p2p, typename t_parametr::request& arg, const CryptoNoteConnectionContext& context) { return p2p.invoke_notify_to_peer(t_parametr::ID, LevinProtocol::encode(arg), context); } template -void relay_post_notify(i_p2p_endpoint& p2p, typename t_parametr::request& arg, const net_connection_id* excludeConnection = nullptr) { +void relay_post_notify(IP2pEndpoint& p2p, typename t_parametr::request& arg, const net_connection_id* excludeConnection = nullptr) { p2p.relay_notify_to_all(t_parametr::ID, LevinProtocol::encode(arg), excludeConnection); } } -cryptonote_protocol_handler::cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log) : +CryptoNoteProtocolHandler::CryptoNoteProtocolHandler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, IP2pEndpoint* p_net_layout, Logging::ILogger& log) : m_dispatcher(dispatcher), m_currency(currency), m_core(rcore), @@ -62,21 +64,21 @@ cryptonote_protocol_handler::cryptonote_protocol_handler(const Currency& currenc } } -size_t cryptonote_protocol_handler::getPeerCount() const { +size_t CryptoNoteProtocolHandler::getPeerCount() const { return m_peersCount; } -void cryptonote_protocol_handler::set_p2p_endpoint(i_p2p_endpoint* p2p) { +void CryptoNoteProtocolHandler::set_p2p_endpoint(IP2pEndpoint* p2p) { if (p2p) m_p2p = p2p; else m_p2p = &m_p2p_stub; } -void cryptonote_protocol_handler::onConnectionOpened(cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::onConnectionOpened(CryptoNoteConnectionContext& context) { } -void cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::onConnectionClosed(CryptoNoteConnectionContext& context) { bool updated = false; { std::lock_guard lock(m_observedHeightMutex); @@ -89,25 +91,25 @@ void cryptonote_protocol_handler::onConnectionClosed(cryptonote_connection_conte if (updated) { logger(TRACE) << "Observed height updated: " << m_observedHeight; - m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + m_observerManager.notify(&ICryptoNoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); } - if (context.m_state != cryptonote_connection_context::state_befor_handshake) { + if (context.m_state != CryptoNoteConnectionContext::state_befor_handshake) { m_peersCount--; - m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + m_observerManager.notify(&ICryptoNoteProtocolObserver::peerCountUpdated, m_peersCount.load()); } } -void cryptonote_protocol_handler::stop() { +void CryptoNoteProtocolHandler::stop() { m_stop = true; } -bool cryptonote_protocol_handler::start_sync(cryptonote_connection_context& context) { +bool CryptoNoteProtocolHandler::start_sync(CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "Starting synchronization"; - if (context.m_state == cryptonote_connection_context::state_synchronizing) { + if (context.m_state == CryptoNoteConnectionContext::state_synchronizing) { NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); + r.block_ids = m_core.buildSparseChain(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); } @@ -115,20 +117,20 @@ bool cryptonote_protocol_handler::start_sync(cryptonote_connection_context& cont return true; } -bool cryptonote_protocol_handler::get_stat_info(core_stat_info& stat_inf) { +bool CryptoNoteProtocolHandler::get_stat_info(core_stat_info& stat_inf) { return m_core.get_stat_info(stat_inf); } -void cryptonote_protocol_handler::log_connections() { +void CryptoNoteProtocolHandler::log_connections() { std::stringstream ss; ss << std::setw(25) << std::left << "Remote Host" << std::setw(20) << "Peer id" << std::setw(25) << "Recv/Sent (inactive,sec)" << std::setw(25) << "State" - << std::setw(20) << "Livetime(seconds)" << ENDL; + << std::setw(20) << "Lifetime(seconds)" << ENDL; - m_p2p->for_each_connection([&](const cryptonote_connection_context& cntxt, peerid_type peer_id) { + m_p2p->for_each_connection([&](const CryptoNoteConnectionContext& cntxt, PeerIdType peer_id) { ss << std::setw(25) << std::left << std::string(cntxt.m_is_income ? "[INC]" : "[OUT]") + Common::ipAddressToString(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) << std::setw(20) << std::hex << peer_id @@ -139,24 +141,24 @@ void cryptonote_protocol_handler::log_connections() { logger(INFO) << "Connections: " << ENDL << ss.str(); } -uint64_t cryptonote_protocol_handler::get_current_blockchain_height() { - uint64_t height; - crypto::hash blockId; +uint32_t CryptoNoteProtocolHandler::get_current_blockchain_height() { + uint32_t height; + Crypto::Hash blockId; m_core.get_blockchain_top(height, blockId); return height; } -bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital) { - if (context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) +bool CryptoNoteProtocolHandler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, CryptoNoteConnectionContext& context, bool is_inital) { + if (context.m_state == CryptoNoteConnectionContext::state_befor_handshake && !is_inital) return true; - if (context.m_state == cryptonote_connection_context::state_synchronizing) { + if (context.m_state == CryptoNoteConnectionContext::state_synchronizing) { } else if (m_core.have_block(hshd.top_id)) { if (is_inital) { on_connection_synchronized(); - context.m_state = cryptonote_connection_context::state_pool_sync_required; + context.m_state = CryptoNoteConnectionContext::state_pool_sync_required; } else { - context.m_state = cryptonote_connection_context::state_normal; + context.m_state = CryptoNoteConnectionContext::state_normal; } } else { int64_t diff = static_cast(hshd.current_height) - static_cast(get_current_blockchain_height()); @@ -169,7 +171,7 @@ bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA logger(Logging::DEBUGGING) << "Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id; //let the socket to send response to handshake, but request callback, to let send request data after response logger(Logging::TRACE) << context << "requesting synchronization"; - context.m_state = cryptonote_connection_context::state_sync_required; + context.m_state = CryptoNoteConnectionContext::state_sync_required; } updateObservedHeight(hshd.current_height, context); @@ -177,21 +179,23 @@ bool cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA if (is_inital) { m_peersCount++; - m_observerManager.notify(&ICryptonoteProtocolObserver::peerCountUpdated, m_peersCount.load()); + m_observerManager.notify(&ICryptoNoteProtocolObserver::peerCountUpdated, m_peersCount.load()); } return true; } -bool cryptonote_protocol_handler::get_payload_sync_data(CORE_SYNC_DATA& hshd) { - m_core.get_blockchain_top(hshd.current_height, hshd.top_id); +bool CryptoNoteProtocolHandler::get_payload_sync_data(CORE_SYNC_DATA& hshd) { + uint32_t current_height; + m_core.get_blockchain_top(current_height, hshd.top_id); + hshd.current_height = current_height; hshd.current_height += 1; return true; } template -int notifyAdaptor(const std::string& reqBuf, cryptonote_connection_context& ctx, Handler handler) { +int notifyAdaptor(const BinaryArray& reqBuf, CryptoNoteConnectionContext& ctx, Handler handler) { typedef typename Command::request Request; int command = Command::ID; @@ -206,18 +210,18 @@ int notifyAdaptor(const std::string& reqBuf, cryptonote_connection_context& ctx, #define HANDLE_NOTIFY(CMD, Handler) case CMD::ID: { ret = notifyAdaptor(in, ctx, std::bind(Handler, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); break; } -int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, const std::string& in, std::string& out, cryptonote_connection_context& ctx, bool& handled) { +int CryptoNoteProtocolHandler::handleCommand(bool is_notify, int command, const BinaryArray& in, BinaryArray& out, CryptoNoteConnectionContext& ctx, bool& handled) { int ret = 0; handled = true; switch (command) { - HANDLE_NOTIFY(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) - HANDLE_NOTIFY(NOTIFY_NEW_TRANSACTIONS, &cryptonote_protocol_handler::handle_notify_new_transactions) - HANDLE_NOTIFY(NOTIFY_REQUEST_GET_OBJECTS, &cryptonote_protocol_handler::handle_request_get_objects) - HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) - HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) - HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) - HANDLE_NOTIFY(NOTIFY_REQUEST_TX_POOL, &cryptonote_protocol_handler::handleRequestTxPool) + HANDLE_NOTIFY(NOTIFY_NEW_BLOCK, &CryptoNoteProtocolHandler::handle_notify_new_block) + HANDLE_NOTIFY(NOTIFY_NEW_TRANSACTIONS, &CryptoNoteProtocolHandler::handle_notify_new_transactions) + HANDLE_NOTIFY(NOTIFY_REQUEST_GET_OBJECTS, &CryptoNoteProtocolHandler::handle_request_get_objects) + HANDLE_NOTIFY(NOTIFY_RESPONSE_GET_OBJECTS, &CryptoNoteProtocolHandler::handle_response_get_objects) + HANDLE_NOTIFY(NOTIFY_REQUEST_CHAIN, &CryptoNoteProtocolHandler::handle_request_chain) + HANDLE_NOTIFY(NOTIFY_RESPONSE_CHAIN_ENTRY, &CryptoNoteProtocolHandler::handle_response_chain_entry) + HANDLE_NOTIFY(NOTIFY_REQUEST_TX_POOL, &CryptoNoteProtocolHandler::handleRequestTxPool) default: handled = false; @@ -228,32 +232,32 @@ int cryptonote_protocol_handler::handleCommand(bool is_notify, int command, cons #undef HANDLE_NOTIFY -int cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"; updateObservedHeight(arg.current_blockchain_height, context); context.m_remote_blockchain_height = arg.current_blockchain_height; - if (context.m_state != cryptonote_connection_context::state_normal) { + if (context.m_state != CryptoNoteConnectionContext::state_normal) { return 1; } for (auto tx_blob_it = arg.b.txs.begin(); tx_blob_it != arg.b.txs.end(); tx_blob_it++) { CryptoNote::tx_verification_context tvc = boost::value_initialized(); - m_core.handle_incoming_tx(*tx_blob_it, tvc, true); + m_core.handle_incoming_tx(asBinaryArray(*tx_blob_it), tvc, true); if (tvc.m_verifivation_failed) { logger(Logging::INFO) << context << "Block verification failed: transaction verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } } block_verification_context bvc = boost::value_initialized(); - m_core.handle_incoming_block_blob(arg.b.block, bvc, true, false); + m_core.handle_incoming_block_blob(asBinaryArray(arg.b.block), bvc, true, false); if (bvc.m_verifivation_failed) { logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } if (bvc.m_added_to_main_chain) { @@ -261,27 +265,29 @@ int cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW //TODO: Add here announce protocol usage relay_post_notify(*m_p2p, arg, &context.m_connection_id); // relay_block(arg, context); + + if (bvc.m_switched_to_alt_chain) { + requestMissingPoolTransactions(context); + } } else if (bvc.m_marked_as_orphaned) { - context.m_state = cryptonote_connection_context::state_synchronizing; + context.m_state = CryptoNoteConnectionContext::state_synchronizing; NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); + r.block_ids = m_core.buildSparseChain(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); - } else if (bvc.m_switched_to_alt_chain) { - requestMissingPoolTransactions(context); } return 1; } -int cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_NEW_TRANSACTIONS"; - if (context.m_state != cryptonote_connection_context::state_normal) + if (context.m_state != CryptoNoteConnectionContext::state_normal) return 1; for (auto tx_blob_it = arg.txs.begin(); tx_blob_it != arg.txs.end();) { CryptoNote::tx_verification_context tvc = boost::value_initialized(); - m_core.handle_incoming_tx(*tx_blob_it, tvc, false); + m_core.handle_incoming_tx(asBinaryArray(*tx_blob_it), tvc, false); if (tvc.m_verifivation_failed) { logger(Logging::INFO) << context << "Tx verification failed"; } @@ -300,12 +306,12 @@ int cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOT return true; } -int cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_REQUEST_GET_OBJECTS"; NOTIFY_RESPONSE_GET_OBJECTS::request rsp; if (!m_core.handle_get_objects(arg, rsp)) { logger(Logging::ERROR) << context << "failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; } logger(Logging::TRACE) << context << "-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size(); @@ -313,13 +319,13 @@ int cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_ return 1; } -int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_RESPONSE_GET_OBJECTS"; if (context.m_last_response_height > arg.current_blockchain_height) { logger(Logging::ERROR) << context << "sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -331,17 +337,17 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY for (const block_complete_entry& block_entry : arg.blocks) { ++count; Block b; - if (!parse_and_validate_block_from_blob(block_entry.block, b)) { + if (!fromBinaryArray(b, asBinaryArray(block_entry.block))) { logger(Logging::ERROR) << context << "sent wrong block: failed to parse and validate block: \r\n" - << blobToHex(block_entry.block) << "\r\n dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + << toHex(asBinaryArray(block_entry.block)) << "\r\n dropping connection"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } //to avoid concurrency in core between connections, suspend connections which delivered block later then first one if (count == 2) { if (m_core.have_block(get_block_hash(b))) { - context.m_state = cryptonote_connection_context::state_idle; + context.m_state = CryptoNoteConnectionContext::state_idle; context.m_needed_objects.clear(); context.m_requested_objects.clear(); logger(Logging::DEBUGGING) << context << "Connection set to idle state."; @@ -351,15 +357,15 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY auto req_it = context.m_requested_objects.find(get_block_hash(b)); if (req_it == context.m_requested_objects.end()) { - logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(get_blob_hash(block_entry.block)) + logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(getBinaryArrayHash(asBinaryArray(block_entry.block))) << " wasn't requested, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } - if (b.txHashes.size() != block_entry.txs.size()) { - logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(get_blob_hash(block_entry.block)) - << ", txHashes.size()=" << b.txHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + if (b.transactionHashes.size() != block_entry.txs.size()) { + logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(getBinaryArrayHash(asBinaryArray(block_entry.block))) + << ", transactionHashes.size()=" << b.transactionHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -370,7 +376,7 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY logger(Logging::ERROR, Logging::BRIGHT_RED) << context << "returned not all requested objects (context.m_requested_objects.size()=" << context.m_requested_objects.size() << "), dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -379,37 +385,25 @@ int cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY BOOST_SCOPE_EXIT_ALL(this) { m_core.update_block_template_and_resume_mining(); }; - auto currentContext = m_dispatcher.getCurrentContext(); - - auto resultFuture = std::async(std::launch::async, [&]{ - int result = processObjects(context, arg.blocks); - m_dispatcher.remoteSpawn([&] { - m_dispatcher.pushContext(currentContext); - }); - - return result; - }); - - m_dispatcher.dispatch(); - int result = resultFuture.get(); + int result = processObjects(context, arg.blocks); if (result != 0) { return result; } } - uint64_t height; - crypto::hash top; + uint32_t height; + Crypto::Hash top; m_core.get_blockchain_top(height, top); logger(DEBUGGING, BRIGHT_GREEN) << "Local blockchain updated, new height = " << height; - if (!m_stop && context.m_state == cryptonote_connection_context::state_synchronizing) { + if (!m_stop && context.m_state == CryptoNoteConnectionContext::state_synchronizing) { request_missing_objects(context, true); } return 1; } -int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& context, const std::list& blocks) { +int CryptoNoteProtocolHandler::processObjects(CryptoNoteConnectionContext& context, const std::vector& blocks) { for (const block_complete_entry& block_entry : blocks) { if (m_stop) { @@ -419,32 +413,34 @@ int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& c //process transactions for (auto& tx_blob : block_entry.txs) { tx_verification_context tvc = boost::value_initialized(); - m_core.handle_incoming_tx(tx_blob, tvc, true); + m_core.handle_incoming_tx(asBinaryArray(tx_blob), tvc, true); if (tvc.m_verifivation_failed) { logger(Logging::ERROR) << context << "transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " - << Common::podToHex(get_blob_hash(tx_blob)) << ", dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + << Common::podToHex(getBinaryArrayHash(asBinaryArray(tx_blob))) << ", dropping connection"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } } // process block block_verification_context bvc = boost::value_initialized(); - m_core.handle_incoming_block_blob(block_entry.block, bvc, false, false); + m_core.handle_incoming_block_blob(asBinaryArray(block_entry.block), bvc, false, false); if (bvc.m_verifivation_failed) { logger(Logging::DEBUGGING) << context << "Block verification failed, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } else if (bvc.m_marked_as_orphaned) { logger(Logging::INFO) << context << "Block received at sync phase was marked as orphaned, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } else if (bvc.m_already_exists) { logger(Logging::DEBUGGING) << context << "Block already exists, switching to idle state"; - context.m_state = cryptonote_connection_context::state_idle; + context.m_state = CryptoNoteConnectionContext::state_idle; return 1; } + + m_dispatcher.yield(); } return 0; @@ -452,23 +448,34 @@ int cryptonote_protocol_handler::processObjects(cryptonote_connection_context& c } -bool cryptonote_protocol_handler::on_idle() { +bool CryptoNoteProtocolHandler::on_idle() { return m_core.on_idle(); } -int cryptonote_protocol_handler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size(); - NOTIFY_RESPONSE_CHAIN_ENTRY::request r; - if (!m_core.find_blockchain_supplement(arg.block_ids, r)) { - logger(Logging::ERROR) << context << "Failed to handle NOTIFY_REQUEST_CHAIN."; + + if (arg.block_ids.empty()) { + logger(Logging::ERROR, Logging::BRIGHT_RED) << context << "Failed to handle NOTIFY_REQUEST_CHAIN. block_ids is empty"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; + return 1; + } + + if (arg.block_ids.back() != m_core.getBlockIdByHeight(0)) { + logger(Logging::ERROR) << context << "Failed to handle NOTIFY_REQUEST_CHAIN. block_ids doesn't end with genesis block ID"; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } + + NOTIFY_RESPONSE_CHAIN_ENTRY::request r; + r.m_block_ids = m_core.findBlockchainSupplement(arg.block_ids, BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT, r.total_height, r.start_height); + logger(Logging::TRACE) << context << "-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size(); post_notify(*m_p2p, r, context); return 1; } -bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks) { +bool CryptoNoteProtocolHandler::request_missing_objects(CryptoNoteConnectionContext& context, bool check_having_blocks) { if (context.m_needed_objects.size()) { //we know objects that we need, request this objects NOTIFY_REQUEST_GET_OBJECTS::request req; @@ -488,7 +495,7 @@ bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_ } else if (context.m_last_response_height < context.m_remote_blockchain_height - 1) {//we have to fetch more objects ids, request blockchain entry NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); - m_core.get_short_chain_history(r.block_ids); + r.block_ids = m_core.buildSparseChain(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); post_notify(*m_p2p, r, context); } else { @@ -508,14 +515,14 @@ bool cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_ requestMissingPoolTransactions(context); - context.m_state = cryptonote_connection_context::state_normal; + context.m_state = CryptoNoteConnectionContext::state_normal; logger(Logging::INFO, Logging::BRIGHT_GREEN) << context << "SYNCHRONIZED OK"; on_connection_synchronized(); } return true; } -bool cryptonote_protocol_handler::on_connection_synchronized() { +bool CryptoNoteProtocolHandler::on_connection_synchronized() { bool val_expected = false; if (m_synchronized.compare_exchange_strong(val_expected, true)) { logger(Logging::INFO) << ENDL << "**********************************************************************" << ENDL @@ -528,22 +535,21 @@ bool cryptonote_protocol_handler::on_connection_synchronized() { << "**********************************************************************"; m_core.on_synchronized(); - uint64_t height; - crypto::hash hash; - if (m_core.get_blockchain_top(height, hash)) { - m_observerManager.notify(&ICryptonoteProtocolObserver::blockchainSynchronized, height); - } + uint32_t height; + Crypto::Hash hash; + m_core.get_blockchain_top(height, hash); + m_observerManager.notify(&ICryptoNoteProtocolObserver::blockchainSynchronized, height); } return true; } -int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height; if (!arg.m_block_ids.size()) { logger(Logging::ERROR) << context << "sent empty m_block_ids, dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -552,12 +558,12 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY << context << "sent m_block_ids starting from unknown id: " << Common::podToHex(arg.m_block_ids.front()) << " , dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } context.m_remote_blockchain_height = arg.total_height; - context.m_last_response_height = arg.start_height + arg.m_block_ids.size() - 1; + context.m_last_response_height = arg.start_height + static_cast(arg.m_block_ids.size()) - 1; if (context.m_last_response_height > context.m_remote_blockchain_height) { logger(Logging::ERROR) @@ -565,7 +571,7 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY << "sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height << "\r\nm_start_height=" << arg.start_height << "\r\nm_block_ids.size()=" << arg.m_block_ids.size(); - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; } for (auto& bl_id : arg.m_block_ids) { @@ -577,18 +583,18 @@ int cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY return 1; } -int cryptonote_protocol_handler::handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, - cryptonote_connection_context& context) { +int CryptoNoteProtocolHandler::handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, + CryptoNoteConnectionContext& context) { logger(Logging::TRACE) << context << "NOTIFY_REQUEST_TX_POOL: txs.size() = " << arg.txs.size(); std::vector addedTransactions; - std::vector deletedTransactions; + std::vector deletedTransactions; m_core.getPoolChanges(arg.txs, addedTransactions, deletedTransactions); if (!addedTransactions.empty()) { NOTIFY_NEW_TRANSACTIONS::request notification; for (auto& tx : addedTransactions) { - notification.txs.push_back(tx_to_blob(tx)); + notification.txs.push_back(asString(toBinaryArray(tx))); } bool ok = post_notify(*m_p2p, notification, context); @@ -601,17 +607,17 @@ int cryptonote_protocol_handler::handleRequestTxPool(int command, NOTIFY_REQUEST } -void cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg) { +void CryptoNoteProtocolHandler::relay_block(NOTIFY_NEW_BLOCK::request& arg) { auto buf = LevinProtocol::encode(arg); m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_BLOCK::ID, buf); } -void cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) { +void CryptoNoteProtocolHandler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) { auto buf = LevinProtocol::encode(arg); m_p2p->externalRelayNotifyToAll(NOTIFY_NEW_TRANSACTIONS::ID, buf); } -void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::requestMissingPoolTransactions(const CryptoNoteConnectionContext& context) { if (context.version < P2PProtocolVersion::V1) { return; } @@ -620,7 +626,7 @@ void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonot NOTIFY_REQUEST_TX_POOL::request notification; for (auto& tx : poolTxs) { - notification.txs.emplace_back(get_transaction_hash(tx)); + notification.txs.emplace_back(getObjectHash(tx)); } bool ok = post_notify(*m_p2p, notification, context); @@ -629,12 +635,12 @@ void cryptonote_protocol_handler::requestMissingPoolTransactions(const cryptonot } } -void cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::updateObservedHeight(uint32_t peerHeight, const CryptoNoteConnectionContext& context) { bool updated = false; { std::lock_guard lock(m_observedHeightMutex); - uint64_t height = m_observedHeight; + uint32_t height = m_observedHeight; if (peerHeight > context.m_remote_blockchain_height) { m_observedHeight = std::max(m_observedHeight, peerHeight); if (m_observedHeight != height) { @@ -651,35 +657,35 @@ void cryptonote_protocol_handler::updateObservedHeight(uint64_t peerHeight, cons if (updated) { logger(TRACE) << "Observed height updated: " << m_observedHeight; - m_observerManager.notify(&ICryptonoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); + m_observerManager.notify(&ICryptoNoteProtocolObserver::lastKnownBlockHeightUpdated, m_observedHeight); } } -void cryptonote_protocol_handler::recalculateMaxObservedHeight(const cryptonote_connection_context& context) { +void CryptoNoteProtocolHandler::recalculateMaxObservedHeight(const CryptoNoteConnectionContext& context) { //should be locked outside - uint64_t peerHeight = 0; - m_p2p->for_each_connection([&peerHeight, &context](const cryptonote_connection_context& ctx, peerid_type peerId) { + uint32_t peerHeight = 0; + m_p2p->for_each_connection([&peerHeight, &context](const CryptoNoteConnectionContext& ctx, PeerIdType peerId) { if (ctx.m_connection_id != context.m_connection_id) { peerHeight = std::max(peerHeight, ctx.m_remote_blockchain_height); } }); - uint64_t localHeight = 0; - crypto::hash ignore; + uint32_t localHeight = 0; + Crypto::Hash ignore; m_core.get_blockchain_top(localHeight, ignore); - m_observedHeight = std::max(peerHeight, localHeight); + m_observedHeight = std::max(peerHeight, localHeight + 1); } -uint64_t cryptonote_protocol_handler::getObservedHeight() const { +uint32_t CryptoNoteProtocolHandler::getObservedHeight() const { std::lock_guard lock(m_observedHeightMutex); return m_observedHeight; }; -bool cryptonote_protocol_handler::addObserver(ICryptonoteProtocolObserver* observer) { +bool CryptoNoteProtocolHandler::addObserver(ICryptoNoteProtocolObserver* observer) { return m_observerManager.add(observer); } -bool cryptonote_protocol_handler::removeObserver(ICryptonoteProtocolObserver* observer) { +bool CryptoNoteProtocolHandler::removeObserver(ICryptoNoteProtocolObserver* observer) { return m_observerManager.remove(observer); } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h old mode 100644 new mode 100755 similarity index 51% rename from src/cryptonote_protocol/cryptonote_protocol_handler.h rename to src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h index bdd8261d69..0c606f241e --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/CryptoNoteProtocol/CryptoNoteProtocolHandler.h @@ -19,19 +19,18 @@ #include -#include #include -#include "cryptonote_core/ICore.h" +#include "CryptoNoteCore/ICore.h" -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" -#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" -#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolObserver.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" -#include "p2p/p2p_protocol_defs.h" -#include "p2p/net_node_common.h" -#include "p2p/connection_context.h" +#include "P2p/P2pProtocolDefinitions.h" +#include "P2p/NetNodeCommon.h" +#include "P2p/ConnectionContext.h" #include @@ -43,57 +42,57 @@ namespace CryptoNote { class Currency; - class cryptonote_protocol_handler : + class CryptoNoteProtocolHandler : public i_cryptonote_protocol, - public ICryptonoteProtocolQuery + public ICryptoNoteProtocolQuery { public: - cryptonote_protocol_handler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, i_p2p_endpoint* p_net_layout, Logging::ILogger& log); + CryptoNoteProtocolHandler(const Currency& currency, System::Dispatcher& dispatcher, ICore& rcore, IP2pEndpoint* p_net_layout, Logging::ILogger& log); - virtual bool addObserver(ICryptonoteProtocolObserver* observer) override; - virtual bool removeObserver(ICryptonoteProtocolObserver* observer) override; + virtual bool addObserver(ICryptoNoteProtocolObserver* observer); + virtual bool removeObserver(ICryptoNoteProtocolObserver* observer); - void set_p2p_endpoint(i_p2p_endpoint* p2p); + void set_p2p_endpoint(IP2pEndpoint* p2p); // ICore& get_core() { return m_core; } virtual bool isSynchronized() const override { return m_synchronized; } void log_connections(); // Interface t_payload_net_handler, where t_payload_net_handler is template argument of nodetool::node_server void stop(); - bool start_sync(cryptonote_connection_context& context); + bool start_sync(CryptoNoteConnectionContext& context); bool on_idle(); - void onConnectionOpened(cryptonote_connection_context& context); - void onConnectionClosed(cryptonote_connection_context& context); + void onConnectionOpened(CryptoNoteConnectionContext& context); + void onConnectionClosed(CryptoNoteConnectionContext& context); bool get_stat_info(core_stat_info& stat_inf); bool get_payload_sync_data(CORE_SYNC_DATA& hshd); - bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); - int handleCommand(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, cryptonote_connection_context& context, bool& handled); - virtual size_t getPeerCount() const override; - virtual uint64_t getObservedHeight() const override; - void requestMissingPoolTransactions(const cryptonote_connection_context& context); + bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, CryptoNoteConnectionContext& context, bool is_inital); + int handleCommand(bool is_notify, int command, const BinaryArray& in_buff, BinaryArray& buff_out, CryptoNoteConnectionContext& context, bool& handled); + virtual size_t getPeerCount() const; + virtual uint32_t getObservedHeight() const; + void requestMissingPoolTransactions(const CryptoNoteConnectionContext& context); private: //----------------- commands handlers ---------------------------------------------- - int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context); - int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context); - int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context); - int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context); - int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); - int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); - int handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, cryptonote_connection_context& context); + int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, CryptoNoteConnectionContext& context); + int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, CryptoNoteConnectionContext& context); + int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context); + int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, CryptoNoteConnectionContext& context); + int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, CryptoNoteConnectionContext& context); + int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, CryptoNoteConnectionContext& context); + int handleRequestTxPool(int command, NOTIFY_REQUEST_TX_POOL::request& arg, CryptoNoteConnectionContext& context); //----------------- i_cryptonote_protocol ---------------------------------- virtual void relay_block(NOTIFY_NEW_BLOCK::request& arg) override; virtual void relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg) override; //---------------------------------------------------------------------------------- - uint64_t get_current_blockchain_height(); - bool request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks); + uint32_t get_current_blockchain_height(); + bool request_missing_objects(CryptoNoteConnectionContext& context, bool check_having_blocks); bool on_connection_synchronized(); - void updateObservedHeight(uint64_t peerHeight, const cryptonote_connection_context& context); - void recalculateMaxObservedHeight(const cryptonote_connection_context& context); - int processObjects(cryptonote_connection_context& context, const std::list& blocks); + void updateObservedHeight(uint32_t peerHeight, const CryptoNoteConnectionContext& context); + void recalculateMaxObservedHeight(const CryptoNoteConnectionContext& context); + int processObjects(CryptoNoteConnectionContext& context, const std::vector& blocks); Logging::LoggerRef logger; private: @@ -103,14 +102,14 @@ namespace CryptoNote const Currency& m_currency; p2p_endpoint_stub m_p2p_stub; - i_p2p_endpoint* m_p2p; + IP2pEndpoint* m_p2p; std::atomic m_synchronized; std::atomic m_stop; mutable std::mutex m_observedHeightMutex; - uint64_t m_observedHeight; + uint32_t m_observedHeight; std::atomic m_peersCount; - tools::ObserverManager m_observerManager; + Tools::ObserverManager m_observerManager; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h old mode 100644 new mode 100755 similarity index 100% rename from src/cryptonote_protocol/cryptonote_protocol_handler_common.h rename to src/CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h diff --git a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h b/src/CryptoNoteProtocol/ICryptoNoteProtocolObserver.h old mode 100644 new mode 100755 similarity index 85% rename from src/cryptonote_protocol/ICryptonoteProtocolObserver.h rename to src/CryptoNoteProtocol/ICryptoNoteProtocolObserver.h index 34c1140215..0d22c38049 --- a/src/cryptonote_protocol/ICryptonoteProtocolObserver.h +++ b/src/CryptoNoteProtocol/ICryptoNoteProtocolObserver.h @@ -23,11 +23,11 @@ namespace CryptoNote { -class ICryptonoteProtocolObserver { +class ICryptoNoteProtocolObserver { public: virtual void peerCountUpdated(size_t count) {} - virtual void lastKnownBlockHeightUpdated(uint64_t height) {} - virtual void blockchainSynchronized(uint64_t topHeight) {} + virtual void lastKnownBlockHeightUpdated(uint32_t height) {} + virtual void blockchainSynchronized(uint32_t topHeight) {} }; } //namespace CryptoNote diff --git a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h b/src/CryptoNoteProtocol/ICryptoNoteProtocolQuery.h old mode 100644 new mode 100755 similarity index 80% rename from src/cryptonote_protocol/ICryptonoteProtocolQuery.h rename to src/CryptoNoteProtocol/ICryptoNoteProtocolQuery.h index b9d422790b..eb353fb2a0 --- a/src/cryptonote_protocol/ICryptonoteProtocolQuery.h +++ b/src/CryptoNoteProtocol/ICryptoNoteProtocolQuery.h @@ -21,14 +21,14 @@ #include namespace CryptoNote { -class ICryptonoteProtocolObserver; +class ICryptoNoteProtocolObserver; -class ICryptonoteProtocolQuery { +class ICryptoNoteProtocolQuery { public: - virtual bool addObserver(ICryptonoteProtocolObserver* observer) = 0; - virtual bool removeObserver(ICryptonoteProtocolObserver* observer) = 0; + virtual bool addObserver(ICryptoNoteProtocolObserver* observer) = 0; + virtual bool removeObserver(ICryptoNoteProtocolObserver* observer) = 0; - virtual uint64_t getObservedHeight() const = 0; + virtual uint32_t getObservedHeight() const = 0; virtual size_t getPeerCount() const = 0; virtual bool isSynchronized() const = 0; }; diff --git a/src/daemon/daemon.cpp b/src/Daemon/Daemon.cpp old mode 100644 new mode 100755 similarity index 92% rename from src/daemon/daemon.cpp rename to src/Daemon/Daemon.cpp index 3c5e6957c3..c96783caf6 --- a/src/daemon/daemon.cpp +++ b/src/Daemon/Daemon.cpp @@ -25,15 +25,15 @@ #include "Common/SignalHandler.h" #include "Common/PathTools.h" #include "crypto/hash.h" -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/CoreConfig.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/MinerConfig.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "p2p/net_node.h" -#include "p2p/NetNodeConfig.h" -#include "rpc/RpcServer.h" -#include "rpc/RpcServerConfig.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteCore/CoreConfig.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/MinerConfig.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "P2p/NetNode.h" +#include "P2p/NetNodeConfig.h" +#include "Rpc/RpcServer.h" +#include "Rpc/RpcServerConfig.h" #include "version.h" #include @@ -100,7 +100,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_only, command_line::arg_version); command_line::add_arg(desc_cmd_only, arg_os_version); // tools::get_default_data_dir() can't be called during static initialization - command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); command_line::add_arg(desc_cmd_only, arg_config_file); command_line::add_arg(desc_cmd_sett, arg_log_file); @@ -183,7 +183,7 @@ int main(int argc, char* argv[]) CryptoNote::Currency currency = currencyBuilder.currency(); CryptoNote::core ccore(currency, nullptr, logManager); - CryptoNote::checkpoints checkpoints(logManager); + CryptoNote::Checkpoints checkpoints(logManager); for (const auto& cp : CryptoNote::CHECKPOINTS) { checkpoints.add_checkpoint(cp.height, cp.blockId); } @@ -196,6 +196,7 @@ int main(int argc, char* argv[]) coreConfig.init(vm); NetNodeConfig netNodeConfig; netNodeConfig.init(vm); + netNodeConfig.setTestnet(testnet_mode); MinerConfig minerConfig; minerConfig.init(vm); RpcServerConfig rpcConfig; @@ -203,8 +204,8 @@ int main(int argc, char* argv[]) System::Dispatcher dispatcher; - CryptoNote::cryptonote_protocol_handler cprotocol(currency, dispatcher, ccore, nullptr, logManager); - CryptoNote::node_server p2psrv(dispatcher, cprotocol, logManager); + CryptoNote::CryptoNoteProtocolHandler cprotocol(currency, dispatcher, ccore, nullptr, logManager); + CryptoNote::NodeServer p2psrv(dispatcher, cprotocol, logManager); CryptoNote::RpcServer rpcServer(dispatcher, logManager, ccore, p2psrv); cprotocol.set_p2p_endpoint(&p2psrv); @@ -213,7 +214,7 @@ int main(int argc, char* argv[]) // initialize objects logger(INFO) << "Initializing p2p server..."; - if (!p2psrv.init(netNodeConfig, testnet_mode)) { + if (!p2psrv.init(netNodeConfig)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize p2p server."; return 1; } @@ -243,9 +244,9 @@ int main(int argc, char* argv[]) rpcServer.start(rpcConfig.bindIp, rpcConfig.bindPort); logger(INFO) << "Core rpc server started ok"; - tools::SignalHandler::install([&dch, &p2psrv] { + Tools::SignalHandler::install([&dch, &p2psrv] { dch.stop_handling(); - p2psrv.send_stop_signal(); + p2psrv.sendStopSignal(); }); logger(INFO) << "Starting p2p net loop..."; @@ -284,7 +285,7 @@ bool command_line_preprocessor(const boost::program_options::variables_map &vm, exit = true; } if (command_line::get_arg(vm, arg_os_version)) { - std::cout << "OS: " << tools::get_os_version_string() << ENDL; + std::cout << "OS: " << Tools::get_os_version_string() << ENDL; exit = true; } diff --git a/src/daemon/DeamonCommandsHandler.cpp b/src/Daemon/DaemonCommandsHandler.cpp old mode 100644 new mode 100755 similarity index 90% rename from src/daemon/DeamonCommandsHandler.cpp rename to src/Daemon/DaemonCommandsHandler.cpp index 241424ca02..c0d3573bde --- a/src/daemon/DeamonCommandsHandler.cpp +++ b/src/Daemon/DaemonCommandsHandler.cpp @@ -17,23 +17,23 @@ #include "DaemonCommandsHandler.h" -#include "p2p/net_node.h" -#include "cryptonote_core/miner.h" -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" - +#include "P2p/NetNode.h" +#include "CryptoNoteCore/Miner.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "Serialization/SerializationTools.h" #include "version.h" namespace { template static bool print_as_json(const T& obj) { - std::cout << CryptoNote::obj_to_json_str(obj) << ENDL; + std::cout << CryptoNote::storeToJson(obj) << ENDL; return true; } } -DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::node_server& srv, Logging::LoggerManager& log) : +DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::NodeServer& srv, Logging::LoggerManager& log) : m_core(core), m_srv(srv), logger(log, "daemon"), m_logManager(log) { m_consoleHandler.setHandler("exit", boost::bind(&DaemonCommandsHandler::exit, this, _1), "Shutdown the daemon"); m_consoleHandler.setHandler("help", boost::bind(&DaemonCommandsHandler::help, this, _1), "Show this help"); @@ -50,7 +50,7 @@ DaemonCommandsHandler::DaemonCommandsHandler(CryptoNote::core& core, CryptoNote: m_consoleHandler.setHandler("print_pool_sh", boost::bind(&DaemonCommandsHandler::print_pool_sh, this, _1), "Print transaction pool (short format)"); m_consoleHandler.setHandler("show_hr", boost::bind(&DaemonCommandsHandler::show_hr, this, _1), "Start showing hash rate"); m_consoleHandler.setHandler("hide_hr", boost::bind(&DaemonCommandsHandler::hide_hr, this, _1), "Stop showing hash rate"); - m_consoleHandler.setHandler("set_log", boost::bind(&DaemonCommandsHandler::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); + m_consoleHandler.setHandler("set_log", boost::bind(&DaemonCommandsHandler::set_log, this, _1), "set_log - Change current log level, is a number 0-4"); } //-------------------------------------------------------------------------------- @@ -69,7 +69,7 @@ std::string DaemonCommandsHandler::get_commands_str() //-------------------------------------------------------------------------------- bool DaemonCommandsHandler::exit(const std::vector& args) { m_consoleHandler.requestStop(); - m_srv.send_stop_signal(); + m_srv.sendStopSignal(); return true; } @@ -88,7 +88,7 @@ bool DaemonCommandsHandler::show_hr(const std::vector& args) { if (!m_core.get_miner().is_mining()) { - std::cout << "Mining is not started. You need start mining before you can see hash rate." << ENDL; + std::cout << "Mining is not started. You need to start mining before you can see hash rate." << ENDL; } else { m_core.get_miner().do_print_hashrate(true); @@ -125,9 +125,9 @@ bool DaemonCommandsHandler::print_bc(const std::vector &args) { return false; } - uint64_t start_index = 0; - uint64_t end_index = 0; - uint64_t end_block_parametr = m_core.get_current_blockchain_height(); + uint32_t start_index = 0; + uint32_t end_index = 0; + uint32_t end_block_parametr = m_core.get_current_blockchain_height(); if (!Common::fromString(args[0], start_index)) { std::cout << "wrong starter block index parameter" << ENDL; return false; @@ -187,7 +187,7 @@ bool DaemonCommandsHandler::set_log(const std::vector& args) } //-------------------------------------------------------------------------------- -bool DaemonCommandsHandler::print_block_by_height(uint64_t height) +bool DaemonCommandsHandler::print_block_by_height(uint32_t height) { std::list blocks; m_core.get_blocks(height, 1, blocks); @@ -196,8 +196,8 @@ bool DaemonCommandsHandler::print_block_by_height(uint64_t height) std::cout << "block_id: " << get_block_hash(blocks.front()) << ENDL; print_as_json(blocks.front()); } else { - uint64_t current_height; - crypto::hash top_id; + uint32_t current_height; + Crypto::Hash top_id; m_core.get_blockchain_top(current_height, top_id); std::cout << "block wasn't found. Current block chain height: " << current_height << ", requested: " << height << std::endl; return false; @@ -208,15 +208,15 @@ bool DaemonCommandsHandler::print_block_by_height(uint64_t height) //-------------------------------------------------------------------------------- bool DaemonCommandsHandler::print_block_by_hash(const std::string& arg) { - crypto::hash block_hash; + Crypto::Hash block_hash; if (!parse_hash256(arg, block_hash)) { return false; } - std::list block_ids; + std::list block_ids; block_ids.push_back(block_hash); std::list blocks; - std::list missed_ids; + std::list missed_ids; m_core.get_blocks(block_ids, blocks, missed_ids); if (1 == blocks.size()) @@ -239,7 +239,7 @@ bool DaemonCommandsHandler::print_block(const std::vector &args) { const std::string &arg = args.front(); try { - uint64_t height = boost::lexical_cast(arg); + uint32_t height = boost::lexical_cast(arg); print_block_by_height(height); } catch (boost::bad_lexical_cast &) { print_block_by_hash(arg); @@ -256,16 +256,16 @@ bool DaemonCommandsHandler::print_tx(const std::vector& args) } const std::string &str_hash = args.front(); - crypto::hash tx_hash; + Crypto::Hash tx_hash; if (!parse_hash256(str_hash, tx_hash)) { return true; } - std::vector tx_ids; + std::vector tx_ids; tx_ids.push_back(tx_hash); std::list txs; - std::list missed_ids; - m_core.getTransactions(tx_ids, txs, missed_ids); + std::list missed_ids; + m_core.getTransactions(tx_ids, txs, missed_ids, true); if (1 == txs.size()) { print_as_json(txs.front()); @@ -306,10 +306,7 @@ bool DaemonCommandsHandler::start_mining(const std::vector &args) { threads_count = (ok && 0 < threads_count) ? threads_count : 1; } - boost::thread::attributes attrs; - attrs.set_stack_size(CryptoNote::THREAD_STACK_SIZE); - - m_core.get_miner().start(adr, threads_count, attrs); + m_core.get_miner().start(adr, threads_count); return true; } diff --git a/src/daemon/DaemonCommandsHandler.h b/src/Daemon/DaemonCommandsHandler.h old mode 100644 new mode 100755 similarity index 91% rename from src/daemon/DaemonCommandsHandler.h rename to src/Daemon/DaemonCommandsHandler.h index bd483cb07d..b65d505c05 --- a/src/daemon/DaemonCommandsHandler.h +++ b/src/Daemon/DaemonCommandsHandler.h @@ -24,13 +24,13 @@ namespace CryptoNote { class core; -class node_server; +class NodeServer; } class DaemonCommandsHandler { public: - DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::node_server& srv, Logging::LoggerManager& log); + DaemonCommandsHandler(CryptoNote::core& core, CryptoNote::NodeServer& srv, Logging::LoggerManager& log); bool start_handling() { m_consoleHandler.start(); @@ -45,12 +45,12 @@ class DaemonCommandsHandler Common::ConsoleHandler m_consoleHandler; CryptoNote::core& m_core; - CryptoNote::node_server& m_srv; + CryptoNote::NodeServer& m_srv; Logging::LoggerRef logger; Logging::LoggerManager& m_logManager; std::string get_commands_str(); - bool print_block_by_height(uint64_t height); + bool print_block_by_height(uint32_t height); bool print_block_by_hash(const std::string& arg); bool exit(const std::vector& args); diff --git a/src/HTTP/HttpParserErrorCodes.cpp b/src/HTTP/HttpParserErrorCodes.cpp old mode 100644 new mode 100755 index f473b609e5..f211ac2890 --- a/src/HTTP/HttpParserErrorCodes.cpp +++ b/src/HTTP/HttpParserErrorCodes.cpp @@ -23,4 +23,4 @@ namespace error { HttpParserErrorCategory HttpParserErrorCategory::INSTANCE; } //namespace error -} //namespace cryptonote +} //namespace CryptoNote diff --git a/src/HTTP/HttpParserErrorCodes.h b/src/HTTP/HttpParserErrorCodes.h old mode 100644 new mode 100755 index ad8475e2ee..f4361fa51d --- a/src/HTTP/HttpParserErrorCodes.h +++ b/src/HTTP/HttpParserErrorCodes.h @@ -59,7 +59,7 @@ class HttpParserErrorCategory : public std::error_category { }; } //namespace error -} //namespace cryptonote +} //namespace CryptoNote inline std::error_code make_error_code(CryptoNote::error::HttpParserErrorCodes e) { return std::error_code(static_cast(e), CryptoNote::error::HttpParserErrorCategory::INSTANCE); diff --git a/src/HTTP/HttpResponse.cpp b/src/HTTP/HttpResponse.cpp index a15b982027..df5b665fae 100755 --- a/src/HTTP/HttpResponse.cpp +++ b/src/HTTP/HttpResponse.cpp @@ -55,7 +55,7 @@ namespace CryptoNote { HttpResponse::HttpResponse() { status = STATUS_200; - headers["Server"] = "Cryptonote-based HTTP server"; + headers["Server"] = "CryptoNote-based HTTP server"; } void HttpResponse::setStatus(HTTP_STATUS s) { diff --git a/src/InProcessNode/InProcessNode.cpp b/src/InProcessNode/InProcessNode.cpp index 0602febc5d..d1c20ac246 100644 --- a/src/InProcessNode/InProcessNode.cpp +++ b/src/InProcessNode/InProcessNode.cpp @@ -19,16 +19,23 @@ #include #include +#include +#include "CryptoNoteConfig.h" #include "Common/StringTools.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/verification_context.h" -#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/IBlock.h" +#include "CryptoNoteCore/VerificationContext.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandlerCommon.h" #include "InProcessNodeErrors.h" +#include "Common/StringTools.h" + +using namespace Crypto; +using namespace Common; namespace CryptoNote { -InProcessNode::InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol) : +InProcessNode::InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol) : state(NOT_INITIALIZED), core(core), protocol(protocol), @@ -97,11 +104,11 @@ bool InProcessNode::doShutdown() { } void InProcessNode::workerFunc() { - ioService.run(); + ioService.run(); } -void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, - uint64_t& startHeight, const Callback& callback) +void InProcessNode::getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -121,15 +128,15 @@ void InProcessNode::getNewBlocks(std::list&& knownBlockIds, std::l ); } -void InProcessNode::getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, - uint64_t& startHeight, const Callback& callback) +void InProcessNode::getNewBlocksAsync(std::vector& knownBlockIds, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { std::error_code ec = doGetNewBlocks(std::move(knownBlockIds), newBlocks, startHeight); callback(ec); } //it's always protected with mutex -std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight) { +std::error_code InProcessNode::doGetNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight) { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -138,18 +145,29 @@ std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlo } try { - uint64_t totalHeight; - std::list > > bs; - if (!core.find_blockchain_supplement(knownBlockIds, bs, totalHeight, startHeight, 1000)) { + // TODO code duplication see RpcServer::on_get_blocks() + if (knownBlockIds.empty()) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + + if (knownBlockIds.back() != core.getBlockIdByHeight(0)) { return make_error_code(CryptoNote::error::REQUEST_ERROR); } - for (auto& b : bs) { + uint32_t totalBlockCount; + std::vector supplement = core.findBlockchainSupplement(knownBlockIds, CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, totalBlockCount, startHeight); + + for (const auto& blockId : supplement) { + assert(core.have_block(blockId)); + auto completeBlock = core.getBlock(blockId); + assert(completeBlock != nullptr); + CryptoNote::block_complete_entry be; - be.block = CryptoNote::block_to_blob(b.first); + be.block = asString(toBinaryArray(completeBlock->getBlock())); - for (auto& t : b.second) { - be.txs.push_back(CryptoNote::tx_to_blob(t)); + be.txs.reserve(completeBlock->getTransactionCount()); + for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { + be.txs.push_back(asString(toBinaryArray(completeBlock->getTransaction(i)))); } newBlocks.push_back(std::move(be)); @@ -163,7 +181,7 @@ std::error_code InProcessNode::doGetNewBlocks(std::list&& knownBlo return std::error_code(); } -void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, +void InProcessNode::getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { std::unique_lock lock(mutex); @@ -183,7 +201,7 @@ void InProcessNode::getTransactionOutsGlobalIndices(const crypto::hash& transact ); } -void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, +void InProcessNode::getTransactionOutsGlobalIndicesAsync(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { std::error_code ec = doGetTransactionOutsGlobalIndices(transactionHash, outsGlobalIndices); @@ -191,7 +209,7 @@ void InProcessNode::getTransactionOutsGlobalIndicesAsync(const crypto::hash& tra } //it's always protected with mutex -std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { +std::error_code InProcessNode::doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices) { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -304,10 +322,10 @@ std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& } try { - CryptoNote::blobdata txBlob = CryptoNote::tx_to_blob(transaction); + CryptoNote::BinaryArray transactionBinaryArray = toBinaryArray(transaction); CryptoNote::tx_verification_context tvc = boost::value_initialized(); - if(!core.handle_incoming_tx(txBlob, tvc, false)) { + if (!core.handle_incoming_tx(transactionBinaryArray, tvc, false)) { return make_error_code(CryptoNote::error::REQUEST_ERROR); } @@ -320,7 +338,7 @@ std::error_code InProcessNode::doRelayTransaction(const CryptoNote::Transaction& } CryptoNote::NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(txBlob); + r.txs.push_back(asString(transactionBinaryArray)); core.get_protocol()->relay_transactions(r); } catch (std::system_error& e) { return e.code(); @@ -342,7 +360,7 @@ size_t InProcessNode::getPeerCount() const { return protocol.getPeerCount(); } -uint64_t InProcessNode::getLocalBlockCount() const { +uint32_t InProcessNode::getLocalBlockCount() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -350,15 +368,15 @@ uint64_t InProcessNode::getLocalBlockCount() const { } } - uint64_t lastIndex; - crypto::hash ignore; + uint32_t lastIndex; + Crypto::Hash ignore; core.get_blockchain_top(lastIndex, ignore); return lastIndex + 1; } -uint64_t InProcessNode::getKnownBlockCount() const { +uint32_t InProcessNode::getKnownBlockCount() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -369,7 +387,7 @@ uint64_t InProcessNode::getKnownBlockCount() const { return protocol.getObservedHeight(); } -uint64_t InProcessNode::getLastLocalBlockHeight() const { +uint32_t InProcessNode::getLastLocalBlockHeight() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -377,17 +395,15 @@ uint64_t InProcessNode::getLastLocalBlockHeight() const { } } - uint64_t height; - crypto::hash ignore; + uint32_t height; + Crypto::Hash ignore; - if (!core.get_blockchain_top(height, ignore)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); - } + core.get_blockchain_top(height, ignore); return height; } -uint64_t InProcessNode::getLastKnownBlockHeight() const { +uint32_t InProcessNode::getLastKnownBlockHeight() const { { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -398,14 +414,6 @@ uint64_t InProcessNode::getLastKnownBlockHeight() const { return protocol.getObservedHeight() - 1; } -void InProcessNode::peerCountUpdated(size_t count) { - observerManager.notify(&INodeObserver::peerCountUpdated, count); -} - -void InProcessNode::lastKnownBlockHeightUpdated(uint64_t height) { - observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); -} - uint64_t InProcessNode::getLastLocalBlockTimestamp() const { std::unique_lock lock(mutex); if (state != INITIALIZED) { @@ -413,12 +421,10 @@ uint64_t InProcessNode::getLastLocalBlockTimestamp() const { } lock.unlock(); - uint64_t ignore; - crypto::hash hash; + uint32_t ignore; + Crypto::Hash hash; - if (!core.get_blockchain_top(ignore, hash)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR)); - } + core.get_blockchain_top(ignore, hash); CryptoNote::Block block; if (!core.getBlockByHash(hash, block)) { @@ -428,9 +434,17 @@ uint64_t InProcessNode::getLastLocalBlockTimestamp() const { return block.timestamp; } +void InProcessNode::peerCountUpdated(size_t count) { + observerManager.notify(&INodeObserver::peerCountUpdated, count); +} + +void InProcessNode::lastKnownBlockHeightUpdated(uint32_t height) { + observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, height); +} + void InProcessNode::blockchainUpdated() { - uint64_t height; - crypto::hash ignore; + uint32_t height; + Crypto::Hash ignore; core.get_blockchain_top(height, ignore); observerManager.notify(&INodeObserver::localBlockchainUpdated, height); @@ -440,13 +454,12 @@ void InProcessNode::poolUpdated() { observerManager.notify(&INodeObserver::poolChanged); } -void InProcessNode::blockchainSynchronized(uint64_t topHeight) { +void InProcessNode::blockchainSynchronized(uint32_t topHeight) { observerManager.notify(&INodeObserver::blockchainSynchronized, topHeight); } -void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, - std::list& newBlocks, uint64_t& startHeight, const InProcessNode::Callback& callback) -{ +void InProcessNode::queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -455,48 +468,60 @@ void InProcessNode::queryBlocks(std::list&& knownBlockIds, uint64_ } ioService.post( - std::bind(&InProcessNode::queryBlocksAsync, - this, - std::move(knownBlockIds), - timestamp, - std::ref(newBlocks), - std::ref(startHeight), - callback - ) + std::bind(&InProcessNode::queryBlocksLiteAsync, + this, + std::move(knownBlockIds), + timestamp, + std::ref(newBlocks), + std::ref(startHeight), + callback + ) ); } -void InProcessNode::queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, - std::list& newBlocks, uint64_t& startHeight, const Callback& callback) -{ - std::error_code ec = doQueryBlocks(std::move(knownBlockIds), timestamp, newBlocks, startHeight); +void InProcessNode::queryBlocksLiteAsync(std::vector& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, + const Callback& callback) { + std::error_code ec = doQueryBlocksLite(std::move(knownBlockIds), timestamp, newBlocks, startHeight); callback(ec); } -std::error_code InProcessNode::doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, - std::list& newBlocks, uint64_t& startHeight) { - uint64_t currentHeight, fullOffset; - std::list entries; +std::error_code InProcessNode::doQueryBlocksLite(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight) { + uint32_t currentHeight, fullOffset; + std::vector entries; - if (!core.queryBlocks(knownBlockIds, timestamp, startHeight, currentHeight, fullOffset, entries)) { + if (!core.queryBlocksLite(knownBlockIds, timestamp, startHeight, currentHeight, fullOffset, entries)) { return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } for (const auto& entry: entries) { - BlockCompleteEntry bce; - bce.blockHash = entry.block_id; - bce.block = entry.block; - std::copy(entry.txs.begin(), entry.txs.end(), std::back_inserter(bce.txs)); + BlockShortEntry bse; + bse.blockHash = entry.blockId; + bse.hasBlock = false; + + if (!entry.block.empty()) { + bse.hasBlock = true; + if (!fromBinaryArray(bse.block, asBinaryArray(entry.block))) { + return std::make_error_code(std::errc::invalid_argument); + } + } - newBlocks.push_back(std::move(bce)); + for (const auto& tsi: entry.txPrefixes) { + TransactionShortInfo tpi; + tpi.txId = tsi.txHash; + tpi.txPrefix = tsi.txPrefix; + + bse.txsShortInfo.push_back(std::move(tpi)); + } + + newBlocks.push_back(std::move(bse)); } return std::error_code(); -} -void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, crypto::hash knownBlockId, bool& isBcActual, std::vector& newTxs, - std::vector& deletedTxIds, const Callback& callback) { +} +void InProcessNode::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -504,32 +529,62 @@ void InProcessNode::getPoolSymmetricDifference(std::vector&& known return; } - ioService.post( - std::bind(&InProcessNode::getPoolSymmetricDifferenceAsync, - this, - std::move(knownPoolTxIds), - knownBlockId, - std::ref(isBcActual), - std::ref(newTxs), - std::ref(deletedTxIds), - callback - ) - ); + ioService.post([this, knownPoolTxIds, knownBlockId, &isBcActual, &newTxs, &deletedTxIds, callback] () mutable { + this->getPoolSymmetricDifferenceAsync(std::move(knownPoolTxIds), knownBlockId, isBcActual, newTxs, deletedTxIds, callback); + }); } -void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, - std::vector& deleted_tx_ids, const Callback& callback) { +void InProcessNode::getPoolSymmetricDifferenceAsync(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { std::error_code ec = std::error_code(); - is_bc_actual = core.getPoolChanges(known_block_id, known_pool_tx_ids, new_txs, deleted_tx_ids); - if (!is_bc_actual) { + std::vector added; + isBcActual = core.getPoolChangesLite(knownBlockId, knownPoolTxIds, added, deletedTxIds); + if (!isBcActual) { ec = make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + callback(ec); + return; + } + + try { + for (const auto& tx: added) { + newTxs.push_back(createTransactionPrefix(tx.txPrefix, reinterpret_cast(tx.txHash))); + } + } catch (std::system_error& ex) { + ec = ex.code(); + } catch (std::exception&) { + ec = make_error_code(std::errc::invalid_argument); + } + + callback(ec); +} + +void InProcessNode::getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post([this, amount, gindex, &out, callback]() mutable { + this->getOutByMSigGIndexAsync(amount, gindex, out, callback); + }); +} + +void InProcessNode::getOutByMSigGIndexAsync(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) { + std::error_code ec = std::error_code(); + bool result = core.getOutByMSigGIndex(amount, gindex, out); + if (!result) { + ec = make_error_code(std::errc::invalid_argument); + callback(ec); + return; } callback(ec); } -void InProcessNode::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { +void InProcessNode::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -541,7 +596,7 @@ void InProcessNode::getBlocks(const std::vector& blockHeights, std::ve std::bind( static_cast< void(InProcessNode::*)( - const std::vector&, + const std::vector&, std::vector>&, const Callback& ) @@ -554,38 +609,129 @@ void InProcessNode::getBlocks(const std::vector& blockHeights, std::ve ); } -void InProcessNode::getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { - std::error_code ec = doGetBlocks(blockHeights, blocks); +void InProcessNode::getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + const std::vector&, + std::vector>& + ) + >(&InProcessNode::doGetBlocks), + this, + std::cref(blockHeights), + std::ref(blocks) + ) + ); callback(ec); } -std::error_code InProcessNode::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks) { - uint64_t topHeight = 0; - crypto::hash topHash = boost::value_initialized(); - if (!core.get_blockchain_top(topHeight, topHash)) { +std::error_code InProcessNode::doGetBlocks(const std::vector& blockHeights, std::vector>& blocks) { + try { + uint32_t topHeight = 0; + Crypto::Hash topHash = boost::value_initialized(); + core.get_blockchain_top(topHeight, topHash); + for (const uint32_t& height : blockHeights) { + if (height > topHeight) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + Crypto::Hash hash = core.getBlockIdByHeight(height); + Block block; + if (!core.getBlockByHash(hash, block)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + BlockDetails blockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + std::vector blocksOnSameHeight; + blocksOnSameHeight.push_back(std::move(blockDetails)); + + //Getting orphans + std::vector orphanBlocks; + core.getOrphanBlocksByHeight(height, orphanBlocks); + for (const Block& orphanBlock : orphanBlocks) { + BlockDetails orphanBlockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(orphanBlock, orphanBlockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocksOnSameHeight.push_back(std::move(orphanBlockDetails)); + } + blocks.push_back(std::move(blocksOnSameHeight)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } - for (const uint64_t& height : blockHeights) { - if (height > topHeight) { - return make_error_code(CryptoNote::error::REQUEST_ERROR); - } - crypto::hash hash = core.getBlockIdByHeight(height); - Block block; - if (!core.getBlockByHash(hash, block)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); - } - BlockDetails blockDetails; - if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + + return std::error_code(); +} + +void InProcessNode::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + static_cast< + void(InProcessNode::*)( + const std::vector&, + std::vector&, + const Callback& + ) + >(&InProcessNode::getBlocksAsync), + this, + std::cref(blockHashes), + std::ref(blocks), + callback + ) + ); +} + +void InProcessNode::getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + const std::vector&, + std::vector& + ) + >(&InProcessNode::doGetBlocks), + this, + std::cref(blockHashes), + std::ref(blocks) + ) + ); + callback(ec); +} + +std::error_code InProcessNode::doGetBlocks(const std::vector& blockHashes, std::vector& blocks) { + try { + for (const Crypto::Hash& hash : blockHashes) { + Block block; + if (!core.getBlockByHash(hash, block)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + BlockDetails blockDetails; + if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocks.push_back(std::move(blockDetails)); } - std::vector blocksOnSameHeight; - blocksOnSameHeight.push_back(std::move(blockDetails)); - blocks.push_back(std::move(blocksOnSameHeight)); + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } -void InProcessNode::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { +void InProcessNode::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -597,40 +743,71 @@ void InProcessNode::getBlocks(const std::vector& blockHashes, std: std::bind( static_cast< void(InProcessNode::*)( - const std::vector&, + uint64_t, + uint64_t, + uint32_t, std::vector&, + uint32_t&, const Callback& ) >(&InProcessNode::getBlocksAsync), this, - std::cref(blockHashes), + timestampBegin, + timestampEnd, + blocksNumberLimit, std::ref(blocks), + std::ref(blocksNumberWithinTimestamps), callback ) ); } -void InProcessNode::getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { - std::error_code ec = doGetBlocks(blockHashes, blocks); +void InProcessNode::getBlocksAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + uint64_t, + uint64_t, + uint32_t, + std::vector&, + uint32_t& + ) + >(&InProcessNode::doGetBlocks), + this, + timestampBegin, + timestampEnd, + blocksNumberLimit, + std::ref(blocks), + std::ref(blocksNumberWithinTimestamps) + ) + ); + callback(ec); } -std::error_code InProcessNode::doGetBlocks(const std::vector& blockHashes, std::vector& blocks) { - for (const crypto::hash& hash : blockHashes) { - Block block; - if (!core.getBlockByHash(hash, block)) { +std::error_code InProcessNode::doGetBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps) { + try { + std::vector rawBlocks; + if (!core.getBlocksByTimestamp(timestampBegin, timestampEnd, blocksNumberLimit, rawBlocks, blocksNumberWithinTimestamps)) { return make_error_code(CryptoNote::error::REQUEST_ERROR); } - BlockDetails blockDetails; - if (!blockchainExplorerDataBuilder.fillBlockDetails(block, blockDetails)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + for (const Block& rawBlock : rawBlocks) { + BlockDetails block; + if (!blockchainExplorerDataBuilder.fillBlockDetails(rawBlock, block)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + blocks.push_back(std::move(block)); } - blocks.push_back(std::move(blockDetails)); + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } -void InProcessNode::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { +void InProcessNode::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { std::unique_lock lock(mutex); if (state != INITIALIZED) { lock.unlock(); @@ -640,7 +817,13 @@ void InProcessNode::getTransactions(const std::vector& transaction ioService.post( std::bind( - &InProcessNode::getTransactionsAsync, + static_cast< + void(InProcessNode::*)( + const std::vector&, + std::vector&, + const Callback& + ) + >(&InProcessNode::getTransactionsAsync), this, std::cref(transactionHashes), std::ref(transactions), @@ -649,24 +832,154 @@ void InProcessNode::getTransactions(const std::vector& transaction ); } -void InProcessNode::getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { - std::error_code ec= doGetTransactions(transactionHashes, transactions); +void InProcessNode::getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + static_cast< + std::error_code(InProcessNode::*)( + const std::vector&, + std::vector& + ) + >(&InProcessNode::doGetTransactions), + this, + std::cref(transactionHashes), + std::ref(transactions) + ) + ); callback(ec); } -std::error_code InProcessNode::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions) { - std::list txs; - std::list missed_txs; - core.getTransactions(transactionHashes, txs, missed_txs, true); - if (missed_txs.size() > 0) { - return make_error_code(CryptoNote::error::REQUEST_ERROR); +std::error_code InProcessNode::doGetTransactions(const std::vector& transactionHashes, std::vector& transactions) { + try { + std::list txs; + std::list missed_txs; + core.getTransactions(transactionHashes, txs, missed_txs, true); + if (missed_txs.size() > 0) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Transaction& tx : txs) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(tx, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +void InProcessNode::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; } - for (const Transaction& tx : txs) { - TransactionDetails transactionDetails; - if (!blockchainExplorerDataBuilder.fillTransactionDetails(tx, transactionDetails)) { - return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + + ioService.post( + std::bind( + &InProcessNode::getPoolTransactionsAsync, + this, + timestampBegin, + timestampEnd, + transactionsNumberLimit, + std::ref(transactions), + std::ref(transactionsNumberWithinTimestamps), + callback + ) + ); +} + +void InProcessNode::getPoolTransactionsAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + &InProcessNode::doGetPoolTransactions, + this, + timestampBegin, + timestampEnd, + transactionsNumberLimit, + std::ref(transactions), + std::ref(transactionsNumberWithinTimestamps) + ) + ); + + callback(ec); +} + +std::error_code InProcessNode::doGetPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps) { + try { + std::vector rawTransactions; + if (!core.getPoolTransactionsByTimestamp(timestampBegin, timestampEnd, transactionsNumberLimit, rawTransactions, transactionsNumberWithinTimestamps)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Transaction& rawTransaction : rawTransactions) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(rawTransaction, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); } - transactions.push_back(std::move(transactionDetails)); + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +void InProcessNode::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { + std::unique_lock lock(mutex); + if (state != INITIALIZED) { + lock.unlock(); + callback(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + return; + } + + ioService.post( + std::bind( + &InProcessNode::getTransactionsByPaymentIdAsync, + this, + std::cref(paymentId), + std::ref(transactions), + callback + ) + ); +} + +void InProcessNode::getTransactionsByPaymentIdAsync(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { + std::error_code ec = core.executeLocked( + std::bind( + &InProcessNode::doGetTransactionsByPaymentId, + this, + paymentId, + std::ref(transactions) + ) + ); + + callback(ec); +} + +std::error_code InProcessNode::doGetTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions) { + try { + std::vector rawTransactions; + if (!core.getTransactionsByPaymentId(paymentId, rawTransactions)) { + return make_error_code(CryptoNote::error::REQUEST_ERROR); + } + for (const Transaction& rawTransaction : rawTransactions) { + TransactionDetails transactionDetails; + if (!blockchainExplorerDataBuilder.fillTransactionDetails(rawTransaction, transactionDetails)) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); + } + transactions.push_back(std::move(transactionDetails)); + } + } catch (std::system_error& e) { + return e.code(); + } catch (std::exception&) { + return make_error_code(CryptoNote::error::INTERNAL_NODE_ERROR); } return std::error_code(); } diff --git a/src/InProcessNode/InProcessNode.h b/src/InProcessNode/InProcessNode.h index 9baed79da9..6f805d6029 100644 --- a/src/InProcessNode/InProcessNode.h +++ b/src/InProcessNode/InProcessNode.h @@ -18,10 +18,11 @@ #pragma once #include "INode.h" -#include "cryptonote_protocol/ICryptonoteProtocolQuery.h" -#include "cryptonote_protocol/ICryptonoteProtocolObserver.h" -#include "cryptonote_core/ICore.h" -#include "cryptonote_core/ICoreObserver.h" +#include "ITransaction.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h" +#include "CryptoNoteProtocol/ICryptoNoteProtocolObserver.h" +#include "CryptoNoteCore/ICore.h" +#include "CryptoNoteCore/ICoreObserver.h" #include "Common/ObserverManager.h" #include "BlockchainExplorer/BlockchainExplorerDataBuilder.h" @@ -32,9 +33,9 @@ namespace CryptoNote { class core; -class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserver, public CryptoNote::ICoreObserver { +class InProcessNode : public INode, public CryptoNote::ICryptoNoteProtocolObserver, public CryptoNote::ICoreObserver { public: - InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptonoteProtocolQuery& protocol); + InProcessNode(CryptoNote::ICore& core, CryptoNote::ICryptoNoteProtocolQuery& protocol); InProcessNode(const InProcessNode&) = delete; InProcessNode(InProcessNode&&) = delete; @@ -51,39 +52,44 @@ class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserv virtual bool removeObserver(INodeObserver* observer) override; virtual size_t getPeerCount() const; - virtual uint64_t getLastLocalBlockHeight() const; - virtual uint64_t getLastKnownBlockHeight() const; - virtual uint64_t getLocalBlockCount() const override; - virtual uint64_t getKnownBlockCount() const override; + virtual uint32_t getLastLocalBlockHeight() const; + virtual uint32_t getLastKnownBlockHeight() const; + virtual uint32_t getLocalBlockCount() const override; + virtual uint32_t getKnownBlockCount() const override; virtual uint64_t getLastLocalBlockTimestamp() const override; - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) override; - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback) override; virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override; - virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, - const Callback& callback) override; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, - std::vector& deleted_tx_ids, const Callback& callback) override; - - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) override; + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) override; + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) override; + + + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) override; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) override; virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; private: virtual void peerCountUpdated(size_t count) override; - virtual void lastKnownBlockHeightUpdated(uint64_t height) override; - virtual void blockchainSynchronized(uint64_t topHeight) override; + virtual void lastKnownBlockHeightUpdated(uint32_t height) override; + virtual void blockchainSynchronized(uint32_t topHeight) override; virtual void blockchainUpdated() override; virtual void poolUpdated() override; - void getNewBlocksAsync(std::list& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback); - std::error_code doGetNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight); + void getNewBlocksAsync(std::vector& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback); + std::error_code doGetNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight); - void getTransactionOutsGlobalIndicesAsync(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); - std::error_code doGetTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); + void getTransactionOutsGlobalIndicesAsync(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback); + std::error_code doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices); void getRandomOutsByAmountsAsync(std::vector& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); @@ -93,21 +99,32 @@ class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserv void relayTransactionAsync(const CryptoNote::Transaction& transaction, const Callback& callback); std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction); - void queryBlocksAsync(std::list& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight, - const Callback& callback); - std::error_code doQueryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, uint64_t& startHeight); + void queryBlocksLiteAsync(std::vector& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, + const Callback& callback); + std::error_code doQueryBlocksLite(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight); - void getPoolSymmetricDifferenceAsync(std::vector& known_pool_tx_ids, crypto::hash known_block_id, bool& is_bc_actual, std::vector& new_txs, - std::vector& deleted_tx_ids, const Callback& callback); + void getPoolSymmetricDifferenceAsync(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback); - void getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback); - std::error_code doGetBlocks(const std::vector& blockHeights, std::vector>& blocks); + void getOutByMSigGIndexAsync(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback); - void getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback); - std::error_code doGetBlocks(const std::vector& blockHashes, std::vector& blocks); + void getBlocksAsync(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback); + std::error_code doGetBlocks(const std::vector& blockHeights, std::vector>& blocks); - void getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback); - std::error_code doGetTransactions(const std::vector& transactionHashes, std::vector& transactions); + void getBlocksAsync(const std::vector& blockHashes, std::vector& blocks, const Callback& callback); + std::error_code doGetBlocks(const std::vector& blockHashes, std::vector& blocks); + + void getBlocksAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback); + std::error_code doGetBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps); + + void getTransactionsAsync(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback); + std::error_code doGetTransactions(const std::vector& transactionHashes, std::vector& transactions); + + void getPoolTransactionsAsync(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback); + std::error_code doGetPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps); + + void getTransactionsByPaymentIdAsync(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback); + std::error_code doGetTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions); void isSynchronizedAsync(bool& syncStatus, const Callback& callback); std::error_code doIsSynchronized(bool& syncStatus); @@ -122,8 +139,8 @@ class InProcessNode : public INode, public CryptoNote::ICryptonoteProtocolObserv State state; CryptoNote::ICore& core; - CryptoNote::ICryptonoteProtocolQuery& protocol; - tools::ObserverManager observerManager; + CryptoNote::ICryptoNoteProtocolQuery& protocol; + Tools::ObserverManager observerManager; boost::asio::io_service ioService; std::unique_ptr workerThread; diff --git a/src/JsonRpcServer/JsonRpcServer.cpp b/src/JsonRpcServer/JsonRpcServer.cpp new file mode 100755 index 0000000000..6939c80bf4 --- /dev/null +++ b/src/JsonRpcServer/JsonRpcServer.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "JsonRpcServer.h" + +#include +#include +#include +#include +#include +#include "HTTP/HttpParserErrorCodes.h" + +#include +#include +#include +#include +#include "HTTP/HttpParser.h" +#include "HTTP/HttpResponse.h" + +#include "Common/JsonValue.h" +#include "Serialization/JsonInputValueSerializer.h" +#include "Serialization/JsonOutputStreamSerializer.h" + +namespace CryptoNote { + +JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup) : + HttpServer(sys, loggerGroup), + system(sys), + stopEvent(stopEvent), + logger(loggerGroup, "JsonRpcServer") +{ +} + +void JsonRpcServer::start(const std::string& bindAddress, uint16_t bindPort) { + HttpServer::start(bindAddress, bindPort); + stopEvent.wait(); + HttpServer::stop(); +} + +void JsonRpcServer::processRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { + try { + logger(Logging::TRACE) << "HTTP request came: \n" << req; + + if (req.getUrl() == "/json_rpc") { + std::istringstream jsonInputStream(req.getBody()); + Common::JsonValue jsonRpcRequest; + Common::JsonValue jsonRpcResponse(Common::JsonValue::OBJECT); + + try { + jsonInputStream >> jsonRpcRequest; + } catch (std::runtime_error&) { + logger(Logging::DEBUGGING) << "Couldn't parse request: \"" << req.getBody() << "\""; + makeJsonParsingErrorResponse(jsonRpcResponse); + resp.setStatus(CryptoNote::HttpResponse::STATUS_200); + resp.setBody(jsonRpcResponse.toString()); + return; + } + + processJsonRpcRequest(jsonRpcRequest, jsonRpcResponse); + + std::ostringstream jsonOutputStream; + jsonOutputStream << jsonRpcResponse; + + resp.setStatus(CryptoNote::HttpResponse::STATUS_200); + resp.setBody(jsonOutputStream.str()); + + } else { + logger(Logging::WARNING) << "Requested url \"" << req.getUrl() << "\" is not found"; + resp.setStatus(CryptoNote::HttpResponse::STATUS_404); + return; + } + } catch (std::exception& e) { + logger(Logging::WARNING) << "Error while processing http request: " << e.what(); + resp.setStatus(CryptoNote::HttpResponse::STATUS_500); + } +} + +void JsonRpcServer::prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp) { + using Common::JsonValue; + + if (req.contains("id")) { + resp.insert("id", req("id")); + } + + resp.insert("jsonrpc", "2.0"); +} + +void JsonRpcServer::makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32000); //Application specific error code + + JsonValue message; + message = ec.message(); + + JsonValue data(JsonValue::OBJECT); + JsonValue appCode; + appCode = static_cast(ec.value()); + data.insert("application_code", appCode); + + error.insert("code", code); + error.insert("message", message); + error.insert("data", data); + + resp.insert("error", error); +} + +void JsonRpcServer::makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(errorCode); + + std::string msg; + if (what) { + msg = what; + } else { + msg = "Unknown application error"; + } + + JsonValue message; + message = msg; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); + +} + +void JsonRpcServer::makeMethodNotFoundResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32601); //ambigous declaration of JsonValue::operator= (between int and JsonValue) + + JsonValue message; + message = "Method not found"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + +void JsonRpcServer::fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp) { + resp.insert("result", v); +} + +void JsonRpcServer::makeJsonParsingErrorResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + resp = JsonValue(JsonValue::OBJECT); + resp.insert("jsonrpc", "2.0"); + resp.insert("id", nullptr); + + JsonValue error(JsonValue::OBJECT); + JsonValue code; + code = static_cast(-32700); //ambigous declaration of JsonValue::operator= (between int and JsonValue) + + JsonValue message = "Parse error"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + +} diff --git a/src/payment_service/JsonRpcServer.h b/src/JsonRpcServer/JsonRpcServer.h old mode 100644 new mode 100755 similarity index 61% rename from src/payment_service/JsonRpcServer.h rename to src/JsonRpcServer/JsonRpcServer.h index feb133e9c3..8dacc6df44 --- a/src/payment_service/JsonRpcServer.h +++ b/src/JsonRpcServer/JsonRpcServer.h @@ -23,9 +23,7 @@ #include #include "Logging/ILogger.h" #include "Logging/LoggerRef.h" -#include "rpc/HttpServer.h" - -#include "PaymentServiceConfiguration.h" +#include "Rpc/HttpServer.h" namespace CryptoNote { @@ -41,37 +39,32 @@ namespace System { class TcpConnection; } -namespace PaymentService { - -class WalletService; +namespace CryptoNote { -class JsonRpcServer : CryptoNote::HttpServer { +class JsonRpcServer : HttpServer { public: - JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); + JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup); JsonRpcServer(const JsonRpcServer&) = delete; - void start(const Configuration& config); + void start(const std::string& bindAddress, uint16_t bindPort); -private: - void sessionProcedure(System::TcpConnection* tcpConnection); +protected: + static void makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp); + static void makeMethodNotFoundResponse(Common::JsonValue& resp); + static void makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode = -32001); + static void fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp); + static void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); + static void makeJsonParsingErrorResponse(Common::JsonValue& resp); + + virtual void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) = 0; +private: // HttpServer virtual void processRequest(const CryptoNote::HttpRequest& request, CryptoNote::HttpResponse& response) override; - void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp); - void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); - - void makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp); - void makeMethodNotFoundResponse(Common::JsonValue& resp); - void makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode = -32001); - void makeJsonParsingErrorResponse(Common::JsonValue& resp); - - void fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp); - System::Dispatcher& system; System::Event& stopEvent; - WalletService& service; Logging::LoggerRef logger; }; -} //namespace PaymentService +} //namespace CryptoNote diff --git a/src/node_rpc_proxy/NodeErrors.cpp b/src/NodeRpcProxy/NodeErrors.cpp similarity index 100% rename from src/node_rpc_proxy/NodeErrors.cpp rename to src/NodeRpcProxy/NodeErrors.cpp diff --git a/src/node_rpc_proxy/NodeErrors.h b/src/NodeRpcProxy/NodeErrors.h similarity index 100% rename from src/node_rpc_proxy/NodeErrors.h rename to src/NodeRpcProxy/NodeErrors.h diff --git a/src/NodeRpcProxy/NodeRpcProxy.cpp b/src/NodeRpcProxy/NodeRpcProxy.cpp new file mode 100644 index 0000000000..bc94b35b5b --- /dev/null +++ b/src/NodeRpcProxy/NodeRpcProxy.cpp @@ -0,0 +1,637 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "NodeRpcProxy.h" +#include "NodeErrors.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Rpc/HttpClient.h" +#include "Rpc/JsonRpc.h" + +#ifndef AUTO_VAL_INIT +#define AUTO_VAL_INIT(n) boost::value_initialized() +#endif + +using namespace Crypto; +using namespace Common; +using namespace System; + +namespace CryptoNote { + +namespace { + +std::error_code interpretResponseStatus(const std::string& status) { + if (CORE_RPC_STATUS_BUSY == status) { + return make_error_code(error::NODE_BUSY); + } else if (CORE_RPC_STATUS_OK != status) { + return make_error_code(error::INTERNAL_NODE_ERROR); + } + return std::error_code(); +} + +} + +NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort) : + m_rpcTimeout(10000), + m_pullInterval(5000), + m_nodeHost(nodeHost), + m_nodePort(nodePort), + m_lastLocalBlockTimestamp(0) { + resetInternalState(); +} + +NodeRpcProxy::~NodeRpcProxy() { + try { + shutdown(); + } catch (std::exception&) { + } +} + +void NodeRpcProxy::resetInternalState() { + m_stop = false; + m_peerCount.store(0, std::memory_order_relaxed); + m_nodeHeight.store(0, std::memory_order_relaxed); + m_networkHeight.store(0, std::memory_order_relaxed); + m_lastKnowHash = CryptoNote::NULL_HASH; +} + +void NodeRpcProxy::init(const INode::Callback& callback) { + std::lock_guard lock(m_mutex); + + if (m_state != STATE_NOT_INITIALIZED) { + callback(make_error_code(error::ALREADY_INITIALIZED)); + return; + } + + m_state = STATE_INITIALIZING; + resetInternalState(); + m_workerThread = std::thread([this, callback] { + workerThread(callback); + }); +} + +bool NodeRpcProxy::shutdown() { + std::unique_lock lock(m_mutex); + + if (m_state == STATE_NOT_INITIALIZED) { + return true; + } else if (m_state == STATE_INITIALIZING) { + m_cv_initialized.wait(lock, [this] { return m_state != STATE_INITIALIZING; }); + if (m_state == STATE_NOT_INITIALIZED) { + return true; + } + } + + assert(m_state == STATE_INITIALIZED); + assert(m_dispatcher != nullptr); + + m_dispatcher->remoteSpawn([this]() { + m_stop = true; + // Run all spawned contexts + m_dispatcher->yield(); + }); + + if (m_workerThread.joinable()) { + m_workerThread.join(); + } + m_state = STATE_NOT_INITIALIZED; + + return true; +} + +void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) { + try { + Dispatcher dispatcher; + m_dispatcher = &dispatcher; + ContextGroup contextGroup(dispatcher); + m_context_group = &contextGroup; + HttpClient httpClient(dispatcher, m_nodeHost, m_nodePort); + m_httpClient = &httpClient; + Event httpEvent(dispatcher); + m_httpEvent = &httpEvent; + m_httpEvent->set(); + + { + std::lock_guard lock(m_mutex); + assert(m_state == STATE_INITIALIZING); + m_state = STATE_INITIALIZED; + m_cv_initialized.notify_all(); + } + + initialized_callback(std::error_code()); + + contextGroup.spawn([this]() { + Timer pullTimer(*m_dispatcher); + while (!m_stop) { + updateNodeStatus(); + if (!m_stop) { + pullTimer.sleep(std::chrono::milliseconds(m_pullInterval)); + } + } + }); + + contextGroup.wait(); + // Make sure all remote spawns are executed + m_dispatcher->yield(); + } catch (std::exception&) { + } + + m_dispatcher = nullptr; + m_context_group = nullptr; + m_httpClient = nullptr; + m_httpEvent = nullptr; +} + +void NodeRpcProxy::updateNodeStatus() { + CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response rsp = AUTO_VAL_INIT(rsp); + + std::error_code ec = jsonRpcCommand("getlastblockheader", req, rsp); + + if (!ec) { + Crypto::Hash blockHash; + if (!parse_hash256(rsp.block_header.hash, blockHash)) { + return; + } + + if (blockHash != m_lastKnowHash) { + m_lastKnowHash = blockHash; + m_nodeHeight.store(static_cast(rsp.block_header.height), std::memory_order_relaxed); + m_lastLocalBlockTimestamp.store(rsp.block_header.timestamp, std::memory_order_relaxed); + // TODO request and update network height + m_networkHeight.store(static_cast(rsp.block_header.height), std::memory_order_relaxed); + m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight.load(std::memory_order_relaxed)); + //if (m_networkHeight.load(std::memory_order_relaxed) != rsp.block_header.network_height) { + // m_networkHeight.store(rsp.block_header.height, std::memory_order_relaxed); + // m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight); + //} + m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight.load(std::memory_order_relaxed)); + } + } + + updatePeerCount(); +} + +void NodeRpcProxy::updatePeerCount() { + CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp); + + std::error_code ec = jsonCommand("/getinfo", req, rsp); + + if (!ec) { + size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count; + if (peerCount != m_peerCount) { + m_peerCount = peerCount; + m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount.load(std::memory_order_relaxed)); + } + } +} + +bool NodeRpcProxy::addObserver(INodeObserver* observer) { + return m_observerManager.add(observer); +} + +bool NodeRpcProxy::removeObserver(INodeObserver* observer) { + return m_observerManager.remove(observer); +} + +size_t NodeRpcProxy::getPeerCount() const { + return m_peerCount.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getLastLocalBlockHeight() const { + return m_nodeHeight.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getLastKnownBlockHeight() const { + return m_networkHeight.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getLocalBlockCount() const { + return m_nodeHeight.load(std::memory_order_relaxed); +} + +uint32_t NodeRpcProxy::getKnownBlockCount() const { + return m_networkHeight.load(std::memory_order_relaxed); +} + +uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const { + return m_lastLocalBlockTimestamp; +} + +void NodeRpcProxy::relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doRelayTransaction, this, transaction), callback); +} + +void NodeRpcProxy::getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, + std::vector& outs, + const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doGetRandomOutsByAmounts, this, std::move(amounts), outsCount, std::ref(outs)), + callback); +} + +void NodeRpcProxy::getNewBlocks(std::vector&& knownBlockIds, + std::vector& newBlocks, + uint32_t& startHeight, + const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doGetNewBlocks, this, std::move(knownBlockIds), std::ref(newBlocks), + std::ref(startHeight)), callback); +} + +void NodeRpcProxy::getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, + std::vector& outsGlobalIndices, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doGetTransactionOutsGlobalIndices, this, transactionHash, + std::ref(outsGlobalIndices)), callback); +} + +void NodeRpcProxy::queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest(std::bind(&NodeRpcProxy::doQueryBlocksLite, this, std::move(knownBlockIds), timestamp, + std::ref(newBlocks), std::ref(startHeight)), callback); +} + +void NodeRpcProxy::getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + scheduleRequest([this, knownPoolTxIds, knownBlockId, &isBcActual, &newTxs, &deletedTxIds] () mutable -> std::error_code { + return this->doGetPoolSymmetricDifference(std::move(knownPoolTxIds), knownBlockId, isBcActual, newTxs, deletedTxIds); } , callback); +} + +void NodeRpcProxy::getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +void NodeRpcProxy::isSynchronized(bool& syncStatus, const Callback& callback) { + std::lock_guard lock(m_mutex); + if (m_state != STATE_INITIALIZED) { + callback(make_error_code(error::NOT_INITIALIZED)); + return; + } + + // TODO NOT IMPLEMENTED + callback(std::error_code()); +} + +std::error_code NodeRpcProxy::doRelayTransaction(const CryptoNote::Transaction& transaction) { + COMMAND_RPC_SEND_RAW_TX::request req; + COMMAND_RPC_SEND_RAW_TX::response rsp; + req.tx_as_hex = toHex(toBinaryArray(transaction)); + return jsonCommand("/sendrawtransaction", req, rsp); +} + +std::error_code NodeRpcProxy::doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, + std::vector& outs) { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response rsp = AUTO_VAL_INIT(rsp); + req.amounts = std::move(amounts); + req.outs_count = outsCount; + + std::error_code ec = binaryCommand("/getrandom_outs.bin", req, rsp); + if (!ec) { + outs = std::move(rsp.outs); + } + + return ec; +} + +std::error_code NodeRpcProxy::doGetNewBlocks(std::vector& knownBlockIds, + std::vector& newBlocks, + uint32_t& startHeight) { + CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_BLOCKS_FAST::response rsp = AUTO_VAL_INIT(rsp); + req.block_ids = std::move(knownBlockIds); + + std::error_code ec = binaryCommand("/getblocks.bin", req, rsp); + if (!ec) { + newBlocks = std::move(rsp.blocks); + startHeight = static_cast(rsp.start_height); + } + + return ec; +} + +std::error_code NodeRpcProxy::doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, + std::vector& outsGlobalIndices) { + CryptoNote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response rsp = AUTO_VAL_INIT(rsp); + req.txid = transactionHash; + + std::error_code ec = binaryCommand("/get_o_indexes.bin", req, rsp); + if (!ec) { + outsGlobalIndices.clear(); + for (auto idx : rsp.o_indexes) { + outsGlobalIndices.push_back(static_cast(idx)); + } + } + + return ec; +} + +std::error_code NodeRpcProxy::doQueryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, + std::vector& newBlocks, uint32_t& startHeight) { + CryptoNote::COMMAND_RPC_QUERY_BLOCKS_LITE::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_QUERY_BLOCKS_LITE::response rsp = AUTO_VAL_INIT(rsp); + + req.blockIds = knownBlockIds; + req.timestamp = timestamp; + + std::error_code ec = binaryCommand("/queryblockslite.bin", req, rsp); + if (ec) { + return ec; + } + + startHeight = static_cast(rsp.startHeight); + + for (auto& item: rsp.items) { + BlockShortEntry bse; + bse.hasBlock = false; + + bse.blockHash = std::move(item.blockId); + if (!item.block.empty()) { + if (!fromBinaryArray(bse.block, asBinaryArray(item.block))) { + return std::make_error_code(std::errc::invalid_argument); + } + + bse.hasBlock = true; + } + + for (const auto& txp: item.txPrefixes) { + TransactionShortInfo tsi; + tsi.txId = txp.txHash; + tsi.txPrefix = txp.txPrefix; + bse.txsShortInfo.push_back(std::move(tsi)); + } + + newBlocks.push_back(std::move(bse)); + } + + return std::error_code(); +} + +std::error_code NodeRpcProxy::doGetPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds) { + CryptoNote::COMMAND_RPC_GET_POOL_CHANGES_LITE::request req = AUTO_VAL_INIT(req); + CryptoNote::COMMAND_RPC_GET_POOL_CHANGES_LITE::response rsp = AUTO_VAL_INIT(rsp); + + req.tailBlockId = knownBlockId; + req.knownTxsIds = knownPoolTxIds; + + std::error_code ec = binaryCommand("/get_pool_changes_lite.bin", req, rsp); + + if (ec) { + return ec; + } + + isBcActual = rsp.isTailBlockActual; + if (!isBcActual) { + return ec; + } + + deletedTxIds = std::move(rsp.deletedTxsIds); + + for (const auto& tpi : rsp.addedTxs) { + newTxs.push_back(createTransactionPrefix(tpi.txPrefix, tpi.txHash)); + } + + return ec; +} + +void NodeRpcProxy::scheduleRequest(std::function&& procedure, const Callback& callback) { + // callback is located on stack, so copy it inside binder + class Wrapper { + public: + Wrapper(std::function&, Callback&)>&& _func, + std::function&& _procedure, const Callback& _callback) + : func(std::move(_func)), procedure(std::move(_procedure)), callback(std::move(_callback)) { + } + Wrapper(const Wrapper& other) + : func(other.func), procedure(other.procedure), callback(other.callback) { + } + Wrapper(Wrapper&& other) // must be noexcept + : func(std::move(other.func)), procedure(std::move(other.procedure)), callback(std::move(other.callback)) { + } + void operator()() { + func(procedure, callback); + } + private: + std::function&, Callback&)> func; + std::function procedure; + Callback callback; + }; + assert(m_dispatcher != nullptr && m_context_group != nullptr); + m_dispatcher->remoteSpawn(Wrapper([this](std::function& procedure, Callback& callback) { + m_context_group->spawn(Wrapper([this](std::function& procedure, const Callback& callback) { + if (m_stop) { + callback(std::make_error_code(std::errc::operation_canceled)); + } else { + std::error_code ec = procedure(); + callback(m_stop ? std::make_error_code(std::errc::operation_canceled) : ec); + } + }, std::move(procedure), std::move(callback))); + }, std::move(procedure), callback)); +} + +template +std::error_code NodeRpcProxy::binaryCommand(const std::string& url, const Request& req, Response& res) { + std::error_code ec; + + try { + EventLock eventLock(*m_httpEvent); + invokeBinaryCommand(*m_httpClient, url, req, res); + ec = interpretResponseStatus(res.status); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +template +std::error_code NodeRpcProxy::jsonCommand(const std::string& url, const Request& req, Response& res) { + std::error_code ec; + + try { + EventLock eventLock(*m_httpEvent); + invokeJsonCommand(*m_httpClient, url, req, res); + ec = interpretResponseStatus(res.status); + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +template +std::error_code NodeRpcProxy::jsonRpcCommand(const std::string& method, const Request& req, Response& res) { + std::error_code ec = make_error_code(error::INTERNAL_NODE_ERROR); + + try { + EventLock eventLock(*m_httpEvent); + + JsonRpc::JsonRpcRequest jsReq; + + jsReq.setMethod(method); + jsReq.setParams(req); + + HttpRequest httpReq; + HttpResponse httpRes; + + httpReq.setUrl("/json_rpc"); + httpReq.setBody(jsReq.getBody()); + + m_httpClient->request(httpReq, httpRes); + + JsonRpc::JsonRpcResponse jsRes; + + if (httpRes.getStatus() == HttpResponse::STATUS_200) { + jsRes.parse(httpRes.getBody()); + if (jsRes.getResult(res)) { + ec = interpretResponseStatus(res.status); + } + } + } catch (const std::exception&) { + ec = make_error_code(error::NETWORK_ERROR); + } + + return ec; +} + +} diff --git a/src/NodeRpcProxy/NodeRpcProxy.h b/src/NodeRpcProxy/NodeRpcProxy.h new file mode 100644 index 0000000000..8b5674c0a7 --- /dev/null +++ b/src/NodeRpcProxy/NodeRpcProxy.h @@ -0,0 +1,139 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/ObserverManager.h" +#include "INode.h" + +namespace System { + class ContextGroup; + class Dispatcher; + class Event; +} + +namespace CryptoNote { + +class HttpClient; + +class NodeRpcProxy : public CryptoNote::INode { +public: + NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort); + virtual ~NodeRpcProxy(); + + virtual bool addObserver(CryptoNote::INodeObserver* observer); + virtual bool removeObserver(CryptoNote::INodeObserver* observer); + + virtual void init(const Callback& callback); + virtual bool shutdown(); + + virtual size_t getPeerCount() const; + virtual uint32_t getLastLocalBlockHeight() const; + virtual uint32_t getLastKnownBlockHeight() const; + virtual uint32_t getLocalBlockCount() const override; + virtual uint32_t getKnownBlockCount() const override; + virtual uint64_t getLastLocalBlockTimestamp() const override; + + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback); + virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, std::vector& result, const Callback& callback); + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback); + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override; + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override; + // TODO INodeObserver::poolChanged() notification NOT implemented!!! + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) override; + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, MultisignatureOutput& out, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, const Callback& callback) override; + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, const Callback& callback) override; + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override; + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override; + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, const Callback& callback) override; + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, const Callback& callback) override; + virtual void isSynchronized(bool& syncStatus, const Callback& callback) override; + + unsigned int rpcTimeout() const { return m_rpcTimeout; } + void rpcTimeout(unsigned int val) { m_rpcTimeout = val; } + +private: + void resetInternalState(); + void workerThread(const Callback& initialized_callback); + + void pullNodeStatusAndScheduleTheNext(); + void updateNodeStatus(); + void updatePeerCount(); + + std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction); + std::error_code doGetRandomOutsByAmounts(std::vector& amounts, uint64_t outsCount, + std::vector& result); + std::error_code doGetNewBlocks(std::vector& knownBlockIds, + std::vector& newBlocks, uint32_t& startHeight); + std::error_code doGetTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, + std::vector& outsGlobalIndices); + std::error_code doQueryBlocksLite(const std::vector& knownBlockIds, uint64_t timestamp, + std::vector& newBlocks, uint32_t& startHeight); + std::error_code doGetPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds); + + void scheduleRequest(std::function&& procedure, const Callback& callback); + template + std::error_code binaryCommand(const std::string& url, const Request& req, Response& res); + template + std::error_code jsonCommand(const std::string& url, const Request& req, Response& res); + template + std::error_code jsonRpcCommand(const std::string& method, const Request& req, Response& res); + + enum State { + STATE_NOT_INITIALIZED, + STATE_INITIALIZING, + STATE_INITIALIZED + }; + +private: + State m_state = STATE_NOT_INITIALIZED; + std::mutex m_mutex; + std::condition_variable m_cv_initialized; + std::thread m_workerThread; + System::Dispatcher* m_dispatcher = nullptr; + System::ContextGroup* m_context_group = nullptr; + Tools::ObserverManager m_observerManager; + + const std::string m_nodeHost; + const unsigned short m_nodePort; + unsigned int m_rpcTimeout; + HttpClient* m_httpClient = nullptr; + System::Event* m_httpEvent = nullptr; + + uint64_t m_pullInterval; + + // Internal state + bool m_stop = false; + std::atomic m_peerCount; + std::atomic m_nodeHeight; + std::atomic m_networkHeight; + + //protect it with mutex if decided to add worker threads + Crypto::Hash m_lastKnowHash; + std::atomic m_lastLocalBlockTimestamp; +}; + +} diff --git a/src/p2p/connection_context.h b/src/P2p/ConnectionContext.h old mode 100644 new mode 100755 similarity index 72% rename from src/p2p/connection_context.h rename to src/P2p/ConnectionContext.h index 97cf40218c..1550e969c0 --- a/src/p2p/connection_context.h +++ b/src/P2p/ConnectionContext.h @@ -27,7 +27,7 @@ namespace CryptoNote { -struct cryptonote_connection_context { +struct CryptoNoteConnectionContext { uint8_t version; boost::uuids::uuid m_connection_id; uint32_t m_remote_ip = 0; @@ -46,25 +46,25 @@ struct cryptonote_connection_context { }; state m_state = state_befor_handshake; - std::list m_needed_objects; - std::unordered_set m_requested_objects; - uint64_t m_remote_blockchain_height = 0; - uint64_t m_last_response_height = 0; + std::list m_needed_objects; + std::unordered_set m_requested_objects; + uint32_t m_remote_blockchain_height = 0; + uint32_t m_last_response_height = 0; }; -inline std::string get_protocol_state_string(cryptonote_connection_context::state s) { +inline std::string get_protocol_state_string(CryptoNoteConnectionContext::state s) { switch (s) { - case cryptonote_connection_context::state_befor_handshake: + case CryptoNoteConnectionContext::state_befor_handshake: return "state_befor_handshake"; - case cryptonote_connection_context::state_synchronizing: + case CryptoNoteConnectionContext::state_synchronizing: return "state_synchronizing"; - case cryptonote_connection_context::state_idle: + case CryptoNoteConnectionContext::state_idle: return "state_idle"; - case cryptonote_connection_context::state_normal: + case CryptoNoteConnectionContext::state_normal: return "state_normal"; - case cryptonote_connection_context::state_sync_required: + case CryptoNoteConnectionContext::state_sync_required: return "state_sync_required"; - case cryptonote_connection_context::state_shutdown: + case CryptoNoteConnectionContext::state_shutdown: return "state_shutdown"; default: return "unknown"; @@ -74,7 +74,7 @@ inline std::string get_protocol_state_string(cryptonote_connection_context::stat } namespace std { -inline std::ostream& operator << (std::ostream& s, const CryptoNote::cryptonote_connection_context& context) { +inline std::ostream& operator << (std::ostream& s, const CryptoNote::CryptoNoteConnectionContext& context) { return s << "[" << Common::ipAddressToString(context.m_remote_ip) << ":" << context.m_remote_port << (context.m_is_income ? " INC" : " OUT") << "] "; } diff --git a/src/P2p/IP2pNodeInternal.cpp b/src/P2p/IP2pNodeInternal.cpp new file mode 100644 index 0000000000..3a46427a7e --- /dev/null +++ b/src/P2p/IP2pNodeInternal.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "IP2pNodeInternal.h" diff --git a/src/CryptoNote/UnsignedKeyInput.h b/src/P2p/IP2pNodeInternal.h old mode 100755 new mode 100644 similarity index 60% rename from src/CryptoNote/UnsignedKeyInput.h rename to src/P2p/IP2pNodeInternal.h index 223c7c9388..b822a8d180 --- a/src/CryptoNote/UnsignedKeyInput.h +++ b/src/P2p/IP2pNodeInternal.h @@ -17,24 +17,23 @@ #pragma once -#include "../crypto/crypto.h" +#include +#include "P2pProtocolDefinitions.h" namespace CryptoNote { -class UnsignedKeyInput { +class P2pContext; + +class IP2pNodeInternal { public: - UnsignedKeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage); - UnsignedKeyInput(const UnsignedKeyInput& other) = delete; - UnsignedKeyInput& operator=(const UnsignedKeyInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputCount() const; - uint32_t getOutputIndex(uint32_t index) const; - const crypto::key_image& getKeyImage() const; + virtual const CORE_SYNC_DATA& getGenesisPayload() const = 0; + virtual std::list getLocalPeerList() const = 0; + virtual basic_node_data getNodeData() const = 0; + virtual PeerIdType getPeerId() const = 0; -private: - uint64_t amount; - std::vector outputs; - crypto::key_image keyImage; + virtual void handleNodeData(const basic_node_data& node, P2pContext& ctx) = 0; + virtual bool handleRemotePeerList(const std::list& peerlist, time_t local_time) = 0; + virtual void tryPing(P2pContext& ctx) = 0; }; } diff --git a/src/p2p/LevinProtocol.cpp b/src/P2p/LevinProtocol.cpp similarity index 73% rename from src/p2p/LevinProtocol.cpp rename to src/P2p/LevinProtocol.cpp index 29baaaa117..ccecdd5f4f 100644 --- a/src/p2p/LevinProtocol.cpp +++ b/src/P2p/LevinProtocol.cpp @@ -44,10 +44,14 @@ struct bucket_head2 } +bool LevinProtocol::Command::needReply() const { + return !(isNotify || isResponse); +} + LevinProtocol::LevinProtocol(System::TcpConnection& connection) : m_conn(connection) {} -std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, bool needResponse, bool readResponse) { +void LevinProtocol::sendMessage(uint32_t command, const BinaryArray& out, bool needResponse) { bucket_head2 head = { 0 }; head.m_signature = LEVIN_SIGNATURE; head.m_cb = out.size(); @@ -57,37 +61,14 @@ std::string LevinProtocol::sendBuf(uint32_t command, const std::string& out, boo head.m_flags = LEVIN_PACKET_REQUEST; // write header and body in one operation - std::string writeBuffer; + BinaryArray writeBuffer; writeBuffer.reserve(sizeof(head) + out.size()); - writeBuffer.append(reinterpret_cast(&head), sizeof(head)); - writeBuffer.append(out); - m_conn.write(reinterpret_cast(writeBuffer.data()), writeBuffer.size()); - std::string response; - - if (readResponse) { - if (!readStrict(reinterpret_cast(&head), sizeof(head))) { - throw std::runtime_error("Levin::sendBuf, failed to read header, peer closed connection"); - } + Common::VectorOutputStream stream(writeBuffer); + stream.writeSome(&head, sizeof(head)); + stream.writeSome(out.data(), out.size()); - if (head.m_signature != LEVIN_SIGNATURE) { - throw std::runtime_error("Levin signature mismatch"); - } - - if (head.m_cb > LEVIN_DEFAULT_MAX_PACKET_SIZE) { - throw std::runtime_error("Levin packet size is too big"); - } - - response.resize(head.m_cb); - - if (response.size()) { - if (!readStrict(&response[0], head.m_cb)) { - throw std::runtime_error("Levin::sendBuf, failed to read body, peer closed connection"); - } - } - } - - return response; + m_conn.write(writeBuffer.data(), writeBuffer.size()); } bool LevinProtocol::readCommand(Command& cmd) { @@ -105,10 +86,10 @@ bool LevinProtocol::readCommand(Command& cmd) { throw std::runtime_error("Levin packet size is too big"); } - std::string buf; - buf.resize(head.m_cb); + BinaryArray buf; - if (!buf.empty()) { + if (head.m_cb != 0) { + buf.resize(head.m_cb); if (!readStrict(&buf[0], head.m_cb)) { return false; } @@ -122,7 +103,7 @@ bool LevinProtocol::readCommand(Command& cmd) { return true; } -void LevinProtocol::sendReply(uint32_t command, const std::string& out, int32_t returnCode) { +void LevinProtocol::sendReply(uint32_t command, const BinaryArray& out, int32_t returnCode) { bucket_head2 head = { 0 }; head.m_signature = LEVIN_SIGNATURE; head.m_cb = out.size(); @@ -147,6 +128,7 @@ bool LevinProtocol::readStrict(void* ptr, size_t size) { if (read == 0) { return false; } + offset += read; } diff --git a/src/p2p/LevinProtocol.h b/src/P2p/LevinProtocol.h old mode 100644 new mode 100755 similarity index 60% rename from src/p2p/LevinProtocol.h rename to src/P2p/LevinProtocol.h index 5d335c9e08..37f394da13 --- a/src/p2p/LevinProtocol.h +++ b/src/P2p/LevinProtocol.h @@ -17,8 +17,11 @@ #pragma once -#include "serialization/KVBinaryInputStreamSerializer.h" -#include "serialization/KVBinaryOutputStreamSerializer.h" +#include "CryptoNote.h" +#include +#include +#include "Serialization/KVBinaryInputStreamSerializer.h" +#include "Serialization/KVBinaryOutputStreamSerializer.h" namespace System { class TcpConnection; @@ -37,41 +40,50 @@ enum class LevinError: int32_t { ERROR_FORMAT = -7, }; +const int32_t LEVIN_PROTOCOL_RETCODE_SUCCESS = 1; + class LevinProtocol { public: LevinProtocol(System::TcpConnection& connection); - template - void invoke(uint32_t command, const Req& req, Resp& resp, bool readResponse = true) { - decode(sendBuf(command, encode(req), true, readResponse), resp); + template + bool invoke(uint32_t command, const Request& request, Response& response) { + sendMessage(command, encode(request), true); + + Command cmd; + readCommand(cmd); + + if (!cmd.isResponse) { + return false; + } + + return decode(cmd.buf, response); } - template - void notify(uint32_t command, const Req& req, int) { - sendBuf(command, encode(req), false, false); + template + void notify(uint32_t command, const Request& request, int) { + sendMessage(command, encode(request), false); } struct Command { uint32_t command; bool isNotify; bool isResponse; - std::string buf; + BinaryArray buf; - bool needReply() const { - return !(isNotify || isResponse); - } + bool needReply() const; }; bool readCommand(Command& cmd); - std::string sendBuf(uint32_t command, const std::string& out, bool needResponse, bool readResponse = false); - void sendReply(uint32_t command, const std::string& out, int32_t returnCode); + void sendMessage(uint32_t command, const BinaryArray& out, bool needResponse); + void sendReply(uint32_t command, const BinaryArray& out, int32_t returnCode); template - static bool decode(const std::string& buf, T& value) { + static bool decode(const BinaryArray& buf, T& value) { try { - std::stringstream stream(buf); + Common::MemoryInputStream stream(buf.data(), buf.size()); KVBinaryInputStreamSerializer serializer(stream); serialize(value, serializer); } catch (std::exception&) { @@ -82,12 +94,13 @@ class LevinProtocol { } template - static std::string encode(const T& value) { + static BinaryArray encode(const T& value) { + BinaryArray result; KVBinaryOutputStreamSerializer serializer; serialize(const_cast(value), serializer); - std::stringstream stream; - serializer.write(stream); - return stream.str(); + Common::VectorOutputStream stream(result); + serializer.dump(stream); + return result; } private: diff --git a/src/p2p/net_node.cpp b/src/P2p/NetNode.cpp similarity index 64% rename from src/p2p/net_node.cpp rename to src/P2p/NetNode.cpp index c77ad260a0..106f0b4187 100644 --- a/src/p2p/net_node.cpp +++ b/src/P2p/NetNode.cpp @@ -15,38 +15,43 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "net_node.h" +#include "NetNode.h" #include -#include #include -#include -#include -#include +#include #include #include +#include #include #include +#include +#include #include #include #include #include -#include #include #include #include "version.h" -#include "Common/util.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Common/Util.h" #include "crypto/crypto.h" -#include "p2p_protocol_defs.h" -#include "net_peerlist_boost_serialization.h" -#include "connection_context.h" +#include "ConnectionContext.h" #include "LevinProtocol.h" +#include "P2pProtocolDefinitions.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/SerializationOverloads.h" + +using namespace Common; using namespace Logging; using namespace CryptoNote; @@ -56,7 +61,7 @@ size_t get_random_index_with_fixed_probability(size_t max_index) { //divide by zero workaround if (!max_index) return 0; - size_t x = crypto::rand() % (max_index + 1); + size_t x = Crypto::rand() % (max_index + 1); return (x*x*x) / (max_index*max_index); //parabola \/ } @@ -95,7 +100,7 @@ void addPortMapping(Logging::LoggerRef& logger, uint32_t port) { } } -bool parse_peer_from_string(net_address& pe, const std::string& node_addr) { +bool parse_peer_from_string(NetworkAddress& pe, const std::string& node_addr) { return Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); } @@ -107,7 +112,7 @@ namespace CryptoNote namespace { const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; - const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(CryptoNote::P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(CryptoNote::P2P_DEFAULT_PORT)}; const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; @@ -116,22 +121,68 @@ namespace CryptoNote " If this option is given the options add-priority-node and seed-node are ignored"}; const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + + std::string print_peerlist_to_string(const std::list& pl) { + time_t now_time = 0; + time(&now_time); + std::stringstream ss; + ss << std::setfill('0') << std::setw(8) << std::hex << std::noshowbase; + for (const auto& pe : pl) { + ss << pe.id << "\t" << pe.adr << " \tlast_seen: " << Common::timeIntervalToString(now_time - pe.last_seen) << std::endl; + } + return ss.str(); + } } - std::string print_peerlist_to_string(const std::list& pl) { - time_t now_time = 0; - time(&now_time); - std::stringstream ss; - ss << std::setfill('0') << std::setw(8) << std::hex << std::noshowbase; - for (const auto& pe : pl) { - ss << pe.id << "\t" << pe.adr << " \tlast_seen: " << Common::timeIntervalToString(now_time - pe.last_seen) << std::endl; + + //----------------------------------------------------------------------------------- + // P2pConnectionContext implementation + //----------------------------------------------------------------------------------- + + bool P2pConnectionContext::pushMessage(P2pMessage&& msg) { + writeQueueSize += msg.size(); + + if (writeQueueSize > P2P_CONNECTION_MAX_WRITE_BUFFER_SIZE) { + logger(DEBUGGING) << *this << "Write queue overflows. Interrupt connection"; + interrupt(); + return false; } - return ss.str(); + + writeQueue.push_back(std::move(msg)); + queueEvent.set(); + return true; + } + + std::vector P2pConnectionContext::popBuffer() { + writeOperationStartTime = TimePoint(); + + while (writeQueue.empty() && !stopped) { + queueEvent.wait(); + } + + std::vector msgs(std::move(writeQueue)); + writeQueue.clear(); + writeQueueSize = 0; + writeOperationStartTime = Clock::now(); + queueEvent.clear(); + return msgs; + } + + uint64_t P2pConnectionContext::writeDuration(TimePoint now) const { // in milliseconds + return writeOperationStartTime == TimePoint() ? 0 : std::chrono::duration_cast(now - writeOperationStartTime).count(); + } + + void P2pConnectionContext::interrupt() { + logger(DEBUGGING) << *this << "Interrupt connection"; + assert(context != nullptr); + stopped = true; + queueEvent.set(); + context->interrupt(); } template - int invokeAdaptor(const std::string& reqBuf, std::string& resBuf, p2p_connection_context& ctx, Handler handler) { + int invokeAdaptor(const BinaryArray& reqBuf, BinaryArray& resBuf, P2pConnectionContext& ctx, Handler handler) { typedef typename Command::request Request; typedef typename Command::response Response; int command = Command::ID; @@ -148,18 +199,18 @@ namespace CryptoNote return ret; } - node_server::node_server(System::Dispatcher& dispatcher, CryptoNote::cryptonote_protocol_handler& payload_handler, Logging::ILogger& log) : + NodeServer::NodeServer(System::Dispatcher& dispatcher, CryptoNote::CryptoNoteProtocolHandler& payload_handler, Logging::ILogger& log) : m_dispatcher(dispatcher), + m_workingContextGroup(dispatcher), m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false), m_network_id(BYTECOIN_NETWORK), logger(log, "node_server"), m_stopEvent(m_dispatcher), - m_shutdownCompleteEvent(m_dispatcher), m_idleTimer(m_dispatcher), m_timedSyncTimer(m_dispatcher), - m_spawnCount(0), + m_timeoutTimer(m_dispatcher), m_stop(false), // intervals // m_peer_handshake_idle_maker_interval(CryptoNote::P2P_DEFAULT_HANDSHAKE_INTERVAL), @@ -167,29 +218,40 @@ namespace CryptoNote m_peerlist_store_interval(60*30, false) { } + void NodeServer::serialize(ISerializer& s) { + uint8_t version = 1; + s(version, "version"); + + if (version != 1) { + return; + } + + s(m_peerlist, "peerlist"); + s(m_config.m_peer_id, "peer_id"); + } #define INVOKE_HANDLER(CMD, Handler) case CMD::ID: { ret = invokeAdaptor(cmd.buf, out, ctx, boost::bind(Handler, this, _1, _2, _3, _4)); break; } - int node_server::handleCommand(const LevinProtocol::Command& cmd, std::string& out, p2p_connection_context& ctx, bool& handled) { + int NodeServer::handleCommand(const LevinProtocol::Command& cmd, BinaryArray& out, P2pConnectionContext& ctx, bool& handled) { int ret = 0; handled = true; if (cmd.isResponse && cmd.command == COMMAND_TIMED_SYNC::ID) { if (!handleTimedSyncResponse(cmd.buf, ctx)) { // invalid response, close connection - ctx.m_state = cryptonote_connection_context::state_shutdown; + ctx.m_state = CryptoNoteConnectionContext::state_shutdown; } return 0; } switch (cmd.command) { - INVOKE_HANDLER(COMMAND_HANDSHAKE, &node_server::handle_handshake) - INVOKE_HANDLER(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) - INVOKE_HANDLER(COMMAND_PING, &node_server::handle_ping) + INVOKE_HANDLER(COMMAND_HANDSHAKE, &NodeServer::handle_handshake) + INVOKE_HANDLER(COMMAND_TIMED_SYNC, &NodeServer::handle_timed_sync) + INVOKE_HANDLER(COMMAND_PING, &NodeServer::handle_ping) #ifdef ALLOW_DEBUG_COMMANDS - INVOKE_HANDLER(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) - INVOKE_HANDLER(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) - INVOKE_HANDLER(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) + INVOKE_HANDLER(COMMAND_REQUEST_STAT_INFO, &NodeServer::handle_get_stat_info) + INVOKE_HANDLER(COMMAND_REQUEST_NETWORK_STATE, &NodeServer::handle_get_network_state) + INVOKE_HANDLER(COMMAND_REQUEST_PEER_ID, &NodeServer::handle_get_peer_id) #endif default: { handled = false; @@ -204,7 +266,7 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - void node_server::init_options(boost::program_options::options_description& desc) + void NodeServer::init_options(boost::program_options::options_description& desc) { command_line::add_arg(desc, arg_p2p_bind_ip); command_line::add_arg(desc, arg_p2p_bind_port); @@ -218,16 +280,25 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::init_config() { + bool NodeServer::init_config() { try { - std::string state_file_path = m_config_folder + "/" + CryptoNote::parameters::P2P_NET_DATA_FILENAME; - std::ifstream p2p_data; - p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::in); - - if (!p2p_data.fail()) { - boost::archive::binary_iarchive a(p2p_data); - a >> *this; - } else { + std::string state_file_path = m_config_folder + "/" + m_p2p_state_filename; + bool loaded = false; + + try { + std::ifstream p2p_data; + p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::in); + + if (!p2p_data.fail()) { + StdInputStream inputStream(p2p_data); + BinaryInputStreamSerializer a(inputStream); + CryptoNote::serialize(*this, a); + loaded = true; + } + } catch (std::exception&) { + } + + if (!loaded) { make_default_config(); } @@ -249,30 +320,30 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - void node_server::for_each_connection(std::function f) + void NodeServer::for_each_connection(std::function f) { for (auto& ctx : m_connections) { - f(ctx.second, ctx.second.peer_id); + f(ctx.second, ctx.second.peerId); } } //----------------------------------------------------------------------------------- - void node_server::externalRelayNotifyToAll(int command, const std::string& data_buff) { + void NodeServer::externalRelayNotifyToAll(int command, const BinaryArray& data_buff) { m_dispatcher.remoteSpawn([this, command, data_buff] { relay_notify_to_all(command, data_buff, nullptr); }); } //----------------------------------------------------------------------------------- - bool node_server::make_default_config() + bool NodeServer::make_default_config() { - m_config.m_peer_id = crypto::rand(); + m_config.m_peer_id = Crypto::rand(); return true; } //----------------------------------------------------------------------------------- - bool node_server::handle_command_line(const boost::program_options::variables_map& vm) + bool NodeServer::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); m_port = command_line::get_arg(vm, arg_p2p_bind_port); @@ -284,8 +355,8 @@ namespace CryptoNote std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); for(const std::string& pr_str: perrs) { - peerlist_entry pe = boost::value_initialized(); - pe.id = crypto::rand(); + PeerlistEntry pe = boost::value_initialized(); + pe.id = Crypto::rand(); bool r = parse_peer_from_string(pe.adr, pr_str); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; } m_command_line_peers.push_back(pe); @@ -312,22 +383,29 @@ namespace CryptoNote return true; } - bool node_server::handleConfig(const NetNodeConfig& config) { - m_bind_ip = config.bindIp; - m_port = config.bindPort; - m_external_port = config.externalPort; - m_allow_local_ip = config.allowLocalIp; + bool NodeServer::handleConfig(const NetNodeConfig& config) { + m_bind_ip = config.getBindIp(); + m_port = std::to_string(config.getBindPort()); + m_external_port = config.getExternalPort(); + m_allow_local_ip = config.getAllowLocalIp(); + + auto peers = config.getPeers(); + std::copy(peers.begin(), peers.end(), std::back_inserter(m_command_line_peers)); + + auto exclusiveNodes = config.getExclusiveNodes(); + std::copy(exclusiveNodes.begin(), exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); - std::copy(config.peers.begin(), config.peers.end(), std::back_inserter(m_command_line_peers)); - std::copy(config.exclusiveNodes.begin(), config.exclusiveNodes.end(), std::back_inserter(m_exclusive_peers)); - std::copy(config.priorityNodes.begin(), config.priorityNodes.end(), std::back_inserter(m_priority_peers)); - std::copy(config.seedNodes.begin(), config.seedNodes.end(), std::back_inserter(m_seed_nodes)); + auto priorityNodes = config.getPriorityNodes(); + std::copy(priorityNodes.begin(), priorityNodes.end(), std::back_inserter(m_priority_peers)); - m_hide_my_port = config.hideMyPort; + auto seedNodes = config.getSeedNodes(); + std::copy(seedNodes.begin(), seedNodes.end(), std::back_inserter(m_seed_nodes)); + + m_hide_my_port = config.getHideMyPort(); return true; } - bool node_server::append_net_address(std::vector& nodes, const std::string& addr) { + bool NodeServer::append_net_address(std::vector& nodes, const std::string& addr) { size_t pos = addr.find_last_of(':'); if (!(std::string::npos != pos && addr.length() - 1 != pos && 0 != pos)) { logger(ERROR, BRIGHT_RED) << "Failed to parse seed address from string: '" << addr << '\''; @@ -341,7 +419,7 @@ namespace CryptoNote System::Ipv4Resolver resolver(m_dispatcher); auto addr = resolver.resolve(host); - nodes.push_back(net_address{hostToNetwork(addr.getValue()), port}); + nodes.push_back(NetworkAddress{hostToNetwork(addr.getValue()), port}); logger(TRACE) << "Added seed node: " << nodes.back() << " (" << host << ")"; @@ -356,8 +434,8 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - bool node_server::init(const NetNodeConfig& config, bool testnet) { - if (!testnet) { + bool NodeServer::init(const NetNodeConfig& config) { + if (!config.getTestnet()) { for (auto seed : CryptoNote::SEED_NODES) { append_net_address(m_seed_nodes, seed); } @@ -369,9 +447,10 @@ namespace CryptoNote logger(ERROR, BRIGHT_RED) << "Failed to handle command line"; return false; } - m_config_folder = config.configFolder; + m_config_folder = config.getConfigFolder(); + m_p2p_state_filename = config.getP2pStateFilename(); - if (!init_config()) { + if (!init_config()) { logger(ERROR, BRIGHT_RED) << "Failed to init config."; return false; } @@ -410,66 +489,52 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - CryptoNote::cryptonote_protocol_handler& node_server::get_payload_object() + CryptoNote::CryptoNoteProtocolHandler& NodeServer::get_payload_object() { return m_payload_handler; } //----------------------------------------------------------------------------------- - bool node_server::run() { + bool NodeServer::run() { logger(INFO) << "Starting node_server"; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::acceptLoop, this)); - - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::onIdle, this)); - - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::timedSyncLoop, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::acceptLoop, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::onIdle, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::timedSyncLoop, this)); + m_workingContextGroup.spawn(std::bind(&NodeServer::timeoutLoop, this)); m_stopEvent.wait(); - logger(INFO) << "Stopping node_server..."; - - m_listener.stop(); - m_idleTimer.stop(); - m_timedSyncTimer.stop(); - - logger(INFO) << "Stopping " << m_connections.size() << " connections"; - - for (auto& conn : m_connections) { - conn.second.connection.stop(); - } - - m_shutdownCompleteEvent.wait(); + logger(INFO) << "Stopping NodeServer and it's" << m_connections.size() << " connections..."; + m_workingContextGroup.interrupt(); + m_workingContextGroup.wait(); - logger(INFO) << "net_service loop stopped"; + logger(INFO) << "NodeServer loop stopped"; return true; } //----------------------------------------------------------------------------------- - uint64_t node_server::get_connections_count() { + uint64_t NodeServer::get_connections_count() { return m_connections.size(); } //----------------------------------------------------------------------------------- - bool node_server::deinit() { + bool NodeServer::deinit() { return store_config(); } //----------------------------------------------------------------------------------- - bool node_server::store_config() + bool NodeServer::store_config() { try { - if (!tools::create_directories_if_necessary(m_config_folder)) { + if (!Tools::create_directories_if_necessary(m_config_folder)) { logger(INFO) << "Failed to create data directory: " << m_config_folder; return false; } - std::string state_file_path = m_config_folder + "/" + CryptoNote::parameters::P2P_NET_DATA_FILENAME; + std::string state_file_path = m_config_folder + "/" + m_p2p_state_filename; std::ofstream p2p_data; p2p_data.open(state_file_path, std::ios_base::binary | std::ios_base::out | std::ios::trunc); if (p2p_data.fail()) { @@ -477,8 +542,9 @@ namespace CryptoNote return false; }; - boost::archive::binary_oarchive a(p2p_data); - a << *this; + StdOutputStream stream(p2p_data); + BinaryOutputStreamSerializer a(stream); + CryptoNote::serialize(*this, a); return true; } catch (const std::exception& e) { logger(WARNING) << "store_config failed: " << e.what(); @@ -488,7 +554,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::send_stop_signal() { + bool NodeServer::sendStopSignal() { m_stop = true; m_dispatcher.remoteSpawn([this] { @@ -501,7 +567,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::handshake(CryptoNote::LevinProtocol& proto, p2p_connection_context& context, bool just_take_peerlist) { + bool NodeServer::handshake(CryptoNote::LevinProtocol& proto, P2pConnectionContext& context, bool just_take_peerlist) { COMMAND_HANDSHAKE::request arg; COMMAND_HANDSHAKE::response rsp; get_local_node_data(arg.node_data); @@ -530,7 +596,7 @@ namespace CryptoNote return false; } - context.peer_id = rsp.node_data.peer_id; + context.peerId = rsp.node_data.peer_id; m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); if (rsp.node_data.peer_id == m_config.m_peer_id) { @@ -543,29 +609,23 @@ namespace CryptoNote } - bool node_server::timedSync() { + bool NodeServer::timedSync() { COMMAND_TIMED_SYNC::request arg = boost::value_initialized(); m_payload_handler.get_payload_sync_data(arg.payload_data); auto cmdBuf = LevinProtocol::encode(arg); - forEachConnection([&](p2p_connection_context& conn) { - if (conn.peer_id && - (conn.m_state == cryptonote_connection_context::state_normal || - conn.m_state == cryptonote_connection_context::state_idle)) { - try { - System::LatchGuard latch(conn.writeLatch); - System::EventLock lock(conn.connectionEvent); - LevinProtocol(conn.connection).sendBuf(COMMAND_TIMED_SYNC::ID, cmdBuf, true, false); - } catch (std::exception&) { - logger(DEBUGGING) << conn << "Failed to send COMMAND_TIMED_SYNC"; - } + forEachConnection([&](P2pConnectionContext& conn) { + if (conn.peerId && + (conn.m_state == CryptoNoteConnectionContext::state_normal || + conn.m_state == CryptoNoteConnectionContext::state_idle)) { + conn.pushMessage(P2pMessage(P2pMessage::COMMAND, COMMAND_TIMED_SYNC::ID, cmdBuf)); } }); return true; } - bool node_server::handleTimedSyncResponse(const std::string& in, p2p_connection_context& context) { + bool NodeServer::handleTimedSyncResponse(const BinaryArray& in, P2pConnectionContext& context) { COMMAND_TIMED_SYNC::response rsp; if (!LevinProtocol::decode(in, rsp)) { return false; @@ -577,7 +637,7 @@ namespace CryptoNote } if (!context.m_is_income) { - m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); + m_peerlist.set_peer_just_seen(context.peerId, context.m_remote_ip, context.m_remote_port); } if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false)) { @@ -587,7 +647,7 @@ namespace CryptoNote return true; } - void node_server::forEachConnection(std::function action) { + void NodeServer::forEachConnection(std::function action) { // create copy of connection ids because the list can be changed during action std::vector connectionIds; @@ -605,13 +665,13 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::is_peer_used(const peerlist_entry& peer) { + bool NodeServer::is_peer_used(const PeerlistEntry& peer) { if(m_config.m_peer_id == peer.id) return true; //dont make connections to ourself for (const auto& kv : m_connections) { const auto& cntxt = kv.second; - if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) { + if(cntxt.peerId == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) { return true; } } @@ -619,7 +679,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::is_addr_connected(const net_address& peer) { + bool NodeServer::is_addr_connected(const NetworkAddress& peer) { for (const auto& conn : m_connections) { if (!conn.second.m_is_income && peer.ip == conn.second.m_remote_ip && peer.port == conn.second.m_remote_port) { return true; @@ -629,47 +689,33 @@ namespace CryptoNote } - bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) { + bool NodeServer::try_to_connect_and_handshake_with_new_peer(const NetworkAddress& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) { logger(DEBUGGING) << "Connecting to " << na << " (white=" << white << ", last_seen: " << (last_seen_stamp ? Common::timeIntervalToString(time(NULL) - last_seen_stamp) : "never") << ")..."; try { - System::TcpConnector connector(m_dispatcher); - - System::Event connectorTimeoutEvent(m_dispatcher); - System::Timer connectorTimeoutTimer(m_dispatcher); - - m_dispatcher.spawn([&]() { - try { - connectorTimeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); - connector.stop(); - } catch (std::exception&) { - } - - connectorTimeoutEvent.set(); - }); - System::TcpConnection connection; try { - connection = - connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); + System::Context connectionContext(m_dispatcher, [&] { + System::TcpConnector connector(m_dispatcher); + return connector.connect(System::Ipv4Address(Common::ipAddressToString(na.ip)), static_cast(na.port)); + }); + + System::Context<> timeoutContext(m_dispatcher, [&] { + System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + connectionContext.interrupt(); + logger(DEBUGGING) << "Connection to " << na <<" timed out, interrupt it"; + }); + + connection = std::move(connectionContext.get()); } catch (System::InterruptedException&) { - connectorTimeoutEvent.wait(); + logger(DEBUGGING) << "Connection timed out"; return false; - } catch (std::exception& e) { - connectorTimeoutTimer.stop(); - connectorTimeoutEvent.wait(); - throw; } - p2p_connection_context ctx(m_dispatcher, std::move(connection)); - - connectorTimeoutTimer.stop(); - connectorTimeoutEvent.wait(); - - // p2p_connection_context ctx(m_dispatcher, std::move(connector.connect())); + P2pConnectionContext ctx(m_dispatcher, logger.getLogger(), std::move(connection)); ctx.m_connection_id = boost::uuids::random_generator()(); ctx.m_remote_ip = na.ip; @@ -677,73 +723,48 @@ namespace CryptoNote ctx.m_is_income = false; ctx.m_started = time(nullptr); - System::Event handshakeTimeoutFinishedEvent(m_dispatcher); - System::Event handshakeFinishedEvent(m_dispatcher); - System::Timer handshakeTimeoutTimer(m_dispatcher); - m_dispatcher.spawn([&] { - try { - handshakeTimeoutTimer.sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); - ctx.connection.stop(); - } catch (std::exception& e) { - } - - handshakeTimeoutFinishedEvent.set(); - }); - - CryptoNote::LevinProtocol proto(ctx.connection); - bool error = false; - std::exception_ptr exceptionHolder; - m_dispatcher.spawn([&] { - try { - if (!handshake(proto, ctx, just_take_peerlist)) { - logger(WARNING) << "Failed to HANDSHAKE with peer " << na; - error = true; - } - } catch (System::InterruptedException&) { - error = true; - handshakeFinishedEvent.set(); - return; - } catch (...) { - exceptionHolder = std::current_exception(); + try { + System::Context handshakeContext(m_dispatcher, [&] { + CryptoNote::LevinProtocol proto(ctx.connection); + return handshake(proto, ctx, just_take_peerlist); + }); + + System::Context<> timeoutContext(m_dispatcher, [&] { + System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); + handshakeContext.interrupt(); + logger(DEBUGGING) << "Handshake with " << na << " timed out, interrupt it"; + }); + + if (!handshakeContext.get()) { + logger(WARNING) << "Failed to HANDSHAKE with peer " << na; + return false; } - - handshakeTimeoutTimer.stop(); - handshakeFinishedEvent.set(); - }); - - handshakeFinishedEvent.wait(); - handshakeTimeoutFinishedEvent.wait(); - if (error) { + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "Handshake timed out"; return false; } - if (exceptionHolder != nullptr) { - std::rethrow_exception(exceptionHolder); - } - if (just_take_peerlist) { logger(Logging::DEBUGGING, Logging::BRIGHT_GREEN) << ctx << "CONNECTION HANDSHAKED OK AND CLOSED."; return true; } - peerlist_entry pe_local = boost::value_initialized(); + PeerlistEntry pe_local = boost::value_initialized(); pe_local.adr = na; - pe_local.id = ctx.peer_id; - time(&pe_local.last_seen); + pe_local.id = ctx.peerId; + pe_local.last_seen = time(nullptr); m_peerlist.append_with_peer_white(pe_local); if (m_stop) { throw System::InterruptedException(); } - auto key = ctx.m_connection_id; - auto iter = m_connections.emplace(key, std::move(ctx)).first; + auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; const boost::uuids::uuid& connectionId = iter->first; - p2p_connection_context& connectionContext = iter->second; + P2pConnectionContext& connectionContext = iter->second; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connectionContext))); + m_workingContextGroup.spawn(std::bind(&NodeServer::connectionHandler, this, std::cref(connectionId), std::ref(connectionContext))); return true; } catch (System::InterruptedException&) { @@ -757,7 +778,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::make_new_connection_from_peerlist(bool use_white_list) + bool NodeServer::make_new_connection_from_peerlist(bool use_white_list) { size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); if(!local_peers_count) @@ -778,7 +799,7 @@ namespace CryptoNote continue; tried_peers.insert(random_index); - peerlist_entry pe = boost::value_initialized(); + PeerlistEntry pe = boost::value_initialized(); bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to get random peer from peerlist(white:" << use_white_list << ")"; return false; } @@ -799,7 +820,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::connections_maker() + bool NodeServer::connections_maker() { if (!connect_to_peerlist(m_exclusive_peers)) { return false; @@ -811,7 +832,7 @@ namespace CryptoNote if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) { size_t try_count = 0; - size_t current_index = crypto::rand()%m_seed_nodes.size(); + size_t current_index = Crypto::rand() % m_seed_nodes.size(); while(true) { if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) @@ -856,7 +877,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::make_expected_connections_count(bool white_list, size_t expected_connections) + bool NodeServer::make_expected_connections_count(bool white_list, size_t expected_connections) { size_t conn_count = get_outgoing_connections_count(); //add new connections from white peers @@ -873,7 +894,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - size_t node_server::get_outgoing_connections_count() { + size_t NodeServer::get_outgoing_connections_count() { size_t count = 0; for (const auto& cntxt : m_connections) { if (!cntxt.second.m_is_income) @@ -883,10 +904,10 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::idle_worker() { + bool NodeServer::idle_worker() { try { - m_connections_maker_interval.call(std::bind(&node_server::connections_maker, this)); - m_peerlist_store_interval.call(std::bind(&node_server::store_config, this)); + m_connections_maker_interval.call(std::bind(&NodeServer::connections_maker, this)); + m_peerlist_store_interval.call(std::bind(&NodeServer::store_config, this)); } catch (std::exception& e) { logger(DEBUGGING) << "exception in idle_worker: " << e.what(); } @@ -894,16 +915,16 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) + bool NodeServer::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) { //fix time delta time_t now = 0; time(&now); delta = now - local_time; - BOOST_FOREACH(peerlist_entry& be, local_peerlist) + BOOST_FOREACH(PeerlistEntry& be, local_peerlist) { - if(be.last_seen > local_time) + if(be.last_seen > uint64_t(local_time)) { logger(ERROR) << "FOUND FUTURE peerlist for entry " << be.adr << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time; return false; @@ -915,10 +936,10 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - bool node_server::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const cryptonote_connection_context& context) + bool NodeServer::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const CryptoNoteConnectionContext& context) { int64_t delta = 0; - std::list peerlist_ = peerlist; + std::list peerlist_ = peerlist; if(!fix_time_delta(peerlist_, local_time, delta)) return false; logger(Logging::TRACE) << context << "REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size(); @@ -927,7 +948,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::get_local_node_data(basic_node_data& node_data) + bool NodeServer::get_local_node_data(basic_node_data& node_data) { node_data.version = P2PProtocolVersion::CURRENT; time_t local_time; @@ -944,7 +965,7 @@ namespace CryptoNote //----------------------------------------------------------------------------------- #ifdef ALLOW_DEBUG_COMMANDS - bool node_server::check_trust(const proof_of_trust &tr) { + bool NodeServer::check_trust(const proof_of_trust &tr) { uint64_t local_time = time(NULL); uint64_t time_delata = local_time > tr.time ? local_time - tr.time : tr.time - local_time; @@ -963,10 +984,10 @@ namespace CryptoNote return false; } - crypto::public_key pk; + Crypto::PublicKey pk; Common::podFromHex(CryptoNote::P2P_STAT_TRUSTED_PUB_KEY, pk); - crypto::hash h = get_proof_of_trust_hash(tr); - if (!crypto::check_signature(h, pk, tr.sign)) { + Crypto::Hash h = get_proof_of_trust_hash(tr); + if (!Crypto::check_signature(h, pk, tr.sign)) { logger(ERROR) << "check_trust failed: sign check failed"; return false; } @@ -977,25 +998,25 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) + int NodeServer::handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, P2pConnectionContext& context) { if(!check_trust(arg.tr)) { - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } rsp.connections_count = get_connections_count(); rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); rsp.version = PROJECT_VERSION_LONG; - rsp.os_version = tools::get_os_version_string(); + rsp.os_version = Tools::get_os_version_string(); m_payload_handler.get_stat_info(rsp.payload_info); return 1; } //----------------------------------------------------------------------------------- - int node_server::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) + int NodeServer::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, P2pConnectionContext& context) { if(!check_trust(arg.tr)) { - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -1003,7 +1024,7 @@ namespace CryptoNote connection_entry ce; ce.adr.ip = cntxt.second.m_remote_ip; ce.adr.port = cntxt.second.m_remote_port; - ce.id = cntxt.second.peer_id; + ce.id = cntxt.second.peerId; ce.is_income = cntxt.second.m_is_income; rsp.connections_list.push_back(ce); } @@ -1015,7 +1036,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) + int NodeServer::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, P2pConnectionContext& context) { rsp.my_id = m_config.m_peer_id; return 1; @@ -1024,43 +1045,30 @@ namespace CryptoNote //----------------------------------------------------------------------------------- - void node_server::relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) { + void NodeServer::relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) { net_connection_id excludeId = excludeConnection ? *excludeConnection : boost::value_initialized(); - forEachConnection([&](p2p_connection_context& conn) { - if (conn.peer_id && conn.m_connection_id != excludeId) { - try { - logger(TRACE) << conn << "Relay command " << command; - System::LatchGuard latch(conn.writeLatch); - System::EventLock lock(conn.connectionEvent); - LevinProtocol(conn.connection).sendBuf(command, data_buff, false); - } catch (const std::exception& e) { - logger(DEBUGGING) << conn << "Failed to relay notification id=" << command << ": " << e.what(); - } + forEachConnection([&](P2pConnectionContext& conn) { + if (conn.peerId && conn.m_connection_id != excludeId) { + conn.pushMessage(P2pMessage(P2pMessage::NOTIFY, command, data_buff)); } }); } //----------------------------------------------------------------------------------- - bool node_server::invoke_notify_to_peer(int command, const std::string& req_buff, const cryptonote_connection_context& context) { + bool NodeServer::invoke_notify_to_peer(int command, const BinaryArray& buffer, const CryptoNoteConnectionContext& context) { auto it = m_connections.find(context.m_connection_id); if (it == m_connections.end()) { return false; } - try { - System::LatchGuard latch(it->second.writeLatch); - System::EventLock lock(it->second.connectionEvent); - LevinProtocol(it->second.connection).sendBuf(command, req_buff, false); - } catch (const std::exception& e) { - logger(DEBUGGING) << it->second << "Failed to invoke notification id=" << command << ": " << e.what(); - } + it->second.pushMessage(P2pMessage(P2pMessage::NOTIFY, command, buffer)); return true; } //----------------------------------------------------------------------------------- - bool node_server::try_ping(basic_node_data& node_data, p2p_connection_context& context) + bool NodeServer::try_ping(basic_node_data& node_data, P2pConnectionContext& context) { if(!node_data.my_port) return false; @@ -1071,7 +1079,7 @@ namespace CryptoNote std::string ip = Common::ipAddressToString(actual_ip); auto port = node_data.my_port; - peerid_type pr = node_data.peer_id; + PeerIdType pr = node_data.peer_id; try { System::TcpConnector connector(m_dispatcher); @@ -1098,11 +1106,11 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) + int NodeServer::handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, P2pConnectionContext& context) { if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) { logger(Logging::ERROR) << context << "Failed to process_payload_sync_data(), dropping connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } @@ -1115,46 +1123,46 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + int NodeServer::handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, P2pConnectionContext& context) { context.version = arg.node_data.version; if (arg.node_data.network_id != m_network_id) { logger(Logging::INFO) << context << "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } if(!context.m_is_income) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came not from incoming connection"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } - if(context.peer_id) { + if(context.peerId) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { logger(Logging::ERROR) << context << "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."; - context.m_state = cryptonote_connection_context::state_shutdown; + context.m_state = CryptoNoteConnectionContext::state_shutdown; return 1; } //associate peer_id with this connection - context.peer_id = arg.node_data.peer_id; + context.peerId = arg.node_data.peer_id; if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) { - peerid_type peer_id_l = arg.node_data.peer_id; + PeerIdType peer_id_l = arg.node_data.peer_id; uint32_t port_l = arg.node_data.my_port; if (try_ping(arg.node_data, context)) { //called only(!) if success pinged, update local peerlist - peerlist_entry pe; + PeerlistEntry pe; pe.adr.ip = context.m_remote_ip; pe.adr.port = port_l; - time(&pe.last_seen); + pe.last_seen = time(nullptr); pe.id = peer_id_l; m_peerlist.append_with_peer_white(pe); @@ -1172,7 +1180,7 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - int node_server::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) + int NodeServer::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, P2pConnectionContext& context) { logger(Logging::TRACE) << context << "COMMAND_PING"; rsp.status = PING_OK_RESPONSE_STATUS_TEXT; @@ -1181,29 +1189,29 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - bool node_server::log_peerlist() + bool NodeServer::log_peerlist() { - std::list pl_wite; - std::list pl_gray; + std::list pl_wite; + std::list pl_gray; m_peerlist.get_peerlist_full(pl_gray, pl_wite); logger(INFO) << ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_wite) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ; return true; } //----------------------------------------------------------------------------------- - bool node_server::log_connections() { + bool NodeServer::log_connections() { logger(INFO) << "Connections: \r\n" << print_connections_container() ; return true; } //----------------------------------------------------------------------------------- - std::string node_server::print_connections_container() { + std::string NodeServer::print_connections_container() { std::stringstream ss; for (const auto& cntxt : m_connections) { ss << Common::ipAddressToString(cntxt.second.m_remote_ip) << ":" << cntxt.second.m_remote_port - << " \t\tpeer_id " << cntxt.second.peer_id + << " \t\tpeer_id " << cntxt.second.peerId << " \t\tconn_id " << cntxt.second.m_connection_id << (cntxt.second.m_is_income ? " INC" : " OUT") << std::endl; } @@ -1212,27 +1220,27 @@ namespace CryptoNote } //----------------------------------------------------------------------------------- - void node_server::on_connection_new(p2p_connection_context& context) + void NodeServer::on_connection_new(P2pConnectionContext& context) { logger(TRACE) << context << "NEW CONNECTION"; m_payload_handler.onConnectionOpened(context); } //----------------------------------------------------------------------------------- - void node_server::on_connection_close(p2p_connection_context& context) + void NodeServer::on_connection_close(P2pConnectionContext& context) { logger(TRACE) << context << "CLOSE CONNECTION"; m_payload_handler.onConnectionClosed(context); } - bool node_server::is_priority_node(const net_address& na) + bool NodeServer::is_priority_node(const NetworkAddress& na) { return (std::find(m_priority_peers.begin(), m_priority_peers.end(), na) != m_priority_peers.end()) || (std::find(m_exclusive_peers.begin(), m_exclusive_peers.end(), na) != m_exclusive_peers.end()); } - bool node_server::connect_to_peerlist(const std::vector& peers) + bool NodeServer::connect_to_peerlist(const std::vector& peers) { for(const auto& na: peers) { if (!is_addr_connected(na)) { @@ -1243,13 +1251,13 @@ namespace CryptoNote return true; } - bool node_server::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, - const command_line::arg_descriptor > & arg, std::vector& container) + bool NodeServer::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor > & arg, std::vector& container) { std::vector perrs = command_line::get_arg(vm, arg); for(const std::string& pr_str: perrs) { - net_address na; + NetworkAddress na; if (!parse_peer_from_string(na, pr_str)) { logger(ERROR, BRIGHT_RED) << "Failed to parse address from string: " << pr_str; return false; @@ -1260,10 +1268,10 @@ namespace CryptoNote return true; } - void node_server::acceptLoop() { + void NodeServer::acceptLoop() { for (;;) { try { - p2p_connection_context ctx(m_dispatcher, m_listener.accept()); + P2pConnectionContext ctx(m_dispatcher, logger.getLogger(), m_listener.accept()); ctx.m_connection_id = boost::uuids::random_generator()(); ctx.m_is_income = true; ctx.m_started = time(nullptr); @@ -1274,11 +1282,11 @@ namespace CryptoNote auto iter = m_connections.emplace(ctx.m_connection_id, std::move(ctx)).first; const boost::uuids::uuid& connectionId = iter->first; - p2p_connection_context& connection = iter->second; + P2pConnectionContext& connection = iter->second; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&node_server::connectionHandler, this, std::cref(connectionId), std::ref(connection))); + m_workingContextGroup.spawn(std::bind(&NodeServer::connectionHandler, this, std::cref(connectionId), std::ref(connection))); } catch (System::InterruptedException&) { + logger(DEBUGGING) << "acceptLoop() is interrupted"; break; } catch (const std::exception& e) { logger(WARNING) << "Exception in acceptLoop: " << e.what(); @@ -1286,13 +1294,9 @@ namespace CryptoNote } logger(DEBUGGING) << "acceptLoop finished"; - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); - } } - void node_server::onIdle() { + void NodeServer::onIdle() { logger(DEBUGGING) << "onIdle started"; try { @@ -1302,92 +1306,152 @@ namespace CryptoNote m_idleTimer.sleep(std::chrono::seconds(1)); } } catch (System::InterruptedException&) { + logger(DEBUGGING) << "onIdle() is interrupted"; } catch (std::exception& e) { logger(WARNING) << "Exception in onIdle: " << e.what(); } logger(DEBUGGING) << "onIdle finished"; + } - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); + void NodeServer::timeoutLoop() { + try { + while (!m_stop) { + m_timeoutTimer.sleep(std::chrono::seconds(10)); + auto now = P2pConnectionContext::Clock::now(); + + for (auto& kv : m_connections) { + auto& ctx = kv.second; + if (ctx.writeDuration(now) > P2P_DEFAULT_INVOKE_TIMEOUT) { + logger(WARNING) << ctx << "write operation timed out, stopping connection"; + ctx.interrupt(); + } + } + } + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "timeoutLoop() is interrupted"; + } catch (std::exception& e) { + logger(WARNING) << "Exception in timeoutLoop: " << e.what(); } } - void node_server::timedSyncLoop() { + void NodeServer::timedSyncLoop() { try { for (;;) { m_timedSyncTimer.sleep(std::chrono::seconds(P2P_DEFAULT_HANDSHAKE_INTERVAL)); timedSync(); } } catch (System::InterruptedException&) { + logger(DEBUGGING) << "timedSyncLoop() is interrupted"; } catch (std::exception& e) { logger(WARNING) << "Exception in timedSyncLoop: " << e.what(); } logger(DEBUGGING) << "timedSyncLoop finished"; + } + + void NodeServer::connectionHandler(const boost::uuids::uuid& connectionId, P2pConnectionContext& ctx) { + // This inner context is necessary in order to stop connection handler at any moment + System::Context<> context(m_dispatcher, [this, &connectionId, &ctx] { + System::Context<> writeContext(m_dispatcher, std::bind(&NodeServer::writeHandler, this, std::ref(ctx))); + + try { + on_connection_new(ctx); + + LevinProtocol proto(ctx.connection); + LevinProtocol::Command cmd; + + for (;;) { + if (ctx.m_state == CryptoNoteConnectionContext::state_sync_required) { + ctx.m_state = CryptoNoteConnectionContext::state_synchronizing; + m_payload_handler.start_sync(ctx); + } else if (ctx.m_state == CryptoNoteConnectionContext::state_pool_sync_required) { + ctx.m_state = CryptoNoteConnectionContext::state_normal; + m_payload_handler.requestMissingPoolTransactions(ctx); + } + + if (!proto.readCommand(cmd)) { + break; + } - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); + BinaryArray response; + bool handled = false; + auto retcode = handleCommand(cmd, response, ctx, handled); + + // send response + if (cmd.needReply()) { + if (!handled) { + retcode = static_cast(LevinError::ERROR_CONNECTION_HANDLER_NOT_DEFINED); + response.clear(); + } + + ctx.pushMessage(P2pMessage(P2pMessage::REPLY, cmd.command, std::move(response), retcode)); + } + + if (ctx.m_state == CryptoNoteConnectionContext::state_shutdown) { + break; + } + } + } catch (System::InterruptedException&) { + logger(DEBUGGING) << ctx << "connectionHandler() inner context is interrupted"; + } catch (std::exception& e) { + logger(WARNING) << ctx << "Exception in connectionHandler: " << e.what(); + } + + ctx.interrupt(); + writeContext.interrupt(); + writeContext.get(); + + on_connection_close(ctx); + m_connections.erase(connectionId); + }); + + ctx.context = &context; + + try { + context.get(); + } catch (System::InterruptedException&) { + logger(DEBUGGING) << "connectionHandler() is interrupted"; } } - void node_server::connectionHandler(const boost::uuids::uuid& connectionId, p2p_connection_context& ctx) { + void NodeServer::writeHandler(P2pConnectionContext& ctx) { + logger(DEBUGGING) << ctx << "writeHandler started"; try { - on_connection_new(ctx); - LevinProtocol proto(ctx.connection); - LevinProtocol::Command cmd; for (;;) { - if (ctx.m_state == cryptonote_connection_context::state_sync_required) { - ctx.m_state = cryptonote_connection_context::state_synchronizing; - m_payload_handler.start_sync(ctx); - } else if (ctx.m_state == cryptonote_connection_context::state_pool_sync_required) { - ctx.m_state = cryptonote_connection_context::state_normal; - m_payload_handler.requestMissingPoolTransactions(ctx); - } - - if (!proto.readCommand(cmd)) { + auto msgs = ctx.popBuffer(); + if (msgs.empty()) { break; } - std::string response; - bool handled = false; - auto retcode = handleCommand(cmd, response, ctx, handled); - - // send response - if (cmd.needReply()) { - System::LatchGuard latch(ctx.writeLatch); - System::EventLock lock(ctx.connectionEvent); - if (handled) { - proto.sendReply(cmd.command, response, retcode); - } else { - proto.sendReply(cmd.command, std::string(), static_cast(LevinError::ERROR_CONNECTION_HANDLER_NOT_DEFINED)); + for (const auto& msg : msgs) { + logger(DEBUGGING) << ctx << "msg " << msg.type << ':' << msg.command; + switch (msg.type) { + case P2pMessage::COMMAND: + proto.sendMessage(msg.command, msg.buffer, true); + break; + case P2pMessage::NOTIFY: + proto.sendMessage(msg.command, msg.buffer, false); + break; + case P2pMessage::REPLY: + proto.sendReply(msg.command, msg.buffer, msg.returnCode); + break; + default: + assert(false); } } - - if (ctx.m_state == cryptonote_connection_context::state_shutdown) { - break; - } } - } catch (System::InterruptedException&) { - logger(TRACE) << "Closing connection..."; + // connection stopped + logger(DEBUGGING) << ctx << "writeHandler() is interrupted"; } catch (std::exception& e) { - logger(WARNING) << "Exception in connectionHandler: " << e.what(); - } - - ctx.writeLatch.wait(); - - on_connection_close(ctx); - m_connections.erase(connectionId); - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); + logger(WARNING) << ctx << "error during write: " << e.what(); + ctx.interrupt(); // stop connection on write error } + logger(DEBUGGING) << ctx << "writeHandler finished"; } - - } diff --git a/src/P2p/NetNode.h b/src/P2p/NetNode.h new file mode 100644 index 0000000000..cadfd240a5 --- /dev/null +++ b/src/P2p/NetNode.h @@ -0,0 +1,273 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "CryptoNoteCore/OnceInInterval.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "Common/CommandLine.h" +#include "Logging/LoggerRef.h" + +#include "ConnectionContext.h" +#include "LevinProtocol.h" +#include "NetNodeCommon.h" +#include "NetNodeConfig.h" +#include "P2pProtocolDefinitions.h" +#include "P2pNetworks.h" +#include "PeerListManager.h" + +namespace System { +class TcpConnection; +} + +namespace CryptoNote +{ + class LevinProtocol; + class ISerializer; + + struct P2pMessage { + enum Type { + COMMAND, + REPLY, + NOTIFY + }; + + P2pMessage(Type type, uint32_t command, const BinaryArray& buffer, int32_t returnCode = 0) : + type(type), command(command), buffer(buffer), returnCode(returnCode) { + } + + P2pMessage(P2pMessage&& msg) : + type(msg.type), command(msg.command), buffer(std::move(msg.buffer)), returnCode(msg.returnCode) { + } + + size_t size() { + return buffer.size(); + } + + Type type; + uint32_t command; + const BinaryArray buffer; + int32_t returnCode; + }; + + struct P2pConnectionContext : public CryptoNoteConnectionContext { + public: + using Clock = std::chrono::steady_clock; + using TimePoint = Clock::time_point; + + System::Context* context; + PeerIdType peerId; + System::TcpConnection connection; + + P2pConnectionContext(System::Dispatcher& dispatcher, Logging::ILogger& log, System::TcpConnection&& conn) : + context(nullptr), + peerId(0), + connection(std::move(conn)), + logger(log, "node_server"), + queueEvent(dispatcher), + stopped(false) { + } + + P2pConnectionContext(P2pConnectionContext&& ctx) : + CryptoNoteConnectionContext(std::move(ctx)), + context(ctx.context), + peerId(ctx.peerId), + connection(std::move(ctx.connection)), + logger(ctx.logger.getLogger(), "node_server"), + queueEvent(std::move(ctx.queueEvent)), + stopped(std::move(ctx.stopped)) { + } + + bool pushMessage(P2pMessage&& msg); + std::vector popBuffer(); + void interrupt(); + + uint64_t writeDuration(TimePoint now) const; + + private: + Logging::LoggerRef logger; + TimePoint writeOperationStartTime; + System::Event queueEvent; + std::vector writeQueue; + size_t writeQueueSize = 0; + bool stopped; + }; + + class NodeServer : public IP2pEndpoint + { + public: + + static void init_options(boost::program_options::options_description& desc); + + NodeServer(System::Dispatcher& dispatcher, CryptoNote::CryptoNoteProtocolHandler& payload_handler, Logging::ILogger& log); + + bool run(); + bool init(const NetNodeConfig& config); + bool deinit(); + bool sendStopSignal(); + uint32_t get_this_peer_port(){return m_listeningPort;} + CryptoNote::CryptoNoteProtocolHandler& get_payload_object(); + + void serialize(ISerializer& s); + + // debug functions + bool log_peerlist(); + bool log_connections(); + virtual uint64_t get_connections_count() override; + size_t get_outgoing_connections_count(); + + CryptoNote::PeerlistManager& getPeerlistManager() { return m_peerlist; } + + private: + + int handleCommand(const LevinProtocol::Command& cmd, BinaryArray& buff_out, P2pConnectionContext& context, bool& handled); + + //----------------- commands handlers ---------------------------------------------- + int handle_handshake(int command, COMMAND_HANDSHAKE::request& arg, COMMAND_HANDSHAKE::response& rsp, P2pConnectionContext& context); + int handle_timed_sync(int command, COMMAND_TIMED_SYNC::request& arg, COMMAND_TIMED_SYNC::response& rsp, P2pConnectionContext& context); + int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, P2pConnectionContext& context); +#ifdef ALLOW_DEBUG_COMMANDS + int handle_get_stat_info(int command, COMMAND_REQUEST_STAT_INFO::request& arg, COMMAND_REQUEST_STAT_INFO::response& rsp, P2pConnectionContext& context); + int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, P2pConnectionContext& context); + int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, P2pConnectionContext& context); +#endif + + bool init_config(); + bool make_default_config(); + bool store_config(); + bool check_trust(const proof_of_trust& tr); + void initUpnp(); + + bool handshake(CryptoNote::LevinProtocol& proto, P2pConnectionContext& context, bool just_take_peerlist = false); + bool timedSync(); + bool handleTimedSyncResponse(const BinaryArray& in, P2pConnectionContext& context); + void forEachConnection(std::function action); + + void on_connection_new(P2pConnectionContext& context); + void on_connection_close(P2pConnectionContext& context); + + //----------------- i_p2p_endpoint ------------------------------------------------------------- + virtual void relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) override; + virtual bool invoke_notify_to_peer(int command, const BinaryArray& req_buff, const CryptoNoteConnectionContext& context) override; + virtual void for_each_connection(std::function f) override; + virtual void externalRelayNotifyToAll(int command, const BinaryArray& data_buff) override; + + //----------------------------------------------------------------------------------------------- + bool handle_command_line(const boost::program_options::variables_map& vm); + bool handleConfig(const NetNodeConfig& config); + bool append_net_address(std::vector& nodes, const std::string& addr); + bool idle_worker(); + bool handle_remote_peerlist(const std::list& peerlist, time_t local_time, const CryptoNoteConnectionContext& context); + bool get_local_node_data(basic_node_data& node_data); + + 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 connections_maker(); + bool make_new_connection_from_peerlist(bool use_white_list); + bool try_to_connect_and_handshake_with_new_peer(const NetworkAddress& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); + bool is_peer_used(const PeerlistEntry& peer); + bool is_addr_connected(const NetworkAddress& peer); + bool try_ping(basic_node_data& node_data, P2pConnectionContext& context); + bool make_expected_connections_count(bool white_list, size_t expected_connections); + bool is_priority_node(const NetworkAddress& na); + + bool connect_to_peerlist(const std::vector& peers); + + bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, + const command_line::arg_descriptor > & arg, std::vector& container); + + //debug functions + std::string print_connections_container(); + + typedef std::unordered_map> ConnectionContainer; + typedef ConnectionContainer::iterator ConnectionIterator; + ConnectionContainer m_connections; + + void acceptLoop(); + void connectionHandler(const boost::uuids::uuid& connectionId, P2pConnectionContext& connection); + void writeHandler(P2pConnectionContext& ctx); + void onIdle(); + void timedSyncLoop(); + void timeoutLoop(); + + struct config + { + network_config m_net_config; + uint64_t m_peer_id; + + void serialize(ISerializer& s) { + KV_MEMBER(m_net_config) + KV_MEMBER(m_peer_id) + } + }; + + config m_config; + std::string m_config_folder; + + bool m_have_address; + bool m_first_connection_maker_call; + uint32_t m_listeningPort; + uint32_t m_external_port; + uint32_t m_ip_address; + bool m_allow_local_ip; + bool m_hide_my_port; + std::string m_p2p_state_filename; + + System::Dispatcher& m_dispatcher; + System::ContextGroup m_workingContextGroup; + System::Event m_stopEvent; + System::Timer m_idleTimer; + System::Timer m_timeoutTimer; + System::TcpListener m_listener; + Logging::LoggerRef logger; + std::atomic m_stop; + + CryptoNoteProtocolHandler& m_payload_handler; + PeerlistManager m_peerlist; + + // OnceInInterval m_peer_handshake_idle_maker_interval; + OnceInInterval m_connections_maker_interval; + OnceInInterval m_peerlist_store_interval; + System::Timer m_timedSyncTimer; + + std::string m_bind_ip; + std::string m_port; +#ifdef ALLOW_DEBUG_COMMANDS + uint64_t m_last_stat_request_time; +#endif + std::vector m_priority_peers; + std::vector m_exclusive_peers; + std::vector m_seed_nodes; + std::list m_command_line_peers; + uint64_t m_peer_livetime; + boost::uuids::uuid m_network_id; + }; +} diff --git a/src/p2p/net_node_common.h b/src/P2p/NetNodeCommon.h old mode 100644 new mode 100755 similarity index 52% rename from src/p2p/net_node_common.h rename to src/P2p/NetNodeCommon.h index 054caa034c..1b36b198ca --- a/src/p2p/net_node_common.h +++ b/src/P2p/NetNodeCommon.h @@ -17,26 +17,27 @@ #pragma once -#include "p2p_protocol_types.h" +#include "CryptoNote.h" +#include "P2pProtocolTypes.h" namespace CryptoNote { - struct cryptonote_connection_context; + struct CryptoNoteConnectionContext; - struct i_p2p_endpoint { - virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) = 0; - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const CryptoNote::cryptonote_connection_context& context) = 0; + struct IP2pEndpoint { + virtual void relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) = 0; + virtual bool invoke_notify_to_peer(int command, const BinaryArray& req_buff, const CryptoNote::CryptoNoteConnectionContext& context) = 0; virtual uint64_t get_connections_count()=0; - virtual void for_each_connection(std::function f) = 0; + virtual void for_each_connection(std::function f) = 0; // can be called from external threads - virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) = 0; + virtual void externalRelayNotifyToAll(int command, const BinaryArray& data_buff) = 0; }; - struct p2p_endpoint_stub: public i_p2p_endpoint { - virtual void relay_notify_to_all(int command, const std::string& data_buff, const net_connection_id* excludeConnection) {} - virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const CryptoNote::cryptonote_connection_context& context) { return true; } - virtual void for_each_connection(std::function f) {} + struct p2p_endpoint_stub: public IP2pEndpoint { + virtual void relay_notify_to_all(int command, const BinaryArray& data_buff, const net_connection_id* excludeConnection) {} + virtual bool invoke_notify_to_peer(int command, const BinaryArray& req_buff, const CryptoNote::CryptoNoteConnectionContext& context) { return true; } + virtual void for_each_connection(std::function f) {} virtual uint64_t get_connections_count() { return 0; } - virtual void externalRelayNotifyToAll(int command, const std::string& data_buff) {} + virtual void externalRelayNotifyToAll(int command, const BinaryArray& data_buff) {} }; } diff --git a/src/p2p/NetNodeConfig.cpp b/src/P2p/NetNodeConfig.cpp old mode 100644 new mode 100755 similarity index 60% rename from src/p2p/NetNodeConfig.cpp rename to src/P2p/NetNodeConfig.cpp index 2ab6d8d389..643b39a9ef --- a/src/p2p/NetNodeConfig.cpp +++ b/src/P2p/NetNodeConfig.cpp @@ -19,18 +19,18 @@ #include -#include -#include "Common/command_line.h" +#include +#include "Common/CommandLine.h" #include "Common/StringTools.h" #include "crypto/crypto.h" -#include "cryptonote_config.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { namespace { const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; -const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", std::to_string(P2P_DEFAULT_PORT)}; -const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; +const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", P2P_DEFAULT_PORT}; +const command_line::arg_descriptor arg_p2p_external_port = { "p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0 }; const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; @@ -39,17 +39,17 @@ const command_line::arg_descriptor > arg_p2p_add_exclus const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; -bool parsePeerFromString(net_address& pe, const std::string& node_addr) { +bool parsePeerFromString(NetworkAddress& pe, const std::string& node_addr) { return Common::parseIpAddressAndPort(pe.ip, pe.port, node_addr); } bool parsePeersAndAddToContainer(const boost::program_options::variables_map& vm, - const command_line::arg_descriptor>& arg, std::vector& container) + const command_line::arg_descriptor>& arg, std::vector& container) { std::vector peers = command_line::get_arg(vm, arg); for(const std::string& str: peers) { - net_address na = boost::value_initialized(); + NetworkAddress na = boost::value_initialized(); if (!parsePeerFromString(na, str)) { return false; } @@ -75,11 +75,11 @@ void NetNodeConfig::initOptions(boost::program_options::options_description& des NetNodeConfig::NetNodeConfig() { bindIp = "0.0.0.0"; - bindPort = std::to_string(P2P_DEFAULT_PORT); + bindPort = P2P_DEFAULT_PORT; externalPort = 0; allowLocalIp = false; hideMyPort = false; - configFolder = tools::get_default_data_dir(); + configFolder = Tools::getDefaultDataDirectory(); } bool NetNodeConfig::init(const boost::program_options::variables_map& vm) @@ -89,14 +89,15 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm) externalPort = command_line::get_arg(vm, arg_p2p_external_port); allowLocalIp = command_line::get_arg(vm, arg_p2p_allow_local_ip); configFolder = command_line::get_arg(vm, command_line::arg_data_dir); + p2pStateFilename = CryptoNote::parameters::P2P_NET_DATA_FILENAME; if (command_line::has_arg(vm, arg_p2p_add_peer)) { std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); for(const std::string& pr_str: perrs) { - peerlist_entry pe = boost::value_initialized(); - pe.id = crypto::rand(); + PeerlistEntry pe = boost::value_initialized(); + pe.id = Crypto::rand(); if (!parsePeerFromString(pe.adr, pr_str)) { return false; } @@ -125,4 +126,105 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm) return true; } +void NetNodeConfig::setTestnet(bool isTestnet) { + testnet = isTestnet; +} + +std::string NetNodeConfig::getP2pStateFilename() const { + if (testnet) { + return "testnet_" + p2pStateFilename; + } + + return p2pStateFilename; +} + +bool NetNodeConfig::getTestnet() const { + return testnet; +} + +std::string NetNodeConfig::getBindIp() const { + return bindIp; +} + +uint16_t NetNodeConfig::getBindPort() const { + return bindPort; +} + +uint16_t NetNodeConfig::getExternalPort() const { + return externalPort; +} + +bool NetNodeConfig::getAllowLocalIp() const { + return allowLocalIp; +} + +std::vector NetNodeConfig::getPeers() const { + return peers; +} + +std::vector NetNodeConfig::getPriorityNodes() const { + return priorityNodes; +} + +std::vector NetNodeConfig::getExclusiveNodes() const { + return exclusiveNodes; +} + +std::vector NetNodeConfig::getSeedNodes() const { + return seedNodes; +} + +bool NetNodeConfig::getHideMyPort() const { + return hideMyPort; +} + +std::string NetNodeConfig::getConfigFolder() const { + return configFolder; +} + +void NetNodeConfig::setP2pStateFilename(const std::string& filename) { + p2pStateFilename = filename; +} + +void NetNodeConfig::setBindIp(const std::string& ip) { + bindIp = ip; +} + +void NetNodeConfig::setBindPort(uint16_t port) { + bindPort = port; +} + +void NetNodeConfig::setExternalPort(uint16_t port) { + externalPort = port; +} + +void NetNodeConfig::setAllowLocalIp(bool allow) { + allowLocalIp = allow; +} + +void NetNodeConfig::setPeers(const std::vector& peerList) { + peers = peerList; +} + +void NetNodeConfig::setPriorityNodes(const std::vector& addresses) { + priorityNodes = addresses; +} + +void NetNodeConfig::setExclusiveNodes(const std::vector& addresses) { + exclusiveNodes = addresses; +} + +void NetNodeConfig::setSeedNodes(const std::vector& addresses) { + seedNodes = addresses; +} + +void NetNodeConfig::setHideMyPort(bool hide) { + hideMyPort = hide; +} + +void NetNodeConfig::setConfigFolder(const std::string& folder) { + configFolder = folder; +} + + } //namespace nodetool diff --git a/src/P2p/NetNodeConfig.h b/src/P2p/NetNodeConfig.h new file mode 100755 index 0000000000..4590ef8f1f --- /dev/null +++ b/src/P2p/NetNodeConfig.h @@ -0,0 +1,76 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +#include +#include "P2pProtocolTypes.h" + +namespace CryptoNote { + +class NetNodeConfig { +public: + NetNodeConfig(); + static void initOptions(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + + std::string getP2pStateFilename() const; + bool getTestnet() const; + std::string getBindIp() const; + uint16_t getBindPort() const; + uint16_t getExternalPort() const; + bool getAllowLocalIp() const; + std::vector getPeers() const; + std::vector getPriorityNodes() const; + std::vector getExclusiveNodes() const; + std::vector getSeedNodes() const; + bool getHideMyPort() const; + std::string getConfigFolder() const; + + void setP2pStateFilename(const std::string& filename); + void setTestnet(bool isTestnet); + void setBindIp(const std::string& ip); + void setBindPort(uint16_t port); + void setExternalPort(uint16_t port); + void setAllowLocalIp(bool allow); + void setPeers(const std::vector& peerList); + void setPriorityNodes(const std::vector& addresses); + void setExclusiveNodes(const std::vector& addresses); + void setSeedNodes(const std::vector& addresses); + void setHideMyPort(bool hide); + void setConfigFolder(const std::string& folder); + +private: + std::string bindIp; + uint16_t bindPort; + uint16_t externalPort; + bool allowLocalIp; + std::vector peers; + std::vector priorityNodes; + std::vector exclusiveNodes; + std::vector seedNodes; + bool hideMyPort; + std::string configFolder; + std::string p2pStateFilename; + bool testnet = false; +}; + +} //namespace nodetool diff --git a/src/P2p/P2pConnectionProxy.cpp b/src/P2p/P2pConnectionProxy.cpp new file mode 100644 index 0000000000..9484d5a3e6 --- /dev/null +++ b/src/P2p/P2pConnectionProxy.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pConnectionProxy.h" + +#include "LevinProtocol.h" +#include "P2pContext.h" +#include "P2pNode.h" + +#include + +using namespace System; + +namespace CryptoNote { + +P2pConnectionProxy::P2pConnectionProxy(P2pContextOwner&& ctx, IP2pNodeInternal& node) + : m_contextOwner(std::move(ctx)), m_context(m_contextOwner.get()), m_node(node) {} + +P2pConnectionProxy::~P2pConnectionProxy() { + m_context.stop(); +} + +bool P2pConnectionProxy::processIncomingHandshake() { + LevinProtocol::Command cmd; + if (!m_context.readCommand(cmd)) { + throw std::runtime_error("Connection unexpectedly closed"); + } + + if (cmd.command == COMMAND_HANDSHAKE::ID) { + handleHandshakeRequest(cmd); + return true; + } + + if (cmd.command == COMMAND_PING::ID) { + COMMAND_PING::response resp{ PING_OK_RESPONSE_STATUS_TEXT, m_node.getPeerId() }; + m_context.writeMessage(makeReply(COMMAND_PING::ID, LevinProtocol::encode(resp), LEVIN_PROTOCOL_RETCODE_SUCCESS)); + return false; + } + + throw std::runtime_error("Unexpected command: " + std::to_string(cmd.command)); +} + +void P2pConnectionProxy::read(P2pMessage& message) { + if (!m_readQueue.empty()) { + message = std::move(m_readQueue.front()); + m_readQueue.pop(); + return; + } + + for (;;) { + LevinProtocol::Command cmd; + if (!m_context.readCommand(cmd)) { + throw InterruptedException(); + } + + message.type = cmd.command; + + if (cmd.command == COMMAND_HANDSHAKE::ID) { + handleHandshakeResponse(cmd, message); + break; + } else if (cmd.command == COMMAND_TIMED_SYNC::ID) { + handleTimedSync(cmd); + } else { + message.data = std::move(cmd.buf); + break; + } + } +} + +void P2pConnectionProxy::write(const P2pMessage &message) { + if (message.type == COMMAND_HANDSHAKE::ID) { + writeHandshake(message); + } else { + m_context.writeMessage(P2pContext::Message(P2pMessage(message), P2pContext::Message::NOTIFY)); + } +} + +void P2pConnectionProxy::ban() { + // not implemented +} + +void P2pConnectionProxy::stop() { + m_context.stop(); +} + +void P2pConnectionProxy::writeHandshake(const P2pMessage &message) { + CORE_SYNC_DATA coreSync; + LevinProtocol::decode(message.data, coreSync); + + if (m_context.isIncoming()) { + // response + COMMAND_HANDSHAKE::response res; + res.node_data = m_node.getNodeData(); + res.payload_data = coreSync; + res.local_peerlist = m_node.getLocalPeerList(); + m_context.writeMessage(makeReply(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(res), LEVIN_PROTOCOL_RETCODE_SUCCESS)); + m_node.tryPing(m_context); + } else { + // request + COMMAND_HANDSHAKE::request req; + req.node_data = m_node.getNodeData(); + req.payload_data = coreSync; + m_context.writeMessage(makeRequest(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(req))); + } +} + +void P2pConnectionProxy::handleHandshakeRequest(const LevinProtocol::Command& cmd) { + COMMAND_HANDSHAKE::request req; + if (!LevinProtocol::decode(cmd.buf, req)) { + throw std::runtime_error("Failed to decode COMMAND_HANDSHAKE request"); + } + + m_node.handleNodeData(req.node_data, m_context); + m_readQueue.push(P2pMessage{ + cmd.command, LevinProtocol::encode(req.payload_data) }); // enqueue payload info +} + +void P2pConnectionProxy::handleHandshakeResponse(const LevinProtocol::Command& cmd, P2pMessage& message) { + if (m_context.isIncoming()) { + // handshake should be already received by P2pNode + throw std::runtime_error("Unexpected COMMAND_HANDSHAKE from incoming connection"); + } + + COMMAND_HANDSHAKE::response res; + if (!LevinProtocol::decode(cmd.buf, res)) { + throw std::runtime_error("Invalid handshake message format"); + } + + m_node.handleNodeData(res.node_data, m_context); + m_node.handleRemotePeerList(res.local_peerlist, res.node_data.local_time); + + message.data = LevinProtocol::encode(res.payload_data); +} + +void P2pConnectionProxy::handleTimedSync(const LevinProtocol::Command& cmd) { + if (cmd.isResponse) { + COMMAND_TIMED_SYNC::response res; + LevinProtocol::decode(cmd.buf, res); + m_node.handleRemotePeerList(res.local_peerlist, res.local_time); + } else { + // we ignore information from the request + // COMMAND_TIMED_SYNC::request req; + // LevinProtocol::decode(cmd.buf, req); + COMMAND_TIMED_SYNC::response res; + + res.local_time = time(nullptr); + res.local_peerlist = m_node.getLocalPeerList(); + res.payload_data = m_node.getGenesisPayload(); + + m_context.writeMessage(makeReply(COMMAND_TIMED_SYNC::ID, LevinProtocol::encode(res), LEVIN_PROTOCOL_RETCODE_SUCCESS)); + } +} + +} diff --git a/src/P2p/P2pConnectionProxy.h b/src/P2p/P2pConnectionProxy.h new file mode 100644 index 0000000000..b91f9fa4a4 --- /dev/null +++ b/src/P2p/P2pConnectionProxy.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "IP2pNodeInternal.h" +#include "LevinProtocol.h" +#include "P2pContextOwner.h" +#include "P2pInterfaces.h" + +namespace CryptoNote { + +class P2pContext; +class P2pNode; + +class P2pConnectionProxy : public IP2pConnection { +public: + + P2pConnectionProxy(P2pContextOwner&& ctx, IP2pNodeInternal& node); + ~P2pConnectionProxy(); + + bool processIncomingHandshake(); + + // IP2pConnection + virtual void read(P2pMessage& message) override; + virtual void write(const P2pMessage &message) override; + virtual void ban() override; + virtual void stop() override; + +private: + + void writeHandshake(const P2pMessage &message); + void handleHandshakeRequest(const LevinProtocol::Command& cmd); + void handleHandshakeResponse(const LevinProtocol::Command& cmd, P2pMessage& message); + void handleTimedSync(const LevinProtocol::Command& cmd); + + std::queue m_readQueue; + P2pContextOwner m_contextOwner; + P2pContext& m_context; + IP2pNodeInternal& m_node; +}; + +} diff --git a/src/P2p/P2pContext.cpp b/src/P2p/P2pContext.cpp new file mode 100755 index 0000000000..d7d36b61f1 --- /dev/null +++ b/src/P2p/P2pContext.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pContext.h" + +#include +#include +#include +#include + +#include "LevinProtocol.h" + +using namespace System; + +namespace CryptoNote { + +P2pContext::Message::Message(P2pMessage&& msg, Type messageType, uint32_t returnCode) : + messageType(messageType), returnCode(returnCode) { + type = msg.type; + data = std::move(msg.data); +} + +size_t P2pContext::Message::size() const { + return data.size(); +} + +P2pContext::P2pContext( + Dispatcher& dispatcher, + TcpConnection&& conn, + bool isIncoming, + const NetworkAddress& remoteAddress, + std::chrono::nanoseconds timedSyncInterval, + const CORE_SYNC_DATA& timedSyncData) + : + incoming(isIncoming), + remoteAddress(remoteAddress), + dispatcher(dispatcher), + contextGroup(dispatcher), + timeStarted(Clock::now()), + timedSyncInterval(timedSyncInterval), + timedSyncData(timedSyncData), + timedSyncTimer(dispatcher), + timedSyncFinished(dispatcher), + connection(std::move(conn)), + writeEvent(dispatcher), + readEvent(dispatcher) { + writeEvent.set(); + readEvent.set(); + lastReadTime = timeStarted; // use current time + contextGroup.spawn(std::bind(&P2pContext::timedSyncLoop, this)); +} + +P2pContext::~P2pContext() { + stop(); + // wait for timedSyncLoop finish + timedSyncFinished.wait(); + // ensure that all read/write operations completed + readEvent.wait(); + writeEvent.wait(); +} + +PeerIdType P2pContext::getPeerId() const { + return peerId; +} + +uint16_t P2pContext::getPeerPort() const { + return peerPort; +} + +const NetworkAddress& P2pContext::getRemoteAddress() const { + return remoteAddress; +} + +bool P2pContext::isIncoming() const { + return incoming; +} + +void P2pContext::setPeerInfo(uint8_t protocolVersion, PeerIdType id, uint16_t port) { + version = protocolVersion; + peerId = id; + if (isIncoming()) { + peerPort = port; + } +} + +bool P2pContext::readCommand(LevinProtocol::Command& cmd) { + if (stopped) { + throw InterruptedException(); + } + + EventLock lk(readEvent); + bool result = LevinProtocol(connection).readCommand(cmd); + lastReadTime = Clock::now(); + return result; +} + +void P2pContext::writeMessage(const Message& msg) { + if (stopped) { + throw InterruptedException(); + } + + EventLock lk(writeEvent); + LevinProtocol proto(connection); + + switch (msg.messageType) { + case P2pContext::Message::NOTIFY: + proto.sendMessage(msg.type, msg.data, false); + break; + case P2pContext::Message::REQUEST: + proto.sendMessage(msg.type, msg.data, true); + break; + case P2pContext::Message::REPLY: + proto.sendReply(msg.type, msg.data, msg.returnCode); + break; + } +} + +void P2pContext::start() { + // stub for OperationTimeout class +} + +void P2pContext::stop() { + if (!stopped) { + stopped = true; + contextGroup.interrupt(); + } +} + +void P2pContext::timedSyncLoop() { + // construct message + P2pContext::Message timedSyncMessage{ + P2pMessage{ + COMMAND_TIMED_SYNC::ID, + LevinProtocol::encode(COMMAND_TIMED_SYNC::request{ timedSyncData }) + }, + P2pContext::Message::REQUEST + }; + + while (!stopped) { + try { + timedSyncTimer.sleep(timedSyncInterval); + + OperationTimeout timeout(dispatcher, *this, timedSyncInterval); + writeMessage(timedSyncMessage); + + // check if we had read operation in given time interval + if ((lastReadTime + timedSyncInterval * 2) < Clock::now()) { + stop(); + break; + } + } catch (InterruptedException&) { + // someone stopped us + } catch (std::exception&) { + stop(); // stop connection on write error + break; + } + } + + timedSyncFinished.set(); +} + +P2pContext::Message makeReply(uint32_t command, const BinaryArray& data, uint32_t returnCode) { + return P2pContext::Message( + P2pMessage{ command, data }, + P2pContext::Message::REPLY, + returnCode); +} + +P2pContext::Message makeRequest(uint32_t command, const BinaryArray& data) { + return P2pContext::Message( + P2pMessage{ command, data }, + P2pContext::Message::REQUEST); +} + +std::ostream& operator <<(std::ostream& s, const P2pContext& conn) { + return s << "[" << conn.getRemoteAddress() << "]"; +} + +} diff --git a/src/P2p/P2pContext.h b/src/P2p/P2pContext.h new file mode 100755 index 0000000000..b74c1a0ac4 --- /dev/null +++ b/src/P2p/P2pContext.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#include "CryptoNoteConfig.h" +#include "LevinProtocol.h" +#include "P2pInterfaces.h" +#include "P2pProtocolDefinitions.h" +#include "P2pProtocolTypes.h" + +namespace CryptoNote { + +class P2pContext { +public: + using Clock = std::chrono::steady_clock; + using TimePoint = Clock::time_point; + + struct Message : P2pMessage { + enum Type { + NOTIFY, + REQUEST, + REPLY + }; + + Type messageType; + uint32_t returnCode; + + Message(P2pMessage&& msg, Type messageType, uint32_t returnCode = 0); + size_t size() const; + }; + + P2pContext(System::Dispatcher& dispatcher, System::TcpConnection&& conn, + bool isIncoming, const NetworkAddress& remoteAddress, std::chrono::nanoseconds timedSyncInterval, const CORE_SYNC_DATA& timedSyncData); + ~P2pContext(); + + PeerIdType getPeerId() const; + uint16_t getPeerPort() const; + const NetworkAddress& getRemoteAddress() const; + bool isIncoming() const; + + void setPeerInfo(uint8_t protocolVersion, PeerIdType id, uint16_t port); + bool readCommand(LevinProtocol::Command& cmd); + void writeMessage(const Message& msg); + + void start(); + void stop(); + +private: + + uint8_t version = 0; + const bool incoming; + const NetworkAddress remoteAddress; + PeerIdType peerId = 0; + uint16_t peerPort = 0; + + System::Dispatcher& dispatcher; + System::ContextGroup contextGroup; + const TimePoint timeStarted; + bool stopped = false; + TimePoint lastReadTime; + + // timed sync info + const std::chrono::nanoseconds timedSyncInterval; + const CORE_SYNC_DATA& timedSyncData; + System::Timer timedSyncTimer; + System::Event timedSyncFinished; + + System::TcpConnection connection; + System::Event writeEvent; + System::Event readEvent; + + void timedSyncLoop(); +}; + +P2pContext::Message makeReply(uint32_t command, const BinaryArray& data, uint32_t returnCode); +P2pContext::Message makeRequest(uint32_t command, const BinaryArray& data); + +std::ostream& operator <<(std::ostream& s, const P2pContext& conn); + +} diff --git a/src/CryptoNote/UnsignedKeyInput.cpp b/src/P2p/P2pContextOwner.cpp old mode 100755 new mode 100644 similarity index 52% rename from src/CryptoNote/UnsignedKeyInput.cpp rename to src/P2p/P2pContextOwner.cpp index 72224dec8d..dabc8322cc --- a/src/CryptoNote/UnsignedKeyInput.cpp +++ b/src/P2p/P2pContextOwner.cpp @@ -15,27 +15,33 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "UnsignedKeyInput.h" +#include "P2pContextOwner.h" +#include +#include "P2pContext.h" namespace CryptoNote { -UnsignedKeyInput::UnsignedKeyInput(uint64_t amount, std::vector&& outputs, const crypto::key_image& keyImage) : amount(amount), outputs(std::move(outputs)), keyImage(keyImage) { +P2pContextOwner::P2pContextOwner(P2pContext* ctx, ContextList& contextList) : contextList(contextList) { + contextIterator = contextList.insert(contextList.end(), ContextList::value_type(ctx)); } -uint64_t UnsignedKeyInput::getAmount() const { - return amount; +P2pContextOwner::P2pContextOwner(P2pContextOwner&& other) : contextList(other.contextList), contextIterator(other.contextIterator) { + other.contextIterator = contextList.end(); } -uint32_t UnsignedKeyInput::getOutputCount() const { - return static_cast(outputs.size()); +P2pContextOwner::~P2pContextOwner() { + if (contextIterator != contextList.end()) { + contextList.erase(contextIterator); + } } -uint32_t UnsignedKeyInput::getOutputIndex(uint32_t index) const { - return outputs[index]; +P2pContext& P2pContextOwner::get() { + assert(contextIterator != contextList.end()); + return *contextIterator->get(); } -const crypto::key_image& UnsignedKeyInput::getKeyImage() const { - return keyImage; +P2pContext* P2pContextOwner::operator -> () { + return &get(); } } diff --git a/src/wallet/MultiWallet.h b/src/P2p/P2pContextOwner.h similarity index 65% rename from src/wallet/MultiWallet.h rename to src/P2p/P2pContextOwner.h index 59840c4f3e..abb1adc007 100644 --- a/src/wallet/MultiWallet.h +++ b/src/P2p/P2pContextOwner.h @@ -17,28 +17,30 @@ #pragma once -#include "IMultiWallet.h" - -#include +#include +#include namespace CryptoNote { -class MultiWallet : public IMultiWallet { +class P2pContext; + +class P2pContextOwner { public: - virtual ~MultiWallet(); - virtual void start() override; - virtual void stop() override; - virtual void refresh() override; + typedef std::list> ContextList; + + P2pContextOwner(P2pContext* ctx, ContextList& contextList); + P2pContextOwner(P2pContextOwner&& other); + P2pContextOwner(const P2pContextOwner& other) = delete; + ~P2pContextOwner(); + + P2pContext& get(); + P2pContext* operator -> (); private: - enum MultiWalletState { - NOT_INITIALIZED = 0, - INITIALIZED - }; - MultiWalletState m_state; - std::string m_password; + ContextList& contextList; + ContextList::iterator contextIterator; }; -} //namespace CryptoNote +} diff --git a/src/System/LatchGuard.h b/src/P2p/P2pInterfaces.cpp similarity index 83% rename from src/System/LatchGuard.h rename to src/P2p/P2pInterfaces.cpp index 22ee3a26e2..c563e8518d 100644 --- a/src/System/LatchGuard.h +++ b/src/P2p/P2pInterfaces.cpp @@ -15,19 +15,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#pragma once +#include "P2pInterfaces.h" -namespace System { +namespace CryptoNote { -class Latch; - -class LatchGuard { -public: - explicit LatchGuard(Latch& latch); - ~LatchGuard(); - -private: - Latch& m_latch; -}; +IP2pConnection::~IP2pConnection() { +} } diff --git a/src/CryptoNote/UnsignedMultisignatureInput.h b/src/P2p/P2pInterfaces.h old mode 100755 new mode 100644 similarity index 66% rename from src/CryptoNote/UnsignedMultisignatureInput.h rename to src/P2p/P2pInterfaces.h index 8f7433e47d..b42152ca5b --- a/src/CryptoNote/UnsignedMultisignatureInput.h +++ b/src/P2p/P2pInterfaces.h @@ -18,20 +18,30 @@ #pragma once #include +#include + +#include namespace CryptoNote { -class UnsignedMultisignatureInput { +struct P2pMessage { + uint32_t type; + BinaryArray data; +}; + +class IP2pConnection { +public: + virtual ~IP2pConnection(); + virtual void read(P2pMessage &message) = 0; + virtual void write(const P2pMessage &message) = 0; + virtual void ban() = 0; + virtual void stop() = 0; +}; + +class IP2pNode { public: - UnsignedMultisignatureInput(uint64_t amount, uint32_t outputIndex); - UnsignedMultisignatureInput(const UnsignedMultisignatureInput& other) = delete; - UnsignedMultisignatureInput& operator=(const UnsignedMultisignatureInput& other) = delete; - uint64_t getAmount() const; - uint32_t getOutputIndex() const; - -private: - uint64_t amount; - uint32_t outputIndex; + virtual std::unique_ptr receiveConnection() = 0; + virtual void stop() = 0; }; } diff --git a/src/p2p/p2p_networks.h b/src/P2p/P2pNetworks.h old mode 100644 new mode 100755 similarity index 100% rename from src/p2p/p2p_networks.h rename to src/P2p/P2pNetworks.h diff --git a/src/P2p/P2pNode.cpp b/src/P2p/P2pNode.cpp new file mode 100755 index 0000000000..7ecafffd6c --- /dev/null +++ b/src/P2p/P2pNode.cpp @@ -0,0 +1,553 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pNode.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" + +#include "LevinProtocol.h" +#include "P2pConnectionProxy.h" +#include "P2pContext.h" +#include "P2pContextOwner.h" +#include "P2pNetworks.h" + +using namespace Common; +using namespace Logging; +using namespace System; + +namespace CryptoNote { + +namespace { + +class PeerIndexGenerator { +public: + + PeerIndexGenerator(size_t maxIndex) + : maxIndex(maxIndex), randCount(0) { + assert(maxIndex > 0); + } + + bool generate(size_t& num) { + while (randCount < (maxIndex + 1) * 3) { + ++randCount; + auto idx = getRandomIndex(); + if (visited.count(idx) == 0) { + visited.insert(idx); + num = idx; + return true; + } + } + + return false; + } + +private: + + size_t getRandomIndex() { + //divide by zero workaround + if (maxIndex == 0) { + return 0; + } + + size_t x = Crypto::rand() % (maxIndex + 1); + return (x * x * x) / (maxIndex * maxIndex); + } + + const size_t maxIndex; + size_t randCount; + std::set visited; +}; + +NetworkAddress getRemoteAddress(const TcpConnection& connection) { + auto addressAndPort = connection.getPeerAddressAndPort(); + NetworkAddress remoteAddress; + remoteAddress.ip = hostToNetwork(addressAndPort.first.getValue()); + remoteAddress.port = addressAndPort.second; + return remoteAddress; +} + +void doWithTimeoutAndThrow(System::Dispatcher& dispatcher, std::chrono::nanoseconds timeout, std::function f) { + std::string result; + System::ContextGroup cg(dispatcher); + System::ContextGroupTimeout cgTimeout(dispatcher, cg, timeout); + + cg.spawn([&] { + try { + f(); + } catch (System::InterruptedException&) { + result = "Operation timeout"; + } catch (std::exception& e) { + result = e.what(); + } + }); + + cg.wait(); + + if (!result.empty()) { + throw std::runtime_error(result); + } +} + +} + +P2pNode::P2pNode(const P2pNodeConfig& cfg, Dispatcher& dispatcher, Logging::ILogger& log, const Crypto::Hash& genesisHash, PeerIdType peerId) : + logger(log, "P2pNode:" + std::to_string(cfg.getBindPort())), + m_stopRequested(false), + m_cfg(cfg), + m_myPeerId(peerId), + m_genesisHash(genesisHash), + m_genesisPayload(CORE_SYNC_DATA{ 1, genesisHash }), + m_dispatcher(dispatcher), + workingContextGroup(dispatcher), + m_connectorTimer(dispatcher), + m_queueEvent(dispatcher) { + m_peerlist.init(cfg.getAllowLocalIp()); + m_listener = TcpListener(m_dispatcher, Ipv4Address(m_cfg.getBindIp()), m_cfg.getBindPort()); + for (auto& peer : cfg.getPeers()) { + m_peerlist.append_with_peer_white(peer); + } +} + +P2pNode::~P2pNode() { + assert(m_contexts.empty()); + assert(m_connectionQueue.empty()); +} + +std::unique_ptr P2pNode::receiveConnection() { + while (m_connectionQueue.empty()) { + m_queueEvent.wait(); + m_queueEvent.clear(); + if (m_stopRequested) { + throw InterruptedException(); + } + } + + auto connection = std::move(m_connectionQueue.front()); + m_connectionQueue.pop_front(); + + return connection; +} + +void P2pNode::start() { + workingContextGroup.spawn(std::bind(&P2pNode::acceptLoop, this)); + workingContextGroup.spawn(std::bind(&P2pNode::connectorLoop, this)); +} + +void P2pNode::stop() { + if (m_stopRequested) { + return; // already stopped + } + + m_stopRequested = true; + // clear prepared connections + m_connectionQueue.clear(); + // stop processing + m_queueEvent.set(); + workingContextGroup.interrupt(); + workingContextGroup.wait(); +} + +void P2pNode::serialize(ISerializer& s) { + uint8_t version = 1; + s(version, "version"); + + if (version != 1) { + return; + } + + s(m_peerlist, "peerlist"); +} + +void P2pNode::save(std::ostream& os) { + StdOutputStream stream(os); + BinaryOutputStreamSerializer a(stream); + CryptoNote::serialize(*this, a); +} + +void P2pNode::load(std::istream& in) { + StdInputStream stream(in); + BinaryInputStreamSerializer a(stream); + CryptoNote::serialize(*this, a); +} + +void P2pNode::acceptLoop() { + while (!m_stopRequested) { + try { + auto connection = m_listener.accept(); + auto ctx = new P2pContext(m_dispatcher, std::move(connection), true, + getRemoteAddress(connection), m_cfg.getTimedSyncInterval(), getGenesisPayload()); + logger(INFO) << "Incoming connection from " << ctx->getRemoteAddress(); + workingContextGroup.spawn([this, ctx] { + preprocessIncomingConnection(ContextPtr(ctx)); + }); + } catch (InterruptedException&) { + break; + } catch (const std::exception& e) { + logger(WARNING) << "Exception in acceptLoop: " << e.what(); + } + } + + logger(DEBUGGING) << "acceptLoop finished"; +} + +void P2pNode::connectorLoop() { + while (!m_stopRequested) { + try { + connectPeers(); + m_connectorTimer.sleep(m_cfg.getConnectInterval()); + } catch (InterruptedException&) { + break; + } catch (const std::exception& e) { + logger(WARNING) << "Exception in connectorLoop: " << e.what(); + } + } +} + +void P2pNode::connectPeers() { + if (!m_cfg.getExclusiveNodes().empty()) { + connectPeerList(m_cfg.getExclusiveNodes()); + return; + } + + // if white peer list is empty, get peers from seeds + if (m_peerlist.get_white_peers_count() == 0 && !m_cfg.getSeedNodes().empty()) { + auto seedNodes = m_cfg.getSeedNodes(); + std::random_shuffle(seedNodes.begin(), seedNodes.end()); + for (const auto& seed : seedNodes) { + auto conn = tryToConnectPeer(seed); + if (conn != nullptr && fetchPeerList(std::move(conn))) { + break; + } + } + } + + connectPeerList(m_cfg.getPriorityNodes()); + + const size_t totalExpectedConnectionsCount = m_cfg.getExpectedOutgoingConnectionsCount(); + const size_t expectedWhiteConnections = (totalExpectedConnectionsCount * m_cfg.getWhiteListConnectionsPercent()) / 100; + const size_t outgoingConnections = getOutgoingConnectionsCount(); + + if (outgoingConnections < totalExpectedConnectionsCount) { + if (outgoingConnections < expectedWhiteConnections) { + //start from white list + makeExpectedConnectionsCount(m_peerlist.getWhite(), expectedWhiteConnections); + //and then do grey list + makeExpectedConnectionsCount(m_peerlist.getGray(), totalExpectedConnectionsCount); + } else { + //start from grey list + makeExpectedConnectionsCount(m_peerlist.getGray(), totalExpectedConnectionsCount); + //and then do white list + makeExpectedConnectionsCount(m_peerlist.getWhite(), totalExpectedConnectionsCount); + } + } +} + +void P2pNode::makeExpectedConnectionsCount(const PeerlistManager::Peerlist& peerlist, size_t connectionsCount) { + while (getOutgoingConnectionsCount() < connectionsCount) { + if (peerlist.count() == 0) { + return; + } + + if (!makeNewConnectionFromPeerlist(peerlist)) { + break; + } + } +} + +bool P2pNode::makeNewConnectionFromPeerlist(const PeerlistManager::Peerlist& peerlist) { + size_t peerIndex; + PeerIndexGenerator idxGen(std::min(peerlist.count() - 1, m_cfg.getPeerListConnectRange())); + + for (size_t tryCount = 0; idxGen.generate(peerIndex) && tryCount < m_cfg.getPeerListGetTryCount(); ++tryCount) { + PeerlistEntry peer; + if (!peerlist.get(peer, peerIndex)) { + logger(WARNING) << "Failed to get peer from list, idx = " << peerIndex; + continue; + } + + if (isPeerUsed(peer)) { + continue; + } + + logger(DEBUGGING) << "Selected peer: [" << peer.id << " " << peer.adr << "] last_seen: " << + (peer.last_seen ? Common::timeIntervalToString(time(NULL) - peer.last_seen) : "never"); + + auto conn = tryToConnectPeer(peer.adr); + if (conn.get()) { + enqueueConnection(createProxy(std::move(conn))); + return true; + } + } + + return false; +} + +void P2pNode::preprocessIncomingConnection(ContextPtr ctx) { + try { + logger(DEBUGGING) << *ctx << "preprocessIncomingConnection"; + + OperationTimeout timeout(m_dispatcher, *ctx, m_cfg.getHandshakeTimeout()); + + // create proxy and process handshake + auto proxy = createProxy(std::move(ctx)); + if (proxy->processIncomingHandshake()) { + enqueueConnection(std::move(proxy)); + } + } catch (std::exception& e) { + logger(WARNING) << " Failed to process connection: " << e.what(); + } +} + +void P2pNode::connectPeerList(const std::vector& peers) { + for (const auto& address : peers) { + if (!isPeerConnected(address)) { + auto conn = tryToConnectPeer(address); + if (conn != nullptr) { + enqueueConnection(createProxy(std::move(conn))); + } + } + } +} + +bool P2pNode::isPeerConnected(const NetworkAddress& address) { + for (const auto& c : m_contexts) { + if (!c->isIncoming() && c->getRemoteAddress() == address) { + return true; + } + } + + return false; +} + +bool P2pNode::isPeerUsed(const PeerlistEntry& peer) { + if (m_myPeerId == peer.id) { + return true; //dont make connections to ourself + } + + for (const auto& c : m_contexts) { + if (c->getPeerId() == peer.id || (!c->isIncoming() && c->getRemoteAddress() == peer.adr)) { + return true; + } + } + + return false; +} + +P2pNode::ContextPtr P2pNode::tryToConnectPeer(const NetworkAddress& address) { + try { + TcpConnector connector(m_dispatcher); + TcpConnection tcpConnection; + + doWithTimeoutAndThrow(m_dispatcher, m_cfg.getConnectTimeout(), [&] { + tcpConnection = connector.connect( + Ipv4Address(Common::ipAddressToString(address.ip)), + static_cast(address.port)); + }); + + logger(DEBUGGING) << "connection established to " << address; + + return ContextPtr(new P2pContext(m_dispatcher, std::move(tcpConnection), false, address, m_cfg.getTimedSyncInterval(), getGenesisPayload())); + } catch (std::exception& e) { + logger(DEBUGGING) << "Connection to " << address << " failed: " << e.what(); + } + + return ContextPtr(); +} + +bool P2pNode::fetchPeerList(ContextPtr connection) { + try { + COMMAND_HANDSHAKE::request request{ getNodeData(), getGenesisPayload() }; + COMMAND_HANDSHAKE::response response; + + OperationTimeout timeout(m_dispatcher, *connection, m_cfg.getHandshakeTimeout()); + + connection->writeMessage(makeRequest(COMMAND_HANDSHAKE::ID, LevinProtocol::encode(request))); + + LevinProtocol::Command cmd; + if (!connection->readCommand(cmd)) { + throw std::runtime_error("Connection closed unexpectedly"); + } + + if (!cmd.isResponse || cmd.command != COMMAND_HANDSHAKE::ID) { + throw std::runtime_error("Received unexpected reply"); + } + + if (!LevinProtocol::decode(cmd.buf, response)) { + throw std::runtime_error("Invalid reply format"); + } + + if (response.node_data.network_id != request.node_data.network_id) { + logger(ERROR) << *connection << "COMMAND_HANDSHAKE failed, wrong network: " << response.node_data.network_id; + return false; + } + + return handleRemotePeerList(response.local_peerlist, response.node_data.local_time); + } catch (std::exception& e) { + logger(INFO) << *connection << "Failed to obtain peer list: " << e.what(); + } + + return false; +} + +namespace { + +std::list fixTimeDelta(const std::list& peerlist, time_t remoteTime) { + //fix time delta + int64_t delta = time(nullptr) - remoteTime; + std::list peerlistCopy(peerlist); + + for (PeerlistEntry& be : peerlistCopy) { + if (be.last_seen > uint64_t(remoteTime)) { + throw std::runtime_error("Invalid peerlist entry (time in future)"); + } + + be.last_seen += delta; + } + + return peerlistCopy; +} +} + +bool P2pNode::handleRemotePeerList(const std::list& peerlist, time_t remoteTime) { + return m_peerlist.merge_peerlist(fixTimeDelta(peerlist, remoteTime)); +} + +const CORE_SYNC_DATA& P2pNode::getGenesisPayload() const { + return m_genesisPayload; +} + +std::list P2pNode::getLocalPeerList() const { + std::list peerlist; + m_peerlist.get_peerlist_head(peerlist); + return peerlist; +} + +basic_node_data P2pNode::getNodeData() const { + basic_node_data nodeData; + nodeData.network_id = m_cfg.getNetworkId(); + nodeData.version = P2PProtocolVersion::CURRENT; + nodeData.local_time = time(nullptr); + nodeData.peer_id = m_myPeerId; + + if (m_cfg.getHideMyPort()) { + nodeData.my_port = 0; + } else { + nodeData.my_port = m_cfg.getExternalPort() ? m_cfg.getExternalPort() : m_cfg.getBindPort(); + } + + return nodeData; +} + +PeerIdType P2pNode::getPeerId() const { + return m_myPeerId; +} + +size_t P2pNode::getOutgoingConnectionsCount() const { + size_t count = 0; + + for (const auto& ctx : m_contexts) { + if (!ctx->isIncoming()) { + ++count; + } + } + + return count; +} + +std::unique_ptr P2pNode::createProxy(ContextPtr ctx) { + return std::unique_ptr( + new P2pConnectionProxy(P2pContextOwner(ctx.release(), m_contexts), *this)); +} + +void P2pNode::enqueueConnection(std::unique_ptr proxy) { + if (m_stopRequested) { + return; // skip operation + } + + m_connectionQueue.push_back(std::move(proxy)); + m_queueEvent.set(); +} + +void P2pNode::tryPing(P2pContext& ctx) { + if (ctx.getPeerId() == m_myPeerId || !m_peerlist.is_ip_allowed(ctx.getRemoteAddress().ip) || ctx.getPeerPort() == 0) { + return; + } + + NetworkAddress peerAddress; + peerAddress.ip = ctx.getRemoteAddress().ip; + peerAddress.port = ctx.getPeerPort(); + + try { + TcpConnector connector(m_dispatcher); + TcpConnection connection; + + doWithTimeoutAndThrow(m_dispatcher, m_cfg.getConnectTimeout(), [&] { + connection = connector.connect(Ipv4Address(Common::ipAddressToString(peerAddress.ip)), static_cast(peerAddress.port)); + }); + + doWithTimeoutAndThrow(m_dispatcher, m_cfg.getHandshakeTimeout(), [&] { + LevinProtocol proto(connection); + COMMAND_PING::request request; + COMMAND_PING::response response; + proto.invoke(COMMAND_PING::ID, request, response); + + if (response.status == PING_OK_RESPONSE_STATUS_TEXT && response.peer_id == ctx.getPeerId()) { + PeerlistEntry entry; + entry.adr = peerAddress; + entry.id = ctx.getPeerId(); + entry.last_seen = time(nullptr); + m_peerlist.append_with_peer_white(entry); + } else { + logger(Logging::DEBUGGING) << ctx << "back ping invoke wrong response \"" << response.status << "\" from" + << peerAddress << ", expected peerId=" << ctx.getPeerId() << ", got " << response.peer_id; + } + }); + } catch (std::exception& e) { + logger(DEBUGGING) << "Ping to " << peerAddress << " failed: " << e.what(); + } +} + +void P2pNode::handleNodeData(const basic_node_data& node, P2pContext& context) { + if (node.network_id != m_cfg.getNetworkId()) { + std::ostringstream msg; + msg << context << "COMMAND_HANDSHAKE Failed, wrong network! (" << node.network_id << ")"; + throw std::runtime_error(msg.str()); + } + + if (node.peer_id == m_myPeerId) { + throw std::runtime_error("Connection to self detected"); + } + + context.setPeerInfo(node.version, node.peer_id, static_cast(node.my_port)); + if (!context.isIncoming()) { + m_peerlist.set_peer_just_seen(node.peer_id, context.getRemoteAddress()); + } +} + +} diff --git a/src/P2p/P2pNode.h b/src/P2p/P2pNode.h new file mode 100755 index 0000000000..27ae7c1a19 --- /dev/null +++ b/src/P2p/P2pNode.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "IP2pNodeInternal.h" +#include "IStreamSerializable.h" +#include "NetNodeConfig.h" +#include "P2pInterfaces.h" +#include "P2pNodeConfig.h" +#include "P2pProtocolDefinitions.h" +#include "PeerListManager.h" + +namespace CryptoNote { + +class P2pContext; +class P2pConnectionProxy; + +class P2pNode : + public IP2pNode, + public IStreamSerializable, + IP2pNodeInternal { + +public: + + P2pNode( + const P2pNodeConfig& cfg, + System::Dispatcher& dispatcher, + Logging::ILogger& log, + const Crypto::Hash& genesisHash, + PeerIdType peerId); + + ~P2pNode(); + + // IP2pNode + virtual std::unique_ptr receiveConnection() override; + virtual void stop() override; + + // IStreamSerializable + virtual void save(std::ostream& os) override; + virtual void load(std::istream& in) override; + + // P2pNode + void start(); + void serialize(ISerializer& s); + +private: + typedef std::unique_ptr ContextPtr; + typedef std::list ContextList; + + Logging::LoggerRef logger; + bool m_stopRequested; + const P2pNodeConfig m_cfg; + const PeerIdType m_myPeerId; + const Crypto::Hash m_genesisHash; + const CORE_SYNC_DATA m_genesisPayload; + + System::Dispatcher& m_dispatcher; + System::ContextGroup workingContextGroup; + System::TcpListener m_listener; + System::Timer m_connectorTimer; + PeerlistManager m_peerlist; + ContextList m_contexts; + System::Event m_queueEvent; + std::deque> m_connectionQueue; + + // IP2pNodeInternal + virtual const CORE_SYNC_DATA& getGenesisPayload() const override; + virtual std::list getLocalPeerList() const override; + virtual basic_node_data getNodeData() const override; + virtual PeerIdType getPeerId() const override; + + virtual void handleNodeData(const basic_node_data& node, P2pContext& ctx) override; + virtual bool handleRemotePeerList(const std::list& peerlist, time_t local_time) override; + virtual void tryPing(P2pContext& ctx) override; + + // spawns + void acceptLoop(); + void connectorLoop(); + + // connection related + void connectPeers(); + void connectPeerList(const std::vector& peers); + bool isPeerConnected(const NetworkAddress& address); + bool isPeerUsed(const PeerlistEntry& peer); + ContextPtr tryToConnectPeer(const NetworkAddress& address); + bool fetchPeerList(ContextPtr connection); + + // making and processing connections + size_t getOutgoingConnectionsCount() const; + void makeExpectedConnectionsCount(const PeerlistManager::Peerlist& peerlist, size_t connectionsCount); + bool makeNewConnectionFromPeerlist(const PeerlistManager::Peerlist& peerlist); + void preprocessIncomingConnection(ContextPtr ctx); + void enqueueConnection(std::unique_ptr proxy); + std::unique_ptr createProxy(ContextPtr ctx); +}; + +} diff --git a/src/P2p/P2pNodeConfig.cpp b/src/P2p/P2pNodeConfig.cpp new file mode 100644 index 0000000000..51c715cc56 --- /dev/null +++ b/src/P2p/P2pNodeConfig.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "P2pNodeConfig.h" +#include "P2pNetworks.h" + +#include + +namespace CryptoNote { + +namespace { + +const std::chrono::nanoseconds P2P_DEFAULT_CONNECT_INTERVAL = std::chrono::seconds(2); +const size_t P2P_DEFAULT_CONNECT_RANGE = 20; +const size_t P2P_DEFAULT_PEERLIST_GET_TRY_COUNT = 10; + +} + +P2pNodeConfig::P2pNodeConfig() : + timedSyncInterval(std::chrono::seconds(P2P_DEFAULT_HANDSHAKE_INTERVAL)), + handshakeTimeout(std::chrono::milliseconds(P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT)), + connectInterval(P2P_DEFAULT_CONNECT_INTERVAL), + connectTimeout(std::chrono::milliseconds(P2P_DEFAULT_CONNECTION_TIMEOUT)), + networkId(BYTECOIN_NETWORK), + expectedOutgoingConnectionsCount(P2P_DEFAULT_CONNECTIONS_COUNT), + whiteListConnectionsPercent(P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT), + peerListConnectRange(P2P_DEFAULT_CONNECT_RANGE), + peerListGetTryCount(P2P_DEFAULT_PEERLIST_GET_TRY_COUNT) { +} + +// getters + +std::chrono::nanoseconds P2pNodeConfig::getTimedSyncInterval() const { + return timedSyncInterval; +} + +std::chrono::nanoseconds P2pNodeConfig::getHandshakeTimeout() const { + return handshakeTimeout; +} + +std::chrono::nanoseconds P2pNodeConfig::getConnectInterval() const { + return connectInterval; +} + +std::chrono::nanoseconds P2pNodeConfig::getConnectTimeout() const { + return connectTimeout; +} + +size_t P2pNodeConfig::getExpectedOutgoingConnectionsCount() const { + return expectedOutgoingConnectionsCount; +} + +size_t P2pNodeConfig::getWhiteListConnectionsPercent() const { + return whiteListConnectionsPercent; +} + +boost::uuids::uuid P2pNodeConfig::getNetworkId() const { + if (getTestnet()) { + boost::uuids::uuid copy = networkId; + copy.data[0] += 1; + return copy; + } + return networkId; +} + +size_t P2pNodeConfig::getPeerListConnectRange() const { + return peerListConnectRange; +} + +size_t P2pNodeConfig::getPeerListGetTryCount() const { + return peerListGetTryCount; +} + +// setters + +void P2pNodeConfig::setTimedSyncInterval(std::chrono::nanoseconds interval) { + timedSyncInterval = interval; +} + +void P2pNodeConfig::setHandshakeTimeout(std::chrono::nanoseconds timeout) { + handshakeTimeout = timeout; +} + +void P2pNodeConfig::setConnectInterval(std::chrono::nanoseconds interval) { + connectInterval = interval; +} + +void P2pNodeConfig::setConnectTimeout(std::chrono::nanoseconds timeout) { + connectTimeout = timeout; +} + +void P2pNodeConfig::setExpectedOutgoingConnectionsCount(size_t count) { + expectedOutgoingConnectionsCount = count; +} + +void P2pNodeConfig::setWhiteListConnectionsPercent(size_t percent) { + if (percent > 100) { + throw std::invalid_argument("whiteListConnectionsPercent cannot be greater than 100"); + } + + whiteListConnectionsPercent = percent; +} + +void P2pNodeConfig::setNetworkId(const boost::uuids::uuid& id) { + networkId = id; +} + +void P2pNodeConfig::setPeerListConnectRange(size_t range) { + peerListConnectRange = range; +} + +void P2pNodeConfig::setPeerListGetTryCount(size_t count) { + peerListGetTryCount = count; +} + +} diff --git a/src/P2p/P2pNodeConfig.h b/src/P2p/P2pNodeConfig.h new file mode 100644 index 0000000000..d8f5fa1cf6 --- /dev/null +++ b/src/P2p/P2pNodeConfig.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include "NetNodeConfig.h" + +namespace CryptoNote { + +class P2pNodeConfig : public NetNodeConfig { +public: + P2pNodeConfig(); + + // getters + std::chrono::nanoseconds getTimedSyncInterval() const; + std::chrono::nanoseconds getHandshakeTimeout() const; + std::chrono::nanoseconds getConnectInterval() const; + std::chrono::nanoseconds getConnectTimeout() const; + size_t getExpectedOutgoingConnectionsCount() const; + size_t getWhiteListConnectionsPercent() const; + boost::uuids::uuid getNetworkId() const; + size_t getPeerListConnectRange() const; + size_t getPeerListGetTryCount() const; + + // setters + void setTimedSyncInterval(std::chrono::nanoseconds interval); + void setHandshakeTimeout(std::chrono::nanoseconds timeout); + void setConnectInterval(std::chrono::nanoseconds interval); + void setConnectTimeout(std::chrono::nanoseconds timeout); + void setExpectedOutgoingConnectionsCount(size_t count); + void setWhiteListConnectionsPercent(size_t percent); + void setNetworkId(const boost::uuids::uuid& id); + void setPeerListConnectRange(size_t range); + void setPeerListGetTryCount(size_t count); + +private: + std::chrono::nanoseconds timedSyncInterval; + std::chrono::nanoseconds handshakeTimeout; + std::chrono::nanoseconds connectInterval; + std::chrono::nanoseconds connectTimeout; + boost::uuids::uuid networkId; + size_t expectedOutgoingConnectionsCount; + size_t whiteListConnectionsPercent; + size_t peerListConnectRange; + size_t peerListGetTryCount; +}; + +} diff --git a/src/p2p/p2p_protocol_defs.h b/src/P2p/P2pProtocolDefinitions.h old mode 100644 new mode 100755 similarity index 85% rename from src/p2p/p2p_protocol_defs.h rename to src/P2p/P2pProtocolDefinitions.h index 84619338bd..e83d6f7950 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/P2p/P2pProtocolDefinitions.h @@ -17,16 +17,16 @@ #pragma once -#include "p2p_protocol_types.h" +#include "P2pProtocolTypes.h" #include "crypto/crypto.h" -#include "cryptonote_config.h" -#include "cryptonote_core/cryptonote_stat_info.h" +#include "CryptoNoteConfig.h" +#include "CryptoNoteCore/CryptoNoteStatInfo.h" // new serialization -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" namespace CryptoNote { @@ -64,7 +64,7 @@ namespace CryptoNote uint8_t version; uint64_t local_time; uint32_t my_port; - peerid_type peer_id; + PeerIdType peer_id; void serialize(ISerializer& s) { KV_MEMBER(network_id) @@ -80,8 +80,8 @@ namespace CryptoNote struct CORE_SYNC_DATA { - uint64_t current_height; - crypto::hash top_id; + uint32_t current_height; + Crypto::Hash top_id; void serialize(ISerializer& s) { KV_MEMBER(current_height) @@ -96,7 +96,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_HANDSHAKE { - const static int ID = P2P_COMMANDS_POOL_BASE + 1; + enum { ID = P2P_COMMANDS_POOL_BASE + 1 }; struct request { @@ -114,7 +114,7 @@ namespace CryptoNote { basic_node_data node_data; CORE_SYNC_DATA payload_data; - std::list local_peerlist; + std::list local_peerlist; void serialize(ISerializer& s) { KV_MEMBER(node_data) @@ -130,7 +130,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_TIMED_SYNC { - const static int ID = P2P_COMMANDS_POOL_BASE + 2; + enum { ID = P2P_COMMANDS_POOL_BASE + 2 }; struct request { @@ -146,7 +146,7 @@ namespace CryptoNote { uint64_t local_time; CORE_SYNC_DATA payload_data; - std::list local_peerlist; + std::list local_peerlist; void serialize(ISerializer& s) { KV_MEMBER(local_time) @@ -167,7 +167,7 @@ namespace CryptoNote have accessible connection point. Only other nodes can add peer to peerlist, and ONLY in case when peer has accepted connection and answered to ping. */ - const static int ID = P2P_COMMANDS_POOL_BASE + 3; + enum { ID = P2P_COMMANDS_POOL_BASE + 3 }; #define PING_OK_RESPONSE_STATUS_TEXT "OK" @@ -180,7 +180,7 @@ namespace CryptoNote struct response { std::string status; - peerid_type peer_id; + PeerIdType peer_id; void serialize(ISerializer& s) { KV_MEMBER(status) @@ -196,9 +196,9 @@ namespace CryptoNote struct proof_of_trust { - peerid_type peer_id; + PeerIdType peer_id; uint64_t time; - crypto::signature sign; + Crypto::Signature sign; void serialize(ISerializer& s) { KV_MEMBER(peer_id) @@ -207,16 +207,16 @@ namespace CryptoNote } }; - inline crypto::hash get_proof_of_trust_hash(const proof_of_trust& pot) { + inline Crypto::Hash get_proof_of_trust_hash(const proof_of_trust& pot) { std::string s; s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); + return Crypto::cn_fast_hash(s.data(), s.size()); } struct COMMAND_REQUEST_STAT_INFO { - const static int ID = P2P_COMMANDS_POOL_BASE + 4; + enum { ID = P2P_COMMANDS_POOL_BASE + 4 }; struct request { @@ -251,7 +251,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_REQUEST_NETWORK_STATE { - const static int ID = P2P_COMMANDS_POOL_BASE + 5; + enum { ID = P2P_COMMANDS_POOL_BASE + 5 }; struct request { @@ -264,10 +264,10 @@ namespace CryptoNote struct response { - std::list local_peerlist_white; - std::list local_peerlist_gray; + std::list local_peerlist_white; + std::list local_peerlist_gray; std::list connections_list; - peerid_type my_id; + PeerIdType my_id; uint64_t local_time; void serialize(ISerializer& s) { @@ -285,7 +285,7 @@ namespace CryptoNote /************************************************************************/ struct COMMAND_REQUEST_PEER_ID { - const static int ID = P2P_COMMANDS_POOL_BASE + 6; + enum { ID = P2P_COMMANDS_POOL_BASE + 6 }; struct request { @@ -294,7 +294,7 @@ namespace CryptoNote struct response { - peerid_type my_id; + PeerIdType my_id; void serialize(ISerializer& s) { KV_MEMBER(my_id) diff --git a/src/p2p/p2p_protocol_types.h b/src/P2p/P2pProtocolTypes.h old mode 100644 new mode 100755 similarity index 78% rename from src/p2p/p2p_protocol_types.h rename to src/P2p/P2pProtocolTypes.h index 27a5510022..b803410218 --- a/src/p2p/p2p_protocol_types.h +++ b/src/P2p/P2pProtocolTypes.h @@ -26,41 +26,41 @@ namespace CryptoNote { typedef boost::uuids::uuid uuid; typedef boost::uuids::uuid net_connection_id; - typedef uint64_t peerid_type; + typedef uint64_t PeerIdType; #pragma pack (push, 1) - struct net_address + struct NetworkAddress { uint32_t ip; uint32_t port; }; - struct peerlist_entry + struct PeerlistEntry { - net_address adr; - peerid_type id; - time_t last_seen; + NetworkAddress adr; + PeerIdType id; + uint64_t last_seen; }; struct connection_entry { - net_address adr; - peerid_type id; + NetworkAddress adr; + PeerIdType id; bool is_income; }; #pragma pack(pop) - inline bool operator < (const net_address& a, const net_address& b) { + inline bool operator < (const NetworkAddress& a, const NetworkAddress& b) { return std::tie(a.ip, a.port) < std::tie(b.ip, b.port); } - inline bool operator == (const net_address& a, const net_address& b) { + inline bool operator == (const NetworkAddress& a, const NetworkAddress& b) { return memcmp(&a, &b, sizeof(a)) == 0; } - inline std::ostream& operator << (std::ostream& s, const net_address& na) { + inline std::ostream& operator << (std::ostream& s, const NetworkAddress& na) { return s << Common::ipAddressToString(na.ip) << ":" << std::to_string(na.port); } diff --git a/src/p2p/PeerListManager.cpp b/src/P2p/PeerListManager.cpp old mode 100644 new mode 100755 similarity index 59% rename from src/p2p/PeerListManager.cpp rename to src/P2p/PeerListManager.cpp index 70f31eb2b9..c1b5de091f --- a/src/p2p/PeerListManager.cpp +++ b/src/P2p/PeerListManager.cpp @@ -21,38 +21,101 @@ #include #include +#include "Serialization/SerializationOverloads.h" + using namespace CryptoNote; -//-------------------------------------------------------------------------------------------------- -bool peerlist_manager::init(bool allow_local_ip) -{ - m_allow_local_ip = allow_local_ip; +namespace CryptoNote { + template + bool serialize(boost::multi_index_container& value, Common::StringView name, ISerializer& s) { + if (s.type() == ISerializer::INPUT) { + readSequence(std::inserter(value, value.end()), name, s); + } else { + writeSequence(value.begin(), value.end(), name, s); + } + + return true; + } + + void serialize(NetworkAddress& na, ISerializer& s) { + s(na.ip, "ip"); + s(na.port, "port"); + } + + void serialize(PeerlistEntry& pe, ISerializer& s) { + s(pe.adr, "adr"); + s(pe.id, "id"); + s(pe.last_seen, "last_seen"); + } + +} + +PeerlistManager::Peerlist::Peerlist(peers_indexed& peers, size_t maxSize) : + m_peers(peers), m_maxSize(maxSize) { +} + +void PeerlistManager::serialize(ISerializer& s) { + const uint8_t currentVersion = 1; + uint8_t version = currentVersion; + + s(version, "version"); + + if (version != currentVersion) { + return; + } + + s(m_peers_white, "whitelist"); + s(m_peers_gray, "graylist"); +} + +size_t PeerlistManager::Peerlist::count() const { + return m_peers.size(); +} + +bool PeerlistManager::Peerlist::get(PeerlistEntry& entry, size_t i) const { + if (i >= m_peers.size()) + return false; + + peers_indexed::index::type& by_time_index = m_peers.get(); + + auto it = by_time_index.rbegin(); + std::advance(it, i); + entry = *it; + return true; } -//-------------------------------------------------------------------------------------------------- - void peerlist_manager::trim_white_peerlist() -{ - while (m_peers_gray.size() > CryptoNote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) - { - peers_indexed::index::type& sorted_index = m_peers_gray.get(); +void PeerlistManager::Peerlist::trim() { + peers_indexed::index::type& sorted_index = m_peers.get(); + while (m_peers.size() > m_maxSize) { sorted_index.erase(sorted_index.begin()); } } + +PeerlistManager::PeerlistManager() : + m_whitePeerlist(m_peers_white, CryptoNote::P2P_LOCAL_WHITE_PEERLIST_LIMIT), + m_grayPeerlist(m_peers_gray, CryptoNote::P2P_LOCAL_GRAY_PEERLIST_LIMIT) {} + //-------------------------------------------------------------------------------------------------- - void peerlist_manager::trim_gray_peerlist() +bool PeerlistManager::init(bool allow_local_ip) { - while (m_peers_white.size() > CryptoNote::P2P_LOCAL_WHITE_PEERLIST_LIMIT) - { - peers_indexed::index::type& sorted_index = m_peers_white.get(); - sorted_index.erase(sorted_index.begin()); - } + m_allow_local_ip = allow_local_ip; + return true; } + //-------------------------------------------------------------------------------------------------- +void PeerlistManager::trim_white_peerlist() { + m_whitePeerlist.trim(); +} +//-------------------------------------------------------------------------------------------------- +void PeerlistManager::trim_gray_peerlist() { + m_grayPeerlist.trim(); +} -bool peerlist_manager::merge_peerlist(const std::list& outer_bs) +//-------------------------------------------------------------------------------------------------- +bool PeerlistManager::merge_peerlist(const std::list& outer_bs) { - for(const peerlist_entry& be : outer_bs) { + for(const PeerlistEntry& be : outer_bs) { append_with_peer_gray(be); } @@ -62,37 +125,19 @@ bool peerlist_manager::merge_peerlist(const std::list& outer_bs) } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_white_peer_by_index(peerlist_entry& p, size_t i) -{ - if (i >= m_peers_white.size()) - return false; - - peers_indexed::index::type& by_time_index = m_peers_white.get(); - - auto it = by_time_index.rbegin(); - std::advance(it, i); - p = *it; - - return true; +bool PeerlistManager::get_white_peer_by_index(PeerlistEntry& p, size_t i) const { + return m_whitePeerlist.get(p, i); } -//-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_gray_peer_by_index(peerlist_entry& p, size_t i) -{ - if (i >= m_peers_gray.size()) - return false; - - peers_indexed::index::type& by_time_index = m_peers_gray.get(); - - auto it = by_time_index.rbegin(); - std::advance(it, i); - p = *it; +//-------------------------------------------------------------------------------------------------- - return true; +bool PeerlistManager::get_gray_peer_by_index(PeerlistEntry& p, size_t i) const { + return m_grayPeerlist.get(p, i); } + //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::is_ip_allowed(uint32_t ip) +bool PeerlistManager::is_ip_allowed(uint32_t ip) const { System::Ipv4Address addr(networkToHost(ip)); @@ -109,9 +154,9 @@ bool peerlist_manager::is_ip_allowed(uint32_t ip) } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_peerlist_head(std::list& bs_head, uint32_t depth) +bool PeerlistManager::get_peerlist_head(std::list& bs_head, uint32_t depth) const { - peers_indexed::index::type& by_time_index = m_peers_white.get(); + const peers_indexed::index::type& by_time_index = m_peers_white.get(); uint32_t cnt = 0; BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index) @@ -126,10 +171,10 @@ bool peerlist_manager::get_peerlist_head(std::list& bs_head, uin } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) +bool PeerlistManager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) const { - peers_indexed::index::type& by_time_index_gr = m_peers_gray.get(); - peers_indexed::index::type& by_time_index_wt = m_peers_white.get(); + const peers_indexed::index::type& by_time_index_gr = m_peers_gray.get(); + const peers_indexed::index::type& by_time_index_wt = m_peers_white.get(); std::copy(by_time_index_gr.rbegin(), by_time_index_gr.rend(), std::back_inserter(pl_gray)); std::copy(by_time_index_wt.rbegin(), by_time_index_wt.rend(), std::back_inserter(pl_white)); @@ -138,20 +183,20 @@ bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port) +bool PeerlistManager::set_peer_just_seen(PeerIdType peer, uint32_t ip, uint32_t port) { - net_address addr; + NetworkAddress addr; addr.ip = ip; addr.port = port; return set_peer_just_seen(peer, addr); } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) +bool PeerlistManager::set_peer_just_seen(PeerIdType peer, const NetworkAddress& addr) { try { //find in white list - peerlist_entry ple; + PeerlistEntry ple; ple.adr = addr; ple.id = peer; ple.last_seen = time(NULL); @@ -163,7 +208,7 @@ bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& a } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) +bool PeerlistManager::append_with_peer_white(const PeerlistEntry& ple) { try { if (!is_ip_allowed(ple.adr.ip)) @@ -191,7 +236,7 @@ bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) } //-------------------------------------------------------------------------------------------------- -bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) +bool PeerlistManager::append_with_peer_gray(const PeerlistEntry& ple) { try { if (!is_ip_allowed(ple.adr.ip)) @@ -220,3 +265,11 @@ bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) return false; } //-------------------------------------------------------------------------------------------------- + +PeerlistManager::Peerlist& PeerlistManager::getWhite() { + return m_whitePeerlist; +} + +PeerlistManager::Peerlist& PeerlistManager::getGray() { + return m_grayPeerlist; +} diff --git a/src/p2p/PeerListManager.h b/src/P2p/PeerListManager.h similarity index 53% rename from src/p2p/PeerListManager.h rename to src/P2p/PeerListManager.h index 17863f0b1d..2ea4f7aa1a 100644 --- a/src/p2p/PeerListManager.h +++ b/src/P2p/PeerListManager.h @@ -24,71 +24,75 @@ #include #include -#include "p2p_protocol_types.h" -#include "cryptonote_config.h" +#include "P2pProtocolTypes.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { +class ISerializer; /************************************************************************/ /* */ /************************************************************************/ -class peerlist_manager -{ -public: - bool init(bool allow_local_ip); - size_t get_white_peers_count(){ return m_peers_white.size(); } - size_t get_gray_peers_count(){ return m_peers_gray.size(); } - bool merge_peerlist(const std::list& outer_bs); - bool get_peerlist_head(std::list& bs_head, uint32_t depth = CryptoNote::P2P_DEFAULT_PEERS_IN_HANDSHAKE); - bool get_peerlist_full(std::list& pl_gray, std::list& 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); - bool append_with_peer_gray(const peerlist_entry& pr); - bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); - bool set_peer_just_seen(peerid_type peer, const net_address& addr); - bool set_peer_unreachable(const peerlist_entry& pr); - bool is_ip_allowed(uint32_t ip); - void trim_white_peerlist(); - void trim_gray_peerlist(); - -private: - +class PeerlistManager { struct by_time{}; struct by_id{}; struct by_addr{}; typedef boost::multi_index_container< - peerlist_entry, + PeerlistEntry, boost::multi_index::indexed_by< // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, + boost::multi_index::ordered_unique, boost::multi_index::member >, // sort by peerlist_entry::last_seen< - boost::multi_index::ordered_non_unique, boost::multi_index::member > + boost::multi_index::ordered_non_unique, boost::multi_index::member > > > peers_indexed; public: - template - void serialize(Archive &a, const t_version_type ver) - { - if (ver < 4) - return; + class Peerlist { + public: + Peerlist(peers_indexed& peers, size_t maxSize); + size_t count() const; + bool get(PeerlistEntry& entry, size_t index) const; + void trim(); - a & m_peers_white; - a & m_peers_gray; - } + private: + peers_indexed& m_peers; + const size_t m_maxSize; + }; -private: + PeerlistManager(); - friend class boost::serialization::access; + bool init(bool allow_local_ip); + size_t get_white_peers_count() const { return m_peers_white.size(); } + size_t get_gray_peers_count() const { return m_peers_gray.size(); } + bool merge_peerlist(const std::list& outer_bs); + bool get_peerlist_head(std::list& bs_head, uint32_t depth = CryptoNote::P2P_DEFAULT_PEERS_IN_HANDSHAKE) const; + bool get_peerlist_full(std::list& pl_gray, std::list& pl_white) const; + bool get_white_peer_by_index(PeerlistEntry& p, size_t i) const; + bool get_gray_peer_by_index(PeerlistEntry& p, size_t i) const; + bool append_with_peer_white(const PeerlistEntry& pr); + bool append_with_peer_gray(const PeerlistEntry& pr); + bool set_peer_just_seen(PeerIdType peer, uint32_t ip, uint32_t port); + bool set_peer_just_seen(PeerIdType peer, const NetworkAddress& addr); + bool set_peer_unreachable(const PeerlistEntry& pr); + bool is_ip_allowed(uint32_t ip) const; + void trim_white_peerlist(); + void trim_gray_peerlist(); + + void serialize(ISerializer& s); + + Peerlist& getWhite(); + Peerlist& getGray(); + +private: std::string m_config_folder; bool m_allow_local_ip; peers_indexed m_peers_gray; peers_indexed m_peers_white; + Peerlist m_whitePeerlist; + Peerlist m_grayPeerlist; }; } - -BOOST_CLASS_VERSION(CryptoNote::peerlist_manager, 4) diff --git a/src/payment_service/NodeFactory.cpp b/src/PaymentGate/NodeFactory.cpp similarity index 51% rename from src/payment_service/NodeFactory.cpp rename to src/PaymentGate/NodeFactory.cpp index 7c468c7f25..23c77c866e 100644 --- a/src/payment_service/NodeFactory.cpp +++ b/src/PaymentGate/NodeFactory.cpp @@ -17,7 +17,7 @@ #include "NodeFactory.h" -#include "node_rpc_proxy/NodeRpcProxy.h" +#include "NodeRpcProxy/NodeRpcProxy.h" #include #include @@ -33,41 +33,53 @@ class NodeRpcStub: public CryptoNote::INode { virtual bool shutdown() { return true; } virtual size_t getPeerCount() const { return 0; } - virtual uint64_t getLastLocalBlockHeight() const { return 0; } - virtual uint64_t getLastKnownBlockHeight() const { return 0; } - virtual uint64_t getLocalBlockCount() const override { return 0; } - virtual uint64_t getKnownBlockCount() const override { return 0; } - virtual uint64_t getLastLocalBlockTimestamp() const { return 0; } + virtual uint32_t getLastLocalBlockHeight() const override { return 0; } + virtual uint32_t getLastKnownBlockHeight() const override { return 0; } + virtual uint32_t getLocalBlockCount() const override { return 0; } + virtual uint32_t getKnownBlockCount() const override { return 0; } + virtual uint64_t getLastLocalBlockTimestamp() const override { return 0; } - virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) { callback(std::error_code()); } + virtual void relayTransaction(const CryptoNote::Transaction& transaction, const Callback& callback) override { callback(std::error_code()); } virtual void getRandomOutsByAmounts(std::vector&& amounts, uint64_t outsCount, - std::vector& result, const Callback& callback) { } - virtual void getNewBlocks(std::list&& knownBlockIds, std::list& newBlocks, uint64_t& startHeight, const Callback& callback) { + std::vector& result, const Callback& callback) override { + } + virtual void getNewBlocks(std::vector&& knownBlockIds, std::vector& newBlocks, uint32_t& startHeight, const Callback& callback) override { startHeight = 0; callback(std::error_code()); } - virtual void getTransactionOutsGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) { } + virtual void getTransactionOutsGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices, const Callback& callback) override { } - virtual void queryBlocks(std::list&& knownBlockIds, uint64_t timestamp, std::list& newBlocks, - uint64_t& startHeight, const CryptoNote::INode::Callback& callback) { + virtual void queryBlocks(std::vector&& knownBlockIds, uint64_t timestamp, std::vector& newBlocks, + uint32_t& startHeight, const Callback& callback) override { startHeight = 0; callback(std::error_code()); - } + }; - virtual void getPoolSymmetricDifference(std::vector&& known_pool_tx_ids, crypto::hash known_block_id, - bool& is_bc_actual, std::vector& new_txs, std::vector& deleted_tx_ids, - const Callback& callback) { - is_bc_actual = true; + virtual void getPoolSymmetricDifference(std::vector&& knownPoolTxIds, Crypto::Hash knownBlockId, bool& isBcActual, + std::vector>& newTxs, std::vector& deletedTxIds, const Callback& callback) { + isBcActual = true; callback(std::error_code()); } - virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, + virtual void getBlocks(const std::vector& blockHeights, std::vector>& blocks, + const Callback& callback) override { } + + virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, + const Callback& callback) override { } + + virtual void getBlocks(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t blocksNumberLimit, std::vector& blocks, uint32_t& blocksNumberWithinTimestamps, const Callback& callback) override { } - virtual void getBlocks(const std::vector& blockHashes, std::vector& blocks, + virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, const Callback& callback) override { } - virtual void getTransactions(const std::vector& transactionHashes, std::vector& transactions, + virtual void getPoolTransactions(uint64_t timestampBegin, uint64_t timestampEnd, uint32_t transactionsNumberLimit, std::vector& transactions, uint64_t& transactionsNumberWithinTimestamps, + const Callback& callback) override { } + + virtual void getTransactionsByPaymentId(const Crypto::Hash& paymentId, std::vector& transactions, + const Callback& callback) override { } + + virtual void getMultisignatureOutputByGlobalIndex(uint64_t amount, uint32_t gindex, CryptoNote::MultisignatureOutput& out, const Callback& callback) override { } virtual void isSynchronized(bool& syncStatus, const Callback& callback) override { } @@ -77,16 +89,16 @@ class NodeRpcStub: public CryptoNote::INode { class NodeInitObserver { public: - NodeInitObserver() {} + NodeInitObserver() { + initFuture = initPromise.get_future(); + } void initCompleted(std::error_code result) { initPromise.set_value(result); } void waitForInitEnd() { - auto future = initPromise.get_future(); - - std::error_code ec = future.get(); + std::error_code ec = initFuture.get(); if (ec) { throw std::system_error(ec); } @@ -95,6 +107,7 @@ class NodeInitObserver { private: std::promise initPromise; + std::future initFuture; }; NodeFactory::NodeFactory() { diff --git a/src/payment_service/NodeFactory.h b/src/PaymentGate/NodeFactory.h similarity index 100% rename from src/payment_service/NodeFactory.h rename to src/PaymentGate/NodeFactory.h diff --git a/src/payment_service/JsonRpcMessages.cpp b/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp old mode 100644 new mode 100755 similarity index 79% rename from src/payment_service/JsonRpcMessages.cpp rename to src/PaymentGate/PaymentServiceJsonRpcMessages.cpp index ab4b68070e..19084665da --- a/src/payment_service/JsonRpcMessages.cpp +++ b/src/PaymentGate/PaymentServiceJsonRpcMessages.cpp @@ -15,8 +15,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "JsonRpcMessages.h" -#include "serialization/SerializationOverloads.h" +#include "PaymentServiceJsonRpcMessages.h" +#include "Serialization/SerializationOverloads.h" namespace PaymentService { @@ -25,7 +25,7 @@ void TransferDestination::serialize(CryptoNote::ISerializer& serializer) { r &= serializer(address, "address"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -37,7 +37,7 @@ void SendTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { serializer(paymentId, "payment_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -45,10 +45,45 @@ void SendTransactionResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(transactionId, "transaction_id"); } +void GetAddressRequest::serialize(CryptoNote::ISerializer& serializer) { + serializer(index, "index"); +} + +void DeleteAddressRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(address, "address"); + + if (!r) { + throw RequestSerializationError(); + } +} + +void DeleteAddressResponse::serialize(CryptoNote::ISerializer& serializer) { +} + +void CreateAddressResponse::serialize(CryptoNote::ISerializer& serializer) { + serializer(address, "address"); +} + +void GetAddressCountResponse::serialize(CryptoNote::ISerializer& serializer) { + serializer(count, "count"); +} + void GetAddressResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(address, "address"); } +void GetActualBalanceRequest::serialize(CryptoNote::ISerializer& serializer) { + bool r = serializer(address, "address"); + + if (!r) { + throw std::runtime_error("Required parameter is missing"); + } +} + +void GetPendingBalanceRequest::serialize(CryptoNote::ISerializer& serializer) { + serializer(address, "address"); +} + void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(actualBalance, "actual_balance"); } @@ -69,7 +104,7 @@ void GetTransactionIdByTransferIdRequest::serialize(CryptoNote::ISerializer& ser bool r = serializer(transferId, "transfer_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -81,7 +116,7 @@ void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { bool r = serializer(transactionId, "transaction_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -91,16 +126,16 @@ void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) { serializer(totalAmount, "total_amount"); serializer(fee, "fee"); serializer(hash, "hash"); - serializer(isCoinbase, "is_coin_base"); serializer(blockHeight, "block_height"); serializer(timestamp, "timestamp"); serializer(extra, "extra"); + serializer(transfers, "transfers"); } void GetTransactionResponse::serialize(CryptoNote::ISerializer& serializer) { serializer(found, "found"); - if (!found) { + if (found) { serializer(transactionInfo, "transaction_info"); } } @@ -127,7 +162,7 @@ void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer) { bool r = serializer(transferId, "transfer_id"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } @@ -142,7 +177,7 @@ void GetIncomingPaymentsRequest::serialize(CryptoNote::ISerializer& serializer) bool r = serializer(payments, "payments"); if (!r) { - throw std::runtime_error("Required parameter is missing"); + throw RequestSerializationError(); } } diff --git a/src/payment_service/JsonRpcMessages.h b/src/PaymentGate/PaymentServiceJsonRpcMessages.h similarity index 82% rename from src/payment_service/JsonRpcMessages.h rename to src/PaymentGate/PaymentServiceJsonRpcMessages.h index c3862b1871..44aa921560 100644 --- a/src/payment_service/JsonRpcMessages.h +++ b/src/PaymentGate/PaymentServiceJsonRpcMessages.h @@ -17,9 +17,10 @@ #pragma once -#include "serialization/ISerializer.h" -#include #include +#include + +#include "Serialization/ISerializer.h" namespace PaymentService { @@ -37,6 +38,7 @@ struct TransferDestination { struct SendTransactionRequest { SendTransactionRequest() : unlockTime(0) {} + std::vector destinations; uint64_t fee; uint64_t mixin; @@ -51,17 +53,53 @@ struct SendTransactionResponse { void serialize(CryptoNote::ISerializer& serializer); }; +struct GetAddressRequest { + GetAddressRequest() : index(0) {} + + size_t index; + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct GetAddressCountResponse { + std::size_t count; + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct DeleteAddressRequest { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct DeleteAddressResponse { + void serialize(CryptoNote::ISerializer& serializer); +}; + +struct CreateAddressResponse { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + struct GetAddressResponse { std::string address; void serialize(CryptoNote::ISerializer& serializer); }; +struct GetActualBalanceRequest { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + struct GetActualBalanceResponse { uint64_t actualBalance; void serialize(CryptoNote::ISerializer& serializer); }; +struct GetPendingBalanceRequest { + std::string address; + void serialize(CryptoNote::ISerializer& serializer); +}; + struct GetPendingBalanceResponse { uint64_t pendingBalance; @@ -98,16 +136,23 @@ struct GetTransactionRequest { void serialize(CryptoNote::ISerializer& serializer); }; +struct TransferRpcInfo { + std::string address; + int64_t amount; + + void serialize(CryptoNote::ISerializer& serializer); +}; + struct TransactionRpcInfo { uint64_t firstTransferId; uint64_t transferCount; int64_t totalAmount; uint64_t fee; std::string hash; - bool isCoinbase; uint64_t blockHeight; uint64_t timestamp; std::string extra; + std::vector transfers; void serialize(CryptoNote::ISerializer& serializer); }; @@ -132,13 +177,6 @@ struct ListTransactionsResponse { void serialize(CryptoNote::ISerializer& serializer); }; -struct TransferRpcInfo { - std::string address; - int64_t amount; - - void serialize(CryptoNote::ISerializer& serializer); -}; - struct GetTransferRequest { uint64_t transferId; diff --git a/src/payment_service/JsonRpcServer.cpp b/src/PaymentGate/PaymentServiceJsonRpcServer.cpp old mode 100644 new mode 100755 similarity index 56% rename from src/payment_service/JsonRpcServer.cpp rename to src/PaymentGate/PaymentServiceJsonRpcServer.cpp index 83cc9debf3..3e1eb8889c --- a/src/payment_service/JsonRpcServer.cpp +++ b/src/PaymentGate/PaymentServiceJsonRpcServer.cpp @@ -15,86 +15,25 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "JsonRpcServer.h" - -#include -#include -#include -#include -#include -#include "HTTP/HttpParserErrorCodes.h" - -#include -#include -#include -#include -#include -#include "HTTP/HttpParser.h" -#include "HTTP/HttpResponse.h" -#include "JsonRpcMessages.h" +#include "PaymentServiceJsonRpcServer.h" + +#include "PaymentServiceJsonRpcMessages.h" #include "WalletService.h" -#include "WalletServiceErrorCodes.h" #include "Common/JsonValue.h" -#include "serialization/JsonInputValueSerializer.h" -#include "serialization/JsonOutputStreamSerializer.h" +#include "Serialization/JsonInputValueSerializer.h" +#include "Serialization/JsonOutputStreamSerializer.h" namespace PaymentService { -JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) : - HttpServer(sys, loggerGroup), - system(sys), - stopEvent(stopEvent), - service(service), - logger(loggerGroup, "JsonRpcServer") +PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) + : JsonRpcServer(sys, stopEvent, loggerGroup) + , service(service) + , logger(loggerGroup, "PaymentServiceJsonRpcServer") { } -void JsonRpcServer::start(const Configuration& config) { - HttpServer::start(config.bindAddress, config.bindPort); - stopEvent.wait(); - HttpServer::stop(); -} - -void JsonRpcServer::processRequest(const CryptoNote::HttpRequest& req, CryptoNote::HttpResponse& resp) { - try { - logger(Logging::TRACE) << "HTTP request came: \n" << req; - - if (req.getUrl() == "/json_rpc") { - std::stringstream jsonInputStream(req.getBody()); - Common::JsonValue jsonRpcRequest; - Common::JsonValue jsonRpcResponse(Common::JsonValue::OBJECT); - - try { - jsonInputStream >> jsonRpcRequest; - } catch (std::runtime_error&) { - logger(Logging::WARNING) << "Couldn't parse request: \"" << req.getBody() << "\""; - makeJsonParsingErrorResponse(jsonRpcResponse); - resp.setStatus(CryptoNote::HttpResponse::STATUS_200); - resp.setBody(jsonRpcResponse.toString()); - return; - } - - processJsonRpcRequest(jsonRpcRequest, jsonRpcResponse); - - std::stringstream jsonOutputStream; - jsonOutputStream << jsonRpcResponse; - - resp.setStatus(CryptoNote::HttpResponse::STATUS_200); - resp.setBody(jsonOutputStream.str()); - - } else { - logger(Logging::WARNING) << "Requested url \"" << req.getUrl() << "\" is not found"; - resp.setStatus(CryptoNote::HttpResponse::STATUS_404); - return; - } - } catch (std::exception& e) { - logger(Logging::WARNING) << "Error while processing http request: " << e.what(); - resp.setStatus(CryptoNote::HttpResponse::STATUS_500); - } -} - -void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { +void PaymentServiceJsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { try { prepareJsonResponse(req, resp); @@ -123,19 +62,82 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: serialize(sendResp, outputSerializer); } else if (method == "get_address") { + GetAddressRequest getAddrReq; GetAddressResponse getAddrResp; - std::error_code ec = service.getAddress(getAddrResp.address); + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(getAddrReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.getAddress(getAddrReq.index, getAddrResp.address); if (ec) { makeErrorResponse(ec, resp); return; } serialize(getAddrResp, outputSerializer); + } else if (method == "create_address") { + CreateAddressResponse createAddrResp; + + std::error_code ec = service.createAddress(createAddrResp.address); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(createAddrResp, outputSerializer); + } else if (method == "get_address_count") { + GetAddressCountResponse addressCountResp; + + std::error_code ec = service.getAddressCount(addressCountResp.count); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(addressCountResp, outputSerializer); + } else if (method == "delete_address") { + DeleteAddressRequest delAddrReq; + DeleteAddressResponse delAddrResp; + + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(delAddrReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec = service.deleteAddress(delAddrReq.address); + if (ec) { + makeErrorResponse(ec, resp); + return; + } + + serialize(delAddrResp, outputSerializer); } else if (method == "get_actual_balance") { + GetActualBalanceRequest actualReq; GetActualBalanceResponse actualResp; - std::error_code ec = service.getActualBalance(actualResp.actualBalance); + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(actualReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec; + if (actualReq.address == "") { + ec = service.getActualBalance(actualResp.actualBalance); + } else { + ec = service.getActualBalance(actualReq.address, actualResp.actualBalance); + } + if (ec) { makeErrorResponse(ec, resp); return; @@ -143,9 +145,24 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: serialize(actualResp, outputSerializer); } else if (method == "get_pending_balance") { + GetPendingBalanceRequest pendingReq; GetPendingBalanceResponse pendingResp; - std::error_code ec = service.getPendingBalance(pendingResp.pendingBalance); + //XXX: refactor it when migrate to different exception types in different subsystems! + try { + serialize(pendingReq, inputSerializer); + } catch (std::exception&) { + makeGenericErrorReponse(resp, "Invalid Request", -32600); + return; + } + + std::error_code ec; + if (pendingReq.address == "") { + ec = service.getPendingBalance(pendingResp.pendingBalance); + } else { + ec = service.getPendingBalance(pendingReq.address, pendingResp.pendingBalance); + } + if (ec) { makeErrorResponse(ec, resp); return; @@ -184,7 +201,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - CryptoNote::TransactionId txId; + size_t txId; std::error_code ec = service.getTransactionByTransferId(getReq.transferId, txId); getResp.transactionid = txId; if (ec) { @@ -224,8 +241,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: return; } - std::error_code ec = service.listTransactions(static_cast(listReq.startingTransactionId), - listReq.maxTransactionCount, listResp.transactions); + std::error_code ec = service.listTransactions(static_cast(listReq.startingTransactionId), listReq.maxTransactionCount, listResp.transactions); if (ec) { makeErrorResponse(ec, resp); return; @@ -266,7 +282,7 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: WalletService::IncomingPayments payments; std::error_code ec = service.getIncomingPayments(getReq.payments, payments); if (ec) { - if (ec == make_error_code(PaymentService::error::REQUEST_ERROR)) { + if (ec == make_error_code(std::errc::argument_out_of_domain)) { makeGenericErrorReponse(resp, "Invalid Request", -32600); } else { makeErrorResponse(ec, resp); @@ -296,108 +312,9 @@ void JsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common:: logger(Logging::WARNING) << "Wrong request came"; makeGenericErrorReponse(resp, "Invalid Request", -32600); } catch (std::exception& e) { - logger(Logging::WARNING) << "Error occured while processing JsonRpc request"; + logger(Logging::WARNING) << "Error occurred while processing JsonRpc request: " << e.what(); makeGenericErrorReponse(resp, e.what()); } } -void JsonRpcServer::prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp) { - using Common::JsonValue; - - if (req.contains("id")) { - resp.insert("id", req("id")); - } - - resp.insert("jsonrpc", "2.0"); -} - -void JsonRpcServer::makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp) { - using Common::JsonValue; - - JsonValue error(JsonValue::OBJECT); - - JsonValue code; - code = static_cast(-32000); //Application specific error code - - JsonValue message; - message = ec.message(); - - JsonValue data(JsonValue::OBJECT); - JsonValue appCode; - appCode = static_cast(ec.value()); - data.insert("application_code", appCode); - - error.insert("code", code); - error.insert("message", message); - error.insert("data", data); - - resp.insert("error", error); -} - -void JsonRpcServer::makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode) { - using Common::JsonValue; - - JsonValue error(JsonValue::OBJECT); - - JsonValue code; - code = static_cast(errorCode); - - std::string msg; - if (what) { - msg = what; - } else { - msg = "Unknown application error"; - } - - JsonValue message; - message = msg; - - error.insert("code", code); - error.insert("message", message); - - resp.insert("error", error); - -} - -void JsonRpcServer::makeMethodNotFoundResponse(Common::JsonValue& resp) { - using Common::JsonValue; - - JsonValue error(JsonValue::OBJECT); - - JsonValue code; - code = static_cast(-32601); //ambigous declaration of JsonValue::operator= (between int and JsonValue) - - JsonValue message; - message = "Method not found"; - - error.insert("code", code); - error.insert("message", message); - - resp.insert("error", error); -} - -void JsonRpcServer::fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp) { - resp.insert("result", v); -} - -void JsonRpcServer::makeJsonParsingErrorResponse(Common::JsonValue& resp) { - using Common::JsonValue; - - resp = JsonValue(JsonValue::OBJECT); - resp.insert("jsonrpc", "2.0"); - resp.insert("id", nullptr); - - JsonValue error(JsonValue::OBJECT); - JsonValue code; - code = static_cast(-32700); //ambigous declaration of JsonValue::operator= (between int and JsonValue) - - JsonValue message; - message = "Parse error"; - - error.insert("code", code); - error.insert("message", message); - - resp.insert("error", error); -} - } diff --git a/src/PaymentGate/PaymentServiceJsonRpcServer.h b/src/PaymentGate/PaymentServiceJsonRpcServer.h new file mode 100644 index 0000000000..8e90b8d899 --- /dev/null +++ b/src/PaymentGate/PaymentServiceJsonRpcServer.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "JsonRpcServer/JsonRpcServer.h" + +namespace PaymentService { + +class WalletService; + +class PaymentServiceJsonRpcServer : public CryptoNote::JsonRpcServer { +public: + PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); + PaymentServiceJsonRpcServer(const PaymentServiceJsonRpcServer&) = delete; + +protected: + virtual void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) override; + +private: + WalletService& service; + Logging::LoggerRef logger; +}; + +} //namespace PaymentService diff --git a/src/payment_service/WalletFactory.cpp b/src/PaymentGate/WalletFactory.cpp old mode 100644 new mode 100755 similarity index 79% rename from src/payment_service/WalletFactory.cpp rename to src/PaymentGate/WalletFactory.cpp index 590960fa3b..288393e9cc --- a/src/payment_service/WalletFactory.cpp +++ b/src/PaymentGate/WalletFactory.cpp @@ -17,9 +17,9 @@ #include "WalletFactory.h" -#include "node_rpc_proxy/NodeRpcProxy.h" -#include "wallet/Wallet.h" -#include "cryptonote_core/Currency.h" +#include "NodeRpcProxy/NodeRpcProxy.h" +#include "Wallet/WalletGreen.h" +#include "CryptoNoteCore/Currency.h" #include #include @@ -34,8 +34,8 @@ WalletFactory::WalletFactory() { WalletFactory::~WalletFactory() { } -CryptoNote::IWallet* WalletFactory::createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node) { - CryptoNote::Wallet* wallet = new CryptoNote::Wallet(currency, node); +CryptoNote::IWallet* WalletFactory::createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node, System::Dispatcher& dispatcher) { + CryptoNote::IWallet* wallet = new CryptoNote::WalletGreen(dispatcher, currency, node); return wallet; } diff --git a/src/payment_service/WalletFactory.h b/src/PaymentGate/WalletFactory.h similarity index 91% rename from src/payment_service/WalletFactory.h rename to src/PaymentGate/WalletFactory.h index 9df4b19e26..1a37e30c86 100644 --- a/src/payment_service/WalletFactory.h +++ b/src/PaymentGate/WalletFactory.h @@ -19,6 +19,7 @@ #include "IWallet.h" #include "INode.h" +#include #include #include @@ -31,7 +32,7 @@ namespace PaymentService { class WalletFactory { public: - static CryptoNote::IWallet* createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node); + static CryptoNote::IWallet* createWallet(const CryptoNote::Currency& currency, CryptoNote::INode& node, System::Dispatcher& dispatcher); private: WalletFactory(); ~WalletFactory(); diff --git a/src/payment_service/WalletService.cpp b/src/PaymentGate/WalletService.cpp old mode 100644 new mode 100755 similarity index 52% rename from src/payment_service/WalletService.cpp rename to src/PaymentGate/WalletService.cpp index 0d7b0de0e1..68dfaf8892 --- a/src/payment_service/WalletService.cpp +++ b/src/PaymentGate/WalletService.cpp @@ -17,15 +17,6 @@ #include "WalletService.h" -#include "WalletServiceErrorCodes.h" -#include "JsonRpcMessages.h" -#include "WalletFactory.h" -#include "NodeFactory.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "crypto/crypto.h" -#include "wallet/LegacyKeysImporter.h" -#include "Common/util.h" #include #include @@ -42,6 +33,21 @@ #include #endif +#include +#include +#include "Common/Util.h" + +#include "crypto/crypto.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/TransactionExtra.h" + +#include "PaymentServiceJsonRpcMessages.h" +#include "WalletFactory.h" +#include "NodeFactory.h" + +#include "Wallet/LegacyKeysImporter.h" + namespace { void addPaymentIdToExtra(const std::string& paymentId, std::string& extra) { @@ -116,7 +122,7 @@ bool deleteFile(const std::string& filename) { } void replaceWalletFiles(const std::string &path, const std::string &tempFilePath) { - tools::replace_file(tempFilePath, path); + Tools::replace_file(tempFilePath, path); } } @@ -137,12 +143,7 @@ void createWalletFile(std::fstream& walletFile, const std::string& filename) { } void saveWallet(CryptoNote::IWallet* wallet, std::fstream& walletFile, bool saveDetailed = true, bool saveCache = true) { - WalletSaveObserver saveObserver; - wallet->addObserver(&saveObserver); wallet->save(walletFile, saveDetailed, saveCache); - saveObserver.waitForSaveEnd(); - wallet->removeObserver(&saveObserver); - walletFile.flush(); } @@ -162,13 +163,13 @@ void secureSaveWallet(CryptoNote::IWallet* wallet, const std::string& path, bool replaceWalletFiles(path, tempFilePath); } -void generateNewWallet(CryptoNote::Currency ¤cy, const Configuration &conf, Logging::ILogger& logger) { +void generateNewWallet(const CryptoNote::Currency ¤cy, const WalletConfiguration &conf, Logging::ILogger& logger, System::Dispatcher& dispatcher) { Logging::LoggerRef log(logger, "generateNewWallet"); CryptoNote::INode* nodeStub = NodeFactory::createNodeStub(); std::unique_ptr nodeGuard(nodeStub); - CryptoNote::IWallet* wallet = WalletFactory::createWallet(currency, *nodeStub); + CryptoNote::IWallet* wallet = WalletFactory::createWallet(currency, *nodeStub, dispatcher); std::unique_ptr walletGuard(wallet); log(Logging::INFO) << "Generating new wallet"; @@ -176,24 +177,19 @@ void generateNewWallet(CryptoNote::Currency ¤cy, const Configuration &conf std::fstream walletFile; createWalletFile(walletFile, conf.walletFile); - WalletLoadObserver loadObserver; - wallet->addObserver(&loadObserver); - - wallet->initAndGenerate(conf.walletPassword); + wallet->initialize(conf.walletPassword); + auto address = wallet->createAddress(); - loadObserver.waitForLoadEnd(); - wallet->removeObserver(&loadObserver); - - log(Logging::INFO) << "New wallet is generated. Address: " << wallet->getAddress(); + log(Logging::INFO) << "New wallet is generated. Address: " << address; saveWallet(wallet, walletFile, false, false); log(Logging::INFO) << "Wallet is saved"; } -void importLegacyKeys(const Configuration& conf) { +void importLegacyKeys(const std::string &legacyKeysFile, const WalletConfiguration &conf) { std::stringstream archive; - CryptoNote::importLegacyKeys(conf.importKeys, conf.walletPassword, archive); + CryptoNote::importLegacyKeys(legacyKeysFile, conf.walletPassword, archive); std::fstream walletFile; createWalletFile(walletFile, conf.walletFile); @@ -204,22 +200,23 @@ void importLegacyKeys(const Configuration& conf) { } WalletService::WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, - const Configuration& conf, Logging::ILogger& logger) : + const WalletConfiguration& conf, Logging::ILogger& logger) : config(conf), inited(false), - sendObserver(sys), - logger(logger, "WaleltService"), + logger(logger, "WalletService"), txIdIndex(boost::get<0>(paymentsCache)), - paymentIdIndex(boost::get<1>(paymentsCache)) + paymentIdIndex(boost::get<1>(paymentsCache)), + dispatcher(sys), + refreshContext(dispatcher) { - wallet.reset(WalletFactory::createWallet(currency, node)); + wallet.reset(WalletFactory::createWallet(currency, node, dispatcher)); } WalletService::~WalletService() { if (wallet) { if (inited) { - wallet->removeObserver(&sendObserver); - wallet->removeObserver(this); + wallet->stop(); + refreshContext.wait(); wallet->shutdown(); } } @@ -227,10 +224,8 @@ WalletService::~WalletService() { void WalletService::init() { loadWallet(); - loadPaymentsCache(); - - wallet->addObserver(&sendObserver); - wallet->addObserver(this); + loadPaymentsCacheAndTransferIndices(); + refreshContext.spawn([this] { refresh(); }); inited = true; } @@ -249,30 +244,23 @@ void WalletService::loadWallet() { logger(Logging::INFO) << "Loading wallet"; - WalletLoadObserver loadObserver; - wallet->addObserver(&loadObserver); - - wallet->initAndLoad(inputWalletFile, config.walletPassword); + wallet->load(inputWalletFile, config.walletPassword); - loadObserver.waitForLoadEnd(); - - wallet->removeObserver(&loadObserver); - - logger(Logging::INFO) << "Wallet loading is finished. Address: " << wallet->getAddress(); + logger(Logging::INFO) << "Wallet loading is finished."; } -void WalletService::loadPaymentsCache() { +void WalletService::loadPaymentsCacheAndTransferIndices() { size_t txCount = wallet->getTransactionCount(); + transfersIndices.resize(1); + transfersIndices[0] = 0; logger(Logging::DEBUGGING) << "seeking for payments among " << txCount << " transactions"; for (size_t id = 0; id < txCount; ++id) { - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(id, tx)) { - logger(Logging::DEBUGGING) << "tx " << id << " doesn't exist"; - continue; - } + CryptoNote::WalletTransaction tx = wallet->getTransaction(id); + transfersIndices.push_back(transfersIndices[id] + wallet->getTransactionTransferCount(id)); + if (tx.totalAmount < 0) { logger(Logging::DEBUGGING) << "tx " << id << " has negative amount"; continue; @@ -280,14 +268,14 @@ void WalletService::loadPaymentsCache() { std::vector extraVector(tx.extra.begin(), tx.extra.end()); - crypto::hash paymentId; + Crypto::Hash paymentId; if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { logger(Logging::DEBUGGING) << "tx " << id << " has no payment id"; continue; } logger(Logging::DEBUGGING) << "transaction " << id << " has been inserted with payment id " << paymentId; - insertTransaction(id, paymentId); + insertTransaction(id, paymentId, tx.blockHeight != CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT); } } @@ -296,7 +284,7 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req logger(Logging::DEBUGGING) << "Send transaction request came"; try { - std::vector transfers; + std::vector transfers; makeTransfers(req.destinations, transfers); std::string extra; @@ -304,19 +292,12 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req addPaymentIdToExtra(req.paymentId, extra); } - CryptoNote::TransactionId txId = wallet->sendTransaction(transfers, req.fee, extra, req.mixin, req.unlockTime); - if (txId == CryptoNote::INVALID_TRANSACTION_ID) { + size_t txId = wallet->transfer(transfers, req.fee, req.mixin, extra, req.unlockTime); + if (txId == CryptoNote::WALLET_INVALID_TRANSACTION_ID) { logger(Logging::WARNING) << "Unable to send transaction"; throw std::runtime_error("Error occured while sending transaction"); } - std::error_code ec; - sendObserver.waitForTransactionFinished(txId, ec); - - if (ec) { - return ec; - } - resp.transactionId = txId; } catch (std::system_error& x) { logger(Logging::WARNING) << "Error while sending transaction: " << x.what(); @@ -326,7 +307,7 @@ std::error_code WalletService::sendTransaction(const SendTransactionRequest& req return std::error_code(); } -void WalletService::makeTransfers(const std::vector& destinations, std::vector& transfers) { +void WalletService::makeTransfers(const std::vector& destinations, std::vector& transfers) { transfers.reserve(destinations.size()); for (auto dest: destinations) { @@ -334,11 +315,11 @@ void WalletService::makeTransfers(const std::vectorgetAddress(); + address = wallet->getAddress(index); } catch (std::system_error& x) { logger(Logging::WARNING) << "Error while getting address: " << x.what(); return x.code(); @@ -347,11 +328,69 @@ std::error_code WalletService::getAddress(std::string& address) { return std::error_code(); } +std::error_code WalletService::getAddressCount(std::size_t& count) { + logger(Logging::DEBUGGING) << "Get address count request came"; + count = wallet->getAddressCount(); + return std::error_code(); +} + +std::error_code WalletService::createAddress(std::string& address) { + logger(Logging::DEBUGGING) << "Create address request came"; + + try { + address = wallet->createAddress(); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Error while creating address: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::deleteAddress(const std::string& address) { + logger(Logging::DEBUGGING) << "Delete address request came"; + + try { + wallet->deleteAddress(address); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Error while deleting address: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getActualBalance(const std::string& address, uint64_t& actualBalance) { + logger(Logging::DEBUGGING) << "Get actual balance for address: " << address << " request came"; + + try { + actualBalance = wallet->getActualBalance(address); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get actual balance: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + +std::error_code WalletService::getPendingBalance(const std::string& address, uint64_t& pendingBalance) { + logger(Logging::DEBUGGING) << "Get pending balance for address: " << address <<" request came"; + + try { + pendingBalance = wallet->getPendingBalance(address); + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get pending balance: " << x.what(); + return x.code(); + } + + return std::error_code(); +} + std::error_code WalletService::getActualBalance(uint64_t& actualBalance) { logger(Logging::DEBUGGING) << "Get actual balance request came"; try { - actualBalance = wallet->actualBalance(); + actualBalance = wallet->getActualBalance(); } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get actual balance: " << x.what(); return x.code(); @@ -364,7 +403,7 @@ std::error_code WalletService::getPendingBalance(uint64_t& pendingBalance) { logger(Logging::DEBUGGING) << "Get pending balance request came"; try { - pendingBalance = wallet->pendingBalance(); + pendingBalance = wallet->getPendingBalance(); } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get pending balance: " << x.what(); return x.code(); @@ -387,43 +426,55 @@ std::error_code WalletService::getTransactionsCount(uint64_t& txCount) { } std::error_code WalletService::getTransfersCount(uint64_t& trCount) { - logger(Logging::DEBUGGING) << "Get get transfers count request came"; - - try { - trCount = wallet->getTransferCount(); - } catch (std::system_error& x) { - logger(Logging::WARNING) << "Unable to get transfers count: " << x.what(); - return x.code(); - } - + logger(Logging::DEBUGGING) << "Get transfers count request came"; + trCount = static_cast(transfersIndices.back()); return std::error_code(); } -std::error_code WalletService::getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction) { +std::error_code WalletService::getTransactionByTransferId(size_t transferId, size_t& transactionId) { logger(Logging::DEBUGGING) << "getTransactionByTransferId request came"; - try { - transaction = wallet->findTransactionByTransferId(transfer); - } catch (std::system_error& x) { - logger(Logging::WARNING) << "Unable to get transaction id by transfer id count: " << x.what(); - return x.code(); + if (transferId >= transfersIndices.back()) { + logger(Logging::WARNING) << "Transfer ID:" << transferId<<" is out of domain"; + return std::make_error_code(std::errc::argument_out_of_domain); } + auto nextTxId = std::lower_bound(transfersIndices.begin(), transfersIndices.end(), transferId); + transactionId = (nextTxId - transfersIndices.begin()) - 1; + return std::error_code(); } -std::error_code WalletService::getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo) { +void WalletService::fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo) { + rpcInfo.firstTransferId = transfersIndices[txId]; + rpcInfo.transferCount = wallet->getTransactionTransferCount(txId); + rpcInfo.totalAmount = tx.totalAmount; + rpcInfo.fee = tx.fee; + rpcInfo.blockHeight = tx.blockHeight; + rpcInfo.timestamp = tx.timestamp; + rpcInfo.extra = Common::toHex(tx.extra.data(), tx.extra.size()); + rpcInfo.hash = Common::podToHex(tx.hash); + for (size_t transferId = 0; transferId < rpcInfo.transferCount; ++transferId) { + auto transfer = wallet->getTransactionTransfer(txId, transferId); + TransferRpcInfo rpcTransfer{ transfer.address, transfer.amount }; + rpcInfo.transfers.push_back(rpcTransfer); + } +} + +std::error_code WalletService::getTransaction(size_t txId, bool& found, TransactionRpcInfo& rpcInfo) { logger(Logging::DEBUGGING) << "getTransaction request came"; + found = false; try { - CryptoNote::TransactionInfo txInfo; - - found = wallet->getTransaction(txId, txInfo); - if (!found) { - return std::error_code(); + auto tx = wallet->getTransaction(txId); + if (txId + 1 >= transfersIndices.size()) { + logger(Logging::WARNING) << "Unable to get transaction " << txId << ": argument out of domain."; + return std::make_error_code(std::errc::argument_out_of_domain); } - fillTransactionRpcInfo(txInfo, rpcInfo); + fillTransactionRpcInfo(txId, tx, rpcInfo); + + found = true; } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get transaction: " << x.what(); return x.code(); @@ -432,19 +483,7 @@ std::error_code WalletService::getTransaction(CryptoNote::TransactionId txId, bo return std::error_code(); } -void WalletService::fillTransactionRpcInfo(const CryptoNote::TransactionInfo& txInfo, TransactionRpcInfo& rpcInfo) { - rpcInfo.firstTransferId = txInfo.firstTransferId; - rpcInfo.transferCount = txInfo.transferCount; - rpcInfo.totalAmount = txInfo.totalAmount; - rpcInfo.fee = txInfo.fee; - rpcInfo.isCoinbase = txInfo.isCoinbase; - rpcInfo.blockHeight = txInfo.blockHeight; - rpcInfo.timestamp = txInfo.timestamp; - rpcInfo.extra = Common::toHex(txInfo.extra.data(), txInfo.extra.size()); - rpcInfo.hash = Common::podToHex(txInfo.hash); -} - -std::error_code WalletService::listTransactions(CryptoNote::TransactionId startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo) { +std::error_code WalletService::listTransactions(size_t startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo) { logger(Logging::DEBUGGING) << "listTransactions request came"; if (maxTxCount == 0) { @@ -453,22 +492,20 @@ std::error_code WalletService::listTransactions(CryptoNote::TransactionId starti } try { - CryptoNote::TransactionId endTxId; - if (startingTxId > std::numeric_limits::max() - static_cast(maxTxCount)) { - endTxId = static_cast(wallet->getTransactionCount()); + size_t endTxId; + if (startingTxId > std::numeric_limits::max() - static_cast(maxTxCount)) { + endTxId = static_cast(wallet->getTransactionCount()); } else { - endTxId = startingTxId + static_cast(maxTxCount); - endTxId = std::min(endTxId, static_cast(wallet->getTransactionCount())); + endTxId = startingTxId + static_cast(maxTxCount); + endTxId = std::min(endTxId, static_cast(wallet->getTransactionCount())); } txsRpcInfo.resize(endTxId - startingTxId); for (auto txId = startingTxId; txId < endTxId; ++txId) { - CryptoNote::TransactionInfo txInfo; assert(txId < wallet->getTransactionCount()); - wallet->getTransaction(txId, txInfo); - - fillTransactionRpcInfo(txInfo, txsRpcInfo[txId - startingTxId]); + auto tx = wallet->getTransaction(txId); + fillTransactionRpcInfo(txId, tx, txsRpcInfo[txId - startingTxId]); } } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to list transaction: " << x.what(); @@ -478,18 +515,23 @@ std::error_code WalletService::listTransactions(CryptoNote::TransactionId starti return std::error_code(); } -std::error_code WalletService::getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo) { +std::error_code WalletService::getTransfer(size_t globalTransferId, bool& found, TransferRpcInfo& rpcInfo) { logger(Logging::DEBUGGING) << "getTransfer request came"; - + found = false; try { - CryptoNote::Transfer transfer; + size_t txId = (std::upper_bound(transfersIndices.begin(), transfersIndices.end(), globalTransferId) - transfersIndices.begin()) - 1; + size_t fakeTxId = transfersIndices.size() - 1; - found = wallet->getTransfer(txId, transfer); - if (!found) { + if (txId == fakeTxId) { return std::error_code(); } - fillTransferRpcInfo(transfer, rpcInfo); + auto transferId = globalTransferId - transfersIndices[txId]; + auto transfer = wallet->getTransactionTransfer(txId, transferId); + + rpcInfo.address = transfer.address; + rpcInfo.amount = transfer.amount; + found = true; } catch (std::system_error& x) { logger(Logging::WARNING) << "Unable to get transfer: " << x.what(); return x.code(); @@ -498,106 +540,81 @@ std::error_code WalletService::getTransfer(CryptoNote::TransferId txId, bool& fo return std::error_code(); } -void WalletService::fillTransferRpcInfo(const CryptoNote::Transfer& transfer, TransferRpcInfo& rpcInfo) { - rpcInfo.address = transfer.address; - rpcInfo.amount = transfer.amount; -} - std::error_code WalletService::getIncomingPayments(const std::vector& payments, IncomingPayments& result) { logger(Logging::DEBUGGING) << "getIncomingPayments request came"; - for (const std::string& payment: payments) { - if (!checkPaymentId(payment)) { - return make_error_code(error::REQUEST_ERROR); - } + try { + for (const std::string& payment: payments) { - std::string paymentString = payment; - std::transform(paymentString.begin(), paymentString.end(), paymentString.begin(), ::tolower); + if (!checkPaymentId(payment)) { + return make_error_code(std::errc::argument_out_of_domain); + } - auto pair = paymentIdIndex.equal_range(paymentString); + std::string paymentString = payment; + std::transform(paymentString.begin(), paymentString.end(), paymentString.begin(), ::tolower); + auto pair = paymentIdIndex.equal_range(paymentString); - for (auto it = pair.first; it != pair.second; ++it) { - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(it->transactionId, tx)) { - continue; - } + for (auto it = pair.first; it != pair.second; ++it) { + auto tx = wallet->getTransaction(it->transactionId); - std::string hashString = Common::podToHex(tx.hash); + std::string hashString = Common::podToHex(tx.hash); - PaymentDetails details; - details.txHash = std::move(hashString); - details.amount = static_cast(tx.totalAmount); - details.blockHeight = tx.blockHeight; - details.unlockTime = 0; //TODO: this is stub. fix it when wallet api allows to retrieve it + PaymentDetails details; + details.txHash = std::move(hashString); + details.amount = static_cast(tx.totalAmount); + details.blockHeight = tx.blockHeight; + details.unlockTime = tx.unlockTime; - result[it->paymentId].push_back(std::move(details)); + result[it->paymentId].push_back(std::move(details)); + } } + } catch (std::system_error& x) { + logger(Logging::WARNING) << "Unable to get payments: " << x.what(); + return x.code(); } return std::error_code(); } -void WalletService::externalTransactionCreated(CryptoNote::TransactionId transactionId) { - logger(Logging::DEBUGGING) << "external transaction created " << transactionId; - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(transactionId, tx)) { - return; - } - - if (tx.totalAmount < 0) { - return; - } - - logger(Logging::DEBUGGING) << "external transaction created " << transactionId << " extra size: " << tx.extra.size(); - std::vector extraVector(tx.extra.begin(), tx.extra.end()); - crypto::hash paymentId; - if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; - return; - } - - insertTransaction(transactionId, paymentId); - - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been added to payments cache"; -} - -void WalletService::transactionUpdated(CryptoNote::TransactionId transactionId) { - CryptoNote::TransactionInfo tx; - if (!wallet->getTransaction(transactionId, tx)) { - return; - } - - if (tx.totalAmount < 0) { - return; - } - - if (tx.blockHeight != CryptoNote::UNCONFIRMED_TRANSACTION_HEIGHT) { - auto it = txIdIndex.find(transactionId); - if (it != txIdIndex.end()) { - return; - } - - //insert confirmed transaction - std::vector extraVector(tx.extra.begin(), tx.extra.end()); - crypto::hash paymentId; - if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; - return; - } - - insertTransaction(transactionId, paymentId); - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been inserted to payments cache"; - } else { - auto it = txIdIndex.find(transactionId); - if (it != txIdIndex.end()) { - txIdIndex.erase(it); - logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been erased from payments cache"; +void WalletService::refresh() { + try { + for (;;) { + auto event = wallet->getEvent(); + if (event.type == CryptoNote::TRANSACTION_CREATED || event.type == CryptoNote::TRANSACTION_UPDATED) { + size_t transactionId; + if (event.type == CryptoNote::TRANSACTION_CREATED) { + transactionId = event.transactionCreated.transactionIndex; + transfersIndices.push_back(transfersIndices[transactionId] + wallet->getTransactionTransferCount(transactionId)); + } else { + transactionId = event.transactionUpdated.transactionIndex; + } + + auto tx = wallet->getTransaction(transactionId); + logger(Logging::DEBUGGING) << "Transaction updated " << transactionId << " extra size: " << tx.extra.size(); + if (tx.totalAmount < 0) { + continue; + } + + std::vector extraVector(tx.extra.begin(), tx.extra.end()); + Crypto::Hash paymentId; + if (!CryptoNote::getPaymentIdFromTxExtra(extraVector, paymentId)) { + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has no payment id"; + continue; + } + + insertTransaction(transactionId, paymentId, tx.blockHeight != CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT); + logger(Logging::DEBUGGING) << "transaction " << transactionId << " has been added to payments cache"; + } } + } catch (std::system_error& e) { + logger(Logging::TRACE) << "refresh is stopped: " << e.what(); + } catch (std::exception& e) { + logger(Logging::WARNING) << "exception thrown in refresh(): " << e.what(); } } -void WalletService::insertTransaction(CryptoNote::TransactionId id, const crypto::hash& paymentIdBin) { - paymentsCache.insert(PaymentItem{ Common::podToHex(paymentIdBin), id }); +void WalletService::insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed) { + paymentsCache.insert(PaymentItem{ Common::podToHex(paymentIdBin), id, confirmed}); } } //namespace PaymentService diff --git a/src/payment_service/WalletService.h b/src/PaymentGate/WalletService.h old mode 100644 new mode 100755 similarity index 58% rename from src/payment_service/WalletService.h rename to src/PaymentGate/WalletService.h index d70175f6be..6fe3791cf9 --- a/src/payment_service/WalletService.h +++ b/src/PaymentGate/WalletService.h @@ -17,13 +17,13 @@ #pragma once +#include #include -#include "PaymentServiceConfiguration.h" +#include #include "IWallet.h" #include "INode.h" -#include "WalletObservers.h" -#include "cryptonote_core/Currency.h" -#include "JsonRpcMessages.h" +#include "CryptoNoteCore/Currency.h" +#include "PaymentServiceJsonRpcMessages.h" #undef ERROR //TODO: workaround for windows build. fix it #include "Logging/LoggerRef.h" @@ -41,14 +41,18 @@ struct TransferDestination; struct TransactionRpcInfo; struct TransferRpcInfo; -void importLegacyKeys(const Configuration& conf); -void generateNewWallet (CryptoNote::Currency ¤cy, const Configuration &conf, Logging::ILogger &logger); +struct WalletConfiguration { + std::string walletFile; + std::string walletPassword; +}; + +void generateNewWallet(const CryptoNote::Currency ¤cy, const WalletConfiguration &conf, Logging::ILogger &logger, System::Dispatcher& dispatcher); -class WalletService : public CryptoNote::IWalletObserver { +class WalletService { public: typedef std::map > IncomingPayments; - explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const Configuration& conf, Logging::ILogger& logger); + explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const WalletConfiguration& conf, Logging::ILogger& logger); virtual ~WalletService(); void init(); @@ -56,34 +60,38 @@ class WalletService : public CryptoNote::IWalletObserver { std::error_code sendTransaction(const SendTransactionRequest& req, SendTransactionResponse& resp); std::error_code getIncomingPayments(const std::vector& payments, IncomingPayments& result); - std::error_code getAddress(std::string& address); + std::error_code getAddress(size_t index, std::string& address); + std::error_code getAddressCount(size_t& count); + std::error_code createAddress(std::string& address); + std::error_code deleteAddress(const std::string& address); + std::error_code getActualBalance(const std::string& address, uint64_t& actualBalance); + std::error_code getPendingBalance(const std::string& address, uint64_t& pendingBalance); std::error_code getActualBalance(uint64_t& actualBalance); std::error_code getPendingBalance(uint64_t& pendingBalance); std::error_code getTransactionsCount(uint64_t& txCount); std::error_code getTransfersCount(uint64_t& trCount); - std::error_code getTransactionByTransferId(CryptoNote::TransferId transfer, CryptoNote::TransactionId& transaction); - std::error_code getTransaction(CryptoNote::TransactionId txId, bool& found, TransactionRpcInfo& rpcInfo); - std::error_code listTransactions(CryptoNote::TransactionId startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo); - std::error_code getTransfer(CryptoNote::TransferId txId, bool& found, TransferRpcInfo& rpcInfo); + std::error_code getTransactionByTransferId(size_t transfer, size_t& transaction); + std::error_code getTransaction(size_t txId, bool& found, TransactionRpcInfo& rpcInfo); + std::error_code listTransactions(size_t startingTxId, uint32_t maxTxCount, std::vector& txsRpcInfo); + std::error_code getTransfer(size_t txId, bool& found, TransferRpcInfo& rpcInfo); private: - void loadWallet(); - void loadPaymentsCache(); - void insertTransaction(CryptoNote::TransactionId id, const crypto::hash& paymentIdBin); + void refresh(); - void makeTransfers(const std::vector& destinations, std::vector& transfers); - void fillTransactionRpcInfo(const CryptoNote::TransactionInfo& txInfo, TransactionRpcInfo& rpcInfo); - void fillTransferRpcInfo(const CryptoNote::Transfer& transfer, TransferRpcInfo& rpcInfo); + void loadWallet(); + void loadPaymentsCacheAndTransferIndices(); + void insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed); - virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId); - virtual void transactionUpdated(CryptoNote::TransactionId transactionId); + void fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo); + void makeTransfers(const std::vector& destinations, std::vector& transfers); struct PaymentItem { std::string paymentId; - CryptoNote::TransactionId transactionId; + size_t transactionId; + bool confirmed; }; - typedef boost::multi_index::hashed_unique TxIdIndex; + typedef boost::multi_index::hashed_unique TxIdIndex; typedef boost::multi_index::hashed_non_unique PaymentIndex; typedef boost::multi_index::multi_index_container< PaymentItem, @@ -93,12 +101,14 @@ class WalletService : public CryptoNote::IWalletObserver { > > PaymentsContainer; - std::unique_ptr wallet; + std::unique_ptr wallet; CryptoNote::INode* node; - const Configuration& config; + const WalletConfiguration& config; bool inited; - WalletTransactionSendObserver sendObserver; Logging::LoggerRef logger; + std::vector transfersIndices; + System::Dispatcher& dispatcher; + System::ContextGroup refreshContext; PaymentsContainer paymentsCache; PaymentsContainer::nth_index<0>::type& txIdIndex; diff --git a/src/payment_service/ConfigurationManager.cpp b/src/PaymentGateService/ConfigurationManager.cpp old mode 100644 new mode 100755 similarity index 96% rename from src/payment_service/ConfigurationManager.cpp rename to src/PaymentGateService/ConfigurationManager.cpp index 70b169b713..4f092d5a40 --- a/src/payment_service/ConfigurationManager.cpp +++ b/src/PaymentGateService/ConfigurationManager.cpp @@ -20,8 +20,8 @@ #include #include -#include "Common/command_line.h" -#include "Common/util.h" +#include "Common/CommandLine.h" +#include "Common/Util.h" namespace PaymentService { @@ -47,8 +47,8 @@ bool ConfigurationManager::init(int argc, char** argv) { ("local", "start with local node (remote is default)") ("testnet", "testnet mode"); - command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, tools::get_default_data_dir()); - command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(cmdGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); + command_line::add_arg(confGeneralOptions, command_line::arg_data_dir, Tools::getDefaultDataDirectory()); Configuration::initOptions(cmdGeneralOptions); Configuration::initOptions(confGeneralOptions); diff --git a/src/payment_service/ConfigurationManager.h b/src/PaymentGateService/ConfigurationManager.h old mode 100644 new mode 100755 similarity index 94% rename from src/payment_service/ConfigurationManager.h rename to src/PaymentGateService/ConfigurationManager.h index 5b3b2c24d2..d33bf1366e --- a/src/payment_service/ConfigurationManager.h +++ b/src/PaymentGateService/ConfigurationManager.h @@ -17,9 +17,9 @@ #pragma once -#include "cryptonote_core/CoreConfig.h" +#include "CryptoNoteCore/CoreConfig.h" #include "PaymentServiceConfiguration.h" -#include "p2p/NetNodeConfig.h" +#include "P2p/NetNodeConfig.h" #include "RpcNodeConfiguration.h" namespace PaymentService { diff --git a/src/PaymentGateService/PaymentGateService.cpp b/src/PaymentGateService/PaymentGateService.cpp new file mode 100755 index 0000000000..734bcef7d7 --- /dev/null +++ b/src/PaymentGateService/PaymentGateService.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "PaymentGateService.h" + +#include + +#include "Common/SignalHandler.h" +#include "InProcessNode/InProcessNode.h" +#include "Logging/LoggerRef.h" +#include "PaymentGate/PaymentServiceJsonRpcServer.h" + +#include "CryptoNoteCore/CoreConfig.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "P2p/NetNode.h" +#include + +#ifdef ERROR +#undef ERROR +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +using namespace PaymentService; + +void changeDirectory(const std::string& path) { + if (chdir(path.c_str())) { + throw std::runtime_error("Couldn't change directory to \'" + path + "\': " + strerror(errno)); + } +} + +void stopSignalHandler(PaymentGateService* pg) { + pg->stop(); +} + +bool PaymentGateService::init(int argc, char** argv) { + if (!config.init(argc, argv)) { + return false; + } + + logger.setMaxLevel(static_cast(config.gateConfiguration.logLevel)); + logger.addLogger(consoleLogger); + + Logging::LoggerRef log(logger, "main"); + + if (config.gateConfiguration.testnet) { + log(Logging::INFO) << "Starting in testnet mode"; + currencyBuilder.testnet(true); + } + + if (!config.gateConfiguration.serverRoot.empty()) { + changeDirectory(config.gateConfiguration.serverRoot); + log(Logging::INFO) << "Current working directory now is " << config.gateConfiguration.serverRoot; + } + + fileStream.open(config.gateConfiguration.logFile, std::ofstream::app); + + if (!fileStream) { + throw std::runtime_error("Couldn't open log file"); + } + + fileLogger.attachToStream(fileStream); + logger.addLogger(fileLogger); + + return true; +} + +WalletConfiguration PaymentGateService::getWalletConfig() const { + return WalletConfiguration{ + config.gateConfiguration.walletFile, + config.gateConfiguration.walletPassword + }; +} + +const CryptoNote::Currency PaymentGateService::getCurrency() { + return currencyBuilder.currency(); +} + +void PaymentGateService::run() { + + System::Dispatcher localDispatcher; + System::Event localStopEvent(localDispatcher); + + this->dispatcher = &localDispatcher; + this->stopEvent = &localStopEvent; + + Tools::SignalHandler::install(std::bind(&stopSignalHandler, this)); + + Logging::LoggerRef log(logger, "run"); + + if (config.startInprocess) { + runInProcess(log); + } else { + runRpcProxy(log); + } + + this->dispatcher = nullptr; + this->stopEvent = nullptr; +} + +void PaymentGateService::stop() { + Logging::LoggerRef log(logger, "stop"); + + log(Logging::INFO) << "Stop signal caught"; + + if (dispatcher != nullptr) { + dispatcher->remoteSpawn([&]() { + if (stopEvent != nullptr) { + stopEvent->set(); + } + }); + } +} + +void PaymentGateService::runInProcess(Logging::LoggerRef& log) { + log(Logging::INFO) << "Starting Payment Gate with local node"; + + CryptoNote::Currency currency = currencyBuilder.currency(); + CryptoNote::core core(currency, NULL, logger); + + CryptoNote::CryptoNoteProtocolHandler protocol(currency, *dispatcher, core, NULL, logger); + CryptoNote::NodeServer p2pNode(*dispatcher, protocol, logger); + + protocol.set_p2p_endpoint(&p2pNode); + core.set_cryptonote_protocol(&protocol); + + log(Logging::INFO) << "initializing p2pNode"; + if (!p2pNode.init(config.netNodeConfig)) { + throw std::runtime_error("Failed to init p2pNode"); + } + + log(Logging::INFO) << "initializing core"; + CryptoNote::MinerConfig emptyMiner; + core.init(config.coreConfig, emptyMiner, true); + + std::promise initPromise; + auto initFuture = initPromise.get_future(); + + std::unique_ptr node(new CryptoNote::InProcessNode(core, protocol)); + + node->init([&initPromise, &log](std::error_code ec) { + if (ec) { + log(Logging::INFO) << "Failed to init node: " << ec.message(); + } else { + log(Logging::INFO) << "node is inited successfully"; + } + + initPromise.set_value(ec); + }); + + auto ec = initFuture.get(); + if (ec) { + throw std::system_error(ec); + } + + log(Logging::INFO) << "Spawning p2p server"; + + System::Event p2pStarted(*dispatcher); + + System::Context<> context(*dispatcher, [&]() { + p2pStarted.set(); + p2pNode.run(); + }); + + p2pStarted.wait(); + + runWalletService(currency, *node); + + p2pNode.sendStopSignal(); + context.get(); + node->shutdown(); + core.deinit(); + p2pNode.deinit(); +} + +void PaymentGateService::runRpcProxy(Logging::LoggerRef& log) { + log(Logging::INFO) << "Starting Payment Gate with remote node"; + CryptoNote::Currency currency = currencyBuilder.currency(); + + std::unique_ptr node( + PaymentService::NodeFactory::createNode( + config.remoteNodeConfig.daemonHost, + config.remoteNodeConfig.daemonPort)); + + runWalletService(currency, *node); +} + +void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node) { + PaymentService::WalletConfiguration walletConfiguration{ + config.gateConfiguration.walletFile, + config.gateConfiguration.walletPassword + }; + + service = new PaymentService::WalletService(currency, *dispatcher, node, walletConfiguration, logger); + std::unique_ptr serviceGuard(service); + try { + service->init(); + } catch (std::exception& e) { + Logging::LoggerRef(logger, "run")(Logging::ERROR) << "Failed to init walletService reason: " << e.what(); + return; + } + + if (config.gateConfiguration.printAddresses) { + // print addresses and exit + size_t addressCount = 0; + service->getAddressCount(addressCount); + for (size_t i = 0; i < addressCount; ++i) { + std::string address; + if (service->getAddress(i, address) == std::error_code()) { + std::cout << "Address: " << address << std::endl; + } + } + } else { + PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger); + rpcServer.start(config.gateConfiguration.bindAddress, config.gateConfiguration.bindPort); + + try { + service->saveWallet(); + } catch (std::exception& ex) { + Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING) << "Couldn't save wallet: " << ex.what(); + } + } +} diff --git a/src/PaymentGateService/PaymentGateService.h b/src/PaymentGateService/PaymentGateService.h new file mode 100644 index 0000000000..f594f7db91 --- /dev/null +++ b/src/PaymentGateService/PaymentGateService.h @@ -0,0 +1,64 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ConfigurationManager.h" +#include "PaymentServiceConfiguration.h" + +#include "Logging/ConsoleLogger.h" +#include "Logging/LoggerGroup.h" +#include "Logging/StreamLogger.h" + +#include "PaymentGate/NodeFactory.h" +#include "PaymentGate/WalletService.h" + +class PaymentGateService { +public: + + PaymentGateService() : dispatcher(nullptr), stopEvent(nullptr), config(), service(nullptr), logger(), currencyBuilder(logger) { + } + + bool init(int argc, char** argv); + + const PaymentService::ConfigurationManager& getConfig() const { return config; } + PaymentService::WalletConfiguration getWalletConfig() const; + const CryptoNote::Currency getCurrency(); + + void run(); + void stop(); + + Logging::ILogger& getLogger() { return logger; } + +private: + + void runInProcess(Logging::LoggerRef& log); + void runRpcProxy(Logging::LoggerRef& log); + + void runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node); + + System::Dispatcher* dispatcher; + System::Event* stopEvent; + PaymentService::ConfigurationManager config; + PaymentService::WalletService* service; + CryptoNote::CurrencyBuilder currencyBuilder; + + Logging::LoggerGroup logger; + std::ofstream fileStream; + Logging::StreamLogger fileLogger; + Logging::ConsoleLogger consoleLogger; +}; diff --git a/src/payment_service/PaymentServiceConfiguration.cpp b/src/PaymentGateService/PaymentServiceConfiguration.cpp similarity index 89% rename from src/payment_service/PaymentServiceConfiguration.cpp rename to src/PaymentGateService/PaymentServiceConfiguration.cpp index f749750bc9..d9401fc87c 100644 --- a/src/payment_service/PaymentServiceConfiguration.cpp +++ b/src/PaymentGateService/PaymentServiceConfiguration.cpp @@ -34,6 +34,7 @@ Configuration::Configuration() { unregisterService = false; logFile = "payment_gate.log"; testnet = false; + printAddresses = false; logLevel = Logging::INFO; } @@ -45,12 +46,14 @@ void Configuration::initOptions(boost::program_options::options_description& des ("wallet-password,p", po::value(), "wallet password") ("generate-wallet,g", "generate new wallet file and exit") ("daemon,d", "run as daemon in Unix or as service in Windows") +#ifdef _WIN32 ("register-service", "register service and exit (Windows only)") ("unregister-service", "unregister service and exit (Windows only)") - ("import-keys,i", po::value(), "import legacy keys file and exit") +#endif ("log-file,l", po::value(), "log file") ("server-root", po::value(), "server root. The service will use it as working directory. Don't set it if don't want to change it") - ("log-level", po::value(), "log level"); + ("log-level", po::value(), "log level") + ("address", "print wallet addresses and exit"); } void Configuration::init(const boost::program_options::variables_map& options) { @@ -79,7 +82,7 @@ void Configuration::init(const boost::program_options::variables_map& options) { } if (options.count("log-level")) { - logLevel = options["log-level"].as(); + logLevel = options["log-level"].as(); if (logLevel > Logging::TRACE) { std::string error = "log-level option must be in " + std::to_string(Logging::FATAL) + ".." + std::to_string(Logging::TRACE) + " interval"; throw ConfigurationError(error.c_str()); @@ -110,12 +113,8 @@ void Configuration::init(const boost::program_options::variables_map& options) { generateNewWallet = true; } - if (options.count("import-keys")) { - importKeys = options["import-keys"].as(); - } - - if (!importKeys.empty() && generateNewWallet) { - throw ConfigurationError("It's impossible to use both \"import\" and \"generate-wallet\" at the same time"); + if (options.count("address")) { + printAddresses = true; } if (!registerService && !unregisterService) { diff --git a/src/payment_service/PaymentServiceConfiguration.h b/src/PaymentGateService/PaymentServiceConfiguration.h similarity index 96% rename from src/payment_service/PaymentServiceConfiguration.h rename to src/PaymentGateService/PaymentServiceConfiguration.h index 42316518e1..dc804fbaa4 100644 --- a/src/payment_service/PaymentServiceConfiguration.h +++ b/src/PaymentGateService/PaymentServiceConfiguration.h @@ -41,7 +41,6 @@ struct Configuration { std::string walletFile; std::string walletPassword; - std::string importKeys; std::string logFile; std::string serverRoot; @@ -50,8 +49,9 @@ struct Configuration { bool registerService; bool unregisterService; bool testnet; + bool printAddresses; - std::size_t logLevel; + size_t logLevel; }; } //namespace PaymentService diff --git a/src/payment_service/RpcNodeConfiguration.cpp b/src/PaymentGateService/RpcNodeConfiguration.cpp similarity index 100% rename from src/payment_service/RpcNodeConfiguration.cpp rename to src/PaymentGateService/RpcNodeConfiguration.cpp diff --git a/src/payment_service/RpcNodeConfiguration.h b/src/PaymentGateService/RpcNodeConfiguration.h similarity index 100% rename from src/payment_service/RpcNodeConfiguration.h rename to src/PaymentGateService/RpcNodeConfiguration.h diff --git a/src/PaymentGateService/main.cpp b/src/PaymentGateService/main.cpp new file mode 100644 index 0000000000..b807836328 --- /dev/null +++ b/src/PaymentGateService/main.cpp @@ -0,0 +1,335 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include +#include + +#include + +#include "PaymentGateService.h" +#include "version.h" + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#define SERVICE_NAME "Payment Gate" + +PaymentGateService* ppg; + +#ifdef WIN32 +SERVICE_STATUS_HANDLE serviceStatusHandle; + +std::string GetLastErrorMessage(DWORD errorMessageID) +{ + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, 0, (LPSTR)&messageBuffer, 0, NULL); + + std::string message(messageBuffer, size); + + LocalFree(messageBuffer); + + return message; +} + +void __stdcall serviceHandler(DWORD fdwControl) { + if (fdwControl == SERVICE_CONTROL_STOP) { + Logging::LoggerRef log(ppg->getLogger(), "serviceHandler"); + log(Logging::INFO) << "Stop signal caught"; + + SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_STOP_PENDING, 0, NO_ERROR, 0, 0, 0 }; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + ppg->stop(); + } +} + +void __stdcall serviceMain(DWORD dwArgc, char **lpszArgv) { + Logging::LoggerRef logRef(ppg->getLogger(), "WindowsService"); + + serviceStatusHandle = RegisterServiceCtrlHandler("PaymentGate", serviceHandler); + if (serviceStatusHandle == NULL) { + logRef(Logging::FATAL) << "Couldn't make RegisterServiceCtrlHandler call: " << GetLastErrorMessage(GetLastError()); + return; + } + + SERVICE_STATUS serviceStatus{ SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, 0, NO_ERROR, 0, 1, 3000 }; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { + logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + return; + } + + serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_RUNNING, SERVICE_ACCEPT_STOP, NO_ERROR, 0, 0, 0 }; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) != TRUE) { + logRef(Logging::FATAL) << "Couldn't make SetServiceStatus call: " << GetLastErrorMessage(GetLastError()); + return; + } + + try { + ppg->run(); + } catch (std::exception& ex) { + logRef(Logging::FATAL) << "Error occured: " << ex.what(); + } + + serviceStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_STOPPED, 0, NO_ERROR, 0, 0, 0 }; + SetServiceStatus(serviceStatusHandle, &serviceStatus); +} +#else +int daemonize() { + pid_t pid; + pid = fork(); + + if (pid < 0) + return pid; + + if (pid > 0) + return pid; + + if (setsid() < 0) + return -1; + + signal(SIGCHLD, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + pid = fork(); + + if (pid < 0) + return pid; + + if (pid > 0) + return pid; + + umask(0); + + return 0; +} +#endif + +int runDaemon() { +#ifdef WIN32 + + SERVICE_TABLE_ENTRY serviceTable[] { + { "Payment Gate", serviceMain }, + { NULL, NULL } + }; + + if (StartServiceCtrlDispatcher(serviceTable) != TRUE) { + return 1; + } + + return 0; + +#else + + int daemonResult = daemonize(); + if (daemonResult > 0) { + //parent + return 0; + } else if (daemonResult < 0) { + //error occured + return 1; + } + + ppg->run(); + + return 0; + +#endif +} + +int registerService() { +#ifdef WIN32 + Logging::LoggerRef logRef(ppg->getLogger(), "ServiceRegistrator"); + + char pathBuff[MAX_PATH]; + std::string modulePath; + SC_HANDLE scManager = NULL; + SC_HANDLE scService = NULL; + int ret = 0; + + for (;;) { + if (GetModuleFileName(NULL, pathBuff, ARRAYSIZE(pathBuff)) == 0) { + logRef(Logging::FATAL) << "GetModuleFileName failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + modulePath.assign(pathBuff); + + std::string moduleDir = modulePath.substr(0, modulePath.find_last_of('\\') + 1); + modulePath += " --config=" + moduleDir + "payment_service.conf -d"; + + scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); + if (scManager == NULL) { + logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + scService = CreateService(scManager, SERVICE_NAME, NULL, SERVICE_QUERY_STATUS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, modulePath.c_str(), NULL, NULL, NULL, NULL, NULL); + + if (scService == NULL) { + logRef(Logging::FATAL) << "CreateService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + logRef(Logging::INFO) << "Service is registered successfully"; + logRef(Logging::INFO) << "Please make sure " << moduleDir + "payment_service.conf" << " exists"; + break; + } + + if (scManager) { + CloseServiceHandle(scManager); + } + + if (scService) { + CloseServiceHandle(scService); + } + + return ret; +#else + return 0; +#endif +} + +int unregisterService() { +#ifdef WIN32 + Logging::LoggerRef logRef(ppg->getLogger(), "ServiceDeregistrator"); + + SC_HANDLE scManager = NULL; + SC_HANDLE scService = NULL; + SERVICE_STATUS ssSvcStatus = { }; + int ret = 0; + + for (;;) { + scManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (scManager == NULL) { + logRef(Logging::FATAL) << "OpenSCManager failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + scService = OpenService(scManager, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE); + if (scService == NULL) { + logRef(Logging::FATAL) << "OpenService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + if (ControlService(scService, SERVICE_CONTROL_STOP, &ssSvcStatus)) { + logRef(Logging::INFO) << "Stopping " << SERVICE_NAME; + Sleep(1000); + + while (QueryServiceStatus(scService, &ssSvcStatus)) { + if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { + logRef(Logging::INFO) << "Waiting..."; + Sleep(1000); + } else { + break; + } + } + + std::cout << std::endl; + if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) { + logRef(Logging::INFO) << SERVICE_NAME << " is stopped"; + } else { + logRef(Logging::FATAL) << SERVICE_NAME << " failed to stop" << std::endl; + } + } + + if (!DeleteService(scService)) { + logRef(Logging::FATAL) << "DeleteService failed with error: " << GetLastErrorMessage(GetLastError()); + ret = 1; + break; + } + + logRef(Logging::INFO) << SERVICE_NAME << " is removed"; + break; + } + + if (scManager) { + CloseServiceHandle(scManager); + } + + if (scService) { + CloseServiceHandle(scService); + } + + return ret; +#else + return 0; +#endif +} + +int main(int argc, char** argv) { + PaymentGateService pg; + ppg = &pg; + + try { + if (!pg.init(argc, argv)) { + return 0; //help message requested or so + } + + Logging::LoggerRef(pg.getLogger(), "main")(Logging::INFO) << "PaymentService " << " v" << PROJECT_VERSION_LONG; + + const auto& config = pg.getConfig(); + + if (config.gateConfiguration.generateNewWallet) { + System::Dispatcher d; + generateNewWallet(pg.getCurrency(), pg.getWalletConfig(), pg.getLogger(), d); + return 0; + } + + if (config.gateConfiguration.registerService) { + return registerService(); + } + + if (config.gateConfiguration.unregisterService) { + return unregisterService(); + } + + if (config.gateConfiguration.daemonize) { + if (runDaemon() != 0) { + throw std::runtime_error("Failed to start daemon"); + } + } else { + pg.run(); + } + + } catch (PaymentService::ConfigurationError& ex) { + std::cerr << "Configuration error: " << ex.what() << std::endl; + return 1; + } catch (std::exception& ex) { + std::cerr << "Fatal error: " << ex.what() << std::endl; + return 1; + } + + return 0; +} diff --git a/src/Platform/Linux/System/Dispatcher.cpp b/src/Platform/Linux/System/Dispatcher.cpp index e130bfb8b0..28f5f0deac 100755 --- a/src/Platform/Linux/System/Dispatcher.cpp +++ b/src/Platform/Linux/System/Dispatcher.cpp @@ -21,38 +21,81 @@ #include #include #include +#include +#include #include #include -#include namespace System { +namespace { + +struct ContextMakingData { + Dispatcher* dispatcher; + void* ucontext; +}; + +class MutextGuard { +public: + MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) { + auto ret = pthread_mutex_lock(&mutex); + if (ret != 0) { + throw std::runtime_error("failed to acquire mutex, errno=" + std::to_string(ret) + ": " + strerror(ret)); + } + } + + ~MutextGuard() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t& mutex; +}; + +static_assert(Dispatcher::SIZEOF_PTHREAD_MUTEX_T == sizeof(pthread_mutex_t), "invalid pthread mutex size"); + +const size_t STACK_SIZE = 64 * 1024; + +}; + Dispatcher::Dispatcher() { std::string message; epoll = ::epoll_create1(0); if (epoll == -1) { message = "epoll_create1() fail errno=" + std::to_string(errno); } else { - currentContext = new ucontext_t; - if (getcontext(reinterpret_cast(currentContext)) == -1) { + mainContext.ucontext = new ucontext_t; + if (getcontext(reinterpret_cast(mainContext.ucontext)) == -1) { message = "getcontext() fail errno=" + std::to_string(errno); } else { remoteSpawnEvent = eventfd(0, O_NONBLOCK); if(remoteSpawnEvent == -1) { message = "eventfd() fail errno=" + std::to_string(errno); } else { - eventContext.writeContext = nullptr; - eventContext.readContext = nullptr; + remoteSpawnEventContext.writeContext = nullptr; + remoteSpawnEventContext.readContext = nullptr; epoll_event remoteSpawnEventEpollEvent; remoteSpawnEventEpollEvent.events = EPOLLIN; - remoteSpawnEventEpollEvent.data.ptr = &eventContext; + remoteSpawnEventEpollEvent.data.ptr = &remoteSpawnEventContext; if (epoll_ctl(epoll, EPOLL_CTL_ADD, remoteSpawnEvent, &remoteSpawnEventEpollEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { - contextCount = 0; *reinterpret_cast(this->mutex) = pthread_mutex_t(PTHREAD_MUTEX_INITIALIZER); + + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; return; } @@ -69,15 +112,21 @@ Dispatcher::Dispatcher() { } Dispatcher::~Dispatcher() { - assert(resumingContexts.empty()); - assert(reusableContexts.size() == contextCount); - assert(spawningProcedures.empty()); - assert(reusableContexts.size() == allocatedStacks.size()); - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); + } + + yield(); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->ucontext); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; } while (!timers.empty()) { @@ -95,12 +144,12 @@ Dispatcher::~Dispatcher() { } void Dispatcher::clear() { - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); - --contextCount; + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->ucontext); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; } while (!timers.empty()) { @@ -114,11 +163,11 @@ void Dispatcher::clear() { } void Dispatcher::dispatch() { - void* context; + NativeContext* context; for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } @@ -133,13 +182,12 @@ void Dispatcher::dispatch() { throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno)); } - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } @@ -163,26 +211,61 @@ void Dispatcher::dispatch() { } if (context != currentContext) { - ucontext_t* oldContext = static_cast(currentContext); + ucontext_t* oldContext = static_cast(currentContext->ucontext); currentContext = context; - if (swapcontext(oldContext, static_cast(context)) == -1) { + if (swapcontext(oldContext, static_cast(context->ucontext)) == -1) { throw std::runtime_error("Dispatcher::dispatch() swapcontext() failed, errno=" + std::to_string(errno)); } } } -void* Dispatcher::getCurrentContext() const { +NativeContext* Dispatcher::getCurrentContext() const { return currentContext; } -void Dispatcher::pushContext(void* context) { - resumingContexts.push(context); +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { + assert(context!=nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(context != nullptr); + context->next = nullptr; + if(firstResumingContext != nullptr) { + assert(lastResumingContext != nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; } void Dispatcher::remoteSpawn(std::function&& procedure) { - pthread_mutex_lock(reinterpret_cast(this->mutex)); - remoteSpawningProcedures.push(std::move(procedure)); - pthread_mutex_unlock(reinterpret_cast(this->mutex)); + { + MutextGuard guard(*reinterpret_cast(this->mutex)); + remoteSpawningProcedures.push(std::move(procedure)); + } uint64_t one = 1; auto transferred = write(remoteSpawnEvent, &one, sizeof one); if(transferred == - 1) { @@ -191,25 +274,23 @@ void Dispatcher::remoteSpawn(std::function&& procedure) { } void Dispatcher::spawn(std::function&& procedure) { - ucontext_t *context; - if (reusableContexts.empty()) { - context = new ucontext_t; - if (getcontext(context) == -1) { //makecontext precondition - throw std::runtime_error("Dispatcher::spawn(), getcontext() fail errno=" + std::to_string(errno)); - } - auto stackPointer = new uint8_t[64 * 1024]; - context->uc_stack.ss_sp = stackPointer; - allocatedStacks.push(stackPointer); - context->uc_stack.ss_size = 64 * 1024; - makecontext(context, (void(*)())contextProcedureStatic, 1, reinterpret_cast(this)); - ++contextCount; + NativeContext* context = &getReusableContext(); + if(contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; } else { - context = static_cast(reusableContexts.top()); - reusableContexts.pop(); + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; } - resumingContexts.push(context); - spawningProcedures.emplace(std::move(procedure)); + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); } void Dispatcher::yield() { @@ -230,21 +311,22 @@ void Dispatcher::yield() { throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno)); } - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } if ((events[i].events & EPOLLOUT) != 0) { - resumingContexts.push(contextPair->writeContext->context); + contextPair->writeContext->context->interruptProcedure = nullptr; + pushContext(contextPair->writeContext->context); contextPair->writeContext->events = events[i].events; } else if ((events[i].events & EPOLLIN) != 0) { - resumingContexts.push(contextPair->readContext->context); + contextPair->readContext->context->interruptProcedure = nullptr; + pushContext(contextPair->readContext->context); contextPair->readContext->events = events[i].events; } else { continue; @@ -257,8 +339,8 @@ void Dispatcher::yield() { } } - if(!resumingContexts.empty()){ - resumingContexts.push(getCurrentContext()); + if (firstResumingContext != nullptr) { + pushContext(currentContext); dispatch(); } } @@ -267,6 +349,41 @@ int Dispatcher::getEpoll() const { return epoll; } +NativeContext& Dispatcher::getReusableContext() { + if(firstReusableContext == nullptr) { + ucontext_t* newlyCreatedContext = new ucontext_t; + if (getcontext(newlyCreatedContext) == -1) { //makecontext precondition + throw std::runtime_error("Dispatcher::getReusableContext(), getcontext() fail errno=" + std::to_string(errno)); + } + + auto stackPointer = new uint8_t[STACK_SIZE]; + newlyCreatedContext->uc_stack.ss_sp = stackPointer; + newlyCreatedContext->uc_stack.ss_size = STACK_SIZE; + + ContextMakingData makingContextData {this, newlyCreatedContext}; + makecontext(newlyCreatedContext, (void(*)())contextProcedureStatic, 1, reinterpret_cast(&makingContextData)); + + ucontext_t* oldContext = static_cast(currentContext->ucontext); + if (swapcontext(oldContext, newlyCreatedContext) == -1) { + throw std::runtime_error("Dispatcher::getReusableContext() swapcontext() failed, errno=" + std::to_string(errno)); + } + + assert(firstReusableContext != nullptr); + assert(firstReusableContext->ucontext == newlyCreatedContext); + firstReusableContext->stackPtr = stackPointer; + }; + + NativeContext* context = firstReusableContext; + firstReusableContext = firstReusableContext-> next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + int Dispatcher::getTimer() { int timer; if (timers.empty()) { @@ -290,20 +407,68 @@ void Dispatcher::pushTimer(int timer) { timers.push(timer); } -void Dispatcher::contextProcedure() { - void* context = currentContext; +void Dispatcher::contextProcedure(void* ucontext) { + assert(firstReusableContext == nullptr); + NativeContext context; + context.ucontext = ucontext; + context.interrupted = false; + context.next = nullptr; + firstReusableContext = &context; + ucontext_t* oldContext = static_cast(context.ucontext); + if (swapcontext(oldContext, static_cast(currentContext->ucontext)) == -1) { + throw std::runtime_error("Dispatcher::contextProcedure() swapcontext() failed, errno=" + std::to_string(errno)); + } + for (;;) { - assert(!spawningProcedures.empty()); - std::function procedure = std::move(spawningProcedures.front()); - spawningProcedures.pop(); - procedure(); - reusableContexts.push(context); + ++runningContextCount; + try { + context.procedure(); + } catch(std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + dispatch(); } -} +}; void Dispatcher::contextProcedureStatic(void *context) { - reinterpret_cast(context)->contextProcedure(); + ContextMakingData* makingContextData = reinterpret_cast(context); + makingContextData->dispatcher->contextProcedure(makingContextData->ucontext); } } diff --git a/src/Platform/Linux/System/Dispatcher.h b/src/Platform/Linux/System/Dispatcher.h index c7574f1105..ce6f0a08ab 100755 --- a/src/Platform/Linux/System/Dispatcher.h +++ b/src/Platform/Linux/System/Dispatcher.h @@ -17,12 +17,45 @@ #pragma once +#include #include #include #include namespace System { +struct NativeContextGroup; + +struct NativeContext { + void* ucontext; + void* stackPtr; + bool interrupted; + NativeContext* next; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + +struct OperationContext { + NativeContext *context; + bool interrupted; + uint32_t events; +}; + +struct ContextPair { + OperationContext *readContext; + OperationContext *writeContext; +}; + class Dispatcher { public: Dispatcher(); @@ -31,25 +64,18 @@ class Dispatcher { Dispatcher& operator=(const Dispatcher&) = delete; void clear(); void dispatch(); - void* getCurrentContext() const; - void pushContext(void* context); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); void remoteSpawn(std::function&& procedure); - void spawn(std::function&& procedure); void yield(); - struct OperationContext { - void *context; - bool interrupted; - uint32_t events; - }; - - struct ContextPair { - OperationContext *readContext; - OperationContext *writeContext; - }; - // system-dependent int getEpoll() const; + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); int getTimer(); void pushTimer(int timer); @@ -64,20 +90,23 @@ class Dispatcher { #endif private: - std::stack allocatedStacks; - std::size_t contextCount; - void* currentContext; + void spawn(std::function&& procedure); int epoll; - ContextPair eventContext; - uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + alignas(void*) uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; int remoteSpawnEvent; + ContextPair remoteSpawnEventContext; std::queue> remoteSpawningProcedures; - std::queue resumingContexts; - std::stack reusableContexts; - std::queue> spawningProcedures; std::stack timers; - void contextProcedure(); + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; + + void contextProcedure(void* ucontext); static void contextProcedureStatic(void* context); }; diff --git a/src/Platform/Linux/System/Ipv4Resolver.cpp b/src/Platform/Linux/System/Ipv4Resolver.cpp index d78195e45c..b9f1e435ba 100755 --- a/src/Platform/Linux/System/Ipv4Resolver.cpp +++ b/src/Platform/Linux/System/Ipv4Resolver.cpp @@ -22,6 +22,7 @@ #include +#include #include #include @@ -30,12 +31,11 @@ namespace System { Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { } -Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { } Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } } @@ -46,28 +46,15 @@ Ipv4Resolver::~Ipv4Resolver() { Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } return *this; } -void Ipv4Resolver::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Ipv4Resolver::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - stopped = true; -} - Ipv4Address Ipv4Resolver::resolve(const std::string& host) { assert(dispatcher != nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } diff --git a/src/Platform/Linux/System/Ipv4Resolver.h b/src/Platform/Linux/System/Ipv4Resolver.h index d59d50e0f2..02dd979396 100755 --- a/src/Platform/Linux/System/Ipv4Resolver.h +++ b/src/Platform/Linux/System/Ipv4Resolver.h @@ -33,13 +33,10 @@ class Ipv4Resolver { ~Ipv4Resolver(); Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; Ipv4Resolver& operator=(Ipv4Resolver&& other); - void start(); - void stop(); Ipv4Address resolve(const std::string& host); private: Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/Linux/System/TcpConnection.cpp b/src/Platform/Linux/System/TcpConnection.cpp index ecb1ab51f4..cce624595f 100755 --- a/src/Platform/Linux/System/TcpConnection.cpp +++ b/src/Platform/Linux/System/TcpConnection.cpp @@ -16,11 +16,11 @@ // along with Bytecoin. If not, see . #include "TcpConnection.h" -#include #include -#include +#include #include +#include #include #include @@ -35,7 +35,6 @@ TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatche assert(other.contextPair.writeContext == nullptr); assert(other.contextPair.readContext == nullptr); connection = other.connection; - stopped = other.stopped; contextPair = other.contextPair; other.dispatcher = nullptr; } @@ -64,7 +63,6 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(other.contextPair.readContext == nullptr); assert(other.contextPair.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; contextPair = other.contextPair; other.dispatcher = nullptr; } @@ -72,41 +70,10 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { return *this; } -void TcpConnection::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - - epoll_event connectionEvent; - connectionEvent.events = 0; - connectionEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { - throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail" + std::to_string(errno)); - } - - if(contextPair.readContext != nullptr) { - contextPair.readContext->interrupted = true; - dispatcher->pushContext(contextPair.readContext->context); - } - - if(contextPair.writeContext != nullptr) { - contextPair.writeContext->interrupted = true; - dispatcher->pushContext(contextPair.writeContext->context); - } - - stopped = true; -} - size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(contextPair.readContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -117,7 +84,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { message = "recv failed, errno=" + std::to_string(errno); } else { epoll_event connectionEvent; - Dispatcher::OperationContext operationContext; + OperationContext operationContext; operationContext.interrupted = false; operationContext.context = dispatcher->getCurrentContext(); contextPair.readContext = &operationContext; @@ -132,7 +99,23 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(contextPair.readContext != nullptr); + epoll_event connectionEvent; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail, errno=" + std::to_string(errno)); + } + + contextPair.readContext->interrupted = true; + dispatcher->pushContext(contextPair.readContext->context); + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(operationContext.context == dispatcher->getCurrentContext()); assert(contextPair.readContext == &operationContext); @@ -178,7 +161,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { std::size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(contextPair.writeContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -197,7 +180,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { message = "send failed, result=" + std::to_string(errno); } else { epoll_event connectionEvent; - Dispatcher::OperationContext operationContext; + OperationContext operationContext; operationContext.interrupted = false; operationContext.context = dispatcher->getCurrentContext(); contextPair.writeContext = &operationContext; @@ -212,7 +195,23 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(contextPair.writeContext != nullptr); + epoll_event connectionEvent; + connectionEvent.events = 0; + connectionEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { + throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail" + std::to_string(errno)); + } + + contextPair.writeContext->interrupted = true; + dispatcher->pushContext(contextPair.writeContext->context); + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(operationContext.context == dispatcher->getCurrentContext()); assert(contextPair.writeContext == &operationContext); @@ -230,12 +229,12 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write, " + message); } } if((operationContext.events & (EPOLLERR | EPOLLHUP)) != 0) { - throw std::runtime_error("TcpConnection::write"); + throw std::runtime_error("TcpConnection::write: events & (EPOLLERR | EPOLLHUP) != 0"); } ssize_t transferred = ::send(connection, (void *)data, size, 0); @@ -248,25 +247,25 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { } } - throw std::runtime_error("TcpConnection::write, "+message); + throw std::runtime_error("TcpConnection::write, " + message); } assert(transferred <= static_cast(size)); return transferred; } -std::pair TcpConnection::getPeerAddressAndPort() { +std::pair TcpConnection::getPeerAddressAndPort() const { sockaddr_in addr; socklen_t size = sizeof(addr); if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { - throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(errno)); + throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, errno=" + std::to_string(errno)); } assert(size == sizeof(sockaddr_in)); return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); } -TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false) { +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket) { contextPair.readContext = nullptr; contextPair.writeContext = nullptr; epoll_event connectionEvent; @@ -274,7 +273,7 @@ TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&d connectionEvent.data.ptr = nullptr; if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) { - throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl() fail" + std::to_string(errno)); + throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl() fail, errno=" + std::to_string(errno)); } } diff --git a/src/Platform/Linux/System/TcpConnection.h b/src/Platform/Linux/System/TcpConnection.h index 034d6cccdf..a84b4757d6 100755 --- a/src/Platform/Linux/System/TcpConnection.h +++ b/src/Platform/Linux/System/TcpConnection.h @@ -34,11 +34,9 @@ class TcpConnection { ~TcpConnection(); TcpConnection& operator=(const TcpConnection&) = delete; TcpConnection& operator=(TcpConnection&& other); - void start(); - void stop(); std::size_t read(uint8_t* data, std::size_t size); std::size_t write(const uint8_t* data, std::size_t size); - std::pair getPeerAddressAndPort(); + std::pair getPeerAddressAndPort() const; private: friend class TcpConnector; @@ -46,8 +44,7 @@ class TcpConnection { Dispatcher* dispatcher; int connection; - bool stopped; - Dispatcher::ContextPair contextPair; + ContextPair contextPair; TcpConnection(Dispatcher& dispatcher, int socket); }; diff --git a/src/Platform/Linux/System/TcpConnector.cpp b/src/Platform/Linux/System/TcpConnector.cpp index 22a6ddbdd4..76d577fbc6 100755 --- a/src/Platform/Linux/System/TcpConnector.cpp +++ b/src/Platform/Linux/System/TcpConnector.cpp @@ -33,7 +33,7 @@ namespace System { namespace { -struct TcpConnectorContextExt : public Dispatcher::OperationContext { +struct TcpConnectorContextExt : public OperationContext { int connection; }; @@ -42,13 +42,12 @@ struct TcpConnectorContextExt : public Dispatcher::OperationContext { TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), stopped(false) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -61,7 +60,6 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -69,34 +67,10 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { return *this; } -void TcpConnector::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TcpConnectorContextExt* connectorContext = static_cast(context); - if (!connectorContext->interrupted) { - if (close(connectorContext->connection) == -1) { - throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); - } - - connectorContext->interrupted = true; - dispatcher->pushContext(connectorContext->context); - } - } - - stopped = true; -} - TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -124,7 +98,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { if (result == -1) { if (errno == EINPROGRESS) { - Dispatcher::ContextPair contextPair; + ContextPair contextPair; TcpConnectorContextExt connectorContext; connectorContext.interrupted = false; connectorContext.context = dispatcher->getCurrentContext(); @@ -140,7 +114,20 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { context = &connectorContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + TcpConnectorContextExt* connectorContext1 = static_cast(context); + if (!connectorContext1->interrupted) { + if (close(connectorContext1->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); + } + + connectorContext1->interrupted = true; + dispatcher->pushContext(connectorContext1->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(connectorContext.context == dispatcher->getCurrentContext()); assert(contextPair.readContext == nullptr); diff --git a/src/Platform/Linux/System/TcpConnector.h b/src/Platform/Linux/System/TcpConnector.h index 9eef67efd2..8dc7b110f7 100755 --- a/src/Platform/Linux/System/TcpConnector.h +++ b/src/Platform/Linux/System/TcpConnector.h @@ -35,14 +35,11 @@ class TcpConnector { ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); - void start(); - void stop(); TcpConnection connect(const Ipv4Address& address, uint16_t port); private: void* context; Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/Linux/System/TcpListener.cpp b/src/Platform/Linux/System/TcpListener.cpp index 4a3b750813..fde904b797 100755 --- a/src/Platform/Linux/System/TcpListener.cpp +++ b/src/Platform/Linux/System/TcpListener.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "Dispatcher.h" #include "TcpConnection.h" @@ -38,33 +39,32 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16 std::string message; listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listener == -1) { - message = "socket() failed, errno=" + std::to_string(errno); + message = "socket() failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { int flags = fcntl(listener, F_GETFL, 0); if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) { - message = "fcntl() failed errno=" + std::to_string(errno); + message = "fcntl() failed errno=" + std::to_string(errno) + ": " + strerror(errno); } else { int on = 1; if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) { - message = "setsockopt failed, errno=" + std::to_string(errno); + message = "setsockopt failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { sockaddr_in address; address.sin_family = AF_INET; address.sin_port = htons(port); address.sin_addr.s_addr = htonl( addr.getValue()); if (bind(listener, reinterpret_cast(&address), sizeof address) != 0) { - message = "bind failed, errno=" + std::to_string(errno); + message = "bind failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else if (listen(listener, SOMAXCONN) != 0) { - message = "listen failed, errno=" + std::to_string(errno); + message = "listen failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { epoll_event listenEvent; listenEvent.events = 0; listenEvent.data.ptr = nullptr; if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) { - message = "epoll_ctl() failed, errno=" + std::to_string(errno); + message = "epoll_ctl() failed, errno=" + std::to_string(errno) + ": " + strerror(errno); } else { - stopped = false; context = nullptr; return; } @@ -83,7 +83,6 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -109,7 +108,6 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -117,43 +115,15 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { return *this; } -void TcpListener::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - Dispatcher::OperationContext* listenerContext = static_cast(context); - if (!listenerContext->interrupted) { - epoll_event listenEvent; - listenEvent.events = 0; - listenEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { - throw std::runtime_error("TcpListener::stop, epoll_ctl() failed, errno=" + std::to_string(errno) ); - } - - listenerContext->interrupted = true; - dispatcher->pushContext(listenerContext->context); - } - } - - stopped = true; -} - TcpConnection TcpListener::accept() { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } - Dispatcher::ContextPair contextPair; - Dispatcher::OperationContext listenerContext; + ContextPair contextPair; + OperationContext listenerContext; listenerContext.interrupted = false; listenerContext.context = dispatcher->getCurrentContext(); @@ -168,7 +138,26 @@ TcpConnection TcpListener::accept() { message = "epoll_ctl() failed, errno=" + std::to_string(errno); } else { context = &listenerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { + epoll_event listenEvent; + listenEvent.events = 0; + listenEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) { + throw std::runtime_error("TcpListener::stop, epoll_ctl() failed, errno=" + std::to_string(errno) ); + } + + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(listenerContext.context == dispatcher->getCurrentContext()); assert(contextPair.writeContext == nullptr); diff --git a/src/Platform/Linux/System/TcpListener.h b/src/Platform/Linux/System/TcpListener.h index 33d625e62c..9478c4232d 100755 --- a/src/Platform/Linux/System/TcpListener.h +++ b/src/Platform/Linux/System/TcpListener.h @@ -35,15 +35,12 @@ class TcpListener { ~TcpListener(); TcpListener& operator=(const TcpListener&) = delete; TcpListener& operator=(TcpListener&& other); - void start(); - void stop(); TcpConnection accept(); private: Dispatcher* dispatcher; void* context; int listener; - bool stopped; }; } diff --git a/src/Platform/Linux/System/Timer.cpp b/src/Platform/Linux/System/Timer.cpp index 8a95a96fbe..6cc0a350a1 100755 --- a/src/Platform/Linux/System/Timer.cpp +++ b/src/Platform/Linux/System/Timer.cpp @@ -31,14 +31,13 @@ namespace System { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), stopped(false), timer(-1) { +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), timer(-1) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -54,7 +53,6 @@ Timer& Timer::operator=(Timer&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; other.timer = -1; @@ -63,50 +61,10 @@ Timer& Timer::operator=(Timer&& other) { return *this; } -void Timer::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - - if (context != nullptr) { - Dispatcher::OperationContext* timerContext = static_cast(context); - if (!timerContext->interrupted) { - - uint64_t value = 0; - if(::read(timer, &value, sizeof value) == -1 ){ - if(errno == EAGAIN || errno == EWOULDBLOCK) { - timerContext->interrupted = true; - dispatcher->pushContext(timerContext->context); - } else { - throw std::runtime_error("Timer::stop, read failed, errno=" + std::to_string(errno)); - } - } else { - assert(value>0); - dispatcher->pushContext(timerContext->context); - } - - epoll_event timerEvent; - timerEvent.events = 0; - timerEvent.data.ptr = nullptr; - - if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { - throw std::runtime_error("Timer::stop epoll_ctl() failed, errno=" + std::to_string(errno)); - } - } - } - - stopped = true; -} - void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -122,8 +80,8 @@ void Timer::sleep(std::chrono::nanoseconds duration) { expires.it_value.tv_nsec = std::chrono::duration_cast(duration - seconds).count(); timerfd_settime(timer, 0, &expires, NULL); - Dispatcher::ContextPair contextPair; - Dispatcher::OperationContext timerContext; + ContextPair contextPair; + OperationContext timerContext; timerContext.interrupted = false; timerContext.context = dispatcher->getCurrentContext(); contextPair.writeContext = nullptr; @@ -136,9 +94,37 @@ void Timer::sleep(std::chrono::nanoseconds duration) { if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { throw std::runtime_error("Timer::sleep, epoll_ctl() failed, errno=" + std::to_string(errno)); } + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + uint64_t value = 0; + if(::read(timer, &value, sizeof value) == -1 ){ + if(errno == EAGAIN || errno == EWOULDBLOCK) { + timerContext->interrupted = true; + dispatcher->pushContext(timerContext->context); + } else { + throw std::runtime_error("Timer::interrupt, read failed, errno=" + std::to_string(errno)); + } + } else { + assert(value>0); + dispatcher->pushContext(timerContext->context); + } + + epoll_event timerEvent; + timerEvent.events = 0; + timerEvent.data.ptr = nullptr; + + if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) { + throw std::runtime_error("Timer::interrupt epoll_ctl() failed, errno=" + std::to_string(errno)); + } + } + }; context = &timerContext; dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(timerContext.context == dispatcher->getCurrentContext()); assert(contextPair.writeContext == nullptr); diff --git a/src/Platform/Linux/System/Timer.h b/src/Platform/Linux/System/Timer.h index 20a0f1a4bd..ef6255b69f 100755 --- a/src/Platform/Linux/System/Timer.h +++ b/src/Platform/Linux/System/Timer.h @@ -32,14 +32,11 @@ class Timer { ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void start(); - void stop(); void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; void* context; - bool stopped; int timer; }; diff --git a/src/Platform/OSX/System/context.c b/src/Platform/OSX/System/Context.c old mode 100644 new mode 100755 similarity index 100% rename from src/Platform/OSX/System/context.c rename to src/Platform/OSX/System/Context.c diff --git a/src/Platform/OSX/System/context.h b/src/Platform/OSX/System/Context.h old mode 100644 new mode 100755 similarity index 100% rename from src/Platform/OSX/System/context.h rename to src/Platform/OSX/System/Context.h diff --git a/src/Platform/OSX/System/Dispatcher.cpp b/src/Platform/OSX/System/Dispatcher.cpp index ba2aad22f0..619200fa44 100755 --- a/src/Platform/OSX/System/Dispatcher.cpp +++ b/src/Platform/OSX/System/Dispatcher.cpp @@ -18,27 +18,56 @@ #include "Dispatcher.h" #include #include - -#include -#include -#include #include +#include #include #include +#include +#include +#include #include - -#include "context.h" +#include "Context.h" namespace System { +namespace{ + +struct ContextMakingData { + void* uctx; + Dispatcher* dispatcher; +}; + +class MutextGuard { +public: + MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) { + auto ret = pthread_mutex_lock(&mutex); + if (ret != 0) { + throw std::runtime_error("failed to acquire mutex, errno=" + std::to_string(ret) + ": " + strerror(ret)); + } + } + + ~MutextGuard() { + pthread_mutex_unlock(&mutex); + } + +private: + pthread_mutex_t& mutex; +}; + +const size_t STACK_SIZE = 64 * 1024; + +} + +static_assert(Dispatcher::SIZEOF_PTHREAD_MUTEX_T == sizeof(pthread_mutex_t), "invalid pthread mutex size"); + Dispatcher::Dispatcher() : lastCreatedTimer(0) { std::string message; kqueue = ::kqueue(); if (kqueue == -1) { message = "kqueue() fail errno=" + std::to_string(errno); } else { - currentContext = new uctx; - if (getcontext(static_cast(currentContext)) == -1) { + mainContext.uctx = new uctx; + if (getcontext(static_cast(mainContext.uctx)) == -1) { message = "getcontext() fail errno=" + std::to_string(errno); } else { struct kevent event; @@ -50,7 +79,19 @@ Dispatcher::Dispatcher() : lastCreatedTimer(0) { message = "pthread_mutex_init() fail errno=" + std::to_string(errno); } else { remoteSpawned = false; - contextCount = 0; + + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; return; } } @@ -64,17 +105,23 @@ Dispatcher::Dispatcher() : lastCreatedTimer(0) { } Dispatcher::~Dispatcher() { - assert(resumingContexts.empty()); - assert(reusableContexts.size() == contextCount); - assert(spawningProcedures.empty()); - assert(reusableContexts.size() == allocatedStacks.size()); - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); } + yield(); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->uctx); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; + } + auto result = close(kqueue); assert(result != -1); result = pthread_mutex_destroy(reinterpret_cast(this->mutex)); @@ -82,33 +129,32 @@ Dispatcher::~Dispatcher() { } void Dispatcher::clear() { - while (!reusableContexts.empty()) { - delete[] allocatedStacks.top(); - allocatedStacks.pop(); - delete static_cast(reusableContexts.top()); - reusableContexts.pop(); - --contextCount; + while (firstReusableContext != nullptr) { + auto ucontext = static_cast(firstReusableContext->uctx); + auto stackPtr = static_cast(firstReusableContext->stackPtr); + firstReusableContext = firstReusableContext->next; + delete[] stackPtr; + delete ucontext; } } void Dispatcher::dispatch() { - void* context; + NativeContext* context; for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } - + if(remoteSpawned.load() == true) { - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } remoteSpawned = false; - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } @@ -132,67 +178,97 @@ void Dispatcher::dispatch() { if (errno != EINTR) { throw std::runtime_error("Dispatcher::dispatch(), kqueue() fail errno=" + std::to_string(errno)); } else { - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } - pthread_mutex_unlock(reinterpret_cast(this->mutex)); } } if (context != currentContext) { - uctx* oldContext = static_cast(currentContext); + uctx* oldContext = static_cast(currentContext->uctx); currentContext = context; - if (swapcontext(oldContext,static_cast(currentContext)) == -1) { + if (swapcontext(oldContext,static_cast(currentContext->uctx)) == -1) { throw std::runtime_error("Dispatcher::dispatch(), swapcontext() failed, errno=" + std::to_string(errno)); } } } -void* Dispatcher::getCurrentContext() const { +NativeContext* Dispatcher::getCurrentContext() const { return currentContext; } -void Dispatcher::pushContext(void* context) { - resumingContexts.push(context); +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { + assert(context!=nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(context!=nullptr); + context->next = nullptr; + if (firstResumingContext != nullptr) { + assert(lastResumingContext != nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; } void Dispatcher::remoteSpawn(std::function&& procedure) { - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); remoteSpawningProcedures.push(std::move(procedure)); - if(remoteSpawned == false) { + if (remoteSpawned == false) { remoteSpawned = true; struct kevent event; - EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ONESHOT, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL); + EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ENABLE, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL); if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) { throw std::runtime_error("Dispatcher::remoteSpawn(), kevent() fail errno=" + std::to_string(errno)); }; } - - pthread_mutex_unlock(reinterpret_cast(this->mutex)); } void Dispatcher::spawn(std::function&& procedure) { - void* context; - if (reusableContexts.empty()) { - context = new uctx; - uint8_t* stackPointer = new uint8_t[64 * 1024]; - allocatedStacks.push(stackPointer); - - static_cast(context)->uc_stack.ss_sp = stackPointer; - static_cast(context)->uc_stack.ss_size = 64 * 1024; - makecontext(static_cast(context), reinterpret_cast(contextProcedureStatic), reinterpret_cast(this)); - - ++contextCount; + NativeContext* context = &getReusableContext(); + if(contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; } else { - context = reusableContexts.top(); - reusableContexts.pop(); + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; } - resumingContexts.push(context); - spawningProcedures.emplace(std::move(procedure)); + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); } void Dispatcher::yield() { @@ -213,18 +289,18 @@ void Dispatcher::yield() { throw std::runtime_error("kevent() fail errno=" + std::to_string(errno)); } - pthread_mutex_lock(reinterpret_cast(this->mutex)); + MutextGuard guard(*reinterpret_cast(this->mutex)); while (!remoteSpawningProcedures.empty()) { spawn(std::move(remoteSpawningProcedures.front())); remoteSpawningProcedures.pop(); } remoteSpawned = false; - pthread_mutex_unlock(reinterpret_cast(this->mutex)); continue; } - resumingContexts.push(static_cast(events[i].udata)->context); + static_cast(events[i].udata)->context->interruptProcedure = nullptr; + pushContext(static_cast(events[i].udata)->context); } } else { if (errno != EINTR) { @@ -233,8 +309,8 @@ void Dispatcher::yield() { } } - if (!resumingContexts.empty()) { - resumingContexts.push(getCurrentContext()); + if (firstResumingContext != nullptr) { + pushContext(currentContext); dispatch(); } } @@ -243,6 +319,37 @@ int Dispatcher::getKqueue() const { return kqueue; } +NativeContext& Dispatcher::getReusableContext() { + if(firstReusableContext == nullptr) { + uctx* newlyCreatedContext = new uctx; + uint8_t* stackPointer = new uint8_t[STACK_SIZE]; + static_cast(newlyCreatedContext)->uc_stack.ss_sp = stackPointer; + static_cast(newlyCreatedContext)->uc_stack.ss_size = STACK_SIZE; + + ContextMakingData makingData{ newlyCreatedContext, this}; + makecontext(static_cast(newlyCreatedContext), reinterpret_cast(contextProcedureStatic), reinterpret_cast(&makingData)); + + uctx* oldContext = static_cast(currentContext->uctx); + if (swapcontext(oldContext, newlyCreatedContext) == -1) { + throw std::runtime_error("Dispatcher::getReusableContext(), swapcontext() failed, errno=" + std::to_string(errno)); + } + + assert(firstReusableContext != nullptr); + assert(firstReusableContext->uctx == newlyCreatedContext); + firstReusableContext->stackPtr = stackPointer; + } + + NativeContext* context = firstReusableContext; + firstReusableContext = firstReusableContext->next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + int Dispatcher::getTimer() { int timer; if (timers.empty()) { @@ -259,20 +366,68 @@ void Dispatcher::pushTimer(int timer) { timers.push(timer); } -void Dispatcher::contextProcedure() { - void* context = currentContext; +void Dispatcher::contextProcedure(void* ucontext) { + assert(firstReusableContext == nullptr); + NativeContext context; + context.uctx = ucontext; + context.interrupted = false; + context.next = nullptr; + firstReusableContext = &context; + uctx* oldContext = static_cast(context.uctx); + if (swapcontext(oldContext, static_cast(currentContext->uctx)) == -1) { + throw std::runtime_error("Dispatcher::contextProcedure() swapcontext() failed, errno=" + std::to_string(errno)); + } + for (;;) { - assert(!spawningProcedures.empty()); - std::function procedure = std::move(spawningProcedures.front()); - spawningProcedures.pop(); - procedure(); - reusableContexts.push(context); + ++runningContextCount; + try { + context.procedure(); + } catch(std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + dispatch(); } } void Dispatcher::contextProcedureStatic(intptr_t context) { - reinterpret_cast(context)->contextProcedure(); + ContextMakingData* makingContextData = reinterpret_cast(context); + makingContextData->dispatcher->contextProcedure(makingContextData->uctx); } } diff --git a/src/Platform/OSX/System/Dispatcher.h b/src/Platform/OSX/System/Dispatcher.h index 482dc3c2ca..48bb5636a4 100755 --- a/src/Platform/OSX/System/Dispatcher.h +++ b/src/Platform/OSX/System/Dispatcher.h @@ -18,12 +18,39 @@ #pragma once #include +#include #include #include #include namespace System { +struct NativeContextGroup; + +struct NativeContext { + void* uctx; + void* stackPtr; + bool interrupted; + NativeContext* next; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + +struct OperationContext { + NativeContext* context; + bool interrupted; +}; + class Dispatcher { public: Dispatcher(); @@ -32,18 +59,17 @@ class Dispatcher { Dispatcher& operator=(const Dispatcher&) = delete; void clear(); void dispatch(); - void* getCurrentContext() const; - void pushContext(void* context); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); void remoteSpawn(std::function&& procedure); - void spawn(std::function&& procedure); void yield(); - struct OperationContext { - void *context; - bool interrupted; - }; - int getKqueue() const; + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); int getTimer(); void pushTimer(int timer); @@ -54,20 +80,24 @@ class Dispatcher { #endif private: - std::stack allocatedStacks; - std::size_t contextCount; - void* currentContext; + void spawn(std::function&& procedure); + int kqueue; int lastCreatedTimer; - uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; + alignas(std::max_align_t) uint8_t mutex[SIZEOF_PTHREAD_MUTEX_T]; std::atomic remoteSpawned; std::queue> remoteSpawningProcedures; - std::queue resumingContexts; - std::queue> spawningProcedures; - std::stack reusableContexts; std::stack timers; - void contextProcedure(); + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; + + void contextProcedure(void* uctx); static void contextProcedureStatic(intptr_t context); }; diff --git a/src/Platform/OSX/System/Ipv4Resolver.cpp b/src/Platform/OSX/System/Ipv4Resolver.cpp index d78195e45c..b9f1e435ba 100755 --- a/src/Platform/OSX/System/Ipv4Resolver.cpp +++ b/src/Platform/OSX/System/Ipv4Resolver.cpp @@ -22,6 +22,7 @@ #include +#include #include #include @@ -30,12 +31,11 @@ namespace System { Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { } -Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { } Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } } @@ -46,28 +46,15 @@ Ipv4Resolver::~Ipv4Resolver() { Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } return *this; } -void Ipv4Resolver::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Ipv4Resolver::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - stopped = true; -} - Ipv4Address Ipv4Resolver::resolve(const std::string& host) { assert(dispatcher != nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } diff --git a/src/Platform/OSX/System/Ipv4Resolver.h b/src/Platform/OSX/System/Ipv4Resolver.h index d59d50e0f2..02dd979396 100755 --- a/src/Platform/OSX/System/Ipv4Resolver.h +++ b/src/Platform/OSX/System/Ipv4Resolver.h @@ -33,13 +33,10 @@ class Ipv4Resolver { ~Ipv4Resolver(); Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; Ipv4Resolver& operator=(Ipv4Resolver&& other); - void start(); - void stop(); Ipv4Address resolve(const std::string& host); private: Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/OSX/System/TcpConnection.cpp b/src/Platform/OSX/System/TcpConnection.cpp index 62872f635b..112c2d5943 100755 --- a/src/Platform/OSX/System/TcpConnection.cpp +++ b/src/Platform/OSX/System/TcpConnection.cpp @@ -38,7 +38,6 @@ TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatche assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -68,7 +67,6 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -77,52 +75,10 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { return *this; } -void TcpConnection::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (writeContext != nullptr) { - Dispatcher::OperationContext* context = static_cast(writeContext); - if (!context->interrupted) { - struct kevent event; - EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - context->interrupted = true; - dispatcher->pushContext(context->context); - } - } - - if (readContext != nullptr) { - Dispatcher::OperationContext* context = static_cast(readContext); - if (!context->interrupted) { - struct kevent event; - EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - context->interrupted = true; - dispatcher->pushContext(context->context); - } - } - - stopped = true; -} - size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(readContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -132,7 +88,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { if (errno != EAGAIN && errno != EWOULDBLOCK) { message = "recv failed, errno=" + std::to_string(errno); } else { - Dispatcher::OperationContext context; + OperationContext context; context.context = dispatcher->getCurrentContext(); context.interrupted = false; struct kevent event; @@ -141,7 +97,25 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { message = "kevent() failed, errno=" + std::to_string(errno); } else { readContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(readContext != nullptr); + OperationContext* context = static_cast(readContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::interruptionProcedure, kevent() failed, errno=" + std::to_string(errno)); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(context.context == dispatcher->getCurrentContext()); assert(readContext == &context); @@ -171,7 +145,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(writeContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -189,7 +163,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) { if (errno != EAGAIN && errno != EWOULDBLOCK) { message = "send failed, result=" + std::to_string(errno); } else { - Dispatcher::OperationContext context; + OperationContext context; context.context = dispatcher->getCurrentContext(); context.interrupted = false; struct kevent event; @@ -198,7 +172,25 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) { message = "kevent() failed, errno=" + std::to_string(errno); } else { writeContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(writeContext != nullptr); + OperationContext* context = static_cast(writeContext); + if (!context->interrupted) { + struct kevent event; + EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + context->interrupted = true; + dispatcher->pushContext(context->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(context.context == dispatcher->getCurrentContext()); assert(writeContext == &context); @@ -225,7 +217,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) { return transferred; } -std::pair TcpConnection::getPeerAddressAndPort() { +std::pair TcpConnection::getPeerAddressAndPort() const { sockaddr_in addr; socklen_t size = sizeof(addr); if (getpeername(connection, reinterpret_cast(&addr), &size) != 0) { @@ -236,7 +228,11 @@ std::pair TcpConnection::getPeerAddressAndPort() { return std::make_pair(Ipv4Address(htonl(addr.sin_addr.s_addr)), htons(addr.sin_port)); } -TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), stopped(false), readContext(nullptr), writeContext(nullptr) { +TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), readContext(nullptr), writeContext(nullptr) { + int val = 1; + if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) { + throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, result=" + std::to_string(errno)); + } } } diff --git a/src/Platform/OSX/System/TcpConnection.h b/src/Platform/OSX/System/TcpConnection.h index c98650f7ad..63da8280ec 100755 --- a/src/Platform/OSX/System/TcpConnection.h +++ b/src/Platform/OSX/System/TcpConnection.h @@ -34,11 +34,9 @@ class TcpConnection { ~TcpConnection(); TcpConnection& operator=(const TcpConnection&) = delete; TcpConnection& operator=(TcpConnection&& other); - void start(); - void stop(); std::size_t read(uint8_t* data, std::size_t size); std::size_t write(const uint8_t* data, std::size_t size); - std::pair getPeerAddressAndPort(); + std::pair getPeerAddressAndPort() const; private: friend class TcpConnector; @@ -46,7 +44,6 @@ class TcpConnection { Dispatcher* dispatcher; int connection; - bool stopped; void* readContext; void* writeContext; diff --git a/src/Platform/OSX/System/TcpConnector.cpp b/src/Platform/OSX/System/TcpConnector.cpp index 794ec5152c..898b953689 100755 --- a/src/Platform/OSX/System/TcpConnector.cpp +++ b/src/Platform/OSX/System/TcpConnector.cpp @@ -35,7 +35,7 @@ namespace System { namespace { -struct ConnectorContext : public Dispatcher::OperationContext { +struct ConnectorContext : public OperationContext { int connection; }; @@ -44,13 +44,12 @@ struct ConnectorContext : public Dispatcher::OperationContext { TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -64,7 +63,6 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (other.dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -72,34 +70,10 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { return *this; } -void TcpConnector::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - ConnectorContext* connectorContext = static_cast(context); - if (!connectorContext->interrupted) { - if (close(connectorContext->connection) == -1) { - throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); - } - - dispatcher->pushContext(connectorContext->context); - connectorContext->interrupted = true; - } - } - - stopped = true; -} - TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -137,7 +111,22 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { message = "kevent() failed, errno=" + std::to_string(errno); } else { context = &connectorContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + ConnectorContext* connectorContext = static_cast(context); + if (!connectorContext->interrupted) { + if (close(connectorContext->connection) == -1) { + throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno)); + } + + dispatcher->pushContext(connectorContext->context); + connectorContext->interrupted = true; + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(connectorContext.context == dispatcher->getCurrentContext()); assert(context == &connectorContext); diff --git a/src/Platform/OSX/System/TcpConnector.h b/src/Platform/OSX/System/TcpConnector.h index 9eef67efd2..8dc7b110f7 100755 --- a/src/Platform/OSX/System/TcpConnector.h +++ b/src/Platform/OSX/System/TcpConnector.h @@ -35,14 +35,11 @@ class TcpConnector { ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); - void start(); - void stop(); TcpConnection connect(const Ipv4Address& address, uint16_t port); private: void* context; Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/OSX/System/TcpListener.cpp b/src/Platform/OSX/System/TcpListener.cpp index 4c9f15bfa5..7c9ea2d3a2 100755 --- a/src/Platform/OSX/System/TcpListener.cpp +++ b/src/Platform/OSX/System/TcpListener.cpp @@ -61,12 +61,11 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16 message = "listen failed, errno=" + std::to_string(errno); } else { struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE, 0, SOMAXCONN, NULL); + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE | EV_CLEAR, 0, SOMAXCONN, NULL); if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) { message = "kevent() failed, errno=" + std::to_string(errno); } else { - stopped = false; context = nullptr; return; } @@ -86,7 +85,6 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -112,7 +110,6 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -120,52 +117,43 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { return *this; } -void TcpListener::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - Dispatcher::OperationContext* listenerContext = static_cast(context); - if (!listenerContext->interrupted) { - - struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - listenerContext->interrupted = true; - dispatcher->pushContext(listenerContext->context); - } - } - - stopped = true; -} - TcpConnection TcpListener::accept() { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } std::string message; - Dispatcher::OperationContext listenerContext; + OperationContext listenerContext; listenerContext.context = dispatcher->getCurrentContext(); listenerContext.interrupted = false; struct kevent event; - EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, SOMAXCONN, &listenerContext); + EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR , 0, SOMAXCONN, &listenerContext); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { message = "kevent() failed, errno=" + std::to_string(errno); } else { context = &listenerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* listenerContext = static_cast(context); + if (!listenerContext->interrupted) { + + struct kevent event; + EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + listenerContext->interrupted = true; + dispatcher->pushContext(listenerContext->context); + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(listenerContext.context == dispatcher->getCurrentContext()); assert(context == &listenerContext); diff --git a/src/Platform/OSX/System/TcpListener.h b/src/Platform/OSX/System/TcpListener.h index 43fc2b4a35..e5baaf8cf7 100755 --- a/src/Platform/OSX/System/TcpListener.h +++ b/src/Platform/OSX/System/TcpListener.h @@ -35,14 +35,11 @@ class TcpListener { ~TcpListener(); TcpListener& operator=(const TcpListener&) = delete; TcpListener& operator=(TcpListener&& other); - void start(); - void stop(); TcpConnection accept(); private: Dispatcher* dispatcher; int listener; - bool stopped; void* context; }; diff --git a/src/Platform/OSX/System/Timer.cpp b/src/Platform/OSX/System/Timer.cpp index a8a9e829e6..6f61c7b550 100755 --- a/src/Platform/OSX/System/Timer.cpp +++ b/src/Platform/OSX/System/Timer.cpp @@ -33,14 +33,13 @@ namespace System { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr), timer(-1) { +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr), timer(-1) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -56,7 +55,6 @@ Timer& Timer::operator=(Timer&& other) { if (other.dispatcher != nullptr) { assert(other.context == nullptr); timer = other.timer; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; other.timer = -1; @@ -65,55 +63,45 @@ Timer& Timer::operator=(Timer&& other) { return *this; } -void Timer::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - - if (context != nullptr) { - Dispatcher::OperationContext* timerContext = static_cast(context); - if (!timerContext->interrupted) { - struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_DISABLE, 0, 0, NULL); - - if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { - throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); - } - - dispatcher->pushContext(timerContext->context); - timerContext->interrupted = true; - } - } - - stopped = true; -} - void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } - Dispatcher::OperationContext timerContext; + OperationContext timerContext; timerContext.context = dispatcher->getCurrentContext(); timerContext.interrupted = false; timer = dispatcher->getTimer(); struct kevent event; - EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, duration.count() / 1000000, &timerContext); + EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_NSECONDS, duration.count(), &timerContext); if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); } context = &timerContext; + dispatcher->getCurrentContext()->interruptProcedure = [&] { + assert(dispatcher != nullptr); + assert(context != nullptr); + OperationContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + struct kevent event; + EV_SET(&event, timer, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + + if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) { + throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno)); + } + + dispatcher->pushContext(timerContext->context); + timerContext->interrupted = true; + } + }; + dispatcher->dispatch(); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; assert(dispatcher != nullptr); assert(timerContext.context == dispatcher->getCurrentContext()); assert(context == &timerContext); diff --git a/src/Platform/OSX/System/Timer.h b/src/Platform/OSX/System/Timer.h index 75840bed2b..ecb9dd814f 100755 --- a/src/Platform/OSX/System/Timer.h +++ b/src/Platform/OSX/System/Timer.h @@ -32,14 +32,11 @@ class Timer { ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void start(); - void stop(); void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; int timer; - bool stopped; void* context; }; diff --git a/src/Platform/Windows/System/Dispatcher.cpp b/src/Platform/Windows/System/Dispatcher.cpp index cb23e6a8b6..2fd59441a9 100755 --- a/src/Platform/Windows/System/Dispatcher.cpp +++ b/src/Platform/Windows/System/Dispatcher.cpp @@ -31,9 +31,11 @@ namespace System { namespace { struct DispatcherContext : public OVERLAPPED { - void* context; + NativeContext* context; }; +const size_t STACK_SIZE = 16384; +const size_t RESERVE_STACK_SIZE = 2097152; } Dispatcher::Dispatcher() { @@ -44,35 +46,40 @@ Dispatcher::Dispatcher() { if (ConvertThreadToFiberEx(NULL, 0) == NULL) { message = "ConvertThreadToFiberEx failed, result=" + std::to_string(GetLastError()); } else { - threadHandle = OpenThread(THREAD_SET_CONTEXT, FALSE, GetCurrentThreadId()); - if (threadHandle == NULL) { - message = "OpenThread failed, result=" + std::to_string(GetLastError()); + completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (completionPort == NULL) { + message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if (completionPort == NULL) { - message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); + WSADATA wsaData; + int wsaResult = WSAStartup(0x0202, &wsaData); + if (wsaResult != 0) { + message = "WSAStartup failed, result=" + std::to_string(wsaResult); } else { - WSADATA wsaData; - int wsaResult = WSAStartup(0x0202, &wsaData); - if (wsaResult != 0) { - message = "WSAStartup failed, result=" + std::to_string(wsaResult); - } else { - contextCount = 0; - remoteNotificationSent = false; - reinterpret_cast(remoteSpawnOverlapped)->hEvent = NULL; - threadId = GetCurrentThreadId(); - return; - } - - BOOL result = CloseHandle(completionPort); - assert(result == TRUE); + remoteNotificationSent = false; + reinterpret_cast(remoteSpawnOverlapped)->hEvent = NULL; + threadId = GetCurrentThreadId(); + + mainContext.fiber = GetCurrentFiber(); + mainContext.interrupted = false; + mainContext.group = &contextGroup; + mainContext.groupPrev = nullptr; + mainContext.groupNext = nullptr; + contextGroup.firstContext = nullptr; + contextGroup.lastContext = nullptr; + contextGroup.firstWaiter = nullptr; + contextGroup.lastWaiter = nullptr; + currentContext = &mainContext; + firstResumingContext = nullptr; + firstReusableContext = nullptr; + runningContextCount = 0; + return; } - BOOL result = CloseHandle(threadHandle); - assert(result == TRUE); + BOOL result2 = CloseHandle(completionPort); + assert(result2 == TRUE); } - BOOL result = ConvertFiberToThread(); + BOOL result2 = ConvertFiberToThread(); assert(result == TRUE); } @@ -81,21 +88,27 @@ Dispatcher::Dispatcher() { } Dispatcher::~Dispatcher() { - assert(resumingContexts.empty()); - assert(reusableContexts.size() == contextCount); - assert(spawningProcedures.empty()); assert(GetCurrentThreadId() == threadId); - while (!reusableContexts.empty()) { - DeleteFiber(reusableContexts.top()); - reusableContexts.pop(); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + interrupt(context); + } + + yield(); + assert(timers.empty()); + assert(contextGroup.firstContext == nullptr); + assert(contextGroup.firstWaiter == nullptr); + assert(firstResumingContext == nullptr); + assert(runningContextCount == 0); + while (firstReusableContext != nullptr) { + void* fiber = firstReusableContext->fiber; + firstReusableContext = firstReusableContext->next; + DeleteFiber(fiber); } int wsaResult = WSACleanup(); assert(wsaResult == 0); BOOL result = CloseHandle(completionPort); assert(result == TRUE); - result = CloseHandle(threadHandle); - assert(result == TRUE); result = ConvertFiberToThread(); assert(result == TRUE); DeleteCriticalSection(reinterpret_cast(criticalSection)); @@ -103,20 +116,20 @@ Dispatcher::~Dispatcher() { void Dispatcher::clear() { assert(GetCurrentThreadId() == threadId); - while (!reusableContexts.empty()) { - DeleteFiber(reusableContexts.top()); - --contextCount; - reusableContexts.pop(); + while (firstReusableContext != nullptr) { + void* fiber = firstReusableContext->fiber; + firstReusableContext = firstReusableContext->next; + DeleteFiber(fiber); } } void Dispatcher::dispatch() { assert(GetCurrentThreadId() == threadId); - void* context; + NativeContext* context; for (;;) { - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } @@ -128,16 +141,16 @@ void Dispatcher::dispatch() { auto timerContextPair = timers.begin(); auto end = timers.end(); while (timerContextPair != end && timerContextPair->first <= currentTime) { - resumingContexts.push(timerContextPair->second); + pushContext(timerContextPair->second); timerContextPair = timers.erase(timerContextPair); } - if (!resumingContexts.empty()) { - context = resumingContexts.front(); - resumingContexts.pop(); + if (firstResumingContext != nullptr) { + context = firstResumingContext; + firstResumingContext = context->next; break; } - + DWORD timeout = timers.empty() ? INFINITE : static_cast(std::min(timers.begin()->first - currentTime, static_cast(INFINITE - 1))); OVERLAPPED_ENTRY entry; ULONG actual = 0; @@ -170,19 +183,55 @@ void Dispatcher::dispatch() { } } - if (context != GetCurrentFiber()) { - SwitchToFiber(context); + if (context != currentContext) { + currentContext = context; + SwitchToFiber(context->fiber); } } -void* Dispatcher::getCurrentContext() const { +NativeContext* Dispatcher::getCurrentContext() const { assert(GetCurrentThreadId() == threadId); - return GetCurrentFiber(); + return currentContext; } -void Dispatcher::pushContext(void* context) { +void Dispatcher::interrupt() { + interrupt(currentContext); +} + +void Dispatcher::interrupt(NativeContext* context) { assert(GetCurrentThreadId() == threadId); - resumingContexts.push(context); + assert(context != nullptr); + if (!context->interrupted) { + if (context->interruptProcedure != nullptr) { + context->interruptProcedure(); + context->interruptProcedure = nullptr; + } else { + context->interrupted = true; + } + } +} + +bool Dispatcher::interrupted() { + if (currentContext->interrupted) { + currentContext->interrupted = false; + return true; + } + + return false; +} + +void Dispatcher::pushContext(NativeContext* context) { + assert(GetCurrentThreadId() == threadId); + assert(context != nullptr); + context->next = nullptr; + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context; + } else { + firstResumingContext = context; + } + + lastResumingContext = context; } void Dispatcher::remoteSpawn(std::function&& procedure) { @@ -201,21 +250,23 @@ void Dispatcher::remoteSpawn(std::function&& procedure) { void Dispatcher::spawn(std::function&& procedure) { assert(GetCurrentThreadId() == threadId); - void* context; - if (reusableContexts.empty()) { - context = CreateFiberEx(16384, 131072, 0, contextProcedureStatic, this); - if (context == NULL) { - throw std::runtime_error("Dispatcher::spawn, CreateFiberEx failed, result=" + std::to_string(GetLastError())); - } - - ++contextCount; + NativeContext* context = &getReusableContext(); + if (contextGroup.firstContext != nullptr) { + context->groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = context; } else { - context = reusableContexts.top(); - reusableContexts.pop(); + context->groupPrev = nullptr; + contextGroup.firstContext = context; + contextGroup.firstWaiter = nullptr; } - resumingContexts.push(context); - spawningProcedures.emplace(std::move(procedure)); + context->interrupted = false; + context->group = &contextGroup; + context->groupNext = nullptr; + context->procedure = std::move(procedure); + contextGroup.lastContext = context; + pushContext(context); } void Dispatcher::yield() { @@ -229,7 +280,8 @@ void Dispatcher::yield() { auto timerContextPair = timers.begin(); auto end = timers.end(); while (timerContextPair != end && timerContextPair->first <= currentTime) { - resumingContexts.push(timerContextPair->second); + timerContextPair->second->interruptProcedure = nullptr; + pushContext(timerContextPair->second); timerContextPair = timers.erase(timerContextPair); } @@ -252,26 +304,27 @@ void Dispatcher::yield() { continue; } - void* context = reinterpret_cast(entries[i].lpOverlapped)->context; - resumingContexts.push(context); + NativeContext* context = reinterpret_cast(entries[i].lpOverlapped)->context; + context->interruptProcedure = nullptr; + pushContext(context); } } else { DWORD lastError = GetLastError(); if (lastError == WAIT_TIMEOUT) { break; } else if (lastError != WAIT_IO_COMPLETION) { - throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); + throw std::runtime_error("Dispatcher::yield, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError)); } } } - if (!resumingContexts.empty()) { - resumingContexts.push(GetCurrentFiber()); + if (firstResumingContext != nullptr) { + pushContext(currentContext); dispatch(); } } -void Dispatcher::addTimer(uint64_t time, void* context) { +void Dispatcher::addTimer(uint64_t time, NativeContext* context) { assert(GetCurrentThreadId() == threadId); timers.insert(std::make_pair(time, context)); } @@ -280,12 +333,36 @@ void* Dispatcher::getCompletionPort() const { return completionPort; } -void Dispatcher::interruptTimer(uint64_t time, void* context) { +NativeContext& Dispatcher::getReusableContext() { + if (firstReusableContext == nullptr) { + void* fiber = CreateFiberEx(STACK_SIZE, RESERVE_STACK_SIZE, 0, contextProcedureStatic, this); + if (fiber == NULL) { + throw std::runtime_error("Dispatcher::getReusableContext, CreateFiberEx failed, result=" + std::to_string(GetLastError())); + } + + SwitchToFiber(fiber); + assert(firstReusableContext != nullptr); + firstReusableContext->fiber = fiber; + } + + NativeContext* context = firstReusableContext; + firstReusableContext = context->next; + return *context; +} + +void Dispatcher::pushReusableContext(NativeContext& context) { + context.next = firstReusableContext; + firstReusableContext = &context; + --runningContextCount; +} + +void Dispatcher::interruptTimer(uint64_t time, NativeContext* context) { assert(GetCurrentThreadId() == threadId); auto range = timers.equal_range(time); - for (auto it = range.first; it != range.second; ++it) { + for (auto it = range.first; ; ++it) { + assert(it != range.second); if (it->second == context) { - resumingContexts.push(context); + pushContext(context); timers.erase(it); break; } @@ -294,12 +371,55 @@ void Dispatcher::interruptTimer(uint64_t time, void* context) { void Dispatcher::contextProcedure() { assert(GetCurrentThreadId() == threadId); + assert(firstReusableContext == nullptr); + NativeContext context; + context.interrupted = false; + context.next = nullptr; + firstReusableContext = &context; + SwitchToFiber(currentContext->fiber); for (;;) { - assert(!spawningProcedures.empty()); - std::function procedure = std::move(spawningProcedures.front()); - spawningProcedures.pop(); - procedure(); - reusableContexts.push(GetCurrentFiber()); + ++runningContextCount; + try { + context.procedure(); + } catch (std::exception&) { + } + + if (context.group != nullptr) { + if (context.groupPrev != nullptr) { + assert(context.groupPrev->groupNext == &context); + context.groupPrev->groupNext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = context.groupPrev; + } else { + assert(context.group->lastContext == &context); + context.group->lastContext = context.groupPrev; + } + } else { + assert(context.group->firstContext == &context); + context.group->firstContext = context.groupNext; + if (context.groupNext != nullptr) { + assert(context.groupNext->groupPrev == &context); + context.groupNext->groupPrev = nullptr; + } else { + assert(context.group->lastContext == &context); + if (context.group->firstWaiter != nullptr) { + if (firstResumingContext != nullptr) { + assert(lastResumingContext->next == nullptr); + lastResumingContext->next = context.group->firstWaiter; + } else { + firstResumingContext = context.group->firstWaiter; + } + + lastResumingContext = context.group->lastWaiter; + context.group->firstWaiter = nullptr; + } + } + } + + pushReusableContext(context); + } + dispatch(); } } diff --git a/src/Platform/Windows/System/Dispatcher.h b/src/Platform/Windows/System/Dispatcher.h index 1d04809b30..0d96f3e08e 100755 --- a/src/Platform/Windows/System/Dispatcher.h +++ b/src/Platform/Windows/System/Dispatcher.h @@ -21,10 +21,29 @@ #include #include #include -#include namespace System { +struct NativeContextGroup; + +struct NativeContext { + void* fiber; + bool interrupted; + NativeContext* next; + NativeContextGroup* group; + NativeContext* groupPrev; + NativeContext* groupNext; + std::function procedure; + std::function interruptProcedure; +}; + +struct NativeContextGroup { + NativeContext* firstContext; + NativeContext* lastContext; + NativeContext* firstWaiter; + NativeContext* lastWaiter; +}; + class Dispatcher { public: Dispatcher(); @@ -33,30 +52,38 @@ class Dispatcher { Dispatcher& operator=(const Dispatcher&) = delete; void clear(); void dispatch(); - void* getCurrentContext() const; - void pushContext(void* context); + NativeContext* getCurrentContext() const; + void interrupt(); + void interrupt(NativeContext* context); + bool interrupted(); + void pushContext(NativeContext* context); void remoteSpawn(std::function&& procedure); - void spawn(std::function&& procedure); void yield(); // Platform-specific - void addTimer(uint64_t time, void* context); + void addTimer(uint64_t time, NativeContext* context); void* getCompletionPort() const; - void interruptTimer(uint64_t time, void* context); + NativeContext& getReusableContext(); + void pushReusableContext(NativeContext&); + void interruptTimer(uint64_t time, NativeContext* context); private: + void spawn(std::function&& procedure); void* completionPort; - std::size_t contextCount; uint8_t criticalSection[2 * sizeof(long) + 4 * sizeof(void*)]; - std::queue resumingContexts; bool remoteNotificationSent; std::queue> remoteSpawningProcedures; uint8_t remoteSpawnOverlapped[4 * sizeof(void*)]; - std::stack reusableContexts; - std::queue> spawningProcedures; - void* threadHandle; uint32_t threadId; - std::multimap timers; + std::multimap timers; + + NativeContext mainContext; + NativeContextGroup contextGroup; + NativeContext* currentContext; + NativeContext* firstResumingContext; + NativeContext* lastResumingContext; + NativeContext* firstReusableContext; + size_t runningContextCount; void contextProcedure(); static void __stdcall contextProcedureStatic(void* context); diff --git a/src/Platform/Windows/System/Ipv4Resolver.cpp b/src/Platform/Windows/System/Ipv4Resolver.cpp index 5d48d6275e..64f03e7acc 100755 --- a/src/Platform/Windows/System/Ipv4Resolver.cpp +++ b/src/Platform/Windows/System/Ipv4Resolver.cpp @@ -22,6 +22,7 @@ #define WIN32_LEAN_AND_MEAN #endif #include +#include #include #include @@ -30,12 +31,11 @@ namespace System { Ipv4Resolver::Ipv4Resolver() : dispatcher(nullptr) { } -Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false) { +Ipv4Resolver::Ipv4Resolver(Dispatcher& dispatcher) : dispatcher(&dispatcher) { } Ipv4Resolver::Ipv4Resolver(Ipv4Resolver&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } } @@ -46,28 +46,15 @@ Ipv4Resolver::~Ipv4Resolver() { Ipv4Resolver& Ipv4Resolver::operator=(Ipv4Resolver&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { - stopped = other.stopped; other.dispatcher = nullptr; } return *this; } -void Ipv4Resolver::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Ipv4Resolver::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - stopped = true; -} - Ipv4Address Ipv4Resolver::resolve(const std::string& host) { assert(dispatcher != nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -78,15 +65,15 @@ Ipv4Address Ipv4Resolver::resolve(const std::string& host) { throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result)); } - std::size_t count = 0; + size_t count = 0; for (addrinfo* addressInfo = addressInfos; addressInfo != nullptr; addressInfo = addressInfo->ai_next) { ++count; } std::mt19937 generator{ std::random_device()() }; - std::size_t index = std::uniform_int_distribution(0, count - 1)(generator); + size_t index = std::uniform_int_distribution(0, count - 1)(generator); addrinfo* addressInfo = addressInfos; - for (std::size_t i = 0; i < index; ++i) { + for (size_t i = 0; i < index; ++i) { addressInfo = addressInfo->ai_next; } diff --git a/src/Platform/Windows/System/Ipv4Resolver.h b/src/Platform/Windows/System/Ipv4Resolver.h index d59d50e0f2..8f4a3fa6f4 100755 --- a/src/Platform/Windows/System/Ipv4Resolver.h +++ b/src/Platform/Windows/System/Ipv4Resolver.h @@ -33,13 +33,10 @@ class Ipv4Resolver { ~Ipv4Resolver(); Ipv4Resolver& operator=(const Ipv4Resolver&) = delete; Ipv4Resolver& operator=(Ipv4Resolver&& other); - void start(); - void stop(); - Ipv4Address resolve(const std::string& host); + Ipv4Address resolve(const std::string& host); private: Dispatcher* dispatcher; - bool stopped; }; } diff --git a/src/Platform/Windows/System/TcpConnection.cpp b/src/Platform/Windows/System/TcpConnection.cpp index 927036d37a..4a8ee6217a 100755 --- a/src/Platform/Windows/System/TcpConnection.cpp +++ b/src/Platform/Windows/System/TcpConnection.cpp @@ -26,14 +26,12 @@ #include #include "Dispatcher.h" -#pragma comment(lib, "Ws2_32.lib") - namespace System { namespace { struct TcpConnectionContext : public OVERLAPPED { - void* context; + NativeContext* context; bool interrupted; }; @@ -47,7 +45,6 @@ TcpConnection::TcpConnection(TcpConnection&& other) : dispatcher(other.dispatche assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -77,7 +74,6 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { assert(other.readContext == nullptr); assert(other.writeContext == nullptr); connection = other.connection; - stopped = other.stopped; readContext = nullptr; writeContext = nullptr; other.dispatcher = nullptr; @@ -86,50 +82,10 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) { return *this; } -void TcpConnection::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnection::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (readContext != nullptr) { - TcpConnectionContext* context = static_cast(readContext); - if (!context->interrupted) { - if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context->interrupted = true; - } - } - - if (writeContext != nullptr) { - TcpConnectionContext* context = static_cast(writeContext); - if (!context->interrupted) { - if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context->interrupted = true; - } - } - - stopped = true; -} - size_t TcpConnection::read(uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(readContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -145,11 +101,30 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { } assert(flags == 0); - context.context = GetCurrentFiber(); + context.context = dispatcher->getCurrentContext(); context.interrupted = false; readContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(readContext != nullptr); + TcpConnectionContext* context = static_cast(readContext); + if (!context->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context->context->interrupted = true; + } + + context->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(readContext == &context); readContext = nullptr; @@ -157,7 +132,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); if (lastError != ERROR_OPERATION_ABORTED) { - throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError)); + throw std::runtime_error("TcpConnection::read, WSAGetOverlappedResult failed, result=" + std::to_string(lastError)); } assert(context.interrupted); @@ -169,10 +144,10 @@ size_t TcpConnection::read(uint8_t* data, size_t size) { return transferred; } -std::size_t TcpConnection::write(const uint8_t* data, size_t size) { +size_t TcpConnection::write(const uint8_t* data, size_t size) { assert(dispatcher != nullptr); assert(writeContext == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -194,11 +169,30 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { } } - context.context = GetCurrentFiber(); + context.context = dispatcher->getCurrentContext(); context.interrupted = false; writeContext = &context; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(writeContext != nullptr); + TcpConnectionContext* context = static_cast(writeContext); + if (!context->interrupted) { + if (CancelIoEx(reinterpret_cast(connection), context) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context->context->interrupted = true; + } + + context->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(writeContext == &context); writeContext = nullptr; @@ -207,7 +201,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) { int lastError = WSAGetLastError(); if (lastError != ERROR_OPERATION_ABORTED) { - throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError)); + throw std::runtime_error("TcpConnection::write, WSAGetOverlappedResult failed, result=" + std::to_string(lastError)); } assert(context.interrupted); @@ -219,7 +213,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) { return transferred; } -std::pair TcpConnection::getPeerAddressAndPort() { +std::pair TcpConnection::getPeerAddressAndPort() const { sockaddr_in address; int size = sizeof(address); if (getpeername(connection, reinterpret_cast(&address), &size) != 0) { @@ -230,7 +224,7 @@ std::pair TcpConnection::getPeerAddressAndPort() { return std::make_pair(Ipv4Address(htonl(address.sin_addr.S_un.S_addr)), htons(address.sin_port)); } -TcpConnection::TcpConnection(Dispatcher& dispatcher, std::size_t connection) : dispatcher(&dispatcher), connection(connection), stopped(false), readContext(nullptr), writeContext(nullptr) { +TcpConnection::TcpConnection(Dispatcher& dispatcher, size_t connection) : dispatcher(&dispatcher), connection(connection), readContext(nullptr), writeContext(nullptr) { } } diff --git a/src/Platform/Windows/System/TcpConnection.h b/src/Platform/Windows/System/TcpConnection.h index cf79c3b96e..a6b0d4bdb3 100755 --- a/src/Platform/Windows/System/TcpConnection.h +++ b/src/Platform/Windows/System/TcpConnection.h @@ -33,23 +33,20 @@ class TcpConnection { ~TcpConnection(); TcpConnection& operator=(const TcpConnection&) = delete; TcpConnection& operator=(TcpConnection&& other); - void start(); - void stop(); - std::size_t read(uint8_t* data, std::size_t size); - std::size_t write(const uint8_t* data, std::size_t size); - std::pair getPeerAddressAndPort(); + size_t read(uint8_t* data, size_t size); + size_t write(const uint8_t* data, size_t size); + std::pair getPeerAddressAndPort() const; private: friend class TcpConnector; friend class TcpListener; Dispatcher* dispatcher; - std::size_t connection; - bool stopped; + size_t connection; void* readContext; void* writeContext; - TcpConnection(Dispatcher& dispatcher, std::size_t connection); + TcpConnection(Dispatcher& dispatcher, size_t connection); }; } diff --git a/src/Platform/Windows/System/TcpConnector.cpp b/src/Platform/Windows/System/TcpConnector.cpp index dc4e810445..5a19ac44d0 100755 --- a/src/Platform/Windows/System/TcpConnector.cpp +++ b/src/Platform/Windows/System/TcpConnector.cpp @@ -32,8 +32,8 @@ namespace System { namespace { struct TcpConnectorContext : public OVERLAPPED { - void* context; - std::size_t connection; + NativeContext* context; + size_t connection; bool interrupted; }; @@ -44,13 +44,12 @@ LPFN_CONNECTEX connectEx = nullptr; TcpConnector::TcpConnector() : dispatcher(nullptr) { } -TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { +TcpConnector::TcpConnector(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } TcpConnector::TcpConnector(TcpConnector&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -65,7 +64,6 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -73,36 +71,10 @@ TcpConnector& TcpConnector::operator=(TcpConnector&& other) { return *this; } -void TcpConnector::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpConnector::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TcpConnectorContext* context2 = static_cast(context); - if (!context2->interrupted) { - if (CancelIoEx(reinterpret_cast(context2->connection), context2) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context2->interrupted = true; - } - } - - stopped = true; -} - TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -140,12 +112,31 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) { if (lastError != WSA_IO_PENDING) { message = "ConnectEx failed, result=" + std::to_string(lastError); } else { - context2.context = GetCurrentFiber(); + context2.context = dispatcher->getCurrentContext(); context2.connection = connection; context2.interrupted = false; context = &context2; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + TcpConnectorContext* context2 = static_cast(context); + if (!context2->interrupted) { + if (CancelIoEx(reinterpret_cast(context2->connection), context2) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context2->context->interrupted = true; + } + + context2->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context2.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context2.context == dispatcher->getCurrentContext()); assert(context2.connection == connection); assert(dispatcher != nullptr); assert(context == &context2); diff --git a/src/Platform/Windows/System/TcpConnector.h b/src/Platform/Windows/System/TcpConnector.h index 562ddfedc6..18762ac57a 100755 --- a/src/Platform/Windows/System/TcpConnector.h +++ b/src/Platform/Windows/System/TcpConnector.h @@ -35,13 +35,10 @@ class TcpConnector { ~TcpConnector(); TcpConnector& operator=(const TcpConnector&) = delete; TcpConnector& operator=(TcpConnector&& other); - void start(); - void stop(); TcpConnection connect(const Ipv4Address& address, uint16_t port); private: Dispatcher* dispatcher; - bool stopped; void* context; }; diff --git a/src/Platform/Windows/System/TcpListener.cpp b/src/Platform/Windows/System/TcpListener.cpp index d64b3106b0..3c546a593d 100755 --- a/src/Platform/Windows/System/TcpListener.cpp +++ b/src/Platform/Windows/System/TcpListener.cpp @@ -32,7 +32,7 @@ namespace System { namespace { struct TcpListenerContext : public OVERLAPPED { - void* context; + NativeContext* context; bool interrupted; }; @@ -67,7 +67,6 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uin if (CreateIoCompletionPort(reinterpret_cast(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) { message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError()); } else { - stopped = false; context = nullptr; return; } @@ -85,7 +84,6 @@ TcpListener::TcpListener(TcpListener&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -111,7 +109,6 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { if (dispatcher != nullptr) { assert(other.context == nullptr); listener = other.listener; - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -119,36 +116,10 @@ TcpListener& TcpListener::operator=(TcpListener&& other) { return *this; } -void TcpListener::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void TcpListener::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TcpListenerContext* context2 = static_cast(context); - if (!context2->interrupted) { - if (CancelIoEx(reinterpret_cast(listener), context2) != TRUE) { - DWORD lastError = GetLastError(); - if (lastError != ERROR_NOT_FOUND) { - throw std::runtime_error("TcpListener::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); - } - } - - context2->interrupted = true; - } - } - - stopped = true; -} - TcpConnection TcpListener::accept() { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -168,11 +139,30 @@ TcpConnection TcpListener::accept() { if (lastError != WSA_IO_PENDING) { message = "AcceptEx failed, result=" + std::to_string(lastError); } else { - context2.context = GetCurrentFiber(); + context2.context = dispatcher->getCurrentContext(); context2.interrupted = false; context = &context2; + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + TcpListenerContext* context2 = static_cast(context); + if (!context2->interrupted) { + if (CancelIoEx(reinterpret_cast(listener), context2) != TRUE) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_NOT_FOUND) { + throw std::runtime_error("TcpListener::stop, CancelIoEx failed, result=" + std::to_string(GetLastError())); + } + + context2->context->interrupted = true; + } + + context2->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(context2.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(context2.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(context == &context2); context = nullptr; diff --git a/src/Platform/Windows/System/TcpListener.h b/src/Platform/Windows/System/TcpListener.h index 3ec2f65bfb..18886de327 100755 --- a/src/Platform/Windows/System/TcpListener.h +++ b/src/Platform/Windows/System/TcpListener.h @@ -35,14 +35,11 @@ class TcpListener { ~TcpListener(); TcpListener& operator=(const TcpListener&) = delete; TcpListener& operator=(TcpListener&& other); - void start(); - void stop(); TcpConnection accept(); private: Dispatcher* dispatcher; - std::size_t listener; - bool stopped; + size_t listener; void* context; }; diff --git a/src/Platform/Windows/System/Timer.cpp b/src/Platform/Windows/System/Timer.cpp index bf3a195ba2..62bc7a5cee 100755 --- a/src/Platform/Windows/System/Timer.cpp +++ b/src/Platform/Windows/System/Timer.cpp @@ -31,7 +31,7 @@ namespace { struct TimerContext { uint64_t time; - void* context; + NativeContext* context; bool interrupted; }; @@ -40,13 +40,12 @@ struct TimerContext { Timer::Timer() : dispatcher(nullptr) { } -Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), stopped(false), context(nullptr) { +Timer::Timer(Dispatcher& dispatcher) : dispatcher(&dispatcher), context(nullptr) { } Timer::Timer(Timer&& other) : dispatcher(other.dispatcher) { if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -61,7 +60,6 @@ Timer& Timer::operator=(Timer&& other) { dispatcher = other.dispatcher; if (dispatcher != nullptr) { assert(other.context == nullptr); - stopped = other.stopped; context = nullptr; other.dispatcher = nullptr; } @@ -69,30 +67,10 @@ Timer& Timer::operator=(Timer&& other) { return *this; } -void Timer::start() { - assert(dispatcher != nullptr); - assert(stopped); - stopped = false; -} - -void Timer::stop() { - assert(dispatcher != nullptr); - assert(!stopped); - if (context != nullptr) { - TimerContext* timerContext = static_cast(context); - if (!timerContext->interrupted) { - dispatcher->interruptTimer(timerContext->time, timerContext->context); - timerContext->interrupted = true; - } - } - - stopped = true; -} - void Timer::sleep(std::chrono::nanoseconds duration) { assert(dispatcher != nullptr); assert(context == nullptr); - if (stopped) { + if (dispatcher->interrupted()) { throw InterruptedException(); } @@ -102,12 +80,22 @@ void Timer::sleep(std::chrono::nanoseconds duration) { QueryPerformanceFrequency(&frequency); uint64_t currentTime = ticks.QuadPart / (frequency.QuadPart / 1000); uint64_t time = currentTime + duration.count() / 1000000; - void* fiber = GetCurrentFiber(); - TimerContext timerContext{ time, fiber, false }; + TimerContext timerContext{ time, dispatcher->getCurrentContext(), false }; context = &timerContext; - dispatcher->addTimer(time, fiber); + dispatcher->addTimer(time, dispatcher->getCurrentContext()); + dispatcher->getCurrentContext()->interruptProcedure = [&]() { + assert(dispatcher != nullptr); + assert(context != nullptr); + TimerContext* timerContext = static_cast(context); + if (!timerContext->interrupted) { + dispatcher->interruptTimer(timerContext->time, timerContext->context); + timerContext->interrupted = true; + } + }; + dispatcher->dispatch(); - assert(timerContext.context == GetCurrentFiber()); + dispatcher->getCurrentContext()->interruptProcedure = nullptr; + assert(timerContext.context == dispatcher->getCurrentContext()); assert(dispatcher != nullptr); assert(context == &timerContext); context = nullptr; diff --git a/src/Platform/Windows/System/Timer.h b/src/Platform/Windows/System/Timer.h index cf2d19b66e..66cf184186 100755 --- a/src/Platform/Windows/System/Timer.h +++ b/src/Platform/Windows/System/Timer.h @@ -32,13 +32,10 @@ class Timer { ~Timer(); Timer& operator=(const Timer&) = delete; Timer& operator=(Timer&& other); - void start(); - void stop(); void sleep(std::chrono::nanoseconds duration); private: Dispatcher* dispatcher; - bool stopped; void* context; }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/Rpc/CoreRpcServerCommandsDefinitions.h old mode 100644 new mode 100755 similarity index 77% rename from src/rpc/core_rpc_server_commands_defs.h rename to src/Rpc/CoreRpcServerCommandsDefinitions.h index 04e070b7ff..1dfa67104a --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/Rpc/CoreRpcServerCommandsDefinitions.h @@ -17,12 +17,12 @@ #pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/difficulty.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/Difficulty.h" #include "crypto/hash.h" -#include "serialization/SerializationOverloads.h" +#include "Serialization/SerializationOverloads.h" namespace CryptoNote { //----------------------------------------------- @@ -58,7 +58,7 @@ struct COMMAND_RPC_GET_HEIGHT { struct COMMAND_RPC_GET_BLOCKS_FAST { struct request { - 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 */ + std::vector 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 */ void serialize(ISerializer &s) { serializeAsBinary(block_ids, "block_ids", s); @@ -66,7 +66,7 @@ struct COMMAND_RPC_GET_BLOCKS_FAST { }; struct response { - std::list blocks; + std::vector blocks; uint64_t start_height; uint64_t current_height; std::string status; @@ -82,7 +82,7 @@ struct COMMAND_RPC_GET_BLOCKS_FAST { //----------------------------------------------- struct COMMAND_RPC_GET_TRANSACTIONS { struct request { - std::list txs_hashes; + std::vector txs_hashes; void serialize(ISerializer &s) { KV_MEMBER(txs_hashes) @@ -90,8 +90,8 @@ struct COMMAND_RPC_GET_TRANSACTIONS { }; struct response { - std::list txs_as_hex; //transactions blobs as hex - std::list missed_tx; //not found transactions + std::vector txs_as_hex; //transactions blobs as hex + std::vector missed_tx; //not found transactions std::string status; void serialize(ISerializer &s) { @@ -104,8 +104,8 @@ struct COMMAND_RPC_GET_TRANSACTIONS { //----------------------------------------------- struct COMMAND_RPC_GET_POOL_CHANGES { struct request { - crypto::hash tailBlockId; - std::vector knownTxsIds; + Crypto::Hash tailBlockId; + std::vector knownTxsIds; void serialize(ISerializer &s) { KV_MEMBER(tailBlockId) @@ -115,8 +115,8 @@ struct COMMAND_RPC_GET_POOL_CHANGES { struct response { bool isTailBlockActual; - std::vector addedTxs; // Added transactions blobs - std::vector deletedTxsIds; // IDs of not found transactions + std::vector addedTxs; // Added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions std::string status; void serialize(ISerializer &s) { @@ -127,11 +127,38 @@ struct COMMAND_RPC_GET_POOL_CHANGES { } }; }; + +struct COMMAND_RPC_GET_POOL_CHANGES_LITE { + struct request { + Crypto::Hash tailBlockId; + std::vector knownTxsIds; + + void serialize(ISerializer &s) { + KV_MEMBER(tailBlockId) + serializeAsBinary(knownTxsIds, "knownTxsIds", s); + } + }; + + struct response { + bool isTailBlockActual; + std::vector addedTxs; // Added transactions blobs + std::vector deletedTxsIds; // IDs of not found transactions + std::string status; + + void serialize(ISerializer &s) { + KV_MEMBER(isTailBlockActual) + KV_MEMBER(addedTxs) + serializeAsBinary(deletedTxsIds, "deletedTxsIds", s); + KV_MEMBER(status) + } + }; +}; + //----------------------------------------------- struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES { struct request { - crypto::hash txid; + Crypto::Hash txid; void serialize(ISerializer &s) { KV_MEMBER(txid) @@ -162,13 +189,13 @@ struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request { #pragma pack(push, 1) struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_out_entry { uint64_t global_amount_index; - crypto::public_key out_key; + Crypto::PublicKey out_key; }; #pragma pack(pop) struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount { uint64_t amount; - std::list outs; + std::vector outs; void serialize(ISerializer &s) { KV_MEMBER(amount) @@ -280,7 +307,7 @@ struct COMMAND_RPC_STOP_DAEMON { // struct COMMAND_RPC_GETBLOCKCOUNT { - typedef std::list request; + typedef std::vector request; struct response { uint64_t count; @@ -313,7 +340,7 @@ struct COMMAND_RPC_GETBLOCKTEMPLATE { uint64_t difficulty; uint32_t height; uint64_t reserved_offset; - blobdata blocktemplate_blob; + std::string blocktemplate_blob; std::string status; void serialize(ISerializer &s) { @@ -343,7 +370,7 @@ struct COMMAND_RPC_SUBMITBLOCK { typedef STATUS_STRUCT response; }; -struct block_header_responce { +struct block_header_response { uint8_t major_version; uint8_t minor_version; uint64_t timestamp; @@ -373,7 +400,7 @@ struct block_header_responce { struct BLOCK_HEADER_RESPONSE { std::string status; - block_header_responce block_header; + block_header_response block_header; void serialize(ISerializer &s) { KV_MEMBER(block_header) @@ -413,7 +440,7 @@ struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT { struct COMMAND_RPC_QUERY_BLOCKS { struct request { - 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 */ + std::vector 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 timestamp; void serialize(ISerializer &s) { @@ -427,7 +454,7 @@ struct COMMAND_RPC_QUERY_BLOCKS { uint64_t start_height; uint64_t current_height; uint64_t full_offset; - std::list items; + std::vector items; void serialize(ISerializer &s) { KV_MEMBER(status) @@ -439,4 +466,32 @@ struct COMMAND_RPC_QUERY_BLOCKS { }; }; +struct COMMAND_RPC_QUERY_BLOCKS_LITE { + struct request { + std::vector blockIds; + uint64_t timestamp; + + void serialize(ISerializer &s) { + serializeAsBinary(blockIds, "block_ids", s); + KV_MEMBER(timestamp) + } + }; + + struct response { + std::string status; + uint64_t startHeight; + uint64_t currentHeight; + uint64_t fullOffset; + std::vector items; + + void serialize(ISerializer &s) { + KV_MEMBER(status) + KV_MEMBER(startHeight) + KV_MEMBER(currentHeight) + KV_MEMBER(fullOffset) + KV_MEMBER(items) + } + }; +}; + } diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/Rpc/CoreRpcServerErrorCodes.h old mode 100644 new mode 100755 similarity index 100% rename from src/rpc/core_rpc_server_error_codes.h rename to src/Rpc/CoreRpcServerErrorCodes.h diff --git a/src/rpc/HttpClient.cpp b/src/Rpc/HttpClient.cpp similarity index 84% rename from src/rpc/HttpClient.cpp rename to src/Rpc/HttpClient.cpp index 36b6f3473e..83ddf90ed9 100644 --- a/src/rpc/HttpClient.cpp +++ b/src/Rpc/HttpClient.cpp @@ -28,6 +28,12 @@ HttpClient::HttpClient(System::Dispatcher& dispatcher, const std::string& addres m_dispatcher(dispatcher), m_address(address), m_port(port) { } +HttpClient::~HttpClient() { + if (m_connected) { + disconnect(); + } +} + void HttpClient::request(const HttpRequest &req, HttpResponse &res) { if (!m_connected) { connect(); @@ -54,7 +60,18 @@ void HttpClient::connect() { void HttpClient::disconnect() { m_streamBuf.reset(); - m_connection = System::TcpConnection(); + try { + m_connection.write(nullptr, 0); //Socket shutdown. + } catch (std::exception&) { + //Ignoring possible exception. + } + + try { + m_connection = System::TcpConnection(); + } catch (std::exception&) { + //Ignoring possible exception. + } + m_connected = false; } diff --git a/src/rpc/HttpClient.h b/src/Rpc/HttpClient.h old mode 100644 new mode 100755 similarity index 97% rename from src/rpc/HttpClient.h rename to src/Rpc/HttpClient.h index 0e351d7fb9..7c1ee1ead6 --- a/src/rpc/HttpClient.h +++ b/src/Rpc/HttpClient.h @@ -24,7 +24,7 @@ #include #include -#include "serialization/SerializationTools.h" +#include "Serialization/SerializationTools.h" namespace CryptoNote { @@ -32,6 +32,7 @@ class HttpClient { public: HttpClient(System::Dispatcher& dispatcher, const std::string& address, uint16_t port); + ~HttpClient(); void request(const HttpRequest& req, HttpResponse& res); private: diff --git a/src/rpc/HttpServer.cpp b/src/Rpc/HttpServer.cpp old mode 100644 new mode 100755 similarity index 83% rename from src/rpc/HttpServer.cpp rename to src/Rpc/HttpServer.cpp index 9386fea199..a0b230434f --- a/src/rpc/HttpServer.cpp +++ b/src/Rpc/HttpServer.cpp @@ -28,30 +28,22 @@ using namespace Logging; namespace CryptoNote { HttpServer::HttpServer(System::Dispatcher& dispatcher, Logging::ILogger& log) - : m_dispatcher(dispatcher), logger(log, "HttpServer"), m_shutdownCompleteEvent(dispatcher) { + : m_dispatcher(dispatcher), workingContextGroup(dispatcher), logger(log, "HttpServer") { } void HttpServer::start(const std::string& address, uint16_t port) { m_listener = System::TcpListener(m_dispatcher, System::Ipv4Address(address), port); - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&HttpServer::acceptLoop, this)); + workingContextGroup.spawn(std::bind(&HttpServer::acceptLoop, this)); } void HttpServer::stop() { - m_listener.stop(); - for (auto connPtr : m_connections) { - connPtr->stop(); - } - - if (m_spawnCount) { - m_shutdownCompleteEvent.wait(); - } + workingContextGroup.interrupt(); + workingContextGroup.wait(); } void HttpServer::acceptLoop() { try { - System::TcpConnection connection; bool accepted = false; @@ -74,8 +66,7 @@ void HttpServer::acceptLoop() { logger(DEBUGGING) << "Incoming connection from " << addr.first.toDottedDecimal() << ":" << addr.second; - ++m_spawnCount; - m_dispatcher.spawn(std::bind(&HttpServer::acceptLoop, this)); + workingContextGroup.spawn(std::bind(&HttpServer::acceptLoop, this)); System::TcpStreambuf streambuf(connection); std::iostream stream(&streambuf); @@ -90,6 +81,10 @@ void HttpServer::acceptLoop() { stream << resp; stream.flush(); + + if (stream.peek() == std::iostream::traits_type::eof()) { + break; + } } logger(DEBUGGING) << "Closing connection from " << addr.first.toDottedDecimal() << ":" << addr.second << " total=" << m_connections.size(); @@ -98,10 +93,6 @@ void HttpServer::acceptLoop() { } catch (std::exception& e) { logger(WARNING) << "Connection error: " << e.what(); } - - if (--m_spawnCount == 0) { - m_shutdownCompleteEvent.set(); - } } } diff --git a/src/rpc/HttpServer.h b/src/Rpc/HttpServer.h old mode 100644 new mode 100755 similarity index 95% rename from src/rpc/HttpServer.h rename to src/Rpc/HttpServer.h index 6139e090e6..ca5535b639 --- a/src/rpc/HttpServer.h +++ b/src/Rpc/HttpServer.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -51,10 +52,9 @@ class HttpServer { void acceptLoop(); void connectionHandler(System::TcpConnection&& conn); + System::ContextGroup workingContextGroup; Logging::LoggerRef logger; System::TcpListener m_listener; - System::Event m_shutdownCompleteEvent; - size_t m_spawnCount = 0; std::unordered_set m_connections; }; diff --git a/src/rpc/JsonRpc.cpp b/src/Rpc/JsonRpc.cpp old mode 100644 new mode 100755 similarity index 98% rename from src/rpc/JsonRpc.cpp rename to src/Rpc/JsonRpc.cpp index f45675080e..57a214e652 --- a/src/rpc/JsonRpc.cpp +++ b/src/Rpc/JsonRpc.cpp @@ -16,7 +16,7 @@ // along with Bytecoin. If not, see . #include "JsonRpc.h" -#include "rpc/HttpClient.h" +#include "Rpc/HttpClient.h" namespace CryptoNote { diff --git a/src/rpc/JsonRpc.h b/src/Rpc/JsonRpc.h old mode 100644 new mode 100755 similarity index 98% rename from src/rpc/JsonRpc.h rename to src/Rpc/JsonRpc.h index ee1ca60dec..dc4840417c --- a/src/rpc/JsonRpc.h +++ b/src/Rpc/JsonRpc.h @@ -21,8 +21,8 @@ #include #include -#include "serialization/ISerializer.h" -#include "serialization/SerializationTools.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationTools.h" #include namespace CryptoNote { diff --git a/src/rpc/RpcServer.cpp b/src/Rpc/RpcServer.cpp old mode 100644 new mode 100755 similarity index 78% rename from src/rpc/RpcServer.cpp rename to src/Rpc/RpcServer.cpp index 594b17fae2..e372279b84 --- a/src/rpc/RpcServer.cpp +++ b/src/Rpc/RpcServer.cpp @@ -21,16 +21,22 @@ #include // CryptoNote -#include "cryptonote_core/cryptonote_core.h" -#include "cryptonote_core/miner.h" -#include "p2p/net_node.h" - -#include "core_rpc_server_error_codes.h" +#include "Common/StringTools.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/Core.h" +#include "CryptoNoteCore/IBlock.h" +#include "CryptoNoteCore/Miner.h" +#include "CryptoNoteCore/TransactionExtra.h" +#include "P2p/NetNode.h" + +#include "CoreRpcServerErrorCodes.h" #include "JsonRpc.h" #undef ERROR using namespace Logging; +using namespace Crypto; +using namespace Common; namespace CryptoNote { @@ -77,9 +83,11 @@ std::unordered_map RpcServer::s_handler // binary handlers { "/getblocks.bin", binMethod(&RpcServer::on_get_blocks) }, { "/queryblocks.bin", binMethod(&RpcServer::on_query_blocks) }, + { "/queryblockslite.bin", binMethod(&RpcServer::on_query_blocks_lite) }, { "/get_o_indexes.bin", binMethod(&RpcServer::on_get_indexes) }, { "/getrandom_outs.bin", binMethod(&RpcServer::on_get_random_outs) }, { "/get_pool_changes.bin", binMethod(&RpcServer::onGetPoolChanges) }, + { "/get_pool_changes_lite.bin", binMethod(&RpcServer::onGetPoolChangesLite) }, // json handlers { "/getinfo", jsonMethod(&RpcServer::on_get_info) }, @@ -94,7 +102,7 @@ std::unordered_map RpcServer::s_handler { "/json_rpc", std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) } }; -RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, node_server& p2p) : +RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p) : HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(p2p) { } @@ -174,18 +182,35 @@ bool RpcServer::checkCoreReady() { // bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { - - std::list>> bs; - if (!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { + // TODO code duplication see InProcessNode::doGetNewBlocks() + if (req.block_ids.empty()) { + res.status = "Failed"; + return false; + } + + if (req.block_ids.back() != m_core.getBlockIdByHeight(0)) { res.status = "Failed"; return false; } - for (auto& b : bs) { + uint32_t totalBlockCount; + uint32_t startBlockIndex; + std::vector supplement = m_core.findBlockchainSupplement(req.block_ids, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT, totalBlockCount, startBlockIndex); + + res.current_height = totalBlockCount; + res.start_height = startBlockIndex; + + for (const auto& blockId : supplement) { + assert(m_core.have_block(blockId)); + auto completeBlock = m_core.getBlock(blockId); + assert(completeBlock != nullptr); + res.blocks.resize(res.blocks.size() + 1); - res.blocks.back().block = block_to_blob(b.first); - for (auto& t : b.second) { - res.blocks.back().txs.push_back(tx_to_blob(t)); + res.blocks.back().block = asString(toBinaryArray(completeBlock->getBlock())); + + res.blocks.back().txs.reserve(completeBlock->getTransactionCount()); + for (size_t i = 0; i < completeBlock->getTransactionCount(); ++i) { + res.blocks.back().txs.push_back(asString(toBinaryArray(completeBlock->getTransaction(i)))); } } @@ -196,11 +221,36 @@ bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, C bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res) { CHECK_CORE_READY(); - if (!m_core.queryBlocks(req.block_ids, req.timestamp, res.start_height, res.current_height, res.full_offset, res.items)) { + uint32_t startHeight; + uint32_t currentHeight; + uint32_t fullOffset; + + if (!m_core.queryBlocks(req.block_ids, req.timestamp, startHeight, currentHeight, fullOffset, res.items)) { + res.status = "Failed to perform query"; + return false; + } + + res.start_height = startHeight; + res.current_height = currentHeight; + res.full_offset = fullOffset; + res.status = CORE_RPC_STATUS_OK; + return true; +} + +bool RpcServer::on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res) { + CHECK_CORE_READY(); + + uint32_t startHeight; + uint32_t currentHeight; + uint32_t fullOffset; + if (!m_core.queryBlocksLite(req.blockIds, req.timestamp, startHeight, currentHeight, fullOffset, res.items)) { res.status = "Failed to perform query"; return false; } + res.startHeight = startHeight; + res.currentHeight = currentHeight; + res.fullOffset = fullOffset; res.status = CORE_RPC_STATUS_OK; return true; } @@ -208,11 +258,13 @@ bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, CO bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { CHECK_CORE_READY(); - if (!m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes)) { + std::vector outputIndexes; + if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) { res.status = "Failed"; return true; } + res.o_indexes.assign(outputIndexes.begin(), outputIndexes.end()); res.status = CORE_RPC_STATUS_OK; logger(TRACE) << "COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"; return true; @@ -256,8 +308,8 @@ bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& re rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds); if (rsp.isTailBlockActual) { for (auto& tx : addedTransactions) { - blobdata txBlob; - if (!CryptoNote::tx_to_blob(tx, txBlob)) { + BinaryArray txBlob; + if (!toBinaryArray(tx, txBlob)) { rsp.status = "Internal error"; break;; } @@ -270,21 +322,30 @@ bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& re } +bool RpcServer::onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp) { + CHECK_CORE_READY(); + + rsp.status = CORE_RPC_STATUS_OK; + rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, req.knownTxsIds, rsp.addedTxs, rsp.deletedTxsIds); + + return true; +} + // // JSON handlers // bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { res.height = m_core.get_current_blockchain_height(); - res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); - res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase + res.difficulty = m_core.getNextBlockDifficulty(); + res.tx_count = m_core.get_blockchain_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(); + res.alt_blocks_count = m_core.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.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.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); + res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); res.status = CORE_RPC_STATUS_OK; return true; } @@ -298,27 +359,26 @@ bool RpcServer::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAN bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { CHECK_CORE_READY(); - std::vector vh; + std::vector vh; for (const auto& tx_hex_str : req.txs_hashes) { - blobdata b; - if (!hexToBlob(tx_hex_str, b)) + BinaryArray b; + if (!fromHex(tx_hex_str, b)) { res.status = "Failed to parse hex representation of transaction hash"; return true; } - if (b.size() != sizeof(crypto::hash)) + if (b.size() != sizeof(Hash)) { res.status = "Failed, size of data mismatch"; } - vh.push_back(*reinterpret_cast(b.data())); + vh.push_back(*reinterpret_cast(b.data())); } - std::list missed_txs; + std::list missed_txs; std::list txs; m_core.getTransactions(vh, txs, missed_txs); for (auto& tx : txs) { - blobdata blob = t_serializable_object_to_blob(tx); - res.txs_as_hex.push_back(blobToHex(blob)); + res.txs_as_hex.push_back(toHex(toBinaryArray(tx))); } for (const auto& miss_tx : missed_txs) { @@ -332,8 +392,8 @@ bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { CHECK_CORE_READY(); - std::string tx_blob; - if (!hexToBlob(req.tx_as_hex, tx_blob)) + BinaryArray tx_blob; + if (!fromHex(req.tx_as_hex, tx_blob)) { logger(INFO) << "[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex; res.status = "Failed"; @@ -364,7 +424,7 @@ bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMM NOTIFY_NEW_TRANSACTIONS::request r; - r.txs.push_back(tx_blob); + r.txs.push_back(asString(tx_blob)); m_core.get_protocol()->relay_transactions(r); //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes res.status = CORE_RPC_STATUS_OK; @@ -379,10 +439,7 @@ bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, CO return true; } - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - - if (!m_core.get_miner().start(adr, static_cast(req.threads_count), attrs)) { + if (!m_core.get_miner().start(adr, static_cast(req.threads_count))) { res.status = "Failed, mining not started"; return true; } @@ -404,7 +461,7 @@ bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMM bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { CHECK_CORE_READY(); if (m_core.currency().isTestnet()) { - m_p2p.send_stop_signal(); + m_p2p.sendStopSignal(); res.status = CORE_RPC_STATUS_OK; } else { res.status = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -427,15 +484,16 @@ bool RpcServer::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, CO throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong parameters, expected height" }; } - uint64_t h = req[0]; - if (m_core.get_current_blockchain_height() <= h) { + uint32_t h = static_cast(req[0]); + Crypto::Hash blockId = m_core.getBlockIdByHeight(h); + if (blockId == NULL_HASH) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT, std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; } - res = Common::podToHex(m_core.getBlockIdByHeight(h)); + res = Common::podToHex(blockId); return true; } @@ -468,16 +526,16 @@ bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& } Block b = boost::value_initialized(); - CryptoNote::blobdata blob_reserve; + CryptoNote::BinaryArray blob_reserve; blob_reserve.resize(req.reserve_size, 0); if (!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) { logger(ERROR) << "Failed to create block template"; throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to create block template" }; } - blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = CryptoNote::get_tx_pub_key_from_extra(b.minerTx); - if (tx_pub_key == null_pkey) { + BinaryArray block_blob = toBinaryArray(b); + PublicKey tx_pub_key = CryptoNote::getTransactionPublicKeyFromExtra(b.baseTransaction.extra); + if (tx_pub_key == NULL_PUBLIC_KEY) { logger(ERROR) << "Failed to find tx pub key in coinbase extra"; throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: failed to find tx pub key in coinbase extra" }; } @@ -497,16 +555,15 @@ bool RpcServer::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& res.reserved_offset = 0; } - res.blocktemplate_blob = blobToHex(block_blob); + res.blocktemplate_blob = toHex(block_blob); res.status = CORE_RPC_STATUS_OK; return true; } bool RpcServer::on_get_currency_id(const COMMAND_RPC_GET_CURRENCY_ID::request& /*req*/, COMMAND_RPC_GET_CURRENCY_ID::response& res) { - crypto::hash currencyId = m_core.currency().genesisBlockHash(); - blobdata blob = t_serializable_object_to_blob(currencyId); - res.currency_id_blob = blobToHex(blob); + Hash currencyId = m_core.currency().genesisBlockHash(); + res.currency_id_blob = Common::podToHex(currencyId); return true; } @@ -515,20 +572,14 @@ bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMM throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_PARAM, "Wrong param" }; } - blobdata blockblob; - if (!hexToBlob(req[0], blockblob)) { + BinaryArray blockblob; + if (!fromHex(req[0], blockblob)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB, "Wrong block blob" }; } block_verification_context bvc = boost::value_initialized(); - System::Event event(m_dispatcher); - auto resultFuture = std::async(std::launch::async, [this, &event, &bvc, &blockblob]{ - m_core.handle_incoming_block_blob(blockblob, bvc, true, true); - m_dispatcher.remoteSpawn([&event]() { event.set(); }); - }); - event.wait(); - resultFuture.get(); + m_core.handle_incoming_block_blob(blockblob, bvc, true, true); if (!bvc.m_added_to_main_chain) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED, "Block not accepted" }; @@ -542,47 +593,45 @@ bool RpcServer::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMM namespace { uint64_t get_block_reward(const Block& blk) { uint64_t reward = 0; - for (const TransactionOutput& out : blk.minerTx.vout) { + for (const TransactionOutput& out : blk.baseTransaction.outputs) { reward += out.amount; } return reward; } } -void RpcServer::fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce) { +void RpcServer::fill_block_header_response(const Block& blk, bool orphan_status, uint64_t height, const Hash& hash, block_header_response& responce) { responce.major_version = blk.majorVersion; responce.minor_version = blk.minorVersion; responce.timestamp = blk.timestamp; - responce.prev_hash = Common::podToHex(blk.prevId); + responce.prev_hash = Common::podToHex(blk.previousBlockHash); responce.nonce = blk.nonce; responce.orphan_status = orphan_status; responce.height = height; responce.depth = m_core.get_current_blockchain_height() - height - 1; responce.hash = Common::podToHex(hash); - responce.difficulty = m_core.get_blockchain_storage().block_difficulty(height); + m_core.getBlockDifficulty(static_cast(height), responce.difficulty); responce.reward = get_block_reward(blk); } bool RpcServer::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res) { - uint64_t last_block_height; - crypto::hash last_block_hash; + uint32_t last_block_height; + Hash last_block_hash; - if (!m_core.get_blockchain_top(last_block_height, last_block_hash)) { - throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; - } + m_core.get_blockchain_top(last_block_height, last_block_hash); Block last_block; if (!m_core.getBlockByHash(last_block_hash, last_block)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get last block hash." }; } - fill_block_header_responce(last_block, false, last_block_height, last_block_hash, res.block_header); + fill_block_header_response(last_block, false, last_block_height, last_block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; } bool RpcServer::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) { - crypto::hash block_hash; + Hash block_hash; if (!parse_hash256(req.hash, block_hash)) { throw JsonRpc::JsonRpcError{ @@ -597,14 +646,14 @@ bool RpcServer::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_B "Internal error: can't get block by hash. Hash = " + req.hash + '.' }; } - if (blk.minerTx.vin.front().type() != typeid(TransactionInputGenerate)) { + if (blk.baseTransaction.inputs.front().type() != typeid(BaseInput)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: coinbase transaction in the block has the wrong type" }; } - uint64_t block_height = boost::get(blk.minerTx.vin.front()).height; - fill_block_header_responce(blk, false, block_height, block_hash, res.block_header); + uint64_t block_height = boost::get(blk.baseTransaction.inputs.front()).blockIndex; + fill_block_header_response(blk, false, block_height, block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; } @@ -615,14 +664,14 @@ bool RpcServer::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()) }; } - crypto::hash block_hash = m_core.getBlockIdByHeight(req.height); + Hash block_hash = m_core.getBlockIdByHeight(static_cast(req.height)); Block blk; if (!m_core.getBlockByHash(block_hash, blk)) { throw JsonRpc::JsonRpcError{ CORE_RPC_ERROR_CODE_INTERNAL_ERROR, "Internal error: can't get block by height. Height = " + std::to_string(req.height) + '.' }; } - fill_block_header_responce(blk, false, req.height, block_hash, res.block_header); + fill_block_header_response(blk, false, req.height, block_hash, res.block_header); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/RpcServer.h b/src/Rpc/RpcServer.h old mode 100644 new mode 100755 similarity index 88% rename from src/rpc/RpcServer.h rename to src/Rpc/RpcServer.h index 6c6ecc2a5f..d4567ff6e5 --- a/src/rpc/RpcServer.h +++ b/src/Rpc/RpcServer.h @@ -21,16 +21,16 @@ #include #include -#include "core_rpc_server_commands_defs.h" +#include "CoreRpcServerCommandsDefinitions.h" namespace CryptoNote { class core; -class node_server; +class NodeServer; class RpcServer : public HttpServer { public: - RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, node_server& p2p); + RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p); typedef std::function HandlerFunction; @@ -46,9 +46,11 @@ class RpcServer : public HttpServer { // binary handlers bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); bool on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res); + bool on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res); bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::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 onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp); + bool onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp); // json handlers bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); @@ -69,11 +71,11 @@ class RpcServer : public HttpServer { 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); 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); - void fill_block_header_responce(const Block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_responce& responce); + void fill_block_header_response(const Block& blk, bool orphan_status, uint64_t height, const Crypto::Hash& hash, block_header_response& responce); Logging::LoggerRef logger; core& m_core; - node_server& m_p2p; + NodeServer& m_p2p; }; } diff --git a/src/rpc/RpcServerConfig.cpp b/src/Rpc/RpcServerConfig.cpp old mode 100644 new mode 100755 similarity index 96% rename from src/rpc/RpcServerConfig.cpp rename to src/Rpc/RpcServerConfig.cpp index 770d2ca5c6..b7276aaa11 --- a/src/rpc/RpcServerConfig.cpp +++ b/src/Rpc/RpcServerConfig.cpp @@ -16,8 +16,8 @@ // along with Bytecoin. If not, see . #include "RpcServerConfig.h" -#include "Common/command_line.h" -#include "cryptonote_config.h" +#include "Common/CommandLine.h" +#include "CryptoNoteConfig.h" namespace CryptoNote { diff --git a/src/rpc/RpcServerConfig.h b/src/Rpc/RpcServerConfig.h similarity index 100% rename from src/rpc/RpcServerConfig.h rename to src/Rpc/RpcServerConfig.h diff --git a/src/serialization/BinaryInputStreamSerializer.cpp b/src/Serialization/BinaryInputStreamSerializer.cpp similarity index 72% rename from src/serialization/BinaryInputStreamSerializer.cpp rename to src/Serialization/BinaryInputStreamSerializer.cpp index bcb80df86b..10b70cc38a 100644 --- a/src/serialization/BinaryInputStreamSerializer.cpp +++ b/src/Serialization/BinaryInputStreamSerializer.cpp @@ -16,54 +16,26 @@ // along with Bytecoin. If not, see . #include "BinaryInputStreamSerializer.h" -#include "SerializationOverloads.h" #include #include #include +#include +#include "SerializationOverloads.h" -namespace { +using namespace Common; -template::digits> -typename std::enable_if::value && std::is_unsigned::value, size_t>::type -readVarint(std::istream& s, T &i) { - size_t read = 0; - i = 0; - for (int shift = 0;; shift += 7) { - if (s.eof()) { - return read; - } - uint8_t byte = s.get(); - if (!s) { - throw std::runtime_error("Stream read error"); - } - ++read; - if (shift + 7 >= bits && byte >= 1 << (bits - shift)) { - throw std::runtime_error("Varint overflow"); - } - if (byte == 0 && shift != 0) { - throw std::runtime_error("Non-canonical varint representation"); - } - i |= static_cast(byte & 0x7f) << shift; - if ((byte & 0x80) == 0) { - break; - } - } - return read; -} +namespace CryptoNote { + +namespace { template -void readVarintAs(std::istream& s, T &i) { - StorageType v; - readVarint(s, v); - i = static_cast(v); +void readVarintAs(IInputStream& s, T &i) { + i = static_cast(readVarint(s)); } - } -namespace CryptoNote { - ISerializer::SerializerType BinaryInputStreamSerializer::type() const { return ISerializer::INPUT; } @@ -75,7 +47,7 @@ bool BinaryInputStreamSerializer::beginObject(Common::StringView name) { void BinaryInputStreamSerializer::endObject() { } -bool BinaryInputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool BinaryInputStreamSerializer::beginArray(size_t& size, Common::StringView name) { readVarintAs(stream, size); return true; } @@ -88,6 +60,16 @@ bool BinaryInputStreamSerializer::operator()(uint8_t& value, Common::StringView return true; } +bool BinaryInputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + readVarint(stream, value); + return true; +} + +bool BinaryInputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + readVarintAs(stream, value); + return true; +} + bool BinaryInputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { readVarint(stream, value); return true; @@ -109,7 +91,7 @@ bool BinaryInputStreamSerializer::operator()(uint64_t& value, Common::StringView } bool BinaryInputStreamSerializer::operator()(bool& value, Common::StringView name) { - value = stream.get() != 0; + value = read(stream) != 0; return true; } @@ -130,7 +112,7 @@ bool BinaryInputStreamSerializer::operator()(std::string& value, Common::StringV return true; } -bool BinaryInputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool BinaryInputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { checkedRead(static_cast(value), size); return true; } @@ -146,9 +128,7 @@ bool BinaryInputStreamSerializer::operator()(double& value, Common::StringView n } void BinaryInputStreamSerializer::checkedRead(char* buf, size_t size) { - if (stream.read(buf, size).gcount() != size) { - throw std::runtime_error("Stream read error"); - } + read(stream, buf, size); } } diff --git a/src/serialization/BinaryInputStreamSerializer.h b/src/Serialization/BinaryInputStreamSerializer.h similarity index 81% rename from src/serialization/BinaryInputStreamSerializer.h rename to src/Serialization/BinaryInputStreamSerializer.h index 002b8024c4..2bf2327cc4 100644 --- a/src/serialization/BinaryInputStreamSerializer.h +++ b/src/Serialization/BinaryInputStreamSerializer.h @@ -17,16 +17,15 @@ #pragma once +#include #include "ISerializer.h" #include "SerializationOverloads.h" -#include - namespace CryptoNote { class BinaryInputStreamSerializer : public ISerializer { public: - BinaryInputStreamSerializer(std::istream& strm) : stream(strm) {} + BinaryInputStreamSerializer(Common::IInputStream& strm) : stream(strm) {} virtual ~BinaryInputStreamSerializer() {} virtual ISerializer::SerializerType type() const; @@ -34,10 +33,12 @@ class BinaryInputStreamSerializer : public ISerializer { virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -45,7 +46,7 @@ class BinaryInputStreamSerializer : public ISerializer { virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template @@ -56,7 +57,7 @@ class BinaryInputStreamSerializer : public ISerializer { private: void checkedRead(char* buf, size_t size); - std::istream& stream; + Common::IInputStream& stream; }; } diff --git a/src/serialization/BinaryOutputStreamSerializer.cpp b/src/Serialization/BinaryOutputStreamSerializer.cpp similarity index 82% rename from src/serialization/BinaryOutputStreamSerializer.cpp rename to src/Serialization/BinaryOutputStreamSerializer.cpp index 9e9c3e6a33..6cdb1e0c87 100644 --- a/src/serialization/BinaryOutputStreamSerializer.cpp +++ b/src/Serialization/BinaryOutputStreamSerializer.cpp @@ -19,24 +19,9 @@ #include #include +#include "Common/StreamTools.h" -namespace { - -template -typename std::enable_if::value && std::is_unsigned::value, void>::type -writeVarint(std::ostream& s, T i) { - while (i >= 0x80) { - s.put((static_cast(i)& 0x7f) | 0x80); - i >>= 7; - } - s.put(static_cast(i)); - - if (!s) { - throw std::runtime_error("Stream write error"); - } -} - -} +using namespace Common; namespace CryptoNote { @@ -51,7 +36,7 @@ bool BinaryOutputStreamSerializer::beginObject(Common::StringView name) { void BinaryOutputStreamSerializer::endObject() { } -bool BinaryOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool BinaryOutputStreamSerializer::beginArray(size_t& size, Common::StringView name) { writeVarint(stream, size); return true; } @@ -64,6 +49,16 @@ bool BinaryOutputStreamSerializer::operator()(uint8_t& value, Common::StringView return true; } +bool BinaryOutputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + writeVarint(stream, value); + return true; +} + +bool BinaryOutputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + writeVarint(stream, static_cast(value)); + return true; +} + bool BinaryOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { writeVarint(stream, value); return true; @@ -96,7 +91,7 @@ bool BinaryOutputStreamSerializer::operator()(std::string& value, Common::String return true; } -bool BinaryOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool BinaryOutputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { checkedWrite(static_cast(value), size); return true; } @@ -113,10 +108,7 @@ bool BinaryOutputStreamSerializer::operator()(double& value, Common::StringView } void BinaryOutputStreamSerializer::checkedWrite(const char* buf, size_t size) { - stream.write(buf, size); - if (!stream) { - throw std::runtime_error("Stream write error"); - } + write(stream, buf, size); } } diff --git a/src/serialization/BinaryOutputStreamSerializer.h b/src/Serialization/BinaryOutputStreamSerializer.h similarity index 81% rename from src/serialization/BinaryOutputStreamSerializer.h rename to src/Serialization/BinaryOutputStreamSerializer.h index e1dc350036..5d585c777f 100644 --- a/src/serialization/BinaryOutputStreamSerializer.h +++ b/src/Serialization/BinaryOutputStreamSerializer.h @@ -17,16 +17,15 @@ #pragma once +#include "Common/IOutputStream.h" #include "ISerializer.h" #include "SerializationOverloads.h" -#include - namespace CryptoNote { class BinaryOutputStreamSerializer : public ISerializer { public: - BinaryOutputStreamSerializer(std::ostream& strm) : stream(strm) {} + BinaryOutputStreamSerializer(Common::IOutputStream& strm) : stream(strm) {} virtual ~BinaryOutputStreamSerializer() {} virtual ISerializer::SerializerType type() const; @@ -34,10 +33,12 @@ class BinaryOutputStreamSerializer : public ISerializer { virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -45,7 +46,7 @@ class BinaryOutputStreamSerializer : public ISerializer { virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template @@ -55,7 +56,7 @@ class BinaryOutputStreamSerializer : public ISerializer { private: void checkedWrite(const char* buf, size_t size); - std::ostream& stream; + Common::IOutputStream& stream; }; } diff --git a/src/Serialization/BinarySerializationTools.h b/src/Serialization/BinarySerializationTools.h new file mode 100644 index 0000000000..e3273ccb1b --- /dev/null +++ b/src/Serialization/BinarySerializationTools.h @@ -0,0 +1,89 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include +#include "BinaryInputStreamSerializer.h" +#include "BinaryOutputStreamSerializer.h" +#include "Common/MemoryInputStream.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Common/VectorOutputStream.h" + +#include + +namespace CryptoNote { + +template +BinaryArray storeToBinary(const T& obj) { + BinaryArray result; + Common::VectorOutputStream stream(result); + BinaryOutputStreamSerializer ba(stream); + serialize(const_cast(obj), ba); + return result; +} + +template +void loadFromBinary(T& obj, const BinaryArray& blob) { + Common::MemoryInputStream stream(blob.data(), blob.size()); + BinaryInputStreamSerializer ba(stream); + serialize(obj, ba); +} + +template +bool storeToBinaryFile(const T& obj, const std::string& filename) { + try { + std::ofstream dataFile; + dataFile.open(filename, std::ios_base::binary | std::ios_base::out | std::ios::trunc); + if (dataFile.fail()) { + return false; + } + + Common::StdOutputStream stream(dataFile); + BinaryOutputStreamSerializer out(stream); + CryptoNote::serialize(const_cast(obj), out); + + if (dataFile.fail()) { + return false; + } + + dataFile.flush(); + } catch (std::exception&) { + return false; + } + + return true; +} + +template +bool loadFromBinaryFile(T& obj, const std::string& filename) { + try { + std::ifstream dataFile; + dataFile.open(filename, std::ios_base::binary | std::ios_base::in); + if (dataFile.fail()) { + return false; + } + + Common::StdInputStream stream(dataFile); + BinaryInputStreamSerializer in(stream); + serialize(obj, in); + return !dataFile.fail(); + } catch (std::exception&) { + return false; + } +} + +} diff --git a/src/serialization/ISerializer.h b/src/Serialization/ISerializer.h similarity index 83% rename from src/serialization/ISerializer.h rename to src/Serialization/ISerializer.h index f7c5c5f08b..d199e48914 100644 --- a/src/serialization/ISerializer.h +++ b/src/Serialization/ISerializer.h @@ -38,10 +38,12 @@ class ISerializer { virtual bool beginObject(Common::StringView name) = 0; virtual void endObject() = 0; - virtual bool beginArray(std::size_t& size, Common::StringView name) = 0; + virtual bool beginArray(size_t& size, Common::StringView name) = 0; virtual void endArray() = 0; virtual bool operator()(uint8_t& value, Common::StringView name) = 0; + virtual bool operator()(int16_t& value, Common::StringView name) = 0; + virtual bool operator()(uint16_t& value, Common::StringView name) = 0; virtual bool operator()(int32_t& value, Common::StringView name) = 0; virtual bool operator()(uint32_t& value, Common::StringView name) = 0; virtual bool operator()(int64_t& value, Common::StringView name) = 0; @@ -51,7 +53,7 @@ class ISerializer { virtual bool operator()(std::string& value, Common::StringView name) = 0; // read/write binary block - virtual bool binary(void* value, std::size_t size, Common::StringView name) = 0; + virtual bool binary(void* value, size_t size, Common::StringView name) = 0; virtual bool binary(std::string& value, Common::StringView name) = 0; template @@ -79,6 +81,13 @@ void serialize(T& value, ISerializer& serializer) { value.serialize(serializer); } +#ifdef __clang__ +template<> inline +bool ISerializer::operator()(size_t& value, Common::StringView name) { + return operator()(*reinterpret_cast(&value), name); +} +#endif + #define KV_MEMBER(member) s(member, #member); } diff --git a/src/serialization/IStream.h b/src/Serialization/IStream.h similarity index 88% rename from src/serialization/IStream.h rename to src/Serialization/IStream.h index c979ac462c..1cfe194944 100644 --- a/src/serialization/IStream.h +++ b/src/Serialization/IStream.h @@ -24,12 +24,12 @@ namespace CryptoNote { class IInputStream { public: - virtual std::size_t read(char* data, std::size_t size) = 0; + virtual size_t read(char* data, size_t size) = 0; }; class IOutputStream { public: - virtual void write(const char* data, std::size_t size) = 0; + virtual void write(const char* data, size_t size) = 0; }; } diff --git a/src/serialization/JsonInputStreamSerializer.cpp b/src/Serialization/JsonInputStreamSerializer.cpp similarity index 77% rename from src/serialization/JsonInputStreamSerializer.cpp rename to src/Serialization/JsonInputStreamSerializer.cpp index 8cb6efe197..ed6192e074 100644 --- a/src/serialization/JsonInputStreamSerializer.cpp +++ b/src/Serialization/JsonInputStreamSerializer.cpp @@ -15,15 +15,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "serialization/JsonInputStreamSerializer.h" +#include "Serialization/JsonInputStreamSerializer.h" #include #include namespace CryptoNote { -JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) : JsonInputValueSerializer(root) { - stream >> root; +namespace { + +Common::JsonValue getJsonValueFromStreamHelper(std::istream& stream) { + Common::JsonValue value; + stream >> value; + return value; +} + +} + +JsonInputStreamSerializer::JsonInputStreamSerializer(std::istream& stream) : JsonInputValueSerializer(getJsonValueFromStreamHelper(stream)) { } JsonInputStreamSerializer::~JsonInputStreamSerializer() { diff --git a/src/serialization/JsonInputStreamSerializer.h b/src/Serialization/JsonInputStreamSerializer.h similarity index 96% rename from src/serialization/JsonInputStreamSerializer.h rename to src/Serialization/JsonInputStreamSerializer.h index ce8ea94597..fdaf04fc85 100644 --- a/src/serialization/JsonInputStreamSerializer.h +++ b/src/Serialization/JsonInputStreamSerializer.h @@ -30,9 +30,6 @@ class JsonInputStreamSerializer : public JsonInputValueSerializer { public: JsonInputStreamSerializer(std::istream& stream); virtual ~JsonInputStreamSerializer(); - -private: - Common::JsonValue root; }; } diff --git a/src/serialization/JsonInputValueSerializer.cpp b/src/Serialization/JsonInputValueSerializer.cpp similarity index 79% rename from src/serialization/JsonInputValueSerializer.cpp rename to src/Serialization/JsonInputValueSerializer.cpp index 35b282dcd6..e8f2891399 100644 --- a/src/serialization/JsonInputValueSerializer.cpp +++ b/src/Serialization/JsonInputValueSerializer.cpp @@ -16,15 +16,29 @@ // along with Bytecoin. If not, see . #include "JsonInputValueSerializer.h" + #include #include -#include + +#include "Common/StringTools.h" using Common::JsonValue; using namespace CryptoNote; -JsonInputValueSerializer::JsonInputValueSerializer(const Common::JsonValue& value) : root(value) { - chain.push_back(&root); +JsonInputValueSerializer::JsonInputValueSerializer(const Common::JsonValue& value) { + if (!value.isObject()) { + throw std::runtime_error("Serializer doesn't support this type of serialization: Object expected."); + } + + chain.push_back(&value); +} + +JsonInputValueSerializer::JsonInputValueSerializer(Common::JsonValue&& value) : value(std::move(value)) { + if (!this->value.isObject()) { + throw std::runtime_error("Serializer doesn't support this type of serialization: Object expected."); + } + + chain.push_back(&this->value); } JsonInputValueSerializer::~JsonInputValueSerializer() { @@ -50,14 +64,6 @@ bool JsonInputValueSerializer::beginObject(Common::StringView name) { } return false; - - //if (parent->isArray()) { - // const JsonValue& v = (*parent)[idxs.back()++]; - // chain.push_back(&v); - //} else { - // const JsonValue& v = (*parent)(name); - // chain.push_back(&v); - //} } void JsonInputValueSerializer::endObject() { @@ -65,7 +71,7 @@ void JsonInputValueSerializer::endObject() { chain.pop_back(); } -bool JsonInputValueSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool JsonInputValueSerializer::beginArray(size_t& size, Common::StringView name) { const JsonValue* parent = chain.back(); std::string strName(name); @@ -89,6 +95,14 @@ void JsonInputValueSerializer::endArray() { idxs.pop_back(); } +bool JsonInputValueSerializer::operator()(uint16_t& value, Common::StringView name) { + return getNumber(name, value); +} + +bool JsonInputValueSerializer::operator()(int16_t& value, Common::StringView name) { + return getNumber(name, value); +} + bool JsonInputValueSerializer::operator()(uint32_t& value, Common::StringView name) { return getNumber(name, value); } @@ -115,7 +129,7 @@ bool JsonInputValueSerializer::operator()(uint8_t& value, Common::StringView nam bool JsonInputValueSerializer::operator()(std::string& value, Common::StringView name) { auto ptr = getValue(name); - if (!ptr) { + if (ptr == nullptr) { return false; } value = ptr->getString(); @@ -124,14 +138,14 @@ bool JsonInputValueSerializer::operator()(std::string& value, Common::StringView bool JsonInputValueSerializer::operator()(bool& value, Common::StringView name) { auto ptr = getValue(name); - if (!ptr) { + if (ptr == nullptr) { return false; } value = ptr->getBool(); return true; } -bool JsonInputValueSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool JsonInputValueSerializer::binary(void* value, size_t size, Common::StringView name) { auto ptr = getValue(name); if (ptr == nullptr) { return false; diff --git a/src/serialization/JsonInputValueSerializer.h b/src/Serialization/JsonInputValueSerializer.h similarity index 84% rename from src/serialization/JsonInputValueSerializer.h rename to src/Serialization/JsonInputValueSerializer.h index e211b700ba..232e4f810d 100644 --- a/src/serialization/JsonInputValueSerializer.h +++ b/src/Serialization/JsonInputValueSerializer.h @@ -17,7 +17,7 @@ #pragma once -#include "../Common/JsonValue.h" +#include "Common/JsonValue.h" #include "ISerializer.h" namespace CryptoNote { @@ -26,6 +26,7 @@ namespace CryptoNote { class JsonInputValueSerializer : public ISerializer { public: JsonInputValueSerializer(const Common::JsonValue& value); + JsonInputValueSerializer(Common::JsonValue&& value); virtual ~JsonInputValueSerializer(); SerializerType type() const; @@ -33,10 +34,12 @@ class JsonInputValueSerializer : public ISerializer { virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -44,7 +47,7 @@ class JsonInputValueSerializer : public ISerializer { virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template @@ -53,7 +56,7 @@ class JsonInputValueSerializer : public ISerializer { } private: - const Common::JsonValue& root; + Common::JsonValue value; std::vector chain; std::vector idxs; diff --git a/src/serialization/JsonOutputStreamSerializer.cpp b/src/Serialization/JsonOutputStreamSerializer.cpp similarity index 88% rename from src/serialization/JsonOutputStreamSerializer.cpp rename to src/Serialization/JsonOutputStreamSerializer.cpp index 3224d4793a..ed33e46cbb 100644 --- a/src/serialization/JsonOutputStreamSerializer.cpp +++ b/src/Serialization/JsonOutputStreamSerializer.cpp @@ -72,7 +72,7 @@ void JsonOutputStreamSerializer::endObject() { chain.pop_back(); } -bool JsonOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool JsonOutputStreamSerializer::beginArray(size_t& size, Common::StringView name) { JsonValue val(JsonValue::ARRAY); JsonValue& res = chain.back()->insert(std::string(name), val); chain.push_back(&res); @@ -89,6 +89,16 @@ bool JsonOutputStreamSerializer::operator()(uint64_t& value, Common::StringView return operator()(v, name); } +bool JsonOutputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + uint64_t v = static_cast(value); + return operator()(v, name); +} + +bool JsonOutputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + int64_t v = static_cast(value); + return operator()(v, name); +} + bool JsonOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { uint64_t v = static_cast(value); return operator()(v, name); @@ -124,7 +134,7 @@ bool JsonOutputStreamSerializer::operator()(bool& value, Common::StringView name return true; } -bool JsonOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool JsonOutputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { std::string hex = Common::toHex(value, size); return (*this)(hex, name); } diff --git a/src/serialization/JsonOutputStreamSerializer.h b/src/Serialization/JsonOutputStreamSerializer.h similarity index 87% rename from src/serialization/JsonOutputStreamSerializer.h rename to src/Serialization/JsonOutputStreamSerializer.h index bea003d9cb..c86e8d67aa 100644 --- a/src/serialization/JsonOutputStreamSerializer.h +++ b/src/Serialization/JsonOutputStreamSerializer.h @@ -33,10 +33,12 @@ class JsonOutputStreamSerializer : public ISerializer { virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -44,7 +46,7 @@ class JsonOutputStreamSerializer : public ISerializer { virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template diff --git a/src/serialization/KVBinaryCommon.h b/src/Serialization/KVBinaryCommon.h similarity index 100% rename from src/serialization/KVBinaryCommon.h rename to src/Serialization/KVBinaryCommon.h diff --git a/src/serialization/KVBinaryInputStreamSerializer.cpp b/src/Serialization/KVBinaryInputStreamSerializer.cpp similarity index 69% rename from src/serialization/KVBinaryInputStreamSerializer.cpp rename to src/Serialization/KVBinaryInputStreamSerializer.cpp index f6e27208d4..fb8f7689fb 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.cpp +++ b/src/Serialization/KVBinaryInputStreamSerializer.cpp @@ -16,96 +16,98 @@ // along with Bytecoin. If not, see . #include "KVBinaryInputStreamSerializer.h" + #include #include #include #include +#include #include "KVBinaryCommon.h" -using Common::JsonValue; +using namespace Common; using namespace CryptoNote; namespace { -void strictRead(std::istream& s, void* ptr, size_t size) { - if (s.read(reinterpret_cast(ptr), size).gcount() != size) { - throw std::runtime_error("read error"); - } -} - template -T readPod(std::istream& s) { +T readPod(Common::IInputStream& s) { T v; - strictRead(s, &v, sizeof(T)); + read(s, &v, sizeof(T)); return v; } template -JsonValue readPodJson(std::istream& s) { +JsonValue readPodJson(Common::IInputStream& s) { JsonValue jv; jv = static_cast(readPod(s)); return jv; } template -JsonValue readIntegerJson(std::istream& s) { +JsonValue readIntegerJson(Common::IInputStream& s) { return readPodJson(s); } -size_t readVarint(std::istream& s) { - size_t v = 0; - uint8_t size_mask = uint8_t(s.peek()) & PORTABLE_RAW_SIZE_MARK_MASK; +size_t readVarint(Common::IInputStream& s) { + uint8_t b = read(s); + uint8_t size_mask = b & PORTABLE_RAW_SIZE_MARK_MASK; + size_t bytesLeft = 0; - switch (size_mask) { + switch (size_mask){ case PORTABLE_RAW_SIZE_MARK_BYTE: - v = readPod(s); + bytesLeft = 0; break; case PORTABLE_RAW_SIZE_MARK_WORD: - v = readPod(s); + bytesLeft = 1; break; case PORTABLE_RAW_SIZE_MARK_DWORD: - v = readPod(s); + bytesLeft = 3; break; case PORTABLE_RAW_SIZE_MARK_INT64: - v = readPod(s); + bytesLeft = 7; break; - default: - throw std::runtime_error("unknown varint size_mask"); } - v >>= 2; - return v; + size_t value = b; + + for (size_t i = 1; i <= bytesLeft; ++i) { + size_t n = read(s); + value |= n << (i * 8); + } + + value >>= 2; + return value; } -std::string readString(std::istream& s) { +std::string readString(Common::IInputStream& s) { auto size = readVarint(s); std::string str; str.resize(size); if (size) { - strictRead(s, &str[0], size); + read(s, &str[0], size); } return str; } -JsonValue readStringJson(std::istream& s) { +JsonValue readStringJson(Common::IInputStream& s) { return JsonValue(readString(s)); } -void readName(std::istream& s, std::string& name) { +void readName(Common::IInputStream& s, std::string& name) { uint8_t len = readPod(s); if (len) { name.resize(len); - strictRead(s, &name[0], len); + read(s, &name[0], len); } } -JsonValue loadValue(std::istream& stream, uint8_t type); -JsonValue loadSection(std::istream& stream); -JsonValue loadEntry(std::istream& stream); -JsonValue loadArray(std::istream& stream, uint8_t itemType); +JsonValue loadValue(Common::IInputStream& stream, uint8_t type); +JsonValue loadSection(Common::IInputStream& stream); +JsonValue loadEntry(Common::IInputStream& stream); +JsonValue loadArray(Common::IInputStream& stream, uint8_t itemType); -JsonValue loadSection(std::istream& stream) { +JsonValue loadSection(Common::IInputStream& stream) { JsonValue sec(JsonValue::OBJECT); size_t count = readVarint(stream); std::string name; @@ -118,7 +120,7 @@ JsonValue loadSection(std::istream& stream) { return sec; } -JsonValue loadValue(std::istream& stream, uint8_t type) { +JsonValue loadValue(Common::IInputStream& stream, uint8_t type) { switch (type) { case BIN_KV_SERIALIZE_TYPE_INT64: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_INT32: return readIntegerJson(stream); @@ -129,7 +131,7 @@ JsonValue loadValue(std::istream& stream, uint8_t type) { case BIN_KV_SERIALIZE_TYPE_UINT16: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_UINT8: return readIntegerJson(stream); case BIN_KV_SERIALIZE_TYPE_DOUBLE: return readPodJson(stream); - case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(stream.get() != 0); + case BIN_KV_SERIALIZE_TYPE_BOOL: return JsonValue(read(stream) != 0); case BIN_KV_SERIALIZE_TYPE_STRING: return readStringJson(stream); case BIN_KV_SERIALIZE_TYPE_OBJECT: return loadSection(stream); case BIN_KV_SERIALIZE_TYPE_ARRAY: return loadArray(stream, type); @@ -139,7 +141,7 @@ JsonValue loadValue(std::istream& stream, uint8_t type) { } } -JsonValue loadEntry(std::istream& stream) { +JsonValue loadEntry(Common::IInputStream& stream) { uint8_t type = readPod(stream); if (type & BIN_KV_SERIALIZE_FLAG_ARRAY) { @@ -150,7 +152,7 @@ JsonValue loadEntry(std::istream& stream) { return loadValue(stream, type); } -JsonValue loadArray(std::istream& stream, uint8_t itemType) { +JsonValue loadArray(Common::IInputStream& stream, uint8_t itemType) { JsonValue arr(JsonValue::ARRAY); size_t count = readVarint(stream); @@ -162,7 +164,7 @@ JsonValue loadArray(std::istream& stream, uint8_t itemType) { } -JsonValue parseBinary(std::istream& stream) { +JsonValue parseBinary(Common::IInputStream& stream) { auto hdr = readPod(stream); if ( @@ -180,10 +182,10 @@ JsonValue parseBinary(std::istream& stream) { } -KVBinaryInputStreamSerializer::KVBinaryInputStreamSerializer(std::istream& strm) : value(parseBinary(strm)), JsonInputValueSerializer(value) { +KVBinaryInputStreamSerializer::KVBinaryInputStreamSerializer(Common::IInputStream& strm) : JsonInputValueSerializer(parseBinary(strm)) { } -bool KVBinaryInputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool KVBinaryInputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { std::string str; if (!(*this)(str, name)) { diff --git a/src/serialization/KVBinaryInputStreamSerializer.h b/src/Serialization/KVBinaryInputStreamSerializer.h similarity index 84% rename from src/serialization/KVBinaryInputStreamSerializer.h rename to src/Serialization/KVBinaryInputStreamSerializer.h index 6fe2962819..05d6dcc471 100644 --- a/src/serialization/KVBinaryInputStreamSerializer.h +++ b/src/Serialization/KVBinaryInputStreamSerializer.h @@ -17,7 +17,7 @@ #pragma once -#include +#include #include "ISerializer.h" #include "JsonInputValueSerializer.h" @@ -25,14 +25,10 @@ namespace CryptoNote { class KVBinaryInputStreamSerializer : public JsonInputValueSerializer { public: - KVBinaryInputStreamSerializer(std::istream& strm); + KVBinaryInputStreamSerializer(Common::IInputStream& strm); - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; - -private: - - Common::JsonValue value; }; } diff --git a/src/serialization/KVBinaryOutputStreamSerializer.cpp b/src/Serialization/KVBinaryOutputStreamSerializer.cpp similarity index 84% rename from src/serialization/KVBinaryOutputStreamSerializer.cpp rename to src/Serialization/KVBinaryOutputStreamSerializer.cpp index 37c77f69b9..a33c34b1e1 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.cpp +++ b/src/Serialization/KVBinaryOutputStreamSerializer.cpp @@ -20,33 +20,23 @@ #include #include +#include +using namespace Common; using namespace CryptoNote; namespace { -class StdOutputStream : public IOutputStream { -public: - StdOutputStream(std::ostream& s) : stream(s) {} - - virtual void write(const char* data, std::size_t size) override { - stream.write(data, size); - } - -private: - std::ostream& stream; -}; - template void writePod(IOutputStream& s, const T& value) { - s.write((const char*)&value, sizeof(T)); + write(s, &value, sizeof(T)); } template size_t packVarint(IOutputStream& s, uint8_t type_or, size_t pv) { T v = static_cast(pv << 2); v |= type_or; - s.write((const char*)&v, sizeof(T)); + write(s, &v, sizeof(T)); return sizeof(T); } @@ -56,8 +46,8 @@ void writeElementName(IOutputStream& s, Common::StringView name) { } uint8_t len = static_cast(name.getSize()); - s.write((const char*)&len, sizeof(len)); - s.write(name.getData(), len); + write(s, &len, sizeof(len)); + write(s, name.getData(), len); } size_t writeArraySize(IOutputStream& s, size_t val) { @@ -79,17 +69,11 @@ size_t writeArraySize(IOutputStream& s, size_t val) { namespace CryptoNote { -using namespace CryptoNote; - - - - KVBinaryOutputStreamSerializer::KVBinaryOutputStreamSerializer() { beginObject(std::string()); } -void KVBinaryOutputStreamSerializer::write(std::ostream& target) { - +void KVBinaryOutputStreamSerializer::dump(IOutputStream& target) { assert(m_objectsStack.size() == 1); assert(m_stack.size() == 1); @@ -98,10 +82,9 @@ void KVBinaryOutputStreamSerializer::write(std::ostream& target) { hdr.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; hdr.m_ver = PORTABLE_STORAGE_FORMAT_VER; - target.write(reinterpret_cast(&hdr), sizeof(hdr)); - StdOutputStream stdstream(target); - writeArraySize(stdstream, m_stack.front().count); - target.write(stream().data(), stream().size()); + Common::write(target, &hdr, sizeof(hdr)); + writeArraySize(target, m_stack.front().count); + write(target, stream().data(), stream().size()); } ISerializer::SerializerType KVBinaryOutputStreamSerializer::type() const { @@ -131,10 +114,10 @@ void KVBinaryOutputStreamSerializer::endObject() { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_OBJECT, level.name); writeArraySize(out, level.count); - out.write(objStream.data(), objStream.size()); + write(out, objStream.data(), objStream.size()); } -bool KVBinaryOutputStreamSerializer::beginArray(std::size_t& size, Common::StringView name) { +bool KVBinaryOutputStreamSerializer::beginArray(size_t& size, Common::StringView name) { m_stack.push_back(Level(name, size)); return true; } @@ -154,6 +137,18 @@ bool KVBinaryOutputStreamSerializer::operator()(uint8_t& value, Common::StringVi return true; } +bool KVBinaryOutputStreamSerializer::operator()(uint16_t& value, Common::StringView name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT16, name); + writePod(stream(), value); + return true; +} + +bool KVBinaryOutputStreamSerializer::operator()(int16_t& value, Common::StringView name) { + writeElementPrefix(BIN_KV_SERIALIZE_TYPE_INT16, name); + writePod(stream(), value); + return true; +} + bool KVBinaryOutputStreamSerializer::operator()(uint32_t& value, Common::StringView name) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_UINT32, name); writePod(stream(), value); @@ -195,16 +190,16 @@ bool KVBinaryOutputStreamSerializer::operator()(std::string& value, Common::Stri auto& out = stream(); writeArraySize(out, value.size()); - out.write(value.data(), value.size()); + write(out, value.data(), value.size()); return true; } -bool KVBinaryOutputStreamSerializer::binary(void* value, std::size_t size, Common::StringView name) { +bool KVBinaryOutputStreamSerializer::binary(void* value, size_t size, Common::StringView name) { if (size > 0) { writeElementPrefix(BIN_KV_SERIALIZE_TYPE_STRING, name); auto& out = stream(); writeArraySize(out, size); - out.write(static_cast(value), size); + write(out, value, size); } return true; } @@ -223,7 +218,7 @@ void KVBinaryOutputStreamSerializer::writeElementPrefix(uint8_t type, Common::St if (!name.isEmpty()) { auto& s = stream(); writeElementName(s, name); - s.write((const char*)&type, 1); + write(s, &type, 1); } ++level.count; } @@ -240,7 +235,7 @@ void KVBinaryOutputStreamSerializer::checkArrayPreamble(uint8_t type) { auto& s = stream(); writeElementName(s, level.name); char c = BIN_KV_SERIALIZE_FLAG_ARRAY | type; - s.write(&c, 1); + write(s, &c, 1); writeArraySize(s, level.count); level.state = State::Array; } diff --git a/src/serialization/KVBinaryOutputStreamSerializer.h b/src/Serialization/KVBinaryOutputStreamSerializer.h similarity index 87% rename from src/serialization/KVBinaryOutputStreamSerializer.h rename to src/Serialization/KVBinaryOutputStreamSerializer.h index 70fb43d933..87ab6f6d3a 100644 --- a/src/serialization/KVBinaryOutputStreamSerializer.h +++ b/src/Serialization/KVBinaryOutputStreamSerializer.h @@ -17,14 +17,11 @@ #pragma once +#include +#include #include "ISerializer.h" #include "MemoryStream.h" -#include -#include -#include -#include - namespace CryptoNote { class KVBinaryOutputStreamSerializer : public ISerializer { @@ -33,17 +30,19 @@ class KVBinaryOutputStreamSerializer : public ISerializer { KVBinaryOutputStreamSerializer(); virtual ~KVBinaryOutputStreamSerializer() {} - void write(std::ostream& target); + void dump(Common::IOutputStream& target); virtual ISerializer::SerializerType type() const; virtual bool beginObject(Common::StringView name) override; virtual void endObject() override; - virtual bool beginArray(std::size_t& size, Common::StringView name) override; + virtual bool beginArray(size_t& size, Common::StringView name) override; virtual void endArray() override; virtual bool operator()(uint8_t& value, Common::StringView name) override; + virtual bool operator()(int16_t& value, Common::StringView name) override; + virtual bool operator()(uint16_t& value, Common::StringView name) override; virtual bool operator()(int32_t& value, Common::StringView name) override; virtual bool operator()(uint32_t& value, Common::StringView name) override; virtual bool operator()(int64_t& value, Common::StringView name) override; @@ -51,7 +50,7 @@ class KVBinaryOutputStreamSerializer : public ISerializer { virtual bool operator()(double& value, Common::StringView name) override; virtual bool operator()(bool& value, Common::StringView name) override; virtual bool operator()(std::string& value, Common::StringView name) override; - virtual bool binary(void* value, std::size_t size, Common::StringView name) override; + virtual bool binary(void* value, size_t size, Common::StringView name) override; virtual bool binary(std::string& value, Common::StringView name) override; template diff --git a/src/serialization/MemoryStream.cpp b/src/Serialization/MemoryStream.cpp similarity index 100% rename from src/serialization/MemoryStream.cpp rename to src/Serialization/MemoryStream.cpp diff --git a/src/serialization/MemoryStream.h b/src/Serialization/MemoryStream.h similarity index 68% rename from src/serialization/MemoryStream.h rename to src/Serialization/MemoryStream.h index 1b995221c8..d4f9b7fe45 100644 --- a/src/serialization/MemoryStream.h +++ b/src/Serialization/MemoryStream.h @@ -17,25 +17,23 @@ #pragma once - -#include "IStream.h" -#include #include +#include #include // memcpy +#include +#include namespace CryptoNote { -class MemoryStream: - public IInputStream, - public IOutputStream { +class MemoryStream: public Common::IOutputStream { public: - MemoryStream() : - m_readPos(0), m_writePos(0) {} + MemoryStream() : m_writePos(0) { + } - virtual void write(const char* data, std::size_t size) override { + virtual size_t writeSome(const void* data, size_t size) override { if (size == 0) { - return; + return 0; } if (m_writePos + size > m_buffer.size()) { @@ -44,38 +42,25 @@ class MemoryStream: memcpy(&m_buffer[m_writePos], data, size); m_writePos += size; - } - - virtual std::size_t read(char* data, std::size_t size) override { - size_t readSize = std::min(size, m_buffer.size() - m_readPos); - - if (readSize > 0) { - memcpy(data, &m_buffer[m_readPos], readSize); - m_readPos += readSize; - } - - return readSize; + return size; } size_t size() { return m_buffer.size(); } - const char* data() { + const uint8_t* data() { return m_buffer.data(); } void clear() { - m_readPos = 0; m_writePos = 0; m_buffer.resize(0); } private: - - size_t m_readPos; size_t m_writePos; - std::vector m_buffer; + std::vector m_buffer; }; } diff --git a/src/Serialization/SerializationOverloads.h b/src/Serialization/SerializationOverloads.h new file mode 100644 index 0000000000..4b115d0f2d --- /dev/null +++ b/src/Serialization/SerializationOverloads.h @@ -0,0 +1,252 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "ISerializer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CryptoNote { + +template +typename std::enable_if::value>::type +serializeAsBinary(std::vector& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + std::string blob; + if (serializer.type() == ISerializer::INPUT) { + serializer.binary(blob, name); + value.resize(blob.size() / sizeof(T)); + if (blob.size()) { + memcpy(&value[0], blob.data(), blob.size()); + } + } else { + if (!value.empty()) { + blob.assign(reinterpret_cast(&value[0]), value.size() * sizeof(T)); + } + serializer.binary(blob, name); + } +} + +template +typename std::enable_if::value>::type +serializeAsBinary(std::list& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + std::string blob; + if (serializer.type() == ISerializer::INPUT) { + serializer.binary(blob, name); + + size_t count = blob.size() / sizeof(T); + const T* ptr = reinterpret_cast(blob.data()); + + while (count--) { + value.push_back(*ptr++); + } + } else { + if (!value.empty()) { + blob.resize(value.size() * sizeof(T)); + T* ptr = reinterpret_cast(&blob[0]); + + for (const auto& item : value) { + *ptr++ = item; + } + } + serializer.binary(blob, name); + } +} + +template +bool serializeContainer(Cont& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + size_t size = value.size(); + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + + value.resize(size); + + for (auto& item : value) { + serializer(const_cast(item), ""); + } + + serializer.endArray(); + return true; +} + +template +bool serializeEnumClass(E& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + static_assert(std::is_enum::value, "E must be an enum class"); + + typedef typename std::underlying_type::type EType; + + if (serializer.type() == CryptoNote::ISerializer::INPUT) { + EType numericValue; + serializer(numericValue, name); + value = static_cast(numericValue); + } else { + auto numericValue = static_cast(value); + serializer(numericValue, name); + } + + return true; +} + +template +bool serialize(std::vector& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeContainer(value, name, serializer); +} + +template +bool serialize(std::list& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeContainer(value, name, serializer); +} + +template +bool serializeMap(MapT& value, Common::StringView name, CryptoNote::ISerializer& serializer, ReserveOp reserve) { + size_t size = value.size(); + + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + + if (serializer.type() == CryptoNote::ISerializer::INPUT) { + reserve(size); + + for (size_t i = 0; i < size; ++i) { + typename MapT::key_type key; + typename MapT::mapped_type v; + + serializer.beginObject(""); + serializer(key, "key"); + serializer(v, "value"); + serializer.endObject(); + + value.insert(std::make_pair(std::move(key), std::move(v))); + } + } else { + for (auto& kv : value) { + serializer.beginObject(""); + serializer(const_cast(kv.first), "key"); + serializer(kv.second, "value"); + serializer.endObject(); + } + } + + serializer.endArray(); + return true; +} + +template +bool serializeSet(SetT& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + size_t size = value.size(); + + if (!serializer.beginArray(size, name)) { + value.clear(); + return false; + } + + if (serializer.type() == CryptoNote::ISerializer::INPUT) { + for (size_t i = 0; i < size; ++i) { + typename SetT::value_type key; + serializer(key, ""); + value.insert(std::move(key)); + } + } else { + for (auto& key : value) { + serializer(const_cast(key), ""); + } + } + + serializer.endArray(); + return true; +} + +template +bool serialize(std::unordered_set& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeSet(value, name, serializer); +} + +template +bool serialize(std::set& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeSet(value, name, serializer); +} + +template +bool serialize(std::unordered_map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [&value](size_t size) { value.reserve(size); }); +} + +template +bool serialize(std::unordered_multimap& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [&value](size_t size) { value.reserve(size); }); +} + +template +bool serialize(std::map& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [](size_t size) {}); +} + +template +bool serialize(std::multimap& value, Common::StringView name, CryptoNote::ISerializer& serializer) { + return serializeMap(value, name, serializer, [](size_t size) {}); +} + +template +bool serialize(std::array& value, Common::StringView name, CryptoNote::ISerializer& s) { + return s.binary(value.data(), value.size(), name); +} + +template +void serialize(std::pair& value, ISerializer& s) { + s(value.first, "first"); + s(value.second, "second"); +} + +template +void writeSequence(Iterator begin, Iterator end, Common::StringView name, ISerializer& s) { + size_t size = std::distance(begin, end); + s.beginArray(size, name); + for (Iterator i = begin; i != end; ++i) { + s(const_cast(*i), ""); + } + s.endArray(); +} + +template +void readSequence(Iterator outputIterator, Common::StringView name, ISerializer& s) { + size_t size = 0; + s.beginArray(size, name); + + while (size--) { + Element e; + s(e, ""); + *outputIterator++ = std::move(e); + } + + s.endArray(); +} + +} diff --git a/src/serialization/SerializationTools.h b/src/Serialization/SerializationTools.h similarity index 91% rename from src/serialization/SerializationTools.h rename to src/Serialization/SerializationTools.h index dc314c7016..827ad69aa5 100644 --- a/src/serialization/SerializationTools.h +++ b/src/Serialization/SerializationTools.h @@ -17,11 +17,14 @@ #pragma once -#include "KVBinaryInputStreamSerializer.h" -#include "KVBinaryOutputStreamSerializer.h" - +#include +#include +#include +#include #include "JsonInputStreamSerializer.h" #include "JsonOutputStreamSerializer.h" +#include "KVBinaryInputStreamSerializer.h" +#include "KVBinaryOutputStreamSerializer.h" namespace Common { @@ -37,7 +40,6 @@ inline std::string getValueAs(const JsonValue& js) { return js.getS template <> inline uint64_t getValueAs(const JsonValue& js) { return static_cast(js.getInteger()); } - } namespace CryptoNote { @@ -67,7 +69,6 @@ Common::JsonValue storeToJsonValue(const std::list& v) { return storeContaine template <> inline Common::JsonValue storeToJsonValue(const std::string& v) { return Common::JsonValue(v); } - template void loadFromJsonValue(T& v, const Common::JsonValue& js) { JsonInputValueSerializer s(js); @@ -88,7 +89,6 @@ void loadFromJsonValue(std::list& v, const Common::JsonValue& js) { } } - template std::string storeToJson(const T& v) { return storeToJsonValue(v).toString(); @@ -113,22 +113,22 @@ std::string storeToBinaryKeyValue(const T& v) { KVBinaryOutputStreamSerializer s; serialize(const_cast(v), s); - std::ostringstream stream; - s.write(stream); - return stream.str(); + std::string result; + Common::StringOutputStream stream(result); + s.dump(stream); + return result; } template bool loadFromBinaryKeyValue(T& v, const std::string& buf) { try { - std::istringstream stream(buf); + Common::MemoryInputStream stream(buf.data(), buf.size()); KVBinaryInputStreamSerializer s(stream); serialize(v, s); - return stream.good(); + return true; } catch (std::exception&) { return false; } } - } diff --git a/src/simplewallet/password_container.cpp b/src/SimpleWallet/PasswordContainer.cpp similarity index 90% rename from src/simplewallet/password_container.cpp rename to src/SimpleWallet/PasswordContainer.cpp index 9a574c7649..1643bf7c04 100644 --- a/src/simplewallet/password_container.cpp +++ b/src/SimpleWallet/PasswordContainer.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "password_container.h" +#include "PasswordContainer.h" #include #include @@ -29,36 +29,36 @@ #include #endif -namespace tools +namespace Tools { namespace { bool is_cin_tty(); } - password_container::password_container() + PasswordContainer::PasswordContainer() : m_empty(true) { } - password_container::password_container(std::string&& password) + PasswordContainer::PasswordContainer(std::string&& password) : m_empty(false) , m_password(std::move(password)) { } - password_container::password_container(password_container&& rhs) + PasswordContainer::PasswordContainer(PasswordContainer&& rhs) : m_empty(std::move(rhs.m_empty)) , m_password(std::move(rhs.m_password)) { } - password_container::~password_container() + PasswordContainer::~PasswordContainer() { clear(); } - void password_container::clear() + void PasswordContainer::clear() { if (0 < m_password.capacity()) { @@ -68,7 +68,7 @@ namespace tools m_empty = true; } - bool password_container::read_password() + bool PasswordContainer::read_password() { clear(); @@ -95,7 +95,7 @@ namespace tools return r; } - bool password_container::read_from_file() + bool PasswordContainer::read_from_file() { m_password.reserve(max_password_size); for (size_t i = 0; i < max_password_size; ++i) @@ -128,7 +128,7 @@ namespace tools } } - bool password_container::read_from_tty() + bool PasswordContainer::read_from_tty() { const char BACKSPACE = 8; @@ -204,7 +204,7 @@ namespace tools } } - bool password_container::read_from_tty() + bool PasswordContainer::read_from_tty() { const char BACKSPACE = 127; diff --git a/src/simplewallet/password_container.h b/src/SimpleWallet/PasswordContainer.h old mode 100644 new mode 100755 similarity index 86% rename from src/simplewallet/password_container.h rename to src/SimpleWallet/PasswordContainer.h index 7be4ccf407..0eeee40d43 --- a/src/simplewallet/password_container.h +++ b/src/SimpleWallet/PasswordContainer.h @@ -19,17 +19,17 @@ #include -namespace tools +namespace Tools { - class password_container + class PasswordContainer { public: static const size_t max_password_size = 1024; - password_container(); - password_container(std::string&& password); - password_container(password_container&& rhs); - ~password_container(); + PasswordContainer(); + PasswordContainer(std::string&& password); + PasswordContainer(PasswordContainer&& rhs); + ~PasswordContainer(); void clear(); bool empty() const { return m_empty; } diff --git a/src/simplewallet/simplewallet.cpp b/src/SimpleWallet/SimpleWallet.cpp similarity index 91% rename from src/simplewallet/simplewallet.cpp rename to src/SimpleWallet/SimpleWallet.cpp index 5dcbdc0460..99ee6bb204 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/SimpleWallet/SimpleWallet.cpp @@ -15,7 +15,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "simplewallet.h" +#include "SimpleWallet.h" #include #include @@ -29,20 +29,20 @@ #include #include -#include "Common/command_line.h" +#include "Common/CommandLine.h" #include "Common/SignalHandler.h" #include "Common/PathTools.h" -#include "Common/util.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include "node_rpc_proxy/NodeRpcProxy.h" -#include "rpc/core_rpc_server_commands_defs.h" -#include "rpc/HttpClient.h" - -#include "wallet/wallet_rpc_server.h" -#include "wallet/Wallet.h" -#include "wallet/LegacyKeysImporter.h" -#include "wallet/WalletHelper.h" +#include "Common/Util.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" +#include "NodeRpcProxy/NodeRpcProxy.h" +#include "Rpc/CoreRpcServerCommandsDefinitions.h" +#include "Rpc/HttpClient.h" + +#include "Wallet/WalletRpcServer.h" +#include "WalletLegacy/WalletLegacy.h" +#include "Wallet/LegacyKeysImporter.h" +#include "WalletLegacy/WalletHelper.h" #include "version.h" @@ -75,13 +75,10 @@ const command_line::arg_descriptor< std::vector > arg_command = { " bool parseUrlAddress(const std::string& url, std::string& address, uint16_t& port) { - auto pos = url.find("://"); size_t addrStart = 0; - if (pos == std::string::npos) { - pos = 0; - } else { + if (pos != std::string::npos) { addrStart = pos + 3; } @@ -145,7 +142,7 @@ class ArgumentReader { struct TransferCommand { const CryptoNote::Currency& m_currency; size_t fake_outs_count; - std::vector dsts; + std::vector dsts; std::vector extra; uint64_t fee; @@ -192,11 +189,11 @@ struct TransferCommand { } } } else { - Transfer destination; - CryptoNote::tx_destination_entry de; + WalletLegacyTransfer destination; + CryptoNote::TransactionDestinationEntry de; if (!m_currency.parseAccountAddressString(arg, de.addr)) { - crypto::hash paymentId; + Crypto::Hash paymentId; if (CryptoNote::parsePaymentId(arg, paymentId)) { logger(ERROR, BRIGHT_RED) << "Invalid payment ID usage. Please, use -p . See help for details."; } else { @@ -252,7 +249,7 @@ JsonValue buildLoggerConfiguration(Level level, const std::string& logfile) { return loggerConfiguration; } -std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, const std::string& password) { +std::error_code initAndLoadWallet(IWalletLegacy& wallet, std::istream& walletFile, const std::string& password) { WalletHelper::InitWalletResultObserver initObserver; std::future f_initError = initObserver.initResult.get_future(); @@ -263,7 +260,7 @@ std::error_code initAndLoadWallet(IWallet& wallet, std::istream& walletFile, con return initError; } -std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { +std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr& wallet, const std::string& walletFile, const std::string& password) { std::string keys_file, walletFileName; WalletHelper::prepareFileNames(walletFile, keys_file, walletFileName); @@ -271,9 +268,10 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr< bool keysExists = boost::filesystem::exists(keys_file, ignore); bool walletExists = boost::filesystem::exists(walletFileName, ignore); if (!walletExists && !keysExists && boost::filesystem::exists(walletFile, ignore)) { - auto replaceEc = tools::replace_file(walletFile, walletFileName); - if (replaceEc) { - throw std::runtime_error("failed to rename file '" + walletFile + "' to '" + walletFileName + "'"); + boost::system::error_code renameEc; + boost::filesystem::rename(walletFile, walletFileName, renameEc); + if (renameEc) { + throw std::runtime_error("failed to rename file '" + walletFile + "' to '" + walletFileName + "': " + renameEc.message()); } walletExists = true; @@ -328,12 +326,10 @@ std::string tryToOpenWalletOrLoadKeysOrThrow(LoggerRef& logger, std::unique_ptr< std::future f_initError = initObserver.initResult.get_future(); WalletHelper::IWalletRemoveObserverGuard removeGuard(*wallet, initObserver); - wallet->initAndLoad(ss, password); auto initError = f_initError.get(); removeGuard.removeObserver(); - if (initError) { throw std::runtime_error("failed to load wallet: " + initError.message()); } @@ -380,14 +376,14 @@ bool simple_wallet::exit(const std::vector &args) { return true; } -simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log) - : m_daemon_port(0) - , m_currency(currency) - , logManager(log) - , m_dispatcher(dispatcher) - , logger(log, "simplewallet") - , m_refresh_progress_reporter(*this) - , m_initResultPromise(nullptr) +simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log) : + m_dispatcher(dispatcher), + m_daemon_port(0), + m_currency(currency), + logManager(log), + logger(log, "simplewallet"), + m_refresh_progress_reporter(*this), + m_initResultPromise(nullptr) { m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [] - Start mining in daemon"); m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); @@ -401,7 +397,7 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C "transfer [ ... ] [-p payment_id] [-f fee]" " - Transfer ,... to ,... , respectively. " " is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); - m_consoleHandler.setHandler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log detalization level, is a number 0-4"); + m_consoleHandler.setHandler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log - Change current log level, is a number 0-4"); m_consoleHandler.setHandler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_consoleHandler.setHandler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); m_consoleHandler.setHandler("reset", boost::bind(&simple_wallet::reset, this, _1), "Discard cache data and start synchronizing from the start"); @@ -505,7 +501,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port); } - tools::password_container pwd_container; + Tools::PasswordContainer pwd_container; if (command_line::has_arg(vm, arg_password)) { pwd_container.password(command_line::get_arg(vm, arg_password)); } else if (!pwd_container.read_password()) { @@ -532,7 +528,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } else { - m_wallet.reset(new Wallet(m_currency, *m_node)); + m_wallet.reset(new WalletLegacy(m_currency, *m_node)); try { m_wallet_file = tryToOpenWalletOrLoadKeysOrThrow(logger, m_wallet, m_wallet_file_arg, pwd_container.password()); @@ -576,7 +572,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string { m_wallet_file = wallet_file; - m_wallet.reset(new Wallet(m_currency, *m_node.get())); + m_wallet.reset(new WalletLegacy(m_currency, *m_node.get())); m_node->addObserver(this); m_wallet->addObserver(this); try @@ -598,7 +594,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string throw; } - WalletAccountKeys keys; + AccountKeys keys; m_wallet->getAccountKeys(keys); logger(INFO, BRIGHT_WHITE) << @@ -624,8 +620,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string //---------------------------------------------------------------------------------------------------- bool simple_wallet::close_wallet() { - try - { + try { CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); } catch (const std::exception& e) @@ -739,27 +734,38 @@ void simple_wallet::initCompleted(std::error_code result) { } } //---------------------------------------------------------------------------------------------------- -void simple_wallet::localBlockchainUpdated(uint64_t height) +void simple_wallet::localBlockchainUpdated(uint32_t height) { m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transactionId) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(transactionId, txInfo); + + std::stringstream logPrefix; + if (txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + logPrefix << "Unconfirmed"; + } else { + logPrefix << "Height " << txInfo.blockHeight << ','; + } if (txInfo.totalAmount >= 0) { logger(INFO, GREEN) << - "Height " << txInfo.blockHeight << ", transaction " << Common::podToHex(txInfo.hash) << + logPrefix.str() << " transaction " << Common::podToHex(txInfo.hash) << ", received " << m_currency.formatAmount(txInfo.totalAmount); } else { logger(INFO, MAGENTA) << - "Height " << txInfo.blockHeight << ", transaction " << Common::podToHex(txInfo.hash) << + logPrefix.str() << " transaction " << Common::podToHex(txInfo.hash) << ", spent " << m_currency.formatAmount(static_cast(-txInfo.totalAmount)); } - m_refresh_progress_reporter.update(txInfo.blockHeight, true); + if (txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + m_refresh_progress_reporter.update(m_node->getLastLocalBlockHeight(), true); + } else { + m_refresh_progress_reporter.update(txInfo.blockHeight, true); + } } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_balance(const std::vector& args/* = std::vector()*/) @@ -775,7 +781,7 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args bool hasTransfers = false; size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.totalAmount < 0) continue; hasTransfers = true; @@ -791,9 +797,9 @@ bool simple_wallet::show_incoming_transfers(const std::vector& args bool simple_wallet::listTransfers(const std::vector& args) { size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (txInfo.state != WalletLegacyTransactionState::Active || txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -802,14 +808,14 @@ bool simple_wallet::listTransfers(const std::vector& args) { extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; - paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? Common::podToHex(paymentId) : ""); + Crypto::Hash paymentId; + paymentIdStr = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != NULL_HASH ? Common::podToHex(paymentId) : ""); std::string address = "-"; if (txInfo.totalAmount < 0) { if (txInfo.transferCount > 0) { - Transfer tr; + WalletLegacyTransfer tr; m_wallet->getTransfer(txInfo.firstTransferId, tr); address = tr.address; } @@ -846,19 +852,19 @@ bool simple_wallet::show_payments(const std::vector &args) bool payments_found = false; for (const std::string& arg: args) { - crypto::hash expectedPaymentId; + Crypto::Hash expectedPaymentId; if (CryptoNote::parsePaymentId(arg, expectedPaymentId)) { size_t transactionsCount = m_wallet->getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet->getTransaction(trantransactionNumber, txInfo); if (txInfo.totalAmount < 0) continue; std::vector extraVec; extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; + Crypto::Hash paymentId; if (CryptoNote::getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { payments_found = true; success_msg_writer(true) << @@ -909,7 +915,7 @@ bool simple_wallet::transfer(const std::vector &args) WalletHelper::IWalletRemoveObserverGuard removeGuard(*m_wallet, sent); CryptoNote::TransactionId tx = m_wallet->sendTransaction(cmd.dsts, cmd.fee, extraString, cmd.fake_outs_count, 0); - if (tx == INVALID_TRANSACTION_ID) { + if (tx == WALLET_LEGACY_INVALID_TRANSACTION_ID) { fail_msg_writer() << "Can't send money"; return true; } @@ -922,7 +928,7 @@ bool simple_wallet::transfer(const std::vector &args) return true; } - CryptoNote::TransactionInfo txInfo; + CryptoNote::WalletLegacyTransaction txInfo; m_wallet->getTransaction(tx, txInfo); success_msg_writer(true) << "Money successfully sent, transaction " << Common::podToHex(txInfo.hash); @@ -989,7 +995,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_command); command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_testnet); - tools::wallet_rpc_server::init_options(desc_params); + Tools::wallet_rpc_server::init_options(desc_params); po::positional_options_description positional_options; positional_options.add(arg_command.name, -1); @@ -1042,7 +1048,7 @@ int main(int argc, char* argv[]) CryptoNote::Currency currency = CryptoNote::CurrencyBuilder(logManager). testnet(command_line::get_arg(vm, arg_testnet)).currency(); - if (command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) { + if (command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_bind_port)) { //runs wallet with rpc interface if (!command_line::has_arg(vm, arg_wallet_file)) { @@ -1064,20 +1070,17 @@ int main(int argc, char* argv[]) std::string wallet_password = command_line::get_arg(vm, arg_password); std::string daemon_address = command_line::get_arg(vm, arg_daemon_address); std::string daemon_host = command_line::get_arg(vm, arg_daemon_host); - int daemon_port = command_line::get_arg(vm, arg_daemon_port); + uint16_t daemon_port = command_line::get_arg(vm, arg_daemon_port); if (daemon_host.empty()) daemon_host = "localhost"; if (!daemon_port) daemon_port = RPC_DEFAULT_PORT; if (!daemon_address.empty()) { - uint16_t port = 0; - if (!parseUrlAddress(daemon_address, daemon_host, port)) { + if (!parseUrlAddress(daemon_address, daemon_host, daemon_port)) { logger(ERROR, BRIGHT_RED) << "failed to parse daemon address: " << daemon_address; return 1; } - - daemon_port = port; } std::unique_ptr node(new NodeRpcProxy(daemon_host, daemon_port)); @@ -1091,7 +1094,7 @@ int main(int argc, char* argv[]) return 1; } - std::unique_ptr wallet(new Wallet(currency, *node.get())); + std::unique_ptr wallet(new WalletLegacy(currency, *node.get())); std::string walletFileName; try { @@ -1106,14 +1109,14 @@ int main(int argc, char* argv[]) return 1; } - tools::wallet_rpc_server wrpc(dispatcher, logManager, *wallet, *node, currency, walletFileName); + Tools::wallet_rpc_server wrpc(dispatcher, logManager, *wallet, *node, currency, walletFileName); if (!wrpc.init(vm)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize wallet rpc server"; return 1; } - tools::SignalHandler::install([&wrpc, &wallet] { + Tools::SignalHandler::install([&wrpc, &wallet] { wrpc.send_stop_signal(); }); @@ -1122,8 +1125,8 @@ int main(int argc, char* argv[]) logger(INFO) << "Stopped wallet rpc server"; try { + logger(INFO) << "Storing wallet..."; CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName); - logger(INFO, BRIGHT_GREEN) << "Stored ok"; } catch (const std::exception& e) @@ -1144,7 +1147,7 @@ int main(int argc, char* argv[]) if (!command.empty()) wal.process_command(command); - tools::SignalHandler::install([&wal] { + Tools::SignalHandler::install([&wal] { wal.stop(); }); diff --git a/src/simplewallet/simplewallet.h b/src/SimpleWallet/SimpleWallet.h old mode 100644 new mode 100755 similarity index 94% rename from src/simplewallet/simplewallet.h rename to src/SimpleWallet/SimpleWallet.h index a2d6ba9d1f..75ff383d64 --- a/src/simplewallet/simplewallet.h +++ b/src/SimpleWallet/SimpleWallet.h @@ -22,14 +22,14 @@ #include -#include "IWallet.h" +#include "IWalletLegacy.h" #include "INode.h" -#include "password_container.h" +#include "PasswordContainer.h" #include "Common/ConsoleHandler.h" -#include "cryptonote_core/cryptonote_basic_impl.h" -#include "cryptonote_core/Currency.h" -#include "wallet/WalletHelper.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" +#include "CryptoNoteCore/Currency.h" +#include "WalletLegacy/WalletHelper.h" #include #include @@ -42,7 +42,7 @@ namespace CryptoNote /************************************************************************/ /* */ /************************************************************************/ - class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletObserver + class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletLegacyObserver { public: simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log); @@ -100,7 +100,7 @@ namespace CryptoNote //---------------------------------------------------------- //----------------- INodeObserver -------------------------- - virtual void localBlockchainUpdated(uint64_t height) override; + virtual void localBlockchainUpdated(uint32_t height) override; //---------------------------------------------------------- friend class refresh_progress_reporter_t; @@ -166,6 +166,8 @@ namespace CryptoNote std::string m_wallet_file; + std::unique_ptr> m_initResultPromise; + Common::ConsoleHandler m_consoleHandler; const CryptoNote::Currency& m_currency; Logging::LoggerManager& logManager; @@ -173,8 +175,7 @@ namespace CryptoNote Logging::LoggerRef logger; std::unique_ptr m_node; - std::unique_ptr m_wallet; + std::unique_ptr m_wallet; refresh_progress_reporter_t m_refresh_progress_reporter; - std::unique_ptr> m_initResultPromise; }; } diff --git a/src/System/Context.h b/src/System/Context.h new file mode 100755 index 0000000000..65c8d4108d --- /dev/null +++ b/src/System/Context.h @@ -0,0 +1,151 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include +#include +#include + +namespace System { + +template +class Context { +public: + Context(Dispatcher& dispatcher, std::function&& target) : + dispatcher(dispatcher), target(std::move(target)), ready(dispatcher), bindingContext(dispatcher.getReusableContext()) { + bindingContext.interrupted = false; + bindingContext.groupNext = nullptr; + bindingContext.groupPrev = nullptr; + bindingContext.group = nullptr; + bindingContext.procedure = [this] { + try { + new(resultStorage) ResultType(this->target()); + } catch (...) { + exceptionPointer = std::current_exception(); + } + + ready.set(); + }; + + dispatcher.pushContext(&bindingContext); + } + + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + ~Context() { + interrupt(); + wait(); + dispatcher.pushReusableContext(bindingContext); + } + + ResultType& get() { + wait(); + if (exceptionPointer != nullptr) { + std::rethrow_exception(exceptionPointer); + } + + return *reinterpret_cast(resultStorage); + } + + void interrupt() { + dispatcher.interrupt(&bindingContext); + } + + void wait() { + for (;;) { + try { + ready.wait(); + break; + } catch (InterruptedException&) { + interrupt(); + } + } + } + +private: + uint8_t resultStorage[sizeof(ResultType)]; + Dispatcher& dispatcher; + std::function target; + Event ready; + NativeContext& bindingContext; + std::exception_ptr exceptionPointer; +}; + +template<> +class Context { +public: + Context(Dispatcher& dispatcher, std::function&& target) : + dispatcher(dispatcher), target(std::move(target)), ready(dispatcher), bindingContext(dispatcher.getReusableContext()) { + bindingContext.interrupted = false; + bindingContext.groupNext = nullptr; + bindingContext.groupPrev = nullptr; + bindingContext.group = nullptr; + bindingContext.procedure = [this] { + try { + this->target(); + } catch (...) { + exceptionPointer = std::current_exception(); + } + + ready.set(); + }; + + dispatcher.pushContext(&bindingContext); + } + + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + ~Context() { + interrupt(); + wait(); + dispatcher.pushReusableContext(bindingContext); + } + + void get() { + wait(); + if (exceptionPointer != nullptr) { + std::rethrow_exception(exceptionPointer); + } + } + + void interrupt() { + dispatcher.interrupt(&bindingContext); + } + + void wait() { + for (;;) { + try { + ready.wait(); + break; + } catch (InterruptedException&) { + interrupt(); + } + } + } + +private: + Dispatcher& dispatcher; + std::function target; + Event ready; + NativeContext& bindingContext; + std::exception_ptr exceptionPointer; +}; + +} diff --git a/src/System/ContextGroup.cpp b/src/System/ContextGroup.cpp new file mode 100755 index 0000000000..a0fd5b7fad --- /dev/null +++ b/src/System/ContextGroup.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "ContextGroup.h" +#include + +namespace System { + +ContextGroup::ContextGroup(Dispatcher& dispatcher) : dispatcher(&dispatcher) { + contextGroup.firstContext = nullptr; +} + +ContextGroup::ContextGroup(ContextGroup&& other) : dispatcher(other.dispatcher) { + if (dispatcher != nullptr) { + assert(other.contextGroup.firstContext == nullptr); + contextGroup.firstContext = nullptr; + other.dispatcher = nullptr; + } +} + +ContextGroup::~ContextGroup() { + if (dispatcher != nullptr) { + interrupt(); + wait(); + } +} + +ContextGroup& ContextGroup::operator=(ContextGroup&& other) { + assert(dispatcher == nullptr || contextGroup.firstContext == nullptr); + dispatcher = other.dispatcher; + if (dispatcher != nullptr) { + assert(other.contextGroup.firstContext == nullptr); + contextGroup.firstContext = nullptr; + other.dispatcher = nullptr; + } + + return *this; +} + +void ContextGroup::interrupt() { + assert(dispatcher != nullptr); + for (NativeContext* context = contextGroup.firstContext; context != nullptr; context = context->groupNext) { + dispatcher->interrupt(context); + } +} + +void ContextGroup::spawn(std::function&& procedure) { + assert(dispatcher != nullptr); + NativeContext& context = dispatcher->getReusableContext(); + if (contextGroup.firstContext != nullptr) { + context.groupPrev = contextGroup.lastContext; + assert(contextGroup.lastContext->groupNext == nullptr); + contextGroup.lastContext->groupNext = &context; + } else { + context.groupPrev = nullptr; + contextGroup.firstContext = &context; + contextGroup.firstWaiter = nullptr; + } + + context.interrupted = false; + context.group = &contextGroup; + context.groupNext = nullptr; + context.procedure = std::move(procedure); + contextGroup.lastContext = &context; + dispatcher->pushContext(&context); +} + +void ContextGroup::wait() { + if (contextGroup.firstContext != nullptr) { + NativeContext* context = dispatcher->getCurrentContext(); + context->next = nullptr; + if (contextGroup.firstWaiter != nullptr) { + assert(contextGroup.lastWaiter->next == nullptr); + contextGroup.lastWaiter->next = context; + } else { + contextGroup.firstWaiter = context; + } + + contextGroup.lastWaiter = context; + dispatcher->dispatch(); + assert(context == dispatcher->getCurrentContext()); + } +} + +} diff --git a/src/System/Latch.h b/src/System/ContextGroup.h similarity index 67% rename from src/System/Latch.h rename to src/System/ContextGroup.h index ebe3755821..40ad6e423c 100755 --- a/src/System/Latch.h +++ b/src/System/ContextGroup.h @@ -17,31 +17,25 @@ #pragma once -#include +#include namespace System { -class Dispatcher; - -class Latch { +class ContextGroup { public: - Latch(); - explicit Latch(Dispatcher& dispatcher); - Latch(const Latch&) = delete; - Latch(Latch&& other); - ~Latch(); - Latch& operator=(const Latch&) = delete; - Latch& operator=(Latch&& other); - std::size_t get() const; - void decrease(std::size_t value = 1); - void increase(std::size_t value = 1); + explicit ContextGroup(Dispatcher& dispatcher); + ContextGroup(const ContextGroup&) = delete; + ContextGroup(ContextGroup&& other); + ~ContextGroup(); + ContextGroup& operator=(const ContextGroup&) = delete; + ContextGroup& operator=(ContextGroup&& other); + void interrupt(); + void spawn(std::function&& procedure); void wait(); private: Dispatcher* dispatcher; - std::size_t value; - void* first; - void* last; + NativeContextGroup contextGroup; }; } diff --git a/tests/System/LatchTests.cpp b/src/System/ContextGroupTimeout.cpp similarity index 64% rename from tests/System/LatchTests.cpp rename to src/System/ContextGroupTimeout.cpp index ce91670dda..ac2fe472b8 100755 --- a/tests/System/LatchTests.cpp +++ b/src/System/ContextGroupTimeout.cpp @@ -15,19 +15,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include -#include -#include +#include "ContextGroupTimeout.h" +#include using namespace System; -TEST(LatchTest, latch1) { - Dispatcher dispatcher; - Latch latch(dispatcher); - ASSERT_EQ(0, latch.get()); - latch.increase(10); - ASSERT_EQ(10, latch.get()); - latch.decrease(10); - ASSERT_EQ(0, latch.get()); - latch.wait(); +ContextGroupTimeout::ContextGroupTimeout(Dispatcher& dispatcher, ContextGroup& contextGroup, std::chrono::nanoseconds timeout) : + workingContextGroup(dispatcher), + timeoutTimer(dispatcher) { + workingContextGroup.spawn([&, timeout] { + try { + timeoutTimer.sleep(timeout); + contextGroup.interrupt(); + } catch (InterruptedException&) { + } + }); } diff --git a/src/System/ContextGroupTimeout.h b/src/System/ContextGroupTimeout.h new file mode 100755 index 0000000000..e36645b479 --- /dev/null +++ b/src/System/ContextGroupTimeout.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once +#include +#include +#include + +namespace System { + +class ContextGroupTimeout { +public: + ContextGroupTimeout(Dispatcher&, ContextGroup&, std::chrono::nanoseconds); + +private: + Timer timeoutTimer; + ContextGroup workingContextGroup; +}; + +} diff --git a/src/System/Event.cpp b/src/System/Event.cpp index 809593e6e2..6d24b09545 100755 --- a/src/System/Event.cpp +++ b/src/System/Event.cpp @@ -18,14 +18,17 @@ #include "Event.h" #include #include +#include namespace System { namespace { struct EventWaiter { + bool interrupted; + EventWaiter* prev; EventWaiter* next; - void* context; + NativeContext* context; }; } @@ -86,6 +89,7 @@ void Event::set() { if (!state) { state = true; for (EventWaiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { + waiter->context->interruptProcedure = nullptr; dispatcher->pushContext(waiter->context); } } @@ -93,10 +97,37 @@ void Event::set() { void Event::wait() { assert(dispatcher != nullptr); + if (dispatcher->interrupted()) { + throw InterruptedException(); + } + if (!state) { - EventWaiter waiter = {nullptr, dispatcher->getCurrentContext()}; + EventWaiter waiter = { false, nullptr, nullptr, dispatcher->getCurrentContext() }; + waiter.context->interruptProcedure = [&] { + if (waiter.next != nullptr) { + assert(waiter.next->prev == &waiter); + waiter.next->prev = waiter.prev; + } else { + assert(last == &waiter); + last = waiter.prev; + } + + if (waiter.prev != nullptr) { + assert(waiter.prev->next == &waiter); + waiter.prev->next = waiter.next; + } else { + assert(first == &waiter); + first = waiter.next; + } + + assert(!waiter.interrupted); + waiter.interrupted = true; + dispatcher->pushContext(waiter.context); + }; + if (first != nullptr) { static_cast(last)->next = &waiter; + waiter.prev = static_cast(last); } else { first = &waiter; } @@ -104,7 +135,11 @@ void Event::wait() { last = &waiter; dispatcher->dispatch(); assert(waiter.context == dispatcher->getCurrentContext()); + assert( waiter.context->interruptProcedure == nullptr); assert(dispatcher != nullptr); + if (waiter.interrupted) { + throw InterruptedException(); + } } } diff --git a/src/System/EventLock.cpp b/src/System/EventLock.cpp old mode 100644 new mode 100755 diff --git a/src/System/EventLock.h b/src/System/EventLock.h old mode 100644 new mode 100755 index e44532bbd3..e2dedf6b88 --- a/src/System/EventLock.h +++ b/src/System/EventLock.h @@ -25,6 +25,7 @@ class EventLock { public: explicit EventLock(Event& event); ~EventLock(); + EventLock& operator=(const EventLock&) = delete; private: Event& event; diff --git a/src/System/InterruptedException.cpp b/src/System/InterruptedException.cpp index 4456177f39..186904ca3f 100755 --- a/src/System/InterruptedException.cpp +++ b/src/System/InterruptedException.cpp @@ -16,3 +16,7 @@ // along with Bytecoin. If not, see . #include "InterruptedException.h" + +namespace { +char suppressMSVCWarningLNK4221; +} diff --git a/src/System/InterruptedException.h b/src/System/InterruptedException.h index 81a34e0db7..b00855251e 100755 --- a/src/System/InterruptedException.h +++ b/src/System/InterruptedException.h @@ -22,6 +22,10 @@ namespace System { class InterruptedException : public std::exception { + public: + const char* what() const throw() { + return "interrupted"; + } }; } diff --git a/src/System/Ipv4Address.cpp b/src/System/Ipv4Address.cpp index 75e9fcee36..bad6e95a68 100755 --- a/src/System/Ipv4Address.cpp +++ b/src/System/Ipv4Address.cpp @@ -22,7 +22,7 @@ namespace System { namespace { -uint8_t readUint8(const std::string& source, std::size_t& offset) { +uint8_t readUint8(const std::string& source, size_t& offset) { if (offset == source.size() || source[offset] < '0' || source[offset] > '9') { throw std::runtime_error("Unable to read value from string"); } @@ -58,7 +58,7 @@ Ipv4Address::Ipv4Address(uint32_t value) : value(value) { } Ipv4Address::Ipv4Address(const std::string& dottedDecimal) { - std::size_t offset = 0; + size_t offset = 0; value = readUint8(dottedDecimal, offset); if (offset == dottedDecimal.size() || dottedDecimal[offset] != '.') { throw std::runtime_error("Invalid Ipv4 address string"); @@ -115,11 +115,11 @@ bool Ipv4Address::isLoopback() const { bool Ipv4Address::isPrivate() const { return // 10.0.0.0/8 - (value & UINT32_C(0xff000000)) == (UINT32_C(10) << 24) || + (value & 0xff000000) == (10 << 24) || // 172.16.0.0/12 - (value & UINT32_C(0xfff00000)) == ((UINT32_C(172) << 24) | (UINT32_C(16) << 16)) || + (value & 0xfff00000) == ((172 << 24) | (16 << 16)) || // 192.168.0.0/16 - (value & UINT32_C(0xffff0000)) == ((UINT32_C(192) << 24) | (UINT32_C(168) << 16)); + (value & 0xffff0000) == ((192 << 24) | (168 << 16)); } } diff --git a/src/System/Latch.cpp b/src/System/Latch.cpp deleted file mode 100755 index edc0e513ca..0000000000 --- a/src/System/Latch.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "Latch.h" -#include -#include - -namespace System { - -namespace { - -struct LatchWaiter { - LatchWaiter* next; - void* context; -}; - -} - -Latch::Latch() : dispatcher(nullptr) { -} - -Latch::Latch(Dispatcher& dispatcher) : dispatcher(&dispatcher), value(0) { -} - -Latch::Latch(Latch&& other) : dispatcher(other.dispatcher) { - if (dispatcher != nullptr) { - value = other.value; - if (value > 0) { - assert(other.first == nullptr); - first = nullptr; - } - - other.dispatcher = nullptr; - } -} - -Latch::~Latch() { - assert(dispatcher == nullptr || value == 0 || first == nullptr); -} - -Latch& Latch::operator=(Latch&& other) { - assert(dispatcher == nullptr || value == 0 || first == nullptr); - dispatcher = other.dispatcher; - if (dispatcher != nullptr) { - value = other.value; - if (value > 0) { - assert(other.first == nullptr); - first = nullptr; - } - - other.dispatcher = nullptr; - } - - return *this; -} - -std::size_t Latch::get() const { - assert(dispatcher != nullptr); - return value; -} - -void Latch::increase(std::size_t value) { - assert(dispatcher != nullptr); - if (value > 0) { - if (this->value == 0) { - first = nullptr; - } - - this->value += value; - } -} - -void Latch::decrease(std::size_t value) { - assert(dispatcher != nullptr); - if (value > 0) { - assert(value <= this->value); - if (this->value > 0) { - this->value -= value; - if (this->value == 0) { - for (LatchWaiter* waiter = static_cast(first); waiter != nullptr; waiter = waiter->next) { - dispatcher->pushContext(waiter->context); - } - } - } - } -} - -void Latch::wait() { - assert(dispatcher != nullptr); - if (value > 0) { - LatchWaiter waiter = {nullptr, dispatcher->getCurrentContext()}; - if (first != nullptr) { - static_cast(last)->next = &waiter; - } else { - first = &waiter; - } - - last = &waiter; - dispatcher->dispatch(); - assert(waiter.context == dispatcher->getCurrentContext()); - assert(dispatcher != nullptr); - } -} - -} diff --git a/src/CryptoNote/MultisignatureOutput.h b/src/System/OperationTimeout.h similarity index 55% rename from src/CryptoNote/MultisignatureOutput.h rename to src/System/OperationTimeout.h index db1006ef37..cb7cedaf30 100755 --- a/src/CryptoNote/MultisignatureOutput.h +++ b/src/System/OperationTimeout.h @@ -17,24 +17,34 @@ #pragma once -#include "../crypto/crypto.h" +#include +#include +#include -namespace CryptoNote { +namespace System { -class MultisignatureOutput { +template class OperationTimeout { public: - MultisignatureOutput(uint64_t amount, std::vector&& keys, uint32_t requiredSignatureCount); - MultisignatureOutput(const MultisignatureOutput& other) = delete; - MultisignatureOutput& operator=(const MultisignatureOutput& other) = delete; - uint64_t getAmount() const; - uint32_t getKeyCount() const; - const crypto::public_key& getKey(uint32_t index) const; - uint32_t getRequiredSignatureCount() const; + OperationTimeout(Dispatcher& dispatcher, T& object, std::chrono::nanoseconds timeout) : + object(object), timerContext(dispatcher), timeoutTimer(dispatcher) { + timerContext.spawn([this, timeout]() { + try { + timeoutTimer.sleep(timeout); + timerContext.interrupt(); + } catch (std::exception&) { + } + }); + } + + ~OperationTimeout() { + timerContext.interrupt(); + timerContext.wait(); + } private: - uint64_t amount; - std::vector keys; - uint32_t requiredSignatureCount; + T& object; + ContextGroup timerContext; + Timer timeoutTimer; }; } diff --git a/src/System/RemoteEventLock.cpp b/src/System/RemoteEventLock.cpp new file mode 100755 index 0000000000..3082ea00a3 --- /dev/null +++ b/src/System/RemoteEventLock.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "RemoteEventLock.h" +#include +#include +#include +#include +#include + +namespace System { + +RemoteEventLock::RemoteEventLock(Dispatcher& dispatcher, Event& event) : dispatcher(dispatcher), event(event) { + std::mutex mutex; + std::condition_variable condition; + bool locked = false; + + dispatcher.remoteSpawn([&]() { + while (!event.get()) { + event.wait(); + } + + event.clear(); + mutex.lock(); + locked = true; + condition.notify_one(); + mutex.unlock(); + }); + + std::unique_lock lock(mutex); + while (!locked) { + condition.wait(lock); + } +} + +RemoteEventLock::~RemoteEventLock() { + Event* eventPointer = &event; + dispatcher.remoteSpawn([=]() { + assert(!eventPointer->get()); + eventPointer->set(); + }); +} + +} diff --git a/src/System/RemoteEventLock.h b/src/System/RemoteEventLock.h new file mode 100755 index 0000000000..a3b6a3eb4d --- /dev/null +++ b/src/System/RemoteEventLock.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +namespace System { + +class Dispatcher; +class Event; + +class RemoteEventLock { +public: + RemoteEventLock(Dispatcher& dispatcher, Event& event); + ~RemoteEventLock(); + +private: + Dispatcher& dispatcher; + Event& event; +}; + +} diff --git a/src/transfers/BlockchainSynchronizer.cpp b/src/Transfers/BlockchainSynchronizer.cpp old mode 100644 new mode 100755 similarity index 89% rename from src/transfers/BlockchainSynchronizer.cpp rename to src/Transfers/BlockchainSynchronizer.cpp index cadf8a46f0..9876fc2a39 --- a/src/transfers/BlockchainSynchronizer.cpp +++ b/src/Transfers/BlockchainSynchronizer.cpp @@ -16,12 +16,16 @@ // along with Bytecoin. If not, see . #include "BlockchainSynchronizer.h" -#include "cryptonote_core/TransactionApi.h" -#include "cryptonote_core/cryptonote_format_utils.h" + #include -#include +#include #include +#include +#include "CryptoNoteCore/TransactionApi.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" + +using namespace Crypto; namespace { @@ -36,7 +40,7 @@ inline std::vector stringToVector(const std::string& s) { namespace CryptoNote { -BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const crypto::hash& genesisBlockHash) : +BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const Hash& genesisBlockHash) : m_node(node), m_genesisBlockHash(genesisBlockHash), m_currentState(State::stopped), m_futureState(State::stopped), shouldSyncConsumersPool(true) { } @@ -89,7 +93,7 @@ void BlockchainSynchronizer::save(std::ostream& os) { } void BlockchainSynchronizer::load(std::istream& in) { - crypto::hash genesisBlockHash; + Hash genesisBlockHash; in.read(reinterpret_cast(&genesisBlockHash), sizeof(genesisBlockHash)); if (genesisBlockHash != m_genesisBlockHash) { throw std::runtime_error("Genesis block hash does not match stored state"); @@ -194,10 +198,10 @@ void BlockchainSynchronizer::stop() { workingThread->join(); } - workingThread.release(); + workingThread.reset(); } -void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint64_t height) { +void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint32_t height) { setFutureState(State::blockchainSync); } @@ -208,11 +212,11 @@ void BlockchainSynchronizer::poolChanged() { BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getUnionPoolHistory() { GetPoolRequest request; - std::unordered_set unionHistory; + std::unordered_set unionHistory; { std::unique_lock lk(m_consumersMutex); for (auto& consumer : m_consumers) { - std::vector consumerKnownIds; + std::vector consumerKnownIds; consumer.first->getKnownPoolTxIds(consumerKnownIds); for (auto& txId : consumerKnownIds) { unionHistory.insert(txId); @@ -238,7 +242,7 @@ BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getIntersectedPoo ++it; for (; it != m_consumers.end(); ++it) { //iterate over consumers - std::vector consumerKnownIds; + std::vector consumerKnownIds; it->first->getKnownPoolTxIds(consumerKnownIds); for (auto itReq = request.knownTxIds.begin(); itReq != request.knownTxIds.end();) { //iterate over intersection if (std::count(consumerKnownIds.begin(), consumerKnownIds.end(), *itReq) == 0) { //consumer doesn't contain id from intersection, so delete this id from intersection @@ -289,7 +293,8 @@ void BlockchainSynchronizer::startBlockchainSync() { asyncOperationCompleted = std::promise(); asyncOperationWaitFuture = asyncOperationCompleted.get_future(); - m_node.queryBlocks(std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight, + m_node.queryBlocks + (std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight, std::bind(&BlockchainSynchronizer::onGetBlocksCompleted, this, std::placeholders::_1)); std::error_code ec = asyncOperationWaitFuture.get(); @@ -297,11 +302,9 @@ void BlockchainSynchronizer::startBlockchainSync() { if (ec) { setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); - m_observerManager.notify( - &IBlockchainSynchronizerObserver::synchronizationCompleted, - ec); + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec); } else { processBlocks(response); } @@ -324,48 +327,37 @@ void BlockchainSynchronizer::onGetBlocksCompleted(std::error_code ec) { } void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { - auto newHeight = response.startHeight + response.newBlocks.size(); + uint32_t newHeight = response.startHeight + static_cast(response.newBlocks.size()); BlockchainInterval interval; interval.startHeight = response.startHeight; std::vector blocks; - // parse blocks - for (const auto& block : response.newBlocks) { + for (auto& block : response.newBlocks) { if (checkIfShouldStop()) { break; } CompleteBlock completeBlock; completeBlock.blockHash = block.blockHash; interval.blocks.push_back(completeBlock.blockHash); - if (!block.block.empty()) { - Block parsedBlock; - if (!parse_and_validate_block_from_blob(block.block, parsedBlock)) { - setFutureStateIf(State::idle, std::bind( - [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); - m_observerManager.notify( - &IBlockchainSynchronizerObserver::synchronizationCompleted, - std::make_error_code(std::errc::invalid_argument)); - return; - } - - completeBlock.block = std::move(parsedBlock); - - completeBlock.transactions.push_back(createTransaction(completeBlock.block->minerTx)); + if (block.hasBlock) { + completeBlock.block = std::move(block.block); + completeBlock.transactions.push_back(createTransactionPrefix(completeBlock.block->baseTransaction)); try { - for (const auto& txblob : block.txs) { - completeBlock.transactions.push_back(createTransaction(stringToVector(txblob))); + for (const auto& txShortInfo : block.txsShortInfo) { + completeBlock.transactions.push_back(createTransactionPrefix(txShortInfo.txPrefix, + reinterpret_cast(txShortInfo.txId))); } - } catch (std::exception &) { + } catch (std::exception&) { setFutureStateIf(State::idle, std::bind( [](State futureState) -> bool { - return futureState != State::stopped; - }, std::ref(m_futureState))); + return futureState != State::stopped; + }, std::ref(m_futureState))); + m_observerManager.notify( &IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument)); + return; } } @@ -430,17 +422,17 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons } if (result.hasNewBlocks) { - size_t startOffset = result.newBlockHeight - interval.startHeight; + uint32_t startOffset = result.newBlockHeight - interval.startHeight; // update consumer if (kv.first->onNewBlocks( blocks.data() + startOffset, result.newBlockHeight, - blocks.size() - startOffset)) { + static_cast(blocks.size()) - startOffset)) { // update state if consumer succeeded kv.second->addBlocks( interval.blocks.data() + startOffset, result.newBlockHeight, - interval.blocks.size() - startOffset); + static_cast(interval.blocks.size()) - startOffset); smthChanged = true; } else { return UpdateConsumersResult::errorOccured; diff --git a/src/transfers/BlockchainSynchronizer.h b/src/Transfers/BlockchainSynchronizer.h old mode 100644 new mode 100755 similarity index 88% rename from src/transfers/BlockchainSynchronizer.h rename to src/Transfers/BlockchainSynchronizer.h index feeb508cfc..105915248b --- a/src/transfers/BlockchainSynchronizer.h +++ b/src/Transfers/BlockchainSynchronizer.h @@ -35,7 +35,7 @@ class BlockchainSynchronizer : public INodeObserver { public: - BlockchainSynchronizer(INode& node, const crypto::hash& genesisBlockHash); + BlockchainSynchronizer(INode& node, const Crypto::Hash& genesisBlockHash); ~BlockchainSynchronizer(); // IBlockchainSynchronizer @@ -51,14 +51,14 @@ class BlockchainSynchronizer : virtual void load(std::istream& in) override; // INodeObserver - virtual void lastKnownBlockHeightUpdated(uint64_t height) override; + virtual void lastKnownBlockHeightUpdated(uint32_t height) override; virtual void poolChanged() override; private: struct GetBlocksResponse { - uint64_t startHeight; - std::list newBlocks; + uint32_t startHeight; + std::vector newBlocks; }; struct GetBlocksRequest { @@ -67,18 +67,18 @@ class BlockchainSynchronizer : syncStart.height = 0; } SynchronizationStart syncStart; - std::list knownBlocks; + std::vector knownBlocks; }; struct GetPoolResponse { bool isLastKnownBlockActual; - std::vector newTxs; - std::vector deletedTxIds; + std::vector> newTxs; + std::vector deletedTxIds; }; struct GetPoolRequest { - std::vector knownTxIds; - crypto::hash lastKnownBlock; + std::vector knownTxIds; + Crypto::Hash lastKnownBlock; }; @@ -123,9 +123,9 @@ class BlockchainSynchronizer : ConsumersMap m_consumers; INode& m_node; - const crypto::hash m_genesisBlockHash; + const Crypto::Hash m_genesisBlockHash; - crypto::hash lastBlockId; + Crypto::Hash lastBlockId; State m_currentState; State m_futureState; diff --git a/src/transfers/CommonTypes.h b/src/Transfers/CommonTypes.h old mode 100644 new mode 100755 similarity index 93% rename from src/transfers/CommonTypes.h rename to src/Transfers/CommonTypes.h index f349c8ead6..9f0ac95d2f --- a/src/transfers/CommonTypes.h +++ b/src/Transfers/CommonTypes.h @@ -29,12 +29,12 @@ namespace CryptoNote { struct BlockchainInterval { - uint64_t startHeight; - std::vector blocks; + uint32_t startHeight; + std::vector blocks; }; struct CompleteBlock { - crypto::hash blockHash; + Crypto::Hash blockHash; boost::optional block; // first transaction is always coinbase std::list> transactions; diff --git a/src/transfers/IBlockchainSynchronizer.h b/src/Transfers/IBlockchainSynchronizer.h old mode 100644 new mode 100755 similarity index 74% rename from src/transfers/IBlockchainSynchronizer.h rename to src/Transfers/IBlockchainSynchronizer.h index 6842ed5422..9deb71229d --- a/src/transfers/IBlockchainSynchronizer.h +++ b/src/Transfers/IBlockchainSynchronizer.h @@ -21,7 +21,7 @@ #include #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" #include "IObservable.h" #include "IStreamSerializable.h" @@ -33,18 +33,18 @@ struct CompleteBlock; class IBlockchainSynchronizerObserver { public: - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) {} + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) {} virtual void synchronizationCompleted(std::error_code result) {} }; class IBlockchainConsumer { public: - + virtual ~IBlockchainConsumer() {} virtual SynchronizationStart getSyncStart() = 0; - virtual void getKnownPoolTxIds(std::vector& ids) = 0; - virtual void onBlockchainDetach(uint64_t height) = 0; - virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) = 0; - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) = 0; + virtual void getKnownPoolTxIds(std::vector& ids) = 0; + virtual void onBlockchainDetach(uint32_t height) = 0; + virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) = 0; + virtual std::error_code onPoolUpdated(const std::vector>& addedTransactions, const std::vector& deletedTransactions) = 0; }; diff --git a/src/transfers/IObservableImpl.h b/src/Transfers/IObservableImpl.h old mode 100644 new mode 100755 similarity index 95% rename from src/transfers/IObservableImpl.h rename to src/Transfers/IObservableImpl.h index 64868b1b69..a3085c392c --- a/src/transfers/IObservableImpl.h +++ b/src/Transfers/IObservableImpl.h @@ -34,7 +34,7 @@ class IObservableImpl : public Base { } protected: - tools::ObserverManager m_observerManager; + Tools::ObserverManager m_observerManager; }; } diff --git a/src/transfers/SynchronizationState.cpp b/src/Transfers/SynchronizationState.cpp old mode 100644 new mode 100755 similarity index 67% rename from src/transfers/SynchronizationState.cpp rename to src/Transfers/SynchronizationState.cpp index f537cf2764..4b3d222446 --- a/src/transfers/SynchronizationState.cpp +++ b/src/Transfers/SynchronizationState.cpp @@ -17,23 +17,26 @@ #include "SynchronizationState.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" -namespace CryptoNote { +using namespace Common; -SynchronizationState::ShortHistory SynchronizationState::getShortHistory(size_t localHeight) const { +namespace CryptoNote { +SynchronizationState::ShortHistory SynchronizationState::getShortHistory(uint32_t localHeight) const { ShortHistory history; - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = std::min(m_blockchain.size(), localHeight + 1); + uint32_t i = 0; + uint32_t current_multiplier = 1; + uint32_t sz = std::min(static_cast(m_blockchain.size()), localHeight + 1); if (!sz) return history; - size_t current_back_offset = 1; + uint32_t current_back_offset = 1; bool genesis_included = false; while (current_back_offset < sz) { @@ -59,10 +62,10 @@ SynchronizationState::CheckResult SynchronizationState::checkInterval(const Bloc CheckResult result = { false, 0, false, 0 }; - size_t intervalEnd = interval.startHeight + interval.blocks.size(); - size_t iterationEnd = std::min(m_blockchain.size(), intervalEnd); + uint32_t intervalEnd = interval.startHeight + static_cast(interval.blocks.size()); + uint32_t iterationEnd = std::min(static_cast(m_blockchain.size()), intervalEnd); - for (size_t i = interval.startHeight; i < iterationEnd; ++i) { + for (uint32_t i = interval.startHeight; i < iterationEnd; ++i) { if (m_blockchain[i] != interval.blocks[i - interval.startHeight]) { result.detachRequired = true; result.detachHeight = i; @@ -78,35 +81,37 @@ SynchronizationState::CheckResult SynchronizationState::checkInterval(const Bloc if (intervalEnd > m_blockchain.size()) { result.hasNewBlocks = true; - result.newBlockHeight = m_blockchain.size(); + result.newBlockHeight = static_cast(m_blockchain.size()); } return result; } -void SynchronizationState::detach(uint64_t height) { +void SynchronizationState::detach(uint32_t height) { assert(height < m_blockchain.size()); m_blockchain.resize(height); } -void SynchronizationState::addBlocks(const crypto::hash* blockHashes, uint64_t height, size_t count) { +void SynchronizationState::addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count) { assert(blockHashes); auto size = m_blockchain.size(); assert( size == height); m_blockchain.insert(m_blockchain.end(), blockHashes, blockHashes + count); } -uint64_t SynchronizationState::getHeight() const { - return m_blockchain.size(); +uint32_t SynchronizationState::getHeight() const { + return static_cast(m_blockchain.size()); } void SynchronizationState::save(std::ostream& os) { - CryptoNote::BinaryOutputStreamSerializer s(os); + StdOutputStream stream(os); + CryptoNote::BinaryOutputStreamSerializer s(stream); serialize(s, "state"); } void SynchronizationState::load(std::istream& in) { - CryptoNote::BinaryInputStreamSerializer s(in); + StdInputStream stream(in); + CryptoNote::BinaryInputStreamSerializer s(stream); serialize(s, "state"); } diff --git a/src/transfers/SynchronizationState.h b/src/Transfers/SynchronizationState.h old mode 100644 new mode 100755 similarity index 75% rename from src/transfers/SynchronizationState.h rename to src/Transfers/SynchronizationState.h index 9e55345afb..13990fa06d --- a/src/transfers/SynchronizationState.h +++ b/src/Transfers/SynchronizationState.h @@ -19,7 +19,7 @@ #include "CommonTypes.h" #include "IStreamSerializable.h" -#include "serialization/ISerializer.h" +#include "Serialization/ISerializer.h" #include #include @@ -30,23 +30,23 @@ class SynchronizationState : public IStreamSerializable { struct CheckResult { bool detachRequired; - uint64_t detachHeight; + uint32_t detachHeight; bool hasNewBlocks; - uint64_t newBlockHeight; + uint32_t newBlockHeight; }; - typedef std::list ShortHistory; + typedef std::vector ShortHistory; - explicit SynchronizationState(const crypto::hash& genesisBlockHash) { + explicit SynchronizationState(const Crypto::Hash& genesisBlockHash) { m_blockchain.push_back(genesisBlockHash); } - ShortHistory getShortHistory(size_t localHeight) const; + ShortHistory getShortHistory(uint32_t localHeight) const; CheckResult checkInterval(const BlockchainInterval& interval) const; - void detach(uint64_t height); - void addBlocks(const crypto::hash* blockHashes, uint64_t height, size_t count); - uint64_t getHeight() const; + void detach(uint32_t height); + void addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count); + uint32_t getHeight() const; // IStreamSerializable virtual void save(std::ostream& os) override; @@ -57,7 +57,7 @@ class SynchronizationState : public IStreamSerializable { private: - std::vector m_blockchain; + std::vector m_blockchain; }; } diff --git a/src/transfers/TransfersConsumer.cpp b/src/Transfers/TransfersConsumer.cpp old mode 100644 new mode 100755 similarity index 81% rename from src/transfers/TransfersConsumer.cpp rename to src/Transfers/TransfersConsumer.cpp index ab2b08f71b..f68437c4d5 --- a/src/transfers/TransfersConsumer.cpp +++ b/src/Transfers/TransfersConsumer.cpp @@ -19,19 +19,21 @@ #include "CommonTypes.h" #include "Common/BlockingQueue.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/TransactionApi.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/TransactionApi.h" -#include "IWallet.h" +#include "IWalletLegacy.h" #include "INode.h" #include +using namespace Crypto; + namespace { using namespace CryptoNote; void checkOutputKey( - const crypto::key_derivation& derivation, + const KeyDerivation& derivation, const PublicKey& key, size_t keyIndex, size_t outputIndex, @@ -39,9 +41,7 @@ void checkOutputKey( std::unordered_map>& outputs) { PublicKey spendKey; - crypto::underive_public_key(derivation, keyIndex, - reinterpret_cast(key), - reinterpret_cast(spendKey)); + underive_public_key(derivation, keyIndex, key, spendKey); if (spendKeys.find(spendKey) != spendKeys.end()) { outputs[spendKey].push_back(static_cast(outputIndex)); @@ -56,12 +56,9 @@ void findMyOutputs( std::unordered_map>& outputs) { auto txPublicKey = tx.getTransactionPublicKey(); - crypto::key_derivation derivation; + KeyDerivation derivation; - if (!crypto::generate_key_derivation( - reinterpret_cast(txPublicKey), - reinterpret_cast(viewSecretKey), - derivation)) { + if (!generate_key_derivation( txPublicKey, viewSecretKey, derivation)) { return; } @@ -74,15 +71,17 @@ void findMyOutputs( if (outType == TransactionTypes::OutputType::Key) { - TransactionTypes::OutputKey out; - tx.getOutput(idx, out); + uint64_t amount; + KeyOutput out; + tx.getOutput(idx, out, amount); checkOutputKey(derivation, out.key, keyIndex, idx, spendKeys, outputs); ++keyIndex; } else if (outType == TransactionTypes::OutputType::Multisignature) { - TransactionTypes::OutputMultisignature out; - tx.getOutput(idx, out); + uint64_t amount; + MultisignatureOutput out; + tx.getOutput(idx, out, amount); for (const auto& key : out.keys) { checkOutputKey(derivation, key, idx, idx, spendKeys, outputs); ++keyIndex; @@ -116,19 +115,19 @@ ITransfersSubscription& TransfersConsumer::addSubscription(const AccountSubscrip return *res; } -bool TransfersConsumer::removeSubscription(const AccountAddress& address) { +bool TransfersConsumer::removeSubscription(const AccountPublicAddress& address) { m_subscriptions.erase(address.spendPublicKey); m_spendKeys.erase(address.spendPublicKey); updateSyncStart(); return m_subscriptions.empty(); } -ITransfersSubscription* TransfersConsumer::getSubscription(const AccountAddress& acc) { +ITransfersSubscription* TransfersConsumer::getSubscription(const AccountPublicAddress& acc) { auto it = m_subscriptions.find(acc.spendPublicKey); return it == m_subscriptions.end() ? nullptr : it->second.get(); } -void TransfersConsumer::getSubscriptions(std::vector& subscriptions) { +void TransfersConsumer::getSubscriptions(std::vector& subscriptions) { for (const auto& kv : m_subscriptions) { subscriptions.push_back(kv.second->getAddress()); } @@ -137,7 +136,7 @@ void TransfersConsumer::getSubscriptions(std::vector& subscripti void TransfersConsumer::updateSyncStart() { SynchronizationStart start; - start.height = std::numeric_limits::max(); + start.height = std::numeric_limits::max(); start.timestamp = std::numeric_limits::max(); for (const auto& kv : m_subscriptions) { @@ -153,13 +152,13 @@ SynchronizationStart TransfersConsumer::getSyncStart() { return m_syncStart; } -void TransfersConsumer::onBlockchainDetach(uint64_t height) { +void TransfersConsumer::onBlockchainDetach(uint32_t height) { for (const auto& kv : m_subscriptions) { kv.second->onBlockchainDetach(height); } } -bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) { +bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) { assert(blocks); struct Tx { @@ -182,7 +181,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH std::atomic stopProcessing(false); auto pushingThread = std::async(std::launch::async, [&] { - for (size_t i = 0; i < count && !stopProcessing; ++i) { + for( uint32_t i = 0; i < count && !stopProcessing; ++i) { const auto& block = blocks[i].block; if (!block.is_initialized()) { @@ -201,7 +200,7 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH for (const auto& tx : blocks[i].transactions) { auto pubKey = tx->getTransactionPublicKey(); - if (*reinterpret_cast(&pubKey) == CryptoNote::null_pkey) { + if (pubKey == NULL_PUBLIC_KEY) { ++blockInfo.transactionIndex; continue; } @@ -282,14 +281,13 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint64_t startH return true; } -std::error_code TransfersConsumer::onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) { +std::error_code TransfersConsumer::onPoolUpdated(const std::vector>& addedTransactions, const std::vector& deletedTransactions) { BlockInfo unconfirmedBlockInfo; unconfirmedBlockInfo.timestamp = 0; - unconfirmedBlockInfo.height = UNCONFIRMED_TRANSACTION_HEIGHT; + unconfirmedBlockInfo.height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; std::error_code processingError; for (auto& cryptonoteTransaction : addedTransactions) { - auto transaction = CryptoNote::createTransaction(cryptonoteTransaction); - processingError = processTransaction(unconfirmedBlockInfo, *transaction.get()); + processingError = processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get()); if (processingError) { break; } @@ -297,7 +295,7 @@ std::error_code TransfersConsumer::onPoolUpdated(const std::vector& if (processingError) { for (auto& sub : m_subscriptions) { - sub.second->onError(processingError, UNCONFIRMED_TRANSACTION_HEIGHT); + sub.second->onError(processingError, WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); } return processingError; @@ -312,11 +310,11 @@ std::error_code TransfersConsumer::onPoolUpdated(const std::vector& return std::error_code(); } -void TransfersConsumer::getKnownPoolTxIds(std::vector& ids) { +void TransfersConsumer::getKnownPoolTxIds(std::vector& ids) { ids.clear(); - std::unordered_set knownIds; + std::unordered_set knownIds; for (auto& sub : m_subscriptions) { - std::vector subscriptionUnconfirmedTxIds; + std::vector subscriptionUnconfirmedTxIds; sub.second->getContainer().getUnconfirmedTransactions(subscriptionUnconfirmedTxIds); knownIds.insert(subscriptionUnconfirmedTxIds.begin(), subscriptionUnconfirmedTxIds.end()); } @@ -330,7 +328,7 @@ std::error_code createTransfers( const BlockInfo& blockInfo, const ITransactionReader& tx, const std::vector& outputs, - const std::vector& globalIdxs, + const std::vector& globalIdxs, std::vector& transfers) { auto txPubKey = tx.getTransactionPublicKey(); @@ -354,32 +352,34 @@ std::error_code createTransfers( info.type = outType; info.transactionPublicKey = txPubKey; info.outputInTransaction = idx; - info.globalOutputIndex = (blockInfo.height == UNCONFIRMED_TRANSACTION_HEIGHT) ? + info.globalOutputIndex = (blockInfo.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) ? UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx]; if (outType == TransactionTypes::OutputType::Key) { - TransactionTypes::OutputKey out; - tx.getOutput(idx, out); + uint64_t amount; + KeyOutput out; + tx.getOutput(idx, out, amount); CryptoNote::KeyPair in_ephemeral; CryptoNote::generate_key_image_helper( - reinterpret_cast(account), - reinterpret_cast(txPubKey), + account, + txPubKey, idx, in_ephemeral, - reinterpret_cast(info.keyImage)); + info.keyImage); - assert(out.key == reinterpret_cast(in_ephemeral.pub)); + assert(out.key == reinterpret_cast(in_ephemeral.publicKey)); - info.amount = out.amount; + info.amount = amount; info.outputKey = out.key; } else if (outType == TransactionTypes::OutputType::Multisignature) { - TransactionTypes::OutputMultisignature out; - tx.getOutput(idx, out); + uint64_t amount; + MultisignatureOutput out; + tx.getOutput(idx, out, amount); - info.amount = out.amount; - info.requiredSignatures = out.requiredSignatures; + info.amount = amount; + info.requiredSignatures = out.requiredSignatureCount; } transfers.push_back(info); @@ -398,8 +398,8 @@ std::error_code TransfersConsumer::preprocessOutputs(const BlockInfo& blockInfo, std::error_code errorCode; auto txHash = tx.getTransactionHash(); - if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { - errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); + if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + errorCode = getGlobalIndices(reinterpret_cast(txHash), info.globalIdxs); if (errorCode) { return errorCode; } @@ -448,13 +448,13 @@ std::error_code TransfersConsumer::processTransaction(const BlockInfo& blockInfo std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, - const ITransactionReader& tx, const std::vector& transfers, const std::vector& globalIdxs) { + const ITransactionReader& tx, const std::vector& transfers, const std::vector& globalIdxs) { - if (blockInfo.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { TransactionInformation subscribtionTxInfo; int64_t txBalance; if (sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo, txBalance)) { - if (subscribtionTxInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (subscribtionTxInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { // pool->blockchain sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs); return std::error_code(); @@ -471,7 +471,7 @@ std::error_code TransfersConsumer::processOutputs(const BlockInfo& blockInfo, Tr } -std::error_code TransfersConsumer::getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices) { +std::error_code TransfersConsumer::getGlobalIndices(const Hash& transactionHash, std::vector& outsGlobalIndices) { std::promise prom; std::future f = prom.get_future(); diff --git a/src/transfers/TransfersConsumer.h b/src/Transfers/TransfersConsumer.h old mode 100644 new mode 100755 similarity index 66% rename from src/transfers/TransfersConsumer.h rename to src/Transfers/TransfersConsumer.h index bff3423874..e8166beca4 --- a/src/transfers/TransfersConsumer.h +++ b/src/Transfers/TransfersConsumer.h @@ -36,20 +36,20 @@ class TransfersConsumer : public IBlockchainConsumer { public: - TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const SecretKey& viewSecret); + TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const Crypto::SecretKey& viewSecret); ITransfersSubscription& addSubscription(const AccountSubscription& subscription); // returns true if no subscribers left - bool removeSubscription(const AccountAddress& address); - ITransfersSubscription* getSubscription(const AccountAddress& acc); - void getSubscriptions(std::vector& subscriptions); + bool removeSubscription(const AccountPublicAddress& address); + ITransfersSubscription* getSubscription(const AccountPublicAddress& acc); + void getSubscriptions(std::vector& subscriptions); // IBlockchainConsumer virtual SynchronizationStart getSyncStart() override; - virtual void onBlockchainDetach(uint64_t height) override; - virtual bool onNewBlocks(const CompleteBlock* blocks, uint64_t startHeight, size_t count) override; - virtual std::error_code onPoolUpdated(const std::vector& addedTransactions, const std::vector& deletedTransactions) override; - virtual void getKnownPoolTxIds(std::vector& ids) override; + virtual void onBlockchainDetach(uint32_t height) override; + virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) override; + virtual std::error_code onPoolUpdated(const std::vector>& addedTransactions, const std::vector& deletedTransactions) override; + virtual void getKnownPoolTxIds(std::vector& ids) override; private: @@ -61,25 +61,25 @@ class TransfersConsumer : public IBlockchainConsumer { } struct PreprocessInfo { - std::unordered_map> outputs; - std::vector globalIdxs; + std::unordered_map> outputs; + std::vector globalIdxs; }; std::error_code preprocessOutputs(const BlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info); std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx); std::error_code processTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info); std::error_code processOutputs(const BlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, - const std::vector& outputs, const std::vector& globalIdxs); + const std::vector& outputs, const std::vector& globalIdxs); - std::error_code getGlobalIndices(const crypto::hash& transactionHash, std::vector& outsGlobalIndices); + std::error_code getGlobalIndices(const Crypto::Hash& transactionHash, std::vector& outsGlobalIndices); void updateSyncStart(); SynchronizationStart m_syncStart; - const SecretKey m_viewSecret; + const Crypto::SecretKey m_viewSecret; // map { spend public key -> subscription } - std::unordered_map> m_subscriptions; - std::unordered_set m_spendKeys; + std::unordered_map> m_subscriptions; + std::unordered_set m_spendKeys; INode& m_node; const CryptoNote::Currency& m_currency; diff --git a/src/transfers/TransfersContainer.cpp b/src/Transfers/TransfersContainer.cpp old mode 100644 new mode 100755 similarity index 92% rename from src/transfers/TransfersContainer.cpp rename to src/Transfers/TransfersContainer.cpp index ed46916544..39dba73f9f --- a/src/transfers/TransfersContainer.cpp +++ b/src/Transfers/TransfersContainer.cpp @@ -16,11 +16,15 @@ // along with Bytecoin. If not, see . #include "TransfersContainer.h" -#include "IWallet.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "IWalletLegacy.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" +using namespace Common; +using namespace Crypto; namespace CryptoNote { @@ -116,7 +120,7 @@ SpentOutputDescriptor::SpentOutputDescriptor(const KeyImage* keyImage) { assign(keyImage); } -SpentOutputDescriptor::SpentOutputDescriptor(uint64_t amount, uint64_t globalOutputIndex) { +SpentOutputDescriptor::SpentOutputDescriptor(uint64_t amount, uint32_t globalOutputIndex) { assign(amount, globalOutputIndex); } @@ -125,7 +129,7 @@ void SpentOutputDescriptor::assign(const KeyImage* keyImage) { m_keyImage = keyImage; } -void SpentOutputDescriptor::assign(uint64_t amount, uint64_t globalOutputIndex) { +void SpentOutputDescriptor::assign(uint64_t amount, uint32_t globalOutputIndex) { m_type = TransactionTypes::OutputType::Multisignature; m_amount = amount; m_globalOutputIndex = globalOutputIndex; @@ -149,7 +153,7 @@ bool SpentOutputDescriptor::operator==(const SpentOutputDescriptor& other) const size_t SpentOutputDescriptor::hash() const { if (m_type == TransactionTypes::OutputType::Key) { static_assert(sizeof(size_t) < sizeof(*m_keyImage), "sizeof(size_t) < sizeof(*m_keyImage)"); - return *reinterpret_cast(m_keyImage->data()); + return *reinterpret_cast(m_keyImage->data); } else if (m_type == TransactionTypes::OutputType::Multisignature) { size_t hashValue = boost::hash_value(m_amount); boost::hash_combine(hashValue, m_globalOutputIndex); @@ -186,7 +190,7 @@ bool TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti addTransaction(block, tx); } - if (block.height != UNCONFIRMED_TRANSACTION_HEIGHT) { + if (block.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { m_currentHeight = block.height; } @@ -210,7 +214,7 @@ void TransfersContainer::addTransaction(const BlockInfo& block, const ITransacti txInfo.extra = tx.getExtra(); if (!tx.getPaymentId(txInfo.paymentId)) { - txInfo.paymentId.fill(0); + txInfo.paymentId = NULL_HASH; } auto result = m_transactions.emplace(std::move(txInfo)); @@ -226,7 +230,7 @@ bool TransfersContainer::addTransactionOutputs(const BlockInfo& block, const ITr bool outputsAdded = false; auto txHash = tx.getTransactionHash(); - bool transactionIsUnconfimed = (block.height == UNCONFIRMED_TRANSACTION_HEIGHT); + bool transactionIsUnconfimed = (block.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); for (const auto& transfer : transfers) { assert(transfer.outputInTransaction < tx.getOutputCount()); assert(transfer.type == tx.getOutputType(transfer.outputInTransaction)); @@ -283,7 +287,7 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra auto inputType = tx.getInputType(i); if (inputType == TransactionTypes::InputType::Key) { - TransactionTypes::InputKey input; + KeyInput input; tx.getInput(i, input); SpentOutputDescriptor descriptor(&input.keyImage); @@ -325,7 +329,7 @@ bool TransfersContainer::addTransactionInputs(const BlockInfo& block, const ITra inputsAdded = true; } else if (inputType == TransactionTypes::InputType::Multisignature) { - TransactionTypes::InputMultisignature input; + MultisignatureInput input; tx.getInput(i, input); auto& outputDescriptorIndex = m_availableTransfers.get(); @@ -351,7 +355,7 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas auto it = m_transactions.find(transactionHash); if (it == m_transactions.end()) { return false; - } else if (it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT) { + } else if (it->blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { return false; } else { deleteTransactionTransfers(it->transactionHash); @@ -361,9 +365,9 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas } bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, - const std::vector& globalIndices) { - if (block.height == UNCONFIRMED_TRANSACTION_HEIGHT) { - throw std::invalid_argument("Block height equals UNCONFIRMED_TRANSACTION_HEIGHT"); + const std::vector& globalIndices) { + if (block.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + throw std::invalid_argument("Block height equals WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT"); } std::unique_lock lock(m_mutex); @@ -373,7 +377,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const return false; } - if (transactionIt->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT) { + if (transactionIt->blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { return false; } @@ -385,7 +389,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const auto availableRange = m_unconfirmedTransfers.get().equal_range(transactionHash); for (auto transferIt = availableRange.first; transferIt != availableRange.second; ) { auto transfer = *transferIt; - assert(transfer.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT); + assert(transfer.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(transfer.globalOutputIndex == UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); if (transfer.outputInTransaction >= globalIndices.size()) { throw std::invalid_argument("Not enough elements in globalIndices"); @@ -419,7 +423,7 @@ bool TransfersContainer::markTransactionConfirmed(const BlockInfo& block, const auto spentRange = spendingTransactionIndex.equal_range(transactionHash); for (auto transferIt = spentRange.first; transferIt != spentRange.second; ++transferIt) { auto transfer = *transferIt; - assert(transfer.spendingBlock.height == UNCONFIRMED_TRANSACTION_HEIGHT); + assert(transfer.spendingBlock.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); transfer.spendingBlock = block; spendingTransactionIndex.replace(transferIt, transfer); @@ -435,7 +439,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) auto& spendingTransactionIndex = m_spentTransfers.get(); auto spentTransfersRange = spendingTransactionIndex.equal_range(transactionHash); for (auto it = spentTransfersRange.first; it != spentTransfersRange.second;) { - assert(it->blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); + assert(it->blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(it->globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); auto result = m_availableTransfers.emplace(static_cast(*it)); @@ -476,7 +480,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash) */ void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output) { - assert(output.blockHeight != UNCONFIRMED_TRANSACTION_HEIGHT); + assert(output.blockHeight != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); assert(output.globalOutputIndex != UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX); SpentTransactionOutput spentOutput; @@ -489,9 +493,9 @@ void TransfersContainer::copyToSpent(const BlockInfo& block, const ITransactionR assert(result.second); } -std::vector TransfersContainer::detach(uint64_t height) { - // This method expects that UNCONFIRMED_TRANSACTION_HEIGHT is a big positive number - assert(height < UNCONFIRMED_TRANSACTION_HEIGHT); +std::vector TransfersContainer::detach(uint32_t height) { + // This method expects that WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT is a big positive number + assert(height < WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT); std::lock_guard lk(m_mutex); @@ -503,14 +507,14 @@ std::vector TransfersContainer::detach(uint64_t height) { --it; bool doDelete = false; - if (it->blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (it->blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { auto range = spendingTransactionIndex.equal_range(it->transactionHash); for (auto spentTransferIt = range.first; spentTransferIt != range.second; ++spentTransferIt) { if (spentTransferIt->blockHeight >= height) { doDelete = true; break; - } - } + } + } } else if (it->blockHeight >= height) { doDelete = true; } else { @@ -521,7 +525,7 @@ std::vector TransfersContainer::detach(uint64_t height) { deleteTransactionTransfers(it->transactionHash); deletedTransactions.emplace_back(it->transactionHash); it = blockHeightIndex.erase(it); - } + } } // TODO: notification on detach @@ -579,7 +583,7 @@ void TransfersContainer::updateTransfersVisibility(const KeyImage& keyImage) { } } -bool TransfersContainer::advanceHeight(uint64_t height) { +bool TransfersContainer::advanceHeight(uint32_t height) { std::lock_guard lk(m_mutex); if (m_currentHeight <= height) { @@ -648,7 +652,7 @@ bool TransfersContainer::getTransactionInformation(const Hash& transactionHash, info = *it; int64_t amountOut = 0; - if (info.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { auto unconfirmedOutputsRange = m_unconfirmedTransfers.get().equal_range(transactionHash); for (auto it = unconfirmedOutputsRange.first; it != unconfirmedOutputsRange.second; ++it) { amountOut += static_cast(it->amount); @@ -702,12 +706,12 @@ std::vector TransfersContainer::getTransactionOutp return result; } -void TransfersContainer::getUnconfirmedTransactions(std::vector& transactions) { +void TransfersContainer::getUnconfirmedTransactions(std::vector& transactions) { std::lock_guard lk(m_mutex); transactions.clear(); for (auto& element : m_transactions) { - if (element.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { - transactions.push_back(*reinterpret_cast(&element.transactionHash)); + if (element.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + transactions.push_back(*reinterpret_cast(&element.transactionHash)); } } } @@ -737,7 +741,8 @@ std::vector TransfersContainer::getSpentOutpu void TransfersContainer::save(std::ostream& os) { std::lock_guard lk(m_mutex); - CryptoNote::BinaryOutputStreamSerializer s(os); + StdOutputStream stream(os); + CryptoNote::BinaryOutputStreamSerializer s(stream); s(const_cast(TRANSFERS_CONTAINER_STORAGE_VERSION), "version"); @@ -750,7 +755,8 @@ void TransfersContainer::save(std::ostream& os) { void TransfersContainer::load(std::istream& in) { std::lock_guard lk(m_mutex); - CryptoNote::BinaryInputStreamSerializer s(in); + StdInputStream stream(in); + CryptoNote::BinaryInputStreamSerializer s(stream); uint32_t version = 0; s(version, "version"); @@ -759,7 +765,7 @@ void TransfersContainer::load(std::istream& in) { throw std::runtime_error("Unsupported transfers storage version"); } - uint64_t currentHeight = 0; + uint32_t currentHeight = 0; TransactionMultiIndex transactions; UnconfirmedTransfersMultiIndex unconfirmedTransfers; AvailableTransfersMultiIndex availableTransfers; @@ -793,7 +799,7 @@ bool TransfersContainer::isSpendTimeUnlocked(uint64_t unlockTime) const { bool TransfersContainer::isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const { uint32_t state; - if (info.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT || !isSpendTimeUnlocked(info.unlockTime)) { + if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT || !isSpendTimeUnlocked(info.unlockTime)) { state = IncludeStateLocked; } else if (m_currentHeight < info.blockHeight + m_transactionSpendableAge) { state = IncludeStateSoftLocked; diff --git a/src/transfers/TransfersContainer.h b/src/Transfers/TransfersContainer.h old mode 100644 new mode 100755 similarity index 80% rename from src/transfers/TransfersContainer.h rename to src/Transfers/TransfersContainer.h index 72d4c17a0f..003208dee3 --- a/src/transfers/TransfersContainer.h +++ b/src/Transfers/TransfersContainer.h @@ -28,14 +28,14 @@ #include #include "crypto/crypto.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "cryptonote_core/Currency.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" +#include "CryptoNoteCore/Currency.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" #include "ITransaction.h" #include "ITransfersContainer.h" -#include "SerializationHelpers.h" namespace CryptoNote { @@ -45,11 +45,11 @@ class SpentOutputDescriptor { public: SpentOutputDescriptor(); SpentOutputDescriptor(const TransactionOutputInformationIn& transactionInfo); - SpentOutputDescriptor(const KeyImage* keyImage); - SpentOutputDescriptor(uint64_t amount, uint64_t globalOutputIndex); + SpentOutputDescriptor(const Crypto::KeyImage* keyImage); + SpentOutputDescriptor(uint64_t amount, uint32_t globalOutputIndex); - void assign(const KeyImage* keyImage); - void assign(uint64_t amount, uint64_t globalOutputIndex); + void assign(const Crypto::KeyImage* keyImage); + void assign(uint64_t amount, uint32_t globalOutputIndex); bool isValid() const; @@ -59,10 +59,10 @@ class SpentOutputDescriptor { private: TransactionTypes::OutputType m_type; union { - const KeyImage* m_keyImage; + const Crypto::KeyImage* m_keyImage; struct { uint64_t m_amount; - uint64_t m_globalOutputIndex; + uint32_t m_globalOutputIndex; }; }; }; @@ -74,17 +74,17 @@ struct SpentOutputDescriptorHasher { }; struct TransactionOutputInformationIn : public TransactionOutputInformation { - KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key + Crypto::KeyImage keyImage; //!< \attention Used only for TransactionTypes::OutputType::Key }; struct TransactionOutputInformationEx : public TransactionOutputInformationIn { uint64_t unlockTime; - uint64_t blockHeight; + uint32_t blockHeight; uint32_t transactionIndex; bool visible; SpentOutputDescriptor getSpentOutputDescriptor() const { return SpentOutputDescriptor(*this); } - const Hash& getTransactionHash() const { return transactionHash; } + const Crypto::Hash& getTransactionHash() const { return transactionHash; } void serialize(CryptoNote::ISerializer& s) { s(reinterpret_cast(type), "type"); @@ -108,7 +108,7 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn { }; struct BlockInfo { - uint64_t height; + uint32_t height; uint64_t timestamp; uint32_t transactionIndex; @@ -121,10 +121,10 @@ struct BlockInfo { struct SpentTransactionOutput : TransactionOutputInformationEx { BlockInfo spendingBlock; - Hash spendingTransactionHash; + Crypto::Hash spendingTransactionHash; uint32_t inputInTransaction; - const Hash& getSpendingTransactionHash() const { + const Crypto::Hash& getSpendingTransactionHash() const { return spendingTransactionHash; } @@ -154,20 +154,20 @@ class TransfersContainer : public ITransfersContainer { TransfersContainer(const CryptoNote::Currency& currency, size_t transactionSpendableAge); bool addTransaction(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers); - bool deleteUnconfirmedTransaction(const Hash& transactionHash); - bool markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); + bool deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); + bool markTransactionConfirmed(const BlockInfo& block, const Crypto::Hash& transactionHash, const std::vector& globalIndices); - std::vector detach(uint64_t height); - bool advanceHeight(uint64_t height); + std::vector detach(uint32_t height); + bool advanceHeight(uint32_t height); // ITransfersContainer virtual size_t transfersCount() override; virtual size_t transactionsCount() override; virtual uint64_t balance(uint32_t flags) override; virtual void getOutputs(std::vector& transfers, uint32_t flags) override; - virtual bool getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) override; - virtual std::vector getTransactionOutputs(const Hash& transactionHash, uint32_t flags) override; - virtual void getUnconfirmedTransactions(std::vector& transactions) override; + virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) override; + virtual std::vector getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags) override; + virtual void getUnconfirmedTransactions(std::vector& transactions) override; virtual std::vector getSpentOutputs() override; // IStreamSerializable @@ -182,8 +182,8 @@ class TransfersContainer : public ITransfersContainer { typedef boost::multi_index_container< TransactionInformation, boost::multi_index::indexed_by< - boost::multi_index::hashed_unique, - boost::multi_index::ordered_non_unique + boost::multi_index::hashed_unique, + boost::multi_index::ordered_non_unique < BOOST_MULTI_INDEX_MEMBER(TransactionInformation, uint32_t, blockHeight) > > > TransactionMultiIndex; @@ -202,7 +202,7 @@ class TransfersContainer : public ITransfersContainer { boost::multi_index::tag, boost::multi_index::const_mem_fun< TransactionOutputInformationEx, - const Hash&, + const Crypto::Hash&, &TransactionOutputInformationEx::getTransactionHash> > > @@ -223,7 +223,7 @@ class TransfersContainer : public ITransfersContainer { boost::multi_index::tag, boost::multi_index::const_mem_fun< TransactionOutputInformationEx, - const Hash&, + const Crypto::Hash&, &TransactionOutputInformationEx::getTransactionHash> > > @@ -244,14 +244,14 @@ class TransfersContainer : public ITransfersContainer { boost::multi_index::tag, boost::multi_index::const_mem_fun< TransactionOutputInformationEx, - const Hash&, + const Crypto::Hash&, &SpentTransactionOutput::getTransactionHash> >, boost::multi_index::hashed_non_unique < boost::multi_index::tag, boost::multi_index::const_mem_fun < SpentTransactionOutput, - const Hash&, + const Crypto::Hash&, &SpentTransactionOutput::getSpendingTransactionHash> > > @@ -262,11 +262,11 @@ class TransfersContainer : public ITransfersContainer { bool addTransactionOutputs(const BlockInfo& block, const ITransactionReader& tx, const std::vector& transfers); bool addTransactionInputs(const BlockInfo& block, const ITransactionReader& tx); - void deleteTransactionTransfers(const Hash& transactionHash); + void deleteTransactionTransfers(const Crypto::Hash& transactionHash); bool isSpendTimeUnlocked(uint64_t unlockTime) const; bool isIncluded(const TransactionOutputInformationEx& info, uint32_t flags) const; static bool isIncluded(TransactionTypes::OutputType type, uint32_t state, uint32_t flags); - void updateTransfersVisibility(const KeyImage& keyImage); + void updateTransfersVisibility(const Crypto::KeyImage& keyImage); void copyToSpent(const BlockInfo& block, const ITransactionReader& tx, size_t inputIndex, const TransactionOutputInformationEx& output); @@ -277,7 +277,7 @@ class TransfersContainer : public ITransfersContainer { SpentTransfersMultiIndex m_spentTransfers; //std::unordered_map> m_keyImages; - uint64_t m_currentHeight; // current height is needed to check if a transfer is unlocked + uint32_t m_currentHeight; // current height is needed to check if a transfer is unlocked size_t m_transactionSpendableAge; const CryptoNote::Currency& m_currency; std::mutex m_mutex; diff --git a/src/transfers/TransfersSubscription.cpp b/src/Transfers/TransfersSubscription.cpp old mode 100644 new mode 100755 similarity index 69% rename from src/transfers/TransfersSubscription.cpp rename to src/Transfers/TransfersSubscription.cpp index cb2309ce8a..7f5f4f4429 --- a/src/transfers/TransfersSubscription.cpp +++ b/src/Transfers/TransfersSubscription.cpp @@ -16,65 +16,66 @@ // along with Bytecoin. If not, see . #include "TransfersSubscription.h" -#include "IWallet.h" +#include "IWalletLegacy.h" + +using namespace Crypto; namespace CryptoNote { TransfersSubscription::TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub) - : m_subscription(sub), m_transfers(currency, sub.transactionSpendableAge) {} + : subscription(sub), transfers(currency, sub.transactionSpendableAge) {} SynchronizationStart TransfersSubscription::getSyncStart() { - return m_subscription.syncStart; + return subscription.syncStart; } -void TransfersSubscription::onBlockchainDetach(uint64_t height) { - std::vector deletedTransactions = m_transfers.detach(height); +void TransfersSubscription::onBlockchainDetach(uint32_t height) { + std::vector deletedTransactions = transfers.detach(height); for (auto& hash : deletedTransactions) { m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, hash); } } -void TransfersSubscription::onError(const std::error_code& ec, uint64_t height) { - if (height != UNCONFIRMED_TRANSACTION_HEIGHT) { - m_transfers.detach(height); +void TransfersSubscription::onError(const std::error_code& ec, uint32_t height) { + if (height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { + transfers.detach(height); } m_observerManager.notify(&ITransfersObserver::onError, this, height, ec); } -bool TransfersSubscription::advanceHeight(uint64_t height) { - return m_transfers.advanceHeight(height); +bool TransfersSubscription::advanceHeight(uint32_t height) { + return transfers.advanceHeight(height); } const AccountKeys& TransfersSubscription::getKeys() const { - return m_subscription.keys; + return subscription.keys; } void TransfersSubscription::addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, - const std::vector& transfers) { - - bool added = m_transfers.addTransaction(blockInfo, tx, transfers); + const std::vector& transfersList) { + bool added = transfers.addTransaction(blockInfo, tx, transfersList); if (added) { m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); } } -AccountAddress TransfersSubscription::getAddress() { - return m_subscription.keys.address; +AccountPublicAddress TransfersSubscription::getAddress() { + return subscription.keys.address; } ITransfersContainer& TransfersSubscription::getContainer() { - return m_transfers; + return transfers; } void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) { - m_transfers.deleteUnconfirmedTransaction(transactionHash); + transfers.deleteUnconfirmedTransaction(transactionHash); m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); } void TransfersSubscription::markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, - const std::vector& globalIndices) { - m_transfers.markTransactionConfirmed(block, transactionHash, globalIndices); + const std::vector& globalIndices) { + transfers.markTransactionConfirmed(block, transactionHash, globalIndices); m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, transactionHash); } diff --git a/src/transfers/TransfersSubscription.h b/src/Transfers/TransfersSubscription.h old mode 100644 new mode 100755 similarity index 75% rename from src/transfers/TransfersSubscription.h rename to src/Transfers/TransfersSubscription.h index da3630dcc2..0dda4132ad --- a/src/transfers/TransfersSubscription.h +++ b/src/Transfers/TransfersSubscription.h @@ -28,23 +28,23 @@ class TransfersSubscription : public IObservableImpl < ITransfersObserver, ITran TransfersSubscription(const CryptoNote::Currency& currency, const AccountSubscription& sub); SynchronizationStart getSyncStart(); - void onBlockchainDetach(uint64_t height); - void onError(const std::error_code& ec, uint64_t height); - bool advanceHeight(uint64_t height); + void onBlockchainDetach(uint32_t height); + void onError(const std::error_code& ec, uint32_t height); + bool advanceHeight(uint32_t height); const AccountKeys& getKeys() const; void addTransaction(const BlockInfo& blockInfo, const ITransactionReader& tx, const std::vector& transfers); - void deleteUnconfirmedTransaction(const Hash& transactionHash); - void markTransactionConfirmed(const BlockInfo& block, const Hash& transactionHash, const std::vector& globalIndices); + void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); + void markTransactionConfirmed(const BlockInfo& block, const Crypto::Hash& transactionHash, const std::vector& globalIndices); // ITransfersSubscription - virtual AccountAddress getAddress() override; + virtual AccountPublicAddress getAddress() override; virtual ITransfersContainer& getContainer() override; private: - TransfersContainer m_transfers; - AccountSubscription m_subscription; + TransfersContainer transfers; + AccountSubscription subscription; }; } diff --git a/src/transfers/TransfersSynchronizer.cpp b/src/Transfers/TransfersSynchronizer.cpp old mode 100644 new mode 100755 similarity index 88% rename from src/transfers/TransfersSynchronizer.cpp rename to src/Transfers/TransfersSynchronizer.cpp index 3dafa027fa..961d2051de --- a/src/transfers/TransfersSynchronizer.cpp +++ b/src/Transfers/TransfersSynchronizer.cpp @@ -18,15 +18,15 @@ #include "TransfersSynchronizer.h" #include "TransfersConsumer.h" -#include "serialization/BinaryInputStreamSerializer.h" -#include "serialization/BinaryOutputStreamSerializer.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/BinaryOutputStreamSerializer.h" -namespace CryptoNote { +using namespace Common; +using namespace Crypto; -void serialize(AccountAddress& acc, CryptoNote::ISerializer& s) { - s(acc.spendPublicKey, "spendKey"); - s(acc.viewPublicKey, "viewKey"); -} +namespace CryptoNote { const uint32_t TRANSFERS_STORAGE_ARCHIVE_VERSION = 0; @@ -54,7 +54,7 @@ ITransfersSubscription& TransfersSyncronizer::addSubscription(const AccountSubsc return it->second->addSubscription(acc); } -bool TransfersSyncronizer::removeSubscription(const AccountAddress& acc) { +bool TransfersSyncronizer::removeSubscription(const AccountPublicAddress& acc) { auto it = m_consumers.find(acc.viewPublicKey); if (it == m_consumers.end()) return false; @@ -67,13 +67,13 @@ bool TransfersSyncronizer::removeSubscription(const AccountAddress& acc) { return true; } -void TransfersSyncronizer::getSubscriptions(std::vector& subscriptions) { +void TransfersSyncronizer::getSubscriptions(std::vector& subscriptions) { for (const auto& kv : m_consumers) { kv.second->getSubscriptions(subscriptions); } } -ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountAddress& acc) { +ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountPublicAddress& acc) { auto it = m_consumers.find(acc.viewPublicKey); return (it == m_consumers.end()) ? 0 : it->second->getSubscription(acc); } @@ -81,7 +81,8 @@ ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountAddre void TransfersSyncronizer::save(std::ostream& os) { m_sync.save(os); - CryptoNote::BinaryOutputStreamSerializer s(os); + StdOutputStream stream(os); + CryptoNote::BinaryOutputStreamSerializer s(stream); s(const_cast(TRANSFERS_STORAGE_ARCHIVE_VERSION), "version"); size_t subscriptionCount = m_consumers.size(); @@ -99,7 +100,7 @@ void TransfersSyncronizer::save(std::ostream& os) { std::string blob = consumerState.str(); s(blob, "state"); - std::vector subscriptions; + std::vector subscriptions; consumer.second->getSubscriptions(subscriptions); size_t subCount = subscriptions.size(); @@ -144,7 +145,8 @@ void setObjectState(IStreamSerializable& obj, const std::string& state) { void TransfersSyncronizer::load(std::istream& is) { m_sync.load(is); - CryptoNote::BinaryInputStreamSerializer s(is); + StdInputStream inputStream(is); + CryptoNote::BinaryInputStreamSerializer s(inputStream); uint32_t version = 0; s(version, "version"); @@ -157,7 +159,7 @@ void TransfersSyncronizer::load(std::istream& is) { struct ConsumerState { PublicKey viewKey; std::string state; - std::vector> subscriptionStates; + std::vector> subscriptionStates; }; std::vector updatedStates; @@ -194,7 +196,7 @@ void TransfersSyncronizer::load(std::istream& is) { while (subCount--) { s.beginObject(""); - AccountAddress acc; + AccountPublicAddress acc; std::string state; s(acc, "address"); diff --git a/src/transfers/TransfersSynchronizer.h b/src/Transfers/TransfersSynchronizer.h similarity index 83% rename from src/transfers/TransfersSynchronizer.h rename to src/Transfers/TransfersSynchronizer.h index 27f1ff75ec..c66a8bed39 100644 --- a/src/transfers/TransfersSynchronizer.h +++ b/src/Transfers/TransfersSynchronizer.h @@ -42,9 +42,9 @@ class TransfersSyncronizer : public ITransfersSynchronizer { // ITransfersSynchronizer virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) override; - virtual bool removeSubscription(const AccountAddress& acc) override; - virtual void getSubscriptions(std::vector& subscriptions) override; - virtual ITransfersSubscription* getSubscription(const AccountAddress& acc) override; + virtual bool removeSubscription(const AccountPublicAddress& acc) override; + virtual void getSubscriptions(std::vector& subscriptions) override; + virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) override; // IStreamSerializable virtual void save(std::ostream& os) override; @@ -53,7 +53,7 @@ class TransfersSyncronizer : public ITransfersSynchronizer { private: // map { view public key -> consumer } - std::unordered_map> m_consumers; + std::unordered_map> m_consumers; // std::unordered_map> m_subscriptions; IBlockchainSynchronizer& m_sync; diff --git a/src/transfers/TypeHelpers.h b/src/Transfers/TypeHelpers.h similarity index 71% rename from src/transfers/TypeHelpers.h rename to src/Transfers/TypeHelpers.h index 5b95fd71a9..556354b826 100644 --- a/src/transfers/TypeHelpers.h +++ b/src/Transfers/TypeHelpers.h @@ -23,8 +23,8 @@ namespace CryptoNote { -inline bool operator==(const AccountAddress &_v1, const AccountAddress &_v2) { - return memcmp(&_v1, &_v2, sizeof(AccountAddress)) == 0; +inline bool operator==(const AccountPublicAddress &_v1, const AccountPublicAddress &_v2) { + return memcmp(&_v1, &_v2, sizeof(AccountPublicAddress)) == 0; } } @@ -32,19 +32,12 @@ inline bool operator==(const AccountAddress &_v1, const AccountAddress &_v2) { namespace std { template<> -struct hash < CryptoNote::AccountAddress > { - std::size_t operator()(const CryptoNote::AccountAddress& val) const { +struct hash < CryptoNote::AccountPublicAddress > { + size_t operator()(const CryptoNote::AccountPublicAddress& val) const { size_t spend = *(reinterpret_cast(&val.spendPublicKey)); size_t view = *(reinterpret_cast(&val.viewPublicKey)); return spend ^ view; } }; -template<> -struct hash < CryptoNote::PublicKey > { - std::size_t operator()(const CryptoNote::PublicKey& val) const { - return *reinterpret_cast(&val); - } -}; - } diff --git a/src/wallet/LegacyKeysImporter.cpp b/src/Wallet/LegacyKeysImporter.cpp similarity index 60% rename from src/wallet/LegacyKeysImporter.cpp rename to src/Wallet/LegacyKeysImporter.cpp index 44110f3432..cee143c5ef 100755 --- a/src/wallet/LegacyKeysImporter.cpp +++ b/src/Wallet/LegacyKeysImporter.cpp @@ -1,98 +1,100 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "LegacyKeysImporter.h" - -#include -#include - -#include "Common/StringTools.h" - -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/account.h" - -#include "serialization/binary_utils.h" -#include "serialization/SerializationTools.h" - -#include "wallet/WalletSerializer.h" -#include "wallet/WalletUserTransactionsCache.h" -#include "wallet/WalletErrors.h" - -namespace { - -struct keys_file_data { - crypto::chacha8_iv iv; - std::string account_data; - - BEGIN_SERIALIZE_OBJECT() - FIELD(iv) - FIELD(account_data) - END_SERIALIZE() -}; - -bool verify_keys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { - crypto::public_key pub; - bool r = crypto::secret_key_to_public_key(sec, pub); - return r && expected_pub == pub; -} - -void loadKeysFromFile(const std::string& filename, const std::string& password, CryptoNote::account_base& account) { - keys_file_data keys_file_data; - std::string buf; - - if (!Common::loadFileToString(filename, buf)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to load \"" + filename + '\"'); - } - - if (!::serialization::parse_binary(buf, keys_file_data)) { - throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to deserialize \"" + filename + '\"'); - } - - crypto::chacha8_key key; - crypto::cn_context cn_context; - crypto::generate_chacha8_key(cn_context, password, key); - std::string account_data; - account_data.resize(keys_file_data.account_data.size()); - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); - - const CryptoNote::account_keys& keys = account.get_keys(); - - if (CryptoNote::loadFromBinaryKeyValue(account, account_data) && - verify_keys(keys.m_view_secret_key, keys.m_account_address.m_viewPublicKey) && - verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spendPublicKey)) { - return; - } - - throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); -} - -} - -namespace CryptoNote { - -void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination) { - CryptoNote::account_base account; - - loadKeysFromFile(legacyKeysFilename, password, account); - - CryptoNote::WalletUserTransactionsCache transactionsCache; - std::string cache; - CryptoNote::WalletSerializer importer(account, transactionsCache); - importer.serialize(destination, password, false, cache); -} - -} //namespace CryptoNote +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "LegacyKeysImporter.h" + +#include +#include + +#include "Common/StringTools.h" + +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/CryptoNoteTools.h" + +#include "Serialization/SerializationTools.h" + +#include "WalletLegacy/WalletLegacySerializer.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "Wallet/WalletErrors.h" + +using namespace Crypto; + +namespace { + +struct keys_file_data { + chacha8_iv iv; + std::string account_data; + + void serialize(CryptoNote::ISerializer& s) { + s(iv, "iv"); + s(account_data, "account_data"); + } +}; + +bool verify_keys(const SecretKey& sec, const PublicKey& expected_pub) { + PublicKey pub; + bool r = secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void loadKeysFromFile(const std::string& filename, const std::string& password, CryptoNote::AccountBase& account) { + keys_file_data keys_file_data; + std::string buf; + + if (!Common::loadFileToString(filename, buf)) { + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to load \"" + filename + '\"'); + } + + if (!CryptoNote::fromBinaryArray(keys_file_data, Common::asBinaryArray(buf))) { + throw std::system_error(make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR), "failed to deserialize \"" + filename + '\"'); + } + + chacha8_key key; + cn_context cn_context; + generate_chacha8_key(cn_context, password, key); + std::string account_data; + account_data.resize(keys_file_data.account_data.size()); + chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + + const CryptoNote::AccountKeys& keys = account.getAccountKeys(); + + if (CryptoNote::loadFromBinaryKeyValue(account, account_data) && + verify_keys(keys.viewSecretKey, keys.address.viewPublicKey) && + verify_keys(keys.spendSecretKey, keys.address.spendPublicKey)) { + return; + } + + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); +} + +} + +namespace CryptoNote { + +void importLegacyKeys(const std::string& legacyKeysFilename, const std::string& password, std::ostream& destination) { + CryptoNote::AccountBase account; + + loadKeysFromFile(legacyKeysFilename, password, account); + + CryptoNote::WalletUserTransactionsCache transactionsCache; + std::string cache; + CryptoNote::WalletLegacySerializer importer(account, transactionsCache); + importer.serialize(destination, password, false, cache); +} + +} //namespace CryptoNote diff --git a/src/wallet/LegacyKeysImporter.h b/src/Wallet/LegacyKeysImporter.h similarity index 100% rename from src/wallet/LegacyKeysImporter.h rename to src/Wallet/LegacyKeysImporter.h diff --git a/src/wallet/WalletAsyncContextCounter.cpp b/src/Wallet/WalletAsyncContextCounter.cpp similarity index 100% rename from src/wallet/WalletAsyncContextCounter.cpp rename to src/Wallet/WalletAsyncContextCounter.cpp diff --git a/src/wallet/WalletAsyncContextCounter.h b/src/Wallet/WalletAsyncContextCounter.h similarity index 100% rename from src/wallet/WalletAsyncContextCounter.h rename to src/Wallet/WalletAsyncContextCounter.h diff --git a/src/wallet/WalletErrors.cpp b/src/Wallet/WalletErrors.cpp similarity index 100% rename from src/wallet/WalletErrors.cpp rename to src/Wallet/WalletErrors.cpp diff --git a/src/wallet/WalletErrors.h b/src/Wallet/WalletErrors.h similarity index 96% rename from src/wallet/WalletErrors.h rename to src/Wallet/WalletErrors.h index 445f21053e..346638fa88 100644 --- a/src/wallet/WalletErrors.h +++ b/src/Wallet/WalletErrors.h @@ -39,7 +39,8 @@ enum WalletErrorCodes { TX_CANCEL_IMPOSSIBLE, TX_CANCELLED, OPERATION_CANCELLED, - TX_TRANSFER_IMPOSSIBLE + TX_TRANSFER_IMPOSSIBLE, + WRONG_VERSION }; // custom category: @@ -71,6 +72,7 @@ class WalletErrorCategory : public std::error_category { case WRONG_STATE: return "The wallet is in wrong state (maybe loading or saving), try again later"; case OPERATION_CANCELLED: return "The operation you've requested has been cancelled"; case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible"; + case WRONG_VERSION: return "Wrong version"; default: return "Unknown error"; } } diff --git a/src/Wallet/WalletGreen.cpp b/src/Wallet/WalletGreen.cpp new file mode 100755 index 0000000000..e612544412 --- /dev/null +++ b/src/Wallet/WalletGreen.cpp @@ -0,0 +1,1218 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletGreen.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ITransaction.h" + +#include "Common/ShuffleGenerator.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Common/StringTools.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/Currency.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "CryptoNoteCore/TransactionApi.h" +#include "crypto/crypto.h" +#include "Transfers/TransfersContainer.h" +#include "WalletSerialization.h" +#include "WalletErrors.h" + +using namespace Common; +using namespace Crypto; +using namespace CryptoNote; + +namespace { + +const uint32_t WALLET_SOFTLOCK_BLOCKS_COUNT = 1; + +const uint64_t DUST_THRESHOLD = 10000; + +void asyncRequestCompletion(System::Event& requestFinished) { + requestFinished.set(); +} + +void parseAddressString(const std::string& string, const CryptoNote::Currency& currency, CryptoNote::AccountPublicAddress& address) { + if (!currency.parseAccountAddressString(string, address)) { + throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + } +} + +bool validateAddress(const std::string& address, const CryptoNote::Currency& currency) { + CryptoNote::AccountPublicAddress ignore; + return currency.parseAccountAddressString(address, ignore); +} + +void validateAddresses(const std::vector& destinations, const CryptoNote::Currency& currency) { + for (const auto& destination: destinations) { + if (!validateAddress(destination.address, currency)) { + throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + } + } +} + +uint64_t countNeededMoney(const std::vector& destinations, uint64_t fee) { + uint64_t neededMoney = 0; + for (const auto& transfer: destinations) { + if (transfer.amount == 0) { + throw std::system_error(make_error_code(CryptoNote::error::ZERO_DESTINATION)); + } else if (transfer.amount < 0) { + throw std::system_error(make_error_code(std::errc::invalid_argument)); + } + + //to supress warning + uint64_t uamount = static_cast(transfer.amount); + neededMoney += uamount; + if (neededMoney < uamount) { + throw std::system_error(make_error_code(CryptoNote::error::SUM_OVERFLOW)); + } + } + + neededMoney += fee; + if (neededMoney < fee) { + throw std::system_error(make_error_code(CryptoNote::error::SUM_OVERFLOW)); + } + + return neededMoney; +} + +void checkIfEnoughMixins(std::vector& mixinResult, uint64_t mixIn) { + auto notEnoughIt = std::find_if(mixinResult.begin(), mixinResult.end(), + [mixIn] (const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& ofa) { return ofa.outs.size() < mixIn; } ); + + if (mixIn == 0 && mixinResult.empty()) { + throw std::system_error(make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG)); + } + + if (notEnoughIt != mixinResult.end()) { + throw std::system_error(make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG)); + } +} + +CryptoNote::WalletEvent makeTransactionUpdatedEvent(size_t id) { + CryptoNote::WalletEvent event; + event.type = CryptoNote::WalletEventType::TRANSACTION_UPDATED; + event.transactionUpdated.transactionIndex = id; + + return event; +} + +CryptoNote::WalletEvent makeTransactionCreatedEvent(size_t id) { + CryptoNote::WalletEvent event; + event.type = CryptoNote::WalletEventType::TRANSACTION_CREATED; + event.transactionCreated.transactionIndex = id; + + return event; +} + +CryptoNote::WalletEvent makeMoneyUnlockedEvent() { + CryptoNote::WalletEvent event; + event.type = CryptoNote::WalletEventType::BALANCE_UNLOCKED; + + return event; +} + +} + +namespace CryptoNote { + +WalletGreen::WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node) : + m_dispatcher(dispatcher), + m_currency(currency), + m_node(node), + m_blockchainSynchronizer(node, currency.genesisBlockHash()), + m_synchronizer(currency, m_blockchainSynchronizer, node), + m_eventOccured(m_dispatcher), + m_readyEvent(m_dispatcher) +{ + m_upperTransactionSizeLimit = m_currency.blockGrantedFullRewardZone() * 125 / 100 - m_currency.minerTxBlobReservedSize(); + m_readyEvent.set(); +} + +WalletGreen::~WalletGreen() { + if (m_state == WalletState::INITIALIZED) { + doShutdown(); + } + + m_dispatcher.yield(); //let remote spawns finish +} + +void WalletGreen::initialize(const std::string& password) { + if (m_state != WalletState::NOT_INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::ALREADY_INITIALIZED)); + } + + throwIfStopped(); + + Crypto::generate_keys(m_viewPublicKey, m_viewSecretKey); + m_password = password; + + m_blockchainSynchronizer.addObserver(this); + + m_state = WalletState::INITIALIZED; +} + +void WalletGreen::shutdown() { + throwIfNotInitialized(); + doShutdown(); + + m_dispatcher.yield(); //let remote spawns finish +} + +void WalletGreen::doShutdown() { + m_blockchainSynchronizer.stop(); + m_blockchainSynchronizer.removeObserver(this); + + clearCaches(); + + std::queue noEvents; + std::swap(m_events, noEvents); + + m_state = WalletState::NOT_INITIALIZED; +} + +void WalletGreen::clearCaches() { + std::vector subscriptions; + m_synchronizer.getSubscriptions(subscriptions); + std::for_each(subscriptions.begin(), subscriptions.end(), [this] (const AccountPublicAddress& address) { m_synchronizer.removeSubscription(address); }); + + m_walletsContainer.clear(); + m_spentOutputs.clear(); + m_unlockTransactionsJob.clear(); + m_transactions.clear(); + m_transfers.clear(); + m_change.clear(); + m_actualBalance = 0; + m_pendingBalance = 0; +} + +void WalletGreen::save(std::ostream& destination, bool saveDetails, bool saveCache) { + throwIfNotInitialized(); + throwIfStopped(); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.stop(); + } + + unsafeSave(destination, saveDetails, saveCache); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.start(); + } +} + +void WalletGreen::unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache) { + WalletSerializer s( + *this, + m_viewPublicKey, + m_viewSecretKey, + m_actualBalance, + m_pendingBalance, + m_walletsContainer, + m_synchronizer, + m_spentOutputs, + m_unlockTransactionsJob, + m_change, + m_transactions, + m_transfers + ); + + StdOutputStream output(destination); + s.save(m_password, output, saveDetails, saveCache); +} + +void WalletGreen::load(std::istream& source, const std::string& password) { + if (m_state != WalletState::NOT_INITIALIZED) { + throw std::system_error(make_error_code(error::WRONG_STATE)); + } + + throwIfStopped(); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.stop(); + } + + unsafeLoad(source, password); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.start(); + } + + m_state = WalletState::INITIALIZED; +} + +void WalletGreen::unsafeLoad(std::istream& source, const std::string& password) { + WalletSerializer s( + *this, + m_viewPublicKey, + m_viewSecretKey, + m_actualBalance, + m_pendingBalance, + m_walletsContainer, + m_synchronizer, + m_spentOutputs, + m_unlockTransactionsJob, + m_change, + m_transactions, + m_transfers + ); + + StdInputStream inputStream(source); + s.load(password, inputStream); + + m_password = password; + m_blockchainSynchronizer.addObserver(this); +} + +void WalletGreen::changePassword(const std::string& oldPassword, const std::string& newPassword) { + throwIfNotInitialized(); + throwIfStopped(); + + if (m_password.compare(oldPassword)) { + throw std::system_error(make_error_code(error::WRONG_PASSWORD)); + } + + m_password = newPassword; +} + +size_t WalletGreen::getAddressCount() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_walletsContainer.get().size(); +} + +std::string WalletGreen::getAddress(size_t index) const { + throwIfNotInitialized(); + throwIfStopped(); + + if (index >= m_walletsContainer.get().size()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + const WalletRecord& wallet = m_walletsContainer.get()[index]; + return m_currency.accountAddressAsString({ wallet.spendPublicKey, m_viewPublicKey }); +} + +std::string WalletGreen::createAddress() { + KeyPair spendKey; + + Crypto::generate_keys(spendKey.publicKey, spendKey.secretKey); + return createAddress(spendKey); +} + +std::string WalletGreen::createAddress(const KeyPair& spendKey) { + throwIfNotInitialized(); + throwIfStopped(); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.stop(); + } + + addWallet(spendKey); + std::string address = m_currency.accountAddressAsString({ spendKey.publicKey, m_viewPublicKey }); + + m_blockchainSynchronizer.start(); + + return address; +} + +void WalletGreen::addWallet(const KeyPair& spendKey) { + time_t creationTimestamp = time(nullptr); + + AccountSubscription sub; + sub.keys.address.viewPublicKey = m_viewPublicKey; + sub.keys.address.spendPublicKey = spendKey.publicKey; + sub.keys.viewSecretKey = m_viewSecretKey; + sub.keys.spendSecretKey = spendKey.secretKey; + sub.transactionSpendableAge = 10; + sub.syncStart.height = 0; + sub.syncStart.timestamp = static_cast(creationTimestamp) - (60 * 60 * 24); + + auto& trSubscription = m_synchronizer.addSubscription(sub); + ITransfersContainer* container = &trSubscription.getContainer(); + + WalletRecord wallet; + wallet.spendPublicKey = spendKey.publicKey; + wallet.spendSecretKey = spendKey.secretKey; + wallet.container = container; + wallet.creationTimestamp = creationTimestamp; + trSubscription.addObserver(this); + + m_walletsContainer.get().push_back(std::move(wallet)); +} + +void WalletGreen::deleteAddress(const std::string& address) { + throwIfNotInitialized(); + throwIfStopped(); + + CryptoNote::AccountPublicAddress pubAddr = parseAddress(address); + + auto it = m_walletsContainer.get().find(pubAddr.spendPublicKey); + if (it == m_walletsContainer.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + m_blockchainSynchronizer.stop(); + + m_actualBalance -= it->actualBalance; + m_pendingBalance -= it->pendingBalance; + + m_synchronizer.removeSubscription(pubAddr); + + m_spentOutputs.get().erase(&(*it)); + m_walletsContainer.get().erase(it); + + if (m_walletsContainer.get().size() != 0) { + m_blockchainSynchronizer.start(); + } +} + +uint64_t WalletGreen::getActualBalance() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_actualBalance; +} + +uint64_t WalletGreen::getActualBalance(const std::string& address) const { + throwIfNotInitialized(); + throwIfStopped(); + + const auto& wallet = getWalletRecord(address); + return wallet.actualBalance; +} + +uint64_t WalletGreen::getPendingBalance() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_pendingBalance; +} + +uint64_t WalletGreen::getPendingBalance(const std::string& address) const { + throwIfNotInitialized(); + throwIfStopped(); + + const auto& wallet = getWalletRecord(address); + return wallet.pendingBalance; +} + +size_t WalletGreen::getTransactionCount() const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_transactions.get().size(); +} + +WalletTransaction WalletGreen::getTransaction(size_t transactionIndex) const { + throwIfNotInitialized(); + throwIfStopped(); + + return m_transactions.get().at(transactionIndex); +} + +size_t WalletGreen::getTransactionTransferCount(size_t transactionIndex) const { + throwIfNotInitialized(); + throwIfStopped(); + + auto bounds = getTransactionTransfers(transactionIndex); + return static_cast(std::distance(bounds.first, bounds.second)); +} + +WalletTransfer WalletGreen::getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const { + throwIfNotInitialized(); + throwIfStopped(); + + auto bounds = getTransactionTransfers(transactionIndex); + + if (transferIndex >= static_cast(std::distance(bounds.first, bounds.second))) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + auto it = bounds.first; + std::advance(it, transferIndex); + return it->second; +} + +std::pair WalletGreen::getTransactionTransfers( + size_t transactionIndex) const { + + auto val = std::make_pair(transactionIndex, WalletTransfer()); + + auto bounds = std::equal_range(m_transfers.begin(), m_transfers.end(), val, [] (const TransactionTransferPair& a, const TransactionTransferPair& b) { + return a.first < b.first; + }); + + return bounds; +} + +size_t WalletGreen::transfer(const WalletTransfer& destination, + uint64_t fee, + uint64_t mixIn, + std::string const& extra, + uint64_t unlockTimestamp) +{ + std::vector destinations { destination }; + return transfer(destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::transfer( + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + + System::EventLock lk(m_readyEvent); + + throwIfNotInitialized(); + throwIfStopped(); + + return doTransfer(pickWalletsWithMoney(), destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::transfer( + const std::string& sourceAddress, + const WalletTransfer& destination, + uint64_t fee, + uint64_t mixIn, + std::string const& extra, + uint64_t unlockTimestamp) { + std::vector destinations { destination }; + return transfer(sourceAddress, destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::transfer( + const std::string& sourceAddress, + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + System::EventLock lk(m_readyEvent); + + throwIfNotInitialized(); + throwIfStopped(); + + WalletOuts wallet = pickWallet(sourceAddress); + std::vector wallets; + + if (!wallet.outs.empty()) { + wallets.push_back(wallet); + } + + return doTransfer(std::move(wallets), destinations, fee, mixIn, extra, unlockTimestamp); +} + +size_t WalletGreen::doTransfer(std::vector&& wallets, + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp) { + if (destinations.empty()) { + throw std::system_error(make_error_code(error::ZERO_DESTINATION)); + } + + validateAddresses(destinations, m_currency); + + uint64_t neededMoney = countNeededMoney(destinations, fee); + + std::vector selectedTransfers; + uint64_t foundMoney = selectTransfers(neededMoney, mixIn == 0, DUST_THRESHOLD, std::move(wallets), selectedTransfers); + + if (foundMoney < neededMoney) { + throw std::system_error(make_error_code(error::WRONG_AMOUNT), "Not enough money"); + } + + typedef CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; + std::vector mixinResult; + + if (mixIn != 0) { + requestMixinOuts(selectedTransfers, mixIn, mixinResult); + } + + std::vector keysInfo; + prepareInputs(selectedTransfers, mixinResult, mixIn, keysInfo); + + WalletTransfer changeDestination; + changeDestination.address = m_currency.accountAddressAsString({ m_walletsContainer.get()[0].spendPublicKey, m_viewPublicKey }); + changeDestination.amount = foundMoney - neededMoney; + + std::vector decomposedOutputs; + splitDestinations(destinations, changeDestination, DUST_THRESHOLD, m_currency, decomposedOutputs); + + std::unique_ptr tx = makeTransaction(decomposedOutputs, keysInfo, extra, unlockTimestamp); + + size_t txId = insertOutgoingTransaction(tx->getTransactionHash(), -static_cast(neededMoney), fee, tx->getExtra(), unlockTimestamp); + pushBackOutgoingTransfers(txId, destinations); + + try { + sendTransaction(tx.get()); + } catch (std::exception&) { + pushEvent(makeTransactionCreatedEvent(txId)); + throw; + } + + auto txIt = m_transactions.get().begin(); + std::advance(txIt, txId); + m_transactions.get().modify(txIt, + [] (WalletTransaction& tx) { tx.state = WalletTransactionState::SUCCEEDED; }); + + markOutputsSpent(tx->getTransactionHash(), selectedTransfers); + m_change[tx->getTransactionHash()] = changeDestination.amount; + updateUsedWalletsBalances(selectedTransfers); + + pushEvent(makeTransactionCreatedEvent(txId)); + + return txId; +} + +void WalletGreen::pushBackOutgoingTransfers(size_t txId, const std::vector &destinations) { + for (const auto& dest: destinations) { + WalletTransfer d { dest.address, -dest.amount }; + m_transfers.push_back(std::make_pair(txId, d)); + } +} + +size_t WalletGreen::insertOutgoingTransaction(const Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp) { + WalletTransaction insertTx; + insertTx.state = WalletTransactionState::FAILED; + insertTx.creationTime = static_cast(time(nullptr)); + insertTx.unlockTime = unlockTimestamp; + insertTx.blockHeight = CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT; + insertTx.extra.assign(reinterpret_cast(extra.data()), extra.size()); + insertTx.fee = fee; + insertTx.hash = transactionHash; + insertTx.totalAmount = totalAmount; + insertTx.timestamp = 0; //0 until included in a block + + size_t txId = m_transactions.get().size(); + m_transactions.get().push_back(std::move(insertTx)); + + return txId; +} + +bool WalletGreen::transactionExists(const Hash& hash) { + auto& hashIndex = m_transactions.get(); + auto it = hashIndex.find(hash); + return it != hashIndex.end(); +} + +void WalletGreen::updateTransactionHeight(const Hash& hash, uint32_t blockHeight) { + auto& hashIndex = m_transactions.get(); + + auto it = hashIndex.find(hash); + if (it != hashIndex.end()) { + bool r = hashIndex.modify(it, [&blockHeight] (WalletTransaction& transaction) { + transaction.blockHeight = blockHeight; + //transaction may be deleted first than added again + transaction.state = WalletTransactionState::SUCCEEDED; + }); + assert(r); + return; + } + + throw std::system_error(make_error_code(std::errc::invalid_argument)); +} + +size_t WalletGreen::insertIncomingTransaction(const TransactionInformation& info, int64_t txBalance) { + auto& index = m_transactions.get(); + + WalletTransaction tx; + tx.state = WalletTransactionState::SUCCEEDED; + tx.timestamp = info.timestamp; + tx.blockHeight = info.blockHeight; + tx.hash = info.transactionHash; + tx.fee = info.totalAmountIn - info.totalAmountOut; + tx.unlockTime = info.unlockTime; + tx.extra.assign(reinterpret_cast(info.extra.data()), info.extra.size()); + tx.totalAmount = txBalance; + tx.creationTime = info.timestamp; + + index.push_back(std::move(tx)); + return index.size() - 1; +} + +void WalletGreen::insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount) { + auto it = std::upper_bound(m_transfers.begin(), m_transfers.end(), txId, [] (size_t val, const TransactionTransferPair& a) { + return val < a.first; + }); + + WalletTransfer tr { address, amount }; + m_transfers.insert(it, std::make_pair(txId, std::move(tr))); +} + +std::unique_ptr WalletGreen::makeTransaction(const std::vector& decomposedOutputs, + std::vector& keysInfo, const std::string& extra, uint64_t unlockTimestamp) { + + std::unique_ptr tx = createTransaction(); + + for (const auto& output: decomposedOutputs) { + for (auto amount: output.amounts) { + tx->addOutput(amount, output.receiver); + } + } + + tx->setUnlockTime(unlockTimestamp); + tx->appendExtra(Common::asBinaryArray(extra)); + + for (auto& input: keysInfo) { + tx->addInput(makeAccountKeys(*input.walletRecord), input.keyInfo, input.ephKeys); + } + + size_t i = 0; + for(auto& input: keysInfo) { + tx->signInputKey(i++, input.keyInfo, input.ephKeys); + } + + return tx; +} + +void WalletGreen::sendTransaction(ITransaction* tx) { + System::Event completion(m_dispatcher); + std::error_code ec; + CryptoNote::Transaction oldTxFormat; + + const auto& ba = tx->getTransactionData(); + + if (ba.size() > m_upperTransactionSizeLimit) { + throw std::system_error(make_error_code(error::TRANSACTION_SIZE_TOO_BIG)); + } + + if (!fromBinaryArray(oldTxFormat, ba)) { + throw std::system_error(make_error_code(error::INTERNAL_WALLET_ERROR)); + } + + throwIfStopped(); + m_node.relayTransaction(oldTxFormat, [&ec, &completion, this] (std::error_code error) { + ec = error; + this->m_dispatcher.remoteSpawn(std::bind(asyncRequestCompletion, std::ref(completion))); + }); + completion.wait(); + + if (ec) { + throw std::system_error(ec); + } +} + +AccountKeys WalletGreen::makeAccountKeys(const WalletRecord& wallet) const { + AccountKeys keys; + keys.address.spendPublicKey = wallet.spendPublicKey; + keys.address.viewPublicKey = m_viewPublicKey; + keys.spendSecretKey = wallet.spendSecretKey; + keys.viewSecretKey = m_viewSecretKey; + + return keys; +} + +void WalletGreen::requestMixinOuts( + const std::vector& selectedTransfers, + uint64_t mixIn, + std::vector& mixinResult) { + + std::vector amounts; + for (const auto& out: selectedTransfers) { + amounts.push_back(out.out.amount); + } + + System::Event requestFinished(m_dispatcher); + std::error_code mixinError; + + throwIfStopped(); + + m_node.getRandomOutsByAmounts(std::move(amounts), mixIn, mixinResult, [&requestFinished, &mixinError, this] (std::error_code ec) { + mixinError = ec; + this->m_dispatcher.remoteSpawn(std::bind(asyncRequestCompletion, std::ref(requestFinished))); + }); + + requestFinished.wait(); + + checkIfEnoughMixins(mixinResult, mixIn); + + if (mixinError) { + throw std::system_error(mixinError); + } +} + +uint64_t WalletGreen::selectTransfers( + uint64_t neededMoney, + bool dust, + uint64_t dustThreshold, + std::vector&& wallets, + std::vector& selectedTransfers) { + + uint64_t foundMoney = 0; + + std::vector walletOuts = wallets; + std::default_random_engine randomGenerator(Crypto::rand()); + + while (foundMoney < neededMoney && !walletOuts.empty()) { + std::uniform_int_distribution walletsDistribution(0, walletOuts.size() - 1); + + size_t walletIndex = walletsDistribution(randomGenerator); + std::vector& addressOuts = walletOuts[walletIndex].outs; + + assert(addressOuts.size() > 0); + std::uniform_int_distribution outDistribution(0, addressOuts.size() - 1); + size_t outIndex = outDistribution(randomGenerator); + + TransactionOutputInformation out = addressOuts[outIndex]; + if (!isOutputUsed(out) && (out.amount > dustThreshold || dust)) { + if (out.amount <= dustThreshold) { + dust = false; + } + + foundMoney += out.amount; + + selectedTransfers.push_back( { std::move(out), walletOuts[walletIndex].wallet } ); + } + + addressOuts.erase(addressOuts.begin() + outIndex); + if (addressOuts.empty()) { + walletOuts.erase(walletOuts.begin() + walletIndex); + } + } + + if (!dust) { + return foundMoney; + } + + for (const auto& addressOuts : walletOuts) { + auto it = std::find_if(addressOuts.outs.begin(), addressOuts.outs.end(), + [dustThreshold, this] (const TransactionOutputInformation& out) { + return out.amount <= dustThreshold && (!this->isOutputUsed(out)); + } + ); + + if (it != addressOuts.outs.end()) { + foundMoney += it->amount; + selectedTransfers.push_back({ *it, addressOuts.wallet }); + break; + } + } + + return foundMoney; +}; + +std::vector WalletGreen::pickWalletsWithMoney() { + auto& walletsIndex = m_walletsContainer.get(); + + std::vector walletOuts; + for (const auto& wallet: walletsIndex) { + if (wallet.actualBalance == 0) { + continue; + } + + ITransfersContainer* container = wallet.container; + + WalletOuts outs; + container->getOutputs(outs.outs, ITransfersContainer::IncludeKeyUnlocked); + outs.wallet = const_cast(&wallet); + + walletOuts.push_back(std::move(outs)); + }; + + return walletOuts; +} + +WalletGreen::WalletOuts WalletGreen::pickWallet(const std::string& address) { + const auto& wallet = getWalletRecord(address); + + ITransfersContainer* container = wallet.container; + WalletOuts outs; + container->getOutputs(outs.outs, ITransfersContainer::IncludeKeyUnlocked); + outs.wallet = const_cast(&wallet); + + return outs; +} + +void WalletGreen::splitDestinations(const std::vector& destinations, + const CryptoNote::WalletTransfer& changeDestination, + uint64_t dustThreshold, + const CryptoNote::Currency& currency, + std::vector& decomposedOutputs) { + + for (const auto& destination: destinations) { + ReceiverAmounts receiverAmounts; + + parseAddressString(destination.address, currency, receiverAmounts.receiver); + decomposeAmount(destination.amount, dustThreshold, receiverAmounts.amounts); + + decomposedOutputs.push_back(std::move(receiverAmounts)); + } + + ReceiverAmounts changeAmounts; + parseAddressString(changeDestination.address, currency, changeAmounts.receiver); + decomposeAmount(changeDestination.amount, dustThreshold, changeAmounts.amounts); + + decomposedOutputs.push_back(std::move(changeAmounts)); +} + +void WalletGreen::prepareInputs( + const std::vector& selectedTransfers, + std::vector& mixinResult, + uint64_t mixIn, + std::vector& keysInfo) { + + typedef CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + + size_t i = 0; + for (const auto& input: selectedTransfers) { + TransactionTypes::InputKeyInfo keyInfo; + keyInfo.amount = input.out.amount; + + if(mixinResult.size()) { + std::sort(mixinResult[i].outs.begin(), mixinResult[i].outs.end(), + [] (const out_entry& a, const out_entry& b) { return a.global_amount_index < b.global_amount_index; }); + for (auto& fakeOut: mixinResult[i].outs) { + + if (input.out.globalOutputIndex == fakeOut.global_amount_index) { + continue; + } + + TransactionTypes::GlobalOutput globalOutput; + globalOutput.outputIndex = static_cast(fakeOut.global_amount_index); + globalOutput.targetKey = reinterpret_cast(fakeOut.out_key); + keyInfo.outputs.push_back(std::move(globalOutput)); + if(keyInfo.outputs.size() >= mixIn) + break; + } + } + + //paste real transaction to the random index + auto insertIn = std::find_if(keyInfo.outputs.begin(), keyInfo.outputs.end(), [&](const TransactionTypes::GlobalOutput& a) { + return a.outputIndex >= input.out.globalOutputIndex; + }); + + TransactionTypes::GlobalOutput realOutput; + realOutput.outputIndex = input.out.globalOutputIndex; + realOutput.targetKey = reinterpret_cast(input.out.outputKey); + + auto insertedIn = keyInfo.outputs.insert(insertIn, realOutput); + + keyInfo.realOutput.transactionPublicKey = reinterpret_cast(input.out.transactionPublicKey); + keyInfo.realOutput.transactionIndex = static_cast(insertedIn - keyInfo.outputs.begin()); + keyInfo.realOutput.outputInTransaction = input.out.outputInTransaction; + + InputInfo inputInfo; + inputInfo.keyInfo = std::move(keyInfo); + inputInfo.walletRecord = input.wallet; + keysInfo.push_back(std::move(inputInfo)); + ++i; + } +} + +void WalletGreen::start() { + m_stopped = false; +} + +void WalletGreen::stop() { + m_stopped = true; + m_eventOccured.set(); +} + +WalletEvent WalletGreen::getEvent() { + throwIfNotInitialized(); + throwIfStopped(); + + while(m_events.empty()) { + m_eventOccured.wait(); + m_eventOccured.clear(); + throwIfStopped(); + } + + WalletEvent event = std::move(m_events.front()); + m_events.pop(); + + return event; +} + +void WalletGreen::throwIfNotInitialized() const { + if (m_state != WalletState::INITIALIZED) { + throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); + } +} + +void WalletGreen::onError(ITransfersSubscription* object, uint32_t height, std::error_code ec) { +} + +void WalletGreen::synchronizationProgressUpdated(uint32_t current, uint32_t total) { + m_dispatcher.remoteSpawn( [current, this] () { this->onSynchronizationProgressUpdated(current); } ); +} + +void WalletGreen::onSynchronizationProgressUpdated(uint32_t current) { + System::EventLock lk(m_readyEvent); + + if (m_state == WalletState::NOT_INITIALIZED) { + return; + } + + unlockBalances(current); +} + +void WalletGreen::unlockBalances(uint32_t height) { + auto& index = m_unlockTransactionsJob.get(); + auto upper = index.upper_bound(height); + + for (auto it = index.begin(); it != upper; ++it) { + updateBalance(it->container); + } + + index.erase(index.begin(), upper); + pushEvent(makeMoneyUnlockedEvent()); +} + +void WalletGreen::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + m_dispatcher.remoteSpawn([object, transactionHash, this] () { this->transactionUpdated(object, transactionHash); } ); +} + +void WalletGreen::transactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + System::EventLock lk(m_readyEvent); + + if (m_state == WalletState::NOT_INITIALIZED) { + return; + } + + CryptoNote::ITransfersContainer* container = &object->getContainer(); + + deleteSpentOutputs(transactionHash); + + CryptoNote::TransactionInformation info; + int64_t txBalance; + bool found = container->getTransactionInformation(transactionHash, info, txBalance); + assert(found); + + WalletEvent event; + + if (transactionExists(info.transactionHash)) { + updateTransactionHeight(info.transactionHash, info.blockHeight); + + auto id = getTransactionId(info.transactionHash); + event = makeTransactionUpdatedEvent(id); + } else { + auto id = insertIncomingTransaction(info, txBalance); + insertIncomingTransfer(id, m_currency.accountAddressAsString({ getWalletRecord(container).spendPublicKey, m_viewPublicKey }), txBalance); + + event = makeTransactionCreatedEvent(id); + } + + if (info.blockHeight != CryptoNote::WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) { + //TODO: make proper calculation of unlock height + uint32_t height = info.blockHeight + static_cast(info.unlockTime) + WALLET_SOFTLOCK_BLOCKS_COUNT + 1; + m_change.erase(transactionHash); + insertUnlockTransactionJob(transactionHash, height, container); + } + + updateBalance(container); + pushEvent(event); +} + +void WalletGreen::pushEvent(const WalletEvent& event) { + m_events.push(event); + m_eventOccured.set(); +} + +size_t WalletGreen::getTransactionId(const Hash& transactionHash) const { + auto it = m_transactions.get().find(transactionHash); + + if (it == m_transactions.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + auto rndIt = m_transactions.project(it); + auto txId = std::distance(m_transactions.get().begin(), rndIt); + + return txId; +} + +void WalletGreen::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + m_dispatcher.remoteSpawn([object, transactionHash, this] () { + this->transactionDeleted(object, transactionHash); }); +} + +void WalletGreen::transactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + System::EventLock lk(m_readyEvent); + + if (m_state == WalletState::NOT_INITIALIZED) { + return; + } + + auto it = m_transactions.get().find(transactionHash); + if (it == m_transactions.get().end()) { + return; + } + + CryptoNote::ITransfersContainer* container = &object->getContainer(); + deleteUnlockTransactionJob(transactionHash); + m_change.erase(transactionHash); + deleteSpentOutputs(transactionHash); + + m_transactions.get().modify(it, [] (CryptoNote::WalletTransaction& tx) { + tx.state = WalletTransactionState::CANCELLED; + tx.blockHeight = WALLET_UNCONFIRMED_TRANSACTION_HEIGHT; + }); + + auto rndIt = m_transactions.project(it); + auto id = std::distance(m_transactions.get().begin(), rndIt); + + updateBalance(container); + pushEvent(makeTransactionUpdatedEvent(id)); +} + +void WalletGreen::insertUnlockTransactionJob(const Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container) { + auto& index = m_unlockTransactionsJob.get(); + index.insert( { blockHeight, container, transactionHash } ); +} + +void WalletGreen::deleteUnlockTransactionJob(const Hash& transactionHash) { + auto& index = m_unlockTransactionsJob.get(); + index.erase(transactionHash); +} + +void WalletGreen::updateBalance(CryptoNote::ITransfersContainer* container) { + auto it = m_walletsContainer.get().find(container); + + if (it == m_walletsContainer.get().end()) { + return; + } + + uint64_t actual = container->balance(ITransfersContainer::IncludeAllUnlocked); + uint64_t pending = container->balance(ITransfersContainer::IncludeAllLocked); + + uint64_t unconfirmedBalance = countSpentBalance(&(*it)); + + actual -= unconfirmedBalance; + + //xxx: i don't like this special case. Decompose this function + if (container == m_walletsContainer.get()[0].container) { + uint64_t change = 0; + std::for_each(m_change.begin(), m_change.end(), [&change] (const TransactionChanges::value_type& item) { change += item.second; }); + pending += change; + } + + if (it->actualBalance < actual) { + m_actualBalance += actual - it->actualBalance; + } else { + m_actualBalance -= it->actualBalance - actual; + } + + if (it->pendingBalance < pending) { + m_pendingBalance += pending - it->pendingBalance; + } else { + m_pendingBalance -= it->pendingBalance - pending; + } + + m_walletsContainer.get().modify(it, [actual, pending] (WalletRecord& wallet) { + wallet.actualBalance = actual; + wallet.pendingBalance = pending; + }); +} + +const WalletRecord& WalletGreen::getWalletRecord(const PublicKey& key) const { + auto it = m_walletsContainer.get().find(key); + if (it == m_walletsContainer.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + return *it; +} + +const WalletRecord& WalletGreen::getWalletRecord(const std::string& address) const { + CryptoNote::AccountPublicAddress pubAddr = parseAddress(address); + return getWalletRecord(pubAddr.spendPublicKey); +} + +const WalletRecord& WalletGreen::getWalletRecord(CryptoNote::ITransfersContainer* container) const { + auto it = m_walletsContainer.get().find(container); + if (it == m_walletsContainer.get().end()) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + return *it; +} + +CryptoNote::AccountPublicAddress WalletGreen::parseAddress(const std::string& address) const { + CryptoNote::AccountPublicAddress pubAddr; + + if (!m_currency.parseAccountAddressString(address, pubAddr)) { + throw std::system_error(std::make_error_code(std::errc::invalid_argument)); + } + + return pubAddr; +} + +bool WalletGreen::isOutputUsed(const TransactionOutputInformation& out) const { + return m_spentOutputs.get().find(boost::make_tuple(out.transactionHash, out.outputInTransaction)) + != + m_spentOutputs.get().end(); +} + +void WalletGreen::markOutputsSpent(const Hash& transactionHash,const std::vector& selectedTransfers) { + auto& index = m_spentOutputs.get(); + + for (const auto& output: selectedTransfers) { + index.insert( {output.out.amount, output.out.transactionHash, output.out.outputInTransaction, output.wallet, transactionHash} ); + } +} + +void WalletGreen::deleteSpentOutputs(const Hash& transactionHash) { + auto& index = m_spentOutputs.get(); + index.erase(transactionHash); +} + +uint64_t WalletGreen::countSpentBalance(const WalletRecord* wallet) { + uint64_t amount = 0; + + auto bounds = m_spentOutputs.get().equal_range(wallet); + for (auto it = bounds.first; it != bounds.second; ++it) { + amount += it->amount; + } + + return amount; +} + +void WalletGreen::updateUsedWalletsBalances(const std::vector& selectedTransfers) { + std::set wallets; + + // wallet #0 recieves change, so we have to update it after transfer + wallets.insert(const_cast(&m_walletsContainer.get()[0])); + + std::for_each(selectedTransfers.begin(), selectedTransfers.end(), [&wallets] (const OutputToTransfer& output) { wallets.insert(output.wallet); } ); + std::for_each(wallets.begin(), wallets.end(), [this] (WalletRecord* wallet) { + this->updateBalance(wallet->container); + }); +} + +void WalletGreen::throwIfStopped() const { + if (m_stopped) { + throw std::system_error(make_error_code(error::OPERATION_CANCELLED)); + } +} + +} //namespace CryptoNote diff --git a/src/Wallet/WalletGreen.h b/src/Wallet/WalletGreen.h new file mode 100755 index 0000000000..00031d9a43 --- /dev/null +++ b/src/Wallet/WalletGreen.h @@ -0,0 +1,211 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" + +#include + +#include "WalletIndices.h" + +#include +#include +#include "Transfers/TransfersSynchronizer.h" +#include "Transfers/BlockchainSynchronizer.h" + +namespace CryptoNote { + +class WalletGreen : public IWallet, + ITransfersObserver, + IBlockchainSynchronizerObserver { +public: + WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node); + virtual ~WalletGreen(); + + virtual void initialize(const std::string& password) override; + virtual void load(std::istream& source, const std::string& password) override; + virtual void shutdown() override; + + virtual void changePassword(const std::string& oldPassword, const std::string& newPassword) override; + virtual void save(std::ostream& destination, bool saveDetails = true, bool saveCache = true) override; + + virtual size_t getAddressCount() const override; + virtual std::string getAddress(size_t index) const override; + virtual std::string createAddress() override; + virtual std::string createAddress(const KeyPair& spendKey) override; + virtual void deleteAddress(const std::string& address) override; + + virtual uint64_t getActualBalance() const override; + virtual uint64_t getActualBalance(const std::string& address) const override; + virtual uint64_t getPendingBalance() const override; + virtual uint64_t getPendingBalance(const std::string& address) const override; + + virtual size_t getTransactionCount() const override; + virtual WalletTransaction getTransaction(size_t transactionIndex) const override; + virtual size_t getTransactionTransferCount(size_t transactionIndex) const override; + virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const override; + + virtual size_t transfer(const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::string& sourceAddress, const WalletTransfer& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + virtual size_t transfer(const std::string& sourceAddress, const std::vector& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; + + virtual void start() override; + virtual void stop() override; + virtual WalletEvent getEvent() override; + +protected: + void throwIfNotInitialized() const; + void throwIfStopped() const; + void doShutdown(); + void clearCaches(); + + struct InputInfo { + TransactionTypes::InputKeyInfo keyInfo; + WalletRecord* walletRecord = nullptr; + KeyPair ephKeys; + }; + + struct OutputToTransfer { + TransactionOutputInformation out; + WalletRecord* wallet; + }; + + struct ReceiverAmounts { + CryptoNote::AccountPublicAddress receiver; + std::vector amounts; + }; + + struct WalletOuts { + WalletRecord* wallet; + std::vector outs; + }; + + virtual void onError(ITransfersSubscription* object, uint32_t height, std::error_code ec) override; + virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; + virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override; + + void transactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash); + void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash); + void onSynchronizationProgressUpdated(uint32_t current); + + std::vector pickWalletsWithMoney(); + WalletOuts pickWallet(const std::string& address); + + void updateBalance(CryptoNote::ITransfersContainer* container); + void unlockBalances(uint32_t height); + + const WalletRecord& getWalletRecord(const Crypto::PublicKey& key) const; + const WalletRecord& getWalletRecord(const std::string& address) const; + const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const; + + CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const; + void addWallet(const KeyPair& spendKey); + bool isOutputUsed(const TransactionOutputInformation& out) const; + void markOutputsSpent(const Crypto::Hash& transactionHash, const std::vector& selectedTransfers); + void deleteSpentOutputs(const Crypto::Hash& transactionHash); + uint64_t countSpentBalance(const WalletRecord* wallet); + void updateUsedWalletsBalances(const std::vector& selectedTransfers); + AccountKeys makeAccountKeys(const WalletRecord& wallet) const; + size_t getTransactionId(const Crypto::Hash& transactionHash) const; + void pushEvent(const WalletEvent& event); + + size_t doTransfer(std::vector&& wallets, + const std::vector& destinations, + uint64_t fee, + uint64_t mixIn, + const std::string& extra, + uint64_t unlockTimestamp); + + void requestMixinOuts(const std::vector& selectedTransfers, + uint64_t mixIn, + std::vector& mixinResult); + + void prepareInputs(const std::vector& selectedTransfers, + std::vector& mixinResult, + uint64_t mixIn, + std::vector& keysInfo); + + uint64_t selectTransfers(uint64_t needeMoney, + bool dust, + uint64_t dustThreshold, + std::vector&& wallets, + std::vector& selectedTransfers); + + void splitDestinations(const std::vector& destinations, const WalletTransfer& changeDestination, + uint64_t dustThreshold, const Currency& currency, std::vector& decomposedOutputs); + + std::unique_ptr makeTransaction(const std::vector& decomposedOutputs, + std::vector& keysInfo, const std::string& extra, uint64_t unlockTimestamp); + + void sendTransaction(ITransaction* tx); + + size_t insertOutgoingTransaction(const Crypto::Hash& transactionHash, int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp); + bool transactionExists(const Crypto::Hash& hash); + void updateTransactionHeight(const Crypto::Hash& hash, uint32_t blockHeight); + size_t insertIncomingTransaction(const TransactionInformation& info, int64_t txBalance); + void insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount); + void pushBackOutgoingTransfers(size_t txId, const std::vector &destinations); + void insertUnlockTransactionJob(const Crypto::Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container); + void deleteUnlockTransactionJob(const Crypto::Hash& transactionHash); + + void unsafeLoad(std::istream& source, const std::string& password); + void unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache); + + + enum class WalletState { + INITIALIZED, + NOT_INITIALIZED + }; + + std::pair getTransactionTransfers(size_t transactionIndex) const; + + System::Dispatcher& m_dispatcher; + const Currency& m_currency; + INode& m_node; + bool m_stopped = false; + + WalletsContainer m_walletsContainer; + SpentOutputs m_spentOutputs; + UnlockTransactionJobs m_unlockTransactionsJob; + WalletTransactions m_transactions; + WalletTransfers m_transfers; //sorted + TransactionChanges m_change; + + BlockchainSynchronizer m_blockchainSynchronizer; + TransfersSyncronizer m_synchronizer; + + System::Event m_eventOccured; + std::queue m_events; + mutable System::Event m_readyEvent; + + WalletState m_state = WalletState::NOT_INITIALIZED; + + std::string m_password; + + Crypto::PublicKey m_viewPublicKey; + Crypto::SecretKey m_viewSecretKey; + + uint64_t m_actualBalance = 0; + uint64_t m_pendingBalance = 0; + + uint64_t m_upperTransactionSizeLimit; +}; + +} //namespace CryptoNote diff --git a/src/Wallet/WalletIndices.h b/src/Wallet/WalletIndices.h new file mode 100644 index 0000000000..4b7fac4f51 --- /dev/null +++ b/src/Wallet/WalletIndices.h @@ -0,0 +1,124 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include + +#include "ITransfersContainer.h" +#include "IWallet.h" +#include "IWalletLegacy.h" //TODO: make common types for all of our APIs (such as PublicKey, KeyPair, etc) + +#include +#include +#include +#include +#include +#include + +struct WalletRecord { + Crypto::PublicKey spendPublicKey; + Crypto::SecretKey spendSecretKey; + CryptoNote::ITransfersContainer* container = nullptr; + uint64_t pendingBalance = 0; + uint64_t actualBalance = 0; + time_t creationTimestamp; +}; + +struct SpentOutput { + uint64_t amount; + Crypto::Hash transactionHash; + uint32_t outputInTransaction; + const WalletRecord* wallet; + Crypto::Hash spendingTransactionHash; +}; + +struct RandomAccessIndex {}; +struct KeysIndex {}; +struct TransfersContainerIndex {}; + +struct WalletIndex {}; +struct TransactionOutputIndex {}; +struct BlockHeightIndex {}; + +struct TransactionHashIndex {}; +struct TransactionIndex {}; + +typedef boost::multi_index_container < + WalletRecord, + boost::multi_index::indexed_by < + boost::multi_index::random_access < boost::multi_index::tag >, + boost::multi_index::hashed_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(WalletRecord, Crypto::PublicKey, spendPublicKey)>, + boost::multi_index::hashed_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(WalletRecord, CryptoNote::ITransfersContainer*, container) > + > +> WalletsContainer; + +struct OutputIndex: boost::multi_index::composite_key < + SpentOutput, + BOOST_MULTI_INDEX_MEMBER(SpentOutput, Crypto::Hash, transactionHash), + BOOST_MULTI_INDEX_MEMBER(SpentOutput, uint32_t, outputInTransaction) +> {}; + +typedef boost::multi_index_container < + SpentOutput, + boost::multi_index::indexed_by < + boost::multi_index::hashed_unique < boost::multi_index::tag , + OutputIndex + >, + boost::multi_index::hashed_non_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(SpentOutput, Crypto::Hash, spendingTransactionHash) + >, + boost::multi_index::hashed_non_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(SpentOutput, const WalletRecord *, wallet) + > + > +> SpentOutputs; + +struct UnlockTransactionJob { + uint32_t blockHeight; + CryptoNote::ITransfersContainer* container; + Crypto::Hash transactionHash; +}; + +typedef boost::multi_index_container < + UnlockTransactionJob, + boost::multi_index::indexed_by < + boost::multi_index::ordered_non_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, uint32_t, blockHeight) + >, + boost::multi_index::hashed_unique < boost::multi_index::tag , + BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, Crypto::Hash, transactionHash) + > + > +> UnlockTransactionJobs; + +typedef boost::multi_index_container < + CryptoNote::WalletTransaction, + boost::multi_index::indexed_by < + boost::multi_index::random_access < boost::multi_index::tag >, + boost::multi_index::hashed_unique < boost::multi_index::tag , + boost::multi_index::member + > + > +> WalletTransactions; + +typedef std::unordered_map TransactionChanges; + +typedef std::pair TransactionTransferPair; +typedef std::vector WalletTransfers; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/Wallet/WalletRpcServer.cpp similarity index 87% rename from src/wallet/wallet_rpc_server.cpp rename to src/Wallet/WalletRpcServer.cpp index bf03f2a3e8..4d76d463f6 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/Wallet/WalletRpcServer.cpp @@ -15,24 +15,24 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "wallet_rpc_server.h" +#include "WalletRpcServer.h" #include -#include "Common/command_line.h" +#include "Common/CommandLine.h" #include "Common/StringTools.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/account.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/Account.h" #include "crypto/hash.h" -#include "WalletHelper.h" +#include "WalletLegacy/WalletHelper.h" // #include "wallet_errors.h" -#include "rpc/JsonRpc.h" +#include "Rpc/JsonRpc.h" using namespace Logging; using namespace CryptoNote; -namespace tools { +namespace Tools { const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", 0, true }; const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = { "rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1" }; @@ -45,7 +45,7 @@ void wallet_rpc_server::init_options(boost::program_options::options_description wallet_rpc_server::wallet_rpc_server( System::Dispatcher& dispatcher, Logging::ILogger& log, - CryptoNote::IWallet&w, + CryptoNote::IWalletLegacy&w, CryptoNote::INode& n, CryptoNote::Currency& currency, const std::string& walletFile) @@ -55,7 +55,6 @@ wallet_rpc_server::wallet_rpc_server( m_dispatcher(dispatcher), m_stopComplete(dispatcher), m_wallet(w), - m_syncWallet(w), m_node(n), m_currency(currency), m_walletFilename(walletFile) { @@ -130,15 +129,15 @@ void wallet_rpc_server::processRequest(const CryptoNote::HttpRequest& request, C //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res) { - res.locked_amount = m_wallet.pendingBalance(); - res.available_balance = m_wallet.actualBalance(); + res.locked_amount = m_wallet.pendingBalance(); + res.available_balance = m_wallet.actualBalance(); return true; } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res) { - std::vector transfers; + std::vector transfers; for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) { - CryptoNote::Transfer transfer; + CryptoNote::WalletLegacyTransfer transfer; transfer.address = it->address; transfer.amount = it->amount; transfers.push_back(transfer); @@ -148,15 +147,15 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ if (!req.payment_id.empty()) { std::string payment_id_str = req.payment_id; - crypto::hash payment_id; + Crypto::Hash payment_id; if (!CryptoNote::parsePaymentId(payment_id_str, payment_id)) { throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment id has invalid format: \"" + payment_id_str + "\", expected 64-character string"); } - std::string extra_nonce; - CryptoNote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); - if (!CryptoNote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) { + BinaryArray extra_nonce; + CryptoNote::setPaymentIdToTransactionExtraNonce(extra_nonce, payment_id); + if (!CryptoNote::addExtraNonceToTransactionExtra(extra, extra_nonce)) { throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string"); } @@ -169,7 +168,7 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ WalletHelper::IWalletRemoveObserverGuard removeGuard(m_wallet, sent); CryptoNote::TransactionId tx = m_wallet.sendTransaction(transfers, req.fee, extraString, req.mixin, req.unlock_time); - if (tx == INVALID_TRANSACTION_ID) { + if (tx == WALLET_LEGACY_INVALID_TRANSACTION_ID) { throw std::runtime_error("Couldn't send transaction"); } @@ -180,7 +179,7 @@ bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::requ throw std::system_error(sendError); } - CryptoNote::TransactionInfo txInfo; + CryptoNote::WalletLegacyTransaction txInfo; m_wallet.getTransaction(tx, txInfo); res.tx_hash = Common::podToHex(txInfo.hash); @@ -201,10 +200,10 @@ bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& r } //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res) { - crypto::hash expectedPaymentId; - CryptoNote::blobdata payment_id_blob; + Crypto::Hash expectedPaymentId; + CryptoNote::BinaryArray payment_id_blob; - if (!hexToBlob(req.payment_id, payment_id_blob)) { + if (!Common::fromHex(req.payment_id, payment_id_blob)) { throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment ID has invald format"); } @@ -212,12 +211,12 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN throw JsonRpc::JsonRpcError(WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID, "Payment ID has invalid size"); } - expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); + expectedPaymentId = *reinterpret_cast(payment_id_blob.data()); size_t transactionsCount = m_wallet.getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet.getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (txInfo.state != WalletLegacyTransactionState::Active || txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } @@ -226,7 +225,7 @@ bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMEN extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; + Crypto::Hash paymentId; if (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId == expectedPaymentId) { wallet_rpc::payment_details rpc_payment; rpc_payment.tx_hash = Common::podToHex(txInfo.hash); @@ -244,16 +243,16 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS res.transfers.clear(); size_t transactionsCount = m_wallet.getTransactionCount(); for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { - TransactionInfo txInfo; + WalletLegacyTransaction txInfo; m_wallet.getTransaction(trantransactionNumber, txInfo); - if (txInfo.state != TransactionState::Active || txInfo.blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (txInfo.state != WalletLegacyTransactionState::Active || txInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { continue; } std::string address = ""; if (txInfo.totalAmount < 0) { if (txInfo.transferCount > 0) { - Transfer tr; + WalletLegacyTransfer tr; m_wallet.getTransfer(txInfo.firstTransferId, tr); address = tr.address; } @@ -274,8 +273,8 @@ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANS extraVec.reserve(txInfo.extra.size()); std::for_each(txInfo.extra.begin(), txInfo.extra.end(), [&extraVec](const char el) { extraVec.push_back(el); }); - crypto::hash paymentId; - transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != null_hash ? Common::podToHex(paymentId) : ""); + Crypto::Hash paymentId; + transfer.paymentId = (getPaymentIdFromTxExtra(extraVec, paymentId) && paymentId != NULL_HASH ? Common::podToHex(paymentId) : ""); res.transfers.push_back(transfer); } diff --git a/src/wallet/wallet_rpc_server.h b/src/Wallet/WalletRpcServer.h old mode 100644 new mode 100755 similarity index 92% rename from src/wallet/wallet_rpc_server.h rename to src/Wallet/WalletRpcServer.h index 26b80f236f..c76b3a0c51 --- a/src/wallet/wallet_rpc_server.h +++ b/src/Wallet/WalletRpcServer.h @@ -20,15 +20,14 @@ #include #include #include -#include "wallet_rpc_server_commans_defs.h" -#include "Wallet.h" -#include "SyncWallet.h" -#include "Common/command_line.h" -#include "rpc/HttpServer.h" +#include "WalletRpcServerCommandsDefinitions.h" +#include "WalletLegacy/WalletLegacy.h" +#include "Common/CommandLine.h" +#include "Rpc/HttpServer.h" #include -namespace tools +namespace Tools { /************************************************************************/ /* */ @@ -40,7 +39,7 @@ namespace tools wallet_rpc_server( System::Dispatcher& dispatcher, Logging::ILogger& log, - CryptoNote::IWallet &w, + CryptoNote::IWalletLegacy &w, CryptoNote::INode &n, CryptoNote::Currency& currency, const std::string& walletFilename); @@ -71,8 +70,7 @@ namespace tools bool handle_command_line(const boost::program_options::variables_map& vm); Logging::LoggerRef logger; - CryptoNote::IWallet& m_wallet; - CryptoNote::SyncWallet m_syncWallet; + CryptoNote::IWalletLegacy& m_wallet; CryptoNote::INode& m_node; uint16_t m_port; std::string m_bind_ip; diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/Wallet/WalletRpcServerCommandsDefinitions.h old mode 100644 new mode 100755 similarity index 95% rename from src/wallet/wallet_rpc_server_commans_defs.h rename to src/Wallet/WalletRpcServerCommandsDefinitions.h index 8ef7be5acc..b61f127b0b --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ b/src/Wallet/WalletRpcServerCommandsDefinitions.h @@ -16,12 +16,12 @@ // along with Bytecoin. If not, see . #pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteProtocol/CryptoNoteProtocolDefinitions.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" #include "crypto/hash.h" -#include "wallet_rpc_server_error_codes.h" +#include "WalletRpcServerErrorCodes.h" -namespace tools +namespace Tools { namespace wallet_rpc { diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/Wallet/WalletRpcServerErrorCodes.h old mode 100644 new mode 100755 similarity index 100% rename from src/wallet/wallet_rpc_server_error_codes.h rename to src/Wallet/WalletRpcServerErrorCodes.h diff --git a/src/Wallet/WalletSerialization.cpp b/src/Wallet/WalletSerialization.cpp new file mode 100755 index 0000000000..d68adff17a --- /dev/null +++ b/src/Wallet/WalletSerialization.cpp @@ -0,0 +1,873 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletSerialization.h" + +#include +#include +#include + +#include "Common/MemoryInputStream.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" + +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "Serialization/SerializationOverloads.h" + +#include "Wallet/WalletErrors.h" +#include "WalletLegacy/KeysStorage.h" +#include "WalletLegacy/WalletLegacySerialization.h" + +using namespace Common; +using namespace Crypto; + +namespace { + +//DO NOT CHANGE IT +struct WalletRecordDto { + PublicKey spendPublicKey; + SecretKey spendSecretKey; + uint64_t pendingBalance = 0; + uint64_t actualBalance = 0; + uint64_t creationTimestamp = 0; +}; + +//DO NOT CHANGE IT +struct SpentOutputDto { + uint64_t amount; + Hash transactionHash; + uint32_t outputInTransaction; + uint64_t walletIndex; + Crypto::Hash spendingTransactionHash; +}; + +//DO NOT CHANGE IT +struct ChangeDto { + Hash txHash; + uint64_t amount; +}; + +//DO NOT CHANGE IT +struct UnlockTransactionJobDto { + uint32_t blockHeight; + Hash transactionHash; + uint64_t walletIndex; +}; + +//DO NOT CHANGE IT +struct WalletTransactionDto { + WalletTransactionDto() {} + + WalletTransactionDto(const CryptoNote::WalletTransaction& wallet) { + state = wallet.state; + timestamp = wallet.timestamp; + blockHeight = wallet.blockHeight; + hash = wallet.hash; + totalAmount = wallet.totalAmount; + fee = wallet.fee; + creationTime = wallet.creationTime; + unlockTime = wallet.unlockTime; + extra = wallet.extra; + } + + CryptoNote::WalletTransactionState state; + uint64_t timestamp; + uint32_t blockHeight; + Hash hash; + int64_t totalAmount; + uint64_t fee; + uint64_t creationTime; + uint64_t unlockTime; + std::string extra; +}; + +//DO NOT CHANGE IT +struct WalletTransferDto { + WalletTransferDto() {} + WalletTransferDto(const CryptoNote::WalletTransfer& tr) { + address = tr.address; + amount = tr.amount; + } + + std::string address; + uint64_t amount; +}; + +void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.spendPublicKey, "spend_public_key"); + serializer(value.spendSecretKey, "spend_secret_key"); + serializer(value.pendingBalance, "pending_balance"); + serializer(value.actualBalance, "actual_balance"); + serializer(value.creationTimestamp, "creation_timestamp"); +} + +void serialize(SpentOutputDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.amount, "amount"); + serializer(value.transactionHash, "transaction_hash"); + serializer(value.outputInTransaction, "output_in_transaction"); + serializer(value.walletIndex, "wallet_index"); + serializer(value.spendingTransactionHash, "spending_transaction_hash"); +} + +void serialize(ChangeDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.txHash, "transaction_hash"); + serializer(value.amount, "amount"); +} + +void serialize(UnlockTransactionJobDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.blockHeight, "block_height"); + serializer(value.transactionHash, "transaction_hash"); + serializer(value.walletIndex, "wallet_index"); +} + +void serialize(WalletTransactionDto& value, CryptoNote::ISerializer& serializer) { + typedef std::underlying_type::type StateType; + + StateType state = static_cast(value.state); + serializer(state, "state"); + value.state = static_cast(state); + + serializer(value.timestamp, "timestamp"); + serializer(value.blockHeight, "block_height"); + serializer(value.hash, "hash"); + serializer(value.totalAmount, "total_amount"); + serializer(value.fee, "fee"); + serializer(value.creationTime, "creation_time"); + serializer(value.unlockTime, "unlock_time"); + serializer(value.extra, "extra"); +} + +void serialize(WalletTransferDto& value, CryptoNote::ISerializer& serializer) { + serializer(value.address, "address"); + serializer(value.amount, "amount"); +} + + +template +std::string serialize(Object& obj, const std::string& name) { + std::stringstream stream; + StdOutputStream output(stream); + CryptoNote::BinaryOutputStreamSerializer s(output); + + s(obj, Common::StringView(name)); + + stream.flush(); + return stream.str(); +} + +std::string encrypt(const std::string& plain, CryptoNote::CryptoContext& cryptoContext) { + std::string cipher; + cipher.resize(plain.size()); + + Crypto::chacha8(plain.data(), plain.size(), cryptoContext.key, cryptoContext.iv, &cipher[0]); + + return cipher; +} + +void addToStream(const std::string& cipher, const std::string& name, Common::IOutputStream& destination) { + CryptoNote::BinaryOutputStreamSerializer s(destination); + s(const_cast(cipher), name); +} + +template +void serializeEncrypted(Object& obj, const std::string& name, CryptoNote::CryptoContext& cryptoContext, Common::IOutputStream& destination) { + std::string plain = serialize(obj, name); + std::string cipher = encrypt(plain, cryptoContext); + + addToStream(cipher, name, destination); +} + +std::string readCipher(Common::IInputStream& source, const std::string& name) { + std::string cipher; + CryptoNote::BinaryInputStreamSerializer s(source); + s(cipher, name); + + return cipher; +} + +std::string decrypt(const std::string& cipher, CryptoNote::CryptoContext& cryptoContext) { + std::string plain; + plain.resize(cipher.size()); + + Crypto::chacha8(cipher.data(), cipher.size(), cryptoContext.key, cryptoContext.iv, &plain[0]); + return plain; +} + +template +void deserialize(Object& obj, const std::string& name, const std::string& plain) { + MemoryInputStream stream(plain.data(), plain.size()); + CryptoNote::BinaryInputStreamSerializer s(stream); + s(obj, Common::StringView(name)); +} + +template +void deserializeEncrypted(Object& obj, const std::string& name, CryptoNote::CryptoContext& cryptoContext, Common::IInputStream& source) { + std::string cipher = readCipher(source, name); + std::string plain = decrypt(cipher, cryptoContext); + + deserialize(obj, name, plain); +} + +bool verifyKeys(const SecretKey& sec, const PublicKey& expected_pub) { + PublicKey pub; + bool r = Crypto::secret_key_to_public_key(sec, pub); + + return r && expected_pub == pub; +} + +void throwIfKeysMissmatch(const SecretKey& sec, const PublicKey& expected_pub) { + if (!verifyKeys(sec, expected_pub)) + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); +} + +CryptoNote::WalletTransaction convert(const CryptoNote::WalletLegacyTransaction& tx) { + CryptoNote::WalletTransaction mtx; + + mtx.state = CryptoNote::WalletTransactionState::SUCCEEDED; + mtx.timestamp = tx.timestamp; + mtx.blockHeight = tx.blockHeight; + mtx.hash = tx.hash; + mtx.totalAmount = tx.totalAmount; + mtx.fee = tx.fee; + mtx.creationTime = tx.sentTime; + mtx.unlockTime = tx.unlockTime; + mtx.extra = tx.extra; + + return mtx; +} + +CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) { + CryptoNote::WalletTransfer mtr; + + mtr.address = tr.address; + mtr.amount = tr.amount; + + return mtr; +} + +} + +namespace CryptoNote { + +const uint32_t WalletSerializer::SERIALIZATION_VERSION = 2; + +void CryptoContext::incIv() { + uint64_t * i = reinterpret_cast(&iv.data[0]); + (*i)++; +} + +WalletSerializer::WalletSerializer( + ITransfersObserver& transfersObserver, + PublicKey& viewPublicKey, + SecretKey& viewSecretKey, + uint64_t& actualBalance, + uint64_t& pendingBalance, + WalletsContainer& walletsContainer, + TransfersSyncronizer& synchronizer, + SpentOutputs& spentOutputs, + UnlockTransactionJobs& unlockTransactions, + TransactionChanges& change, + WalletTransactions& transactions, + WalletTransfers& transfers +) : + m_transfersObserver(transfersObserver), + m_viewPublicKey(viewPublicKey), + m_viewSecretKey(viewSecretKey), + m_actualBalance(actualBalance), + m_pendingBalance(pendingBalance), + m_walletsContainer(walletsContainer), + m_synchronizer(synchronizer), + m_spentOutputs(spentOutputs), + m_unlockTransactions(unlockTransactions), + m_change(change), + m_transactions(transactions), + m_transfers(transfers) +{ } + +void WalletSerializer::save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache) { + CryptoContext cryptoContext = generateCryptoContext(password); + + CryptoNote::BinaryOutputStreamSerializer s(destination); + s.beginObject("wallet"); + + saveVersion(destination); + saveIv(destination, cryptoContext.iv); + + saveKeys(destination, cryptoContext); + saveWallets(destination, saveCache, cryptoContext); + saveFlags(saveDetails, saveCache, destination, cryptoContext); + + if (saveDetails) { + saveTransactions(destination, cryptoContext); + saveTransfers(destination, cryptoContext); + } + + if (saveCache) { + saveBalances(destination, saveCache, cryptoContext); + saveTransfersSynchronizer(destination, cryptoContext); + saveSpentOutputs(destination, cryptoContext); + saveUnlockTransactionsJobs(destination, cryptoContext); + saveChange(destination, cryptoContext); + } + + s.endObject(); +} + +CryptoContext WalletSerializer::generateCryptoContext(const std::string& password) { + CryptoContext context; + + Crypto::cn_context c; + Crypto::generate_chacha8_key(c, password, context.key); + + context.iv = Crypto::rand(); + + return context; +} + +void WalletSerializer::saveVersion(Common::IOutputStream& destination) { + uint32_t version = SERIALIZATION_VERSION; + + BinaryOutputStreamSerializer s(destination); + s(version, "version"); +} + +void WalletSerializer::saveIv(Common::IOutputStream& destination, Crypto::chacha8_iv& iv) { + BinaryOutputStreamSerializer s(destination); + s.binary(reinterpret_cast(&iv.data), sizeof(iv.data), "chacha_iv"); +} + +void WalletSerializer::saveKeys(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + savePublicKey(destination, cryptoContext); + saveSecretKey(destination, cryptoContext); +} + +void WalletSerializer::savePublicKey(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + serializeEncrypted(m_viewPublicKey, "public_key", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveSecretKey(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + serializeEncrypted(m_viewSecretKey, "secret_key", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveFlags(bool saveDetails, bool saveCache, Common::IOutputStream& destination, CryptoContext& cryptoContext) { + serializeEncrypted(saveDetails, "details", cryptoContext, destination); + cryptoContext.incIv(); + + serializeEncrypted(saveCache, "cache", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext) { + auto& index = m_walletsContainer.get(); + + uint64_t count = index.size(); + serializeEncrypted(count, "wallets_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& w: index) { + WalletRecordDto dto; + dto.spendPublicKey = w.spendPublicKey; + dto.spendSecretKey = w.spendSecretKey; + dto.pendingBalance = saveCache ? w.pendingBalance : 0; + dto.actualBalance = saveCache ? w.actualBalance : 0; + dto.creationTimestamp = static_cast(w.creationTimestamp); + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext) { + uint64_t actual = saveCache ? m_actualBalance : 0; + uint64_t pending = saveCache ? m_pendingBalance : 0; + + serializeEncrypted(actual, "actual_balance", cryptoContext, destination); + cryptoContext.incIv(); + + serializeEncrypted(pending, "pending_balance", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + std::stringstream stream; + m_synchronizer.save(stream); + stream.flush(); + + std::string plain = stream.str(); + serializeEncrypted(plain, "transfers_synchronizer", cryptoContext, destination); + cryptoContext.incIv(); +} + +void WalletSerializer::saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + auto& index = m_spentOutputs.get(); + + uint64_t outsCount = index.size(); + serializeEncrypted(outsCount, "spent_outputs_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& o: index) { + auto it = m_walletsContainer.get().iterator_to(*o.wallet); + uint64_t walletIndex = std::distance(m_walletsContainer.get().begin(), it); + + SpentOutputDto dto; + dto.amount = o.amount; + dto.transactionHash = o.transactionHash; + dto.outputInTransaction = o.outputInTransaction; + dto.walletIndex = walletIndex; + dto.spendingTransactionHash = o.spendingTransactionHash; + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + auto& index = m_unlockTransactions.get(); + auto& wallets = m_walletsContainer.get(); + + uint64_t jobsCount = index.size(); + serializeEncrypted(jobsCount, "unlock_transactions_jobs_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& j: index) { + auto containerIt = wallets.find(j.container); + assert(containerIt != wallets.end()); + + auto rndIt = m_walletsContainer.project(containerIt); + assert(rndIt != m_walletsContainer.get().end()); + + uint64_t walletIndex = std::distance(m_walletsContainer.get().begin(), rndIt); + + UnlockTransactionJobDto dto; + dto.blockHeight = j.blockHeight; + dto.transactionHash = j.transactionHash; + dto.walletIndex = walletIndex; + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + uint64_t count = m_change.size(); + serializeEncrypted(count, "changes_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& kv: m_change) { + ChangeDto dto; + dto.txHash = kv.first; + dto.amount = kv.second; + + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + uint64_t count = m_transactions.size(); + serializeEncrypted(count, "transactions_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& tx: m_transactions) { + WalletTransactionDto dto(tx); + serializeEncrypted(dto, "", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext) { + uint64_t count = m_transfers.size(); + serializeEncrypted(count, "transfers_count", cryptoContext, destination); + cryptoContext.incIv(); + + for (const auto& kv: m_transfers) { + uint64_t txId = kv.first; + WalletTransferDto tr(kv.second); + + serializeEncrypted(txId, "transaction_id", cryptoContext, destination); + cryptoContext.incIv(); + + serializeEncrypted(tr, "transfer", cryptoContext, destination); + cryptoContext.incIv(); + } +} + +void WalletSerializer::load(const std::string& password, Common::IInputStream& source) { + CryptoNote::BinaryInputStreamSerializer s(source); + s.beginObject("wallet"); + + uint32_t version = loadVersion(source); + + if (version == SERIALIZATION_VERSION) { + loadCurrentVersion(source, password); + } else if (version == 1) { + loadWalletV1(source, password); + } else { + throw std::system_error(make_error_code(error::WRONG_VERSION)); + } + + s.endObject(); +} + +void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const std::string& password) { + CryptoNote::CryptoContext cryptoContext; + + bool details = false; + bool cache = false; + + loadIv(source, cryptoContext.iv); + generateKey(password, cryptoContext.key); + + loadKeys(source, cryptoContext); + checkKeys(); + + loadWallets(source, cryptoContext); + subscribeWallets(); + + loadFlags(details, cache, source, cryptoContext); + + if (details) { + loadTransactions(source, cryptoContext); + loadTransfers(source, cryptoContext); + } + + if (cache) { + loadBalances(source, cryptoContext); + loadTransfersSynchronizer(source, cryptoContext); + loadSpentOutputs(source, cryptoContext); + loadUnlockTransactionsJobs(source, cryptoContext); + loadChange(source, cryptoContext); + } +} + +void WalletSerializer::loadWalletV1(Common::IInputStream& source, const std::string& password) { + CryptoNote::CryptoContext cryptoContext; + + CryptoNote::BinaryInputStreamSerializer encrypted(source); + + encrypted(cryptoContext.iv, "iv"); + generateKey(password, cryptoContext.key); + + std::string cipher; + encrypted(cipher, "data"); + + std::string plain = decrypt(cipher, cryptoContext); + + MemoryInputStream decryptedStream(plain.data(), plain.size()); + CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); + + loadWalletV1Keys(serializer); + checkKeys(); + + subscribeWallets(); + + bool detailsSaved; + serializer(detailsSaved, "has_details"); + + if (detailsSaved) { + loadWalletV1Details(serializer); + } +} + +void WalletSerializer::loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer) { + CryptoNote::KeysStorage keys; + keys.serialize(serializer, "keys"); + + m_viewPublicKey = keys.viewPublicKey; + m_viewSecretKey = keys.viewSecretKey; + + WalletRecord wallet; + wallet.spendPublicKey = keys.spendPublicKey; + wallet.spendSecretKey = keys.spendSecretKey; + wallet.actualBalance = 0; + wallet.pendingBalance = 0; + wallet.creationTimestamp = static_cast(keys.creationTimestamp); + + m_walletsContainer.get().push_back(wallet); +} + +void WalletSerializer::loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer) { + std::vector txs; + std::vector trs; + + serializer(txs, "transactions"); + serializer(trs, "transfers"); + + addWalletV1Details(txs, trs); +} + +uint32_t WalletSerializer::loadVersion(Common::IInputStream& source) { + CryptoNote::BinaryInputStreamSerializer s(source); + + uint32_t version = std::numeric_limits::max(); + s(version, "version"); + + return version; +} + +void WalletSerializer::loadIv(Common::IInputStream& source, Crypto::chacha8_iv& iv) { + CryptoNote::BinaryInputStreamSerializer s(source); + + s.binary(static_cast(&iv.data), sizeof(iv.data), "chacha_iv"); +} + +void WalletSerializer::generateKey(const std::string& password, Crypto::chacha8_key& key) { + Crypto::cn_context context; + Crypto::generate_chacha8_key(context, password, key); +} + +void WalletSerializer::loadKeys(Common::IInputStream& source, CryptoContext& cryptoContext) { + loadPublicKey(source, cryptoContext); + loadSecretKey(source, cryptoContext); +} + +void WalletSerializer::loadPublicKey(Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(m_viewPublicKey, "public_key", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::loadSecretKey(Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(m_viewSecretKey, "secret_key", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::checkKeys() { + throwIfKeysMissmatch(m_viewSecretKey, m_viewPublicKey); +} + +void WalletSerializer::loadFlags(bool& details, bool& cache, Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(details, "details", cryptoContext, source); + cryptoContext.incIv(); + + deserializeEncrypted(cache, "cache", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::loadWallets(Common::IInputStream& source, CryptoContext& cryptoContext) { + auto& index = m_walletsContainer.get(); + + uint64_t count = 0; + deserializeEncrypted(count, "wallets_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < count; ++i) { + WalletRecordDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + WalletRecord wallet; + wallet.spendPublicKey = dto.spendPublicKey; + wallet.spendSecretKey = dto.spendSecretKey; + wallet.actualBalance = dto.actualBalance; + wallet.pendingBalance = dto.pendingBalance; + wallet.creationTimestamp = static_cast(dto.creationTimestamp); + wallet.container = reinterpret_cast(i); //dirty hack. container field must be unique + + index.push_back(wallet); + } +} + +void WalletSerializer::subscribeWallets() { + auto& index = m_walletsContainer.get(); + + for (auto it = index.begin(); it != index.end(); ++it) { + const auto& wallet = *it; + + AccountSubscription sub; + sub.keys.address.viewPublicKey = m_viewPublicKey; + sub.keys.address.spendPublicKey = wallet.spendPublicKey; + sub.keys.viewSecretKey = m_viewSecretKey; + sub.keys.spendSecretKey = wallet.spendSecretKey; + sub.transactionSpendableAge = 10; + sub.syncStart.height = 0; + sub.syncStart.timestamp = static_cast(wallet.creationTimestamp) - (60 * 60 * 24); + + auto& subscription = m_synchronizer.addSubscription(sub); + bool r = index.modify(it, [&subscription] (WalletRecord& rec) { rec.container = &subscription.getContainer(); }); + assert(r); + + subscription.addObserver(&m_transfersObserver); + } +} + +void WalletSerializer::loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext) { + deserializeEncrypted(m_actualBalance, "actual_balance", cryptoContext, source); + cryptoContext.incIv(); + + deserializeEncrypted(m_pendingBalance, "pending_balance", cryptoContext, source); + cryptoContext.incIv(); +} + +void WalletSerializer::loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext) { + std::string deciphered; + deserializeEncrypted(deciphered, "transfers_synchronizer", cryptoContext, source); + cryptoContext.incIv(); + + std::stringstream stream(deciphered); + deciphered.clear(); + + m_synchronizer.load(stream); +} + +void WalletSerializer::loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) { + auto& index = m_spentOutputs.get(); + auto& walletsIndex = m_walletsContainer.get(); + const uint64_t walletsSize = walletsIndex.size(); + + uint64_t count = 0; + deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < count; ++i) { + SpentOutputDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + assert(dto.walletIndex < walletsSize); + + SpentOutput output; + output.amount = dto.amount; + output.transactionHash = dto.transactionHash; + output.outputInTransaction = dto.outputInTransaction; + output.spendingTransactionHash = dto.spendingTransactionHash; + output.wallet = &walletsIndex[dto.walletIndex]; + + index.insert(std::move(output)); + } +} + +void WalletSerializer::loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext) { + auto& index = m_unlockTransactions.get(); + auto& walletsIndex = m_walletsContainer.get(); + const uint64_t walletsSize = walletsIndex.size(); + + uint64_t jobsCount = 0; + deserializeEncrypted(jobsCount, "unlock_transactions_jobs_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < jobsCount; ++i) { + UnlockTransactionJobDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + assert(dto.walletIndex < walletsSize); + + UnlockTransactionJob job; + job.blockHeight = dto.blockHeight; + job.transactionHash = dto.transactionHash; + job.container = walletsIndex[dto.walletIndex].container; + + index.insert(std::move(job)); + } +} + +void WalletSerializer::loadChange(Common::IInputStream& source, CryptoContext& cryptoContext) { + uint64_t count = 0; + deserializeEncrypted(count, "changes_count", cryptoContext, source); + cryptoContext.incIv(); + + for (uint64_t i = 0; i < count; i++) { + ChangeDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + m_change[dto.txHash] = dto.amount; + } +} + +void WalletSerializer::loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext) { + uint64_t count = 0; + deserializeEncrypted(count, "transactions_count", cryptoContext, source); + cryptoContext.incIv(); + + m_transactions.get().reserve(count); + + for (uint64_t i = 0; i < count; ++i) { + WalletTransactionDto dto; + deserializeEncrypted(dto, "", cryptoContext, source); + cryptoContext.incIv(); + + WalletTransaction tx; + tx.state = dto.state; + tx.timestamp = dto.timestamp; + tx.blockHeight = dto.blockHeight; + tx.hash = dto.hash; + tx.totalAmount = dto.totalAmount; + tx.fee = dto.fee; + tx.creationTime = dto.creationTime; + tx.unlockTime = dto.unlockTime; + tx.extra = dto.extra; + + m_transactions.get().push_back(std::move(tx)); + } +} + +void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext) { + uint64_t count = 0; + deserializeEncrypted(count, "transfers_count", cryptoContext, source); + cryptoContext.incIv(); + + m_transfers.reserve(count); + + for (uint64_t i = 0; i < count; ++i) { + uint64_t txId = 0; + deserializeEncrypted(txId, "transaction_id", cryptoContext, source); + cryptoContext.incIv(); + + WalletTransferDto dto; + deserializeEncrypted(dto, "transfer", cryptoContext, source); + cryptoContext.incIv(); + + WalletTransfer tr; + tr.address = dto.address; + tr.amount = dto.amount; + + m_transfers.push_back(std::make_pair(txId, tr)); + } +} + +void WalletSerializer::addWalletV1Details(const std::vector& txs, const std::vector& trs) { + size_t txId = 0; + m_transfers.reserve(trs.size()); + + for (const auto& tx: txs) { + WalletTransaction mtx = convert(tx); + m_transactions.get().push_back(std::move(mtx)); + + if (tx.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID && tx.transferCount != 0) { + size_t firstTr = tx.firstTransferId; + size_t lastTr = firstTr + tx.transferCount; + + if (lastTr > trs.size()) { + throw std::system_error(make_error_code(error::INTERNAL_WALLET_ERROR)); + } + + for (; firstTr < lastTr; firstTr++) { + WalletTransfer tr = convert(trs[firstTr]); + m_transfers.push_back(std::make_pair(txId, tr)); + } + } + + txId++; + } +} + +} //namespace CryptoNote diff --git a/src/Wallet/WalletSerialization.h b/src/Wallet/WalletSerialization.h new file mode 100755 index 0000000000..4a518a9c94 --- /dev/null +++ b/src/Wallet/WalletSerialization.h @@ -0,0 +1,117 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#pragma once + +#include "IWallet.h" +#include "WalletIndices.h" +#include "Common/IInputStream.h" +#include "Common/IOutputStream.h" +#include "Transfers/TransfersSynchronizer.h" +#include "Serialization/BinaryInputStreamSerializer.h" + +#include "crypto/chacha8.h" + +namespace CryptoNote { + +struct CryptoContext { + Crypto::chacha8_key key; + Crypto::chacha8_iv iv; + + void incIv(); +}; + +class WalletSerializer { +public: + WalletSerializer( + ITransfersObserver& transfersObserver, + Crypto::PublicKey& viewPublicKey, + Crypto::SecretKey& viewSecretKey, + uint64_t& actualBalance, + uint64_t& pendingBalance, + WalletsContainer& walletsContainer, + TransfersSyncronizer& synchronizer, + SpentOutputs& spentOutputs, + UnlockTransactionJobs& unlockTransactions, + TransactionChanges& change, + WalletTransactions& transactions, + WalletTransfers& transfers + ); + + void save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache); + void load(const std::string& password, Common::IInputStream& source); + +private: + static const uint32_t SERIALIZATION_VERSION; + + void loadCurrentVersion(Common::IInputStream& source, const std::string& password); + void loadWalletV1(Common::IInputStream& source, const std::string& password); + + CryptoContext generateCryptoContext(const std::string& password); + + void saveVersion(Common::IOutputStream& destination); + void saveIv(Common::IOutputStream& destination, Crypto::chacha8_iv& iv); + void saveKeys(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void savePublicKey(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveSecretKey(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveFlags(bool saveDetails, bool saveCache, Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext); + void saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext); + void saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext); + void saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext); + + uint32_t loadVersion(Common::IInputStream& source); + void loadIv(Common::IInputStream& source, Crypto::chacha8_iv& iv); + void generateKey(const std::string& password, Crypto::chacha8_key& key); + void loadKeys(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadPublicKey(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadSecretKey(Common::IInputStream& source, CryptoContext& cryptoContext); + void checkKeys(); + void loadFlags(bool& details, bool& cache, Common::IInputStream& source, CryptoContext& cryptoContext); + void loadWallets(Common::IInputStream& source, CryptoContext& cryptoContext); + void subscribeWallets(); + void loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadChange(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext); + void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext); + + void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer); + void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer); + void addWalletV1Details(const std::vector& txs, const std::vector& trs); + + ITransfersObserver& m_transfersObserver; + Crypto::PublicKey& m_viewPublicKey; + Crypto::SecretKey& m_viewSecretKey; + uint64_t& m_actualBalance; + uint64_t& m_pendingBalance; + WalletsContainer& m_walletsContainer; + TransfersSyncronizer& m_synchronizer; + SpentOutputs& m_spentOutputs; + UnlockTransactionJobs& m_unlockTransactions; + TransactionChanges& m_change; + WalletTransactions& m_transactions; + WalletTransfers& m_transfers; +}; + +} //namespace CryptoNote diff --git a/src/wallet/KeysStorage.cpp b/src/WalletLegacy/KeysStorage.cpp old mode 100644 new mode 100755 similarity index 86% rename from src/wallet/KeysStorage.cpp rename to src/WalletLegacy/KeysStorage.cpp index 503a4df127..ce5ca7e2c3 --- a/src/wallet/KeysStorage.cpp +++ b/src/WalletLegacy/KeysStorage.cpp @@ -17,10 +17,10 @@ #include "KeysStorage.h" -#include "WalletSerialization.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" -#include "cryptonote_core/cryptonote_serialization.h" +#include "WalletLegacy/WalletLegacySerialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" namespace CryptoNote { diff --git a/src/wallet/KeysStorage.h b/src/WalletLegacy/KeysStorage.h similarity index 84% rename from src/wallet/KeysStorage.h rename to src/WalletLegacy/KeysStorage.h index f7275f2bc8..dd712e3249 100644 --- a/src/wallet/KeysStorage.h +++ b/src/WalletLegacy/KeysStorage.h @@ -25,14 +25,15 @@ namespace CryptoNote { class ISerializer; +//This is DTO structure. Do not change it. struct KeysStorage { uint64_t creationTimestamp; - crypto::public_key spendPublicKey; - crypto::secret_key spendSecretKey; + Crypto::PublicKey spendPublicKey; + Crypto::SecretKey spendSecretKey; - crypto::public_key viewPublicKey; - crypto::secret_key viewSecretKey; + Crypto::PublicKey viewPublicKey; + Crypto::SecretKey viewSecretKey; void serialize(ISerializer& serializer, const std::string& name); }; diff --git a/src/wallet/WalletHelper.cpp b/src/WalletLegacy/WalletHelper.cpp similarity index 86% rename from src/wallet/WalletHelper.cpp rename to src/WalletLegacy/WalletHelper.cpp index 86fc754839..3d6bb36437 100755 --- a/src/wallet/WalletHelper.cpp +++ b/src/WalletLegacy/WalletHelper.cpp @@ -16,13 +16,11 @@ // along with Bytecoin. If not, see . #include "WalletHelper.h" +#include "Common/PathTools.h" #include #include -#include "Common/PathTools.h" -#include "cryptonote_protocol/blobdatatype.h" - using namespace CryptoNote; namespace { @@ -34,7 +32,7 @@ void openOutputFileStream(const std::string& filename, std::ofstream& file) { } } -std::error_code walletSaveWrapper(CryptoNote::IWallet& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { +std::error_code walletSaveWrapper(CryptoNote::IWalletLegacy& wallet, std::ofstream& file, bool saveDetailes, bool saveCache) { CryptoNote::WalletHelper::SaveWalletResultObserver o; std::error_code e; @@ -75,11 +73,21 @@ void WalletHelper::SendCompleteResultObserver::sendTransactionCompleted(CryptoNo std::error_code WalletHelper::SendCompleteResultObserver::wait(CryptoNote::TransactionId transactionId) { std::unique_lock lock(m_mutex); - m_condition.wait(lock, [this, &transactionId] { return m_finishedTransactions.find(transactionId) != m_finishedTransactions.end(); }); - return m_finishedTransactions.find(transactionId)->second; + + m_condition.wait(lock, [this, &transactionId] { + auto it = m_finishedTransactions.find(transactionId); + if (it == m_finishedTransactions.end()) { + return false; + } + + m_result = it->second; + return true; + }); + + return m_result; } -WalletHelper::IWalletRemoveObserverGuard::IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer) : +WalletHelper::IWalletRemoveObserverGuard::IWalletRemoveObserverGuard(CryptoNote::IWalletLegacy& wallet, CryptoNote::IWalletLegacyObserver& observer) : m_wallet(wallet), m_observer(observer), m_removed(false) { @@ -97,7 +105,7 @@ void WalletHelper::IWalletRemoveObserverGuard::removeObserver() { m_removed = true; } -void WalletHelper::storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename) { +void WalletHelper::storeWallet(CryptoNote::IWalletLegacy& wallet, const std::string& walletFilename) { boost::filesystem::path tempFile = boost::filesystem::unique_path(walletFilename + ".tmp.%%%%-%%%%"); if (boost::filesystem::exists(walletFilename)) { diff --git a/src/wallet/WalletHelper.h b/src/WalletLegacy/WalletHelper.h similarity index 75% rename from src/wallet/WalletHelper.h rename to src/WalletLegacy/WalletHelper.h index 4a4d87058b..adcc16db58 100755 --- a/src/wallet/WalletHelper.h +++ b/src/WalletLegacy/WalletHelper.h @@ -22,25 +22,24 @@ #include #include "crypto/hash.h" -#include "IWallet.h" - +#include "IWalletLegacy.h" namespace CryptoNote { namespace WalletHelper { -class SaveWalletResultObserver : public CryptoNote::IWalletObserver { +class SaveWalletResultObserver : public CryptoNote::IWalletLegacyObserver { public: std::promise saveResult; virtual void saveCompleted(std::error_code result) override { saveResult.set_value(result); } }; -class InitWalletResultObserver : public CryptoNote::IWalletObserver { +class InitWalletResultObserver : public CryptoNote::IWalletLegacyObserver { public: std::promise initResult; virtual void initCompleted(std::error_code result) override { initResult.set_value(result); } }; -class SendCompleteResultObserver : public CryptoNote::IWalletObserver { +class SendCompleteResultObserver : public CryptoNote::IWalletLegacyObserver { public: virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override; std::error_code wait(CryptoNote::TransactionId transactionId); @@ -49,21 +48,24 @@ class SendCompleteResultObserver : public CryptoNote::IWalletObserver { std::mutex m_mutex; std::condition_variable m_condition; std::map m_finishedTransactions; + std::error_code m_result; }; class IWalletRemoveObserverGuard { public: - IWalletRemoveObserverGuard(CryptoNote::IWallet& wallet, CryptoNote::IWalletObserver& observer); + IWalletRemoveObserverGuard(CryptoNote::IWalletLegacy& wallet, CryptoNote::IWalletLegacyObserver& observer); ~IWalletRemoveObserverGuard(); void removeObserver(); + private: - CryptoNote::IWallet& m_wallet; - CryptoNote::IWalletObserver& m_observer; + CryptoNote::IWalletLegacy& m_wallet; + CryptoNote::IWalletLegacyObserver& m_observer; bool m_removed; }; void prepareFileNames(const std::string& file_path, std::string& keys_file, std::string& wallet_file); -void storeWallet(CryptoNote::IWallet& wallet, const std::string& walletFilename); +void storeWallet(CryptoNote::IWalletLegacy& wallet, const std::string& walletFilename); -} } +} +} diff --git a/src/wallet/Wallet.cpp b/src/WalletLegacy/WalletLegacy.cpp similarity index 59% rename from src/wallet/Wallet.cpp rename to src/WalletLegacy/WalletLegacy.cpp index 6d047e591d..664a8309bb 100755 --- a/src/wallet/Wallet.cpp +++ b/src/WalletLegacy/WalletLegacy.cpp @@ -15,16 +15,17 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "Wallet.h" +#include "WalletLegacy.h" #include #include -#include "serialization/binary_utils.h" -#include "WalletHelper.h" -#include "WalletSerialization.h" -#include "WalletSerializer.h" -#include "WalletUtils.h" +#include "WalletLegacy/WalletHelper.h" +#include "WalletLegacy/WalletLegacySerialization.h" +#include "WalletLegacy/WalletLegacySerializer.h" +#include "WalletLegacy/WalletUtils.h" + +using namespace Crypto; namespace { @@ -34,12 +35,6 @@ void throwNotDefined() { throw std::runtime_error("The behavior is not defined!"); } -bool verifyKeys(const crypto::secret_key& sec, const crypto::public_key& expected_pub) { - crypto::public_key pub; - bool r = crypto::secret_key_to_public_key(sec, pub); - return r && expected_pub == pub; -} - class ContextCounterHolder { public: @@ -56,7 +51,7 @@ void runAtomic(std::mutex& mutex, F f) { f(); } -class InitWaiter : public CryptoNote::IWalletObserver { +class InitWaiter : public CryptoNote::IWalletLegacyObserver { public: InitWaiter() : future(promise.get_future()) {} @@ -73,7 +68,7 @@ class InitWaiter : public CryptoNote::IWalletObserver { }; -class SaveWaiter : public CryptoNote::IWalletObserver { +class SaveWaiter : public CryptoNote::IWalletLegacyObserver { public: SaveWaiter() : future(promise.get_future()) {} @@ -94,7 +89,7 @@ class SaveWaiter : public CryptoNote::IWalletObserver { namespace CryptoNote { -class SyncStarter : public CryptoNote::IWalletObserver { +class SyncStarter : public CryptoNote::IWalletLegacyObserver { public: SyncStarter(BlockchainSynchronizer& sync) : m_sync(sync) {} virtual ~SyncStarter() {} @@ -108,7 +103,7 @@ class SyncStarter : public CryptoNote::IWalletObserver { BlockchainSynchronizer& m_sync; }; -Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : +WalletLegacy::WalletLegacy(const CryptoNote::Currency& currency, INode& node) : m_state(NOT_INITIALIZED), m_currency(currency), m_node(node), @@ -124,7 +119,7 @@ Wallet::Wallet(const CryptoNote::Currency& currency, INode& node) : addObserver(m_onInitSyncStarter.get()); } -Wallet::~Wallet() { +WalletLegacy::~WalletLegacy() { removeObserver(m_onInitSyncStarter.get()); { @@ -141,15 +136,15 @@ Wallet::~Wallet() { m_sender.release(); } -void Wallet::addObserver(IWalletObserver* observer) { +void WalletLegacy::addObserver(IWalletLegacyObserver* observer) { m_observerManager.add(observer); } -void Wallet::removeObserver(IWalletObserver* observer) { +void WalletLegacy::removeObserver(IWalletLegacyObserver* observer) { m_observerManager.remove(observer); } -void Wallet::initAndGenerate(const std::string& password) { +void WalletLegacy::initAndGenerate(const std::string& password) { { std::unique_lock stateLock(m_cacheMutex); @@ -163,10 +158,10 @@ void Wallet::initAndGenerate(const std::string& password) { initSync(); } - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, std::error_code()); } -void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password) { +void WalletLegacy::initWithKeys(const AccountKeys& accountKeys, const std::string& password) { { std::unique_lock stateLock(m_cacheMutex); @@ -174,35 +169,17 @@ void Wallet::initWithKeys(const WalletAccountKeys& accountKeys, const std::strin throw std::system_error(make_error_code(error::ALREADY_INITIALIZED)); } - account_keys keys; - - std::copy(accountKeys.spendPublicKey.begin(), - accountKeys.spendPublicKey.end(), - reinterpret_cast(&keys.m_account_address.m_spendPublicKey)); - - std::copy(accountKeys.viewPublicKey.begin(), - accountKeys.viewPublicKey.end(), - reinterpret_cast(&keys.m_account_address.m_viewPublicKey)); - - std::copy(accountKeys.spendSecretKey.begin(), - accountKeys.spendSecretKey.end(), - reinterpret_cast(&keys.m_spend_secret_key)); - - std::copy(accountKeys.viewSecretKey.begin(), - accountKeys.viewSecretKey.end(), - reinterpret_cast(&keys.m_view_secret_key)); - - m_account.set_keys(keys); + m_account.setAccountKeys(accountKeys); m_account.set_createtime(ACCOUN_CREATE_TIME_ACCURACY); m_password = password; initSync(); } - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, std::error_code()); } -void Wallet::initAndLoad(std::istream& source, const std::string& password) { +void WalletLegacy::initAndLoad(std::istream& source, const std::string& password) { std::unique_lock stateLock(m_cacheMutex); if (m_state != NOT_INITIALIZED) { @@ -213,13 +190,13 @@ void Wallet::initAndLoad(std::istream& source, const std::string& password) { m_state = LOADING; m_asyncContextCounter.addAsyncContext(); - std::thread loader(&Wallet::doLoad, this, std::ref(source)); + std::thread loader(&WalletLegacy::doLoad, this, std::ref(source)); loader.detach(); } -void Wallet::initSync() { +void WalletLegacy::initSync() { AccountSubscription sub; - sub.keys = reinterpret_cast(m_account.get_keys()); + sub.keys = reinterpret_cast(m_account.getAccountKeys()); sub.transactionSpendableAge = 1; sub.syncStart.height = 0; sub.syncStart.timestamp = m_account.get_createtime() - ACCOUN_CREATE_TIME_ACCURACY; @@ -228,19 +205,19 @@ void Wallet::initSync() { m_transferDetails = &subObject.getContainer(); subObject.addObserver(this); - m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.get_keys(), *m_transferDetails)); + m_sender.reset(new WalletTransactionSender(m_currency, m_transactionsCache, m_account.getAccountKeys(), *m_transferDetails)); m_state = INITIALIZED; m_blockchainSync.addObserver(this); } -void Wallet::doLoad(std::istream& source) { +void WalletLegacy::doLoad(std::istream& source) { ContextCounterHolder counterHolder(m_asyncContextCounter); try { std::unique_lock lock(m_cacheMutex); std::string cache; - WalletSerializer serializer(m_account, m_transactionsCache); + WalletLegacySerializer serializer(m_account, m_transactionsCache); serializer.deserialize(source, m_password, cache); initSync(); @@ -254,19 +231,19 @@ void Wallet::doLoad(std::istream& source) { // ignore cache loading errors } } catch (std::system_error& e) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::initCompleted, e.code()); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::NOT_INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, e.code()); return; } catch (std::exception&) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::NOT_INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::NOT_INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } - m_observerManager.notify(&IWalletObserver::initCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::initCompleted, std::error_code()); } -void Wallet::shutdown() { +void WalletLegacy::shutdown() { { std::unique_lock lock(m_cacheMutex); @@ -292,7 +269,7 @@ void Wallet::shutdown() { m_isStopping = false; m_state = NOT_INITIALIZED; - const AccountAddress& accountAddress = reinterpret_cast(m_account.get_keys().m_account_address); + const auto& accountAddress = m_account.getAccountKeys().address; auto subObject = m_transfersSync.getSubscription(accountAddress); assert(subObject != nullptr); subObject->removeObserver(this); @@ -305,19 +282,21 @@ void Wallet::shutdown() { } } -void Wallet::reset() { - InitWaiter initWaiter; - SaveWaiter saveWaiter; - WalletHelper::IWalletRemoveObserverGuard initGuarantee(*this, initWaiter); - WalletHelper::IWalletRemoveObserverGuard saveGuarantee(*this, saveWaiter); - - std::stringstream ss; +void WalletLegacy::reset() { try { - save(ss, false, false); + std::error_code saveError; + std::stringstream ss; + { + SaveWaiter saveWaiter; + WalletHelper::IWalletRemoveObserverGuard saveGuarantee(*this, saveWaiter); + save(ss, false, false); + saveError = saveWaiter.waitSave(); + } - auto saveError = saveWaiter.waitSave(); if (!saveError) { shutdown(); + InitWaiter initWaiter; + WalletHelper::IWalletRemoveObserverGuard initGuarantee(*this, initWaiter); initAndLoad(ss, m_password); initWaiter.waitInit(); } @@ -326,9 +305,9 @@ void Wallet::reset() { } } -void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) { +void WalletLegacy::save(std::ostream& destination, bool saveDetailed, bool saveCache) { if(m_isStopping) { - m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(CryptoNote::error::OPERATION_CANCELLED)); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, make_error_code(CryptoNote::error::OPERATION_CANCELLED)); return; } @@ -341,18 +320,18 @@ void Wallet::save(std::ostream& destination, bool saveDetailed, bool saveCache) } m_asyncContextCounter.addAsyncContext(); - std::thread saver(&Wallet::doSave, this, std::ref(destination), saveDetailed, saveCache); + std::thread saver(&WalletLegacy::doSave, this, std::ref(destination), saveDetailed, saveCache); saver.detach(); } -void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache) { +void WalletLegacy::doSave(std::ostream& destination, bool saveDetailed, bool saveCache) { ContextCounterHolder counterHolder(m_asyncContextCounter); try { m_blockchainSync.stop(); std::unique_lock lock(m_cacheMutex); - WalletSerializer serializer(m_account, m_transactionsCache); + WalletLegacySerializer serializer(m_account, m_transactionsCache); std::string cache; if (saveCache) { @@ -367,20 +346,20 @@ void Wallet::doSave(std::ostream& destination, bool saveDetailed, bool saveCache m_blockchainSync.start(); //XXX: start can throw. what to do in this case? } catch (std::system_error& e) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::saveCompleted, e.code()); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, e.code()); return; } catch (std::exception&) { - runAtomic(m_cacheMutex, [this] () {this->m_state = Wallet::INITIALIZED;} ); - m_observerManager.notify(&IWalletObserver::saveCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); + runAtomic(m_cacheMutex, [this] () {this->m_state = WalletLegacy::INITIALIZED;} ); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR)); return; } - m_observerManager.notify(&IWalletObserver::saveCompleted, std::error_code()); + m_observerManager.notify(&IWalletLegacyObserver::saveCompleted, std::error_code()); } -std::error_code Wallet::changePassword(const std::string& oldPassword, const std::string& newPassword) { +std::error_code WalletLegacy::changePassword(const std::string& oldPassword, const std::string& newPassword) { std::unique_lock passLock(m_cacheMutex); throwIfNotInitialised(); @@ -394,14 +373,14 @@ std::error_code Wallet::changePassword(const std::string& oldPassword, const std return std::error_code(); } -std::string Wallet::getAddress() { +std::string WalletLegacy::getAddress() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_currency.accountAddressAsString(m_account); } -uint64_t Wallet::actualBalance() { +uint64_t WalletLegacy::actualBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); @@ -409,7 +388,7 @@ uint64_t Wallet::actualBalance() { m_transactionsCache.unconfrimedOutsAmount(); } -uint64_t Wallet::pendingBalance() { +uint64_t WalletLegacy::pendingBalance() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); @@ -417,53 +396,53 @@ uint64_t Wallet::pendingBalance() { return m_transferDetails->balance(ITransfersContainer::IncludeKeyNotUnlocked) + change; } -size_t Wallet::getTransactionCount() { +size_t WalletLegacy::getTransactionCount() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransactionCount(); } -size_t Wallet::getTransferCount() { +size_t WalletLegacy::getTransferCount() { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransferCount(); } -TransactionId Wallet::findTransactionByTransferId(TransferId transferId) { +TransactionId WalletLegacy::findTransactionByTransferId(TransferId transferId) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.findTransactionByTransferId(transferId); } -bool Wallet::getTransaction(TransactionId transactionId, TransactionInfo& transaction) { +bool WalletLegacy::getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransaction(transactionId, transaction); } -bool Wallet::getTransfer(TransferId transferId, Transfer& transfer) { +bool WalletLegacy::getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) { std::unique_lock lock(m_cacheMutex); throwIfNotInitialised(); return m_transactionsCache.getTransfer(transferId, transfer); } -TransactionId Wallet::sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { - std::vector transfers; +TransactionId WalletLegacy::sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { + std::vector transfers; transfers.push_back(transfer); throwIfNotInitialised(); return sendTransaction(transfers, fee, extra, mixIn, unlockTimestamp); } -TransactionId Wallet::sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { +TransactionId WalletLegacy::sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { TransactionId txId = 0; std::shared_ptr request; - std::deque > events; + std::deque> events; throwIfNotInitialised(); { @@ -475,15 +454,15 @@ TransactionId Wallet::sendTransaction(const std::vector& transfers, ui if (request) { m_asyncContextCounter.addAsyncContext(); - request->perform(m_node, std::bind(&Wallet::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); + request->perform(m_node, std::bind(&WalletLegacy::sendTransactionCallback, this, std::placeholders::_1, std::placeholders::_2)); } return txId; } -void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) { +void WalletLegacy::sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec) { ContextCounterHolder counterHolder(m_asyncContextCounter); - std::deque > events; + std::deque > events; boost::optional > nextRequest; { @@ -495,14 +474,14 @@ void Wallet::sendTransactionCallback(WalletRequest::Callback callback, std::erro if (nextRequest) { m_asyncContextCounter.addAsyncContext(); - (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + (*nextRequest)->perform(m_node, std::bind(&WalletLegacy::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } } -void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { +void WalletLegacy::synchronizationCallback(WalletRequest::Callback callback, std::error_code ec) { ContextCounterHolder counterHolder(m_asyncContextCounter); - std::deque > events; + std::deque > events; boost::optional > nextRequest; { std::unique_lock lock(m_cacheMutex); @@ -513,25 +492,25 @@ void Wallet::synchronizationCallback(WalletRequest::Callback callback, std::erro if (nextRequest) { m_asyncContextCounter.addAsyncContext(); - (*nextRequest)->perform(m_node, std::bind(&Wallet::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); + (*nextRequest)->perform(m_node, std::bind(&WalletLegacy::synchronizationCallback, this, std::placeholders::_1, std::placeholders::_2)); } } -std::error_code Wallet::cancelTransaction(size_t transactionId) { +std::error_code WalletLegacy::cancelTransaction(size_t transactionId) { return make_error_code(CryptoNote::error::TX_CANCEL_IMPOSSIBLE); } -void Wallet::synchronizationProgressUpdated(uint64_t current, uint64_t total) { +void WalletLegacy::synchronizationProgressUpdated(uint32_t current, uint32_t total) { // forward notification - m_observerManager.notify(&IWalletObserver::synchronizationProgressUpdated, current, total); + m_observerManager.notify(&IWalletLegacyObserver::synchronizationProgressUpdated, current, total); // check if balance has changed and notify client notifyIfBalanceChanged(); } -void Wallet::synchronizationCompleted(std::error_code result) { +void WalletLegacy::synchronizationCompleted(std::error_code result) { if (result != std::make_error_code(std::errc::interrupted)) { - m_observerManager.notify(&IWalletObserver::synchronizationCompleted, result); + m_observerManager.notify(&IWalletLegacyObserver::synchronizationCompleted, result); } if (!result) { @@ -539,8 +518,8 @@ void Wallet::synchronizationCompleted(std::error_code result) { } } -void Wallet::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { - std::shared_ptr event; +void WalletLegacy::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) { + std::shared_ptr event; TransactionInformation txInfo; int64_t txBalance; @@ -554,8 +533,8 @@ void Wallet::onTransactionUpdated(ITransfersSubscription* object, const Hash& tr } } -void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { - std::shared_ptr event; +void WalletLegacy::onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) { + std::shared_ptr event; { std::unique_lock lock(m_cacheMutex); @@ -567,59 +546,44 @@ void Wallet::onTransactionDeleted(ITransfersSubscription* object, const Hash& tr } } -void Wallet::throwIfNotInitialised() { +void WalletLegacy::throwIfNotInitialised() { if (m_state == NOT_INITIALIZED || m_state == LOADING) { throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } assert(m_transferDetails); } -void Wallet::notifyClients(std::deque >& events) { +void WalletLegacy::notifyClients(std::deque >& events) { while (!events.empty()) { - std::shared_ptr event = events.front(); + std::shared_ptr event = events.front(); event->notify(m_observerManager); events.pop_front(); } } -void Wallet::notifyIfBalanceChanged() { +void WalletLegacy::notifyIfBalanceChanged() { auto actual = actualBalance(); auto prevActual = m_lastNotifiedActualBalance.exchange(actual); if (prevActual != actual) { - m_observerManager.notify(&IWalletObserver::actualBalanceUpdated, actual); + m_observerManager.notify(&IWalletLegacyObserver::actualBalanceUpdated, actual); } auto pending = pendingBalance(); auto prevPending = m_lastNotifiedPendingBalance.exchange(pending); if (prevPending != pending) { - m_observerManager.notify(&IWalletObserver::pendingBalanceUpdated, pending); + m_observerManager.notify(&IWalletLegacyObserver::pendingBalanceUpdated, pending); } } -void Wallet::getAccountKeys(WalletAccountKeys& keys) { +void WalletLegacy::getAccountKeys(AccountKeys& keys) { if (m_state == NOT_INITIALIZED) { throw std::system_error(make_error_code(CryptoNote::error::NOT_INITIALIZED)); } - const CryptoNote::account_keys& accountKeys = m_account.get_keys(); - std::copy(reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey), - reinterpret_cast(&accountKeys.m_account_address.m_spendPublicKey) + sizeof(crypto::public_key), - keys.spendPublicKey.begin()); - - std::copy(reinterpret_cast(&accountKeys.m_spend_secret_key), - reinterpret_cast(&accountKeys.m_spend_secret_key) + sizeof(crypto::secret_key), - keys.spendSecretKey.begin()); - - std::copy(reinterpret_cast(&accountKeys.m_account_address.m_viewPublicKey), - reinterpret_cast(&accountKeys.m_account_address.m_viewPublicKey) + sizeof(crypto::public_key), - keys.viewPublicKey.begin()); - - std::copy(reinterpret_cast(&accountKeys.m_view_secret_key), - reinterpret_cast(&accountKeys.m_view_secret_key) + sizeof(crypto::secret_key), - keys.viewSecretKey.begin()); + keys = m_account.getAccountKeys(); } } //namespace CryptoNote diff --git a/src/wallet/Wallet.h b/src/WalletLegacy/WalletLegacy.h old mode 100644 new mode 100755 similarity index 62% rename from src/wallet/Wallet.h rename to src/WalletLegacy/WalletLegacy.h index 6080012782..24fbd6b6db --- a/src/wallet/Wallet.h +++ b/src/WalletLegacy/WalletLegacy.h @@ -23,42 +23,42 @@ #include #include -#include "IWallet.h" +#include "IWalletLegacy.h" #include "INode.h" -#include "WalletErrors.h" -#include "WalletAsyncContextCounter.h" +#include "Wallet/WalletErrors.h" +#include "Wallet/WalletAsyncContextCounter.h" #include "Common/ObserverManager.h" -#include "cryptonote_core/tx_extra.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/Currency.h" -#include "WalletUserTransactionsCache.h" -#include "WalletUnconfirmedTransactions.h" +#include "CryptoNoteCore/TransactionExtra.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/Currency.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" -#include "WalletTransactionSender.h" -#include "WalletRequest.h" +#include "WalletLegacy/WalletTransactionSender.h" +#include "WalletLegacy/WalletRequest.h" -#include "transfers/BlockchainSynchronizer.h" -#include "transfers/TransfersSynchronizer.h" +#include "Transfers/BlockchainSynchronizer.h" +#include "Transfers/TransfersSynchronizer.h" namespace CryptoNote { class SyncStarter; -class Wallet : - public IWallet, +class WalletLegacy : + public IWalletLegacy, IBlockchainSynchronizerObserver, ITransfersObserver { public: - Wallet(const CryptoNote::Currency& currency, INode& node); - virtual ~Wallet(); + WalletLegacy(const CryptoNote::Currency& currency, INode& node); + virtual ~WalletLegacy(); - virtual void addObserver(IWalletObserver* observer); - virtual void removeObserver(IWalletObserver* observer); + virtual void addObserver(IWalletLegacyObserver* observer); + virtual void removeObserver(IWalletLegacyObserver* observer); virtual void initAndGenerate(const std::string& password); virtual void initAndLoad(std::istream& source, const std::string& password); - virtual void initWithKeys(const WalletAccountKeys& accountKeys, const std::string& password); + virtual void initWithKeys(const AccountKeys& accountKeys, const std::string& password); virtual void shutdown(); virtual void reset(); @@ -76,24 +76,24 @@ class Wallet : virtual TransactionId findTransactionByTransferId(TransferId transferId); - virtual bool getTransaction(TransactionId transactionId, TransactionInfo& transaction); - virtual bool getTransfer(TransferId transferId, Transfer& transfer); + virtual bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction); + virtual bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer); - virtual TransactionId sendTransaction(const Transfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); - virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + virtual TransactionId sendTransaction(const WalletLegacyTransfer& transfer, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + virtual TransactionId sendTransaction(const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); virtual std::error_code cancelTransaction(size_t transactionId); - virtual void getAccountKeys(WalletAccountKeys& keys); + virtual void getAccountKeys(AccountKeys& keys); private: // IBlockchainSynchronizerObserver - virtual void synchronizationProgressUpdated(uint64_t current, uint64_t total) override; + virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override; virtual void synchronizationCompleted(std::error_code result) override; // ITransfersObserver - virtual void onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) override; - virtual void onTransactionDeleted(ITransfersSubscription* object, const Hash& transactionHash) override; + virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; + virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; void initSync(); void throwIfNotInitialised(); @@ -103,7 +103,7 @@ class Wallet : void synchronizationCallback(WalletRequest::Callback callback, std::error_code ec); void sendTransactionCallback(WalletRequest::Callback callback, std::error_code ec); - void notifyClients(std::deque >& events); + void notifyClients(std::deque >& events); void notifyIfBalanceChanged(); enum WalletState @@ -116,7 +116,7 @@ class Wallet : WalletState m_state; std::mutex m_cacheMutex; - CryptoNote::account_base m_account; + CryptoNote::AccountBase m_account; std::string m_password; const CryptoNote::Currency& m_currency; INode& m_node; @@ -133,7 +133,7 @@ class Wallet : std::unique_ptr m_sender; WalletAsyncContextCounter m_asyncContextCounter; - tools::ObserverManager m_observerManager; + Tools::ObserverManager m_observerManager; std::unique_ptr m_onInitSyncStarter; }; diff --git a/src/wallet/WalletEvent.h b/src/WalletLegacy/WalletLegacyEvent.h old mode 100644 new mode 100755 similarity index 50% rename from src/wallet/WalletEvent.h rename to src/WalletLegacy/WalletLegacyEvent.h index faf64e034e..b1d9706162 --- a/src/wallet/WalletEvent.h +++ b/src/WalletLegacy/WalletLegacyEvent.h @@ -17,44 +17,45 @@ #pragma once -#include "IWallet.h" +#include "IWalletLegacy.h" #include "Common/ObserverManager.h" namespace CryptoNote { -class WalletEvent +class WalletLegacyEvent { public: - virtual ~WalletEvent() {}; + virtual ~WalletLegacyEvent() { + }; - virtual void notify(tools::ObserverManager& observer) = 0; + virtual void notify(Tools::ObserverManager& observer) = 0; }; -class WalletTransactionUpdatedEvent : public WalletEvent +class WalletTransactionUpdatedEvent : public WalletLegacyEvent { public: WalletTransactionUpdatedEvent(TransactionId transactionId) : m_id(transactionId) {}; virtual ~WalletTransactionUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::transactionUpdated, m_id); + observer.notify(&IWalletLegacyObserver::transactionUpdated, m_id); } private: TransactionId m_id; }; -class WalletSendTransactionCompletedEvent : public WalletEvent +class WalletSendTransactionCompletedEvent : public WalletLegacyEvent { public: WalletSendTransactionCompletedEvent(TransactionId transactionId, std::error_code result) : m_id(transactionId), m_error(result) {}; virtual ~WalletSendTransactionCompletedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::sendTransactionCompleted, m_id, m_error); + observer.notify(&IWalletLegacyObserver::sendTransactionCompleted, m_id, m_error); } private: @@ -62,72 +63,72 @@ class WalletSendTransactionCompletedEvent : public WalletEvent std::error_code m_error; }; -class WalletExternalTransactionCreatedEvent : public WalletEvent +class WalletExternalTransactionCreatedEvent : public WalletLegacyEvent { public: WalletExternalTransactionCreatedEvent(TransactionId transactionId) : m_id(transactionId) {}; virtual ~WalletExternalTransactionCreatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::externalTransactionCreated, m_id); + observer.notify(&IWalletLegacyObserver::externalTransactionCreated, m_id); } private: TransactionId m_id; }; -class WalletSynchronizationProgressUpdatedEvent : public WalletEvent +class WalletSynchronizationProgressUpdatedEvent : public WalletLegacyEvent { public: - WalletSynchronizationProgressUpdatedEvent(uint64_t current, uint64_t total) : m_current(current), m_total(total) {}; + WalletSynchronizationProgressUpdatedEvent(uint32_t current, uint32_t total) : m_current(current), m_total(total) {}; virtual ~WalletSynchronizationProgressUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::synchronizationProgressUpdated, m_current, m_total); + observer.notify(&IWalletLegacyObserver::synchronizationProgressUpdated, m_current, m_total); } private: - uint64_t m_current; - uint64_t m_total; + uint32_t m_current; + uint32_t m_total; }; -class WalletSynchronizationCompletedEvent : public WalletEvent { +class WalletSynchronizationCompletedEvent : public WalletLegacyEvent { public: - WalletSynchronizationCompletedEvent(uint64_t current, uint64_t total, std::error_code result) : m_ec(result) {}; + WalletSynchronizationCompletedEvent(uint32_t current, uint32_t total, std::error_code result) : m_ec(result) {}; virtual ~WalletSynchronizationCompletedEvent() {}; - virtual void notify(tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::synchronizationCompleted, m_ec); + virtual void notify(Tools::ObserverManager& observer) { + observer.notify(&IWalletLegacyObserver::synchronizationCompleted, m_ec); } private: std::error_code m_ec; }; -class WalletActualBalanceUpdatedEvent : public WalletEvent +class WalletActualBalanceUpdatedEvent : public WalletLegacyEvent { public: WalletActualBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {}; virtual ~WalletActualBalanceUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::actualBalanceUpdated, m_balance); + observer.notify(&IWalletLegacyObserver::actualBalanceUpdated, m_balance); } private: uint64_t m_balance; }; -class WalletPendingBalanceUpdatedEvent : public WalletEvent +class WalletPendingBalanceUpdatedEvent : public WalletLegacyEvent { public: WalletPendingBalanceUpdatedEvent(uint64_t balance) : m_balance(balance) {}; virtual ~WalletPendingBalanceUpdatedEvent() {}; - virtual void notify(tools::ObserverManager& observer) + virtual void notify(Tools::ObserverManager& observer) { - observer.notify(&IWalletObserver::pendingBalanceUpdated, m_balance); + observer.notify(&IWalletLegacyObserver::pendingBalanceUpdated, m_balance); } private: uint64_t m_balance; diff --git a/src/wallet/WalletSerialization.cpp b/src/WalletLegacy/WalletLegacySerialization.cpp old mode 100644 new mode 100755 similarity index 67% rename from src/wallet/WalletSerialization.cpp rename to src/WalletLegacy/WalletLegacySerialization.cpp index 6246c2bde8..88166a4c80 --- a/src/wallet/WalletSerialization.cpp +++ b/src/WalletLegacy/WalletLegacySerialization.cpp @@ -15,13 +15,13 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletSerialization.h" -#include "WalletUnconfirmedTransactions.h" -#include "IWallet.h" +#include "WalletLegacySerialization.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" +#include "IWalletLegacy.h" -#include "cryptonote_core/cryptonote_serialization.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" namespace CryptoNote { @@ -37,7 +37,7 @@ void serialize(UnconfirmedTransferDetails& utd, CryptoNote::ISerializer& seriali utd.transactionId = static_cast(txId); } -void serialize(TransactionInfo& txi, CryptoNote::ISerializer& serializer) { +void serialize(WalletLegacyTransaction& txi, CryptoNote::ISerializer& serializer) { uint64_t trId = static_cast(txi.firstTransferId); serializer(trId, "first_transfer_id"); txi.firstTransferId = static_cast(trId); @@ -51,13 +51,31 @@ void serialize(TransactionInfo& txi, CryptoNote::ISerializer& serializer) { serializer(txi.fee, "fee"); serializer(txi.hash, "hash"); serializer(txi.isCoinbase, "is_coinbase"); - serializer(txi.blockHeight, "block_height"); + + if (serializer.type() == ISerializer::INPUT) { + uint64_t height = 0; + serializer(height, "block_height"); + + if (height == std::numeric_limits::max()) { + txi.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; + } else { + txi.blockHeight = static_cast(height); + } + } else { + serializer(txi.blockHeight, "block_height"); + } + serializer(txi.timestamp, "timestamp"); serializer(txi.unlockTime, "unlock_time"); serializer(txi.extra, "extra"); + + //this field has been added later in the structure. + //in order to not break backward binary compatibility + // we just set it to zero + txi.sentTime = 0; } -void serialize(Transfer& tr, CryptoNote::ISerializer& serializer) { +void serialize(WalletLegacyTransfer& tr, CryptoNote::ISerializer& serializer) { serializer(tr.address, "address"); serializer(tr.amount, "amount"); } diff --git a/src/wallet/WalletSerialization.h b/src/WalletLegacy/WalletLegacySerialization.h similarity index 81% rename from src/wallet/WalletSerialization.h rename to src/WalletLegacy/WalletLegacySerialization.h index 85a0f24051..751f45969a 100755 --- a/src/wallet/WalletSerialization.h +++ b/src/WalletLegacy/WalletLegacySerialization.h @@ -21,17 +21,17 @@ #include #include -#include "IWallet.h" +#include "IWalletLegacy.h" namespace CryptoNote { class ISerializer; struct UnconfirmedTransferDetails; -struct TransactionInfo; -struct Transfer; +struct WalletLegacyTransaction; +struct WalletLegacyTransfer; void serialize(UnconfirmedTransferDetails& utd, ISerializer& serializer); -void serialize(TransactionInfo& txi, ISerializer& serializer); -void serialize(Transfer& tr, ISerializer& serializer); +void serialize(WalletLegacyTransaction& txi, ISerializer& serializer); +void serialize(WalletLegacyTransfer& tr, ISerializer& serializer); } diff --git a/src/WalletLegacy/WalletLegacySerializer.cpp b/src/WalletLegacy/WalletLegacySerializer.cpp new file mode 100755 index 0000000000..1be62e6091 --- /dev/null +++ b/src/WalletLegacy/WalletLegacySerializer.cpp @@ -0,0 +1,186 @@ +// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers +// +// This file is part of Bytecoin. +// +// Bytecoin is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Bytecoin 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with Bytecoin. If not, see . + +#include "WalletLegacySerializer.h" + +#include + +#include "Common/MemoryInputStream.h" +#include "Common/StdInputStream.h" +#include "Common/StdOutputStream.h" +#include "Serialization/BinaryOutputStreamSerializer.h" +#include "Serialization/BinaryInputStreamSerializer.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/CryptoNoteSerialization.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "Wallet/WalletErrors.h" +#include "WalletLegacy/KeysStorage.h" + +using namespace Common; + +namespace { + +bool verifyKeys(const Crypto::SecretKey& sec, const Crypto::PublicKey& expected_pub) { + Crypto::PublicKey pub; + bool r = Crypto::secret_key_to_public_key(sec, pub); + return r && expected_pub == pub; +} + +void throwIfKeysMissmatch(const Crypto::SecretKey& sec, const Crypto::PublicKey& expected_pub) { + if (!verifyKeys(sec, expected_pub)) + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); +} + +} + +namespace CryptoNote { + +WalletLegacySerializer::WalletLegacySerializer(CryptoNote::AccountBase& account, WalletUserTransactionsCache& transactionsCache) : + account(account), + transactionsCache(transactionsCache), + walletSerializationVersion(1) +{ +} + +void WalletLegacySerializer::serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache) { + std::stringstream plainArchive; + StdOutputStream plainStream(plainArchive); + CryptoNote::BinaryOutputStreamSerializer serializer(plainStream); + saveKeys(serializer); + + serializer(saveDetailed, "has_details"); + + if (saveDetailed) { + serializer(transactionsCache, "details"); + } + + serializer.binary(const_cast(cache), "cache"); + + std::string plain = plainArchive.str(); + std::string cipher; + + Crypto::chacha8_iv iv = encrypt(plain, password, cipher); + + uint32_t version = walletSerializationVersion; + StdOutputStream output(stream); + CryptoNote::BinaryOutputStreamSerializer s(output); + s.beginObject("wallet"); + s(version, "version"); + s(iv, "iv"); + s(cipher, "data"); + s.endObject(); + + stream.flush(); +} + +void WalletLegacySerializer::saveKeys(CryptoNote::ISerializer& serializer) { + CryptoNote::KeysStorage keys; + CryptoNote::AccountKeys acc = account.getAccountKeys(); + + keys.creationTimestamp = account.get_createtime(); + keys.spendPublicKey = acc.address.spendPublicKey; + keys.spendSecretKey = acc.spendSecretKey; + keys.viewPublicKey = acc.address.viewPublicKey; + keys.viewSecretKey = acc.viewSecretKey; + + keys.serialize(serializer, "keys"); +} + +Crypto::chacha8_iv WalletLegacySerializer::encrypt(const std::string& plain, const std::string& password, std::string& cipher) { + Crypto::chacha8_key key; + Crypto::cn_context context; + Crypto::generate_chacha8_key(context, password, key); + + cipher.resize(plain.size()); + + Crypto::chacha8_iv iv = Crypto::rand(); + Crypto::chacha8(plain.data(), plain.size(), key, iv, &cipher[0]); + + return iv; +} + + +void WalletLegacySerializer::deserialize(std::istream& stream, const std::string& password, std::string& cache) { + StdInputStream stdStream(stream); + CryptoNote::BinaryInputStreamSerializer serializerEncrypted(stdStream); + + serializerEncrypted.beginObject("wallet"); + + uint32_t version; + serializerEncrypted(version, "version"); + + Crypto::chacha8_iv iv; + serializerEncrypted(iv, "iv"); + + std::string cipher; + serializerEncrypted(cipher, "data"); + + serializerEncrypted.endObject(); + + std::string plain; + decrypt(cipher, plain, iv, password); + + MemoryInputStream decryptedStream(plain.data(), plain.size()); + CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); + + try + { + loadKeys(serializer); + throwIfKeysMissmatch(account.getAccountKeys().viewSecretKey, account.getAccountKeys().address.viewPublicKey); + throwIfKeysMissmatch(account.getAccountKeys().spendSecretKey, account.getAccountKeys().address.spendPublicKey); + } + catch (std::exception&) { + throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); + } + + bool detailsSaved; + + serializer(detailsSaved, "has_details"); + + if (detailsSaved) { + serializer(transactionsCache, "details"); + } + + serializer.binary(cache, "cache"); +} + +void WalletLegacySerializer::decrypt(const std::string& cipher, std::string& plain, Crypto::chacha8_iv iv, const std::string& password) { + Crypto::chacha8_key key; + Crypto::cn_context context; + Crypto::generate_chacha8_key(context, password, key); + + plain.resize(cipher.size()); + + Crypto::chacha8(cipher.data(), cipher.size(), key, iv, &plain[0]); +} + +void WalletLegacySerializer::loadKeys(CryptoNote::ISerializer& serializer) { + CryptoNote::KeysStorage keys; + + keys.serialize(serializer, "keys"); + + CryptoNote::AccountKeys acc; + acc.address.spendPublicKey = keys.spendPublicKey; + acc.spendSecretKey = keys.spendSecretKey; + acc.address.viewPublicKey = keys.viewPublicKey; + acc.viewSecretKey = keys.viewSecretKey; + + account.setAccountKeys(acc); + account.set_createtime(keys.creationTimestamp); +} + +} diff --git a/src/wallet/WalletSerializer.h b/src/WalletLegacy/WalletLegacySerializer.h old mode 100644 new mode 100755 similarity index 82% rename from src/wallet/WalletSerializer.h rename to src/WalletLegacy/WalletLegacySerializer.h index aec3eac975..98e7f9bca7 --- a/src/wallet/WalletSerializer.h +++ b/src/WalletLegacy/WalletLegacySerializer.h @@ -25,7 +25,7 @@ #include "crypto/chacha8.h" namespace CryptoNote { -class account_base; +class AccountBase; class ISerializer; } @@ -33,9 +33,9 @@ namespace CryptoNote { class WalletUserTransactionsCache; -class WalletSerializer { +class WalletLegacySerializer { public: - WalletSerializer(CryptoNote::account_base& account, WalletUserTransactionsCache& transactionsCache); + WalletLegacySerializer(CryptoNote::AccountBase& account, WalletUserTransactionsCache& transactionsCache); void serialize(std::ostream& stream, const std::string& password, bool saveDetailed, const std::string& cache); void deserialize(std::istream& stream, const std::string& password, std::string& cache); @@ -44,10 +44,10 @@ class WalletSerializer { void saveKeys(CryptoNote::ISerializer& serializer); void loadKeys(CryptoNote::ISerializer& serializer); - crypto::chacha8_iv encrypt(const std::string& plain, const std::string& password, std::string& cipher); - void decrypt(const std::string& cipher, std::string& plain, crypto::chacha8_iv iv, const std::string& password); + Crypto::chacha8_iv encrypt(const std::string& plain, const std::string& password, std::string& cipher); + void decrypt(const std::string& cipher, std::string& plain, Crypto::chacha8_iv iv, const std::string& password); - CryptoNote::account_base& account; + CryptoNote::AccountBase& account; WalletUserTransactionsCache& transactionsCache; const uint32_t walletSerializationVersion; }; diff --git a/src/wallet/WalletRequest.h b/src/WalletLegacy/WalletRequest.h similarity index 89% rename from src/wallet/WalletRequest.h rename to src/WalletLegacy/WalletRequest.h index c87264533b..b29ee323d4 100644 --- a/src/wallet/WalletRequest.h +++ b/src/WalletLegacy/WalletRequest.h @@ -19,8 +19,8 @@ #include "INode.h" // #include "WalletSynchronizationContext.h" -#include "WalletSendTransactionContext.h" -#include "WalletEvent.h" +#include "WalletLegacy/WalletSendTransactionContext.h" +#include "WalletLegacy/WalletLegacyEvent.h" #include @@ -33,7 +33,7 @@ namespace CryptoNote { class WalletRequest { public: - typedef std::function >& events, boost::optional >& nextRequest, std::error_code ec)> Callback; + typedef std::function>& events, boost::optional >& nextRequest, std::error_code ec)> Callback; virtual ~WalletRequest() {}; diff --git a/src/wallet/WalletSendTransactionContext.h b/src/WalletLegacy/WalletSendTransactionContext.h old mode 100644 new mode 100755 similarity index 95% rename from src/wallet/WalletSendTransactionContext.h rename to src/WalletLegacy/WalletSendTransactionContext.h index 2fe65d8fd8..0066c14b3e --- a/src/wallet/WalletSendTransactionContext.h +++ b/src/WalletLegacy/WalletSendTransactionContext.h @@ -20,8 +20,8 @@ #include #include -#include "cryptonote_core/cryptonote_basic.h" -#include "IWallet.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "IWalletLegacy.h" #include "ITransfersContainer.h" namespace CryptoNote { diff --git a/src/wallet/WalletTransactionSender.cpp b/src/WalletLegacy/WalletTransactionSender.cpp similarity index 62% rename from src/wallet/WalletTransactionSender.cpp rename to src/WalletLegacy/WalletTransactionSender.cpp index 53eee3b64e..72d0c10c57 100644 --- a/src/wallet/WalletTransactionSender.cpp +++ b/src/WalletLegacy/WalletTransactionSender.cpp @@ -15,61 +15,60 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "cryptonote_core/account.h" -#include "cryptonote_core/cryptonote_format_utils.h" +#include "crypto/crypto.h" //for rand() +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/CryptoNoteFormatUtils.h" +#include "CryptoNoteCore/CryptoNoteTools.h" -#include "WalletTransactionSender.h" -#include "WalletUtils.h" +#include "WalletLegacy/WalletTransactionSender.h" +#include "WalletLegacy/WalletUtils.h" -#include "cryptonote_core/cryptonote_basic_impl.h" +#include "CryptoNoteCore/CryptoNoteBasicImpl.h" #include #include +using namespace Crypto; + namespace { using namespace CryptoNote; -uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { +uint64_t countNeededMoney(uint64_t fee, const std::vector& transfers) { uint64_t needed_money = fee; for (auto& transfer: transfers) { - CryptoNote::throwIf(transfer.amount == 0, CryptoNote::error::ZERO_DESTINATION); - CryptoNote::throwIf(transfer.amount < 0, CryptoNote::error::WRONG_AMOUNT); + throwIf(transfer.amount == 0, error::ZERO_DESTINATION); + throwIf(transfer.amount < 0, error::WRONG_AMOUNT); needed_money += transfer.amount; - CryptoNote::throwIf(static_cast(needed_money) < transfer.amount, CryptoNote::error::SUM_OVERFLOW); + throwIf(static_cast(needed_money) < transfer.amount, error::SUM_OVERFLOW); } return needed_money; } -void createChangeDestinations(const CryptoNote::AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, CryptoNote::tx_destination_entry& changeDts) { +void createChangeDestinations(const AccountPublicAddress& address, uint64_t neededMoney, uint64_t foundMoney, TransactionDestinationEntry& changeDts) { if (neededMoney < foundMoney) { changeDts.addr = address; changeDts.amount = foundMoney - neededMoney; } } -void constructTx(const CryptoNote::account_keys keys, const std::vector& sources, const std::vector& splittedDests, - const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, CryptoNote::Transaction& tx) { +void constructTx(const AccountKeys keys, const std::vector& sources, const std::vector& splittedDests, + const std::string& extra, uint64_t unlockTimestamp, uint64_t sizeLimit, Transaction& tx) { std::vector extraVec; extraVec.reserve(extra.size()); std::for_each(extra.begin(), extra.end(), [&extraVec] (const char el) { extraVec.push_back(el);}); Logging::LoggerGroup nullLog; - bool r = CryptoNote::construct_tx(keys, sources, splittedDests, extraVec, tx, unlockTimestamp, nullLog); - - CryptoNote::throwIf(!r, CryptoNote::error::INTERNAL_WALLET_ERROR); - CryptoNote::throwIf(CryptoNote::get_object_blobsize(tx) >= sizeLimit, CryptoNote::error::TRANSACTION_SIZE_TOO_BIG); -} + bool r = constructTransaction(keys, sources, splittedDests, extraVec, tx, unlockTimestamp, nullLog); -void fillTransactionHash(const CryptoNote::Transaction& tx, CryptoNote::TransactionHash& hash) { - crypto::hash h = CryptoNote::get_transaction_hash(tx); - memcpy(hash.data(), reinterpret_cast(&h), hash.size()); + throwIf(!r, error::INTERNAL_WALLET_ERROR); + throwIf(getObjectBinarySize(tx) >= sizeLimit, error::TRANSACTION_SIZE_TOO_BIG); } -std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { +std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& transactionCache, size_t transactionId, std::error_code ec) { transactionCache.updateTransactionSendingState(transactionId, ec); return std::make_shared(transactionId, ec); } @@ -78,7 +77,7 @@ std::shared_ptr makeCompleteEvent(WalletUserTransactionsCache& tran namespace CryptoNote { -WalletTransactionSender::WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer) : +WalletTransactionSender::WalletTransactionSender(const Currency& currency, WalletUserTransactionsCache& transactionsCache, AccountKeys keys, ITransfersContainer& transfersContainer) : m_currency(currency), m_transactionsCache(transactionsCache), m_isStoping(false), @@ -91,31 +90,31 @@ void WalletTransactionSender::stop() { } bool WalletTransactionSender::validateDestinationAddress(const std::string& address) { - CryptoNote::AccountPublicAddress ignore; + AccountPublicAddress ignore; return m_currency.parseAccountAddressString(address, ignore); } -void WalletTransactionSender::validateTransfersAddresses(const std::vector& transfers) { - for (const Transfer& tr: transfers) { +void WalletTransactionSender::validateTransfersAddresses(const std::vector& transfers) { + for (const WalletLegacyTransfer& tr : transfers) { if (!validateDestinationAddress(tr.address)) { - throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + throw std::system_error(make_error_code(error::BAD_ADDRESS)); } } } -std::shared_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque >& events, - const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { +std::shared_ptr WalletTransactionSender::makeSendRequest(TransactionId& transactionId, std::deque>& events, + const std::vector& transfers, uint64_t fee, const std::string& extra, uint64_t mixIn, uint64_t unlockTimestamp) { using namespace CryptoNote; - throwIf(transfers.empty(), CryptoNote::error::ZERO_DESTINATION); + throwIf(transfers.empty(), error::ZERO_DESTINATION); validateTransfersAddresses(transfers); uint64_t neededMoney = countNeededMoney(fee, transfers); std::shared_ptr context = std::make_shared(); context->foundMoney = selectTransfersToSend(neededMoney, 0 == mixIn, context->dustPolicy.dustThreshold, context->selectedTransfers); - throwIf(context->foundMoney < neededMoney, CryptoNote::error::WRONG_AMOUNT); + throwIf(context->foundMoney < neededMoney, error::WRONG_AMOUNT); transactionId = m_transactionsCache.addNewTransaction(neededMoney, fee, extra, transfers, unlockTimestamp); context->transactionId = transactionId; @@ -141,11 +140,11 @@ std::shared_ptr WalletTransactionSender::makeGetRandomOutsRequest this, context, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } -void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, +void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque>& events, boost::optional >& nextRequest, std::error_code ec) { if (m_isStoping) { - ec = make_error_code(CryptoNote::error::TX_CANCELLED); + ec = make_error_code(error::TX_CANCELLED); } if (ec) { @@ -154,10 +153,10 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< } auto scanty_it = std::find_if(context->outs.begin(), context->outs.end(), - [&] (CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); + [&] (COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& out) {return out.outs.size() < context->mixIn;}); if (scanty_it != context->outs.end()) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::MIXIN_COUNT_TOO_BIG))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(error::MIXIN_COUNT_TOO_BIG))); return; } @@ -166,31 +165,31 @@ void WalletTransactionSender::sendTransactionRandomOutsByAmount(std::shared_ptr< nextRequest = req; } -std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque >& events) { +std::shared_ptr WalletTransactionSender::doSendTransaction(std::shared_ptr context, std::deque>& events) { if (m_isStoping) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::TX_CANCELLED))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(error::TX_CANCELLED))); return std::shared_ptr(); } try { - TransactionInfo& transaction = m_transactionsCache.getTransaction(context->transactionId); + WalletLegacyTransaction& transaction = m_transactionsCache.getTransaction(context->transactionId); - std::vector sources; + std::vector sources; prepareInputs(context->selectedTransfers, context->outs, sources, context->mixIn); - CryptoNote::tx_destination_entry changeDts; + TransactionDestinationEntry changeDts; changeDts.amount = 0; uint64_t totalAmount = -transaction.totalAmount; - createChangeDestinations(m_keys.m_account_address, totalAmount, context->foundMoney, changeDts); + createChangeDestinations(m_keys.address, totalAmount, context->foundMoney, changeDts); - std::vector splittedDests; + std::vector splittedDests; splitDestinations(transaction.firstTransferId, transaction.transferCount, changeDts, context->dustPolicy, splittedDests); - CryptoNote::Transaction tx; + Transaction tx; constructTx(m_keys, sources, splittedDests, transaction.extra, transaction.unlockTime, m_upperTransactionSizeLimit, tx); - fillTransactionHash(tx, transaction.hash); + getObjectHash(tx, transaction.hash); m_transactionsCache.updateTransaction(context->transactionId, tx, totalAmount, context->selectedTransfers); @@ -203,14 +202,14 @@ std::shared_ptr WalletTransactionSender::doSendTransaction(std::s events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, ec.code())); } catch(std::exception&) { - events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR))); + events.push_back(makeCompleteEvent(m_transactionsCache, context->transactionId, make_error_code(error::INTERNAL_WALLET_ERROR))); } return std::shared_ptr(); } -void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque >& events, - boost::optional >& nextRequest, std::error_code ec) { +void WalletTransactionSender::relayTransactionCallback(std::shared_ptr context, std::deque>& events, + boost::optional >& nextRequest, std::error_code ec) { if (m_isStoping) { return; } @@ -219,65 +218,66 @@ void WalletTransactionSender::relayTransactionCallback(std::shared_ptr& splittedDests) { +void WalletTransactionSender::splitDestinations(TransferId firstTransferId, size_t transfersCount, const TransactionDestinationEntry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests) { uint64_t dust = 0; digitSplitStrategy(firstTransferId, transfersCount, changeDts, dustPolicy.dustThreshold, splittedDests, dust); - throwIf(dustPolicy.dustThreshold < dust, CryptoNote::error::INTERNAL_WALLET_ERROR); + throwIf(dustPolicy.dustThreshold < dust, error::INTERNAL_WALLET_ERROR); if (0 != dust && !dustPolicy.addToFee) { - splittedDests.push_back(CryptoNote::tx_destination_entry(dust, dustPolicy.addrForDust)); + splittedDests.push_back(TransactionDestinationEntry(dust, dustPolicy.addrForDust)); } } void WalletTransactionSender::digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, - const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust) { + const TransactionDestinationEntry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust) { splitted_dsts.clear(); dust = 0; for (TransferId idx = firstTransferId; idx < firstTransferId + transfersCount; ++idx) { - Transfer& de = m_transactionsCache.getTransfer(idx); + WalletLegacyTransfer& de = m_transactionsCache.getTransfer(idx); - CryptoNote::AccountPublicAddress addr; + AccountPublicAddress addr; if (!m_currency.parseAccountAddressString(de.address, addr)) { - throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS)); + throw std::system_error(make_error_code(error::BAD_ADDRESS)); } - CryptoNote::decompose_amount_into_digits(de.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, addr)); }, - [&](uint64_t a_dust) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(a_dust, addr)); } ); + decompose_amount_into_digits(de.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(TransactionDestinationEntry(chunk, addr)); }, + [&](uint64_t a_dust) { splitted_dsts.push_back(TransactionDestinationEntry(a_dust, addr)); }); } - CryptoNote::decompose_amount_into_digits(change_dst.amount, dust_threshold, - [&](uint64_t chunk) { splitted_dsts.push_back(CryptoNote::tx_destination_entry(chunk, change_dst.addr)); }, + decompose_amount_into_digits(change_dst.amount, dust_threshold, + [&](uint64_t chunk) { splitted_dsts.push_back(TransactionDestinationEntry(chunk, change_dst.addr)); }, [&](uint64_t a_dust) { dust = a_dust; } ); } void WalletTransactionSender::prepareInputs( const std::list& selectedTransfers, - std::vector& outs, - std::vector& sources, uint64_t mixIn) { + std::vector& outs, + std::vector& sources, uint64_t mixIn) { size_t i = 0; for (const auto& td: selectedTransfers) { sources.resize(sources.size()+1); - CryptoNote::tx_source_entry& src = sources.back(); + TransactionSourceEntry& src = sources.back(); src.amount = td.amount; //paste mixin transaction if(outs.size()) { - outs[i].outs.sort([](const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const CryptoNote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); + std::sort(outs[i].outs.begin(), outs[i].outs.end(), + [](const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& a, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& b){return a.global_amount_index < b.global_amount_index;}); for (auto& daemon_oe: outs[i].outs) { if(td.globalOutputIndex == daemon_oe.global_amount_index) continue; - CryptoNote::tx_source_entry::output_entry oe; - oe.first = daemon_oe.global_amount_index; + TransactionSourceEntry::OutputEntry oe; + oe.first = static_cast(daemon_oe.global_amount_index); oe.second = daemon_oe.out_key; src.outputs.push_back(oe); if(src.outputs.size() >= mixIn) @@ -286,22 +286,22 @@ void WalletTransactionSender::prepareInputs( } //paste real transaction to the random index - auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const CryptoNote::tx_source_entry::output_entry& a) { return a.first >= td.globalOutputIndex; }); + auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const TransactionSourceEntry::OutputEntry& a) { return a.first >= td.globalOutputIndex; }); - CryptoNote::tx_source_entry::output_entry real_oe; + TransactionSourceEntry::OutputEntry real_oe; real_oe.first = td.globalOutputIndex; - real_oe.second = reinterpret_cast(td.outputKey); + real_oe.second = td.outputKey; auto interted_it = src.outputs.insert(it_to_insert, real_oe); - src.real_out_tx_key = reinterpret_cast(td.transactionPublicKey); - src.real_output = interted_it - src.outputs.begin(); - src.real_output_in_tx_index = td.outputInTransaction; + src.realTransactionPublicKey = td.transactionPublicKey; + src.realOutput = interted_it - src.outputs.begin(); + src.realOutputIndexInTransaction = td.outputInTransaction; ++i; } } -void WalletTransactionSender::notifyBalanceChanged(std::deque >& events) { +void WalletTransactionSender::notifyBalanceChanged(std::deque>& events) { uint64_t unconfirmedOutsAmount = m_transactionsCache.unconfrimedOutsAmount(); uint64_t change = unconfirmedOutsAmount - m_transactionsCache.unconfirmedTransactionsAmount(); @@ -355,7 +355,7 @@ uint64_t WalletTransactionSender::selectTransfersToSend(uint64_t neededMoney, bo } } - std::default_random_engine randomGenerator(crypto::rand()); + std::default_random_engine randomGenerator(Crypto::rand()); bool selectOneDust = addDust && !unusedDust.empty(); uint64_t foundMoney = 0; diff --git a/src/wallet/WalletTransactionSender.h b/src/WalletLegacy/WalletTransactionSender.h old mode 100644 new mode 100755 similarity index 56% rename from src/wallet/WalletTransactionSender.h rename to src/WalletLegacy/WalletTransactionSender.h index 4ea01a59e7..97fdae4f99 --- a/src/wallet/WalletTransactionSender.h +++ b/src/WalletLegacy/WalletTransactionSender.h @@ -17,14 +17,14 @@ #pragma once -#include "cryptonote_core/account.h" -#include "cryptonote_core/Currency.h" +#include "CryptoNoteCore/Account.h" +#include "CryptoNoteCore/Currency.h" #include "INode.h" -#include "WalletSendTransactionContext.h" -#include "WalletUserTransactionsCache.h" -#include "WalletUnconfirmedTransactions.h" -#include "WalletRequest.h" +#include "WalletLegacy/WalletSendTransactionContext.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" +#include "WalletLegacy/WalletRequest.h" #include "ITransfersContainer.h" @@ -33,36 +33,35 @@ namespace CryptoNote { class WalletTransactionSender { public: - WalletTransactionSender(const CryptoNote::Currency& currency, WalletUserTransactionsCache& transactionsCache, CryptoNote::account_keys keys, ITransfersContainer& transfersContainer); + WalletTransactionSender(const Currency& currency, WalletUserTransactionsCache& transactionsCache, AccountKeys keys, ITransfersContainer& transfersContainer); - void init(CryptoNote::account_keys keys, ITransfersContainer& transfersContainer); void stop(); - std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque >& events, const std::vector& transfers, - uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); + std::shared_ptr makeSendRequest(TransactionId& transactionId, std::deque>& events, + const std::vector& transfers, uint64_t fee, const std::string& extra = "", uint64_t mixIn = 0, uint64_t unlockTimestamp = 0); private: std::shared_ptr makeGetRandomOutsRequest(std::shared_ptr context); - std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque >& events); - void prepareInputs(const std::list& selectedTransfers, std::vector& outs, - std::vector& sources, uint64_t mixIn); - void splitDestinations(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& changeDts, - const TxDustPolicy& dustPolicy, std::vector& splittedDests); - void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const CryptoNote::tx_destination_entry& change_dst, uint64_t dust_threshold, - std::vector& splitted_dsts, uint64_t& dust); - void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque >& events, + std::shared_ptr doSendTransaction(std::shared_ptr context, std::deque>& events); + void prepareInputs(const std::list& selectedTransfers, std::vector& outs, + std::vector& sources, uint64_t mixIn); + void splitDestinations(TransferId firstTransferId, size_t transfersCount, const TransactionDestinationEntry& changeDts, + const TxDustPolicy& dustPolicy, std::vector& splittedDests); + void digitSplitStrategy(TransferId firstTransferId, size_t transfersCount, const TransactionDestinationEntry& change_dst, uint64_t dust_threshold, + std::vector& splitted_dsts, uint64_t& dust); + void sendTransactionRandomOutsByAmount(std::shared_ptr context, std::deque>& events, boost::optional >& nextRequest, std::error_code ec); - void relayTransactionCallback(std::shared_ptr context, std::deque >& events, + void relayTransactionCallback(std::shared_ptr context, std::deque>& events, boost::optional >& nextRequest, std::error_code ec); - void notifyBalanceChanged(std::deque >& events); + void notifyBalanceChanged(std::deque>& events); - void validateTransfersAddresses(const std::vector& transfers); + void validateTransfersAddresses(const std::vector& transfers); bool validateDestinationAddress(const std::string& address); uint64_t selectTransfersToSend(uint64_t neededMoney, bool addDust, uint64_t dust, std::list& selectedTransfers); - const CryptoNote::Currency& m_currency; - CryptoNote::account_keys m_keys; + const Currency& m_currency; + AccountKeys m_keys; WalletUserTransactionsCache& m_transactionsCache; uint64_t m_upperTransactionSizeLimit; diff --git a/src/wallet/WalletUnconfirmedTransactions.cpp b/src/WalletLegacy/WalletUnconfirmedTransactions.cpp old mode 100644 new mode 100755 similarity index 77% rename from src/wallet/WalletUnconfirmedTransactions.cpp rename to src/WalletLegacy/WalletUnconfirmedTransactions.cpp index da528c7d87..ebd9a60ad9 --- a/src/wallet/WalletUnconfirmedTransactions.cpp +++ b/src/WalletLegacy/WalletUnconfirmedTransactions.cpp @@ -16,11 +16,13 @@ // along with Bytecoin. If not, see . #include "WalletUnconfirmedTransactions.h" -#include "WalletSerialization.h" +#include "WalletLegacy/WalletLegacySerialization.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "CryptoNoteCore/CryptoNoteTools.h" +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" + +using namespace Crypto; namespace CryptoNote { @@ -28,15 +30,15 @@ inline TransactionOutputId getOutputId(const TransactionOutputInformation& out) return std::make_pair(out.transactionPublicKey, out.outputInTransaction); } -bool WalletUnconfirmedTransactions::serialize(CryptoNote::ISerializer& s) { +bool WalletUnconfirmedTransactions::serialize(ISerializer& s) { s(m_unconfirmedTxs, "transactions"); - if (s.type() == CryptoNote::ISerializer::INPUT) { + if (s.type() == ISerializer::INPUT) { collectUsedOutputs(); } return true; } -bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& hash, TransactionId& id) { +bool WalletUnconfirmedTransactions::findTransactionId(const Hash& hash, TransactionId& id) { auto it = m_unconfirmedTxs.find(hash); if (it == m_unconfirmedTxs.end()) { return false; @@ -46,7 +48,7 @@ bool WalletUnconfirmedTransactions::findTransactionId(const TransactionHash& has return true; } -void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { +void WalletUnconfirmedTransactions::erase(const Hash& hash) { auto it = m_unconfirmedTxs.find(hash); if (it == m_unconfirmedTxs.end()) { return; @@ -58,13 +60,10 @@ void WalletUnconfirmedTransactions::erase(const TransactionHash& hash) { m_unconfirmedTxs.erase(it); } -void WalletUnconfirmedTransactions::add(const CryptoNote::Transaction& tx, TransactionId transactionId, +void WalletUnconfirmedTransactions::add(const Transaction& tx, TransactionId transactionId, uint64_t amount, const std::list& usedOutputs) { - auto cryptoHash = CryptoNote::get_transaction_hash(tx); - TransactionHash hash = reinterpret_cast(cryptoHash); - - UnconfirmedTransferDetails& utd = m_unconfirmedTxs[hash]; + UnconfirmedTransferDetails& utd = m_unconfirmedTxs[getObjectHash(tx)]; utd.amount = amount; utd.sentTime = time(nullptr); @@ -84,7 +83,7 @@ void WalletUnconfirmedTransactions::add(const CryptoNote::Transaction& tx, Trans utd.outsAmount = outsAmount; } -void WalletUnconfirmedTransactions::updateTransactionId(const TransactionHash& hash, TransactionId id) { +void WalletUnconfirmedTransactions::updateTransactionId(const Hash& hash, TransactionId id) { auto it = m_unconfirmedTxs.find(hash); if (it != m_unconfirmedTxs.end()) { it->second.transactionId = id; diff --git a/src/wallet/WalletUnconfirmedTransactions.h b/src/WalletLegacy/WalletUnconfirmedTransactions.h old mode 100644 new mode 100755 similarity index 67% rename from src/wallet/WalletUnconfirmedTransactions.h rename to src/WalletLegacy/WalletUnconfirmedTransactions.h index 0ccdf1c2cc..9fbb2929c1 --- a/src/wallet/WalletUnconfirmedTransactions.h +++ b/src/WalletLegacy/WalletUnconfirmedTransactions.h @@ -17,29 +17,41 @@ #pragma once -#include "IWallet.h" +#include "IWalletLegacy.h" #include "ITransfersContainer.h" #include -#include +#include #include #include -#include "crypto/hash.h" -#include "cryptonote_core/cryptonote_basic.h" +#include "CryptoNoteCore/CryptoNoteBasic.h" +#include "crypto/crypto.h" namespace CryptoNote { class ISerializer; + +typedef std::pair TransactionOutputId; +} + +namespace std { + +template<> +struct hash { + size_t operator()(const CryptoNote::TransactionOutputId &_v) const { + return hash()(_v.first) ^ _v.second; + } +}; + } namespace CryptoNote { -typedef std::pair TransactionOutputId; struct UnconfirmedTransferDetails { UnconfirmedTransferDetails() : - amount(0), sentTime(0), transactionId(INVALID_TRANSACTION_ID) {} + amount(0), sentTime(0), transactionId(WALLET_LEGACY_INVALID_TRANSACTION_ID) {} CryptoNote::Transaction tx; uint64_t amount; @@ -55,11 +67,11 @@ class WalletUnconfirmedTransactions bool serialize(CryptoNote::ISerializer& s); - bool findTransactionId(const TransactionHash& hash, TransactionId& id); - void erase(const TransactionHash& hash); + bool findTransactionId(const Crypto::Hash& hash, TransactionId& id); + void erase(const Crypto::Hash& hash); void add(const CryptoNote::Transaction& tx, TransactionId transactionId, uint64_t amount, const std::list& usedOutputs); - void updateTransactionId(const TransactionHash& hash, TransactionId id); + void updateTransactionId(const Crypto::Hash& hash, TransactionId id); uint64_t countUnconfirmedOutsAmount() const; uint64_t countUnconfirmedTransactionsAmount() const; @@ -70,8 +82,8 @@ class WalletUnconfirmedTransactions void collectUsedOutputs(); - typedef std::unordered_map> UnconfirmedTxsContainer; - typedef std::set UsedOutputsContainer; + typedef std::unordered_map> UnconfirmedTxsContainer; + typedef std::unordered_set UsedOutputsContainer; UnconfirmedTxsContainer m_unconfirmedTxs; UsedOutputsContainer m_usedOutputs; diff --git a/src/wallet/WalletUserTransactionsCache.cpp b/src/WalletLegacy/WalletUserTransactionsCache.cpp old mode 100644 new mode 100755 similarity index 70% rename from src/wallet/WalletUserTransactionsCache.cpp rename to src/WalletLegacy/WalletUserTransactionsCache.cpp index 2772988058..021e02f88d --- a/src/wallet/WalletUserTransactionsCache.cpp +++ b/src/WalletLegacy/WalletUserTransactionsCache.cpp @@ -15,18 +15,19 @@ // You should have received a copy of the GNU Lesser General Public License // along with Bytecoin. If not, see . -#include "WalletErrors.h" -#include "WalletUserTransactionsCache.h" -#include "WalletSerialization.h" -#include "WalletUtils.h" - -#include "serialization/ISerializer.h" -#include "serialization/SerializationOverloads.h" +#include "IWalletLegacy.h" +#include "Wallet/WalletErrors.h" +#include "WalletLegacy/WalletUserTransactionsCache.h" +#include "WalletLegacy/WalletLegacySerialization.h" +#include "WalletLegacy/WalletUtils.h" + +#include "Serialization/ISerializer.h" +#include "Serialization/SerializationOverloads.h" #include -namespace CryptoNote { - +using namespace Crypto; +namespace CryptoNote { bool WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s) { if (s.type() == CryptoNote::ISerializer::INPUT) { s(m_transactions, "transactions"); @@ -63,9 +64,9 @@ size_t WalletUserTransactionsCache::getTransferCount() const { } TransactionId WalletUserTransactionsCache::addNewTransaction( - uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime) { + uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime) { - TransactionInfo transaction; + WalletLegacyTransaction transaction; transaction.firstTransferId = insertTransfers(transfers); transaction.transferCount = transfers.size(); @@ -75,8 +76,8 @@ TransactionId WalletUserTransactionsCache::addNewTransaction( transaction.isCoinbase = false; transaction.timestamp = 0; transaction.extra = extra; - transaction.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; - transaction.state = TransactionState::Sending; + transaction.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; + transaction.state = WalletLegacyTransactionState::Sending; transaction.unlockTime = unlockTime; return insertTransaction(std::move(transaction)); @@ -93,19 +94,18 @@ void WalletUserTransactionsCache::updateTransaction( void WalletUserTransactionsCache::updateTransactionSendingState(TransactionId transactionId, std::error_code ec) { auto& txInfo = m_transactions.at(transactionId); if (ec) { - txInfo.state = ec.value() == error::TX_CANCELLED ? TransactionState::Cancelled : TransactionState::Failed; + txInfo.state = ec.value() == error::TX_CANCELLED ? WalletLegacyTransactionState::Cancelled : WalletLegacyTransactionState::Failed; m_unconfirmedTransactions.erase(txInfo.hash); } else { txInfo.sentTime = time(nullptr); // update sending time - txInfo.state = TransactionState::Active; + txInfo.state = WalletLegacyTransactionState::Active; } } -std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, - int64_t txBalance) { - std::shared_ptr event; +std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance) { + std::shared_ptr event; - TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; + TransactionId id = CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID; if (!m_unconfirmedTransactions.findTransactionId(txInfo.transactionHash, id)) { id = findTransactionByHash(txInfo.transactionHash); @@ -115,9 +115,9 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c bool isCoinbase = txInfo.totalAmountIn == 0; - if (id == CryptoNote::INVALID_TRANSACTION_ID) { - TransactionInfo transaction; - transaction.firstTransferId = INVALID_TRANSFER_ID; + if (id == CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID) { + WalletLegacyTransaction transaction; + transaction.firstTransferId = WALLET_LEGACY_INVALID_TRANSFER_ID; transaction.transferCount = 0; transaction.totalAmount = txBalance; transaction.fee = isCoinbase ? 0 : txInfo.totalAmountIn - txInfo.totalAmountOut; @@ -127,17 +127,17 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c transaction.isCoinbase = isCoinbase; transaction.timestamp = txInfo.timestamp; transaction.extra.assign(txInfo.extra.begin(), txInfo.extra.end()); - transaction.state = TransactionState::Active; + transaction.state = WalletLegacyTransactionState::Active; transaction.unlockTime = txInfo.unlockTime; id = insertTransaction(std::move(transaction)); // notification event event = std::make_shared(id); } else { - TransactionInfo& tr = getTransaction(id); + WalletLegacyTransaction& tr = getTransaction(id); tr.blockHeight = txInfo.blockHeight; tr.timestamp = txInfo.timestamp; - tr.state = TransactionState::Active; + tr.state = WalletLegacyTransactionState::Active; // notification event event = std::make_shared(id); } @@ -145,8 +145,8 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionUpdated(c return event; } -std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const TransactionHash& transactionHash) { - TransactionId id = CryptoNote::INVALID_TRANSACTION_ID; +std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(const Hash& transactionHash) { + TransactionId id = CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID; if (m_unconfirmedTransactions.findTransactionId(transactionHash, id)) { m_unconfirmedTransactions.erase(transactionHash); // LOG_ERROR("Unconfirmed transaction is deleted: id = " << id << ", hash = " << transactionHash); @@ -155,12 +155,12 @@ std::shared_ptr WalletUserTransactionsCache::onTransactionDeleted(c id = findTransactionByHash(transactionHash); } - std::shared_ptr event; - if (id != CryptoNote::INVALID_TRANSACTION_ID) { - TransactionInfo& tr = getTransaction(id); - tr.blockHeight = UNCONFIRMED_TRANSACTION_HEIGHT; + std::shared_ptr event; + if (id != CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID) { + WalletLegacyTransaction& tr = getTransaction(id); + tr.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; tr.timestamp = 0; - tr.state = TransactionState::Deleted; + tr.state = WalletLegacyTransactionState::Deleted; event = std::make_shared(id); } else { @@ -175,9 +175,9 @@ TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferI { TransactionId id; for (id = 0; id < m_transactions.size(); ++id) { - const TransactionInfo& tx = m_transactions[id]; + const WalletLegacyTransaction& tx = m_transactions[id]; - if (tx.firstTransferId == INVALID_TRANSFER_ID || tx.transferCount == 0) + if (tx.firstTransferId == WALLET_LEGACY_INVALID_TRANSFER_ID || tx.transferCount == 0) continue; if (transferId >= tx.firstTransferId && transferId < (tx.firstTransferId + tx.transferCount)) @@ -185,12 +185,12 @@ TransactionId WalletUserTransactionsCache::findTransactionByTransferId(TransferI } if (id == m_transactions.size()) - return INVALID_TRANSACTION_ID; + return WALLET_LEGACY_INVALID_TRANSACTION_ID; return id; } -bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, TransactionInfo& transaction) const +bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) const { if (transactionId >= m_transactions.size()) return false; @@ -200,7 +200,7 @@ bool WalletUserTransactionsCache::getTransaction(TransactionId transactionId, Tr return true; } -bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& transfer) const +bool WalletUserTransactionsCache::getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) const { if (transferId >= m_transfers.size()) return false; @@ -210,16 +210,16 @@ bool WalletUserTransactionsCache::getTransfer(TransferId transferId, Transfer& t return true; } -TransactionId WalletUserTransactionsCache::insertTransaction(TransactionInfo&& Transaction) { +TransactionId WalletUserTransactionsCache::insertTransaction(WalletLegacyTransaction&& Transaction) { m_transactions.emplace_back(std::move(Transaction)); return m_transactions.size() - 1; } -TransactionId WalletUserTransactionsCache::findTransactionByHash(const TransactionHash& hash) { - auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash] (const TransactionInfo& tx) { return tx.hash == hash; }); +TransactionId WalletUserTransactionsCache::findTransactionByHash(const Hash& hash) { + auto it = std::find_if(m_transactions.begin(), m_transactions.end(), [&hash](const WalletLegacyTransaction& tx) { return tx.hash == hash; }); if (it == m_transactions.end()) - return CryptoNote::INVALID_TRANSACTION_ID; + return CryptoNote::WALLET_LEGACY_INVALID_TRANSACTION_ID; return std::distance(m_transactions.begin(), it); } @@ -228,7 +228,7 @@ bool WalletUserTransactionsCache::isUsed(const TransactionOutputInformation& out return m_unconfirmedTransactions.isUsed(out); } -TransactionInfo& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { +WalletLegacyTransaction& WalletUserTransactionsCache::getTransaction(TransactionId transactionId) { return m_transactions.at(transactionId); } @@ -237,23 +237,23 @@ void WalletUserTransactionsCache::getGoodItems(UserTransactions& transactions, U for (size_t txId = 0; txId < m_transactions.size(); ++txId) { bool isGood = - m_transactions[txId].state != TransactionState::Cancelled && - m_transactions[txId].state != TransactionState::Failed; + m_transactions[txId].state != WalletLegacyTransactionState::Cancelled && + m_transactions[txId].state != WalletLegacyTransactionState::Failed; if (isGood) { getGoodTransaction(txId, offset, transactions, transfers); } else { - const TransactionInfo& t = m_transactions[txId]; - offset += t.firstTransferId != INVALID_TRANSFER_ID ? t.transferCount : 0; + const WalletLegacyTransaction& t = m_transactions[txId]; + offset += t.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID ? t.transferCount : 0; } } } void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t offset, UserTransactions& transactions, UserTransfers& transfers) { transactions.push_back(m_transactions[txId]); - TransactionInfo& tx = transactions.back(); + WalletLegacyTransaction& tx = transactions.back(); - if (tx.firstTransferId == INVALID_TRANSFER_ID) { + if (tx.firstTransferId == WALLET_LEGACY_INVALID_TRANSFER_ID) { return; } @@ -266,29 +266,29 @@ void WalletUserTransactionsCache::getGoodTransaction(TransactionId txId, size_t } void WalletUserTransactionsCache::getTransfersByTx(TransactionId id, UserTransfers& transfers) { - const TransactionInfo& tx = m_transactions[id]; + const WalletLegacyTransaction& tx = m_transactions[id]; - if (tx.firstTransferId != INVALID_TRANSFER_ID) { + if (tx.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID) { UserTransfers::const_iterator first = m_transfers.begin() + tx.firstTransferId; UserTransfers::const_iterator last = first + tx.transferCount; std::copy(first, last, std::back_inserter(transfers)); } } -TransferId WalletUserTransactionsCache::insertTransfers(const std::vector& transfers) { +TransferId WalletUserTransactionsCache::insertTransfers(const std::vector& transfers) { std::copy(transfers.begin(), transfers.end(), std::back_inserter(m_transfers)); return m_transfers.size() - transfers.size(); } void WalletUserTransactionsCache::updateUnconfirmedTransactions() { for (TransactionId id = 0; id < m_transactions.size(); ++id) { - if (m_transactions[id].blockHeight == UNCONFIRMED_TRANSACTION_HEIGHT) { + if (m_transactions[id].blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { m_unconfirmedTransactions.updateTransactionId(m_transactions[id].hash, id); } } } -Transfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { +WalletLegacyTransfer& WalletUserTransactionsCache::getTransfer(TransferId transferId) { return m_transfers.at(transferId); } diff --git a/src/wallet/WalletUserTransactionsCache.h b/src/WalletLegacy/WalletUserTransactionsCache.h old mode 100644 new mode 100755 similarity index 66% rename from src/wallet/WalletUserTransactionsCache.h rename to src/WalletLegacy/WalletUserTransactionsCache.h index 10faeca816..89754b7bf1 --- a/src/wallet/WalletUserTransactionsCache.h +++ b/src/WalletLegacy/WalletUserTransactionsCache.h @@ -18,11 +18,11 @@ #pragma once #include "crypto/hash.h" -#include "IWallet.h" +#include "IWalletLegacy.h" #include "ITransfersContainer.h" -#include "WalletEvent.h" -#include "WalletUnconfirmedTransactions.h" +#include "WalletLegacy/WalletLegacyEvent.h" +#include "WalletLegacy/WalletUnconfirmedTransactions.h" namespace CryptoNote { class ISerializer; @@ -42,32 +42,32 @@ class WalletUserTransactionsCache size_t getTransactionCount() const; size_t getTransferCount() const; - TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime); + TransactionId addNewTransaction(uint64_t amount, uint64_t fee, const std::string& extra, const std::vector& transfers, uint64_t unlockTime); void updateTransaction(TransactionId transactionId, const CryptoNote::Transaction& tx, uint64_t amount, const std::list& usedOutputs); void updateTransactionSendingState(TransactionId transactionId, std::error_code ec); - std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); - std::shared_ptr onTransactionDeleted(const TransactionHash& transactionHash); + std::shared_ptr onTransactionUpdated(const TransactionInformation& txInfo, int64_t txBalance); + std::shared_ptr onTransactionDeleted(const Crypto::Hash& transactionHash); TransactionId findTransactionByTransferId(TransferId transferId) const; - bool getTransaction(TransactionId transactionId, TransactionInfo& transaction) const; - TransactionInfo& getTransaction(TransactionId transactionId); - bool getTransfer(TransferId transferId, Transfer& transfer) const; - Transfer& getTransfer(TransferId transferId); + bool getTransaction(TransactionId transactionId, WalletLegacyTransaction& transaction) const; + WalletLegacyTransaction& getTransaction(TransactionId transactionId); + bool getTransfer(TransferId transferId, WalletLegacyTransfer& transfer) const; + WalletLegacyTransfer& getTransfer(TransferId transferId); bool isUsed(const TransactionOutputInformation& out) const; void reset(); private: - TransactionId findTransactionByHash(const TransactionHash& hash); - TransactionId insertTransaction(TransactionInfo&& Transaction); - TransferId insertTransfers(const std::vector& transfers); + TransactionId findTransactionByHash(const Crypto::Hash& hash); + TransactionId insertTransaction(WalletLegacyTransaction&& Transaction); + TransferId insertTransfers(const std::vector& transfers); void updateUnconfirmedTransactions(); - typedef std::vector UserTransfers; - typedef std::vector UserTransactions; + typedef std::vector UserTransfers; + typedef std::vector UserTransactions; void getGoodItems(UserTransactions& transactions, UserTransfers& transfers); void getGoodTransaction(TransactionId txId, size_t offset, UserTransactions& transactions, UserTransfers& transfers); diff --git a/src/wallet/WalletUtils.h b/src/WalletLegacy/WalletUtils.h old mode 100644 new mode 100755 similarity index 88% rename from src/wallet/WalletUtils.h rename to src/WalletLegacy/WalletUtils.h index 65c9dfb467..abdb04c972 --- a/src/wallet/WalletUtils.h +++ b/src/WalletLegacy/WalletUtils.h @@ -21,8 +21,8 @@ #include #include -#include "IWallet.h" -#include "WalletErrors.h" +#include "IWalletLegacy.h" +#include "Wallet/WalletErrors.h" namespace CryptoNote { @@ -32,11 +32,11 @@ inline void throwIf(bool expr, CryptoNote::error::WalletErrorCodes ec) throw std::system_error(make_error_code(ec)); } -inline std::ostream& operator <<(std::ostream& ostr, const TransactionHash& hash) { +inline std::ostream& operator <<(std::ostream& ostr, const Crypto::Hash& hash) { std::ios_base::fmtflags flags = ostr.setf(std::ios_base::hex, std::ios_base::basefield); char fill = ostr.fill('0'); - for (auto b : hash) { + for (auto b : hash.data) { ostr << std::setw(2) << static_cast(b); } diff --git a/src/crypto/chacha8.c b/src/crypto/chacha8.c old mode 100644 new mode 100755 diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h index 04ec5d7b7e..fca5df703c 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha8.h @@ -8,10 +8,11 @@ #if defined(__cplusplus) #include +#include #include "hash.h" -namespace crypto { +namespace Crypto { extern "C" { #endif void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); @@ -36,14 +37,14 @@ namespace crypto { static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); - inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { + inline void chacha8(const void* data, size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { chacha8(data, length, reinterpret_cast(&key), reinterpret_cast(&iv), cipher); } - inline void generate_chacha8_key(crypto::cn_context &context, std::string password, chacha8_key& key) { - static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); - crypto::hash pwd_hash; - crypto::cn_slow_hash(context, password.data(), password.size(), pwd_hash); + inline void generate_chacha8_key(Crypto::cn_context &context, const std::string& password, chacha8_key& key) { + static_assert(sizeof(chacha8_key) <= sizeof(Hash), "Size of hash must be at least that of chacha8_key"); + Hash pwd_hash; + cn_slow_hash(context, password.data(), password.size(), pwd_hash); memcpy(&key, &pwd_hash, sizeof(key)); memset(&pwd_hash, 0, sizeof(pwd_hash)); } diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp old mode 100644 new mode 100755 index efa724e1f1..7b2103c81f --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -24,17 +24,16 @@ #include #include -#include "Common/varint.h" +#include "Common/Varint.h" #include "crypto.h" #include "hash.h" -namespace crypto { +namespace Crypto { using std::abort; using std::int32_t; using std::lock_guard; using std::mutex; - using std::size_t; extern "C" { #include "crypto-ops.h" @@ -43,200 +42,286 @@ namespace crypto { mutex random_lock; - static inline unsigned char *operator &(ec_point &point) { - return &reinterpret_cast(point); - } - - static inline const unsigned char *operator &(const ec_point &point) { - return &reinterpret_cast(point); - } - - static inline unsigned char *operator &(ec_scalar &scalar) { - return &reinterpret_cast(scalar); - } - - static inline const unsigned char *operator &(const ec_scalar &scalar) { - return &reinterpret_cast(scalar); - } - - static inline void random_scalar(ec_scalar &res) { + static inline void random_scalar(EllipticCurveScalar &res) { unsigned char tmp[64]; generate_random_bytes(64, tmp); sc_reduce(tmp); memcpy(&res, tmp, 32); } - static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { - cn_fast_hash(data, length, reinterpret_cast(res)); - sc_reduce32(&res); + static inline void hash_to_scalar(const void *data, size_t length, EllipticCurveScalar &res) { + cn_fast_hash(data, length, reinterpret_cast(res)); + sc_reduce32(reinterpret_cast(&res)); } - void crypto_ops::generate_keys(public_key &pub, secret_key &sec) { + void crypto_ops::generate_keys(PublicKey &pub, SecretKey &sec) { lock_guard lock(random_lock); ge_p3 point; - random_scalar(sec); - ge_scalarmult_base(&point, &sec); - ge_p3_tobytes(&pub, &point); + random_scalar(reinterpret_cast(sec)); + ge_scalarmult_base(&point, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&pub), &point); } - bool crypto_ops::check_key(const public_key &key) { + bool crypto_ops::check_key(const PublicKey &key) { ge_p3 point; - return ge_frombytes_vartime(&point, &key) == 0; + return ge_frombytes_vartime(&point, reinterpret_cast(&key)) == 0; } - bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) { + bool crypto_ops::secret_key_to_public_key(const SecretKey &sec, PublicKey &pub) { ge_p3 point; - if (sc_check(&sec) != 0) { + if (sc_check(reinterpret_cast(&sec)) != 0) { return false; } - ge_scalarmult_base(&point, &sec); - ge_p3_tobytes(&pub, &point); + ge_scalarmult_base(&point, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&pub), &point); return true; } - bool crypto_ops::generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + bool crypto_ops::generate_key_derivation(const PublicKey &key1, const SecretKey &key2, KeyDerivation &derivation) { ge_p3 point; ge_p2 point2; ge_p1p1 point3; - assert(sc_check(&key2) == 0); - if (ge_frombytes_vartime(&point, &key1) != 0) { + assert(sc_check(reinterpret_cast(&key2)) == 0); + if (ge_frombytes_vartime(&point, reinterpret_cast(&key1)) != 0) { return false; } - ge_scalarmult(&point2, &key2, &point); + ge_scalarmult(&point2, reinterpret_cast(&key2), &point); ge_mul8(&point3, &point2); ge_p1p1_to_p2(&point2, &point3); - ge_tobytes(&derivation, &point2); + ge_tobytes(reinterpret_cast(&derivation), &point2); return true; } - static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { + static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_index, EllipticCurveScalar &res) { struct { - key_derivation derivation; + KeyDerivation derivation; char output_index[(sizeof(size_t) * 8 + 6) / 7]; } buf; char *end = buf.output_index; buf.derivation = derivation; - tools::write_varint(end, output_index); + Tools::write_varint(end, output_index); assert(end <= buf.output_index + sizeof buf.output_index); hash_to_scalar(&buf, end - reinterpret_cast(&buf), res); } - bool crypto_ops::derive_public_key(const key_derivation &derivation, size_t output_index, - const public_key &base, public_key &derived_key) { - ec_scalar scalar; + static void derivation_to_scalar(const KeyDerivation &derivation, size_t output_index, const uint8_t* suffix, size_t suffixLength, EllipticCurveScalar &res) { + assert(suffixLength <= 32); + struct { + KeyDerivation derivation; + char output_index[(sizeof(size_t) * 8 + 6) / 7 + 32]; + } buf; + char *end = buf.output_index; + buf.derivation = derivation; + Tools::write_varint(end, output_index); + assert(end <= buf.output_index + sizeof buf.output_index); + size_t bufSize = end - reinterpret_cast(&buf); + memcpy(end, suffix, suffixLength); + hash_to_scalar(&buf, bufSize + suffixLength, res); + } + + bool crypto_ops::derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, PublicKey &derived_key) { + EllipticCurveScalar scalar; ge_p3 point1; ge_p3 point2; ge_cached point3; ge_p1p1 point4; ge_p2 point5; - if (ge_frombytes_vartime(&point1, &base) != 0) { + if (ge_frombytes_vartime(&point1, reinterpret_cast(&base)) != 0) { return false; } derivation_to_scalar(derivation, output_index, scalar); - ge_scalarmult_base(&point2, &scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); + ge_p3_to_cached(&point3, &point2); + ge_add(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(reinterpret_cast(&derived_key), &point5); + return true; + } + + bool crypto_ops::derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, const uint8_t* suffix, size_t suffixLength, PublicKey &derived_key) { + EllipticCurveScalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, reinterpret_cast(&base)) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, suffix, suffixLength, scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); ge_p3_to_cached(&point3, &point2); ge_add(&point4, &point1, &point3); ge_p1p1_to_p2(&point5, &point4); - ge_tobytes(&derived_key, &point5); + ge_tobytes(reinterpret_cast(&derived_key), &point5); return true; } - void crypto_ops::derive_secret_key(const key_derivation &derivation, size_t output_index, - const secret_key &base, secret_key &derived_key) { - ec_scalar scalar; - assert(sc_check(&base) == 0); + bool crypto_ops::underive_public_key_and_get_scalar(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, PublicKey &base, EllipticCurveScalar &hashed_derivation) { + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, reinterpret_cast(&derived_key)) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, hashed_derivation); + ge_scalarmult_base(&point2, reinterpret_cast(&hashed_derivation)); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(reinterpret_cast(&base), &point5); + return true; + } + + void crypto_ops::derive_secret_key(const KeyDerivation &derivation, size_t output_index, + const SecretKey &base, SecretKey &derived_key) { + EllipticCurveScalar scalar; + assert(sc_check(reinterpret_cast(&base)) == 0); derivation_to_scalar(derivation, output_index, scalar); - sc_add(&derived_key, &base, &scalar); + sc_add(reinterpret_cast(&derived_key), reinterpret_cast(&base), reinterpret_cast(&scalar)); + } + + void crypto_ops::derive_secret_key(const KeyDerivation &derivation, size_t output_index, + const SecretKey &base, const uint8_t* suffix, size_t suffixLength, SecretKey &derived_key) { + EllipticCurveScalar scalar; + assert(sc_check(reinterpret_cast(&base)) == 0); + derivation_to_scalar(derivation, output_index, suffix, suffixLength, scalar); + sc_add(reinterpret_cast(&derived_key), reinterpret_cast(&base), reinterpret_cast(&scalar)); } - bool crypto_ops::underive_public_key(const key_derivation &derivation, size_t output_index, - const public_key &derived_key, public_key &base) { - ec_scalar scalar; + + bool crypto_ops::underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, PublicKey &base) { + EllipticCurveScalar scalar; ge_p3 point1; ge_p3 point2; ge_cached point3; ge_p1p1 point4; ge_p2 point5; - if (ge_frombytes_vartime(&point1, &derived_key) != 0) { + if (ge_frombytes_vartime(&point1, reinterpret_cast(&derived_key)) != 0) { return false; } derivation_to_scalar(derivation, output_index, scalar); - ge_scalarmult_base(&point2, &scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); ge_p3_to_cached(&point3, &point2); ge_sub(&point4, &point1, &point3); ge_p1p1_to_p2(&point5, &point4); - ge_tobytes(&base, &point5); + ge_tobytes(reinterpret_cast(&base), &point5); return true; } + bool crypto_ops::underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, const uint8_t* suffix, size_t suffixLength, PublicKey &base) { + EllipticCurveScalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, reinterpret_cast(&derived_key)) != 0) { + return false; + } + + derivation_to_scalar(derivation, output_index, suffix, suffixLength, scalar); + ge_scalarmult_base(&point2, reinterpret_cast(&scalar)); + ge_p3_to_cached(&point3, &point2); + ge_sub(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(reinterpret_cast(&base), &point5); + return true; + } + + struct s_comm { - hash h; - ec_point key; - ec_point comm; + Hash h; + EllipticCurvePoint key; + EllipticCurvePoint comm; }; - void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + void crypto_ops::generate_signature(const Hash &prefix_hash, const PublicKey &pub, const SecretKey &sec, Signature &sig) { lock_guard lock(random_lock); ge_p3 tmp3; - ec_scalar k; + EllipticCurveScalar k; s_comm buf; #if !defined(NDEBUG) { ge_p3 t; - public_key t2; - assert(sc_check(&sec) == 0); - ge_scalarmult_base(&t, &sec); - ge_p3_tobytes(&t2, &t); + PublicKey t2; + assert(sc_check(reinterpret_cast(&sec)) == 0); + ge_scalarmult_base(&t, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&t2), &t); assert(pub == t2); } #endif buf.h = prefix_hash; - buf.key = pub; + buf.key = reinterpret_cast(pub); random_scalar(k); - ge_scalarmult_base(&tmp3, &k); - ge_p3_tobytes(&buf.comm, &tmp3); - hash_to_scalar(&buf, sizeof(s_comm), sig.c); - sc_mulsub(&sig.r, &sig.c, &sec, &k); + ge_scalarmult_base(&tmp3, reinterpret_cast(&k)); + ge_p3_tobytes(reinterpret_cast(&buf.comm), &tmp3); + hash_to_scalar(&buf, sizeof(s_comm), reinterpret_cast(sig)); + sc_mulsub(reinterpret_cast(&sig) + 32, reinterpret_cast(&sig), reinterpret_cast(&sec), reinterpret_cast(&k)); } - bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { + bool crypto_ops::check_signature(const Hash &prefix_hash, const PublicKey &pub, const Signature &sig) { ge_p2 tmp2; ge_p3 tmp3; - ec_scalar c; + EllipticCurveScalar c; s_comm buf; assert(check_key(pub)); buf.h = prefix_hash; - buf.key = pub; - if (ge_frombytes_vartime(&tmp3, &pub) != 0) { + buf.key = reinterpret_cast(pub); + if (ge_frombytes_vartime(&tmp3, reinterpret_cast(&pub)) != 0) { abort(); } - if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) { + if (sc_check(reinterpret_cast(&sig)) != 0 || sc_check(reinterpret_cast(&sig) + 32) != 0) { return false; } - ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r); - ge_tobytes(&buf.comm, &tmp2); + ge_double_scalarmult_base_vartime(&tmp2, reinterpret_cast(&sig), &tmp3, reinterpret_cast(&sig) + 32); + ge_tobytes(reinterpret_cast(&buf.comm), &tmp2); hash_to_scalar(&buf, sizeof(s_comm), c); - sc_sub(&c, &c, &sig.c); - return sc_isnonzero(&c) == 0; + sc_sub(reinterpret_cast(&c), reinterpret_cast(&c), reinterpret_cast(&sig)); + return sc_isnonzero(reinterpret_cast(&c)) == 0; } - static void hash_to_ec(const public_key &key, ge_p3 &res) { - hash h; + static void hash_to_ec(const PublicKey &key, ge_p3 &res) { + Hash h; ge_p2 point; ge_p1p1 point2; - cn_fast_hash(std::addressof(key), sizeof(public_key), h); + cn_fast_hash(std::addressof(key), sizeof(PublicKey), h); ge_fromfe_frombytes_vartime(&point, reinterpret_cast(&h)); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); } - void crypto_ops::generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { + void crypto_ops::hash_data_to_ec(const uint8_t* data, std::size_t len, PublicKey& key) { + Hash h; + ge_p2 point; + ge_p1p1 point2; + cn_fast_hash(data, len, h); + ge_fromfe_frombytes_vartime(&point, reinterpret_cast(&h)); + ge_mul8(&point2, &point); + ge_p1p1_to_p2(&point, &point2); + ge_tobytes(reinterpret_cast(&key), &point); + } + + void crypto_ops::generate_key_image(const PublicKey &pub, const SecretKey &sec, KeyImage &image) { ge_p3 point; ge_p2 point2; - assert(sc_check(&sec) == 0); + assert(sc_check(reinterpret_cast(&sec)) == 0); + hash_to_ec(pub, point); + ge_scalarmult(&point2, reinterpret_cast(&sec), &point); + ge_tobytes(reinterpret_cast(&image), &point2); + } + + void crypto_ops::generate_incomplete_key_image(const PublicKey &pub, EllipticCurvePoint &incomplete_key_image) { + ge_p3 point; hash_to_ec(pub, point); - ge_scalarmult(&point2, &sec, &point); - ge_tobytes(&image, &point2); + ge_p3_tobytes(reinterpret_cast(&incomplete_key_image), &point); } #ifdef _MSC_VER @@ -244,9 +329,9 @@ namespace crypto { #endif struct rs_comm { - hash h; + Hash h; struct { - ec_point a, b; + EllipticCurvePoint a, b; } ab[]; }; @@ -254,25 +339,25 @@ namespace crypto { return sizeof(rs_comm) + pubs_count * sizeof(rs_comm().ab[0]); } - void crypto_ops::generate_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, size_t pubs_count, - const secret_key &sec, size_t sec_index, - signature *sig) { + void crypto_ops::generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, size_t pubs_count, + const SecretKey &sec, size_t sec_index, + Signature *sig) { lock_guard lock(random_lock); size_t i; ge_p3 image_unp; ge_dsmp image_pre; - ec_scalar sum, k, h; + EllipticCurveScalar sum, k, h; rs_comm *const buf = reinterpret_cast(alloca(rs_comm_size(pubs_count))); assert(sec_index < pubs_count); #if !defined(NDEBUG) { ge_p3 t; - public_key t2; - key_image t3; - assert(sc_check(&sec) == 0); - ge_scalarmult_base(&t, &sec); - ge_p3_tobytes(&t2, &t); + PublicKey t2; + KeyImage t3; + assert(sc_check(reinterpret_cast(&sec)) == 0); + ge_scalarmult_base(&t, reinterpret_cast(&sec)); + ge_p3_tobytes(reinterpret_cast(&t2), &t); assert(*pubs[sec_index] == t2); generate_key_image(*pubs[sec_index], sec, t3); assert(image == t3); @@ -281,78 +366,78 @@ namespace crypto { } } #endif - if (ge_frombytes_vartime(&image_unp, &image) != 0) { + if (ge_frombytes_vartime(&image_unp, reinterpret_cast(&image)) != 0) { abort(); } ge_dsm_precomp(image_pre, &image_unp); - sc_0(&sum); + sc_0(reinterpret_cast(&sum)); buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; if (i == sec_index) { random_scalar(k); - ge_scalarmult_base(&tmp3, &k); - ge_p3_tobytes(&buf->ab[i].a, &tmp3); + ge_scalarmult_base(&tmp3, reinterpret_cast(&k)); + ge_p3_tobytes(reinterpret_cast(&buf->ab[i].a), &tmp3); hash_to_ec(*pubs[i], tmp3); - ge_scalarmult(&tmp2, &k, &tmp3); - ge_tobytes(&buf->ab[i].b, &tmp2); + ge_scalarmult(&tmp2, reinterpret_cast(&k), &tmp3); + ge_tobytes(reinterpret_cast(&buf->ab[i].b), &tmp2); } else { - random_scalar(sig[i].c); - random_scalar(sig[i].r); - if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { + random_scalar(reinterpret_cast(sig[i])); + random_scalar(*reinterpret_cast(reinterpret_cast(&sig[i]) + 32)); + if (ge_frombytes_vartime(&tmp3, reinterpret_cast(&*pubs[i])) != 0) { abort(); } - ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); - ge_tobytes(&buf->ab[i].a, &tmp2); + ge_double_scalarmult_base_vartime(&tmp2, reinterpret_cast(&sig[i]), &tmp3, reinterpret_cast(&sig[i]) + 32); + ge_tobytes(reinterpret_cast(&buf->ab[i].a), &tmp2); hash_to_ec(*pubs[i], tmp3); - ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); - ge_tobytes(&buf->ab[i].b, &tmp2); - sc_add(&sum, &sum, &sig[i].c); + ge_double_scalarmult_precomp_vartime(&tmp2, reinterpret_cast(&sig[i]) + 32, &tmp3, reinterpret_cast(&sig[i]), image_pre); + ge_tobytes(reinterpret_cast(&buf->ab[i].b), &tmp2); + sc_add(reinterpret_cast(&sum), reinterpret_cast(&sum), reinterpret_cast(&sig[i])); } } hash_to_scalar(buf, rs_comm_size(pubs_count), h); - sc_sub(&sig[sec_index].c, &h, &sum); - sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &sec, &k); + sc_sub(reinterpret_cast(&sig[sec_index]), reinterpret_cast(&h), reinterpret_cast(&sum)); + sc_mulsub(reinterpret_cast(&sig[sec_index]) + 32, reinterpret_cast(&sig[sec_index]), reinterpret_cast(&sec), reinterpret_cast(&k)); } - bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, size_t pubs_count, - const signature *sig) { + bool crypto_ops::check_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, size_t pubs_count, + const Signature *sig) { size_t i; ge_p3 image_unp; ge_dsmp image_pre; - ec_scalar sum, h; + EllipticCurveScalar sum, h; rs_comm *const buf = reinterpret_cast(alloca(rs_comm_size(pubs_count))); #if !defined(NDEBUG) for (i = 0; i < pubs_count; i++) { assert(check_key(*pubs[i])); } #endif - if (ge_frombytes_vartime(&image_unp, &image) != 0) { + if (ge_frombytes_vartime(&image_unp, reinterpret_cast(&image)) != 0) { return false; } ge_dsm_precomp(image_pre, &image_unp); - sc_0(&sum); + sc_0(reinterpret_cast(&sum)); buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; - if (sc_check(&sig[i].c) != 0 || sc_check(&sig[i].r) != 0) { + if (sc_check(reinterpret_cast(&sig[i])) != 0 || sc_check(reinterpret_cast(&sig[i]) + 32) != 0) { return false; } - if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) { + if (ge_frombytes_vartime(&tmp3, reinterpret_cast(&*pubs[i])) != 0) { abort(); } - ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r); - ge_tobytes(&buf->ab[i].a, &tmp2); + ge_double_scalarmult_base_vartime(&tmp2, reinterpret_cast(&sig[i]), &tmp3, reinterpret_cast(&sig[i]) + 32); + ge_tobytes(reinterpret_cast(&buf->ab[i].a), &tmp2); hash_to_ec(*pubs[i], tmp3); - ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre); - ge_tobytes(&buf->ab[i].b, &tmp2); - sc_add(&sum, &sum, &sig[i].c); + ge_double_scalarmult_precomp_vartime(&tmp2, reinterpret_cast(&sig[i]) + 32, &tmp3, reinterpret_cast(&sig[i]), image_pre); + ge_tobytes(reinterpret_cast(&buf->ab[i].b), &tmp2); + sc_add(reinterpret_cast(&sum), reinterpret_cast(&sum), reinterpret_cast(&sig[i])); } hash_to_scalar(buf, rs_comm_size(pubs_count), h); - sc_sub(&h, &h, &sum); - return sc_isnonzero(&h) == 0; + sc_sub(reinterpret_cast(&h), reinterpret_cast(&h), reinterpret_cast(&sum)); + return sc_isnonzero(reinterpret_cast(&h)) == 0; } } diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h old mode 100644 new mode 100755 index 9bc0bcd1d1..6426a85638 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -20,15 +20,15 @@ #include #include #include +#include #include -#include "Common/pod-class.h" +#include + #include "generic-ops.h" #include "hash.h" -namespace crypto { - - using std::size_t; +namespace Crypto { extern "C" { #include "random.h" @@ -36,41 +36,13 @@ namespace crypto { extern std::mutex random_lock; -#pragma pack(push, 1) - POD_CLASS ec_point { - char data[32]; - }; - - POD_CLASS ec_scalar { - char data[32]; - }; - - POD_CLASS public_key: ec_point { - friend class crypto_ops; - }; - - POD_CLASS secret_key: ec_scalar { - friend class crypto_ops; - }; - - POD_CLASS key_derivation: ec_point { - friend class crypto_ops; - }; - - POD_CLASS key_image: ec_point { - friend class crypto_ops; - }; - - POD_CLASS signature { - ec_scalar c, r; - friend class crypto_ops; - }; -#pragma pack(pop) +struct EllipticCurvePoint { + uint8_t data[32]; +}; - static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && - sizeof(public_key) == 32 && sizeof(secret_key) == 32 && - sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && - sizeof(signature) == 64, "Invalid structure size"); +struct EllipticCurveScalar { + uint8_t data[32]; +}; class crypto_ops { crypto_ops(); @@ -78,34 +50,48 @@ namespace crypto { void operator=(const crypto_ops &); ~crypto_ops(); - static void generate_keys(public_key &, secret_key &); - friend void generate_keys(public_key &, secret_key &); - static bool check_key(const public_key &); - friend bool check_key(const public_key &); - static bool secret_key_to_public_key(const secret_key &, public_key &); - friend bool secret_key_to_public_key(const secret_key &, public_key &); - static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); - friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); - static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); - friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); - static bool underive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - friend bool underive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); - static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); - friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); - static bool check_signature(const hash &, const public_key &, const signature &); - friend bool check_signature(const hash &, const public_key &, const signature &); - static void generate_key_image(const public_key &, const secret_key &, key_image &); - friend void generate_key_image(const public_key &, const secret_key &, key_image &); - static void generate_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const secret_key &, std::size_t, signature *); - friend void generate_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const secret_key &, std::size_t, signature *); - static bool check_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const signature *); - friend bool check_ring_signature(const hash &, const key_image &, - const public_key *const *, std::size_t, const signature *); + static void generate_keys(PublicKey &, SecretKey &); + friend void generate_keys(PublicKey &, SecretKey &); + static bool check_key(const PublicKey &); + friend bool check_key(const PublicKey &); + static bool secret_key_to_public_key(const SecretKey &, PublicKey &); + friend bool secret_key_to_public_key(const SecretKey &, PublicKey &); + static bool generate_key_derivation(const PublicKey &, const SecretKey &, KeyDerivation &); + friend bool generate_key_derivation(const PublicKey &, const SecretKey &, KeyDerivation &); + static bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + friend bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + friend bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + static bool derive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + //hack for pg + static bool underive_public_key_and_get_scalar(const KeyDerivation &, std::size_t, const PublicKey &, PublicKey &, EllipticCurveScalar &); + friend bool underive_public_key_and_get_scalar(const KeyDerivation &, std::size_t, const PublicKey &, PublicKey &, EllipticCurveScalar &); + static void generate_incomplete_key_image(const PublicKey &, EllipticCurvePoint &); + friend void generate_incomplete_key_image(const PublicKey &, EllipticCurvePoint &); + // + static void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, SecretKey &); + friend void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, SecretKey &); + static void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, const uint8_t*, size_t, SecretKey &); + friend void derive_secret_key(const KeyDerivation &, size_t, const SecretKey &, const uint8_t*, size_t, SecretKey &); + static bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + friend bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, PublicKey &); + static bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + friend bool underive_public_key(const KeyDerivation &, size_t, const PublicKey &, const uint8_t*, size_t, PublicKey &); + static void generate_signature(const Hash &, const PublicKey &, const SecretKey &, Signature &); + friend void generate_signature(const Hash &, const PublicKey &, const SecretKey &, Signature &); + static bool check_signature(const Hash &, const PublicKey &, const Signature &); + friend bool check_signature(const Hash &, const PublicKey &, const Signature &); + static void generate_key_image(const PublicKey &, const SecretKey &, KeyImage &); + friend void generate_key_image(const PublicKey &, const SecretKey &, KeyImage &); + static void hash_data_to_ec(const uint8_t*, std::size_t, PublicKey&); + friend void hash_data_to_ec(const uint8_t*, std::size_t, PublicKey&); + static void generate_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const SecretKey &, size_t, Signature *); + friend void generate_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const SecretKey &, size_t, Signature *); + static bool check_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const Signature *); + friend bool check_ring_signature(const Hash &, const KeyImage &, + const PublicKey *const *, size_t, const Signature *); }; /* Generate a value filled with random bytes. @@ -118,7 +104,7 @@ namespace crypto { return res; } - /* Random number engine based on crypto::rand() + /* Random number engine based on Crypto::rand() */ template class random_engine { @@ -149,19 +135,19 @@ namespace crypto { /* Generate a new key pair */ - inline void generate_keys(public_key &pub, secret_key &sec) { + inline void generate_keys(PublicKey &pub, SecretKey &sec) { crypto_ops::generate_keys(pub, sec); } /* Check a public key. Returns true if it is valid, false otherwise. */ - inline bool check_key(const public_key &key) { + inline bool check_key(const PublicKey &key) { return crypto_ops::check_key(key); } /* Checks a private key and computes the corresponding public key. */ - inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) { + inline bool secret_key_to_public_key(const SecretKey &sec, PublicKey &pub) { return crypto_ops::secret_key_to_public_key(sec, pub); } @@ -171,31 +157,55 @@ namespace crypto { * * The sender uses key derivation, the output index, and the receivers' "spend" key to derive an ephemeral public key. * * The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key (to spend the money). */ - inline bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + inline bool generate_key_derivation(const PublicKey &key1, const SecretKey &key2, KeyDerivation &derivation) { return crypto_ops::generate_key_derivation(key1, key2, derivation); } - inline bool derive_public_key(const key_derivation &derivation, std::size_t output_index, - const public_key &base, public_key &derived_key) { + + inline bool derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, const uint8_t* prefix, size_t prefixLength, PublicKey &derived_key) { + return crypto_ops::derive_public_key(derivation, output_index, base, prefix, prefixLength, derived_key); + } + + inline bool derive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &base, PublicKey &derived_key) { return crypto_ops::derive_public_key(derivation, output_index, base, derived_key); } - inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index, - const secret_key &base, secret_key &derived_key) { + + + inline bool underive_public_key_and_get_scalar(const KeyDerivation &derivation, std::size_t output_index, + const PublicKey &derived_key, PublicKey &base, EllipticCurveScalar &hashed_derivation) { + return crypto_ops::underive_public_key_and_get_scalar(derivation, output_index, derived_key, base, hashed_derivation); + } + + inline void derive_secret_key(const KeyDerivation &derivation, std::size_t output_index, + const SecretKey &base, const uint8_t* prefix, size_t prefixLength, SecretKey &derived_key) { + crypto_ops::derive_secret_key(derivation, output_index, base, prefix, prefixLength, derived_key); + } + + inline void derive_secret_key(const KeyDerivation &derivation, std::size_t output_index, + const SecretKey &base, SecretKey &derived_key) { crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); } + /* Inverse function of derive_public_key. It can be used by the receiver to find which "spend" key was used to generate a transaction. This may be useful if the receiver used multiple addresses which only differ in "spend" key. */ - inline bool underive_public_key(const key_derivation &derivation, std::size_t output_index, - const public_key &derived_key, public_key &base) { + inline bool underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, const uint8_t* prefix, size_t prefixLength, PublicKey &base) { + return crypto_ops::underive_public_key(derivation, output_index, derived_key, prefix, prefixLength, base); + } + + inline bool underive_public_key(const KeyDerivation &derivation, size_t output_index, + const PublicKey &derived_key, PublicKey &base) { return crypto_ops::underive_public_key(derivation, output_index, derived_key, base); } /* Generation and checking of a standard signature. */ - inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + inline void generate_signature(const Hash &prefix_hash, const PublicKey &pub, const SecretKey &sec, Signature &sig) { crypto_ops::generate_signature(prefix_hash, pub, sec, sig); } - inline bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { + inline bool check_signature(const Hash &prefix_hash, const PublicKey &pub, const Signature &sig) { return crypto_ops::check_signature(prefix_hash, pub, sig); } @@ -205,36 +215,43 @@ namespace crypto { * * Then he selects a bunch of outputs, including the one he spends, and uses them to generate a ring signature. * To check the signature, it is necessary to collect all the keys that were used to generate it. To detect double spends, it is necessary to check that each key image is used at most once. */ - inline void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { + inline void generate_key_image(const PublicKey &pub, const SecretKey &sec, KeyImage &image) { crypto_ops::generate_key_image(pub, sec, image); } - inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, std::size_t pubs_count, - const secret_key &sec, std::size_t sec_index, - signature *sig) { + + inline void hash_data_to_ec(const uint8_t* data, std::size_t len, PublicKey& key) { + crypto_ops::hash_data_to_ec(data, len, key); + } + + inline void generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, std::size_t pubs_count, + const SecretKey &sec, std::size_t sec_index, + Signature *sig) { crypto_ops::generate_ring_signature(prefix_hash, image, pubs, pubs_count, sec, sec_index, sig); } - inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, - const public_key *const *pubs, std::size_t pubs_count, - const signature *sig) { + inline bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const PublicKey *const *pubs, size_t pubs_count, + const Signature *sig) { return crypto_ops::check_ring_signature(prefix_hash, image, pubs, pubs_count, sig); } - /* Variants with vector parameters. + /* Variants with vector parameters. */ - inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, - const std::vector &pubs, - const secret_key &sec, std::size_t sec_index, - signature *sig) { + inline void generate_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const std::vector &pubs, + const SecretKey &sec, size_t sec_index, + Signature *sig) { generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sig); } - inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, - const std::vector &pubs, - const signature *sig) { + inline bool check_ring_signature(const Hash &prefix_hash, const KeyImage &image, + const std::vector &pubs, + const Signature *sig) { return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig); } + } -CRYPTO_MAKE_HASHABLE(public_key) -CRYPTO_MAKE_HASHABLE(key_image) -CRYPTO_MAKE_COMPARABLE(signature) +CRYPTO_MAKE_HASHABLE(PublicKey) +CRYPTO_MAKE_HASHABLE(KeyImage) +CRYPTO_MAKE_COMPARABLE(Signature) +CRYPTO_MAKE_COMPARABLE(SecretKey) diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h index 4b4b16d909..91d3529726 100644 --- a/src/crypto/generic-ops.h +++ b/src/crypto/generic-ops.h @@ -22,7 +22,7 @@ #include #define CRYPTO_MAKE_COMPARABLE(type) \ -namespace crypto { \ +namespace Crypto { \ inline bool operator==(const type &_v1, const type &_v2) { \ return std::memcmp(&_v1, &_v2, sizeof(type)) == 0; \ } \ @@ -33,17 +33,17 @@ namespace crypto { \ #define CRYPTO_MAKE_HASHABLE(type) \ CRYPTO_MAKE_COMPARABLE(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) { \ - return reinterpret_cast(_v); \ +namespace Crypto { \ + static_assert(sizeof(size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ + inline size_t hash_value(const type &_v) { \ + return reinterpret_cast(_v); \ } \ } \ namespace std { \ template<> \ - struct hash { \ - std::size_t operator()(const crypto::type &_v) const { \ - return reinterpret_cast(_v); \ + struct hash { \ + size_t operator()(const Crypto::type &_v) const { \ + return reinterpret_cast(_v); \ } \ }; \ } diff --git a/src/crypto/groestl.c b/src/crypto/groestl.c index 00bf987c98..e194d7ea5a 100644 --- a/src/crypto/groestl.c +++ b/src/crypto/groestl.c @@ -204,7 +204,7 @@ static void OutputTransformation(hashState *ctx) { /* initialise context */ static void Init(hashState* ctx) { - int i = 0; + unsigned int i = 0; /* allocate memory for state and data buffer */ for(;i<(SIZE512/sizeof(uint32_t));i++) diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h old mode 100644 new mode 100755 diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 05e2e4595d..08410a2cf8 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -19,33 +19,25 @@ #include -#include "Common/pod-class.h" +#include #include "generic-ops.h" -namespace crypto { +namespace Crypto { extern "C" { #include "hash-ops.h" } -#pragma pack(push, 1) - POD_CLASS hash { - char data[HASH_SIZE]; - }; -#pragma pack(pop) - - static_assert(sizeof(hash) == HASH_SIZE, "Invalid structure size"); - /* Cryptonight hash functions */ - inline void cn_fast_hash(const void *data, std::size_t length, hash &hash) { + inline void cn_fast_hash(const void *data, size_t length, Hash &hash) { cn_fast_hash(data, length, reinterpret_cast(&hash)); } - inline hash cn_fast_hash(const void *data, std::size_t length) { - hash h; + inline Hash cn_fast_hash(const void *data, size_t length) { + Hash h; cn_fast_hash(data, length, reinterpret_cast(&h)); return h; } @@ -63,25 +55,25 @@ namespace crypto { private: void *data; - friend inline void cn_slow_hash(cn_context &, const void *, std::size_t, hash &); + friend inline void cn_slow_hash(cn_context &, const void *, size_t, Hash &); }; - inline void cn_slow_hash(cn_context &context, const void *data, std::size_t length, hash &hash) { + inline void cn_slow_hash(cn_context &context, const void *data, size_t length, Hash &hash) { (*cn_slow_hash_f)(context.data, data, length, reinterpret_cast(&hash)); } - inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { + inline void tree_hash(const Hash *hashes, size_t count, Hash &root_hash) { tree_hash(reinterpret_cast(hashes), count, reinterpret_cast(&root_hash)); } - inline void tree_branch(const hash *hashes, std::size_t count, hash *branch) { + inline void tree_branch(const Hash *hashes, size_t count, Hash *branch) { tree_branch(reinterpret_cast(hashes), count, reinterpret_cast(branch)); } - inline void tree_hash_from_branch(const hash *branch, std::size_t depth, const hash &leaf, const void *path, hash &root_hash) { + inline void tree_hash_from_branch(const Hash *branch, size_t depth, const Hash &leaf, const void *path, Hash &root_hash) { tree_hash_from_branch(reinterpret_cast(branch), depth, reinterpret_cast(&leaf), path, reinterpret_cast(&root_hash)); } } -CRYPTO_MAKE_HASHABLE(hash) +CRYPTO_MAKE_HASHABLE(Hash) diff --git a/src/crypto/skein.c b/src/crypto/skein.c index 9c8ac288dd..65e4525c33 100644 --- a/src/crypto/skein.c +++ b/src/crypto/skein.c @@ -77,7 +77,7 @@ typedef struct /* 1024-bit Skein hash context stru } Skein1024_Ctxt_t; /* Skein APIs for (incremental) "straight hashing" */ -#if SKEIN_256_NIST_MAX_HASH_BITS +#if SKEIN_256_NIST_MAX_HASHBITS static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); #endif static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); @@ -1941,7 +1941,7 @@ static HashReturn Final (hashState *state, BitSequence *hashval); /* select the context size and init the context */ static HashReturn Init(hashState *state, int hashbitlen) { -#if SKEIN_256_NIST_MAX_HASH_BITS +#if SKEIN_256_NIST_MAX_HASHBITS if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) { Skein_Assert(hashbitlen > 0,BAD_HASHLEN); diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h old mode 100644 new mode 100755 diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c old mode 100644 new mode 100755 diff --git a/src/crypto/slow-hash.cpp b/src/crypto/slow-hash.cpp index 33fa46b79d..58fc44ccea 100644 --- a/src/crypto/slow-hash.cpp +++ b/src/crypto/slow-hash.cpp @@ -27,7 +27,7 @@ using std::bad_alloc; -namespace crypto { +namespace Crypto { enum { MAP_SIZE = SLOW_HASH_CONTEXT_SIZE + ((-SLOW_HASH_CONTEXT_SIZE) & 0xfff) diff --git a/src/cryptonote_core/BlockIndex.cpp b/src/cryptonote_core/BlockIndex.cpp deleted file mode 100644 index 12e973bdeb..0000000000 --- a/src/cryptonote_core/BlockIndex.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "BlockIndex.h" -#include - -namespace CryptoNote -{ - crypto::hash BlockIndex::getBlockId(uint64_t height) const { - if (height >= m_container.size()) - return boost::value_initialized(); - return m_container[static_cast(height)]; - } - - bool BlockIndex::getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items) const { - if (startHeight >= m_container.size()) - return false; - - for (size_t i = startHeight; i < (startHeight + maxCount) && i < m_container.size(); ++i) { - items.push_back(m_container[i]); - } - - return true; - } - - - bool BlockIndex::findSupplement(const std::list& ids, uint64_t& offset) const { - - for (const auto& id : ids) { - if (getBlockHeight(id, offset)) - return true; - } - - return false; - } - - bool BlockIndex::getShortChainHistory(std::list& ids) const { - size_t i = 0; - size_t current_multiplier = 1; - size_t sz = size(); - - if (!sz) - return true; - - size_t current_back_offset = 1; - bool genesis_included = false; - - while (current_back_offset < sz) { - ids.push_back(m_container[sz - current_back_offset]); - if (sz - current_back_offset == 0) - genesis_included = true; - if (i < 10) { - ++current_back_offset; - } else { - current_back_offset += current_multiplier *= 2; - } - ++i; - } - - if (!genesis_included) - ids.push_back(m_container[0]); - - return true; - } - - crypto::hash BlockIndex::getTailId() const { - if (m_container.empty()) - return boost::value_initialized(); - return m_container.back(); - } - - -} diff --git a/src/cryptonote_core/ICore.h b/src/cryptonote_core/ICore.h deleted file mode 100755 index 5ab1625477..0000000000 --- a/src/cryptonote_core/ICore.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include -#include -#include - -#include "crypto/hash.h" -#include "cryptonote_core/difficulty.h" -#include "cryptonote_protocol/blobdatatype.h" - -namespace CryptoNote { - -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; -struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; -struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; -struct NOTIFY_RESPONSE_GET_OBJECTS_request; -struct NOTIFY_REQUEST_GET_OBJECTS_request; - -class Currency; -class ICoreObserver; -struct Block; -struct block_verification_context; -struct BlockFullInfo; -struct core_stat_info; -struct i_cryptonote_protocol; -struct Transaction; -struct TransactionInputMultisignature; -struct TransactionInputToKey; -struct tx_verification_context; - -class ICore { -public: - virtual ~ICore() {} - - virtual bool addObserver(ICoreObserver* observer) = 0; - virtual bool removeObserver(ICoreObserver* observer) = 0; - - virtual bool have_block(const crypto::hash& id) = 0; - virtual bool get_short_chain_history(std::list& ids) = 0; - virtual bool get_stat_info(CryptoNote::core_stat_info& st_inf) = 0; - virtual bool on_idle() = 0; - virtual void pause_mining() = 0; - virtual void update_block_template_and_resume_mining() = 0; - virtual bool handle_incoming_block_blob(const CryptoNote::blobdata& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; - virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; - virtual void on_synchronized() = 0; - virtual bool is_ready() = 0; - - virtual bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) = 0; - virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, - uint64_t& total_height, uint64_t& start_height, size_t max_count) = 0; - virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp) = 0; - virtual 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) = 0; - virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) = 0; - virtual i_cryptonote_protocol* get_protocol() = 0; - virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) = 0; - virtual std::vector getPoolTransactions() = 0; - virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) = 0; - virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) = 0; - virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, - uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries) = 0; - - virtual crypto::hash getBlockIdByHeight(uint64_t height) = 0; - virtual bool getBlockByHash(const crypto::hash &h, Block &blk) = 0; - virtual void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) = 0; - virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) = 0; - virtual bool getBlockSize(const crypto::hash& hash, size_t& size) = 0; - virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) = 0; - virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) = 0; - virtual bool scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) = 0; - virtual bool getBlockDifficulty(uint64_t height, difficulty_type& difficulty) = 0; - virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) = 0; - virtual bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) = 0; -}; - -} //namespace CryptoNote diff --git a/src/cryptonote_core/Transaction.cpp b/src/cryptonote_core/Transaction.cpp deleted file mode 100644 index c7dada3281..0000000000 --- a/src/cryptonote_core/Transaction.cpp +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "ITransaction.h" -#include "TransactionExtra.h" - -#include "cryptonote_format_utils.h" -#include "account.h" - -#include -#include -#include - -namespace { - - using namespace CryptoNote; - - void derivePublicKey(const AccountAddress& to, const crypto::secret_key& txKey, size_t outputIndex, crypto::public_key& ephemeralKey) { - crypto::key_derivation derivation; - crypto::generate_key_derivation(*reinterpret_cast(&to.viewPublicKey), txKey, derivation); - crypto::derive_public_key(derivation, outputIndex, *reinterpret_cast(&to.spendPublicKey), ephemeralKey); - } - - bool checkInputsKeyimagesDiff(const CryptoNote::Transaction& tx) { - std::unordered_set ki; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (!ki.insert(boost::get(in).keyImage).second) - return false; - } - } - return true; - } - - - // TransactionInput helper functions - - size_t getRequiredSignaturesCount(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return boost::get(in).keyOffsets.size(); - } - if (in.type() == typeid(TransactionInputMultisignature)) { - return boost::get(in).signatures; - } - return 0; - } - - uint64_t getTransactionInputAmount(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return boost::get(in).amount; - } - if (in.type() == typeid(TransactionInputMultisignature)) { - return boost::get(in).amount; - } - return 0; - } - - TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { - if (in.type() == typeid(TransactionInputToKey)) { - return TransactionTypes::InputType::Key; - } - if (in.type() == typeid(TransactionInputMultisignature)) { - return TransactionTypes::InputType::Multisignature; - } - if (in.type() == typeid(TransactionInputGenerate)) { - return TransactionTypes::InputType::Generating; - } - return TransactionTypes::InputType::Invalid; - } - - const TransactionInput& getInputChecked(const CryptoNote::Transaction& transaction, size_t index) { - if (transaction.vin.size() <= index) { - throw std::runtime_error("Transaction input index out of range"); - } - return transaction.vin[index]; - } - - const TransactionInput& getInputChecked(const CryptoNote::Transaction& transaction, size_t index, TransactionTypes::InputType type) { - const auto& input = getInputChecked(transaction, index); - if (getTransactionInputType(input) != type) { - throw std::runtime_error("Unexpected transaction input type"); - } - return input; - } - - // TransactionOutput helper functions - - TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { - if (out.type() == typeid(TransactionOutputToKey)) { - return TransactionTypes::OutputType::Key; - } - if (out.type() == typeid(TransactionOutputMultisignature)) { - return TransactionTypes::OutputType::Multisignature; - } - return TransactionTypes::OutputType::Invalid; - } - - const TransactionOutput& getOutputChecked(const CryptoNote::Transaction& transaction, size_t index) { - if (transaction.vout.size() <= index) { - throw std::runtime_error("Transaction output index out of range"); - } - return transaction.vout[index]; - } - - const TransactionOutput& getOutputChecked(const CryptoNote::Transaction& transaction, size_t index, TransactionTypes::OutputType type) { - const auto& output = getOutputChecked(transaction, index); - if (getTransactionOutputType(output.target) != type) { - throw std::runtime_error("Unexpected transaction output target type"); - } - return output; - } -} - - -namespace CryptoNote { - - using namespace TransactionTypes; - - //////////////////////////////////////////////////////////////////////// - // class Transaction declaration - //////////////////////////////////////////////////////////////////////// - - class TransactionImpl : public ITransaction { - public: - TransactionImpl(); - TransactionImpl(const Blob& txblob); - TransactionImpl(const CryptoNote::Transaction& tx); - - // ITransactionReader - virtual Hash getTransactionHash() const override; - virtual Hash getTransactionPrefixHash() const override; - virtual PublicKey getTransactionPublicKey() const override; - virtual uint64_t getUnlockTime() const override; - virtual bool getPaymentId(Hash& hash) const override; - virtual bool getExtraNonce(std::string& nonce) const override; - virtual Blob getExtra() const override; - - // inputs - virtual size_t getInputCount() const override; - virtual uint64_t getInputTotalAmount() const override; - virtual TransactionTypes::InputType getInputType(size_t index) const override; - virtual void getInput(size_t index, TransactionTypes::InputKey& input) const override; - virtual void getInput(size_t index, TransactionTypes::InputMultisignature& input) const override; - - // outputs - virtual size_t getOutputCount() const override; - virtual uint64_t getOutputTotalAmount() const override; - virtual TransactionTypes::OutputType getOutputType(size_t index) const override; - virtual void getOutput(size_t index, TransactionTypes::OutputKey& output) const override; - virtual void getOutput(size_t index, TransactionTypes::OutputMultisignature& output) const override; - - virtual size_t getRequiredSignaturesCount(size_t index) const override; - virtual bool findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& outs, uint64_t& outputAmount) const override; - - // various checks - virtual bool validateInputs() const override; - virtual bool validateOutputs() const override; - virtual bool validateSignatures() const override; - - // get serialized transaction - virtual Blob getTransactionData() const override; - - // ITransactionWriter - - virtual void setUnlockTime(uint64_t unlockTime) override; - virtual void setPaymentId(const Hash& hash) override; - virtual void setExtraNonce(const std::string& nonce) override; - virtual void appendExtra(const Blob& extraData) override; - - // Inputs/Outputs - virtual size_t addInput(const TransactionTypes::InputKey& input) override; - virtual size_t addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) override; - virtual size_t addInput(const TransactionTypes::InputMultisignature& input) override; - - virtual size_t addOutput(uint64_t amount, const AccountAddress& to) override; - virtual size_t addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) override; - - virtual void signInputKey(size_t input, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) override; - virtual void signInputMultisignature(size_t input, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) override; - - // secret key - virtual bool getTransactionSecretKey(SecretKey& key) const override; - virtual void setTransactionSecretKey(const SecretKey& key) override; - - private: - - void invalidateHash(); - - std::vector& getSignatures(size_t input); - - const crypto::secret_key& txSecretKey() const { - if (!secretKey) { - throw std::runtime_error("Operation requires transaction secret key"); - } - return *secretKey; - } - - void checkIfSigning() const { - if (!transaction.signatures.empty()) { - throw std::runtime_error("Cannot perform requested operation, since it will invalidate transaction signatures"); - } - } - - CryptoNote::Transaction transaction; - boost::optional secretKey; - mutable boost::optional transactionHash; - TransactionExtra extra; - }; - - - //////////////////////////////////////////////////////////////////////// - // class Transaction implementation - //////////////////////////////////////////////////////////////////////// - - std::unique_ptr createTransaction() { - return std::unique_ptr(new TransactionImpl()); - } - - std::unique_ptr createTransaction(const Blob& transactionBlob) { - return std::unique_ptr(new TransactionImpl(transactionBlob)); - } - - std::unique_ptr createTransaction(const CryptoNote::Transaction& tx) { - return std::unique_ptr(new TransactionImpl(tx)); - } - - TransactionImpl::TransactionImpl() { - CryptoNote::KeyPair txKeys(CryptoNote::KeyPair::generate()); - - tx_extra_pub_key pk = { txKeys.pub }; - extra.set(pk); - - transaction.version = CURRENT_TRANSACTION_VERSION; - transaction.unlockTime = 0; - transaction.extra = extra.serialize(); - - secretKey = txKeys.sec; - } - - TransactionImpl::TransactionImpl(const Blob& data) { - CryptoNote::blobdata blob(reinterpret_cast(data.data()), data.size()); - if (!parse_and_validate_tx_from_blob(blob, transaction)) { - throw std::runtime_error("Invalid transaction data"); - } - - extra.parse(transaction.extra); - transactionHash = get_blob_hash(blob); // avoid serialization if we already have blob - } - - TransactionImpl::TransactionImpl(const CryptoNote::Transaction& tx) : transaction(tx) { - extra.parse(transaction.extra); - } - - void TransactionImpl::invalidateHash() { - if (transactionHash.is_initialized()) { - transactionHash = decltype(transactionHash)(); - } - } - - Hash TransactionImpl::getTransactionHash() const { - if (!transactionHash.is_initialized()) { - transactionHash = get_transaction_hash(transaction); - } - - return reinterpret_cast(transactionHash.get()); - } - - Hash TransactionImpl::getTransactionPrefixHash() const { - auto hash = get_transaction_prefix_hash(transaction); - return reinterpret_cast(hash); - } - - PublicKey TransactionImpl::getTransactionPublicKey() const { - crypto::public_key pk(null_pkey); - extra.getPublicKey(pk); - return reinterpret_cast(pk); - } - - uint64_t TransactionImpl::getUnlockTime() const { - return transaction.unlockTime; - } - - void TransactionImpl::setUnlockTime(uint64_t unlockTime) { - checkIfSigning(); - transaction.unlockTime = unlockTime; - invalidateHash(); - } - - bool TransactionImpl::getTransactionSecretKey(SecretKey& key) const { - if (!secretKey) { - return false; - } - key = reinterpret_cast(secretKey.get()); - return true; - } - - void TransactionImpl::setTransactionSecretKey(const SecretKey& key) { - const auto& sk = reinterpret_cast(key); - crypto::public_key pk; - crypto::public_key txPubKey; - - crypto::secret_key_to_public_key(sk, pk); - extra.getPublicKey(txPubKey); - - if (txPubKey != pk) { - throw std::runtime_error("Secret transaction key does not match public key"); - } - - secretKey = reinterpret_cast(key); - } - - size_t TransactionImpl::addInput(const InputKey& input) { - checkIfSigning(); - TransactionInputToKey inKey = { input.amount, input.keyOffsets, *reinterpret_cast(&input.keyImage) }; - transaction.vin.emplace_back(inKey); - invalidateHash(); - return transaction.vin.size() - 1; - } - - size_t TransactionImpl::addInput(const AccountKeys& senderKeys, const TransactionTypes::InputKeyInfo& info, TransactionTypes::KeyPair& ephKeys) { - checkIfSigning(); - InputKey input; - input.amount = info.amount; - - generate_key_image_helper( - reinterpret_cast(senderKeys), - reinterpret_cast(info.realOutput.transactionPublicKey), - info.realOutput.outputInTransaction, - reinterpret_cast(ephKeys), - reinterpret_cast(input.keyImage)); - - // fill outputs array and use relative offsets - for (const auto& out : info.outputs) { - input.keyOffsets.push_back(out.outputIndex); - } - - input.keyOffsets = absolute_output_offsets_to_relative(input.keyOffsets); - return addInput(input); - } - - size_t TransactionImpl::addInput(const InputMultisignature& input) { - checkIfSigning(); - - TransactionInputMultisignature inMsig; - inMsig.amount = input.amount; - inMsig.outputIndex = input.outputIndex; - inMsig.signatures = input.signatures; - transaction.vin.push_back(inMsig); - invalidateHash(); - - return transaction.vin.size() - 1; - } - - size_t TransactionImpl::addOutput(uint64_t amount, const AccountAddress& to) { - checkIfSigning(); - - TransactionOutputToKey outKey; - derivePublicKey(to, txSecretKey(), transaction.vout.size(), outKey.key); - TransactionOutput out = { amount, outKey }; - transaction.vout.emplace_back(out); - invalidateHash(); - - return transaction.vout.size() - 1; - } - - size_t TransactionImpl::addOutput(uint64_t amount, const std::vector& to, uint32_t requiredSignatures) { - checkIfSigning(); - - const auto& txKey = txSecretKey(); - size_t outputIndex = transaction.vout.size(); - TransactionOutputMultisignature outMsig; - outMsig.requiredSignatures = requiredSignatures; - outMsig.keys.resize(to.size()); - - for (size_t i = 0; i < to.size(); ++i) { - derivePublicKey(to[i], txKey, outputIndex, outMsig.keys[i]); - } - - TransactionOutput out = { amount, outMsig }; - transaction.vout.emplace_back(out); - invalidateHash(); - - return outputIndex; - } - - void TransactionImpl::signInputKey(size_t index, const TransactionTypes::InputKeyInfo& info, const TransactionTypes::KeyPair& ephKeys) { - const auto& input = boost::get(getInputChecked(transaction, index, InputType::Key)); - Hash prefixHash = getTransactionPrefixHash(); - - std::vector signatures; - std::vector keysPtrs; - - for (const auto& o : info.outputs) { - keysPtrs.push_back(reinterpret_cast(&o.targetKey)); - } - - signatures.resize(keysPtrs.size()); - - generate_ring_signature( - reinterpret_cast(prefixHash), - reinterpret_cast(input.keyImage), - keysPtrs, - reinterpret_cast(ephKeys.secretKey), - info.realOutput.transactionIndex, - signatures.data()); - - getSignatures(index) = signatures; - invalidateHash(); - } - - void TransactionImpl::signInputMultisignature(size_t index, const PublicKey& sourceTransactionKey, size_t outputIndex, const AccountKeys& accountKeys) { - crypto::key_derivation derivation; - crypto::public_key ephemeralPublicKey; - crypto::secret_key ephemeralSecretKey; - - crypto::generate_key_derivation( - reinterpret_cast(sourceTransactionKey), - reinterpret_cast(accountKeys.viewSecretKey), - derivation); - - crypto::derive_public_key(derivation, outputIndex, - reinterpret_cast(accountKeys.address.spendPublicKey), ephemeralPublicKey); - crypto::derive_secret_key(derivation, outputIndex, - reinterpret_cast(accountKeys.spendSecretKey), ephemeralSecretKey); - - crypto::signature signature; - auto txPrefixHash = getTransactionPrefixHash(); - - crypto::generate_signature(reinterpret_cast(txPrefixHash), - ephemeralPublicKey, ephemeralSecretKey, signature); - - getSignatures(index).push_back(signature); - invalidateHash(); - } - - std::vector& TransactionImpl::getSignatures(size_t input) { - // update signatures container size if needed - if (transaction.signatures.size() < transaction.vin.size()) { - transaction.signatures.resize(transaction.vin.size()); - } - // check range - if (input >= transaction.signatures.size()) { - throw std::runtime_error("Invalid input index"); - } - - return transaction.signatures[input]; - } - - std::vector TransactionImpl::getTransactionData() const { - return stringToVector(t_serializable_object_to_blob(transaction)); - } - - void TransactionImpl::setPaymentId(const Hash& hash) { - checkIfSigning(); - blobdata paymentIdBlob; - set_payment_id_to_tx_extra_nonce(paymentIdBlob, reinterpret_cast(hash)); - setExtraNonce(paymentIdBlob); - } - - bool TransactionImpl::getPaymentId(Hash& hash) const { - blobdata nonce; - if (getExtraNonce(nonce)) { - crypto::hash paymentId; - if (get_payment_id_from_tx_extra_nonce(nonce, paymentId)) { - hash = reinterpret_cast(paymentId); - return true; - } - } - return false; - } - - void TransactionImpl::setExtraNonce(const std::string& nonce) { - checkIfSigning(); - tx_extra_nonce extraNonce = { nonce }; - extra.set(extraNonce); - transaction.extra = extra.serialize(); - invalidateHash(); - } - - void TransactionImpl::appendExtra(const Blob& extraData) { - checkIfSigning(); - transaction.extra.insert( - transaction.extra.end(), extraData.begin(), extraData.end()); - } - - bool TransactionImpl::getExtraNonce(std::string& nonce) const { - tx_extra_nonce extraNonce; - if (extra.get(extraNonce)) { - nonce = extraNonce.nonce; - return true; - } - return false; - } - - Blob TransactionImpl::getExtra() const { - return transaction.extra; - } - - size_t TransactionImpl::getInputCount() const { - return transaction.vin.size(); - } - - uint64_t TransactionImpl::getInputTotalAmount() const { - return std::accumulate(transaction.vin.begin(), transaction.vin.end(), 0ULL, [](uint64_t val, const TransactionInput& in) { - return val + getTransactionInputAmount(in); }); - } - - TransactionTypes::InputType TransactionImpl::getInputType(size_t index) const { - return getTransactionInputType(getInputChecked(transaction, index)); - } - - void TransactionImpl::getInput(size_t index, InputKey& input) const { - const auto& k = boost::get(getInputChecked(transaction, index, InputType::Key)); - input.amount = k.amount; - input.keyImage = reinterpret_cast(k.keyImage); - input.keyOffsets = k.keyOffsets; - } - - void TransactionImpl::getInput(size_t index, InputMultisignature& input) const { - const auto& m = boost::get(getInputChecked(transaction, index, InputType::Multisignature)); - input.amount = m.amount; - input.outputIndex = m.outputIndex; - input.signatures = m.signatures; - } - - size_t TransactionImpl::getOutputCount() const { - return transaction.vout.size(); - } - - uint64_t TransactionImpl::getOutputTotalAmount() const { - return std::accumulate(transaction.vout.begin(), transaction.vout.end(), 0ULL, [](uint64_t val, const TransactionOutput& out) { - return val + out.amount; }); - } - - TransactionTypes::OutputType TransactionImpl::getOutputType(size_t index) const { - return getTransactionOutputType(getOutputChecked(transaction, index).target); - } - - void TransactionImpl::getOutput(size_t index, OutputKey& output) const { - const auto& out = getOutputChecked(transaction, index, OutputType::Key); - const auto& k = boost::get(out.target); - output.amount = out.amount; - output.key = reinterpret_cast(k.key); - } - - void TransactionImpl::getOutput(size_t index, OutputMultisignature& output) const { - const auto& out = getOutputChecked(transaction, index, OutputType::Multisignature); - const auto& m = boost::get(out.target); - output.amount = out.amount; - output.keys = reinterpret_cast&>(m.keys); - output.requiredSignatures = m.requiredSignatures; - } - - bool isOutToKey(const crypto::public_key& spendPublicKey, const crypto::public_key& outKey, const crypto::key_derivation& derivation, size_t keyIndex) { - crypto::public_key pk; - derive_public_key(derivation, keyIndex, spendPublicKey, pk); - return pk == outKey; - } - - bool TransactionImpl::findOutputsToAccount(const AccountAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) const { - account_keys keys; - keys.m_account_address = reinterpret_cast(addr); - // only view secret key is used, spend key is not needed - keys.m_view_secret_key = reinterpret_cast(viewSecretKey); - - auto pk = getTransactionPublicKey(); - crypto::public_key txPubKey = reinterpret_cast(pk); - - amount = 0; - size_t keyIndex = 0; - uint32_t outputIndex = 0; - - crypto::key_derivation derivation; - generate_key_derivation(txPubKey, keys.m_view_secret_key, derivation); - - for (const TransactionOutput& o : transaction.vout) { - assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); - if (o.target.type() == typeid(TransactionOutputToKey)) { - if (is_out_to_acc(keys, boost::get(o.target), derivation, keyIndex)) { - out.push_back(outputIndex); - amount += o.amount; - } - ++keyIndex; - } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { - const auto& target = boost::get(o.target); - for (const auto& key : target.keys) { - if (isOutToKey(keys.m_account_address.m_spendPublicKey, key, derivation, static_cast(outputIndex))) { - out.push_back(outputIndex); - } - ++keyIndex; - } - } - ++outputIndex; - } - - return true; - } - - size_t TransactionImpl::getRequiredSignaturesCount(size_t index) const { - return ::getRequiredSignaturesCount(getInputChecked(transaction, index)); - } - - bool TransactionImpl::validateInputs() const { - return - check_inputs_types_supported(transaction) && - check_inputs_overflow(transaction) && - checkInputsKeyimagesDiff(transaction) && - checkMultisignatureInputsDiff(transaction); - } - - bool TransactionImpl::validateOutputs() const { - return - check_outs_valid(transaction) && - check_outs_overflow(transaction); - } - - bool TransactionImpl::validateSignatures() const { - if (transaction.signatures.size() < transaction.vin.size()) { - return false; - } - - for (size_t i = 0; i < transaction.vin.size(); ++i) { - if (getRequiredSignaturesCount(i) > transaction.signatures[i].size()) { - return false; - } - } - - return true; - } -} diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h deleted file mode 100644 index 6b0781dead..0000000000 --- a/src/cryptonote_core/blockchain_storage.h +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include - -#include "google/sparse_hash_set" -#include "google/sparse_hash_map" - -#include "Common/ObserverManager.h" -#include "Common/util.h" -#include "cryptonote_core/BlockIndex.h" -#include "cryptonote_core/checkpoints.h" -#include "cryptonote_core/Currency.h" -#include "cryptonote_core/IBlockchainStorageObserver.h" -#include "cryptonote_core/ITransactionValidator.h" -#include "cryptonote_core/SwappedVector.h" -#include "cryptonote_core/UpgradeDetector.h" -#include "cryptonote_core/cryptonote_format_utils.h" -#include "cryptonote_core/tx_pool.h" - - -#include - -#undef ERROR - -namespace CryptoNote { - struct NOTIFY_RESPONSE_CHAIN_ENTRY_request; - struct NOTIFY_REQUEST_GET_OBJECTS_request; - struct NOTIFY_RESPONSE_GET_OBJECTS_request; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_request; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_response; - struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount; - - using CryptoNote::BlockInfo; - class blockchain_storage : public CryptoNote::ITransactionValidator { - public: - blockchain_storage(const Currency& currency, tx_memory_pool& tx_pool, Logging::ILogger& logger); - - bool addObserver(IBlockchainStorageObserver* observer); - bool removeObserver(IBlockchainStorageObserver* observer); - - // ITransactionValidator - virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock); - virtual bool checkTransactionInputs(const CryptoNote::Transaction& tx, BlockInfo& maxUsedBlock, BlockInfo& lastFailed); - virtual bool haveSpentKeyImages(const CryptoNote::Transaction& tx); - - bool init() { return init(tools::get_default_data_dir(), true); } - bool init(const std::string& config_folder, bool load_existing); - bool deinit(); - - bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint64_t& height); - bool getBlockIds(uint64_t startHeight, size_t maxCount, std::list& items); - - void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); - bool get_alternative_blocks(std::list& blocks); - size_t get_alternative_blocks_count(); - crypto::hash get_block_id_by_height(uint64_t height); - bool get_block_by_hash(const crypto::hash &h, Block &blk); - - template void serialize(archive_t & ar, const unsigned int version); - - bool have_tx(const crypto::hash &id); - bool have_tx_keyimges_as_spent(const Transaction &tx); - - uint64_t get_current_blockchain_height(); - crypto::hash get_tail_id(); - crypto::hash get_tail_id(uint64_t& height); - difficulty_type get_difficulty_for_next_block(); - uint64_t getCoinsInCirculation(); - uint8_t get_block_major_version_for_height(uint64_t height) const; - bool add_new_block(const Block& bl_, block_verification_context& bvc); - bool reset_and_set_genesis_block(const Block& b); - bool create_block_template(Block& b, const AccountPublicAddress& miner_address, difficulty_type& di, uint32_t& height, const blobdata& ex_nonce); - bool have_block(const crypto::hash& id); - size_t get_total_transactions(); - bool get_short_chain_history(std::list& ids); - bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); // !!!! - bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); - bool find_blockchain_supplement(const std::list& qblock_ids, std::list>>& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp); - 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); - bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count); - bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - bool check_tx_inputs(const Transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, BlockInfo* tail = 0); - uint64_t get_current_comulative_blocksize_limit(); - bool is_storing_blockchain(){return m_is_blockchain_storing;} - uint64_t block_difficulty(size_t i); - bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds); - void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds); - bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight); - bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins); - bool getBlockSize(const crypto::hash& hash, size_t& size); - bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference); - - template bool scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); - - template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) { - std::lock_guard lk(m_blockchain_lock); - - for (const auto& bl_id : block_ids) { - uint64_t height = 0; - if (!m_blockIndex.getBlockHeight(bl_id, height)) { - missed_bs.push_back(bl_id); - } else { - if (!(height < m_blocks.size())) { logger(Logging::ERROR, Logging::BRIGHT_RED) << "Internal error: bl_id=" << Common::podToHex(bl_id) - << " have index record with offset=" << height << ", bigger then m_blocks.size()=" << m_blocks.size(); return false; } - blocks.push_back(m_blocks[height].bl); - } - } - - return true; - } - - template - void getBlockchainTransactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) { - std::lock_guard bcLock(m_blockchain_lock); - - for (const auto& tx_id : txs_ids) { - auto it = m_transactionMap.find(tx_id); - if (it == m_transactionMap.end()) { - missed_txs.push_back(tx_id); - } else { - txs.push_back(transactionByIndex(it->second).tx); - } - } - } - - template - void get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool checkTxPool = false) { - if (checkTxPool){ - std::lock_guard txLock(m_tx_pool); - - getBlockchainTransactions(txs_ids, txs, missed_txs); - - auto poolTxIds = std::move(missed_txs); - missed_txs.clear(); - m_tx_pool.getTransactions(poolTxIds, txs, missed_txs); - - } else { - getBlockchainTransactions(txs_ids, txs, missed_txs); - } - } - - //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - void print_blockchain_outs(const std::string& file); - - private: - struct TransactionEntry { - Transaction tx; - std::vector m_global_output_indexes; - - template void serialize(archive_t & ar, unsigned int version); - - BEGIN_SERIALIZE_OBJECT() - FIELD(tx) - FIELD(m_global_output_indexes) - END_SERIALIZE() - }; - - struct BlockEntry { - Block bl; - uint32_t height; - uint64_t block_cumulative_size; - difficulty_type cumulative_difficulty; - uint64_t already_generated_coins; - std::vector transactions; - - template void serialize(Archive& archive, unsigned int version); - - BEGIN_SERIALIZE_OBJECT() - FIELD(bl) - VARINT_FIELD(height) - VARINT_FIELD(block_cumulative_size) - VARINT_FIELD(cumulative_difficulty) - VARINT_FIELD(already_generated_coins) - FIELD(transactions) - END_SERIALIZE() - }; - - struct TransactionIndex { - uint32_t block; - uint16_t transaction; - - template void serialize(Archive& archive, unsigned int version); - }; - - struct MultisignatureOutputUsage { - TransactionIndex transactionIndex; - uint16_t outputIndex; - bool isUsed; - - template void serialize(Archive& archive, unsigned int version); - }; - - typedef google::sparse_hash_set key_images_container; - typedef std::unordered_map blocks_ext_by_hash; - typedef google::sparse_hash_map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction - typedef std::map> MultisignatureOutputsContainer; - - const Currency& m_currency; - tx_memory_pool& m_tx_pool; - std::recursive_mutex m_blockchain_lock; // TODO: add here reader/writer lock - crypto::cn_context m_cn_context; - tools::ObserverManager m_observerManager; - - key_images_container m_spent_keys; - size_t m_current_block_cumul_sz_limit; - blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info - outputs_container m_outputs; - - std::string m_config_folder; - checkpoints m_checkpoints; - std::atomic m_is_in_checkpoint_zone; - std::atomic m_is_blockchain_storing; - - typedef SwappedVector Blocks; - typedef std::unordered_map BlockMap; - typedef std::unordered_map TransactionMap; - typedef BasicUpgradeDetector UpgradeDetector; - - friend class BlockCacheSerializer; - - Blocks m_blocks; - CryptoNote::BlockIndex m_blockIndex; - TransactionMap m_transactionMap; - MultisignatureOutputsContainer m_multisignatureOutputs; - UpgradeDetector m_upgradeDetector; - - Logging::LoggerRef logger; - - bool storeCache(); - bool switch_to_alternative_blockchain(std::list& alt_chain, bool discard_disconnected_chain); - bool handle_alternative_block(const Block& b, const crypto::hash& id, block_verification_context& bvc); - difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, BlockEntry& bei); - bool prevalidate_miner_transaction(const Block& b, uint64_t height); - bool validate_miner_transaction(const Block& b, uint64_t height, size_t cumulativeBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, uint64_t& reward, int64_t& emissionChange); - bool validate_transaction(const Block& b, uint64_t height, const Transaction& tx); - bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); - bool get_last_n_blocks_sizes(std::vector& sz, size_t count); - bool add_out_to_get_random_outs(std::vector>& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_outs_for_amount& result_outs, uint64_t amount, size_t i); - bool is_tx_spendtime_unlocked(uint64_t unlock_time); - size_t find_end_of_allowed_index(const std::vector>& amount_outs); - bool check_block_timestamp_main(const Block& b); - bool check_block_timestamp(std::vector timestamps, const Block& b); - uint64_t get_adjusted_time(); - bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); - bool checkBlockVersion(const Block& b, const crypto::hash& blockHash); - bool checkParentBlockSize(const Block& b, const crypto::hash& blockHash); - bool checkCumulativeBlockSize(const crypto::hash& blockId, size_t cumulativeBlockSize, uint64_t height); - bool getBlockCumulativeSize(const Block& block, size_t& cumulativeSize); - bool update_next_comulative_size_limit(); - bool check_tx_input(const TransactionInputToKey& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); - bool check_tx_inputs(const Transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); - bool check_tx_inputs(const Transaction& tx, uint64_t* pmax_used_block_height = NULL); - bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); - const TransactionEntry& transactionByIndex(TransactionIndex index); - bool pushBlock(const Block& blockData, block_verification_context& bvc); - bool pushBlock(BlockEntry& block); - void popBlock(const crypto::hash& blockHash); - bool pushTransaction(BlockEntry& block, const crypto::hash& transactionHash, TransactionIndex transactionIndex); - void popTransaction(const Transaction& transaction, const crypto::hash& transactionHash); - void popTransactions(const BlockEntry& block, const crypto::hash& minerTransactionHash); - bool validateInput(const TransactionInputMultisignature& input, const crypto::hash& transactionHash, const crypto::hash& transactionPrefixHash, const std::vector& transactionSignatures); - - friend class LockedBlockchainStorage; - }; - - class LockedBlockchainStorage: boost::noncopyable { - public: - - LockedBlockchainStorage(blockchain_storage& bc) - : m_bc(bc), m_lock(bc.m_blockchain_lock) {} - - blockchain_storage* operator -> () { - return &m_bc; - } - - private: - - blockchain_storage& m_bc; - std::lock_guard m_lock; - }; - - template bool blockchain_storage::scan_outputkeys_for_indexes(const TransactionInputToKey& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) { - std::lock_guard lk(m_blockchain_lock); - auto it = m_outputs.find(tx_in_to_key.amount); - if (it == m_outputs.end() || !tx_in_to_key.keyOffsets.size()) - return false; - - std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.keyOffsets); - std::vector>& amount_outs_vec = it->second; - size_t count = 0; - for (uint64_t i : absolute_offsets) { - if(i >= amount_outs_vec.size() ) { - logger(Logging::INFO) << "Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1; - return false; - } - - //auto tx_it = m_transactionMap.find(amount_outs_vec[i].first); - //if (!(tx_it != m_transactionMap.end())) { logger(ERROR, BRIGHT_RED) << "Wrong transaction id in output indexes: " << Common::podToHex(amount_outs_vec[i].first); return false; } - - const TransactionEntry& tx = transactionByIndex(amount_outs_vec[i].first); - - if (!(amount_outs_vec[i].second < tx.tx.vout.size())) { - logger(Logging::ERROR, Logging::BRIGHT_RED) - << "Wrong index in transaction outputs: " - << amount_outs_vec[i].second << ", expected less then " - << tx.tx.vout.size(); - return false; - } - - if (!vis.handle_output(tx.tx, tx.tx.vout[amount_outs_vec[i].second], amount_outs_vec[i].second)) { - logger(Logging::INFO) << "Failed to handle_output for output no = " << count << ", with absolute offset " << i; - return false; - } - - if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) { - if (*pmax_related_block_height < amount_outs_vec[i].first.block) { - *pmax_related_block_height = amount_outs_vec[i].first.block; - } - } - } - - return true; - } -} - -#include "cryptonote_boost_serialization.h" -#include "blockchain_storage_boost_serialization.h" diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h deleted file mode 100644 index af5261170f..0000000000 --- a/src/cryptonote_core/blockchain_storage_boost_serialization.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once -/* -namespace boost -{ - namespace serialization - { - - - template - void serialize(archive_t & ar, CryptoNote::blockchain_storage::transaction_chain_entry& te, const unsigned int version) - { - ar & te.tx; - ar & te.m_keeper_block_height; - ar & te.m_blob_size; - ar & te.m_global_output_indexes; - } - - template - void serialize(archive_t & ar, CryptoNote::blockchain_storage::block_extended_info& ei, const unsigned int version) - { - ar & ei.bl; - ar & ei.height; - ar & ei.cumulative_difficulty; - ar & ei.block_cumulative_size; - ar & ei.already_generated_coins; - } - - } -} -*/ diff --git a/src/cryptonote_core/cryptonote_basic.cpp b/src/cryptonote_core/cryptonote_basic.cpp deleted file mode 100755 index 81d1234715..0000000000 --- a/src/cryptonote_core/cryptonote_basic.cpp +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "cryptonote_basic.h" -#include "../Common/StreamTools.h" -#include "../Common/StringTools.h" - -using Common::IInputStream; -using Common::IOutputStream; -using Common::read; -using Common::readVarint; -using Common::toString; - -namespace CryptoNote { - -void TransactionInputGenerate::serialize(IOutputStream& out) const { - writeVarint(out, height); -} - -TransactionInputGenerate TransactionInputGenerate::deserialize(IInputStream& in) { - TransactionInputGenerate input; - readVarint(in, input.height); - return input; -} - -void TransactionInputToKey::serialize(IOutputStream& out) const { - writeVarint(out, amount); - writeVarint(out, keyOffsets.size()); - for (uint64_t outputIndex : keyOffsets) { - writeVarint(out, outputIndex); - } - - write(out, &keyImage, sizeof(keyImage)); -} - -TransactionInputToKey TransactionInputToKey::deserialize(IInputStream& in) { - TransactionInputToKey input; - readVarint(in, input.amount); - input.keyOffsets.resize(readVarint(in)); - for (uint64_t& outputIndex : input.keyOffsets) { - readVarint(in, outputIndex); - } - - read(in, &input.keyImage, sizeof(input.keyImage)); - return input; -} - -void TransactionInputMultisignature::serialize(IOutputStream& out) const { - writeVarint(out, amount); - writeVarint(out, signatures); - writeVarint(out, outputIndex); -} - -TransactionInputMultisignature TransactionInputMultisignature::deserialize(IInputStream& in) { - TransactionInputMultisignature input; - readVarint(in, input.amount); - readVarint(in, input.signatures); - readVarint(in, input.outputIndex); - return input; -} - -void TransactionOutputToKey::serialize(IOutputStream& out) const { - write(out, &key, sizeof(key)); -} - -TransactionOutputToKey TransactionOutputToKey::deserialize(IInputStream& in) { - TransactionOutputToKey output; - read(in, &output.key, sizeof(output.key)); - return output; -} - -void TransactionOutputMultisignature::serialize(IOutputStream& out) const { - writeVarint(out, keys.size()); - for (const crypto::public_key& key : keys) { - write(out, &key, sizeof(key)); - } - - writeVarint(out, requiredSignatures); -} - -TransactionOutputMultisignature TransactionOutputMultisignature::deserialize(IInputStream& in) { - TransactionOutputMultisignature output; - output.keys.resize(readVarint(in)); - for (crypto::public_key& key : output.keys) { - read(in, &key, sizeof(key)); - } - - readVarint(in, output.requiredSignatures); - if (output.requiredSignatures > output.keys.size()) { - throw std::runtime_error("TransactionOutputMultisignature::deserialize"); - } - - return output; -} - -void TransactionOutput::serialize(IOutputStream& out) const { - writeVarint(out, amount); - if (target.type() == typeid(TransactionOutputToKey)) { - write(out, static_cast(2)); - boost::get(target).serialize(out); - } else { - write(out, static_cast(3)); - boost::get(target).serialize(out); - } -} - -TransactionOutput TransactionOutput::deserialize(IInputStream& in) { - TransactionOutput output; - readVarint(in, output.amount); - uint8_t targetType = read(in); - if (targetType == 2) { - output.target = TransactionOutputToKey::deserialize(in); - } else if (targetType == 3) { - output.target = TransactionOutputMultisignature::deserialize(in); - } else { - throw std::runtime_error("TransactionOutput::deserialize"); - } - - return output; -} - -void Transaction::serialize(IOutputStream& out) const { - writeVarint(out, version); - writeVarint(out, unlockTime); - writeVarint(out, vin.size()); - for (const TransactionInput& input : vin) { - if (input.type() == typeid(TransactionInputGenerate)) { - write(out, static_cast(255)); - boost::get(input).serialize(out); - } else if (input.type() == typeid(TransactionInputToKey)) { - write(out, static_cast(2)); - boost::get(input).serialize(out); - } else { - write(out, static_cast(3)); - boost::get(input).serialize(out); - } - } - - writeVarint(out, vout.size()); - for (const TransactionOutput& output : vout) { - output.serialize(out); - } - - writeVarint(out, extra.size()); - write(out, extra); - std::size_t signatureCount = 0; - for (const std::vector& inputSignatures : signatures) { - signatureCount += inputSignatures.size(); - } - - for (const std::vector& inputSignatures : signatures) { - for (const crypto::signature& signature : inputSignatures) { - write(out, &signature, sizeof(signature)); - } - } -} - -Transaction Transaction::deserialize(IInputStream& in) { - Transaction transaction; - readVarint(in, transaction.version); - if (transaction.version != CURRENT_TRANSACTION_VERSION) { - throw std::runtime_error("Transaction::deserialize"); - } - - readVarint(in, transaction.unlockTime); - transaction.vin.resize(readVarint(in)); - for (TransactionInput& input : transaction.vin) { - uint8_t inputType = read(in); - if (inputType == 255) { - input = TransactionInputGenerate::deserialize(in); - } else if (inputType == 2) { - input = TransactionInputToKey::deserialize(in); - } else if (inputType == 3) { - input = TransactionInputMultisignature::deserialize(in); - } else { - throw std::runtime_error("Transaction::deserialize"); - } - } - - transaction.vout.resize(readVarint(in)); - for (TransactionOutput& output : transaction.vout) { - output = TransactionOutput::deserialize(in); - } - - transaction.extra.resize(readVarint(in)); - read(in, transaction.extra.data(), transaction.extra.size()); - transaction.signatures.resize(transaction.vin.size()); - for (std::size_t i = 0; i < transaction.vin.size(); ++i) { - std::size_t signatureCount; - if (transaction.vin[i].type() == typeid(TransactionInputGenerate)) { - signatureCount = 0; - } else if (transaction.vin[i].type() == typeid(TransactionInputToKey)) { - signatureCount = boost::get(transaction.vin[i]).keyOffsets.size(); - } else { - signatureCount = boost::get(transaction.vin[i]).signatures; - } - - transaction.signatures[i].resize(signatureCount); - for (crypto::signature& signature : transaction.signatures[i]) { - read(in, &signature, sizeof(signature)); - } - } - - return transaction; -} - -void Block::serialize(IOutputStream& out) const { - writeVarint(out, majorVersion); - writeVarint(out, minorVersion); - if (majorVersion == BLOCK_MAJOR_VERSION_1) { - writeVarint(out, timestamp); - write(out, &prevId, sizeof(prevId)); - write(out, nonce); - } else { - write(out, &prevId, sizeof(prevId)); - writeVarint(out, parentBlock.majorVersion); - writeVarint(out, parentBlock.minorVersion); - writeVarint(out, timestamp); - write(out, &parentBlock.prevId, sizeof(parentBlock.prevId)); - write(out, nonce); - writeVarint(out, parentBlock.numberOfTransactions); - for (const crypto::hash& hash : parentBlock.minerTxBranch) { - write(out, &hash, sizeof(hash)); - } - - parentBlock.minerTx.serialize(out); - for (const crypto::hash& hash : parentBlock.blockchainBranch) { - write(out, &hash, sizeof(hash)); - } - } - - minerTx.serialize(out); - writeVarint(out, txHashes.size()); - for (const crypto::hash& hash : txHashes) { - write(out, &hash, sizeof(hash)); - } -} - -Block Block::deserialize(IInputStream& in) { - Block block; - readVarint(in, block.majorVersion); - if (block.majorVersion == BLOCK_MAJOR_VERSION_1) { - readVarint(in, block.minorVersion); - if (block.minorVersion != BLOCK_MINOR_VERSION_0 && block.minorVersion != BLOCK_MINOR_VERSION_1) { - throw std::runtime_error("Invalid block minor version (" + toString(static_cast(block.minorVersion)) + ") for major version 1"); - } - - readVarint(in, block.timestamp); - read(in, &block.prevId, sizeof(block.prevId)); - read(in, block.nonce); - } else if (block.majorVersion == BLOCK_MAJOR_VERSION_2) { - readVarint(in, block.minorVersion); - if (block.minorVersion != BLOCK_MINOR_VERSION_0) { - throw std::runtime_error("Invalid block minor version (" + toString(static_cast(block.minorVersion)) + ") for major version 2"); - } - - read(in, &block.prevId, sizeof(block.prevId)); - readVarint(in, block.parentBlock.majorVersion); - if (block.parentBlock.majorVersion != BLOCK_MAJOR_VERSION_1) { - throw std::runtime_error("Invalid parent block major version (" + toString(static_cast(block.parentBlock.majorVersion)) + ')'); - } - - readVarint(in, block.parentBlock.minorVersion); - if (block.parentBlock.minorVersion != BLOCK_MINOR_VERSION_0) { - throw std::runtime_error("Invalid parent block minor version (" + toString(static_cast(block.parentBlock.minorVersion)) + ')'); - } - - - readVarint(in, block.timestamp); - read(in, &block.parentBlock.prevId, sizeof(block.parentBlock.prevId)); - read(in, block.nonce); - readVarint(in, block.parentBlock.numberOfTransactions); - - block.parentBlock.minerTxBranch.resize(crypto::tree_depth(block.parentBlock.numberOfTransactions)); - for (crypto::hash& hash : block.parentBlock.minerTxBranch) { - read(in, &hash, sizeof(hash)); - } - - block.parentBlock.minerTx = Transaction::deserialize(in); - tx_extra_merge_mining_tag mergedMiningTag; - if (!get_mm_tag_from_extra(block.parentBlock.minerTx.extra, mergedMiningTag)) { - throw std::runtime_error("Cannot get merged mining tag"); - } - - if (mergedMiningTag.depth > 8 * sizeof(crypto::hash)) { - throw std::runtime_error("Invalid merged mining tag depth (" + toString(mergedMiningTag.depth) + ')'); - } - - - block.parentBlock.blockchainBranch.resize(mergedMiningTag.depth); - for (crypto::hash& hash : block.parentBlock.blockchainBranch) { - read(in, &hash, sizeof(hash)); - } - } else { - throw std::runtime_error("Invalid block major version (" + toString(static_cast(block.majorVersion)) + ')'); - } - - block.minerTx = Transaction::deserialize(in); - block.txHashes.resize(readVarint(in)); - for (crypto::hash& hash : block.txHashes) { - read(in, &hash, sizeof(hash)); - } - - return block; -} - -} diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h deleted file mode 100644 index 17d533c569..0000000000 --- a/src/cryptonote_core/cryptonote_basic.h +++ /dev/null @@ -1,451 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -#include -#include - -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "cryptonote_core/tx_extra.h" -#include "serialization/binary_archive.h" -#include "serialization/crypto.h" -#include "serialization/json_archive.h" -#include "serialization/serialization.h" -#include "serialization/variant.h" -#include "cryptonote_config.h" - -namespace Common { -class IInputStream; -class IOutputStream; -} - -namespace CryptoNote { - class account_base; - struct account_keys; - struct Block; - struct Transaction; - struct tx_extra_merge_mining_tag; - - // Implemented in cryptonote_format_utils.cpp - bool get_transaction_hash(const Transaction& t, crypto::hash& res); - bool get_mm_tag_from_extra(const std::vector& tx, tx_extra_merge_mining_tag& mm_tag); - - const static crypto::hash null_hash = boost::value_initialized(); - const static crypto::public_key null_pkey = boost::value_initialized(); - - /* inputs */ - - struct TransactionInputGenerate { - uint32_t height; - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(height); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionInputGenerate deserialize(Common::IInputStream& in); - }; - - struct TransactionInputToKey { - uint64_t amount; - std::vector keyOffsets; - crypto::key_image keyImage; // double spending protection - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount); - FIELD(keyOffsets); - FIELD(keyImage); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionInputToKey deserialize(Common::IInputStream& in); - }; - - struct TransactionInputMultisignature { - uint64_t amount; - uint32_t signatures; - uint64_t outputIndex; - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount); - VARINT_FIELD(signatures); - VARINT_FIELD(outputIndex); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionInputMultisignature deserialize(Common::IInputStream& in); - }; - - /* outputs */ - - struct TransactionOutputToKey { - TransactionOutputToKey() { } - TransactionOutputToKey(const crypto::public_key &_key) : key(_key) { } - crypto::public_key key; - - void serialize(Common::IOutputStream& out) const; - static TransactionOutputToKey deserialize(Common::IInputStream& in); - }; - - struct TransactionOutputMultisignature { - std::vector keys; - uint32_t requiredSignatures; - - BEGIN_SERIALIZE_OBJECT() - FIELD(keys); - VARINT_FIELD(requiredSignatures); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionOutputMultisignature deserialize(Common::IInputStream& in); - }; - - struct TransactionInputToScript { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - struct TransactionInputToScriptHash { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - struct TransactionOutputToScript { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - struct TransactionOutputToScriptHash { - BEGIN_SERIALIZE_OBJECT() - END_SERIALIZE() - }; - - typedef boost::variant< - TransactionInputGenerate, - TransactionInputToScript, - TransactionInputToScriptHash, - TransactionInputToKey, - TransactionInputMultisignature> TransactionInput; - - typedef boost::variant< - TransactionOutputToScript, - TransactionOutputToScriptHash, - TransactionOutputToKey, - TransactionOutputMultisignature> TransactionOutputTarget; - - struct TransactionOutput { - uint64_t amount; - TransactionOutputTarget target; - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD(amount); - FIELD(target); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static TransactionOutput deserialize(Common::IInputStream& in); - }; - - struct TransactionPrefix { - // tx information - uint8_t version; - uint64_t unlockTime; //number of block (or time), used as a limitation like: spend this tx not early then block/time - - std::vector vin; - std::vector vout; - //extra - std::vector extra; - - BEGIN_SERIALIZE() - VARINT_FIELD(version); - if(CURRENT_TRANSACTION_VERSION < version) { - return false; - } - VARINT_FIELD(unlockTime); - FIELD(vin); - FIELD(vout); - FIELD(extra); - END_SERIALIZE() - - protected: - TransactionPrefix() {} - }; - - struct Transaction: public TransactionPrefix { - std::vector > signatures; //count signatures always the same as inputs count - - Transaction() { - clear(); - } - - void clear() { - version = 0; - unlockTime = 0; - vin.clear(); - vout.clear(); - extra.clear(); - signatures.clear(); - } - - BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)) - - ar.tag("signatures"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); - bool signatures_not_expected = signatures.empty(); - if (!signatures_not_expected && vin.size() != signatures.size()) { - return false; - } - - for (size_t i = 0; i < vin.size(); ++i) { - size_t signatureSize = getSignatureSize(vin[i]); - if (signatures_not_expected) { - if (0 == signatureSize) { - continue; - } else { - return false; - } - } - - PREPARE_CUSTOM_VECTOR_SERIALIZATION(signatureSize, signatures[i]); - if (signatureSize != signatures[i].size()) { - return false; - } - - FIELDS(signatures[i]); - - if (vin.size() - i > 1) { - ar.delimit_array(); - } - } - ar.end_array(); - END_SERIALIZE() - - static size_t getSignatureSize(const TransactionInput& input) { - struct txin_signature_size_visitor : public boost::static_visitor { - size_t operator()(const TransactionInputGenerate& txin) const { return 0; } - size_t operator()(const TransactionInputToScript& txin) const { assert(false); return 0; } - size_t operator()(const TransactionInputToScriptHash& txin) const { assert(false); return 0; } - size_t operator()(const TransactionInputToKey& txin) const { return txin.keyOffsets.size();} - size_t operator()(const TransactionInputMultisignature& txin) const { return txin.signatures; } - }; - - return boost::apply_visitor(txin_signature_size_visitor(), input); - } - - void serialize(Common::IOutputStream& out) const; - static Transaction deserialize(Common::IInputStream& in); - }; - - struct ParentBlock { - uint8_t majorVersion; - uint8_t minorVersion; - crypto::hash prevId; - uint16_t numberOfTransactions; - std::vector minerTxBranch; - Transaction minerTx; - std::vector blockchainBranch; - }; - - struct ParentBlockSerializer { - ParentBlockSerializer(ParentBlock& parentBlock, uint64_t& timestamp, uint32_t& nonce, bool hashingSerialization, bool headerOnly) : - m_parentBlock(parentBlock), m_timestamp(timestamp), m_nonce(nonce), m_hashingSerialization(hashingSerialization), m_headerOnly(headerOnly) { - } - - BEGIN_SERIALIZE_OBJECT() - VARINT_FIELD_N("majorVersion", m_parentBlock.majorVersion); - if (BLOCK_MAJOR_VERSION_1 < m_parentBlock.majorVersion) { - return false; - } - VARINT_FIELD_N("minorVersion", m_parentBlock.minorVersion); - VARINT_FIELD_N("timestamp", m_timestamp); - FIELD_N("prevId", m_parentBlock.prevId); - FIELD_N("nonce", m_nonce); - - if (m_hashingSerialization) { - crypto::hash minerTxHash; - if (!get_transaction_hash(m_parentBlock.minerTx, minerTxHash)) { - return false; - } - - crypto::hash merkleRoot; - crypto::tree_hash_from_branch(m_parentBlock.minerTxBranch.data(), m_parentBlock.minerTxBranch.size(), minerTxHash, 0, merkleRoot); - - FIELD(merkleRoot); - } - - VARINT_FIELD_N("numberOfTransactions", m_parentBlock.numberOfTransactions); - if (m_parentBlock.numberOfTransactions < 1) { - return false; - } - - if (!m_headerOnly) { - ar.tag("minerTxBranch"); - ar.begin_array(); - size_t branchSize = crypto::tree_depth(m_parentBlock.numberOfTransactions); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(branchSize, const_cast(m_parentBlock).minerTxBranch); - if (m_parentBlock.minerTxBranch.size() != branchSize) { - return false; - } - for (size_t i = 0; i < branchSize; ++i) { - FIELDS(m_parentBlock.minerTxBranch[i]); - if (i + 1 < branchSize) { - ar.delimit_array(); - } - } - ar.end_array(); - - FIELD(m_parentBlock.minerTx); - - tx_extra_merge_mining_tag mmTag; - if (!get_mm_tag_from_extra(m_parentBlock.minerTx.extra, mmTag)) { - return false; - } - - if (mmTag.depth > 8 * sizeof(crypto::hash)) { - return false; - } - - ar.tag("blockchainBranch"); - ar.begin_array(); - PREPARE_CUSTOM_VECTOR_SERIALIZATION(mmTag.depth, const_cast(m_parentBlock).blockchainBranch); - if (mmTag.depth != m_parentBlock.blockchainBranch.size()) { - return false; - } - for (size_t i = 0; i < mmTag.depth; ++i) { - FIELDS(m_parentBlock.blockchainBranch[i]); - if (i + 1 < mmTag.depth) { - ar.delimit_array(); - } - } - ar.end_array(); - } - END_SERIALIZE() - - ParentBlock& m_parentBlock; - uint64_t& m_timestamp; - uint32_t& m_nonce; - bool m_hashingSerialization; - bool m_headerOnly; - }; - - // Implemented below - inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly); - - struct BlockHeader { - uint8_t majorVersion; - uint8_t minorVersion; - uint32_t nonce; - uint64_t timestamp; - crypto::hash prevId; - - BEGIN_SERIALIZE() - VARINT_FIELD(majorVersion) - if (majorVersion > BLOCK_MAJOR_VERSION_2) { - return false; - } - VARINT_FIELD(minorVersion) - if (majorVersion == BLOCK_MAJOR_VERSION_1) { - VARINT_FIELD(timestamp); - FIELD(prevId); - FIELD(nonce); - } else if (majorVersion == BLOCK_MAJOR_VERSION_2) { - FIELD(prevId); - } else { - return false; - } - END_SERIALIZE() - }; - - struct Block: public BlockHeader { - ParentBlock parentBlock; - - Transaction minerTx; - std::vector txHashes; - - BEGIN_SERIALIZE_OBJECT() - FIELDS(*static_cast(this)); - if (majorVersion == BLOCK_MAJOR_VERSION_2) { - auto serializer = makeParentBlockSerializer(*this, false, false); - FIELD_N("parentBlock", serializer); - } - FIELD(minerTx); - FIELD(txHashes); - END_SERIALIZE() - - void serialize(Common::IOutputStream& out) const; - static Block deserialize(Common::IInputStream& in); - }; - - inline ParentBlockSerializer makeParentBlockSerializer(const Block& b, bool hashingSerialization, bool headerOnly) { - Block& blockRef = const_cast(b); - return ParentBlockSerializer(blockRef.parentBlock, blockRef.timestamp, blockRef.nonce, hashingSerialization, headerOnly); - } - - struct AccountPublicAddress { - crypto::public_key m_spendPublicKey; - crypto::public_key m_viewPublicKey; - - BEGIN_SERIALIZE_OBJECT() - FIELD(m_spendPublicKey); - FIELD(m_viewPublicKey); - END_SERIALIZE() - }; - - struct KeyPair { - crypto::public_key pub; - crypto::secret_key sec; - - static KeyPair generate() { - KeyPair k; - generate_keys(k.pub, k.sec); - return k; - } - }; -} - -BLOB_SERIALIZER(CryptoNote::TransactionOutputToKey); - -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputGenerate, 0xff); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToScript, 0x0); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToScriptHash, 0x1); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputToKey, 0x2); -VARIANT_TAG(binary_archive, CryptoNote::TransactionInputMultisignature, 0x3); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToScript, 0x0); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToScriptHash, 0x1); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputToKey, 0x2); -VARIANT_TAG(binary_archive, CryptoNote::TransactionOutputMultisignature, 0x3); -VARIANT_TAG(binary_archive, CryptoNote::Transaction, 0xcc); -VARIANT_TAG(binary_archive, CryptoNote::Block, 0xbb); - -VARIANT_TAG(json_archive, CryptoNote::TransactionInputGenerate, "generate"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputToScript, "script"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputToScriptHash, "scripthash"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputToKey, "key"); -VARIANT_TAG(json_archive, CryptoNote::TransactionInputMultisignature, "multisignature"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToScript, "script"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToScriptHash, "scripthash"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputToKey, "key"); -VARIANT_TAG(json_archive, CryptoNote::TransactionOutputMultisignature, "multisignature"); -VARIANT_TAG(json_archive, CryptoNote::Transaction, "Transaction"); -VARIANT_TAG(json_archive, CryptoNote::Block, "Block"); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp deleted file mode 100644 index de23c8667d..0000000000 --- a/src/cryptonote_core/cryptonote_core.cpp +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "cryptonote_core.h" -#include -#include -#include "../cryptonote_config.h" -#include "../Common/command_line.h" -#include "../Common/util.h" -#include "../crypto/crypto.h" -#include "../cryptonote_protocol/cryptonote_protocol_defs.h" -#include "../Logging/LoggerRef.h" -#include "../rpc/core_rpc_server_commands_defs.h" -#include "cryptonote_format_utils.h" -#include "cryptonote_stat_info.h" -#include "miner.h" -#undef ERROR - -using namespace Logging; -#include "cryptonote_core/CoreConfig.h" - -namespace CryptoNote { - -core::core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger) : -m_currency(currency), -logger(logger, "core"), -m_mempool(currency, m_blockchain_storage, m_timeProvider, logger), -m_blockchain_storage(currency, m_mempool, logger), -m_miner(new miner(currency, *this, logger)), -m_starter_message_showed(false) { - set_cryptonote_protocol(pprotocol); - m_blockchain_storage.addObserver(this); - m_mempool.addObserver(this); - } - //----------------------------------------------------------------------------------------------- - core::~core() { - m_blockchain_storage.removeObserver(this); -} - -void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) { - if (pprotocol) - m_pprotocol = pprotocol; - else - m_pprotocol = &m_protocol_stub; -} -//----------------------------------------------------------------------------------- -void core::set_checkpoints(checkpoints&& chk_pts) { - m_blockchain_storage.set_checkpoints(std::move(chk_pts)); -} -//----------------------------------------------------------------------------------- -void core::init_options(boost::program_options::options_description& /*desc*/) { -} - -bool core::handle_command_line(const boost::program_options::variables_map& vm) { - m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); - return true; -} - -bool core::is_ready() { - return !m_blockchain_storage.is_storing_blockchain(); -} - - -uint64_t core::get_current_blockchain_height() { - return m_blockchain_storage.get_current_blockchain_height(); -} - -bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) { - top_id = m_blockchain_storage.get_tail_id(height); - return true; -} - -bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) { - 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) { - return m_blockchain_storage.get_blocks(start_offset, count, blocks); -} -void core::getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool) { - m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs, checkTxPool); -} - -bool core::get_alternative_blocks(std::list& blocks) { - return m_blockchain_storage.get_alternative_blocks(blocks); -} - -size_t core::get_alternative_blocks_count() { - return m_blockchain_storage.get_alternative_blocks_count(); - } - //----------------------------------------------------------------------------------------------- - bool core::init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing) { - m_config_folder = config.configFolder; - bool r = m_mempool.init(m_config_folder); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize memory pool"; return false; } - - r = m_blockchain_storage.init(m_config_folder, load_existing); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } - - r = m_miner->init(minerConfig); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "Failed to initialize blockchain storage"; return false; } - - return load_state_data(); -} - -bool core::set_genesis_block(const Block& b) { - return m_blockchain_storage.reset_and_set_genesis_block(b); -} - -bool core::load_state_data() { - // may be some code later - return true; -} - -bool core::deinit() { - m_miner->stop(); - m_mempool.deinit(); - m_blockchain_storage.deinit(); - return true; -} - -bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) { - tvc = boost::value_initialized(); - //want to process all transactions sequentially - std::lock_guard lk(m_incoming_tx_lock); - - if (tx_blob.size() > m_currency.maxTxSize()) { - logger(INFO) << "WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"; - tvc.m_verifivation_failed = true; - return false; - } - - crypto::hash tx_hash = null_hash; - crypto::hash tx_prefixt_hash = null_hash; - Transaction tx; - - if (!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) { - logger(INFO) << "WRONG TRANSACTION BLOB, Failed to parse, rejected"; - tvc.m_verifivation_failed = true; - return false; - } - //std::cout << "!"<< tx.vin.size() << std::endl; - - if (!check_tx_syntax(tx)) { - logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"; - tvc.m_verifivation_failed = true; - return false; - } - - if (!check_tx_semantic(tx, keeped_by_block)) { - logger(INFO) << "WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"; - tvc.m_verifivation_failed = true; - return false; - } - - bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); - if (tvc.m_verifivation_failed) { - if (!tvc.m_tx_fee_too_small) { - logger(ERROR) << "Transaction verification failed: " << tx_hash; - } else { - logger(INFO) << "Transaction verification failed: " << tx_hash; - } - } else if (tvc.m_verifivation_impossible) { - logger(ERROR) << "Transaction verification impossible: " << tx_hash; - } - - if (tvc.m_added_to_pool) { - logger(DEBUGGING) << "tx added: " << tx_hash; - poolUpdated(); - } - - return r; -} - -bool core::get_stat_info(core_stat_info& st_inf) { - st_inf.mining_speed = m_miner->get_speed(); - st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); - st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - st_inf.tx_pool_size = m_mempool.get_transactions_count(); - st_inf.top_block_id_str = Common::podToHex(m_blockchain_storage.get_tail_id()); - return true; -} - - -bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) { - if (!tx.vin.size()) { - logger(ERROR) << "tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx); - return false; - } - - if (!check_inputs_types_supported(tx)) { - logger(ERROR) << "unsupported input types for tx id= " << get_transaction_hash(tx); - return false; - } - - std::string errmsg; - if (!check_outs_valid(tx, &errmsg)) { - logger(ERROR) << "tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx) << ": " << errmsg; - return false; - } - - if (!check_money_overflow(tx)) { - logger(ERROR) << "tx have money overflow, rejected for tx id= " << get_transaction_hash(tx); - return false; - } - - uint64_t amount_in = 0; - get_inputs_money_amount(tx, amount_in); - uint64_t amount_out = get_outs_money_amount(tx); - - if (amount_in <= amount_out) { - logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx); - return false; - } - - if (!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()) { - logger(ERROR) << "transaction is too big " << get_object_blobsize(tx) << ", maximum allowed size is " << - (m_blockchain_storage.get_current_comulative_blocksize_limit() - m_currency.minerTxBlobReservedSize()); - return false; - } - - //check if tx use different key images - if (!check_tx_inputs_keyimages_diff(tx)) { - logger(ERROR) << "tx has a few inputs with identical keyimages"; - return false; - } - - if (!checkMultisignatureInputsDiff(tx)) { - logger(ERROR) << "tx has a few multisignature inputs with identical output indexes"; - return false; - } - - return true; -} - -bool core::check_tx_inputs_keyimages_diff(const Transaction& tx) { - std::unordered_set ki; - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - if (!ki.insert(boost::get(in).keyImage).second) - return false; - } - } - return true; -} - -size_t core::get_blockchain_total_transactions() { - return m_blockchain_storage.get_total_transactions(); -} - -//bool core::get_outs(uint64_t amount, std::list& pkeys) -//{ -// return m_blockchain_storage.get_outs(amount, pkeys); -//} - -bool core::add_new_tx(const 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) { - if (m_blockchain_storage.have_tx(tx_hash)) { - logger(TRACE) << "tx " << tx_hash << " is already in blockchain"; - return true; - } - - // It's not very good to lock on m_mempool here, because it's very hard to understand the order of locking - // tx_memory_pool::m_transactions_lock, blockchain_storage::m_blockchain_lock, and core::m_incoming_tx_lock - std::lock_guard lk(m_mempool); - - if (m_mempool.have_tx(tx_hash)) { - logger(TRACE) << "tx " << tx_hash << " is already in transaction pool"; - return true; - } - - return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); -} - -bool core::get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce) { - return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); -} - -bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); -} - -bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) { - return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); -} - -void core::print_blockchain(uint64_t start_index, uint64_t end_index) { - m_blockchain_storage.print_blockchain(start_index, end_index); -} - -void core::print_blockchain_index() { - m_blockchain_storage.print_blockchain_index(); -} - -void core::print_blockchain_outs(const std::string& file) { - m_blockchain_storage.print_blockchain_outs(file); -} - -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) { - return m_blockchain_storage.get_random_outs_for_amounts(req, res); -} - -bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) { - return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); -} - -void core::pause_mining() { - m_miner->pause(); -} - -void core::update_block_template_and_resume_mining() { - update_miner_block_template(); - m_miner->resume(); -} - -bool core::handle_block_found(Block& b) { - block_verification_context bvc = boost::value_initialized(); - handle_incoming_block(b, bvc, true, true); - - if (bvc.m_verifivation_failed) { - logger(ERROR) << "mined block failed verification"; - } - - return bvc.m_added_to_main_chain; -} - -void core::on_synchronized() { - m_miner->on_synchronized(); -} -//----------------------------------------------------------------------------------------------- -bool core::getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) { - return m_blockchain_storage.getPoolChanges(tailBlockId, knownTxsIds, addedTxs, deletedTxsIds); -} -//----------------------------------------------------------------------------------------------- -void core::getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) { - m_blockchain_storage.getPoolChanges(knownTxsIds, addedTxs, deletedTxsIds); -} -//----------------------------------------------------------------------------------------------- -bool core::handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block) { - if (block_blob.size() > m_currency.maxBlockBlobSize()) { - logger(INFO) << "WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"; - bvc.m_verifivation_failed = true; - return false; - } - - Block b; - if (!parse_and_validate_block_from_blob(block_blob, b)) { - logger(INFO) << "Failed to parse and validate new block"; - bvc.m_verifivation_failed = true; - return false; - } - - return handle_incoming_block(b, bvc, control_miner, relay_block); -} - -bool core::handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block) { - if (control_miner) { - pause_mining(); - } - - m_blockchain_storage.add_new_block(b, bvc); - - if (control_miner) { - update_block_template_and_resume_mining(); - } - - if (relay_block && bvc.m_added_to_main_chain) { - std::list missed_txs; - std::list txs; - m_blockchain_storage.get_transactions(b.txHashes, txs, missed_txs); - if (!missed_txs.empty() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) { - logger(INFO) << "Block added, but it seems that reorganize just happened after that, do not relay this block"; - } else { - if (!(txs.size() == b.txHashes.size() && missed_txs.empty())) { - logger(ERROR, BRIGHT_RED) << "can't find some transactions in found block:" << - get_block_hash(b) << " txs.size()=" << txs.size() << ", b.txHashes.size()=" << b.txHashes.size() << ", missed_txs.size()" << missed_txs.size(); return false; - } - - NOTIFY_NEW_BLOCK::request arg; - arg.hop = 0; - arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); - bool r = block_to_blob(b, arg.b.block); - if (!(r)) { logger(ERROR, BRIGHT_RED) << "failed to serialize block"; return false; } - for (auto& tx : txs) { - arg.b.txs.push_back(t_serializable_object_to_blob(tx)); - } - - m_pprotocol->relay_block(arg); - } - } - - return true; -} - -crypto::hash core::get_tail_id() { - return m_blockchain_storage.get_tail_id(); -} - -size_t core::get_pool_transactions_count() { - return m_mempool.get_transactions_count(); -} - -bool core::have_block(const crypto::hash& id) { - return m_blockchain_storage.have_block(id); -} - -bool core::parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) { - return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); -} - -bool core::check_tx_syntax(const Transaction& tx) { - return true; -} - -std::vector core::getPoolTransactions() { - std::list txs; - m_mempool.get_transactions(txs); - - std::vector result; - for (auto& tx : txs) { - result.emplace_back(std::move(tx)); - } - return result; -} - -bool core::get_short_chain_history(std::list& ids) { - return m_blockchain_storage.get_short_chain_history(ids); -} - -bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) { - return m_blockchain_storage.handle_get_objects(arg, rsp); -} - -crypto::hash core::getBlockIdByHeight(uint64_t height) { - return m_blockchain_storage.get_block_id_by_height(height); -} - -bool core::getBlockByHash(const crypto::hash &h, Block &blk) { - return m_blockchain_storage.get_block_by_hash(h, blk); -} - -//void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { -// m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); -//} - -std::string core::print_pool(bool short_format) { - return m_mempool.print_pool(short_format); -} - -bool core::update_miner_block_template() { - m_miner->on_block_chain_update(); - return true; -} - -bool core::on_idle() { - if (!m_starter_message_showed) { - logger(INFO) << ENDL << "**********************************************************************" << ENDL - << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL - << ENDL - << "You can set the level of process detailization* through \"set_log \" command*, where is between 0 (no details) and 4 (very verbose)." << ENDL - << ENDL - << "Use \"help\" command to see the list of available commands." << ENDL - << ENDL - << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL - << "**********************************************************************"; - m_starter_message_showed = true; - } - - m_miner->on_idle(); - m_mempool.on_idle(); - return true; -} - -bool core::addObserver(ICoreObserver* observer) { - return m_observerManager.add(observer); -} - -bool core::removeObserver(ICoreObserver* observer) { - return m_observerManager.remove(observer); -} - -void core::blockchainUpdated() { - m_observerManager.notify(&ICoreObserver::blockchainUpdated); -} - - void core::txDeletedFromPool() { - poolUpdated(); - } - - void core::poolUpdated() { - m_observerManager.notify(&ICoreObserver::poolUpdated); - } - - bool core::queryBlocks(const std::list& knownBlockIds, uint64_t timestamp, - uint64_t& resStartHeight, uint64_t& resCurrentHeight, uint64_t& resFullOffset, std::list& entries) { - - LockedBlockchainStorage lbs(m_blockchain_storage); - - uint64_t currentHeight = lbs->get_current_blockchain_height(); - uint64_t startOffset = 0; - - if (!lbs->find_blockchain_supplement(knownBlockIds, startOffset)) { - return false; - } - - uint64_t startFullOffset = 0; - - if (!lbs->getLowerBound(timestamp, startOffset, startFullOffset)) - startFullOffset = startOffset; - - resFullOffset = startFullOffset; - - if (startOffset != startFullOffset) { - std::list blockIds; - if (!lbs->getBlockIds(startOffset, std::min(uint64_t(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT), startFullOffset - startOffset), blockIds)) { - return false; - } - - for (const auto& id : blockIds) { - entries.push_back(BlockFullInfo()); - entries.back().block_id = id; - } - } - - auto blocksLeft = std::min(BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT - entries.size(), size_t(BLOCKS_SYNCHRONIZING_DEFAULT_COUNT)); - - if (blocksLeft) { - std::list blocks; - lbs->get_blocks(startFullOffset, blocksLeft, blocks); - - for (auto& b : blocks) { - BlockFullInfo item; - - item.block_id = get_block_hash(b); - - if (b.timestamp >= timestamp) { - // query transactions - std::list txs; - std::list missedTxs; - lbs->get_transactions(b.txHashes, txs, missedTxs); - - // fill data - block_complete_entry& completeEntry = item; - completeEntry.block = block_to_blob(b); - for (auto& tx : txs) { - completeEntry.txs.push_back(tx_to_blob(tx)); - } - } - - entries.push_back(std::move(item)); - } - } - - resCurrentHeight = currentHeight; - resStartHeight = startOffset; - - return true; -} - -bool core::getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) { - return m_blockchain_storage.get_backward_blocks_sizes(fromHeight, sizes, count); -} - -bool core::getBlockSize(const crypto::hash& hash, size_t& size) { - return m_blockchain_storage.getBlockSize(hash, size); -} - -bool core::getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) { - return m_blockchain_storage.getAlreadyGeneratedCoins(hash, generatedCoins); -} - -bool core::getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) { - return m_currency.getBlockReward(medianSize, currentBlockSize, alreadyGeneratedCoins, fee, penalizeFee, reward, emissionChange); -} - -bool core::scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) { - struct outputs_visitor - { - std::list>& m_resultsCollector; - outputs_visitor(std::list>& resultsCollector):m_resultsCollector(resultsCollector){} - bool handle_output(const Transaction& tx, const TransactionOutput& out, size_t transactionOutputIndex) - { - m_resultsCollector.push_back(std::make_pair(get_transaction_hash(tx), transactionOutputIndex)); - return true; - } - }; - - outputs_visitor vi(outputReferences); - - return m_blockchain_storage.scan_outputkeys_for_indexes(txInToKey, vi); -} - -bool core::getBlockDifficulty(uint64_t height, difficulty_type& difficulty) { - difficulty = m_blockchain_storage.block_difficulty(height); - return true; -} - -bool core::getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) { - return m_blockchain_storage.getBlockContainingTx(txId, blockId, blockHeight); -} - -bool core::getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& outputReference) { - return m_blockchain_storage.getMultisigOutputReference(txInMultisig, outputReference); -} - -} diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h deleted file mode 100644 index d26891b936..0000000000 --- a/src/cryptonote_core/cryptonote_core.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include -#include - -#include "p2p/net_node_common.h" -#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" -#include "Currency.h" -#include "tx_pool.h" -#include "blockchain_storage.h" -#include "cryptonote_core/i_miner_handler.h" -#include "cryptonote_core/MinerConfig.h" -#include "crypto/hash.h" -#include "ICore.h" -#include "ICoreObserver.h" -#include "Common/ObserverManager.h" -#include - -namespace CryptoNote { - - struct core_stat_info; - class miner; - class CoreConfig; - - class core : public ICore, public i_miner_handler, public IBlockchainStorageObserver, public ITxPoolObserver { - public: - core(const Currency& currency, i_cryptonote_protocol* pprotocol, Logging::ILogger& logger); - ~core(); - - bool on_idle(); - virtual bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); - bool handle_incoming_block_blob(const blobdata& block_blob, block_verification_context& bvc, bool control_miner, bool relay_block); - virtual i_cryptonote_protocol* get_protocol(){return m_pprotocol;} - const Currency& currency() const { return m_currency; } - - //-------------------- i_miner_handler ----------------------- - virtual bool handle_block_found(Block& b); - virtual bool get_block_template(Block& b, const AccountPublicAddress& adr, difficulty_type& diffic, uint32_t& height, const blobdata& ex_nonce); - - bool addObserver(ICoreObserver* observer); - bool removeObserver(ICoreObserver* observer); - - miner& get_miner() { return *m_miner; } - static void init_options(boost::program_options::options_description& desc); - bool init(const CoreConfig& config, const MinerConfig& minerConfig, bool load_existing); - bool set_genesis_block(const Block& b); - bool deinit(); - - // ICore - virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) override; - virtual bool getBackwardBlocksSizes(uint64_t fromHeight, std::vector& sizes, size_t count) override; - virtual bool getBlockSize(const crypto::hash& hash, size_t& size) override; - virtual bool getAlreadyGeneratedCoins(const crypto::hash& hash, uint64_t& generatedCoins) override; - virtual bool getBlockReward(size_t medianSize, size_t currentBlockSize, uint64_t alreadyGeneratedCoins, uint64_t fee, - bool penalizeFee, uint64_t& reward, int64_t& emissionChange) override; - virtual bool scanOutputkeysForIndices(const TransactionInputToKey& txInToKey, std::list>& outputReferences) override; - virtual bool getBlockDifficulty(uint64_t height, difficulty_type& difficulty) override; - virtual bool getBlockContainingTx(const crypto::hash& txId, crypto::hash& blockId, uint64_t& blockHeight) override; - virtual bool getMultisigOutputReference(const TransactionInputMultisignature& txInMultisig, std::pair& output_reference) override; - - uint64_t get_current_blockchain_height(); - bool have_block(const crypto::hash& id); - bool get_short_chain_history(std::list& ids); - void on_synchronized(); - bool is_ready() override; - - virtual bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); - bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); - template - bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) - { - return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); - } - virtual bool queryBlocks(const std::list& block_ids, uint64_t timestamp, - uint64_t& start_height, uint64_t& current_height, uint64_t& full_offset, std::list& entries); - virtual crypto::hash getBlockIdByHeight(uint64_t height) override; - void getTransactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs, bool checkTxPool = false) override; - virtual bool getBlockByHash(const crypto::hash &h, Block &blk) override; - //void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); - - bool get_alternative_blocks(std::list& blocks); - size_t get_alternative_blocks_count(); - - void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); - void set_checkpoints(checkpoints&& chk_pts); - - std::vector getPoolTransactions() override; - size_t get_pool_transactions_count(); - size_t get_blockchain_total_transactions(); - //bool get_outs(uint64_t amount, std::list& pkeys); - virtual bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY_request& resp); - virtual bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); - bool get_stat_info(core_stat_info& st_inf); - - virtual bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); - crypto::hash get_tail_id(); - virtual 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); - void pause_mining(); - void update_block_template_and_resume_mining(); - blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} - //debug functions - void print_blockchain(uint64_t start_index, uint64_t end_index); - void print_blockchain_index(); - std::string print_pool(bool short_format); - void print_blockchain_outs(const std::string& file); - virtual bool getPoolChanges(const crypto::hash& tailBlockId, const std::vector& knownTxsIds, - std::vector& addedTxs, std::vector& deletedTxsIds) override; - virtual void getPoolChanges(const std::vector& knownTxsIds, std::vector& addedTxs, - std::vector& deletedTxsIds) override; - - private: - bool add_new_tx(const 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 load_state_data(); - bool parse_tx_from_blob(Transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); - bool handle_incoming_block(const Block& b, block_verification_context& bvc, bool control_miner, bool relay_block); - - bool check_tx_syntax(const Transaction& tx); - //check correct values, amounts and all lightweight checks not related with database - bool check_tx_semantic(const Transaction& tx, bool keeped_by_block); - //check if tx already in memory pool or in main blockchain - - bool is_key_image_spent(const crypto::key_image& key_im); - - bool check_tx_ring_signature(const TransactionInputToKey& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); - bool is_tx_spendtime_unlocked(uint64_t unlock_time); - bool update_miner_block_template(); - bool handle_command_line(const boost::program_options::variables_map& vm); - bool on_update_blocktemplate_interval(); - bool check_tx_inputs_keyimages_diff(const Transaction& tx); - virtual void blockchainUpdated() override; - virtual void txDeletedFromPool() override; - void poolUpdated(); - - const Currency& m_currency; - Logging::LoggerRef logger; - CryptoNote::RealTimeProvider m_timeProvider; - tx_memory_pool m_mempool; - blockchain_storage m_blockchain_storage; - i_cryptonote_protocol* m_pprotocol; - std::mutex m_incoming_tx_lock; - std::unique_ptr m_miner; - std::string m_config_folder; - cryptonote_protocol_stub m_protocol_stub; - friend class tx_validate_inputs; - std::atomic m_starter_message_showed; - tools::ObserverManager m_observerManager; - }; -} diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp deleted file mode 100644 index e24b121ecf..0000000000 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ /dev/null @@ -1,754 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#include "cryptonote_format_utils.h" -#include -#include "../Logging/LoggerRef.h" -#include "account.h" -#include "cryptonote_basic_impl.h" - -using namespace Logging; - -namespace CryptoNote { - -void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h) { - std::ostringstream s; - binary_archive a(s); - ::serialization::serialize(a, const_cast(tx)); - crypto::cn_fast_hash(s.str().data(), s.str().size(), h); -} - -crypto::hash get_transaction_prefix_hash(const TransactionPrefix& tx) { - crypto::hash h = null_hash; - get_transaction_prefix_hash(tx, h); - return h; -} - -bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, Transaction& tx) { - std::stringstream ss; - ss << tx_blob; - binary_archive ba(ss); - return ::serialization::serialize(ba, tx); -} - -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; - ss << tx_blob; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, tx); - - if (!r) { - return false; - } - - //TODO: validate tx - crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); - get_transaction_prefix_hash(tx, tx_prefix_hash); - return true; -} - -bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki) { - crypto::key_derivation recv_derivation; - bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); - - assert(r && "key image helper: failed to generate_key_derivation"); - - if (!r) { - return false; - } - - r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spendPublicKey, in_ephemeral.pub); - - assert(r && "key image helper: failed to derive_public_key"); - - if (!r) { - return false; - } - - crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); - crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); - return true; -} - -uint64_t power_integral(uint64_t a, uint64_t b) { - if (b == 0) - return 1; - uint64_t total = a; - for (uint64_t i = 1; i != b; i++) - total *= a; - return total; -} - -bool get_tx_fee(const Transaction& tx, uint64_t & fee) { - uint64_t amount_in = 0; - uint64_t amount_out = 0; - - for (const auto& in : tx.vin) { - if (in.type() == typeid(TransactionInputToKey)) { - amount_in += boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount_in += boost::get(in).amount; - } - } - - for (const auto& o : tx.vout) { - amount_out += o.amount; - } - - if (!(amount_in >= amount_out)) { - return false; - } - - fee = amount_in - amount_out; - return true; -} - -uint64_t get_tx_fee(const Transaction& tx) { - uint64_t r = 0; - if (!get_tx_fee(tx, r)) - return 0; - return r; -} - -bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields) { - tx_extra_fields.clear(); - - if (tx_extra.empty()) - 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; - while (!eof) { - tx_extra_field field; - bool r = ::do_serialize(ar, field); - if (!r) { - return false; - } - tx_extra_fields.push_back(field); - - std::ios_base::iostate state = iss.rdstate(); - eof = (EOF == iss.peek()); - iss.clear(state); - } - - if (!::serialization::check_stream_state(ar)) { - return false; - } - - return true; -} - -crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra) { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - tx_extra_pub_key pub_key_field; - if (!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field)) - return null_pkey; - - return pub_key_field.pub_key; -} - -crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx) { - return get_tx_pub_key_from_extra(tx.extra); -} - -bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key) { - tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); - tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; - *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; - return true; -} - -bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce) { - if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { - return false; - } - - size_t start_pos = tx_extra.size(); - tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); - //write tag - tx_extra[start_pos] = TX_EXTRA_NONCE; - //write len - ++start_pos; - tx_extra[start_pos] = static_cast(extra_nonce.size()); - //write data - ++start_pos; - memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); - return true; -} - -bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag) { - blobdata blob; - if (!t_serializable_object_to_blob(mm_tag, blob)) { - return false; - } - - tx_extra.push_back(TX_EXTRA_MERGE_MINING_TAG); - std::copy(reinterpret_cast(blob.data()), reinterpret_cast(blob.data() + blob.size()), std::back_inserter(tx_extra)); - return true; -} - -bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag) { - std::vector tx_extra_fields; - parse_tx_extra(tx_extra, tx_extra_fields); - - return find_tx_extra_field_by_type(tx_extra_fields, mm_tag); -} - -void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id) { - extra_nonce.clear(); - extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); - const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); - std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); -} - -bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id) { - if (sizeof(crypto::hash) + 1 != extra_nonce.size()) - return false; - if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) - return false; - payment_id = *reinterpret_cast(extra_nonce.data() + 1); - return true; -} - -bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId) { - return Common::podFromHex(paymentIdString, paymentId); -} - -bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { - crypto::hash paymentIdBin; - - if (!parsePaymentId(paymentIdString, paymentIdBin)) { - return false; - } - - std::string extraNonce; - CryptoNote::set_payment_id_to_tx_extra_nonce(extraNonce, paymentIdBin); - - if (!CryptoNote::add_extra_nonce_to_tx_extra(extra, extraNonce)) { - return false; - } - - return true; -} - -bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId) { - std::vector tx_extra_fields; - if (!parse_tx_extra(extra, tx_extra_fields)) { - return false; - } - - tx_extra_nonce extra_nonce; - if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) { - if (!get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, paymentId)) { - return false; - } - } else { - return false; - } - - return true; -} - -bool construct_tx( - const account_keys& sender_account_keys, - const std::vector& sources, - const std::vector& destinations, - std::vector extra, - Transaction& tx, - uint64_t unlock_time, - Logging::ILogger& log) { - LoggerRef logger(log, "construct_tx"); - - tx.vin.clear(); - tx.vout.clear(); - tx.signatures.clear(); - - tx.version = CURRENT_TRANSACTION_VERSION; - tx.unlockTime = unlock_time; - - tx.extra = extra; - KeyPair txkey = KeyPair::generate(); - add_tx_pub_key_to_extra(tx, txkey.pub); - - struct input_generation_context_data { - KeyPair in_ephemeral; - }; - - std::vector in_contexts; - uint64_t summary_inputs_money = 0; - //fill inputs - for (const tx_source_entry& src_entr : sources) { - if (src_entr.real_output >= src_entr.outputs.size()) { - logger(ERROR) << "real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size(); - return false; - } - summary_inputs_money += src_entr.amount; - - //key_derivation recv_derivation; - in_contexts.push_back(input_generation_context_data()); - KeyPair& in_ephemeral = in_contexts.back().in_ephemeral; - crypto::key_image img; - if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) - return false; - - //check that derivated key is equal with real output key - if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second)) { - logger(ERROR) << "derived public key missmatch with output public key! " << ENDL << "derived_key:" - << Common::podToHex(in_ephemeral.pub) << ENDL << "real output_public_key:" - << Common::podToHex(src_entr.outputs[src_entr.real_output].second); - return false; - } - - //put key image into tx input - TransactionInputToKey input_to_key; - input_to_key.amount = src_entr.amount; - input_to_key.keyImage = img; - - //fill outputs array and use relative offsets - for (const tx_source_entry::output_entry& out_entry : src_entr.outputs) { - input_to_key.keyOffsets.push_back(out_entry.first); - } - - input_to_key.keyOffsets = absolute_output_offsets_to_relative(input_to_key.keyOffsets); - tx.vin.push_back(input_to_key); - } - - // "Shuffle" outs - std::vector shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; }); - - uint64_t summary_outs_money = 0; - //fill outputs - size_t output_index = 0; - for (const tx_destination_entry& dst_entr : shuffled_dsts) { - if (!(dst_entr.amount > 0)) { - logger(ERROR, BRIGHT_RED) << "Destination with wrong amount: " << dst_entr.amount; - return false; - } - crypto::key_derivation derivation; - crypto::public_key out_eph_public_key; - bool r = crypto::generate_key_derivation(dst_entr.addr.m_viewPublicKey, txkey.sec, derivation); - - if (!(r)) { - logger(ERROR, BRIGHT_RED) - << "at creation outs: failed to generate_key_derivation(" - << dst_entr.addr.m_viewPublicKey << ", " << txkey.sec << ")"; - return false; - } - - r = crypto::derive_public_key(derivation, output_index, - dst_entr.addr.m_spendPublicKey, - out_eph_public_key); - if (!(r)) { - logger(ERROR, BRIGHT_RED) - << "at creation outs: failed to derive_public_key(" << derivation - << ", " << output_index << ", " << dst_entr.addr.m_spendPublicKey - << ")"; - return false; - } - - TransactionOutput out; - out.amount = dst_entr.amount; - TransactionOutputToKey tk; - tk.key = out_eph_public_key; - out.target = tk; - tx.vout.push_back(out); - output_index++; - summary_outs_money += dst_entr.amount; - } - - //check money - if (summary_outs_money > summary_inputs_money) { - logger(ERROR) << "Transaction inputs money (" << summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"; - return false; - } - - //generate ring signatures - crypto::hash tx_prefix_hash; - get_transaction_prefix_hash(tx, tx_prefix_hash); - - size_t i = 0; - for (const tx_source_entry& src_entr : sources) { - std::vector keys_ptrs; - for (const tx_source_entry::output_entry& o : src_entr.outputs) { - keys_ptrs.push_back(&o.second); - } - - tx.signatures.push_back(std::vector()); - std::vector& sigs = tx.signatures.back(); - sigs.resize(src_entr.outputs.size()); - crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).keyImage, keys_ptrs, - in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); - i++; - } - - return true; -} - -bool get_inputs_money_amount(const Transaction& tx, uint64_t& money) { - money = 0; - - for (const auto& in : tx.vin) { - uint64_t amount = 0; - - if (in.type() == typeid(TransactionInputToKey)) { - amount = boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount = boost::get(in).amount; - } - - money += amount; - } - return true; -} - -uint32_t get_block_height(const Block& b) { - if (b.minerTx.vin.size() != 1) { - return 0; - } - const auto& in = b.minerTx.vin[0]; - if (in.type() != typeid(TransactionInputGenerate)) { - return 0; - } - return boost::get(in).height; -} - -bool check_inputs_types_supported(const Transaction& tx) { - for (const auto& in : tx.vin) { - if (in.type() != typeid(TransactionInputToKey) && in.type() != typeid(TransactionInputMultisignature)) { - return false; - } - } - - return true; -} - -bool check_outs_valid(const Transaction& tx, std::string* error) { - for (const TransactionOutput& out : tx.vout) { - if (out.target.type() == typeid(TransactionOutputToKey)) { - if (out.amount == 0) { - if (error) { - *error = "Zero amount ouput"; - } - return false; - } - - if (!check_key(boost::get(out.target).key)) { - if (error) { - *error = "Output with invalid key"; - } - return false; - } - } else if (out.target.type() == typeid(TransactionOutputMultisignature)) { - const TransactionOutputMultisignature& multisignatureOutput = ::boost::get(out.target); - if (multisignatureOutput.requiredSignatures > multisignatureOutput.keys.size()) { - if (error) { - *error = "Multisignature output with invalid required signature count"; - } - return false; - } - for (const crypto::public_key& key : multisignatureOutput.keys) { - if (!check_key(key)) { - if (error) { - *error = "Multisignature output with invalid public key"; - } - return false; - } - } - } else { - if (error) { - *error = "Output with invalid type"; - } - return false; - } - } - - return true; -} - -bool checkMultisignatureInputsDiff(const Transaction& tx) { - std::set> inputsUsage; - for (const auto& inv : tx.vin) { - if (inv.type() == typeid(TransactionInputMultisignature)) { - const TransactionInputMultisignature& in = ::boost::get(inv); - if (!inputsUsage.insert(std::make_pair(in.amount, static_cast(in.outputIndex))).second) { - return false; - } - } - } - return true; -} - -bool check_money_overflow(const Transaction &tx) { - return check_inputs_overflow(tx) && check_outs_overflow(tx); -} - -bool check_inputs_overflow(const Transaction &tx) { - uint64_t money = 0; - - for (const auto &in : tx.vin) { - uint64_t amount = 0; - - if (in.type() == typeid(TransactionInputToKey)) { - amount = boost::get(in).amount; - } else if (in.type() == typeid(TransactionInputMultisignature)) { - amount = boost::get(in).amount; - } - - if (money > amount + money) - return false; - - money += amount; - } - return true; -} - -bool check_outs_overflow(const Transaction& tx) { - uint64_t money = 0; - for (const auto& o : tx.vout) { - if (money > o.amount + money) - return false; - money += o.amount; - } - return true; -} - -uint64_t get_outs_money_amount(const Transaction& tx) { - uint64_t outputs_amount = 0; - for (const auto& o : tx.vout) { - outputs_amount += o.amount; - } - return outputs_amount; -} - -std::string short_hash_str(const crypto::hash& h) { - std::string res = Common::podToHex(h); - - if (res.size() == 64) { - auto erased_pos = res.erase(8, 48); - res.insert(8, "...."); - } - - return res; -} - -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex) { - crypto::public_key pk; - derive_public_key(derivation, keyIndex, acc.m_account_address.m_spendPublicKey, pk); - return pk == out_key.key; -} - -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex) { - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - return is_out_to_acc(acc, out_key, derivation, keyIndex); -} - -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered) { - crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx); - if (null_pkey == tx_pub_key) - return false; - return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered); -} - -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) { - money_transfered = 0; - size_t keyIndex = 0; - size_t outputIndex = 0; - - crypto::key_derivation derivation; - generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); - - for (const TransactionOutput& o : tx.vout) { - assert(o.target.type() == typeid(TransactionOutputToKey) || o.target.type() == typeid(TransactionOutputMultisignature)); - if (o.target.type() == typeid(TransactionOutputToKey)) { - if (is_out_to_acc(acc, boost::get(o.target), derivation, keyIndex)) { - outs.push_back(outputIndex); - money_transfered += o.amount; - } - - ++keyIndex; - } else if (o.target.type() == typeid(TransactionOutputMultisignature)) { - keyIndex += boost::get(o.target).keys.size(); - } - - ++outputIndex; - } - return true; -} - -void get_blob_hash(const blobdata& blob, crypto::hash& res) { - cn_fast_hash(blob.data(), blob.size(), res); -} - -crypto::hash get_blob_hash(const blobdata& blob) { - crypto::hash h = null_hash; - get_blob_hash(blob, h); - return h; -} - -crypto::hash get_transaction_hash(const Transaction& t) { - crypto::hash h = null_hash; - size_t blob_size = 0; - get_object_hash(t, h, blob_size); - return h; -} - -bool get_transaction_hash(const Transaction& t, crypto::hash& res) { - size_t blob_size = 0; - return get_object_hash(t, res, blob_size); -} - -bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size) { - return get_object_hash(t, res, blob_size); -} - -bool get_block_hashing_blob(const Block& b, blobdata& blob) { - if (!t_serializable_object_to_blob(static_cast(b), blob)) { - return false; - } - crypto::hash tree_root_hash = get_tx_tree_hash(b); - blob.append(reinterpret_cast(&tree_root_hash), sizeof(tree_root_hash)); - blob.append(tools::get_varint_data(b.txHashes.size() + 1)); - - return true; -} - -bool get_parent_block_hashing_blob(const Block& b, blobdata& blob) { - auto serializer = makeParentBlockSerializer(b, true, true); - return t_serializable_object_to_blob(serializer, blob); -} - -bool get_block_hash(const Block& b, crypto::hash& res) { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) { - return false; - } - - if (BLOCK_MAJOR_VERSION_2 <= b.majorVersion) { - blobdata parent_blob; - auto serializer = makeParentBlockSerializer(b, true, false); - if (!t_serializable_object_to_blob(serializer, parent_blob)) - return false; - - blob.append(parent_blob); - } - - return get_object_hash(blob, res); -} - -crypto::hash get_block_hash(const Block& b) { - crypto::hash p = null_hash; - get_block_hash(b, p); - return p; -} - -bool get_aux_block_header_hash(const Block& b, crypto::hash& res) { - blobdata blob; - if (!get_block_hashing_blob(b, blob)) { - return false; - } - - return get_object_hash(blob, res); -} - -bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res) { - blobdata bd; - if (b.majorVersion == BLOCK_MAJOR_VERSION_1) { - if (!get_block_hashing_blob(b, bd)) { - return false; - } - } else if (b.majorVersion == BLOCK_MAJOR_VERSION_2) { - if (!get_parent_block_hashing_blob(b, bd)) { - return false; - } - } else { - return false; - } - crypto::cn_slow_hash(context, bd.data(), bd.size(), res); - return true; -} - -std::vector relative_output_offsets_to_absolute(const std::vector& off) { - std::vector res = off; - for (size_t i = 1; i < res.size(); i++) - res[i] += res[i - 1]; - return res; -} - -std::vector absolute_output_offsets_to_relative(const std::vector& off) { - std::vector res = off; - if (!off.size()) - return res; - std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted - for (size_t i = res.size() - 1; i != 0; i--) - res[i] -= res[i - 1]; - - return res; -} - -bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b) { - std::stringstream ss; - ss << b_blob; - binary_archive ba(ss); - return ::serialization::serialize(ba, b); -} - -blobdata block_to_blob(const Block& b) { - return t_serializable_object_to_blob(b); -} - -bool block_to_blob(const Block& b, blobdata& b_blob) { - return t_serializable_object_to_blob(b, b_blob); -} - -blobdata tx_to_blob(const Transaction& tx) { - return t_serializable_object_to_blob(tx); -} - -bool tx_to_blob(const Transaction& tx, blobdata& b_blob) { - return t_serializable_object_to_blob(tx, b_blob); -} - -void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) { - tree_hash(tx_hashes.data(), tx_hashes.size(), h); -} - -crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) { - crypto::hash h = null_hash; - get_tx_tree_hash(tx_hashes, h); - return h; -} - -crypto::hash get_tx_tree_hash(const Block& b) { - std::vector txs_ids; - crypto::hash h = null_hash; - size_t bl_sz = 0; - get_transaction_hash(b.minerTx, h, bl_sz); - txs_ids.push_back(h); - for (auto& th : b.txHashes) { - txs_ids.push_back(th); - } - return get_tx_tree_hash(txs_ids); -} - -} diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h deleted file mode 100644 index 47899aa7a6..0000000000 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "../cryptonote_protocol/blobdatatype.h" -#include "cryptonote_basic.h" - -namespace Logging { -class ILogger; -} - -namespace CryptoNote { - -void get_transaction_prefix_hash(const TransactionPrefix& tx, crypto::hash& h); -crypto::hash get_transaction_prefix_hash(const TransactionPrefix& 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); - -struct tx_source_entry { - typedef std::pair output_entry; - - std::vector outputs; //index + key - size_t real_output; //index in outputs vector of real output_entry - crypto::public_key real_out_tx_key; //incoming real tx public key - size_t real_output_in_tx_index; //index in transaction outputs vector - uint64_t amount; //money -}; - -struct tx_destination_entry { - uint64_t amount; //money - AccountPublicAddress addr; //destination address - - tx_destination_entry() : amount(0), addr(boost::value_initialized()) { } - tx_destination_entry(uint64_t a, const AccountPublicAddress &ad) : amount(a), addr(ad) { } -}; - - -bool construct_tx( - const account_keys& sender_account_keys, - const std::vector& sources, - const std::vector& destinations, - std::vector extra, Transaction& tx, uint64_t unlock_time, Logging::ILogger& log); - -template -bool find_tx_extra_field_by_type(const std::vector& tx_extra_fields, T& field) { - auto it = std::find_if(tx_extra_fields.begin(), tx_extra_fields.end(), - [](const tx_extra_field& f) { return typeid(T) == f.type(); }); - - if (tx_extra_fields.end() == it) - return false; - - field = boost::get(*it); - return true; -} - -bool parse_tx_extra(const std::vector& tx_extra, std::vector& tx_extra_fields); -crypto::public_key get_tx_pub_key_from_extra(const std::vector& tx_extra); -crypto::public_key get_tx_pub_key_from_extra(const Transaction& tx); -bool add_tx_pub_key_to_extra(Transaction& tx, const crypto::public_key& tx_pub_key); -bool add_extra_nonce_to_tx_extra(std::vector& tx_extra, const blobdata& extra_nonce); -void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id); -bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id); -bool append_mm_tag_to_extra(std::vector& tx_extra, const tx_extra_merge_mining_tag& mm_tag); -bool get_mm_tag_from_extra(const std::vector& tx_extra, tx_extra_merge_mining_tag& mm_tag); -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::public_key& tx_pub_key, size_t keyIndex); -bool is_out_to_acc(const account_keys& acc, const TransactionOutputToKey& out_key, const crypto::key_derivation& derivation, size_t keyIndex); -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); -bool lookup_acc_outs(const account_keys& acc, const Transaction& tx, std::vector& outs, uint64_t& money_transfered); -bool get_tx_fee(const Transaction& tx, uint64_t & fee); -uint64_t get_tx_fee(const Transaction& tx); -bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, KeyPair& in_ephemeral, crypto::key_image& ki); -void get_blob_hash(const blobdata& blob, crypto::hash& res); -crypto::hash get_blob_hash(const blobdata& blob); -std::string short_hash_str(const crypto::hash& h); -bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra); -//returns false if payment id is not found or parse error -bool getPaymentIdFromTxExtra(const std::vector& extra, crypto::hash& paymentId); -bool parsePaymentId(const std::string& paymentIdString, crypto::hash& paymentId); - -crypto::hash get_transaction_hash(const Transaction& t); -bool get_transaction_hash(const Transaction& t, crypto::hash& res); -bool get_transaction_hash(const Transaction& t, crypto::hash& res, size_t& blob_size); -bool get_block_hashing_blob(const Block& b, blobdata& blob); -bool get_parent_block_hashing_blob(const Block& b, blobdata& blob); -bool get_aux_block_header_hash(const Block& b, crypto::hash& res); -bool get_block_hash(const Block& b, crypto::hash& res); -crypto::hash get_block_hash(const Block& b); -bool get_block_longhash(crypto::cn_context &context, const Block& b, crypto::hash& res); -bool parse_and_validate_block_from_blob(const blobdata& b_blob, Block& b); -bool get_inputs_money_amount(const Transaction& tx, uint64_t& money); -uint64_t get_outs_money_amount(const Transaction& tx); -bool check_inputs_types_supported(const Transaction& tx); -bool check_outs_valid(const Transaction& tx, std::string* error = 0); -bool checkMultisignatureInputsDiff(const Transaction& tx); - -bool check_money_overflow(const Transaction& tx); -bool check_outs_overflow(const Transaction& tx); -bool check_inputs_overflow(const Transaction& tx); -uint32_t get_block_height(const Block& b); -std::vector relative_output_offsets_to_absolute(const std::vector& off); -std::vector absolute_output_offsets_to_relative(const std::vector& off); - -template -bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) { - std::stringstream ss; - binary_archive ba(ss); - bool r = ::serialization::serialize(ba, const_cast(to)); - b_blob = ss.str(); - return r; -} - -template -blobdata t_serializable_object_to_blob(const t_object& to) { - blobdata b; - t_serializable_object_to_blob(to, b); - return b; -} - -template -bool get_object_hash(const t_object& o, crypto::hash& res) { - get_blob_hash(t_serializable_object_to_blob(o), res); - return true; -} - -template -bool get_object_blobsize(const t_object& o, size_t& size) { - blobdata blob; - if (!t_serializable_object_to_blob(o, blob)) { - size = (std::numeric_limits::max)(); - return false; - } - size = blob.size(); - return true; -} - -template -size_t get_object_blobsize(const t_object& o) { - size_t size; - get_object_blobsize(o, size); - return size; -} - -template -bool get_object_hash(const t_object& o, crypto::hash& res, size_t& blob_size) { - blobdata bl = t_serializable_object_to_blob(o); - blob_size = bl.size(); - get_blob_hash(bl, res); - return true; -} - -template -std::string obj_to_json_str(const T& obj) { - std::stringstream ss; - json_archive ar(ss, true); - bool r = ::serialization::serialize(ar, *const_cast(&obj)); - if (!r) { - return ""; - } - return ss.str(); -} - -// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold -template -void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) { - if (0 == amount) { - return; - } - - bool is_dust_handled = false; - uint64_t dust = 0; - uint64_t order = 1; - while (0 != amount) { - uint64_t chunk = (amount % 10) * order; - amount /= 10; - order *= 10; - - if (dust + chunk <= dust_threshold) { - dust += chunk; - } else { - if (!is_dust_handled && 0 != dust) { - dust_handler(dust); - is_dust_handled = true; - } - if (0 != chunk) { - chunk_handler(chunk); - } - } - } - - if (!is_dust_handled && 0 != dust) { - dust_handler(dust); - } -} - -blobdata block_to_blob(const Block& b); -bool block_to_blob(const Block& b, blobdata& b_blob); -blobdata tx_to_blob(const Transaction& b); -bool tx_to_blob(const Transaction& b, blobdata& b_blob); -void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h); -crypto::hash get_tx_tree_hash(const std::vector& tx_hashes); -crypto::hash get_tx_tree_hash(const Block& b); - -#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ - if(variant_var.type() != typeid(specific_type)) { return fail_return_val; } \ - specific_type& variable_name = boost::get(variant_var); - -} diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h deleted file mode 100644 index e7d3c14ded..0000000000 --- a/src/cryptonote_core/tx_extra.h +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers -// -// This file is part of Bytecoin. -// -// Bytecoin is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Bytecoin 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 Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Bytecoin. If not, see . - -#pragma once - -#include "crypto/crypto.h" -#include "crypto/hash.h" -#include "serialization/binary_archive.h" -#include "serialization/crypto.h" -#include "serialization/serialization.h" -#include "serialization/variant.h" - -#define TX_EXTRA_PADDING_MAX_COUNT 255 -#define TX_EXTRA_NONCE_MAX_COUNT 255 - -#define TX_EXTRA_TAG_PADDING 0x00 -#define TX_EXTRA_TAG_PUBKEY 0x01 -#define TX_EXTRA_NONCE 0x02 -#define TX_EXTRA_MERGE_MINING_TAG 0x03 - -#define TX_EXTRA_NONCE_PAYMENT_ID 0x00 - -namespace CryptoNote -{ - struct tx_extra_padding - { - size_t size; - - // load - template